summaryrefslogtreecommitdiffstats
path: root/third_party
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 01:13:33 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 01:13:33 +0000
commit086c044dc34dfc0f74fbe41f4ecb402b2cd34884 (patch)
treea4f824bd33cb075dd5aa3eb5a0a94af221bbe83a /third_party
parentAdding debian version 124.0.1-1. (diff)
downloadfirefox-086c044dc34dfc0f74fbe41f4ecb402b2cd34884.tar.xz
firefox-086c044dc34dfc0f74fbe41f4ecb402b2cd34884.zip
Merging upstream version 125.0.1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party')
-rw-r--r--third_party/WinToast/moz-check-system-shortcut.patch81
-rw-r--r--third_party/WinToast/moz-disable-create-shortcut.patch110
-rw-r--r--third_party/WinToast/moz.yaml37
-rw-r--r--third_party/WinToast/upstream-add-toast-scenario.patch123
-rw-r--r--third_party/WinToast/wintoastlib.cpp1197
-rw-r--r--third_party/WinToast/wintoastlib.h234
-rw-r--r--third_party/aom/AUTHORS1
-rw-r--r--third_party/aom/CHANGELOG36
-rw-r--r--third_party/aom/CMakeLists.txt2
-rw-r--r--third_party/aom/aom/src/aom_codec.c1
-rwxr-xr-xthird_party/aom/aom_dsp/aom_dsp_rtcd_defs.pl21
-rw-r--r--third_party/aom/aom_dsp/arm/highbd_variance_sve.c9
-rw-r--r--third_party/aom/aom_dsp/arm/intrapred_neon.c1
-rw-r--r--third_party/aom/aom_dsp/flow_estimation/arm/disflow_neon.c2
-rw-r--r--third_party/aom/aom_dsp/flow_estimation/corner_match.c2
-rw-r--r--third_party/aom/aom_dsp/flow_estimation/disflow.c4
-rw-r--r--third_party/aom/aom_dsp/flow_estimation/x86/disflow_sse4.c2
-rw-r--r--third_party/aom/av1/av1.cmake1
-rw-r--r--third_party/aom/av1/common/arm/highbd_warp_plane_sve.c293
-rw-r--r--third_party/aom/av1/common/av1_rtcd_defs.pl2
-rw-r--r--third_party/aom/av1/common/reconintra.c193
-rw-r--r--third_party/aom/av1/encoder/encoder.c13
-rw-r--r--third_party/aom/av1/encoder/encoder.h4
-rw-r--r--third_party/aom/av1/encoder/mcomp.c1
-rw-r--r--third_party/aom/av1/encoder/speed_features.c8
-rw-r--r--third_party/aom/common/tools_common.c20
-rwxr-xr-xthird_party/aom/test/av1_c_vs_simd_encode.sh38
-rw-r--r--third_party/aom/test/dr_prediction_test.cc68
-rw-r--r--third_party/aom/test/encode_api_test.cc48
-rw-r--r--third_party/aom/test/resize_test.cc69
-rw-r--r--third_party/aom/test/variance_test.cc6
-rw-r--r--third_party/aom/test/warp_filter_test.cc6
-rw-r--r--third_party/dav1d/NEWS14
-rw-r--r--third_party/dav1d/meson.build4
-rw-r--r--third_party/dav1d/src/arm/64/itx.S7
-rw-r--r--third_party/dav1d/src/decode.c42
-rw-r--r--third_party/dav1d/src/picture.c44
-rw-r--r--third_party/dav1d/src/riscv/64/itx.S803
-rw-r--r--third_party/dav1d/src/riscv/itx.h6
-rw-r--r--third_party/dav1d/src/x86/ipred.h1
-rw-r--r--third_party/dav1d/src/x86/ipred16_avx512.asm610
-rwxr-xr-xthird_party/dav1d/tests/dav1d_argon.bash16
-rw-r--r--third_party/function2/Readme.md14
-rw-r--r--third_party/function2/include/function2/function2.hpp8
-rw-r--r--third_party/function2/moz.yaml4
-rw-r--r--third_party/gemmology/gemmology.h95
-rw-r--r--third_party/gemmology/kernels/GemmologyEngineNeon64I8mm.cpp19
-rw-r--r--third_party/gemmology/moz.yaml4
-rw-r--r--third_party/jpeg-xl/.clang-tidy33
-rw-r--r--third_party/jpeg-xl/AUTHORS3
-rw-r--r--third_party/jpeg-xl/CHANGELOG.md38
-rw-r--r--third_party/jpeg-xl/CMakeLists.txt4
-rw-r--r--third_party/jpeg-xl/MODULE.bazel5
-rw-r--r--third_party/jpeg-xl/README.md9
-rw-r--r--third_party/jpeg-xl/WORKSPACE182
-rwxr-xr-xthird_party/jpeg-xl/bash_test.sh6
-rwxr-xr-xthird_party/jpeg-xl/ci.sh75
-rw-r--r--third_party/jpeg-xl/debian/control1
-rwxr-xr-xthird_party/jpeg-xl/deps.sh2
-rw-r--r--third_party/jpeg-xl/examples/CMakeLists.txt10
-rw-r--r--third_party/jpeg-xl/examples/decode_exif_metadata.cc6
-rw-r--r--third_party/jpeg-xl/examples/decode_oneshot.cc20
-rw-r--r--third_party/jpeg-xl/examples/decode_progressive.cc12
-rw-r--r--third_party/jpeg-xl/examples/encode_oneshot.cc22
-rw-r--r--third_party/jpeg-xl/flake.nix1
-rw-r--r--third_party/jpeg-xl/lib/BUILD11
-rw-r--r--third_party/jpeg-xl/lib/CMakeLists.txt10
-rw-r--r--third_party/jpeg-xl/lib/extras/alpha_blend.cc7
-rw-r--r--third_party/jpeg-xl/lib/extras/alpha_blend.h2
-rw-r--r--third_party/jpeg-xl/lib/extras/codec.h2
-rw-r--r--third_party/jpeg-xl/lib/extras/codec_test.cc36
-rw-r--r--third_party/jpeg-xl/lib/extras/dec/apng.cc119
-rw-r--r--third_party/jpeg-xl/lib/extras/dec/color_description.cc99
-rw-r--r--third_party/jpeg-xl/lib/extras/dec/color_hints.cc9
-rw-r--r--third_party/jpeg-xl/lib/extras/dec/decode.cc6
-rw-r--r--third_party/jpeg-xl/lib/extras/dec/decode.h2
-rw-r--r--third_party/jpeg-xl/lib/extras/dec/exr.cc9
-rw-r--r--third_party/jpeg-xl/lib/extras/dec/gif.cc28
-rw-r--r--third_party/jpeg-xl/lib/extras/dec/jpegli.cc21
-rw-r--r--third_party/jpeg-xl/lib/extras/dec/jpg.cc30
-rw-r--r--third_party/jpeg-xl/lib/extras/dec/jxl.cc90
-rw-r--r--third_party/jpeg-xl/lib/extras/dec/jxl.h4
-rw-r--r--third_party/jpeg-xl/lib/extras/dec/pgx.cc11
-rw-r--r--third_party/jpeg-xl/lib/extras/dec/pnm.cc31
-rw-r--r--third_party/jpeg-xl/lib/extras/enc/apng.cc29
-rw-r--r--third_party/jpeg-xl/lib/extras/enc/encode.cc2
-rw-r--r--third_party/jpeg-xl/lib/extras/enc/encode.h2
-rw-r--r--third_party/jpeg-xl/lib/extras/enc/exr.cc4
-rw-r--r--third_party/jpeg-xl/lib/extras/enc/jpegli.cc82
-rw-r--r--third_party/jpeg-xl/lib/extras/enc/jpg.cc81
-rw-r--r--third_party/jpeg-xl/lib/extras/enc/jxl.cc45
-rw-r--r--third_party/jpeg-xl/lib/extras/enc/jxl.h8
-rw-r--r--third_party/jpeg-xl/lib/extras/enc/npy.cc35
-rw-r--r--third_party/jpeg-xl/lib/extras/enc/pgx.cc4
-rw-r--r--third_party/jpeg-xl/lib/extras/enc/pnm.cc2
-rw-r--r--third_party/jpeg-xl/lib/extras/jpegli_test.cc18
-rw-r--r--third_party/jpeg-xl/lib/extras/mmap.cc7
-rw-r--r--third_party/jpeg-xl/lib/extras/packed_image.h55
-rw-r--r--third_party/jpeg-xl/lib/extras/packed_image_convert.cc51
-rw-r--r--third_party/jpeg-xl/lib/extras/tone_mapping.cc2
-rw-r--r--third_party/jpeg-xl/lib/extras/tone_mapping_gbench.cc6
-rw-r--r--third_party/jpeg-xl/lib/include/jxl/cms_interface.h27
-rw-r--r--third_party/jpeg-xl/lib/include/jxl/codestream_header.h35
-rw-r--r--third_party/jpeg-xl/lib/include/jxl/color_encoding.h39
-rw-r--r--third_party/jpeg-xl/lib/include/jxl/decode.h411
-rw-r--r--third_party/jpeg-xl/lib/include/jxl/encode.h403
-rw-r--r--third_party/jpeg-xl/lib/include/jxl/parallel_runner.h35
-rw-r--r--third_party/jpeg-xl/lib/include/jxl/resizable_parallel_runner.h10
-rw-r--r--third_party/jpeg-xl/lib/include/jxl/stats.h8
-rw-r--r--third_party/jpeg-xl/lib/include/jxl/thread_parallel_runner.h8
-rw-r--r--third_party/jpeg-xl/lib/include/jxl/types.h10
-rw-r--r--third_party/jpeg-xl/lib/jpegli.cmake1
-rw-r--r--third_party/jpeg-xl/lib/jpegli/adaptive_quantization.cc19
-rw-r--r--third_party/jpeg-xl/lib/jpegli/bitstream.cc7
-rw-r--r--third_party/jpeg-xl/lib/jpegli/bitstream.h5
-rw-r--r--third_party/jpeg-xl/lib/jpegli/color_quantize.cc40
-rw-r--r--third_party/jpeg-xl/lib/jpegli/color_quantize.h2
-rw-r--r--third_party/jpeg-xl/lib/jpegli/dct-inl.h10
-rw-r--r--third_party/jpeg-xl/lib/jpegli/decode.cc39
-rw-r--r--third_party/jpeg-xl/lib/jpegli/decode_api_test.cc35
-rw-r--r--third_party/jpeg-xl/lib/jpegli/decode_internal.h17
-rw-r--r--third_party/jpeg-xl/lib/jpegli/decode_marker.cc63
-rw-r--r--third_party/jpeg-xl/lib/jpegli/decode_marker.h4
-rw-r--r--third_party/jpeg-xl/lib/jpegli/decode_scan.cc9
-rw-r--r--third_party/jpeg-xl/lib/jpegli/decode_scan.h4
-rw-r--r--third_party/jpeg-xl/lib/jpegli/destination_manager.cc16
-rw-r--r--third_party/jpeg-xl/lib/jpegli/downsample.cc8
-rw-r--r--third_party/jpeg-xl/lib/jpegli/encode.cc33
-rw-r--r--third_party/jpeg-xl/lib/jpegli/encode_api_test.cc24
-rw-r--r--third_party/jpeg-xl/lib/jpegli/encode_finish.cc2
-rw-r--r--third_party/jpeg-xl/lib/jpegli/entropy_coding.cc58
-rw-r--r--third_party/jpeg-xl/lib/jpegli/error.h5
-rw-r--r--third_party/jpeg-xl/lib/jpegli/error_handling_test.cc21
-rw-r--r--third_party/jpeg-xl/lib/jpegli/huffman.cc5
-rw-r--r--third_party/jpeg-xl/lib/jpegli/idct.cc5
-rw-r--r--third_party/jpeg-xl/lib/jpegli/input.cc18
-rw-r--r--third_party/jpeg-xl/lib/jpegli/input_suspension_test.cc30
-rw-r--r--third_party/jpeg-xl/lib/jpegli/libjpeg_test_util.cc15
-rw-r--r--third_party/jpeg-xl/lib/jpegli/libjpeg_wrapper.cc4
-rw-r--r--third_party/jpeg-xl/lib/jpegli/memory_manager.h3
-rw-r--r--third_party/jpeg-xl/lib/jpegli/output_suspension_test.cc12
-rw-r--r--third_party/jpeg-xl/lib/jpegli/quant.cc18
-rw-r--r--third_party/jpeg-xl/lib/jpegli/render.cc31
-rw-r--r--third_party/jpeg-xl/lib/jpegli/source_manager.cc4
-rw-r--r--third_party/jpeg-xl/lib/jpegli/source_manager_test.cc4
-rw-r--r--third_party/jpeg-xl/lib/jpegli/streaming_test.cc18
-rw-r--r--third_party/jpeg-xl/lib/jpegli/test_utils-inl.h40
-rw-r--r--third_party/jpeg-xl/lib/jpegli/test_utils.cc81
-rw-r--r--third_party/jpeg-xl/lib/jpegli/testing.h20
-rw-r--r--third_party/jpeg-xl/lib/jpegli/transpose-inl.h8
-rw-r--r--third_party/jpeg-xl/lib/jpegli/upsample.cc6
-rw-r--r--third_party/jpeg-xl/lib/jxl.cmake4
-rw-r--r--third_party/jpeg-xl/lib/jxl/ac_strategy.cc13
-rw-r--r--third_party/jpeg-xl/lib/jxl/ac_strategy.h13
-rw-r--r--third_party/jpeg-xl/lib/jxl/ac_strategy_test.cc9
-rw-r--r--third_party/jpeg-xl/lib/jxl/alpha_test.cc111
-rw-r--r--third_party/jpeg-xl/lib/jxl/ans_common.h3
-rw-r--r--third_party/jpeg-xl/lib/jxl/ans_test.cc17
-rw-r--r--third_party/jpeg-xl/lib/jxl/base/bits.h3
-rw-r--r--third_party/jpeg-xl/lib/jxl/base/byte_order.h8
-rw-r--r--third_party/jpeg-xl/lib/jxl/base/common.h44
-rw-r--r--third_party/jpeg-xl/lib/jxl/base/exif.h1
-rw-r--r--third_party/jpeg-xl/lib/jxl/base/float.h10
-rw-r--r--third_party/jpeg-xl/lib/jxl/base/matrix_ops.h78
-rw-r--r--third_party/jpeg-xl/lib/jxl/base/override.h4
-rw-r--r--third_party/jpeg-xl/lib/jxl/base/rational_polynomial-inl.h3
-rw-r--r--third_party/jpeg-xl/lib/jxl/base/scope_guard.h6
-rw-r--r--third_party/jpeg-xl/lib/jxl/base/span.h4
-rw-r--r--third_party/jpeg-xl/lib/jxl/base/status.h15
-rw-r--r--third_party/jpeg-xl/lib/jxl/bits_test.cc6
-rw-r--r--third_party/jpeg-xl/lib/jxl/blending.cc14
-rw-r--r--third_party/jpeg-xl/lib/jxl/blending.h10
-rw-r--r--third_party/jpeg-xl/lib/jxl/blending_test.cc8
-rw-r--r--third_party/jpeg-xl/lib/jxl/box_content_decoder.cc2
-rw-r--r--third_party/jpeg-xl/lib/jxl/butteraugli/butteraugli.cc340
-rw-r--r--third_party/jpeg-xl/lib/jxl/butteraugli/butteraugli.h45
-rw-r--r--third_party/jpeg-xl/lib/jxl/butteraugli/butteraugli_test.cc6
-rw-r--r--third_party/jpeg-xl/lib/jxl/cache_aligned.cc3
-rw-r--r--third_party/jpeg-xl/lib/jxl/cache_aligned.h2
-rw-r--r--third_party/jpeg-xl/lib/jxl/chroma_from_luma.cc22
-rw-r--r--third_party/jpeg-xl/lib/jxl/chroma_from_luma.h11
-rw-r--r--third_party/jpeg-xl/lib/jxl/cms/color_encoding_cms.h4
-rw-r--r--third_party/jpeg-xl/lib/jxl/cms/jxl_cms.cc95
-rw-r--r--third_party/jpeg-xl/lib/jxl/cms/jxl_cms_internal.h179
-rw-r--r--third_party/jpeg-xl/lib/jxl/cms/opsin_params.h27
-rw-r--r--third_party/jpeg-xl/lib/jxl/cms/tone_mapping-inl.h6
-rw-r--r--third_party/jpeg-xl/lib/jxl/cms/tone_mapping.h52
-rw-r--r--third_party/jpeg-xl/lib/jxl/cms/tone_mapping_test.cc30
-rw-r--r--third_party/jpeg-xl/lib/jxl/coeff_order.h25
-rw-r--r--third_party/jpeg-xl/lib/jxl/color_encoding_internal.cc4
-rw-r--r--third_party/jpeg-xl/lib/jxl/color_encoding_internal.h8
-rw-r--r--third_party/jpeg-xl/lib/jxl/color_encoding_internal_test.cc23
-rw-r--r--third_party/jpeg-xl/lib/jxl/color_management_test.cc226
-rw-r--r--third_party/jpeg-xl/lib/jxl/common.h32
-rw-r--r--third_party/jpeg-xl/lib/jxl/compressed_dc.cc40
-rw-r--r--third_party/jpeg-xl/lib/jxl/compressed_dc.h6
-rw-r--r--third_party/jpeg-xl/lib/jxl/convolve-inl.h20
-rw-r--r--third_party/jpeg-xl/lib/jxl/convolve_separable5.cc10
-rw-r--r--third_party/jpeg-xl/lib/jxl/convolve_symmetric3.cc11
-rw-r--r--third_party/jpeg-xl/lib/jxl/convolve_symmetric5.cc5
-rw-r--r--third_party/jpeg-xl/lib/jxl/convolve_test.cc33
-rw-r--r--third_party/jpeg-xl/lib/jxl/dct-inl.h8
-rw-r--r--third_party/jpeg-xl/lib/jxl/dct_block-inl.h4
-rw-r--r--third_party/jpeg-xl/lib/jxl/dct_util.h13
-rw-r--r--third_party/jpeg-xl/lib/jxl/dec_ans.cc14
-rw-r--r--third_party/jpeg-xl/lib/jxl/dec_ans.h13
-rw-r--r--third_party/jpeg-xl/lib/jxl/dec_cache.cc5
-rw-r--r--third_party/jpeg-xl/lib/jxl/dec_cache.h27
-rw-r--r--third_party/jpeg-xl/lib/jxl/dec_context_map.cc9
-rw-r--r--third_party/jpeg-xl/lib/jxl/dec_external_image.cc42
-rw-r--r--third_party/jpeg-xl/lib/jxl/dec_external_image_gbench.cc5
-rw-r--r--third_party/jpeg-xl/lib/jxl/dec_frame.cc74
-rw-r--r--third_party/jpeg-xl/lib/jxl/dec_frame.h18
-rw-r--r--third_party/jpeg-xl/lib/jxl/dec_group.cc39
-rw-r--r--third_party/jpeg-xl/lib/jxl/dec_huffman.cc35
-rw-r--r--third_party/jpeg-xl/lib/jxl/dec_modular.cc64
-rw-r--r--third_party/jpeg-xl/lib/jxl/dec_modular.h2
-rw-r--r--third_party/jpeg-xl/lib/jxl/dec_noise.cc5
-rw-r--r--third_party/jpeg-xl/lib/jxl/dec_patch_dictionary.cc45
-rw-r--r--third_party/jpeg-xl/lib/jxl/dec_patch_dictionary.h5
-rw-r--r--third_party/jpeg-xl/lib/jxl/dec_transforms_testonly.cc10
-rw-r--r--third_party/jpeg-xl/lib/jxl/dec_transforms_testonly.h4
-rw-r--r--third_party/jpeg-xl/lib/jxl/dec_xyb-inl.h8
-rw-r--r--third_party/jpeg-xl/lib/jxl/dec_xyb.cc43
-rw-r--r--third_party/jpeg-xl/lib/jxl/dec_xyb.h4
-rw-r--r--third_party/jpeg-xl/lib/jxl/decode.cc144
-rw-r--r--third_party/jpeg-xl/lib/jxl/decode_test.cc434
-rw-r--r--third_party/jpeg-xl/lib/jxl/enc_ac_strategy.cc65
-rw-r--r--third_party/jpeg-xl/lib/jxl/enc_ac_strategy.h16
-rw-r--r--third_party/jpeg-xl/lib/jxl/enc_adaptive_quantization.cc227
-rw-r--r--third_party/jpeg-xl/lib/jxl/enc_adaptive_quantization.h19
-rw-r--r--third_party/jpeg-xl/lib/jxl/enc_ans.cc172
-rw-r--r--third_party/jpeg-xl/lib/jxl/enc_ans.h2
-rw-r--r--third_party/jpeg-xl/lib/jxl/enc_ans_params.h11
-rw-r--r--third_party/jpeg-xl/lib/jxl/enc_ar_control_field.cc30
-rw-r--r--third_party/jpeg-xl/lib/jxl/enc_ar_control_field.h24
-rw-r--r--third_party/jpeg-xl/lib/jxl/enc_aux_out.cc20
-rw-r--r--third_party/jpeg-xl/lib/jxl/enc_bit_writer.cc8
-rw-r--r--third_party/jpeg-xl/lib/jxl/enc_butteraugli_comparator.cc42
-rw-r--r--third_party/jpeg-xl/lib/jxl/enc_butteraugli_comparator.h16
-rw-r--r--third_party/jpeg-xl/lib/jxl/enc_cache.cc56
-rw-r--r--third_party/jpeg-xl/lib/jxl/enc_cache.h2
-rw-r--r--third_party/jpeg-xl/lib/jxl/enc_chroma_from_luma.cc21
-rw-r--r--third_party/jpeg-xl/lib/jxl/enc_chroma_from_luma.h2
-rw-r--r--third_party/jpeg-xl/lib/jxl/enc_cluster.cc19
-rw-r--r--third_party/jpeg-xl/lib/jxl/enc_cluster.h6
-rw-r--r--third_party/jpeg-xl/lib/jxl/enc_coeff_order.cc13
-rw-r--r--third_party/jpeg-xl/lib/jxl/enc_comparator.cc27
-rw-r--r--third_party/jpeg-xl/lib/jxl/enc_comparator.h8
-rw-r--r--third_party/jpeg-xl/lib/jxl/enc_context_map.cc28
-rw-r--r--third_party/jpeg-xl/lib/jxl/enc_debug_image.cc78
-rw-r--r--third_party/jpeg-xl/lib/jxl/enc_debug_image.h20
-rw-r--r--third_party/jpeg-xl/lib/jxl/enc_detect_dots.cc154
-rw-r--r--third_party/jpeg-xl/lib/jxl/enc_detect_dots.h5
-rw-r--r--third_party/jpeg-xl/lib/jxl/enc_dot_dictionary.cc19
-rw-r--r--third_party/jpeg-xl/lib/jxl/enc_dot_dictionary.h10
-rw-r--r--third_party/jpeg-xl/lib/jxl/enc_entropy_coder.cc20
-rw-r--r--third_party/jpeg-xl/lib/jxl/enc_entropy_coder.h2
-rw-r--r--third_party/jpeg-xl/lib/jxl/enc_external_image.cc17
-rw-r--r--third_party/jpeg-xl/lib/jxl/enc_fast_lossless.cc59
-rw-r--r--third_party/jpeg-xl/lib/jxl/enc_fields.cc6
-rw-r--r--third_party/jpeg-xl/lib/jxl/enc_frame.cc308
-rw-r--r--third_party/jpeg-xl/lib/jxl/enc_frame.h2
-rw-r--r--third_party/jpeg-xl/lib/jxl/enc_gaborish.cc9
-rw-r--r--third_party/jpeg-xl/lib/jxl/enc_gaborish.h5
-rw-r--r--third_party/jpeg-xl/lib/jxl/enc_gaborish_test.cc14
-rw-r--r--third_party/jpeg-xl/lib/jxl/enc_group.cc16
-rw-r--r--third_party/jpeg-xl/lib/jxl/enc_heuristics.cc188
-rw-r--r--third_party/jpeg-xl/lib/jxl/enc_heuristics.h4
-rw-r--r--third_party/jpeg-xl/lib/jxl/enc_huffman.cc4
-rw-r--r--third_party/jpeg-xl/lib/jxl/enc_huffman_tree.cc5
-rw-r--r--third_party/jpeg-xl/lib/jxl/enc_icc_codec.cc26
-rw-r--r--third_party/jpeg-xl/lib/jxl/enc_icc_codec.h4
-rw-r--r--third_party/jpeg-xl/lib/jxl/enc_image_bundle.cc25
-rw-r--r--third_party/jpeg-xl/lib/jxl/enc_linalg.cc37
-rw-r--r--third_party/jpeg-xl/lib/jxl/enc_linalg.h10
-rw-r--r--third_party/jpeg-xl/lib/jxl/enc_linalg_test.cc106
-rw-r--r--third_party/jpeg-xl/lib/jxl/enc_modular.cc631
-rw-r--r--third_party/jpeg-xl/lib/jxl/enc_modular.h67
-rw-r--r--third_party/jpeg-xl/lib/jxl/enc_noise.cc4
-rw-r--r--third_party/jpeg-xl/lib/jxl/enc_optimize_test.cc2
-rw-r--r--third_party/jpeg-xl/lib/jxl/enc_params.h39
-rw-r--r--third_party/jpeg-xl/lib/jxl/enc_patch_dictionary.cc106
-rw-r--r--third_party/jpeg-xl/lib/jxl/enc_patch_dictionary.h19
-rw-r--r--third_party/jpeg-xl/lib/jxl/enc_photon_noise_test.cc47
-rw-r--r--third_party/jpeg-xl/lib/jxl/enc_quant_weights.cc29
-rw-r--r--third_party/jpeg-xl/lib/jxl/enc_quant_weights.h6
-rw-r--r--third_party/jpeg-xl/lib/jxl/enc_splines.cc4
-rw-r--r--third_party/jpeg-xl/lib/jxl/enc_transforms.cc9
-rw-r--r--third_party/jpeg-xl/lib/jxl/enc_transforms.h3
-rw-r--r--third_party/jpeg-xl/lib/jxl/enc_xyb.cc43
-rw-r--r--third_party/jpeg-xl/lib/jxl/enc_xyb.h5
-rw-r--r--third_party/jpeg-xl/lib/jxl/encode.cc123
-rw-r--r--third_party/jpeg-xl/lib/jxl/encode_internal.h19
-rw-r--r--third_party/jpeg-xl/lib/jxl/encode_test.cc186
-rw-r--r--third_party/jpeg-xl/lib/jxl/entropy_coder.cc2
-rw-r--r--third_party/jpeg-xl/lib/jxl/fast_dct-inl.h2
-rw-r--r--third_party/jpeg-xl/lib/jxl/fast_dct.cc37
-rw-r--r--third_party/jpeg-xl/lib/jxl/fast_dct.h9
-rw-r--r--third_party/jpeg-xl/lib/jxl/fast_dct_test.cc9
-rw-r--r--third_party/jpeg-xl/lib/jxl/fast_math_test.cc8
-rw-r--r--third_party/jpeg-xl/lib/jxl/fields.cc9
-rw-r--r--third_party/jpeg-xl/lib/jxl/fields.h2
-rw-r--r--third_party/jpeg-xl/lib/jxl/fields_test.cc25
-rw-r--r--third_party/jpeg-xl/lib/jxl/frame_header.cc14
-rw-r--r--third_party/jpeg-xl/lib/jxl/frame_header.h10
-rw-r--r--third_party/jpeg-xl/lib/jxl/gradient_test.cc116
-rw-r--r--third_party/jpeg-xl/lib/jxl/headers.cc2
-rw-r--r--third_party/jpeg-xl/lib/jxl/icc_codec.cc6
-rw-r--r--third_party/jpeg-xl/lib/jxl/icc_codec.h6
-rw-r--r--third_party/jpeg-xl/lib/jxl/icc_codec_common.cc6
-rw-r--r--third_party/jpeg-xl/lib/jxl/icc_codec_common.h2
-rw-r--r--third_party/jpeg-xl/lib/jxl/icc_codec_test.cc6
-rw-r--r--third_party/jpeg-xl/lib/jxl/image.cc210
-rw-r--r--third_party/jpeg-xl/lib/jxl/image.h86
-rw-r--r--third_party/jpeg-xl/lib/jxl/image_bundle.h8
-rw-r--r--third_party/jpeg-xl/lib/jxl/image_metadata.cc16
-rw-r--r--third_party/jpeg-xl/lib/jxl/image_metadata.h2
-rw-r--r--third_party/jpeg-xl/lib/jxl/image_ops.cc91
-rw-r--r--third_party/jpeg-xl/lib/jxl/image_ops.h183
-rw-r--r--third_party/jpeg-xl/lib/jxl/image_ops_test.cc54
-rw-r--r--third_party/jpeg-xl/lib/jxl/image_test_utils.h42
-rw-r--r--third_party/jpeg-xl/lib/jxl/inverse_mtf-inl.h2
-rw-r--r--third_party/jpeg-xl/lib/jxl/jpeg/dec_jpeg_data_writer.cc54
-rw-r--r--third_party/jpeg-xl/lib/jxl/jpeg/enc_jpeg_data.cc24
-rw-r--r--third_party/jpeg-xl/lib/jxl/jpeg/enc_jpeg_data_reader.cc8
-rw-r--r--third_party/jpeg-xl/lib/jxl/jpeg/enc_jpeg_data_reader.h2
-rw-r--r--third_party/jpeg-xl/lib/jxl/jpeg/jpeg_data.cc13
-rw-r--r--third_party/jpeg-xl/lib/jxl/jxl_test.cc326
-rw-r--r--third_party/jpeg-xl/lib/jxl/libjxl.pc.in4
-rw-r--r--third_party/jpeg-xl/lib/jxl/libjxl_cms.pc.in4
-rw-r--r--third_party/jpeg-xl/lib/jxl/memory_manager_internal.h10
-rw-r--r--third_party/jpeg-xl/lib/jxl/modular/encoding/context_predict.h12
-rw-r--r--third_party/jpeg-xl/lib/jxl/modular/encoding/dec_ma.cc2
-rw-r--r--third_party/jpeg-xl/lib/jxl/modular/encoding/enc_debug_tree.cc2
-rw-r--r--third_party/jpeg-xl/lib/jxl/modular/encoding/enc_encoding.cc82
-rw-r--r--third_party/jpeg-xl/lib/jxl/modular/encoding/enc_ma.cc15
-rw-r--r--third_party/jpeg-xl/lib/jxl/modular/encoding/encoding.cc12
-rw-r--r--third_party/jpeg-xl/lib/jxl/modular/modular_image.cc35
-rw-r--r--third_party/jpeg-xl/lib/jxl/modular/modular_image.h36
-rw-r--r--third_party/jpeg-xl/lib/jxl/modular/options.h8
-rw-r--r--third_party/jpeg-xl/lib/jxl/modular/transform/enc_palette.cc38
-rw-r--r--third_party/jpeg-xl/lib/jxl/modular/transform/enc_squeeze.cc49
-rw-r--r--third_party/jpeg-xl/lib/jxl/modular/transform/palette.cc19
-rw-r--r--third_party/jpeg-xl/lib/jxl/modular/transform/palette.h1
-rw-r--r--third_party/jpeg-xl/lib/jxl/modular/transform/squeeze.cc19
-rw-r--r--third_party/jpeg-xl/lib/jxl/modular/transform/squeeze.h2
-rw-r--r--third_party/jpeg-xl/lib/jxl/modular/transform/transform.h14
-rw-r--r--third_party/jpeg-xl/lib/jxl/modular_test.cc67
-rw-r--r--third_party/jpeg-xl/lib/jxl/noise.h7
-rw-r--r--third_party/jpeg-xl/lib/jxl/opsin_image_test.cc36
-rw-r--r--third_party/jpeg-xl/lib/jxl/opsin_inverse_test.cc12
-rw-r--r--third_party/jpeg-xl/lib/jxl/opsin_params.cc22
-rw-r--r--third_party/jpeg-xl/lib/jxl/opsin_params.h5
-rw-r--r--third_party/jpeg-xl/lib/jxl/padded_bytes.h2
-rw-r--r--third_party/jpeg-xl/lib/jxl/passes_state.cc30
-rw-r--r--third_party/jpeg-xl/lib/jxl/passes_test.cc46
-rw-r--r--third_party/jpeg-xl/lib/jxl/patch_dictionary_test.cc2
-rw-r--r--third_party/jpeg-xl/lib/jxl/preview_test.cc4
-rw-r--r--third_party/jpeg-xl/lib/jxl/quant_weights.cc25
-rw-r--r--third_party/jpeg-xl/lib/jxl/quant_weights.h29
-rw-r--r--third_party/jpeg-xl/lib/jxl/quant_weights_test.cc20
-rw-r--r--third_party/jpeg-xl/lib/jxl/quantizer.h2
-rw-r--r--third_party/jpeg-xl/lib/jxl/quantizer_test.cc11
-rw-r--r--third_party/jpeg-xl/lib/jxl/rational_polynomial_test.cc10
-rw-r--r--third_party/jpeg-xl/lib/jxl/render_pipeline/low_memory_render_pipeline.cc131
-rw-r--r--third_party/jpeg-xl/lib/jxl/render_pipeline/low_memory_render_pipeline.h16
-rw-r--r--third_party/jpeg-xl/lib/jxl/render_pipeline/render_pipeline.cc19
-rw-r--r--third_party/jpeg-xl/lib/jxl/render_pipeline/render_pipeline.h14
-rw-r--r--third_party/jpeg-xl/lib/jxl/render_pipeline/render_pipeline_stage.h14
-rw-r--r--third_party/jpeg-xl/lib/jxl/render_pipeline/render_pipeline_test.cc28
-rw-r--r--third_party/jpeg-xl/lib/jxl/render_pipeline/simple_render_pipeline.cc58
-rw-r--r--third_party/jpeg-xl/lib/jxl/render_pipeline/simple_render_pipeline.h4
-rw-r--r--third_party/jpeg-xl/lib/jxl/render_pipeline/stage_blending.cc17
-rw-r--r--third_party/jpeg-xl/lib/jxl/render_pipeline/stage_chroma_upsampling.cc14
-rw-r--r--third_party/jpeg-xl/lib/jxl/render_pipeline/stage_cms.cc23
-rw-r--r--third_party/jpeg-xl/lib/jxl/render_pipeline/stage_epf.cc22
-rw-r--r--third_party/jpeg-xl/lib/jxl/render_pipeline/stage_from_linear.cc12
-rw-r--r--third_party/jpeg-xl/lib/jxl/render_pipeline/stage_gaborish.cc9
-rw-r--r--third_party/jpeg-xl/lib/jxl/render_pipeline/stage_noise.cc27
-rw-r--r--third_party/jpeg-xl/lib/jxl/render_pipeline/stage_patches.cc9
-rw-r--r--third_party/jpeg-xl/lib/jxl/render_pipeline/stage_splines.cc7
-rw-r--r--third_party/jpeg-xl/lib/jxl/render_pipeline/stage_spot.cc9
-rw-r--r--third_party/jpeg-xl/lib/jxl/render_pipeline/stage_to_linear.cc12
-rw-r--r--third_party/jpeg-xl/lib/jxl/render_pipeline/stage_tone_mapping.cc12
-rw-r--r--third_party/jpeg-xl/lib/jxl/render_pipeline/stage_upsampling.cc7
-rw-r--r--third_party/jpeg-xl/lib/jxl/render_pipeline/stage_write.cc56
-rw-r--r--third_party/jpeg-xl/lib/jxl/render_pipeline/stage_xyb.cc22
-rw-r--r--third_party/jpeg-xl/lib/jxl/render_pipeline/stage_ycbcr.cc7
-rw-r--r--third_party/jpeg-xl/lib/jxl/render_pipeline/test_render_pipeline_stages.h30
-rw-r--r--third_party/jpeg-xl/lib/jxl/roundtrip_test.cc98
-rw-r--r--third_party/jpeg-xl/lib/jxl/sanitizers.h30
-rw-r--r--third_party/jpeg-xl/lib/jxl/simd_util.cc39
-rw-r--r--third_party/jpeg-xl/lib/jxl/simd_util.h4
-rw-r--r--third_party/jpeg-xl/lib/jxl/speed_tier_test.cc2
-rw-r--r--third_party/jpeg-xl/lib/jxl/splines.cc36
-rw-r--r--third_party/jpeg-xl/lib/jxl/splines.h7
-rw-r--r--third_party/jpeg-xl/lib/jxl/splines_gbench.cc19
-rw-r--r--third_party/jpeg-xl/lib/jxl/splines_test.cc256
-rw-r--r--third_party/jpeg-xl/lib/jxl/test_image.cc20
-rw-r--r--third_party/jpeg-xl/lib/jxl/test_utils.cc57
-rw-r--r--third_party/jpeg-xl/lib/jxl/test_utils.h24
-rw-r--r--third_party/jpeg-xl/lib/jxl/testing.h33
-rw-r--r--third_party/jpeg-xl/lib/jxl/toc.h2
-rw-r--r--third_party/jpeg-xl/lib/jxl/transpose-inl.h18
-rw-r--r--third_party/jpeg-xl/lib/jxl/version.h.in2
-rw-r--r--third_party/jpeg-xl/lib/jxl_cms.cmake2
-rw-r--r--third_party/jpeg-xl/lib/jxl_lists.bzl3
-rw-r--r--third_party/jpeg-xl/lib/jxl_lists.cmake3
-rw-r--r--third_party/jpeg-xl/lib/jxl_tests.cmake1
-rw-r--r--third_party/jpeg-xl/lib/jxl_vars.bzl5
-rw-r--r--third_party/jpeg-xl/lib/nothing.cc7
-rw-r--r--third_party/jpeg-xl/lib/threads/thread_parallel_runner.cc10
-rw-r--r--third_party/jpeg-xl/lib/threads/thread_parallel_runner_internal.cc7
-rw-r--r--third_party/jpeg-xl/lib/threads/thread_parallel_runner_internal.h4
-rw-r--r--third_party/jpeg-xl/plugins/gdk-pixbuf/pixbufloader-jxl.c10
-rw-r--r--third_party/jpeg-xl/plugins/gdk-pixbuf/pixbufloader_test.cc7
-rw-r--r--third_party/jpeg-xl/plugins/gimp/common.cc9
-rw-r--r--third_party/jpeg-xl/plugins/gimp/file-jxl-load.cc23
-rw-r--r--third_party/jpeg-xl/plugins/gimp/file-jxl-save.cc59
-rw-r--r--third_party/jpeg-xl/plugins/mime/README.md2
-rw-r--r--third_party/libwebrtc/BUILD.gn7
-rw-r--r--third_party/libwebrtc/DEPS197
-rw-r--r--third_party/libwebrtc/README.moz-ff-commit837
-rw-r--r--third_party/libwebrtc/README.mozilla558
-rw-r--r--third_party/libwebrtc/api/BUILD.gn179
-rw-r--r--third_party/libwebrtc/api/DEPS80
-rw-r--r--third_party/libwebrtc/api/adaptation/resource_adaptation_api_gn/moz.build5
-rw-r--r--third_party/libwebrtc/api/array_view_gn/moz.build7
-rw-r--r--third_party/libwebrtc/api/async_dns_resolver_gn/moz.build7
-rw-r--r--third_party/libwebrtc/api/async_resolver_factory.h36
-rw-r--r--third_party/libwebrtc/api/audio/aec3_config_gn/moz.build5
-rw-r--r--third_party/libwebrtc/api/audio/aec3_factory_gn/moz.build5
-rw-r--r--third_party/libwebrtc/api/audio/audio_frame_api_gn/moz.build5
-rw-r--r--third_party/libwebrtc/api/audio/audio_frame_processor_gn/moz.build7
-rw-r--r--third_party/libwebrtc/api/audio/audio_mixer_api_gn/moz.build7
-rw-r--r--third_party/libwebrtc/api/audio/echo_control_gn/moz.build7
-rw-r--r--third_party/libwebrtc/api/audio_codecs/BUILD.gn1
-rw-r--r--third_party/libwebrtc/api/audio_codecs/L16/audio_decoder_L16_gn/moz.build5
-rw-r--r--third_party/libwebrtc/api/audio_codecs/L16/audio_encoder_L16_gn/moz.build5
-rw-r--r--third_party/libwebrtc/api/audio_codecs/audio_codecs_api_gn/moz.build5
-rw-r--r--third_party/libwebrtc/api/audio_codecs/audio_decoder_factory.h4
-rw-r--r--third_party/libwebrtc/api/audio_codecs/builtin_audio_decoder_factory_gn/moz.build5
-rw-r--r--third_party/libwebrtc/api/audio_codecs/builtin_audio_encoder_factory_gn/moz.build5
-rw-r--r--third_party/libwebrtc/api/audio_codecs/g711/audio_decoder_g711_gn/moz.build5
-rw-r--r--third_party/libwebrtc/api/audio_codecs/g711/audio_encoder_g711_gn/moz.build5
-rw-r--r--third_party/libwebrtc/api/audio_codecs/g722/audio_decoder_g722_gn/moz.build5
-rw-r--r--third_party/libwebrtc/api/audio_codecs/g722/audio_encoder_g722_config_gn/moz.build7
-rw-r--r--third_party/libwebrtc/api/audio_codecs/g722/audio_encoder_g722_gn/moz.build5
-rw-r--r--third_party/libwebrtc/api/audio_codecs/ilbc/audio_decoder_ilbc_gn/moz.build5
-rw-r--r--third_party/libwebrtc/api/audio_codecs/ilbc/audio_encoder_ilbc_config_gn/moz.build7
-rw-r--r--third_party/libwebrtc/api/audio_codecs/ilbc/audio_encoder_ilbc_gn/moz.build5
-rw-r--r--third_party/libwebrtc/api/audio_codecs/opus/audio_decoder_multiopus_gn/moz.build5
-rw-r--r--third_party/libwebrtc/api/audio_codecs/opus/audio_decoder_opus_config_gn/moz.build7
-rw-r--r--third_party/libwebrtc/api/audio_codecs/opus/audio_decoder_opus_gn/moz.build5
-rw-r--r--third_party/libwebrtc/api/audio_codecs/opus/audio_encoder_multiopus_gn/moz.build5
-rw-r--r--third_party/libwebrtc/api/audio_codecs/opus/audio_encoder_opus_config_gn/moz.build5
-rw-r--r--third_party/libwebrtc/api/audio_codecs/opus/audio_encoder_opus_gn/moz.build5
-rw-r--r--third_party/libwebrtc/api/audio_options_api_gn/moz.build5
-rw-r--r--third_party/libwebrtc/api/bitrate_allocation_gn/moz.build7
-rw-r--r--third_party/libwebrtc/api/call/call_factory_interface.h7
-rw-r--r--third_party/libwebrtc/api/call_api_gn/moz.build7
-rw-r--r--third_party/libwebrtc/api/callfactory_api_gn/moz.build7
-rw-r--r--third_party/libwebrtc/api/create_peerconnection_factory.cc77
-rw-r--r--third_party/libwebrtc/api/create_peerconnection_factory.h19
-rw-r--r--third_party/libwebrtc/api/crypto/frame_decryptor_interface_gn/moz.build7
-rw-r--r--third_party/libwebrtc/api/crypto/frame_encryptor_interface_gn/moz.build7
-rw-r--r--third_party/libwebrtc/api/crypto/options_gn/moz.build5
-rw-r--r--third_party/libwebrtc/api/data_channel_interface.h4
-rw-r--r--third_party/libwebrtc/api/dtls_transport_interface.h4
-rw-r--r--third_party/libwebrtc/api/dtmf_sender_interface.h4
-rw-r--r--third_party/libwebrtc/api/enable_media.cc72
-rw-r--r--third_party/libwebrtc/api/enable_media.h27
-rw-r--r--third_party/libwebrtc/api/enable_media_with_defaults.cc46
-rw-r--r--third_party/libwebrtc/api/enable_media_with_defaults.h28
-rw-r--r--third_party/libwebrtc/api/environment/BUILD.gn63
-rw-r--r--third_party/libwebrtc/api/environment/OWNERS15
-rw-r--r--third_party/libwebrtc/api/environment/environment.h148
-rw-r--r--third_party/libwebrtc/api/environment/environment_factory.cc123
-rw-r--r--third_party/libwebrtc/api/environment/environment_factory.h148
-rw-r--r--third_party/libwebrtc/api/environment/environment_gn/moz.build (renamed from third_party/libwebrtc/rtc_base/async_resolver_interface_gn/moz.build)37
-rw-r--r--third_party/libwebrtc/api/environment/environment_unittest.cc275
-rw-r--r--third_party/libwebrtc/api/fec_controller_api_gn/moz.build7
-rw-r--r--third_party/libwebrtc/api/field_trials_registry_gn/moz.build5
-rw-r--r--third_party/libwebrtc/api/field_trials_view_gn/moz.build7
-rw-r--r--third_party/libwebrtc/api/frame_transformer_interface.h15
-rw-r--r--third_party/libwebrtc/api/frame_transformer_interface_gn/moz.build7
-rw-r--r--third_party/libwebrtc/api/function_view_gn/moz.build7
-rw-r--r--third_party/libwebrtc/api/ice_transport_interface.h21
-rw-r--r--third_party/libwebrtc/api/jsep.h7
-rw-r--r--third_party/libwebrtc/api/legacy_stats_types.h4
-rw-r--r--third_party/libwebrtc/api/libjingle_logging_api_gn/moz.build7
-rw-r--r--third_party/libwebrtc/api/libjingle_peerconnection_api_gn/moz.build7
-rw-r--r--third_party/libwebrtc/api/location_gn/moz.build7
-rw-r--r--third_party/libwebrtc/api/make_ref_counted_gn/moz.build7
-rw-r--r--third_party/libwebrtc/api/media_stream_interface.h10
-rw-r--r--third_party/libwebrtc/api/media_stream_interface_gn/moz.build5
-rw-r--r--third_party/libwebrtc/api/metronome/metronome.cc19
-rw-r--r--third_party/libwebrtc/api/metronome/metronome_gn/moz.build7
-rw-r--r--third_party/libwebrtc/api/neteq/default_neteq_controller_factory_gn/moz.build5
-rw-r--r--third_party/libwebrtc/api/neteq/neteq_api_gn/moz.build5
-rw-r--r--third_party/libwebrtc/api/neteq/neteq_controller_api_gn/moz.build7
-rw-r--r--third_party/libwebrtc/api/neteq/tick_timer_gn/moz.build5
-rw-r--r--third_party/libwebrtc/api/network_state_predictor_api_gn/moz.build7
-rw-r--r--third_party/libwebrtc/api/packet_socket_factory.h21
-rw-r--r--third_party/libwebrtc/api/peer_connection_interface.cc2
-rw-r--r--third_party/libwebrtc/api/peer_connection_interface.h44
-rw-r--r--third_party/libwebrtc/api/priority_gn/moz.build7
-rw-r--r--third_party/libwebrtc/api/ref_count.h67
-rw-r--r--third_party/libwebrtc/api/ref_count_gn/moz.build198
-rw-r--r--third_party/libwebrtc/api/refcountedbase_gn/moz.build7
-rw-r--r--third_party/libwebrtc/api/rtc_error_gn/moz.build5
-rw-r--r--third_party/libwebrtc/api/rtc_event_log/BUILD.gn8
-rw-r--r--third_party/libwebrtc/api/rtc_event_log/rtc_event_log_factory.cc35
-rw-r--r--third_party/libwebrtc/api/rtc_event_log/rtc_event_log_factory.h18
-rw-r--r--third_party/libwebrtc/api/rtc_event_log/rtc_event_log_factory_interface.h8
-rw-r--r--third_party/libwebrtc/api/rtc_event_log/rtc_event_log_gn/moz.build5
-rw-r--r--third_party/libwebrtc/api/rtp_headers_gn/moz.build5
-rw-r--r--third_party/libwebrtc/api/rtp_packet_info_gn/moz.build5
-rw-r--r--third_party/libwebrtc/api/rtp_parameters_gn/moz.build5
-rw-r--r--third_party/libwebrtc/api/rtp_receiver_interface.h4
-rw-r--r--third_party/libwebrtc/api/rtp_sender_interface.h4
-rw-r--r--third_party/libwebrtc/api/rtp_sender_interface_gn/moz.build7
-rw-r--r--third_party/libwebrtc/api/rtp_sender_setparameters_callback_gn/moz.build5
-rw-r--r--third_party/libwebrtc/api/rtp_transceiver_direction_gn/moz.build7
-rw-r--r--third_party/libwebrtc/api/rtp_transceiver_interface.h4
-rw-r--r--third_party/libwebrtc/api/scoped_refptr.h33
-rw-r--r--third_party/libwebrtc/api/scoped_refptr_gn/moz.build7
-rw-r--r--third_party/libwebrtc/api/sctp_transport_interface.h4
-rw-r--r--third_party/libwebrtc/api/sequence_checker_gn/moz.build7
-rw-r--r--third_party/libwebrtc/api/set_local_description_observer_interface.h4
-rw-r--r--third_party/libwebrtc/api/set_remote_description_observer_interface.h4
-rw-r--r--third_party/libwebrtc/api/simulated_network_api_gn/moz.build7
-rw-r--r--third_party/libwebrtc/api/stats/rtc_stats_report.h2
-rw-r--r--third_party/libwebrtc/api/task_queue/BUILD.gn5
-rw-r--r--third_party/libwebrtc/api/task_queue/pending_task_safety_flag_gn/moz.build5
-rw-r--r--third_party/libwebrtc/api/task_queue/task_queue_gn/moz.build5
-rw-r--r--third_party/libwebrtc/api/task_queue/task_queue_test.cc2
-rw-r--r--third_party/libwebrtc/api/test/compile_all_headers.cc1
-rw-r--r--third_party/libwebrtc/api/test/create_time_controller.cc43
-rw-r--r--third_party/libwebrtc/api/test/create_time_controller.h12
-rw-r--r--third_party/libwebrtc/api/test/create_video_codec_tester.cc27
-rw-r--r--third_party/libwebrtc/api/test/create_video_codec_tester.h26
-rw-r--r--third_party/libwebrtc/api/test/mock_transformable_audio_frame.h3
-rw-r--r--third_party/libwebrtc/api/test/mock_transformable_frame.h45
-rw-r--r--third_party/libwebrtc/api/test/pclf/media_configuration.h1
-rw-r--r--third_party/libwebrtc/api/test/pclf/media_quality_test_params.h2
-rw-r--r--third_party/libwebrtc/api/test/peerconnection_quality_test_fixture.h1
-rw-r--r--third_party/libwebrtc/api/test/video_codec_stats.cc97
-rw-r--r--third_party/libwebrtc/api/test/video_codec_stats.h120
-rw-r--r--third_party/libwebrtc/api/test/video_codec_tester.h148
-rw-r--r--third_party/libwebrtc/api/transport/bitrate_settings_gn/moz.build5
-rw-r--r--third_party/libwebrtc/api/transport/datagram_transport_interface_gn/moz.build7
-rw-r--r--third_party/libwebrtc/api/transport/field_trial_based_config_gn/moz.build5
-rw-r--r--third_party/libwebrtc/api/transport/goog_cc_gn/moz.build5
-rw-r--r--third_party/libwebrtc/api/transport/network_control_gn/moz.build5
-rw-r--r--third_party/libwebrtc/api/transport/rtp/dependency_descriptor_gn/moz.build5
-rw-r--r--third_party/libwebrtc/api/transport/rtp/rtp_source_gn/moz.build7
-rw-r--r--third_party/libwebrtc/api/transport/stun.cc24
-rw-r--r--third_party/libwebrtc/api/transport/stun.h19
-rw-r--r--third_party/libwebrtc/api/transport/stun_types_gn/moz.build7
-rw-r--r--third_party/libwebrtc/api/transport/stun_unittest.cc156
-rw-r--r--third_party/libwebrtc/api/transport_api_gn/moz.build5
-rw-r--r--third_party/libwebrtc/api/units/data_rate_gn/moz.build5
-rw-r--r--third_party/libwebrtc/api/units/data_size_gn/moz.build5
-rw-r--r--third_party/libwebrtc/api/units/frequency_gn/moz.build5
-rw-r--r--third_party/libwebrtc/api/units/time_delta_gn/moz.build5
-rw-r--r--third_party/libwebrtc/api/units/timestamp_gn/moz.build5
-rw-r--r--third_party/libwebrtc/api/video/BUILD.gn1
-rw-r--r--third_party/libwebrtc/api/video/builtin_video_bitrate_allocator_factory_gn/moz.build5
-rw-r--r--third_party/libwebrtc/api/video/encoded_frame_gn/moz.build5
-rw-r--r--third_party/libwebrtc/api/video/encoded_image_gn/moz.build5
-rw-r--r--third_party/libwebrtc/api/video/frame_buffer_gn/moz.build5
-rw-r--r--third_party/libwebrtc/api/video/recordable_encoded_frame_gn/moz.build7
-rw-r--r--third_party/libwebrtc/api/video/render_resolution_gn/moz.build7
-rw-r--r--third_party/libwebrtc/api/video/resolution_gn/moz.build7
-rw-r--r--third_party/libwebrtc/api/video/video_adaptation_gn/moz.build5
-rw-r--r--third_party/libwebrtc/api/video/video_bitrate_allocation_gn/moz.build5
-rw-r--r--third_party/libwebrtc/api/video/video_bitrate_allocator_factory_gn/moz.build7
-rw-r--r--third_party/libwebrtc/api/video/video_bitrate_allocator_gn/moz.build5
-rw-r--r--third_party/libwebrtc/api/video/video_codec_constants_gn/moz.build7
-rw-r--r--third_party/libwebrtc/api/video/video_frame.h8
-rw-r--r--third_party/libwebrtc/api/video/video_frame_buffer.h4
-rw-r--r--third_party/libwebrtc/api/video/video_frame_gn/moz.build5
-rw-r--r--third_party/libwebrtc/api/video/video_frame_i010_gn/moz.build5
-rw-r--r--third_party/libwebrtc/api/video/video_frame_metadata_gn/moz.build5
-rw-r--r--third_party/libwebrtc/api/video/video_frame_type_gn/moz.build7
-rw-r--r--third_party/libwebrtc/api/video/video_layers_allocation_gn/moz.build7
-rw-r--r--third_party/libwebrtc/api/video/video_rtp_headers_gn/moz.build5
-rw-r--r--third_party/libwebrtc/api/video/video_stream_encoder_gn/moz.build7
-rw-r--r--third_party/libwebrtc/api/video_codecs/bitstream_parser_api_gn/moz.build7
-rw-r--r--third_party/libwebrtc/api/video_codecs/rtc_software_fallback_wrappers_gn/moz.build5
-rw-r--r--third_party/libwebrtc/api/video_codecs/scalability_mode_gn/moz.build5
-rw-r--r--third_party/libwebrtc/api/video_codecs/video_codecs_api_gn/moz.build5
-rw-r--r--third_party/libwebrtc/api/video_codecs/vp8_temporal_layers_factory_gn/moz.build5
-rw-r--r--third_party/libwebrtc/api/video_track_source_constraints_gn/moz.build7
-rw-r--r--third_party/libwebrtc/api/wrapping_async_dns_resolver.h135
-rw-r--r--third_party/libwebrtc/audio/BUILD.gn2
-rw-r--r--third_party/libwebrtc/audio/audio_gn/moz.build5
-rw-r--r--third_party/libwebrtc/audio/audio_send_stream.cc4
-rw-r--r--third_party/libwebrtc/audio/audio_send_stream_unittest.cc11
-rw-r--r--third_party/libwebrtc/audio/channel_receive.cc26
-rw-r--r--third_party/libwebrtc/audio/channel_receive_frame_transformer_delegate.cc36
-rw-r--r--third_party/libwebrtc/audio/channel_receive_frame_transformer_delegate.h9
-rw-r--r--third_party/libwebrtc/audio/channel_receive_frame_transformer_delegate_unittest.cc28
-rw-r--r--third_party/libwebrtc/audio/channel_receive_unittest.cc36
-rw-r--r--third_party/libwebrtc/audio/channel_send.cc31
-rw-r--r--third_party/libwebrtc/audio/channel_send.h1
-rw-r--r--third_party/libwebrtc/audio/channel_send_frame_transformer_delegate.cc44
-rw-r--r--third_party/libwebrtc/audio/channel_send_frame_transformer_delegate.h10
-rw-r--r--third_party/libwebrtc/audio/channel_send_frame_transformer_delegate_unittest.cc116
-rw-r--r--third_party/libwebrtc/audio/channel_send_unittest.cc7
-rw-r--r--third_party/libwebrtc/audio/mock_voe_channel_proxy.h4
-rw-r--r--third_party/libwebrtc/audio/utility/audio_frame_operations_gn/moz.build5
-rw-r--r--third_party/libwebrtc/build/config/android/BUILD.gn8
-rw-r--r--third_party/libwebrtc/call/BUILD.gn6
-rw-r--r--third_party/libwebrtc/call/adaptation/resource_adaptation_gn/moz.build5
-rw-r--r--third_party/libwebrtc/call/audio_sender_interface_gn/moz.build7
-rw-r--r--third_party/libwebrtc/call/bitrate_allocator_gn/moz.build5
-rw-r--r--third_party/libwebrtc/call/bitrate_configurator_gn/moz.build5
-rw-r--r--third_party/libwebrtc/call/bitrate_estimator_tests.cc4
-rw-r--r--third_party/libwebrtc/call/call.cc5
-rw-r--r--third_party/libwebrtc/call/call_config.cc9
-rw-r--r--third_party/libwebrtc/call/call_config.h18
-rw-r--r--third_party/libwebrtc/call/call_gn/moz.build5
-rw-r--r--third_party/libwebrtc/call/call_interfaces_gn/moz.build5
-rw-r--r--third_party/libwebrtc/call/call_perf_tests.cc9
-rw-r--r--third_party/libwebrtc/call/call_unittest.cc13
-rw-r--r--third_party/libwebrtc/call/rampup_tests.cc21
-rw-r--r--third_party/libwebrtc/call/receive_stream_interface_gn/moz.build7
-rw-r--r--third_party/libwebrtc/call/rtp_demuxer.cc13
-rw-r--r--third_party/libwebrtc/call/rtp_demuxer.h3
-rw-r--r--third_party/libwebrtc/call/rtp_interfaces_gn/moz.build5
-rw-r--r--third_party/libwebrtc/call/rtp_receiver_gn/moz.build5
-rw-r--r--third_party/libwebrtc/call/rtp_sender_gn/moz.build5
-rw-r--r--third_party/libwebrtc/call/rtp_transport_config.h3
-rw-r--r--third_party/libwebrtc/call/rtp_transport_controller_send.cc7
-rw-r--r--third_party/libwebrtc/call/rtp_video_sender_unittest.cc4
-rw-r--r--third_party/libwebrtc/call/simulated_network.h2
-rw-r--r--third_party/libwebrtc/call/version.cc2
-rw-r--r--third_party/libwebrtc/call/version_gn/moz.build5
-rw-r--r--third_party/libwebrtc/call/video_send_stream.h1
-rw-r--r--third_party/libwebrtc/call/video_stream_api_gn/moz.build5
-rw-r--r--third_party/libwebrtc/common_audio/common_audio_avx2_gn/moz.build4
-rw-r--r--third_party/libwebrtc/common_audio/common_audio_c_arm_asm_gn/moz.build7
-rw-r--r--third_party/libwebrtc/common_audio/common_audio_c_gn/moz.build5
-rw-r--r--third_party/libwebrtc/common_audio/common_audio_cc_gn/moz.build5
-rw-r--r--third_party/libwebrtc/common_audio/common_audio_gn/moz.build5
-rw-r--r--third_party/libwebrtc/common_audio/common_audio_neon_c_gn/moz.build1
-rw-r--r--third_party/libwebrtc/common_audio/common_audio_neon_gn/moz.build1
-rw-r--r--third_party/libwebrtc/common_audio/common_audio_sse2_gn/moz.build4
-rw-r--r--third_party/libwebrtc/common_audio/fir_filter_factory_gn/moz.build5
-rw-r--r--third_party/libwebrtc/common_audio/fir_filter_gn/moz.build7
-rw-r--r--third_party/libwebrtc/common_audio/sinc_resampler_gn/moz.build7
-rw-r--r--third_party/libwebrtc/common_audio/third_party/ooura/fft_size_128_gn/moz.build5
-rw-r--r--third_party/libwebrtc/common_audio/third_party/ooura/fft_size_256_gn/moz.build5
-rw-r--r--third_party/libwebrtc/common_audio/third_party/spl_sqrt_floor/spl_sqrt_floor_gn/moz.build5
-rw-r--r--third_party/libwebrtc/common_video/common_video_gn/moz.build5
-rw-r--r--third_party/libwebrtc/common_video/frame_counts_gn/moz.build7
-rw-r--r--third_party/libwebrtc/common_video/generic_frame_descriptor/generic_frame_descriptor_gn/moz.build5
-rw-r--r--third_party/libwebrtc/common_video/h265/h265_bitstream_parser.cc4
-rw-r--r--third_party/libwebrtc/common_video/h265/h265_common.h4
-rw-r--r--third_party/libwebrtc/examples/BUILD.gn2
-rw-r--r--third_party/libwebrtc/examples/androidnativeapi/BUILD.gn2
-rw-r--r--third_party/libwebrtc/examples/androidnativeapi/jni/android_call_client.cc13
-rw-r--r--third_party/libwebrtc/examples/objcnativeapi/objc/objc_call_client.mm18
-rw-r--r--third_party/libwebrtc/examples/peerconnection/client/peer_connection_client.cc1
-rw-r--r--third_party/libwebrtc/examples/stunserver/stunserver_main.cc3
-rwxr-xr-xthird_party/libwebrtc/experiments/field_trials.py6
-rw-r--r--third_party/libwebrtc/experiments/registered_field_trials_gn/moz.build7
-rw-r--r--third_party/libwebrtc/g3doc/style-guide.md5
-rw-r--r--third_party/libwebrtc/infra/specs/client.webrtc.json189
-rw-r--r--third_party/libwebrtc/infra/specs/internal.client.webrtc.json135
-rw-r--r--third_party/libwebrtc/infra/specs/mixins.pyl12
-rw-r--r--third_party/libwebrtc/infra/specs/mixins_webrtc.pyl6
-rw-r--r--third_party/libwebrtc/infra/specs/tryserver.webrtc.json189
-rw-r--r--third_party/libwebrtc/infra/specs/variants.pyl15
-rw-r--r--third_party/libwebrtc/logging/BUILD.gn14
-rw-r--r--third_party/libwebrtc/logging/rtc_event_audio_gn/moz.build5
-rw-r--r--third_party/libwebrtc/logging/rtc_event_bwe_gn/moz.build5
-rw-r--r--third_party/libwebrtc/logging/rtc_event_field_gn/moz.build5
-rw-r--r--third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder_legacy.cc3
-rw-r--r--third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder_new_format.cc3
-rw-r--r--third_party/libwebrtc/logging/rtc_event_log/fake_rtc_event_log_factory.cc12
-rw-r--r--third_party/libwebrtc/logging/rtc_event_log/fake_rtc_event_log_factory.h13
-rw-r--r--third_party/libwebrtc/logging/rtc_event_log/rtc_event_log2_proto_include.h4
-rw-r--r--third_party/libwebrtc/logging/rtc_event_log/rtc_event_log_parser.h3
-rw-r--r--third_party/libwebrtc/logging/rtc_event_log/rtc_event_log_unittest.cc41
-rw-r--r--third_party/libwebrtc/logging/rtc_event_log_parse_status_gn/moz.build7
-rw-r--r--third_party/libwebrtc/logging/rtc_event_number_encodings_gn/moz.build5
-rw-r--r--third_party/libwebrtc/logging/rtc_event_pacing_gn/moz.build5
-rw-r--r--third_party/libwebrtc/logging/rtc_event_rtp_rtcp_gn/moz.build5
-rw-r--r--third_party/libwebrtc/logging/rtc_event_video_gn/moz.build5
-rw-r--r--third_party/libwebrtc/logging/rtc_stream_config_gn/moz.build5
-rw-r--r--third_party/libwebrtc/media/BUILD.gn32
-rw-r--r--third_party/libwebrtc/media/base/codec.cc3
-rw-r--r--third_party/libwebrtc/media/base/codec.h4
-rw-r--r--third_party/libwebrtc/media/codec_gn/moz.build5
-rw-r--r--third_party/libwebrtc/media/engine/null_webrtc_video_engine_unittest.cc2
-rw-r--r--third_party/libwebrtc/media/engine/webrtc_media_engine.cc1
-rw-r--r--third_party/libwebrtc/media/engine/webrtc_media_engine.h8
-rw-r--r--third_party/libwebrtc/media/engine/webrtc_media_engine_defaults.cc43
-rw-r--r--third_party/libwebrtc/media/engine/webrtc_media_engine_defaults.h24
-rw-r--r--third_party/libwebrtc/media/engine/webrtc_media_engine_unittest.cc13
-rw-r--r--third_party/libwebrtc/media/engine/webrtc_video_engine_unittest.cc61
-rw-r--r--third_party/libwebrtc/media/engine/webrtc_voice_engine.cc29
-rw-r--r--third_party/libwebrtc/media/engine/webrtc_voice_engine.h8
-rw-r--r--third_party/libwebrtc/media/engine/webrtc_voice_engine_unittest.cc89
-rw-r--r--third_party/libwebrtc/media/media_channel_gn/moz.build7
-rw-r--r--third_party/libwebrtc/media/media_channel_impl_gn/moz.build7
-rw-r--r--third_party/libwebrtc/media/media_constants_gn/moz.build5
-rw-r--r--third_party/libwebrtc/media/rid_description_gn/moz.build7
-rw-r--r--third_party/libwebrtc/media/rtc_media_base_gn/moz.build5
-rw-r--r--third_party/libwebrtc/media/rtc_media_config_gn/moz.build7
-rw-r--r--third_party/libwebrtc/media/rtc_simulcast_encoder_adapter_gn/moz.build5
-rw-r--r--third_party/libwebrtc/media/rtp_utils_gn/moz.build7
-rw-r--r--third_party/libwebrtc/media/stream_params_gn/moz.build7
-rw-r--r--third_party/libwebrtc/modules/async_audio_processing/async_audio_processing_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/audio_coding/BUILD.gn4
-rw-r--r--third_party/libwebrtc/modules/audio_coding/acm2/audio_coding_module_unittest.cc26
-rw-r--r--third_party/libwebrtc/modules/audio_coding/audio_coding_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/audio_coding/audio_coding_module_typedefs_gn/moz.build7
-rw-r--r--third_party/libwebrtc/modules/audio_coding/audio_coding_opus_common_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/audio_coding/audio_encoder_cng_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/audio_coding/audio_network_adaptor/controller_manager.cc4
-rw-r--r--third_party/libwebrtc/modules/audio_coding/audio_network_adaptor/controller_manager_unittest.cc3
-rw-r--r--third_party/libwebrtc/modules/audio_coding/audio_network_adaptor/debug_dump_writer.cc3
-rw-r--r--third_party/libwebrtc/modules/audio_coding/audio_network_adaptor/debug_dump_writer.h4
-rw-r--r--third_party/libwebrtc/modules/audio_coding/audio_network_adaptor_config_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/audio_coding/audio_network_adaptor_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/audio_coding/default_neteq_factory_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/audio_coding/g711_c_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/audio_coding/g711_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/audio_coding/g722_c_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/audio_coding/g722_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/audio_coding/ilbc_c_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/audio_coding/ilbc_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/audio_coding/isac_bwinfo_gn/moz.build7
-rw-r--r--third_party/libwebrtc/modules/audio_coding/isac_vad_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/audio_coding/legacy_encoded_audio_frame_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/audio_coding/neteq/decision_logic.cc30
-rw-r--r--third_party/libwebrtc/modules/audio_coding/neteq/decision_logic.h16
-rw-r--r--third_party/libwebrtc/modules/audio_coding/neteq/decision_logic_unittest.cc62
-rw-r--r--third_party/libwebrtc/modules/audio_coding/neteq/mock/mock_packet_arrival_history.h32
-rw-r--r--third_party/libwebrtc/modules/audio_coding/neteq/mock/mock_packet_buffer.h47
-rw-r--r--third_party/libwebrtc/modules/audio_coding/neteq/neteq_impl.cc185
-rw-r--r--third_party/libwebrtc/modules/audio_coding/neteq/neteq_impl.h8
-rw-r--r--third_party/libwebrtc/modules/audio_coding/neteq/neteq_impl_unittest.cc99
-rw-r--r--third_party/libwebrtc/modules/audio_coding/neteq/neteq_network_stats_unittest.cc7
-rw-r--r--third_party/libwebrtc/modules/audio_coding/neteq/neteq_unittest.cc20
-rw-r--r--third_party/libwebrtc/modules/audio_coding/neteq/packet_arrival_history.h5
-rw-r--r--third_party/libwebrtc/modules/audio_coding/neteq/packet_buffer.cc200
-rw-r--r--third_party/libwebrtc/modules/audio_coding/neteq/packet_buffer.h61
-rw-r--r--third_party/libwebrtc/modules/audio_coding/neteq/packet_buffer_unittest.cc468
-rw-r--r--third_party/libwebrtc/modules/audio_coding/neteq/test/neteq_decoding_test.cc4
-rw-r--r--third_party/libwebrtc/modules/audio_coding/neteq/test/result_sink.cc5
-rw-r--r--third_party/libwebrtc/modules/audio_coding/neteq_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/audio_coding/pcm16b_c_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/audio_coding/pcm16b_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/audio_coding/red_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/audio_coding/webrtc_cng_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/audio_coding/webrtc_multiopus_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/audio_coding/webrtc_opus_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/audio_coding/webrtc_opus_wrapper_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/audio_device/BUILD.gn2
-rw-r--r--third_party/libwebrtc/modules/audio_device/audio_device_gn/moz.build7
-rw-r--r--third_party/libwebrtc/modules/audio_device/include/audio_device.h4
-rw-r--r--third_party/libwebrtc/modules/audio_device/include/fake_audio_device.h4
-rw-r--r--third_party/libwebrtc/modules/audio_mixer/audio_frame_manipulator_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/audio_mixer/audio_mixer_impl_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/audio_processing/BUILD.gn5
-rw-r--r--third_party/libwebrtc/modules/audio_processing/aec3/adaptive_fir_filter_erl_gn/moz.build7
-rw-r--r--third_party/libwebrtc/modules/audio_processing/aec3/adaptive_fir_filter_gn/moz.build7
-rw-r--r--third_party/libwebrtc/modules/audio_processing/aec3/aec3_avx2_gn/moz.build4
-rw-r--r--third_party/libwebrtc/modules/audio_processing/aec3/aec3_common_gn/moz.build7
-rw-r--r--third_party/libwebrtc/modules/audio_processing/aec3/aec3_fft_gn/moz.build7
-rw-r--r--third_party/libwebrtc/modules/audio_processing/aec3/aec3_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/audio_processing/aec3/fft_data_gn/moz.build7
-rw-r--r--third_party/libwebrtc/modules/audio_processing/aec3/matched_filter_gn/moz.build7
-rw-r--r--third_party/libwebrtc/modules/audio_processing/aec3/render_buffer_gn/moz.build7
-rw-r--r--third_party/libwebrtc/modules/audio_processing/aec3/vector_math_gn/moz.build7
-rw-r--r--third_party/libwebrtc/modules/audio_processing/aec_dump/BUILD.gn1
-rw-r--r--third_party/libwebrtc/modules/audio_processing/aec_dump/aec_dump_gn/moz.build7
-rw-r--r--third_party/libwebrtc/modules/audio_processing/aec_dump/aec_dump_impl.h3
-rw-r--r--third_party/libwebrtc/modules/audio_processing/aec_dump/capture_stream_info.h3
-rw-r--r--third_party/libwebrtc/modules/audio_processing/aec_dump/null_aec_dump_factory_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/audio_processing/aec_dump_interface_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/audio_processing/aecm/aecm_core_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/audio_processing/agc/agc_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/audio_processing/agc/gain_control_interface_gn/moz.build7
-rw-r--r--third_party/libwebrtc/modules/audio_processing/agc/legacy_agc_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/audio_processing/agc/level_estimation_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/audio_processing/agc2/adaptive_digital_gain_controller_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/audio_processing/agc2/biquad_filter_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/audio_processing/agc2/clipping_predictor_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/audio_processing/agc2/common_gn/moz.build7
-rw-r--r--third_party/libwebrtc/modules/audio_processing/agc2/cpu_features_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/audio_processing/agc2/fixed_digital_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/audio_processing/agc2/gain_applier_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/audio_processing/agc2/gain_map_gn/moz.build7
-rw-r--r--third_party/libwebrtc/modules/audio_processing/agc2/input_volume_controller_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/audio_processing/agc2/input_volume_stats_reporter_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/audio_processing/agc2/noise_level_estimator_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/audio_processing/agc2/rnn_vad/rnn_vad_auto_correlation_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/audio_processing/agc2/rnn_vad/rnn_vad_common_gn/moz.build7
-rw-r--r--third_party/libwebrtc/modules/audio_processing/agc2/rnn_vad/rnn_vad_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/audio_processing/agc2/rnn_vad/rnn_vad_layers_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/audio_processing/agc2/rnn_vad/rnn_vad_lp_residual_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/audio_processing/agc2/rnn_vad/rnn_vad_pitch_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/audio_processing/agc2/rnn_vad/rnn_vad_ring_buffer_gn/moz.build7
-rw-r--r--third_party/libwebrtc/modules/audio_processing/agc2/rnn_vad/rnn_vad_sequence_buffer_gn/moz.build7
-rw-r--r--third_party/libwebrtc/modules/audio_processing/agc2/rnn_vad/rnn_vad_spectral_features_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/audio_processing/agc2/rnn_vad/rnn_vad_symmetric_matrix_buffer_gn/moz.build7
-rw-r--r--third_party/libwebrtc/modules/audio_processing/agc2/rnn_vad/vector_math_avx2_gn/moz.build4
-rw-r--r--third_party/libwebrtc/modules/audio_processing/agc2/rnn_vad/vector_math_gn/moz.build7
-rw-r--r--third_party/libwebrtc/modules/audio_processing/agc2/saturation_protector_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/audio_processing/agc2/speech_level_estimator_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/audio_processing/agc2/vad_wrapper_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/audio_processing/api_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/audio_processing/apm_logging_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/audio_processing/audio_buffer_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/audio_processing/audio_frame_proxies_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/audio_processing/audio_frame_view_gn/moz.build7
-rw-r--r--third_party/libwebrtc/modules/audio_processing/audio_processing_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/audio_processing/audio_processing_impl.h1
-rw-r--r--third_party/libwebrtc/modules/audio_processing/audio_processing_impl_unittest.cc2
-rw-r--r--third_party/libwebrtc/modules/audio_processing/audio_processing_statistics_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/audio_processing/audio_processing_unittest.cc6
-rw-r--r--third_party/libwebrtc/modules/audio_processing/capture_levels_adjuster/capture_levels_adjuster_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/audio_processing/gain_controller2_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/audio_processing/high_pass_filter_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/audio_processing/include/audio_processing.h6
-rw-r--r--third_party/libwebrtc/modules/audio_processing/ns/ns_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/audio_processing/optionally_built_submodule_creators_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/audio_processing/rms_level_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/audio_processing/test/aec_dump_based_simulator.h3
-rw-r--r--third_party/libwebrtc/modules/audio_processing/test/debug_dump_replayer.h4
-rw-r--r--third_party/libwebrtc/modules/audio_processing/test/protobuf_utils.h4
-rw-r--r--third_party/libwebrtc/modules/audio_processing/transient/transient_suppressor_api_gn/moz.build7
-rw-r--r--third_party/libwebrtc/modules/audio_processing/transient/transient_suppressor_impl_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/audio_processing/transient/voice_probability_delay_unit_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/audio_processing/utility/cascaded_biquad_filter_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/audio_processing/utility/legacy_delay_estimator_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/audio_processing/utility/pffft_wrapper_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/audio_processing/vad/vad_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/congestion_controller_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/alr_detector_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/delay_based_bwe_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/estimators_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/goog_cc_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/link_capacity_estimator_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/loss_based_bwe_v1_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/loss_based_bwe_v2.cc184
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/loss_based_bwe_v2.h19
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/loss_based_bwe_v2_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/loss_based_bwe_v2_test.cc407
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/probe_controller_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/pushback_controller_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.cc30
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.h4
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/send_side_bwe_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/rtp/control_handler_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/rtp/transport_feedback_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/wayland/base_capturer_pipewire.cc1
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/wayland/shared_screencast_stream.cc28
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/mac/desktop_frame_provider.h2
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/mac/screen_capturer_mac.mm4
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/dxgi_duplicator_controller.h2
-rw-r--r--third_party/libwebrtc/modules/module_api_gn/moz.build7
-rw-r--r--third_party/libwebrtc/modules/module_api_public_gn/moz.build7
-rw-r--r--third_party/libwebrtc/modules/module_fec_api_gn/moz.build7
-rw-r--r--third_party/libwebrtc/modules/pacing/interval_budget_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/pacing/pacing_controller.cc2
-rw-r--r--third_party/libwebrtc/modules/pacing/pacing_controller.h5
-rw-r--r--third_party/libwebrtc/modules/pacing/pacing_controller_unittest.cc144
-rw-r--r--third_party/libwebrtc/modules/pacing/pacing_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/pacing/task_queue_paced_sender.cc34
-rw-r--r--third_party/libwebrtc/modules/pacing/task_queue_paced_sender.h31
-rw-r--r--third_party/libwebrtc/modules/pacing/task_queue_paced_sender_unittest.cc44
-rw-r--r--third_party/libwebrtc/modules/portal/pipewire_utils.h75
-rw-r--r--third_party/libwebrtc/modules/remote_bitrate_estimator/aimd_rate_control.h2
-rw-r--r--third_party/libwebrtc/modules/remote_bitrate_estimator/aimd_rate_control_unittest.cc18
-rw-r--r--third_party/libwebrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/BUILD.gn11
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/leb128_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/rtp_rtcp_format_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/rtp_rtcp_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/rtp_video_header_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/flexfec_header_reader_writer.cc18
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/flexfec_header_reader_writer_unittest.cc121
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtp_format.cc9
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtp_packetizer_av1.cc3
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtp_packetizer_h265.cc350
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtp_packetizer_h265.h66
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtp_packetizer_h265_unittest.cc525
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtp_rtcp_impl2.cc4
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtp_sender_audio.cc3
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtp_sender_audio.h3
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtp_sender_audio_unittest.cc15
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate.cc50
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate.h3
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate_unittest.cc30
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtp_video_stream_receiver_frame_transformer_delegate.cc26
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtp_video_stream_receiver_frame_transformer_delegate.h5
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtp_video_stream_receiver_frame_transformer_delegate_unittest.cc23
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/video_rtp_depacketizer_av1.cc3
-rw-r--r--third_party/libwebrtc/modules/third_party/fft/fft_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/third_party/g711/g711_3p_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/third_party/g722/g722_3p_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/utility/utility_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/video_capture/linux/camera_portal.cc16
-rw-r--r--third_party/libwebrtc/modules/video_capture/linux/device_info_pipewire.cc8
-rw-r--r--third_party/libwebrtc/modules/video_capture/linux/device_info_pipewire.h2
-rw-r--r--third_party/libwebrtc/modules/video_capture/linux/device_info_v4l2.cc18
-rw-r--r--third_party/libwebrtc/modules/video_capture/linux/video_capture_pipewire.cc46
-rw-r--r--third_party/libwebrtc/modules/video_capture/linux/video_capture_v4l2.cc2
-rw-r--r--third_party/libwebrtc/modules/video_capture/video_capture.h2
-rw-r--r--third_party/libwebrtc/modules/video_capture/video_capture_internal_impl_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/video_capture/video_capture_module_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/video_coding/BUILD.gn67
-rw-r--r--third_party/libwebrtc/modules/video_coding/chain_diff_calculator_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/video_coding/codec_globals_headers_gn/moz.build7
-rw-r--r--third_party/libwebrtc/modules/video_coding/codecs/av1/av1_svc_config_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/video_coding/codecs/test/video_codec_analyzer.cc193
-rw-r--r--third_party/libwebrtc/modules/video_coding/codecs/test/video_codec_analyzer.h75
-rw-r--r--third_party/libwebrtc/modules/video_coding/codecs/test/video_codec_analyzer_unittest.cc127
-rw-r--r--third_party/libwebrtc/modules/video_coding/codecs/test/video_codec_stats_impl.cc278
-rw-r--r--third_party/libwebrtc/modules/video_coding/codecs/test/video_codec_stats_impl.h62
-rw-r--r--third_party/libwebrtc/modules/video_coding/codecs/test/video_codec_stats_impl_unittest.cc148
-rw-r--r--third_party/libwebrtc/modules/video_coding/codecs/test/video_codec_test.cc888
-rw-r--r--third_party/libwebrtc/modules/video_coding/codecs/test/video_codec_tester_impl.cc437
-rw-r--r--third_party/libwebrtc/modules/video_coding/codecs/test/video_codec_tester_impl.h45
-rw-r--r--third_party/libwebrtc/modules/video_coding/codecs/test/video_codec_tester_impl_unittest.cc205
-rw-r--r--third_party/libwebrtc/modules/video_coding/encoded_frame_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/video_coding/frame_dependencies_calculator_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/video_coding/frame_helpers_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/video_coding/generic_decoder.cc13
-rw-r--r--third_party/libwebrtc/modules/video_coding/include/video_codec_interface.h16
-rw-r--r--third_party/libwebrtc/modules/video_coding/nack_requester_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/video_coding/packet_buffer_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/video_coding/svc/scalability_mode_util_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/video_coding/svc/scalability_structures_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/video_coding/svc/scalable_video_controller_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/video_coding/svc/svc_rate_allocator_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/video_coding/timing/decode_time_percentile_filter_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/video_coding/timing/frame_delay_variation_kalman_filter_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/video_coding/timing/inter_frame_delay_variation_calculator_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/video_coding/timing/jitter_estimator_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/video_coding/timing/rtt_filter_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/video_coding/timing/timestamp_extrapolator_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/video_coding/timing/timing_module_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/video_coding/video_codec_interface_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/video_coding/video_coding_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/video_coding/video_coding_utility_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/video_coding/webrtc_libvpx_interface_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/video_coding/webrtc_vp8_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/video_coding/webrtc_vp8_scalability_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/video_coding/webrtc_vp8_temporal_layers_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/video_coding/webrtc_vp9_gn/moz.build5
-rw-r--r--third_party/libwebrtc/modules/video_coding/webrtc_vp9_helpers_gn/moz.build5
-rw-r--r--third_party/libwebrtc/moz-patch-stack/0001.patch18
-rw-r--r--third_party/libwebrtc/moz-patch-stack/0002.patch2
-rw-r--r--third_party/libwebrtc/moz-patch-stack/0005.patch6
-rw-r--r--third_party/libwebrtc/moz-patch-stack/0006.patch2
-rw-r--r--third_party/libwebrtc/moz-patch-stack/0007.patch2
-rw-r--r--third_party/libwebrtc/moz-patch-stack/0008.patch8
-rw-r--r--third_party/libwebrtc/moz-patch-stack/0009.patch4
-rw-r--r--third_party/libwebrtc/moz-patch-stack/0030.patch188
-rw-r--r--third_party/libwebrtc/moz-patch-stack/0031.patch4
-rw-r--r--third_party/libwebrtc/moz-patch-stack/0033.patch16
-rw-r--r--third_party/libwebrtc/moz-patch-stack/0041.patch4
-rw-r--r--third_party/libwebrtc/moz-patch-stack/0042.patch10
-rw-r--r--third_party/libwebrtc/moz-patch-stack/0044.patch6
-rw-r--r--third_party/libwebrtc/moz-patch-stack/0049.patch2
-rw-r--r--third_party/libwebrtc/moz-patch-stack/0050.patch6
-rw-r--r--third_party/libwebrtc/moz-patch-stack/0052.patch13
-rw-r--r--third_party/libwebrtc/moz-patch-stack/0053.patch4
-rw-r--r--third_party/libwebrtc/moz-patch-stack/0054.patch6
-rw-r--r--third_party/libwebrtc/moz-patch-stack/0059.patch6
-rw-r--r--third_party/libwebrtc/moz-patch-stack/0061.patch7
-rw-r--r--third_party/libwebrtc/moz-patch-stack/0063.patch6
-rw-r--r--third_party/libwebrtc/moz-patch-stack/0064.patch6
-rw-r--r--third_party/libwebrtc/moz-patch-stack/0066.patch6
-rw-r--r--third_party/libwebrtc/moz-patch-stack/0067.patch4
-rw-r--r--third_party/libwebrtc/moz-patch-stack/0068.patch2
-rw-r--r--third_party/libwebrtc/moz-patch-stack/0078.patch109
-rw-r--r--third_party/libwebrtc/moz-patch-stack/0079.patch76
-rw-r--r--third_party/libwebrtc/moz-patch-stack/0080.patch54
-rw-r--r--third_party/libwebrtc/moz-patch-stack/0081.patch49
-rw-r--r--third_party/libwebrtc/moz-patch-stack/0082.patch38
-rw-r--r--third_party/libwebrtc/moz-patch-stack/0083.patch42
-rw-r--r--third_party/libwebrtc/moz-patch-stack/0084.patch43
-rw-r--r--third_party/libwebrtc/moz-patch-stack/0085.patch89
-rw-r--r--third_party/libwebrtc/moz-patch-stack/0086.patch212
-rw-r--r--third_party/libwebrtc/moz-patch-stack/0087.patch186
-rw-r--r--third_party/libwebrtc/moz-patch-stack/0088.patch64
-rw-r--r--third_party/libwebrtc/moz-patch-stack/0089.patch63
-rw-r--r--third_party/libwebrtc/moz-patch-stack/0090.patch81
-rw-r--r--third_party/libwebrtc/moz-patch-stack/0091.patch123
-rw-r--r--third_party/libwebrtc/moz-patch-stack/0092.patch115
-rw-r--r--third_party/libwebrtc/moz-patch-stack/0093.patch54
-rw-r--r--third_party/libwebrtc/moz-patch-stack/0094.patch62
-rw-r--r--third_party/libwebrtc/moz-patch-stack/0095.patch55
-rw-r--r--third_party/libwebrtc/moz-patch-stack/0096.patch44
-rw-r--r--third_party/libwebrtc/moz-patch-stack/0097.patch2207
-rw-r--r--third_party/libwebrtc/moz-patch-stack/0098.patch36922
-rw-r--r--third_party/libwebrtc/moz-patch-stack/0099.patch35372
-rw-r--r--third_party/libwebrtc/moz-patch-stack/0100.patch78
-rw-r--r--third_party/libwebrtc/moz-patch-stack/0101.patch88
-rw-r--r--third_party/libwebrtc/moz-patch-stack/0102.patch213
-rw-r--r--third_party/libwebrtc/moz-patch-stack/0103.patch127
-rw-r--r--third_party/libwebrtc/moz-patch-stack/0104.patch146
-rw-r--r--third_party/libwebrtc/moz-patch-stack/0105.patch93
-rw-r--r--third_party/libwebrtc/moz-patch-stack/0106.patch208
-rw-r--r--third_party/libwebrtc/moz-patch-stack/058bfe3ae3.no-op-cherry-pick-msg1
-rw-r--r--third_party/libwebrtc/moz-patch-stack/334e9133dc.no-op-cherry-pick-msg1
-rw-r--r--third_party/libwebrtc/moz-patch-stack/a9d497b52d.no-op-cherry-pick-msg1
-rw-r--r--third_party/libwebrtc/moz.build3
-rw-r--r--third_party/libwebrtc/net/dcsctp/common/BUILD.gn8
-rw-r--r--third_party/libwebrtc/net/dcsctp/fuzzers/dcsctp_fuzzers.h2
-rw-r--r--third_party/libwebrtc/net/dcsctp/packet/BUILD.gn3
-rw-r--r--third_party/libwebrtc/net/dcsctp/packet/chunk/sack_chunk.cc2
-rw-r--r--third_party/libwebrtc/net/dcsctp/packet/error_cause/missing_mandatory_parameter_cause.cc4
-rw-r--r--third_party/libwebrtc/net/dcsctp/packet/parameter/supported_extensions_parameter.cc4
-rw-r--r--third_party/libwebrtc/net/dcsctp/public/BUILD.gn2
-rw-r--r--third_party/libwebrtc/net/dcsctp/public/dcsctp_socket.h15
-rw-r--r--third_party/libwebrtc/net/dcsctp/public/types.h10
-rw-r--r--third_party/libwebrtc/net/dcsctp/rx/BUILD.gn2
-rw-r--r--third_party/libwebrtc/net/dcsctp/rx/data_tracker_test.cc19
-rw-r--r--third_party/libwebrtc/net/dcsctp/rx/reassembly_queue.cc51
-rw-r--r--third_party/libwebrtc/net/dcsctp/rx/reassembly_queue.h7
-rw-r--r--third_party/libwebrtc/net/dcsctp/rx/reassembly_queue_test.cc27
-rw-r--r--third_party/libwebrtc/net/dcsctp/socket/BUILD.gn6
-rw-r--r--third_party/libwebrtc/net/dcsctp/socket/callback_deferrer.cc4
-rw-r--r--third_party/libwebrtc/net/dcsctp/socket/callback_deferrer.h1
-rw-r--r--third_party/libwebrtc/net/dcsctp/socket/context.h7
-rw-r--r--third_party/libwebrtc/net/dcsctp/socket/dcsctp_socket.cc44
-rw-r--r--third_party/libwebrtc/net/dcsctp/socket/dcsctp_socket.h6
-rw-r--r--third_party/libwebrtc/net/dcsctp/socket/dcsctp_socket_network_test.cc16
-rw-r--r--third_party/libwebrtc/net/dcsctp/socket/dcsctp_socket_test.cc121
-rw-r--r--third_party/libwebrtc/net/dcsctp/socket/heartbeat_handler.cc43
-rw-r--r--third_party/libwebrtc/net/dcsctp/socket/heartbeat_handler.h6
-rw-r--r--third_party/libwebrtc/net/dcsctp/socket/heartbeat_handler_test.cc19
-rw-r--r--third_party/libwebrtc/net/dcsctp/socket/mock_context.h7
-rw-r--r--third_party/libwebrtc/net/dcsctp/socket/mock_dcsctp_socket_callbacks.h12
-rw-r--r--third_party/libwebrtc/net/dcsctp/socket/stream_reset_handler.cc8
-rw-r--r--third_party/libwebrtc/net/dcsctp/socket/stream_reset_handler.h5
-rw-r--r--third_party/libwebrtc/net/dcsctp/socket/stream_reset_handler_test.cc21
-rw-r--r--third_party/libwebrtc/net/dcsctp/socket/transmission_control_block.cc45
-rw-r--r--third_party/libwebrtc/net/dcsctp/socket/transmission_control_block.h18
-rw-r--r--third_party/libwebrtc/net/dcsctp/timer/BUILD.gn4
-rw-r--r--third_party/libwebrtc/net/dcsctp/timer/fake_timeout.h37
-rw-r--r--third_party/libwebrtc/net/dcsctp/timer/task_queue_timeout.cc24
-rw-r--r--third_party/libwebrtc/net/dcsctp/timer/task_queue_timeout.h8
-rw-r--r--third_party/libwebrtc/net/dcsctp/timer/timer.cc35
-rw-r--r--third_party/libwebrtc/net/dcsctp/timer/timer.h40
-rw-r--r--third_party/libwebrtc/net/dcsctp/timer/timer_test.cc203
-rw-r--r--third_party/libwebrtc/net/dcsctp/tx/BUILD.gn12
-rw-r--r--third_party/libwebrtc/net/dcsctp/tx/mock_send_queue.h10
-rw-r--r--third_party/libwebrtc/net/dcsctp/tx/outstanding_data.cc261
-rw-r--r--third_party/libwebrtc/net/dcsctp/tx/outstanding_data.h67
-rw-r--r--third_party/libwebrtc/net/dcsctp/tx/outstanding_data_test.cc58
-rw-r--r--third_party/libwebrtc/net/dcsctp/tx/retransmission_queue.cc69
-rw-r--r--third_party/libwebrtc/net/dcsctp/tx/retransmission_queue.h22
-rw-r--r--third_party/libwebrtc/net/dcsctp/tx/retransmission_queue_test.cc248
-rw-r--r--third_party/libwebrtc/net/dcsctp/tx/retransmission_timeout.cc19
-rw-r--r--third_party/libwebrtc/net/dcsctp/tx/retransmission_timeout.h22
-rw-r--r--third_party/libwebrtc/net/dcsctp/tx/retransmission_timeout_test.cc153
-rw-r--r--third_party/libwebrtc/net/dcsctp/tx/rr_send_queue.cc15
-rw-r--r--third_party/libwebrtc/net/dcsctp/tx/rr_send_queue.h11
-rw-r--r--third_party/libwebrtc/net/dcsctp/tx/rr_send_queue_test.cc21
-rw-r--r--third_party/libwebrtc/net/dcsctp/tx/send_queue.h6
-rw-r--r--third_party/libwebrtc/net/dcsctp/tx/stream_scheduler.cc6
-rw-r--r--third_party/libwebrtc/net/dcsctp/tx/stream_scheduler.h8
-rw-r--r--third_party/libwebrtc/net/dcsctp/tx/stream_scheduler_test.cc149
-rw-r--r--third_party/libwebrtc/p2p/BUILD.gn11
-rw-r--r--third_party/libwebrtc/p2p/base/async_stun_tcp_socket.cc33
-rw-r--r--third_party/libwebrtc/p2p/base/async_stun_tcp_socket.h2
-rw-r--r--third_party/libwebrtc/p2p/base/async_stun_tcp_socket_unittest.cc44
-rw-r--r--third_party/libwebrtc/p2p/base/basic_async_resolver_factory.cc33
-rw-r--r--third_party/libwebrtc/p2p/base/basic_async_resolver_factory.h46
-rw-r--r--third_party/libwebrtc/p2p/base/basic_async_resolver_factory_unittest.cc93
-rw-r--r--third_party/libwebrtc/p2p/base/basic_packet_socket_factory.cc5
-rw-r--r--third_party/libwebrtc/p2p/base/basic_packet_socket_factory.h4
-rw-r--r--third_party/libwebrtc/p2p/base/connection.cc25
-rw-r--r--third_party/libwebrtc/p2p/base/connection.h8
-rw-r--r--third_party/libwebrtc/p2p/base/fake_ice_transport.h4
-rw-r--r--third_party/libwebrtc/p2p/base/ice_transport_internal.cc8
-rw-r--r--third_party/libwebrtc/p2p/base/ice_transport_internal.h51
-rw-r--r--third_party/libwebrtc/p2p/base/mock_async_resolver.h62
-rw-r--r--third_party/libwebrtc/p2p/base/p2p_transport_channel.cc42
-rw-r--r--third_party/libwebrtc/p2p/base/p2p_transport_channel.h2
-rw-r--r--third_party/libwebrtc/p2p/base/p2p_transport_channel_unittest.cc56
-rw-r--r--third_party/libwebrtc/p2p/base/port.cc18
-rw-r--r--third_party/libwebrtc/p2p/base/port.h23
-rw-r--r--third_party/libwebrtc/p2p/base/port_unittest.cc174
-rw-r--r--third_party/libwebrtc/p2p/base/pseudo_tcp.cc3
-rw-r--r--third_party/libwebrtc/p2p/base/stun_dictionary.cc2
-rw-r--r--third_party/libwebrtc/p2p/base/stun_port.cc38
-rw-r--r--third_party/libwebrtc/p2p/base/stun_port.h11
-rw-r--r--third_party/libwebrtc/p2p/base/stun_port_unittest.cc40
-rw-r--r--third_party/libwebrtc/p2p/base/stun_request.cc25
-rw-r--r--third_party/libwebrtc/p2p/base/stun_request.h7
-rw-r--r--third_party/libwebrtc/p2p/base/stun_request_unittest.cc4
-rw-r--r--third_party/libwebrtc/p2p/base/stun_server.cc27
-rw-r--r--third_party/libwebrtc/p2p/base/stun_server.h14
-rw-r--r--third_party/libwebrtc/p2p/base/stun_server_unittest.cc9
-rw-r--r--third_party/libwebrtc/p2p/base/tcp_port.cc30
-rw-r--r--third_party/libwebrtc/p2p/base/tcp_port.h11
-rw-r--r--third_party/libwebrtc/p2p/base/test_stun_server.cc19
-rw-r--r--third_party/libwebrtc/p2p/base/test_stun_server.h16
-rw-r--r--third_party/libwebrtc/p2p/base/turn_port.cc51
-rw-r--r--third_party/libwebrtc/p2p/base/turn_port.h11
-rw-r--r--third_party/libwebrtc/p2p/base/turn_port_unittest.cc31
-rw-r--r--third_party/libwebrtc/p2p/base/turn_server.cc79
-rw-r--r--third_party/libwebrtc/p2p/base/turn_server.h29
-rw-r--r--third_party/libwebrtc/p2p/client/basic_port_allocator.cc22
-rw-r--r--third_party/libwebrtc/p2p/client/basic_port_allocator.h8
-rw-r--r--third_party/libwebrtc/p2p/client/basic_port_allocator_unittest.cc4
-rw-r--r--third_party/libwebrtc/p2p/stunprober/stun_prober.cc34
-rw-r--r--third_party/libwebrtc/p2p/stunprober/stun_prober.h2
-rw-r--r--third_party/libwebrtc/p2p/stunprober/stun_prober_unittest.cc14
-rw-r--r--third_party/libwebrtc/pc/BUILD.gn50
-rw-r--r--third_party/libwebrtc/pc/audio_rtp_receiver.cc2
-rw-r--r--third_party/libwebrtc/pc/audio_rtp_receiver.h3
-rw-r--r--third_party/libwebrtc/pc/audio_rtp_receiver_unittest.cc2
-rw-r--r--third_party/libwebrtc/pc/audio_track.h2
-rw-r--r--third_party/libwebrtc/pc/channel.cc30
-rw-r--r--third_party/libwebrtc/pc/channel_unittest.cc28
-rw-r--r--third_party/libwebrtc/pc/connection_context.cc21
-rw-r--r--third_party/libwebrtc/pc/connection_context.h21
-rw-r--r--third_party/libwebrtc/pc/data_channel_controller_unittest.cc2
-rw-r--r--third_party/libwebrtc/pc/data_channel_integrationtest.cc10
-rw-r--r--third_party/libwebrtc/pc/data_channel_unittest.cc17
-rw-r--r--third_party/libwebrtc/pc/dtls_srtp_transport.h9
-rw-r--r--third_party/libwebrtc/pc/ice_server_parsing_unittest.cc7
-rw-r--r--third_party/libwebrtc/pc/ice_transport_unittest.cc2
-rw-r--r--third_party/libwebrtc/pc/jsep_transport_controller.cc228
-rw-r--r--third_party/libwebrtc/pc/jsep_transport_controller.h52
-rw-r--r--third_party/libwebrtc/pc/jsep_transport_controller_unittest.cc591
-rw-r--r--third_party/libwebrtc/pc/legacy_stats_collector.cc12
-rw-r--r--third_party/libwebrtc/pc/legacy_stats_collector.h6
-rw-r--r--third_party/libwebrtc/pc/media_factory.h45
-rw-r--r--third_party/libwebrtc/pc/media_session.cc865
-rw-r--r--third_party/libwebrtc/pc/media_session.h43
-rw-r--r--third_party/libwebrtc/pc/media_session_unittest.cc852
-rw-r--r--third_party/libwebrtc/pc/media_stream_unittest.cc10
-rw-r--r--third_party/libwebrtc/pc/peer_connection.cc94
-rw-r--r--third_party/libwebrtc/pc/peer_connection.h29
-rw-r--r--third_party/libwebrtc/pc/peer_connection_crypto_unittest.cc2
-rw-r--r--third_party/libwebrtc/pc/peer_connection_data_channel_unittest.cc7
-rw-r--r--third_party/libwebrtc/pc/peer_connection_encodings_integrationtest.cc214
-rw-r--r--third_party/libwebrtc/pc/peer_connection_factory.cc76
-rw-r--r--third_party/libwebrtc/pc/peer_connection_factory.h9
-rw-r--r--third_party/libwebrtc/pc/peer_connection_factory_proxy.h4
-rw-r--r--third_party/libwebrtc/pc/peer_connection_factory_unittest.cc218
-rw-r--r--third_party/libwebrtc/pc/peer_connection_field_trial_tests.cc23
-rw-r--r--third_party/libwebrtc/pc/peer_connection_header_extension_unittest.cc17
-rw-r--r--third_party/libwebrtc/pc/peer_connection_histogram_unittest.cc128
-rw-r--r--third_party/libwebrtc/pc/peer_connection_ice_unittest.cc6
-rw-r--r--third_party/libwebrtc/pc/peer_connection_integrationtest.cc376
-rw-r--r--third_party/libwebrtc/pc/peer_connection_interface_unittest.cc235
-rw-r--r--third_party/libwebrtc/pc/peer_connection_jsep_unittest.cc12
-rw-r--r--third_party/libwebrtc/pc/peer_connection_media_unittest.cc71
-rw-r--r--third_party/libwebrtc/pc/peer_connection_rampup_tests.cc2
-rw-r--r--third_party/libwebrtc/pc/peer_connection_rtp_unittest.cc14
-rw-r--r--third_party/libwebrtc/pc/peer_connection_signaling_unittest.cc32
-rw-r--r--third_party/libwebrtc/pc/peer_connection_simulcast_unittest.cc2
-rw-r--r--third_party/libwebrtc/pc/peer_connection_svc_integrationtest.cc77
-rw-r--r--third_party/libwebrtc/pc/peer_connection_wrapper.cc3
-rw-r--r--third_party/libwebrtc/pc/rtc_stats_collector.cc4
-rw-r--r--third_party/libwebrtc/pc/rtc_stats_collector.h2
-rw-r--r--third_party/libwebrtc/pc/rtc_stats_collector_unittest.cc25
-rw-r--r--third_party/libwebrtc/pc/rtc_stats_traversal_unittest.cc2
-rw-r--r--third_party/libwebrtc/pc/rtp_sender.cc16
-rw-r--r--third_party/libwebrtc/pc/rtp_sender.h4
-rw-r--r--third_party/libwebrtc/pc/rtp_sender_receiver_unittest.cc80
-rw-r--r--third_party/libwebrtc/pc/rtp_transceiver.cc6
-rw-r--r--third_party/libwebrtc/pc/rtp_transceiver.h10
-rw-r--r--third_party/libwebrtc/pc/rtp_transceiver_unittest.cc19
-rw-r--r--third_party/libwebrtc/pc/rtp_transport.cc19
-rw-r--r--third_party/libwebrtc/pc/rtp_transport.h2
-rw-r--r--third_party/libwebrtc/pc/rtp_transport_internal.h4
-rw-r--r--third_party/libwebrtc/pc/rtp_transport_unittest.cc24
-rw-r--r--third_party/libwebrtc/pc/sctp_transport.h2
-rw-r--r--third_party/libwebrtc/pc/sctp_utils.cc2
-rw-r--r--third_party/libwebrtc/pc/sctp_utils_unittest.cc4
-rw-r--r--third_party/libwebrtc/pc/sdp_offer_answer.cc105
-rw-r--r--third_party/libwebrtc/pc/sdp_offer_answer.h4
-rw-r--r--third_party/libwebrtc/pc/sdp_offer_answer_unittest.cc23
-rw-r--r--third_party/libwebrtc/pc/slow_peer_connection_integration_test.cc18
-rw-r--r--third_party/libwebrtc/pc/srtp_session.cc6
-rw-r--r--third_party/libwebrtc/pc/srtp_session.h8
-rw-r--r--third_party/libwebrtc/pc/srtp_session_unittest.cc32
-rw-r--r--third_party/libwebrtc/pc/srtp_transport.cc95
-rw-r--r--third_party/libwebrtc/pc/srtp_transport.h7
-rw-r--r--third_party/libwebrtc/pc/srtp_transport_unittest.cc64
-rw-r--r--third_party/libwebrtc/pc/test/android_test_initializer.cc2
-rw-r--r--third_party/libwebrtc/pc/test/enable_fake_media.cc63
-rw-r--r--third_party/libwebrtc/pc/test/enable_fake_media.h38
-rw-r--r--third_party/libwebrtc/pc/test/fake_peer_connection_base.h2
-rw-r--r--third_party/libwebrtc/pc/test/fake_peer_connection_for_stats.h16
-rw-r--r--third_party/libwebrtc/pc/test/fake_periodic_video_source.h4
-rw-r--r--third_party/libwebrtc/pc/test/integration_test_helpers.cc2
-rw-r--r--third_party/libwebrtc/pc/test/integration_test_helpers.h290
-rw-r--r--third_party/libwebrtc/pc/test/mock_peer_connection_observers.h19
-rw-r--r--third_party/libwebrtc/pc/test/rtp_transport_test_util.h17
-rw-r--r--third_party/libwebrtc/pc/test/svc_e2e_tests.cc42
-rw-r--r--third_party/libwebrtc/pc/video_rtp_receiver_unittest.cc2
-rw-r--r--third_party/libwebrtc/pc/video_rtp_track_source_unittest.cc4
-rw-r--r--third_party/libwebrtc/pc/video_track.h2
-rw-r--r--third_party/libwebrtc/pc/video_track_source_proxy.h2
-rw-r--r--third_party/libwebrtc/pc/webrtc_sdp.cc8
-rw-r--r--third_party/libwebrtc/pc/webrtc_sdp_unittest.cc21
-rw-r--r--third_party/libwebrtc/rtc_base/BUILD.gn49
-rw-r--r--third_party/libwebrtc/rtc_base/async_dns_resolver_gn/moz.build5
-rw-r--r--third_party/libwebrtc/rtc_base/async_packet_socket.cc37
-rw-r--r--third_party/libwebrtc/rtc_base/async_packet_socket.h22
-rw-r--r--third_party/libwebrtc/rtc_base/async_packet_socket_gn/moz.build7
-rw-r--r--third_party/libwebrtc/rtc_base/async_packet_socket_unittest.cc110
-rw-r--r--third_party/libwebrtc/rtc_base/async_resolver.cc239
-rw-r--r--third_party/libwebrtc/rtc_base/async_resolver.h80
-rw-r--r--third_party/libwebrtc/rtc_base/async_resolver_interface.cc19
-rw-r--r--third_party/libwebrtc/rtc_base/async_resolver_interface.h55
-rw-r--r--third_party/libwebrtc/rtc_base/async_tcp_socket.cc44
-rw-r--r--third_party/libwebrtc/rtc_base/async_tcp_socket.h7
-rw-r--r--third_party/libwebrtc/rtc_base/async_udp_socket.cc4
-rw-r--r--third_party/libwebrtc/rtc_base/audio_format_to_string_gn/moz.build5
-rw-r--r--third_party/libwebrtc/rtc_base/bit_buffer_gn/moz.build5
-rw-r--r--third_party/libwebrtc/rtc_base/bitrate_tracker_gn/moz.build5
-rw-r--r--third_party/libwebrtc/rtc_base/bitstream_reader_gn/moz.build5
-rw-r--r--third_party/libwebrtc/rtc_base/buffer_gn/moz.build7
-rw-r--r--third_party/libwebrtc/rtc_base/byte_buffer.cc54
-rw-r--r--third_party/libwebrtc/rtc_base/byte_buffer.h78
-rw-r--r--third_party/libwebrtc/rtc_base/byte_buffer_gn/moz.build5
-rw-r--r--third_party/libwebrtc/rtc_base/byte_buffer_unittest.cc59
-rw-r--r--third_party/libwebrtc/rtc_base/byte_order_gn/moz.build7
-rw-r--r--third_party/libwebrtc/rtc_base/checks_gn/moz.build5
-rw-r--r--third_party/libwebrtc/rtc_base/compile_assert_c_gn/moz.build7
-rw-r--r--third_party/libwebrtc/rtc_base/containers/flat_containers_internal_gn/moz.build5
-rw-r--r--third_party/libwebrtc/rtc_base/containers/flat_map_gn/moz.build7
-rw-r--r--third_party/libwebrtc/rtc_base/containers/flat_set_gn/moz.build7
-rw-r--r--third_party/libwebrtc/rtc_base/copy_on_write_buffer.h2
-rw-r--r--third_party/libwebrtc/rtc_base/copy_on_write_buffer_gn/moz.build5
-rw-r--r--third_party/libwebrtc/rtc_base/copy_on_write_buffer_unittest.cc12
-rw-r--r--third_party/libwebrtc/rtc_base/criticalsection_gn/moz.build5
-rw-r--r--third_party/libwebrtc/rtc_base/divide_round_gn/moz.build7
-rw-r--r--third_party/libwebrtc/rtc_base/dscp_gn/moz.build7
-rw-r--r--third_party/libwebrtc/rtc_base/event_tracer_gn/moz.build5
-rw-r--r--third_party/libwebrtc/rtc_base/experiments/alr_experiment_gn/moz.build5
-rw-r--r--third_party/libwebrtc/rtc_base/experiments/balanced_degradation_settings_gn/moz.build5
-rw-r--r--third_party/libwebrtc/rtc_base/experiments/bandwidth_quality_scaler_settings_gn/moz.build5
-rw-r--r--third_party/libwebrtc/rtc_base/experiments/cpu_speed_experiment_gn/moz.build5
-rw-r--r--third_party/libwebrtc/rtc_base/experiments/encoder_info_settings_gn/moz.build5
-rw-r--r--third_party/libwebrtc/rtc_base/experiments/field_trial_parser_gn/moz.build5
-rw-r--r--third_party/libwebrtc/rtc_base/experiments/keyframe_interval_settings_experiment_gn/moz.build5
-rw-r--r--third_party/libwebrtc/rtc_base/experiments/min_video_bitrate_experiment_gn/moz.build5
-rw-r--r--third_party/libwebrtc/rtc_base/experiments/normalize_simulcast_size_experiment_gn/moz.build5
-rw-r--r--third_party/libwebrtc/rtc_base/experiments/quality_rampup_experiment_gn/moz.build5
-rw-r--r--third_party/libwebrtc/rtc_base/experiments/quality_scaler_settings_gn/moz.build5
-rw-r--r--third_party/libwebrtc/rtc_base/experiments/quality_scaling_experiment_gn/moz.build5
-rw-r--r--third_party/libwebrtc/rtc_base/experiments/rate_control_settings_gn/moz.build5
-rw-r--r--third_party/libwebrtc/rtc_base/experiments/rtt_mult_experiment_gn/moz.build5
-rw-r--r--third_party/libwebrtc/rtc_base/experiments/stable_target_rate_experiment_gn/moz.build5
-rw-r--r--third_party/libwebrtc/rtc_base/frequency_tracker_gn/moz.build5
-rw-r--r--third_party/libwebrtc/rtc_base/gtest_prod_gn/moz.build7
-rw-r--r--third_party/libwebrtc/rtc_base/histogram_percentile_counter_gn/moz.build5
-rw-r--r--third_party/libwebrtc/rtc_base/ifaddrs_android_gn/moz.build5
-rw-r--r--third_party/libwebrtc/rtc_base/ignore_wundef_gn/moz.build7
-rw-r--r--third_party/libwebrtc/rtc_base/ip_address_gn/moz.build5
-rw-r--r--third_party/libwebrtc/rtc_base/logging_gn/moz.build5
-rw-r--r--third_party/libwebrtc/rtc_base/macromagic_gn/moz.build7
-rw-r--r--third_party/libwebrtc/rtc_base/memory/aligned_malloc_gn/moz.build5
-rw-r--r--third_party/libwebrtc/rtc_base/mod_ops_gn/moz.build7
-rw-r--r--third_party/libwebrtc/rtc_base/moving_max_counter_gn/moz.build7
-rw-r--r--third_party/libwebrtc/rtc_base/nat_unittest.cc72
-rw-r--r--third_party/libwebrtc/rtc_base/net_helpers_gn/moz.build5
-rw-r--r--third_party/libwebrtc/rtc_base/network/BUILD.gn2
-rw-r--r--third_party/libwebrtc/rtc_base/network/received_packet.cc21
-rw-r--r--third_party/libwebrtc/rtc_base/network/received_packet.h18
-rw-r--r--third_party/libwebrtc/rtc_base/network/sent_packet_gn/moz.build5
-rw-r--r--third_party/libwebrtc/rtc_base/network_constants_gn/moz.build5
-rw-r--r--third_party/libwebrtc/rtc_base/network_route_gn/moz.build5
-rw-r--r--third_party/libwebrtc/rtc_base/null_socket_server_gn/moz.build5
-rw-r--r--third_party/libwebrtc/rtc_base/one_time_event_gn/moz.build7
-rw-r--r--third_party/libwebrtc/rtc_base/openssl_adapter.cc20
-rw-r--r--third_party/libwebrtc/rtc_base/openssl_adapter.h8
-rw-r--r--third_party/libwebrtc/rtc_base/openssl_adapter_unittest.cc15
-rw-r--r--third_party/libwebrtc/rtc_base/openssl_stream_adapter.cc51
-rw-r--r--third_party/libwebrtc/rtc_base/openssl_stream_adapter.h9
-rw-r--r--third_party/libwebrtc/rtc_base/physical_socket_server.h2
-rw-r--r--third_party/libwebrtc/rtc_base/platform_thread_gn/moz.build5
-rw-r--r--third_party/libwebrtc/rtc_base/platform_thread_types_gn/moz.build5
-rw-r--r--third_party/libwebrtc/rtc_base/protobuf_utils_gn/moz.build7
-rw-r--r--third_party/libwebrtc/rtc_base/race_checker_gn/moz.build5
-rw-r--r--third_party/libwebrtc/rtc_base/random_gn/moz.build5
-rw-r--r--third_party/libwebrtc/rtc_base/rate_limiter_gn/moz.build5
-rw-r--r--third_party/libwebrtc/rtc_base/rate_statistics_gn/moz.build5
-rw-r--r--third_party/libwebrtc/rtc_base/rate_tracker_gn/moz.build5
-rw-r--r--third_party/libwebrtc/rtc_base/ref_count.h55
-rw-r--r--third_party/libwebrtc/rtc_base/refcount_gn/moz.build7
-rw-r--r--third_party/libwebrtc/rtc_base/rolling_accumulator_gn/moz.build7
-rw-r--r--third_party/libwebrtc/rtc_base/rtc_event_gn/moz.build5
-rw-r--r--third_party/libwebrtc/rtc_base/rtc_numerics_gn/moz.build5
-rw-r--r--third_party/libwebrtc/rtc_base/rtc_task_queue_gn/moz.build5
-rw-r--r--third_party/libwebrtc/rtc_base/safe_compare_gn/moz.build7
-rw-r--r--third_party/libwebrtc/rtc_base/safe_conversions_gn/moz.build7
-rw-r--r--third_party/libwebrtc/rtc_base/safe_minmax_gn/moz.build7
-rw-r--r--third_party/libwebrtc/rtc_base/sample_counter_gn/moz.build5
-rw-r--r--third_party/libwebrtc/rtc_base/sanitizer_gn/moz.build7
-rw-r--r--third_party/libwebrtc/rtc_base/server_socket_adapters.cc3
-rw-r--r--third_party/libwebrtc/rtc_base/socket_adapters.cc3
-rw-r--r--third_party/libwebrtc/rtc_base/socket_address_gn/moz.build5
-rw-r--r--third_party/libwebrtc/rtc_base/socket_factory_gn/moz.build7
-rw-r--r--third_party/libwebrtc/rtc_base/socket_gn/moz.build5
-rw-r--r--third_party/libwebrtc/rtc_base/socket_server_gn/moz.build7
-rw-r--r--third_party/libwebrtc/rtc_base/socket_unittest.cc6
-rw-r--r--third_party/libwebrtc/rtc_base/ssl_gn/moz.build7
-rw-r--r--third_party/libwebrtc/rtc_base/ssl_stream_adapter_unittest.cc188
-rw-r--r--third_party/libwebrtc/rtc_base/strings/str_join.h (renamed from third_party/libwebrtc/net/dcsctp/common/str_join.h)10
-rw-r--r--third_party/libwebrtc/rtc_base/strings/str_join_unittest.cc (renamed from third_party/libwebrtc/net/dcsctp/common/str_join_test.cc)8
-rw-r--r--third_party/libwebrtc/rtc_base/stringutils_gn/moz.build5
-rw-r--r--third_party/libwebrtc/rtc_base/swap_queue_gn/moz.build7
-rw-r--r--third_party/libwebrtc/rtc_base/synchronization/mutex_gn/moz.build7
-rw-r--r--third_party/libwebrtc/rtc_base/synchronization/sequence_checker_internal_gn/moz.build5
-rw-r--r--third_party/libwebrtc/rtc_base/synchronization/yield_gn/moz.build5
-rw-r--r--third_party/libwebrtc/rtc_base/synchronization/yield_policy_gn/moz.build5
-rw-r--r--third_party/libwebrtc/rtc_base/system/arch_gn/moz.build7
-rw-r--r--third_party/libwebrtc/rtc_base/system/asm_defines_gn/moz.build1
-rw-r--r--third_party/libwebrtc/rtc_base/system/file_wrapper_gn/moz.build5
-rw-r--r--third_party/libwebrtc/rtc_base/system/ignore_warnings_gn/moz.build7
-rw-r--r--third_party/libwebrtc/rtc_base/system/inline_gn/moz.build7
-rw-r--r--third_party/libwebrtc/rtc_base/system/no_unique_address_gn/moz.build7
-rw-r--r--third_party/libwebrtc/rtc_base/system/rtc_export_gn/moz.build7
-rw-r--r--third_party/libwebrtc/rtc_base/system/unused_gn/moz.build7
-rw-r--r--third_party/libwebrtc/rtc_base/system/warn_current_thread_is_deadlocked_gn/moz.build7
-rw-r--r--third_party/libwebrtc/rtc_base/task_utils/repeating_task_gn/moz.build5
-rw-r--r--third_party/libwebrtc/rtc_base/test_client.cc55
-rw-r--r--third_party/libwebrtc/rtc_base/test_client.h25
-rw-r--r--third_party/libwebrtc/rtc_base/test_echo_server.h13
-rw-r--r--third_party/libwebrtc/rtc_base/third_party/base64/base64_gn/moz.build5
-rw-r--r--third_party/libwebrtc/rtc_base/third_party/sigslot/sigslot_gn/moz.build5
-rw-r--r--third_party/libwebrtc/rtc_base/threading_gn/moz.build6
-rw-r--r--third_party/libwebrtc/rtc_base/timeutils_gn/moz.build5
-rw-r--r--third_party/libwebrtc/rtc_base/type_traits_gn/moz.build7
-rw-r--r--third_party/libwebrtc/rtc_base/unique_id_generator_gn/moz.build7
-rw-r--r--third_party/libwebrtc/rtc_base/units/unit_base_gn/moz.build7
-rw-r--r--third_party/libwebrtc/rtc_base/virtual_socket_server.cc1
-rw-r--r--third_party/libwebrtc/rtc_base/weak_ptr_gn/moz.build5
-rw-r--r--third_party/libwebrtc/rtc_base/zero_memory_gn/moz.build5
-rw-r--r--third_party/libwebrtc/rtc_tools/BUILD.gn17
-rw-r--r--third_party/libwebrtc/rtc_tools/network_tester/BUILD.gn2
-rw-r--r--third_party/libwebrtc/rtc_tools/network_tester/config_reader.h3
-rw-r--r--third_party/libwebrtc/rtc_tools/network_tester/packet_logger.h4
-rw-r--r--third_party/libwebrtc/rtc_tools/network_tester/packet_sender.h3
-rw-r--r--third_party/libwebrtc/rtc_tools/network_tester/server.cc1
-rw-r--r--third_party/libwebrtc/rtc_tools/network_tester/test_controller.h5
-rw-r--r--third_party/libwebrtc/rtc_tools/rtc_event_log_visualizer/plot_base.h4
-rw-r--r--third_party/libwebrtc/rtc_tools/rtc_event_log_visualizer/plot_protobuf.cc43
-rw-r--r--third_party/libwebrtc/rtc_tools/rtc_event_log_visualizer/plot_protobuf.h40
-rw-r--r--third_party/libwebrtc/rtc_tools/rtc_event_log_visualizer/plot_python.cc46
-rw-r--r--third_party/libwebrtc/rtc_tools/rtc_event_log_visualizer/plot_python.h39
-rw-r--r--third_party/libwebrtc/rtc_tools/rtp_generator/rtp_generator.cc11
-rw-r--r--third_party/libwebrtc/rtc_tools/rtp_generator/rtp_generator.h6
-rw-r--r--third_party/libwebrtc/rtc_tools/unpack_aecdump/unpack.cc4
-rw-r--r--third_party/libwebrtc/rtc_tools/video_file_reader.h4
-rw-r--r--third_party/libwebrtc/rtc_tools/video_replay.cc41
-rw-r--r--third_party/libwebrtc/sdk/BUILD.gn12
-rw-r--r--third_party/libwebrtc/sdk/android/api/org/webrtc/EglRenderer.java4
-rw-r--r--third_party/libwebrtc/sdk/android/api/org/webrtc/RenderSynchronizer.java11
-rw-r--r--third_party/libwebrtc/sdk/android/src/java/org/webrtc/HardwareVideoEncoder.java2
-rw-r--r--third_party/libwebrtc/sdk/objc/api/peerconnection/RTCPeerConnectionFactory.mm22
-rw-r--r--third_party/libwebrtc/sdk/objc/components/audio/RTCAudioSession.mm6
-rw-r--r--third_party/libwebrtc/system_wrappers/denormal_disabler_gn/moz.build5
-rw-r--r--third_party/libwebrtc/system_wrappers/field_trial_gn/moz.build5
-rw-r--r--third_party/libwebrtc/system_wrappers/metrics_gn/moz.build5
-rw-r--r--third_party/libwebrtc/system_wrappers/system_wrappers_gn/moz.build5
-rw-r--r--third_party/libwebrtc/test/BUILD.gn55
-rw-r--r--third_party/libwebrtc/test/call_test.cc72
-rw-r--r--third_party/libwebrtc/test/call_test.h17
-rw-r--r--third_party/libwebrtc/test/fuzzers/BUILD.gn1
-rw-r--r--third_party/libwebrtc/test/fuzzers/forward_error_correction_fuzzer.cc6
-rw-r--r--third_party/libwebrtc/test/fuzzers/rtp_frame_reference_finder_fuzzer.cc2
-rw-r--r--third_party/libwebrtc/test/fuzzers/stun_parser_fuzzer.cc3
-rw-r--r--third_party/libwebrtc/test/fuzzers/utils/BUILD.gn4
-rw-r--r--third_party/libwebrtc/test/fuzzers/utils/rtp_replayer.cc11
-rw-r--r--third_party/libwebrtc/test/fuzzers/utils/rtp_replayer.h1
-rw-r--r--third_party/libwebrtc/test/network/BUILD.gn3
-rw-r--r--third_party/libwebrtc/test/network/emulated_turn_server.cc102
-rw-r--r--third_party/libwebrtc/test/network/emulated_turn_server.h3
-rw-r--r--third_party/libwebrtc/test/network/network_emulation_pc_unittest.cc14
-rw-r--r--third_party/libwebrtc/test/pc/e2e/BUILD.gn3
-rw-r--r--third_party/libwebrtc/test/pc/e2e/test_peer_factory.cc50
-rw-r--r--third_party/libwebrtc/test/peer_scenario/peer_scenario_client.cc24
-rw-r--r--third_party/libwebrtc/test/peer_scenario/scenario_connection.cc20
-rw-r--r--third_party/libwebrtc/test/rtp_test_utils_gn/moz.build7
-rw-r--r--third_party/libwebrtc/test/scenario/BUILD.gn4
-rw-r--r--third_party/libwebrtc/test/scenario/audio_stream.cc2
-rw-r--r--third_party/libwebrtc/test/scenario/call_client.cc53
-rw-r--r--third_party/libwebrtc/test/scenario/call_client.h6
-rw-r--r--third_party/libwebrtc/test/scenario/scenario_config.h1
-rw-r--r--third_party/libwebrtc/test/scenario/video_stream.cc18
-rw-r--r--third_party/libwebrtc/test/video_codec_tester.cc1324
-rw-r--r--third_party/libwebrtc/test/video_codec_tester.h227
-rw-r--r--third_party/libwebrtc/test/video_codec_tester_unittest.cc513
-rw-r--r--third_party/libwebrtc/third_party/abseil-cpp/absl/algorithm/algorithm_gn/moz.build7
-rw-r--r--third_party/libwebrtc/third_party/abseil-cpp/absl/algorithm/container_gn/moz.build7
-rw-r--r--third_party/libwebrtc/third_party/abseil-cpp/absl/base/atomic_hook_gn/moz.build7
-rw-r--r--third_party/libwebrtc/third_party/abseil-cpp/absl/base/base_internal_gn/moz.build7
-rw-r--r--third_party/libwebrtc/third_party/abseil-cpp/absl/base/config_gn/moz.build7
-rw-r--r--third_party/libwebrtc/third_party/abseil-cpp/absl/base/core_headers_gn/moz.build7
-rw-r--r--third_party/libwebrtc/third_party/abseil-cpp/absl/base/log_severity_gn/moz.build5
-rw-r--r--third_party/libwebrtc/third_party/abseil-cpp/absl/base/nullability_gn/moz.build7
-rw-r--r--third_party/libwebrtc/third_party/abseil-cpp/absl/base/raw_logging_internal_gn/moz.build5
-rw-r--r--third_party/libwebrtc/third_party/abseil-cpp/absl/base/throw_delegate_gn/moz.build5
-rw-r--r--third_party/libwebrtc/third_party/abseil-cpp/absl/cleanup/cleanup_gn/moz.build7
-rw-r--r--third_party/libwebrtc/third_party/abseil-cpp/absl/cleanup/cleanup_internal_gn/moz.build7
-rw-r--r--third_party/libwebrtc/third_party/abseil-cpp/absl/container/compressed_tuple_gn/moz.build7
-rw-r--r--third_party/libwebrtc/third_party/abseil-cpp/absl/container/inlined_vector_gn/moz.build7
-rw-r--r--third_party/libwebrtc/third_party/abseil-cpp/absl/container/inlined_vector_internal_gn/moz.build7
-rw-r--r--third_party/libwebrtc/third_party/abseil-cpp/absl/functional/any_invocable_gn/moz.build7
-rw-r--r--third_party/libwebrtc/third_party/abseil-cpp/absl/functional/bind_front_gn/moz.build7
-rw-r--r--third_party/libwebrtc/third_party/abseil-cpp/absl/memory/memory_gn/moz.build7
-rw-r--r--third_party/libwebrtc/third_party/abseil-cpp/absl/meta/type_traits_gn/moz.build7
-rw-r--r--third_party/libwebrtc/third_party/abseil-cpp/absl/numeric/bits_gn/moz.build7
-rw-r--r--third_party/libwebrtc/third_party/abseil-cpp/absl/numeric/int128_gn/moz.build5
-rw-r--r--third_party/libwebrtc/third_party/abseil-cpp/absl/strings/string_view_gn/moz.build5
-rw-r--r--third_party/libwebrtc/third_party/abseil-cpp/absl/strings/strings_gn/moz.build5
-rw-r--r--third_party/libwebrtc/third_party/abseil-cpp/absl/types/bad_optional_access_gn/moz.build5
-rw-r--r--third_party/libwebrtc/third_party/abseil-cpp/absl/types/bad_variant_access_gn/moz.build5
-rw-r--r--third_party/libwebrtc/third_party/abseil-cpp/absl/types/optional_gn/moz.build7
-rw-r--r--third_party/libwebrtc/third_party/abseil-cpp/absl/types/span_gn/moz.build7
-rw-r--r--third_party/libwebrtc/third_party/abseil-cpp/absl/types/variant_gn/moz.build7
-rw-r--r--third_party/libwebrtc/third_party/abseil-cpp/absl/utility/utility_gn/moz.build7
-rw-r--r--third_party/libwebrtc/third_party/libyuv/libyuv_gn/moz.build7
-rw-r--r--third_party/libwebrtc/third_party/pffft/pffft_gn/moz.build5
-rw-r--r--third_party/libwebrtc/third_party/rnnoise/rnn_vad_gn/moz.build5
-rw-r--r--third_party/libwebrtc/tools/clang/OWNERS2
-rw-r--r--third_party/libwebrtc/tools/clang/plugins/ChromeClassTester.cpp294
-rw-r--r--third_party/libwebrtc/tools/clang/plugins/ChromeClassTester.h84
-rw-r--r--third_party/libwebrtc/tools/clang/plugins/FindBadConstructs.cpp435
-rw-r--r--third_party/libwebrtc/tools/clang/plugins/Makefile19
-rw-r--r--third_party/libwebrtc/tools/clang/plugins/OWNERS1
-rw-r--r--third_party/libwebrtc/tools/clang/plugins/README.chromium4
-rw-r--r--third_party/libwebrtc/tools/clang/plugins/tests/base_refcounted.cpp72
-rw-r--r--third_party/libwebrtc/tools/clang/plugins/tests/base_refcounted.h121
-rw-r--r--third_party/libwebrtc/tools/clang/plugins/tests/base_refcounted.txt23
-rw-r--r--third_party/libwebrtc/tools/clang/plugins/tests/inline_copy_ctor.cpp5
-rw-r--r--third_party/libwebrtc/tools/clang/plugins/tests/inline_copy_ctor.h12
-rw-r--r--third_party/libwebrtc/tools/clang/plugins/tests/inline_copy_ctor.txt5
-rw-r--r--third_party/libwebrtc/tools/clang/plugins/tests/inline_ctor.cpp25
-rw-r--r--third_party/libwebrtc/tools/clang/plugins/tests/inline_ctor.h21
-rw-r--r--third_party/libwebrtc/tools/clang/plugins/tests/inline_ctor.txt8
-rw-r--r--third_party/libwebrtc/tools/clang/plugins/tests/missing_ctor.cpp23
-rw-r--r--third_party/libwebrtc/tools/clang/plugins/tests/missing_ctor.h19
-rw-r--r--third_party/libwebrtc/tools/clang/plugins/tests/missing_ctor.txt6
-rw-r--r--third_party/libwebrtc/tools/clang/plugins/tests/nested_class_inline_ctor.cpp5
-rw-r--r--third_party/libwebrtc/tools/clang/plugins/tests/nested_class_inline_ctor.h22
-rw-r--r--third_party/libwebrtc/tools/clang/plugins/tests/nested_class_inline_ctor.txt8
-rw-r--r--third_party/libwebrtc/tools/clang/plugins/tests/overridden_methods.cpp38
-rw-r--r--third_party/libwebrtc/tools/clang/plugins/tests/overridden_methods.h54
-rw-r--r--third_party/libwebrtc/tools/clang/plugins/tests/overridden_methods.txt20
-rwxr-xr-xthird_party/libwebrtc/tools/clang/plugins/tests/test.sh72
-rw-r--r--third_party/libwebrtc/tools/clang/plugins/tests/virtual_methods.cpp36
-rw-r--r--third_party/libwebrtc/tools/clang/plugins/tests/virtual_methods.h39
-rw-r--r--third_party/libwebrtc/tools/clang/plugins/tests/virtual_methods.txt8
-rwxr-xr-xthird_party/libwebrtc/tools/clang/scripts/package.sh87
-rwxr-xr-xthird_party/libwebrtc/tools/clang/scripts/plugin_flags.sh24
-rwxr-xr-xthird_party/libwebrtc/tools/clang/scripts/update.py34
-rwxr-xr-xthird_party/libwebrtc/tools/clang/scripts/update.sh286
-rw-r--r--third_party/libwebrtc/tools_webrtc/mb/mb_config.pyl67
-rwxr-xr-xthird_party/libwebrtc/tools_webrtc/remove_extra_namespace.py93
-rw-r--r--third_party/libwebrtc/video/BUILD.gn3
-rw-r--r--third_party/libwebrtc/video/adaptation/video_adaptation_gn/moz.build5
-rw-r--r--third_party/libwebrtc/video/config/encoder_config_gn/moz.build5
-rw-r--r--third_party/libwebrtc/video/config/streams_config_gn/moz.build5
-rw-r--r--third_party/libwebrtc/video/decode_synchronizer_gn/moz.build5
-rw-r--r--third_party/libwebrtc/video/end_to_end_tests/multi_stream_tester.cc18
-rw-r--r--third_party/libwebrtc/video/end_to_end_tests/network_state_tests.cc2
-rw-r--r--third_party/libwebrtc/video/end_to_end_tests/stats_tests.cc8
-rw-r--r--third_party/libwebrtc/video/frame_cadence_adapter.cc154
-rw-r--r--third_party/libwebrtc/video/frame_cadence_adapter.h17
-rw-r--r--third_party/libwebrtc/video/frame_cadence_adapter_gn/moz.build5
-rw-r--r--third_party/libwebrtc/video/frame_cadence_adapter_unittest.cc145
-rw-r--r--third_party/libwebrtc/video/frame_decode_scheduler_gn/moz.build7
-rw-r--r--third_party/libwebrtc/video/frame_decode_timing_gn/moz.build5
-rw-r--r--third_party/libwebrtc/video/frame_dumping_decoder_gn/moz.build5
-rw-r--r--third_party/libwebrtc/video/frame_dumping_encoder_gn/moz.build5
-rw-r--r--third_party/libwebrtc/video/render/incoming_video_stream_gn/moz.build5
-rw-r--r--third_party/libwebrtc/video/render/video_render_frames_gn/moz.build5
-rw-r--r--third_party/libwebrtc/video/send_statistics_proxy.cc7
-rw-r--r--third_party/libwebrtc/video/task_queue_frame_decode_scheduler_gn/moz.build5
-rw-r--r--third_party/libwebrtc/video/unique_timestamp_counter_gn/moz.build5
-rw-r--r--third_party/libwebrtc/video/video_gn/moz.build5
-rw-r--r--third_party/libwebrtc/video/video_quality_test.cc51
-rw-r--r--third_party/libwebrtc/video/video_receive_stream2.cc21
-rw-r--r--third_party/libwebrtc/video/video_receive_stream_timeout_tracker_gn/moz.build5
-rw-r--r--third_party/libwebrtc/video/video_stream_buffer_controller_gn/moz.build5
-rw-r--r--third_party/libwebrtc/video/video_stream_encoder.cc115
-rw-r--r--third_party/libwebrtc/video/video_stream_encoder.h69
-rw-r--r--third_party/libwebrtc/video/video_stream_encoder_impl_gn/moz.build5
-rw-r--r--third_party/libwebrtc/video/video_stream_encoder_interface_gn/moz.build7
-rw-r--r--third_party/libwebrtc/video/video_stream_encoder_observer.h1
-rw-r--r--third_party/libwebrtc/video/video_stream_encoder_unittest.cc64
-rw-r--r--third_party/libwebrtc/webrtc.gni8
-rw-r--r--third_party/libwebrtc/webrtc_gn/moz.build7
-rw-r--r--third_party/libwebrtc/webrtc_lib_link_test.cc21
-rw-r--r--third_party/moz.build3
-rw-r--r--third_party/python/Mako/Mako-1.1.2.dist-info/AUTHORS13
-rw-r--r--third_party/python/Mako/Mako-1.1.2.dist-info/LICENSE19
-rw-r--r--third_party/python/Mako/Mako-1.1.2.dist-info/METADATA82
-rw-r--r--third_party/python/Mako/Mako-1.1.2.dist-info/RECORD33
-rw-r--r--third_party/python/Mako/Mako-1.1.2.dist-info/WHEEL6
-rw-r--r--third_party/python/Mako/Mako-1.1.2.dist-info/entry_points.txt20
-rw-r--r--third_party/python/Mako/Mako-1.1.2.dist-info/top_level.txt1
-rw-r--r--third_party/python/Mako/mako/__init__.py8
-rw-r--r--third_party/python/Mako/mako/_ast_util.py716
-rw-r--r--third_party/python/Mako/mako/ast.py205
-rw-r--r--third_party/python/Mako/mako/cache.py240
-rw-r--r--third_party/python/Mako/mako/cmd.py103
-rw-r--r--third_party/python/Mako/mako/codegen.py1318
-rw-r--r--third_party/python/Mako/mako/compat.py166
-rw-r--r--third_party/python/Mako/mako/exceptions.py430
-rw-r--r--third_party/python/Mako/mako/ext/__init__.py0
-rw-r--r--third_party/python/Mako/mako/ext/autohandler.py70
-rw-r--r--third_party/python/Mako/mako/ext/babelplugin.py58
-rw-r--r--third_party/python/Mako/mako/ext/beaker_cache.py82
-rw-r--r--third_party/python/Mako/mako/ext/extract.py125
-rw-r--r--third_party/python/Mako/mako/ext/linguaplugin.py57
-rw-r--r--third_party/python/Mako/mako/ext/preprocessors.py20
-rw-r--r--third_party/python/Mako/mako/ext/pygmentplugin.py157
-rw-r--r--third_party/python/Mako/mako/ext/turbogears.py61
-rw-r--r--third_party/python/Mako/mako/filters.py219
-rw-r--r--third_party/python/Mako/mako/lexer.py490
-rw-r--r--third_party/python/Mako/mako/lookup.py372
-rw-r--r--third_party/python/Mako/mako/parsetree.py665
-rw-r--r--third_party/python/Mako/mako/pygen.py305
-rw-r--r--third_party/python/Mako/mako/pyparser.py242
-rw-r--r--third_party/python/Mako/mako/runtime.py970
-rw-r--r--third_party/python/Mako/mako/template.py780
-rw-r--r--third_party/python/Mako/mako/util.py400
-rw-r--r--third_party/python/glean_parser/glean_parser-13.0.0.dist-info/AUTHORS.md (renamed from third_party/python/glean_parser/glean_parser-11.0.1.dist-info/AUTHORS.md)0
-rw-r--r--third_party/python/glean_parser/glean_parser-13.0.0.dist-info/LICENSE (renamed from third_party/python/glean_parser/glean_parser-11.0.1.dist-info/LICENSE)0
-rw-r--r--third_party/python/glean_parser/glean_parser-13.0.0.dist-info/METADATA (renamed from third_party/python/glean_parser/glean_parser-11.0.1.dist-info/METADATA)19
-rw-r--r--third_party/python/glean_parser/glean_parser-13.0.0.dist-info/RECORD (renamed from third_party/python/glean_parser/glean_parser-11.0.1.dist-info/RECORD)36
-rw-r--r--third_party/python/glean_parser/glean_parser-13.0.0.dist-info/WHEEL (renamed from third_party/python/glean_parser/glean_parser-11.0.1.dist-info/WHEEL)0
-rw-r--r--third_party/python/glean_parser/glean_parser-13.0.0.dist-info/entry_points.txt (renamed from third_party/python/glean_parser/glean_parser-11.0.1.dist-info/entry_points.txt)0
-rw-r--r--third_party/python/glean_parser/glean_parser-13.0.0.dist-info/top_level.txt (renamed from third_party/python/glean_parser/glean_parser-11.0.1.dist-info/top_level.txt)0
-rw-r--r--third_party/python/glean_parser/glean_parser/go_server.py145
-rw-r--r--third_party/python/glean_parser/glean_parser/metrics.py60
-rw-r--r--third_party/python/glean_parser/glean_parser/pings.py4
-rw-r--r--third_party/python/glean_parser/glean_parser/python_server.py130
-rw-r--r--third_party/python/glean_parser/glean_parser/rust.py23
-rw-r--r--third_party/python/glean_parser/glean_parser/schemas/metrics.2-0-0.schema.yaml13
-rw-r--r--third_party/python/glean_parser/glean_parser/schemas/pings.2-0-0.schema.yaml15
-rw-r--r--third_party/python/glean_parser/glean_parser/templates/go_server.jinja2225
-rw-r--r--third_party/python/glean_parser/glean_parser/templates/python_server.jinja2194
-rw-r--r--third_party/python/glean_parser/glean_parser/templates/rust.jinja249
-rw-r--r--third_party/python/glean_parser/glean_parser/templates/swift.jinja21
-rw-r--r--third_party/python/glean_parser/glean_parser/translate.py4
-rw-r--r--third_party/python/glean_parser/glean_parser/util.py1
-rw-r--r--third_party/python/poetry.lock28
-rw-r--r--third_party/python/requirements.in3
-rw-r--r--third_party/python/requirements.txt10
-rw-r--r--third_party/rlbox_wasm2c_sandbox/include/rlbox_wasm2c_sandbox.hpp30
-rw-r--r--third_party/rust/aa-stroke/.cargo-checksum.json2
-rw-r--r--third_party/rust/aa-stroke/src/bezierflattener.rs40
-rw-r--r--third_party/rust/aa-stroke/src/lib.rs56
-rw-r--r--third_party/rust/ahash/.cargo-checksum.json2
-rw-r--r--third_party/rust/ahash/Cargo.toml61
-rw-r--r--third_party/rust/ahash/build.rs4
-rw-r--r--third_party/rust/ahash/src/aes_hash.rs13
-rw-r--r--third_party/rust/ahash/src/hash_quality_test.rs36
-rw-r--r--third_party/rust/ahash/src/random_state.rs7
-rw-r--r--third_party/rust/cc/.cargo-checksum.json2
-rw-r--r--third_party/rust/cc/Cargo.lock110
-rw-r--r--third_party/rust/cc/Cargo.toml22
-rw-r--r--third_party/rust/cc/README.md212
-rw-r--r--third_party/rust/cc/src/bin/gcc-shim.rs48
-rw-r--r--third_party/rust/cc/src/command_helpers.rs433
-rw-r--r--third_party/rust/cc/src/lib.rs2668
-rw-r--r--third_party/rust/cc/src/parallel/async_executor.rs118
-rw-r--r--third_party/rust/cc/src/parallel/job_token.rs255
-rw-r--r--third_party/rust/cc/src/parallel/mod.rs20
-rw-r--r--third_party/rust/cc/src/parallel/stderr.rs90
-rw-r--r--third_party/rust/cc/src/tool.rs399
-rw-r--r--third_party/rust/cc/src/windows/com.rs (renamed from third_party/rust/cc/src/com.rs)42
-rw-r--r--third_party/rust/cc/src/windows/find_tools.rs (renamed from third_party/rust/cc/src/windows_registry.rs)456
-rw-r--r--third_party/rust/cc/src/windows/mod.rs20
-rw-r--r--third_party/rust/cc/src/windows/registry.rs (renamed from third_party/rust/cc/src/registry.rs)94
-rw-r--r--third_party/rust/cc/src/windows/setup_config.rs (renamed from third_party/rust/cc/src/setup_config.rs)34
-rw-r--r--third_party/rust/cc/src/windows/vs_instances.rs (renamed from third_party/rust/cc/src/vs_instances.rs)2
-rw-r--r--third_party/rust/cc/src/windows/winapi.rs (renamed from third_party/rust/cc/src/winapi.rs)102
-rw-r--r--third_party/rust/cc/src/windows/windows_sys.rs223
-rw-r--r--third_party/rust/cc/tests/cc_env.rs118
-rw-r--r--third_party/rust/cc/tests/cflags.rs15
-rw-r--r--third_party/rust/cc/tests/cxxflags.rs15
-rw-r--r--third_party/rust/cc/tests/support/mod.rs172
-rw-r--r--third_party/rust/cc/tests/test.rs461
-rw-r--r--third_party/rust/document-features/.cargo-checksum.json1
-rw-r--r--third_party/rust/document-features/CHANGELOG.md44
-rw-r--r--third_party/rust/document-features/Cargo.toml40
-rw-r--r--third_party/rust/document-features/LICENSE-APACHE73
-rw-r--r--third_party/rust/document-features/LICENSE-MIT (renamed from third_party/WinToast/LICENSE)4
-rw-r--r--third_party/rust/document-features/README.md43
-rw-r--r--third_party/rust/document-features/lib.rs877
-rw-r--r--third_party/rust/document-features/rustfmt.toml1
-rw-r--r--third_party/rust/document-features/tests/self-doc.rs37
-rw-r--r--third_party/rust/glean-core/.cargo-checksum.json2
-rw-r--r--third_party/rust/glean-core/Cargo.toml4
-rw-r--r--third_party/rust/glean-core/src/core/mod.rs6
-rw-r--r--third_party/rust/glean-core/src/glean.udl10
-rw-r--r--third_party/rust/glean-core/src/internal_pings.rs4
-rw-r--r--third_party/rust/glean-core/src/lib.rs38
-rw-r--r--third_party/rust/glean-core/src/lib_unit_tests.rs4
-rw-r--r--third_party/rust/glean-core/src/metrics/boolean.rs12
-rw-r--r--third_party/rust/glean-core/src/metrics/counter.rs12
-rw-r--r--third_party/rust/glean-core/src/metrics/custom_distribution.rs38
-rw-r--r--third_party/rust/glean-core/src/metrics/datetime.rs10
-rw-r--r--third_party/rust/glean-core/src/metrics/denominator.rs11
-rw-r--r--third_party/rust/glean-core/src/metrics/event.rs7
-rw-r--r--third_party/rust/glean-core/src/metrics/experiment.rs9
-rw-r--r--third_party/rust/glean-core/src/metrics/labeled.rs2
-rw-r--r--third_party/rust/glean-core/src/metrics/memory_distribution.rs11
-rw-r--r--third_party/rust/glean-core/src/metrics/mod.rs11
-rw-r--r--third_party/rust/glean-core/src/metrics/numerator.rs8
-rw-r--r--third_party/rust/glean-core/src/metrics/object.rs135
-rw-r--r--third_party/rust/glean-core/src/metrics/ping.rs28
-rw-r--r--third_party/rust/glean-core/src/metrics/quantity.rs11
-rw-r--r--third_party/rust/glean-core/src/metrics/rate.rs11
-rw-r--r--third_party/rust/glean-core/src/metrics/string.rs11
-rw-r--r--third_party/rust/glean-core/src/metrics/string_list.rs11
-rw-r--r--third_party/rust/glean-core/src/metrics/text.rs11
-rw-r--r--third_party/rust/glean-core/src/metrics/timespan.rs11
-rw-r--r--third_party/rust/glean-core/src/metrics/timing_distribution.rs43
-rw-r--r--third_party/rust/glean-core/src/metrics/url.rs11
-rw-r--r--third_party/rust/glean-core/src/metrics/uuid.rs11
-rw-r--r--third_party/rust/glean-core/src/ping/mod.rs44
-rw-r--r--third_party/rust/glean-core/src/traits/custom_distribution.rs16
-rw-r--r--third_party/rust/glean-core/src/traits/mod.rs6
-rw-r--r--third_party/rust/glean-core/src/traits/object.rs53
-rw-r--r--third_party/rust/glean-core/src/traits/timing_distribution.rs25
-rw-r--r--third_party/rust/glean-core/src/upload/directory.rs78
-rw-r--r--third_party/rust/glean-core/src/upload/mod.rs343
-rw-r--r--third_party/rust/glean-core/src/upload/request.rs37
-rw-r--r--third_party/rust/glean-core/tests/custom_distribution.rs20
-rw-r--r--third_party/rust/glean-core/tests/event.rs2
-rw-r--r--third_party/rust/glean-core/tests/object.rs104
-rw-r--r--third_party/rust/glean-core/tests/ping.rs14
-rw-r--r--third_party/rust/glean-core/tests/ping_maker.rs26
-rw-r--r--third_party/rust/glean-core/tests/timing_distribution.rs6
-rw-r--r--third_party/rust/glean/.cargo-checksum.json2
-rw-r--r--third_party/rust/glean/Cargo.toml34
-rw-r--r--third_party/rust/glean/src/common_test.rs1
-rw-r--r--third_party/rust/glean/src/configuration.rs2
-rw-r--r--third_party/rust/glean/src/lib.rs2
-rw-r--r--third_party/rust/glean/src/net/http_uploader.rs11
-rw-r--r--third_party/rust/glean/src/net/mod.rs25
-rw-r--r--third_party/rust/glean/src/private/mod.rs2
-rw-r--r--third_party/rust/glean/src/private/object.rs192
-rw-r--r--third_party/rust/glean/src/private/ping.rs2
-rw-r--r--third_party/rust/glean/src/test.rs215
-rw-r--r--third_party/rust/glean/tests/init_fails.rs2
-rw-r--r--third_party/rust/glean/tests/never_init.rs2
-rw-r--r--third_party/rust/glean/tests/no_time_to_init.rs2
-rw-r--r--third_party/rust/glean/tests/schema.rs13
-rw-r--r--third_party/rust/glean/tests/simple.rs2
-rw-r--r--third_party/rust/glean/tests/upload_timing.rs10
-rw-r--r--third_party/rust/glslopt/.cargo-checksum.json2
-rw-r--r--third_party/rust/glslopt/Cargo.toml21
-rw-r--r--third_party/rust/glslopt/glsl-optimizer/include/c99_alloca.h49
-rw-r--r--third_party/rust/glslopt/glsl-optimizer/include/c99_math.h211
-rw-r--r--third_party/rust/glslopt/glsl-optimizer/src/compiler/glsl/lower_instructions.cpp2
-rw-r--r--third_party/rust/glslopt/glsl-optimizer/src/util/rounding.h3
-rw-r--r--third_party/rust/glslopt/glsl-optimizer/src/util/u_math.h3
-rw-r--r--third_party/rust/litrs/.cargo-checksum.json1
-rw-r--r--third_party/rust/litrs/CHANGELOG.md103
-rw-r--r--third_party/rust/litrs/Cargo.toml51
-rw-r--r--third_party/rust/litrs/LICENSE-APACHE176
-rw-r--r--third_party/rust/litrs/LICENSE-MIT25
-rw-r--r--third_party/rust/litrs/README.md88
-rw-r--r--third_party/rust/litrs/src/bool/mod.rs55
-rw-r--r--third_party/rust/litrs/src/bool/tests.rs48
-rw-r--r--third_party/rust/litrs/src/byte/mod.rs107
-rw-r--r--third_party/rust/litrs/src/byte/tests.rs188
-rw-r--r--third_party/rust/litrs/src/bytestr/mod.rs126
-rw-r--r--third_party/rust/litrs/src/bytestr/tests.rs224
-rw-r--r--third_party/rust/litrs/src/char/mod.rs105
-rw-r--r--third_party/rust/litrs/src/char/tests.rs227
-rw-r--r--third_party/rust/litrs/src/err.rs371
-rw-r--r--third_party/rust/litrs/src/escape.rs262
-rw-r--r--third_party/rust/litrs/src/float/mod.rs257
-rw-r--r--third_party/rust/litrs/src/float/tests.rs253
-rw-r--r--third_party/rust/litrs/src/impls.rs401
-rw-r--r--third_party/rust/litrs/src/integer/mod.rs356
-rw-r--r--third_party/rust/litrs/src/integer/tests.rs357
-rw-r--r--third_party/rust/litrs/src/lib.rs370
-rw-r--r--third_party/rust/litrs/src/parse.rs125
-rw-r--r--third_party/rust/litrs/src/string/mod.rs125
-rw-r--r--third_party/rust/litrs/src/string/tests.rs278
-rw-r--r--third_party/rust/litrs/src/test_util.rs128
-rw-r--r--third_party/rust/litrs/src/tests.rs349
-rw-r--r--third_party/rust/naga/.cargo-checksum.json2
-rw-r--r--third_party/rust/naga/Cargo.toml4
-rw-r--r--third_party/rust/naga/src/back/glsl/features.rs53
-rw-r--r--third_party/rust/naga/src/back/glsl/mod.rs194
-rw-r--r--third_party/rust/naga/src/back/hlsl/conv.rs12
-rw-r--r--third_party/rust/naga/src/back/hlsl/help.rs150
-rw-r--r--third_party/rust/naga/src/back/hlsl/keywords.rs2
-rw-r--r--third_party/rust/naga/src/back/hlsl/mod.rs2
-rw-r--r--third_party/rust/naga/src/back/hlsl/storage.rs84
-rw-r--r--third_party/rust/naga/src/back/hlsl/writer.rs183
-rw-r--r--third_party/rust/naga/src/back/msl/keywords.rs2
-rw-r--r--third_party/rust/naga/src/back/msl/mod.rs15
-rw-r--r--third_party/rust/naga/src/back/msl/writer.rs200
-rw-r--r--third_party/rust/naga/src/back/spv/block.rs242
-rw-r--r--third_party/rust/naga/src/back/spv/writer.rs3
-rw-r--r--third_party/rust/naga/src/back/wgsl/writer.rs25
-rw-r--r--third_party/rust/naga/src/front/glsl/functions.rs2
-rw-r--r--third_party/rust/naga/src/front/glsl/parser/functions.rs2
-rw-r--r--third_party/rust/naga/src/front/spv/function.rs464
-rw-r--r--third_party/rust/naga/src/front/spv/mod.rs68
-rw-r--r--third_party/rust/naga/src/front/wgsl/error.rs2
-rw-r--r--third_party/rust/naga/src/front/wgsl/lower/mod.rs2
-rw-r--r--third_party/rust/naga/src/front/wgsl/parse/conv.rs8
-rw-r--r--third_party/rust/naga/src/front/wgsl/parse/number.rs16
-rw-r--r--third_party/rust/naga/src/front/wgsl/tests.rs1
-rw-r--r--third_party/rust/naga/src/keywords/wgsl.rs2
-rw-r--r--third_party/rust/naga/src/lib.rs27
-rw-r--r--third_party/rust/naga/src/proc/constant_evaluator.rs242
-rw-r--r--third_party/rust/naga/src/proc/mod.rs19
-rw-r--r--third_party/rust/naga/src/valid/expression.rs65
-rw-r--r--third_party/rust/naga/src/valid/mod.rs4
-rw-r--r--third_party/rust/naga/src/valid/type.rs44
-rw-r--r--third_party/rust/neqo-common/.cargo-checksum.json2
-rw-r--r--third_party/rust/neqo-common/Cargo.toml52
-rw-r--r--third_party/rust/neqo-common/build.rs6
-rw-r--r--third_party/rust/neqo-common/src/codec.rs6
-rw-r--r--third_party/rust/neqo-common/src/datagram.rs6
-rw-r--r--third_party/rust/neqo-common/src/event.rs2
-rw-r--r--third_party/rust/neqo-common/src/hrtime.rs5
-rw-r--r--third_party/rust/neqo-common/src/lib.rs5
-rw-r--r--third_party/rust/neqo-common/src/log.rs24
-rw-r--r--third_party/rust/neqo-common/src/qlog.rs3
-rw-r--r--third_party/rust/neqo-common/src/timer.rs62
-rw-r--r--third_party/rust/neqo-common/src/tos.rs37
-rw-r--r--third_party/rust/neqo-common/src/udp.rs222
-rw-r--r--third_party/rust/neqo-common/tests/log.rs3
-rw-r--r--third_party/rust/neqo-crypto/.cargo-checksum.json2
-rw-r--r--third_party/rust/neqo-crypto/Cargo.toml40
-rw-r--r--third_party/rust/neqo-crypto/build.rs22
-rw-r--r--third_party/rust/neqo-crypto/src/aead.rs1
-rw-r--r--third_party/rust/neqo-crypto/src/agent.rs12
-rw-r--r--third_party/rust/neqo-crypto/src/agentio.rs9
-rw-r--r--third_party/rust/neqo-crypto/src/cert.rs18
-rw-r--r--third_party/rust/neqo-crypto/src/ech.rs10
-rw-r--r--third_party/rust/neqo-crypto/src/ext.rs4
-rw-r--r--third_party/rust/neqo-crypto/src/hkdf.rs23
-rw-r--r--third_party/rust/neqo-crypto/src/hp.rs8
-rw-r--r--third_party/rust/neqo-crypto/src/lib.rs132
-rw-r--r--third_party/rust/neqo-crypto/src/once.rs44
-rw-r--r--third_party/rust/neqo-crypto/src/p11.rs114
-rw-r--r--third_party/rust/neqo-crypto/src/replay.rs1
-rw-r--r--third_party/rust/neqo-crypto/src/selfencrypt.rs2
-rw-r--r--third_party/rust/neqo-crypto/src/time.rs74
-rw-r--r--third_party/rust/neqo-crypto/tests/aead.rs7
-rw-r--r--third_party/rust/neqo-crypto/tests/agent.rs9
-rw-r--r--third_party/rust/neqo-crypto/tests/ext.rs7
-rw-r--r--third_party/rust/neqo-crypto/tests/handshake.rs9
-rw-r--r--third_party/rust/neqo-crypto/tests/hkdf.rs7
-rw-r--r--third_party/rust/neqo-crypto/tests/hp.rs7
-rw-r--r--third_party/rust/neqo-crypto/tests/init.rs7
-rw-r--r--third_party/rust/neqo-crypto/tests/selfencrypt.rs8
-rw-r--r--third_party/rust/neqo-http3/.cargo-checksum.json2
-rw-r--r--third_party/rust/neqo-http3/Cargo.toml45
-rw-r--r--third_party/rust/neqo-http3/src/client_events.rs6
-rw-r--r--third_party/rust/neqo-http3/src/connection.rs3
-rw-r--r--third_party/rust/neqo-http3/src/connection_client.rs33
-rw-r--r--third_party/rust/neqo-http3/src/control_stream_local.rs5
-rw-r--r--third_party/rust/neqo-http3/src/features/extended_connect/tests/webtransport/datagrams.rs2
-rw-r--r--third_party/rust/neqo-http3/src/features/extended_connect/tests/webtransport/mod.rs6
-rw-r--r--third_party/rust/neqo-http3/src/features/extended_connect/webtransport_session.rs20
-rw-r--r--third_party/rust/neqo-http3/src/features/extended_connect/webtransport_streams.rs10
-rw-r--r--third_party/rust/neqo-http3/src/frames/hframe.rs7
-rw-r--r--third_party/rust/neqo-http3/src/frames/reader.rs2
-rw-r--r--third_party/rust/neqo-http3/src/frames/wtframe.rs2
-rw-r--r--third_party/rust/neqo-http3/src/headers_checks.rs2
-rw-r--r--third_party/rust/neqo-http3/src/lib.rs29
-rw-r--r--third_party/rust/neqo-http3/src/priority.rs8
-rw-r--r--third_party/rust/neqo-http3/src/push_controller.rs2
-rw-r--r--third_party/rust/neqo-http3/src/qlog.rs7
-rw-r--r--third_party/rust/neqo-http3/src/recv_message.rs8
-rw-r--r--third_party/rust/neqo-http3/src/send_message.rs18
-rw-r--r--third_party/rust/neqo-http3/src/server.rs6
-rw-r--r--third_party/rust/neqo-http3/src/server_events.rs1
-rw-r--r--third_party/rust/neqo-http3/tests/priority.rs6
-rw-r--r--third_party/rust/neqo-http3/tests/send_message.rs31
-rw-r--r--third_party/rust/neqo-http3/tests/webtransport.rs6
-rw-r--r--third_party/rust/neqo-qpack/.cargo-checksum.json2
-rw-r--r--third_party/rust/neqo-qpack/Cargo.toml30
-rw-r--r--third_party/rust/neqo-qpack/src/decoder.rs4
-rw-r--r--third_party/rust/neqo-qpack/src/encoder.rs5
-rw-r--r--third_party/rust/neqo-qpack/src/huffman.rs6
-rw-r--r--third_party/rust/neqo-qpack/src/huffman_decode_helper.rs9
-rw-r--r--third_party/rust/neqo-qpack/src/lib.rs6
-rw-r--r--third_party/rust/neqo-qpack/src/qpack_send_buf.rs2
-rw-r--r--third_party/rust/neqo-qpack/src/reader.rs19
-rw-r--r--third_party/rust/neqo-qpack/src/table.rs2
-rw-r--r--third_party/rust/neqo-transport/.cargo-checksum.json2
-rw-r--r--third_party/rust/neqo-transport/Cargo.toml59
-rw-r--r--third_party/rust/neqo-transport/benches/range_tracker.rs50
-rw-r--r--third_party/rust/neqo-transport/benches/rx_stream_orderer.rs6
-rw-r--r--third_party/rust/neqo-transport/benches/transfer.rs70
-rw-r--r--third_party/rust/neqo-transport/src/ackrate.rs3
-rw-r--r--third_party/rust/neqo-transport/src/addr_valid.rs46
-rw-r--r--third_party/rust/neqo-transport/src/cc/classic_cc.rs6
-rw-r--r--third_party/rust/neqo-transport/src/cc/cubic.rs3
-rw-r--r--third_party/rust/neqo-transport/src/cc/mod.rs1
-rw-r--r--third_party/rust/neqo-transport/src/cc/new_reno.rs1
-rw-r--r--third_party/rust/neqo-transport/src/cc/tests/cubic.rs1
-rw-r--r--third_party/rust/neqo-transport/src/cc/tests/mod.rs1
-rw-r--r--third_party/rust/neqo-transport/src/cc/tests/new_reno.rs1
-rw-r--r--third_party/rust/neqo-transport/src/cid.rs98
-rw-r--r--third_party/rust/neqo-transport/src/connection/dump.rs4
-rw-r--r--third_party/rust/neqo-transport/src/connection/mod.rs210
-rw-r--r--third_party/rust/neqo-transport/src/connection/params.rs39
-rw-r--r--third_party/rust/neqo-transport/src/connection/state.rs19
-rw-r--r--third_party/rust/neqo-transport/src/connection/tests/ackrate.rs4
-rw-r--r--third_party/rust/neqo-transport/src/connection/tests/cc.rs4
-rw-r--r--third_party/rust/neqo-transport/src/connection/tests/close.rs2
-rw-r--r--third_party/rust/neqo-transport/src/connection/tests/datagram.rs2
-rw-r--r--third_party/rust/neqo-transport/src/connection/tests/fuzzing.rs2
-rw-r--r--third_party/rust/neqo-transport/src/connection/tests/handshake.rs70
-rw-r--r--third_party/rust/neqo-transport/src/connection/tests/idle.rs34
-rw-r--r--third_party/rust/neqo-transport/src/connection/tests/keys.rs2
-rw-r--r--third_party/rust/neqo-transport/src/connection/tests/migration.rs108
-rw-r--r--third_party/rust/neqo-transport/src/connection/tests/mod.rs13
-rw-r--r--third_party/rust/neqo-transport/src/connection/tests/priority.rs4
-rw-r--r--third_party/rust/neqo-transport/src/connection/tests/resumption.rs8
-rw-r--r--third_party/rust/neqo-transport/src/connection/tests/stream.rs2
-rw-r--r--third_party/rust/neqo-transport/src/connection/tests/vn.rs2
-rw-r--r--third_party/rust/neqo-transport/src/connection/tests/zerortt.rs2
-rw-r--r--third_party/rust/neqo-transport/src/crypto.rs34
-rw-r--r--third_party/rust/neqo-transport/src/events.rs9
-rw-r--r--third_party/rust/neqo-transport/src/fc.rs3
-rw-r--r--third_party/rust/neqo-transport/src/frame.rs14
-rw-r--r--third_party/rust/neqo-transport/src/lib.rs8
-rw-r--r--third_party/rust/neqo-transport/src/pace.rs2
-rw-r--r--third_party/rust/neqo-transport/src/packet/mod.rs41
-rw-r--r--third_party/rust/neqo-transport/src/packet/retry.rs2
-rw-r--r--third_party/rust/neqo-transport/src/path.rs12
-rw-r--r--third_party/rust/neqo-transport/src/qlog.rs10
-rw-r--r--third_party/rust/neqo-transport/src/quic_datagrams.rs4
-rw-r--r--third_party/rust/neqo-transport/src/recovery.rs15
-rw-r--r--third_party/rust/neqo-transport/src/recv_stream.rs82
-rw-r--r--third_party/rust/neqo-transport/src/rtt.rs2
-rw-r--r--third_party/rust/neqo-transport/src/send_stream.rs904
-rw-r--r--third_party/rust/neqo-transport/src/sender.rs2
-rw-r--r--third_party/rust/neqo-transport/src/server.rs59
-rw-r--r--third_party/rust/neqo-transport/src/stats.rs1
-rw-r--r--third_party/rust/neqo-transport/src/stream_id.rs14
-rw-r--r--third_party/rust/neqo-transport/src/streams.rs49
-rw-r--r--third_party/rust/neqo-transport/src/tparams.rs80
-rw-r--r--third_party/rust/neqo-transport/src/tracking.rs166
-rw-r--r--third_party/rust/neqo-transport/src/version.rs19
-rw-r--r--third_party/rust/neqo-transport/tests/common/mod.rs6
-rw-r--r--third_party/rust/neqo-transport/tests/conn_vectors.rs4
-rw-r--r--third_party/rust/neqo-transport/tests/connection.rs8
-rw-r--r--third_party/rust/neqo-transport/tests/network.rs117
-rw-r--r--third_party/rust/neqo-transport/tests/retry.rs5
-rw-r--r--third_party/rust/neqo-transport/tests/server.rs7
-rw-r--r--third_party/rust/neqo-transport/tests/sim/connection.rs315
-rw-r--r--third_party/rust/neqo-transport/tests/sim/delay.rs102
-rw-r--r--third_party/rust/neqo-transport/tests/sim/drop.rs75
-rw-r--r--third_party/rust/neqo-transport/tests/sim/mod.rs232
-rw-r--r--third_party/rust/neqo-transport/tests/sim/net.rs111
-rw-r--r--third_party/rust/neqo-transport/tests/sim/rng.rs81
-rw-r--r--third_party/rust/neqo-transport/tests/sim/taildrop.rs188
-rw-r--r--third_party/rust/qlog/.cargo-checksum.json2
-rw-r--r--third_party/rust/qlog/Cargo.toml8
-rw-r--r--third_party/rust/qlog/src/events/h3.rs3
-rw-r--r--third_party/rust/serde/.cargo-checksum.json2
-rw-r--r--third_party/rust/serde/Cargo.toml4
-rw-r--r--third_party/rust/serde/README.md6
-rw-r--r--third_party/rust/serde/src/de/mod.rs61
-rw-r--r--third_party/rust/serde/src/de/value.rs4
-rw-r--r--third_party/rust/serde/src/lib.rs15
-rw-r--r--third_party/rust/serde_derive/.cargo-checksum.json2
-rw-r--r--third_party/rust/serde_derive/Cargo.toml14
-rw-r--r--third_party/rust/serde_derive/README.md6
-rw-r--r--third_party/rust/serde_derive/src/lib.rs2
-rw-r--r--third_party/rust/smallvec/.cargo-checksum.json2
-rw-r--r--third_party/rust/smallvec/Cargo.toml2
-rw-r--r--third_party/rust/smallvec/benches/bench.rs4
-rw-r--r--third_party/rust/smallvec/src/lib.rs66
-rw-r--r--third_party/rust/smallvec/src/tests.rs21
-rw-r--r--third_party/rust/thiserror-impl/.cargo-checksum.json2
-rw-r--r--third_party/rust/thiserror-impl/Cargo.toml2
-rw-r--r--third_party/rust/thiserror-impl/src/attr.rs34
-rw-r--r--third_party/rust/thiserror-impl/src/fmt.rs3
-rw-r--r--third_party/rust/thiserror/.cargo-checksum.json2
-rw-r--r--third_party/rust/thiserror/Cargo.toml4
-rw-r--r--third_party/rust/thiserror/src/lib.rs2
-rw-r--r--third_party/rust/thiserror/tests/test_display.rs57
-rw-r--r--third_party/rust/thiserror/tests/ui/no-display.stderr3
-rw-r--r--third_party/rust/thiserror/tests/ui/source-enum-not-error.stderr8
-rw-r--r--third_party/rust/thiserror/tests/ui/source-enum-unnamed-field-not-error.stderr8
-rw-r--r--third_party/rust/thiserror/tests/ui/source-struct-not-error.stderr9
-rw-r--r--third_party/rust/thiserror/tests/ui/source-struct-unnamed-field-not-error.stderr9
-rw-r--r--third_party/rust/thiserror/tests/ui/transparent-enum-not-error.stderr5
-rw-r--r--third_party/rust/thiserror/tests/ui/transparent-enum-unnamed-field-not-error.stderr5
-rw-r--r--third_party/rust/thiserror/tests/ui/transparent-struct-not-error.stderr5
-rw-r--r--third_party/rust/thiserror/tests/ui/transparent-struct-unnamed-field-not-error.stderr5
-rw-r--r--third_party/rust/unicode-bidi/.appveyor.yml19
-rw-r--r--third_party/rust/unicode-bidi/.cargo-checksum.json2
-rw-r--r--third_party/rust/unicode-bidi/.github/workflows/main.yml49
-rw-r--r--third_party/rust/unicode-bidi/.rustfmt.toml1
-rw-r--r--third_party/rust/unicode-bidi/Cargo.lock175
-rw-r--r--third_party/rust/unicode-bidi/Cargo.toml10
-rw-r--r--third_party/rust/unicode-bidi/src/char_data/mod.rs5
-rw-r--r--third_party/rust/unicode-bidi/src/char_data/tables.rs4
-rw-r--r--third_party/rust/unicode-bidi/src/deprecated.rs9
-rw-r--r--third_party/rust/unicode-bidi/src/explicit.rs129
-rw-r--r--third_party/rust/unicode-bidi/src/implicit.rs93
-rw-r--r--third_party/rust/unicode-bidi/src/level.rs15
-rw-r--r--third_party/rust/unicode-bidi/src/lib.rs129
-rw-r--r--third_party/rust/unicode-bidi/src/prepare.rs266
-rw-r--r--third_party/rust/unicode-bidi/src/utf16.rs36
-rw-r--r--third_party/rust/wgpu-core/.cargo-checksum.json2
-rw-r--r--third_party/rust/wgpu-core/Cargo.toml1
-rw-r--r--third_party/rust/wgpu-core/src/binding_model.rs26
-rw-r--r--third_party/rust/wgpu-core/src/command/bundle.rs294
-rw-r--r--third_party/rust/wgpu-core/src/command/clear.rs45
-rw-r--r--third_party/rust/wgpu-core/src/command/compute.rs35
-rw-r--r--third_party/rust/wgpu-core/src/command/draw.rs120
-rw-r--r--third_party/rust/wgpu-core/src/command/mod.rs5
-rw-r--r--third_party/rust/wgpu-core/src/command/query.rs9
-rw-r--r--third_party/rust/wgpu-core/src/command/render.rs54
-rw-r--r--third_party/rust/wgpu-core/src/device/global.rs245
-rw-r--r--third_party/rust/wgpu-core/src/device/life.rs129
-rw-r--r--third_party/rust/wgpu-core/src/device/queue.rs125
-rw-r--r--third_party/rust/wgpu-core/src/device/resource.rs204
-rw-r--r--third_party/rust/wgpu-core/src/id.rs2
-rw-r--r--third_party/rust/wgpu-core/src/identity.rs63
-rw-r--r--third_party/rust/wgpu-core/src/instance.rs22
-rw-r--r--third_party/rust/wgpu-core/src/lib.rs30
-rw-r--r--third_party/rust/wgpu-core/src/pipeline.rs23
-rw-r--r--third_party/rust/wgpu-core/src/present.rs43
-rw-r--r--third_party/rust/wgpu-core/src/registry.rs9
-rw-r--r--third_party/rust/wgpu-core/src/resource.rs38
-rw-r--r--third_party/rust/wgpu-core/src/track/buffer.rs65
-rw-r--r--third_party/rust/wgpu-core/src/track/metadata.rs11
-rw-r--r--third_party/rust/wgpu-core/src/track/mod.rs218
-rw-r--r--third_party/rust/wgpu-core/src/track/stateless.rs43
-rw-r--r--third_party/rust/wgpu-core/src/track/texture.rs65
-rw-r--r--third_party/rust/wgpu-core/src/validation.rs115
-rw-r--r--third_party/rust/wgpu-hal/.cargo-checksum.json2
-rw-r--r--third_party/rust/wgpu-hal/Cargo.toml14
-rw-r--r--third_party/rust/wgpu-hal/src/auxil/dxgi/result.rs18
-rw-r--r--third_party/rust/wgpu-hal/src/dx12/adapter.rs25
-rw-r--r--third_party/rust/wgpu-hal/src/dx12/command.rs7
-rw-r--r--third_party/rust/wgpu-hal/src/dx12/device.rs6
-rw-r--r--third_party/rust/wgpu-hal/src/dx12/mod.rs3
-rw-r--r--third_party/rust/wgpu-hal/src/dx12/shader_compilation.rs4
-rw-r--r--third_party/rust/wgpu-hal/src/gles/adapter.rs73
-rw-r--r--third_party/rust/wgpu-hal/src/gles/command.rs7
-rw-r--r--third_party/rust/wgpu-hal/src/gles/device.rs5
-rw-r--r--third_party/rust/wgpu-hal/src/gles/egl.rs69
-rw-r--r--third_party/rust/wgpu-hal/src/gles/mod.rs53
-rw-r--r--third_party/rust/wgpu-hal/src/gles/queue.rs8
-rw-r--r--third_party/rust/wgpu-hal/src/gles/wgl.rs3
-rw-r--r--third_party/rust/wgpu-hal/src/lib.rs97
-rw-r--r--third_party/rust/wgpu-hal/src/metal/adapter.rs16
-rw-r--r--third_party/rust/wgpu-hal/src/metal/mod.rs1
-rw-r--r--third_party/rust/wgpu-hal/src/vulkan/adapter.rs18
-rw-r--r--third_party/rust/wgpu-hal/src/vulkan/instance.rs183
-rw-r--r--third_party/rust/wgpu-hal/src/vulkan/mod.rs34
-rw-r--r--third_party/rust/wgpu-types/.cargo-checksum.json2
-rw-r--r--third_party/rust/wgpu-types/Cargo.toml2
-rw-r--r--third_party/rust/wgpu-types/src/lib.rs409
-rw-r--r--third_party/sqlite3/ext/fts5.c13
-rw-r--r--third_party/sqlite3/moz.yaml4
-rw-r--r--third_party/sqlite3/src/VERSION.txt2
-rw-r--r--third_party/sqlite3/src/sqlite3.c291
-rw-r--r--third_party/sqlite3/src/sqlite3.h8
-rw-r--r--third_party/wayland-proxy/wayland-proxy.cpp151
-rw-r--r--third_party/wayland-proxy/wayland-proxy.h5
-rw-r--r--third_party/xsimd/include/xsimd/arch/generic/xsimd_generic_math.hpp59
-rw-r--r--third_party/xsimd/include/xsimd/arch/xsimd_avx2.hpp38
-rw-r--r--third_party/xsimd/include/xsimd/arch/xsimd_avx512bw.hpp38
-rw-r--r--third_party/xsimd/include/xsimd/arch/xsimd_i8mm_neon64.hpp17
-rw-r--r--third_party/xsimd/include/xsimd/arch/xsimd_isa.hpp4
-rw-r--r--third_party/xsimd/include/xsimd/arch/xsimd_neon.hpp68
-rw-r--r--third_party/xsimd/include/xsimd/arch/xsimd_neon64.hpp85
-rw-r--r--third_party/xsimd/include/xsimd/arch/xsimd_scalar.hpp33
-rw-r--r--third_party/xsimd/include/xsimd/arch/xsimd_sse2.hpp42
-rw-r--r--third_party/xsimd/include/xsimd/arch/xsimd_wasm.hpp40
-rw-r--r--third_party/xsimd/include/xsimd/config/xsimd_arch.hpp2
-rw-r--r--third_party/xsimd/include/xsimd/config/xsimd_config.hpp11
-rw-r--r--third_party/xsimd/include/xsimd/config/xsimd_cpuid.hpp9
-rw-r--r--third_party/xsimd/include/xsimd/types/xsimd_all_registers.hpp2
-rw-r--r--third_party/xsimd/include/xsimd/types/xsimd_api.hpp30
-rw-r--r--third_party/xsimd/include/xsimd/types/xsimd_i8mm_neon64_register.hpp46
-rw-r--r--third_party/xsimd/moz.yaml4
2004 files changed, 90919 insertions, 71038 deletions
diff --git a/third_party/WinToast/moz-check-system-shortcut.patch b/third_party/WinToast/moz-check-system-shortcut.patch
deleted file mode 100644
index 84411ae7bc..0000000000
--- a/third_party/WinToast/moz-check-system-shortcut.patch
+++ /dev/null
@@ -1,81 +0,0 @@
-diff --git a/src/wintoastlib.cpp b/src/wintoastlib.cpp
-index 0895ff7..ac8d5cf 100644
---- a/src/wintoastlib.cpp
-+++ b/src/wintoastlib.cpp
-@@ -213,8 +213,8 @@ namespace Util {
- }
-
-
-- inline HRESULT defaultShellLinksDirectory(_In_ WCHAR* path, _In_ DWORD nSize = MAX_PATH) {
-- DWORD written = GetEnvironmentVariableW(L"APPDATA", path, nSize);
-+ inline HRESULT commonShellLinksDirectory(_In_ const WCHAR* baseEnv, _In_ WCHAR* path, _In_ DWORD nSize) {
-+ DWORD written = GetEnvironmentVariableW(baseEnv, path, nSize);
- HRESULT hr = written > 0 ? S_OK : E_INVALIDARG;
- if (SUCCEEDED(hr)) {
- errno_t result = wcscat_s(path, nSize, DEFAULT_SHELL_LINKS_PATH);
-@@ -224,8 +224,8 @@ namespace Util {
- return hr;
- }
-
-- inline HRESULT defaultShellLinkPath(const std::wstring& appname, _In_ WCHAR* path, _In_ DWORD nSize = MAX_PATH) {
-- HRESULT hr = defaultShellLinksDirectory(path, nSize);
-+ inline HRESULT commonShellLinkPath(_In_ const WCHAR* baseEnv, const std::wstring& appname, _In_ WCHAR* path, _In_ DWORD nSize) {
-+ HRESULT hr = commonShellLinksDirectory(baseEnv, path, nSize);
- if (SUCCEEDED(hr)) {
- const std::wstring appLink(appname + DEFAULT_LINK_FORMAT);
- errno_t result = wcscat_s(path, nSize, appLink.c_str());
-@@ -235,6 +235,13 @@ namespace Util {
- return hr;
- }
-
-+ inline HRESULT defaultUserShellLinkPath(const std::wstring& appname, _In_ WCHAR* path, _In_ DWORD nSize = MAX_PATH) {
-+ return commonShellLinkPath(L"APPDATA", appname, path, nSize);
-+ }
-+
-+ inline HRESULT defaultSystemShellLinkPath(const std::wstring& appname, _In_ WCHAR* path, _In_ DWORD nSize = MAX_PATH) {
-+ return commonShellLinkPath(L"PROGRAMDATA", appname, path, nSize);
-+ }
-
- inline PCWSTR AsString(ComPtr<IXmlDocument> &xmlDocument) {
- HSTRING xml;
-@@ -523,12 +530,19 @@ const std::wstring& WinToast::appUserModelId() const {
-
- HRESULT WinToast::validateShellLinkHelper(_Out_ bool& wasChanged) {
- WCHAR path[MAX_PATH] = { L'\0' };
-- Util::defaultShellLinkPath(_appName, path);
-+ Util::defaultUserShellLinkPath(_appName, path);
- // Check if the file exist
- DWORD attr = GetFileAttributesW(path);
- if (attr >= 0xFFFFFFF) {
-- DEBUG_MSG("Error, shell link not found. Try to create a new one in: " << path);
-- return E_FAIL;
-+ // The shortcut may be in the system Start Menu.
-+ WCHAR systemPath[MAX_PATH] = { L'\0' };
-+ Util::defaultSystemShellLinkPath(_appName, systemPath);
-+ attr = GetFileAttributesW(systemPath);
-+ if (attr >= 0xFFFFFFF) {
-+ DEBUG_MSG("Error, shell link not found. Try to create a new one in: " << path);
-+ return E_FAIL;
-+ }
-+ wcscpy(path, systemPath);
- }
-
- // Let's load the file as shell link to validate.
-@@ -543,7 +557,7 @@ HRESULT WinToast::validateShellLinkHelper(_Out_ bool& wasChanged) {
- ComPtr<IPersistFile> persistFile;
- hr = shellLink.As(&persistFile);
- if (SUCCEEDED(hr)) {
-- hr = persistFile->Load(path, STGM_READWRITE);
-+ hr = persistFile->Load(path, STGM_READ);
- if (SUCCEEDED(hr)) {
- ComPtr<IPropertyStore> propertyStore;
- hr = shellLink.As(&propertyStore);
-@@ -583,7 +597,7 @@ HRESULT WinToast::validateShellLinkHelper(_Out_ bool& wasChanged) {
- HRESULT WinToast::createShellLinkHelper() {
- WCHAR exePath[MAX_PATH]{L'\0'};
- WCHAR slPath[MAX_PATH]{L'\0'};
-- Util::defaultShellLinkPath(_appName, slPath);
-+ Util::defaultUserShellLinkPath(_appName, slPath);
- Util::defaultExecutablePath(exePath);
- ComPtr<IShellLinkW> shellLink;
- HRESULT hr = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&shellLink));
diff --git a/third_party/WinToast/moz-disable-create-shortcut.patch b/third_party/WinToast/moz-disable-create-shortcut.patch
deleted file mode 100644
index 96ccac2732..0000000000
--- a/third_party/WinToast/moz-disable-create-shortcut.patch
+++ /dev/null
@@ -1,110 +0,0 @@
-diff --git a/src/wintoastlib.cpp b/src/wintoastlib.cpp
-index 0895ff7..52de554 100644
---- a/src/wintoastlib.cpp
-+++ b/src/wintoastlib.cpp
-@@ -391,6 +391,10 @@ void WinToast::setAppUserModelId(_In_ const std::wstring& aumi) {
- DEBUG_MSG(L"Default App User Model Id: " << _aumi.c_str());
- }
-
-+void WinToast::setShortcutPolicy(_In_ ShortcutPolicy shortcutPolicy) {
-+ _shortcutPolicy = shortcutPolicy;
-+}
-+
- bool WinToast::isCompatible() {
- DllImporter::initialize();
- return !((DllImporter::SetCurrentProcessExplicitAppUserModelID == nullptr)
-@@ -492,10 +496,12 @@ bool WinToast::initialize(_Out_ WinToastError* error) {
- return false;
- }
-
-- if (createShortcut() < 0) {
-- setError(error, WinToastError::ShellLinkNotCreated);
-- DEBUG_MSG(L"Error while attaching the AUMI to the current proccess =(");
-- return false;
-+ if (_shortcutPolicy != SHORTCUT_POLICY_IGNORE) {
-+ if (createShortcut() < 0) {
-+ setError(error, WinToastError::ShellLinkNotCreated);
-+ DEBUG_MSG(L"Error while attaching the AUMI to the current proccess =(");
-+ return false;
-+ }
- }
-
- if (FAILED(DllImporter::SetCurrentProcessExplicitAppUserModelID(_aumi.c_str()))) {
-@@ -555,18 +561,23 @@ HRESULT WinToast::validateShellLinkHelper(_Out_ bool& wasChanged) {
- hr = DllImporter::PropVariantToString(appIdPropVar, AUMI, MAX_PATH);
- wasChanged = false;
- if (FAILED(hr) || _aumi != AUMI) {
-- // AUMI Changed for the same app, let's update the current value! =)
-- wasChanged = true;
-- PropVariantClear(&appIdPropVar);
-- hr = InitPropVariantFromString(_aumi.c_str(), &appIdPropVar);
-- if (SUCCEEDED(hr)) {
-- hr = propertyStore->SetValue(PKEY_AppUserModel_ID, appIdPropVar);
-+ if (_shortcutPolicy == SHORTCUT_POLICY_REQUIRE_CREATE) {
-+ // AUMI Changed for the same app, let's update the current value! =)
-+ wasChanged = true;
-+ PropVariantClear(&appIdPropVar);
-+ hr = InitPropVariantFromString(_aumi.c_str(), &appIdPropVar);
- if (SUCCEEDED(hr)) {
-- hr = propertyStore->Commit();
-- if (SUCCEEDED(hr) && SUCCEEDED(persistFile->IsDirty())) {
-- hr = persistFile->Save(path, TRUE);
-+ hr = propertyStore->SetValue(PKEY_AppUserModel_ID, appIdPropVar);
-+ if (SUCCEEDED(hr)) {
-+ hr = propertyStore->Commit();
-+ if (SUCCEEDED(hr) && SUCCEEDED(persistFile->IsDirty())) {
-+ hr = persistFile->Save(path, TRUE);
-+ }
- }
- }
-+ } else {
-+ // Not allowed to touch the shortcut to fix the AUMI
-+ hr = E_FAIL;
- }
- }
- PropVariantClear(&appIdPropVar);
-@@ -581,6 +592,10 @@ HRESULT WinToast::validateShellLinkHelper(_Out_ bool& wasChanged) {
-
-
- HRESULT WinToast::createShellLinkHelper() {
-+ if (_shortcutPolicy != SHORTCUT_POLICY_REQUIRE_CREATE) {
-+ return E_FAIL;
-+ }
-+
- WCHAR exePath[MAX_PATH]{L'\0'};
- WCHAR slPath[MAX_PATH]{L'\0'};
- Util::defaultShellLinkPath(_appName, slPath);
-diff --git a/src/wintoastlib.h b/src/wintoastlib.h
-index 68b1cb1..dc8d745 100644
---- a/src/wintoastlib.h
-+++ b/src/wintoastlib.h
-@@ -173,6 +173,16 @@ namespace WinToastLib {
- SHORTCUT_CREATE_FAILED = -4
- };
-
-+ enum ShortcutPolicy {
-+ /* Don't check, create, or modify a shortcut. */
-+ SHORTCUT_POLICY_IGNORE = 0,
-+ /* Require a shortcut with matching AUMI, don't create or modify an existing one. */
-+ SHORTCUT_POLICY_REQUIRE_NO_CREATE = 1,
-+ /* Require a shortcut with matching AUMI, create if missing, modify if not matching.
-+ * This is the default. */
-+ SHORTCUT_POLICY_REQUIRE_CREATE = 2,
-+ };
-+
- WinToast(void);
- virtual ~WinToast();
- static WinToast* instance();
-@@ -194,10 +204,12 @@ namespace WinToastLib {
- const std::wstring& appUserModelId() const;
- void setAppUserModelId(_In_ const std::wstring& aumi);
- void setAppName(_In_ const std::wstring& appName);
-+ void setShortcutPolicy(_In_ ShortcutPolicy policy);
-
- protected:
- bool _isInitialized{false};
- bool _hasCoInitialized{false};
-+ ShortcutPolicy _shortcutPolicy{SHORTCUT_POLICY_REQUIRE_CREATE};
- std::wstring _appName{};
- std::wstring _aumi{};
- std::map<INT64, ComPtr<IToastNotification>> _buffer{};
diff --git a/third_party/WinToast/moz.yaml b/third_party/WinToast/moz.yaml
deleted file mode 100644
index 7a27bf29ad..0000000000
--- a/third_party/WinToast/moz.yaml
+++ /dev/null
@@ -1,37 +0,0 @@
-# Version of this schema
-schema: 1
-
-# Manual Update can be done with:
-# cd third_pary/WinToast
-# wget https://raw.githubusercontent.com/mohabouje/WinToast/master/src/wintoastlib.cpp
-# wget https://raw.githubusercontent.com/mohabouje/WinToast/master/src/wintoastlib.h
-# patch -p2 < moz-check-system-shortcut.patch
-# patch -p2 < moz-disable-create-shortcut.patch
-# patch -p2 < upstream-add-toast-scenario.patch
-
-bugzilla:
- # Bugzilla product and component for this directory and subdirectories
- product: Toolkit
- component: "General"
-
-# Document the source of externally hosted code
-origin:
-
- # Short name of the package/library
- name: WinToast
-
- description: WinToast is a lightly library written in C++ which brings a complete integration of the modern toast notifications of Windows 8 & Windows 10.
-
- # Full URL for the package's homepage/etc
- # Usually different from repository url
- url: https://github.com/mohabouje/WinToast
-
- # Human-readable identifier for this version/release
- # Generally "version NNN", "tag SSS", "bookmark SSS"
- release: commit 09227c72f16ccefc36e9d430dea3b435346dbcbc
-
- # The package's license, where possible using the mnemonic from
- # https://spdx.org/licenses/
- # Multiple licenses can be specified (as a YAML list)
- # A "LICENSE" file must exist containing the full license text
- license: MIT
diff --git a/third_party/WinToast/upstream-add-toast-scenario.patch b/third_party/WinToast/upstream-add-toast-scenario.patch
deleted file mode 100644
index 0be8bf878d..0000000000
--- a/third_party/WinToast/upstream-add-toast-scenario.patch
+++ /dev/null
@@ -1,123 +0,0 @@
-diff --git a/src/wintoastlib.cpp b/src/wintoastlib.cpp
-index 3cf5f21..1adfe19 100644
---- a/src/wintoastlib.cpp
-+++ b/src/wintoastlib.cpp
-@@ -677,6 +677,10 @@ INT64 WinToast::showToast(_In_ const WinToastTemplate& toast, _In_ IWinToastHan
- (toast.duration() == WinToastTemplate::Duration::Short) ? L"short" : L"long");
- }
-
-+ if (SUCCEEDED(hr)) {
-+ hr = addScenarioHelper(xmlDocument.Get(), toast.scenario());
-+ }
-+
- } else {
- DEBUG_MSG("Modern features (Actions/Sounds/Attributes) not supported in this os version");
- }
-@@ -828,6 +832,28 @@ HRESULT WinToast::addDurationHelper(_In_ IXmlDocument *xml, _In_ const std::wstr
- return hr;
- }
-
-+HRESULT WinToast::addScenarioHelper(_In_ IXmlDocument* xml, _In_ const std::wstring& scenario) {
-+ ComPtr<IXmlNodeList> nodeList;
-+ HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"toast").Get(), &nodeList);
-+ if (SUCCEEDED(hr)) {
-+ UINT32 length;
-+ hr = nodeList->get_Length(&length);
-+ if (SUCCEEDED(hr)) {
-+ ComPtr<IXmlNode> toastNode;
-+ hr = nodeList->Item(0, &toastNode);
-+ if (SUCCEEDED(hr)) {
-+ ComPtr<IXmlElement> toastElement;
-+ hr = toastNode.As(&toastElement);
-+ if (SUCCEEDED(hr)) {
-+ hr = toastElement->SetAttribute(WinToastStringWrapper(L"scenario").Get(),
-+ WinToastStringWrapper(scenario).Get());
-+ }
-+ }
-+ }
-+ }
-+ return hr;
-+}
-+
- HRESULT WinToast::setTextFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& text, _In_ UINT32 pos) {
- ComPtr<IXmlNodeList> nodeList;
- HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"text").Get(), &nodeList);
-@@ -1065,6 +1091,15 @@ void WinToastTemplate::setExpiration(_In_ INT64 millisecondsFromNow) {
- _expiration = millisecondsFromNow;
- }
-
-+void WinToastLib::WinToastTemplate::setScenario(Scenario scenario) {
-+ switch (scenario) {
-+ case Scenario::Default: _scenario = L"Default"; break;
-+ case Scenario::Alarm: _scenario = L"Alarm"; break;
-+ case Scenario::IncomingCall: _scenario = L"IncomingCall"; break;
-+ case Scenario::Reminder: _scenario = L"Reminder"; break;
-+ }
-+}
-+
- void WinToastTemplate::setAttributionText(_In_ const std::wstring& attributionText) {
- _attributionText = attributionText;
- }
-@@ -1112,6 +1147,10 @@ const std::wstring& WinToastTemplate::attributionText() const {
- return _attributionText;
- }
-
-+const std::wstring& WinToastLib::WinToastTemplate::scenario() const {
-+ return _scenario;
-+}
-+
- INT64 WinToastTemplate::expiration() const {
- return _expiration;
- }
-diff --git a/src/wintoastlib.h b/src/wintoastlib.h
-index d028994..291e15f 100644
---- a/src/wintoastlib.h
-+++ b/src/wintoastlib.h
-@@ -63,6 +63,7 @@ namespace WinToastLib {
-
- class WinToastTemplate {
- public:
-+ enum class Scenario { Default, Alarm, IncomingCall, Reminder };
- enum Duration { System, Short, Long };
- enum AudioOption { Default = 0, Silent, Loop };
- enum TextField { FirstLine = 0, SecondLine, ThirdLine };
-@@ -114,13 +115,14 @@ namespace WinToastLib {
- void setSecondLine(_In_ const std::wstring& text);
- void setThirdLine(_In_ const std::wstring& text);
- void setTextField(_In_ const std::wstring& txt, _In_ TextField pos);
-- void setAttributionText(_In_ const std::wstring & attributionText);
-+ void setAttributionText(_In_ const std::wstring& attributionText);
- void setImagePath(_In_ const std::wstring& imgPath);
- void setAudioPath(_In_ WinToastTemplate::AudioSystemFile audio);
- void setAudioPath(_In_ const std::wstring& audioPath);
- void setAudioOption(_In_ WinToastTemplate::AudioOption audioOption);
- void setDuration(_In_ Duration duration);
- void setExpiration(_In_ INT64 millisecondsFromNow);
-+ void setScenario(_In_ Scenario scenario);
- void addAction(_In_ const std::wstring& label);
-
- std::size_t textFieldsCount() const;
-@@ -132,6 +134,7 @@ namespace WinToastLib {
- const std::wstring& imagePath() const;
- const std::wstring& audioPath() const;
- const std::wstring& attributionText() const;
-+ const std::wstring& scenario() const;
- INT64 expiration() const;
- WinToastTemplateType type() const;
- WinToastTemplate::AudioOption audioOption() const;
-@@ -142,6 +145,7 @@ namespace WinToastLib {
- std::wstring _imagePath{};
- std::wstring _audioPath{};
- std::wstring _attributionText{};
-+ std::wstring _scenario{L"Default"};
- INT64 _expiration{0};
- AudioOption _audioOption{WinToastTemplate::AudioOption::Default};
- WinToastTemplateType _type{WinToastTemplateType::Text01};
-@@ -210,6 +214,7 @@ namespace WinToastLib {
- HRESULT setAttributionTextFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& text);
- HRESULT addActionHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& action, _In_ const std::wstring& arguments);
- HRESULT addDurationHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& duration);
-+ HRESULT addScenarioHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& scenario);
- ComPtr<IToastNotifier> notifier(_In_ bool* succeded) const;
- void setError(_Out_opt_ WinToastError* error, _In_ WinToastError value);
- };
diff --git a/third_party/WinToast/wintoastlib.cpp b/third_party/WinToast/wintoastlib.cpp
deleted file mode 100644
index ea5648a61d..0000000000
--- a/third_party/WinToast/wintoastlib.cpp
+++ /dev/null
@@ -1,1197 +0,0 @@
-/* * Copyright (C) 2016-2019 Mohammed Boujemaoui <mohabouje@gmail.com>
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy of
- * this software and associated documentation files (the "Software"), to deal in
- * the Software without restriction, including without limitation the rights to
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
- * the Software, and to permit persons to whom the Software is furnished to do so,
- * subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
- * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
- * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
- * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-#include "wintoastlib.h"
-#include <memory>
-#include <assert.h>
-#include <unordered_map>
-#include <array>
-
-#pragma comment(lib,"shlwapi")
-#pragma comment(lib,"user32")
-
-#ifdef NDEBUG
- #define DEBUG_MSG(str) do { } while ( false )
-#else
- #define DEBUG_MSG(str) do { std::wcout << str << std::endl; } while( false )
-#endif
-
-#define DEFAULT_SHELL_LINKS_PATH L"\\Microsoft\\Windows\\Start Menu\\Programs\\"
-#define DEFAULT_LINK_FORMAT L".lnk"
-#define STATUS_SUCCESS (0x00000000)
-
-
-// Quickstart: Handling toast activations from Win32 apps in Windows 10
-// https://blogs.msdn.microsoft.com/tiles_and_toasts/2015/10/16/quickstart-handling-toast-activations-from-win32-apps-in-windows-10/
-using namespace WinToastLib;
-namespace DllImporter {
-
- // Function load a function from library
- template <typename Function>
- HRESULT loadFunctionFromLibrary(HINSTANCE library, LPCSTR name, Function &func) {
- if (!library) {
- return E_INVALIDARG;
- }
- func = reinterpret_cast<Function>(GetProcAddress(library, name));
- return (func != nullptr) ? S_OK : E_FAIL;
- }
-
- typedef HRESULT(FAR STDAPICALLTYPE *f_SetCurrentProcessExplicitAppUserModelID)(__in PCWSTR AppID);
- typedef HRESULT(FAR STDAPICALLTYPE *f_PropVariantToString)(_In_ REFPROPVARIANT propvar, _Out_writes_(cch) PWSTR psz, _In_ UINT cch);
- typedef HRESULT(FAR STDAPICALLTYPE *f_RoGetActivationFactory)(_In_ HSTRING activatableClassId, _In_ REFIID iid, _COM_Outptr_ void ** factory);
- typedef HRESULT(FAR STDAPICALLTYPE *f_WindowsCreateStringReference)(_In_reads_opt_(length + 1) PCWSTR sourceString, UINT32 length, _Out_ HSTRING_HEADER * hstringHeader, _Outptr_result_maybenull_ _Result_nullonfailure_ HSTRING * string);
- typedef PCWSTR(FAR STDAPICALLTYPE *f_WindowsGetStringRawBuffer)(_In_ HSTRING string, _Out_ UINT32 *length);
- typedef HRESULT(FAR STDAPICALLTYPE *f_WindowsDeleteString)(_In_opt_ HSTRING string);
-
- static f_SetCurrentProcessExplicitAppUserModelID SetCurrentProcessExplicitAppUserModelID;
- static f_PropVariantToString PropVariantToString;
- static f_RoGetActivationFactory RoGetActivationFactory;
- static f_WindowsCreateStringReference WindowsCreateStringReference;
- static f_WindowsGetStringRawBuffer WindowsGetStringRawBuffer;
- static f_WindowsDeleteString WindowsDeleteString;
-
-
- template<class T>
- _Check_return_ __inline HRESULT _1_GetActivationFactory(_In_ HSTRING activatableClassId, _COM_Outptr_ T** factory) {
- return RoGetActivationFactory(activatableClassId, IID_INS_ARGS(factory));
- }
-
- template<typename T>
- inline HRESULT Wrap_GetActivationFactory(_In_ HSTRING activatableClassId, _Inout_ Details::ComPtrRef<T> factory) noexcept {
- return _1_GetActivationFactory(activatableClassId, factory.ReleaseAndGetAddressOf());
- }
-
- inline HRESULT initialize() {
- HINSTANCE LibShell32 = LoadLibraryW(L"SHELL32.DLL");
- HRESULT hr = loadFunctionFromLibrary(LibShell32, "SetCurrentProcessExplicitAppUserModelID", SetCurrentProcessExplicitAppUserModelID);
- if (SUCCEEDED(hr)) {
- HINSTANCE LibPropSys = LoadLibraryW(L"PROPSYS.DLL");
- hr = loadFunctionFromLibrary(LibPropSys, "PropVariantToString", PropVariantToString);
- if (SUCCEEDED(hr)) {
- HINSTANCE LibComBase = LoadLibraryW(L"COMBASE.DLL");
- const bool succeded = SUCCEEDED(loadFunctionFromLibrary(LibComBase, "RoGetActivationFactory", RoGetActivationFactory))
- && SUCCEEDED(loadFunctionFromLibrary(LibComBase, "WindowsCreateStringReference", WindowsCreateStringReference))
- && SUCCEEDED(loadFunctionFromLibrary(LibComBase, "WindowsGetStringRawBuffer", WindowsGetStringRawBuffer))
- && SUCCEEDED(loadFunctionFromLibrary(LibComBase, "WindowsDeleteString", WindowsDeleteString));
- return succeded ? S_OK : E_FAIL;
- }
- }
- return hr;
- }
-}
-
-class WinToastStringWrapper {
-public:
- WinToastStringWrapper(_In_reads_(length) PCWSTR stringRef, _In_ UINT32 length) noexcept {
- HRESULT hr = DllImporter::WindowsCreateStringReference(stringRef, length, &_header, &_hstring);
- if (!SUCCEEDED(hr)) {
- RaiseException(static_cast<DWORD>(STATUS_INVALID_PARAMETER), EXCEPTION_NONCONTINUABLE, 0, nullptr);
- }
- }
-
- WinToastStringWrapper(_In_ const std::wstring &stringRef) noexcept {
- HRESULT hr = DllImporter::WindowsCreateStringReference(stringRef.c_str(), static_cast<UINT32>(stringRef.length()), &_header, &_hstring);
- if (FAILED(hr)) {
- RaiseException(static_cast<DWORD>(STATUS_INVALID_PARAMETER), EXCEPTION_NONCONTINUABLE, 0, nullptr);
- }
- }
-
- ~WinToastStringWrapper() {
- DllImporter::WindowsDeleteString(_hstring);
- }
-
- inline HSTRING Get() const noexcept {
- return _hstring;
- }
-private:
- HSTRING _hstring;
- HSTRING_HEADER _header;
-
-};
-
-class InternalDateTime : public IReference<DateTime> {
-public:
- static INT64 Now() {
- FILETIME now;
- GetSystemTimeAsFileTime(&now);
- return ((((INT64)now.dwHighDateTime) << 32) | now.dwLowDateTime);
- }
-
- InternalDateTime(DateTime dateTime) : _dateTime(dateTime) {}
-
- InternalDateTime(INT64 millisecondsFromNow) {
- _dateTime.UniversalTime = Now() + millisecondsFromNow * 10000;
- }
-
- virtual ~InternalDateTime() = default;
-
- operator INT64() {
- return _dateTime.UniversalTime;
- }
-
- HRESULT STDMETHODCALLTYPE get_Value(DateTime *dateTime) {
- *dateTime = _dateTime;
- return S_OK;
- }
-
- HRESULT STDMETHODCALLTYPE QueryInterface(const IID& riid, void** ppvObject) {
- if (!ppvObject) {
- return E_POINTER;
- }
- if (riid == __uuidof(IUnknown) || riid == __uuidof(IReference<DateTime>)) {
- *ppvObject = static_cast<IUnknown*>(static_cast<IReference<DateTime>*>(this));
- return S_OK;
- }
- return E_NOINTERFACE;
- }
-
- ULONG STDMETHODCALLTYPE Release() {
- return 1;
- }
-
- ULONG STDMETHODCALLTYPE AddRef() {
- return 2;
- }
-
- HRESULT STDMETHODCALLTYPE GetIids(ULONG*, IID**) {
- return E_NOTIMPL;
- }
-
- HRESULT STDMETHODCALLTYPE GetRuntimeClassName(HSTRING*) {
- return E_NOTIMPL;
- }
-
- HRESULT STDMETHODCALLTYPE GetTrustLevel(TrustLevel*) {
- return E_NOTIMPL;
- }
-
-protected:
- DateTime _dateTime;
-};
-
-namespace Util {
-
- typedef LONG NTSTATUS, *PNTSTATUS;
- typedef NTSTATUS(WINAPI* RtlGetVersionPtr)(PRTL_OSVERSIONINFOW);
- inline RTL_OSVERSIONINFOW getRealOSVersion() {
- HMODULE hMod = ::GetModuleHandleW(L"ntdll.dll");
- if (hMod) {
- RtlGetVersionPtr fxPtr = (RtlGetVersionPtr)::GetProcAddress(hMod, "RtlGetVersion");
- if (fxPtr != nullptr) {
- RTL_OSVERSIONINFOW rovi = { 0 };
- rovi.dwOSVersionInfoSize = sizeof(rovi);
- if (STATUS_SUCCESS == fxPtr(&rovi)) {
- return rovi;
- }
- }
- }
- RTL_OSVERSIONINFOW rovi = { 0 };
- return rovi;
- }
-
- inline HRESULT defaultExecutablePath(_In_ WCHAR* path, _In_ DWORD nSize = MAX_PATH) {
- DWORD written = GetModuleFileNameExW(GetCurrentProcess(), nullptr, path, nSize);
- DEBUG_MSG("Default executable path: " << path);
- return (written > 0) ? S_OK : E_FAIL;
- }
-
-
- inline HRESULT commonShellLinksDirectory(_In_ const WCHAR* baseEnv, _In_ WCHAR* path, _In_ DWORD nSize) {
- DWORD written = GetEnvironmentVariableW(baseEnv, path, nSize);
- HRESULT hr = written > 0 ? S_OK : E_INVALIDARG;
- if (SUCCEEDED(hr)) {
- errno_t result = wcscat_s(path, nSize, DEFAULT_SHELL_LINKS_PATH);
- hr = (result == 0) ? S_OK : E_INVALIDARG;
- DEBUG_MSG("Default shell link path: " << path);
- }
- return hr;
- }
-
- inline HRESULT commonShellLinkPath(_In_ const WCHAR* baseEnv, const std::wstring& appname, _In_ WCHAR* path, _In_ DWORD nSize) {
- HRESULT hr = commonShellLinksDirectory(baseEnv, path, nSize);
- if (SUCCEEDED(hr)) {
- const std::wstring appLink(appname + DEFAULT_LINK_FORMAT);
- errno_t result = wcscat_s(path, nSize, appLink.c_str());
- hr = (result == 0) ? S_OK : E_INVALIDARG;
- DEBUG_MSG("Default shell link file path: " << path);
- }
- return hr;
- }
-
- inline HRESULT defaultUserShellLinkPath(const std::wstring& appname, _In_ WCHAR* path, _In_ DWORD nSize = MAX_PATH) {
- return commonShellLinkPath(L"APPDATA", appname, path, nSize);
- }
-
- inline HRESULT defaultSystemShellLinkPath(const std::wstring& appname, _In_ WCHAR* path, _In_ DWORD nSize = MAX_PATH) {
- return commonShellLinkPath(L"PROGRAMDATA", appname, path, nSize);
- }
-
- inline PCWSTR AsString(ComPtr<IXmlDocument> &xmlDocument) {
- HSTRING xml;
- ComPtr<IXmlNodeSerializer> ser;
- HRESULT hr = xmlDocument.As<IXmlNodeSerializer>(&ser);
- hr = ser->GetXml(&xml);
- if (SUCCEEDED(hr))
- return DllImporter::WindowsGetStringRawBuffer(xml, nullptr);
- return nullptr;
- }
-
- inline PCWSTR AsString(HSTRING hstring) {
- return DllImporter::WindowsGetStringRawBuffer(hstring, nullptr);
- }
-
- inline HRESULT setNodeStringValue(const std::wstring& string, IXmlNode *node, IXmlDocument *xml) {
- ComPtr<IXmlText> textNode;
- HRESULT hr = xml->CreateTextNode( WinToastStringWrapper(string).Get(), &textNode);
- if (SUCCEEDED(hr)) {
- ComPtr<IXmlNode> stringNode;
- hr = textNode.As(&stringNode);
- if (SUCCEEDED(hr)) {
- ComPtr<IXmlNode> appendedChild;
- hr = node->AppendChild(stringNode.Get(), &appendedChild);
- }
- }
- return hr;
- }
-
- inline HRESULT setEventHandlers(_In_ IToastNotification* notification, _In_ std::shared_ptr<IWinToastHandler> eventHandler, _In_ INT64 expirationTime) {
- EventRegistrationToken activatedToken, dismissedToken, failedToken;
- HRESULT hr = notification->add_Activated(
- Callback < Implements < RuntimeClassFlags<ClassicCom>,
- ITypedEventHandler<ToastNotification*, IInspectable* >> >(
- [eventHandler](IToastNotification*, IInspectable* inspectable)
- {
- IToastActivatedEventArgs *activatedEventArgs;
- HRESULT hr = inspectable->QueryInterface(&activatedEventArgs);
- if (SUCCEEDED(hr)) {
- HSTRING argumentsHandle;
- hr = activatedEventArgs->get_Arguments(&argumentsHandle);
- if (SUCCEEDED(hr)) {
- PCWSTR arguments = Util::AsString(argumentsHandle);
- if (arguments && *arguments) {
- eventHandler->toastActivated(static_cast<int>(wcstol(arguments, nullptr, 10)));
- return S_OK;
- }
- }
- }
- eventHandler->toastActivated();
- return S_OK;
- }).Get(), &activatedToken);
-
- if (SUCCEEDED(hr)) {
- hr = notification->add_Dismissed(Callback < Implements < RuntimeClassFlags<ClassicCom>,
- ITypedEventHandler<ToastNotification*, ToastDismissedEventArgs* >> >(
- [eventHandler, expirationTime](IToastNotification*, IToastDismissedEventArgs* e)
- {
- ToastDismissalReason reason;
- if (SUCCEEDED(e->get_Reason(&reason)))
- {
- if (reason == ToastDismissalReason_UserCanceled && expirationTime && InternalDateTime::Now() >= expirationTime)
- reason = ToastDismissalReason_TimedOut;
- eventHandler->toastDismissed(static_cast<IWinToastHandler::WinToastDismissalReason>(reason));
- }
- return S_OK;
- }).Get(), &dismissedToken);
- if (SUCCEEDED(hr)) {
- hr = notification->add_Failed(Callback < Implements < RuntimeClassFlags<ClassicCom>,
- ITypedEventHandler<ToastNotification*, ToastFailedEventArgs* >> >(
- [eventHandler](IToastNotification*, IToastFailedEventArgs*)
- {
- eventHandler->toastFailed();
- return S_OK;
- }).Get(), &failedToken);
- }
- }
- return hr;
- }
-
- inline HRESULT addAttribute(_In_ IXmlDocument *xml, const std::wstring &name, IXmlNamedNodeMap *attributeMap) {
- ComPtr<ABI::Windows::Data::Xml::Dom::IXmlAttribute> srcAttribute;
- HRESULT hr = xml->CreateAttribute(WinToastStringWrapper(name).Get(), &srcAttribute);
- if (SUCCEEDED(hr)) {
- ComPtr<IXmlNode> node;
- hr = srcAttribute.As(&node);
- if (SUCCEEDED(hr)) {
- ComPtr<IXmlNode> pNode;
- hr = attributeMap->SetNamedItem(node.Get(), &pNode);
- }
- }
- return hr;
- }
-
- inline HRESULT createElement(_In_ IXmlDocument *xml, _In_ const std::wstring& root_node, _In_ const std::wstring& element_name, _In_ const std::vector<std::wstring>& attribute_names) {
- ComPtr<IXmlNodeList> rootList;
- HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(root_node).Get(), &rootList);
- if (SUCCEEDED(hr)) {
- ComPtr<IXmlNode> root;
- hr = rootList->Item(0, &root);
- if (SUCCEEDED(hr)) {
- ComPtr<ABI::Windows::Data::Xml::Dom::IXmlElement> audioElement;
- hr = xml->CreateElement(WinToastStringWrapper(element_name).Get(), &audioElement);
- if (SUCCEEDED(hr)) {
- ComPtr<IXmlNode> audioNodeTmp;
- hr = audioElement.As(&audioNodeTmp);
- if (SUCCEEDED(hr)) {
- ComPtr<IXmlNode> audioNode;
- hr = root->AppendChild(audioNodeTmp.Get(), &audioNode);
- if (SUCCEEDED(hr)) {
- ComPtr<IXmlNamedNodeMap> attributes;
- hr = audioNode->get_Attributes(&attributes);
- if (SUCCEEDED(hr)) {
- for (const auto& it : attribute_names) {
- hr = addAttribute(xml, it, attributes.Get());
- }
- }
- }
- }
- }
- }
- }
- return hr;
- }
-}
-
-WinToast* WinToast::instance() {
- static WinToast instance;
- return &instance;
-}
-
-WinToast::WinToast() :
- _isInitialized(false),
- _hasCoInitialized(false)
-{
- if (!isCompatible()) {
- DEBUG_MSG(L"Warning: Your system is not compatible with this library ");
- }
-}
-
-WinToast::~WinToast() {
- if (_hasCoInitialized) {
- CoUninitialize();
- }
-}
-
-void WinToast::setAppName(_In_ const std::wstring& appName) {
- _appName = appName;
-}
-
-
-void WinToast::setAppUserModelId(_In_ const std::wstring& aumi) {
- _aumi = aumi;
- DEBUG_MSG(L"Default App User Model Id: " << _aumi.c_str());
-}
-
-void WinToast::setShortcutPolicy(_In_ ShortcutPolicy shortcutPolicy) {
- _shortcutPolicy = shortcutPolicy;
-}
-
-bool WinToast::isCompatible() {
- DllImporter::initialize();
- return !((DllImporter::SetCurrentProcessExplicitAppUserModelID == nullptr)
- || (DllImporter::PropVariantToString == nullptr)
- || (DllImporter::RoGetActivationFactory == nullptr)
- || (DllImporter::WindowsCreateStringReference == nullptr)
- || (DllImporter::WindowsDeleteString == nullptr));
-}
-
-bool WinToastLib::WinToast::isSupportingModernFeatures() {
- constexpr auto MinimumSupportedVersion = 6;
- return Util::getRealOSVersion().dwMajorVersion > MinimumSupportedVersion;
-
-}
-std::wstring WinToast::configureAUMI(_In_ const std::wstring &companyName,
- _In_ const std::wstring &productName,
- _In_ const std::wstring &subProduct,
- _In_ const std::wstring &versionInformation)
-{
- std::wstring aumi = companyName;
- aumi += L"." + productName;
- if (subProduct.length() > 0) {
- aumi += L"." + subProduct;
- if (versionInformation.length() > 0) {
- aumi += L"." + versionInformation;
- }
- }
-
- if (aumi.length() > SCHAR_MAX) {
- DEBUG_MSG("Error: max size allowed for AUMI: 128 characters.");
- }
- return aumi;
-}
-
-const std::wstring& WinToast::strerror(WinToastError error) {
- static const std::unordered_map<WinToastError, std::wstring> Labels = {
- {WinToastError::NoError, L"No error. The process was executed correctly"},
- {WinToastError::NotInitialized, L"The library has not been initialized"},
- {WinToastError::SystemNotSupported, L"The OS does not support WinToast"},
- {WinToastError::ShellLinkNotCreated, L"The library was not able to create a Shell Link for the app"},
- {WinToastError::InvalidAppUserModelID, L"The AUMI is not a valid one"},
- {WinToastError::InvalidParameters, L"The parameters used to configure the library are not valid normally because an invalid AUMI or App Name"},
- {WinToastError::NotDisplayed, L"The toast was created correctly but WinToast was not able to display the toast"},
- {WinToastError::UnknownError, L"Unknown error"}
- };
-
- const auto iter = Labels.find(error);
- assert(iter != Labels.end());
- return iter->second;
-}
-
-enum WinToast::ShortcutResult WinToast::createShortcut() {
- if (_aumi.empty() || _appName.empty()) {
- DEBUG_MSG(L"Error: App User Model Id or Appname is empty!");
- return SHORTCUT_MISSING_PARAMETERS;
- }
-
- if (!isCompatible()) {
- DEBUG_MSG(L"Your OS is not compatible with this library! =(");
- return SHORTCUT_INCOMPATIBLE_OS;
- }
-
- if (!_hasCoInitialized) {
- HRESULT initHr = CoInitializeEx(nullptr, COINIT::COINIT_MULTITHREADED);
- if (initHr != RPC_E_CHANGED_MODE) {
- if (FAILED(initHr) && initHr != S_FALSE) {
- DEBUG_MSG(L"Error on COM library initialization!");
- return SHORTCUT_COM_INIT_FAILURE;
- }
- else {
- _hasCoInitialized = true;
- }
- }
- }
-
- bool wasChanged;
- HRESULT hr = validateShellLinkHelper(wasChanged);
- if (SUCCEEDED(hr))
- return wasChanged ? SHORTCUT_WAS_CHANGED : SHORTCUT_UNCHANGED;
-
- hr = createShellLinkHelper();
- return SUCCEEDED(hr) ? SHORTCUT_WAS_CREATED : SHORTCUT_CREATE_FAILED;
-}
-
-bool WinToast::initialize(_Out_ WinToastError* error) {
- _isInitialized = false;
- setError(error, WinToastError::NoError);
-
- if (!isCompatible()) {
- setError(error, WinToastError::SystemNotSupported);
- DEBUG_MSG(L"Error: system not supported.");
- return false;
- }
-
-
- if (_aumi.empty() || _appName.empty()) {
- setError(error, WinToastError::InvalidParameters);
- DEBUG_MSG(L"Error while initializing, did you set up a valid AUMI and App name?");
- return false;
- }
-
- if (_shortcutPolicy != SHORTCUT_POLICY_IGNORE) {
- if (createShortcut() < 0) {
- setError(error, WinToastError::ShellLinkNotCreated);
- DEBUG_MSG(L"Error while attaching the AUMI to the current proccess =(");
- return false;
- }
- }
-
- if (FAILED(DllImporter::SetCurrentProcessExplicitAppUserModelID(_aumi.c_str()))) {
- setError(error, WinToastError::InvalidAppUserModelID);
- DEBUG_MSG(L"Error while attaching the AUMI to the current proccess =(");
- return false;
- }
-
- _isInitialized = true;
- return _isInitialized;
-}
-
-bool WinToast::isInitialized() const {
- return _isInitialized;
-}
-
-const std::wstring& WinToast::appName() const {
- return _appName;
-}
-
-const std::wstring& WinToast::appUserModelId() const {
- return _aumi;
-}
-
-
-HRESULT WinToast::validateShellLinkHelper(_Out_ bool& wasChanged) {
- WCHAR path[MAX_PATH] = { L'\0' };
- Util::defaultUserShellLinkPath(_appName, path);
- // Check if the file exist
- DWORD attr = GetFileAttributesW(path);
- if (attr >= 0xFFFFFFF) {
- // The shortcut may be in the system Start Menu.
- WCHAR systemPath[MAX_PATH] = { L'\0' };
- Util::defaultSystemShellLinkPath(_appName, systemPath);
- attr = GetFileAttributesW(systemPath);
- if (attr >= 0xFFFFFFF) {
- DEBUG_MSG("Error, shell link not found. Try to create a new one in: " << path);
- return E_FAIL;
- }
- wcscpy(path, systemPath);
- }
-
- // Let's load the file as shell link to validate.
- // - Create a shell link
- // - Create a persistant file
- // - Load the path as data for the persistant file
- // - Read the property AUMI and validate with the current
- // - Review if AUMI is equal.
- ComPtr<IShellLink> shellLink;
- HRESULT hr = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&shellLink));
- if (SUCCEEDED(hr)) {
- ComPtr<IPersistFile> persistFile;
- hr = shellLink.As(&persistFile);
- if (SUCCEEDED(hr)) {
- hr = persistFile->Load(path, STGM_READ);
- if (SUCCEEDED(hr)) {
- ComPtr<IPropertyStore> propertyStore;
- hr = shellLink.As(&propertyStore);
- if (SUCCEEDED(hr)) {
- PROPVARIANT appIdPropVar;
- hr = propertyStore->GetValue(PKEY_AppUserModel_ID, &appIdPropVar);
- if (SUCCEEDED(hr)) {
- WCHAR AUMI[MAX_PATH];
- hr = DllImporter::PropVariantToString(appIdPropVar, AUMI, MAX_PATH);
- wasChanged = false;
- if (FAILED(hr) || _aumi != AUMI) {
- if (_shortcutPolicy == SHORTCUT_POLICY_REQUIRE_CREATE) {
- // AUMI Changed for the same app, let's update the current value! =)
- wasChanged = true;
- PropVariantClear(&appIdPropVar);
- hr = InitPropVariantFromString(_aumi.c_str(), &appIdPropVar);
- if (SUCCEEDED(hr)) {
- hr = propertyStore->SetValue(PKEY_AppUserModel_ID, appIdPropVar);
- if (SUCCEEDED(hr)) {
- hr = propertyStore->Commit();
- if (SUCCEEDED(hr) && SUCCEEDED(persistFile->IsDirty())) {
- hr = persistFile->Save(path, TRUE);
- }
- }
- }
- } else {
- // Not allowed to touch the shortcut to fix the AUMI
- hr = E_FAIL;
- }
- }
- PropVariantClear(&appIdPropVar);
- }
- }
- }
- }
- }
- return hr;
-}
-
-
-
-HRESULT WinToast::createShellLinkHelper() {
- if (_shortcutPolicy != SHORTCUT_POLICY_REQUIRE_CREATE) {
- return E_FAIL;
- }
-
- WCHAR exePath[MAX_PATH]{L'\0'};
- WCHAR slPath[MAX_PATH]{L'\0'};
- Util::defaultUserShellLinkPath(_appName, slPath);
- Util::defaultExecutablePath(exePath);
- ComPtr<IShellLinkW> shellLink;
- HRESULT hr = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&shellLink));
- if (SUCCEEDED(hr)) {
- hr = shellLink->SetPath(exePath);
- if (SUCCEEDED(hr)) {
- hr = shellLink->SetArguments(L"");
- if (SUCCEEDED(hr)) {
- hr = shellLink->SetWorkingDirectory(exePath);
- if (SUCCEEDED(hr)) {
- ComPtr<IPropertyStore> propertyStore;
- hr = shellLink.As(&propertyStore);
- if (SUCCEEDED(hr)) {
- PROPVARIANT appIdPropVar;
- hr = InitPropVariantFromString(_aumi.c_str(), &appIdPropVar);
- if (SUCCEEDED(hr)) {
- hr = propertyStore->SetValue(PKEY_AppUserModel_ID, appIdPropVar);
- if (SUCCEEDED(hr)) {
- hr = propertyStore->Commit();
- if (SUCCEEDED(hr)) {
- ComPtr<IPersistFile> persistFile;
- hr = shellLink.As(&persistFile);
- if (SUCCEEDED(hr)) {
- hr = persistFile->Save(slPath, TRUE);
- }
- }
- }
- PropVariantClear(&appIdPropVar);
- }
- }
- }
- }
- }
- }
- return hr;
-}
-
-INT64 WinToast::showToast(_In_ const WinToastTemplate& toast, _In_ IWinToastHandler* handler, _Out_ WinToastError* error) {
- setError(error, WinToastError::NoError);
- INT64 id = -1;
- if (!isInitialized()) {
- setError(error, WinToastError::NotInitialized);
- DEBUG_MSG("Error when launching the toast. WinToast is not initialized.");
- return id;
- }
- if (!handler) {
- setError(error, WinToastError::InvalidHandler);
- DEBUG_MSG("Error when launching the toast. Handler cannot be nullptr.");
- return id;
- }
-
- ComPtr<IToastNotificationManagerStatics> notificationManager;
- HRESULT hr = DllImporter::Wrap_GetActivationFactory(WinToastStringWrapper(RuntimeClass_Windows_UI_Notifications_ToastNotificationManager).Get(), &notificationManager);
- if (SUCCEEDED(hr)) {
- ComPtr<IToastNotifier> notifier;
- hr = notificationManager->CreateToastNotifierWithId(WinToastStringWrapper(_aumi).Get(), &notifier);
- if (SUCCEEDED(hr)) {
- ComPtr<IToastNotificationFactory> notificationFactory;
- hr = DllImporter::Wrap_GetActivationFactory(WinToastStringWrapper(RuntimeClass_Windows_UI_Notifications_ToastNotification).Get(), &notificationFactory);
- if (SUCCEEDED(hr)) {
- ComPtr<IXmlDocument> xmlDocument;
- HRESULT hr = notificationManager->GetTemplateContent(ToastTemplateType(toast.type()), &xmlDocument);
- if (SUCCEEDED(hr)) {
- for (std::size_t i = 0, fieldsCount = toast.textFieldsCount(); i < fieldsCount && SUCCEEDED(hr); i++) {
- hr = setTextFieldHelper(xmlDocument.Get(), toast.textField(WinToastTemplate::TextField(i)), i);
- }
-
- // Modern feature are supported Windows > Windows 10
- if (SUCCEEDED(hr) && isSupportingModernFeatures()) {
-
- // Note that we do this *after* using toast.textFieldsCount() to
- // iterate/fill the template's text fields, since we're adding yet another text field.
- if (SUCCEEDED(hr)
- && !toast.attributionText().empty()) {
- hr = setAttributionTextFieldHelper(xmlDocument.Get(), toast.attributionText());
- }
-
- std::array<WCHAR, 12> buf;
- for (std::size_t i = 0, actionsCount = toast.actionsCount(); i < actionsCount && SUCCEEDED(hr); i++) {
- _snwprintf_s(buf.data(), buf.size(), _TRUNCATE, L"%zd", i);
- hr = addActionHelper(xmlDocument.Get(), toast.actionLabel(i), buf.data());
- }
-
- if (SUCCEEDED(hr)) {
- hr = (toast.audioPath().empty() && toast.audioOption() == WinToastTemplate::AudioOption::Default)
- ? hr : setAudioFieldHelper(xmlDocument.Get(), toast.audioPath(), toast.audioOption());
- }
-
- if (SUCCEEDED(hr) && toast.duration() != WinToastTemplate::Duration::System) {
- hr = addDurationHelper(xmlDocument.Get(),
- (toast.duration() == WinToastTemplate::Duration::Short) ? L"short" : L"long");
- }
-
- if (SUCCEEDED(hr)) {
- hr = addScenarioHelper(xmlDocument.Get(), toast.scenario());
- }
-
- } else {
- DEBUG_MSG("Modern features (Actions/Sounds/Attributes) not supported in this os version");
- }
-
- if (SUCCEEDED(hr)) {
- hr = toast.hasImage() ? setImageFieldHelper(xmlDocument.Get(), toast.imagePath()) : hr;
- if (SUCCEEDED(hr)) {
- ComPtr<IToastNotification> notification;
- hr = notificationFactory->CreateToastNotification(xmlDocument.Get(), &notification);
- if (SUCCEEDED(hr)) {
- INT64 expiration = 0, relativeExpiration = toast.expiration();
- if (relativeExpiration > 0) {
- InternalDateTime expirationDateTime(relativeExpiration);
- expiration = expirationDateTime;
- hr = notification->put_ExpirationTime(&expirationDateTime);
- }
-
- if (SUCCEEDED(hr)) {
- hr = Util::setEventHandlers(notification.Get(), std::shared_ptr<IWinToastHandler>(handler), expiration);
- if (FAILED(hr)) {
- setError(error, WinToastError::InvalidHandler);
- }
- }
-
- if (SUCCEEDED(hr)) {
- GUID guid;
- hr = CoCreateGuid(&guid);
- if (SUCCEEDED(hr)) {
- id = guid.Data1;
- _buffer[id] = notification;
- DEBUG_MSG("xml: " << Util::AsString(xmlDocument));
- hr = notifier->Show(notification.Get());
- if (FAILED(hr)) {
- setError(error, WinToastError::NotDisplayed);
- }
- }
- }
- }
- }
- }
- }
- }
- }
- }
- return FAILED(hr) ? -1 : id;
-}
-
-ComPtr<IToastNotifier> WinToast::notifier(_In_ bool* succeded) const {
- ComPtr<IToastNotificationManagerStatics> notificationManager;
- ComPtr<IToastNotifier> notifier;
- HRESULT hr = DllImporter::Wrap_GetActivationFactory(WinToastStringWrapper(RuntimeClass_Windows_UI_Notifications_ToastNotificationManager).Get(), &notificationManager);
- if (SUCCEEDED(hr)) {
- hr = notificationManager->CreateToastNotifierWithId(WinToastStringWrapper(_aumi).Get(), &notifier);
- }
- *succeded = SUCCEEDED(hr);
- return notifier;
-}
-
-bool WinToast::hideToast(_In_ INT64 id) {
- if (!isInitialized()) {
- DEBUG_MSG("Error when hiding the toast. WinToast is not initialized.");
- return false;
- }
-
- if (_buffer.find(id) != _buffer.end()) {
- auto succeded = false;
- auto notify = notifier(&succeded);
- if (succeded) {
- auto result = notify->Hide(_buffer[id].Get());
- _buffer.erase(id);
- return SUCCEEDED(result);
- }
- }
- return false;
-}
-
-void WinToast::clear() {
- auto succeded = false;
- auto notify = notifier(&succeded);
- if (succeded) {
- auto end = _buffer.end();
- for (auto it = _buffer.begin(); it != end; ++it) {
- notify->Hide(it->second.Get());
- }
- _buffer.clear();
- }
-}
-
-//
-// Available as of Windows 10 Anniversary Update
-// Ref: https://docs.microsoft.com/en-us/windows/uwp/design/shell/tiles-and-notifications/adaptive-interactive-toasts
-//
-// NOTE: This will add a new text field, so be aware when iterating over
-// the toast's text fields or getting a count of them.
-//
-HRESULT WinToast::setAttributionTextFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& text) {
- Util::createElement(xml, L"binding", L"text", { L"placement" });
- ComPtr<IXmlNodeList> nodeList;
- HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"text").Get(), &nodeList);
- if (SUCCEEDED(hr)) {
- UINT32 nodeListLength;
- hr = nodeList->get_Length(&nodeListLength);
- if (SUCCEEDED(hr)) {
- for (UINT32 i = 0; i < nodeListLength; i++) {
- ComPtr<IXmlNode> textNode;
- hr = nodeList->Item(i, &textNode);
- if (SUCCEEDED(hr)) {
- ComPtr<IXmlNamedNodeMap> attributes;
- hr = textNode->get_Attributes(&attributes);
- if (SUCCEEDED(hr)) {
- ComPtr<IXmlNode> editedNode;
- if (SUCCEEDED(hr)) {
- hr = attributes->GetNamedItem(WinToastStringWrapper(L"placement").Get(), &editedNode);
- if (FAILED(hr) || !editedNode) {
- continue;
- }
- hr = Util::setNodeStringValue(L"attribution", editedNode.Get(), xml);
- if (SUCCEEDED(hr)) {
- return setTextFieldHelper(xml, text, i);
- }
- }
- }
- }
- }
- }
- }
- return hr;
-}
-
-HRESULT WinToast::addDurationHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& duration) {
- ComPtr<IXmlNodeList> nodeList;
- HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"toast").Get(), &nodeList);
- if (SUCCEEDED(hr)) {
- UINT32 length;
- hr = nodeList->get_Length(&length);
- if (SUCCEEDED(hr)) {
- ComPtr<IXmlNode> toastNode;
- hr = nodeList->Item(0, &toastNode);
- if (SUCCEEDED(hr)) {
- ComPtr<IXmlElement> toastElement;
- hr = toastNode.As(&toastElement);
- if (SUCCEEDED(hr)) {
- hr = toastElement->SetAttribute(WinToastStringWrapper(L"duration").Get(),
- WinToastStringWrapper(duration).Get());
- }
- }
- }
- }
- return hr;
-}
-
-HRESULT WinToast::addScenarioHelper(_In_ IXmlDocument* xml, _In_ const std::wstring& scenario) {
- ComPtr<IXmlNodeList> nodeList;
- HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"toast").Get(), &nodeList);
- if (SUCCEEDED(hr)) {
- UINT32 length;
- hr = nodeList->get_Length(&length);
- if (SUCCEEDED(hr)) {
- ComPtr<IXmlNode> toastNode;
- hr = nodeList->Item(0, &toastNode);
- if (SUCCEEDED(hr)) {
- ComPtr<IXmlElement> toastElement;
- hr = toastNode.As(&toastElement);
- if (SUCCEEDED(hr)) {
- hr = toastElement->SetAttribute(WinToastStringWrapper(L"scenario").Get(),
- WinToastStringWrapper(scenario).Get());
- }
- }
- }
- }
- return hr;
-}
-
-HRESULT WinToast::setTextFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& text, _In_ UINT32 pos) {
- ComPtr<IXmlNodeList> nodeList;
- HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"text").Get(), &nodeList);
- if (SUCCEEDED(hr)) {
- ComPtr<IXmlNode> node;
- hr = nodeList->Item(pos, &node);
- if (SUCCEEDED(hr)) {
- hr = Util::setNodeStringValue(text, node.Get(), xml);
- }
- }
- return hr;
-}
-
-
-HRESULT WinToast::setImageFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& path) {
- assert(path.size() < MAX_PATH);
-
- wchar_t imagePath[MAX_PATH] = L"file:///";
- HRESULT hr = StringCchCatW(imagePath, MAX_PATH, path.c_str());
- if (SUCCEEDED(hr)) {
- ComPtr<IXmlNodeList> nodeList;
- HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"image").Get(), &nodeList);
- if (SUCCEEDED(hr)) {
- ComPtr<IXmlNode> node;
- hr = nodeList->Item(0, &node);
- if (SUCCEEDED(hr)) {
- ComPtr<IXmlNamedNodeMap> attributes;
- hr = node->get_Attributes(&attributes);
- if (SUCCEEDED(hr)) {
- ComPtr<IXmlNode> editedNode;
- hr = attributes->GetNamedItem(WinToastStringWrapper(L"src").Get(), &editedNode);
- if (SUCCEEDED(hr)) {
- Util::setNodeStringValue(imagePath, editedNode.Get(), xml);
- }
- }
- }
- }
- }
- return hr;
-}
-
-HRESULT WinToast::setAudioFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& path, _In_opt_ WinToastTemplate::AudioOption option) {
- std::vector<std::wstring> attrs;
- if (!path.empty()) attrs.push_back(L"src");
- if (option == WinToastTemplate::AudioOption::Loop) attrs.push_back(L"loop");
- if (option == WinToastTemplate::AudioOption::Silent) attrs.push_back(L"silent");
- Util::createElement(xml, L"toast", L"audio", attrs);
-
- ComPtr<IXmlNodeList> nodeList;
- HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"audio").Get(), &nodeList);
- if (SUCCEEDED(hr)) {
- ComPtr<IXmlNode> node;
- hr = nodeList->Item(0, &node);
- if (SUCCEEDED(hr)) {
- ComPtr<IXmlNamedNodeMap> attributes;
- hr = node->get_Attributes(&attributes);
- if (SUCCEEDED(hr)) {
- ComPtr<IXmlNode> editedNode;
- if (!path.empty()) {
- if (SUCCEEDED(hr)) {
- hr = attributes->GetNamedItem(WinToastStringWrapper(L"src").Get(), &editedNode);
- if (SUCCEEDED(hr)) {
- hr = Util::setNodeStringValue(path, editedNode.Get(), xml);
- }
- }
- }
-
- if (SUCCEEDED(hr)) {
- switch (option) {
- case WinToastTemplate::AudioOption::Loop:
- hr = attributes->GetNamedItem(WinToastStringWrapper(L"loop").Get(), &editedNode);
- if (SUCCEEDED(hr)) {
- hr = Util::setNodeStringValue(L"true", editedNode.Get(), xml);
- }
- break;
- case WinToastTemplate::AudioOption::Silent:
- hr = attributes->GetNamedItem(WinToastStringWrapper(L"silent").Get(), &editedNode);
- if (SUCCEEDED(hr)) {
- hr = Util::setNodeStringValue(L"true", editedNode.Get(), xml);
- }
- default:
- break;
- }
- }
- }
- }
- }
- return hr;
-}
-
-HRESULT WinToast::addActionHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& content, _In_ const std::wstring& arguments) {
- ComPtr<IXmlNodeList> nodeList;
- HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"actions").Get(), &nodeList);
- if (SUCCEEDED(hr)) {
- UINT32 length;
- hr = nodeList->get_Length(&length);
- if (SUCCEEDED(hr)) {
- ComPtr<IXmlNode> actionsNode;
- if (length > 0) {
- hr = nodeList->Item(0, &actionsNode);
- } else {
- hr = xml->GetElementsByTagName(WinToastStringWrapper(L"toast").Get(), &nodeList);
- if (SUCCEEDED(hr)) {
- hr = nodeList->get_Length(&length);
- if (SUCCEEDED(hr)) {
- ComPtr<IXmlNode> toastNode;
- hr = nodeList->Item(0, &toastNode);
- if (SUCCEEDED(hr)) {
- ComPtr<IXmlElement> toastElement;
- hr = toastNode.As(&toastElement);
- if (SUCCEEDED(hr))
- hr = toastElement->SetAttribute(WinToastStringWrapper(L"template").Get(), WinToastStringWrapper(L"ToastGeneric").Get());
- if (SUCCEEDED(hr))
- hr = toastElement->SetAttribute(WinToastStringWrapper(L"duration").Get(), WinToastStringWrapper(L"long").Get());
- if (SUCCEEDED(hr)) {
- ComPtr<IXmlElement> actionsElement;
- hr = xml->CreateElement(WinToastStringWrapper(L"actions").Get(), &actionsElement);
- if (SUCCEEDED(hr)) {
- hr = actionsElement.As(&actionsNode);
- if (SUCCEEDED(hr)) {
- ComPtr<IXmlNode> appendedChild;
- hr = toastNode->AppendChild(actionsNode.Get(), &appendedChild);
- }
- }
- }
- }
- }
- }
- }
- if (SUCCEEDED(hr)) {
- ComPtr<IXmlElement> actionElement;
- hr = xml->CreateElement(WinToastStringWrapper(L"action").Get(), &actionElement);
- if (SUCCEEDED(hr))
- hr = actionElement->SetAttribute(WinToastStringWrapper(L"content").Get(), WinToastStringWrapper(content).Get());
- if (SUCCEEDED(hr))
- hr = actionElement->SetAttribute(WinToastStringWrapper(L"arguments").Get(), WinToastStringWrapper(arguments).Get());
- if (SUCCEEDED(hr)) {
- ComPtr<IXmlNode> actionNode;
- hr = actionElement.As(&actionNode);
- if (SUCCEEDED(hr)) {
- ComPtr<IXmlNode> appendedChild;
- hr = actionsNode->AppendChild(actionNode.Get(), &appendedChild);
- }
- }
- }
- }
- }
- return hr;
-}
-
-void WinToast::setError(_Out_ WinToastError* error, _In_ WinToastError value) {
- if (error) {
- *error = value;
- }
-}
-
-WinToastTemplate::WinToastTemplate(_In_ WinToastTemplateType type) : _type(type) {
- static constexpr std::size_t TextFieldsCount[] = { 1, 2, 2, 3, 1, 2, 2, 3};
- _textFields = std::vector<std::wstring>(TextFieldsCount[type], L"");
-}
-
-WinToastTemplate::~WinToastTemplate() {
- _textFields.clear();
-}
-
-void WinToastTemplate::setTextField(_In_ const std::wstring& txt, _In_ WinToastTemplate::TextField pos) {
- const auto position = static_cast<std::size_t>(pos);
- assert(position < _textFields.size());
- _textFields[position] = txt;
-}
-
-void WinToastTemplate::setImagePath(_In_ const std::wstring& imgPath) {
- _imagePath = imgPath;
-}
-
-void WinToastTemplate::setAudioPath(_In_ const std::wstring& audioPath) {
- _audioPath = audioPath;
-}
-
-void WinToastTemplate::setAudioPath(_In_ AudioSystemFile file) {
- static const std::unordered_map<AudioSystemFile, std::wstring> Files = {
- {AudioSystemFile::DefaultSound, L"ms-winsoundevent:Notification.Default"},
- {AudioSystemFile::IM, L"ms-winsoundevent:Notification.IM"},
- {AudioSystemFile::Mail, L"ms-winsoundevent:Notification.Mail"},
- {AudioSystemFile::Reminder, L"ms-winsoundevent:Notification.Reminder"},
- {AudioSystemFile::SMS, L"ms-winsoundevent:Notification.SMS"},
- {AudioSystemFile::Alarm, L"ms-winsoundevent:Notification.Looping.Alarm"},
- {AudioSystemFile::Alarm2, L"ms-winsoundevent:Notification.Looping.Alarm2"},
- {AudioSystemFile::Alarm3, L"ms-winsoundevent:Notification.Looping.Alarm3"},
- {AudioSystemFile::Alarm4, L"ms-winsoundevent:Notification.Looping.Alarm4"},
- {AudioSystemFile::Alarm5, L"ms-winsoundevent:Notification.Looping.Alarm5"},
- {AudioSystemFile::Alarm6, L"ms-winsoundevent:Notification.Looping.Alarm6"},
- {AudioSystemFile::Alarm7, L"ms-winsoundevent:Notification.Looping.Alarm7"},
- {AudioSystemFile::Alarm8, L"ms-winsoundevent:Notification.Looping.Alarm8"},
- {AudioSystemFile::Alarm9, L"ms-winsoundevent:Notification.Looping.Alarm9"},
- {AudioSystemFile::Alarm10, L"ms-winsoundevent:Notification.Looping.Alarm10"},
- {AudioSystemFile::Call, L"ms-winsoundevent:Notification.Looping.Call"},
- {AudioSystemFile::Call1, L"ms-winsoundevent:Notification.Looping.Call1"},
- {AudioSystemFile::Call2, L"ms-winsoundevent:Notification.Looping.Call2"},
- {AudioSystemFile::Call3, L"ms-winsoundevent:Notification.Looping.Call3"},
- {AudioSystemFile::Call4, L"ms-winsoundevent:Notification.Looping.Call4"},
- {AudioSystemFile::Call5, L"ms-winsoundevent:Notification.Looping.Call5"},
- {AudioSystemFile::Call6, L"ms-winsoundevent:Notification.Looping.Call6"},
- {AudioSystemFile::Call7, L"ms-winsoundevent:Notification.Looping.Call7"},
- {AudioSystemFile::Call8, L"ms-winsoundevent:Notification.Looping.Call8"},
- {AudioSystemFile::Call9, L"ms-winsoundevent:Notification.Looping.Call9"},
- {AudioSystemFile::Call10, L"ms-winsoundevent:Notification.Looping.Call10"},
- };
- const auto iter = Files.find(file);
- assert(iter != Files.end());
- _audioPath = iter->second;
-}
-
-void WinToastTemplate::setAudioOption(_In_ WinToastTemplate::AudioOption audioOption) {
- _audioOption = audioOption;
-}
-
-void WinToastTemplate::setFirstLine(const std::wstring &text) {
- setTextField(text, WinToastTemplate::FirstLine);
-}
-
-void WinToastTemplate::setSecondLine(const std::wstring &text) {
- setTextField(text, WinToastTemplate::SecondLine);
-}
-
-void WinToastTemplate::setThirdLine(const std::wstring &text) {
- setTextField(text, WinToastTemplate::ThirdLine);
-}
-
-void WinToastTemplate::setDuration(_In_ Duration duration) {
- _duration = duration;
-}
-
-void WinToastTemplate::setExpiration(_In_ INT64 millisecondsFromNow) {
- _expiration = millisecondsFromNow;
-}
-
-void WinToastLib::WinToastTemplate::setScenario(Scenario scenario) {
- switch (scenario) {
- case Scenario::Default: _scenario = L"Default"; break;
- case Scenario::Alarm: _scenario = L"Alarm"; break;
- case Scenario::IncomingCall: _scenario = L"IncomingCall"; break;
- case Scenario::Reminder: _scenario = L"Reminder"; break;
- }
-}
-
-void WinToastTemplate::setAttributionText(_In_ const std::wstring& attributionText) {
- _attributionText = attributionText;
-}
-
-void WinToastTemplate::addAction(_In_ const std::wstring & label) {
- _actions.push_back(label);
-}
-
-std::size_t WinToastTemplate::textFieldsCount() const {
- return _textFields.size();
-}
-
-std::size_t WinToastTemplate::actionsCount() const {
- return _actions.size();
-}
-
-bool WinToastTemplate::hasImage() const {
- return _type < WinToastTemplateType::Text01;
-}
-
-const std::vector<std::wstring>& WinToastTemplate::textFields() const {
- return _textFields;
-}
-
-const std::wstring& WinToastTemplate::textField(_In_ TextField pos) const {
- const auto position = static_cast<std::size_t>(pos);
- assert(position < _textFields.size());
- return _textFields[position];
-}
-
-const std::wstring& WinToastTemplate::actionLabel(_In_ std::size_t position) const {
- assert(position < _actions.size());
- return _actions[position];
-}
-
-const std::wstring& WinToastTemplate::imagePath() const {
- return _imagePath;
-}
-
-const std::wstring& WinToastTemplate::audioPath() const {
- return _audioPath;
-}
-
-const std::wstring& WinToastTemplate::attributionText() const {
- return _attributionText;
-}
-
-const std::wstring& WinToastLib::WinToastTemplate::scenario() const {
- return _scenario;
-}
-
-INT64 WinToastTemplate::expiration() const {
- return _expiration;
-}
-
-WinToastTemplate::WinToastTemplateType WinToastTemplate::type() const {
- return _type;
-}
-
-WinToastTemplate::AudioOption WinToastTemplate::audioOption() const {
- return _audioOption;
-}
-
-WinToastTemplate::Duration WinToastTemplate::duration() const {
- return _duration;
-}
diff --git a/third_party/WinToast/wintoastlib.h b/third_party/WinToast/wintoastlib.h
deleted file mode 100644
index 93546dc5b8..0000000000
--- a/third_party/WinToast/wintoastlib.h
+++ /dev/null
@@ -1,234 +0,0 @@
-/* * Copyright (C) 2016-2019 Mohammed Boujemaoui <mohabouje@gmail.com>
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy of
- * this software and associated documentation files (the "Software"), to deal in
- * the Software without restriction, including without limitation the rights to
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
- * the Software, and to permit persons to whom the Software is furnished to do so,
- * subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
- * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
- * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
- * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-#ifndef WINTOASTLIB_H
-#define WINTOASTLIB_H
-#include <Windows.h>
-#include <sdkddkver.h>
-#include <WinUser.h>
-#include <ShObjIdl.h>
-#include <wrl/implements.h>
-#include <wrl/event.h>
-#include <windows.ui.notifications.h>
-#include <strsafe.h>
-#include <Psapi.h>
-#include <ShlObj.h>
-#include <roapi.h>
-#include <propvarutil.h>
-#include <functiondiscoverykeys.h>
-#include <iostream>
-#include <winstring.h>
-#include <string.h>
-#include <vector>
-#include <map>
-using namespace Microsoft::WRL;
-using namespace ABI::Windows::Data::Xml::Dom;
-using namespace ABI::Windows::Foundation;
-using namespace ABI::Windows::UI::Notifications;
-using namespace Windows::Foundation;
-
-
-namespace WinToastLib {
-
- class IWinToastHandler {
- public:
- enum WinToastDismissalReason {
- UserCanceled = ToastDismissalReason::ToastDismissalReason_UserCanceled,
- ApplicationHidden = ToastDismissalReason::ToastDismissalReason_ApplicationHidden,
- TimedOut = ToastDismissalReason::ToastDismissalReason_TimedOut
- };
- virtual ~IWinToastHandler() = default;
- virtual void toastActivated() const = 0;
- virtual void toastActivated(int actionIndex) const = 0;
- virtual void toastDismissed(WinToastDismissalReason state) const = 0;
- virtual void toastFailed() const = 0;
- };
-
- class WinToastTemplate {
- public:
- enum class Scenario { Default, Alarm, IncomingCall, Reminder };
- enum Duration { System, Short, Long };
- enum AudioOption { Default = 0, Silent, Loop };
- enum TextField { FirstLine = 0, SecondLine, ThirdLine };
- enum WinToastTemplateType {
- ImageAndText01 = ToastTemplateType::ToastTemplateType_ToastImageAndText01,
- ImageAndText02 = ToastTemplateType::ToastTemplateType_ToastImageAndText02,
- ImageAndText03 = ToastTemplateType::ToastTemplateType_ToastImageAndText03,
- ImageAndText04 = ToastTemplateType::ToastTemplateType_ToastImageAndText04,
- Text01 = ToastTemplateType::ToastTemplateType_ToastText01,
- Text02 = ToastTemplateType::ToastTemplateType_ToastText02,
- Text03 = ToastTemplateType::ToastTemplateType_ToastText03,
- Text04 = ToastTemplateType::ToastTemplateType_ToastText04,
- };
-
- enum AudioSystemFile {
- DefaultSound,
- IM,
- Mail,
- Reminder,
- SMS,
- Alarm,
- Alarm2,
- Alarm3,
- Alarm4,
- Alarm5,
- Alarm6,
- Alarm7,
- Alarm8,
- Alarm9,
- Alarm10,
- Call,
- Call1,
- Call2,
- Call3,
- Call4,
- Call5,
- Call6,
- Call7,
- Call8,
- Call9,
- Call10,
- };
-
-
- WinToastTemplate(_In_ WinToastTemplateType type = WinToastTemplateType::ImageAndText02);
- ~WinToastTemplate();
-
- void setFirstLine(_In_ const std::wstring& text);
- void setSecondLine(_In_ const std::wstring& text);
- void setThirdLine(_In_ const std::wstring& text);
- void setTextField(_In_ const std::wstring& txt, _In_ TextField pos);
- void setAttributionText(_In_ const std::wstring& attributionText);
- void setImagePath(_In_ const std::wstring& imgPath);
- void setAudioPath(_In_ WinToastTemplate::AudioSystemFile audio);
- void setAudioPath(_In_ const std::wstring& audioPath);
- void setAudioOption(_In_ WinToastTemplate::AudioOption audioOption);
- void setDuration(_In_ Duration duration);
- void setExpiration(_In_ INT64 millisecondsFromNow);
- void setScenario(_In_ Scenario scenario);
- void addAction(_In_ const std::wstring& label);
-
- std::size_t textFieldsCount() const;
- std::size_t actionsCount() const;
- bool hasImage() const;
- const std::vector<std::wstring>& textFields() const;
- const std::wstring& textField(_In_ TextField pos) const;
- const std::wstring& actionLabel(_In_ std::size_t pos) const;
- const std::wstring& imagePath() const;
- const std::wstring& audioPath() const;
- const std::wstring& attributionText() const;
- const std::wstring& scenario() const;
- INT64 expiration() const;
- WinToastTemplateType type() const;
- WinToastTemplate::AudioOption audioOption() const;
- Duration duration() const;
- private:
- std::vector<std::wstring> _textFields{};
- std::vector<std::wstring> _actions{};
- std::wstring _imagePath{};
- std::wstring _audioPath{};
- std::wstring _attributionText{};
- std::wstring _scenario{L"Default"};
- INT64 _expiration{0};
- AudioOption _audioOption{WinToastTemplate::AudioOption::Default};
- WinToastTemplateType _type{WinToastTemplateType::Text01};
- Duration _duration{Duration::System};
- };
-
- class WinToast {
- public:
- enum WinToastError {
- NoError = 0,
- NotInitialized,
- SystemNotSupported,
- ShellLinkNotCreated,
- InvalidAppUserModelID,
- InvalidParameters,
- InvalidHandler,
- NotDisplayed,
- UnknownError
- };
-
- enum ShortcutResult {
- SHORTCUT_UNCHANGED = 0,
- SHORTCUT_WAS_CHANGED = 1,
- SHORTCUT_WAS_CREATED = 2,
-
- SHORTCUT_MISSING_PARAMETERS = -1,
- SHORTCUT_INCOMPATIBLE_OS = -2,
- SHORTCUT_COM_INIT_FAILURE = -3,
- SHORTCUT_CREATE_FAILED = -4
- };
-
- enum ShortcutPolicy {
- /* Don't check, create, or modify a shortcut. */
- SHORTCUT_POLICY_IGNORE = 0,
- /* Require a shortcut with matching AUMI, don't create or modify an existing one. */
- SHORTCUT_POLICY_REQUIRE_NO_CREATE = 1,
- /* Require a shortcut with matching AUMI, create if missing, modify if not matching.
- * This is the default. */
- SHORTCUT_POLICY_REQUIRE_CREATE = 2,
- };
-
- WinToast(void);
- virtual ~WinToast();
- static WinToast* instance();
- static bool isCompatible();
- static bool isSupportingModernFeatures();
- static std::wstring configureAUMI(_In_ const std::wstring& companyName,
- _In_ const std::wstring& productName,
- _In_ const std::wstring& subProduct = std::wstring(),
- _In_ const std::wstring& versionInformation = std::wstring());
- static const std::wstring& strerror(_In_ WinToastError error);
- virtual bool initialize(_Out_ WinToastError* error = nullptr);
- virtual bool isInitialized() const;
- virtual bool hideToast(_In_ INT64 id);
- virtual INT64 showToast(_In_ const WinToastTemplate& toast, _In_ IWinToastHandler* handler, _Out_ WinToastError* error = nullptr);
- virtual void clear();
- virtual enum ShortcutResult createShortcut();
-
- const std::wstring& appName() const;
- const std::wstring& appUserModelId() const;
- void setAppUserModelId(_In_ const std::wstring& aumi);
- void setAppName(_In_ const std::wstring& appName);
- void setShortcutPolicy(_In_ ShortcutPolicy policy);
-
- protected:
- bool _isInitialized{false};
- bool _hasCoInitialized{false};
- ShortcutPolicy _shortcutPolicy{SHORTCUT_POLICY_REQUIRE_CREATE};
- std::wstring _appName{};
- std::wstring _aumi{};
- std::map<INT64, ComPtr<IToastNotification>> _buffer{};
-
- HRESULT validateShellLinkHelper(_Out_ bool& wasChanged);
- HRESULT createShellLinkHelper();
- HRESULT setImageFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& path);
- HRESULT setAudioFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& path, _In_opt_ WinToastTemplate::AudioOption option = WinToastTemplate::AudioOption::Default);
- HRESULT setTextFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& text, _In_ UINT32 pos);
- HRESULT setAttributionTextFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& text);
- HRESULT addActionHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& action, _In_ const std::wstring& arguments);
- HRESULT addDurationHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& duration);
- HRESULT addScenarioHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& scenario);
- ComPtr<IToastNotifier> notifier(_In_ bool* succeded) const;
- void setError(_Out_ WinToastError* error, _In_ WinToastError value);
- };
-}
-#endif // WINTOASTLIB_H
diff --git a/third_party/aom/AUTHORS b/third_party/aom/AUTHORS
index ade7a1a5d0..509c0d1c9d 100644
--- a/third_party/aom/AUTHORS
+++ b/third_party/aom/AUTHORS
@@ -235,6 +235,7 @@ Ronald S. Bultje <rsbultje@gmail.com>
Rostislav Pehlivanov <rpehlivanov@mozilla.com>
Ruiling Song <ruiling.song@intel.com>
Rui Ueyama <ruiu@google.com>
+Ruoyu Zhong <zhongruoyu@outlook.com>
Rupert Swarbrick <rupert.swarbrick@argondesign.com>
Ryan Lei <ryanlei@fb.com>
Ryan Overbeck <rover@google.com>
diff --git a/third_party/aom/CHANGELOG b/third_party/aom/CHANGELOG
index b243837d3c..b5c1afbba2 100644
--- a/third_party/aom/CHANGELOG
+++ b/third_party/aom/CHANGELOG
@@ -1,3 +1,39 @@
+2024-01-17 v3.8.1
+ This release includes several bug fixes. This release is ABI
+ compatible with the last release. See
+ https://aomedia.googlesource.com/aom/+log/v3.8.0..v3.8.1 for all the
+ commits in this release.
+
+ - Bug Fixes
+ * aomedia:3520: get_cubic_kernel_dbl: Assertion `0 <= x && x < 1'
+ failed.
+ * aomedia:3526: alloc_compressor_data() is called during every
+ aom_codec_control() call on the encoder.
+ * aomedia:3527: aom/av1/encoder/mcomp.c:1810: av1_full_pixel_search:
+ Assertion `ms_params->ms_buffers.ref->width ==
+ ms_params->ms_buffers.src->width' failed.
+ * aomedia:3534: libaom encoder crashed by AOM_USAGE_ALL_INTRA and
+ AOM_EFLAG_NO_REF_LAST flags.
+ * b/310455204: Recreate workers if necessary.
+ * b/310548198: Update frame size in actual encoding.
+ * b/314858909: Do not use adaptive error estimate.
+ * Fix a hang of cmake on arm64 macOS with cmake 3.27.0 or later.
+
+2024-01-18 v3.7.2
+ This release includes three bug fixes. This release is ABI compatible
+ with the last release. See
+ https://aomedia.googlesource.com/aom/+log/v3.7.1..v3.7.2 for all the
+ commits in this release.
+
+ - Bug Fixes
+ * aomedia:3520: get_cubic_kernel_dbl: Assertion `0 <= x && x < 1'
+ failed.
+ * aomedia:3526: alloc_compressor_data() is called during every
+ aom_codec_control() call on the encoder. Note that this partially
+ reverts the fix for bug aomedia:3349.
+ * b/310457427 and b/310766628: Only use rec_sse in CBR mode.
+ * Fix a hang of cmake on arm64 macOS with cmake 3.27.0 or later.
+
2023-11-30 v3.8.0
This release includes new codec interfaces, compression efficiency and
perceptual improvements, speedup and memory optimizations and many bug
diff --git a/third_party/aom/CMakeLists.txt b/third_party/aom/CMakeLists.txt
index 76944e6917..a02b220bdb 100644
--- a/third_party/aom/CMakeLists.txt
+++ b/third_party/aom/CMakeLists.txt
@@ -59,7 +59,7 @@ endif()
#
# We set SO_FILE_VERSION = [c-a].a.r
set(LT_CURRENT 11)
-set(LT_REVISION 0)
+set(LT_REVISION 1)
set(LT_AGE 8)
math(EXPR SO_VERSION "${LT_CURRENT} - ${LT_AGE}")
set(SO_FILE_VERSION "${SO_VERSION}.${LT_AGE}.${LT_REVISION}")
diff --git a/third_party/aom/aom/src/aom_codec.c b/third_party/aom/aom/src/aom_codec.c
index 512fd28196..316cc6fd23 100644
--- a/third_party/aom/aom/src/aom_codec.c
+++ b/third_party/aom/aom/src/aom_codec.c
@@ -170,6 +170,7 @@ void aom_internal_error(struct aom_internal_error_info *info,
void aom_internal_error_copy(struct aom_internal_error_info *info,
const struct aom_internal_error_info *src) {
assert(info != src);
+ assert(!src->setjmp);
if (!src->has_detail) {
aom_internal_error(info, src->error_code, NULL);
diff --git a/third_party/aom/aom_dsp/aom_dsp_rtcd_defs.pl b/third_party/aom/aom_dsp/aom_dsp_rtcd_defs.pl
index 4b49605e53..7bb156ac59 100755
--- a/third_party/aom/aom_dsp/aom_dsp_rtcd_defs.pl
+++ b/third_party/aom/aom_dsp/aom_dsp_rtcd_defs.pl
@@ -1352,16 +1352,19 @@ if (aom_config("CONFIG_AV1_ENCODER") eq "yes") {
add_proto qw/unsigned int/, "aom_highbd_${bd}_mse8x16", "const uint8_t *src_ptr, int source_stride, const uint8_t *ref_ptr, int recon_stride, unsigned int *sse";
add_proto qw/unsigned int/, "aom_highbd_${bd}_mse8x8", "const uint8_t *src_ptr, int source_stride, const uint8_t *ref_ptr, int recon_stride, unsigned int *sse";
- specialize "aom_highbd_${bd}_mse16x16", qw/sse2 neon sve/;
- specialize "aom_highbd_${bd}_mse16x8", qw/neon sve/;
- specialize "aom_highbd_${bd}_mse8x16", qw/neon sve/;
- specialize "aom_highbd_${bd}_mse8x8", qw/sse2 neon sve/;
- }
+ if ($bd eq 8) {
+ specialize "aom_highbd_${bd}_mse16x16", qw/sse2 neon neon_dotprod/;
+ specialize "aom_highbd_${bd}_mse16x8", qw/neon neon_dotprod/;
+ specialize "aom_highbd_${bd}_mse8x16", qw/neon neon_dotprod/;
+ specialize "aom_highbd_${bd}_mse8x8", qw/sse2 neon neon_dotprod/;
+ } else {
+ specialize "aom_highbd_${bd}_mse16x16", qw/sse2 neon sve/;
+ specialize "aom_highbd_${bd}_mse16x8", qw/neon sve/;
+ specialize "aom_highbd_${bd}_mse8x16", qw/neon sve/;
+ specialize "aom_highbd_${bd}_mse8x8", qw/sse2 neon sve/;
+ }
- specialize "aom_highbd_8_mse16x16", qw/neon_dotprod/;
- specialize "aom_highbd_8_mse16x8", qw/neon_dotprod/;
- specialize "aom_highbd_8_mse8x16", qw/neon_dotprod/;
- specialize "aom_highbd_8_mse8x8", qw/neon_dotprod/;
+ }
}
#
diff --git a/third_party/aom/aom_dsp/arm/highbd_variance_sve.c b/third_party/aom/aom_dsp/arm/highbd_variance_sve.c
index d0058bfa90..a2c30a1688 100644
--- a/third_party/aom/aom_dsp/arm/highbd_variance_sve.c
+++ b/third_party/aom/aom_dsp/arm/highbd_variance_sve.c
@@ -348,15 +348,6 @@ static INLINE uint32_t highbd_mse_wxh_sve(const uint16_t *src_ptr,
}
#define HIGHBD_MSE_WXH_SVE(w, h) \
- uint32_t aom_highbd_8_mse##w##x##h##_sve( \
- const uint8_t *src_ptr, int src_stride, const uint8_t *ref_ptr, \
- int ref_stride, uint32_t *sse) { \
- uint16_t *src = CONVERT_TO_SHORTPTR(src_ptr); \
- uint16_t *ref = CONVERT_TO_SHORTPTR(ref_ptr); \
- highbd_mse_wxh_sve(src, src_stride, ref, ref_stride, w, h, sse); \
- return *sse; \
- } \
- \
uint32_t aom_highbd_10_mse##w##x##h##_sve( \
const uint8_t *src_ptr, int src_stride, const uint8_t *ref_ptr, \
int ref_stride, uint32_t *sse) { \
diff --git a/third_party/aom/aom_dsp/arm/intrapred_neon.c b/third_party/aom/aom_dsp/arm/intrapred_neon.c
index d8dc60c1fe..c3716b3a78 100644
--- a/third_party/aom/aom_dsp/arm/intrapred_neon.c
+++ b/third_party/aom/aom_dsp/arm/intrapred_neon.c
@@ -11,6 +11,7 @@
#include <arm_neon.h>
#include <assert.h>
+#include <stdint.h>
#include "config/aom_config.h"
#include "config/aom_dsp_rtcd.h"
diff --git a/third_party/aom/aom_dsp/flow_estimation/arm/disflow_neon.c b/third_party/aom/aom_dsp/flow_estimation/arm/disflow_neon.c
index ee42be7393..62729133e3 100644
--- a/third_party/aom/aom_dsp/flow_estimation/arm/disflow_neon.c
+++ b/third_party/aom/aom_dsp/flow_estimation/arm/disflow_neon.c
@@ -22,7 +22,7 @@
static INLINE void get_cubic_kernel_dbl(double x, double kernel[4]) {
// Check that the fractional position is in range.
//
- // Note: x is calculated from (eg.) `u_frac = u - floor(u)`.
+ // Note: x is calculated from, e.g., `u_frac = u - floor(u)`.
// Mathematically, this implies that 0 <= x < 1. However, in practice it is
// possible to have x == 1 due to floating point rounding. This is fine,
// and we still interpolate correctly if we allow x = 1.
diff --git a/third_party/aom/aom_dsp/flow_estimation/corner_match.c b/third_party/aom/aom_dsp/flow_estimation/corner_match.c
index cef719b68d..dc7589a8c6 100644
--- a/third_party/aom/aom_dsp/flow_estimation/corner_match.c
+++ b/third_party/aom/aom_dsp/flow_estimation/corner_match.c
@@ -224,7 +224,7 @@ bool av1_compute_global_motion_feature_match(
*mem_alloc_failed = true;
return false;
}
- if (!av1_compute_corner_list(src_pyramid, src_corners)) {
+ if (!av1_compute_corner_list(ref_pyramid, ref_corners)) {
*mem_alloc_failed = true;
return false;
}
diff --git a/third_party/aom/aom_dsp/flow_estimation/disflow.c b/third_party/aom/aom_dsp/flow_estimation/disflow.c
index 147a8ab3b3..82b531c729 100644
--- a/third_party/aom/aom_dsp/flow_estimation/disflow.c
+++ b/third_party/aom/aom_dsp/flow_estimation/disflow.c
@@ -25,7 +25,7 @@
#include "config/aom_dsp_rtcd.h"
// Amount to downsample the flow field by.
-// eg. DOWNSAMPLE_SHIFT = 2 (DOWNSAMPLE_FACTOR == 4) means we calculate
+// e.g., DOWNSAMPLE_SHIFT = 2 (DOWNSAMPLE_FACTOR == 4) means we calculate
// one flow point for each 4x4 pixel region of the frame
// Must be a power of 2
#define DOWNSAMPLE_SHIFT 3
@@ -66,7 +66,7 @@ static double flow_upscale_filter[2][FLOW_UPSCALE_TAPS] = {
static INLINE void get_cubic_kernel_dbl(double x, double kernel[4]) {
// Check that the fractional position is in range.
//
- // Note: x is calculated from (eg.) `u_frac = u - floor(u)`.
+ // Note: x is calculated from, e.g., `u_frac = u - floor(u)`.
// Mathematically, this implies that 0 <= x < 1. However, in practice it is
// possible to have x == 1 due to floating point rounding. This is fine,
// and we still interpolate correctly if we allow x = 1.
diff --git a/third_party/aom/aom_dsp/flow_estimation/x86/disflow_sse4.c b/third_party/aom/aom_dsp/flow_estimation/x86/disflow_sse4.c
index d2b04c1973..2c5effd638 100644
--- a/third_party/aom/aom_dsp/flow_estimation/x86/disflow_sse4.c
+++ b/third_party/aom/aom_dsp/flow_estimation/x86/disflow_sse4.c
@@ -30,7 +30,7 @@
static INLINE void get_cubic_kernel_dbl(double x, double kernel[4]) {
// Check that the fractional position is in range.
//
- // Note: x is calculated from (eg.) `u_frac = u - floor(u)`.
+ // Note: x is calculated from, e.g., `u_frac = u - floor(u)`.
// Mathematically, this implies that 0 <= x < 1. However, in practice it is
// possible to have x == 1 due to floating point rounding. This is fine,
// and we still interpolate correctly if we allow x = 1.
diff --git a/third_party/aom/av1/av1.cmake b/third_party/aom/av1/av1.cmake
index 15577d0c0e..c66a748d40 100644
--- a/third_party/aom/av1/av1.cmake
+++ b/third_party/aom/av1/av1.cmake
@@ -406,6 +406,7 @@ list(APPEND AOM_AV1_COMMON_INTRIN_NEON_I8MM
"${AOM_ROOT}/av1/common/arm/warp_plane_neon_i8mm.c")
list(APPEND AOM_AV1_COMMON_INTRIN_SVE
+ "${AOM_ROOT}/av1/common/arm/highbd_warp_plane_sve.c"
"${AOM_ROOT}/av1/common/arm/warp_plane_sve.c")
list(APPEND AOM_AV1_ENCODER_INTRIN_SSE4_2
diff --git a/third_party/aom/av1/common/arm/highbd_warp_plane_sve.c b/third_party/aom/av1/common/arm/highbd_warp_plane_sve.c
new file mode 100644
index 0000000000..7a14f21846
--- /dev/null
+++ b/third_party/aom/av1/common/arm/highbd_warp_plane_sve.c
@@ -0,0 +1,293 @@
+/*
+ * Copyright (c) 2024, Alliance for Open Media. All rights reserved
+ *
+ * This source code is subject to the terms of the BSD 2 Clause License and
+ * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+ * was not distributed with this source code in the LICENSE file, you can
+ * obtain it at www.aomedia.org/license/software. If the Alliance for Open
+ * Media Patent License 1.0 was not distributed with this source code in the
+ * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+ */
+
+#include <arm_neon.h>
+#include <assert.h>
+#include <stdbool.h>
+#include <arm_neon_sve_bridge.h>
+
+#include "aom_dsp/aom_dsp_common.h"
+#include "aom_dsp/arm/dot_sve.h"
+#include "aom_dsp/arm/mem_neon.h"
+#include "aom_dsp/arm/transpose_neon.h"
+#include "aom_ports/mem.h"
+#include "av1/common/scale.h"
+#include "av1/common/warped_motion.h"
+#include "config/av1_rtcd.h"
+#include "highbd_warp_plane_neon.h"
+
+static INLINE int16x8_t highbd_horizontal_filter_4x1_f4(uint16x8x2_t in, int bd,
+ int sx, int alpha) {
+ int16x8_t f[4];
+ load_filters_4(f, sx, alpha);
+
+ int16x8_t rv0 = vextq_s16(vreinterpretq_s16_u16(in.val[0]),
+ vreinterpretq_s16_u16(in.val[1]), 0);
+ int16x8_t rv1 = vextq_s16(vreinterpretq_s16_u16(in.val[0]),
+ vreinterpretq_s16_u16(in.val[1]), 1);
+ int16x8_t rv2 = vextq_s16(vreinterpretq_s16_u16(in.val[0]),
+ vreinterpretq_s16_u16(in.val[1]), 2);
+ int16x8_t rv3 = vextq_s16(vreinterpretq_s16_u16(in.val[0]),
+ vreinterpretq_s16_u16(in.val[1]), 3);
+
+ int64x2_t m0 = aom_sdotq_s16(vdupq_n_s64(0), rv0, f[0]);
+ int64x2_t m1 = aom_sdotq_s16(vdupq_n_s64(0), rv1, f[1]);
+ int64x2_t m2 = aom_sdotq_s16(vdupq_n_s64(0), rv2, f[2]);
+ int64x2_t m3 = aom_sdotq_s16(vdupq_n_s64(0), rv3, f[3]);
+
+ int64x2_t m01 = vpaddq_s64(m0, m1);
+ int64x2_t m23 = vpaddq_s64(m2, m3);
+
+ const int round0 = bd == 12 ? ROUND0_BITS + 2 : ROUND0_BITS;
+ const int offset_bits_horiz = bd + FILTER_BITS - 1;
+
+ int32x4_t res = vcombine_s32(vmovn_s64(m01), vmovn_s64(m23));
+ res = vaddq_s32(res, vdupq_n_s32(1 << offset_bits_horiz));
+ res = vrshlq_s32(res, vdupq_n_s32(-round0));
+ return vcombine_s16(vmovn_s32(res), vdup_n_s16(0));
+}
+
+static INLINE int16x8_t highbd_horizontal_filter_8x1_f8(uint16x8x2_t in, int bd,
+ int sx, int alpha) {
+ int16x8_t f[8];
+ load_filters_8(f, sx, alpha);
+
+ int16x8_t rv0 = vextq_s16(vreinterpretq_s16_u16(in.val[0]),
+ vreinterpretq_s16_u16(in.val[1]), 0);
+ int16x8_t rv1 = vextq_s16(vreinterpretq_s16_u16(in.val[0]),
+ vreinterpretq_s16_u16(in.val[1]), 1);
+ int16x8_t rv2 = vextq_s16(vreinterpretq_s16_u16(in.val[0]),
+ vreinterpretq_s16_u16(in.val[1]), 2);
+ int16x8_t rv3 = vextq_s16(vreinterpretq_s16_u16(in.val[0]),
+ vreinterpretq_s16_u16(in.val[1]), 3);
+ int16x8_t rv4 = vextq_s16(vreinterpretq_s16_u16(in.val[0]),
+ vreinterpretq_s16_u16(in.val[1]), 4);
+ int16x8_t rv5 = vextq_s16(vreinterpretq_s16_u16(in.val[0]),
+ vreinterpretq_s16_u16(in.val[1]), 5);
+ int16x8_t rv6 = vextq_s16(vreinterpretq_s16_u16(in.val[0]),
+ vreinterpretq_s16_u16(in.val[1]), 6);
+ int16x8_t rv7 = vextq_s16(vreinterpretq_s16_u16(in.val[0]),
+ vreinterpretq_s16_u16(in.val[1]), 7);
+
+ int64x2_t m0 = aom_sdotq_s16(vdupq_n_s64(0), rv0, f[0]);
+ int64x2_t m1 = aom_sdotq_s16(vdupq_n_s64(0), rv1, f[1]);
+ int64x2_t m2 = aom_sdotq_s16(vdupq_n_s64(0), rv2, f[2]);
+ int64x2_t m3 = aom_sdotq_s16(vdupq_n_s64(0), rv3, f[3]);
+ int64x2_t m4 = aom_sdotq_s16(vdupq_n_s64(0), rv4, f[4]);
+ int64x2_t m5 = aom_sdotq_s16(vdupq_n_s64(0), rv5, f[5]);
+ int64x2_t m6 = aom_sdotq_s16(vdupq_n_s64(0), rv6, f[6]);
+ int64x2_t m7 = aom_sdotq_s16(vdupq_n_s64(0), rv7, f[7]);
+
+ int64x2_t m01 = vpaddq_s64(m0, m1);
+ int64x2_t m23 = vpaddq_s64(m2, m3);
+ int64x2_t m45 = vpaddq_s64(m4, m5);
+ int64x2_t m67 = vpaddq_s64(m6, m7);
+
+ const int round0 = bd == 12 ? ROUND0_BITS + 2 : ROUND0_BITS;
+ const int offset_bits_horiz = bd + FILTER_BITS - 1;
+
+ int32x4_t res0 = vcombine_s32(vmovn_s64(m01), vmovn_s64(m23));
+ int32x4_t res1 = vcombine_s32(vmovn_s64(m45), vmovn_s64(m67));
+ res0 = vaddq_s32(res0, vdupq_n_s32(1 << offset_bits_horiz));
+ res1 = vaddq_s32(res1, vdupq_n_s32(1 << offset_bits_horiz));
+ res0 = vrshlq_s32(res0, vdupq_n_s32(-round0));
+ res1 = vrshlq_s32(res1, vdupq_n_s32(-round0));
+ return vcombine_s16(vmovn_s32(res0), vmovn_s32(res1));
+}
+
+static INLINE int16x8_t highbd_horizontal_filter_4x1_f1(uint16x8x2_t in, int bd,
+ int sx) {
+ int16x8_t f = load_filters_1(sx);
+
+ int16x8_t rv0 = vextq_s16(vreinterpretq_s16_u16(in.val[0]),
+ vreinterpretq_s16_u16(in.val[1]), 0);
+ int16x8_t rv1 = vextq_s16(vreinterpretq_s16_u16(in.val[0]),
+ vreinterpretq_s16_u16(in.val[1]), 1);
+ int16x8_t rv2 = vextq_s16(vreinterpretq_s16_u16(in.val[0]),
+ vreinterpretq_s16_u16(in.val[1]), 2);
+ int16x8_t rv3 = vextq_s16(vreinterpretq_s16_u16(in.val[0]),
+ vreinterpretq_s16_u16(in.val[1]), 3);
+
+ int64x2_t m0 = aom_sdotq_s16(vdupq_n_s64(0), rv0, f);
+ int64x2_t m1 = aom_sdotq_s16(vdupq_n_s64(0), rv1, f);
+ int64x2_t m2 = aom_sdotq_s16(vdupq_n_s64(0), rv2, f);
+ int64x2_t m3 = aom_sdotq_s16(vdupq_n_s64(0), rv3, f);
+
+ int64x2_t m01 = vpaddq_s64(m0, m1);
+ int64x2_t m23 = vpaddq_s64(m2, m3);
+
+ const int round0 = bd == 12 ? ROUND0_BITS + 2 : ROUND0_BITS;
+ const int offset_bits_horiz = bd + FILTER_BITS - 1;
+
+ int32x4_t res = vcombine_s32(vmovn_s64(m01), vmovn_s64(m23));
+ res = vaddq_s32(res, vdupq_n_s32(1 << offset_bits_horiz));
+ res = vrshlq_s32(res, vdupq_n_s32(-round0));
+ return vcombine_s16(vmovn_s32(res), vdup_n_s16(0));
+}
+
+static INLINE int16x8_t highbd_horizontal_filter_8x1_f1(uint16x8x2_t in, int bd,
+ int sx) {
+ int16x8_t f = load_filters_1(sx);
+
+ int16x8_t rv0 = vextq_s16(vreinterpretq_s16_u16(in.val[0]),
+ vreinterpretq_s16_u16(in.val[1]), 0);
+ int16x8_t rv1 = vextq_s16(vreinterpretq_s16_u16(in.val[0]),
+ vreinterpretq_s16_u16(in.val[1]), 1);
+ int16x8_t rv2 = vextq_s16(vreinterpretq_s16_u16(in.val[0]),
+ vreinterpretq_s16_u16(in.val[1]), 2);
+ int16x8_t rv3 = vextq_s16(vreinterpretq_s16_u16(in.val[0]),
+ vreinterpretq_s16_u16(in.val[1]), 3);
+ int16x8_t rv4 = vextq_s16(vreinterpretq_s16_u16(in.val[0]),
+ vreinterpretq_s16_u16(in.val[1]), 4);
+ int16x8_t rv5 = vextq_s16(vreinterpretq_s16_u16(in.val[0]),
+ vreinterpretq_s16_u16(in.val[1]), 5);
+ int16x8_t rv6 = vextq_s16(vreinterpretq_s16_u16(in.val[0]),
+ vreinterpretq_s16_u16(in.val[1]), 6);
+ int16x8_t rv7 = vextq_s16(vreinterpretq_s16_u16(in.val[0]),
+ vreinterpretq_s16_u16(in.val[1]), 7);
+
+ int64x2_t m0 = aom_sdotq_s16(vdupq_n_s64(0), rv0, f);
+ int64x2_t m1 = aom_sdotq_s16(vdupq_n_s64(0), rv1, f);
+ int64x2_t m2 = aom_sdotq_s16(vdupq_n_s64(0), rv2, f);
+ int64x2_t m3 = aom_sdotq_s16(vdupq_n_s64(0), rv3, f);
+ int64x2_t m4 = aom_sdotq_s16(vdupq_n_s64(0), rv4, f);
+ int64x2_t m5 = aom_sdotq_s16(vdupq_n_s64(0), rv5, f);
+ int64x2_t m6 = aom_sdotq_s16(vdupq_n_s64(0), rv6, f);
+ int64x2_t m7 = aom_sdotq_s16(vdupq_n_s64(0), rv7, f);
+
+ int64x2_t m01 = vpaddq_s64(m0, m1);
+ int64x2_t m23 = vpaddq_s64(m2, m3);
+ int64x2_t m45 = vpaddq_s64(m4, m5);
+ int64x2_t m67 = vpaddq_s64(m6, m7);
+
+ const int round0 = bd == 12 ? ROUND0_BITS + 2 : ROUND0_BITS;
+ const int offset_bits_horiz = bd + FILTER_BITS - 1;
+
+ int32x4_t res0 = vcombine_s32(vmovn_s64(m01), vmovn_s64(m23));
+ int32x4_t res1 = vcombine_s32(vmovn_s64(m45), vmovn_s64(m67));
+ res0 = vaddq_s32(res0, vdupq_n_s32(1 << offset_bits_horiz));
+ res1 = vaddq_s32(res1, vdupq_n_s32(1 << offset_bits_horiz));
+ res0 = vrshlq_s32(res0, vdupq_n_s32(-round0));
+ res1 = vrshlq_s32(res1, vdupq_n_s32(-round0));
+ return vcombine_s16(vmovn_s32(res0), vmovn_s32(res1));
+}
+
+static INLINE int32x4_t vertical_filter_4x1_f1(const int16x8_t *tmp, int sy) {
+ const int16x8_t f = load_filters_1(sy);
+ const int16x4_t f0123 = vget_low_s16(f);
+ const int16x4_t f4567 = vget_high_s16(f);
+
+ // No benefit to using SDOT here, the cost of rearrangement is too high.
+ int32x4_t m0123 = vmull_lane_s16(vget_low_s16(tmp[0]), f0123, 0);
+ m0123 = vmlal_lane_s16(m0123, vget_low_s16(tmp[1]), f0123, 1);
+ m0123 = vmlal_lane_s16(m0123, vget_low_s16(tmp[2]), f0123, 2);
+ m0123 = vmlal_lane_s16(m0123, vget_low_s16(tmp[3]), f0123, 3);
+ m0123 = vmlal_lane_s16(m0123, vget_low_s16(tmp[4]), f4567, 0);
+ m0123 = vmlal_lane_s16(m0123, vget_low_s16(tmp[5]), f4567, 1);
+ m0123 = vmlal_lane_s16(m0123, vget_low_s16(tmp[6]), f4567, 2);
+ m0123 = vmlal_lane_s16(m0123, vget_low_s16(tmp[7]), f4567, 3);
+ return m0123;
+}
+
+static INLINE int32x4x2_t vertical_filter_8x1_f1(const int16x8_t *tmp, int sy) {
+ const int16x8_t f = load_filters_1(sy);
+ const int16x4_t f0123 = vget_low_s16(f);
+ const int16x4_t f4567 = vget_high_s16(f);
+
+ // No benefit to using SDOT here, the cost of rearrangement is too high.
+ int32x4_t m0123 = vmull_lane_s16(vget_low_s16(tmp[0]), f0123, 0);
+ m0123 = vmlal_lane_s16(m0123, vget_low_s16(tmp[1]), f0123, 1);
+ m0123 = vmlal_lane_s16(m0123, vget_low_s16(tmp[2]), f0123, 2);
+ m0123 = vmlal_lane_s16(m0123, vget_low_s16(tmp[3]), f0123, 3);
+ m0123 = vmlal_lane_s16(m0123, vget_low_s16(tmp[4]), f4567, 0);
+ m0123 = vmlal_lane_s16(m0123, vget_low_s16(tmp[5]), f4567, 1);
+ m0123 = vmlal_lane_s16(m0123, vget_low_s16(tmp[6]), f4567, 2);
+ m0123 = vmlal_lane_s16(m0123, vget_low_s16(tmp[7]), f4567, 3);
+
+ int32x4_t m4567 = vmull_lane_s16(vget_high_s16(tmp[0]), f0123, 0);
+ m4567 = vmlal_lane_s16(m4567, vget_high_s16(tmp[1]), f0123, 1);
+ m4567 = vmlal_lane_s16(m4567, vget_high_s16(tmp[2]), f0123, 2);
+ m4567 = vmlal_lane_s16(m4567, vget_high_s16(tmp[3]), f0123, 3);
+ m4567 = vmlal_lane_s16(m4567, vget_high_s16(tmp[4]), f4567, 0);
+ m4567 = vmlal_lane_s16(m4567, vget_high_s16(tmp[5]), f4567, 1);
+ m4567 = vmlal_lane_s16(m4567, vget_high_s16(tmp[6]), f4567, 2);
+ m4567 = vmlal_lane_s16(m4567, vget_high_s16(tmp[7]), f4567, 3);
+ return (int32x4x2_t){ { m0123, m4567 } };
+}
+
+static INLINE int32x4_t vertical_filter_4x1_f4(const int16x8_t *tmp, int sy,
+ int gamma) {
+ int16x8_t s0, s1, s2, s3;
+ transpose_elems_s16_4x8(
+ vget_low_s16(tmp[0]), vget_low_s16(tmp[1]), vget_low_s16(tmp[2]),
+ vget_low_s16(tmp[3]), vget_low_s16(tmp[4]), vget_low_s16(tmp[5]),
+ vget_low_s16(tmp[6]), vget_low_s16(tmp[7]), &s0, &s1, &s2, &s3);
+
+ int16x8_t f[4];
+ load_filters_4(f, sy, gamma);
+
+ int64x2_t m0 = aom_sdotq_s16(vdupq_n_s64(0), s0, f[0]);
+ int64x2_t m1 = aom_sdotq_s16(vdupq_n_s64(0), s1, f[1]);
+ int64x2_t m2 = aom_sdotq_s16(vdupq_n_s64(0), s2, f[2]);
+ int64x2_t m3 = aom_sdotq_s16(vdupq_n_s64(0), s3, f[3]);
+
+ int64x2_t m01 = vpaddq_s64(m0, m1);
+ int64x2_t m23 = vpaddq_s64(m2, m3);
+ return vcombine_s32(vmovn_s64(m01), vmovn_s64(m23));
+}
+
+static INLINE int32x4x2_t vertical_filter_8x1_f8(const int16x8_t *tmp, int sy,
+ int gamma) {
+ int16x8_t s0 = tmp[0];
+ int16x8_t s1 = tmp[1];
+ int16x8_t s2 = tmp[2];
+ int16x8_t s3 = tmp[3];
+ int16x8_t s4 = tmp[4];
+ int16x8_t s5 = tmp[5];
+ int16x8_t s6 = tmp[6];
+ int16x8_t s7 = tmp[7];
+ transpose_elems_inplace_s16_8x8(&s0, &s1, &s2, &s3, &s4, &s5, &s6, &s7);
+
+ int16x8_t f[8];
+ load_filters_8(f, sy, gamma);
+
+ int64x2_t m0 = aom_sdotq_s16(vdupq_n_s64(0), s0, f[0]);
+ int64x2_t m1 = aom_sdotq_s16(vdupq_n_s64(0), s1, f[1]);
+ int64x2_t m2 = aom_sdotq_s16(vdupq_n_s64(0), s2, f[2]);
+ int64x2_t m3 = aom_sdotq_s16(vdupq_n_s64(0), s3, f[3]);
+ int64x2_t m4 = aom_sdotq_s16(vdupq_n_s64(0), s4, f[4]);
+ int64x2_t m5 = aom_sdotq_s16(vdupq_n_s64(0), s5, f[5]);
+ int64x2_t m6 = aom_sdotq_s16(vdupq_n_s64(0), s6, f[6]);
+ int64x2_t m7 = aom_sdotq_s16(vdupq_n_s64(0), s7, f[7]);
+
+ int64x2_t m01 = vpaddq_s64(m0, m1);
+ int64x2_t m23 = vpaddq_s64(m2, m3);
+ int64x2_t m45 = vpaddq_s64(m4, m5);
+ int64x2_t m67 = vpaddq_s64(m6, m7);
+
+ int32x4x2_t ret;
+ ret.val[0] = vcombine_s32(vmovn_s64(m01), vmovn_s64(m23));
+ ret.val[1] = vcombine_s32(vmovn_s64(m45), vmovn_s64(m67));
+ return ret;
+}
+
+void av1_highbd_warp_affine_sve(const int32_t *mat, const uint16_t *ref,
+ int width, int height, int stride,
+ uint16_t *pred, int p_col, int p_row,
+ int p_width, int p_height, int p_stride,
+ int subsampling_x, int subsampling_y, int bd,
+ ConvolveParams *conv_params, int16_t alpha,
+ int16_t beta, int16_t gamma, int16_t delta) {
+ highbd_warp_affine_common(mat, ref, width, height, stride, pred, p_col, p_row,
+ p_width, p_height, p_stride, subsampling_x,
+ subsampling_y, bd, conv_params, alpha, beta, gamma,
+ delta);
+}
diff --git a/third_party/aom/av1/common/av1_rtcd_defs.pl b/third_party/aom/av1/common/av1_rtcd_defs.pl
index c5fe389ba1..ef999fbba2 100644
--- a/third_party/aom/av1/common/av1_rtcd_defs.pl
+++ b/third_party/aom/av1/common/av1_rtcd_defs.pl
@@ -541,7 +541,7 @@ if ($opts{config} !~ /libs-x86-win32-vs.*/) {
# WARPED_MOTION / GLOBAL_MOTION functions
if (aom_config("CONFIG_AV1_HIGHBITDEPTH") eq "yes") {
add_proto qw/void av1_highbd_warp_affine/, "const int32_t *mat, const uint16_t *ref, int width, int height, int stride, uint16_t *pred, int p_col, int p_row, int p_width, int p_height, int p_stride, int subsampling_x, int subsampling_y, int bd, ConvolveParams *conv_params, int16_t alpha, int16_t beta, int16_t gamma, int16_t delta";
- specialize qw/av1_highbd_warp_affine sse4_1 avx2 neon/;
+ specialize qw/av1_highbd_warp_affine sse4_1 avx2 neon sve/;
}
add_proto qw/void av1_warp_affine/, "const int32_t *mat, const uint8_t *ref, int width, int height, int stride, uint8_t *pred, int p_col, int p_row, int p_width, int p_height, int p_stride, int subsampling_x, int subsampling_y, ConvolveParams *conv_params, int16_t alpha, int16_t beta, int16_t gamma, int16_t delta";
diff --git a/third_party/aom/av1/common/reconintra.c b/third_party/aom/av1/common/reconintra.c
index 20a1e12476..f68af18cb1 100644
--- a/third_party/aom/av1/common/reconintra.c
+++ b/third_party/aom/av1/common/reconintra.c
@@ -1368,7 +1368,7 @@ void av1_highbd_upsample_intra_edge_c(uint16_t *p, int sz, int bd) {
}
}
-static void highbd_build_intra_predictors(
+static void highbd_build_directional_and_filter_intra_predictors(
const uint8_t *ref8, int ref_stride, uint8_t *dst8, int dst_stride,
PREDICTION_MODE mode, int p_angle, FILTER_INTRA_MODE filter_intra_mode,
TX_SIZE tx_size, int disable_edge_filter, int n_top_px, int n_topright_px,
@@ -1376,7 +1376,7 @@ static void highbd_build_intra_predictors(
int bit_depth) {
int i;
uint16_t *dst = CONVERT_TO_SHORTPTR(dst8);
- uint16_t *ref = CONVERT_TO_SHORTPTR(ref8);
+ const uint16_t *const ref = CONVERT_TO_SHORTPTR(ref8);
DECLARE_ALIGNED(16, uint16_t, left_data[NUM_INTRA_NEIGHBOUR_PIXELS]);
DECLARE_ALIGNED(16, uint16_t, above_data[NUM_INTRA_NEIGHBOUR_PIXELS]);
uint16_t *const above_row = above_data + 16;
@@ -1390,7 +1390,8 @@ static void highbd_build_intra_predictors(
const uint16_t *left_ref = ref - 1;
const int is_dr_mode = av1_is_directional_mode(mode);
const int use_filter_intra = filter_intra_mode != FILTER_INTRA_MODES;
- int base = 128 << (bit_depth - 8);
+ assert(use_filter_intra || is_dr_mode);
+ const int base = 128 << (bit_depth - 8);
// The left_data, above_data buffers must be zeroed to fix some intermittent
// valgrind errors. Uninitialized reads in intra pred modules (e.g. width = 4
// path in av1_highbd_dr_prediction_z2_avx2()) from left_data, above_data are
@@ -1492,49 +1493,124 @@ static void highbd_build_intra_predictors(
return;
}
- if (is_dr_mode) {
- int upsample_above = 0;
- int upsample_left = 0;
- if (!disable_edge_filter) {
- const int need_right = p_angle < 90;
- const int need_bottom = p_angle > 180;
- if (p_angle != 90 && p_angle != 180) {
- const int ab_le = need_above_left ? 1 : 0;
- if (need_above && need_left && (txwpx + txhpx >= 24)) {
- highbd_filter_intra_edge_corner(above_row, left_col);
- }
- if (need_above && n_top_px > 0) {
- const int strength = intra_edge_filter_strength(
- txwpx, txhpx, p_angle - 90, intra_edge_filter_type);
- const int n_px = n_top_px + ab_le + (need_right ? txhpx : 0);
- av1_highbd_filter_intra_edge(above_row - ab_le, n_px, strength);
- }
- if (need_left && n_left_px > 0) {
- const int strength = intra_edge_filter_strength(
- txhpx, txwpx, p_angle - 180, intra_edge_filter_type);
- const int n_px = n_left_px + ab_le + (need_bottom ? txwpx : 0);
- av1_highbd_filter_intra_edge(left_col - ab_le, n_px, strength);
- }
+ assert(is_dr_mode);
+ int upsample_above = 0;
+ int upsample_left = 0;
+ if (!disable_edge_filter) {
+ const int need_right = p_angle < 90;
+ const int need_bottom = p_angle > 180;
+ if (p_angle != 90 && p_angle != 180) {
+ const int ab_le = need_above_left ? 1 : 0;
+ if (need_above && need_left && (txwpx + txhpx >= 24)) {
+ highbd_filter_intra_edge_corner(above_row, left_col);
}
- upsample_above = av1_use_intra_edge_upsample(txwpx, txhpx, p_angle - 90,
- intra_edge_filter_type);
- if (need_above && upsample_above) {
- const int n_px = txwpx + (need_right ? txhpx : 0);
- av1_highbd_upsample_intra_edge(above_row, n_px, bit_depth);
+ if (need_above && n_top_px > 0) {
+ const int strength = intra_edge_filter_strength(
+ txwpx, txhpx, p_angle - 90, intra_edge_filter_type);
+ const int n_px = n_top_px + ab_le + (need_right ? txhpx : 0);
+ av1_highbd_filter_intra_edge(above_row - ab_le, n_px, strength);
}
- upsample_left = av1_use_intra_edge_upsample(txhpx, txwpx, p_angle - 180,
- intra_edge_filter_type);
- if (need_left && upsample_left) {
- const int n_px = txhpx + (need_bottom ? txwpx : 0);
- av1_highbd_upsample_intra_edge(left_col, n_px, bit_depth);
+ if (need_left && n_left_px > 0) {
+ const int strength = intra_edge_filter_strength(
+ txhpx, txwpx, p_angle - 180, intra_edge_filter_type);
+ const int n_px = n_left_px + ab_le + (need_bottom ? txwpx : 0);
+ av1_highbd_filter_intra_edge(left_col - ab_le, n_px, strength);
}
}
- highbd_dr_predictor(dst, dst_stride, tx_size, above_row, left_col,
- upsample_above, upsample_left, p_angle, bit_depth);
+ upsample_above = av1_use_intra_edge_upsample(txwpx, txhpx, p_angle - 90,
+ intra_edge_filter_type);
+ if (need_above && upsample_above) {
+ const int n_px = txwpx + (need_right ? txhpx : 0);
+ av1_highbd_upsample_intra_edge(above_row, n_px, bit_depth);
+ }
+ upsample_left = av1_use_intra_edge_upsample(txhpx, txwpx, p_angle - 180,
+ intra_edge_filter_type);
+ if (need_left && upsample_left) {
+ const int n_px = txhpx + (need_bottom ? txwpx : 0);
+ av1_highbd_upsample_intra_edge(left_col, n_px, bit_depth);
+ }
+ }
+ highbd_dr_predictor(dst, dst_stride, tx_size, above_row, left_col,
+ upsample_above, upsample_left, p_angle, bit_depth);
+}
+
+// For HBD encode/decode, this function generates the pred data of a given
+// block for non-directional intra prediction modes (i.e., DC, SMOOTH, SMOOTH_H,
+// SMOOTH_V and PAETH).
+static void highbd_build_non_directional_intra_predictors(
+ const uint8_t *ref8, int ref_stride, uint8_t *dst8, int dst_stride,
+ PREDICTION_MODE mode, TX_SIZE tx_size, int n_top_px, int n_left_px,
+ int bit_depth) {
+ int i = 0;
+ uint16_t *dst = CONVERT_TO_SHORTPTR(dst8);
+ const uint16_t *const ref = CONVERT_TO_SHORTPTR(ref8);
+ const int txwpx = tx_size_wide[tx_size];
+ const int txhpx = tx_size_high[tx_size];
+ int need_left = extend_modes[mode] & NEED_LEFT;
+ int need_above = extend_modes[mode] & NEED_ABOVE;
+ int need_above_left = extend_modes[mode] & NEED_ABOVELEFT;
+ const uint16_t *above_ref = ref - ref_stride;
+ const uint16_t *left_ref = ref - 1;
+ const int base = 128 << (bit_depth - 8);
+
+ assert(n_top_px >= 0);
+ assert(n_left_px >= 0);
+ assert(mode == DC_PRED || mode == SMOOTH_PRED || mode == SMOOTH_V_PRED ||
+ mode == SMOOTH_H_PRED || mode == PAETH_PRED);
+
+ if ((!need_above && n_left_px == 0) || (!need_left && n_top_px == 0)) {
+ int val = 0;
+ if (need_left) {
+ val = (n_top_px > 0) ? above_ref[0] : base + 1;
+ } else {
+ val = (n_left_px > 0) ? left_ref[0] : base - 1;
+ }
+ for (i = 0; i < txhpx; ++i) {
+ aom_memset16(dst, val, txwpx);
+ dst += dst_stride;
+ }
return;
}
- // predict
+ DECLARE_ALIGNED(16, uint16_t, left_data[NUM_INTRA_NEIGHBOUR_PIXELS]);
+ DECLARE_ALIGNED(16, uint16_t, above_data[NUM_INTRA_NEIGHBOUR_PIXELS]);
+ uint16_t *const above_row = above_data + 16;
+ uint16_t *const left_col = left_data + 16;
+
+ if (need_left) {
+ aom_memset16(left_data, base + 1, NUM_INTRA_NEIGHBOUR_PIXELS);
+ if (n_left_px > 0) {
+ for (i = 0; i < n_left_px; i++) left_col[i] = left_ref[i * ref_stride];
+ if (i < txhpx) aom_memset16(&left_col[i], left_col[i - 1], txhpx - i);
+ } else if (n_top_px > 0) {
+ aom_memset16(left_col, above_ref[0], txhpx);
+ }
+ }
+
+ if (need_above) {
+ aom_memset16(above_data, base - 1, NUM_INTRA_NEIGHBOUR_PIXELS);
+ if (n_top_px > 0) {
+ memcpy(above_row, above_ref, n_top_px * sizeof(above_ref[0]));
+ i = n_top_px;
+ if (i < txwpx) aom_memset16(&above_row[i], above_row[i - 1], (txwpx - i));
+ } else if (n_left_px > 0) {
+ aom_memset16(above_row, left_ref[0], txwpx);
+ }
+ }
+
+ if (need_above_left) {
+ if (n_top_px > 0 && n_left_px > 0) {
+ above_row[-1] = above_ref[-1];
+ } else if (n_top_px > 0) {
+ above_row[-1] = above_ref[0];
+ } else if (n_left_px > 0) {
+ above_row[-1] = left_ref[0];
+ } else {
+ above_row[-1] = base;
+ }
+ left_col[-1] = above_row[-1];
+ }
+
if (mode == DC_PRED) {
dc_pred_high[n_left_px > 0][n_top_px > 0][tx_size](
dst, dst_stride, above_row, left_col, bit_depth);
@@ -1660,12 +1736,19 @@ void av1_predict_intra_block(const MACROBLOCKD *xd, BLOCK_SIZE sb_size,
// separate function build_non_directional_intra_predictors() is introduced
// for these modes to avoid redundant computations while generating pred data.
- // TODO(aomedia:3532): Enable this refactoring for high bd path as well.
- if (!is_hbd && !use_filter_intra && !is_dr_mode) {
- build_non_directional_intra_predictors(
- ref, ref_stride, dst, dst_stride, mode, tx_size,
- have_top ? AOMMIN(txwpx, xr + txwpx) : 0,
- have_left ? AOMMIN(txhpx, yd + txhpx) : 0);
+ const int n_top_px = have_top ? AOMMIN(txwpx, xr + txwpx) : 0;
+ const int n_left_px = have_left ? AOMMIN(txhpx, yd + txhpx) : 0;
+ if (!use_filter_intra && !is_dr_mode) {
+#if CONFIG_AV1_HIGHBITDEPTH
+ if (is_hbd) {
+ highbd_build_non_directional_intra_predictors(
+ ref, ref_stride, dst, dst_stride, mode, tx_size, n_top_px, n_left_px,
+ xd->bd);
+ return;
+ }
+#endif // CONFIG_AV1_HIGHBITDEPTH
+ build_non_directional_intra_predictors(ref, ref_stride, dst, dst_stride,
+ mode, tx_size, n_top_px, n_left_px);
return;
}
@@ -1717,25 +1800,23 @@ void av1_predict_intra_block(const MACROBLOCKD *xd, BLOCK_SIZE sb_size,
const int disable_edge_filter = !enable_intra_edge_filter;
const int intra_edge_filter_type = get_intra_edge_filter_type(xd, plane);
+ const int n_topright_px =
+ have_top_right > 0 ? AOMMIN(txwpx, xr) : have_top_right;
+ const int n_bottomleft_px =
+ have_bottom_left > 0 ? AOMMIN(txhpx, yd) : have_bottom_left;
#if CONFIG_AV1_HIGHBITDEPTH
if (is_hbd) {
- highbd_build_intra_predictors(
+ highbd_build_directional_and_filter_intra_predictors(
ref, ref_stride, dst, dst_stride, mode, p_angle, filter_intra_mode,
- tx_size, disable_edge_filter, have_top ? AOMMIN(txwpx, xr + txwpx) : 0,
- have_top_right > 0 ? AOMMIN(txwpx, xr) : have_top_right,
- have_left ? AOMMIN(txhpx, yd + txhpx) : 0,
- have_bottom_left > 0 ? AOMMIN(txhpx, yd) : have_bottom_left,
- intra_edge_filter_type, xd->bd);
+ tx_size, disable_edge_filter, n_top_px, n_topright_px, n_left_px,
+ n_bottomleft_px, intra_edge_filter_type, xd->bd);
return;
}
#endif
build_directional_and_filter_intra_predictors(
ref, ref_stride, dst, dst_stride, mode, p_angle, filter_intra_mode,
- tx_size, disable_edge_filter, have_top ? AOMMIN(txwpx, xr + txwpx) : 0,
- have_top_right > 0 ? AOMMIN(txwpx, xr) : have_top_right,
- have_left ? AOMMIN(txhpx, yd + txhpx) : 0,
- have_bottom_left > 0 ? AOMMIN(txhpx, yd) : have_bottom_left,
- intra_edge_filter_type);
+ tx_size, disable_edge_filter, n_top_px, n_topright_px, n_left_px,
+ n_bottomleft_px, intra_edge_filter_type);
}
void av1_predict_intra_block_facade(const AV1_COMMON *cm, MACROBLOCKD *xd,
diff --git a/third_party/aom/av1/encoder/encoder.c b/third_party/aom/av1/encoder/encoder.c
index 4732ad435b..fe053af5cc 100644
--- a/third_party/aom/av1/encoder/encoder.c
+++ b/third_party/aom/av1/encoder/encoder.c
@@ -2594,15 +2594,19 @@ static int encode_without_recode(AV1_COMP *cpi) {
if (cpi->ref_frame_flags & av1_ref_frame_flag_list[GOLDEN_FRAME]) {
const YV12_BUFFER_CONFIG *const ref =
get_ref_frame_yv12_buf(cm, GOLDEN_FRAME);
- if (ref->y_crop_width != cm->width || ref->y_crop_height != cm->height)
+ if (ref == NULL || ref->y_crop_width != cm->width ||
+ ref->y_crop_height != cm->height) {
cpi->ref_frame_flags ^= AOM_GOLD_FLAG;
+ }
}
}
if (cpi->ref_frame_flags & av1_ref_frame_flag_list[ALTREF_FRAME]) {
const YV12_BUFFER_CONFIG *const ref =
get_ref_frame_yv12_buf(cm, ALTREF_FRAME);
- if (ref->y_crop_width != cm->width || ref->y_crop_height != cm->height)
+ if (ref == NULL || ref->y_crop_width != cm->width ||
+ ref->y_crop_height != cm->height) {
cpi->ref_frame_flags ^= AOM_ALT_FLAG;
+ }
}
}
@@ -2700,10 +2704,13 @@ static int encode_without_recode(AV1_COMP *cpi) {
update_motion_stat(cpi);
// Adjust the refresh of the golden (longer-term) reference based on QP
- // selected for this frame. This is for CBR with 1 layer/non-svc RTC mode.
+ // selected for this frame. This is for CBR real-time mode, and only
+ // for single layer without usage of the set_ref_frame_config (so
+ // reference structure for 1 layer is set internally).
if (!frame_is_intra_only(cm) && cpi->oxcf.rc_cfg.mode == AOM_CBR &&
cpi->oxcf.mode == REALTIME && svc->number_spatial_layers == 1 &&
svc->number_temporal_layers == 1 && !cpi->rc.rtc_external_ratectrl &&
+ !cpi->ppi->rtc_ref.set_ref_frame_config &&
sf->rt_sf.gf_refresh_based_on_qp)
av1_adjust_gf_refresh_qp_one_pass_rt(cpi);
diff --git a/third_party/aom/av1/encoder/encoder.h b/third_party/aom/av1/encoder/encoder.h
index 5f6f67eda8..e87ab9be1f 100644
--- a/third_party/aom/av1/encoder/encoder.h
+++ b/third_party/aom/av1/encoder/encoder.h
@@ -3156,14 +3156,14 @@ typedef struct AV1_COMP {
FRAME_INDEX_SET frame_index_set;
/*!
- * Store the cm->width in the last call of alloc_compressor_data(). Help
+ * Stores the cm->width in the last call of alloc_compressor_data(). Helps
* determine whether compressor data should be reallocated when cm->width
* changes.
*/
int data_alloc_width;
/*!
- * Store the cm->height in the last call of alloc_compressor_data(). Help
+ * Stores the cm->height in the last call of alloc_compressor_data(). Helps
* determine whether compressor data should be reallocated when cm->height
* changes.
*/
diff --git a/third_party/aom/av1/encoder/mcomp.c b/third_party/aom/av1/encoder/mcomp.c
index 4e53447379..f3a9828cb3 100644
--- a/third_party/aom/av1/encoder/mcomp.c
+++ b/third_party/aom/av1/encoder/mcomp.c
@@ -1807,7 +1807,6 @@ int av1_full_pixel_search(const FULLPEL_MV start_mv,
}
assert(ms_params->ms_buffers.ref->stride == ms_params->search_sites->stride);
- assert(ms_params->ms_buffers.ref->width == ms_params->ms_buffers.src->width);
switch (search_method) {
case FAST_BIGDIA:
diff --git a/third_party/aom/av1/encoder/speed_features.c b/third_party/aom/av1/encoder/speed_features.c
index a6c0971096..63d69cadc5 100644
--- a/third_party/aom/av1/encoder/speed_features.c
+++ b/third_party/aom/av1/encoder/speed_features.c
@@ -1624,6 +1624,14 @@ static void set_rt_speed_feature_framesize_dependent(const AV1_COMP *const cpi,
sf->rt_sf.use_rtc_tf = 0;
sf->rt_sf.nonrd_prune_ref_frame_search = 1;
}
+ // rtc_tf feature allocates new source because of possible
+ // temporal filtering which may change the input source during encoding:
+ // this causes an issue on resized frames when psnr is calculated,
+ // so disable it here for frames that are resized (encoding width/height
+ // different from configured width/height).
+ if (is_psnr_calc_enabled(cpi) && (cpi->oxcf.frm_dim_cfg.width != cm->width ||
+ cpi->oxcf.frm_dim_cfg.height != cm->height))
+ sf->rt_sf.use_rtc_tf = 0;
}
// TODO(kyslov): now this is very similar to
diff --git a/third_party/aom/common/tools_common.c b/third_party/aom/common/tools_common.c
index 4d77a1b427..db02ca6299 100644
--- a/third_party/aom/common/tools_common.c
+++ b/third_party/aom/common/tools_common.c
@@ -97,7 +97,7 @@ int read_yuv_frame(struct AvxInputContext *input_ctx, aom_image_t *yuv_frame) {
int w = aom_img_plane_width(yuv_frame, plane);
const int h = aom_img_plane_height(yuv_frame, plane);
int r;
- // Assuming that for nv12 we read all chroma data at one time
+ // Assuming that for nv12 we read all chroma data at once
if (yuv_frame->fmt == AOM_IMG_FMT_NV12 && plane > 1) break;
if (yuv_frame->fmt == AOM_IMG_FMT_NV12 && plane == 1) w *= 2;
/* Determine the correct plane based on the image format. The for-loop
@@ -245,17 +245,21 @@ uint32_t get_fourcc_by_aom_decoder(aom_codec_iface_t *iface) {
void aom_img_write(const aom_image_t *img, FILE *file) {
int plane;
+ const int bytespp = (img->fmt & AOM_IMG_FMT_HIGHBITDEPTH) ? 2 : 1;
for (plane = 0; plane < 3; ++plane) {
const unsigned char *buf = img->planes[plane];
const int stride = img->stride[plane];
- const int w = aom_img_plane_width(img, plane) *
- ((img->fmt & AOM_IMG_FMT_HIGHBITDEPTH) ? 2 : 1);
+ int w = aom_img_plane_width(img, plane);
const int h = aom_img_plane_height(img, plane);
int y;
+ // Assuming that for nv12 we write all chroma data at once
+ if (img->fmt == AOM_IMG_FMT_NV12 && plane > 1) break;
+ if (img->fmt == AOM_IMG_FMT_NV12 && plane == 1) w *= 2;
+
for (y = 0; y < h; ++y) {
- fwrite(buf, 1, w, file);
+ fwrite(buf, bytespp, w, file);
buf += stride;
}
}
@@ -268,12 +272,16 @@ bool aom_img_read(aom_image_t *img, FILE *file) {
for (plane = 0; plane < 3; ++plane) {
unsigned char *buf = img->planes[plane];
const int stride = img->stride[plane];
- const int w = aom_img_plane_width(img, plane) * bytespp;
+ int w = aom_img_plane_width(img, plane);
const int h = aom_img_plane_height(img, plane);
int y;
+ // Assuming that for nv12 we read all chroma data at once
+ if (img->fmt == AOM_IMG_FMT_NV12 && plane > 1) break;
+ if (img->fmt == AOM_IMG_FMT_NV12 && plane == 1) w *= 2;
+
for (y = 0; y < h; ++y) {
- if (fread(buf, 1, w, file) != (size_t)w) return false;
+ if (fread(buf, bytespp, w, file) != (size_t)w) return false;
buf += stride;
}
}
diff --git a/third_party/aom/test/av1_c_vs_simd_encode.sh b/third_party/aom/test/av1_c_vs_simd_encode.sh
index 296204d118..897ac081c1 100755
--- a/third_party/aom/test/av1_c_vs_simd_encode.sh
+++ b/third_party/aom/test/av1_c_vs_simd_encode.sh
@@ -104,16 +104,16 @@ av1_c_vs_simd_enc_verify_environment () {
# }
# Echo AOM_SIMD_CAPS_MASK for different instruction set architecture.
-avx512f() {
+avx2() {
echo "0x1FF"
}
-avx2() {
- echo "0x0FF"
+avx() {
+ echo "0x17F"
}
-avx() {
- echo "0x07F"
+sse4_2() {
+ echo "0x13F"
}
sse4_1() {
@@ -443,21 +443,21 @@ av1_test_generic() {
done
}
-# This function encodes AV1 bitstream by enabling SSE2, SSE3, SSSE3, SSE4_1, AVX, AVX2 as there are
-# no functions with MMX, SSE and AVX512 specialization.
+# This function encodes AV1 bitstream by enabling SSE2, SSE3, SSSE3, SSE4_1, SSE4_2, AVX, AVX2 as
+# there are no functions with MMX, SSE and AVX512 specialization.
# The value of environment variable 'AOM_SIMD_CAPS_MASK' controls enabling of different instruction
# set extension optimizations. The value of the flag 'AOM_SIMD_CAPS_MASK' and the corresponding
# instruction set extension optimization enabled are as follows:
-# AVX512 AVX2 AVX SSE4_1 SSSE3 SSE3 SSE2 SSE MMX
-# 1 1 1 1 1 1 1 1 1 -> 0x1FF -> Enable AVX512 and lower variants
-# 0 1 1 1 1 1 1 1 1 -> 0x0FF -> Enable AVX2 and lower variants
-# 0 0 1 1 1 1 1 1 1 -> 0x07F -> Enable AVX and lower variants
-# 0 0 0 1 1 1 1 1 1 -> 0x03F -> Enable SSE4_1 and lower variants
-# 0 0 0 0 1 1 1 1 1 -> 0x01F -> Enable SSSE3 and lower variants
-# 0 0 0 0 0 1 1 1 1 -> 0x00F -> Enable SSE3 and lower variants
-# 0 0 0 0 0 0 1 1 1 -> 0x007 -> Enable SSE2 and lower variants
-# 0 0 0 0 0 0 0 1 1 -> 0x003 -> Enable SSE and lower variants
-# 0 0 0 0 0 0 0 0 1 -> 0x001 -> Enable MMX
+# SSE4_2 AVX2 AVX SSE4_1 SSSE3 SSE3 SSE2 SSE MMX
+# 1 1 1 1 1 1 1 1 1 -> 0x1FF -> Enable AVX2 and lower variants
+# 1 0 1 1 1 1 1 1 1 -> 0x17F -> Enable AVX and lower variants
+# 1 0 0 1 1 1 1 1 1 -> 0x13F -> Enable SSE4_2 and lower variants
+# 0 0 0 1 1 1 1 1 1 -> 0x03F -> Enable SSE4_1 and lower variants
+# 0 0 0 0 1 1 1 1 1 -> 0x01F -> Enable SSSE3 and lower variants
+# 0 0 0 0 0 1 1 1 1 -> 0x00F -> Enable SSE3 and lower variants
+# 0 0 0 0 0 0 1 1 1 -> 0x007 -> Enable SSE2 and lower variants
+# 0 0 0 0 0 0 0 1 1 -> 0x003 -> Enable SSE and lower variants
+# 0 0 0 0 0 0 0 0 1 -> 0x001 -> Enable MMX
## NOTE: In x86_64 platform, it is not possible to enable sse/mmx/c using "AOM_SIMD_CAPS_MASK" as
# all x86_64 platforms implement sse2.
av1_test_x86() {
@@ -478,8 +478,8 @@ av1_test_x86() {
local cmake_command="cmake $LIBAOM_SOURCE_DIR"
fi
- # Available x86 isa variants: "avx2 avx sse4_1 ssse3 sse3 sse2"
- local x86_isa_variants="avx2 sse4_1 sse2"
+ # Available x86 isa variants: "avx2 avx sse4_2 sse4_1 ssse3 sse3 sse2"
+ local x86_isa_variants="avx2 sse4_2 sse2"
echo "Build for x86: ${target}"
if ! av1_enc_build "${target}" "${cmake_command}"; then
diff --git a/third_party/aom/test/dr_prediction_test.cc b/third_party/aom/test/dr_prediction_test.cc
index 3865810e9b..c23b08e481 100644
--- a/third_party/aom/test/dr_prediction_test.cc
+++ b/third_party/aom/test/dr_prediction_test.cc
@@ -10,6 +10,7 @@
*/
#include <tuple>
+#include <vector>
#include "third_party/googletest/src/googletest/include/gtest/gtest.h"
@@ -18,6 +19,7 @@
#include "aom_mem/aom_mem.h"
#include "aom_ports/aom_timer.h"
+#include "aom_ports/sanitizer.h"
#include "av1/common/blockd.h"
#include "av1/common/pred_common.h"
#include "av1/common/reconintra.h"
@@ -149,8 +151,6 @@ class DrPredTest : public ::testing::TestWithParam<DrPredFunc<FuncType> > {
protected:
static const int kMaxNumTests = 10000;
static const int kIterations = 10;
- static const int kDstStride = 64;
- static const int kDstSize = kDstStride * kDstStride;
static const int kOffset = 16;
static const int kBufSize = ((2 * MAX_TX_SIZE) << 1) + 16;
@@ -161,9 +161,6 @@ class DrPredTest : public ::testing::TestWithParam<DrPredFunc<FuncType> > {
start_angle_ = params_.start_angle;
stop_angle_ = start_angle_ + 90;
- dst_ref_ = &dst_ref_data_[0];
- dst_tst_ = &dst_tst_data_[0];
- dst_stride_ = kDstStride;
above_ = &above_data_[kOffset];
left_ = &left_data_[kOffset];
@@ -171,16 +168,12 @@ class DrPredTest : public ::testing::TestWithParam<DrPredFunc<FuncType> > {
above_data_[i] = rng_.Rand8();
left_data_[i] = rng_.Rand8();
}
-
- for (int i = 0; i < kDstSize; ++i) {
- dst_ref_[i] = 0;
- dst_tst_[i] = 0;
- }
}
~DrPredTest() override = default;
- void Predict(bool speedtest, int tx) {
+ void Predict(bool speedtest, int tx, Pixel *dst_ref, Pixel *dst_tst,
+ int dst_stride) {
const int kNumTests = speedtest ? kMaxNumTests : 1;
aom_usec_timer timer;
int tst_time = 0;
@@ -189,7 +182,7 @@ class DrPredTest : public ::testing::TestWithParam<DrPredFunc<FuncType> > {
aom_usec_timer_start(&timer);
for (int k = 0; k < kNumTests; ++k) {
- params_.ref_fn(dst_ref_, dst_stride_, bw_, bh_, above_, left_,
+ params_.ref_fn(dst_ref, dst_stride, bw_, bh_, above_, left_,
upsample_above_, upsample_left_, dx_, dy_, bd_);
}
aom_usec_timer_mark(&timer);
@@ -198,15 +191,17 @@ class DrPredTest : public ::testing::TestWithParam<DrPredFunc<FuncType> > {
if (params_.tst_fn) {
aom_usec_timer_start(&timer);
for (int k = 0; k < kNumTests; ++k) {
- API_REGISTER_STATE_CHECK(params_.tst_fn(dst_tst_, dst_stride_, bw_, bh_,
+ API_REGISTER_STATE_CHECK(params_.tst_fn(dst_tst, dst_stride, bw_, bh_,
above_, left_, upsample_above_,
upsample_left_, dx_, dy_, bd_));
}
aom_usec_timer_mark(&timer);
tst_time = static_cast<int>(aom_usec_timer_elapsed(&timer));
} else {
- for (int i = 0; i < kDstSize; ++i) {
- dst_ref_[i] = dst_tst_[i];
+ for (int r = 0; r < bh_; ++r) {
+ for (int c = 0; c < bw_; ++c) {
+ dst_tst[r * dst_stride + c] = dst_ref[r * dst_stride + c];
+ }
}
}
@@ -222,18 +217,6 @@ class DrPredTest : public ::testing::TestWithParam<DrPredFunc<FuncType> > {
}
}
for (int tx = 0; tx < TX_SIZES_ALL; ++tx) {
- if (params_.tst_fn == nullptr) {
- for (int i = 0; i < kDstSize; ++i) {
- dst_tst_[i] = (1 << bd_) - 1;
- dst_ref_[i] = (1 << bd_) - 1;
- }
- } else {
- for (int i = 0; i < kDstSize; ++i) {
- dst_ref_[i] = 0;
- dst_tst_[i] = 0;
- }
- }
-
bw_ = tx_size_wide[kTxSize[tx]];
bh_ = tx_size_high[kTxSize[tx]];
@@ -246,12 +229,31 @@ class DrPredTest : public ::testing::TestWithParam<DrPredFunc<FuncType> > {
upsample_above_ = upsample_left_ = 0;
}
- Predict(speedtest, tx);
+ // Add additional padding to allow detection of over reads/writes when
+ // the transform width is equal to MAX_TX_SIZE.
+ const int dst_stride = MAX_TX_SIZE + 16;
+ std::vector<Pixel> dst_ref(dst_stride * bh_);
+ std::vector<Pixel> dst_tst(dst_stride * bh_);
+
+ for (int r = 0; r < bh_; ++r) {
+ ASAN_POISON_MEMORY_REGION(&dst_ref[r * dst_stride + bw_],
+ (dst_stride - bw_) * sizeof(Pixel));
+ ASAN_POISON_MEMORY_REGION(&dst_tst[r * dst_stride + bw_],
+ (dst_stride - bw_) * sizeof(Pixel));
+ }
+
+ Predict(speedtest, tx, dst_ref.data(), dst_tst.data(), dst_stride);
+
+ for (int r = 0; r < bh_; ++r) {
+ ASAN_UNPOISON_MEMORY_REGION(&dst_ref[r * dst_stride + bw_],
+ (dst_stride - bw_) * sizeof(Pixel));
+ ASAN_UNPOISON_MEMORY_REGION(&dst_tst[r * dst_stride + bw_],
+ (dst_stride - bw_) * sizeof(Pixel));
+ }
for (int r = 0; r < bh_; ++r) {
for (int c = 0; c < bw_; ++c) {
- ASSERT_EQ(dst_ref_[r * dst_stride_ + c],
- dst_tst_[r * dst_stride_ + c])
+ ASSERT_EQ(dst_ref[r * dst_stride + c], dst_tst[r * dst_stride + c])
<< bw_ << "x" << bh_ << " r: " << r << " c: " << c
<< " dx: " << dx_ << " dy: " << dy_
<< " upsample_above: " << upsample_above_
@@ -292,18 +294,12 @@ class DrPredTest : public ::testing::TestWithParam<DrPredFunc<FuncType> > {
}
}
- Pixel dst_ref_data_[kDstSize];
- Pixel dst_tst_data_[kDstSize];
-
Pixel left_data_[kBufSize];
Pixel dummy_data_[kBufSize];
Pixel above_data_[kBufSize];
- Pixel *dst_ref_;
- Pixel *dst_tst_;
Pixel *above_;
Pixel *left_;
- int dst_stride_;
int enable_upsample_;
int upsample_above_;
diff --git a/third_party/aom/test/encode_api_test.cc b/third_party/aom/test/encode_api_test.cc
index aa4084f9e4..605743f9be 100644
--- a/third_party/aom/test/encode_api_test.cc
+++ b/third_party/aom/test/encode_api_test.cc
@@ -654,6 +654,52 @@ TEST(EncodeAPI, AllIntraMode) {
cfg.kf_max_dist = 1;
EXPECT_EQ(AOM_CODEC_INVALID_PARAM, aom_codec_enc_init(&enc, iface, &cfg, 0));
}
-#endif
+
+TEST(EncodeAPI, AllIntraAndUsePsnr) {
+ aom_codec_iface_t *iface = aom_codec_av1_cx();
+ aom_codec_enc_cfg_t cfg;
+ ASSERT_EQ(aom_codec_enc_config_default(iface, &cfg, AOM_USAGE_ALL_INTRA),
+ AOM_CODEC_OK);
+
+ aom_codec_ctx_t enc;
+ ASSERT_EQ(aom_codec_enc_init(&enc, iface, &cfg, AOM_CODEC_USE_PSNR),
+ AOM_CODEC_OK);
+
+ aom_image_t *image = CreateGrayImage(AOM_IMG_FMT_I420, cfg.g_w, cfg.g_h);
+ ASSERT_NE(image, nullptr);
+
+ ASSERT_EQ(aom_codec_encode(&enc, image, 0, 1, 0), AOM_CODEC_OK);
+ const aom_codec_cx_pkt_t *pkt;
+ aom_codec_iter_t iter = nullptr;
+ while ((pkt = aom_codec_get_cx_data(&enc, &iter)) != nullptr) {
+ if (pkt->kind != AOM_CODEC_CX_FRAME_PKT) {
+ ASSERT_EQ(pkt->kind, AOM_CODEC_PSNR_PKT);
+ }
+ }
+
+ aom_img_free(image);
+ ASSERT_EQ(aom_codec_destroy(&enc), AOM_CODEC_OK);
+}
+
+// A test that reproduces bug aomedia:3534.
+TEST(EncodeAPI, AllIntraAndNoRefLast) {
+ aom_codec_iface_t *iface = aom_codec_av1_cx();
+ aom_codec_enc_cfg_t cfg;
+ ASSERT_EQ(aom_codec_enc_config_default(iface, &cfg, AOM_USAGE_ALL_INTRA),
+ AOM_CODEC_OK);
+
+ aom_codec_ctx_t enc;
+ ASSERT_EQ(aom_codec_enc_init(&enc, iface, &cfg, 0), AOM_CODEC_OK);
+
+ aom_image_t *image = CreateGrayImage(AOM_IMG_FMT_I420, cfg.g_w, cfg.g_h);
+ ASSERT_NE(image, nullptr);
+
+ ASSERT_EQ(aom_codec_encode(&enc, image, 0, 1, AOM_EFLAG_NO_REF_LAST),
+ AOM_CODEC_OK);
+
+ aom_img_free(image);
+ ASSERT_EQ(aom_codec_destroy(&enc), AOM_CODEC_OK);
+}
+#endif // !CONFIG_REALTIME_ONLY
} // namespace
diff --git a/third_party/aom/test/resize_test.cc b/third_party/aom/test/resize_test.cc
index 7bad45300a..755d4e3d02 100644
--- a/third_party/aom/test/resize_test.cc
+++ b/third_party/aom/test/resize_test.cc
@@ -11,15 +11,17 @@
#include <climits>
#include <vector>
+
+#include "aom/aomcx.h"
#include "aom_dsp/aom_dsp_common.h"
-#include "common/tools_common.h"
#include "av1/encoder/encoder.h"
+#include "common/tools_common.h"
#include "third_party/googletest/src/googletest/include/gtest/gtest.h"
#include "test/codec_factory.h"
#include "test/encode_test_driver.h"
#include "test/i420_video_source.h"
-#include "test/video_source.h"
#include "test/util.h"
+#include "test/video_source.h"
#include "test/y4m_video_source.h"
// Enable(1) or Disable(0) writing of the compressed bitstream.
@@ -403,7 +405,7 @@ class ResizeRealtimeTest
ResizeRealtimeTest()
: EncoderTest(GET_PARAM(0)), num_threads_(GET_PARAM(3)),
set_scale_mode_(false), set_scale_mode2_(false),
- set_scale_mode3_(false) {}
+ set_scale_mode3_(false), is_screen_(false) {}
~ResizeRealtimeTest() override = default;
void PreEncodeFrameHook(libaom_test::VideoSource *video,
@@ -415,6 +417,8 @@ class ResizeRealtimeTest
encoder->Control(AV1E_SET_ENABLE_OBMC, 0);
encoder->Control(AOME_SET_CPUUSED, set_cpu_used_);
encoder->Control(AV1E_SET_FRAME_PARALLEL_DECODING, 1);
+ if (is_screen_)
+ encoder->Control(AV1E_SET_TUNE_CONTENT, AOM_CONTENT_SCREEN);
}
if (set_scale_mode_) {
struct aom_scaling_mode mode;
@@ -508,6 +512,7 @@ class ResizeRealtimeTest
bool set_scale_mode_;
bool set_scale_mode2_;
bool set_scale_mode3_;
+ bool is_screen_;
};
// Check the AOME_SET_SCALEMODE control by downsizing to
@@ -740,6 +745,7 @@ TEST_P(ResizeRealtimeTest, TestInternalResizeDown) {
TEST_P(ResizeRealtimeTest, TestInternalResizeDownUpChangeBitRate) {
::libaom_test::I420VideoSource video("niklas_640_480_30.yuv", 640, 480, 30, 1,
0, 400);
+ init_flags_ = AOM_CODEC_USE_PSNR;
cfg_.g_w = 640;
cfg_.g_h = 480;
change_bitrate_ = true;
@@ -795,6 +801,63 @@ TEST_P(ResizeRealtimeTest, TestInternalResizeDownUpChangeBitRate) {
#endif
}
+// Verify the dynamic resizer behavior for real time, 1 pass CBR mode for
+// screen content mode. Start at low target bitrate, raise the bitrate in the
+// middle of the clip (at frame# = frame_change_bitrate_), scaling-up should
+// occur after bitrate is increased.
+TEST_P(ResizeRealtimeTest, TestInternalResizeDownUpChangeBitRateScreen) {
+ ::libaom_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288,
+ 30, 1, 0, 300);
+ init_flags_ = AOM_CODEC_USE_PSNR;
+ cfg_.g_w = 352;
+ cfg_.g_h = 288;
+ change_bitrate_ = true;
+ frame_change_bitrate_ = 120;
+ set_scale_mode_ = false;
+ set_scale_mode2_ = false;
+ set_scale_mode3_ = false;
+ mismatch_psnr_ = 0.0;
+ mismatch_nframes_ = 0;
+ is_screen_ = true;
+ DefaultConfig();
+ // Disable dropped frames.
+ cfg_.rc_dropframe_thresh = 0;
+ // Starting bitrate low.
+ cfg_.rc_target_bitrate = 100;
+ cfg_.rc_resize_mode = RESIZE_DYNAMIC;
+ cfg_.g_forced_max_frame_width = 1280;
+ cfg_.g_forced_max_frame_height = 1280;
+ ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
+
+ unsigned int last_w = cfg_.g_w;
+ unsigned int last_h = cfg_.g_h;
+ unsigned int frame_number = 0;
+ int resize_down_count = 0;
+ for (std::vector<FrameInfo>::const_iterator info = frame_info_list_.begin();
+ info != frame_info_list_.end(); ++info) {
+ if (info->w != last_w || info->h != last_h) {
+ if (frame_number < frame_change_bitrate_) {
+ // Verify that resize down occurs, before bitrate is increased.
+ ASSERT_LT(info->w, last_w);
+ ASSERT_LT(info->h, last_h);
+ resize_down_count++;
+ }
+ last_w = info->w;
+ last_h = info->h;
+ }
+ frame_number++;
+ }
+
+#if CONFIG_AV1_DECODER
+ // Verify that we get at least 1 resize event in this test.
+ ASSERT_GE(resize_down_count, 1)
+ << "Resizing down should occur at lease once.";
+ EXPECT_EQ(static_cast<unsigned int>(0), GetMismatchFrames());
+#else
+ printf("Warning: AV1 decoder unavailable, unable to check resize count!\n");
+#endif
+}
+
class ResizeCspTest : public ResizeTest {
protected:
#if WRITE_COMPRESSED_STREAM
diff --git a/third_party/aom/test/variance_test.cc b/third_party/aom/test/variance_test.cc
index a493a1f4cb..e31f8f820c 100644
--- a/third_party/aom/test/variance_test.cc
+++ b/third_party/aom/test/variance_test.cc
@@ -2165,11 +2165,7 @@ INSTANTIATE_TEST_SUITE_P(
MseParams(4, 4, &aom_highbd_10_mse16x16_sve, 10),
MseParams(4, 3, &aom_highbd_10_mse16x8_sve, 10),
MseParams(3, 4, &aom_highbd_10_mse8x16_sve, 10),
- MseParams(3, 3, &aom_highbd_10_mse8x8_sve, 10),
- MseParams(4, 4, &aom_highbd_8_mse16x16_sve, 8),
- MseParams(4, 3, &aom_highbd_8_mse16x8_sve, 8),
- MseParams(3, 4, &aom_highbd_8_mse8x16_sve, 8),
- MseParams(3, 3, &aom_highbd_8_mse8x8_sve, 8)));
+ MseParams(3, 3, &aom_highbd_10_mse8x8_sve, 10)));
#endif // HAVE_SVE
const VarianceParams kArrayHBDVariance_c[] = {
diff --git a/third_party/aom/test/warp_filter_test.cc b/third_party/aom/test/warp_filter_test.cc
index f0be7d226b..8844ba77ca 100644
--- a/third_party/aom/test/warp_filter_test.cc
+++ b/third_party/aom/test/warp_filter_test.cc
@@ -88,6 +88,12 @@ INSTANTIATE_TEST_SUITE_P(
INSTANTIATE_TEST_SUITE_P(
SVE, AV1WarpFilterTest,
libaom_test::AV1WarpFilter::BuildParams(av1_warp_affine_sve));
+
+#if CONFIG_AV1_HIGHBITDEPTH
+INSTANTIATE_TEST_SUITE_P(
+ SVE, AV1HighbdWarpFilterTest,
+ libaom_test::AV1HighbdWarpFilter::BuildParams(av1_highbd_warp_affine_sve));
+#endif // CONFIG_AV1_HIGHBITDEPTH
#endif // HAVE_SVE
} // namespace
diff --git a/third_party/dav1d/NEWS b/third_party/dav1d/NEWS
index 54f8557328..3645474a04 100644
--- a/third_party/dav1d/NEWS
+++ b/third_party/dav1d/NEWS
@@ -1,3 +1,17 @@
+Changes for 1.4.0 'Road Runner':
+--------------------------------
+
+1.4.0 is a medium release of dav1d, focusing on new architecture support and optimizations
+
+- AVX-512 optimizations for z1, z2, z3 in 8bit and high-bitdepth
+- New architecture supported: loongarch
+- Loongarch optimizations for 8bit
+- New architecture supported: RISC-V
+- RISC-V optimizations for itx
+- Misc improvements in threading and in reducing binary size
+- Fix potential integer overflow with extremely large frame sizes (CVE-2024-1580)
+
+
Changes for 1.3.0 'Tundra Peregrine Falcon (Calidus)':
------------------------------------------------------
diff --git a/third_party/dav1d/meson.build b/third_party/dav1d/meson.build
index 30ed4acc09..6e49852103 100644
--- a/third_party/dav1d/meson.build
+++ b/third_party/dav1d/meson.build
@@ -23,7 +23,7 @@
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
project('dav1d', ['c'],
- version: '1.3.0',
+ version: '1.4.0',
default_options: ['c_std=c99',
'warning_level=2',
'buildtype=release',
@@ -136,7 +136,7 @@ if host_machine.system() == 'windows'
rc_data.set('API_VERSION_MAJOR', dav1d_api_version_major)
rc_data.set('API_VERSION_MINOR', dav1d_api_version_minor)
rc_data.set('API_VERSION_REVISION', dav1d_api_version_revision)
- rc_data.set('COPYRIGHT_YEARS', '2018-2023')
+ rc_data.set('COPYRIGHT_YEARS', '2018-2024')
else
thread_dependency = dependency('threads')
thread_compat_dep = []
diff --git a/third_party/dav1d/src/arm/64/itx.S b/third_party/dav1d/src/arm/64/itx.S
index b1b2f8fe65..53490cd677 100644
--- a/third_party/dav1d/src/arm/64/itx.S
+++ b/third_party/dav1d/src/arm/64/itx.S
@@ -1426,6 +1426,7 @@ endfunc
function inv_txfm_add_16x16_neon
mov x15, x30
sub sp, sp, #512
+ mov x8, #16*2
.irp i, 0, 8
add x6, sp, #(\i*16*2)
.if \i == 8
@@ -1433,7 +1434,6 @@ function inv_txfm_add_16x16_neon
b.lt 1f
.endif
add x7, x2, #(\i*2)
- mov x8, #16*2
blr x9
.endr
b 2f
@@ -1449,7 +1449,6 @@ function inv_txfm_add_16x16_neon
.irp i, 0, 8
add x6, x0, #(\i)
add x7, sp, #(\i*2)
- mov x8, #32
bl inv_txfm_add_vert_8x16_neon
.endr
@@ -2461,10 +2460,10 @@ function inv_txfm_add_dct_dct_32x16_8bpc_neon, export=1
b.gt 2b
3:
+ mov x8, #32*2
.irp i, 0, 8, 16, 24
add x6, x0, #(\i)
add x7, sp, #(\i*2)
- mov x8, #32*2
bl inv_txfm_add_vert_8x16_neon
.endr
@@ -3205,10 +3204,10 @@ function inv_txfm_add_dct_dct_64x16_8bpc_neon, export=1
3:
adr x5, inv_dct_8h_x16_neon
+ mov x8, #64*2
.irp i, 0, 8, 16, 24, 32, 40, 48, 56
add x6, x0, #(\i)
add x7, x4, #(\i*2)
- mov x8, #64*2
bl inv_txfm_add_vert_8x16_neon
.endr
diff --git a/third_party/dav1d/src/decode.c b/third_party/dav1d/src/decode.c
index 97d15ca1c6..eed9dfb756 100644
--- a/third_party/dav1d/src/decode.c
+++ b/third_party/dav1d/src/decode.c
@@ -2616,6 +2616,25 @@ static void read_restoration_info(Dav1dTaskContext *const t,
}
}
+// modeled after the equivalent function in aomdec:decodeframe.c
+static int check_trailing_bits_after_symbol_coder(const MsacContext *const msac) {
+ // check marker bit (single 1), followed by zeroes
+ const int n_bits = -(msac->cnt + 14);
+ assert(n_bits <= 0); // this assumes we errored out when cnt <= -15 in caller
+ const int n_bytes = (n_bits + 7) >> 3;
+ const uint8_t *p = &msac->buf_pos[n_bytes];
+ const int pattern = 128 >> ((n_bits - 1) & 7);
+ if ((p[-1] & (2 * pattern - 1)) != pattern)
+ return 1;
+
+ // check remainder zero bytes
+ for (; p < msac->buf_end; p++)
+ if (*p)
+ return 1;
+
+ return 0;
+}
+
int dav1d_decode_tile_sbrow(Dav1dTaskContext *const t) {
const Dav1dFrameContext *const f = t->f;
const enum BlockLevel root_bl = f->seq_hdr->sb128 ? BL_128X128 : BL_64X64;
@@ -2659,9 +2678,6 @@ int dav1d_decode_tile_sbrow(Dav1dTaskContext *const t) {
return 0;
}
- // error out on symbol decoder overread
- if (ts->msac.cnt < -15) return 1;
-
if (f->c->n_tc > 1 && f->frame_hdr->use_ref_frame_mvs) {
f->c->refmvs_dsp.load_tmvs(&f->rf, ts->tiling.row,
ts->tiling.col_start >> 1, ts->tiling.col_end >> 1,
@@ -2767,7 +2783,12 @@ int dav1d_decode_tile_sbrow(Dav1dTaskContext *const t) {
memcpy(&f->lf.tx_lpf_right_edge[1][align_h * tile_col + (t->by >> ss_ver)],
&t->l.tx_lpf_uv[(t->by & 16) >> ss_ver], sb_step >> ss_ver);
- return 0;
+ // error out on symbol decoder overread
+ if (ts->msac.cnt <= -15) return 1;
+
+ return c->strict_std_compliance &&
+ (t->by >> f->sb_shift) + 1 >= f->frame_hdr->tiling.row_start_sb[tile_row + 1] &&
+ check_trailing_bits_after_symbol_coder(&ts->msac);
}
int dav1d_decode_frame_init(Dav1dFrameContext *const f) {
@@ -3262,7 +3283,7 @@ error:
return retval;
}
-void dav1d_decode_frame_exit(Dav1dFrameContext *const f, const int retval) {
+void dav1d_decode_frame_exit(Dav1dFrameContext *const f, int retval) {
const Dav1dContext *const c = f->c;
if (f->sr_cur.p.data[0])
@@ -3273,8 +3294,16 @@ void dav1d_decode_frame_exit(Dav1dFrameContext *const f, const int retval) {
(size_t)f->frame_thread.cf_sz * 128 * 128 / 2);
}
for (int i = 0; i < 7; i++) {
- if (f->refp[i].p.frame_hdr)
+ if (f->refp[i].p.frame_hdr) {
+ if (!retval && c->n_fc > 1 && c->strict_std_compliance &&
+ atomic_load(&f->refp[i].progress[1]) == FRAME_ERROR)
+ {
+ retval = DAV1D_ERR(EINVAL);
+ atomic_store(&f->task_thread.error, 1);
+ atomic_store(&f->sr_cur.progress[1], FRAME_ERROR);
+ }
dav1d_thread_picture_unref(&f->refp[i]);
+ }
dav1d_ref_dec(&f->ref_mvs_ref[i]);
}
@@ -3328,6 +3357,7 @@ int dav1d_decode_frame(Dav1dFrameContext *const f) {
}
}
dav1d_decode_frame_exit(f, res);
+ res = f->task_thread.retval;
f->n_tile_data = 0;
return res;
}
diff --git a/third_party/dav1d/src/picture.c b/third_party/dav1d/src/picture.c
index f22f05f0ca..94365bce8c 100644
--- a/third_party/dav1d/src/picture.c
+++ b/third_party/dav1d/src/picture.c
@@ -111,15 +111,15 @@ void dav1d_picture_free_itut_t35(const uint8_t *const data, void *const user_dat
dav1d_free(itut_t35_ctx);
}
-static int picture_alloc_with_edges(Dav1dContext *const c,
- Dav1dPicture *const p,
- const int w, const int h,
- Dav1dSequenceHeader *const seq_hdr, Dav1dRef *const seq_hdr_ref,
- Dav1dFrameHeader *const frame_hdr, Dav1dRef *const frame_hdr_ref,
- const int bpc,
- const Dav1dDataProps *const props,
- Dav1dPicAllocator *const p_allocator,
- void **const extra_ptr)
+static int picture_alloc(Dav1dContext *const c,
+ Dav1dPicture *const p,
+ const int w, const int h,
+ Dav1dSequenceHeader *const seq_hdr, Dav1dRef *const seq_hdr_ref,
+ Dav1dFrameHeader *const frame_hdr, Dav1dRef *const frame_hdr_ref,
+ const int bpc,
+ const Dav1dDataProps *const props,
+ Dav1dPicAllocator *const p_allocator,
+ void **const extra_ptr)
{
if (p->data[0]) {
dav1d_log(c, "Picture already allocated!\n");
@@ -194,12 +194,11 @@ int dav1d_thread_picture_alloc(Dav1dContext *const c, Dav1dFrameContext *const f
{
Dav1dThreadPicture *const p = &f->sr_cur;
- const int res =
- picture_alloc_with_edges(c, &p->p, f->frame_hdr->width[1], f->frame_hdr->height,
- f->seq_hdr, f->seq_hdr_ref,
- f->frame_hdr, f->frame_hdr_ref,
- bpc, &f->tile[0].data.m, &c->allocator,
- (void **) &p->progress);
+ const int res = picture_alloc(c, &p->p, f->frame_hdr->width[1], f->frame_hdr->height,
+ f->seq_hdr, f->seq_hdr_ref,
+ f->frame_hdr, f->frame_hdr_ref,
+ bpc, &f->tile[0].data.m, &c->allocator,
+ (void **) &p->progress);
if (res) return res;
dav1d_picture_copy_props(&p->p, c->content_light, c->content_light_ref,
@@ -212,9 +211,10 @@ int dav1d_thread_picture_alloc(Dav1dContext *const c, Dav1dFrameContext *const f
c->itut_t35 = NULL;
c->n_itut_t35 = 0;
- // Don't clear these flags from c->frame_flags if the frame is not visible.
+ // Don't clear these flags from c->frame_flags if the frame is not going to be output.
// This way they will be added to the next visible frame too.
- const int flags_mask = (f->frame_hdr->show_frame || c->output_invisible_frames)
+ const int flags_mask = ((f->frame_hdr->show_frame || c->output_invisible_frames) &&
+ c->max_spatial_id == f->frame_hdr->spatial_id)
? 0 : (PICTURE_FLAG_NEW_SEQUENCE | PICTURE_FLAG_NEW_OP_PARAMS_INFO);
p->flags = c->frame_flags;
c->frame_flags &= flags_mask;
@@ -233,11 +233,11 @@ int dav1d_picture_alloc_copy(Dav1dContext *const c, Dav1dPicture *const dst, con
{
Dav1dMemPoolBuffer *const buf = (Dav1dMemPoolBuffer *)src->ref->const_data;
struct pic_ctx_context *const pic_ctx = buf->data;
- const int res = picture_alloc_with_edges(c, dst, w, src->p.h,
- src->seq_hdr, src->seq_hdr_ref,
- src->frame_hdr, src->frame_hdr_ref,
- src->p.bpc, &src->m, &pic_ctx->allocator,
- NULL);
+ const int res = picture_alloc(c, dst, w, src->p.h,
+ src->seq_hdr, src->seq_hdr_ref,
+ src->frame_hdr, src->frame_hdr_ref,
+ src->p.bpc, &src->m, &pic_ctx->allocator,
+ NULL);
if (res) return res;
dav1d_picture_copy_props(dst, src->content_light, src->content_light_ref,
diff --git a/third_party/dav1d/src/riscv/64/itx.S b/third_party/dav1d/src/riscv/64/itx.S
index f7d907eedf..60d045150d 100644
--- a/third_party/dav1d/src/riscv/64/itx.S
+++ b/third_party/dav1d/src/riscv/64/itx.S
@@ -117,39 +117,50 @@ function inv_identity_e16_x4_rvv, export=1, ext=v
jr t0
endfunc
+.macro iwht_4
+ vadd.vv v0, v0, v1
+ vsub.vv v5, v2, v3
+ vsub.vv v4, v0, v5
+ vsra.vi v4, v4, 1
+ vsub.vv v2, v4, v1
+ vsub.vv v1, v4, v3
+ vadd.vv v3, v5, v2
+ vsub.vv v0, v0, v1
+.endm
+
.macro idct_4 o0, o1, o2, o3
li t1, 2896
li t2, 1567
li t3, 3784
- vwmul.vx v8, \o0, t1
- vwmul.vx v10, \o0, t1
- vwmacc.vx v8, t1, \o2
+ vwmul.vx v16, \o0, t1
+ vwmul.vx v18, \o0, t1
+ vwmacc.vx v16, t1, \o2
neg t1, t1
- vwmacc.vx v10, t1, \o2
+ vwmacc.vx v18, t1, \o2
- vwmul.vx v12, \o1, t3
+ vwmul.vx v20, \o1, t3
neg t3, t3
- vwmul.vx v14, \o1, t2
- vwmacc.vx v12, t2, \o3
- vwmacc.vx v14, t3, \o3
+ vwmul.vx v22, \o1, t2
+ vwmacc.vx v20, t2, \o3
+ vwmacc.vx v22, t3, \o3
li t1, 2048
- vwadd.wx v8, v8, t1
- vwadd.wx v10, v10, t1
- vwadd.wx v12, v12, t1
- vwadd.wx v14, v14, t1
+ vwadd.wx v16, v16, t1
+ vwadd.wx v18, v18, t1
+ vwadd.wx v20, v20, t1
+ vwadd.wx v22, v22, t1
- vnsra.wi v8, v8, 12
- vnsra.wi v10, v10, 12
- vnsra.wi v12, v12, 12
- vnsra.wi v14, v14, 12
+ vnsra.wi v16, v16, 12
+ vnsra.wi v18, v18, 12
+ vnsra.wi v20, v20, 12
+ vnsra.wi v22, v22, 12
- vsadd.vv \o0, v8, v12
- vsadd.vv \o1, v10, v14
- vssub.vv \o2, v10, v14
- vssub.vv \o3, v8, v12
+ vsadd.vv \o0, v16, v20
+ vsadd.vv \o1, v18, v22
+ vssub.vv \o2, v18, v22
+ vssub.vv \o3, v16, v20
.endm
.macro iadst_4 o0, o1, o2, o3
@@ -211,6 +222,45 @@ function inv_flipadst_e16_x4_rvv, export=1, ext=v
jr t0
endfunc
+function inv_txfm_add_wht_wht_4x4_8bpc_rvv, export=1, ext=v
+ csrw vxrm, zero
+
+ vsetivli zero, 4, e16, mf2, ta, ma
+ vle16.v v0, (a2)
+ addi t0, a2, 8
+ vle16.v v1, (t0)
+ addi t0, t0, 8
+ vle16.v v2, (t0)
+ addi t0, t0, 8
+ vle16.v v3, (t0)
+
+ vsra.vi v0, v0, 2
+ vsra.vi v1, v1, 2
+ vsra.vi v2, v2, 2
+ vsra.vi v3, v3, 2
+
+ iwht_4
+
+ vmv.v.x v4, zero
+
+ vsseg4e16.v v0, (a2)
+ vle16.v v0, (a2)
+ vse16.v v4, (a2)
+ addi t0, a2, 8
+ vle16.v v1, (t0)
+ vse16.v v4, (t0)
+ addi t0, t0, 8
+ vle16.v v2, (t0)
+ vse16.v v4, (t0)
+ addi t0, t0, 8
+ vle16.v v3, (t0)
+ vse16.v v4, (t0)
+
+ iwht_4
+
+ j itx_4x4_end
+endfunc
+
.macro def_fn_4x4 txfm1, txfm2
function inv_txfm_add_\txfm1\()_\txfm2\()_4x4_8bpc_rvv, export=1, ext=v
.ifc \txfm1\()_\txfm2, dct_dct
@@ -353,7 +403,7 @@ itx_8x8_end:
vwaddu.wv v6, v6, v14
vwaddu.wv v7, v7, v15
- vsetvli zero, zero, e16, m1
+ vsetvli zero, zero, e16, m1, ta, ma
vmax.vx v0, v0, zero
vmax.vx v1, v1, zero
vmax.vx v2, v2, zero
@@ -410,69 +460,67 @@ function inv_identity_e16_x8_rvv, export=1, ext=v
jr t0
endfunc
-function inv_dct_e16_x8_rvv, export=1, ext=v
- idct_4 v0, v2, v4, v6
+.macro idct_8 o0, o1, o2, o3, o4, o5, o6, o7
+ idct_4 \o0, \o2, \o4, \o6
li t1, 799
li t2, 4017
li t3, 3406
li t4, 2276
- vwmul.vx v14, v1, t2
+ vwmul.vx v22, \o1, t2
neg t2, t2
- vwmul.vx v8, v1, t1
- vwmacc.vx v14, t1, v7
- vwmacc.vx v8, t2, v7
+ vwmul.vx v16, \o1, t1
+ vwmacc.vx v22, t1, \o7
+ vwmacc.vx v16, t2, \o7
- vwmul.vx v12, v5, t4
+ vwmul.vx v20, \o5, t4
neg t4, t4
- vwmul.vx v10, v5, t3
- vwmacc.vx v12, t3, v3
- vwmacc.vx v10, t4, v3
+ vwmul.vx v18, \o5, t3
+ vwmacc.vx v20, t3, \o3
+ vwmacc.vx v18, t4, \o3
li t1, 2048
- vwadd.wx v8, v8, t1
- vwadd.wx v10, v10, t1
- vwadd.wx v12, v12, t1
- vwadd.wx v14, v14, t1
+ vwadd.wx v16, v16, t1
+ vwadd.wx v18, v18, t1
+ vwadd.wx v20, v20, t1
+ vwadd.wx v22, v22, t1
- vnsra.wi v8, v8, 12
- vnsra.wi v10, v10, 12
- vnsra.wi v12, v12, 12
- vnsra.wi v14, v14, 12
+ vnsra.wi v16, v16, 12
+ vnsra.wi v18, v18, 12
+ vnsra.wi v20, v20, 12
+ vnsra.wi v22, v22, 12
- vssub.vv v7, v14, v12
- vsadd.vv v14, v14, v12
- vssub.vv v1, v8, v10
- vsadd.vv v8, v8, v10
+ vssub.vv \o7, v22, v20
+ vsadd.vv v22, v22, v20
+ vssub.vv \o1, v16, v18
+ vsadd.vv v16, v16, v18
li t2, 2896
- vwmul.vx v10, v7, t2
- vwmul.vx v12, v7, t2
- vwmacc.vx v12, t2, v1
+ vwmul.vx v18, \o7, t2
+ vwmul.vx v20, \o7, t2
+ vwmacc.vx v20, t2, \o1
neg t2, t2
- vwmacc.vx v10, t2, v1
+ vwmacc.vx v18, t2, \o1
- vwadd.wx v10, v10, t1
- vwadd.wx v12, v12, t1
-
- vnsra.wi v10, v10, 12
- vnsra.wi v12, v12, 12
+ vwadd.wx v18, v18, t1
+ vwadd.wx v20, v20, t1
- vssub.vv v7, v0, v14
- vsadd.vv v0, v0, v14
- vssub.vv v9, v2, v12
- vsadd.vv v1, v2, v12
- vssub.vv v5, v4, v10
- vsadd.vv v2, v4, v10
- vssub.vv v4, v6, v8
- vsadd.vv v3, v6, v8
- vmv.v.v v6, v9
+ vnsra.wi v18, v18, 12
+ vnsra.wi v20, v20, 12
- jr t0
-endfunc
+ vssub.vv \o7, \o0, v22
+ vsadd.vv \o0, \o0, v22
+ vssub.vv v17, \o2, v20
+ vsadd.vv \o1, \o2, v20
+ vssub.vv \o5, \o4, v18
+ vsadd.vv \o2, \o4, v18
+ vssub.vv \o4, \o6, v16
+ vsadd.vv \o3, \o6, v16
+ vmv.v.v \o6, v17
+.endm
.macro iadst_8 o0, o1, o2, o3, o4, o5, o6, o7
li t1, 4076
@@ -598,6 +646,11 @@ endfunc
vssub.vv \o7, v8, \o7
.endm
+function inv_dct_e16_x8_rvv, export=1, ext=v
+ idct_8 v0, v1, v2, v3, v4, v5, v6, v7
+ jr t0
+endfunc
+
function inv_adst_e16_x8_rvv, export=1, ext=v
iadst_8 v0, v1, v2, v3, v4, v5, v6, v7
jr t0
@@ -660,3 +713,627 @@ def_fn_8x8 adst, identity
def_fn_8x8 flipadst, identity
def_fn_8x8 identity, adst
def_fn_8x8 identity, flipadst
+
+function inv_identity_e16_x16_rvv, export=1, ext=v
+ li t1, 2*(5793-4096)*8
+.irp i, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
+ vsmul.vx v16, v\i, t1
+ vsadd.vv v\i, v\i, v\i
+ vsadd.vv v\i, v\i, v16
+.endr
+ jr t0
+endfunc
+
+function inv_dct_e16_x16_rvv, export=1, ext=v
+ idct_8 v0, v2, v4, v6, v8, v10, v12, v14
+
+ li t1, 401
+ li t2, 4076
+ li t3, 3166
+ li t4, 2598
+
+ vwmul.vx v30, v1, t2
+ neg t2, t2
+ vwmul.vx v16, v1, t1
+ vwmacc.vx v30, t1, v15
+ vwmacc.vx v16, t2, v15
+
+ vwmul.vx v28, v9, t4
+ neg t4, t4
+ vwmul.vx v18, v9, t3
+ vwmacc.vx v28, t3, v7
+ vwmacc.vx v18, t4, v7
+
+ li t1, 1931
+ li t2, 3612
+ li t3, 3920
+ li t4, 1189
+
+ vwmul.vx v26, v5, t2
+ neg t2, t2
+ vwmul.vx v20, v5, t1
+ vwmacc.vx v26, t1, v11
+ vwmacc.vx v20, t2, v11
+
+ vwmul.vx v24, v13, t4
+ neg t4, t4
+ vwmul.vx v22, v13, t3
+ vwmacc.vx v24, t3, v3
+ vwmacc.vx v22, t4, v3
+
+ li t1, 2048
+ li t2, 2896
+ li t3, 1567
+ li t4, 3784
+
+ vwadd.wx v16, v16, t1
+ vwadd.wx v18, v18, t1
+ vwadd.wx v20, v20, t1
+ vwadd.wx v22, v22, t1
+ vwadd.wx v24, v24, t1
+ vwadd.wx v26, v26, t1
+ vwadd.wx v28, v28, t1
+ vwadd.wx v30, v30, t1
+
+ vnsra.wi v16, v16, 12
+ vnsra.wi v18, v18, 12
+ vnsra.wi v20, v20, 12
+ vnsra.wi v22, v22, 12
+ vnsra.wi v24, v24, 12
+ vnsra.wi v26, v26, 12
+ vnsra.wi v28, v28, 12
+ vnsra.wi v30, v30, 12
+
+ vssub.vv v3, v16, v18
+ vsadd.vv v16, v16, v18
+ vssub.vv v5, v22, v20
+ vsadd.vv v22, v22, v20
+ vssub.vv v11, v24, v26
+ vsadd.vv v24, v24, v26
+ vssub.vv v13, v30, v28
+ vsadd.vv v30, v30, v28
+
+ vwmul.vx v28, v13, t4
+ neg t4, t4
+ vwmul.vx v18, v13, t3
+ vwmul.vx v26, v11, t3
+ vwmacc.vx v28, t3, v3
+ neg t3, t3
+ vwmul.vx v20, v11, t4
+ vwmacc.vx v18, t4, v3
+ vwmacc.vx v20, t3, v5
+ vwmacc.vx v26, t4, v5
+
+ vwadd.wx v18, v18, t1
+ vwadd.wx v20, v20, t1
+ vwadd.wx v26, v26, t1
+ vwadd.wx v28, v28, t1
+
+ vnsra.wi v18, v18, 12
+ vnsra.wi v20, v20, 12
+ vnsra.wi v26, v26, 12
+ vnsra.wi v28, v28, 12
+
+ vssub.vv v5, v18, v20
+ vsadd.vv v18, v18, v20
+ vssub.vv v11, v28, v26
+ vsadd.vv v28, v28, v26
+
+ vssub.vv v7, v16, v22
+ vsadd.vv v16, v16, v22
+ vssub.vv v9, v30, v24
+ vsadd.vv v30, v30, v24
+
+ vwmul.vx v20, v11, t2
+ vwmul.vx v22, v9, t2
+ vwmul.vx v24, v9, t2
+ vwmul.vx v26, v11, t2
+ vwmacc.vx v24, t2, v7
+ vwmacc.vx v26, t2, v5
+ neg t2, t2
+ vwmacc.vx v20, t2, v5
+ vwmacc.vx v22, t2, v7
+
+ vwadd.wx v20, v20, t1
+ vwadd.wx v22, v22, t1
+ vwadd.wx v24, v24, t1
+ vwadd.wx v26, v26, t1
+
+ vnsra.wi v20, v20, 12
+ vnsra.wi v22, v22, 12
+ vnsra.wi v24, v24, 12
+ vnsra.wi v26, v26, 12
+
+ vssub.vv v15, v0, v30
+ vsadd.vv v0, v0, v30
+ vssub.vv v17, v2, v28
+ vsadd.vv v1, v2, v28
+ vssub.vv v13, v4, v26
+ vsadd.vv v2, v4, v26
+ vssub.vv v19, v6, v24
+ vsadd.vv v3, v6, v24
+ vssub.vv v11, v8, v22
+ vsadd.vv v4, v8, v22
+ vsadd.vv v5, v10, v20
+ vssub.vv v10, v10, v20
+ vssub.vv v9, v12, v18
+ vsadd.vv v6, v12, v18
+ vssub.vv v8, v14, v16
+ vsadd.vv v7, v14, v16
+ vmv.v.v v14, v17
+ vmv.v.v v12, v19
+
+ jr t0
+endfunc
+
+.macro iadst_16 o0, o1, o2, o3, o4, o5, o6, o7, o8, o9, o10, o11, o12, o13, o14, o15
+ li t1, 4091
+ li t2, 201
+ li t3, 3973
+ li t4, 995
+
+ vwmul.vx v16, v15, t1
+ neg t1, t1
+ vwmul.vx v18, v15, t2
+ vwmacc.vx v16, t2, v0
+ vwmacc.vx v18, t1, v0
+
+ vwmul.vx v20, v13, t3
+ neg t3, t3
+ vwmul.vx v22, v13, t4
+ vwmacc.vx v20, t4, v2
+ vwmacc.vx v22, t3, v2
+
+ li t1, 3703
+ li t2, 1751
+ li t3, 3290
+ li t4, 2440
+
+ vwmul.vx v24, v11, t1
+ neg t1, t1
+ vwmul.vx v26, v11, t2
+ vwmacc.vx v24, t2, v4
+ vwmacc.vx v26, t1, v4
+
+ vwmul.vx v28, v9, t3
+ neg t3, t3
+ vwmul.vx v30, v9, t4
+ vwmacc.vx v28, t4, v6
+ vwmacc.vx v30, t3, v6
+
+ li t1, 2048
+
+ vwadd.wx v16, v16, t1
+ vwadd.wx v18, v18, t1
+ vwadd.wx v20, v20, t1
+ vwadd.wx v22, v22, t1
+ vwadd.wx v24, v24, t1
+ vwadd.wx v26, v26, t1
+ vwadd.wx v28, v28, t1
+ vwadd.wx v30, v30, t1
+
+ vnsra.wi v0, v16, 12
+ vnsra.wi v18, v18, 12
+ vnsra.wi v2, v20, 12
+ vnsra.wi v22, v22, 12
+ vnsra.wi v4, v24, 12
+ vnsra.wi v26, v26, 12
+ vnsra.wi v6, v28, 12
+ vnsra.wi v30, v30, 12
+
+ li t1, 2751
+ li t2, 3035
+ li t3, 2106
+ li t4, 3513
+
+ vwmul.vx v16, v7, t1
+ neg t1, t1
+ vwmul.vx v20, v7, t2
+ vwmacc.vx v16, t2, v8
+ vwmacc.vx v20, t1, v8
+
+ vwmul.vx v24, v5, t3
+ neg t3, t3
+ vwmul.vx v28, v5, t4
+ vwmacc.vx v24, t4, v10
+ vwmacc.vx v28, t3, v10
+
+ li t1, 2048
+
+ vwadd.wx v16, v16, t1
+ vwadd.wx v20, v20, t1
+ vwadd.wx v24, v24, t1
+ vwadd.wx v28, v28, t1
+
+ vnsra.wi v16, v16, 12
+ vnsra.wi v9, v20, 12
+ vnsra.wi v24, v24, 12
+ vnsra.wi v11, v28, 12
+
+ vssub.vv v8, v0, v16
+ vsadd.vv v0, v0, v16
+ vssub.vv v10, v2, v24
+ vsadd.vv v2, v2, v24
+
+ li t1, 1380
+ li t2, 3857
+ li t3, 601
+ li t4, 4052
+
+ vwmul.vx v16, v3, t1
+ neg t1, t1
+ vwmul.vx v20, v3, t2
+ vwmacc.vx v16, t2, v12
+ vwmacc.vx v20, t1, v12
+
+ vwmul.vx v24, v1, t3
+ neg t3, t3
+ vwmul.vx v28, v1, t4
+ vwmacc.vx v24, t4, v14
+ vwmacc.vx v28, t3, v14
+
+ li t1, 2048
+
+ vwadd.wx v16, v16, t1
+ vwadd.wx v20, v20, t1
+ vwadd.wx v24, v24, t1
+ vwadd.wx v28, v28, t1
+
+ vnsra.wi v16, v16, 12
+ vnsra.wi v13, v20, 12
+ vnsra.wi v24, v24, 12
+ vnsra.wi v15, v28, 12
+
+ vssub.vv v12, v4, v16
+ vsadd.vv v16, v4, v16
+ vssub.vv v14, v6, v24
+ vsadd.vv v20, v6, v24
+
+ vsadd.vv v1, v18, v9
+ vssub.vv v9, v18, v9
+ vsadd.vv v3, v22, v11
+ vssub.vv v11, v22, v11
+ vsadd.vv v18, v26, v13
+ vssub.vv v13, v26, v13
+ vsadd.vv v22, v30, v15
+ vssub.vv v15, v30, v15
+
+ vssub.vv v4, v0, v16
+ vsadd.vv v0, v0, v16
+ vssub.vv v5, v1, v18
+ vsadd.vv v1, v1, v18
+ vssub.vv v6, v2, v20
+ vsadd.vv v2, v2, v20
+ vssub.vv v7, v3, v22
+ vsadd.vv v3, v3, v22
+
+ li t1, 799
+ li t2, 4017
+ li t3, 3406
+ li t4, 2276
+
+ vwmul.vx v16, v8, t2
+ vwmul.vx v18, v8, t1
+ vwmul.vx v20, v10, t4
+ vwmul.vx v22, v10, t3
+ vwmul.vx v24, v13, t2
+ vwmul.vx v26, v13, t1
+ vwmul.vx v28, v15, t4
+ vwmul.vx v30, v15, t3
+ vwmacc.vx v16, t1, v9
+ neg t1, t1
+ vwmacc.vx v20, t3, v11
+ neg t3, t3
+ vwmacc.vx v26, t2, v12
+ neg t2, t2
+ vwmacc.vx v30, t4, v14
+ neg t4, t4
+ vwmacc.vx v18, t2, v9
+ vwmacc.vx v22, t4, v11
+ vwmacc.vx v24, t1, v12
+ vwmacc.vx v28, t3, v14
+
+ li t1, 2048
+ li t2, 2896
+ li t3, 1567
+ li t4, 3784
+
+ vwadd.wx v16, v16, t1
+ vwadd.wx v18, v18, t1
+ vwadd.wx v20, v20, t1
+ vwadd.wx v22, v22, t1
+ vwadd.wx v24, v24, t1
+ vwadd.wx v26, v26, t1
+ vwadd.wx v28, v28, t1
+ vwadd.wx v30, v30, t1
+
+ vnsra.wi v16, v16, 12
+ vnsra.wi v18, v18, 12
+ vnsra.wi v20, v20, 12
+ vnsra.wi v22, v22, 12
+ vnsra.wi v24, v24, 12
+ vnsra.wi v26, v26, 12
+ vnsra.wi v28, v28, 12
+ vnsra.wi v30, v30, 12
+
+ vsadd.vv v8, v16, v24
+ vsadd.vv v9, v18, v26
+ vsadd.vv v10, v20, v28
+ vsadd.vv v11, v22, v30
+ vssub.vv v12, v16, v24
+ vssub.vv v13, v18, v26
+ vssub.vv v14, v20, v28
+ vssub.vv v15, v22, v30
+
+ vwmul.vx v16, v4, t4
+ vwmul.vx v18, v4, t3
+ vwmul.vx v20, v7, t4
+ vwmul.vx v22, v7, t3
+ vwmul.vx v24, v12, t4
+ vwmul.vx v26, v12, t3
+ vwmul.vx v28, v15, t4
+ vwmul.vx v30, v15, t3
+ vwmacc.vx v16, t3, v5
+ vwmacc.vx v22, t4, v6
+ vwmacc.vx v24, t3, v13
+ neg t3, t3
+ vwmacc.vx v30, t4, v14
+ neg t4, t4
+ vwmacc.vx v20, t3, v6
+ vwmacc.vx v28, t3, v14
+ vwmacc.vx v18, t4, v5
+ vwmacc.vx v26, t4, v13
+
+ vwadd.wx v16, v16, t1
+ vwadd.wx v18, v18, t1
+ vwadd.wx v20, v20, t1
+ vwadd.wx v22, v22, t1
+ vwadd.wx v24, v24, t1
+ vwadd.wx v26, v26, t1
+ vwadd.wx v28, v28, t1
+ vwadd.wx v30, v30, t1
+
+ vnsra.wi v16, v16, 12
+ vnsra.wi v18, v18, 12
+ vnsra.wi v20, v20, 12
+ vnsra.wi v22, v22, 12
+ vnsra.wi v24, v24, 12
+ vnsra.wi v26, v26, 12
+ vnsra.wi v28, v28, 12
+ vnsra.wi v30, v30, 12
+
+.ifc \o0, v0
+ vsadd.vv \o14, v9, v11
+ vssub.vv v11, v9, v11
+ vssub.vv v9, v1, v3
+ vsadd.vv \o15, v1, v3
+ vsadd.vv \o1, v8, v10
+ vssub.vv v10, v8, v10
+ vssub.vv v8, v0, v2
+ vsadd.vv \o0, v0, v2
+.else
+ vsadd.vv \o1, v8, v10
+ vssub.vv v10, v8, v10
+ vssub.vv v8, v0, v2
+ vsadd.vv \o0, v0, v2
+ vsadd.vv v2, v9, v11
+ vssub.vv v11, v9, v11
+ vssub.vv v9, v1, v3
+ vsadd.vv \o15, v1, v3
+ vmv.v.v \o14, v2
+.endif
+
+ vsadd.vv \o3, v16, v20
+ vssub.vv v6, v16, v20
+ vsadd.vv \o12, v18, v22
+ vssub.vv v7, v18, v22
+ vsadd.vv \o2, v24, v28
+ vssub.vv v24, v24, v28
+ vsadd.vv \o13, v26, v30
+ vssub.vv v26, v26, v30
+
+ neg t3, t2
+
+ vwmul.vx v28, v24, t2
+ vwmul.vx v30, v24, t2
+ vwmacc.vx v28, t2, v26
+ vwmacc.vx v30, t3, v26
+
+ vwmul.vx v24, v10, t2
+ vwmul.vx v26, v10, t2
+ vwmacc.vx v24, t2, v11
+ vwmacc.vx v26, t3, v11
+
+ vwmul.vx v20, v6, t2
+ vwmul.vx v22, v6, t2
+ vwmacc.vx v20, t2, v7
+ vwmacc.vx v22, t3, v7
+
+ vwmul.vx v16, v8, t2
+ vwmul.vx v18, v8, t2
+ vwmacc.vx v16, t2, v9
+ vwmacc.vx v18, t3, v9
+
+ vwadd.wx v16, v16, t1
+ vwadd.wx v18, v18, t1
+ vwadd.wx v20, v20, t1
+ vwadd.wx v22, v22, t1
+ vwadd.wx v24, v24, t1
+ vwadd.wx v26, v26, t1
+ vwadd.wx v28, v28, t1
+ vwadd.wx v30, v30, t1
+
+ vnsra.wi \o7, v16, 12
+ vnsra.wi \o8, v18, 12
+ vnsra.wi \o4, v20, 12
+ vnsra.wi \o11, v22, 12
+ vnsra.wi \o6, v24, 12
+ vnsra.wi \o9, v26, 12
+ vnsra.wi \o5, v28, 12
+ vnsra.wi \o10, v30, 12
+
+ vmv.v.x v16, zero
+ vssub.vv \o1, v16, \o1
+ vssub.vv \o3, v16, \o3
+ vssub.vv \o5, v16, \o5
+ vssub.vv \o7, v16, \o7
+ vssub.vv \o9, v16, \o9
+ vssub.vv \o11, v16, \o11
+ vssub.vv \o13, v16, \o13
+ vssub.vv \o15, v16, \o15
+.endm
+
+function inv_adst_e16_x16_rvv, export=1, ext=v
+ iadst_16 v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15
+ jr t0
+endfunc
+
+function inv_flipadst_e16_x16_rvv, export=1, ext=v
+ iadst_16 v15, v14, v13, v12, v11, v10, v9, v8, v7, v6, v5, v4, v3, v2, v1, v0
+ jr t0
+endfunc
+
+.macro def_horz_16 variant
+function inv_txfm_horz\variant\()_16x8_rvv, export=1, ext=v
+ vmv.v.x v16, zero
+.irp i, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
+ vle16.v v\i, (t4)
+ vse16.v v16, (t4)
+ add t4, t4, t6
+.endr
+.ifc \variant, _identity
+ li t1, 2*(5793-4096)*8
+.irp i, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
+ vsmul.vx v16, v\i, t1
+ vsra.vi v16, v16, 1
+ vaadd.vv v\i, v\i, v16
+.endr
+.else
+ jalr t0, a4
+.irp i, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
+ vssra.vi v\i, v\i, 2
+.endr
+.endif
+.irp i, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
+ vsse16.v v\i, (t5), t6
+ addi t5, t5, 2
+.endr
+ jr a7
+endfunc
+.endm
+
+def_horz_16
+def_horz_16 _identity
+
+function inv_txfm_add_vert_8x16_rvv, export=1, ext=v
+ vsetivli zero, 8, e16, m1, ta, ma
+.irp i, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
+ vle16.v v\i, (t4)
+ add t4, t4, t6
+.endr
+ jalr t0, a5
+
+.irp i, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
+ vssra.vi v\i, v\i, 4
+.endr
+
+ vsetivli zero, 8, e8, mf2, ta, ma
+ mv t0, t5
+.irp i, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31
+ vle8.v v\i, (t0)
+ add t0, t0, a1
+.endr
+
+ vwaddu.wv v0, v0, v16
+ vwaddu.wv v1, v1, v17
+ vwaddu.wv v2, v2, v18
+ vwaddu.wv v3, v3, v19
+ vwaddu.wv v4, v4, v20
+ vwaddu.wv v5, v5, v21
+ vwaddu.wv v6, v6, v22
+ vwaddu.wv v7, v7, v23
+ vwaddu.wv v8, v8, v24
+ vwaddu.wv v9, v9, v25
+ vwaddu.wv v10, v10, v26
+ vwaddu.wv v11, v11, v27
+ vwaddu.wv v12, v12, v28
+ vwaddu.wv v13, v13, v29
+ vwaddu.wv v14, v14, v30
+ vwaddu.wv v15, v15, v31
+
+ vsetvli zero, zero, e16, m1, ta, ma
+.irp i, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
+ vmax.vx v\i, v\i, zero
+.endr
+
+ vsetvli zero, zero, e8, mf2, ta, ma
+ vnclipu.wi v16, v0, 0
+ vnclipu.wi v17, v1, 0
+ vnclipu.wi v18, v2, 0
+ vnclipu.wi v19, v3, 0
+ vnclipu.wi v20, v4, 0
+ vnclipu.wi v21, v5, 0
+ vnclipu.wi v22, v6, 0
+ vnclipu.wi v23, v7, 0
+ vnclipu.wi v24, v8, 0
+ vnclipu.wi v25, v9, 0
+ vnclipu.wi v26, v10, 0
+ vnclipu.wi v27, v11, 0
+ vnclipu.wi v28, v12, 0
+ vnclipu.wi v29, v13, 0
+ vnclipu.wi v30, v14, 0
+ vnclipu.wi v31, v15, 0
+
+.irp i, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31
+ vse8.v v\i, (t5)
+ add t5, t5, a1
+.endr
+
+ jr a7
+endfunc
+
+function inv_txfm_add_16x16_rvv, export=1, ext=v
+ csrw vxrm, zero
+ vsetivli zero, 8, e16, m1, ta, ma
+ addi sp, sp, -16*32
+.irp i, 0, 8
+ addi t4, a2, \i*2
+ addi t5, sp, \i*16*2
+ li t6, 16*2
+ jalr a7, a6
+.endr
+.irp i, 0, 8
+ addi t4, sp, \i*2
+ addi t5, a0, \i
+ li t6, 16*2
+ jal a7, inv_txfm_add_vert_8x16_rvv
+.endr
+ addi sp, sp, 16*32
+ ret
+endfunc
+
+.macro def_fn_16x16 txfm1, txfm2
+function inv_txfm_add_\txfm1\()_\txfm2\()_16x16_8bpc_rvv, export=1, ext=v
+.ifc \txfm1, identity
+ la a6, inv_txfm_horz_identity_16x8_rvv
+.else
+ la a6, inv_txfm_horz_16x8_rvv
+ la a4, inv_\txfm1\()_e16_x16_rvv
+.endif
+ la a5, inv_\txfm2\()_e16_x16_rvv
+ j inv_txfm_add_16x16_rvv
+endfunc
+.endm
+
+def_fn_16x16 dct, dct
+def_fn_16x16 identity, identity
+def_fn_16x16 dct, adst
+def_fn_16x16 dct, flipadst
+def_fn_16x16 dct, identity
+def_fn_16x16 adst, dct
+def_fn_16x16 adst, adst
+def_fn_16x16 adst, flipadst
+def_fn_16x16 flipadst, dct
+def_fn_16x16 flipadst, adst
+def_fn_16x16 flipadst, flipadst
+def_fn_16x16 identity, dct
diff --git a/third_party/dav1d/src/riscv/itx.h b/third_party/dav1d/src/riscv/itx.h
index bed215471b..28c5e54d42 100644
--- a/third_party/dav1d/src/riscv/itx.h
+++ b/third_party/dav1d/src/riscv/itx.h
@@ -58,7 +58,8 @@ decl_itx_fn(BF(dav1d_inv_txfm_add_wht_wht_##w##x##h, opt))
#define decl_itx_fns(ext) \
decl_itx17_fns( 4, 4, ext); \
-decl_itx16_fns( 8, 8, ext)
+decl_itx16_fns( 8, 8, ext); \
+decl_itx16_fns(16, 16, ext)
decl_itx_fns(rvv);
@@ -103,7 +104,8 @@ static ALWAYS_INLINE void itx_dsp_init_riscv(Dav1dInvTxfmDSPContext *const c, in
if (!(flags & DAV1D_RISCV_CPU_FLAG_V)) return;
#if BITDEPTH == 8
- assign_itx16_fn( , 4, 4, rvv);
+ assign_itx17_fn( , 4, 4, rvv);
assign_itx16_fn( , 8, 8, rvv);
+ assign_itx12_fn( , 16, 16, rvv);
#endif
}
diff --git a/third_party/dav1d/src/x86/ipred.h b/third_party/dav1d/src/x86/ipred.h
index f5f187e53d..57aff0f38c 100644
--- a/third_party/dav1d/src/x86/ipred.h
+++ b/third_party/dav1d/src/x86/ipred.h
@@ -144,6 +144,7 @@ static ALWAYS_INLINE void intra_pred_dsp_init_x86(Dav1dIntraPredDSPContext *cons
init_angular_ipred_fn(SMOOTH_H_PRED, ipred_smooth_h, avx512icl);
init_angular_ipred_fn(SMOOTH_V_PRED, ipred_smooth_v, avx512icl);
init_angular_ipred_fn(Z1_PRED, ipred_z1, avx512icl);
+ init_angular_ipred_fn(Z2_PRED, ipred_z2, avx512icl);
init_angular_ipred_fn(Z3_PRED, ipred_z3, avx512icl);
init_angular_ipred_fn(FILTER_PRED, ipred_filter, avx512icl);
diff --git a/third_party/dav1d/src/x86/ipred16_avx512.asm b/third_party/dav1d/src/x86/ipred16_avx512.asm
index 8124a3b145..69802614c7 100644
--- a/third_party/dav1d/src/x86/ipred16_avx512.asm
+++ b/third_party/dav1d/src/x86/ipred16_avx512.asm
@@ -79,14 +79,17 @@ z_filter_wh: db 7, 7, 11, 11, 15, 15, 19, 19, 19, 23, 23, 23, 31, 31, 31, 39
z_filter_k: dw 8, 8, 6, 6, 4, 4
dw 4, 4, 5, 5, 4, 4
dw 0, 0, 0, 0, 2, 2
+pb_90: times 4 db 90
pw_15: times 2 dw 15
pw_16: times 2 dw 16
pw_17: times 2 dw 17
pw_24: times 2 dw 24
+pw_31: times 2 dw 31
pw_32: times 2 dw 32
pw_63: times 2 dw 63
pw_64: times 2 dw 64
pw_512: times 2 dw 512
+pw_2048: times 2 dw 2048
pw_31806: times 2 dw 31806
pw_32640: times 2 dw 32640
pw_32672: times 2 dw 32672
@@ -114,6 +117,7 @@ JMP_TABLE ipred_smooth_16bpc, avx512icl, w4, w8, w16, w32, w64
JMP_TABLE ipred_smooth_h_16bpc, avx512icl, w4, w8, w16, w32, w64
JMP_TABLE ipred_smooth_v_16bpc, avx512icl, w4, w8, w16, w32, w64
JMP_TABLE ipred_z1_16bpc, avx512icl, w4, w8, w16, w32, w64
+JMP_TABLE ipred_z2_16bpc, avx512icl, w4, w8, w16, w32, w64
JMP_TABLE ipred_z3_16bpc, avx512icl, w4, w8, w16, w32, w64
JMP_TABLE pal_pred_16bpc, avx512icl, w4, w8, w16, w32, w64
@@ -1174,6 +1178,612 @@ cglobal ipred_z1_16bpc, 3, 8, 16, dst, stride, tl, w, h, angle, dx
mov rsp, r7
RET
+cglobal ipred_z2_16bpc, 3, 9, 16, dst, stride, tl, w, h, angle, dx, _, dy
+ tzcnt wd, wm
+ movifnidn angled, anglem
+ lea dxq, [dr_intra_derivative-90]
+ movzx dyd, angleb
+ xor angled, 0x400
+ mov r7, dxq
+ sub dxq, dyq
+ movifnidn hd, hm
+ and dyd, ~1
+ vpbroadcastw m12, [tlq]
+ and dxq, ~1
+ movzx dyd, word [r7+dyq] ; angle - 90
+ lea r7, [z_filter_t0]
+ movzx dxd, word [dxq+270] ; 180 - angle
+ mova m0, [base+pw_31to0]
+ movsxd wq, [base+ipred_z2_16bpc_avx512icl_table+wq*4]
+ movu m4, [tlq+2]
+ neg dyd
+ vpermw m7, m0, [tlq-64*1]
+ lea wq, [base+ipred_z2_16bpc_avx512icl_table+wq]
+ vpbroadcastd m14, [base+pw_31806]
+ vpbroadcastd m15, [base+pw_1]
+ jmp wq
+.w4:
+ movq xm3, [tlq]
+ vpbroadcastq m8, [base+pw_1to32]
+ test angled, 0x400
+ jnz .w4_main ; !enable_intra_edge_filter
+ lea r3d, [hq+2]
+ add angled, 1022
+ shl r3d, 6
+ test r3d, angled
+ jnz .w4_no_upsample_above ; angle >= 130 || h > 8 || (is_sm && h == 8)
+ pshuflw xm0, xm4, q3321
+ sub angled, 1075 ; angle - 53
+ lea r3d, [hq+3]
+ call .upsample_above
+ punpcklwd xm4, xm3, xm4
+ palignr xm3, xm4, xm12, 14
+ jmp .w4_main
+.w4_upsample_left:
+ call .upsample_left
+ movsldup m1, [base+z_xpos_mul]
+ paddw m1, m1
+ jmp .w4_main2
+.w4_no_upsample_above:
+ lea r3d, [hq+3]
+ vpbroadcastd ym0, [base+pw_3]
+ sub angled, 1112 ; angle - 90
+ call .filter_above2
+ lea r3d, [hq+2]
+ add angled, 973 ; angle + 883
+ palignr xm3, xm4, xm12, 14
+ shl r3d, 6
+ test r3d, angled
+ jz .w4_upsample_left ; angle <= 140 || h > 8 || (is_sm && h == 8)
+ call .filter_left16
+.w4_main:
+ movsldup m1, [base+z_xpos_mul]
+ psllw m15, 3
+.w4_main2:
+ vpbroadcastq m0, [base+pw_1to32]
+ vpbroadcastw m11, dxd
+ movsldup m2, [base+z_xpos_mul]
+ vpbroadcastw m13, dyd
+ vpbroadcastd m5, [tlq-2]
+ psllw m10, m8, 6
+ valignq m5, m7, m5, 6
+ pmullw m2, m11
+ psubw m10, m2 ; xpos
+ pmullw m13, m0 ; ypos
+ palignr m5, m7, m5, 14
+ psrlw m12, m13, 6
+ psllw m13, 9
+ paddw m12, m1 ; base_y
+ pand m13, m14 ; frac_y << 9
+ psllw m11, 3
+ lea r5, [strideq*3]
+.w4_loop:
+ psrlw m1, m10, 6 ; base_x
+ pand m2, m14, m10 ; frac
+ vpermw m0, m1, m3 ; top[base_x]
+ vpermw m1, m1, m4 ; top[base_x+1]
+ vpmovw2m k1, m10 ; base_x < 0
+ psllw m2, 9
+ vpermw m0{k1}, m12, m5 ; left[base_y]
+ vpermw m1{k1}, m12, m7 ; left[base_y+1]
+ vmovdqu16 m2{k1}, m13
+ psubw m1, m0
+ pmulhrsw m1, m2
+ paddw m0, m1
+ vextracti32x4 xm1, ym0, 1
+ movq [dstq+strideq*0], xm0
+ movhps [dstq+strideq*1], xm0
+ movq [dstq+strideq*2], xm1
+ movhps [dstq+r5 ], xm1
+ sub hd, 8
+ jl .w4_end
+ vextracti32x8 ym0, m0, 1
+ psubw m10, m11 ; base_x -= dx
+ lea dstq, [dstq+strideq*4]
+ paddw m12, m15 ; base_y++
+ vextracti32x4 xm1, ym0, 1
+ movq [dstq+strideq*0], xm0
+ movhps [dstq+strideq*1], xm0
+ movq [dstq+strideq*2], xm1
+ movhps [dstq+r5 ], xm1
+ lea dstq, [dstq+strideq*4]
+ jg .w4_loop
+.w4_end:
+ RET
+.upsample_above: ; w4/w8
+ mova ym9, [base+pw_1to32]
+ palignr xm1, xm4, xm12, 12
+ paddw xm3, xm4 ; b+c
+ xor angled, 0x7f ; 180 - angle
+ paddw xm0, xm1 ; a+d
+ vpbroadcastw xm1, r9m ; pixel_max
+ vpbroadcastb xm11, r3d
+ psubw xm0, xm3, xm0
+ vpbroadcastb xm2, angled
+ psraw xm0, 3
+ shr angled, 8
+ paddw xm3, xm0
+ pxor xm0, xm0
+ vpcmpeqb k2, xm11, [base+z_filter_wh]
+ pmaxsw xm3, xm0
+ add dxd, dxd
+ pavgw xm3, xm0
+ vpcmpgtb k2{k2}, xm2, [base+z_filter_t0+angleq*8]
+ pminsw xm3, xm1
+ paddw m8, m8
+ jmp .filter_left16b
+.upsample_left: ; h4/h8
+ lea r3d, [hq-1]
+ palignr xm2, xm7, xm12, 14
+ vpbroadcastw xm0, r3d
+ palignr xm1, xm7, xm12, 12
+ pminuw xm0, xm9
+ paddw xm2, xm7 ; b+c
+ vpermw xm0, xm0, xm7
+ add dyd, dyd
+ paddw xm0, xm1 ; a+d
+ vpbroadcastw xm1, r9m ; pixel_max
+ psubw xm0, xm2, xm0
+ psraw xm0, 3
+ paddw xm2, xm0
+ pxor xm0, xm0
+ pmaxsw xm2, xm0
+ pavgw xm2, xm0
+ pminsw xm2, xm1
+ punpckhwd xm0, xm2, xm7
+ punpcklwd xm7, xm2, xm7
+ vinserti32x4 ym7, xm0, 1
+ ret
+.filter_above:
+ sub angled, 90
+.filter_above2:
+ vpbroadcastb ym1, r3d
+ vpbroadcastb ym10, angled
+ mov r3d, angled
+ shr r3d, 8
+ vpcmpeqb k2, ym1, [base+z_filter_wh]
+ mova xm11, [base+z_filter_t0+r3*8]
+ vpcmpgtb k1{k2}, ym10, ym11
+ mova m9, [base+pw_1to32]
+ kmovd r3d, k1
+ test r3d, r3d
+ jz .filter_end
+ pminuw ym0, ym9
+ popcnt r3d, r3d
+ vpbroadcastd ym6, r7m ; max_w
+ kxnorw k1, k1, k1
+ vpbroadcastd ym5, [base+z_filter_k+(r3-1)*4+12*0]
+ kaddw k1, k1, k1 ; ~1
+ vpbroadcastd ym13, [base+z_filter_k+(r3-1)*4+12*1]
+ vpermw ym2, ym0, ym4 ; +1
+ pmullw ym5, ym4
+ paddw ym1, ym2, ym3
+ vmovdqu16 m3{k1}, [tlq-2] ; -2
+ vpermw ym2, ym0, ym2 ; +2
+ vpbroadcastd ym0, [base+z_filter_k+(r3-1)*4+12*2]
+ pmullw ym1, ym13
+ movu m13, [base+pw_0to31]
+ paddw ym2, ym3
+ packssdw ym6, ym6
+ pmullw ym2, ym0
+ paddw ym1, ym5
+ vpcmpgtw k1, ym6, ym13
+ paddw ym1, ym2
+ pxor ym2, ym2
+ psrlw ym1, 3
+ pavgw ym4{k1}, ym1, ym2
+.filter_end:
+ ret
+.filter_left16:
+ vpbroadcastd ym1, [base+pb_90]
+ psubb ym1, ym10
+ vpcmpgtb k2{k2}, ym1, ym11
+.filter_left16b:
+ kmovd r3d, k2
+ test r3d, r3d
+ jz .filter_end
+ lea r5d, [hq-1]
+ vinserti32x4 ym0, ym12, xm7, 1
+ vpbroadcastw ym1, r5d
+ popcnt r3d, r3d
+ vpbroadcastd ym6, r8m ; max_h
+ pminuw ym9, ym1
+ vpbroadcastd ym5, [base+z_filter_k+(r3-1)*4+12*0]
+ vpermw ym2, ym9, ym7 ; +1
+ vpbroadcastd ym10, [base+z_filter_k+(r3-1)*4+12*1]
+ palignr ym1, ym7, ym0, 14 ; -1
+ pmullw ym5, ym7
+ palignr ym0, ym7, ym0, 12 ; -2
+ paddw ym1, ym2
+ vpermw ym2, ym9, ym2 ; +2
+ vpbroadcastd ym9, [base+z_filter_k+(r3-1)*4+12*2]
+ pmullw ym1, ym10
+ paddw ym2, ym0
+ packssdw ym6, ym6
+ pmullw ym2, ym9
+ paddw ym1, ym5
+ vpcmpgtw k1, ym6, [base+pw_0to31]
+ paddw ym1, ym2
+ pxor ym2, ym2
+ psrlw ym1, 3
+ pavgw ym7{k1}, ym1, ym2
+ ret
+.filter_left:
+ cmp hd, 32
+ jl .filter_left16
+ vpbroadcastd m5, [base+pw_3]
+ pminud m0, m9, [base+pw_31] {1to16}
+.filter_left32:
+ vpbroadcastd m6, r8m ; max_h
+ valignq m2, m7, m12, 6
+ packssdw m6, m6
+ palignr m1, m7, m2, 14 ; -1
+ paddw m1, m7
+ palignr m2, m7, m2, 12 ; -2
+ vpcmpgtw k1, m6, m13
+ paddw m2, m5
+ cmp hd, 64
+ je .filter_left64
+ lea r3d, [hq-1]
+ vpbroadcastw m10, r3d
+ pminuw m0, m10
+ vpermw m10, m0, m7 ; +1
+ paddw m1, m10
+ vpermw m10, m0, m10 ; +2
+ pavgw m2, m10
+ paddw m1, m2
+ vpsrlw m7{k1}, m1, 2
+ ret
+.filter_left64:
+ valignq m10, m8, m7, 2
+ vpaddd m13, [base+pw_32] {1to16}
+ palignr m11, m10, m7, 2 ; +1
+ paddw m1, m11
+ palignr m11, m10, m7, 4 ; +2
+ valignq m10, m8, m7, 6
+ pavgw m11, m2
+ vpermw m2, m0, m8 ; 32+1
+ paddw m1, m11
+ vpsrlw m7{k1}, m1, 2
+ palignr m1, m8, m10, 14 ; 32-1
+ paddw m1, m8
+ palignr m10, m8, m10, 12 ; 32-2
+ paddw m1, m2
+ vpermw m2, m0, m2 ; 32+2
+ paddw m10, m5
+ vpcmpgtw k1, m6, m13
+ pavgw m2, m10
+ paddw m1, m2
+ vpsrlw m8{k1}, m1, 2
+ ret
+.w8:
+ mova xm3, [tlq]
+ vbroadcasti32x4 m8, [base+pw_1to32]
+ test angled, 0x400
+ jnz .w8_main
+ lea r3d, [angleq+126]
+ mov r3b, hb
+ cmp r3d, 8
+ ja .w8_no_upsample_above ; angle >= 130 || h > 8 || is_sm
+ psrldq xm0, xm4, 2
+ sub angled, 53
+ pshufhw xm0, xm0, q2210
+ lea r3d, [hq+7]
+ call .upsample_above
+ punpcklwd xm0, xm3, xm4
+ punpckhwd xm4, xm3, xm4
+ vinserti32x4 ym3, ym12, xm0, 1
+ vinserti32x4 ym4, ym0, xm4, 1
+ palignr ym3, ym4, ym3, 14
+ jmp .w8_main
+.w8_upsample_left:
+ call .upsample_left
+ movshdup m1, [base+z_xpos_mul]
+ psllw m15, 3
+ paddw m1, m1
+ jmp .w8_main2
+.w8_no_upsample_above:
+ lea r3d, [hq+7]
+ vpbroadcastd ym0, [base+pw_7]
+ call .filter_above
+ lea r3d, [angleq-51]
+ mov r3b, hb
+ palignr xm3, xm4, xm12, 14
+ cmp r3d, 8
+ jbe .w8_upsample_left ; angle > 140 && h <= 8 && !is_sm
+ call .filter_left
+.w8_main:
+ movshdup m1, [base+z_xpos_mul]
+ psllw m15, 2
+.w8_main2:
+ vbroadcasti32x4 m0, [base+pw_1to32]
+ vpbroadcastw m11, dxd
+ movshdup m2, [base+z_xpos_mul]
+ vpbroadcastw m13, dyd
+ psllw m10, m8, 6
+ valignq m5, m7, m12, 6
+ pmullw m2, m11
+ psubw m10, m2 ; xpos
+ pmullw m13, m0 ; ypos
+ palignr m5, m7, m5, 14
+ psrlw m12, m13, 6
+ psllw m13, 9
+ mov r2d, 1<<6
+ paddw m12, m1 ; base_y
+ lea r3d, [dxq-(8<<6)] ; left-only threshold
+ pand m13, m14 ; frac_y << 9
+ shl dxd, 2
+ psllw m11, 2
+ lea r5, [strideq*3]
+.w8_loop:
+ psrlw m1, m10, 6
+ pand m2, m14, m10
+ vpermw m0, m1, m3
+ vpermw m1, m1, m4
+ psllw m2, 9
+ sub r2d, dxd
+ jge .w8_toponly
+ vpmovw2m k1, m10
+ vpermw m0{k1}, m12, m5
+ vpermw m1{k1}, m12, m7
+ vmovdqu16 m2{k1}, m13
+.w8_toponly:
+ psubw m1, m0
+ pmulhrsw m1, m2
+ paddw m0, m1
+ mova [dstq+strideq*0], xm0
+ vextracti32x4 [dstq+strideq*1], ym0, 1
+ vextracti32x4 [dstq+strideq*2], m0, 2
+ vextracti32x4 [dstq+r5 ], m0, 3
+ sub hd, 4
+ jz .w8_end
+ psubw m10, m11 ; base_x -= dx
+ lea dstq, [dstq+strideq*4]
+ paddw m12, m15 ; base_y++
+ cmp r2d, r3d
+ jge .w8_loop
+.w8_leftonly_loop:
+ vpermw m0, m12, m5
+ vpermw m1, m12, m7
+ psubw m1, m0
+ pmulhrsw m1, m13
+ paddw m12, m15
+ paddw m0, m1
+ mova [dstq+strideq*0], xm0
+ vextracti32x4 [dstq+strideq*1], ym0, 1
+ vextracti32x4 [dstq+strideq*2], m0, 2
+ vextracti32x4 [dstq+r5 ], m0, 3
+ lea dstq, [dstq+strideq*4]
+ sub hd, 4
+ jg .w8_leftonly_loop
+.w8_end:
+ RET
+.w16:
+ mova ym3, [tlq]
+ vpermw m8, m0, [tlq-64*2]
+ test angled, 0x400
+ jnz .w16_main
+ lea r3d, [hq+15]
+ vpbroadcastd ym0, [base+pw_15]
+ call .filter_above
+ call .filter_left
+ vinserti32x4 ym3, ym12, xm4, 1
+ palignr ym3, ym4, ym3, 14
+.w16_main:
+ vbroadcasti32x8 m0, [base+pw_1to32]
+ vpbroadcastw m11, dxd
+ vpbroadcastw m13, dyd
+ kxnorw k2, k2, k2
+ psllw m10, m0, 6
+ valignq m5, m7, m12, 6
+ psubw m10, m11 ; xpos
+ valignq m6, m8, m7, 6
+ pmullw m13, m0 ; ypos
+ knotd k1, k2
+ palignr m5, m7, m5, 14
+ palignr m6, m8, m6, 14
+ vpsubw m10{k1}, m11
+ psrlw m12, m13, 6
+ psllw m13, 9
+ mov r2d, 1<<6
+ vpsubw m12{k2}, m15 ; base_y
+ pand m13, m14 ; frac_y << 9
+ lea r3d, [dxq-(16<<6)]
+ paddw m11, m11
+ add dxd, dxd
+ paddw m15, m15
+.w16_loop:
+ psrlw m1, m10, 6
+ pand m2, m14, m10
+ vpermw m0, m1, m3
+ vpermw m1, m1, m4
+ psllw m2, 9
+ psubw m1, m0
+ pmulhrsw m1, m2
+ paddw m12, m15 ; base_y++
+ paddw m0, m1
+ sub r2d, dxd
+ jge .w16_toponly
+ mova m1, m5
+ vpermt2w m1, m12, m6
+ mova m2, m7
+ vpermt2w m2, m12, m8
+ vpmovw2m k1, m10
+ psubw m2, m1
+ pmulhrsw m2, m13
+ vpaddw m0{k1}, m1, m2
+.w16_toponly:
+ mova [dstq+strideq*0], ym0
+ vextracti32x8 [dstq+strideq*1], m0, 1
+ sub hd, 2
+ jz .w16_end
+ psubw m10, m11 ; base_x -= dx
+ lea dstq, [dstq+strideq*2]
+ cmp r2d, r3d
+ jge .w16_loop
+ paddw m12, m15
+ vpermt2w m5, m12, m6
+ mova m1, m7
+ vpermt2w m1, m12, m8
+ jmp .w16_leftonly_loop_start
+.w16_leftonly_loop:
+ mova m1, m7
+ vpermt2w m1, m12, m8
+ vshufi32x4 m5, m1, q1032
+.w16_leftonly_loop_start:
+ psubw m0, m1, m5
+ pmulhrsw m0, m13
+ paddw m12, m15
+ paddw m0, m5
+ mova m5, m1
+ mova [dstq+strideq*0], ym0
+ vextracti32x8 [dstq+strideq*1], m0, 1
+ lea dstq, [dstq+strideq*2]
+ sub hd, 2
+ jg .w16_leftonly_loop
+.w16_end:
+ RET
+.w32:
+ mova m3, [tlq]
+ vpermw m8, m0, [tlq-64*2]
+ mova m9, [base+pw_1to32]
+ test angled, 0x400
+ jnz .w32_main
+ pminud m0, m9, [base+pw_31] {1to16}
+ mov r3d, ~1
+ kmovd k1, r3d
+ vpbroadcastd m5, [base+pw_3]
+ vpbroadcastd m6, r6m ; max_w
+ vpermw m2, m0, m4 ; +1
+ movu m13, [base+pw_0to31]
+ paddw m1, m4, m3
+ vmovdqu16 m3{k1}, [tlq-2] ; -2
+ packssdw m6, m6
+ paddw m1, m2
+ vpermw m2, m0, m2 ; +2
+ paddw m3, m5
+ vpcmpgtw k1, m6, m13
+ pavgw m2, m3
+ paddw m1, m2
+ psrlw m4{k1}, m1, 2
+ call .filter_left32
+.w32_main:
+ sub rsp, 64*2
+ call .w32_main1
+ add rsp, 64*2
+ RET
+.w32_main1:
+ vpbroadcastw m11, dxd
+ movu [rsp+64], m4
+ vpbroadcastw m4, dyd
+ movd [rsp+60], xm12
+ valignq m5, m7, m12, 6
+ psllw m3, m9, 6 ; xpos
+ valignq m6, m8, m7, 6
+ pmullw m9, m4 ; ypos
+ palignr m5, m7, m5, 14
+ mov r2d, 33<<6
+ palignr m6, m8, m6, 14
+ mova m10, m3
+.w32_main2:
+ psllw m13, m9, 9
+ sub r2d, dxd
+ psrlw m12, m9, 6 ; base_y
+ mov r8d, hd
+ pand m13, m14 ; frac_y << 9
+.w32_loop:
+ mov r3d, r2d
+ shr r3d, 6
+ psubw m10, m11 ; base_x -= dx
+ movu m0, [rsp+r3*2-2]
+ pand m2, m10, m14 ; frac_x
+ movu m1, [rsp+r3*2]
+ psllw m2, 9
+ psubw m1, m0
+ pmulhrsw m1, m2
+ paddw m12, m15 ; base_y++
+ paddw m0, m1
+ cmp r2d, 32<<6
+ jge .w32_toponly
+ mova m1, m5
+ vpermt2w m1, m12, m6
+ mova m2, m7
+ vpermt2w m2, m12, m8
+ vpmovw2m k1, m10
+ psubw m2, m1
+ pmulhrsw m2, m13
+ vpaddw m0{k1}, m1, m2
+.w32_toponly:
+ mova [dstq], m0
+ dec r8d
+ jz .w32_end
+ add dstq, strideq
+ sub r2d, dxd
+ jge .w32_loop
+ paddw m12, m15
+ mova m2, m5
+ vpermt2w m2, m12, m6
+.w32_leftonly_loop:
+ mova m1, m7
+ vpermt2w m1, m12, m8
+ psubw m0, m1, m2
+ pmulhrsw m0, m13
+ paddw m12, m15
+ paddw m0, m2
+ mova m2, m1
+ mova [dstq], m0
+ add dstq, strideq
+ dec r8d
+ jg .w32_leftonly_loop
+.w32_end:
+ ret
+.w64:
+ movu m3, [tlq+66]
+ vpermw m8, m0, [tlq-64*2]
+ mova m9, [base+pw_1to32]
+ test angled, 0x400
+ jnz .w64_main
+ mova m2, [tlq] ; -1
+ mov r3d, ~1
+ vpbroadcastd m5, [base+pw_3]
+ kmovd k1, r3d
+ movu m13, [base+pw_0to31]
+ vpbroadcastd m6, r6m ; max_w
+ pminud m0, m9, [base+pw_31] {1to16}
+ paddw m1, m4, m2
+ vmovdqu16 m2{k1}, [tlq-2] ; -2
+ packssdw m6, m6
+ paddw m1, [tlq+4] ; +1
+ paddw m2, m5
+ vpcmpgtw k1, m6, m13
+ pavgw m2, [tlq+6] ; +2
+ paddw m1, m2
+ vpermw m2, m0, m3 ; 32+1
+ psrlw m4{k1}, m1, 2
+ paddw m1, m3, [tlq+64] ; 32-1
+ vpaddd m11, m13, [base+pw_32] {1to16}
+ paddw m1, m2
+ vpermw m2, m0, m2 ; 32+2
+ paddw m10, m5, [tlq+62] ; 32-2
+ vpcmpgtw k1, m6, m11
+ pavgw m2, m10
+ paddw m1, m2
+ psrlw m3{k1}, m1, 2
+ call .filter_left32
+.w64_main:
+ sub rsp, 64*3
+ movu [rsp+64*2-gprsize], m3
+ mov r5, dstq
+ call .w32_main1
+ psllw m4, 5
+ mov r2d, 65<<6
+ vpaddd m10, m3, [base+pw_2048] {1to16} ; xpos
+ lea dstq, [r5+64]
+ paddw m9, m4 ; ypos
+ call .w32_main2
+ add rsp, 64*3
+ RET
+
cglobal ipred_z3_16bpc, 3, 8, 16, dst, stride, tl, w, h, angle, dy
lea r7, [z_filter_t0]
tzcnt wd, wm
diff --git a/third_party/dav1d/tests/dav1d_argon.bash b/third_party/dav1d/tests/dav1d_argon.bash
index 27a8d61911..954dad8d2d 100755
--- a/third_party/dav1d/tests/dav1d_argon.bash
+++ b/third_party/dav1d/tests/dav1d_argon.bash
@@ -4,8 +4,8 @@ DAV1D="tools/dav1d"
ARGON_DIR='.'
FILMGRAIN=1
CPUMASK=-1
-THREADS=0
-JOBS=1
+THREADS=1
+JOBS=0
usage() {
NAME=$(basename "$0")
@@ -19,8 +19,8 @@ usage() {
printf " -a dir path to argon dir (default: 'tests/argon' if found; '.' otherwise)\n"
printf " -g \$num enable filmgrain (default: 1)\n"
printf " -c \$mask use restricted cpumask (default: -1)\n"
- printf " -t \$num number of threads per dav1d (default: 0)\n"
- printf " -j \$num number of parallel dav1d processes (default: 1)\n\n"
+ printf " -t \$num number of threads per dav1d (default: 1)\n"
+ printf " -j \$num number of parallel dav1d processes (default: 0)\n\n"
} >&2
exit 1
}
@@ -110,6 +110,14 @@ while getopts ":d:a:g:c:t:j:" opt; do
done
shift $((OPTIND-1))
+if [ "$JOBS" -eq 0 ]; then
+ if [ "$THREADS" -gt 0 ]; then
+ JOBS="$((($( (nproc || sysctl -n hw.logicalcpu || getconf _NPROCESSORS_ONLN || echo 1) 2>/dev/null)+THREADS-1)/THREADS))"
+ else
+ JOBS=1
+ fi
+fi
+
if [ "$#" -eq 0 ]; then
# Everything except large scale tiles and stress files.
dirs=("$ARGON_DIR/profile0_core" "$ARGON_DIR/profile0_core_special"
diff --git a/third_party/function2/Readme.md b/third_party/function2/Readme.md
index d8d2bdc45e..7556e83e8e 100644
--- a/third_party/function2/Readme.md
+++ b/third_party/function2/Readme.md
@@ -63,7 +63,7 @@ Use `fu2::function` as a wrapper for copyable function wrappers and `fu2::unique
The standard implementation `std::function` and `fu2::function` are convertible to each other, see [the chapter convertibility of functions](#convertibility-of-functions) for details.
A function wrapper is declared as following:
-```cpp
+```c++
fu2::function<void(int, float) const>
// Return type ~^ ^ ^ ^
// Parameters ~~~~~|~~~~~| ^
@@ -98,7 +98,7 @@ fu2::function<void(int, float) const>
`fu2::function` and `fu2::unique_function` (non copyable) are easy to use:
-```cpp
+```c++
fu2::function<void() const> fun = [] {
// ...
};
@@ -111,7 +111,7 @@ fun();
`fu2::unique_function` also works with non copyable functors/ lambdas.
-```cpp
+```c++
fu2::unique_function<bool() const> fun = [ptr = std::make_unique<bool>(true)] {
return *ptr;
};
@@ -127,7 +127,7 @@ otherfun();
A `fu2::function_view` can be used to create a non owning view on a persistent object. Note that the view is only valid as long as the object lives.
-```cpp
+```c++
auto callable = [ptr = std::make_unique<bool>(true)] {
return *ptr;
};
@@ -164,7 +164,7 @@ fu2::function_view<bool() const> view(callable);
| fu2::unique_function | No | Yes | No |
| std::function | Yes | Yes | Yes |
-```cpp
+```c++
fu2::function<void()> fun = []{};
// OK
std::function<void()> std_fun = fun;
@@ -197,14 +197,14 @@ struct my_capacity {
The following code defines an owning function with a variadic signature which is copyable and sfo optimization is disabled:
-```cpp
+```c++
template<typename Signature>
using my_function = fu2::function_base<true, true, fu2::capacity_none, true, false, Signature>;
```
The following code defines a non copyable function which just takes 1 argument, and has a huge capacity for internal sfo optimization. Also it must be called as r-value.
-```cpp
+```c++
template<typename Arg>
using my_consumer = fu2::function_base<true, false, fu2::capacity_fixed<100U>,
true, false, void(Arg)&&>;
diff --git a/third_party/function2/include/function2/function2.hpp b/third_party/function2/include/function2/function2.hpp
index e6dfca072e..ffb82b5ef7 100644
--- a/third_party/function2/include/function2/function2.hpp
+++ b/third_party/function2/include/function2/function2.hpp
@@ -1828,6 +1828,14 @@ constexpr auto overload(T&&... callables) {
}
} // namespace fu2
+namespace std{
+template <typename Config, typename Property, typename Alloc>
+struct uses_allocator<
+ ::fu2::detail::function<Config, Property>,
+ Alloc
+> : std::true_type {};
+} // namespace std
+
#undef FU2_DETAIL_EXPAND_QUALIFIERS
#undef FU2_DETAIL_EXPAND_QUALIFIERS_NOEXCEPT
#undef FU2_DETAIL_EXPAND_CV
diff --git a/third_party/function2/moz.yaml b/third_party/function2/moz.yaml
index f1f1376b9f..d4fd5a2043 100644
--- a/third_party/function2/moz.yaml
+++ b/third_party/function2/moz.yaml
@@ -10,9 +10,9 @@ origin:
url: https://naios.github.io/function2/
- release: a354bd093d2b6e50c9325dbce84d20b4e77aabc6 (2023-11-04T12:44:54Z).
+ release: 43fc0ca473ecb081918709bd7d524d84c2ff8dce (2024-02-25T17:37:03Z).
- revision: a354bd093d2b6e50c9325dbce84d20b4e77aabc6
+ revision: 43fc0ca473ecb081918709bd7d524d84c2ff8dce
license: BSL-1.0
license-file: LICENSE.txt
diff --git a/third_party/gemmology/gemmology.h b/third_party/gemmology/gemmology.h
index d774c53388..eb5ebed3b4 100644
--- a/third_party/gemmology/gemmology.h
+++ b/third_party/gemmology/gemmology.h
@@ -198,6 +198,17 @@ PermuteSummer(xsimd::batch<int32_t, Arch> pack0123,
return _mm256_add_epi32(rev, blended);
}
+template <class Arch>
+inline xsimd::batch<int32_t, Arch> Pack0123(xsimd::batch<int32_t, Arch> sum0,
+ xsimd::batch<int32_t, Arch> sum1,
+ xsimd::batch<int32_t, Arch> sum2,
+ xsimd::batch<int32_t, Arch> sum3,
+ xsimd::kernel::requires_arch<xsimd::avx2>) {
+ auto pack01 = _mm256_hadd_epi32(sum0, sum1);
+ auto pack23 = _mm256_hadd_epi32(sum2, sum3);
+ return _mm256_hadd_epi32(pack01, pack23);
+}
+
#ifdef __AVXVNNI__
template <class Arch>
@@ -245,6 +256,17 @@ madd(xsimd::batch<int8_t, Arch> x, xsimd::batch<int8_t, Arch> y,
xsimd::kernel::requires_arch<xsimd::ssse3>) {
return _mm_maddubs_epi16(xsimd::abs(x), _mm_sign_epi8(y, x));
}
+
+template <class Arch>
+inline xsimd::batch<int32_t, Arch> Pack0123(xsimd::batch<int32_t, Arch> sum0,
+ xsimd::batch<int32_t, Arch> sum1,
+ xsimd::batch<int32_t, Arch> sum2,
+ xsimd::batch<int32_t, Arch> sum3,
+ xsimd::kernel::requires_arch<xsimd::ssse3>) {
+ auto pack01 = _mm_hadd_epi32(sum0, sum1);
+ auto pack23 = _mm_hadd_epi32(sum2, sum3);
+ return _mm_hadd_epi32(pack01, pack23);
+}
#endif
#ifdef __SSE2__
@@ -524,7 +546,8 @@ xsimd::batch<int8_t, Arch>
deinterleave(xsimd::batch<int16_t, Arch> first,
xsimd::batch<int16_t, Arch> second,
xsimd::kernel::requires_arch<xsimd::neon64>) {
- return vcombine_s8(vqmovn_s16(first), vqmovn_s16(second));
+
+ return vqmovn_high_s16(vqmovn_s16(first), second);
}
template <class Arch>
@@ -532,27 +555,18 @@ xsimd::batch<int16_t, Arch>
deinterleave(xsimd::batch<int32_t, Arch> first,
xsimd::batch<int32_t, Arch> second,
xsimd::kernel::requires_arch<xsimd::neon64>) {
- return vcombine_s16(vqmovn_s32(first), vqmovn_s32(second));
+ return vqmovn_high_s32(vqmovn_s32(first), second);
}
+#ifdef __ARM_FEATURE_MATMUL_INT8
template <class Arch>
inline xsimd::batch<int32_t, Arch>
-madd(xsimd::batch<int16_t, Arch> x, xsimd::batch<int16_t, Arch> y,
- xsimd::kernel::requires_arch<xsimd::neon64>) {
- int32x4_t low = vmull_s16(vget_low_s16(x), vget_low_s16(y));
- return vmlal_high_s16(low, x, y);
-}
-
-template <class Arch>
-inline xsimd::batch<int16_t, Arch>
-madd(xsimd::batch<uint8_t, Arch> x, xsimd::batch<int8_t, Arch> y,
- xsimd::kernel::requires_arch<xsimd::neon64>) {
-
- int16x8_t tl = vmull_s8(vreinterpret_s8_u8(vget_low_u8(x)),
- vget_low_s8(y));
- int16x8_t th = vmull_high_s8(vreinterpretq_s8_u8(x), y);
- return vqaddq_s16(vuzp1q_s16(tl, th), vuzp2q_s16(tl, th));
+maddw(xsimd::batch<uint8_t, Arch> x, xsimd::batch<int8_t, Arch> y,
+ xsimd::batch<int32_t, Arch> z,
+ xsimd::kernel::requires_arch<xsimd::i8mm<xsimd::neon64>>) {
+ return vusdotq_s32(z, x, y);
}
+#endif
template <class Arch>
inline xsimd::batch<int32_t, Arch>
@@ -564,15 +578,17 @@ maddw(xsimd::batch<uint8_t, Arch> x, xsimd::batch<int8_t, Arch> y,
int16x8_t th = vmulq_s16(vreinterpretq_s16_u16(vmovl_u8(vget_high_u8(x))),
vmovl_s8(vget_high_s8(y)));
return vpadalq_s16(vpadalq_s16(z, tl), th);
- //TODO: investigate using vdotq_s32
}
template <class Arch>
-inline xsimd::batch<int16_t, Arch>
-madd(xsimd::batch<int8_t, Arch> x, xsimd::batch<int8_t, Arch> y,
- xsimd::kernel::requires_arch<xsimd::neon64>) {
- int16x8_t low = vmull_s8(vget_low_s8(x), vget_low_s8(y));
- return vmlal_high_s8(low, x, y);
+inline xsimd::batch<int32_t, Arch> Pack0123(xsimd::batch<int32_t, Arch> sum0,
+ xsimd::batch<int32_t, Arch> sum1,
+ xsimd::batch<int32_t, Arch> sum2,
+ xsimd::batch<int32_t, Arch> sum3,
+ xsimd::kernel::requires_arch<xsimd::neon64>) {
+ auto pack01 = vpaddq_s32(sum0, sum1);
+ auto pack23 = vpaddq_s32(sum2, sum3);
+ return vpaddq_s32(pack01, pack23);
}
#endif
@@ -644,20 +660,35 @@ inline auto PermuteSummer(xsimd::batch<int32_t, Arch> pack0123,
return kernel::PermuteSummer(pack0123, pack4567, Arch{});
}
+
+namespace kernel {
+
+ template <class Arch>
+ inline xsimd::batch<int32_t, Arch> Pack0123(xsimd::batch<int32_t, Arch> sum0,
+ xsimd::batch<int32_t, Arch> sum1,
+ xsimd::batch<int32_t, Arch> sum2,
+ xsimd::batch<int32_t, Arch> sum3,
+ xsimd::kernel::requires_arch<xsimd::generic>) {
+
+ std::tie(sum0, sum1) = interleave(sum0, sum1, Arch{});
+ auto pack01 = sum0 + sum1;
+ std::tie(sum2, sum3) = interleave(sum2, sum3, Arch{});
+ auto pack23 = sum2 + sum3;
+
+ auto packed = interleave(xsimd::bitwise_cast<int64_t>(pack01),
+ xsimd::bitwise_cast<int64_t>(pack23),
+ Arch{});
+ return xsimd::bitwise_cast<int32_t>(std::get<0>(packed)) +
+ xsimd::bitwise_cast<int32_t>(std::get<1>(packed));
+ }
+}
+
template <class Arch>
inline xsimd::batch<int32_t, Arch> Pack0123(xsimd::batch<int32_t, Arch> sum0,
xsimd::batch<int32_t, Arch> sum1,
xsimd::batch<int32_t, Arch> sum2,
xsimd::batch<int32_t, Arch> sum3) {
- std::tie(sum0, sum1) = interleave(sum0, sum1);
- auto pack01 = sum0 + sum1;
- std::tie(sum2, sum3) = interleave(sum2, sum3);
- auto pack23 = sum2 + sum3;
-
- auto packed = interleave(xsimd::bitwise_cast<int64_t>(pack01),
- xsimd::bitwise_cast<int64_t>(pack23));
- return xsimd::bitwise_cast<int32_t>(std::get<0>(packed)) +
- xsimd::bitwise_cast<int32_t>(std::get<1>(packed));
+ return kernel::Pack0123(sum0, sum1, sum2, sum3, Arch{});
}
template <class Arch>
diff --git a/third_party/gemmology/kernels/GemmologyEngineNeon64I8mm.cpp b/third_party/gemmology/kernels/GemmologyEngineNeon64I8mm.cpp
new file mode 100644
index 0000000000..d8259e750f
--- /dev/null
+++ b/third_party/gemmology/kernels/GemmologyEngineNeon64I8mm.cpp
@@ -0,0 +1,19 @@
+/* -*- mode: c++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* this source code form is subject to the terms of the mozilla public
+ * license, v. 2.0. if a copy of the mpl was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <gemmology.h>
+
+namespace gemmology {
+template struct Engine<xsimd::i8mm<xsimd::neon64>>;
+template void Engine<xsimd::i8mm<xsimd::neon64>>::SelectColumnsB(int8_t const*, int8_t*,
+ size_t, uint32_t const*,
+ uint32_t const*);
+template void Engine<xsimd::i8mm<xsimd::neon64>>::Shift::Multiply(
+ uint8_t const*, int8_t const*, size_t, size_t, size_t,
+ gemmology::callbacks::UnquantizeAndAddBiasAndWrite);
+template void Engine<xsimd::i8mm<xsimd::neon64>>::Shift::PrepareBias(
+ int8_t const*, size_t, size_t,
+ gemmology::callbacks::UnquantizeAndAddBiasAndWrite);
+} // namespace gemmology
diff --git a/third_party/gemmology/moz.yaml b/third_party/gemmology/moz.yaml
index d9f9472da7..749227e2ee 100644
--- a/third_party/gemmology/moz.yaml
+++ b/third_party/gemmology/moz.yaml
@@ -10,8 +10,8 @@ origin:
url: https://github.com/mozilla/gemmology
- release: ec535e87d0ab9d1457ff6d2af247cc8113e74694 (2024-02-05T09:05:20Z).
- revision: ec535e87d0ab9d1457ff6d2af247cc8113e74694
+ release: dbcd029c3bc6e183355ea597216d379677ff9b19 (2024-02-20T12:36:14Z).
+ revision: dbcd029c3bc6e183355ea597216d379677ff9b19
license: MIT
diff --git a/third_party/jpeg-xl/.clang-tidy b/third_party/jpeg-xl/.clang-tidy
index 06c9875624..42d386276b 100644
--- a/third_party/jpeg-xl/.clang-tidy
+++ b/third_party/jpeg-xl/.clang-tidy
@@ -25,16 +25,31 @@ Checks: >-
modernize-*,
performance-*,
readability-*,
+ -bugprone-narrowing-conversions,
+ -bugprone-branch-clone,
+ -bugprone-easily-swappable-parameters,
+ -bugprone-implicit-widening-of-multiplication-result,
+ -bugprone-infinite-loop,
+ -bugprone-unused-local-non-trivial-variable,
+ -modernize-avoid-c-arrays,
-modernize-deprecated-headers,
-modernize-return-braced-init-list,
-modernize-use-auto,
-modernize-use-default-member-init,
-modernize-use-trailing-return-type,
-modernize-use-using,
+ -performance-enum-size,
+ -readability-avoid-nested-conditional-operator,
-readability-else-after-return,
-readability-function-cognitive-complexity,
+ -readability-identifier-length,
+ -readability-magic-numbers,
+ -readability-redundant-access-specifiers,
+ -readability-simplify-boolean-expr,
-readability-static-accessed-through-instance,
+ -readability-suspicious-call-argument,
-readability-uppercase-literal-suffix,
+ -readability-use-anyofallof,
WarningsAsErrors: >-
@@ -57,11 +72,13 @@ WarningsAsErrors: >-
HeaderFilterRegex: '^.*/(lib|tools)/.*\.h$'
CheckOptions:
- - key: readability-braces-around-statements.ShortStatementLines
- value: '2'
- - key: google-readability-braces-around-statements.ShortStatementLines
- value: '2'
- - key: readability-implicit-bool-conversion.AllowPointerConditions
- value: '1'
- - key: readability-implicit-bool-conversion.AllowIntegerConditions
- value: '1'
+ - key: readability-braces-around-statements.ShortStatementLines
+ value: '2'
+ - key: google-readability-braces-around-statements.ShortStatementLines
+ value: '2'
+ - key: readability-implicit-bool-conversion.AllowPointerConditions
+ value: '1'
+ - key: readability-implicit-bool-conversion.AllowIntegerConditions
+ value: '1'
+ - key: bugprone-signed-char-misuse.CharTypdefsToIgnore
+ value: 'int8_t'
diff --git a/third_party/jpeg-xl/AUTHORS b/third_party/jpeg-xl/AUTHORS
index 04d436b8fe..ed6d72db66 100644
--- a/third_party/jpeg-xl/AUTHORS
+++ b/third_party/jpeg-xl/AUTHORS
@@ -32,6 +32,7 @@ Zoltan Szabadka <szabadka@google.com>
# Individuals:
a-shvedov
Aditya Patadia <adityapatadia@users.noreply.github.com>
+Ahmad Amsyar Asyadiq Bin Syaiful Bahri <27284123+Ahmad-Amsyar@users.noreply.github.com>
Alex Xu (Hello71) <alex_y_xu@yahoo.ca>
Alexander Sago <cagelight@gmail.com>
Alistair Barrow
@@ -74,6 +75,7 @@ Misaki Kasumi <misakikasumi@outlook.com>
Moonchild Straver <moonchild@palemoon.org>
Nicholas Hayes <0xC0000054@users.noreply.github.com>
Nigel Tao <nigeltao@golang.org>
+oupson <oupson1er@gmail.com>
Petr Diblík
Pieter Wuille
roland-rollo
@@ -93,4 +95,3 @@ xiota
Yonatan Nebenzhal <yonatan.nebenzhl@gmail.com>
Ziemowit Zabawa <ziemek.zabawa@outlook.com>
源文雨 <41315874+fumiama@users.noreply.github.com>
-oupson <oupson1er@gmail.com>
diff --git a/third_party/jpeg-xl/CHANGELOG.md b/third_party/jpeg-xl/CHANGELOG.md
index add193bf67..3c62a1f2df 100644
--- a/third_party/jpeg-xl/CHANGELOG.md
+++ b/third_party/jpeg-xl/CHANGELOG.md
@@ -13,6 +13,32 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed / clarified
+### Fixed
+
+
+## [0.10.0] - 2024-02-21
+
+### Added
+ - decoder API: added `JxlDecoderGetBoxSizeContents` for getting the size of the
+ content of a box without the headers.
+ - encoder API: implemented new api functions for streaming encoding.
+
+### Changed / clarified
+ - decoder/encoder API: return failure when surface allocation fail
+ - encoder API / cjxl: updated modular effort levels to faster settings; the
+ effort range is now 1-10, with 11 available in advanced mode.
+
+## [0.9.2] - 2024-02-07
+
+### Fixed
+ - bugs in the gdk-pixbuf plugin
+ - some build issues
+
+## [0.9.1] - 2024-01-08
+
+### Fixed
+ - multiple build issues
+
## [0.9.0] - 2023-12-22
### Added
@@ -57,6 +83,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- fixed how large boxes are decoded (#2958)
- fixed encoding files with unreadable patches (#3042, #3046)
+## [0.8.2] - 2023-06-14
+
+### Changed
+ - Security: Fix an integer underflow bug in patch decoding (#2551- CVE-2023-35790).
+
+## [0.8.1] - 2023-02-03
+
+### Changed
+ - Allow fast-lossless for 16-bit float input (#2093)
+ - Fix bug in palette (#2120)
+ - Security: Fix OOB read in exif.h (#2101 - [CVE-2023-0645](https://www.cve.org/cverecord?id=CVE-2023-0645))
+
## [0.8.0] - 2023-01-18
### Added
diff --git a/third_party/jpeg-xl/CMakeLists.txt b/third_party/jpeg-xl/CMakeLists.txt
index 85f3c265c2..9b74537f1c 100644
--- a/third_party/jpeg-xl/CMakeLists.txt
+++ b/third_party/jpeg-xl/CMakeLists.txt
@@ -161,13 +161,13 @@ set(JPEGXL_ENABLE_AVX512_SPR false CACHE BOOL
set(JPEGXL_ENABLE_AVX512_ZEN4 false CACHE BOOL
"Build with Zen4-optimized AVX512 support (faster on CPUs that support it, but larger binary size).")
set(JPEGXL_ENABLE_WASM_TRHEADS true CACHE BOOL
- "Builds WASM modules with threads suppurt")
+ "Builds WASM modules with threads support")
# Force system dependencies.
set(JPEGXL_FORCE_SYSTEM_BROTLI false CACHE BOOL
"Force using system installed brotli instead of third_party/brotli source.")
set(JPEGXL_FORCE_SYSTEM_GTEST false CACHE BOOL
- "Force using system installed googletest (gtest/gmock) instead of third_party/googletest source.")
+ "Force using system installed googletest (gtest) instead of third_party/googletest source.")
set(JPEGXL_FORCE_SYSTEM_LCMS2 false CACHE BOOL
"Force using system installed lcms2 instead of third_party/lcms source.")
set(JPEGXL_FORCE_SYSTEM_HWY false CACHE BOOL
diff --git a/third_party/jpeg-xl/MODULE.bazel b/third_party/jpeg-xl/MODULE.bazel
index c838384185..64a44711b3 100644
--- a/third_party/jpeg-xl/MODULE.bazel
+++ b/third_party/jpeg-xl/MODULE.bazel
@@ -1,4 +1,7 @@
bazel_dep(name = "bazel_skylib", version = "1.5.0")
+bazel_dep(name = "giflib", version = "5.2.1")
bazel_dep(name = "googletest", version = "1.14.0")
-bazel_dep(name = "openexr", version = "3.2.1")
+bazel_dep(name = "libjpeg_turbo", version = "2.1.91")
bazel_dep(name = "libpng", version = "1.6.40")
+bazel_dep(name = "libwebp", version = "1.3.2")
+bazel_dep(name = "openexr", version = "3.2.1")
diff --git a/third_party/jpeg-xl/README.md b/third_party/jpeg-xl/README.md
index bcea13ff23..d4520fff21 100644
--- a/third_party/jpeg-xl/README.md
+++ b/third_party/jpeg-xl/README.md
@@ -73,11 +73,14 @@ To decode a JPEG XL file run:
djxl input.jxl output.png
```
-When possible `cjxl`/`djxl` are able to read/write the following
-image formats: .exr, .gif, .jpeg/.jpg, .pfm, .pgm/.ppm, .pgx, .png.
+When possible, `cjxl`/`djxl` are able to read/write the following image formats:
+OpenEXR (`.exr`), GIF (`.gif`), JPEG (`.jpg`/`.jpeg`), NetPBM (`.pam`/`.pgm`/`.ppm`),
+Portable FloatMap (`.pfm`), PGX Test Format (`.pgx`), Portable Network Graphics (`.png`),
+Animated PNG (`.png`/`.apng`), and JPEG XL itself (`.jxl`).
+
Specifically for JPEG files, the default `cjxl` behavior is to apply lossless
recompression and the default `djxl` behavior is to reconstruct the original
-JPEG file (when the extension of the output file is .jpg).
+JPEG file (when the extension of the output file is `.jpg`).
### Benchmarking
diff --git a/third_party/jpeg-xl/WORKSPACE b/third_party/jpeg-xl/WORKSPACE
index 4e0e1326bb..1383b44e0d 100644
--- a/third_party/jpeg-xl/WORKSPACE
+++ b/third_party/jpeg-xl/WORKSPACE
@@ -30,185 +30,3 @@ cc_library(
""",
path = "third_party/skcms",
)
-
-new_git_repository(
- name = "libjpeg_turbo",
- build_file_content = """
-load("@bazel_skylib//rules:expand_template.bzl", "expand_template")
-SUBSTITUTIONS = {
- "@BUILD@" : "20230208",
- "@CMAKE_PROJECT_NAME@" : "libjpeg-turbo",
- "@COPYRIGHT_YEAR@" : "2023",
- "@INLINE@" : "__inline__",
- "@JPEG_LIB_VERSION@" : "62",
- "@LIBJPEG_TURBO_VERSION_NUMBER@" : "2001091",
- "@SIZE_T@" : "8",
- "@THREAD_LOCAL@" : "__thread",
- "@VERSION@" : "2.1.91",
-}
-YES_DEFINES = [
- "C_ARITH_CODING_SUPPORTED", "D_ARITH_CODING_SUPPORTED",
- "HAVE_BUILTIN_CTZL", "MEM_SRCDST_SUPPORTED"
-]
-NO_DEFINES = [
- "WITH_SIMD", "RIGHT_SHIFT_IS_UNSIGNED", "HAVE_INTRIN_H"
-]
-SUBSTITUTIONS.update({
- "#cmakedefine " + key : "#define " + key for key in YES_DEFINES
-})
-SUBSTITUTIONS.update({
- "#cmakedefine " + key : "// #define " + key for key in NO_DEFINES
-})
-[
- expand_template(
- name = "expand_" + src,
- template = src + ".in",
- out = src,
- substitutions = SUBSTITUTIONS,
- visibility = ["//visibility:public"],
- ) for src in ["jconfig.h", "jconfigint.h", "jversion.h"]
-]
-JPEG16_SOURCES = [
- "jccolor.c",
- "jcdiffct.c",
- "jclossls.c",
- "jcmainct.c",
- "jcprepct.c",
- "jcsample.c",
- "jdcolor.c",
- "jddiffct.c",
- "jdlossls.c",
- "jdmainct.c",
- "jdmerge.c",
- "jdpostct.c",
- "jdsample.c",
- "jquant1.c",
- "jquant2.c",
- "jutils.c",
-]
-JPEG12_SOURCES = JPEG16_SOURCES + [
- "jccoefct.c",
- "jcdctmgr.c",
- "jdcoefct.c",
- "jddctmgr.c",
- "jfdctfst.c",
- "jfdctint.c",
- "jidctflt.c",
- "jidctfst.c",
- "jidctint.c",
- "jidctred.c",
-]
-JPEG_SOURCES = JPEG12_SOURCES + [
- "jaricom.c",
- "jcapimin.c",
- "jcapistd.c",
- "jcarith.c",
- "jchuff.c",
- "jcicc.c",
- "jcinit.c",
- "jclhuff.c",
- "jcmarker.c",
- "jcmaster.c",
- "jcomapi.c",
- "jcparam.c",
- "jcphuff.c",
- "jdapimin.c",
- "jdapistd.c",
- "jdarith.c",
- "jdatadst.c",
- "jdatasrc.c",
- "jdhuff.c",
- "jdicc.c",
- "jdinput.c",
- "jdlhuff.c",
- "jdmarker.c",
- "jdmaster.c",
- "jdphuff.c",
- "jdtrans.c",
- "jerror.c",
- "jfdctflt.c",
- "jmemmgr.c",
- "jmemnobs.c",
-]
-JPEG_HEADERS = [
- "jccolext.c",
- "jchuff.h",
- "jcmaster.h",
- "jconfig.h",
- "jconfigint.h",
- "jdcoefct.h",
- "jdcol565.c",
- "jdcolext.c",
- "jdct.h",
- "jdhuff.h",
- "jdmainct.h",
- "jdmaster.h",
- "jdmerge.h",
- "jdmrg565.c",
- "jdmrgext.c",
- "jdsample.h",
- "jerror.h",
- "jinclude.h",
- "jlossls.h",
- "jmemsys.h",
- "jmorecfg.h",
- "jpeg_nbits_table.h",
- "jpegapicomp.h",
- "jpegint.h",
- "jpeglib.h",
- "jsamplecomp.h",
- "jsimd.h",
- "jsimddct.h",
- "jstdhuff.c",
- "jversion.h",
-]
-cc_library(
- name = "jpeg16",
- srcs = JPEG16_SOURCES,
- hdrs = JPEG_HEADERS,
- local_defines = ["BITS_IN_JSAMPLE=16"],
- visibility = ["//visibility:public"],
-)
-cc_library(
- name = "jpeg12",
- srcs = JPEG12_SOURCES,
- hdrs = JPEG_HEADERS,
- local_defines = ["BITS_IN_JSAMPLE=12"],
- visibility = ["//visibility:public"],
-)
-cc_library(
- name = "jpeg",
- srcs = JPEG_SOURCES,
- hdrs = JPEG_HEADERS,
- deps = [":jpeg16", ":jpeg12"],
- includes = ["."],
- visibility = ["//visibility:public"],
-)
-exports_files([
- "jmorecfg.h",
- "jpeglib.h",
-])
- """,
- remote = "https://github.com/libjpeg-turbo/libjpeg-turbo.git",
- tag = "2.1.91",
-)
-
-http_archive(
- name = "gif",
- build_file_content = """
-cc_library(
- name = "gif",
- srcs = [
- "dgif_lib.c", "egif_lib.c", "gifalloc.c", "gif_err.c", "gif_font.c",
- "gif_hash.c", "openbsd-reallocarray.c", "gif_hash.h",
- "gif_lib_private.h"
- ],
- hdrs = ["gif_lib.h"],
- includes = ["."],
- visibility = ["//visibility:public"],
-)
- """,
- sha256 = "31da5562f44c5f15d63340a09a4fd62b48c45620cd302f77a6d9acf0077879bd",
- strip_prefix = "giflib-5.2.1",
- url = "https://netcologne.dl.sourceforge.net/project/giflib/giflib-5.2.1.tar.gz",
-)
diff --git a/third_party/jpeg-xl/bash_test.sh b/third_party/jpeg-xl/bash_test.sh
index 9bd28f413b..d24ca976e6 100755
--- a/third_party/jpeg-xl/bash_test.sh
+++ b/third_party/jpeg-xl/bash_test.sh
@@ -106,12 +106,6 @@ test_printf_size_t() {
ret=1
fi
- if grep -n -E 'gmock\.h' \
- $(git ls-files | grep -E '(\.c|\.cc|\.cpp|\.h)$' | grep -v -F /testing.h); then
- echo "Don't include gmock directly, instead include 'testing.h'. " >&2
- ret=1
- fi
-
local f
for f in $(git ls-files | grep -E "\.cc$" | xargs grep 'PRI[udx]S' |
cut -f 1 -d : | uniq); do
diff --git a/third_party/jpeg-xl/ci.sh b/third_party/jpeg-xl/ci.sh
index f33efd9693..448a7f9927 100755
--- a/third_party/jpeg-xl/ci.sh
+++ b/third_party/jpeg-xl/ci.sh
@@ -16,6 +16,8 @@ MYDIR=$(dirname $(realpath "$0"))
### Environment parameters:
TEST_STACK_LIMIT="${TEST_STACK_LIMIT:-256}"
+BENCHMARK_NUM_THREADS="${BENCHMARK_NUM_THREADS:-0}"
+BUILD_CONFIG=${BUILD_CONFIG:-}
CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE:-RelWithDebInfo}
CMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH:-}
CMAKE_C_COMPILER_LAUNCHER=${CMAKE_C_COMPILER_LAUNCHER:-}
@@ -79,6 +81,12 @@ if [[ "${ENABLE_WASM_SIMD}" -eq "2" ]]; then
CMAKE_C_FLAGS="${CMAKE_C_FLAGS} -DHWY_WANT_WASM2"
fi
+if [[ -z "${BUILD_CONFIG}" ]]; then
+ TOOLS_DIR="${BUILD_DIR}/tools"
+else
+ TOOLS_DIR="${BUILD_DIR}/tools/${BUILD_CONFIG}"
+fi
+
if [[ ! -z "${HWY_BASELINE_TARGETS}" ]]; then
CMAKE_CXX_FLAGS="${CMAKE_CXX_FLAGS} -DHWY_BASELINE_TARGETS=${HWY_BASELINE_TARGETS}"
fi
@@ -128,17 +136,17 @@ if [[ "${BUILD_TARGET%%-*}" != "arm" ]]; then
)
fi
-CLANG_TIDY_BIN=$(which clang-tidy-6.0 clang-tidy-7 clang-tidy-8 clang-tidy | head -n 1)
+CLANG_TIDY_BIN=$(which clang-tidy-6.0 clang-tidy-7 clang-tidy-8 clang-tidy 2>/dev/null | head -n 1)
# Default to "cat" if "colordiff" is not installed or if stdout is not a tty.
if [[ -t 1 ]]; then
- COLORDIFF_BIN=$(which colordiff cat | head -n 1)
+ COLORDIFF_BIN=$(which colordiff cat 2>/dev/null | head -n 1)
else
COLORDIFF_BIN="cat"
fi
-FIND_BIN=$(which gfind find | head -n 1)
+FIND_BIN=$(which gfind find 2>/dev/null | head -n 1)
# "false" will disable wine64 when not installed. This won't allow
# cross-compiling.
-WINE_BIN=$(which wine64 false | head -n 1)
+WINE_BIN=$(which wine64 false 2>/dev/null | head -n 1)
CLANG_VERSION="${CLANG_VERSION:-}"
# Detect the clang version suffix and store it in CLANG_VERSION. For example,
@@ -411,7 +419,7 @@ cmake_build_and_test() {
if [[ "${PACK_TEST:-}" == "1" ]]; then
(cd "${BUILD_DIR}"
${FIND_BIN} -name '*.cmake' -a '!' -path '*CMakeFiles*'
- # gtest / gmock / gtest_main shared libs
+ # gtest / gtest_main shared libs
${FIND_BIN} lib/ -name 'libg*.so*'
${FIND_BIN} -type d -name tests -a '!' -path '*CMakeFiles*'
) | tar -C "${BUILD_DIR}" -cf "${BUILD_DIR}/tests.tar.xz" -T - \
@@ -791,9 +799,13 @@ cmd_ossfuzz_ninja() {
cmd_fast_benchmark() {
local small_corpus_tar="${BENCHMARK_CORPORA}/jyrki-full.tar"
+ local small_corpus_url="https://storage.googleapis.com/artifacts.jpegxl.appspot.com/corpora/jyrki-full.tar"
mkdir -p "${BENCHMARK_CORPORA}"
- curl --show-error -o "${small_corpus_tar}" -z "${small_corpus_tar}" \
- "https://storage.googleapis.com/artifacts.jpegxl.appspot.com/corpora/jyrki-full.tar"
+ if [ -f "${small_corpus_tar}" ]; then
+ curl --show-error -o "${small_corpus_tar}" -z "${small_corpus_tar}" "${small_corpus_url}"
+ else
+ curl --show-error -o "${small_corpus_tar}" "${small_corpus_url}"
+ fi
local tmpdir=$(mktemp -d)
CLEANUP_FILES+=("${tmpdir}")
@@ -831,7 +843,7 @@ cmd_benchmark() {
png_filename="${filename%.ppm}.png"
png_filename=$(echo "${png_filename}" | tr '/' '_')
sem --bg --id "${sem_id}" -j"${nprocs}" -- \
- "${BUILD_DIR}/tools/decode_and_encode" \
+ "${TOOLS_DIR}/decode_and_encode" \
"${tmpdir}/${filename}" "${mode}" "${tmpdir}/${png_filename}"
images+=( "${png_filename}" )
done < <(cd "${tmpdir}"; ${FIND_BIN} . -name '*.ppm' -type f)
@@ -844,6 +856,8 @@ cmd_benchmark() {
get_mem_available() {
if [[ "${OS}" == "Darwin" ]]; then
echo $(vm_stat | grep -F 'Pages free:' | awk '{print $3 * 4}')
+ elif [[ "${OS}" == MINGW* ]]; then
+ echo $(vmstat | tail -n 1 | awk '{print $4 * 4}')
else
echo $(grep -F MemAvailable: /proc/meminfo | awk '{print $2}')
fi
@@ -856,15 +870,24 @@ run_benchmark() {
local output_dir="${BUILD_DIR}/benchmark_results"
mkdir -p "${output_dir}"
- # The memory available at the beginning of the benchmark run in kB. The number
- # of threads depends on the available memory, and the passed memory per
- # thread. We also add a 2 GiB of constant memory.
- local mem_available="$(get_mem_available)"
- # Check that we actually have a MemAvailable value.
- [[ -n "${mem_available}" ]]
- local num_threads=$(( (${mem_available} - 1048576) / ${mem_per_thread} ))
- if [[ ${num_threads} -le 0 ]]; then
- num_threads=1
+ if [[ "${OS}" == MINGW* ]]; then
+ src_img_dir=`cygpath -w "${src_img_dir}"`
+ fi
+
+ local num_threads=1
+ if [[ ${BENCHMARK_NUM_THREADS} -gt 0 ]]; then
+ num_threads=${BENCHMARK_NUM_THREADS}
+ else
+ # The memory available at the beginning of the benchmark run in kB. The number
+ # of threads depends on the available memory, and the passed memory per
+ # thread. We also add a 2 GiB of constant memory.
+ local mem_available="$(get_mem_available)"
+ # Check that we actually have a MemAvailable value.
+ [[ -n "${mem_available}" ]]
+ num_threads=$(( (${mem_available} - 1048576) / ${mem_per_thread} ))
+ if [[ ${num_threads} -le 0 ]]; then
+ num_threads=1
+ fi
fi
local benchmark_args=(
@@ -873,20 +896,20 @@ run_benchmark() {
--output_dir "${output_dir}"
--show_progress
--num_threads="${num_threads}"
+ --decode_reps=11
+ --encode_reps=11
)
if [[ "${STORE_IMAGES}" == "1" ]]; then
benchmark_args+=(--save_decompressed --save_compressed)
fi
(
[[ "${TEST_STACK_LIMIT}" == "none" ]] || ulimit -s "${TEST_STACK_LIMIT}"
- "${BUILD_DIR}/tools/benchmark_xl" "${benchmark_args[@]}" | \
+ "${TOOLS_DIR}/benchmark_xl" "${benchmark_args[@]}" | \
tee "${output_dir}/results.txt"
# Check error code for benckmark_xl command. This will exit if not.
return ${PIPESTATUS[0]}
)
-
-
}
# Helper function to wait for the CPU temperature to cool down on ARM.
@@ -1027,7 +1050,7 @@ cmd_arm_benchmark() {
local src_img
for src_img in "${jpg_images[@]}" "${images[@]}"; do
local src_img_hash=$(sha1sum "${src_img}" | cut -f 1 -d ' ')
- local enc_binaries=("${BUILD_DIR}/tools/cjxl")
+ local enc_binaries=("${TOOLS_DIR}/cjxl")
local src_ext="${src_img##*.}"
for enc_binary in "${enc_binaries[@]}"; do
local enc_binary_base=$(basename "${enc_binary}")
@@ -1076,7 +1099,7 @@ cmd_arm_benchmark() {
local dec_output
wait_for_temp
- dec_output=$("${BUILD_DIR}/tools/djxl" "${enc_file}" \
+ dec_output=$("${TOOLS_DIR}/djxl" "${enc_file}" \
--num_reps=5 --num_threads="${num_threads}" 2>&1 | tee /dev/stderr |
grep -E "M[BP]/s \[")
local img_size=$(echo "${dec_output}" | cut -f 1 -d ',')
@@ -1092,7 +1115,7 @@ cmd_arm_benchmark() {
if [[ "${src_ext}" == "jpg" ]]; then
wait_for_temp
local dec_file="${BUILD_DIR}/arm_benchmark/${enc_file_hash}.jpg"
- dec_output=$("${BUILD_DIR}/tools/djxl" "${enc_file}" \
+ dec_output=$("${TOOLS_DIR}/djxl" "${enc_file}" \
"${dec_file}" --num_reps=5 --num_threads="${num_threads}" 2>&1 | \
tee /dev/stderr | grep -E "M[BP]/s \[")
local jpeg_dec_mps_speed=$(_speed_from_output "${dec_output}")
@@ -1122,12 +1145,12 @@ cmd_fuzz() {
local fuzzer_crash_dir=$(realpath "${BUILD_DIR}/fuzzer_crash")
mkdir -p "${corpus_dir}" "${fuzzer_crash_dir}"
# Generate step.
- "${BUILD_DIR}/tools/fuzzer_corpus" "${corpus_dir}"
+ "${TOOLS_DIR}/fuzzer_corpus" "${corpus_dir}"
# Run step:
local nprocs=$(nproc --all || echo 1)
(
- cd "${BUILD_DIR}"
- "tools/djxl_fuzzer" "${fuzzer_crash_dir}" "${corpus_dir}" \
+ cd "${TOOLS_DIR}"
+ djxl_fuzzer "${fuzzer_crash_dir}" "${corpus_dir}" \
-max_total_time="${FUZZER_MAX_TIME}" -jobs=${nprocs} \
-artifact_prefix="${fuzzer_crash_dir}/"
)
diff --git a/third_party/jpeg-xl/debian/control b/third_party/jpeg-xl/debian/control
index f5dc5ce0cc..e0169a3488 100644
--- a/third_party/jpeg-xl/debian/control
+++ b/third_party/jpeg-xl/debian/control
@@ -11,7 +11,6 @@ Build-Depends:
libgdk-pixbuf-2.0-dev | libgdk-pixbuf2.0-dev,
libgif-dev,
libgimp2.0-dev,
- libgmock-dev,
libgoogle-perftools-dev,
libgtest-dev,
libhwy-dev (>= 1.0.0),
diff --git a/third_party/jpeg-xl/deps.sh b/third_party/jpeg-xl/deps.sh
index 70b3a15f07..6c51a5cd4a 100755
--- a/third_party/jpeg-xl/deps.sh
+++ b/third_party/jpeg-xl/deps.sh
@@ -15,7 +15,7 @@ MYDIR=$(dirname $(realpath "$0"))
# update a git submodule.
TESTDATA="873045a9c42ed60721756e26e2a6b32e17415205"
THIRD_PARTY_BROTLI="36533a866ed1ca4b75cf049f4521e4ec5fe24727"
-THIRD_PARTY_HIGHWAY="ba0900a4957b929390ab73827235557959234fea"
+THIRD_PARTY_HIGHWAY="58b52a717469e62b2d9b8eaa2f5dddb44d4a4cbf"
THIRD_PARTY_SKCMS="42030a771244ba67f86b1c1c76a6493f873c5f91"
THIRD_PARTY_SJPEG="e5ab13008bb214deb66d5f3e17ca2f8dbff150bf"
THIRD_PARTY_ZLIB="51b7f2abdade71cd9bb0e7a373ef2610ec6f9daf" # v1.3.1
diff --git a/third_party/jpeg-xl/examples/CMakeLists.txt b/third_party/jpeg-xl/examples/CMakeLists.txt
index aee5fd43ee..8cdb09217e 100644
--- a/third_party/jpeg-xl/examples/CMakeLists.txt
+++ b/third_party/jpeg-xl/examples/CMakeLists.txt
@@ -11,17 +11,15 @@ project(SAMPLE_LIBJXL LANGUAGES C CXX)
# Use pkg-config to find libjxl.
find_package(PkgConfig)
-pkg_check_modules(Jxl REQUIRED IMPORTED_TARGET libjxl)
-pkg_check_modules(JxlCms REQUIRED IMPORTED_TARGET libjxl_cms)
-pkg_check_modules(JxlThreads REQUIRED IMPORTED_TARGET libjxl_threads)
+pkg_check_modules(Jxl REQUIRED IMPORTED_TARGET libjxl libjxl_cms libjxl_threads)
# Build the example encoder/decoder binaries using the default shared libraries
# installed.
add_executable(decode_oneshot decode_oneshot.cc)
-target_link_libraries(decode_oneshot PkgConfig::Jxl PkgConfig::JxlCms PkgConfig::JxlThreads)
+target_link_libraries(decode_oneshot PkgConfig::Jxl)
add_executable(decode_progressive decode_progressive.cc)
-target_link_libraries(decode_progressive PkgConfig::Jxl PkgConfig::JxlCms PkgConfig::JxlThreads)
+target_link_libraries(decode_progressive PkgConfig::Jxl)
add_executable(encode_oneshot encode_oneshot.cc)
-target_link_libraries(encode_oneshot PkgConfig::Jxl PkgConfig::JxlCms PkgConfig::JxlThreads)
+target_link_libraries(encode_oneshot PkgConfig::Jxl)
diff --git a/third_party/jpeg-xl/examples/decode_exif_metadata.cc b/third_party/jpeg-xl/examples/decode_exif_metadata.cc
index 8ec999e4f4..d5f11705bd 100644
--- a/third_party/jpeg-xl/examples/decode_exif_metadata.cc
+++ b/third_party/jpeg-xl/examples/decode_exif_metadata.cc
@@ -11,6 +11,7 @@
#include <jxl/decode_cxx.h>
#include <limits.h>
#include <stdint.h>
+#include <stdio.h>
#include <string.h>
#include <vector>
@@ -55,8 +56,9 @@ bool DecodeJpegXlExif(const uint8_t* jxl, size_t size,
return true;
}
JxlBoxType type;
- if (JXL_DEC_SUCCESS !=
- JxlDecoderGetBoxType(dec.get(), type, support_decompression)) {
+ status = JxlDecoderGetBoxType(dec.get(), type,
+ TO_JXL_BOOL(support_decompression));
+ if (JXL_DEC_SUCCESS != status) {
fprintf(stderr, "Error, failed to get box type\n");
return false;
}
diff --git a/third_party/jpeg-xl/examples/decode_oneshot.cc b/third_party/jpeg-xl/examples/decode_oneshot.cc
index f8d21710bf..a24bd73bfb 100644
--- a/third_party/jpeg-xl/examples/decode_oneshot.cc
+++ b/third_party/jpeg-xl/examples/decode_oneshot.cc
@@ -7,11 +7,6 @@
// available at once). The example outputs the pixels and color information to a
// floating point image and an ICC profile on disk.
-#ifndef __STDC_FORMAT_MACROS
-#define __STDC_FORMAT_MACROS
-#endif
-
-#include <inttypes.h>
#include <jxl/codestream_header.h>
#include <jxl/decode.h>
#include <jxl/decode_cxx.h>
@@ -102,13 +97,13 @@ bool DecodeJpegXlOneShot(const uint8_t* jxl, size_t size,
return false;
}
if (buffer_size != *xsize * *ysize * 16) {
- fprintf(stderr, "Invalid out buffer size %" PRIu64 " %" PRIu64 "\n",
- static_cast<uint64_t>(buffer_size),
- static_cast<uint64_t>(*xsize * *ysize * 16));
+ fprintf(stderr, "Invalid out buffer size %d %d\n",
+ static_cast<int>(buffer_size),
+ static_cast<int>(*xsize * *ysize * 16));
return false;
}
pixels->resize(*xsize * *ysize * 4);
- void* pixels_buffer = (void*)pixels->data();
+ void* pixels_buffer = static_cast<void*>(pixels->data());
size_t pixels_buffer_size = pixels->size() * sizeof(float);
if (JXL_DEC_SUCCESS != JxlDecoderSetImageOutBuffer(dec.get(), &format,
pixels_buffer,
@@ -147,8 +142,8 @@ bool WritePFM(const char* filename, const float* pixels, size_t xsize,
uint8_t little_endian[4];
memcpy(little_endian, &endian_test, 4);
- fprintf(file, "PF\n%d %d\n%s\n", (int)xsize, (int)ysize,
- little_endian[0] ? "-1.0" : "1.0");
+ fprintf(file, "PF\n%d %d\n%s\n", static_cast<int>(xsize),
+ static_cast<int>(ysize), little_endian[0] ? "-1.0" : "1.0");
for (int y = ysize - 1; y >= 0; y--) {
for (size_t x = 0; x < xsize; x++) {
for (size_t c = 0; c < 3; c++) {
@@ -233,7 +228,8 @@ int main(int argc, char* argv[]) {
std::vector<float> pixels;
std::vector<uint8_t> icc_profile;
- size_t xsize = 0, ysize = 0;
+ size_t xsize = 0;
+ size_t ysize = 0;
if (!DecodeJpegXlOneShot(jxl.data(), jxl.size(), &pixels, &xsize, &ysize,
&icc_profile)) {
fprintf(stderr, "Error while decoding the jxl file\n");
diff --git a/third_party/jpeg-xl/examples/decode_progressive.cc b/third_party/jpeg-xl/examples/decode_progressive.cc
index a094cbeb4f..fa7f3df663 100644
--- a/third_party/jpeg-xl/examples/decode_progressive.cc
+++ b/third_party/jpeg-xl/examples/decode_progressive.cc
@@ -6,10 +6,6 @@
// This C++ example decodes a JPEG XL image progressively (input bytes are
// passed in chunks). The example outputs the intermediate steps to PAM files.
-#ifndef __STDC_FORMAT_MACROS
-#define __STDC_FORMAT_MACROS
-#endif
-
#include <inttypes.h>
#include <jxl/decode.h>
#include <jxl/decode_cxx.h>
@@ -29,10 +25,9 @@ bool WritePAM(const char* filename, const uint8_t* buffer, size_t w, size_t h) {
return false;
}
fprintf(fp,
- "P7\nWIDTH %" PRIu64 "\nHEIGHT %" PRIu64
- "\nDEPTH 4\nMAXVAL 255\nTUPLTYPE "
+ "P7\nWIDTH %d\nHEIGHT %d\nDEPTH 4\nMAXVAL 255\nTUPLTYPE "
"RGB_ALPHA\nENDHDR\n",
- static_cast<uint64_t>(w), static_cast<uint64_t>(h));
+ static_cast<int>(w), static_cast<int>(h));
size_t num_bytes = w * h * 4;
if (fwrite(buffer, 1, num_bytes, fp) != num_bytes) {
fclose(fp);
@@ -51,7 +46,8 @@ bool DecodeJpegXlProgressive(const uint8_t* jxl, size_t size,
const char* filename, size_t chunksize) {
std::vector<uint8_t> pixels;
std::vector<uint8_t> icc_profile;
- size_t xsize = 0, ysize = 0;
+ size_t xsize = 0;
+ size_t ysize = 0;
// Multi-threaded parallel runner.
auto runner = JxlResizableParallelRunnerMake(nullptr);
diff --git a/third_party/jpeg-xl/examples/encode_oneshot.cc b/third_party/jpeg-xl/examples/encode_oneshot.cc
index 336446ff17..1582570432 100644
--- a/third_party/jpeg-xl/examples/encode_oneshot.cc
+++ b/third_party/jpeg-xl/examples/encode_oneshot.cc
@@ -39,8 +39,9 @@ bool ReadPFM(const char* filename, std::vector<float>* pixels, uint32_t* xsize,
return false;
}
uint32_t endian_test = 1;
- uint8_t little_endian[4];
- memcpy(little_endian, &endian_test, 4);
+ uint8_t little_endian_check[4];
+ memcpy(little_endian_check, &endian_test, 4);
+ bool little_endian = (little_endian_check[0] == 1);
if (fseek(file, 0, SEEK_END) != 0) {
fclose(file);
@@ -63,7 +64,7 @@ bool ReadPFM(const char* filename, std::vector<float>* pixels, uint32_t* xsize,
data.resize(size);
size_t readsize = fread(data.data(), 1, size, file);
- if ((long)readsize != size) {
+ if (static_cast<long>(readsize) != size) {
fclose(file);
return false;
}
@@ -116,12 +117,13 @@ bool ReadPFM(const char* filename, std::vector<float>* pixels, uint32_t* xsize,
fprintf(stderr,
"%s doesn't seem to be a Portable FloatMap file (pixel data bytes "
"are %d, but expected %d * %d * 3 * 4 + %d (%d).\n",
- filename, (int)data.size(), (int)*ysize, (int)*xsize, (int)offset,
- (int)(*ysize * *xsize * 3 * 4 + offset));
+ filename, static_cast<int>(data.size()), static_cast<int>(*ysize),
+ static_cast<int>(*xsize), static_cast<int>(offset),
+ static_cast<int>(*ysize * *xsize * 3 * 4 + offset));
return false;
}
- if (!!little_endian[0] != input_little_endian) {
+ if (little_endian != input_little_endian) {
fprintf(stderr,
"%s has a different endianness than we do, conversion is not "
"supported.\n",
@@ -132,7 +134,7 @@ bool ReadPFM(const char* filename, std::vector<float>* pixels, uint32_t* xsize,
pixels->resize(*ysize * *xsize * 3);
for (int y = *ysize - 1; y >= 0; y--) {
- for (int x = 0; x < (int)*xsize; x++) {
+ for (int x = 0; x < static_cast<int>(*xsize); x++) {
for (int c = 0; c < 3; c++) {
memcpy(pixels->data() + (y * *xsize + x) * 3 + c, data.data() + offset,
sizeof(float));
@@ -180,8 +182,8 @@ bool EncodeJxlOneshot(const std::vector<float>& pixels, const uint32_t xsize,
}
JxlColorEncoding color_encoding = {};
- JxlColorEncodingSetToSRGB(&color_encoding,
- /*is_gray=*/pixel_format.num_channels < 3);
+ JXL_BOOL is_gray = TO_JXL_BOOL(pixel_format.num_channels < 3);
+ JxlColorEncodingSetToSRGB(&color_encoding, is_gray);
if (JXL_ENC_SUCCESS !=
JxlEncoderSetColorEncoding(enc.get(), &color_encoding)) {
fprintf(stderr, "JxlEncoderSetColorEncoding failed\n");
@@ -193,7 +195,7 @@ bool EncodeJxlOneshot(const std::vector<float>& pixels, const uint32_t xsize,
if (JXL_ENC_SUCCESS !=
JxlEncoderAddImageFrame(frame_settings, &pixel_format,
- (void*)pixels.data(),
+ static_cast<const void*>(pixels.data()),
sizeof(float) * pixels.size())) {
fprintf(stderr, "JxlEncoderAddImageFrame failed\n");
return false;
diff --git a/third_party/jpeg-xl/flake.nix b/third_party/jpeg-xl/flake.nix
index 4832f5b144..64a36311ab 100644
--- a/third_party/jpeg-xl/flake.nix
+++ b/third_party/jpeg-xl/flake.nix
@@ -19,7 +19,6 @@
cmake
pkg-config
gtest
- gmock
doxygen
graphviz
python3
diff --git a/third_party/jpeg-xl/lib/BUILD b/third_party/jpeg-xl/lib/BUILD
index 39e61de0eb..d93858f22d 100644
--- a/third_party/jpeg-xl/lib/BUILD
+++ b/third_party/jpeg-xl/lib/BUILD
@@ -23,8 +23,8 @@ load(
"libjxl_enc_sources",
"libjxl_extras_for_tools_sources",
"libjxl_extras_sources",
- #'libjxl_gbench_sources',
- "libjxl_jpegli_lib_version",
+ # "libjxl_gbench_sources",
+ # "libjxl_jpegli_lib_version",
"libjxl_jpegli_libjpeg_helper_files",
"libjxl_jpegli_sources",
"libjxl_jpegli_testlib_files",
@@ -51,13 +51,14 @@ load(
"libjxl_deps_png",
"libjxl_deps_runfiles",
"libjxl_deps_skcms",
- "libjxl_deps_testdata",
+ # "libjxl_deps_testdata",
+ # "libjxl_deps_webp",
"libjxl_root_package",
"libjxl_test_shards",
"libjxl_test_timeouts",
)
-load("@bazel_skylib//rules:expand_template.bzl", "expand_template")
load("@bazel_skylib//rules:copy_file.bzl", "copy_file")
+load("@bazel_skylib//rules:expand_template.bzl", "expand_template")
DEFAULT_VISIBILITY = ["//:__subpackages__"]
@@ -66,7 +67,7 @@ DEFAULT_COMPATIBILITY = []
INCLUDES_DIR = "include"
package(
- default_visibility = ["//:__subpackages__"],
+ default_visibility = DEFAULT_VISIBILITY,
)
licenses(["notice"])
diff --git a/third_party/jpeg-xl/lib/CMakeLists.txt b/third_party/jpeg-xl/lib/CMakeLists.txt
index 938f15dcbb..1e6f4abf74 100644
--- a/third_party/jpeg-xl/lib/CMakeLists.txt
+++ b/third_party/jpeg-xl/lib/CMakeLists.txt
@@ -149,6 +149,16 @@ else()
set(PKGCONFIG_TARGET_LIBS "\${exec_prefix}/${CMAKE_INSTALL_LIBDIR}")
endif()
+include(CheckCXXSymbolExists)
+set(PKGCONFIG_CXX_LIB "")
+check_cxx_symbol_exists(__GLIBCXX__ iostream LIBSTDCXX)
+check_cxx_symbol_exists(_LIBCPP_VERSION iostream LIBCXX)
+if(LIBSTDCXX)
+ set(PKGCONFIG_CXX_LIB "-lstdc++")
+elseif(LIBCXX)
+ set(PKGCONFIG_CXX_LIB "-lc++")
+endif()
+
# The jxl_cms library definition.
include(jxl_cms.cmake)
# The jxl library definition.
diff --git a/third_party/jpeg-xl/lib/extras/alpha_blend.cc b/third_party/jpeg-xl/lib/extras/alpha_blend.cc
index 50c141c6fb..afb37b0a7d 100644
--- a/third_party/jpeg-xl/lib/extras/alpha_blend.cc
+++ b/third_party/jpeg-xl/lib/extras/alpha_blend.cc
@@ -12,7 +12,7 @@ namespace extras {
namespace {
-void AlphaBlend(PackedFrame* frame, float background[3]) {
+void AlphaBlend(PackedFrame* frame, const float background[3]) {
if (!frame) return;
const PackedImage& im = frame->color;
JxlPixelFormat format = im.format;
@@ -20,7 +20,8 @@ void AlphaBlend(PackedFrame* frame, float background[3]) {
return;
}
--format.num_channels;
- PackedImage blended(im.xsize, im.ysize, format);
+ JXL_ASSIGN_OR_DIE(PackedImage blended,
+ PackedImage::Create(im.xsize, im.ysize, format));
// TODO(szabadka) SIMDify this and make it work for float16.
for (size_t y = 0; y < im.ysize; ++y) {
for (size_t x = 0; x < im.xsize; ++x) {
@@ -48,7 +49,7 @@ void AlphaBlend(PackedFrame* frame, float background[3]) {
} // namespace
-void AlphaBlend(PackedPixelFile* ppf, float background[3]) {
+void AlphaBlend(PackedPixelFile* ppf, const float background[3]) {
if (!ppf || ppf->info.alpha_bits == 0) {
return;
}
diff --git a/third_party/jpeg-xl/lib/extras/alpha_blend.h b/third_party/jpeg-xl/lib/extras/alpha_blend.h
index 4d78e8681b..e49349393c 100644
--- a/third_party/jpeg-xl/lib/extras/alpha_blend.h
+++ b/third_party/jpeg-xl/lib/extras/alpha_blend.h
@@ -11,7 +11,7 @@
namespace jxl {
namespace extras {
-void AlphaBlend(PackedPixelFile* ppf, float background[3]);
+void AlphaBlend(PackedPixelFile* ppf, const float background[3]);
} // namespace extras
} // namespace jxl
diff --git a/third_party/jpeg-xl/lib/extras/codec.h b/third_party/jpeg-xl/lib/extras/codec.h
index b7bc2a397e..9fd61503cb 100644
--- a/third_party/jpeg-xl/lib/extras/codec.h
+++ b/third_party/jpeg-xl/lib/extras/codec.h
@@ -43,7 +43,7 @@ JXL_INLINE Status SetFromBytes(const Span<const uint8_t> bytes, CodecInOut* io,
orig_codec);
}
-Status Encode(const extras::PackedPixelFile& ppf, const extras::Codec codec,
+Status Encode(const extras::PackedPixelFile& ppf, extras::Codec codec,
std::vector<uint8_t>* bytes, ThreadPool* pool);
Status Encode(const extras::PackedPixelFile& ppf, const std::string& pathname,
diff --git a/third_party/jpeg-xl/lib/extras/codec_test.cc b/third_party/jpeg-xl/lib/extras/codec_test.cc
index 4d8a60cb33..50d5e37bc9 100644
--- a/third_party/jpeg-xl/lib/extras/codec_test.cc
+++ b/third_party/jpeg-xl/lib/extras/codec_test.cc
@@ -40,12 +40,6 @@ using test::ThreadPoolForTests;
namespace extras {
namespace {
-using ::testing::AllOf;
-using ::testing::Contains;
-using ::testing::Field;
-using ::testing::IsEmpty;
-using ::testing::SizeIs;
-
std::string ExtensionFromCodec(Codec codec, const bool is_gray,
const bool has_alpha,
const size_t bits_per_sample) {
@@ -78,8 +72,8 @@ void VerifySameImage(const PackedImage& im0, size_t bits_per_sample0,
};
double factor0 = get_factor(im0.format, bits_per_sample0);
double factor1 = get_factor(im1.format, bits_per_sample1);
- auto pixels0 = static_cast<const uint8_t*>(im0.pixels());
- auto pixels1 = static_cast<const uint8_t*>(im1.pixels());
+ const auto* pixels0 = static_cast<const uint8_t*>(im0.pixels());
+ const auto* pixels1 = static_cast<const uint8_t*>(im1.pixels());
auto rgba0 =
test::ConvertToRGBA32(pixels0, im0.xsize, im0.ysize, im0.format, factor0);
auto rgba1 =
@@ -227,19 +221,23 @@ void CreateTestImage(const TestImageParams& params, PackedPixelFile* ppf) {
ppf->info.exponent_bits_per_sample = params.bits_per_sample == 32 ? 8 : 0;
ppf->info.num_color_channels = params.is_gray ? 1 : 3;
ppf->info.alpha_bits = params.add_alpha ? params.bits_per_sample : 0;
- ppf->info.alpha_premultiplied = (params.codec == Codec::kEXR);
+ ppf->info.alpha_premultiplied = TO_JXL_BOOL(params.codec == Codec::kEXR);
JxlColorEncoding color_encoding = CreateTestColorEncoding(params.is_gray);
ppf->icc = GenerateICC(color_encoding);
ppf->color_encoding = color_encoding;
- PackedFrame frame(params.xsize, params.ysize, params.PixelFormat());
+ JXL_ASSIGN_OR_DIE(
+ PackedFrame frame,
+ PackedFrame::Create(params.xsize, params.ysize, params.PixelFormat()));
FillPackedImage(params.bits_per_sample, &frame.color);
if (params.add_extra_channels) {
for (size_t i = 0; i < 7; ++i) {
JxlPixelFormat ec_format = params.PixelFormat();
ec_format.num_channels = 1;
- PackedImage ec(params.xsize, params.ysize, ec_format);
+ JXL_ASSIGN_OR_DIE(
+ PackedImage ec,
+ PackedImage::Create(params.xsize, params.ysize, ec_format));
FillPackedImage(params.bits_per_sample, &ec);
frame.extra_channels.emplace_back(std::move(ec));
PackedExtraChannel pec;
@@ -432,15 +430,17 @@ TEST(CodecTest, EncodeToPNG) {
ASSERT_TRUE(extras::DecodeBytes(Bytes(original_png), ColorHints(), &ppf));
const JxlPixelFormat& format = ppf.frames.front().color.format;
- ASSERT_THAT(
- png_encoder->AcceptedFormats(),
- Contains(AllOf(Field(&JxlPixelFormat::num_channels, format.num_channels),
- Field(&JxlPixelFormat::data_type, format.data_type),
- Field(&JxlPixelFormat::endianness, format.endianness))));
+ const auto& format_matcher = [&format](const JxlPixelFormat& candidate) {
+ return (candidate.num_channels == format.num_channels) &&
+ (candidate.data_type == format.data_type) &&
+ (candidate.endianness == format.endianness);
+ };
+ const auto formats = png_encoder->AcceptedFormats();
+ ASSERT_TRUE(std::any_of(formats.begin(), formats.end(), format_matcher));
EncodedImage encoded_png;
ASSERT_TRUE(png_encoder->Encode(ppf, &encoded_png, pool));
- EXPECT_THAT(encoded_png.icc, IsEmpty());
- ASSERT_THAT(encoded_png.bitstreams, SizeIs(1));
+ EXPECT_TRUE(encoded_png.icc.empty());
+ ASSERT_EQ(encoded_png.bitstreams.size(), 1);
PackedPixelFile decoded_ppf;
ASSERT_TRUE(extras::DecodeBytes(Bytes(encoded_png.bitstreams.front()),
diff --git a/third_party/jpeg-xl/lib/extras/dec/apng.cc b/third_party/jpeg-xl/lib/extras/dec/apng.cc
index f77dab77d1..8b0da06eb1 100644
--- a/third_party/jpeg-xl/lib/extras/dec/apng.cc
+++ b/third_party/jpeg-xl/lib/extras/dec/apng.cc
@@ -71,9 +71,7 @@ const png_byte kIgnoredPngChunks[] = {
};
// Returns floating-point value from the PNG encoding (times 10^5).
-static double F64FromU32(const uint32_t x) {
- return static_cast<int32_t>(x) * 1E-5;
-}
+double F64FromU32(const uint32_t x) { return static_cast<int32_t>(x) * 1E-5; }
Status DecodeSRGB(const unsigned char* payload, const size_t payload_size,
JxlColorEncoding* color_encoding) {
@@ -402,7 +400,8 @@ class BlobsReaderPNG {
}
if (pos + 2 >= encoded_end) return false; // Truncated base16 2;
- uint32_t nibble0, nibble1;
+ uint32_t nibble0;
+ uint32_t nibble1;
JXL_RETURN_IF_ERROR(DecodeNibble(pos[0], &nibble0));
JXL_RETURN_IF_ERROR(DecodeNibble(pos[1], &nibble1));
bytes->push_back(static_cast<uint8_t>((nibble0 << 4) + nibble1));
@@ -432,9 +431,22 @@ constexpr uint32_t kId_cHRM = 0x4D524863;
constexpr uint32_t kId_eXIf = 0x66495865;
struct APNGFrame {
- std::vector<uint8_t> pixels;
+ APNGFrame() : pixels(nullptr, free) {}
+ std::unique_ptr<void, decltype(free)*> pixels;
+ size_t pixels_size = 0;
std::vector<uint8_t*> rows;
unsigned int w, h, delay_num, delay_den;
+ Status Resize(size_t new_size) {
+ if (new_size > pixels_size) {
+ pixels.reset(malloc(new_size));
+ if (!pixels.get()) {
+ // TODO(szabadka): use specialized OOM error code
+ return JXL_FAILURE("Failed to allocate memory for image buffer");
+ }
+ pixels_size = new_size;
+ }
+ return true;
+ }
};
struct Reader {
@@ -447,7 +459,7 @@ struct Reader {
next += to_copy;
return (len == to_copy);
}
- bool Eof() { return next == last; }
+ bool Eof() const { return next == last; }
};
const unsigned long cMaxPNGSize = 1000000UL;
@@ -463,10 +475,11 @@ void info_fn(png_structp png_ptr, png_infop info_ptr) {
void row_fn(png_structp png_ptr, png_bytep new_row, png_uint_32 row_num,
int pass) {
- APNGFrame* frame = (APNGFrame*)png_get_progressive_ptr(png_ptr);
+ APNGFrame* frame =
+ reinterpret_cast<APNGFrame*>(png_get_progressive_ptr(png_ptr));
JXL_CHECK(frame);
JXL_CHECK(row_num < frame->rows.size());
- JXL_CHECK(frame->rows[row_num] < frame->pixels.data() + frame->pixels.size());
+ JXL_CHECK(frame->rows[row_num] < frame->rows[0] + frame->pixels_size);
png_progressive_combine_row(png_ptr, frame->rows[row_num], new_row);
}
@@ -494,12 +507,13 @@ int processing_start(png_structp& png_ptr, png_infop& info_ptr, void* frame_ptr,
unsigned char header[8] = {137, 80, 78, 71, 13, 10, 26, 10};
// Cleanup prior decoder, if any.
- png_destroy_read_struct(&png_ptr, &info_ptr, 0);
+ png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);
// Just in case. Not all versions on libpng wipe-out the pointers.
png_ptr = nullptr;
info_ptr = nullptr;
- png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ png_ptr =
+ png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
info_ptr = png_create_info_struct(png_ptr);
if (!png_ptr || !info_ptr) return 1;
@@ -508,18 +522,17 @@ int processing_start(png_structp& png_ptr, png_infop& info_ptr, void* frame_ptr,
}
png_set_keep_unknown_chunks(png_ptr, 1, kIgnoredPngChunks,
- (int)sizeof(kIgnoredPngChunks) / 5);
+ static_cast<int>(sizeof(kIgnoredPngChunks) / 5));
png_set_crc_action(png_ptr, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE);
- png_set_progressive_read_fn(png_ptr, frame_ptr, info_fn, row_fn, NULL);
+ png_set_progressive_read_fn(png_ptr, frame_ptr, info_fn, row_fn, nullptr);
png_process_data(png_ptr, info_ptr, header, 8);
png_process_data(png_ptr, info_ptr, chunkIHDR.data(), chunkIHDR.size());
if (hasInfo) {
- for (unsigned int i = 0; i < chunksInfo.size(); i++) {
- png_process_data(png_ptr, info_ptr, chunksInfo[i].data(),
- chunksInfo[i].size());
+ for (auto& chunk : chunksInfo) {
+ png_process_data(png_ptr, info_ptr, chunk.data(), chunk.size());
}
}
return 0;
@@ -575,8 +588,6 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes,
const SizeConstraints* constraints) {
#if JPEGXL_ENABLE_APNG
Reader r;
- unsigned int id, j, w, h, w0, h0, x0, y0;
- unsigned int delay_num, delay_den, dop, bop, rowbytes, imagesize;
unsigned char sig[8];
png_structp png_ptr = nullptr;
png_infop info_ptr = nullptr;
@@ -588,7 +599,7 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes,
bool seenFctl = false;
APNGFrame frameRaw = {};
uint32_t num_channels;
- JxlPixelFormat format;
+ JxlPixelFormat format = {};
unsigned int bytes_per_pixel = 0;
struct FrameInfo {
@@ -604,7 +615,7 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes,
// Make sure png memory is released in any case.
auto scope_guard = MakeScopeGuard([&]() {
- png_destroy_read_struct(&png_ptr, &info_ptr, 0);
+ png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);
// Just in case. Not all versions on libpng wipe-out the pointers.
png_ptr = nullptr;
info_ptr = nullptr;
@@ -616,7 +627,7 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes,
if (!r.Read(sig, 8) || memcmp(sig, png_signature, 8) != 0) {
return false;
}
- id = read_chunk(&r, &chunkIHDR);
+ unsigned int id = read_chunk(&r, &chunkIHDR);
ppf->info.exponent_bits_per_sample = 0;
ppf->info.alpha_exponent_bits = 0;
@@ -625,18 +636,22 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes,
ppf->frames.clear();
bool have_color = false;
- bool have_cicp = false, have_iccp = false, have_srgb = false;
+ bool have_cicp = false;
+ bool have_iccp = false;
+ bool have_srgb = false;
bool errorstate = true;
if (id == kId_IHDR && chunkIHDR.size() == 25) {
- x0 = 0;
- y0 = 0;
- delay_num = 1;
- delay_den = 10;
- dop = 0;
- bop = 0;
-
- w0 = w = png_get_uint_32(chunkIHDR.data() + 8);
- h0 = h = png_get_uint_32(chunkIHDR.data() + 12);
+ unsigned int x0 = 0;
+ unsigned int y0 = 0;
+ unsigned int delay_num = 1;
+ unsigned int delay_den = 10;
+ unsigned int dop = 0;
+ unsigned int bop = 0;
+
+ unsigned int w = png_get_uint_32(chunkIHDR.data() + 8);
+ unsigned int h = png_get_uint_32(chunkIHDR.data() + 12);
+ unsigned int w0 = w;
+ unsigned int h0 = h;
if (w > cMaxPNGSize || h > cMaxPNGSize) {
return false;
}
@@ -648,8 +663,8 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes,
ppf->color_encoding.transfer_function = JXL_TRANSFER_FUNCTION_SRGB;
ppf->color_encoding.rendering_intent = JXL_RENDERING_INTENT_RELATIVE;
- if (!processing_start(png_ptr, info_ptr, (void*)&frameRaw, hasInfo,
- chunkIHDR, chunksInfo)) {
+ if (!processing_start(png_ptr, info_ptr, static_cast<void*>(&frameRaw),
+ hasInfo, chunkIHDR, chunksInfo)) {
while (!r.Eof()) {
id = read_chunk(&r, &chunk);
if (!id) break;
@@ -657,7 +672,7 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes,
if (id == kId_acTL && !hasInfo && !isAnimated) {
isAnimated = true;
- ppf->info.have_animation = true;
+ ppf->info.have_animation = JXL_TRUE;
ppf->info.animation.tps_numerator = 1000;
ppf->info.animation.tps_denominator = 1;
} else if (id == kId_IEND ||
@@ -666,8 +681,10 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes,
if (!processing_finish(png_ptr, info_ptr, &ppf->metadata)) {
// Allocates the frame buffer.
uint32_t duration = delay_num * 1000 / delay_den;
- frames.push_back(FrameInfo{PackedImage(w0, h0, format), duration,
- x0, w0, y0, h0, dop, bop});
+ JXL_ASSIGN_OR_RETURN(PackedImage image,
+ PackedImage::Create(w0, h0, format));
+ frames.push_back(FrameInfo{std::move(image), duration, x0, w0, y0,
+ h0, dop, bop});
auto& frame = frames.back().data;
for (size_t y = 0; y < h0; ++y) {
memcpy(static_cast<uint8_t*>(frame.pixels()) + frame.stride * y,
@@ -707,7 +724,8 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes,
if (hasInfo) {
memcpy(chunkIHDR.data() + 8, chunk.data() + 12, 8);
- if (processing_start(png_ptr, info_ptr, (void*)&frameRaw, hasInfo,
+ if (processing_start(png_ptr, info_ptr,
+ static_cast<void*>(&frameRaw), hasInfo,
chunkIHDR, chunksInfo)) {
break;
}
@@ -724,7 +742,7 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes,
int colortype = png_get_color_type(png_ptr, info_ptr);
int png_bit_depth = png_get_bit_depth(png_ptr, info_ptr);
ppf->info.bits_per_sample = png_bit_depth;
- png_color_8p sigbits = NULL;
+ png_color_8p sigbits = nullptr;
png_get_sBIT(png_ptr, info_ptr, &sigbits);
if (colortype & 1) {
// palette will actually be 8-bit regardless of the index bitdepth
@@ -784,12 +802,18 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes,
}
bytes_per_pixel =
num_channels * (format.data_type == JXL_TYPE_UINT16 ? 2 : 1);
- rowbytes = w * bytes_per_pixel;
- imagesize = h * rowbytes;
- frameRaw.pixels.resize(imagesize);
+ size_t rowbytes = w * bytes_per_pixel;
+ if (h > std::numeric_limits<size_t>::max() / rowbytes) {
+ return JXL_FAILURE("Image too big.");
+ }
+ size_t imagesize = h * rowbytes;
+ JXL_RETURN_IF_ERROR(frameRaw.Resize(imagesize));
frameRaw.rows.resize(h);
- for (j = 0; j < h; j++)
- frameRaw.rows[j] = frameRaw.pixels.data() + j * rowbytes;
+ for (size_t j = 0; j < h; j++) {
+ frameRaw.rows[j] =
+ reinterpret_cast<uint8_t*>(frameRaw.pixels.get()) +
+ j * rowbytes;
+ }
if (processing_data(png_ptr, info_ptr, chunk.data(), chunk.size())) {
break;
@@ -813,6 +837,8 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes,
have_cicp = true;
have_color = true;
ppf->icc.clear();
+ ppf->primary_color_representation =
+ PackedPixelFile::kColorEncodingIsPrimary;
}
} else if (!have_cicp && id == kId_iCCP) {
if (processing_data(png_ptr, info_ptr, chunk.data(), chunk.size())) {
@@ -830,6 +856,7 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes,
&profile, &proflen);
if (ok && proflen) {
ppf->icc.assign(profile, profile + proflen);
+ ppf->primary_color_representation = PackedPixelFile::kIccIsPrimary;
have_color = true;
have_iccp = true;
} else {
@@ -922,7 +949,8 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes,
py0 + pys >= y0 + ysize && use_for_next_frame) {
// If the new frame is contained within the old frame, we can pad the
// new frame with zeros and not blend.
- PackedImage new_data(pxs, pys, frame.data.format);
+ JXL_ASSIGN_OR_RETURN(PackedImage new_data,
+ PackedImage::Create(pxs, pys, frame.data.format));
memset(new_data.pixels(), 0, new_data.pixels_size);
for (size_t y = 0; y < ysize; y++) {
size_t bytes_per_pixel =
@@ -944,7 +972,8 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes,
ppf->frames.emplace_back(std::move(new_data));
} else {
// If all else fails, insert a placeholder blank frame with kReplace.
- PackedImage blank(pxs, pys, frame.data.format);
+ JXL_ASSIGN_OR_RETURN(PackedImage blank,
+ PackedImage::Create(pxs, pys, frame.data.format));
memset(blank.pixels(), 0, blank.pixels_size);
ppf->frames.emplace_back(std::move(blank));
auto& pframe = ppf->frames.back();
@@ -984,7 +1013,7 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes,
has_nontrivial_background && frame.dispose_op == DISPOSE_OP_BACKGROUND;
}
if (ppf->frames.empty()) return JXL_FAILURE("No frames decoded");
- ppf->frames.back().frame_info.is_last = true;
+ ppf->frames.back().frame_info.is_last = JXL_TRUE;
return true;
#else
diff --git a/third_party/jpeg-xl/lib/extras/dec/color_description.cc b/third_party/jpeg-xl/lib/extras/dec/color_description.cc
index 54f6aa4206..bf229632d0 100644
--- a/third_party/jpeg-xl/lib/extras/dec/color_description.cc
+++ b/third_party/jpeg-xl/lib/extras/dec/color_description.cc
@@ -9,6 +9,8 @@
#include <cmath>
+#include "lib/jxl/base/common.h"
+
namespace jxl {
namespace {
@@ -19,49 +21,46 @@ struct EnumName {
T value;
};
-const EnumName<JxlColorSpace> kJxlColorSpaceNames[] = {
- {"RGB", JXL_COLOR_SPACE_RGB},
- {"Gra", JXL_COLOR_SPACE_GRAY},
- {"XYB", JXL_COLOR_SPACE_XYB},
- {"CS?", JXL_COLOR_SPACE_UNKNOWN},
-};
-
-const EnumName<JxlWhitePoint> kJxlWhitePointNames[] = {
- {"D65", JXL_WHITE_POINT_D65},
- {"Cst", JXL_WHITE_POINT_CUSTOM},
- {"EER", JXL_WHITE_POINT_E},
- {"DCI", JXL_WHITE_POINT_DCI},
-};
-
-const EnumName<JxlPrimaries> kJxlPrimariesNames[] = {
- {"SRG", JXL_PRIMARIES_SRGB},
- {"Cst", JXL_PRIMARIES_CUSTOM},
- {"202", JXL_PRIMARIES_2100},
- {"DCI", JXL_PRIMARIES_P3},
-};
-
-const EnumName<JxlTransferFunction> kJxlTransferFunctionNames[] = {
- {"709", JXL_TRANSFER_FUNCTION_709},
- {"TF?", JXL_TRANSFER_FUNCTION_UNKNOWN},
- {"Lin", JXL_TRANSFER_FUNCTION_LINEAR},
- {"SRG", JXL_TRANSFER_FUNCTION_SRGB},
- {"PeQ", JXL_TRANSFER_FUNCTION_PQ},
- {"DCI", JXL_TRANSFER_FUNCTION_DCI},
- {"HLG", JXL_TRANSFER_FUNCTION_HLG},
- {"", JXL_TRANSFER_FUNCTION_GAMMA},
-};
-
-const EnumName<JxlRenderingIntent> kJxlRenderingIntentNames[] = {
- {"Per", JXL_RENDERING_INTENT_PERCEPTUAL},
- {"Rel", JXL_RENDERING_INTENT_RELATIVE},
- {"Sat", JXL_RENDERING_INTENT_SATURATION},
- {"Abs", JXL_RENDERING_INTENT_ABSOLUTE},
-};
-
-template <typename T>
-Status ParseEnum(const std::string& token, const EnumName<T>* enum_values,
- size_t enum_len, T* value) {
- for (size_t i = 0; i < enum_len; i++) {
+constexpr auto kJxlColorSpaceNames =
+ to_array<EnumName<JxlColorSpace>>({{"RGB", JXL_COLOR_SPACE_RGB},
+ {"Gra", JXL_COLOR_SPACE_GRAY},
+ {"XYB", JXL_COLOR_SPACE_XYB},
+ {"CS?", JXL_COLOR_SPACE_UNKNOWN}});
+
+constexpr auto kJxlWhitePointNames =
+ to_array<EnumName<JxlWhitePoint>>({{"D65", JXL_WHITE_POINT_D65},
+ {"Cst", JXL_WHITE_POINT_CUSTOM},
+ {"EER", JXL_WHITE_POINT_E},
+ {"DCI", JXL_WHITE_POINT_DCI}});
+
+constexpr auto kJxlPrimariesNames =
+ to_array<EnumName<JxlPrimaries>>({{"SRG", JXL_PRIMARIES_SRGB},
+ {"Cst", JXL_PRIMARIES_CUSTOM},
+ {"202", JXL_PRIMARIES_2100},
+ {"DCI", JXL_PRIMARIES_P3}});
+
+constexpr auto kJxlTransferFunctionNames =
+ to_array<EnumName<JxlTransferFunction>>(
+ {{"709", JXL_TRANSFER_FUNCTION_709},
+ {"TF?", JXL_TRANSFER_FUNCTION_UNKNOWN},
+ {"Lin", JXL_TRANSFER_FUNCTION_LINEAR},
+ {"SRG", JXL_TRANSFER_FUNCTION_SRGB},
+ {"PeQ", JXL_TRANSFER_FUNCTION_PQ},
+ {"DCI", JXL_TRANSFER_FUNCTION_DCI},
+ {"HLG", JXL_TRANSFER_FUNCTION_HLG},
+ {"", JXL_TRANSFER_FUNCTION_GAMMA}});
+
+constexpr auto kJxlRenderingIntentNames =
+ to_array<EnumName<JxlRenderingIntent>>(
+ {{"Per", JXL_RENDERING_INTENT_PERCEPTUAL},
+ {"Rel", JXL_RENDERING_INTENT_RELATIVE},
+ {"Sat", JXL_RENDERING_INTENT_SATURATION},
+ {"Abs", JXL_RENDERING_INTENT_ABSOLUTE}});
+
+template <typename T, size_t N>
+Status ParseEnum(const std::string& token,
+ const std::array<EnumName<T>, N>& enum_values, T* value) {
+ for (size_t i = 0; i < enum_values.size(); i++) {
if (enum_values[i].name == token) {
*value = enum_values[i].value;
return true;
@@ -69,9 +68,6 @@ Status ParseEnum(const std::string& token, const EnumName<T>* enum_values,
}
return false;
}
-#define ARRAY_SIZE(X) (sizeof(X) / sizeof((X)[0]))
-#define PARSE_ENUM(type, token, value) \
- ParseEnum<type>(token, k##type##Names, ARRAY_SIZE(k##type##Names), value)
class Tokenizer {
public:
@@ -122,7 +118,7 @@ Status ParseColorSpace(Tokenizer* tokenizer, JxlColorEncoding* c) {
std::string str;
JXL_RETURN_IF_ERROR(tokenizer->Next(&str));
JxlColorSpace cs;
- if (PARSE_ENUM(JxlColorSpace, str, &cs)) {
+ if (ParseEnum(str, kJxlColorSpaceNames, &cs)) {
c->color_space = cs;
return true;
}
@@ -139,7 +135,7 @@ Status ParseWhitePoint(Tokenizer* tokenizer, JxlColorEncoding* c) {
std::string str;
JXL_RETURN_IF_ERROR(tokenizer->Next(&str));
- if (PARSE_ENUM(JxlWhitePoint, str, &c->white_point)) return true;
+ if (ParseEnum(str, kJxlWhitePointNames, &c->white_point)) return true;
Tokenizer xy_tokenizer(&str, ';');
c->white_point = JXL_WHITE_POINT_CUSTOM;
@@ -157,7 +153,7 @@ Status ParsePrimaries(Tokenizer* tokenizer, JxlColorEncoding* c) {
std::string str;
JXL_RETURN_IF_ERROR(tokenizer->Next(&str));
- if (PARSE_ENUM(JxlPrimaries, str, &c->primaries)) return true;
+ if (ParseEnum(str, kJxlPrimariesNames, &c->primaries)) return true;
Tokenizer xy_tokenizer(&str, ';');
JXL_RETURN_IF_ERROR(ParseDouble(&xy_tokenizer, c->primaries_red_xy + 0));
@@ -174,7 +170,8 @@ Status ParsePrimaries(Tokenizer* tokenizer, JxlColorEncoding* c) {
Status ParseRenderingIntent(Tokenizer* tokenizer, JxlColorEncoding* c) {
std::string str;
JXL_RETURN_IF_ERROR(tokenizer->Next(&str));
- if (PARSE_ENUM(JxlRenderingIntent, str, &c->rendering_intent)) return true;
+ if (ParseEnum(str, kJxlRenderingIntentNames, &c->rendering_intent))
+ return true;
return JXL_FAILURE("Invalid RenderingIntent %s\n", str.c_str());
}
@@ -189,7 +186,7 @@ Status ParseTransferFunction(Tokenizer* tokenizer, JxlColorEncoding* c) {
std::string str;
JXL_RETURN_IF_ERROR(tokenizer->Next(&str));
- if (PARSE_ENUM(JxlTransferFunction, str, &c->transfer_function)) {
+ if (ParseEnum(str, kJxlTransferFunctionNames, &c->transfer_function)) {
return true;
}
diff --git a/third_party/jpeg-xl/lib/extras/dec/color_hints.cc b/third_party/jpeg-xl/lib/extras/dec/color_hints.cc
index 5c6d7b84a0..b1edd9f6ea 100644
--- a/third_party/jpeg-xl/lib/extras/dec/color_hints.cc
+++ b/third_party/jpeg-xl/lib/extras/dec/color_hints.cc
@@ -43,20 +43,21 @@ Status ApplyColorHints(const ColorHints& color_hints,
} else if (key == "icc") {
const uint8_t* data = reinterpret_cast<const uint8_t*>(value.data());
std::vector<uint8_t> icc(data, data + value.size());
- ppf->icc.swap(icc);
+ ppf->icc = std::move(icc);
+ ppf->primary_color_representation = PackedPixelFile::kIccIsPrimary;
got_color_space = true;
} else if (key == "exif") {
const uint8_t* data = reinterpret_cast<const uint8_t*>(value.data());
std::vector<uint8_t> blob(data, data + value.size());
- ppf->metadata.exif.swap(blob);
+ ppf->metadata.exif = std::move(blob);
} else if (key == "xmp") {
const uint8_t* data = reinterpret_cast<const uint8_t*>(value.data());
std::vector<uint8_t> blob(data, data + value.size());
- ppf->metadata.xmp.swap(blob);
+ ppf->metadata.xmp = std::move(blob);
} else if (key == "jumbf") {
const uint8_t* data = reinterpret_cast<const uint8_t*>(value.data());
std::vector<uint8_t> blob(data, data + value.size());
- ppf->metadata.jumbf.swap(blob);
+ ppf->metadata.jumbf = std::move(blob);
} else {
JXL_WARNING("Ignoring %s hint", key.c_str());
}
diff --git a/third_party/jpeg-xl/lib/extras/dec/decode.cc b/third_party/jpeg-xl/lib/extras/dec/decode.cc
index b3ca711bb2..3546cb65c0 100644
--- a/third_party/jpeg-xl/lib/extras/dec/decode.cc
+++ b/third_party/jpeg-xl/lib/extras/dec/decode.cc
@@ -35,7 +35,8 @@ std::string GetExtension(const std::string& path) {
} // namespace
-Codec CodecFromPath(std::string path, size_t* JXL_RESTRICT bits_per_sample,
+Codec CodecFromPath(const std::string& path,
+ size_t* JXL_RESTRICT bits_per_sample,
std::string* extension) {
std::string ext = GetExtension(path);
if (extension) {
@@ -98,7 +99,7 @@ Status DecodeBytes(const Span<const uint8_t> bytes,
*ppf = extras::PackedPixelFile();
// Default values when not set by decoders.
- ppf->info.uses_original_profile = true;
+ ppf->info.uses_original_profile = JXL_TRUE;
ppf->info.orientation = JXL_ORIENT_IDENTITY;
const auto choose_codec = [&]() -> Codec {
@@ -116,6 +117,7 @@ Status DecodeBytes(const Span<const uint8_t> bytes,
dparams.accepted_formats.push_back(
{num_channels, JXL_TYPE_FLOAT, JXL_LITTLE_ENDIAN, /*align=*/0});
}
+ dparams.output_bitdepth.type = JXL_BIT_DEPTH_FROM_CODESTREAM;
size_t decoded_bytes;
if (DecodeImageJXL(bytes.data(), bytes.size(), dparams, &decoded_bytes,
ppf) &&
diff --git a/third_party/jpeg-xl/lib/extras/dec/decode.h b/third_party/jpeg-xl/lib/extras/dec/decode.h
index 0d7dfcbef2..1a90f4c6a3 100644
--- a/third_party/jpeg-xl/lib/extras/dec/decode.h
+++ b/third_party/jpeg-xl/lib/extras/dec/decode.h
@@ -40,7 +40,7 @@ bool CanDecode(Codec codec);
// If and only if extension is ".pfm", *bits_per_sample is updated to 32 so
// that Encode() would encode to PFM instead of PPM.
-Codec CodecFromPath(std::string path,
+Codec CodecFromPath(const std::string& path,
size_t* JXL_RESTRICT bits_per_sample = nullptr,
std::string* extension = nullptr);
diff --git a/third_party/jpeg-xl/lib/extras/dec/exr.cc b/third_party/jpeg-xl/lib/extras/dec/exr.cc
index 821e0f4b21..d5903155e9 100644
--- a/third_party/jpeg-xl/lib/extras/dec/exr.cc
+++ b/third_party/jpeg-xl/lib/extras/dec/exr.cc
@@ -121,7 +121,12 @@ Status DecodeImageEXR(Span<const uint8_t> bytes, const ColorHints& color_hints,
};
ppf->frames.clear();
// Allocates the frame buffer.
- ppf->frames.emplace_back(image_size.x, image_size.y, format);
+ {
+ JXL_ASSIGN_OR_RETURN(
+ PackedFrame frame,
+ PackedFrame::Create(image_size.x, image_size.y, format));
+ ppf->frames.emplace_back(std::move(frame));
+ }
const auto& frame = ppf->frames.back();
const int row_size = input.dataWindow().size().x + 1;
@@ -188,7 +193,7 @@ Status DecodeImageEXR(Span<const uint8_t> bytes, const ColorHints& color_hints,
if (has_alpha) {
ppf->info.alpha_bits = kExrAlphaBits;
ppf->info.alpha_exponent_bits = ppf->info.exponent_bits_per_sample;
- ppf->info.alpha_premultiplied = true;
+ ppf->info.alpha_premultiplied = JXL_TRUE;
}
ppf->info.intensity_target = intensity_target;
return true;
diff --git a/third_party/jpeg-xl/lib/extras/dec/gif.cc b/third_party/jpeg-xl/lib/extras/dec/gif.cc
index 3d963941c0..3f89d460b8 100644
--- a/third_party/jpeg-xl/lib/extras/dec/gif.cc
+++ b/third_party/jpeg-xl/lib/extras/dec/gif.cc
@@ -50,8 +50,10 @@ void ensure_have_alpha(PackedFrame* frame) {
/*endianness=*/JXL_NATIVE_ENDIAN,
/*align=*/0,
};
- frame->extra_channels.emplace_back(frame->color.xsize, frame->color.ysize,
- alpha_format);
+ JXL_ASSIGN_OR_DIE(PackedImage image,
+ PackedImage::Create(frame->color.xsize, frame->color.ysize,
+ alpha_format));
+ frame->extra_channels.emplace_back(std::move(image));
// We need to set opaque-by-default.
std::fill_n(static_cast<uint8_t*>(frame->extra_channels[0].pixels()),
frame->color.xsize * frame->color.ysize, 255u);
@@ -136,7 +138,7 @@ Status DecodeImageGIF(Span<const uint8_t> bytes, const ColorHints& color_hints,
}
if (gif->ImageCount > 1) {
- ppf->info.have_animation = true;
+ ppf->info.have_animation = JXL_TRUE;
// Delays in GIF are specified in 100ths of a second.
ppf->info.animation.tps_numerator = 100;
ppf->info.animation.tps_denominator = 1;
@@ -186,7 +188,9 @@ Status DecodeImageGIF(Span<const uint8_t> bytes, const ColorHints& color_hints,
}
const PackedRgba background_rgba{background_color.Red, background_color.Green,
background_color.Blue, 0};
- PackedFrame canvas(gif->SWidth, gif->SHeight, canvas_format);
+ JXL_ASSIGN_OR_RETURN(
+ PackedFrame canvas,
+ PackedFrame::Create(gif->SWidth, gif->SHeight, canvas_format));
std::fill_n(static_cast<PackedRgba*>(canvas.color.pixels()),
canvas.color.xsize * canvas.color.ysize, background_rgba);
Rect canvas_rect{0, 0, canvas.color.xsize, canvas.color.ysize};
@@ -230,8 +234,14 @@ Status DecodeImageGIF(Span<const uint8_t> bytes, const ColorHints& color_hints,
}
// Allocates the frame buffer.
- ppf->frames.emplace_back(total_rect.xsize(), total_rect.ysize(),
- packed_frame_format);
+ {
+ JXL_ASSIGN_OR_RETURN(
+ PackedFrame frame,
+ PackedFrame::Create(total_rect.xsize(), total_rect.ysize(),
+ packed_frame_format));
+ ppf->frames.emplace_back(std::move(frame));
+ }
+
PackedFrame* frame = &ppf->frames.back();
// We cannot tell right from the start whether there will be a
@@ -301,8 +311,10 @@ Status DecodeImageGIF(Span<const uint8_t> bytes, const ColorHints& color_hints,
}
// Update the canvas by creating a copy first.
- PackedImage new_canvas_image(canvas.color.xsize, canvas.color.ysize,
- canvas.color.format);
+ JXL_ASSIGN_OR_RETURN(
+ PackedImage new_canvas_image,
+ PackedImage::Create(canvas.color.xsize, canvas.color.ysize,
+ canvas.color.format));
memcpy(new_canvas_image.pixels(), canvas.color.pixels(),
new_canvas_image.pixels_size);
for (size_t y = 0, byte_index = 0; y < image_rect.ysize(); ++y) {
diff --git a/third_party/jpeg-xl/lib/extras/dec/jpegli.cc b/third_party/jpeg-xl/lib/extras/dec/jpegli.cc
index ffa1b79c25..443dfe86ba 100644
--- a/third_party/jpeg-xl/lib/extras/dec/jpegli.cc
+++ b/third_party/jpeg-xl/lib/extras/dec/jpegli.cc
@@ -27,7 +27,7 @@ constexpr unsigned char kExifSignature[6] = {0x45, 0x78, 0x69,
constexpr int kExifMarker = JPEG_APP0 + 1;
constexpr int kICCMarker = JPEG_APP0 + 2;
-static inline bool IsJPG(const std::vector<uint8_t>& bytes) {
+inline bool IsJPG(const std::vector<uint8_t>& bytes) {
if (bytes.size() < 2) return false;
if (bytes[0] != 0xFF || bytes[1] != 0xD8) return false;
return true;
@@ -188,7 +188,11 @@ Status DecodeJpeg(const std::vector<uint8_t>& compressed,
} else if (dparams.force_grayscale) {
cinfo.out_color_space = JCS_GRAYSCALE;
}
- if (!ReadICCProfile(&cinfo, &ppf->icc)) {
+ if (ReadICCProfile(&cinfo, &ppf->icc)) {
+ ppf->primary_color_representation = PackedPixelFile::kIccIsPrimary;
+ } else {
+ ppf->primary_color_representation =
+ PackedPixelFile::kColorEncodingIsPrimary;
ppf->icc.clear();
// Default to SRGB
ppf->color_encoding.color_space =
@@ -214,7 +218,7 @@ Status DecodeJpeg(const std::vector<uint8_t>& compressed,
} else {
return failure("unsupported data type");
}
- ppf->info.uses_original_profile = true;
+ ppf->info.uses_original_profile = JXL_TRUE;
// No alpha in JPG
ppf->info.alpha_bits = 0;
@@ -227,8 +231,8 @@ Status DecodeJpeg(const std::vector<uint8_t>& compressed,
if (dparams.num_colors > 0) {
cinfo.quantize_colors = TRUE;
cinfo.desired_number_of_colors = dparams.num_colors;
- cinfo.two_pass_quantize = dparams.two_pass_quant;
- cinfo.dither_mode = (J_DITHER_MODE)dparams.dither_mode;
+ cinfo.two_pass_quantize = static_cast<boolean>(dparams.two_pass_quant);
+ cinfo.dither_mode = static_cast<J_DITHER_MODE>(dparams.dither_mode);
}
jpegli_start_decompress(&cinfo);
@@ -242,7 +246,12 @@ Status DecodeJpeg(const std::vector<uint8_t>& compressed,
};
ppf->frames.clear();
// Allocates the frame buffer.
- ppf->frames.emplace_back(cinfo.image_width, cinfo.image_height, format);
+ {
+ JXL_ASSIGN_OR_RETURN(
+ PackedFrame frame,
+ PackedFrame::Create(cinfo.image_width, cinfo.image_height, format));
+ ppf->frames.emplace_back(std::move(frame));
+ }
const auto& frame = ppf->frames.back();
JXL_ASSERT(sizeof(JSAMPLE) * cinfo.out_color_components *
cinfo.image_width <=
diff --git a/third_party/jpeg-xl/lib/extras/dec/jpg.cc b/third_party/jpeg-xl/lib/extras/dec/jpg.cc
index 3c8a4bccfe..4a3e0d3b21 100644
--- a/third_party/jpeg-xl/lib/extras/dec/jpg.cc
+++ b/third_party/jpeg-xl/lib/extras/dec/jpg.cc
@@ -34,7 +34,7 @@ constexpr unsigned char kExifSignature[6] = {0x45, 0x78, 0x69,
0x66, 0x00, 0x00};
constexpr int kExifMarker = JPEG_APP0 + 1;
-static inline bool IsJPG(const Span<const uint8_t> bytes) {
+inline bool IsJPG(const Span<const uint8_t> bytes) {
if (bytes.size() < 2) return false;
if (bytes[0] != 0xFF || bytes[1] != 0xD8) return false;
return true;
@@ -242,7 +242,11 @@ Status DecodeImageJPG(const Span<const uint8_t> bytes,
if (nbcomp != 1 && nbcomp != 3) {
return failure("unsupported number of components in JPEG");
}
- if (!ReadICCProfile(&cinfo, &ppf->icc)) {
+ if (ReadICCProfile(&cinfo, &ppf->icc)) {
+ ppf->primary_color_representation = PackedPixelFile::kIccIsPrimary;
+ } else {
+ ppf->primary_color_representation =
+ PackedPixelFile::kColorEncodingIsPrimary;
ppf->icc.clear();
// Default to SRGB
// Actually, (cinfo.output_components == nbcomp) will be checked after
@@ -266,7 +270,7 @@ Status DecodeImageJPG(const Span<const uint8_t> bytes,
ppf->info.bits_per_sample = BITS_IN_JSAMPLE;
JXL_ASSERT(BITS_IN_JSAMPLE == 8 || BITS_IN_JSAMPLE == 16);
ppf->info.exponent_bits_per_sample = 0;
- ppf->info.uses_original_profile = true;
+ ppf->info.uses_original_profile = JXL_TRUE;
// No alpha in JPG
ppf->info.alpha_bits = 0;
@@ -278,8 +282,8 @@ Status DecodeImageJPG(const Span<const uint8_t> bytes,
if (dparams && dparams->num_colors > 0) {
cinfo.quantize_colors = TRUE;
cinfo.desired_number_of_colors = dparams->num_colors;
- cinfo.two_pass_quantize = dparams->two_pass_quant;
- cinfo.dither_mode = (J_DITHER_MODE)dparams->dither_mode;
+ cinfo.two_pass_quantize = static_cast<boolean>(dparams->two_pass_quant);
+ cinfo.dither_mode = static_cast<J_DITHER_MODE>(dparams->dither_mode);
}
jpeg_start_decompress(&cinfo);
@@ -295,19 +299,25 @@ Status DecodeImageJPG(const Span<const uint8_t> bytes,
};
ppf->frames.clear();
// Allocates the frame buffer.
- ppf->frames.emplace_back(cinfo.image_width, cinfo.image_height, format);
+ {
+ JXL_ASSIGN_OR_RETURN(
+ PackedFrame frame,
+ PackedFrame::Create(cinfo.image_width, cinfo.image_height, format));
+ ppf->frames.emplace_back(std::move(frame));
+ }
const auto& frame = ppf->frames.back();
JXL_ASSERT(sizeof(JSAMPLE) * cinfo.out_color_components *
cinfo.image_width <=
frame.color.stride);
if (cinfo.quantize_colors) {
- jxl::msan::UnpoisonMemory(cinfo.colormap, cinfo.out_color_components *
- sizeof(cinfo.colormap[0]));
+ JSAMPLE** colormap = cinfo.colormap;
+ jxl::msan::UnpoisonMemory(reinterpret_cast<void*>(colormap),
+ cinfo.out_color_components * sizeof(JSAMPLE*));
for (int c = 0; c < cinfo.out_color_components; ++c) {
jxl::msan::UnpoisonMemory(
- cinfo.colormap[c],
- cinfo.actual_number_of_colors * sizeof(cinfo.colormap[c][0]));
+ reinterpret_cast<void*>(colormap[c]),
+ cinfo.actual_number_of_colors * sizeof(JSAMPLE));
}
}
for (size_t y = 0; y < cinfo.image_height; ++y) {
diff --git a/third_party/jpeg-xl/lib/extras/dec/jxl.cc b/third_party/jpeg-xl/lib/extras/dec/jxl.cc
index f3e62c970a..5b7fa03f02 100644
--- a/third_party/jpeg-xl/lib/extras/dec/jxl.cc
+++ b/third_party/jpeg-xl/lib/extras/dec/jxl.cc
@@ -16,20 +16,23 @@
#include "lib/extras/dec/color_description.h"
#include "lib/jxl/base/exif.h"
#include "lib/jxl/base/printf_macros.h"
+#include "lib/jxl/base/status.h"
namespace jxl {
namespace extras {
namespace {
struct BoxProcessor {
- BoxProcessor(JxlDecoder* dec) : dec_(dec) { Reset(); }
+ explicit BoxProcessor(JxlDecoder* dec) : dec_(dec) { Reset(); }
void InitializeOutput(std::vector<uint8_t>* out) {
+ JXL_ASSERT(out != nullptr);
box_data_ = out;
AddMoreOutput();
}
bool AddMoreOutput() {
+ JXL_ASSERT(box_data_ != nullptr);
Flush();
static const size_t kBoxOutputChunkSize = 1 << 16;
box_data_->resize(box_data_->size() + kBoxOutputChunkSize);
@@ -126,7 +129,7 @@ bool DecodeImageJXL(const uint8_t* bytes, size_t bytes_size,
return false;
}
- JxlPixelFormat format;
+ JxlPixelFormat format = {}; // Initialize to calm down clang-tidy.
std::vector<JxlPixelFormat> accepted_formats = dparams.accepted_formats;
JxlColorEncoding color_encoding;
@@ -177,18 +180,18 @@ bool DecodeImageJXL(const uint8_t* bytes, size_t bytes_size,
return false;
}
if (jpeg_bytes == nullptr) {
- if (JXL_DEC_SUCCESS !=
- JxlDecoderSetRenderSpotcolors(dec, dparams.render_spotcolors)) {
+ if (JXL_DEC_SUCCESS != JxlDecoderSetRenderSpotcolors(
+ dec, TO_JXL_BOOL(dparams.render_spotcolors))) {
fprintf(stderr, "JxlDecoderSetRenderSpotColors failed\n");
return false;
}
- if (JXL_DEC_SUCCESS !=
- JxlDecoderSetKeepOrientation(dec, dparams.keep_orientation)) {
+ if (JXL_DEC_SUCCESS != JxlDecoderSetKeepOrientation(
+ dec, TO_JXL_BOOL(dparams.keep_orientation))) {
fprintf(stderr, "JxlDecoderSetKeepOrientation failed\n");
return false;
}
- if (JXL_DEC_SUCCESS !=
- JxlDecoderSetUnpremultiplyAlpha(dec, dparams.unpremultiply_alpha)) {
+ if (JXL_DEC_SUCCESS != JxlDecoderSetUnpremultiplyAlpha(
+ dec, TO_JXL_BOOL(dparams.unpremultiply_alpha))) {
fprintf(stderr, "JxlDecoderSetUnpremultiplyAlpha failed\n");
return false;
}
@@ -267,6 +270,7 @@ bool DecodeImageJXL(const uint8_t* bytes, size_t bytes_size,
return false;
}
} else if (status == JXL_DEC_JPEG_NEED_MORE_OUTPUT) {
+ JXL_ASSERT(jpeg_bytes != nullptr); // Help clang-tidy.
// Decoded a chunk to JPEG.
size_t used_jpeg_output =
jpeg_data_chunk.size() - JxlDecoderReleaseJPEGBuffer(dec);
@@ -309,7 +313,7 @@ bool DecodeImageJXL(const uint8_t* bytes, size_t bytes_size,
} else {
if (dparams.unpremultiply_alpha) {
// Mark in the basic info that alpha was unpremultiplied.
- ppf->info.alpha_premultiplied = false;
+ ppf->info.alpha_premultiplied = JXL_FALSE;
}
}
bool alpha_found = false;
@@ -327,7 +331,8 @@ bool DecodeImageJXL(const uint8_t* bytes, size_t bytes_size,
}
std::string name(eci.name_length + 1, 0);
if (JXL_DEC_SUCCESS !=
- JxlDecoderGetExtraChannelName(dec, i, &name[0], name.size())) {
+ JxlDecoderGetExtraChannelName(
+ dec, i, const_cast<char*>(name.data()), name.size())) {
fprintf(stderr, "JxlDecoderGetExtraChannelName failed\n");
return false;
}
@@ -351,24 +356,26 @@ bool DecodeImageJXL(const uint8_t* bytes, size_t bytes_size,
}
size_t icc_size = 0;
JxlColorProfileTarget target = JXL_COLOR_PROFILE_TARGET_DATA;
- ppf->color_encoding.color_space = JXL_COLOR_SPACE_UNKNOWN;
- if (JXL_DEC_SUCCESS != JxlDecoderGetColorAsEncodedProfile(
- dec, target, &ppf->color_encoding) ||
- dparams.need_icc) {
- // only get ICC if it is not an Enum color encoding
- if (JXL_DEC_SUCCESS !=
- JxlDecoderGetICCProfileSize(dec, target, &icc_size)) {
- fprintf(stderr, "JxlDecoderGetICCProfileSize failed\n");
- }
- if (icc_size != 0) {
- ppf->icc.resize(icc_size);
- if (JXL_DEC_SUCCESS != JxlDecoderGetColorAsICCProfile(
- dec, target, ppf->icc.data(), icc_size)) {
- fprintf(stderr, "JxlDecoderGetColorAsICCProfile failed\n");
- return false;
- }
+ if (JXL_DEC_SUCCESS !=
+ JxlDecoderGetICCProfileSize(dec, target, &icc_size)) {
+ fprintf(stderr, "JxlDecoderGetICCProfileSize failed\n");
+ }
+ if (icc_size != 0) {
+ ppf->primary_color_representation = PackedPixelFile::kIccIsPrimary;
+ ppf->icc.resize(icc_size);
+ if (JXL_DEC_SUCCESS != JxlDecoderGetColorAsICCProfile(
+ dec, target, ppf->icc.data(), icc_size)) {
+ fprintf(stderr, "JxlDecoderGetColorAsICCProfile failed\n");
+ return false;
}
}
+ if (JXL_DEC_SUCCESS == JxlDecoderGetColorAsEncodedProfile(
+ dec, target, &ppf->color_encoding)) {
+ ppf->primary_color_representation =
+ PackedPixelFile::kColorEncodingIsPrimary;
+ } else {
+ ppf->color_encoding.color_space = JXL_COLOR_SPACE_UNKNOWN;
+ }
icc_size = 0;
target = JXL_COLOR_PROFILE_TARGET_ORIGINAL;
if (JXL_DEC_SUCCESS !=
@@ -385,14 +392,21 @@ bool DecodeImageJXL(const uint8_t* bytes, size_t bytes_size,
}
}
} else if (status == JXL_DEC_FRAME) {
- jxl::extras::PackedFrame frame(ppf->info.xsize, ppf->info.ysize, format);
+ auto frame_or = jxl::extras::PackedFrame::Create(ppf->info.xsize,
+ ppf->info.ysize, format);
+ if (!frame_or.status()) {
+ fprintf(stderr, "Failed to create image frame.");
+ return false;
+ }
+ jxl::extras::PackedFrame frame = std::move(frame_or).value();
if (JXL_DEC_SUCCESS != JxlDecoderGetFrameHeader(dec, &frame.frame_info)) {
fprintf(stderr, "JxlDecoderGetFrameHeader failed\n");
return false;
}
frame.name.resize(frame.frame_info.name_length + 1, 0);
if (JXL_DEC_SUCCESS !=
- JxlDecoderGetFrameName(dec, &frame.name[0], frame.name.size())) {
+ JxlDecoderGetFrameName(dec, const_cast<char*>(frame.name.data()),
+ frame.name.size())) {
fprintf(stderr, "JxlDecoderGetFrameName failed\n");
return false;
}
@@ -423,9 +437,16 @@ bool DecodeImageJXL(const uint8_t* bytes, size_t bytes_size,
fprintf(stderr, "JxlDecoderPreviewOutBufferSize failed\n");
return false;
}
+ auto preview_image_or = jxl::extras::PackedImage::Create(
+ ppf->info.preview.xsize, ppf->info.preview.ysize, format);
+ if (!preview_image_or.status()) {
+ fprintf(stderr, "Failed to create preview image\n");
+ return false;
+ }
+ jxl::extras::PackedImage preview_image =
+ std::move(preview_image_or).value();
ppf->preview_frame = std::unique_ptr<jxl::extras::PackedFrame>(
- new jxl::extras::PackedFrame(ppf->info.preview.xsize,
- ppf->info.preview.ysize, format));
+ new jxl::extras::PackedFrame(std::move(preview_image)));
if (buffer_size != ppf->preview_frame->color.pixels_size) {
fprintf(stderr, "Invalid out buffer size %" PRIuS " %" PRIuS "\n",
buffer_size, ppf->preview_frame->color.pixels_size);
@@ -492,8 +513,13 @@ bool DecodeImageJXL(const uint8_t* bytes, size_t bytes_size,
JxlPixelFormat ec_format = format;
ec_format.num_channels = 1;
for (auto& eci : ppf->extra_channels_info) {
- frame.extra_channels.emplace_back(jxl::extras::PackedImage(
- ppf->info.xsize, ppf->info.ysize, ec_format));
+ auto image = jxl::extras::PackedImage::Create(
+ ppf->info.xsize, ppf->info.ysize, ec_format);
+ if (!image.status()) {
+ fprintf(stderr, "Failed to create extra channel image\n");
+ return false;
+ }
+ frame.extra_channels.emplace_back(std::move(image).value());
auto& ec = frame.extra_channels.back();
size_t buffer_size;
if (JXL_DEC_SUCCESS != JxlDecoderExtraChannelBufferSize(
diff --git a/third_party/jpeg-xl/lib/extras/dec/jxl.h b/third_party/jpeg-xl/lib/extras/dec/jxl.h
index cbada1f6dd..5f4ed7f683 100644
--- a/third_party/jpeg-xl/lib/extras/dec/jxl.h
+++ b/third_party/jpeg-xl/lib/extras/dec/jxl.h
@@ -41,10 +41,6 @@ struct JXLDecompressParams {
// Whether truncated input should be treated as an error.
bool allow_partial_input = false;
- // Set to true if an ICC profile has to be synthesized for Enum color
- // encodings
- bool need_icc = false;
-
// How many passes to decode at most. By default, decode everything.
uint32_t max_passes = std::numeric_limits<uint32_t>::max();
diff --git a/third_party/jpeg-xl/lib/extras/dec/pgx.cc b/third_party/jpeg-xl/lib/extras/dec/pgx.cc
index a99eb0f4ee..4499069d77 100644
--- a/third_party/jpeg-xl/lib/extras/dec/pgx.cc
+++ b/third_party/jpeg-xl/lib/extras/dec/pgx.cc
@@ -150,7 +150,7 @@ Status DecodeImagePGX(const Span<const uint8_t> bytes,
const SizeConstraints* constraints) {
Parser parser(bytes);
HeaderPGX header = {};
- const uint8_t* pos;
+ const uint8_t* pos = nullptr;
if (!parser.ParseHeader(&header, &pos)) return false;
JXL_RETURN_IF_ERROR(
VerifyDimensions(constraints, header.xsize, header.ysize));
@@ -165,7 +165,7 @@ Status DecodeImagePGX(const Span<const uint8_t> bytes,
// Original data is uint, so exponent_bits_per_sample = 0.
ppf->info.bits_per_sample = header.bits_per_sample;
ppf->info.exponent_bits_per_sample = 0;
- ppf->info.uses_original_profile = true;
+ ppf->info.uses_original_profile = JXL_TRUE;
// No alpha in PGX
ppf->info.alpha_bits = 0;
@@ -188,7 +188,12 @@ Status DecodeImagePGX(const Span<const uint8_t> bytes,
};
ppf->frames.clear();
// Allocates the frame buffer.
- ppf->frames.emplace_back(header.xsize, header.ysize, format);
+ {
+ JXL_ASSIGN_OR_RETURN(
+ PackedFrame frame,
+ PackedFrame::Create(header.xsize, header.ysize, format));
+ ppf->frames.emplace_back(std::move(frame));
+ }
const auto& frame = ppf->frames.back();
size_t pgx_remaining_size = bytes.data() + bytes.size() - pos;
if (pgx_remaining_size < frame.color.pixels_size) {
diff --git a/third_party/jpeg-xl/lib/extras/dec/pnm.cc b/third_party/jpeg-xl/lib/extras/dec/pnm.cc
index 4c4618d41d..040c0bff81 100644
--- a/third_party/jpeg-xl/lib/extras/dec/pnm.cc
+++ b/third_party/jpeg-xl/lib/extras/dec/pnm.cc
@@ -9,6 +9,7 @@
#include <string.h>
#include <cmath>
+#include <cstdint>
#include <mutex>
#include "jxl/encode.h"
@@ -55,8 +56,10 @@ class Parser {
case 'f':
header->is_gray = true;
return ParseHeaderPFM(header, pos);
+
+ default:
+ return false;
}
- return false;
}
// Exposed for testing
@@ -160,11 +163,12 @@ class Parser {
Status MatchString(const char* keyword, bool skipws = true) {
const uint8_t* ppos = pos_;
- while (*keyword) {
+ const uint8_t* kw = reinterpret_cast<const uint8_t*>(keyword);
+ while (*kw) {
if (ppos >= end_) return JXL_FAILURE("PAM: unexpected end of input");
- if (*keyword != *ppos) return false;
+ if (*kw != *ppos) return false;
ppos++;
- keyword++;
+ kw++;
}
pos_ = ppos;
if (skipws) {
@@ -387,8 +391,8 @@ StatusOr<ChunkedPNMDecoder> ChunkedPNMDecoder::Init(const char* path) {
const size_t num_channels = dec.header_.is_gray ? 1 : 3;
const size_t bytes_per_pixel = num_channels * bytes_per_channel;
size_t row_size = dec.header_.xsize * bytes_per_pixel;
- if (header.ysize * row_size + dec.data_start_ < size) {
- return JXL_FAILURE("Invalid ppm");
+ if (size < header.ysize * row_size + dec.data_start_) {
+ return JXL_FAILURE("PNM file too small");
}
return dec;
}
@@ -495,10 +499,18 @@ Status DecodeImagePNM(const Span<const uint8_t> bytes,
};
const JxlPixelFormat ec_format{1, format.data_type, format.endianness, 0};
ppf->frames.clear();
- ppf->frames.emplace_back(header.xsize, header.ysize, format);
+ {
+ JXL_ASSIGN_OR_RETURN(
+ PackedFrame frame,
+ PackedFrame::Create(header.xsize, header.ysize, format));
+ ppf->frames.emplace_back(std::move(frame));
+ }
auto* frame = &ppf->frames.back();
for (size_t i = 0; i < header.ec_types.size(); ++i) {
- frame->extra_channels.emplace_back(header.xsize, header.ysize, ec_format);
+ JXL_ASSIGN_OR_RETURN(
+ PackedImage ec,
+ PackedImage::Create(header.xsize, header.ysize, ec_format));
+ frame->extra_channels.emplace_back(std::move(ec));
}
size_t pnm_remaining_size = bytes.data() + bytes.size() - pos;
if (pnm_remaining_size < frame->color.pixels_size) {
@@ -533,6 +545,9 @@ Status DecodeImagePNM(const Span<const uint8_t> bytes,
}
}
}
+ if (ppf->info.exponent_bits_per_sample == 0) {
+ ppf->input_bitdepth.type = JXL_BIT_DEPTH_FROM_CODESTREAM;
+ }
return true;
}
diff --git a/third_party/jpeg-xl/lib/extras/enc/apng.cc b/third_party/jpeg-xl/lib/extras/enc/apng.cc
index 413a9c8081..40aa876e84 100644
--- a/third_party/jpeg-xl/lib/extras/enc/apng.cc
+++ b/third_party/jpeg-xl/lib/extras/enc/apng.cc
@@ -86,7 +86,7 @@ class APNGEncoder : public Encoder {
std::vector<uint8_t>* bytes) const;
};
-static void PngWrite(png_structp png_ptr, png_bytep data, png_size_t length) {
+void PngWrite(png_structp png_ptr, png_bytep data, png_size_t length) {
std::vector<uint8_t>* bytes =
static_cast<std::vector<uint8_t>*>(png_get_io_ptr(png_ptr));
bytes->insert(bytes->end(), data, data + length);
@@ -137,7 +137,7 @@ class BlobsWriterPNG {
std::vector<std::string>* strings) {
// Encoding: base16 with newline after 72 chars.
const size_t base16_size =
- 2 * bytes.size() + DivCeil(bytes.size(), size_t(36)) + 1;
+ 2 * bytes.size() + DivCeil(bytes.size(), static_cast<size_t>(36)) + 1;
std::string base16;
base16.reserve(base16_size);
for (size_t i = 0; i < bytes.size(); ++i) {
@@ -155,7 +155,7 @@ class BlobsWriterPNG {
snprintf(header, sizeof(header), "\n%s\n%8" PRIuS, type.c_str(),
bytes.size());
- strings->push_back(std::string(key));
+ strings->emplace_back(key);
strings->push_back(std::string(header) + base16);
return true;
}
@@ -303,7 +303,7 @@ Status APNGEncoder::EncodePackedPixelFileToAPNG(
out[i] = static_cast<uint8_t>(in[i] * mul + 0.5);
}
} else {
- memcpy(&out[0], in, out_size);
+ memcpy(out.data(), in, out_size);
}
} else if (format.data_type == JXL_TYPE_UINT16) {
if (ppf.info.bits_per_sample < 16 ||
@@ -317,20 +317,21 @@ Status APNGEncoder::EncodePackedPixelFileToAPNG(
StoreBE16(static_cast<uint32_t>(val * mul + 0.5), p_out);
}
} else {
- memcpy(&out[0], in, out_size);
+ memcpy(out.data(), in, out_size);
}
}
png_structp png_ptr;
png_infop info_ptr;
- png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr,
+ nullptr);
if (!png_ptr) return JXL_FAILURE("Could not init png encoder");
info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr) return JXL_FAILURE("Could not init png info struct");
- png_set_write_fn(png_ptr, bytes, PngWrite, NULL);
+ png_set_write_fn(png_ptr, bytes, PngWrite, nullptr);
png_set_flush(png_ptr, 0);
int width = xsize;
@@ -344,11 +345,13 @@ Status APNGEncoder::EncodePackedPixelFileToAPNG(
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
PNG_FILTER_TYPE_BASE);
if (count == 0) {
- if (!ppf.icc.empty()) {
- png_set_benign_errors(png_ptr, 1);
- png_set_iCCP(png_ptr, info_ptr, "1", 0, ppf.icc.data(), ppf.icc.size());
- } else if (!MaybeAddSRGB(ppf.color_encoding, png_ptr, info_ptr)) {
+ if (!MaybeAddSRGB(ppf.color_encoding, png_ptr, info_ptr)) {
MaybeAddCICP(ppf.color_encoding, png_ptr, info_ptr);
+ if (!ppf.icc.empty()) {
+ png_set_benign_errors(png_ptr, 1);
+ png_set_iCCP(png_ptr, info_ptr, "1", 0, ppf.icc.data(),
+ ppf.icc.size());
+ }
MaybeAddCHRM(ppf.color_encoding, png_ptr, info_ptr);
MaybeAddGAMA(ppf.color_encoding, png_ptr, info_ptr);
}
@@ -404,7 +407,7 @@ Status APNGEncoder::EncodePackedPixelFileToAPNG(
png_write_flush(png_ptr);
const size_t pos = bytes->size();
- png_write_image(png_ptr, &rows[0]);
+ png_write_image(png_ptr, rows.data());
png_write_flush(png_ptr);
if (count > 0) {
std::vector<uint8_t> fdata(4);
@@ -428,7 +431,7 @@ Status APNGEncoder::EncodePackedPixelFileToAPNG(
count++;
if (count == ppf.frames.size() || !ppf.info.have_animation) {
- png_write_end(png_ptr, NULL);
+ png_write_end(png_ptr, nullptr);
}
png_destroy_write_struct(&png_ptr, &info_ptr);
diff --git a/third_party/jpeg-xl/lib/extras/enc/encode.cc b/third_party/jpeg-xl/lib/extras/enc/encode.cc
index 8c9a148b27..c5e22d8c7e 100644
--- a/third_party/jpeg-xl/lib/extras/enc/encode.cc
+++ b/third_party/jpeg-xl/lib/extras/enc/encode.cc
@@ -54,7 +54,7 @@ Status Encoder::VerifyBitDepth(JxlDataType data_type, uint32_t bits_per_sample,
(bits_per_sample > 16 || exponent_bits > 5))) {
return JXL_FAILURE(
"Incompatible data_type %d and bit depth %u with exponent bits %u",
- (int)data_type, bits_per_sample, exponent_bits);
+ static_cast<int>(data_type), bits_per_sample, exponent_bits);
}
return true;
}
diff --git a/third_party/jpeg-xl/lib/extras/enc/encode.h b/third_party/jpeg-xl/lib/extras/enc/encode.h
index da5f509838..2502d9976b 100644
--- a/third_party/jpeg-xl/lib/extras/enc/encode.h
+++ b/third_party/jpeg-xl/lib/extras/enc/encode.h
@@ -56,7 +56,7 @@ class Encoder {
// Any existing data in encoded_image is discarded.
virtual Status Encode(const PackedPixelFile& ppf, EncodedImage* encoded_image,
- ThreadPool* pool = nullptr) const = 0;
+ ThreadPool* pool) const = 0;
void SetOption(std::string name, std::string value) {
options_[std::move(name)] = std::move(value);
diff --git a/third_party/jpeg-xl/lib/extras/enc/exr.cc b/third_party/jpeg-xl/lib/extras/enc/exr.cc
index d4005c3097..5a4f7d768c 100644
--- a/third_party/jpeg-xl/lib/extras/enc/exr.cc
+++ b/third_party/jpeg-xl/lib/extras/enc/exr.cc
@@ -84,7 +84,7 @@ Status EncodeImageEXR(const PackedImage& image, const JxlBasicInfo& info,
const size_t xsize = info.xsize;
const size_t ysize = info.ysize;
const bool has_alpha = info.alpha_bits > 0;
- const bool alpha_is_premultiplied = info.alpha_premultiplied;
+ const bool alpha_is_premultiplied = FROM_JXL_BOOL(info.alpha_premultiplied);
if (info.num_color_channels != 3 ||
c_enc.color_space != JXL_COLOR_SPACE_RGB ||
@@ -177,7 +177,7 @@ class EXREncoder : public Encoder {
return formats;
}
Status Encode(const PackedPixelFile& ppf, EncodedImage* encoded_image,
- ThreadPool* pool = nullptr) const override {
+ ThreadPool* pool) const override {
JXL_RETURN_IF_ERROR(VerifyBasicInfo(ppf.info));
encoded_image->icc.clear();
encoded_image->bitstreams.clear();
diff --git a/third_party/jpeg-xl/lib/extras/enc/jpegli.cc b/third_party/jpeg-xl/lib/extras/enc/jpegli.cc
index 7e1aa426df..aa10b584d0 100644
--- a/third_party/jpeg-xl/lib/extras/enc/jpegli.cc
+++ b/third_party/jpeg-xl/lib/extras/enc/jpegli.cc
@@ -34,6 +34,7 @@
#include "lib/jxl/color_encoding_internal.h"
#include "lib/jxl/enc_xyb.h"
#include "lib/jxl/image.h"
+#include "lib/jxl/simd_util.h"
namespace jxl {
namespace extras {
@@ -53,6 +54,10 @@ Status VerifyInput(const PackedPixelFile& ppf) {
if (ppf.frames.size() != 1) {
return JXL_FAILURE("JPEG input must have exactly one frame.");
}
+ if (info.num_color_channels != 1 && info.num_color_channels != 3) {
+ return JXL_FAILURE("Invalid number of color channels %d",
+ info.num_color_channels);
+ }
const PackedImage& image = ppf.frames[0].color;
JXL_RETURN_IF_ERROR(Encoder::VerifyImageSize(image, info));
if (image.format.data_type == JXL_TYPE_FLOAT16) {
@@ -71,7 +76,7 @@ Status VerifyInput(const PackedPixelFile& ppf) {
Status GetColorEncoding(const PackedPixelFile& ppf,
ColorEncoding* color_encoding) {
- if (!ppf.icc.empty()) {
+ if (ppf.primary_color_representation == PackedPixelFile::kIccIsPrimary) {
IccBytes icc = ppf.icc;
JXL_RETURN_IF_ERROR(
color_encoding->SetICC(std::move(icc), JxlGetDefaultCms()));
@@ -122,12 +127,12 @@ Status WriteAppData(j_compress_ptr cinfo,
return true;
}
-static constexpr int kICCMarker = 0xe2;
+constexpr int kICCMarker = 0xe2;
constexpr unsigned char kICCSignature[12] = {
0x49, 0x43, 0x43, 0x5F, 0x50, 0x52, 0x4F, 0x46, 0x49, 0x4C, 0x45, 0x00};
-static constexpr uint8_t kUnknownTf = 2;
-static constexpr unsigned char kCICPTagSignature[4] = {0x63, 0x69, 0x63, 0x70};
-static constexpr size_t kCICPTagSize = 12;
+constexpr uint8_t kUnknownTf = 2;
+constexpr unsigned char kCICPTagSignature[4] = {0x63, 0x69, 0x63, 0x70};
+constexpr size_t kCICPTagSize = 12;
bool FindCICPTag(const uint8_t* icc_data, size_t len, bool is_first_chunk,
size_t* cicp_offset, size_t* cicp_length, uint8_t* cicp_tag,
@@ -248,32 +253,48 @@ JpegliEndianness ConvertEndianness(JxlEndianness endianness) {
}
}
-void ToFloatRow(const uint8_t* row_in, JxlPixelFormat format, size_t len,
- float* row_out) {
+void ToFloatRow(const uint8_t* row_in, JxlPixelFormat format, size_t xsize,
+ size_t c_out, float* row_out) {
bool is_little_endian =
(format.endianness == JXL_LITTLE_ENDIAN ||
(format.endianness == JXL_NATIVE_ENDIAN && IsLittleEndian()));
static constexpr double kMul8 = 1.0 / 255.0;
static constexpr double kMul16 = 1.0 / 65535.0;
+ const size_t c_in = format.num_channels;
if (format.data_type == JXL_TYPE_UINT8) {
- for (size_t x = 0; x < len; ++x) {
- row_out[x] = row_in[x] * kMul8;
+ for (size_t x = 0; x < xsize; ++x) {
+ for (size_t c = 0; c < c_out; ++c) {
+ const size_t ix = c_in * x + c;
+ row_out[c_out * x + c] = row_in[ix] * kMul8;
+ }
}
} else if (format.data_type == JXL_TYPE_UINT16 && is_little_endian) {
- for (size_t x = 0; x < len; ++x) {
- row_out[x] = LoadLE16(&row_in[2 * x]) * kMul16;
+ for (size_t x = 0; x < xsize; ++x) {
+ for (size_t c = 0; c < c_out; ++c) {
+ const size_t ix = c_in * x + c;
+ row_out[c_out * x + c] = LoadLE16(&row_in[2 * ix]) * kMul16;
+ }
}
} else if (format.data_type == JXL_TYPE_UINT16 && !is_little_endian) {
- for (size_t x = 0; x < len; ++x) {
- row_out[x] = LoadBE16(&row_in[2 * x]) * kMul16;
+ for (size_t x = 0; x < xsize; ++x) {
+ for (size_t c = 0; c < c_out; ++c) {
+ const size_t ix = c_in * x + c;
+ row_out[c_out * x + c] = LoadBE16(&row_in[2 * ix]) * kMul16;
+ }
}
} else if (format.data_type == JXL_TYPE_FLOAT && is_little_endian) {
- for (size_t x = 0; x < len; ++x) {
- row_out[x] = LoadLEFloat(&row_in[4 * x]);
+ for (size_t x = 0; x < xsize; ++x) {
+ for (size_t c = 0; c < c_out; ++c) {
+ const size_t ix = c_in * x + c;
+ row_out[c_out * x + c] = LoadLEFloat(&row_in[4 * ix]);
+ }
}
} else if (format.data_type == JXL_TYPE_FLOAT && !is_little_endian) {
- for (size_t x = 0; x < len; ++x) {
- row_out[x] = LoadBEFloat(&row_in[4 * x]);
+ for (size_t x = 0; x < xsize; ++x) {
+ for (size_t c = 0; c < c_out; ++c) {
+ const size_t ix = c_in * x + c;
+ row_out[c_out * x + c] = LoadBEFloat(&row_in[4 * ix]);
+ }
}
}
}
@@ -352,9 +373,6 @@ Status EncodeJpeg(const PackedPixelFile& ppf, const JpegSettings& jpeg_settings,
ColorSpaceTransform c_transform(*JxlGetDefaultCms());
ColorEncoding xyb_encoding;
if (jpeg_settings.xyb) {
- if (ppf.info.num_color_channels != 3) {
- return JXL_FAILURE("Only RGB input is supported in XYB mode.");
- }
if (HasICCProfile(jpeg_settings.app_data)) {
return JXL_FAILURE("APP data ICC profile is not supported in XYB mode.");
}
@@ -374,11 +392,11 @@ Status EncodeJpeg(const PackedPixelFile& ppf, const JpegSettings& jpeg_settings,
unsigned char* output_buffer = nullptr;
unsigned long output_size = 0;
std::vector<uint8_t> row_bytes;
- size_t rowlen = RoundUpTo(ppf.info.xsize, VectorSize());
+ size_t rowlen = RoundUpTo(ppf.info.xsize, MaxVectorSize());
hwy::AlignedFreeUniquePtr<float[]> xyb_tmp =
hwy::AllocateAligned<float>(6 * rowlen);
hwy::AlignedFreeUniquePtr<float[]> premul_absorb =
- hwy::AllocateAligned<float>(VectorSize() * 12);
+ hwy::AllocateAligned<float>(MaxVectorSize() * 12);
ComputePremulAbsorb(255.0f, premul_absorb.get());
jpeg_compress_struct cinfo;
@@ -401,6 +419,8 @@ Status EncodeJpeg(const PackedPixelFile& ppf, const JpegSettings& jpeg_settings,
cinfo.input_components == 1 ? JCS_GRAYSCALE : JCS_RGB;
if (jpeg_settings.xyb) {
jpegli_set_xyb_mode(&cinfo);
+ cinfo.input_components = 3;
+ cinfo.in_color_space = JCS_RGB;
} else if (jpeg_settings.use_std_quant_tables) {
jpegli_use_standard_quant_tables(&cinfo);
}
@@ -436,7 +456,7 @@ Status EncodeJpeg(const PackedPixelFile& ppf, const JpegSettings& jpeg_settings,
}
}
jpegli_enable_adaptive_quantization(
- &cinfo, jpeg_settings.use_adaptive_quantization);
+ &cinfo, TO_JXL_BOOL(jpeg_settings.use_adaptive_quantization));
if (jpeg_settings.psnr_target > 0.0) {
jpegli_set_psnr(&cinfo, jpeg_settings.psnr_target,
jpeg_settings.search_tolerance,
@@ -448,11 +468,11 @@ Status EncodeJpeg(const PackedPixelFile& ppf, const JpegSettings& jpeg_settings,
jpegli_set_distance(&cinfo, jpeg_settings.distance, TRUE);
}
jpegli_set_progressive_level(&cinfo, jpeg_settings.progressive_level);
- cinfo.optimize_coding = jpeg_settings.optimize_coding;
+ cinfo.optimize_coding = TO_JXL_BOOL(jpeg_settings.optimize_coding);
if (!jpeg_settings.app_data.empty()) {
// Make sure jpegli_start_compress() does not write any APP markers.
- cinfo.write_JFIF_header = false;
- cinfo.write_Adobe_marker = false;
+ cinfo.write_JFIF_header = JXL_FALSE;
+ cinfo.write_Adobe_marker = JXL_FALSE;
}
const PackedImage& image = ppf.frames[0].color;
if (jpeg_settings.xyb) {
@@ -476,10 +496,10 @@ Status EncodeJpeg(const PackedPixelFile& ppf, const JpegSettings& jpeg_settings,
float* dst_buf = c_transform.BufDst(0);
for (size_t y = 0; y < image.ysize; ++y) {
// convert to float
- ToFloatRow(&pixels[y * image.stride], image.format, 3 * image.xsize,
- src_buf);
+ ToFloatRow(&pixels[y * image.stride], image.format, image.xsize,
+ info.num_color_channels, src_buf);
// convert to linear srgb
- if (!c_transform.Run(0, src_buf, dst_buf)) {
+ if (!c_transform.Run(0, src_buf, dst_buf, image.xsize)) {
return false;
}
// deinterleave channels
@@ -508,9 +528,9 @@ Status EncodeJpeg(const PackedPixelFile& ppf, const JpegSettings& jpeg_settings,
}
} else {
row_bytes.resize(image.stride);
- if (cinfo.num_components == (int)image.format.num_channels) {
+ if (cinfo.num_components == static_cast<int>(image.format.num_channels)) {
for (size_t y = 0; y < info.ysize; ++y) {
- memcpy(&row_bytes[0], pixels + y * image.stride, image.stride);
+ memcpy(row_bytes.data(), pixels + y * image.stride, image.stride);
JSAMPROW row[] = {row_bytes.data()};
jpegli_write_scanlines(&cinfo, row, 1);
}
diff --git a/third_party/jpeg-xl/lib/extras/enc/jpg.cc b/third_party/jpeg-xl/lib/extras/enc/jpg.cc
index c34dc6c13f..de0228fc0d 100644
--- a/third_party/jpeg-xl/lib/extras/enc/jpg.cc
+++ b/third_party/jpeg-xl/lib/extras/enc/jpg.cc
@@ -23,6 +23,7 @@
#include <vector>
#include "lib/extras/exif.h"
+#include "lib/jxl/base/common.h"
#include "lib/jxl/base/status.h"
#include "lib/jxl/sanitizers.h"
#if JPEGXL_ENABLE_SJPEG
@@ -50,23 +51,21 @@ enum class JpegEncoder {
kSJpeg,
};
-#define ARRAY_SIZE(X) (sizeof(X) / sizeof((X)[0]))
-
// Popular jpeg scan scripts
// The fields of the individual scans are:
// comps_in_scan, component_index[], Ss, Se, Ah, Al
-static constexpr jpeg_scan_info kScanScript1[] = {
+constexpr auto kScanScript1 = to_array<jpeg_scan_info>({
{1, {0}, 0, 0, 0, 0}, //
{1, {1}, 0, 0, 0, 0}, //
{1, {2}, 0, 0, 0, 0}, //
{1, {0}, 1, 8, 0, 0}, //
{1, {0}, 9, 63, 0, 0}, //
{1, {1}, 1, 63, 0, 0}, //
- {1, {2}, 1, 63, 0, 0}, //
-};
-static constexpr size_t kNumScans1 = ARRAY_SIZE(kScanScript1);
+ {1, {2}, 1, 63, 0, 0} //
+});
+constexpr size_t kNumScans1 = kScanScript1.size();
-static constexpr jpeg_scan_info kScanScript2[] = {
+constexpr auto kScanScript2 = to_array<jpeg_scan_info>({
{1, {0}, 0, 0, 0, 0}, //
{1, {1}, 0, 0, 0, 0}, //
{1, {2}, 0, 0, 0, 0}, //
@@ -74,11 +73,11 @@ static constexpr jpeg_scan_info kScanScript2[] = {
{1, {0}, 3, 63, 0, 1}, //
{1, {0}, 1, 63, 1, 0}, //
{1, {1}, 1, 63, 0, 0}, //
- {1, {2}, 1, 63, 0, 0}, //
-};
-static constexpr size_t kNumScans2 = ARRAY_SIZE(kScanScript2);
+ {1, {2}, 1, 63, 0, 0} //
+});
+constexpr size_t kNumScans2 = kScanScript2.size();
-static constexpr jpeg_scan_info kScanScript3[] = {
+constexpr auto kScanScript3 = to_array<jpeg_scan_info>({
{1, {0}, 0, 0, 0, 0}, //
{1, {1}, 0, 0, 0, 0}, //
{1, {2}, 0, 0, 0, 0}, //
@@ -86,11 +85,11 @@ static constexpr jpeg_scan_info kScanScript3[] = {
{1, {0}, 1, 63, 2, 1}, //
{1, {0}, 1, 63, 1, 0}, //
{1, {1}, 1, 63, 0, 0}, //
- {1, {2}, 1, 63, 0, 0}, //
-};
-static constexpr size_t kNumScans3 = ARRAY_SIZE(kScanScript3);
+ {1, {2}, 1, 63, 0, 0} //
+});
+constexpr size_t kNumScans3 = kScanScript3.size();
-static constexpr jpeg_scan_info kScanScript4[] = {
+constexpr auto kScanScript4 = to_array<jpeg_scan_info>({
{3, {0, 1, 2}, 0, 0, 0, 1}, //
{1, {0}, 1, 5, 0, 2}, //
{1, {2}, 1, 63, 0, 1}, //
@@ -100,11 +99,11 @@ static constexpr jpeg_scan_info kScanScript4[] = {
{3, {0, 1, 2}, 0, 0, 1, 0}, //
{1, {2}, 1, 63, 1, 0}, //
{1, {1}, 1, 63, 1, 0}, //
- {1, {0}, 1, 63, 1, 0}, //
-};
-static constexpr size_t kNumScans4 = ARRAY_SIZE(kScanScript4);
+ {1, {0}, 1, 63, 1, 0} //
+});
+constexpr size_t kNumScans4 = kScanScript4.size();
-static constexpr jpeg_scan_info kScanScript5[] = {
+constexpr auto kScanScript5 = to_array<jpeg_scan_info>({
{3, {0, 1, 2}, 0, 0, 0, 1}, //
{1, {0}, 1, 5, 0, 2}, //
{1, {1}, 1, 5, 0, 2}, //
@@ -118,12 +117,12 @@ static constexpr jpeg_scan_info kScanScript5[] = {
{3, {0, 1, 2}, 0, 0, 1, 0}, //
{1, {0}, 1, 63, 1, 0}, //
{1, {1}, 1, 63, 1, 0}, //
- {1, {2}, 1, 63, 1, 0}, //
-};
-static constexpr size_t kNumScans5 = ARRAY_SIZE(kScanScript5);
+ {1, {2}, 1, 63, 1, 0} //
+});
+constexpr size_t kNumScans5 = kScanScript5.size();
// default progressive mode of jpegli
-static constexpr jpeg_scan_info kScanScript6[] = {
+constexpr auto kScanScript6 = to_array<jpeg_scan_info>({
{3, {0, 1, 2}, 0, 0, 0, 0}, //
{1, {0}, 1, 2, 0, 0}, //
{1, {1}, 1, 2, 0, 0}, //
@@ -137,8 +136,8 @@ static constexpr jpeg_scan_info kScanScript6[] = {
{1, {0}, 3, 63, 1, 0}, //
{1, {1}, 3, 63, 1, 0}, //
{1, {2}, 3, 63, 1, 0}, //
-};
-static constexpr size_t kNumScans6 = ARRAY_SIZE(kScanScript6);
+});
+constexpr size_t kNumScans6 = kScanScript6.size();
// Adapt RGB scan info to grayscale jpegs.
void FilterScanComponents(const jpeg_compress_struct* cinfo,
@@ -163,12 +162,12 @@ Status SetJpegProgression(int progressive_id,
jpeg_simple_progression(cinfo);
return true;
}
- constexpr const jpeg_scan_info* kScanScripts[] = {kScanScript1, kScanScript2,
- kScanScript3, kScanScript4,
- kScanScript5, kScanScript6};
- constexpr size_t kNumScans[] = {kNumScans1, kNumScans2, kNumScans3,
- kNumScans4, kNumScans5, kNumScans6};
- if (progressive_id > static_cast<int>(ARRAY_SIZE(kNumScans))) {
+ const jpeg_scan_info* kScanScripts[] = {
+ kScanScript1.data(), kScanScript2.data(), kScanScript3.data(),
+ kScanScript4.data(), kScanScript5.data(), kScanScript6.data()};
+ constexpr auto kNumScans = to_array<size_t>(
+ {kNumScans1, kNumScans2, kNumScans3, kNumScans4, kNumScans5, kNumScans6});
+ if (progressive_id > static_cast<int>(kNumScans.size())) {
return JXL_FAILURE("Unknown jpeg scan script id %d", progressive_id);
}
const jpeg_scan_info* scan_script = kScanScripts[progressive_id - 1];
@@ -178,7 +177,7 @@ Status SetJpegProgression(int progressive_id,
jpeg_scan_info scan_info = scan_script[i];
FilterScanComponents(cinfo, &scan_info);
if (scan_info.comps_in_scan > 0) {
- scan_infos->emplace_back(std::move(scan_info));
+ scan_infos->emplace_back(scan_info);
}
}
cinfo->scan_info = scan_infos->data();
@@ -217,8 +216,8 @@ void WriteExif(jpeg_compress_struct* const cinfo,
for (const unsigned char c : kExifSignature) {
jpeg_write_m_byte(cinfo, c);
}
- for (size_t i = 0; i < exif.size(); ++i) {
- jpeg_write_m_byte(cinfo, exif[i]);
+ for (uint8_t c : exif) {
+ jpeg_write_m_byte(cinfo, c);
}
}
@@ -284,7 +283,7 @@ Status EncodeWithLibJpeg(const PackedImage& image, const JxlBasicInfo& info,
cinfo.input_components = info.num_color_channels;
cinfo.in_color_space = info.num_color_channels == 1 ? JCS_GRAYSCALE : JCS_RGB;
jpeg_set_defaults(&cinfo);
- cinfo.optimize_coding = params.optimize_coding;
+ cinfo.optimize_coding = static_cast<boolean>(params.optimize_coding);
if (cinfo.input_components == 3) {
JXL_RETURN_IF_ERROR(
SetChromaSubsampling(params.chroma_subsampling, &cinfo));
@@ -310,10 +309,10 @@ Status EncodeWithLibJpeg(const PackedImage& image, const JxlBasicInfo& info,
std::vector<uint8_t> row_bytes(image.stride);
const uint8_t* pixels = reinterpret_cast<const uint8_t*>(image.pixels());
- if (cinfo.num_components == (int)image.format.num_channels &&
+ if (cinfo.num_components == static_cast<int>(image.format.num_channels) &&
image.format.data_type == JXL_TYPE_UINT8) {
for (size_t y = 0; y < info.ysize; ++y) {
- memcpy(&row_bytes[0], pixels + y * image.stride, image.stride);
+ memcpy(row_bytes.data(), pixels + y * image.stride, image.stride);
JSAMPROW row[] = {row_bytes.data()};
jpeg_write_scanlines(&cinfo, row, 1);
}
@@ -401,7 +400,7 @@ struct MySearchHook : public sjpeg::SearchHook {
}
bool Update(float result) override {
value = result;
- if (fabs(value - target) < tolerance * target) {
+ if (std::fabs(value - target) < tolerance * target) {
return true;
}
if (value > target) {
@@ -420,9 +419,9 @@ struct MySearchHook : public sjpeg::SearchHook {
} else {
q = (qmin + qmax) / 2.;
}
- return (pass > 0 && fabs(q - last_q) < q_precision);
+ return (pass > 0 && std::fabs(q - last_q) < q_precision);
}
- ~MySearchHook() override {}
+ ~MySearchHook() override = default;
};
#endif
@@ -539,7 +538,7 @@ class JPEGEncoder : public Encoder {
return formats;
}
Status Encode(const PackedPixelFile& ppf, EncodedImage* encoded_image,
- ThreadPool* pool = nullptr) const override {
+ ThreadPool* pool) const override {
JXL_RETURN_IF_ERROR(VerifyBasicInfo(ppf.info));
JpegEncoder jpeg_encoder = JpegEncoder::kLibJpeg;
JpegParams params;
diff --git a/third_party/jpeg-xl/lib/extras/enc/jxl.cc b/third_party/jpeg-xl/lib/extras/enc/jxl.cc
index 00adbb7dda..d563f298e6 100644
--- a/third_party/jpeg-xl/lib/extras/enc/jxl.cc
+++ b/third_party/jpeg-xl/lib/extras/enc/jxl.cc
@@ -7,6 +7,7 @@
#include <jxl/encode.h>
#include <jxl/encode_cxx.h>
+#include <jxl/types.h>
#include "lib/jxl/base/exif.h"
@@ -132,7 +133,7 @@ bool EncodeImageJXL(const JXLCompressParams& params, const PackedPixelFile& ppf,
return false;
}
- auto settings = JxlEncoderFrameSettingsCreate(enc, nullptr);
+ auto* settings = JxlEncoderFrameSettingsCreate(enc, nullptr);
size_t option_idx = 0;
if (!SetFrameOptions(params.options, 0, &option_idx, settings)) {
return false;
@@ -150,10 +151,11 @@ bool EncodeImageJXL(const JXLCompressParams& params, const PackedPixelFile& ppf,
JxlEncoderCollectStats(settings, params.stats);
}
+ bool has_jpeg_bytes = (jpeg_bytes != nullptr);
bool use_boxes = !ppf.metadata.exif.empty() || !ppf.metadata.xmp.empty() ||
!ppf.metadata.jumbf.empty() || !ppf.metadata.iptc.empty();
bool use_container = params.use_container || use_boxes ||
- (jpeg_bytes && params.jpeg_store_metadata);
+ (has_jpeg_bytes && params.jpeg_store_metadata);
if (JXL_ENC_SUCCESS !=
JxlEncoderUseContainer(enc, static_cast<int>(use_container))) {
@@ -161,12 +163,22 @@ bool EncodeImageJXL(const JXLCompressParams& params, const PackedPixelFile& ppf,
return false;
}
- if (jpeg_bytes) {
+ if (has_jpeg_bytes) {
if (params.jpeg_store_metadata &&
JXL_ENC_SUCCESS != JxlEncoderStoreJPEGMetadata(enc, JXL_TRUE)) {
fprintf(stderr, "Storing JPEG metadata failed.\n");
return false;
}
+ if (params.jpeg_store_metadata && params.jpeg_strip_exif) {
+ fprintf(stderr,
+ "Cannot store metadata and strip exif at the same time.\n");
+ return false;
+ }
+ if (params.jpeg_store_metadata && params.jpeg_strip_xmp) {
+ fprintf(stderr,
+ "Cannot store metadata and strip xmp at the same time.\n");
+ return false;
+ }
if (!params.jpeg_store_metadata && params.jpeg_strip_exif) {
JxlEncoderFrameSettingsSetOption(settings,
JXL_ENC_FRAME_SETTING_JPEG_KEEP_EXIF, 0);
@@ -210,8 +222,8 @@ bool EncodeImageJXL(const JXLCompressParams& params, const PackedPixelFile& ppf,
basic_info.num_extra_channels =
std::max<uint32_t>(num_alpha_channels, ppf.info.num_extra_channels);
basic_info.num_color_channels = ppf.info.num_color_channels;
- const bool lossless = params.distance == 0;
- basic_info.uses_original_profile = lossless;
+ const bool lossless = (params.distance == 0);
+ basic_info.uses_original_profile = TO_JXL_BOOL(lossless);
if (params.override_bitdepth != 0) {
basic_info.bits_per_sample = params.override_bitdepth;
basic_info.exponent_bits_per_sample =
@@ -233,7 +245,7 @@ bool EncodeImageJXL(const JXLCompressParams& params, const PackedPixelFile& ppf,
return false;
}
if (JXL_ENC_SUCCESS !=
- JxlEncoderSetFrameBitDepth(settings, &params.input_bitdepth)) {
+ JxlEncoderSetFrameBitDepth(settings, &ppf.input_bitdepth)) {
fprintf(stderr, "JxlEncoderSetFrameBitDepth() failed.\n");
return false;
}
@@ -248,7 +260,7 @@ bool EncodeImageJXL(const JXLCompressParams& params, const PackedPixelFile& ppf,
fprintf(stderr, "JxlEncoderSetFrameLossless() failed.\n");
return false;
}
- if (!ppf.icc.empty()) {
+ if (ppf.primary_color_representation == PackedPixelFile::kIccIsPrimary) {
if (JXL_ENC_SUCCESS !=
JxlEncoderSetICCProfile(enc, ppf.icc.data(), ppf.icc.size())) {
fprintf(stderr, "JxlEncoderSetICCProfile() failed.\n");
@@ -284,14 +296,15 @@ bool EncodeImageJXL(const JXLCompressParams& params, const PackedPixelFile& ppf,
{"jumb", ppf.metadata.jumbf},
{"xml ", ppf.metadata.iptc},
};
- for (size_t i = 0; i < sizeof boxes / sizeof *boxes; ++i) {
- const BoxInfo& box = boxes[i];
- if (!box.bytes.empty() &&
- JXL_ENC_SUCCESS != JxlEncoderAddBox(enc, box.type, box.bytes.data(),
- box.bytes.size(),
- params.compress_boxes)) {
- fprintf(stderr, "JxlEncoderAddBox() failed (%s).\n", box.type);
- return false;
+ for (auto box : boxes) {
+ if (!box.bytes.empty()) {
+ if (JXL_ENC_SUCCESS !=
+ JxlEncoderAddBox(enc, box.type, box.bytes.data(),
+ box.bytes.size(),
+ TO_JXL_BOOL(params.compress_boxes))) {
+ fprintf(stderr, "JxlEncoderAddBox() failed (%s).\n", box.type);
+ return false;
+ }
}
}
JxlEncoderCloseBoxes(enc);
@@ -336,7 +349,7 @@ bool EncodeImageJXL(const JXLCompressParams& params, const PackedPixelFile& ppf,
}
const bool last_frame = fi + 1 == ppf.chunked_frames.size();
if (JXL_ENC_SUCCESS !=
- JxlEncoderAddChunkedFrame(settings, last_frame,
+ JxlEncoderAddChunkedFrame(settings, TO_JXL_BOOL(last_frame),
chunked_frame.GetInputSource())) {
fprintf(stderr, "JxlEncoderAddChunkedFrame() failed.\n");
return false;
diff --git a/third_party/jpeg-xl/lib/extras/enc/jxl.h b/third_party/jpeg-xl/lib/extras/enc/jxl.h
index b8ca5bda2f..2b3793c0c4 100644
--- a/third_party/jpeg-xl/lib/extras/enc/jxl.h
+++ b/third_party/jpeg-xl/lib/extras/enc/jxl.h
@@ -38,7 +38,7 @@ struct JXLCompressParams {
std::vector<JXLOption> options;
// Target butteraugli distance, 0.0 means lossless.
float distance = 1.0f;
- float alpha_distance = 1.0f;
+ float alpha_distance = 0.0f;
// If set to true, forces container mode.
bool use_container = false;
// Whether to enable/disable byte-exact jpeg reconstruction for jpeg inputs.
@@ -57,8 +57,6 @@ struct JXLCompressParams {
size_t override_bitdepth = 0;
int32_t codestream_level = -1;
int32_t premultiply = -1;
- // Override input buffer interpretation.
- JxlBitDepth input_bitdepth = {JXL_BIT_DEPTH_FROM_PIXEL_FORMAT, 0, 0};
// If runner_opaque is set, the decoder uses this parallel runner.
JxlParallelRunner runner = JxlThreadParallelRunner;
void* runner_opaque = nullptr;
@@ -69,10 +67,10 @@ struct JXLCompressParams {
bool allow_expert_options = false;
void AddOption(JxlEncoderFrameSettingId id, int64_t val) {
- options.emplace_back(JXLOption(id, val, 0));
+ options.emplace_back(id, val, 0);
}
void AddFloatOption(JxlEncoderFrameSettingId id, float val) {
- options.emplace_back(JXLOption(id, val, 0));
+ options.emplace_back(id, val, 0);
}
bool HasOutputProcessor() const {
return (output_processor.get_buffer != nullptr &&
diff --git a/third_party/jpeg-xl/lib/extras/enc/npy.cc b/third_party/jpeg-xl/lib/extras/enc/npy.cc
index ae8cf13cc4..8d9954ef31 100644
--- a/third_party/jpeg-xl/lib/extras/enc/npy.cc
+++ b/third_party/jpeg-xl/lib/extras/enc/npy.cc
@@ -7,11 +7,13 @@
#include <jxl/types.h>
+#include <memory>
#include <sstream>
#include <string>
#include <vector>
#include "lib/extras/packed_image.h"
+#include "lib/jxl/base/common.h"
namespace jxl {
namespace extras {
@@ -52,14 +54,17 @@ class JSONDict : public JSONField {
static_assert(std::is_convertible<T*, JSONField*>::value,
"T must be a JSONField");
T* ret = new T();
- values_.emplace_back(
- key, std::unique_ptr<JSONField>(static_cast<JSONField*>(ret)));
+ JSONField* field = static_cast<JSONField*>(ret);
+ auto handle = std::unique_ptr<JSONField>(field);
+ values_.emplace_back(key, std::move(handle));
return ret;
}
template <typename T>
void Add(const std::string& key, const T& value) {
- values_.emplace_back(key, std::unique_ptr<JSONField>(new JSONValue(value)));
+ JSONField* field = static_cast<JSONField*>(new JSONValue(value));
+ auto handle = std::unique_ptr<JSONField>(field);
+ values_.emplace_back(key, std::move(handle));
}
void Write(std::ostream& o, uint32_t indent) const override {
@@ -71,11 +76,11 @@ class JSONDict : public JSONField {
o << ",";
}
is_first = false;
- o << std::endl << indent_str << " \"" << key_value.first << "\": ";
+ o << "\n" << indent_str << " \"" << key_value.first << "\": ";
key_value.second->Write(o, indent + 2);
}
if (!values_.empty()) {
- o << std::endl << indent_str;
+ o << "\n" << indent_str;
}
o << "}";
}
@@ -112,11 +117,11 @@ class JSONArray : public JSONField {
o << ",";
}
is_first = false;
- o << std::endl << indent_str << " ";
+ o << "\n" << indent_str << " ";
value->Write(o, indent + 2);
}
if (!values_.empty()) {
- o << std::endl << indent_str;
+ o << "\n" << indent_str;
}
o << "]";
}
@@ -160,13 +165,13 @@ void GenerateMetadata(const PackedPixelFile& ppf, std::vector<uint8_t>* out) {
}
{
- auto ectype = meta.AddEmpty<JSONArray>("extra_channel_type");
- auto bps = meta.AddEmpty<JSONArray>("bits_per_sample");
- auto ebps = meta.AddEmpty<JSONArray>("exp_bits_per_sample");
+ auto* ectype = meta.AddEmpty<JSONArray>("extra_channel_type");
+ auto* bps = meta.AddEmpty<JSONArray>("bits_per_sample");
+ auto* ebps = meta.AddEmpty<JSONArray>("exp_bits_per_sample");
bps->Add(ppf.info.bits_per_sample);
ebps->Add(ppf.info.exponent_bits_per_sample);
- for (size_t i = 0; i < ppf.extra_channels_info.size(); i++) {
- switch (ppf.extra_channels_info[i].ec_info.type) {
+ for (const auto& eci : ppf.extra_channels_info) {
+ switch (eci.ec_info.type) {
case JXL_CHANNEL_ALPHA: {
ectype->Add(std::string("Alpha"));
break;
@@ -200,8 +205,8 @@ void GenerateMetadata(const PackedPixelFile& ppf, std::vector<uint8_t>* out) {
break;
}
}
- bps->Add(ppf.extra_channels_info[i].ec_info.bits_per_sample);
- ebps->Add(ppf.extra_channels_info[i].ec_info.exponent_bits_per_sample);
+ bps->Add(eci.ec_info.bits_per_sample);
+ ebps->Add(eci.ec_info.exponent_bits_per_sample);
}
}
@@ -282,7 +287,7 @@ bool WriteNPYArray(const PackedPixelFile& ppf, std::vector<uint8_t>* out) {
class NumPyEncoder : public Encoder {
public:
Status Encode(const PackedPixelFile& ppf, EncodedImage* encoded_image,
- ThreadPool* pool = nullptr) const override {
+ ThreadPool* pool) const override {
JXL_RETURN_IF_ERROR(VerifyBasicInfo(ppf.info));
GenerateMetadata(ppf, &encoded_image->metadata);
encoded_image->bitstreams.emplace_back();
diff --git a/third_party/jpeg-xl/lib/extras/enc/pgx.cc b/third_party/jpeg-xl/lib/extras/enc/pgx.cc
index d4809e38b6..eb8eab4271 100644
--- a/third_party/jpeg-xl/lib/extras/enc/pgx.cc
+++ b/third_party/jpeg-xl/lib/extras/enc/pgx.cc
@@ -60,7 +60,7 @@ Status EncodeImagePGX(const PackedFrame& frame, const JxlBasicInfo& info,
std::vector<uint8_t> pixels(num_samples * bytes_per_sample);
if (format.data_type == JXL_TYPE_UINT8) {
- memcpy(&pixels[0], in, num_samples * bytes_per_sample);
+ memcpy(pixels.data(), in, num_samples * bytes_per_sample);
} else if (format.data_type == JXL_TYPE_UINT16) {
if (format.endianness != JXL_BIG_ENDIAN) {
const uint8_t* p_in = in;
@@ -69,7 +69,7 @@ Status EncodeImagePGX(const PackedFrame& frame, const JxlBasicInfo& info,
StoreBE16(LoadLE16(p_in), p_out);
}
} else {
- memcpy(&pixels[0], in, num_samples * bytes_per_sample);
+ memcpy(pixels.data(), in, num_samples * bytes_per_sample);
}
} else {
return JXL_FAILURE("Unsupported pixel data type");
diff --git a/third_party/jpeg-xl/lib/extras/enc/pnm.cc b/third_party/jpeg-xl/lib/extras/enc/pnm.cc
index 4183900198..966611cfca 100644
--- a/third_party/jpeg-xl/lib/extras/enc/pnm.cc
+++ b/third_party/jpeg-xl/lib/extras/enc/pnm.cc
@@ -31,7 +31,7 @@ constexpr size_t kMaxHeaderSize = 200;
class BasePNMEncoder : public Encoder {
public:
Status Encode(const PackedPixelFile& ppf, EncodedImage* encoded_image,
- ThreadPool* pool = nullptr) const override {
+ ThreadPool* pool) const override {
JXL_RETURN_IF_ERROR(VerifyBasicInfo(ppf.info));
if (!ppf.metadata.exif.empty() || !ppf.metadata.iptc.empty() ||
!ppf.metadata.jumbf.empty() || !ppf.metadata.xmp.empty()) {
diff --git a/third_party/jpeg-xl/lib/extras/jpegli_test.cc b/third_party/jpeg-xl/lib/extras/jpegli_test.cc
index 66c18617a6..3049049a64 100644
--- a/third_party/jpeg-xl/lib/extras/jpegli_test.cc
+++ b/third_party/jpeg-xl/lib/extras/jpegli_test.cc
@@ -85,7 +85,7 @@ Status EncodeWithLibjpeg(const PackedPixelFile& ppf, int quality,
std::unique_ptr<Encoder> encoder = GetJPEGEncoder();
encoder->SetOption("q", std::to_string(quality));
EncodedImage encoded;
- JXL_RETURN_IF_ERROR(encoder->Encode(ppf, &encoded));
+ JXL_RETURN_IF_ERROR(encoder->Encode(ppf, &encoded, nullptr));
JXL_RETURN_IF_ERROR(!encoded.bitstreams.empty());
*compressed = std::move(encoded.bitstreams[0]);
return true;
@@ -156,8 +156,8 @@ TEST(JpegliTest, JpegliXYBEncodeTest) {
PackedPixelFile ppf_out;
ASSERT_TRUE(DecodeWithLibjpeg(compressed, &ppf_out));
- EXPECT_THAT(BitsPerPixel(ppf_in, compressed), IsSlightlyBelow(1.45f));
- EXPECT_THAT(ButteraugliDistance(ppf_in, ppf_out), IsSlightlyBelow(1.32f));
+ EXPECT_SLIGHTLY_BELOW(BitsPerPixel(ppf_in, compressed), 1.45f);
+ EXPECT_SLIGHTLY_BELOW(ButteraugliDistance(ppf_in, ppf_out), 1.32f);
}
TEST(JpegliTest, JpegliDecodeTestLargeSmoothArea) {
@@ -205,8 +205,8 @@ TEST(JpegliTest, JpegliYUVEncodeTest) {
PackedPixelFile ppf_out;
ASSERT_TRUE(DecodeWithLibjpeg(compressed, &ppf_out));
- EXPECT_THAT(BitsPerPixel(ppf_in, compressed), IsSlightlyBelow(1.7f));
- EXPECT_THAT(ButteraugliDistance(ppf_in, ppf_out), IsSlightlyBelow(1.32f));
+ EXPECT_SLIGHTLY_BELOW(BitsPerPixel(ppf_in, compressed), 1.7f);
+ EXPECT_SLIGHTLY_BELOW(ButteraugliDistance(ppf_in, ppf_out), 1.32f);
}
TEST(JpegliTest, JpegliYUVChromaSubsamplingEncodeTest) {
@@ -247,8 +247,8 @@ TEST(JpegliTest, JpegliYUVEncodeTestNoAq) {
PackedPixelFile ppf_out;
ASSERT_TRUE(DecodeWithLibjpeg(compressed, &ppf_out));
- EXPECT_THAT(BitsPerPixel(ppf_in, compressed), IsSlightlyBelow(1.85f));
- EXPECT_THAT(ButteraugliDistance(ppf_in, ppf_out), IsSlightlyBelow(1.25f));
+ EXPECT_SLIGHTLY_BELOW(BitsPerPixel(ppf_in, compressed), 1.85f);
+ EXPECT_SLIGHTLY_BELOW(ButteraugliDistance(ppf_in, ppf_out), 1.25f);
}
TEST(JpegliTest, JpegliHDRRoundtripTest) {
@@ -267,8 +267,8 @@ TEST(JpegliTest, JpegliHDRRoundtripTest) {
JpegDecompressParams dparams;
dparams.output_data_type = JXL_TYPE_UINT16;
ASSERT_TRUE(DecodeJpeg(compressed, dparams, nullptr, &ppf_out));
- EXPECT_THAT(BitsPerPixel(ppf_in, compressed), IsSlightlyBelow(2.95f));
- EXPECT_THAT(ButteraugliDistance(ppf_in, ppf_out), IsSlightlyBelow(1.05f));
+ EXPECT_SLIGHTLY_BELOW(BitsPerPixel(ppf_in, compressed), 2.95f);
+ EXPECT_SLIGHTLY_BELOW(ButteraugliDistance(ppf_in, ppf_out), 1.05f);
}
TEST(JpegliTest, JpegliSetAppData) {
diff --git a/third_party/jpeg-xl/lib/extras/mmap.cc b/third_party/jpeg-xl/lib/extras/mmap.cc
index 7852831ebd..9f5bba97ed 100644
--- a/third_party/jpeg-xl/lib/extras/mmap.cc
+++ b/third_party/jpeg-xl/lib/extras/mmap.cc
@@ -10,7 +10,8 @@
#include "lib/jxl/base/common.h"
-#if __unix__
+#if defined(__unix__) || defined(__unix) || \
+ defined(__APPLE__) && defined(__MACH__)
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
@@ -54,7 +55,7 @@ struct MemoryMappedFileImpl {
} // namespace jxl
-#elif __WIN32__
+#elif defined(_WIN32)
#include <string.h>
#include <windows.h>
@@ -97,6 +98,8 @@ struct MemoryMappedFileImpl {
return f;
}
+ ~MemoryMappedFileImpl() { UnmapViewOfFile(ptr); }
+
const uint8_t* data() const { return reinterpret_cast<const uint8_t*>(ptr); }
size_t size() const { return fsize.QuadPart; }
diff --git a/third_party/jpeg-xl/lib/extras/packed_image.h b/third_party/jpeg-xl/lib/extras/packed_image.h
index edd5f1be75..a66ddfbd70 100644
--- a/third_party/jpeg-xl/lib/extras/packed_image.h
+++ b/third_party/jpeg-xl/lib/extras/packed_image.h
@@ -37,11 +37,18 @@ namespace extras {
// Class representing an interleaved image with a bunch of channels.
class PackedImage {
public:
- PackedImage(size_t xsize, size_t ysize, const JxlPixelFormat& format)
- : PackedImage(xsize, ysize, format, CalcStride(format, xsize)) {}
+ static StatusOr<PackedImage> Create(size_t xsize, size_t ysize,
+ const JxlPixelFormat& format) {
+ PackedImage image(xsize, ysize, format, CalcStride(format, xsize));
+ if (!image.pixels()) {
+ // TODO(szabadka): use specialized OOM error code
+ return JXL_FAILURE("Failed to allocate memory for image");
+ }
+ return image;
+ }
PackedImage Copy() const {
- PackedImage copy(xsize, ysize, format);
+ PackedImage copy(xsize, ysize, format, CalcStride(format, xsize));
memcpy(reinterpret_cast<uint8_t*>(copy.pixels()),
reinterpret_cast<const uint8_t*>(pixels()), pixels_size);
return copy;
@@ -108,7 +115,7 @@ class PackedImage {
}
}
- void SetPixelValue(size_t y, size_t x, size_t c, float val) {
+ void SetPixelValue(size_t y, size_t x, size_t c, float val) const {
uint8_t* data = pixels(y, x, c);
switch (format.data_type) {
case JXL_TYPE_UINT8:
@@ -169,17 +176,25 @@ class PackedImage {
// as all other frames in the same image.
class PackedFrame {
public:
- template <typename... Args>
- explicit PackedFrame(Args&&... args) : color(std::forward<Args>(args)...) {}
+ explicit PackedFrame(PackedImage&& image) : color(std::move(image)) {}
+
+ static StatusOr<PackedFrame> Create(size_t xsize, size_t ysize,
+ const JxlPixelFormat& format) {
+ JXL_ASSIGN_OR_RETURN(PackedImage image,
+ PackedImage::Create(xsize, ysize, format));
+ PackedFrame frame(std::move(image));
+ return frame;
+ }
- PackedFrame Copy() const {
- PackedFrame copy(color.xsize, color.ysize, color.format);
+ StatusOr<PackedFrame> Copy() const {
+ JXL_ASSIGN_OR_RETURN(
+ PackedFrame copy,
+ PackedFrame::Create(color.xsize, color.ysize, color.format));
copy.frame_info = frame_info;
copy.name = name;
copy.color = color.Copy();
- for (size_t i = 0; i < extra_channels.size(); ++i) {
- PackedImage ec = extra_channels[i].Copy();
- copy.extra_channels.emplace_back(std::move(ec));
+ for (const auto& ec : extra_channels) {
+ copy.extra_channels.emplace_back(ec.Copy());
}
return copy;
}
@@ -244,12 +259,26 @@ class PackedPixelFile {
std::vector<PackedExtraChannel> extra_channels_info;
// Color information of the decoded pixels.
- // If the icc is empty, the JxlColorEncoding should be used instead.
- std::vector<uint8_t> icc;
+ // `primary_color_representation` indicates whether `color_encoding` or `icc`
+ // is the “authoritative” encoding of the colorspace, as opposed to a fallback
+ // encoding. For example, if `color_encoding` is the primary one, as would
+ // occur when decoding a jxl file with such a representation, then `enc/jxl`
+ // will use it and ignore the ICC profile, whereas `enc/png` will include the
+ // ICC profile for compatibility.
+ // If `icc` is the primary representation, `enc/jxl` will preserve it when
+ // compressing losslessly, but *may* encode it as a color_encoding when
+ // compressing lossily.
+ enum {
+ kColorEncodingIsPrimary,
+ kIccIsPrimary
+ } primary_color_representation = kColorEncodingIsPrimary;
JxlColorEncoding color_encoding = {};
+ std::vector<uint8_t> icc;
// The icc profile of the original image.
std::vector<uint8_t> orig_icc;
+ JxlBitDepth input_bitdepth = {JXL_BIT_DEPTH_FROM_PIXEL_FORMAT, 0, 0};
+
std::unique_ptr<PackedFrame> preview_frame;
std::vector<PackedFrame> frames;
mutable std::vector<ChunkedPackedFrame> chunked_frames;
diff --git a/third_party/jpeg-xl/lib/extras/packed_image_convert.cc b/third_party/jpeg-xl/lib/extras/packed_image_convert.cc
index 56f3b044a4..2ad001bf09 100644
--- a/third_party/jpeg-xl/lib/extras/packed_image_convert.cc
+++ b/third_party/jpeg-xl/lib/extras/packed_image_convert.cc
@@ -22,15 +22,15 @@ namespace jxl {
namespace extras {
Status ConvertPackedFrameToImageBundle(const JxlBasicInfo& info,
+ const JxlBitDepth& input_bitdepth,
const PackedFrame& frame,
const CodecInOut& io, ThreadPool* pool,
ImageBundle* bundle) {
JXL_ASSERT(frame.color.pixels() != nullptr);
- const bool float_in = frame.color.format.data_type == JXL_TYPE_FLOAT16 ||
- frame.color.format.data_type == JXL_TYPE_FLOAT;
size_t frame_bits_per_sample =
- float_in ? PackedImage::BitsPerChannel(frame.color.format.data_type)
- : info.bits_per_sample;
+ input_bitdepth.type == JXL_BIT_DEPTH_FROM_PIXEL_FORMAT
+ ? PackedImage::BitsPerChannel(frame.color.format.data_type)
+ : info.bits_per_sample;
JXL_ASSERT(frame_bits_per_sample != 0);
// It is ok for the frame.color.format.num_channels to not match the
// number of channels on the image.
@@ -64,7 +64,8 @@ Status ConvertPackedFrameToImageBundle(const JxlBasicInfo& info,
bundle->extra_channels().resize(io.metadata.m.extra_channel_info.size());
for (size_t i = 0; i < frame.extra_channels.size(); i++) {
const auto& ppf_ec = frame.extra_channels[i];
- bundle->extra_channels()[i] = ImageF(ppf_ec.xsize, ppf_ec.ysize);
+ JXL_ASSIGN_OR_RETURN(bundle->extra_channels()[i],
+ ImageF::Create(ppf_ec.xsize, ppf_ec.ysize));
JXL_CHECK(BufferToImageF(ppf_ec.format, ppf_ec.xsize, ppf_ec.ysize,
ppf_ec.pixels(), ppf_ec.pixels_size, pool,
&bundle->extra_channels()[i]));
@@ -97,23 +98,23 @@ Status ConvertPackedPixelFileToCodecInOut(const PackedPixelFile& ppf,
ppf.info.exponent_bits_per_sample == 0 && ppf.info.bits_per_sample <= 12;
io->metadata.m.SetAlphaBits(ppf.info.alpha_bits,
- ppf.info.alpha_premultiplied);
+ FROM_JXL_BOOL(ppf.info.alpha_premultiplied));
ExtraChannelInfo* alpha = io->metadata.m.Find(ExtraChannel::kAlpha);
if (alpha) alpha->bit_depth = io->metadata.m.bit_depth;
- io->metadata.m.xyb_encoded = !ppf.info.uses_original_profile;
+ io->metadata.m.xyb_encoded = !FROM_JXL_BOOL(ppf.info.uses_original_profile);
JXL_ASSERT(ppf.info.orientation > 0 && ppf.info.orientation <= 8);
io->metadata.m.orientation = ppf.info.orientation;
// Convert animation metadata
JXL_ASSERT(ppf.frames.size() == 1 || ppf.info.have_animation);
- io->metadata.m.have_animation = ppf.info.have_animation;
+ io->metadata.m.have_animation = FROM_JXL_BOOL(ppf.info.have_animation);
io->metadata.m.animation.tps_numerator = ppf.info.animation.tps_numerator;
io->metadata.m.animation.tps_denominator = ppf.info.animation.tps_denominator;
io->metadata.m.animation.num_loops = ppf.info.animation.num_loops;
// Convert the color encoding.
- if (!ppf.icc.empty()) {
+ if (ppf.primary_color_representation == PackedPixelFile::kIccIsPrimary) {
IccBytes icc = ppf.icc;
if (!io->metadata.m.color_encoding.SetICC(std::move(icc),
JxlGetDefaultCms())) {
@@ -170,15 +171,16 @@ Status ConvertPackedPixelFileToCodecInOut(const PackedPixelFile& ppf,
JXL_RETURN_IF_ERROR(
io->metadata.m.preview_size.Set(preview_xsize, preview_ysize));
JXL_RETURN_IF_ERROR(ConvertPackedFrameToImageBundle(
- ppf.info, *ppf.preview_frame, *io, pool, &io->preview_frame));
+ ppf.info, ppf.input_bitdepth, *ppf.preview_frame, *io, pool,
+ &io->preview_frame));
}
// Convert the pixels
io->frames.clear();
for (const auto& frame : ppf.frames) {
ImageBundle bundle(&io->metadata.m);
- JXL_RETURN_IF_ERROR(
- ConvertPackedFrameToImageBundle(ppf.info, frame, *io, pool, &bundle));
+ JXL_RETURN_IF_ERROR(ConvertPackedFrameToImageBundle(
+ ppf.info, ppf.input_bitdepth, frame, *io, pool, &bundle));
io->frames.push_back(std::move(bundle));
}
@@ -210,7 +212,8 @@ PackedPixelFile ConvertImage3FToPackedPixelFile(const Image3F& image,
: 0;
ppf.color_encoding = c_enc.ToExternal();
ppf.frames.clear();
- PackedFrame frame(image.xsize(), image.ysize(), format);
+ JXL_ASSIGN_OR_DIE(PackedFrame frame,
+ PackedFrame::Create(image.xsize(), image.ysize(), format));
const ImageF* channels[3];
for (int c = 0; c < 3; ++c) {
channels[c] = &image.Plane(c);
@@ -242,7 +245,8 @@ Status ConvertCodecInOutToPackedPixelFile(const CodecInOut& io,
ppf->info.alpha_bits = alpha_channel->bit_depth.bits_per_sample;
ppf->info.alpha_exponent_bits =
alpha_channel->bit_depth.exponent_bits_per_sample;
- ppf->info.alpha_premultiplied = alpha_channel->alpha_associated;
+ ppf->info.alpha_premultiplied =
+ TO_JXL_BOOL(alpha_channel->alpha_associated);
}
// Convert the image metadata
@@ -257,9 +261,9 @@ Status ConvertCodecInOutToPackedPixelFile(const CodecInOut& io,
ppf->info.linear_below = io.metadata.m.tone_mapping.linear_below;
ppf->info.min_nits = io.metadata.m.tone_mapping.min_nits;
ppf->info.relative_to_max_display =
- io.metadata.m.tone_mapping.relative_to_max_display;
+ TO_JXL_BOOL(io.metadata.m.tone_mapping.relative_to_max_display);
- ppf->info.uses_original_profile = !io.metadata.m.xyb_encoded;
+ ppf->info.uses_original_profile = TO_JXL_BOOL(!io.metadata.m.xyb_encoded);
JXL_ASSERT(0 < io.metadata.m.orientation && io.metadata.m.orientation <= 8);
ppf->info.orientation =
static_cast<JxlOrientation>(io.metadata.m.orientation);
@@ -267,13 +271,16 @@ Status ConvertCodecInOutToPackedPixelFile(const CodecInOut& io,
// Convert animation metadata
JXL_ASSERT(io.frames.size() == 1 || io.metadata.m.have_animation);
- ppf->info.have_animation = io.metadata.m.have_animation;
+ ppf->info.have_animation = TO_JXL_BOOL(io.metadata.m.have_animation);
ppf->info.animation.tps_numerator = io.metadata.m.animation.tps_numerator;
ppf->info.animation.tps_denominator = io.metadata.m.animation.tps_denominator;
ppf->info.animation.num_loops = io.metadata.m.animation.num_loops;
// Convert the color encoding
ppf->icc.assign(c_desired.ICC().begin(), c_desired.ICC().end());
+ ppf->primary_color_representation =
+ c_desired.WantICC() ? PackedPixelFile::kIccIsPrimary
+ : PackedPixelFile::kColorEncodingIsPrimary;
ppf->color_encoding = c_desired.ToExternal();
// Convert the extra blobs
@@ -289,22 +296,24 @@ Status ConvertCodecInOutToPackedPixelFile(const CodecInOut& io,
JXL_ASSERT(frame.metadata()->bit_depth.bits_per_sample != 0);
// It is ok for the frame.color().kNumPlanes to not match the
// number of channels on the image.
+ const uint32_t alpha_channels = has_alpha ? 1 : 0;
const uint32_t num_channels =
- frame.metadata()->color_encoding.Channels() + has_alpha;
+ frame.metadata()->color_encoding.Channels() + alpha_channels;
JxlPixelFormat format{/*num_channels=*/num_channels,
/*data_type=*/pixel_format.data_type,
/*endianness=*/pixel_format.endianness,
/*align=*/pixel_format.align};
- PackedFrame packed_frame(frame.oriented_xsize(), frame.oriented_ysize(),
- format);
+ JXL_ASSIGN_OR_RETURN(PackedFrame packed_frame,
+ PackedFrame::Create(frame.oriented_xsize(),
+ frame.oriented_ysize(), format));
const size_t bits_per_sample =
float_out ? packed_frame.color.BitsPerChannel(pixel_format.data_type)
: ppf->info.bits_per_sample;
packed_frame.name = frame.name;
packed_frame.frame_info.name_length = frame.name.size();
// Color transform
- ImageBundle ib = frame.Copy();
+ JXL_ASSIGN_OR_RETURN(ImageBundle ib, frame.Copy());
const ImageBundle* to_color_transform = &ib;
ImageMetadata metadata = io.metadata.m;
ImageBundle store(&metadata);
diff --git a/third_party/jpeg-xl/lib/extras/tone_mapping.cc b/third_party/jpeg-xl/lib/extras/tone_mapping.cc
index 3d0269524b..39df304501 100644
--- a/third_party/jpeg-xl/lib/extras/tone_mapping.cc
+++ b/third_party/jpeg-xl/lib/extras/tone_mapping.cc
@@ -19,7 +19,7 @@ HWY_BEFORE_NAMESPACE();
namespace jxl {
namespace HWY_NAMESPACE {
-static constexpr float rec2020_luminances[3] = {0.2627f, 0.6780f, 0.0593f};
+static constexpr Vector3 rec2020_luminances{0.2627f, 0.6780f, 0.0593f};
Status ToneMapFrame(const std::pair<float, float> display_nits,
ImageBundle* const ib, ThreadPool* const pool) {
diff --git a/third_party/jpeg-xl/lib/extras/tone_mapping_gbench.cc b/third_party/jpeg-xl/lib/extras/tone_mapping_gbench.cc
index 34cbdde781..8fc928a4fd 100644
--- a/third_party/jpeg-xl/lib/extras/tone_mapping_gbench.cc
+++ b/third_party/jpeg-xl/lib/extras/tone_mapping_gbench.cc
@@ -6,11 +6,12 @@
#include "benchmark/benchmark.h"
#include "lib/extras/codec.h"
#include "lib/extras/tone_mapping.h"
+#include "lib/jxl/image.h"
namespace jxl {
static void BM_ToneMapping(benchmark::State& state) {
- Image3F color(2268, 1512);
+ JXL_ASSIGN_OR_DIE(Image3F color, Image3F::Create(2268, 1512));
FillImage(0.5f, &color);
// Use linear Rec. 2020 so that `ToneMapTo` doesn't have to convert to it and
@@ -25,7 +26,8 @@ static void BM_ToneMapping(benchmark::State& state) {
for (auto _ : state) {
state.PauseTiming();
CodecInOut tone_mapping_input;
- Image3F color2(color.xsize(), color.ysize());
+ JXL_ASSIGN_OR_DIE(Image3F color2,
+ Image3F::Create(color.xsize(), color.ysize()));
CopyImageTo(color, &color2);
tone_mapping_input.SetFromImage(std::move(color2), linear_rec2020);
tone_mapping_input.metadata.m.SetIntensityTarget(255);
diff --git a/third_party/jpeg-xl/lib/include/jxl/cms_interface.h b/third_party/jpeg-xl/lib/include/jxl/cms_interface.h
index c164eaccb5..25c700867a 100644
--- a/third_party/jpeg-xl/lib/include/jxl/cms_interface.h
+++ b/third_party/jpeg-xl/lib/include/jxl/cms_interface.h
@@ -29,10 +29,10 @@ extern "C" {
/** Parses an ICC profile and populates @p c and @p cmyk with the data.
*
- * @param user_data JxlCmsInterface::set_fields_data passed as-is.
+ * @param user_data @ref JxlCmsInterface::set_fields_data passed as-is.
* @param icc_data the ICC data to parse.
* @param icc_size how many bytes of icc_data are valid.
- * @param c a JxlColorEncoding to populate if applicable.
+ * @param c a @ref JxlColorEncoding to populate if applicable.
* @param cmyk a boolean to set to whether the colorspace is a CMYK colorspace.
* @return Whether the relevant fields in @p c were successfully populated.
*/
@@ -66,22 +66,23 @@ typedef struct {
/** Allocates and returns the data needed for @p num_threads parallel transforms
* from the @p input colorspace to @p output, with up to @p pixels_per_thread
- * pixels to transform per call to JxlCmsInterface::run. @p init_data comes
- * directly from the JxlCmsInterface instance. Since @c run only receives the
- * data returned by @c init, a reference to @p init_data should be kept there
- * if access to it is desired in @c run. Likewise for JxlCmsInterface::destroy.
+ * pixels to transform per call to @ref JxlCmsInterface::run. @p init_data comes
+ * directly from the @ref JxlCmsInterface instance. Since @c run only receives
+ * the data returned by @c init, a reference to @p init_data should be kept
+ * there if access to it is desired in @c run. Likewise for @ref
+ * JxlCmsInterface::destroy.
*
* The ICC data in @p input and @p output is guaranteed to outlive the @c init /
* @c run / @c destroy cycle.
*
- * @param init_data JxlCmsInterface::init_data passed as-is.
+ * @param init_data @ref JxlCmsInterface::init_data passed as-is.
* @param num_threads the maximum number of threads from which
- * JxlCmsInterface::run will be called.
+ * @ref JxlCmsInterface::run will be called.
* @param pixels_per_thread the maximum number of pixels that each call to
- * JxlCmsInterface::run will have to transform.
+ * @ref JxlCmsInterface::run will have to transform.
* @param input_profile the input colorspace for the transform.
- * @param output_profile the colorspace to which JxlCmsInterface::run should
- * convert the input data.
+ * @param output_profile the colorspace to which @ref JxlCmsInterface::run
+ * should convert the input data.
* @param intensity_target for colorspaces where luminance is relative
* (essentially: not PQ), indicates the luminance at which (1, 1, 1) will
* be displayed. This is useful for conversions between PQ and a relative
@@ -135,7 +136,7 @@ typedef float* (*jpegxl_cms_get_buffer_func)(void* user_data, size_t thread);
* @param output_buffer the buffer receiving the transformed pixel data.
* @param num_pixels the number of pixels to transform from @p input to
* @p output.
- * @return JXL_TRUE on success, JXL_FALSE on failure.
+ * @return ::JXL_TRUE on success, ::JXL_FALSE on failure.
*/
typedef JXL_BOOL (*jpegxl_cms_run_func)(void* user_data, size_t thread,
const float* input_buffer,
@@ -226,7 +227,7 @@ typedef void (*jpegxl_cms_destroy_func)(void*);
typedef struct {
/** CMS-specific data that will be passed to @ref set_fields_from_icc. */
void* set_fields_data;
- /** Populates a JxlColorEncoding from an ICC profile. */
+ /** Populates a @ref JxlColorEncoding from an ICC profile. */
jpegxl_cms_set_fields_from_icc_func set_fields_from_icc;
/** CMS-specific data that will be passed to @ref init. */
diff --git a/third_party/jpeg-xl/lib/include/jxl/codestream_header.h b/third_party/jpeg-xl/lib/include/jxl/codestream_header.h
index fb71484233..e60eb03355 100644
--- a/third_party/jpeg-xl/lib/include/jxl/codestream_header.h
+++ b/third_party/jpeg-xl/lib/include/jxl/codestream_header.h
@@ -71,7 +71,7 @@ typedef struct {
} JxlPreviewHeader;
/** The codestream animation header, optionally present in the beginning of
- * the codestream, and if it is it applies to all animation frames, unlike
+ * the codestream, and if it is it applies to all animation frames, unlike @ref
* JxlFrameHeader which applies to an individual frame.
*/
typedef struct {
@@ -166,12 +166,12 @@ typedef struct {
* it to to the original color profile. The decoder also does not convert to
* the target display color profile. To convert the pixel data produced by
* the decoder to the original color profile, one of the JxlDecoderGetColor*
- * functions needs to be called with @ref JXL_COLOR_PROFILE_TARGET_DATA to get
- * the color profile of the decoder output, and then an external CMS can be
- * used for conversion.
- * Note that for lossy compression, this should be set to false for most use
- * cases, and if needed, the image should be converted to the original color
- * profile after decoding, as described above.
+ * functions needs to be called with
+ * ::JXL_COLOR_PROFILE_TARGET_DATA to get the color profile of the decoder
+ * output, and then an external CMS can be used for conversion. Note that for
+ * lossy compression, this should be set to false for most use cases, and if
+ * needed, the image should be converted to the original color profile after
+ * decoding, as described above.
*/
JXL_BOOL uses_original_profile;
@@ -194,17 +194,19 @@ typedef struct {
* grayscale data, or 3 for colored data. This count does not include
* the alpha channel or other extra channels. To check presence of an alpha
* channel, such as in the case of RGBA color, check alpha_bits != 0.
- * If and only if this is 1, the JxlColorSpace in the JxlColorEncoding is
- * JXL_COLOR_SPACE_GRAY.
+ * If and only if this is 1, the @ref JxlColorSpace in the @ref
+ * JxlColorEncoding is
+ * ::JXL_COLOR_SPACE_GRAY.
*/
uint32_t num_color_channels;
/** Number of additional image channels. This includes the main alpha channel,
* but can also include additional channels such as depth, additional alpha
* channels, spot colors, and so on. Information about the extra channels
- * can be queried with JxlDecoderGetExtraChannelInfo. The main alpha channel,
- * if it exists, also has its information available in the alpha_bits,
- * alpha_exponent_bits and alpha_premultiplied fields in this JxlBasicInfo.
+ * can be queried with @ref JxlDecoderGetExtraChannelInfo. The main alpha
+ * channel, if it exists, also has its information available in the
+ * alpha_bits, alpha_exponent_bits and alpha_premultiplied fields in this @ref
+ * JxlBasicInfo.
*/
uint32_t num_extra_channels;
@@ -388,7 +390,8 @@ typedef struct {
/** The header of one displayed frame or non-coalesced layer. */
typedef struct {
/** How long to wait after rendering in ticks. The duration in seconds of a
- * tick is given by tps_numerator and tps_denominator in JxlAnimationHeader.
+ * tick is given by tps_numerator and tps_denominator in @ref
+ * JxlAnimationHeader.
*/
uint32_t duration;
@@ -396,9 +399,9 @@ typedef struct {
* interpreted from most-significant to least-significant as hour, minute,
* second, and frame. If timecode is nonzero, it is strictly larger than that
* of a previous frame with nonzero duration. These values are only available
- * if have_timecodes in JxlAnimationHeader is JXL_TRUE.
- * This value is only used if have_timecodes in JxlAnimationHeader is
- * JXL_TRUE.
+ * if have_timecodes in @ref JxlAnimationHeader is ::JXL_TRUE.
+ * This value is only used if have_timecodes in @ref JxlAnimationHeader is
+ * ::JXL_TRUE.
*/
uint32_t timecode;
diff --git a/third_party/jpeg-xl/lib/include/jxl/color_encoding.h b/third_party/jpeg-xl/lib/include/jxl/color_encoding.h
index 928117e8dd..e6325dcb30 100644
--- a/third_party/jpeg-xl/lib/include/jxl/color_encoding.h
+++ b/third_party/jpeg-xl/lib/include/jxl/color_encoding.h
@@ -24,9 +24,9 @@ extern "C" {
typedef enum {
/** Tristimulus RGB */
JXL_COLOR_SPACE_RGB,
- /** Luminance based, the primaries in JxlColorEncoding must be ignored. This
- * value implies that num_color_channels in JxlBasicInfo is 1, any other value
- * implies num_color_channels is 3. */
+ /** Luminance based, the primaries in @ref JxlColorEncoding must be ignored.
+ * This value implies that num_color_channels in @ref JxlBasicInfo is 1, any
+ * other value implies num_color_channels is 3. */
JXL_COLOR_SPACE_GRAY,
/** XYB (opsin) color space */
JXL_COLOR_SPACE_XYB,
@@ -35,18 +35,18 @@ typedef enum {
} JxlColorSpace;
/** Built-in whitepoints for color encoding. When decoding, the numerical xy
- * whitepoint value can be read from the JxlColorEncoding white_point field
+ * whitepoint value can be read from the @ref JxlColorEncoding white_point field
* regardless of the enum value. When encoding, enum values except
- * JXL_WHITE_POINT_CUSTOM override the numerical fields. Some enum values match
- * a subset of CICP (Rec. ITU-T H.273 | ISO/IEC 23091-2:2019(E)), however the
- * white point and RGB primaries are separate enums here.
+ * ::JXL_WHITE_POINT_CUSTOM override the numerical fields. Some enum values
+ * match a subset of CICP (Rec. ITU-T H.273 | ISO/IEC 23091-2:2019(E)), however
+ * the white point and RGB primaries are separate enums here.
*/
typedef enum {
/** CIE Standard Illuminant D65: 0.3127, 0.3290 */
JXL_WHITE_POINT_D65 = 1,
- /** White point must be read from the JxlColorEncoding white_point field, or
- * as ICC profile. This enum value is not an exact match of the corresponding
- * CICP value. */
+ /** White point must be read from the @ref JxlColorEncoding white_point field,
+ * or as ICC profile. This enum value is not an exact match of the
+ * corresponding CICP value. */
JXL_WHITE_POINT_CUSTOM = 2,
/** CIE Standard Illuminant E (equal-energy): 1/3, 1/3 */
JXL_WHITE_POINT_E = 10,
@@ -55,10 +55,10 @@ typedef enum {
} JxlWhitePoint;
/** Built-in primaries for color encoding. When decoding, the primaries can be
- * read from the JxlColorEncoding primaries_red_xy, primaries_green_xy and
+ * read from the @ref JxlColorEncoding primaries_red_xy, primaries_green_xy and
* primaries_blue_xy fields regardless of the enum value. When encoding, the
- * enum values except JXL_PRIMARIES_CUSTOM override the numerical fields. Some
- * enum values match a subset of CICP (Rec. ITU-T H.273 | ISO/IEC
+ * enum values except ::JXL_PRIMARIES_CUSTOM override the numerical fields.
+ * Some enum values match a subset of CICP (Rec. ITU-T H.273 | ISO/IEC
* 23091-2:2019(E)), however the white point and RGB primaries are separate
* enums here.
*/
@@ -66,7 +66,7 @@ typedef enum {
/** The CIE xy values of the red, green and blue primaries are: 0.639998686,
0.330010138; 0.300003784, 0.600003357; 0.150002046, 0.059997204 */
JXL_PRIMARIES_SRGB = 1,
- /** Primaries must be read from the JxlColorEncoding primaries_red_xy,
+ /** Primaries must be read from the @ref JxlColorEncoding primaries_red_xy,
* primaries_green_xy and primaries_blue_xy fields, or as ICC profile. This
* enum value is not an exact match of the corresponding CICP value. */
JXL_PRIMARIES_CUSTOM = 2,
@@ -94,7 +94,7 @@ typedef enum {
JXL_TRANSFER_FUNCTION_DCI = 17,
/** As specified in Rec. ITU-R BT.2100-1 (HLG) */
JXL_TRANSFER_FUNCTION_HLG = 18,
- /** Transfer function follows power law given by the gamma value in
+ /** Transfer function follows power law given by the gamma value in @ref
JxlColorEncoding. Not a CICP value. */
JXL_TRANSFER_FUNCTION_GAMMA = 65535,
} JxlTransferFunction;
@@ -118,7 +118,7 @@ typedef struct {
*/
JxlColorSpace color_space;
- /** Built-in white point. If this value is JXL_WHITE_POINT_CUSTOM, must
+ /** Built-in white point. If this value is ::JXL_WHITE_POINT_CUSTOM, must
* use the numerical whitepoint values from white_point_xy.
*/
JxlWhitePoint white_point;
@@ -126,10 +126,10 @@ typedef struct {
/** Numerical whitepoint values in CIE xy space. */
double white_point_xy[2];
- /** Built-in RGB primaries. If this value is JXL_PRIMARIES_CUSTOM, must
+ /** Built-in RGB primaries. If this value is ::JXL_PRIMARIES_CUSTOM, must
* use the numerical primaries values below. This field and the custom values
* below are unused and must be ignored if the color space is
- * JXL_COLOR_SPACE_GRAY or JXL_COLOR_SPACE_XYB.
+ * ::JXL_COLOR_SPACE_GRAY or ::JXL_COLOR_SPACE_XYB.
*/
JxlPrimaries primaries;
@@ -145,7 +145,8 @@ typedef struct {
/** Transfer function if have_gamma is 0 */
JxlTransferFunction transfer_function;
- /** Gamma value used when transfer_function is JXL_TRANSFER_FUNCTION_GAMMA
+ /** Gamma value used when transfer_function is @ref
+ * JXL_TRANSFER_FUNCTION_GAMMA
*/
double gamma;
diff --git a/third_party/jpeg-xl/lib/include/jxl/decode.h b/third_party/jpeg-xl/lib/include/jxl/decode.h
index eaee70fa61..599b8336f3 100644
--- a/third_party/jpeg-xl/lib/include/jxl/decode.h
+++ b/third_party/jpeg-xl/lib/include/jxl/decode.h
@@ -66,12 +66,12 @@ typedef enum {
* @p size doesn't need to be a full image, only the beginning of the file.
*
* @return a flag indicating if a JPEG XL signature was found and what type.
- * - @ref JXL_SIG_NOT_ENOUGH_BYTES if not enough bytes were passed to
+ * - ::JXL_SIG_NOT_ENOUGH_BYTES if not enough bytes were passed to
* determine if a valid signature is there.
- * - @ref JXL_SIG_INVALID if no valid signature found for JPEG XL decoding.
- * - @ref JXL_SIG_CODESTREAM if a valid JPEG XL codestream signature was
+ * - ::JXL_SIG_INVALID if no valid signature found for JPEG XL decoding.
+ * - ::JXL_SIG_CODESTREAM if a valid JPEG XL codestream signature was
* found.
- * - @ref JXL_SIG_CONTAINER if a valid JPEG XL container signature was found.
+ * - ::JXL_SIG_CONTAINER if a valid JPEG XL container signature was found.
*/
JXL_EXPORT JxlSignature JxlSignatureCheck(const uint8_t* buf, size_t len);
@@ -115,7 +115,7 @@ JXL_EXPORT void JxlDecoderDestroy(JxlDecoder* dec);
/**
* Return value for @ref JxlDecoderProcessInput.
- * The values from @ref JXL_DEC_BASIC_INFO onwards are optional informative
+ * The values from ::JXL_DEC_BASIC_INFO onwards are optional informative
* events that can be subscribed to, they are never returned if they
* have not been registered with @ref JxlDecoderSubscribeEvents.
*/
@@ -123,12 +123,12 @@ typedef enum {
/** Function call finished successfully, or decoding is finished and there is
* nothing more to be done.
*
- * Note that @ref JxlDecoderProcessInput will return JXL_DEC_SUCCESS if all
- * events that were registered with @ref JxlDecoderSubscribeEvents were
+ * Note that @ref JxlDecoderProcessInput will return ::JXL_DEC_SUCCESS if
+ * all events that were registered with @ref JxlDecoderSubscribeEvents were
* processed, even before the end of the JPEG XL codestream.
*
* In this case, the return value @ref JxlDecoderReleaseInput will be the same
- * as it was at the last signaled event. E.g. if JXL_DEC_FULL_IMAGE was
+ * as it was at the last signaled event. E.g. if ::JXL_DEC_FULL_IMAGE was
* subscribed to, then all bytes from the end of the JPEG XL codestream
* (including possible boxes needed for jpeg reconstruction) will be returned
* as unprocessed.
@@ -151,14 +151,14 @@ typedef enum {
* In most cases, @ref JxlDecoderReleaseInput will return no unprocessed bytes
* at this event, the only exceptions are if the previously set input ended
* within (a) the raw codestream signature, (b) the signature box, (c) a box
- * header, or (d) the first 4 bytes of a brob, ftyp, or jxlp box. In any of
- * these cases the number of unprocessed bytes is less than 20.
+ * header, or (d) the first 4 bytes of a `brob`, `ftyp`, or `jxlp` box. In any
+ * of these cases the number of unprocessed bytes is less than 20.
*/
JXL_DEC_NEED_MORE_INPUT = 2,
/** The decoder is able to decode a preview image and requests setting a
* preview output buffer using @ref JxlDecoderSetPreviewOutBuffer. This occurs
- * if @ref JXL_DEC_PREVIEW_IMAGE is requested and it is possible to decode a
+ * if ::JXL_DEC_PREVIEW_IMAGE is requested and it is possible to decode a
* preview image from the codestream and the preview out buffer was not yet
* set. There is maximum one preview image in a codestream.
* In this case, @ref JxlDecoderReleaseInput will return all bytes from the
@@ -179,13 +179,13 @@ typedef enum {
/** The JPEG reconstruction buffer is too small for reconstructed JPEG
* codestream to fit. @ref JxlDecoderSetJPEGBuffer must be called again to
* make room for remaining bytes. This event may occur multiple times
- * after @ref JXL_DEC_JPEG_RECONSTRUCTION.
+ * after ::JXL_DEC_JPEG_RECONSTRUCTION.
*/
JXL_DEC_JPEG_NEED_MORE_OUTPUT = 6,
/** The box contents output buffer is too small. @ref JxlDecoderSetBoxBuffer
* must be called again to make room for remaining bytes. This event may occur
- * multiple times after @ref JXL_DEC_BOX.
+ * multiple times after ::JXL_DEC_BOX.
*/
JXL_DEC_BOX_NEED_MORE_OUTPUT = 7,
@@ -201,7 +201,7 @@ typedef enum {
/** Informative event by @ref JxlDecoderProcessInput
* "JxlDecoderProcessInput": Color encoding or ICC profile from the
* codestream header. This event occurs max once per image and always later
- * than @ref JXL_DEC_BASIC_INFO and earlier than any pixel data.
+ * than ::JXL_DEC_BASIC_INFO and earlier than any pixel data.
* In this case, @ref JxlDecoderReleaseInput will return all bytes from the
* end of the image header (which is the start of the first frame) as
* unprocessed.
@@ -212,7 +212,7 @@ typedef enum {
* "JxlDecoderProcessInput": Preview image, a small frame, decoded. This
* event can only happen if the image has a preview frame encoded. This event
* occurs max once for the codestream and always later than @ref
- * JXL_DEC_COLOR_ENCODING and before @ref JXL_DEC_FRAME.
+ * JXL_DEC_COLOR_ENCODING and before ::JXL_DEC_FRAME.
* In this case, @ref JxlDecoderReleaseInput will return all bytes from the
* end of the preview frame as unprocessed.
*/
@@ -223,19 +223,19 @@ typedef enum {
* JxlDecoderGetFrameHeader can be used at this point. A note on frames:
* a JPEG XL image can have internal frames that are not intended to be
* displayed (e.g. used for compositing a final frame), but this only returns
- * displayed frames, unless @ref JxlDecoderSetCoalescing was set to JXL_FALSE:
- * in that case, the individual layers are returned, without blending. Note
- * that even when coalescing is disabled, only frames of type kRegularFrame
- * are returned; frames of type kReferenceOnly and kLfFrame are always for
- * internal purposes only and cannot be accessed. A displayed frame either has
- * an animation duration or is the only or last frame in the image. This event
- * occurs max once per displayed frame, always later than @ref
- * JXL_DEC_COLOR_ENCODING, and always earlier than any pixel data. While
- * JPEG XL supports encoding a single frame as the composition of multiple
- * internal sub-frames also called frames, this event is not indicated for the
- * internal frames.
- * In this case, @ref JxlDecoderReleaseInput will return all bytes from the
- * end of the frame header (including ToC) as unprocessed.
+ * displayed frames, unless @ref JxlDecoderSetCoalescing was set to @ref
+ * JXL_FALSE "JXL_FALSE": in that case, the individual layers are returned,
+ * without blending. Note that even when coalescing is disabled, only frames
+ * of type kRegularFrame are returned; frames of type kReferenceOnly
+ * and kLfFrame are always for internal purposes only and cannot be accessed.
+ * A displayed frame either has an animation duration or is the only or last
+ * frame in the image. This event occurs max once per displayed frame, always
+ * later than ::JXL_DEC_COLOR_ENCODING, and always earlier than any pixel
+ * data. While JPEG XL supports encoding a single frame as the composition of
+ * multiple internal sub-frames also called frames, this event is not
+ * indicated for the internal frames. In this case, @ref
+ * JxlDecoderReleaseInput will return all bytes from the end of the frame
+ * header (including ToC) as unprocessed.
*/
JXL_DEC_FRAME = 0x400,
@@ -246,7 +246,7 @@ typedef enum {
* not this return status only indicates we're past this point in the
* codestream. This event occurs max once per frame.
* In this case, @ref JxlDecoderReleaseInput will return all bytes from the
- * end of the frame (or if @ref JXL_DEC_JPEG_RECONSTRUCTION is subscribed to,
+ * end of the frame (or if ::JXL_DEC_JPEG_RECONSTRUCTION is subscribed to,
* from the end of the last box that is needed for jpeg reconstruction) as
* unprocessed.
*/
@@ -259,9 +259,9 @@ typedef enum {
* is set a byte stream identical to the JPEG codestream used to encode the
* image will be written to the JPEG reconstruction buffer instead of pixels
* to the image out buffer. This event occurs max once per image and always
- * before @ref JXL_DEC_FULL_IMAGE.
+ * before ::JXL_DEC_FULL_IMAGE.
* In this case, @ref JxlDecoderReleaseInput will return all bytes from the
- * end of the 'jbrd' box as unprocessed.
+ * end of the `jbrd` box as unprocessed.
*/
JXL_DEC_JPEG_RECONSTRUCTION = 0x2000,
@@ -290,8 +290,8 @@ typedef enum {
*
* The buffer set with @ref JxlDecoderSetBoxBuffer must be set again for each
* next box to be obtained, or can be left unset to skip outputting this box.
- * The output buffer contains the full box data when the next @ref JXL_DEC_BOX
- * event or @ref JXL_DEC_SUCCESS occurs. @ref JXL_DEC_BOX occurs for all
+ * The output buffer contains the full box data when the next ::JXL_DEC_BOX
+ * event or ::JXL_DEC_SUCCESS occurs. ::JXL_DEC_BOX occurs for all
* boxes, including non-metadata boxes such as the signature box or codestream
* boxes. To check whether the box is a metadata type for respectively EXIF,
* XMP or JUMBF, use @ref JxlDecoderGetBoxType and check for types "Exif",
@@ -324,26 +324,40 @@ typedef enum {
* Setting a progressive detail with value N implies all progressive details
* with smaller or equal value. Currently only the following level of
* progressive detail is implemented:
- * - kDC (which implies kFrames)
- * - kLastPasses (which implies kDC and kFrames)
- * - kPasses (which implies kLastPasses, kDC and kFrames)
+ * - @ref kDC (which implies kFrames)
+ * - @ref kLastPasses (which implies @ref kDC and @ref kFrames)
+ * - @ref kPasses (which implies @ref kLastPasses, kDC and @ref kFrames)
*/
typedef enum {
- // after completed kRegularFrames
+ /**
+ * after completed kRegularFrames
+ */
kFrames = 0,
- // after completed DC (1:8)
+ /**
+ * after completed DC (1:8)
+ */
kDC = 1,
- // after completed AC passes that are the last pass for their resolution
- // target.
+ /**
+ * after completed AC passes that are the last pass for their resolution
+ * target.
+ */
kLastPasses = 2,
- // after completed AC passes that are not the last pass for their resolution
- // target.
+ /**
+ * after completed AC passes that are not the last pass for their resolution
+ * target.
+ */
kPasses = 3,
- // during DC frame when lower resolution are completed (1:32, 1:16)
+ /**
+ * during DC frame when lower resolution are completed (1:32, 1:16)
+ */
kDCProgressive = 4,
- // after completed groups
+ /**
+ * after completed groups
+ */
kDCGroups = 5,
- // after completed groups
+ /**
+ * after completed groups
+ */
kGroups = 6,
} JxlProgressiveDetail;
@@ -354,8 +368,8 @@ typedef enum {
* more efficiently with @ref JxlDecoderSkipFrames. Settings such as parallel
* runner or subscribed events are kept. After rewind, @ref
* JxlDecoderSubscribeEvents can be used again, and it is feasible to leave out
- * events that were already handled before, such as @ref JXL_DEC_BASIC_INFO
- * and @ref JXL_DEC_COLOR_ENCODING, since they will provide the same information
+ * events that were already handled before, such as ::JXL_DEC_BASIC_INFO
+ * and ::JXL_DEC_COLOR_ENCODING, since they will provide the same information
* as before.
* The difference to @ref JxlDecoderReset is that some state is kept, namely
* settings set by a call to
@@ -376,14 +390,14 @@ JXL_EXPORT void JxlDecoderRewind(JxlDecoder* dec);
* the input, but will not output the frame events. It can be more efficient
* when skipping frames, and even more so when using this after @ref
* JxlDecoderRewind. If the decoder is already processing a frame (could
- * have emitted @ref JXL_DEC_FRAME but not yet @ref JXL_DEC_FULL_IMAGE), it
+ * have emitted ::JXL_DEC_FRAME but not yet ::JXL_DEC_FULL_IMAGE), it
* starts skipping from the next frame. If the amount is larger than the amount
* of frames remaining in the image, all remaining frames are skipped. Calling
* this function multiple times adds the amount to skip to the already existing
* amount.
*
* A frame here is defined as a frame that without skipping emits events such
- * as @ref JXL_DEC_FRAME and @ref JXL_DEC_FULL_IMAGE, frames that are internal
+ * as ::JXL_DEC_FRAME and ::JXL_DEC_FULL_IMAGE, frames that are internal
* to the file format but are not rendered as part of an animation, or are not
* the final still frame of a still image, are not counted.
*
@@ -394,14 +408,14 @@ JXL_EXPORT void JxlDecoderSkipFrames(JxlDecoder* dec, size_t amount);
/**
* Skips processing the current frame. Can be called after frame processing
- * already started, signaled by a @ref JXL_DEC_NEED_IMAGE_OUT_BUFFER event,
- * but before the corresponding @ref JXL_DEC_FULL_IMAGE event. The next signaled
- * event will be another @ref JXL_DEC_FRAME, or @ref JXL_DEC_SUCCESS if there
+ * already started, signaled by a ::JXL_DEC_NEED_IMAGE_OUT_BUFFER event,
+ * but before the corresponding ::JXL_DEC_FULL_IMAGE event. The next signaled
+ * event will be another ::JXL_DEC_FRAME, or ::JXL_DEC_SUCCESS if there
* are no more frames. If pixel data is required from the already processed part
* of the frame, @ref JxlDecoderFlushImage must be called before this.
*
* @param dec decoder object
- * @return @ref JXL_DEC_SUCCESS if there is a frame to skip, and @ref
+ * @return ::JXL_DEC_SUCCESS if there is a frame to skip, and @ref
* JXL_DEC_ERROR if the function was not called during frame processing.
*/
JXL_EXPORT JxlDecoderStatus JxlDecoderSkipCurrentFrame(JxlDecoder* dec);
@@ -415,7 +429,7 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderSkipCurrentFrame(JxlDecoder* dec);
* be NULL to use the default, single-threaded, runner. A multithreaded
* runner should be set to reach fast performance.
* @param parallel_runner_opaque opaque pointer for parallel_runner.
- * @return @ref JXL_DEC_SUCCESS if the runner was set, @ref JXL_DEC_ERROR
+ * @return ::JXL_DEC_SUCCESS if the runner was set, ::JXL_DEC_ERROR
* otherwise (the previous runner remains set).
*/
JXL_EXPORT JxlDecoderStatus
@@ -439,7 +453,7 @@ JxlDecoderSetParallelRunner(JxlDecoder* dec, JxlParallelRunner parallel_runner,
*/
JXL_EXPORT size_t JxlDecoderSizeHintBasicInfo(const JxlDecoder* dec);
-/** Select for which informative events, i.e. @ref JXL_DEC_BASIC_INFO, etc., the
+/** Select for which informative events, i.e. ::JXL_DEC_BASIC_INFO, etc., the
* decoder should return with a status. It is not required to subscribe to any
* events, data can still be requested from the decoder as soon as it available.
* By default, the decoder is subscribed to no events (events_wanted == 0), and
@@ -449,7 +463,7 @@ JXL_EXPORT size_t JxlDecoderSizeHintBasicInfo(const JxlDecoder* dec);
*
* @param dec decoder object
* @param events_wanted bitfield of desired events.
- * @return @ref JXL_DEC_SUCCESS if no error, @ref JXL_DEC_ERROR otherwise.
+ * @return ::JXL_DEC_SUCCESS if no error, ::JXL_DEC_ERROR otherwise.
*/
JXL_EXPORT JxlDecoderStatus JxlDecoderSubscribeEvents(JxlDecoder* dec,
int events_wanted);
@@ -459,14 +473,14 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderSubscribeEvents(JxlDecoder* dec,
* indicating that the decoder must perform a rotation and/or
* mirroring to the encoded image data.
*
- * - If skip_reorientation is JXL_FALSE (the default): the decoder
+ * - If skip_reorientation is ::JXL_FALSE (the default): the decoder
* will apply the transformation from the orientation setting, hence
* rendering the image according to its specified intent. When
- * producing a JxlBasicInfo, the decoder will always set the
+ * producing a @ref JxlBasicInfo, the decoder will always set the
* orientation field to JXL_ORIENT_IDENTITY (matching the returned
* pixel data) and also align xsize and ysize so that they correspond
* to the width and the height of the returned pixel data.
- * - If skip_reorientation is JXL_TRUE: the decoder will skip
+ * - If skip_reorientation is ::JXL_TRUE "JXL_TRUE": the decoder will skip
* applying the transformation from the orientation setting, returning
* the image in the as-in-bitstream pixeldata orientation.
* This may be faster to decode since the decoder doesn't have to apply the
@@ -483,17 +497,18 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderSubscribeEvents(JxlDecoder* dec,
*
* @param dec decoder object
* @param skip_reorientation JXL_TRUE to enable, JXL_FALSE to disable.
- * @return @ref JXL_DEC_SUCCESS if no error, @ref JXL_DEC_ERROR otherwise.
+ * @return ::JXL_DEC_SUCCESS if no error, ::JXL_DEC_ERROR otherwise.
*/
JXL_EXPORT JxlDecoderStatus
JxlDecoderSetKeepOrientation(JxlDecoder* dec, JXL_BOOL skip_reorientation);
/**
* Enables or disables preserving of associated alpha channels. If
- * unpremul_alpha is set to JXL_FALSE then for associated alpha channel, the
- * pixel data is returned with premultiplied colors. If it is set to JXL_TRUE,
- * The colors will be unpremultiplied based on the alpha channel. This function
- * has no effect if the image does not have an associated alpha channel.
+ * unpremul_alpha is set to ::JXL_FALSE then for associated alpha channel,
+ * the pixel data is returned with premultiplied colors. If it is set to @ref
+ * JXL_TRUE, The colors will be unpremultiplied based on the alpha channel. This
+ * function has no effect if the image does not have an associated alpha
+ * channel.
*
* By default, this option is disabled, and the returned pixel data "as is".
*
@@ -501,20 +516,20 @@ JxlDecoderSetKeepOrientation(JxlDecoder* dec, JXL_BOOL skip_reorientation);
*
* @param dec decoder object
* @param unpremul_alpha JXL_TRUE to enable, JXL_FALSE to disable.
- * @return @ref JXL_DEC_SUCCESS if no error, @ref JXL_DEC_ERROR otherwise.
+ * @return ::JXL_DEC_SUCCESS if no error, ::JXL_DEC_ERROR otherwise.
*/
JXL_EXPORT JxlDecoderStatus
JxlDecoderSetUnpremultiplyAlpha(JxlDecoder* dec, JXL_BOOL unpremul_alpha);
/** Enables or disables rendering spot colors. By default, spot colors
* are rendered, which is OK for viewing the decoded image. If render_spotcolors
- * is JXL_FALSE, then spot colors are not rendered, and have to be retrieved
- * separately using @ref JxlDecoderSetExtraChannelBuffer. This is useful for
- * e.g. printing applications.
+ * is ::JXL_FALSE, then spot colors are not rendered, and have to be
+ * retrieved separately using @ref JxlDecoderSetExtraChannelBuffer. This is
+ * useful for e.g. printing applications.
*
* @param dec decoder object
* @param render_spotcolors JXL_TRUE to enable (default), JXL_FALSE to disable.
- * @return @ref JXL_DEC_SUCCESS if no error, @ref JXL_DEC_ERROR otherwise.
+ * @return ::JXL_DEC_SUCCESS if no error, ::JXL_DEC_ERROR otherwise.
*/
JXL_EXPORT JxlDecoderStatus
JxlDecoderSetRenderSpotcolors(JxlDecoder* dec, JXL_BOOL render_spotcolors);
@@ -530,7 +545,7 @@ JxlDecoderSetRenderSpotcolors(JxlDecoder* dec, JXL_BOOL render_spotcolors);
* @param dec decoder object
* @param coalescing JXL_TRUE to enable coalescing (default), JXL_FALSE to
* disable it.
- * @return @ref JXL_DEC_SUCCESS if no error, @ref JXL_DEC_ERROR otherwise.
+ * @return ::JXL_DEC_SUCCESS if no error, ::JXL_DEC_ERROR otherwise.
*/
JXL_EXPORT JxlDecoderStatus JxlDecoderSetCoalescing(JxlDecoder* dec,
JXL_BOOL coalescing);
@@ -547,32 +562,32 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderSetCoalescing(JxlDecoder* dec,
*
* The returned status indicates whether the decoder needs more input bytes, or
* more output buffer for a certain type of output data. No matter what the
- * returned status is (other than @ref JXL_DEC_ERROR), new information, such
+ * returned status is (other than ::JXL_DEC_ERROR), new information, such
* as @ref JxlDecoderGetBasicInfo, may have become available after this call.
- * When the return value is not @ref JXL_DEC_ERROR or @ref JXL_DEC_SUCCESS, the
+ * When the return value is not ::JXL_DEC_ERROR or ::JXL_DEC_SUCCESS, the
* decoding requires more @ref JxlDecoderProcessInput calls to continue.
*
* @param dec decoder object
- * @return @ref JXL_DEC_SUCCESS when decoding finished and all events handled.
+ * @return ::JXL_DEC_SUCCESS when decoding finished and all events handled.
* If you still have more unprocessed input data anyway, then you can still
* continue by using @ref JxlDecoderSetInput and calling @ref
* JxlDecoderProcessInput again, similar to handling @ref
- * JXL_DEC_NEED_MORE_INPUT. @ref JXL_DEC_SUCCESS can occur instead of @ref
+ * JXL_DEC_NEED_MORE_INPUT. ::JXL_DEC_SUCCESS can occur instead of @ref
* JXL_DEC_NEED_MORE_INPUT when, for example, the input data ended right at
* the boundary of a box of the container format, all essential codestream
* boxes were already decoded, but extra metadata boxes are still present in
* the next data. @ref JxlDecoderProcessInput cannot return success if all
* codestream boxes have not been seen yet.
- * @return @ref JXL_DEC_ERROR when decoding failed, e.g. invalid codestream.
+ * @return ::JXL_DEC_ERROR when decoding failed, e.g. invalid codestream.
* TODO(lode): document the input data mechanism
- * @return @ref JXL_DEC_NEED_MORE_INPUT when more input data is necessary.
- * @return @ref JXL_DEC_BASIC_INFO when basic info such as image dimensions is
+ * @return ::JXL_DEC_NEED_MORE_INPUT when more input data is necessary.
+ * @return ::JXL_DEC_BASIC_INFO when basic info such as image dimensions is
* available and this informative event is subscribed to.
- * @return @ref JXL_DEC_COLOR_ENCODING when color profile information is
+ * @return ::JXL_DEC_COLOR_ENCODING when color profile information is
* available and this informative event is subscribed to.
- * @return @ref JXL_DEC_PREVIEW_IMAGE when preview pixel information is
+ * @return ::JXL_DEC_PREVIEW_IMAGE when preview pixel information is
* available and output in the preview buffer.
- * @return @ref JXL_DEC_FULL_IMAGE when all pixel information at highest detail
+ * @return ::JXL_DEC_FULL_IMAGE when all pixel information at highest detail
* is available and has been output in the pixel buffer.
*/
JXL_EXPORT JxlDecoderStatus JxlDecoderProcessInput(JxlDecoder* dec);
@@ -588,8 +603,8 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderProcessInput(JxlDecoder* dec);
* @param dec decoder object
* @param data pointer to next bytes to read from
* @param size amount of bytes available starting from data
- * @return @ref JXL_DEC_ERROR if input was already set without releasing or @ref
- * JxlDecoderCloseInput was already called, @ref JXL_DEC_SUCCESS otherwise.
+ * @return ::JXL_DEC_ERROR if input was already set without releasing or @ref
+ * JxlDecoderCloseInput was already called, ::JXL_DEC_SUCCESS otherwise.
*/
JXL_EXPORT JxlDecoderStatus JxlDecoderSetInput(JxlDecoder* dec,
const uint8_t* data,
@@ -602,17 +617,17 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderSetInput(JxlDecoder* dec,
* whenever any input is already set and new input needs to be added with @ref
* JxlDecoderSetInput, but is not required before @ref JxlDecoderDestroy or @ref
* JxlDecoderReset. Calling @ref JxlDecoderReleaseInput when no input is set is
- * not an error and returns 0.
+ * not an error and returns `0`.
*
* @param dec decoder object
* @return The amount of bytes the decoder has not yet processed that are still
- * remaining in the data set by @ref JxlDecoderSetInput, or 0 if no input is
- * set or @ref JxlDecoderReleaseInput was already called. For a next call
- * to @ref JxlDecoderProcessInput, the buffer must start with these
- * unprocessed bytes. From this value it is possible to infer the position
- * of certain JPEG XL codestream elements (e.g. end of headers, frame
- * start/end). See the documentation of individual values of @ref
- * JxlDecoderStatus for more information.
+ * remaining in the data set by @ref JxlDecoderSetInput, or `0` if no input
+ * is set or @ref JxlDecoderReleaseInput was already called. For a next call to
+ * @ref JxlDecoderProcessInput, the buffer must start with these unprocessed
+ * bytes. From this value it is possible to infer the position of certain JPEG
+ * XL codestream elements (e.g. end of headers, frame start/end). See the
+ * documentation of individual values of @ref JxlDecoderStatus for more
+ * information.
*/
JXL_EXPORT size_t JxlDecoderReleaseInput(JxlDecoder* dec);
@@ -621,9 +636,9 @@ JXL_EXPORT size_t JxlDecoderReleaseInput(JxlDecoder* dec);
* will be called. This function allows the decoder to determine correctly if it
* should return success, need more input or error in certain cases. For
* backwards compatibility with a previous version of the API, using this
- * function is optional when not using the @ref JXL_DEC_BOX event (the decoder
+ * function is optional when not using the ::JXL_DEC_BOX event (the decoder
* is able to determine the end of the image frames without marking the end),
- * but using this function is required when using @ref JXL_DEC_BOX for getting
+ * but using this function is required when using ::JXL_DEC_BOX for getting
* metadata box contents. This function does not replace @ref
* JxlDecoderReleaseInput, that function should still be called if its return
* value is needed.
@@ -643,8 +658,8 @@ JXL_EXPORT void JxlDecoderCloseInput(JxlDecoder* dec);
* @param dec decoder object
* @param info struct to copy the information into, or NULL to only check
* whether the information is available through the return value.
- * @return @ref JXL_DEC_SUCCESS if the value is available, @ref
- * JXL_DEC_NEED_MORE_INPUT if not yet available, @ref JXL_DEC_ERROR
+ * @return ::JXL_DEC_SUCCESS if the value is available, @ref
+ * JXL_DEC_NEED_MORE_INPUT if not yet available, ::JXL_DEC_ERROR
* in case of other error conditions.
*/
JXL_EXPORT JxlDecoderStatus JxlDecoderGetBasicInfo(const JxlDecoder* dec,
@@ -652,14 +667,14 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderGetBasicInfo(const JxlDecoder* dec,
/**
* Outputs information for extra channel at the given index. The index must be
- * smaller than num_extra_channels in the associated JxlBasicInfo.
+ * smaller than num_extra_channels in the associated @ref JxlBasicInfo.
*
* @param dec decoder object
* @param index index of the extra channel to query.
* @param info struct to copy the information into, or NULL to only check
* whether the information is available through the return value.
- * @return @ref JXL_DEC_SUCCESS if the value is available, @ref
- * JXL_DEC_NEED_MORE_INPUT if not yet available, @ref JXL_DEC_ERROR
+ * @return ::JXL_DEC_SUCCESS if the value is available, @ref
+ * JXL_DEC_NEED_MORE_INPUT if not yet available, ::JXL_DEC_ERROR
* in case of other error conditions.
*/
JXL_EXPORT JxlDecoderStatus JxlDecoderGetExtraChannelInfo(
@@ -667,16 +682,16 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderGetExtraChannelInfo(
/**
* Outputs name for extra channel at the given index in UTF-8. The index must be
- * smaller than num_extra_channels in the associated JxlBasicInfo. The buffer
- * for name must have at least name_length + 1 bytes allocated, gotten from
- * the associated JxlExtraChannelInfo.
+ * smaller than `num_extra_channels` in the associated @ref JxlBasicInfo. The
+ * buffer for name must have at least `name_length + 1` bytes allocated, gotten
+ * from the associated @ref JxlExtraChannelInfo.
*
* @param dec decoder object
* @param index index of the extra channel to query.
* @param name buffer to copy the name into
* @param size size of the name buffer in bytes
- * @return @ref JXL_DEC_SUCCESS if the value is available, @ref
- * JXL_DEC_NEED_MORE_INPUT if not yet available, @ref JXL_DEC_ERROR
+ * @return ::JXL_DEC_SUCCESS if the value is available, @ref
+ * JXL_DEC_NEED_MORE_INPUT if not yet available, ::JXL_DEC_ERROR
* in case of other error conditions.
*/
JXL_EXPORT JxlDecoderStatus JxlDecoderGetExtraChannelName(const JxlDecoder* dec,
@@ -719,7 +734,7 @@ typedef enum {
* problematic, in that: while ICC profiles can encode a transfer function
* that happens to approximate those of PQ and HLG (HLG for only one given
* system gamma at a time, and necessitating a 3D LUT if gamma is to be
- * different from 1), they cannot (before ICCv4.4) semantically signal that
+ * different from `1`), they cannot (before ICCv4.4) semantically signal that
* this is the color space that they represent. Therefore, they will
* typically not actually be interpreted as representing an HDR color space.
* This is especially detrimental to PQ which will then be interpreted as if
@@ -741,8 +756,8 @@ typedef enum {
* or the color profile of the decoded pixels.
* @param color_encoding struct to copy the information into, or NULL to only
* check whether the information is available through the return value.
- * @return @ref JXL_DEC_SUCCESS if the data is available and returned, @ref
- * JXL_DEC_NEED_MORE_INPUT if not yet available, @ref JXL_DEC_ERROR in
+ * @return ::JXL_DEC_SUCCESS if the data is available and returned, @ref
+ * JXL_DEC_NEED_MORE_INPUT if not yet available, ::JXL_DEC_ERROR in
* case the encoded structured color profile does not exist in the
* codestream.
*/
@@ -766,10 +781,10 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderGetColorAsEncodedProfile(
* or the color profile of the decoded pixels.
* @param size variable to output the size into, or NULL to only check the
* return status.
- * @return @ref JXL_DEC_SUCCESS if the ICC profile is available, @ref
+ * @return ::JXL_DEC_SUCCESS if the ICC profile is available, @ref
* JXL_DEC_NEED_MORE_INPUT if the decoder has not yet received enough
* input data to determine whether an ICC profile is available or what its
- * size is, @ref JXL_DEC_ERROR in case the ICC profile is not available and
+ * size is, ::JXL_DEC_ERROR in case the ICC profile is not available and
* cannot be generated.
*/
JXL_EXPORT JxlDecoderStatus JxlDecoderGetICCProfileSize(
@@ -785,8 +800,8 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderGetICCProfileSize(
* or the color profile of the decoded pixels.
* @param icc_profile buffer to copy the ICC profile into
* @param size size of the icc_profile buffer in bytes
- * @return @ref JXL_DEC_SUCCESS if the profile was successfully returned is
- * available, @ref JXL_DEC_NEED_MORE_INPUT if not yet available, @ref
+ * @return ::JXL_DEC_SUCCESS if the profile was successfully returned is
+ * available, ::JXL_DEC_NEED_MORE_INPUT if not yet available, @ref
* JXL_DEC_ERROR if the profile doesn't exist or the output size is not
* large enough.
*/
@@ -801,7 +816,7 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderGetColorAsICCProfile(
*
* @param dec decoder object
* @param color_encoding the default color encoding to set
- * @return @ref JXL_DEC_SUCCESS if the preference was set successfully, @ref
+ * @return ::JXL_DEC_SUCCESS if the preference was set successfully, @ref
* JXL_DEC_ERROR otherwise.
*/
JXL_EXPORT JxlDecoderStatus JxlDecoderSetPreferredColorProfile(
@@ -814,7 +829,7 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderSetPreferredColorProfile(
* change from version to version.
* @param dec decoder object
* @param desired_intensity_target the intended target peak luminance
- * @return @ref JXL_DEC_SUCCESS if the preference was set successfully, @ref
+ * @return ::JXL_DEC_SUCCESS if the preference was set successfully, @ref
* JXL_DEC_ERROR otherwise.
*/
JXL_EXPORT JxlDecoderStatus JxlDecoderSetDesiredIntensityTarget(
@@ -823,7 +838,7 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderSetDesiredIntensityTarget(
/**
* Sets the desired output color profile of the decoded image either from a
* color encoding or an ICC profile. Valid calls of this function have either @c
- * color_encoding or @c icc_data set to NULL and @c icc_size must be 0 if and
+ * color_encoding or @c icc_data set to NULL and @c icc_size must be `0` if and
* only if @c icc_data is NULL.
*
* Depending on whether a color management system (CMS) has been set the
@@ -848,17 +863,17 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderSetDesiredIntensityTarget(
* If called with an ICC profile (after a call to @ref JxlDecoderSetCms), the
* ICC profile has to be a valid RGB or grayscale color profile.
*
- * Can only be set after the @ref JXL_DEC_COLOR_ENCODING event occurred and
+ * Can only be set after the ::JXL_DEC_COLOR_ENCODING event occurred and
* before any other event occurred, and should be used before getting
- * JXL_COLOR_PROFILE_TARGET_DATA.
+ * ::JXL_COLOR_PROFILE_TARGET_DATA.
*
- * This function must not be called before JxlDecoderSetCms.
+ * This function must not be called before @ref JxlDecoderSetCms.
*
* @param dec decoder orbject
* @param color_encoding the output color encoding
* @param icc_data bytes of the icc profile
* @param icc_size size of the icc profile in bytes
- * @return @ref JXL_DEC_SUCCESS if the color profile was set successfully, @ref
+ * @return ::JXL_DEC_SUCCESS if the color profile was set successfully, @ref
* JXL_DEC_ERROR otherwise.
*/
JXL_EXPORT JxlDecoderStatus JxlDecoderSetOutputColorProfile(
@@ -891,7 +906,7 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderSetCms(JxlDecoder* dec,
* @param dec decoder object
* @param format format of pixels
* @param size output value, buffer size in bytes
- * @return @ref JXL_DEC_SUCCESS on success, @ref JXL_DEC_ERROR on error, such as
+ * @return ::JXL_DEC_SUCCESS on success, ::JXL_DEC_ERROR on error, such as
* information not available yet.
*/
JXL_EXPORT JxlDecoderStatus JxlDecoderPreviewOutBufferSize(
@@ -901,15 +916,15 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderPreviewOutBufferSize(
* Sets the buffer to write the small resolution preview image
* to. The size of the buffer must be at least as large as given by @ref
* JxlDecoderPreviewOutBufferSize. The buffer follows the format described
- * by JxlPixelFormat. The preview image dimensions are given by the
- * JxlPreviewHeader. The buffer is owned by the caller.
+ * by @ref JxlPixelFormat. The preview image dimensions are given by the
+ * @ref JxlPreviewHeader. The buffer is owned by the caller.
*
* @param dec decoder object
* @param format format of pixels. Object owned by user and its contents are
* copied internally.
* @param buffer buffer type to output the pixel data to
* @param size size of buffer in bytes
- * @return @ref JXL_DEC_SUCCESS on success, @ref JXL_DEC_ERROR on error, such as
+ * @return ::JXL_DEC_SUCCESS on success, ::JXL_DEC_ERROR on error, such as
* size too small.
*/
JXL_EXPORT JxlDecoderStatus JxlDecoderSetPreviewOutBuffer(
@@ -917,14 +932,14 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderSetPreviewOutBuffer(
/**
* Outputs the information from the frame, such as duration when have_animation.
- * This function can be called when @ref JXL_DEC_FRAME occurred for the current
+ * This function can be called when ::JXL_DEC_FRAME occurred for the current
* frame, even when have_animation in the JxlBasicInfo is JXL_FALSE.
*
* @param dec decoder object
* @param header struct to copy the information into, or NULL to only check
* whether the information is available through the return value.
- * @return @ref JXL_DEC_SUCCESS if the value is available, @ref
- * JXL_DEC_NEED_MORE_INPUT if not yet available, @ref JXL_DEC_ERROR in
+ * @return ::JXL_DEC_SUCCESS if the value is available, @ref
+ * JXL_DEC_NEED_MORE_INPUT if not yet available, ::JXL_DEC_ERROR in
* case of other error conditions.
*/
JXL_EXPORT JxlDecoderStatus JxlDecoderGetFrameHeader(const JxlDecoder* dec,
@@ -932,14 +947,14 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderGetFrameHeader(const JxlDecoder* dec,
/**
* Outputs name for the current frame. The buffer for name must have at least
- * name_length + 1 bytes allocated, gotten from the associated JxlFrameHeader.
+ * `name_length + 1` bytes allocated, gotten from the associated JxlFrameHeader.
*
* @param dec decoder object
* @param name buffer to copy the name into
* @param size size of the name buffer in bytes, including zero termination
- * character, so this must be at least JxlFrameHeader.name_length + 1.
- * @return @ref JXL_DEC_SUCCESS if the value is available, @ref
- * JXL_DEC_NEED_MORE_INPUT if not yet available, @ref JXL_DEC_ERROR in
+ * character, so this must be at least @ref JxlFrameHeader.name_length + 1.
+ * @return ::JXL_DEC_SUCCESS if the value is available, @ref
+ * JXL_DEC_NEED_MORE_INPUT if not yet available, ::JXL_DEC_ERROR in
* case of other error conditions.
*/
JXL_EXPORT JxlDecoderStatus JxlDecoderGetFrameName(const JxlDecoder* dec,
@@ -947,15 +962,15 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderGetFrameName(const JxlDecoder* dec,
/**
* Outputs the blend information for the current frame for a specific extra
- * channel. This function can be called when @ref JXL_DEC_FRAME occurred for the
- * current frame, even when have_animation in the JxlBasicInfo is JXL_FALSE.
- * This information is only useful if coalescing is disabled; otherwise the
- * decoder will have performed blending already.
+ * channel. This function can be called when ::JXL_DEC_FRAME occurred for the
+ * current frame, even when have_animation in the @ref JxlBasicInfo is @ref
+ * JXL_FALSE. This information is only useful if coalescing is disabled;
+ * otherwise the decoder will have performed blending already.
*
* @param dec decoder object
* @param index the index of the extra channel
* @param blend_info struct to copy the information into
- * @return @ref JXL_DEC_SUCCESS on success, @ref JXL_DEC_ERROR on error
+ * @return ::JXL_DEC_SUCCESS on success, ::JXL_DEC_ERROR on error
*/
JXL_EXPORT JxlDecoderStatus JxlDecoderGetExtraChannelBlendInfo(
const JxlDecoder* dec, size_t index, JxlBlendInfo* blend_info);
@@ -965,14 +980,14 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderGetExtraChannelBlendInfo(
* given format. This is the buffer for @ref JxlDecoderSetImageOutBuffer.
* Requires that the basic image information is available in the decoder in the
* case of coalescing enabled (default). In case coalescing is disabled, this
- * can only be called after the @ref JXL_DEC_FRAME event occurs. In that case,
+ * can only be called after the ::JXL_DEC_FRAME event occurs. In that case,
* it will return the size required to store the possibly cropped frame (which
* can be larger or smaller than the image dimensions).
*
* @param dec decoder object
* @param format format of the pixels.
* @param size output value, buffer size in bytes
- * @return @ref JXL_DEC_SUCCESS on success, @ref JXL_DEC_ERROR on error, such as
+ * @return ::JXL_DEC_SUCCESS on success, ::JXL_DEC_ERROR on error, such as
* information not available yet.
*/
JXL_EXPORT JxlDecoderStatus JxlDecoderImageOutBufferSize(
@@ -980,18 +995,18 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderImageOutBufferSize(
/**
* Sets the buffer to write the full resolution image to. This can be set when
- * the @ref JXL_DEC_FRAME event occurs, must be set when the @ref
+ * the ::JXL_DEC_FRAME event occurs, must be set when the @ref
* JXL_DEC_NEED_IMAGE_OUT_BUFFER event occurs, and applies only for the
* current frame. The size of the buffer must be at least as large as given
* by @ref JxlDecoderImageOutBufferSize. The buffer follows the format described
- * by JxlPixelFormat. The buffer is owned by the caller.
+ * by @ref JxlPixelFormat. The buffer is owned by the caller.
*
* @param dec decoder object
* @param format format of the pixels. Object owned by user and its contents
* are copied internally.
* @param buffer buffer type to output the pixel data to
* @param size size of buffer in bytes
- * @return @ref JXL_DEC_SUCCESS on success, @ref JXL_DEC_ERROR on error, such as
+ * @return ::JXL_DEC_SUCCESS on success, ::JXL_DEC_ERROR on error, such as
* size too small.
*/
JXL_EXPORT JxlDecoderStatus JxlDecoderSetImageOutBuffer(
@@ -1062,15 +1077,15 @@ typedef void (*JxlImageOutDestroyCallback)(void* run_opaque);
/**
* Sets pixel output callback. This is an alternative to @ref
- * JxlDecoderSetImageOutBuffer. This can be set when the @ref JXL_DEC_FRAME
- * event occurs, must be set when the @ref JXL_DEC_NEED_IMAGE_OUT_BUFFER event
+ * JxlDecoderSetImageOutBuffer. This can be set when the ::JXL_DEC_FRAME
+ * event occurs, must be set when the ::JXL_DEC_NEED_IMAGE_OUT_BUFFER event
* occurs, and applies only for the current frame. Only one of @ref
* JxlDecoderSetImageOutBuffer or @ref JxlDecoderSetImageOutCallback may be used
* for the same frame, not both at the same time.
*
* The callback will be called multiple times, to receive the image
* data in small chunks. The callback receives a horizontal stripe of pixel
- * data, 1 pixel high, xsize pixels wide, called a scanline. The xsize here is
+ * data, `1` pixel high, xsize pixels wide, called a scanline. The xsize here is
* not the same as the full image width, the scanline may be a partial section,
* and xsize may differ between calls. The user can then process and/or copy the
* partial scanline to an image buffer. The callback may be called
@@ -1099,7 +1114,7 @@ typedef void (*JxlImageOutDestroyCallback)(void* run_opaque);
* data.
* @param opaque optional user data, which will be passed on to the callback,
* may be NULL.
- * @return @ref JXL_DEC_SUCCESS on success, @ref JXL_DEC_ERROR on error, such
+ * @return ::JXL_DEC_SUCCESS on success, ::JXL_DEC_ERROR on error, such
* as @ref JxlDecoderSetImageOutBuffer already set.
*/
JXL_EXPORT JxlDecoderStatus
@@ -1122,7 +1137,7 @@ JxlDecoderSetImageOutCallback(JxlDecoder* dec, const JxlPixelFormat* format,
* @param init_opaque optional user data passed to @c init_callback, may be NULL
* (unlike the return value from @c init_callback which may only be NULL if
* initialization failed).
- * @return @ref JXL_DEC_SUCCESS on success, @ref JXL_DEC_ERROR on error, such
+ * @return ::JXL_DEC_SUCCESS on success, ::JXL_DEC_ERROR on error, such
* as @ref JxlDecoderSetImageOutBuffer having already been called.
*/
JXL_EXPORT JxlDecoderStatus JxlDecoderSetMultithreadedImageOutCallback(
@@ -1137,12 +1152,12 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderSetMultithreadedImageOutCallback(
*
* @param dec decoder object
* @param format format of the pixels. The num_channels value is ignored and is
- * always treated to be 1.
+ * always treated to be `1`.
* @param size output value, buffer size in bytes
* @param index which extra channel to get, matching the index used in @ref
* JxlDecoderGetExtraChannelInfo. Must be smaller than num_extra_channels in
- * the associated JxlBasicInfo.
- * @return @ref JXL_DEC_SUCCESS on success, @ref JXL_DEC_ERROR on error, such as
+ * the associated @ref JxlBasicInfo.
+ * @return ::JXL_DEC_SUCCESS on success, ::JXL_DEC_ERROR on error, such as
* information not available yet or invalid index.
*/
JXL_EXPORT JxlDecoderStatus JxlDecoderExtraChannelBufferSize(
@@ -1151,13 +1166,13 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderExtraChannelBufferSize(
/**
* Sets the buffer to write an extra channel to. This can be set when
- * the @ref JXL_DEC_FRAME or @ref JXL_DEC_NEED_IMAGE_OUT_BUFFER event occurs,
+ * the ::JXL_DEC_FRAME or ::JXL_DEC_NEED_IMAGE_OUT_BUFFER event occurs,
* and applies only for the current frame. The size of the buffer must be at
* least as large as given by @ref JxlDecoderExtraChannelBufferSize. The buffer
- * follows the format described by JxlPixelFormat, but where num_channels is 1.
- * The buffer is owned by the caller. The amount of extra channels is given by
- * the num_extra_channels field in the associated JxlBasicInfo, and the
- * information of individual extra channels can be queried with @ref
+ * follows the format described by @ref JxlPixelFormat, but where num_channels
+ * is `1`. The buffer is owned by the caller. The amount of extra channels is
+ * given by the num_extra_channels field in the associated @ref JxlBasicInfo,
+ * and the information of individual extra channels can be queried with @ref
* JxlDecoderGetExtraChannelInfo. To get multiple extra channels, this function
* must be called multiple times, once for each wanted index. Not all images
* have extra channels. The alpha channel is an extra channel and can be gotten
@@ -1170,13 +1185,13 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderExtraChannelBufferSize(
* @param dec decoder object
* @param format format of the pixels. Object owned by user and its contents
* are copied internally. The num_channels value is ignored and is always
- * treated to be 1.
+ * treated to be `1`.
* @param buffer buffer type to output the pixel data to
* @param size size of buffer in bytes
* @param index which extra channel to get, matching the index used in @ref
* JxlDecoderGetExtraChannelInfo. Must be smaller than num_extra_channels in
- * the associated JxlBasicInfo.
- * @return @ref JXL_DEC_SUCCESS on success, @ref JXL_DEC_ERROR on error, such as
+ * the associated @ref JxlBasicInfo.
+ * @return ::JXL_DEC_SUCCESS on success, ::JXL_DEC_ERROR on error, such as
* size too small or invalid index.
*/
JXL_EXPORT JxlDecoderStatus
@@ -1197,8 +1212,8 @@ JxlDecoderSetExtraChannelBuffer(JxlDecoder* dec, const JxlPixelFormat* format,
* @param dec decoder object
* @param data pointer to next bytes to write to
* @param size amount of bytes available starting from data
- * @return @ref JXL_DEC_ERROR if output buffer was already set and @ref
- * JxlDecoderReleaseJPEGBuffer was not called on it, @ref JXL_DEC_SUCCESS
+ * @return ::JXL_DEC_ERROR if output buffer was already set and @ref
+ * JxlDecoderReleaseJPEGBuffer was not called on it, ::JXL_DEC_SUCCESS
* otherwise
*/
JXL_EXPORT JxlDecoderStatus JxlDecoderSetJPEGBuffer(JxlDecoder* dec,
@@ -1213,11 +1228,11 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderSetJPEGBuffer(JxlDecoder* dec,
* JxlDecoderDestroy or @ref JxlDecoderReset.
*
* Calling @ref JxlDecoderReleaseJPEGBuffer when no buffer is set is
- * not an error and returns 0.
+ * not an error and returns `0`.
*
* @param dec decoder object
* @return the amount of bytes the decoder has not yet written to of the data
- * set by @ref JxlDecoderSetJPEGBuffer, or 0 if no buffer is set or @ref
+ * set by @ref JxlDecoderSetJPEGBuffer, or `0` if no buffer is set or @ref
* JxlDecoderReleaseJPEGBuffer was already called.
*/
JXL_EXPORT size_t JxlDecoderReleaseJPEGBuffer(JxlDecoder* dec);
@@ -1233,15 +1248,15 @@ JXL_EXPORT size_t JxlDecoderReleaseJPEGBuffer(JxlDecoder* dec);
* JxlDecoderReleaseBoxBuffer, bytes that the decoder has already output
* should not be included, only the remaining bytes output must be set.
*
- * The @ref JxlDecoderReleaseBoxBuffer must be used at the next @ref JXL_DEC_BOX
- * event or final @ref JXL_DEC_SUCCESS event to compute the size of the output
+ * The @ref JxlDecoderReleaseBoxBuffer must be used at the next ::JXL_DEC_BOX
+ * event or final ::JXL_DEC_SUCCESS event to compute the size of the output
* box bytes.
*
* @param dec decoder object
* @param data pointer to next bytes to write to
* @param size amount of bytes available starting from data
- * @return @ref JXL_DEC_ERROR if output buffer was already set and @ref
- * JxlDecoderReleaseBoxBuffer was not called on it, @ref JXL_DEC_SUCCESS
+ * @return ::JXL_DEC_ERROR if output buffer was already set and @ref
+ * JxlDecoderReleaseBoxBuffer was not called on it, ::JXL_DEC_SUCCESS
* otherwise
*/
JXL_EXPORT JxlDecoderStatus JxlDecoderSetBoxBuffer(JxlDecoder* dec,
@@ -1256,11 +1271,11 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderSetBoxBuffer(JxlDecoder* dec,
* JxlDecoderDestroy or @ref JxlDecoderReset.
*
* Calling @ref JxlDecoderReleaseBoxBuffer when no buffer is set is
- * not an error and returns 0.
+ * not an error and returns `0`.
*
* @param dec decoder object
* @return the amount of bytes the decoder has not yet written to of the data
- * set by @ref JxlDecoderSetBoxBuffer, or 0 if no buffer is set or @ref
+ * set by @ref JxlDecoderSetBoxBuffer, or `0` if no buffer is set or @ref
* JxlDecoderReleaseBoxBuffer was already called.
*/
JXL_EXPORT size_t JxlDecoderReleaseBoxBuffer(JxlDecoder* dec);
@@ -1274,23 +1289,23 @@ JXL_EXPORT size_t JxlDecoderReleaseBoxBuffer(JxlDecoder* dec);
* finished.
*
* The default mode is raw. This setting can only be changed before decoding, or
- * directly after a @ref JXL_DEC_BOX event, and is remembered until the decoder
+ * directly after a ::JXL_DEC_BOX event, and is remembered until the decoder
* is reset or destroyed.
*
* Enabling decompressed mode requires Brotli support from the library.
*
* @param dec decoder object
- * @param decompress JXL_TRUE to transparently decompress, JXL_FALSE to get
- * boxes in raw mode.
- * @return @ref JXL_DEC_ERROR if decompressed mode is set and Brotli is not
- * available, @ref JXL_DEC_SUCCESS otherwise.
+ * @param decompress ::JXL_TRUE to transparently decompress, ::JXL_FALSE
+ * to get boxes in raw mode.
+ * @return ::JXL_DEC_ERROR if decompressed mode is set and Brotli is not
+ * available, ::JXL_DEC_SUCCESS otherwise.
*/
JXL_EXPORT JxlDecoderStatus JxlDecoderSetDecompressBoxes(JxlDecoder* dec,
JXL_BOOL decompress);
/**
- * Outputs the type of the current box, after a @ref JXL_DEC_BOX event occurred,
- * as 4 characters without null termination character. In case of a compressed
+ * Outputs the type of the current box, after a ::JXL_DEC_BOX event occurred,
+ * as `4` characters without null termination character. In case of a compressed
* "brob" box, this will return "brob" if the decompressed argument is
* JXL_FALSE, or the underlying box type if the decompressed argument is
* JXL_TRUE.
@@ -1306,15 +1321,15 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderSetDecompressBoxes(JxlDecoder* dec,
* - "xml ": a box with XML data, in particular XMP metadata.
* - "jumb": a JUMBF superbox (JPEG Universal Metadata Box Format, ISO/IEC
* 19566-5).
- * - "JXL ": mandatory signature box, must come first, 12 bytes long including
- * the box header
- * - "ftyp": a second mandatory signature box, must come second, 20 bytes long
- * including the box header
- * - "jxll": a JXL level box. This indicates if the codestream is level 5 or
- * level 10 compatible. If not present, it is level 5. Level 10 allows more
- * features such as very high image resolution and bit-depths above 16 bits
- * per channel. Added automatically by the encoder when
- * JxlEncoderSetCodestreamLevel is used
+ * - "JXL ": mandatory signature box, must come first, `12` bytes long
+ * including the box header
+ * - "ftyp": a second mandatory signature box, must come second, `20` bytes
+ * long including the box header
+ * - "jxll": a JXL level box. This indicates if the codestream is level `5` or
+ * level `10` compatible. If not present, it is level `5`. Level `10` allows
+ * more features such as very high image resolution and bit-depths above `16`
+ * bits per channel. Added automatically by the encoder when
+ * @ref JxlEncoderSetCodestreamLevel is used
* - "jxlc": a box with the image codestream, in case the codestream is not
* split across multiple boxes. The codestream contains the JPEG XL image
* itself, including the basic info such as image dimensions, ICC color
@@ -1350,7 +1365,7 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderSetDecompressBoxes(JxlDecoder* dec,
* @param type buffer to copy the type into
* @param decompressed which box type to get: JXL_FALSE to get the raw box type,
* which can be "brob", JXL_TRUE, get the underlying box type.
- * @return @ref JXL_DEC_SUCCESS if the value is available, @ref JXL_DEC_ERROR if
+ * @return ::JXL_DEC_SUCCESS if the value is available, ::JXL_DEC_ERROR if
* not, for example the JXL file does not use the container format.
*/
JXL_EXPORT JxlDecoderStatus JxlDecoderGetBoxType(JxlDecoder* dec,
@@ -1363,13 +1378,29 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderGetBoxType(JxlDecoder* dec,
*
* @param dec decoder object
* @param size raw size of the box in bytes
- * @return @ref JXL_DEC_ERROR if no box size is available, @ref JXL_DEC_SUCCESS
+ * @return ::JXL_DEC_ERROR if no box size is available, ::JXL_DEC_SUCCESS
* otherwise.
*/
JXL_EXPORT JxlDecoderStatus JxlDecoderGetBoxSizeRaw(const JxlDecoder* dec,
uint64_t* size);
/**
+ * Returns the size of the contents of a box, after the @ref
+ * JXL_DEC_BOX event. This does not include any of the headers of the box. For
+ * compressed "brob" boxes, this is the size of the compressed content. Even
+ * when @ref JxlDecoderSetDecompressBoxes is enabled, the return value of
+ * function does not change, and the decompressed size is not known before it
+ * has already been decompressed and output.
+ *
+ * @param dec decoder object
+ * @param size size of the payload of the box in bytes
+ * @return @ref JXL_DEC_ERROR if no box size is available, @ref JXL_DEC_SUCCESS
+ * otherwise.
+ */
+JXL_EXPORT JxlDecoderStatus JxlDecoderGetBoxSizeContents(const JxlDecoder* dec,
+ uint64_t* size);
+
+/**
* Configures at which progressive steps in frame decoding these @ref
* JXL_DEC_FRAME_PROGRESSION event occurs. The default value for the level
* of detail if this function is never called is `kDC`.
@@ -1377,7 +1408,7 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderGetBoxSizeRaw(const JxlDecoder* dec,
* @param dec decoder object
* @param detail at which level of detail to trigger @ref
* JXL_DEC_FRAME_PROGRESSION
- * @return @ref JXL_DEC_SUCCESS on success, @ref JXL_DEC_ERROR on error, such as
+ * @return ::JXL_DEC_SUCCESS on success, ::JXL_DEC_ERROR on error, such as
* an invalid value for the progressive detail.
*/
JXL_EXPORT JxlDecoderStatus
@@ -1385,11 +1416,11 @@ JxlDecoderSetProgressiveDetail(JxlDecoder* dec, JxlProgressiveDetail detail);
/**
* Returns the intended downsampling ratio for the progressive frame produced
- * by @ref JxlDecoderFlushImage after the latest @ref JXL_DEC_FRAME_PROGRESSION
+ * by @ref JxlDecoderFlushImage after the latest ::JXL_DEC_FRAME_PROGRESSION
* event.
*
* @param dec decoder object
- * @return The intended downsampling ratio, can be 1, 2, 4 or 8.
+ * @return The intended downsampling ratio, can be `1`, `2`, `4` or `8`.
*/
JXL_EXPORT size_t JxlDecoderGetIntendedDownsamplingRatio(JxlDecoder* dec);
@@ -1399,12 +1430,12 @@ JXL_EXPORT size_t JxlDecoderGetIntendedDownsamplingRatio(JxlDecoder* dec);
* JxlDecoderSetImageOutBuffer will contain partial image data.
*
* Can be called when @ref JxlDecoderProcessInput returns @ref
- * JXL_DEC_NEED_MORE_INPUT, after the @ref JXL_DEC_FRAME event already occurred
- * and before the @ref JXL_DEC_FULL_IMAGE event occurred for a frame.
+ * JXL_DEC_NEED_MORE_INPUT, after the ::JXL_DEC_FRAME event already occurred
+ * and before the ::JXL_DEC_FULL_IMAGE event occurred for a frame.
*
* @param dec decoder object
- * @return @ref JXL_DEC_SUCCESS if image data was flushed to the output buffer,
- * or @ref JXL_DEC_ERROR when no flush was done, e.g. if not enough image
+ * @return ::JXL_DEC_SUCCESS if image data was flushed to the output buffer,
+ * or ::JXL_DEC_ERROR when no flush was done, e.g. if not enough image
* data was available yet even for flush, or no output buffer was set yet.
* This error is not fatal, it only indicates no flushed image is available
* right now. Regular decoding can still be performed.
@@ -1416,11 +1447,11 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderFlushImage(JxlDecoder* dec);
*
* Can be called after @ref JxlDecoderSetImageOutBuffer or @ref
* JxlDecoderSetImageOutCallback. For float pixel data types, only the default
- * @ref JXL_BIT_DEPTH_FROM_PIXEL_FORMAT setting is supported.
+ * ::JXL_BIT_DEPTH_FROM_PIXEL_FORMAT setting is supported.
*
* @param dec decoder object
* @param bit_depth the bit depth setting of the pixel output
- * @return @ref JXL_DEC_SUCCESS on success, @ref JXL_DEC_ERROR on error, such as
+ * @return ::JXL_DEC_SUCCESS on success, ::JXL_DEC_ERROR on error, such as
* incompatible custom bit depth and pixel data type.
*/
JXL_EXPORT JxlDecoderStatus
diff --git a/third_party/jpeg-xl/lib/include/jxl/encode.h b/third_party/jpeg-xl/lib/include/jxl/encode.h
index 2a87821f06..bb11dd7572 100644
--- a/third_party/jpeg-xl/lib/include/jxl/encode.h
+++ b/third_party/jpeg-xl/lib/include/jxl/encode.h
@@ -41,8 +41,8 @@ JXL_EXPORT uint32_t JxlEncoderVersion(void);
/**
* Opaque structure that holds the JPEG XL encoder.
*
- * Allocated and initialized with JxlEncoderCreate().
- * Cleaned up and deallocated with JxlEncoderDestroy().
+ * Allocated and initialized with @ref JxlEncoderCreate().
+ * Cleaned up and deallocated with @ref JxlEncoderDestroy().
*/
typedef struct JxlEncoderStruct JxlEncoder;
@@ -50,9 +50,9 @@ typedef struct JxlEncoderStruct JxlEncoder;
* Settings and metadata for a single image frame. This includes encoder options
* for a frame such as compression quality and speed.
*
- * Allocated and initialized with JxlEncoderFrameSettingsCreate().
+ * Allocated and initialized with @ref JxlEncoderFrameSettingsCreate().
* Cleaned up and deallocated when the encoder is destroyed with
- * JxlEncoderDestroy().
+ * @ref JxlEncoderDestroy().
*/
typedef struct JxlEncoderFrameSettingsStruct JxlEncoderFrameSettings;
@@ -126,7 +126,7 @@ typedef enum {
typedef enum {
/** Sets encoder effort/speed level without affecting decoding speed. Valid
* values are, from faster to slower speed: 1:lightning 2:thunder 3:falcon
- * 4:cheetah 5:hare 6:wombat 7:squirrel 8:kitten 9:tortoise.
+ * 4:cheetah 5:hare 6:wombat 7:squirrel 8:kitten 9:tortoise 10:glacier.
* Default: squirrel (7).
*/
JXL_ENC_FRAME_SETTING_EFFORT = 0,
@@ -145,7 +145,7 @@ typedef enum {
*/
JXL_ENC_FRAME_SETTING_RESAMPLING = 2,
- /** Similar to JXL_ENC_FRAME_SETTING_RESAMPLING, but for extra channels.
+ /** Similar to ::JXL_ENC_FRAME_SETTING_RESAMPLING, but for extra channels.
* Integer option, use -1 for the default behavior (depends on encoder
* implementation), 1 for no downsampling (1x1), 2 for 2x2 downsampling, 4 for
* 4x4 downsampling, 8 for 8x8 downsampling.
@@ -158,7 +158,7 @@ typedef enum {
* downsampled resolution, not the full image resolution. The downsampled
* resolution is given by ceil(xsize / resampling), ceil(ysize / resampling)
* with xsize and ysize the dimensions given in the basic info, and resampling
- * the factor set with @ref JXL_ENC_FRAME_SETTING_RESAMPLING.
+ * the factor set with ::JXL_ENC_FRAME_SETTING_RESAMPLING.
* Use 0 to disable, 1 to enable. Default value is 0.
*/
JXL_ENC_FRAME_SETTING_ALREADY_DOWNSAMPLED = 4,
@@ -171,8 +171,8 @@ typedef enum {
JXL_ENC_FRAME_SETTING_PHOTON_NOISE = 5,
/** Enables adaptive noise generation. This setting is not recommended for
- * use, please use JXL_ENC_FRAME_SETTING_PHOTON_NOISE instead. Use -1 for the
- * default (encoder chooses), 0 to disable, 1 to enable.
+ * use, please use ::JXL_ENC_FRAME_SETTING_PHOTON_NOISE instead. Use -1 for
+ * the default (encoder chooses), 0 to disable, 1 to enable.
*/
JXL_ENC_FRAME_SETTING_NOISE = 6,
@@ -320,27 +320,28 @@ typedef enum {
* 1 = index this frame within the Frame Index Box.
* If any frames are indexed, the first frame needs to
* be indexed, too. If the first frame is not indexed, and
- * a later frame is attempted to be indexed, JXL_ENC_ERROR will occur.
+ * a later frame is attempted to be indexed, ::JXL_ENC_ERROR will occur.
* If non-keyframes, i.e., frames with cropping, blending or patches are
- * attempted to be indexed, JXL_ENC_ERROR will occur.
+ * attempted to be indexed, ::JXL_ENC_ERROR will occur.
*/
JXL_ENC_FRAME_INDEX_BOX = 31,
- /** Sets brotli encode effort for use in JPEG recompression and compressed
- * metadata boxes (brob). Can be -1 (default) or 0 (fastest) to 11 (slowest).
- * Default is based on the general encode effort in case of JPEG
+ /** Sets brotli encode effort for use in JPEG recompression and
+ * compressed metadata boxes (brob). Can be -1 (default) or 0 (fastest) to 11
+ * (slowest). Default is based on the general encode effort in case of JPEG
* recompression, and 4 for brob boxes.
*/
JXL_ENC_FRAME_SETTING_BROTLI_EFFORT = 32,
/** Enables or disables brotli compression of metadata boxes derived from
- * a JPEG frame when using JxlEncoderAddJPEGFrame. This has no effect on boxes
- * added using JxlEncoderAddBox.
- * -1 = default, 0 = disable compression, 1 = enable compression.
+ * a JPEG frame when using @ref JxlEncoderAddJPEGFrame. This has no effect on
+ * boxes added using @ref JxlEncoderAddBox. -1 = default, 0 = disable
+ * compression, 1 = enable compression.
*/
JXL_ENC_FRAME_SETTING_JPEG_COMPRESS_BOXES = 33,
/** Control what kind of buffering is used, when using chunked image frames.
+ * -1 = default (let the encoder decide)
* 0 = buffers everything, basically the same as non-streamed code path
(mainly for testing)
* 1 = buffers everything for images that are smaller than 2048 x 2048, and
@@ -351,30 +352,30 @@ typedef enum {
*
* When using streaming input and output the encoder minimizes memory usage at
* the cost of compression density. Also note that images produced with
- * streaming mode might can not be decoded progressively.
+ * streaming mode might not be progressively decodeable.
*/
JXL_ENC_FRAME_SETTING_BUFFERING = 34,
/** Keep or discard Exif metadata boxes derived from a JPEG frame when using
- * JxlEncoderAddJPEGFrame. This has no effect on boxes added using
- * JxlEncoderAddBox. When JxlEncoderStoreJPEGMetadata is set to 1, this option
- * cannot be set to 0. Even when Exif metadata is discarded, the orientation
- * will still be applied. 0 = discard Exif metadata, 1 = keep Exif metadata
- * (default).
+ * @ref JxlEncoderAddJPEGFrame. This has no effect on boxes added using
+ * @ref JxlEncoderAddBox. When @ref JxlEncoderStoreJPEGMetadata is set to 1,
+ * this option cannot be set to 0. Even when Exif metadata is discarded, the
+ * orientation will still be applied. 0 = discard Exif metadata, 1 = keep Exif
+ * metadata (default).
*/
JXL_ENC_FRAME_SETTING_JPEG_KEEP_EXIF = 35,
/** Keep or discard XMP metadata boxes derived from a JPEG frame when using
- * JxlEncoderAddJPEGFrame. This has no effect on boxes added using
- * JxlEncoderAddBox. When JxlEncoderStoreJPEGMetadata is set to 1, this option
- * cannot be set to 0. 0 = discard XMP metadata, 1 = keep XMP metadata
- * (default).
+ * @ref JxlEncoderAddJPEGFrame. This has no effect on boxes added using
+ * @ref JxlEncoderAddBox. When @ref JxlEncoderStoreJPEGMetadata is set to 1,
+ * this option cannot be set to 0. 0 = discard XMP metadata, 1 = keep XMP
+ * metadata (default).
*/
JXL_ENC_FRAME_SETTING_JPEG_KEEP_XMP = 36,
/** Keep or discard JUMBF metadata boxes derived from a JPEG frame when using
- * JxlEncoderAddJPEGFrame. This has no effect on boxes added using
- * JxlEncoderAddBox. 0 = discard JUMBF metadata, 1 = keep JUMBF metadata
+ * @ref JxlEncoderAddJPEGFrame. This has no effect on boxes added using
+ * @ref JxlEncoderAddBox. 0 = discard JUMBF metadata, 1 = keep JUMBF metadata
* (default).
*/
JXL_ENC_FRAME_SETTING_JPEG_KEEP_JUMBF = 37,
@@ -395,7 +396,7 @@ typedef enum {
} JxlEncoderFrameSettingId;
/**
- * Creates an instance of JxlEncoder and initializes it.
+ * Creates an instance of @ref JxlEncoder and initializes it.
*
* @p memory_manager will be used for all the library dynamic allocations made
* from this instance. The parameter may be NULL, in which case the default
@@ -404,21 +405,21 @@ typedef enum {
* @param memory_manager custom allocator function. It may be NULL. The memory
* manager will be copied internally.
* @return @c NULL if the instance can not be allocated or initialized
- * @return pointer to initialized JxlEncoder otherwise
+ * @return pointer to initialized @ref JxlEncoder otherwise
*/
JXL_EXPORT JxlEncoder* JxlEncoderCreate(const JxlMemoryManager* memory_manager);
/**
- * Re-initializes a JxlEncoder instance, so it can be re-used for encoding
+ * Re-initializes a @ref JxlEncoder instance, so it can be re-used for encoding
* another image. All state and settings are reset as if the object was
- * newly created with JxlEncoderCreate, but the memory manager is kept.
+ * newly created with @ref JxlEncoderCreate, but the memory manager is kept.
*
* @param enc instance to be re-initialized.
*/
JXL_EXPORT void JxlEncoderReset(JxlEncoder* enc);
/**
- * Deinitializes and frees JxlEncoder instance.
+ * Deinitializes and frees a @ref JxlEncoder instance.
*
* @param enc instance to be cleaned up and deallocated.
*/
@@ -430,8 +431,8 @@ JXL_EXPORT void JxlEncoderDestroy(JxlEncoder* enc);
* left unset, the default CMS implementation will be used.
*
* @param enc encoder object.
- * @param cms structure representing a CMS implementation. See JxlCmsInterface
- * for more details.
+ * @param cms structure representing a CMS implementation. See @ref
+ * JxlCmsInterface for more details.
*/
JXL_EXPORT void JxlEncoderSetCms(JxlEncoder* enc, JxlCmsInterface cms);
@@ -444,7 +445,7 @@ JXL_EXPORT void JxlEncoderSetCms(JxlEncoder* enc, JxlCmsInterface cms);
* be NULL to use the default, single-threaded, runner. A multithreaded
* runner should be set to reach fast performance.
* @param parallel_runner_opaque opaque pointer for parallel_runner.
- * @return JXL_ENC_SUCCESS if the runner was set, JXL_ENC_ERROR
+ * @return ::JXL_ENC_SUCCESS if the runner was set, ::JXL_ENC_ERROR
* otherwise (the previous runner remains set).
*/
JXL_EXPORT JxlEncoderStatus
@@ -452,16 +453,16 @@ JxlEncoderSetParallelRunner(JxlEncoder* enc, JxlParallelRunner parallel_runner,
void* parallel_runner_opaque);
/**
- * Get the (last) error code in case JXL_ENC_ERROR was returned.
+ * Get the (last) error code in case ::JXL_ENC_ERROR was returned.
*
* @param enc encoder object.
- * @return the JxlEncoderError that caused the (last) JXL_ENC_ERROR to be
- * returned.
+ * @return the @ref JxlEncoderError that caused the (last) ::JXL_ENC_ERROR to
+ * be returned.
*/
JXL_EXPORT JxlEncoderError JxlEncoderGetError(JxlEncoder* enc);
/**
- * Encodes JPEG XL file using the available bytes. @p *avail_out indicates how
+ * Encodes a JPEG XL file using the available bytes. @p *avail_out indicates how
* many output bytes are available, and @p *next_out points to the input bytes.
* *avail_out will be decremented by the amount of bytes that have been
* processed by the encoder and *next_out will be incremented by the same
@@ -469,12 +470,12 @@ JXL_EXPORT JxlEncoderError JxlEncoderGetError(JxlEncoder* enc);
* bytes.
*
* The returned status indicates whether the encoder needs more output bytes.
- * When the return value is not JXL_ENC_ERROR or JXL_ENC_SUCCESS, the encoding
- * requires more JxlEncoderProcessOutput calls to continue.
+ * When the return value is not ::JXL_ENC_ERROR or ::JXL_ENC_SUCCESS, the
+ * encoding requires more @ref JxlEncoderProcessOutput calls to continue.
*
* The caller must guarantee that *avail_out >= 32 when calling
- * JxlEncoderProcessOutput; otherwise, JXL_ENC_NEED_MORE_OUTPUT will be
- * returned. It is guaranteed that, if *avail_out >= 32, at least one byte of
+ * @ref JxlEncoderProcessOutput; otherwise, ::JXL_ENC_NEED_MORE_OUTPUT will
+ * be returned. It is guaranteed that, if *avail_out >= 32, at least one byte of
* output will be written.
*
* This encodes the frames and/or boxes added so far. If the last frame or last
@@ -486,9 +487,9 @@ JXL_EXPORT JxlEncoderError JxlEncoderGetError(JxlEncoder* enc);
* @param enc encoder object.
* @param next_out pointer to next bytes to write to.
* @param avail_out amount of bytes available starting from *next_out.
- * @return JXL_ENC_SUCCESS when encoding finished and all events handled.
- * @return JXL_ENC_ERROR when encoding failed, e.g. invalid input.
- * @return JXL_ENC_NEED_MORE_OUTPUT more output buffer is necessary.
+ * @return ::JXL_ENC_SUCCESS when encoding finished and all events handled.
+ * @return ::JXL_ENC_ERROR when encoding failed, e.g. invalid input.
+ * @return ::JXL_ENC_NEED_MORE_OUTPUT more output buffer is necessary.
*/
JXL_EXPORT JxlEncoderStatus JxlEncoderProcessOutput(JxlEncoder* enc,
uint8_t** next_out,
@@ -509,13 +510,14 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderProcessOutput(JxlEncoder* enc,
* time duration of 0, making them form a composite still. See @ref
* JxlFrameHeader for more information.
*
- * This information is stored in the JxlEncoderFrameSettings and so is used for
- * any frame encoded with these JxlEncoderFrameSettings. It is ok to change
- * between @ref JxlEncoderAddImageFrame calls, each added image frame will have
- * the frame header that was set in the options at the time of calling
- * JxlEncoderAddImageFrame.
+ * This information is stored in the @ref JxlEncoderFrameSettings and so is used
+ * for any frame encoded with these @ref JxlEncoderFrameSettings. It is ok to
+ * change between @ref JxlEncoderAddImageFrame calls, each added image frame
+ * will have the frame header that was set in the options at the time of calling
+ * @ref JxlEncoderAddImageFrame.
*
- * The is_last and name_length fields of the JxlFrameHeader are ignored, use
+ * The is_last and name_length fields of the @ref JxlFrameHeader are ignored,
+ * use
* @ref JxlEncoderCloseFrames to indicate last frame, and @ref
* JxlEncoderSetFrameName to indicate the name and its length instead.
* Calling this function will clear any name that was previously set with @ref
@@ -525,7 +527,7 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderProcessOutput(JxlEncoder* enc,
* includes reference to the encoder object.
* @param frame_header frame header data to set. Object owned by the caller and
* does not need to be kept in memory, its information is copied internally.
- * @return JXL_ENC_SUCCESS on success, JXL_ENC_ERROR on error
+ * @return ::JXL_ENC_SUCCESS on success, ::JXL_ENC_ERROR on error
*/
JXL_EXPORT JxlEncoderStatus
JxlEncoderSetFrameHeader(JxlEncoderFrameSettings* frame_settings,
@@ -540,7 +542,7 @@ JxlEncoderSetFrameHeader(JxlEncoderFrameSettings* frame_settings,
* includes reference to the encoder object.
* @param index index of the extra channel to use.
* @param blend_info blend info to set for the extra channel
- * @return JXL_ENC_SUCCESS on success, JXL_ENC_ERROR on error
+ * @return ::JXL_ENC_SUCCESS on success, ::JXL_ENC_ERROR on error
*/
JXL_EXPORT JxlEncoderStatus JxlEncoderSetExtraChannelBlendInfo(
JxlEncoderFrameSettings* frame_settings, size_t index,
@@ -550,8 +552,9 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderSetExtraChannelBlendInfo(
* Sets the name of the animation frame. This function is optional, frames are
* not required to have a name. This setting is a part of the frame header, and
* the same principles as for @ref JxlEncoderSetFrameHeader apply. The
- * name_length field of JxlFrameHeader is ignored by the encoder, this function
- * determines the name length instead as the length in bytes of the C string.
+ * name_length field of @ref JxlFrameHeader is ignored by the encoder, this
+ * function determines the name length instead as the length in bytes of the C
+ * string.
*
* The maximum possible name length is 1071 bytes (excluding terminating null
* character).
@@ -563,7 +566,7 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderSetExtraChannelBlendInfo(
* includes reference to the encoder object.
* @param frame_name name of the next frame to be encoded, as a UTF-8 encoded C
* string (zero terminated). Owned by the caller, and copied internally.
- * @return JXL_ENC_SUCCESS on success, JXL_ENC_ERROR on error
+ * @return ::JXL_ENC_SUCCESS on success, ::JXL_ENC_ERROR on error
*/
JXL_EXPORT JxlEncoderStatus JxlEncoderSetFrameName(
JxlEncoderFrameSettings* frame_settings, const char* frame_name);
@@ -571,15 +574,17 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderSetFrameName(
/**
* Sets the bit depth of the input buffer.
*
- * For float pixel formats, only the default JXL_BIT_DEPTH_FROM_PIXEL_FORMAT
+ * For float pixel formats, only the default @ref
+ JXL_BIT_DEPTH_FROM_PIXEL_FORMAT
* setting is allowed, while for unsigned pixel formats,
- * JXL_BIT_DEPTH_FROM_CODESTREAM setting is also allowed. See the comment on
+ * ::JXL_BIT_DEPTH_FROM_CODESTREAM setting is also allowed. See the comment
+ on
* @ref JxlEncoderAddImageFrame for the effects of the bit depth setting.
* @param frame_settings set of options and metadata for this frame. Also
* includes reference to the encoder object.
* @param bit_depth the bit depth setting of the pixel input
- * @return JXL_ENC_SUCCESS on success, JXL_ENC_ERROR on error
+ * @return ::JXL_ENC_SUCCESS on success, ::JXL_ENC_ERROR on error
*/
JXL_EXPORT JxlEncoderStatus JxlEncoderSetFrameBitDepth(
JxlEncoderFrameSettings* frame_settings, const JxlBitDepth* bit_depth);
@@ -587,13 +592,13 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderSetFrameBitDepth(
/**
* Sets the buffer to read JPEG encoded bytes from for the next frame to encode.
*
- * If JxlEncoderSetBasicInfo has not yet been called, calling
- * JxlEncoderAddJPEGFrame will implicitly call it with the parameters of the
- * added JPEG frame.
+ * If @ref JxlEncoderSetBasicInfo has not yet been called, calling
+ * @ref JxlEncoderAddJPEGFrame will implicitly call it with the parameters of
+ * the added JPEG frame.
*
- * If JxlEncoderSetColorEncoding or JxlEncoderSetICCProfile has not yet been
- * called, calling JxlEncoderAddJPEGFrame will implicitly call it with the
- * parameters of the added JPEG frame.
+ * If @ref JxlEncoderSetColorEncoding or @ref JxlEncoderSetICCProfile has not
+ * yet been called, calling @ref JxlEncoderAddJPEGFrame will implicitly call it
+ * with the parameters of the added JPEG frame.
*
* If the encoder is set to store JPEG reconstruction metadata using @ref
* JxlEncoderStoreJPEGMetadata and a single JPEG frame is added, it will be
@@ -603,12 +608,16 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderSetFrameBitDepth(
* JxlEncoderCloseFrames must be called before the next
* @ref JxlEncoderProcessOutput call.
*
+ * Note, this can only be used to add JPEG frames for lossless compression. To
+ * encode with lossy compression, the JPEG must be decoded manually and a pixel
+ * buffer added using JxlEncoderAddImageFrame.
+ *
* @param frame_settings set of options and metadata for this frame. Also
* includes reference to the encoder object.
* @param buffer bytes to read JPEG from. Owned by the caller and its contents
* are copied internally.
* @param size size of buffer in bytes.
- * @return JXL_ENC_SUCCESS on success, JXL_ENC_ERROR on error
+ * @return ::JXL_ENC_SUCCESS on success, ::JXL_ENC_ERROR on error
*/
JXL_EXPORT JxlEncoderStatus
JxlEncoderAddJPEGFrame(const JxlEncoderFrameSettings* frame_settings,
@@ -616,33 +625,36 @@ JxlEncoderAddJPEGFrame(const JxlEncoderFrameSettings* frame_settings,
/**
* Sets the buffer to read pixels from for the next image to encode. Must call
- * JxlEncoderSetBasicInfo before JxlEncoderAddImageFrame.
+ * @ref JxlEncoderSetBasicInfo before @ref JxlEncoderAddImageFrame.
*
* Currently only some data types for pixel formats are supported:
- * - JXL_TYPE_UINT8, with range 0..255
- * - JXL_TYPE_UINT16, with range 0..65535
- * - JXL_TYPE_FLOAT16, with nominal range 0..1
- * - JXL_TYPE_FLOAT, with nominal range 0..1
+ * - ::JXL_TYPE_UINT8, with range 0..255
+ * - ::JXL_TYPE_UINT16, with range 0..65535
+ * - ::JXL_TYPE_FLOAT16, with nominal range 0..1
+ * - ::JXL_TYPE_FLOAT, with nominal range 0..1
*
* Note: the sample data type in pixel_format is allowed to be different from
- * what is described in the JxlBasicInfo. The type in pixel_format, together
- * with an optional @ref JxlBitDepth parameter set by @ref
+ * what is described in the @ref JxlBasicInfo. The type in pixel_format,
+ * together with an optional @ref JxlBitDepth parameter set by @ref
* JxlEncoderSetFrameBitDepth describes the format of the uncompressed pixel
- * buffer. The bits_per_sample and exponent_bits_per_sample in the JxlBasicInfo
- * describes what will actually be encoded in the JPEG XL codestream.
- * For example, to encode a 12-bit image, you would set bits_per_sample to 12,
- * while the input frame buffer can be in the following formats:
- * - if pixel format is in JXL_TYPE_UINT16 with default bit depth setting
- * (i.e. JXL_BIT_DEPTH_FROM_PIXEL_FORMAT), input sample values are rescaled
- * to 16-bit, i.e. multiplied by 65535/4095;
- * - if pixel format is in JXL_TYPE_UINT16 with JXL_BIT_DEPTH_FROM_CODESTREAM
- * bit depth setting, input sample values are provided unscaled;
- * - if pixel format is in JXL_TYPE_FLOAT, input sample values are rescaled
- * to 0..1, i.e. multiplied by 1.f/4095.f.
- * While it is allowed, it is obviously not recommended to use a pixel_format
- * with lower precision than what is specified in the JxlBasicInfo.
- *
- * We support interleaved channels as described by the JxlPixelFormat:
+ * buffer. The bits_per_sample and exponent_bits_per_sample in the @ref
+ * JxlBasicInfo describes what will actually be encoded in the JPEG XL
+ * codestream. For example, to encode a 12-bit image, you would set
+ * bits_per_sample to 12, while the input frame buffer can be in the following
+ * formats:
+ * - if pixel format is in ::JXL_TYPE_UINT16 with default bit depth setting
+ * (i.e. ::JXL_BIT_DEPTH_FROM_PIXEL_FORMAT), input sample values are
+ * rescaled to 16-bit, i.e. multiplied by 65535/4095;
+ * - if pixel format is in ::JXL_TYPE_UINT16 with @ref
+ * JXL_BIT_DEPTH_FROM_CODESTREAM bit depth setting, input sample values are
+ * provided unscaled;
+ * - if pixel format is in ::JXL_TYPE_FLOAT, input sample values are
+ * rescaled to 0..1, i.e. multiplied by 1.f/4095.f. While it is allowed, it is
+ * obviously not recommended to use a pixel_format with lower precision than
+ * what is specified in the @ref JxlBasicInfo.
+ *
+ * We support interleaved channels as described by the @ref JxlPixelFormat
+ * "JxlPixelFormat":
* - single-channel data, e.g. grayscale
* - single-channel + alpha
* - trichromatic, e.g. RGB
@@ -654,10 +666,11 @@ JxlEncoderAddJPEGFrame(const JxlEncoderFrameSettings* frame_settings,
* set to all-opaque (an alpha value of 1.0 everywhere).
*
* The pixels are assumed to be encoded in the original profile that is set with
- * JxlEncoderSetColorEncoding or JxlEncoderSetICCProfile. If none of these
- * functions were used, the pixels are assumed to be nonlinear sRGB for integer
- * data types (JXL_TYPE_UINT8, JXL_TYPE_UINT16), and linear sRGB for floating
- * point data types (JXL_TYPE_FLOAT16, JXL_TYPE_FLOAT).
+ * @ref JxlEncoderSetColorEncoding or @ref JxlEncoderSetICCProfile. If none of
+ * these functions were used, the pixels are assumed to be nonlinear sRGB for
+ * integer data types (::JXL_TYPE_UINT8, ::JXL_TYPE_UINT16), and linear
+ * sRGB for floating point data types (::JXL_TYPE_FLOAT16, @ref
+ * JXL_TYPE_FLOAT).
*
* Sample values in floating-point pixel formats are allowed to be outside the
* nominal range, e.g. to represent out-of-sRGB-gamut colors in the
@@ -676,14 +689,14 @@ JxlEncoderAddJPEGFrame(const JxlEncoderFrameSettings* frame_settings,
* and its contents are copied internally.
* @param size size of buffer in bytes. This size should match what is implied
* by the frame dimensions and the pixel format.
- * @return JXL_ENC_SUCCESS on success, JXL_ENC_ERROR on error
+ * @return ::JXL_ENC_SUCCESS on success, ::JXL_ENC_ERROR on error
*/
JXL_EXPORT JxlEncoderStatus JxlEncoderAddImageFrame(
const JxlEncoderFrameSettings* frame_settings,
const JxlPixelFormat* pixel_format, const void* buffer, size_t size);
/**
- * The JxlEncoderOutputProcessor structure provides an interface for the
+ * The @ref JxlEncoderOutputProcessor structure provides an interface for the
* encoder's output processing. Users of the library, who want to do streaming
* encoding, should implement the required callbacks for buffering, writing,
* seeking (if supported), and setting a finalized position during the encoding
@@ -785,7 +798,7 @@ struct JxlEncoderOutputProcessor {
* @param enc encoder object.
* @param output_processor the struct containing the callbacks for managing
* output.
- * @return JXL_ENC_SUCCESS on success, JXL_ENC_ERROR on error.
+ * @return ::JXL_ENC_SUCCESS on success, ::JXL_ENC_ERROR on error.
*/
JXL_EXPORT JxlEncoderStatus JxlEncoderSetOutputProcessor(
JxlEncoder* enc, struct JxlEncoderOutputProcessor output_processor);
@@ -801,7 +814,7 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderSetOutputProcessor(
* This should not be used when using @ref JxlEncoderProcessOutput.
*
* @param enc encoder object.
- * @return JXL_ENC_SUCCESS on success, JXL_ENC_ERROR on error.
+ * @return ::JXL_ENC_SUCCESS on success, ::JXL_ENC_ERROR on error.
*/
JXL_EXPORT JxlEncoderStatus JxlEncoderFlushInput(JxlEncoder* enc);
@@ -923,9 +936,9 @@ struct JxlChunkedFrameInputSource {
* chunked or streaming manner, which can be especially useful when dealing with
* large images that may not fit entirely in memory or when trying to optimize
* memory usage. The input data is provided through callbacks defined in the
- * `JxlChunkedFrameInputSource` struct. Once the frame data has been completely
- * retrieved, this function will flush the input and close it if it is the last
- * frame.
+ * @ref JxlChunkedFrameInputSource struct. Once the frame data has been
+ * completely retrieved, this function will flush the input and close it if it
+ * is the last frame.
*
* @param frame_settings set of options and metadata for this frame. Also
* includes reference to the encoder object.
@@ -943,7 +956,7 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderAddChunkedFrame(
/**
* Sets the buffer to read pixels from for an extra channel at a given index.
* The index must be smaller than the num_extra_channels in the associated
- * JxlBasicInfo. Must call @ref JxlEncoderSetExtraChannelInfo before
+ * @ref JxlBasicInfo. Must call @ref JxlEncoderSetExtraChannelInfo before @ref
* JxlEncoderSetExtraChannelBuffer.
*
* TODO(firsching): mention what data types in pixel formats are supported.
@@ -961,15 +974,15 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderAddChunkedFrame(
* @param size size of buffer in bytes. This size should match what is implied
* by the frame dimensions and the pixel format.
* @param index index of the extra channel to use.
- * @return JXL_ENC_SUCCESS on success, JXL_ENC_ERROR on error
+ * @return ::JXL_ENC_SUCCESS on success, ::JXL_ENC_ERROR on error
*/
JXL_EXPORT JxlEncoderStatus JxlEncoderSetExtraChannelBuffer(
const JxlEncoderFrameSettings* frame_settings,
const JxlPixelFormat* pixel_format, const void* buffer, size_t size,
uint32_t index);
-/** Adds a metadata box to the file format. JxlEncoderProcessOutput must be used
- * to effectively write the box to the output. @ref JxlEncoderUseBoxes must
+/** Adds a metadata box to the file format. @ref JxlEncoderProcessOutput must be
+ * used to effectively write the box to the output. @ref JxlEncoderUseBoxes must
* be enabled before using this function.
*
* Boxes allow inserting application-specific data and metadata (Exif, XML/XMP,
@@ -996,10 +1009,10 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderSetExtraChannelBuffer(
* the encoder encodes the size header itself. Most boxes are written
* automatically by the encoder as needed ("JXL ", "ftyp", "jxll", "jxlc",
* "jxlp", "jxli", "jbrd"), and this function only needs to be called to add
- * optional metadata when encoding from pixels (using JxlEncoderAddImageFrame).
- * When recompressing JPEG files (using JxlEncoderAddJPEGFrame), if the input
- * JPEG contains EXIF, XMP or JUMBF metadata, the corresponding boxes are
- * already added automatically.
+ * optional metadata when encoding from pixels (using @ref
+ * JxlEncoderAddImageFrame). When recompressing JPEG files (using @ref
+ * JxlEncoderAddJPEGFrame), if the input JPEG contains EXIF, XMP or JUMBF
+ * metadata, the corresponding boxes are already added automatically.
*
* Box types are given by 4 characters. The following boxes can be added with
* this function:
@@ -1032,9 +1045,9 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderSetExtraChannelBuffer(
* @param size size of the box contents.
* @param compress_box Whether to compress this box as a "brob" box. Requires
* Brotli support.
- * @return JXL_ENC_SUCCESS on success, JXL_ENC_ERROR on error, such as when
- * using this function without JxlEncoderUseContainer, or adding a box type
- * that would result in an invalid file format.
+ * @return ::JXL_ENC_SUCCESS on success, ::JXL_ENC_ERROR on error, such as
+ * when using this function without @ref JxlEncoderUseContainer, or adding a box
+ * type that would result in an invalid file format.
*/
JXL_EXPORT JxlEncoderStatus JxlEncoderAddBox(JxlEncoder* enc,
const JxlBoxType type,
@@ -1061,9 +1074,9 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderUseBoxes(JxlEncoder* enc);
* the stream will be finished. It is not necessary to use this function if
* @ref JxlEncoderUseBoxes is not used. Further frames may still be added.
*
- * Must be called between JxlEncoderAddBox of the last box
- * and the next call to JxlEncoderProcessOutput, or @ref JxlEncoderProcessOutput
- * won't output the last box correctly.
+ * Must be called between @ref JxlEncoderAddBox of the last box
+ * and the next call to @ref JxlEncoderProcessOutput, or @ref
+ * JxlEncoderProcessOutput won't output the last box correctly.
*
* NOTE: if you don't need to close frames and boxes at separate times, you can
* use @ref JxlEncoderCloseInput instead to close both at once.
@@ -1087,10 +1100,10 @@ JXL_EXPORT void JxlEncoderCloseBoxes(JxlEncoder* enc);
JXL_EXPORT void JxlEncoderCloseFrames(JxlEncoder* enc);
/**
- * Closes any input to the encoder, equivalent to calling JxlEncoderCloseFrames
- * as well as calling JxlEncoderCloseBoxes if needed. No further input of any
- * kind may be given to the encoder, but further @ref JxlEncoderProcessOutput
- * calls should be done to create the final output.
+ * Closes any input to the encoder, equivalent to calling @ref
+ * JxlEncoderCloseFrames as well as calling @ref JxlEncoderCloseBoxes if needed.
+ * No further input of any kind may be given to the encoder, but further @ref
+ * JxlEncoderProcessOutput calls should be done to create the final output.
*
* The requirements of both @ref JxlEncoderCloseFrames and @ref
* JxlEncoderCloseBoxes apply to this function. Either this function or the
@@ -1104,39 +1117,39 @@ JXL_EXPORT void JxlEncoderCloseInput(JxlEncoder* enc);
/**
* Sets the original color encoding of the image encoded by this encoder. This
- * is an alternative to JxlEncoderSetICCProfile and only one of these two must
- * be used. This one sets the color encoding as a @ref JxlColorEncoding, while
- * the other sets it as ICC binary data.
- * Must be called after JxlEncoderSetBasicInfo.
+ * is an alternative to @ref JxlEncoderSetICCProfile and only one of these two
+ * must be used. This one sets the color encoding as a @ref JxlColorEncoding,
+ * while the other sets it as ICC binary data. Must be called after @ref
+ * JxlEncoderSetBasicInfo.
*
* @param enc encoder object.
* @param color color encoding. Object owned by the caller and its contents are
* copied internally.
- * @return JXL_ENC_SUCCESS if the operation was successful, JXL_ENC_ERROR or
- * JXL_ENC_NOT_SUPPORTED otherwise
+ * @return ::JXL_ENC_SUCCESS if the operation was successful, @ref
+ * JXL_ENC_ERROR otherwise
*/
JXL_EXPORT JxlEncoderStatus
JxlEncoderSetColorEncoding(JxlEncoder* enc, const JxlColorEncoding* color);
/**
* Sets the original color encoding of the image encoded by this encoder as an
- * ICC color profile. This is an alternative to JxlEncoderSetColorEncoding and
- * only one of these two must be used. This one sets the color encoding as ICC
- * binary data, while the other defines it as a @ref JxlColorEncoding.
- * Must be called after JxlEncoderSetBasicInfo.
+ * ICC color profile. This is an alternative to @ref JxlEncoderSetColorEncoding
+ * and only one of these two must be used. This one sets the color encoding as
+ * ICC binary data, while the other defines it as a @ref JxlColorEncoding. Must
+ * be called after @ref JxlEncoderSetBasicInfo.
*
* @param enc encoder object.
* @param icc_profile bytes of the original ICC profile
* @param size size of the icc_profile buffer in bytes
- * @return JXL_ENC_SUCCESS if the operation was successful, JXL_ENC_ERROR or
- * JXL_ENC_NOT_SUPPORTED otherwise
+ * @return ::JXL_ENC_SUCCESS if the operation was successful, @ref
+ * JXL_ENC_ERROR otherwise
*/
JXL_EXPORT JxlEncoderStatus JxlEncoderSetICCProfile(JxlEncoder* enc,
const uint8_t* icc_profile,
size_t size);
/**
- * Initializes a JxlBasicInfo struct to default values.
+ * Initializes a @ref JxlBasicInfo struct to default values.
* For forwards-compatibility, this function has to be called before values
* are assigned to the struct fields.
* The default values correspond to an 8-bit RGB image, no alpha or any
@@ -1147,7 +1160,7 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderSetICCProfile(JxlEncoder* enc,
JXL_EXPORT void JxlEncoderInitBasicInfo(JxlBasicInfo* info);
/**
- * Initializes a JxlFrameHeader struct to default values.
+ * Initializes a @ref JxlFrameHeader struct to default values.
* For forwards-compatibility, this function has to be called before values
* are assigned to the struct fields.
* The default values correspond to a frame with no animation duration and the
@@ -1159,7 +1172,7 @@ JXL_EXPORT void JxlEncoderInitBasicInfo(JxlBasicInfo* info);
JXL_EXPORT void JxlEncoderInitFrameHeader(JxlFrameHeader* frame_header);
/**
- * Initializes a JxlBlendInfo struct to default values.
+ * Initializes a @ref JxlBlendInfo struct to default values.
* For forwards-compatibility, this function has to be called before values
* are assigned to the struct fields.
*
@@ -1170,26 +1183,26 @@ JXL_EXPORT void JxlEncoderInitBlendInfo(JxlBlendInfo* blend_info);
/**
* Sets the global metadata of the image encoded by this encoder.
*
- * If the JxlBasicInfo contains information of extra channels beyond an alpha
- * channel, then @ref JxlEncoderSetExtraChannelInfo must be called between
- * JxlEncoderSetBasicInfo and @ref JxlEncoderAddImageFrame. In order to indicate
- * extra channels, the value of `info.num_extra_channels` should be set to the
- * number of extra channels, also counting the alpha channel if present.
+ * If the @ref JxlBasicInfo contains information of extra channels beyond an
+ * alpha channel, then @ref JxlEncoderSetExtraChannelInfo must be called between
+ * @ref JxlEncoderSetBasicInfo and @ref JxlEncoderAddImageFrame. In order to
+ * indicate extra channels, the value of `info.num_extra_channels` should be set
+ * to the number of extra channels, also counting the alpha channel if present.
*
* @param enc encoder object.
* @param info global image metadata. Object owned by the caller and its
* contents are copied internally.
- * @return JXL_ENC_SUCCESS if the operation was successful,
- * JXL_ENC_ERROR or JXL_ENC_NOT_SUPPORTED otherwise
+ * @return ::JXL_ENC_SUCCESS if the operation was successful,
+ * ::JXL_ENC_ERROR otherwise
*/
JXL_EXPORT JxlEncoderStatus JxlEncoderSetBasicInfo(JxlEncoder* enc,
const JxlBasicInfo* info);
/**
* Sets the upsampling method the decoder will use in case there are frames
- * with JXL_ENC_FRAME_SETTING_RESAMPLING set. This is useful in combination
- * with the JXL_ENC_FRAME_SETTING_ALREADY_DOWNSAMPLED option, to control the
- * type of upsampling that will be used.
+ * with ::JXL_ENC_FRAME_SETTING_RESAMPLING set. This is useful in combination
+ * with the ::JXL_ENC_FRAME_SETTING_ALREADY_DOWNSAMPLED option, to control
+ * the type of upsampling that will be used.
*
* @param enc encoder object.
* @param factor upsampling factor to configure (1, 2, 4 or 8; for 1 this
@@ -1198,15 +1211,15 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderSetBasicInfo(JxlEncoder* enc,
* -1: default (good for photographic images, no signaling overhead)
* 0: nearest neighbor (good for pixel art)
* 1: 'pixel dots' (same as NN for 2x, diamond-shaped 'pixel dots' for 4x/8x)
- * @return JXL_ENC_SUCCESS if the operation was successful,
- * JXL_ENC_ERROR or JXL_ENC_NOT_SUPPORTED otherwise
+ * @return ::JXL_ENC_SUCCESS if the operation was successful,
+ * ::JXL_ENC_ERROR otherwise
*/
JXL_EXPORT JxlEncoderStatus JxlEncoderSetUpsamplingMode(JxlEncoder* enc,
int64_t factor,
int64_t mode);
/**
- * Initializes a JxlExtraChannelInfo struct to default values.
+ * Initializes a @ref JxlExtraChannelInfo struct to default values.
* For forwards-compatibility, this function has to be called before values
* are assigned to the struct fields.
* The default values correspond to an 8-bit channel of the provided type.
@@ -1220,23 +1233,24 @@ JXL_EXPORT void JxlEncoderInitExtraChannelInfo(JxlExtraChannelType type,
/**
* Sets information for the extra channel at the given index. The index
- * must be smaller than num_extra_channels in the associated JxlBasicInfo.
+ * must be smaller than num_extra_channels in the associated @ref JxlBasicInfo.
*
* @param enc encoder object
* @param index index of the extra channel to set.
* @param info global extra channel metadata. Object owned by the caller and its
* contents are copied internally.
- * @return JXL_ENC_SUCCESS on success, JXL_ENC_ERROR on error
+ * @return ::JXL_ENC_SUCCESS on success, ::JXL_ENC_ERROR on error
*/
JXL_EXPORT JxlEncoderStatus JxlEncoderSetExtraChannelInfo(
JxlEncoder* enc, size_t index, const JxlExtraChannelInfo* info);
/**
* Sets the name for the extra channel at the given index in UTF-8. The index
- * must be smaller than the num_extra_channels in the associated JxlBasicInfo.
+ * must be smaller than the num_extra_channels in the associated @ref
+ * JxlBasicInfo.
*
* TODO(lode): remove size parameter for consistency with
- * JxlEncoderSetFrameName
+ * @ref JxlEncoderSetFrameName
*
* @param enc encoder object
* @param index index of the extra channel to set.
@@ -1252,17 +1266,18 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderSetExtraChannelName(JxlEncoder* enc,
/**
* Sets a frame-specific option of integer type to the encoder options.
- * The JxlEncoderFrameSettingId argument determines which option is set.
+ * The @ref JxlEncoderFrameSettingId argument determines which option is set.
*
* @param frame_settings set of options and metadata for this frame. Also
* includes reference to the encoder object.
* @param option ID of the option to set.
* @param value Integer value to set for this option.
- * @return JXL_ENC_SUCCESS if the operation was successful, JXL_ENC_ERROR in
- * case of an error, such as invalid or unknown option id, or invalid integer
- * value for the given option. If an error is returned, the state of the
- * JxlEncoderFrameSettings object is still valid and is the same as before this
- * function was called.
+ * @return ::JXL_ENC_SUCCESS if the operation was successful, @ref
+ * JXL_ENC_ERROR in case of an error, such as invalid or unknown option id, or
+ * invalid integer value for the given option. If an error is returned, the
+ * state of the
+ * @ref JxlEncoderFrameSettings object is still valid and is the same as before
+ * this function was called.
*/
JXL_EXPORT JxlEncoderStatus JxlEncoderFrameSettingsSetOption(
JxlEncoderFrameSettings* frame_settings, JxlEncoderFrameSettingId option,
@@ -1270,17 +1285,18 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderFrameSettingsSetOption(
/**
* Sets a frame-specific option of float type to the encoder options.
- * The JxlEncoderFrameSettingId argument determines which option is set.
+ * The @ref JxlEncoderFrameSettingId argument determines which option is set.
*
* @param frame_settings set of options and metadata for this frame. Also
* includes reference to the encoder object.
* @param option ID of the option to set.
* @param value Float value to set for this option.
- * @return JXL_ENC_SUCCESS if the operation was successful, JXL_ENC_ERROR in
- * case of an error, such as invalid or unknown option id, or invalid integer
- * value for the given option. If an error is returned, the state of the
- * JxlEncoderFrameSettings object is still valid and is the same as before this
- * function was called.
+ * @return ::JXL_ENC_SUCCESS if the operation was successful, @ref
+ * JXL_ENC_ERROR in case of an error, such as invalid or unknown option id, or
+ * invalid integer value for the given option. If an error is returned, the
+ * state of the
+ * @ref JxlEncoderFrameSettings object is still valid and is the same as before
+ * this function was called.
*/
JXL_EXPORT JxlEncoderStatus JxlEncoderFrameSettingsSetFloatOption(
JxlEncoderFrameSettings* frame_settings, JxlEncoderFrameSettingId option,
@@ -1292,7 +1308,7 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderFrameSettingsSetFloatOption(
* When using @ref JxlEncoderUseBoxes, @ref JxlEncoderStoreJPEGMetadata or @ref
* JxlEncoderSetCodestreamLevel with level 10, the encoder will automatically
* also use the container format, it is not necessary to use
- * JxlEncoderUseContainer for those use cases.
+ * @ref JxlEncoderUseContainer for those use cases.
*
* By default this setting is disabled.
*
@@ -1318,8 +1334,8 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderUseContainer(JxlEncoder* enc,
*
* @param enc encoder object.
* @param store_jpeg_metadata true if the encoder should store JPEG metadata.
- * @return JXL_ENC_SUCCESS if the operation was successful, JXL_ENC_ERROR
- * otherwise.
+ * @return ::JXL_ENC_SUCCESS if the operation was successful, @ref
+ * JXL_ENC_ERROR otherwise.
*/
JXL_EXPORT JxlEncoderStatus
JxlEncoderStoreJPEGMetadata(JxlEncoder* enc, JXL_BOOL store_jpeg_metadata);
@@ -1334,8 +1350,8 @@ JxlEncoderStoreJPEGMetadata(JxlEncoder* enc, JXL_BOOL store_jpeg_metadata);
* 268435456 pixels total with a maximum width or height of 262144 pixels,
* maximum 16-bit color channel depth, maximum 120 frames per second for
* animation, maximum ICC color profile size of 4 MiB, it allows all color
- * models and extra channel types except CMYK and the JXL_CHANNEL_BLACK extra
- * channel, and a maximum of 4 extra channels in addition to the 3 color
+ * models and extra channel types except CMYK and the JXL_CHANNEL_BLACK
+ * extra channel, and a maximum of 4 extra channels in addition to the 3 color
* channels. It also sets boundaries to certain internally used coding tools.
*
* Level 10: this level removes or increases the bounds of most of the level
@@ -1355,8 +1371,8 @@ JxlEncoderStoreJPEGMetadata(JxlEncoder* enc, JXL_BOOL store_jpeg_metadata);
*
* @param enc encoder object.
* @param level the level value to set, must be -1, 5, or 10.
- * @return JXL_ENC_SUCCESS if the operation was successful, JXL_ENC_ERROR
- * otherwise.
+ * @return ::JXL_ENC_SUCCESS if the operation was successful, @ref
+ * JXL_ENC_ERROR otherwise.
*/
JXL_EXPORT JxlEncoderStatus JxlEncoderSetCodestreamLevel(JxlEncoder* enc,
int level);
@@ -1370,9 +1386,10 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderSetCodestreamLevel(JxlEncoder* enc,
* the JPEG XL file.
*
* If this returns 5, nothing needs to be done and the codestream can be
- * compatible with any decoder. If this returns 10, JxlEncoderSetCodestreamLevel
- * has to be used to set the codestream level to 10, or the encoder can be
- * configured differently to allow using the more compatible level 5.
+ * compatible with any decoder. If this returns 10, @ref
+ * JxlEncoderSetCodestreamLevel has to be used to set the codestream level to
+ * 10, or the encoder can be configured differently to allow using the more
+ * compatible level 5.
*
* @param enc encoder object.
* @return -1 if no level can support the configuration (e.g. image dimensions
@@ -1391,14 +1408,14 @@ JXL_EXPORT int JxlEncoderGetRequiredCodestreamLevel(const JxlEncoder* enc);
*
* When disabled, those options are not overridden, but since those options
* could still have been manually set to a combination that operates losslessly,
- * using this function with lossless set to JXL_DEC_FALSE does not guarantee
- * lossy encoding, though the default set of options is lossy.
+ * using this function with lossless set to ::JXL_FALSE does not
+ * guarantee lossy encoding, though the default set of options is lossy.
*
* @param frame_settings set of options and metadata for this frame. Also
* includes reference to the encoder object.
* @param lossless whether to override options for lossless mode
- * @return JXL_ENC_SUCCESS if the operation was successful, JXL_ENC_ERROR
- * otherwise.
+ * @return ::JXL_ENC_SUCCESS if the operation was successful, @ref
+ * JXL_ENC_ERROR otherwise.
*/
JXL_EXPORT JxlEncoderStatus JxlEncoderSetFrameLossless(
JxlEncoderFrameSettings* frame_settings, JXL_BOOL lossless);
@@ -1406,7 +1423,7 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderSetFrameLossless(
/**
* Sets the distance level for lossy compression: target max butteraugli
* distance, lower = higher quality. Range: 0 .. 25.
- * 0.0 = mathematically lossless (however, use JxlEncoderSetFrameLossless
+ * 0.0 = mathematically lossless (however, use @ref JxlEncoderSetFrameLossless
* instead to use true lossless, as setting distance to 0 alone is not the only
* requirement). 1.0 = visually lossless. Recommended range: 0.5 .. 3.0. Default
* value: 1.0.
@@ -1414,24 +1431,25 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderSetFrameLossless(
* @param frame_settings set of options and metadata for this frame. Also
* includes reference to the encoder object.
* @param distance the distance value to set.
- * @return JXL_ENC_SUCCESS if the operation was successful, JXL_ENC_ERROR
- * otherwise.
+ * @return ::JXL_ENC_SUCCESS if the operation was successful, @ref
+ * JXL_ENC_ERROR otherwise.
*/
JXL_EXPORT JxlEncoderStatus JxlEncoderSetFrameDistance(
JxlEncoderFrameSettings* frame_settings, float distance);
/**
* Sets the distance level for lossy compression of extra channels.
- * The distance is as in JxlEncoderSetFrameDistance (lower = higher quality).
- * If not set, or if set to the special value -1, the distance that was set with
- * JxlEncoderSetFrameDistance will be used.
+ * The distance is as in @ref JxlEncoderSetFrameDistance (lower = higher
+ * quality). If not set, or if set to the special value -1, the distance that
+ * was set with
+ * @ref JxlEncoderSetFrameDistance will be used.
*
* @param frame_settings set of options and metadata for this frame. Also
* includes reference to the encoder object.
* @param index index of the extra channel to set a distance value for.
* @param distance the distance value to set.
- * @return JXL_ENC_SUCCESS if the operation was successful, JXL_ENC_ERROR
- * otherwise.
+ * @return ::JXL_ENC_SUCCESS if the operation was successful, @ref
+ * JXL_ENC_ERROR otherwise.
*/
JXL_EXPORT JxlEncoderStatus JxlEncoderSetExtraChannelDistance(
JxlEncoderFrameSettings* frame_settings, size_t index, float distance);
@@ -1441,8 +1459,7 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderSetExtraChannelDistance(
*
* This function takes in input a JPEG-style quality factor `quality` and
* produces as output a `distance` value suitable to be used with @ref
- * JxlEncoderSetFrameDistance and
- * @ref JxlEncoderSetExtraChannelDistance.
+ * JxlEncoderSetFrameDistance and @ref JxlEncoderSetExtraChannelDistance.
*
* The `distance` value influences the level of compression, with lower values
* indicating higher quality:
@@ -1479,10 +1496,10 @@ JXL_EXPORT float JxlEncoderDistanceFromQuality(float quality);
* the @p source options, or set to default if @p source is NULL.
*
* The returned pointer is an opaque struct tied to the encoder and it will be
- * deallocated by the encoder when JxlEncoderDestroy() is called. For functions
- * taking both a @ref JxlEncoder and a @ref JxlEncoderFrameSettings, only
- * JxlEncoderFrameSettings created with this function for the same encoder
- * instance can be used.
+ * deallocated by the encoder when @ref JxlEncoderDestroy() is called. For
+ * functions taking both a @ref JxlEncoder and a @ref JxlEncoderFrameSettings,
+ * only @ref JxlEncoderFrameSettings created with this function for the same
+ * encoder instance can be used.
*
* @param enc encoder object.
* @param source source options to copy initial values from, or NULL to get
@@ -1513,7 +1530,7 @@ JXL_EXPORT void JxlColorEncodingSetToLinearSRGB(
/**
* Enables usage of expert options.
*
- * At the moment, the only expert option is setting an effort value of 10,
+ * At the moment, the only expert option is setting an effort value of 11,
* which gives the best compression for pixel-lossless modes but is very slow.
*
* @param enc encoder object.
diff --git a/third_party/jpeg-xl/lib/include/jxl/parallel_runner.h b/third_party/jpeg-xl/lib/include/jxl/parallel_runner.h
index e71e0aa926..ea66685dbc 100644
--- a/third_party/jpeg-xl/lib/include/jxl/parallel_runner.h
+++ b/third_party/jpeg-xl/lib/include/jxl/parallel_runner.h
@@ -46,39 +46,41 @@ extern "C" {
/** Return code used in the JxlParallel* functions as return value. A value
* of 0 means success and any other value means error. The special value
- * JXL_PARALLEL_RET_RUNNER_ERROR can be used by the runner to indicate any
+ * ::JXL_PARALLEL_RET_RUNNER_ERROR can be used by the runner to indicate any
* other error.
*/
typedef int JxlParallelRetCode;
/**
- * General error returned by the JxlParallelRunInit function to indicate
+ * General error returned by the @ref JxlParallelRunInit function to indicate
* an error.
*/
#define JXL_PARALLEL_RET_RUNNER_ERROR (-1)
/**
- * Parallel run initialization callback. See JxlParallelRunner for details.
+ * Parallel run initialization callback. See @ref JxlParallelRunner for details.
*
* This function MUST be called by the JxlParallelRunner only once, on the
- * same thread that called JxlParallelRunner, before any parallel execution.
- * The purpose of this call is to provide the maximum number of threads that the
- * JxlParallelRunner will use, which can be used by JPEG XL to allocate
+ * same thread that called @ref JxlParallelRunner, before any parallel
+ * execution. The purpose of this call is to provide the maximum number of
+ * threads that the
+ * @ref JxlParallelRunner will use, which can be used by JPEG XL to allocate
* per-thread storage if needed.
*
* @param jpegxl_opaque the @p jpegxl_opaque handle provided to
- * JxlParallelRunner() must be passed here.
+ * @ref JxlParallelRunner() must be passed here.
* @param num_threads the maximum number of threads. This value must be
* positive.
* @return 0 if the initialization process was successful.
* @return an error code if there was an error, which should be returned by
- * JxlParallelRunner().
+ * @ref JxlParallelRunner().
*/
typedef JxlParallelRetCode (*JxlParallelRunInit)(void* jpegxl_opaque,
size_t num_threads);
/**
- * Parallel run data processing callback. See JxlParallelRunner for details.
+ * Parallel run data processing callback. See @ref JxlParallelRunner for
+ * details.
*
* This function MUST be called once for every number in the range [start_range,
* end_range) (including start_range but not including end_range) passing this
@@ -86,11 +88,11 @@ typedef JxlParallelRetCode (*JxlParallelRunInit)(void* jpegxl_opaque,
* different threads in parallel.
*
* @param jpegxl_opaque the @p jpegxl_opaque handle provided to
- * JxlParallelRunner() must be passed here.
+ * @ref JxlParallelRunner() must be passed here.
* @param value the number in the range [start_range, end_range) of the call.
* @param thread_id the thread number where this function is being called from.
* This must be lower than the @p num_threads value passed to
- * JxlParallelRunInit.
+ * @ref JxlParallelRunInit.
*/
typedef void (*JxlParallelRunFunction)(void* jpegxl_opaque, uint32_t value,
size_t thread_id);
@@ -103,11 +105,12 @@ typedef void (*JxlParallelRunFunction)(void* jpegxl_opaque, uint32_t value,
* number in the range [start_range, end_range) (including start_range but not
* including end_range) possibly from different multiple threads in parallel.
*
- * The JxlParallelRunner function does not need to be re-entrant. This means
- * that the same JxlParallelRunner function with the same runner_opaque
- * provided parameter will not be called from the library from either @p init or
+ * The @ref JxlParallelRunner function does not need to be re-entrant. This
+ * means that the same @ref JxlParallelRunner function with the same
+ * runner_opaque provided parameter will not be called from the library from
+ * either @p init or
* @p func in the same decoder or encoder instance. However, a single decoding
- * or encoding instance may call the provided JxlParallelRunner multiple
+ * or encoding instance may call the provided @ref JxlParallelRunner multiple
* times for different parts of the decoding or encoding process.
*
* @return 0 if the @p init call succeeded (returned 0) and no other error
@@ -120,7 +123,7 @@ typedef JxlParallelRetCode (*JxlParallelRunner)(
void* runner_opaque, void* jpegxl_opaque, JxlParallelRunInit init,
JxlParallelRunFunction func, uint32_t start_range, uint32_t end_range);
-/* The following is an example of a JxlParallelRunner that doesn't use any
+/* The following is an example of a @ref JxlParallelRunner that doesn't use any
* multi-threading. Note that this implementation doesn't store any state
* between multiple calls of the ExampleSequentialRunner function, so the
* runner_opaque value is not used.
diff --git a/third_party/jpeg-xl/lib/include/jxl/resizable_parallel_runner.h b/third_party/jpeg-xl/lib/include/jxl/resizable_parallel_runner.h
index a65015d861..c82b0bc23b 100644
--- a/third_party/jpeg-xl/lib/include/jxl/resizable_parallel_runner.h
+++ b/third_party/jpeg-xl/lib/include/jxl/resizable_parallel_runner.h
@@ -16,7 +16,7 @@
* created can be changed after creation of the thread pool; the threads
* (including the main thread) are re-used for every
* ResizableParallelRunner::Runner call. Only one concurrent
- * JxlResizableParallelRunner call per instance is allowed at a time.
+ * @ref JxlResizableParallelRunner call per instance is allowed at a time.
*
* This is a scalable, lower-overhead thread pool runner, especially suitable
* for data-parallel computations in the fork-join model, where clients need to
@@ -41,20 +41,20 @@
extern "C" {
#endif
-/** Parallel runner internally using std::thread. Use as JxlParallelRunner.
+/** Parallel runner internally using std::thread. Use as @ref JxlParallelRunner.
*/
JXL_THREADS_EXPORT JxlParallelRetCode JxlResizableParallelRunner(
void* runner_opaque, void* jpegxl_opaque, JxlParallelRunInit init,
JxlParallelRunFunction func, uint32_t start_range, uint32_t end_range);
-/** Creates the runner for JxlResizableParallelRunner. Use as the opaque
+/** Creates the runner for @ref JxlResizableParallelRunner. Use as the opaque
* runner. The runner will execute tasks on the calling thread until
* @ref JxlResizableParallelRunnerSetThreads is called.
*/
JXL_THREADS_EXPORT void* JxlResizableParallelRunnerCreate(
const JxlMemoryManager* memory_manager);
-/** Changes the number of threads for JxlResizableParallelRunner.
+/** Changes the number of threads for @ref JxlResizableParallelRunner.
*/
JXL_THREADS_EXPORT void JxlResizableParallelRunnerSetThreads(
void* runner_opaque, size_t num_threads);
@@ -64,7 +64,7 @@ JXL_THREADS_EXPORT void JxlResizableParallelRunnerSetThreads(
JXL_THREADS_EXPORT uint32_t
JxlResizableParallelRunnerSuggestThreads(uint64_t xsize, uint64_t ysize);
-/** Destroys the runner created by JxlResizableParallelRunnerCreate.
+/** Destroys the runner created by @ref JxlResizableParallelRunnerCreate.
*/
JXL_THREADS_EXPORT void JxlResizableParallelRunnerDestroy(void* runner_opaque);
diff --git a/third_party/jpeg-xl/lib/include/jxl/stats.h b/third_party/jpeg-xl/lib/include/jxl/stats.h
index c9359dc870..5ed440636f 100644
--- a/third_party/jpeg-xl/lib/include/jxl/stats.h
+++ b/third_party/jpeg-xl/lib/include/jxl/stats.h
@@ -23,15 +23,15 @@ extern "C" {
/**
* Opaque structure that holds the encoder statistics.
*
- * Allocated and initialized with JxlEncoderStatsCreate().
- * Cleaned up and deallocated with JxlEncoderStatsDestroy().
+ * Allocated and initialized with @ref JxlEncoderStatsCreate().
+ * Cleaned up and deallocated with @ref JxlEncoderStatsDestroy().
*/
typedef struct JxlEncoderStatsStruct JxlEncoderStats;
/**
* Creates an instance of JxlEncoderStats and initializes it.
*
- * @return pointer to initialized JxlEncoderStats instance
+ * @return pointer to initialized @ref JxlEncoderStats instance
*/
JXL_EXPORT JxlEncoderStats* JxlEncoderStatsCreate(void);
@@ -43,7 +43,7 @@ JXL_EXPORT JxlEncoderStats* JxlEncoderStatsCreate(void);
*/
JXL_EXPORT void JxlEncoderStatsDestroy(JxlEncoderStats* stats);
-/** Data type for querying JxlEncoderStats object
+/** Data type for querying @ref JxlEncoderStats object
*/
typedef enum {
JXL_ENC_STAT_HEADER_BITS,
diff --git a/third_party/jpeg-xl/lib/include/jxl/thread_parallel_runner.h b/third_party/jpeg-xl/lib/include/jxl/thread_parallel_runner.h
index 6166fe7d36..933f373ce9 100644
--- a/third_party/jpeg-xl/lib/include/jxl/thread_parallel_runner.h
+++ b/third_party/jpeg-xl/lib/include/jxl/thread_parallel_runner.h
@@ -41,24 +41,24 @@
extern "C" {
#endif
-/** Parallel runner internally using std::thread. Use as JxlParallelRunner.
+/** Parallel runner internally using std::thread. Use as @ref JxlParallelRunner.
*/
JXL_THREADS_EXPORT JxlParallelRetCode JxlThreadParallelRunner(
void* runner_opaque, void* jpegxl_opaque, JxlParallelRunInit init,
JxlParallelRunFunction func, uint32_t start_range, uint32_t end_range);
-/** Creates the runner for JxlThreadParallelRunner. Use as the opaque
+/** Creates the runner for @ref JxlThreadParallelRunner. Use as the opaque
* runner.
*/
JXL_THREADS_EXPORT void* JxlThreadParallelRunnerCreate(
const JxlMemoryManager* memory_manager, size_t num_worker_threads);
-/** Destroys the runner created by JxlThreadParallelRunnerCreate.
+/** Destroys the runner created by @ref JxlThreadParallelRunnerCreate.
*/
JXL_THREADS_EXPORT void JxlThreadParallelRunnerDestroy(void* runner_opaque);
/** Returns a default num_worker_threads value for
- * JxlThreadParallelRunnerCreate.
+ * @ref JxlThreadParallelRunnerCreate.
*/
JXL_THREADS_EXPORT size_t JxlThreadParallelRunnerDefaultNumWorkerThreads(void);
diff --git a/third_party/jpeg-xl/lib/include/jxl/types.h b/third_party/jpeg-xl/lib/include/jxl/types.h
index a47216f878..2538dbaa82 100644
--- a/third_party/jpeg-xl/lib/include/jxl/types.h
+++ b/third_party/jpeg-xl/lib/include/jxl/types.h
@@ -13,7 +13,6 @@
#ifndef JXL_TYPES_H_
#define JXL_TYPES_H_
-#include <jxl/jxl_export.h>
#include <stddef.h>
#include <stdint.h>
@@ -32,15 +31,17 @@ extern "C" {
#define JXL_TRUE 1
/** Portable @c false replacement. */
#define JXL_FALSE 0
-/** Converts of bool-like value to either JXL_TRUE or JXL_FALSE. */
+/** Converts of bool-like value to either ::JXL_TRUE or ::JXL_FALSE. */
#define TO_JXL_BOOL(C) (!!(C) ? JXL_TRUE : JXL_FALSE)
+/** Converts JXL_BOOL to C++ bool. */
+#define FROM_JXL_BOOL(C) (static_cast<bool>(C))
/** Data type for the sample values per channel per pixel.
*/
typedef enum {
/** Use 32-bit single-precision floating point values, with range 0.0-1.0
* (within gamut, may go outside this range for wide color gamut). Floating
- * point output, either JXL_TYPE_FLOAT or JXL_TYPE_FLOAT16, is recommended
+ * point output, either ::JXL_TYPE_FLOAT or ::JXL_TYPE_FLOAT16, is recommended
* for HDR and wide gamut images when color profile conversion is required. */
JXL_TYPE_FLOAT = 0,
@@ -92,8 +93,7 @@ typedef struct {
JxlDataType data_type;
/** Whether multi-byte data types are represented in big endian or little
- * endian format. This applies to JXL_TYPE_UINT16, JXL_TYPE_UINT32
- * and JXL_TYPE_FLOAT.
+ * endian format. This applies to ::JXL_TYPE_UINT16 and ::JXL_TYPE_FLOAT.
*/
JxlEndianness endianness;
diff --git a/third_party/jpeg-xl/lib/jpegli.cmake b/third_party/jpeg-xl/lib/jpegli.cmake
index f06912f438..a471c8b2ab 100644
--- a/third_party/jpeg-xl/lib/jpegli.cmake
+++ b/third_party/jpeg-xl/lib/jpegli.cmake
@@ -88,7 +88,6 @@ foreach (TESTFILE IN LISTS JPEGXL_INTERNAL_JPEGLI_TESTS)
target_link_libraries(${TESTNAME}
hwy
jpegli-static
- gmock
GTest::GTest
GTest::Main
${JPEG_LIBRARIES}
diff --git a/third_party/jpeg-xl/lib/jpegli/adaptive_quantization.cc b/third_party/jpeg-xl/lib/jpegli/adaptive_quantization.cc
index 6a8c4d3128..2039326cbd 100644
--- a/third_party/jpeg-xl/lib/jpegli/adaptive_quantization.cc
+++ b/third_party/jpeg-xl/lib/jpegli/adaptive_quantization.cc
@@ -5,6 +5,7 @@
#include "lib/jpegli/adaptive_quantization.h"
+#include <jxl/types.h>
#include <stddef.h>
#include <stdlib.h>
@@ -46,7 +47,7 @@ using hwy::HWY_NAMESPACE::Sqrt;
using hwy::HWY_NAMESPACE::Sub;
using hwy::HWY_NAMESPACE::ZeroIfNegative;
-static constexpr float kInputScaling = 1.0f / 255.0f;
+constexpr float kInputScaling = 1.0f / 255.0f;
// Primary template: default to actual division.
template <typename T, class V>
@@ -65,7 +66,7 @@ struct FastDivision<float, V> {
}
V operator()(const V n, const V d) const {
-#if 1 // Faster on SKX
+#if JXL_TRUE // Faster on SKX
return Div(n, d);
#else
return n * ReciprocalNR(d);
@@ -191,12 +192,12 @@ V ComputeMask(const D d, const V out_val) {
}
// mul and mul2 represent a scaling difference between jxl and butteraugli.
-static const float kSGmul = 226.0480446705883f;
-static const float kSGmul2 = 1.0f / 73.377132366608819f;
-static const float kLog2 = 0.693147181f;
+const float kSGmul = 226.0480446705883f;
+const float kSGmul2 = 1.0f / 73.377132366608819f;
+const float kLog2 = 0.693147181f;
// Includes correction factor for std::log -> log2.
-static const float kSGRetMul = kSGmul2 * 18.6580932135f * kLog2;
-static const float kSGVOffset = 7.14672470003f;
+const float kSGRetMul = kSGmul2 * 18.6580932135f * kLog2;
+const float kSGVOffset = 7.14672470003f;
template <bool invert, typename D, typename V>
V RatioOfDerivativesOfCubicRootToSimpleGamma(const D d, V v) {
@@ -226,7 +227,7 @@ V RatioOfDerivativesOfCubicRootToSimpleGamma(const D d, V v) {
}
template <bool invert = false>
-static float RatioOfDerivativesOfCubicRootToSimpleGamma(float v) {
+float RatioOfDerivativesOfCubicRootToSimpleGamma(float v) {
using DScalar = HWY_CAPPED(float, 1);
auto vscalar = Load(DScalar(), &v);
return GetLane(
@@ -503,7 +504,7 @@ HWY_EXPORT(PerBlockModulations);
namespace {
-static constexpr int kPreErosionBorder = 1;
+constexpr int kPreErosionBorder = 1;
} // namespace
diff --git a/third_party/jpeg-xl/lib/jpegli/bitstream.cc b/third_party/jpeg-xl/lib/jpegli/bitstream.cc
index 3448367dde..4dbeb738bb 100644
--- a/third_party/jpeg-xl/lib/jpegli/bitstream.cc
+++ b/third_party/jpeg-xl/lib/jpegli/bitstream.cc
@@ -90,8 +90,8 @@ bool EncodeDQT(j_compress_ptr cinfo, bool write_all_tables) {
JPEGLI_ERROR("Missing quant table %d", i);
}
int precision = 0;
- for (size_t k = 0; k < DCTSIZE2; ++k) {
- if (quant_table->quantval[k] > 255) {
+ for (UINT16 q : quant_table->quantval) {
+ if (q > 255) {
precision = 1;
is_baseline = false;
}
@@ -123,7 +123,6 @@ bool EncodeDQT(j_compress_ptr cinfo, bool write_all_tables) {
void EncodeSOF(j_compress_ptr cinfo, bool is_baseline) {
if (cinfo->data_precision != kJpegPrecision) {
- is_baseline = false;
JPEGLI_ERROR("Unsupported data precision %d", cinfo->data_precision);
}
const uint8_t marker = cinfo->progressive_mode ? 0xc2
@@ -302,7 +301,7 @@ void WriteBlock(const int32_t* JXL_RESTRICT symbols,
namespace {
-static JXL_INLINE void EmitMarker(JpegBitWriter* bw, int marker) {
+JXL_INLINE void EmitMarker(JpegBitWriter* bw, int marker) {
bw->data[bw->pos++] = 0xFF;
bw->data[bw->pos++] = marker;
}
diff --git a/third_party/jpeg-xl/lib/jpegli/bitstream.h b/third_party/jpeg-xl/lib/jpegli/bitstream.h
index aa54c73d7e..bed441aefe 100644
--- a/third_party/jpeg-xl/lib/jpegli/bitstream.h
+++ b/third_party/jpeg-xl/lib/jpegli/bitstream.h
@@ -32,9 +32,8 @@ void EncodeSOS(j_compress_ptr cinfo, int scan_index);
void WriteScanHeader(j_compress_ptr cinfo, int scan_index);
void WriteBlock(const int32_t* JXL_RESTRICT symbols,
- const int32_t* JXL_RESTRICT extra_bits, const int num_nonzeros,
- const bool emit_eob,
- const HuffmanCodeTable* JXL_RESTRICT dc_code,
+ const int32_t* JXL_RESTRICT extra_bits, int num_nonzeros,
+ bool emit_eob, const HuffmanCodeTable* JXL_RESTRICT dc_code,
const HuffmanCodeTable* JXL_RESTRICT ac_code,
JpegBitWriter* JXL_RESTRICT bw);
void WriteScanData(j_compress_ptr cinfo, int scan_index);
diff --git a/third_party/jpeg-xl/lib/jpegli/color_quantize.cc b/third_party/jpeg-xl/lib/jpegli/color_quantize.cc
index e8357e2160..c4f32bf439 100644
--- a/third_party/jpeg-xl/lib/jpegli/color_quantize.cc
+++ b/third_party/jpeg-xl/lib/jpegli/color_quantize.cc
@@ -11,13 +11,14 @@
#include "lib/jpegli/decode_internal.h"
#include "lib/jpegli/error.h"
+#include "lib/jxl/base/status.h"
namespace jpegli {
namespace {
-static constexpr int kNumColorCellBits[kMaxComponents] = {3, 4, 3, 3};
-static constexpr int kCompW[kMaxComponents] = {2, 3, 1, 1};
+constexpr int kNumColorCellBits[kMaxComponents] = {3, 4, 3, 3};
+constexpr int kCompW[kMaxComponents] = {2, 3, 1, 1};
int Pow(int a, int b) {
int r = 1;
@@ -102,8 +103,8 @@ namespace {
// 2^13 priority levels for the PQ seems to be a good compromise between
// accuracy, running time and stack space usage.
-static const int kMaxPriority = 1 << 13;
-static const int kMaxLevel = 3;
+const int kMaxPriority = 1 << 13;
+const int kMaxLevel = 3;
// This function is used in the multi-resolution grid to be able to compute
// the keys for the different resolutions by just shifting the first key.
@@ -153,7 +154,7 @@ inline int ColorIntQuadDistanceRGB(uint8_t r1, uint8_t g1, uint8_t b1,
}
inline int ScaleQuadDistanceRGB(int d) {
- return static_cast<int>(sqrt(d * 0.25) + 0.5);
+ return static_cast<int>(std::lround(sqrt(d * 0.25)));
}
// The function updates the minimal distances, the clustering and the
@@ -216,9 +217,9 @@ struct WangHasher {
// to a unique integer index assigned to the different colors in order of
// appearance in the image. Return the number of unique colors found.
// The colors are pre-quantized to 3 * 6 bits precision.
-static int BuildRGBColorIndex(const uint8_t* const image, int const num_pixels,
- int* const count, uint8_t* const red,
- uint8_t* const green, uint8_t* const blue) {
+int BuildRGBColorIndex(const uint8_t* const image, int const num_pixels,
+ int* const count, uint8_t* const red,
+ uint8_t* const green, uint8_t* const blue) {
// Impossible because rgb are in the low 24 bits, and the upper 8 bits is 0.
const uint32_t impossible_pixel_value = 0x10000000;
std::unordered_map<uint32_t, int, RGBPixelHasher> index_map(1 << 12);
@@ -264,7 +265,7 @@ void ChooseColorMap2Pass(j_decompress_ptr cinfo) {
std::unique_ptr<uint8_t[]> blue(new uint8_t[max_color_count]);
std::vector<int> count(max_color_count, 0);
// number of colors
- int n = BuildRGBColorIndex(m->pixels_, num_pixels, &count[0], &red[0],
+ int n = BuildRGBColorIndex(m->pixels_, num_pixels, count.data(), &red[0],
&green[0], &blue[0]);
std::vector<int> dist(n, std::numeric_limits<int>::max());
@@ -285,14 +286,14 @@ void ChooseColorMap2Pass(j_decompress_ptr cinfo) {
winner = i;
}
if (!in_palette[i] && count[i] > count_threshold) {
- AddToRGBPalette(&red[0], &green[0], &blue[0], &count[0], i, k++, n,
- &dist[0], &cluster[0], &center[0], &error);
+ AddToRGBPalette(&red[0], &green[0], &blue[0], count.data(), i, k++, n,
+ dist.data(), cluster.data(), &center[0], &error);
in_palette[i] = true;
}
}
if (k == 0) {
- AddToRGBPalette(&red[0], &green[0], &blue[0], &count[0], winner, k++, n,
- &dist[0], &cluster[0], &center[0], &error);
+ AddToRGBPalette(&red[0], &green[0], &blue[0], count.data(), winner, k++, n,
+ dist.data(), cluster.data(), &center[0], &error);
in_palette[winner] = true;
}
@@ -365,8 +366,8 @@ void ChooseColorMap2Pass(j_decompress_ptr cinfo) {
if (priority < top_priority) {
bucket_array[priority].push_back(i);
} else {
- AddToRGBPalette(&red[0], &green[0], &blue[0], &count[0], i, k++, n,
- &dist[0], &cluster[0], &center[0], &error);
+ AddToRGBPalette(&red[0], &green[0], &blue[0], count.data(), i, k++, n,
+ dist.data(), cluster.data(), &center[0], &error);
}
bucket_array[top_priority].pop_back();
while (top_priority >= 0 && bucket_array[top_priority].empty()) {
@@ -387,7 +388,7 @@ void ChooseColorMap2Pass(j_decompress_ptr cinfo) {
namespace {
-void FindCandidatesForCell(j_decompress_ptr cinfo, int ncomp, int cell[],
+void FindCandidatesForCell(j_decompress_ptr cinfo, int ncomp, const int cell[],
std::vector<uint8_t>* candidates) {
int cell_min[kMaxComponents];
int cell_max[kMaxComponents];
@@ -404,7 +405,8 @@ void FindCandidatesForCell(j_decompress_ptr cinfo, int ncomp, int cell[],
int dmax = 0;
for (int c = 0; c < ncomp; ++c) {
int palette_c = cinfo->colormap[c][i];
- int dminc = 0, dmaxc;
+ int dminc = 0;
+ int dmaxc;
if (palette_c < cell_min[c]) {
dminc = cell_min[c] - palette_c;
dmaxc = cell_max[c] - palette_c;
@@ -436,6 +438,8 @@ void FindCandidatesForCell(j_decompress_ptr cinfo, int ncomp, int cell[],
void CreateInverseColorMap(j_decompress_ptr cinfo) {
jpeg_decomp_master* m = cinfo->master;
int ncomp = cinfo->out_color_components;
+ JXL_ASSERT(ncomp > 0);
+ JXL_ASSERT(ncomp <= kMaxComponents);
int num_cells = 1;
for (int c = 0; c < ncomp; ++c) {
num_cells *= (1 << kNumColorCellBits[c]);
@@ -455,7 +459,7 @@ void CreateInverseColorMap(j_decompress_ptr cinfo) {
m->regenerate_inverse_colormap_ = false;
}
-int LookupColorIndex(j_decompress_ptr cinfo, JSAMPLE* pixel) {
+int LookupColorIndex(j_decompress_ptr cinfo, const JSAMPLE* pixel) {
jpeg_decomp_master* m = cinfo->master;
int num_channels = cinfo->out_color_components;
int index = 0;
diff --git a/third_party/jpeg-xl/lib/jpegli/color_quantize.h b/third_party/jpeg-xl/lib/jpegli/color_quantize.h
index 3dda1d8713..92b922f756 100644
--- a/third_party/jpeg-xl/lib/jpegli/color_quantize.h
+++ b/third_party/jpeg-xl/lib/jpegli/color_quantize.h
@@ -20,7 +20,7 @@ void CreateOrderedDitherTables(j_decompress_ptr cinfo);
void InitFSDitherState(j_decompress_ptr cinfo);
-int LookupColorIndex(j_decompress_ptr cinfo, JSAMPLE* pixel);
+int LookupColorIndex(j_decompress_ptr cinfo, const JSAMPLE* pixel);
} // namespace jpegli
diff --git a/third_party/jpeg-xl/lib/jpegli/dct-inl.h b/third_party/jpeg-xl/lib/jpegli/dct-inl.h
index 1cbe704002..66cc3b6b53 100644
--- a/third_party/jpeg-xl/lib/jpegli/dct-inl.h
+++ b/third_party/jpeg-xl/lib/jpegli/dct-inl.h
@@ -187,7 +187,7 @@ void DCT1D(const float* JXL_RESTRICT pixels, size_t pixels_stride,
}
}
-static JXL_INLINE JXL_MAYBE_UNUSED void TransformFromPixels(
+JXL_INLINE JXL_MAYBE_UNUSED void TransformFromPixels(
const float* JXL_RESTRICT pixels, size_t pixels_stride,
float* JXL_RESTRICT coefficients, float* JXL_RESTRICT scratch_space) {
DCT1D(pixels, pixels_stride, scratch_space);
@@ -196,14 +196,14 @@ static JXL_INLINE JXL_MAYBE_UNUSED void TransformFromPixels(
Transpose8x8Block(scratch_space, coefficients);
}
-static JXL_INLINE JXL_MAYBE_UNUSED void StoreQuantizedValue(const Vec<DI>& ival,
- int16_t* out) {
+JXL_INLINE JXL_MAYBE_UNUSED void StoreQuantizedValue(const Vec<DI>& ival,
+ int16_t* out) {
Rebind<int16_t, DI> di16;
Store(DemoteTo(di16, ival), di16, out);
}
-static JXL_INLINE JXL_MAYBE_UNUSED void StoreQuantizedValue(const Vec<DI>& ival,
- int32_t* out) {
+JXL_INLINE JXL_MAYBE_UNUSED void StoreQuantizedValue(const Vec<DI>& ival,
+ int32_t* out) {
DI di;
Store(ival, di, out);
}
diff --git a/third_party/jpeg-xl/lib/jpegli/decode.cc b/third_party/jpeg-xl/lib/jpegli/decode.cc
index 758babeb5e..9fdf68dd18 100644
--- a/third_party/jpeg-xl/lib/jpegli/decode.cc
+++ b/third_party/jpeg-xl/lib/jpegli/decode.cc
@@ -115,8 +115,10 @@ void InitProgressMonitor(j_decompress_ptr cinfo, bool coef_only) {
cinfo->progress->total_passes = 1;
} else {
int input_passes = !cinfo->buffered_image && m->is_multiscan_ ? 1 : 0;
- bool two_pass_quant = cinfo->quantize_colors && !cinfo->colormap &&
- cinfo->two_pass_quantize && cinfo->enable_2pass_quant;
+ bool two_pass_quant = FROM_JXL_BOOL(cinfo->quantize_colors) &&
+ (cinfo->colormap != nullptr) &&
+ FROM_JXL_BOOL(cinfo->two_pass_quantize) &&
+ FROM_JXL_BOOL(cinfo->enable_2pass_quant);
cinfo->progress->total_passes = input_passes + (two_pass_quant ? 2 : 1);
}
cinfo->progress->completed_passes = 0;
@@ -175,7 +177,7 @@ void BuildHuffmanLookupTable(j_decompress_ptr cinfo, JHUFF_TBL* table,
for (int i = 0; i < total_count; ++i) {
int value = table->huffval[i];
if (values_seen[value]) {
- return JPEGLI_ERROR("Duplicate Huffman code value %d", value);
+ JPEGLI_ERROR("Duplicate Huffman code value %d", value);
}
values_seen[value] = 1;
values[i] = value;
@@ -223,7 +225,7 @@ void PrepareForScan(j_decompress_ptr cinfo) {
HuffmanTableEntry* huff_lut =
&m->dc_huff_lut_[dc_tbl_idx * kJpegHuffmanLutSize];
if (!table) {
- return JPEGLI_ERROR("DC Huffman table %d not found", dc_tbl_idx);
+ JPEGLI_ERROR("DC Huffman table %d not found", dc_tbl_idx);
}
BuildHuffmanLookupTable(cinfo, table, huff_lut);
}
@@ -233,7 +235,7 @@ void PrepareForScan(j_decompress_ptr cinfo) {
HuffmanTableEntry* huff_lut =
&m->ac_huff_lut_[ac_tbl_idx * kJpegHuffmanLutSize];
if (!table) {
- return JPEGLI_ERROR("AC Huffman table %d not found", ac_tbl_idx);
+ JPEGLI_ERROR("AC Huffman table %d not found", ac_tbl_idx);
}
BuildHuffmanLookupTable(cinfo, table, huff_lut);
}
@@ -543,8 +545,8 @@ void jpegli_CreateDecompress(j_decompress_ptr cinfo, int version,
cinfo->is_decompressor = TRUE;
cinfo->progress = nullptr;
cinfo->src = nullptr;
- for (int i = 0; i < NUM_QUANT_TBLS; i++) {
- cinfo->quant_tbl_ptrs[i] = nullptr;
+ for (auto& quant_tbl_ptr : cinfo->quant_tbl_ptrs) {
+ quant_tbl_ptr = nullptr;
}
for (int i = 0; i < NUM_HUFF_TBLS; i++) {
cinfo->dc_huff_tbl_ptrs[i] = nullptr;
@@ -555,8 +557,8 @@ void jpegli_CreateDecompress(j_decompress_ptr cinfo, int version,
cinfo->rec_outbuf_height = 1; // output works with any buffer height
cinfo->master = new jpeg_decomp_master;
jpeg_decomp_master* m = cinfo->master;
- for (int i = 0; i < 16; ++i) {
- m->app_marker_parsers[i] = nullptr;
+ for (auto& app_marker_parser : m->app_marker_parsers) {
+ app_marker_parser = nullptr;
}
m->com_marker_parser = nullptr;
memset(m->markers_to_save_, 0, sizeof(m->markers_to_save_));
@@ -661,7 +663,7 @@ boolean jpegli_read_icc_profile(j_decompress_ptr cinfo, JOCTET** icc_data_ptr,
return FALSE;
}
*icc_data_len = m->icc_profile_.size();
- *icc_data_ptr = (JOCTET*)malloc(*icc_data_len);
+ *icc_data_ptr = static_cast<JOCTET*>(malloc(*icc_data_len));
if (*icc_data_ptr == nullptr) {
JPEGLI_ERROR("jpegli_read_icc_profile: Out of memory");
}
@@ -738,21 +740,26 @@ void jpegli_calc_output_dimensions(j_decompress_ptr cinfo) {
}
boolean jpegli_has_multiple_scans(j_decompress_ptr cinfo) {
- if (cinfo->input_scan_number == 0) {
- JPEGLI_ERROR("No SOS marker found.");
+ if (cinfo->global_state != jpegli::kDecHeaderDone &&
+ cinfo->global_state != jpegli::kDecProcessScan &&
+ cinfo->global_state != jpegli::kDecProcessMarkers) {
+ JPEGLI_ERROR("jpegli_has_multiple_scans: unexpected state %d",
+ cinfo->global_state);
}
- return cinfo->master->is_multiscan_;
+ return TO_JXL_BOOL(cinfo->master->is_multiscan_);
}
boolean jpegli_input_complete(j_decompress_ptr cinfo) {
- return cinfo->master->found_eoi_;
+ return TO_JXL_BOOL(cinfo->master->found_eoi_);
}
boolean jpegli_start_decompress(j_decompress_ptr cinfo) {
jpeg_decomp_master* m = cinfo->master;
if (cinfo->global_state == jpegli::kDecHeaderDone) {
- m->streaming_mode_ = !m->is_multiscan_ && !cinfo->buffered_image &&
- (!cinfo->quantize_colors || !cinfo->two_pass_quantize);
+ m->streaming_mode_ = !m->is_multiscan_ &&
+ !FROM_JXL_BOOL(cinfo->buffered_image) &&
+ (!FROM_JXL_BOOL(cinfo->quantize_colors) ||
+ !FROM_JXL_BOOL(cinfo->two_pass_quantize));
jpegli::AllocateCoefficientBuffer(cinfo);
jpegli_calc_output_dimensions(cinfo);
jpegli::PrepareForScan(cinfo);
diff --git a/third_party/jpeg-xl/lib/jpegli/decode_api_test.cc b/third_party/jpeg-xl/lib/jpegli/decode_api_test.cc
index c48b9377c3..0cc5a194d7 100644
--- a/third_party/jpeg-xl/lib/jpegli/decode_api_test.cc
+++ b/third_party/jpeg-xl/lib/jpegli/decode_api_test.cc
@@ -18,8 +18,8 @@
namespace jpegli {
namespace {
-static constexpr uint8_t kFakeEoiMarker[2] = {0xff, 0xd9};
-static constexpr size_t kNumSourceBuffers = 4;
+constexpr uint8_t kFakeEoiMarker[2] = {0xff, 0xd9};
+constexpr size_t kNumSourceBuffers = 4;
// Custom source manager that refills the input buffer in chunks, simulating
// a file reader with a fixed buffer size.
@@ -61,7 +61,7 @@ class SourceManager {
static void init_source(j_decompress_ptr cinfo) {}
static boolean fill_input_buffer(j_decompress_ptr cinfo) {
- auto src = reinterpret_cast<SourceManager*>(cinfo->src);
+ auto* src = reinterpret_cast<SourceManager*>(cinfo->src);
if (src->pos_ < src->len_) {
size_t chunk_size = std::min(src->len_ - src->pos_, src->max_chunk_size_);
size_t next_idx = ++src->chunk_idx_ % kNumSourceBuffers;
@@ -79,7 +79,7 @@ class SourceManager {
}
static void skip_input_data(j_decompress_ptr cinfo, long num_bytes) {
- auto src = reinterpret_cast<SourceManager*>(cinfo->src);
+ auto* src = reinterpret_cast<SourceManager*>(cinfo->src);
if (num_bytes <= 0) {
return;
}
@@ -166,9 +166,9 @@ void ReadOutputImage(const DecompressParams& dparams, j_decompress_ptr cinfo,
rowdata[c][i] =
y0 + i < ysize ? &output->raw_data[c][(y0 + i) * xsize] : nullptr;
}
- data[c] = &rowdata[c][0];
+ data[c] = rowdata[c].data();
}
- num_output_lines = jpegli_read_raw_data(cinfo, &data[0], max_lines);
+ num_output_lines = jpegli_read_raw_data(cinfo, data.data(), max_lines);
} else {
size_t max_output_lines = dparams.max_output_lines;
if (max_output_lines == 0) max_output_lines = cinfo->output_height;
@@ -189,7 +189,7 @@ void ReadOutputImage(const DecompressParams& dparams, j_decompress_ptr cinfo,
scanlines[i] = &output->pixels[yidx * stride];
}
num_output_lines =
- jpegli_read_scanlines(cinfo, &scanlines[0], max_lines);
+ jpegli_read_scanlines(cinfo, scanlines.data(), max_lines);
if (cinfo->quantize_colors) {
for (size_t i = 0; i < num_output_lines; ++i) {
UnmapColors(scanlines[i], cinfo->output_width,
@@ -222,7 +222,7 @@ struct TestConfig {
std::vector<uint8_t> GetTestJpegData(TestConfig& config) {
std::vector<uint8_t> compressed;
if (!config.fn.empty()) {
- compressed = ReadTestData(config.fn.c_str());
+ compressed = ReadTestData(config.fn);
} else {
GeneratePixels(&config.input);
JXL_CHECK(EncodeWithJpegli(config.input, config.jparams, &compressed));
@@ -297,10 +297,10 @@ void TestAPIBuffered(const CompressParams& jparams,
SetDecompressParams(dparams, cinfo);
jpegli_set_output_format(cinfo, dparams.data_type, dparams.endianness);
VerifyHeader(jparams, cinfo);
+ bool has_multiple_scans = FROM_JXL_BOOL(jpegli_has_multiple_scans(cinfo));
EXPECT_TRUE(jpegli_start_decompress(cinfo));
// start decompress should not read the whole input in buffered image mode
EXPECT_FALSE(jpegli_input_complete(cinfo));
- bool has_multiple_scans = jpegli_has_multiple_scans(cinfo);
EXPECT_EQ(0, cinfo->output_scan_number);
int sos_marker_cnt = 1; // read_header reads the first SOS marker
while (!jpegli_input_complete(cinfo)) {
@@ -341,8 +341,11 @@ void TestAPIBuffered(const CompressParams& jparams,
}
TEST(DecodeAPITest, ReuseCinfo) {
- TestImage input, output, expected;
- std::vector<TestImage> output_progression, expected_output_progression;
+ TestImage input;
+ TestImage output;
+ TestImage expected;
+ std::vector<TestImage> output_progression;
+ std::vector<TestImage> expected_output_progression;
CompressParams jparams;
DecompressParams dparams;
std::vector<uint8_t> compressed;
@@ -383,8 +386,8 @@ TEST(DecodeAPITest, ReuseCinfo) {
expected.Clear();
DecodeWithLibjpeg(jparams, dparams, compressed, &expected);
output.Clear();
- cinfo.buffered_image = false;
- cinfo.raw_data_out = false;
+ cinfo.buffered_image = JXL_FALSE;
+ cinfo.raw_data_out = JXL_FALSE;
cinfo.scale_num = cinfo.scale_denom = 1;
SourceManager src(compressed.data(), compressed.size(),
1u << 12);
@@ -1245,7 +1248,8 @@ std::ostream& operator<<(std::ostream& os, const DecompressParams& dparams) {
}
os << IOMethodName(dparams.data_type, dparams.endianness);
if (dparams.set_out_color_space) {
- os << "OutColor" << ColorSpaceName((J_COLOR_SPACE)dparams.out_color_space);
+ os << "OutColor"
+ << ColorSpaceName(static_cast<J_COLOR_SPACE>(dparams.out_color_space));
}
if (dparams.crop_output) {
os << "Crop";
@@ -1265,7 +1269,8 @@ std::ostream& operator<<(std::ostream& os, const DecompressParams& dparams) {
if (i > 0) os << "_";
const auto& sparam = dparams.scan_params[i];
os << QuantMode(sparam.color_quant_mode);
- os << DitherMode((J_DITHER_MODE)sparam.dither_mode) << "Dither";
+ os << DitherMode(static_cast<J_DITHER_MODE>(sparam.dither_mode))
+ << "Dither";
}
}
if (dparams.skip_scans) {
diff --git a/third_party/jpeg-xl/lib/jpegli/decode_internal.h b/third_party/jpeg-xl/lib/jpegli/decode_internal.h
index ed7baa39e9..37dfcc4526 100644
--- a/third_party/jpeg-xl/lib/jpegli/decode_internal.h
+++ b/third_party/jpeg-xl/lib/jpegli/decode_internal.h
@@ -45,12 +45,13 @@ struct jpeg_decomp_master {
size_t input_buffer_pos_;
// Number of bits after codestream_pos_ that were already processed.
size_t codestream_bits_ahead_;
- bool streaming_mode_;
// Coefficient buffers
jvirt_barray_ptr* coef_arrays;
JBLOCKARRAY coeff_rows[jpegli::kMaxComponents];
+ bool streaming_mode_;
+
//
// Marker data processing state.
//
@@ -58,6 +59,11 @@ struct jpeg_decomp_master {
bool found_dri_;
bool found_sof_;
bool found_eoi_;
+
+ // Whether this jpeg has multiple scans (progressive or non-interleaved
+ // sequential).
+ bool is_multiscan_;
+
size_t icc_index_;
size_t icc_total_;
std::vector<uint8_t> icc_profile_;
@@ -66,9 +72,6 @@ struct jpeg_decomp_master {
uint8_t markers_to_save_[32];
jpeg_marker_parser_method app_marker_parsers[16];
jpeg_marker_parser_method com_marker_parser;
- // Whether this jpeg has multiple scans (progressive or non-interleaved
- // sequential).
- bool is_multiscan_;
// Fields defined by SOF marker.
size_t iMCU_cols_;
@@ -96,9 +99,11 @@ struct jpeg_decomp_master {
//
int output_passes_done_;
JpegliDataType output_data_type_ = JPEGLI_TYPE_UINT8;
- bool swap_endianness_ = false;
size_t xoffset_;
+ bool swap_endianness_ = false;
bool need_context_rows_;
+ bool regenerate_inverse_colormap_;
+ bool apply_smoothing;
int min_scaled_dct_size;
int scaled_dct_size[jpegli::kMaxComponents];
@@ -127,7 +132,6 @@ struct jpeg_decomp_master {
uint8_t* pixels_;
JSAMPARRAY scanlines_;
std::vector<std::vector<uint8_t>> candidate_lists_;
- bool regenerate_inverse_colormap_;
float* dither_[jpegli::kMaxComponents];
float* error_row_[2 * jpegli::kMaxComponents];
size_t dither_size_;
@@ -145,7 +149,6 @@ struct jpeg_decomp_master {
// i.e. the bottom half when rendering incomplete scans.
int (*coef_bits_latch)[SAVED_COEFS];
int (*prev_coef_bits_latch)[SAVED_COEFS];
- bool apply_smoothing;
};
#endif // LIB_JPEGLI_DECODE_INTERNAL_H_
diff --git a/third_party/jpeg-xl/lib/jpegli/decode_marker.cc b/third_party/jpeg-xl/lib/jpegli/decode_marker.cc
index c5c5790cdf..a9ed4df329 100644
--- a/third_party/jpeg-xl/lib/jpegli/decode_marker.cc
+++ b/third_party/jpeg-xl/lib/jpegli/decode_marker.cc
@@ -5,6 +5,7 @@
#include "lib/jpegli/decode_marker.h"
+#include <jxl/types.h>
#include <string.h>
#include "lib/jpegli/common.h"
@@ -22,23 +23,22 @@ constexpr uint8_t kIccProfileTag[12] = "ICC_PROFILE";
// Macros for commonly used error conditions.
-#define JPEG_VERIFY_LEN(n) \
- if (pos + (n) > len) { \
- return JPEGLI_ERROR("Unexpected end of marker: pos=%" PRIuS \
- " need=%d len=%" PRIuS, \
- pos, static_cast<int>(n), len); \
+#define JPEG_VERIFY_LEN(n) \
+ if (pos + (n) > len) { \
+ JPEGLI_ERROR("Unexpected end of marker: pos=%" PRIuS \
+ " need=%d len=%" PRIuS, \
+ pos, static_cast<int>(n), len); \
}
-#define JPEG_VERIFY_INPUT(var, low, high) \
- if ((var) < (low) || (var) > (high)) { \
- return JPEGLI_ERROR("Invalid " #var ": %d", static_cast<int>(var)); \
+#define JPEG_VERIFY_INPUT(var, low, high) \
+ if ((var) < (low) || (var) > (high)) { \
+ JPEGLI_ERROR("Invalid " #var ": %d", static_cast<int>(var)); \
}
-#define JPEG_VERIFY_MARKER_END() \
- if (pos != len) { \
- return JPEGLI_ERROR("Invalid marker length: declared=%" PRIuS \
- " actual=%" PRIuS, \
- len, pos); \
+#define JPEG_VERIFY_MARKER_END() \
+ if (pos != len) { \
+ JPEGLI_ERROR("Invalid marker length: declared=%" PRIuS " actual=%" PRIuS, \
+ len, pos); \
}
inline int ReadUint8(const uint8_t* data, size_t* pos) {
@@ -60,7 +60,7 @@ void ProcessSOF(j_decompress_ptr cinfo, const uint8_t* data, size_t len) {
JPEGLI_ERROR("Duplicate SOF marker.");
}
m->found_sof_ = true;
- cinfo->progressive_mode = (cinfo->unread_marker == 0xc2);
+ cinfo->progressive_mode = TO_JXL_BOOL(cinfo->unread_marker == 0xc2);
cinfo->arith_code = 0;
size_t pos = 2;
JPEG_VERIFY_LEN(6);
@@ -181,7 +181,7 @@ void ProcessSOS(j_decompress_ptr cinfo, const uint8_t* data, size_t len) {
for (int i = 0; i < cinfo->comps_in_scan; ++i) {
int id = ReadUint8(data, &pos);
if (ids_seen[id]) { // (cf. section B.2.3, regarding CSj)
- return JPEGLI_ERROR("Duplicate ID %d in SOS.", id);
+ JPEGLI_ERROR("Duplicate ID %d in SOS.", id);
}
ids_seen[id] = 1;
jpeg_component_info* comp = nullptr;
@@ -192,8 +192,7 @@ void ProcessSOS(j_decompress_ptr cinfo, const uint8_t* data, size_t len) {
}
}
if (!comp) {
- return JPEGLI_ERROR("SOS marker: Could not find component with id %d",
- id);
+ JPEGLI_ERROR("SOS marker: Could not find component with id %d", id);
}
int c = ReadUint8(data, &pos);
comp->dc_tbl_no = c >> 4;
@@ -222,7 +221,7 @@ void ProcessSOS(j_decompress_ptr cinfo, const uint8_t* data, size_t len) {
if (cinfo->input_scan_number == 0) {
m->is_multiscan_ = (cinfo->comps_in_scan < cinfo->num_components ||
- cinfo->progressive_mode);
+ FROM_JXL_BOOL(cinfo->progressive_mode));
}
if (cinfo->Ah != 0 && cinfo->Al != cinfo->Ah - 1) {
// section G.1.1.1.2 : Successive approximation control only improves
@@ -261,12 +260,12 @@ void ProcessSOS(j_decompress_ptr cinfo, const uint8_t* data, size_t len) {
int comp_idx = cinfo->cur_comp_info[i]->component_index;
for (int k = cinfo->Ss; k <= cinfo->Se; ++k) {
if (m->scan_progression_[comp_idx][k] & scan_bitmask) {
- return JPEGLI_ERROR(
+ JPEGLI_ERROR(
"Overlapping scans: component=%d k=%d prev_mask: %u cur_mask %u",
comp_idx, k, m->scan_progression_[i][k], scan_bitmask);
}
if (m->scan_progression_[comp_idx][k] & refinement_bitmask) {
- return JPEGLI_ERROR(
+ JPEGLI_ERROR(
"Invalid scan order, a more refined scan was already done: "
"component=%d k=%d prev_mask=%u cur_mask=%u",
comp_idx, k, m->scan_progression_[i][k], scan_bitmask);
@@ -275,7 +274,7 @@ void ProcessSOS(j_decompress_ptr cinfo, const uint8_t* data, size_t len) {
}
}
if (cinfo->Al > 10) {
- return JPEGLI_ERROR("Scan parameter Al=%d is not supported.", cinfo->Al);
+ JPEGLI_ERROR("Scan parameter Al=%d is not supported.", cinfo->Al);
}
}
@@ -285,7 +284,7 @@ void ProcessSOS(j_decompress_ptr cinfo, const uint8_t* data, size_t len) {
void ProcessDHT(j_decompress_ptr cinfo, const uint8_t* data, size_t len) {
size_t pos = 2;
if (pos == len) {
- return JPEGLI_ERROR("DHT marker: no Huffman table found");
+ JPEGLI_ERROR("DHT marker: no Huffman table found");
}
while (pos < len) {
JPEG_VERIFY_LEN(1 + kJpegHuffmanMaxBitLength);
@@ -293,7 +292,7 @@ void ProcessDHT(j_decompress_ptr cinfo, const uint8_t* data, size_t len) {
// component Huffman codes, 0x10 is added to the index.
int slot_id = ReadUint8(data, &pos);
int huffman_index = slot_id;
- int is_ac_table = (slot_id & 0x10) != 0;
+ bool is_ac_table = ((slot_id & 0x10) != 0);
JHUFF_TBL** table;
if (is_ac_table) {
huffman_index -= 0x10;
@@ -343,7 +342,7 @@ void ProcessDQT(j_decompress_ptr cinfo, const uint8_t* data, size_t len) {
}
size_t pos = 2;
if (pos == len) {
- return JPEGLI_ERROR("DQT marker: no quantization table found");
+ JPEGLI_ERROR("DQT marker: no quantization table found");
}
while (pos < len) {
JPEG_VERIFY_LEN(1);
@@ -377,7 +376,7 @@ void ProcessDNL(j_decompress_ptr cinfo, const uint8_t* data, size_t len) {
void ProcessDRI(j_decompress_ptr cinfo, const uint8_t* data, size_t len) {
jpeg_decomp_master* m = cinfo->master;
if (m->found_dri_) {
- return JPEGLI_ERROR("Duplicate DRI marker.");
+ JPEGLI_ERROR("Duplicate DRI marker.");
}
m->found_dri_ = true;
size_t pos = 2;
@@ -411,24 +410,24 @@ void ProcessAPP(j_decompress_ptr cinfo, const uint8_t* data, size_t len) {
payload += sizeof(kIccProfileTag);
payload_size -= sizeof(kIccProfileTag);
if (payload_size < 2) {
- return JPEGLI_ERROR("ICC chunk is too small.");
+ JPEGLI_ERROR("ICC chunk is too small.");
}
uint8_t index = payload[0];
uint8_t total = payload[1];
++m->icc_index_;
if (m->icc_index_ != index) {
- return JPEGLI_ERROR("Invalid ICC chunk order.");
+ JPEGLI_ERROR("Invalid ICC chunk order.");
}
if (total == 0) {
- return JPEGLI_ERROR("Invalid ICC chunk total.");
+ JPEGLI_ERROR("Invalid ICC chunk total.");
}
if (m->icc_total_ == 0) {
m->icc_total_ = total;
} else if (m->icc_total_ != total) {
- return JPEGLI_ERROR("Invalid ICC chunk total.");
+ JPEGLI_ERROR("Invalid ICC chunk total.");
}
if (m->icc_index_ > m->icc_total_) {
- return JPEGLI_ERROR("Invalid ICC chunk index.");
+ JPEGLI_ERROR("Invalid ICC chunk index.");
}
m->icc_profile_.insert(m->icc_profile_.end(), payload + 2,
payload + payload_size);
@@ -494,8 +493,8 @@ uint8_t ProcessNextMarker(j_decompress_ptr cinfo, const uint8_t* const data,
marker = data[*pos + 1];
if (num_skipped > 0) {
if (m->found_soi_) {
- JPEGLI_WARN("Skipped %d bytes before marker 0x%02x", (int)num_skipped,
- marker);
+ JPEGLI_WARN("Skipped %d bytes before marker 0x%02x",
+ static_cast<int>(num_skipped), marker);
} else {
JPEGLI_ERROR("Did not find SOI marker.");
}
diff --git a/third_party/jpeg-xl/lib/jpegli/decode_marker.h b/third_party/jpeg-xl/lib/jpegli/decode_marker.h
index fb24b3ee87..f3d47f6ad2 100644
--- a/third_party/jpeg-xl/lib/jpegli/decode_marker.h
+++ b/third_party/jpeg-xl/lib/jpegli/decode_marker.h
@@ -22,8 +22,8 @@ namespace jpegli {
// EOI marker. Input buffer refill is handled by the caller;
// * JPEG_REACHED_SOS, if the next SOS marker is found;
// * JPEG_REACHED_EOR, if the end of the input is found.
-int ProcessMarkers(j_decompress_ptr cinfo, const uint8_t* const data,
- const size_t len, size_t* pos);
+int ProcessMarkers(j_decompress_ptr cinfo, const uint8_t* data, size_t len,
+ size_t* pos);
jpeg_marker_parser_method GetMarkerProcessor(j_decompress_ptr cinfo);
diff --git a/third_party/jpeg-xl/lib/jpegli/decode_scan.cc b/third_party/jpeg-xl/lib/jpegli/decode_scan.cc
index 05b1f37220..1b50792f0a 100644
--- a/third_party/jpeg-xl/lib/jpegli/decode_scan.cc
+++ b/third_party/jpeg-xl/lib/jpegli/decode_scan.cc
@@ -61,7 +61,7 @@ struct BitReaderState {
if (bits_left_ <= 16) {
while (bits_left_ <= 56) {
val_ <<= 8;
- val_ |= (uint64_t)GetNextByte();
+ val_ |= static_cast<uint64_t>(GetNextByte());
bits_left_ += 8;
}
}
@@ -427,7 +427,7 @@ void PrepareForiMCURow(j_decompress_ptr cinfo) {
int offset = m->streaming_mode_ ? 0 : by0;
m->coeff_rows[c] = (*cinfo->mem->access_virt_barray)(
reinterpret_cast<j_common_ptr>(cinfo), m->coef_arrays[c], offset,
- max_block_rows, true);
+ max_block_rows, TRUE);
}
}
@@ -451,7 +451,8 @@ int ProcessScan(j_decompress_ptr cinfo, const uint8_t* const data,
++num_skipped;
}
if (num_skipped > 0) {
- JPEGLI_WARN("Skipped %d bytes before restart marker", (int)num_skipped);
+ JPEGLI_WARN("Skipped %d bytes before restart marker",
+ static_cast<int>(num_skipped));
}
if (*pos + 2 > len) {
return kNeedMoreInput;
@@ -471,7 +472,7 @@ int ProcessScan(j_decompress_ptr cinfo, const uint8_t* const data,
}
// Decode one MCU.
- HWY_ALIGN_MAX coeff_t sink_block[DCTSIZE2];
+ HWY_ALIGN_MAX static coeff_t sink_block[DCTSIZE2] = {0};
bool scan_ok = true;
for (int i = 0; i < cinfo->comps_in_scan; ++i) {
const jpeg_component_info* comp = cinfo->cur_comp_info[i];
diff --git a/third_party/jpeg-xl/lib/jpegli/decode_scan.h b/third_party/jpeg-xl/lib/jpegli/decode_scan.h
index 1d7b18fc1a..dd1bfcd110 100644
--- a/third_party/jpeg-xl/lib/jpegli/decode_scan.h
+++ b/third_party/jpeg-xl/lib/jpegli/decode_scan.h
@@ -21,8 +21,8 @@ namespace jpegli {
// * JPEG_SUSPENDED, if the input buffer ends before the end of an iMCU row;
// * JPEG_ROW_COMPLETED, if the next iMCU row (but not the scan) is reached;
// * JPEG_SCAN_COMPLETED, if the end of the scan is reached.
-int ProcessScan(j_decompress_ptr cinfo, const uint8_t* const data,
- const size_t len, size_t* pos, size_t* bit_pos);
+int ProcessScan(j_decompress_ptr cinfo, const uint8_t* data, size_t len,
+ size_t* pos, size_t* bit_pos);
void PrepareForiMCURow(j_decompress_ptr cinfo);
diff --git a/third_party/jpeg-xl/lib/jpegli/destination_manager.cc b/third_party/jpeg-xl/lib/jpegli/destination_manager.cc
index 9bc269f0c9..6548130866 100644
--- a/third_party/jpeg-xl/lib/jpegli/destination_manager.cc
+++ b/third_party/jpeg-xl/lib/jpegli/destination_manager.cc
@@ -19,13 +19,13 @@ struct StdioDestinationManager {
uint8_t* buffer;
static void init_destination(j_compress_ptr cinfo) {
- auto dest = reinterpret_cast<StdioDestinationManager*>(cinfo->dest);
+ auto* dest = reinterpret_cast<StdioDestinationManager*>(cinfo->dest);
dest->pub.next_output_byte = dest->buffer;
dest->pub.free_in_buffer = kDestBufferSize;
}
static boolean empty_output_buffer(j_compress_ptr cinfo) {
- auto dest = reinterpret_cast<StdioDestinationManager*>(cinfo->dest);
+ auto* dest = reinterpret_cast<StdioDestinationManager*>(cinfo->dest);
if (fwrite(dest->buffer, 1, kDestBufferSize, dest->f) != kDestBufferSize) {
JPEGLI_ERROR("Failed to write to output stream.");
}
@@ -35,7 +35,7 @@ struct StdioDestinationManager {
}
static void term_destination(j_compress_ptr cinfo) {
- auto dest = reinterpret_cast<StdioDestinationManager*>(cinfo->dest);
+ auto* dest = reinterpret_cast<StdioDestinationManager*>(cinfo->dest);
size_t bytes_left = kDestBufferSize - dest->pub.free_in_buffer;
if (bytes_left &&
fwrite(dest->buffer, 1, bytes_left, dest->f) != bytes_left) {
@@ -62,7 +62,7 @@ struct MemoryDestinationManager {
static void init_destination(j_compress_ptr cinfo) {}
static boolean empty_output_buffer(j_compress_ptr cinfo) {
- auto dest = reinterpret_cast<MemoryDestinationManager*>(cinfo->dest);
+ auto* dest = reinterpret_cast<MemoryDestinationManager*>(cinfo->dest);
uint8_t* next_buffer =
reinterpret_cast<uint8_t*>(malloc(dest->buffer_size * 2));
memcpy(next_buffer, dest->current_buffer, dest->buffer_size);
@@ -80,7 +80,7 @@ struct MemoryDestinationManager {
}
static void term_destination(j_compress_ptr cinfo) {
- auto dest = reinterpret_cast<MemoryDestinationManager*>(cinfo->dest);
+ auto* dest = reinterpret_cast<MemoryDestinationManager*>(cinfo->dest);
*dest->output_size = dest->buffer_size - dest->pub.free_in_buffer;
}
};
@@ -99,7 +99,7 @@ void jpegli_stdio_dest(j_compress_ptr cinfo, FILE* outfile) {
cinfo->dest = reinterpret_cast<jpeg_destination_mgr*>(
jpegli::Allocate<jpegli::StdioDestinationManager>(cinfo, 1));
}
- auto dest = reinterpret_cast<jpegli::StdioDestinationManager*>(cinfo->dest);
+ auto* dest = reinterpret_cast<jpegli::StdioDestinationManager*>(cinfo->dest);
dest->f = outfile;
dest->buffer = jpegli::Allocate<uint8_t>(cinfo, jpegli::kDestBufferSize);
dest->pub.next_output_byte = dest->buffer;
@@ -122,11 +122,11 @@ void jpegli_mem_dest(j_compress_ptr cinfo, unsigned char** outbuffer,
JPEGLI_ERROR("jpegli_mem_dest: a different dest manager was already set");
}
if (!cinfo->dest) {
- auto dest = jpegli::Allocate<jpegli::MemoryDestinationManager>(cinfo, 1);
+ auto* dest = jpegli::Allocate<jpegli::MemoryDestinationManager>(cinfo, 1);
dest->temp_buffer = nullptr;
cinfo->dest = reinterpret_cast<jpeg_destination_mgr*>(dest);
}
- auto dest = reinterpret_cast<jpegli::MemoryDestinationManager*>(cinfo->dest);
+ auto* dest = reinterpret_cast<jpegli::MemoryDestinationManager*>(cinfo->dest);
dest->pub.init_destination =
jpegli::MemoryDestinationManager::init_destination;
dest->pub.empty_output_buffer =
diff --git a/third_party/jpeg-xl/lib/jpegli/downsample.cc b/third_party/jpeg-xl/lib/jpegli/downsample.cc
index df2c156972..f1e945d509 100644
--- a/third_party/jpeg-xl/lib/jpegli/downsample.cc
+++ b/third_party/jpeg-xl/lib/jpegli/downsample.cc
@@ -29,7 +29,7 @@ void DownsampleRow2x1(const float* row_in, size_t len, float* row_out) {
const size_t N = Lanes(d);
const size_t len_out = len / 2;
const auto mul = Set(d, 0.5f);
- Vec<D> v0, v1;
+ Vec<D> v0, v1; // NOLINT
for (size_t x = 0; x < len_out; x += N) {
LoadInterleaved2(d, row_in + 2 * x, v0, v1);
Store(Mul(mul, Add(v0, v1)), d, row_out + x);
@@ -40,7 +40,7 @@ void DownsampleRow3x1(const float* row_in, size_t len, float* row_out) {
const size_t N = Lanes(d);
const size_t len_out = len / 3;
const auto mul = Set(d, 1.0f / 3);
- Vec<D> v0, v1, v2;
+ Vec<D> v0, v1, v2; // NOLINT
for (size_t x = 0; x < len_out; x += N) {
LoadInterleaved3(d, row_in + 3 * x, v0, v1, v2);
Store(Mul(mul, Add(Add(v0, v1), v2)), d, row_out + x);
@@ -51,7 +51,7 @@ void DownsampleRow4x1(const float* row_in, size_t len, float* row_out) {
const size_t N = Lanes(d);
const size_t len_out = len / 4;
const auto mul = Set(d, 0.25f);
- Vec<D> v0, v1, v2, v3;
+ Vec<D> v0, v1, v2, v3; // NOLINT
for (size_t x = 0; x < len_out; x += N) {
LoadInterleaved4(d, row_in + 4 * x, v0, v1, v2, v3);
Store(Mul(mul, Add(Add(v0, v1), Add(v2, v3))), d, row_out + x);
@@ -91,7 +91,7 @@ void Downsample2x2(float* rows_in[MAX_SAMP_FACTOR], size_t len,
const auto mul = Set(d, 0.25f);
float* row0 = rows_in[0];
float* row1 = rows_in[1];
- Vec<D> v0, v1, v2, v3;
+ Vec<D> v0, v1, v2, v3; // NOLINT
for (size_t x = 0; x < len_out; x += N) {
LoadInterleaved2(d, row0 + 2 * x, v0, v1);
LoadInterleaved2(d, row1 + 2 * x, v2, v3);
diff --git a/third_party/jpeg-xl/lib/jpegli/encode.cc b/third_party/jpeg-xl/lib/jpegli/encode.cc
index 8a106e239a..5326f2cb0f 100644
--- a/third_party/jpeg-xl/lib/jpegli/encode.cc
+++ b/third_party/jpeg-xl/lib/jpegli/encode.cc
@@ -5,6 +5,8 @@
#include "lib/jpegli/encode.h"
+#include <jxl/types.h>
+
#include <cmath>
#include <initializer_list>
#include <vector>
@@ -323,8 +325,8 @@ void ProcessCompressionParams(j_compress_ptr cinfo) {
if (cinfo->scan_info == nullptr) {
SetDefaultScanScript(cinfo);
}
- cinfo->progressive_mode =
- cinfo->scan_info->Ss != 0 || cinfo->scan_info->Se != DCTSIZE2 - 1;
+ cinfo->progressive_mode = TO_JXL_BOOL(cinfo->scan_info->Ss != 0 ||
+ cinfo->scan_info->Se != DCTSIZE2 - 1);
ValidateScanScript(cinfo);
m->scan_token_info =
Allocate<ScanTokenInfo>(cinfo, cinfo->num_scans, JPOOL_IMAGE);
@@ -449,7 +451,7 @@ void AllocateBuffers(j_compress_ptr cinfo) {
const size_t ysize_blocks = comp->height_in_blocks;
m->coeff_buffers[c] = (*cinfo->mem->request_virt_barray)(
reinterpret_cast<j_common_ptr>(cinfo), JPOOL_IMAGE,
- /*pre_zero=*/false, xsize_blocks, ysize_blocks, comp->v_samp_factor);
+ /*pre_zero=*/FALSE, xsize_blocks, ysize_blocks, comp->v_samp_factor);
}
}
if (m->use_adaptive_quantization) {
@@ -663,8 +665,8 @@ void jpegli_CreateCompress(j_compress_ptr cinfo, int version,
cinfo->num_components = 0;
cinfo->jpeg_color_space = JCS_UNKNOWN;
cinfo->comp_info = nullptr;
- for (int i = 0; i < NUM_QUANT_TBLS; ++i) {
- cinfo->quant_tbl_ptrs[i] = nullptr;
+ for (auto& quant_tbl_ptr : cinfo->quant_tbl_ptrs) {
+ quant_tbl_ptr = nullptr;
}
for (int i = 0; i < NUM_HUFF_TBLS; ++i) {
cinfo->dc_huff_tbl_ptrs[i] = nullptr;
@@ -673,7 +675,7 @@ void jpegli_CreateCompress(j_compress_ptr cinfo, int version,
memset(cinfo->arith_dc_L, 0, sizeof(cinfo->arith_dc_L));
memset(cinfo->arith_dc_U, 0, sizeof(cinfo->arith_dc_U));
memset(cinfo->arith_ac_K, 0, sizeof(cinfo->arith_ac_K));
- cinfo->write_Adobe_marker = false;
+ cinfo->write_Adobe_marker = FALSE;
cinfo->master = jpegli::Allocate<jpeg_comp_master>(cinfo, 1);
jpegli::InitializeCompressParams(cinfo);
cinfo->master->force_baseline = true;
@@ -763,7 +765,7 @@ void jpegli_set_colorspace(j_compress_ptr cinfo, J_COLOR_SPACE colorspace) {
JPEGLI_ERROR("Unsupported jpeg colorspace %d", colorspace);
}
// Adobe marker is only needed to distinguish CMYK and YCCK JPEGs.
- cinfo->write_Adobe_marker = (cinfo->jpeg_color_space == JCS_YCCK);
+ cinfo->write_Adobe_marker = TO_JXL_BOOL(cinfo->jpeg_color_space == JCS_YCCK);
if (cinfo->comp_info == nullptr) {
cinfo->comp_info =
jpegli::Allocate<jpeg_component_info>(cinfo, MAX_COMPONENTS);
@@ -810,7 +812,7 @@ void jpegli_set_colorspace(j_compress_ptr cinfo, J_COLOR_SPACE colorspace) {
void jpegli_set_distance(j_compress_ptr cinfo, float distance,
boolean force_baseline) {
CheckState(cinfo, jpegli::kEncStart);
- cinfo->master->force_baseline = force_baseline;
+ cinfo->master->force_baseline = FROM_JXL_BOOL(force_baseline);
float distances[NUM_QUANT_TBLS] = {distance, distance, distance};
jpegli::SetQuantMatrices(cinfo, distances, /*add_two_chroma_tables=*/true);
}
@@ -834,7 +836,7 @@ void jpegli_set_psnr(j_compress_ptr cinfo, float psnr, float tolerance,
void jpegli_set_quality(j_compress_ptr cinfo, int quality,
boolean force_baseline) {
CheckState(cinfo, jpegli::kEncStart);
- cinfo->master->force_baseline = force_baseline;
+ cinfo->master->force_baseline = FROM_JXL_BOOL(force_baseline);
float distance = jpegli_quality_to_distance(quality);
float distances[NUM_QUANT_TBLS] = {distance, distance, distance};
jpegli::SetQuantMatrices(cinfo, distances, /*add_two_chroma_tables=*/false);
@@ -843,7 +845,7 @@ void jpegli_set_quality(j_compress_ptr cinfo, int quality,
void jpegli_set_linear_quality(j_compress_ptr cinfo, int scale_factor,
boolean force_baseline) {
CheckState(cinfo, jpegli::kEncStart);
- cinfo->master->force_baseline = force_baseline;
+ cinfo->master->force_baseline = FROM_JXL_BOOL(force_baseline);
float distance = jpegli::LinearQualityToDistance(scale_factor);
float distances[NUM_QUANT_TBLS] = {distance, distance, distance};
jpegli::SetQuantMatrices(cinfo, distances, /*add_two_chroma_tables=*/false);
@@ -894,7 +896,7 @@ void jpegli_add_quant_table(j_compress_ptr cinfo, int which_tbl,
void jpegli_enable_adaptive_quantization(j_compress_ptr cinfo, boolean value) {
CheckState(cinfo, jpegli::kEncStart);
- cinfo->master->use_adaptive_quantization = value;
+ cinfo->master->use_adaptive_quantization = FROM_JXL_BOOL(value);
}
void jpegli_simple_progression(j_compress_ptr cinfo) {
@@ -955,7 +957,7 @@ void jpegli_copy_critical_parameters(j_decompress_ptr srcinfo,
jpegli_set_colorspace(dstinfo, srcinfo->jpeg_color_space);
if (dstinfo->num_components != srcinfo->num_components) {
const auto& cinfo = dstinfo;
- return JPEGLI_ERROR("Mismatch between src colorspace and components");
+ JPEGLI_ERROR("Mismatch between src colorspace and components");
}
dstinfo->data_precision = srcinfo->data_precision;
dstinfo->CCIR601_sampling = srcinfo->CCIR601_sampling;
@@ -1005,7 +1007,7 @@ void jpegli_write_coefficients(j_compress_ptr cinfo,
jvirt_barray_ptr* coef_arrays) {
CheckState(cinfo, jpegli::kEncStart);
cinfo->global_state = jpegli::kEncWriteCoeffs;
- jpegli::InitCompress(cinfo, /*write_all_tables=*/true);
+ jpegli::InitCompress(cinfo, /*write_all_tables=*/TRUE);
cinfo->master->coeff_buffers = coef_arrays;
cinfo->next_scanline = cinfo->image_height;
cinfo->master->next_input_row = cinfo->image_height;
@@ -1047,7 +1049,7 @@ void jpegli_write_m_header(j_compress_ptr cinfo, int marker,
marker_data[1] = marker;
marker_data[2] = (datalen + 2) >> 8;
marker_data[3] = (datalen + 2) & 0xff;
- jpegli::WriteOutput(cinfo, &marker_data[0], 4);
+ jpegli::WriteOutput(cinfo, marker_data.data(), 4);
}
void jpegli_write_m_byte(j_compress_ptr cinfo, int val) {
@@ -1213,7 +1215,8 @@ void jpegli_finish_compress(j_compress_ptr cinfo) {
}
const bool tokens_done = jpegli::IsStreamingSupported(cinfo);
- const bool bitstream_done = tokens_done && !cinfo->optimize_coding;
+ const bool bitstream_done =
+ tokens_done && !FROM_JXL_BOOL(cinfo->optimize_coding);
if (!tokens_done) {
jpegli::TokenizeJpeg(cinfo);
diff --git a/third_party/jpeg-xl/lib/jpegli/encode_api_test.cc b/third_party/jpeg-xl/lib/jpegli/encode_api_test.cc
index 8d53557567..1afdcf610d 100644
--- a/third_party/jpeg-xl/lib/jpegli/encode_api_test.cc
+++ b/third_party/jpeg-xl/lib/jpegli/encode_api_test.cc
@@ -133,12 +133,11 @@ TEST(EncodeAPITest, ReuseCinfoSameMemOutput) {
jpegli_destroy_compress(&cinfo);
}
size_t pos = 0;
- for (size_t i = 0; i < all_configs.size(); ++i) {
+ for (auto& config : all_configs) {
TestImage output;
- pos +=
- DecodeWithLibjpeg(all_configs[i].jparams, DecompressParams(), nullptr,
- 0, buffer + pos, buffer_size - pos, &output);
- VerifyOutputImage(all_configs[i].input, output, all_configs[i].max_dist);
+ pos += DecodeWithLibjpeg(config.jparams, DecompressParams(), nullptr, 0,
+ buffer + pos, buffer_size - pos, &output);
+ VerifyOutputImage(config.input, output, config.max_dist);
}
if (buffer) free(buffer);
}
@@ -164,20 +163,21 @@ TEST(EncodeAPITest, ReuseCinfoSameStdOutput) {
size_t total_size = ftell(tmpf);
rewind(tmpf);
std::vector<uint8_t> compressed(total_size);
- JXL_CHECK(total_size == fread(&compressed[0], 1, total_size, tmpf));
+ JXL_CHECK(total_size == fread(compressed.data(), 1, total_size, tmpf));
fclose(tmpf);
size_t pos = 0;
- for (size_t i = 0; i < all_configs.size(); ++i) {
+ for (auto& config : all_configs) {
TestImage output;
- pos += DecodeWithLibjpeg(all_configs[i].jparams, DecompressParams(),
- nullptr, 0, &compressed[pos],
- compressed.size() - pos, &output);
- VerifyOutputImage(all_configs[i].input, output, all_configs[i].max_dist);
+ pos +=
+ DecodeWithLibjpeg(config.jparams, DecompressParams(), nullptr, 0,
+ &compressed[pos], compressed.size() - pos, &output);
+ VerifyOutputImage(config.input, output, config.max_dist);
}
}
TEST(EncodeAPITest, ReuseCinfoChangeParams) {
- TestImage input, output;
+ TestImage input;
+ TestImage output;
CompressParams jparams;
DecompressParams dparams;
uint8_t* buffer = nullptr;
diff --git a/third_party/jpeg-xl/lib/jpegli/encode_finish.cc b/third_party/jpeg-xl/lib/jpegli/encode_finish.cc
index 955676bdee..767a6532c5 100644
--- a/third_party/jpeg-xl/lib/jpegli/encode_finish.cc
+++ b/third_party/jpeg-xl/lib/jpegli/encode_finish.cc
@@ -213,6 +213,8 @@ float FindDistanceForPSNR(j_compress_ptr cinfo) {
d = best_distance;
if (sampling == 1 && PSNR_SEARCH_DBG) {
printf("Final PSNR %.2f at distance %.4f\n", best_psnr, d);
+ } else {
+ (void)best_psnr;
}
}
return d;
diff --git a/third_party/jpeg-xl/lib/jpegli/entropy_coding.cc b/third_party/jpeg-xl/lib/jpegli/entropy_coding.cc
index 7e50bbc3a7..515996a43d 100644
--- a/third_party/jpeg-xl/lib/jpegli/entropy_coding.cc
+++ b/third_party/jpeg-xl/lib/jpegli/entropy_coding.cc
@@ -99,10 +99,16 @@ void TokenizeACProgressiveScan(j_compress_ptr cinfo, int scan_index,
TokenArray* ta = &m->token_arrays[m->cur_token_array];
sti->token_offset = m->total_num_tokens + ta->num_tokens;
sti->restarts = Allocate<size_t>(cinfo, num_restarts, JPOOL_IMAGE);
+ const auto emit_eob_run = [&]() {
+ int nbits = jxl::FloorLog2Nonzero<uint32_t>(eob_run);
+ int symbol = nbits << 4u;
+ *m->next_token++ = Token(context, symbol, eob_run & ((1 << nbits) - 1));
+ eob_run = 0;
+ };
for (JDIMENSION by = 0; by < comp->height_in_blocks; ++by) {
JBLOCKARRAY ba = (*cinfo->mem->access_virt_barray)(
reinterpret_cast<j_common_ptr>(cinfo), m->coeff_buffers[comp_idx], by,
- 1, false);
+ 1, FALSE);
// Each coefficient can appear in at most one token, but we have to reserve
// one extra EOBrun token that was rolled over from the previous block-row
// and has to be flushed at the end.
@@ -121,13 +127,7 @@ void TokenizeACProgressiveScan(j_compress_ptr cinfo, int scan_index,
}
for (JDIMENSION bx = 0; bx < comp->width_in_blocks; ++bx) {
if (restart_interval > 0 && restarts_to_go == 0) {
- if (eob_run > 0) {
- int nbits = jxl::FloorLog2Nonzero<uint32_t>(eob_run);
- int symbol = nbits << 4u;
- *m->next_token++ =
- Token(context, symbol, eob_run & ((1 << nbits) - 1));
- eob_run = 0;
- }
+ if (eob_run > 0) emit_eob_run();
ta->num_tokens = m->next_token - ta->tokens;
sti->restarts[restart_idx++] = m->total_num_tokens + ta->num_tokens;
restarts_to_go = restart_interval;
@@ -139,7 +139,8 @@ void TokenizeACProgressiveScan(j_compress_ptr cinfo, int scan_index,
int num_nzeros = 0;
int num_future_nzeros = 0;
for (int k = Ss; k <= Se; ++k) {
- if ((temp = block[k]) == 0) {
+ temp = block[k];
+ if (temp == 0) {
r++;
continue;
}
@@ -156,13 +157,7 @@ void TokenizeACProgressiveScan(j_compress_ptr cinfo, int scan_index,
num_future_nzeros++;
continue;
}
- if (eob_run > 0) {
- int nbits = jxl::FloorLog2Nonzero<uint32_t>(eob_run);
- int symbol = nbits << 4u;
- *m->next_token++ =
- Token(context, symbol, eob_run & ((1 << nbits) - 1));
- eob_run = 0;
- }
+ if (eob_run > 0) emit_eob_run();
while (r > 15) {
*m->next_token++ = Token(context, 0xf0, 0);
r -= 16;
@@ -175,13 +170,7 @@ void TokenizeACProgressiveScan(j_compress_ptr cinfo, int scan_index,
}
if (r > 0) {
++eob_run;
- if (eob_run == 0x7FFF) {
- int nbits = jxl::FloorLog2Nonzero<uint32_t>(eob_run);
- int symbol = nbits << 4u;
- *m->next_token++ =
- Token(context, symbol, eob_run & ((1 << nbits) - 1));
- eob_run = 0;
- }
+ if (eob_run == 0x7FFF) emit_eob_run();
}
sti->num_nonzeros += num_nzeros;
sti->num_future_nonzeros += num_future_nzeros;
@@ -190,11 +179,8 @@ void TokenizeACProgressiveScan(j_compress_ptr cinfo, int scan_index,
ta->num_tokens = m->next_token - ta->tokens;
}
if (eob_run > 0) {
- int nbits = jxl::FloorLog2Nonzero<uint32_t>(eob_run);
- int symbol = nbits << 4u;
- *m->next_token++ = Token(context, symbol, eob_run & ((1 << nbits) - 1));
+ emit_eob_run();
++ta->num_tokens;
- eob_run = 0;
}
sti->num_tokens = m->total_num_tokens + ta->num_tokens - sti->token_offset;
sti->restarts[restart_idx++] = m->total_num_tokens + ta->num_tokens;
@@ -229,7 +215,7 @@ void TokenizeACRefinementScan(j_compress_ptr cinfo, int scan_index,
for (JDIMENSION by = 0; by < comp->height_in_blocks; ++by) {
JBLOCKARRAY ba = (*cinfo->mem->access_virt_barray)(
reinterpret_cast<j_common_ptr>(cinfo), m->coeff_buffers[comp_idx], by,
- 1, false);
+ 1, FALSE);
for (JDIMENSION bx = 0; bx < comp->width_in_blocks; ++bx) {
if (restart_interval > 0 && restarts_to_go == 0) {
sti->restarts[restart_idx++] = next_token - sti->tokens;
@@ -337,7 +323,7 @@ void TokenizeScan(j_compress_ptr cinfo, size_t scan_index, int ac_ctx_offset,
// "Non-interleaved" means color data comes in separate scans, in other words
// each scan can contain only one color component.
const bool is_interleaved = (scan_info->comps_in_scan > 1);
- const bool is_progressive = cinfo->progressive_mode;
+ const bool is_progressive = FROM_JXL_BOOL(cinfo->progressive_mode);
const int Ah = scan_info->Ah;
const int Al = scan_info->Al;
HWY_ALIGN constexpr coeff_t kSinkBlock[DCTSIZE2] = {0};
@@ -373,7 +359,7 @@ void TokenizeScan(j_compress_ptr cinfo, size_t scan_index, int ac_ctx_offset,
int max_block_rows = std::min(n_blocks_y, block_rows_left);
ba[i] = (*cinfo->mem->access_virt_barray)(
reinterpret_cast<j_common_ptr>(cinfo), m->coeff_buffers[comp_idx],
- by0, max_block_rows, false);
+ by0, max_block_rows, FALSE);
}
if (!cinfo->progressive_mode) {
int max_tokens_per_mcu_row = MaxNumTokensPerMCURow(cinfo);
@@ -557,7 +543,7 @@ float HistogramCost(const Histogram& histo) {
}
counts[kJpegHuffmanAlphabetSize] = 1;
CreateHuffmanTree(counts.data(), counts.size(), kJpegHuffmanMaxBitLength,
- &depths[0]);
+ depths.data());
size_t header_bits = (1 + kJpegHuffmanMaxBitLength) * 8;
size_t data_bits = 0;
for (size_t i = 0; i < kJpegHuffmanAlphabetSize; ++i) {
@@ -576,8 +562,8 @@ void AddHistograms(const Histogram& a, const Histogram& b, Histogram* c) {
}
bool IsEmptyHistogram(const Histogram& histo) {
- for (size_t i = 0; i < kJpegHuffmanAlphabetSize; ++i) {
- if (histo.count[i]) return false;
+ for (int count : histo.count) {
+ if (count) return false;
}
return true;
}
@@ -668,7 +654,7 @@ void BuildJpegHuffmanTable(const Histogram& histo, JHUFF_TBL* table) {
}
counts[kJpegHuffmanAlphabetSize] = 1;
CreateHuffmanTree(counts.data(), counts.size(), kJpegHuffmanMaxBitLength,
- &depths[0]);
+ depths.data());
memset(table, 0, sizeof(JHUFF_TBL));
for (size_t i = 0; i < kJpegHuffmanAlphabetSize; ++i) {
if (depths[i] > 0) {
@@ -726,7 +712,7 @@ void OptimizeHuffmanCodes(j_compress_ptr cinfo) {
jpeg_comp_master* m = cinfo->master;
// Build DC and AC histograms.
std::vector<Histogram> histograms(m->num_contexts);
- BuildHistograms(cinfo, &histograms[0]);
+ BuildHistograms(cinfo, histograms.data());
// Cluster DC histograms.
JpegClusteredHistograms dc_clusters;
@@ -760,7 +746,7 @@ void OptimizeHuffmanCodes(j_compress_ptr cinfo) {
m->context_map = Allocate<uint8_t>(cinfo, m->num_contexts, JPOOL_IMAGE);
memset(m->context_map, 0, m->num_contexts);
for (size_t i = 0; i < m->num_contexts; ++i) {
- if (i < (size_t)cinfo->num_components) {
+ if (i < static_cast<size_t>(cinfo->num_components)) {
m->context_map[i] = dc_clusters.histogram_indexes[i];
} else if (i >= 4) {
m->context_map[i] = num_dc_huff + ac_clusters.histogram_indexes[i - 4];
diff --git a/third_party/jpeg-xl/lib/jpegli/error.h b/third_party/jpeg-xl/lib/jpegli/error.h
index 4451abd416..5f266baee1 100644
--- a/third_party/jpeg-xl/lib/jpegli/error.h
+++ b/third_party/jpeg-xl/lib/jpegli/error.h
@@ -10,6 +10,7 @@
#include <stdint.h>
#include "lib/jpegli/common.h"
+#include "lib/jxl/base/status.h"
namespace jpegli {
@@ -17,10 +18,12 @@ bool FormatString(char* buffer, const char* format, ...);
} // namespace jpegli
+// `error_exit` should be no-return; but let's add some guarantees on our side.
#define JPEGLI_ERROR(format, ...) \
jpegli::FormatString(cinfo->err->msg_parm.s, ("%s:%d: " format), __FILE__, \
__LINE__, ##__VA_ARGS__), \
- (*cinfo->err->error_exit)(reinterpret_cast<j_common_ptr>(cinfo))
+ (*cinfo->err->error_exit)(reinterpret_cast<j_common_ptr>(cinfo)), \
+ (void)jxl::Abort()
#define JPEGLI_WARN(format, ...) \
jpegli::FormatString(cinfo->err->msg_parm.s, ("%s:%d: " format), __FILE__, \
diff --git a/third_party/jpeg-xl/lib/jpegli/error_handling_test.cc b/third_party/jpeg-xl/lib/jpegli/error_handling_test.cc
index 0d481c572a..3eaf6a313b 100644
--- a/third_party/jpeg-xl/lib/jpegli/error_handling_test.cc
+++ b/third_party/jpeg-xl/lib/jpegli/error_handling_test.cc
@@ -241,9 +241,10 @@ TEST(EncoderErrorHandlingTest, InvalidQuantValue) {
cinfo.image_height = 1;
cinfo.input_components = 1;
jpegli_set_defaults(&cinfo);
- cinfo.quant_tbl_ptrs[0] = jpegli_alloc_quant_table((j_common_ptr)&cinfo);
- for (size_t k = 0; k < DCTSIZE2; ++k) {
- cinfo.quant_tbl_ptrs[0]->quantval[k] = 0;
+ cinfo.quant_tbl_ptrs[0] =
+ jpegli_alloc_quant_table(reinterpret_cast<j_common_ptr>(&cinfo));
+ for (UINT16& q : cinfo.quant_tbl_ptrs[0]->quantval) {
+ q = 0;
}
jpegli_start_compress(&cinfo, TRUE);
JSAMPLE image[1] = {0};
@@ -992,7 +993,7 @@ TEST(EncoderErrorHandlingTest, AddOnTableNoStringParam) {
jpegli_destroy_compress(&cinfo);
}
-static const uint8_t kCompressed0[] = {
+const uint8_t kCompressed0[] = {
// SOI
0xff, 0xd8, //
// DQT
@@ -1036,12 +1037,12 @@ static const uint8_t kCompressed0[] = {
// EOI
0xff, 0xd9, //
};
-static const size_t kLen0 = sizeof(kCompressed0);
+const size_t kLen0 = sizeof(kCompressed0);
-static const size_t kDQTOffset = 2;
-static const size_t kSOFOffset = 71;
-static const size_t kDHTOffset = 84;
-static const size_t kSOSOffset = 296;
+const size_t kDQTOffset = 2;
+const size_t kSOFOffset = 71;
+const size_t kDHTOffset = 84;
+const size_t kSOSOffset = 296;
TEST(DecoderErrorHandlingTest, MinimalSuccess) {
JXL_CHECK(kCompressed0[kDQTOffset] == 0xff);
@@ -1130,7 +1131,7 @@ TEST(DecoderErrorHandlingTest, NoReadScanlines) {
jpegli_destroy_decompress(&cinfo);
}
-static const size_t kMaxImageWidth = 0xffff;
+const size_t kMaxImageWidth = 0xffff;
JSAMPLE kOutputBuffer[MAX_COMPONENTS * kMaxImageWidth];
bool ParseCompressed(const std::vector<uint8_t>& compressed) {
diff --git a/third_party/jpeg-xl/lib/jpegli/huffman.cc b/third_party/jpeg-xl/lib/jpegli/huffman.cc
index 1cf88a5536..5391030213 100644
--- a/third_party/jpeg-xl/lib/jpegli/huffman.cc
+++ b/third_party/jpeg-xl/lib/jpegli/huffman.cc
@@ -183,7 +183,8 @@ void CreateHuffmanTree(const uint32_t* data, const size_t length,
size_t i = 0; // Points to the next leaf node.
size_t j = n + 1; // Points to the next non-leaf node.
for (size_t k = n - 1; k != 0; --k) {
- size_t left, right;
+ size_t left;
+ size_t right;
if (tree[i].total_count <= tree[j].total_count) {
left = i;
++i;
@@ -210,7 +211,7 @@ void CreateHuffmanTree(const uint32_t* data, const size_t length,
tree.push_back(sentinel);
}
JXL_DASSERT(tree.size() == 2 * n + 1);
- SetDepth(tree[2 * n - 1], &tree[0], depth, 0);
+ SetDepth(tree[2 * n - 1], tree.data(), depth, 0);
// We need to pack the Huffman tree in tree_limit bits.
// If this was not successful, add fake entities to the lowest values
diff --git a/third_party/jpeg-xl/lib/jpegli/idct.cc b/third_party/jpeg-xl/lib/jpegli/idct.cc
index 4d10563583..9859e8ef85 100644
--- a/third_party/jpeg-xl/lib/jpegli/idct.cc
+++ b/third_party/jpeg-xl/lib/jpegli/idct.cc
@@ -197,7 +197,7 @@ void InverseTransformBlock8x8(const int16_t* JXL_RESTRICT qblock,
// Computes the N-point IDCT of in[], and stores the result in out[]. The in[]
// array is at most 8 values long, values in[8:N-1] are assumed to be 0.
-void Compute1dIDCT(float* in, float* out, size_t N) {
+void Compute1dIDCT(const float* in, float* out, size_t N) {
switch (N) {
case 3: {
static constexpr float kC3[3] = {
@@ -608,6 +608,9 @@ void Compute1dIDCT(float* in, float* out, size_t N) {
out[8] = even7 - odd7;
break;
}
+ default:
+ JXL_ABORT("Compute1dIDCT does not support N=%d", static_cast<int>(N));
+ break;
}
}
diff --git a/third_party/jpeg-xl/lib/jpegli/input.cc b/third_party/jpeg-xl/lib/jpegli/input.cc
index 765bf98946..16299477f7 100644
--- a/third_party/jpeg-xl/lib/jpegli/input.cc
+++ b/third_party/jpeg-xl/lib/jpegli/input.cc
@@ -89,7 +89,7 @@ void ReadUint8RowInterleaved2(const uint8_t* row_in, size_t len,
const size_t simd_len = len & (~(N - 1));
float* JXL_RESTRICT const row0 = row_out[0];
float* JXL_RESTRICT const row1 = row_out[1];
- Vec<DU8> out0, out1;
+ Vec<DU8> out0, out1; // NOLINT
for (size_t x = 0; x < simd_len; x += N) {
LoadInterleaved2(du8, row_in + 2 * x, out0, out1);
Store(ConvertTo(d, PromoteTo(du, out0)), d, row0 + x);
@@ -105,7 +105,7 @@ void ReadUint8RowInterleaved3(const uint8_t* row_in, size_t len,
float* JXL_RESTRICT const row0 = row_out[0];
float* JXL_RESTRICT const row1 = row_out[1];
float* JXL_RESTRICT const row2 = row_out[2];
- Vec<DU8> out0, out1, out2;
+ Vec<DU8> out0, out1, out2; // NOLINT
for (size_t x = 0; x < simd_len; x += N) {
LoadInterleaved3(du8, row_in + 3 * x, out0, out1, out2);
Store(ConvertTo(d, PromoteTo(du, out0)), d, row0 + x);
@@ -123,7 +123,7 @@ void ReadUint8RowInterleaved4(const uint8_t* row_in, size_t len,
float* JXL_RESTRICT const row1 = row_out[1];
float* JXL_RESTRICT const row2 = row_out[2];
float* JXL_RESTRICT const row3 = row_out[3];
- Vec<DU8> out0, out1, out2, out3;
+ Vec<DU8> out0, out1, out2, out3; // NOLINT
for (size_t x = 0; x < simd_len; x += N) {
LoadInterleaved4(du8, row_in + 4 * x, out0, out1, out2, out3);
Store(ConvertTo(d, PromoteTo(du, out0)), d, row0 + x);
@@ -158,7 +158,7 @@ void ReadUint16RowInterleaved2(const uint8_t* row_in, size_t len,
reinterpret_cast<const uint16_t*>(row_in);
float* JXL_RESTRICT const row0 = row_out[0];
float* JXL_RESTRICT const row1 = row_out[1];
- Vec<DU16> out0, out1;
+ Vec<DU16> out0, out1; // NOLINT
for (size_t x = 0; x < simd_len; x += N) {
LoadInterleaved2(du16, row + 2 * x, out0, out1);
Store(Mul(mul, ConvertTo(d, PromoteTo(du, out0))), d, row0 + x);
@@ -177,7 +177,7 @@ void ReadUint16RowInterleaved3(const uint8_t* row_in, size_t len,
float* JXL_RESTRICT const row0 = row_out[0];
float* JXL_RESTRICT const row1 = row_out[1];
float* JXL_RESTRICT const row2 = row_out[2];
- Vec<DU16> out0, out1, out2;
+ Vec<DU16> out0, out1, out2; // NOLINT
for (size_t x = 0; x < simd_len; x += N) {
LoadInterleaved3(du16, row + 3 * x, out0, out1, out2);
Store(Mul(mul, ConvertTo(d, PromoteTo(du, out0))), d, row0 + x);
@@ -198,7 +198,7 @@ void ReadUint16RowInterleaved4(const uint8_t* row_in, size_t len,
float* JXL_RESTRICT const row1 = row_out[1];
float* JXL_RESTRICT const row2 = row_out[2];
float* JXL_RESTRICT const row3 = row_out[3];
- Vec<DU16> out0, out1, out2, out3;
+ Vec<DU16> out0, out1, out2, out3; // NOLINT
for (size_t x = 0; x < simd_len; x += N) {
LoadInterleaved4(du16, row + 4 * x, out0, out1, out2, out3);
Store(Mul(mul, ConvertTo(d, PromoteTo(du, out0))), d, row0 + x);
@@ -250,7 +250,7 @@ void ReadFloatRowInterleaved2(const uint8_t* row_in, size_t len,
const float* JXL_RESTRICT const row = reinterpret_cast<const float*>(row_in);
float* JXL_RESTRICT const row0 = row_out[0];
float* JXL_RESTRICT const row1 = row_out[1];
- Vec<D> out0, out1;
+ Vec<D> out0, out1; // NOLINT
for (size_t x = 0; x < simd_len; x += N) {
LoadInterleaved2(d, row + 2 * x, out0, out1);
Store(Mul(mul, out0), d, row0 + x);
@@ -268,7 +268,7 @@ void ReadFloatRowInterleaved3(const uint8_t* row_in, size_t len,
float* JXL_RESTRICT const row0 = row_out[0];
float* JXL_RESTRICT const row1 = row_out[1];
float* JXL_RESTRICT const row2 = row_out[2];
- Vec<D> out0, out1, out2;
+ Vec<D> out0, out1, out2; // NOLINT
for (size_t x = 0; x < simd_len; x += N) {
LoadInterleaved3(d, row + 3 * x, out0, out1, out2);
Store(Mul(mul, out0), d, row0 + x);
@@ -288,7 +288,7 @@ void ReadFloatRowInterleaved4(const uint8_t* row_in, size_t len,
float* JXL_RESTRICT const row1 = row_out[1];
float* JXL_RESTRICT const row2 = row_out[2];
float* JXL_RESTRICT const row3 = row_out[3];
- Vec<D> out0, out1, out2, out3;
+ Vec<D> out0, out1, out2, out3; // NOLINT
for (size_t x = 0; x < simd_len; x += N) {
LoadInterleaved4(d, row + 4 * x, out0, out1, out2, out3);
Store(Mul(mul, out0), d, row0 + x);
diff --git a/third_party/jpeg-xl/lib/jpegli/input_suspension_test.cc b/third_party/jpeg-xl/lib/jpegli/input_suspension_test.cc
index 09bafd9188..eb8b7ebc26 100644
--- a/third_party/jpeg-xl/lib/jpegli/input_suspension_test.cc
+++ b/third_party/jpeg-xl/lib/jpegli/input_suspension_test.cc
@@ -17,7 +17,7 @@
namespace jpegli {
namespace {
-static constexpr uint8_t kFakeEoiMarker[2] = {0xff, 0xd9};
+constexpr uint8_t kFakeEoiMarker[2] = {0xff, 0xd9};
struct SourceManager {
SourceManager(const uint8_t* data, size_t len, size_t max_chunk_size,
@@ -50,14 +50,14 @@ struct SourceManager {
}
if (pub_.bytes_in_buffer > 0) {
EXPECT_LE(pub_.bytes_in_buffer, buffer_.size());
- memmove(&buffer_[0], pub_.next_input_byte, pub_.bytes_in_buffer);
+ memmove(buffer_.data(), pub_.next_input_byte, pub_.bytes_in_buffer);
}
size_t chunk_size =
pos_ < len_ ? std::min(len_ - pos_, max_chunk_size_) : 2;
buffer_.resize(pub_.bytes_in_buffer + chunk_size);
memcpy(&buffer_[pub_.bytes_in_buffer],
pos_ < len_ ? data_ + pos_ : kFakeEoiMarker, chunk_size);
- pub_.next_input_byte = &buffer_[0];
+ pub_.next_input_byte = buffer_.data();
pub_.bytes_in_buffer += chunk_size;
pos_ += chunk_size;
return true;
@@ -73,7 +73,7 @@ struct SourceManager {
bool is_partial_file_;
static void init_source(j_decompress_ptr cinfo) {
- auto src = reinterpret_cast<SourceManager*>(cinfo->src);
+ auto* src = reinterpret_cast<SourceManager*>(cinfo->src);
src->pub_.next_input_byte = nullptr;
src->pub_.bytes_in_buffer = 0;
}
@@ -81,7 +81,7 @@ struct SourceManager {
static boolean fill_input_buffer(j_decompress_ptr cinfo) { return FALSE; }
static void skip_input_data(j_decompress_ptr cinfo, long num_bytes) {
- auto src = reinterpret_cast<SourceManager*>(cinfo->src);
+ auto* src = reinterpret_cast<SourceManager*>(cinfo->src);
if (num_bytes <= 0) {
return;
}
@@ -156,10 +156,10 @@ void ReadOutputImage(const DecompressParams& dparams, j_decompress_ptr cinfo,
rowdata[c][i] =
y0 + i < ysize ? &output->raw_data[c][(y0 + i) * xsize] : nullptr;
}
- data[c] = &rowdata[c][0];
+ data[c] = rowdata[c].data();
}
while ((num_output_lines =
- jpegli_read_raw_data(cinfo, &data[0], max_lines)) == 0) {
+ jpegli_read_raw_data(cinfo, data.data(), max_lines)) == 0) {
JXL_CHECK(src && src->LoadNextChunk());
}
} else {
@@ -173,7 +173,7 @@ void ReadOutputImage(const DecompressParams& dparams, j_decompress_ptr cinfo,
size_t yidx = cinfo->output_scanline + i;
scanlines[i] = &output->pixels[yidx * stride];
}
- while ((num_output_lines = jpegli_read_scanlines(cinfo, &scanlines[0],
+ while ((num_output_lines = jpegli_read_scanlines(cinfo, scanlines.data(),
max_lines)) == 0) {
JXL_CHECK(src && src->LoadNextChunk());
}
@@ -197,7 +197,7 @@ struct TestConfig {
std::vector<uint8_t> GetTestJpegData(TestConfig& config) {
if (!config.fn.empty()) {
- return ReadTestData(config.fn.c_str());
+ return ReadTestData(config.fn);
}
GeneratePixels(&config.input);
std::vector<uint8_t> compressed;
@@ -249,7 +249,7 @@ TEST_P(InputSuspensionTestParam, InputOutputLockStepNonBuffered) {
EXPECT_EQ(0, memcmp(markers_seen, kMarkerSequence, num_markers_seen));
}
VerifyHeader(config.jparams, &cinfo);
- cinfo.raw_data_out = dparams.output_mode == RAW_DATA;
+ cinfo.raw_data_out = TO_JXL_BOOL(dparams.output_mode == RAW_DATA);
if (dparams.output_mode == COEFFICIENTS) {
jvirt_barray_ptr* coef_arrays;
@@ -303,7 +303,7 @@ TEST_P(InputSuspensionTestParam, InputOutputLockStepBuffered) {
jpegli_set_output_format(&cinfo, dparams.data_type, dparams.endianness);
cinfo.buffered_image = TRUE;
- cinfo.raw_data_out = dparams.output_mode == RAW_DATA;
+ cinfo.raw_data_out = TO_JXL_BOOL(dparams.output_mode == RAW_DATA);
EXPECT_TRUE(jpegli_start_decompress(&cinfo));
EXPECT_FALSE(jpegli_input_complete(&cinfo));
@@ -380,8 +380,8 @@ TEST_P(InputSuspensionTestParam, PreConsumeInputBuffered) {
}
EXPECT_EQ(JPEG_REACHED_SOS, jpegli_consume_input(&cinfo));
cinfo.buffered_image = TRUE;
- cinfo.raw_data_out = dparams.output_mode == RAW_DATA;
- cinfo.do_block_smoothing = dparams.do_block_smoothing;
+ cinfo.raw_data_out = TO_JXL_BOOL(dparams.output_mode == RAW_DATA);
+ cinfo.do_block_smoothing = TO_JXL_BOOL(dparams.do_block_smoothing);
EXPECT_TRUE(jpegli_start_decompress(&cinfo));
EXPECT_FALSE(jpegli_input_complete(&cinfo));
@@ -446,8 +446,8 @@ TEST_P(InputSuspensionTestParam, PreConsumeInputNonBuffered) {
}
}
EXPECT_EQ(JPEG_REACHED_SOS, jpegli_consume_input(&cinfo));
- cinfo.raw_data_out = dparams.output_mode == RAW_DATA;
- cinfo.do_block_smoothing = dparams.do_block_smoothing;
+ cinfo.raw_data_out = TO_JXL_BOOL(dparams.output_mode == RAW_DATA);
+ cinfo.do_block_smoothing = TO_JXL_BOOL(dparams.do_block_smoothing);
if (dparams.output_mode == COEFFICIENTS) {
jpegli_read_coefficients(&cinfo);
diff --git a/third_party/jpeg-xl/lib/jpegli/libjpeg_test_util.cc b/third_party/jpeg-xl/lib/jpegli/libjpeg_test_util.cc
index de2303756e..020adf5e9e 100644
--- a/third_party/jpeg-xl/lib/jpegli/libjpeg_test_util.cc
+++ b/third_party/jpeg-xl/lib/jpegli/libjpeg_test_util.cc
@@ -37,12 +37,13 @@ void ReadOutputPass(j_decompress_ptr cinfo, const DecompressParams& dparams,
output->ysize = ysize_cropped;
output->components = cinfo->out_color_components;
if (cinfo->quantize_colors) {
- jxl::msan::UnpoisonMemory(cinfo->colormap, cinfo->out_color_components *
- sizeof(cinfo->colormap[0]));
+ JSAMPLE** colormap = cinfo->colormap;
+ jxl::msan::UnpoisonMemory(reinterpret_cast<void*>(colormap),
+ cinfo->out_color_components * sizeof(JSAMPLE*));
for (int c = 0; c < cinfo->out_color_components; ++c) {
jxl::msan::UnpoisonMemory(
- cinfo->colormap[c],
- cinfo->actual_number_of_colors * sizeof(cinfo->colormap[c][0]));
+ reinterpret_cast<void*>(colormap[c]),
+ cinfo->actual_number_of_colors * sizeof(JSAMPLE));
}
}
if (!cinfo->raw_data_out) {
@@ -89,10 +90,10 @@ void ReadOutputPass(j_decompress_ptr cinfo, const DecompressParams& dparams,
rowdata[c][i] =
y0 + i < ysize ? &output->raw_data[c][(y0 + i) * xsize] : nullptr;
}
- data[c] = &rowdata[c][0];
+ data[c] = rowdata[c].data();
}
JXL_CHECK(iMCU_height ==
- jpeg_read_raw_data(cinfo, &data[0], iMCU_height));
+ jpeg_read_raw_data(cinfo, data.data(), iMCU_height));
}
}
JXL_CHECK(cinfo->total_iMCU_rows ==
@@ -113,7 +114,7 @@ void DecodeWithLibjpeg(const CompressParams& jparams,
jpeg_read_header(cinfo, /*require_image=*/TRUE));
if (!jparams.icc.empty()) {
uint8_t* icc_data = nullptr;
- unsigned int icc_len;
+ unsigned int icc_len = 0; // "unpoison" via initialization
JXL_CHECK(jpeg_read_icc_profile(cinfo, &icc_data, &icc_len));
JXL_CHECK(icc_data);
jxl::msan::UnpoisonMemory(icc_data, icc_len);
diff --git a/third_party/jpeg-xl/lib/jpegli/libjpeg_wrapper.cc b/third_party/jpeg-xl/lib/jpegli/libjpeg_wrapper.cc
index b38d16f255..471b7c7192 100644
--- a/third_party/jpeg-xl/lib/jpegli/libjpeg_wrapper.cc
+++ b/third_party/jpeg-xl/lib/jpegli/libjpeg_wrapper.cc
@@ -122,11 +122,11 @@ boolean jpeg_read_icc_profile(j_decompress_ptr cinfo, JOCTET **icc_data_ptr,
}
void jpeg_abort_decompress(j_decompress_ptr cinfo) {
- return jpegli_abort_decompress(cinfo);
+ jpegli_abort_decompress(cinfo);
}
void jpeg_destroy_decompress(j_decompress_ptr cinfo) {
- return jpegli_destroy_decompress(cinfo);
+ jpegli_destroy_decompress(cinfo);
}
void jpeg_CreateCompress(j_compress_ptr cinfo, int version, size_t structsize) {
diff --git a/third_party/jpeg-xl/lib/jpegli/memory_manager.h b/third_party/jpeg-xl/lib/jpegli/memory_manager.h
index 3e2bdabe06..c650caad49 100644
--- a/third_party/jpeg-xl/lib/jpegli/memory_manager.h
+++ b/third_party/jpeg-xl/lib/jpegli/memory_manager.h
@@ -19,7 +19,8 @@ void InitMemoryManager(j_common_ptr cinfo);
template <typename T>
T* Allocate(j_common_ptr cinfo, size_t len, int pool_id = JPOOL_PERMANENT) {
- void* p = (*cinfo->mem->alloc_small)(cinfo, pool_id, len * sizeof(T));
+ const size_t size = len * sizeof(T); // NOLINT
+ void* p = (*cinfo->mem->alloc_small)(cinfo, pool_id, size);
return reinterpret_cast<T*>(p);
}
diff --git a/third_party/jpeg-xl/lib/jpegli/output_suspension_test.cc b/third_party/jpeg-xl/lib/jpegli/output_suspension_test.cc
index 73db791727..3cb2fd3ee4 100644
--- a/third_party/jpeg-xl/lib/jpegli/output_suspension_test.cc
+++ b/third_party/jpeg-xl/lib/jpegli/output_suspension_test.cc
@@ -10,8 +10,8 @@
namespace jpegli {
namespace {
-static constexpr size_t kInitialBufferSize = 1024;
-static constexpr size_t kFinalBufferSize = 18;
+constexpr size_t kInitialBufferSize = 1024;
+constexpr size_t kFinalBufferSize = 18;
struct DestinationManager {
jpeg_destination_mgr pub;
@@ -37,7 +37,7 @@ struct DestinationManager {
}
static void init_destination(j_compress_ptr cinfo) {
- auto us = reinterpret_cast<DestinationManager*>(cinfo->dest);
+ auto* us = reinterpret_cast<DestinationManager*>(cinfo->dest);
us->buffer.resize(kInitialBufferSize);
us->Rewind();
}
@@ -84,7 +84,7 @@ TEST_P(OutputSuspensionTestParam, PixelData) {
while (cinfo.next_scanline < cinfo.image_height) {
size_t lines_left = cinfo.image_height - cinfo.next_scanline;
size_t num_lines = std::min(config.lines_batch_size, lines_left);
- memcpy(&row_bytes[0], &input.pixels[cinfo.next_scanline * stride],
+ memcpy(row_bytes.data(), &input.pixels[cinfo.next_scanline * stride],
num_lines * stride);
std::vector<JSAMPROW> rows(num_lines);
for (size_t i = 0; i < num_lines; ++i) {
@@ -142,7 +142,7 @@ TEST_P(OutputSuspensionTestParam, RawData) {
std::vector<JSAMPARRAY> data(cinfo.num_components);
for (int c = 0; c < cinfo.num_components; ++c) {
rowdata[c].resize(config.jparams.v_samp(c) * DCTSIZE);
- data[c] = &rowdata[c][0];
+ data[c] = rowdata[c].data();
}
while (cinfo.next_scanline < cinfo.image_height) {
for (int c = 0; c < cinfo.num_components; ++c) {
@@ -155,7 +155,7 @@ TEST_P(OutputSuspensionTestParam, RawData) {
(y0 + i < cheight ? &raw_data[c][(y0 + i) * cwidth] : nullptr);
}
}
- while (jpegli_write_raw_data(&cinfo, &data[0], max_lines) == 0) {
+ while (jpegli_write_raw_data(&cinfo, data.data(), max_lines) == 0) {
dest.EmptyTo(&compressed, config.buffer_size);
}
}
diff --git a/third_party/jpeg-xl/lib/jpegli/quant.cc b/third_party/jpeg-xl/lib/jpegli/quant.cc
index 36f1df4cdd..14db6701b2 100644
--- a/third_party/jpeg-xl/lib/jpegli/quant.cc
+++ b/third_party/jpeg-xl/lib/jpegli/quant.cc
@@ -26,7 +26,7 @@ namespace {
constexpr float kGlobalScaleXYB = 1.43951668f;
constexpr float kGlobalScaleYCbCr = 1.73966010f;
-static constexpr float kBaseQuantMatrixXYB[] = {
+constexpr float kBaseQuantMatrixXYB[] = {
// c = 0
7.5629935265f,
19.8247814178f,
@@ -224,7 +224,7 @@ static constexpr float kBaseQuantMatrixXYB[] = {
63.6065597534f,
};
-static const float kBaseQuantMatrixYCbCr[] = {
+const float kBaseQuantMatrixYCbCr[] = {
// c = 0
1.2397409345866273f, //
1.7227115097630963f, //
@@ -422,8 +422,8 @@ static const float kBaseQuantMatrixYCbCr[] = {
114.89202448569779f, //
};
-static const float k420GlobalScale = 1.22;
-static const float k420Rescale[64] = {
+const float k420GlobalScale = 1.22;
+const float k420Rescale[64] = {
0.4093, 0.3209, 0.3477, 0.3333, 0.3144, 0.2823, 0.3214, 0.3354, //
0.3209, 0.3111, 0.3489, 0.2801, 0.3059, 0.3119, 0.4135, 0.3445, //
0.3477, 0.3489, 0.3586, 0.3257, 0.2727, 0.3754, 0.3369, 0.3484, //
@@ -434,7 +434,7 @@ static const float k420Rescale[64] = {
0.3354, 0.3445, 0.3484, 0.3839, 0.3836, 0.0726, 0.0553, 0.3368, //
};
-static const float kBaseQuantMatrixStd[] = {
+const float kBaseQuantMatrixStd[] = {
// c = 0
16.0f, 11.0f, 10.0f, 16.0f, 24.0f, 40.0f, 51.0f, 61.0f, //
12.0f, 12.0f, 14.0f, 19.0f, 26.0f, 58.0f, 60.0f, 55.0f, //
@@ -455,7 +455,7 @@ static const float kBaseQuantMatrixStd[] = {
99.0f, 99.0f, 99.0f, 99.0f, 99.0f, 99.0f, 99.0f, 99.0f, //
};
-static const float kZeroBiasMulYCbCrLQ[] = {
+const float kZeroBiasMulYCbCrLQ[] = {
// c = 0
0.0000f, 0.0568f, 0.3880f, 0.6190f, 0.6190f, 0.4490f, 0.4490f, 0.6187f, //
0.0568f, 0.5829f, 0.6189f, 0.6190f, 0.6190f, 0.7190f, 0.6190f, 0.6189f, //
@@ -485,7 +485,7 @@ static const float kZeroBiasMulYCbCrLQ[] = {
0.2960f, 0.2113f, 0.2426f, 0.1590f, 0.5403f, 0.3060f, 0.3060f, 0.3060f, //
};
-static const float kZeroBiasMulYCbCrHQ[] = {
+const float kZeroBiasMulYCbCrHQ[] = {
// c = 0
0.0000f, 0.0044f, 0.2521f, 0.6547f, 0.8161f, 0.6130f, 0.8841f, 0.8155f, //
0.0044f, 0.6831f, 0.6553f, 0.6295f, 0.7848f, 0.7843f, 0.8474f, 0.7836f, //
@@ -515,9 +515,9 @@ static const float kZeroBiasMulYCbCrHQ[] = {
0.4836f, 0.4897f, 0.2583f, 0.3565f, 0.5949f, 0.6629f, 0.6644f, 0.6644f, //
};
-static const float kZeroBiasOffsetYCbCrDC[] = {0.0f, 0.0f, 0.0f};
+const float kZeroBiasOffsetYCbCrDC[] = {0.0f, 0.0f, 0.0f};
-static const float kZeroBiasOffsetYCbCrAC[] = {
+const float kZeroBiasOffsetYCbCrAC[] = {
0.59082f,
0.58146f,
0.57988f,
diff --git a/third_party/jpeg-xl/lib/jpegli/render.cc b/third_party/jpeg-xl/lib/jpegli/render.cc
index 24e7e99618..c550f9a575 100644
--- a/third_party/jpeg-xl/lib/jpegli/render.cc
+++ b/third_party/jpeg-xl/lib/jpegli/render.cc
@@ -8,7 +8,6 @@
#include <string.h>
#include <array>
-#include <atomic>
#include <cmath>
#include <cstddef>
#include <cstdint>
@@ -203,12 +202,13 @@ void WriteToOutput(j_decompress_ptr cinfo, float* JXL_RESTRICT rows[],
if (cinfo->quantize_colors && m->quant_pass_ == 1) {
float* error_row[kMaxComponents];
float* next_error_row[kMaxComponents];
- if (cinfo->dither_mode == JDITHER_ORDERED) {
+ J_DITHER_MODE dither_mode = cinfo->dither_mode;
+ if (dither_mode == JDITHER_ORDERED) {
for (size_t c = 0; c < num_channels; ++c) {
DitherRow(cinfo, &rows[c][xoffset], c, cinfo->output_scanline,
cinfo->output_width);
}
- } else if (cinfo->dither_mode == JDITHER_FS) {
+ } else if (dither_mode == JDITHER_FS) {
for (size_t c = 0; c < num_channels; ++c) {
if (cinfo->output_scanline % 2 == 0) {
error_row[c] = m->error_row_[c];
@@ -221,12 +221,12 @@ void WriteToOutput(j_decompress_ptr cinfo, float* JXL_RESTRICT rows[],
}
}
const float mul = 255.0f;
- if (cinfo->dither_mode != JDITHER_FS) {
+ if (dither_mode != JDITHER_FS) {
StoreUnsignedRow(rows, xoffset, len, num_channels, mul, scratch_space);
}
for (size_t i = 0; i < len; ++i) {
uint8_t* pixel = &scratch_space[num_channels * i];
- if (cinfo->dither_mode == JDITHER_FS) {
+ if (dither_mode == JDITHER_FS) {
for (size_t c = 0; c < num_channels; ++c) {
float val = rows[c][i] * mul + LimitError(error_row[c][i]);
pixel[c] = std::round(std::min(255.0f, std::max(0.0f, val)));
@@ -234,7 +234,7 @@ void WriteToOutput(j_decompress_ptr cinfo, float* JXL_RESTRICT rows[],
}
int index = LookupColorIndex(cinfo, pixel);
output[i] = index;
- if (cinfo->dither_mode == JDITHER_FS) {
+ if (dither_mode == JDITHER_FS) {
size_t prev_i = i > 0 ? i - 1 : 0;
size_t next_i = i + 1 < len ? i + 1 : len - 1;
for (size_t c = 0; c < num_channels; ++c) {
@@ -293,19 +293,18 @@ HWY_EXPORT(DecenterRow);
void GatherBlockStats(const int16_t* JXL_RESTRICT coeffs,
const size_t coeffs_size, int32_t* JXL_RESTRICT nonzeros,
int32_t* JXL_RESTRICT sumabs) {
- return HWY_DYNAMIC_DISPATCH(GatherBlockStats)(coeffs, coeffs_size, nonzeros,
- sumabs);
+ HWY_DYNAMIC_DISPATCH(GatherBlockStats)(coeffs, coeffs_size, nonzeros, sumabs);
}
void WriteToOutput(j_decompress_ptr cinfo, float* JXL_RESTRICT rows[],
size_t xoffset, size_t len, size_t num_channels,
uint8_t* JXL_RESTRICT output) {
- return HWY_DYNAMIC_DISPATCH(WriteToOutput)(cinfo, rows, xoffset, len,
- num_channels, output);
+ HWY_DYNAMIC_DISPATCH(WriteToOutput)
+ (cinfo, rows, xoffset, len, num_channels, output);
}
void DecenterRow(float* row, size_t xsize) {
- return HWY_DYNAMIC_DISPATCH(DecenterRow)(row, xsize);
+ HWY_DYNAMIC_DISPATCH(DecenterRow)(row, xsize);
}
bool ShouldApplyDequantBiases(j_decompress_ptr cinfo, int ci) {
@@ -360,8 +359,8 @@ bool do_smoothing(j_decompress_ptr cinfo) {
if (!cinfo->progressive_mode || cinfo->coef_bits == nullptr) {
return false;
}
- auto coef_bits_latch = m->coef_bits_latch;
- auto prev_coef_bits_latch = m->prev_coef_bits_latch;
+ auto* coef_bits_latch = m->coef_bits_latch;
+ auto* prev_coef_bits_latch = m->prev_coef_bits_latch;
for (int ci = 0; ci < cinfo->num_components; ci++) {
jpeg_component_info* compptr = &cinfo->comp_info[ci];
@@ -468,6 +467,7 @@ void PredictSmooth(j_decompress_ptr cinfo, JBLOCKARRAY blocks, int component,
return swap_indices ? dc_values[j][i] : dc_values[i][j];
};
Al = coef_bits[coef_index];
+ JXL_ASSERT(coef_index >= 0 && coef_index < 10);
switch (coef_index) {
case 0:
// set the DC
@@ -520,6 +520,7 @@ void PredictSmooth(j_decompress_ptr cinfo, JBLOCKARRAY blocks, int component,
break;
case 7:
case 8:
+ default:
// set Q12 and Q21
num = (dc(1, 1) - 3 * dc(1, 2) + dc(1, 3) - dc(3, 1) + 3 * dc(3, 2) -
dc(3, 3));
@@ -551,7 +552,7 @@ void PredictSmooth(j_decompress_ptr cinfo, JBLOCKARRAY blocks, int component,
void PrepareForOutput(j_decompress_ptr cinfo) {
jpeg_decomp_master* m = cinfo->master;
bool smoothing = do_smoothing(cinfo);
- m->apply_smoothing = smoothing && cinfo->do_block_smoothing;
+ m->apply_smoothing = smoothing && FROM_JXL_BOOL(cinfo->do_block_smoothing);
size_t coeffs_per_block = cinfo->num_components * DCTSIZE2;
memset(m->nonzeros_, 0, coeffs_per_block * sizeof(m->nonzeros_[0]));
memset(m->sumabs_, 0, coeffs_per_block * sizeof(m->sumabs_[0]));
@@ -584,7 +585,7 @@ void DecodeCurrentiMCURow(j_decompress_ptr cinfo) {
int offset = m->streaming_mode_ ? 0 : by0;
ba[c] = (*cinfo->mem->access_virt_barray)(
reinterpret_cast<j_common_ptr>(cinfo), m->coef_arrays[c], offset,
- max_block_rows, false);
+ max_block_rows, FALSE);
}
for (int c = 0; c < cinfo->num_components; ++c) {
size_t k0 = c * DCTSIZE2;
diff --git a/third_party/jpeg-xl/lib/jpegli/source_manager.cc b/third_party/jpeg-xl/lib/jpegli/source_manager.cc
index 0b8e0a5c8c..58adf803b1 100644
--- a/third_party/jpeg-xl/lib/jpegli/source_manager.cc
+++ b/third_party/jpeg-xl/lib/jpegli/source_manager.cc
@@ -39,7 +39,7 @@ struct StdioSourceManager {
uint8_t* buffer;
static boolean fill_input_buffer(j_decompress_ptr cinfo) {
- auto src = reinterpret_cast<StdioSourceManager*>(cinfo->src);
+ auto* src = reinterpret_cast<StdioSourceManager*>(cinfo->src);
size_t num_bytes_read = fread(src->buffer, 1, kStdioBufferSize, src->f);
if (num_bytes_read == 0) {
return EmitFakeEoiMarker(cinfo);
@@ -77,7 +77,7 @@ void jpegli_stdio_src(j_decompress_ptr cinfo, FILE* infile) {
cinfo->src = reinterpret_cast<jpeg_source_mgr*>(
jpegli::Allocate<jpegli::StdioSourceManager>(cinfo, 1));
}
- auto src = reinterpret_cast<jpegli::StdioSourceManager*>(cinfo->src);
+ auto* src = reinterpret_cast<jpegli::StdioSourceManager*>(cinfo->src);
src->f = infile;
src->buffer = jpegli::Allocate<uint8_t>(cinfo, jpegli::kStdioBufferSize);
src->pub.next_input_byte = src->buffer;
diff --git a/third_party/jpeg-xl/lib/jpegli/source_manager_test.cc b/third_party/jpeg-xl/lib/jpegli/source_manager_test.cc
index 4e137876c9..59d12b001b 100644
--- a/third_party/jpeg-xl/lib/jpegli/source_manager_test.cc
+++ b/third_party/jpeg-xl/lib/jpegli/source_manager_test.cc
@@ -50,7 +50,7 @@ FILE* MemOpen(const std::vector<uint8_t>& data) {
TEST_P(SourceManagerTestParam, TestStdioSourceManager) {
TestConfig config = GetParam();
- std::vector<uint8_t> compressed = ReadTestData(config.fn.c_str());
+ std::vector<uint8_t> compressed = ReadTestData(config.fn);
if (config.dparams.size_factor < 1.0) {
compressed.resize(compressed.size() * config.dparams.size_factor);
}
@@ -77,7 +77,7 @@ TEST_P(SourceManagerTestParam, TestStdioSourceManager) {
TEST_P(SourceManagerTestParam, TestMemSourceManager) {
TestConfig config = GetParam();
- std::vector<uint8_t> compressed = ReadTestData(config.fn.c_str());
+ std::vector<uint8_t> compressed = ReadTestData(config.fn);
if (config.dparams.size_factor < 1.0f) {
compressed.resize(compressed.size() * config.dparams.size_factor);
}
diff --git a/third_party/jpeg-xl/lib/jpegli/streaming_test.cc b/third_party/jpeg-xl/lib/jpegli/streaming_test.cc
index 8d2e3577f3..2e6f7029b0 100644
--- a/third_party/jpeg-xl/lib/jpegli/streaming_test.cc
+++ b/third_party/jpeg-xl/lib/jpegli/streaming_test.cc
@@ -36,13 +36,13 @@ struct SourceManager {
// input buffer. The buffer size is kept short because empty_output_buffer() is
// called only when the output buffer is full, and we want to update the decoder
// input frequently to demonstrate that streaming works.
-static constexpr size_t kOutputBufferSize = 1024;
+constexpr size_t kOutputBufferSize = 1024;
struct DestinationManager {
jpeg_destination_mgr pub;
std::vector<uint8_t> buffer;
SourceManager* dest;
- DestinationManager(SourceManager* src)
+ explicit DestinationManager(SourceManager* src)
: buffer(kOutputBufferSize), dest(src) {
pub.next_output_byte = buffer.data();
pub.free_in_buffer = buffer.size();
@@ -54,7 +54,7 @@ struct DestinationManager {
static void init_destination(j_compress_ptr cinfo) {}
static boolean empty_output_buffer(j_compress_ptr cinfo) {
- auto us = reinterpret_cast<DestinationManager*>(cinfo->dest);
+ auto* us = reinterpret_cast<DestinationManager*>(cinfo->dest);
jpeg_destination_mgr* src = &us->pub;
jpeg_source_mgr* dst = &us->dest->pub;
std::vector<uint8_t>& src_buf = us->buffer;
@@ -69,7 +69,7 @@ struct DestinationManager {
dst->bytes_in_buffer = dst_buf.size();
src->next_output_byte = src_buf.data();
src->free_in_buffer = src_buf.size();
- return true;
+ return TRUE;
}
static void term_destination(j_compress_ptr cinfo) {
@@ -87,6 +87,7 @@ class StreamingTestParam : public ::testing::TestWithParam<TestConfig> {};
TEST_P(StreamingTestParam, TestStreaming) {
jpeg_decompress_struct dinfo = {};
jpeg_compress_struct cinfo = {};
+ SourceManager src;
TestConfig config = GetParam();
TestImage& input = config.input;
TestImage output;
@@ -99,7 +100,6 @@ TEST_P(StreamingTestParam, TestStreaming) {
// compressor's output is connected to the decompressor's input.
jpegli_create_decompress(&dinfo);
jpegli_create_compress(&cinfo);
- SourceManager src;
dinfo.src = reinterpret_cast<jpeg_source_mgr*>(&src);
DestinationManager dest(&src);
cinfo.dest = reinterpret_cast<jpeg_destination_mgr*>(&dest);
@@ -107,7 +107,7 @@ TEST_P(StreamingTestParam, TestStreaming) {
cinfo.image_width = input.xsize;
cinfo.image_height = input.ysize;
cinfo.input_components = input.components;
- cinfo.in_color_space = (J_COLOR_SPACE)input.color_space;
+ cinfo.in_color_space = static_cast<J_COLOR_SPACE>(input.color_space);
jpegli_set_defaults(&cinfo);
cinfo.comp_info[0].v_samp_factor = config.jparams.v_sampling[0];
jpegli_set_progressive_level(&cinfo, 0);
@@ -122,13 +122,13 @@ TEST_P(StreamingTestParam, TestStreaming) {
while (yin < cinfo.image_height) {
// Feed one iMCU row at a time to the compressor.
size_t lines_in = std::min(iMCU_height, cinfo.image_height - yin);
- memcpy(&row_bytes[0], &input.pixels[yin * stride], lines_in * stride);
+ memcpy(row_bytes.data(), &input.pixels[yin * stride], lines_in * stride);
std::vector<JSAMPROW> rows_in(lines_in);
for (size_t i = 0; i < lines_in; ++i) {
rows_in[i] = &row_bytes[i * stride];
}
EXPECT_EQ(lines_in,
- jpegli_write_scanlines(&cinfo, &rows_in[0], lines_in));
+ jpegli_write_scanlines(&cinfo, rows_in.data(), lines_in));
yin += lines_in;
if (yin == cinfo.image_height) {
jpegli_finish_compress(&cinfo);
@@ -180,7 +180,7 @@ TEST_P(StreamingTestParam, TestStreaming) {
reinterpret_cast<JSAMPLE*>(&output.pixels[(yout + i) * stride]);
}
EXPECT_EQ(lines_out,
- jpegli_read_scanlines(&dinfo, &rows_out[0], lines_out));
+ jpegli_read_scanlines(&dinfo, rows_out.data(), lines_out));
VerifyOutputImage(input, output, yout, lines_out, 3.8f);
yout += lines_out;
diff --git a/third_party/jpeg-xl/lib/jpegli/test_utils-inl.h b/third_party/jpeg-xl/lib/jpegli/test_utils-inl.h
index a454917187..4fbcb721e4 100644
--- a/third_party/jpeg-xl/lib/jpegli/test_utils-inl.h
+++ b/third_party/jpeg-xl/lib/jpegli/test_utils-inl.h
@@ -8,20 +8,20 @@
// include paths for the jpeg headers.
// Sequential non-interleaved.
-static constexpr jpeg_scan_info kScript1[] = {
+constexpr jpeg_scan_info kScript1[] = {
{1, {0}, 0, 63, 0, 0},
{1, {1}, 0, 63, 0, 0},
{1, {2}, 0, 63, 0, 0},
};
// Sequential partially interleaved, chroma first.
-static constexpr jpeg_scan_info kScript2[] = {
+constexpr jpeg_scan_info kScript2[] = {
{2, {1, 2}, 0, 63, 0, 0},
{1, {0}, 0, 63, 0, 0},
};
// Rest of the scan scripts are progressive.
-static constexpr jpeg_scan_info kScript3[] = {
+constexpr jpeg_scan_info kScript3[] = {
// Interleaved full DC.
{3, {0, 1, 2}, 0, 0, 0, 0},
// Full AC scans.
@@ -29,7 +29,7 @@ static constexpr jpeg_scan_info kScript3[] = {
{1, {1}, 1, 63, 0, 0},
{1, {2}, 1, 63, 0, 0},
};
-static constexpr jpeg_scan_info kScript4[] = {
+constexpr jpeg_scan_info kScript4[] = {
// Non-interleaved full DC.
{1, {0}, 0, 0, 0, 0},
{1, {1}, 0, 0, 0, 0},
@@ -39,7 +39,7 @@ static constexpr jpeg_scan_info kScript4[] = {
{1, {1}, 1, 63, 0, 0},
{1, {2}, 1, 63, 0, 0},
};
-static constexpr jpeg_scan_info kScript5[] = {
+constexpr jpeg_scan_info kScript5[] = {
// Partially interleaved full DC, chroma first.
{2, {1, 2}, 0, 0, 0, 0},
{1, {0}, 0, 0, 0, 0},
@@ -52,7 +52,7 @@ static constexpr jpeg_scan_info kScript5[] = {
{1, {1}, 1, 63, 1, 0},
{1, {2}, 1, 63, 1, 0},
};
-static constexpr jpeg_scan_info kScript6[] = {
+constexpr jpeg_scan_info kScript6[] = {
// Interleaved DC shifted by 2 bits.
{3, {0, 1, 2}, 0, 0, 0, 2},
// Interleaved DC refinement scans.
@@ -64,7 +64,7 @@ static constexpr jpeg_scan_info kScript6[] = {
{1, {2}, 1, 63, 0, 0},
};
-static constexpr jpeg_scan_info kScript7[] = {
+constexpr jpeg_scan_info kScript7[] = {
// Non-interleaved DC shifted by 2 bits.
{1, {0}, 0, 0, 0, 2},
{1, {1}, 0, 0, 0, 2},
@@ -83,7 +83,7 @@ static constexpr jpeg_scan_info kScript7[] = {
{1, {2}, 1, 63, 0, 0},
};
-static constexpr jpeg_scan_info kScript8[] = {
+constexpr jpeg_scan_info kScript8[] = {
// Partially interleaved DC shifted by 2 bits, chroma first
{2, {1, 2}, 0, 0, 0, 2},
{1, {0}, 0, 0, 0, 2},
@@ -99,7 +99,7 @@ static constexpr jpeg_scan_info kScript8[] = {
{1, {2}, 1, 63, 0, 0},
};
-static constexpr jpeg_scan_info kScript9[] = {
+constexpr jpeg_scan_info kScript9[] = {
// Interleaved full DC.
{3, {0, 1, 2}, 0, 0, 0, 0},
// AC scans for component 0
@@ -123,7 +123,7 @@ static constexpr jpeg_scan_info kScript9[] = {
{1, {2}, 17, 63, 1, 0},
};
-static constexpr jpeg_scan_info kScript10[] = {
+constexpr jpeg_scan_info kScript10[] = {
// Interleaved full DC.
{3, {0, 1, 2}, 0, 0, 0, 0},
// AC scans for spectral range 1..16
@@ -156,14 +156,14 @@ struct ScanScript {
const jpeg_scan_info* scans;
};
-static constexpr ScanScript kTestScript[] = {
+constexpr ScanScript kTestScript[] = {
{ARRAY_SIZE(kScript1), kScript1}, {ARRAY_SIZE(kScript2), kScript2},
{ARRAY_SIZE(kScript3), kScript3}, {ARRAY_SIZE(kScript4), kScript4},
{ARRAY_SIZE(kScript5), kScript5}, {ARRAY_SIZE(kScript6), kScript6},
{ARRAY_SIZE(kScript7), kScript7}, {ARRAY_SIZE(kScript8), kScript8},
{ARRAY_SIZE(kScript9), kScript9}, {ARRAY_SIZE(kScript10), kScript10},
};
-static constexpr int kNumTestScripts = ARRAY_SIZE(kTestScript);
+constexpr int kNumTestScripts = ARRAY_SIZE(kTestScript);
void SetScanDecompressParams(const DecompressParams& dparams,
j_decompress_ptr cinfo, int scan_number) {
@@ -178,7 +178,7 @@ void SetScanDecompressParams(const DecompressParams& dparams,
return;
}
if (dparams.quantize_colors) {
- cinfo->dither_mode = (J_DITHER_MODE)sparams->dither_mode;
+ cinfo->dither_mode = static_cast<J_DITHER_MODE>(sparams->dither_mode);
if (sparams->color_quant_mode == CQUANT_1PASS) {
cinfo->two_pass_quantize = FALSE;
cinfo->colormap = nullptr;
@@ -194,7 +194,8 @@ void SetScanDecompressParams(const DecompressParams& dparams,
cinfo->colormap = (*cinfo->mem->alloc_sarray)(
reinterpret_cast<j_common_ptr>(cinfo), JPOOL_IMAGE,
cinfo->actual_number_of_colors, 3);
- jxl::msan::UnpoisonMemory(cinfo->colormap, 3 * sizeof(JSAMPROW));
+ jxl::msan::UnpoisonMemory(reinterpret_cast<void*>(cinfo->colormap),
+ 3 * sizeof(JSAMPLE*));
for (int i = 0; i < kTestColorMapNumColors; ++i) {
cinfo->colormap[0][i] = (kTestColorMap[i] >> 16) & 0xff;
cinfo->colormap[1][i] = (kTestColorMap[i] >> 8) & 0xff;
@@ -212,20 +213,21 @@ void SetScanDecompressParams(const DecompressParams& dparams,
void SetDecompressParams(const DecompressParams& dparams,
j_decompress_ptr cinfo) {
- cinfo->do_block_smoothing = dparams.do_block_smoothing;
- cinfo->do_fancy_upsampling = dparams.do_fancy_upsampling;
+ cinfo->do_block_smoothing = dparams.do_block_smoothing ? 1 : 0;
+ cinfo->do_fancy_upsampling = dparams.do_fancy_upsampling ? 1 : 0;
if (dparams.output_mode == RAW_DATA) {
cinfo->raw_data_out = TRUE;
}
if (dparams.set_out_color_space) {
- cinfo->out_color_space = (J_COLOR_SPACE)dparams.out_color_space;
+ cinfo->out_color_space =
+ static_cast<J_COLOR_SPACE>(dparams.out_color_space);
if (dparams.out_color_space == JCS_UNKNOWN) {
cinfo->jpeg_color_space = JCS_UNKNOWN;
}
}
cinfo->scale_num = dparams.scale_num;
cinfo->scale_denom = dparams.scale_denom;
- cinfo->quantize_colors = dparams.quantize_colors;
+ cinfo->quantize_colors = dparams.quantize_colors ? 1 : 0;
cinfo->desired_number_of_colors = dparams.desired_number_of_colors;
if (!dparams.scan_params.empty()) {
if (cinfo->buffered_image) {
@@ -420,7 +422,7 @@ void CopyCoefficients(j_decompress_ptr cinfo, jvirt_barray_ptr* coef_arrays,
DCTSIZE2);
for (size_t by = 0; by < comp->height_in_blocks; ++by) {
JBLOCKARRAY ba = (*cinfo->mem->access_virt_barray)(comptr, coef_arrays[c],
- by, 1, true);
+ by, 1, TRUE);
size_t stride = comp->width_in_blocks * sizeof(JBLOCK);
size_t offset = by * comp->width_in_blocks * DCTSIZE2;
memcpy(&coeffs[offset], ba[0], stride);
diff --git a/third_party/jpeg-xl/lib/jpegli/test_utils.cc b/third_party/jpeg-xl/lib/jpegli/test_utils.cc
index 232b937496..4e675070cf 100644
--- a/third_party/jpeg-xl/lib/jpegli/test_utils.cc
+++ b/third_party/jpeg-xl/lib/jpegli/test_utils.cc
@@ -153,7 +153,7 @@ bool ReadPNM(const std::vector<uint8_t>& data, size_t* xsize, size_t* ysize,
return false;
}
pixels->resize(data.data() + data.size() - pos);
- memcpy(&(*pixels)[0], pos, pixels->size());
+ memcpy(pixels->data(), pos, pixels->size());
return true;
}
@@ -216,7 +216,8 @@ std::ostream& operator<<(std::ostream& os, const TestImage& input) {
os << input.xsize << "x" << input.ysize;
os << IOMethodName(input.data_type, input.endianness);
if (input.color_space != JCS_RGB) {
- os << "InputColor" << ColorSpaceName((J_COLOR_SPACE)input.color_space);
+ os << "InputColor"
+ << ColorSpaceName(static_cast<J_COLOR_SPACE>(input.color_space));
}
if (input.color_space == JCS_UNKNOWN) {
os << input.components;
@@ -229,18 +230,18 @@ std::ostream& operator<<(std::ostream& os, const CompressParams& jparams) {
os << SamplingId(jparams);
if (jparams.set_jpeg_colorspace) {
os << "JpegColor"
- << ColorSpaceName((J_COLOR_SPACE)jparams.jpeg_color_space);
+ << ColorSpaceName(static_cast<J_COLOR_SPACE>(jparams.jpeg_color_space));
}
if (!jparams.comp_ids.empty()) {
os << "CID";
- for (size_t i = 0; i < jparams.comp_ids.size(); ++i) {
- os << jparams.comp_ids[i];
+ for (int cid : jparams.comp_ids) {
+ os << cid;
}
}
if (!jparams.quant_indexes.empty()) {
os << "QIDX";
- for (size_t i = 0; i < jparams.quant_indexes.size(); ++i) {
- os << jparams.quant_indexes[i];
+ for (int qi : jparams.quant_indexes) {
+ os << qi;
}
for (const auto& table : jparams.quant_tables) {
os << "TABLE" << table.slot_idx << "T" << table.table_type << "F"
@@ -320,7 +321,7 @@ void RGBToYCbCr(float r, float g, float b, float* y, float* cb, float* cr) {
void ConvertPixel(const uint8_t* input_rgb, uint8_t* out,
J_COLOR_SPACE colorspace, size_t num_channels,
JpegliDataType data_type = JPEGLI_TYPE_UINT8,
- bool swap_endianness = JPEGLI_NATIVE_ENDIAN) {
+ JXL_BOOL swap_endianness = JPEGLI_NATIVE_ENDIAN) {
const float kMul = 255.0f;
float r = input_rgb[0] / kMul;
float g = input_rgb[1] / kMul;
@@ -334,7 +335,9 @@ void ConvertPixel(const uint8_t* input_rgb, uint8_t* out,
out8[c] = input_rgb[std::min<size_t>(2, c)];
}
} else if (colorspace == JCS_YCbCr) {
- float Y, Cb, Cr;
+ float Y;
+ float Cb;
+ float Cr;
RGBToYCbCr(r, g, b, &Y, &Cb, &Cr);
out8[0] = static_cast<uint8_t>(std::round(Y * kMul));
out8[1] = static_cast<uint8_t>(std::round(Cb * kMul));
@@ -350,7 +353,9 @@ void ConvertPixel(const uint8_t* input_rgb, uint8_t* out,
out8[1] = static_cast<uint8_t>(std::round((1.0f - g) * kMul));
out8[2] = static_cast<uint8_t>(std::round((1.0f - b) * kMul));
} else if (colorspace == JCS_YCCK) {
- float Y, Cb, Cr;
+ float Y;
+ float Cb;
+ float Cr;
RGBToYCbCr(r, g, b, &Y, &Cb, &Cr);
out8[0] = static_cast<uint8_t>(std::round(Y * kMul));
out8[1] = static_cast<uint8_t>(std::round(Cb * kMul));
@@ -399,7 +404,10 @@ void ConvertToGrayscale(TestImage* img) {
void GeneratePixels(TestImage* img) {
const std::vector<uint8_t> imgdata = ReadTestData("jxl/flower/flower.pnm");
- size_t xsize, ysize, channels, bitdepth;
+ size_t xsize;
+ size_t ysize;
+ size_t channels;
+ size_t bitdepth;
std::vector<uint8_t> pixels;
JXL_CHECK(ReadPNM(imgdata, &xsize, &ysize, &channels, &bitdepth, &pixels));
if (img->xsize == 0) img->xsize = xsize;
@@ -412,7 +420,8 @@ void GeneratePixels(TestImage* img) {
size_t in_stride = xsize * in_bytes_per_pixel;
size_t x0 = (xsize - img->xsize) / 2;
size_t y0 = (ysize - img->ysize) / 2;
- SetNumChannels((J_COLOR_SPACE)img->color_space, &img->components);
+ SetNumChannels(static_cast<J_COLOR_SPACE>(img->color_space),
+ &img->components);
size_t out_bytes_per_pixel =
jpegli_bytes_per_sample(img->data_type) * img->components;
size_t out_stride = img->xsize * out_bytes_per_pixel;
@@ -427,8 +436,9 @@ void GeneratePixels(TestImage* img) {
size_t idx_in = y * in_stride + x * in_bytes_per_pixel;
size_t idx_out = iy * out_stride + ix * out_bytes_per_pixel;
ConvertPixel(&pixels[idx_in], &img->pixels[idx_out],
- (J_COLOR_SPACE)img->color_space, img->components,
- img->data_type, swap_endianness);
+ static_cast<J_COLOR_SPACE>(img->color_space),
+ img->components, img->data_type,
+ TO_JXL_BOOL(swap_endianness));
}
}
}
@@ -492,7 +502,7 @@ void EncodeWithJpegli(const TestImage& input, const CompressParams& jparams,
jpegli_set_progressive_level(cinfo, 0);
}
jpegli_set_defaults(cinfo);
- cinfo->in_color_space = (J_COLOR_SPACE)input.color_space;
+ cinfo->in_color_space = static_cast<J_COLOR_SPACE>(input.color_space);
jpegli_default_colorspace(cinfo);
if (jparams.override_JFIF >= 0) {
cinfo->write_JFIF_header = jparams.override_JFIF;
@@ -501,7 +511,8 @@ void EncodeWithJpegli(const TestImage& input, const CompressParams& jparams,
cinfo->write_Adobe_marker = jparams.override_Adobe;
}
if (jparams.set_jpeg_colorspace) {
- jpegli_set_colorspace(cinfo, (J_COLOR_SPACE)jparams.jpeg_color_space);
+ jpegli_set_colorspace(cinfo,
+ static_cast<J_COLOR_SPACE>(jparams.jpeg_color_space));
}
if (!jparams.comp_ids.empty()) {
for (int c = 0; c < cinfo->num_components; ++c) {
@@ -522,15 +533,16 @@ void EncodeWithJpegli(const TestImage& input, const CompressParams& jparams,
for (const auto& table : jparams.quant_tables) {
if (table.add_raw) {
cinfo->quant_tbl_ptrs[table.slot_idx] =
- jpegli_alloc_quant_table((j_common_ptr)cinfo);
+ jpegli_alloc_quant_table(reinterpret_cast<j_common_ptr>(cinfo));
for (int k = 0; k < DCTSIZE2; ++k) {
cinfo->quant_tbl_ptrs[table.slot_idx]->quantval[k] =
table.quantval[k];
}
cinfo->quant_tbl_ptrs[table.slot_idx]->sent_table = FALSE;
} else {
- jpegli_add_quant_table(cinfo, table.slot_idx, &table.basic_table[0],
- table.scale_factor, table.force_baseline);
+ jpegli_add_quant_table(cinfo, table.slot_idx, table.basic_table.data(),
+ table.scale_factor,
+ TO_JXL_BOOL(table.force_baseline));
}
}
}
@@ -546,7 +558,8 @@ void EncodeWithJpegli(const TestImage& input, const CompressParams& jparams,
jpegli_set_progressive_level(cinfo, jparams.progressive_mode);
}
jpegli_set_input_format(cinfo, input.data_type, input.endianness);
- jpegli_enable_adaptive_quantization(cinfo, jparams.use_adaptive_quantization);
+ jpegli_enable_adaptive_quantization(
+ cinfo, TO_JXL_BOOL(jparams.use_adaptive_quantization));
cinfo->restart_interval = jparams.restart_interval;
cinfo->restart_in_rows = jparams.restart_in_rows;
cinfo->smoothing_factor = jparams.smoothing_factor;
@@ -555,7 +568,7 @@ void EncodeWithJpegli(const TestImage& input, const CompressParams& jparams,
} else if (jparams.optimize_coding == 0) {
cinfo->optimize_coding = FALSE;
}
- cinfo->raw_data_in = !input.raw_data.empty();
+ cinfo->raw_data_in = TO_JXL_BOOL(!input.raw_data.empty());
if (jparams.optimize_coding == 0 && jparams.use_flat_dc_luma_code) {
JHUFF_TBL* tbl = cinfo->dc_huff_tbl_ptrs[0];
memset(tbl, 0, sizeof(*tbl));
@@ -572,13 +585,13 @@ void EncodeWithJpegli(const TestImage& input, const CompressParams& jparams,
cinfo->ac_huff_tbl_ptrs[0]->sent_table = TRUE;
cinfo->ac_huff_tbl_ptrs[1]->sent_table = TRUE;
}
- jpegli_start_compress(cinfo, write_all_tables);
+ jpegli_start_compress(cinfo, TO_JXL_BOOL(write_all_tables));
if (jparams.add_marker) {
jpegli_write_marker(cinfo, kSpecialMarker0, kMarkerData,
sizeof(kMarkerData));
jpegli_write_m_header(cinfo, kSpecialMarker1, sizeof(kMarkerData));
- for (size_t p = 0; p < sizeof(kMarkerData); ++p) {
- jpegli_write_m_byte(cinfo, kMarkerData[p]);
+ for (uint8_t c : kMarkerData) {
+ jpegli_write_m_byte(cinfo, c);
}
for (size_t i = 0; i < kMarkerSequenceLen; ++i) {
jpegli_write_marker(cinfo, kMarkerSequence[i], kMarkerData,
@@ -597,7 +610,7 @@ void EncodeWithJpegli(const TestImage& input, const CompressParams& jparams,
std::vector<JSAMPARRAY> data(cinfo->num_components);
for (int c = 0; c < cinfo->num_components; ++c) {
rowdata[c].resize(jparams.v_samp(c) * DCTSIZE);
- data[c] = &rowdata[c][0];
+ data[c] = rowdata[c].data();
}
while (cinfo->next_scanline < cinfo->image_height) {
for (int c = 0; c < cinfo->num_components; ++c) {
@@ -610,7 +623,7 @@ void EncodeWithJpegli(const TestImage& input, const CompressParams& jparams,
(y0 + i < cheight ? &raw_data[c][(y0 + i) * cwidth] : nullptr);
}
}
- size_t num_lines = jpegli_write_raw_data(cinfo, &data[0], max_lines);
+ size_t num_lines = jpegli_write_raw_data(cinfo, data.data(), max_lines);
JXL_CHECK(num_lines == max_lines);
}
} else if (!input.coeffs.empty()) {
@@ -630,15 +643,15 @@ void EncodeWithJpegli(const TestImage& input, const CompressParams& jparams,
jpegli_write_marker(cinfo, kSpecialMarker0, kMarkerData,
sizeof(kMarkerData));
jpegli_write_m_header(cinfo, kSpecialMarker1, sizeof(kMarkerData));
- for (size_t p = 0; p < sizeof(kMarkerData); ++p) {
- jpegli_write_m_byte(cinfo, kMarkerData[p]);
+ for (uint8_t c : kMarkerData) {
+ jpegli_write_m_byte(cinfo, c);
}
}
for (int c = 0; c < cinfo->num_components; ++c) {
jpeg_component_info* comp = &cinfo->comp_info[c];
for (size_t by = 0; by < comp->height_in_blocks; ++by) {
JBLOCKARRAY ba = (*cinfo->mem->access_virt_barray)(
- comptr, coef_arrays[c], by, 1, true);
+ comptr, coef_arrays[c], by, 1, TRUE);
size_t stride = comp->width_in_blocks * sizeof(JBLOCK);
size_t offset = by * comp->width_in_blocks * DCTSIZE2;
memcpy(ba[0], &input.coeffs[c][offset], stride);
@@ -649,7 +662,7 @@ void EncodeWithJpegli(const TestImage& input, const CompressParams& jparams,
jpegli_bytes_per_sample(input.data_type);
std::vector<uint8_t> row_bytes(stride);
for (size_t y = 0; y < cinfo->image_height; ++y) {
- memcpy(&row_bytes[0], &input.pixels[y * stride], stride);
+ memcpy(row_bytes.data(), &input.pixels[y * stride], stride);
JSAMPROW row[] = {row_bytes.data()};
jpegli_write_scanlines(cinfo, row, 1);
}
@@ -681,15 +694,15 @@ bool EncodeWithJpegli(const TestImage& input, const CompressParams& jparams,
int NumTestScanScripts() { return kNumTestScripts; }
-void DumpImage(const TestImage& image, const std::string fn) {
+void DumpImage(const TestImage& image, const std::string& fn) {
JXL_CHECK(image.components == 1 || image.components == 3);
size_t bytes_per_sample = jpegli_bytes_per_sample(image.data_type);
uint32_t maxval = (1u << (8 * bytes_per_sample)) - 1;
char type = image.components == 1 ? '5' : '6';
std::ofstream out(fn.c_str(), std::ofstream::binary);
- out << "P" << type << std::endl
- << image.xsize << " " << image.ysize << std::endl
- << maxval << std::endl;
+ out << "P" << type << "\n"
+ << image.xsize << " " << image.ysize << "\n"
+ << maxval << "\n";
out.write(reinterpret_cast<const char*>(image.pixels.data()),
image.pixels.size());
out.close();
diff --git a/third_party/jpeg-xl/lib/jpegli/testing.h b/third_party/jpeg-xl/lib/jpegli/testing.h
index 873a0171e7..6a6e0ca638 100644
--- a/third_party/jpeg-xl/lib/jpegli/testing.h
+++ b/third_party/jpeg-xl/lib/jpegli/testing.h
@@ -6,15 +6,7 @@
#ifndef LIB_JPEGLI_TESTING_H_
#define LIB_JPEGLI_TESTING_H_
-// GTest/GMock specific macros / wrappers.
-
-// gmock unconditionally redefines those macros (to wrong values).
-// Lets include it only here and mitigate the problem.
-#pragma push_macro("PRIdS")
-#pragma push_macro("PRIuS")
-#include "gmock/gmock.h"
-#pragma pop_macro("PRIuS")
-#pragma pop_macro("PRIdS")
+// GTest specific macros / wrappers.
#include "gtest/gtest.h"
@@ -28,8 +20,12 @@
// Ensures that we don't make our test bounds too lax, effectively disabling the
// tests.
-MATCHER_P(IsSlightlyBelow, max, "") {
- return max * 0.75 <= arg && arg <= max * 1.0;
-}
+#define EXPECT_SLIGHTLY_BELOW(A, E) \
+ { \
+ double _actual = (A); \
+ double _expected = (E); \
+ EXPECT_LE(_actual, _expected); \
+ EXPECT_GE(_actual, 0.75 * _expected); \
+ }
#endif // LIB_JPEGLI_TESTING_H_
diff --git a/third_party/jpeg-xl/lib/jpegli/transpose-inl.h b/third_party/jpeg-xl/lib/jpegli/transpose-inl.h
index 9fdd222f4e..cdc289f96c 100644
--- a/third_party/jpeg-xl/lib/jpegli/transpose-inl.h
+++ b/third_party/jpeg-xl/lib/jpegli/transpose-inl.h
@@ -18,8 +18,8 @@ namespace HWY_NAMESPACE {
namespace {
#if HWY_CAP_GE256
-static JXL_INLINE void Transpose8x8Block(const float* JXL_RESTRICT from,
- float* JXL_RESTRICT to) {
+JXL_INLINE void Transpose8x8Block(const float* JXL_RESTRICT from,
+ float* JXL_RESTRICT to) {
const HWY_CAPPED(float, 8) d;
auto i0 = Load(d, from);
auto i1 = Load(d, from + 1 * 8);
@@ -67,8 +67,8 @@ static JXL_INLINE void Transpose8x8Block(const float* JXL_RESTRICT from,
Store(i7, d, to + 7 * 8);
}
#elif HWY_TARGET != HWY_SCALAR
-static JXL_INLINE void Transpose8x8Block(const float* JXL_RESTRICT from,
- float* JXL_RESTRICT to) {
+JXL_INLINE void Transpose8x8Block(const float* JXL_RESTRICT from,
+ float* JXL_RESTRICT to) {
const HWY_CAPPED(float, 4) d;
for (size_t n = 0; n < 8; n += 4) {
for (size_t m = 0; m < 8; m += 4) {
diff --git a/third_party/jpeg-xl/lib/jpegli/upsample.cc b/third_party/jpeg-xl/lib/jpegli/upsample.cc
index 5559aa78a6..7dae841b8a 100644
--- a/third_party/jpeg-xl/lib/jpegli/upsample.cc
+++ b/third_party/jpeg-xl/lib/jpegli/upsample.cc
@@ -122,7 +122,7 @@ HWY_EXPORT(Upsample2Vertical);
void Upsample2Horizontal(float* JXL_RESTRICT row,
float* JXL_RESTRICT scratch_space, size_t len_out) {
- return HWY_DYNAMIC_DISPATCH(Upsample2Horizontal)(row, scratch_space, len_out);
+ HWY_DYNAMIC_DISPATCH(Upsample2Horizontal)(row, scratch_space, len_out);
}
void Upsample2Vertical(const float* JXL_RESTRICT row_top,
@@ -130,8 +130,8 @@ void Upsample2Vertical(const float* JXL_RESTRICT row_top,
const float* JXL_RESTRICT row_bot,
float* JXL_RESTRICT row_out0,
float* JXL_RESTRICT row_out1, size_t len) {
- return HWY_DYNAMIC_DISPATCH(Upsample2Vertical)(row_top, row_mid, row_bot,
- row_out0, row_out1, len);
+ HWY_DYNAMIC_DISPATCH(Upsample2Vertical)
+ (row_top, row_mid, row_bot, row_out0, row_out1, len);
}
} // namespace jpegli
#endif // HWY_ONCE
diff --git a/third_party/jpeg-xl/lib/jxl.cmake b/third_party/jpeg-xl/lib/jxl.cmake
index 8c7e711f52..86fa37151d 100644
--- a/third_party/jpeg-xl/lib/jxl.cmake
+++ b/third_party/jpeg-xl/lib/jxl.cmake
@@ -60,7 +60,7 @@ include(GenerateExportHeader)
# CMake does not allow generate_export_header for INTERFACE library, so we
# add this stub library just for file generation.
-add_library(jxl_export OBJECT ${JPEGXL_INTERNAL_PUBLIC_HEADERS})
+add_library(jxl_export OBJECT ${JPEGXL_INTERNAL_PUBLIC_HEADERS} nothing.cc)
set_target_properties(jxl_export PROPERTIES
CXX_VISIBILITY_PRESET hidden
VISIBILITY_INLINES_HIDDEN 1
@@ -269,8 +269,10 @@ set(JPEGXL_LIBRARY_REQUIRES
if (BUILD_SHARED_LIBS)
set(JPEGXL_REQUIRES_TYPE "Requires.private")
+ set(JPEGXL_PRIVATE_LIBS "-lm ${PKGCONFIG_CXX_LIB}")
else()
set(JPEGXL_REQUIRES_TYPE "Requires")
+ set(JPEGXL_PUBLIC_LIBS "-lm ${PKGCONFIG_CXX_LIB}")
endif()
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/jxl/libjxl.pc.in"
diff --git a/third_party/jpeg-xl/lib/jxl/ac_strategy.cc b/third_party/jpeg-xl/lib/jxl/ac_strategy.cc
index 3de477f71c..e7a72f5a33 100644
--- a/third_party/jpeg-xl/lib/jxl/ac_strategy.cc
+++ b/third_party/jpeg-xl/lib/jxl/ac_strategy.cc
@@ -8,12 +8,9 @@
#include <string.h>
#include <algorithm>
-#include <numeric> // iota
-#include <type_traits>
#include <utility>
#include "lib/jxl/base/bits.h"
-#include "lib/jxl/image_ops.h"
namespace jxl {
@@ -86,10 +83,12 @@ constexpr size_t AcStrategy::kMaxCoeffBlocks;
constexpr size_t AcStrategy::kMaxBlockDim;
constexpr size_t AcStrategy::kMaxCoeffArea;
-AcStrategyImage::AcStrategyImage(size_t xsize, size_t ysize)
- : layers_(xsize, ysize) {
- row_ = layers_.Row(0);
- stride_ = layers_.PixelsPerRow();
+StatusOr<AcStrategyImage> AcStrategyImage::Create(size_t xsize, size_t ysize) {
+ AcStrategyImage img;
+ JXL_ASSIGN_OR_RETURN(img.layers_, ImageB::Create(xsize, ysize));
+ img.row_ = img.layers_.Row(0);
+ img.stride_ = img.layers_.PixelsPerRow();
+ return img;
}
size_t AcStrategyImage::CountBlocks(AcStrategy::Type type) const {
diff --git a/third_party/jpeg-xl/lib/jxl/ac_strategy.h b/third_party/jpeg-xl/lib/jxl/ac_strategy.h
index ecdcbbbd32..9e5917ff1b 100644
--- a/third_party/jpeg-xl/lib/jxl/ac_strategy.h
+++ b/third_party/jpeg-xl/lib/jxl/ac_strategy.h
@@ -144,7 +144,7 @@ class AcStrategy {
8, 4, 8, 16, 8, 16, 32, 16, 32};
static_assert(sizeof(kLut) / sizeof(*kLut) == kNumValidStrategies,
"Update LUT");
- return kLut[size_t(strategy_)];
+ return kLut[static_cast<size_t>(strategy_)];
}
JXL_INLINE size_t covered_blocks_y() const {
@@ -153,7 +153,7 @@ class AcStrategy {
8, 8, 4, 16, 16, 8, 32, 32, 16};
static_assert(sizeof(kLut) / sizeof(*kLut) == kNumValidStrategies,
"Update LUT");
- return kLut[size_t(strategy_)];
+ return kLut[static_cast<size_t>(strategy_)];
}
JXL_INLINE size_t log2_covered_blocks() const {
@@ -162,7 +162,7 @@ class AcStrategy {
6, 5, 5, 8, 7, 7, 10, 9, 9};
static_assert(sizeof(kLut) / sizeof(*kLut) == kNumValidStrategies,
"Update LUT");
- return kLut[size_t(strategy_)];
+ return kLut[static_cast<size_t>(strategy_)];
}
private:
@@ -181,7 +181,9 @@ class AcStrategyRow {
public:
explicit AcStrategyRow(const uint8_t* row) : row_(row) {}
AcStrategy operator[](size_t x) const {
- return AcStrategy(static_cast<AcStrategy::Type>(row_[x] >> 1), row_[x] & 1);
+ AcStrategy::Type strategy = static_cast<AcStrategy::Type>(row_[x] >> 1);
+ bool is_first = static_cast<bool>(row_[x] & 1);
+ return AcStrategy(strategy, is_first);
}
private:
@@ -191,7 +193,8 @@ class AcStrategyRow {
class AcStrategyImage {
public:
AcStrategyImage() = default;
- AcStrategyImage(size_t xsize, size_t ysize);
+ static StatusOr<AcStrategyImage> Create(size_t xsize, size_t ysize);
+
AcStrategyImage(AcStrategyImage&&) = default;
AcStrategyImage& operator=(AcStrategyImage&&) = default;
diff --git a/third_party/jpeg-xl/lib/jxl/ac_strategy_test.cc b/third_party/jpeg-xl/lib/jxl/ac_strategy_test.cc
index 3745db2b32..b1d9103466 100644
--- a/third_party/jpeg-xl/lib/jxl/ac_strategy_test.cc
+++ b/third_party/jpeg-xl/lib/jxl/ac_strategy_test.cc
@@ -81,7 +81,8 @@ class AcStrategyRoundtrip : public ::hwy::TestWithParamTargetAndT<int> {
HWY_TARGET_INSTANTIATE_TEST_SUITE_P_T(
AcStrategyRoundtrip,
- ::testing::Range(0, int(AcStrategy::Type::kNumValidStrategies)));
+ ::testing::Range(0,
+ static_cast<int>(AcStrategy::Type::kNumValidStrategies)));
TEST_P(AcStrategyRoundtrip, Test) { Run(); }
@@ -141,7 +142,8 @@ class AcStrategyRoundtripDownsample
HWY_TARGET_INSTANTIATE_TEST_SUITE_P_T(
AcStrategyRoundtripDownsample,
- ::testing::Range(0, int(AcStrategy::Type::kNumValidStrategies)));
+ ::testing::Range(0,
+ static_cast<int>(AcStrategy::Type::kNumValidStrategies)));
TEST_P(AcStrategyRoundtripDownsample, Test) { Run(); }
@@ -205,7 +207,8 @@ class AcStrategyDownsample : public ::hwy::TestWithParamTargetAndT<int> {
HWY_TARGET_INSTANTIATE_TEST_SUITE_P_T(
AcStrategyDownsample,
- ::testing::Range(0, int(AcStrategy::Type::kNumValidStrategies)));
+ ::testing::Range(0,
+ static_cast<int>(AcStrategy::Type::kNumValidStrategies)));
TEST_P(AcStrategyDownsample, Test) { Run(); }
diff --git a/third_party/jpeg-xl/lib/jxl/alpha_test.cc b/third_party/jpeg-xl/lib/jxl/alpha_test.cc
index ddafd829ec..a93254f3dd 100644
--- a/third_party/jpeg-xl/lib/jxl/alpha_test.cc
+++ b/third_party/jpeg-xl/lib/jxl/alpha_test.cc
@@ -5,69 +5,59 @@
#include "lib/jxl/alpha.h"
-#include "lib/jxl/test_utils.h"
+#include <array>
+
+#include "lib/jxl/base/common.h"
#include "lib/jxl/testing.h"
namespace jxl {
namespace {
-using ::testing::_;
-using ::testing::ElementsAre;
-using ::testing::FloatNear;
-
TEST(AlphaTest, BlendingWithNonPremultiplied) {
- const float bg_rgb[3] = {100, 110, 120};
+ const Color bg_rgb{100, 110, 120};
const float bg_a = 180.f / 255;
- const float fg_rgb[3] = {25, 21, 23};
+ const Color fg_rgb{25, 21, 23};
const float fg_a = 15420.f / 65535;
const float fg_a2 = 2.0f;
- float out_rgb[3];
+ Color out_rgb;
float out_a;
PerformAlphaBlending(
/*bg=*/{&bg_rgb[0], &bg_rgb[1], &bg_rgb[2], &bg_a},
/*fg=*/{&fg_rgb[0], &fg_rgb[1], &fg_rgb[2], &fg_a},
/*out=*/{&out_rgb[0], &out_rgb[1], &out_rgb[2], &out_a}, 1,
/*alpha_is_premultiplied=*/false, /*clamp=*/false);
- EXPECT_THAT(out_rgb,
- ElementsAre(FloatNear(77.2f, .05f), FloatNear(83.0f, .05f),
- FloatNear(90.6f, .05f)));
+ EXPECT_ARRAY_NEAR(out_rgb, (Color{77.2f, 83.0f, 90.6f}), 0.05f);
EXPECT_NEAR(out_a, 3174.f / 4095, 1e-5);
PerformAlphaBlending(
/*bg=*/{&bg_rgb[0], &bg_rgb[1], &bg_rgb[2], &bg_a},
/*fg=*/{&fg_rgb[0], &fg_rgb[1], &fg_rgb[2], &fg_a2},
/*out=*/{&out_rgb[0], &out_rgb[1], &out_rgb[2], &out_a}, 1,
/*alpha_is_premultiplied=*/false, /*clamp=*/true);
- EXPECT_THAT(out_rgb, ElementsAre(FloatNear(fg_rgb[0], .05f),
- FloatNear(fg_rgb[1], .05f),
- FloatNear(fg_rgb[2], .05f)));
+ EXPECT_ARRAY_NEAR(out_rgb, fg_rgb, 0.05f);
EXPECT_NEAR(out_a, 1.0f, 1e-5);
}
TEST(AlphaTest, BlendingWithPremultiplied) {
- const float bg_rgb[3] = {100, 110, 120};
+ const Color bg_rgb{100, 110, 120};
const float bg_a = 180.f / 255;
- const float fg_rgb[3] = {25, 21, 23};
+ const Color fg_rgb{25, 21, 23};
const float fg_a = 15420.f / 65535;
const float fg_a2 = 2.0f;
- float out_rgb[3];
+ Color out_rgb;
float out_a;
PerformAlphaBlending(
/*bg=*/{&bg_rgb[0], &bg_rgb[1], &bg_rgb[2], &bg_a},
/*fg=*/{&fg_rgb[0], &fg_rgb[1], &fg_rgb[2], &fg_a},
/*out=*/{&out_rgb[0], &out_rgb[1], &out_rgb[2], &out_a}, 1,
/*alpha_is_premultiplied=*/true, /*clamp=*/false);
- EXPECT_THAT(out_rgb,
- ElementsAre(FloatNear(101.5f, .05f), FloatNear(105.1f, .05f),
- FloatNear(114.8f, .05f)));
+ EXPECT_ARRAY_NEAR(out_rgb, (Color{101.5f, 105.1f, 114.8f}), 0.05f);
EXPECT_NEAR(out_a, 3174.f / 4095, 1e-5);
PerformAlphaBlending(
/*bg=*/{&bg_rgb[0], &bg_rgb[1], &bg_rgb[2], &bg_a},
/*fg=*/{&fg_rgb[0], &fg_rgb[1], &fg_rgb[2], &fg_a2},
/*out=*/{&out_rgb[0], &out_rgb[1], &out_rgb[2], &out_a}, 1,
/*alpha_is_premultiplied=*/true, /*clamp=*/true);
- EXPECT_THAT(out_rgb, ElementsAre(FloatNear(fg_rgb[0], .05f),
- FloatNear(fg_rgb[1], .05f),
- FloatNear(fg_rgb[2], .05f)));
+ EXPECT_ARRAY_NEAR(out_rgb, fg_rgb, 0.05f);
EXPECT_NEAR(out_a, 1.0f, 1e-5);
}
@@ -76,58 +66,51 @@ TEST(AlphaTest, Mul) {
const float fg = 25;
float out;
PerformMulBlending(&bg, &fg, &out, 1, /*clamp=*/false);
- EXPECT_THAT(out, FloatNear(fg * bg, .05f));
+ EXPECT_NEAR(out, fg * bg, .05f);
PerformMulBlending(&bg, &fg, &out, 1, /*clamp=*/true);
- EXPECT_THAT(out, FloatNear(bg, .05f));
+ EXPECT_NEAR(out, bg, .05f);
}
TEST(AlphaTest, PremultiplyAndUnpremultiply) {
- const float alpha[] = {0.f, 63.f / 255, 127.f / 255, 1.f};
- float r[] = {120, 130, 140, 150};
- float g[] = {124, 134, 144, 154};
- float b[] = {127, 137, 147, 157};
+ using F4 = std::array<float, 4>;
+ const F4 alpha{0.f, 63.f / 255, 127.f / 255, 1.f};
+ F4 r{120, 130, 140, 150};
+ F4 g{124, 134, 144, 154};
+ F4 b{127, 137, 147, 157};
- PremultiplyAlpha(r, g, b, alpha, 4);
- EXPECT_THAT(
- r, ElementsAre(FloatNear(0.f, 1e-5f), FloatNear(130 * 63.f / 255, 1e-5f),
- FloatNear(140 * 127.f / 255, 1e-5f), 150));
- EXPECT_THAT(
- g, ElementsAre(FloatNear(0.f, 1e-5f), FloatNear(134 * 63.f / 255, 1e-5f),
- FloatNear(144 * 127.f / 255, 1e-5f), 154));
- EXPECT_THAT(
- b, ElementsAre(FloatNear(0.f, 1e-5f), FloatNear(137 * 63.f / 255, 1e-5f),
- FloatNear(147 * 127.f / 255, 1e-5f), 157));
+ PremultiplyAlpha(r.data(), g.data(), b.data(), alpha.data(), alpha.size());
+ EXPECT_ARRAY_NEAR(r, (F4{0.0f, 130 * 63.f / 255, 140 * 127.f / 255, 150}),
+ 1e-5f);
+ EXPECT_ARRAY_NEAR(g, (F4{0.0f, 134 * 63.f / 255, 144 * 127.f / 255, 154}),
+ 1e-5f);
+ EXPECT_ARRAY_NEAR(b, (F4{0.0f, 137 * 63.f / 255, 147 * 127.f / 255, 157}),
+ 1e-5f);
- UnpremultiplyAlpha(r, g, b, alpha, 4);
- EXPECT_THAT(r, ElementsAre(FloatNear(120, 1e-4f), FloatNear(130, 1e-4f),
- FloatNear(140, 1e-4f), FloatNear(150, 1e-4f)));
- EXPECT_THAT(g, ElementsAre(FloatNear(124, 1e-4f), FloatNear(134, 1e-4f),
- FloatNear(144, 1e-4f), FloatNear(154, 1e-4f)));
- EXPECT_THAT(b, ElementsAre(FloatNear(127, 1e-4f), FloatNear(137, 1e-4f),
- FloatNear(147, 1e-4f), FloatNear(157, 1e-4f)));
+ UnpremultiplyAlpha(r.data(), g.data(), b.data(), alpha.data(), alpha.size());
+ EXPECT_ARRAY_NEAR(r, (F4{120, 130, 140, 150}), 1e-4f);
+ EXPECT_ARRAY_NEAR(g, (F4{124, 134, 144, 154}), 1e-4f);
+ EXPECT_ARRAY_NEAR(b, (F4{127, 137, 147, 157}), 1e-4f);
}
TEST(AlphaTest, UnpremultiplyAndPremultiply) {
- const float alpha[] = {0.f, 63.f / 255, 127.f / 255, 1.f};
- float r[] = {50, 60, 70, 80};
- float g[] = {54, 64, 74, 84};
- float b[] = {57, 67, 77, 87};
+ using F4 = std::array<float, 4>;
+ const F4 alpha{0.f, 63.f / 255, 127.f / 255, 1.f};
+ F4 r{50, 60, 70, 80};
+ F4 g{54, 64, 74, 84};
+ F4 b{57, 67, 77, 87};
- UnpremultiplyAlpha(r, g, b, alpha, 4);
- EXPECT_THAT(r, ElementsAre(_, FloatNear(60 * 255.f / 63, 1e-4f),
- FloatNear(70 * 255.f / 127, 1e-4f), 80));
- EXPECT_THAT(g, ElementsAre(_, FloatNear(64 * 255.f / 63, 1e-4f),
- FloatNear(74 * 255.f / 127, 1e-4f), 84));
- EXPECT_THAT(b, ElementsAre(_, FloatNear(67 * 255.f / 63, 1e-4f),
- FloatNear(77 * 255.f / 127, 1e-4f), 87));
+ UnpremultiplyAlpha(r.data(), g.data(), b.data(), alpha.data(), alpha.size());
+ EXPECT_ARRAY_NEAR(
+ r, (F4{50.0f * (1 << 26), 60 * 255.f / 63, 70 * 255.f / 127, 80}), 1e-4f);
+ EXPECT_ARRAY_NEAR(
+ g, (F4{54.0f * (1 << 26), 64 * 255.f / 63, 74 * 255.f / 127, 84}), 1e-4f);
+ EXPECT_ARRAY_NEAR(
+ b, (F4{57.0f * (1 << 26), 67 * 255.f / 63, 77 * 255.f / 127, 87}), 1e-4f);
- PremultiplyAlpha(r, g, b, alpha, 4);
- EXPECT_THAT(r, ElementsAre(FloatNear(50, 1e-4f), FloatNear(60, 1e-4f),
- FloatNear(70, 1e-4f), FloatNear(80, 1e-4f)));
- EXPECT_THAT(g, ElementsAre(FloatNear(54, 1e-4f), FloatNear(64, 1e-4f),
- FloatNear(74, 1e-4f), FloatNear(84, 1e-4f)));
- EXPECT_THAT(b, ElementsAre(FloatNear(57, 1e-4f), FloatNear(67, 1e-4f),
- FloatNear(77, 1e-4f), FloatNear(87, 1e-4f)));
+ PremultiplyAlpha(r.data(), g.data(), b.data(), alpha.data(), alpha.size());
+ EXPECT_ARRAY_NEAR(r, (F4{50, 60, 70, 80}), 1e-4);
+ EXPECT_ARRAY_NEAR(g, (F4{54, 64, 74, 84}), 1e-4);
+ EXPECT_ARRAY_NEAR(b, (F4{57, 67, 77, 87}), 1e-4);
}
} // namespace
diff --git a/third_party/jpeg-xl/lib/jxl/ans_common.h b/third_party/jpeg-xl/lib/jxl/ans_common.h
index fb5058e310..44b8e3fba1 100644
--- a/third_party/jpeg-xl/lib/jxl/ans_common.h
+++ b/third_party/jpeg-xl/lib/jxl/ans_common.h
@@ -24,7 +24,8 @@ namespace jxl {
static JXL_INLINE uint32_t GetPopulationCountPrecision(uint32_t logcount,
uint32_t shift) {
int32_t r = std::min<int>(
- logcount, int(shift) - int((ANS_LOG_TAB_SIZE - logcount) >> 1));
+ logcount, static_cast<int>(shift) -
+ static_cast<int>((ANS_LOG_TAB_SIZE - logcount) >> 1));
if (r < 0) return 0;
return r;
}
diff --git a/third_party/jpeg-xl/lib/jxl/ans_test.cc b/third_party/jpeg-xl/lib/jxl/ans_test.cc
index c28daf7b85..5d6a5ef090 100644
--- a/third_party/jpeg-xl/lib/jxl/ans_test.cc
+++ b/third_party/jpeg-xl/lib/jxl/ans_test.cc
@@ -113,8 +113,8 @@ void RoundtripRandomUnbalancedStream(int alphabet_size) {
Rng rng(0);
for (size_t i = 0; i < kReps; i++) {
std::vector<int> distributions[kNumHistograms] = {};
- for (int j = 0; j < kNumHistograms; j++) {
- distributions[j].resize(kPrecision);
+ for (auto& distr : distributions) {
+ distr.resize(kPrecision);
int symbol = 0;
int remaining = 1;
for (int k = 0; k < kPrecision; k++) {
@@ -126,7 +126,7 @@ void RoundtripRandomUnbalancedStream(int alphabet_size) {
// sufficiently dissimilar.
remaining = rng.UniformU(0, kPrecision - k + 1);
}
- distributions[j][k] = symbol;
+ distr[k] = symbol;
remaining--;
}
}
@@ -158,7 +158,8 @@ TEST(ANSTest, RandomUnbalancedStreamRoundtripBig) {
TEST(ANSTest, UintConfigRoundtrip) {
for (size_t log_alpha_size = 5; log_alpha_size <= 8; log_alpha_size++) {
- std::vector<HybridUintConfig> uint_config, uint_config_dec;
+ std::vector<HybridUintConfig> uint_config;
+ std::vector<HybridUintConfig> uint_config_dec;
for (size_t i = 0; i < log_alpha_size; i++) {
for (size_t j = 0; j <= i; j++) {
for (size_t k = 0; k <= i - j; k++) {
@@ -187,16 +188,16 @@ TEST(ANSTest, UintConfigRoundtrip) {
void TestCheckpointing(bool ans, bool lz77) {
std::vector<std::vector<Token>> input_values(1);
for (size_t i = 0; i < 1024; i++) {
- input_values[0].push_back(Token(0, i % 4));
+ input_values[0].emplace_back(0, i % 4);
}
// up to lz77 window size.
for (size_t i = 0; i < (1 << 20) - 1022; i++) {
- input_values[0].push_back(Token(0, (i % 5) + 4));
+ input_values[0].emplace_back(0, (i % 5) + 4);
}
// Ensure that when the window wraps around, new values are different.
- input_values[0].push_back(Token(0, 0));
+ input_values[0].emplace_back(0, 0);
for (size_t i = 0; i < 1024; i++) {
- input_values[0].push_back(Token(0, i % 4));
+ input_values[0].emplace_back(0, i % 4);
}
std::vector<uint8_t> context_map;
diff --git a/third_party/jpeg-xl/lib/jxl/base/bits.h b/third_party/jpeg-xl/lib/jxl/base/bits.h
index 9f86118e72..a79fdc2c99 100644
--- a/third_party/jpeg-xl/lib/jxl/base/bits.h
+++ b/third_party/jpeg-xl/lib/jxl/base/bits.h
@@ -26,7 +26,8 @@ struct SizeTag {};
template <typename T>
constexpr bool IsSigned() {
- return T(0) > T(-1);
+ // TODO(eustas): remove dupes
+ return static_cast<T>(0) > static_cast<T>(-1);
}
// Undefined results for x == 0.
diff --git a/third_party/jpeg-xl/lib/jxl/base/byte_order.h b/third_party/jpeg-xl/lib/jxl/base/byte_order.h
index 8966834e08..cf8d7db082 100644
--- a/third_party/jpeg-xl/lib/jxl/base/byte_order.h
+++ b/third_party/jpeg-xl/lib/jxl/base/byte_order.h
@@ -237,22 +237,22 @@ struct OrderLE {};
// Wrappers for calling from generic code.
static JXL_INLINE void Store16(OrderBE /*tag*/, const uint32_t native,
uint8_t* p) {
- return StoreBE16(native, p);
+ StoreBE16(native, p);
}
static JXL_INLINE void Store16(OrderLE /*tag*/, const uint32_t native,
uint8_t* p) {
- return StoreLE16(native, p);
+ StoreLE16(native, p);
}
static JXL_INLINE void Store32(OrderBE /*tag*/, const uint32_t native,
uint8_t* p) {
- return StoreBE32(native, p);
+ StoreBE32(native, p);
}
static JXL_INLINE void Store32(OrderLE /*tag*/, const uint32_t native,
uint8_t* p) {
- return StoreLE32(native, p);
+ StoreLE32(native, p);
}
static JXL_INLINE uint32_t Load16(OrderBE /*tag*/, const uint8_t* p) {
diff --git a/third_party/jpeg-xl/lib/jxl/base/common.h b/third_party/jpeg-xl/lib/jxl/base/common.h
index b7fe6ab0bc..0893ef26b5 100644
--- a/third_party/jpeg-xl/lib/jxl/base/common.h
+++ b/third_party/jpeg-xl/lib/jxl/base/common.h
@@ -8,11 +8,13 @@
// Shared constants and helper functions.
+#include <array>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <memory>
#include <string>
+#include <type_traits>
#include "lib/jxl/base/compiler_specific.h"
@@ -22,11 +24,11 @@ namespace jxl {
constexpr size_t kBitsPerByte = 8; // more clear than CHAR_BIT
constexpr inline size_t RoundUpBitsToByteMultiple(size_t bits) {
- return (bits + 7) & ~size_t(7);
+ return (bits + 7) & ~static_cast<size_t>(7);
}
constexpr inline size_t RoundUpToBlockDim(size_t dim) {
- return (dim + 7) & ~size_t(7);
+ return (dim + 7) & ~static_cast<size_t>(7);
}
static inline bool JXL_MAYBE_UNUSED SafeAdd(const uint64_t a, const uint64_t b,
@@ -68,6 +70,37 @@ std::unique_ptr<T> make_unique(Args&&... args) {
using std::make_unique;
#endif
+typedef std::array<float, 3> Color;
+
+// Backported std::experimental::to_array
+
+template <typename T>
+using remove_cv_t = typename std::remove_cv<T>::type;
+
+template <size_t... I>
+struct index_sequence {};
+
+template <size_t N, size_t... I>
+struct make_index_sequence : make_index_sequence<N - 1, N - 1, I...> {};
+
+template <size_t... I>
+struct make_index_sequence<0, I...> : index_sequence<I...> {};
+
+namespace detail {
+
+template <typename T, size_t N, size_t... I>
+constexpr auto to_array(T (&&arr)[N], index_sequence<I...> _)
+ -> std::array<remove_cv_t<T>, N> {
+ return {{std::move(arr[I])...}};
+}
+
+} // namespace detail
+
+template <typename T, size_t N>
+constexpr auto to_array(T (&&arr)[N]) -> std::array<remove_cv_t<T>, N> {
+ return detail::to_array(std::move(arr), make_index_sequence<N>());
+}
+
template <typename T>
JXL_INLINE T Clamp1(T val, T low, T hi) {
return val < low ? low : val > hi ? hi : val;
@@ -77,10 +110,10 @@ JXL_INLINE T Clamp1(T val, T low, T hi) {
template <typename T>
std::string ToString(T n) {
char data[32] = {};
- if (T(0.1) != T(0)) {
+ if (std::is_floating_point<T>::value) {
// float
snprintf(data, sizeof(data), "%g", static_cast<double>(n));
- } else if (T(-1) > T(0)) {
+ } else if (std::is_unsigned<T>::value) {
// unsigned
snprintf(data, sizeof(data), "%llu", static_cast<unsigned long long>(n));
} else {
@@ -90,6 +123,9 @@ std::string ToString(T n) {
return data;
}
+#define JXL_JOIN(x, y) JXL_DO_JOIN(x, y)
+#define JXL_DO_JOIN(x, y) x##y
+
} // namespace jxl
#endif // LIB_JXL_BASE_COMMON_H_
diff --git a/third_party/jpeg-xl/lib/jxl/base/exif.h b/third_party/jpeg-xl/lib/jxl/base/exif.h
index 2caafddc04..a3574a16ff 100644
--- a/third_party/jpeg-xl/lib/jxl/base/exif.h
+++ b/third_party/jpeg-xl/lib/jxl/base/exif.h
@@ -79,7 +79,6 @@ JXL_INLINE void InterpretExif(const std::vector<uint8_t>& exif,
uint32_t count = (bigendian ? LoadBE32(t) : LoadLE32(t));
t += 4;
uint16_t value = (bigendian ? LoadBE16(t) : LoadLE16(t));
- t += 4;
if (type == 3 && count == 1 && value >= 1 && value <= 8) {
*orientation = static_cast<JxlOrientation>(value);
}
diff --git a/third_party/jpeg-xl/lib/jxl/base/float.h b/third_party/jpeg-xl/lib/jxl/base/float.h
index 00e112bb34..0f5b3b1f3a 100644
--- a/third_party/jpeg-xl/lib/jxl/base/float.h
+++ b/third_party/jpeg-xl/lib/jxl/base/float.h
@@ -17,9 +17,9 @@
namespace jxl {
-namespace {
+namespace detail {
// Based on highway scalar implementation, for testing
-float LoadFloat16(uint16_t bits16) {
+static JXL_INLINE float LoadFloat16(uint16_t bits16) {
const uint32_t sign = bits16 >> 15;
const uint32_t biased_exp = (bits16 >> 10) & 0x1F;
const uint32_t mantissa = bits16 & 0x3FF;
@@ -40,7 +40,7 @@ float LoadFloat16(uint16_t bits16) {
memcpy(&result, &bits32, 4);
return result;
}
-} // namespace
+} // namespace detail
template <typename SaveFloatAtFn>
static Status JXL_INLINE LoadFloatRow(const uint8_t* src, size_t count,
@@ -83,11 +83,11 @@ static Status JXL_INLINE LoadFloatRow(const uint8_t* src, size_t count,
case JXL_TYPE_FLOAT16:
if (little_endian) {
for (size_t i = 0; i < count; ++i) {
- callback(i, LoadFloat16(LoadLE16(src + stride * i)));
+ callback(i, detail::LoadFloat16(LoadLE16(src + stride * i)));
}
} else {
for (size_t i = 0; i < count; ++i) {
- callback(i, LoadFloat16(LoadBE16(src + stride * i)));
+ callback(i, detail::LoadFloat16(LoadBE16(src + stride * i)));
}
}
return true;
diff --git a/third_party/jpeg-xl/lib/jxl/base/matrix_ops.h b/third_party/jpeg-xl/lib/jxl/base/matrix_ops.h
index 1a969bd4f0..cde6a64b1e 100644
--- a/third_party/jpeg-xl/lib/jxl/base/matrix_ops.h
+++ b/third_party/jpeg-xl/lib/jxl/base/matrix_ops.h
@@ -8,6 +8,7 @@
// 3x3 matrix operations.
+#include <array>
#include <cmath> // abs
#include <cstddef>
@@ -15,66 +16,67 @@
namespace jxl {
+typedef std::array<float, 3> Vector3;
+typedef std::array<double, 3> Vector3d;
+typedef std::array<Vector3, 3> Matrix3x3;
+typedef std::array<Vector3d, 3> Matrix3x3d;
+
// Computes C = A * B, where A, B, C are 3x3 matrices.
-template <typename T>
-void Mul3x3Matrix(const T* a, const T* b, T* c) {
- alignas(16) T temp[3]; // For transposed column
+template <typename Matrix>
+void Mul3x3Matrix(const Matrix& a, const Matrix& b, Matrix& c) {
for (size_t x = 0; x < 3; x++) {
- for (size_t z = 0; z < 3; z++) {
- temp[z] = b[z * 3 + x];
- }
+ alignas(16) Vector3d temp{b[0][x], b[1][x], b[2][x]}; // transpose
for (size_t y = 0; y < 3; y++) {
- double e = 0;
- for (size_t z = 0; z < 3; z++) {
- e += a[y * 3 + z] * temp[z];
- }
- c[y * 3 + x] = e;
+ c[y][x] = a[y][0] * temp[0] + a[y][1] * temp[1] + a[y][2] * temp[2];
}
}
}
// Computes C = A * B, where A is 3x3 matrix and B is vector.
-template <typename T>
-void Mul3x3Vector(const T* a, const T* b, T* c) {
+template <typename Matrix, typename Vector>
+void Mul3x3Vector(const Matrix& a, const Vector& b, Vector& c) {
for (size_t y = 0; y < 3; y++) {
double e = 0;
for (size_t x = 0; x < 3; x++) {
- e += a[y * 3 + x] * b[x];
+ e += a[y][x] * b[x];
}
c[y] = e;
}
}
// Inverts a 3x3 matrix in place.
-template <typename T>
-Status Inv3x3Matrix(T* matrix) {
+template <typename Matrix>
+Status Inv3x3Matrix(Matrix& matrix) {
// Intermediate computation is done in double precision.
- double temp[9];
- temp[0] = static_cast<double>(matrix[4]) * matrix[8] -
- static_cast<double>(matrix[5]) * matrix[7];
- temp[1] = static_cast<double>(matrix[2]) * matrix[7] -
- static_cast<double>(matrix[1]) * matrix[8];
- temp[2] = static_cast<double>(matrix[1]) * matrix[5] -
- static_cast<double>(matrix[2]) * matrix[4];
- temp[3] = static_cast<double>(matrix[5]) * matrix[6] -
- static_cast<double>(matrix[3]) * matrix[8];
- temp[4] = static_cast<double>(matrix[0]) * matrix[8] -
- static_cast<double>(matrix[2]) * matrix[6];
- temp[5] = static_cast<double>(matrix[2]) * matrix[3] -
- static_cast<double>(matrix[0]) * matrix[5];
- temp[6] = static_cast<double>(matrix[3]) * matrix[7] -
- static_cast<double>(matrix[4]) * matrix[6];
- temp[7] = static_cast<double>(matrix[1]) * matrix[6] -
- static_cast<double>(matrix[0]) * matrix[7];
- temp[8] = static_cast<double>(matrix[0]) * matrix[4] -
- static_cast<double>(matrix[1]) * matrix[3];
- double det = matrix[0] * temp[0] + matrix[1] * temp[3] + matrix[2] * temp[6];
+ Matrix3x3d temp;
+ temp[0][0] = static_cast<double>(matrix[1][1]) * matrix[2][2] -
+ static_cast<double>(matrix[1][2]) * matrix[2][1];
+ temp[0][1] = static_cast<double>(matrix[0][2]) * matrix[2][1] -
+ static_cast<double>(matrix[0][1]) * matrix[2][2];
+ temp[0][2] = static_cast<double>(matrix[0][1]) * matrix[1][2] -
+ static_cast<double>(matrix[0][2]) * matrix[1][1];
+ temp[1][0] = static_cast<double>(matrix[1][2]) * matrix[2][0] -
+ static_cast<double>(matrix[1][0]) * matrix[2][2];
+ temp[1][1] = static_cast<double>(matrix[0][0]) * matrix[2][2] -
+ static_cast<double>(matrix[0][2]) * matrix[2][0];
+ temp[1][2] = static_cast<double>(matrix[0][2]) * matrix[1][0] -
+ static_cast<double>(matrix[0][0]) * matrix[1][2];
+ temp[2][0] = static_cast<double>(matrix[1][0]) * matrix[2][1] -
+ static_cast<double>(matrix[1][1]) * matrix[2][0];
+ temp[2][1] = static_cast<double>(matrix[0][1]) * matrix[2][0] -
+ static_cast<double>(matrix[0][0]) * matrix[2][1];
+ temp[2][2] = static_cast<double>(matrix[0][0]) * matrix[1][1] -
+ static_cast<double>(matrix[0][1]) * matrix[1][0];
+ double det = matrix[0][0] * temp[0][0] + matrix[0][1] * temp[1][0] +
+ matrix[0][2] * temp[2][0];
if (std::abs(det) < 1e-10) {
return JXL_FAILURE("Matrix determinant is too close to 0");
}
double idet = 1.0 / det;
- for (size_t i = 0; i < 9; i++) {
- matrix[i] = temp[i] * idet;
+ for (size_t j = 0; j < 3; j++) {
+ for (size_t i = 0; i < 3; i++) {
+ matrix[j][i] = temp[j][i] * idet;
+ }
}
return true;
}
diff --git a/third_party/jpeg-xl/lib/jxl/base/override.h b/third_party/jpeg-xl/lib/jxl/base/override.h
index 1f8b657974..da070e6e62 100644
--- a/third_party/jpeg-xl/lib/jxl/base/override.h
+++ b/third_party/jpeg-xl/lib/jxl/base/override.h
@@ -6,13 +6,15 @@
#ifndef LIB_JXL_BASE_OVERRIDE_H_
#define LIB_JXL_BASE_OVERRIDE_H_
+#include <cstdint>
+
// 'Trool' for command line arguments: force enable/disable, or use default.
namespace jxl {
// No effect if kDefault, otherwise forces a feature (typically a FrameHeader
// flag) on or off.
-enum class Override : int { kOn = 1, kOff = 0, kDefault = -1 };
+enum class Override : int8_t { kOn = 1, kOff = 0, kDefault = -1 };
static inline Override OverrideFromBool(bool flag) {
return flag ? Override::kOn : Override::kOff;
diff --git a/third_party/jpeg-xl/lib/jxl/base/rational_polynomial-inl.h b/third_party/jpeg-xl/lib/jxl/base/rational_polynomial-inl.h
index e073937675..7a89c0bac1 100644
--- a/third_party/jpeg-xl/lib/jxl/base/rational_polynomial-inl.h
+++ b/third_party/jpeg-xl/lib/jxl/base/rational_polynomial-inl.h
@@ -13,6 +13,7 @@
#define LIB_JXL_BASE_RATIONAL_POLYNOMIAL_INL_H_
#endif
+#include <jxl/types.h>
#include <stddef.h>
#include <hwy/highway.h>
@@ -42,7 +43,7 @@ struct FastDivision<float, V> {
}
V operator()(const V n, const V d) const {
-#if 1 // Faster on SKX
+#if JXL_TRUE // Faster on SKX
return Div(n, d);
#else
return n * ReciprocalNR(d);
diff --git a/third_party/jpeg-xl/lib/jxl/base/scope_guard.h b/third_party/jpeg-xl/lib/jxl/base/scope_guard.h
index a18a44cb79..f060c5bc1d 100644
--- a/third_party/jpeg-xl/lib/jxl/base/scope_guard.h
+++ b/third_party/jpeg-xl/lib/jxl/base/scope_guard.h
@@ -24,8 +24,8 @@ class ScopeGuard {
}
template <typename CallbackParam>
- explicit ScopeGuard(CallbackParam &&callback)
- : callback_(std::forward<CallbackParam>(callback)), armed_(true) {}
+ ScopeGuard(CallbackParam &&callback, bool armed)
+ : callback_(std::forward<CallbackParam>(callback)), armed_(armed) {}
~ScopeGuard() {
if (armed_) callback_();
@@ -40,7 +40,7 @@ class ScopeGuard {
template <typename Callback>
ScopeGuard<Callback> MakeScopeGuard(Callback &&callback) {
- return ScopeGuard<Callback>{std::forward<Callback>(callback)};
+ return ScopeGuard<Callback>{std::forward<Callback>(callback), true};
}
} // namespace jxl
diff --git a/third_party/jpeg-xl/lib/jxl/base/span.h b/third_party/jpeg-xl/lib/jxl/base/span.h
index dc1c781b9d..ba09d62316 100644
--- a/third_party/jpeg-xl/lib/jxl/base/span.h
+++ b/third_party/jpeg-xl/lib/jxl/base/span.h
@@ -64,8 +64,8 @@ class Span {
// NCT == non-const-T; compiler will complain if NCT is not compatible with T.
template <typename NCT>
- void AppendTo(std::vector<NCT>* dst) const {
- dst->insert(dst->end(), begin(), end());
+ void AppendTo(std::vector<NCT>& dst) const {
+ dst.insert(dst.end(), begin(), end());
}
private:
diff --git a/third_party/jpeg-xl/lib/jxl/base/status.h b/third_party/jpeg-xl/lib/jxl/base/status.h
index b33bd64fc3..2e88ba68ae 100644
--- a/third_party/jpeg-xl/lib/jxl/base/status.h
+++ b/third_party/jpeg-xl/lib/jxl/base/status.h
@@ -16,6 +16,7 @@
#include <type_traits>
#include <utility>
+#include "lib/jxl/base/common.h"
#include "lib/jxl/base/compiler_specific.h"
#include "lib/jxl/base/sanitizer_definitions.h"
@@ -442,7 +443,7 @@ class JXL_MUST_USE_RESULT StatusOr {
#define JXL_ASSIGN_OR_RETURN(lhs, statusor) \
PRIVATE_JXL_ASSIGN_OR_RETURN_IMPL( \
- assign_or_return_temporary_variable##__LINE__, lhs, statusor)
+ JXL_JOIN(assign_or_return_temporary_variable, __LINE__), lhs, statusor)
// NOLINTBEGIN(bugprone-macro-parentheses)
#define PRIVATE_JXL_ASSIGN_OR_RETURN_IMPL(name, lhs, statusor) \
@@ -451,6 +452,18 @@ class JXL_MUST_USE_RESULT StatusOr {
lhs = std::move(name).value();
// NOLINTEND(bugprone-macro-parentheses)
+// NB: do not use outside of tests / tools!!!
+#define JXL_ASSIGN_OR_DIE(lhs, statusor) \
+ PRIVATE_JXL_ASSIGN_OR_DIE_IMPL( \
+ JXL_JOIN(assign_or_die_temporary_variable, __LINE__), lhs, statusor)
+
+// NOLINTBEGIN(bugprone-macro-parentheses)
+#define PRIVATE_JXL_ASSIGN_OR_DIE_IMPL(name, lhs, statusor) \
+ auto name = statusor; \
+ if (!name.ok()) jxl::Abort(); \
+ lhs = std::move(name).value();
+// NOLINTEND(bugprone-macro-parentheses)
+
} // namespace jxl
#endif // LIB_JXL_BASE_STATUS_H_
diff --git a/third_party/jpeg-xl/lib/jxl/bits_test.cc b/third_party/jpeg-xl/lib/jxl/bits_test.cc
index bd7aa548c8..45db8c0d6e 100644
--- a/third_party/jpeg-xl/lib/jxl/bits_test.cc
+++ b/third_party/jpeg-xl/lib/jxl/bits_test.cc
@@ -38,7 +38,8 @@ TEST(BitsTest, TestFloorLog2) {
const size_t expected[7] = {0, 1, 1, 2, 2, 2, 2};
for (uint32_t i = 1; i <= 7; ++i) {
EXPECT_EQ(expected[i - 1], FloorLog2Nonzero(i)) << " " << i;
- EXPECT_EQ(expected[i - 1], FloorLog2Nonzero(uint64_t(i))) << " " << i;
+ EXPECT_EQ(expected[i - 1], FloorLog2Nonzero(static_cast<uint64_t>(i)))
+ << " " << i;
}
EXPECT_EQ(11u, FloorLog2Nonzero(0x00000fffu)); // 4095
@@ -63,7 +64,8 @@ TEST(BitsTest, TestCeilLog2) {
const size_t expected[7] = {0, 1, 2, 2, 3, 3, 3};
for (uint32_t i = 1; i <= 7; ++i) {
EXPECT_EQ(expected[i - 1], CeilLog2Nonzero(i)) << " " << i;
- EXPECT_EQ(expected[i - 1], CeilLog2Nonzero(uint64_t(i))) << " " << i;
+ EXPECT_EQ(expected[i - 1], CeilLog2Nonzero(static_cast<uint64_t>(i)))
+ << " " << i;
}
EXPECT_EQ(12u, CeilLog2Nonzero(0x00000fffu)); // 4095
diff --git a/third_party/jpeg-xl/lib/jxl/blending.cc b/third_party/jpeg-xl/lib/jxl/blending.cc
index ccb168ee45..7575ec6e4a 100644
--- a/third_party/jpeg-xl/lib/jxl/blending.cc
+++ b/third_party/jpeg-xl/lib/jxl/blending.cc
@@ -6,7 +6,6 @@
#include "lib/jxl/blending.h"
#include "lib/jxl/alpha.h"
-#include "lib/jxl/image_ops.h"
namespace jxl {
@@ -29,11 +28,11 @@ bool NeedsBlending(const FrameHeader& frame_header) {
return true;
}
-void PerformBlending(const float* const* bg, const float* const* fg,
- float* const* out, size_t x0, size_t xsize,
- const PatchBlending& color_blending,
- const PatchBlending* ec_blending,
- const std::vector<ExtraChannelInfo>& extra_channel_info) {
+Status PerformBlending(
+ const float* const* bg, const float* const* fg, float* const* out,
+ size_t x0, size_t xsize, const PatchBlending& color_blending,
+ const PatchBlending* ec_blending,
+ const std::vector<ExtraChannelInfo>& extra_channel_info) {
bool has_alpha = false;
size_t num_ec = extra_channel_info.size();
for (size_t i = 0; i < num_ec; i++) {
@@ -42,7 +41,7 @@ void PerformBlending(const float* const* bg, const float* const* fg,
break;
}
}
- ImageF tmp(xsize, 3 + num_ec);
+ JXL_ASSIGN_OR_RETURN(ImageF tmp, ImageF::Create(xsize, 3 + num_ec));
// Blend extra channels first so that we use the pre-blending alpha.
for (size_t i = 0; i < num_ec; i++) {
if (ec_blending[i].mode == PatchBlendMode::kAdd) {
@@ -146,6 +145,7 @@ void PerformBlending(const float* const* bg, const float* const* fg,
for (size_t i = 0; i < 3 + num_ec; i++) {
if (xsize != 0) memcpy(out[i] + x0, tmp.Row(i), xsize * sizeof(**out));
}
+ return true;
}
} // namespace jxl
diff --git a/third_party/jpeg-xl/lib/jxl/blending.h b/third_party/jpeg-xl/lib/jxl/blending.h
index 3f23297f1d..94bead8dd6 100644
--- a/third_party/jpeg-xl/lib/jxl/blending.h
+++ b/third_party/jpeg-xl/lib/jxl/blending.h
@@ -16,11 +16,11 @@ namespace jxl {
bool NeedsBlending(const FrameHeader& frame_header);
-void PerformBlending(const float* const* bg, const float* const* fg,
- float* const* out, size_t x0, size_t xsize,
- const PatchBlending& color_blending,
- const PatchBlending* ec_blending,
- const std::vector<ExtraChannelInfo>& extra_channel_info);
+Status PerformBlending(const float* const* bg, const float* const* fg,
+ float* const* out, size_t x0, size_t xsize,
+ const PatchBlending& color_blending,
+ const PatchBlending* ec_blending,
+ const std::vector<ExtraChannelInfo>& extra_channel_info);
} // namespace jxl
diff --git a/third_party/jpeg-xl/lib/jxl/blending_test.cc b/third_party/jpeg-xl/lib/jxl/blending_test.cc
index c34ab5c7ca..c363358592 100644
--- a/third_party/jpeg-xl/lib/jxl/blending_test.cc
+++ b/third_party/jpeg-xl/lib/jxl/blending_test.cc
@@ -20,8 +20,6 @@
namespace jxl {
namespace {
-using ::testing::SizeIs;
-
TEST(BlendingTest, Crops) {
const std::vector<uint8_t> compressed =
jxl::test::ReadTestData("jxl/blending/cropped_traffic_light.jxl");
@@ -30,7 +28,7 @@ TEST(BlendingTest, Crops) {
extras::PackedPixelFile decoded;
ASSERT_TRUE(DecodeImageJXL(compressed.data(), compressed.size(), dparams,
/*decoded_bytes=*/nullptr, &decoded));
- ASSERT_THAT(decoded.frames, SizeIs(4));
+ ASSERT_EQ(decoded.frames.size(), 4);
int i = 0;
for (auto&& decoded_frame : decoded.frames) {
@@ -40,8 +38,10 @@ TEST(BlendingTest, Crops) {
jxl::test::ReadTestData(filename.str());
extras::PackedPixelFile decoded_frame_ppf;
decoded_frame_ppf.info = decoded.info;
- decoded_frame_ppf.icc = decoded.icc;
+ decoded_frame_ppf.primary_color_representation =
+ decoded.primary_color_representation;
decoded_frame_ppf.color_encoding = decoded.color_encoding;
+ decoded_frame_ppf.icc = decoded.icc;
decoded_frame_ppf.extra_channels_info = decoded.extra_channels_info;
decoded_frame_ppf.frames.emplace_back(std::move(decoded_frame));
extras::PackedPixelFile expected_frame_ppf;
diff --git a/third_party/jpeg-xl/lib/jxl/box_content_decoder.cc b/third_party/jpeg-xl/lib/jxl/box_content_decoder.cc
index c4cba3a31a..c7d87f4ca0 100644
--- a/third_party/jpeg-xl/lib/jxl/box_content_decoder.cc
+++ b/third_party/jpeg-xl/lib/jxl/box_content_decoder.cc
@@ -9,7 +9,7 @@
namespace jxl {
-JxlBoxContentDecoder::JxlBoxContentDecoder() {}
+JxlBoxContentDecoder::JxlBoxContentDecoder() = default;
JxlBoxContentDecoder::~JxlBoxContentDecoder() {
if (brotli_dec) {
diff --git a/third_party/jpeg-xl/lib/jxl/butteraugli/butteraugli.cc b/third_party/jpeg-xl/lib/jxl/butteraugli/butteraugli.cc
index 66dde9afb1..2e06d79eba 100644
--- a/third_party/jpeg-xl/lib/jxl/butteraugli/butteraugli.cc
+++ b/third_party/jpeg-xl/lib/jxl/butteraugli/butteraugli.cc
@@ -28,11 +28,12 @@
#include <string.h>
#include <algorithm>
-#include <array>
#include <cmath>
-#include <new>
+#include <memory>
#include <vector>
+#include "lib/jxl/image.h"
+
#undef HWY_TARGET_INCLUDE
#define HWY_TARGET_INCLUDE "lib/jxl/butteraugli/butteraugli.cc"
#include <hwy/foreach_target.h>
@@ -222,8 +223,8 @@ void ConvolutionWithTranspose(const ImageF& in,
// We retain a special case for 5x5 kernels (even faster than gauss_blur),
// optionally use gauss_blur followed by fixup of the borders for large images,
// or fall back to the previous truncated FIR followed by a transpose.
-void Blur(const ImageF& in, float sigma, const ButteraugliParams& params,
- BlurTemp* temp, ImageF* out) {
+Status Blur(const ImageF& in, float sigma, const ButteraugliParams& params,
+ BlurTemp* temp, ImageF* out) {
std::vector<float> kernel = ComputeKernel(sigma);
// Separable5 does an in-place convolution, so this fast path is not safe if
// in aliases out.
@@ -241,12 +242,14 @@ void Blur(const ImageF& in, float sigma, const ButteraugliParams& params,
{HWY_REP4(w0), HWY_REP4(w1), HWY_REP4(w2)},
};
Separable5(in, Rect(in), weights, /*pool=*/nullptr, out);
- return;
+ return true;
}
- ImageF* JXL_RESTRICT temp_t = temp->GetTransposed(in);
+ ImageF* temp_t;
+ JXL_RETURN_IF_ERROR(temp->GetTransposed(in, &temp_t));
ConvolutionWithTranspose(in, kernel, temp_t);
ConvolutionWithTranspose(*temp_t, kernel, out);
+ return true;
}
// Allows PaddedMaltaUnit to call either function via overloading.
@@ -386,29 +389,32 @@ void Subtract(const ImageF& a, const ImageF& b, ImageF* c) {
}
}
-void SeparateLFAndMF(const ButteraugliParams& params, const Image3F& xyb,
- Image3F* lf, Image3F* mf, BlurTemp* blur_temp) {
+Status SeparateLFAndMF(const ButteraugliParams& params, const Image3F& xyb,
+ Image3F* lf, Image3F* mf, BlurTemp* blur_temp) {
static const double kSigmaLf = 7.15593339443;
for (int i = 0; i < 3; ++i) {
// Extract lf ...
- Blur(xyb.Plane(i), kSigmaLf, params, blur_temp, &lf->Plane(i));
+ JXL_RETURN_IF_ERROR(
+ Blur(xyb.Plane(i), kSigmaLf, params, blur_temp, &lf->Plane(i)));
// ... and keep everything else in mf.
Subtract(xyb.Plane(i), lf->Plane(i), &mf->Plane(i));
}
XybLowFreqToVals(lf);
+ return true;
}
-void SeparateMFAndHF(const ButteraugliParams& params, Image3F* mf, ImageF* hf,
- BlurTemp* blur_temp) {
+Status SeparateMFAndHF(const ButteraugliParams& params, Image3F* mf, ImageF* hf,
+ BlurTemp* blur_temp) {
const HWY_FULL(float) d;
static const double kSigmaHf = 3.22489901262;
const size_t xsize = mf->xsize();
const size_t ysize = mf->ysize();
- hf[0] = ImageF(xsize, ysize);
- hf[1] = ImageF(xsize, ysize);
+ JXL_ASSIGN_OR_RETURN(hf[0], ImageF::Create(xsize, ysize));
+ JXL_ASSIGN_OR_RETURN(hf[1], ImageF::Create(xsize, ysize));
for (int i = 0; i < 3; ++i) {
if (i == 2) {
- Blur(mf->Plane(i), kSigmaHf, params, blur_temp, &mf->Plane(i));
+ JXL_RETURN_IF_ERROR(
+ Blur(mf->Plane(i), kSigmaHf, params, blur_temp, &mf->Plane(i)));
break;
}
for (size_t y = 0; y < ysize; ++y) {
@@ -418,7 +424,8 @@ void SeparateMFAndHF(const ButteraugliParams& params, Image3F* mf, ImageF* hf,
Store(Load(d, row_mf + x), d, row_hf + x);
}
}
- Blur(mf->Plane(i), kSigmaHf, params, blur_temp, &mf->Plane(i));
+ JXL_RETURN_IF_ERROR(
+ Blur(mf->Plane(i), kSigmaHf, params, blur_temp, &mf->Plane(i)));
static const double kRemoveMfRange = 0.29;
static const double kAddMfRange = 0.1;
if (i == 0) {
@@ -450,16 +457,17 @@ void SeparateMFAndHF(const ButteraugliParams& params, Image3F* mf, ImageF* hf,
}
// Suppress red-green by intensity change in the high freq channels.
SuppressXByY(hf[1], &hf[0]);
+ return true;
}
-void SeparateHFAndUHF(const ButteraugliParams& params, ImageF* hf, ImageF* uhf,
- BlurTemp* blur_temp) {
+Status SeparateHFAndUHF(const ButteraugliParams& params, ImageF* hf,
+ ImageF* uhf, BlurTemp* blur_temp) {
const HWY_FULL(float) d;
const size_t xsize = hf[0].xsize();
const size_t ysize = hf[0].ysize();
static const double kSigmaUhf = 1.56416327805;
- uhf[0] = ImageF(xsize, ysize);
- uhf[1] = ImageF(xsize, ysize);
+ JXL_ASSIGN_OR_RETURN(uhf[0], ImageF::Create(xsize, ysize));
+ JXL_ASSIGN_OR_RETURN(uhf[1], ImageF::Create(xsize, ysize));
for (int i = 0; i < 2; ++i) {
// Divide hf into hf and uhf.
for (size_t y = 0; y < ysize; ++y) {
@@ -469,7 +477,7 @@ void SeparateHFAndUHF(const ButteraugliParams& params, ImageF* hf, ImageF* uhf,
row_uhf[x] = row_hf[x];
}
}
- Blur(hf[i], kSigmaUhf, params, blur_temp, &hf[i]);
+ JXL_RETURN_IF_ERROR(Blur(hf[i], kSigmaUhf, params, blur_temp, &hf[i]));
static const double kRemoveHfRange = 1.5;
static const double kAddHfRange = 0.132;
static const double kRemoveUhfRange = 0.04;
@@ -510,6 +518,7 @@ void SeparateHFAndUHF(const ButteraugliParams& params, ImageF* hf, ImageF* uhf,
}
}
}
+ return true;
}
void DeallocateHFAndUHF(ImageF* hf, ImageF* uhf) {
@@ -519,15 +528,16 @@ void DeallocateHFAndUHF(ImageF* hf, ImageF* uhf) {
}
}
-static void SeparateFrequencies(size_t xsize, size_t ysize,
- const ButteraugliParams& params,
- BlurTemp* blur_temp, const Image3F& xyb,
- PsychoImage& ps) {
- ps.lf = Image3F(xyb.xsize(), xyb.ysize());
- ps.mf = Image3F(xyb.xsize(), xyb.ysize());
- SeparateLFAndMF(params, xyb, &ps.lf, &ps.mf, blur_temp);
- SeparateMFAndHF(params, &ps.mf, &ps.hf[0], blur_temp);
- SeparateHFAndUHF(params, &ps.hf[0], &ps.uhf[0], blur_temp);
+Status SeparateFrequencies(size_t xsize, size_t ysize,
+ const ButteraugliParams& params, BlurTemp* blur_temp,
+ const Image3F& xyb, PsychoImage& ps) {
+ JXL_ASSIGN_OR_RETURN(ps.lf, Image3F::Create(xyb.xsize(), xyb.ysize()));
+ JXL_ASSIGN_OR_RETURN(ps.mf, Image3F::Create(xyb.xsize(), xyb.ysize()));
+ JXL_RETURN_IF_ERROR(SeparateLFAndMF(params, xyb, &ps.lf, &ps.mf, blur_temp));
+ JXL_RETURN_IF_ERROR(SeparateMFAndHF(params, &ps.mf, &ps.hf[0], blur_temp));
+ JXL_RETURN_IF_ERROR(
+ SeparateHFAndUHF(params, &ps.hf[0], &ps.uhf[0], blur_temp));
+ return true;
}
namespace {
@@ -1024,7 +1034,7 @@ static void MaltaDiffMapT(const Tag tag, const ImageF& lum0, const ImageF& lum1,
}
const HWY_FULL(float) df;
- const size_t aligned_x = std::max(size_t(4), Lanes(df));
+ const size_t aligned_x = std::max(static_cast<size_t>(4), Lanes(df));
const intptr_t stride = diffs->PixelsPerRow();
// Middle
@@ -1097,7 +1107,7 @@ void CombineChannelsForMasking(const ImageF* hf, const ImageF* uhf,
float xdiff = (row_x_uhf[x] + row_x_hf[x]) * muls[0];
float ydiff = row_y_uhf[x] * muls[1] + row_y_hf[x] * muls[2];
row[x] = xdiff * xdiff + ydiff * ydiff;
- row[x] = sqrt(row[x]);
+ row[x] = std::sqrt(row[x]);
}
}
}
@@ -1106,13 +1116,13 @@ void DiffPrecompute(const ImageF& xyb, float mul, float bias_arg, ImageF* out) {
const size_t xsize = xyb.xsize();
const size_t ysize = xyb.ysize();
const float bias = mul * bias_arg;
- const float sqrt_bias = sqrt(bias);
+ const float sqrt_bias = std::sqrt(bias);
for (size_t y = 0; y < ysize; ++y) {
const float* BUTTERAUGLI_RESTRICT row_in = xyb.Row(y);
float* BUTTERAUGLI_RESTRICT row_out = out->Row(y);
for (size_t x = 0; x < xsize; ++x) {
// kBias makes sqrt behave more linearly.
- row_out[x] = sqrt(mul * std::abs(row_in[x]) + bias) - sqrt_bias;
+ row_out[x] = std::sqrt(mul * std::abs(row_in[x]) + bias) - sqrt_bias;
}
}
}
@@ -1188,25 +1198,25 @@ void FuzzyErosion(const ImageF& from, ImageF* to) {
// Compute values of local frequency and dc masking based on the activity
// in the two images. img_diff_ac may be null.
-void Mask(const ImageF& mask0, const ImageF& mask1,
- const ButteraugliParams& params, BlurTemp* blur_temp,
- ImageF* BUTTERAUGLI_RESTRICT mask,
- ImageF* BUTTERAUGLI_RESTRICT diff_ac) {
+Status Mask(const ImageF& mask0, const ImageF& mask1,
+ const ButteraugliParams& params, BlurTemp* blur_temp,
+ ImageF* BUTTERAUGLI_RESTRICT mask,
+ ImageF* BUTTERAUGLI_RESTRICT diff_ac) {
const size_t xsize = mask0.xsize();
const size_t ysize = mask0.ysize();
- *mask = ImageF(xsize, ysize);
+ JXL_ASSIGN_OR_RETURN(*mask, ImageF::Create(xsize, ysize));
static const float kMul = 6.19424080439;
static const float kBias = 12.61050594197;
static const float kRadius = 2.7;
- ImageF diff0(xsize, ysize);
- ImageF diff1(xsize, ysize);
- ImageF blurred0(xsize, ysize);
- ImageF blurred1(xsize, ysize);
+ JXL_ASSIGN_OR_RETURN(ImageF diff0, ImageF::Create(xsize, ysize));
+ JXL_ASSIGN_OR_RETURN(ImageF diff1, ImageF::Create(xsize, ysize));
+ JXL_ASSIGN_OR_RETURN(ImageF blurred0, ImageF::Create(xsize, ysize));
+ JXL_ASSIGN_OR_RETURN(ImageF blurred1, ImageF::Create(xsize, ysize));
DiffPrecompute(mask0, kMul, kBias, &diff0);
DiffPrecompute(mask1, kMul, kBias, &diff1);
- Blur(diff0, kRadius, params, blur_temp, &blurred0);
+ JXL_RETURN_IF_ERROR(Blur(diff0, kRadius, params, blur_temp, &blurred0));
FuzzyErosion(blurred0, &diff0);
- Blur(diff1, kRadius, params, blur_temp, &blurred1);
+ JXL_RETURN_IF_ERROR(Blur(diff1, kRadius, params, blur_temp, &blurred1));
for (size_t y = 0; y < ysize; ++y) {
for (size_t x = 0; x < xsize; ++x) {
mask->Row(y)[x] = diff0.Row(y)[x];
@@ -1217,19 +1227,21 @@ void Mask(const ImageF& mask0, const ImageF& mask1,
}
}
}
+ return true;
}
// `diff_ac` may be null.
-void MaskPsychoImage(const PsychoImage& pi0, const PsychoImage& pi1,
- const size_t xsize, const size_t ysize,
- const ButteraugliParams& params, BlurTemp* blur_temp,
- ImageF* BUTTERAUGLI_RESTRICT mask,
- ImageF* BUTTERAUGLI_RESTRICT diff_ac) {
- ImageF mask0(xsize, ysize);
- ImageF mask1(xsize, ysize);
+Status MaskPsychoImage(const PsychoImage& pi0, const PsychoImage& pi1,
+ const size_t xsize, const size_t ysize,
+ const ButteraugliParams& params, BlurTemp* blur_temp,
+ ImageF* BUTTERAUGLI_RESTRICT mask,
+ ImageF* BUTTERAUGLI_RESTRICT diff_ac) {
+ JXL_ASSIGN_OR_RETURN(ImageF mask0, ImageF::Create(xsize, ysize));
+ JXL_ASSIGN_OR_RETURN(ImageF mask1, ImageF::Create(xsize, ysize));
CombineChannelsForMasking(&pi0.hf[0], &pi0.uhf[0], &mask0);
CombineChannelsForMasking(&pi1.hf[0], &pi1.uhf[0], &mask1);
- Mask(mask0, mask1, params, blur_temp, mask, diff_ac);
+ JXL_RETURN_IF_ERROR(Mask(mask0, mask1, params, blur_temp, mask, diff_ac));
+ return true;
}
double MaskY(double delta) {
@@ -1275,8 +1287,8 @@ void CombineChannelsToDiffmap(const ImageF& mask, const Image3F& block_diff_dc,
}
diff_ac[0] *= xmul;
diff_dc[0] *= xmul;
- row_out[x] =
- sqrt(MaskColor(diff_dc, dc_maskval) + MaskColor(diff_ac, maskval));
+ row_out[x] = std::sqrt(MaskColor(diff_dc, dc_maskval) +
+ MaskColor(diff_ac, maskval));
}
}
}
@@ -1430,12 +1442,15 @@ BUTTERAUGLI_INLINE void OpsinAbsorbance(const DF df, const V& in0, const V& in1,
}
// `blurred` is a temporary image used inside this function and not returned.
-void OpsinDynamicsImage(const Image3F& rgb, const ButteraugliParams& params,
- Image3F* blurred, BlurTemp* blur_temp, Image3F* xyb) {
+Status OpsinDynamicsImage(const Image3F& rgb, const ButteraugliParams& params,
+ Image3F* blurred, BlurTemp* blur_temp, Image3F* xyb) {
const double kSigma = 1.2;
- Blur(rgb.Plane(0), kSigma, params, blur_temp, &blurred->Plane(0));
- Blur(rgb.Plane(1), kSigma, params, blur_temp, &blurred->Plane(1));
- Blur(rgb.Plane(2), kSigma, params, blur_temp, &blurred->Plane(2));
+ JXL_RETURN_IF_ERROR(
+ Blur(rgb.Plane(0), kSigma, params, blur_temp, &blurred->Plane(0)));
+ JXL_RETURN_IF_ERROR(
+ Blur(rgb.Plane(1), kSigma, params, blur_temp, &blurred->Plane(1)));
+ JXL_RETURN_IF_ERROR(
+ Blur(rgb.Plane(2), kSigma, params, blur_temp, &blurred->Plane(2)));
const HWY_FULL(float) df;
const auto intensity_target_multiplier = Set(df, params.intensity_target);
for (size_t y = 0; y < rgb.ysize(); ++y) {
@@ -1497,31 +1512,36 @@ void OpsinDynamicsImage(const Image3F& rgb, const ButteraugliParams& params,
Store(cur_mixed2, df, row_out_b + x);
}
}
+ return true;
}
-void ButteraugliDiffmapInPlace(Image3F& image0, Image3F& image1,
- const ButteraugliParams& params,
- ImageF& diffmap) {
+Status ButteraugliDiffmapInPlace(Image3F& image0, Image3F& image1,
+ const ButteraugliParams& params,
+ ImageF& diffmap) {
// image0 and image1 are in linear sRGB color space
const size_t xsize = image0.xsize();
const size_t ysize = image0.ysize();
BlurTemp blur_temp;
{
// Convert image0 and image1 to XYB in-place
- Image3F temp(xsize, ysize);
- OpsinDynamicsImage(image0, params, &temp, &blur_temp, &image0);
- OpsinDynamicsImage(image1, params, &temp, &blur_temp, &image1);
+ JXL_ASSIGN_OR_RETURN(Image3F temp, Image3F::Create(xsize, ysize));
+ JXL_RETURN_IF_ERROR(
+ OpsinDynamicsImage(image0, params, &temp, &blur_temp, &image0));
+ JXL_RETURN_IF_ERROR(
+ OpsinDynamicsImage(image1, params, &temp, &blur_temp, &image1));
}
// image0 and image1 are in XYB color space
- ImageF block_diff_dc(xsize, ysize);
+ JXL_ASSIGN_OR_RETURN(ImageF block_diff_dc, ImageF::Create(xsize, ysize));
ZeroFillImage(&block_diff_dc);
{
// separate out LF components from image0 and image1 and compute the dc
// diff image from them
- Image3F lf0 = Image3F(xsize, ysize);
- Image3F lf1 = Image3F(xsize, ysize);
- SeparateLFAndMF(params, image0, &lf0, &image0, &blur_temp);
- SeparateLFAndMF(params, image1, &lf1, &image1, &blur_temp);
+ JXL_ASSIGN_OR_RETURN(Image3F lf0, Image3F::Create(xsize, ysize));
+ JXL_ASSIGN_OR_RETURN(Image3F lf1, Image3F::Create(xsize, ysize));
+ JXL_RETURN_IF_ERROR(
+ SeparateLFAndMF(params, image0, &lf0, &image0, &blur_temp));
+ JXL_RETURN_IF_ERROR(
+ SeparateLFAndMF(params, image1, &lf1, &image1, &blur_temp));
for (size_t c = 0; c < 3; ++c) {
L2Diff(lf0.Plane(c), lf1.Plane(c), wmul[6 + c], &block_diff_dc);
}
@@ -1529,15 +1549,15 @@ void ButteraugliDiffmapInPlace(Image3F& image0, Image3F& image1,
// image0 and image1 are MF residuals (before blurring) in XYB color space
ImageF hf0[2];
ImageF hf1[2];
- SeparateMFAndHF(params, &image0, &hf0[0], &blur_temp);
- SeparateMFAndHF(params, &image1, &hf1[0], &blur_temp);
+ JXL_RETURN_IF_ERROR(SeparateMFAndHF(params, &image0, &hf0[0], &blur_temp));
+ JXL_RETURN_IF_ERROR(SeparateMFAndHF(params, &image1, &hf1[0], &blur_temp));
// image0 and image1 are MF-images in XYB color space
- ImageF block_diff_ac(xsize, ysize);
+ JXL_ASSIGN_OR_RETURN(ImageF block_diff_ac, ImageF::Create(xsize, ysize));
ZeroFillImage(&block_diff_ac);
// start accumulating ac diff image from MF images
{
- ImageF diffs(xsize, ysize);
+ JXL_ASSIGN_OR_RETURN(ImageF diffs, ImageF::Create(xsize, ysize));
MaltaDiffMapLF(image0.Plane(1), image1.Plane(1), wMfMalta, wMfMalta,
norm1Mf, &diffs, &block_diff_ac);
MaltaDiffMapLF(image0.Plane(0), image1.Plane(0), wMfMaltaX, wMfMaltaX,
@@ -1553,13 +1573,13 @@ void ButteraugliDiffmapInPlace(Image3F& image0, Image3F& image1,
ImageF uhf0[2];
ImageF uhf1[2];
- SeparateHFAndUHF(params, &hf0[0], &uhf0[0], &blur_temp);
- SeparateHFAndUHF(params, &hf1[0], &uhf1[0], &blur_temp);
+ JXL_RETURN_IF_ERROR(SeparateHFAndUHF(params, &hf0[0], &uhf0[0], &blur_temp));
+ JXL_RETURN_IF_ERROR(SeparateHFAndUHF(params, &hf1[0], &uhf1[0], &blur_temp));
// continue accumulating ac diff image from HF and UHF images
const float hf_asymmetry = params.hf_asymmetry;
{
- ImageF diffs(xsize, ysize);
+ JXL_ASSIGN_OR_RETURN(ImageF diffs, ImageF::Create(xsize, ysize));
MaltaDiffMap(uhf0[1], uhf1[1], wUhfMalta * hf_asymmetry,
wUhfMalta / hf_asymmetry, norm1Uhf, &diffs, &block_diff_ac);
MaltaDiffMap(uhf0[0], uhf1[0], wUhfMaltaX * hf_asymmetry,
@@ -1577,19 +1597,20 @@ void ButteraugliDiffmapInPlace(Image3F& image0, Image3F& image1,
}
// compute mask image from HF and UHF X and Y images
- ImageF mask(xsize, ysize);
+ JXL_ASSIGN_OR_RETURN(ImageF mask, ImageF::Create(xsize, ysize));
{
- ImageF mask0(xsize, ysize);
- ImageF mask1(xsize, ysize);
+ JXL_ASSIGN_OR_RETURN(ImageF mask0, ImageF::Create(xsize, ysize));
+ JXL_ASSIGN_OR_RETURN(ImageF mask1, ImageF::Create(xsize, ysize));
CombineChannelsForMasking(&hf0[0], &uhf0[0], &mask0);
CombineChannelsForMasking(&hf1[0], &uhf1[0], &mask1);
DeallocateHFAndUHF(&hf1[0], &uhf1[0]);
DeallocateHFAndUHF(&hf0[0], &uhf0[0]);
- Mask(mask0, mask1, params, &blur_temp, &mask, &block_diff_ac);
+ JXL_RETURN_IF_ERROR(
+ Mask(mask0, mask1, params, &blur_temp, &mask, &block_diff_ac));
}
// compute final diffmap from mask image and ac and dc diff images
- diffmap = ImageF(xsize, ysize);
+ JXL_ASSIGN_OR_RETURN(diffmap, ImageF::Create(xsize, ysize));
for (size_t y = 0; y < ysize; ++y) {
const float* row_dc = block_diff_dc.Row(y);
const float* row_ac = block_diff_ac.Row(y);
@@ -1599,6 +1620,7 @@ void ButteraugliDiffmapInPlace(Image3F& image0, Image3F& image1,
row_out[x] = sqrt(row_dc[x] * MaskDcY(val) + row_ac[x] * MaskY(val));
}
}
+ return true;
}
// NOLINTNEXTLINE(google-readability-namespace-comments)
@@ -1669,10 +1691,10 @@ static inline void CheckImage(const ImageF& image, const char* name) {
// Calculate a 2x2 subsampled image for purposes of recursive butteraugli at
// multiresolution.
-static Image3F SubSample2x(const Image3F& in) {
+static StatusOr<Image3F> SubSample2x(const Image3F& in) {
size_t xs = (in.xsize() + 1) / 2;
size_t ys = (in.ysize() + 1) / 2;
- Image3F retval(xs, ys);
+ JXL_ASSIGN_OR_RETURN(Image3F retval, Image3F::Create(xs, ys));
for (size_t c = 0; c < 3; ++c) {
for (size_t y = 0; y < ys; ++y) {
for (size_t x = 0; x < xs; ++x) {
@@ -1724,69 +1746,86 @@ Image3F* ButteraugliComparator::Temp() const {
void ButteraugliComparator::ReleaseTemp() const { temp_in_use_.clear(); }
-ButteraugliComparator::ButteraugliComparator(const Image3F& rgb0,
+ButteraugliComparator::ButteraugliComparator(size_t xsize, size_t ysize,
const ButteraugliParams& params)
- : xsize_(rgb0.xsize()),
- ysize_(rgb0.ysize()),
- params_(params),
- temp_(xsize_, ysize_) {
- if (xsize_ < 8 || ysize_ < 8) {
- return;
+ : xsize_(xsize), ysize_(ysize), params_(params) {}
+
+StatusOr<std::unique_ptr<ButteraugliComparator>> ButteraugliComparator::Make(
+ const Image3F& rgb0, const ButteraugliParams& params) {
+ size_t xsize = rgb0.xsize();
+ size_t ysize = rgb0.ysize();
+ std::unique_ptr<ButteraugliComparator> result =
+ std::unique_ptr<ButteraugliComparator>(
+ new ButteraugliComparator(xsize, ysize, params));
+ JXL_ASSIGN_OR_RETURN(result->temp_, Image3F::Create(xsize, ysize));
+
+ if (xsize < 8 || ysize < 8) {
+ return result;
}
- Image3F xyb0(xsize_, ysize_);
- HWY_DYNAMIC_DISPATCH(OpsinDynamicsImage)
- (rgb0, params, Temp(), &blur_temp_, &xyb0);
- ReleaseTemp();
- HWY_DYNAMIC_DISPATCH(SeparateFrequencies)
- (xsize_, ysize_, params_, &blur_temp_, xyb0, pi0_);
+ JXL_ASSIGN_OR_RETURN(Image3F xyb0, Image3F::Create(xsize, ysize));
+ JXL_RETURN_IF_ERROR(HWY_DYNAMIC_DISPATCH(OpsinDynamicsImage)(
+ rgb0, params, result->Temp(), &result->blur_temp_, &xyb0));
+ result->ReleaseTemp();
+ JXL_RETURN_IF_ERROR(HWY_DYNAMIC_DISPATCH(SeparateFrequencies)(
+ xsize, ysize, params, &result->blur_temp_, xyb0, result->pi0_));
// Awful recursive construction of samples of different resolution.
// This is an after-thought and possibly somewhat parallel in
// functionality with the PsychoImage multi-resolution approach.
- sub_.reset(new ButteraugliComparator(SubSample2x(rgb0), params));
+ JXL_ASSIGN_OR_RETURN(Image3F subsampledRgb0, SubSample2x(rgb0));
+ StatusOr<std::unique_ptr<ButteraugliComparator>> sub =
+ ButteraugliComparator::Make(subsampledRgb0, params);
+ if (!sub.ok()) return sub.status();
+ result->sub_ = std::move(sub).value();
+
+ return result;
}
-void ButteraugliComparator::Mask(ImageF* BUTTERAUGLI_RESTRICT mask) const {
- HWY_DYNAMIC_DISPATCH(MaskPsychoImage)
- (pi0_, pi0_, xsize_, ysize_, params_, &blur_temp_, mask, nullptr);
+Status ButteraugliComparator::Mask(ImageF* BUTTERAUGLI_RESTRICT mask) const {
+ return HWY_DYNAMIC_DISPATCH(MaskPsychoImage)(
+ pi0_, pi0_, xsize_, ysize_, params_, &blur_temp_, mask, nullptr);
}
-void ButteraugliComparator::Diffmap(const Image3F& rgb1, ImageF& result) const {
+Status ButteraugliComparator::Diffmap(const Image3F& rgb1,
+ ImageF& result) const {
if (xsize_ < 8 || ysize_ < 8) {
ZeroFillImage(&result);
- return;
+ return true;
}
- Image3F xyb1(xsize_, ysize_);
- HWY_DYNAMIC_DISPATCH(OpsinDynamicsImage)
- (rgb1, params_, Temp(), &blur_temp_, &xyb1);
+ JXL_ASSIGN_OR_RETURN(Image3F xyb1, Image3F::Create(xsize_, ysize_));
+ JXL_RETURN_IF_ERROR(HWY_DYNAMIC_DISPATCH(OpsinDynamicsImage)(
+ rgb1, params_, Temp(), &blur_temp_, &xyb1));
ReleaseTemp();
- DiffmapOpsinDynamicsImage(xyb1, result);
+ JXL_RETURN_IF_ERROR(DiffmapOpsinDynamicsImage(xyb1, result));
if (sub_) {
if (sub_->xsize_ < 8 || sub_->ysize_ < 8) {
- return;
+ return true;
}
- Image3F sub_xyb(sub_->xsize_, sub_->ysize_);
- HWY_DYNAMIC_DISPATCH(OpsinDynamicsImage)
- (SubSample2x(rgb1), params_, sub_->Temp(), &sub_->blur_temp_, &sub_xyb);
+ JXL_ASSIGN_OR_RETURN(Image3F sub_xyb,
+ Image3F::Create(sub_->xsize_, sub_->ysize_));
+ JXL_ASSIGN_OR_RETURN(Image3F subsampledRgb1, SubSample2x(rgb1));
+ JXL_RETURN_IF_ERROR(HWY_DYNAMIC_DISPATCH(OpsinDynamicsImage)(
+ subsampledRgb1, params_, sub_->Temp(), &sub_->blur_temp_, &sub_xyb));
sub_->ReleaseTemp();
ImageF subresult;
- sub_->DiffmapOpsinDynamicsImage(sub_xyb, subresult);
+ JXL_RETURN_IF_ERROR(sub_->DiffmapOpsinDynamicsImage(sub_xyb, subresult));
AddSupersampled2x(subresult, 0.5, result);
}
+ return true;
}
-void ButteraugliComparator::DiffmapOpsinDynamicsImage(const Image3F& xyb1,
- ImageF& result) const {
+Status ButteraugliComparator::DiffmapOpsinDynamicsImage(const Image3F& xyb1,
+ ImageF& result) const {
if (xsize_ < 8 || ysize_ < 8) {
ZeroFillImage(&result);
- return;
+ return true;
}
PsychoImage pi1;
- HWY_DYNAMIC_DISPATCH(SeparateFrequencies)
- (xsize_, ysize_, params_, &blur_temp_, xyb1, pi1);
- result = ImageF(xsize_, ysize_);
- DiffmapPsychoImage(pi1, result);
+ JXL_RETURN_IF_ERROR(HWY_DYNAMIC_DISPATCH(SeparateFrequencies)(
+ xsize_, ysize_, params_, &blur_temp_, xyb1, pi1));
+ JXL_ASSIGN_OR_RETURN(result, ImageF::Create(xsize_, ysize_));
+ return DiffmapPsychoImage(pi1, result);
}
namespace {
@@ -1809,18 +1848,18 @@ void MaltaDiffMapLF(const ImageF& lum0, const ImageF& lum1, const double w_0gt1,
} // namespace
-void ButteraugliComparator::DiffmapPsychoImage(const PsychoImage& pi1,
- ImageF& diffmap) const {
+Status ButteraugliComparator::DiffmapPsychoImage(const PsychoImage& pi1,
+ ImageF& diffmap) const {
if (xsize_ < 8 || ysize_ < 8) {
ZeroFillImage(&diffmap);
- return;
+ return true;
}
const float hf_asymmetry_ = params_.hf_asymmetry;
const float xmul_ = params_.xmul;
- ImageF diffs(xsize_, ysize_);
- Image3F block_diff_ac(xsize_, ysize_);
+ JXL_ASSIGN_OR_RETURN(ImageF diffs, ImageF::Create(xsize_, ysize_));
+ JXL_ASSIGN_OR_RETURN(Image3F block_diff_ac, Image3F::Create(xsize_, ysize_));
ZeroFillImage(&block_diff_ac);
MaltaDiffMap(pi0_.uhf[1], pi1.uhf[1], wUhfMalta * hf_asymmetry_,
wUhfMalta / hf_asymmetry_, norm1Uhf, &diffs, &block_diff_ac, 1);
@@ -1838,7 +1877,7 @@ void ButteraugliComparator::DiffmapPsychoImage(const PsychoImage& pi1,
MaltaDiffMapLF(pi0_.mf.Plane(0), pi1.mf.Plane(0), wMfMaltaX, wMfMaltaX,
norm1MfX, &diffs, &block_diff_ac, 0);
- Image3F block_diff_dc(xsize_, ysize_);
+ JXL_ASSIGN_OR_RETURN(Image3F block_diff_dc, Image3F::Create(xsize_, ysize_));
for (size_t c = 0; c < 3; ++c) {
if (c < 2) { // No blue channel error accumulated at HF.
HWY_DYNAMIC_DISPATCH(L2DiffAsymmetric)
@@ -1852,12 +1891,13 @@ void ButteraugliComparator::DiffmapPsychoImage(const PsychoImage& pi1,
}
ImageF mask;
- HWY_DYNAMIC_DISPATCH(MaskPsychoImage)
- (pi0_, pi1, xsize_, ysize_, params_, &blur_temp_, &mask,
- &block_diff_ac.Plane(1));
+ JXL_RETURN_IF_ERROR(HWY_DYNAMIC_DISPATCH(MaskPsychoImage)(
+ pi0_, pi1, xsize_, ysize_, params_, &blur_temp_, &mask,
+ &block_diff_ac.Plane(1)));
HWY_DYNAMIC_DISPATCH(CombineChannelsToDiffmap)
(mask, block_diff_dc, block_diff_ac, xmul_, &diffmap);
+ return true;
}
double ButteraugliScoreFromDiffmap(const ImageF& diffmap,
@@ -1872,8 +1912,8 @@ double ButteraugliScoreFromDiffmap(const ImageF& diffmap,
return retval;
}
-bool ButteraugliDiffmap(const Image3F& rgb0, const Image3F& rgb1,
- double hf_asymmetry, double xmul, ImageF& diffmap) {
+Status ButteraugliDiffmap(const Image3F& rgb0, const Image3F& rgb1,
+ double hf_asymmetry, double xmul, ImageF& diffmap) {
ButteraugliParams params;
params.hf_asymmetry = hf_asymmetry;
params.xmul = xmul;
@@ -1893,8 +1933,8 @@ bool ButteraugliDiffmapSmall(const Image3F& rgb0, const Image3F& rgb1,
size_t yborder = ysize < kMax ? (kMax - ysize) / 2 : 0;
size_t xscaled = std::max<size_t>(kMax, xsize);
size_t yscaled = std::max<size_t>(kMax, ysize);
- Image3F scaled0(xscaled, yscaled);
- Image3F scaled1(xscaled, yscaled);
+ JXL_ASSIGN_OR_RETURN(Image3F scaled0, Image3F::Create(xscaled, yscaled));
+ JXL_ASSIGN_OR_RETURN(Image3F scaled1, Image3F::Create(xscaled, yscaled));
for (int i = 0; i < 3; ++i) {
for (size_t y = 0; y < yscaled; ++y) {
for (size_t x = 0; x < xscaled; ++x) {
@@ -1907,7 +1947,7 @@ bool ButteraugliDiffmapSmall(const Image3F& rgb0, const Image3F& rgb1,
}
ImageF diffmap_scaled;
const bool ok = ButteraugliDiffmap(scaled0, scaled1, params, diffmap_scaled);
- diffmap = ImageF(xsize, ysize);
+ JXL_ASSIGN_OR_RETURN(diffmap, ImageF::Create(xsize, ysize));
for (size_t y = 0; y < ysize; ++y) {
for (size_t x = 0; x < xsize; ++x) {
diffmap.Row(y)[x] = diffmap_scaled.Row(y + yborder)[x + xborder];
@@ -1916,8 +1956,8 @@ bool ButteraugliDiffmapSmall(const Image3F& rgb0, const Image3F& rgb1,
return ok;
}
-bool ButteraugliDiffmap(const Image3F& rgb0, const Image3F& rgb1,
- const ButteraugliParams& params, ImageF& diffmap) {
+Status ButteraugliDiffmap(const Image3F& rgb0, const Image3F& rgb1,
+ const ButteraugliParams& params, ImageF& diffmap) {
const size_t xsize = rgb0.xsize();
const size_t ysize = rgb0.ysize();
if (xsize < 1 || ysize < 1) {
@@ -1930,8 +1970,9 @@ bool ButteraugliDiffmap(const Image3F& rgb0, const Image3F& rgb1,
if (xsize < kMax || ysize < kMax) {
return ButteraugliDiffmapSmall<kMax>(rgb0, rgb1, params, diffmap);
}
- ButteraugliComparator butteraugli(rgb0, params);
- butteraugli.Diffmap(rgb1, diffmap);
+ JXL_ASSIGN_OR_RETURN(std::unique_ptr<ButteraugliComparator> butteraugli,
+ ButteraugliComparator::Make(rgb0, params));
+ JXL_RETURN_IF_ERROR(butteraugli->Diffmap(rgb1, diffmap));
return true;
}
@@ -1954,9 +1995,9 @@ bool ButteraugliInterface(const Image3F& rgb0, const Image3F& rgb1,
return true;
}
-bool ButteraugliInterfaceInPlace(Image3F&& rgb0, Image3F&& rgb1,
- const ButteraugliParams& params,
- ImageF& diffmap, double& diffvalue) {
+Status ButteraugliInterfaceInPlace(Image3F&& rgb0, Image3F&& rgb1,
+ const ButteraugliParams& params,
+ ImageF& diffmap, double& diffvalue) {
const size_t xsize = rgb0.xsize();
const size_t ysize = rgb0.ysize();
if (xsize < 1 || ysize < 1) {
@@ -1973,12 +2014,13 @@ bool ButteraugliInterfaceInPlace(Image3F&& rgb0, Image3F&& rgb1,
}
ImageF subdiffmap;
if (xsize >= 15 && ysize >= 15) {
- Image3F rgb0_sub = SubSample2x(rgb0);
- Image3F rgb1_sub = SubSample2x(rgb1);
- HWY_DYNAMIC_DISPATCH(ButteraugliDiffmapInPlace)
- (rgb0_sub, rgb1_sub, params, subdiffmap);
+ JXL_ASSIGN_OR_RETURN(Image3F rgb0_sub, SubSample2x(rgb0));
+ JXL_ASSIGN_OR_RETURN(Image3F rgb1_sub, SubSample2x(rgb1));
+ JXL_RETURN_IF_ERROR(HWY_DYNAMIC_DISPATCH(ButteraugliDiffmapInPlace)(
+ rgb0_sub, rgb1_sub, params, subdiffmap));
}
- HWY_DYNAMIC_DISPATCH(ButteraugliDiffmapInPlace)(rgb0, rgb1, params, diffmap);
+ JXL_RETURN_IF_ERROR(HWY_DYNAMIC_DISPATCH(ButteraugliDiffmapInPlace)(
+ rgb0, rgb1, params, diffmap));
if (xsize >= 15 && ysize >= 15) {
AddSupersampled2x(subdiffmap, 0.5, diffmap);
}
@@ -2066,9 +2108,11 @@ void ScoreToRgb(double score, double good_threshold, double bad_threshold,
} // namespace
-Image3F CreateHeatMapImage(const ImageF& distmap, double good_threshold,
- double bad_threshold) {
- Image3F heatmap(distmap.xsize(), distmap.ysize());
+StatusOr<Image3F> CreateHeatMapImage(const ImageF& distmap,
+ double good_threshold,
+ double bad_threshold) {
+ JXL_ASSIGN_OR_RETURN(Image3F heatmap,
+ Image3F::Create(distmap.xsize(), distmap.ysize()));
for (size_t y = 0; y < distmap.ysize(); ++y) {
const float* BUTTERAUGLI_RESTRICT row_distmap = distmap.ConstRow(y);
float* BUTTERAUGLI_RESTRICT row_h0 = heatmap.PlaneRow(0, y);
diff --git a/third_party/jpeg-xl/lib/jxl/butteraugli/butteraugli.h b/third_party/jpeg-xl/lib/jxl/butteraugli/butteraugli.h
index 29130e8768..e0bfd354e1 100644
--- a/third_party/jpeg-xl/lib/jxl/butteraugli/butteraugli.h
+++ b/third_party/jpeg-xl/lib/jxl/butteraugli/butteraugli.h
@@ -14,12 +14,12 @@
#include <atomic>
#include <cmath>
+#include <cstddef>
#include <memory>
-#include <vector>
#include "lib/jxl/base/compiler_specific.h"
+#include "lib/jxl/base/status.h"
#include "lib/jxl/image.h"
-#include "lib/jxl/image_ops.h"
#define BUTTERAUGLI_ENABLE_CHECKS 0
#define BUTTERAUGLI_RESTRICT JXL_RESTRICT
@@ -87,9 +87,9 @@ bool ButteraugliInterface(const Image3F &rgb0, const Image3F &rgb1,
// Same as ButteraugliInterface, but reuses rgb0 and rgb1 for other purposes
// inside the function after they are not needed any more, and it ignores
// params.xmul.
-bool ButteraugliInterfaceInPlace(Image3F &&rgb0, Image3F &&rgb1,
- const ButteraugliParams &params,
- ImageF &diffmap, double &diffvalue);
+Status ButteraugliInterfaceInPlace(Image3F &&rgb0, Image3F &&rgb1,
+ const ButteraugliParams &params,
+ ImageF &diffmap, double &diffvalue);
// Converts the butteraugli score into fuzzy class values that are continuous
// at the class boundary. The class boundary location is based on human
@@ -147,11 +147,13 @@ struct PsychoImage {
// Blur needs a transposed image.
// Hold it here and only allocate on demand to reduce memory usage.
struct BlurTemp {
- ImageF *GetTransposed(const ImageF &in) {
+ Status GetTransposed(const ImageF &in, ImageF **out) {
if (transposed_temp.xsize() == 0) {
- transposed_temp = ImageF(in.ysize(), in.xsize());
+ JXL_ASSIGN_OR_RETURN(transposed_temp,
+ ImageF::Create(in.ysize(), in.xsize()));
}
- return &transposed_temp;
+ *out = &transposed_temp;
+ return true;
}
ImageF transposed_temp;
@@ -162,22 +164,26 @@ class ButteraugliComparator {
// Butteraugli is calibrated at xmul = 1.0. We add a multiplier here so that
// we can test the hypothesis that a higher weighing of the X channel would
// improve results at higher Butteraugli values.
- ButteraugliComparator(const Image3F &rgb0, const ButteraugliParams &params);
virtual ~ButteraugliComparator() = default;
+ static StatusOr<std::unique_ptr<ButteraugliComparator>> Make(
+ const Image3F &rgb0, const ButteraugliParams &params);
+
// Computes the butteraugli map between the original image given in the
// constructor and the distorted image give here.
- void Diffmap(const Image3F &rgb1, ImageF &result) const;
+ Status Diffmap(const Image3F &rgb1, ImageF &result) const;
// Same as above, but OpsinDynamicsImage() was already applied.
- void DiffmapOpsinDynamicsImage(const Image3F &xyb1, ImageF &result) const;
+ Status DiffmapOpsinDynamicsImage(const Image3F &xyb1, ImageF &result) const;
// Same as above, but the frequency decomposition was already applied.
- void DiffmapPsychoImage(const PsychoImage &pi1, ImageF &diffmap) const;
+ Status DiffmapPsychoImage(const PsychoImage &pi1, ImageF &diffmap) const;
- void Mask(ImageF *BUTTERAUGLI_RESTRICT mask) const;
+ Status Mask(ImageF *BUTTERAUGLI_RESTRICT mask) const;
private:
+ ButteraugliComparator(size_t xsize, size_t ysize,
+ const ButteraugliParams &params);
Image3F *Temp() const;
void ReleaseTemp() const;
@@ -196,18 +202,19 @@ class ButteraugliComparator {
};
// Deprecated.
-bool ButteraugliDiffmap(const Image3F &rgb0, const Image3F &rgb1,
- double hf_asymmetry, double xmul, ImageF &diffmap);
+Status ButteraugliDiffmap(const Image3F &rgb0, const Image3F &rgb1,
+ double hf_asymmetry, double xmul, ImageF &diffmap);
-bool ButteraugliDiffmap(const Image3F &rgb0, const Image3F &rgb1,
- const ButteraugliParams &params, ImageF &diffmap);
+Status ButteraugliDiffmap(const Image3F &rgb0, const Image3F &rgb1,
+ const ButteraugliParams &params, ImageF &diffmap);
double ButteraugliScoreFromDiffmap(const ImageF &diffmap,
const ButteraugliParams *params = nullptr);
// Generate rgb-representation of the distance between two images.
-Image3F CreateHeatMapImage(const ImageF &distmap, double good_threshold,
- double bad_threshold);
+StatusOr<Image3F> CreateHeatMapImage(const ImageF &distmap,
+ double good_threshold,
+ double bad_threshold);
} // namespace jxl
diff --git a/third_party/jpeg-xl/lib/jxl/butteraugli/butteraugli_test.cc b/third_party/jpeg-xl/lib/jxl/butteraugli/butteraugli_test.cc
index c2ccf56175..39df4bd473 100644
--- a/third_party/jpeg-xl/lib/jxl/butteraugli/butteraugli_test.cc
+++ b/third_party/jpeg-xl/lib/jxl/butteraugli/butteraugli_test.cc
@@ -30,7 +30,7 @@ using extras::PackedPixelFile;
using test::TestImage;
Image3F SinglePixelImage(float red, float green, float blue) {
- Image3F img(1, 1);
+ JXL_ASSIGN_OR_DIE(Image3F img, Image3F::Create(1, 1));
img.PlaneRow(0, 0)[0] = red;
img.PlaneRow(1, 0)[0] = green;
img.PlaneRow(2, 0)[0] = blue;
@@ -42,7 +42,7 @@ Image3F GetColorImage(const PackedPixelFile& ppf) {
const PackedImage& image = ppf.frames[0].color;
const JxlPixelFormat& format = image.format;
const uint8_t* pixels = reinterpret_cast<const uint8_t*>(image.pixels());
- Image3F color(image.xsize, image.ysize);
+ JXL_ASSIGN_OR_DIE(Image3F color, Image3F::Create(image.xsize, image.ysize));
for (size_t c = 0; c < format.num_channels; ++c) {
JXL_CHECK(ConvertFromExternal(pixels, image.pixels_size, image.xsize,
image.ysize, ppf.info.bits_per_sample, format,
@@ -93,7 +93,7 @@ TEST(ButteraugliInPlaceTest, LargeImage) {
TestImage img;
img.SetDimensions(xsize, ysize).AddFrame().RandomFill(777);
Image3F rgb0 = GetColorImage(img.ppf());
- Image3F rgb1(xsize, ysize);
+ JXL_ASSIGN_OR_DIE(Image3F rgb1, Image3F::Create(xsize, ysize));
CopyImageTo(rgb0, &rgb1);
AddUniformNoise(&rgb1, 0.02f, 7777);
AddEdge(&rgb1, 0.1f, xsize / 2, xsize / 2);
diff --git a/third_party/jpeg-xl/lib/jxl/cache_aligned.cc b/third_party/jpeg-xl/lib/jxl/cache_aligned.cc
index 992efc4d48..8a95634d68 100644
--- a/third_party/jpeg-xl/lib/jxl/cache_aligned.cc
+++ b/third_party/jpeg-xl/lib/jxl/cache_aligned.cc
@@ -5,6 +5,7 @@
#include "lib/jxl/cache_aligned.h"
+#include <jxl/types.h>
#include <stdio.h>
#include <stdlib.h>
@@ -95,7 +96,7 @@ void* CacheAligned::Allocate(const size_t payload_size, size_t offset) {
aligned &= ~(kAlias - 1);
#endif
-#if 0
+#if JXL_FALSE
// No effect.
uintptr_t page_aligned = reinterpret_cast<uintptr_t>(allocated);
page_aligned &= ~(4096 - 1);
diff --git a/third_party/jpeg-xl/lib/jxl/cache_aligned.h b/third_party/jpeg-xl/lib/jxl/cache_aligned.h
index d79d7be461..8480471e5c 100644
--- a/third_party/jpeg-xl/lib/jxl/cache_aligned.h
+++ b/third_party/jpeg-xl/lib/jxl/cache_aligned.h
@@ -49,7 +49,7 @@ class CacheAligned {
// Avoids the need for a function pointer (deleter) in CacheAlignedUniquePtr.
struct CacheAlignedDeleter {
void operator()(uint8_t* aligned_pointer) const {
- return CacheAligned::Free(aligned_pointer);
+ CacheAligned::Free(aligned_pointer);
}
};
diff --git a/third_party/jpeg-xl/lib/jxl/chroma_from_luma.cc b/third_party/jpeg-xl/lib/jxl/chroma_from_luma.cc
index 63d21cbb4b..69585c44cf 100644
--- a/third_party/jpeg-xl/lib/jxl/chroma_from_luma.cc
+++ b/third_party/jpeg-xl/lib/jxl/chroma_from_luma.cc
@@ -5,17 +5,25 @@
#include "lib/jxl/chroma_from_luma.h"
+#include "lib/jxl/image_ops.h"
+
namespace jxl {
-ColorCorrelationMap::ColorCorrelationMap(size_t xsize, size_t ysize, bool XYB)
- : ytox_map(DivCeil(xsize, kColorTileDim), DivCeil(ysize, kColorTileDim)),
- ytob_map(DivCeil(xsize, kColorTileDim), DivCeil(ysize, kColorTileDim)) {
- ZeroFillImage(&ytox_map);
- ZeroFillImage(&ytob_map);
+StatusOr<ColorCorrelationMap> ColorCorrelationMap::Create(size_t xsize,
+ size_t ysize,
+ bool XYB) {
+ ColorCorrelationMap result;
+ size_t xblocks = DivCeil(xsize, kColorTileDim);
+ size_t yblocks = DivCeil(ysize, kColorTileDim);
+ JXL_ASSIGN_OR_RETURN(result.ytox_map, ImageSB::Create(xblocks, yblocks));
+ JXL_ASSIGN_OR_RETURN(result.ytob_map, ImageSB::Create(xblocks, yblocks));
+ ZeroFillImage(&result.ytox_map);
+ ZeroFillImage(&result.ytob_map);
if (!XYB) {
- base_correlation_b_ = 0;
+ result.base_correlation_b_ = 0;
}
- RecomputeDCFactors();
+ result.RecomputeDCFactors();
+ return result;
}
} // namespace jxl
diff --git a/third_party/jpeg-xl/lib/jxl/chroma_from_luma.h b/third_party/jpeg-xl/lib/jxl/chroma_from_luma.h
index cb3b710762..1f2353d9af 100644
--- a/third_party/jpeg-xl/lib/jxl/chroma_from_luma.h
+++ b/third_party/jpeg-xl/lib/jxl/chroma_from_luma.h
@@ -12,19 +12,15 @@
#include <stddef.h>
#include <stdint.h>
-#include <vector>
+#include <limits>
-#include "lib/jxl/base/compiler_specific.h"
-#include "lib/jxl/base/data_parallel.h"
#include "lib/jxl/base/status.h"
#include "lib/jxl/cms/opsin_params.h"
#include "lib/jxl/dec_bit_reader.h"
-#include "lib/jxl/entropy_coder.h"
#include "lib/jxl/field_encodings.h"
#include "lib/jxl/fields.h"
#include "lib/jxl/frame_dimensions.h"
#include "lib/jxl/image.h"
-#include "lib/jxl/quant_weights.h"
namespace jxl {
@@ -55,7 +51,8 @@ struct ColorCorrelationMap {
// xsize/ysize are in pixels
// set XYB=false to do something close to no-op cmap (needed for now since
// cmap is mandatory)
- ColorCorrelationMap(size_t xsize, size_t ysize, bool XYB = true);
+ static StatusOr<ColorCorrelationMap> Create(size_t xsize, size_t ysize,
+ bool XYB = true);
float YtoXRatio(int32_t x_factor) const {
return base_correlation_x_ + x_factor * color_scale_;
@@ -96,7 +93,7 @@ struct ColorCorrelationMap {
color_factor_ == kDefaultColorFactor;
}
- int32_t RatioJPEG(int32_t factor) const {
+ static int32_t RatioJPEG(int32_t factor) {
return factor * (1 << kCFLFixedPointPrecision) / kDefaultColorFactor;
}
diff --git a/third_party/jpeg-xl/lib/jxl/cms/color_encoding_cms.h b/third_party/jpeg-xl/lib/jxl/cms/color_encoding_cms.h
index db61f820ca..3b7ae09d6f 100644
--- a/third_party/jpeg-xl/lib/jxl/cms/color_encoding_cms.h
+++ b/third_party/jpeg-xl/lib/jxl/cms/color_encoding_cms.h
@@ -96,6 +96,8 @@ enum class RenderingIntent : uint32_t {
// Chromaticity (Y is omitted because it is 1 for white points and implicit for
// primaries)
struct CIExy {
+ CIExy() = default;
+ CIExy(double x, double y) : x(x), y(y) {}
double x = 0.0;
double y = 0.0;
};
@@ -516,7 +518,7 @@ struct ColorEncoding {
JXL_RETURN_IF_ERROR(cms.set_fields_from_icc(cms.set_fields_data,
new_icc.data(), new_icc.size(),
&external, &new_cmyk));
- cmyk = new_cmyk;
+ cmyk = static_cast<bool>(new_cmyk);
JXL_RETURN_IF_ERROR(FromExternal(external));
icc = std::move(new_icc);
return true;
diff --git a/third_party/jpeg-xl/lib/jxl/cms/jxl_cms.cc b/third_party/jpeg-xl/lib/jxl/cms/jxl_cms.cc
index dd00b8b81f..e35b3ce172 100644
--- a/third_party/jpeg-xl/lib/jxl/cms/jxl_cms.cc
+++ b/third_party/jpeg-xl/lib/jxl/cms/jxl_cms.cc
@@ -307,7 +307,7 @@ int DoColorSpaceTransform(void* t, size_t thread, const float* buf_src,
#if JPEGXL_ENABLE_SKCMS
-JXL_MUST_USE_RESULT CIExy CIExyFromXYZ(const float XYZ[3]) {
+JXL_MUST_USE_RESULT CIExy CIExyFromXYZ(const Color& XYZ) {
const float factor = 1.f / (XYZ[0] + XYZ[1] + XYZ[2]);
CIExy xy;
xy.x = XYZ[0] * factor;
@@ -405,8 +405,8 @@ ColorSpace ColorSpaceFromProfile(const skcms_ICCProfile& profile) {
}
// vector_out := matmul(matrix, vector_in)
-void MatrixProduct(const skcms_Matrix3x3& matrix, const float vector_in[3],
- float vector_out[3]) {
+void MatrixProduct(const skcms_Matrix3x3& matrix, const Color& vector_in,
+ Color& vector_out) {
for (int i = 0; i < 3; ++i) {
vector_out[i] = 0;
for (int j = 0; j < 3; ++j) {
@@ -418,8 +418,8 @@ void MatrixProduct(const skcms_Matrix3x3& matrix, const float vector_in[3],
// Returns white point that was specified when creating the profile.
JXL_MUST_USE_RESULT Status UnadaptedWhitePoint(const skcms_ICCProfile& profile,
CIExy* out) {
- float media_white_point_XYZ[3];
- if (!skcms_GetWTPT(&profile, media_white_point_XYZ)) {
+ Color media_white_point_XYZ;
+ if (!skcms_GetWTPT(&profile, media_white_point_XYZ.data())) {
return JXL_FAILURE("ICC profile does not contain WhitePoint tag");
}
skcms_Matrix3x3 CHAD;
@@ -435,7 +435,7 @@ JXL_MUST_USE_RESULT Status UnadaptedWhitePoint(const skcms_ICCProfile& profile,
if (!skcms_Matrix3x3_invert(&CHAD, &inverse_CHAD)) {
return JXL_FAILURE("Non-invertible ChromaticAdaptation matrix");
}
- float unadapted_white_point_XYZ[3];
+ Color unadapted_white_point_XYZ;
MatrixProduct(inverse_CHAD, media_white_point_XYZ, unadapted_white_point_XYZ);
*out = CIExyFromXYZ(unadapted_white_point_XYZ);
return true;
@@ -445,7 +445,8 @@ Status IdentifyPrimaries(const skcms_ICCProfile& profile,
const CIExy& wp_unadapted, ColorEncoding* c) {
if (!c->HasPrimaries()) return true;
- skcms_Matrix3x3 CHAD, inverse_CHAD;
+ skcms_Matrix3x3 CHAD;
+ skcms_Matrix3x3 inverse_CHAD;
if (skcms_GetCHAD(&profile, &CHAD)) {
JXL_RETURN_IF_ERROR(skcms_Matrix3x3_invert(&CHAD, &inverse_CHAD));
} else {
@@ -457,11 +458,12 @@ Status IdentifyPrimaries(const skcms_ICCProfile& profile,
{{0.9869929, -0.1470543, 0.1599627},
{0.4323053, 0.5183603, 0.0492912},
{-0.0085287, 0.0400428, 0.9684867}}};
- static constexpr float kWpD50XYZ[3] = {0.96420288, 1.0, 0.82490540};
- float wp_unadapted_XYZ[3];
+ static constexpr Color kWpD50XYZ{0.96420288, 1.0, 0.82490540};
+ Color wp_unadapted_XYZ;
JXL_RETURN_IF_ERROR(
CIEXYZFromWhiteCIExy(wp_unadapted.x, wp_unadapted.y, wp_unadapted_XYZ));
- float wp_D50_LMS[3], wp_unadapted_LMS[3];
+ Color wp_D50_LMS;
+ Color wp_unadapted_LMS;
MatrixProduct(kLMSFromXYZ, kWpD50XYZ, wp_D50_LMS);
MatrixProduct(kLMSFromXYZ, wp_unadapted_XYZ, wp_unadapted_LMS);
inverse_CHAD = {{{wp_unadapted_LMS[0] / wp_D50_LMS[0], 0, 0},
@@ -471,16 +473,16 @@ Status IdentifyPrimaries(const skcms_ICCProfile& profile,
inverse_CHAD = skcms_Matrix3x3_concat(&inverse_CHAD, &kLMSFromXYZ);
}
- float XYZ[3];
+ Color XYZ;
PrimariesCIExy primaries;
CIExy* const chromaticities[] = {&primaries.r, &primaries.g, &primaries.b};
for (int i = 0; i < 3; ++i) {
float RGB[3] = {};
RGB[i] = 1;
skcms_Transform(RGB, skcms_PixelFormat_RGB_fff, skcms_AlphaFormat_Opaque,
- &profile, XYZ, skcms_PixelFormat_RGB_fff,
+ &profile, XYZ.data(), skcms_PixelFormat_RGB_fff,
skcms_AlphaFormat_Opaque, skcms_XYZD50_profile(), 1);
- float unadapted_XYZ[3];
+ Color unadapted_XYZ;
MatrixProduct(inverse_CHAD, XYZ, unadapted_XYZ);
*chromaticities[i] = CIExyFromXYZ(unadapted_XYZ);
}
@@ -829,17 +831,17 @@ Status GetPrimariesLuminances(const ColorEncoding& encoding,
// From there, by multiplying each total by its corresponding y, we get Y for
// that primary.
- float white_XYZ[3];
+ Color white_XYZ;
CIExy wp = encoding.GetWhitePoint();
JXL_RETURN_IF_ERROR(CIEXYZFromWhiteCIExy(wp.x, wp.y, white_XYZ));
const PrimariesCIExy primaries = encoding.GetPrimaries();
- double chromaticities[3][3] = {
- {primaries.r.x, primaries.g.x, primaries.b.x},
- {primaries.r.y, primaries.g.y, primaries.b.y},
- {1 - primaries.r.x - primaries.r.y, 1 - primaries.g.x - primaries.g.y,
- 1 - primaries.b.x - primaries.b.y}};
- JXL_RETURN_IF_ERROR(Inv3x3Matrix(&chromaticities[0][0]));
+ Matrix3x3d chromaticities{
+ {{primaries.r.x, primaries.g.x, primaries.b.x},
+ {primaries.r.y, primaries.g.y, primaries.b.y},
+ {1 - primaries.r.x - primaries.r.y, 1 - primaries.g.x - primaries.g.y,
+ 1 - primaries.b.x - primaries.b.y}}};
+ JXL_RETURN_IF_ERROR(Inv3x3Matrix(chromaticities));
const double ys[3] = {primaries.r.y, primaries.g.y, primaries.b.y};
for (size_t i = 0; i < 3; ++i) {
luminances[i] = ys[i] * (chromaticities[i][0] * white_XYZ[0] +
@@ -971,14 +973,31 @@ JXL_BOOL JxlCmsSetFieldsFromICC(void* user_data, const uint8_t* icc_data,
JXL_RETURN_IF_ERROR(skcms_Parse(icc_data, icc_size, &profile));
// skcms does not return the rendering intent, so get it from the file. It
- // is encoded as big-endian 32-bit integer in bytes 60..63.
- uint32_t rendering_intent32 = icc_data[67];
- if (rendering_intent32 > 3 || icc_data[64] != 0 || icc_data[65] != 0 ||
- icc_data[66] != 0) {
- return JXL_FAILURE("Invalid rendering intent %u\n", rendering_intent32);
+ // should be encoded as big-endian 32-bit integer in bytes 60..63.
+ uint32_t big_endian_rendering_intent = icc_data[67] + (icc_data[66] << 8) +
+ (icc_data[65] << 16) +
+ (icc_data[64] << 24);
+ // Some files encode rendering intent as little endian, which is not spec
+ // compliant. However we accept those with a warning.
+ uint32_t little_endian_rendering_intent = (icc_data[67] << 24) +
+ (icc_data[66] << 16) +
+ (icc_data[65] << 8) + icc_data[64];
+ uint32_t candidate_rendering_intent =
+ std::min(big_endian_rendering_intent, little_endian_rendering_intent);
+ if (candidate_rendering_intent != big_endian_rendering_intent) {
+ JXL_WARNING(
+ "Invalid rendering intent bytes: [0x%02X 0x%02X 0x%02X 0x%02X], "
+ "assuming %u was meant",
+ icc_data[64], icc_data[65], icc_data[66], icc_data[67],
+ candidate_rendering_intent);
+ }
+ if (candidate_rendering_intent > 3) {
+ return JXL_FAILURE("Invalid rendering intent %u\n",
+ candidate_rendering_intent);
}
// ICC and RenderingIntent have the same values (0..3).
- c_enc.rendering_intent = static_cast<RenderingIntent>(rendering_intent32);
+ c_enc.rendering_intent =
+ static_cast<RenderingIntent>(candidate_rendering_intent);
if (profile.has_CICP &&
ApplyCICP(profile.CICP.color_primaries,
@@ -986,11 +1005,11 @@ JXL_BOOL JxlCmsSetFieldsFromICC(void* user_data, const uint8_t* icc_data,
profile.CICP.matrix_coefficients,
profile.CICP.video_full_range_flag, &c_enc)) {
*c = c_enc.ToExternal();
- return true;
+ return JXL_TRUE;
}
c_enc.color_space = ColorSpaceFromProfile(profile);
- *cmyk = (profile.data_color_space == skcms_Signature_CMYK);
+ *cmyk = TO_JXL_BOOL(profile.data_color_space == skcms_Signature_CMYK);
CIExy wp_unadapted;
JXL_RETURN_IF_ERROR(UnadaptedWhitePoint(profile, &wp_unadapted));
@@ -1026,14 +1045,14 @@ JXL_BOOL JxlCmsSetFieldsFromICC(void* user_data, const uint8_t* icc_data,
ApplyCICP(cicp_buffer[8], cicp_buffer[9], cicp_buffer[10],
cicp_buffer[11], &c_enc)) {
*c = c_enc.ToExternal();
- return true;
+ return JXL_TRUE;
}
c_enc.color_space = ColorSpaceFromProfile(profile);
if (cmsGetColorSpace(profile.get()) == cmsSigCmykData) {
*cmyk = JXL_TRUE;
*c = c_enc.ToExternal();
- return true;
+ return JXL_TRUE;
}
const cmsCIEXYZ wp_unadapted = UnadaptedWhitePoint(context, profile, c_enc);
@@ -1049,7 +1068,7 @@ JXL_BOOL JxlCmsSetFieldsFromICC(void* user_data, const uint8_t* icc_data,
#endif // JPEGXL_ENABLE_SKCMS
*c = c_enc.ToExternal();
- return true;
+ return JXL_TRUE;
}
} // namespace
@@ -1084,9 +1103,10 @@ void* JxlCmsInit(void* init_data, size_t num_threads, size_t xsize,
const JxlColorProfile* input, const JxlColorProfile* output,
float intensity_target) {
JXL_ASSERT(init_data != nullptr);
- auto cms = static_cast<const JxlCmsInterface*>(init_data);
+ const auto* cms = static_cast<const JxlCmsInterface*>(init_data);
auto t = jxl::make_unique<JxlCms>();
- IccBytes icc_src, icc_dst;
+ IccBytes icc_src;
+ IccBytes icc_dst;
if (input->icc.size == 0) {
JXL_NOTIFY_ERROR("JxlCmsInit: empty input ICC");
return nullptr;
@@ -1259,8 +1279,6 @@ void* JxlCmsInit(void* init_data, size_t num_threads, size_t xsize,
// Not including alpha channel (copied separately).
const size_t channels_src = (c_src.cmyk ? 4 : c_src.Channels());
const size_t channels_dst = c_dst.Channels();
- JXL_CHECK(channels_src == channels_dst ||
- (channels_src == 4 && channels_dst == 3));
#if JXL_CMS_VERBOSE
printf("Channels: %" PRIuS "; Threads: %" PRIuS "\n", channels_src,
num_threads);
@@ -1294,13 +1312,14 @@ void* JxlCmsInit(void* init_data, size_t num_threads, size_t xsize,
// outputs (or vice versa), we use floating point input/output.
t->channels_src = channels_src;
t->channels_dst = channels_dst;
+#if !JPEGXL_ENABLE_SKCMS
size_t actual_channels_src = channels_src;
size_t actual_channels_dst = channels_dst;
-#if JPEGXL_ENABLE_SKCMS
+#else
// SkiaCMS doesn't support grayscale float buffers, so we create space for RGB
// float buffers anyway.
- actual_channels_src = (channels_src == 4 ? 4 : 3);
- actual_channels_dst = 3;
+ size_t actual_channels_src = (channels_src == 4 ? 4 : 3);
+ size_t actual_channels_dst = 3;
#endif
AllocateBuffer(xsize * actual_channels_src, num_threads, &t->src_storage,
&t->buf_src);
diff --git a/third_party/jpeg-xl/lib/jxl/cms/jxl_cms_internal.h b/third_party/jpeg-xl/lib/jxl/cms/jxl_cms_internal.h
index c00fe82d8c..7f59e688d0 100644
--- a/third_party/jpeg-xl/lib/jxl/cms/jxl_cms_internal.h
+++ b/third_party/jpeg-xl/lib/jxl/cms/jxl_cms_internal.h
@@ -41,7 +41,7 @@ enum class ExtraTF {
};
static Status PrimariesToXYZ(float rx, float ry, float gx, float gy, float bx,
- float by, float wx, float wy, float matrix[9]) {
+ float by, float wx, float wy, Matrix3x3& matrix) {
bool ok = (wx >= 0) && (wx <= 1) && (wy > 0) && (wy <= 1);
if (!ok) {
return JXL_FAILURE("Invalid white point");
@@ -49,51 +49,48 @@ static Status PrimariesToXYZ(float rx, float ry, float gx, float gy, float bx,
// TODO(lode): also require rx, ry, gx, gy, bx, to be in range 0-1? ICC
// profiles in theory forbid negative XYZ values, but in practice the ACES P0
// color space uses a negative y for the blue primary.
- float primaries[9] = {
- rx, gx, bx, ry, gy, by, 1.0f - rx - ry, 1.0f - gx - gy, 1.0f - bx - by};
- float primaries_inv[9];
- memcpy(primaries_inv, primaries, sizeof(float) * 9);
+ Matrix3x3 primaries{{{rx, gx, bx},
+ {ry, gy, by},
+ {1.0f - rx - ry, 1.0f - gx - gy, 1.0f - bx - by}}};
+ Matrix3x3 primaries_inv;
+ primaries_inv = primaries;
JXL_RETURN_IF_ERROR(Inv3x3Matrix(primaries_inv));
- float w[3] = {wx / wy, 1.0f, (1.0f - wx - wy) / wy};
+ Vector3 w{wx / wy, 1.0f, (1.0f - wx - wy) / wy};
// 1 / tiny float can still overflow
JXL_RETURN_IF_ERROR(std::isfinite(w[0]) && std::isfinite(w[2]));
- float xyz[3];
+ Vector3 xyz;
Mul3x3Vector(primaries_inv, w, xyz);
- float a[9] = {
- xyz[0], 0, 0, 0, xyz[1], 0, 0, 0, xyz[2],
- };
+ Matrix3x3 a{{{xyz[0], 0, 0}, {0, xyz[1], 0}, {0, 0, xyz[2]}}};
Mul3x3Matrix(primaries, a, matrix);
return true;
}
/* Chromatic adaptation matrices*/
-constexpr float kBradford[9] = {
- 0.8951f, 0.2664f, -0.1614f, -0.7502f, 1.7135f,
- 0.0367f, 0.0389f, -0.0685f, 1.0296f,
-};
-constexpr float kBradfordInv[9] = {
- 0.9869929f, -0.1470543f, 0.1599627f, 0.4323053f, 0.5183603f,
- 0.0492912f, -0.0085287f, 0.0400428f, 0.9684867f,
-};
+constexpr Matrix3x3 kBradford{{{0.8951f, 0.2664f, -0.1614f},
+ {-0.7502f, 1.7135f, 0.0367f},
+ {0.0389f, -0.0685f, 1.0296f}}};
+constexpr Matrix3x3 kBradfordInv{{{0.9869929f, -0.1470543f, 0.1599627f},
+ {0.4323053f, 0.5183603f, 0.0492912f},
+ {-0.0085287f, 0.0400428f, 0.9684867f}}};
// Adapts whitepoint x, y to D50
-static Status AdaptToXYZD50(float wx, float wy, float matrix[9]) {
+static Status AdaptToXYZD50(float wx, float wy, Matrix3x3& matrix) {
bool ok = (wx >= 0) && (wx <= 1) && (wy > 0) && (wy <= 1);
if (!ok) {
// Out of range values can cause division through zero
// further down with the bradford adaptation too.
return JXL_FAILURE("Invalid white point");
}
- float w[3] = {wx / wy, 1.0f, (1.0f - wx - wy) / wy};
+ Vector3 w{wx / wy, 1.0f, (1.0f - wx - wy) / wy};
// 1 / tiny float can still overflow
JXL_RETURN_IF_ERROR(std::isfinite(w[0]) && std::isfinite(w[2]));
- float w50[3] = {0.96422f, 1.0f, 0.82521f};
+ Vector3 w50{0.96422f, 1.0f, 0.82521f};
- float lms[3];
- float lms50[3];
+ Vector3 lms;
+ Vector3 lms50;
Mul3x3Vector(kBradford, w, lms);
Mul3x3Vector(kBradford, w50, lms50);
@@ -101,15 +98,15 @@ static Status AdaptToXYZD50(float wx, float wy, float matrix[9]) {
if (lms[0] == 0 || lms[1] == 0 || lms[2] == 0) {
return JXL_FAILURE("Invalid white point");
}
- float a[9] = {
- // /----> 0, 1, 2, 3, /----> 4, 5, 6, 7, /----> 8,
- lms50[0] / lms[0], 0, 0, 0, lms50[1] / lms[1], 0, 0, 0, lms50[2] / lms[2],
- };
- if (!std::isfinite(a[0]) || !std::isfinite(a[4]) || !std::isfinite(a[8])) {
+ Matrix3x3 a{{{lms50[0] / lms[0], 0, 0},
+ {0, lms50[1] / lms[1], 0},
+ {0, 0, lms50[2] / lms[2]}}};
+ if (!std::isfinite(a[0][0]) || !std::isfinite(a[1][1]) ||
+ !std::isfinite(a[2][2])) {
return JXL_FAILURE("Invalid white point");
}
- float b[9];
+ Matrix3x3 b;
Mul3x3Matrix(a, kBradford, b);
Mul3x3Matrix(kBradfordInv, b, matrix);
@@ -118,10 +115,10 @@ static Status AdaptToXYZD50(float wx, float wy, float matrix[9]) {
static Status PrimariesToXYZD50(float rx, float ry, float gx, float gy,
float bx, float by, float wx, float wy,
- float matrix[9]) {
- float toXYZ[9];
+ Matrix3x3& matrix) {
+ Matrix3x3 toXYZ;
JXL_RETURN_IF_ERROR(PrimariesToXYZ(rx, ry, gx, gy, bx, by, wx, wy, toXYZ));
- float d50[9];
+ Matrix3x3 d50;
JXL_RETURN_IF_ERROR(AdaptToXYZD50(wx, wy, d50));
Mul3x3Matrix(d50, toXYZ, matrix);
@@ -130,14 +127,13 @@ static Status PrimariesToXYZD50(float rx, float ry, float gx, float gy,
static Status ToneMapPixel(const JxlColorEncoding& c, const float in[3],
uint8_t pcslab_out[3]) {
- float primaries_XYZ[9];
+ Matrix3x3 primaries_XYZ;
JXL_RETURN_IF_ERROR(PrimariesToXYZ(
c.primaries_red_xy[0], c.primaries_red_xy[1], c.primaries_green_xy[0],
c.primaries_green_xy[1], c.primaries_blue_xy[0], c.primaries_blue_xy[1],
c.white_point_xy[0], c.white_point_xy[1], primaries_XYZ));
- const float luminances[3] = {primaries_XYZ[3], primaries_XYZ[4],
- primaries_XYZ[5]};
- float linear[3];
+ const Vector3 luminances = primaries_XYZ[1];
+ Color linear;
JxlTransferFunction tf = c.transfer_function;
if (tf == JXL_TRANSFER_FUNCTION_PQ) {
for (size_t i = 0; i < 3; ++i) {
@@ -151,25 +147,25 @@ static Status ToneMapPixel(const JxlColorEncoding& c, const float in[3],
}
if (tf == JXL_TRANSFER_FUNCTION_PQ) {
Rec2408ToneMapperBase tone_mapper({0, 10000}, {0, 250}, luminances);
- tone_mapper.ToneMap(&linear[0], &linear[1], &linear[2]);
+ tone_mapper.ToneMap(linear);
} else {
HlgOOTF_Base ootf(/*source_luminance=*/300, /*target_luminance=*/80,
luminances);
- ootf.Apply(&linear[0], &linear[1], &linear[2]);
+ ootf.Apply(linear);
}
- GamutMapScalar(&linear[0], &linear[1], &linear[2], luminances,
+ GamutMapScalar(linear, luminances,
/*preserve_saturation=*/0.3f);
- float chad[9];
+ Matrix3x3 chad;
JXL_RETURN_IF_ERROR(
AdaptToXYZD50(c.white_point_xy[0], c.white_point_xy[1], chad));
- float to_xyzd50[9];
+ Matrix3x3 to_xyzd50;
Mul3x3Matrix(chad, primaries_XYZ, to_xyzd50);
- float xyz[3] = {0, 0, 0};
+ Vector3 xyz{0, 0, 0};
for (size_t xyz_c = 0; xyz_c < 3; ++xyz_c) {
for (size_t rgb_c = 0; rgb_c < 3; ++rgb_c) {
- xyz[xyz_c] += linear[rgb_c] * to_xyzd50[3 * xyz_c + rgb_c];
+ xyz[xyz_c] += linear[rgb_c] * to_xyzd50[xyz_c][rgb_c];
}
}
@@ -206,7 +202,7 @@ static std::vector<uint16_t> CreateTableCurve(uint32_t N, const ExtraTF tf,
JXL_ASSERT(N <= 4096); // ICC MFT2 only allows 4K entries
JXL_ASSERT(tf == ExtraTF::kPQ || tf == ExtraTF::kHLG);
- static constexpr float kLuminances[] = {1.f / 3, 1.f / 3, 1.f / 3};
+ static constexpr Vector3 kLuminances{1.f / 3, 1.f / 3, 1.f / 3};
Rec2408ToneMapperBase tone_mapper({0, kPQIntensityTarget},
{0, kDefaultIntensityTarget}, kLuminances);
// No point using float - LCMS converts to 16-bit for A2B/MFT.
@@ -220,9 +216,10 @@ static std::vector<uint16_t> CreateTableCurve(uint32_t N, const ExtraTF tf,
: TF_PQ_Base::DisplayFromEncoded(kPQIntensityTarget, dx);
if (tone_map && tf == ExtraTF::kPQ &&
kPQIntensityTarget > kDefaultIntensityTarget) {
- float r = y * 10000 / kPQIntensityTarget, g = r, b = r;
- tone_mapper.ToneMap(&r, &g, &b);
- y = r;
+ float l = y * 10000 / kPQIntensityTarget;
+ Color gray{l, l, l};
+ tone_mapper.ToneMap(gray);
+ y = gray[0];
}
JXL_ASSERT(y >= 0.0);
// Clamp to table range - necessary for HLG.
@@ -233,7 +230,7 @@ static std::vector<uint16_t> CreateTableCurve(uint32_t N, const ExtraTF tf,
return table;
}
-static Status CIEXYZFromWhiteCIExy(double wx, double wy, float XYZ[3]) {
+static Status CIEXYZFromWhiteCIExy(double wx, double wy, Color& XYZ) {
// Target Y = 1.
if (std::abs(wy) < 1e-12) return JXL_FAILURE("Y value is too small");
const float factor = 1 / wy;
@@ -292,10 +289,18 @@ static void ICCComputeMD5(const std::vector<uint8_t>& data, uint8_t sum[16])
6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21,
};
- uint32_t a0 = 0x67452301, b0 = 0xefcdab89, c0 = 0x98badcfe, d0 = 0x10325476;
+ uint32_t a0 = 0x67452301;
+ uint32_t b0 = 0xefcdab89;
+ uint32_t c0 = 0x98badcfe;
+ uint32_t d0 = 0x10325476;
for (size_t i = 0; i < data64.size(); i += 64) {
- uint32_t a = a0, b = b0, c = c0, d = d0, f, g;
+ uint32_t a = a0;
+ uint32_t b = b0;
+ uint32_t c = c0;
+ uint32_t d = d0;
+ uint32_t f;
+ uint32_t g;
for (size_t j = 0; j < 64; j++) {
if (j < 16) {
f = (b & c) | ((~b) & d);
@@ -310,8 +315,10 @@ static void ICCComputeMD5(const std::vector<uint8_t>& data, uint8_t sum[16])
f = c ^ (b | (~d));
g = (7 * j) & 0xf;
}
- uint32_t dg0 = data64[i + g * 4 + 0], dg1 = data64[i + g * 4 + 1],
- dg2 = data64[i + g * 4 + 2], dg3 = data64[i + g * 4 + 3];
+ uint32_t dg0 = data64[i + g * 4 + 0];
+ uint32_t dg1 = data64[i + g * 4 + 1];
+ uint32_t dg2 = data64[i + g * 4 + 2];
+ uint32_t dg3 = data64[i + g * 4 + 3];
uint32_t u = dg0 | (dg1 << 8u) | (dg2 << 16u) | (dg3 << 24u);
f += a + sineparts[j] + u;
a = d;
@@ -342,23 +349,23 @@ static void ICCComputeMD5(const std::vector<uint8_t>& data, uint8_t sum[16])
sum[15] = d0 >> 24u;
}
-static Status CreateICCChadMatrix(double wx, double wy, float result[9]) {
- float m[9];
+static Status CreateICCChadMatrix(double wx, double wy, Matrix3x3& result) {
+ Matrix3x3 m;
if (wy == 0) { // WhitePoint can not be pitch-black.
return JXL_FAILURE("Invalid WhitePoint");
}
JXL_RETURN_IF_ERROR(AdaptToXYZD50(wx, wy, m));
- memcpy(result, m, sizeof(float) * 9);
+ result = m;
return true;
}
// Creates RGB to XYZ matrix given RGB primaries and whitepoint in xy.
static Status CreateICCRGBMatrix(double rx, double ry, double gx, double gy,
double bx, double by, double wx, double wy,
- float result[9]) {
- float m[9];
+ Matrix3x3& result) {
+ Matrix3x3 m;
JXL_RETURN_IF_ERROR(PrimariesToXYZD50(rx, ry, gx, gy, bx, by, wx, wy, m));
- memcpy(result, m, sizeof(float) * 9);
+ result = m;
return true;
}
@@ -433,8 +440,12 @@ static Status CreateICCHeader(const JxlColorEncoding& c,
// Three uint32_t's date/time encoding.
// TODO(lode): encode actual date and time, this is a placeholder
- uint32_t year = 2019, month = 12, day = 1;
- uint32_t hour = 0, minute = 0, second = 0;
+ uint32_t year = 2019;
+ uint32_t month = 12;
+ uint32_t day = 1;
+ uint32_t hour = 0;
+ uint32_t minute = 0;
+ uint32_t second = 0;
WriteICCUint16(year, 24, header);
WriteICCUint16(month, 26, header);
WriteICCUint16(day, 28, header);
@@ -491,13 +502,13 @@ static void CreateICCMlucTag(const std::string& text,
WriteICCTag("enUS", tags->size(), tags);
WriteICCUint32(text.size() * 2, tags->size(), tags);
WriteICCUint32(28, tags->size(), tags);
- for (size_t i = 0; i < text.size(); i++) {
+ for (char c : text) {
tags->push_back(0); // prepend 0 for UTF-16
- tags->push_back(text[i]);
+ tags->push_back(c);
}
}
-static Status CreateICCXYZTag(float xyz[3], std::vector<uint8_t>* tags) {
+static Status CreateICCXYZTag(const Color& xyz, std::vector<uint8_t>* tags) {
WriteICCTag("XYZ ", tags->size(), tags);
WriteICCUint32(0, tags->size(), tags);
for (size_t i = 0; i < 3; ++i) {
@@ -506,11 +517,14 @@ static Status CreateICCXYZTag(float xyz[3], std::vector<uint8_t>* tags) {
return true;
}
-static Status CreateICCChadTag(float chad[9], std::vector<uint8_t>* tags) {
+static Status CreateICCChadTag(const Matrix3x3& chad,
+ std::vector<uint8_t>* tags) {
WriteICCTag("sf32", tags->size(), tags);
WriteICCUint32(0, tags->size(), tags);
- for (size_t i = 0; i < 9; i++) {
- JXL_RETURN_IF_ERROR(WriteICCS15Fixed16(chad[i], tags->size(), tags));
+ for (size_t j = 0; j < 3; j++) {
+ for (size_t i = 0; i < 3; i++) {
+ JXL_RETURN_IF_ERROR(WriteICCS15Fixed16(chad[j][i], tags->size(), tags));
+ }
}
return true;
}
@@ -573,8 +587,8 @@ static Status CreateICCCurvParaTag(std::vector<float> params, size_t curve_type,
WriteICCUint32(0, tags->size(), tags);
WriteICCUint16(curve_type, tags->size(), tags);
WriteICCUint16(0, tags->size(), tags);
- for (size_t i = 0; i < params.size(); i++) {
- JXL_RETURN_IF_ERROR(WriteICCS15Fixed16(params[i], tags->size(), tags));
+ for (float param : params) {
+ JXL_RETURN_IF_ERROR(WriteICCS15Fixed16(param, tags->size(), tags));
}
return true;
}
@@ -649,8 +663,8 @@ static Status CreateICCLutAtoBTagForXYB(std::vector<uint8_t>* tags) {
-0.050022, 0.5683655, -0.018344,
-1.387676, 1.1145555, 0.6857255};
// 12 * 4 = 48 bytes
- for (size_t i = 0; i < 9; ++i) {
- JXL_RETURN_IF_ERROR(WriteICCS15Fixed16(matrix[i], tags->size(), tags));
+ for (double v : matrix) {
+ JXL_RETURN_IF_ERROR(WriteICCS15Fixed16(v, tags->size(), tags));
}
for (size_t i = 0; i < 3; ++i) {
float intercept = 0;
@@ -880,7 +894,9 @@ static std::string ColorEncodingDescriptionImpl(const JxlColorEncoding& c) {
static Status MaybeCreateProfileImpl(const JxlColorEncoding& c,
std::vector<uint8_t>* icc) {
- std::vector<uint8_t> header, tagtable, tags;
+ std::vector<uint8_t> header;
+ std::vector<uint8_t> tagtable;
+ std::vector<uint8_t> tags;
JxlTransferFunction tf = c.transfer_function;
if (c.color_space == JXL_COLOR_SPACE_UNKNOWN ||
tf == JXL_TRANSFER_FUNCTION_UNKNOWN) {
@@ -910,7 +926,8 @@ static Status MaybeCreateProfileImpl(const JxlColorEncoding& c,
// tag count, deferred to later
WriteICCUint32(0, tagtable.size(), &tagtable);
- size_t tag_offset = 0, tag_size = 0;
+ size_t tag_offset = 0;
+ size_t tag_size = 0;
CreateICCMlucTag(ColorEncodingDescriptionImpl(c), &tags);
FinalizeICCTag(&tags, &tag_offset, &tag_size);
@@ -923,12 +940,12 @@ static Status MaybeCreateProfileImpl(const JxlColorEncoding& c,
// TODO(eustas): isn't it the other way round: gray image has d50 WhitePoint?
if (c.color_space == JXL_COLOR_SPACE_GRAY) {
- float wtpt[3];
+ Color wtpt;
JXL_RETURN_IF_ERROR(
CIEXYZFromWhiteCIExy(c.white_point_xy[0], c.white_point_xy[1], wtpt));
JXL_RETURN_IF_ERROR(CreateICCXYZTag(wtpt, &tags));
} else {
- float d50[3] = {0.964203, 1.0, 0.824905};
+ Color d50{0.964203, 1.0, 0.824905};
JXL_RETURN_IF_ERROR(CreateICCXYZTag(d50, &tags));
}
FinalizeICCTag(&tags, &tag_offset, &tag_size);
@@ -936,7 +953,7 @@ static Status MaybeCreateProfileImpl(const JxlColorEncoding& c,
if (c.color_space != JXL_COLOR_SPACE_GRAY) {
// Chromatic adaptation matrix
- float chad[9];
+ Matrix3x3 chad;
JXL_RETURN_IF_ERROR(
CreateICCChadMatrix(c.white_point_xy[0], c.white_point_xy[1], chad));
@@ -949,14 +966,14 @@ static Status MaybeCreateProfileImpl(const JxlColorEncoding& c,
MaybeCreateICCCICPTag(c, &tags, &tag_offset, &tag_size, &tagtable,
&offsets);
- float m[9];
+ Matrix3x3 m;
JXL_RETURN_IF_ERROR(CreateICCRGBMatrix(
c.primaries_red_xy[0], c.primaries_red_xy[1], c.primaries_green_xy[0],
c.primaries_green_xy[1], c.primaries_blue_xy[0], c.primaries_blue_xy[1],
c.white_point_xy[0], c.white_point_xy[1], m));
- float r[3] = {m[0], m[3], m[6]};
- float g[3] = {m[1], m[4], m[7]};
- float b[3] = {m[2], m[5], m[8]};
+ Color r{m[0][0], m[1][0], m[2][0]};
+ Color g{m[0][1], m[1][1], m[2][1]};
+ Color b{m[0][2], m[1][2], m[2][2]};
JXL_RETURN_IF_ERROR(CreateICCXYZTag(r, &tags));
FinalizeICCTag(&tags, &tag_offset, &tag_size);
@@ -1042,8 +1059,8 @@ static Status MaybeCreateProfileImpl(const JxlColorEncoding& c,
WriteICCUint32(header.size() + tagtable.size() + tags.size(), 0, &header);
*icc = header;
- Bytes(tagtable).AppendTo(icc);
- Bytes(tags).AppendTo(icc);
+ Bytes(tagtable).AppendTo(*icc);
+ Bytes(tags).AppendTo(*icc);
// The MD5 checksum must be computed on the profile with profile flags,
// rendering intent, and region of the checksum itself, set to 0.
diff --git a/third_party/jpeg-xl/lib/jxl/cms/opsin_params.h b/third_party/jpeg-xl/lib/jxl/cms/opsin_params.h
index 48e8e254f7..69dcd0b512 100644
--- a/third_party/jpeg-xl/lib/jxl/cms/opsin_params.h
+++ b/third_party/jpeg-xl/lib/jxl/cms/opsin_params.h
@@ -6,7 +6,7 @@
#ifndef LIB_JXL_CMS_OPSIN_PARAMS_H_
#define LIB_JXL_CMS_OPSIN_PARAMS_H_
-#include <array>
+#include "lib/jxl/base/matrix_ops.h"
// Constants that define the XYB color space.
@@ -35,21 +35,20 @@ constexpr float kOpsinAbsorbanceBias1 = kOpsinAbsorbanceBias0;
constexpr float kOpsinAbsorbanceBias2 = kOpsinAbsorbanceBias0;
// Opsin absorbance matrix is now frozen.
-constexpr std::array<float, 9> kOpsinAbsorbanceMatrix = {
- kM00, kM01, kM02, kM10, kM11, kM12, kM20, kM21, kM22,
-};
+constexpr Matrix3x3 kOpsinAbsorbanceMatrix{
+ {{kM00, kM01, kM02}, {kM10, kM11, kM12}, {kM20, kM21, kM22}}};
-constexpr std::array<float, 9> kDefaultInverseOpsinAbsorbanceMatrix = {
- 11.031566901960783f, -9.866943921568629f, -0.16462299647058826f,
- -3.254147380392157f, 4.418770392156863f, -0.16462299647058826f,
- -3.6588512862745097f, 2.7129230470588235f, 1.9459282392156863f};
+constexpr Matrix3x3 kDefaultInverseOpsinAbsorbanceMatrix{
+ {{11.031566901960783f, -9.866943921568629f, -0.16462299647058826f},
+ {-3.254147380392157f, 4.418770392156863f, -0.16462299647058826f},
+ {-3.6588512862745097f, 2.7129230470588235f, 1.9459282392156863f}}};
// Must be the inverse matrix of kOpsinAbsorbanceMatrix and match the spec.
-static inline const float* DefaultInverseOpsinAbsorbanceMatrix() {
- return kDefaultInverseOpsinAbsorbanceMatrix.data();
+static inline const Matrix3x3& DefaultInverseOpsinAbsorbanceMatrix() {
+ return kDefaultInverseOpsinAbsorbanceMatrix;
}
-constexpr std::array<float, 3> kOpsinAbsorbanceBias = {
+constexpr Vector3 kOpsinAbsorbanceBias = {
kOpsinAbsorbanceBias0,
kOpsinAbsorbanceBias1,
kOpsinAbsorbanceBias2,
@@ -63,14 +62,14 @@ constexpr float kScaledXYBOffset0 = 0.015386134f;
constexpr float kScaledXYBOffset1 = 0.0f;
constexpr float kScaledXYBOffset2 = 0.27770459f;
-constexpr std::array<float, 3> kScaledXYBOffset = {
- kScaledXYBOffset0, kScaledXYBOffset1, kScaledXYBOffset2};
+constexpr Vector3 kScaledXYBOffset = {kScaledXYBOffset0, kScaledXYBOffset1,
+ kScaledXYBOffset2};
constexpr float kScaledXYBScale0 = 22.995788804f;
constexpr float kScaledXYBScale1 = 1.183000077f;
constexpr float kScaledXYBScale2 = 1.502141333f;
-constexpr std::array<float, 3> kScaledXYBScale = {
+constexpr Vector3 kScaledXYBScale = {
kScaledXYBScale0,
kScaledXYBScale1,
kScaledXYBScale2,
diff --git a/third_party/jpeg-xl/lib/jxl/cms/tone_mapping-inl.h b/third_party/jpeg-xl/lib/jxl/cms/tone_mapping-inl.h
index 3d94ccea12..7d09beed32 100644
--- a/third_party/jpeg-xl/lib/jxl/cms/tone_mapping-inl.h
+++ b/third_party/jpeg-xl/lib/jxl/cms/tone_mapping-inl.h
@@ -100,14 +100,14 @@ class HlgOOTF : HlgOOTF_Base {
using HlgOOTF_Base::HlgOOTF_Base;
static HlgOOTF FromSceneLight(float display_luminance,
- const float primaries_luminances[3]) {
+ const Vector3& primaries_luminances) {
return HlgOOTF(/*gamma=*/1.2f *
std::pow(1.111f, std::log2(display_luminance / 1000.f)),
primaries_luminances);
}
static HlgOOTF ToSceneLight(float display_luminance,
- const float primaries_luminances[3]) {
+ const Vector3& primaries_luminances) {
return HlgOOTF(
/*gamma=*/(1 / 1.2f) *
std::pow(1.111f, -std::log2(display_luminance / 1000.f)),
@@ -132,7 +132,7 @@ class HlgOOTF : HlgOOTF_Base {
};
template <typename V>
-void GamutMap(V* red, V* green, V* blue, const float primaries_luminances[3],
+void GamutMap(V* red, V* green, V* blue, const Vector3& primaries_luminances,
float preserve_saturation = 0.1f) {
hwy::HWY_NAMESPACE::DFromV<V> df;
const V luminance =
diff --git a/third_party/jpeg-xl/lib/jxl/cms/tone_mapping.h b/third_party/jpeg-xl/lib/jxl/cms/tone_mapping.h
index a114109ea6..81f301a31d 100644
--- a/third_party/jpeg-xl/lib/jxl/cms/tone_mapping.h
+++ b/third_party/jpeg-xl/lib/jxl/cms/tone_mapping.h
@@ -12,6 +12,7 @@
#include "lib/jxl/base/common.h"
#include "lib/jxl/base/compiler_specific.h"
+#include "lib/jxl/base/matrix_ops.h"
#include "lib/jxl/cms/transfer_functions.h"
namespace jxl {
@@ -20,7 +21,7 @@ class Rec2408ToneMapperBase {
public:
explicit Rec2408ToneMapperBase(std::pair<float, float> source_range,
std::pair<float, float> target_range,
- const float primaries_luminances[3])
+ const Vector3& primaries_luminances)
: source_range_(source_range),
target_range_(target_range),
red_Y_(primaries_luminances[0]),
@@ -28,10 +29,10 @@ class Rec2408ToneMapperBase {
blue_Y_(primaries_luminances[2]) {}
// TODO(eustas): test me
- void ToneMap(float* red, float* green, float* blue) const {
+ void ToneMap(Color& rgb) const {
const float luminance =
source_range_.second *
- (red_Y_ * *red + green_Y_ * *green + blue_Y_ * *blue);
+ (red_Y_ * rgb[0] + green_Y_ * rgb[1] + blue_Y_ * rgb[2]);
const float normalized_pq =
std::min(1.f, (InvEOTF(luminance) - pq_mastering_min_) *
inv_pq_mastering_range_);
@@ -49,8 +50,8 @@ class Rec2408ToneMapperBase {
const float ratio = new_luminance / std::max(luminance, min_luminance);
const float cap = new_luminance * inv_target_peak_;
const float multiplier = ratio * normalizer_;
- for (float* const val : {red, green, blue}) {
- *val = use_cap ? cap : *val * multiplier;
+ for (size_t idx : {0, 1, 2}) {
+ rgb[idx] = use_cap ? cap : rgb[idx] * multiplier;
}
}
@@ -96,23 +97,24 @@ class Rec2408ToneMapperBase {
class HlgOOTF_Base {
public:
explicit HlgOOTF_Base(float source_luminance, float target_luminance,
- const float primaries_luminances[3])
+ const Vector3& primaries_luminances)
: HlgOOTF_Base(/*gamma=*/std::pow(1.111f, std::log2(target_luminance /
source_luminance)),
primaries_luminances) {}
// TODO(eustas): test me
- void Apply(float* red, float* green, float* blue) const {
+ void Apply(Color& rgb) const {
if (!apply_ootf_) return;
- const float luminance = red_Y_ * *red + green_Y_ * *green + blue_Y_ * *blue;
+ const float luminance =
+ red_Y_ * rgb[0] + green_Y_ * rgb[1] + blue_Y_ * rgb[2];
const float ratio = std::min<float>(powf(luminance, exponent_), 1e9);
- *red *= ratio;
- *green *= ratio;
- *blue *= ratio;
+ rgb[0] *= ratio;
+ rgb[1] *= ratio;
+ rgb[2] *= ratio;
}
protected:
- explicit HlgOOTF_Base(float gamma, const float luminances[3])
+ explicit HlgOOTF_Base(float gamma, const Vector3& luminances)
: exponent_(gamma - 1),
red_Y_(luminances[0]),
green_Y_(luminances[1]),
@@ -124,13 +126,12 @@ class HlgOOTF_Base {
const float blue_Y_;
};
-static JXL_MAYBE_UNUSED void GamutMapScalar(float* red, float* green,
- float* blue,
- const float primaries_luminances[3],
+static JXL_MAYBE_UNUSED void GamutMapScalar(Color& rgb,
+ const Vector3& primaries_luminances,
float preserve_saturation = 0.1f) {
- const float luminance = primaries_luminances[0] * *red +
- primaries_luminances[1] * *green +
- primaries_luminances[2] * *blue;
+ const float luminance = primaries_luminances[0] * rgb[0] +
+ primaries_luminances[1] * rgb[1] +
+ primaries_luminances[2] * rgb[2];
// Desaturate out-of-gamut pixels. This is done by mixing each pixel
// with just enough gray of the target luminance to make all
@@ -142,8 +143,8 @@ static JXL_MAYBE_UNUSED void GamutMapScalar(float* red, float* green,
// done by mixing in yet more gray. That will desaturate it further.
float gray_mix_saturation = 0.0f;
float gray_mix_luminance = 0.0f;
- for (const float* ch : {red, green, blue}) {
- const float& val = *ch;
+ for (size_t idx : {0, 1, 2}) {
+ const float& val = rgb[idx];
const float val_minus_gray = val - luminance;
const float inv_val_minus_gray =
1.0f / ((val_minus_gray == 0.0f) ? 1.0f : val_minus_gray);
@@ -162,15 +163,14 @@ static JXL_MAYBE_UNUSED void GamutMapScalar(float* red, float* green,
Clamp1((preserve_saturation * (gray_mix_saturation - gray_mix_luminance) +
gray_mix_luminance),
0.0f, 1.0f);
- for (float* const ch : {red, green, blue}) {
- float& val = *ch;
+ for (size_t idx : {0, 1, 2}) {
+ float& val = rgb[idx];
val = gray_mix * (luminance - val) + val;
}
- const float max_clr = std::max({1.0f, *red, *green, *blue});
+ const float max_clr = std::max({1.0f, rgb[0], rgb[1], rgb[2]});
const float normalizer = 1.0f / max_clr;
- for (float* const ch : {red, green, blue}) {
- float& val = *ch;
- val *= normalizer;
+ for (size_t idx : {0, 1, 2}) {
+ rgb[idx] *= normalizer;
}
}
diff --git a/third_party/jpeg-xl/lib/jxl/cms/tone_mapping_test.cc b/third_party/jpeg-xl/lib/jxl/cms/tone_mapping_test.cc
index dda2bbb0aa..76165d26e7 100644
--- a/third_party/jpeg-xl/lib/jxl/cms/tone_mapping_test.cc
+++ b/third_party/jpeg-xl/lib/jxl/cms/tone_mapping_test.cc
@@ -30,17 +30,17 @@ HWY_NOINLINE void TestRec2408ToneMap() {
for (size_t i = 0; i < kNumTrials; i++) {
float src = 11000.0 + rng.UniformF(-150.0f, 150.0f);
float tgt = 250 + rng.UniformF(-5.0f, 5.0f);
- float luminances[3] = {rng.UniformF(0.2f, 0.4f), rng.UniformF(0.2f, 0.4f),
- rng.UniformF(0.2f, 0.4f)};
- float rgb[3] = {rng.UniformF(0.0f, 1.0f), rng.UniformF(0.0f, 1.0f),
- rng.UniformF(0.0f, 1.0f)};
+ Vector3 luminances{rng.UniformF(0.2f, 0.4f), rng.UniformF(0.2f, 0.4f),
+ rng.UniformF(0.2f, 0.4f)};
+ Color rgb{rng.UniformF(0.0f, 1.0f), rng.UniformF(0.0f, 1.0f),
+ rng.UniformF(0.0f, 1.0f)};
Rec2408ToneMapper<decltype(d)> tone_mapper({0, src}, {0, tgt}, luminances);
auto r = Set(d, rgb[0]);
auto g = Set(d, rgb[1]);
auto b = Set(d, rgb[2]);
tone_mapper.ToneMap(&r, &g, &b);
Rec2408ToneMapperBase tone_mapper_base({0, src}, {0, tgt}, luminances);
- tone_mapper_base.ToneMap(&rgb[0], &rgb[1], &rgb[2]);
+ tone_mapper_base.ToneMap(rgb);
const float actual_r = GetLane(r);
const float expected_r = rgb[0];
const float abs_err_r = std::abs(expected_r - actual_r);
@@ -66,17 +66,17 @@ HWY_NOINLINE void TestHlgOotfApply() {
for (size_t i = 0; i < kNumTrials; i++) {
float src = 300.0 + rng.UniformF(-50.0f, 50.0f);
float tgt = 80 + rng.UniformF(-5.0f, 5.0f);
- float luminances[3] = {rng.UniformF(0.2f, 0.4f), rng.UniformF(0.2f, 0.4f),
- rng.UniformF(0.2f, 0.4f)};
- float rgb[3] = {rng.UniformF(0.0f, 1.0f), rng.UniformF(0.0f, 1.0f),
- rng.UniformF(0.0f, 1.0f)};
+ Vector3 luminances{rng.UniformF(0.2f, 0.4f), rng.UniformF(0.2f, 0.4f),
+ rng.UniformF(0.2f, 0.4f)};
+ Color rgb{rng.UniformF(0.0f, 1.0f), rng.UniformF(0.0f, 1.0f),
+ rng.UniformF(0.0f, 1.0f)};
HlgOOTF ootf(src, tgt, luminances);
auto r = Set(d, rgb[0]);
auto g = Set(d, rgb[1]);
auto b = Set(d, rgb[2]);
ootf.Apply(&r, &g, &b);
HlgOOTF_Base ootf_base(src, tgt, luminances);
- ootf_base.Apply(&rgb[0], &rgb[1], &rgb[2]);
+ ootf_base.Apply(rgb);
const float actual_r = GetLane(r);
const float expected_r = rgb[0];
const float abs_err_r = std::abs(expected_r - actual_r);
@@ -101,15 +101,15 @@ HWY_NOINLINE void TestGamutMap() {
HWY_FULL(float) d;
for (size_t i = 0; i < kNumTrials; i++) {
float preserve_saturation = rng.UniformF(0.2f, 0.4f);
- float luminances[3] = {rng.UniformF(0.2f, 0.4f), rng.UniformF(0.2f, 0.4f),
- rng.UniformF(0.2f, 0.4f)};
- float rgb[3] = {rng.UniformF(0.0f, 1.0f), rng.UniformF(0.0f, 1.0f),
- rng.UniformF(0.0f, 1.0f)};
+ Vector3 luminances{rng.UniformF(0.2f, 0.4f), rng.UniformF(0.2f, 0.4f),
+ rng.UniformF(0.2f, 0.4f)};
+ Color rgb{rng.UniformF(0.0f, 1.0f), rng.UniformF(0.0f, 1.0f),
+ rng.UniformF(0.0f, 1.0f)};
auto r = Set(d, rgb[0]);
auto g = Set(d, rgb[1]);
auto b = Set(d, rgb[2]);
GamutMap(&r, &g, &b, luminances, preserve_saturation);
- GamutMapScalar(&rgb[0], &rgb[1], &rgb[2], luminances, preserve_saturation);
+ GamutMapScalar(rgb, luminances, preserve_saturation);
const float actual_r = GetLane(r);
const float expected_r = rgb[0];
const float abs_err_r = std::abs(expected_r - actual_r);
diff --git a/third_party/jpeg-xl/lib/jxl/coeff_order.h b/third_party/jpeg-xl/lib/jxl/coeff_order.h
index 75f6f99e9f..79c0c976c9 100644
--- a/third_party/jpeg-xl/lib/jxl/coeff_order.h
+++ b/third_party/jpeg-xl/lib/jxl/coeff_order.h
@@ -10,10 +10,10 @@
#include <stdint.h>
#include "lib/jxl/ac_strategy.h"
+#include "lib/jxl/base/common.h"
#include "lib/jxl/base/compiler_specific.h"
#include "lib/jxl/base/status.h"
#include "lib/jxl/coeff_order_fwd.h"
-#include "lib/jxl/dct_util.h"
#include "lib/jxl/frame_dimensions.h"
namespace jxl {
@@ -21,22 +21,21 @@ namespace jxl {
class BitReader;
// Those offsets get multiplied by kDCTBlockSize.
-static constexpr size_t kCoeffOrderOffset[] = {
+
+static constexpr size_t kCoeffOrderLimit = 6156;
+
+static constexpr std::array<size_t, 3 * kNumOrders + 1> kCoeffOrderOffset = {
0, 1, 2, 3, 4, 5, 6, 10, 14, 18,
34, 50, 66, 68, 70, 72, 76, 80, 84, 92,
100, 108, 172, 236, 300, 332, 364, 396, 652, 908,
- 1164, 1292, 1420, 1548, 2572, 3596, 4620, 5132, 5644, 6156,
-};
-static_assert(3 * kNumOrders + 1 ==
- sizeof(kCoeffOrderOffset) / sizeof(*kCoeffOrderOffset),
- "Update this array when adding or removing order types.");
+ 1164, 1292, 1420, 1548, 2572, 3596, 4620, 5132, 5644, kCoeffOrderLimit};
-static constexpr size_t CoeffOrderOffset(size_t order, size_t c) {
- return kCoeffOrderOffset[3 * order + c] * kDCTBlockSize;
-}
+// TODO(eustas): rollback to constexpr once modern C++ becomes reuired.
+#define CoeffOrderOffset(O, C) \
+ (kCoeffOrderOffset[3 * (O) + (C)] * kDCTBlockSize)
-static constexpr size_t kCoeffOrderMaxSize =
- kCoeffOrderOffset[3 * kNumOrders] * kDCTBlockSize;
+static JXL_MAYBE_UNUSED constexpr size_t kCoeffOrderMaxSize =
+ kCoeffOrderLimit * kDCTBlockSize;
// Mapping from AC strategy to order bucket. Strategies with different natural
// orders must have different buckets.
@@ -49,7 +48,7 @@ static_assert(AcStrategy::kNumValidStrategies ==
sizeof(kStrategyOrder) / sizeof(*kStrategyOrder),
"Update this array when adding or removing AC strategies.");
-constexpr uint32_t kPermutationContexts = 8;
+constexpr JXL_MAYBE_UNUSED uint32_t kPermutationContexts = 8;
uint32_t CoeffOrderContext(uint32_t val);
diff --git a/third_party/jpeg-xl/lib/jxl/color_encoding_internal.cc b/third_party/jpeg-xl/lib/jxl/color_encoding_internal.cc
index 19273dad3c..895faaa07d 100644
--- a/third_party/jpeg-xl/lib/jxl/color_encoding_internal.cc
+++ b/third_party/jpeg-xl/lib/jxl/color_encoding_internal.cc
@@ -47,12 +47,12 @@ std::array<ColorEncoding, 2> ColorEncoding::CreateC2(Primaries pr,
const ColorEncoding& ColorEncoding::SRGB(bool is_gray) {
static std::array<ColorEncoding, 2> c2 =
CreateC2(Primaries::kSRGB, TransferFunction::kSRGB);
- return c2[is_gray];
+ return c2[is_gray ? 1 : 0];
}
const ColorEncoding& ColorEncoding::LinearSRGB(bool is_gray) {
static std::array<ColorEncoding, 2> c2 =
CreateC2(Primaries::kSRGB, TransferFunction::kLinear);
- return c2[is_gray];
+ return c2[is_gray ? 1 : 0];
}
Status ColorEncoding::SetWhitePointType(const WhitePoint& wp) {
diff --git a/third_party/jpeg-xl/lib/jxl/color_encoding_internal.h b/third_party/jpeg-xl/lib/jxl/color_encoding_internal.h
index 0a104a12b2..61e4628dbd 100644
--- a/third_party/jpeg-xl/lib/jxl/color_encoding_internal.h
+++ b/third_party/jpeg-xl/lib/jxl/color_encoding_internal.h
@@ -314,7 +314,6 @@ class ColorSpaceTransform {
Status Init(const ColorEncoding& c_src, const ColorEncoding& c_dst,
float intensity_target, size_t xsize, size_t num_threads) {
- xsize_ = xsize;
JxlColorProfile input_profile;
icc_src_ = c_src.ICC();
input_profile.icc.data = icc_src_.data();
@@ -343,8 +342,10 @@ class ColorSpaceTransform {
return cms_.get_dst_buf(cms_data_, thread);
}
- Status Run(const size_t thread, const float* buf_src, float* buf_dst) {
- return cms_.run(cms_data_, thread, buf_src, buf_dst, xsize_);
+ Status Run(const size_t thread, const float* buf_src, float* buf_dst,
+ size_t xsize) {
+ // TODO(eustas): convert false to Status?
+ return FROM_JXL_BOOL(cms_.run(cms_data_, thread, buf_src, buf_dst, xsize));
}
private:
@@ -353,7 +354,6 @@ class ColorSpaceTransform {
// The interface may retain pointers into these.
IccBytes icc_src_;
IccBytes icc_dst_;
- size_t xsize_;
};
} // namespace jxl
diff --git a/third_party/jpeg-xl/lib/jxl/color_encoding_internal_test.cc b/third_party/jpeg-xl/lib/jxl/color_encoding_internal_test.cc
index 4d2d3e8119..2a9f60427a 100644
--- a/third_party/jpeg-xl/lib/jxl/color_encoding_internal_test.cc
+++ b/third_party/jpeg-xl/lib/jxl/color_encoding_internal_test.cc
@@ -97,24 +97,29 @@ TEST(ColorEncodingTest, InternalExternalConversion) {
ColorEncoding source_internal;
ColorEncoding destination_internal;
+ const auto rand_float = []() {
+ return (static_cast<float>(rand()) / static_cast<float>(RAND_MAX) * 0.5) +
+ 0.25;
+ };
+
for (int i = 0; i < 100; i++) {
source_internal.color_space = static_cast<ColorSpace>(rand() % 4);
CIExy wp;
- wp.x = (float(rand()) / float((RAND_MAX)) * 0.5) + 0.25;
- wp.y = (float(rand()) / float((RAND_MAX)) * 0.5) + 0.25;
+ wp.x = rand_float();
+ wp.y = rand_float();
EXPECT_TRUE(source_internal.SetWhitePoint(wp));
if (source_internal.HasPrimaries()) {
PrimariesCIExy primaries;
- primaries.r.x = (float(rand()) / float((RAND_MAX)) * 0.5) + 0.25;
- primaries.r.y = (float(rand()) / float((RAND_MAX)) * 0.5) + 0.25;
- primaries.g.x = (float(rand()) / float((RAND_MAX)) * 0.5) + 0.25;
- primaries.g.y = (float(rand()) / float((RAND_MAX)) * 0.5) + 0.25;
- primaries.b.x = (float(rand()) / float((RAND_MAX)) * 0.5) + 0.25;
- primaries.b.y = (float(rand()) / float((RAND_MAX)) * 0.5) + 0.25;
+ primaries.r.x = rand_float();
+ primaries.r.y = rand_float();
+ primaries.g.x = rand_float();
+ primaries.g.y = rand_float();
+ primaries.b.x = rand_float();
+ primaries.b.y = rand_float();
EXPECT_TRUE(source_internal.SetPrimaries(primaries));
}
jxl::cms::CustomTransferFunction tf;
- EXPECT_TRUE(tf.SetGamma((float(rand()) / float((RAND_MAX)) * 0.5) + 0.25));
+ EXPECT_TRUE(tf.SetGamma(rand_float()));
source_internal.tf = tf;
source_internal.rendering_intent = static_cast<RenderingIntent>(rand() % 4);
diff --git a/third_party/jpeg-xl/lib/jxl/color_management_test.cc b/third_party/jpeg-xl/lib/jxl/color_management_test.cc
index ca50c9960e..b2d47c73f9 100644
--- a/third_party/jpeg-xl/lib/jxl/color_management_test.cc
+++ b/third_party/jpeg-xl/lib/jxl/color_management_test.cc
@@ -45,55 +45,10 @@ std::ostream& operator<<(std::ostream& os, const PrimariesCIExy& primaries) {
namespace {
-using ::testing::ElementsAre;
-using ::testing::FloatNear;
-
// Small enough to be fast. If changed, must update Generate*.
-static constexpr size_t kWidth = 16;
-
-static constexpr size_t kNumThreads = 1; // only have a single row.
+constexpr size_t kWidth = 16;
-MATCHER_P(HasSameFieldsAs, expected, "") {
- if (arg.GetRenderingIntent() != expected.GetRenderingIntent()) {
- *result_listener << "which has a different rendering intent: "
- << ToString(arg.GetRenderingIntent()) << " instead of "
- << ToString(expected.GetRenderingIntent());
- return false;
- }
- if (arg.GetColorSpace() != expected.GetColorSpace()) {
- *result_listener << "which has a different color space: "
- << ToString(arg.GetColorSpace()) << " instead of "
- << ToString(expected.GetColorSpace());
- return false;
- }
- if (arg.GetWhitePointType() != expected.GetWhitePointType()) {
- *result_listener << "which has a different white point: "
- << ToString(arg.GetWhitePointType()) << " instead of "
- << ToString(expected.GetWhitePointType());
- return false;
- }
- if (arg.HasPrimaries() &&
- arg.GetPrimariesType() != expected.GetPrimariesType()) {
- *result_listener << "which has different primaries: "
- << ToString(arg.GetPrimariesType()) << " instead of "
- << ToString(expected.GetPrimariesType());
- return false;
- }
- if (!arg.Tf().IsSame(expected.Tf())) {
- static const auto tf_to_string =
- [](const jxl::cms::CustomTransferFunction& tf) {
- if (tf.have_gamma) {
- return "g" + ToString(tf.GetGamma());
- }
- return ToString(tf.transfer_function);
- };
- *result_listener << "which has a different transfer function: "
- << tf_to_string(arg.Tf()) << " instead of "
- << tf_to_string(expected.Tf());
- return false;
- }
- return true;
-}
+constexpr size_t kNumThreads = 1; // only have a single row.
struct Globals {
// TODO(deymo): Make this a const.
@@ -106,15 +61,15 @@ struct Globals {
Globals() {
in_gray = GenerateGray();
in_color = GenerateColor();
- out_gray = ImageF(kWidth, 1);
- out_color = ImageF(kWidth * 3, 1);
+ JXL_ASSIGN_OR_DIE(out_gray, ImageF::Create(kWidth, 1));
+ JXL_ASSIGN_OR_DIE(out_color, ImageF::Create(kWidth * 3, 1));
c_native = ColorEncoding::LinearSRGB(/*is_gray=*/false);
c_gray = ColorEncoding::LinearSRGB(/*is_gray=*/true);
}
static ImageF GenerateGray() {
- ImageF gray(kWidth, 1);
+ JXL_ASSIGN_OR_DIE(ImageF gray, ImageF::Create(kWidth, 1));
float* JXL_RESTRICT row = gray.Row(0);
// Increasing left to right
for (uint32_t x = 0; x < kWidth; ++x) {
@@ -124,7 +79,7 @@ struct Globals {
}
static ImageF GenerateColor() {
- ImageF image(kWidth * 3, 1);
+ JXL_ASSIGN_OR_DIE(ImageF image, ImageF::Create(kWidth * 3, 1));
float* JXL_RESTRICT interleaved = image.Row(0);
std::fill(interleaved, interleaved + kWidth * 3, 0.0f);
@@ -182,8 +137,10 @@ class ColorManagementTest
const size_t thread = 0;
const ImageF& in = c.IsGray() ? g->in_gray : g->in_color;
ImageF* JXL_RESTRICT out = c.IsGray() ? &g->out_gray : &g->out_color;
- ASSERT_TRUE(xform_fwd.Run(thread, in.Row(0), xform_fwd.BufDst(thread)));
- ASSERT_TRUE(xform_rev.Run(thread, xform_fwd.BufDst(thread), out->Row(0)));
+ ASSERT_TRUE(
+ xform_fwd.Run(thread, in.Row(0), xform_fwd.BufDst(thread), kWidth));
+ ASSERT_TRUE(
+ xform_rev.Run(thread, xform_fwd.BufDst(thread), out->Row(0), kWidth));
// With lcms2, this value is lower: 5E-5
double max_l1 = 7E-4;
@@ -200,77 +157,108 @@ JXL_GTEST_INSTANTIATE_TEST_SUITE_P(ColorManagementTestInstantiation,
// Exercises the ColorManagement interface for ALL ColorEncoding synthesizable
// via enums.
TEST_P(ColorManagementTest, VerifyAllProfiles) {
- ColorEncoding c = ColorEncodingFromDescriptor(GetParam());
- printf("%s\n", Description(c).c_str());
+ ColorEncoding actual = ColorEncodingFromDescriptor(GetParam());
+ printf("%s\n", Description(actual).c_str());
// Can create profile.
- ASSERT_TRUE(c.CreateICC());
+ ASSERT_TRUE(actual.CreateICC());
// Can set an equivalent ColorEncoding from the generated ICC profile.
- ColorEncoding c3;
- ASSERT_TRUE(c3.SetICC(IccBytes(c.ICC()), JxlGetDefaultCms()));
- EXPECT_THAT(c3, HasSameFieldsAs(c));
+ ColorEncoding expected;
+ ASSERT_TRUE(expected.SetICC(IccBytes(actual.ICC()), JxlGetDefaultCms()));
+
+ EXPECT_EQ(actual.GetRenderingIntent(), expected.GetRenderingIntent())
+ << "different rendering intent: " << ToString(actual.GetRenderingIntent())
+ << " instead of " << ToString(expected.GetRenderingIntent());
+ EXPECT_EQ(actual.GetColorSpace(), expected.GetColorSpace())
+ << "different color space: " << ToString(actual.GetColorSpace())
+ << " instead of " << ToString(expected.GetColorSpace());
+ EXPECT_EQ(actual.GetWhitePointType(), expected.GetWhitePointType())
+ << "different white point: " << ToString(actual.GetWhitePointType())
+ << " instead of " << ToString(expected.GetWhitePointType());
+ EXPECT_EQ(actual.HasPrimaries(), expected.HasPrimaries());
+ if (actual.HasPrimaries()) {
+ EXPECT_EQ(actual.GetPrimariesType(), expected.GetPrimariesType())
+ << "different primaries: " << ToString(actual.GetPrimariesType())
+ << " instead of " << ToString(expected.GetPrimariesType());
+ }
- VerifyPixelRoundTrip(c);
-}
+ static const auto tf_to_string =
+ [](const jxl::cms::CustomTransferFunction& tf) {
+ if (tf.have_gamma) {
+ return "g" + ToString(tf.GetGamma());
+ }
+ return ToString(tf.transfer_function);
+ };
+ EXPECT_TRUE(actual.Tf().IsSame(expected.Tf()))
+ << "different transfer function: " << tf_to_string(actual.Tf())
+ << " instead of " << tf_to_string(expected.Tf());
-testing::Matcher<CIExy> CIExyIs(const double x, const double y) {
- static constexpr double kMaxError = 1e-4;
- return testing::AllOf(
- testing::Field(&CIExy::x, testing::DoubleNear(x, kMaxError)),
- testing::Field(&CIExy::y, testing::DoubleNear(y, kMaxError)));
+ VerifyPixelRoundTrip(actual);
}
-testing::Matcher<PrimariesCIExy> PrimariesAre(
- const testing::Matcher<CIExy>& r, const testing::Matcher<CIExy>& g,
- const testing::Matcher<CIExy>& b) {
- return testing::AllOf(testing::Field(&PrimariesCIExy::r, r),
- testing::Field(&PrimariesCIExy::g, g),
- testing::Field(&PrimariesCIExy::b, b));
-}
+#define EXPECT_CIEXY_NEAR(A, E, T) \
+ { \
+ CIExy _actual = (A); \
+ CIExy _expected = (E); \
+ double _tolerance = (T); \
+ EXPECT_NEAR(_actual.x, _expected.x, _tolerance) << "x is different"; \
+ EXPECT_NEAR(_actual.y, _expected.y, _tolerance) << "y is different"; \
+ }
+
+#define EXPECT_PRIMARIES_NEAR(A, E, T) \
+ { \
+ PrimariesCIExy _actual = (A); \
+ PrimariesCIExy _expected = (E); \
+ double _tolerance = (T); \
+ EXPECT_NEAR(_actual.r.x, _expected.r.x, _tolerance) << "r.x is different"; \
+ EXPECT_NEAR(_actual.r.y, _expected.r.y, _tolerance) << "r.y is different"; \
+ EXPECT_NEAR(_actual.g.x, _expected.g.x, _tolerance) << "g.x is different"; \
+ EXPECT_NEAR(_actual.g.y, _expected.g.y, _tolerance) << "g.y is different"; \
+ EXPECT_NEAR(_actual.b.x, _expected.b.x, _tolerance) << "b.x is different"; \
+ EXPECT_NEAR(_actual.b.y, _expected.b.y, _tolerance) << "b.y is different"; \
+ }
TEST_F(ColorManagementTest, sRGBChromaticity) {
const ColorEncoding sRGB = ColorEncoding::SRGB();
- EXPECT_THAT(sRGB.GetWhitePoint(), CIExyIs(0.3127, 0.3290));
- EXPECT_THAT(sRGB.GetPrimaries(),
- PrimariesAre(CIExyIs(0.64, 0.33), CIExyIs(0.30, 0.60),
- CIExyIs(0.15, 0.06)));
+ EXPECT_CIEXY_NEAR(sRGB.GetWhitePoint(), CIExy(0.3127, 0.3290), 1e-4);
+ PrimariesCIExy srgb_primaries = {{0.64, 0.33}, {0.30, 0.60}, {0.15, 0.06}};
+ EXPECT_PRIMARIES_NEAR(sRGB.GetPrimaries(), srgb_primaries, 1e-4);
}
TEST_F(ColorManagementTest, D2700Chromaticity) {
std::vector<uint8_t> icc_data =
jxl::test::ReadTestData("jxl/color_management/sRGB-D2700.icc");
IccBytes icc;
- Bytes(icc_data).AppendTo(&icc);
+ Bytes(icc_data).AppendTo(icc);
ColorEncoding sRGB_D2700;
ASSERT_TRUE(sRGB_D2700.SetICC(std::move(icc), JxlGetDefaultCms()));
- EXPECT_THAT(sRGB_D2700.GetWhitePoint(), CIExyIs(0.45986, 0.41060));
+ EXPECT_CIEXY_NEAR(sRGB_D2700.GetWhitePoint(), CIExy(0.45986, 0.41060), 1e-4);
// The illuminant-relative chromaticities of this profile's primaries are the
// same as for sRGB. It is the PCS-relative chromaticities that would be
// different.
- EXPECT_THAT(sRGB_D2700.GetPrimaries(),
- PrimariesAre(CIExyIs(0.64, 0.33), CIExyIs(0.30, 0.60),
- CIExyIs(0.15, 0.06)));
+ PrimariesCIExy srgb_primaries = {{0.64, 0.33}, {0.30, 0.60}, {0.15, 0.06}};
+ EXPECT_PRIMARIES_NEAR(sRGB_D2700.GetPrimaries(), srgb_primaries, 1e-4);
}
TEST_F(ColorManagementTest, D2700ToSRGB) {
std::vector<uint8_t> icc_data =
jxl::test::ReadTestData("jxl/color_management/sRGB-D2700.icc");
IccBytes icc;
- Bytes(icc_data).AppendTo(&icc);
+ Bytes(icc_data).AppendTo(icc);
ColorEncoding sRGB_D2700;
ASSERT_TRUE(sRGB_D2700.SetICC(std::move(icc), JxlGetDefaultCms()));
ColorSpaceTransform transform(*JxlGetDefaultCms());
ASSERT_TRUE(transform.Init(sRGB_D2700, ColorEncoding::SRGB(),
kDefaultIntensityTarget, 1, 1));
- const float sRGB_D2700_values[3] = {0.863, 0.737, 0.490};
- float sRGB_values[3];
- ASSERT_TRUE(transform.Run(0, sRGB_D2700_values, sRGB_values));
- EXPECT_THAT(sRGB_values,
- ElementsAre(FloatNear(0.914, 1e-3), FloatNear(0.745, 1e-3),
- FloatNear(0.601, 1e-3)));
+ Color sRGB_D2700_values{0.863, 0.737, 0.490};
+ Color sRGB_values;
+ ASSERT_TRUE(
+ transform.Run(0, sRGB_D2700_values.data(), sRGB_values.data(), 1));
+ Color sRGB_expected{0.914, 0.745, 0.601};
+ EXPECT_ARRAY_NEAR(sRGB_values, sRGB_expected, 1e-3);
}
TEST_F(ColorManagementTest, P3HlgTo2020Hlg) {
@@ -287,12 +275,12 @@ TEST_F(ColorManagementTest, P3HlgTo2020Hlg) {
ColorSpaceTransform transform(*JxlGetDefaultCms());
ASSERT_TRUE(transform.Init(p3_hlg, rec2020_hlg, 1000, 1, 1));
- const float p3_hlg_values[3] = {0., 0.75, 0.};
- float rec2020_hlg_values[3];
- ASSERT_TRUE(transform.Run(0, p3_hlg_values, rec2020_hlg_values));
- EXPECT_THAT(rec2020_hlg_values,
- ElementsAre(FloatNear(0.3973, 1e-4), FloatNear(0.7382, 1e-4),
- FloatNear(0.1183, 1e-4)));
+ Color p3_hlg_values{0., 0.75, 0.};
+ Color rec2020_hlg_values;
+ ASSERT_TRUE(
+ transform.Run(0, p3_hlg_values.data(), rec2020_hlg_values.data(), 1));
+ Color rec2020_hlg_expected{0.3973, 0.7382, 0.1183};
+ EXPECT_ARRAY_NEAR(rec2020_hlg_values, rec2020_hlg_expected, 1e-4);
}
TEST_F(ColorManagementTest, HlgOotf) {
@@ -307,38 +295,34 @@ TEST_F(ColorManagementTest, HlgOotf) {
ASSERT_TRUE(
transform_to_1000.Init(p3_hlg, ColorEncoding::LinearSRGB(), 1000, 1, 1));
// HDR reference white: https://www.itu.int/pub/R-REP-BT.2408-4-2021
- float p3_hlg_values[3] = {0.75, 0.75, 0.75};
- float linear_srgb_values[3];
- ASSERT_TRUE(transform_to_1000.Run(0, p3_hlg_values, linear_srgb_values));
+ Color p3_hlg_values{0.75, 0.75, 0.75};
+ Color linear_srgb_values;
+ ASSERT_TRUE(transform_to_1000.Run(0, p3_hlg_values.data(),
+ linear_srgb_values.data(), 1));
// On a 1000-nit display, HDR reference white should be 203 cd/m² which is
// 0.203 times the maximum.
- EXPECT_THAT(linear_srgb_values,
- ElementsAre(FloatNear(0.203, 1e-3), FloatNear(0.203, 1e-3),
- FloatNear(0.203, 1e-3)));
+ EXPECT_ARRAY_NEAR(linear_srgb_values, (Color{0.203, 0.203, 0.203}), 1e-3);
ColorSpaceTransform transform_to_400(*JxlGetDefaultCms());
ASSERT_TRUE(
transform_to_400.Init(p3_hlg, ColorEncoding::LinearSRGB(), 400, 1, 1));
- ASSERT_TRUE(transform_to_400.Run(0, p3_hlg_values, linear_srgb_values));
+ ASSERT_TRUE(transform_to_400.Run(0, p3_hlg_values.data(),
+ linear_srgb_values.data(), 1));
// On a 400-nit display, it should be 100 cd/m².
- EXPECT_THAT(linear_srgb_values,
- ElementsAre(FloatNear(0.250, 1e-3), FloatNear(0.250, 1e-3),
- FloatNear(0.250, 1e-3)));
+ EXPECT_ARRAY_NEAR(linear_srgb_values, (Color{0.250, 0.250, 0.250}), 1e-3);
p3_hlg_values[2] = 0.50;
- ASSERT_TRUE(transform_to_1000.Run(0, p3_hlg_values, linear_srgb_values));
- EXPECT_THAT(linear_srgb_values,
- ElementsAre(FloatNear(0.201, 1e-3), FloatNear(0.201, 1e-3),
- FloatNear(0.050, 1e-3)));
+ ASSERT_TRUE(transform_to_1000.Run(0, p3_hlg_values.data(),
+ linear_srgb_values.data(), 1));
+ EXPECT_ARRAY_NEAR(linear_srgb_values, (Color{0.201, 0.201, 0.050}), 1e-3);
ColorSpaceTransform transform_from_400(*JxlGetDefaultCms());
ASSERT_TRUE(
transform_from_400.Init(ColorEncoding::LinearSRGB(), p3_hlg, 400, 1, 1));
linear_srgb_values[0] = linear_srgb_values[1] = linear_srgb_values[2] = 0.250;
- ASSERT_TRUE(transform_from_400.Run(0, linear_srgb_values, p3_hlg_values));
- EXPECT_THAT(p3_hlg_values,
- ElementsAre(FloatNear(0.75, 1e-3), FloatNear(0.75, 1e-3),
- FloatNear(0.75, 1e-3)));
+ ASSERT_TRUE(transform_from_400.Run(0, linear_srgb_values.data(),
+ p3_hlg_values.data(), 1));
+ EXPECT_ARRAY_NEAR(p3_hlg_values, (Color{0.75, 0.75, 0.75}), 1e-3);
ColorEncoding grayscale_hlg;
grayscale_hlg.SetColorSpace(ColorSpace::kGray);
@@ -352,8 +336,8 @@ TEST_F(ColorManagementTest, HlgOotf) {
const float grayscale_hlg_value = 0.75;
float linear_grayscale_value;
ASSERT_TRUE(grayscale_transform.Run(0, &grayscale_hlg_value,
- &linear_grayscale_value));
- EXPECT_THAT(linear_grayscale_value, FloatNear(0.203, 1e-3));
+ &linear_grayscale_value, 1));
+ EXPECT_NEAR(linear_grayscale_value, 0.203, 1e-3);
}
TEST_F(ColorManagementTest, XYBProfile) {
@@ -373,7 +357,7 @@ TEST_F(ColorManagementTest, XYBProfile) {
ImageMetadata metadata;
metadata.color_encoding = c_native;
ImageBundle ib(&metadata);
- Image3F native(kNumColors, 1);
+ JXL_ASSIGN_OR_DIE(Image3F native, Image3F::Create(kNumColors, 1));
float mul = 1.0f / (kGridDim - 1);
for (size_t ir = 0, x = 0; ir < kGridDim; ++ir) {
for (size_t ig = 0; ig < kGridDim; ++ig) {
@@ -386,10 +370,10 @@ TEST_F(ColorManagementTest, XYBProfile) {
}
ib.SetFromImage(std::move(native), c_native);
const Image3F& in = *ib.color();
- Image3F opsin(kNumColors, 1);
- ToXYB(ib, nullptr, &opsin, cms, nullptr);
+ JXL_ASSIGN_OR_DIE(Image3F opsin, Image3F::Create(kNumColors, 1));
+ JXL_CHECK(ToXYB(ib, nullptr, &opsin, cms, nullptr));
- Image3F opsin2(kNumColors, 1);
+ JXL_ASSIGN_OR_DIE(Image3F opsin2, Image3F::Create(kNumColors, 1));
CopyImageTo(opsin, &opsin2);
ScaleXYB(&opsin2);
@@ -401,9 +385,9 @@ TEST_F(ColorManagementTest, XYBProfile) {
}
float* dst = xform.BufDst(0);
- ASSERT_TRUE(xform.Run(0, src, dst));
+ ASSERT_TRUE(xform.Run(0, src, dst, kNumColors));
- Image3F out(kNumColors, 1);
+ JXL_ASSIGN_OR_DIE(Image3F out, Image3F::Create(kNumColors, 1));
for (size_t i = 0; i < kNumColors; ++i) {
for (size_t c = 0; c < 3; ++c) {
out.PlaneRow(c, 0)[i] = dst[3 * i + c];
diff --git a/third_party/jpeg-xl/lib/jxl/common.h b/third_party/jpeg-xl/lib/jxl/common.h
index d619711c9f..d593244433 100644
--- a/third_party/jpeg-xl/lib/jxl/common.h
+++ b/third_party/jpeg-xl/lib/jxl/common.h
@@ -33,6 +33,38 @@ constexpr size_t kMaxNumPasses = 11;
// Maximum number of reference frames.
constexpr size_t kMaxNumReferenceFrames = 4;
+enum class SpeedTier {
+ // Try multiple combinations of Glacier flags for modular mode. Otherwise
+ // like kGlacier.
+ kTectonicPlate = -1,
+ // Learn a global tree in Modular mode.
+ kGlacier = 0,
+ // Turns on FindBestQuantizationHQ loop. Equivalent to "guetzli" mode.
+ kTortoise = 1,
+ // Turns on FindBestQuantization butteraugli loop.
+ kKitten = 2,
+ // Turns on dots, patches, and spline detection by default, as well as full
+ // context clustering. Default.
+ kSquirrel = 3,
+ // Turns on error diffusion and full AC strategy heuristics. Equivalent to
+ // "fast" mode.
+ kWombat = 4,
+ // Turns on gaborish by default, non-default cmap, initial quant field.
+ kHare = 5,
+ // Turns on simple heuristics for AC strategy, quant field, and clustering;
+ // also enables coefficient reordering.
+ kCheetah = 6,
+ // Turns off most encoder features. Does context clustering.
+ // Modular: uses fixed tree with Weighted predictor.
+ kFalcon = 7,
+ // Currently fastest possible setting for VarDCT.
+ // Modular: uses fixed tree with Gradient predictor.
+ kThunder = 8,
+ // VarDCT: same as kThunder.
+ // Modular: no tree, Gradient predictor, fast histograms
+ kLightning = 9
+};
+
} // namespace jxl
#endif // LIB_JXL_COMMON_H_
diff --git a/third_party/jpeg-xl/lib/jxl/compressed_dc.cc b/third_party/jpeg-xl/lib/jxl/compressed_dc.cc
index b21b1da18b..250be9e9a6 100644
--- a/third_party/jpeg-xl/lib/jxl/compressed_dc.cc
+++ b/third_party/jpeg-xl/lib/jxl/compressed_dc.cc
@@ -10,9 +10,6 @@
#include <string.h>
#include <algorithm>
-#include <array>
-#include <memory>
-#include <utility>
#include <vector>
#undef HWY_TARGET_INCLUDE
@@ -21,17 +18,9 @@
#include <hwy/foreach_target.h>
#include <hwy/highway.h>
-#include "lib/jxl/ac_strategy.h"
-#include "lib/jxl/ans_params.h"
-#include "lib/jxl/base/bits.h"
#include "lib/jxl/base/compiler_specific.h"
#include "lib/jxl/base/data_parallel.h"
#include "lib/jxl/base/status.h"
-#include "lib/jxl/chroma_from_luma.h"
-#include "lib/jxl/dec_ans.h"
-#include "lib/jxl/dec_bit_reader.h"
-#include "lib/jxl/dec_cache.h"
-#include "lib/jxl/entropy_coder.h"
#include "lib/jxl/image.h"
HWY_BEFORE_NAMESPACE();
namespace jxl {
@@ -131,21 +120,21 @@ JXL_INLINE void ComputePixel(
Store(out, d, out_rows[2] + x);
}
-void AdaptiveDCSmoothing(const float* dc_factors, Image3F* dc,
- ThreadPool* pool) {
+Status AdaptiveDCSmoothing(const float* dc_factors, Image3F* dc,
+ ThreadPool* pool) {
const size_t xsize = dc->xsize();
const size_t ysize = dc->ysize();
- if (ysize <= 2 || xsize <= 2) return;
+ if (ysize <= 2 || xsize <= 2) return true;
// TODO(veluca): use tile-based processing?
// TODO(veluca): decide if changes to the y channel should be propagated to
// the x and b channels through color correlation.
JXL_ASSERT(w1 + w2 < 0.25f);
- Image3F smoothed(xsize, ysize);
+ JXL_ASSIGN_OR_RETURN(Image3F smoothed, Image3F::Create(xsize, ysize));
// Fill in borders that the loop below will not. First and last are unused.
for (size_t c = 0; c < 3; c++) {
- for (size_t y : {size_t(0), ysize - 1}) {
+ for (size_t y : {static_cast<size_t>(0), ysize - 1}) {
memcpy(smoothed.PlaneRow(c, y), dc->PlaneRow(c, y),
xsize * sizeof(float));
}
@@ -171,7 +160,7 @@ void AdaptiveDCSmoothing(const float* dc_factors, Image3F* dc,
smoothed.PlaneRow(1, y),
smoothed.PlaneRow(2, y),
};
- for (size_t x : {size_t(0), xsize - 1}) {
+ for (size_t x : {static_cast<size_t>(0), xsize - 1}) {
for (size_t c = 0; c < 3; c++) {
rows_out[c][x] = rows[c][x];
}
@@ -197,12 +186,13 @@ void AdaptiveDCSmoothing(const float* dc_factors, Image3F* dc,
JXL_CHECK(RunOnPool(pool, 1, ysize - 1, ThreadPool::NoInit, process_row,
"DCSmoothingRow"));
dc->Swap(smoothed);
+ return true;
}
// DC dequantization.
void DequantDC(const Rect& r, Image3F* dc, ImageB* quant_dc, const Image& in,
const float* dc_factors, float mul, const float* cfl_factors,
- YCbCrChromaSubsampling chroma_subsampling,
+ const YCbCrChromaSubsampling& chroma_subsampling,
const BlockCtxMap& bctx) {
const HWY_FULL(float) df;
const Rebind<pixel_type, HWY_FULL(float)> di; // assumes pixel_type <= float
@@ -265,7 +255,9 @@ void DequantDC(const Rect& r, Image3F* dc, ImageB* quant_dc, const Image& in,
const int32_t* quant_row_b =
in.channel[2].plane.Row(y >> chroma_subsampling.VShift(2));
for (size_t x = 0; x < r.xsize(); x++) {
- int bucket_x = 0, bucket_y = 0, bucket_b = 0;
+ int bucket_x = 0;
+ int bucket_y = 0;
+ int bucket_b = 0;
for (int t : bctx.dc_thresholds[0]) {
if (quant_row_x[x >> chroma_subsampling.HShift(0)] > t) bucket_x++;
}
@@ -296,17 +288,17 @@ namespace jxl {
HWY_EXPORT(DequantDC);
HWY_EXPORT(AdaptiveDCSmoothing);
-void AdaptiveDCSmoothing(const float* dc_factors, Image3F* dc,
- ThreadPool* pool) {
+Status AdaptiveDCSmoothing(const float* dc_factors, Image3F* dc,
+ ThreadPool* pool) {
return HWY_DYNAMIC_DISPATCH(AdaptiveDCSmoothing)(dc_factors, dc, pool);
}
void DequantDC(const Rect& r, Image3F* dc, ImageB* quant_dc, const Image& in,
const float* dc_factors, float mul, const float* cfl_factors,
- YCbCrChromaSubsampling chroma_subsampling,
+ const YCbCrChromaSubsampling& chroma_subsampling,
const BlockCtxMap& bctx) {
- return HWY_DYNAMIC_DISPATCH(DequantDC)(r, dc, quant_dc, in, dc_factors, mul,
- cfl_factors, chroma_subsampling, bctx);
+ HWY_DYNAMIC_DISPATCH(DequantDC)
+ (r, dc, quant_dc, in, dc_factors, mul, cfl_factors, chroma_subsampling, bctx);
}
} // namespace jxl
diff --git a/third_party/jpeg-xl/lib/jxl/compressed_dc.h b/third_party/jpeg-xl/lib/jxl/compressed_dc.h
index b06e5931f0..30259ebd56 100644
--- a/third_party/jpeg-xl/lib/jxl/compressed_dc.h
+++ b/third_party/jpeg-xl/lib/jxl/compressed_dc.h
@@ -21,12 +21,12 @@
namespace jxl {
// Smooth DC in already-smooth areas, to counteract banding.
-void AdaptiveDCSmoothing(const float* dc_factors, Image3F* dc,
- ThreadPool* pool);
+Status AdaptiveDCSmoothing(const float* dc_factors, Image3F* dc,
+ ThreadPool* pool);
void DequantDC(const Rect& r, Image3F* dc, ImageB* quant_dc, const Image& in,
const float* dc_factors, float mul, const float* cfl_factors,
- YCbCrChromaSubsampling chroma_subsampling,
+ const YCbCrChromaSubsampling& chroma_subsampling,
const BlockCtxMap& bctx);
} // namespace jxl
diff --git a/third_party/jpeg-xl/lib/jxl/convolve-inl.h b/third_party/jpeg-xl/lib/jxl/convolve-inl.h
index cd79153a3a..949fd8ad67 100644
--- a/third_party/jpeg-xl/lib/jxl/convolve-inl.h
+++ b/third_party/jpeg-xl/lib/jxl/convolve-inl.h
@@ -118,7 +118,7 @@ class Neighbors {
// Returns indices for SetTableIndices such that TableLookupLanes on the
// rightmost unaligned vector (rightmost sample in its most-significant lane)
// returns the mirrored values, with the mirror outside the last valid sample.
-static inline const int32_t* MirrorLanes(const size_t mod) {
+inline const int32_t* MirrorLanes(const size_t mod) {
const HWY_CAPPED(float, 16) d;
constexpr size_t kN = MaxLanes(d);
@@ -181,7 +181,7 @@ class ConvolveT {
JXL_CHECK(SameSize(rect, *out));
JXL_CHECK(rect.xsize() >= MinWidth());
- static_assert(int64_t(kRadius) <= 3,
+ static_assert(static_cast<int64_t>(kRadius) <= 3,
"Must handle [0, kRadius) and >= kRadius");
switch (rect.xsize() % Lanes(Simd())) {
case 0:
@@ -273,15 +273,17 @@ class ConvolveT {
const Weights& weights, ThreadPool* pool,
Image* out) {
const int64_t ysize = rect.ysize();
- RunBorderRows<kSizeModN>(in, rect, 0, std::min(int64_t(kRadius), ysize),
+ RunBorderRows<kSizeModN>(in, rect, 0,
+ std::min(static_cast<int64_t>(kRadius), ysize),
weights, out);
- if (ysize > 2 * int64_t(kRadius)) {
- RunInteriorRows<kSizeModN>(in, rect, int64_t(kRadius),
- ysize - int64_t(kRadius), weights, pool, out);
+ if (ysize > 2 * static_cast<int64_t>(kRadius)) {
+ RunInteriorRows<kSizeModN>(in, rect, static_cast<int64_t>(kRadius),
+ ysize - static_cast<int64_t>(kRadius), weights,
+ pool, out);
}
- if (ysize > int64_t(kRadius)) {
- RunBorderRows<kSizeModN>(in, rect, ysize - int64_t(kRadius), ysize,
- weights, out);
+ if (ysize > static_cast<int64_t>(kRadius)) {
+ RunBorderRows<kSizeModN>(in, rect, ysize - static_cast<int64_t>(kRadius),
+ ysize, weights, out);
}
}
};
diff --git a/third_party/jpeg-xl/lib/jxl/convolve_separable5.cc b/third_party/jpeg-xl/lib/jxl/convolve_separable5.cc
index db533606a1..ae618b9990 100644
--- a/third_party/jpeg-xl/lib/jxl/convolve_separable5.cc
+++ b/third_party/jpeg-xl/lib/jxl/convolve_separable5.cc
@@ -185,7 +185,8 @@ class Separable5Strategy {
const V l1 = LoadU(d, row + x - 1);
const V l2 = LoadU(d, row + x - 2);
- V r1, r2;
+ V r1;
+ V r2;
#if HWY_TARGET == HWY_SCALAR
r1 = LoadU(d, row + Mirror(x + 1, xsize));
r2 = LoadU(d, row + Mirror(x + 2, xsize));
@@ -236,10 +237,11 @@ void Separable5(const ImageF& in, const Rect& rect,
ImageF* out) {
using Conv = ConvolveT<Separable5Strategy>;
if (rect.xsize() >= Conv::MinWidth()) {
- return Conv::Run(in, rect, weights, pool, out);
+ Conv::Run(in, rect, weights, pool, out);
+ return;
}
- return SlowSeparable5(in, rect, weights, pool, out, Rect(*out));
+ SlowSeparable5(in, rect, weights, pool, out, Rect(*out));
}
// NOLINTNEXTLINE(google-readability-namespace-comments)
@@ -254,7 +256,7 @@ HWY_EXPORT(Separable5);
void Separable5(const ImageF& in, const Rect& rect,
const WeightsSeparable5& weights, ThreadPool* pool,
ImageF* out) {
- return HWY_DYNAMIC_DISPATCH(Separable5)(in, rect, weights, pool, out);
+ HWY_DYNAMIC_DISPATCH(Separable5)(in, rect, weights, pool, out);
}
} // namespace jxl
diff --git a/third_party/jpeg-xl/lib/jxl/convolve_symmetric3.cc b/third_party/jpeg-xl/lib/jxl/convolve_symmetric3.cc
index 06b59dfb60..618ad03a86 100644
--- a/third_party/jpeg-xl/lib/jxl/convolve_symmetric3.cc
+++ b/third_party/jpeg-xl/lib/jxl/convolve_symmetric3.cc
@@ -93,7 +93,9 @@ class Symmetric3Strategy {
const V mc = LoadU(d, row_m + x);
const V bc = LoadU(d, row_b + x);
- V tr, mr, br;
+ V tr;
+ V mr;
+ V br;
#if HWY_TARGET == HWY_SCALAR
tr = tc; // Single-lane => mirrored right neighbor = center value.
mr = mc;
@@ -169,10 +171,11 @@ void Symmetric3(const ImageF& in, const Rect& rect,
ImageF* out) {
using Conv = ConvolveT<Symmetric3Strategy>;
if (rect.xsize() >= Conv::MinWidth()) {
- return Conv::Run(in, rect, weights, pool, out);
+ Conv::Run(in, rect, weights, pool, out);
+ return;
}
- return SlowSymmetric3(in, rect, weights, pool, out);
+ SlowSymmetric3(in, rect, weights, pool, out);
}
// NOLINTNEXTLINE(google-readability-namespace-comments)
@@ -187,7 +190,7 @@ HWY_EXPORT(Symmetric3);
void Symmetric3(const ImageF& in, const Rect& rect,
const WeightsSymmetric3& weights, ThreadPool* pool,
ImageF* out) {
- return HWY_DYNAMIC_DISPATCH(Symmetric3)(in, rect, weights, pool, out);
+ HWY_DYNAMIC_DISPATCH(Symmetric3)(in, rect, weights, pool, out);
}
} // namespace jxl
diff --git a/third_party/jpeg-xl/lib/jxl/convolve_symmetric5.cc b/third_party/jpeg-xl/lib/jxl/convolve_symmetric5.cc
index 2e203fd08f..7ed384894e 100644
--- a/third_party/jpeg-xl/lib/jxl/convolve_symmetric5.cc
+++ b/third_party/jpeg-xl/lib/jxl/convolve_symmetric5.cc
@@ -175,14 +175,13 @@ HWY_EXPORT(Symmetric5);
void Symmetric5(const ImageF& in, const Rect& in_rect,
const WeightsSymmetric5& weights, ThreadPool* pool,
ImageF* JXL_RESTRICT out, const Rect& out_rect) {
- return HWY_DYNAMIC_DISPATCH(Symmetric5)(in, in_rect, weights, pool, out,
- out_rect);
+ HWY_DYNAMIC_DISPATCH(Symmetric5)(in, in_rect, weights, pool, out, out_rect);
}
void Symmetric5(const ImageF& in, const Rect& rect,
const WeightsSymmetric5& weights, ThreadPool* pool,
ImageF* JXL_RESTRICT out) {
- return Symmetric5(in, rect, weights, pool, out, Rect(*out));
+ Symmetric5(in, rect, weights, pool, out, Rect(*out));
}
} // namespace jxl
diff --git a/third_party/jpeg-xl/lib/jxl/convolve_test.cc b/third_party/jpeg-xl/lib/jxl/convolve_test.cc
index 6a8dc9c400..09cbdc12a6 100644
--- a/third_party/jpeg-xl/lib/jxl/convolve_test.cc
+++ b/third_party/jpeg-xl/lib/jxl/convolve_test.cc
@@ -5,6 +5,7 @@
#include "lib/jxl/convolve.h"
+#include <jxl/types.h>
#include <time.h>
#undef HWY_TARGET_INCLUDE
@@ -68,11 +69,11 @@ void VerifySymmetric3(const size_t xsize, const size_t ysize, ThreadPool* pool,
Rng* rng) {
const Rect rect(0, 0, xsize, ysize);
- ImageF in(xsize, ysize);
+ JXL_ASSIGN_OR_DIE(ImageF in, ImageF::Create(xsize, ysize));
GenerateImage(*rng, &in, 0.0f, 1.0f);
- ImageF out_expected(xsize, ysize);
- ImageF out_actual(xsize, ysize);
+ JXL_ASSIGN_OR_DIE(ImageF out_expected, ImageF::Create(xsize, ysize));
+ JXL_ASSIGN_OR_DIE(ImageF out_actual, ImageF::Create(xsize, ysize));
const WeightsSymmetric3& weights = WeightsSymmetric3Lowpass();
Symmetric3(in, rect, weights, pool, &out_expected);
@@ -86,7 +87,7 @@ std::vector<Rect> GenerateTestRectangles(size_t xsize, size_t ysize) {
for (size_t tl : {0, 1, 13}) {
for (size_t br : {0, 1, 13}) {
if (xsize > tl + br && ysize > tl + br) {
- out.push_back(Rect(tl, tl, xsize - tl - br, ysize - tl - br));
+ out.emplace_back(tl, tl, xsize - tl - br, ysize - tl - br);
}
}
}
@@ -96,7 +97,7 @@ std::vector<Rect> GenerateTestRectangles(size_t xsize, size_t ysize) {
// Ensures Symmetric and Separable give the same result.
void VerifySymmetric5(const size_t xsize, const size_t ysize, ThreadPool* pool,
Rng* rng) {
- ImageF in(xsize, ysize);
+ JXL_ASSIGN_OR_DIE(ImageF in, ImageF::Create(xsize, ysize));
GenerateImage(*rng, &in, 0.0f, 1.0f);
for (const Rect& in_rect : GenerateTestRectangles(xsize, ysize)) {
@@ -105,8 +106,8 @@ void VerifySymmetric5(const size_t xsize, const size_t ysize, ThreadPool* pool,
in_rect.xsize(), in_rect.ysize(), in_rect.x0(), in_rect.y0());
{
Rect out_rect = in_rect;
- ImageF out_expected(xsize, ysize);
- ImageF out_actual(xsize, ysize);
+ JXL_ASSIGN_OR_DIE(ImageF out_expected, ImageF::Create(xsize, ysize));
+ JXL_ASSIGN_OR_DIE(ImageF out_actual, ImageF::Create(xsize, ysize));
FillImage(-1.0f, &out_expected);
FillImage(-1.0f, &out_actual);
@@ -120,8 +121,10 @@ void VerifySymmetric5(const size_t xsize, const size_t ysize, ThreadPool* pool,
}
{
Rect out_rect(0, 0, in_rect.xsize(), in_rect.ysize());
- ImageF out_expected(out_rect.xsize(), out_rect.ysize());
- ImageF out_actual(out_rect.xsize(), out_rect.ysize());
+ JXL_ASSIGN_OR_DIE(ImageF out_expected,
+ ImageF::Create(out_rect.xsize(), out_rect.ysize()));
+ JXL_ASSIGN_OR_DIE(ImageF out_actual,
+ ImageF::Create(out_rect.xsize(), out_rect.ysize()));
SlowSeparable5(in, in_rect, WeightsSeparable5Lowpass(), pool,
&out_expected, out_rect);
@@ -138,11 +141,11 @@ void VerifySeparable5(const size_t xsize, const size_t ysize, ThreadPool* pool,
Rng* rng) {
const Rect rect(0, 0, xsize, ysize);
- ImageF in(xsize, ysize);
+ JXL_ASSIGN_OR_DIE(ImageF in, ImageF::Create(xsize, ysize));
GenerateImage(*rng, &in, 0.0f, 1.0f);
- ImageF out_expected(xsize, ysize);
- ImageF out_actual(xsize, ysize);
+ JXL_ASSIGN_OR_DIE(ImageF out_expected, ImageF::Create(xsize, ysize));
+ JXL_ASSIGN_OR_DIE(ImageF out_actual, ImageF::Create(xsize, ysize));
const WeightsSeparable5& weights = WeightsSeparable5Lowpass();
SlowSeparable5(in, rect, weights, pool, &out_expected, rect);
@@ -197,10 +200,10 @@ void BenchmarkConv(const char* caption, const Conv& conv,
hwy::Result results[kNumInputs];
const size_t kDim = 160; // in+out fit in L2
- ImageF in(kDim, kDim);
+ JXL_ASSIGN_OR_DIE(ImageF in, ImageF::Create(kDim, kDim));
ZeroFillImage(&in);
in.Row(kDim / 2)[kDim / 2] = unpredictable1;
- ImageF out(kDim, kDim);
+ JXL_ASSIGN_OR_DIE(ImageF out, ImageF::Create(kDim, kDim));
hwy::Params p;
p.verbose = false;
@@ -239,7 +242,7 @@ struct ConvSeparable5 {
};
void BenchmarkAll() {
-#if 0 // disabled to avoid test timeouts, run manually on demand
+#if JXL_FALSE // disabled to avoid test timeouts, run manually on demand
const hwy::FuncInput unpredictable1 = time(nullptr) != 1234;
BenchmarkConv("Symmetric3", ConvSymmetric3(), unpredictable1);
BenchmarkConv("Separable5", ConvSeparable5(), unpredictable1);
diff --git a/third_party/jpeg-xl/lib/jxl/dct-inl.h b/third_party/jpeg-xl/lib/jxl/dct-inl.h
index cb6c54bc46..05cfbde553 100644
--- a/third_party/jpeg-xl/lib/jxl/dct-inl.h
+++ b/third_party/jpeg-xl/lib/jxl/dct-inl.h
@@ -154,12 +154,12 @@ struct DCT1DImpl;
template <size_t SZ>
struct DCT1DImpl<1, SZ> {
- JXL_INLINE void operator()(float* JXL_RESTRICT mem, float*) {}
+ JXL_INLINE void operator()(float* JXL_RESTRICT mem, float* /* tmp */) {}
};
template <size_t SZ>
struct DCT1DImpl<2, SZ> {
- JXL_INLINE void operator()(float* JXL_RESTRICT mem, float*) {
+ JXL_INLINE void operator()(float* JXL_RESTRICT mem, float* /* tmp */) {
auto in1 = Load(FV<SZ>(), mem);
auto in2 = Load(FV<SZ>(), mem + SZ);
Store(Add(in1, in2), FV<SZ>(), mem);
@@ -186,7 +186,7 @@ struct IDCT1DImpl;
template <size_t SZ>
struct IDCT1DImpl<1, SZ> {
JXL_INLINE void operator()(const float* from, size_t from_stride, float* to,
- size_t to_stride, float* JXL_RESTRICT) {
+ size_t to_stride, float* JXL_RESTRICT /* tmp */) {
StoreU(LoadU(FV<SZ>(), from), FV<SZ>(), to);
}
};
@@ -194,7 +194,7 @@ struct IDCT1DImpl<1, SZ> {
template <size_t SZ>
struct IDCT1DImpl<2, SZ> {
JXL_INLINE void operator()(const float* from, size_t from_stride, float* to,
- size_t to_stride, float* JXL_RESTRICT) {
+ size_t to_stride, float* JXL_RESTRICT /* tmp */) {
JXL_DASSERT(from_stride >= SZ);
JXL_DASSERT(to_stride >= SZ);
auto in1 = LoadU(FV<SZ>(), from);
diff --git a/third_party/jpeg-xl/lib/jxl/dct_block-inl.h b/third_party/jpeg-xl/lib/jxl/dct_block-inl.h
index 50646a737f..6db5f0e7c8 100644
--- a/third_party/jpeg-xl/lib/jxl/dct_block-inl.h
+++ b/third_party/jpeg-xl/lib/jxl/dct_block-inl.h
@@ -44,7 +44,7 @@ class DCTFrom {
DCTFrom(const float* data, size_t stride) : stride_(stride), data_(data) {}
template <typename D>
- HWY_INLINE Vec<D> LoadPart(D, const size_t row, size_t i) const {
+ HWY_INLINE Vec<D> LoadPart(D /* tag */, const size_t row, size_t i) const {
JXL_DASSERT(Lanes(D()) <= stride_);
// Since these functions are used also for DC, no alignment at all is
// guaranteed in the case of floating blocks.
@@ -74,7 +74,7 @@ class DCTTo {
DCTTo(float* data, size_t stride) : stride_(stride), data_(data) {}
template <typename D>
- HWY_INLINE void StorePart(D, const Vec<D>& v, const size_t row,
+ HWY_INLINE void StorePart(D /* tag */, const Vec<D>& v, const size_t row,
size_t i) const {
JXL_DASSERT(Lanes(D()) <= stride_);
// Since these functions are used also for DC, no alignment at all is
diff --git a/third_party/jpeg-xl/lib/jxl/dct_util.h b/third_party/jpeg-xl/lib/jxl/dct_util.h
index 2f29449677..90a02658af 100644
--- a/third_party/jpeg-xl/lib/jxl/dct_util.h
+++ b/third_party/jpeg-xl/lib/jxl/dct_util.h
@@ -8,8 +8,9 @@
#include <stddef.h>
-#include "lib/jxl/base/compiler_specific.h"
-#include "lib/jxl/base/data_parallel.h"
+#include <memory>
+
+#include "lib/jxl/base/common.h"
#include "lib/jxl/base/status.h"
#include "lib/jxl/image.h"
#include "lib/jxl/image_ops.h"
@@ -50,12 +51,16 @@ template <typename T>
class ACImageT final : public ACImage {
public:
ACImageT() = default;
- ACImageT(size_t xsize, size_t ysize) {
+
+ static StatusOr<std::unique_ptr<ACImageT>> Make(size_t xsize, size_t ysize) {
static_assert(
std::is_same<T, int16_t>::value || std::is_same<T, int32_t>::value,
"ACImage must be either 32- or 16- bit");
- img_ = Image3<T>(xsize, ysize);
+ std::unique_ptr<ACImageT> result = jxl::make_unique<ACImageT>();
+ JXL_ASSIGN_OR_RETURN(result->img_, Image3<T>::Create(xsize, ysize));
+ return result;
}
+
ACType Type() const override {
return sizeof(T) == 2 ? ACType::k16 : ACType::k32;
}
diff --git a/third_party/jpeg-xl/lib/jxl/dec_ans.cc b/third_party/jpeg-xl/lib/jxl/dec_ans.cc
index 29d41c8062..8b7b54ce91 100644
--- a/third_party/jpeg-xl/lib/jxl/dec_ans.cc
+++ b/third_party/jpeg-xl/lib/jxl/dec_ans.cc
@@ -154,7 +154,7 @@ Status ReadHistogram(int precision_bits, std::vector<int32_t>* counts,
(*counts)[i] = prev;
numsame--;
} else {
- int code = logcounts[i];
+ unsigned int code = logcounts[i];
// omit_pos may not be negative at this point (checked before).
if (i == static_cast<size_t>(omit_pos)) {
continue;
@@ -164,7 +164,7 @@ Status ReadHistogram(int precision_bits, std::vector<int32_t>* counts,
(*counts)[i] = 1;
} else {
int bitcount = GetPopulationCountPrecision(code - 1, shift);
- (*counts)[i] = (1 << (code - 1)) +
+ (*counts)[i] = (1u << (code - 1)) +
(input->ReadBits(bitcount) << (code - 1 - bitcount));
}
}
@@ -260,7 +260,8 @@ Status DecodeUintConfig(size_t log_alpha_size, HybridUintConfig* uint_config,
BitReader* br) {
br->Refill();
size_t split_exponent = br->ReadBits(CeilLog2Nonzero(log_alpha_size + 1));
- size_t msb_in_token = 0, lsb_in_token = 0;
+ size_t msb_in_token = 0;
+ size_t lsb_in_token = 0;
if (split_exponent != log_alpha_size) {
// otherwise, msb/lsb don't matter.
size_t nbits = CeilLog2Nonzero(split_exponent + 1);
@@ -284,9 +285,8 @@ Status DecodeUintConfigs(size_t log_alpha_size,
std::vector<HybridUintConfig>* uint_config,
BitReader* br) {
// TODO(veluca): RLE?
- for (size_t i = 0; i < uint_config->size(); i++) {
- JXL_RETURN_IF_ERROR(
- DecodeUintConfig(log_alpha_size, &(*uint_config)[i], br));
+ for (auto& cfg : *uint_config) {
+ JXL_RETURN_IF_ERROR(DecodeUintConfig(log_alpha_size, &cfg, br));
}
return true;
}
@@ -345,7 +345,7 @@ Status DecodeHistograms(BitReader* br, size_t num_contexts, ANSCode* code,
4, "Decoded context map of size %" PRIuS " and %" PRIuS " histograms",
num_contexts, num_histograms);
code->lz77.nonserialized_distance_context = context_map->back();
- code->use_prefix_code = br->ReadFixedBits<1>();
+ code->use_prefix_code = static_cast<bool>(br->ReadFixedBits<1>());
if (code->use_prefix_code) {
code->log_alpha_size = PREFIX_MAX_BITS;
} else {
diff --git a/third_party/jpeg-xl/lib/jxl/dec_ans.h b/third_party/jpeg-xl/lib/jxl/dec_ans.h
index 57faad25a7..cbff1deebe 100644
--- a/third_party/jpeg-xl/lib/jxl/dec_ans.h
+++ b/third_party/jpeg-xl/lib/jxl/dec_ans.h
@@ -9,6 +9,7 @@
// Library to decode the ANS population counts from the bit-stream and build a
// decoding table from them.
+#include <jxl/types.h>
#include <stddef.h>
#include <stdint.h>
@@ -133,6 +134,11 @@ static constexpr int8_t kSpecialDistances[kNumSpecialDistances][2] = {
{8, 0}, {4, 7}, {-4, 7}, {7, 4}, {-7, 4}, {8, 1}, {8, 2}, {6, 6},
{-6, 6}, {8, 3}, {5, 7}, {-5, 7}, {7, 5}, {-7, 5}, {8, 4}, {6, 7},
{-6, 7}, {7, 6}, {-7, 6}, {8, 5}, {7, 7}, {-7, 7}, {8, 6}, {8, 7}};
+static JXL_INLINE int SpecialDistance(size_t index, int multiplier) {
+ int dist = kSpecialDistances[index][0] +
+ static_cast<int>(multiplier) * kSpecialDistances[index][1];
+ return (dist > 1) ? dist : 1;
+}
struct ANSCode {
CacheAlignedUniquePtr alias_tables;
@@ -179,10 +185,7 @@ class ANSSymbolReader {
num_special_distances_ =
distance_multiplier == 0 ? 0 : kNumSpecialDistances;
for (size_t i = 0; i < num_special_distances_; i++) {
- int dist = kSpecialDistances[i][0];
- dist += static_cast<int>(distance_multiplier) * kSpecialDistances[i][1];
- if (dist < 1) dist = 1;
- special_distances_[i] = dist;
+ special_distances_[i] = SpecialDistance(i, distance_multiplier);
}
}
@@ -196,7 +199,7 @@ class ANSSymbolReader {
AliasTable::Lookup(table, res, log_entry_size_, entry_size_minus_1_);
state_ = symbol.freq * (state_ >> ANS_LOG_TAB_SIZE) + symbol.offset;
-#if 1
+#if JXL_TRUE
// Branchless version is about equally fast on SKX.
const uint32_t new_state =
(state_ << 16u) | static_cast<uint32_t>(br->PeekFixedBits<16>());
diff --git a/third_party/jpeg-xl/lib/jxl/dec_cache.cc b/third_party/jpeg-xl/lib/jxl/dec_cache.cc
index 8d12bce02e..2a89420018 100644
--- a/third_party/jpeg-xl/lib/jxl/dec_cache.cc
+++ b/third_party/jpeg-xl/lib/jxl/dec_cache.cc
@@ -5,6 +5,7 @@
#include "lib/jxl/dec_cache.h"
+#include "lib/jxl/base/status.h"
#include "lib/jxl/blending.h"
#include "lib/jxl/common.h" // JXL_HIGH_PRECISION
#include "lib/jxl/render_pipeline/stage_blending.h"
@@ -247,6 +248,7 @@ Status PassesDecoderState::PreparePipeline(const FrameHeader& frame_header,
}
linear = false;
}
+ (void)linear;
if (main_output.callback.IsPresent() || main_output.buffer) {
builder.AddStage(GetWriteToOutputStage(main_output, width, height,
@@ -257,7 +259,8 @@ Status PassesDecoderState::PreparePipeline(const FrameHeader& frame_header,
decoded, output_encoding_info.color_encoding));
}
}
- render_pipeline = std::move(builder).Finalize(shared->frame_dim);
+ JXL_ASSIGN_OR_RETURN(render_pipeline,
+ std::move(builder).Finalize(shared->frame_dim));
return render_pipeline->IsInitialized();
}
diff --git a/third_party/jpeg-xl/lib/jxl/dec_cache.h b/third_party/jpeg-xl/lib/jxl/dec_cache.h
index d4cc7a1957..d031074532 100644
--- a/third_party/jpeg-xl/lib/jxl/dec_cache.h
+++ b/third_party/jpeg-xl/lib/jxl/dec_cache.h
@@ -52,7 +52,8 @@ struct PixelCallback {
const bool has_init = init != nullptr;
const bool has_run = run != nullptr;
const bool has_destroy = destroy != nullptr;
- JXL_ASSERT(has_init == has_run && has_run == has_destroy);
+ const bool healthy = (has_init == has_run) && (has_run == has_destroy);
+ JXL_ASSERT(healthy);
#endif
}
@@ -128,7 +129,7 @@ struct PassesDecoderState {
std::atomic<uint32_t> used_acs{0};
// Storage for coefficients if in "accumulate" mode.
- std::unique_ptr<ACImage> coefficients = make_unique<ACImageT<int32_t>>(0, 0);
+ std::unique_ptr<ACImage> coefficients = make_unique<ACImageT<int32_t>>();
// Rendering pipeline.
std::unique_ptr<RenderPipeline> render_pipeline;
@@ -166,8 +167,10 @@ struct PassesDecoderState {
upsampler8x = GetUpsamplingStage(shared->metadata->transform_data, 0, 3);
if (frame_header.loop_filter.epf_iters > 0) {
- sigma = ImageF(shared->frame_dim.xsize_blocks + 2 * kSigmaPadding,
- shared->frame_dim.ysize_blocks + 2 * kSigmaPadding);
+ JXL_ASSIGN_OR_RETURN(
+ sigma,
+ ImageF::Create(shared->frame_dim.xsize_blocks + 2 * kSigmaPadding,
+ shared->frame_dim.ysize_blocks + 2 * kSigmaPadding));
}
return true;
}
@@ -193,14 +196,16 @@ struct PassesDecoderState {
// Temp images required for decoding a single group. Reduces memory allocations
// for large images because we only initialize min(#threads, #groups) instances.
struct GroupDecCache {
- void InitOnce(size_t num_passes, size_t used_acs) {
+ Status InitOnce(size_t num_passes, size_t used_acs) {
for (size_t i = 0; i < num_passes; i++) {
if (num_nzeroes[i].xsize() == 0) {
// Allocate enough for a whole group - partial groups on the
// right/bottom border just use a subset. The valid size is passed via
// Rect.
- num_nzeroes[i] = Image3I(kGroupDimInBlocks, kGroupDimInBlocks);
+ JXL_ASSIGN_OR_RETURN(
+ num_nzeroes[i],
+ Image3I::Create(kGroupDimInBlocks, kGroupDimInBlocks));
}
}
size_t max_block_area = 0;
@@ -227,13 +232,17 @@ struct GroupDecCache {
scratch_space = dec_group_block + max_block_area_ * 3;
dec_group_qblock = int32_memory_.get();
dec_group_qblock16 = int16_memory_.get();
+ return true;
}
- void InitDCBufferOnce() {
+ Status InitDCBufferOnce() {
if (dc_buffer.xsize() == 0) {
- dc_buffer = ImageF(kGroupDimInBlocks + kRenderPipelineXOffset * 2,
- kGroupDimInBlocks + 4);
+ JXL_ASSIGN_OR_RETURN(
+ dc_buffer,
+ ImageF::Create(kGroupDimInBlocks + kRenderPipelineXOffset * 2,
+ kGroupDimInBlocks + 4));
}
+ return true;
}
// Scratch space used by DecGroupImpl().
diff --git a/third_party/jpeg-xl/lib/jxl/dec_context_map.cc b/third_party/jpeg-xl/lib/jxl/dec_context_map.cc
index 2c936722da..baff87fa49 100644
--- a/third_party/jpeg-xl/lib/jxl/dec_context_map.cc
+++ b/third_party/jpeg-xl/lib/jxl/dec_context_map.cc
@@ -6,6 +6,7 @@
#include "lib/jxl/dec_context_map.h"
#include <algorithm>
+#include <cstdint>
#include <vector>
#include "lib/jxl/ans_params.h"
@@ -41,18 +42,18 @@ Status VerifyContextMap(const std::vector<uint8_t>& context_map,
Status DecodeContextMap(std::vector<uint8_t>* context_map, size_t* num_htrees,
BitReader* input) {
- bool is_simple = input->ReadFixedBits<1>();
+ bool is_simple = static_cast<bool>(input->ReadFixedBits<1>());
if (is_simple) {
int bits_per_entry = input->ReadFixedBits<2>();
if (bits_per_entry != 0) {
- for (size_t i = 0; i < context_map->size(); i++) {
- (*context_map)[i] = input->ReadBits(bits_per_entry);
+ for (uint8_t& entry : *context_map) {
+ entry = input->ReadBits(bits_per_entry);
}
} else {
std::fill(context_map->begin(), context_map->end(), 0);
}
} else {
- bool use_mtf = input->ReadFixedBits<1>();
+ bool use_mtf = static_cast<bool>(input->ReadFixedBits<1>());
ANSCode code;
std::vector<uint8_t> sink_ctx_map;
// Usage of LZ77 is disallowed if decoding only two symbols. This doesn't
diff --git a/third_party/jpeg-xl/lib/jxl/dec_external_image.cc b/third_party/jpeg-xl/lib/jxl/dec_external_image.cc
index 06cd573378..51e12fcc81 100644
--- a/third_party/jpeg-xl/lib/jxl/dec_external_image.cc
+++ b/third_party/jpeg-xl/lib/jxl/dec_external_image.cc
@@ -9,11 +9,11 @@
#include <string.h>
#include <algorithm>
-#include <array>
-#include <functional>
#include <utility>
#include <vector>
+#include "lib/jxl/base/status.h"
+
#undef HWY_TARGET_INCLUDE
#define HWY_TARGET_INCLUDE "lib/jxl/dec_external_image.cc"
#include <hwy/foreach_target.h>
@@ -113,7 +113,7 @@ Status UndoOrientation(jxl::Orientation undo_orientation, const Plane<T>& image,
const size_t ysize = image.ysize();
if (undo_orientation == Orientation::kFlipHorizontal) {
- out = Plane<T>(xsize, ysize);
+ JXL_ASSIGN_OR_RETURN(out, Plane<T>::Create(xsize, ysize));
JXL_RETURN_IF_ERROR(RunOnPool(
pool, 0, static_cast<uint32_t>(ysize), ThreadPool::NoInit,
[&](const uint32_t task, size_t /*thread*/) {
@@ -126,7 +126,7 @@ Status UndoOrientation(jxl::Orientation undo_orientation, const Plane<T>& image,
},
"UndoOrientation"));
} else if (undo_orientation == Orientation::kRotate180) {
- out = Plane<T>(xsize, ysize);
+ JXL_ASSIGN_OR_RETURN(out, Plane<T>::Create(xsize, ysize));
JXL_RETURN_IF_ERROR(RunOnPool(
pool, 0, static_cast<uint32_t>(ysize), ThreadPool::NoInit,
[&](const uint32_t task, size_t /*thread*/) {
@@ -139,7 +139,7 @@ Status UndoOrientation(jxl::Orientation undo_orientation, const Plane<T>& image,
},
"UndoOrientation"));
} else if (undo_orientation == Orientation::kFlipVertical) {
- out = Plane<T>(xsize, ysize);
+ JXL_ASSIGN_OR_RETURN(out, Plane<T>::Create(xsize, ysize));
JXL_RETURN_IF_ERROR(RunOnPool(
pool, 0, static_cast<uint32_t>(ysize), ThreadPool::NoInit,
[&](const uint32_t task, size_t /*thread*/) {
@@ -152,7 +152,7 @@ Status UndoOrientation(jxl::Orientation undo_orientation, const Plane<T>& image,
},
"UndoOrientation"));
} else if (undo_orientation == Orientation::kTranspose) {
- out = Plane<T>(ysize, xsize);
+ JXL_ASSIGN_OR_RETURN(out, Plane<T>::Create(ysize, xsize));
JXL_RETURN_IF_ERROR(RunOnPool(
pool, 0, static_cast<uint32_t>(ysize), ThreadPool::NoInit,
[&](const uint32_t task, size_t /*thread*/) {
@@ -164,7 +164,7 @@ Status UndoOrientation(jxl::Orientation undo_orientation, const Plane<T>& image,
},
"UndoOrientation"));
} else if (undo_orientation == Orientation::kRotate90) {
- out = Plane<T>(ysize, xsize);
+ JXL_ASSIGN_OR_RETURN(out, Plane<T>::Create(ysize, xsize));
JXL_RETURN_IF_ERROR(RunOnPool(
pool, 0, static_cast<uint32_t>(ysize), ThreadPool::NoInit,
[&](const uint32_t task, size_t /*thread*/) {
@@ -176,7 +176,7 @@ Status UndoOrientation(jxl::Orientation undo_orientation, const Plane<T>& image,
},
"UndoOrientation"));
} else if (undo_orientation == Orientation::kAntiTranspose) {
- out = Plane<T>(ysize, xsize);
+ JXL_ASSIGN_OR_RETURN(out, Plane<T>::Create(ysize, xsize));
JXL_RETURN_IF_ERROR(RunOnPool(
pool, 0, static_cast<uint32_t>(ysize), ThreadPool::NoInit,
[&](const uint32_t task, size_t /*thread*/) {
@@ -188,7 +188,7 @@ Status UndoOrientation(jxl::Orientation undo_orientation, const Plane<T>& image,
},
"UndoOrientation"));
} else if (undo_orientation == Orientation::kRotate270) {
- out = Plane<T>(ysize, xsize);
+ JXL_ASSIGN_OR_RETURN(out, Plane<T>::Create(ysize, xsize));
JXL_RETURN_IF_ERROR(RunOnPool(
pool, 0, static_cast<uint32_t>(ysize), ThreadPool::NoInit,
[&](const uint32_t task, size_t /*thread*/) {
@@ -247,7 +247,8 @@ Status ConvertChannelsToExternal(const ImageF* in_channels[],
JXL_DASSERT(in_channels[0] != nullptr);
JXL_CHECK(float_out ? bits_per_sample == 16 || bits_per_sample == 32
: bits_per_sample > 0 && bits_per_sample <= 16);
- if (!!out_image == out_callback.IsPresent()) {
+ const bool has_out_image = (out_image != nullptr);
+ if (has_out_image == out_callback.IsPresent()) {
return JXL_FAILURE(
"Must provide either an out_image or an out_callback, but not both.");
}
@@ -309,7 +310,7 @@ Status ConvertChannelsToExternal(const ImageF* in_channels[],
ImageF ones;
for (size_t c = 0; c < num_channels; ++c) {
if (!channels[c]) {
- ones = ImageF(xsize, 1);
+ JXL_ASSIGN_OR_RETURN(ones, ImageF::Create(xsize, 1));
FillImage(1.0f, &ones);
break;
}
@@ -322,9 +323,12 @@ Status ConvertChannelsToExternal(const ImageF* in_channels[],
JXL_RETURN_IF_ERROR(RunOnPool(
pool, 0, static_cast<uint32_t>(ysize),
[&](size_t num_threads) {
- f16_cache =
- Plane<hwy::float16_t>(xsize, num_channels * num_threads);
- return InitOutCallback(num_threads);
+ StatusOr<Plane<hwy::float16_t>> f16_cache_or =
+ Plane<hwy::float16_t>::Create(xsize,
+ num_channels * num_threads);
+ if (!f16_cache_or.ok()) return false;
+ f16_cache = std::move(f16_cache_or).value();
+ return !!InitOutCallback(num_threads);
},
[&](const uint32_t task, const size_t thread) {
const int64_t y = task;
@@ -398,8 +402,11 @@ Status ConvertChannelsToExternal(const ImageF* in_channels[],
JXL_RETURN_IF_ERROR(RunOnPool(
pool, 0, static_cast<uint32_t>(ysize),
[&](size_t num_threads) {
- u32_cache = Plane<uint32_t>(xsize, num_channels * num_threads);
- return InitOutCallback(num_threads);
+ StatusOr<Plane<uint32_t>> u32_cache_or =
+ Plane<uint32_t>::Create(xsize, num_channels * num_threads);
+ if (!u32_cache_or.ok()) return false;
+ u32_cache = std::move(u32_cache_or).value();
+ return !!InitOutCallback(num_threads);
},
[&](const uint32_t task, const size_t thread) {
const int64_t y = task;
@@ -453,7 +460,8 @@ Status ConvertToExternal(const jxl::ImageBundle& ib, size_t bits_per_sample,
// Undo premultiplied alpha.
Image3F unpremul;
if (ib.AlphaIsPremultiplied() && ib.HasAlpha() && unpremul_alpha) {
- unpremul = Image3F(color->xsize(), color->ysize());
+ JXL_ASSIGN_OR_RETURN(unpremul,
+ Image3F::Create(color->xsize(), color->ysize()));
CopyImageTo(*color, &unpremul);
for (size_t y = 0; y < unpremul.ysize(); y++) {
UnpremultiplyAlpha(unpremul.PlaneRow(0, y), unpremul.PlaneRow(1, y),
diff --git a/third_party/jpeg-xl/lib/jxl/dec_external_image_gbench.cc b/third_party/jpeg-xl/lib/jxl/dec_external_image_gbench.cc
index c87a4d5f36..720a278fc0 100644
--- a/third_party/jpeg-xl/lib/jxl/dec_external_image_gbench.cc
+++ b/third_party/jpeg-xl/lib/jxl/dec_external_image_gbench.cc
@@ -5,6 +5,7 @@
#include "benchmark/benchmark.h"
#include "lib/jxl/dec_external_image.h"
+#include "lib/jxl/image.h"
#include "lib/jxl/image_ops.h"
namespace jxl {
@@ -20,10 +21,10 @@ void BM_DecExternalImage_ConvertImageRGBA(benchmark::State& state) {
ImageMetadata im;
im.SetAlphaBits(8);
ImageBundle ib(&im);
- Image3F color(xsize, ysize);
+ JXL_ASSIGN_OR_DIE(Image3F color, Image3F::Create(xsize, ysize));
ZeroFillImage(&color);
ib.SetFromImage(std::move(color), ColorEncoding::SRGB());
- ImageF alpha(xsize, ysize);
+ JXL_ASSIGN_OR_DIE(ImageF alpha, ImageF::Create(xsize, ysize));
ZeroFillImage(&alpha);
ib.SetAlpha(std::move(alpha));
diff --git a/third_party/jpeg-xl/lib/jxl/dec_frame.cc b/third_party/jpeg-xl/lib/jxl/dec_frame.cc
index 918dbe7c37..a2a82ad1fb 100644
--- a/third_party/jpeg-xl/lib/jxl/dec_frame.cc
+++ b/third_party/jpeg-xl/lib/jxl/dec_frame.cc
@@ -331,21 +331,23 @@ Status FrameDecoder::ProcessDCGroup(size_t dc_group_id, BitReader* br) {
} else if (lf.epf_iters > 0) {
FillImage(kInvSigmaNum / lf.epf_sigma_for_modular, &dec_state_->sigma);
}
- decoded_dc_groups_[dc_group_id] = uint8_t{true};
+ decoded_dc_groups_[dc_group_id] = JXL_TRUE;
return true;
}
-void FrameDecoder::FinalizeDC() {
+Status FrameDecoder::FinalizeDC() {
// Do Adaptive DC smoothing if enabled. This *must* happen between all the
// ProcessDCGroup and ProcessACGroup.
if (frame_header_.encoding == FrameEncoding::kVarDCT &&
!(frame_header_.flags & FrameHeader::kSkipAdaptiveDCSmoothing) &&
!(frame_header_.flags & FrameHeader::kUseDcFrame)) {
- AdaptiveDCSmoothing(dec_state_->shared->quantizer.MulDC(),
- &dec_state_->shared_storage.dc_storage, pool_);
+ JXL_RETURN_IF_ERROR(
+ AdaptiveDCSmoothing(dec_state_->shared->quantizer.MulDC(),
+ &dec_state_->shared_storage.dc_storage, pool_));
}
finalized_dc_ = true;
+ return true;
}
Status FrameDecoder::AllocateOutput() {
@@ -410,9 +412,11 @@ Status FrameDecoder::ProcessACGlobal(BitReader* br) {
size_t xs = store ? kGroupDim * kGroupDim : 0;
size_t ys = store ? frame_dim_.num_groups : 0;
if (use_16_bit) {
- dec_state_->coefficients = make_unique<ACImageT<int16_t>>(xs, ys);
+ JXL_ASSIGN_OR_RETURN(dec_state_->coefficients,
+ ACImageT<int16_t>::Make(xs, ys));
} else {
- dec_state_->coefficients = make_unique<ACImageT<int32_t>>(xs, ys);
+ JXL_ASSIGN_OR_RETURN(dec_state_->coefficients,
+ ACImageT<int32_t>::Make(xs, ys));
}
if (store) {
dec_state_->coefficients->ZeroFill();
@@ -482,8 +486,8 @@ Status FrameDecoder::ProcessACGroup(size_t ac_group_id,
bool should_run_pipeline = true;
if (frame_header_.encoding == FrameEncoding::kVarDCT) {
- group_dec_caches_[thread].InitOnce(frame_header_.passes.num_passes,
- dec_state_->used_acs);
+ JXL_RETURN_IF_ERROR(group_dec_caches_[thread].InitOnce(
+ frame_header_.passes.num_passes, dec_state_->used_acs));
JXL_RETURN_IF_ERROR(DecodeGroup(frame_header_, br, num_passes, ac_group_id,
dec_state_, &group_dec_caches_[thread],
thread, render_pipeline_input, decoded_,
@@ -498,10 +502,16 @@ Status FrameDecoder::ProcessACGroup(size_t ac_group_id,
size_t pass1 =
force_draw ? frame_header_.passes.num_passes : pass0 + num_passes;
for (size_t i = pass0; i < pass1; ++i) {
- int minShift, maxShift;
+ int minShift;
+ int maxShift;
frame_header_.passes.GetDownsamplingBracket(i, minShift, maxShift);
bool modular_pass_ready = true;
+ JXL_DEBUG_V(2, "Decoding modular in group %d pass %d",
+ static_cast<int>(ac_group_id), static_cast<int>(i));
if (i < pass0 + num_passes) {
+ JXL_DEBUG_V(2, "Bit reader position: %" PRIuS " / %" PRIuS,
+ br[i - pass0]->TotalBitsConsumed(),
+ br[i - pass0]->TotalBytes() * kBitsPerByte);
JXL_RETURN_IF_ERROR(modular_frame_decoder_.DecodeGroup(
frame_header_, mrect, br[i - pass0], minShift, maxShift,
ModularStreamId::ModularAC(ac_group_id, i),
@@ -546,7 +556,7 @@ Status FrameDecoder::ProcessACGroup(size_t ac_group_id,
if (!modular_frame_decoder_.UsesFullImage() && !decoded_->IsJPEG()) {
if (should_run_pipeline && modular_ready) {
- render_pipeline_input.Done();
+ JXL_RETURN_IF_ERROR(render_pipeline_input.Done());
} else if (force_draw) {
return JXL_FAILURE("Modular group decoding failed.");
}
@@ -555,11 +565,11 @@ Status FrameDecoder::ProcessACGroup(size_t ac_group_id,
}
void FrameDecoder::MarkSections(const SectionInfo* sections, size_t num,
- SectionStatus* section_status) {
+ const SectionStatus* section_status) {
num_sections_done_ += num;
for (size_t i = 0; i < num; i++) {
if (section_status[i] != SectionStatus::kDone) {
- processed_section_[sections[i].id] = false;
+ processed_section_[sections[i].id] = JXL_FALSE;
num_sections_done_--;
}
}
@@ -583,8 +593,8 @@ Status FrameDecoder::ProcessSections(const SectionInfo* sections, size_t num,
if (single_section) {
JXL_ASSERT(num == 1);
JXL_ASSERT(sections[0].id == 0);
- if (processed_section_[0] == false) {
- processed_section_[0] = true;
+ if (processed_section_[0] == JXL_FALSE) {
+ processed_section_[0] = JXL_TRUE;
ac_group_sec[0].resize(1);
dc_global_sec = ac_global_sec = dc_group_sec[0] = ac_group_sec[0][0] = 0;
desired_num_ac_passes[0] = 1;
@@ -614,7 +624,7 @@ Status FrameDecoder::ProcessSections(const SectionInfo* sections, size_t num,
}
ac_group_sec[acg][acp] = i;
}
- processed_section_[sections[i].id] = true;
+ processed_section_[sections[i].id] = JXL_TRUE;
}
// Count number of new passes per group.
for (size_t g = 0; g < ac_group_sec.size(); g++) {
@@ -645,9 +655,11 @@ Status FrameDecoder::ProcessSections(const SectionInfo* sections, size_t num,
pool_, 0, dc_group_sec.size(), ThreadPool::NoInit,
[this, &dc_group_sec, &num, &sections, &section_status, &has_error](
size_t i, size_t thread) {
+ if (has_error) return;
if (dc_group_sec[i] != num) {
if (!ProcessDCGroup(i, sections[dc_group_sec[i]].br)) {
has_error = true;
+ return;
} else {
section_status[dc_group_sec[i]] = SectionStatus::kDone;
}
@@ -657,8 +669,7 @@ Status FrameDecoder::ProcessSections(const SectionInfo* sections, size_t num,
}
if (has_error) return JXL_FAILURE("Error in DC group");
- if (*std::min_element(decoded_dc_groups_.begin(), decoded_dc_groups_.end()) &&
- !finalized_dc_) {
+ if (!HasDcGroupToDecode() && !finalized_dc_) {
PassesDecoderState::PipelineOptions pipeline_options;
pipeline_options.use_slow_render_pipeline = use_slow_rendering_pipeline_;
pipeline_options.coalescing = coalescing_;
@@ -666,7 +677,7 @@ Status FrameDecoder::ProcessSections(const SectionInfo* sections, size_t num,
pipeline_options.render_noise = true;
JXL_RETURN_IF_ERROR(
dec_state_->PreparePipeline(frame_header_, decoded_, pipeline_options));
- FinalizeDC();
+ JXL_RETURN_IF_ERROR(FinalizeDC());
JXL_RETURN_IF_ERROR(AllocateOutput());
if (progressive_detail_ >= JxlProgressiveDetail::kDC) {
MarkSections(sections, num, section_status);
@@ -776,21 +787,22 @@ Status FrameDecoder::Flush() {
decoded_passes_per_ac_group_.size());
},
[this, &has_error](const uint32_t g, size_t thread) {
+ if (has_error) return;
if (decoded_passes_per_ac_group_[g] ==
frame_header_.passes.num_passes) {
// This group was drawn already, nothing to do.
return;
}
BitReader* JXL_RESTRICT readers[kMaxNumPasses] = {};
- bool ok = ProcessACGroup(
- g, readers, /*num_passes=*/0, GetStorageLocation(thread, g),
- /*force_draw=*/true, /*dc_only=*/!decoded_ac_global_);
- if (!ok) has_error = true;
+ if (!ProcessACGroup(
+ g, readers, /*num_passes=*/0, GetStorageLocation(thread, g),
+ /*force_draw=*/true, /*dc_only=*/!decoded_ac_global_)) {
+ has_error = true;
+ return;
+ }
},
"ForceDrawGroup"));
- if (has_error) {
- return JXL_FAILURE("Drawing groups failed");
- }
+ if (has_error) return JXL_FAILURE("Drawing groups failed");
}
// undo global modular transforms and copy int pixel buffers to float ones
@@ -815,10 +827,8 @@ int FrameDecoder::SavedAs(const FrameHeader& header) {
bool FrameDecoder::HasEverything() const {
if (!decoded_dc_global_) return false;
if (!decoded_ac_global_) return false;
- for (auto& have_dc_group : decoded_dc_groups_) {
- if (!have_dc_group) return false;
- }
- for (auto& nb_passes : decoded_passes_per_ac_group_) {
+ if (HasDcGroupToDecode()) return false;
+ for (const auto& nb_passes : decoded_passes_per_ac_group_) {
if (nb_passes < frame_header_.passes.num_passes) return false;
}
return true;
@@ -840,9 +850,9 @@ int FrameDecoder::References() const {
result |= (1 << frame_header_.blending_info.source);
}
const auto& extra = frame_header_.extra_channel_blending_info;
- for (size_t i = 0; i < extra.size(); ++i) {
- if (cropped || extra[i].mode != BlendMode::kReplace) {
- result |= (1 << extra[i].source);
+ for (const auto& ecbi : extra) {
+ if (cropped || ecbi.mode != BlendMode::kReplace) {
+ result |= (1 << ecbi.source);
}
}
}
diff --git a/third_party/jpeg-xl/lib/jxl/dec_frame.h b/third_party/jpeg-xl/lib/jxl/dec_frame.h
index 09bdbc9675..663f1a8b33 100644
--- a/third_party/jpeg-xl/lib/jxl/dec_frame.h
+++ b/third_party/jpeg-xl/lib/jxl/dec_frame.h
@@ -242,14 +242,14 @@ class FrameDecoder {
private:
Status ProcessDCGlobal(BitReader* br);
Status ProcessDCGroup(size_t dc_group_id, BitReader* br);
- void FinalizeDC();
+ Status FinalizeDC();
Status AllocateOutput();
Status ProcessACGlobal(BitReader* br);
Status ProcessACGroup(size_t ac_group_id, BitReader* JXL_RESTRICT* br,
size_t num_passes, size_t thread, bool force_draw,
bool dc_only);
void MarkSections(const SectionInfo* sections, size_t num,
- SectionStatus* section_status);
+ const SectionStatus* section_status);
// Allocates storage for parallel decoding using up to `num_threads` threads
// of up to `num_tasks` tasks. The value of `thread` passed to
@@ -262,9 +262,10 @@ class FrameDecoder {
group_dec_caches_.resize(storage_size);
}
use_task_id_ = num_threads > num_tasks;
- bool use_group_ids = (modular_frame_decoder_.UsesFullImage() &&
- (frame_header_.encoding == FrameEncoding::kVarDCT ||
- (frame_header_.flags & FrameHeader::kNoise)));
+ bool use_noise = (frame_header_.flags & FrameHeader::kNoise) != 0;
+ bool use_group_ids =
+ (modular_frame_decoder_.UsesFullImage() &&
+ (frame_header_.encoding == FrameEncoding::kVarDCT || use_noise));
if (dec_state_->render_pipeline) {
JXL_RETURN_IF_ERROR(dec_state_->render_pipeline->PrepareForThreads(
storage_size, use_group_ids));
@@ -272,7 +273,7 @@ class FrameDecoder {
return true;
}
- size_t GetStorageLocation(size_t thread, size_t task) {
+ size_t GetStorageLocation(size_t thread, size_t task) const {
if (use_task_id_) return task;
return thread;
}
@@ -292,6 +293,11 @@ class FrameDecoder {
return stride;
}
+ bool HasDcGroupToDecode() const {
+ return std::any_of(decoded_dc_groups_.cbegin(), decoded_dc_groups_.cend(),
+ [](uint8_t ready) { return ready == 0; });
+ }
+
PassesDecoderState* dec_state_;
ThreadPool* pool_;
std::vector<TocEntry> toc_;
diff --git a/third_party/jpeg-xl/lib/jxl/dec_group.cc b/third_party/jpeg-xl/lib/jxl/dec_group.cc
index 186318e63d..7dc4772eba 100644
--- a/third_party/jpeg-xl/lib/jxl/dec_group.cc
+++ b/third_party/jpeg-xl/lib/jxl/dec_group.cc
@@ -27,13 +27,10 @@
#include "lib/jxl/base/status.h"
#include "lib/jxl/coeff_order.h"
#include "lib/jxl/common.h" // kMaxNumPasses
-#include "lib/jxl/convolve.h"
-#include "lib/jxl/dct_scales.h"
#include "lib/jxl/dec_cache.h"
#include "lib/jxl/dec_transforms-inl.h"
#include "lib/jxl/dec_xyb.h"
#include "lib/jxl/entropy_coder.h"
-#include "lib/jxl/epf.h"
#include "lib/jxl/quant_weights.h"
#include "lib/jxl/quantizer-inl.h"
#include "lib/jxl/quantizer.h"
@@ -70,6 +67,11 @@ namespace jxl {
namespace HWY_NAMESPACE {
// These templates are not found via ADL.
+using hwy::HWY_NAMESPACE::AllFalse;
+using hwy::HWY_NAMESPACE::Gt;
+using hwy::HWY_NAMESPACE::Le;
+using hwy::HWY_NAMESPACE::MaskFromVec;
+using hwy::HWY_NAMESPACE::Or;
using hwy::HWY_NAMESPACE::Rebind;
using hwy::HWY_NAMESPACE::ShiftRight;
@@ -77,9 +79,11 @@ using D = HWY_FULL(float);
using DU = HWY_FULL(uint32_t);
using DI = HWY_FULL(int32_t);
using DI16 = Rebind<int16_t, DI>;
+using DI16_FULL = HWY_CAPPED(int16_t, kDCTBlockSize);
constexpr D d;
constexpr DI di;
constexpr DI16 di16;
+constexpr DI16_FULL di16_full;
// TODO(veluca): consider SIMDfying.
void Transpose8x8InPlace(int32_t* JXL_RESTRICT block) {
@@ -181,6 +185,9 @@ Status DecodeGroupImpl(const FrameHeader& frame_header,
const YCbCrChromaSubsampling& cs = frame_header.chroma_subsampling;
+ const auto kJpegDctMin = Set(di16_full, -4095);
+ const auto kJpegDctMax = Set(di16_full, 4095);
+
size_t idct_stride[3];
for (size_t c = 0; c < 3; c++) {
idct_stride[c] = render_pipeline_input.GetBuffer(c).first->PixelsPerRow();
@@ -355,7 +362,7 @@ Status DecodeGroupImpl(const FrameHeader& frame_header,
int16_t* JXL_RESTRICT jpeg_pos =
jpeg_row[c] + sbx[c] * kDCTBlockSize;
// JPEG XL is transposed, JPEG is not.
- auto transposed_dct = qblock[c].ptr32;
+ auto* transposed_dct = qblock[c].ptr32;
Transpose8x8InPlace(transposed_dct);
// No CfL - no need to store the y block converted to integers.
if (!cs.Is444() ||
@@ -391,6 +398,16 @@ Status DecodeGroupImpl(const FrameHeader& frame_header,
}
jpeg_pos[0] =
Clamp1<float>(dc_rows[c][sbx[c]] - dcoff[c], -2047, 2047);
+ auto overflow = MaskFromVec(Set(di16_full, 0));
+ auto underflow = MaskFromVec(Set(di16_full, 0));
+ for (int i = 0; i < 64; i += Lanes(di16_full)) {
+ auto in = LoadU(di16_full, jpeg_pos + i);
+ overflow = Or(overflow, Gt(in, kJpegDctMax));
+ underflow = Or(underflow, Lt(in, kJpegDctMin));
+ }
+ if (!AllFalse(di16_full, Or(overflow, underflow))) {
+ return JXL_FAILURE("JPEG DCT coefficients out of range");
+ }
}
} else {
HWY_ALIGN float* const block = group_dec_cache->dec_group_block;
@@ -683,7 +700,7 @@ Status DecodeGroup(const FrameHeader& frame_header,
}
if (draw == kDraw && num_passes == 0 && first_pass == 0) {
- group_dec_cache->InitDCBufferOnce();
+ JXL_RETURN_IF_ERROR(group_dec_cache->InitDCBufferOnce());
const YCbCrChromaSubsampling& cs = frame_header.chroma_subsampling;
for (size_t c : {0, 1, 2}) {
size_t hs = cs.HShift(c);
@@ -726,7 +743,7 @@ Status DecodeGroup(const FrameHeader& frame_header,
y++) {
for (ssize_t iy = 0; iy < 5; iy++) {
input_rows[0][iy] = group_dec_cache->dc_buffer.Row(
- Mirror(ssize_t(y) + iy - 2,
+ Mirror(static_cast<ssize_t>(y) + iy - 2,
dec_state->shared->dc->Plane(c).ysize() >> vs) +
2 - src_rect.y0());
}
@@ -736,9 +753,9 @@ Status DecodeGroup(const FrameHeader& frame_header,
kRenderPipelineXOffset;
}
// Arguments set to 0/nullptr are not used.
- dec_state->upsampler8x->ProcessRow(input_rows, output_rows,
- /*xextra=*/0, src_rect.xsize(), 0, 0,
- thread);
+ JXL_RETURN_IF_ERROR(dec_state->upsampler8x->ProcessRow(
+ input_rows, output_rows,
+ /*xextra=*/0, src_rect.xsize(), 0, 0, thread));
}
}
return true;
@@ -780,9 +797,9 @@ Status DecodeGroupForRoundtrip(const FrameHeader& frame_header,
ImageBundle* JXL_RESTRICT decoded,
AuxOut* aux_out) {
GetBlockFromEncoder get_block(ac, group_idx, frame_header.passes.shift);
- group_dec_cache->InitOnce(
+ JXL_RETURN_IF_ERROR(group_dec_cache->InitOnce(
/*num_passes=*/0,
- /*used_acs=*/(1u << AcStrategy::kNumValidStrategies) - 1);
+ /*used_acs=*/(1u << AcStrategy::kNumValidStrategies) - 1));
return HWY_DYNAMIC_DISPATCH(DecodeGroupImpl)(
frame_header, &get_block, group_dec_cache, dec_state, thread, group_idx,
diff --git a/third_party/jpeg-xl/lib/jxl/dec_huffman.cc b/third_party/jpeg-xl/lib/jxl/dec_huffman.cc
index 05b275773a..849b1a5f64 100644
--- a/third_party/jpeg-xl/lib/jxl/dec_huffman.cc
+++ b/third_party/jpeg-xl/lib/jxl/dec_huffman.cc
@@ -5,6 +5,7 @@
#include "lib/jxl/dec_huffman.h"
+#include <jxl/types.h>
#include <string.h> /* for memset */
#include <vector>
@@ -22,9 +23,9 @@ static const uint8_t kCodeLengthCodeOrder[kCodeLengthCodes] = {
static const uint8_t kDefaultCodeLength = 8;
static const uint8_t kCodeLengthRepeatCode = 16;
-int ReadHuffmanCodeLengths(const uint8_t* code_length_code_lengths,
- int num_symbols, uint8_t* code_lengths,
- BitReader* br) {
+JXL_BOOL ReadHuffmanCodeLengths(const uint8_t* code_length_code_lengths,
+ int num_symbols, uint8_t* code_lengths,
+ BitReader* br) {
int symbol = 0;
uint8_t prev_code_len = kDefaultCodeLength;
int repeat = 0;
@@ -38,7 +39,7 @@ int ReadHuffmanCodeLengths(const uint8_t* code_length_code_lengths,
}
if (!BuildHuffmanTable(table, 5, code_length_code_lengths, kCodeLengthCodes,
&counts[0])) {
- return 0;
+ return JXL_FALSE;
}
while (symbol < num_symbols && space > 0) {
@@ -47,7 +48,7 @@ int ReadHuffmanCodeLengths(const uint8_t* code_length_code_lengths,
br->Refill();
p += br->PeekFixedBits<5>();
br->Consume(p->bits);
- code_len = (uint8_t)p->value;
+ code_len = static_cast<uint8_t>(p->value);
if (code_len < kCodeLengthRepeatCode) {
repeat = 0;
code_lengths[symbol++] = code_len;
@@ -72,12 +73,13 @@ int ReadHuffmanCodeLengths(const uint8_t* code_length_code_lengths,
repeat -= 2;
repeat <<= extra_bits;
}
- repeat += (int)br->ReadBits(extra_bits) + 3;
+ repeat += static_cast<int>(br->ReadBits(extra_bits) + 3);
repeat_delta = repeat - old_repeat;
if (symbol + repeat_delta > num_symbols) {
return 0;
}
- memset(&code_lengths[symbol], repeat_code_len, (size_t)repeat_delta);
+ memset(&code_lengths[symbol], repeat_code_len,
+ static_cast<size_t>(repeat_delta));
symbol += repeat_delta;
if (repeat_code_len != 0) {
space -= repeat_delta << (15 - repeat_code_len);
@@ -85,10 +87,10 @@ int ReadHuffmanCodeLengths(const uint8_t* code_length_code_lengths,
}
}
if (space != 0) {
- return 0;
+ return JXL_FALSE;
}
- memset(&code_lengths[symbol], 0, (size_t)(num_symbols - symbol));
- return true;
+ memset(&code_lengths[symbol], 0, static_cast<size_t>(num_symbols - symbol));
+ return JXL_TRUE;
}
static JXL_INLINE bool ReadSimpleCode(size_t alphabet_size, BitReader* br,
@@ -176,7 +178,7 @@ static JXL_INLINE bool ReadSimpleCode(size_t alphabet_size, BitReader* br,
const uint32_t goal_size = 1u << kHuffmanTableBits;
while (table_size != goal_size) {
memcpy(&table[table_size], &table[0],
- (size_t)table_size * sizeof(table[0]));
+ static_cast<size_t>(table_size) * sizeof(table[0]));
table_size <<= 1;
}
@@ -212,16 +214,17 @@ bool HuffmanDecodingData::ReadFromBitStream(size_t alphabet_size,
br->Refill();
p += br->PeekFixedBits<4>();
br->Consume(p->bits);
- v = (uint8_t)p->value;
+ v = static_cast<uint8_t>(p->value);
code_length_code_lengths[code_len_idx] = v;
if (v != 0) {
space -= (32u >> v);
++num_codes;
}
}
- bool ok = (num_codes == 1 || space == 0) &&
- ReadHuffmanCodeLengths(code_length_code_lengths, alphabet_size,
- &code_lengths[0], br);
+ bool ok =
+ (num_codes == 1 || space == 0) &&
+ FROM_JXL_BOOL(ReadHuffmanCodeLengths(
+ code_length_code_lengths, alphabet_size, code_lengths.data(), br));
if (!ok) return false;
uint16_t counts[16] = {0};
@@ -230,7 +233,7 @@ bool HuffmanDecodingData::ReadFromBitStream(size_t alphabet_size,
}
table_.resize(alphabet_size + 376);
uint32_t table_size =
- BuildHuffmanTable(table_.data(), kHuffmanTableBits, &code_lengths[0],
+ BuildHuffmanTable(table_.data(), kHuffmanTableBits, code_lengths.data(),
alphabet_size, &counts[0]);
table_.resize(table_size);
return (table_size > 0);
diff --git a/third_party/jpeg-xl/lib/jxl/dec_modular.cc b/third_party/jpeg-xl/lib/jxl/dec_modular.cc
index 4fcba489e2..49561e6ec2 100644
--- a/third_party/jpeg-xl/lib/jxl/dec_modular.cc
+++ b/third_party/jpeg-xl/lib/jxl/dec_modular.cc
@@ -8,7 +8,6 @@
#include <stdint.h>
#include <atomic>
-#include <sstream>
#include <vector>
#include "lib/jxl/frame_header.h"
@@ -18,10 +17,8 @@
#include <hwy/foreach_target.h>
#include <hwy/highway.h>
-#include "lib/jxl/alpha.h"
#include "lib/jxl/base/compiler_specific.h"
#include "lib/jxl/base/printf_macros.h"
-#include "lib/jxl/base/span.h"
#include "lib/jxl/base/status.h"
#include "lib/jxl/compressed_dc.h"
#include "lib/jxl/epf.h"
@@ -189,7 +186,7 @@ Status ModularFrameDecoder::DecodeGlobalInfo(BitReader* reader,
}
do_color = decode_color;
size_t nb_extra = metadata.extra_channel_info.size();
- bool has_tree = reader->ReadBits(1);
+ bool has_tree = static_cast<bool>(reader->ReadBits(1));
if (!allow_truncated_group ||
reader->TotalBitsConsumed() < reader->TotalBytes() * kBitsPerByte) {
if (has_tree) {
@@ -216,8 +213,10 @@ Status ModularFrameDecoder::DecodeGlobalInfo(BitReader* reader,
}
}
- Image gi(frame_dim.xsize, frame_dim.ysize, metadata.bit_depth.bits_per_sample,
- nb_chans + nb_extra);
+ JXL_ASSIGN_OR_RETURN(
+ Image gi,
+ Image::Create(frame_dim.xsize, frame_dim.ysize,
+ metadata.bit_depth.bits_per_sample, nb_chans + nb_extra));
all_same_shift = true;
if (frame_header.color_transform == ColorTransform::kYCbCr) {
@@ -228,7 +227,7 @@ Status ModularFrameDecoder::DecodeGlobalInfo(BitReader* reader,
DivCeil(frame_dim.xsize, 1 << gi.channel[c].hshift);
size_t ysize_shifted =
DivCeil(frame_dim.ysize, 1 << gi.channel[c].vshift);
- gi.channel[c].shrink(xsize_shifted, ysize_shifted);
+ JXL_RETURN_IF_ERROR(gi.channel[c].shrink(xsize_shifted, ysize_shifted));
if (gi.channel[c].hshift != gi.channel[0].hshift ||
gi.channel[c].vshift != gi.channel[0].vshift)
all_same_shift = false;
@@ -237,8 +236,9 @@ Status ModularFrameDecoder::DecodeGlobalInfo(BitReader* reader,
for (size_t ec = 0, c = nb_chans; ec < nb_extra; ec++, c++) {
size_t ecups = frame_header.extra_channel_upsampling[ec];
- gi.channel[c].shrink(DivCeil(frame_dim.xsize_upsampled, ecups),
- DivCeil(frame_dim.ysize_upsampled, ecups));
+ JXL_RETURN_IF_ERROR(
+ gi.channel[c].shrink(DivCeil(frame_dim.xsize_upsampled, ecups),
+ DivCeil(frame_dim.ysize_upsampled, ecups)));
gi.channel[c].hshift = gi.channel[c].vshift =
CeilLog2Nonzero(ecups) - CeilLog2Nonzero(frame_header.upsampling);
if (gi.channel[c].hshift != gi.channel[0].hshift ||
@@ -306,7 +306,8 @@ Status ModularFrameDecoder::DecodeGroup(
stream.kind == ModularStreamId::kModularAC);
const size_t xsize = rect.xsize();
const size_t ysize = rect.ysize();
- Image gi(xsize, ysize, full_image.bitdepth, 0);
+ JXL_ASSIGN_OR_RETURN(Image gi,
+ Image::Create(xsize, ysize, full_image.bitdepth, 0));
// start at the first bigger-than-groupsize non-metachannel
size_t c = full_image.nb_meta_channels;
for (; c < full_image.channel.size(); c++) {
@@ -328,7 +329,7 @@ Status ModularFrameDecoder::DecodeGroup(
memset(row_out, 0, r.xsize() * sizeof(*row_out));
}
} else {
- Channel gc(r.xsize(), r.ysize());
+ JXL_ASSIGN_OR_RETURN(Channel gc, Channel::Create(r.xsize(), r.ysize()));
if (zerofill) ZeroFillImage(&gc.plane);
gc.hshift = fc.hshift;
gc.vshift = fc.vshift;
@@ -398,7 +399,8 @@ Status ModularFrameDecoder::DecodeVarDCTDC(const FrameHeader& frame_header,
// 3 comes from XybToRgb that cubes the values, and "magic" is
// the sum of all other contributions. 2**18 is known to lead
// to NaN on input found by fuzzing (see commit message).
- Image image(r.xsize(), r.ysize(), full_image.bitdepth, 3);
+ JXL_ASSIGN_OR_RETURN(
+ Image image, Image::Create(r.xsize(), r.ysize(), full_image.bitdepth, 3));
size_t stream_id = ModularStreamId::VarDCTDC(group_id).ID(frame_dim);
reader->Refill();
size_t extra_precision = reader->ReadFixedBits<2>();
@@ -408,12 +410,13 @@ Status ModularFrameDecoder::DecodeVarDCTDC(const FrameHeader& frame_header,
Channel& ch = image.channel[c < 2 ? c ^ 1 : c];
ch.w >>= frame_header.chroma_subsampling.HShift(c);
ch.h >>= frame_header.chroma_subsampling.VShift(c);
- ch.shrink();
+ JXL_RETURN_IF_ERROR(ch.shrink());
}
if (!ModularGenericDecompress(
reader, image, /*header=*/nullptr, stream_id, &options,
/*undo_transforms=*/true, &tree, &code, &context_map)) {
- return JXL_FAILURE("Failed to decode VarDCT DC group");
+ return JXL_FAILURE("Failed to decode VarDCT DC group (DC group id %d)",
+ static_cast<int>(group_id));
}
DequantDC(r, &dec_state->shared_storage.dc_storage,
&dec_state->shared_storage.quant_dc, image,
@@ -433,12 +436,15 @@ Status ModularFrameDecoder::DecodeAcMetadata(const FrameHeader& frame_header,
size_t count = reader->ReadBits(CeilLog2Nonzero(upper_bound)) + 1;
size_t stream_id = ModularStreamId::ACMetadata(group_id).ID(frame_dim);
// YToX, YToB, ACS + QF, EPF
- Image image(r.xsize(), r.ysize(), full_image.bitdepth, 4);
+ JXL_ASSIGN_OR_RETURN(
+ Image image, Image::Create(r.xsize(), r.ysize(), full_image.bitdepth, 4));
static_assert(kColorTileDimInBlocks == 8, "Color tile size changed");
Rect cr(r.x0() >> 3, r.y0() >> 3, (r.xsize() + 7) >> 3, (r.ysize() + 7) >> 3);
- image.channel[0] = Channel(cr.xsize(), cr.ysize(), 3, 3);
- image.channel[1] = Channel(cr.xsize(), cr.ysize(), 3, 3);
- image.channel[2] = Channel(count, 2, 0, 0);
+ JXL_ASSIGN_OR_RETURN(image.channel[0],
+ Channel::Create(cr.xsize(), cr.ysize(), 3, 3));
+ JXL_ASSIGN_OR_RETURN(image.channel[1],
+ Channel::Create(cr.xsize(), cr.ysize(), 3, 3));
+ JXL_ASSIGN_OR_RETURN(image.channel[2], Channel::Create(count, 2, 0, 0));
ModularOptions options;
if (!ModularGenericDecompress(
reader, image, /*header=*/nullptr, stream_id, &options,
@@ -513,7 +519,7 @@ Status ModularFrameDecoder::DecodeAcMetadata(const FrameHeader& frame_header,
Status ModularFrameDecoder::ModularImageToDecodedRect(
const FrameHeader& frame_header, Image& gi, PassesDecoderState* dec_state,
jxl::ThreadPool* pool, RenderPipelineInput& render_pipeline_input,
- Rect modular_rect) {
+ Rect modular_rect) const {
const auto* metadata = frame_header.nonserialized_metadata;
JXL_CHECK(gi.transform.empty());
@@ -686,7 +692,12 @@ Status ModularFrameDecoder::FinalizeDecoding(const FrameHeader& frame_header,
jxl::ThreadPool* pool,
bool inplace) {
if (!use_full_image) return true;
- Image gi = (inplace ? std::move(full_image) : full_image.clone());
+ Image gi;
+ if (inplace) {
+ gi = std::move(full_image);
+ } else {
+ JXL_ASSIGN_OR_RETURN(gi, Image::Clone(full_image));
+ }
size_t xsize = gi.w;
size_t ysize = gi.h;
@@ -714,6 +725,7 @@ Status ModularFrameDecoder::FinalizeDecoding(const FrameHeader& frame_header,
use_group_ids);
},
[&](const uint32_t group, size_t thread_id) {
+ if (has_error) return;
RenderPipelineInput input =
dec_state->render_pipeline->GetInputBuffers(group, thread_id);
if (!ModularImageToDecodedRect(
@@ -722,12 +734,13 @@ Status ModularFrameDecoder::FinalizeDecoding(const FrameHeader& frame_header,
has_error = true;
return;
}
- input.Done();
+ if (!input.Done()) {
+ has_error = true;
+ return;
+ }
},
"ModularToRect"));
- if (has_error) {
- return JXL_FAILURE("Error producing input to render pipeline");
- }
+ if (has_error) return JXL_FAILURE("Error producing input to render pipeline");
return true;
}
@@ -743,7 +756,8 @@ Status ModularFrameDecoder::DecodeQuantTable(
// be negative.
return JXL_FAILURE("Invalid qtable_den: value too small");
}
- Image image(required_size_x, required_size_y, 8, 3);
+ JXL_ASSIGN_OR_RETURN(Image image,
+ Image::Create(required_size_x, required_size_y, 8, 3));
ModularOptions options;
if (modular_frame_decoder) {
JXL_RETURN_IF_ERROR(ModularGenericDecompress(
diff --git a/third_party/jpeg-xl/lib/jxl/dec_modular.h b/third_party/jpeg-xl/lib/jxl/dec_modular.h
index 58a6562740..23caad0c16 100644
--- a/third_party/jpeg-xl/lib/jxl/dec_modular.h
+++ b/third_party/jpeg-xl/lib/jxl/dec_modular.h
@@ -123,7 +123,7 @@ class ModularFrameDecoder {
PassesDecoderState* dec_state,
jxl::ThreadPool* pool,
RenderPipelineInput& render_pipeline_input,
- Rect modular_rect);
+ Rect modular_rect) const;
Image full_image;
std::vector<Transform> global_transform;
diff --git a/third_party/jpeg-xl/lib/jxl/dec_noise.cc b/third_party/jpeg-xl/lib/jxl/dec_noise.cc
index ae46b1062f..24f0136490 100644
--- a/third_party/jpeg-xl/lib/jxl/dec_noise.cc
+++ b/third_party/jpeg-xl/lib/jxl/dec_noise.cc
@@ -108,9 +108,8 @@ void Random3Planes(size_t visible_frame_index, size_t nonvisible_frame_index,
size_t x0, size_t y0, const std::pair<ImageF*, Rect>& plane0,
const std::pair<ImageF*, Rect>& plane1,
const std::pair<ImageF*, Rect>& plane2) {
- return HWY_DYNAMIC_DISPATCH(Random3Planes)(visible_frame_index,
- nonvisible_frame_index, x0, y0,
- plane0, plane1, plane2);
+ HWY_DYNAMIC_DISPATCH(Random3Planes)
+ (visible_frame_index, nonvisible_frame_index, x0, y0, plane0, plane1, plane2);
}
void DecodeFloatParam(float precision, float* val, BitReader* br) {
diff --git a/third_party/jpeg-xl/lib/jxl/dec_patch_dictionary.cc b/third_party/jpeg-xl/lib/jxl/dec_patch_dictionary.cc
index 0ae2223252..120a977ca7 100644
--- a/third_party/jpeg-xl/lib/jxl/dec_patch_dictionary.cc
+++ b/third_party/jpeg-xl/lib/jxl/dec_patch_dictionary.cc
@@ -10,26 +10,16 @@
#include <sys/types.h>
#include <algorithm>
-#include <string>
-#include <tuple>
#include <utility>
#include <vector>
-#include "lib/jxl/ans_params.h"
-#include "lib/jxl/base/compiler_specific.h"
-#include "lib/jxl/base/override.h"
#include "lib/jxl/base/printf_macros.h"
#include "lib/jxl/base/status.h"
#include "lib/jxl/blending.h"
-#include "lib/jxl/chroma_from_luma.h"
#include "lib/jxl/common.h" // kMaxNumReferenceFrames
#include "lib/jxl/dec_ans.h"
-#include "lib/jxl/dec_frame.h"
-#include "lib/jxl/entropy_coder.h"
-#include "lib/jxl/frame_header.h"
#include "lib/jxl/image.h"
#include "lib/jxl/image_bundle.h"
-#include "lib/jxl/image_ops.h"
#include "lib/jxl/pack_signed.h"
#include "lib/jxl/patch_dictionary_internal.h"
@@ -138,7 +128,8 @@ Status PatchDictionary::Decode(BitReader* br, size_t xsize, size_t ysize,
}
for (size_t j = 0; j < num_ec + 1; j++) {
uint32_t blend_mode = read_num(kPatchBlendModeContext);
- if (blend_mode >= uint32_t(PatchBlendMode::kNumBlendModes)) {
+ if (blend_mode >=
+ static_cast<uint32_t>(PatchBlendMode::kNumBlendModes)) {
return JXL_FAILURE("Invalid patch blend mode: %u", blend_mode);
}
PatchBlending info;
@@ -157,21 +148,22 @@ Status PatchDictionary::Decode(BitReader* br, size_t xsize, size_t ysize,
return JXL_FAILURE(
"Invalid alpha channel for blending: %u out of %u\n",
info.alpha_channel,
- (uint32_t)shared_->metadata->m.extra_channel_info.size());
+ static_cast<uint32_t>(
+ shared_->metadata->m.extra_channel_info.size()));
}
} else {
info.alpha_channel = 0;
}
if (UsesClamp(info.mode)) {
- info.clamp = read_num(kPatchClampContext);
+ info.clamp = static_cast<bool>(read_num(kPatchClampContext));
} else {
info.clamp = false;
}
blendings_.push_back(info);
}
- positions_.push_back(std::move(pos));
+ positions_.emplace_back(pos);
}
- ref_positions_.emplace_back(std::move(ref_pos));
+ ref_positions_.emplace_back(ref_pos);
}
positions_.shrink_to_fit();
@@ -185,8 +177,8 @@ Status PatchDictionary::Decode(BitReader* br, size_t xsize, size_t ysize,
int PatchDictionary::GetReferences() const {
int result = 0;
- for (size_t i = 0; i < ref_positions_.size(); ++i) {
- result |= (1 << static_cast<int>(ref_positions_[i].ref));
+ for (const auto& ref_pos : ref_positions_) {
+ result |= (1 << static_cast<int>(ref_pos.ref));
}
return result;
}
@@ -263,11 +255,11 @@ void PatchDictionary::ComputePatchTree() {
node.start = sorted_patches_y0_.size();
for (ssize_t i = static_cast<ssize_t>(right_start) - 1;
i >= static_cast<ssize_t>(left_end); --i) {
- sorted_patches_y1_.push_back({intervals[i].y1, intervals[i].idx});
+ sorted_patches_y1_.emplace_back(intervals[i].y1, intervals[i].idx);
}
sort_by_y0(left_end, right_start);
for (size_t i = left_end; i < right_start; ++i) {
- sorted_patches_y0_.push_back({intervals[i].y0, intervals[i].idx});
+ sorted_patches_y0_.emplace_back(intervals[i].y0, intervals[i].idx);
}
// Create the left and right nodes (if not empty).
node.left_child = node.right_child = -1;
@@ -294,7 +286,7 @@ std::vector<size_t> PatchDictionary::GetPatchesForRow(size_t y) const {
if (y < num_patches_.size() && num_patches_[y] > 0) {
result.reserve(num_patches_[y]);
for (ssize_t tree_idx = 0; tree_idx != -1;) {
- JXL_DASSERT(tree_idx < (ssize_t)patch_tree_.size());
+ JXL_DASSERT(tree_idx < static_cast<ssize_t>(patch_tree_.size()));
const auto& node = patch_tree_[tree_idx];
if (y <= node.y_center) {
for (size_t i = 0; i < node.num; ++i) {
@@ -322,8 +314,8 @@ std::vector<size_t> PatchDictionary::GetPatchesForRow(size_t y) const {
// Adds patches to a segment of `xsize` pixels, starting at `inout`, assumed
// to be located at position (x0, y) in the frame.
-void PatchDictionary::AddOneRow(float* const* inout, size_t y, size_t x0,
- size_t xsize) const {
+Status PatchDictionary::AddOneRow(float* const* inout, size_t y, size_t x0,
+ size_t xsize) const {
size_t num_ec = shared_->metadata->m.num_extra_channels;
std::vector<const float*> fg_ptrs(3 + num_ec);
for (size_t pos_idx : GetPatchesForRow(y)) {
@@ -352,10 +344,11 @@ void PatchDictionary::AddOneRow(float* const* inout, size_t y, size_t x0,
ref_pos.y0 + iy) +
ref_pos.x0 + x0 - bx;
}
- PerformBlending(inout, fg_ptrs.data(), inout, patch_x0 - x0,
- patch_x1 - patch_x0, blendings_[blending_idx],
- blendings_.data() + blending_idx + 1,
- shared_->metadata->m.extra_channel_info);
+ JXL_RETURN_IF_ERROR(PerformBlending(
+ inout, fg_ptrs.data(), inout, patch_x0 - x0, patch_x1 - patch_x0,
+ blendings_[blending_idx], blendings_.data() + blending_idx + 1,
+ shared_->metadata->m.extra_channel_info));
}
+ return true;
}
} // namespace jxl
diff --git a/third_party/jpeg-xl/lib/jxl/dec_patch_dictionary.h b/third_party/jpeg-xl/lib/jxl/dec_patch_dictionary.h
index aac6111ae6..72dca8f057 100644
--- a/third_party/jpeg-xl/lib/jxl/dec_patch_dictionary.h
+++ b/third_party/jpeg-xl/lib/jxl/dec_patch_dictionary.h
@@ -12,12 +12,10 @@
#include <string.h>
#include <sys/types.h>
-#include <tuple>
#include <vector>
#include "lib/jxl/base/status.h"
#include "lib/jxl/dec_bit_reader.h"
-#include "lib/jxl/image.h"
namespace jxl {
@@ -109,7 +107,8 @@ class PatchDictionary {
// Adds patches to a segment of `xsize` pixels, starting at `inout`, assumed
// to be located at position (x0, y) in the frame.
- void AddOneRow(float* const* inout, size_t y, size_t x0, size_t xsize) const;
+ Status AddOneRow(float* const* inout, size_t y, size_t x0,
+ size_t xsize) const;
// Returns dependencies of this patch dictionary on reference frame ids as a
// bit mask: bits 0-3 indicate reference frame 0-3.
diff --git a/third_party/jpeg-xl/lib/jxl/dec_transforms_testonly.cc b/third_party/jpeg-xl/lib/jxl/dec_transforms_testonly.cc
index 2d40740262..6451c41f6d 100644
--- a/third_party/jpeg-xl/lib/jxl/dec_transforms_testonly.cc
+++ b/third_party/jpeg-xl/lib/jxl/dec_transforms_testonly.cc
@@ -21,21 +21,21 @@ void TransformToPixels(AcStrategy::Type strategy,
float* JXL_RESTRICT coefficients,
float* JXL_RESTRICT pixels, size_t pixels_stride,
float* scratch_space) {
- return HWY_DYNAMIC_DISPATCH(TransformToPixels)(strategy, coefficients, pixels,
- pixels_stride, scratch_space);
+ HWY_DYNAMIC_DISPATCH(TransformToPixels)
+ (strategy, coefficients, pixels, pixels_stride, scratch_space);
}
HWY_EXPORT(LowestFrequenciesFromDC);
void LowestFrequenciesFromDC(const jxl::AcStrategy::Type strategy,
const float* dc, size_t dc_stride, float* llf,
float* JXL_RESTRICT scratch) {
- return HWY_DYNAMIC_DISPATCH(LowestFrequenciesFromDC)(strategy, dc, dc_stride,
- llf, scratch);
+ HWY_DYNAMIC_DISPATCH(LowestFrequenciesFromDC)
+ (strategy, dc, dc_stride, llf, scratch);
}
HWY_EXPORT(AFVIDCT4x4);
void AFVIDCT4x4(const float* JXL_RESTRICT coeffs, float* JXL_RESTRICT pixels) {
- return HWY_DYNAMIC_DISPATCH(AFVIDCT4x4)(coeffs, pixels);
+ HWY_DYNAMIC_DISPATCH(AFVIDCT4x4)(coeffs, pixels);
}
#endif // HWY_ONCE
diff --git a/third_party/jpeg-xl/lib/jxl/dec_transforms_testonly.h b/third_party/jpeg-xl/lib/jxl/dec_transforms_testonly.h
index f68481fda9..2cbf6bd5f6 100644
--- a/third_party/jpeg-xl/lib/jxl/dec_transforms_testonly.h
+++ b/third_party/jpeg-xl/lib/jxl/dec_transforms_testonly.h
@@ -22,8 +22,8 @@ void TransformToPixels(AcStrategy::Type strategy,
float* JXL_RESTRICT scratch_space);
// Equivalent of the above for DC image.
-void LowestFrequenciesFromDC(const jxl::AcStrategy::Type strategy,
- const float* dc, size_t dc_stride, float* llf,
+void LowestFrequenciesFromDC(jxl::AcStrategy::Type strategy, const float* dc,
+ size_t dc_stride, float* llf,
float* JXL_RESTRICT scratch);
void AFVIDCT4x4(const float* JXL_RESTRICT coeffs, float* JXL_RESTRICT pixels);
diff --git a/third_party/jpeg-xl/lib/jxl/dec_xyb-inl.h b/third_party/jpeg-xl/lib/jxl/dec_xyb-inl.h
index 495693b257..09a8013960 100644
--- a/third_party/jpeg-xl/lib/jxl/dec_xyb-inl.h
+++ b/third_party/jpeg-xl/lib/jxl/dec_xyb-inl.h
@@ -83,7 +83,7 @@ HWY_INLINE HWY_MAYBE_UNUSED void XybToRgb(D d, const V opsin_x, const V opsin_y,
*linear_b = MulAdd(LoadDup128(d, &inverse_matrix[8 * 4]), mixed_b, *linear_b);
}
-static inline HWY_MAYBE_UNUSED bool HasFastXYBTosRGB8() {
+inline HWY_MAYBE_UNUSED bool HasFastXYBTosRGB8() {
#if HWY_TARGET == HWY_NEON
return true;
#else
@@ -91,9 +91,9 @@ static inline HWY_MAYBE_UNUSED bool HasFastXYBTosRGB8() {
#endif
}
-static inline HWY_MAYBE_UNUSED void FastXYBTosRGB8(const float* input[4],
- uint8_t* output,
- bool is_rgba, size_t xsize) {
+inline HWY_MAYBE_UNUSED void FastXYBTosRGB8(const float* input[4],
+ uint8_t* output, bool is_rgba,
+ size_t xsize) {
// This function is very NEON-specific. As such, it uses intrinsics directly.
#if HWY_TARGET == HWY_NEON
// WARNING: doing fixed point arithmetic correctly is very complicated.
diff --git a/third_party/jpeg-xl/lib/jxl/dec_xyb.cc b/third_party/jpeg-xl/lib/jxl/dec_xyb.cc
index 7010f0d813..a719b3eb8c 100644
--- a/third_party/jpeg-xl/lib/jxl/dec_xyb.cc
+++ b/third_party/jpeg-xl/lib/jxl/dec_xyb.cc
@@ -160,20 +160,19 @@ namespace jxl {
HWY_EXPORT(OpsinToLinearInplace);
void OpsinToLinearInplace(Image3F* JXL_RESTRICT inout, ThreadPool* pool,
const OpsinParams& opsin_params) {
- return HWY_DYNAMIC_DISPATCH(OpsinToLinearInplace)(inout, pool, opsin_params);
+ HWY_DYNAMIC_DISPATCH(OpsinToLinearInplace)(inout, pool, opsin_params);
}
HWY_EXPORT(OpsinToLinear);
void OpsinToLinear(const Image3F& opsin, const Rect& rect, ThreadPool* pool,
Image3F* JXL_RESTRICT linear,
const OpsinParams& opsin_params) {
- return HWY_DYNAMIC_DISPATCH(OpsinToLinear)(opsin, rect, pool, linear,
- opsin_params);
+ HWY_DYNAMIC_DISPATCH(OpsinToLinear)(opsin, rect, pool, linear, opsin_params);
}
HWY_EXPORT(YcbcrToRgb);
void YcbcrToRgb(const Image3F& ycbcr, Image3F* rgb, const Rect& rect) {
- return HWY_DYNAMIC_DISPATCH(YcbcrToRgb)(ycbcr, rgb, rect);
+ HWY_DYNAMIC_DISPATCH(YcbcrToRgb)(ycbcr, rgb, rect);
}
HWY_EXPORT(HasFastXYBTosRGB8);
@@ -182,7 +181,7 @@ bool HasFastXYBTosRGB8() { return HWY_DYNAMIC_DISPATCH(HasFastXYBTosRGB8)(); }
HWY_EXPORT(FastXYBTosRGB8);
void FastXYBTosRGB8(const float* input[4], uint8_t* output, bool is_rgba,
size_t xsize) {
- return HWY_DYNAMIC_DISPATCH(FastXYBTosRGB8)(input, output, is_rgba, xsize);
+ HWY_DYNAMIC_DISPATCH(FastXYBTosRGB8)(input, output, is_rgba, xsize);
}
void OpsinParams::Init(float intensity_target) {
@@ -218,7 +217,7 @@ Status OutputEncodingInfo::SetFromMetadata(const CodecMetadata& metadata) {
orig_intensity_target = metadata.m.IntensityTarget();
desired_intensity_target = orig_intensity_target;
const auto& im = metadata.transform_data.opsin_inverse_matrix;
- memcpy(orig_inverse_matrix, im.inverse_matrix, sizeof(orig_inverse_matrix));
+ orig_inverse_matrix = im.inverse_matrix;
default_transform = im.all_default;
xyb_encoded = metadata.m.xyb_encoded;
std::copy(std::begin(im.opsin_biases), std::end(im.opsin_biases),
@@ -258,38 +257,38 @@ Status OutputEncodingInfo::SetColorEncoding(const ColorEncoding& c_desired) {
// Compute the opsin inverse matrix and luminances based on primaries and
// white point.
- float inverse_matrix[9];
+ Matrix3x3 inverse_matrix;
bool inverse_matrix_is_default = default_transform;
- memcpy(inverse_matrix, orig_inverse_matrix, sizeof(inverse_matrix));
- constexpr float kSRGBLuminances[3] = {0.2126, 0.7152, 0.0722};
- memcpy(luminances, kSRGBLuminances, sizeof(luminances));
+ inverse_matrix = orig_inverse_matrix;
+ constexpr Vector3 kSRGBLuminances{0.2126, 0.7152, 0.0722};
+ luminances = kSRGBLuminances;
if ((c_desired.GetPrimariesType() != Primaries::kSRGB ||
c_desired.GetWhitePointType() != WhitePoint::kD65) &&
!c_desired.IsGray()) {
- float srgb_to_xyzd50[9];
+ Matrix3x3 srgb_to_xyzd50;
const auto& srgb = ColorEncoding::SRGB(/*is_gray=*/false);
PrimariesCIExy p = srgb.GetPrimaries();
CIExy w = srgb.GetWhitePoint();
JXL_CHECK(PrimariesToXYZD50(p.r.x, p.r.y, p.g.x, p.g.y, p.b.x, p.b.y, w.x,
w.y, srgb_to_xyzd50));
- float original_to_xyz[3][3];
+ Matrix3x3 original_to_xyz;
p = c_desired.GetPrimaries();
w = c_desired.GetWhitePoint();
if (!PrimariesToXYZ(p.r.x, p.r.y, p.g.x, p.g.y, p.b.x, p.b.y, w.x, w.y,
- &original_to_xyz[0][0])) {
+ original_to_xyz)) {
return JXL_FAILURE("PrimariesToXYZ failed");
}
- memcpy(luminances, original_to_xyz[1], sizeof luminances);
+ luminances = original_to_xyz[1];
if (xyb_encoded) {
- float adapt_to_d50[9];
+ Matrix3x3 adapt_to_d50;
if (!AdaptToXYZD50(c_desired.GetWhitePoint().x,
c_desired.GetWhitePoint().y, adapt_to_d50)) {
return JXL_FAILURE("AdaptToXYZD50 failed");
}
- float xyzd50_to_original[9];
- Mul3x3Matrix(adapt_to_d50, &original_to_xyz[0][0], xyzd50_to_original);
+ Matrix3x3 xyzd50_to_original;
+ Mul3x3Matrix(adapt_to_d50, original_to_xyz, xyzd50_to_original);
JXL_RETURN_IF_ERROR(Inv3x3Matrix(xyzd50_to_original));
- float srgb_to_original[9];
+ Matrix3x3 srgb_to_original;
Mul3x3Matrix(xyzd50_to_original, srgb_to_xyzd50, srgb_to_original);
Mul3x3Matrix(srgb_to_original, orig_inverse_matrix, inverse_matrix);
inverse_matrix_is_default = false;
@@ -297,12 +296,8 @@ Status OutputEncodingInfo::SetColorEncoding(const ColorEncoding& c_desired) {
}
if (c_desired.IsGray()) {
- float tmp_inv_matrix[9];
- memcpy(tmp_inv_matrix, inverse_matrix, sizeof(inverse_matrix));
- float srgb_to_luma[9];
- memcpy(&srgb_to_luma[0], luminances, sizeof(luminances));
- memcpy(&srgb_to_luma[3], luminances, sizeof(luminances));
- memcpy(&srgb_to_luma[6], luminances, sizeof(luminances));
+ Matrix3x3 tmp_inv_matrix = inverse_matrix;
+ Matrix3x3 srgb_to_luma{luminances, luminances, luminances};
Mul3x3Matrix(srgb_to_luma, tmp_inv_matrix, inverse_matrix);
}
diff --git a/third_party/jpeg-xl/lib/jxl/dec_xyb.h b/third_party/jpeg-xl/lib/jxl/dec_xyb.h
index ddfd555632..65317f2f54 100644
--- a/third_party/jpeg-xl/lib/jxl/dec_xyb.h
+++ b/third_party/jpeg-xl/lib/jxl/dec_xyb.h
@@ -39,7 +39,7 @@ struct OutputEncodingInfo {
// Used for the HLG OOTF and PQ tone mapping.
float orig_intensity_target;
// Opsin inverse matrix taken from the metadata.
- float orig_inverse_matrix[9];
+ Matrix3x3 orig_inverse_matrix;
bool default_transform;
bool xyb_encoded;
//
@@ -60,7 +60,7 @@ struct OutputEncodingInfo {
// Luminances of color_encoding's primaries, used for the HLG inverse OOTF and
// for PQ tone mapping.
// Default to sRGB's.
- float luminances[3];
+ Vector3 luminances;
// Used for the HLG inverse OOTF and PQ tone mapping.
float desired_intensity_target;
bool cms_set = false;
diff --git a/third_party/jpeg-xl/lib/jxl/decode.cc b/third_party/jpeg-xl/lib/jxl/decode.cc
index b674d1ba88..605c2d6fd6 100644
--- a/third_party/jpeg-xl/lib/jxl/decode.cc
+++ b/third_party/jpeg-xl/lib/jxl/decode.cc
@@ -721,7 +721,7 @@ void JxlDecoderRewindDecodingState(JxlDecoder* dec) {
dec->events_wanted = dec->orig_events_wanted;
dec->basic_info_size_hint = InitialBasicInfoSizeHint();
- dec->have_container = 0;
+ dec->have_container = false;
dec->box_count = 0;
dec->downsampling_target = 8;
dec->image_out_buffer_set = false;
@@ -733,7 +733,7 @@ void JxlDecoderRewindDecodingState(JxlDecoder* dec) {
dec->image_out_size = 0;
dec->image_out_bit_depth.type = JXL_BIT_DEPTH_FROM_PIXEL_FORMAT;
dec->extra_channel_output.clear();
- dec->next_in = 0;
+ dec->next_in = nullptr;
dec->avail_in = 0;
dec->input_closed = false;
@@ -839,9 +839,9 @@ void JxlDecoderSkipFrames(JxlDecoder* dec, size_t amount) {
internal_index, dec->frame_saved_as, dec->frame_references);
dec->frame_required.resize(internal_index + 1, 0);
- for (size_t i = 0; i < deps.size(); i++) {
- JXL_ASSERT(deps[i] < dec->frame_required.size());
- dec->frame_required[deps[i]] = 1;
+ for (size_t idx : deps) {
+ JXL_ASSERT(idx < dec->frame_required.size());
+ dec->frame_required[idx] = 1;
}
}
}
@@ -894,7 +894,7 @@ JxlDecoderStatus JxlDecoderSetKeepOrientation(JxlDecoder* dec,
if (dec->stage != DecoderStage::kInited) {
return JXL_API_ERROR("Must set keep_orientation option before starting");
}
- dec->keep_orientation = !!skip_reorientation;
+ dec->keep_orientation = FROM_JXL_BOOL(skip_reorientation);
return JXL_DEC_SUCCESS;
}
@@ -903,7 +903,7 @@ JxlDecoderStatus JxlDecoderSetUnpremultiplyAlpha(JxlDecoder* dec,
if (dec->stage != DecoderStage::kInited) {
return JXL_API_ERROR("Must set unpremul_alpha option before starting");
}
- dec->unpremul_alpha = !!unpremul_alpha;
+ dec->unpremul_alpha = FROM_JXL_BOOL(unpremul_alpha);
return JXL_DEC_SUCCESS;
}
@@ -912,7 +912,7 @@ JxlDecoderStatus JxlDecoderSetRenderSpotcolors(JxlDecoder* dec,
if (dec->stage != DecoderStage::kInited) {
return JXL_API_ERROR("Must set render_spotcolors option before starting");
}
- dec->render_spotcolors = !!render_spotcolors;
+ dec->render_spotcolors = FROM_JXL_BOOL(render_spotcolors);
return JXL_DEC_SUCCESS;
}
@@ -920,7 +920,7 @@ JxlDecoderStatus JxlDecoderSetCoalescing(JxlDecoder* dec, JXL_BOOL coalescing) {
if (dec->stage != DecoderStage::kInited) {
return JXL_API_ERROR("Must set coalescing option before starting");
}
- dec->coalescing = !!coalescing;
+ dec->coalescing = FROM_JXL_BOOL(coalescing);
return JXL_DEC_SUCCESS;
}
@@ -1076,7 +1076,7 @@ JxlDecoderStatus JxlDecoderReadAllHeaders(JxlDecoder* dec) {
return JXL_DEC_ERROR;
}
IccBytes icc;
- Bytes(decoded_icc).AppendTo(&icc);
+ Bytes(decoded_icc).AppendTo(icc);
dec->metadata.m.color_encoding.SetICCRaw(std::move(icc));
}
@@ -1118,7 +1118,7 @@ JxlDecoderStatus JxlDecoderProcessSections(JxlDecoder* dec) {
if (OutOfBounds(pos, size, span.size())) {
break;
}
- auto br = new jxl::BitReader(jxl::Bytes(span.data() + pos, size));
+ auto* br = new jxl::BitReader(jxl::Bytes(span.data() + pos, size));
section_info.emplace_back(jxl::FrameDecoder::SectionInfo{br, id, i});
section_status.emplace_back();
pos += size;
@@ -1263,9 +1263,9 @@ JxlDecoderStatus JxlDecoderProcessCodestream(JxlDecoder* dec) {
frame_dim.ysize_upsampled_padded)) {
return JXL_INPUT_ERROR("frame is too large");
}
- bool output_needed =
- (dec->preview_frame ? (dec->events_wanted & JXL_DEC_PREVIEW_IMAGE)
- : (dec->events_wanted & JXL_DEC_FULL_IMAGE));
+ int output_type =
+ dec->preview_frame ? JXL_DEC_PREVIEW_IMAGE : JXL_DEC_FULL_IMAGE;
+ bool output_needed = ((dec->events_wanted & output_type) != 0);
if (output_needed) {
JXL_API_RETURN_IF_ERROR(dec->frame_dec->InitFrameOutput());
}
@@ -1376,7 +1376,7 @@ JxlDecoderStatus JxlDecoderProcessCodestream(JxlDecoder* dec) {
} else {
dec->frame_prog_detail = JxlProgressiveDetail::kFrames;
}
- dec->dc_frame_progression_done = 0;
+ dec->dc_frame_progression_done = false;
dec->next_section = 0;
dec->section_processed.clear();
@@ -1413,7 +1413,8 @@ JxlDecoderStatus JxlDecoderProcessCodestream(JxlDecoder* dec) {
}
if (dec->image_out_buffer_set) {
- size_t xsize, ysize;
+ size_t xsize;
+ size_t ysize;
GetCurrentDimensions(dec, xsize, ysize);
size_t bits_per_sample = GetBitDepth(
dec->image_out_bit_depth, dec->metadata.m, dec->image_out_format);
@@ -1765,7 +1766,8 @@ static JxlDecoderStatus HandleBoxes(JxlDecoder* dec) {
return JXL_DEC_SUCCESS;
}
- uint64_t box_size, header_size;
+ uint64_t box_size;
+ uint64_t header_size;
JxlDecoderStatus status =
ParseBoxHeader(dec->next_in, dec->avail_in, 0, dec->file_pos,
dec->box_type, &box_size, &header_size);
@@ -2042,7 +2044,7 @@ JxlDecoderStatus JxlDecoderProcessInput(JxlDecoder* dec) {
dec->got_signature = true;
if (sig == JXL_SIG_CONTAINER) {
- dec->have_container = 1;
+ dec->have_container = true;
} else {
dec->last_codestream_seen = true;
}
@@ -2083,16 +2085,16 @@ JxlDecoderStatus JxlDecoderGetBasicInfo(const JxlDecoder* dec,
const jxl::ImageMetadata& meta = dec->metadata.m;
- info->have_container = dec->have_container;
+ info->have_container = TO_JXL_BOOL(dec->have_container);
info->xsize = dec->metadata.size.xsize();
info->ysize = dec->metadata.size.ysize();
- info->uses_original_profile = !meta.xyb_encoded;
+ info->uses_original_profile = TO_JXL_BOOL(!meta.xyb_encoded);
info->bits_per_sample = meta.bit_depth.bits_per_sample;
info->exponent_bits_per_sample = meta.bit_depth.exponent_bits_per_sample;
- info->have_preview = meta.have_preview;
- info->have_animation = meta.have_animation;
+ info->have_preview = TO_JXL_BOOL(meta.have_preview);
+ info->have_animation = TO_JXL_BOOL(meta.have_animation);
info->orientation = static_cast<JxlOrientation>(meta.orientation);
if (!dec->keep_orientation) {
@@ -2107,14 +2109,15 @@ JxlDecoderStatus JxlDecoderGetBasicInfo(const JxlDecoder* dec,
info->intensity_target = dec->desired_intensity_target;
}
info->min_nits = meta.tone_mapping.min_nits;
- info->relative_to_max_display = meta.tone_mapping.relative_to_max_display;
+ info->relative_to_max_display =
+ TO_JXL_BOOL(meta.tone_mapping.relative_to_max_display);
info->linear_below = meta.tone_mapping.linear_below;
const jxl::ExtraChannelInfo* alpha = meta.Find(jxl::ExtraChannel::kAlpha);
if (alpha != nullptr) {
info->alpha_bits = alpha->bit_depth.bits_per_sample;
info->alpha_exponent_bits = alpha->bit_depth.exponent_bits_per_sample;
- info->alpha_premultiplied = alpha->alpha_associated;
+ info->alpha_premultiplied = TO_JXL_BOOL(alpha->alpha_associated);
} else {
info->alpha_bits = 0;
info->alpha_exponent_bits = 0;
@@ -2136,7 +2139,8 @@ JxlDecoderStatus JxlDecoderGetBasicInfo(const JxlDecoder* dec,
info->animation.tps_denominator =
dec->metadata.m.animation.tps_denominator;
info->animation.num_loops = dec->metadata.m.animation.num_loops;
- info->animation.have_timecodes = dec->metadata.m.animation.have_timecodes;
+ info->animation.have_timecodes =
+ TO_JXL_BOOL(dec->metadata.m.animation.have_timecodes);
}
if (meta.have_intrinsic_size) {
@@ -2170,7 +2174,7 @@ JxlDecoderStatus JxlDecoderGetExtraChannelInfo(const JxlDecoder* dec,
: 0;
info->dim_shift = channel.dim_shift;
info->name_length = channel.name.size();
- info->alpha_premultiplied = channel.alpha_associated;
+ info->alpha_premultiplied = TO_JXL_BOOL(channel.alpha_associated);
info->spot_color[0] = channel.spot_color[0];
info->spot_color[1] = channel.spot_color[1];
info->spot_color[2] = channel.spot_color[2];
@@ -2348,29 +2352,41 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderSetCms(JxlDecoder* dec,
return JXL_DEC_SUCCESS;
}
-JXL_EXPORT JxlDecoderStatus JxlDecoderPreviewOutBufferSize(
- const JxlDecoder* dec, const JxlPixelFormat* format, size_t* size) {
+static JxlDecoderStatus GetMinSize(const JxlDecoder* dec,
+ const JxlPixelFormat* format,
+ size_t num_channels, size_t* min_size,
+ bool preview) {
size_t bits;
JxlDecoderStatus status = PrepareSizeCheck(dec, format, &bits);
if (status != JXL_DEC_SUCCESS) return status;
- if (format->num_channels < 3 &&
- !dec->image_metadata.color_encoding.IsGray()) {
- return JXL_API_ERROR("Number of channels is too low for color output");
+ size_t xsize;
+ size_t ysize;
+ if (preview) {
+ xsize = dec->metadata.oriented_preview_xsize(dec->keep_orientation);
+ ysize = dec->metadata.oriented_preview_ysize(dec->keep_orientation);
+ } else {
+ GetCurrentDimensions(dec, xsize, ysize);
}
-
- size_t xsize = dec->metadata.oriented_preview_xsize(dec->keep_orientation);
- size_t ysize = dec->metadata.oriented_preview_ysize(dec->keep_orientation);
-
+ if (num_channels == 0) num_channels = format->num_channels;
size_t row_size =
- jxl::DivCeil(xsize * format->num_channels * bits, jxl::kBitsPerByte);
+ jxl::DivCeil(xsize * num_channels * bits, jxl::kBitsPerByte);
size_t last_row_size = row_size;
if (format->align > 1) {
row_size = jxl::DivCeil(row_size, format->align) * format->align;
}
- *size = row_size * (ysize - 1) + last_row_size;
+ *min_size = row_size * (ysize - 1) + last_row_size;
return JXL_DEC_SUCCESS;
}
+JXL_EXPORT JxlDecoderStatus JxlDecoderPreviewOutBufferSize(
+ const JxlDecoder* dec, const JxlPixelFormat* format, size_t* size) {
+ if (format->num_channels < 3 &&
+ !dec->image_metadata.color_encoding.IsGray()) {
+ return JXL_API_ERROR("Number of channels is too low for color output");
+ }
+ return GetMinSize(dec, format, 0, size, true);
+}
+
JXL_EXPORT JxlDecoderStatus JxlDecoderSetPreviewOutBuffer(
JxlDecoder* dec, const JxlPixelFormat* format, void* buffer, size_t size) {
if (!dec->got_basic_info || !dec->metadata.m.have_preview ||
@@ -2401,23 +2417,12 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderSetPreviewOutBuffer(
JXL_EXPORT JxlDecoderStatus JxlDecoderImageOutBufferSize(
const JxlDecoder* dec, const JxlPixelFormat* format, size_t* size) {
- size_t bits;
- JxlDecoderStatus status = PrepareSizeCheck(dec, format, &bits);
- if (status != JXL_DEC_SUCCESS) return status;
if (format->num_channels < 3 &&
!dec->image_metadata.color_encoding.IsGray()) {
return JXL_API_ERROR("Number of channels is too low for color output");
}
- size_t xsize, ysize;
- GetCurrentDimensions(dec, xsize, ysize);
- size_t row_size =
- jxl::DivCeil(xsize * format->num_channels * bits, jxl::kBitsPerByte);
- if (format->align > 1) {
- row_size = jxl::DivCeil(row_size, format->align) * format->align;
- }
- *size = row_size * ysize;
- return JXL_DEC_SUCCESS;
+ return GetMinSize(dec, format, 0, size, false);
}
JxlDecoderStatus JxlDecoderSetImageOutBuffer(JxlDecoder* dec,
@@ -2463,22 +2468,7 @@ JxlDecoderStatus JxlDecoderExtraChannelBufferSize(const JxlDecoder* dec,
return JXL_API_ERROR("Invalid extra channel index");
}
- size_t num_channels = 1; // Do not use format's num_channels
-
- size_t bits;
- JxlDecoderStatus status = PrepareSizeCheck(dec, format, &bits);
- if (status != JXL_DEC_SUCCESS) return status;
-
- size_t xsize, ysize;
- GetCurrentDimensions(dec, xsize, ysize);
- size_t row_size =
- jxl::DivCeil(xsize * num_channels * bits, jxl::kBitsPerByte);
- if (format->align > 1) {
- row_size = jxl::DivCeil(row_size, format->align) * format->align;
- }
- *size = row_size * ysize;
-
- return JXL_DEC_SUCCESS;
+ return GetMinSize(dec, format, 1, size, false);
}
JxlDecoderStatus JxlDecoderSetExtraChannelBuffer(JxlDecoder* dec,
@@ -2577,8 +2567,9 @@ JxlDecoderStatus JxlDecoderGetFrameHeader(const JxlDecoder* dec,
}
}
header->name_length = dec->frame_header->name.size();
- header->is_last = dec->frame_header->is_last;
- size_t xsize, ysize;
+ header->is_last = TO_JXL_BOOL(dec->frame_header->is_last);
+ size_t xsize;
+ size_t ysize;
GetCurrentDimensions(dec, xsize, ysize);
header->layer_info.xsize = xsize;
header->layer_info.ysize = ysize;
@@ -2620,7 +2611,7 @@ JxlDecoderStatus JxlDecoderGetFrameHeader(const JxlDecoder* dec,
header->layer_info.blend_info.alpha =
dec->frame_header->blending_info.alpha_channel;
header->layer_info.blend_info.clamp =
- dec->frame_header->blending_info.clamp;
+ TO_JXL_BOOL(dec->frame_header->blending_info.clamp);
header->layer_info.save_as_reference = dec->frame_header->save_as_reference;
}
return JXL_DEC_SUCCESS;
@@ -2643,7 +2634,7 @@ JxlDecoderStatus JxlDecoderGetExtraChannelBlendInfo(const JxlDecoder* dec,
blend_info->alpha =
dec->frame_header->extra_channel_blending_info[index].alpha_channel;
blend_info->clamp =
- dec->frame_header->extra_channel_blending_info[index].clamp;
+ TO_JXL_BOOL(dec->frame_header->extra_channel_blending_info[index].clamp);
return JXL_DEC_SUCCESS;
}
@@ -2717,7 +2708,7 @@ JxlDecoderStatus JxlDecoderSetOutputColorProfile(
"setting output color profile from icc_data not yet implemented.");
}
JXL_API_RETURN_IF_ERROR(
- (int)output_encoding.MaybeSetColorEncoding(std::move(c_dst)));
+ static_cast<int>(output_encoding.MaybeSetColorEncoding(c_dst)));
return JXL_DEC_SUCCESS;
}
@@ -2769,7 +2760,7 @@ JxlDecoderStatus JxlDecoderSetDecompressBoxes(JxlDecoder* dec,
JXL_BOOL decompress) {
// TODO(lode): return error if libbrotli is not compiled in the jxl decoding
// library
- dec->decompress_boxes = decompress;
+ dec->decompress_boxes = FROM_JXL_BOOL(decompress);
return JXL_DEC_SUCCESS;
}
@@ -2798,6 +2789,17 @@ JxlDecoderStatus JxlDecoderGetBoxSizeRaw(const JxlDecoder* dec,
return JXL_DEC_SUCCESS;
}
+JxlDecoderStatus JxlDecoderGetBoxSizeContents(const JxlDecoder* dec,
+ uint64_t* size) {
+ if (!dec->box_event) {
+ return JXL_API_ERROR("can only get box info after JXL_DEC_BOX event");
+ }
+ if (size) {
+ *size = dec->box_contents_size;
+ }
+ return JXL_DEC_SUCCESS;
+}
+
JxlDecoderStatus JxlDecoderSetProgressiveDetail(JxlDecoder* dec,
JxlProgressiveDetail detail) {
if (detail != kDC && detail != kLastPasses && detail != kPasses) {
diff --git a/third_party/jpeg-xl/lib/jxl/decode_test.cc b/third_party/jpeg-xl/lib/jxl/decode_test.cc
index caee6dbc56..33176cfd66 100644
--- a/third_party/jpeg-xl/lib/jxl/decode_test.cc
+++ b/third_party/jpeg-xl/lib/jxl/decode_test.cc
@@ -47,7 +47,6 @@
#include "lib/jxl/dec_bit_reader.h"
#include "lib/jxl/dec_external_image.h"
#include "lib/jxl/enc_aux_out.h"
-#include "lib/jxl/enc_butteraugli_comparator.h"
#include "lib/jxl/enc_external_image.h"
#include "lib/jxl/enc_fields.h"
#include "lib/jxl/enc_frame.h"
@@ -113,23 +112,23 @@ enum CodeStreamBoxFormat {
};
// Unknown boxes for testing
-static const char* unk1_box_type = "unk1";
-static const char* unk1_box_contents = "abcdefghijklmnopqrstuvwxyz";
-static const size_t unk1_box_size = strlen(unk1_box_contents);
-static const char* unk2_box_type = "unk2";
-static const char* unk2_box_contents = "0123456789";
-static const size_t unk2_box_size = strlen(unk2_box_contents);
-static const char* unk3_box_type = "unk3";
-static const char* unk3_box_contents = "ABCDEF123456";
-static const size_t unk3_box_size = strlen(unk3_box_contents);
+const char* unk1_box_type = "unk1";
+const char* unk1_box_contents = "abcdefghijklmnopqrstuvwxyz";
+const size_t unk1_box_size = strlen(unk1_box_contents);
+const char* unk2_box_type = "unk2";
+const char* unk2_box_contents = "0123456789";
+const size_t unk2_box_size = strlen(unk2_box_contents);
+const char* unk3_box_type = "unk3";
+const char* unk3_box_contents = "ABCDEF123456";
+const size_t unk3_box_size = strlen(unk3_box_contents);
// Box with brob-compressed exif, including header
-static const uint8_t* box_brob_exif = reinterpret_cast<const uint8_t*>(
+const uint8_t* box_brob_exif = reinterpret_cast<const uint8_t*>(
"\0\0\0@brobExif\241\350\2\300\177\244v\2525\304\360\27=?\267{"
"\33\37\314\332\214QX17PT\"\256\0\0\202s\214\313t\333\310\320k\20\276\30"
"\204\277l$\326c#\1\b");
size_t box_brob_exif_size = 64;
// The uncompressed Exif data from the brob box
-static const uint8_t* exif_uncompressed = reinterpret_cast<const uint8_t*>(
+const uint8_t* exif_uncompressed = reinterpret_cast<const uint8_t*>(
"\0\0\0\0MM\0*"
"\0\0\0\b\0\5\1\22\0\3\0\0\0\1\0\5\0\0\1\32\0\5\0\0\0\1\0\0\0J\1\33\0\5\0\0"
"\0\1\0\0\0R\1("
@@ -193,7 +192,7 @@ void AppendTestBox(const char* type, const char* contents, size_t contents_size,
bytes->push_back(type[2]);
bytes->push_back(type[3]);
const uint8_t* contents_u = reinterpret_cast<const uint8_t*>(contents);
- Bytes(contents_u, contents_size).AppendTo(bytes);
+ Bytes(contents_u, contents_size).AppendTo(*bytes);
}
enum PreviewMode {
@@ -214,13 +213,15 @@ void GeneratePreview(PreviewMode preview_mode, ImageBundle* ib) {
}
}
};
- Image3F preview(ib->xsize() * 7, ib->ysize() * 7);
+ JXL_ASSIGN_OR_DIE(Image3F preview,
+ Image3F::Create(ib->xsize() * 7, ib->ysize() * 7));
for (size_t c = 0; c < 3; ++c) {
upsample7(ib->color()->Plane(c), &preview.Plane(c));
}
std::vector<ImageF> extra_channels;
for (size_t i = 0; i < ib->extra_channels().size(); ++i) {
- ImageF ec(ib->xsize() * 7, ib->ysize() * 7);
+ JXL_ASSIGN_OR_DIE(ImageF ec,
+ ImageF::Create(ib->xsize() * 7, ib->ysize() * 7));
upsample7(ib->extra_channels()[i], &ec);
extra_channels.emplace_back(std::move(ec));
}
@@ -258,7 +259,8 @@ std::vector<uint8_t> CreateTestJXLCodestream(
const TestCodestreamParams& params) {
// Compress the pixels with JPEG XL.
bool grayscale = (num_channels <= 2);
- bool include_alpha = !(num_channels & 1) && params.jpeg_codestream == nullptr;
+ bool have_alpha = ((num_channels & 1) == 0);
+ bool include_alpha = have_alpha && params.jpeg_codestream == nullptr;
size_t bitdepth = params.jpeg_codestream == nullptr ? 16 : 8;
CodecInOut io;
io.SetSize(xsize, ysize);
@@ -296,7 +298,8 @@ std::vector<uint8_t> CreateTestJXLCodestream(
if (jxl::extras::CanDecode(jxl::extras::Codec::kJPG)) {
std::vector<uint8_t> jpeg_bytes;
extras::PackedPixelFile ppf;
- extras::PackedFrame frame(xsize, ysize, format);
+ JXL_ASSIGN_OR_DIE(extras::PackedFrame frame,
+ extras::PackedFrame::Create(xsize, ysize, format));
JXL_ASSERT(frame.color.pixels_size == pixels.size());
memcpy(frame.color.pixels(0, 0, 0), pixels.data(), pixels.size());
ppf.frames.emplace_back(std::move(frame));
@@ -307,9 +310,9 @@ std::vector<uint8_t> CreateTestJXLCodestream(
auto encoder = extras::GetJPEGEncoder();
encoder->SetOption("quality", "70");
extras::EncodedImage encoded;
- EXPECT_TRUE(encoder->Encode(ppf, &encoded));
+ EXPECT_TRUE(encoder->Encode(ppf, &encoded, nullptr));
jpeg_bytes = encoded.bitstreams[0];
- Bytes(jpeg_bytes).AppendTo(params.jpeg_codestream);
+ Bytes(jpeg_bytes).AppendTo(*params.jpeg_codestream);
EXPECT_TRUE(jxl::jpeg::DecodeImageJPG(
jxl::Bytes(jpeg_bytes.data(), jpeg_bytes.size()), &io));
EXPECT_TRUE(
@@ -321,7 +324,7 @@ std::vector<uint8_t> CreateTestJXLCodestream(
}
}
if (params.preview_mode) {
- io.preview_frame = io.Main().Copy();
+ JXL_ASSIGN_OR_DIE(io.preview_frame, io.Main().Copy());
GeneratePreview(params.preview_mode, &io.preview_frame);
io.metadata.m.have_preview = true;
EXPECT_TRUE(io.metadata.m.preview_size.Set(io.preview_frame.xsize(),
@@ -358,11 +361,11 @@ std::vector<uint8_t> CreateTestJXLCodestream(
compressed.data() + compressed.size());
std::vector<uint8_t> c;
- Bytes(header).AppendTo(&c);
+ Bytes(header).AppendTo(c);
if (params.jpeg_codestream != nullptr) {
jxl::AppendBoxHeader(jxl::MakeBoxType("jbrd"), jpeg_data.size(), false,
&c);
- Bytes(jpeg_data).AppendTo(&c);
+ Bytes(jpeg_data).AppendTo(c);
}
uint32_t jxlp_index = 0;
if (add_container == kCSBF_Multi_First_Empty) {
@@ -381,7 +384,7 @@ std::vector<uint8_t> CreateTestJXLCodestream(
c.push_back('l');
c.push_back('p');
AppendU32BE(jxlp_index++, &c);
- Bytes(compressed0).AppendTo(&c);
+ Bytes(compressed0).AppendTo(c);
// A few non-codestream boxes in between
AppendTestBox(unk1_box_type, unk1_box_contents, unk1_box_size, false, &c);
AppendTestBox(unk2_box_type, unk2_box_contents, unk2_box_size, false, &c);
@@ -399,7 +402,7 @@ std::vector<uint8_t> CreateTestJXLCodestream(
c.push_back('l');
c.push_back('p');
AppendU32BE(jxlp_index++, &c);
- Bytes(compressed1).AppendTo(&c);
+ Bytes(compressed1).AppendTo(c);
// Third (last) codestream part
AppendU32BE(add_container == kCSBF_Multi_Zero_Terminated
? 0
@@ -414,7 +417,7 @@ std::vector<uint8_t> CreateTestJXLCodestream(
} else {
AppendU32BE(jxlp_index++, &c);
}
- Bytes(compressed2).AppendTo(&c);
+ Bytes(compressed2).AppendTo(c);
if (add_container == kCSBF_Multi_Last_Empty_Other) {
// Empty placeholder codestream part
AppendU32BE(12, &c);
@@ -437,14 +440,14 @@ std::vector<uint8_t> CreateTestJXLCodestream(
compressed.swap(c);
} else {
std::vector<uint8_t> c;
- Bytes(header).AppendTo(&c);
+ Bytes(header).AppendTo(c);
if (params.jpeg_codestream != nullptr) {
jxl::AppendBoxHeader(jxl::MakeBoxType("jbrd"), jpeg_data.size(), false,
&c);
- Bytes(jpeg_data).AppendTo(&c);
+ Bytes(jpeg_data).AppendTo(c);
}
if (add_container == kCSBF_Brob_Exif) {
- Bytes(box_brob_exif, box_brob_exif_size).AppendTo(&c);
+ Bytes(box_brob_exif, box_brob_exif_size).AppendTo(c);
}
AppendU32BE(add_container == kCSBF_Single_Zero_Terminated
? 0
@@ -454,7 +457,7 @@ std::vector<uint8_t> CreateTestJXLCodestream(
c.push_back('x');
c.push_back('l');
c.push_back('c');
- Bytes(compressed).AppendTo(&c);
+ Bytes(compressed).AppendTo(c);
if (add_container == kCSBF_Single_Other) {
AppendTestBox(unk1_box_type, unk1_box_contents, unk1_box_size, false,
&c);
@@ -609,7 +612,7 @@ std::vector<uint8_t> DecodeWithAPI(Span<const uint8_t> compressed,
bool use_callback, bool set_buffer_early,
bool use_resizable_runner,
bool require_boxes, bool expect_success) {
- JxlDecoder* dec = JxlDecoderCreate(NULL);
+ JxlDecoder* dec = JxlDecoderCreate(nullptr);
std::vector<uint8_t> pixels =
DecodeWithAPI(dec, compressed, format, use_callback, set_buffer_early,
use_resizable_runner, require_boxes, expect_success);
@@ -622,6 +625,11 @@ std::vector<uint8_t> DecodeWithAPI(Span<const uint8_t> compressed,
////////////////////////////////////////////////////////////////////////////////
+using jxl::Image3F;
+using jxl::ImageF;
+using jxl::test::BoolToCStr;
+using jxl::test::ButteraugliDistance;
+
TEST(DecodeTest, JxlSignatureCheckTest) {
std::vector<std::pair<int, std::vector<uint8_t>>> tests = {
// No JPEGXL header starts with 'a'.
@@ -728,22 +736,22 @@ std::vector<uint8_t> GetTestHeader(size_t xsize, size_t ysize,
const std::vector<uint8_t> codestream_box_header = {0, 0, 0, 0xff,
'j', 'x', 'l', 'c'};
- for (size_t i = 0; i < signature_box.size(); i++) {
- writer.Write(8, signature_box[i]);
+ for (uint8_t c : signature_box) {
+ writer.Write(8, c);
}
- for (size_t i = 0; i < filetype_box.size(); i++) {
- writer.Write(8, filetype_box[i]);
+ for (uint8_t c : filetype_box) {
+ writer.Write(8, c);
}
if (insert_extra_box) {
- for (size_t i = 0; i < extra_box_header.size(); i++) {
- writer.Write(8, extra_box_header[i]);
+ for (uint8_t c : extra_box_header) {
+ writer.Write(8, c);
}
for (size_t i = 0; i < 255 - 8; i++) {
writer.Write(8, 0);
}
}
- for (size_t i = 0; i < codestream_box_header.size(); i++) {
- writer.Write(8, codestream_box_header[i]);
+ for (uint8_t c : codestream_box_header) {
+ writer.Write(8, c);
}
}
@@ -794,7 +802,7 @@ TEST(DecodeTest, BasicInfoTest) {
size_t bits_per_sample[2] = {8, 23};
size_t orientation[2] = {3, 5};
size_t alpha_bits[2] = {0, 8};
- JXL_BOOL have_container[2] = {0, 1};
+ bool have_container[2] = {false, true};
bool xyb_encoded = false;
std::vector<std::vector<uint8_t>> test_samples;
@@ -823,14 +831,15 @@ TEST(DecodeTest, BasicInfoTest) {
JxlDecoderStatus status = JxlDecoderProcessInput(dec);
JxlBasicInfo info;
- bool have_basic_info = !JxlDecoderGetBasicInfo(dec, &info);
+ JxlDecoderStatus bi_status = JxlDecoderGetBasicInfo(dec, &info);
+ bool have_basic_info = (bi_status == JXL_DEC_SUCCESS);
if (size == data.size()) {
EXPECT_EQ(JXL_DEC_BASIC_INFO, status);
// All header bytes given so the decoder must have the basic info.
EXPECT_EQ(true, have_basic_info);
- EXPECT_EQ(have_container[i], info.have_container);
+ EXPECT_EQ(have_container[i], FROM_JXL_BOOL(info.have_container));
EXPECT_EQ(alpha_bits[i], info.alpha_bits);
// Orientations 5..8 swap the dimensions
if (orientation[i] >= 5) {
@@ -1291,7 +1300,7 @@ class DecodeTestParam : public ::testing::TestWithParam<PixelTestConfig> {};
TEST_P(DecodeTestParam, PixelTest) {
PixelTestConfig config = GetParam();
- JxlDecoder* dec = JxlDecoderCreate(NULL);
+ JxlDecoder* dec = JxlDecoderCreate(nullptr);
if (config.keep_orientation) {
EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetKeepOrientation(dec, JXL_TRUE));
@@ -1352,7 +1361,7 @@ TEST_P(DecodeTestParam, PixelTest) {
color_encoding, 16, format_orig, nullptr,
&io.Main()));
- for (size_t i = 0; i < pixels.size(); i++) pixels[i] = 0;
+ for (uint8_t& pixel : pixels) pixel = 0;
EXPECT_TRUE(ConvertToExternal(
io.Main(), 16,
/*float_out=*/false, orig_channels, JXL_BIG_ENDIAN,
@@ -1420,7 +1429,7 @@ std::vector<PixelTestConfig> GeneratePixelTests() {
c.add_intrinsic_size = intrinsic_size;
c.xsize = xsize;
c.ysize = ysize;
- c.add_container = (CodeStreamBoxFormat)box;
+ c.add_container = box;
c.output_channels = ch.output_channels;
c.data_type = format.data_type;
c.endianness = format.endianness;
@@ -1435,7 +1444,7 @@ std::vector<PixelTestConfig> GeneratePixelTests() {
// Test output formats and methods.
for (ChannelInfo ch : ch_info) {
- for (int use_callback = 0; use_callback <= 1; use_callback++) {
+ for (bool use_callback : {false, true}) {
for (size_t upsampling : {1, 2, 4, 8}) {
for (OutputFormat fmt : out_formats) {
make_test(ch, 301, 33, jxl::kNoPreview,
@@ -1451,8 +1460,8 @@ std::vector<PixelTestConfig> GeneratePixelTests() {
// Test codestream formats.
for (size_t box = 1; box < kCSBF_NUM_ENTRIES; ++box) {
make_test(ch_info[0], 77, 33, jxl::kNoPreview,
- /*add_intrinsic_size=*/false, (CodeStreamBoxFormat)box,
- JXL_ORIENT_IDENTITY,
+ /*add_intrinsic_size=*/false,
+ static_cast<CodeStreamBoxFormat>(box), JXL_ORIENT_IDENTITY,
/*keep_orientation=*/false, out_formats[0],
/*use_callback=*/false,
/*set_buffer_early=*/false, /*resizable_runner=*/false, 1);
@@ -1460,7 +1469,7 @@ std::vector<PixelTestConfig> GeneratePixelTests() {
// Test previews.
for (int preview_mode = 0; preview_mode < jxl::kNumPreviewModes;
preview_mode++) {
- make_test(ch_info[0], 77, 33, (jxl::PreviewMode)preview_mode,
+ make_test(ch_info[0], 77, 33, static_cast<jxl::PreviewMode>(preview_mode),
/*add_intrinsic_size=*/false, CodeStreamBoxFormat::kCSBF_None,
JXL_ORIENT_IDENTITY,
/*keep_orientation=*/false, out_formats[0],
@@ -1468,8 +1477,7 @@ std::vector<PixelTestConfig> GeneratePixelTests() {
/*resizable_runner=*/false, 1);
}
// Test intrinsic sizes.
- for (int add_intrinsic_size = 0; add_intrinsic_size <= 1;
- add_intrinsic_size++) {
+ for (bool add_intrinsic_size : {false, true}) {
make_test(ch_info[0], 55, 34, jxl::kNoPreview, add_intrinsic_size,
CodeStreamBoxFormat::kCSBF_None, JXL_ORIENT_IDENTITY,
/*keep_orientation=*/false, out_formats[0],
@@ -1496,8 +1504,8 @@ std::vector<PixelTestConfig> GeneratePixelTests() {
// Test orientations.
for (int orientation = 2; orientation <= 8; ++orientation) {
- for (int keep_orientation = 0; keep_orientation <= 1; keep_orientation++) {
- for (int use_callback = 0; use_callback <= 1; use_callback++) {
+ for (bool keep_orientation : {false, true}) {
+ for (bool use_callback : {false, true}) {
for (ChannelInfo ch : ch_info) {
for (OutputFormat fmt : out_formats) {
make_test(ch, 280, 12, jxl::kNoPreview,
@@ -1549,7 +1557,7 @@ std::ostream& operator<<(std::ostream& os, const PixelTestConfig& c) {
}
if (c.add_container != CodeStreamBoxFormat::kCSBF_None) {
os << "Box";
- os << (size_t)c.add_container;
+ os << static_cast<size_t>(c.add_container);
}
if (c.preview_mode == jxl::kSmallPreview) os << "Preview";
if (c.preview_mode == jxl::kBigPreview) os << "BigPreview";
@@ -1575,9 +1583,10 @@ JXL_GTEST_INSTANTIATE_TEST_SUITE_P(DecodeTest, DecodeTestParam,
PixelTestDescription);
TEST(DecodeTest, PixelTestWithICCProfileLossless) {
- JxlDecoder* dec = JxlDecoderCreate(NULL);
+ JxlDecoder* dec = JxlDecoderCreate(nullptr);
- size_t xsize = 123, ysize = 77;
+ size_t xsize = 123;
+ size_t ysize = 77;
size_t num_pixels = xsize * ysize;
std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, 4, 0);
JxlPixelFormat format_orig = {4, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0};
@@ -1642,9 +1651,10 @@ TEST(DecodeTest, PixelTestWithICCProfileLossless) {
}
TEST(DecodeTest, PixelTestWithICCProfileLossy) {
- JxlDecoder* dec = JxlDecoderCreate(NULL);
+ JxlDecoder* dec = JxlDecoderCreate(nullptr);
- size_t xsize = 123, ysize = 77;
+ size_t xsize = 123;
+ size_t ysize = 77;
size_t num_pixels = xsize * ysize;
std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, 3, 0);
JxlPixelFormat format_orig = {3, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0};
@@ -1678,7 +1688,7 @@ TEST(DecodeTest, PixelTestWithICCProfileLossy) {
jxl::ColorEncoding color_encoding1;
jxl::IccBytes icc;
- jxl::Bytes(icc_data).AppendTo(&icc);
+ jxl::Bytes(icc_data).AppendTo(icc);
EXPECT_TRUE(color_encoding1.SetICC(std::move(icc), JxlGetDefaultCms()));
jxl::Span<const uint8_t> span1(pixels2.data(), pixels2.size());
jxl::CodecInOut io1;
@@ -1688,10 +1698,10 @@ TEST(DecodeTest, PixelTestWithICCProfileLossy) {
/*pool=*/nullptr, &io1.Main()));
jxl::ButteraugliParams ba;
- EXPECT_THAT(
+ EXPECT_SLIGHTLY_BELOW(
ButteraugliDistance(io0.frames, io1.frames, ba, *JxlGetDefaultCms(),
/*distmap=*/nullptr, nullptr),
- IsSlightlyBelow(0.56f));
+ 0.56f);
JxlDecoderDestroy(dec);
}
@@ -1753,7 +1763,8 @@ JXL_GTEST_INSTANTIATE_TEST_SUITE_P(
DecodeAllEncodingsTestInstantiation, DecodeAllEncodingsTest,
::testing::ValuesIn(jxl::test::AllEncodings()));
TEST_P(DecodeAllEncodingsTest, PreserveOriginalProfileTest) {
- size_t xsize = 123, ysize = 77;
+ size_t xsize = 123;
+ size_t ysize = 77;
std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, 3, 0);
JxlPixelFormat format = {3, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0};
int events = JXL_DEC_BASIC_INFO | JXL_DEC_COLOR_ENCODING | JXL_DEC_FULL_IMAGE;
@@ -1796,7 +1807,8 @@ namespace {
void SetPreferredColorProfileTest(
const jxl::test::ColorEncodingDescriptor& from, bool icc_dst,
bool use_cms) {
- size_t xsize = 123, ysize = 77;
+ size_t xsize = 123;
+ size_t ysize = 77;
int events = JXL_DEC_BASIC_INFO | JXL_DEC_COLOR_ENCODING | JXL_DEC_FULL_IMAGE;
jxl::ColorEncoding c_in = jxl::test::ColorEncodingFromDescriptor(from);
if (c_in.GetRenderingIntent() != jxl::RenderingIntent::kRelative) return;
@@ -1970,6 +1982,7 @@ void DecodeImageWithColorEncoding(const std::vector<uint8_t>& compressed,
EXPECT_EQ(JXL_DEC_BASIC_INFO, JxlDecoderProcessInput(dec));
EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBasicInfo(dec, &info));
EXPECT_EQ(JXL_DEC_COLOR_ENCODING, JxlDecoderProcessInput(dec));
+ // TODO(eustas): why unused?
std::string color_space_in = GetOrigProfile(dec);
if (with_cms) {
JxlDecoderSetCms(dec, *JxlGetDefaultCms());
@@ -2009,7 +2022,8 @@ JXL_GTEST_INSTANTIATE_TEST_SUITE_P(
TEST_P(DecodeAllEncodingsWithCMSTest, DecodeWithCMS) {
auto all_encodings = jxl::test::AllEncodings();
uint32_t num_channels = 3;
- size_t xsize = 177, ysize = 123;
+ size_t xsize = 177;
+ size_t ysize = 123;
std::vector<uint8_t> pixels =
jxl::test::GetSomeTestImage(xsize, ysize, num_channels, 0);
jxl::TestCodestreamParams params;
@@ -2045,9 +2059,10 @@ TEST_P(DecodeAllEncodingsWithCMSTest, DecodeWithCMS) {
// and to RGBA8
TEST(DecodeTest, PixelTestOpaqueSrgbLossy) {
for (unsigned channels = 3; channels <= 4; channels++) {
- JxlDecoder* dec = JxlDecoderCreate(NULL);
+ JxlDecoder* dec = JxlDecoderCreate(nullptr);
- size_t xsize = 123, ysize = 77;
+ size_t xsize = 123;
+ size_t ysize = 77;
size_t num_pixels = xsize * ysize;
std::vector<uint8_t> pixels =
jxl::test::GetSomeTestImage(xsize, ysize, 3, 0);
@@ -2082,10 +2097,10 @@ TEST(DecodeTest, PixelTestOpaqueSrgbLossy) {
/*pool=*/nullptr, &io1.Main()));
jxl::ButteraugliParams ba;
- EXPECT_THAT(
+ EXPECT_SLIGHTLY_BELOW(
ButteraugliDistance(io0.frames, io1.frames, ba, *JxlGetDefaultCms(),
/*distmap=*/nullptr, nullptr),
- IsSlightlyBelow(0.65f));
+ 0.65f);
JxlDecoderDestroy(dec);
}
@@ -2094,9 +2109,10 @@ TEST(DecodeTest, PixelTestOpaqueSrgbLossy) {
// Opaque image with noise enabled, decoded to RGB8 and RGBA8.
TEST(DecodeTest, PixelTestOpaqueSrgbLossyNoise) {
for (unsigned channels = 3; channels <= 4; channels++) {
- JxlDecoder* dec = JxlDecoderCreate(NULL);
+ JxlDecoder* dec = JxlDecoderCreate(nullptr);
- size_t xsize = 512, ysize = 300;
+ size_t xsize = 512;
+ size_t ysize = 300;
size_t num_pixels = xsize * ysize;
std::vector<uint8_t> pixels =
jxl::test::GetSomeTestImage(xsize, ysize, 3, 0);
@@ -2132,26 +2148,28 @@ TEST(DecodeTest, PixelTestOpaqueSrgbLossyNoise) {
/*pool=*/nullptr, &io1.Main()));
jxl::ButteraugliParams ba;
- EXPECT_THAT(
+ EXPECT_SLIGHTLY_BELOW(
ButteraugliDistance(io0.frames, io1.frames, ba, *JxlGetDefaultCms(),
/*distmap=*/nullptr, nullptr),
- IsSlightlyBelow(1.3f));
+ 1.3f);
JxlDecoderDestroy(dec);
}
}
TEST(DecodeTest, ProcessEmptyInputWithBoxes) {
- size_t xsize = 123, ysize = 77;
+ size_t xsize = 123;
+ size_t ysize = 77;
std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, 3, 0);
jxl::CompressParams cparams;
uint32_t channels = 3;
JxlPixelFormat format = {channels, JXL_TYPE_FLOAT, JXL_LITTLE_ENDIAN, 0};
for (int i = 0; i < kCSBF_NUM_ENTRIES; ++i) {
- JxlDecoder* dec = JxlDecoderCreate(NULL);
+ JxlDecoder* dec = JxlDecoderCreate(nullptr);
jxl::TestCodestreamParams params;
- params.box_format = (CodeStreamBoxFormat)i;
- printf("Testing empty input with box format %d\n", (int)params.box_format);
+ params.box_format = static_cast<CodeStreamBoxFormat>(i);
+ printf("Testing empty input with box format %d\n",
+ static_cast<int>(params.box_format));
std::vector<uint8_t> compressed = jxl::CreateTestJXLCodestream(
jxl::Bytes(pixels.data(), pixels.size()), xsize, ysize, 3, params);
const int events =
@@ -2175,14 +2193,15 @@ TEST(DecodeTest, ProcessEmptyInputWithBoxes) {
}
TEST(DecodeTest, ExtraBytesAfterCompressedStream) {
- size_t xsize = 123, ysize = 77;
+ size_t xsize = 123;
+ size_t ysize = 77;
size_t num_pixels = xsize * ysize;
std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, 3, 0);
jxl::CompressParams cparams;
for (int i = 0; i < kCSBF_NUM_ENTRIES; ++i) {
- CodeStreamBoxFormat box_format = (CodeStreamBoxFormat)i;
+ CodeStreamBoxFormat box_format = static_cast<CodeStreamBoxFormat>(i);
if (box_format == kCSBF_Multi_Other_Zero_Terminated) continue;
- printf("Testing with box format %d\n", (int)box_format);
+ printf("Testing with box format %d\n", static_cast<int>(box_format));
size_t last_unknown_box_size = 0;
if (box_format == kCSBF_Single_Other) {
last_unknown_box_size = unk1_box_size + 8;
@@ -2201,7 +2220,7 @@ TEST(DecodeTest, ExtraBytesAfterCompressedStream) {
compressed.push_back(0);
compressed.push_back(1);
compressed.push_back(2);
- JxlDecoder* dec = JxlDecoderCreate(NULL);
+ JxlDecoder* dec = JxlDecoderCreate(nullptr);
uint32_t channels = 3;
JxlPixelFormat format = {channels, JXL_TYPE_FLOAT, JXL_LITTLE_ENDIAN, 0};
std::vector<uint8_t> pixels2 = jxl::DecodeWithAPI(
@@ -2217,14 +2236,15 @@ TEST(DecodeTest, ExtraBytesAfterCompressedStream) {
}
TEST(DecodeTest, ExtraBytesAfterCompressedStreamRequireBoxes) {
- size_t xsize = 123, ysize = 77;
+ size_t xsize = 123;
+ size_t ysize = 77;
size_t num_pixels = xsize * ysize;
std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, 3, 0);
jxl::CompressParams cparams;
for (int i = 0; i < kCSBF_NUM_ENTRIES; ++i) {
- CodeStreamBoxFormat box_format = (CodeStreamBoxFormat)i;
+ CodeStreamBoxFormat box_format = static_cast<CodeStreamBoxFormat>(i);
if (box_format == kCSBF_Multi_Other_Zero_Terminated) continue;
- printf("Testing with box format %d\n", (int)box_format);
+ printf("Testing with box format %d\n", static_cast<int>(box_format));
bool expect_success = (box_format == kCSBF_None ||
box_format == kCSBF_Single_Zero_Terminated ||
box_format == kCSBF_Multi_Zero_Terminated);
@@ -2236,7 +2256,7 @@ TEST(DecodeTest, ExtraBytesAfterCompressedStreamRequireBoxes) {
compressed.push_back(0);
compressed.push_back(1);
compressed.push_back(2);
- JxlDecoder* dec = JxlDecoderCreate(NULL);
+ JxlDecoder* dec = JxlDecoderCreate(nullptr);
uint32_t channels = 3;
JxlPixelFormat format = {channels, JXL_TYPE_FLOAT, JXL_LITTLE_ENDIAN, 0};
std::vector<uint8_t> pixels2 = jxl::DecodeWithAPI(
@@ -2251,35 +2271,38 @@ TEST(DecodeTest, ExtraBytesAfterCompressedStreamRequireBoxes) {
}
TEST(DecodeTest, ConcatenatedCompressedStreams) {
- size_t xsize = 123, ysize = 77;
+ size_t xsize = 123;
+ size_t ysize = 77;
size_t num_pixels = xsize * ysize;
std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, 3, 0);
jxl::CompressParams cparams;
for (int i = 0; i < kCSBF_NUM_ENTRIES; ++i) {
- CodeStreamBoxFormat first_box_format = (CodeStreamBoxFormat)i;
+ CodeStreamBoxFormat first_box_format = static_cast<CodeStreamBoxFormat>(i);
if (first_box_format == kCSBF_Multi_Other_Zero_Terminated) continue;
jxl::TestCodestreamParams params1;
params1.box_format = first_box_format;
std::vector<uint8_t> compressed1 = jxl::CreateTestJXLCodestream(
jxl::Bytes(pixels.data(), pixels.size()), xsize, ysize, 3, params1);
for (int j = 0; j < kCSBF_NUM_ENTRIES; ++j) {
- CodeStreamBoxFormat second_box_format = (CodeStreamBoxFormat)j;
+ CodeStreamBoxFormat second_box_format =
+ static_cast<CodeStreamBoxFormat>(j);
if (second_box_format == kCSBF_Multi_Other_Zero_Terminated) continue;
- printf("Testing with box format pair %d, %d\n", (int)first_box_format,
- (int)second_box_format);
+ printf("Testing with box format pair %d, %d\n",
+ static_cast<int>(first_box_format),
+ static_cast<int>(second_box_format));
jxl::TestCodestreamParams params2;
params2.box_format = second_box_format;
std::vector<uint8_t> compressed2 = jxl::CreateTestJXLCodestream(
jxl::Bytes(pixels.data(), pixels.size()), xsize, ysize, 3, params2);
std::vector<uint8_t> concat;
- jxl::Bytes(compressed1).AppendTo(&concat);
- jxl::Bytes(compressed2).AppendTo(&concat);
+ jxl::Bytes(compressed1).AppendTo(concat);
+ jxl::Bytes(compressed2).AppendTo(concat);
uint32_t channels = 3;
JxlPixelFormat format = {channels, JXL_TYPE_FLOAT, JXL_LITTLE_ENDIAN, 0};
size_t remaining = concat.size();
for (int part = 0; part < 2; ++part) {
printf(" Decoding part %d\n", part + 1);
- JxlDecoder* dec = JxlDecoderCreate(NULL);
+ JxlDecoder* dec = JxlDecoderCreate(nullptr);
size_t pos = concat.size() - remaining;
bool expect_success =
(part == 0 || second_box_format == kCSBF_None ||
@@ -2300,7 +2323,8 @@ TEST(DecodeTest, ConcatenatedCompressedStreams) {
}
void TestPartialStream(bool reconstructible_jpeg) {
- size_t xsize = 123, ysize = 77;
+ size_t xsize = 123;
+ size_t ysize = 77;
uint32_t channels = 4;
if (reconstructible_jpeg) {
channels = 3;
@@ -2325,7 +2349,7 @@ void TestPartialStream(bool reconstructible_jpeg) {
std::vector<std::vector<uint8_t>> codestreams(kCSBF_NUM_ENTRIES);
std::vector<std::vector<uint8_t>> jpeg_codestreams(kCSBF_NUM_ENTRIES);
for (size_t i = 0; i < kCSBF_NUM_ENTRIES; ++i) {
- params.box_format = (CodeStreamBoxFormat)i;
+ params.box_format = static_cast<CodeStreamBoxFormat>(i);
if (reconstructible_jpeg) {
params.jpeg_codestream = &jpeg_codestreams[i];
}
@@ -2338,10 +2362,10 @@ void TestPartialStream(bool reconstructible_jpeg) {
// box parsing.
std::vector<size_t> increments = {1, 3, 17, 23, 120, 700, 1050};
- for (size_t index = 0; index < increments.size(); index++) {
+ for (size_t increment : increments) {
for (size_t i = 0; i < kCSBF_NUM_ENTRIES; ++i) {
- if (reconstructible_jpeg &&
- (CodeStreamBoxFormat)i == CodeStreamBoxFormat::kCSBF_None) {
+ if (reconstructible_jpeg && static_cast<CodeStreamBoxFormat>(i) ==
+ CodeStreamBoxFormat::kCSBF_None) {
continue;
}
const std::vector<uint8_t>& data = codestreams[i];
@@ -2376,7 +2400,6 @@ void TestPartialStream(bool reconstructible_jpeg) {
break;
}
- size_t increment = increments[index];
// End of the file reached, should be the final test.
if (total_size + increment > data.size()) {
increment = data.size() - total_size;
@@ -2467,7 +2490,7 @@ TEST(DecodeTest, DCNotGettableTest) {
"\363\6\22\bp\0\200\237\34\231W2d\255$\1",
68);
- JxlDecoder* dec = JxlDecoderCreate(NULL);
+ JxlDecoder* dec = JxlDecoderCreate(nullptr);
EXPECT_EQ(JXL_DEC_SUCCESS,
JxlDecoderSubscribeEvents(dec, JXL_DEC_BASIC_INFO));
@@ -2487,7 +2510,8 @@ TEST(DecodeTest, DCNotGettableTest) {
}
TEST(DecodeTest, PreviewTest) {
- size_t xsize = 77, ysize = 120;
+ size_t xsize = 77;
+ size_t ysize = 120;
std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, 3, 0);
JxlPixelFormat format_orig = {3, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0};
for (jxl::PreviewMode mode : {jxl::kSmallPreview, jxl::kBigPreview}) {
@@ -2499,7 +2523,7 @@ TEST(DecodeTest, PreviewTest) {
JxlPixelFormat format = {3, JXL_TYPE_UINT8, JXL_LITTLE_ENDIAN, 0};
- JxlDecoder* dec = JxlDecoderCreate(NULL);
+ JxlDecoder* dec = JxlDecoderCreate(nullptr);
const uint8_t* next_in = compressed.data();
size_t avail_in = compressed.size();
@@ -2561,7 +2585,8 @@ TEST(DecodeTest, PreviewTest) {
}
TEST(DecodeTest, AlignTest) {
- size_t xsize = 123, ysize = 77;
+ size_t xsize = 123;
+ size_t ysize = 77;
std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, 4, 0);
JxlPixelFormat format_orig = {4, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0};
@@ -2575,22 +2600,27 @@ TEST(DecodeTest, AlignTest) {
size_t align = 17;
JxlPixelFormat format = {3, JXL_TYPE_UINT8, JXL_LITTLE_ENDIAN, align};
// On purpose not using jxl::RoundUpTo to test it independently.
- size_t expected_line_bytes = (1 * 3 * xsize + align - 1) / align * align;
+ size_t expected_line_size_last = 1 * 3 * xsize;
+ size_t expected_line_size =
+ ((expected_line_size_last + align - 1) / align) * align;
+ size_t expected_pixels_size =
+ expected_line_size * (ysize - 1) + expected_line_size_last;
- for (int use_callback = 0; use_callback <= 1; ++use_callback) {
+ for (bool use_callback : {false, true}) {
std::vector<uint8_t> pixels2 = jxl::DecodeWithAPI(
jxl::Bytes(compressed.data(), compressed.size()), format, use_callback,
/*set_buffer_early=*/false,
/*use_resizable_runner=*/false, /*require_boxes=*/false,
/*expect_success=*/true);
- EXPECT_EQ(expected_line_bytes * ysize, pixels2.size());
+ EXPECT_EQ(expected_pixels_size, pixels2.size());
EXPECT_EQ(0u, jxl::test::ComparePixels(pixels.data(), pixels2.data(), xsize,
ysize, format_orig, format));
}
}
TEST(DecodeTest, AnimationTest) {
- size_t xsize = 123, ysize = 77;
+ size_t xsize = 123;
+ size_t ysize = 77;
static const size_t num_frames = 2;
std::vector<uint8_t> frames[2];
frames[0] = jxl::test::GetSomeTestImage(xsize, ysize, 3, 0);
@@ -2631,12 +2661,12 @@ TEST(DecodeTest, AnimationTest) {
// Decode and test the animation frames
- JxlDecoder* dec = JxlDecoderCreate(NULL);
+ JxlDecoder* dec = JxlDecoderCreate(nullptr);
const uint8_t* next_in = compressed.data();
size_t avail_in = compressed.size();
void* runner = JxlThreadParallelRunnerCreate(
- NULL, JxlThreadParallelRunnerDefaultNumWorkerThreads());
+ nullptr, JxlThreadParallelRunnerDefaultNumWorkerThreads());
EXPECT_EQ(JXL_DEC_SUCCESS,
JxlDecoderSetParallelRunner(dec, JxlThreadParallelRunner, runner));
@@ -2690,7 +2720,8 @@ TEST(DecodeTest, AnimationTest) {
}
TEST(DecodeTest, AnimationTestStreaming) {
- size_t xsize = 123, ysize = 77;
+ size_t xsize = 123;
+ size_t ysize = 77;
static const size_t num_frames = 2;
std::vector<uint8_t> frames[2];
frames[0] = jxl::test::GetSomeTestImage(xsize, ysize, 3, 0);
@@ -2733,7 +2764,7 @@ TEST(DecodeTest, AnimationTestStreaming) {
const size_t step_size = 16;
- JxlDecoder* dec = JxlDecoderCreate(NULL);
+ JxlDecoder* dec = JxlDecoderCreate(nullptr);
const uint8_t* next_in = compressed.data();
size_t avail_in = 0;
size_t frame_headers_seen = 0;
@@ -2741,7 +2772,7 @@ TEST(DecodeTest, AnimationTestStreaming) {
bool seen_basic_info = false;
void* runner = JxlThreadParallelRunnerCreate(
- NULL, JxlThreadParallelRunnerDefaultNumWorkerThreads());
+ nullptr, JxlThreadParallelRunnerDefaultNumWorkerThreads());
EXPECT_EQ(JXL_DEC_SUCCESS,
JxlDecoderSetParallelRunner(dec, JxlThreadParallelRunner, runner));
@@ -2805,7 +2836,7 @@ TEST(DecodeTest, AnimationTestStreaming) {
frames_seen++;
EXPECT_EQ(frame_headers_seen, frames_seen);
} else {
- fprintf(stderr, "Unexpected status: %d\n", (int)status);
+ fprintf(stderr, "Unexpected status: %d\n", static_cast<int>(status));
FAIL();
}
}
@@ -2822,7 +2853,8 @@ TEST(DecodeTest, AnimationTestStreaming) {
}
TEST(DecodeTest, ExtraChannelTest) {
- size_t xsize = 55, ysize = 257;
+ size_t xsize = 55;
+ size_t ysize = 257;
std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, 4, 0);
JxlPixelFormat format_orig = {4, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0};
@@ -2836,7 +2868,7 @@ TEST(DecodeTest, ExtraChannelTest) {
size_t align = 17;
JxlPixelFormat format = {3, JXL_TYPE_UINT8, JXL_LITTLE_ENDIAN, align};
- JxlDecoder* dec = JxlDecoderCreate(NULL);
+ JxlDecoder* dec = JxlDecoderCreate(nullptr);
EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSubscribeEvents(
dec, JXL_DEC_BASIC_INFO | JXL_DEC_FULL_IMAGE));
@@ -2899,7 +2931,8 @@ TEST(DecodeTest, ExtraChannelTest) {
}
TEST(DecodeTest, SkipCurrentFrameTest) {
- size_t xsize = 90, ysize = 120;
+ size_t xsize = 90;
+ size_t ysize = 120;
constexpr size_t num_frames = 7;
std::vector<uint8_t> frames[num_frames];
for (size_t i = 0; i < num_frames; i++) {
@@ -2945,7 +2978,7 @@ TEST(DecodeTest, SkipCurrentFrameTest) {
cparams.custom_progressive_mode = &progressive_mode;
EXPECT_TRUE(jxl::test::EncodeFile(cparams, &io, &compressed));
- JxlDecoder* dec = JxlDecoderCreate(NULL);
+ JxlDecoder* dec = JxlDecoderCreate(nullptr);
const uint8_t* next_in = compressed.data();
size_t avail_in = compressed.size();
@@ -2964,7 +2997,7 @@ TEST(DecodeTest, SkipCurrentFrameTest) {
EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBasicInfo(dec, &info));
for (size_t i = 0; i < num_frames; ++i) {
- printf("Decoding frame %d\n", (int)i);
+ printf("Decoding frame %d\n", static_cast<int>(i));
EXPECT_EQ(JXL_DEC_ERROR, JxlDecoderSkipCurrentFrame(dec));
std::vector<uint8_t> pixels(buffer_size);
EXPECT_EQ(JXL_DEC_FRAME, JxlDecoderProcessInput(dec));
@@ -3010,7 +3043,8 @@ TEST(DecodeTest, SkipCurrentFrameTest) {
}
TEST(DecodeTest, SkipFrameTest) {
- size_t xsize = 90, ysize = 120;
+ size_t xsize = 90;
+ size_t ysize = 120;
constexpr size_t num_frames = 16;
std::vector<uint8_t> frames[num_frames];
for (size_t i = 0; i < num_frames; i++) {
@@ -3056,12 +3090,12 @@ TEST(DecodeTest, SkipFrameTest) {
// Decode and test the animation frames
- JxlDecoder* dec = JxlDecoderCreate(NULL);
+ JxlDecoder* dec = JxlDecoderCreate(nullptr);
const uint8_t* next_in = compressed.data();
size_t avail_in = compressed.size();
void* runner = JxlThreadParallelRunnerCreate(
- NULL, JxlThreadParallelRunnerDefaultNumWorkerThreads());
+ nullptr, JxlThreadParallelRunnerDefaultNumWorkerThreads());
EXPECT_EQ(JXL_DEC_SUCCESS,
JxlDecoderSetParallelRunner(dec, JxlThreadParallelRunner, runner));
@@ -3146,7 +3180,8 @@ TEST(DecodeTest, SkipFrameTest) {
}
TEST(DecodeTest, SkipFrameWithBlendingTest) {
- size_t xsize = 90, ysize = 120;
+ size_t xsize = 90;
+ size_t ysize = 120;
constexpr size_t num_frames = 16;
std::vector<uint8_t> frames[num_frames];
JxlPixelFormat format = {3, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0};
@@ -3213,23 +3248,22 @@ TEST(DecodeTest, SkipFrameWithBlendingTest) {
// Independently decode all frames without any skipping, to create the
// expected blended frames, for the actual tests below to compare with.
{
- JxlDecoder* dec = JxlDecoderCreate(NULL);
+ JxlDecoder* dec = JxlDecoderCreate(nullptr);
const uint8_t* next_in = compressed.data();
size_t avail_in = compressed.size();
void* runner = JxlThreadParallelRunnerCreate(
- NULL, JxlThreadParallelRunnerDefaultNumWorkerThreads());
+ nullptr, JxlThreadParallelRunnerDefaultNumWorkerThreads());
EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetParallelRunner(
dec, JxlThreadParallelRunner, runner));
EXPECT_EQ(JXL_DEC_SUCCESS,
JxlDecoderSubscribeEvents(dec, JXL_DEC_FULL_IMAGE));
EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, next_in, avail_in));
- for (size_t i = 0; i < num_frames; ++i) {
+ for (auto& frame : frames) {
EXPECT_EQ(JXL_DEC_NEED_IMAGE_OUT_BUFFER, JxlDecoderProcessInput(dec));
- frames[i].resize(xsize * ysize * 6);
- EXPECT_EQ(JXL_DEC_SUCCESS,
- JxlDecoderSetImageOutBuffer(dec, &format, frames[i].data(),
- frames[i].size()));
+ frame.resize(xsize * ysize * 6);
+ EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetImageOutBuffer(
+ dec, &format, frame.data(), frame.size()));
EXPECT_EQ(JXL_DEC_FULL_IMAGE, JxlDecoderProcessInput(dec));
}
@@ -3240,12 +3274,12 @@ TEST(DecodeTest, SkipFrameWithBlendingTest) {
JxlDecoderDestroy(dec);
}
- JxlDecoder* dec = JxlDecoderCreate(NULL);
+ JxlDecoder* dec = JxlDecoderCreate(nullptr);
const uint8_t* next_in = compressed.data();
size_t avail_in = compressed.size();
void* runner = JxlThreadParallelRunnerCreate(
- NULL, JxlThreadParallelRunnerDefaultNumWorkerThreads());
+ nullptr, JxlThreadParallelRunnerDefaultNumWorkerThreads());
EXPECT_EQ(JXL_DEC_SUCCESS,
JxlDecoderSetParallelRunner(dec, JxlThreadParallelRunner, runner));
@@ -3360,7 +3394,8 @@ TEST(DecodeTest, SkipFrameWithBlendingTest) {
}
TEST(DecodeTest, SkipFrameWithAlphaBlendingTest) {
- size_t xsize = 90, ysize = 120;
+ size_t xsize = 90;
+ size_t ysize = 120;
constexpr size_t num_frames = 16;
std::vector<uint8_t> frames[num_frames + 5];
JxlPixelFormat format = {4, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0};
@@ -3376,7 +3411,10 @@ TEST(DecodeTest, SkipFrameWithAlphaBlendingTest) {
std::vector<uint32_t> frame_durations_c;
std::vector<uint32_t> frame_durations_nc;
- std::vector<uint32_t> frame_xsize, frame_ysize, frame_x0, frame_y0;
+ std::vector<uint32_t> frame_xsize;
+ std::vector<uint32_t> frame_ysize;
+ std::vector<uint32_t> frame_x0;
+ std::vector<uint32_t> frame_y0;
for (size_t i = 0; i < num_frames; ++i) {
size_t cropxsize = 1 + xsize * 2 / (i + 1);
@@ -3444,12 +3482,12 @@ TEST(DecodeTest, SkipFrameWithAlphaBlendingTest) {
// Independently decode all frames without any skipping, to create the
// expected blended frames, for the actual tests below to compare with.
{
- JxlDecoder* dec = JxlDecoderCreate(NULL);
+ JxlDecoder* dec = JxlDecoderCreate(nullptr);
const uint8_t* next_in = compressed.data();
size_t avail_in = compressed.size();
EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetCoalescing(dec, coalescing));
void* runner = JxlThreadParallelRunnerCreate(
- NULL, JxlThreadParallelRunnerDefaultNumWorkerThreads());
+ nullptr, JxlThreadParallelRunnerDefaultNumWorkerThreads());
EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetParallelRunner(
dec, JxlThreadParallelRunner, runner));
EXPECT_EQ(JXL_DEC_SUCCESS,
@@ -3479,13 +3517,13 @@ TEST(DecodeTest, SkipFrameWithAlphaBlendingTest) {
JxlDecoderDestroy(dec);
}
- JxlDecoder* dec = JxlDecoderCreate(NULL);
+ JxlDecoder* dec = JxlDecoderCreate(nullptr);
const uint8_t* next_in = compressed.data();
size_t avail_in = compressed.size();
EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetCoalescing(dec, coalescing));
void* runner = JxlThreadParallelRunnerCreate(
- NULL, JxlThreadParallelRunnerDefaultNumWorkerThreads());
+ nullptr, JxlThreadParallelRunnerDefaultNumWorkerThreads());
EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetParallelRunner(
dec, JxlThreadParallelRunner, runner));
@@ -3648,7 +3686,8 @@ TEST(DecodeTest, SkipFrameWithAlphaBlendingTest) {
TEST(DecodeTest, OrientedCroppedFrameTest) {
const auto test = [](bool keep_orientation, uint32_t orientation,
uint32_t resampling) {
- size_t xsize = 90, ysize = 120;
+ size_t xsize = 90;
+ size_t ysize = 120;
JxlPixelFormat format = {4, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0};
size_t oxsize = (!keep_orientation && orientation > 4 ? ysize : xsize);
size_t oysize = (!keep_orientation && orientation > 4 ? xsize : ysize);
@@ -3698,14 +3737,14 @@ TEST(DecodeTest, OrientedCroppedFrameTest) {
// Independently decode all frames without any skipping, to create the
// expected blended frames, for the actual tests below to compare with.
{
- JxlDecoder* dec = JxlDecoderCreate(NULL);
+ JxlDecoder* dec = JxlDecoderCreate(nullptr);
const uint8_t* next_in = compressed.data();
size_t avail_in = compressed.size();
EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetCoalescing(dec, coalescing));
EXPECT_EQ(JXL_DEC_SUCCESS,
JxlDecoderSetKeepOrientation(dec, keep_orientation));
void* runner = JxlThreadParallelRunnerCreate(
- NULL, JxlThreadParallelRunnerDefaultNumWorkerThreads());
+ nullptr, JxlThreadParallelRunnerDefaultNumWorkerThreads());
EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetParallelRunner(
dec, JxlThreadParallelRunner, runner));
EXPECT_EQ(JXL_DEC_SUCCESS,
@@ -3747,10 +3786,10 @@ TEST(DecodeTest, OrientedCroppedFrameTest) {
for (int y = 0; y < static_cast<int>(oysize); y++) {
if (y < y0 || y >= y0 + h) continue;
// pointers do whole 16-bit RGBA pixels at a time
- uint64_t* row_merged = static_cast<uint64_t*>(
- (void*)(frames[4].data() + y * oxsize * 8));
- uint64_t* row_layer = static_cast<uint64_t*>(
- (void*)(frames[i].data() + (y - y0) * w * 8));
+ uint64_t* row_merged = reinterpret_cast<uint64_t*>(
+ frames[4].data() + y * oxsize * 8);
+ uint64_t* row_layer = reinterpret_cast<uint64_t*>(
+ frames[i].data() + (y - y0) * w * 8);
for (int x = 0; x < static_cast<int>(oxsize); x++) {
if (x < x0 || x >= x0 + w) continue;
row_merged[x] = row_layer[x - x0];
@@ -3825,18 +3864,18 @@ void AnalyzeCodestream(const std::vector<uint8_t>& data,
codestream.insert(codestream.end(), in + pos + 8, in + pos + box_size);
codestream_end = true;
} else if (memcmp(in + pos + 4, "jxlp", 4) == 0) {
- codestream_end = (LoadBE32(in + pos + 8) & 0x80000000);
+ codestream_end = ((LoadBE32(in + pos + 8) & 0x80000000) != 0);
if (codestream.empty()) {
streampos->codestream_start = pos + 12;
} else if (box_size > 12 || !codestream_end) {
- breakpoints.push_back({codestream.size(), 12});
+ breakpoints.emplace_back(codestream.size(), 12);
}
codestream.insert(codestream.end(), in + pos + 12, in + pos + box_size);
} else if (memcmp(in + pos + 4, "jbrd", 4) == 0) {
EXPECT_TRUE(codestream.empty());
streampos->jbrd_end = pos + box_size;
} else if (!codestream.empty() && !codestream_end) {
- breakpoints.push_back({codestream.size(), box_size});
+ breakpoints.emplace_back(codestream.size(), box_size);
}
pos += box_size;
}
@@ -3931,7 +3970,7 @@ void VerifyProgression(size_t xsize, size_t ysize, uint32_t num_channels,
double prev_dist = 1.0;
for (;;) {
JxlDecoderStatus status = JxlDecoderProcessInput(dec);
- printf("bp: %d status: 0x%x\n", bp, (int)status);
+ printf("bp: %d status: 0x%x\n", bp, static_cast<int>(status));
if (status == JXL_DEC_BASIC_INFO) {
JxlBasicInfo info;
EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBasicInfo(dec, &info));
@@ -3977,7 +4016,7 @@ void VerifyProgression(size_t xsize, size_t ysize, uint32_t num_channels,
avail_in = breakpoints[bp].file_pos - (next_in - data.data());
EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, next_in, avail_in));
} else {
- printf("Unexpected status: 0x%x\n", (int)status);
+ printf("Unexpected status: 0x%x\n", static_cast<int>(status));
FAIL(); // unexpected returned status
}
}
@@ -3985,7 +4024,8 @@ void VerifyProgression(size_t xsize, size_t ysize, uint32_t num_channels,
}
TEST(DecodeTest, ProgressionTest) {
- size_t xsize = 508, ysize = 470;
+ size_t xsize = 508;
+ size_t ysize = 470;
uint32_t num_channels = 3;
std::vector<uint8_t> pixels =
jxl::test::GetSomeTestImage(xsize, ysize, num_channels, 0);
@@ -4020,7 +4060,8 @@ TEST(DecodeTest, ProgressionTest) {
}
TEST(DecodeTest, ProgressionTestLosslessAlpha) {
- size_t xsize = 508, ysize = 470;
+ size_t xsize = 508;
+ size_t ysize = 470;
uint32_t num_channels = 4;
std::vector<uint8_t> pixels =
jxl::test::GetSomeTestImage(xsize, ysize, num_channels, 0);
@@ -4063,7 +4104,8 @@ void VerifyFilePosition(size_t expected_pos, const std::vector<uint8_t>& data,
}
TEST(DecodeTest, InputHandlingTestOneShot) {
- size_t xsize = 508, ysize = 470;
+ size_t xsize = 508;
+ size_t ysize = 470;
uint32_t num_channels = 3;
std::vector<uint8_t> pixels =
jxl::test::GetSomeTestImage(xsize, ysize, num_channels, 0);
@@ -4072,7 +4114,7 @@ TEST(DecodeTest, InputHandlingTestOneShot) {
jxl::TestCodestreamParams params;
params.cparams.progressive_dc = 1;
params.preview_mode = jxl::kSmallPreview;
- params.box_format = (CodeStreamBoxFormat)i;
+ params.box_format = static_cast<CodeStreamBoxFormat>(i);
std::vector<uint8_t> data =
jxl::CreateTestJXLCodestream(jxl::Bytes(pixels.data(), pixels.size()),
xsize, ysize, num_channels, params);
@@ -4167,7 +4209,7 @@ TEST(DecodeTest, JXL_TRANSCODE_JPEG_TEST(InputHandlingTestJPEGOneshot)) {
params.cparams.color_transform = jxl::ColorTransform::kNone;
params.jpeg_codestream = &jpeg_codestream;
params.preview_mode = jxl::kSmallPreview;
- params.box_format = (CodeStreamBoxFormat)i;
+ params.box_format = static_cast<CodeStreamBoxFormat>(i);
std::vector<uint8_t> data =
jxl::CreateTestJXLCodestream(jxl::Bytes(pixels.data(), pixels.size()),
xsize, ysize, channels, params);
@@ -4246,7 +4288,8 @@ TEST(DecodeTest, JXL_TRANSCODE_JPEG_TEST(InputHandlingTestJPEGOneshot)) {
}
TEST(DecodeTest, InputHandlingTestStreaming) {
- size_t xsize = 508, ysize = 470;
+ size_t xsize = 508;
+ size_t ysize = 470;
uint32_t num_channels = 3;
std::vector<uint8_t> pixels =
jxl::test::GetSomeTestImage(xsize, ysize, num_channels, 0);
@@ -4255,7 +4298,7 @@ TEST(DecodeTest, InputHandlingTestStreaming) {
fflush(stdout);
jxl::TestCodestreamParams params;
params.cparams.progressive_dc = 1;
- params.box_format = (CodeStreamBoxFormat)i;
+ params.box_format = static_cast<CodeStreamBoxFormat>(i);
params.preview_mode = jxl::kSmallPreview;
std::vector<uint8_t> data =
jxl::CreateTestJXLCodestream(jxl::Bytes(pixels.data(), pixels.size()),
@@ -4334,7 +4377,7 @@ TEST(DecodeTest, InputHandlingTestStreaming) {
ASSERT_LT(box_index, streampos.box_start.size());
EXPECT_EQ(file_pos, streampos.box_start[box_index++]);
} else {
- printf("Unexpected status: 0x%x\n", (int)status);
+ printf("Unexpected status: 0x%x\n", static_cast<int>(status));
FAIL();
}
}
@@ -4346,7 +4389,8 @@ TEST(DecodeTest, InputHandlingTestStreaming) {
TEST(DecodeTest, FlushTest) {
// Size large enough for multiple groups, required to have progressive
// stages
- size_t xsize = 333, ysize = 300;
+ size_t xsize = 333;
+ size_t ysize = 300;
uint32_t num_channels = 3;
std::vector<uint8_t> pixels =
jxl::test::GetSomeTestImage(xsize, ysize, num_channels, 0);
@@ -4421,7 +4465,8 @@ TEST(DecodeTest, FlushTest) {
TEST(DecodeTest, FlushTestImageOutCallback) {
// Size large enough for multiple groups, required to have progressive
// stages
- size_t xsize = 333, ysize = 300;
+ size_t xsize = 333;
+ size_t ysize = 300;
uint32_t num_channels = 3;
std::vector<uint8_t> pixels =
jxl::test::GetSomeTestImage(xsize, ysize, num_channels, 0);
@@ -4507,7 +4552,8 @@ TEST(DecodeTest, FlushTestImageOutCallback) {
TEST(DecodeTest, FlushTestLossyProgressiveAlpha) {
// Size large enough for multiple groups, required to have progressive
// stages
- size_t xsize = 333, ysize = 300;
+ size_t xsize = 333;
+ size_t ysize = 300;
uint32_t num_channels = 4;
std::vector<uint8_t> pixels =
jxl::test::GetSomeTestImage(xsize, ysize, num_channels, 0);
@@ -4577,7 +4623,8 @@ TEST(DecodeTest, FlushTestLossyProgressiveAlpha) {
JxlDecoderDestroy(dec);
}
TEST(DecodeTest, FlushTestLossyProgressiveAlphaUpsampling) {
- size_t xsize = 533, ysize = 401;
+ size_t xsize = 533;
+ size_t ysize = 401;
uint32_t num_channels = 4;
std::vector<uint8_t> pixels =
jxl::test::GetSomeTestImage(xsize, ysize, num_channels, 0);
@@ -4651,7 +4698,8 @@ TEST(DecodeTest, FlushTestLossyProgressiveAlphaUpsampling) {
TEST(DecodeTest, FlushTestLosslessProgressiveAlpha) {
// Size large enough for multiple groups, required to have progressive
// stages
- size_t xsize = 333, ysize = 300;
+ size_t xsize = 333;
+ size_t ysize = 300;
uint32_t num_channels = 4;
std::vector<uint8_t> pixels =
jxl::test::GetSomeTestImage(xsize, ysize, num_channels, 0);
@@ -4731,17 +4779,19 @@ JXL_GTEST_INSTANTIATE_TEST_SUITE_P(DecodeProgressiveTestInstantiation,
::testing::Range(0, 8));
TEST_P(DecodeProgressiveTest, ProgressiveEventTest) {
const int params = GetParam();
- int single_group = params & 1;
- int lossless = (params >> 1) & 1;
+ bool single_group = ((params & 1) != 0);
+ bool lossless = (((params >> 1) & 1) != 0);
uint32_t num_channels = 3 + ((params >> 2) & 1);
+ bool has_alpha = ((num_channels & 1) == 0);
std::set<JxlProgressiveDetail> progressive_details = {kDC, kLastPasses,
kPasses};
for (auto prog_detail : progressive_details) {
// Only few combinations are expected to support outputting
// intermediate flushes for complete DC and complete passes.
// The test can be updated if more cases are expected to support it.
- bool expect_flush = (num_channels & 1) && !lossless;
- size_t xsize, ysize;
+ bool expect_flush = !has_alpha && !lossless;
+ size_t xsize;
+ size_t ysize;
if (single_group) {
// An image smaller than 256x256 ensures it contains only 1 group.
xsize = 99;
@@ -4774,12 +4824,13 @@ TEST_P(DecodeProgressiveTest, ProgressiveEventTest) {
jxl::CreateTestJXLCodestream(jxl::Bytes(pixels.data(), pixels.size()),
xsize, ysize, num_channels, params);
- for (size_t increment : {(size_t)1, data.size()}) {
+ for (size_t increment : {static_cast<size_t>(1), data.size()}) {
printf(
- "Testing with single_group=%d, lossless=%d, "
+ "Testing with single_group=%s, lossless=%s, "
"num_channels=%d, prog_detail=%d, increment=%d\n",
- single_group, lossless, (int)num_channels, (int)prog_detail,
- (int)increment);
+ BoolToCStr(single_group), BoolToCStr(lossless),
+ static_cast<int>(num_channels), static_cast<int>(prog_detail),
+ static_cast<int>(increment));
std::vector<std::vector<uint8_t>> passes(kNumPasses + 1);
for (int i = 0; i <= kNumPasses; ++i) {
passes[i].resize(pixels.size());
@@ -4963,13 +5014,13 @@ TEST(DecodeTest, JXL_TRANSCODE_JPEG_TEST(JPEGReconstructionTest)) {
ASSERT_TRUE(
EncodeJPEGData(*orig_io.Main().jpeg_data.get(), &jpeg_data, cparams));
std::vector<uint8_t> container;
- jxl::Bytes(jxl::kContainerHeader).AppendTo(&container);
+ jxl::Bytes(jxl::kContainerHeader).AppendTo(container);
jxl::AppendBoxHeader(jxl::MakeBoxType("jbrd"), jpeg_data.size(), false,
&container);
- jxl::Bytes(jpeg_data).AppendTo(&container);
+ jxl::Bytes(jpeg_data).AppendTo(container);
jxl::AppendBoxHeader(jxl::MakeBoxType("jxlc"), 0, true, &container);
jxl::PaddedBytes codestream = std::move(writer).TakeBytes();
- jxl::Bytes(codestream).AppendTo(&container);
+ jxl::Bytes(codestream).AppendTo(container);
VerifyJPEGReconstruction(jxl::Bytes(container), jxl::Bytes(orig));
}
@@ -4982,7 +5033,8 @@ TEST(DecodeTest, JXL_TRANSCODE_JPEG_TEST(JPEGReconstructionMetadataTest)) {
}
TEST(DecodeTest, ContinueFinalNonEssentialBoxTest) {
- size_t xsize = 80, ysize = 90;
+ size_t xsize = 80;
+ size_t ysize = 90;
std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, 4, 0);
jxl::TestCodestreamParams params;
params.box_format = kCSBF_Multi_Other_Terminated;
@@ -5038,7 +5090,7 @@ TEST(DecodeTest, ContinueFinalNonEssentialBoxTest) {
}
namespace {
-bool BoxTypeEquals(const std::string& type_string, JxlBoxType type) {
+bool BoxTypeEquals(const std::string& type_string, const JxlBoxType type) {
return type_string.size() == 4 && type_string[0] == type[0] &&
type_string[1] == type[1] && type_string[2] == type[2] &&
type_string[3] == type[3];
@@ -5054,28 +5106,38 @@ TEST(DecodeTest, ExtentedBoxSizeTest) {
JxlBoxType type;
uint64_t box_size;
+ uint64_t contents_size;
EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, orig.data(), orig.size()));
EXPECT_EQ(JXL_DEC_BOX, JxlDecoderProcessInput(dec));
EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBoxType(dec, type, JXL_FALSE));
EXPECT_TRUE(BoxTypeEquals("JXL ", type));
EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBoxSizeRaw(dec, &box_size));
EXPECT_EQ(12, box_size);
+ EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBoxSizeContents(dec, &contents_size));
+ EXPECT_EQ(contents_size + 8, box_size);
EXPECT_EQ(JXL_DEC_BOX, JxlDecoderProcessInput(dec));
EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBoxType(dec, type, JXL_FALSE));
EXPECT_TRUE(BoxTypeEquals("ftyp", type));
EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBoxSizeRaw(dec, &box_size));
EXPECT_EQ(20, box_size);
+ EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBoxSizeContents(dec, &contents_size));
+ EXPECT_EQ(contents_size + 8, box_size);
EXPECT_EQ(JXL_DEC_BOX, JxlDecoderProcessInput(dec));
EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBoxType(dec, type, JXL_FALSE));
EXPECT_TRUE(BoxTypeEquals("jxlc", type));
EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBoxSizeRaw(dec, &box_size));
EXPECT_EQ(72, box_size);
+ EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBoxSizeContents(dec, &contents_size));
+ // This is an extended box, hence the difference between `box_size` and
+ // `contents_size` is 16.
+ EXPECT_EQ(contents_size + 8 + 8, box_size);
JxlDecoderDestroy(dec);
}
TEST(DecodeTest, JXL_BOXES_TEST(BoxTest)) {
- size_t xsize = 1, ysize = 1;
+ size_t xsize = 1;
+ size_t ysize = 1;
std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, 4, 0);
jxl::TestCodestreamParams params;
params.box_format = kCSBF_Multi_Other_Terminated;
@@ -5096,6 +5158,7 @@ TEST(DecodeTest, JXL_BOXES_TEST(BoxTest)) {
JxlBoxType type;
uint64_t box_size;
+ uint64_t contents_size;
std::vector<uint8_t> contents(50);
size_t expected_release_size = 0;
@@ -5113,6 +5176,9 @@ TEST(DecodeTest, JXL_BOXES_TEST(BoxTest)) {
EXPECT_TRUE(BoxTypeEquals(expected_box_types[i], type));
if (expected_box_sizes[i]) {
EXPECT_EQ(expected_box_sizes[i], box_size);
+ EXPECT_EQ(JXL_DEC_SUCCESS,
+ JxlDecoderGetBoxSizeContents(dec, &contents_size));
+ EXPECT_EQ(contents_size + 8, box_size);
}
if (expected_release_size > 0) {
@@ -5147,7 +5213,8 @@ TEST(DecodeTest, JXL_BOXES_TEST(BoxTest)) {
}
TEST(DecodeTest, JXL_BOXES_TEST(ExifBrobBoxTest)) {
- size_t xsize = 1, ysize = 1;
+ size_t xsize = 1;
+ size_t ysize = 1;
std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, 4, 0);
jxl::TestCodestreamParams params;
// Lossless to verify pixels exactly after roundtrip.
@@ -5328,7 +5395,8 @@ TEST(DecodeTest, JXL_BOXES_TEST(ExifBrobBoxTest)) {
}
TEST(DecodeTest, JXL_BOXES_TEST(PartialCodestreamBoxTest)) {
- size_t xsize = 23, ysize = 81;
+ size_t xsize = 23;
+ size_t ysize = 81;
std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, 4, 0);
JxlPixelFormat format_orig = {4, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0};
// Lossless to verify pixels exactly after roundtrip.
@@ -5482,10 +5550,11 @@ TEST(DecodeTest, JXL_BOXES_TEST(PartialCodestreamBoxTest)) {
TEST(DecodeTest, SpotColorTest) {
jxl::CodecInOut io;
- size_t xsize = 55, ysize = 257;
+ size_t xsize = 55;
+ size_t ysize = 257;
io.metadata.m.color_encoding = jxl::ColorEncoding::LinearSRGB();
- jxl::Image3F main(xsize, ysize);
- jxl::ImageF spot(xsize, ysize);
+ JXL_ASSIGN_OR_DIE(Image3F main, Image3F::Create(xsize, ysize));
+ JXL_ASSIGN_OR_DIE(ImageF spot, ImageF::Create(xsize, ysize));
jxl::ZeroFillImage(&main);
jxl::ZeroFillImage(&spot);
@@ -5508,7 +5577,7 @@ TEST(DecodeTest, SpotColorTest) {
info.spot_color[3] = 0.5f;
io.metadata.m.extra_channel_info.push_back(info);
- std::vector<jxl::ImageF> ec;
+ std::vector<ImageF> ec;
ec.push_back(std::move(spot));
io.frames[0].SetExtraChannels(std::move(ec));
@@ -5524,7 +5593,7 @@ TEST(DecodeTest, SpotColorTest) {
for (size_t render_spot = 0; render_spot < 2; render_spot++) {
JxlPixelFormat format = {3, JXL_TYPE_UINT8, JXL_LITTLE_ENDIAN, 0};
- JxlDecoder* dec = JxlDecoderCreate(NULL);
+ JxlDecoder* dec = JxlDecoderCreate(nullptr);
EXPECT_EQ(JXL_DEC_SUCCESS,
JxlDecoderSubscribeEvents(
@@ -5545,7 +5614,8 @@ TEST(DecodeTest, SpotColorTest) {
JxlExtraChannelInfo extra_info;
EXPECT_EQ(JXL_DEC_SUCCESS,
JxlDecoderGetExtraChannelInfo(dec, 0, &extra_info));
- EXPECT_EQ((unsigned int)jxl::ExtraChannel::kSpotColor, extra_info.type);
+ EXPECT_EQ(static_cast<unsigned int>(jxl::ExtraChannel::kSpotColor),
+ extra_info.type);
EXPECT_EQ(JXL_DEC_NEED_IMAGE_OUT_BUFFER, JxlDecoderProcessInput(dec));
size_t buffer_size;
diff --git a/third_party/jpeg-xl/lib/jxl/enc_ac_strategy.cc b/third_party/jpeg-xl/lib/jxl/enc_ac_strategy.cc
index d3b5ad3269..4aafd7a5e5 100644
--- a/third_party/jpeg-xl/lib/jxl/enc_ac_strategy.cc
+++ b/third_party/jpeg-xl/lib/jxl/enc_ac_strategy.cc
@@ -18,20 +18,15 @@
#include <hwy/highway.h>
#include "lib/jxl/ac_strategy.h"
-#include "lib/jxl/ans_params.h"
#include "lib/jxl/base/bits.h"
#include "lib/jxl/base/compiler_specific.h"
#include "lib/jxl/base/fast_math-inl.h"
#include "lib/jxl/base/status.h"
-#include "lib/jxl/coeff_order_fwd.h"
-#include "lib/jxl/convolve.h"
-#include "lib/jxl/dct_scales.h"
#include "lib/jxl/dec_transforms-inl.h"
#include "lib/jxl/enc_aux_out.h"
#include "lib/jxl/enc_debug_image.h"
#include "lib/jxl/enc_params.h"
#include "lib/jxl/enc_transforms-inl.h"
-#include "lib/jxl/entropy_coder.h"
#include "lib/jxl/simd_util.h"
// Some of the floating point constants in this file and in other
@@ -215,10 +210,10 @@ const uint8_t* TypeMask(const uint8_t& raw_strategy) {
return kMask[raw_strategy];
}
-void DumpAcStrategy(const AcStrategyImage& ac_strategy, size_t xsize,
- size_t ysize, const char* tag, AuxOut* aux_out,
- const CompressParams& cparams) {
- Image3F color_acs(xsize, ysize);
+Status DumpAcStrategy(const AcStrategyImage& ac_strategy, size_t xsize,
+ size_t ysize, const char* tag, AuxOut* aux_out,
+ const CompressParams& cparams) {
+ JXL_ASSIGN_OR_RETURN(Image3F color_acs, Image3F::Create(xsize, ysize));
for (size_t y = 0; y < ysize; y++) {
float* JXL_RESTRICT rows[3] = {
color_acs.PlaneRow(0, y),
@@ -269,7 +264,7 @@ void DumpAcStrategy(const AcStrategyImage& ac_strategy, size_t xsize,
}
}
}
- DumpImage(cparams, tag, color_acs);
+ return DumpImage(cparams, tag, color_acs);
}
} // namespace
@@ -353,7 +348,9 @@ bool MultiBlockTransformCrossesVerticalBoundary(
float EstimateEntropy(const AcStrategy& acs, float entropy_mul, size_t x,
size_t y, const ACSConfig& config,
const float* JXL_RESTRICT cmap_factors, float* block,
- float* scratch_space, uint32_t* quantized) {
+ float* full_scratch_space, uint32_t* quantized) {
+ float* mem = full_scratch_space;
+ float* scratch_space = full_scratch_space + AcStrategy::kMaxCoeffArea;
const size_t size = (1 << acs.log2_covered_blocks()) * kDCTBlockSize;
// Apply transform.
@@ -403,8 +400,6 @@ float EstimateEntropy(const AcStrategy& acs, float entropy_mul, size_t x,
float entropy = 0.0f;
const HWY_CAPPED(float, 8) df8;
- auto mem_alloc = hwy::AllocateAligned<float>(AcStrategy::kMaxCoeffArea);
- float* mem = mem_alloc.get();
auto loss = Zero(df8);
for (size_t c = 0; c < 3; c++) {
const float* inv_matrix = config.dequant->InvMatrix(acs.RawStrategy(), c);
@@ -792,6 +787,7 @@ void FindBestFirstLevelDivisionForSquare(
void ProcessRectACS(const CompressParams& cparams, const ACSConfig& config,
const Rect& rect, const ColorCorrelationMap& cmap,
+ float* JXL_RESTRICT block, uint32_t* JXL_RESTRICT quantized,
AcStrategyImage* ac_strategy) {
// Main philosophy here:
// 1. First find best 8x8 transform for each area.
@@ -804,15 +800,7 @@ void ProcessRectACS(const CompressParams& cparams, const ACSConfig& config,
// integral transforms cross these boundaries leads to
// additional complications.
const float butteraugli_target = cparams.butteraugli_distance;
- const size_t dct_scratch_size =
- 3 * (MaxVectorSize() / sizeof(float)) * AcStrategy::kMaxBlockDim;
- // TODO(veluca): reuse allocations
- auto mem = hwy::AllocateAligned<float>(5 * AcStrategy::kMaxCoeffArea +
- dct_scratch_size);
- auto qmem = hwy::AllocateAligned<uint32_t>(AcStrategy::kMaxCoeffArea);
- uint32_t* JXL_RESTRICT quantized = qmem.get();
- float* JXL_RESTRICT block = mem.get();
- float* JXL_RESTRICT scratch_space = mem.get() + 3 * AcStrategy::kMaxCoeffArea;
+ float* JXL_RESTRICT scratch_space = block + 3 * AcStrategy::kMaxCoeffArea;
size_t bx = rect.x0();
size_t by = rect.y0();
JXL_ASSERT(rect.xsize() <= 8);
@@ -1085,26 +1073,37 @@ void AcStrategyHeuristics::Init(const Image3F& src, const Rect& rect_in,
static const float kPow1 = 0.33677806662454718;
static const float kPow2 = 0.50990926717963703;
static const float kPow3 = 0.36702940662370243;
- config.info_loss_multiplier *= pow(ratio, kPow1);
- config.zeros_mul *= pow(ratio, kPow2);
- config.cost_delta *= pow(ratio, kPow3);
+ config.info_loss_multiplier *= std::pow(ratio, kPow1);
+ config.zeros_mul *= std::pow(ratio, kPow2);
+ config.cost_delta *= std::pow(ratio, kPow3);
+}
+
+void AcStrategyHeuristics::PrepareForThreads(std::size_t num_threads) {
+ const size_t dct_scratch_size =
+ 3 * (MaxVectorSize() / sizeof(float)) * AcStrategy::kMaxBlockDim;
+ mem_per_thread = 6 * AcStrategy::kMaxCoeffArea + dct_scratch_size;
+ mem = hwy::AllocateAligned<float>(num_threads * mem_per_thread);
+ qmem_per_thread = AcStrategy::kMaxCoeffArea;
+ qmem = hwy::AllocateAligned<uint32_t>(num_threads * qmem_per_thread);
}
void AcStrategyHeuristics::ProcessRect(const Rect& rect,
const ColorCorrelationMap& cmap,
- AcStrategyImage* ac_strategy) {
+ AcStrategyImage* ac_strategy,
+ size_t thread) {
// In Falcon mode, use DCT8 everywhere and uniform quantization.
if (cparams.speed_tier >= SpeedTier::kCheetah) {
ac_strategy->FillDCT8(rect);
return;
}
HWY_DYNAMIC_DISPATCH(ProcessRectACS)
- (cparams, config, rect, cmap, ac_strategy);
+ (cparams, config, rect, cmap, mem.get() + thread * mem_per_thread,
+ qmem.get() + thread * qmem_per_thread, ac_strategy);
}
-void AcStrategyHeuristics::Finalize(const FrameDimensions& frame_dim,
- const AcStrategyImage& ac_strategy,
- AuxOut* aux_out) {
+Status AcStrategyHeuristics::Finalize(const FrameDimensions& frame_dim,
+ const AcStrategyImage& ac_strategy,
+ AuxOut* aux_out) {
// Accounting and debug output.
if (aux_out != nullptr) {
aux_out->num_small_blocks =
@@ -1141,9 +1140,11 @@ void AcStrategyHeuristics::Finalize(const FrameDimensions& frame_dim,
// if (JXL_DEBUG_AC_STRATEGY && WantDebugOutput(aux_out)) {
if (JXL_DEBUG_AC_STRATEGY && WantDebugOutput(cparams)) {
- DumpAcStrategy(ac_strategy, frame_dim.xsize, frame_dim.ysize, "ac_strategy",
- aux_out, cparams);
+ JXL_RETURN_IF_ERROR(DumpAcStrategy(ac_strategy, frame_dim.xsize,
+ frame_dim.ysize, "ac_strategy", aux_out,
+ cparams));
}
+ return true;
}
} // namespace jxl
diff --git a/third_party/jpeg-xl/lib/jxl/enc_ac_strategy.h b/third_party/jpeg-xl/lib/jxl/enc_ac_strategy.h
index 9f6d92a6f7..31eb73c6a0 100644
--- a/third_party/jpeg-xl/lib/jxl/enc_ac_strategy.h
+++ b/third_party/jpeg-xl/lib/jxl/enc_ac_strategy.h
@@ -58,16 +58,22 @@ struct ACSConfig {
};
struct AcStrategyHeuristics {
- AcStrategyHeuristics(const CompressParams& cparams) : cparams(cparams) {}
+ explicit AcStrategyHeuristics(const CompressParams& cparams)
+ : cparams(cparams), mem_per_thread(0), qmem_per_thread(0) {}
void Init(const Image3F& src, const Rect& rect_in, const ImageF& quant_field,
const ImageF& mask, const ImageF& mask1x1,
DequantMatrices* matrices);
+ void PrepareForThreads(std::size_t num_threads);
void ProcessRect(const Rect& rect, const ColorCorrelationMap& cmap,
- AcStrategyImage* ac_strategy);
- void Finalize(const FrameDimensions& frame_dim,
- const AcStrategyImage& ac_strategy, AuxOut* aux_out);
+ AcStrategyImage* ac_strategy, size_t thread);
+ Status Finalize(const FrameDimensions& frame_dim,
+ const AcStrategyImage& ac_strategy, AuxOut* aux_out);
const CompressParams& cparams;
- ACSConfig config;
+ ACSConfig config = {};
+ size_t mem_per_thread;
+ hwy::AlignedFreeUniquePtr<float[]> mem;
+ size_t qmem_per_thread;
+ hwy::AlignedFreeUniquePtr<uint32_t[]> qmem;
};
} // namespace jxl
diff --git a/third_party/jpeg-xl/lib/jxl/enc_adaptive_quantization.cc b/third_party/jpeg-xl/lib/jxl/enc_adaptive_quantization.cc
index ae4cd3bd3b..4c2ddba95b 100644
--- a/third_party/jpeg-xl/lib/jxl/enc_adaptive_quantization.cc
+++ b/third_party/jpeg-xl/lib/jxl/enc_adaptive_quantization.cc
@@ -9,6 +9,7 @@
#include <stdlib.h>
#include <algorithm>
+#include <atomic>
#include <cmath>
#include <string>
#include <vector>
@@ -26,8 +27,6 @@
#include "lib/jxl/base/status.h"
#include "lib/jxl/butteraugli/butteraugli.h"
#include "lib/jxl/cms/opsin_params.h"
-#include "lib/jxl/coeff_order_fwd.h"
-#include "lib/jxl/color_encoding_internal.h"
#include "lib/jxl/convolve.h"
#include "lib/jxl/dec_cache.h"
#include "lib/jxl/dec_group.h"
@@ -102,12 +101,12 @@ V ComputeMask(const D d, const V out_val) {
}
// mul and mul2 represent a scaling difference between jxl and butteraugli.
-static const float kSGmul = 226.77216153508914f;
-static const float kSGmul2 = 1.0f / 73.377132366608819f;
-static const float kLog2 = 0.693147181f;
+const float kSGmul = 226.77216153508914f;
+const float kSGmul2 = 1.0f / 73.377132366608819f;
+const float kLog2 = 0.693147181f;
// Includes correction factor for std::log -> log2.
-static const float kSGRetMul = kSGmul2 * 18.6580932135f * kLog2;
-static const float kSGVOffset = 7.7825991679894591f;
+const float kSGRetMul = kSGmul2 * 18.6580932135f * kLog2;
+const float kSGVOffset = 7.7825991679894591f;
template <bool invert, typename D, typename V>
V RatioOfDerivativesOfCubicRootToSimpleGamma(const D d, V v) {
@@ -131,7 +130,7 @@ V RatioOfDerivativesOfCubicRootToSimpleGamma(const D d, V v) {
}
template <bool invert = false>
-static float RatioOfDerivativesOfCubicRootToSimpleGamma(float v) {
+float RatioOfDerivativesOfCubicRootToSimpleGamma(float v) {
using DScalar = HWY_CAPPED(float, 1);
auto vscalar = Load(DScalar(), &v);
return GetLane(
@@ -396,12 +395,16 @@ void FuzzyErosion(const float butteraugli_target, const Rect& from_rect,
}
struct AdaptiveQuantizationImpl {
- void PrepareBuffers(size_t num_threads) {
- diff_buffer = ImageF(kEncTileDim + 8, num_threads);
+ Status PrepareBuffers(size_t num_threads) {
+ JXL_ASSIGN_OR_RETURN(diff_buffer,
+ ImageF::Create(kEncTileDim + 8, num_threads));
for (size_t i = pre_erosion.size(); i < num_threads; i++) {
- pre_erosion.emplace_back(kEncTileDimInBlocks * 2 + 2,
- kEncTileDimInBlocks * 2 + 2);
+ JXL_ASSIGN_OR_RETURN(ImageF tmp,
+ ImageF::Create(kEncTileDimInBlocks * 2 + 2,
+ kEncTileDimInBlocks * 2 + 2));
+ pre_erosion.emplace_back(std::move(tmp));
}
+ return true;
}
void ComputeTile(float butteraugli_target, float scale, const Image3F& xyb,
@@ -568,8 +571,7 @@ struct AdaptiveQuantizationImpl {
ImageF diff_buffer;
};
-static void Blur1x1Masking(ThreadPool* pool, ImageF* mask1x1,
- const Rect& rect) {
+Status Blur1x1Masking(ThreadPool* pool, ImageF* mask1x1, const Rect& rect) {
// Blur the mask1x1 to obtain the masking image.
// Before blurring it contains an image of absolute value of the
// Laplacian of the intensity channel.
@@ -595,30 +597,30 @@ static void Blur1x1Masking(ThreadPool* pool, ImageF* mask1x1,
{HWY_REP4(normalize_mul * kFilterMask1x1[1])},
{HWY_REP4(normalize_mul * kFilterMask1x1[4])},
{HWY_REP4(normalize_mul * kFilterMask1x1[3])}};
- ImageF temp(rect.xsize(), rect.ysize());
+ JXL_ASSIGN_OR_RETURN(ImageF temp, ImageF::Create(rect.xsize(), rect.ysize()));
Symmetric5(*mask1x1, rect, weights, pool, &temp);
*mask1x1 = std::move(temp);
+ return true;
}
-ImageF AdaptiveQuantizationMap(const float butteraugli_target,
- const Image3F& xyb, const Rect& rect,
- float scale, ThreadPool* pool, ImageF* mask,
- ImageF* mask1x1) {
+StatusOr<ImageF> AdaptiveQuantizationMap(const float butteraugli_target,
+ const Image3F& xyb, const Rect& rect,
+ float scale, ThreadPool* pool,
+ ImageF* mask, ImageF* mask1x1) {
JXL_DASSERT(rect.xsize() % kBlockDim == 0);
JXL_DASSERT(rect.ysize() % kBlockDim == 0);
AdaptiveQuantizationImpl impl;
const size_t xsize_blocks = rect.xsize() / kBlockDim;
const size_t ysize_blocks = rect.ysize() / kBlockDim;
- impl.aq_map = ImageF(xsize_blocks, ysize_blocks);
- *mask = ImageF(xsize_blocks, ysize_blocks);
- *mask1x1 = ImageF(xyb.xsize(), xyb.ysize());
+ JXL_ASSIGN_OR_RETURN(impl.aq_map, ImageF::Create(xsize_blocks, ysize_blocks));
+ JXL_ASSIGN_OR_RETURN(*mask, ImageF::Create(xsize_blocks, ysize_blocks));
+ JXL_ASSIGN_OR_RETURN(*mask1x1, ImageF::Create(xyb.xsize(), xyb.ysize()));
JXL_CHECK(RunOnPool(
pool, 0,
DivCeil(xsize_blocks, kEncTileDimInBlocks) *
DivCeil(ysize_blocks, kEncTileDimInBlocks),
[&](const size_t num_threads) {
- impl.PrepareBuffers(num_threads);
- return true;
+ return !!impl.PrepareBuffers(num_threads);
},
[&](const uint32_t tid, const size_t thread) {
size_t n_enc_tiles = DivCeil(xsize_blocks, kEncTileDimInBlocks);
@@ -634,7 +636,7 @@ ImageF AdaptiveQuantizationMap(const float butteraugli_target,
},
"AQ DiffPrecompute"));
- Blur1x1Masking(pool, mask1x1, rect);
+ JXL_RETURN_IF_ERROR(Blur1x1Masking(pool, mask1x1, rect));
return std::move(impl).aq_map;
}
@@ -654,24 +656,28 @@ namespace {
// If true, prints the quantization maps at each iteration.
constexpr bool FLAGS_dump_quant_state = false;
-void DumpHeatmap(const CompressParams& cparams, const AuxOut* aux_out,
- const std::string& label, const ImageF& image,
- float good_threshold, float bad_threshold) {
+Status DumpHeatmap(const CompressParams& cparams, const AuxOut* aux_out,
+ const std::string& label, const ImageF& image,
+ float good_threshold, float bad_threshold) {
if (JXL_DEBUG_ADAPTIVE_QUANTIZATION) {
- Image3F heatmap = CreateHeatMapImage(image, good_threshold, bad_threshold);
+ JXL_ASSIGN_OR_RETURN(
+ Image3F heatmap,
+ CreateHeatMapImage(image, good_threshold, bad_threshold));
char filename[200];
snprintf(filename, sizeof(filename), "%s%05d", label.c_str(),
aux_out->num_butteraugli_iters);
- DumpImage(cparams, filename, heatmap);
+ JXL_RETURN_IF_ERROR(DumpImage(cparams, filename, heatmap));
}
+ return true;
}
-void DumpHeatmaps(const CompressParams& cparams, const AuxOut* aux_out,
- float ba_target, const ImageF& quant_field,
- const ImageF& tile_heatmap, const ImageF& bt_diffmap) {
+Status DumpHeatmaps(const CompressParams& cparams, const AuxOut* aux_out,
+ float ba_target, const ImageF& quant_field,
+ const ImageF& tile_heatmap, const ImageF& bt_diffmap) {
if (JXL_DEBUG_ADAPTIVE_QUANTIZATION) {
- if (!WantDebugOutput(cparams)) return;
- ImageF inv_qmap(quant_field.xsize(), quant_field.ysize());
+ if (!WantDebugOutput(cparams)) return true;
+ JXL_ASSIGN_OR_RETURN(ImageF inv_qmap, ImageF::Create(quant_field.xsize(),
+ quant_field.ysize()));
for (size_t y = 0; y < quant_field.ysize(); ++y) {
const float* JXL_RESTRICT row_q = quant_field.ConstRow(y);
float* JXL_RESTRICT row_inv_q = inv_qmap.Row(y);
@@ -679,21 +685,24 @@ void DumpHeatmaps(const CompressParams& cparams, const AuxOut* aux_out,
row_inv_q[x] = 1.0f / row_q[x]; // never zero
}
}
- DumpHeatmap(cparams, aux_out, "quant_heatmap", inv_qmap, 4.0f * ba_target,
- 6.0f * ba_target);
- DumpHeatmap(cparams, aux_out, "tile_heatmap", tile_heatmap, ba_target,
- 1.5f * ba_target);
+ JXL_RETURN_IF_ERROR(DumpHeatmap(cparams, aux_out, "quant_heatmap", inv_qmap,
+ 4.0f * ba_target, 6.0f * ba_target));
+ JXL_RETURN_IF_ERROR(DumpHeatmap(cparams, aux_out, "tile_heatmap",
+ tile_heatmap, ba_target, 1.5f * ba_target));
// matches heat maps produced by the command line tool.
- DumpHeatmap(cparams, aux_out, "bt_diffmap", bt_diffmap,
- ButteraugliFuzzyInverse(1.5), ButteraugliFuzzyInverse(0.5));
+ JXL_RETURN_IF_ERROR(DumpHeatmap(cparams, aux_out, "bt_diffmap", bt_diffmap,
+ ButteraugliFuzzyInverse(1.5),
+ ButteraugliFuzzyInverse(0.5)));
}
+ return true;
}
-ImageF TileDistMap(const ImageF& distmap, int tile_size, int margin,
- const AcStrategyImage& ac_strategy) {
+StatusOr<ImageF> TileDistMap(const ImageF& distmap, int tile_size, int margin,
+ const AcStrategyImage& ac_strategy) {
const int tile_xsize = (distmap.xsize() + tile_size - 1) / tile_size;
const int tile_ysize = (distmap.ysize() + tile_size - 1) / tile_size;
- ImageF tile_distmap(tile_xsize, tile_ysize);
+ JXL_ASSIGN_OR_RETURN(ImageF tile_distmap,
+ ImageF::Create(tile_xsize, tile_ysize));
size_t distmap_stride = tile_distmap.PixelsPerRow();
for (int tile_y = 0; tile_y < tile_ysize; ++tile_y) {
AcStrategyRow ac_strategy_row = ac_strategy.ConstRow(tile_y);
@@ -754,14 +763,16 @@ ImageF TileDistMap(const ImageF& distmap, int tile_size, int margin,
return tile_distmap;
}
-static const float kDcQuantPow = 0.83f;
-static const float kDcQuant = 1.095924047623553f;
-static const float kAcQuant = 0.7381485255235064f;
+const float kDcQuantPow = 0.83f;
+const float kDcQuant = 1.095924047623553f;
+const float kAcQuant = 0.7381485255235064f;
// Computes the decoded image for a given set of compression parameters.
-ImageBundle RoundtripImage(const FrameHeader& frame_header,
- const Image3F& opsin, PassesEncoderState* enc_state,
- const JxlCmsInterface& cms, ThreadPool* pool) {
+StatusOr<ImageBundle> RoundtripImage(const FrameHeader& frame_header,
+ const Image3F& opsin,
+ PassesEncoderState* enc_state,
+ const JxlCmsInterface& cms,
+ ThreadPool* pool) {
std::unique_ptr<PassesDecoderState> dec_state =
jxl::make_unique<PassesDecoderState>();
JXL_CHECK(dec_state->output_encoding_info.SetFromMetadata(
@@ -775,7 +786,8 @@ ImageBundle RoundtripImage(const FrameHeader& frame_header,
size_t num_special_frames = enc_state->special_frames.size();
size_t num_passes = enc_state->progressive_splitter.GetNumPasses();
- ModularFrameEncoder modular_frame_encoder(frame_header, enc_state->cparams);
+ ModularFrameEncoder modular_frame_encoder(frame_header, enc_state->cparams,
+ false);
JXL_CHECK(InitializePassesEncoder(frame_header, opsin, Rect(opsin), cms, pool,
enc_state, &modular_frame_encoder,
nullptr));
@@ -784,7 +796,9 @@ ImageBundle RoundtripImage(const FrameHeader& frame_header,
ImageBundle decoded(&enc_state->shared.metadata->m);
decoded.origin = frame_header.frame_origin;
- decoded.SetFromImage(Image3F(opsin.xsize(), opsin.ysize()),
+ JXL_ASSIGN_OR_RETURN(Image3F tmp,
+ Image3F::Create(opsin.xsize(), opsin.ysize()));
+ decoded.SetFromImage(std::move(tmp),
dec_state->output_encoding_info.color_encoding);
PassesDecoderState::PipelineOptions options;
@@ -806,8 +820,10 @@ ImageBundle RoundtripImage(const FrameHeader& frame_header,
group_dec_caches = hwy::MakeUniqueAlignedArray<GroupDecCache>(num_threads);
return true;
};
+ std::atomic<bool> has_error{false};
const auto process_group = [&](const uint32_t group_index,
const size_t thread) {
+ if (has_error) return;
if (frame_header.loop_filter.epf_iters > 0) {
ComputeSigma(frame_header.loop_filter,
dec_state->shared->frame_dim.BlockGroupRect(group_index),
@@ -822,10 +838,14 @@ ImageBundle RoundtripImage(const FrameHeader& frame_header,
std::pair<ImageF*, Rect> ri = input.GetBuffer(3 + c);
FillPlane(0.0f, ri.first, ri.second);
}
- input.Done();
+ if (!input.Done()) {
+ has_error = true;
+ return;
+ }
};
JXL_CHECK(RunOnPool(pool, 0, num_groups, allocate_storage, process_group,
"AQ loop"));
+ if (has_error) return JXL_FAILURE("AQ loop failure");
// Ensure we don't create any new special frames.
enc_state->special_frames.resize(num_special_frames);
@@ -835,18 +855,18 @@ ImageBundle RoundtripImage(const FrameHeader& frame_header,
constexpr int kMaxButteraugliIters = 4;
-void FindBestQuantization(const FrameHeader& frame_header,
- const Image3F& linear, const Image3F& opsin,
- ImageF& quant_field, PassesEncoderState* enc_state,
- const JxlCmsInterface& cms, ThreadPool* pool,
- AuxOut* aux_out) {
+Status FindBestQuantization(const FrameHeader& frame_header,
+ const Image3F& linear, const Image3F& opsin,
+ ImageF& quant_field, PassesEncoderState* enc_state,
+ const JxlCmsInterface& cms, ThreadPool* pool,
+ AuxOut* aux_out) {
const CompressParams& cparams = enc_state->cparams;
if (cparams.resampling > 1 &&
cparams.original_butteraugli_distance <= 4.0 * cparams.resampling) {
// For downsampled opsin image, the butteraugli based adaptive quantization
// loop would only make the size bigger without improving the distance much,
// so in this case we enable it only for very high butteraugli targets.
- return;
+ return true;
}
Quantizer& quantizer = enc_state->shared.quantizer;
ImageI& raw_quant_field = enc_state->shared.raw_quant_field;
@@ -863,10 +883,13 @@ void FindBestQuantization(const FrameHeader& frame_header,
AdjustQuantField(enc_state->shared.ac_strategy, Rect(quant_field),
original_butteraugli, &quant_field);
ImageF tile_distmap;
- ImageF initial_quant_field(quant_field.xsize(), quant_field.ysize());
+ JXL_ASSIGN_OR_RETURN(
+ ImageF initial_quant_field,
+ ImageF::Create(quant_field.xsize(), quant_field.ysize()));
CopyImageTo(quant_field, &initial_quant_field);
- float initial_qf_min, initial_qf_max;
+ float initial_qf_min;
+ float initial_qf_max;
ImageMinMax(initial_quant_field, &initial_qf_min, &initial_qf_max);
float initial_qf_ratio = initial_qf_max / initial_qf_min;
float qf_max_deviation_low = std::sqrt(250 / initial_qf_ratio);
@@ -893,8 +916,9 @@ void FindBestQuantization(const FrameHeader& frame_header,
}
}
quantizer.SetQuantField(initial_quant_dc, quant_field, &raw_quant_field);
- ImageBundle dec_linear =
- RoundtripImage(frame_header, opsin, enc_state, cms, pool);
+ JXL_ASSIGN_OR_RETURN(
+ ImageBundle dec_linear,
+ RoundtripImage(frame_header, opsin, enc_state, cms, pool));
float score;
ImageF diffmap;
JXL_CHECK(comparator.CompareWith(dec_linear, &diffmap, &score));
@@ -902,16 +926,19 @@ void FindBestQuantization(const FrameHeader& frame_header,
score = -score;
ScaleImage(-1.0f, &diffmap);
}
- tile_distmap = TileDistMap(diffmap, 8 * cparams.resampling, 0,
- enc_state->shared.ac_strategy);
+ JXL_ASSIGN_OR_RETURN(tile_distmap,
+ TileDistMap(diffmap, 8 * cparams.resampling, 0,
+ enc_state->shared.ac_strategy));
if (JXL_DEBUG_ADAPTIVE_QUANTIZATION && WantDebugOutput(cparams)) {
- DumpImage(cparams, ("dec" + ToString(i)).c_str(), *dec_linear.color());
- DumpHeatmaps(cparams, aux_out, butteraugli_target, quant_field,
- tile_distmap, diffmap);
+ JXL_RETURN_IF_ERROR(DumpImage(cparams, ("dec" + ToString(i)).c_str(),
+ *dec_linear.color()));
+ JXL_RETURN_IF_ERROR(DumpHeatmaps(cparams, aux_out, butteraugli_target,
+ quant_field, tile_distmap, diffmap));
}
if (aux_out != nullptr) ++aux_out->num_butteraugli_iters;
if (JXL_DEBUG_ADAPTIVE_QUANTIZATION) {
- float minval, maxval;
+ float minval;
+ float maxval;
ImageMinMax(quant_field, &minval, &maxval);
printf("\nButteraugli iter: %d/%d\n", i, kMaxButteraugliIters);
printf("Butteraugli distance: %f (target = %f)\n", score,
@@ -967,8 +994,10 @@ void FindBestQuantization(const FrameHeader& frame_header,
if (diff > 1.0f) {
float old = row_q[x];
row_q[x] *= diff;
- int qf_old = old * quantizer.InvGlobalScale() + 0.5;
- int qf_new = row_q[x] * quantizer.InvGlobalScale() + 0.5;
+ int qf_old =
+ static_cast<int>(std::lround(old * quantizer.InvGlobalScale()));
+ int qf_new = static_cast<int>(
+ std::lround(row_q[x] * quantizer.InvGlobalScale()));
if (qf_old == qf_new) {
row_q[x] = old + quantizer.Scale();
}
@@ -988,8 +1017,10 @@ void FindBestQuantization(const FrameHeader& frame_header,
} else {
float old = row_q[x];
row_q[x] *= diff;
- int qf_old = old * quantizer.InvGlobalScale() + 0.5;
- int qf_new = row_q[x] * quantizer.InvGlobalScale() + 0.5;
+ int qf_old =
+ static_cast<int>(std::lround(old * quantizer.InvGlobalScale()));
+ int qf_new = static_cast<int>(
+ std::lround(row_q[x] * quantizer.InvGlobalScale()));
if (qf_old == qf_new) {
row_q[x] = old + quantizer.Scale();
}
@@ -1001,13 +1032,14 @@ void FindBestQuantization(const FrameHeader& frame_header,
}
}
quantizer.SetQuantField(initial_quant_dc, quant_field, &raw_quant_field);
+ return true;
}
-void FindBestQuantizationMaxError(const FrameHeader& frame_header,
- const Image3F& opsin, ImageF& quant_field,
- PassesEncoderState* enc_state,
- const JxlCmsInterface& cms, ThreadPool* pool,
- AuxOut* aux_out) {
+Status FindBestQuantizationMaxError(const FrameHeader& frame_header,
+ const Image3F& opsin, ImageF& quant_field,
+ PassesEncoderState* enc_state,
+ const JxlCmsInterface& cms,
+ ThreadPool* pool, AuxOut* aux_out) {
// TODO(szabadka): Make this work for non-opsin color spaces.
const CompressParams& cparams = enc_state->cparams;
Quantizer& quantizer = enc_state->shared.quantizer;
@@ -1026,12 +1058,15 @@ void FindBestQuantizationMaxError(const FrameHeader& frame_header,
for (int i = 0; i < kMaxButteraugliIters + 1; ++i) {
quantizer.SetQuantField(initial_quant_dc, quant_field, &raw_quant_field);
if (JXL_DEBUG_ADAPTIVE_QUANTIZATION && aux_out) {
- DumpXybImage(cparams, ("ops" + ToString(i)).c_str(), opsin);
+ JXL_RETURN_IF_ERROR(
+ DumpXybImage(cparams, ("ops" + ToString(i)).c_str(), opsin));
}
- ImageBundle decoded =
- RoundtripImage(frame_header, opsin, enc_state, cms, pool);
+ JXL_ASSIGN_OR_RETURN(
+ ImageBundle decoded,
+ RoundtripImage(frame_header, opsin, enc_state, cms, pool));
if (JXL_DEBUG_ADAPTIVE_QUANTIZATION && aux_out) {
- DumpXybImage(cparams, ("dec" + ToString(i)).c_str(), *decoded.color());
+ JXL_RETURN_IF_ERROR(DumpXybImage(cparams, ("dec" + ToString(i)).c_str(),
+ *decoded.color()));
}
for (size_t by = 0; by < enc_state->shared.frame_dim.ysize_blocks; by++) {
AcStrategyRow ac_strategy_row =
@@ -1073,6 +1108,7 @@ void FindBestQuantizationMaxError(const FrameHeader& frame_header,
}
}
quantizer.SetQuantField(initial_quant_dc, quant_field, &raw_quant_field);
+ return true;
}
} // namespace
@@ -1142,28 +1178,31 @@ float InitialQuantDC(float butteraugli_target) {
return std::min(kDcQuant / butteraugli_target_dc, 50.f);
}
-ImageF InitialQuantField(const float butteraugli_target, const Image3F& opsin,
- const Rect& rect, ThreadPool* pool, float rescale,
- ImageF* mask, ImageF* mask1x1) {
+StatusOr<ImageF> InitialQuantField(const float butteraugli_target,
+ const Image3F& opsin, const Rect& rect,
+ ThreadPool* pool, float rescale,
+ ImageF* mask, ImageF* mask1x1) {
const float quant_ac = kAcQuant / butteraugli_target;
return HWY_DYNAMIC_DISPATCH(AdaptiveQuantizationMap)(
butteraugli_target, opsin, rect, quant_ac * rescale, pool, mask, mask1x1);
}
-void FindBestQuantizer(const FrameHeader& frame_header, const Image3F* linear,
- const Image3F& opsin, ImageF& quant_field,
- PassesEncoderState* enc_state,
- const JxlCmsInterface& cms, ThreadPool* pool,
- AuxOut* aux_out, double rescale) {
+Status FindBestQuantizer(const FrameHeader& frame_header, const Image3F* linear,
+ const Image3F& opsin, ImageF& quant_field,
+ PassesEncoderState* enc_state,
+ const JxlCmsInterface& cms, ThreadPool* pool,
+ AuxOut* aux_out, double rescale) {
const CompressParams& cparams = enc_state->cparams;
if (cparams.max_error_mode) {
- FindBestQuantizationMaxError(frame_header, opsin, quant_field, enc_state,
- cms, pool, aux_out);
+ JXL_RETURN_IF_ERROR(FindBestQuantizationMaxError(
+ frame_header, opsin, quant_field, enc_state, cms, pool, aux_out));
} else if (linear && cparams.speed_tier <= SpeedTier::kKitten) {
// Normal encoding to a butteraugli score.
- FindBestQuantization(frame_header, *linear, opsin, quant_field, enc_state,
- cms, pool, aux_out);
+ JXL_RETURN_IF_ERROR(FindBestQuantization(frame_header, *linear, opsin,
+ quant_field, enc_state, cms, pool,
+ aux_out));
}
+ return true;
}
} // namespace jxl
diff --git a/third_party/jpeg-xl/lib/jxl/enc_adaptive_quantization.h b/third_party/jpeg-xl/lib/jxl/enc_adaptive_quantization.h
index 6aa8b10df6..26ed3f26ca 100644
--- a/third_party/jpeg-xl/lib/jxl/enc_adaptive_quantization.h
+++ b/third_party/jpeg-xl/lib/jxl/enc_adaptive_quantization.h
@@ -31,10 +31,11 @@ struct AuxOut;
// of the input image, while a value less than 1.0 indicates that less
// fine-grained quantization should be enough. Returns a mask, too, which
// can later be used to make better decisions about ac strategy.
-ImageF InitialQuantField(float butteraugli_target, const Image3F& opsin,
- const Rect& rect, ThreadPool* pool, float rescale,
- ImageF* initial_quant_mask,
- ImageF* initial_quant_mask1x1);
+StatusOr<ImageF> InitialQuantField(float butteraugli_target,
+ const Image3F& opsin, const Rect& rect,
+ ThreadPool* pool, float rescale,
+ ImageF* initial_quant_mask,
+ ImageF* initial_quant_mask1x1);
float InitialQuantDC(float butteraugli_target);
@@ -45,11 +46,11 @@ void AdjustQuantField(const AcStrategyImage& ac_strategy, const Rect& rect,
// quant_field. Also computes the dequant_map corresponding to the given
// dequant_float_map and chosen quantization levels.
// `linear` is only used in Kitten mode or slower.
-void FindBestQuantizer(const FrameHeader& frame_header, const Image3F* linear,
- const Image3F& opsin, ImageF& quant_field,
- PassesEncoderState* enc_state,
- const JxlCmsInterface& cms, ThreadPool* pool,
- AuxOut* aux_out, double rescale = 1.0);
+Status FindBestQuantizer(const FrameHeader& frame_header, const Image3F* linear,
+ const Image3F& opsin, ImageF& quant_field,
+ PassesEncoderState* enc_state,
+ const JxlCmsInterface& cms, ThreadPool* pool,
+ AuxOut* aux_out, double rescale = 1.0);
} // namespace jxl
diff --git a/third_party/jpeg-xl/lib/jxl/enc_ans.cc b/third_party/jpeg-xl/lib/jxl/enc_ans.cc
index 3efa62d8e1..5e59790b1e 100644
--- a/third_party/jpeg-xl/lib/jxl/enc_ans.cc
+++ b/third_party/jpeg-xl/lib/jxl/enc_ans.cc
@@ -5,11 +5,13 @@
#include "lib/jxl/enc_ans.h"
+#include <jxl/types.h>
#include <stdint.h>
#include <algorithm>
#include <array>
#include <cmath>
+#include <cstdint>
#include <limits>
#include <numeric>
#include <type_traits>
@@ -20,12 +22,15 @@
#include "lib/jxl/ans_common.h"
#include "lib/jxl/base/bits.h"
#include "lib/jxl/base/fast_math-inl.h"
+#include "lib/jxl/base/status.h"
#include "lib/jxl/dec_ans.h"
+#include "lib/jxl/enc_ans_params.h"
#include "lib/jxl/enc_aux_out.h"
#include "lib/jxl/enc_cluster.h"
#include "lib/jxl/enc_context_map.h"
#include "lib/jxl/enc_fields.h"
#include "lib/jxl/enc_huffman.h"
+#include "lib/jxl/enc_params.h"
#include "lib/jxl/fields.h"
namespace jxl {
@@ -37,7 +42,7 @@ constexpr
#endif
bool ans_fuzzer_friendly_ = false;
-static const int kMaxNumSymbolsForSmallCode = 4;
+const int kMaxNumSymbolsForSmallCode = 4;
void ANSBuildInfoTable(const ANSHistBin* counts, const AliasTable::Entry* table,
size_t alphabet_size, size_t log_alpha_size,
@@ -99,16 +104,16 @@ float EstimateDataBitsFlat(const ANSHistBin* histogram, size_t len) {
// Static Huffman code for encoding logcounts. The last symbol is used as RLE
// sequence.
-static const uint8_t kLogCountBitLengths[ANS_LOG_TAB_SIZE + 2] = {
+const uint8_t kLogCountBitLengths[ANS_LOG_TAB_SIZE + 2] = {
5, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 6, 7, 7,
};
-static const uint8_t kLogCountSymbols[ANS_LOG_TAB_SIZE + 2] = {
+const uint8_t kLogCountSymbols[ANS_LOG_TAB_SIZE + 2] = {
17, 11, 15, 3, 9, 7, 4, 2, 5, 6, 0, 33, 1, 65,
};
// Returns the difference between largest count that can be represented and is
// smaller than "count" and smallest representable count larger than "count".
-static int SmallestIncrement(uint32_t count, uint32_t shift) {
+int SmallestIncrement(uint32_t count, uint32_t shift) {
int bits = count == 0 ? -1 : FloorLog2Nonzero(count);
int drop_bits = bits - GetPopulationCountPrecision(bits, shift);
return drop_bits < 0 ? 1 : (1 << drop_bits);
@@ -148,10 +153,11 @@ bool RebalanceHistogram(const float* targets, int max_symbol, int table_size,
int inc = SmallestIncrement(counts[n], shift);
counts[n] -= counts[n] & (inc - 1);
// TODO(robryk): Should we rescale targets[n]?
- const float target =
- minimize_error_of_sum ? (sum_nonrounded - sum) : targets[n];
+ const int target = minimize_error_of_sum
+ ? (static_cast<int>(sum_nonrounded) - sum)
+ : static_cast<int>(targets[n]);
if (counts[n] == 0 ||
- (target > counts[n] + inc / 2 && counts[n] + inc < table_size)) {
+ (target >= counts[n] + inc / 2 && counts[n] + inc < table_size)) {
counts[n] += inc;
}
sum += counts[n];
@@ -203,11 +209,11 @@ Status NormalizeCounts(ANSHistBin* counts, int* omit_pos, const int length,
for (size_t n = 0; n < targets.size(); ++n) {
targets[n] = norm * counts[n];
}
- if (!RebalanceHistogram<false>(&targets[0], max_symbol, table_size, shift,
+ if (!RebalanceHistogram<false>(targets.data(), max_symbol, table_size, shift,
omit_pos, counts)) {
// Use an alternative rebalancing mechanism if the one above failed
// to create a histogram that is positive wherever the original one was.
- if (!RebalanceHistogram<true>(&targets[0], max_symbol, table_size, shift,
+ if (!RebalanceHistogram<true>(targets.data(), max_symbol, table_size, shift,
omit_pos, counts)) {
return JXL_FAILURE("Logic error: couldn't rebalance a histogram");
}
@@ -482,8 +488,8 @@ size_t BuildAndStoreANSEncodingData(
std::vector<ANSHistBin> counts(histogram, histogram + alphabet_size);
if (!counts.empty()) {
size_t sum = 0;
- for (size_t i = 0; i < counts.size(); i++) {
- sum += counts[i];
+ for (int count : counts) {
+ sum += count;
}
if (sum == 0) {
counts[0] = ANS_TAB_SIZE;
@@ -538,8 +544,8 @@ template <typename Writer>
void EncodeUintConfigs(const std::vector<HybridUintConfig>& uint_config,
Writer* writer, size_t log_alpha_size) {
// TODO(veluca): RLE?
- for (size_t i = 0; i < uint_config.size(); i++) {
- EncodeUintConfig(uint_config[i], writer, log_alpha_size);
+ for (const auto& cfg : uint_config) {
+ EncodeUintConfig(cfg, writer, log_alpha_size);
}
}
template void EncodeUintConfigs(const std::vector<HybridUintConfig>&,
@@ -553,8 +559,7 @@ void ChooseUintConfigs(const HistogramParams& params,
std::vector<Histogram>* clustered_histograms,
EntropyEncodingData* codes, size_t* log_alpha_size) {
codes->uint_config.resize(clustered_histograms->size());
- if (params.streaming_mode ||
- params.uint_method == HistogramParams::HybridUintMethod::kNone) {
+ if (params.uint_method == HistogramParams::HybridUintMethod::kNone) {
return;
}
if (params.uint_method == HistogramParams::HybridUintMethod::k000) {
@@ -570,6 +575,12 @@ void ChooseUintConfigs(const HistogramParams& params,
return;
}
+ // If the uint config is adaptive, just stick with the default in streaming
+ // mode.
+ if (params.streaming_mode) {
+ return;
+ }
+
// Brute-force method that tries a few options.
std::vector<HybridUintConfig> configs;
if (params.uint_method == HistogramParams::HybridUintMethod::kBest) {
@@ -619,12 +630,11 @@ void ChooseUintConfigs(const HistogramParams& params,
std::fill(is_valid.begin(), is_valid.end(), true);
std::fill(extra_bits.begin(), extra_bits.end(), 0);
- for (size_t i = 0; i < clustered_histograms->size(); i++) {
- (*clustered_histograms)[i].Clear();
+ for (auto& histo : *clustered_histograms) {
+ histo.Clear();
}
- for (size_t i = 0; i < tokens.size(); ++i) {
- for (size_t j = 0; j < tokens[i].size(); ++j) {
- const Token token = tokens[i][j];
+ for (const auto& stream : tokens) {
+ for (const auto& token : stream) {
// TODO(veluca): do not ignore lz77 commands.
if (token.is_lz77_length) continue;
size_t histo = context_map[token.context];
@@ -632,7 +642,7 @@ void ChooseUintConfigs(const HistogramParams& params,
cfg.Encode(token.value, &tok, &nbits, &bits);
if (tok >= max_alpha ||
(codes->lz77.enabled && tok >= codes->lz77.min_symbol)) {
- is_valid[histo] = false;
+ is_valid[histo] = JXL_FALSE;
continue;
}
extra_bits[histo] += nbits;
@@ -654,13 +664,12 @@ void ChooseUintConfigs(const HistogramParams& params,
}
// Rebuild histograms.
- for (size_t i = 0; i < clustered_histograms->size(); i++) {
- (*clustered_histograms)[i].Clear();
+ for (auto& histo : *clustered_histograms) {
+ histo.Clear();
}
*log_alpha_size = 4;
- for (size_t i = 0; i < tokens.size(); ++i) {
- for (size_t j = 0; j < tokens[i].size(); ++j) {
- const Token token = tokens[i][j];
+ for (const auto& stream : tokens) {
+ for (const auto& token : stream) {
uint32_t tok, nbits, bits;
size_t histo = context_map[token.context];
(token.is_lz77_length ? codes->lz77.length_uint_config
@@ -771,7 +780,7 @@ class HistogramBuilder {
}
SizeWriter size_writer; // Used if writer == nullptr to estimate costs.
cost += 1;
- if (writer) writer->Write(1, codes->use_prefix_code);
+ if (writer) writer->Write(1, TO_JXL_BOOL(codes->use_prefix_code));
if (codes->use_prefix_code) {
log_alpha_size = PREFIX_MAX_BITS;
@@ -785,8 +794,8 @@ class HistogramBuilder {
EncodeUintConfigs(codes->uint_config, writer, log_alpha_size);
}
if (codes->use_prefix_code) {
- for (size_t c = 0; c < clustered_histograms.size(); ++c) {
- size_t alphabet_size = clustered_histograms[c].alphabet_size();
+ for (const auto& histo : clustered_histograms) {
+ size_t alphabet_size = histo.alphabet_size();
if (writer) {
StoreVarLenUint16(alphabet_size - 1, writer);
} else {
@@ -832,9 +841,8 @@ class SymbolCostEstimator {
HistogramBuilder builder(num_contexts);
// Build histograms for estimating lz77 savings.
HybridUintConfig uint_config;
- for (size_t i = 0; i < tokens.size(); ++i) {
- for (size_t j = 0; j < tokens[i].size(); ++j) {
- const Token token = tokens[i][j];
+ for (const auto& stream : tokens) {
+ for (const auto& token : stream) {
uint32_t tok, nbits, bits;
(token.is_lz77_length ? lz77.length_uint_config : uint_config)
.Encode(token.value, &tok, &nbits, &bits);
@@ -1025,12 +1033,7 @@ struct HashChain {
// Count down, so if due to small distance multiplier multiple distances
// map to the same code, the smallest code will be used in the end.
for (int i = kNumSpecialDistances - 1; i >= 0; --i) {
- int xi = kSpecialDistances[i][0];
- int yi = kSpecialDistances[i][1];
- int distance = yi * distance_multiplier + xi;
- // Ensure that we map distance 1 to the lowest symbols.
- if (distance < 1) distance = 1;
- special_dist_table_[distance] = i;
+ special_dist_table_[SpecialDistance(i, distance_multiplier)] = i;
}
num_special_distances_ = kNumSpecialDistances;
}
@@ -1041,9 +1044,9 @@ struct HashChain {
if (pos + 2 < size_) {
// TODO(lode): take the MSB's of the uint32_t values into account as well,
// given that the hash code itself is less than 32 bits.
- result ^= (uint32_t)(data_[pos + 0] << 0u);
- result ^= (uint32_t)(data_[pos + 1] << hash_shift_);
- result ^= (uint32_t)(data_[pos + 2] << (hash_shift_ * 2));
+ result ^= static_cast<uint32_t>(data_[pos + 0] << 0u);
+ result ^= static_cast<uint32_t>(data_[pos + 1] << hash_shift_);
+ result ^= static_cast<uint32_t>(data_[pos + 2] << (hash_shift_ * 2));
} else {
// No need to compute hash of last 2 bytes, the length 2 is too short.
return 0;
@@ -1071,7 +1074,7 @@ struct HashChain {
uint32_t hashval = GetHash(pos);
uint32_t wpos = pos & window_mask_;
- val[wpos] = (int)hashval;
+ val[wpos] = static_cast<int>(hashval);
if (head[hashval] != -1) chain[wpos] = head[hashval];
head[hashval] = wpos;
@@ -1142,7 +1145,10 @@ struct HashChain {
} else {
if (hashpos == chain[hashpos]) break;
hashpos = chain[hashpos];
- if (val[hashpos] != (int)hashval) break; // outdated hash value
+ if (val[hashpos] != static_cast<int>(hashval)) {
+ // outdated hash value
+ break;
+ }
}
}
}
@@ -1274,7 +1280,8 @@ void ApplyLZ77_LZ77(const HistogramParams& params, size_t num_contexts,
HashChain chain(in.data(), in.size(), window_size, min_length, max_length,
distance_multiplier);
- size_t len, dist_symbol;
+ size_t len;
+ size_t dist_symbol;
const size_t max_lazy_match_len = 256; // 0 to disable lazy matching
@@ -1507,7 +1514,7 @@ void EncodeHistograms(const std::vector<uint8_t>& context_map,
}
EncodeContextMap(context_map, codes.encoding_info.size(), writer, layer,
aux_out);
- writer->Write(1, codes.use_prefix_code);
+ writer->Write(1, TO_JXL_BOOL(codes.use_prefix_code));
size_t log_alpha_size = 8;
if (codes.use_prefix_code) {
log_alpha_size = PREFIX_MAX_BITS;
@@ -1583,10 +1590,9 @@ size_t BuildAndEncodeHistograms(const HistogramParams& params,
if (ans_fuzzer_friendly_) {
uint_config = HybridUintConfig(10, 0, 0);
}
- for (size_t i = 0; i < tokens.size(); ++i) {
+ for (const auto& stream : tokens) {
if (codes->lz77.enabled) {
- for (size_t j = 0; j < tokens[i].size(); ++j) {
- const Token& token = tokens[i][j];
+ for (const auto& token : stream) {
total_tokens++;
uint32_t tok, nbits, bits;
(token.is_lz77_length ? codes->lz77.length_uint_config : uint_config)
@@ -1595,16 +1601,14 @@ size_t BuildAndEncodeHistograms(const HistogramParams& params,
builder.VisitSymbol(tok, token.context);
}
} else if (num_contexts == 1) {
- for (size_t j = 0; j < tokens[i].size(); ++j) {
- const Token& token = tokens[i][j];
+ for (const auto& token : stream) {
total_tokens++;
uint32_t tok, nbits, bits;
uint_config.Encode(token.value, &tok, &nbits, &bits);
builder.VisitSymbol(tok, /*token.context=*/0);
}
} else {
- for (size_t j = 0; j < tokens[i].size(); ++j) {
- const Token& token = tokens[i][j];
+ for (const auto& token : stream) {
total_tokens++;
uint32_t tok, nbits, bits;
uint_config.Encode(token.value, &tok, &nbits, &bits);
@@ -1654,10 +1658,10 @@ size_t BuildAndEncodeHistograms(const HistogramParams& params,
codes->encoded_histograms.emplace_back();
BitWriter* histo_writer = &codes->encoded_histograms.back();
BitWriter::Allotment allotment(histo_writer, 256 + alphabet_size * 24);
- BuildAndStoreANSEncodingData(params.ans_histogram_strategy, counts.data(),
- alphabet_size, log_alpha_size,
- codes->use_prefix_code,
- &codes->encoding_info.back()[0], histo_writer);
+ BuildAndStoreANSEncodingData(
+ params.ans_histogram_strategy, counts.data(), alphabet_size,
+ log_alpha_size, codes->use_prefix_code,
+ codes->encoding_info.back().data(), histo_writer);
allotment.ReclaimAndCharge(histo_writer, 0, nullptr);
}
@@ -1680,9 +1684,8 @@ size_t WriteTokens(const std::vector<Token>& tokens,
size_t context_offset, BitWriter* writer) {
size_t num_extra_bits = 0;
if (codes.use_prefix_code) {
- for (size_t i = 0; i < tokens.size(); i++) {
+ for (const auto& token : tokens) {
uint32_t tok, nbits, bits;
- const Token& token = tokens[i];
size_t histo = context_map[context_offset + token.context];
(token.is_lz77_length ? codes.lz77.length_uint_config
: codes.uint_config[histo])
@@ -1693,7 +1696,8 @@ size_t WriteTokens(const std::vector<Token>& tokens,
// codes.encoding_info[histo][tok].bits);
// writer->Write(nbits, bits);
uint64_t data = codes.encoding_info[histo][tok].bits;
- data |= bits << codes.encoding_info[histo][tok].depth;
+ data |= static_cast<uint64_t>(bits)
+ << codes.encoding_info[histo][tok].depth;
writer->Write(codes.encoding_info[histo][tok].depth + nbits, data);
num_extra_bits += nbits;
}
@@ -1765,7 +1769,8 @@ void WriteTokens(const std::vector<Token>& tokens,
const EntropyEncodingData& codes,
const std::vector<uint8_t>& context_map, size_t context_offset,
BitWriter* writer, size_t layer, AuxOut* aux_out) {
- BitWriter::Allotment allotment(writer, 32 * tokens.size() + 32 * 1024 * 4);
+ // Theoretically, we could have 15 prefix code bits + 31 extra bits.
+ BitWriter::Allotment allotment(writer, 46 * tokens.size() + 32 * 1024 * 4);
size_t num_extra_bits =
WriteTokens(tokens, codes, context_map, context_offset, writer);
allotment.ReclaimAndCharge(writer, layer, aux_out);
@@ -1779,4 +1784,51 @@ void SetANSFuzzerFriendly(bool ans_fuzzer_friendly) {
ans_fuzzer_friendly_ = ans_fuzzer_friendly;
#endif
}
+
+HistogramParams HistogramParams::ForModular(
+ const CompressParams& cparams,
+ const std::vector<uint8_t>& extra_dc_precision, bool streaming_mode) {
+ HistogramParams params;
+ params.streaming_mode = streaming_mode;
+ if (cparams.speed_tier > SpeedTier::kKitten) {
+ params.clustering = HistogramParams::ClusteringType::kFast;
+ params.ans_histogram_strategy =
+ cparams.speed_tier > SpeedTier::kThunder
+ ? HistogramParams::ANSHistogramStrategy::kFast
+ : HistogramParams::ANSHistogramStrategy::kApproximate;
+ params.lz77_method =
+ cparams.decoding_speed_tier >= 3 && cparams.modular_mode
+ ? (cparams.speed_tier >= SpeedTier::kFalcon
+ ? HistogramParams::LZ77Method::kRLE
+ : HistogramParams::LZ77Method::kLZ77)
+ : HistogramParams::LZ77Method::kNone;
+ // Near-lossless DC, as well as modular mode, require choosing hybrid uint
+ // more carefully.
+ if ((!extra_dc_precision.empty() && extra_dc_precision[0] != 0) ||
+ (cparams.modular_mode && cparams.speed_tier < SpeedTier::kCheetah)) {
+ params.uint_method = HistogramParams::HybridUintMethod::kFast;
+ } else {
+ params.uint_method = HistogramParams::HybridUintMethod::kNone;
+ }
+ } else if (cparams.speed_tier <= SpeedTier::kTortoise) {
+ params.lz77_method = HistogramParams::LZ77Method::kOptimal;
+ } else {
+ params.lz77_method = HistogramParams::LZ77Method::kLZ77;
+ }
+ if (cparams.decoding_speed_tier >= 1) {
+ params.max_histograms = 12;
+ }
+ if (cparams.decoding_speed_tier >= 1 && cparams.responsive) {
+ params.lz77_method = cparams.speed_tier >= SpeedTier::kCheetah
+ ? HistogramParams::LZ77Method::kRLE
+ : cparams.speed_tier >= SpeedTier::kKitten
+ ? HistogramParams::LZ77Method::kLZ77
+ : HistogramParams::LZ77Method::kOptimal;
+ }
+ if (cparams.decoding_speed_tier >= 2 && cparams.responsive) {
+ params.uint_method = HistogramParams::HybridUintMethod::k000;
+ params.force_huffman = true;
+ }
+ return params;
+}
} // namespace jxl
diff --git a/third_party/jpeg-xl/lib/jxl/enc_ans.h b/third_party/jpeg-xl/lib/jxl/enc_ans.h
index 445a5f0c9a..ae4d955a56 100644
--- a/third_party/jpeg-xl/lib/jxl/enc_ans.h
+++ b/third_party/jpeg-xl/lib/jxl/enc_ans.h
@@ -84,7 +84,7 @@ struct EntropyEncodingData {
// Integer to be encoded by an entropy coder, either ANS or Huffman.
struct Token {
- Token() {}
+ Token() = default;
Token(uint32_t c, uint32_t value)
: is_lz77_length(false), context(c), value(value) {}
uint32_t is_lz77_length : 1;
diff --git a/third_party/jpeg-xl/lib/jxl/enc_ans_params.h b/third_party/jpeg-xl/lib/jxl/enc_ans_params.h
index 86664f593e..23921097b4 100644
--- a/third_party/jpeg-xl/lib/jxl/enc_ans_params.h
+++ b/third_party/jpeg-xl/lib/jxl/enc_ans_params.h
@@ -11,10 +11,15 @@
#include <stdint.h>
#include <stdlib.h>
-#include "lib/jxl/enc_params.h"
+#include <vector>
+
+#include "lib/jxl/common.h"
namespace jxl {
+// Forward declaration to break include cycle.
+struct CompressParams;
+
// RebalanceHistogram requires a signed type.
using ANSHistBin = int32_t;
@@ -65,6 +70,10 @@ struct HistogramParams {
}
}
+ static HistogramParams ForModular(
+ const CompressParams& cparams,
+ const std::vector<uint8_t>& extra_dc_precision, bool streaming_mode);
+
ClusteringType clustering = ClusteringType::kBest;
HybridUintMethod uint_method = HybridUintMethod::kBest;
LZ77Method lz77_method = LZ77Method::kRLE;
diff --git a/third_party/jpeg-xl/lib/jxl/enc_ar_control_field.cc b/third_party/jpeg-xl/lib/jxl/enc_ar_control_field.cc
index ed8a42d299..e80771248e 100644
--- a/third_party/jpeg-xl/lib/jxl/enc_ar_control_field.cc
+++ b/third_party/jpeg-xl/lib/jxl/enc_ar_control_field.cc
@@ -17,16 +17,10 @@
#include "lib/jxl/ac_strategy.h"
#include "lib/jxl/base/compiler_specific.h"
-#include "lib/jxl/base/data_parallel.h"
#include "lib/jxl/base/status.h"
-#include "lib/jxl/chroma_from_luma.h"
-#include "lib/jxl/enc_adaptive_quantization.h"
#include "lib/jxl/enc_params.h"
#include "lib/jxl/image.h"
-#include "lib/jxl/image_bundle.h"
#include "lib/jxl/image_ops.h"
-#include "lib/jxl/quant_weights.h"
-#include "lib/jxl/quantizer.h"
HWY_BEFORE_NAMESPACE();
namespace jxl {
@@ -40,11 +34,12 @@ using hwy::HWY_NAMESPACE::Mul;
using hwy::HWY_NAMESPACE::MulAdd;
using hwy::HWY_NAMESPACE::Sqrt;
-void ProcessTile(const CompressParams& cparams, const FrameHeader& frame_header,
- const Image3F& opsin, const Rect& opsin_rect,
- const ImageF& quant_field, const AcStrategyImage& ac_strategy,
- ImageB* epf_sharpness, const Rect& rect,
- ArControlFieldHeuristics::TempImages* temp_image) {
+Status ProcessTile(const CompressParams& cparams,
+ const FrameHeader& frame_header, const Image3F& opsin,
+ const Rect& opsin_rect, const ImageF& quant_field,
+ const AcStrategyImage& ac_strategy, ImageB* epf_sharpness,
+ const Rect& rect,
+ ArControlFieldHeuristics::TempImages* temp_image) {
JXL_ASSERT(opsin_rect.x0() % 8 == 0);
JXL_ASSERT(opsin_rect.y0() % 8 == 0);
JXL_ASSERT(opsin_rect.xsize() % 8 == 0);
@@ -54,7 +49,7 @@ void ProcessTile(const CompressParams& cparams, const FrameHeader& frame_header,
cparams.speed_tier > SpeedTier::kWombat ||
frame_header.loop_filter.epf_iters == 0) {
FillPlane(static_cast<uint8_t>(4), epf_sharpness, rect);
- return;
+ return true;
}
// Likely better to have a higher X weight, like:
@@ -70,7 +65,7 @@ void ProcessTile(const CompressParams& cparams, const FrameHeader& frame_header,
size_t by1 = by0 + rect.ysize();
size_t bx0 = opsin_rect.x0() / 8 + rect.x0();
size_t bx1 = bx0 + rect.xsize();
- temp_image->InitOnce();
+ JXL_RETURN_IF_ERROR(temp_image->InitOnce());
ImageF& laplacian_sqrsum = temp_image->laplacian_sqrsum;
// Calculate the L2 of the 3x3 Laplacian in an integral transform
// (for example 32x32 dct). This relates to transforms ability
@@ -295,6 +290,7 @@ void ProcessTile(const CompressParams& cparams, const FrameHeader& frame_header,
}
}
}
+ return true;
}
} // namespace
@@ -307,14 +303,14 @@ HWY_AFTER_NAMESPACE();
namespace jxl {
HWY_EXPORT(ProcessTile);
-void ArControlFieldHeuristics::RunRect(
+Status ArControlFieldHeuristics::RunRect(
const CompressParams& cparams, const FrameHeader& frame_header,
const Rect& block_rect, const Image3F& opsin, const Rect& opsin_rect,
const ImageF& quant_field, const AcStrategyImage& ac_strategy,
ImageB* epf_sharpness, size_t thread) {
- HWY_DYNAMIC_DISPATCH(ProcessTile)
- (cparams, frame_header, opsin, opsin_rect, quant_field, ac_strategy,
- epf_sharpness, block_rect, &temp_images[thread]);
+ return HWY_DYNAMIC_DISPATCH(ProcessTile)(
+ cparams, frame_header, opsin, opsin_rect, quant_field, ac_strategy,
+ epf_sharpness, block_rect, &temp_images[thread]);
}
} // namespace jxl
diff --git a/third_party/jpeg-xl/lib/jxl/enc_ar_control_field.h b/third_party/jpeg-xl/lib/jxl/enc_ar_control_field.h
index fe602c16e3..f3c5a97a1b 100644
--- a/third_party/jpeg-xl/lib/jxl/enc_ar_control_field.h
+++ b/third_party/jpeg-xl/lib/jxl/enc_ar_control_field.h
@@ -21,11 +21,15 @@ struct PassesEncoderState;
struct ArControlFieldHeuristics {
struct TempImages {
- void InitOnce() {
- if (laplacian_sqrsum.xsize() != 0) return;
- laplacian_sqrsum = ImageF(kEncTileDim + 4, kEncTileDim + 4);
- sqrsum_00 = ImageF(kEncTileDim / 4, kEncTileDim / 4);
- sqrsum_22 = ImageF(kEncTileDim / 4 + 1, kEncTileDim / 4 + 1);
+ Status InitOnce() {
+ if (laplacian_sqrsum.xsize() != 0) return true;
+ JXL_ASSIGN_OR_RETURN(laplacian_sqrsum,
+ ImageF::Create(kEncTileDim + 4, kEncTileDim + 4));
+ JXL_ASSIGN_OR_RETURN(sqrsum_00,
+ ImageF::Create(kEncTileDim / 4, kEncTileDim / 4));
+ JXL_ASSIGN_OR_RETURN(
+ sqrsum_22, ImageF::Create(kEncTileDim / 4 + 1, kEncTileDim / 4 + 1));
+ return true;
}
ImageF laplacian_sqrsum;
@@ -37,11 +41,11 @@ struct ArControlFieldHeuristics {
temp_images.resize(num_threads);
}
- void RunRect(const CompressParams& cparams, const FrameHeader& frame_header,
- const Rect& block_rect, const Image3F& opsin,
- const Rect& opsin_rect, const ImageF& quant_field,
- const AcStrategyImage& ac_strategy, ImageB* epf_sharpness,
- size_t thread);
+ Status RunRect(const CompressParams& cparams, const FrameHeader& frame_header,
+ const Rect& block_rect, const Image3F& opsin,
+ const Rect& opsin_rect, const ImageF& quant_field,
+ const AcStrategyImage& ac_strategy, ImageB* epf_sharpness,
+ size_t thread);
std::vector<TempImages> temp_images;
};
diff --git a/third_party/jpeg-xl/lib/jxl/enc_aux_out.cc b/third_party/jpeg-xl/lib/jxl/enc_aux_out.cc
index 12c8619e91..d529b381f8 100644
--- a/third_party/jpeg-xl/lib/jxl/enc_aux_out.cc
+++ b/third_party/jpeg-xl/lib/jxl/enc_aux_out.cc
@@ -5,16 +5,10 @@
#include "lib/jxl/enc_aux_out.h"
-#ifndef __STDC_FORMAT_MACROS
-#define __STDC_FORMAT_MACROS
-#endif
-
#include <inttypes.h>
#include <stddef.h>
#include <stdint.h>
-#include <algorithm>
-#include <numeric> // accumulate
#include <sstream>
#include "lib/jxl/base/printf_macros.h"
@@ -61,13 +55,11 @@ const char* LayerName(size_t layer) {
void AuxOut::LayerTotals::Print(size_t num_inputs) const {
if (JXL_DEBUG_V_LEVEL > 0) {
- printf("%10" PRId64, static_cast<int64_t>(total_bits));
+ printf("%10" PRIuS, total_bits);
if (histogram_bits != 0) {
- printf(" [c/i:%6.2f | hst:%8" PRId64 " | ex:%8" PRId64
- " | h+c+e:%12.3f",
- num_clustered_histograms * 1.0 / num_inputs,
- static_cast<int64_t>(histogram_bits >> 3),
- static_cast<int64_t>(extra_bits >> 3),
+ printf(" [c/i:%6.2f | hst:%8" PRIuS " | ex:%8" PRIuS " | h+c+e:%12.3f",
+ num_clustered_histograms * 1.0 / num_inputs, histogram_bits >> 3,
+ extra_bits >> 3,
(histogram_bits + clustered_entropy + extra_bits) / 8.0);
printf("]");
}
@@ -99,8 +91,8 @@ void AuxOut::Print(size_t num_inputs) const {
if (num_inputs == 0) return;
LayerTotals all_layers;
- for (size_t i = 0; i < layers.size(); ++i) {
- all_layers.Assimilate(layers[i]);
+ for (const auto& layer : layers) {
+ all_layers.Assimilate(layer);
}
printf("Average butteraugli iters: %10.2f\n",
diff --git a/third_party/jpeg-xl/lib/jxl/enc_bit_writer.cc b/third_party/jpeg-xl/lib/jxl/enc_bit_writer.cc
index a9a86dca3b..6e6e6a353c 100644
--- a/third_party/jpeg-xl/lib/jxl/enc_bit_writer.cc
+++ b/third_party/jpeg-xl/lib/jxl/enc_bit_writer.cc
@@ -5,6 +5,7 @@
#include "lib/jxl/enc_bit_writer.h"
+#include <jxl/types.h>
#include <string.h> // memcpy
#include "lib/jxl/base/byte_order.h"
@@ -43,10 +44,11 @@ void BitWriter::Allotment::FinishedHistogram(BitWriter* JXL_RESTRICT writer) {
void BitWriter::Allotment::ReclaimAndCharge(BitWriter* JXL_RESTRICT writer,
size_t layer,
AuxOut* JXL_RESTRICT aux_out) {
- size_t used_bits = 0, unused_bits = 0;
+ size_t used_bits = 0;
+ size_t unused_bits = 0;
PrivateReclaim(writer, &used_bits, &unused_bits);
-#if 0
+#if JXL_FALSE
printf("Layer %s bits: max %" PRIuS " used %" PRIuS " unused %" PRIuS "\n",
LayerName(layer), MaxBits(), used_bits, unused_bits);
#endif
@@ -77,7 +79,7 @@ void BitWriter::Allotment::PrivateReclaim(BitWriter* JXL_RESTRICT writer,
writer->storage_.resize(writer->storage_.size() - unused_bytes);
writer->current_allotment_ = parent_;
// Ensure we don't also charge the parent for these bits.
- auto parent = parent_;
+ auto* parent = parent_;
while (parent != nullptr) {
parent->prev_bits_written_ += *used_bits;
parent = parent->parent_;
diff --git a/third_party/jpeg-xl/lib/jxl/enc_butteraugli_comparator.cc b/third_party/jpeg-xl/lib/jxl/enc_butteraugli_comparator.cc
index 019d6125a2..b20fa751c1 100644
--- a/third_party/jpeg-xl/lib/jxl/enc_butteraugli_comparator.cc
+++ b/third_party/jpeg-xl/lib/jxl/enc_butteraugli_comparator.cc
@@ -5,9 +5,7 @@
#include "lib/jxl/enc_butteraugli_comparator.h"
-#include <algorithm>
-#include <vector>
-
+#include "lib/jxl/base/status.h"
#include "lib/jxl/enc_image_bundle.h"
namespace jxl {
@@ -24,9 +22,8 @@ Status JxlButteraugliComparator::SetReferenceImage(const ImageBundle& ref) {
/*pool=*/nullptr, &store, &ref_linear_srgb)) {
return false;
}
-
- comparator_.reset(
- new ButteraugliComparator(ref_linear_srgb->color(), params_));
+ JXL_ASSIGN_OR_RETURN(comparator_, ButteraugliComparator::Make(
+ ref_linear_srgb->color(), params_));
xsize_ = ref.xsize();
ysize_ = ref.ysize();
return true;
@@ -34,7 +31,8 @@ Status JxlButteraugliComparator::SetReferenceImage(const ImageBundle& ref) {
Status JxlButteraugliComparator::SetLinearReferenceImage(
const Image3F& linear) {
- comparator_.reset(new ButteraugliComparator(linear, params_));
+ JXL_ASSIGN_OR_RETURN(comparator_,
+ ButteraugliComparator::Make(linear, params_));
xsize_ = linear.xsize();
ysize_ = linear.ysize();
return true;
@@ -58,8 +56,9 @@ Status JxlButteraugliComparator::CompareWith(const ImageBundle& actual,
return false;
}
- ImageF temp_diffmap(xsize_, ysize_);
- comparator_->Diffmap(actual_linear_srgb->color(), temp_diffmap);
+ JXL_ASSIGN_OR_RETURN(ImageF temp_diffmap, ImageF::Create(xsize_, ysize_));
+ JXL_RETURN_IF_ERROR(
+ comparator_->Diffmap(actual_linear_srgb->color(), temp_diffmap));
if (score != nullptr) {
*score = ButteraugliScoreFromDiffmap(temp_diffmap, &params_);
@@ -79,29 +78,4 @@ float JxlButteraugliComparator::BadQualityScore() const {
return ButteraugliFuzzyInverse(0.5);
}
-float ButteraugliDistance(const ImageBundle& rgb0, const ImageBundle& rgb1,
- const ButteraugliParams& params,
- const JxlCmsInterface& cms, ImageF* distmap,
- ThreadPool* pool, bool ignore_alpha) {
- JxlButteraugliComparator comparator(params, cms);
- return ComputeScore(rgb0, rgb1, &comparator, cms, distmap, pool,
- ignore_alpha);
-}
-
-float ButteraugliDistance(const std::vector<ImageBundle>& frames0,
- const std::vector<ImageBundle>& frames1,
- const ButteraugliParams& params,
- const JxlCmsInterface& cms, ImageF* distmap,
- ThreadPool* pool) {
- JxlButteraugliComparator comparator(params, cms);
- JXL_ASSERT(frames0.size() == frames1.size());
- float max_dist = 0.0f;
- for (size_t i = 0; i < frames0.size(); ++i) {
- max_dist = std::max(
- max_dist,
- ComputeScore(frames0[i], frames1[i], &comparator, cms, distmap, pool));
- }
- return max_dist;
-}
-
} // namespace jxl
diff --git a/third_party/jpeg-xl/lib/jxl/enc_butteraugli_comparator.h b/third_party/jpeg-xl/lib/jxl/enc_butteraugli_comparator.h
index 641d7732d5..4f70e21a1b 100644
--- a/third_party/jpeg-xl/lib/jxl/enc_butteraugli_comparator.h
+++ b/third_party/jpeg-xl/lib/jxl/enc_butteraugli_comparator.h
@@ -10,9 +10,7 @@
#include <stddef.h>
#include <memory>
-#include <vector>
-#include "lib/jxl/base/data_parallel.h"
#include "lib/jxl/base/status.h"
#include "lib/jxl/butteraugli/butteraugli.h"
#include "lib/jxl/enc_comparator.h"
@@ -43,20 +41,6 @@ class JxlButteraugliComparator : public Comparator {
size_t ysize_ = 0;
};
-// Returns the butteraugli distance between rgb0 and rgb1.
-// If distmap is not null, it must be the same size as rgb0 and rgb1.
-float ButteraugliDistance(const ImageBundle& rgb0, const ImageBundle& rgb1,
- const ButteraugliParams& params,
- const JxlCmsInterface& cms, ImageF* distmap = nullptr,
- ThreadPool* pool = nullptr,
- bool ignore_alpha = false);
-
-float ButteraugliDistance(const std::vector<ImageBundle>& frames0,
- const std::vector<ImageBundle>& frames1,
- const ButteraugliParams& params,
- const JxlCmsInterface& cms, ImageF* distmap = nullptr,
- ThreadPool* pool = nullptr);
-
} // namespace jxl
#endif // LIB_JXL_ENC_BUTTERAUGLI_COMPARATOR_H_
diff --git a/third_party/jpeg-xl/lib/jxl/enc_cache.cc b/third_party/jpeg-xl/lib/jxl/enc_cache.cc
index ff62c57e4d..200ec83b65 100644
--- a/third_party/jpeg-xl/lib/jxl/enc_cache.cc
+++ b/third_party/jpeg-xl/lib/jxl/enc_cache.cc
@@ -8,15 +8,14 @@
#include <stddef.h>
#include <stdint.h>
-#include <type_traits>
+#include <memory>
-#include "lib/jxl/ac_strategy.h"
#include "lib/jxl/base/common.h"
#include "lib/jxl/base/compiler_specific.h"
#include "lib/jxl/base/span.h"
+#include "lib/jxl/base/status.h"
#include "lib/jxl/color_encoding_internal.h"
#include "lib/jxl/compressed_dc.h"
-#include "lib/jxl/dct_scales.h"
#include "lib/jxl/dct_util.h"
#include "lib/jxl/dec_frame.h"
#include "lib/jxl/enc_aux_out.h"
@@ -50,8 +49,11 @@ Status InitializePassesEncoder(const FrameHeader& frame_header,
for (size_t i = enc_state->coeffs.size();
i < frame_header.passes.num_passes; i++) {
// Allocate enough coefficients for each group on every row.
- enc_state->coeffs.emplace_back(make_unique<ACImageT<int32_t>>(
- kGroupDim * kGroupDim, shared.frame_dim.num_groups));
+ JXL_ASSIGN_OR_RETURN(
+ std::unique_ptr<ACImageT<int32_t>> coeffs,
+ ACImageT<int32_t>::Make(kGroupDim * kGroupDim,
+ shared.frame_dim.num_groups));
+ enc_state->coeffs.emplace_back(std::move(coeffs));
}
}
while (enc_state->coeffs.size() > frame_header.passes.num_passes) {
@@ -65,7 +67,9 @@ Status InitializePassesEncoder(const FrameHeader& frame_header,
shared.quantizer.RecomputeFromGlobalScale();
}
- Image3F dc(shared.frame_dim.xsize_blocks, shared.frame_dim.ysize_blocks);
+ JXL_ASSIGN_OR_RETURN(Image3F dc,
+ Image3F::Create(shared.frame_dim.xsize_blocks,
+ shared.frame_dim.ysize_blocks));
JXL_RETURN_IF_ERROR(RunOnPool(
pool, 0, shared.frame_dim.num_groups, ThreadPool::NoInit,
[&](size_t group_idx, size_t _) {
@@ -90,9 +94,9 @@ Status InitializePassesEncoder(const FrameHeader& frame_header,
// and kModular for the smallest DC (first in the bitstream)
if (cparams.progressive_dc == 0) {
cparams.modular_mode = true;
- cparams.speed_tier =
- SpeedTier(std::max(static_cast<int>(SpeedTier::kTortoise),
- static_cast<int>(cparams.speed_tier) - 1));
+ cparams.speed_tier = static_cast<SpeedTier>(
+ std::max(static_cast<int>(SpeedTier::kTortoise),
+ static_cast<int>(cparams.speed_tier) - 1));
cparams.butteraugli_distance =
std::max(kMinButteraugliDistance,
enc_state->cparams.butteraugli_distance * 0.02f);
@@ -120,7 +124,8 @@ Status InitializePassesEncoder(const FrameHeader& frame_header,
std::vector<ImageF> extra_channels;
extra_channels.reserve(ib.metadata()->extra_channel_info.size());
for (size_t i = 0; i < ib.metadata()->extra_channel_info.size(); i++) {
- extra_channels.emplace_back(ib.xsize(), ib.ysize());
+ JXL_ASSIGN_OR_RETURN(ImageF ch, ImageF::Create(ib.xsize(), ib.ysize()));
+ extra_channels.emplace_back(std::move(ch));
// Must initialize the image with data to not affect blending with
// uninitialized memory.
// TODO(lode): dc_level must copy and use the real extra channels
@@ -168,32 +173,41 @@ Status InitializePassesEncoder(const FrameHeader& frame_header,
// outputs multiple frames, this assumption could be wrong.
const Image3F& dc_frame =
dec_state->shared->dc_frames[frame_header.dc_level];
- shared.dc_storage = Image3F(dc_frame.xsize(), dc_frame.ysize());
+ JXL_ASSIGN_OR_RETURN(shared.dc_storage,
+ Image3F::Create(dc_frame.xsize(), dc_frame.ysize()));
CopyImageTo(dc_frame, &shared.dc_storage);
ZeroFillImage(&shared.quant_dc);
shared.dc = &shared.dc_storage;
JXL_CHECK(encoded_size == 0);
} else {
+ std::atomic<bool> has_error{false};
auto compute_dc_coeffs = [&](int group_index, int /* thread */) {
+ if (has_error) return;
const Rect r = enc_state->shared.frame_dim.DCGroupRect(group_index);
int modular_group_index = group_index;
if (enc_state->streaming_mode) {
JXL_ASSERT(group_index == 0);
modular_group_index = enc_state->dc_group_index;
}
- modular_frame_encoder->AddVarDCTDC(
- frame_header, dc, r, modular_group_index,
- enc_state->cparams.speed_tier < SpeedTier::kFalcon, enc_state,
- /*jpeg_transcode=*/false);
+ if (!modular_frame_encoder->AddVarDCTDC(
+ frame_header, dc, r, modular_group_index,
+ enc_state->cparams.speed_tier < SpeedTier::kFalcon, enc_state,
+ /*jpeg_transcode=*/false)) {
+ has_error = true;
+ return;
+ }
};
JXL_RETURN_IF_ERROR(RunOnPool(pool, 0, shared.frame_dim.num_dc_groups,
ThreadPool::NoInit, compute_dc_coeffs,
"Compute DC coeffs"));
+ if (has_error) return JXL_FAILURE("Compute DC coeffs failed");
// TODO(veluca): this is only useful in tests and if inspection is enabled.
if (!(frame_header.flags & FrameHeader::kSkipAdaptiveDCSmoothing)) {
- AdaptiveDCSmoothing(shared.quantizer.MulDC(), &shared.dc_storage, pool);
+ JXL_RETURN_IF_ERROR(AdaptiveDCSmoothing(shared.quantizer.MulDC(),
+ &shared.dc_storage, pool));
}
}
+ std::atomic<bool> has_error{false};
auto compute_ac_meta = [&](int group_index, int /* thread */) {
const Rect r = enc_state->shared.frame_dim.DCGroupRect(group_index);
int modular_group_index = group_index;
@@ -201,13 +215,17 @@ Status InitializePassesEncoder(const FrameHeader& frame_header,
JXL_ASSERT(group_index == 0);
modular_group_index = enc_state->dc_group_index;
}
- modular_frame_encoder->AddACMetadata(r, modular_group_index,
- /*jpeg_transcode=*/false, enc_state);
+ if (!modular_frame_encoder->AddACMetadata(r, modular_group_index,
+ /*jpeg_transcode=*/false,
+ enc_state)) {
+ has_error = true;
+ return;
+ }
};
JXL_RETURN_IF_ERROR(RunOnPool(pool, 0, shared.frame_dim.num_dc_groups,
ThreadPool::NoInit, compute_ac_meta,
"Compute AC Metadata"));
-
+ if (has_error) return JXL_FAILURE("Compute AC Metadata failed");
return true;
}
diff --git a/third_party/jpeg-xl/lib/jxl/enc_cache.h b/third_party/jpeg-xl/lib/jxl/enc_cache.h
index 6efcc081c1..43ee7bc9c8 100644
--- a/third_party/jpeg-xl/lib/jxl/enc_cache.h
+++ b/third_party/jpeg-xl/lib/jxl/enc_cache.h
@@ -55,7 +55,7 @@ struct PassesEncoderState {
};
std::vector<PassData> passes;
- std::vector<uint8_t> histogram_idx;
+ std::vector<size_t> histogram_idx;
// Block sizes seen so far.
uint32_t used_acs = 0;
diff --git a/third_party/jpeg-xl/lib/jxl/enc_chroma_from_luma.cc b/third_party/jpeg-xl/lib/jxl/enc_chroma_from_luma.cc
index 9a894d89cc..4039da2858 100644
--- a/third_party/jpeg-xl/lib/jxl/enc_chroma_from_luma.cc
+++ b/third_party/jpeg-xl/lib/jxl/enc_chroma_from_luma.cc
@@ -9,7 +9,6 @@
#include <stdlib.h>
#include <algorithm>
-#include <array>
#include <cmath>
#undef HWY_TARGET_INCLUDE
@@ -18,18 +17,13 @@
#include <hwy/foreach_target.h>
#include <hwy/highway.h>
-#include "lib/jxl/base/bits.h"
#include "lib/jxl/base/common.h"
-#include "lib/jxl/base/span.h"
#include "lib/jxl/base/status.h"
#include "lib/jxl/cms/opsin_params.h"
#include "lib/jxl/dec_transforms-inl.h"
#include "lib/jxl/enc_aux_out.h"
#include "lib/jxl/enc_params.h"
#include "lib/jxl/enc_transforms-inl.h"
-#include "lib/jxl/entropy_coder.h"
-#include "lib/jxl/image_ops.h"
-#include "lib/jxl/modular/encoding/encoding.h"
#include "lib/jxl/quantizer.h"
#include "lib/jxl/simd_util.h"
HWY_BEFORE_NAMESPACE();
@@ -149,7 +143,8 @@ int32_t FindBestMultiplier(const float* values_m, const float* values_s,
// Derivatives are approximate due to the high amount of noise in the exact
// derivatives.
for (size_t i = 0; i < 20; i++) {
- float dfpeps, dfmeps;
+ float dfpeps;
+ float dfmeps;
float df = fn.Compute(x, eps, &dfpeps, &dfmeps);
float ddf = (dfpeps - dfmeps) / (2 * eps);
float kExperimentalInsignificantStabilizer = 0.85;
@@ -175,12 +170,13 @@ int32_t FindBestMultiplier(const float* values_m, const float* values_s,
return std::max(-128.0f, std::min(127.0f, roundf(x)));
}
-void InitDCStorage(size_t num_blocks, ImageF* dc_values) {
+Status InitDCStorage(size_t num_blocks, ImageF* dc_values) {
// First row: Y channel
// Second row: X channel
// Third row: Y channel
// Fourth row: B channel
- *dc_values = ImageF(RoundUpTo(num_blocks, Lanes(df)), 4);
+ JXL_ASSIGN_OR_RETURN(*dc_values,
+ ImageF::Create(RoundUpTo(num_blocks, Lanes(df)), 4));
JXL_ASSERT(dc_values->xsize() != 0);
// Zero-fill the last lanes
@@ -190,6 +186,7 @@ void InitDCStorage(size_t num_blocks, ImageF* dc_values) {
dc_values->Row(y)[x] = 0;
}
}
+ return true;
}
void ComputeTile(const Image3F& opsin, const Rect& opsin_rect,
@@ -352,11 +349,11 @@ namespace jxl {
HWY_EXPORT(InitDCStorage);
HWY_EXPORT(ComputeTile);
-void CfLHeuristics::Init(const Rect& rect) {
+Status CfLHeuristics::Init(const Rect& rect) {
size_t xsize_blocks = rect.xsize() / kBlockDim;
size_t ysize_blocks = rect.ysize() / kBlockDim;
- HWY_DYNAMIC_DISPATCH(InitDCStorage)
- (xsize_blocks * ysize_blocks, &dc_values);
+ return HWY_DYNAMIC_DISPATCH(InitDCStorage)(xsize_blocks * ysize_blocks,
+ &dc_values);
}
void CfLHeuristics::ComputeTile(const Rect& r, const Image3F& opsin,
diff --git a/third_party/jpeg-xl/lib/jxl/enc_chroma_from_luma.h b/third_party/jpeg-xl/lib/jxl/enc_chroma_from_luma.h
index 04743842bf..c6481a0ec9 100644
--- a/third_party/jpeg-xl/lib/jxl/enc_chroma_from_luma.h
+++ b/third_party/jpeg-xl/lib/jxl/enc_chroma_from_luma.h
@@ -29,7 +29,7 @@ void ColorCorrelationMapEncodeDC(const ColorCorrelationMap& map,
AuxOut* aux_out);
struct CfLHeuristics {
- void Init(const Rect& rect);
+ Status Init(const Rect& rect);
void PrepareForThreads(size_t num_threads) {
mem = hwy::AllocateAligned<float>(num_threads * ItemsPerThread());
diff --git a/third_party/jpeg-xl/lib/jxl/enc_cluster.cc b/third_party/jpeg-xl/lib/jxl/enc_cluster.cc
index df1b31ddf7..f7284331d9 100644
--- a/third_party/jpeg-xl/lib/jxl/enc_cluster.cc
+++ b/third_party/jpeg-xl/lib/jxl/enc_cluster.cc
@@ -236,7 +236,7 @@ void HistogramReindex(std::vector<Histogram>* out, size_t prev_histograms,
// Clusters similar histograms in 'in' together, the selected histograms are
// placed in 'out', and for each index in 'in', *histogram_symbols will
// indicate which of the 'out' histograms is the best approximation.
-void ClusterHistograms(const HistogramParams params,
+void ClusterHistograms(const HistogramParams& params,
const std::vector<Histogram>& in, size_t max_histograms,
std::vector<Histogram>* out,
std::vector<uint32_t>* histogram_symbols) {
@@ -252,9 +252,9 @@ void ClusterHistograms(const HistogramParams params,
if (prev_histograms == 0 &&
params.clustering == HistogramParams::ClusteringType::kBest) {
- for (size_t i = 0; i < out->size(); i++) {
- (*out)[i].entropy_ =
- ANSPopulationCost((*out)[i].data_.data(), (*out)[i].data_.size());
+ for (auto& histo : *out) {
+ histo.entropy_ =
+ ANSPopulationCost(histo.data_.data(), histo.data_.size());
}
uint32_t next_version = 2;
std::vector<uint32_t> version(out->size(), 1);
@@ -308,9 +308,9 @@ void ClusterHistograms(const HistogramParams params,
(*out)[first].AddHistogram((*out)[second]);
(*out)[first].entropy_ = ANSPopulationCost((*out)[first].data_.data(),
(*out)[first].data_.size());
- for (size_t i = 0; i < renumbering.size(); i++) {
- if (renumbering[i] == second) {
- renumbering[i] = first;
+ for (uint32_t& item : renumbering) {
+ if (item == second) {
+ item = first;
}
}
version[second] = 0;
@@ -338,9 +338,8 @@ void ClusterHistograms(const HistogramParams params,
reverse_renumbering[i] = num_alive - 1;
}
out->resize(num_alive);
- for (size_t i = 0; i < histogram_symbols->size(); i++) {
- (*histogram_symbols)[i] =
- reverse_renumbering[renumbering[(*histogram_symbols)[i]]];
+ for (uint32_t& item : *histogram_symbols) {
+ item = reverse_renumbering[renumbering[item]];
}
}
diff --git a/third_party/jpeg-xl/lib/jxl/enc_cluster.h b/third_party/jpeg-xl/lib/jxl/enc_cluster.h
index 923aaaccfe..7ddc003cf2 100644
--- a/third_party/jpeg-xl/lib/jxl/enc_cluster.h
+++ b/third_party/jpeg-xl/lib/jxl/enc_cluster.h
@@ -15,6 +15,7 @@
#include <vector>
#include "lib/jxl/ans_params.h"
+#include "lib/jxl/base/common.h"
#include "lib/jxl/enc_ans_params.h"
namespace jxl {
@@ -61,8 +62,9 @@ struct Histogram {
static constexpr size_t kRounding = 8;
};
-void ClusterHistograms(HistogramParams params, const std::vector<Histogram>& in,
- size_t max_histograms, std::vector<Histogram>* out,
+void ClusterHistograms(const HistogramParams& params,
+ const std::vector<Histogram>& in, size_t max_histograms,
+ std::vector<Histogram>* out,
std::vector<uint32_t>* histogram_symbols);
} // namespace jxl
diff --git a/third_party/jpeg-xl/lib/jxl/enc_coeff_order.cc b/third_party/jpeg-xl/lib/jxl/enc_coeff_order.cc
index 5be012aa92..49129f5d9e 100644
--- a/third_party/jpeg-xl/lib/jxl/enc_coeff_order.cc
+++ b/third_party/jpeg-xl/lib/jxl/enc_coeff_order.cc
@@ -6,21 +6,16 @@
#include <stdint.h>
#include <algorithm>
+#include <cmath>
#include <hwy/aligned_allocator.h>
#include <vector>
-#include "lib/jxl/ans_params.h"
-#include "lib/jxl/base/span.h"
#include "lib/jxl/coeff_order.h"
#include "lib/jxl/coeff_order_fwd.h"
-#include "lib/jxl/dec_ans.h"
-#include "lib/jxl/dec_bit_reader.h"
+#include "lib/jxl/dct_util.h"
#include "lib/jxl/enc_ans.h"
#include "lib/jxl/enc_bit_writer.h"
-#include "lib/jxl/entropy_coder.h"
#include "lib/jxl/lehmer_code.h"
-#include "lib/jxl/modular/encoding/encoding.h"
-#include "lib/jxl/modular/modular_image.h"
namespace jxl {
@@ -240,7 +235,7 @@ void EncodePermutation(const coeff_order_t* JXL_RESTRICT order, size_t skip,
size_t size, BitWriter* writer, int layer,
AuxOut* aux_out) {
std::vector<std::vector<Token>> tokens(1);
- TokenizePermutation(order, skip, size, &tokens[0]);
+ TokenizePermutation(order, skip, size, tokens.data());
std::vector<uint8_t> context_map;
EntropyEncodingData codes;
BuildAndEncodeHistograms(HistogramParams(), kPermutationContexts, tokens,
@@ -280,7 +275,7 @@ void EncodeCoeffOrders(uint16_t used_orders,
if (natural_order_lut.size() < size) natural_order_lut.resize(size);
acs.ComputeNaturalCoeffOrderLut(natural_order_lut.data());
for (size_t c = 0; c < 3; c++) {
- EncodeCoeffOrder(&order[CoeffOrderOffset(ord, c)], acs, &tokens[0],
+ EncodeCoeffOrder(&order[CoeffOrderOffset(ord, c)], acs, tokens.data(),
mem.get(), natural_order_lut);
}
}
diff --git a/third_party/jpeg-xl/lib/jxl/enc_comparator.cc b/third_party/jpeg-xl/lib/jxl/enc_comparator.cc
index 268122af06..3ef6a49b8f 100644
--- a/third_party/jpeg-xl/lib/jxl/enc_comparator.cc
+++ b/third_party/jpeg-xl/lib/jxl/enc_comparator.cc
@@ -66,9 +66,10 @@ float ComputeScoreImpl(const ImageBundle& rgb0, const ImageBundle& rgb1,
} // namespace
-float ComputeScore(const ImageBundle& rgb0, const ImageBundle& rgb1,
- Comparator* comparator, const JxlCmsInterface& cms,
- ImageF* diffmap, ThreadPool* pool, bool ignore_alpha) {
+Status ComputeScore(const ImageBundle& rgb0, const ImageBundle& rgb1,
+ Comparator* comparator, const JxlCmsInterface& cms,
+ float* score, ImageF* diffmap, ThreadPool* pool,
+ bool ignore_alpha) {
// Convert to linear sRGB (unless already in that space)
ImageMetadata metadata0 = *rgb0.metadata();
ImageBundle store0(&metadata0);
@@ -83,25 +84,28 @@ float ComputeScore(const ImageBundle& rgb0, const ImageBundle& rgb1,
// No alpha: skip blending, only need a single call to Butteraugli.
if (ignore_alpha || (!rgb0.HasAlpha() && !rgb1.HasAlpha())) {
- return ComputeScoreImpl(*linear_srgb0, *linear_srgb1, comparator, diffmap);
+ *score =
+ ComputeScoreImpl(*linear_srgb0, *linear_srgb1, comparator, diffmap);
+ return true;
}
// Blend on black and white backgrounds
const float black = 0.0f;
- ImageBundle blended_black0 = linear_srgb0->Copy();
- ImageBundle blended_black1 = linear_srgb1->Copy();
+ JXL_ASSIGN_OR_RETURN(ImageBundle blended_black0, linear_srgb0->Copy());
+ JXL_ASSIGN_OR_RETURN(ImageBundle blended_black1, linear_srgb1->Copy());
AlphaBlend(black, &blended_black0);
AlphaBlend(black, &blended_black1);
const float white = 1.0f;
- ImageBundle blended_white0 = linear_srgb0->Copy();
- ImageBundle blended_white1 = linear_srgb1->Copy();
+ JXL_ASSIGN_OR_RETURN(ImageBundle blended_white0, linear_srgb0->Copy());
+ JXL_ASSIGN_OR_RETURN(ImageBundle blended_white1, linear_srgb1->Copy());
AlphaBlend(white, &blended_white0);
AlphaBlend(white, &blended_white1);
- ImageF diffmap_black, diffmap_white;
+ ImageF diffmap_black;
+ ImageF diffmap_white;
const float dist_black = ComputeScoreImpl(blended_black0, blended_black1,
comparator, &diffmap_black);
const float dist_white = ComputeScoreImpl(blended_white0, blended_white1,
@@ -111,7 +115,7 @@ float ComputeScore(const ImageBundle& rgb0, const ImageBundle& rgb1,
if (diffmap != nullptr) {
const size_t xsize = rgb0.xsize();
const size_t ysize = rgb0.ysize();
- *diffmap = ImageF(xsize, ysize);
+ JXL_ASSIGN_OR_RETURN(*diffmap, ImageF::Create(xsize, ysize));
for (size_t y = 0; y < ysize; ++y) {
const float* JXL_RESTRICT row_black = diffmap_black.ConstRow(y);
const float* JXL_RESTRICT row_white = diffmap_white.ConstRow(y);
@@ -121,7 +125,8 @@ float ComputeScore(const ImageBundle& rgb0, const ImageBundle& rgb1,
}
}
}
- return std::max(dist_black, dist_white);
+ *score = std::max(dist_black, dist_white);
+ return true;
}
} // namespace jxl
diff --git a/third_party/jpeg-xl/lib/jxl/enc_comparator.h b/third_party/jpeg-xl/lib/jxl/enc_comparator.h
index c545ea6111..ee62ab6f28 100644
--- a/third_party/jpeg-xl/lib/jxl/enc_comparator.h
+++ b/third_party/jpeg-xl/lib/jxl/enc_comparator.h
@@ -43,10 +43,10 @@ class Comparator {
// Computes the score given images in any RGB color model, optionally with
// alpha channel.
-float ComputeScore(const ImageBundle& rgb0, const ImageBundle& rgb1,
- Comparator* comparator, const JxlCmsInterface& cms,
- ImageF* diffmap = nullptr, ThreadPool* pool = nullptr,
- bool ignore_alpha = false);
+Status ComputeScore(const ImageBundle& rgb0, const ImageBundle& rgb1,
+ Comparator* comparator, const JxlCmsInterface& cms,
+ float* score, ImageF* diffmap = nullptr,
+ ThreadPool* pool = nullptr, bool ignore_alpha = false);
} // namespace jxl
diff --git a/third_party/jpeg-xl/lib/jxl/enc_context_map.cc b/third_party/jpeg-xl/lib/jxl/enc_context_map.cc
index 6968a6fbae..36efc4e649 100644
--- a/third_party/jpeg-xl/lib/jxl/enc_context_map.cc
+++ b/third_party/jpeg-xl/lib/jxl/enc_context_map.cc
@@ -7,6 +7,7 @@
#include "lib/jxl/enc_context_map.h"
+#include <jxl/types.h>
#include <stdint.h>
#include <algorithm>
@@ -18,6 +19,7 @@
#include "lib/jxl/enc_ans.h"
#include "lib/jxl/enc_aux_out.h"
#include "lib/jxl/entropy_coder.h"
+#include "lib/jxl/fields.h"
#include "lib/jxl/pack_signed.h"
namespace jxl {
@@ -69,16 +71,18 @@ void EncodeContextMap(const std::vector<uint8_t>& context_map,
}
std::vector<uint8_t> transformed_symbols = MoveToFrontTransform(context_map);
- std::vector<std::vector<Token>> tokens(1), mtf_tokens(1);
- for (size_t i = 0; i < context_map.size(); i++) {
- tokens[0].emplace_back(0, context_map[i]);
+ std::vector<std::vector<Token>> tokens(1);
+ std::vector<std::vector<Token>> mtf_tokens(1);
+ for (const uint8_t& ctx : context_map) {
+ tokens[0].emplace_back(0, ctx);
}
- for (size_t i = 0; i < transformed_symbols.size(); i++) {
- mtf_tokens[0].emplace_back(0, transformed_symbols[i]);
+ for (const uint8_t& sym : transformed_symbols) {
+ mtf_tokens[0].emplace_back(0, sym);
}
HistogramParams params;
params.uint_method = HistogramParams::HybridUintMethod::kContextMap;
- size_t ans_cost, mtf_cost;
+ size_t ans_cost;
+ size_t mtf_cost;
{
EntropyEncodingData codes;
std::vector<uint8_t> sink_context_map;
@@ -104,14 +108,14 @@ void EncodeContextMap(const std::vector<uint8_t>& context_map,
BitWriter::Allotment allotment(writer, 3 + entry_bits * context_map.size());
writer->Write(1, 1);
writer->Write(2, entry_bits);
- for (size_t i = 0; i < context_map.size(); i++) {
- writer->Write(entry_bits, context_map[i]);
+ for (uint8_t entry : context_map) {
+ writer->Write(entry_bits, entry);
}
allotment.ReclaimAndCharge(writer, layer, aux_out);
} else {
BitWriter::Allotment allotment(writer, 2 + tokens[0].size() * 24);
writer->Write(1, 0);
- writer->Write(1, use_mtf); // Use/don't use MTF.
+ writer->Write(1, TO_JXL_BOOL(use_mtf)); // Use/don't use MTF.
EntropyEncodingData codes;
std::vector<uint8_t> sink_context_map;
BuildAndEncodeHistograms(params, 1, tokens, &codes, &sink_context_map,
@@ -123,9 +127,9 @@ void EncodeContextMap(const std::vector<uint8_t>& context_map,
void EncodeBlockCtxMap(const BlockCtxMap& block_ctx_map, BitWriter* writer,
AuxOut* aux_out) {
- auto& dct = block_ctx_map.dc_thresholds;
- auto& qft = block_ctx_map.qf_thresholds;
- auto& ctx_map = block_ctx_map.ctx_map;
+ const auto& dct = block_ctx_map.dc_thresholds;
+ const auto& qft = block_ctx_map.qf_thresholds;
+ const auto& ctx_map = block_ctx_map.ctx_map;
BitWriter::Allotment allotment(
writer,
(dct[0].size() + dct[1].size() + dct[2].size() + qft.size()) * 34 + 1 +
diff --git a/third_party/jpeg-xl/lib/jxl/enc_debug_image.cc b/third_party/jpeg-xl/lib/jxl/enc_debug_image.cc
index 261570e690..d67ab7db46 100644
--- a/third_party/jpeg-xl/lib/jxl/enc_debug_image.cc
+++ b/third_party/jpeg-xl/lib/jxl/enc_debug_image.cc
@@ -18,32 +18,29 @@ namespace jxl {
namespace {
template <typename From>
-Plane<float> ConvertToFloat(const Plane<From>& from) {
+StatusOr<Image3F> ConvertToFloat(const Image3<From>& from) {
float factor = 1.0f / std::numeric_limits<From>::max();
if (std::is_same<From, double>::value || std::is_same<From, float>::value) {
factor = 1.0f;
}
- Plane<float> to(from.xsize(), from.ysize());
- for (size_t y = 0; y < from.ysize(); ++y) {
- const From* const JXL_RESTRICT row_from = from.Row(y);
- float* const JXL_RESTRICT row_to = to.Row(y);
- for (size_t x = 0; x < from.xsize(); ++x) {
- row_to[x] = row_from[x] * factor;
+ JXL_ASSIGN_OR_RETURN(Image3F to, Image3F::Create(from.xsize(), from.ysize()));
+ for (size_t c = 0; c < 3; ++c) {
+ for (size_t y = 0; y < from.ysize(); ++y) {
+ const From* const JXL_RESTRICT row_from = from.ConstPlaneRow(c, y);
+ float* const JXL_RESTRICT row_to = to.PlaneRow(c, y);
+ for (size_t x = 0; x < from.xsize(); ++x) {
+ row_to[x] = row_from[x] * factor;
+ }
}
}
return to;
}
-template <typename From>
-Image3F ConvertToFloat(const Image3<From>& from) {
- return Image3F(ConvertToFloat(from.Plane(0)), ConvertToFloat(from.Plane(1)),
- ConvertToFloat(from.Plane(2)));
-}
template <typename T>
-void DumpImageT(const CompressParams& cparams, const char* label,
- const ColorEncoding& color_encoding, const Image3<T>& image) {
- if (!cparams.debug_image) return;
- Image3F float_image = ConvertToFloat(image);
+Status DumpImageT(const CompressParams& cparams, const char* label,
+ const ColorEncoding& color_encoding, const Image3<T>& image) {
+ if (!cparams.debug_image) return true;
+ JXL_ASSIGN_OR_RETURN(Image3F float_image, ConvertToFloat(image));
JxlColorEncoding color = color_encoding.ToExternal();
size_t num_pixels = 3 * image.xsize() * image.ysize();
std::vector<uint16_t> pixels(num_pixels);
@@ -53,18 +50,20 @@ void DumpImageT(const CompressParams& cparams, const char* label,
}
JXL_CHECK(ConvertChannelsToExternal(
channels, 3, 16, false, JXL_BIG_ENDIAN, 6 * image.xsize(), nullptr,
- &pixels[0], 2 * num_pixels, PixelCallback(), Orientation::kIdentity));
+ pixels.data(), 2 * num_pixels, PixelCallback(), Orientation::kIdentity));
(*cparams.debug_image)(cparams.debug_image_opaque, label, image.xsize(),
- image.ysize(), &color, &pixels[0]);
+ image.ysize(), &color, pixels.data());
+ return true;
}
template <typename T>
-void DumpPlaneNormalizedT(const CompressParams& cparams, const char* label,
- const Plane<T>& image) {
+Status DumpPlaneNormalizedT(const CompressParams& cparams, const char* label,
+ const Plane<T>& image) {
T min;
T max;
ImageMinMax(image, &min, &max);
- Image3B normalized(image.xsize(), image.ysize());
+ JXL_ASSIGN_OR_RETURN(Image3B normalized,
+ Image3B::Create(image.xsize(), image.ysize()));
for (size_t c = 0; c < 3; ++c) {
float mul = min == max ? 0 : (255.0f / (max - min));
for (size_t y = 0; y < image.ysize(); ++y) {
@@ -75,41 +74,42 @@ void DumpPlaneNormalizedT(const CompressParams& cparams, const char* label,
}
}
}
- DumpImageT(cparams, label, ColorEncoding::SRGB(), normalized);
+ return DumpImageT(cparams, label, ColorEncoding::SRGB(), normalized);
}
} // namespace
-void DumpImage(const CompressParams& cparams, const char* label,
- const Image3<float>& image) {
- DumpImageT(cparams, label, ColorEncoding::SRGB(), image);
+Status DumpImage(const CompressParams& cparams, const char* label,
+ const Image3<float>& image) {
+ return DumpImageT(cparams, label, ColorEncoding::SRGB(), image);
}
-void DumpImage(const CompressParams& cparams, const char* label,
- const Image3<uint8_t>& image) {
- DumpImageT(cparams, label, ColorEncoding::SRGB(), image);
+Status DumpImage(const CompressParams& cparams, const char* label,
+ const Image3<uint8_t>& image) {
+ return DumpImageT(cparams, label, ColorEncoding::SRGB(), image);
}
-void DumpXybImage(const CompressParams& cparams, const char* label,
- const Image3F& image) {
- if (!cparams.debug_image) return;
+Status DumpXybImage(const CompressParams& cparams, const char* label,
+ const Image3F& image) {
+ if (!cparams.debug_image) return true;
- Image3F linear(image.xsize(), image.ysize());
+ JXL_ASSIGN_OR_RETURN(Image3F linear,
+ Image3F::Create(image.xsize(), image.ysize()));
OpsinParams opsin_params;
opsin_params.Init(kDefaultIntensityTarget);
OpsinToLinear(image, Rect(linear), nullptr, &linear, opsin_params);
- DumpImageT(cparams, label, ColorEncoding::LinearSRGB(), linear);
+ return DumpImageT(cparams, label, ColorEncoding::LinearSRGB(), linear);
}
-void DumpPlaneNormalized(const CompressParams& cparams, const char* label,
- const Plane<float>& image) {
- DumpPlaneNormalizedT(cparams, label, image);
+Status DumpPlaneNormalized(const CompressParams& cparams, const char* label,
+ const Plane<float>& image) {
+ return DumpPlaneNormalizedT(cparams, label, image);
}
-void DumpPlaneNormalized(const CompressParams& cparams, const char* label,
- const Plane<uint8_t>& image) {
- DumpPlaneNormalizedT(cparams, label, image);
+Status DumpPlaneNormalized(const CompressParams& cparams, const char* label,
+ const Plane<uint8_t>& image) {
+ return DumpPlaneNormalizedT(cparams, label, image);
}
} // namespace jxl
diff --git a/third_party/jpeg-xl/lib/jxl/enc_debug_image.h b/third_party/jpeg-xl/lib/jxl/enc_debug_image.h
index 33799a5f7f..428293f8bc 100644
--- a/third_party/jpeg-xl/lib/jxl/enc_debug_image.h
+++ b/third_party/jpeg-xl/lib/jxl/enc_debug_image.h
@@ -16,16 +16,16 @@
namespace jxl {
-void DumpImage(const CompressParams& cparams, const char* label,
- const Image3<float>& image);
-void DumpImage(const CompressParams& cparams, const char* label,
- const Image3<uint8_t>& image);
-void DumpXybImage(const CompressParams& cparams, const char* label,
- const Image3<float>& image);
-void DumpPlaneNormalized(const CompressParams& cparams, const char* label,
- const Plane<float>& image);
-void DumpPlaneNormalized(const CompressParams& cparams, const char* label,
- const Plane<uint8_t>& image);
+Status DumpImage(const CompressParams& cparams, const char* label,
+ const Image3<float>& image);
+Status DumpImage(const CompressParams& cparams, const char* label,
+ const Image3<uint8_t>& image);
+Status DumpXybImage(const CompressParams& cparams, const char* label,
+ const Image3<float>& image);
+Status DumpPlaneNormalized(const CompressParams& cparams, const char* label,
+ const Plane<float>& image);
+Status DumpPlaneNormalized(const CompressParams& cparams, const char* label,
+ const Plane<uint8_t>& image);
// Used to skip image creation if they won't be written to debug directory.
static inline bool WantDebugOutput(const CompressParams& cparams) {
diff --git a/third_party/jpeg-xl/lib/jxl/enc_detect_dots.cc b/third_party/jpeg-xl/lib/jxl/enc_detect_dots.cc
index 4ee8808766..94e6fefb61 100644
--- a/third_party/jpeg-xl/lib/jxl/enc_detect_dots.cc
+++ b/third_party/jpeg-xl/lib/jxl/enc_detect_dots.cc
@@ -26,7 +26,6 @@
#include "lib/jxl/base/status.h"
#include "lib/jxl/convolve.h"
#include "lib/jxl/enc_linalg.h"
-#include "lib/jxl/enc_optimize.h"
#include "lib/jxl/image.h"
#include "lib/jxl/image_ops.h"
@@ -44,14 +43,16 @@ using hwy::HWY_NAMESPACE::Add;
using hwy::HWY_NAMESPACE::Mul;
using hwy::HWY_NAMESPACE::Sub;
-ImageF SumOfSquareDifferences(const Image3F& forig, const Image3F& smooth,
- ThreadPool* pool) {
+StatusOr<ImageF> SumOfSquareDifferences(const Image3F& forig,
+ const Image3F& smooth,
+ ThreadPool* pool) {
const HWY_FULL(float) d;
const auto color_coef0 = Set(d, 0.0f);
const auto color_coef1 = Set(d, 10.0f);
const auto color_coef2 = Set(d, 0.0f);
- ImageF sum_of_squares(forig.xsize(), forig.ysize());
+ JXL_ASSIGN_OR_RETURN(ImageF sum_of_squares,
+ ImageF::Create(forig.xsize(), forig.ysize()));
JXL_CHECK(RunOnPool(
pool, 0, forig.ysize(), ThreadPool::NoInit,
[&](const uint32_t task, size_t thread) {
@@ -142,11 +143,12 @@ const WeightsSeparable5& WeightsSeparable5Gaussian3() {
return weights;
}
-ImageF ComputeEnergyImage(const Image3F& orig, Image3F* smooth,
- ThreadPool* pool) {
+StatusOr<ImageF> ComputeEnergyImage(const Image3F& orig, Image3F* smooth,
+ ThreadPool* pool) {
// Prepare guidance images for dot selection.
- Image3F forig(orig.xsize(), orig.ysize());
- *smooth = Image3F(orig.xsize(), orig.ysize());
+ JXL_ASSIGN_OR_RETURN(Image3F forig,
+ Image3F::Create(orig.xsize(), orig.ysize()));
+ JXL_ASSIGN_OR_RETURN(*smooth, Image3F::Create(orig.xsize(), orig.ysize()));
Rect rect(orig);
const auto& weights1 = WeightsSeparable5Gaussian0_65();
@@ -176,7 +178,7 @@ const size_t kMaxCCSize = 1000;
// Extracts a connected component from a Binary image where seed is part
// of the component
-bool ExtractComponent(ImageF* img, std::vector<Pixel>* pixels,
+bool ExtractComponent(const Rect& rect, ImageF* img, std::vector<Pixel>* pixels,
const Pixel& seed, double threshold) {
static const std::vector<Pixel> neighbors{{1, -1}, {1, 0}, {1, 1}, {0, -1},
{0, 1}, {-1, -1}, {-1, 1}, {1, 0}};
@@ -188,9 +190,9 @@ bool ExtractComponent(ImageF* img, std::vector<Pixel>* pixels,
if (pixels->size() > kMaxCCSize) return false;
for (const Pixel& delta : neighbors) {
Pixel child = current + delta;
- if (child.x >= 0 && static_cast<size_t>(child.x) < img->xsize() &&
- child.y >= 0 && static_cast<size_t>(child.y) < img->ysize()) {
- float* value = &img->Row(child.y)[child.x];
+ if (child.x >= 0 && static_cast<size_t>(child.x) < rect.xsize() &&
+ child.y >= 0 && static_cast<size_t>(child.y) < rect.ysize()) {
+ float* value = &rect.Row(img, child.y)[child.x];
if (*value > threshold) {
*value = 0.0;
q.push_back(child);
@@ -221,7 +223,7 @@ struct ConnectedComponent {
float score;
Pixel mode;
- void CompStats(const ImageF& energy, int extra) {
+ void CompStats(const ImageF& energy, const Rect& rect, int extra) {
maxEnergy = 0.0;
meanEnergy = 0.0;
varEnergy = 0.0;
@@ -234,12 +236,12 @@ struct ConnectedComponent {
for (int sy = -extra; sy < (static_cast<int>(bounds.ysize()) + extra);
sy++) {
int y = sy + static_cast<int>(bounds.y0());
- if (y < 0 || static_cast<size_t>(y) >= energy.ysize()) continue;
- const float* JXL_RESTRICT erow = energy.ConstRow(y);
+ if (y < 0 || static_cast<size_t>(y) >= rect.ysize()) continue;
+ const float* JXL_RESTRICT erow = rect.ConstRow(energy, y);
for (int sx = -extra; sx < (static_cast<int>(bounds.xsize()) + extra);
sx++) {
int x = sx + static_cast<int>(bounds.x0());
- if (x < 0 || static_cast<size_t>(x) >= energy.xsize()) continue;
+ if (x < 0 || static_cast<size_t>(x) >= rect.xsize()) continue;
if (erow[x] > maxEnergy) {
maxEnergy = erow[x];
mode.x = x;
@@ -266,7 +268,10 @@ struct ConnectedComponent {
Rect BoundingRectangle(const std::vector<Pixel>& pixels) {
JXL_ASSERT(!pixels.empty());
- int low_x, high_x, low_y, high_y;
+ int low_x;
+ int high_x;
+ int low_y;
+ int high_y;
low_x = high_x = pixels[0].x;
low_y = high_y = pixels[0].y;
for (const Pixel& p : pixels) {
@@ -278,22 +283,25 @@ Rect BoundingRectangle(const std::vector<Pixel>& pixels) {
return Rect(low_x, low_y, high_x - low_x + 1, high_y - low_y + 1);
}
-std::vector<ConnectedComponent> FindCC(const ImageF& energy, double t_low,
- double t_high, uint32_t maxWindow,
- double minScore) {
+StatusOr<std::vector<ConnectedComponent>> FindCC(const ImageF& energy,
+ const Rect& rect, double t_low,
+ double t_high,
+ uint32_t maxWindow,
+ double minScore) {
const int kExtraRect = 4;
- ImageF img(energy.xsize(), energy.ysize());
+ JXL_ASSIGN_OR_RETURN(ImageF img,
+ ImageF::Create(energy.xsize(), energy.ysize()));
CopyImageTo(energy, &img);
std::vector<ConnectedComponent> ans;
- for (size_t y = 0; y < img.ysize(); y++) {
- float* JXL_RESTRICT row = img.Row(y);
- for (size_t x = 0; x < img.xsize(); x++) {
+ for (size_t y = 0; y < rect.ysize(); y++) {
+ float* JXL_RESTRICT row = rect.Row(&img, y);
+ for (size_t x = 0; x < rect.xsize(); x++) {
if (row[x] > t_high) {
std::vector<Pixel> pixels;
row[x] = 0.0;
bool success = ExtractComponent(
- &img, &pixels, Pixel{static_cast<int>(x), static_cast<int>(y)},
- t_low);
+ rect, &img, &pixels,
+ Pixel{static_cast<int>(x), static_cast<int>(y)}, t_low);
if (!success) continue;
#if JXL_DEBUG_DOT_DETECT
for (size_t i = 0; i < pixels.size(); i++) {
@@ -304,7 +312,7 @@ std::vector<ConnectedComponent> FindCC(const ImageF& energy, double t_low,
Rect bounds = BoundingRectangle(pixels);
if (bounds.xsize() < maxWindow && bounds.ysize() < maxWindow) {
ConnectedComponent cc{bounds, std::move(pixels)};
- cc.CompStats(energy, kExtraRect);
+ cc.CompStats(energy, rect, kExtraRect);
if (cc.score < minScore) continue;
JXL_DEBUG(JXL_DEBUG_DOT_DETECT,
"cc mode: (%d,%d), max: %f, bgMean: %f bgVar: "
@@ -323,12 +331,14 @@ std::vector<ConnectedComponent> FindCC(const ImageF& energy, double t_low,
// TODO(sggonzalez): Adapt this function for the different color spaces or
// remove it if the color space with the best performance does not need it
void ComputeDotLosses(GaussianEllipse* ellipse, const ConnectedComponent& cc,
- const Image3F& img, const Image3F& background) {
+ const Rect& rect, const Image3F& img,
+ const Image3F& background) {
const int rectBounds = 2;
const double kIntensityR = 0.0; // 0.015;
const double kSigmaR = 0.0; // 0.01;
const double kZeroEpsilon = 0.1; // Tolerance to consider a value negative
- double ct = cos(ellipse->angle), st = sin(ellipse->angle);
+ double ct = cos(ellipse->angle);
+ double st = sin(ellipse->angle);
const std::array<double, 3> channelGains{{1.0, 1.0, 1.0}};
int N = 0;
ellipse->l1_loss = 0.0;
@@ -342,15 +352,15 @@ void ComputeDotLosses(GaussianEllipse* ellipse, const ConnectedComponent& cc,
for (int sy = -rectBounds;
sy < (static_cast<int>(cc.bounds.ysize()) + rectBounds); sy++) {
int y = sy + cc.bounds.y0();
- if (y < 0 || static_cast<size_t>(y) >= img.ysize()) continue;
- const float* JXL_RESTRICT row = img.ConstPlaneRow(c, y);
+ if (y < 0 || static_cast<size_t>(y) >= rect.ysize()) continue;
+ const float* JXL_RESTRICT row = rect.ConstPlaneRow(img, c, y);
// bgrow is only used if kOptimizeBackground is false.
// NOLINTNEXTLINE(clang-analyzer-deadcode.DeadStores)
- const float* JXL_RESTRICT bgrow = background.ConstPlaneRow(c, y);
+ const float* JXL_RESTRICT bgrow = rect.ConstPlaneRow(background, c, y);
for (int sx = -rectBounds;
sx < (static_cast<int>(cc.bounds.xsize()) + rectBounds); sx++) {
int x = sx + cc.bounds.x0();
- if (x < 0 || static_cast<size_t>(x) >= img.xsize()) continue;
+ if (x < 0 || static_cast<size_t>(x) >= rect.xsize()) continue;
double target = row[x];
double dotDelta = DotGaussianModel(
x - ellipse->x, y - ellipse->y, ct, st, ellipse->sigma_x,
@@ -385,9 +395,8 @@ void ComputeDotLosses(GaussianEllipse* ellipse, const ConnectedComponent& cc,
ellipse->ridge_loss = ellipse->l2_loss + ridgeTerm;
}
-GaussianEllipse FitGaussianFast(const ConnectedComponent& cc,
- const ImageF& energy, const Image3F& img,
- const Image3F& background) {
+GaussianEllipse FitGaussianFast(const ConnectedComponent& cc, const Rect& rect,
+ const Image3F& img, const Image3F& background) {
constexpr bool leastSqIntensity = true;
constexpr double kEpsilon = 1e-6;
GaussianEllipse ans;
@@ -405,18 +414,18 @@ GaussianEllipse FitGaussianFast(const ConnectedComponent& cc,
"%" PRIuS " %" PRIuS " %" PRIuS " %" PRIuS "\n", cc.bounds.x0(),
cc.bounds.y0(), cc.bounds.xsize(), cc.bounds.ysize());
for (int c = 0; c < 3; c++) {
- color[c] = img.ConstPlaneRow(c, cc.mode.y)[cc.mode.x] -
- background.ConstPlaneRow(c, cc.mode.y)[cc.mode.x];
+ color[c] = rect.ConstPlaneRow(img, c, cc.mode.y)[cc.mode.x] -
+ rect.ConstPlaneRow(background, c, cc.mode.y)[cc.mode.x];
}
double sign = (color[1] > 0) ? 1 : -1;
for (int sy = -kRectBounds; sy <= kRectBounds; sy++) {
int y = sy + cc.mode.y;
- if (y < 0 || static_cast<size_t>(y) >= energy.ysize()) continue;
- const float* JXL_RESTRICT row = img.ConstPlaneRow(1, y);
- const float* JXL_RESTRICT bgrow = background.ConstPlaneRow(1, y);
+ if (y < 0 || static_cast<size_t>(y) >= rect.ysize()) continue;
+ const float* JXL_RESTRICT row = rect.ConstPlaneRow(img, 1, y);
+ const float* JXL_RESTRICT bgrow = rect.ConstPlaneRow(background, 1, y);
for (int sx = -kRectBounds; sx <= kRectBounds; sx++) {
int x = sx + cc.mode.x;
- if (x < 0 || static_cast<size_t>(x) >= energy.xsize()) continue;
+ if (x < 0 || static_cast<size_t>(x) >= rect.xsize()) continue;
double w = std::max(kEpsilon, sign * (row[x] - bgrow[x]));
sum += w;
@@ -426,7 +435,7 @@ GaussianEllipse FitGaussianFast(const ConnectedComponent& cc,
m2[1] += w * x * y;
m2[2] += w * y * y;
for (int c = 0; c < 3; c++) {
- bgColor[c] += background.ConstPlaneRow(c, y)[x];
+ bgColor[c] += rect.ConstPlaneRow(background, c, y)[x];
}
N++;
}
@@ -450,14 +459,16 @@ GaussianEllipse FitGaussianFast(const ConnectedComponent& cc,
ans.intensity[j] = kScaleMult[j] * color[j];
}
- ImageD Sigma(2, 2), D(1, 2), U(2, 2);
- Sigma.Row(0)[0] = m2[0] - m1[0] * m1[0];
- Sigma.Row(1)[1] = m2[2] - m1[1] * m1[1];
- Sigma.Row(0)[1] = Sigma.Row(1)[0] = m2[1] - m1[0] * m1[1];
- ConvertToDiagonal(Sigma, &D, &U);
- const double* JXL_RESTRICT d = D.ConstRow(0);
- const double* JXL_RESTRICT u = U.ConstRow(1);
- int p1 = 0, p2 = 1;
+ Matrix2x2 Sigma;
+ Vector2 d;
+ Matrix2x2 U;
+ Sigma[0][0] = m2[0] - m1[0] * m1[0];
+ Sigma[1][1] = m2[2] - m1[1] * m1[1];
+ Sigma[0][1] = Sigma[1][0] = m2[1] - m1[0] * m1[1];
+ ConvertToDiagonal(Sigma, d, U);
+ Vector2& u = U[1];
+ int p1 = 0;
+ int p2 = 1;
if (d[0] < d[1]) std::swap(p1, p2);
ans.sigma_x = kSigmaMult * d[p1];
ans.sigma_y = kSigmaMult * d[p2];
@@ -466,7 +477,8 @@ GaussianEllipse FitGaussianFast(const ConnectedComponent& cc,
ans.bgColor = bgColor;
if (leastSqIntensity) {
GaussianEllipse* ellipse = &ans;
- double ct = cos(ans.angle), st = sin(ans.angle);
+ double ct = cos(ans.angle);
+ double st = sin(ans.angle);
// Estimate intensity with least squares (fixed background)
for (int c = 0; c < 3; c++) {
double gg = 0.0;
@@ -474,11 +486,11 @@ GaussianEllipse FitGaussianFast(const ConnectedComponent& cc,
int yc = static_cast<int>(cc.mode.y);
int xc = static_cast<int>(cc.mode.x);
for (int y = yc - kRectBounds; y <= yc + kRectBounds; y++) {
- if (y < 0 || static_cast<size_t>(y) >= img.ysize()) continue;
- const float* JXL_RESTRICT row = img.ConstPlaneRow(c, y);
- const float* JXL_RESTRICT bgrow = background.ConstPlaneRow(c, y);
+ if (y < 0 || static_cast<size_t>(y) >= rect.ysize()) continue;
+ const float* JXL_RESTRICT row = rect.ConstPlaneRow(img, c, y);
+ const float* JXL_RESTRICT bgrow = rect.ConstPlaneRow(background, c, y);
for (int x = xc - kRectBounds; x <= xc + kRectBounds; x++) {
- if (x < 0 || static_cast<size_t>(x) >= img.xsize()) continue;
+ if (x < 0 || static_cast<size_t>(x) >= rect.xsize()) continue;
double target = row[x] - bgrow[x];
double gaussian =
DotGaussianModel(x - ellipse->x, y - ellipse->y, ct, st,
@@ -490,13 +502,13 @@ GaussianEllipse FitGaussianFast(const ConnectedComponent& cc,
ans.intensity[c] = gd / (gg + 1e-6); // Regularized least squares
}
}
- ComputeDotLosses(&ans, cc, img, background);
+ ComputeDotLosses(&ans, cc, rect, img, background);
return ans;
}
-GaussianEllipse FitGaussian(const ConnectedComponent& cc, const ImageF& energy,
+GaussianEllipse FitGaussian(const ConnectedComponent& cc, const Rect& rect,
const Image3F& img, const Image3F& background) {
- auto ellipse = FitGaussianFast(cc, energy, img, background);
+ auto ellipse = FitGaussianFast(cc, rect, img, background);
if (ellipse.sigma_x < ellipse.sigma_y) {
std::swap(ellipse.sigma_x, ellipse.sigma_y);
ellipse.angle += kPi / 2.0;
@@ -522,14 +534,16 @@ GaussianEllipse FitGaussian(const ConnectedComponent& cc, const ImageF& energy,
} // namespace
-std::vector<PatchInfo> DetectGaussianEllipses(
- const Image3F& opsin, const GaussianDetectParams& params,
+StatusOr<std::vector<PatchInfo>> DetectGaussianEllipses(
+ const Image3F& opsin, const Rect& rect, const GaussianDetectParams& params,
const EllipseQuantParams& qParams, ThreadPool* pool) {
std::vector<PatchInfo> dots;
- Image3F smooth(opsin.xsize(), opsin.ysize());
- ImageF energy = ComputeEnergyImage(opsin, &smooth, pool);
- std::vector<ConnectedComponent> components = FindCC(
- energy, params.t_low, params.t_high, params.maxWinSize, params.minScore);
+ JXL_ASSIGN_OR_RETURN(Image3F smooth,
+ Image3F::Create(opsin.xsize(), opsin.ysize()));
+ JXL_ASSIGN_OR_RETURN(ImageF energy, ComputeEnergyImage(opsin, &smooth, pool));
+ JXL_ASSIGN_OR_RETURN(std::vector<ConnectedComponent> components,
+ FindCC(energy, rect, params.t_low, params.t_high,
+ params.maxWinSize, params.minScore));
size_t numCC =
std::min(params.maxCC, (components.size() * params.percCC) / 100);
if (components.size() > numCC) {
@@ -541,11 +555,11 @@ std::vector<PatchInfo> DetectGaussianEllipses(
components.erase(components.begin() + numCC, components.end());
}
for (const auto& cc : components) {
- GaussianEllipse ellipse = FitGaussian(cc, energy, opsin, smooth);
+ GaussianEllipse ellipse = FitGaussian(cc, rect, opsin, smooth);
if (ellipse.x < 0.0 ||
- std::ceil(ellipse.x) >= static_cast<double>(opsin.xsize()) ||
+ std::ceil(ellipse.x) >= static_cast<double>(rect.xsize()) ||
ellipse.y < 0.0 ||
- std::ceil(ellipse.y) >= static_cast<double>(opsin.ysize())) {
+ std::ceil(ellipse.y) >= static_cast<double>(rect.ysize())) {
continue;
}
if (ellipse.neg_pixels > params.maxNegPixels) continue;
@@ -573,8 +587,8 @@ std::vector<PatchInfo> DetectGaussianEllipses(
for (size_t x = 0; x < patch.xsize; x++) {
for (size_t c = 0; c < 3; c++) {
patch.fpixels[c][y * patch.xsize + x] =
- opsin.ConstPlaneRow(c, y0 + y)[x0 + x] -
- smooth.ConstPlaneRow(c, y0 + y)[x0 + x];
+ rect.ConstPlaneRow(opsin, c, y0 + y)[x0 + x] -
+ rect.ConstPlaneRow(smooth, c, y0 + y)[x0 + x];
}
}
}
diff --git a/third_party/jpeg-xl/lib/jxl/enc_detect_dots.h b/third_party/jpeg-xl/lib/jxl/enc_detect_dots.h
index c3071d9a2f..59def59f8f 100644
--- a/third_party/jpeg-xl/lib/jxl/enc_detect_dots.h
+++ b/third_party/jpeg-xl/lib/jxl/enc_detect_dots.h
@@ -14,7 +14,6 @@
#include <vector>
#include "lib/jxl/base/data_parallel.h"
-#include "lib/jxl/dec_patch_dictionary.h"
#include "lib/jxl/enc_patch_dictionary.h"
#include "lib/jxl/image.h"
@@ -58,8 +57,8 @@ struct EllipseQuantParams {
};
// Detects dots in XYB image.
-std::vector<PatchInfo> DetectGaussianEllipses(
- const Image3F& opsin, const GaussianDetectParams& params,
+StatusOr<std::vector<PatchInfo>> DetectGaussianEllipses(
+ const Image3F& opsin, const Rect& rect, const GaussianDetectParams& params,
const EllipseQuantParams& qParams, ThreadPool* pool);
} // namespace jxl
diff --git a/third_party/jpeg-xl/lib/jxl/enc_dot_dictionary.cc b/third_party/jpeg-xl/lib/jxl/enc_dot_dictionary.cc
index a5b1af63b2..7d76ba9002 100644
--- a/third_party/jpeg-xl/lib/jxl/enc_dot_dictionary.cc
+++ b/third_party/jpeg-xl/lib/jxl/enc_dot_dictionary.cc
@@ -9,17 +9,12 @@
#include <string.h>
#include <array>
-#include <utility>
#include "lib/jxl/base/override.h"
#include "lib/jxl/base/status.h"
#include "lib/jxl/chroma_from_luma.h"
-#include "lib/jxl/dec_bit_reader.h"
-#include "lib/jxl/dec_xyb.h"
-#include "lib/jxl/enc_bit_writer.h"
#include "lib/jxl/enc_detect_dots.h"
#include "lib/jxl/enc_params.h"
-#include "lib/jxl/enc_xyb.h"
#include "lib/jxl/image.h"
namespace jxl {
@@ -39,10 +34,9 @@ const std::array<double, 3> kEllipseMaxIntensity{{0.05, 1.0, 0.4}};
const std::array<size_t, 3> kEllipseIntensityQ{{10, 36, 10}};
} // namespace
-std::vector<PatchInfo> FindDotDictionary(const CompressParams& cparams,
- const Image3F& opsin,
- const ColorCorrelationMap& cmap,
- ThreadPool* pool) {
+StatusOr<std::vector<PatchInfo>> FindDotDictionary(
+ const CompressParams& cparams, const Image3F& opsin, const Rect& rect,
+ const ColorCorrelationMap& cmap, ThreadPool* pool) {
if (ApplyOverride(cparams.dots,
cparams.butteraugli_distance >= kMinButteraugliForDots)) {
GaussianDetectParams ellipse_params;
@@ -58,14 +52,15 @@ std::vector<PatchInfo> FindDotDictionary(const CompressParams& cparams,
ellipse_params.maxCC = 100;
ellipse_params.percCC = 100;
EllipseQuantParams qParams{
- opsin.xsize(), opsin.ysize(), kEllipsePosQ,
+ rect.xsize(), rect.ysize(), kEllipsePosQ,
kEllipseMinSigma, kEllipseMaxSigma, kEllipseSigmaQ,
kEllipseAngleQ, kEllipseMinIntensity, kEllipseMaxIntensity,
kEllipseIntensityQ, kEllipsePosQ <= 5, cmap.YtoXRatio(0),
cmap.YtoBRatio(0)};
- return DetectGaussianEllipses(opsin, ellipse_params, qParams, pool);
+ return DetectGaussianEllipses(opsin, rect, ellipse_params, qParams, pool);
}
- return {};
+ std::vector<PatchInfo> nothing;
+ return nothing;
}
} // namespace jxl
diff --git a/third_party/jpeg-xl/lib/jxl/enc_dot_dictionary.h b/third_party/jpeg-xl/lib/jxl/enc_dot_dictionary.h
index 2ba4393f30..d348fb73b8 100644
--- a/third_party/jpeg-xl/lib/jxl/enc_dot_dictionary.h
+++ b/third_party/jpeg-xl/lib/jxl/enc_dot_dictionary.h
@@ -15,19 +15,15 @@
#include "lib/jxl/base/status.h"
#include "lib/jxl/chroma_from_luma.h"
-#include "lib/jxl/dec_bit_reader.h"
-#include "lib/jxl/dec_patch_dictionary.h"
-#include "lib/jxl/enc_bit_writer.h"
#include "lib/jxl/enc_params.h"
#include "lib/jxl/enc_patch_dictionary.h"
#include "lib/jxl/image.h"
namespace jxl {
-std::vector<PatchInfo> FindDotDictionary(const CompressParams& cparams,
- const Image3F& opsin,
- const ColorCorrelationMap& cmap,
- ThreadPool* pool);
+StatusOr<std::vector<PatchInfo>> FindDotDictionary(
+ const CompressParams& cparams, const Image3F& opsin, const Rect& rect,
+ const ColorCorrelationMap& cmap, ThreadPool* pool);
} // namespace jxl
diff --git a/third_party/jpeg-xl/lib/jxl/enc_entropy_coder.cc b/third_party/jpeg-xl/lib/jxl/enc_entropy_coder.cc
index 07601a2221..b71bb07bc9 100644
--- a/third_party/jpeg-xl/lib/jxl/enc_entropy_coder.cc
+++ b/third_party/jpeg-xl/lib/jxl/enc_entropy_coder.cc
@@ -90,8 +90,8 @@ int32_t NumNonZeroExceptLLF(const size_t cx, const size_t cy,
}
// We want area - sum_zero, add because neg_sum_zero is already negated.
- const int32_t nzeros =
- int32_t(cx * cy * kDCTBlockSize) + GetLane(SumOfLanes(di, neg_sum_zero));
+ const int32_t nzeros = static_cast<int32_t>(cx * cy * kDCTBlockSize) +
+ GetLane(SumOfLanes(di, neg_sum_zero));
const int32_t shifted_nzeros = static_cast<int32_t>(
(nzeros + covered_blocks - 1) >> log2_covered_blocks);
@@ -139,8 +139,8 @@ int32_t NumNonZero8x8ExceptDC(const int32_t* JXL_RESTRICT block,
}
// We want 64 - sum_zero, add because neg_sum_zero is already negated.
- const int32_t nzeros =
- int32_t(kDCTBlockSize) + GetLane(SumOfLanes(di, neg_sum_zero));
+ const int32_t nzeros = static_cast<int32_t>(kDCTBlockSize) +
+ GetLane(SumOfLanes(di, neg_sum_zero));
*nzeros_pos = nzeros;
@@ -157,7 +157,7 @@ void TokenizeCoefficients(const coeff_order_t* JXL_RESTRICT orders,
const Rect& rect,
const int32_t* JXL_RESTRICT* JXL_RESTRICT ac_rows,
const AcStrategyImage& ac_strategy,
- YCbCrChromaSubsampling cs,
+ const YCbCrChromaSubsampling& cs,
Image3I* JXL_RESTRICT tmp_num_nzeroes,
std::vector<Token>* JXL_RESTRICT output,
const ImageB& qdc, const ImageI& qf,
@@ -236,7 +236,7 @@ void TokenizeCoefficients(const coeff_order_t* JXL_RESTRICT orders,
log2_covered_blocks, prev);
uint32_t u_coeff = PackSigned(coeff);
output->emplace_back(ctx, u_coeff);
- prev = coeff != 0;
+ prev = (coeff != 0) ? 1 : 0;
nzeros -= prev;
}
JXL_DASSERT(nzeros == 0);
@@ -258,14 +258,14 @@ void TokenizeCoefficients(const coeff_order_t* JXL_RESTRICT orders,
const Rect& rect,
const int32_t* JXL_RESTRICT* JXL_RESTRICT ac_rows,
const AcStrategyImage& ac_strategy,
- YCbCrChromaSubsampling cs,
+ const YCbCrChromaSubsampling& cs,
Image3I* JXL_RESTRICT tmp_num_nzeroes,
std::vector<Token>* JXL_RESTRICT output,
const ImageB& qdc, const ImageI& qf,
const BlockCtxMap& block_ctx_map) {
- return HWY_DYNAMIC_DISPATCH(TokenizeCoefficients)(
- orders, rect, ac_rows, ac_strategy, cs, tmp_num_nzeroes, output, qdc, qf,
- block_ctx_map);
+ HWY_DYNAMIC_DISPATCH(TokenizeCoefficients)
+ (orders, rect, ac_rows, ac_strategy, cs, tmp_num_nzeroes, output, qdc, qf,
+ block_ctx_map);
}
} // namespace jxl
diff --git a/third_party/jpeg-xl/lib/jxl/enc_entropy_coder.h b/third_party/jpeg-xl/lib/jxl/enc_entropy_coder.h
index 7dfc71c726..6df3e8e770 100644
--- a/third_party/jpeg-xl/lib/jxl/enc_entropy_coder.h
+++ b/third_party/jpeg-xl/lib/jxl/enc_entropy_coder.h
@@ -35,7 +35,7 @@ void TokenizeCoefficients(const coeff_order_t* JXL_RESTRICT orders,
const Rect& rect,
const int32_t* JXL_RESTRICT* JXL_RESTRICT ac_rows,
const AcStrategyImage& ac_strategy,
- YCbCrChromaSubsampling cs,
+ const YCbCrChromaSubsampling& cs,
Image3I* JXL_RESTRICT tmp_num_nzeroes,
std::vector<Token>* JXL_RESTRICT output,
const ImageB& qdc, const ImageI& qf,
diff --git a/third_party/jpeg-xl/lib/jxl/enc_external_image.cc b/third_party/jpeg-xl/lib/jxl/enc_external_image.cc
index 680323e79a..90e4937e7b 100644
--- a/third_party/jpeg-xl/lib/jxl/enc_external_image.cc
+++ b/third_party/jpeg-xl/lib/jxl/enc_external_image.cc
@@ -8,14 +8,9 @@
#include <jxl/types.h>
#include <string.h>
-#include <algorithm>
-#include <array>
#include <atomic>
-#include <functional>
#include <utility>
-#include <vector>
-#include "lib/jxl/alpha.h"
#include "lib/jxl/base/byte_order.h"
#include "lib/jxl/base/common.h"
#include "lib/jxl/base/float.h"
@@ -114,7 +109,7 @@ Status ConvertFromExternalNoSizeCheck(const uint8_t* data, size_t xsize,
color_channels, format.num_channels);
}
- Image3F color(xsize, ysize);
+ JXL_ASSIGN_OR_RETURN(Image3F color, Image3F::Create(xsize, ysize));
for (size_t c = 0; c < color_channels; ++c) {
JXL_RETURN_IF_ERROR(ConvertFromExternalNoSizeCheck(
data, xsize, ysize, stride, bits_per_sample, format, c, pool,
@@ -129,7 +124,7 @@ Status ConvertFromExternalNoSizeCheck(const uint8_t* data, size_t xsize,
// Passing an interleaved image with an alpha channel to an image that doesn't
// have alpha channel just discards the passed alpha channel.
if (has_alpha && ib->HasAlpha()) {
- ImageF alpha(xsize, ysize);
+ JXL_ASSIGN_OR_RETURN(ImageF alpha, ImageF::Create(xsize, ysize));
JXL_RETURN_IF_ERROR(ConvertFromExternalNoSizeCheck(
data, xsize, ysize, stride, bits_per_sample, format,
format.num_channels - 1, pool, &alpha));
@@ -137,7 +132,7 @@ Status ConvertFromExternalNoSizeCheck(const uint8_t* data, size_t xsize,
} else if (!has_alpha && ib->HasAlpha()) {
// if alpha is not passed, but it is expected, then assume
// it is all-opaque
- ImageF alpha(xsize, ysize);
+ JXL_ASSIGN_OR_RETURN(ImageF alpha, ImageF::Create(xsize, ysize));
FillImage(1.0f, &alpha);
ib->SetAlpha(std::move(alpha));
}
@@ -184,7 +179,7 @@ Status ConvertFromExternal(Span<const uint8_t> bytes, size_t xsize,
color_channels, format.num_channels);
}
- Image3F color(xsize, ysize);
+ JXL_ASSIGN_OR_RETURN(Image3F color, Image3F::Create(xsize, ysize));
for (size_t c = 0; c < color_channels; ++c) {
JXL_RETURN_IF_ERROR(ConvertFromExternal(bytes.data(), bytes.size(), xsize,
ysize, bits_per_sample, format, c,
@@ -199,7 +194,7 @@ Status ConvertFromExternal(Span<const uint8_t> bytes, size_t xsize,
// Passing an interleaved image with an alpha channel to an image that doesn't
// have alpha channel just discards the passed alpha channel.
if (has_alpha && ib->HasAlpha()) {
- ImageF alpha(xsize, ysize);
+ JXL_ASSIGN_OR_RETURN(ImageF alpha, ImageF::Create(xsize, ysize));
JXL_RETURN_IF_ERROR(ConvertFromExternal(
bytes.data(), bytes.size(), xsize, ysize, bits_per_sample, format,
format.num_channels - 1, pool, &alpha));
@@ -207,7 +202,7 @@ Status ConvertFromExternal(Span<const uint8_t> bytes, size_t xsize,
} else if (!has_alpha && ib->HasAlpha()) {
// if alpha is not passed, but it is expected, then assume
// it is all-opaque
- ImageF alpha(xsize, ysize);
+ JXL_ASSIGN_OR_RETURN(ImageF alpha, ImageF::Create(xsize, ysize));
FillImage(1.0f, &alpha);
ib->SetAlpha(std::move(alpha));
}
diff --git a/third_party/jpeg-xl/lib/jxl/enc_fast_lossless.cc b/third_party/jpeg-xl/lib/jxl/enc_fast_lossless.cc
index b32d2478e0..58d0d00eaa 100644
--- a/third_party/jpeg-xl/lib/jxl/enc_fast_lossless.cc
+++ b/third_party/jpeg-xl/lib/jxl/enc_fast_lossless.cc
@@ -331,6 +331,8 @@ struct PrefixCode {
uint8_t* min_limit,
uint8_t* max_limit,
uint8_t* nbits) {
+ assert(precision < 15);
+ assert(n <= kMaxNumSymbols);
std::vector<T> dynp(((1U << precision) + 1) * (n + 1), infty);
auto d = [&](size_t sym, size_t off) -> T& {
return dynp[sym * ((1 << precision) + 1) + off];
@@ -428,10 +430,11 @@ struct PrefixCode {
}
// Invalid code, used to construct arrays.
- PrefixCode() {}
+ PrefixCode() = default;
template <typename BitDepth>
- PrefixCode(BitDepth, uint64_t* raw_counts, uint64_t* lz77_counts) {
+ PrefixCode(BitDepth /* bitdepth */, uint64_t* raw_counts,
+ uint64_t* lz77_counts) {
// "merge" together all the lz77 counts in a single symbol for the level 1
// table (containing just the raw symbols, up to length 7).
uint64_t level1_counts[kNumRawSymbols + 1];
@@ -1317,7 +1320,7 @@ struct Mask32 {
SIMDVec32 IfThenElse(const SIMDVec32& if_true, const SIMDVec32& if_false);
size_t CountPrefix() const {
return CtzNonZero(~static_cast<uint64_t>(
- (uint8_t)_mm256_movemask_ps(_mm256_castsi256_ps(mask))));
+ static_cast<uint8_t>(_mm256_movemask_ps(_mm256_castsi256_ps(mask)))));
}
};
@@ -1414,8 +1417,8 @@ struct Mask16 {
return Mask16{_mm256_and_si256(mask, oth.mask)};
}
size_t CountPrefix() const {
- return CtzNonZero(
- ~static_cast<uint64_t>((uint32_t)_mm256_movemask_epi8(mask))) /
+ return CtzNonZero(~static_cast<uint64_t>(
+ static_cast<uint32_t>(_mm256_movemask_epi8(mask)))) /
2;
}
};
@@ -3151,9 +3154,9 @@ void StoreYCoCg(SIMDVec16 r, SIMDVec16 g, SIMDVec16 b, int16_t* y, int16_t* co,
SIMDVec16 tmp = b.Add(co_v.SignedShiftRight<1>());
SIMDVec16 cg_v = g.Sub(tmp);
SIMDVec16 y_v = tmp.Add(cg_v.SignedShiftRight<1>());
- y_v.Store((uint16_t*)y);
- co_v.Store((uint16_t*)co);
- cg_v.Store((uint16_t*)cg);
+ y_v.Store(reinterpret_cast<uint16_t*>(y));
+ co_v.Store(reinterpret_cast<uint16_t*>(co));
+ cg_v.Store(reinterpret_cast<uint16_t*>(cg));
}
void StoreYCoCg(SIMDVec16 r, SIMDVec16 g, SIMDVec16 b, int32_t* y, int32_t* co,
@@ -3169,12 +3172,12 @@ void StoreYCoCg(SIMDVec16 r, SIMDVec16 g, SIMDVec16 b, int32_t* y, int32_t* co,
SIMDVec32 tmp_hi = b_up.hi.Add(co_hi_v.SignedShiftRight<1>());
SIMDVec32 cg_hi_v = g_up.hi.Sub(tmp_hi);
SIMDVec32 y_hi_v = tmp_hi.Add(cg_hi_v.SignedShiftRight<1>());
- y_lo_v.Store((uint32_t*)y);
- co_lo_v.Store((uint32_t*)co);
- cg_lo_v.Store((uint32_t*)cg);
- y_hi_v.Store((uint32_t*)y + SIMDVec32::kLanes);
- co_hi_v.Store((uint32_t*)co + SIMDVec32::kLanes);
- cg_hi_v.Store((uint32_t*)cg + SIMDVec32::kLanes);
+ y_lo_v.Store(reinterpret_cast<uint32_t*>(y));
+ co_lo_v.Store(reinterpret_cast<uint32_t*>(co));
+ cg_lo_v.Store(reinterpret_cast<uint32_t*>(cg));
+ y_hi_v.Store(reinterpret_cast<uint32_t*>(y) + SIMDVec32::kLanes);
+ co_hi_v.Store(reinterpret_cast<uint32_t*>(co) + SIMDVec32::kLanes);
+ cg_hi_v.Store(reinterpret_cast<uint32_t*>(cg) + SIMDVec32::kLanes);
}
#endif
@@ -3573,8 +3576,7 @@ void PrepareDCGlobalPalette(bool is_single_group, size_t width, size_t height,
int16_t p[4][32 + 1024] = {};
uint8_t prgba[4];
size_t i = 0;
- size_t have_zero = 0;
- if (palette[pcolors - 1] == 0) have_zero = 1;
+ size_t have_zero = 1;
for (; i < pcolors; i++) {
memcpy(prgba, &palette[i], 4);
p[0][16 + i + have_zero] = prgba[0];
@@ -3735,10 +3737,13 @@ JxlFastLosslessFrameState* LLPrepare(JxlChunkedFrameInputSource input,
const void* buffer =
input.get_color_channel_data_at(input.opaque, x0, y0, xs, ys, &stride);
auto rgba = reinterpret_cast<const unsigned char*>(buffer);
- int y_begin = std::max<int>(0, ys - 2 * effort) / 2;
- int y_count = std::min<int>(num_rows, y0 + ys - y_begin - 1);
+ int y_begin_group =
+ std::max<ssize_t>(
+ 0, static_cast<ssize_t>(ys) - static_cast<ssize_t>(num_rows)) /
+ 2;
+ int y_count = std::min<int>(num_rows, ys - y_begin_group);
int x_max = xs / kChunkSize * kChunkSize;
- CollectSamples(rgba, 0, y_begin, x_max, stride, y_count, raw_counts,
+ CollectSamples(rgba, 0, y_begin_group, x_max, stride, y_count, raw_counts,
lz77_counts, onegroup, !collided, bitdepth, nb_chans,
big_endian, lookup.data());
input.release_buffer(input.opaque, buffer);
@@ -4020,7 +4025,7 @@ namespace default_implementation {
#else // FJXL_ENABLE_NEON
namespace default_implementation {
-#include "lib/jxl/enc_fast_lossless.cc"
+#include "lib/jxl/enc_fast_lossless.cc" // NOLINT
}
#if FJXL_ENABLE_AVX2
@@ -4039,7 +4044,7 @@ namespace default_implementation {
namespace AVX2 {
#define FJXL_AVX2
-#include "lib/jxl/enc_fast_lossless.cc"
+#include "lib/jxl/enc_fast_lossless.cc" // NOLINT
#undef FJXL_AVX2
} // namespace AVX2
@@ -4174,18 +4179,20 @@ void JxlFastLosslessProcessFrame(
__builtin_cpu_supports("avx512vbmi") &&
__builtin_cpu_supports("avx512bw") && __builtin_cpu_supports("avx512f") &&
__builtin_cpu_supports("avx512vl")) {
- return AVX512::JxlFastLosslessProcessFrameImpl(
- frame_state, is_last, runner_opaque, runner, output_processor);
+ AVX512::JxlFastLosslessProcessFrameImpl(frame_state, is_last, runner_opaque,
+ runner, output_processor);
+ return;
}
#endif
#if FJXL_ENABLE_AVX2
if (__builtin_cpu_supports("avx2")) {
- return AVX2::JxlFastLosslessProcessFrameImpl(
- frame_state, is_last, runner_opaque, runner, output_processor);
+ AVX2::JxlFastLosslessProcessFrameImpl(frame_state, is_last, runner_opaque,
+ runner, output_processor);
+ return;
}
#endif
- return default_implementation::JxlFastLosslessProcessFrameImpl(
+ default_implementation::JxlFastLosslessProcessFrameImpl(
frame_state, is_last, runner_opaque, runner, output_processor);
}
diff --git a/third_party/jpeg-xl/lib/jxl/enc_fields.cc b/third_party/jpeg-xl/lib/jxl/enc_fields.cc
index dc0cbb7913..fa513297fd 100644
--- a/third_party/jpeg-xl/lib/jxl/enc_fields.cc
+++ b/third_party/jpeg-xl/lib/jxl/enc_fields.cc
@@ -74,7 +74,8 @@ class WriteVisitor : public VisitorBase {
Status Bundle::Write(const Fields& fields, BitWriter* writer, size_t layer,
AuxOut* aux_out) {
- size_t extension_bits, total_bits;
+ size_t extension_bits;
+ size_t total_bits;
JXL_RETURN_IF_ERROR(Bundle::CanEncode(fields, &extension_bits, &total_bits));
BitWriter::Allotment allotment(writer, total_bits);
@@ -173,7 +174,8 @@ Status F16Coder::Write(float value, BitWriter* JXL_RESTRICT writer) {
return true;
}
- uint32_t biased_exp16, mantissa16;
+ uint32_t biased_exp16;
+ uint32_t mantissa16;
// exp = [-24, -15] => subnormal
if (JXL_UNLIKELY(exp < -14)) {
diff --git a/third_party/jpeg-xl/lib/jxl/enc_frame.cc b/third_party/jpeg-xl/lib/jxl/enc_frame.cc
index aae59c49a6..8587e1aed2 100644
--- a/third_party/jpeg-xl/lib/jxl/enc_frame.cc
+++ b/third_party/jpeg-xl/lib/jxl/enc_frame.cc
@@ -12,13 +12,13 @@
#include <array>
#include <atomic>
#include <cmath>
-#include <limits>
+#include <memory>
#include <numeric>
+#include <utility>
#include <vector>
#include "lib/jxl/ac_context.h"
#include "lib/jxl/ac_strategy.h"
-#include "lib/jxl/ans_params.h"
#include "lib/jxl/base/bits.h"
#include "lib/jxl/base/common.h"
#include "lib/jxl/base/compiler_specific.h"
@@ -31,7 +31,6 @@
#include "lib/jxl/coeff_order_fwd.h"
#include "lib/jxl/color_encoding_internal.h"
#include "lib/jxl/common.h" // kMaxNumPasses
-#include "lib/jxl/compressed_dc.h"
#include "lib/jxl/dct_util.h"
#include "lib/jxl/dec_external_image.h"
#include "lib/jxl/enc_ac_strategy.h"
@@ -47,7 +46,6 @@
#include "lib/jxl/enc_entropy_coder.h"
#include "lib/jxl/enc_external_image.h"
#include "lib/jxl/enc_fields.h"
-#include "lib/jxl/enc_gaborish.h"
#include "lib/jxl/enc_group.h"
#include "lib/jxl/enc_heuristics.h"
#include "lib/jxl/enc_modular.h"
@@ -285,7 +283,8 @@ Status LoopFilterFromParams(const CompressParams& cparams, bool streaming_mode,
if (frame_header->encoding == FrameEncoding::kModular &&
!cparams.IsLossless()) {
// TODO(veluca): this formula is nonsense.
- loop_filter->epf_sigma_for_modular = cparams.butteraugli_distance;
+ loop_filter->epf_sigma_for_modular =
+ std::max(cparams.butteraugli_distance, 1.0f);
}
if (frame_header->encoding == FrameEncoding::kModular &&
cparams.lossy_palette) {
@@ -539,7 +538,7 @@ struct PixelStatsForChromacityAdjustment {
float dx = 0;
float db = 0;
float exposed_blue = 0;
- float CalcPlane(const ImageF* JXL_RESTRICT plane, const Rect& rect) const {
+ static float CalcPlane(const ImageF* JXL_RESTRICT plane, const Rect& rect) {
float xmax = 0;
float ymax = 0;
for (size_t ty = 1; ty < rect.ysize(); ++ty) {
@@ -583,7 +582,7 @@ struct PixelStatsForChromacityAdjustment {
dx = CalcPlane(&opsin->Plane(0), rect);
CalcExposedBlue(&opsin->Plane(1), &opsin->Plane(2), rect);
}
- int HowMuchIsXChannelPixelized() {
+ int HowMuchIsXChannelPixelized() const {
if (dx >= 0.03) {
return 2;
}
@@ -592,7 +591,7 @@ struct PixelStatsForChromacityAdjustment {
}
return 0;
}
- int HowMuchIsBChannelPixelized() {
+ int HowMuchIsBChannelPixelized() const {
int add = exposed_blue >= 0.13 ? 1 : 0;
if (db > 0.38) {
return 2 + add;
@@ -682,12 +681,12 @@ void ComputeNoiseParams(const CompressParams& cparams, bool streaming_mode,
}
}
-void DownsampleColorChannels(const CompressParams& cparams,
- const FrameHeader& frame_header,
- bool color_is_jpeg, Image3F* opsin) {
+Status DownsampleColorChannels(const CompressParams& cparams,
+ const FrameHeader& frame_header,
+ bool color_is_jpeg, Image3F* opsin) {
if (color_is_jpeg || frame_header.upsampling == 1 ||
cparams.already_downsampled) {
- return;
+ return true;
}
if (frame_header.encoding == FrameEncoding::kVarDCT &&
frame_header.upsampling == 2) {
@@ -698,16 +697,18 @@ void DownsampleColorChannels(const CompressParams& cparams,
// TODO(lode): DownsampleImage2_Iterative is currently too slow to
// be used for squirrel, make it faster, and / or enable it only for
// kitten.
- DownsampleImage2_Iterative(opsin);
+ JXL_RETURN_IF_ERROR(DownsampleImage2_Iterative(opsin));
} else {
- DownsampleImage2_Sharper(opsin);
+ JXL_RETURN_IF_ERROR(DownsampleImage2_Sharper(opsin));
}
} else {
- DownsampleImage(opsin, frame_header.upsampling);
+ JXL_ASSIGN_OR_RETURN(*opsin,
+ DownsampleImage(*opsin, frame_header.upsampling));
}
if (frame_header.encoding == FrameEncoding::kVarDCT) {
PadImageToBlockMultipleInPlace(opsin);
}
+ return true;
}
template <typename V, typename R>
@@ -741,14 +742,17 @@ Status ComputeJPEGTranscodingData(const jpeg::JPEGData& jpeg_data,
const size_t ysize_blocks = frame_dim.ysize_blocks;
// no-op chroma from luma
- shared.cmap = ColorCorrelationMap(xsize, ysize, false);
+ JXL_ASSIGN_OR_RETURN(shared.cmap,
+ ColorCorrelationMap::Create(xsize, ysize, false));
shared.ac_strategy.FillDCT8();
- FillImage(uint8_t(0), &shared.epf_sharpness);
+ FillImage(static_cast<uint8_t>(0), &shared.epf_sharpness);
enc_state->coeffs.clear();
while (enc_state->coeffs.size() < enc_state->passes.size()) {
- enc_state->coeffs.emplace_back(make_unique<ACImageT<int32_t>>(
- kGroupDim * kGroupDim, frame_dim.num_groups));
+ JXL_ASSIGN_OR_RETURN(
+ std::unique_ptr<ACImageT<int32_t>> coeffs,
+ ACImageT<int32_t>::Make(kGroupDim * kGroupDim, frame_dim.num_groups));
+ enc_state->coeffs.emplace_back(std::move(coeffs));
}
// convert JPEG quantization table to a Quantizer object
@@ -779,7 +783,8 @@ Status ComputeJPEGTranscodingData(const jpeg::JPEGData& jpeg_data,
1.0f / dcquantization[2]};
qe[AcStrategy::Type::DCT] = QuantEncoding::RAW(qt);
- DequantMatricesSetCustom(&shared.matrices, qe, enc_modular);
+ JXL_RETURN_IF_ERROR(
+ DequantMatricesSetCustom(&shared.matrices, qe, enc_modular));
// Ensure that InvGlobalScale() is 1.
shared.quantizer = Quantizer(&shared.matrices, 1, kGlobalScaleDenom);
@@ -844,7 +849,8 @@ Status ComputeJPEGTranscodingData(const jpeg::JPEGData& jpeg_data,
kScale * row_s[x * kDCTBlockSize + coeffpos] +
(kOffset - kBase * kScale) * scaled_m;
if (std::abs(scaled_m) > 1e-8f) {
- float from, to;
+ float from;
+ float to;
if (scaled_m > 0) {
from = (scaled_s - kZeroThresh) / scaled_m;
to = (scaled_s + kZeroThresh) / scaled_m;
@@ -889,7 +895,7 @@ Status ComputeJPEGTranscodingData(const jpeg::JPEGData& jpeg_data,
}
}
- Image3F dc = Image3F(xsize_blocks, ysize_blocks);
+ JXL_ASSIGN_OR_RETURN(Image3F dc, Image3F::Create(xsize_blocks, ysize_blocks));
if (!frame_header.chroma_subsampling.Is444()) {
ZeroFillImage(&dc);
for (auto& coeff : enc_state->coeffs) {
@@ -944,7 +950,7 @@ Status ComputeJPEGTranscodingData(const jpeg::JPEGData& jpeg_data,
idc = inputjpeg[base] + 1024 / qt[c * 64];
}
dc_counts[c][std::min(static_cast<uint32_t>(idc + 1024),
- uint32_t(2047))]++;
+ static_cast<uint32_t>(2047))]++;
total_dc[c]++;
fdc[bx >> hshift] = idc * dcquantization_r[c];
if (c == 1 || !enc_state->cparams.force_cfl_jpeg_recompression ||
@@ -1024,18 +1030,27 @@ Status ComputeJPEGTranscodingData(const jpeg::JPEGData& jpeg_data,
*std::max_element(ctx_map.begin(), ctx_map.end()) + 1;
// disable DC frame for now
+ std::atomic<bool> has_error{false};
auto compute_dc_coeffs = [&](const uint32_t group_index,
size_t /* thread */) {
+ if (has_error) return;
const Rect r = enc_state->shared.frame_dim.DCGroupRect(group_index);
- enc_modular->AddVarDCTDC(frame_header, dc, r, group_index,
- /*nl_dc=*/false, enc_state,
- /*jpeg_transcode=*/true);
- enc_modular->AddACMetadata(r, group_index, /*jpeg_transcode=*/true,
- enc_state);
+ if (!enc_modular->AddVarDCTDC(frame_header, dc, r, group_index,
+ /*nl_dc=*/false, enc_state,
+ /*jpeg_transcode=*/true)) {
+ has_error = true;
+ return;
+ }
+ if (!enc_modular->AddACMetadata(r, group_index, /*jpeg_transcode=*/true,
+ enc_state)) {
+ has_error = true;
+ return;
+ }
};
JXL_RETURN_IF_ERROR(RunOnPool(pool, 0, shared.frame_dim.num_dc_groups,
ThreadPool::NoInit, compute_dc_coeffs,
"Compute DC coeffs"));
+ if (has_error) return JXL_FAILURE("Compute DC coeffs failed");
return true;
}
@@ -1077,10 +1092,12 @@ void ComputeAllCoeffOrders(PassesEncoderState& enc_state,
// Working area for TokenizeCoefficients (per-group!)
struct EncCache {
// Allocates memory when first called.
- void InitOnce() {
+ Status InitOnce() {
if (num_nzeroes.xsize() == 0) {
- num_nzeroes = Image3I(kGroupDimInBlocks, kGroupDimInBlocks);
+ JXL_ASSIGN_OR_RETURN(
+ num_nzeroes, Image3I::Create(kGroupDimInBlocks, kGroupDimInBlocks));
}
+ return true;
}
// TokenizeCoefficients
Image3I num_nzeroes;
@@ -1095,8 +1112,10 @@ Status TokenizeAllCoefficients(const FrameHeader& frame_header,
group_caches.resize(num_threads);
return true;
};
+ std::atomic<bool> has_error{false};
const auto tokenize_group = [&](const uint32_t group_index,
const size_t thread) {
+ if (has_error) return;
// Tokenize coefficients.
const Rect rect = shared.frame_dim.BlockGroupRect(group_index);
for (size_t idx_pass = 0; idx_pass < enc_state->passes.size(); idx_pass++) {
@@ -1107,7 +1126,10 @@ Status TokenizeAllCoefficients(const FrameHeader& frame_header,
enc_state->coeffs[idx_pass]->PlaneRow(2, group_index, 0).ptr32,
};
// Ensure group cache is initialized.
- group_caches[thread].InitOnce();
+ if (!group_caches[thread].InitOnce()) {
+ has_error = true;
+ return;
+ }
TokenizeCoefficients(
&shared.coeff_orders[idx_pass * shared.coeff_order_size], rect,
ac_rows, shared.ac_strategy, frame_header.chroma_subsampling,
@@ -1116,8 +1138,11 @@ Status TokenizeAllCoefficients(const FrameHeader& frame_header,
shared.raw_quant_field, shared.block_ctx_map);
}
};
- return RunOnPool(pool, 0, shared.frame_dim.num_groups, tokenize_group_init,
- tokenize_group, "TokenizeGroup");
+ JXL_RETURN_IF_ERROR(RunOnPool(pool, 0, shared.frame_dim.num_groups,
+ tokenize_group_init, tokenize_group,
+ "TokenizeGroup"));
+ if (has_error) return JXL_FAILURE("TokenizeGroup failed");
+ return true;
}
Status EncodeGlobalDCInfo(const PassesSharedState& shared, BitWriter* writer,
@@ -1208,10 +1233,13 @@ Status EncodeGroups(const FrameHeader& frame_header,
const size_t num_groups = frame_dim.num_groups;
const size_t num_passes = enc_state->progressive_splitter.GetNumPasses();
const size_t global_ac_index = frame_dim.num_dc_groups + 1;
- const bool is_small_image = frame_dim.num_groups == 1 && num_passes == 1;
-
- group_codes->resize(
- NumTocEntries(num_groups, frame_dim.num_dc_groups, num_passes));
+ const bool is_small_image =
+ !enc_state->streaming_mode && num_groups == 1 && num_passes == 1;
+ const size_t num_toc_entries =
+ is_small_image ? 1
+ : AcGroupIndex(0, 0, num_groups, frame_dim.num_dc_groups) +
+ num_groups * num_passes;
+ group_codes->resize(num_toc_entries);
const auto get_output = [&](const size_t index) {
return &(*group_codes)[is_small_image ? 0 : index];
@@ -1308,35 +1336,48 @@ Status EncodeGroups(const FrameHeader& frame_header,
enc_state, get_output(global_ac_index), enc_modular, aux_out));
}
- std::atomic<int> num_errors{0};
+ std::atomic<bool> has_error{false};
const auto process_group = [&](const uint32_t group_index,
const size_t thread) {
+ if (has_error) return;
AuxOut* my_aux_out = aux_outs[thread].get();
+ size_t ac_group_id =
+ enc_state->streaming_mode
+ ? enc_modular->ComputeStreamingAbsoluteAcGroupId(
+ enc_state->dc_group_index, group_index, shared.frame_dim)
+ : group_index;
+
for (size_t i = 0; i < num_passes; i++) {
+ JXL_DEBUG_V(2, "Encoding AC group %u [abs %" PRIuS "] pass %" PRIuS,
+ group_index, ac_group_id, i);
if (frame_header.encoding == FrameEncoding::kVarDCT) {
if (!EncodeGroupTokenizedCoefficients(
group_index, i, enc_state->histogram_idx[group_index],
*enc_state, ac_group_code(i, group_index), my_aux_out)) {
- num_errors.fetch_add(1, std::memory_order_relaxed);
+ has_error = true;
return;
}
}
// Write all modular encoded data (color?, alpha, depth, extra channels)
if (!enc_modular->EncodeStream(
ac_group_code(i, group_index), my_aux_out, kLayerModularAcGroup,
- ModularStreamId::ModularAC(group_index, i))) {
- num_errors.fetch_add(1, std::memory_order_relaxed);
+ ModularStreamId::ModularAC(ac_group_id, i))) {
+ has_error = true;
return;
}
+ JXL_DEBUG_V(2,
+ "AC group %u [abs %" PRIuS "] pass %" PRIuS
+ " encoded size is %" PRIuS " bits",
+ group_index, ac_group_id, i,
+ ac_group_code(i, group_index)->BitsWritten());
}
};
JXL_RETURN_IF_ERROR(RunOnPool(pool, 0, num_groups, resize_aux_outs,
process_group, "EncodeGroupCoefficients"));
-
+ if (has_error) return JXL_FAILURE("EncodeGroupCoefficients failed");
// Resizing aux_outs to 0 also Assimilates the array.
static_cast<void>(resize_aux_outs(0));
- JXL_RETURN_IF_ERROR(num_errors.load(std::memory_order_relaxed) == 0);
for (BitWriter& bw : *group_codes) {
BitWriter::Allotment allotment(&bw, 8);
@@ -1360,29 +1401,39 @@ Status ComputeEncodingData(
PassesSharedState& shared = enc_state.shared;
shared.metadata = metadata;
if (enc_state.streaming_mode) {
- shared.frame_dim.Set(xsize, ysize, /*group_size_shift=*/1,
- /*maxhshift=*/0, /*maxvshift=*/0,
- /*modular_mode=*/false, /*upsampling=*/1);
+ shared.frame_dim.Set(
+ xsize, ysize, frame_header.group_size_shift,
+ /*max_hshift=*/0, /*max_vshift=*/0,
+ mutable_frame_header.encoding == FrameEncoding::kModular,
+ /*upsampling=*/1);
} else {
shared.frame_dim = frame_header.ToFrameDimensions();
}
shared.image_features.patches.SetPassesSharedState(&shared);
const FrameDimensions& frame_dim = shared.frame_dim;
- shared.ac_strategy =
- AcStrategyImage(frame_dim.xsize_blocks, frame_dim.ysize_blocks);
- shared.raw_quant_field =
- ImageI(frame_dim.xsize_blocks, frame_dim.ysize_blocks);
- shared.epf_sharpness = ImageB(frame_dim.xsize_blocks, frame_dim.ysize_blocks);
- shared.cmap = ColorCorrelationMap(frame_dim.xsize, frame_dim.ysize);
+ JXL_ASSIGN_OR_RETURN(
+ shared.ac_strategy,
+ AcStrategyImage::Create(frame_dim.xsize_blocks, frame_dim.ysize_blocks));
+ JXL_ASSIGN_OR_RETURN(
+ shared.raw_quant_field,
+ ImageI::Create(frame_dim.xsize_blocks, frame_dim.ysize_blocks));
+ JXL_ASSIGN_OR_RETURN(
+ shared.epf_sharpness,
+ ImageB::Create(frame_dim.xsize_blocks, frame_dim.ysize_blocks));
+ JXL_ASSIGN_OR_RETURN(shared.cmap, ColorCorrelationMap::Create(
+ frame_dim.xsize, frame_dim.ysize));
shared.coeff_order_size = kCoeffOrderMaxSize;
if (frame_header.encoding == FrameEncoding::kVarDCT) {
shared.coeff_orders.resize(frame_header.passes.num_passes *
kCoeffOrderMaxSize);
}
- shared.quant_dc = ImageB(frame_dim.xsize_blocks, frame_dim.ysize_blocks);
- shared.dc_storage = Image3F(frame_dim.xsize_blocks, frame_dim.ysize_blocks);
+ JXL_ASSIGN_OR_RETURN(shared.quant_dc, ImageB::Create(frame_dim.xsize_blocks,
+ frame_dim.ysize_blocks));
+ JXL_ASSIGN_OR_RETURN(
+ shared.dc_storage,
+ Image3F::Create(frame_dim.xsize_blocks, frame_dim.ysize_blocks));
shared.dc = &shared.dc_storage;
const size_t num_extra_channels = metadata->m.num_extra_channels;
@@ -1397,16 +1448,19 @@ Status ComputeEncodingData(
// computing inverse Gaborish and adaptive quantization map.
int max_border = enc_state.streaming_mode ? kBlockDim : 0;
Rect frame_rect(0, 0, frame_data.xsize, frame_data.ysize);
- Rect patch_rect = Rect(x0, y0, xsize, ysize).Extend(max_border, frame_rect);
+ Rect frame_area_rect = Rect(x0, y0, xsize, ysize);
+ Rect patch_rect = frame_area_rect.Extend(max_border, frame_rect);
JXL_ASSERT(patch_rect.IsInside(frame_rect));
// Allocating a large enough image avoids a copy when padding.
- Image3F color(RoundUpToBlockDim(patch_rect.xsize()),
- RoundUpToBlockDim(patch_rect.ysize()));
+ JXL_ASSIGN_OR_RETURN(Image3F color,
+ Image3F::Create(RoundUpToBlockDim(patch_rect.xsize()),
+ RoundUpToBlockDim(patch_rect.ysize())));
color.ShrinkTo(patch_rect.xsize(), patch_rect.ysize());
std::vector<ImageF> extra_channels(num_extra_channels);
for (auto& extra_channel : extra_channels) {
- extra_channel = jxl::ImageF(patch_rect.xsize(), patch_rect.ysize());
+ JXL_ASSIGN_OR_RETURN(
+ extra_channel, ImageF::Create(patch_rect.xsize(), patch_rect.ysize()));
}
ImageF* alpha = alpha_eci ? &extra_channels[alpha_idx] : nullptr;
ImageF* black = black_eci ? &extra_channels[black_idx] : nullptr;
@@ -1432,7 +1486,9 @@ Status ComputeEncodingData(
frame_info.ib_needs_color_transform) {
if (frame_header.encoding == FrameEncoding::kVarDCT &&
cparams.speed_tier <= SpeedTier::kKitten) {
- linear_storage = Image3F(patch_rect.xsize(), patch_rect.ysize());
+ JXL_ASSIGN_OR_RETURN(
+ linear_storage,
+ Image3F::Create(patch_rect.xsize(), patch_rect.ysize()));
linear = &linear_storage;
}
ToXYB(c_enc, metadata->m.IntensityTarget(), black, pool, &color, cms,
@@ -1446,7 +1502,7 @@ Status ComputeEncodingData(
bool lossless = cparams.IsLossless();
if (alpha && !alpha_eci->alpha_associated &&
frame_header.frame_type == FrameType::kRegularFrame &&
- !ApplyOverride(cparams.keep_invisible, lossless) &&
+ !ApplyOverride(cparams.keep_invisible, true) &&
cparams.ec_resampling == cparams.resampling) {
// simplify invisible pixels
SimplifyInvisible(&color, *alpha, lossless);
@@ -1467,15 +1523,17 @@ Status ComputeEncodingData(
&mutable_frame_header);
}
- ComputeNoiseParams(cparams, enc_state.streaming_mode, !!jpeg_data, color,
+ bool has_jpeg_data = (jpeg_data != nullptr);
+ ComputeNoiseParams(cparams, enc_state.streaming_mode, has_jpeg_data, color,
frame_dim, &mutable_frame_header,
&shared.image_features.noise_params);
- DownsampleColorChannels(cparams, frame_header, !!jpeg_data, &color);
+ JXL_RETURN_IF_ERROR(
+ DownsampleColorChannels(cparams, frame_header, has_jpeg_data, &color));
if (cparams.ec_resampling != 1 && !cparams.already_downsampled) {
for (ImageF& ec : extra_channels) {
- DownsampleImage(&ec, cparams.ec_resampling);
+ JXL_ASSIGN_OR_RETURN(ec, DownsampleImage(ec, cparams.ec_resampling));
}
}
@@ -1505,15 +1563,21 @@ Status ComputeEncodingData(
TokenizeAllCoefficients(frame_header, pool, &enc_state));
}
+ if (cparams.modular_mode || !extra_channels.empty()) {
+ JXL_RETURN_IF_ERROR(enc_modular.ComputeEncodingData(
+ frame_header, metadata->m, &color, extra_channels, group_rect,
+ frame_dim, frame_area_rect, &enc_state, cms, pool, aux_out,
+ /*do_color=*/cparams.modular_mode));
+ }
+
if (!enc_state.streaming_mode) {
- if (cparams.modular_mode || !extra_channels.empty()) {
- JXL_RETURN_IF_ERROR(enc_modular.ComputeEncodingData(
- frame_header, metadata->m, &color, extra_channels, &enc_state, cms,
- pool, aux_out, /*do_color=*/cparams.modular_mode));
+ if (cparams.speed_tier < SpeedTier::kTortoise ||
+ !cparams.ModularPartIsLossless() || cparams.responsive ||
+ !cparams.custom_fixed_tree.empty()) {
+ // Use local trees if doing lossless modular, unless at very slow speeds.
+ JXL_RETURN_IF_ERROR(enc_modular.ComputeTree(pool));
+ JXL_RETURN_IF_ERROR(enc_modular.ComputeTokens(pool));
}
- JXL_RETURN_IF_ERROR(enc_modular.ComputeTree(pool));
- JXL_RETURN_IF_ERROR(enc_modular.ComputeTokens(pool));
-
mutable_frame_header.UpdateFlag(shared.image_features.patches.HasAny(),
FrameHeader::kPatches);
mutable_frame_header.UpdateFlag(shared.image_features.splines.HasAny(),
@@ -1526,6 +1590,7 @@ Status ComputeEncodingData(
const size_t group_index = enc_state.dc_group_index;
enc_modular.ClearStreamData(ModularStreamId::VarDCTDC(group_index));
enc_modular.ClearStreamData(ModularStreamId::ACMetadata(group_index));
+ enc_modular.ClearModularStreamData();
}
return true;
}
@@ -1614,49 +1679,58 @@ bool CanDoStreamingEncoding(const CompressParams& cparams,
const FrameInfo& frame_info,
const CodecMetadata& metadata,
const JxlEncoderChunkedFrameAdapter& frame_data) {
- if (frame_data.IsJPEG()) {
- return false;
- }
- if (cparams.noise == Override::kOn || cparams.patches == Override::kOn) {
+ if (cparams.buffering == 0) {
return false;
}
- if (cparams.progressive_dc != 0 || frame_info.dc_level != 0) {
- return false;
+ if (cparams.buffering == -1) {
+ if (cparams.speed_tier < SpeedTier::kTortoise) return false;
+ if (cparams.speed_tier < SpeedTier::kSquirrel &&
+ cparams.butteraugli_distance > 0.5f) {
+ return false;
+ }
+ if (cparams.speed_tier == SpeedTier::kSquirrel &&
+ cparams.butteraugli_distance >= 3.f) {
+ return false;
+ }
}
- if (cparams.resampling != 1 || cparams.ec_resampling != 1) {
+
+ // TODO(veluca): handle different values of `buffering`.
+ if (frame_data.xsize <= 2048 && frame_data.ysize <= 2048) {
return false;
}
- if (cparams.max_error_mode) {
+ if (frame_data.IsJPEG()) {
return false;
}
- if (cparams.color_transform != ColorTransform::kXYB) {
+ if (cparams.noise == Override::kOn || cparams.patches == Override::kOn) {
return false;
}
- if (cparams.modular_mode) {
+ if (cparams.progressive_dc != 0 || frame_info.dc_level != 0) {
return false;
}
- if (metadata.m.num_extra_channels > 0) {
+ if (cparams.resampling != 1 || cparams.ec_resampling != 1) {
return false;
}
- if (cparams.buffering == 0) {
+ if (cparams.max_error_mode) {
return false;
}
- if (cparams.buffering == 1 && frame_data.xsize <= 2048 &&
- frame_data.ysize <= 2048) {
- return false;
+ if (!cparams.ModularPartIsLossless() || cparams.responsive > 0) {
+ if (metadata.m.num_extra_channels > 0 || cparams.modular_mode) {
+ return false;
+ }
}
- if (frame_data.xsize <= 256 && frame_data.ysize <= 256) {
+ ColorTransform ok_color_transform =
+ cparams.modular_mode ? ColorTransform::kNone : ColorTransform::kXYB;
+ if (cparams.color_transform != ok_color_transform) {
return false;
}
return true;
}
void ComputePermutationForStreaming(size_t xsize, size_t ysize,
- size_t num_passes,
+ size_t group_size, size_t num_passes,
std::vector<coeff_order_t>& permutation,
std::vector<size_t>& dc_group_order) {
// This is only valid in VarDCT mode, otherwise there can be group shift.
- const size_t group_size = 256;
const size_t dc_group_size = group_size * kBlockDim;
const size_t group_xsize = DivCeil(xsize, group_size);
const size_t group_ysize = DivCeil(ysize, group_size);
@@ -1794,7 +1868,7 @@ void RemoveUnusedHistograms(std::vector<uint8_t>& context_map,
for (uint8_t histo_idx : inv_remap) {
new_codes.encoding_info.emplace_back(
std::move(codes.encoding_info[histo_idx]));
- new_codes.uint_config.emplace_back(std::move(codes.uint_config[histo_idx]));
+ new_codes.uint_config.emplace_back(codes.uint_config[histo_idx]);
new_codes.encoded_histograms.emplace_back(
std::move(codes.encoded_histograms[histo_idx]));
}
@@ -1864,14 +1938,13 @@ Status EncodeFrameStreaming(const CompressParams& cparams,
frame_info, jpeg_data.get(), true,
&frame_header));
const size_t num_passes = enc_state.progressive_splitter.GetNumPasses();
- ModularFrameEncoder enc_modular(frame_header, cparams);
+ ModularFrameEncoder enc_modular(frame_header, cparams, true);
std::vector<coeff_order_t> permutation;
std::vector<size_t> dc_group_order;
- ComputePermutationForStreaming(frame_data.xsize, frame_data.ysize, num_passes,
- permutation, dc_group_order);
+ size_t group_size = frame_header.ToFrameDimensions().group_dim;
+ ComputePermutationForStreaming(frame_data.xsize, frame_data.ysize, group_size,
+ num_passes, permutation, dc_group_order);
enc_state.shared.num_histograms = dc_group_order.size();
- // This is only valid in VarDCT mode, otherwise there can be group shift.
- size_t group_size = 256;
size_t dc_group_size = group_size * kBlockDim;
size_t dc_group_xsize = DivCeil(frame_data.xsize, dc_group_size);
size_t min_dc_global_size = 0;
@@ -1898,8 +1971,7 @@ Status EncodeFrameStreaming(const CompressParams& cparams,
enc_state.streaming_mode = true;
enc_state.initialize_global_state = (i == 0);
enc_state.dc_group_index = dc_ix;
- enc_state.histogram_idx =
- std::vector<uint8_t>(group_xsize * group_ysize, i);
+ enc_state.histogram_idx = std::vector<size_t>(group_xsize * group_ysize, i);
std::vector<BitWriter> group_codes;
JXL_RETURN_IF_ERROR(ComputeEncodingData(
cparams, frame_info, metadata, frame_data, jpeg_data.get(), x0, y0,
@@ -1931,9 +2003,13 @@ Status EncodeFrameStreaming(const CompressParams& cparams,
JXL_RETURN_IF_ERROR(
OutputGroups(std::move(group_codes), &group_sizes, output_processor));
}
- JXL_RETURN_IF_ERROR(OutputAcGlobal(enc_state,
- frame_header.ToFrameDimensions(),
- &group_sizes, output_processor, aux_out));
+ if (frame_header.encoding == FrameEncoding::kVarDCT) {
+ JXL_RETURN_IF_ERROR(
+ OutputAcGlobal(enc_state, frame_header.ToFrameDimensions(),
+ &group_sizes, output_processor, aux_out));
+ } else {
+ group_sizes.push_back(0);
+ }
JXL_ASSERT(group_sizes.size() == permutation.size());
size_t end_pos = output_processor->CurrentPosition();
output_processor->Seek(start_pos);
@@ -1975,7 +2051,7 @@ Status EncodeFrameOneShot(const CompressParams& cparams,
frame_info, jpeg_data.get(), false,
&frame_header));
const size_t num_passes = enc_state.progressive_splitter.GetNumPasses();
- ModularFrameEncoder enc_modular(frame_header, cparams);
+ ModularFrameEncoder enc_modular(frame_header, cparams, false);
JXL_RETURN_IF_ERROR(ComputeEncodingData(
cparams, frame_info, metadata, frame_data, jpeg_data.get(), 0, 0,
frame_data.xsize, frame_data.ysize, cms, pool, frame_header, enc_modular,
@@ -2008,15 +2084,21 @@ Status EncodeFrame(const CompressParams& cparams_orig,
JxlEncoderOutputProcessorWrapper* output_processor,
AuxOut* aux_out) {
CompressParams cparams = cparams_orig;
- if (cparams.speed_tier == SpeedTier::kGlacier && !cparams.IsLossless()) {
- cparams.speed_tier = SpeedTier::kTortoise;
+ if (cparams.speed_tier == SpeedTier::kTectonicPlate &&
+ !cparams.IsLossless()) {
+ cparams.speed_tier = SpeedTier::kGlacier;
+ }
+ // Lightning mode is handled externally, so switch to Thunder mode to handle
+ // potentially weird cases.
+ if (cparams.speed_tier == SpeedTier::kLightning) {
+ cparams.speed_tier = SpeedTier::kThunder;
}
- if (cparams.speed_tier == SpeedTier::kGlacier) {
+ if (cparams.speed_tier == SpeedTier::kTectonicPlate) {
std::vector<CompressParams> all_params;
std::vector<size_t> size;
CompressParams cparams_attempt = cparams_orig;
- cparams_attempt.speed_tier = SpeedTier::kTortoise;
+ cparams_attempt.speed_tier = SpeedTier::kGlacier;
cparams_attempt.options.max_properties = 4;
for (float x : {0.0f, 80.f}) {
@@ -2027,8 +2109,9 @@ Status EncodeFrame(const CompressParams& cparams_orig,
// modular headers.
for (int K : {0, 1 << 10, 70000}) {
cparams_attempt.palette_colors = K;
- for (int tree_mode : {-1, (int)ModularOptions::TreeMode::kNoWP,
- (int)ModularOptions::TreeMode::kDefault}) {
+ for (int tree_mode :
+ {-1, static_cast<int>(ModularOptions::TreeMode::kNoWP),
+ static_cast<int>(ModularOptions::TreeMode::kDefault)}) {
if (tree_mode == -1) {
// LZ77 only
cparams_attempt.options.nb_repeats = 0;
@@ -2054,11 +2137,12 @@ Status EncodeFrame(const CompressParams& cparams_orig,
size.resize(all_params.size());
- std::atomic<int> num_errors{0};
+ std::atomic<bool> has_error{false};
JXL_RETURN_IF_ERROR(RunOnPool(
pool, 0, all_params.size(), ThreadPool::NoInit,
[&](size_t task, size_t) {
+ if (has_error) return;
std::vector<uint8_t> output(64);
uint8_t* next_out = output.data();
size_t avail_out = output.size();
@@ -2066,13 +2150,13 @@ Status EncodeFrame(const CompressParams& cparams_orig,
local_output.SetAvailOut(&next_out, &avail_out);
if (!EncodeFrame(all_params[task], frame_info, metadata, frame_data,
cms, nullptr, &local_output, aux_out)) {
- num_errors.fetch_add(1, std::memory_order_relaxed);
+ has_error = true;
return;
}
size[task] = local_output.CurrentPosition();
},
- "Compress kGlacier"));
- JXL_RETURN_IF_ERROR(num_errors.load(std::memory_order_relaxed) == 0);
+ "Compress kTectonicPlate"));
+ if (has_error) return JXL_FAILURE("Compress kTectonicPlate failed");
size_t best_idx = 0;
for (size_t i = 1; i < all_params.size(); i++) {
@@ -2156,7 +2240,7 @@ Status EncodeFrame(const CompressParams& cparams_orig,
size_t stride = ib.xsize() * num_channels * 4;
color.resize(ib.ysize() * stride);
JXL_RETURN_IF_ERROR(ConvertToExternal(
- ib, /*bites_per_sample=*/32, /*float_out=*/true, num_channels,
+ ib, /*bits_per_sample=*/32, /*float_out=*/true, num_channels,
JXL_NATIVE_ENDIAN, stride, pool, color.data(), color.size(),
/*out_callback=*/{}, Orientation::kIdentity));
JxlPixelFormat format{num_channels, JXL_TYPE_FLOAT, JXL_NATIVE_ENDIAN, 0};
@@ -2169,7 +2253,7 @@ Status EncodeFrame(const CompressParams& cparams_orig,
const ImageF* channel = &ib.extra_channels()[ec];
JXL_RETURN_IF_ERROR(ConvertChannelsToExternal(
&channel, 1,
- /*bites_per_sample=*/32,
+ /*bits_per_sample=*/32,
/*float_out=*/true, JXL_NATIVE_ENDIAN, ec_stride, pool, ec_data.data(),
ec_data.size(), /*out_callback=*/{}, Orientation::kIdentity));
frame_data.SetFromBuffer(1 + ec, ec_data.data(), ec_data.size(), ec_format);
diff --git a/third_party/jpeg-xl/lib/jxl/enc_frame.h b/third_party/jpeg-xl/lib/jxl/enc_frame.h
index c6db64ee4e..a5b74cc648 100644
--- a/third_party/jpeg-xl/lib/jxl/enc_frame.h
+++ b/third_party/jpeg-xl/lib/jxl/enc_frame.h
@@ -57,7 +57,7 @@ struct FrameInfo {
// Corresponds to BlendingInfo::source from the FrameHeader.
size_t source = 1;
// Corresponds to BlendingInfo::clamp from the FrameHeader.
- size_t clamp = 1;
+ bool clamp = true;
// Corresponds to BlendingInfo::alpha_channel from the FrameHeader, or set to
// -1 to automatically choose it as the index of the first extra channel of
// type alpha.
diff --git a/third_party/jpeg-xl/lib/jxl/enc_gaborish.cc b/third_party/jpeg-xl/lib/jxl/enc_gaborish.cc
index 3f2ee32afd..7467a4d669 100644
--- a/third_party/jpeg-xl/lib/jxl/enc_gaborish.cc
+++ b/third_party/jpeg-xl/lib/jxl/enc_gaborish.cc
@@ -15,8 +15,8 @@
namespace jxl {
-void GaborishInverse(Image3F* in_out, const Rect& rect, float mul[3],
- ThreadPool* pool) {
+Status GaborishInverse(Image3F* in_out, const Rect& rect, const float mul[3],
+ ThreadPool* pool) {
WeightsSymmetric5 weights[3];
// Only an approximation. One or even two 3x3, and rank-1 (separable) 5x5
// are insufficient. The numbers here have been obtained by butteraugli
@@ -47,7 +47,9 @@ void GaborishInverse(Image3F* in_out, const Rect& rect, float mul[3],
// Note that we cannot *allocate* a plane, as doing so might cause Image3F to
// have planes of different stride. Instead, we copy one plane in a temporary
// image and reuse the existing planes of the in/out image.
- ImageF temp(in_out->Plane(2).xsize(), in_out->Plane(2).ysize());
+ ImageF temp;
+ JXL_ASSIGN_OR_RETURN(
+ temp, ImageF::Create(in_out->Plane(2).xsize(), in_out->Plane(2).ysize()));
CopyImageTo(in_out->Plane(2), &temp);
Rect xrect = rect.Extend(3, Rect(*in_out));
Symmetric5(in_out->Plane(0), xrect, weights[0], pool, &in_out->Plane(2),
@@ -59,6 +61,7 @@ void GaborishInverse(Image3F* in_out, const Rect& rect, float mul[3],
in_out->Plane(0).Swap(in_out->Plane(1));
// 2 1 0
in_out->Plane(0).Swap(in_out->Plane(2));
+ return true;
}
} // namespace jxl
diff --git a/third_party/jpeg-xl/lib/jxl/enc_gaborish.h b/third_party/jpeg-xl/lib/jxl/enc_gaborish.h
index ece4959f36..041edcec96 100644
--- a/third_party/jpeg-xl/lib/jxl/enc_gaborish.h
+++ b/third_party/jpeg-xl/lib/jxl/enc_gaborish.h
@@ -9,6 +9,7 @@
// Linear smoothing (3x3 convolution) for deblocking without too much blur.
#include "lib/jxl/base/data_parallel.h"
+#include "lib/jxl/base/status.h"
#include "lib/jxl/image.h"
namespace jxl {
@@ -16,8 +17,8 @@ namespace jxl {
// Used in encoder to reduce the impact of the decoder's smoothing.
// This is not exact. Works in-place to reduce memory use.
// The input is typically in XYB space.
-void GaborishInverse(Image3F* in_out, const Rect& rect, float mul[3],
- ThreadPool* pool);
+Status GaborishInverse(Image3F* in_out, const Rect& rect, const float mul[3],
+ ThreadPool* pool);
} // namespace jxl
diff --git a/third_party/jpeg-xl/lib/jxl/enc_gaborish_test.cc b/third_party/jpeg-xl/lib/jxl/enc_gaborish_test.cc
index 426f08ecb0..0d173c5eb8 100644
--- a/third_party/jpeg-xl/lib/jxl/enc_gaborish_test.cc
+++ b/third_party/jpeg-xl/lib/jxl/enc_gaborish_test.cc
@@ -5,6 +5,8 @@
#include "lib/jxl/enc_gaborish.h"
+#include <jxl/types.h>
+
#include <hwy/base.h>
#include "lib/jxl/base/compiler_specific.h"
@@ -40,7 +42,7 @@ void ConvolveGaborish(const ImageF& in, float weight1, float weight2,
}
void TestRoundTrip(const Image3F& in, float max_l1) {
- Image3F fwd(in.xsize(), in.ysize());
+ JXL_ASSIGN_OR_DIE(Image3F fwd, Image3F::Create(in.xsize(), in.ysize()));
ThreadPool* null_pool = nullptr;
ConvolveGaborish(in.Plane(0), 0, 0, null_pool, &fwd.Plane(0));
ConvolveGaborish(in.Plane(1), 0, 0, null_pool, &fwd.Plane(1));
@@ -51,20 +53,20 @@ void TestRoundTrip(const Image3F& in, float max_l1) {
w,
w,
};
- GaborishInverse(&fwd, Rect(fwd), weights, null_pool);
+ JXL_CHECK(GaborishInverse(&fwd, Rect(fwd), weights, null_pool));
JXL_ASSERT_OK(VerifyRelativeError(in, fwd, max_l1, 1E-4f, _));
}
TEST(GaborishTest, TestZero) {
- Image3F in(20, 20);
+ JXL_ASSIGN_OR_DIE(Image3F in, Image3F::Create(20, 20));
ZeroFillImage(&in);
TestRoundTrip(in, 0.0f);
}
// Disabled: large difference.
-#if 0
+#if JXL_FALSE
TEST(GaborishTest, TestDirac) {
- Image3F in(20, 20);
+ JXL_ASSIGN_OR_DIE(Image3F in, Image3F::Create(20, 20));
ZeroFillImage(&in);
in.PlaneRow(1, 10)[10] = 10.0f;
TestRoundTrip(in, 0.26f);
@@ -72,7 +74,7 @@ TEST(GaborishTest, TestDirac) {
#endif
TEST(GaborishTest, TestFlat) {
- Image3F in(20, 20);
+ JXL_ASSIGN_OR_DIE(Image3F in, Image3F::Create(20, 20));
FillImage(1.0f, &in);
TestRoundTrip(in, 1E-5f);
}
diff --git a/third_party/jpeg-xl/lib/jxl/enc_group.cc b/third_party/jpeg-xl/lib/jxl/enc_group.cc
index 09bab534c9..1967fdaba9 100644
--- a/third_party/jpeg-xl/lib/jxl/enc_group.cc
+++ b/third_party/jpeg-xl/lib/jxl/enc_group.cc
@@ -286,10 +286,11 @@ void AdjustQuantBlockAC(const Quantizer& quantizer, size_t c,
{
// Reduce quant in highly active areas.
int32_t div = (xsize * ysize);
- int32_t activity = (hfNonZeros[0] + div / 2) / div;
+ int32_t activity = (static_cast<int32_t>(hfNonZeros[0]) + div / 2) / div;
int32_t orig_qp_limit = std::max(4, *quant / 2);
for (int i = 1; i < 4; ++i) {
- activity = std::min<int32_t>(activity, (hfNonZeros[i] + div / 2) / div);
+ activity = std::min(
+ activity, (static_cast<int32_t>(hfNonZeros[i]) + div / 2) / div);
}
if (activity >= 15) {
activity = 15;
@@ -316,7 +317,7 @@ void QuantizeRoundtripYBlockAC(PassesEncoderState* enc_state, const size_t size,
float* JXL_RESTRICT inout,
int32_t* JXL_RESTRICT quantized) {
float thres_y[4] = {0.58f, 0.64f, 0.64f, 0.64f};
- {
+ if (enc_state->cparams.speed_tier <= SpeedTier::kHare) {
int32_t max_quant = 0;
int quant_orig = *quant;
float val[3] = {enc_state->x_qm_multiplier, 1.0f,
@@ -337,6 +338,11 @@ void QuantizeRoundtripYBlockAC(PassesEncoderState* enc_state, const size_t size,
max_quant = std::max(*quant, max_quant);
}
*quant = max_quant;
+ } else {
+ thres_y[0] = 0.56;
+ thres_y[1] = 0.62;
+ thres_y[2] = 0.62;
+ thres_y[3] = 0.62;
}
QuantizeBlockAC(quantizer, error_diffusion, 1, 1.0f, quant_kind, xsize, ysize,
@@ -507,8 +513,8 @@ namespace jxl {
HWY_EXPORT(ComputeCoefficients);
void ComputeCoefficients(size_t group_idx, PassesEncoderState* enc_state,
const Image3F& opsin, const Rect& rect, Image3F* dc) {
- return HWY_DYNAMIC_DISPATCH(ComputeCoefficients)(group_idx, enc_state, opsin,
- rect, dc);
+ HWY_DYNAMIC_DISPATCH(ComputeCoefficients)
+ (group_idx, enc_state, opsin, rect, dc);
}
Status EncodeGroupTokenizedCoefficients(size_t group_idx, size_t pass_idx,
diff --git a/third_party/jpeg-xl/lib/jxl/enc_heuristics.cc b/third_party/jpeg-xl/lib/jxl/enc_heuristics.cc
index 9d6bf11184..685558ac7c 100644
--- a/third_party/jpeg-xl/lib/jxl/enc_heuristics.cc
+++ b/third_party/jpeg-xl/lib/jxl/enc_heuristics.cc
@@ -149,8 +149,10 @@ void FindBestBlockEntropyModel(const CompressParams& cparams, const ImageI& rqf,
std::vector<uint8_t> remap((qft.size() + 1) * kNumOrders);
std::iota(remap.begin(), remap.end(), 0);
std::vector<uint8_t> clusters(remap);
- size_t nb_clusters = Clamp1((int)(tot / size_for_ctx_model / 2), 2, 9);
- size_t nb_clusters_chroma = Clamp1((int)(tot / size_for_ctx_model / 3), 1, 5);
+ size_t nb_clusters =
+ Clamp1(static_cast<int>(tot / size_for_ctx_model / 2), 2, 9);
+ size_t nb_clusters_chroma =
+ Clamp1(static_cast<int>(tot / size_for_ctx_model / 3), 1, 5);
// This is O(n^2 log n), but n is small.
while (clusters.size() > nb_clusters) {
std::sort(clusters.begin(), clusters.end(),
@@ -181,8 +183,8 @@ void FindBestBlockEntropyModel(const CompressParams& cparams, const ImageI& rqf,
// for chroma, only use up to nb_clusters_chroma separate block contexts
// (those for the biggest clusters)
for (size_t i = remap.size(); i < remap.size() * 3; i++) {
- ctx_map[i] = num + Clamp1((int)remap[i % remap.size()], 0,
- (int)nb_clusters_chroma - 1);
+ ctx_map[i] = num + Clamp1(static_cast<int>(remap[i % remap.size()]), 0,
+ static_cast<int>(nb_clusters_chroma) - 1);
}
block_ctx_map->num_ctxs =
*std::max_element(ctx_map.begin(), ctx_map.end()) + 1;
@@ -190,9 +192,9 @@ void FindBestBlockEntropyModel(const CompressParams& cparams, const ImageI& rqf,
namespace {
-void FindBestDequantMatrices(const CompressParams& cparams,
- ModularFrameEncoder* modular_frame_encoder,
- DequantMatrices* dequant_matrices) {
+Status FindBestDequantMatrices(const CompressParams& cparams,
+ ModularFrameEncoder* modular_frame_encoder,
+ DequantMatrices* dequant_matrices) {
// TODO(veluca): quant matrices for no-gaborish.
// TODO(veluca): heuristics for in-bitstream quant tables.
*dequant_matrices = DequantMatrices();
@@ -204,13 +206,14 @@ void FindBestDequantMatrices(const CompressParams& cparams,
DctQuantWeightParams dct_params(weights);
std::vector<QuantEncoding> encodings(DequantMatrices::kNum,
QuantEncoding::DCT(dct_params));
- DequantMatricesSetCustom(dequant_matrices, encodings,
- modular_frame_encoder);
+ JXL_RETURN_IF_ERROR(DequantMatricesSetCustom(dequant_matrices, encodings,
+ modular_frame_encoder));
float dc_weights[3] = {1.0f / cparams.max_error[0],
1.0f / cparams.max_error[1],
1.0f / cparams.max_error[2]};
DequantMatricesSetCustomDC(dequant_matrices, dc_weights);
}
+ return true;
}
void StoreMin2(const float v, float& min1, float& min2) {
@@ -226,9 +229,9 @@ void StoreMin2(const float v, float& min1, float& min2) {
void CreateMask(const ImageF& image, ImageF& mask) {
for (size_t y = 0; y < image.ysize(); y++) {
- auto* row_n = y > 0 ? image.Row(y - 1) : image.Row(y);
- auto* row_in = image.Row(y);
- auto* row_s = y + 1 < image.ysize() ? image.Row(y + 1) : image.Row(y);
+ const auto* row_n = y > 0 ? image.Row(y - 1) : image.Row(y);
+ const auto* row_in = image.Row(y);
+ const auto* row_s = y + 1 < image.ysize() ? image.Row(y + 1) : image.Row(y);
auto* row_out = mask.Row(y);
for (size_t x = 0; x < image.xsize(); x++) {
// Center, west, east, north, south values and their absolute difference
@@ -258,7 +261,7 @@ void CreateMask(const ImageF& image, ImageF& mask) {
// by the decoder. Ringing is slightly reduced by clamping the values of the
// resulting pixels within certain bounds of a small region in the original
// image.
-void DownsampleImage2_Sharper(const ImageF& input, ImageF* output) {
+Status DownsampleImage2_Sharper(const ImageF& input, ImageF* output) {
const int64_t kernelx = 12;
const int64_t kernely = 12;
@@ -315,11 +318,12 @@ void DownsampleImage2_Sharper(const ImageF& input, ImageF* output) {
int64_t xsize = input.xsize();
int64_t ysize = input.ysize();
- ImageF box_downsample(xsize, ysize);
+ JXL_ASSIGN_OR_RETURN(ImageF box_downsample, ImageF::Create(xsize, ysize));
CopyImageTo(input, &box_downsample);
- DownsampleImage(&box_downsample, 2);
+ JXL_ASSIGN_OR_RETURN(box_downsample, DownsampleImage(box_downsample, 2));
- ImageF mask(box_downsample.xsize(), box_downsample.ysize());
+ JXL_ASSIGN_OR_RETURN(ImageF mask, ImageF::Create(box_downsample.xsize(),
+ box_downsample.ysize()));
CreateMask(box_downsample, mask);
for (size_t y = 0; y < output->ysize(); y++) {
@@ -379,50 +383,54 @@ void DownsampleImage2_Sharper(const ImageF& input, ImageF* output) {
}
}
}
+ return true;
}
} // namespace
-void DownsampleImage2_Sharper(Image3F* opsin) {
+Status DownsampleImage2_Sharper(Image3F* opsin) {
// Allocate extra space to avoid a reallocation when padding.
- Image3F downsampled(DivCeil(opsin->xsize(), 2) + kBlockDim,
- DivCeil(opsin->ysize(), 2) + kBlockDim);
+ JXL_ASSIGN_OR_RETURN(Image3F downsampled,
+ Image3F::Create(DivCeil(opsin->xsize(), 2) + kBlockDim,
+ DivCeil(opsin->ysize(), 2) + kBlockDim));
downsampled.ShrinkTo(downsampled.xsize() - kBlockDim,
downsampled.ysize() - kBlockDim);
for (size_t c = 0; c < 3; c++) {
- DownsampleImage2_Sharper(opsin->Plane(c), &downsampled.Plane(c));
+ JXL_RETURN_IF_ERROR(
+ DownsampleImage2_Sharper(opsin->Plane(c), &downsampled.Plane(c)));
}
*opsin = std::move(downsampled);
+ return true;
}
namespace {
// The default upsampling kernels used by Upsampler in the decoder.
-static const constexpr int64_t kSize = 5;
+const constexpr int64_t kSize = 5;
-static const float kernel00[25] = {
+const float kernel00[25] = {
-0.01716200f, -0.03452303f, -0.04022174f, -0.02921014f, -0.00624645f,
-0.03452303f, 0.14111091f, 0.28896755f, 0.00278718f, -0.01610267f,
-0.04022174f, 0.28896755f, 0.56661550f, 0.03777607f, -0.01986694f,
-0.02921014f, 0.00278718f, 0.03777607f, -0.03144731f, -0.01185068f,
-0.00624645f, -0.01610267f, -0.01986694f, -0.01185068f, -0.00213539f,
};
-static const float kernel01[25] = {
+const float kernel01[25] = {
-0.00624645f, -0.01610267f, -0.01986694f, -0.01185068f, -0.00213539f,
-0.02921014f, 0.00278718f, 0.03777607f, -0.03144731f, -0.01185068f,
-0.04022174f, 0.28896755f, 0.56661550f, 0.03777607f, -0.01986694f,
-0.03452303f, 0.14111091f, 0.28896755f, 0.00278718f, -0.01610267f,
-0.01716200f, -0.03452303f, -0.04022174f, -0.02921014f, -0.00624645f,
};
-static const float kernel10[25] = {
+const float kernel10[25] = {
-0.00624645f, -0.02921014f, -0.04022174f, -0.03452303f, -0.01716200f,
-0.01610267f, 0.00278718f, 0.28896755f, 0.14111091f, -0.03452303f,
-0.01986694f, 0.03777607f, 0.56661550f, 0.28896755f, -0.04022174f,
-0.01185068f, -0.03144731f, 0.03777607f, 0.00278718f, -0.02921014f,
-0.00213539f, -0.01185068f, -0.01986694f, -0.01610267f, -0.00624645f,
};
-static const float kernel11[25] = {
+const float kernel11[25] = {
-0.00213539f, -0.01185068f, -0.01986694f, -0.01610267f, -0.00624645f,
-0.01185068f, -0.03144731f, 0.03777607f, 0.00278718f, -0.02921014f,
-0.01986694f, 0.03777607f, 0.56661550f, 0.28896755f, -0.04022174f,
@@ -435,14 +443,14 @@ static const float kernel11[25] = {
// TODO(lode): use Upsampler instead. However, it requires pre-initialization
// and padding on the left side of the image which requires refactoring the
// other code using this.
-static void UpsampleImage(const ImageF& input, ImageF* output) {
+void UpsampleImage(const ImageF& input, ImageF* output) {
int64_t xsize = input.xsize();
int64_t ysize = input.ysize();
int64_t xsize2 = output->xsize();
int64_t ysize2 = output->ysize();
for (int64_t y = 0; y < ysize2; y++) {
for (int64_t x = 0; x < xsize2; x++) {
- auto kernel = kernel00;
+ const auto* kernel = kernel00;
if ((x & 1) && (y & 1)) {
kernel = kernel11;
} else if (x & 1) {
@@ -492,7 +500,7 @@ static void UpsampleImage(const ImageF& input, ImageF* output) {
// Returns the derivative of Upsampler, with respect to input pixel x2, y2, to
// output pixel x, y (ignoring the clamping).
float UpsamplerDeriv(int64_t x2, int64_t y2, int64_t x, int64_t y) {
- auto kernel = kernel00;
+ const auto* kernel = kernel00;
if ((x & 1) && (y & 1)) {
kernel = kernel11;
} else if (x & 1) {
@@ -597,11 +605,9 @@ void ReduceRinging(const ImageF& initial, const ImageF& mask, ImageF& down) {
float max = initial.Row(y)[x];
for (int64_t yi = -1; yi < 2; yi++) {
for (int64_t xi = -1; xi < 2; xi++) {
- int64_t x2 = (int64_t)x + xi;
- int64_t y2 = (int64_t)y + yi;
- if (x2 < 0 || y2 < 0 || x2 >= (int64_t)xsize2 ||
- y2 >= (int64_t)ysize2)
- continue;
+ int64_t x2 = static_cast<int64_t>(x) + xi;
+ int64_t y2 = static_cast<int64_t>(y) + yi;
+ if (x2 < 0 || y2 < 0 || x2 >= xsize2 || y2 >= ysize2) continue;
min = std::min<float>(min, initial.Row(y2)[x2]);
max = std::max<float>(max, initial.Row(y2)[x2]);
}
@@ -625,32 +631,35 @@ void ReduceRinging(const ImageF& initial, const ImageF& mask, ImageF& down) {
}
// TODO(lode): move this to a separate file enc_downsample.cc
-void DownsampleImage2_Iterative(const ImageF& orig, ImageF* output) {
+Status DownsampleImage2_Iterative(const ImageF& orig, ImageF* output) {
int64_t xsize = orig.xsize();
int64_t ysize = orig.ysize();
int64_t xsize2 = DivCeil(orig.xsize(), 2);
int64_t ysize2 = DivCeil(orig.ysize(), 2);
- ImageF box_downsample(xsize, ysize);
+ JXL_ASSIGN_OR_RETURN(ImageF box_downsample, ImageF::Create(xsize, ysize));
CopyImageTo(orig, &box_downsample);
- DownsampleImage(&box_downsample, 2);
- ImageF mask(box_downsample.xsize(), box_downsample.ysize());
+ JXL_ASSIGN_OR_RETURN(box_downsample, DownsampleImage(box_downsample, 2));
+ JXL_ASSIGN_OR_RETURN(ImageF mask, ImageF::Create(box_downsample.xsize(),
+ box_downsample.ysize()));
CreateMask(box_downsample, mask);
output->ShrinkTo(xsize2, ysize2);
// Initial result image using the sharper downsampling.
// Allocate extra space to avoid a reallocation when padding.
- ImageF initial(DivCeil(orig.xsize(), 2) + kBlockDim,
- DivCeil(orig.ysize(), 2) + kBlockDim);
+ JXL_ASSIGN_OR_RETURN(ImageF initial,
+ ImageF::Create(DivCeil(orig.xsize(), 2) + kBlockDim,
+ DivCeil(orig.ysize(), 2) + kBlockDim));
initial.ShrinkTo(initial.xsize() - kBlockDim, initial.ysize() - kBlockDim);
- DownsampleImage2_Sharper(orig, &initial);
+ JXL_RETURN_IF_ERROR(DownsampleImage2_Sharper(orig, &initial));
- ImageF down(initial.xsize(), initial.ysize());
+ JXL_ASSIGN_OR_RETURN(ImageF down,
+ ImageF::Create(initial.xsize(), initial.ysize()));
CopyImageTo(initial, &down);
- ImageF up(xsize, ysize);
- ImageF corr(xsize, ysize);
- ImageF corr2(xsize2, ysize2);
+ JXL_ASSIGN_OR_RETURN(ImageF up, ImageF::Create(xsize, ysize));
+ JXL_ASSIGN_OR_RETURN(ImageF corr, ImageF::Create(xsize, ysize));
+ JXL_ASSIGN_OR_RETURN(ImageF corr2, ImageF::Create(xsize2, ysize2));
// In the weights map, relatively higher values will allow less ringing but
// also less sharpness. With all constant values, it optimizes equally
@@ -659,25 +668,25 @@ void DownsampleImage2_Iterative(const ImageF& orig, ImageF* output) {
// TODO(lode): Make use of the weights field for anti-ringing and clamping,
// the values are all set to 1 for now, but it is intended to be used for
// reducing ringing based on the mask, and taking clamping into account.
- ImageF weights(xsize, ysize);
+ JXL_ASSIGN_OR_RETURN(ImageF weights, ImageF::Create(xsize, ysize));
for (size_t y = 0; y < weights.ysize(); y++) {
auto* row = weights.Row(y);
for (size_t x = 0; x < weights.xsize(); x++) {
row[x] = 1;
}
}
- ImageF weights2(xsize2, ysize2);
+ JXL_ASSIGN_OR_RETURN(ImageF weights2, ImageF::Create(xsize2, ysize2));
AntiUpsample(weights, &weights2);
const size_t num_it = 3;
for (size_t it = 0; it < num_it; ++it) {
UpsampleImage(down, &up);
- corr = LinComb<float>(1, orig, -1, up);
+ JXL_ASSIGN_OR_RETURN(corr, LinComb<float>(1, orig, -1, up));
ElwiseMul(corr, weights, &corr);
AntiUpsample(corr, &corr2);
ElwiseDiv(corr2, weights2, &corr2);
- down = LinComb<float>(1, down, 1, corr2);
+ JXL_ASSIGN_OR_RETURN(down, LinComb<float>(1, down, 1, corr2));
}
ReduceRinging(initial, mask, down);
@@ -690,32 +699,40 @@ void DownsampleImage2_Iterative(const ImageF& orig, ImageF* output) {
output->Row(y)[x] = v;
}
}
+ return true;
}
} // namespace
-void DownsampleImage2_Iterative(Image3F* opsin) {
+Status DownsampleImage2_Iterative(Image3F* opsin) {
// Allocate extra space to avoid a reallocation when padding.
- Image3F downsampled(DivCeil(opsin->xsize(), 2) + kBlockDim,
- DivCeil(opsin->ysize(), 2) + kBlockDim);
+ JXL_ASSIGN_OR_RETURN(Image3F downsampled,
+ Image3F::Create(DivCeil(opsin->xsize(), 2) + kBlockDim,
+ DivCeil(opsin->ysize(), 2) + kBlockDim));
downsampled.ShrinkTo(downsampled.xsize() - kBlockDim,
downsampled.ysize() - kBlockDim);
- Image3F rgb(opsin->xsize(), opsin->ysize());
+ JXL_ASSIGN_OR_RETURN(Image3F rgb,
+ Image3F::Create(opsin->xsize(), opsin->ysize()));
OpsinParams opsin_params; // TODO(user): use the ones that are actually used
opsin_params.Init(kDefaultIntensityTarget);
OpsinToLinear(*opsin, Rect(rgb), nullptr, &rgb, opsin_params);
- ImageF mask(opsin->xsize(), opsin->ysize());
+ JXL_ASSIGN_OR_RETURN(ImageF mask,
+ ImageF::Create(opsin->xsize(), opsin->ysize()));
ButteraugliParams butter_params;
- ButteraugliComparator butter(rgb, butter_params);
- butter.Mask(&mask);
- ImageF mask_fuzzy(opsin->xsize(), opsin->ysize());
+ JXL_ASSIGN_OR_RETURN(std::unique_ptr<ButteraugliComparator> butter,
+ ButteraugliComparator::Make(rgb, butter_params));
+ JXL_RETURN_IF_ERROR(butter->Mask(&mask));
+ JXL_ASSIGN_OR_RETURN(ImageF mask_fuzzy,
+ ImageF::Create(opsin->xsize(), opsin->ysize()));
for (size_t c = 0; c < 3; c++) {
- DownsampleImage2_Iterative(opsin->Plane(c), &downsampled.Plane(c));
+ JXL_RETURN_IF_ERROR(
+ DownsampleImage2_Iterative(opsin->Plane(c), &downsampled.Plane(c)));
}
*opsin = std::move(downsampled);
+ return true;
}
Status LossyFrameHeuristics(const FrameHeader& frame_header,
@@ -739,10 +756,11 @@ Status LossyFrameHeuristics(const FrameHeader& frame_header,
BlockCtxMap& block_ctx_map = shared.block_ctx_map;
// Find and subtract splines.
+ if (cparams.custom_splines.HasAny()) {
+ image_features.splines = cparams.custom_splines;
+ }
if (!streaming_mode && cparams.speed_tier <= SpeedTier::kSquirrel) {
- if (cparams.custom_splines.HasAny()) {
- image_features.splines = cparams.custom_splines;
- } else {
+ if (!cparams.custom_splines.HasAny()) {
image_features.splines = FindSplines(*opsin);
}
JXL_RETURN_IF_ERROR(image_features.splines.InitializeDrawCache(
@@ -754,7 +772,8 @@ Status LossyFrameHeuristics(const FrameHeader& frame_header,
if (!streaming_mode &&
ApplyOverride(cparams.patches,
cparams.speed_tier <= SpeedTier::kSquirrel)) {
- FindBestPatchDictionary(*opsin, enc_state, cms, pool, aux_out);
+ JXL_RETURN_IF_ERROR(
+ FindBestPatchDictionary(*opsin, enc_state, cms, pool, aux_out));
PatchDictionaryEncoder::SubtractFrom(image_features.patches, opsin);
}
@@ -791,10 +810,12 @@ Status LossyFrameHeuristics(const FrameHeader& frame_header,
// on simple heuristics in FindBestAcStrategy, or set a constant for Falcon
// mode.
if (cparams.speed_tier > SpeedTier::kHare) {
- initial_quant_field =
- ImageF(frame_dim.xsize_blocks, frame_dim.ysize_blocks);
- initial_quant_masking =
- ImageF(frame_dim.xsize_blocks, frame_dim.ysize_blocks);
+ JXL_ASSIGN_OR_RETURN(
+ initial_quant_field,
+ ImageF::Create(frame_dim.xsize_blocks, frame_dim.ysize_blocks));
+ JXL_ASSIGN_OR_RETURN(
+ initial_quant_masking,
+ ImageF::Create(frame_dim.xsize_blocks, frame_dim.ysize_blocks));
float q = 0.79 / cparams.butteraugli_distance;
FillImage(q, &initial_quant_field);
FillImage(1.0f / (q + 0.001f), &initial_quant_masking);
@@ -805,9 +826,11 @@ Status LossyFrameHeuristics(const FrameHeader& frame_header,
if (!frame_header.loop_filter.gab) {
butteraugli_distance_for_iqf *= 0.73f;
}
- initial_quant_field = InitialQuantField(
- butteraugli_distance_for_iqf, *opsin, rect, pool, 1.0f,
- &initial_quant_masking, &initial_quant_masking1x1);
+ JXL_ASSIGN_OR_RETURN(
+ initial_quant_field,
+ InitialQuantField(butteraugli_distance_for_iqf, *opsin, rect, pool,
+ 1.0f, &initial_quant_masking,
+ &initial_quant_masking1x1));
float q = 0.39 / cparams.butteraugli_distance;
quantizer.ComputeGlobalScaleAndQuant(quant_dc, q, 0);
}
@@ -822,18 +845,21 @@ Status LossyFrameHeuristics(const FrameHeader& frame_header,
0.99406123118127299f,
0.99719338015886894f,
};
- GaborishInverse(opsin, rect, weight, pool);
+ JXL_RETURN_IF_ERROR(GaborishInverse(opsin, rect, weight, pool));
}
if (initialize_global_state) {
- FindBestDequantMatrices(cparams, modular_frame_encoder, &matrices);
+ JXL_RETURN_IF_ERROR(
+ FindBestDequantMatrices(cparams, modular_frame_encoder, &matrices));
}
- cfl_heuristics.Init(rect);
+ JXL_RETURN_IF_ERROR(cfl_heuristics.Init(rect));
acs_heuristics.Init(*opsin, rect, initial_quant_field, initial_quant_masking,
initial_quant_masking1x1, &matrices);
+ std::atomic<bool> has_error{false};
auto process_tile = [&](const uint32_t tid, const size_t thread) {
+ if (has_error) return;
size_t n_enc_tiles = DivCeil(frame_dim.xsize_blocks, kEncTileDimInBlocks);
size_t tx = tid % n_enc_tiles;
size_t ty = tid / n_enc_tiles;
@@ -856,13 +882,16 @@ Status LossyFrameHeuristics(const FrameHeader& frame_header,
}
// Choose block sizes.
- acs_heuristics.ProcessRect(r, cmap, &ac_strategy);
+ acs_heuristics.ProcessRect(r, cmap, &ac_strategy, thread);
// Choose amount of post-processing smoothing.
// TODO(veluca): should this go *after* AdjustQuantField?
- ar_heuristics.RunRect(cparams, frame_header, r, *opsin, rect,
- initial_quant_field, ac_strategy, &epf_sharpness,
- thread);
+ if (!ar_heuristics.RunRect(cparams, frame_header, r, *opsin, rect,
+ initial_quant_field, ac_strategy, &epf_sharpness,
+ thread)) {
+ has_error = true;
+ return;
+ }
// Always set the initial quant field, so we can compute the CfL map with
// more accuracy. The initial quant field might change in slower modes, but
@@ -884,18 +913,21 @@ Status LossyFrameHeuristics(const FrameHeader& frame_header,
DivCeil(frame_dim.xsize_blocks, kEncTileDimInBlocks) *
DivCeil(frame_dim.ysize_blocks, kEncTileDimInBlocks),
[&](const size_t num_threads) {
+ acs_heuristics.PrepareForThreads(num_threads);
ar_heuristics.PrepareForThreads(num_threads);
cfl_heuristics.PrepareForThreads(num_threads);
return true;
},
process_tile, "Enc Heuristics"));
+ if (has_error) return JXL_FAILURE("Enc Heuristics failed");
- acs_heuristics.Finalize(frame_dim, ac_strategy, aux_out);
+ JXL_RETURN_IF_ERROR(acs_heuristics.Finalize(frame_dim, ac_strategy, aux_out));
// Refine quantization levels.
if (!streaming_mode) {
- FindBestQuantizer(frame_header, original_pixels, *opsin,
- initial_quant_field, enc_state, cms, pool, aux_out);
+ JXL_RETURN_IF_ERROR(FindBestQuantizer(frame_header, original_pixels, *opsin,
+ initial_quant_field, enc_state, cms,
+ pool, aux_out));
}
// Choose a context model that depends on the amount of quantization for AC.
diff --git a/third_party/jpeg-xl/lib/jxl/enc_heuristics.h b/third_party/jpeg-xl/lib/jxl/enc_heuristics.h
index 14cb596387..0dd93e4288 100644
--- a/third_party/jpeg-xl/lib/jxl/enc_heuristics.h
+++ b/third_party/jpeg-xl/lib/jxl/enc_heuristics.h
@@ -38,8 +38,8 @@ Status LossyFrameHeuristics(const FrameHeader& frame_header,
void FindBestBlockEntropyModel(PassesEncoderState& enc_state);
-void DownsampleImage2_Iterative(Image3F* output);
-void DownsampleImage2_Sharper(Image3F* opsin);
+Status DownsampleImage2_Iterative(Image3F* opsin);
+Status DownsampleImage2_Sharper(Image3F* opsin);
} // namespace jxl
diff --git a/third_party/jpeg-xl/lib/jxl/enc_huffman.cc b/third_party/jpeg-xl/lib/jxl/enc_huffman.cc
index 3eab2c218a..6af92f6ed5 100644
--- a/third_party/jpeg-xl/lib/jxl/enc_huffman.cc
+++ b/third_party/jpeg-xl/lib/jxl/enc_huffman.cc
@@ -70,6 +70,7 @@ void StoreHuffmanTreeToBitMask(const size_t huffman_tree_size,
for (size_t i = 0; i < huffman_tree_size; ++i) {
size_t ix = huffman_tree[i];
writer->Write(code_length_bitdepth[ix], code_length_bitdepth_symbols[ix]);
+ JXL_ASSERT(ix <= 17);
// Extra bits
switch (ix) {
case 16:
@@ -78,6 +79,9 @@ void StoreHuffmanTreeToBitMask(const size_t huffman_tree_size,
case 17:
writer->Write(3, huffman_tree_extra_bits[i]);
break;
+ default:
+ // no-op
+ break;
}
}
}
diff --git a/third_party/jpeg-xl/lib/jxl/enc_huffman_tree.cc b/third_party/jpeg-xl/lib/jxl/enc_huffman_tree.cc
index 5c40dea770..82ae4bccc7 100644
--- a/third_party/jpeg-xl/lib/jxl/enc_huffman_tree.cc
+++ b/third_party/jpeg-xl/lib/jxl/enc_huffman_tree.cc
@@ -85,7 +85,8 @@ void CreateHuffmanTree(const uint32_t* data, const size_t length,
size_t i = 0; // Points to the next leaf node.
size_t j = n + 1; // Points to the next non-leaf node.
for (size_t k = n - 1; k != 0; --k) {
- size_t left, right;
+ size_t left;
+ size_t right;
if (tree[i].total_count <= tree[j].total_count) {
left = i;
++i;
@@ -112,7 +113,7 @@ void CreateHuffmanTree(const uint32_t* data, const size_t length,
tree.push_back(sentinel);
}
JXL_DASSERT(tree.size() == 2 * n + 1);
- SetDepth(tree[2 * n - 1], &tree[0], depth, 0);
+ SetDepth(tree[2 * n - 1], tree.data(), depth, 0);
// We need to pack the Huffman tree in tree_limit bits.
// If this was not successful, add fake entities to the lowest values
diff --git a/third_party/jpeg-xl/lib/jxl/enc_icc_codec.cc b/third_party/jpeg-xl/lib/jxl/enc_icc_codec.cc
index 8e92fe3452..a29fb3f299 100644
--- a/third_party/jpeg-xl/lib/jxl/enc_icc_codec.cc
+++ b/third_party/jpeg-xl/lib/jxl/enc_icc_codec.cc
@@ -13,6 +13,7 @@
#include <vector>
#include "lib/jxl/base/byte_order.h"
+#include "lib/jxl/color_encoding_internal.h"
#include "lib/jxl/enc_ans.h"
#include "lib/jxl/enc_aux_out.h"
#include "lib/jxl/fields.h"
@@ -36,7 +37,8 @@ void Unshuffle(uint8_t* data, size_t size, size_t width) {
size_t height = (size + width - 1) / width; // amount of rows of input
PaddedBytes result(size);
// i = input index, j output index
- size_t s = 0, j = 0;
+ size_t s = 0;
+ size_t j = 0;
for (size_t i = 0; i < size; i++) {
result[j] = data[i];
j += height;
@@ -71,7 +73,7 @@ Status PredictAndShuffle(size_t stride, size_t width, int order, size_t num,
return true;
}
-static inline void EncodeVarInt(uint64_t value, PaddedBytes* data) {
+inline void EncodeVarInt(uint64_t value, PaddedBytes* data) {
size_t pos = data->size();
data->resize(data->size() + 9);
size_t output_size = data->size();
@@ -83,13 +85,13 @@ static inline void EncodeVarInt(uint64_t value, PaddedBytes* data) {
// TODO(eustas): should it be `<` ?
JXL_CHECK(pos <= output_size);
// |128: Set the next byte flag
- output[pos++] = ((uint8_t)(value & 127)) | 128;
+ output[pos++] = (static_cast<uint8_t>(value & 127)) | 128;
// Remove the seven bits we just wrote
value >>= 7;
}
// TODO(eustas): should it be `<` ?
JXL_CHECK(pos <= output_size);
- output[pos++] = ((uint8_t)value) & 127;
+ output[pos++] = static_cast<uint8_t>(value & 127);
data->resize(pos);
}
@@ -235,7 +237,9 @@ Status PredictICC(const uint8_t* icc, size_t size, PaddedBytes* result) {
// allowed for tagged elements to overlap, e.g. the curve for R, G and B could
// all point to the same one.
Tag tag;
- size_t tagstart = 0, tagsize = 0, clutstart = 0;
+ size_t tagstart = 0;
+ size_t tagsize = 0;
+ size_t clutstart = 0;
// Should always check tag_sane before doing math with tagsize.
const auto tag_sane = [&tagsize]() {
@@ -292,7 +296,9 @@ Status PredictICC(const uint8_t* icc, size_t size, PaddedBytes* result) {
commands_add.push_back(kCommandTypeStartFirst + 5);
pos += 8;
commands_add.push_back(kCommandPredict);
- int order = 1, width = 2, stride = width;
+ int order = 1;
+ int width = 2;
+ int stride = width;
commands_add.push_back((order << 2) | (width - 1));
EncodeVarInt(num, &commands_add);
JXL_RETURN_IF_ERROR(PredictAndShuffle(stride, width, order, num, icc,
@@ -310,7 +316,9 @@ Status PredictICC(const uint8_t* icc, size_t size, PaddedBytes* result) {
pos += 12;
last1 = pos;
commands_add.push_back(kCommandPredict);
- int order = 1, width = 2, stride = width;
+ int order = 1;
+ int width = 2;
+ int stride = width;
commands_add.push_back((order << 2) | (width - 1));
EncodeVarInt(num, &commands_add);
JXL_RETURN_IF_ERROR(PredictAndShuffle(stride, width, order, num, icc,
@@ -352,7 +360,9 @@ Status PredictICC(const uint8_t* icc, size_t size, PaddedBytes* result) {
if (commands_add.empty() && data_add.empty() && tag == kGbd_Tag &&
tag_sane() && pos == tagstart + 8 && pos + tagsize - 8 <= size &&
pos > 16) {
- size_t width = 4, order = 0, stride = width;
+ size_t width = 4;
+ size_t order = 0;
+ size_t stride = width;
size_t num = tagsize - 8;
uint8_t flags = (order << 2) | (width - 1) | (stride == width ? 0 : 16);
commands_add.push_back(kCommandPredict);
diff --git a/third_party/jpeg-xl/lib/jxl/enc_icc_codec.h b/third_party/jpeg-xl/lib/jxl/enc_icc_codec.h
index 224c2e5316..a99e11b19c 100644
--- a/third_party/jpeg-xl/lib/jxl/enc_icc_codec.h
+++ b/third_party/jpeg-xl/lib/jxl/enc_icc_codec.h
@@ -15,7 +15,6 @@
#include "lib/jxl/base/compiler_specific.h"
#include "lib/jxl/base/status.h"
-#include "lib/jxl/dec_bit_reader.h"
#include "lib/jxl/enc_bit_writer.h"
namespace jxl {
@@ -27,9 +26,6 @@ class PaddedBytes;
Status WriteICC(const std::vector<uint8_t>& icc, BitWriter* JXL_RESTRICT writer,
size_t layer, AuxOut* JXL_RESTRICT aux_out);
-// Exposed only for testing
-Status PredictICC(const uint8_t* icc, size_t size, PaddedBytes* result);
-
} // namespace jxl
#endif // LIB_JXL_ENC_ICC_CODEC_H_
diff --git a/third_party/jpeg-xl/lib/jxl/enc_image_bundle.cc b/third_party/jpeg-xl/lib/jxl/enc_image_bundle.cc
index 1b41361320..044f763363 100644
--- a/third_party/jpeg-xl/lib/jxl/enc_image_bundle.cc
+++ b/third_party/jpeg-xl/lib/jxl/enc_image_bundle.cc
@@ -8,13 +8,10 @@
#include <jxl/cms_interface.h>
#include <atomic>
-#include <limits>
#include <utility>
-#include "lib/jxl/alpha.h"
-#include "lib/jxl/base/byte_order.h"
+#include "lib/jxl/base/status.h"
#include "lib/jxl/color_encoding_internal.h"
-#include "lib/jxl/fields.h"
#include "lib/jxl/image_bundle.h"
namespace jxl {
@@ -30,11 +27,11 @@ Status ApplyColorTransform(const ColorEncoding& c_current,
JXL_CHECK(c_current.IsGray() == c_desired.IsGray());
bool is_gray = c_current.IsGray();
if (out->xsize() < rect.xsize() || out->ysize() < rect.ysize()) {
- *out = Image3F(rect.xsize(), rect.ysize());
+ JXL_ASSIGN_OR_RETURN(*out, Image3F::Create(rect.xsize(), rect.ysize()));
} else {
out->ShrinkTo(rect.xsize(), rect.ysize());
}
- std::atomic<bool> ok{true};
+ std::atomic<bool> has_error{false};
JXL_RETURN_IF_ERROR(RunOnPool(
pool, 0, rect.ysize(),
[&](const size_t num_threads) {
@@ -42,6 +39,7 @@ Status ApplyColorTransform(const ColorEncoding& c_current,
rect.xsize(), num_threads);
},
[&](const uint32_t y, const size_t thread) {
+ if (has_error) return;
float* mutable_src_buf = c_transform.BufSrc(thread);
const float* src_buf = mutable_src_buf;
// Interleave input.
@@ -49,7 +47,7 @@ Status ApplyColorTransform(const ColorEncoding& c_current,
src_buf = rect.ConstPlaneRow(color, 0, y);
} else if (c_current.IsCMYK()) {
if (!black) {
- ok.store(false);
+ has_error = true;
return;
}
const float* JXL_RESTRICT row_in0 = rect.ConstPlaneRow(color, 0, y);
@@ -74,8 +72,8 @@ Status ApplyColorTransform(const ColorEncoding& c_current,
}
}
float* JXL_RESTRICT dst_buf = c_transform.BufDst(thread);
- if (!c_transform.Run(thread, src_buf, dst_buf)) {
- ok.store(false);
+ if (!c_transform.Run(thread, src_buf, dst_buf, rect.xsize())) {
+ has_error = true;
return;
}
float* JXL_RESTRICT row_out0 = out->PlaneRow(0, y);
@@ -97,7 +95,8 @@ Status ApplyColorTransform(const ColorEncoding& c_current,
}
},
"Colorspace transform"));
- return ok.load();
+ if (has_error) return JXL_FAILURE("Colorspace transform failed");
+ return true;
}
namespace {
@@ -133,7 +132,8 @@ Status TransformIfNeeded(const ImageBundle& in, const ColorEncoding& c_desired,
}
// TODO(janwas): avoid copying via createExternal+copyBackToIO
// instead of copy+createExternal+copyBackToIO
- Image3F color(in.color().xsize(), in.color().ysize());
+ JXL_ASSIGN_OR_RETURN(Image3F color,
+ Image3F::Create(in.color().xsize(), in.color().ysize()));
CopyImageTo(in.color(), &color);
store->SetFromImage(std::move(color), in.c_current());
@@ -141,7 +141,8 @@ Status TransformIfNeeded(const ImageBundle& in, const ColorEncoding& c_desired,
if (in.HasExtraChannels()) {
std::vector<ImageF> extra_channels;
for (const ImageF& extra_channel : in.extra_channels()) {
- ImageF ec(extra_channel.xsize(), extra_channel.ysize());
+ JXL_ASSIGN_OR_RETURN(ImageF ec, ImageF::Create(extra_channel.xsize(),
+ extra_channel.ysize()));
CopyImageTo(extra_channel, &ec);
extra_channels.emplace_back(std::move(ec));
}
diff --git a/third_party/jpeg-xl/lib/jxl/enc_linalg.cc b/third_party/jpeg-xl/lib/jxl/enc_linalg.cc
index fe2090a909..452c17f4e8 100644
--- a/third_party/jpeg-xl/lib/jxl/enc_linalg.cc
+++ b/third_party/jpeg-xl/lib/jxl/enc_linalg.cc
@@ -7,46 +7,43 @@
#include <cmath>
-#include "lib/jxl/base/compiler_specific.h"
#include "lib/jxl/base/status.h"
namespace jxl {
-void ConvertToDiagonal(const ImageD& A, ImageD* const JXL_RESTRICT diag,
- ImageD* const JXL_RESTRICT U) {
+void ConvertToDiagonal(const Matrix2x2& A, Vector2& diag, Matrix2x2& U) {
#if JXL_ENABLE_ASSERT
- JXL_ASSERT(A.xsize() == 2);
- JXL_ASSERT(A.ysize() == 2);
- JXL_ASSERT(std::abs(A.Row(0)[1] - A.Row(1)[0]) < 1e-15);
+ // Check A is symmetric.
+ JXL_ASSERT(std::abs(A[0][1] - A[1][0]) < 1e-15);
#endif
- if (std::abs(A.ConstRow(0)[1]) < 1e-15) {
+ if (std::abs(A[0][1]) < 1e-15) {
// Already diagonal.
- diag->Row(0)[0] = A.ConstRow(0)[0];
- diag->Row(0)[1] = A.ConstRow(1)[1];
- U->Row(0)[0] = U->Row(1)[1] = 1.0;
- U->Row(0)[1] = U->Row(1)[0] = 0.0;
+ diag[0] = A[0][0];
+ diag[1] = A[1][1];
+ U[0][0] = U[1][1] = 1.0;
+ U[0][1] = U[1][0] = 0.0;
return;
}
- double b = -(A.Row(0)[0] + A.Row(1)[1]);
- double c = A.Row(0)[0] * A.Row(1)[1] - A.Row(0)[1] * A.Row(0)[1];
+ double b = -(A[0][0] + A[1][1]);
+ double c = A[0][0] * A[1][1] - A[0][1] * A[0][1];
double d = b * b - 4.0 * c;
double sqd = std::sqrt(d);
double l1 = (-b - sqd) * 0.5;
double l2 = (-b + sqd) * 0.5;
- double v1[2] = {A.Row(0)[0] - l1, A.Row(1)[0]};
+ Vector2 v1 = {A[0][0] - l1, A[1][0]};
double v1n = 1.0 / std::hypot(v1[0], v1[1]);
v1[0] = v1[0] * v1n;
v1[1] = v1[1] * v1n;
- diag->Row(0)[0] = l1;
- diag->Row(0)[1] = l2;
+ diag[0] = l1;
+ diag[1] = l2;
- U->Row(0)[0] = v1[1];
- U->Row(0)[1] = -v1[0];
- U->Row(1)[0] = v1[0];
- U->Row(1)[1] = v1[1];
+ U[0][0] = v1[1];
+ U[0][1] = -v1[0];
+ U[1][0] = v1[0];
+ U[1][1] = v1[1];
}
} // namespace jxl
diff --git a/third_party/jpeg-xl/lib/jxl/enc_linalg.h b/third_party/jpeg-xl/lib/jxl/enc_linalg.h
index 791770d5d4..b9a36c7ca1 100644
--- a/third_party/jpeg-xl/lib/jxl/enc_linalg.h
+++ b/third_party/jpeg-xl/lib/jxl/enc_linalg.h
@@ -8,16 +8,16 @@
// Linear algebra.
-#include "lib/jxl/base/compiler_specific.h"
-#include "lib/jxl/image.h"
+#include <array>
namespace jxl {
-using ImageD = Plane<double>;
+typedef std::array<double, 2> Vector2;
+// NB: matrix2x2[row][column]
+typedef std::array<Vector2, 2> Matrix2x2;
// A is symmetric, U is orthogonal, and A = U * Diagonal(diag) * Transpose(U).
-void ConvertToDiagonal(const ImageD& A, ImageD* JXL_RESTRICT diag,
- ImageD* JXL_RESTRICT U);
+void ConvertToDiagonal(const Matrix2x2& A, Vector2& diag, Matrix2x2& U);
} // namespace jxl
diff --git a/third_party/jpeg-xl/lib/jxl/enc_linalg_test.cc b/third_party/jpeg-xl/lib/jxl/enc_linalg_test.cc
index 967b9a3afb..c02f009ca7 100644
--- a/third_party/jpeg-xl/lib/jxl/enc_linalg_test.cc
+++ b/third_party/jpeg-xl/lib/jxl/enc_linalg_test.cc
@@ -5,110 +5,78 @@
#include "lib/jxl/enc_linalg.h"
-#include "lib/jxl/image_test_utils.h"
+#include "lib/jxl/base/random.h"
#include "lib/jxl/testing.h"
namespace jxl {
namespace {
-ImageD Identity(const size_t N) {
- ImageD out(N, N);
- for (size_t i = 0; i < N; ++i) {
- double* JXL_RESTRICT row = out.Row(i);
- std::fill(row, row + N, 0);
- row[i] = 1.0;
- }
- return out;
-}
+Matrix2x2 Diagonal(const Vector2& d) { return {{{d[0], 0.0}, {0.0, d[1]}}}; }
-ImageD Diagonal(const ImageD& d) {
- JXL_ASSERT(d.ysize() == 1);
- ImageD out(d.xsize(), d.xsize());
- const double* JXL_RESTRICT row_diag = d.Row(0);
- for (size_t k = 0; k < d.xsize(); ++k) {
- double* JXL_RESTRICT row_out = out.Row(k);
- std::fill(row_out, row_out + d.xsize(), 0.0);
- row_out[k] = row_diag[k];
- }
- return out;
-}
+Matrix2x2 Identity() { return Diagonal({1.0, 1.0}); }
-ImageD MatMul(const ImageD& A, const ImageD& B) {
- JXL_ASSERT(A.ysize() == B.xsize());
- ImageD out(A.xsize(), B.ysize());
- for (size_t y = 0; y < B.ysize(); ++y) {
- const double* const JXL_RESTRICT row_b = B.Row(y);
- double* const JXL_RESTRICT row_out = out.Row(y);
- for (size_t x = 0; x < A.xsize(); ++x) {
- row_out[x] = 0.0;
- for (size_t k = 0; k < B.xsize(); ++k) {
- row_out[x] += A.Row(k)[x] * row_b[k];
- }
+Matrix2x2 MatMul(const Matrix2x2& A, const Matrix2x2& B) {
+ Matrix2x2 out;
+ for (size_t y = 0; y < 2; ++y) {
+ for (size_t x = 0; x < 2; ++x) {
+ out[y][x] = A[0][x] * B[y][0] + A[1][x] * B[y][1];
}
}
return out;
}
-ImageD Transpose(const ImageD& A) {
- ImageD out(A.ysize(), A.xsize());
- for (size_t x = 0; x < A.xsize(); ++x) {
- double* const JXL_RESTRICT row_out = out.Row(x);
- for (size_t y = 0; y < A.ysize(); ++y) {
- row_out[y] = A.Row(y)[x];
- }
- }
- return out;
+Matrix2x2 Transpose(const Matrix2x2& A) {
+ return {{{A[0][0], A[1][0]}, {A[0][1], A[1][1]}}};
}
-ImageD RandomSymmetricMatrix(const size_t N, Rng& rng, const double vmin,
- const double vmax) {
- ImageD A(N, N);
- GenerateImage(rng, &A, vmin, vmax);
- for (size_t i = 0; i < N; ++i) {
- for (size_t j = 0; j < i; ++j) {
- A.Row(j)[i] = A.Row(i)[j];
- }
- }
+Matrix2x2 RandomSymmetricMatrix(Rng& rng, const double vmin,
+ const double vmax) {
+ Matrix2x2 A;
+ A[0][0] = rng.UniformF(vmin, vmax);
+ A[0][1] = A[1][0] = rng.UniformF(vmin, vmax);
+ A[1][1] = rng.UniformF(vmin, vmax);
return A;
}
-void VerifyMatrixEqual(const ImageD& A, const ImageD& B, const double eps) {
- ASSERT_EQ(A.xsize(), B.xsize());
- ASSERT_EQ(A.ysize(), B.ysize());
- for (size_t y = 0; y < A.ysize(); ++y) {
- for (size_t x = 0; x < A.xsize(); ++x) {
- ASSERT_NEAR(A.Row(y)[x], B.Row(y)[x], eps);
+void VerifyMatrixEqual(const Matrix2x2& A, const Matrix2x2& B,
+ const double eps) {
+ for (size_t y = 0; y < 2; ++y) {
+ for (size_t x = 0; x < 2; ++x) {
+ ASSERT_NEAR(A[y][x], B[y][x], eps);
}
}
}
-void VerifyOrthogonal(const ImageD& A, const double eps) {
- VerifyMatrixEqual(Identity(A.xsize()), MatMul(Transpose(A), A), eps);
+void VerifyOrthogonal(const Matrix2x2& A, const double eps) {
+ VerifyMatrixEqual(Identity(), MatMul(Transpose(A), A), eps);
}
TEST(LinAlgTest, ConvertToDiagonal) {
{
- ImageD I = Identity(2);
- ImageD U(2, 2), d(2, 1);
- ConvertToDiagonal(I, &d, &U);
+ Matrix2x2 I = Identity();
+ Matrix2x2 U;
+ Vector2 d;
+ ConvertToDiagonal(I, d, U);
VerifyMatrixEqual(I, U, 1e-15);
for (size_t k = 0; k < 2; ++k) {
- ASSERT_NEAR(d.Row(0)[k], 1.0, 1e-15);
+ ASSERT_NEAR(d[k], 1.0, 1e-15);
}
}
{
- ImageD A = Identity(2);
- A.Row(0)[1] = A.Row(1)[0] = 2.0;
- ImageD U(2, 2), d(2, 1);
- ConvertToDiagonal(A, &d, &U);
+ Matrix2x2 A = Identity();
+ A[0][1] = A[1][0] = 2.0;
+ Matrix2x2 U;
+ Vector2 d;
+ ConvertToDiagonal(A, d, U);
VerifyOrthogonal(U, 1e-12);
VerifyMatrixEqual(A, MatMul(U, MatMul(Diagonal(d), Transpose(U))), 1e-12);
}
Rng rng(0);
for (size_t i = 0; i < 100; ++i) {
- ImageD A = RandomSymmetricMatrix(2, rng, -1.0, 1.0);
- ImageD U(2, 2), d(2, 1);
- ConvertToDiagonal(A, &d, &U);
+ Matrix2x2 A = RandomSymmetricMatrix(rng, -1.0, 1.0);
+ Matrix2x2 U;
+ Vector2 d;
+ ConvertToDiagonal(A, d, U);
VerifyOrthogonal(U, 1e-12);
VerifyMatrixEqual(A, MatMul(U, MatMul(Diagonal(d), Transpose(U))), 1e-12);
}
diff --git a/third_party/jpeg-xl/lib/jxl/enc_modular.cc b/third_party/jpeg-xl/lib/jxl/enc_modular.cc
index b8366953b7..dbd62d4a01 100644
--- a/third_party/jpeg-xl/lib/jxl/enc_modular.cc
+++ b/third_party/jpeg-xl/lib/jxl/enc_modular.cc
@@ -10,8 +10,8 @@
#include <array>
#include <atomic>
+#include <cstdint>
#include <limits>
-#include <queue>
#include <utility>
#include <vector>
@@ -28,9 +28,9 @@
#include "lib/jxl/enc_params.h"
#include "lib/jxl/enc_patch_dictionary.h"
#include "lib/jxl/enc_quant_weights.h"
+#include "lib/jxl/frame_dimensions.h"
#include "lib/jxl/frame_header.h"
#include "lib/jxl/modular/encoding/context_predict.h"
-#include "lib/jxl/modular/encoding/enc_debug_tree.h"
#include "lib/jxl/modular/encoding/enc_encoding.h"
#include "lib/jxl/modular/encoding/encoding.h"
#include "lib/jxl/modular/encoding/ma_common.h"
@@ -38,7 +38,7 @@
#include "lib/jxl/modular/options.h"
#include "lib/jxl/modular/transform/enc_transform.h"
#include "lib/jxl/pack_signed.h"
-#include "lib/jxl/toc.h"
+#include "modular/options.h"
namespace jxl {
@@ -48,15 +48,15 @@ namespace {
// Squeeze default quantization factors
// these quantization factors are for -Q 50 (other qualities simply scale the
// factors; things are rounded down and obviously cannot get below 1)
-static const float squeeze_quality_factor =
+const float squeeze_quality_factor =
0.35; // for easy tweaking of the quality range (decrease this number for
// higher quality)
-static const float squeeze_luma_factor =
+const float squeeze_luma_factor =
1.1; // for easy tweaking of the balance between luma (or anything
// non-chroma) and chroma (decrease this number for higher quality
// luma)
-static const float squeeze_quality_factor_xyb = 2.4f;
-static const float squeeze_xyb_qtable[3][16] = {
+const float squeeze_quality_factor_xyb = 2.4f;
+const float squeeze_xyb_qtable[3][16] = {
{163.84, 81.92, 40.96, 20.48, 10.24, 5.12, 2.56, 1.28, 0.64, 0.32, 0.16,
0.08, 0.04, 0.02, 0.01, 0.005}, // Y
{1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1, 0.5, 0.5, 0.5, 0.5,
@@ -65,12 +65,12 @@ static const float squeeze_xyb_qtable[3][16] = {
0.5}, // B-Y
};
-static const float squeeze_luma_qtable[16] = {
- 163.84, 81.92, 40.96, 20.48, 10.24, 5.12, 2.56, 1.28,
- 0.64, 0.32, 0.16, 0.08, 0.04, 0.02, 0.01, 0.005};
+const float squeeze_luma_qtable[16] = {163.84, 81.92, 40.96, 20.48, 10.24, 5.12,
+ 2.56, 1.28, 0.64, 0.32, 0.16, 0.08,
+ 0.04, 0.02, 0.01, 0.005};
// for 8-bit input, the range of YCoCg chroma is -255..255 so basically this
// does 4:2:0 subsampling (two most fine grained layers get quantized away)
-static const float squeeze_chroma_qtable[16] = {
+const float squeeze_chroma_qtable[16] = {
1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1, 0.5, 0.5, 0.5, 0.5, 0.5};
// Merges the trees in `trees` using nodes that decide on stream_id, as defined
@@ -139,10 +139,12 @@ Status float_to_int(const float* const row_in, pixel_type* const row_out,
}
if (bits == 32 && fp) {
JXL_ASSERT(exp_bits == 8);
- memcpy((void*)row_out, (const void*)row_in, 4 * xsize);
+ memcpy(static_cast<void*>(row_out), static_cast<const void*>(row_in),
+ 4 * xsize);
return true;
}
+ JXL_ASSERT(bits > 0);
int exp_bias = (1 << (exp_bits - 1)) - 1;
int max_exp = (1 << exp_bits) - 1;
uint32_t sign = (1u << (bits - 1));
@@ -186,14 +188,144 @@ Status float_to_int(const float* const row_in, pixel_type* const row_out,
f = (signbit ? sign : 0);
f |= (exp << mant_bits);
f |= mantissa;
- row_out[x] = (pixel_type)f;
+ row_out[x] = static_cast<pixel_type>(f);
}
return true;
}
+
+float EstimateWPCost(const Image& img, size_t i) {
+ size_t extra_bits = 0;
+ float histo_cost = 0;
+ HybridUintConfig config;
+ int32_t cutoffs[] = {-500, -392, -255, -191, -127, -95, -63, -47, -31,
+ -23, -15, -11, -7, -4, -3, -1, 0, 1,
+ 3, 5, 7, 11, 15, 23, 31, 47, 63,
+ 95, 127, 191, 255, 392, 500};
+ constexpr size_t nc = sizeof(cutoffs) / sizeof(*cutoffs) + 1;
+ Histogram histo[nc] = {};
+ weighted::Header wp_header;
+ PredictorMode(i, &wp_header);
+ for (const Channel& ch : img.channel) {
+ const intptr_t onerow = ch.plane.PixelsPerRow();
+ weighted::State wp_state(wp_header, ch.w, ch.h);
+ Properties properties(1);
+ for (size_t y = 0; y < ch.h; y++) {
+ const pixel_type* JXL_RESTRICT r = ch.Row(y);
+ for (size_t x = 0; x < ch.w; x++) {
+ size_t offset = 0;
+ pixel_type_w left = (x ? r[x - 1] : y ? *(r + x - onerow) : 0);
+ pixel_type_w top = (y ? *(r + x - onerow) : left);
+ pixel_type_w topleft = (x && y ? *(r + x - 1 - onerow) : left);
+ pixel_type_w topright =
+ (x + 1 < ch.w && y ? *(r + x + 1 - onerow) : top);
+ pixel_type_w toptop = (y > 1 ? *(r + x - onerow - onerow) : top);
+ pixel_type guess = wp_state.Predict</*compute_properties=*/true>(
+ x, y, ch.w, top, left, topright, topleft, toptop, &properties,
+ offset);
+ size_t ctx = 0;
+ for (int c : cutoffs) {
+ ctx += (c >= properties[0]) ? 1 : 0;
+ }
+ pixel_type res = r[x] - guess;
+ uint32_t token;
+ uint32_t nbits;
+ uint32_t bits;
+ config.Encode(PackSigned(res), &token, &nbits, &bits);
+ histo[ctx].Add(token);
+ extra_bits += nbits;
+ wp_state.UpdateErrors(r[x], x, y, ch.w);
+ }
+ }
+ for (auto& h : histo) {
+ histo_cost += h.ShannonEntropy();
+ h.Clear();
+ }
+ }
+ return histo_cost + extra_bits;
+}
+
+float EstimateCost(const Image& img) {
+ // TODO(veluca): consider SIMDfication of this code.
+ size_t extra_bits = 0;
+ float histo_cost = 0;
+ HybridUintConfig config;
+ uint32_t cutoffs[] = {0, 1, 3, 5, 7, 11, 15, 23, 31,
+ 47, 63, 95, 127, 191, 255, 392, 500};
+ constexpr size_t nc = sizeof(cutoffs) / sizeof(*cutoffs) + 1;
+ Histogram histo[nc] = {};
+ for (const Channel& ch : img.channel) {
+ const intptr_t onerow = ch.plane.PixelsPerRow();
+ for (size_t y = 0; y < ch.h; y++) {
+ const pixel_type* JXL_RESTRICT r = ch.Row(y);
+ for (size_t x = 0; x < ch.w; x++) {
+ pixel_type_w left = (x ? r[x - 1] : y ? *(r + x - onerow) : 0);
+ pixel_type_w top = (y ? *(r + x - onerow) : left);
+ pixel_type_w topleft = (x && y ? *(r + x - 1 - onerow) : left);
+ size_t maxdiff = std::max(std::max(left, top), topleft) -
+ std::min(std::min(left, top), topleft);
+ size_t ctx = 0;
+ for (uint32_t c : cutoffs) {
+ ctx += (c > maxdiff) ? 1 : 0;
+ }
+ pixel_type res = r[x] - ClampedGradient(top, left, topleft);
+ uint32_t token;
+ uint32_t nbits;
+ uint32_t bits;
+ config.Encode(PackSigned(res), &token, &nbits, &bits);
+ histo[ctx].Add(token);
+ extra_bits += nbits;
+ }
+ }
+ for (auto& h : histo) {
+ histo_cost += h.ShannonEntropy();
+ h.Clear();
+ }
+ }
+ return histo_cost + extra_bits;
+}
+
+bool do_transform(Image& image, const Transform& tr,
+ const weighted::Header& wp_header,
+ jxl::ThreadPool* pool = nullptr, bool force_jxlart = false) {
+ Transform t = tr;
+ bool did_it = true;
+ if (force_jxlart) {
+ if (!t.MetaApply(image)) return false;
+ } else {
+ did_it = TransformForward(t, image, wp_header, pool);
+ }
+ if (did_it) image.transform.push_back(t);
+ return did_it;
+}
+
+bool maybe_do_transform(Image& image, const Transform& tr,
+ const CompressParams& cparams,
+ const weighted::Header& wp_header,
+ jxl::ThreadPool* pool = nullptr,
+ bool force_jxlart = false) {
+ if (force_jxlart || cparams.speed_tier >= SpeedTier::kSquirrel) {
+ return do_transform(image, tr, wp_header, pool, force_jxlart);
+ }
+ float cost_before = EstimateCost(image);
+ bool did_it = do_transform(image, tr, wp_header, pool);
+ if (did_it) {
+ float cost_after = EstimateCost(image);
+ JXL_DEBUG_V(7, "Cost before: %f cost after: %f", cost_before, cost_after);
+ if (cost_after > cost_before) {
+ Transform t = image.transform.back();
+ JXL_RETURN_IF_ERROR(t.Inverse(image, wp_header, pool));
+ image.transform.pop_back();
+ did_it = false;
+ }
+ }
+ return did_it;
+}
+
} // namespace
ModularFrameEncoder::ModularFrameEncoder(const FrameHeader& frame_header,
- const CompressParams& cparams_orig)
+ const CompressParams& cparams_orig,
+ bool streaming_mode)
: frame_dim_(frame_header.ToFrameDimensions()), cparams_(cparams_orig) {
size_t num_streams =
ModularStreamId::Num(frame_dim_, frame_header.passes.num_passes);
@@ -253,10 +385,16 @@ ModularFrameEncoder::ModularFrameEncoder(const FrameHeader& frame_header,
// Same, but for the non-Squeeze case.
prop_order = {0, 1, 15, 9, 10, 11, 12, 13, 14, 2, 3, 4, 5, 6, 7, 8};
// if few groups, don't use group as a property
- if (num_streams < 30 && cparams_.speed_tier > SpeedTier::kTortoise) {
+ if (num_streams < 30 && cparams_.speed_tier > SpeedTier::kTortoise &&
+ cparams_orig.ModularPartIsLossless()) {
prop_order.erase(prop_order.begin() + 1);
}
}
+ int max_properties = std::min<int>(
+ cparams_.options.max_properties,
+ static_cast<int>(
+ frame_header.nonserialized_metadata->m.num_extra_channels) +
+ (frame_header.encoding == FrameEncoding::kModular ? 2 : -1));
switch (cparams_.speed_tier) {
case SpeedTier::kHare:
cparams_.options.splitting_heuristics_properties.assign(
@@ -278,6 +416,7 @@ ModularFrameEncoder::ModularFrameEncoder(const FrameHeader& frame_header,
prop_order.begin(), prop_order.begin() + 10);
cparams_.options.max_property_values = 96;
break;
+ case SpeedTier::kGlacier:
case SpeedTier::kTortoise:
cparams_.options.splitting_heuristics_properties = prop_order;
cparams_.options.max_property_values = 256;
@@ -290,24 +429,36 @@ ModularFrameEncoder::ModularFrameEncoder(const FrameHeader& frame_header,
}
if (cparams_.speed_tier > SpeedTier::kTortoise) {
// Gradient in previous channels.
- for (int i = 0; i < cparams_.options.max_properties; i++) {
+ for (int i = 0; i < max_properties; i++) {
cparams_.options.splitting_heuristics_properties.push_back(
kNumNonrefProperties + i * 4 + 3);
}
} else {
// All the extra properties in Tortoise mode.
- for (int i = 0; i < cparams_.options.max_properties * 4; i++) {
+ for (int i = 0; i < max_properties * 4; i++) {
cparams_.options.splitting_heuristics_properties.push_back(
kNumNonrefProperties + i);
}
}
}
- if (cparams_.options.predictor == static_cast<Predictor>(-1)) {
+ if ((cparams_.options.predictor == Predictor::Average0 ||
+ cparams_.options.predictor == Predictor::Average1 ||
+ cparams_.options.predictor == Predictor::Average2 ||
+ cparams_.options.predictor == Predictor::Average3 ||
+ cparams_.options.predictor == Predictor::Average4 ||
+ cparams_.options.predictor == Predictor::Weighted) &&
+ !cparams_.ModularPartIsLossless()) {
+ // Lossy + Average/Weighted predictors does not work, so switch to default
+ // predictors.
+ cparams_.options.predictor = kUndefinedPredictor;
+ }
+
+ if (cparams_.options.predictor == kUndefinedPredictor) {
// no explicit predictor(s) given, set a good default
- if ((cparams_.speed_tier <= SpeedTier::kTortoise ||
+ if ((cparams_.speed_tier <= SpeedTier::kGlacier ||
cparams_.modular_mode == false) &&
- cparams_.IsLossless() && cparams_.responsive == false) {
+ cparams_.IsLossless() && cparams_.responsive == JXL_FALSE) {
// TODO(veluca): allow all predictors that don't break residual
// multipliers in lossy mode.
cparams_.options.predictor = Predictor::Variable;
@@ -354,48 +505,54 @@ ModularFrameEncoder::ModularFrameEncoder(const FrameHeader& frame_header,
// TODO(veluca): figure out how to use different predictor sets per channel.
stream_options_.resize(num_streams, cparams_.options);
-}
-bool do_transform(Image& image, const Transform& tr,
- const weighted::Header& wp_header,
- jxl::ThreadPool* pool = nullptr, bool force_jxlart = false) {
- Transform t = tr;
- bool did_it = true;
- if (force_jxlart) {
- if (!t.MetaApply(image)) return false;
- } else {
- did_it = TransformForward(t, image, wp_header, pool);
+ stream_options_[0] = cparams_.options;
+ if (cparams_.speed_tier == SpeedTier::kFalcon) {
+ stream_options_[0].tree_kind = ModularOptions::TreeKind::kWPFixedDC;
+ } else if (cparams_.speed_tier == SpeedTier::kThunder) {
+ stream_options_[0].tree_kind = ModularOptions::TreeKind::kGradientFixedDC;
}
- if (did_it) image.transform.push_back(t);
- return did_it;
+ stream_options_[0].histogram_params =
+ HistogramParams::ForModular(cparams_, {}, streaming_mode);
}
Status ModularFrameEncoder::ComputeEncodingData(
const FrameHeader& frame_header, const ImageMetadata& metadata,
Image3F* JXL_RESTRICT color, const std::vector<ImageF>& extra_channels,
- PassesEncoderState* JXL_RESTRICT enc_state, const JxlCmsInterface& cms,
- ThreadPool* pool, AuxOut* aux_out, bool do_color) {
+ const Rect& group_rect, const FrameDimensions& patch_dim,
+ const Rect& frame_area_rect, PassesEncoderState* JXL_RESTRICT enc_state,
+ const JxlCmsInterface& cms, ThreadPool* pool, AuxOut* aux_out,
+ bool do_color) {
JXL_DEBUG_V(6, "Computing modular encoding data for frame %s",
frame_header.DebugString().c_str());
- if (do_color && frame_header.loop_filter.gab) {
+ bool groupwise = enc_state->streaming_mode;
+
+ if (do_color && frame_header.loop_filter.gab && !groupwise) {
float w = 0.9908511000000001f;
float weights[3] = {w, w, w};
- GaborishInverse(color, Rect(*color), weights, pool);
+ JXL_RETURN_IF_ERROR(GaborishInverse(color, Rect(*color), weights, pool));
}
if (do_color && metadata.bit_depth.bits_per_sample <= 16 &&
cparams_.speed_tier < SpeedTier::kCheetah &&
- cparams_.decoding_speed_tier < 2) {
- FindBestPatchDictionary(*color, enc_state, cms, nullptr, aux_out,
- cparams_.color_transform == ColorTransform::kXYB);
+ cparams_.decoding_speed_tier < 2 && !groupwise) {
+ JXL_RETURN_IF_ERROR(FindBestPatchDictionary(
+ *color, enc_state, cms, nullptr, aux_out,
+ cparams_.color_transform == ColorTransform::kXYB));
PatchDictionaryEncoder::SubtractFrom(
enc_state->shared.image_features.patches, color);
}
+ if (cparams_.custom_splines.HasAny()) {
+ PassesSharedState& shared = enc_state->shared;
+ ImageFeatures& image_features = shared.image_features;
+ image_features.splines = cparams_.custom_splines;
+ }
+
// Convert ImageBundle to modular Image object
- const size_t xsize = frame_dim_.xsize;
- const size_t ysize = frame_dim_.ysize;
+ const size_t xsize = patch_dim.xsize;
+ const size_t ysize = patch_dim.ysize;
int nb_chans = 3;
if (metadata.color_encoding.IsGray() &&
@@ -423,7 +580,9 @@ Status ModularFrameEncoder::ComputeEncodingData(
int max_bitdepth =
do_color ? metadata.bit_depth.bits_per_sample + (fp ? 0 : 1) : 0;
Image& gi = stream_images_[0];
- gi = Image(xsize, ysize, metadata.bit_depth.bits_per_sample, nb_chans);
+ JXL_ASSIGN_OR_RETURN(
+ gi, Image::Create(xsize, ysize, metadata.bit_depth.bits_per_sample,
+ nb_chans));
int c = 0;
if (cparams_.color_transform == ColorTransform::kXYB &&
cparams_.modular_mode == true) {
@@ -478,17 +637,21 @@ Status ModularFrameEncoder::ComputeEncodingData(
gi.channel[c_out].vshift = frame_header.chroma_subsampling.VShift(c);
size_t xsize_shifted = DivCeil(xsize, 1 << gi.channel[c_out].hshift);
size_t ysize_shifted = DivCeil(ysize, 1 << gi.channel[c_out].vshift);
- gi.channel[c_out].shrink(xsize_shifted, ysize_shifted);
+ JXL_RETURN_IF_ERROR(
+ gi.channel[c_out].shrink(xsize_shifted, ysize_shifted));
std::atomic<bool> has_error{false};
JXL_RETURN_IF_ERROR(RunOnPool(
pool, 0, ysize_shifted, ThreadPool::NoInit,
[&](const int task, const int thread) {
+ if (has_error) return;
const size_t y = task;
- const float* const JXL_RESTRICT row_in = color->PlaneRow(c, y);
+ const float* const JXL_RESTRICT row_in =
+ color->PlaneRow(c, y + group_rect.y0()) + group_rect.x0();
pixel_type* const JXL_RESTRICT row_out = gi.channel[c_out].Row(y);
if (!float_to_int(row_in, row_out, xsize_shifted, bits, exp_bits,
fp, factor)) {
has_error = true;
+ return;
};
},
"float2int"));
@@ -505,8 +668,9 @@ Status ModularFrameEncoder::ComputeEncodingData(
for (size_t ec = 0; ec < extra_channels.size(); ec++, c++) {
const ExtraChannelInfo& eci = metadata.extra_channel_info[ec];
size_t ecups = frame_header.extra_channel_upsampling[ec];
- gi.channel[c].shrink(DivCeil(frame_dim_.xsize_upsampled, ecups),
- DivCeil(frame_dim_.ysize_upsampled, ecups));
+ JXL_RETURN_IF_ERROR(
+ gi.channel[c].shrink(DivCeil(patch_dim.xsize_upsampled, ecups),
+ DivCeil(patch_dim.ysize_upsampled, ecups)));
gi.channel[c].hshift = gi.channel[c].vshift =
CeilLog2Nonzero(ecups) - CeilLog2Nonzero(frame_header.upsampling);
@@ -519,12 +683,15 @@ Status ModularFrameEncoder::ComputeEncodingData(
JXL_RETURN_IF_ERROR(RunOnPool(
pool, 0, gi.channel[c].plane.ysize(), ThreadPool::NoInit,
[&](const int task, const int thread) {
+ if (has_error) return;
const size_t y = task;
- const float* const JXL_RESTRICT row_in = extra_channels[ec].Row(y);
+ const float* const JXL_RESTRICT row_in =
+ extra_channels[ec].Row(y + group_rect.y0()) + group_rect.x0();
pixel_type* const JXL_RESTRICT row_out = gi.channel[c].Row(y);
if (!float_to_int(row_in, row_out, gi.channel[c].plane.xsize(), bits,
exp_bits, fp, factor)) {
has_error = true;
+ return;
};
},
"float2int"));
@@ -533,11 +700,12 @@ Status ModularFrameEncoder::ComputeEncodingData(
JXL_ASSERT(c == nb_chans);
int level_max_bitdepth = (cparams_.level == 5 ? 16 : 32);
- if (max_bitdepth > level_max_bitdepth)
+ if (max_bitdepth > level_max_bitdepth) {
return JXL_FAILURE(
"Bitdepth too high for level %i (need %i bits, have only %i in this "
"level)",
cparams_.level, max_bitdepth, level_max_bitdepth);
+ }
// Set options and apply transformations
if (!cparams_.ModularPartIsLossless()) {
@@ -553,14 +721,14 @@ Status ModularFrameEncoder::ComputeEncodingData(
}
// Global palette
- if (cparams_.palette_colors != 0 || cparams_.lossy_palette) {
+ if ((cparams_.palette_colors != 0 || cparams_.lossy_palette) && !groupwise) {
// all-channel palette (e.g. RGBA)
if (gi.channel.size() - gi.nb_meta_channels > 1) {
Transform maybe_palette(TransformId::kPalette);
maybe_palette.begin_c = gi.nb_meta_channels;
maybe_palette.num_c = gi.channel.size() - gi.nb_meta_channels;
- maybe_palette.nb_colors =
- std::min((int)(xsize * ysize / 2), std::abs(cparams_.palette_colors));
+ maybe_palette.nb_colors = std::min(static_cast<int>(xsize * ysize / 2),
+ std::abs(cparams_.palette_colors));
maybe_palette.ordered_palette = cparams_.palette_colors >= 0;
maybe_palette.lossy_palette =
(cparams_.lossy_palette && maybe_palette.num_c == 3);
@@ -569,8 +737,8 @@ Status ModularFrameEncoder::ComputeEncodingData(
}
// TODO(veluca): use a custom weighted header if using the weighted
// predictor.
- do_transform(gi, maybe_palette, weighted::Header(), pool,
- cparams_.options.zero_tokens);
+ maybe_do_transform(gi, maybe_palette, cparams_, weighted::Header(), pool,
+ cparams_.options.zero_tokens);
}
// all-minus-one-channel palette (RGB with separate alpha, or CMY with
// separate K)
@@ -578,20 +746,20 @@ Status ModularFrameEncoder::ComputeEncodingData(
Transform maybe_palette_3(TransformId::kPalette);
maybe_palette_3.begin_c = gi.nb_meta_channels;
maybe_palette_3.num_c = gi.channel.size() - gi.nb_meta_channels - 1;
- maybe_palette_3.nb_colors =
- std::min((int)(xsize * ysize / 3), std::abs(cparams_.palette_colors));
+ maybe_palette_3.nb_colors = std::min(static_cast<int>(xsize * ysize / 3),
+ std::abs(cparams_.palette_colors));
maybe_palette_3.ordered_palette = cparams_.palette_colors >= 0;
maybe_palette_3.lossy_palette = cparams_.lossy_palette;
if (maybe_palette_3.lossy_palette) {
maybe_palette_3.predictor = delta_pred_;
}
- do_transform(gi, maybe_palette_3, weighted::Header(), pool,
- cparams_.options.zero_tokens);
+ maybe_do_transform(gi, maybe_palette_3, cparams_, weighted::Header(),
+ pool, cparams_.options.zero_tokens);
}
}
// Global channel palette
- if (cparams_.channel_colors_pre_transform_percent > 0 &&
+ if (!groupwise && cparams_.channel_colors_pre_transform_percent > 0 &&
!cparams_.lossy_palette &&
(cparams_.speed_tier <= SpeedTier::kThunder ||
(do_color && metadata.bit_depth.bits_per_sample > 8))) {
@@ -600,9 +768,10 @@ Status ModularFrameEncoder::ComputeEncodingData(
int orig_bitdepth = max_bitdepth;
max_bitdepth = 0;
for (size_t i = 0; i < nb_channels; i++) {
- int32_t min, max;
+ int32_t min;
+ int32_t max;
compute_minmax(gi.channel[gi.nb_meta_channels + i], &min, &max);
- int64_t colors = (int64_t)max - min + 1;
+ int64_t colors = static_cast<int64_t>(max) - min + 1;
JXL_DEBUG_V(10, "Channel %" PRIuS ": range=%i..%i", i, min, max);
Transform maybe_palette_1(TransformId::kPalette);
maybe_palette_1.begin_c = i + gi.nb_meta_channels;
@@ -612,9 +781,11 @@ Status ModularFrameEncoder::ComputeEncodingData(
// (but only if the channel palette is less than 6% the size of the
// image itself)
maybe_palette_1.nb_colors = std::min(
- (int)(xsize * ysize / 16),
- (int)(cparams_.channel_colors_pre_transform_percent / 100. * colors));
- if (do_transform(gi, maybe_palette_1, weighted::Header(), pool)) {
+ static_cast<int>(xsize * ysize / 16),
+ static_cast<int>(cparams_.channel_colors_pre_transform_percent /
+ 100. * colors));
+ if (maybe_do_transform(gi, maybe_palette_1, cparams_, weighted::Header(),
+ pool)) {
// effective bit depth is lower, adjust quantization accordingly
compute_minmax(gi.channel[gi.nb_meta_channels + i], &min, &max);
if (max < maxval) maxval = max;
@@ -646,8 +817,28 @@ Status ModularFrameEncoder::ComputeEncodingData(
}
}
+ if (cparams_.move_to_front_from_channel > 0) {
+ for (size_t tgt = 0;
+ tgt + cparams_.move_to_front_from_channel < gi.channel.size(); tgt++) {
+ size_t pos = cparams_.move_to_front_from_channel;
+ while (pos > 0) {
+ Transform move(TransformId::kRCT);
+ if (pos == 1) {
+ move.begin_c = tgt;
+ move.rct_type = 28; // RGB -> GRB
+ pos -= 1;
+ } else {
+ move.begin_c = tgt + pos - 2;
+ move.rct_type = 14; // RGB -> BRG
+ pos -= 2;
+ }
+ do_transform(gi, move, weighted::Header(), pool);
+ }
+ }
+ }
+
// don't do squeeze if we don't have some spare bits
- if (cparams_.responsive && !gi.channel.empty() &&
+ if (!groupwise && cparams_.responsive && !gi.channel.empty() &&
max_bitdepth + 2 < level_max_bitdepth) {
Transform t(TransformId::kSqueeze);
do_transform(gi, t, weighted::Header(), pool);
@@ -674,8 +865,8 @@ Status ModularFrameEncoder::ComputeEncodingData(
bitdepth_correction = maxval / 255.f;
}
std::vector<float> quantizers;
- float dist = cparams_.butteraugli_distance;
for (size_t i = 0; i < 3; i++) {
+ float dist = cparams_.butteraugli_distance;
quantizers.push_back(quantizer * dist * bitdepth_correction);
}
for (size_t i = 0; i < extra_channels.size(); i++) {
@@ -683,6 +874,7 @@ Status ModularFrameEncoder::ComputeEncodingData(
metadata.extra_channel_info[i].bit_depth.bits_per_sample;
pixel_type ec_maxval = ec_bitdepth < 32 ? (1u << ec_bitdepth) - 1 : 0;
bitdepth_correction = ec_maxval / 255.f;
+ float dist = 0;
if (i < cparams_.ec_distance.size()) dist = cparams_.ec_distance[i];
if (dist < 0) dist = cparams_.butteraugli_distance;
quantizers.push_back(quantizer * dist * bitdepth_correction);
@@ -722,57 +914,57 @@ Status ModularFrameEncoder::ComputeEncodingData(
}
// Fill other groups.
- struct GroupParams {
- Rect rect;
- int minShift;
- int maxShift;
- ModularStreamId id;
- };
- std::vector<GroupParams> stream_params;
-
- stream_options_[0] = cparams_.options;
-
// DC
- for (size_t group_id = 0; group_id < frame_dim_.num_dc_groups; group_id++) {
- const size_t gx = group_id % frame_dim_.xsize_dc_groups;
- const size_t gy = group_id / frame_dim_.xsize_dc_groups;
- const Rect rect(gx * frame_dim_.dc_group_dim, gy * frame_dim_.dc_group_dim,
- frame_dim_.dc_group_dim, frame_dim_.dc_group_dim);
+ for (size_t group_id = 0; group_id < patch_dim.num_dc_groups; group_id++) {
+ const size_t rgx = group_id % patch_dim.xsize_dc_groups;
+ const size_t rgy = group_id / patch_dim.xsize_dc_groups;
+ const Rect rect(rgx * patch_dim.dc_group_dim, rgy * patch_dim.dc_group_dim,
+ patch_dim.dc_group_dim, patch_dim.dc_group_dim);
+ size_t gx = rgx + frame_area_rect.x0() / 2048;
+ size_t gy = rgy + frame_area_rect.y0() / 2048;
+ size_t real_group_id = gy * frame_dim_.xsize_dc_groups + gx;
// minShift==3 because (frame_dim.dc_group_dim >> 3) == frame_dim.group_dim
// maxShift==1000 is infinity
- stream_params.push_back(
- GroupParams{rect, 3, 1000, ModularStreamId::ModularDC(group_id)});
+ stream_params_.push_back(
+ GroupParams{rect, 3, 1000, ModularStreamId::ModularDC(real_group_id)});
}
// AC global -> nothing.
// AC
- for (size_t group_id = 0; group_id < frame_dim_.num_groups; group_id++) {
- const size_t gx = group_id % frame_dim_.xsize_groups;
- const size_t gy = group_id / frame_dim_.xsize_groups;
- const Rect mrect(gx * frame_dim_.group_dim, gy * frame_dim_.group_dim,
- frame_dim_.group_dim, frame_dim_.group_dim);
+ for (size_t group_id = 0; group_id < patch_dim.num_groups; group_id++) {
+ const size_t rgx = group_id % patch_dim.xsize_groups;
+ const size_t rgy = group_id / patch_dim.xsize_groups;
+ const Rect mrect(rgx * patch_dim.group_dim, rgy * patch_dim.group_dim,
+ patch_dim.group_dim, patch_dim.group_dim);
+ size_t gx = rgx + frame_area_rect.x0() / (frame_dim_.group_dim);
+ size_t gy = rgy + frame_area_rect.y0() / (frame_dim_.group_dim);
+ size_t real_group_id = gy * frame_dim_.xsize_groups + gx;
for (size_t i = 0; i < enc_state->progressive_splitter.GetNumPasses();
i++) {
- int maxShift, minShift;
+ int maxShift;
+ int minShift;
frame_header.passes.GetDownsamplingBracket(i, minShift, maxShift);
- stream_params.push_back(GroupParams{
- mrect, minShift, maxShift, ModularStreamId::ModularAC(group_id, i)});
+ stream_params_.push_back(
+ GroupParams{mrect, minShift, maxShift,
+ ModularStreamId::ModularAC(real_group_id, i)});
}
}
// if there's only one group, everything ends up in GlobalModular
// in that case, also try RCTs/WP params for the one group
- if (stream_params.size() == 2) {
- stream_params.push_back(GroupParams{Rect(0, 0, xsize, ysize), 0, 1000,
- ModularStreamId::Global()});
+ if (stream_params_.size() == 2) {
+ stream_params_.push_back(GroupParams{Rect(0, 0, xsize, ysize), 0, 1000,
+ ModularStreamId::Global()});
}
gi_channel_.resize(stream_images_.size());
JXL_RETURN_IF_ERROR(RunOnPool(
- pool, 0, stream_params.size(), ThreadPool::NoInit,
+ pool, 0, stream_params_.size(), ThreadPool::NoInit,
[&](const uint32_t i, size_t /* thread */) {
- stream_options_[stream_params[i].id.ID(frame_dim_)] = cparams_.options;
+ size_t stream = stream_params_[i].id.ID(frame_dim_);
+ stream_options_[stream] = stream_options_[0];
JXL_CHECK(PrepareStreamParams(
- stream_params[i].rect, cparams_, stream_params[i].minShift,
- stream_params[i].maxShift, stream_params[i].id, do_color));
+ stream_params_[i].rect, cparams_, stream_params_[i].minShift,
+ stream_params_[i].maxShift, stream_params_[i].id, do_color,
+ groupwise));
},
"ChooseParams"));
{
@@ -821,7 +1013,7 @@ Status ModularFrameEncoder::ComputeTree(ThreadPool* pool) {
StaticPropRange range;
range[0] = {{i, i + 1}};
range[1] = {{stream_id, stream_id + 1}};
- multiplier_info.push_back({range, (uint32_t)q});
+ multiplier_info.push_back({range, static_cast<uint32_t>(q)});
} else {
// Previous channel in the same group had the same quantization
// factor. Don't provide two different ranges, as that creates
@@ -922,11 +1114,10 @@ Status ModularFrameEncoder::ComputeTree(ThreadPool* pool) {
StaticPropRange range;
range[0] = {{0, max_c}};
range[1] = {{start, stop}};
- auto local_multiplier_info = multiplier_info;
tree_samples.PreQuantizeProperties(
- range, local_multiplier_info, group_pixel_count,
- channel_pixel_count, pixel_samples, diff_samples,
+ range, multiplier_info, group_pixel_count, channel_pixel_count,
+ pixel_samples, diff_samples,
stream_options_[start].max_property_values);
for (size_t i = start; i < stop; i++) {
JXL_CHECK(ModularGenericCompress(
@@ -937,7 +1128,7 @@ Status ModularFrameEncoder::ComputeTree(ThreadPool* pool) {
// TODO(veluca): parallelize more.
trees[chunk] =
LearnTree(std::move(tree_samples), total_pixels,
- stream_options_[start], local_multiplier_info, range);
+ stream_options_[start], multiplier_info, range);
},
"LearnTrees"));
if (invalid_force_wp.test_and_set(std::memory_order_acq_rel)) {
@@ -966,7 +1157,7 @@ Status ModularFrameEncoder::ComputeTree(ThreadPool* pool) {
tree_tokens_.resize(1);
tree_tokens_[0].clear();
Tree decoded_tree;
- TokenizeTree(tree_, &tree_tokens_[0], &decoded_tree);
+ TokenizeTree(tree_, tree_tokens_.data(), &decoded_tree);
JXL_ASSERT(tree_.size() == decoded_tree.size());
tree_ = std::move(decoded_tree);
@@ -1019,46 +1210,8 @@ Status ModularFrameEncoder::EncodeGlobalInfo(bool streaming_mode,
allotment.ReclaimAndCharge(writer, kLayerModularTree, aux_out);
// Write tree
- HistogramParams params;
- if (cparams_.speed_tier > SpeedTier::kKitten) {
- params.clustering = HistogramParams::ClusteringType::kFast;
- params.ans_histogram_strategy =
- cparams_.speed_tier > SpeedTier::kThunder
- ? HistogramParams::ANSHistogramStrategy::kFast
- : HistogramParams::ANSHistogramStrategy::kApproximate;
- params.lz77_method =
- cparams_.decoding_speed_tier >= 3 && cparams_.modular_mode
- ? (cparams_.speed_tier >= SpeedTier::kFalcon
- ? HistogramParams::LZ77Method::kRLE
- : HistogramParams::LZ77Method::kLZ77)
- : HistogramParams::LZ77Method::kNone;
- // Near-lossless DC, as well as modular mode, require choosing hybrid uint
- // more carefully.
- if ((!extra_dc_precision.empty() && extra_dc_precision[0] != 0) ||
- (cparams_.modular_mode && cparams_.speed_tier < SpeedTier::kCheetah)) {
- params.uint_method = HistogramParams::HybridUintMethod::kFast;
- } else {
- params.uint_method = HistogramParams::HybridUintMethod::kNone;
- }
- } else if (cparams_.speed_tier <= SpeedTier::kTortoise) {
- params.lz77_method = HistogramParams::LZ77Method::kOptimal;
- } else {
- params.lz77_method = HistogramParams::LZ77Method::kLZ77;
- }
- if (cparams_.decoding_speed_tier >= 1) {
- params.max_histograms = 12;
- }
- if (cparams_.decoding_speed_tier >= 1 && cparams_.responsive) {
- params.lz77_method = cparams_.speed_tier >= SpeedTier::kCheetah
- ? HistogramParams::LZ77Method::kRLE
- : cparams_.speed_tier >= SpeedTier::kKitten
- ? HistogramParams::LZ77Method::kLZ77
- : HistogramParams::LZ77Method::kOptimal;
- }
- if (cparams_.decoding_speed_tier >= 2 && cparams_.responsive) {
- params.uint_method = HistogramParams::HybridUintMethod::k000;
- params.force_huffman = true;
- }
+ HistogramParams params =
+ HistogramParams::ForModular(cparams_, extra_dc_precision, streaming_mode);
{
EntropyEncodingData tree_code;
std::vector<uint8_t> tree_context_map;
@@ -1082,6 +1235,7 @@ Status ModularFrameEncoder::EncodeStream(BitWriter* writer, AuxOut* aux_out,
const ModularStreamId& stream) {
size_t stream_id = stream.ID(frame_dim_);
if (stream_images_[stream_id].channel.empty()) {
+ JXL_DEBUG_V(10, "Modular stream %" PRIuS " is empty.", stream_id);
return true; // Image with no channels, header never gets decoded.
}
if (tokens_.empty()) {
@@ -1103,113 +1257,44 @@ void ModularFrameEncoder::ClearStreamData(const ModularStreamId& stream) {
std::swap(stream_images_[stream_id], empty_image);
}
-namespace {
-float EstimateWPCost(const Image& img, size_t i) {
- size_t extra_bits = 0;
- float histo_cost = 0;
- HybridUintConfig config;
- int32_t cutoffs[] = {-500, -392, -255, -191, -127, -95, -63, -47, -31,
- -23, -15, -11, -7, -4, -3, -1, 0, 1,
- 3, 5, 7, 11, 15, 23, 31, 47, 63,
- 95, 127, 191, 255, 392, 500};
- constexpr size_t nc = sizeof(cutoffs) / sizeof(*cutoffs) + 1;
- Histogram histo[nc] = {};
- weighted::Header wp_header;
- PredictorMode(i, &wp_header);
- for (const Channel& ch : img.channel) {
- const intptr_t onerow = ch.plane.PixelsPerRow();
- weighted::State wp_state(wp_header, ch.w, ch.h);
- Properties properties(1);
- for (size_t y = 0; y < ch.h; y++) {
- const pixel_type* JXL_RESTRICT r = ch.Row(y);
- for (size_t x = 0; x < ch.w; x++) {
- size_t offset = 0;
- pixel_type_w left = (x ? r[x - 1] : y ? *(r + x - onerow) : 0);
- pixel_type_w top = (y ? *(r + x - onerow) : left);
- pixel_type_w topleft = (x && y ? *(r + x - 1 - onerow) : left);
- pixel_type_w topright =
- (x + 1 < ch.w && y ? *(r + x + 1 - onerow) : top);
- pixel_type_w toptop = (y > 1 ? *(r + x - onerow - onerow) : top);
- pixel_type guess = wp_state.Predict</*compute_properties=*/true>(
- x, y, ch.w, top, left, topright, topleft, toptop, &properties,
- offset);
- size_t ctx = 0;
- for (int c : cutoffs) {
- ctx += c >= properties[0];
- }
- pixel_type res = r[x] - guess;
- uint32_t token, nbits, bits;
- config.Encode(PackSigned(res), &token, &nbits, &bits);
- histo[ctx].Add(token);
- extra_bits += nbits;
- wp_state.UpdateErrors(r[x], x, y, ch.w);
- }
- }
- for (size_t h = 0; h < nc; h++) {
- histo_cost += histo[h].ShannonEntropy();
- histo[h].Clear();
- }
+void ModularFrameEncoder::ClearModularStreamData() {
+ for (const auto& group : stream_params_) {
+ ClearStreamData(group.id);
}
- return histo_cost + extra_bits;
+ stream_params_.clear();
}
-float EstimateCost(const Image& img) {
- // TODO(veluca): consider SIMDfication of this code.
- size_t extra_bits = 0;
- float histo_cost = 0;
- HybridUintConfig config;
- uint32_t cutoffs[] = {0, 1, 3, 5, 7, 11, 15, 23, 31,
- 47, 63, 95, 127, 191, 255, 392, 500};
- constexpr size_t nc = sizeof(cutoffs) / sizeof(*cutoffs) + 1;
- Histogram histo[nc] = {};
- for (const Channel& ch : img.channel) {
- const intptr_t onerow = ch.plane.PixelsPerRow();
- for (size_t y = 0; y < ch.h; y++) {
- const pixel_type* JXL_RESTRICT r = ch.Row(y);
- for (size_t x = 0; x < ch.w; x++) {
- pixel_type_w left = (x ? r[x - 1] : y ? *(r + x - onerow) : 0);
- pixel_type_w top = (y ? *(r + x - onerow) : left);
- pixel_type_w topleft = (x && y ? *(r + x - 1 - onerow) : left);
- size_t maxdiff = std::max(std::max(left, top), topleft) -
- std::min(std::min(left, top), topleft);
- size_t ctx = 0;
- for (uint32_t c : cutoffs) {
- ctx += c > maxdiff;
- }
- pixel_type res = r[x] - ClampedGradient(top, left, topleft);
- uint32_t token, nbits, bits;
- config.Encode(PackSigned(res), &token, &nbits, &bits);
- histo[ctx].Add(token);
- extra_bits += nbits;
- }
- }
- for (size_t h = 0; h < nc; h++) {
- histo_cost += histo[h].ShannonEntropy();
- histo[h].Clear();
- }
- }
- return histo_cost + extra_bits;
+size_t ModularFrameEncoder::ComputeStreamingAbsoluteAcGroupId(
+ size_t dc_group_id, size_t ac_group_id,
+ const FrameDimensions& patch_dim) const {
+ size_t dc_group_x = dc_group_id % frame_dim_.xsize_dc_groups;
+ size_t dc_group_y = dc_group_id / frame_dim_.xsize_dc_groups;
+ size_t ac_group_x = ac_group_id % patch_dim.xsize_groups;
+ size_t ac_group_y = ac_group_id / patch_dim.xsize_groups;
+ return (dc_group_x * 8 + ac_group_x) +
+ (dc_group_y * 8 + ac_group_y) * frame_dim_.xsize_groups;
}
-} // namespace
-
Status ModularFrameEncoder::PrepareStreamParams(const Rect& rect,
const CompressParams& cparams_,
int minShift, int maxShift,
const ModularStreamId& stream,
- bool do_color) {
+ bool do_color, bool groupwise) {
size_t stream_id = stream.ID(frame_dim_);
Image& full_image = stream_images_[0];
const size_t xsize = rect.xsize();
const size_t ysize = rect.ysize();
Image& gi = stream_images_[stream_id];
if (stream_id > 0) {
- gi = Image(xsize, ysize, full_image.bitdepth, 0);
+ JXL_ASSIGN_OR_RETURN(gi,
+ Image::Create(xsize, ysize, full_image.bitdepth, 0));
// start at the first bigger-than-frame_dim.group_dim non-metachannel
size_t c = full_image.nb_meta_channels;
- for (; c < full_image.channel.size(); c++) {
- Channel& fc = full_image.channel[c];
- if (fc.w > frame_dim_.group_dim || fc.h > frame_dim_.group_dim) break;
+ if (!groupwise) {
+ for (; c < full_image.channel.size(); c++) {
+ Channel& fc = full_image.channel[c];
+ if (fc.w > frame_dim_.group_dim || fc.h > frame_dim_.group_dim) break;
+ }
}
for (; c < full_image.channel.size(); c++) {
Channel& fc = full_image.channel[c];
@@ -1220,7 +1305,7 @@ Status ModularFrameEncoder::PrepareStreamParams(const Rect& rect,
rect.xsize() >> fc.hshift, rect.ysize() >> fc.vshift, fc.w, fc.h);
if (r.xsize() == 0 || r.ysize() == 0) continue;
gi_channel_[stream_id].push_back(c);
- Channel gc(r.xsize(), r.ysize());
+ JXL_ASSIGN_OR_RETURN(Channel gc, Channel::Create(r.xsize(), r.ysize()));
gc.hshift = fc.hshift;
gc.vshift = fc.vshift;
for (size_t y = 0; y < r.ysize(); ++y) {
@@ -1245,7 +1330,7 @@ Status ModularFrameEncoder::PrepareStreamParams(const Rect& rect,
maybe_palette.num_c = gi.channel.size() - gi.nb_meta_channels;
maybe_palette.nb_colors = std::abs(cparams_.palette_colors);
maybe_palette.ordered_palette = cparams_.palette_colors >= 0;
- do_transform(gi, maybe_palette, weighted::Header());
+ maybe_do_transform(gi, maybe_palette, cparams_, weighted::Header());
}
// all-minus-one-channel palette (RGB with separate alpha, or CMY with
// separate K)
@@ -1259,7 +1344,7 @@ Status ModularFrameEncoder::PrepareStreamParams(const Rect& rect,
if (maybe_palette_3.lossy_palette) {
maybe_palette_3.predictor = Predictor::Weighted;
}
- do_transform(gi, maybe_palette_3, weighted::Header());
+ maybe_do_transform(gi, maybe_palette_3, cparams_, weighted::Header());
}
}
@@ -1271,9 +1356,10 @@ Status ModularFrameEncoder::PrepareStreamParams(const Rect& rect,
// single channel palette (like FLIF's ChannelCompact)
size_t nb_channels = gi.channel.size() - gi.nb_meta_channels;
for (size_t i = 0; i < nb_channels; i++) {
- int32_t min, max;
+ int32_t min;
+ int32_t max;
compute_minmax(gi.channel[gi.nb_meta_channels + i], &min, &max);
- int64_t colors = (int64_t)max - min + 1;
+ int64_t colors = static_cast<int64_t>(max) - min + 1;
JXL_DEBUG_V(10, "Channel %" PRIuS ": range=%i..%i", i, min, max);
Transform maybe_palette_1(TransformId::kPalette);
maybe_palette_1.begin_c = i + gi.nb_meta_channels;
@@ -1282,10 +1368,10 @@ Status ModularFrameEncoder::PrepareStreamParams(const Rect& rect,
// actually occur, it is probably worth it to do a compaction
// (but only if the channel palette is less than 80% the size of the
// image itself)
- maybe_palette_1.nb_colors =
- std::min((int)(xsize * ysize * 0.8),
- (int)(cparams_.channel_colors_percent / 100. * colors));
- do_transform(gi, maybe_palette_1, weighted::Header());
+ maybe_palette_1.nb_colors = std::min(
+ static_cast<int>(xsize * ysize * 0.8),
+ static_cast<int>(cparams_.channel_colors_percent / 100. * colors));
+ maybe_do_transform(gi, maybe_palette_1, cparams_, weighted::Header());
}
}
}
@@ -1295,7 +1381,7 @@ Status ModularFrameEncoder::PrepareStreamParams(const Rect& rect,
if (cparams_.color_transform == ColorTransform::kNone &&
cparams_.IsLossless() && cparams_.colorspace < 0 &&
gi.channel.size() - gi.nb_meta_channels >= 3 &&
- cparams_.responsive == false && do_color &&
+ cparams_.responsive == JXL_FALSE && do_color &&
cparams_.speed_tier <= SpeedTier::kHare) {
Transform sg(TransformId::kRCT);
sg.begin_c = gi.nb_meta_channels;
@@ -1319,6 +1405,7 @@ Status ModularFrameEncoder::PrepareStreamParams(const Rect& rect,
case SpeedTier::kKitten:
nb_rcts_to_try = 9;
break;
+ case SpeedTier::kTectonicPlate:
case SpeedTier::kGlacier:
case SpeedTier::kTortoise:
nb_rcts_to_try = 19;
@@ -1403,11 +1490,11 @@ int QuantizeGradient(const int32_t* qrow, size_t onerow, size_t c, size_t x,
return residual + pred.guess;
}
-void ModularFrameEncoder::AddVarDCTDC(const FrameHeader& frame_header,
- const Image3F& dc, const Rect& r,
- size_t group_index, bool nl_dc,
- PassesEncoderState* enc_state,
- bool jpeg_transcode) {
+Status ModularFrameEncoder::AddVarDCTDC(const FrameHeader& frame_header,
+ const Image3F& dc, const Rect& r,
+ size_t group_index, bool nl_dc,
+ PassesEncoderState* enc_state,
+ bool jpeg_transcode) {
extra_dc_precision[group_index] = nl_dc ? 1 : 0;
float mul = 1 << extra_dc_precision[group_index];
@@ -1430,8 +1517,11 @@ void ModularFrameEncoder::AddVarDCTDC(const FrameHeader& frame_header,
stream_options_[stream_id].tree_kind =
ModularOptions::TreeKind::kGradientFixedDC;
}
+ stream_options_[stream_id].histogram_params =
+ stream_options_[0].histogram_params;
- stream_images_[stream_id] = Image(r.xsize(), r.ysize(), 8, 3);
+ JXL_ASSIGN_OR_RETURN(stream_images_[stream_id],
+ Image::Create(r.xsize(), r.ysize(), 8, 3));
if (nl_dc && stream_options_[stream_id].tree_kind ==
ModularOptions::TreeKind::kGradientFixedDC) {
JXL_ASSERT(frame_header.chroma_subsampling.Is444());
@@ -1531,7 +1621,7 @@ void ModularFrameEncoder::AddVarDCTDC(const FrameHeader& frame_header,
Channel& ch = stream_images_[stream_id].channel[c < 2 ? c ^ 1 : c];
ch.w = xs;
ch.h = ys;
- ch.shrink();
+ JXL_RETURN_IF_ERROR(ch.shrink());
for (size_t y = 0; y < ys; y++) {
int32_t* quant_row = ch.plane.Row(y);
const float* row = rect.ConstPlaneRow(dc, c, y);
@@ -1546,14 +1636,17 @@ void ModularFrameEncoder::AddVarDCTDC(const FrameHeader& frame_header,
stream_images_[stream_id], enc_state->shared.quantizer.MulDC(),
1.0 / mul, enc_state->shared.cmap.DCFactors(),
frame_header.chroma_subsampling, enc_state->shared.block_ctx_map);
+ return true;
}
-void ModularFrameEncoder::AddACMetadata(const Rect& r, size_t group_index,
- bool jpeg_transcode,
- PassesEncoderState* enc_state) {
+Status ModularFrameEncoder::AddACMetadata(const Rect& r, size_t group_index,
+ bool jpeg_transcode,
+ PassesEncoderState* enc_state) {
size_t stream_id = ModularStreamId::ACMetadata(group_index).ID(frame_dim_);
stream_options_[stream_id].max_chan_size = 0xFFFFFF;
- stream_options_[stream_id].wp_tree_mode = ModularOptions::TreeMode::kNoWP;
+ if (stream_options_[stream_id].predictor != Predictor::Weighted) {
+ stream_options_[stream_id].wp_tree_mode = ModularOptions::TreeMode::kNoWP;
+ }
if (jpeg_transcode) {
stream_options_[stream_id].tree_kind =
ModularOptions::TreeKind::kJpegTranscodeACMeta;
@@ -1569,14 +1662,19 @@ void ModularFrameEncoder::AddACMetadata(const Rect& r, size_t group_index,
cparams_.force_cfl_jpeg_recompression) {
stream_options_[stream_id].tree_kind = ModularOptions::TreeKind::kLearn;
}
+ stream_options_[stream_id].histogram_params =
+ stream_options_[0].histogram_params;
// YToX, YToB, ACS + QF, EPF
Image& image = stream_images_[stream_id];
- image = Image(r.xsize(), r.ysize(), 8, 4);
+ JXL_ASSIGN_OR_RETURN(image, Image::Create(r.xsize(), r.ysize(), 8, 4));
static_assert(kColorTileDimInBlocks == 8, "Color tile size changed");
Rect cr(r.x0() >> 3, r.y0() >> 3, (r.xsize() + 7) >> 3, (r.ysize() + 7) >> 3);
- image.channel[0] = Channel(cr.xsize(), cr.ysize(), 3, 3);
- image.channel[1] = Channel(cr.xsize(), cr.ysize(), 3, 3);
- image.channel[2] = Channel(r.xsize() * r.ysize(), 2, 0, 0);
+ JXL_ASSIGN_OR_RETURN(image.channel[0],
+ Channel::Create(cr.xsize(), cr.ysize(), 3, 3));
+ JXL_ASSIGN_OR_RETURN(image.channel[1],
+ Channel::Create(cr.xsize(), cr.ysize(), 3, 3));
+ JXL_ASSIGN_OR_RETURN(image.channel[2],
+ Channel::Create(r.xsize() * r.ysize(), 2, 0, 0));
ConvertPlaneAndClamp(cr, enc_state->shared.cmap.ytox_map,
Rect(image.channel[0].plane), &image.channel[0].plane);
ConvertPlaneAndClamp(cr, enc_state->shared.cmap.ytob_map,
@@ -1599,9 +1697,10 @@ void ModularFrameEncoder::AddACMetadata(const Rect& r, size_t group_index,
}
image.channel[2].w = num;
ac_metadata_size[group_index] = num;
+ return true;
}
-void ModularFrameEncoder::EncodeQuantTable(
+Status ModularFrameEncoder::EncodeQuantTable(
size_t size_x, size_t size_y, BitWriter* writer,
const QuantEncoding& encoding, size_t idx,
ModularFrameEncoder* modular_frame_encoder) {
@@ -1611,9 +1710,9 @@ void ModularFrameEncoder::EncodeQuantTable(
if (modular_frame_encoder) {
JXL_CHECK(modular_frame_encoder->EncodeStream(
writer, nullptr, 0, ModularStreamId::QuantTable(idx)));
- return;
+ return true;
}
- Image image(size_x, size_y, 8, 3);
+ JXL_ASSIGN_OR_RETURN(Image image, Image::Create(size_x, size_y, 8, 3));
for (size_t c = 0; c < 3; c++) {
for (size_t y = 0; y < size_y; y++) {
int32_t* JXL_RESTRICT row = image.channel[c].Row(y);
@@ -1624,16 +1723,17 @@ void ModularFrameEncoder::EncodeQuantTable(
}
ModularOptions cfopts;
JXL_CHECK(ModularGenericCompress(image, cfopts, writer));
+ return true;
}
-void ModularFrameEncoder::AddQuantTable(size_t size_x, size_t size_y,
- const QuantEncoding& encoding,
- size_t idx) {
+Status ModularFrameEncoder::AddQuantTable(size_t size_x, size_t size_y,
+ const QuantEncoding& encoding,
+ size_t idx) {
size_t stream_id = ModularStreamId::QuantTable(idx).ID(frame_dim_);
JXL_ASSERT(encoding.qraw.qtable != nullptr);
JXL_ASSERT(size_x * size_y * 3 == encoding.qraw.qtable->size());
Image& image = stream_images_[stream_id];
- image = Image(size_x, size_y, 8, 3);
+ JXL_ASSIGN_OR_RETURN(image, Image::Create(size_x, size_y, 8, 3));
for (size_t c = 0; c < 3; c++) {
for (size_t y = 0; y < size_y; y++) {
int32_t* JXL_RESTRICT row = image.channel[c].Row(y);
@@ -1642,5 +1742,6 @@ void ModularFrameEncoder::AddQuantTable(size_t size_x, size_t size_y,
}
}
}
+ return true;
}
} // namespace jxl
diff --git a/third_party/jpeg-xl/lib/jxl/enc_modular.h b/third_party/jpeg-xl/lib/jxl/enc_modular.h
index 2158a781af..8e2015b226 100644
--- a/third_party/jpeg-xl/lib/jxl/enc_modular.h
+++ b/third_party/jpeg-xl/lib/jxl/enc_modular.h
@@ -6,18 +6,29 @@
#ifndef LIB_JXL_ENC_MODULAR_H_
#define LIB_JXL_ENC_MODULAR_H_
+#include <jxl/cms_interface.h>
+
+#include <cstddef>
#include <cstdint>
+#include <vector>
+#include "lib/jxl/base/compiler_specific.h"
+#include "lib/jxl/base/data_parallel.h"
#include "lib/jxl/base/status.h"
#include "lib/jxl/dec_modular.h"
+#include "lib/jxl/enc_ans.h"
#include "lib/jxl/enc_bit_writer.h"
#include "lib/jxl/enc_cache.h"
#include "lib/jxl/enc_params.h"
+#include "lib/jxl/frame_dimensions.h"
#include "lib/jxl/frame_header.h"
#include "lib/jxl/image.h"
-#include "lib/jxl/image_bundle.h"
+#include "lib/jxl/image_metadata.h"
+#include "lib/jxl/modular/encoding/dec_ma.h"
#include "lib/jxl/modular/encoding/encoding.h"
#include "lib/jxl/modular/modular_image.h"
+#include "lib/jxl/modular/options.h"
+#include "lib/jxl/quant_weights.h"
namespace jxl {
@@ -26,14 +37,14 @@ struct AuxOut;
class ModularFrameEncoder {
public:
ModularFrameEncoder(const FrameHeader& frame_header,
- const CompressParams& cparams_orig);
- Status ComputeEncodingData(const FrameHeader& frame_header,
- const ImageMetadata& metadata,
- Image3F* JXL_RESTRICT color,
- const std::vector<ImageF>& extra_channels,
- PassesEncoderState* JXL_RESTRICT enc_state,
- const JxlCmsInterface& cms, ThreadPool* pool,
- AuxOut* aux_out, bool do_color);
+ const CompressParams& cparams_orig, bool streaming_mode);
+ Status ComputeEncodingData(
+ const FrameHeader& frame_header, const ImageMetadata& metadata,
+ Image3F* JXL_RESTRICT color, const std::vector<ImageF>& extra_channels,
+ const Rect& group_rect, const FrameDimensions& patch_dim,
+ const Rect& frame_area_rect, PassesEncoderState* JXL_RESTRICT enc_state,
+ const JxlCmsInterface& cms, ThreadPool* pool, AuxOut* aux_out,
+ bool do_color);
Status ComputeTree(ThreadPool* pool);
Status ComputeTokens(ThreadPool* pool);
// Encodes global info (tree + histograms) in the `writer`.
@@ -43,28 +54,35 @@ class ModularFrameEncoder {
// assigning bits to the provided `layer`.
Status EncodeStream(BitWriter* writer, AuxOut* aux_out, size_t layer,
const ModularStreamId& stream);
+
void ClearStreamData(const ModularStreamId& stream);
+ void ClearModularStreamData();
+ size_t ComputeStreamingAbsoluteAcGroupId(
+ size_t dc_group_id, size_t ac_group_id,
+ const FrameDimensions& patch_dim) const;
+
// Creates a modular image for a given DC group of VarDCT mode. `dc` is the
// input DC image, not quantized; the group is specified by `group_index`, and
// `nl_dc` decides whether to apply a near-lossless processing to the DC or
// not.
- void AddVarDCTDC(const FrameHeader& frame_header, const Image3F& dc,
- const Rect& r, size_t group_index, bool nl_dc,
- PassesEncoderState* enc_state, bool jpeg_transcode);
+ Status AddVarDCTDC(const FrameHeader& frame_header, const Image3F& dc,
+ const Rect& r, size_t group_index, bool nl_dc,
+ PassesEncoderState* enc_state, bool jpeg_transcode);
// Creates a modular image for the AC metadata of the given group
// (`group_index`).
- void AddACMetadata(const Rect& r, size_t group_index, bool jpeg_transcode,
- PassesEncoderState* enc_state);
+ Status AddACMetadata(const Rect& r, size_t group_index, bool jpeg_transcode,
+ PassesEncoderState* enc_state);
// Encodes a RAW quantization table in `writer`. If `modular_frame_encoder` is
// null, the quantization table in `encoding` is used, with dimensions `size_x
// x size_y`. Otherwise, the table with ID `idx` is encoded from the given
// `modular_frame_encoder`.
- static void EncodeQuantTable(size_t size_x, size_t size_y, BitWriter* writer,
- const QuantEncoding& encoding, size_t idx,
- ModularFrameEncoder* modular_frame_encoder);
+ static Status EncodeQuantTable(size_t size_x, size_t size_y,
+ BitWriter* writer,
+ const QuantEncoding& encoding, size_t idx,
+ ModularFrameEncoder* modular_frame_encoder);
// Stores a quantization table for future usage with `EncodeQuantTable`.
- void AddQuantTable(size_t size_x, size_t size_y,
- const QuantEncoding& encoding, size_t idx);
+ Status AddQuantTable(size_t size_x, size_t size_y,
+ const QuantEncoding& encoding, size_t idx);
std::vector<size_t> ac_metadata_size;
std::vector<uint8_t> extra_dc_precision;
@@ -72,7 +90,8 @@ class ModularFrameEncoder {
private:
Status PrepareStreamParams(const Rect& rect, const CompressParams& cparams,
int minShift, int maxShift,
- const ModularStreamId& stream, bool do_color);
+ const ModularStreamId& stream, bool do_color,
+ bool groupwise);
std::vector<Image> stream_images_;
std::vector<ModularOptions> stream_options_;
std::vector<uint32_t> quants_;
@@ -89,6 +108,14 @@ class ModularFrameEncoder {
std::vector<std::vector<uint32_t>> gi_channel_;
std::vector<size_t> image_widths_;
Predictor delta_pred_ = Predictor::Average4;
+
+ struct GroupParams {
+ Rect rect;
+ int minShift;
+ int maxShift;
+ ModularStreamId id;
+ };
+ std::vector<GroupParams> stream_params_;
};
} // namespace jxl
diff --git a/third_party/jpeg-xl/lib/jxl/enc_noise.cc b/third_party/jpeg-xl/lib/jxl/enc_noise.cc
index a12a9e6dc4..80b90eed2c 100644
--- a/third_party/jpeg-xl/lib/jxl/enc_noise.cc
+++ b/third_party/jpeg-xl/lib/jxl/enc_noise.cc
@@ -114,7 +114,7 @@ class NoiseHistogram {
private:
template <typename T>
T ClampX(const T x) const {
- return std::min(std::max(T(0), x), T(kBins - 1));
+ return std::min(std::max(static_cast<T>(0), x), static_cast<T>(kBins - 1));
}
size_t Index(const float x) const { return ClampX(static_cast<int>(x)); }
@@ -321,7 +321,7 @@ std::vector<NoiseLevel> GetNoiseLevel(
void EncodeFloatParam(float val, float precision, BitWriter* writer) {
JXL_ASSERT(val >= 0);
- const int absval_quant = static_cast<int>(val * precision + 0.5f);
+ const int absval_quant = static_cast<int>(std::lround(val * precision));
JXL_ASSERT(absval_quant < (1 << 10));
writer->Write(10, absval_quant);
}
diff --git a/third_party/jpeg-xl/lib/jxl/enc_optimize_test.cc b/third_party/jpeg-xl/lib/jxl/enc_optimize_test.cc
index cc65bf1a0c..9184765be2 100644
--- a/third_party/jpeg-xl/lib/jxl/enc_optimize_test.cc
+++ b/third_party/jpeg-xl/lib/jxl/enc_optimize_test.cc
@@ -12,7 +12,7 @@ namespace optimize {
namespace {
// The maximum number of iterations for the test.
-static const size_t kMaxTestIter = 100000;
+const size_t kMaxTestIter = 100000;
// F(w) = (w - w_min)^2.
struct SimpleQuadraticFunction {
diff --git a/third_party/jpeg-xl/lib/jxl/enc_params.h b/third_party/jpeg-xl/lib/jxl/enc_params.h
index 89fd2c924f..162c59d04c 100644
--- a/third_party/jpeg-xl/lib/jxl/enc_params.h
+++ b/third_party/jpeg-xl/lib/jxl/enc_params.h
@@ -15,6 +15,7 @@
#include <vector>
#include "lib/jxl/base/override.h"
+#include "lib/jxl/common.h"
#include "lib/jxl/enc_progressive_split.h"
#include "lib/jxl/frame_dimensions.h"
#include "lib/jxl/frame_header.h"
@@ -24,36 +25,6 @@
namespace jxl {
-enum class SpeedTier {
- // Try multiple combinations of Tortoise flags for modular mode. Otherwise
- // like kTortoise.
- kGlacier = 0,
- // Turns on FindBestQuantizationHQ loop. Equivalent to "guetzli" mode.
- kTortoise = 1,
- // Turns on FindBestQuantization butteraugli loop.
- kKitten = 2,
- // Turns on dots, patches, and spline detection by default, as well as full
- // context clustering. Default.
- kSquirrel = 3,
- // Turns on error diffusion and full AC strategy heuristics. Equivalent to
- // "fast" mode.
- kWombat = 4,
- // Turns on gaborish by default, non-default cmap, initial quant field.
- kHare = 5,
- // Turns on simple heuristics for AC strategy, quant field, and clustering;
- // also enables coefficient reordering.
- kCheetah = 6,
- // Turns off most encoder features. Does context clustering.
- // Modular: uses fixed tree with Weighted predictor.
- kFalcon = 7,
- // Currently fastest possible setting for VarDCT.
- // Modular: uses fixed tree with Gradient predictor.
- kThunder = 8,
- // VarDCT: same as kThunder.
- // Modular: no tree, Gradient predictor, fast histograms
- kLightning = 9
-};
-
// NOLINTNEXTLINE(clang-analyzer-optin.performance.Padding)
struct CompressParams {
float butteraugli_distance = 1.0f;
@@ -106,7 +77,7 @@ struct CompressParams {
int progressive_dc = -1;
// If on: preserve color of invisible pixels (if off: don't care)
- // Default: on for lossless, off for lossy
+ // Default: on
Override keep_invisible = Override::kDefault;
JxlCmsInterface cms;
@@ -137,6 +108,7 @@ struct CompressParams {
ModularOptions options;
int responsive = -1;
int colorspace = -1;
+ int move_to_front_from_channel = -1;
// Use Global channel palette if #colors < this percentage of range
float channel_colors_pre_transform_percent = 95.f;
// Use Local channel palette if #colors < this percentage of range
@@ -159,9 +131,6 @@ struct CompressParams {
if (f > 0) return false;
if (f < 0 && butteraugli_distance != 0) return false;
}
- // if no explicit ec_distance given, and using vardct, then the modular part
- // is empty or not lossless
- if (!modular_mode && ec_distance.empty()) return false;
// all modular channels are encoded at distance 0
return true;
}
@@ -194,7 +163,7 @@ struct CompressParams {
int level = -1;
// See JXL_ENC_FRAME_SETTING_BUFFERING option value.
- int buffering = 0;
+ int buffering = -1;
// See JXL_ENC_FRAME_SETTING_USE_FULL_IMAGE_HEURISTICS option value.
bool use_full_image_heuristics = true;
diff --git a/third_party/jpeg-xl/lib/jxl/enc_patch_dictionary.cc b/third_party/jpeg-xl/lib/jxl/enc_patch_dictionary.cc
index 0abd177809..f19ba0dd9e 100644
--- a/third_party/jpeg-xl/lib/jxl/enc_patch_dictionary.cc
+++ b/third_party/jpeg-xl/lib/jxl/enc_patch_dictionary.cc
@@ -5,24 +5,22 @@
#include "lib/jxl/enc_patch_dictionary.h"
+#include <jxl/types.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/types.h>
#include <algorithm>
#include <atomic>
-#include <string>
-#include <tuple>
#include <utility>
#include <vector>
-#include "lib/jxl/ans_params.h"
#include "lib/jxl/base/common.h"
#include "lib/jxl/base/compiler_specific.h"
#include "lib/jxl/base/override.h"
+#include "lib/jxl/base/printf_macros.h"
#include "lib/jxl/base/random.h"
#include "lib/jxl/base/status.h"
-#include "lib/jxl/chroma_from_luma.h"
#include "lib/jxl/dec_cache.h"
#include "lib/jxl/dec_frame.h"
#include "lib/jxl/enc_ans.h"
@@ -31,7 +29,6 @@
#include "lib/jxl/enc_debug_image.h"
#include "lib/jxl/enc_dot_dictionary.h"
#include "lib/jxl/enc_frame.h"
-#include "lib/jxl/entropy_coder.h"
#include "lib/jxl/frame_header.h"
#include "lib/jxl/image.h"
#include "lib/jxl/image_bundle.h"
@@ -100,7 +97,7 @@ void PatchDictionaryEncoder::Encode(const PatchDictionary& pdic,
add_num(kPatchAlphaChannelContext, info.alpha_channel);
}
if (UsesClamp(info.mode)) {
- add_num(kPatchClampContext, info.clamp);
+ add_num(kPatchClampContext, TO_JXL_BOOL(info.clamp));
}
}
}
@@ -159,7 +156,7 @@ void PatchDictionaryEncoder::SubtractFrom(const PatchDictionary& pdic,
// Nothing to do.
} else {
JXL_UNREACHABLE("Blending mode %u not yet implemented",
- (uint32_t)mode);
+ static_cast<uint32_t>(mode));
}
}
}
@@ -208,11 +205,12 @@ struct PatchColorspaceInfo {
}
};
-std::vector<PatchInfo> FindTextLikePatches(
+StatusOr<std::vector<PatchInfo>> FindTextLikePatches(
const CompressParams& cparams, const Image3F& opsin,
const PassesEncoderState* JXL_RESTRICT state, ThreadPool* pool,
AuxOut* aux_out, bool is_xyb) {
- if (state->cparams.patches == Override::kOff) return {};
+ std::vector<PatchInfo> info;
+ if (state->cparams.patches == Override::kOff) return info;
const auto& frame_dim = state->shared.frame_dim;
PatchColorspaceInfo pci(is_xyb);
@@ -222,7 +220,8 @@ std::vector<PatchInfo> FindTextLikePatches(
std::pair<uint32_t, uint32_t> p2,
const float* JXL_RESTRICT rows[3],
size_t stride, float threshold) {
- float v1[3], v2[3];
+ float v1[3];
+ float v2[3];
for (size_t c = 0; c < 3; c++) {
v1[c] = rows[c][p1.second * stride + p1.first];
v2[c] = rows[c][p2.second * stride + p2.first];
@@ -258,8 +257,9 @@ std::vector<PatchInfo> FindTextLikePatches(
// Look for kPatchSide size squares, naturally aligned, that all have the same
// pixel values.
- ImageB is_screenshot_like(DivCeil(frame_dim.xsize, kPatchSide),
- DivCeil(frame_dim.ysize, kPatchSide));
+ JXL_ASSIGN_OR_RETURN(ImageB is_screenshot_like,
+ ImageB::Create(DivCeil(frame_dim.xsize, kPatchSide),
+ DivCeil(frame_dim.ysize, kPatchSide)));
ZeroFillImage(&is_screenshot_like);
uint8_t* JXL_RESTRICT screenshot_row = is_screenshot_like.Row(0);
const size_t screenshot_stride = is_screenshot_like.PixelsPerRow();
@@ -302,19 +302,22 @@ std::vector<PatchInfo> FindTextLikePatches(
// TODO(veluca): also parallelize the rest of this function.
if (WantDebugOutput(cparams)) {
- DumpPlaneNormalized(cparams, "screenshot_like", is_screenshot_like);
+ JXL_RETURN_IF_ERROR(
+ DumpPlaneNormalized(cparams, "screenshot_like", is_screenshot_like));
}
constexpr int kSearchRadius = 1;
if (!ApplyOverride(state->cparams.patches, has_screenshot_areas)) {
- return {};
+ return info;
}
// Search for "similar enough" pixels near the screenshot-like areas.
- ImageB is_background(frame_dim.xsize, frame_dim.ysize);
+ JXL_ASSIGN_OR_RETURN(ImageB is_background,
+ ImageB::Create(frame_dim.xsize, frame_dim.ysize));
ZeroFillImage(&is_background);
- Image3F background(frame_dim.xsize, frame_dim.ysize);
+ JXL_ASSIGN_OR_RETURN(Image3F background,
+ Image3F::Create(frame_dim.xsize, frame_dim.ysize));
ZeroFillImage(&background);
constexpr size_t kDistanceLimit = 50;
float* JXL_RESTRICT background_rows[3] = {
@@ -383,13 +386,14 @@ std::vector<PatchInfo> FindTextLikePatches(
Rng rng(0);
bool paint_ccs = false;
if (WantDebugOutput(cparams)) {
- DumpPlaneNormalized(cparams, "is_background", is_background);
+ JXL_RETURN_IF_ERROR(
+ DumpPlaneNormalized(cparams, "is_background", is_background));
if (is_xyb) {
- DumpXybImage(cparams, "background", background);
+ JXL_RETURN_IF_ERROR(DumpXybImage(cparams, "background", background));
} else {
- DumpImage(cparams, "background", background);
+ JXL_RETURN_IF_ERROR(DumpImage(cparams, "background", background));
}
- ccs = ImageF(frame_dim.xsize, frame_dim.ysize);
+ JXL_ASSIGN_OR_RETURN(ccs, ImageF::Create(frame_dim.xsize, frame_dim.ysize));
ZeroFillImage(&ccs);
paint_ccs = true;
}
@@ -407,11 +411,10 @@ std::vector<PatchInfo> FindTextLikePatches(
constexpr int kMinPeak = 2;
constexpr int kHasSimilarRadius = 2;
- std::vector<PatchInfo> info;
-
// Find small CC outside the "similar enough" areas, compute bounding boxes,
// and run heuristics to exclude some patches.
- ImageB visited(frame_dim.xsize, frame_dim.ysize);
+ JXL_ASSIGN_OR_RETURN(ImageB visited,
+ ImageB::Create(frame_dim.xsize, frame_dim.ysize));
ZeroFillImage(&visited);
uint8_t* JXL_RESTRICT visited_row = visited.Row(0);
const size_t visited_stride = visited.PixelsPerRow();
@@ -525,10 +528,10 @@ std::vector<PatchInfo> FindTextLikePatches(
if (paint_ccs) {
JXL_ASSERT(WantDebugOutput(cparams));
- DumpPlaneNormalized(cparams, "ccs", ccs);
+ JXL_RETURN_IF_ERROR(DumpPlaneNormalized(cparams, "ccs", ccs));
}
if (info.empty()) {
- return {};
+ return info;
}
// Remove duplicates.
@@ -560,19 +563,22 @@ std::vector<PatchInfo> FindTextLikePatches(
// don't use patches if all patches are smaller than this
constexpr size_t kMinMaxPatchSize = 20;
- if (max_patch_size < kMinMaxPatchSize) return {};
+ if (max_patch_size < kMinMaxPatchSize) {
+ info.clear();
+ }
return info;
}
} // namespace
-void FindBestPatchDictionary(const Image3F& opsin,
- PassesEncoderState* JXL_RESTRICT state,
- const JxlCmsInterface& cms, ThreadPool* pool,
- AuxOut* aux_out, bool is_xyb) {
- std::vector<PatchInfo> info =
- FindTextLikePatches(state->cparams, opsin, state, pool, aux_out, is_xyb);
+Status FindBestPatchDictionary(const Image3F& opsin,
+ PassesEncoderState* JXL_RESTRICT state,
+ const JxlCmsInterface& cms, ThreadPool* pool,
+ AuxOut* aux_out, bool is_xyb) {
+ JXL_ASSIGN_OR_RETURN(
+ std::vector<PatchInfo> info,
+ FindTextLikePatches(state->cparams, opsin, state, pool, aux_out, is_xyb));
// TODO(veluca): this doesn't work if both dots and patches are enabled.
// For now, since dots and patches are not likely to occur in the same kind of
@@ -582,10 +588,13 @@ void FindBestPatchDictionary(const Image3F& opsin,
state->cparams.dots,
state->cparams.speed_tier <= SpeedTier::kSquirrel &&
state->cparams.butteraugli_distance >= kMinButteraugliForDots)) {
- info = FindDotDictionary(state->cparams, opsin, state->shared.cmap, pool);
+ Rect rect(0, 0, state->shared.frame_dim.xsize,
+ state->shared.frame_dim.ysize);
+ JXL_ASSIGN_OR_RETURN(info, FindDotDictionary(state->cparams, opsin, rect,
+ state->shared.cmap, pool));
}
- if (info.empty()) return;
+ if (info.empty()) return true;
std::sort(
info.begin(), info.end(), [&](const PatchInfo& a, const PatchInfo& b) {
@@ -616,7 +625,7 @@ void FindBestPatchDictionary(const Image3F& opsin,
ref_xsize = ref_xsize * kBinPackingSlackness + 1;
ref_ysize = ref_ysize * kBinPackingSlackness + 1;
- ImageB occupied(ref_xsize, ref_ysize);
+ JXL_ASSIGN_OR_RETURN(ImageB occupied, ImageB::Create(ref_xsize, ref_ysize));
ZeroFillImage(&occupied);
uint8_t* JXL_RESTRICT occupied_rows = occupied.Row(0);
size_t occupied_stride = occupied.PixelsPerRow();
@@ -667,7 +676,7 @@ void FindBestPatchDictionary(const Image3F& opsin,
ref_positions[patch] = {x0, y0};
for (size_t y = y0; y < y0 + ysize; y++) {
for (size_t x = x0; x < x0 + xsize; x++) {
- occupied_rows[y * occupied_stride + x] = true;
+ occupied_rows[y * occupied_stride + x] = JXL_TRUE;
}
}
max_y = std::max(max_y, y0 + ysize);
@@ -680,7 +689,8 @@ void FindBestPatchDictionary(const Image3F& opsin,
ref_ysize = max_y;
- Image3F reference_frame(ref_xsize, ref_ysize);
+ JXL_ASSIGN_OR_RETURN(Image3F reference_frame,
+ Image3F::Create(ref_xsize, ref_ysize));
// TODO(veluca): figure out a better way to fill the image.
ZeroFillImage(&reference_frame);
std::vector<PatchPosition> positions;
@@ -710,6 +720,8 @@ void FindBestPatchDictionary(const Image3F& opsin,
}
}
for (const auto& pos : info[i].second) {
+ JXL_DEBUG_V(4, "Patch %" PRIuS "x%" PRIuS " at position %u,%u",
+ ref_pos.xsize, ref_pos.ysize, pos.first, pos.second);
positions.emplace_back(
PatchPosition{pos.first, pos.second, pref_positions.size()});
// Add blending for color channels, ignore other channels.
@@ -718,15 +730,16 @@ void FindBestPatchDictionary(const Image3F& opsin,
blendings.push_back({PatchBlendMode::kNone, 0, false});
}
}
- pref_positions.emplace_back(std::move(ref_pos));
+ pref_positions.emplace_back(ref_pos);
}
CompressParams cparams = state->cparams;
// Recursive application of patches could create very weird issues.
cparams.patches = Override::kOff;
- RoundtripPatchFrame(&reference_frame, state, kPatchFrameReferenceId, cparams,
- cms, pool, aux_out, /*subtract=*/true);
+ JXL_RETURN_IF_ERROR(RoundtripPatchFrame(&reference_frame, state,
+ kPatchFrameReferenceId, cparams, cms,
+ pool, aux_out, /*subtract=*/true));
// TODO(veluca): this assumes that applying patches is commutative, which is
// not true for all blending modes. This code only produces kAdd patches, so
@@ -734,12 +747,13 @@ void FindBestPatchDictionary(const Image3F& opsin,
PatchDictionaryEncoder::SetPositions(
&state->shared.image_features.patches, std::move(positions),
std::move(pref_positions), std::move(blendings));
+ return true;
}
-void RoundtripPatchFrame(Image3F* reference_frame,
- PassesEncoderState* JXL_RESTRICT state, int idx,
- CompressParams& cparams, const JxlCmsInterface& cms,
- ThreadPool* pool, AuxOut* aux_out, bool subtract) {
+Status RoundtripPatchFrame(Image3F* reference_frame,
+ PassesEncoderState* JXL_RESTRICT state, int idx,
+ CompressParams& cparams, const JxlCmsInterface& cms,
+ ThreadPool* pool, AuxOut* aux_out, bool subtract) {
FrameInfo patch_frame_info;
cparams.resampling = 1;
cparams.ec_resampling = 1;
@@ -768,7 +782,8 @@ void RoundtripPatchFrame(Image3F* reference_frame,
std::vector<ImageF> extra_channels;
extra_channels.reserve(ib.metadata()->extra_channel_info.size());
for (size_t i = 0; i < ib.metadata()->extra_channel_info.size(); i++) {
- extra_channels.emplace_back(ib.xsize(), ib.ysize());
+ JXL_ASSIGN_OR_RETURN(ImageF ch, ImageF::Create(ib.xsize(), ib.ysize()));
+ extra_channels.emplace_back(std::move(ch));
// Must initialize the image with data to not affect blending with
// uninitialized memory.
// TODO(lode): patches must copy and use the real extra channels instead.
@@ -814,6 +829,7 @@ void RoundtripPatchFrame(Image3F* reference_frame,
} else {
state->shared.reference_frames[idx].frame = std::move(ib);
}
+ return true;
}
} // namespace jxl
diff --git a/third_party/jpeg-xl/lib/jxl/enc_patch_dictionary.h b/third_party/jpeg-xl/lib/jxl/enc_patch_dictionary.h
index e17bfe4f04..ac236d7f17 100644
--- a/third_party/jpeg-xl/lib/jxl/enc_patch_dictionary.h
+++ b/third_party/jpeg-xl/lib/jxl/enc_patch_dictionary.h
@@ -12,13 +12,10 @@
#include <string.h>
#include <sys/types.h>
-#include <tuple>
#include <vector>
#include "lib/jxl/base/data_parallel.h"
#include "lib/jxl/base/status.h"
-#include "lib/jxl/chroma_from_luma.h"
-#include "lib/jxl/dec_bit_reader.h"
#include "lib/jxl/dec_patch_dictionary.h"
#include "lib/jxl/enc_bit_writer.h"
#include "lib/jxl/enc_cache.h"
@@ -92,15 +89,15 @@ class PatchDictionaryEncoder {
static void SubtractFrom(const PatchDictionary& pdic, Image3F* opsin);
};
-void FindBestPatchDictionary(const Image3F& opsin,
- PassesEncoderState* JXL_RESTRICT state,
- const JxlCmsInterface& cms, ThreadPool* pool,
- AuxOut* aux_out, bool is_xyb = true);
+Status FindBestPatchDictionary(const Image3F& opsin,
+ PassesEncoderState* JXL_RESTRICT state,
+ const JxlCmsInterface& cms, ThreadPool* pool,
+ AuxOut* aux_out, bool is_xyb = true);
-void RoundtripPatchFrame(Image3F* reference_frame,
- PassesEncoderState* JXL_RESTRICT state, int idx,
- CompressParams& cparams, const JxlCmsInterface& cms,
- ThreadPool* pool, AuxOut* aux_out, bool subtract);
+Status RoundtripPatchFrame(Image3F* reference_frame,
+ PassesEncoderState* JXL_RESTRICT state, int idx,
+ CompressParams& cparams, const JxlCmsInterface& cms,
+ ThreadPool* pool, AuxOut* aux_out, bool subtract);
} // namespace jxl
diff --git a/third_party/jpeg-xl/lib/jxl/enc_photon_noise_test.cc b/third_party/jpeg-xl/lib/jxl/enc_photon_noise_test.cc
index be11b465ad..2e10dd5457 100644
--- a/third_party/jpeg-xl/lib/jxl/enc_photon_noise_test.cc
+++ b/third_party/jpeg-xl/lib/jxl/enc_photon_noise_test.cc
@@ -5,46 +5,37 @@
#include "lib/jxl/enc_photon_noise.h"
-#include "lib/jxl/test_utils.h"
+#include "lib/jxl/base/common.h"
+#include "lib/jxl/noise.h"
#include "lib/jxl/testing.h"
namespace jxl {
namespace {
-using ::testing::FloatNear;
-using ::testing::Pointwise;
-
-MATCHER(AreApproximatelyEqual, "") {
- constexpr float kTolerance = 1e-6;
- const float actual = std::get<0>(arg);
- const float expected = std::get<1>(arg);
- return testing::ExplainMatchResult(FloatNear(expected, kTolerance), actual,
- result_listener);
-}
-
TEST(EncPhotonNoiseTest, LUTs) {
- EXPECT_THAT(
+ const double kTolerance = 1e-6;
+ EXPECT_ARRAY_NEAR(
SimulatePhotonNoise(/*xsize=*/6000, /*ysize=*/4000, /*iso=*/100).lut,
- Pointwise(AreApproximatelyEqual(),
- {0.00259652, 0.0139648, 0.00681551, 0.00632582, 0.00694917,
- 0.00803922, 0.00934574, 0.0107607}));
- EXPECT_THAT(
+ (NoiseParams::Lut{0.00259652, 0.0139648, 0.00681551, 0.00632582,
+ 0.00694917, 0.00803922, 0.00934574, 0.0107607}),
+ kTolerance);
+ EXPECT_ARRAY_NEAR(
SimulatePhotonNoise(/*xsize=*/6000, /*ysize=*/4000, /*iso=*/800).lut,
- Pointwise(AreApproximatelyEqual(),
- {0.02077220, 0.0420923, 0.01820690, 0.01439020, 0.01293670,
- 0.01254030, 0.01277390, 0.0134161}));
- EXPECT_THAT(
+ (NoiseParams::Lut{0.02077220, 0.0420923, 0.01820690, 0.01439020,
+ 0.01293670, 0.01254030, 0.01277390, 0.0134161}),
+ kTolerance);
+ EXPECT_ARRAY_NEAR(
SimulatePhotonNoise(/*xsize=*/6000, /*ysize=*/4000, /*iso=*/6400).lut,
- Pointwise(AreApproximatelyEqual(),
- {0.1661770, 0.1691120, 0.05309080, 0.03963960, 0.03357410,
- 0.03001650, 0.02776740, 0.0263478}));
+ (NoiseParams::Lut{0.1661770, 0.1691120, 0.05309080, 0.03963960,
+ 0.03357410, 0.03001650, 0.02776740, 0.0263478}),
+ kTolerance);
// Lower when measured on a per-pixel basis as there are fewer of them.
- EXPECT_THAT(
+ EXPECT_ARRAY_NEAR(
SimulatePhotonNoise(/*xsize=*/4000, /*ysize=*/3000, /*iso=*/6400).lut,
- Pointwise(AreApproximatelyEqual(),
- {0.0830886, 0.1008720, 0.0367748, 0.0280305, 0.0240236,
- 0.0218040, 0.0205771, 0.0200058}));
+ (NoiseParams::Lut{0.0830886, 0.1008720, 0.0367748, 0.0280305, 0.0240236,
+ 0.0218040, 0.0205771, 0.0200058}),
+ kTolerance);
}
} // namespace
diff --git a/third_party/jpeg-xl/lib/jxl/enc_quant_weights.cc b/third_party/jpeg-xl/lib/jxl/enc_quant_weights.cc
index 236ddaacfd..35e49d5993 100644
--- a/third_party/jpeg-xl/lib/jxl/enc_quant_weights.cc
+++ b/third_party/jpeg-xl/lib/jxl/enc_quant_weights.cc
@@ -5,24 +5,18 @@
#include "lib/jxl/enc_quant_weights.h"
+#include <jxl/types.h>
#include <stdlib.h>
-#include <algorithm>
#include <cmath>
-#include <limits>
-#include <utility>
-#include "lib/jxl/base/bits.h"
#include "lib/jxl/base/common.h"
#include "lib/jxl/base/status.h"
-#include "lib/jxl/dct_scales.h"
#include "lib/jxl/enc_aux_out.h"
#include "lib/jxl/enc_bit_writer.h"
#include "lib/jxl/enc_modular.h"
#include "lib/jxl/fields.h"
-#include "lib/jxl/image.h"
#include "lib/jxl/modular/encoding/encoding.h"
-#include "lib/jxl/modular/options.h"
namespace jxl {
@@ -95,8 +89,8 @@ Status EncodeQuant(const QuantEncoding& encoding, size_t idx, size_t size_x,
break;
}
case QuantEncoding::kQuantModeRAW: {
- ModularFrameEncoder::EncodeQuantTable(size_x, size_y, writer, encoding,
- idx, modular_frame_encoder);
+ JXL_RETURN_IF_ERROR(ModularFrameEncoder::EncodeQuantTable(
+ size_x, size_y, writer, encoding, idx, modular_frame_encoder));
break;
}
case QuantEncoding::kQuantModeAFV: {
@@ -130,7 +124,7 @@ Status DequantMatricesEncode(const DequantMatrices& matrices, BitWriter* writer,
}
// TODO(janwas): better bound
BitWriter::Allotment allotment(writer, 512 * 1024);
- writer->Write(1, all_default);
+ writer->Write(1, TO_JXL_BOOL(all_default));
if (!all_default) {
for (size_t i = 0; i < encodings.size(); i++) {
JXL_RETURN_IF_ERROR(EncodeQuant(
@@ -153,7 +147,7 @@ Status DequantMatricesEncodeDC(const DequantMatrices& matrices,
}
}
BitWriter::Allotment allotment(writer, 1 + sizeof(float) * kBitsPerByte * 3);
- writer->Write(1, all_default);
+ writer->Write(1, TO_JXL_BOOL(all_default));
if (!all_default) {
for (size_t c = 0; c < 3; c++) {
JXL_RETURN_IF_ERROR(F16Coder::Write(dc_quant[c] * 128.0f, writer));
@@ -195,19 +189,20 @@ void DequantMatricesRoundtrip(DequantMatrices* matrices) {
JXL_CHECK(br.Close());
}
-void DequantMatricesSetCustom(DequantMatrices* matrices,
- const std::vector<QuantEncoding>& encodings,
- ModularFrameEncoder* encoder) {
+Status DequantMatricesSetCustom(DequantMatrices* matrices,
+ const std::vector<QuantEncoding>& encodings,
+ ModularFrameEncoder* encoder) {
JXL_ASSERT(encodings.size() == DequantMatrices::kNum);
matrices->SetEncodings(encodings);
for (size_t i = 0; i < encodings.size(); i++) {
if (encodings[i].mode == QuantEncodingInternal::kQuantModeRAW) {
- encoder->AddQuantTable(DequantMatrices::required_size_x[i] * kBlockDim,
- DequantMatrices::required_size_y[i] * kBlockDim,
- encodings[i], i);
+ JXL_RETURN_IF_ERROR(encoder->AddQuantTable(
+ DequantMatrices::required_size_x[i] * kBlockDim,
+ DequantMatrices::required_size_y[i] * kBlockDim, encodings[i], i));
}
}
DequantMatricesRoundtrip(matrices);
+ return true;
}
} // namespace jxl
diff --git a/third_party/jpeg-xl/lib/jxl/enc_quant_weights.h b/third_party/jpeg-xl/lib/jxl/enc_quant_weights.h
index a47dfd4988..82d8278b72 100644
--- a/third_party/jpeg-xl/lib/jxl/enc_quant_weights.h
+++ b/third_party/jpeg-xl/lib/jxl/enc_quant_weights.h
@@ -27,9 +27,9 @@ void DequantMatricesSetCustomDC(DequantMatrices* matrices, const float* dc);
void DequantMatricesScaleDC(DequantMatrices* matrices, float scale);
-void DequantMatricesSetCustom(DequantMatrices* matrices,
- const std::vector<QuantEncoding>& encodings,
- ModularFrameEncoder* encoder);
+Status DequantMatricesSetCustom(DequantMatrices* matrices,
+ const std::vector<QuantEncoding>& encodings,
+ ModularFrameEncoder* encoder);
// Roundtrip encode/decode the matrices to ensure same values as decoder.
void DequantMatricesRoundtrip(DequantMatrices* matrices);
diff --git a/third_party/jpeg-xl/lib/jxl/enc_splines.cc b/third_party/jpeg-xl/lib/jxl/enc_splines.cc
index de6c9670ea..fa15648ca5 100644
--- a/third_party/jpeg-xl/lib/jxl/enc_splines.cc
+++ b/third_party/jpeg-xl/lib/jxl/enc_splines.cc
@@ -73,13 +73,13 @@ void EncodeSplines(const Splines& splines, BitWriter* writer,
splines.QuantizedSplines();
std::vector<std::vector<Token>> tokens(1);
tokens[0].emplace_back(kNumSplinesContext, quantized_splines.size() - 1);
- EncodeAllStartingPoints(splines.StartingPoints(), &tokens[0]);
+ EncodeAllStartingPoints(splines.StartingPoints(), tokens.data());
tokens[0].emplace_back(kQuantizationAdjustmentContext,
PackSigned(splines.GetQuantizationAdjustment()));
for (const QuantizedSpline& spline : quantized_splines) {
- QuantizedSplineEncoder::Tokenize(spline, &tokens[0]);
+ QuantizedSplineEncoder::Tokenize(spline, tokens.data());
}
EntropyEncodingData codes;
diff --git a/third_party/jpeg-xl/lib/jxl/enc_transforms.cc b/third_party/jpeg-xl/lib/jxl/enc_transforms.cc
index 8978ba1dcb..d116183404 100644
--- a/third_party/jpeg-xl/lib/jxl/enc_transforms.cc
+++ b/third_party/jpeg-xl/lib/jxl/enc_transforms.cc
@@ -21,20 +21,19 @@ void TransformFromPixels(const AcStrategy::Type strategy,
const float* JXL_RESTRICT pixels, size_t pixels_stride,
float* JXL_RESTRICT coefficients,
float* scratch_space) {
- return HWY_DYNAMIC_DISPATCH(TransformFromPixels)(
- strategy, pixels, pixels_stride, coefficients, scratch_space);
+ HWY_DYNAMIC_DISPATCH(TransformFromPixels)
+ (strategy, pixels, pixels_stride, coefficients, scratch_space);
}
HWY_EXPORT(DCFromLowestFrequencies);
void DCFromLowestFrequencies(AcStrategy::Type strategy, const float* block,
float* dc, size_t dc_stride) {
- return HWY_DYNAMIC_DISPATCH(DCFromLowestFrequencies)(strategy, block, dc,
- dc_stride);
+ HWY_DYNAMIC_DISPATCH(DCFromLowestFrequencies)(strategy, block, dc, dc_stride);
}
HWY_EXPORT(AFVDCT4x4);
void AFVDCT4x4(const float* JXL_RESTRICT pixels, float* JXL_RESTRICT coeffs) {
- return HWY_DYNAMIC_DISPATCH(AFVDCT4x4)(pixels, coeffs);
+ HWY_DYNAMIC_DISPATCH(AFVDCT4x4)(pixels, coeffs);
}
#endif // HWY_ONCE
diff --git a/third_party/jpeg-xl/lib/jxl/enc_transforms.h b/third_party/jpeg-xl/lib/jxl/enc_transforms.h
index 039ccc3893..f0ce95659d 100644
--- a/third_party/jpeg-xl/lib/jxl/enc_transforms.h
+++ b/third_party/jpeg-xl/lib/jxl/enc_transforms.h
@@ -9,14 +9,13 @@
// Facade for (non-inlined) integral transforms.
#include <stddef.h>
-#include <stdint.h>
#include "lib/jxl/ac_strategy.h"
#include "lib/jxl/base/compiler_specific.h"
namespace jxl {
-void TransformFromPixels(const AcStrategy::Type strategy,
+void TransformFromPixels(AcStrategy::Type strategy,
const float* JXL_RESTRICT pixels, size_t pixels_stride,
float* JXL_RESTRICT coefficients,
float* JXL_RESTRICT scratch_space);
diff --git a/third_party/jpeg-xl/lib/jxl/enc_xyb.cc b/third_party/jpeg-xl/lib/jxl/enc_xyb.cc
index e538e8c91d..9fc68d8474 100644
--- a/third_party/jpeg-xl/lib/jxl/enc_xyb.cc
+++ b/third_party/jpeg-xl/lib/jxl/enc_xyb.cc
@@ -21,9 +21,7 @@
#include "lib/jxl/cms/opsin_params.h"
#include "lib/jxl/cms/transfer_functions-inl.h"
#include "lib/jxl/color_encoding_internal.h"
-#include "lib/jxl/enc_bit_writer.h"
#include "lib/jxl/enc_image_bundle.h"
-#include "lib/jxl/fields.h"
#include "lib/jxl/image_bundle.h"
#include "lib/jxl/image_ops.h"
@@ -44,7 +42,7 @@ JXL_INLINE void OpsinAbsorbance(const V r, const V g, const V b,
const float* JXL_RESTRICT premul_absorb,
V* JXL_RESTRICT mixed0, V* JXL_RESTRICT mixed1,
V* JXL_RESTRICT mixed2) {
- const float* bias = &jxl::cms::kOpsinAbsorbanceBias[0];
+ const float* bias = jxl::cms::kOpsinAbsorbanceBias.data();
const HWY_FULL(float) d;
const size_t N = Lanes(d);
const auto m0 = Load(d, premul_absorb + 0 * N);
@@ -77,7 +75,9 @@ void LinearRGBToXYB(const V r, const V g, const V b,
const float* JXL_RESTRICT premul_absorb,
float* JXL_RESTRICT valx, float* JXL_RESTRICT valy,
float* JXL_RESTRICT valz) {
- V mixed0, mixed1, mixed2;
+ V mixed0;
+ V mixed1;
+ V mixed2;
OpsinAbsorbance(r, g, b, premul_absorb, &mixed0, &mixed1, &mixed2);
// mixed* should be non-negative even for wide-gamut, so clamp to zero.
@@ -198,9 +198,11 @@ void ComputePremulAbsorb(float intensity_target, float* premul_absorb) {
const HWY_FULL(float) d;
const size_t N = Lanes(d);
const float mul = intensity_target / 255.0f;
- for (size_t i = 0; i < 9; ++i) {
- const auto absorb = Set(d, jxl::cms::kOpsinAbsorbanceMatrix[i] * mul);
- Store(absorb, d, premul_absorb + i * N);
+ for (size_t j = 0; j < 3; ++j) {
+ for (size_t i = 0; i < 3; ++i) {
+ const auto absorb = Set(d, jxl::cms::kOpsinAbsorbanceMatrix[j][i] * mul);
+ Store(absorb, d, premul_absorb + (j * 3 + i) * N);
+ }
}
for (size_t i = 0; i < 3; ++i) {
const auto neg_bias_cbrt =
@@ -209,15 +211,16 @@ void ComputePremulAbsorb(float intensity_target, float* premul_absorb) {
}
}
-Image3F TransformToLinearRGB(const Image3F& in,
- const ColorEncoding& color_encoding,
- float intensity_target, const JxlCmsInterface& cms,
- ThreadPool* pool) {
+StatusOr<Image3F> TransformToLinearRGB(const Image3F& in,
+ const ColorEncoding& color_encoding,
+ float intensity_target,
+ const JxlCmsInterface& cms,
+ ThreadPool* pool) {
ColorSpaceTransform c_transform(cms);
bool is_gray = color_encoding.IsGray();
const ColorEncoding& c_desired = ColorEncoding::LinearSRGB(is_gray);
- Image3F out(in.xsize(), in.ysize());
- std::atomic<bool> ok{true};
+ JXL_ASSIGN_OR_RETURN(Image3F out, Image3F::Create(in.xsize(), in.ysize()));
+ std::atomic<bool> has_error{false};
JXL_CHECK(RunOnPool(
pool, 0, in.ysize(),
[&](const size_t num_threads) {
@@ -225,6 +228,7 @@ Image3F TransformToLinearRGB(const Image3F& in,
in.xsize(), num_threads);
},
[&](const uint32_t y, const size_t thread) {
+ if (has_error) return;
float* mutable_src_buf = c_transform.BufSrc(thread);
const float* src_buf = mutable_src_buf;
// Interleave input.
@@ -241,8 +245,8 @@ Image3F TransformToLinearRGB(const Image3F& in,
}
}
float* JXL_RESTRICT dst_buf = c_transform.BufDst(thread);
- if (!c_transform.Run(thread, src_buf, dst_buf)) {
- ok.store(false);
+ if (!c_transform.Run(thread, src_buf, dst_buf, in.xsize())) {
+ has_error = true;
return;
}
float* JXL_RESTRICT row_out0 = out.PlaneRow(0, y);
@@ -264,7 +268,7 @@ Image3F TransformToLinearRGB(const Image3F& in,
}
},
"Colorspace transform"));
- JXL_CHECK(ok.load());
+ JXL_CHECK(!has_error);
return out;
}
@@ -394,12 +398,13 @@ void ToXYB(const ColorEncoding& c_current, float intensity_target,
(c_current, intensity_target, black, pool, image, cms, linear);
}
-void ToXYB(const ImageBundle& in, ThreadPool* pool, Image3F* JXL_RESTRICT xyb,
- const JxlCmsInterface& cms, Image3F* JXL_RESTRICT linear) {
- *xyb = Image3F(in.xsize(), in.ysize());
+Status ToXYB(const ImageBundle& in, ThreadPool* pool, Image3F* JXL_RESTRICT xyb,
+ const JxlCmsInterface& cms, Image3F* JXL_RESTRICT linear) {
+ JXL_ASSIGN_OR_RETURN(*xyb, Image3F::Create(in.xsize(), in.ysize()));
CopyImageTo(in.color(), xyb);
ToXYB(in.c_current(), in.metadata()->IntensityTarget(),
in.HasBlack() ? &in.black() : nullptr, pool, xyb, cms, linear);
+ return true;
}
HWY_EXPORT(LinearRGBRowToXYB);
diff --git a/third_party/jpeg-xl/lib/jxl/enc_xyb.h b/third_party/jpeg-xl/lib/jxl/enc_xyb.h
index 6a2e7c4123..741d447b92 100644
--- a/third_party/jpeg-xl/lib/jxl/enc_xyb.h
+++ b/third_party/jpeg-xl/lib/jxl/enc_xyb.h
@@ -27,8 +27,9 @@ void ToXYB(const ColorEncoding& c_current, float intensity_target,
const ImageF* black, ThreadPool* pool, Image3F* JXL_RESTRICT image,
const JxlCmsInterface& cms, Image3F* JXL_RESTRICT linear);
-void ToXYB(const ImageBundle& in, ThreadPool* pool, Image3F* JXL_RESTRICT xyb,
- const JxlCmsInterface& cms, Image3F* JXL_RESTRICT linear = nullptr);
+Status ToXYB(const ImageBundle& in, ThreadPool* pool, Image3F* JXL_RESTRICT xyb,
+ const JxlCmsInterface& cms,
+ Image3F* JXL_RESTRICT linear = nullptr);
void LinearRGBRowToXYB(float* JXL_RESTRICT row0, float* JXL_RESTRICT row1,
float* JXL_RESTRICT row2,
diff --git a/third_party/jpeg-xl/lib/jxl/encode.cc b/third_party/jpeg-xl/lib/jxl/encode.cc
index 76f2148d62..4dbbeba4e7 100644
--- a/third_party/jpeg-xl/lib/jxl/encode.cc
+++ b/third_party/jpeg-xl/lib/jxl/encode.cc
@@ -595,21 +595,21 @@ JxlEncoderStatus VerifyInputBitDepth(JxlBitDepth bit_depth,
return JxlErrorOrStatus::Success();
}
-static inline bool EncodeVarInt(uint64_t value, size_t output_size,
- size_t* output_pos, uint8_t* output) {
+inline bool EncodeVarInt(uint64_t value, size_t output_size, size_t* output_pos,
+ uint8_t* output) {
// While more than 7 bits of data are left,
// store 7 bits and set the next byte flag
while (value > 127) {
// TODO(eustas): should it be `>=` ?
if (*output_pos > output_size) return false;
// |128: Set the next byte flag
- output[(*output_pos)++] = ((uint8_t)(value & 127)) | 128;
+ output[(*output_pos)++] = (static_cast<uint8_t>(value & 127)) | 128;
// Remove the seven bits we just wrote
value >>= 7;
}
// TODO(eustas): should it be `>=` ?
if (*output_pos > output_size) return false;
- output[(*output_pos)++] = ((uint8_t)value) & 127;
+ output[(*output_pos)++] = static_cast<uint8_t>(value & 127);
return true;
}
@@ -854,7 +854,7 @@ jxl::Status JxlEncoderStruct::ProcessOneEnqueuedInput() {
timecode = 0;
}
- const bool last_frame = frames_closed && !num_queued_frames;
+ const bool last_frame = frames_closed && (num_queued_frames == 0);
uint32_t max_bits_per_sample = metadata.m.bit_depth.bits_per_sample;
for (const auto& info : metadata.m.extra_channel_info) {
@@ -901,7 +901,7 @@ jxl::Status JxlEncoderStruct::ProcessOneEnqueuedInput() {
return JXL_API_ERROR(
this, JXL_ENC_ERR_API_USAGE,
"Cannot use save_as_reference values >=3 (found: %d)",
- (int)save_as_reference);
+ static_cast<int>(save_as_reference));
}
jxl::FrameInfo frame_info;
@@ -909,8 +909,8 @@ jxl::Status JxlEncoderStruct::ProcessOneEnqueuedInput() {
frame_info.save_as_reference = save_as_reference;
frame_info.source =
input_frame->option_values.header.layer_info.blend_info.source;
- frame_info.clamp =
- input_frame->option_values.header.layer_info.blend_info.clamp;
+ frame_info.clamp = FROM_JXL_BOOL(
+ input_frame->option_values.header.layer_info.blend_info.clamp);
frame_info.alpha_channel =
input_frame->option_values.header.layer_info.blend_info.alpha;
frame_info.extra_channel_blending_info.resize(
@@ -989,7 +989,7 @@ jxl::Status JxlEncoderStruct::ProcessOneEnqueuedInput() {
#endif
jxl::WriteBoxHeader(jxl::MakeBoxType("jxlc"), frame_codestream_size,
/*unbounded=*/false, use_large_box,
- &box_header[0]);
+ box_header.data());
JXL_ASSERT(n == box_header_size);
} else {
#if JXL_ENABLE_ASSERT
@@ -997,7 +997,7 @@ jxl::Status JxlEncoderStruct::ProcessOneEnqueuedInput() {
#endif
jxl::WriteBoxHeader(
jxl::MakeBoxType("jxlp"), frame_codestream_size + 4,
- /*unbounded=*/false, use_large_box, &box_header[0]);
+ /*unbounded=*/false, use_large_box, box_header.data());
JXL_ASSERT(n == box_header_size - 4);
WriteJxlpBoxCounter(jxlp_counter++, last_frame,
&box_header[box_header_size - 4]);
@@ -1061,15 +1061,17 @@ JxlEncoderStatus JxlEncoderSetColorEncoding(JxlEncoder* enc,
}
if (enc->metadata.m.color_encoding.GetColorSpace() ==
jxl::ColorSpace::kGray) {
- if (enc->basic_info.num_color_channels != 1)
+ if (enc->basic_info.num_color_channels != 1) {
return JXL_API_ERROR(
enc, JXL_ENC_ERR_API_USAGE,
"Cannot use grayscale color encoding with num_color_channels != 1");
+ }
} else {
- if (enc->basic_info.num_color_channels != 3)
+ if (enc->basic_info.num_color_channels != 3) {
return JXL_API_ERROR(
enc, JXL_ENC_ERR_API_USAGE,
"Cannot use RGB color encoding with num_color_channels != 3");
+ }
}
enc->color_encoding_set = true;
if (!enc->intensity_target_set) {
@@ -1103,15 +1105,17 @@ JxlEncoderStatus JxlEncoderSetICCProfile(JxlEncoder* enc,
}
if (enc->metadata.m.color_encoding.GetColorSpace() ==
jxl::ColorSpace::kGray) {
- if (enc->basic_info.num_color_channels != 1)
+ if (enc->basic_info.num_color_channels != 1) {
return JXL_API_ERROR(
enc, JXL_ENC_ERR_BAD_INPUT,
"Cannot use grayscale ICC profile with num_color_channels != 1");
+ }
} else {
- if (enc->basic_info.num_color_channels != 3)
+ if (enc->basic_info.num_color_channels != 3) {
return JXL_API_ERROR(
enc, JXL_ENC_ERR_BAD_INPUT,
"Cannot use RGB ICC profile with num_color_channels != 3");
+ }
// TODO(jon): also check that a kBlack extra channel is provided in the CMYK
// case
}
@@ -1210,7 +1214,8 @@ JxlEncoderStatus JxlEncoderSetBasicInfo(JxlEncoder* enc,
enc->metadata.m.bit_depth.floating_point_sample =
(info->exponent_bits_per_sample != 0u);
enc->metadata.m.modular_16_bit_buffer_sufficient =
- (!info->uses_original_profile || info->bits_per_sample <= 12) &&
+ (!FROM_JXL_BOOL(info->uses_original_profile) ||
+ info->bits_per_sample <= 12) &&
info->alpha_bits <= 12;
if ((info->intrinsic_xsize > 0 || info->intrinsic_ysize > 0) &&
(info->intrinsic_xsize != info->xsize ||
@@ -1248,7 +1253,7 @@ JxlEncoderStatus JxlEncoderSetBasicInfo(JxlEncoder* enc,
}
}
- enc->metadata.m.xyb_encoded = !info->uses_original_profile;
+ enc->metadata.m.xyb_encoded = !FROM_JXL_BOOL(info->uses_original_profile);
if (info->orientation > 0 && info->orientation <= 8) {
enc->metadata.m.orientation = info->orientation;
} else {
@@ -1271,12 +1276,12 @@ JxlEncoderStatus JxlEncoderSetBasicInfo(JxlEncoder* enc,
}
enc->metadata.m.tone_mapping.min_nits = info->min_nits;
enc->metadata.m.tone_mapping.relative_to_max_display =
- info->relative_to_max_display;
+ FROM_JXL_BOOL(info->relative_to_max_display);
enc->metadata.m.tone_mapping.linear_below = info->linear_below;
enc->basic_info = *info;
enc->basic_info_set = true;
- enc->metadata.m.have_animation = info->have_animation;
+ enc->metadata.m.have_animation = FROM_JXL_BOOL(info->have_animation);
if (info->have_animation) {
if (info->animation.tps_denominator < 1) {
return JXL_API_ERROR(
@@ -1290,7 +1295,8 @@ JxlEncoderStatus JxlEncoderSetBasicInfo(JxlEncoder* enc,
enc->metadata.m.animation.tps_numerator = info->animation.tps_numerator;
enc->metadata.m.animation.tps_denominator = info->animation.tps_denominator;
enc->metadata.m.animation.num_loops = info->animation.num_loops;
- enc->metadata.m.animation.have_timecodes = info->animation.have_timecodes;
+ enc->metadata.m.animation.have_timecodes =
+ FROM_JXL_BOOL(info->animation.have_timecodes);
}
std::string level_message;
int required_level = VerifyLevelSettings(enc, &level_message);
@@ -1326,14 +1332,16 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderSetUpsamplingMode(JxlEncoder* enc,
const int64_t mode) {
// for convenience, allow calling this with factor 1 and just make it a no-op
if (factor == 1) return JxlErrorOrStatus::Success();
- if (factor != 2 && factor != 4 && factor != 8)
+ if (factor != 2 && factor != 4 && factor != 8) {
return JXL_API_ERROR(enc, JXL_ENC_ERR_API_USAGE,
"Invalid upsampling factor");
+ }
if (mode < -1)
return JXL_API_ERROR(enc, JXL_ENC_ERR_API_USAGE, "Invalid upsampling mode");
- if (mode > 1)
+ if (mode > 1) {
return JXL_API_ERROR(enc, JXL_ENC_ERR_NOT_SUPPORTED,
"Unsupported upsampling mode");
+ }
const size_t count = (factor == 2 ? 15 : (factor == 4 ? 55 : 210));
auto& td = enc->metadata.transform_data;
@@ -1436,7 +1444,7 @@ JxlEncoderFrameSettings* JxlEncoderFrameSettingsCreate(
}
opts->values.cparams.level = enc->codestream_level;
opts->values.cparams.ec_distance.resize(enc->metadata.m.num_extra_channels,
- -1);
+ 0);
JxlEncoderFrameSettings* ret = opts.get();
enc->encoder_options.emplace_back(std::move(opts));
@@ -1451,7 +1459,7 @@ JxlEncoderStatus JxlEncoderSetFrameLossless(
frame_settings->enc, JXL_ENC_ERR_API_USAGE,
"Set uses_original_profile=true for lossless encoding");
}
- frame_settings->values.lossless = lossless;
+ frame_settings->values.lossless = FROM_JXL_BOOL(lossless);
return JxlErrorOrStatus::Success();
}
@@ -1489,7 +1497,7 @@ JxlEncoderStatus JxlEncoderSetExtraChannelDistance(
// This can only happen if JxlEncoderFrameSettingsCreate() was called before
// JxlEncoderSetBasicInfo().
frame_settings->values.cparams.ec_distance.resize(
- frame_settings->enc->metadata.m.num_extra_channels, -1);
+ frame_settings->enc->metadata.m.num_extra_channels, 0);
}
frame_settings->values.cparams.ec_distance[index] = distance;
@@ -1537,14 +1545,14 @@ JxlEncoderStatus JxlEncoderFrameSettingsSetOption(
switch (option) {
case JXL_ENC_FRAME_SETTING_EFFORT:
if (frame_settings->enc->allow_expert_options) {
- if (value < 1 || value > 10) {
+ if (value < 1 || value > 11) {
return JXL_API_ERROR(frame_settings->enc, JXL_ENC_ERR_NOT_SUPPORTED,
- "Encode effort has to be in [1..10]");
+ "Encode effort has to be in [1..11]");
}
} else {
- if (value < 1 || value > 9) {
+ if (value < 1 || value > 10) {
return JXL_API_ERROR(frame_settings->enc, JXL_ENC_ERR_NOT_SUPPORTED,
- "Encode effort has to be in [1..9]");
+ "Encode effort has to be in [1..10]");
}
}
frame_settings->values.cparams.speed_tier =
@@ -1747,9 +1755,9 @@ JxlEncoderStatus JxlEncoderFrameSettingsSetOption(
frame_settings->values.cparams.jpeg_compress_boxes = value;
break;
case JXL_ENC_FRAME_SETTING_BUFFERING:
- if (value < 0 || value > 3) {
+ if (value < -1 || value > 3) {
return JXL_API_ERROR(frame_settings->enc, JXL_ENC_ERR_NOT_SUPPORTED,
- "Buffering has to be in [0..3]");
+ "Buffering has to be in [-1..3]");
}
frame_settings->values.cparams.buffering = value;
break;
@@ -1934,7 +1942,7 @@ JxlEncoderStatus JxlEncoderUseContainer(JxlEncoder* enc,
return JXL_API_ERROR(enc, JXL_ENC_ERR_API_USAGE,
"this setting can only be set at the beginning");
}
- enc->use_container = static_cast<bool>(use_container);
+ enc->use_container = FROM_JXL_BOOL(use_container);
return JxlErrorOrStatus::Success();
}
@@ -1944,7 +1952,7 @@ JxlEncoderStatus JxlEncoderStoreJPEGMetadata(JxlEncoder* enc,
return JXL_API_ERROR(enc, JXL_ENC_ERR_API_USAGE,
"this setting can only be set at the beginning");
}
- enc->store_jpeg_metadata = static_cast<bool>(store_jpeg_metadata);
+ enc->store_jpeg_metadata = FROM_JXL_BOOL(store_jpeg_metadata);
return JxlErrorOrStatus::Success();
}
@@ -2034,7 +2042,7 @@ JxlEncoderStatus JxlEncoderAddJPEGFrame(
JxlEncoderInitBasicInfo(&basic_info);
basic_info.xsize = io.Main().jpeg_data->width;
basic_info.ysize = io.Main().jpeg_data->height;
- basic_info.uses_original_profile = true;
+ basic_info.uses_original_profile = JXL_TRUE;
if (JxlEncoderSetBasicInfo(frame_settings->enc, &basic_info) !=
JXL_ENC_SUCCESS) {
return JXL_API_ERROR(frame_settings->enc, JXL_ENC_ERR_GENERIC,
@@ -2042,7 +2050,8 @@ JxlEncoderStatus JxlEncoderAddJPEGFrame(
}
}
- size_t xsize, ysize;
+ size_t xsize;
+ size_t ysize;
if (GetCurrentDimensions(frame_settings, xsize, ysize) != JXL_ENC_SUCCESS) {
return JXL_API_ERROR(frame_settings->enc, JXL_ENC_ERR_GENERIC,
"bad dimensions");
@@ -2074,21 +2083,23 @@ JxlEncoderStatus JxlEncoderAddJPEGFrame(
std::vector<uint8_t> exif(exif_size);
memcpy(exif.data() + 4, io.blobs.exif.data(), io.blobs.exif.size());
JxlEncoderUseBoxes(frame_settings->enc);
- JxlEncoderAddBox(frame_settings->enc, "Exif", exif.data(), exif_size,
- frame_settings->values.cparams.jpeg_compress_boxes);
+ JxlEncoderAddBox(
+ frame_settings->enc, "Exif", exif.data(), exif_size,
+ TO_JXL_BOOL(frame_settings->values.cparams.jpeg_compress_boxes));
}
if (!io.blobs.xmp.empty() && frame_settings->values.cparams.jpeg_keep_xmp) {
JxlEncoderUseBoxes(frame_settings->enc);
- JxlEncoderAddBox(frame_settings->enc, "xml ", io.blobs.xmp.data(),
- io.blobs.xmp.size(),
- frame_settings->values.cparams.jpeg_compress_boxes);
+ JxlEncoderAddBox(
+ frame_settings->enc, "xml ", io.blobs.xmp.data(), io.blobs.xmp.size(),
+ TO_JXL_BOOL(frame_settings->values.cparams.jpeg_compress_boxes));
}
if (!io.blobs.jumbf.empty() &&
frame_settings->values.cparams.jpeg_keep_jumbf) {
JxlEncoderUseBoxes(frame_settings->enc);
- JxlEncoderAddBox(frame_settings->enc, "jumb", io.blobs.jumbf.data(),
- io.blobs.jumbf.size(),
- frame_settings->values.cparams.jpeg_compress_boxes);
+ JxlEncoderAddBox(
+ frame_settings->enc, "jumb", io.blobs.jumbf.data(),
+ io.blobs.jumbf.size(),
+ TO_JXL_BOOL(frame_settings->values.cparams.jpeg_compress_boxes));
}
if (frame_settings->enc->store_jpeg_metadata) {
if (!frame_settings->values.cparams.jpeg_keep_exif ||
@@ -2259,10 +2270,12 @@ JxlEncoderStatus JxlEncoderAddImageFrameInternal(
pool, 0, count, jxl::ThreadPool::NoInit,
[&](size_t i, size_t) { fun(opaque, i); }, "Encode fast lossless"));
};
- auto frame_state = JxlFastLosslessPrepareFrame(
+ JXL_BOOL oneshot = TO_JXL_BOOL(!frame_data.StreamingInput());
+ auto* frame_state = JxlFastLosslessPrepareFrame(
frame_data.GetInputSource(), xsize, ysize, num_channels,
- frame_settings->enc->metadata.m.bit_depth.bits_per_sample, big_endian,
- /*effort=*/2, /*oneshot=*/!frame_data.StreamingInput());
+ frame_settings->enc->metadata.m.bit_depth.bits_per_sample,
+ TO_JXL_BOOL(big_endian),
+ /*effort=*/2, oneshot);
if (!streaming) {
JxlFastLosslessProcessFrame(frame_state, /*is_last=*/false,
frame_settings->enc->thread_pool.get(),
@@ -2325,7 +2338,8 @@ JxlEncoderStatus JxlEncoderAddImageFrameInternal(
JxlEncoderStatus JxlEncoderAddImageFrame(
const JxlEncoderFrameSettings* frame_settings,
const JxlPixelFormat* pixel_format, const void* buffer, size_t size) {
- size_t xsize, ysize;
+ size_t xsize;
+ size_t ysize;
if (GetCurrentDimensions(frame_settings, xsize, ysize) != JXL_ENC_SUCCESS) {
return JXL_API_ERROR(frame_settings->enc, JXL_ENC_ERR_GENERIC,
"bad dimensions");
@@ -2345,7 +2359,8 @@ JxlEncoderStatus JxlEncoderAddImageFrame(
JxlEncoderStatus JxlEncoderAddChunkedFrame(
const JxlEncoderFrameSettings* frame_settings, JXL_BOOL is_last_frame,
JxlChunkedFrameInputSource chunked_frame_input) {
- size_t xsize, ysize;
+ size_t xsize;
+ size_t ysize;
if (GetCurrentDimensions(frame_settings, xsize, ysize) != JXL_ENC_SUCCESS) {
return JXL_API_ERROR(frame_settings->enc, JXL_ENC_ERR_GENERIC,
"bad dimensions");
@@ -2417,7 +2432,7 @@ JxlEncoderStatus JxlEncoderAddBox(JxlEncoder* enc, const JxlBoxType type,
box->type = jxl::MakeBoxType(type);
box->contents.assign(contents, contents + size);
- box->compress_box = !!compress_box;
+ box->compress_box = FROM_JXL_BOOL(compress_box);
QueueBox(enc, box);
return JxlErrorOrStatus::Success();
}
@@ -2450,7 +2465,7 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderSetExtraChannelBuffer(
return JXL_API_ERROR_NOSET("Invalid input bit depth");
}
const uint8_t* uint8_buffer = reinterpret_cast<const uint8_t*>(buffer);
- auto queued_frame = frame_settings->enc->input_queue.back().frame.get();
+ auto* queued_frame = frame_settings->enc->input_queue.back().frame.get();
if (!queued_frame->frame_data.SetFromBuffer(1 + index, uint8_buffer, size,
ec_format)) {
return JXL_API_ERROR(frame_settings->enc, JXL_ENC_ERR_API_USAGE,
@@ -2588,12 +2603,14 @@ JxlEncoderStatus JxlEncoderSetFrameBitDepth(
void JxlColorEncodingSetToSRGB(JxlColorEncoding* color_encoding,
JXL_BOOL is_gray) {
- *color_encoding = jxl::ColorEncoding::SRGB(is_gray).ToExternal();
+ *color_encoding =
+ jxl::ColorEncoding::SRGB(FROM_JXL_BOOL(is_gray)).ToExternal();
}
void JxlColorEncodingSetToLinearSRGB(JxlColorEncoding* color_encoding,
JXL_BOOL is_gray) {
- *color_encoding = jxl::ColorEncoding::LinearSRGB(is_gray).ToExternal();
+ *color_encoding =
+ jxl::ColorEncoding::LinearSRGB(FROM_JXL_BOOL(is_gray)).ToExternal();
}
void JxlEncoderAllowExpertOptions(JxlEncoder* enc) {
@@ -2611,9 +2628,7 @@ JXL_EXPORT JxlEncoderStats* JxlEncoderStatsCreate() {
return new JxlEncoderStats();
}
-JXL_EXPORT void JxlEncoderStatsDestroy(JxlEncoderStats* stats) {
- if (stats) delete stats;
-}
+JXL_EXPORT void JxlEncoderStatsDestroy(JxlEncoderStats* stats) { delete stats; }
JXL_EXPORT void JxlEncoderCollectStats(JxlEncoderFrameSettings* frame_settings,
JxlEncoderStats* stats) {
diff --git a/third_party/jpeg-xl/lib/jxl/encode_internal.h b/third_party/jpeg-xl/lib/jxl/encode_internal.h
index e89993f253..001df5fed5 100644
--- a/third_party/jpeg-xl/lib/jxl/encode_internal.h
+++ b/third_party/jpeg-xl/lib/jxl/encode_internal.h
@@ -580,12 +580,9 @@ jxl::Status AppendData(JxlEncoderOutputProcessorWrapper& output_processor,
// Internal use only struct, can only be initialized correctly by
// JxlEncoderCreate.
struct JxlEncoderStruct {
- JxlEncoderError error = JxlEncoderError::JXL_ENC_ERR_OK;
JxlMemoryManager memory_manager;
jxl::MemoryManagerUniquePtr<jxl::ThreadPool> thread_pool{
nullptr, jxl::MemoryManagerDeleteHelper(&memory_manager)};
- JxlCmsInterface cms;
- bool cms_set;
std::vector<jxl::MemoryManagerUniquePtr<JxlEncoderFrameSettings>>
encoder_options;
@@ -603,6 +600,9 @@ struct JxlEncoderStruct {
size_t codestream_bytes_written_end_of_frame;
jxl::JxlEncoderFrameIndexBox frame_index_box;
+ JxlCmsInterface cms;
+ bool cms_set;
+
// Force using the container even if not needed
bool use_container;
// User declared they will add metadata boxes
@@ -611,23 +611,26 @@ struct JxlEncoderStruct {
// TODO(lode): move level into jxl::CompressParams since some C++
// implementation decisions should be based on it: level 10 allows more
// features to be used.
- int32_t codestream_level;
bool store_jpeg_metadata;
+ int32_t codestream_level;
jxl::CodecMetadata metadata;
std::vector<uint8_t> jpeg_metadata;
- // Wrote any output at all, so wrote the data before the first user added
- // frame or box, such as signature, basic info, ICC profile or jpeg
- // reconstruction box.
- bool wrote_bytes;
jxl::CompressParams last_used_cparams;
JxlBasicInfo basic_info;
+ JxlEncoderError error = JxlEncoderError::JXL_ENC_ERR_OK;
+
// Encoder wrote a jxlp (partial codestream) box, so any next codestream
// parts must also be written in jxlp boxes, a single jxlc box cannot be
// used. The counter is used for the 4-byte jxlp box index header.
size_t jxlp_counter;
+ // Wrote any output at all, so wrote the data before the first user added
+ // frame or box, such as signature, basic info, ICC profile or jpeg
+ // reconstruction box.
+ bool wrote_bytes;
+
bool frames_closed;
bool boxes_closed;
bool basic_info_set;
diff --git a/third_party/jpeg-xl/lib/jxl/encode_test.cc b/third_party/jpeg-xl/lib/jxl/encode_test.cc
index 2c17fcab21..3e519cc45d 100644
--- a/third_party/jpeg-xl/lib/jxl/encode_test.cc
+++ b/third_party/jpeg-xl/lib/jxl/encode_test.cc
@@ -33,6 +33,7 @@
#include "lib/extras/packed_image.h"
#include "lib/jxl/base/byte_order.h"
#include "lib/jxl/base/c_callback_support.h"
+#include "lib/jxl/base/override.h"
#include "lib/jxl/base/span.h"
#include "lib/jxl/base/status.h"
#include "lib/jxl/common.h" // JXL_HIGH_PRECISION
@@ -79,16 +80,16 @@ TEST(EncodeTest, AddFrameAfterCloseInputTest) {
jxl::test::JxlBasicInfoSetFromPixelFormat(&basic_info, &pixel_format);
basic_info.xsize = xsize;
basic_info.ysize = ysize;
- basic_info.uses_original_profile = false;
+ basic_info.uses_original_profile = 0;
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetCodestreamLevel(enc.get(), 10));
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetBasicInfo(enc.get(), &basic_info));
JxlColorEncoding color_encoding;
- JxlColorEncodingSetToSRGB(&color_encoding,
- /*is_gray=*/pixel_format.num_channels < 3);
+ JXL_BOOL is_gray = TO_JXL_BOOL(pixel_format.num_channels < 3);
+ JxlColorEncodingSetToSRGB(&color_encoding, is_gray);
EXPECT_EQ(JXL_ENC_SUCCESS,
JxlEncoderSetColorEncoding(enc.get(), &color_encoding));
JxlEncoderFrameSettings* frame_settings =
- JxlEncoderFrameSettingsCreate(enc.get(), NULL);
+ JxlEncoderFrameSettingsCreate(enc.get(), nullptr);
EXPECT_EQ(JXL_ENC_ERROR,
JxlEncoderAddImageFrame(frame_settings, &pixel_format,
pixels.data(), pixels.size()));
@@ -104,7 +105,7 @@ TEST(EncodeTest, AddJPEGAfterCloseTest) {
const std::vector<uint8_t> orig = jxl::test::ReadTestData(jpeg_path);
JxlEncoderFrameSettings* frame_settings =
- JxlEncoderFrameSettingsCreate(enc.get(), NULL);
+ JxlEncoderFrameSettingsCreate(enc.get(), nullptr);
EXPECT_EQ(JXL_ENC_ERROR,
JxlEncoderAddJPEGFrame(frame_settings, orig.data(), orig.size()));
@@ -123,12 +124,12 @@ TEST(EncodeTest, AddFrameBeforeBasicInfoTest) {
jxl::test::SomeTestImageToCodecInOut(pixels, 4, xsize, ysize);
JxlColorEncoding color_encoding;
- JxlColorEncodingSetToSRGB(&color_encoding,
- /*is_gray=*/pixel_format.num_channels < 3);
+ JXL_BOOL is_gray = TO_JXL_BOOL(pixel_format.num_channels < 3);
+ JxlColorEncodingSetToSRGB(&color_encoding, is_gray);
EXPECT_EQ(JXL_ENC_ERROR,
JxlEncoderSetColorEncoding(enc.get(), &color_encoding));
JxlEncoderFrameSettings* frame_settings =
- JxlEncoderFrameSettingsCreate(enc.get(), NULL);
+ JxlEncoderFrameSettingsCreate(enc.get(), nullptr);
EXPECT_EQ(JXL_ENC_ERROR,
JxlEncoderAddImageFrame(frame_settings, &pixel_format,
pixels.data(), pixels.size()));
@@ -188,17 +189,17 @@ void VerifyFrameEncoding(size_t xsize, size_t ysize, JxlEncoder* enc,
basic_info.xsize = xsize;
basic_info.ysize = ysize;
if (frame_settings->values.lossless || lossy_use_original_profile) {
- basic_info.uses_original_profile = true;
+ basic_info.uses_original_profile = JXL_TRUE;
} else {
- basic_info.uses_original_profile = false;
+ basic_info.uses_original_profile = JXL_FALSE;
}
// 16-bit alpha means this requires level 10
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetCodestreamLevel(enc, 10));
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetBasicInfo(enc, &basic_info));
JxlColorEncoding color_encoding;
- JxlColorEncodingSetToSRGB(&color_encoding, true);
+ JxlColorEncodingSetToSRGB(&color_encoding, JXL_TRUE);
EXPECT_EQ(JXL_ENC_ERROR, JxlEncoderSetColorEncoding(enc, &color_encoding));
- JxlColorEncodingSetToSRGB(&color_encoding, false);
+ JxlColorEncodingSetToSRGB(&color_encoding, JXL_FALSE);
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetColorEncoding(enc, &color_encoding));
pixel_format.num_channels = 1;
EXPECT_EQ(JXL_ENC_ERROR,
@@ -230,18 +231,20 @@ void VerifyFrameEncoding(size_t xsize, size_t ysize, JxlEncoder* enc,
EXPECT_TRUE(jxl::test::DecodeFile(
{}, jxl::Bytes(compressed.data(), compressed.size()), &decoded_io));
- EXPECT_LE(
- ComputeDistance2(input_io.Main(), decoded_io.Main(), *JxlGetDefaultCms()),
+ static constexpr double kMaxButteraugli =
#if JXL_HIGH_PRECISION
- 1.84);
+ 1.84;
#else
- 8.7);
+ 8.7;
#endif
+ EXPECT_LE(
+ ComputeDistance2(input_io.Main(), decoded_io.Main(), *JxlGetDefaultCms()),
+ kMaxButteraugli);
}
void VerifyFrameEncoding(JxlEncoder* enc,
const JxlEncoderFrameSettings* frame_settings) {
- VerifyFrameEncoding(63, 129, enc, frame_settings, 2700,
+ VerifyFrameEncoding(63, 129, enc, frame_settings, 27000,
/*lossy_use_original_profile=*/false);
}
@@ -256,7 +259,7 @@ TEST(EncodeTest, EncoderResetTest) {
JxlEncoderPtr enc = JxlEncoderMake(nullptr);
EXPECT_NE(nullptr, enc.get());
VerifyFrameEncoding(50, 200, enc.get(),
- JxlEncoderFrameSettingsCreate(enc.get(), nullptr), 4300,
+ JxlEncoderFrameSettingsCreate(enc.get(), nullptr), 4550,
false);
// Encoder should become reusable for a new image from scratch after using
// reset.
@@ -293,7 +296,7 @@ TEST(EncodeTest, CmsTest) {
JxlEncoderSetCms(enc.get(), cms);
JxlEncoderFrameSettings* frame_settings =
JxlEncoderFrameSettingsCreate(enc.get(), nullptr);
- JxlEncoderSetFrameLossless(frame_settings, false);
+ JxlEncoderSetFrameLossless(frame_settings, JXL_FALSE);
ASSERT_EQ(JXL_ENC_SUCCESS,
JxlEncoderFrameSettingsSetOption(frame_settings,
JXL_ENC_FRAME_SETTING_EFFORT, 8));
@@ -306,7 +309,7 @@ TEST(EncodeTest, frame_settingsTest) {
JxlEncoderPtr enc = JxlEncoderMake(nullptr);
EXPECT_NE(nullptr, enc.get());
JxlEncoderFrameSettings* frame_settings =
- JxlEncoderFrameSettingsCreate(enc.get(), NULL);
+ JxlEncoderFrameSettingsCreate(enc.get(), nullptr);
EXPECT_EQ(JXL_ENC_SUCCESS,
JxlEncoderFrameSettingsSetOption(
frame_settings, JXL_ENC_FRAME_SETTING_EFFORT, 5));
@@ -318,7 +321,7 @@ TEST(EncodeTest, frame_settingsTest) {
JxlEncoderPtr enc = JxlEncoderMake(nullptr);
EXPECT_NE(nullptr, enc.get());
JxlEncoderFrameSettings* frame_settings =
- JxlEncoderFrameSettingsCreate(enc.get(), NULL);
+ JxlEncoderFrameSettingsCreate(enc.get(), nullptr);
const size_t nb_options = 23;
const JxlEncoderFrameSettingId options[nb_options] = {
JXL_ENC_FRAME_SETTING_EFFORT,
@@ -346,7 +349,7 @@ TEST(EncodeTest, frame_settingsTest) {
JXL_ENC_FRAME_SETTING_JPEG_KEEP_JUMBF};
const int too_low[nb_options] = {0, -2, -2, 3, -2, -2, -2, -2,
-2, -2, -2, -2, -2, -2, -2, -2,
- -2, -1, -2, -1, -2, -2, -2};
+ -2, -1, -2, -2, -2, -2, -2};
const int too_high[nb_options] = {11, 12, 5, 16, 6, 2, 4, -3,
-3, 3, 70914, 3, 42, 4, 16, 12,
2, 2, 2, 4, 2, 2, 2};
@@ -367,14 +370,14 @@ TEST(EncodeTest, frame_settingsTest) {
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderFrameSettingsSetOption(
frame_settings, options[i], in_range[i]));
}
- // Effort 10 should only work when expert options are allowed
+ // Effort 11 should only work when expert options are allowed
EXPECT_EQ(JXL_ENC_ERROR,
JxlEncoderFrameSettingsSetOption(
- frame_settings, JXL_ENC_FRAME_SETTING_EFFORT, 10));
+ frame_settings, JXL_ENC_FRAME_SETTING_EFFORT, 11));
JxlEncoderAllowExpertOptions(enc.get());
EXPECT_EQ(JXL_ENC_SUCCESS,
JxlEncoderFrameSettingsSetOption(
- frame_settings, JXL_ENC_FRAME_SETTING_EFFORT, 10));
+ frame_settings, JXL_ENC_FRAME_SETTING_EFFORT, 11));
// Non-existing option
EXPECT_EQ(JXL_ENC_ERROR,
@@ -438,14 +441,14 @@ TEST(EncodeTest, frame_settingsTest) {
JxlEncoderFrameSettingsSetOption(
frame_settings, JXL_ENC_FRAME_SETTING_PHOTON_NOISE, 50.0f));
- VerifyFrameEncoding(63, 129, enc.get(), frame_settings, 2500, false);
+ VerifyFrameEncoding(63, 129, enc.get(), frame_settings, 3700, false);
}
{
JxlEncoderPtr enc = JxlEncoderMake(nullptr);
EXPECT_NE(nullptr, enc.get());
JxlEncoderFrameSettings* frame_settings =
- JxlEncoderFrameSettingsCreate(enc.get(), NULL);
+ JxlEncoderFrameSettingsCreate(enc.get(), nullptr);
EXPECT_EQ(JXL_ENC_SUCCESS,
JxlEncoderSetFrameLossless(frame_settings, JXL_TRUE));
VerifyFrameEncoding(63, 129, enc.get(), frame_settings, 3000, false);
@@ -456,16 +459,16 @@ TEST(EncodeTest, frame_settingsTest) {
JxlEncoderPtr enc = JxlEncoderMake(nullptr);
EXPECT_NE(nullptr, enc.get());
JxlEncoderFrameSettings* frame_settings =
- JxlEncoderFrameSettingsCreate(enc.get(), NULL);
+ JxlEncoderFrameSettingsCreate(enc.get(), nullptr);
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetFrameDistance(frame_settings, 0.5));
- VerifyFrameEncoding(63, 129, enc.get(), frame_settings, 3030, false);
+ VerifyFrameEncoding(63, 129, enc.get(), frame_settings, 3130, false);
EXPECT_EQ(0.5, enc->last_used_cparams.butteraugli_distance);
}
{
JxlEncoderPtr enc = JxlEncoderMake(nullptr);
JxlEncoderFrameSettings* frame_settings =
- JxlEncoderFrameSettingsCreate(enc.get(), NULL);
+ JxlEncoderFrameSettingsCreate(enc.get(), nullptr);
// Disallowed negative distance
EXPECT_EQ(JXL_ENC_ERROR, JxlEncoderSetFrameDistance(frame_settings, -1));
}
@@ -474,7 +477,7 @@ TEST(EncodeTest, frame_settingsTest) {
JxlEncoderPtr enc = JxlEncoderMake(nullptr);
EXPECT_NE(nullptr, enc.get());
JxlEncoderFrameSettings* frame_settings =
- JxlEncoderFrameSettingsCreate(enc.get(), NULL);
+ JxlEncoderFrameSettingsCreate(enc.get(), nullptr);
EXPECT_EQ(JXL_ENC_SUCCESS,
JxlEncoderFrameSettingsSetOption(
frame_settings, JXL_ENC_FRAME_SETTING_DECODING_SPEED, 2));
@@ -486,7 +489,7 @@ TEST(EncodeTest, frame_settingsTest) {
JxlEncoderPtr enc = JxlEncoderMake(nullptr);
EXPECT_NE(nullptr, enc.get());
JxlEncoderFrameSettings* frame_settings =
- JxlEncoderFrameSettingsCreate(enc.get(), NULL);
+ JxlEncoderFrameSettingsCreate(enc.get(), nullptr);
EXPECT_EQ(JXL_ENC_ERROR,
JxlEncoderFrameSettingsSetOption(
frame_settings, JXL_ENC_FRAME_SETTING_GROUP_ORDER, 100));
@@ -506,7 +509,7 @@ TEST(EncodeTest, frame_settingsTest) {
JxlEncoderPtr enc = JxlEncoderMake(nullptr);
EXPECT_NE(nullptr, enc.get());
JxlEncoderFrameSettings* frame_settings =
- JxlEncoderFrameSettingsCreate(enc.get(), NULL);
+ JxlEncoderFrameSettingsCreate(enc.get(), nullptr);
EXPECT_EQ(JXL_ENC_SUCCESS,
JxlEncoderFrameSettingsSetOption(
frame_settings, JXL_ENC_FRAME_SETTING_RESPONSIVE, 0));
@@ -519,7 +522,7 @@ TEST(EncodeTest, frame_settingsTest) {
EXPECT_EQ(JXL_ENC_SUCCESS,
JxlEncoderFrameSettingsSetOption(
frame_settings, JXL_ENC_FRAME_SETTING_PROGRESSIVE_DC, 2));
- VerifyFrameEncoding(63, 129, enc.get(), frame_settings, 2830,
+ VerifyFrameEncoding(63, 129, enc.get(), frame_settings, 3430,
/*lossy_use_original_profile=*/false);
EXPECT_EQ(false, enc->last_used_cparams.responsive);
EXPECT_EQ(jxl::Override::kOn, enc->last_used_cparams.progressive_mode);
@@ -530,7 +533,7 @@ TEST(EncodeTest, frame_settingsTest) {
JxlEncoderPtr enc = JxlEncoderMake(nullptr);
EXPECT_NE(nullptr, enc.get());
JxlEncoderFrameSettings* frame_settings =
- JxlEncoderFrameSettingsCreate(enc.get(), NULL);
+ JxlEncoderFrameSettingsCreate(enc.get(), nullptr);
EXPECT_EQ(
JXL_ENC_SUCCESS,
JxlEncoderFrameSettingsSetFloatOption(
@@ -543,7 +546,7 @@ TEST(EncodeTest, frame_settingsTest) {
JxlEncoderPtr enc = JxlEncoderMake(nullptr);
EXPECT_NE(nullptr, enc.get());
JxlEncoderFrameSettings* frame_settings =
- JxlEncoderFrameSettingsCreate(enc.get(), NULL);
+ JxlEncoderFrameSettingsCreate(enc.get(), nullptr);
EXPECT_EQ(JXL_ENC_SUCCESS,
JxlEncoderFrameSettingsSetFloatOption(
frame_settings,
@@ -571,7 +574,7 @@ TEST(EncodeTest, frame_settingsTest) {
JxlEncoderPtr enc = JxlEncoderMake(nullptr);
EXPECT_NE(nullptr, enc.get());
JxlEncoderFrameSettings* frame_settings =
- JxlEncoderFrameSettingsCreate(enc.get(), NULL);
+ JxlEncoderFrameSettingsCreate(enc.get(), nullptr);
EXPECT_EQ(
JXL_ENC_SUCCESS,
JxlEncoderFrameSettingsSetOption(
@@ -603,7 +606,7 @@ TEST(EncodeTest, frame_settingsTest) {
JxlEncoderPtr enc = JxlEncoderMake(nullptr);
EXPECT_NE(nullptr, enc.get());
JxlEncoderFrameSettings* frame_settings =
- JxlEncoderFrameSettingsCreate(enc.get(), NULL);
+ JxlEncoderFrameSettingsCreate(enc.get(), nullptr);
EXPECT_EQ(JXL_ENC_SUCCESS,
JxlEncoderFrameSettingsSetOption(
frame_settings, JXL_ENC_FRAME_SETTING_JPEG_RECON_CFL, 0));
@@ -615,7 +618,7 @@ TEST(EncodeTest, frame_settingsTest) {
JxlEncoderPtr enc = JxlEncoderMake(nullptr);
EXPECT_NE(nullptr, enc.get());
JxlEncoderFrameSettings* frame_settings =
- JxlEncoderFrameSettingsCreate(enc.get(), NULL);
+ JxlEncoderFrameSettingsCreate(enc.get(), nullptr);
EXPECT_EQ(JXL_ENC_SUCCESS,
JxlEncoderFrameSettingsSetOption(
frame_settings, JXL_ENC_FRAME_SETTING_JPEG_RECON_CFL, 1));
@@ -629,14 +632,14 @@ TEST(EncodeTest, LossyEncoderUseOriginalProfileTest) {
JxlEncoderPtr enc = JxlEncoderMake(nullptr);
ASSERT_NE(nullptr, enc.get());
JxlEncoderFrameSettings* frame_settings =
- JxlEncoderFrameSettingsCreate(enc.get(), NULL);
+ JxlEncoderFrameSettingsCreate(enc.get(), nullptr);
VerifyFrameEncoding(63, 129, enc.get(), frame_settings, 7897, true);
}
{
JxlEncoderPtr enc = JxlEncoderMake(nullptr);
ASSERT_NE(nullptr, enc.get());
JxlEncoderFrameSettings* frame_settings =
- JxlEncoderFrameSettingsCreate(enc.get(), NULL);
+ JxlEncoderFrameSettingsCreate(enc.get(), nullptr);
EXPECT_EQ(JXL_ENC_SUCCESS,
JxlEncoderFrameSettingsSetOption(
frame_settings, JXL_ENC_FRAME_SETTING_PROGRESSIVE_DC, 2));
@@ -646,7 +649,7 @@ TEST(EncodeTest, LossyEncoderUseOriginalProfileTest) {
JxlEncoderPtr enc = JxlEncoderMake(nullptr);
ASSERT_NE(nullptr, enc.get());
JxlEncoderFrameSettings* frame_settings =
- JxlEncoderFrameSettingsCreate(enc.get(), NULL);
+ JxlEncoderFrameSettingsCreate(enc.get(), nullptr);
ASSERT_EQ(JXL_ENC_SUCCESS,
JxlEncoderFrameSettingsSetOption(
frame_settings, JXL_ENC_FRAME_SETTING_EFFORT, 8));
@@ -787,7 +790,7 @@ TEST(EncodeTest, SingleFrameBoundedJXLCTest) {
EXPECT_NE(nullptr, enc.get());
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderUseContainer(enc.get(), true));
JxlEncoderFrameSettings* frame_settings =
- JxlEncoderFrameSettingsCreate(enc.get(), NULL);
+ JxlEncoderFrameSettingsCreate(enc.get(), nullptr);
size_t xsize = 71;
size_t ysize = 23;
@@ -798,12 +801,12 @@ TEST(EncodeTest, SingleFrameBoundedJXLCTest) {
jxl::test::JxlBasicInfoSetFromPixelFormat(&basic_info, &pixel_format);
basic_info.xsize = xsize;
basic_info.ysize = ysize;
- basic_info.uses_original_profile = false;
+ basic_info.uses_original_profile = JXL_FALSE;
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetCodestreamLevel(enc.get(), 10));
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetBasicInfo(enc.get(), &basic_info));
JxlColorEncoding color_encoding;
JxlColorEncodingSetToSRGB(&color_encoding,
- /*is_gray=*/false);
+ /*is_gray=*/JXL_FALSE);
EXPECT_EQ(JXL_ENC_SUCCESS,
JxlEncoderSetColorEncoding(enc.get(), &color_encoding));
EXPECT_EQ(JXL_ENC_SUCCESS,
@@ -866,17 +869,17 @@ TEST(EncodeTest, CodestreamLevelTest) {
jxl::test::JxlBasicInfoSetFromPixelFormat(&basic_info, &pixel_format);
basic_info.xsize = xsize;
basic_info.ysize = ysize;
- basic_info.uses_original_profile = false;
+ basic_info.uses_original_profile = 0;
JxlEncoderPtr enc = JxlEncoderMake(nullptr);
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetCodestreamLevel(enc.get(), 10));
JxlEncoderFrameSettings* frame_settings =
- JxlEncoderFrameSettingsCreate(enc.get(), NULL);
+ JxlEncoderFrameSettingsCreate(enc.get(), nullptr);
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetBasicInfo(enc.get(), &basic_info));
JxlColorEncoding color_encoding;
- JxlColorEncodingSetToSRGB(&color_encoding,
- /*is_gray=*/pixel_format.num_channels < 3);
+ JXL_BOOL is_gray = TO_JXL_BOOL(pixel_format.num_channels < 3);
+ JxlColorEncodingSetToSRGB(&color_encoding, is_gray);
EXPECT_EQ(JXL_ENC_SUCCESS,
JxlEncoderSetColorEncoding(enc.get(), &color_encoding));
EXPECT_EQ(JXL_ENC_SUCCESS,
@@ -915,7 +918,7 @@ TEST(EncodeTest, CodestreamLevelVerificationTest) {
jxl::test::JxlBasicInfoSetFromPixelFormat(&basic_info, &pixel_format);
basic_info.xsize = 64;
basic_info.ysize = 64;
- basic_info.uses_original_profile = false;
+ basic_info.uses_original_profile = JXL_FALSE;
JxlEncoderPtr enc = JxlEncoderMake(nullptr);
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetBasicInfo(enc.get(), &basic_info));
@@ -943,7 +946,7 @@ TEST(EncodeTest, JXL_TRANSCODE_JPEG_TEST(JPEGReconstructionTest)) {
JxlEncoderPtr enc = JxlEncoderMake(nullptr);
JxlEncoderFrameSettings* frame_settings =
- JxlEncoderFrameSettingsCreate(enc.get(), NULL);
+ JxlEncoderFrameSettingsCreate(enc.get(), nullptr);
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderStoreJPEGMetadata(enc.get(), JXL_TRUE));
EXPECT_EQ(JXL_ENC_SUCCESS,
@@ -983,7 +986,7 @@ TEST(EncodeTest, JXL_TRANSCODE_JPEG_TEST(ProgressiveJPEGReconstructionTest)) {
JxlEncoderPtr enc = JxlEncoderMake(nullptr);
JxlEncoderFrameSettings* frame_settings =
- JxlEncoderFrameSettingsCreate(enc.get(), NULL);
+ JxlEncoderFrameSettingsCreate(enc.get(), nullptr);
frame_settings->values.cparams.progressive_mode = jxl::Override::kOn;
@@ -1043,7 +1046,7 @@ TEST(EncodeTest, BasicInfoTest) {
EXPECT_NE(nullptr, enc.get());
JxlEncoderFrameSettings* frame_settings =
- JxlEncoderFrameSettingsCreate(enc.get(), NULL);
+ JxlEncoderFrameSettingsCreate(enc.get(), nullptr);
size_t xsize = 1;
size_t ysize = 1;
JxlPixelFormat pixel_format = {4, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0};
@@ -1052,8 +1055,8 @@ TEST(EncodeTest, BasicInfoTest) {
jxl::test::JxlBasicInfoSetFromPixelFormat(&basic_info, &pixel_format);
basic_info.xsize = xsize;
basic_info.ysize = ysize;
- basic_info.uses_original_profile = false;
- basic_info.have_animation = true;
+ basic_info.uses_original_profile = 0;
+ basic_info.have_animation = 1;
basic_info.intensity_target = 123.4;
basic_info.min_nits = 5.0;
basic_info.linear_below = 12.7;
@@ -1067,7 +1070,7 @@ TEST(EncodeTest, BasicInfoTest) {
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetCodestreamLevel(enc.get(), 10));
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetBasicInfo(enc.get(), &basic_info));
JxlColorEncoding color_encoding;
- JxlColorEncodingSetToSRGB(&color_encoding, /*is_gray=*/false);
+ JxlColorEncodingSetToSRGB(&color_encoding, /*is_gray=*/JXL_FALSE);
EXPECT_EQ(JXL_ENC_SUCCESS,
JxlEncoderSetColorEncoding(enc.get(), &color_encoding));
@@ -1155,7 +1158,7 @@ TEST(EncodeTest, AnimationHeaderTest) {
EXPECT_NE(nullptr, enc.get());
JxlEncoderFrameSettings* frame_settings =
- JxlEncoderFrameSettingsCreate(enc.get(), NULL);
+ JxlEncoderFrameSettingsCreate(enc.get(), nullptr);
size_t xsize = 1;
size_t ysize = 1;
JxlPixelFormat pixel_format = {4, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0};
@@ -1164,14 +1167,14 @@ TEST(EncodeTest, AnimationHeaderTest) {
jxl::test::JxlBasicInfoSetFromPixelFormat(&basic_info, &pixel_format);
basic_info.xsize = xsize;
basic_info.ysize = ysize;
- basic_info.have_animation = true;
+ basic_info.have_animation = JXL_TRUE;
basic_info.animation.tps_numerator = 1000;
basic_info.animation.tps_denominator = 1;
basic_info.animation.have_timecodes = JXL_TRUE;
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetCodestreamLevel(enc.get(), 10));
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetBasicInfo(enc.get(), &basic_info));
JxlColorEncoding color_encoding;
- JxlColorEncodingSetToSRGB(&color_encoding, /*is_gray=*/false);
+ JxlColorEncodingSetToSRGB(&color_encoding, /*is_gray=*/JXL_FALSE);
EXPECT_EQ(JXL_ENC_SUCCESS,
JxlEncoderSetColorEncoding(enc.get(), &color_encoding));
@@ -1258,7 +1261,7 @@ TEST(EncodeTest, CroppedFrameTest) {
EXPECT_NE(nullptr, enc.get());
JxlEncoderFrameSettings* frame_settings =
- JxlEncoderFrameSettingsCreate(enc.get(), NULL);
+ JxlEncoderFrameSettingsCreate(enc.get(), nullptr);
size_t xsize = 300;
size_t ysize = 300;
JxlPixelFormat pixel_format = {4, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0};
@@ -1273,7 +1276,7 @@ TEST(EncodeTest, CroppedFrameTest) {
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetCodestreamLevel(enc.get(), 10));
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetBasicInfo(enc.get(), &basic_info));
JxlColorEncoding color_encoding;
- JxlColorEncodingSetToSRGB(&color_encoding, /*is_gray=*/false);
+ JxlColorEncodingSetToSRGB(&color_encoding, /*is_gray=*/JXL_FALSE);
EXPECT_EQ(JXL_ENC_SUCCESS,
JxlEncoderSetColorEncoding(enc.get(), &color_encoding));
@@ -1358,7 +1361,7 @@ TEST_P(EncodeBoxTest, JXL_BOXES_TEST(BoxTest)) {
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderUseBoxes(enc.get()));
JxlEncoderFrameSettings* frame_settings =
- JxlEncoderFrameSettingsCreate(enc.get(), NULL);
+ JxlEncoderFrameSettingsCreate(enc.get(), nullptr);
size_t xsize = 50;
size_t ysize = 17;
JxlPixelFormat pixel_format = {4, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0};
@@ -1367,12 +1370,12 @@ TEST_P(EncodeBoxTest, JXL_BOXES_TEST(BoxTest)) {
jxl::test::JxlBasicInfoSetFromPixelFormat(&basic_info, &pixel_format);
basic_info.xsize = xsize;
basic_info.ysize = ysize;
- basic_info.uses_original_profile = false;
+ basic_info.uses_original_profile = JXL_FALSE;
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetCodestreamLevel(enc.get(), 10));
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetBasicInfo(enc.get(), &basic_info));
JxlColorEncoding color_encoding;
JxlColorEncodingSetToSRGB(&color_encoding,
- /*is_gray=*/false);
+ /*is_gray=*/JXL_FALSE);
EXPECT_EQ(JXL_ENC_SUCCESS,
JxlEncoderSetColorEncoding(enc.get(), &color_encoding));
@@ -1386,7 +1389,8 @@ TEST_P(EncodeBoxTest, JXL_BOXES_TEST(BoxTest)) {
const uint8_t* exif_data = reinterpret_cast<const uint8_t*>(exif_test_string);
// Skip the 4 zeroes for strlen
const size_t exif_size = 4 + strlen(exif_test_string + 4);
- JxlEncoderAddBox(enc.get(), "Exif", exif_data, exif_size, compress_box);
+ JxlEncoderAddBox(enc.get(), "Exif", exif_data, exif_size,
+ TO_JXL_BOOL(compress_box));
// Write to output
ProcessEncoder(enc.get(), compressed, next_out, avail_out);
@@ -1405,7 +1409,8 @@ TEST_P(EncodeBoxTest, JXL_BOXES_TEST(BoxTest)) {
constexpr const char* xml_test_string = "<some random xml data>";
const uint8_t* xml_data = reinterpret_cast<const uint8_t*>(xml_test_string);
size_t xml_size = strlen(xml_test_string);
- JxlEncoderAddBox(enc.get(), "XML ", xml_data, xml_size, compress_box);
+ JxlEncoderAddBox(enc.get(), "XML ", xml_data, xml_size,
+ TO_JXL_BOOL(compress_box));
// Indicate this is the last box
JxlEncoderCloseBoxes(enc.get());
@@ -1495,7 +1500,7 @@ TEST(EncodeTest, JXL_TRANSCODE_JPEG_TEST(JPEGFrameTest)) {
JxlEncoderPtr enc = JxlEncoderMake(nullptr);
JxlEncoderFrameSettings* frame_settings =
- JxlEncoderFrameSettingsCreate(enc.get(), NULL);
+ JxlEncoderFrameSettingsCreate(enc.get(), nullptr);
JxlEncoderFrameSettingsSetOption(frame_settings,
JXL_ENC_FRAME_SETTING_EFFORT, 1);
if (!skip_basic_info) {
@@ -1503,13 +1508,13 @@ TEST(EncodeTest, JXL_TRANSCODE_JPEG_TEST(JPEGFrameTest)) {
JxlEncoderInitBasicInfo(&basic_info);
basic_info.xsize = orig_io.xsize();
basic_info.ysize = orig_io.ysize();
- basic_info.uses_original_profile = true;
+ basic_info.uses_original_profile = JXL_TRUE;
EXPECT_EQ(JXL_ENC_SUCCESS,
JxlEncoderSetBasicInfo(enc.get(), &basic_info));
}
if (!skip_color_encoding) {
JxlColorEncoding color_encoding;
- JxlColorEncodingSetToSRGB(&color_encoding, /*is_gray=*/false);
+ JxlColorEncodingSetToSRGB(&color_encoding, /*is_gray=*/JXL_FALSE);
EXPECT_EQ(JXL_ENC_SUCCESS,
JxlEncoderSetColorEncoding(enc.get(), &color_encoding));
}
@@ -1696,14 +1701,16 @@ class JxlChunkedFrameInputSourceAdapter {
struct StreamingTestParam {
size_t bitmask;
- bool use_container() const { return bitmask & 0x1; }
- bool return_large_buffers() const { return bitmask & 0x2; }
- bool multiple_frames() const { return bitmask & 0x4; }
- bool fast_lossless() const { return bitmask & 0x8; }
- bool can_seek() const { return bitmask & 0x10; }
- bool with_extra_channels() const { return bitmask & 0x20; }
- bool color_includes_alpha() const { return bitmask & 0x40; }
- bool onegroup() const { return bitmask & 0x80; }
+ bool use_container() const { return static_cast<bool>(bitmask & 0x1); }
+ bool return_large_buffers() const { return static_cast<bool>(bitmask & 0x2); }
+ bool multiple_frames() const { return static_cast<bool>(bitmask & 0x4); }
+ bool fast_lossless() const { return static_cast<bool>(bitmask & 0x8); }
+ bool can_seek() const { return static_cast<bool>(bitmask & 0x10); }
+ bool with_extra_channels() const { return static_cast<bool>(bitmask & 0x20); }
+ bool color_includes_alpha() const {
+ return static_cast<bool>(bitmask & 0x40);
+ }
+ bool onegroup() const { return static_cast<bool>(bitmask & 0x80); }
bool is_lossless() const { return fast_lossless(); }
@@ -1773,8 +1780,9 @@ class EncoderStreamingTest : public testing::TestWithParam<StreamingTestParam> {
bool include_alpha, bool is_lossless) {
basic_info.xsize = xsize;
basic_info.ysize = ysize;
- basic_info.num_extra_channels = number_extra_channels + include_alpha;
- basic_info.uses_original_profile = is_lossless;
+ basic_info.num_extra_channels =
+ number_extra_channels + (include_alpha ? 1 : 0);
+ basic_info.uses_original_profile = TO_JXL_BOOL(is_lossless);
}
static void SetupEncoder(JxlEncoderFrameSettings* frame_settings,
@@ -1791,7 +1799,7 @@ class EncoderStreamingTest : public testing::TestWithParam<StreamingTestParam> {
frame_settings, JXL_ENC_FRAME_SETTING_EFFORT, 1));
}
JxlColorEncoding color_encoding;
- JxlColorEncodingSetToSRGB(&color_encoding, /*is_gray=*/false);
+ JxlColorEncodingSetToSRGB(&color_encoding, /*is_gray=*/JXL_FALSE);
EXPECT_EQ(JXL_ENC_SUCCESS,
JxlEncoderSetColorEncoding(enc, &color_encoding));
EXPECT_EQ(JXL_ENC_SUCCESS,
@@ -1825,7 +1833,7 @@ class EncoderStreamingTest : public testing::TestWithParam<StreamingTestParam> {
// Copy pixel data here because it is only guaranteed to be available
// during the call to JxlEncoderAddImageFrame().
std::vector<uint8_t> pixels(frame.pixels_size);
- memcpy(&pixels[0], frame.pixels(), pixels.size());
+ memcpy(pixels.data(), frame.pixels(), pixels.size());
EXPECT_EQ(JXL_ENC_SUCCESS,
JxlEncoderAddImageFrame(frame_settings, &frame.format,
pixels.data(), pixels.size()));
@@ -1834,7 +1842,7 @@ class EncoderStreamingTest : public testing::TestWithParam<StreamingTestParam> {
// Copy pixel data here because it is only guaranteed to be available
// during the call to JxlEncoderSetExtraChannelBuffer().
std::vector<uint8_t> ec_pixels(ec_frame.pixels_size);
- memcpy(&ec_pixels[0], ec_frame.pixels(), ec_pixels.size());
+ memcpy(ec_pixels.data(), ec_frame.pixels(), ec_pixels.size());
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetExtraChannelBuffer(
frame_settings, &ec_frame.format,
ec_pixels.data(), ec_pixels.size(), i));
@@ -1885,7 +1893,7 @@ TEST_P(EncoderStreamingTest, OutputCallback) {
JxlEncoderPtr enc = JxlEncoderMake(nullptr);
ASSERT_NE(nullptr, enc.get());
JxlEncoderFrameSettings* frame_settings =
- JxlEncoderFrameSettingsCreate(enc.get(), NULL);
+ JxlEncoderFrameSettingsCreate(enc.get(), nullptr);
SetupEncoder(frame_settings, p, basic_info, number_extra_channels, false);
SetupInputNonStreaming(frame_settings, p, number_extra_channels, frame,
ec_frame);
@@ -1900,7 +1908,7 @@ TEST_P(EncoderStreamingTest, OutputCallback) {
JxlEncoderPtr enc = JxlEncoderMake(nullptr);
ASSERT_NE(nullptr, enc.get());
JxlEncoderFrameSettings* frame_settings =
- JxlEncoderFrameSettingsCreate(enc.get(), NULL);
+ JxlEncoderFrameSettingsCreate(enc.get(), nullptr);
SetupEncoder(frame_settings, p, basic_info, number_extra_channels, true);
SetupInputNonStreaming(frame_settings, p, number_extra_channels, frame,
ec_frame);
@@ -1938,7 +1946,7 @@ TEST_P(EncoderStreamingTest, ChunkedFrame) {
JxlEncoderPtr enc = JxlEncoderMake(nullptr);
ASSERT_NE(nullptr, enc.get());
JxlEncoderFrameSettings* frame_settings =
- JxlEncoderFrameSettingsCreate(enc.get(), NULL);
+ JxlEncoderFrameSettingsCreate(enc.get(), nullptr);
SetupEncoder(frame_settings, p, basic_info, number_extra_channels, false);
SetupInputNonStreaming(frame_settings, p, number_extra_channels, frame,
ec_frame);
@@ -1952,7 +1960,7 @@ TEST_P(EncoderStreamingTest, ChunkedFrame) {
JxlEncoderPtr enc = JxlEncoderMake(nullptr);
ASSERT_NE(nullptr, enc.get());
JxlEncoderFrameSettings* frame_settings =
- JxlEncoderFrameSettingsCreate(enc.get(), NULL);
+ JxlEncoderFrameSettingsCreate(enc.get(), nullptr);
SetupEncoder(frame_settings, p, basic_info, number_extra_channels, true);
SetupInputStreaming(frame_settings, p, number_extra_channels, frame,
ec_frame);
@@ -1988,7 +1996,7 @@ TEST_P(EncoderStreamingTest, ChunkedAndOutputCallback) {
JxlEncoderPtr enc = JxlEncoderMake(nullptr);
ASSERT_NE(nullptr, enc.get());
JxlEncoderFrameSettings* frame_settings =
- JxlEncoderFrameSettingsCreate(enc.get(), NULL);
+ JxlEncoderFrameSettingsCreate(enc.get(), nullptr);
SetupEncoder(frame_settings, p, basic_info, number_extra_channels, false);
SetupInputNonStreaming(frame_settings, p, number_extra_channels, frame,
ec_frame);
@@ -2003,7 +2011,7 @@ TEST_P(EncoderStreamingTest, ChunkedAndOutputCallback) {
JxlEncoderPtr enc = JxlEncoderMake(nullptr);
ASSERT_NE(nullptr, enc.get());
JxlEncoderFrameSettings* frame_settings =
- JxlEncoderFrameSettingsCreate(enc.get(), NULL);
+ JxlEncoderFrameSettingsCreate(enc.get(), nullptr);
SetupEncoder(frame_settings, p, basic_info, number_extra_channels, true);
JxlStreamingAdapter streaming_adapter =
JxlStreamingAdapter(enc.get(), p.return_large_buffers(), p.can_seek());
@@ -2049,7 +2057,7 @@ TEST(EncoderTest, CMYK) {
JxlEncoderStruct* enc = enc_ptr.get();
ASSERT_NE(nullptr, enc);
JxlEncoderFrameSettings* frame_settings =
- JxlEncoderFrameSettingsCreate(enc, NULL);
+ JxlEncoderFrameSettingsCreate(enc, nullptr);
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetBasicInfo(enc, &basic_info));
JxlExtraChannelInfo channel_info;
diff --git a/third_party/jpeg-xl/lib/jxl/entropy_coder.cc b/third_party/jpeg-xl/lib/jxl/entropy_coder.cc
index a90ed0257a..5dc101b36f 100644
--- a/third_party/jpeg-xl/lib/jxl/entropy_coder.cc
+++ b/third_party/jpeg-xl/lib/jxl/entropy_coder.cc
@@ -33,7 +33,7 @@ Status DecodeBlockCtxMap(BitReader* br, BlockCtxMap* block_ctx_map) {
auto& dct = block_ctx_map->dc_thresholds;
auto& qft = block_ctx_map->qf_thresholds;
auto& ctx_map = block_ctx_map->ctx_map;
- bool is_default = br->ReadFixedBits<1>();
+ bool is_default = static_cast<bool>(br->ReadFixedBits<1>());
if (is_default) {
*block_ctx_map = BlockCtxMap();
return true;
diff --git a/third_party/jpeg-xl/lib/jxl/fast_dct-inl.h b/third_party/jpeg-xl/lib/jxl/fast_dct-inl.h
index de1f845901..e315200b0c 100644
--- a/third_party/jpeg-xl/lib/jxl/fast_dct-inl.h
+++ b/third_party/jpeg-xl/lib/jxl/fast_dct-inl.h
@@ -191,7 +191,7 @@ HWY_NOINLINE void TestFastIDCT() {
}
}
printf("max error: %f mantissa bits: %d\n", max_error,
- 14 - (int)integer_bits);
+ 14 - static_cast<int>(integer_bits));
#endif
}
diff --git a/third_party/jpeg-xl/lib/jxl/fast_dct.cc b/third_party/jpeg-xl/lib/jxl/fast_dct.cc
deleted file mode 100644
index d796018fd0..0000000000
--- a/third_party/jpeg-xl/lib/jxl/fast_dct.cc
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright (c) the JPEG XL Project Authors. All rights reserved.
-//
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-#undef HWY_TARGET_INCLUDE
-#define HWY_TARGET_INCLUDE "lib/jxl/fast_dct.cc"
-#include <hwy/foreach_target.h>
-#include <hwy/highway.h>
-
-#include "lib/jxl/base/random.h"
-#include "lib/jxl/dct-inl.h"
-#include "lib/jxl/fast_dct-inl.h"
-HWY_BEFORE_NAMESPACE();
-namespace jxl {
-namespace HWY_NAMESPACE {
-namespace {
-void BenchmarkFloatIDCT32x32() { TestFloatIDCT<32, 32>(); }
-void BenchmarkFastIDCT32x32() { TestFastIDCT<32, 32>(); }
-} // namespace
-// NOLINTNEXTLINE(google-readability-namespace-comments)
-} // namespace HWY_NAMESPACE
-} // namespace jxl
-HWY_AFTER_NAMESPACE();
-
-#if HWY_ONCE
-namespace jxl {
-HWY_EXPORT(BenchmarkFloatIDCT32x32);
-HWY_EXPORT(BenchmarkFastIDCT32x32);
-void BenchmarkFloatIDCT32x32() {
- HWY_DYNAMIC_DISPATCH(BenchmarkFloatIDCT32x32)();
-}
-void BenchmarkFastIDCT32x32() {
- HWY_DYNAMIC_DISPATCH(BenchmarkFastIDCT32x32)();
-}
-} // namespace jxl
-#endif
diff --git a/third_party/jpeg-xl/lib/jxl/fast_dct.h b/third_party/jpeg-xl/lib/jxl/fast_dct.h
deleted file mode 100644
index 641933d8a0..0000000000
--- a/third_party/jpeg-xl/lib/jxl/fast_dct.h
+++ /dev/null
@@ -1,9 +0,0 @@
-// Copyright (c) the JPEG XL Project Authors. All rights reserved.
-//
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-namespace jxl {
-void BenchmarkFloatIDCT32x32();
-void BenchmarkFastIDCT32x32();
-} // namespace jxl
diff --git a/third_party/jpeg-xl/lib/jxl/fast_dct_test.cc b/third_party/jpeg-xl/lib/jxl/fast_dct_test.cc
index a55b67afb2..417e202988 100644
--- a/third_party/jpeg-xl/lib/jxl/fast_dct_test.cc
+++ b/third_party/jpeg-xl/lib/jxl/fast_dct_test.cc
@@ -12,7 +12,6 @@
#include "lib/jxl/base/random.h"
#include "lib/jxl/dct-inl.h"
#include "lib/jxl/fast_dct-inl.h"
-#include "lib/jxl/fast_dct.h"
#include "lib/jxl/testing.h"
#include "lib/jxl/transpose-inl.h"
@@ -21,9 +20,13 @@
#include <hwy/tests/hwy_gtest.h>
HWY_BEFORE_NAMESPACE();
namespace jxl {
+
namespace HWY_NAMESPACE {
namespace {
+void BenchmarkFloatIDCT32x32() { TestFloatIDCT<32, 32>(); }
+void BenchmarkFastIDCT32x32() { TestFastIDCT<32, 32>(); }
+
template <size_t N, size_t M>
HWY_NOINLINE void TestFastTranspose() {
#if HWY_TARGET == HWY_NEON
@@ -370,8 +373,8 @@ HWY_EXPORT_AND_TEST_P(FastDCTTargetTest, TestFloatIDCT256x256);
HWY_EXPORT_AND_TEST_P(FastDCTTargetTest, TestFastIDCT256x256);
*/
-TEST(FastDCTTest, TestWrapperFloat) { BenchmarkFloatIDCT32x32(); }
-TEST(FastDCTTest, TestWrapperFast) { BenchmarkFastIDCT32x32(); }
+HWY_EXPORT_AND_TEST_P(FastDCTTargetTest, BenchmarkFloatIDCT32x32);
+HWY_EXPORT_AND_TEST_P(FastDCTTargetTest, BenchmarkFastIDCT32x32);
} // namespace jxl
#endif // HWY_ONCE
diff --git a/third_party/jpeg-xl/lib/jxl/fast_math_test.cc b/third_party/jpeg-xl/lib/jxl/fast_math_test.cc
index 868e1b72f4..b242dbe575 100644
--- a/third_party/jpeg-xl/lib/jxl/fast_math_test.cc
+++ b/third_party/jpeg-xl/lib/jxl/fast_math_test.cc
@@ -167,7 +167,8 @@ HWY_NOINLINE void TestFastXYB() {
for (int cr = 0; cr < n; cr += kChunk) {
for (int cg = 0; cg < n; cg += kChunk) {
for (int cb = 0; cb < n; cb += kChunk) {
- Image3F chunk(kChunk * kChunk, kChunk);
+ JXL_ASSIGN_OR_DIE(Image3F chunk,
+ Image3F::Create(kChunk * kChunk, kChunk));
for (int ir = 0; ir < kChunk; ir++) {
for (int ig = 0; ig < kChunk; ig++) {
for (int ib = 0; ib < kChunk; ib++) {
@@ -181,9 +182,10 @@ HWY_NOINLINE void TestFastXYB() {
}
}
ib.SetFromImage(std::move(chunk), ColorEncoding::SRGB());
- Image3F xyb(kChunk * kChunk, kChunk);
+ JXL_ASSIGN_OR_DIE(Image3F xyb,
+ Image3F::Create(kChunk * kChunk, kChunk));
std::vector<uint8_t> roundtrip(kChunk * kChunk * kChunk * 3);
- ToXYB(ib, nullptr, &xyb, *JxlGetDefaultCms());
+ JXL_CHECK(ToXYB(ib, nullptr, &xyb, *JxlGetDefaultCms()));
for (int y = 0; y < kChunk; y++) {
const float* xyba[4] = {xyb.PlaneRow(0, y), xyb.PlaneRow(1, y),
xyb.PlaneRow(2, y), nullptr};
diff --git a/third_party/jpeg-xl/lib/jxl/fields.cc b/third_party/jpeg-xl/lib/jxl/fields.cc
index 746d7e4d30..6bb5eae25d 100644
--- a/third_party/jpeg-xl/lib/jxl/fields.cc
+++ b/third_party/jpeg-xl/lib/jxl/fields.cc
@@ -109,7 +109,7 @@ struct SetDefaultVisitor : public VisitorBase {
class AllDefaultVisitor : public VisitorBase {
public:
- explicit AllDefaultVisitor() : VisitorBase() {}
+ explicit AllDefaultVisitor() = default;
Status Bits(const size_t bits, const uint32_t default_value,
uint32_t* JXL_RESTRICT value) override {
@@ -148,7 +148,7 @@ class AllDefaultVisitor : public VisitorBase {
class ReadVisitor : public VisitorBase {
public:
- explicit ReadVisitor(BitReader* reader) : VisitorBase(), reader_(reader) {}
+ explicit ReadVisitor(BitReader* reader) : reader_(reader) {}
Status Bits(const size_t bits, const uint32_t /*default_value*/,
uint32_t* JXL_RESTRICT value) override {
@@ -267,7 +267,8 @@ class ReadVisitor : public VisitorBase {
uint64_t total_extension_bits_ = 0;
size_t pos_after_ext_size_ = 0; // 0 iff extensions == 0.
- friend Status jxl::CheckHasEnoughBits(Visitor*, size_t);
+ friend Status jxl::CheckHasEnoughBits(Visitor* /* visitor */,
+ size_t /* bits */);
};
class MaxBitsVisitor : public VisitorBase {
@@ -321,7 +322,7 @@ class MaxBitsVisitor : public VisitorBase {
class CanEncodeVisitor : public VisitorBase {
public:
- explicit CanEncodeVisitor() : VisitorBase() {}
+ explicit CanEncodeVisitor() = default;
Status Bits(const size_t bits, const uint32_t /*default_value*/,
uint32_t* JXL_RESTRICT value) override {
diff --git a/third_party/jpeg-xl/lib/jxl/fields.h b/third_party/jpeg-xl/lib/jxl/fields.h
index d05fe4517e..a8d8d8671a 100644
--- a/third_party/jpeg-xl/lib/jxl/fields.h
+++ b/third_party/jpeg-xl/lib/jxl/fields.h
@@ -298,7 +298,7 @@ class ExtensionStates {
class VisitorBase : public Visitor {
public:
- explicit VisitorBase() {}
+ explicit VisitorBase() = default;
~VisitorBase() override { JXL_ASSERT(depth_ == 0); }
// This is the only call site of Fields::VisitFields.
diff --git a/third_party/jpeg-xl/lib/jxl/fields_test.cc b/third_party/jpeg-xl/lib/jxl/fields_test.cc
index b178a6bd6a..0584458d07 100644
--- a/third_party/jpeg-xl/lib/jxl/fields_test.cc
+++ b/third_party/jpeg-xl/lib/jxl/fields_test.cc
@@ -200,7 +200,8 @@ TEST(FieldsTest, TestRoundtripSize) {
SizeHeader size;
ASSERT_TRUE(size.Set(123 + 77 * i, 7 + i));
- size_t extension_bits = 999, total_bits = 999; // Initialize as garbage.
+ size_t extension_bits = 999;
+ size_t total_bits = 999; // Initialize as garbage.
ASSERT_TRUE(Bundle::CanEncode(size, &extension_bits, &total_bits));
EXPECT_EQ(0u, extension_bits);
@@ -230,7 +231,8 @@ TEST(FieldsTest, TestCropRect) {
f.frame_origin.y0 = i;
f.frame_size.xsize = 1000 + i;
f.frame_size.ysize = 1000 + i;
- size_t extension_bits = 0, total_bits = 0;
+ size_t extension_bits = 0;
+ size_t total_bits = 0;
ASSERT_TRUE(Bundle::CanEncode(f, &extension_bits, &total_bits));
EXPECT_EQ(0u, extension_bits);
EXPECT_GE(total_bits, 9u);
@@ -241,7 +243,8 @@ TEST(FieldsTest, TestPreview) {
for (uint32_t i = 1; i < 4360; ++i) {
PreviewHeader p;
ASSERT_TRUE(p.Set(i, i));
- size_t extension_bits = 0, total_bits = 0;
+ size_t extension_bits = 0;
+ size_t total_bits = 0;
ASSERT_TRUE(Bundle::CanEncode(p, &extension_bits, &total_bits));
EXPECT_EQ(0u, extension_bits);
EXPECT_GE(total_bits, 6u);
@@ -254,7 +257,8 @@ TEST(FieldsTest, TestRoundtripFrame) {
FrameHeader h(&metadata);
h.extensions = 0x800;
- size_t extension_bits = 999, total_bits = 999; // Initialize as garbage.
+ size_t extension_bits = 999;
+ size_t total_bits = 999; // Initialize as garbage.
ASSERT_TRUE(Bundle::CanEncode(h, &extension_bits, &total_bits));
EXPECT_EQ(0u, extension_bits);
BitWriter writer;
@@ -277,7 +281,8 @@ TEST(FieldsTest, TestRoundtripFrame) {
TEST(FieldsTest, TestOutOfRange) {
SizeHeader h;
ASSERT_TRUE(h.Set(0xFFFFFFFFull, 0xFFFFFFFFull));
- size_t extension_bits = 999, total_bits = 999; // Initialize as garbage.
+ size_t extension_bits = 999;
+ size_t total_bits = 999; // Initialize as garbage.
ASSERT_FALSE(Bundle::CanEncode(h, &extension_bits, &total_bits));
}
#endif
@@ -315,12 +320,12 @@ struct NewBundle : public Fields {
visitor->U32(Bits(7), Bits(12), Bits(16), Bits(32), 0, &old_large));
JXL_QUIET_RETURN_IF_ERROR(visitor->BeginExtensions(&extensions));
- if (visitor->Conditional(extensions & 1)) {
+ if (visitor->Conditional((extensions & 1) != 0)) {
JXL_QUIET_RETURN_IF_ERROR(
visitor->U32(Val(2), Bits(2), Bits(3), Bits(4), 2, &new_small));
JXL_QUIET_RETURN_IF_ERROR(visitor->F16(-2.0f, &new_f));
}
- if (visitor->Conditional(extensions & 2)) {
+ if (visitor->Conditional((extensions & 2) != 0)) {
JXL_QUIET_RETURN_IF_ERROR(
visitor->U32(Bits(9), Bits(12), Bits(16), Bits(32), 0, &new_large));
}
@@ -349,7 +354,8 @@ TEST(FieldsTest, TestNewDecoderOldData) {
const size_t kMaxOutBytes = 999;
BitWriter writer;
// Make sure values are initialized by code under test.
- size_t extension_bits = 12345, total_bits = 12345;
+ size_t extension_bits = 12345;
+ size_t total_bits = 12345;
ASSERT_TRUE(Bundle::CanEncode(old_bundle, &extension_bits, &total_bits));
ASSERT_LE(total_bits, kMaxOutBytes * kBitsPerByte);
EXPECT_EQ(0u, extension_bits);
@@ -393,7 +399,8 @@ TEST(FieldsTest, TestOldDecoderNewData) {
constexpr size_t kMaxOutBytes = 999;
BitWriter writer;
// Make sure values are initialized by code under test.
- size_t extension_bits = 12345, total_bits = 12345;
+ size_t extension_bits = 12345;
+ size_t total_bits = 12345;
ASSERT_TRUE(Bundle::CanEncode(new_bundle, &extension_bits, &total_bits));
EXPECT_NE(0u, extension_bits);
AuxOut aux_out;
diff --git a/third_party/jpeg-xl/lib/jxl/frame_header.cc b/third_party/jpeg-xl/lib/jxl/frame_header.cc
index a9e79ff1b8..6648e6d8cc 100644
--- a/third_party/jpeg-xl/lib/jxl/frame_header.cc
+++ b/third_party/jpeg-xl/lib/jxl/frame_header.cc
@@ -397,16 +397,18 @@ Status FrameHeader::VisitFields(Visitor* JXL_RESTRICT visitor) {
} else if (visitor->Conditional(frame_type == FrameType::kReferenceOnly)) {
JXL_QUIET_RETURN_IF_ERROR(
visitor->Bool(true, &save_before_color_transform));
+ size_t xsize = custom_size_or_origin ? frame_size.xsize
+ : nonserialized_metadata->xsize();
+ size_t ysize = custom_size_or_origin ? frame_size.ysize
+ : nonserialized_metadata->ysize();
if (!save_before_color_transform &&
- (frame_size.xsize < nonserialized_metadata->xsize() ||
- frame_size.ysize < nonserialized_metadata->ysize() ||
- frame_origin.x0 != 0 || frame_origin.y0 != 0)) {
+ (xsize < nonserialized_metadata->xsize() ||
+ ysize < nonserialized_metadata->ysize() || frame_origin.x0 != 0 ||
+ frame_origin.y0 != 0)) {
return JXL_FAILURE(
"non-patch reference frame with invalid crop: %" PRIuS "x%" PRIuS
"%+d%+d",
- static_cast<size_t>(frame_size.xsize),
- static_cast<size_t>(frame_size.ysize),
- static_cast<int>(frame_origin.x0),
+ xsize, ysize, static_cast<int>(frame_origin.x0),
static_cast<int>(frame_origin.y0));
}
}
diff --git a/third_party/jpeg-xl/lib/jxl/frame_header.h b/third_party/jpeg-xl/lib/jxl/frame_header.h
index b246bf813e..30c62d5f27 100644
--- a/third_party/jpeg-xl/lib/jxl/frame_header.h
+++ b/third_party/jpeg-xl/lib/jxl/frame_header.h
@@ -82,8 +82,8 @@ struct YCbCrChromaSubsampling : public Fields {
Status VisitFields(Visitor* JXL_RESTRICT visitor) override {
// TODO(veluca): consider allowing 4x downsamples
- for (size_t i = 0; i < 3; i++) {
- JXL_QUIET_RETURN_IF_ERROR(visitor->Bits(2, 0, &channel_mode_[i]));
+ for (uint32_t& ch : channel_mode_) {
+ JXL_QUIET_RETURN_IF_ERROR(visitor->Bits(2, 0, &ch));
}
Recompute();
return true;
@@ -152,9 +152,9 @@ struct YCbCrChromaSubsampling : public Fields {
void Recompute() {
maxhs_ = 0;
maxvs_ = 0;
- for (size_t i = 0; i < 3; i++) {
- maxhs_ = std::max(maxhs_, kHShift[channel_mode_[i]]);
- maxvs_ = std::max(maxvs_, kVShift[channel_mode_[i]]);
+ for (uint32_t ch : channel_mode_) {
+ maxhs_ = std::max(maxhs_, kHShift[ch]);
+ maxvs_ = std::max(maxvs_, kVShift[ch]);
}
}
static const uint8_t kHShift[4];
diff --git a/third_party/jpeg-xl/lib/jxl/gradient_test.cc b/third_party/jpeg-xl/lib/jxl/gradient_test.cc
index 055a419f5b..d2c83619fc 100644
--- a/third_party/jpeg-xl/lib/jxl/gradient_test.cc
+++ b/third_party/jpeg-xl/lib/jxl/gradient_test.cc
@@ -9,7 +9,6 @@
#include <stdint.h>
#include <algorithm>
-#include <array>
#include <cmath>
#include <utility>
#include <vector>
@@ -45,10 +44,10 @@ double PointLineDist(double x0, double y0, double x1, double y1, double x,
// angle in which the change direction happens.
Image3F GenerateTestGradient(uint32_t color0, uint32_t color1, double angle,
size_t xsize, size_t ysize) {
- Image3F image(xsize, ysize);
+ JXL_ASSIGN_OR_DIE(Image3F image, Image3F::Create(xsize, ysize));
- double x0 = xsize / 2;
- double y0 = ysize / 2;
+ double x0 = xsize / 2.0;
+ double y0 = ysize / 2.0;
double x1 = x0 + std::sin(angle / 360.0 * 2.0 * kPi);
double y1 = y0 + std::cos(angle / 360.0 * 2.0 * kPi);
@@ -78,63 +77,60 @@ Image3F GenerateTestGradient(uint32_t color0, uint32_t color1, double angle,
// delta and right delta (top/bottom for vertical direction).
// The radius over which the derivative is computed is only 1 pixel and it only
// checks two angles (hor and ver), but this approximation works well enough.
-static ImageF Gradient2(const ImageF& image) {
+Image3F Gradient2(const Image3F& image) {
size_t xsize = image.xsize();
size_t ysize = image.ysize();
- ImageF image2(image.xsize(), image.ysize());
- for (size_t y = 1; y + 1 < ysize; y++) {
- const auto* JXL_RESTRICT row0 = image.Row(y - 1);
- const auto* JXL_RESTRICT row1 = image.Row(y);
- const auto* JXL_RESTRICT row2 = image.Row(y + 1);
- auto* row_out = image2.Row(y);
- for (size_t x = 1; x + 1 < xsize; x++) {
- float ddx = (row1[x] - row1[x - 1]) - (row1[x + 1] - row1[x]);
- float ddy = (row1[x] - row0[x]) - (row2[x] - row1[x]);
- row_out[x] = std::max(fabsf(ddx), fabsf(ddy));
- }
- }
- // Copy to the borders
- if (ysize > 2) {
- auto* JXL_RESTRICT row0 = image2.Row(0);
- const auto* JXL_RESTRICT row1 = image2.Row(1);
- const auto* JXL_RESTRICT row2 = image2.Row(ysize - 2);
- auto* JXL_RESTRICT row3 = image2.Row(ysize - 1);
- for (size_t x = 1; x + 1 < xsize; x++) {
- row0[x] = row1[x];
- row3[x] = row2[x];
- }
- } else {
- const auto* row0_in = image.Row(0);
- const auto* row1_in = image.Row(ysize - 1);
- auto* row0_out = image2.Row(0);
- auto* row1_out = image2.Row(ysize - 1);
- for (size_t x = 1; x + 1 < xsize; x++) {
- // Image too narrow, take first derivative instead
- row0_out[x] = row1_out[x] = fabsf(row0_in[x] - row1_in[x]);
+ JXL_ASSIGN_OR_DIE(Image3F image2, Image3F::Create(xsize, ysize));
+ for (size_t c = 0; c < 3; ++c) {
+ for (size_t y = 1; y + 1 < ysize; y++) {
+ const auto* JXL_RESTRICT row0 = image.ConstPlaneRow(c, y - 1);
+ const auto* JXL_RESTRICT row1 = image.ConstPlaneRow(c, y);
+ const auto* JXL_RESTRICT row2 = image.ConstPlaneRow(c, y + 1);
+ auto* row_out = image2.PlaneRow(c, y);
+ for (size_t x = 1; x + 1 < xsize; x++) {
+ float ddx = (row1[x] - row1[x - 1]) - (row1[x + 1] - row1[x]);
+ float ddy = (row1[x] - row0[x]) - (row2[x] - row1[x]);
+ row_out[x] = std::max(fabsf(ddx), fabsf(ddy));
+ }
}
- }
- if (xsize > 2) {
- for (size_t y = 0; y < ysize; y++) {
- auto* row = image2.Row(y);
- row[0] = row[1];
- row[xsize - 1] = row[xsize - 2];
+ // Copy to the borders
+ if (ysize > 2) {
+ auto* JXL_RESTRICT row0 = image2.PlaneRow(c, 0);
+ const auto* JXL_RESTRICT row1 = image2.PlaneRow(c, 1);
+ const auto* JXL_RESTRICT row2 = image2.PlaneRow(c, ysize - 2);
+ auto* JXL_RESTRICT row3 = image2.PlaneRow(c, ysize - 1);
+ for (size_t x = 1; x + 1 < xsize; x++) {
+ row0[x] = row1[x];
+ row3[x] = row2[x];
+ }
+ } else {
+ const auto* row0_in = image.ConstPlaneRow(c, 0);
+ const auto* row1_in = image.ConstPlaneRow(c, ysize - 1);
+ auto* row0_out = image2.PlaneRow(c, 0);
+ auto* row1_out = image2.PlaneRow(c, ysize - 1);
+ for (size_t x = 1; x + 1 < xsize; x++) {
+ // Image too narrow, take first derivative instead
+ row0_out[x] = row1_out[x] = fabsf(row0_in[x] - row1_in[x]);
+ }
}
- } else {
- for (size_t y = 0; y < ysize; y++) {
- const auto* JXL_RESTRICT row_in = image.Row(y);
- auto* row_out = image2.Row(y);
- // Image too narrow, take first derivative instead
- row_out[0] = row_out[xsize - 1] = fabsf(row_in[0] - row_in[xsize - 1]);
+ if (xsize > 2) {
+ for (size_t y = 0; y < ysize; y++) {
+ auto* row = image2.PlaneRow(c, y);
+ row[0] = row[1];
+ row[xsize - 1] = row[xsize - 2];
+ }
+ } else {
+ for (size_t y = 0; y < ysize; y++) {
+ const auto* JXL_RESTRICT row_in = image.ConstPlaneRow(c, y);
+ auto* row_out = image2.PlaneRow(c, y);
+ // Image too narrow, take first derivative instead
+ row_out[0] = row_out[xsize - 1] = fabsf(row_in[0] - row_in[xsize - 1]);
+ }
}
}
return image2;
}
-static Image3F Gradient2(const Image3F& image) {
- return Image3F(Gradient2(image.Plane(0)), Gradient2(image.Plane(1)),
- Gradient2(image.Plane(2)));
-}
-
/*
Tests if roundtrip with jxl on a gradient image doesn't cause banding.
Only tests if use_gradient is true. Set to false for debugging to see the
@@ -173,17 +169,19 @@ void TestGradient(ThreadPool* pool, uint32_t color0, uint32_t color1,
// butteraugli_distance).
Image3F gradient2 = Gradient2(*io2.Main().color());
- std::array<float, 3> image_max;
- Image3Max(gradient2, &image_max);
-
// TODO(jyrki): These values used to work with 0.2, 0.2, 0.2.
- EXPECT_LE(image_max[0], 3.15);
- EXPECT_LE(image_max[1], 1.72);
- EXPECT_LE(image_max[2], 5.05);
+ float image_min;
+ float image_max;
+ ImageMinMax(gradient2.Plane(0), &image_min, &image_max);
+ EXPECT_LE(image_max, 3.15);
+ ImageMinMax(gradient2.Plane(1), &image_min, &image_max);
+ EXPECT_LE(image_max, 1.72);
+ ImageMinMax(gradient2.Plane(2), &image_min, &image_max);
+ EXPECT_LE(image_max, 5.05);
}
}
-static constexpr bool fast_mode = true;
+constexpr bool fast_mode = true;
TEST(GradientTest, SteepGradient) {
test::ThreadPoolForTests pool(8);
diff --git a/third_party/jpeg-xl/lib/jxl/headers.cc b/third_party/jpeg-xl/lib/jxl/headers.cc
index db88147687..52e7cf5db3 100644
--- a/third_party/jpeg-xl/lib/jxl/headers.cc
+++ b/third_party/jpeg-xl/lib/jxl/headers.cc
@@ -17,7 +17,7 @@ struct Rational {
// Returns floor(multiplicand * rational).
constexpr uint32_t MulTruncate(uint32_t multiplicand) const {
- return uint64_t(multiplicand) * num / den;
+ return static_cast<uint64_t>(multiplicand) * num / den;
}
uint32_t num;
diff --git a/third_party/jpeg-xl/lib/jxl/icc_codec.cc b/third_party/jpeg-xl/lib/jxl/icc_codec.cc
index a1f118ebfb..8501c684ac 100644
--- a/third_party/jpeg-xl/lib/jxl/icc_codec.cc
+++ b/third_party/jpeg-xl/lib/jxl/icc_codec.cc
@@ -33,7 +33,8 @@ void Shuffle(uint8_t* data, size_t size, size_t width) {
size_t height = (size + width - 1) / width; // amount of rows of output
PaddedBytes result(size);
// i = output index, j input index
- size_t s = 0, j = 0;
+ size_t s = 0;
+ size_t j = 0;
for (size_t i = 0; i < size; i++) {
result[i] = data[j];
j += height;
@@ -55,7 +56,8 @@ uint64_t DecodeVarInt(const uint8_t* input, size_t inputSize, size_t* pos) {
size_t i;
uint64_t ret = 0;
for (i = 0; *pos + i < inputSize && i < 10; ++i) {
- ret |= uint64_t(input[*pos + i] & 127) << uint64_t(7 * i);
+ ret |= static_cast<uint64_t>(input[*pos + i] & 127)
+ << static_cast<uint64_t>(7 * i);
// If the next-byte flag is not set, stop
if ((input[*pos + i] & 128) == 0) break;
}
diff --git a/third_party/jpeg-xl/lib/jxl/icc_codec.h b/third_party/jpeg-xl/lib/jxl/icc_codec.h
index 87e523a575..8b880c7d3b 100644
--- a/third_party/jpeg-xl/lib/jxl/icc_codec.h
+++ b/third_party/jpeg-xl/lib/jxl/icc_codec.h
@@ -39,12 +39,6 @@ struct ICCReader {
PaddedBytes decompressed_;
};
-// Exposed only for testing
-Status PredictICC(const uint8_t* icc, size_t size, PaddedBytes* result);
-
-// Exposed only for testing
-Status UnpredictICC(const uint8_t* enc, size_t size, PaddedBytes* result);
-
} // namespace jxl
#endif // LIB_JXL_ICC_CODEC_H_
diff --git a/third_party/jpeg-xl/lib/jxl/icc_codec_common.cc b/third_party/jpeg-xl/lib/jxl/icc_codec_common.cc
index d420567b6f..1cb4542687 100644
--- a/third_party/jpeg-xl/lib/jxl/icc_codec_common.cc
+++ b/third_party/jpeg-xl/lib/jxl/icc_codec_common.cc
@@ -17,7 +17,7 @@
namespace jxl {
namespace {
-static uint8_t ByteKind1(uint8_t b) {
+uint8_t ByteKind1(uint8_t b) {
if ('a' <= b && b <= 'z') return 0;
if ('A' <= b && b <= 'Z') return 0;
if ('0' <= b && b <= '9') return 1;
@@ -30,7 +30,7 @@ static uint8_t ByteKind1(uint8_t b) {
return 7;
}
-static uint8_t ByteKind2(uint8_t b) {
+uint8_t ByteKind2(uint8_t b) {
if ('a' <= b && b <= 'z') return 0;
if ('A' <= b && b <= 'Z') return 0;
if ('0' <= b && b <= '9') return 1;
@@ -105,7 +105,7 @@ const uint8_t kIccInitialHeaderPrediction[kICCHeaderSize] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
-const Span<const uint8_t> ICCInitialHeaderPrediction() {
+Span<const uint8_t> ICCInitialHeaderPrediction() {
return Bytes(kIccInitialHeaderPrediction);
}
diff --git a/third_party/jpeg-xl/lib/jxl/icc_codec_common.h b/third_party/jpeg-xl/lib/jxl/icc_codec_common.h
index 702a0e7b1f..0922d74145 100644
--- a/third_party/jpeg-xl/lib/jxl/icc_codec_common.h
+++ b/third_party/jpeg-xl/lib/jxl/icc_codec_common.h
@@ -95,7 +95,7 @@ void AppendKeyword(const Tag& keyword, PaddedBytes* data);
Status CheckOutOfBounds(uint64_t a, uint64_t b, uint64_t size);
Status CheckIs32Bit(uint64_t v);
-const Span<const uint8_t> ICCInitialHeaderPrediction();
+Span<const uint8_t> ICCInitialHeaderPrediction();
void ICCPredictHeader(const uint8_t* icc, size_t size, uint8_t* header,
size_t pos);
uint8_t LinearPredictICCValue(const uint8_t* data, size_t start, size_t i,
diff --git a/third_party/jpeg-xl/lib/jxl/icc_codec_test.cc b/third_party/jpeg-xl/lib/jxl/icc_codec_test.cc
index 743aa9a30e..175b4768a0 100644
--- a/third_party/jpeg-xl/lib/jxl/icc_codec_test.cc
+++ b/third_party/jpeg-xl/lib/jxl/icc_codec_test.cc
@@ -37,12 +37,12 @@ void TestProfile(const IccBytes& icc) {
void TestProfile(const std::string& icc) {
IccBytes data;
- Bytes(icc).AppendTo(&data);
+ Bytes(icc).AppendTo(data);
TestProfile(data);
}
// Valid profile from one of the images output by the decoder.
-static const unsigned char kTestProfile[] = {
+const unsigned char kTestProfile[] = {
0x00, 0x00, 0x03, 0x80, 0x6c, 0x63, 0x6d, 0x73, 0x04, 0x30, 0x00, 0x00,
0x6d, 0x6e, 0x74, 0x72, 0x52, 0x47, 0x42, 0x20, 0x58, 0x59, 0x5a, 0x20,
0x07, 0xe3, 0x00, 0x04, 0x00, 0x1d, 0x00, 0x0f, 0x00, 0x32, 0x00, 0x2e,
@@ -139,7 +139,7 @@ TEST(IccCodecTest, Icc) {
{
IccBytes profile;
- Bytes(kTestProfile, sizeof(kTestProfile)).AppendTo(&profile);
+ Bytes(kTestProfile, sizeof(kTestProfile)).AppendTo(profile);
TestProfile(profile);
}
diff --git a/third_party/jpeg-xl/lib/jxl/image.cc b/third_party/jpeg-xl/lib/jxl/image.cc
index 382c957799..7468dad130 100644
--- a/third_party/jpeg-xl/lib/jxl/image.cc
+++ b/third_party/jpeg-xl/lib/jxl/image.cc
@@ -5,123 +5,93 @@
#include "lib/jxl/image.h"
-#include <algorithm> // swap
+#include <algorithm> // fill, swap
+#include <cstddef>
+#include <cstdint>
-#undef HWY_TARGET_INCLUDE
-#define HWY_TARGET_INCLUDE "lib/jxl/image.cc"
-#include <hwy/foreach_target.h>
-#include <hwy/highway.h>
+#include "lib/jxl/base/status.h"
+#include "lib/jxl/cache_aligned.h"
+#include "lib/jxl/simd_util.h"
+#if defined(MEMORY_SANITIZER)
#include "lib/jxl/base/common.h"
-#include "lib/jxl/frame_dimensions.h"
-#include "lib/jxl/image_ops.h"
#include "lib/jxl/sanitizers.h"
+#endif
-HWY_BEFORE_NAMESPACE();
namespace jxl {
+namespace detail {
-namespace HWY_NAMESPACE {
-size_t GetVectorSize() { return HWY_LANES(uint8_t); }
-// NOLINTNEXTLINE(google-readability-namespace-comments)
-} // namespace HWY_NAMESPACE
-
-} // namespace jxl
-HWY_AFTER_NAMESPACE();
-
-#if HWY_ONCE
-namespace jxl {
namespace {
-HWY_EXPORT(GetVectorSize); // Local function.
+// Initializes the minimum bytes required to suppress MSAN warnings from
+// legitimate vector loads/stores on the right border, where some lanes are
+// uninitialized and assumed to be unused.
+void InitializePadding(PlaneBase& plane, const size_t sizeof_t) {
+#if defined(MEMORY_SANITIZER)
+ size_t xsize = plane.xsize();
+ size_t ysize = plane.ysize();
+ if (xsize == 0 || ysize == 0) return;
-// Returns distance [bytes] between the start of two consecutive rows, a
-// multiple of vector/cache line size but NOT CacheAligned::kAlias - see below.
-size_t BytesPerRow(const size_t xsize, const size_t sizeof_t) {
- const size_t vec_size = VectorSize();
- size_t valid_bytes = xsize * sizeof_t;
+ const size_t vec_size = MaxVectorSize();
+ if (vec_size == 0) return; // Scalar mode: no padding needed
- // Allow unaligned accesses starting at the last valid value - this may raise
- // msan errors unless the user calls InitializePaddingForUnalignedAccesses.
- // Skip for the scalar case because no extra lanes will be loaded.
- if (vec_size != 0) {
- valid_bytes += vec_size - sizeof_t;
- }
+ const size_t valid_size = xsize * sizeof_t;
+ const size_t initialize_size = RoundUpTo(valid_size, vec_size);
+ if (valid_size == initialize_size) return;
- // Round up to vector and cache line size.
- const size_t align = std::max(vec_size, CacheAligned::kAlignment);
- size_t bytes_per_row = RoundUpTo(valid_bytes, align);
-
- // During the lengthy window before writes are committed to memory, CPUs
- // guard against read after write hazards by checking the address, but
- // only the lower 11 bits. We avoid a false dependency between writes to
- // consecutive rows by ensuring their sizes are not multiples of 2 KiB.
- // Avoid2K prevents the same problem for the planes of an Image3.
- if (bytes_per_row % CacheAligned::kAlias == 0) {
- bytes_per_row += align;
+ for (size_t y = 0; y < ysize; ++y) {
+ uint8_t* JXL_RESTRICT row = plane.bytes() + y * plane.bytes_per_row();
+#if defined(__clang__) && \
+ ((!defined(__apple_build_version__) && __clang_major__ <= 6) || \
+ (defined(__apple_build_version__) && \
+ __apple_build_version__ <= 10001145))
+ // There's a bug in MSAN in clang-6 when handling AVX2 operations. This
+ // workaround allows tests to pass on MSAN, although it is slower and
+ // prevents MSAN warnings from uninitialized images.
+ std::fill(row, msan::kSanitizerSentinelByte, initialize_size);
+#else
+ memset(row + valid_size, msan::kSanitizerSentinelByte,
+ initialize_size - valid_size);
+#endif // clang6
}
-
- JXL_ASSERT(bytes_per_row % align == 0);
- return bytes_per_row;
+#endif // MEMORY_SANITIZER
}
} // namespace
-size_t VectorSize() {
- static size_t bytes = HWY_DYNAMIC_DISPATCH(GetVectorSize)();
- return bytes;
-}
-
PlaneBase::PlaneBase(const size_t xsize, const size_t ysize,
const size_t sizeof_t)
: xsize_(static_cast<uint32_t>(xsize)),
ysize_(static_cast<uint32_t>(ysize)),
orig_xsize_(static_cast<uint32_t>(xsize)),
- orig_ysize_(static_cast<uint32_t>(ysize)) {
+ orig_ysize_(static_cast<uint32_t>(ysize)),
+ bytes_per_row_(BytesPerRow(xsize_, sizeof_t)),
+ bytes_(nullptr),
+ sizeof_t_(sizeof_t) {
+ // TODO(eustas): turn to error instead of abort.
JXL_CHECK(xsize == xsize_);
JXL_CHECK(ysize == ysize_);
JXL_ASSERT(sizeof_t == 1 || sizeof_t == 2 || sizeof_t == 4 || sizeof_t == 8);
+}
+
+Status PlaneBase::Allocate() {
+ JXL_CHECK(!bytes_.get());
- bytes_per_row_ = 0;
// Dimensions can be zero, e.g. for lazily-allocated images. Only allocate
// if nonzero, because "zero" bytes still have padding/bookkeeping overhead.
- if (xsize != 0 && ysize != 0) {
- bytes_per_row_ = BytesPerRow(xsize, sizeof_t);
- bytes_ = AllocateArray(bytes_per_row_ * ysize);
- JXL_CHECK(bytes_.get());
- InitializePadding(sizeof_t, Padding::kRoundUp);
+ if (xsize_ == 0 || ysize_ == 0) {
+ return true;
}
-}
-
-void PlaneBase::InitializePadding(const size_t sizeof_t, Padding padding) {
-#if defined(MEMORY_SANITIZER) || HWY_IDE
- if (xsize_ == 0 || ysize_ == 0) return;
-
- const size_t vec_size = VectorSize();
- if (vec_size == 0) return; // Scalar mode: no padding needed
-
- const size_t valid_size = xsize_ * sizeof_t;
- const size_t initialize_size = padding == Padding::kRoundUp
- ? RoundUpTo(valid_size, vec_size)
- : valid_size + vec_size - sizeof_t;
- if (valid_size == initialize_size) return;
- for (size_t y = 0; y < ysize_; ++y) {
- uint8_t* JXL_RESTRICT row = static_cast<uint8_t*>(VoidRow(y));
-#if defined(__clang__) && \
- ((!defined(__apple_build_version__) && __clang_major__ <= 6) || \
- (defined(__apple_build_version__) && \
- __apple_build_version__ <= 10001145))
- // There's a bug in msan in clang-6 when handling AVX2 operations. This
- // workaround allows tests to pass on msan, although it is slower and
- // prevents msan warnings from uninitialized images.
- std::fill(row, msan::kSanitizerSentinelByte, initialize_size);
-#else
- memset(row + valid_size, msan::kSanitizerSentinelByte,
- initialize_size - valid_size);
-#endif // clang6
+ bytes_ = AllocateArray(bytes_per_row_ * ysize_);
+ if (!bytes_.get()) {
+ // TODO(eustas): use specialized OOM error code
+ return JXL_FAILURE("Failed to allocate memory for image surface");
}
-#endif // MEMORY_SANITIZER
+ InitializePadding(*this, sizeof_t_);
+
+ return true;
}
void PlaneBase::Swap(PlaneBase& other) {
@@ -133,73 +103,5 @@ void PlaneBase::Swap(PlaneBase& other) {
std::swap(bytes_, other.bytes_);
}
-void PadImageToBlockMultipleInPlace(Image3F* JXL_RESTRICT in,
- size_t block_dim) {
- const size_t xsize_orig = in->xsize();
- const size_t ysize_orig = in->ysize();
- const size_t xsize = RoundUpTo(xsize_orig, block_dim);
- const size_t ysize = RoundUpTo(ysize_orig, block_dim);
- // Expands image size to the originally-allocated size.
- in->ShrinkTo(xsize, ysize);
- for (size_t c = 0; c < 3; c++) {
- for (size_t y = 0; y < ysize_orig; y++) {
- float* JXL_RESTRICT row = in->PlaneRow(c, y);
- for (size_t x = xsize_orig; x < xsize; x++) {
- row[x] = row[xsize_orig - 1];
- }
- }
- const float* JXL_RESTRICT row_src = in->ConstPlaneRow(c, ysize_orig - 1);
- for (size_t y = ysize_orig; y < ysize; y++) {
- memcpy(in->PlaneRow(c, y), row_src, xsize * sizeof(float));
- }
- }
-}
-
-static void DownsampleImage(const ImageF& input, size_t factor,
- ImageF* output) {
- JXL_ASSERT(factor != 1);
- output->ShrinkTo(DivCeil(input.xsize(), factor),
- DivCeil(input.ysize(), factor));
- size_t in_stride = input.PixelsPerRow();
- for (size_t y = 0; y < output->ysize(); y++) {
- float* row_out = output->Row(y);
- const float* row_in = input.Row(factor * y);
- for (size_t x = 0; x < output->xsize(); x++) {
- size_t cnt = 0;
- float sum = 0;
- for (size_t iy = 0; iy < factor && iy + factor * y < input.ysize();
- iy++) {
- for (size_t ix = 0; ix < factor && ix + factor * x < input.xsize();
- ix++) {
- sum += row_in[iy * in_stride + x * factor + ix];
- cnt++;
- }
- }
- row_out[x] = sum / cnt;
- }
- }
-}
-
-void DownsampleImage(ImageF* image, size_t factor) {
- // Allocate extra space to avoid a reallocation when padding.
- ImageF downsampled(DivCeil(image->xsize(), factor) + kBlockDim,
- DivCeil(image->ysize(), factor) + kBlockDim);
- DownsampleImage(*image, factor, &downsampled);
- *image = std::move(downsampled);
-}
-
-void DownsampleImage(Image3F* opsin, size_t factor) {
- JXL_ASSERT(factor != 1);
- // Allocate extra space to avoid a reallocation when padding.
- Image3F downsampled(DivCeil(opsin->xsize(), factor) + kBlockDim,
- DivCeil(opsin->ysize(), factor) + kBlockDim);
- downsampled.ShrinkTo(downsampled.xsize() - kBlockDim,
- downsampled.ysize() - kBlockDim);
- for (size_t c = 0; c < 3; c++) {
- DownsampleImage(opsin->Plane(c), factor, &downsampled.Plane(c));
- }
- *opsin = std::move(downsampled);
-}
-
+} // namespace detail
} // namespace jxl
-#endif // HWY_ONCE
diff --git a/third_party/jpeg-xl/lib/jxl/image.h b/third_party/jpeg-xl/lib/jxl/image.h
index 98c387bb77..be97b929e3 100644
--- a/third_party/jpeg-xl/lib/jxl/image.h
+++ b/third_party/jpeg-xl/lib/jxl/image.h
@@ -28,8 +28,8 @@
namespace jxl {
-// Helper function to create rows that are multiples of SIMD vector size.
-size_t VectorSize();
+// DO NOT use PlaneBase outside of image.{h|cc}
+namespace detail {
// Type-independent parts of Plane<> - reduces code duplication and facilitates
// moving member function implementations to cc file.
@@ -40,8 +40,8 @@ struct PlaneBase {
orig_xsize_(0),
orig_ysize_(0),
bytes_per_row_(0),
- bytes_(nullptr) {}
- PlaneBase(size_t xsize, size_t ysize, size_t sizeof_t);
+ bytes_(nullptr),
+ sizeof_t_(0) {}
// Copy construction/assignment is forbidden to avoid inadvertent copies,
// which can be very expensive. Use CopyImageTo() instead.
@@ -88,13 +88,16 @@ struct PlaneBase {
}
protected:
+ PlaneBase(size_t xsize, size_t ysize, size_t sizeof_t);
+ Status Allocate();
+
// Returns pointer to the start of a row.
JXL_INLINE void* VoidRow(const size_t y) const {
#if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER) || \
defined(THREAD_SANITIZER)
if (y >= ysize_) {
- JXL_ABORT("Row(%" PRIu64 ") in (%u x %u) image\n", (uint64_t)y, xsize_,
- ysize_);
+ JXL_ABORT("Row(%" PRIu64 ") in (%u x %u) image\n",
+ static_cast<uint64_t>(y), xsize_, ysize_);
}
#endif
@@ -102,21 +105,6 @@ struct PlaneBase {
return JXL_ASSUME_ALIGNED(row, 64);
}
- enum class Padding {
- // Allow Load(d, row + x) for x = 0; x < xsize(); x += Lanes(d). Default.
- kRoundUp,
- // Allow LoadU(d, row + x) for x = xsize() - 1. This requires an extra
- // vector to be initialized. If done by default, this would suppress
- // legitimate msan warnings. We therefore require users to explicitly call
- // InitializePadding before using unaligned loads (e.g. convolution).
- kUnaligned
- };
-
- // Initializes the minimum bytes required to suppress msan warnings from
- // legitimate (according to Padding mode) vector loads/stores on the right
- // border, where some lanes are uninitialized and assumed to be unused.
- void InitializePadding(size_t sizeof_t, Padding padding);
-
// (Members are non-const to enable assignment during move-assignment.)
uint32_t xsize_; // In valid pixels, not including any padding.
uint32_t ysize_;
@@ -124,8 +112,11 @@ struct PlaneBase {
uint32_t orig_ysize_;
size_t bytes_per_row_; // Includes padding.
CacheAlignedUniquePtr bytes_;
+ size_t sizeof_t_;
};
+} // namespace detail
+
// Single channel, aligned rows separated by padding. T must be POD.
//
// 'Single channel' (one 2D array per channel) simplifies vectorization
@@ -148,17 +139,17 @@ struct PlaneBase {
// provides convenient accessors for xsize/ysize, which shortens function
// argument lists. Supports move-construction so it can be stored in containers.
template <typename ComponentType>
-class Plane : public PlaneBase {
+class Plane : public detail::PlaneBase {
public:
using T = ComponentType;
static constexpr size_t kNumPlanes = 1;
Plane() = default;
- Plane(const size_t xsize, const size_t ysize)
- : PlaneBase(xsize, ysize, sizeof(T)) {}
- void InitializePaddingForUnalignedAccesses() {
- InitializePadding(sizeof(T), Padding::kUnaligned);
+ static StatusOr<Plane> Create(const size_t xsize, const size_t ysize) {
+ Plane plane(xsize, ysize, sizeof(T));
+ JXL_RETURN_IF_ERROR(plane.Allocate());
+ return plane;
}
JXL_INLINE T* Row(const size_t y) { return static_cast<T*>(VoidRow(y)); }
@@ -179,6 +170,10 @@ class Plane : public PlaneBase {
JXL_INLINE intptr_t PixelsPerRow() const {
return static_cast<intptr_t>(bytes_per_row_ / sizeof(T));
}
+
+ private:
+ Plane(size_t xsize, size_t ysize, size_t sizeof_t)
+ : detail::PlaneBase(xsize, ysize, sizeof_t) {}
};
using ImageSB = Plane<int8_t>;
@@ -189,12 +184,6 @@ using ImageI = Plane<int32_t>;
using ImageF = Plane<float>;
using ImageD = Plane<double>;
-// Also works for Image3 and mixed argument types.
-template <class Image1, class Image2>
-bool SameSize(const Image1& image1, const Image2& image2) {
- return image1.xsize() == image2.xsize() && image1.ysize() == image2.ysize();
-}
-
template <typename T>
class Image3;
@@ -339,7 +328,8 @@ class RectT {
template <typename U>
RectT<U> As() const {
- return RectT<U>(U(x0_), U(y0_), U(xsize_), U(ysize_));
+ return RectT<U>(static_cast<U>(x0_), static_cast<U>(y0_),
+ static_cast<U>(xsize_), static_cast<U>(ysize_));
}
private:
@@ -394,24 +384,12 @@ class Image3 {
Image3() : planes_{PlaneT(), PlaneT(), PlaneT()} {}
- Image3(const size_t xsize, const size_t ysize)
- : planes_{PlaneT(xsize, ysize), PlaneT(xsize, ysize),
- PlaneT(xsize, ysize)} {}
-
Image3(Image3&& other) noexcept {
for (size_t i = 0; i < kNumPlanes; i++) {
planes_[i] = std::move(other.planes_[i]);
}
}
- Image3(PlaneT&& plane0, PlaneT&& plane1, PlaneT&& plane2) {
- JXL_CHECK(SameSize(plane0, plane1));
- JXL_CHECK(SameSize(plane0, plane2));
- planes_[0] = std::move(plane0);
- planes_[1] = std::move(plane1);
- planes_[2] = std::move(plane2);
- }
-
// Copy construction/assignment is forbidden to avoid inadvertent copies,
// which can be very expensive. Use CopyImageTo instead.
Image3(const Image3& other) = delete;
@@ -424,6 +402,17 @@ class Image3 {
return *this;
}
+ static StatusOr<Image3> Create(const size_t xsize, const size_t ysize) {
+ StatusOr<PlaneT> plane0 = PlaneT::Create(xsize, ysize);
+ JXL_RETURN_IF_ERROR(plane0.status());
+ StatusOr<PlaneT> plane1 = PlaneT::Create(xsize, ysize);
+ JXL_RETURN_IF_ERROR(plane1.status());
+ StatusOr<PlaneT> plane2 = PlaneT::Create(xsize, ysize);
+ JXL_RETURN_IF_ERROR(plane2.status());
+ return Image3(std::move(plane0).value(), std::move(plane1).value(),
+ std::move(plane2).value());
+ }
+
// Returns row pointer; usage: PlaneRow(idx_plane, y)[x] = val.
JXL_INLINE T* PlaneRow(const size_t c, const size_t y) {
// Custom implementation instead of calling planes_[c].Row ensures only a
@@ -481,6 +470,12 @@ class Image3 {
JXL_INLINE intptr_t PixelsPerRow() const { return planes_[0].PixelsPerRow(); }
private:
+ Image3(PlaneT&& plane0, PlaneT&& plane1, PlaneT&& plane2) {
+ planes_[0] = std::move(plane0);
+ planes_[1] = std::move(plane1);
+ planes_[2] = std::move(plane2);
+ }
+
void PlaneRowBoundsCheck(const size_t c, const size_t y) const {
#if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER) || \
defined(THREAD_SANITIZER)
@@ -493,7 +488,6 @@ class Image3 {
#endif
}
- private:
PlaneT planes_[kNumPlanes];
};
diff --git a/third_party/jpeg-xl/lib/jxl/image_bundle.h b/third_party/jpeg-xl/lib/jxl/image_bundle.h
index 2eea496d5e..c8a76a9f59 100644
--- a/third_party/jpeg-xl/lib/jxl/image_bundle.h
+++ b/third_party/jpeg-xl/lib/jxl/image_bundle.h
@@ -42,14 +42,16 @@ class ImageBundle {
ImageBundle(ImageBundle&&) = default;
ImageBundle& operator=(ImageBundle&&) = default;
- ImageBundle Copy() const {
+ StatusOr<ImageBundle> Copy() const {
ImageBundle copy(metadata_);
- copy.color_ = Image3F(color_.xsize(), color_.ysize());
+ JXL_ASSIGN_OR_RETURN(copy.color_,
+ Image3F::Create(color_.xsize(), color_.ysize()));
CopyImageTo(color_, &copy.color_);
copy.c_current_ = c_current_;
copy.extra_channels_.reserve(extra_channels_.size());
for (const ImageF& plane : extra_channels_) {
- ImageF ec(plane.xsize(), plane.ysize());
+ JXL_ASSIGN_OR_RETURN(ImageF ec,
+ ImageF::Create(plane.xsize(), plane.ysize()));
CopyImageTo(plane, &ec);
copy.extra_channels_.emplace_back(std::move(ec));
}
diff --git a/third_party/jpeg-xl/lib/jxl/image_metadata.cc b/third_party/jpeg-xl/lib/jxl/image_metadata.cc
index 4cca910753..91d27ff44e 100644
--- a/third_party/jpeg-xl/lib/jxl/image_metadata.cc
+++ b/third_party/jpeg-xl/lib/jxl/image_metadata.cc
@@ -10,6 +10,7 @@
#include "lib/jxl/alpha.h"
#include "lib/jxl/base/byte_order.h"
+#include "lib/jxl/base/matrix_ops.h"
#include "lib/jxl/cms/opsin_params.h"
#include "lib/jxl/fields.h"
#include "lib/jxl/frame_header.h"
@@ -245,8 +246,8 @@ Status ExtraChannelInfo::VisitFields(Visitor* JXL_RESTRICT visitor) {
}
if (type == ExtraChannel::kUnknown ||
- (int(ExtraChannel::kReserved0) <= int(type) &&
- int(type) <= int(ExtraChannel::kReserved7))) {
+ (static_cast<int>(ExtraChannel::kReserved0) <= static_cast<int>(type) &&
+ static_cast<int>(type) <= static_cast<int>(ExtraChannel::kReserved7))) {
return JXL_FAILURE("Unknown extra channel (bits %u, shift %u, name '%s')\n",
bit_depth.bits_per_sample, dim_shift, name.c_str());
}
@@ -354,10 +355,13 @@ Status OpsinInverseMatrix::VisitFields(Visitor* JXL_RESTRICT visitor) {
visitor->SetDefault(this);
return true;
}
- for (int i = 0; i < 9; ++i) {
- JXL_QUIET_RETURN_IF_ERROR(
- visitor->F16(jxl::cms::DefaultInverseOpsinAbsorbanceMatrix()[i],
- &inverse_matrix[i]));
+ const Matrix3x3& default_inverse =
+ jxl::cms::DefaultInverseOpsinAbsorbanceMatrix();
+ for (int j = 0; j < 3; ++j) {
+ for (int i = 0; i < 3; ++i) {
+ JXL_QUIET_RETURN_IF_ERROR(
+ visitor->F16(default_inverse[j][i], &inverse_matrix[j][i]));
+ }
}
for (int i = 0; i < 3; ++i) {
JXL_QUIET_RETURN_IF_ERROR(visitor->F16(
diff --git a/third_party/jpeg-xl/lib/jxl/image_metadata.h b/third_party/jpeg-xl/lib/jxl/image_metadata.h
index be603a49f3..2b647cfa30 100644
--- a/third_party/jpeg-xl/lib/jxl/image_metadata.h
+++ b/third_party/jpeg-xl/lib/jxl/image_metadata.h
@@ -138,7 +138,7 @@ struct OpsinInverseMatrix : public Fields {
mutable bool all_default;
- float inverse_matrix[9];
+ Matrix3x3 inverse_matrix;
float opsin_biases[3];
float quant_biases[4];
};
diff --git a/third_party/jpeg-xl/lib/jxl/image_ops.cc b/third_party/jpeg-xl/lib/jxl/image_ops.cc
new file mode 100644
index 0000000000..36d19df938
--- /dev/null
+++ b/third_party/jpeg-xl/lib/jxl/image_ops.cc
@@ -0,0 +1,91 @@
+// Copyright (c) the JPEG XL Project Authors. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "lib/jxl/image_ops.h"
+
+#include <cstddef>
+#include <cstring>
+
+#include "lib/jxl/base/common.h"
+#include "lib/jxl/base/compiler_specific.h"
+#include "lib/jxl/base/status.h"
+#include "lib/jxl/frame_dimensions.h"
+#include "lib/jxl/image.h"
+
+namespace jxl {
+
+void PadImageToBlockMultipleInPlace(Image3F* JXL_RESTRICT in,
+ size_t block_dim) {
+ const size_t xsize_orig = in->xsize();
+ const size_t ysize_orig = in->ysize();
+ const size_t xsize = RoundUpTo(xsize_orig, block_dim);
+ const size_t ysize = RoundUpTo(ysize_orig, block_dim);
+ // Expands image size to the originally-allocated size.
+ in->ShrinkTo(xsize, ysize);
+ for (size_t c = 0; c < 3; c++) {
+ for (size_t y = 0; y < ysize_orig; y++) {
+ float* JXL_RESTRICT row = in->PlaneRow(c, y);
+ for (size_t x = xsize_orig; x < xsize; x++) {
+ row[x] = row[xsize_orig - 1];
+ }
+ }
+ const float* JXL_RESTRICT row_src = in->ConstPlaneRow(c, ysize_orig - 1);
+ for (size_t y = ysize_orig; y < ysize; y++) {
+ memcpy(in->PlaneRow(c, y), row_src, xsize * sizeof(float));
+ }
+ }
+}
+
+static void DoDownsampleImage(const ImageF& input, size_t factor,
+ ImageF* output) {
+ JXL_ASSERT(factor != 1);
+ output->ShrinkTo(DivCeil(input.xsize(), factor),
+ DivCeil(input.ysize(), factor));
+ size_t in_stride = input.PixelsPerRow();
+ for (size_t y = 0; y < output->ysize(); y++) {
+ float* row_out = output->Row(y);
+ const float* row_in = input.Row(factor * y);
+ for (size_t x = 0; x < output->xsize(); x++) {
+ size_t cnt = 0;
+ float sum = 0;
+ for (size_t iy = 0; iy < factor && iy + factor * y < input.ysize();
+ iy++) {
+ for (size_t ix = 0; ix < factor && ix + factor * x < input.xsize();
+ ix++) {
+ sum += row_in[iy * in_stride + x * factor + ix];
+ cnt++;
+ }
+ }
+ row_out[x] = sum / cnt;
+ }
+ }
+}
+
+StatusOr<ImageF> DownsampleImage(const ImageF& image, size_t factor) {
+ ImageF downsampled;
+ // Allocate extra space to avoid a reallocation when padding.
+ JXL_ASSIGN_OR_RETURN(
+ downsampled, ImageF::Create(DivCeil(image.xsize(), factor) + kBlockDim,
+ DivCeil(image.ysize(), factor) + kBlockDim));
+ DoDownsampleImage(image, factor, &downsampled);
+ return downsampled;
+}
+
+StatusOr<Image3F> DownsampleImage(const Image3F& opsin, size_t factor) {
+ JXL_ASSERT(factor != 1);
+ // Allocate extra space to avoid a reallocation when padding.
+ Image3F downsampled;
+ JXL_ASSIGN_OR_RETURN(
+ downsampled, Image3F::Create(DivCeil(opsin.xsize(), factor) + kBlockDim,
+ DivCeil(opsin.ysize(), factor) + kBlockDim));
+ downsampled.ShrinkTo(downsampled.xsize() - kBlockDim,
+ downsampled.ysize() - kBlockDim);
+ for (size_t c = 0; c < 3; c++) {
+ DoDownsampleImage(opsin.Plane(c), factor, &downsampled.Plane(c));
+ }
+ return downsampled;
+}
+
+} // namespace jxl
diff --git a/third_party/jpeg-xl/lib/jxl/image_ops.h b/third_party/jpeg-xl/lib/jxl/image_ops.h
index b2ce23f13d..84cf7dad76 100644
--- a/third_party/jpeg-xl/lib/jxl/image_ops.h
+++ b/third_party/jpeg-xl/lib/jxl/image_ops.h
@@ -9,16 +9,23 @@
// Operations on images.
#include <algorithm>
-#include <array>
+#include <cstddef>
+#include <cstdint>
#include <limits>
-#include <vector>
+#include "lib/jxl/base/compiler_specific.h"
#include "lib/jxl/base/status.h"
#include "lib/jxl/frame_dimensions.h"
#include "lib/jxl/image.h"
namespace jxl {
+// Works for mixed image-like argument types.
+template <class Image1, class Image2>
+bool SameSize(const Image1& image1, const Image2& image2) {
+ return image1.xsize() == image2.xsize() && image1.ysize() == image2.ysize();
+}
+
template <typename T>
void CopyImageTo(const Plane<T>& from, Plane<T>* JXL_RESTRICT to) {
JXL_ASSERT(SameSize(from, *to));
@@ -102,76 +109,15 @@ void CopyImageToWithPadding(const Rect& from_rect, const T& from,
to);
}
-template <class ImageIn, class ImageOut>
-void Subtract(const ImageIn& image1, const ImageIn& image2, ImageOut* out) {
- using T = typename ImageIn::T;
- const size_t xsize = image1.xsize();
- const size_t ysize = image1.ysize();
- JXL_CHECK(xsize == image2.xsize());
- JXL_CHECK(ysize == image2.ysize());
-
- for (size_t y = 0; y < ysize; ++y) {
- const T* const JXL_RESTRICT row1 = image1.Row(y);
- const T* const JXL_RESTRICT row2 = image2.Row(y);
- T* const JXL_RESTRICT row_out = out->Row(y);
- for (size_t x = 0; x < xsize; ++x) {
- row_out[x] = row1[x] - row2[x];
- }
- }
-}
-
-// In-place.
-template <typename Tin, typename Tout>
-void SubtractFrom(const Plane<Tin>& what, Plane<Tout>* to) {
- const size_t xsize = what.xsize();
- const size_t ysize = what.ysize();
- for (size_t y = 0; y < ysize; ++y) {
- const Tin* JXL_RESTRICT row_what = what.ConstRow(y);
- Tout* JXL_RESTRICT row_to = to->Row(y);
- for (size_t x = 0; x < xsize; ++x) {
- row_to[x] -= row_what[x];
- }
- }
-}
-
-// In-place.
-template <typename Tin, typename Tout>
-void AddTo(const Plane<Tin>& what, Plane<Tout>* to) {
- const size_t xsize = what.xsize();
- const size_t ysize = what.ysize();
- for (size_t y = 0; y < ysize; ++y) {
- const Tin* JXL_RESTRICT row_what = what.ConstRow(y);
- Tout* JXL_RESTRICT row_to = to->Row(y);
- for (size_t x = 0; x < xsize; ++x) {
- row_to[x] += row_what[x];
- }
- }
-}
-
-template <typename Tin, typename Tout>
-void AddTo(Rect rectFrom, const Plane<Tin>& what, Rect rectTo,
- Plane<Tout>* to) {
- JXL_ASSERT(SameSize(rectFrom, rectTo));
- const size_t xsize = rectTo.xsize();
- const size_t ysize = rectTo.ysize();
- for (size_t y = 0; y < ysize; ++y) {
- const Tin* JXL_RESTRICT row_what = rectFrom.ConstRow(what, y);
- Tout* JXL_RESTRICT row_to = rectTo.Row(to, y);
- for (size_t x = 0; x < xsize; ++x) {
- row_to[x] += row_what[x];
- }
- }
-}
-
// Returns linear combination of two grayscale images.
template <typename T>
-Plane<T> LinComb(const T lambda1, const Plane<T>& image1, const T lambda2,
- const Plane<T>& image2) {
+StatusOr<Plane<T>> LinComb(const T lambda1, const Plane<T>& image1,
+ const T lambda2, const Plane<T>& image2) {
const size_t xsize = image1.xsize();
const size_t ysize = image1.ysize();
JXL_CHECK(xsize == image2.xsize());
JXL_CHECK(ysize == image2.ysize());
- Plane<T> out(xsize, ysize);
+ JXL_ASSIGN_OR_RETURN(Plane<T> out, Plane<T>::Create(xsize, ysize));
for (size_t y = 0; y < ysize; ++y) {
const T* const JXL_RESTRICT row1 = image1.Row(y);
const T* const JXL_RESTRICT row2 = image2.Row(y);
@@ -291,35 +237,6 @@ struct WrapRowUnchanged {
}
};
-// Sets "thickness" pixels on each border to "value". This is faster than
-// initializing the entire image and overwriting valid/interior pixels.
-template <typename T>
-void SetBorder(const size_t thickness, const T value, Plane<T>* image) {
- const size_t xsize = image->xsize();
- const size_t ysize = image->ysize();
- // Top: fill entire row
- for (size_t y = 0; y < std::min(thickness, ysize); ++y) {
- T* const JXL_RESTRICT row = image->Row(y);
- std::fill(row, row + xsize, value);
- }
-
- // Bottom: fill entire row
- for (size_t y = ysize - thickness; y < ysize; ++y) {
- T* const JXL_RESTRICT row = image->Row(y);
- std::fill(row, row + xsize, value);
- }
-
- // Left/right: fill the 'columns' on either side, but only if the image is
- // big enough that they don't already belong to the top/bottom rows.
- if (ysize >= 2 * thickness) {
- for (size_t y = thickness; y < ysize - thickness; ++y) {
- T* const JXL_RESTRICT row = image->Row(y);
- std::fill(row, row + thickness, value);
- std::fill(row + xsize - thickness, row + xsize, value);
- }
- }
-}
-
// Computes the minimum and maximum pixel value.
template <typename T>
void ImageMinMax(const Plane<T>& image, T* const JXL_RESTRICT min,
@@ -335,48 +252,6 @@ void ImageMinMax(const Plane<T>& image, T* const JXL_RESTRICT min,
}
}
-template <typename T>
-Plane<T> ImageFromPacked(const std::vector<T>& packed, const size_t xsize,
- const size_t ysize) {
- Plane<T> out(xsize, ysize);
- for (size_t y = 0; y < ysize; ++y) {
- T* const JXL_RESTRICT row = out.Row(y);
- const T* const JXL_RESTRICT packed_row = &packed[y * xsize];
- memcpy(row, packed_row, xsize * sizeof(T));
- }
- return out;
-}
-
-template <typename T>
-void Image3Max(const Image3<T>& image, std::array<T, 3>* out_max) {
- for (size_t c = 0; c < 3; ++c) {
- T max = std::numeric_limits<T>::min();
- for (size_t y = 0; y < image.ysize(); ++y) {
- const T* JXL_RESTRICT row = image.ConstPlaneRow(c, y);
- for (size_t x = 0; x < image.xsize(); ++x) {
- max = std::max(max, row[x]);
- }
- }
- (*out_max)[c] = max;
- }
-}
-
-template <typename T>
-std::vector<T> PackedFromImage(const Plane<T>& image, const Rect& rect) {
- const size_t xsize = rect.xsize();
- const size_t ysize = rect.ysize();
- std::vector<T> packed(xsize * ysize);
- for (size_t y = 0; y < rect.ysize(); ++y) {
- memcpy(&packed[y * xsize], rect.ConstRow(image, y), xsize * sizeof(T));
- }
- return packed;
-}
-
-template <typename T>
-std::vector<T> PackedFromImage(const Plane<T>& image) {
- return PackedFromImage(image, Rect(image));
-}
-
// Initializes all planes to the same "value".
template <typename T>
void FillImage(const T value, Image3<T>* image) {
@@ -391,28 +266,6 @@ void FillImage(const T value, Image3<T>* image) {
}
template <typename T>
-void FillPlane(const T value, Plane<T>* image) {
- for (size_t y = 0; y < image->ysize(); ++y) {
- T* JXL_RESTRICT row = image->Row(y);
- for (size_t x = 0; x < image->xsize(); ++x) {
- row[x] = value;
- }
- }
-}
-
-template <typename T>
-void FillImage(const T value, Image3<T>* image, Rect rect) {
- for (size_t c = 0; c < 3; ++c) {
- for (size_t y = 0; y < rect.ysize(); ++y) {
- T* JXL_RESTRICT row = rect.PlaneRow(image, c, y);
- for (size_t x = 0; x < rect.xsize(); ++x) {
- row[x] = value;
- }
- }
- }
-}
-
-template <typename T>
void FillPlane(const T value, Plane<T>* image, Rect rect) {
for (size_t y = 0; y < rect.ysize(); ++y) {
T* JXL_RESTRICT row = rect.Row(image, y);
@@ -432,22 +285,14 @@ void ZeroFillImage(Image3<T>* image) {
}
}
-template <typename T>
-void ZeroFillPlane(Plane<T>* image, Rect rect) {
- for (size_t y = 0; y < rect.ysize(); ++y) {
- T* JXL_RESTRICT row = rect.Row(image, y);
- memset(row, 0, rect.xsize() * sizeof(T));
- }
-}
-
// Same as above, but operates in-place. Assumes that the `in` image was
// allocated large enough.
void PadImageToBlockMultipleInPlace(Image3F* JXL_RESTRICT in,
size_t block_dim = kBlockDim);
// Downsamples an image by a given factor.
-void DownsampleImage(Image3F* opsin, size_t factor);
-void DownsampleImage(ImageF* image, size_t factor);
+StatusOr<Image3F> DownsampleImage(const Image3F& opsin, size_t factor);
+StatusOr<ImageF> DownsampleImage(const ImageF& image, size_t factor);
} // namespace jxl
diff --git a/third_party/jpeg-xl/lib/jxl/image_ops_test.cc b/third_party/jpeg-xl/lib/jxl/image_ops_test.cc
index dfcb2292c5..6b308baab4 100644
--- a/third_party/jpeg-xl/lib/jxl/image_ops_test.cc
+++ b/third_party/jpeg-xl/lib/jxl/image_ops_test.cc
@@ -8,43 +8,19 @@
#include <stdint.h>
#include <stdlib.h>
-#include <utility>
+#include <cstring>
+#include "lib/jxl/base/compiler_specific.h"
#include "lib/jxl/base/printf_macros.h"
+#include "lib/jxl/base/random.h"
+#include "lib/jxl/base/status.h"
+#include "lib/jxl/cache_aligned.h"
#include "lib/jxl/image.h"
-#include "lib/jxl/image_test_utils.h"
#include "lib/jxl/testing.h"
namespace jxl {
namespace {
-template <typename T>
-void TestPacked(const size_t xsize, const size_t ysize) {
- Plane<T> image1(xsize, ysize);
- RandomFillImage(&image1);
- const std::vector<T>& packed = PackedFromImage(image1);
- const Plane<T>& image2 = ImageFromPacked(packed, xsize, ysize);
- JXL_EXPECT_OK(SamePixels(image1, image2, _));
-}
-
-TEST(ImageTest, TestPacked) {
- TestPacked<uint8_t>(1, 1);
- TestPacked<uint8_t>(7, 1);
- TestPacked<uint8_t>(1, 7);
-
- TestPacked<int16_t>(1, 1);
- TestPacked<int16_t>(7, 1);
- TestPacked<int16_t>(1, 7);
-
- TestPacked<uint16_t>(1, 1);
- TestPacked<uint16_t>(7, 1);
- TestPacked<uint16_t>(1, 7);
-
- TestPacked<float>(1, 1);
- TestPacked<float>(7, 1);
- TestPacked<float>(1, 7);
-}
-
// Ensure entire payload is readable/writable for various size/offset combos.
TEST(ImageTest, TestAllocator) {
Rng rng(0);
@@ -70,18 +46,18 @@ TEST(ImageTest, TestAllocator) {
template <typename T>
void TestFillImpl(Image3<T>* img, const char* layout) {
- FillImage(T(1), img);
+ FillImage(static_cast<T>(1), img);
for (size_t y = 0; y < img->ysize(); ++y) {
for (size_t c = 0; c < 3; ++c) {
T* JXL_RESTRICT row = img->PlaneRow(c, y);
for (size_t x = 0; x < img->xsize(); ++x) {
- if (row[x] != T(1)) {
+ if (row[x] != static_cast<T>(1)) {
printf("Not 1 at c=%" PRIuS " %" PRIuS ", %" PRIuS " (%" PRIuS
" x %" PRIuS ") (%s)\n",
c, x, y, img->xsize(), img->ysize(), layout);
abort();
}
- row[x] = T(2);
+ row[x] = static_cast<T>(2);
}
}
}
@@ -92,13 +68,13 @@ void TestFillImpl(Image3<T>* img, const char* layout) {
for (size_t y = 0; y < img->ysize(); ++y) {
T* JXL_RESTRICT row = img->PlaneRow(c, y);
for (size_t x = 0; x < img->xsize(); ++x) {
- if (row[x] != T(0)) {
+ if (row[x] != static_cast<T>(0)) {
printf("Not 0 at c=%" PRIuS " %" PRIuS ", %" PRIuS " (%" PRIuS
" x %" PRIuS ") (%s)\n",
c, x, y, img->xsize(), img->ysize(), layout);
abort();
}
- row[x] = T(3);
+ row[x] = static_cast<T>(3);
}
}
}
@@ -108,12 +84,8 @@ template <typename T>
void TestFillT() {
for (uint32_t xsize : {0, 1, 15, 16, 31, 32}) {
for (uint32_t ysize : {0, 1, 15, 16, 31, 32}) {
- Image3<T> image(xsize, ysize);
+ JXL_ASSIGN_OR_DIE(Image3<T> image, Image3<T>::Create(xsize, ysize));
TestFillImpl(&image, "size ctor");
-
- Image3<T> planar(Plane<T>(xsize, ysize), Plane<T>(xsize, ysize),
- Plane<T>(xsize, ysize));
- TestFillImpl(&planar, "planar");
}
}
}
@@ -127,7 +99,7 @@ TEST(ImageTest, TestFill) {
}
TEST(ImageTest, CopyImageToWithPaddingTest) {
- Plane<uint32_t> src(100, 61);
+ JXL_ASSIGN_OR_DIE(Plane<uint32_t> src, Plane<uint32_t>::Create(100, 61));
for (size_t y = 0; y < src.ysize(); y++) {
for (size_t x = 0; x < src.xsize(); x++) {
src.Row(y)[x] = x * 1000 + y;
@@ -136,7 +108,7 @@ TEST(ImageTest, CopyImageToWithPaddingTest) {
Rect src_rect(10, 20, 30, 40);
EXPECT_TRUE(src_rect.IsInside(src));
- Plane<uint32_t> dst(60, 50);
+ JXL_ASSIGN_OR_DIE(Plane<uint32_t> dst, Plane<uint32_t>::Create(60, 50));
FillImage(0u, &dst);
Rect dst_rect(20, 5, 30, 40);
EXPECT_TRUE(dst_rect.IsInside(dst));
diff --git a/third_party/jpeg-xl/lib/jxl/image_test_utils.h b/third_party/jpeg-xl/lib/jxl/image_test_utils.h
index e7d72285e6..7bb146866e 100644
--- a/third_party/jpeg-xl/lib/jxl/image_test_utils.h
+++ b/third_party/jpeg-xl/lib/jxl/image_test_utils.h
@@ -6,10 +6,6 @@
#ifndef LIB_JXL_IMAGE_TEST_UTILS_H_
#define LIB_JXL_IMAGE_TEST_UTILS_H_
-#ifndef __STDC_FORMAT_MACROS
-#define __STDC_FORMAT_MACROS
-#endif
-
#include <inttypes.h>
#include <stddef.h>
#include <stdint.h>
@@ -37,7 +33,8 @@ bool SamePixels(const Plane<T>& image1, const Plane<T>& image2,
for (size_t x = rect.x0(); x < rect.xsize(); ++x) {
if (row1[x] != row2[x]) {
failures << "pixel mismatch" << x << ", " << y << ": "
- << double(row1[x]) << " != " << double(row2[x]) << "\n";
+ << static_cast<double>(row1[x])
+ << " != " << static_cast<double>(row2[x]) << "\n";
if (++mismatches > 4) {
return false;
}
@@ -66,7 +63,7 @@ bool VerifyRelativeError(const Plane<T>& expected, const Plane<T>& actual,
const double threshold_l1,
const double threshold_relative,
std::stringstream& failures, const intptr_t border = 0,
- const size_t c = 0) {
+ const int c = 0) {
JXL_CHECK(SameSize(expected, actual));
const intptr_t xsize = expected.xsize();
const intptr_t ysize = expected.ysize();
@@ -89,7 +86,8 @@ bool VerifyRelativeError(const Plane<T>& expected, const Plane<T>& actual,
max_l1 = std::max(max_l1, l1);
}
} else {
- const double relative = l1 / std::abs(double(row_expected[x]));
+ const double relative =
+ l1 / std::abs(static_cast<double>(row_expected[x]));
if (l1 > threshold_l1 && relative > threshold_relative) {
// Fails both tolerances => will exit below, update max_*.
any_bad = true;
@@ -104,13 +102,11 @@ bool VerifyRelativeError(const Plane<T>& expected, const Plane<T>& actual,
}
// Never had a valid relative value, don't print it.
if (max_relative < 0) {
- fprintf(stderr, "c=%" PRIu64 ": max +/- %E exceeds +/- %.2E\n",
- static_cast<uint64_t>(c), max_l1, threshold_l1);
+ fprintf(stderr, "c=%d: max +/- %E exceeds +/- %.2E\n", c, max_l1,
+ threshold_l1);
} else {
- fprintf(stderr,
- "c=%" PRIu64 ": max +/- %E, x %E exceeds +/- %.2E, x %.2E\n",
- static_cast<uint64_t>(c), max_l1, max_relative, threshold_l1,
- threshold_relative);
+ fprintf(stderr, "c=%d: max +/- %E, x %E exceeds +/- %.2E, x %.2E\n", c,
+ max_l1, max_relative, threshold_l1, threshold_relative);
}
// Dump the expected image and actual image if the region is small enough.
const intptr_t kMaxTestDumpSize = 16;
@@ -134,7 +130,8 @@ bool VerifyRelativeError(const Plane<T>& expected, const Plane<T>& actual,
bool bad = l1 > threshold_l1;
if (row_expected[x] > 1E-10) {
- const double relative = l1 / std::abs(double(row_expected[x]));
+ const double relative =
+ l1 / std::abs(static_cast<double>(row_expected[x]));
bad &= relative > threshold_relative;
}
if (bad) {
@@ -157,7 +154,8 @@ bool VerifyRelativeError(const Plane<T>& expected, const Plane<T>& actual,
bool bad = l1 > threshold_l1;
if (row_expected[x] > 1E-10) {
- const double relative = l1 / std::abs(double(row_expected[x]));
+ const double relative =
+ l1 / std::abs(static_cast<double>(row_expected[x]));
bad &= relative > threshold_relative;
}
if (bad) {
@@ -179,9 +177,9 @@ bool VerifyRelativeError(const Image3<T>& expected, const Image3<T>& actual,
std::stringstream& failures,
const intptr_t border = 0) {
for (size_t c = 0; c < 3; ++c) {
- bool ok =
- VerifyRelativeError(expected.Plane(c), actual.Plane(c), threshold_l1,
- threshold_relative, failures, border, c);
+ bool ok = VerifyRelativeError(expected.Plane(c), actual.Plane(c),
+ threshold_l1, threshold_relative, failures,
+ border, static_cast<int>(c));
if (!ok) {
return false;
}
@@ -216,8 +214,8 @@ template <typename T>
typename std::enable_if<std::is_integral<T>::value>::type RandomFillImage(
Plane<T>* image) {
Rng rng(129);
- GenerateImage(rng, image, int64_t(0),
- int64_t(std::numeric_limits<T>::max()) + 1);
+ GenerateImage(rng, image, static_cast<int64_t>(0),
+ static_cast<int64_t>(std::numeric_limits<T>::max()) + 1);
}
JXL_INLINE void RandomFillImage(Plane<float>* image) {
@@ -236,8 +234,8 @@ template <typename T>
typename std::enable_if<std::is_integral<T>::value>::type RandomFillImage(
Image3<T>* image) {
Rng rng(129);
- GenerateImage(rng, image, int64_t(0),
- int64_t(std::numeric_limits<T>::max()) + 1);
+ GenerateImage(rng, image, static_cast<int64_t>(0),
+ static_cast<int64_t>(std::numeric_limits<T>::max()) + 1);
}
JXL_INLINE void RandomFillImage(Image3F* image) {
diff --git a/third_party/jpeg-xl/lib/jxl/inverse_mtf-inl.h b/third_party/jpeg-xl/lib/jxl/inverse_mtf-inl.h
index fcb01d7396..60ea69ddbd 100644
--- a/third_party/jpeg-xl/lib/jxl/inverse_mtf-inl.h
+++ b/third_party/jpeg-xl/lib/jxl/inverse_mtf-inl.h
@@ -82,7 +82,7 @@ HWY_AFTER_NAMESPACE();
namespace jxl {
inline void InverseMoveToFrontTransform(uint8_t* v, int v_len) {
- return HWY_STATIC_DISPATCH(InverseMoveToFrontTransform)(v, v_len);
+ HWY_STATIC_DISPATCH(InverseMoveToFrontTransform)(v, v_len);
}
} // namespace jxl
diff --git a/third_party/jpeg-xl/lib/jxl/jpeg/dec_jpeg_data_writer.cc b/third_party/jpeg-xl/lib/jxl/jpeg/dec_jpeg_data_writer.cc
index 64560d9ab0..31bb2dda23 100644
--- a/third_party/jpeg-xl/lib/jxl/jpeg/dec_jpeg_data_writer.cc
+++ b/third_party/jpeg-xl/lib/jxl/jpeg/dec_jpeg_data_writer.cc
@@ -18,6 +18,7 @@
#include "lib/jxl/base/bits.h"
#include "lib/jxl/base/byte_order.h"
#include "lib/jxl/base/common.h"
+#include "lib/jxl/base/status.h"
#include "lib/jxl/frame_dimensions.h"
#include "lib/jxl/image_bundle.h"
#include "lib/jxl/jpeg/dec_jpeg_serialization_state.h"
@@ -42,7 +43,7 @@ const size_t kJpegBitWriterChunkSize = 16384;
// Returns non-zero if and only if x has a zero byte, i.e. one of
// x & 0xff, x & 0xff00, ..., x & 0xff00000000000000 is zero.
-static JXL_INLINE uint64_t HasZeroByte(uint64_t x) {
+JXL_INLINE uint64_t HasZeroByte(uint64_t x) {
return (x - 0x0101010101010101ULL) & ~x & 0x8080808080808080ULL;
}
@@ -57,7 +58,7 @@ void JpegBitWriterInit(JpegBitWriter* bw,
bw->data = bw->chunk.buffer->data();
}
-static JXL_NOINLINE void SwapBuffer(JpegBitWriter* bw) {
+JXL_NOINLINE void SwapBuffer(JpegBitWriter* bw) {
bw->chunk.len = bw->pos;
bw->output->emplace_back(std::move(bw->chunk));
bw->chunk = OutputChunk(kJpegBitWriterChunkSize);
@@ -65,7 +66,7 @@ static JXL_NOINLINE void SwapBuffer(JpegBitWriter* bw) {
bw->pos = 0;
}
-static JXL_INLINE void Reserve(JpegBitWriter* bw, size_t n_bytes) {
+JXL_INLINE void Reserve(JpegBitWriter* bw, size_t n_bytes) {
if (JXL_UNLIKELY((bw->pos + n_bytes) > kJpegBitWriterChunkSize)) {
SwapBuffer(bw);
}
@@ -77,14 +78,14 @@ static JXL_INLINE void Reserve(JpegBitWriter* bw, size_t n_bytes) {
* This method is "careless" - caller must make sure that there is enough
* space in the output buffer. Emits up to 2 bytes to buffer.
*/
-static JXL_INLINE void EmitByte(JpegBitWriter* bw, int byte) {
+JXL_INLINE void EmitByte(JpegBitWriter* bw, int byte) {
bw->data[bw->pos] = byte;
bw->data[bw->pos + 1] = 0;
bw->pos += (byte != 0xFF ? 1 : 2);
}
-static JXL_INLINE void DischargeBitBuffer(JpegBitWriter* bw, int nbits,
- uint64_t bits) {
+JXL_INLINE void DischargeBitBuffer(JpegBitWriter* bw, int nbits,
+ uint64_t bits) {
// At this point we are ready to emit the put_buffer to the output.
// The JPEG format requires that after every 0xff byte in the entropy
// coded section, there is a zero byte, therefore we first check if any of
@@ -111,7 +112,7 @@ static JXL_INLINE void DischargeBitBuffer(JpegBitWriter* bw, int nbits,
bw->put_buffer = bits << bw->put_bits;
}
-static JXL_INLINE void WriteBits(JpegBitWriter* bw, int nbits, uint64_t bits) {
+JXL_INLINE void WriteBits(JpegBitWriter* bw, int nbits, uint64_t bits) {
JXL_DASSERT(nbits > 0);
bw->put_bits -= nbits;
if (JXL_UNLIKELY(bw->put_bits < 0)) {
@@ -142,12 +143,14 @@ bool JumpToByteBoundary(JpegBitWriter* bw, const uint8_t** pad_bits,
} else {
pad_pattern = 0;
const uint8_t* src = *pad_bits;
- // TODO(eustas): bitwise reading looks insanely ineffective...
+ // TODO(eustas): bitwise reading looks insanely ineffective!
while (n_bits--) {
pad_pattern <<= 1;
if (src >= pad_bits_end) return false;
- // TODO(eustas): DCHECK *src == {0, 1}
- pad_pattern |= !!*(src++);
+ uint8_t bit = *src;
+ src++;
+ JXL_ASSERT(bit <= 1);
+ pad_pattern |= bit;
}
*pad_bits = src;
}
@@ -187,21 +190,20 @@ void DCTCodingStateInit(DCTCodingState* s) {
s->refinement_bits_.reserve(64);
}
-static JXL_INLINE void WriteSymbol(int symbol, HuffmanCodeTable* table,
- JpegBitWriter* bw) {
+JXL_INLINE void WriteSymbol(int symbol, HuffmanCodeTable* table,
+ JpegBitWriter* bw) {
WriteBits(bw, table->depth[symbol], table->code[symbol]);
}
-static JXL_INLINE void WriteSymbolBits(int symbol, HuffmanCodeTable* table,
- JpegBitWriter* bw, int nbits,
- uint64_t bits) {
+JXL_INLINE void WriteSymbolBits(int symbol, HuffmanCodeTable* table,
+ JpegBitWriter* bw, int nbits, uint64_t bits) {
WriteBits(bw, nbits + table->depth[symbol],
bits | (table->code[symbol] << nbits));
}
// Emit all buffered data to the bit stream using the given Huffman code and
// bit writer.
-static JXL_INLINE void Flush(DCTCodingState* s, JpegBitWriter* bw) {
+JXL_INLINE void Flush(DCTCodingState* s, JpegBitWriter* bw) {
if (s->eob_run_ > 0) {
Reserve(bw, 16);
int nbits = FloorLog2Nonzero<uint32_t>(s->eob_run_);
@@ -233,11 +235,9 @@ static JXL_INLINE void Flush(DCTCodingState* s, JpegBitWriter* bw) {
// Buffer some more data at the end-of-band (the last non-zero or newly
// non-zero coefficient within the [Ss, Se] spectral band).
-static JXL_INLINE void BufferEndOfBand(DCTCodingState* s,
- HuffmanCodeTable* ac_huff,
- const int* new_bits_array,
- size_t new_bits_count,
- JpegBitWriter* bw) {
+JXL_INLINE void BufferEndOfBand(DCTCodingState* s, HuffmanCodeTable* ac_huff,
+ const int* new_bits_array,
+ size_t new_bits_count, JpegBitWriter* bw) {
if (s->eob_run_ == 0) {
s->cur_ac_huff_ = ac_huff;
}
@@ -530,7 +530,7 @@ bool EncodeDCTBlockSequential(const coeff_t* coeffs, HuffmanCodeTable* dc_huff,
int dc_nbits = (temp2 == 0) ? 0 : (FloorLog2Nonzero<uint32_t>(temp2) + 1);
WriteSymbol(dc_nbits, dc_huff, bw);
-#if false
+#if JXL_FALSE
// If the input is corrupt, this could be triggered. Checking is
// costly though, so it makes more sense to avoid this branch.
// (producing a corrupt JPEG when the input is corrupt, instead
@@ -543,7 +543,8 @@ bool EncodeDCTBlockSequential(const coeff_t* coeffs, HuffmanCodeTable* dc_huff,
int16_t r = 0;
for (size_t i = 1; i < 64; i++) {
- if ((temp = coeffs[kJPEGNaturalOrder[i]]) == 0) {
+ temp = coeffs[kJPEGNaturalOrder[i]];
+ if (temp == 0) {
r++;
} else {
temp2 = temp >> (8 * sizeof(coeff_t) - 1);
@@ -611,7 +612,8 @@ bool EncodeDCTBlockProgressive(const coeff_t* coeffs, HuffmanCodeTable* dc_huff,
}
int r = 0;
for (int k = Ss; k <= Se; ++k) {
- if ((temp = coeffs[kJPEGNaturalOrder[k]]) == 0) {
+ temp = coeffs[kJPEGNaturalOrder[k]];
+ if (temp == 0) {
r++;
continue;
}
@@ -884,8 +886,8 @@ SerializationStatus JXL_NOINLINE DoEncodeScan(const JPEGData& jpg,
return SerializationStatus::DONE;
}
-static SerializationStatus JXL_INLINE EncodeScan(const JPEGData& jpg,
- SerializationState* state) {
+SerializationStatus JXL_INLINE EncodeScan(const JPEGData& jpg,
+ SerializationState* state) {
const JPEGScanInfo& scan_info = jpg.scan_info[state->scan_index];
const bool is_progressive = state->is_progressive;
const int Al = is_progressive ? scan_info.Al : 0;
diff --git a/third_party/jpeg-xl/lib/jxl/jpeg/enc_jpeg_data.cc b/third_party/jpeg-xl/lib/jxl/jpeg/enc_jpeg_data.cc
index 97342553e5..d311908415 100644
--- a/third_party/jpeg-xl/lib/jxl/jpeg/enc_jpeg_data.cc
+++ b/third_party/jpeg-xl/lib/jxl/jpeg/enc_jpeg_data.cc
@@ -8,7 +8,7 @@
#include <brotli/encode.h>
#include "lib/jxl/codec_in_out.h"
-#include "lib/jxl/enc_fields.h"
+#include "lib/jxl/enc_bit_writer.h"
#include "lib/jxl/image_bundle.h"
#include "lib/jxl/jpeg/enc_jpeg_data_reader.h"
#include "lib/jxl/luminance.h"
@@ -76,7 +76,8 @@ bool GetMarkerPayload(const uint8_t* data, size_t size, ByteSpan* payload) {
Status DetectBlobs(jpeg::JPEGData& jpeg_data) {
JXL_DASSERT(jpeg_data.app_data.size() == jpeg_data.app_marker_type.size());
- bool have_exif = false, have_xmp = false;
+ bool have_exif = false;
+ bool have_xmp = false;
for (size_t i = 0; i < jpeg_data.app_data.size(); i++) {
auto& marker = jpeg_data.app_data[i];
if (marker.empty() || marker[0] != kApp1) {
@@ -165,7 +166,7 @@ Status ParseChunkedMarker(const jpeg::JPEGData& src, uint8_t marker_type,
if (!presence[index]) {
return JXL_FAILURE("Missing chunk.");
}
- chunks[index].AppendTo(output);
+ chunks[index].AppendTo(*output);
}
return true;
@@ -173,7 +174,7 @@ Status ParseChunkedMarker(const jpeg::JPEGData& src, uint8_t marker_type,
Status SetBlobsFromJpegData(const jpeg::JPEGData& jpeg_data, Blobs* blobs) {
for (size_t i = 0; i < jpeg_data.app_data.size(); i++) {
- auto& marker = jpeg_data.app_data[i];
+ const auto& marker = jpeg_data.app_data[i];
if (marker.empty() || marker[0] != kApp1) {
continue;
}
@@ -210,7 +211,7 @@ Status SetBlobsFromJpegData(const jpeg::JPEGData& jpeg_data, Blobs* blobs) {
return true;
}
-static inline bool IsJPG(const Span<const uint8_t> bytes) {
+inline bool IsJPG(const Span<const uint8_t> bytes) {
return bytes.size() >= 2 && bytes[0] == 0xFF && bytes[1] == 0xD8;
}
@@ -239,14 +240,16 @@ Status SetChromaSubsamplingFromJpegData(const JPEGData& jpg,
return JXL_FAILURE("Cannot recompress JPEGs with neither 1 nor 3 channels");
}
if (nbcomp == 3) {
- uint8_t hsample[3], vsample[3];
+ uint8_t hsample[3];
+ uint8_t vsample[3];
for (size_t i = 0; i < nbcomp; i++) {
hsample[i] = jpg.components[i].h_samp_factor;
vsample[i] = jpg.components[i].v_samp_factor;
}
JXL_RETURN_IF_ERROR(cs->Set(hsample, vsample));
} else if (nbcomp == 1) {
- uint8_t hsample[3], vsample[3];
+ uint8_t hsample[3];
+ uint8_t vsample[3];
for (size_t i = 0; i < 3; i++) {
hsample[i] = jpg.components[0].h_samp_factor;
vsample[i] = jpg.components[0].v_samp_factor;
@@ -330,7 +333,7 @@ Status EncodeJPEGData(JPEGData& jpeg_data, std::vector<uint8_t>* bytes,
{
PaddedBytes serialized_jpeg_data = std::move(writer).TakeBytes();
bytes->reserve(serialized_jpeg_data.size() + brotli_capacity);
- Bytes(serialized_jpeg_data).AppendTo(bytes);
+ Bytes(serialized_jpeg_data).AppendTo(*bytes);
}
BrotliEncoderState* brotli_enc =
@@ -394,8 +397,9 @@ Status DecodeImageJPG(const Span<const uint8_t> bytes, CodecInOut* io) {
io->metadata.m.SetIntensityTarget(kDefaultIntensityTarget);
io->metadata.m.SetUintSamples(BITS_IN_JSAMPLE);
- io->SetFromImage(Image3F(jpeg_data->width, jpeg_data->height),
- io->metadata.m.color_encoding);
+ JXL_ASSIGN_OR_RETURN(Image3F tmp,
+ Image3F::Create(jpeg_data->width, jpeg_data->height));
+ io->SetFromImage(std::move(tmp), io->metadata.m.color_encoding);
SetIntensityTarget(&io->metadata.m);
return true;
}
diff --git a/third_party/jpeg-xl/lib/jxl/jpeg/enc_jpeg_data_reader.cc b/third_party/jpeg-xl/lib/jxl/jpeg/enc_jpeg_data_reader.cc
index ce64dae47b..d1e8476db6 100644
--- a/third_party/jpeg-xl/lib/jxl/jpeg/enc_jpeg_data_reader.cc
+++ b/third_party/jpeg-xl/lib/jxl/jpeg/enc_jpeg_data_reader.cc
@@ -23,7 +23,7 @@ namespace jxl {
namespace jpeg {
namespace {
-static const int kBrunsliMaxSampling = 15;
+const int kBrunsliMaxSampling = 15;
// Macros for commonly used error conditions.
@@ -234,7 +234,7 @@ bool ProcessDHT(const uint8_t* data, const size_t len, JpegReadMode mode,
JPEGHuffmanCode huff;
huff.slot_id = ReadUint8(data, pos);
int huffman_index = huff.slot_id;
- int is_ac_table = (huff.slot_id & 0x10) != 0;
+ bool is_ac_table = ((huff.slot_id & 0x10) != 0);
HuffmanTableEntry* huff_lut;
if (is_ac_table) {
huffman_index -= 0x10;
@@ -292,7 +292,7 @@ bool ProcessDHT(const uint8_t* data, const size_t len, JpegReadMode mode,
}
huff.is_last = (*pos == start_pos + marker_len);
if (mode == JpegReadMode::kReadAll) {
- BuildJpegHuffmanTable(&huff.counts[0], &huff.values[0], huff_lut);
+ BuildJpegHuffmanTable(huff.counts.data(), huff.values.data(), huff_lut);
}
jpg->huffman_code.push_back(huff);
}
@@ -419,7 +419,7 @@ struct BitReaderState {
if (bits_left_ <= 16) {
while (bits_left_ <= 56) {
val_ <<= 8;
- val_ |= (uint64_t)GetNextByte();
+ val_ |= static_cast<uint64_t>(GetNextByte());
bits_left_ += 8;
}
}
diff --git a/third_party/jpeg-xl/lib/jxl/jpeg/enc_jpeg_data_reader.h b/third_party/jpeg-xl/lib/jxl/jpeg/enc_jpeg_data_reader.h
index 3fad820e9d..4389b2ffe3 100644
--- a/third_party/jpeg-xl/lib/jxl/jpeg/enc_jpeg_data_reader.h
+++ b/third_party/jpeg-xl/lib/jxl/jpeg/enc_jpeg_data_reader.h
@@ -27,7 +27,7 @@ enum class JpegReadMode {
// If mode is kReadHeader, it fills in only the image dimensions in *jpg.
// Returns false if the data is not valid JPEG, or if it contains an unsupported
// JPEG feature.
-bool ReadJpeg(const uint8_t* data, const size_t len, JpegReadMode mode,
+bool ReadJpeg(const uint8_t* data, size_t len, JpegReadMode mode,
JPEGData* jpg);
} // namespace jpeg
diff --git a/third_party/jpeg-xl/lib/jxl/jpeg/jpeg_data.cc b/third_party/jpeg-xl/lib/jxl/jpeg/jpeg_data.cc
index 6744e6935a..aeb9914cca 100644
--- a/third_party/jpeg-xl/lib/jxl/jpeg/jpeg_data.cc
+++ b/third_party/jpeg-xl/lib/jxl/jpeg/jpeg_data.cc
@@ -5,6 +5,8 @@
#include "lib/jxl/jpeg/jpeg_data.h"
+#include <jxl/types.h>
+
#include "lib/jxl/base/printf_macros.h"
#include "lib/jxl/base/status.h"
#include "lib/jxl/common.h" // kMaxNumPasses, JPEGXL_ENABLE_TRANSCODE_JPEG
@@ -214,7 +216,7 @@ Status JPEGData::VisitFields(Visitor* visitor) {
huffman_code.resize(num_huff);
}
for (JPEGHuffmanCode& hc : huffman_code) {
- bool is_ac = hc.slot_id >> 4;
+ bool is_ac = ((hc.slot_id >> 4) != 0);
uint32_t id = hc.slot_id & 0xF;
JXL_RETURN_IF_ERROR(visitor->Bool(false, &is_ac));
JXL_RETURN_IF_ERROR(visitor->Bits(2, 0, &id));
@@ -240,7 +242,8 @@ Status JPEGData::VisitFields(Visitor* visitor) {
JXL_RETURN_IF_ERROR(visitor->U32(Bits(2), BitsOffset(2, 4),
BitsOffset(4, 8), BitsOffset(8, 1), 0,
&hc.values[i]));
- value_slots[hc.values[i] >> 6] |= (uint64_t)1 << (hc.values[i] & 0x3F);
+ value_slots[hc.values[i] >> 6] |= static_cast<uint64_t>(1)
+ << (hc.values[i] & 0x3F);
}
if (hc.values[num_symbols - 1] != kJpegHuffmanAlphabetSize) {
return JXL_FAILURE("Missing EOI symbol");
@@ -361,13 +364,13 @@ Status JPEGData::VisitFields(Visitor* visitor) {
for (uint32_t i = 0; i < nbit; i++) {
bool bbit = false;
JXL_RETURN_IF_ERROR(visitor->Bool(false, &bbit));
- padding_bits.push_back(bbit);
+ padding_bits.push_back(TO_JXL_BOOL(bbit));
}
} else {
for (uint8_t& bit : padding_bits) {
- bool bbit = bit;
+ bool bbit = FROM_JXL_BOOL(bit);
JXL_RETURN_IF_ERROR(visitor->Bool(false, &bbit));
- bit = bbit;
+ bit = TO_JXL_BOOL(bbit);
}
}
}
diff --git a/third_party/jpeg-xl/lib/jxl/jxl_test.cc b/third_party/jpeg-xl/lib/jxl/jxl_test.cc
index a91dbd0672..b0933d5d50 100644
--- a/third_party/jpeg-xl/lib/jxl/jxl_test.cc
+++ b/third_party/jpeg-xl/lib/jxl/jxl_test.cc
@@ -33,7 +33,6 @@
#include "lib/jxl/codec_in_out.h"
#include "lib/jxl/color_encoding_internal.h"
#include "lib/jxl/common.h" // JXL_HIGH_PRECISION
-#include "lib/jxl/enc_butteraugli_comparator.h"
#include "lib/jxl/enc_params.h"
#include "lib/jxl/fake_parallel_runner_testonly.h"
#include "lib/jxl/image.h"
@@ -72,7 +71,7 @@ TEST(JxlTest, RoundtripSinglePixelWithAlpha) {
TestImage t;
t.SetDimensions(1, 1).SetChannels(4).AddFrame().ZeroFill();
PackedPixelFile ppf_out;
- EXPECT_EQ(Roundtrip(t.ppf(), {}, {}, nullptr, &ppf_out), 59);
+ EXPECT_EQ(Roundtrip(t.ppf(), {}, {}, nullptr, &ppf_out), 58);
}
// Changing serialized signature causes Decode to fail.
@@ -87,7 +86,7 @@ TEST(JxlTest, RoundtripMarker) {
compressed[i] ^= 0xFF;
PackedPixelFile ppf_out;
EXPECT_FALSE(extras::DecodeImageJXL(compressed.data(), compressed.size(),
- {}, /*decodec_bytes=*/nullptr,
+ {}, /* decoded_bytes */ nullptr,
&ppf_out));
}
}
@@ -121,7 +120,7 @@ TEST(JxlTest, RoundtripSmallD1) {
{
PackedPixelFile ppf_out;
EXPECT_NEAR(Roundtrip(t.ppf(), {}, {}, pool, &ppf_out), 916, 40);
- EXPECT_THAT(ButteraugliDistance(t.ppf(), ppf_out), IsSlightlyBelow(0.888));
+ EXPECT_SLIGHTLY_BELOW(ButteraugliDistance(t.ppf(), ppf_out), 0.888);
}
// With a lower intensity target than the default, the bitrate should be
@@ -131,7 +130,7 @@ TEST(JxlTest, RoundtripSmallD1) {
{
PackedPixelFile ppf_out;
EXPECT_NEAR(Roundtrip(t.ppf(), {}, {}, pool, &ppf_out), 745, 20);
- EXPECT_THAT(ButteraugliDistance(t.ppf(), ppf_out), IsSlightlyBelow(1.3));
+ EXPECT_SLIGHTLY_BELOW(ButteraugliDistance(t.ppf(), ppf_out), 1.3);
EXPECT_EQ(ppf_out.info.intensity_target, t.ppf().info.intensity_target);
}
}
@@ -147,8 +146,8 @@ TEST(JxlTest, RoundtripResample2) {
cparams.AddOption(JXL_ENC_FRAME_SETTING_EFFORT, 3); // kFalcon
PackedPixelFile ppf_out;
- EXPECT_NEAR(Roundtrip(t.ppf(), cparams, {}, pool, &ppf_out), 18500, 200);
- EXPECT_THAT(ComputeDistance2(t.ppf(), ppf_out), IsSlightlyBelow(90));
+ EXPECT_NEAR(Roundtrip(t.ppf(), cparams, {}, pool, &ppf_out), 17300, 500);
+ EXPECT_SLIGHTLY_BELOW(ComputeDistance2(t.ppf(), ppf_out), 90);
}
TEST(JxlTest, RoundtripResample2Slow) {
@@ -165,7 +164,7 @@ TEST(JxlTest, RoundtripResample2Slow) {
PackedPixelFile ppf_out;
EXPECT_NEAR(Roundtrip(t.ppf(), cparams, {}, pool, &ppf_out), 3888, 200);
- EXPECT_THAT(ComputeDistance2(t.ppf(), ppf_out), IsSlightlyBelow(250));
+ EXPECT_SLIGHTLY_BELOW(ComputeDistance2(t.ppf(), ppf_out), 250);
}
TEST(JxlTest, RoundtripResample2MT) {
@@ -180,8 +179,8 @@ TEST(JxlTest, RoundtripResample2MT) {
cparams.AddOption(JXL_ENC_FRAME_SETTING_EFFORT, 3); // kFalcon
PackedPixelFile ppf_out;
- EXPECT_NEAR(Roundtrip(t.ppf(), cparams, {}, &pool, &ppf_out), 223310, 2000);
- EXPECT_THAT(ComputeDistance2(t.ppf(), ppf_out), IsSlightlyBelow(340));
+ EXPECT_NEAR(Roundtrip(t.ppf(), cparams, {}, &pool, &ppf_out), 203300, 2000);
+ EXPECT_SLIGHTLY_BELOW(ComputeDistance2(t.ppf(), ppf_out), 340);
}
// Roundtrip the image using a parallel runner that executes single-threaded but
@@ -237,7 +236,7 @@ TEST(JxlTest, RoundtripResample4) {
PackedPixelFile ppf_out;
EXPECT_NEAR(Roundtrip(t.ppf(), cparams, {}, pool, &ppf_out), 5758, 100);
- EXPECT_THAT(ButteraugliDistance(t.ppf(), ppf_out), IsSlightlyBelow(22));
+ EXPECT_SLIGHTLY_BELOW(ButteraugliDistance(t.ppf(), ppf_out), 22);
}
TEST(JxlTest, RoundtripResample8) {
@@ -252,7 +251,7 @@ TEST(JxlTest, RoundtripResample8) {
PackedPixelFile ppf_out;
EXPECT_NEAR(Roundtrip(t.ppf(), cparams, {}, pool, &ppf_out), 2036, 50);
- EXPECT_THAT(ButteraugliDistance(t.ppf(), ppf_out), IsSlightlyBelow(50));
+ EXPECT_SLIGHTLY_BELOW(ButteraugliDistance(t.ppf(), ppf_out), 50);
}
TEST(JxlTest, RoundtripUnalignedD2) {
@@ -270,7 +269,7 @@ TEST(JxlTest, RoundtripUnalignedD2) {
PackedPixelFile ppf_out;
EXPECT_NEAR(Roundtrip(t.ppf(), cparams, {}, pool, &ppf_out), 506, 30);
- EXPECT_THAT(ButteraugliDistance(t.ppf(), ppf_out), IsSlightlyBelow(1.72));
+ EXPECT_SLIGHTLY_BELOW(ButteraugliDistance(t.ppf(), ppf_out), 1.72);
}
TEST(JxlTest, RoundtripMultiGroup) {
@@ -289,8 +288,8 @@ TEST(JxlTest, RoundtripMultiGroup) {
PackedPixelFile ppf_out;
EXPECT_NEAR(Roundtrip(t.ppf(), cparams, {}, &pool, &ppf_out), expected_size,
700);
- EXPECT_THAT(ComputeDistance2(t.ppf(), ppf_out),
- IsSlightlyBelow(expected_distance));
+ EXPECT_SLIGHTLY_BELOW(ComputeDistance2(t.ppf(), ppf_out),
+ expected_distance);
};
auto run_kitten = std::async(std::launch::async, test, SpeedTier::kKitten,
@@ -339,10 +338,11 @@ TEST(JxlTest, RoundtripRGBToGrayscale) {
ASSERT_TRUE(io.frames[0].TransformTo(srgb_gamma, *JxlGetDefaultCms()));
io.metadata.m.color_encoding = io2.Main().c_current();
io.Main().OverrideProfile(io2.Main().c_current());
- EXPECT_THAT(ButteraugliDistance(io.frames, io2.frames, ButteraugliParams(),
- *JxlGetDefaultCms(),
- /*distmap=*/nullptr, &pool),
- IsSlightlyBelow(1.36));
+ EXPECT_SLIGHTLY_BELOW(
+ ButteraugliDistance(io.frames, io2.frames, ButteraugliParams(),
+ *JxlGetDefaultCms(),
+ /*distmap=*/nullptr, &pool),
+ 1.4);
}
TEST(JxlTest, RoundtripLargeFast) {
@@ -355,8 +355,57 @@ TEST(JxlTest, RoundtripLargeFast) {
cparams.AddOption(JXL_ENC_FRAME_SETTING_EFFORT, 7); // kSquirrel
PackedPixelFile ppf_out;
- EXPECT_NEAR(Roundtrip(t.ppf(), cparams, {}, &pool, &ppf_out), 492867, 5000);
- EXPECT_THAT(ComputeDistance2(t.ppf(), ppf_out), IsSlightlyBelow(78));
+ EXPECT_NEAR(Roundtrip(t.ppf(), cparams, {}, &pool, &ppf_out), 503000, 12000);
+ EXPECT_SLIGHTLY_BELOW(ComputeDistance2(t.ppf(), ppf_out), 78);
+}
+
+TEST(JxlTest, JXL_X86_64_TEST(RoundtripLargeEmptyModular)) {
+ ThreadPoolForTests pool(8);
+ TestImage t;
+ t.SetDimensions(4096, 4096).SetDataType(JXL_TYPE_UINT8).SetChannels(4);
+ TestImage::Frame frame = t.AddFrame();
+ frame.ZeroFill();
+ for (size_t c = 0; c < 4; ++c) {
+ for (size_t y = 0; y < 1024; y += (c + 1)) {
+ for (size_t x = 0; x < 1024; x += ((y % 4) + 3)) {
+ frame.SetValue(y, x, c, 0.88);
+ }
+ }
+ }
+
+ JXLCompressParams cparams;
+ cparams.AddOption(JXL_ENC_FRAME_SETTING_EFFORT, 1);
+ cparams.AddOption(JXL_ENC_FRAME_SETTING_MODULAR, 1);
+ cparams.AddOption(JXL_ENC_FRAME_SETTING_RESPONSIVE, 1);
+ cparams.AddOption(JXL_ENC_FRAME_SETTING_DECODING_SPEED, 2);
+
+ PackedPixelFile ppf_out;
+ EXPECT_NEAR(Roundtrip(t.ppf(), cparams, {}, &pool, &ppf_out), 3474795,
+ 100000);
+ EXPECT_SLIGHTLY_BELOW(ComputeDistance2(t.ppf(), ppf_out),
+#if JXL_HIGH_PRECISION
+ 2050
+#else
+ 12100
+#endif
+ );
+}
+
+TEST(JxlTest, RoundtripOutputColorSpace) {
+ ThreadPoolForTests pool(8);
+ const std::vector<uint8_t> orig = ReadTestData("jxl/flower/flower.png");
+ TestImage t;
+ t.DecodeFromBytes(orig).ClearMetadata();
+
+ JXLCompressParams cparams;
+ cparams.AddOption(JXL_ENC_FRAME_SETTING_EFFORT, 7); // kSquirrel
+
+ JXLDecompressParams dparams;
+ dparams.color_space = "RGB_D65_DCI_Rel_709";
+ PackedPixelFile ppf_out;
+ EXPECT_NEAR(Roundtrip(t.ppf(), cparams, dparams, &pool, &ppf_out), 503000,
+ 12000);
+ EXPECT_SLIGHTLY_BELOW(ComputeDistance2(t.ppf(), ppf_out), 78);
}
TEST(JxlTest, RoundtripDotsForceEpf) {
@@ -373,7 +422,7 @@ TEST(JxlTest, RoundtripDotsForceEpf) {
PackedPixelFile ppf_out;
EXPECT_NEAR(Roundtrip(t.ppf(), cparams, {}, &pool, &ppf_out), 41355, 300);
- EXPECT_THAT(ComputeDistance2(t.ppf(), ppf_out), IsSlightlyBelow(18));
+ EXPECT_SLIGHTLY_BELOW(ComputeDistance2(t.ppf(), ppf_out), 18);
}
// Checks for differing size/distance in two consecutive runs of distance 2,
@@ -453,7 +502,7 @@ TEST(JxlTest, RoundtripSmallNL) {
PackedPixelFile ppf_out;
EXPECT_NEAR(Roundtrip(t.ppf(), {}, {}, pool, &ppf_out), 916, 45);
- EXPECT_THAT(ButteraugliDistance(t.ppf(), ppf_out), IsSlightlyBelow(0.82));
+ EXPECT_SLIGHTLY_BELOW(ButteraugliDistance(t.ppf(), ppf_out), 0.82);
}
TEST(JxlTest, RoundtripNoGaborishNoAR) {
@@ -469,7 +518,7 @@ TEST(JxlTest, RoundtripNoGaborishNoAR) {
PackedPixelFile ppf_out;
EXPECT_NEAR(Roundtrip(t.ppf(), cparams, {}, pool, &ppf_out), 41142, 400);
- EXPECT_THAT(ButteraugliDistance(t.ppf(), ppf_out), IsSlightlyBelow(1.8));
+ EXPECT_SLIGHTLY_BELOW(ButteraugliDistance(t.ppf(), ppf_out), 1.8);
}
TEST(JxlTest, RoundtripSmallNoGaborish) {
@@ -487,7 +536,7 @@ TEST(JxlTest, RoundtripSmallNoGaborish) {
PackedPixelFile ppf_out;
EXPECT_NEAR(Roundtrip(t.ppf(), cparams, {}, pool, &ppf_out), 1006, 20);
- EXPECT_THAT(ButteraugliDistance(t.ppf(), ppf_out), IsSlightlyBelow(1.1));
+ EXPECT_SLIGHTLY_BELOW(ButteraugliDistance(t.ppf(), ppf_out), 1.1);
}
TEST(JxlTest, RoundtripSmallPatchesAlpha) {
@@ -513,7 +562,7 @@ TEST(JxlTest, RoundtripSmallPatchesAlpha) {
PackedPixelFile ppf_out;
EXPECT_NEAR(Roundtrip(t.ppf(), cparams, {}, pool, &ppf_out), 597, 100);
- EXPECT_THAT(ButteraugliDistance(t.ppf(), ppf_out), IsSlightlyBelow(0.018f));
+ EXPECT_SLIGHTLY_BELOW(ButteraugliDistance(t.ppf(), ppf_out), 0.018f);
}
TEST(JxlTest, RoundtripSmallPatches) {
@@ -538,17 +587,17 @@ TEST(JxlTest, RoundtripSmallPatches) {
PackedPixelFile ppf_out;
EXPECT_NEAR(Roundtrip(t.ppf(), cparams, {}, pool, &ppf_out), 486, 100);
- EXPECT_THAT(ButteraugliDistance(t.ppf(), ppf_out), IsSlightlyBelow(0.018f));
+ EXPECT_SLIGHTLY_BELOW(ButteraugliDistance(t.ppf(), ppf_out), 0.018f);
}
// TODO(szabadka) Add encoder and decoder API functions that accept frame
// buffers in arbitrary unsigned and floating point formats, and then roundtrip
// test the lossless codepath to make sure the exact binary representations
// are preserved.
-#if 0
+#if JXL_FALSE
TEST(JxlTest, RoundtripImageBundleOriginalBits) {
// Image does not matter, only io.metadata.m and io2.metadata.m are tested.
- Image3F image(1, 1);
+ JXL_ASSIGN_OR_DIE(Image3F image, Image3F::Create(1, 1));
ZeroFillImage(&image);
CodecInOut io;
io.metadata.m.color_encoding = ColorEncoding::LinearSRGB();
@@ -639,10 +688,11 @@ TEST(JxlTest, RoundtripGrayscale) {
EXPECT_TRUE(io2.Main().IsGray());
EXPECT_LE(compressed.size(), 7000u);
- EXPECT_THAT(ButteraugliDistance(io.frames, io2.frames, ButteraugliParams(),
- *JxlGetDefaultCms(),
- /*distmap=*/nullptr),
- IsSlightlyBelow(1.6));
+ EXPECT_SLIGHTLY_BELOW(
+ ButteraugliDistance(io.frames, io2.frames, ButteraugliParams(),
+ *JxlGetDefaultCms(),
+ /*distmap=*/nullptr),
+ 1.6);
}
// Test with larger butteraugli distance and other settings enabled so
@@ -658,10 +708,11 @@ TEST(JxlTest, RoundtripGrayscale) {
EXPECT_TRUE(io2.Main().IsGray());
EXPECT_LE(compressed.size(), 1300u);
- EXPECT_THAT(ButteraugliDistance(io.frames, io2.frames, ButteraugliParams(),
- *JxlGetDefaultCms(),
- /*distmap=*/nullptr),
- IsSlightlyBelow(6.7));
+ EXPECT_SLIGHTLY_BELOW(
+ ButteraugliDistance(io.frames, io2.frames, ButteraugliParams(),
+ *JxlGetDefaultCms(),
+ /*distmap=*/nullptr),
+ 6.7);
}
{
@@ -678,10 +729,11 @@ TEST(JxlTest, RoundtripGrayscale) {
EXPECT_FALSE(io2.Main().IsGray());
EXPECT_LE(compressed.size(), 7000u);
- EXPECT_THAT(ButteraugliDistance(io.frames, io2.frames, ButteraugliParams(),
- *JxlGetDefaultCms(),
- /*distmap=*/nullptr),
- IsSlightlyBelow(1.6));
+ EXPECT_SLIGHTLY_BELOW(
+ ButteraugliDistance(io.frames, io2.frames, ButteraugliParams(),
+ *JxlGetDefaultCms(),
+ /*distmap=*/nullptr),
+ 1.6);
}
}
@@ -706,7 +758,7 @@ TEST(JxlTest, RoundtripAlpha) {
std::vector<uint8_t> compressed;
EXPECT_TRUE(test::EncodeFile(cparams, &io, &compressed));
- EXPECT_LE(compressed.size(), 10077u);
+ EXPECT_LE(compressed.size(), 20000u);
for (bool use_image_callback : {false, true}) {
for (bool unpremul_alpha : {false, true}) {
@@ -715,10 +767,11 @@ TEST(JxlTest, RoundtripAlpha) {
dparams.use_image_callback = use_image_callback;
dparams.unpremultiply_alpha = unpremul_alpha;
EXPECT_TRUE(test::DecodeFile(dparams, Bytes(compressed), &io2));
- EXPECT_THAT(ButteraugliDistance(io.frames, io2.frames,
- ButteraugliParams(), *JxlGetDefaultCms(),
- /*distmap=*/nullptr),
- IsSlightlyBelow(1.15));
+ EXPECT_SLIGHTLY_BELOW(
+ ButteraugliDistance(io.frames, io2.frames, ButteraugliParams(),
+ *JxlGetDefaultCms(),
+ /*distmap=*/nullptr),
+ 1.15);
}
}
}
@@ -781,7 +834,8 @@ bool UnpremultiplyAlpha(CodecInOut& io) {
TEST(JxlTest, RoundtripAlphaPremultiplied) {
const std::vector<uint8_t> orig =
ReadTestData("external/wesaturate/500px/tmshre_riaphotographs_alpha.png");
- CodecInOut io, io_nopremul;
+ CodecInOut io;
+ CodecInOut io_nopremul;
ASSERT_TRUE(SetFromBytes(Bytes(orig), &io));
ASSERT_TRUE(SetFromBytes(Bytes(orig), &io_nopremul));
@@ -803,7 +857,7 @@ TEST(JxlTest, RoundtripAlphaPremultiplied) {
std::vector<uint8_t> compressed;
EXPECT_TRUE(test::EncodeFile(cparams, &io, &compressed));
- EXPECT_LE(compressed.size(), 10000u);
+ EXPECT_LE(compressed.size(), 18000u);
for (bool use_image_callback : {false, true}) {
for (bool unpremul_alpha : {false, true}) {
@@ -826,19 +880,19 @@ TEST(JxlTest, RoundtripAlphaPremultiplied) {
EXPECT_EQ(unpremul_alpha, !io2.Main().AlphaIsPremultiplied());
if (!unpremul_alpha) {
- EXPECT_THAT(
+ EXPECT_SLIGHTLY_BELOW(
ButteraugliDistance(io.frames, io2.frames, ButteraugliParams(),
*JxlGetDefaultCms(),
/*distmap=*/nullptr),
- IsSlightlyBelow(1.111));
+ 1.111);
EXPECT_TRUE(UnpremultiplyAlpha(io2));
EXPECT_FALSE(io2.Main().AlphaIsPremultiplied());
}
- EXPECT_THAT(
+ EXPECT_SLIGHTLY_BELOW(
ButteraugliDistance(io_nopremul.frames, io2.frames,
ButteraugliParams(), *JxlGetDefaultCms(),
/*distmap=*/nullptr),
- IsSlightlyBelow(1.55));
+ 1.0);
}
}
}
@@ -854,13 +908,14 @@ TEST(JxlTest, RoundtripAlphaResampling) {
ASSERT_TRUE(t.ppf().info.alpha_bits > 0);
JXLCompressParams cparams;
+ cparams.alpha_distance = 1.0;
cparams.AddOption(JXL_ENC_FRAME_SETTING_EFFORT, 5); // kHare
cparams.AddOption(JXL_ENC_FRAME_SETTING_RESAMPLING, 2);
cparams.AddOption(JXL_ENC_FRAME_SETTING_EXTRA_CHANNEL_RESAMPLING, 2);
PackedPixelFile ppf_out;
EXPECT_NEAR(Roundtrip(t.ppf(), cparams, {}, pool, &ppf_out), 13507, 130);
- EXPECT_THAT(ButteraugliDistance(t.ppf(), ppf_out), IsSlightlyBelow(5.2));
+ EXPECT_SLIGHTLY_BELOW(ButteraugliDistance(t.ppf(), ppf_out), 5.2);
}
TEST(JxlTest, RoundtripAlphaResamplingOnlyAlpha) {
@@ -873,12 +928,13 @@ TEST(JxlTest, RoundtripAlphaResamplingOnlyAlpha) {
ASSERT_TRUE(t.ppf().info.alpha_bits > 0);
JXLCompressParams cparams;
+ cparams.alpha_distance = 1.0;
cparams.AddOption(JXL_ENC_FRAME_SETTING_EFFORT, 3); // kFalcon
cparams.AddOption(JXL_ENC_FRAME_SETTING_EXTRA_CHANNEL_RESAMPLING, 2);
PackedPixelFile ppf_out;
- EXPECT_NEAR(Roundtrip(t.ppf(), cparams, {}, pool, &ppf_out), 33571, 400);
- EXPECT_THAT(ButteraugliDistance(t.ppf(), ppf_out), IsSlightlyBelow(1.49));
+ EXPECT_NEAR(Roundtrip(t.ppf(), cparams, {}, pool, &ppf_out), 32000, 1000);
+ EXPECT_SLIGHTLY_BELOW(ButteraugliDistance(t.ppf(), ppf_out), 1.52);
}
TEST(JxlTest, RoundtripAlphaNonMultipleOf8) {
@@ -893,13 +949,14 @@ TEST(JxlTest, RoundtripAlphaNonMultipleOf8) {
PackedPixelFile ppf_out;
EXPECT_NEAR(Roundtrip(t.ppf(), {}, {}, pool, &ppf_out), 107, 10);
- EXPECT_THAT(ButteraugliDistance(t.ppf(), ppf_out), IsSlightlyBelow(0.95));
+ EXPECT_SLIGHTLY_BELOW(ButteraugliDistance(t.ppf(), ppf_out), 0.006);
}
TEST(JxlTest, RoundtripAlpha16) {
ThreadPoolForTests pool(4);
// The image is wider than 512 pixels to ensure multiple groups are tested.
- size_t xsize = 1200, ysize = 160;
+ size_t xsize = 1200;
+ size_t ysize = 160;
TestImage t;
t.SetDimensions(xsize, ysize).SetChannels(4).SetAllBitDepths(16);
TestImage::Frame frame = t.AddFrame();
@@ -923,12 +980,13 @@ TEST(JxlTest, RoundtripAlpha16) {
JXLCompressParams cparams;
cparams.AddOption(JXL_ENC_FRAME_SETTING_EFFORT, 6); // kWombat
cparams.distance = 0.5;
+ cparams.alpha_distance = 0.5;
PackedPixelFile ppf_out;
// TODO(szabadka) Investigate big size difference on i686
// This still keeps happening (2023-04-18).
EXPECT_NEAR(Roundtrip(t.ppf(), cparams, {}, &pool, &ppf_out), 3666, 120);
- EXPECT_THAT(ButteraugliDistance(t.ppf(), ppf_out), IsSlightlyBelow(0.65));
+ EXPECT_SLIGHTLY_BELOW(ButteraugliDistance(t.ppf(), ppf_out), 0.65);
}
namespace {
@@ -990,8 +1048,8 @@ TEST(JxlTest, JXL_SLOW_TEST(RoundtripLossless8LightningGradient)) {
PackedPixelFile ppf_out;
// Lax comparison because different SIMD will cause different compression.
- EXPECT_THAT(Roundtrip(t.ppf(), cparams, dparams, &pool, &ppf_out),
- IsSlightlyBelow(286848u));
+ EXPECT_SLIGHTLY_BELOW(Roundtrip(t.ppf(), cparams, dparams, &pool, &ppf_out),
+ 286848u);
EXPECT_EQ(ComputeDistance2(t.ppf(), ppf_out), 0.0);
}
@@ -1035,7 +1093,8 @@ TEST(JxlTest, RoundtripLossless8Alpha) {
TEST(JxlTest, RoundtripLossless16Alpha) {
ThreadPool* pool = nullptr;
- size_t xsize = 1200, ysize = 160;
+ size_t xsize = 1200;
+ size_t ysize = 160;
TestImage t;
t.SetDimensions(xsize, ysize).SetChannels(4).SetAllBitDepths(16);
TestImage::Frame frame = t.AddFrame();
@@ -1062,7 +1121,7 @@ TEST(JxlTest, RoundtripLossless16Alpha) {
PackedPixelFile ppf_out;
// TODO(szabadka) Investigate big size difference on i686
- EXPECT_NEAR(Roundtrip(t.ppf(), cparams, dparams, pool, &ppf_out), 4884, 100);
+ EXPECT_NEAR(Roundtrip(t.ppf(), cparams, dparams, pool, &ppf_out), 4665, 100);
EXPECT_EQ(ComputeDistance2(t.ppf(), ppf_out), 0.0);
EXPECT_EQ(ppf_out.info.alpha_bits, 16);
EXPECT_TRUE(test::SameAlpha(t.ppf(), ppf_out));
@@ -1070,7 +1129,8 @@ TEST(JxlTest, RoundtripLossless16Alpha) {
TEST(JxlTest, RoundtripLossless16AlphaNotMisdetectedAs8Bit) {
ThreadPool* pool = nullptr;
- size_t xsize = 128, ysize = 128;
+ size_t xsize = 128;
+ size_t ysize = 128;
TestImage t;
t.SetDimensions(xsize, ysize).SetChannels(4).SetAllBitDepths(16);
TestImage::Frame frame = t.AddFrame();
@@ -1098,7 +1158,7 @@ TEST(JxlTest, RoundtripLossless16AlphaNotMisdetectedAs8Bit) {
dparams.accepted_formats.push_back(t.ppf().frames[0].color.format);
PackedPixelFile ppf_out;
- EXPECT_NEAR(Roundtrip(t.ppf(), cparams, dparams, pool, &ppf_out), 591, 50);
+ EXPECT_NEAR(Roundtrip(t.ppf(), cparams, dparams, pool, &ppf_out), 280, 50);
EXPECT_EQ(ComputeDistance2(t.ppf(), ppf_out), 0.0);
EXPECT_EQ(ppf_out.info.bits_per_sample, 16);
EXPECT_EQ(ppf_out.info.alpha_bits, 16);
@@ -1123,7 +1183,7 @@ TEST(JxlTest, RoundtripDots) {
PackedPixelFile ppf_out;
EXPECT_NEAR(Roundtrip(t.ppf(), cparams, {}, pool, &ppf_out), 280333, 4000);
- EXPECT_THAT(ButteraugliDistance(t.ppf(), ppf_out), IsSlightlyBelow(0.35));
+ EXPECT_SLIGHTLY_BELOW(ButteraugliDistance(t.ppf(), ppf_out), 0.35);
}
TEST(JxlTest, RoundtripNoise) {
@@ -1143,7 +1203,7 @@ TEST(JxlTest, RoundtripNoise) {
PackedPixelFile ppf_out;
EXPECT_NEAR(Roundtrip(t.ppf(), cparams, {}, pool, &ppf_out), 41009, 750);
- EXPECT_THAT(ButteraugliDistance(t.ppf(), ppf_out), IsSlightlyBelow(1.42));
+ EXPECT_SLIGHTLY_BELOW(ButteraugliDistance(t.ppf(), ppf_out), 1.42);
}
TEST(JxlTest, RoundtripLossless8Gray) {
@@ -1182,17 +1242,17 @@ TEST(JxlTest, RoundtripAnimation) {
dparams.accepted_formats.push_back(t.ppf().frames[0].color.format);
PackedPixelFile ppf_out;
- EXPECT_THAT(Roundtrip(t.ppf(), {}, dparams, pool, &ppf_out),
- IsSlightlyBelow(2888));
+ EXPECT_SLIGHTLY_BELOW(Roundtrip(t.ppf(), {}, dparams, pool, &ppf_out), 3350);
t.CoalesceGIFAnimationWithAlpha();
ASSERT_EQ(ppf_out.frames.size(), t.ppf().frames.size());
- EXPECT_LE(ButteraugliDistance(t.ppf(), ppf_out),
+ static constexpr double kMaxButteraugli =
#if JXL_HIGH_PRECISION
- 1.55);
+ 1.55;
#else
- 1.75);
+ 1.75;
#endif
+ EXPECT_LE(ButteraugliDistance(t.ppf(), ppf_out), kMaxButteraugli);
}
TEST(JxlTest, RoundtripLosslessAnimation) {
@@ -1212,8 +1272,8 @@ TEST(JxlTest, RoundtripLosslessAnimation) {
dparams.accepted_formats.push_back(t.ppf().frames[0].color.format);
PackedPixelFile ppf_out;
- EXPECT_THAT(Roundtrip(t.ppf(), cparams, dparams, pool, &ppf_out),
- IsSlightlyBelow(958));
+ EXPECT_SLIGHTLY_BELOW(Roundtrip(t.ppf(), cparams, dparams, pool, &ppf_out),
+ 958);
t.CoalesceGIFAnimationWithAlpha();
ASSERT_EQ(ppf_out.frames.size(), t.ppf().frames.size());
@@ -1240,11 +1300,11 @@ TEST(JxlTest, RoundtripAnimationPatches) {
PackedPixelFile ppf_out;
// 40k with no patches, 27k with patch frames encoded multiple times.
- EXPECT_THAT(Roundtrip(t.ppf(), cparams, dparams, pool, &ppf_out),
- IsSlightlyBelow(19300));
+ EXPECT_SLIGHTLY_BELOW(Roundtrip(t.ppf(), cparams, dparams, pool, &ppf_out),
+ 19300);
EXPECT_EQ(ppf_out.frames.size(), t.ppf().frames.size());
// >10 with broken patches; not all patches are detected on borders.
- EXPECT_THAT(ButteraugliDistance(t.ppf(), ppf_out), IsSlightlyBelow(1.9));
+ EXPECT_SLIGHTLY_BELOW(ButteraugliDistance(t.ppf(), ppf_out), 1.9);
}
size_t RoundtripJpeg(const std::vector<uint8_t>& jpeg_in, ThreadPool* pool) {
@@ -1293,7 +1353,7 @@ TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompression444)) {
const std::vector<uint8_t> orig =
ReadTestData("jxl/flower/flower.png.im_q85_444.jpg");
// JPEG size is 696,659 bytes.
- EXPECT_NEAR(RoundtripJpeg(orig, &pool), 568940u, 20);
+ EXPECT_NEAR(RoundtripJpeg(orig, &pool), 568891u, 20);
}
TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompressionToPixels)) {
@@ -1306,7 +1366,7 @@ TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompressionToPixels)) {
PackedPixelFile ppf_out;
RoundtripJpegToPixels(orig, {}, &pool, &ppf_out);
- EXPECT_THAT(ComputeDistance2(t.ppf(), ppf_out), IsSlightlyBelow(12));
+ EXPECT_SLIGHTLY_BELOW(ComputeDistance2(t.ppf(), ppf_out), 12);
}
TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompressionToPixels420)) {
@@ -1319,7 +1379,7 @@ TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompressionToPixels420)) {
PackedPixelFile ppf_out;
RoundtripJpegToPixels(orig, {}, &pool, &ppf_out);
- EXPECT_THAT(ComputeDistance2(t.ppf(), ppf_out), IsSlightlyBelow(11));
+ EXPECT_SLIGHTLY_BELOW(ComputeDistance2(t.ppf(), ppf_out), 11);
}
TEST(JxlTest,
@@ -1336,7 +1396,7 @@ TEST(JxlTest,
PackedPixelFile ppf_out;
RoundtripJpegToPixels(orig, dparams, &pool, &ppf_out);
- EXPECT_THAT(ComputeDistance2(t.ppf(), ppf_out), IsSlightlyBelow(4410));
+ EXPECT_SLIGHTLY_BELOW(ComputeDistance2(t.ppf(), ppf_out), 4410);
}
TEST(JxlTest,
@@ -1350,7 +1410,7 @@ TEST(JxlTest,
PackedPixelFile ppf_out;
RoundtripJpegToPixels(orig, {}, &pool, &ppf_out);
- EXPECT_THAT(ComputeDistance2(t.ppf(), ppf_out), IsSlightlyBelow(4));
+ EXPECT_SLIGHTLY_BELOW(ComputeDistance2(t.ppf(), ppf_out), 4);
}
TEST(JxlTest,
@@ -1364,7 +1424,7 @@ TEST(JxlTest,
PackedPixelFile ppf_out;
RoundtripJpegToPixels(orig, {}, &pool, &ppf_out);
- EXPECT_THAT(ComputeDistance2(t.ppf(), ppf_out), IsSlightlyBelow(10));
+ EXPECT_SLIGHTLY_BELOW(ComputeDistance2(t.ppf(), ppf_out), 10);
}
TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompressionGray)) {
@@ -1380,7 +1440,7 @@ TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompression420)) {
const std::vector<uint8_t> orig =
ReadTestData("jxl/flower/flower.png.im_q85_420.jpg");
// JPEG size is 546,797 bytes.
- EXPECT_NEAR(RoundtripJpeg(orig, &pool), 455560u, 10);
+ EXPECT_NEAR(RoundtripJpeg(orig, &pool), 455510u, 20);
}
TEST(JxlTest,
@@ -1389,7 +1449,7 @@ TEST(JxlTest,
const std::vector<uint8_t> orig =
ReadTestData("jxl/flower/flower.png.im_q85_luma_subsample.jpg");
// JPEG size is 400,724 bytes.
- EXPECT_NEAR(RoundtripJpeg(orig, &pool), 325354u, 15);
+ EXPECT_NEAR(RoundtripJpeg(orig, &pool), 325310u, 20);
}
TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompression444_12)) {
@@ -1398,7 +1458,7 @@ TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompression444_12)) {
const std::vector<uint8_t> orig =
ReadTestData("jxl/flower/flower.png.im_q85_444_1x2.jpg");
// JPEG size is 703,874 bytes.
- EXPECT_NEAR(RoundtripJpeg(orig, &pool), 569679u, 10);
+ EXPECT_NEAR(RoundtripJpeg(orig, &pool), 569630u, 20);
}
TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompression422)) {
@@ -1406,7 +1466,7 @@ TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompression422)) {
const std::vector<uint8_t> orig =
ReadTestData("jxl/flower/flower.png.im_q85_422.jpg");
// JPEG size is 522,057 bytes.
- EXPECT_NEAR(RoundtripJpeg(orig, &pool), 499282u, 10);
+ EXPECT_NEAR(RoundtripJpeg(orig, &pool), 499236u, 20);
}
TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompression440)) {
@@ -1414,7 +1474,7 @@ TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompression440)) {
const std::vector<uint8_t> orig =
ReadTestData("jxl/flower/flower.png.im_q85_440.jpg");
// JPEG size is 603,623 bytes.
- EXPECT_NEAR(RoundtripJpeg(orig, &pool), 501151u, 10);
+ EXPECT_NEAR(RoundtripJpeg(orig, &pool), 501101u, 20);
}
TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompression_asymmetric)) {
@@ -1424,7 +1484,7 @@ TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompression_asymmetric)) {
const std::vector<uint8_t> orig =
ReadTestData("jxl/flower/flower.png.im_q85_asymmetric.jpg");
// JPEG size is 604,601 bytes.
- EXPECT_NEAR(RoundtripJpeg(orig, &pool), 500602u, 10);
+ EXPECT_NEAR(RoundtripJpeg(orig, &pool), 500548u, 20);
}
TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompression420Progr)) {
@@ -1432,7 +1492,7 @@ TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompression420Progr)) {
const std::vector<uint8_t> orig =
ReadTestData("jxl/flower/flower.png.im_q85_420_progr.jpg");
// JPEG size is 522,057 bytes.
- EXPECT_NEAR(RoundtripJpeg(orig, &pool), 455499u, 10);
+ EXPECT_NEAR(RoundtripJpeg(orig, &pool), 455454u, 20);
}
TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompressionMetadata)) {
@@ -1440,7 +1500,8 @@ TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompressionMetadata)) {
const std::vector<uint8_t> orig =
ReadTestData("jxl/jpeg_reconstruction/1x1_exif_xmp.jpg");
// JPEG size is 4290 bytes
- EXPECT_NEAR(RoundtripJpeg(orig, &pool), 1400u, 30);
+ // 1370 on 386, so higher margin.
+ EXPECT_NEAR(RoundtripJpeg(orig, &pool), 1334u, 100);
}
TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompressionRestarts)) {
@@ -1448,7 +1509,7 @@ TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompressionRestarts)) {
const std::vector<uint8_t> orig =
ReadTestData("jxl/jpeg_reconstruction/bicycles_restarts.jpg");
// JPEG size is 87478 bytes
- EXPECT_NEAR(RoundtripJpeg(orig, &pool), 76125u, 30);
+ EXPECT_NEAR(RoundtripJpeg(orig, &pool), 76054u, 30);
}
TEST(JxlTest,
@@ -1475,7 +1536,7 @@ TEST(JxlTest, RoundtripProgressive) {
PackedPixelFile ppf_out;
EXPECT_NEAR(Roundtrip(t.ppf(), cparams, {}, &pool, &ppf_out), 70544, 750);
- EXPECT_THAT(ButteraugliDistance(t.ppf(), ppf_out), IsSlightlyBelow(1.4));
+ EXPECT_SLIGHTLY_BELOW(ButteraugliDistance(t.ppf(), ppf_out), 1.4);
}
TEST(JxlTest, RoundtripProgressiveLevel2Slow) {
@@ -1492,7 +1553,7 @@ TEST(JxlTest, RoundtripProgressiveLevel2Slow) {
PackedPixelFile ppf_out;
EXPECT_NEAR(Roundtrip(t.ppf(), cparams, {}, &pool, &ppf_out), 76666, 1000);
- EXPECT_THAT(ButteraugliDistance(t.ppf(), ppf_out), IsSlightlyBelow(1.17));
+ EXPECT_SLIGHTLY_BELOW(ButteraugliDistance(t.ppf(), ppf_out), 1.17);
}
TEST(JxlTest, RoundtripUnsignedCustomBitdepthLossless) {
@@ -1509,9 +1570,9 @@ TEST(JxlTest, RoundtripUnsignedCustomBitdepthLossless) {
t.SetAllBitDepths(bitdepth).SetEndianness(endianness);
TestImage::Frame frame = t.AddFrame();
frame.RandomFill();
+ t.ppf().input_bitdepth.type = JXL_BIT_DEPTH_FROM_CODESTREAM;
JXLCompressParams cparams = CompressParamsForLossless();
- cparams.input_bitdepth.type = JXL_BIT_DEPTH_FROM_CODESTREAM;
JXLDecompressParams dparams;
dparams.accepted_formats.push_back(t.ppf().frames[0].color.format);
@@ -1543,7 +1604,6 @@ TEST(JxlTest, LosslessPNMRoundtrip) {
JXLCompressParams cparams = CompressParamsForLossless();
cparams.AddOption(JXL_ENC_FRAME_SETTING_EFFORT, 1); // kLightning
- cparams.input_bitdepth.type = JXL_BIT_DEPTH_FROM_CODESTREAM;
JXLDecompressParams dparams;
dparams.accepted_formats.push_back(t.ppf().frames[0].color.format);
@@ -1592,10 +1652,13 @@ struct StreamingTestParam {
size_t xsize;
size_t ysize;
bool is_grey;
+ bool has_alpha;
int effort;
bool progressive;
- size_t num_channels() const { return is_grey ? 1 : 3; }
+ size_t num_channels() const {
+ return (is_grey ? 1 : 3) + (has_alpha ? 1 : 0);
+ }
float max_psnr() const { return is_grey ? 90 : 50; }
@@ -1603,11 +1666,13 @@ struct StreamingTestParam {
std::vector<StreamingTestParam> params;
for (int e : {1, 3, 4, 7}) {
for (bool g : {false, true}) {
- params.push_back(StreamingTestParam{357, 517, g, e, false});
- params.push_back(StreamingTestParam{2247, 2357, g, e, false});
+ params.push_back(StreamingTestParam{357, 517, g, false, e, false});
+ params.push_back(StreamingTestParam{2247, 2357, g, false, e, false});
}
}
- params.push_back(StreamingTestParam{2247, 2357, false, 1, true});
+ params.push_back(StreamingTestParam{2247, 2357, false, false, 1, true});
+ params.push_back(StreamingTestParam{2247, 2157, false, false, 5, false});
+ params.push_back(StreamingTestParam{2247, 2157, false, true, 5, false});
return params;
}
};
@@ -1651,15 +1716,66 @@ JXL_GTEST_INSTANTIATE_TEST_SUITE_P(
JxlStreamingTest, JxlStreamingTest,
testing::ValuesIn(StreamingTestParam::All()));
+struct StreamingEncodingTestParam {
+ std::string file;
+ int effort;
+ float distance;
+ int group_size;
+ float palette_percent;
+
+ static std::vector<StreamingEncodingTestParam> All() {
+ std::vector<StreamingEncodingTestParam> params;
+ for (const auto* file :
+ {"jxl/flower/flower.png", "jxl/flower/flower_alpha.png"}) {
+ for (int effort : {1, 3, 5, 6}) {
+ if (effort != 1) {
+ params.push_back(
+ StreamingEncodingTestParam{file, effort, 1.0, 1, -1});
+ params.push_back(
+ StreamingEncodingTestParam{file, effort, 4.0, 1, -1});
+ }
+ for (auto group_size : {-1, 0}) {
+ for (float palette_percent : {-1, 50, 100}) {
+ params.push_back(StreamingEncodingTestParam{
+ file, effort, 0.0, group_size, palette_percent});
+ }
+ }
+ }
+ }
+ return params;
+ }
+};
+
+std::ostream& operator<<(std::ostream& out,
+ const StreamingEncodingTestParam& p) {
+ out << p.file << "-";
+ out << "e" << p.effort;
+ if (p.distance == 0) {
+ out << "Lossless";
+ out << "G" << p.group_size << "P" << p.palette_percent;
+ } else {
+ out << "D" << p.distance;
+ }
+ return out;
+}
+
+class JxlStreamingEncodingTest
+ : public ::testing::TestWithParam<StreamingEncodingTestParam> {};
+
// This is broken on mingw32, so we only enable it for x86_64 now.
-TEST(JxlTest, JXL_X86_64_TEST(StreamingSamePixels)) {
- const std::vector<uint8_t> orig = ReadTestData("jxl/flower/flower.png");
+TEST_P(JxlStreamingEncodingTest, JXL_X86_64_TEST(StreamingSamePixels)) {
+ const auto param = GetParam();
+ const std::vector<uint8_t> orig = ReadTestData(param.file);
jxl::test::TestImage image;
image.DecodeFromBytes(orig);
+
JXLCompressParams cparams;
- cparams.distance = 1.0;
- cparams.AddOption(JXL_ENC_FRAME_SETTING_EFFORT, 6);
+ cparams.distance = param.distance;
+ cparams.AddOption(JXL_ENC_FRAME_SETTING_EFFORT, param.effort);
+ cparams.AddOption(JXL_ENC_FRAME_SETTING_MODULAR_GROUP_SIZE, param.group_size);
+ cparams.AddFloatOption(JXL_ENC_FRAME_SETTING_CHANNEL_COLORS_GROUP_PERCENT,
+ param.palette_percent);
cparams.AddOption(JXL_ENC_FRAME_SETTING_USE_FULL_IMAGE_HEURISTICS, 0);
ThreadPoolForTests pool(8);
@@ -1673,5 +1789,9 @@ TEST(JxlTest, JXL_X86_64_TEST(StreamingSamePixels)) {
EXPECT_TRUE(jxl::test::SamePixels(ppf_out, ppf_out_streaming));
}
+JXL_GTEST_INSTANTIATE_TEST_SUITE_P(
+ JxlStreamingTest, JxlStreamingEncodingTest,
+ testing::ValuesIn(StreamingEncodingTestParam::All()));
+
} // namespace
} // namespace jxl
diff --git a/third_party/jpeg-xl/lib/jxl/libjxl.pc.in b/third_party/jpeg-xl/lib/jxl/libjxl.pc.in
index 58b6941305..f5a9c43650 100644
--- a/third_party/jpeg-xl/lib/jxl/libjxl.pc.in
+++ b/third_party/jpeg-xl/lib/jxl/libjxl.pc.in
@@ -7,7 +7,7 @@ Name: libjxl
Description: Loads and saves JPEG XL files
Version: @JPEGXL_LIBRARY_VERSION@
@JPEGXL_REQUIRES_TYPE@: @JPEGXL_LIBRARY_REQUIRES@
-Libs: -L${libdir} -ljxl
-Libs.private: -lm
+Libs: -L${libdir} -ljxl @JPEGXL_PUBLIC_LIBS@
+Libs.private: @JPEGXL_PRIVATE_LIBS@
Cflags: -I${includedir}
Cflags.private: -DJXL_STATIC_DEFINE
diff --git a/third_party/jpeg-xl/lib/jxl/libjxl_cms.pc.in b/third_party/jpeg-xl/lib/jxl/libjxl_cms.pc.in
index 9aaa3f4dbe..deab7367ac 100644
--- a/third_party/jpeg-xl/lib/jxl/libjxl_cms.pc.in
+++ b/third_party/jpeg-xl/lib/jxl/libjxl_cms.pc.in
@@ -7,7 +7,7 @@ Name: libjxl_cms
Description: CMS support library for libjxl
Version: @JPEGXL_LIBRARY_VERSION@
@JPEGXL_REQUIRES_TYPE@: @JPEGXL_CMS_LIBRARY_REQUIRES@
-Libs: -L${libdir} -ljxl_cms
-Libs.private: -lm
+Libs: -L${libdir} -ljxl_cms @JPEGXL_CMS_PUBLIC_LIBS@
+Libs.private: @JPEGXL_CMS_PRIVATE_LIBS@
Cflags: -I${includedir}
Cflags.private: -DJXL_CMS_STATIC_DEFINE
diff --git a/third_party/jpeg-xl/lib/jxl/memory_manager_internal.h b/third_party/jpeg-xl/lib/jxl/memory_manager_internal.h
index c728d62e35..e578e91f8e 100644
--- a/third_party/jpeg-xl/lib/jxl/memory_manager_internal.h
+++ b/third_party/jpeg-xl/lib/jxl/memory_manager_internal.h
@@ -36,11 +36,13 @@ static JXL_INLINE Status MemoryManagerInit(
} else {
memset(self, 0, sizeof(*self));
}
- if (!self->alloc != !self->free) {
+ bool is_default_alloc = (self->alloc == nullptr);
+ bool is_default_free = (self->free == nullptr);
+ if (is_default_alloc != is_default_free) {
return false;
}
- if (!self->alloc) self->alloc = jxl::MemoryManagerDefaultAlloc;
- if (!self->free) self->free = jxl::MemoryManagerDefaultFree;
+ if (is_default_alloc) self->alloc = jxl::MemoryManagerDefaultAlloc;
+ if (is_default_free) self->free = jxl::MemoryManagerDefaultFree;
return true;
}
@@ -52,7 +54,7 @@ static JXL_INLINE void* MemoryManagerAlloc(
static JXL_INLINE void MemoryManagerFree(const JxlMemoryManager* memory_manager,
void* address) {
- return memory_manager->free(memory_manager->opaque, address);
+ memory_manager->free(memory_manager->opaque, address);
}
// Helper class to be used as a deleter in a unique_ptr<T> call.
diff --git a/third_party/jpeg-xl/lib/jxl/modular/encoding/context_predict.h b/third_party/jpeg-xl/lib/jxl/modular/encoding/context_predict.h
index 4c3a33a52a..7bec5128fc 100644
--- a/third_party/jpeg-xl/lib/jxl/modular/encoding/context_predict.h
+++ b/third_party/jpeg-xl/lib/jxl/modular/encoding/context_predict.h
@@ -10,6 +10,7 @@
#include <vector>
#include "lib/jxl/fields.h"
+#include "lib/jxl/image_ops.h"
#include "lib/jxl/modular/modular_image.h"
#include "lib/jxl/modular/options.h"
@@ -78,14 +79,14 @@ struct State {
294337, 289262, 284359, 279620, 275036, 270600, 266305, 262144};
constexpr static pixel_type_w AddBits(pixel_type_w x) {
- return uint64_t(x) << kPredExtraBits;
+ return static_cast<uint64_t>(x) << kPredExtraBits;
}
State(Header header, size_t xsize, size_t ysize) : header(header) {
// Extra margin to avoid out-of-bounds writes.
// All have space for two rows of data.
- for (size_t i = 0; i < 4; i++) {
- pred_errors[i].resize((xsize + 2) * 2);
+ for (auto &pred_error : pred_errors) {
+ pred_error.resize((xsize + 2) * 2);
}
error.resize((xsize + 2) * 2);
}
@@ -538,8 +539,9 @@ JXL_INLINE PredictionResult Predict(
}
if (mode & kAllPredictions) {
for (size_t i = 0; i < kNumModularPredictors; i++) {
- predictions[i] = PredictOne((Predictor)i, left, top, toptop, topleft,
- topright, leftleft, toprightright, wp_pred);
+ predictions[i] =
+ PredictOne(static_cast<Predictor>(i), left, top, toptop, topleft,
+ topright, leftleft, toprightright, wp_pred);
}
}
result.guess += PredictOne(predictor, left, top, toptop, topleft, topright,
diff --git a/third_party/jpeg-xl/lib/jxl/modular/encoding/dec_ma.cc b/third_party/jpeg-xl/lib/jxl/modular/encoding/dec_ma.cc
index ee7177bcd6..b53b9a9103 100644
--- a/third_party/jpeg-xl/lib/jxl/modular/encoding/dec_ma.cc
+++ b/third_party/jpeg-xl/lib/jxl/modular/encoding/dec_ma.cc
@@ -5,6 +5,8 @@
#include "lib/jxl/modular/encoding/dec_ma.h"
+#include <limits>
+
#include "lib/jxl/base/printf_macros.h"
#include "lib/jxl/dec_ans.h"
#include "lib/jxl/modular/encoding/ma_common.h"
diff --git a/third_party/jpeg-xl/lib/jxl/modular/encoding/enc_debug_tree.cc b/third_party/jpeg-xl/lib/jxl/modular/encoding/enc_debug_tree.cc
index bd27f28458..f863823629 100644
--- a/third_party/jpeg-xl/lib/jxl/modular/encoding/enc_debug_tree.cc
+++ b/third_party/jpeg-xl/lib/jxl/modular/encoding/enc_debug_tree.cc
@@ -95,7 +95,7 @@ std::string PropertyName(size_t i) {
case 15:
return "WGH";
default:
- return "ch[" + ToString(15 - (int)i) + "]";
+ return "ch[" + ToString(15 - static_cast<int>(i)) + "]";
}
}
diff --git a/third_party/jpeg-xl/lib/jxl/modular/encoding/enc_encoding.cc b/third_party/jpeg-xl/lib/jxl/modular/encoding/enc_encoding.cc
index fc2e69e4a6..84d8137d21 100644
--- a/third_party/jpeg-xl/lib/jxl/modular/encoding/enc_encoding.cc
+++ b/third_party/jpeg-xl/lib/jxl/modular/encoding/enc_encoding.cc
@@ -6,35 +6,24 @@
#include <stdint.h>
#include <stdlib.h>
-#include <cinttypes>
#include <limits>
-#include <numeric>
#include <queue>
-#include <set>
-#include <unordered_map>
-#include <unordered_set>
#include "lib/jxl/base/common.h"
#include "lib/jxl/base/printf_macros.h"
#include "lib/jxl/base/status.h"
-#include "lib/jxl/dec_ans.h"
-#include "lib/jxl/dec_bit_reader.h"
#include "lib/jxl/enc_ans.h"
#include "lib/jxl/enc_aux_out.h"
#include "lib/jxl/enc_bit_writer.h"
#include "lib/jxl/enc_fields.h"
-#include "lib/jxl/entropy_coder.h"
#include "lib/jxl/fields.h"
#include "lib/jxl/image_ops.h"
#include "lib/jxl/modular/encoding/context_predict.h"
-#include "lib/jxl/modular/encoding/enc_debug_tree.h"
#include "lib/jxl/modular/encoding/enc_ma.h"
#include "lib/jxl/modular/encoding/encoding.h"
#include "lib/jxl/modular/encoding/ma_common.h"
#include "lib/jxl/modular/options.h"
-#include "lib/jxl/modular/transform/transform.h"
#include "lib/jxl/pack_signed.h"
-#include "lib/jxl/toc.h"
namespace jxl {
@@ -61,7 +50,7 @@ inline std::array<uint8_t, 3> PredictorColor(Predictor p) {
return {{255, 0, 255}};
case Predictor::Weighted:
return {{0, 255, 255}};
- // TODO
+ // TODO(jon)
default:
return {{255, 255, 255}};
};
@@ -101,17 +90,17 @@ Tree MakeFixedTree(int property, const std::vector<int32_t> &cutoffs,
} // namespace
-void GatherTreeData(const Image &image, pixel_type chan, size_t group_id,
- const weighted::Header &wp_header,
- const ModularOptions &options, TreeSamples &tree_samples,
- size_t *total_pixels) {
+Status GatherTreeData(const Image &image, pixel_type chan, size_t group_id,
+ const weighted::Header &wp_header,
+ const ModularOptions &options, TreeSamples &tree_samples,
+ size_t *total_pixels) {
const Channel &channel = image.channel[chan];
JXL_DEBUG_V(7, "Learning %" PRIuS "x%" PRIuS " channel %d", channel.w,
channel.h, chan);
std::array<pixel_type, kNumStaticProperties> static_props = {
- {chan, (int)group_id}};
+ {chan, static_cast<int>(group_id)}};
Properties properties(kNumNonrefProperties +
kExtraPropsPerChannel * options.max_properties);
double pixel_fraction = std::min(1.0f, options.nb_repeats);
@@ -137,7 +126,9 @@ void GatherTreeData(const Image &image, pixel_type chan, size_t group_id,
};
const intptr_t onerow = channel.plane.PixelsPerRow();
- Channel references(properties.size() - kNumNonrefProperties, channel.w);
+ JXL_ASSIGN_OR_RETURN(
+ Channel references,
+ Channel::Create(properties.size() - kNumNonrefProperties, channel.w));
weighted::State wp_state(wp_header, channel.w, channel.h);
tree_samples.PrepareForSamples(pixel_fraction * channel.h * channel.w + 64);
const bool multiple_predictors = tree_samples.NumPredictors() != 1;
@@ -198,6 +189,7 @@ void GatherTreeData(const Image &image, pixel_type chan, size_t group_id,
}
}
}
+ return true;
}
Tree PredefinedTree(ModularOptions::TreeKind tree_kind, size_t total_pixels) {
@@ -316,7 +308,9 @@ Status EncodeModularChannelMAANS(const Image &image, pixel_type chan,
JXL_ASSERT(channel.w != 0 && channel.h != 0);
Image3F predictor_img;
- if (kWantDebug) predictor_img = Image3F(channel.w, channel.h);
+ if (kWantDebug) {
+ JXL_ASSIGN_OR_RETURN(predictor_img, Image3F::Create(channel.w, channel.h));
+ }
JXL_DEBUG_V(6,
"Encoding %" PRIuS "x%" PRIuS
@@ -325,8 +319,9 @@ Status EncodeModularChannelMAANS(const Image &image, pixel_type chan,
channel.w, channel.h, chan, channel.hshift, channel.vshift);
std::array<pixel_type, kNumStaticProperties> static_props = {
- {chan, (int)group_id}};
- bool use_wp, is_wp_only;
+ {chan, static_cast<int>(group_id)}};
+ bool use_wp;
+ bool is_wp_only;
bool is_gradient_only;
size_t num_props;
FlatTree tree = FilterTree(global_tree, static_props, &num_props, &use_wp,
@@ -439,7 +434,8 @@ Status EncodeModularChannelMAANS(const Image &image, pixel_type chan,
FillImage(static_cast<float>(PredictorColor(tree[0].predictor)[c]),
&predictor_img.Plane(c));
}
- uint32_t mul_shift = FloorLog2Nonzero((uint32_t)tree[0].multiplier);
+ uint32_t mul_shift =
+ FloorLog2Nonzero(static_cast<uint32_t>(tree[0].multiplier));
const intptr_t onerow = channel.plane.PixelsPerRow();
for (size_t y = 0; y < channel.h; y++) {
const pixel_type *JXL_RESTRICT r = channel.Row(y);
@@ -454,7 +450,9 @@ Status EncodeModularChannelMAANS(const Image &image, pixel_type chan,
} else if (!use_wp && !skip_encoder_fast_path) {
const intptr_t onerow = channel.plane.PixelsPerRow();
- Channel references(properties.size() - kNumNonrefProperties, channel.w);
+ JXL_ASSIGN_OR_RETURN(
+ Channel references,
+ Channel::Create(properties.size() - kNumNonrefProperties, channel.w));
for (size_t y = 0; y < channel.h; y++) {
const pixel_type *JXL_RESTRICT p = channel.Row(y);
PrecomputeReferences(channel, y, image, chan, &references);
@@ -481,7 +479,9 @@ Status EncodeModularChannelMAANS(const Image &image, pixel_type chan,
}
} else {
const intptr_t onerow = channel.plane.PixelsPerRow();
- Channel references(properties.size() - kNumNonrefProperties, channel.w);
+ JXL_ASSIGN_OR_RETURN(
+ Channel references,
+ Channel::Create(properties.size() - kNumNonrefProperties, channel.w));
weighted::State wp_state(wp_header, channel.w, channel.h);
for (size_t y = 0; y < channel.h; y++) {
const pixel_type *JXL_RESTRICT p = channel.Row(y);
@@ -556,8 +556,20 @@ Status ModularEncode(const Image &image, const ModularOptions &options,
TreeSamples tree_samples_storage;
size_t total_pixels_storage = 0;
if (!total_pixels) total_pixels = &total_pixels_storage;
+ if (*total_pixels == 0) {
+ for (size_t i = 0; i < nb_channels; i++) {
+ if (i >= image.nb_meta_channels &&
+ (image.channel[i].w > options.max_chan_size ||
+ image.channel[i].h > options.max_chan_size)) {
+ break;
+ }
+ *total_pixels += image.channel[i].w * image.channel[i].h;
+ }
+ *total_pixels = std::max<size_t>(*total_pixels, 1);
+ }
// If there's no tree, compute one (or gather data to).
- if (tree == nullptr) {
+ if (tree == nullptr &&
+ options.tree_kind == ModularOptions::TreeKind::kLearn) {
bool gather_data = tree_samples != nullptr;
if (tree_samples == nullptr) {
JXL_RETURN_IF_ERROR(tree_samples_storage.SetPredictor(
@@ -586,9 +598,9 @@ Status ModularEncode(const Image &image, const ModularOptions &options,
image.channel[i].h > options.max_chan_size)) {
break;
}
- GatherTreeData(image, i, group_id, header->wp_header, options,
- gather_data ? *tree_samples : tree_samples_storage,
- total_pixels);
+ JXL_RETURN_IF_ERROR(GatherTreeData(
+ image, i, group_id, header->wp_header, options,
+ gather_data ? *tree_samples : tree_samples_storage, total_pixels));
}
if (gather_data) return true;
}
@@ -609,10 +621,10 @@ Status ModularEncode(const Image &image, const ModularOptions &options,
? LearnTree(std::move(tree_samples_storage), *total_pixels, options)
: PredefinedTree(options.tree_kind, *total_pixels);
tree = &tree_storage;
- tokens = &tokens_storage[0];
+ tokens = tokens_storage.data();
Tree decoded_tree;
- TokenizeTree(*tree, &tree_tokens[0], &decoded_tree);
+ TokenizeTree(*tree, tree_tokens.data(), &decoded_tree);
JXL_ASSERT(tree->size() == decoded_tree.size());
tree_storage = std::move(decoded_tree);
@@ -622,9 +634,9 @@ Status ModularEncode(const Image &image, const ModularOptions &options,
} */
// Write tree
- BuildAndEncodeHistograms(HistogramParams(), kNumTreeContexts, tree_tokens,
- &code, &context_map, writer, kLayerModularTree,
- aux_out);
+ BuildAndEncodeHistograms(options.histogram_params, kNumTreeContexts,
+ tree_tokens, &code, &context_map, writer,
+ kLayerModularTree, aux_out);
WriteTokens(tree_tokens[0], code, context_map, 0, writer, kLayerModularTree,
aux_out);
}
@@ -669,7 +681,7 @@ Status ModularEncode(const Image &image, const ModularOptions &options,
if (!header->use_global_tree) {
EntropyEncodingData code;
std::vector<uint8_t> context_map;
- HistogramParams histo_params;
+ HistogramParams histo_params = options.histogram_params;
histo_params.image_widths.push_back(image_width);
BuildAndEncodeHistograms(histo_params, (tree->size() + 1) / 2,
tokens_storage, &code, &context_map, writer, layer,
@@ -691,7 +703,7 @@ Status ModularGenericCompress(Image &image, const ModularOptions &opts,
if (image.w == 0 || image.h == 0) return true;
ModularOptions options = opts; // Make a copy to modify it.
- if (options.predictor == static_cast<Predictor>(-1)) {
+ if (options.predictor == kUndefinedPredictor) {
options.predictor = Predictor::Gradient;
}
diff --git a/third_party/jpeg-xl/lib/jxl/modular/encoding/enc_ma.cc b/third_party/jpeg-xl/lib/jxl/modular/encoding/enc_ma.cc
index ef72b2477b..de629ad038 100644
--- a/third_party/jpeg-xl/lib/jxl/modular/encoding/enc_ma.cc
+++ b/third_party/jpeg-xl/lib/jxl/modular/encoding/enc_ma.cc
@@ -109,8 +109,8 @@ IntersectionType BoxIntersects(StaticPropRange needle, StaticPropRange haystack,
void SplitTreeSamples(TreeSamples &tree_samples, size_t begin, size_t pos,
size_t end, size_t prop) {
auto cmp = [&](size_t a, size_t b) {
- return int32_t(tree_samples.Property(prop, a)) -
- int32_t(tree_samples.Property(prop, b));
+ return static_cast<int32_t>(tree_samples.Property(prop, a)) -
+ static_cast<int32_t>(tree_samples.Property(prop, b));
};
Rng rng(0);
while (end > begin + 1) {
@@ -243,7 +243,8 @@ void FindBestSplit(TreeSamples &tree_samples, float threshold,
// properties. We do this even if the current node is not a leaf, to
// minimize the number of nodes in the resulting tree.
for (size_t i = 0; i < mul_info.size(); i++) {
- uint32_t axis, val;
+ uint32_t axis;
+ uint32_t val;
IntersectionType t =
BoxIntersects(static_prop_range, mul_info[i].range, axis, val);
if (t == IntersectionType::kNone) continue;
@@ -696,7 +697,11 @@ void TreeSamples::Swap(size_t a, size_t b) {
}
void TreeSamples::ThreeShuffle(size_t a, size_t b, size_t c) {
- if (b == c) return Swap(a, b);
+ if (b == c) {
+ Swap(a, b);
+ return;
+ }
+
for (auto &r : residuals) {
auto tmp = r[a];
r[a] = r[c];
@@ -966,7 +971,7 @@ void CollectPixelSamples(const Image &image, const ModularOptions &options,
const pixel_type *row = image.channel[channel_ids[i]].Row(y);
pixel_samples.push_back(row[x]);
size_t xp = x == 0 ? 1 : x - 1;
- diff_samples.push_back((int64_t)row[x] - row[xp]);
+ diff_samples.push_back(static_cast<int64_t>(row[x]) - row[xp]);
}
}
diff --git a/third_party/jpeg-xl/lib/jxl/modular/encoding/encoding.cc b/third_party/jpeg-xl/lib/jxl/modular/encoding/encoding.cc
index a6abdcfc91..bb690b74ba 100644
--- a/third_party/jpeg-xl/lib/jxl/modular/encoding/encoding.cc
+++ b/third_party/jpeg-xl/lib/jxl/modular/encoding/encoding.cc
@@ -14,6 +14,8 @@
#include "lib/jxl/base/scope_guard.h"
#include "lib/jxl/dec_ans.h"
#include "lib/jxl/dec_bit_reader.h"
+#include "lib/jxl/frame_dimensions.h"
+#include "lib/jxl/image_ops.h"
#include "lib/jxl/modular/encoding/context_predict.h"
#include "lib/jxl/modular/options.h"
#include "lib/jxl/pack_signed.h"
@@ -141,7 +143,7 @@ Status DecodeModularChannelMAANS(BitReader *br, ANSSymbolReader *reader,
Channel &channel = image->channel[chan];
std::array<pixel_type, kNumStaticProperties> static_props = {
- {chan, (int)group_id}};
+ {chan, static_cast<int>(group_id)}};
// TODO(veluca): filter the tree according to static_props.
// zero pixel channel? could happen
@@ -376,7 +378,9 @@ Status DecodeModularChannelMAANS(BitReader *br, ANSSymbolReader *reader,
MATreeLookup tree_lookup(tree);
Properties properties = Properties(num_props);
const intptr_t onerow = channel.plane.PixelsPerRow();
- Channel references(properties.size() - kNumNonrefProperties, channel.w);
+ JXL_ASSIGN_OR_RETURN(
+ Channel references,
+ Channel::Create(properties.size() - kNumNonrefProperties, channel.w));
for (size_t y = 0; y < channel.h; y++) {
pixel_type *JXL_RESTRICT p = channel.Row(y);
PrecomputeReferences(channel, y, *image, chan, &references);
@@ -422,7 +426,9 @@ Status DecodeModularChannelMAANS(BitReader *br, ANSSymbolReader *reader,
MATreeLookup tree_lookup(tree);
Properties properties = Properties(num_props);
const intptr_t onerow = channel.plane.PixelsPerRow();
- Channel references(properties.size() - kNumNonrefProperties, channel.w);
+ JXL_ASSIGN_OR_RETURN(
+ Channel references,
+ Channel::Create(properties.size() - kNumNonrefProperties, channel.w));
weighted::State wp_state(wp_header, channel.w, channel.h);
for (size_t y = 0; y < channel.h; y++) {
pixel_type *JXL_RESTRICT p = channel.Row(y);
diff --git a/third_party/jpeg-xl/lib/jxl/modular/modular_image.cc b/third_party/jpeg-xl/lib/jxl/modular/modular_image.cc
index 746d7c87fd..32a5531080 100644
--- a/third_party/jpeg-xl/lib/jxl/modular/modular_image.cc
+++ b/third_party/jpeg-xl/lib/jxl/modular/modular_image.cc
@@ -8,6 +8,7 @@
#include <sstream>
#include "lib/jxl/base/status.h"
+#include "lib/jxl/image_ops.h"
#include "lib/jxl/modular/transform/transform.h"
namespace jxl {
@@ -28,9 +29,18 @@ void Image::undo_transforms(const weighted::Header &wp_header,
}
}
-Image::Image(size_t iw, size_t ih, int bitdepth, int nb_chans)
- : w(iw), h(ih), bitdepth(bitdepth), nb_meta_channels(0), error(false) {
- for (int i = 0; i < nb_chans; i++) channel.emplace_back(Channel(iw, ih));
+Image::Image(size_t iw, size_t ih, int bitdepth)
+ : w(iw), h(ih), bitdepth(bitdepth), nb_meta_channels(0), error(false) {}
+
+StatusOr<Image> Image::Create(size_t iw, size_t ih, int bitdepth,
+ int nb_chans) {
+ Image result(iw, ih, bitdepth);
+ for (int i = 0; i < nb_chans; i++) {
+ StatusOr<Channel> channel_or = Channel::Create(iw, ih);
+ JXL_RETURN_IF_ERROR(channel_or.status());
+ result.channel.emplace_back(std::move(channel_or).value());
+ }
+ return result;
}
Image::Image() : w(0), h(0), bitdepth(8), nb_meta_channels(0), error(true) {}
@@ -46,17 +56,18 @@ Image &Image::operator=(Image &&other) noexcept {
return *this;
}
-Image Image::clone() {
- Image c(w, h, bitdepth, 0);
- c.nb_meta_channels = nb_meta_channels;
- c.error = error;
- c.transform = transform;
- for (Channel &ch : channel) {
- Channel a(ch.w, ch.h, ch.hshift, ch.vshift);
+StatusOr<Image> Image::Clone(const Image &that) {
+ Image clone(that.w, that.h, that.bitdepth);
+ clone.nb_meta_channels = that.nb_meta_channels;
+ clone.error = that.error;
+ clone.transform = that.transform;
+ for (const Channel &ch : that.channel) {
+ JXL_ASSIGN_OR_RETURN(Channel a,
+ Channel::Create(ch.w, ch.h, ch.hshift, ch.vshift));
CopyImageTo(ch.plane, &a.plane);
- c.channel.push_back(std::move(a));
+ clone.channel.push_back(std::move(a));
}
- return c;
+ return clone;
}
#if JXL_DEBUG_V_LEVEL >= 1
diff --git a/third_party/jpeg-xl/lib/jxl/modular/modular_image.h b/third_party/jpeg-xl/lib/jxl/modular/modular_image.h
index 56e80d823a..eb95b1cb6c 100644
--- a/third_party/jpeg-xl/lib/jxl/modular/modular_image.h
+++ b/third_party/jpeg-xl/lib/jxl/modular/modular_image.h
@@ -18,7 +18,6 @@
#include "lib/jxl/base/data_parallel.h"
#include "lib/jxl/base/status.h"
#include "lib/jxl/image.h"
-#include "lib/jxl/image_ops.h"
namespace jxl {
@@ -36,12 +35,16 @@ class Channel {
jxl::Plane<pixel_type> plane;
size_t w, h;
int hshift, vshift; // w ~= image.w >> hshift; h ~= image.h >> vshift
- Channel(size_t iw, size_t ih, int hsh = 0, int vsh = 0)
- : plane(iw, ih), w(iw), h(ih), hshift(hsh), vshift(vsh) {}
-
Channel(const Channel& other) = delete;
Channel& operator=(const Channel& other) = delete;
+ static StatusOr<Channel> Create(size_t iw, size_t ih, int hsh = 0,
+ int vsh = 0) {
+ JXL_ASSIGN_OR_RETURN(Plane<pixel_type> plane,
+ Plane<pixel_type>::Create(iw, ih));
+ return Channel(std::move(plane), iw, ih, hsh, vsh);
+ }
+
// Move assignment
Channel& operator=(Channel&& other) noexcept {
w = other.w;
@@ -55,21 +58,25 @@ class Channel {
// Move constructor
Channel(Channel&& other) noexcept = default;
- void shrink() {
- if (plane.xsize() == w && plane.ysize() == h) return;
- jxl::Plane<pixel_type> resizedplane(w, h);
- plane = std::move(resizedplane);
+ Status shrink() {
+ if (plane.xsize() == w && plane.ysize() == h) return true;
+ JXL_ASSIGN_OR_RETURN(plane, Plane<pixel_type>::Create(w, h));
+ return true;
}
- void shrink(int nw, int nh) {
+ Status shrink(int nw, int nh) {
w = nw;
h = nh;
- shrink();
+ return shrink();
}
JXL_INLINE pixel_type* Row(const size_t y) { return plane.Row(y); }
JXL_INLINE const pixel_type* Row(const size_t y) const {
return plane.Row(y);
}
+
+ private:
+ Channel(jxl::Plane<pixel_type>&& p, size_t iw, size_t ih, int hsh, int vsh)
+ : plane(std::move(p)), w(iw), h(ih), hshift(hsh), vshift(vsh) {}
};
class Transform;
@@ -88,7 +95,6 @@ class Image {
size_t nb_meta_channels; // first few channels might contain palette(s)
bool error; // true if a fatal error occurred, false otherwise
- Image(size_t iw, size_t ih, int bitdepth, int nb_chans);
Image();
Image(const Image& other) = delete;
@@ -97,6 +103,9 @@ class Image {
Image& operator=(Image&& other) noexcept;
Image(Image&& other) noexcept = default;
+ static StatusOr<Image> Create(size_t iw, size_t ih, int bitdepth,
+ int nb_chans);
+
bool empty() const {
for (const auto& ch : channel) {
if (ch.w && ch.h) return false;
@@ -104,12 +113,15 @@ class Image {
return true;
}
- Image clone();
+ static StatusOr<Image> Clone(const Image& that);
void undo_transforms(const weighted::Header& wp_header,
jxl::ThreadPool* pool = nullptr);
std::string DebugString() const;
+
+ private:
+ Image(size_t iw, size_t ih, int bitdepth);
};
} // namespace jxl
diff --git a/third_party/jpeg-xl/lib/jxl/modular/options.h b/third_party/jpeg-xl/lib/jxl/modular/options.h
index ce6596b912..6613b513de 100644
--- a/third_party/jpeg-xl/lib/jxl/modular/options.h
+++ b/third_party/jpeg-xl/lib/jxl/modular/options.h
@@ -11,6 +11,8 @@
#include <array>
#include <vector>
+#include "lib/jxl/enc_ans_params.h"
+
namespace jxl {
using PropertyVal = int32_t;
@@ -37,6 +39,8 @@ enum class Predictor : uint32_t {
15, // Find the best decision tree for predictors/predictor per row
};
+constexpr Predictor kUndefinedPredictor = static_cast<Predictor>(~0u);
+
constexpr size_t kNumModularPredictors =
static_cast<size_t>(Predictor::Average4) + 1;
constexpr size_t kNumModularEncoderPredictors =
@@ -80,7 +84,7 @@ struct ModularOptions {
size_t max_property_values = 32;
// Predictor to use for each channel.
- Predictor predictor = static_cast<Predictor>(-1);
+ Predictor predictor = kUndefinedPredictor;
int wp_mode = 0;
@@ -108,6 +112,8 @@ struct ModularOptions {
};
TreeKind tree_kind = TreeKind::kLearn;
+ HistogramParams histogram_params;
+
// Ignore the image and just pretend all tokens are zeroes
bool zero_tokens = false;
};
diff --git a/third_party/jpeg-xl/lib/jxl/modular/transform/enc_palette.cc b/third_party/jpeg-xl/lib/jxl/modular/transform/enc_palette.cc
index f5172aa126..24c64f5aad 100644
--- a/third_party/jpeg-xl/lib/jxl/modular/transform/enc_palette.cc
+++ b/third_party/jpeg-xl/lib/jxl/modular/transform/enc_palette.cc
@@ -10,8 +10,8 @@
#include <set>
#include "lib/jxl/base/common.h"
-#include "lib/jxl/base/data_parallel.h"
#include "lib/jxl/base/status.h"
+#include "lib/jxl/image_ops.h"
#include "lib/jxl/modular/encoding/context_predict.h"
#include "lib/jxl/modular/modular_image.h"
#include "lib/jxl/modular/transform/enc_transform.h"
@@ -34,7 +34,8 @@ float ColorDistance(const std::vector<float> &JXL_RESTRICT a,
if (a.size() >= 3) {
ave3 = (a[0] + b[0] + a[1] + b[1] + a[2] + b[2]) * (1.21f / 3.0f);
}
- float sum_a = 0, sum_b = 0;
+ float sum_a = 0;
+ float sum_b = 0;
for (size_t c = 0; c < a.size(); ++c) {
const float difference =
static_cast<float>(a[c]) - static_cast<float>(b[c]);
@@ -132,7 +133,8 @@ struct PaletteIterationData {
delta_frequency.first[1],
delta_frequency.first[2]};
float delta_distance =
- sqrt(palette_internal::ColorDistance({0, 0, 0}, current_delta)) + 1;
+ std::sqrt(palette_internal::ColorDistance({0, 0, 0}, current_delta)) +
+ 1;
delta_frequency.second *= delta_distance * delta_distance_multiplier;
}
@@ -174,7 +176,8 @@ Status FwdPaletteIteration(Image &input, uint32_t begin_c, uint32_t end_c,
// Channel palette special case
if (nb_colors == 0) return false;
std::vector<pixel_type> lookup;
- pixel_type minval, maxval;
+ pixel_type minval;
+ pixel_type maxval;
compute_minmax(input.channel[begin_c], &minval, &maxval);
size_t lookup_table_size =
static_cast<int64_t>(maxval) - static_cast<int64_t>(minval) + 1;
@@ -189,12 +192,12 @@ Status FwdPaletteIteration(Image &input, uint32_t begin_c, uint32_t end_c,
const bool new_color = chpalette.insert(p[x]).second;
if (new_color) {
idx++;
- if (idx > (int)nb_colors) return false;
+ if (idx > static_cast<int>(nb_colors)) return false;
}
}
}
JXL_DEBUG_V(6, "Channel %i uses only %i colors.", begin_c, idx);
- Channel pch(idx, 1);
+ JXL_ASSIGN_OR_RETURN(Channel pch, Channel::Create(idx, 1));
pch.hshift = -1;
pch.vshift = -1;
nb_colors = idx;
@@ -206,9 +209,12 @@ Status FwdPaletteIteration(Image &input, uint32_t begin_c, uint32_t end_c,
for (size_t y = 0; y < h; y++) {
pixel_type *p = input.channel[begin_c].Row(y);
for (size_t x = 0; x < w; x++) {
- for (idx = 0; p[x] != p_palette[idx] && idx < (int)nb_colors; idx++) {
+ for (idx = 0;
+ p[x] != p_palette[idx] && idx < static_cast<int>(nb_colors);
+ idx++) {
+ // no-op
}
- JXL_DASSERT(idx < (int)nb_colors);
+ JXL_DASSERT(idx < static_cast<int>(nb_colors));
p[x] = idx;
}
}
@@ -226,12 +232,12 @@ Status FwdPaletteIteration(Image &input, uint32_t begin_c, uint32_t end_c,
if (lookup[p[x] - minval] == 0) {
lookup[p[x] - minval] = 1;
idx++;
- if (idx > (int)nb_colors) return false;
+ if (idx > static_cast<int>(nb_colors)) return false;
}
}
}
JXL_DEBUG_V(6, "Channel %i uses only %i colors.", begin_c, idx);
- Channel pch(idx, 1);
+ JXL_ASSIGN_OR_RETURN(Channel pch, Channel::Create(idx, 1));
pch.hshift = -1;
pch.vshift = -1;
nb_colors = idx;
@@ -256,7 +262,8 @@ Status FwdPaletteIteration(Image &input, uint32_t begin_c, uint32_t end_c,
Image quantized_input;
if (lossy) {
- quantized_input = Image(w, h, input.bitdepth, nb);
+ JXL_ASSIGN_OR_RETURN(quantized_input,
+ Image::Create(w, h, input.bitdepth, nb));
for (size_t c = 0; c < nb; c++) {
CopyImageTo(input.channel[begin_c + c].plane,
&quantized_input.channel[c].plane);
@@ -337,7 +344,7 @@ Status FwdPaletteIteration(Image &input, uint32_t begin_c, uint32_t end_c,
JXL_DEBUG_V(6, "Channels %i-%i can be represented using a %i-color palette.",
begin_c, end_c, nb_colors);
- Channel pch(nb_colors, nb);
+ JXL_ASSIGN_OR_RETURN(Channel pch, Channel::Create(nb_colors, nb));
pch.hshift = -1;
pch.vshift = -1;
pixel_type *JXL_RESTRICT p_palette = pch.Row(0);
@@ -361,7 +368,8 @@ Status FwdPaletteIteration(Image &input, uint32_t begin_c, uint32_t end_c,
std::sort(candidate_palette_imageorder.begin(),
candidate_palette_imageorder.end(),
[](std::vector<pixel_type> ap, std::vector<pixel_type> bp) {
- float ay, by;
+ float ay;
+ float by;
ay = (0.299f * ap[0] + 0.587f * ap[1] + 0.114f * ap[2] + 0.1f);
if (ap.size() > 3) ay *= 1.f + ap[3];
by = (0.299f * bp[0] + 0.587f * bp[1] + 0.114f * bp[2] + 0.1f);
@@ -420,7 +428,7 @@ Status FwdPaletteIteration(Image &input, uint32_t begin_c, uint32_t end_c,
for (int diffusion_index = 0; diffusion_index < 2; ++diffusion_index) {
for (size_t c = 0; c < nb; c++) {
color_with_error[c] =
- p_in[c][x] + palette_iteration_data.final_run *
+ p_in[c][x] + (palette_iteration_data.final_run ? 1 : 0) *
kDiffusionMultiplier[diffusion_index] *
error_row[0][c][x + 2];
color[c] = Clamp1(lroundf(color_with_error[c]), 0l,
@@ -503,7 +511,7 @@ Status FwdPaletteIteration(Image &input, uint32_t begin_c, uint32_t end_c,
float local_error = color_with_error[c] - best_val[c];
len_error += local_error * local_error;
}
- len_error = sqrt(len_error);
+ len_error = std::sqrt(len_error);
float modulate = 1.0;
int len_limit = 38 << std::max(0, bit_depth - 8);
if (len_error > len_limit) {
diff --git a/third_party/jpeg-xl/lib/jxl/modular/transform/enc_squeeze.cc b/third_party/jpeg-xl/lib/jxl/modular/transform/enc_squeeze.cc
index 489f72a90d..0d924c0ace 100644
--- a/third_party/jpeg-xl/lib/jxl/modular/transform/enc_squeeze.cc
+++ b/third_party/jpeg-xl/lib/jxl/modular/transform/enc_squeeze.cc
@@ -14,15 +14,20 @@
namespace jxl {
-void FwdHSqueeze(Image &input, int c, int rc) {
+#define AVERAGE(X, Y) (((X) + (Y) + (((X) > (Y)) ? 1 : 0)) >> 1)
+
+Status FwdHSqueeze(Image &input, int c, int rc) {
const Channel &chin = input.channel[c];
JXL_DEBUG_V(4, "Doing horizontal squeeze of channel %i to new channel %i", c,
rc);
- Channel chout((chin.w + 1) / 2, chin.h, chin.hshift + 1, chin.vshift);
- Channel chout_residual(chin.w - chout.w, chout.h, chin.hshift + 1,
- chin.vshift);
+ JXL_ASSIGN_OR_RETURN(
+ Channel chout,
+ Channel::Create((chin.w + 1) / 2, chin.h, chin.hshift + 1, chin.vshift));
+ JXL_ASSIGN_OR_RETURN(
+ Channel chout_residual,
+ Channel::Create(chin.w - chout.w, chout.h, chin.hshift + 1, chin.vshift));
for (size_t y = 0; y < chout.h; y++) {
const pixel_type *JXL_RESTRICT p_in = chin.Row(y);
@@ -31,18 +36,19 @@ void FwdHSqueeze(Image &input, int c, int rc) {
for (size_t x = 0; x < chout_residual.w; x++) {
pixel_type A = p_in[x * 2];
pixel_type B = p_in[x * 2 + 1];
- pixel_type avg = (A + B + (A > B)) >> 1;
+ pixel_type avg = AVERAGE(A, B);
p_out[x] = avg;
pixel_type diff = A - B;
pixel_type next_avg = avg;
if (x + 1 < chout_residual.w) {
- next_avg = (p_in[x * 2 + 2] + p_in[x * 2 + 3] +
- (p_in[x * 2 + 2] > p_in[x * 2 + 3])) >>
- 1; // which will be chout.value(y,x+1)
- } else if (chin.w & 1)
+ pixel_type C = p_in[x * 2 + 2];
+ pixel_type D = p_in[x * 2 + 3];
+ next_avg = AVERAGE(C, D); // which will be chout.value(y,x+1)
+ } else if (chin.w & 1) {
next_avg = p_in[x * 2 + 2];
+ }
pixel_type left = (x > 0 ? p_in[x * 2 - 1] : avg);
pixel_type tendency = SmoothTendency(left, avg, next_avg);
@@ -55,17 +61,21 @@ void FwdHSqueeze(Image &input, int c, int rc) {
}
input.channel[c] = std::move(chout);
input.channel.insert(input.channel.begin() + rc, std::move(chout_residual));
+ return true;
}
-void FwdVSqueeze(Image &input, int c, int rc) {
+Status FwdVSqueeze(Image &input, int c, int rc) {
const Channel &chin = input.channel[c];
JXL_DEBUG_V(4, "Doing vertical squeeze of channel %i to new channel %i", c,
rc);
- Channel chout(chin.w, (chin.h + 1) / 2, chin.hshift, chin.vshift + 1);
- Channel chout_residual(chin.w, chin.h - chout.h, chin.hshift,
- chin.vshift + 1);
+ JXL_ASSIGN_OR_RETURN(
+ Channel chout,
+ Channel::Create(chin.w, (chin.h + 1) / 2, chin.hshift, chin.vshift + 1));
+ JXL_ASSIGN_OR_RETURN(
+ Channel chout_residual,
+ Channel::Create(chin.w, chin.h - chout.h, chin.hshift, chin.vshift + 1));
intptr_t onerow_in = chin.plane.PixelsPerRow();
for (size_t y = 0; y < chout_residual.h; y++) {
const pixel_type *JXL_RESTRICT p_in = chin.Row(y * 2);
@@ -74,16 +84,16 @@ void FwdVSqueeze(Image &input, int c, int rc) {
for (size_t x = 0; x < chout.w; x++) {
pixel_type A = p_in[x];
pixel_type B = p_in[x + onerow_in];
- pixel_type avg = (A + B + (A > B)) >> 1;
+ pixel_type avg = AVERAGE(A, B);
p_out[x] = avg;
pixel_type diff = A - B;
pixel_type next_avg = avg;
if (y + 1 < chout_residual.h) {
- next_avg = (p_in[x + 2 * onerow_in] + p_in[x + 3 * onerow_in] +
- (p_in[x + 2 * onerow_in] > p_in[x + 3 * onerow_in])) >>
- 1; // which will be chout.value(y+1,x)
+ pixel_type C = p_in[x + 2 * onerow_in];
+ pixel_type D = p_in[x + 3 * onerow_in];
+ next_avg = AVERAGE(C, D); // which will be chout.value(y+1,x)
} else if (chin.h & 1) {
next_avg = p_in[x + 2 * onerow_in];
}
@@ -104,6 +114,7 @@ void FwdVSqueeze(Image &input, int c, int rc) {
}
input.channel[c] = std::move(chout);
input.channel.insert(input.channel.begin() + rc, std::move(chout_residual));
+ return true;
}
Status FwdSqueeze(Image &input, std::vector<SqueezeParams> parameters,
@@ -128,9 +139,9 @@ Status FwdSqueeze(Image &input, std::vector<SqueezeParams> parameters,
}
for (uint32_t c = beginc; c <= endc; c++) {
if (horizontal) {
- FwdHSqueeze(input, c, offset + c - beginc);
+ JXL_RETURN_IF_ERROR(FwdHSqueeze(input, c, offset + c - beginc));
} else {
- FwdVSqueeze(input, c, offset + c - beginc);
+ JXL_RETURN_IF_ERROR(FwdVSqueeze(input, c, offset + c - beginc));
}
}
}
diff --git a/third_party/jpeg-xl/lib/jxl/modular/transform/palette.cc b/third_party/jpeg-xl/lib/jxl/modular/transform/palette.cc
index bffbacf160..1ab499ccf6 100644
--- a/third_party/jpeg-xl/lib/jxl/modular/transform/palette.cc
+++ b/third_party/jpeg-xl/lib/jxl/modular/transform/palette.cc
@@ -23,9 +23,11 @@ Status InvPalette(Image &input, uint32_t begin_c, uint32_t nb_colors,
size_t h = input.channel[c0].h;
if (nb < 1) return JXL_FAILURE("Corrupted transforms");
for (int i = 1; i < nb; i++) {
- input.channel.insert(
- input.channel.begin() + c0 + 1,
- Channel(w, h, input.channel[c0].hshift, input.channel[c0].vshift));
+ StatusOr<Channel> channel_or = Channel::Create(
+ w, h, input.channel[c0].hshift, input.channel[c0].vshift);
+ JXL_RETURN_IF_ERROR(channel_or.status());
+ input.channel.insert(input.channel.begin() + c0 + 1,
+ std::move(channel_or).value());
}
const Channel &palette = input.channel[0];
const pixel_type *JXL_RESTRICT p_palette = input.channel[0].Row(0);
@@ -44,7 +46,8 @@ Status InvPalette(Image &input, uint32_t begin_c, uint32_t nb_colors,
const size_t y = task;
pixel_type *p = input.channel[c0].Row(y);
for (size_t x = 0; x < w; x++) {
- const int index = Clamp1<int>(p[x], 0, (pixel_type)palette.w - 1);
+ const int index =
+ Clamp1<int>(p[x], 0, static_cast<pixel_type>(palette.w) - 1);
p[x] = palette_internal::GetPaletteValue(
p_palette, index, /*c=*/0,
/*palette_size=*/palette.w,
@@ -75,8 +78,10 @@ Status InvPalette(Image &input, uint32_t begin_c, uint32_t nb_colors,
}
} else {
// Parallelized per channel.
- ImageI indices = std::move(input.channel[c0].plane);
- input.channel[c0].plane = ImageI(indices.xsize(), indices.ysize());
+ ImageI indices;
+ ImageI &plane = input.channel[c0].plane;
+ JXL_ASSIGN_OR_RETURN(indices, ImageI::Create(plane.xsize(), plane.ysize()));
+ plane.Swap(indices);
if (predictor == Predictor::Weighted) {
JXL_RETURN_IF_ERROR(RunOnPool(
pool, 0, nb, ThreadPool::NoInit,
@@ -167,7 +172,7 @@ Status MetaPalette(Image &input, uint32_t begin_c, uint32_t end_c,
}
input.channel.erase(input.channel.begin() + begin_c + 1,
input.channel.begin() + end_c + 1);
- Channel pch(nb_colors + nb_deltas, nb);
+ JXL_ASSIGN_OR_RETURN(Channel pch, Channel::Create(nb_colors + nb_deltas, nb));
pch.hshift = -1;
pch.vshift = -1;
input.channel.insert(input.channel.begin(), std::move(pch));
diff --git a/third_party/jpeg-xl/lib/jxl/modular/transform/palette.h b/third_party/jpeg-xl/lib/jxl/modular/transform/palette.h
index 279ef04568..e0405a2162 100644
--- a/third_party/jpeg-xl/lib/jxl/modular/transform/palette.h
+++ b/third_party/jpeg-xl/lib/jxl/modular/transform/palette.h
@@ -101,6 +101,7 @@ GetPaletteValue(const pixel_type *const palette, int index, const size_t c,
// index >= kLargeCube ** 3 ?
switch (c) {
case 0:
+ default:
break;
case 1:
index /= kLargeCube;
diff --git a/third_party/jpeg-xl/lib/jxl/modular/transform/squeeze.cc b/third_party/jpeg-xl/lib/jxl/modular/transform/squeeze.cc
index e9892ea48f..580829741a 100644
--- a/third_party/jpeg-xl/lib/jxl/modular/transform/squeeze.cc
+++ b/third_party/jpeg-xl/lib/jxl/modular/transform/squeeze.cc
@@ -113,7 +113,9 @@ Status InvHSqueeze(Image &input, uint32_t c, uint32_t rc, ThreadPool *pool) {
}
// Note: chin.w >= chin_residual.w and at most 1 different.
- Channel chout(chin.w + chin_residual.w, chin.h, chin.hshift - 1, chin.vshift);
+ JXL_ASSIGN_OR_RETURN(Channel chout,
+ Channel::Create(chin.w + chin_residual.w, chin.h,
+ chin.hshift - 1, chin.vshift));
JXL_DEBUG_V(4,
"Undoing horizontal squeeze of channel %i using residuals in "
"channel %i (going from width %" PRIuS " to %" PRIuS ")",
@@ -222,7 +224,9 @@ Status InvVSqueeze(Image &input, uint32_t c, uint32_t rc, ThreadPool *pool) {
}
// Note: chin.h >= chin_residual.h and at most 1 different.
- Channel chout(chin.w, chin.h + chin_residual.h, chin.hshift, chin.vshift - 1);
+ JXL_ASSIGN_OR_RETURN(Channel chout,
+ Channel::Create(chin.w, chin.h + chin_residual.h,
+ chin.hshift, chin.vshift - 1));
JXL_DEBUG_V(
4,
"Undoing vertical squeeze of channel %i using residuals in channel "
@@ -238,7 +242,8 @@ Status InvVSqueeze(Image &input, uint32_t c, uint32_t rc, ThreadPool *pool) {
static constexpr const int kColsPerThread = 64;
const auto unsqueeze_slice = [&](const uint32_t task, size_t /* thread */) {
const size_t x0 = task * kColsPerThread;
- const size_t x1 = std::min((size_t)(task + 1) * kColsPerThread, chin.w);
+ const size_t x1 =
+ std::min(static_cast<size_t>(task + 1) * kColsPerThread, chin.w);
const size_t w = x1 - x0;
// We only iterate up to std::min(chin_residual.h, chin.h) which is
// always chin_residual.h.
@@ -289,7 +294,7 @@ Status InvVSqueeze(Image &input, uint32_t c, uint32_t rc, ThreadPool *pool) {
return true;
}
-Status InvSqueeze(Image &input, std::vector<SqueezeParams> parameters,
+Status InvSqueeze(Image &input, const std::vector<SqueezeParams> &parameters,
ThreadPool *pool) {
for (int i = parameters.size() - 1; i >= 0; i--) {
JXL_RETURN_IF_ERROR(
@@ -340,7 +345,7 @@ HWY_AFTER_NAMESPACE();
namespace jxl {
HWY_EXPORT(InvSqueeze);
-Status InvSqueeze(Image &input, std::vector<SqueezeParams> parameters,
+Status InvSqueeze(Image &input, const std::vector<SqueezeParams> &parameters,
ThreadPool *pool) {
return HWY_DYNAMIC_DISPATCH(InvSqueeze)(input, parameters, pool);
}
@@ -459,8 +464,8 @@ Status MetaSqueeze(Image &image, std::vector<SqueezeParams> *parameters) {
if (image.channel[c].vshift >= 0) image.channel[c].vshift++;
h = h - (h + 1) / 2;
}
- image.channel[c].shrink();
- Channel placeholder(w, h);
+ JXL_RETURN_IF_ERROR(image.channel[c].shrink());
+ JXL_ASSIGN_OR_RETURN(Channel placeholder, Channel::Create(w, h));
placeholder.hshift = image.channel[c].hshift;
placeholder.vshift = image.channel[c].vshift;
diff --git a/third_party/jpeg-xl/lib/jxl/modular/transform/squeeze.h b/third_party/jpeg-xl/lib/jxl/modular/transform/squeeze.h
index 305a0ca3ec..bbd16c59c0 100644
--- a/third_party/jpeg-xl/lib/jxl/modular/transform/squeeze.h
+++ b/third_party/jpeg-xl/lib/jxl/modular/transform/squeeze.h
@@ -81,7 +81,7 @@ Status CheckMetaSqueezeParams(const SqueezeParams &parameter, int num_channels);
Status MetaSqueeze(Image &image, std::vector<SqueezeParams> *parameters);
-Status InvSqueeze(Image &input, std::vector<SqueezeParams> parameters,
+Status InvSqueeze(Image &input, const std::vector<SqueezeParams> &parameters,
ThreadPool *pool);
} // namespace jxl
diff --git a/third_party/jpeg-xl/lib/jxl/modular/transform/transform.h b/third_party/jpeg-xl/lib/jxl/modular/transform/transform.h
index d5d3259f7a..b68861706f 100644
--- a/third_party/jpeg-xl/lib/jxl/modular/transform/transform.h
+++ b/third_party/jpeg-xl/lib/jxl/modular/transform/transform.h
@@ -77,11 +77,13 @@ class Transform : public Fields {
Transform() : Transform(TransformId::kInvalid) {}
Status VisitFields(Visitor *JXL_RESTRICT visitor) override {
- JXL_QUIET_RETURN_IF_ERROR(visitor->U32(
- Val((uint32_t)TransformId::kRCT), Val((uint32_t)TransformId::kPalette),
- Val((uint32_t)TransformId::kSqueeze),
- Val((uint32_t)TransformId::kInvalid), (uint32_t)TransformId::kRCT,
- reinterpret_cast<uint32_t *>(&id)));
+ JXL_QUIET_RETURN_IF_ERROR(
+ visitor->U32(Val(static_cast<uint32_t>(TransformId::kRCT)),
+ Val(static_cast<uint32_t>(TransformId::kPalette)),
+ Val(static_cast<uint32_t>(TransformId::kSqueeze)),
+ Val(static_cast<uint32_t>(TransformId::kInvalid)),
+ static_cast<uint32_t>(TransformId::kRCT),
+ reinterpret_cast<uint32_t *>(&id)));
if (id == TransformId::kInvalid) {
return JXL_FAILURE("Invalid transform ID");
}
@@ -109,7 +111,7 @@ class Transform : public Fields {
visitor->U32(Val(0), BitsOffset(8, 1), BitsOffset(10, 257),
BitsOffset(16, 1281), 0, &nb_deltas));
JXL_QUIET_RETURN_IF_ERROR(
- visitor->Bits(4, (uint32_t)Predictor::Zero,
+ visitor->Bits(4, static_cast<uint32_t>(Predictor::Zero),
reinterpret_cast<uint32_t *>(&predictor)));
if (predictor >= Predictor::Best) {
return JXL_FAILURE("Invalid predictor");
diff --git a/third_party/jpeg-xl/lib/jxl/modular_test.cc b/third_party/jpeg-xl/lib/jxl/modular_test.cc
index 689063ce95..bd1a947493 100644
--- a/third_party/jpeg-xl/lib/jxl/modular_test.cc
+++ b/third_party/jpeg-xl/lib/jxl/modular_test.cc
@@ -7,6 +7,7 @@
#include <jxl/encode.h>
#include <jxl/types.h>
+#include <cmath>
#include <cstddef>
#include <cstdint>
#include <sstream>
@@ -29,7 +30,6 @@
#include "lib/jxl/dec_bit_reader.h"
#include "lib/jxl/enc_aux_out.h"
#include "lib/jxl/enc_bit_writer.h"
-#include "lib/jxl/enc_butteraugli_comparator.h"
#include "lib/jxl/enc_fields.h"
#include "lib/jxl/enc_params.h"
#include "lib/jxl/enc_toc.h"
@@ -54,6 +54,7 @@
namespace jxl {
namespace {
+using test::ButteraugliDistance;
using test::ReadTestData;
using test::Roundtrip;
using test::TestImage;
@@ -130,10 +131,11 @@ TEST(ModularTest, RoundtripLossyDeltaPalette) {
size_t compressed_size;
JXL_EXPECT_OK(Roundtrip(&io, cparams, {}, &io_out, _, &compressed_size));
EXPECT_LE(compressed_size, 6800u);
- EXPECT_THAT(ButteraugliDistance(io.frames, io_out.frames, ButteraugliParams(),
- *JxlGetDefaultCms(),
- /*distmap=*/nullptr),
- IsSlightlyBelow(1.5));
+ EXPECT_SLIGHTLY_BELOW(
+ ButteraugliDistance(io.frames, io_out.frames, ButteraugliParams(),
+ *JxlGetDefaultCms(),
+ /*distmap=*/nullptr),
+ 1.5);
}
TEST(ModularTest, RoundtripLossyDeltaPaletteWP) {
const std::vector<uint8_t> orig =
@@ -153,10 +155,11 @@ TEST(ModularTest, RoundtripLossyDeltaPaletteWP) {
size_t compressed_size;
JXL_EXPECT_OK(Roundtrip(&io, cparams, {}, &io_out, _, &compressed_size));
EXPECT_LE(compressed_size, 7000u);
- EXPECT_THAT(ButteraugliDistance(io.frames, io_out.frames, ButteraugliParams(),
- *JxlGetDefaultCms(),
- /*distmap=*/nullptr),
- IsSlightlyBelow(10.1));
+ EXPECT_SLIGHTLY_BELOW(
+ ButteraugliDistance(io.frames, io_out.frames, ButteraugliParams(),
+ *JxlGetDefaultCms(),
+ /*distmap=*/nullptr),
+ 10.1);
}
TEST(ModularTest, RoundtripLossy) {
@@ -175,10 +178,11 @@ TEST(ModularTest, RoundtripLossy) {
size_t compressed_size;
JXL_EXPECT_OK(Roundtrip(&io, cparams, {}, &io_out, _, &compressed_size));
EXPECT_LE(compressed_size, 30000u);
- EXPECT_THAT(ButteraugliDistance(io.frames, io_out.frames, ButteraugliParams(),
- *JxlGetDefaultCms(),
- /*distmap=*/nullptr),
- IsSlightlyBelow(2.3));
+ EXPECT_SLIGHTLY_BELOW(
+ ButteraugliDistance(io.frames, io_out.frames, ButteraugliParams(),
+ *JxlGetDefaultCms(),
+ /*distmap=*/nullptr),
+ 2.3);
}
TEST(ModularTest, RoundtripLossy16) {
@@ -201,15 +205,17 @@ TEST(ModularTest, RoundtripLossy16) {
size_t compressed_size;
JXL_EXPECT_OK(Roundtrip(&io, cparams, {}, &io_out, _, &compressed_size));
EXPECT_LE(compressed_size, 300u);
- EXPECT_THAT(ButteraugliDistance(io.frames, io_out.frames, ButteraugliParams(),
- *JxlGetDefaultCms(),
- /*distmap=*/nullptr),
- IsSlightlyBelow(1.6));
+ EXPECT_SLIGHTLY_BELOW(
+ ButteraugliDistance(io.frames, io_out.frames, ButteraugliParams(),
+ *JxlGetDefaultCms(),
+ /*distmap=*/nullptr),
+ 1.6);
}
TEST(ModularTest, RoundtripExtraProperties) {
constexpr size_t kSize = 250;
- Image image(kSize, kSize, /*bitdepth=*/8, 3);
+ JXL_ASSIGN_OR_DIE(Image image,
+ Image::Create(kSize, kSize, /*bitdepth=*/8, 3));
ModularOptions options;
options.max_properties = 4;
options.predictor = Predictor::Zero;
@@ -224,10 +230,12 @@ TEST(ModularTest, RoundtripExtraProperties) {
BitWriter writer;
ASSERT_TRUE(ModularGenericCompress(image, options, &writer));
writer.ZeroPadToByte();
- Image decoded(kSize, kSize, /*bitdepth=*/8, image.channel.size());
+ JXL_ASSIGN_OR_DIE(Image decoded, Image::Create(kSize, kSize, /*bitdepth=*/8,
+ image.channel.size()));
for (size_t i = 0; i < image.channel.size(); i++) {
const Channel& ch = image.channel[i];
- decoded.channel[i] = Channel(ch.w, ch.h, ch.hshift, ch.vshift);
+ JXL_ASSIGN_OR_DIE(decoded.channel[i],
+ Channel::Create(ch.w, ch.h, ch.hshift, ch.vshift));
}
Status status = true;
{
@@ -302,7 +310,7 @@ TEST_P(ModularTestParam, RoundtripLossless) {
double factor = ((1lu << bitdepth) - 1lu);
double ifactor = 1.0 / factor;
- Image3F noise_added(xsize, ysize);
+ JXL_ASSIGN_OR_DIE(Image3F noise_added, Image3F::Create(xsize, ysize));
for (size_t c = 0; c < 3; c++) {
for (size_t y = 0; y < ysize; y++) {
@@ -313,7 +321,7 @@ TEST_P(ModularTestParam, RoundtripLossless) {
float f = in[x] + generator.UniformF(0.0f, 1.f / 255.f);
if (f > 1.f) f = 1.f;
// quantize to the bitdepth we're testing
- unsigned int u = f * factor + 0.5;
+ unsigned int u = static_cast<unsigned int>(std::lround(f * factor));
out[x] = u * ifactor;
}
}
@@ -330,7 +338,7 @@ TEST_P(ModularTestParam, RoundtripLossless) {
CodecInOut io2;
size_t compressed_size;
JXL_EXPECT_OK(Roundtrip(&io, cparams, {}, &io2, _, &compressed_size));
- EXPECT_LE(compressed_size, bitdepth * xsize * ysize / 3);
+ EXPECT_LE(compressed_size, bitdepth * xsize * ysize / 3.0 * 1.1);
EXPECT_LE(0, ComputeDistance2(io.Main(), io2.Main(), *JxlGetDefaultCms()));
size_t different = 0;
for (size_t c = 0; c < 3; c++) {
@@ -350,7 +358,8 @@ TEST_P(ModularTestParam, RoundtripLossless) {
TEST(ModularTest, RoundtripLosslessCustomFloat) {
CodecInOut io;
- size_t xsize = 100, ysize = 300;
+ size_t xsize = 100;
+ size_t ysize = 300;
io.SetSize(xsize, ysize);
io.metadata.m.bit_depth.bits_per_sample = 18;
io.metadata.m.bit_depth.exponent_bits_per_sample = 6;
@@ -359,7 +368,7 @@ TEST(ModularTest, RoundtripLosslessCustomFloat) {
ColorEncoding color_encoding;
color_encoding.Tf().SetTransferFunction(TransferFunction::kLinear);
color_encoding.SetColorSpace(ColorSpace::kRGB);
- Image3F testimage(xsize, ysize);
+ JXL_ASSIGN_OR_DIE(Image3F testimage, Image3F::Create(xsize, ysize));
float factor = 1.f / (1 << 14);
for (size_t c = 0; c < 3; c++) {
for (size_t y = 0; y < ysize; y++) {
@@ -442,7 +451,7 @@ TEST(ModularTest, PredictorIntegerOverflow) {
WriteHeaders(&writer, xsize, ysize);
std::vector<BitWriter> group_codes(1);
{
- BitWriter* bw = &group_codes[0];
+ BitWriter* bw = group_codes.data();
BitWriter::Allotment allotment(bw, 1 << 20);
WriteHistograms(bw);
GroupHeader header;
@@ -466,7 +475,7 @@ TEST(ModularTest, PredictorIntegerOverflow) {
nullptr, &ppf));
ASSERT_EQ(1, ppf.frames.size());
const auto& img = ppf.frames[0].color;
- const auto pixels = reinterpret_cast<const float*>(img.pixels());
+ const auto* pixels = reinterpret_cast<const float*>(img.pixels());
EXPECT_EQ(-1.0f, pixels[0]);
}
@@ -478,7 +487,7 @@ TEST(ModularTest, UnsqueezeIntegerOverflow) {
WriteHeaders(&writer, xsize, ysize);
std::vector<BitWriter> group_codes(1);
{
- BitWriter* bw = &group_codes[0];
+ BitWriter* bw = group_codes.data();
BitWriter::Allotment allotment(bw, 1 << 20);
WriteHistograms(bw);
GroupHeader header;
@@ -514,7 +523,7 @@ TEST(ModularTest, UnsqueezeIntegerOverflow) {
nullptr, &ppf));
ASSERT_EQ(1, ppf.frames.size());
const auto& img = ppf.frames[0].color;
- const auto pixels = reinterpret_cast<const float*>(img.pixels());
+ const float* pixels = reinterpret_cast<const float*>(img.pixels());
for (size_t x = 0; x < xsize; ++x) {
EXPECT_NEAR(-0.5f, pixels[x], 1e-10);
EXPECT_NEAR(0.5f, pixels[xsize + x], 1e-10);
diff --git a/third_party/jpeg-xl/lib/jxl/noise.h b/third_party/jpeg-xl/lib/jxl/noise.h
index 585fab0d42..c588c59d33 100644
--- a/third_party/jpeg-xl/lib/jxl/noise.h
+++ b/third_party/jpeg-xl/lib/jxl/noise.h
@@ -11,6 +11,7 @@
#include <stddef.h>
#include <algorithm>
+#include <array>
#include <cmath>
#include <utility>
@@ -23,7 +24,9 @@ const float kNoisePrecision = 1 << 10;
struct NoiseParams {
// LUT index is an intensity of pixel / mean intensity of patch
static constexpr size_t kNumNoisePoints = 8;
- float lut[kNumNoisePoints];
+ using Lut = std::array<float, kNumNoisePoints>;
+
+ Lut lut;
void Clear() {
for (float& i : lut) i = 0.f;
@@ -39,7 +42,7 @@ struct NoiseParams {
static inline std::pair<int, float> IndexAndFrac(float x) {
constexpr size_t kScaleNumerator = NoiseParams::kNumNoisePoints - 2;
// TODO(user): instead of 1, this should be a proper Y range.
- constexpr float kScale = kScaleNumerator / 1;
+ constexpr float kScale = kScaleNumerator / 1.0f;
float scaled_x = std::max(0.f, x * kScale);
float floor_x;
float frac_x = std::modf(scaled_x, &floor_x);
diff --git a/third_party/jpeg-xl/lib/jxl/opsin_image_test.cc b/third_party/jpeg-xl/lib/jxl/opsin_image_test.cc
index f7842c32e4..b8ea839b9e 100644
--- a/third_party/jpeg-xl/lib/jxl/opsin_image_test.cc
+++ b/third_party/jpeg-xl/lib/jxl/opsin_image_test.cc
@@ -27,7 +27,7 @@ namespace {
void LinearSrgbToOpsin(float rgb_r, float rgb_g, float rgb_b,
float* JXL_RESTRICT xyb_x, float* JXL_RESTRICT xyb_y,
float* JXL_RESTRICT xyb_b) {
- Image3F linear(1, 1);
+ JXL_ASSIGN_OR_DIE(Image3F linear, Image3F::Create(1, 1));
linear.PlaneRow(0, 0)[0] = rgb_r;
linear.PlaneRow(1, 0)[0] = rgb_g;
linear.PlaneRow(2, 0)[0] = rgb_b;
@@ -37,7 +37,7 @@ void LinearSrgbToOpsin(float rgb_r, float rgb_g, float rgb_b,
metadata.color_encoding = ColorEncoding::LinearSRGB();
ImageBundle ib(&metadata);
ib.SetFromImage(std::move(linear), metadata.color_encoding);
- Image3F opsin(1, 1);
+ JXL_ASSIGN_OR_DIE(Image3F opsin, Image3F::Create(1, 1));
(void)ToXYB(ib, /*pool=*/nullptr, &opsin, *JxlGetDefaultCms());
*xyb_x = opsin.PlaneRow(0, 0)[0];
@@ -50,11 +50,11 @@ void LinearSrgbToOpsin(float rgb_r, float rgb_g, float rgb_b,
void OpsinToLinearSrgb(float xyb_x, float xyb_y, float xyb_b,
float* JXL_RESTRICT rgb_r, float* JXL_RESTRICT rgb_g,
float* JXL_RESTRICT rgb_b) {
- Image3F opsin(1, 1);
+ JXL_ASSIGN_OR_DIE(Image3F opsin, Image3F::Create(1, 1));
opsin.PlaneRow(0, 0)[0] = xyb_x;
opsin.PlaneRow(1, 0)[0] = xyb_y;
opsin.PlaneRow(2, 0)[0] = xyb_b;
- Image3F linear(1, 1);
+ JXL_ASSIGN_OR_DIE(Image3F linear, Image3F::Create(1, 1));
OpsinParams opsin_params;
opsin_params.Init(/*intensity_target=*/255.0f);
OpsinToLinear(opsin, Rect(opsin), nullptr, &linear, opsin_params);
@@ -64,9 +64,13 @@ void OpsinToLinearSrgb(float xyb_x, float xyb_y, float xyb_b,
}
void OpsinRoundtripTestRGB(float r, float g, float b) {
- float xyb_x, xyb_y, xyb_b;
+ float xyb_x;
+ float xyb_y;
+ float xyb_b;
LinearSrgbToOpsin(r, g, b, &xyb_x, &xyb_y, &xyb_b);
- float r2, g2, b2;
+ float r2;
+ float g2;
+ float b2;
OpsinToLinearSrgb(xyb_x, xyb_y, xyb_b, &r2, &g2, &b2);
EXPECT_NEAR(r, r2, 1e-3);
EXPECT_NEAR(g, g2, 1e-3);
@@ -74,13 +78,13 @@ void OpsinRoundtripTestRGB(float r, float g, float b) {
}
TEST(OpsinImageTest, VerifyOpsinAbsorbanceInverseMatrix) {
- float matrix[9]; // writable copy
- for (int i = 0; i < 9; i++) {
- matrix[i] = GetOpsinAbsorbanceInverseMatrix()[i];
- }
+ Matrix3x3 matrix; // writable copy
+ matrix = GetOpsinAbsorbanceInverseMatrix();
EXPECT_TRUE(Inv3x3Matrix(matrix));
- for (int i = 0; i < 9; i++) {
- EXPECT_NEAR(matrix[i], jxl::cms::kOpsinAbsorbanceMatrix[i], 1e-6);
+ for (int j = 0; j < 3; j++) {
+ for (int i = 0; i < 3; i++) {
+ EXPECT_NEAR(matrix[j][i], jxl::cms::kOpsinAbsorbanceMatrix[j][i], 1e-6);
+ }
}
}
@@ -105,7 +109,9 @@ TEST(OpsinImageTest, OpsinRoundtrip) {
TEST(OpsinImageTest, VerifyZero) {
// Test that black color (zero energy) is 0,0,0 in xyb.
- float x, y, b;
+ float x;
+ float y;
+ float b;
LinearSrgbToOpsin(0, 0, 0, &x, &y, &b);
EXPECT_NEAR(0, x, 1e-9);
EXPECT_NEAR(0, y, 1e-7);
@@ -115,7 +121,9 @@ TEST(OpsinImageTest, VerifyZero) {
TEST(OpsinImageTest, VerifyGray) {
// Test that grayscale colors have a fixed y/b ratio and x==0.
for (size_t i = 1; i < 255; i++) {
- float x, y, b;
+ float x;
+ float y;
+ float b;
LinearSrgbToOpsin(i / 255., i / 255., i / 255., &x, &y, &b);
EXPECT_NEAR(0, x, 1e-6);
EXPECT_NEAR(jxl::cms::kYToBRatio, b / y, 3e-5);
diff --git a/third_party/jpeg-xl/lib/jxl/opsin_inverse_test.cc b/third_party/jpeg-xl/lib/jxl/opsin_inverse_test.cc
index b8c151fbea..b8f9aa13df 100644
--- a/third_party/jpeg-xl/lib/jxl/opsin_inverse_test.cc
+++ b/third_party/jpeg-xl/lib/jxl/opsin_inverse_test.cc
@@ -21,17 +21,17 @@ namespace jxl {
namespace {
TEST(OpsinInverseTest, LinearInverseInverts) {
- Image3F linear(128, 128);
+ JXL_ASSIGN_OR_DIE(Image3F linear, Image3F::Create(128, 128));
RandomFillImage(&linear, 0.0f, 1.0f);
CodecInOut io;
io.metadata.m.SetFloat32Samples();
io.metadata.m.color_encoding = ColorEncoding::LinearSRGB();
- Image3F linear2(128, 128);
+ JXL_ASSIGN_OR_DIE(Image3F linear2, Image3F::Create(128, 128));
CopyImageTo(linear, &linear2);
io.SetFromImage(std::move(linear2), io.metadata.m.color_encoding);
ThreadPool* null_pool = nullptr;
- Image3F opsin(io.xsize(), io.ysize());
+ JXL_ASSIGN_OR_DIE(Image3F opsin, Image3F::Create(io.xsize(), io.ysize()));
(void)ToXYB(io.Main(), null_pool, &opsin, *JxlGetDefaultCms());
OpsinParams opsin_params;
@@ -42,16 +42,16 @@ TEST(OpsinInverseTest, LinearInverseInverts) {
}
TEST(OpsinInverseTest, YcbCrInverts) {
- Image3F rgb(128, 128);
+ JXL_ASSIGN_OR_DIE(Image3F rgb, Image3F::Create(128, 128));
RandomFillImage(&rgb, 0.0f, 1.0f);
ThreadPool* null_pool = nullptr;
- Image3F ycbcr(rgb.xsize(), rgb.ysize());
+ JXL_ASSIGN_OR_DIE(Image3F ycbcr, Image3F::Create(rgb.xsize(), rgb.ysize()));
EXPECT_TRUE(RgbToYcbcr(rgb.Plane(0), rgb.Plane(1), rgb.Plane(2),
&ycbcr.Plane(1), &ycbcr.Plane(0), &ycbcr.Plane(2),
null_pool));
- Image3F rgb2(rgb.xsize(), rgb.ysize());
+ JXL_ASSIGN_OR_DIE(Image3F rgb2, Image3F::Create(rgb.xsize(), rgb.ysize()));
YcbcrToRgb(ycbcr, &rgb2, Rect(rgb));
JXL_ASSERT_OK(VerifyRelativeError(rgb, rgb2, 4E-5, 4E-7, _));
diff --git a/third_party/jpeg-xl/lib/jxl/opsin_params.cc b/third_party/jpeg-xl/lib/jxl/opsin_params.cc
index e1fdda5322..8aae4e3597 100644
--- a/third_party/jpeg-xl/lib/jxl/opsin_params.cc
+++ b/third_party/jpeg-xl/lib/jxl/opsin_params.cc
@@ -9,24 +9,19 @@
#define INVERSE_OPSIN_FROM_SPEC 1
-#if not(INVERSE_OPSIN_FROM_SPEC)
#include "lib/jxl/base/matrix_ops.h"
-#endif
namespace jxl {
-const float* GetOpsinAbsorbanceInverseMatrix() {
+const Matrix3x3& GetOpsinAbsorbanceInverseMatrix() {
#if INVERSE_OPSIN_FROM_SPEC
return jxl::cms::DefaultInverseOpsinAbsorbanceMatrix();
#else // INVERSE_OPSIN_FROM_SPEC
// Compute the inverse opsin matrix from the forward matrix. Less precise
// than taking the values from the specification, but must be used if the
// forward transform is changed and the spec will require updating.
- static const float* const kInverse = [] {
- static float inverse[9];
- for (int i = 0; i < 9; i++) {
- inverse[i] = kOpsinAbsorbanceMatrix[i];
- }
+ static const Matrix3x3 const kInverse = [] {
+ static Matrix3x3 inverse = kOpsinAbsorbanceMatrix;
Inv3x3Matrix(inverse);
return inverse;
}();
@@ -34,12 +29,15 @@ const float* GetOpsinAbsorbanceInverseMatrix() {
#endif // INVERSE_OPSIN_FROM_SPEC
}
-void InitSIMDInverseMatrix(const float* JXL_RESTRICT inverse,
+void InitSIMDInverseMatrix(const Matrix3x3& inverse,
float* JXL_RESTRICT simd_inverse,
float intensity_target) {
- for (size_t i = 0; i < 9; ++i) {
- simd_inverse[4 * i] = simd_inverse[4 * i + 1] = simd_inverse[4 * i + 2] =
- simd_inverse[4 * i + 3] = inverse[i] * (255.0f / intensity_target);
+ for (size_t j = 0; j < 3; ++j) {
+ for (size_t i = 0; i < 3; ++i) {
+ size_t idx = (j * 3 + i) * 4;
+ simd_inverse[idx] = simd_inverse[idx + 1] = simd_inverse[idx + 2] =
+ simd_inverse[idx + 3] = inverse[j][i] * (255.0f / intensity_target);
+ }
}
}
diff --git a/third_party/jpeg-xl/lib/jxl/opsin_params.h b/third_party/jpeg-xl/lib/jxl/opsin_params.h
index fc285ac208..4ed2124fd7 100644
--- a/third_party/jpeg-xl/lib/jxl/opsin_params.h
+++ b/third_party/jpeg-xl/lib/jxl/opsin_params.h
@@ -9,14 +9,15 @@
// Constants that define the XYB color space.
#include "lib/jxl/base/compiler_specific.h"
+#include "lib/jxl/base/matrix_ops.h"
namespace jxl {
// Returns 3x3 row-major matrix inverse of kOpsinAbsorbanceMatrix.
// opsin_image_test verifies this is actually the inverse.
-const float* GetOpsinAbsorbanceInverseMatrix();
+const Matrix3x3& GetOpsinAbsorbanceInverseMatrix();
-void InitSIMDInverseMatrix(const float* JXL_RESTRICT inverse,
+void InitSIMDInverseMatrix(const Matrix3x3& inverse,
float* JXL_RESTRICT simd_inverse,
float intensity_target);
diff --git a/third_party/jpeg-xl/lib/jxl/padded_bytes.h b/third_party/jpeg-xl/lib/jxl/padded_bytes.h
index 0d696475fa..38167ed408 100644
--- a/third_party/jpeg-xl/lib/jxl/padded_bytes.h
+++ b/third_party/jpeg-xl/lib/jxl/padded_bytes.h
@@ -71,7 +71,7 @@ class PaddedBytes {
return *this;
}
- void swap(PaddedBytes& other) {
+ void swap(PaddedBytes& other) noexcept {
std::swap(size_, other.size_);
std::swap(capacity_, other.capacity_);
std::swap(data_, other.data_);
diff --git a/third_party/jpeg-xl/lib/jxl/passes_state.cc b/third_party/jpeg-xl/lib/jxl/passes_state.cc
index 12cc6a0c93..5da731b48e 100644
--- a/third_party/jpeg-xl/lib/jxl/passes_state.cc
+++ b/third_party/jpeg-xl/lib/jxl/passes_state.cc
@@ -5,6 +5,7 @@
#include "lib/jxl/passes_state.h"
+#include "lib/jxl/base/status.h"
#include "lib/jxl/chroma_from_luma.h"
#include "lib/jxl/coeff_order.h"
#include "lib/jxl/frame_dimensions.h"
@@ -21,13 +22,17 @@ Status InitializePassesSharedState(const FrameHeader& frame_header,
const FrameDimensions& frame_dim = shared->frame_dim;
- shared->ac_strategy =
- AcStrategyImage(frame_dim.xsize_blocks, frame_dim.ysize_blocks);
- shared->raw_quant_field =
- ImageI(frame_dim.xsize_blocks, frame_dim.ysize_blocks);
- shared->epf_sharpness =
- ImageB(frame_dim.xsize_blocks, frame_dim.ysize_blocks);
- shared->cmap = ColorCorrelationMap(frame_dim.xsize, frame_dim.ysize);
+ JXL_ASSIGN_OR_RETURN(
+ shared->ac_strategy,
+ AcStrategyImage::Create(frame_dim.xsize_blocks, frame_dim.ysize_blocks));
+ JXL_ASSIGN_OR_RETURN(
+ shared->raw_quant_field,
+ ImageI::Create(frame_dim.xsize_blocks, frame_dim.ysize_blocks));
+ JXL_ASSIGN_OR_RETURN(
+ shared->epf_sharpness,
+ ImageB::Create(frame_dim.xsize_blocks, frame_dim.ysize_blocks));
+ JXL_ASSIGN_OR_RETURN(shared->cmap, ColorCorrelationMap::Create(
+ frame_dim.xsize, frame_dim.ysize));
// In the decoder, we allocate coeff orders afterwards, when we know how many
// we will actually need.
@@ -40,9 +45,11 @@ Status InitializePassesSharedState(const FrameHeader& frame_header,
kCoeffOrderMaxSize);
}
- shared->quant_dc = ImageB(frame_dim.xsize_blocks, frame_dim.ysize_blocks);
+ JXL_ASSIGN_OR_RETURN(
+ shared->quant_dc,
+ ImageB::Create(frame_dim.xsize_blocks, frame_dim.ysize_blocks));
- bool use_dc_frame = !!(frame_header.flags & FrameHeader::kUseDcFrame);
+ bool use_dc_frame = ((frame_header.flags & FrameHeader::kUseDcFrame) != 0u);
if (!encoder && use_dc_frame) {
if (frame_header.dc_level == 4) {
return JXL_FAILURE("Invalid DC level for kUseDcFrame: %u",
@@ -58,8 +65,9 @@ Status InitializePassesSharedState(const FrameHeader& frame_header,
}
ZeroFillImage(&shared->quant_dc);
} else {
- shared->dc_storage =
- Image3F(frame_dim.xsize_blocks, frame_dim.ysize_blocks);
+ JXL_ASSIGN_OR_RETURN(
+ shared->dc_storage,
+ Image3F::Create(frame_dim.xsize_blocks, frame_dim.ysize_blocks));
shared->dc = &shared->dc_storage;
}
diff --git a/third_party/jpeg-xl/lib/jxl/passes_test.cc b/third_party/jpeg-xl/lib/jxl/passes_test.cc
index a47134cd00..cb9164706f 100644
--- a/third_party/jpeg-xl/lib/jxl/passes_test.cc
+++ b/third_party/jpeg-xl/lib/jxl/passes_test.cc
@@ -17,7 +17,6 @@
#include "lib/jxl/base/data_parallel.h"
#include "lib/jxl/base/override.h"
#include "lib/jxl/base/span.h"
-#include "lib/jxl/enc_butteraugli_comparator.h"
#include "lib/jxl/enc_params.h"
#include "lib/jxl/image.h"
#include "lib/jxl/image_bundle.h"
@@ -27,6 +26,7 @@
namespace jxl {
+using test::ButteraugliDistance;
using test::ReadTestData;
using test::Roundtrip;
using test::ThreadPoolForTests;
@@ -47,10 +47,11 @@ TEST(PassesTest, RoundtripSmallPasses) {
CodecInOut io2;
JXL_EXPECT_OK(Roundtrip(&io, cparams, {}, &io2, _));
- EXPECT_THAT(ButteraugliDistance(io.frames, io2.frames, ButteraugliParams(),
- *JxlGetDefaultCms(),
- /*distmap=*/nullptr),
- IsSlightlyBelow(0.8222));
+ EXPECT_SLIGHTLY_BELOW(
+ ButteraugliDistance(io.frames, io2.frames, ButteraugliParams(),
+ *JxlGetDefaultCms(),
+ /*distmap=*/nullptr),
+ 0.8222);
}
TEST(PassesTest, RoundtripUnalignedPasses) {
@@ -67,10 +68,11 @@ TEST(PassesTest, RoundtripUnalignedPasses) {
CodecInOut io2;
JXL_EXPECT_OK(Roundtrip(&io, cparams, {}, &io2, _));
- EXPECT_THAT(ButteraugliDistance(io.frames, io2.frames, ButteraugliParams(),
- *JxlGetDefaultCms(),
- /*distmap=*/nullptr),
- IsSlightlyBelow(1.72));
+ EXPECT_SLIGHTLY_BELOW(
+ ButteraugliDistance(io.frames, io2.frames, ButteraugliParams(),
+ *JxlGetDefaultCms(),
+ /*distmap=*/nullptr),
+ 1.72);
}
TEST(PassesTest, RoundtripMultiGroupPasses) {
@@ -91,10 +93,11 @@ TEST(PassesTest, RoundtripMultiGroupPasses) {
CodecInOut io2;
JXL_EXPECT_OK(Roundtrip(&io, cparams, {}, &io2, _,
/* compressed_size */ nullptr, &pool));
- EXPECT_THAT(ButteraugliDistance(io.frames, io2.frames, ButteraugliParams(),
- *JxlGetDefaultCms(),
- /*distmap=*/nullptr, &pool),
- IsSlightlyBelow(target_distance + threshold));
+ EXPECT_SLIGHTLY_BELOW(
+ ButteraugliDistance(io.frames, io2.frames, ButteraugliParams(),
+ *JxlGetDefaultCms(),
+ /*distmap=*/nullptr, &pool),
+ target_distance + threshold);
};
auto run1 = std::async(std::launch::async, test, 1.0f, 0.15f);
@@ -256,7 +259,7 @@ TEST(PassesTest, ProgressiveDownsample2DegradesCorrectlyGrayscale) {
ASSERT_TRUE(SetFromBytes(Bytes(orig), &io_orig, &pool));
Rect rect(0, 0, io_orig.xsize(), 128);
// need 2 DC groups for the DC frame to actually be progressive.
- Image3F large(4242, rect.ysize());
+ JXL_ASSIGN_OR_DIE(Image3F large, Image3F::Create(4242, rect.ysize()));
ZeroFillImage(&large);
CopyImageTo(rect, *io_orig.Main().color(), rect, &large);
CodecInOut io;
@@ -268,7 +271,7 @@ TEST(PassesTest, ProgressiveDownsample2DegradesCorrectlyGrayscale) {
CompressParams cparams;
cparams.speed_tier = SpeedTier::kSquirrel;
cparams.progressive_dc = 1;
- cparams.responsive = true;
+ cparams.responsive = JXL_TRUE;
cparams.qprogressive_mode = Override::kOn;
cparams.butteraugli_distance = 1.0;
ASSERT_TRUE(test::EncodeFile(cparams, &io, &compressed, &pool));
@@ -300,7 +303,7 @@ TEST(PassesTest, ProgressiveDownsample2DegradesCorrectly) {
ASSERT_TRUE(SetFromBytes(Bytes(orig), &io_orig, &pool));
Rect rect(0, 0, io_orig.xsize(), 128);
// need 2 DC groups for the DC frame to actually be progressive.
- Image3F large(4242, rect.ysize());
+ JXL_ASSIGN_OR_DIE(Image3F large, Image3F::Create(4242, rect.ysize()));
ZeroFillImage(&large);
CopyImageTo(rect, *io_orig.Main().color(), rect, &large);
CodecInOut io;
@@ -311,7 +314,7 @@ TEST(PassesTest, ProgressiveDownsample2DegradesCorrectly) {
CompressParams cparams;
cparams.speed_tier = SpeedTier::kSquirrel;
cparams.progressive_dc = 1;
- cparams.responsive = true;
+ cparams.responsive = JXL_TRUE;
cparams.qprogressive_mode = Override::kOn;
cparams.butteraugli_distance = 1.0;
ASSERT_TRUE(test::EncodeFile(cparams, &io, &compressed, &pool));
@@ -375,10 +378,11 @@ TEST(PassesTest, RoundtripSmallNoGaborishPasses) {
CodecInOut io2;
JXL_EXPECT_OK(Roundtrip(&io, cparams, {}, &io2, _));
- EXPECT_THAT(ButteraugliDistance(io.frames, io2.frames, ButteraugliParams(),
- *JxlGetDefaultCms(),
- /*distmap=*/nullptr),
- IsSlightlyBelow(1.2));
+ EXPECT_SLIGHTLY_BELOW(
+ ButteraugliDistance(io.frames, io2.frames, ButteraugliParams(),
+ *JxlGetDefaultCms(),
+ /*distmap=*/nullptr),
+ 1.2);
}
} // namespace
diff --git a/third_party/jpeg-xl/lib/jxl/patch_dictionary_test.cc b/third_party/jpeg-xl/lib/jxl/patch_dictionary_test.cc
index 60f7c32229..fd04b7fc2e 100644
--- a/third_party/jpeg-xl/lib/jxl/patch_dictionary_test.cc
+++ b/third_party/jpeg-xl/lib/jxl/patch_dictionary_test.cc
@@ -12,7 +12,6 @@
#include "lib/extras/codec.h"
#include "lib/jxl/base/override.h"
#include "lib/jxl/base/span.h"
-#include "lib/jxl/enc_butteraugli_comparator.h"
#include "lib/jxl/enc_params.h"
#include "lib/jxl/image_test_utils.h"
#include "lib/jxl/test_utils.h"
@@ -21,6 +20,7 @@
namespace jxl {
namespace {
+using test::ButteraugliDistance;
using test::ReadTestData;
using test::Roundtrip;
diff --git a/third_party/jpeg-xl/lib/jxl/preview_test.cc b/third_party/jpeg-xl/lib/jxl/preview_test.cc
index b7fe855d4d..c482db9fd8 100644
--- a/third_party/jpeg-xl/lib/jxl/preview_test.cc
+++ b/third_party/jpeg-xl/lib/jxl/preview_test.cc
@@ -13,7 +13,6 @@
#include "lib/extras/codec.h"
#include "lib/jxl/base/span.h"
#include "lib/jxl/codec_in_out.h"
-#include "lib/jxl/enc_butteraugli_comparator.h"
#include "lib/jxl/enc_params.h"
#include "lib/jxl/headers.h"
#include "lib/jxl/image_bundle.h"
@@ -22,6 +21,7 @@
namespace jxl {
namespace {
+using test::ButteraugliDistance;
using test::ReadTestData;
using test::Roundtrip;
@@ -32,7 +32,7 @@ TEST(PreviewTest, RoundtripGivenPreview) {
ASSERT_TRUE(SetFromBytes(Bytes(orig), &io));
io.ShrinkTo(io.xsize() / 8, io.ysize() / 8);
// Same as main image
- io.preview_frame = io.Main().Copy();
+ JXL_ASSIGN_OR_DIE(io.preview_frame, io.Main().Copy());
const size_t preview_xsize = 15;
const size_t preview_ysize = 27;
io.preview_frame.ShrinkTo(preview_xsize, preview_ysize);
diff --git a/third_party/jpeg-xl/lib/jxl/quant_weights.cc b/third_party/jpeg-xl/lib/jxl/quant_weights.cc
index 70b3b9e451..00563c5cbd 100644
--- a/third_party/jpeg-xl/lib/jxl/quant_weights.cc
+++ b/third_party/jpeg-xl/lib/jxl/quant_weights.cc
@@ -160,8 +160,8 @@ Status ComputeQuantTable(const QuantEncoding& encoding,
float* JXL_RESTRICT inv_table, size_t table_num,
DequantMatrices::QuantTable kind, size_t* pos) {
constexpr size_t N = kBlockDim;
- size_t wrows = 8 * DequantMatrices::required_size_x[kind],
- wcols = 8 * DequantMatrices::required_size_y[kind];
+ size_t wrows = 8 * DequantMatrices::required_size_x[kind];
+ size_t wcols = 8 * DequantMatrices::required_size_y[kind];
size_t num = wrows * wcols;
std::vector<float> weights(3 * num);
@@ -361,7 +361,7 @@ namespace {
HWY_EXPORT(ComputeQuantTable);
-static constexpr const float kAlmostZero = 1e-8f;
+constexpr const float kAlmostZero = 1e-8f;
Status DecodeDctParams(BitReader* br, DctQuantWeightParams* params) {
params->num_distance_bands =
@@ -474,16 +474,16 @@ Status Decode(BitReader* br, QuantEncoding* encoding, size_t required_size_x,
default:
return JXL_FAILURE("Invalid quantization table encoding");
}
- encoding->mode = QuantEncoding::Mode(mode);
+ encoding->mode = static_cast<QuantEncoding::Mode>(mode);
return true;
}
} // namespace
// These definitions are needed before C++17.
-constexpr size_t DequantMatrices::required_size_[];
-constexpr size_t DequantMatrices::required_size_x[];
-constexpr size_t DequantMatrices::required_size_y[];
+constexpr const std::array<int, 17> DequantMatrices::required_size_x;
+constexpr const std::array<int, 17> DequantMatrices::required_size_y;
+constexpr const size_t DequantMatrices::kSumRequiredXy;
constexpr DequantMatrices::QuantTable DequantMatrices::kQuantTable[];
Status DequantMatrices::Decode(BitReader* br,
@@ -502,7 +502,7 @@ Status DequantMatrices::Decode(BitReader* br,
}
Status DequantMatrices::DecodeDC(BitReader* br) {
- bool all_default = br->ReadBits(1);
+ bool all_default = static_cast<bool>(br->ReadBits(1));
if (!br->AllReadsWithinBounds()) return JXL_FAILURE("EOS during DecodeDC");
if (!all_default) {
for (size_t c = 0; c < 3; c++) {
@@ -1162,11 +1162,12 @@ const QuantEncoding* DequantMatrices::Library() {
}
DequantMatrices::DequantMatrices() {
- encodings_.resize(size_t(QuantTable::kNum), QuantEncoding::Library(0));
+ encodings_.resize(static_cast<size_t>(QuantTable::kNum),
+ QuantEncoding::Library(0));
size_t pos = 0;
size_t offsets[kNum * 3];
- for (size_t i = 0; i < size_t(QuantTable::kNum); i++) {
- size_t num = required_size_[i] * kDCTBlockSize;
+ for (size_t i = 0; i < static_cast<size_t>(QuantTable::kNum); i++) {
+ size_t num = required_size_x[i] * required_size_y[i] * kDCTBlockSize;
for (size_t c = 0; c < 3; c++) {
offsets[3 * i + c] = pos + c * num;
}
@@ -1191,7 +1192,7 @@ Status DequantMatrices::EnsureComputed(uint32_t acs_mask) {
size_t offsets[kNum * 3 + 1];
size_t pos = 0;
for (size_t i = 0; i < kNum; i++) {
- size_t num = required_size_[i] * kDCTBlockSize;
+ size_t num = required_size_x[i] * required_size_y[i] * kDCTBlockSize;
for (size_t c = 0; c < 3; c++) {
offsets[3 * i + c] = pos + c * num;
}
diff --git a/third_party/jpeg-xl/lib/jxl/quant_weights.h b/third_party/jpeg-xl/lib/jxl/quant_weights.h
index 3004176aba..0fa23ffddb 100644
--- a/third_party/jpeg-xl/lib/jxl/quant_weights.h
+++ b/third_party/jpeg-xl/lib/jxl/quant_weights.h
@@ -15,6 +15,7 @@
#include <vector>
#include "lib/jxl/ac_strategy.h"
+#include "lib/jxl/base/common.h"
#include "lib/jxl/base/compiler_specific.h"
#include "lib/jxl/base/span.h"
#include "lib/jxl/base/status.h"
@@ -23,12 +24,6 @@
namespace jxl {
-template <typename T, size_t N>
-constexpr T ArraySum(T (&a)[N], size_t i = N - 1) {
- static_assert(N > 0, "Trying to compute the sum of an empty array");
- return i == 0 ? a[0] : a[i] + ArraySum(a, i - 1);
-}
-
static constexpr size_t kMaxQuantTableSize = AcStrategy::kMaxCoeffArea;
static constexpr size_t kNumPredefinedTables = 1;
static constexpr size_t kCeilLog2NumPredefinedTables = 0;
@@ -410,25 +405,23 @@ class DequantMatrices {
const std::vector<QuantEncoding>& encodings() const { return encodings_; }
- static constexpr size_t required_size_x[] = {1, 1, 1, 1, 2, 4, 1, 1, 2,
- 1, 1, 8, 4, 16, 8, 32, 16};
- static_assert(kNum == sizeof(required_size_x) / sizeof(*required_size_x),
+ static constexpr auto required_size_x =
+ to_array<int>({1, 1, 1, 1, 2, 4, 1, 1, 2, 1, 1, 8, 4, 16, 8, 32, 16});
+ static_assert(kNum == required_size_x.size(),
"Update this array when adding or removing quant tables.");
- static constexpr size_t required_size_y[] = {1, 1, 1, 1, 2, 4, 2, 4, 4,
- 1, 1, 8, 8, 16, 16, 32, 32};
- static_assert(kNum == sizeof(required_size_y) / sizeof(*required_size_y),
+ static constexpr auto required_size_y =
+ to_array<int>({1, 1, 1, 1, 2, 4, 2, 4, 4, 1, 1, 8, 8, 16, 16, 32, 32});
+ static_assert(kNum == required_size_y.size(),
"Update this array when adding or removing quant tables.");
+ // MUST be equal `sum(dot(required_size_x, required_size_y))`.
+ static constexpr size_t kSumRequiredXy = 2056;
+
Status EnsureComputed(uint32_t acs_mask);
private:
- static constexpr size_t required_size_[] = {
- 1, 1, 1, 1, 4, 16, 2, 4, 8, 1, 1, 64, 32, 256, 128, 1024, 512};
- static_assert(kNum == sizeof(required_size_) / sizeof(*required_size_),
- "Update this array when adding or removing quant tables.");
- static constexpr size_t kTotalTableSize =
- ArraySum(required_size_) * kDCTBlockSize * 3;
+ static constexpr size_t kTotalTableSize = kSumRequiredXy * kDCTBlockSize * 3;
uint32_t computed_mask_ = 0;
// kTotalTableSize entries followed by kTotalTableSize for inv_table
diff --git a/third_party/jpeg-xl/lib/jxl/quant_weights_test.cc b/third_party/jpeg-xl/lib/jxl/quant_weights_test.cc
index 2dd513804c..e92cbf2151 100644
--- a/third_party/jpeg-xl/lib/jxl/quant_weights_test.cc
+++ b/third_party/jpeg-xl/lib/jxl/quant_weights_test.cc
@@ -23,6 +23,18 @@
namespace jxl {
namespace {
+// This should have been static assert; not compiling though with C++<17.
+TEST(QuantWeightsTest, Invariant) {
+ size_t sum = 0;
+ ASSERT_EQ(DequantMatrices::required_size_x.size(),
+ DequantMatrices::required_size_y.size());
+ for (size_t i = 0; i < DequantMatrices::required_size_x.size(); ++i) {
+ sum += DequantMatrices::required_size_x[i] *
+ DequantMatrices::required_size_y[i];
+ }
+ ASSERT_EQ(DequantMatrices::kSumRequiredXy, sum);
+}
+
template <typename T>
void CheckSimilar(T a, T b) {
EXPECT_EQ(a, b);
@@ -50,8 +62,8 @@ void RoundtripMatrices(const std::vector<QuantEncoding>& encodings) {
DequantMatrices mat;
CodecMetadata metadata;
FrameHeader frame_header(&metadata);
- ModularFrameEncoder encoder(frame_header, CompressParams{});
- DequantMatricesSetCustom(&mat, encodings, &encoder);
+ ModularFrameEncoder encoder(frame_header, CompressParams{}, false);
+ JXL_CHECK(DequantMatricesSetCustom(&mat, encodings, &encoder));
const std::vector<QuantEncoding>& encodings_dec = mat.encodings();
for (size_t i = 0; i < encodings.size(); i++) {
const QuantEncoding& e = encodings[i];
@@ -172,8 +184,8 @@ TEST_P(QuantWeightsTargetTest, DCTUniform) {
DequantMatrices dequant_matrices;
CodecMetadata metadata;
FrameHeader frame_header(&metadata);
- ModularFrameEncoder encoder(frame_header, CompressParams{});
- DequantMatricesSetCustom(&dequant_matrices, encodings, &encoder);
+ ModularFrameEncoder encoder(frame_header, CompressParams{}, false);
+ JXL_CHECK(DequantMatricesSetCustom(&dequant_matrices, encodings, &encoder));
JXL_CHECK(dequant_matrices.EnsureComputed(~0u));
const float dc_quant[3] = {1.0f / kUniformQuant, 1.0f / kUniformQuant,
diff --git a/third_party/jpeg-xl/lib/jxl/quantizer.h b/third_party/jpeg-xl/lib/jxl/quantizer.h
index 4e34ac78e8..602d12bdfa 100644
--- a/third_party/jpeg-xl/lib/jxl/quantizer.h
+++ b/third_party/jpeg-xl/lib/jxl/quantizer.h
@@ -73,7 +73,7 @@ class Quantizer {
}
float ScaleGlobalScale(const float scale) {
- int new_global_scale = static_cast<int>(global_scale_ * scale + 0.5f);
+ int new_global_scale = static_cast<int>(std::lround(global_scale_ * scale));
float scale_out = new_global_scale * 1.0f / global_scale_;
global_scale_ = new_global_scale;
RecomputeFromGlobalScale();
diff --git a/third_party/jpeg-xl/lib/jxl/quantizer_test.cc b/third_party/jpeg-xl/lib/jxl/quantizer_test.cc
index aff19f42c1..eeaac9ba53 100644
--- a/third_party/jpeg-xl/lib/jxl/quantizer_test.cc
+++ b/third_party/jpeg-xl/lib/jxl/quantizer_test.cc
@@ -5,10 +5,8 @@
#include "lib/jxl/quantizer.h"
-#include "lib/jxl/base/span.h"
#include "lib/jxl/dec_bit_reader.h"
#include "lib/jxl/enc_fields.h"
-#include "lib/jxl/image_ops.h"
#include "lib/jxl/image_test_utils.h"
#include "lib/jxl/testing.h"
@@ -24,7 +22,8 @@ TEST(QuantizerTest, QuantizerParams) {
for (uint32_t i = 1; i < 10000; ++i) {
QuantizerParams p;
p.global_scale = i;
- size_t extension_bits = 0, total_bits = 0;
+ size_t extension_bits = 0;
+ size_t total_bits = 0;
EXPECT_TRUE(Bundle::CanEncode(p, &extension_bits, &total_bits));
EXPECT_EQ(0u, extension_bits);
EXPECT_GE(total_bits, 4u);
@@ -36,7 +35,7 @@ TEST(QuantizerTest, BitStreamRoundtripSameQuant) {
const int qysize = 8;
DequantMatrices dequant;
Quantizer quantizer1(&dequant);
- ImageI raw_quant_field(qxsize, qysize);
+ JXL_ASSIGN_OR_DIE(ImageI raw_quant_field, ImageI::Create(qxsize, qysize));
quantizer1.SetQuant(0.17f, 0.17f, &raw_quant_field);
BitWriter writer;
QuantizerParams params = quantizer1.GetParams();
@@ -57,10 +56,10 @@ TEST(QuantizerTest, BitStreamRoundtripRandomQuant) {
const int qysize = 8;
DequantMatrices dequant;
Quantizer quantizer1(&dequant);
- ImageI raw_quant_field(qxsize, qysize);
+ JXL_ASSIGN_OR_DIE(ImageI raw_quant_field, ImageI::Create(qxsize, qysize));
quantizer1.SetQuant(0.17f, 0.17f, &raw_quant_field);
float quant_dc = 0.17f;
- ImageF qf(qxsize, qysize);
+ JXL_ASSIGN_OR_DIE(ImageF qf, ImageF::Create(qxsize, qysize));
RandomFillImage(&qf, 0.0f, 1.0f);
quantizer1.SetQuantField(quant_dc, qf, &raw_quant_field);
BitWriter writer;
diff --git a/third_party/jpeg-xl/lib/jxl/rational_polynomial_test.cc b/third_party/jpeg-xl/lib/jxl/rational_polynomial_test.cc
index bc31cdd092..e5211cda7a 100644
--- a/third_party/jpeg-xl/lib/jxl/rational_polynomial_test.cc
+++ b/third_party/jpeg-xl/lib/jxl/rational_polynomial_test.cc
@@ -158,8 +158,8 @@ void TestExp() {
const T q[4 * (2 + 1)] = {HWY_REP4(9.6259895571622622E-01),
HWY_REP4(-4.7272457588933831E-01),
HWY_REP4(7.4802088567547664E-02)};
- const T err =
- RunApproximation(-1, 1, p, q, EvalPoly(), [](T x) { return T(exp(x)); });
+ const T err = RunApproximation(-1, 1, p, q, EvalPoly(),
+ [](T x) { return static_cast<T>(exp(x)); });
EXPECT_LT(err, 1E-4);
}
@@ -174,8 +174,8 @@ void TestNegExp() {
HWY_REP4(5.9579108238812878E-02), HWY_REP4(3.4542074345478582E-02),
HWY_REP4(8.7263562483501714E-03), HWY_REP4(1.4095109143061216E-03)};
- const T err =
- RunApproximation(0, 10, p, q, EvalPoly(), [](T x) { return T(exp(-x)); });
+ const T err = RunApproximation(0, 10, p, q, EvalPoly(),
+ [](T x) { return static_cast<T>(exp(-x)); });
EXPECT_LT(err, sizeof(T) == 8 ? 2E-5 : 3E-5);
}
@@ -191,7 +191,7 @@ void TestSin() {
HWY_REP4(3.1546157932479282E-03), HWY_REP4(-1.6692542019380155E-04)};
const T err = RunApproximation(0, Pi<T>(1) * 2, p, q, EvalPoly(),
- [](T x) { return T(sin(x)); });
+ [](T x) { return static_cast<T>(sin(x)); });
EXPECT_LT(err, sizeof(T) == 8 ? 5E-4 : 7E-4);
}
diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/low_memory_render_pipeline.cc b/third_party/jpeg-xl/lib/jxl/render_pipeline/low_memory_render_pipeline.cc
index 9aefdd007d..27718e6413 100644
--- a/third_party/jpeg-xl/lib/jxl/render_pipeline/low_memory_render_pipeline.cc
+++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/low_memory_render_pipeline.cc
@@ -6,10 +6,9 @@
#include "lib/jxl/render_pipeline/low_memory_render_pipeline.h"
#include <algorithm>
-#include <queue>
-#include <tuple>
#include "lib/jxl/base/arch_macros.h"
+#include "lib/jxl/base/status.h"
#include "lib/jxl/image_ops.h"
namespace jxl {
@@ -174,7 +173,7 @@ size_t LowMemoryRenderPipeline::GroupInputYSize(size_t c) const {
channel_shifts_[0][c].second;
}
-void LowMemoryRenderPipeline::EnsureBordersStorage() {
+Status LowMemoryRenderPipeline::EnsureBordersStorage() {
const auto& shifts = channel_shifts_[0];
if (borders_horizontal_.size() < shifts.size()) {
borders_horizontal_.resize(shifts.size());
@@ -194,16 +193,20 @@ void LowMemoryRenderPipeline::EnsureBordersStorage() {
1 << shifts[c].second);
Rect horizontal = Rect(0, 0, downsampled_xsize, bordery * num_yborders);
if (!SameSize(horizontal, borders_horizontal_[c])) {
- borders_horizontal_[c] = ImageF(horizontal.xsize(), horizontal.ysize());
+ JXL_ASSIGN_OR_RETURN(
+ borders_horizontal_[c],
+ ImageF::Create(horizontal.xsize(), horizontal.ysize()));
}
Rect vertical = Rect(0, 0, borderx * num_xborders, downsampled_ysize);
if (!SameSize(vertical, borders_vertical_[c])) {
- borders_vertical_[c] = ImageF(vertical.xsize(), vertical.ysize());
+ JXL_ASSIGN_OR_RETURN(borders_vertical_[c],
+ ImageF::Create(vertical.xsize(), vertical.ysize()));
}
}
+ return true;
}
-void LowMemoryRenderPipeline::Init() {
+Status LowMemoryRenderPipeline::Init() {
group_border_ = {0, 0};
base_color_shift_ = CeilLog2Nonzero(frame_dimensions_.xsize_upsampled_padded /
frame_dimensions_.xsize_padded);
@@ -255,7 +258,7 @@ void LowMemoryRenderPipeline::Init() {
group_data_x_border_ = RoundUpTo(max_border.first, kGroupXAlign);
group_data_y_border_ = max_border.second;
- EnsureBordersStorage();
+ JXL_RETURN_IF_ERROR(EnsureBordersStorage());
group_border_assigner_.Init(frame_dimensions_);
for (first_trailing_stage_ = stages_.size(); first_trailing_stage_ > 0;
@@ -282,7 +285,7 @@ void LowMemoryRenderPipeline::Init() {
DivCeil(frame_dimensions_.ysize_upsampled,
1 << channel_shifts_[i][c].second));
}
- stages_[i]->SetInputSizes(input_sizes);
+ JXL_RETURN_IF_ERROR(stages_[i]->SetInputSizes(input_sizes));
if (stages_[i]->SwitchToImageDimensions()) {
// We don't allow kInOut after switching to image dimensions.
JXL_ASSERT(i >= first_trailing_stage_);
@@ -300,7 +303,7 @@ void LowMemoryRenderPipeline::Init() {
for (size_t c = 0; c < shifts.size(); c++) {
input_sizes[c] = {full_image_xsize_, full_image_ysize_};
}
- stages_[i]->SetInputSizes(input_sizes);
+ JXL_RETURN_IF_ERROR(stages_[i]->SetInputSizes(input_sizes));
}
anyc_.resize(stages_.size());
@@ -355,10 +358,11 @@ void LowMemoryRenderPipeline::Init() {
}
}
}
+ return true;
}
-void LowMemoryRenderPipeline::PrepareForThreadsInternal(size_t num,
- bool use_group_ids) {
+Status LowMemoryRenderPipeline::PrepareForThreadsInternal(size_t num,
+ bool use_group_ids) {
const auto& shifts = channel_shifts_[0];
use_group_ids_ = use_group_ids;
size_t num_buffers = use_group_ids_ ? frame_dimensions_.num_groups : num;
@@ -366,8 +370,10 @@ void LowMemoryRenderPipeline::PrepareForThreadsInternal(size_t num,
group_data_.emplace_back();
group_data_[t].resize(shifts.size());
for (size_t c = 0; c < shifts.size(); c++) {
- group_data_[t][c] = ImageF(GroupInputXSize(c) + group_data_x_border_ * 2,
- GroupInputYSize(c) + group_data_y_border_ * 2);
+ JXL_ASSIGN_OR_RETURN(
+ group_data_[t][c],
+ ImageF::Create(GroupInputXSize(c) + group_data_x_border_ * 2,
+ GroupInputYSize(c) + group_data_y_border_ * 2));
}
}
// TODO(veluca): avoid reallocating buffers if not needed.
@@ -390,7 +396,9 @@ void LowMemoryRenderPipeline::PrepareForThreadsInternal(size_t num,
2 * next_y_border + (1 << stages_[i]->settings_.shift_y);
stage_buffer_ysize = 1 << CeilLog2Nonzero(stage_buffer_ysize);
next_y_border = stages_[i]->settings_.border_y;
- stage_data_[t][c][i] = ImageF(stage_buffer_xsize, stage_buffer_ysize);
+ JXL_ASSIGN_OR_RETURN(
+ stage_data_[t][c][i],
+ ImageF::Create(stage_buffer_xsize, stage_buffer_ysize));
}
}
}
@@ -412,9 +420,11 @@ void LowMemoryRenderPipeline::PrepareForThreadsInternal(size_t num,
std::max(left_padding, std::max(middle_padding, right_padding));
out_of_frame_data_.resize(num);
for (size_t t = 0; t < num; t++) {
- out_of_frame_data_[t] = ImageF(out_of_frame_xsize, shifts.size());
+ JXL_ASSIGN_OR_RETURN(out_of_frame_data_[t],
+ ImageF::Create(out_of_frame_xsize, shifts.size()));
}
}
+ return true;
}
std::vector<std::pair<ImageF*, Rect>> LowMemoryRenderPipeline::PrepareBuffers(
@@ -520,7 +530,8 @@ class Rows {
.Translate(-group_data_x_border, -group_data_y_border)
.ShiftLeft(base_color_shift)
.CeilShiftRight(group_data_shift[c])
- .Translate(group_data_x_border - ssize_t(kRenderPipelineXOffset),
+ .Translate(group_data_x_border -
+ static_cast<ssize_t>(kRenderPipelineXOffset),
group_data_y_border);
rows_[0][c].base_ptr = channel_group_data_rect.Row(&input_data[c], 0);
rows_[0][c].stride = input_data[c].PixelsPerRow();
@@ -533,7 +544,8 @@ class Rows {
JXL_INLINE float* GetBuffer(int stage, int y, size_t c) const {
JXL_DASSERT(stage >= -1);
const RowInfo& info = rows_[stage + 1][c];
- return info.base_ptr + ssize_t(info.stride) * (y & info.ymod_minus_1);
+ return info.base_ptr +
+ static_cast<ssize_t>(info.stride) * (y & info.ymod_minus_1);
}
private:
@@ -551,10 +563,10 @@ class Rows {
} // namespace
-void LowMemoryRenderPipeline::RenderRect(size_t thread_id,
- std::vector<ImageF>& input_data,
- Rect data_max_color_channel_rect,
- Rect image_max_color_channel_rect) {
+Status LowMemoryRenderPipeline::RenderRect(size_t thread_id,
+ std::vector<ImageF>& input_data,
+ Rect data_max_color_channel_rect,
+ Rect image_max_color_channel_rect) {
// For each stage, the rect corresponding to the image area currently being
// processed, in the coordinates of that stage (i.e. with the scaling factor
// that that stage has).
@@ -599,7 +611,7 @@ void LowMemoryRenderPipeline::RenderRect(size_t thread_id,
// is no point in proceeding. Note: this uses the assumption that if there is
// a stage with observable effects (i.e. a kInput stage), it only appears
// after the stage that switches to image dimensions.
- if (full_image_x1 <= full_image_x0) return;
+ if (full_image_x1 <= full_image_x0) return true;
// Data structures to hold information about input/output rows and their
// buffers.
@@ -643,7 +655,7 @@ void LowMemoryRenderPipeline::RenderRect(size_t thread_id,
}
// If we already have rows from a previous iteration, we can just shift
// the rows by 1 and insert the new one.
- if (input_rows[i][c].size() == 2 * size_t(bordery) + 1) {
+ if (input_rows[i][c].size() == 2 * static_cast<size_t>(bordery) + 1) {
for (ssize_t iy = 0; iy < 2 * bordery; iy++) {
input_rows[i][c][iy] = input_rows[i][c][iy + 1];
}
@@ -674,7 +686,7 @@ void LowMemoryRenderPipeline::RenderRect(size_t thread_id,
virtual_ypadding_for_output_.end());
for (int vy = -num_extra_rows;
- vy < int(image_area_rect.ysize()) + num_extra_rows; vy++) {
+ vy < static_cast<int>(image_area_rect.ysize()) + num_extra_rows; vy++) {
for (size_t i = 0; i < first_trailing_stage_; i++) {
int stage_vy = vy - num_extra_rows + virtual_ypadding_for_output_[i];
@@ -688,9 +700,10 @@ void LowMemoryRenderPipeline::RenderRect(size_t thread_id,
int y = stage_vy >> channel_shifts_[i][anyc_[i]].second;
- ssize_t image_y = ssize_t(group_rect[i].y0()) + y;
+ ssize_t image_y = static_cast<ssize_t>(group_rect[i].y0()) + y;
// Do not produce rows in out-of-bounds areas.
- if (image_y < 0 || image_y >= ssize_t(image_rect_[i].ysize())) {
+ if (image_y < 0 ||
+ image_y >= static_cast<ssize_t>(image_rect_[i].ysize())) {
continue;
}
@@ -698,9 +711,9 @@ void LowMemoryRenderPipeline::RenderRect(size_t thread_id,
prepare_io_rows(y, i);
// Produce output rows.
- stages_[i]->ProcessRow(input_rows[i], output_rows,
- xpadding_for_output_[i], group_rect[i].xsize(),
- group_rect[i].x0(), image_y, thread_id);
+ JXL_RETURN_IF_ERROR(stages_[i]->ProcessRow(
+ input_rows[i], output_rows, xpadding_for_output_[i],
+ group_rect[i].xsize(), group_rect[i].x0(), image_y, thread_id));
}
// Process trailing stages, i.e. the final set of non-kInOut stages; they
@@ -719,7 +732,7 @@ void LowMemoryRenderPipeline::RenderRect(size_t thread_id,
// Check that we are not outside of the bounds for the current rendering
// rect. Not doing so might result in overwriting some rows that have been
// written (or will be written) by other threads.
- if (y < 0 || y >= ssize_t(image_area_rect.ysize())) {
+ if (y < 0 || y >= static_cast<ssize_t>(image_area_rect.ysize())) {
continue;
}
@@ -728,7 +741,8 @@ void LowMemoryRenderPipeline::RenderRect(size_t thread_id,
// (and may be necessary for correctness, as some stages assume coordinates
// are within bounds).
ssize_t full_image_y = frame_y0 + image_area_rect.y0() + y;
- if (full_image_y < 0 || full_image_y >= ssize_t(full_image_ysize)) {
+ if (full_image_y < 0 ||
+ full_image_y >= static_cast<ssize_t>(full_image_ysize)) {
continue;
}
@@ -739,15 +753,16 @@ void LowMemoryRenderPipeline::RenderRect(size_t thread_id,
i < first_image_dim_stage_ ? full_image_x0 - frame_x0 : full_image_x0;
size_t y =
i < first_image_dim_stage_ ? full_image_y - frame_y0 : full_image_y;
- stages_[i]->ProcessRow(input_rows[first_trailing_stage_], output_rows,
- /*xextra=*/0, full_image_x1 - full_image_x0, x0, y,
- thread_id);
+ JXL_RETURN_IF_ERROR(stages_[i]->ProcessRow(
+ input_rows[first_trailing_stage_], output_rows,
+ /*xextra=*/0, full_image_x1 - full_image_x0, x0, y, thread_id));
}
}
+ return true;
}
-void LowMemoryRenderPipeline::RenderPadding(size_t thread_id, Rect rect) {
- if (rect.xsize() == 0) return;
+Status LowMemoryRenderPipeline::RenderPadding(size_t thread_id, Rect rect) {
+ if (rect.xsize() == 0) return true;
size_t numc = channel_shifts_[0].size();
RenderPipelineStage::RowInfo input_rows(numc, std::vector<float*>(1));
RenderPipelineStage::RowInfo output_rows;
@@ -760,15 +775,16 @@ void LowMemoryRenderPipeline::RenderPadding(size_t thread_id, Rect rect) {
stages_[first_image_dim_stage_ - 1]->ProcessPaddingRow(
input_rows, rect.xsize(), rect.x0(), rect.y0() + y);
for (size_t i = first_image_dim_stage_; i < stages_.size(); i++) {
- stages_[i]->ProcessRow(input_rows, output_rows,
- /*xextra=*/0, rect.xsize(), rect.x0(),
- rect.y0() + y, thread_id);
+ JXL_RETURN_IF_ERROR(stages_[i]->ProcessRow(
+ input_rows, output_rows,
+ /*xextra=*/0, rect.xsize(), rect.x0(), rect.y0() + y, thread_id));
}
}
+ return true;
}
-void LowMemoryRenderPipeline::ProcessBuffers(size_t group_id,
- size_t thread_id) {
+Status LowMemoryRenderPipeline::ProcessBuffers(size_t group_id,
+ size_t thread_id) {
std::vector<ImageF>& input_data =
group_data_[use_group_ids_ ? group_id : thread_id];
@@ -804,38 +820,43 @@ void LowMemoryRenderPipeline::ProcessBuffers(size_t group_id,
if (group_id == 0 && (image_rect.xsize() == 0 || image_rect.ysize() == 0)) {
// If this frame does not intersect with the full image, we have to
// initialize the whole image area with RenderPadding.
- RenderPadding(thread_id,
- Rect(0, 0, full_image_xsize_, full_image_ysize_));
+ JXL_RETURN_IF_ERROR(RenderPadding(
+ thread_id, Rect(0, 0, full_image_xsize_, full_image_ysize_)));
}
// Render padding for groups that intersect with the full image. The case
// where no groups intersect was handled above.
if (group_rect.xsize() > 0 && group_rect.ysize() > 0) {
if (gx == 0 && gy == 0) {
- RenderPadding(thread_id, Rect(0, 0, x0, y0));
+ JXL_RETURN_IF_ERROR(RenderPadding(thread_id, Rect(0, 0, x0, y0)));
}
if (gy == 0) {
- RenderPadding(thread_id, Rect(x0, 0, x1 - x0, y0));
+ JXL_RETURN_IF_ERROR(RenderPadding(thread_id, Rect(x0, 0, x1 - x0, y0)));
}
if (gx == 0) {
- RenderPadding(thread_id, Rect(0, y0, x0, y1 - y0));
+ JXL_RETURN_IF_ERROR(RenderPadding(thread_id, Rect(0, y0, x0, y1 - y0)));
}
if (gx == 0 && gy + 1 == frame_dimensions_.ysize_groups) {
- RenderPadding(thread_id, Rect(0, y1, x0, full_image_ysize_ - y1));
+ JXL_RETURN_IF_ERROR(
+ RenderPadding(thread_id, Rect(0, y1, x0, full_image_ysize_ - y1)));
}
if (gy + 1 == frame_dimensions_.ysize_groups) {
- RenderPadding(thread_id, Rect(x0, y1, x1 - x0, full_image_ysize_ - y1));
+ JXL_RETURN_IF_ERROR(RenderPadding(
+ thread_id, Rect(x0, y1, x1 - x0, full_image_ysize_ - y1)));
}
if (gy == 0 && gx + 1 == frame_dimensions_.xsize_groups) {
- RenderPadding(thread_id, Rect(x1, 0, full_image_xsize_ - x1, y0));
+ JXL_RETURN_IF_ERROR(
+ RenderPadding(thread_id, Rect(x1, 0, full_image_xsize_ - x1, y0)));
}
if (gx + 1 == frame_dimensions_.xsize_groups) {
- RenderPadding(thread_id, Rect(x1, y0, full_image_xsize_ - x1, y1 - y0));
+ JXL_RETURN_IF_ERROR(RenderPadding(
+ thread_id, Rect(x1, y0, full_image_xsize_ - x1, y1 - y0)));
}
if (gy + 1 == frame_dimensions_.ysize_groups &&
gx + 1 == frame_dimensions_.xsize_groups) {
- RenderPadding(thread_id, Rect(x1, y1, full_image_xsize_ - x1,
- full_image_ysize_ - y1));
+ JXL_RETURN_IF_ERROR(RenderPadding(
+ thread_id,
+ Rect(x1, y1, full_image_xsize_ - x1, full_image_ysize_ - y1)));
}
}
}
@@ -857,8 +878,10 @@ void LowMemoryRenderPipeline::ProcessBuffers(size_t group_id,
gy * frame_dimensions_.group_dim,
image_max_color_channel_rect.xsize(),
image_max_color_channel_rect.ysize());
- RenderRect(thread_id, input_data, data_max_color_channel_rect,
- image_max_color_channel_rect);
+ JXL_RETURN_IF_ERROR(RenderRect(thread_id, input_data,
+ data_max_color_channel_rect,
+ image_max_color_channel_rect));
}
+ return true;
}
} // namespace jxl
diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/low_memory_render_pipeline.h b/third_party/jpeg-xl/lib/jxl/render_pipeline/low_memory_render_pipeline.h
index b386f7c078..f0b21d3dca 100644
--- a/third_party/jpeg-xl/lib/jxl/render_pipeline/low_memory_render_pipeline.h
+++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/low_memory_render_pipeline.h
@@ -20,21 +20,21 @@ class LowMemoryRenderPipeline final : public RenderPipeline {
std::vector<std::pair<ImageF*, Rect>> PrepareBuffers(
size_t group_id, size_t thread_id) override;
- void PrepareForThreadsInternal(size_t num, bool use_group_ids) override;
+ Status PrepareForThreadsInternal(size_t num, bool use_group_ids) override;
- void ProcessBuffers(size_t group_id, size_t thread_id) override;
+ Status ProcessBuffers(size_t group_id, size_t thread_id) override;
void ClearDone(size_t i) override { group_border_assigner_.ClearDone(i); }
- void Init() override;
+ Status Init() override;
- void EnsureBordersStorage();
+ Status EnsureBordersStorage();
size_t GroupInputXSize(size_t c) const;
size_t GroupInputYSize(size_t c) const;
- void RenderRect(size_t thread_id, std::vector<ImageF>& input_data,
- Rect data_max_color_channel_rect,
- Rect image_max_color_channel_rect);
- void RenderPadding(size_t thread_id, Rect rect);
+ Status RenderRect(size_t thread_id, std::vector<ImageF>& input_data,
+ Rect data_max_color_channel_rect,
+ Rect image_max_color_channel_rect);
+ Status RenderPadding(size_t thread_id, Rect rect);
void SaveBorders(size_t group_id, size_t c, const ImageF& in);
void LoadBorders(size_t group_id, size_t c, const Rect& r, ImageF* out);
diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/render_pipeline.cc b/third_party/jpeg-xl/lib/jxl/render_pipeline/render_pipeline.cc
index 68b6ef613f..14bd363110 100644
--- a/third_party/jpeg-xl/lib/jxl/render_pipeline/render_pipeline.cc
+++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/render_pipeline.cc
@@ -5,8 +5,7 @@
#include "lib/jxl/render_pipeline/render_pipeline.h"
-#include <algorithm>
-
+#include "lib/jxl/base/status.h"
#include "lib/jxl/render_pipeline/low_memory_render_pipeline.h"
#include "lib/jxl/render_pipeline/simple_render_pipeline.h"
#include "lib/jxl/sanitizers.h"
@@ -18,7 +17,7 @@ void RenderPipeline::Builder::AddStage(
stages_.push_back(std::move(stage));
}
-std::unique_ptr<RenderPipeline> RenderPipeline::Builder::Finalize(
+StatusOr<std::unique_ptr<RenderPipeline>> RenderPipeline::Builder::Finalize(
FrameDimensions frame_dimensions) && {
#if JXL_ENABLE_ASSERT
// Check that the last stage is not an kInOut stage for any channel, and that
@@ -88,7 +87,7 @@ std::unique_ptr<RenderPipeline> RenderPipeline::Builder::Finalize(
}
}
res->stages_ = std::move(stages_);
- res->Init();
+ JXL_RETURN_IF_ERROR(res->Init());
return res;
}
@@ -103,7 +102,7 @@ RenderPipelineInput RenderPipeline::GetInputBuffers(size_t group_id,
return ret;
}
-void RenderPipeline::InputReady(
+Status RenderPipeline::InputReady(
size_t group_id, size_t thread_id,
const std::vector<std::pair<ImageF*, Rect>>& buffers) {
JXL_DASSERT(group_id < group_completed_passes_.size());
@@ -113,20 +112,22 @@ void RenderPipeline::InputReady(
JXL_CHECK_PLANE_INITIALIZED(*buffers[i].first, buffers[i].second, i);
}
- ProcessBuffers(group_id, thread_id);
+ JXL_RETURN_IF_ERROR(ProcessBuffers(group_id, thread_id));
+ return true;
}
Status RenderPipeline::PrepareForThreads(size_t num, bool use_group_ids) {
for (const auto& stage : stages_) {
JXL_RETURN_IF_ERROR(stage->PrepareForThreads(num));
}
- PrepareForThreadsInternal(num, use_group_ids);
+ JXL_RETURN_IF_ERROR(PrepareForThreadsInternal(num, use_group_ids));
return true;
}
-void RenderPipelineInput::Done() {
+Status RenderPipelineInput::Done() {
JXL_ASSERT(pipeline_);
- pipeline_->InputReady(group_id_, thread_id_, buffers_);
+ JXL_RETURN_IF_ERROR(pipeline_->InputReady(group_id_, thread_id_, buffers_));
+ return true;
}
} // namespace jxl
diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/render_pipeline.h b/third_party/jpeg-xl/lib/jxl/render_pipeline/render_pipeline.h
index bf3ad4975e..c61420be4b 100644
--- a/third_party/jpeg-xl/lib/jxl/render_pipeline/render_pipeline.h
+++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/render_pipeline.h
@@ -32,7 +32,7 @@ class RenderPipelineInput {
}
RenderPipelineInput() = default;
- void Done();
+ Status Done();
const std::pair<ImageF*, Rect>& GetBuffer(size_t c) const {
JXL_ASSERT(c < buffers_.size());
@@ -63,7 +63,7 @@ class RenderPipeline {
// Finalizes setup of the pipeline. Shifts for all channels should be 0 at
// this point.
- std::unique_ptr<RenderPipeline> Finalize(
+ StatusOr<std::unique_ptr<RenderPipeline>> Finalize(
FrameDimensions frame_dimensions) &&;
private:
@@ -118,20 +118,20 @@ class RenderPipeline {
friend class RenderPipelineInput;
private:
- void InputReady(size_t group_id, size_t thread_id,
- const std::vector<std::pair<ImageF*, Rect>>& buffers);
+ Status InputReady(size_t group_id, size_t thread_id,
+ const std::vector<std::pair<ImageF*, Rect>>& buffers);
virtual std::vector<std::pair<ImageF*, Rect>> PrepareBuffers(
size_t group_id, size_t thread_id) = 0;
- virtual void ProcessBuffers(size_t group_id, size_t thread_id) = 0;
+ virtual Status ProcessBuffers(size_t group_id, size_t thread_id) = 0;
// Note that this method may be called multiple times with different (or
// equal) `num`.
- virtual void PrepareForThreadsInternal(size_t num, bool use_group_ids) = 0;
+ virtual Status PrepareForThreadsInternal(size_t num, bool use_group_ids) = 0;
// Called once frame dimensions and stages are known.
- virtual void Init() {}
+ virtual Status Init() { return true; }
};
} // namespace jxl
diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/render_pipeline_stage.h b/third_party/jpeg-xl/lib/jxl/render_pipeline/render_pipeline_stage.h
index d1a0074161..d054027ba7 100644
--- a/third_party/jpeg-xl/lib/jxl/render_pipeline/render_pipeline_stage.h
+++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/render_pipeline_stage.h
@@ -9,6 +9,7 @@
#include <stdint.h>
#include "lib/jxl/base/arch_macros.h"
+#include "lib/jxl/base/status.h"
#include "lib/jxl/frame_header.h"
namespace jxl {
@@ -99,9 +100,10 @@ class RenderPipelineStage {
// `GroupBorderAssigner::kPaddingXRound`. If `settings_.temp_buffer_size` is
// nonzero, `temp` will point to an HWY-aligned buffer of at least that number
// of floats; concurrent calls will have different buffers.
- virtual void ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows,
- size_t xextra, size_t xsize, size_t xpos, size_t ypos,
- size_t thread_id) const = 0;
+ virtual Status ProcessRow(const RowInfo& input_rows,
+ const RowInfo& output_rows, size_t xextra,
+ size_t xsize, size_t xpos, size_t ypos,
+ size_t thread_id) const = 0;
// How each channel will be processed. Channels are numbered starting from
// color channels (always 3) and followed by all other channels.
@@ -114,8 +116,10 @@ class RenderPipelineStage {
// Informs the stage about the total size of each channel. Few stages will
// actually need to use this information.
- virtual void SetInputSizes(
- const std::vector<std::pair<size_t, size_t>>& input_sizes) {}
+ virtual Status SetInputSizes(
+ const std::vector<std::pair<size_t, size_t>>& input_sizes) {
+ return true;
+ }
virtual Status PrepareForThreads(size_t num_threads) { return true; }
diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/render_pipeline_test.cc b/third_party/jpeg-xl/lib/jxl/render_pipeline/render_pipeline_test.cc
index 51b9f273f8..e9cb913983 100644
--- a/third_party/jpeg-xl/lib/jxl/render_pipeline/render_pipeline_test.cc
+++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/render_pipeline_test.cc
@@ -111,7 +111,7 @@ TEST(RenderPipelineTest, Build) {
frame_dimensions.Set(/*xsize=*/1024, /*ysize=*/1024, /*group_size_shift=*/0,
/*max_hshift=*/0, /*max_vshift=*/0,
/*modular_mode=*/false, /*upsampling=*/1);
- std::move(builder).Finalize(frame_dimensions);
+ std::move(builder).Finalize(frame_dimensions).value();
}
TEST(RenderPipelineTest, CallAllGroups) {
@@ -124,14 +124,14 @@ TEST(RenderPipelineTest, CallAllGroups) {
frame_dimensions.Set(/*xsize=*/1024, /*ysize=*/1024, /*group_size_shift=*/0,
/*max_hshift=*/0, /*max_vshift=*/0,
/*modular_mode=*/false, /*upsampling=*/1);
- auto pipeline = std::move(builder).Finalize(frame_dimensions);
+ auto pipeline = std::move(builder).Finalize(frame_dimensions).value();
ASSERT_TRUE(pipeline->PrepareForThreads(1, /*use_group_ids=*/false));
for (size_t i = 0; i < frame_dimensions.num_groups; i++) {
auto input_buffers = pipeline->GetInputBuffers(i, 0);
FillPlane(0.0f, input_buffers.GetBuffer(0).first,
input_buffers.GetBuffer(0).second);
- input_buffers.Done();
+ JXL_CHECK(input_buffers.Done());
}
EXPECT_EQ(pipeline->PassesWithAllInput(), 1);
@@ -146,7 +146,7 @@ TEST(RenderPipelineTest, BuildFast) {
frame_dimensions.Set(/*xsize=*/1024, /*ysize=*/1024, /*group_size_shift=*/0,
/*max_hshift=*/0, /*max_vshift=*/0,
/*modular_mode=*/false, /*upsampling=*/1);
- std::move(builder).Finalize(frame_dimensions);
+ std::move(builder).Finalize(frame_dimensions).value();
}
TEST(RenderPipelineTest, CallAllGroupsFast) {
@@ -159,14 +159,14 @@ TEST(RenderPipelineTest, CallAllGroupsFast) {
frame_dimensions.Set(/*xsize=*/1024, /*ysize=*/1024, /*group_size_shift=*/0,
/*max_hshift=*/0, /*max_vshift=*/0,
/*modular_mode=*/false, /*upsampling=*/1);
- auto pipeline = std::move(builder).Finalize(frame_dimensions);
+ auto pipeline = std::move(builder).Finalize(frame_dimensions).value();
ASSERT_TRUE(pipeline->PrepareForThreads(1, /*use_group_ids=*/false));
for (size_t i = 0; i < frame_dimensions.num_groups; i++) {
auto input_buffers = pipeline->GetInputBuffers(i, 0);
FillPlane(0.0f, input_buffers.GetBuffer(0).first,
input_buffers.GetBuffer(0).second);
- input_buffers.Done();
+ JXL_CHECK(input_buffers.Done());
}
EXPECT_EQ(pipeline->PassesWithAllInput(), 1);
@@ -208,7 +208,7 @@ TEST_P(RenderPipelineTestParam, PipelineTest) {
io.ShrinkTo(config.xsize, config.ysize);
if (config.add_spot_color) {
- jxl::ImageF spot(config.xsize, config.ysize);
+ JXL_ASSIGN_OR_DIE(ImageF spot, ImageF::Create(config.xsize, config.ysize));
jxl::ZeroFillImage(&spot);
for (size_t y = 0; y < config.ysize; y++) {
@@ -227,7 +227,7 @@ TEST_P(RenderPipelineTestParam, PipelineTest) {
info.spot_color[3] = 0.5f;
io.metadata.m.extra_channel_info.push_back(info);
- std::vector<jxl::ImageF> ec;
+ std::vector<ImageF> ec;
ec.push_back(std::move(spot));
io.frames[0].SetExtraChannels(std::move(ec));
}
@@ -267,11 +267,11 @@ Splines CreateTestSplines() {
const ColorCorrelationMap cmap;
std::vector<Spline::Point> control_points{{9, 54}, {118, 159}, {97, 3},
{10, 40}, {150, 25}, {120, 300}};
- const Spline spline{
- control_points,
- /*color_dct=*/
- {{0.03125f, 0.00625f, 0.003125f}, {1.f, 0.321875f}, {1.f, 0.24375f}},
- /*sigma_dct=*/{0.3125f, 0.f, 0.f, 0.0625f}};
+ const Spline spline{control_points,
+ /*color_dct=*/
+ {Dct32{0.03125f, 0.00625f, 0.003125f},
+ Dct32{1.f, 0.321875f}, Dct32{1.f, 0.24375f}},
+ /*sigma_dct=*/{0.3125f, 0.f, 0.f, 0.0625f}};
std::vector<Spline> spline_data = {spline};
std::vector<QuantizedSpline> quantized_splines;
std::vector<Spline::Point> starting_points;
@@ -522,7 +522,7 @@ std::ostream& operator<<(std::ostream& os,
filename = c.input_path.substr(pos + 1);
}
std::replace_if(
- filename.begin(), filename.end(), [](char c) { return !isalnum(c); },
+ filename.begin(), filename.end(), [](char c) { return isalnum(c) == 0; },
'_');
os << filename << "_" << (c.jpeg_transcode ? "JPEG_" : "") << c.xsize << "x"
<< c.ysize << "_" << c.cparams_descr;
diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/simple_render_pipeline.cc b/third_party/jpeg-xl/lib/jxl/render_pipeline/simple_render_pipeline.cc
index 4495288860..7f5a8ef00f 100644
--- a/third_party/jpeg-xl/lib/jxl/render_pipeline/simple_render_pipeline.cc
+++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/simple_render_pipeline.cc
@@ -7,27 +7,31 @@
#include <hwy/base.h>
+#include "lib/jxl/base/status.h"
#include "lib/jxl/image_ops.h"
#include "lib/jxl/render_pipeline/render_pipeline_stage.h"
#include "lib/jxl/sanitizers.h"
namespace jxl {
-void SimpleRenderPipeline::PrepareForThreadsInternal(size_t num,
- bool use_group_ids) {
+Status SimpleRenderPipeline::PrepareForThreadsInternal(size_t num,
+ bool use_group_ids) {
if (!channel_data_.empty()) {
- return;
+ return true;
}
auto ch_size = [](size_t frame_size, size_t shift) {
return DivCeil(frame_size, 1 << shift) + kRenderPipelineXOffset * 2;
};
for (size_t c = 0; c < channel_shifts_[0].size(); c++) {
- channel_data_.push_back(ImageF(
- ch_size(frame_dimensions_.xsize_upsampled, channel_shifts_[0][c].first),
- ch_size(frame_dimensions_.ysize_upsampled,
- channel_shifts_[0][c].second)));
+ JXL_ASSIGN_OR_RETURN(
+ ImageF ch, ImageF::Create(ch_size(frame_dimensions_.xsize_upsampled,
+ channel_shifts_[0][c].first),
+ ch_size(frame_dimensions_.ysize_upsampled,
+ channel_shifts_[0][c].second)));
+ channel_data_.push_back(std::move(ch));
msan::PoisonImage(channel_data_.back());
}
+ return true;
}
Rect SimpleRenderPipeline::MakeChannelRect(size_t group_id, size_t channel) {
@@ -60,14 +64,14 @@ std::vector<std::pair<ImageF*, Rect>> SimpleRenderPipeline::PrepareBuffers(
return ret;
}
-void SimpleRenderPipeline::ProcessBuffers(size_t group_id, size_t thread_id) {
+Status SimpleRenderPipeline::ProcessBuffers(size_t group_id, size_t thread_id) {
for (size_t c = 0; c < channel_data_.size(); c++) {
Rect r = MakeChannelRect(group_id, c);
(void)r;
JXL_CHECK_PLANE_INITIALIZED(channel_data_[c], r, c);
}
- if (PassesWithAllInput() <= processed_passes_) return;
+ if (PassesWithAllInput() <= processed_passes_) return true;
processed_passes_++;
for (size_t stage_id = 0; stage_id < stages_.size(); stage_id++) {
@@ -89,11 +93,13 @@ void SimpleRenderPipeline::ProcessBuffers(size_t group_id, size_t thread_id) {
}
// Ensure that the newly allocated channels are large enough to avoid
// problems with padding.
- new_channels[c] =
- ImageF(frame_dimensions_.xsize_upsampled_padded +
- kRenderPipelineXOffset * 2 + hwy::kMaxVectorSize * 8,
- frame_dimensions_.ysize_upsampled_padded +
- kRenderPipelineXOffset * 2);
+ JXL_ASSIGN_OR_RETURN(
+ new_channels[c],
+ ImageF::Create(frame_dimensions_.xsize_upsampled_padded +
+ kRenderPipelineXOffset * 2 +
+ hwy::kMaxVectorSize * 8,
+ frame_dimensions_.ysize_upsampled_padded +
+ kRenderPipelineXOffset * 2));
new_channels[c].ShrinkTo(
(input_sizes[c].first << stage->settings_.shift_x) +
kRenderPipelineXOffset * 2,
@@ -116,7 +122,8 @@ void SimpleRenderPipeline::ProcessBuffers(size_t group_id, size_t thread_id) {
for (size_t y = 0; y < input_sizes[c].second; y++) {
float* row = get_row(c, y);
for (size_t ix = 0; ix < stage->settings_.border_x; ix++) {
- *(row - ix - 1) = row[Mirror(-ssize_t(ix) - 1, input_sizes[c].first)];
+ *(row - ix - 1) =
+ row[Mirror(-static_cast<ssize_t>(ix) - 1, input_sizes[c].first)];
}
for (size_t ix = 0; ix < stage->settings_.border_x; ix++) {
*(row + ix + input_sizes[c].first) =
@@ -126,7 +133,8 @@ void SimpleRenderPipeline::ProcessBuffers(size_t group_id, size_t thread_id) {
// Vertical mirroring.
for (int y = 0; y < static_cast<int>(stage->settings_.border_y); y++) {
memcpy(get_row(c, -y - 1) - stage->settings_.border_x,
- get_row(c, Mirror(-ssize_t(y) - 1, input_sizes[c].second)) -
+ get_row(c, Mirror(-static_cast<ssize_t>(y) - 1,
+ input_sizes[c].second)) -
stage->settings_.border_x,
sizeof(float) *
(input_sizes[c].first + 2 * stage->settings_.border_x));
@@ -160,7 +168,7 @@ void SimpleRenderPipeline::ProcessBuffers(size_t group_id, size_t thread_id) {
// Run the pipeline.
{
- stage->SetInputSizes(input_sizes);
+ JXL_RETURN_IF_ERROR(stage->SetInputSizes(input_sizes));
int border_y = stage->settings_.border_y;
for (size_t y = 0; y < ysize; y++) {
// Prepare input rows.
@@ -183,8 +191,9 @@ void SimpleRenderPipeline::ProcessBuffers(size_t group_id, size_t thread_id) {
(y << stage->settings_.shift_y) + iy + kRenderPipelineXOffset);
}
}
- stage->ProcessRow(input_rows, output_rows, /*xextra=*/0, xsize,
- /*xpos=*/0, y, thread_id);
+ JXL_RETURN_IF_ERROR(stage->ProcessRow(input_rows, output_rows,
+ /*xextra=*/0, xsize,
+ /*xpos=*/0, y, thread_id));
}
}
@@ -210,7 +219,8 @@ void SimpleRenderPipeline::ProcessBuffers(size_t group_id, size_t thread_id) {
}
if (stage->SwitchToImageDimensions()) {
- size_t image_xsize, image_ysize;
+ size_t image_xsize;
+ size_t image_ysize;
FrameOrigin frame_origin;
stage->GetImageDimensions(&image_xsize, &image_ysize, &frame_origin);
frame_dimensions_.Set(image_xsize, image_ysize, 0, 0, 0, false, 1);
@@ -218,8 +228,11 @@ void SimpleRenderPipeline::ProcessBuffers(size_t group_id, size_t thread_id) {
channel_data_.clear();
channel_data_.reserve(old_channels.size());
for (size_t c = 0; c < old_channels.size(); c++) {
- channel_data_.emplace_back(2 * kRenderPipelineXOffset + image_xsize,
- 2 * kRenderPipelineXOffset + image_ysize);
+ JXL_ASSIGN_OR_RETURN(
+ ImageF ch,
+ ImageF::Create(2 * kRenderPipelineXOffset + image_xsize,
+ 2 * kRenderPipelineXOffset + image_ysize));
+ channel_data_.emplace_back(std::move(ch));
}
for (size_t y = 0; y < image_ysize; ++y) {
for (size_t c = 0; c < channel_data_.size(); c++) {
@@ -262,5 +275,6 @@ void SimpleRenderPipeline::ProcessBuffers(size_t group_id, size_t thread_id) {
}
}
}
+ return true;
}
} // namespace jxl
diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/simple_render_pipeline.h b/third_party/jpeg-xl/lib/jxl/render_pipeline/simple_render_pipeline.h
index 10f4505912..1240b9fa46 100644
--- a/third_party/jpeg-xl/lib/jxl/render_pipeline/simple_render_pipeline.h
+++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/simple_render_pipeline.h
@@ -19,9 +19,9 @@ class SimpleRenderPipeline : public RenderPipeline {
std::vector<std::pair<ImageF*, Rect>> PrepareBuffers(
size_t group_id, size_t thread_id) override;
- void ProcessBuffers(size_t group_id, size_t thread_id) override;
+ Status ProcessBuffers(size_t group_id, size_t thread_id) override;
- void PrepareForThreadsInternal(size_t num, bool use_group_ids) override;
+ Status PrepareForThreadsInternal(size_t num, bool use_group_ids) override;
// Full frame buffers. Both X and Y dimensions are padded by
// kRenderPipelineXOffset.
diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_blending.cc b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_blending.cc
index b68105f4c9..ef3899d1b3 100644
--- a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_blending.cc
+++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_blending.cc
@@ -109,7 +109,7 @@ class BlendingStage : public RenderPipelineStage {
}
}
};
- make_blending(info_, &blending_info_[0]);
+ make_blending(info_, blending_info_.data());
for (size_t i = 0; i < ec_info.size(); i++) {
make_blending(ec_info[i], &blending_info_[1 + i]);
}
@@ -117,9 +117,9 @@ class BlendingStage : public RenderPipelineStage {
Status IsInitialized() const override { return initialized_; }
- void ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows,
- size_t xextra, size_t xsize, size_t xpos, size_t ypos,
- size_t thread_id) const final {
+ Status ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows,
+ size_t xextra, size_t xsize, size_t xpos, size_t ypos,
+ size_t thread_id) const final {
JXL_ASSERT(initialized_);
const FrameOrigin& frame_origin = frame_header_.frame_origin;
ssize_t bg_xpos = frame_origin.x0 + static_cast<ssize_t>(xpos);
@@ -128,7 +128,8 @@ class BlendingStage : public RenderPipelineStage {
if (bg_xpos + static_cast<ssize_t>(xsize) <= 0 ||
frame_origin.x0 >= static_cast<ssize_t>(image_xsize_) || bg_ypos < 0 ||
bg_ypos >= static_cast<ssize_t>(image_ysize_)) {
- return;
+ // TODO(eustas): or fail?
+ return true;
}
if (bg_xpos < 0) {
offset -= bg_xpos;
@@ -160,9 +161,9 @@ class BlendingStage : public RenderPipelineStage {
: zeroes_.data();
}
}
- PerformBlending(bg_row_ptrs_.data(), fg_row_ptrs_.data(),
- fg_row_ptrs_.data(), 0, xsize, blending_info_[0],
- blending_info_.data() + 1, *extra_channel_info_);
+ return PerformBlending(bg_row_ptrs_.data(), fg_row_ptrs_.data(),
+ fg_row_ptrs_.data(), 0, xsize, blending_info_[0],
+ blending_info_.data() + 1, *extra_channel_info_);
}
RenderPipelineChannelMode GetChannelMode(size_t c) const final {
diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_chroma_upsampling.cc b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_chroma_upsampling.cc
index 936fbd3a44..2bc88ada67 100644
--- a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_chroma_upsampling.cc
+++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_chroma_upsampling.cc
@@ -27,9 +27,9 @@ class HorizontalChromaUpsamplingStage : public RenderPipelineStage {
/*shift=*/1, /*border=*/1)),
c_(channel) {}
- void ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows,
- size_t xextra, size_t xsize, size_t xpos, size_t ypos,
- size_t thread_id) const final {
+ Status ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows,
+ size_t xextra, size_t xsize, size_t xpos, size_t ypos,
+ size_t thread_id) const final {
HWY_FULL(float) df;
xextra = RoundUpTo(xextra, Lanes(df));
auto threefour = Set(df, 0.75f);
@@ -45,6 +45,7 @@ class HorizontalChromaUpsamplingStage : public RenderPipelineStage {
auto right = MulAdd(onefour, next, current);
StoreInterleaved(df, left, right, row_out + x * 2);
}
+ return true;
}
RenderPipelineChannelMode GetChannelMode(size_t c) const final {
@@ -65,9 +66,9 @@ class VerticalChromaUpsamplingStage : public RenderPipelineStage {
/*shift=*/1, /*border=*/1)),
c_(channel) {}
- void ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows,
- size_t xextra, size_t xsize, size_t xpos, size_t ypos,
- size_t thread_id) const final {
+ Status ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows,
+ size_t xextra, size_t xsize, size_t xpos, size_t ypos,
+ size_t thread_id) const final {
HWY_FULL(float) df;
xextra = RoundUpTo(xextra, Lanes(df));
auto threefour = Set(df, 0.75f);
@@ -86,6 +87,7 @@ class VerticalChromaUpsamplingStage : public RenderPipelineStage {
Store(MulAdd(it, onefour, im_scaled), df, row_out0 + x);
Store(MulAdd(ib, onefour, im_scaled), df, row_out1 + x);
}
+ return true;
}
RenderPipelineChannelMode GetChannelMode(size_t c) const final {
diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_cms.cc b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_cms.cc
index 2465146b47..3202a03e44 100644
--- a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_cms.cc
+++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_cms.cc
@@ -7,10 +7,8 @@
#include <memory>
-#include "jxl/cms_interface.h"
-#include "jxl/color_encoding.h"
+#include "lib/jxl/base/status.h"
#include "lib/jxl/color_encoding_internal.h"
-#include "lib/jxl/common.h"
#include "lib/jxl/dec_xyb.h"
#undef HWY_TARGET_INCLUDE
@@ -19,7 +17,6 @@
#include <hwy/highway.h>
#include "lib/jxl/dec_xyb-inl.h"
-#include "lib/jxl/sanitizers.h"
HWY_BEFORE_NAMESPACE();
namespace jxl {
@@ -44,10 +41,10 @@ class CmsStage : public RenderPipelineStage {
not_mixing_color_and_grey;
}
- void ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows,
- size_t xextra, size_t xsize, size_t xpos, size_t ypos,
- size_t thread_id) const final {
- JXL_ASSERT(xsize == xsize_);
+ Status ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows,
+ size_t xextra, size_t xsize, size_t xpos, size_t ypos,
+ size_t thread_id) const final {
+ JXL_ASSERT(xsize <= xsize_);
// TODO(firsching): handle grey case seperately
// interleave
float* JXL_RESTRICT row0 = GetInputRow(input_rows, 0, 0);
@@ -62,16 +59,15 @@ class CmsStage : public RenderPipelineStage {
}
const float* buf_src = mutable_buf_src;
float* JXL_RESTRICT buf_dst = color_space_transform->BufDst(thread_id);
- if (!color_space_transform->Run(thread_id, buf_src, buf_dst)) {
- // TODO(firsching): somehow mark failing here?
- return;
- }
+ JXL_RETURN_IF_ERROR(
+ color_space_transform->Run(thread_id, buf_src, buf_dst, xsize));
// de-interleave
for (size_t x = 0; x < xsize; x++) {
row0[x] = buf_dst[3 * x + 0];
row1[x] = buf_dst[3 * x + 1];
row2[x] = buf_dst[3 * x + 2];
}
+ return true;
}
RenderPipelineChannelMode GetChannelMode(size_t c) const final {
return c < 3 ? RenderPipelineChannelMode::kInPlace
@@ -86,7 +82,7 @@ class CmsStage : public RenderPipelineStage {
std::unique_ptr<jxl::ColorSpaceTransform> color_space_transform;
ColorEncoding c_src_;
- void SetInputSizes(
+ Status SetInputSizes(
const std::vector<std::pair<size_t, size_t>>& input_sizes) override {
#if JXL_ENABLE_ASSERT
JXL_ASSERT(input_sizes.size() >= 3);
@@ -96,6 +92,7 @@ class CmsStage : public RenderPipelineStage {
}
#endif
xsize_ = input_sizes[0].first;
+ return true;
}
Status PrepareForThreads(size_t num_threads) override {
diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_epf.cc b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_epf.cc
index 5d1a379ede..d3030b02cb 100644
--- a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_epf.cc
+++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_epf.cc
@@ -8,7 +8,6 @@
#include "lib/jxl/base/common.h"
#include "lib/jxl/common.h" // JXL_HIGH_PRECISION
#include "lib/jxl/epf.h"
-#include "lib/jxl/sanitizers.h"
#undef HWY_TARGET_INCLUDE
#define HWY_TARGET_INCLUDE "lib/jxl/render_pipeline/stage_epf.cc"
@@ -67,9 +66,9 @@ class EPF0Stage : public RenderPipelineStage {
*B = MulAdd(weight, cb, *B);
}
- void ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows,
- size_t xextra, size_t xsize, size_t xpos, size_t ypos,
- size_t thread_id) const final {
+ Status ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows,
+ size_t xextra, size_t xsize, size_t xpos, size_t ypos,
+ size_t thread_id) const final {
DF df;
using V = decltype(Zero(df));
@@ -163,6 +162,7 @@ class EPF0Stage : public RenderPipelineStage {
StoreU(Mul(Y, inv_w), df, GetOutputRow(output_rows, 1, 0) + x);
StoreU(Mul(B, inv_w), df, GetOutputRow(output_rows, 2, 0) + x);
}
+ return true;
}
RenderPipelineChannelMode GetChannelMode(size_t c) const final {
@@ -207,9 +207,9 @@ class EPF1Stage : public RenderPipelineStage {
*B = MulAdd(weight, cb, *B);
}
- void ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows,
- size_t xextra, size_t xsize, size_t xpos, size_t ypos,
- size_t thread_id) const final {
+ Status ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows,
+ size_t xextra, size_t xsize, size_t xpos, size_t ypos,
+ size_t thread_id) const final {
DF df;
xextra = RoundUpTo(xextra, Lanes(df));
const float* JXL_RESTRICT row_sigma =
@@ -343,6 +343,7 @@ class EPF1Stage : public RenderPipelineStage {
Store(Mul(Y, inv_w), df, GetOutputRow(output_rows, 1, 0) + x);
Store(Mul(B, inv_w), df, GetOutputRow(output_rows, 2, 0) + x);
}
+ return true;
}
RenderPipelineChannelMode GetChannelMode(size_t c) const final {
@@ -392,9 +393,9 @@ class EPF2Stage : public RenderPipelineStage {
*B = MulAdd(weight, cb, *B);
}
- void ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows,
- size_t xextra, size_t xsize, size_t xpos, size_t ypos,
- size_t thread_id) const final {
+ Status ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows,
+ size_t xextra, size_t xsize, size_t xpos, size_t ypos,
+ size_t thread_id) const final {
DF df;
xextra = RoundUpTo(xextra, Lanes(df));
const float* JXL_RESTRICT row_sigma =
@@ -465,6 +466,7 @@ class EPF2Stage : public RenderPipelineStage {
Store(Mul(Y, inv_w), df, GetOutputRow(output_rows, 1, 0) + x);
Store(Mul(B, inv_w), df, GetOutputRow(output_rows, 2, 0) + x);
}
+ return true;
}
RenderPipelineChannelMode GetChannelMode(size_t c) const final {
diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_from_linear.cc b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_from_linear.cc
index 6b1f646cd5..922c7da366 100644
--- a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_from_linear.cc
+++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_from_linear.cc
@@ -68,7 +68,7 @@ struct OpPq {
};
struct OpHlg {
- explicit OpHlg(const float luminances[3], const float intensity_target)
+ explicit OpHlg(const Vector3& luminances, const float intensity_target)
: hlg_ootf_(HlgOOTF::ToSceneLight(/*display_luminance=*/intensity_target,
luminances)) {}
@@ -105,9 +105,9 @@ class FromLinearStage : public RenderPipelineStage {
: RenderPipelineStage(RenderPipelineStage::Settings()),
op_(std::move(op)) {}
- void ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows,
- size_t xextra, size_t xsize, size_t xpos, size_t ypos,
- size_t thread_id) const final {
+ Status ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows,
+ size_t xextra, size_t xsize, size_t xpos, size_t ypos,
+ size_t thread_id) const final {
const HWY_FULL(float) d;
const size_t xsize_v = RoundUpTo(xsize, Lanes(d));
float* JXL_RESTRICT row0 = GetInputRow(input_rows, 0, 0);
@@ -119,7 +119,8 @@ class FromLinearStage : public RenderPipelineStage {
msan::UnpoisonMemory(row0 + xsize, sizeof(float) * (xsize_v - xsize));
msan::UnpoisonMemory(row1 + xsize, sizeof(float) * (xsize_v - xsize));
msan::UnpoisonMemory(row2 + xsize, sizeof(float) * (xsize_v - xsize));
- for (ssize_t x = -xextra; x < (ssize_t)(xsize + xextra); x += Lanes(d)) {
+ for (ssize_t x = -xextra; x < static_cast<ssize_t>(xsize + xextra);
+ x += Lanes(d)) {
auto r = LoadU(d, row0 + x);
auto g = LoadU(d, row1 + x);
auto b = LoadU(d, row2 + x);
@@ -131,6 +132,7 @@ class FromLinearStage : public RenderPipelineStage {
msan::PoisonMemory(row0 + xsize, sizeof(float) * (xsize_v - xsize));
msan::PoisonMemory(row1 + xsize, sizeof(float) * (xsize_v - xsize));
msan::PoisonMemory(row2 + xsize, sizeof(float) * (xsize_v - xsize));
+ return true;
}
RenderPipelineChannelMode GetChannelMode(size_t c) const final {
diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_gaborish.cc b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_gaborish.cc
index 0917db3f9a..6fcd5e14df 100644
--- a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_gaborish.cc
+++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_gaborish.cc
@@ -44,9 +44,9 @@ class GaborishStage : public RenderPipelineStage {
}
}
- void ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows,
- size_t xextra, size_t xsize, size_t xpos, size_t ypos,
- size_t thread_id) const final {
+ Status ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows,
+ size_t xextra, size_t xsize, size_t xpos, size_t ypos,
+ size_t thread_id) const final {
const HWY_FULL(float) d;
for (size_t c = 0; c < 3; c++) {
float* JXL_RESTRICT row_t = GetInputRow(input_rows, c, -1);
@@ -66,7 +66,7 @@ class GaborishStage : public RenderPipelineStage {
// Since GetInputRow(input_rows, c, {-1, 0, 1}) is aligned, rounding
// xextra up to Lanes(d) doesn't access anything problematic.
for (ssize_t x = -RoundUpTo(xextra, Lanes(d));
- x < (ssize_t)(xsize + xextra); x += Lanes(d)) {
+ x < static_cast<ssize_t>(xsize + xextra); x += Lanes(d)) {
const auto t = LoadMaybeU(d, row_t + x);
const auto tl = LoadU(d, row_t + x - 1);
const auto tr = LoadU(d, row_t + x + 1);
@@ -83,6 +83,7 @@ class GaborishStage : public RenderPipelineStage {
Store(pixels, d, row_out + x);
}
}
+ return true;
}
#undef LoadMaybeU
diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_noise.cc b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_noise.cc
index 5cf8a6ed51..eca679b948 100644
--- a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_noise.cc
+++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_noise.cc
@@ -5,6 +5,8 @@
#include "lib/jxl/render_pipeline/stage_noise.h"
+#include "lib/jxl/noise.h"
+
#undef HWY_TARGET_INCLUDE
#define HWY_TARGET_INCLUDE "lib/jxl/render_pipeline/stage_noise.cc"
#include <hwy/foreach_target.h>
@@ -61,9 +63,10 @@ class StrengthEvalLut {
#endif
{
#if HWY_TARGET != HWY_SCALAR
- uint32_t lut[8];
- memcpy(lut, noise_params.lut, sizeof(lut));
- for (size_t i = 0; i < 8; i++) {
+ uint32_t lut[NoiseParams::kNumNoisePoints];
+ memcpy(lut, noise_params.lut.data(),
+ NoiseParams::kNumNoisePoints * sizeof(uint32_t));
+ for (size_t i = 0; i < NoiseParams::kNumNoisePoints; i++) {
low16_lut[2 * i] = (lut[i] >> 0) & 0xFF;
low16_lut[2 * i + 1] = (lut[i] >> 8) & 0xFF;
high16_lut[2 * i] = (lut[i] >> 16) & 0xFF;
@@ -161,10 +164,10 @@ class AddNoiseStage : public RenderPipelineStage {
cmap_(cmap),
first_c_(first_c) {}
- void ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows,
- size_t xextra, size_t xsize, size_t xpos, size_t ypos,
- size_t thread_id) const final {
- if (!noise_params_.HasAny()) return;
+ Status ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows,
+ size_t xextra, size_t xsize, size_t xpos, size_t ypos,
+ size_t thread_id) const final {
+ if (!noise_params_.HasAny()) return true;
const StrengthEvalLut noise_model(noise_params_);
D d;
const auto half = Set(d, 0.5f);
@@ -212,6 +215,7 @@ class AddNoiseStage : public RenderPipelineStage {
msan::PoisonMemory(row_x + xsize, (xsize_v - xsize) * sizeof(float));
msan::PoisonMemory(row_y + xsize, (xsize_v - xsize) * sizeof(float));
msan::PoisonMemory(row_b + xsize, (xsize_v - xsize) * sizeof(float));
+ return true;
}
RenderPipelineChannelMode GetChannelMode(size_t c) const final {
@@ -241,9 +245,9 @@ class ConvolveNoiseStage : public RenderPipelineStage {
/*shift=*/0, /*border=*/2)),
first_c_(first_c) {}
- void ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows,
- size_t xextra, size_t xsize, size_t xpos, size_t ypos,
- size_t thread_id) const final {
+ Status ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows,
+ size_t xextra, size_t xsize, size_t xpos, size_t ypos,
+ size_t thread_id) const final {
const HWY_FULL(float) d;
for (size_t c = first_c_; c < first_c_ + 3; c++) {
float* JXL_RESTRICT rows[5];
@@ -252,7 +256,7 @@ class ConvolveNoiseStage : public RenderPipelineStage {
}
float* JXL_RESTRICT row_out = GetOutputRow(output_rows, c, 0);
for (ssize_t x = -RoundUpTo(xextra, Lanes(d));
- x < (ssize_t)(xsize + xextra); x += Lanes(d)) {
+ x < static_cast<ssize_t>(xsize + xextra); x += Lanes(d)) {
const auto p00 = LoadU(d, rows[2] + x);
auto others = Zero(d);
// TODO(eustas): sum loaded values to reduce the calculation chain
@@ -271,6 +275,7 @@ class ConvolveNoiseStage : public RenderPipelineStage {
StoreU(pixels, d, row_out + x);
}
}
+ return true;
}
RenderPipelineChannelMode GetChannelMode(size_t c) const final {
diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_patches.cc b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_patches.cc
index c5a75b09f7..e0a66167d4 100644
--- a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_patches.cc
+++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_patches.cc
@@ -14,16 +14,17 @@ class PatchDictionaryStage : public RenderPipelineStage {
patches_(*patches),
num_channels_(num_channels) {}
- void ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows,
- size_t xextra, size_t xsize, size_t xpos, size_t ypos,
- size_t thread_id) const final {
+ Status ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows,
+ size_t xextra, size_t xsize, size_t xpos, size_t ypos,
+ size_t thread_id) const final {
JXL_ASSERT(xpos == 0 || xpos >= xextra);
size_t x0 = xpos ? xpos - xextra : 0;
std::vector<float*> row_ptrs(num_channels_);
for (size_t i = 0; i < num_channels_; i++) {
row_ptrs[i] = GetInputRow(input_rows, i, 0) + x0 - xpos;
}
- patches_.AddOneRow(row_ptrs.data(), ypos, x0, xsize + xextra + xpos - x0);
+ return patches_.AddOneRow(row_ptrs.data(), ypos, x0,
+ xsize + xextra + xpos - x0);
}
RenderPipelineChannelMode GetChannelMode(size_t c) const final {
diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_splines.cc b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_splines.cc
index 4a0529ce2c..92a13090a7 100644
--- a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_splines.cc
+++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_splines.cc
@@ -20,13 +20,14 @@ class SplineStage : public RenderPipelineStage {
: RenderPipelineStage(RenderPipelineStage::Settings()),
splines_(*splines) {}
- void ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows,
- size_t xextra, size_t xsize, size_t xpos, size_t ypos,
- size_t thread_id) const final {
+ Status ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows,
+ size_t xextra, size_t xsize, size_t xpos, size_t ypos,
+ size_t thread_id) const final {
float* row_x = GetInputRow(input_rows, 0, 0);
float* row_y = GetInputRow(input_rows, 1, 0);
float* row_b = GetInputRow(input_rows, 2, 0);
splines_.AddToRow(row_x, row_y, row_b, Rect(xpos, ypos, xsize, 1));
+ return true;
}
RenderPipelineChannelMode GetChannelMode(size_t c) const final {
diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_spot.cc b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_spot.cc
index a43cb4e1ab..18588e2012 100644
--- a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_spot.cc
+++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_spot.cc
@@ -15,19 +15,20 @@ class SpotColorStage : public RenderPipelineStage {
JXL_ASSERT(spot_c_ >= 3);
}
- void ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows,
- size_t xextra, size_t xsize, size_t xpos, size_t ypos,
- size_t thread_id) const final {
+ Status ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows,
+ size_t xextra, size_t xsize, size_t xpos, size_t ypos,
+ size_t thread_id) const final {
// TODO(veluca): add SIMD.
float scale = spot_color_[3];
for (size_t c = 0; c < 3; c++) {
float* JXL_RESTRICT p = GetInputRow(input_rows, c, 0);
const float* JXL_RESTRICT s = GetInputRow(input_rows, spot_c_, 0);
- for (ssize_t x = -xextra; x < ssize_t(xsize + xextra); x++) {
+ for (ssize_t x = -xextra; x < static_cast<ssize_t>(xsize + xextra); x++) {
float mix = scale * s[x];
p[x] = mix * spot_color_[c] + (1.0f - mix) * p[x];
}
}
+ return true;
}
RenderPipelineChannelMode GetChannelMode(size_t c) const final {
diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_to_linear.cc b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_to_linear.cc
index 85eca2f039..c2c5ac484b 100644
--- a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_to_linear.cc
+++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_to_linear.cc
@@ -63,7 +63,7 @@ struct OpPq {
};
struct OpHlg {
- explicit OpHlg(const float luminances[3], const float intensity_target)
+ explicit OpHlg(const Vector3& luminances, const float intensity_target)
: hlg_ootf_(HlgOOTF::FromSceneLight(
/*display_luminance=*/intensity_target, luminances)) {}
@@ -113,9 +113,9 @@ class ToLinearStage : public RenderPipelineStage {
explicit ToLinearStage()
: RenderPipelineStage(RenderPipelineStage::Settings()), valid_(false) {}
- void ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows,
- size_t xextra, size_t xsize, size_t xpos, size_t ypos,
- size_t thread_id) const final {
+ Status ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows,
+ size_t xextra, size_t xsize, size_t xpos, size_t ypos,
+ size_t thread_id) const final {
const HWY_FULL(float) d;
const size_t xsize_v = RoundUpTo(xsize, Lanes(d));
float* JXL_RESTRICT row0 = GetInputRow(input_rows, 0, 0);
@@ -127,7 +127,8 @@ class ToLinearStage : public RenderPipelineStage {
msan::UnpoisonMemory(row0 + xsize, sizeof(float) * (xsize_v - xsize));
msan::UnpoisonMemory(row1 + xsize, sizeof(float) * (xsize_v - xsize));
msan::UnpoisonMemory(row2 + xsize, sizeof(float) * (xsize_v - xsize));
- for (ssize_t x = -xextra; x < (ssize_t)(xsize + xextra); x += Lanes(d)) {
+ for (ssize_t x = -xextra; x < static_cast<ssize_t>(xsize + xextra);
+ x += Lanes(d)) {
auto r = LoadU(d, row0 + x);
auto g = LoadU(d, row1 + x);
auto b = LoadU(d, row2 + x);
@@ -139,6 +140,7 @@ class ToLinearStage : public RenderPipelineStage {
msan::PoisonMemory(row0 + xsize, sizeof(float) * (xsize_v - xsize));
msan::PoisonMemory(row1 + xsize, sizeof(float) * (xsize_v - xsize));
msan::PoisonMemory(row2 + xsize, sizeof(float) * (xsize_v - xsize));
+ return true;
}
RenderPipelineChannelMode GetChannelMode(size_t c) const final {
diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_tone_mapping.cc b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_tone_mapping.cc
index 2a272e15dc..e8cd90b244 100644
--- a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_tone_mapping.cc
+++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_tone_mapping.cc
@@ -56,10 +56,10 @@ class ToneMappingStage : public RenderPipelineStage {
bool IsNeeded() const { return tone_mapper_ || hlg_ootf_; }
- void ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows,
- size_t xextra, size_t xsize, size_t xpos, size_t ypos,
- size_t thread_id) const final {
- if (!(tone_mapper_ || hlg_ootf_)) return;
+ Status ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows,
+ size_t xextra, size_t xsize, size_t xpos, size_t ypos,
+ size_t thread_id) const final {
+ if (!(tone_mapper_ || hlg_ootf_)) return true;
const HWY_FULL(float) d;
const size_t xsize_v = RoundUpTo(xsize, Lanes(d));
@@ -72,7 +72,8 @@ class ToneMappingStage : public RenderPipelineStage {
msan::UnpoisonMemory(row0 + xsize, sizeof(float) * (xsize_v - xsize));
msan::UnpoisonMemory(row1 + xsize, sizeof(float) * (xsize_v - xsize));
msan::UnpoisonMemory(row2 + xsize, sizeof(float) * (xsize_v - xsize));
- for (ssize_t x = -xextra; x < (ssize_t)(xsize + xextra); x += Lanes(d)) {
+ for (ssize_t x = -xextra; x < static_cast<ssize_t>(xsize + xextra);
+ x += Lanes(d)) {
auto r = LoadU(d, row0 + x);
auto g = LoadU(d, row1 + x);
auto b = LoadU(d, row2 + x);
@@ -100,6 +101,7 @@ class ToneMappingStage : public RenderPipelineStage {
msan::PoisonMemory(row0 + xsize, sizeof(float) * (xsize_v - xsize));
msan::PoisonMemory(row1 + xsize, sizeof(float) * (xsize_v - xsize));
msan::PoisonMemory(row2 + xsize, sizeof(float) * (xsize_v - xsize));
+ return true;
}
RenderPipelineChannelMode GetChannelMode(size_t c) const final {
diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_upsampling.cc b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_upsampling.cc
index ade37d59a6..897b20c4c6 100644
--- a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_upsampling.cc
+++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_upsampling.cc
@@ -46,9 +46,9 @@ class UpsamplingStage : public RenderPipelineStage {
}
}
- void ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows,
- size_t xextra, size_t xsize, size_t xpos, size_t ypos,
- size_t thread_id) const final {
+ Status ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows,
+ size_t xextra, size_t xsize, size_t xpos, size_t ypos,
+ size_t thread_id) const final {
static HWY_FULL(float) df;
size_t shift = settings_.shift_x;
size_t N = 1 << shift;
@@ -74,6 +74,7 @@ class UpsamplingStage : public RenderPipelineStage {
msan::PoisonMemory(dst_row + xsize * N,
sizeof(float) * (xsize_v - xsize) * N);
}
+ return true;
}
RenderPipelineChannelMode GetChannelMode(size_t c) const final {
diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_write.cc b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_write.cc
index 847972acc8..c5a91e8efd 100644
--- a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_write.cc
+++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_write.cc
@@ -9,7 +9,9 @@
#include "lib/jxl/alpha.h"
#include "lib/jxl/base/common.h"
+#include "lib/jxl/base/status.h"
#include "lib/jxl/dec_cache.h"
+#include "lib/jxl/image.h"
#include "lib/jxl/image_bundle.h"
#include "lib/jxl/sanitizers.h"
@@ -150,13 +152,13 @@ class WriteToOutputStage : public RenderPipelineStage {
}
}
- void ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows,
- size_t xextra, size_t xsize, size_t xpos, size_t ypos,
- size_t thread_id) const final {
+ Status ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows,
+ size_t xextra, size_t xsize, size_t xpos, size_t ypos,
+ size_t thread_id) const final {
JXL_DASSERT(xextra == 0);
JXL_DASSERT(main_.run_opaque_ || main_.buffer_);
- if (ypos >= height_) return;
- if (xpos >= width_) return;
+ if (ypos >= height_) return true;
+ if (xpos >= width_) return true;
if (flip_y_) {
ypos = height_ - 1u - ypos;
}
@@ -184,6 +186,7 @@ class WriteToOutputStage : public RenderPipelineStage {
OutputBuffers(extra, thread_id, ypos, xstart, len, line_buffers);
}
}
+ return true;
}
RenderPipelineChannelMode GetChannelMode(size_t c) const final {
@@ -202,7 +205,7 @@ class WriteToOutputStage : public RenderPipelineStage {
private:
struct Output {
- Output(const ImageOutput& image_out)
+ explicit Output(const ImageOutput& image_out)
: pixel_callback_(image_out.callback),
buffer_(image_out.buffer),
buffer_size_(image_out.buffer_size),
@@ -406,8 +409,8 @@ class WriteToOutputStage : public RenderPipelineStage {
sizeof(output[0]) * out.num_channels_ * padding);
}
- void StoreFloat16Row(const Output& out, const float* input[4], size_t len,
- uint16_t* output) const {
+ static void StoreFloat16Row(const Output& out, const float* input[4],
+ size_t len, uint16_t* output) {
const HWY_FULL(float) d;
const Rebind<uint16_t, decltype(d)> du;
const Rebind<hwy::float16_t, decltype(d)> df16;
@@ -452,8 +455,8 @@ class WriteToOutputStage : public RenderPipelineStage {
sizeof(output[0]) * out.num_channels_ * padding);
}
- void StoreFloatRow(const Output& out, const float* input[4], size_t len,
- float* output) const {
+ static void StoreFloatRow(const Output& out, const float* input[4],
+ size_t len, float* output) {
const HWY_FULL(float) d;
if (out.num_channels_ == 1) {
memcpy(output, input[0], len * sizeof(output[0]));
@@ -559,7 +562,7 @@ class WriteToImageBundleStage : public RenderPipelineStage {
image_bundle_(image_bundle),
color_encoding_(std::move(color_encoding)) {}
- void SetInputSizes(
+ Status SetInputSizes(
const std::vector<std::pair<size_t, size_t>>& input_sizes) override {
#if JXL_ENABLE_ASSERT
JXL_ASSERT(input_sizes.size() >= 3);
@@ -569,19 +572,22 @@ class WriteToImageBundleStage : public RenderPipelineStage {
}
#endif
// TODO(eustas): what should we do in the case of "want only ECs"?
- image_bundle_->SetFromImage(
- Image3F(input_sizes[0].first, input_sizes[0].second), color_encoding_);
+ JXL_ASSIGN_OR_RETURN(Image3F tmp, Image3F::Create(input_sizes[0].first,
+ input_sizes[0].second));
+ image_bundle_->SetFromImage(std::move(tmp), color_encoding_);
// TODO(veluca): consider not reallocating ECs if not needed.
image_bundle_->extra_channels().clear();
for (size_t c = 3; c < input_sizes.size(); c++) {
- image_bundle_->extra_channels().emplace_back(input_sizes[c].first,
- input_sizes[c].second);
+ JXL_ASSIGN_OR_RETURN(ImageF ch, ImageF::Create(input_sizes[c].first,
+ input_sizes[c].second));
+ image_bundle_->extra_channels().emplace_back(std::move(ch));
}
+ return true;
}
- void ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows,
- size_t xextra, size_t xsize, size_t xpos, size_t ypos,
- size_t thread_id) const final {
+ Status ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows,
+ size_t xextra, size_t xsize, size_t xpos, size_t ypos,
+ size_t thread_id) const final {
for (size_t c = 0; c < 3; c++) {
memcpy(image_bundle_->color()->PlaneRow(c, ypos) + xpos - xextra,
GetInputRow(input_rows, c, 0) - xextra,
@@ -594,6 +600,7 @@ class WriteToImageBundleStage : public RenderPipelineStage {
GetInputRow(input_rows, 3 + ec, 0) - xextra,
sizeof(float) * (xsize + 2 * xextra));
}
+ return true;
}
RenderPipelineChannelMode GetChannelMode(size_t c) const final {
@@ -612,7 +619,7 @@ class WriteToImage3FStage : public RenderPipelineStage {
explicit WriteToImage3FStage(Image3F* image)
: RenderPipelineStage(RenderPipelineStage::Settings()), image_(image) {}
- void SetInputSizes(
+ Status SetInputSizes(
const std::vector<std::pair<size_t, size_t>>& input_sizes) override {
#if JXL_ENABLE_ASSERT
JXL_ASSERT(input_sizes.size() >= 3);
@@ -621,17 +628,20 @@ class WriteToImage3FStage : public RenderPipelineStage {
JXL_ASSERT(input_sizes[c].second == input_sizes[0].second);
}
#endif
- *image_ = Image3F(input_sizes[0].first, input_sizes[0].second);
+ JXL_ASSIGN_OR_RETURN(
+ *image_, Image3F::Create(input_sizes[0].first, input_sizes[0].second));
+ return true;
}
- void ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows,
- size_t xextra, size_t xsize, size_t xpos, size_t ypos,
- size_t thread_id) const final {
+ Status ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows,
+ size_t xextra, size_t xsize, size_t xpos, size_t ypos,
+ size_t thread_id) const final {
for (size_t c = 0; c < 3; c++) {
memcpy(image_->PlaneRow(c, ypos) + xpos - xextra,
GetInputRow(input_rows, c, 0) - xextra,
sizeof(float) * (xsize + 2 * xextra));
}
+ return true;
}
RenderPipelineChannelMode GetChannelMode(size_t c) const final {
diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_xyb.cc b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_xyb.cc
index 56e86e6095..a20e686de6 100644
--- a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_xyb.cc
+++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_xyb.cc
@@ -28,9 +28,9 @@ class XYBStage : public RenderPipelineStage {
output_is_xyb_(output_encoding_info.color_encoding.GetColorSpace() ==
ColorSpace::kXYB) {}
- void ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows,
- size_t xextra, size_t xsize, size_t xpos, size_t ypos,
- size_t thread_id) const final {
+ Status ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows,
+ size_t xextra, size_t xsize, size_t xpos, size_t ypos,
+ size_t thread_id) const final {
const HWY_FULL(float) d;
JXL_ASSERT(xextra == 0);
const size_t xsize_v = RoundUpTo(xsize, Lanes(d));
@@ -52,7 +52,8 @@ class XYBStage : public RenderPipelineStage {
const auto offset_x = Set(d, jxl::cms::kScaledXYBOffset[0]);
const auto offset_y = Set(d, jxl::cms::kScaledXYBOffset[1]);
const auto offset_bmy = Set(d, jxl::cms::kScaledXYBOffset[2]);
- for (ssize_t x = -xextra; x < (ssize_t)(xsize + xextra); x += Lanes(d)) {
+ for (ssize_t x = -xextra; x < static_cast<ssize_t>(xsize + xextra);
+ x += Lanes(d)) {
const auto in_x = LoadU(d, row0 + x);
const auto in_y = LoadU(d, row1 + x);
const auto in_b = LoadU(d, row2 + x);
@@ -64,7 +65,8 @@ class XYBStage : public RenderPipelineStage {
StoreU(out_b, d, row2 + x);
}
} else {
- for (ssize_t x = -xextra; x < (ssize_t)(xsize + xextra); x += Lanes(d)) {
+ for (ssize_t x = -xextra; x < static_cast<ssize_t>(xsize + xextra);
+ x += Lanes(d)) {
const auto in_opsin_x = LoadU(d, row0 + x);
const auto in_opsin_y = LoadU(d, row1 + x);
const auto in_opsin_b = LoadU(d, row2 + x);
@@ -81,6 +83,7 @@ class XYBStage : public RenderPipelineStage {
msan::PoisonMemory(row0 + xsize, sizeof(float) * (xsize_v - xsize));
msan::PoisonMemory(row1 + xsize, sizeof(float) * (xsize_v - xsize));
msan::PoisonMemory(row2 + xsize, sizeof(float) * (xsize_v - xsize));
+ return true;
}
RenderPipelineChannelMode GetChannelMode(size_t c) const final {
@@ -130,10 +133,10 @@ class FastXYBStage : public RenderPipelineStage {
has_alpha_(has_alpha),
alpha_c_(alpha_c) {}
- void ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows,
- size_t xextra, size_t xsize, size_t xpos, size_t ypos,
- size_t thread_id) const final {
- if (ypos >= height_) return;
+ Status ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows,
+ size_t xextra, size_t xsize, size_t xpos, size_t ypos,
+ size_t thread_id) const final {
+ if (ypos >= height_) return true;
JXL_ASSERT(xextra == 0);
const float* xyba[4] = {
GetInputRow(input_rows, 0, 0), GetInputRow(input_rows, 1, 0),
@@ -142,6 +145,7 @@ class FastXYBStage : public RenderPipelineStage {
uint8_t* out_buf = rgb_ + stride_ * ypos + (rgba_ ? 4 : 3) * xpos;
FastXYBTosRGB8(xyba, out_buf, rgba_,
xsize + xpos <= width_ ? xsize : width_ - xpos);
+ return true;
}
RenderPipelineChannelMode GetChannelMode(size_t c) const final {
diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_ycbcr.cc b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_ycbcr.cc
index 30ad327221..f21a00c728 100644
--- a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_ycbcr.cc
+++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_ycbcr.cc
@@ -22,9 +22,9 @@ class kYCbCrStage : public RenderPipelineStage {
public:
kYCbCrStage() : RenderPipelineStage(RenderPipelineStage::Settings()) {}
- void ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows,
- size_t xextra, size_t xsize, size_t xpos, size_t ypos,
- size_t thread_id) const final {
+ Status ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows,
+ size_t xextra, size_t xsize, size_t xpos, size_t ypos,
+ size_t thread_id) const final {
const HWY_FULL(float) df;
// Full-range BT.601 as defined by JFIF Clause 7:
@@ -51,6 +51,7 @@ class kYCbCrStage : public RenderPipelineStage {
StoreU(g_vec, df, row1 + x);
StoreU(b_vec, df, row2 + x);
}
+ return true;
}
RenderPipelineChannelMode GetChannelMode(size_t c) const final {
diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/test_render_pipeline_stages.h b/third_party/jpeg-xl/lib/jxl/render_pipeline/test_render_pipeline_stages.h
index 789a52f8b2..c2c25c46c3 100644
--- a/third_party/jpeg-xl/lib/jxl/render_pipeline/test_render_pipeline_stages.h
+++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/test_render_pipeline_stages.h
@@ -7,10 +7,7 @@
#include <stdint.h>
#include <stdio.h>
-#include <algorithm>
-#include <utility>
-#include <vector>
-
+#include "lib/jxl/base/status.h"
#include "lib/jxl/render_pipeline/render_pipeline_stage.h"
namespace jxl {
@@ -20,13 +17,13 @@ class UpsampleXSlowStage : public RenderPipelineStage {
UpsampleXSlowStage()
: RenderPipelineStage(RenderPipelineStage::Settings::ShiftX(1, 1)) {}
- void ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows,
- size_t xextra, size_t xsize, size_t xpos, size_t ypos,
- size_t thread_id) const final {
+ Status ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows,
+ size_t xextra, size_t xsize, size_t xpos, size_t ypos,
+ size_t thread_id) const final {
for (size_t c = 0; c < input_rows.size(); c++) {
const float* row = GetInputRow(input_rows, c, 0);
float* row_out = GetOutputRow(output_rows, c, 0);
- for (int64_t x = -xextra; x < (int64_t)(xsize + xextra); x++) {
+ for (int64_t x = -xextra; x < static_cast<int64_t>(xsize + xextra); x++) {
float xp = *(row + x - 1);
float xc = *(row + x);
float xn = *(row + x + 1);
@@ -36,6 +33,7 @@ class UpsampleXSlowStage : public RenderPipelineStage {
*(row_out + 2 * x + 1) = xout1;
}
}
+ return true;
}
const char* GetName() const override { return "TEST::UpsampleXSlowStage"; }
@@ -50,16 +48,16 @@ class UpsampleYSlowStage : public RenderPipelineStage {
UpsampleYSlowStage()
: RenderPipelineStage(RenderPipelineStage::Settings::ShiftY(1, 1)) {}
- void ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows,
- size_t xextra, size_t xsize, size_t xpos, size_t ypos,
- size_t thread_id) const final {
+ Status ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows,
+ size_t xextra, size_t xsize, size_t xpos, size_t ypos,
+ size_t thread_id) const final {
for (size_t c = 0; c < input_rows.size(); c++) {
const float* rowp = GetInputRow(input_rows, c, -1);
const float* rowc = GetInputRow(input_rows, c, 0);
const float* rown = GetInputRow(input_rows, c, 1);
float* row_out0 = GetOutputRow(output_rows, c, 0);
float* row_out1 = GetOutputRow(output_rows, c, 1);
- for (int64_t x = -xextra; x < (int64_t)(xsize + xextra); x++) {
+ for (int64_t x = -xextra; x < static_cast<int64_t>(xsize + xextra); x++) {
float xp = *(rowp + x);
float xc = *(rowc + x);
float xn = *(rown + x);
@@ -69,6 +67,7 @@ class UpsampleYSlowStage : public RenderPipelineStage {
*(row_out1 + x) = yout1;
}
}
+ return true;
}
RenderPipelineChannelMode GetChannelMode(size_t c) const final {
@@ -82,14 +81,15 @@ class Check0FinalStage : public RenderPipelineStage {
public:
Check0FinalStage() : RenderPipelineStage(RenderPipelineStage::Settings()) {}
- void ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows,
- size_t xextra, size_t xsize, size_t xpos, size_t ypos,
- size_t thread_id) const final {
+ Status ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows,
+ size_t xextra, size_t xsize, size_t xpos, size_t ypos,
+ size_t thread_id) const final {
for (size_t c = 0; c < input_rows.size(); c++) {
for (size_t x = 0; x < xsize; x++) {
JXL_CHECK(fabsf(GetInputRow(input_rows, c, 0)[x]) < 1e-8);
}
}
+ return true;
}
RenderPipelineChannelMode GetChannelMode(size_t c) const final {
diff --git a/third_party/jpeg-xl/lib/jxl/roundtrip_test.cc b/third_party/jpeg-xl/lib/jxl/roundtrip_test.cc
index c00fda0de1..a4a87bebb7 100644
--- a/third_party/jpeg-xl/lib/jxl/roundtrip_test.cc
+++ b/third_party/jpeg-xl/lib/jxl/roundtrip_test.cc
@@ -26,7 +26,6 @@
#include "lib/jxl/butteraugli/butteraugli.h"
#include "lib/jxl/color_encoding_internal.h"
#include "lib/jxl/dec_bit_reader.h"
-#include "lib/jxl/enc_butteraugli_comparator.h"
#include "lib/jxl/enc_external_image.h"
#include "lib/jxl/encode_internal.h"
#include "lib/jxl/image.h"
@@ -37,6 +36,9 @@
namespace {
+using jxl::ImageF;
+using jxl::test::ButteraugliDistance;
+
// Converts a test image to a CodecInOut.
// icc_profile can be empty to automatically deduce profile from the pixel
// format, or filled in to force this ICC profile
@@ -94,7 +96,7 @@ jxl::CodecInOut ConvertTestImage(const std::vector<uint8_t>& buf,
jxl::ColorEncoding color_encoding;
if (!icc_profile.empty()) {
jxl::IccBytes icc_profile_copy;
- icc_profile.AppendTo(&icc_profile_copy);
+ icc_profile.AppendTo(icc_profile_copy);
EXPECT_TRUE(
color_encoding.SetICC(std::move(icc_profile_copy), JxlGetDefaultCms()));
} else if (pixel_format.data_type == JXL_TYPE_FLOAT) {
@@ -118,12 +120,12 @@ float ConvertTestPixel<float>(const float val) {
template <>
uint16_t ConvertTestPixel<uint16_t>(const float val) {
- return (uint16_t)(val * UINT16_MAX);
+ return static_cast<uint16_t>(val * UINT16_MAX);
}
template <>
uint8_t ConvertTestPixel<uint8_t>(const float val) {
- return (uint8_t)(val * UINT8_MAX);
+ return static_cast<uint8_t>(val * UINT8_MAX);
}
// Returns a test image.
@@ -146,6 +148,7 @@ std::vector<uint8_t> GetTestImage(const size_t xsize, const size_t ysize,
val = static_cast<float>(x + y) / static_cast<float>(xsize + ysize);
break;
case 3:
+ default:
val = static_cast<float>(x * y) / static_cast<float>(xsize * ysize);
break;
}
@@ -233,7 +236,7 @@ void VerifyRoundtripCompression(
}
}
if (alpha_in_extra_channels_vector && !has_interleaved_alpha) {
- jxl::ImageF alpha_channel(xsize, ysize);
+ JXL_ASSIGN_OR_DIE(ImageF alpha_channel, ImageF::Create(xsize, ysize));
EXPECT_TRUE(jxl::ConvertFromExternal(
extra_channel_bytes.data(), extra_channel_bytes.size(), xsize, ysize,
basic_info.bits_per_sample, extra_channel_pixel_format, 0,
@@ -298,17 +301,18 @@ void VerifyRoundtripCompression(
frame_settings, JXL_ENC_FRAME_SETTING_ALREADY_DOWNSAMPLED,
already_downsampled));
}
- EXPECT_EQ(JXL_ENC_SUCCESS,
- JxlEncoderAddImageFrame(frame_settings, &input_pixel_format,
- (void*)original_bytes.data(),
- original_bytes.size()));
+ EXPECT_EQ(
+ JXL_ENC_SUCCESS,
+ JxlEncoderAddImageFrame(frame_settings, &input_pixel_format,
+ static_cast<const void*>(original_bytes.data()),
+ original_bytes.size()));
EXPECT_EQ(frame_settings->enc->input_queue.empty(), false);
for (size_t index = 0; index < channel_infos.size(); index++) {
EXPECT_EQ(JXL_ENC_SUCCESS,
JxlEncoderSetExtraChannelBuffer(
frame_settings, &extra_channel_pixel_format,
- (void*)extra_channel_bytes.data(), extra_channel_bytes.size(),
- index + has_interleaved_alpha));
+ static_cast<const void*>(extra_channel_bytes.data()),
+ extra_channel_bytes.size(), index + has_interleaved_alpha));
}
JxlEncoderCloseInput(enc);
std::vector<uint8_t> compressed;
@@ -412,10 +416,10 @@ void VerifyRoundtripCompression(
if (already_downsampled) {
jxl::Image3F* color = decoded_io.Main().color();
- jxl::DownsampleImage(color, resampling);
+ JXL_ASSIGN_OR_DIE(*color, jxl::DownsampleImage(*color, resampling));
if (decoded_io.Main().HasAlpha()) {
- jxl::ImageF* alpha = decoded_io.Main().alpha();
- jxl::DownsampleImage(alpha, resampling);
+ ImageF* alpha = decoded_io.Main().alpha();
+ JXL_ASSIGN_OR_DIE(*alpha, jxl::DownsampleImage(*alpha, resampling));
}
decoded_io.SetSize(color->xsize(), color->ysize());
}
@@ -463,8 +467,8 @@ TEST(RoundtripTest, FloatFrameRoundtripTest) {
{JXL_CHANNEL_CFA, "my cfa channel"},
{JXL_CHANNEL_OPTIONAL, "optional channel"}},
{{JXL_CHANNEL_DEPTH, "very deep"}}};
- for (int use_container = 0; use_container < 2; use_container++) {
- for (int lossless = 0; lossless < 2; lossless++) {
+ for (bool use_container : {false, true}) {
+ for (bool lossless : {false, true}) {
for (uint32_t num_channels = 1; num_channels < 5; num_channels++) {
for (auto& extra_channels : extra_channels_cases) {
uint32_t has_alpha = static_cast<uint32_t>(num_channels % 2 == 0);
@@ -475,8 +479,8 @@ TEST(RoundtripTest, FloatFrameRoundtripTest) {
JxlPixelFormat pixel_format = JxlPixelFormat{
num_channels, JXL_TYPE_FLOAT, JXL_NATIVE_ENDIAN, 0};
VerifyRoundtripCompression<float>(
- 63, 129, pixel_format, pixel_format, (bool)lossless,
- (bool)use_container, 1, false, extra_channels);
+ 63, 129, pixel_format, pixel_format, lossless, use_container, 1,
+ false, extra_channels);
}
}
}
@@ -499,8 +503,8 @@ TEST(RoundtripTest, Uint16FrameRoundtripTest) {
JxlPixelFormat pixel_format = JxlPixelFormat{
num_channels, JXL_TYPE_UINT16, JXL_NATIVE_ENDIAN, 0};
VerifyRoundtripCompression<uint16_t>(
- 63, 129, pixel_format, pixel_format, (bool)lossless,
- (bool)use_container, 1, false, extra_channels);
+ 63, 129, pixel_format, pixel_format, static_cast<bool>(lossless),
+ static_cast<bool>(use_container), 1, false, extra_channels);
}
}
}
@@ -523,8 +527,8 @@ TEST(RoundtripTest, Uint8FrameRoundtripTest) {
JxlPixelFormat pixel_format = JxlPixelFormat{
num_channels, JXL_TYPE_UINT8, JXL_NATIVE_ENDIAN, 0};
VerifyRoundtripCompression<uint8_t>(
- 63, 129, pixel_format, pixel_format, (bool)lossless,
- (bool)use_container, 1, false, extra_channels);
+ 63, 129, pixel_format, pixel_format, static_cast<bool>(lossless),
+ static_cast<bool>(use_container), 1, false, extra_channels);
}
}
}
@@ -540,7 +544,7 @@ TEST(RoundtripTest, TestNonlinearSrgbAsXybEncoded) {
JxlPixelFormat{num_channels, JXL_TYPE_FLOAT, JXL_NATIVE_ENDIAN, 0};
VerifyRoundtripCompression<uint8_t>(
63, 129, pixel_format_in, pixel_format_out,
- /*lossless=*/false, (bool)use_container, 1, false, {});
+ /*lossless=*/false, static_cast<bool>(use_container), 1, false, {});
}
}
}
@@ -586,25 +590,25 @@ TEST(RoundtripTest, ExtraBoxesTest) {
jxl::test::JxlBasicInfoSetFromPixelFormat(&basic_info, &pixel_format);
basic_info.xsize = xsize;
basic_info.ysize = ysize;
- basic_info.uses_original_profile = false;
+ basic_info.uses_original_profile = JXL_FALSE;
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetCodestreamLevel(enc, 10));
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetBasicInfo(enc, &basic_info));
JxlColorEncoding color_encoding;
+ JXL_BOOL is_gray = TO_JXL_BOOL(pixel_format.num_channels < 3);
if (pixel_format.data_type == JXL_TYPE_FLOAT) {
- JxlColorEncodingSetToLinearSRGB(&color_encoding,
- /*is_gray=*/pixel_format.num_channels < 3);
+ JxlColorEncodingSetToLinearSRGB(&color_encoding, is_gray);
} else {
- JxlColorEncodingSetToSRGB(&color_encoding,
- /*is_gray=*/pixel_format.num_channels < 3);
+ JxlColorEncodingSetToSRGB(&color_encoding, is_gray);
}
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetColorEncoding(enc, &color_encoding));
JxlEncoderFrameSettings* frame_settings =
JxlEncoderFrameSettingsCreate(enc, nullptr);
- JxlEncoderSetFrameLossless(frame_settings, false);
- EXPECT_EQ(JXL_ENC_SUCCESS,
- JxlEncoderAddImageFrame(frame_settings, &pixel_format,
- (void*)original_bytes.data(),
- original_bytes.size()));
+ JxlEncoderSetFrameLossless(frame_settings, JXL_FALSE);
+ EXPECT_EQ(
+ JXL_ENC_SUCCESS,
+ JxlEncoderAddImageFrame(frame_settings, &pixel_format,
+ static_cast<const void*>(original_bytes.data()),
+ original_bytes.size()));
JxlEncoderCloseInput(enc);
std::vector<uint8_t> compressed;
@@ -703,19 +707,17 @@ TEST(RoundtripTest, MultiFrameTest) {
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetBasicInfo(enc, &basic_info));
JxlColorEncoding color_encoding;
+ JXL_BOOL is_gray = TO_JXL_BOOL(pixel_format.num_channels < 3);
if (pixel_format.data_type == JXL_TYPE_FLOAT) {
- JxlColorEncodingSetToLinearSRGB(
- &color_encoding,
- /*is_gray=*/pixel_format.num_channels < 3);
+ JxlColorEncodingSetToLinearSRGB(&color_encoding, is_gray);
} else {
- JxlColorEncodingSetToSRGB(&color_encoding,
- /*is_gray=*/pixel_format.num_channels < 3);
+ JxlColorEncodingSetToSRGB(&color_encoding, is_gray);
}
EXPECT_EQ(JXL_ENC_SUCCESS,
JxlEncoderSetColorEncoding(enc, &color_encoding));
JxlEncoderFrameSettings* frame_settings =
JxlEncoderFrameSettingsCreate(enc, nullptr);
- JxlEncoderSetFrameLossless(frame_settings, false);
+ JxlEncoderSetFrameLossless(frame_settings, JXL_FALSE);
if (index_frames == 1) {
EXPECT_EQ(JXL_ENC_SUCCESS,
JxlEncoderFrameSettingsSetOption(frame_settings,
@@ -733,9 +735,10 @@ TEST(RoundtripTest, MultiFrameTest) {
JxlEncoderSetFrameHeader(frame_settings, &frame_header);
EXPECT_EQ(
JXL_ENC_SUCCESS,
- JxlEncoderAddImageFrame(
- frame_settings, &pixel_format,
- (void*)(original_bytes.data() + oneframesize * i), oneframesize));
+ JxlEncoderAddImageFrame(frame_settings, &pixel_format,
+ static_cast<const void*>(
+ original_bytes.data() + oneframesize * i),
+ oneframesize));
}
JxlEncoderCloseInput(enc);
@@ -877,10 +880,11 @@ TEST(RoundtripTest, TestICCProfile) {
JxlEncoderSetICCProfile(enc, icc.data(), icc.size()));
JxlEncoderFrameSettings* frame_settings =
JxlEncoderFrameSettingsCreate(enc, nullptr);
- EXPECT_EQ(JXL_ENC_SUCCESS,
- JxlEncoderAddImageFrame(frame_settings, &format,
- (void*)original_bytes.data(),
- original_bytes.size()));
+ EXPECT_EQ(
+ JXL_ENC_SUCCESS,
+ JxlEncoderAddImageFrame(frame_settings, &format,
+ static_cast<const void*>(original_bytes.data()),
+ original_bytes.size()));
JxlEncoderCloseInput(enc);
std::vector<uint8_t> compressed;
@@ -946,7 +950,7 @@ TEST(RoundtripTest, JXL_TRANSCODE_JPEG_TEST(TestJPEGReconstruction)) {
JxlEncoderPtr enc = JxlEncoderMake(nullptr);
JxlEncoderFrameSettings* frame_settings =
- JxlEncoderFrameSettingsCreate(enc.get(), NULL);
+ JxlEncoderFrameSettingsCreate(enc.get(), nullptr);
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderUseContainer(enc.get(), JXL_TRUE));
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderStoreJPEGMetadata(enc.get(), JXL_TRUE));
diff --git a/third_party/jpeg-xl/lib/jxl/sanitizers.h b/third_party/jpeg-xl/lib/jxl/sanitizers.h
index adeaea67ed..bb133e9203 100644
--- a/third_party/jpeg-xl/lib/jxl/sanitizers.h
+++ b/third_party/jpeg-xl/lib/jxl/sanitizers.h
@@ -43,28 +43,18 @@ static JXL_INLINE JXL_MAYBE_UNUSED void UnpoisonMemory(const volatile void* m,
__msan_unpoison(m, size);
}
-static JXL_INLINE JXL_MAYBE_UNUSED void UnpoisonCStr(const char* c) {
- do {
- UnpoisonMemory(c, 1);
- } while (*c++);
-}
-
static JXL_INLINE JXL_MAYBE_UNUSED void MemoryIsInitialized(
const volatile void* m, size_t size) {
__msan_check_mem_is_initialized(m, size);
}
// Mark all the bytes of an image (including padding) as poisoned bytes.
-static JXL_INLINE JXL_MAYBE_UNUSED void PoisonImage(const PlaneBase& im) {
+template <typename T>
+static JXL_INLINE JXL_MAYBE_UNUSED void PoisonImage(const Plane<T>& im) {
PoisonMemory(im.bytes(), im.bytes_per_row() * im.ysize());
}
-template <typename T>
-static JXL_INLINE JXL_MAYBE_UNUSED void PoisonImage(const Image3<T>& im) {
- PoisonImage(im.Plane(0));
- PoisonImage(im.Plane(1));
- PoisonImage(im.Plane(2));
-}
+namespace {
// Print the uninitialized regions of an image.
template <typename T>
@@ -210,6 +200,8 @@ static JXL_INLINE JXL_MAYBE_UNUSED void CheckImageInitialized(
}
}
+} // namespace
+
#define JXL_CHECK_IMAGE_INITIALIZED(im, r) \
::jxl::msan::CheckImageInitialized(im, r, "im=" #im ", r=" #r);
@@ -221,13 +213,13 @@ static JXL_INLINE JXL_MAYBE_UNUSED void CheckImageInitialized(
// In non-msan mode these functions don't use volatile since it is not needed
// for the empty functions.
-static JXL_INLINE JXL_MAYBE_UNUSED void PoisonMemory(const void*, size_t) {}
-static JXL_INLINE JXL_MAYBE_UNUSED void UnpoisonMemory(const void*, size_t) {}
-static JXL_INLINE JXL_MAYBE_UNUSED void UnpoisonCStr(const char*) {}
-static JXL_INLINE JXL_MAYBE_UNUSED void MemoryIsInitialized(const void*,
- size_t) {}
+static JXL_INLINE JXL_MAYBE_UNUSED void PoisonMemory(const void* m,
+ size_t size) {}
+static JXL_INLINE JXL_MAYBE_UNUSED void UnpoisonMemory(const void* m,
+ size_t size) {}
+static JXL_INLINE JXL_MAYBE_UNUSED void MemoryIsInitialized(const void* m,
+ size_t size) {}
-static JXL_INLINE JXL_MAYBE_UNUSED void PoisonImage(const PlaneBase& im) {}
template <typename T>
static JXL_INLINE JXL_MAYBE_UNUSED void PoisonImage(const Plane<T>& im) {}
diff --git a/third_party/jpeg-xl/lib/jxl/simd_util.cc b/third_party/jpeg-xl/lib/jxl/simd_util.cc
index a3971ff900..6515daaa2b 100644
--- a/third_party/jpeg-xl/lib/jxl/simd_util.cc
+++ b/third_party/jpeg-xl/lib/jxl/simd_util.cc
@@ -5,11 +5,18 @@
#include "lib/jxl/simd_util.h"
+#include <algorithm>
+#include <cstddef>
+
#undef HWY_TARGET_INCLUDE
#define HWY_TARGET_INCLUDE "lib/jxl/simd_util.cc"
#include <hwy/foreach_target.h>
#include <hwy/highway.h>
+#include "lib/jxl/base/common.h"
+#include "lib/jxl/base/status.h"
+#include "lib/jxl/cache_aligned.h"
+
HWY_BEFORE_NAMESPACE();
namespace jxl {
namespace HWY_NAMESPACE {
@@ -36,5 +43,37 @@ size_t MaxVectorSize() {
return HWY_DYNAMIC_DISPATCH(MaxVectorSize)();
}
+size_t BytesPerRow(const size_t xsize, const size_t sizeof_t) {
+ // Special case: we don't allow any ops -> don't need extra padding/
+ if (xsize == 0) {
+ return 0;
+ }
+
+ const size_t vec_size = MaxVectorSize();
+ size_t valid_bytes = xsize * sizeof_t;
+
+ // Allow unaligned accesses starting at the last valid value.
+ // Skip for the scalar case because no extra lanes will be loaded.
+ if (vec_size != 0) {
+ valid_bytes += vec_size - sizeof_t;
+ }
+
+ // Round up to vector and cache line size.
+ const size_t align = std::max(vec_size, CacheAligned::kAlignment);
+ size_t bytes_per_row = RoundUpTo(valid_bytes, align);
+
+ // During the lengthy window before writes are committed to memory, CPUs
+ // guard against read after write hazards by checking the address, but
+ // only the lower 11 bits. We avoid a false dependency between writes to
+ // consecutive rows by ensuring their sizes are not multiples of 2 KiB.
+ // Avoid2K prevents the same problem for the planes of an Image3.
+ if (bytes_per_row % CacheAligned::kAlias == 0) {
+ bytes_per_row += align;
+ }
+
+ JXL_ASSERT(bytes_per_row % align == 0);
+ return bytes_per_row;
+}
+
} // namespace jxl
#endif
diff --git a/third_party/jpeg-xl/lib/jxl/simd_util.h b/third_party/jpeg-xl/lib/jxl/simd_util.h
index 84938a931a..62b68c989c 100644
--- a/third_party/jpeg-xl/lib/jxl/simd_util.h
+++ b/third_party/jpeg-xl/lib/jxl/simd_util.h
@@ -12,6 +12,10 @@ namespace jxl {
// Maximal vector size in bytes.
size_t MaxVectorSize();
+// Returns distance [bytes] between the start of two consecutive rows, a
+// multiple of vector/cache line size but NOT CacheAligned::kAlias - see below.
+size_t BytesPerRow(size_t xsize, size_t sizeof_t);
+
} // namespace jxl
#endif // LIB_JXL_SIMD_UTIL_H_
diff --git a/third_party/jpeg-xl/lib/jxl/speed_tier_test.cc b/third_party/jpeg-xl/lib/jxl/speed_tier_test.cc
index 7874bdc158..381367b54d 100644
--- a/third_party/jpeg-xl/lib/jxl/speed_tier_test.cc
+++ b/third_party/jpeg-xl/lib/jxl/speed_tier_test.cc
@@ -105,7 +105,7 @@ TEST_P(SpeedTierTest, Roundtrip) {
{
extras::PackedPixelFile ppf_out;
test::Roundtrip(t.ppf(), cparams, dparams, nullptr, &ppf_out);
- EXPECT_LE(test::ButteraugliDistance(t.ppf(), ppf_out), 1.6);
+ EXPECT_LE(test::ButteraugliDistance(t.ppf(), ppf_out), 2.0);
}
if (params.shrink8) {
cparams.distance = 0.0f;
diff --git a/third_party/jpeg-xl/lib/jxl/splines.cc b/third_party/jpeg-xl/lib/jxl/splines.cc
index acbaf38428..1d4fc69e3e 100644
--- a/third_party/jpeg-xl/lib/jxl/splines.cc
+++ b/third_party/jpeg-xl/lib/jxl/splines.cc
@@ -40,7 +40,7 @@ using hwy::HWY_NAMESPACE::Sub;
// Given a set of DCT coefficients, this returns the result of performing cosine
// interpolation on the original samples.
-float ContinuousIDCT(const float dct[32], const float t) {
+float ContinuousIDCT(const Dct32& dct, const float t) {
// We compute here the DCT-3 of the `dct` vector, rescaled by a factor of
// sqrt(32). This is such that an input vector vector {x, 0, ..., 0} produces
// a constant result of x. dct[0] was scaled in Dequantize() to allow uniform
@@ -60,7 +60,7 @@ float ContinuousIDCT(const float dct[32], const float t) {
for (int i = 0; i < 32; i += Lanes(df)) {
auto cos_arg = Mul(LoadU(df, kMultipliers + i), tandhalf);
auto cos = FastCosf(df, cos_arg);
- auto local_res = Mul(LoadU(df, dct + i), cos);
+ auto local_res = Mul(LoadU(df, dct.data() + i), cos);
result = MulAdd(Set(df, kSqrt2), local_res, result);
}
return GetLane(SumOfLanes(df, result));
@@ -140,8 +140,9 @@ void ComputeSegments(const Spline::Point& center, const float intensity,
segment.inv_sigma = 1.0f / sigma;
segment.sigma_over_4_times_intensity = .25f * sigma * intensity;
segment.maximum_distance = maximum_distance;
- ssize_t y0 = center.y - maximum_distance + .5f;
- ssize_t y1 = center.y + maximum_distance + 1.5f; // one-past-the-end
+ ssize_t y0 = std::llround(center.y - maximum_distance);
+ ssize_t y1 =
+ std::llround(center.y + maximum_distance) + 1; // one-past-the-end
for (ssize_t y = std::max<ssize_t>(y0, 0); y < y1; y++) {
segments_by_y.emplace_back(y, segments.size());
}
@@ -227,7 +228,7 @@ float InvAdjustedQuant(const int32_t adjustment) {
}
// X, Y, B, sigma.
-static constexpr float kChannelWeight[] = {0.0042f, 0.075f, 0.07f, .3333f};
+constexpr float kChannelWeight[] = {0.0042f, 0.075f, 0.07f, .3333f};
Status DecodeAllStartingPoints(std::vector<Spline::Point>* const points,
BitReader* const br, ANSSymbolReader* reader,
@@ -366,7 +367,8 @@ QuantizedSpline::QuantizedSpline(const Spline& original,
const Spline::Point& starting_point = original.control_points.front();
int previous_x = static_cast<int>(std::roundf(starting_point.x));
int previous_y = static_cast<int>(std::roundf(starting_point.y));
- int previous_delta_x = 0, previous_delta_y = 0;
+ int previous_delta_x = 0;
+ int previous_delta_y = 0;
for (auto it = original.control_points.begin() + 1;
it != original.control_points.end(); ++it) {
const int new_x = static_cast<int>(std::roundf(it->x));
@@ -426,9 +428,10 @@ Status QuantizedSpline::Dequantize(const Spline::Point& starting_point,
JXL_RETURN_IF_ERROR(ValidateSplinePointPos(px, py));
int current_x = static_cast<int>(px);
int current_y = static_cast<int>(py);
- result.control_points.push_back(Spline::Point{static_cast<float>(current_x),
- static_cast<float>(current_y)});
- int current_delta_x = 0, current_delta_y = 0;
+ result.control_points.emplace_back(static_cast<float>(current_x),
+ static_cast<float>(current_y));
+ int current_delta_x = 0;
+ int current_delta_y = 0;
uint64_t manhattan_distance = 0;
for (const auto& point : control_points_) {
current_delta_x += point.first;
@@ -443,8 +446,8 @@ Status QuantizedSpline::Dequantize(const Spline::Point& starting_point,
current_x += current_delta_x;
current_y += current_delta_y;
JXL_RETURN_IF_ERROR(ValidateSplinePointPos(current_x, current_y));
- result.control_points.push_back(Spline::Point{
- static_cast<float>(current_x), static_cast<float>(current_y)});
+ result.control_points.emplace_back(static_cast<float>(current_x),
+ static_cast<float>(current_y));
}
const auto inv_quant = InvAdjustedQuant(quantization_adjustment);
@@ -605,15 +608,15 @@ Status Splines::Decode(jxl::BitReader* br, const size_t num_pixels) {
void Splines::AddTo(Image3F* const opsin, const Rect& opsin_rect,
const Rect& image_rect) const {
- return Apply</*add=*/true>(opsin, opsin_rect, image_rect);
+ Apply</*add=*/true>(opsin, opsin_rect, image_rect);
}
void Splines::AddToRow(float* JXL_RESTRICT row_x, float* JXL_RESTRICT row_y,
float* JXL_RESTRICT row_b, const Rect& image_row) const {
- return ApplyToRow</*add=*/true>(row_x, row_y, row_b, image_row);
+ ApplyToRow</*add=*/true>(row_x, row_y, row_b, image_row);
}
void Splines::SubtractFrom(Image3F* const opsin) const {
- return Apply</*add=*/false>(opsin, Rect(*opsin), Rect(*opsin));
+ Apply</*add=*/false>(opsin, Rect(*opsin), Rect(*opsin));
}
Status Splines::InitializeDrawCache(const size_t image_xsize,
@@ -646,8 +649,9 @@ Status Splines::InitializeDrawCache(const size_t image_xsize,
}
// TODO(firsching) Change this into a JXL_FAILURE for level 5 codestreams.
if (total_estimated_area_reached >
- std::min((8 * image_xsize * image_ysize + (uint64_t(1) << 25)),
- (uint64_t(1) << 30))) {
+ std::min(
+ (8 * image_xsize * image_ysize + (static_cast<uint64_t>(1) << 25)),
+ (static_cast<uint64_t>(1) << 30))) {
JXL_WARNING(
"Large total_estimated_area_reached, expect slower decoding: %" PRIu64,
total_estimated_area_reached);
diff --git a/third_party/jpeg-xl/lib/jxl/splines.h b/third_party/jpeg-xl/lib/jxl/splines.h
index acdd0857d0..af51ec937e 100644
--- a/third_party/jpeg-xl/lib/jxl/splines.h
+++ b/third_party/jpeg-xl/lib/jxl/splines.h
@@ -6,6 +6,7 @@
#ifndef LIB_JXL_SPLINES_H_
#define LIB_JXL_SPLINES_H_
+#include <array>
#include <cmath>
#include <cstddef>
#include <cstdint>
@@ -24,6 +25,8 @@ class BitReader;
static constexpr float kDesiredRenderingDistance = 1.f;
+typedef std::array<float, 32> Dct32;
+
enum SplineEntropyContexts : size_t {
kQuantizationAdjustmentContext = 0,
kStartingPositionContext,
@@ -45,10 +48,10 @@ struct Spline {
};
std::vector<Point> control_points;
// X, Y, B.
- float color_dct[3][32];
+ std::array<Dct32, 3> color_dct;
// Splines are draws by normalized Gaussian splatting. This controls the
// Gaussian's parameter along the spline.
- float sigma_dct[32];
+ Dct32 sigma_dct;
};
class QuantizedSplineEncoder;
diff --git a/third_party/jpeg-xl/lib/jxl/splines_gbench.cc b/third_party/jpeg-xl/lib/jxl/splines_gbench.cc
index 78ff6d41c0..903c5b6328 100644
--- a/third_party/jpeg-xl/lib/jxl/splines_gbench.cc
+++ b/third_party/jpeg-xl/lib/jxl/splines_gbench.cc
@@ -3,7 +3,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include <array>
+
#include "benchmark/benchmark.h"
+#include "lib/jxl/image_ops.h"
#include "lib/jxl/splines.h"
namespace jxl {
@@ -17,12 +20,14 @@ const float kYToB = cmap->YtoBRatio(0);
void BM_Splines(benchmark::State& state) {
const size_t n = state.range();
- std::vector<Spline> spline_data = {
- {/*control_points=*/{
- {9, 54}, {118, 159}, {97, 3}, {10, 40}, {150, 25}, {120, 300}},
- /*color_dct=*/
- {{0.03125f, 0.00625f, 0.003125f}, {1.f, 0.321875f}, {1.f, 0.24375f}},
- /*sigma_dct=*/{0.3125f, 0.f, 0.f, 0.0625f}}};
+ Spline spline1{
+ /*control_points=*/{
+ {9, 54}, {118, 159}, {97, 3}, {10, 40}, {150, 25}, {120, 300}},
+ /*color_dct=*/
+ {Dct32{0.03125f, 0.00625f, 0.003125f}, Dct32{1.f, 0.321875f},
+ Dct32{1.f, 0.24375f}},
+ /*sigma_dct=*/{0.3125f, 0.f, 0.f, 0.0625f}};
+ std::vector<Spline> spline_data = {spline1};
std::vector<QuantizedSpline> quantized_splines;
std::vector<Spline::Point> starting_points;
for (const Spline& spline : spline_data) {
@@ -33,7 +38,7 @@ void BM_Splines(benchmark::State& state) {
Splines splines(kQuantizationAdjustment, std::move(quantized_splines),
std::move(starting_points));
- Image3F drawing_area(320, 320);
+ JXL_ASSIGN_OR_DIE(Image3F drawing_area, Image3F::Create(320, 320));
ZeroFillImage(&drawing_area);
for (auto _ : state) {
for (size_t i = 0; i < n; ++i) {
diff --git a/third_party/jpeg-xl/lib/jxl/splines_test.cc b/third_party/jpeg-xl/lib/jxl/splines_test.cc
index d812545a37..83cc524234 100644
--- a/third_party/jpeg-xl/lib/jxl/splines_test.cc
+++ b/third_party/jpeg-xl/lib/jxl/splines_test.cc
@@ -23,7 +23,6 @@
#include "lib/jxl/chroma_from_luma.h"
#include "lib/jxl/enc_aux_out.h"
#include "lib/jxl/enc_bit_writer.h"
-#include "lib/jxl/enc_params.h"
#include "lib/jxl/enc_splines.h"
#include "lib/jxl/image.h"
#include "lib/jxl/image_ops.h"
@@ -45,10 +44,6 @@ std::ostream& operator<<(std::ostream& os, const Spline& spline) {
namespace {
using test::ReadTestData;
-using ::testing::AllOf;
-using ::testing::Field;
-using ::testing::FloatNear;
-using ::testing::Pointwise;
constexpr int kQuantizationAdjustment = 0;
const ColorCorrelationMap* const cmap = new ColorCorrelationMap;
@@ -73,127 +68,75 @@ std::vector<Spline> DequantizeSplines(const Splines& splines) {
return dequantized;
}
-MATCHER(ControlPointIs, "") {
- const Spline::Point& actual = std::get<0>(arg);
- const Spline::Point& expected = std::get<1>(arg);
- return testing::ExplainMatchResult(
- AllOf(Field(&Spline::Point::x, FloatNear(expected.x, kTolerance)),
- Field(&Spline::Point::y, FloatNear(expected.y, kTolerance))),
- actual, result_listener);
-}
-
-MATCHER(ControlPointsMatch, "") {
- const Spline& actual = std::get<0>(arg);
- const Spline& expected = std::get<1>(arg);
- return testing::ExplainMatchResult(
- Field(&Spline::control_points,
- Pointwise(ControlPointIs(), expected.control_points)),
- actual, result_listener);
-}
-
-MATCHER(SplinesMatch, "") {
- const Spline& actual = std::get<0>(arg);
- const Spline& expected = std::get<1>(arg);
- if (!testing::ExplainMatchResult(ControlPointsMatch(), arg,
- result_listener)) {
- return false;
- }
- for (int i = 0; i < 3; ++i) {
- size_t color_dct_size =
- sizeof(expected.color_dct[i]) / sizeof(expected.color_dct[i][0]);
- for (size_t j = 0; j < color_dct_size; j++) {
- testing::StringMatchResultListener color_dct_listener;
- if (!testing::ExplainMatchResult(
- FloatNear(expected.color_dct[i][j], kTolerance),
- actual.color_dct[i][j], &color_dct_listener)) {
- *result_listener << ", where color_dct[" << i << "][" << j
- << "] don't match, " << color_dct_listener.str();
- return false;
- }
- }
- }
- size_t sigma_dct_size =
- sizeof(expected.sigma_dct) / sizeof(expected.sigma_dct[0]);
- for (size_t i = 0; i < sigma_dct_size; i++) {
- testing::StringMatchResultListener sigma_listener;
- if (!testing::ExplainMatchResult(
- FloatNear(expected.sigma_dct[i], kTolerance), actual.sigma_dct[i],
- &sigma_listener)) {
- *result_listener << ", where sigma_dct[" << i << "] don't match, "
- << sigma_listener.str();
- return false;
- }
- }
- return true;
-}
-
} // namespace
TEST(SplinesTest, Serialization) {
- std::vector<Spline> spline_data = {
- {/*control_points=*/{
- {109, 54}, {218, 159}, {80, 3}, {110, 274}, {94, 185}, {17, 277}},
- /*color_dct=*/
- {{36.3, 39.7, 23.2, 67.5, 4.4, 71.5, 62.3, 32.3, 92.2, 10.1, 10.8,
- 9.2, 6.1, 10.5, 79.1, 7, 24.6, 90.8, 5.5, 84, 43.8, 49,
- 33.5, 78.9, 54.5, 77.9, 62.1, 51.4, 36.4, 14.3, 83.7, 35.4},
- {9.4, 53.4, 9.5, 74.9, 72.7, 26.7, 7.9, 0.9, 84.9, 23.2, 26.5,
- 31.1, 91, 11.7, 74.1, 39.3, 23.7, 82.5, 4.8, 2.7, 61.2, 96.4,
- 13.7, 66.7, 62.9, 82.4, 5.9, 98.7, 21.5, 7.9, 51.7, 63.1},
- {48, 39.3, 6.9, 26.3, 33.3, 6.2, 1.7, 98.9, 59.9, 59.6, 95,
- 61.3, 82.7, 53, 6.1, 30.4, 34.7, 96.9, 93.4, 17, 38.8, 80.8,
- 63, 18.6, 43.6, 32.3, 61, 20.2, 24.3, 28.3, 69.1, 62.4}},
- /*sigma_dct=*/{32.7, 21.5, 44.4, 1.8, 45.8, 90.6, 29.3, 59.2,
- 23.7, 85.2, 84.8, 27.2, 42.1, 84.1, 50.6, 17.6,
- 93.7, 4.9, 2.6, 69.8, 94.9, 52, 24.3, 18.8,
- 12.1, 95.7, 28.5, 81.4, 89.9, 31.4, 74.8, 52}},
- {/*control_points=*/{{172, 309},
- {196, 277},
- {42, 238},
- {114, 350},
- {307, 290},
- {316, 269},
- {124, 66},
- {233, 267}},
- /*color_dct=*/
- {{15, 28.9, 22, 6.6, 41.8, 83, 8.6, 56.8, 68.9, 9.7, 5.4,
- 19.8, 70.8, 90, 52.5, 65.2, 7.8, 23.5, 26.4, 72.2, 64.7, 87.1,
- 1.3, 67.5, 46, 68.4, 65.4, 35.5, 29.1, 13, 41.6, 23.9},
- {47.7, 79.4, 62.7, 29.1, 96.8, 18.5, 17.6, 15.2, 80.5, 56, 96.2,
- 59.9, 26.7, 96.1, 92.3, 42.1, 35.8, 54, 23.2, 55, 76, 35.8,
- 58.4, 88.7, 2.4, 78.1, 95.6, 27.5, 6.6, 78.5, 24.1, 69.8},
- {43.8, 96.5, 0.9, 95.1, 49.1, 71.2, 25.1, 33.6, 75.2, 95, 82.1,
- 19.7, 10.5, 44.9, 50, 93.3, 83.5, 99.5, 64.6, 54, 3.5, 99.7,
- 45.3, 82.1, 22.4, 37.9, 60, 32.2, 12.6, 4.6, 65.5, 96.4}},
- /*sigma_dct=*/{72.5, 2.6, 41.7, 2.2, 39.7, 79.1, 69.6, 19.9,
- 92.3, 71.5, 41.9, 62.1, 30, 49.4, 70.3, 45.3,
- 62.5, 47.2, 46.7, 41.2, 90.8, 46.8, 91.2, 55,
- 8.1, 69.6, 25.4, 84.7, 61.7, 27.6, 3.7, 46.9}},
- {/*control_points=*/{{100, 186},
- {257, 97},
- {170, 49},
- {25, 169},
- {309, 104},
- {232, 237},
- {385, 101},
- {122, 168},
- {26, 300},
- {390, 88}},
- /*color_dct=*/
- {{16.9, 64.8, 4.2, 10.6, 23.5, 17, 79.3, 5.7, 60.4, 16.6, 94.9,
- 63.7, 87.6, 10.5, 3.8, 61.1, 22.9, 81.9, 80.4, 40.5, 45.9, 25.4,
- 39.8, 30, 50.2, 90.4, 27.9, 93.7, 65.1, 48.2, 22.3, 43.9},
- {24.9, 66, 3.5, 90.2, 97.1, 15.8, 35.6, 0.6, 68, 39.6, 24.4,
- 85.9, 57.7, 77.6, 47.5, 67.9, 4.3, 5.4, 91.2, 58.5, 0.1, 52.2,
- 3.5, 47.8, 63.2, 43.5, 85.8, 35.8, 50.2, 35.9, 19.2, 48.2},
- {82.8, 44.9, 76.4, 39.5, 94.1, 14.3, 89.8, 10, 10.5, 74.5, 56.3,
- 65.8, 7.8, 23.3, 52.8, 99.3, 56.8, 46, 76.7, 13.5, 67, 22.4,
- 29.9, 43.3, 70.3, 26, 74.3, 53.9, 62, 19.1, 49.3, 46.7}},
- /*sigma_dct=*/{83.5, 1.7, 25.1, 18.7, 46.5, 75.3, 28, 62.3,
- 50.3, 23.3, 85.6, 96, 45.8, 33.1, 33.4, 52.9,
- 26.3, 58.5, 19.6, 70, 92.6, 22.5, 57, 21.6,
- 76.8, 87.5, 22.9, 66.3, 35.7, 35.6, 56.8, 67.2}},
- };
+ Spline spline1{
+ /*control_points=*/{
+ {109, 54}, {218, 159}, {80, 3}, {110, 274}, {94, 185}, {17, 277}},
+ /*color_dct=*/
+ {Dct32{36.3, 39.7, 23.2, 67.5, 4.4, 71.5, 62.3, 32.3, 92.2, 10.1, 10.8,
+ 9.2, 6.1, 10.5, 79.1, 7, 24.6, 90.8, 5.5, 84, 43.8, 49,
+ 33.5, 78.9, 54.5, 77.9, 62.1, 51.4, 36.4, 14.3, 83.7, 35.4},
+ Dct32{9.4, 53.4, 9.5, 74.9, 72.7, 26.7, 7.9, 0.9, 84.9, 23.2, 26.5,
+ 31.1, 91, 11.7, 74.1, 39.3, 23.7, 82.5, 4.8, 2.7, 61.2, 96.4,
+ 13.7, 66.7, 62.9, 82.4, 5.9, 98.7, 21.5, 7.9, 51.7, 63.1},
+ Dct32{48, 39.3, 6.9, 26.3, 33.3, 6.2, 1.7, 98.9, 59.9, 59.6, 95,
+ 61.3, 82.7, 53, 6.1, 30.4, 34.7, 96.9, 93.4, 17, 38.8, 80.8,
+ 63, 18.6, 43.6, 32.3, 61, 20.2, 24.3, 28.3, 69.1, 62.4}},
+ /*sigma_dct=*/{32.7, 21.5, 44.4, 1.8, 45.8, 90.6, 29.3, 59.2,
+ 23.7, 85.2, 84.8, 27.2, 42.1, 84.1, 50.6, 17.6,
+ 93.7, 4.9, 2.6, 69.8, 94.9, 52, 24.3, 18.8,
+ 12.1, 95.7, 28.5, 81.4, 89.9, 31.4, 74.8, 52}};
+ Spline spline2{
+ /*control_points=*/{{172, 309},
+ {196, 277},
+ {42, 238},
+ {114, 350},
+ {307, 290},
+ {316, 269},
+ {124, 66},
+ {233, 267}},
+ /*color_dct=*/
+ {Dct32{15, 28.9, 22, 6.6, 41.8, 83, 8.6, 56.8, 68.9, 9.7, 5.4,
+ 19.8, 70.8, 90, 52.5, 65.2, 7.8, 23.5, 26.4, 72.2, 64.7, 87.1,
+ 1.3, 67.5, 46, 68.4, 65.4, 35.5, 29.1, 13, 41.6, 23.9},
+ Dct32{47.7, 79.4, 62.7, 29.1, 96.8, 18.5, 17.6, 15.2, 80.5, 56, 96.2,
+ 59.9, 26.7, 96.1, 92.3, 42.1, 35.8, 54, 23.2, 55, 76, 35.8,
+ 58.4, 88.7, 2.4, 78.1, 95.6, 27.5, 6.6, 78.5, 24.1, 69.8},
+ Dct32{43.8, 96.5, 0.9, 95.1, 49.1, 71.2, 25.1, 33.6, 75.2, 95, 82.1,
+ 19.7, 10.5, 44.9, 50, 93.3, 83.5, 99.5, 64.6, 54, 3.5, 99.7,
+ 45.3, 82.1, 22.4, 37.9, 60, 32.2, 12.6, 4.6, 65.5, 96.4}},
+ /*sigma_dct=*/{72.5, 2.6, 41.7, 2.2, 39.7, 79.1, 69.6, 19.9,
+ 92.3, 71.5, 41.9, 62.1, 30, 49.4, 70.3, 45.3,
+ 62.5, 47.2, 46.7, 41.2, 90.8, 46.8, 91.2, 55,
+ 8.1, 69.6, 25.4, 84.7, 61.7, 27.6, 3.7, 46.9}};
+ Spline spline3{
+ /*control_points=*/{{100, 186},
+ {257, 97},
+ {170, 49},
+ {25, 169},
+ {309, 104},
+ {232, 237},
+ {385, 101},
+ {122, 168},
+ {26, 300},
+ {390, 88}},
+ /*color_dct=*/
+ {Dct32{16.9, 64.8, 4.2, 10.6, 23.5, 17, 79.3, 5.7, 60.4, 16.6, 94.9,
+ 63.7, 87.6, 10.5, 3.8, 61.1, 22.9, 81.9, 80.4, 40.5, 45.9, 25.4,
+ 39.8, 30, 50.2, 90.4, 27.9, 93.7, 65.1, 48.2, 22.3, 43.9},
+ Dct32{24.9, 66, 3.5, 90.2, 97.1, 15.8, 35.6, 0.6, 68, 39.6, 24.4,
+ 85.9, 57.7, 77.6, 47.5, 67.9, 4.3, 5.4, 91.2, 58.5, 0.1, 52.2,
+ 3.5, 47.8, 63.2, 43.5, 85.8, 35.8, 50.2, 35.9, 19.2, 48.2},
+ Dct32{82.8, 44.9, 76.4, 39.5, 94.1, 14.3, 89.8, 10, 10.5, 74.5, 56.3,
+ 65.8, 7.8, 23.3, 52.8, 99.3, 56.8, 46, 76.7, 13.5, 67, 22.4,
+ 29.9, 43.3, 70.3, 26, 74.3, 53.9, 62, 19.1, 49.3, 46.7}},
+ /*sigma_dct=*/{83.5, 1.7, 25.1, 18.7, 46.5, 75.3, 28, 62.3,
+ 50.3, 23.3, 85.6, 96, 45.8, 33.1, 33.4, 52.9,
+ 26.3, 58.5, 19.6, 70, 92.6, 22.5, 57, 21.6,
+ 76.8, 87.5, 22.9, 66.3, 35.7, 35.6, 56.8, 67.2}};
+ std::vector<Spline> spline_data{spline1, spline2, spline3};
std::vector<QuantizedSpline> quantized_splines;
std::vector<Spline::Point> starting_points;
@@ -206,8 +149,20 @@ TEST(SplinesTest, Serialization) {
Splines splines(kQuantizationAdjustment, std::move(quantized_splines),
std::move(starting_points));
const std::vector<Spline> quantized_spline_data = DequantizeSplines(splines);
- EXPECT_THAT(quantized_spline_data,
- Pointwise(ControlPointsMatch(), spline_data));
+ EXPECT_EQ(quantized_spline_data.size(), spline_data.size());
+ for (size_t i = 0; i < quantized_spline_data.size(); ++i) {
+ const Spline& actual = quantized_spline_data[i];
+ const Spline& expected = spline_data[i];
+ const auto& actual_points = actual.control_points;
+ const auto& expected_points = expected.control_points;
+ EXPECT_EQ(actual_points.size(), expected_points.size());
+ for (size_t j = 0; j < actual_points.size(); ++j) {
+ EXPECT_NEAR(actual_points[j].x, expected_points[j].x, kTolerance)
+ << "spline " << i << " point " << j;
+ EXPECT_NEAR(actual_points[j].y, expected_points[j].y, kTolerance)
+ << "spline " << i << " point " << j;
+ }
+ }
BitWriter writer;
EncodeSplines(splines, &writer, kLayerSplines, HistogramParams(), nullptr);
@@ -225,8 +180,28 @@ TEST(SplinesTest, Serialization) {
const std::vector<Spline> decoded_spline_data =
DequantizeSplines(decoded_splines);
- EXPECT_THAT(decoded_spline_data,
- Pointwise(SplinesMatch(), quantized_spline_data));
+
+ EXPECT_EQ(decoded_spline_data.size(), quantized_spline_data.size());
+ for (size_t i = 0; i < decoded_spline_data.size(); ++i) {
+ const Spline& actual = decoded_spline_data[i];
+ const Spline& expected = quantized_spline_data[i];
+ const auto& actual_points = actual.control_points;
+ const auto& expected_points = expected.control_points;
+ EXPECT_EQ(actual_points.size(), expected_points.size());
+ for (size_t j = 0; j < actual_points.size(); ++j) {
+ EXPECT_NEAR(actual_points[j].x, expected_points[j].x, kTolerance)
+ << "spline " << i << " point " << j;
+ EXPECT_NEAR(actual_points[j].y, expected_points[j].y, kTolerance)
+ << "spline " << i << " point " << j;
+ }
+
+ const auto& actual_color_dct = actual.color_dct;
+ const auto& expected_color_dct = expected.color_dct;
+ for (size_t j = 0; j < actual_color_dct.size(); ++j) {
+ EXPECT_ARRAY_NEAR(actual_color_dct[j], expected_color_dct[j], kTolerance);
+ }
+ EXPECT_ARRAY_NEAR(actual.sigma_dct, expected.sigma_dct, kTolerance);
+ }
}
#ifdef JXL_CRASH_ON_ERROR
@@ -240,10 +215,10 @@ TEST(SplinesTest, TooManySplinesTest) {
std::vector<QuantizedSpline> quantized_splines;
std::vector<Spline::Point> starting_points;
for (size_t i = 0; i < kNumSplines; i++) {
- Spline spline = {
+ Spline spline{
/*control_points=*/{{1.f + i, 2}, {10.f + i, 25}, {30.f + i, 300}},
/*color_dct=*/
- {{1.f, 0.2f, 0.1f}, {35.7f, 10.3f}, {35.7f, 7.8f}},
+ {Dct32{1.f, 0.2f, 0.1f}, Dct32{35.7f, 10.3f}, Dct32{35.7f, 7.8f}},
/*sigma_dct=*/{10.f, 0.f, 0.f, 2.f}};
quantized_splines.emplace_back(spline, kQuantizationAdjustment, kYToX,
kYToB);
@@ -271,10 +246,11 @@ TEST(SplinesTest, DuplicatePoints) {
std::vector<Spline::Point> control_points{
{9, 54}, {118, 159}, {97, 3}, // Repeated.
{97, 3}, {10, 40}, {150, 25}, {120, 300}};
- Spline spline{control_points,
- /*color_dct=*/
- {{1.f, 0.2f, 0.1f}, {35.7f, 10.3f}, {35.7f, 7.8f}},
- /*sigma_dct=*/{10.f, 0.f, 0.f, 2.f}};
+ Spline spline{
+ control_points,
+ /*color_dct=*/
+ {Dct32{1.f, 0.2f, 0.1f}, Dct32{35.7f, 10.3f}, Dct32{35.7f, 7.8f}},
+ /*sigma_dct=*/{10.f, 0.f, 0.f, 2.f}};
std::vector<Spline> spline_data{spline};
std::vector<QuantizedSpline> quantized_splines;
std::vector<Spline::Point> starting_points;
@@ -286,7 +262,7 @@ TEST(SplinesTest, DuplicatePoints) {
Splines splines(kQuantizationAdjustment, std::move(quantized_splines),
std::move(starting_points));
- Image3F image(320, 320);
+ JXL_ASSIGN_OR_DIE(Image3F image, Image3F::Create(320, 320));
ZeroFillImage(&image);
EXPECT_FALSE(
splines.InitializeDrawCache(image.xsize(), image.ysize(), *cmap));
@@ -304,10 +280,10 @@ TEST(SplinesTest, Drawing) {
const Spline spline{
control_points,
/*color_dct=*/
- {{0.4989345073699951171875000f, 0.4997999966144561767578125f},
- {0.4772970676422119140625000f, 0.f, 0.5250000357627868652343750f},
- {-0.0176776945590972900390625f, 0.4900000095367431640625000f,
- 0.5250000357627868652343750f}},
+ {Dct32{0.4989345073699951171875000f, 0.4997999966144561767578125f},
+ Dct32{0.4772970676422119140625000f, 0.f, 0.5250000357627868652343750f},
+ Dct32{-0.0176776945590972900390625f, 0.4900000095367431640625000f,
+ 0.5250000357627868652343750f}},
/*sigma_dct=*/
{0.9427147507667541503906250f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f,
0.6665999889373779296875000f}};
@@ -322,13 +298,13 @@ TEST(SplinesTest, Drawing) {
Splines splines(kQuantizationAdjustment, std::move(quantized_splines),
std::move(starting_points));
- Image3F image(320, 320);
+ JXL_ASSIGN_OR_DIE(Image3F image, Image3F::Create(320, 320));
ZeroFillImage(&image);
ASSERT_TRUE(splines.InitializeDrawCache(image.xsize(), image.ysize(), *cmap));
splines.AddTo(&image, Rect(image), Rect(image));
CodecInOut io_actual;
- Image3F image2(320, 320);
+ JXL_ASSIGN_OR_DIE(Image3F image2, Image3F::Create(320, 320));
CopyImageTo(image, &image2);
io_actual.SetFromImage(std::move(image2), ColorEncoding::SRGB());
ASSERT_TRUE(io_actual.frames[0].TransformTo(io_expected.Main().c_current(),
diff --git a/third_party/jpeg-xl/lib/jxl/test_image.cc b/third_party/jpeg-xl/lib/jxl/test_image.cc
index 098e9c25a1..42f028d53a 100644
--- a/third_party/jpeg-xl/lib/jxl/test_image.cc
+++ b/third_party/jpeg-xl/lib/jxl/test_image.cc
@@ -323,7 +323,7 @@ TestImage& TestImage::SetColorEncoding(const std::string& description) {
}
TestImage& TestImage::CoalesceGIFAnimationWithAlpha() {
- extras::PackedFrame canvas = ppf_.frames[0].Copy();
+ JXL_ASSIGN_OR_DIE(extras::PackedFrame canvas, ppf_.frames[0].Copy());
JXL_CHECK(canvas.color.format.num_channels == 3);
JXL_CHECK(canvas.color.format.data_type == JXL_TYPE_UINT8);
JXL_CHECK(canvas.extra_channels.size() == 1);
@@ -331,7 +331,7 @@ TestImage& TestImage::CoalesceGIFAnimationWithAlpha() {
const extras::PackedFrame& frame = ppf_.frames[i];
JXL_CHECK(frame.extra_channels.size() == 1);
const JxlLayerInfo& layer_info = frame.frame_info.layer_info;
- extras::PackedFrame rendered = canvas.Copy();
+ JXL_ASSIGN_OR_DIE(extras::PackedFrame rendered, canvas.Copy());
uint8_t* pixels_rendered =
reinterpret_cast<uint8_t*>(rendered.color.pixels());
const uint8_t* pixels_frame =
@@ -353,7 +353,7 @@ TestImage& TestImage::CoalesceGIFAnimationWithAlpha() {
}
}
if (layer_info.save_as_reference != 0) {
- canvas = rendered.Copy();
+ JXL_ASSIGN_OR_DIE(canvas, rendered.Copy());
}
ppf_.frames[i] = std::move(rendered);
}
@@ -393,10 +393,14 @@ void TestImage::Frame::SetValue(size_t y, size_t x, size_t c, float val) {
TestImage::Frame TestImage::AddFrame() {
size_t index = ppf_.frames.size();
- extras::PackedFrame frame(ppf_.info.xsize, ppf_.info.ysize, format_);
+ JXL_ASSIGN_OR_DIE(
+ extras::PackedFrame frame,
+ extras::PackedFrame::Create(ppf_.info.xsize, ppf_.info.ysize, format_));
for (size_t i = 0; i < ppf_.extra_channels_info.size(); ++i) {
JxlPixelFormat ec_format = {1, format_.data_type, format_.endianness, 0};
- extras::PackedImage image(ppf_.info.xsize, ppf_.info.ysize, ec_format);
+ JXL_ASSIGN_OR_DIE(extras::PackedImage image,
+ extras::PackedImage::Create(ppf_.info.xsize,
+ ppf_.info.ysize, ec_format));
frame.extra_channels.emplace_back(std::move(image));
}
ppf_.frames.emplace_back(std::move(frame));
@@ -404,10 +408,12 @@ TestImage::Frame TestImage::AddFrame() {
}
TestImage::Frame TestImage::AddPreview(size_t xsize, size_t ysize) {
- extras::PackedFrame frame(xsize, ysize, format_);
+ JXL_ASSIGN_OR_DIE(extras::PackedFrame frame,
+ extras::PackedFrame::Create(xsize, ysize, format_));
for (size_t i = 0; i < ppf_.extra_channels_info.size(); ++i) {
JxlPixelFormat ec_format = {1, format_.data_type, format_.endianness, 0};
- extras::PackedImage image(xsize, ysize, ec_format);
+ JXL_ASSIGN_OR_DIE(extras::PackedImage image,
+ extras::PackedImage::Create(xsize, ysize, ec_format));
frame.extra_channels.emplace_back(std::move(image));
}
ppf_.preview_frame = make_unique<extras::PackedFrame>(std::move(frame));
diff --git a/third_party/jpeg-xl/lib/jxl/test_utils.cc b/third_party/jpeg-xl/lib/jxl/test_utils.cc
index 451f2a0a03..308efb9a13 100644
--- a/third_party/jpeg-xl/lib/jxl/test_utils.cc
+++ b/third_party/jpeg-xl/lib/jxl/test_utils.cc
@@ -228,7 +228,7 @@ bool Roundtrip(const CodecInOut* io, const CompressParams& cparams,
}
size_t Roundtrip(const extras::PackedPixelFile& ppf_in,
- extras::JXLCompressParams cparams,
+ const extras::JXLCompressParams& cparams,
extras::JXLDecompressParams dparams, ThreadPool* pool,
extras::PackedPixelFile* ppf_out) {
DefaultAcceptedFormats(dparams);
@@ -303,12 +303,12 @@ bool Near(double expected, double value, double max_dist) {
float LoadLEFloat16(const uint8_t* p) {
uint16_t bits16 = LoadLE16(p);
- return LoadFloat16(bits16);
+ return detail::LoadFloat16(bits16);
}
float LoadBEFloat16(const uint8_t* p) {
uint16_t bits16 = LoadBE16(p);
- return LoadFloat16(bits16);
+ return detail::LoadFloat16(bits16);
}
size_t GetPrecision(JxlDataType data_type) {
@@ -385,7 +385,10 @@ std::vector<double> ConvertToRGBA32(const uint8_t* pixels, size_t xsize,
for (size_t x = 0; x < xsize; ++x) {
size_t j = (y * xsize + x) * 4;
size_t i = y * stride + x * num_channels * 2;
- double r, g, b, a;
+ double r;
+ double g;
+ double b;
+ double a;
if (endianness == JXL_BIG_ENDIAN) {
r = (pixels[i + 0] << 8) + pixels[i + 1];
g = gray ? r : (pixels[i + 2] << 8) + pixels[i + 3];
@@ -413,7 +416,10 @@ std::vector<double> ConvertToRGBA32(const uint8_t* pixels, size_t xsize,
for (size_t x = 0; x < xsize; ++x) {
size_t j = (y * xsize + x) * 4;
size_t i = y * stride + x * num_channels * 4;
- double r, g, b, a;
+ double r;
+ double g;
+ double b;
+ double a;
if (endianness == JXL_BIG_ENDIAN) {
r = LoadBEFloat(pixels + i);
g = gray ? r : LoadBEFloat(pixels + i + 4);
@@ -437,7 +443,10 @@ std::vector<double> ConvertToRGBA32(const uint8_t* pixels, size_t xsize,
for (size_t x = 0; x < xsize; ++x) {
size_t j = (y * xsize + x) * 4;
size_t i = y * stride + x * num_channels * 2;
- double r, g, b, a;
+ double r;
+ double g;
+ double b;
+ double a;
if (endianness == JXL_BIG_ENDIAN) {
r = LoadBEFloat16(pixels + i);
g = gray ? r : LoadBEFloat16(pixels + i + 2);
@@ -470,8 +479,8 @@ size_t ComparePixels(const uint8_t* a, const uint8_t* b, size_t xsize,
std::vector<double> b_full = ConvertToRGBA32(b, xsize, ysize, format_b);
bool gray_a = format_a.num_channels < 3;
bool gray_b = format_b.num_channels < 3;
- bool alpha_a = !(format_a.num_channels & 1);
- bool alpha_b = !(format_b.num_channels & 1);
+ bool alpha_a = ((format_a.num_channels & 1) == 0);
+ bool alpha_b = ((format_b.num_channels & 1) == 0);
size_t bits_a = GetPrecision(format_a.data_type);
size_t bits_b = GetPrecision(format_b.data_type);
size_t bits = std::min(bits_a, bits_b);
@@ -558,6 +567,34 @@ float ButteraugliDistance(const extras::PackedPixelFile& a,
/*distmap=*/nullptr, pool);
}
+float ButteraugliDistance(const ImageBundle& rgb0, const ImageBundle& rgb1,
+ const ButteraugliParams& params,
+ const JxlCmsInterface& cms, ImageF* distmap,
+ ThreadPool* pool, bool ignore_alpha) {
+ JxlButteraugliComparator comparator(params, cms);
+ float distance;
+ JXL_CHECK(ComputeScore(rgb0, rgb1, &comparator, cms, &distance, distmap, pool,
+ ignore_alpha));
+ return distance;
+}
+
+float ButteraugliDistance(const std::vector<ImageBundle>& frames0,
+ const std::vector<ImageBundle>& frames1,
+ const ButteraugliParams& params,
+ const JxlCmsInterface& cms, ImageF* distmap,
+ ThreadPool* pool) {
+ JxlButteraugliComparator comparator(params, cms);
+ JXL_ASSERT(frames0.size() == frames1.size());
+ float max_dist = 0.0f;
+ for (size_t i = 0; i < frames0.size(); ++i) {
+ float frame_score;
+ JXL_CHECK(ComputeScore(frames0[i], frames1[i], &comparator, cms,
+ &frame_score, distmap, pool));
+ max_dist = std::max(max_dist, frame_score);
+ }
+ return max_dist;
+}
+
float Butteraugli3Norm(const extras::PackedPixelFile& a,
const extras::PackedPixelFile& b, ThreadPool* pool) {
CodecInOut io0;
@@ -683,7 +720,7 @@ Status ReadICC(BitReader* JXL_RESTRICT reader,
PaddedBytes icc_buffer;
JXL_RETURN_IF_ERROR(icc_reader.Init(reader, output_limit));
JXL_RETURN_IF_ERROR(icc_reader.Process(reader, &icc_buffer));
- Bytes(icc_buffer).AppendTo(icc);
+ Bytes(icc_buffer).AppendTo(*icc);
return true;
}
@@ -787,7 +824,7 @@ Status EncodeFile(const CompressParams& params, const CodecInOut* io,
}
PaddedBytes output = std::move(writer).TakeBytes();
- Bytes(output).AppendTo(compressed);
+ Bytes(output).AppendTo(*compressed);
return true;
}
diff --git a/third_party/jpeg-xl/lib/jxl/test_utils.h b/third_party/jpeg-xl/lib/jxl/test_utils.h
index 6734380bf5..15057cc92d 100644
--- a/third_party/jpeg-xl/lib/jxl/test_utils.h
+++ b/third_party/jpeg-xl/lib/jxl/test_utils.h
@@ -18,13 +18,13 @@
#include <ostream>
#include <vector>
-#include "lib/extras/dec/decode.h"
#include "lib/extras/dec/jxl.h"
#include "lib/extras/enc/jxl.h"
#include "lib/extras/packed_image.h"
#include "lib/jxl/base/data_parallel.h"
#include "lib/jxl/base/span.h"
#include "lib/jxl/base/status.h"
+#include "lib/jxl/butteraugli/butteraugli.h"
#include "lib/jxl/codec_in_out.h"
#include "lib/jxl/color_encoding_internal.h"
#include "lib/jxl/enc_params.h"
@@ -63,9 +63,8 @@ void SetThreadParallelRunner(Params params, ThreadPool* pool) {
}
}
-Status DecodeFile(extras::JXLDecompressParams dparams,
- const Span<const uint8_t> file, CodecInOut* JXL_RESTRICT io,
- ThreadPool* pool = nullptr);
+Status DecodeFile(extras::JXLDecompressParams dparams, Span<const uint8_t> file,
+ CodecInOut* JXL_RESTRICT io, ThreadPool* pool = nullptr);
bool Roundtrip(const CodecInOut* io, const CompressParams& cparams,
extras::JXLDecompressParams dparams,
@@ -74,7 +73,7 @@ bool Roundtrip(const CodecInOut* io, const CompressParams& cparams,
// Returns compressed size [bytes].
size_t Roundtrip(const extras::PackedPixelFile& ppf_in,
- extras::JXLCompressParams cparams,
+ const extras::JXLCompressParams& cparams,
extras::JXLDecompressParams dparams, ThreadPool* pool,
extras::PackedPixelFile* ppf_out);
@@ -141,6 +140,18 @@ float ButteraugliDistance(const extras::PackedPixelFile& a,
const extras::PackedPixelFile& b,
ThreadPool* pool = nullptr);
+float ButteraugliDistance(const ImageBundle& rgb0, const ImageBundle& rgb1,
+ const ButteraugliParams& params,
+ const JxlCmsInterface& cms, ImageF* distmap = nullptr,
+ ThreadPool* pool = nullptr,
+ bool ignore_alpha = false);
+
+float ButteraugliDistance(const std::vector<ImageBundle>& frames0,
+ const std::vector<ImageBundle>& frames1,
+ const ButteraugliParams& params,
+ const JxlCmsInterface& cms, ImageF* distmap = nullptr,
+ ThreadPool* pool = nullptr);
+
float Butteraugli3Norm(const extras::PackedPixelFile& a,
const extras::PackedPixelFile& b,
ThreadPool* pool = nullptr);
@@ -169,6 +180,7 @@ class ThreadPoolForTests {
}
ThreadPoolForTests(const ThreadPoolForTests&) = delete;
ThreadPoolForTests& operator&(const ThreadPoolForTests&) = delete;
+ // TODO(eustas): avoid unary `&` overload?
ThreadPool* operator&() { return pool_.get(); }
private:
@@ -188,6 +200,8 @@ Status ReadICC(BitReader* JXL_RESTRICT reader,
Status EncodeFile(const CompressParams& params, const CodecInOut* io,
std::vector<uint8_t>* compressed, ThreadPool* pool = nullptr);
+constexpr const char* BoolToCStr(bool b) { return b ? "true" : "false"; }
+
} // namespace test
bool operator==(const jxl::Bytes& a, const jxl::Bytes& b);
diff --git a/third_party/jpeg-xl/lib/jxl/testing.h b/third_party/jpeg-xl/lib/jxl/testing.h
index 5344399c4c..1fac352a78 100644
--- a/third_party/jpeg-xl/lib/jxl/testing.h
+++ b/third_party/jpeg-xl/lib/jxl/testing.h
@@ -6,15 +6,7 @@
#ifndef LIB_JXL_TESTING_H_
#define LIB_JXL_TESTING_H_
-// GTest/GMock specific macros / wrappers.
-
-// gmock unconditionally redefines those macros (to wrong values).
-// Lets include it only here and mitigate the problem.
-#pragma push_macro("PRIdS")
-#pragma push_macro("PRIuS")
-#include "gmock/gmock.h"
-#pragma pop_macro("PRIuS")
-#pragma pop_macro("PRIdS")
+// GTest specific macros / wrappers.
#include "gtest/gtest.h"
// JPEGXL_ENABLE_BOXES, JPEGXL_ENABLE_TRANSCODE_JPEG
@@ -60,9 +52,26 @@
// Ensures that we don't make our test bounds too lax, effectively disabling the
// tests.
-MATCHER_P(IsSlightlyBelow, max, "") {
- return max * 0.75 <= arg && arg <= max * 1.0;
-}
+#define EXPECT_SLIGHTLY_BELOW(A, E) \
+ { \
+ double _actual = (A); \
+ double _expected = (E); \
+ EXPECT_LE(_actual, _expected); \
+ EXPECT_GE(_actual, 0.75 * _expected); \
+ }
+
+#define EXPECT_ARRAY_NEAR(A, E, T) \
+ { \
+ const auto _actual = (A); \
+ const auto _expected = (E); \
+ const auto _tolerance = (T); \
+ size_t _n = _expected.size(); \
+ ASSERT_EQ(_actual.size(), _n); \
+ for (size_t _i = 0; _i < _n; ++_i) { \
+ EXPECT_NEAR(_actual[_i], _expected[_i], _tolerance) \
+ << "@" << _i << ": " << _actual[_i] << " !~= " << _expected[_i]; \
+ } \
+ }
#define JXL_EXPECT_OK(F) \
{ \
diff --git a/third_party/jpeg-xl/lib/jxl/toc.h b/third_party/jpeg-xl/lib/jxl/toc.h
index 00006440b7..f5b9c65763 100644
--- a/third_party/jpeg-xl/lib/jxl/toc.h
+++ b/third_party/jpeg-xl/lib/jxl/toc.h
@@ -24,7 +24,7 @@ namespace jxl {
constexpr U32Enc kTocDist(Bits(10), BitsOffset(14, 1024), BitsOffset(22, 17408),
BitsOffset(30, 4211712));
-size_t MaxBits(const size_t num_sizes);
+size_t MaxBits(size_t num_sizes);
// TODO(veluca): move these to FrameDimensions.
static JXL_INLINE size_t AcGroupIndex(size_t pass, size_t group,
diff --git a/third_party/jpeg-xl/lib/jxl/transpose-inl.h b/third_party/jpeg-xl/lib/jxl/transpose-inl.h
index 4674420737..efe7d90569 100644
--- a/third_party/jpeg-xl/lib/jxl/transpose-inl.h
+++ b/third_party/jpeg-xl/lib/jxl/transpose-inl.h
@@ -45,9 +45,9 @@ struct TransposeSimdTag {};
// TODO(veluca): it's not super useful to have this in the SIMD namespace.
template <size_t ROWS_or_0, size_t COLS_or_0, class From, class To>
-JXL_INLINE_TRANSPOSE void GenericTransposeBlock(TransposeSimdTag<false>,
- const From& from, const To& to,
- size_t ROWSp, size_t COLSp) {
+JXL_INLINE_TRANSPOSE void GenericTransposeBlock(
+ TransposeSimdTag<false> /* tag */, const From& from, const To& to,
+ size_t ROWSp, size_t COLSp) {
size_t ROWS = ROWS_or_0 == 0 ? ROWSp : ROWS_or_0;
size_t COLS = COLS_or_0 == 0 ? COLSp : COLS_or_0;
for (size_t n = 0; n < ROWS; ++n) {
@@ -64,9 +64,9 @@ constexpr bool TransposeUseSimd(size_t ROWS, size_t COLS) {
}
template <size_t ROWS_or_0, size_t COLS_or_0, class From, class To>
-JXL_INLINE_TRANSPOSE void GenericTransposeBlock(TransposeSimdTag<true>,
- const From& from, const To& to,
- size_t ROWSp, size_t COLSp) {
+JXL_INLINE_TRANSPOSE void GenericTransposeBlock(
+ TransposeSimdTag<true> /* tag */, const From& from, const To& to,
+ size_t ROWSp, size_t COLSp) {
size_t ROWS = ROWS_or_0 == 0 ? ROWSp : ROWS_or_0;
size_t COLS = COLS_or_0 == 0 ? COLSp : COLS_or_0;
static_assert(MaxLanes(BlockDesc<8>()) == 8, "Invalid descriptor size");
@@ -128,9 +128,9 @@ constexpr bool TransposeUseSimd(size_t ROWS, size_t COLS) {
}
template <size_t ROWS_or_0, size_t COLS_or_0, class From, class To>
-JXL_INLINE_TRANSPOSE void GenericTransposeBlock(TransposeSimdTag<true>,
- const From& from, const To& to,
- size_t ROWSp, size_t COLSp) {
+JXL_INLINE_TRANSPOSE void GenericTransposeBlock(
+ TransposeSimdTag<true> /* tag */, const From& from, const To& to,
+ size_t ROWSp, size_t COLSp) {
size_t ROWS = ROWS_or_0 == 0 ? ROWSp : ROWS_or_0;
size_t COLS = COLS_or_0 == 0 ? COLSp : COLS_or_0;
static_assert(MaxLanes(BlockDesc<4>()) == 4, "Invalid descriptor size");
diff --git a/third_party/jpeg-xl/lib/jxl/version.h.in b/third_party/jpeg-xl/lib/jxl/version.h.in
index d077abec79..ad1eb24409 100644
--- a/third_party/jpeg-xl/lib/jxl/version.h.in
+++ b/third_party/jpeg-xl/lib/jxl/version.h.in
@@ -29,7 +29,7 @@
* #endif
* @endcode
*/
-#define JPEGXL_COMPUTE_NUMERIC_VERSION(major,minor,patch) ((major<<24) | (minor<<16) | (patch<<8) | 0)
+#define JPEGXL_COMPUTE_NUMERIC_VERSION(major,minor,patch) (((major)<<24) | ((minor)<<16) | ((patch)<<8) | 0)
/* Numeric representation of the version */
#define JPEGXL_NUMERIC_VERSION JPEGXL_COMPUTE_NUMERIC_VERSION(JPEGXL_MAJOR_VERSION,JPEGXL_MINOR_VERSION,JPEGXL_PATCH_VERSION)
diff --git a/third_party/jpeg-xl/lib/jxl_cms.cmake b/third_party/jpeg-xl/lib/jxl_cms.cmake
index 47757c8f3b..04980066c1 100644
--- a/third_party/jpeg-xl/lib/jxl_cms.cmake
+++ b/third_party/jpeg-xl/lib/jxl_cms.cmake
@@ -62,8 +62,10 @@ install(TARGETS jxl_cms
if (BUILD_SHARED_LIBS)
set(JPEGXL_REQUIRES_TYPE "Requires.private")
+ set(JPEGXL_CMS_PRIVATE_LIBS "-lm ${PKGCONFIG_CXX_LIB}")
else()
set(JPEGXL_REQUIRES_TYPE "Requires")
+ set(JPEGXL_CMS_PRIVATE_LIBS "-lm ${PKGCONFIG_CXX_LIB}")
endif()
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/jxl/libjxl_cms.pc.in"
diff --git a/third_party/jpeg-xl/lib/jxl_lists.bzl b/third_party/jpeg-xl/lib/jxl_lists.bzl
index bbbc444b36..6c98ca15ed 100644
--- a/third_party/jpeg-xl/lib/jxl_lists.bzl
+++ b/third_party/jpeg-xl/lib/jxl_lists.bzl
@@ -188,8 +188,6 @@ libjxl_dec_sources = [
"jxl/epf.cc",
"jxl/epf.h",
"jxl/fast_dct-inl.h",
- "jxl/fast_dct.cc",
- "jxl/fast_dct.h",
"jxl/fast_dct128-inl.h",
"jxl/fast_dct16-inl.h",
"jxl/fast_dct256-inl.h",
@@ -216,6 +214,7 @@ libjxl_dec_sources = [
"jxl/image_bundle.h",
"jxl/image_metadata.cc",
"jxl/image_metadata.h",
+ "jxl/image_ops.cc",
"jxl/image_ops.h",
"jxl/inverse_mtf-inl.h",
"jxl/lehmer_code.h",
diff --git a/third_party/jpeg-xl/lib/jxl_lists.cmake b/third_party/jpeg-xl/lib/jxl_lists.cmake
index e6bf4e5e22..d1a56f9ca8 100644
--- a/third_party/jpeg-xl/lib/jxl_lists.cmake
+++ b/third_party/jpeg-xl/lib/jxl_lists.cmake
@@ -185,8 +185,6 @@ set(JPEGXL_INTERNAL_DEC_SOURCES
jxl/epf.cc
jxl/epf.h
jxl/fast_dct-inl.h
- jxl/fast_dct.cc
- jxl/fast_dct.h
jxl/fast_dct128-inl.h
jxl/fast_dct16-inl.h
jxl/fast_dct256-inl.h
@@ -213,6 +211,7 @@ set(JPEGXL_INTERNAL_DEC_SOURCES
jxl/image_bundle.h
jxl/image_metadata.cc
jxl/image_metadata.h
+ jxl/image_ops.cc
jxl/image_ops.h
jxl/inverse_mtf-inl.h
jxl/lehmer_code.h
diff --git a/third_party/jpeg-xl/lib/jxl_tests.cmake b/third_party/jpeg-xl/lib/jxl_tests.cmake
index eec4149fba..64f807cb82 100644
--- a/third_party/jpeg-xl/lib/jxl_tests.cmake
+++ b/third_party/jpeg-xl/lib/jxl_tests.cmake
@@ -76,7 +76,6 @@ foreach (TESTFILE IN LISTS JPEGXL_INTERNAL_TESTS)
${JPEGXL_COVERAGE_FLAGS}
)
target_link_libraries(${TESTNAME}
- gmock
GTest::GTest
GTest::Main
jxl_testlib-internal
diff --git a/third_party/jpeg-xl/lib/jxl_vars.bzl b/third_party/jpeg-xl/lib/jxl_vars.bzl
index 2c8bccfdf2..fb00f6e34c 100644
--- a/third_party/jpeg-xl/lib/jxl_vars.bzl
+++ b/third_party/jpeg-xl/lib/jxl_vars.bzl
@@ -8,17 +8,18 @@
libjxl_root_package = "libjxl"
libjxl_deps_brotli = ["@brotli//:brotlidec", "@brotli//:brotlienc"]
-libjxl_deps_gif = ["@gif//:gif"]
+libjxl_deps_exr = ["@openexr//:OpenEXR"]
+libjxl_deps_gif = ["@giflib//:giflib"]
libjxl_deps_gtest = ["@googletest//:gtest_main"]
libjxl_deps_hwy = ["@highway//:hwy"]
libjxl_deps_hwy_nanobenchmark = ["@highway//:nanobenchmark"]
libjxl_deps_hwy_test_util = ["@highway//:hwy_test_util"]
libjxl_deps_jpeg = ["@libjpeg_turbo//:jpeg"]
-libjxl_deps_exr = ["@openexr//:OpenEXR"]
libjxl_deps_png = ["@libpng//:png"]
libjxl_deps_runfiles = ["@bazel_tools//tools/cpp/runfiles"]
libjxl_deps_skcms = ["@skcms//:skcms"]
libjxl_deps_testdata = ["//:testdata"]
+libjxl_deps_webp = ["@libwebp//:webp"]
libjxl_test_shards = {
"jpegli/decode_api_test": 10,
diff --git a/third_party/jpeg-xl/lib/nothing.cc b/third_party/jpeg-xl/lib/nothing.cc
new file mode 100644
index 0000000000..e65f587da6
--- /dev/null
+++ b/third_party/jpeg-xl/lib/nothing.cc
@@ -0,0 +1,7 @@
+/* Copyright (c) the JPEG XL Project Authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+
+// Nothing at all. Just a compilation unit.
diff --git a/third_party/jpeg-xl/lib/threads/thread_parallel_runner.cc b/third_party/jpeg-xl/lib/threads/thread_parallel_runner.cc
index d12947ce55..558c9dbe42 100644
--- a/third_party/jpeg-xl/lib/threads/thread_parallel_runner.cc
+++ b/third_party/jpeg-xl/lib/threads/thread_parallel_runner.cc
@@ -41,11 +41,13 @@ bool ThreadMemoryManagerInit(JxlMemoryManager* self,
} else {
memset(self, 0, sizeof(*self));
}
- if (!self->alloc != !self->free) {
+ bool is_default_alloc = (self->alloc == nullptr);
+ bool is_default_free = (self->free == nullptr);
+ if (is_default_alloc != is_default_free) {
return false;
}
- if (!self->alloc) self->alloc = ThreadMemoryManagerDefaultAlloc;
- if (!self->free) self->free = ThreadMemoryManagerDefaultFree;
+ if (is_default_alloc) self->alloc = ThreadMemoryManagerDefaultAlloc;
+ if (is_default_free) self->free = ThreadMemoryManagerDefaultFree;
return true;
}
@@ -57,7 +59,7 @@ void* ThreadMemoryManagerAlloc(const JxlMemoryManager* memory_manager,
void ThreadMemoryManagerFree(const JxlMemoryManager* memory_manager,
void* address) {
- return memory_manager->free(memory_manager->opaque, address);
+ memory_manager->free(memory_manager->opaque, address);
}
} // namespace
diff --git a/third_party/jpeg-xl/lib/threads/thread_parallel_runner_internal.cc b/third_party/jpeg-xl/lib/threads/thread_parallel_runner_internal.cc
index 5f73d94897..7ffcb34428 100644
--- a/third_party/jpeg-xl/lib/threads/thread_parallel_runner_internal.cc
+++ b/third_party/jpeg-xl/lib/threads/thread_parallel_runner_internal.cc
@@ -6,6 +6,7 @@
#include "lib/threads/thread_parallel_runner_internal.h"
#include <jxl/parallel_runner.h>
+#include <jxl/types.h>
#include <algorithm>
#include <atomic>
@@ -120,9 +121,9 @@ void ThreadParallelRunner::RunRange(ThreadParallelRunner* self,
// because it avoids user-specified parameters.
for (;;) {
-#if 0
- // dynamic
- const uint32_t my_size = std::max(num_tasks / (num_worker_threads * 4), 1);
+#if JXL_FALSE
+ // dynamic
+ const uint32_t my_size = std::max(num_tasks / (num_worker_threads * 4), 1);
#else
// guided
const uint32_t num_reserved =
diff --git a/third_party/jpeg-xl/lib/threads/thread_parallel_runner_internal.h b/third_party/jpeg-xl/lib/threads/thread_parallel_runner_internal.h
index 199a5f2a8b..5b28384bf0 100644
--- a/third_party/jpeg-xl/lib/threads/thread_parallel_runner_internal.h
+++ b/third_party/jpeg-xl/lib/threads/thread_parallel_runner_internal.h
@@ -132,8 +132,8 @@ class ThreadParallelRunner {
// Attempts to reserve and perform some work from the global range of tasks,
// which is encoded within "command". Returns after all tasks are reserved.
- static void RunRange(ThreadParallelRunner* self, const WorkerCommand command,
- const int thread);
+ static void RunRange(ThreadParallelRunner* self, WorkerCommand command,
+ int thread);
static void ThreadFunc(ThreadParallelRunner* self, int thread);
diff --git a/third_party/jpeg-xl/plugins/gdk-pixbuf/pixbufloader-jxl.c b/third_party/jpeg-xl/plugins/gdk-pixbuf/pixbufloader-jxl.c
index bafa57b167..6a0de0486b 100644
--- a/third_party/jpeg-xl/plugins/gdk-pixbuf/pixbufloader-jxl.c
+++ b/third_party/jpeg-xl/plugins/gdk-pixbuf/pixbufloader-jxl.c
@@ -331,9 +331,8 @@ static gboolean load_increment(gpointer context, const guchar *buf, guint size,
GError **error) {
GdkPixbufJxlAnimation *decoder_state = context;
if (decoder_state->done == TRUE) {
- g_set_error(error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_FAILED,
- "JXL decoder load_increment called after end of file");
- return FALSE;
+ g_warning_once("Trailing data found at end of JXL file");
+ return TRUE;
}
JxlDecoderStatus status;
@@ -491,9 +490,8 @@ static gboolean load_increment(gpointer context, const guchar *buf, guint size,
decoder_state->frames->len - 1)
.data;
decoder_state->pixel_format.align = gdk_pixbuf_get_rowstride(output);
- guchar *dst = gdk_pixbuf_get_pixels(output);
- size_t num_pixels = decoder_state->xsize * decoder_state->ysize;
- size_t size = num_pixels * decoder_state->pixel_format.num_channels;
+ guint size;
+ guchar *dst = gdk_pixbuf_get_pixels_with_length(output, &size);
if (JXL_DEC_SUCCESS != JxlDecoderSetImageOutBuffer(
decoder_state->decoder,
&decoder_state->pixel_format, dst, size)) {
diff --git a/third_party/jpeg-xl/plugins/gdk-pixbuf/pixbufloader_test.cc b/third_party/jpeg-xl/plugins/gdk-pixbuf/pixbufloader_test.cc
index 5e5642d491..a2e5dc9703 100644
--- a/third_party/jpeg-xl/plugins/gdk-pixbuf/pixbufloader_test.cc
+++ b/third_party/jpeg-xl/plugins/gdk-pixbuf/pixbufloader_test.cc
@@ -16,12 +16,13 @@ int main(int argc, char* argv[]) {
const char* loaders_cache = argv[1];
const char* filename = argv[2];
- setenv("GDK_PIXBUF_MODULE_FILE", loaders_cache, true);
+ const int kDoOverwrite = 1;
+ setenv("GDK_PIXBUF_MODULE_FILE", loaders_cache, kDoOverwrite);
// XDG_DATA_HOME is the path where we look for the mime cache.
// XDG_DATA_DIRS directories are used in addition to XDG_DATA_HOME.
- setenv("XDG_DATA_HOME", ".", true);
- setenv("XDG_DATA_DIRS", "", true);
+ setenv("XDG_DATA_HOME", ".", kDoOverwrite);
+ setenv("XDG_DATA_DIRS", "", kDoOverwrite);
if (!gdk_init_check(nullptr, nullptr)) {
fprintf(stderr, "This test requires a DISPLAY\n");
diff --git a/third_party/jpeg-xl/plugins/gimp/common.cc b/third_party/jpeg-xl/plugins/gimp/common.cc
index 1a884570cb..a40340aaca 100644
--- a/third_party/jpeg-xl/plugins/gimp/common.cc
+++ b/third_party/jpeg-xl/plugins/gimp/common.cc
@@ -15,13 +15,10 @@ JpegXlGimpProgress::JpegXlGimpProgress(const char *message) {
}
void JpegXlGimpProgress::update() {
- gimp_progress_update((float)++cur_progress / (float)max_progress);
- return;
+ gimp_progress_update(static_cast<float>(++cur_progress) /
+ static_cast<float>(max_progress));
}
-void JpegXlGimpProgress::finished() {
- gimp_progress_update(1.0);
- return;
-}
+void JpegXlGimpProgress::finished() { gimp_progress_update(1.0); }
} // namespace jxl
diff --git a/third_party/jpeg-xl/plugins/gimp/file-jxl-load.cc b/third_party/jpeg-xl/plugins/gimp/file-jxl-load.cc
index 4796c1708c..07acd524d2 100644
--- a/third_party/jpeg-xl/plugins/gimp/file-jxl-load.cc
+++ b/third_party/jpeg-xl/plugins/gimp/file-jxl-load.cc
@@ -39,11 +39,14 @@ bool LoadJpegXlImage(const gchar *const filename, gint32 *const image_id) {
GimpColorProfile *profile_icc = nullptr;
GimpColorProfile *profile_int = nullptr;
bool is_linear = false;
- unsigned long xsize = 0, ysize = 0;
- long crop_x0 = 0, crop_y0 = 0;
+ unsigned long xsize = 0;
+ unsigned long ysize = 0;
+ long crop_x0 = 0;
+ long crop_y0 = 0;
size_t layer_idx = 0;
uint32_t frame_duration = 0;
- double tps_denom = 1.f, tps_numer = 1.f;
+ double tps_denom = 1.f;
+ double tps_numer = 1.f;
gint32 layer;
@@ -356,13 +359,13 @@ bool LoadJpegXlImage(const gchar *const filename, gint32 *const image_id) {
const GString *blend_null_flag = g_string_new("");
const GString *blend_replace_flag = g_string_new(" (replace)");
const GString *blend_combine_flag = g_string_new(" (combine)");
- GString *blend;
+ const GString *blend;
if (blend_mode == JXL_BLEND_REPLACE) {
- blend = (GString *)blend_replace_flag;
+ blend = blend_replace_flag;
} else if (blend_mode == JXL_BLEND_BLEND) {
- blend = (GString *)blend_combine_flag;
+ blend = blend_combine_flag;
} else {
- blend = (GString *)blend_null_flag;
+ blend = blend_null_flag;
}
char *temp_frame_name = nullptr;
bool must_free_frame_name = false;
@@ -433,8 +436,10 @@ bool LoadJpegXlImage(const gchar *const filename, gint32 *const image_id) {
" Warning: JxlDecoderGetFrameHeader: Unhandled blend mode: %d\n",
blend_mode);
}
- if ((frame_name_len = frame_header.name_length) > 0) {
- frame_name = (char *)realloc(frame_name, frame_name_len);
+ frame_name_len = frame_header.name_length;
+ if (frame_name_len > 0) {
+ frame_name =
+ reinterpret_cast<char *>(realloc(frame_name, frame_name_len));
if (JXL_DEC_SUCCESS !=
JxlDecoderGetFrameName(dec.get(), frame_name, frame_name_len)) {
g_printerr(LOAD_PROC "Error: JxlDecoderGetFrameName failed");
diff --git a/third_party/jpeg-xl/plugins/gimp/file-jxl-save.cc b/third_party/jpeg-xl/plugins/gimp/file-jxl-save.cc
index 45aaa1f8df..284a9f2771 100644
--- a/third_party/jpeg-xl/plugins/gimp/file-jxl-save.cc
+++ b/third_party/jpeg-xl/plugins/gimp/file-jxl-save.cc
@@ -7,6 +7,7 @@
#include <jxl/encode.h>
#include <jxl/encode_cxx.h>
+#include <jxl/types.h>
#include <cmath>
#include <utility>
@@ -229,7 +230,7 @@ bool JpegXlSaveGui::GuiOnChangeAdvancedMode(GtkWidget* toggle,
gtk_widget_set_sensitive(self->frame_advanced, jxl_save_opts.advanced_mode);
if (!jxl_save_opts.advanced_mode) {
- jxl_save_opts.basic_info.uses_original_profile = false;
+ jxl_save_opts.basic_info.uses_original_profile = JXL_FALSE;
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(self->toggle_no_xyb), false);
jxl_save_opts.use_container = true;
@@ -290,19 +291,20 @@ bool JpegXlSaveGui::SaveDialog() {
"\n\td\u00A0=\u00A03\tFair"
"\n\td\u00A0=\u00A06\tPoor";
- entry_distance = (GtkAdjustment*)gimp_scale_entry_new(
- GTK_TABLE(table), 0, 0, "Distance", SCALE_WIDTH, 0,
- jxl_save_opts.distance, 0.0, 15.0, 0.001, 1.0, 3, true, 0.0, 0.0,
- distance_help, SAVE_PROC);
- gimp_scale_entry_set_logarithmic((GtkObject*)entry_distance, true);
+ entry_distance = reinterpret_cast<GtkAdjustment*>(
+ gimp_scale_entry_new(GTK_TABLE(table), 0, 0, "Distance", SCALE_WIDTH, 0,
+ jxl_save_opts.distance, 0.0, 15.0, 0.001, 1.0, 3,
+ true, 0.0, 0.0, distance_help, SAVE_PROC));
+ gimp_scale_entry_set_logarithmic(reinterpret_cast<GtkObject*>(entry_distance),
+ true);
// Quality Slider
static gchar quality_help[] =
"JPEG-style Quality is remapped to distance. "
"Values roughly match libjpeg quality settings.";
- entry_quality = (GtkAdjustment*)gimp_scale_entry_new(
+ entry_quality = reinterpret_cast<GtkAdjustment*>(gimp_scale_entry_new(
GTK_TABLE(table), 0, 1, "Quality", SCALE_WIDTH, 0, jxl_save_opts.quality,
- 8.26, 100.0, 1.0, 10.0, 2, true, 0.0, 0.0, quality_help, SAVE_PROC);
+ 8.26, 100.0, 1.0, 10.0, 2, true, 0.0, 0.0, quality_help, SAVE_PROC));
// Distance and Quality Signals
handle_entry_distance = g_signal_connect(
@@ -322,10 +324,10 @@ bool JpegXlSaveGui::SaveDialog() {
"the encoder uses less effort to hit distance targets. "
"As\u00A0a\u00A0result, image quality may be decreased. "
"Default\u00A0=\u00A03.";
- entry_effort = (GtkAdjustment*)gimp_scale_entry_new(
- GTK_TABLE(table), 0, 3, "Speed", SCALE_WIDTH, 0,
- 10 - jxl_save_opts.encoding_effort, 1, 9, 1, 2, 0, true, 0.0, 0.0,
- effort_help, SAVE_PROC);
+ entry_effort = reinterpret_cast<GtkAdjustment*>(
+ gimp_scale_entry_new(GTK_TABLE(table), 0, 3, "Speed", SCALE_WIDTH, 0,
+ 10 - jxl_save_opts.encoding_effort, 1, 9, 1, 2, 0,
+ true, 0.0, 0.0, effort_help, SAVE_PROC));
// effort signal
g_signal_connect(entry_effort, "value-changed", G_CALLBACK(GuiOnChangeEffort),
@@ -415,10 +417,10 @@ bool JpegXlSaveGui::SaveDialog() {
gtk_container_add(GTK_CONTAINER(vbox), table);
gtk_widget_show(table);
- entry_faster = (GtkAdjustment*)gimp_scale_entry_new(
- GTK_TABLE(table), 0, 0, "Faster Decoding", SCALE_WIDTH, 0,
- jxl_save_opts.faster_decoding, 0, 4, 1, 1, 0, true, 0.0, 0.0, faster_help,
- SAVE_PROC);
+ entry_faster = reinterpret_cast<GtkAdjustment*>(
+ gimp_scale_entry_new(GTK_TABLE(table), 0, 0, "Faster Decoding",
+ SCALE_WIDTH, 0, jxl_save_opts.faster_decoding, 0, 4,
+ 1, 1, 0, true, 0.0, 0.0, faster_help, SAVE_PROC));
// Faster Decoding Signals
g_signal_connect(entry_faster, "value-changed",
@@ -472,7 +474,6 @@ JpegXlSaveOpts::JpegXlSaveOpts() {
pixel_format.align = 0;
JxlEncoderInitBasicInfo(&basic_info);
- return;
} // JpegXlSaveOpts constructor
bool JpegXlSaveOpts::SetModel(bool is_linear_) {
@@ -568,7 +569,8 @@ bool JpegXlSaveOpts::SetNumChannels(int channels) {
pixel_format.num_channels = 2;
basic_info.num_color_channels = 1;
basic_info.num_extra_channels = 1;
- basic_info.alpha_bits = int(std::fmin(16, basic_info.bits_per_sample));
+ basic_info.alpha_bits =
+ static_cast<int>(std::fmin(16, basic_info.bits_per_sample));
basic_info.alpha_exponent_bits = 0;
break;
case 3:
@@ -582,7 +584,8 @@ bool JpegXlSaveOpts::SetNumChannels(int channels) {
pixel_format.num_channels = 4;
basic_info.num_color_channels = 3;
basic_info.num_extra_channels = 1;
- basic_info.alpha_bits = int(std::fmin(16, basic_info.bits_per_sample));
+ basic_info.alpha_bits =
+ static_cast<int>(std::fmin(16, basic_info.bits_per_sample));
basic_info.alpha_exponent_bits = 0;
break;
default:
@@ -698,7 +701,7 @@ bool SaveJpegXlImage(const gint32 image_id, const gint32 drawable_id,
// treat layers as animation frames, for now
if (nlayers > 1) {
- jxl_save_opts.basic_info.have_animation = true;
+ jxl_save_opts.basic_info.have_animation = JXL_TRUE;
jxl_save_opts.basic_info.animation.tps_numerator = 100;
}
@@ -738,12 +741,12 @@ bool SaveJpegXlImage(const gint32 image_id, const gint32 drawable_id,
jxl_save_opts.icc_attached = true;
} else {
g_printerr(SAVE_PROC " Warning: JxlEncoderSetICCProfile failed.\n");
- jxl_save_opts.basic_info.uses_original_profile = false;
+ jxl_save_opts.basic_info.uses_original_profile = JXL_FALSE;
jxl_save_opts.lossless = false;
}
} else {
g_printerr(SAVE_PROC " Warning: Using internal profile.\n");
- jxl_save_opts.basic_info.uses_original_profile = false;
+ jxl_save_opts.basic_info.uses_original_profile = JXL_FALSE;
jxl_save_opts.lossless = false;
}
@@ -751,9 +754,11 @@ bool SaveJpegXlImage(const gint32 image_id, const gint32 drawable_id,
JxlColorEncoding color_encoding = {};
if (jxl_save_opts.is_linear) {
- JxlColorEncodingSetToLinearSRGB(&color_encoding, jxl_save_opts.is_gray);
+ JxlColorEncodingSetToLinearSRGB(&color_encoding,
+ TO_JXL_BOOL(jxl_save_opts.is_gray));
} else {
- JxlColorEncodingSetToSRGB(&color_encoding, jxl_save_opts.is_gray);
+ JxlColorEncodingSetToSRGB(&color_encoding,
+ TO_JXL_BOOL(jxl_save_opts.is_gray));
}
if (JXL_ENC_SUCCESS !=
@@ -777,15 +782,15 @@ bool SaveJpegXlImage(const gint32 image_id, const gint32 drawable_id,
// lossless mode doesn't work well with floating point
jxl_save_opts.distance = 0.01;
jxl_save_opts.lossless = false;
- JxlEncoderSetFrameLossless(frame_settings, false);
+ JxlEncoderSetFrameLossless(frame_settings, JXL_FALSE);
JxlEncoderSetFrameDistance(frame_settings, 0.01);
} else {
JxlEncoderSetFrameDistance(frame_settings, 0);
- JxlEncoderSetFrameLossless(frame_settings, true);
+ JxlEncoderSetFrameLossless(frame_settings, JXL_TRUE);
}
} else {
jxl_save_opts.lossless = false;
- JxlEncoderSetFrameLossless(frame_settings, false);
+ JxlEncoderSetFrameLossless(frame_settings, JXL_FALSE);
JxlEncoderSetFrameDistance(frame_settings, jxl_save_opts.distance);
}
diff --git a/third_party/jpeg-xl/plugins/mime/README.md b/third_party/jpeg-xl/plugins/mime/README.md
index 4d398c7b90..6954a91e48 100644
--- a/third_party/jpeg-xl/plugins/mime/README.md
+++ b/third_party/jpeg-xl/plugins/mime/README.md
@@ -1,6 +1,6 @@
## :warning: Not needed anymore
-As `image/jxl` is now supported by [shared-mine-info 2.2](https://gitlab.freedesktop.org/xdg/shared-mime-info/-/releases/2.2), it should not be necessary anymore to install this plugin.
+As `image/jxl` is now supported by [shared-mime-info 2.2](https://gitlab.freedesktop.org/xdg/shared-mime-info/-/releases/2.2), it should not be necessary anymore to install this plugin.
You can test if your system correctly understand the MIME type of JPEG XL image by obtaining a JPEG XL image, e.g. with
```bash
diff --git a/third_party/libwebrtc/BUILD.gn b/third_party/libwebrtc/BUILD.gn
index 612483cef8..7feca08e60 100644
--- a/third_party/libwebrtc/BUILD.gn
+++ b/third_party/libwebrtc/BUILD.gn
@@ -509,6 +509,7 @@ if (!build_with_chromium) {
deps = [
"api:create_peerconnection_factory",
+ "api:enable_media",
"api:libjingle_peerconnection_api",
"api:rtc_error",
"api:transport_api",
@@ -544,6 +545,7 @@ if (!build_with_chromium) {
if (build_with_mozilla) {
deps -= [
"api:create_peerconnection_factory",
+ "api:enable_media",
"api:rtc_error",
"api:transport_api",
"api/crypto",
@@ -657,6 +659,7 @@ if (rtc_include_tests && !build_with_chromium) {
"p2p:libstunprober_unittests",
"p2p:rtc_p2p_unittests",
"rtc_base:async_dns_resolver_unittests",
+ "rtc_base:async_packet_socket_unittest",
"rtc_base:callback_list_unittests",
"rtc_base:rtc_base_approved_unittests",
"rtc_base:rtc_base_unittests",
@@ -850,10 +853,10 @@ rtc_static_library("dcsctp") {
group("poison_audio_codecs") {
}
-group("poison_default_task_queue") {
+group("poison_default_echo_detector") {
}
-group("poison_default_echo_detector") {
+group("poison_environment_construction") {
}
group("poison_software_video_codecs") {
diff --git a/third_party/libwebrtc/DEPS b/third_party/libwebrtc/DEPS
index 0b5b97252c..0f25b04412 100644
--- a/third_party/libwebrtc/DEPS
+++ b/third_party/libwebrtc/DEPS
@@ -10,7 +10,7 @@ vars = {
# chromium waterfalls. More info at: crbug.com/570091.
'checkout_configuration': 'default',
'checkout_instrumented_libraries': 'checkout_linux and checkout_configuration == "default"',
- 'chromium_revision': 'c89d7a6d7f77a1afc1e3759f0e66a49eca1ee1d7',
+ 'chromium_revision': '60cf2ce5ba3417695d02754c90bd919eb438e4b5',
# Fetch the prebuilt binaries for llvm-cov and llvm-profdata. Needed to
# process the raw profiles produced by instrumented targets (built with
@@ -25,14 +25,14 @@ vars = {
# By default, download the fuchsia sdk from the public sdk directory.
'fuchsia_sdk_cipd_prefix': 'fuchsia/sdk/core/',
- 'fuchsia_version': 'version:15.20231022.3.1',
+ 'fuchsia_version': 'version:16.20231129.1.1',
# By default, download the fuchsia images from the fuchsia GCS bucket.
'fuchsia_images_bucket': 'fuchsia',
'checkout_fuchsia': False,
# Since the images are hundreds of MB, default to only downloading the image
# most commonly useful for developers. Bots and developers that need to use
# other images can override this with additional images.
- 'checkout_fuchsia_boot_images': "terminal.qemu-x64,terminal.x64",
+ 'checkout_fuchsia_boot_images': "terminal.x64",
'checkout_fuchsia_product_bundles': '"{checkout_fuchsia_boot_images}" != ""',
# Fetch configuration files required for the 'use_remoteexec' gn arg
@@ -40,7 +40,7 @@ vars = {
# RBE instance to use for running remote builds
'rbe_instance': 'projects/rbe-webrtc-developer/instances/default_instance',
# reclient CIPD package version
- 'reclient_version': 're_client_version:0.117.1.21520c6-gomaip',
+ 'reclient_version': 're_client_version:0.121.0.e622934-gomaip',
# ninja CIPD package version
# https://chrome-infra-packages.appspot.com/p/infra/3pp/tools/ninja
@@ -50,30 +50,30 @@ vars = {
deps = {
# TODO(kjellander): Move this to be Android-only.
'src/base':
- 'https://chromium.googlesource.com/chromium/src/base@1546e3adb67bb711ca2fd39c3913cb56bd889748',
+ 'https://chromium.googlesource.com/chromium/src/base@f0b935140fa4d6c206b3419056f8e647ec7e6583',
'src/build':
- 'https://chromium.googlesource.com/chromium/src/build@a21fc6065131d0442e8a54c3ca2638e393b69438',
+ 'https://chromium.googlesource.com/chromium/src/build@bb826aaf00833bb61244a7ab5c4ca8c69c51314a',
'src/buildtools':
- 'https://chromium.googlesource.com/chromium/src/buildtools@6f834e2039daedfc68a2749c217922b26d5e8497',
+ 'https://chromium.googlesource.com/chromium/src/buildtools@b17c7e870e1d722d81f59738707392accf633011',
# Gradle 6.6.1. Used for testing Android Studio project generation for WebRTC.
'src/examples/androidtests/third_party/gradle': {
'url': 'https://chromium.googlesource.com/external/github.com/gradle/gradle.git@f2d1fb54a951d8b11d25748e4711bec8d128d7e3',
'condition': 'checkout_android',
},
'src/ios': {
- 'url': 'https://chromium.googlesource.com/chromium/src/ios@5139a7efd464e4514a6df1054e44e0e4fac67536',
+ 'url': 'https://chromium.googlesource.com/chromium/src/ios@f85ff5cfa70484822ca7181012597114ae7ad125',
'condition': 'checkout_ios',
},
'src/testing':
- 'https://chromium.googlesource.com/chromium/src/testing@46366a7e4d08bf9fceeb3c1c3b5eab8a6a5024b5',
+ 'https://chromium.googlesource.com/chromium/src/testing@189d923e10bfcb856eff08164d6140f93938d854',
'src/third_party':
- 'https://chromium.googlesource.com/chromium/src/third_party@64d9ec3158b4629163f88b779a53e16f1cc24f81',
+ 'https://chromium.googlesource.com/chromium/src/third_party@c35e8a3c66aaeb31689af01f6ef63509504b68ff',
'src/buildtools/linux64': {
'packages': [
{
'package': 'gn/gn/linux-${{arch}}',
- 'version': 'git_revision:e4702d7409069c4f12d45ea7b7f0890717ca3f4b',
+ 'version': 'git_revision:7367b0df0a0aa25440303998d54045bda73935a5',
}
],
'dep_type': 'cipd',
@@ -83,7 +83,7 @@ deps = {
'packages': [
{
'package': 'gn/gn/mac-${{arch}}',
- 'version': 'git_revision:e4702d7409069c4f12d45ea7b7f0890717ca3f4b',
+ 'version': 'git_revision:7367b0df0a0aa25440303998d54045bda73935a5',
}
],
'dep_type': 'cipd',
@@ -93,7 +93,7 @@ deps = {
'packages': [
{
'package': 'gn/gn/windows-amd64',
- 'version': 'git_revision:e4702d7409069c4f12d45ea7b7f0890717ca3f4b',
+ 'version': 'git_revision:7367b0df0a0aa25440303998d54045bda73935a5',
}
],
'dep_type': 'cipd',
@@ -115,11 +115,11 @@ deps = {
'src/third_party/clang-format/script':
'https://chromium.googlesource.com/external/github.com/llvm/llvm-project/clang/tools/clang-format.git@e5337933f2951cacd3aeacd238ce4578163ca0b9',
'src/third_party/libc++/src':
- 'https://chromium.googlesource.com/external/github.com/llvm/llvm-project/libcxx.git@a429c26ae25c26a569ff12390d5f9be70c5e286b',
+ 'https://chromium.googlesource.com/external/github.com/llvm/llvm-project/libcxx.git@0ad014cff4509d293e62d1d8c7ffd080bcb2f2d6',
'src/third_party/libc++abi/src':
- 'https://chromium.googlesource.com/external/github.com/llvm/llvm-project/libcxxabi.git@2ca9f38714b1465b9f55b5fbd0da5e4342811e2b',
+ 'https://chromium.googlesource.com/external/github.com/llvm/llvm-project/libcxxabi.git@4cb5c2cefedc025433f81735bacbc0f773fdcd8f',
'src/third_party/libunwind/src':
- 'https://chromium.googlesource.com/external/github.com/llvm/llvm-project/libunwind.git@7686b5d38c69d14932abfb1c1a66ba56c78791ad',
+ 'https://chromium.googlesource.com/external/github.com/llvm/llvm-project/libunwind.git@bbe2764382995e4ec9a8c26c50018afc9520ea4f',
'src/third_party/ninja': {
'packages': [
@@ -166,7 +166,18 @@ deps = {
'packages': [
{
'package': 'chromium/third_party/android_build_tools/bundletool',
- 'version': 'xOeKyLIaK_RRHU0Qv0EdxTrRlq_22HAwoOr1xn5yoOcC',
+ 'version': 'XoK0RwIzanpFScg7dU_8th5zMvLgKvk7c_PmhZ5LSEQC',
+ },
+ ],
+ 'condition': 'checkout_android',
+ 'dep_type': 'cipd',
+ },
+
+ 'src/third_party/aosp_dalvik': {
+ 'packages': [
+ {
+ 'package': 'chromium/third_party/aosp_dalvik/linux-amd64',
+ 'version': 'version:2@13.0.0_r24.cr1',
},
],
'condition': 'checkout_android',
@@ -174,11 +185,11 @@ deps = {
},
'src/third_party/boringssl/src':
- 'https://boringssl.googlesource.com/boringssl.git@c38dc29860a72540eb2c4fdb8a8bfb27ef94ddf3',
+ 'https://boringssl.googlesource.com/boringssl.git@1b7fdbd9101dedc3e0aa3fcf4ff74eacddb34ecc',
'src/third_party/breakpad/breakpad':
- 'https://chromium.googlesource.com/breakpad/breakpad.git@8988364bcddd9b194b0bf931c10bc125987330ed',
+ 'https://chromium.googlesource.com/breakpad/breakpad.git@f49c2f1a2023da0cb055874fba050563dfea57db',
'src/third_party/catapult':
- 'https://chromium.googlesource.com/catapult.git@47efdb4b1428e549c58a6d6c2fa79c4a8ceaf9b4',
+ 'https://chromium.googlesource.com/catapult.git@ee967548fe6a699fc295d81bd05c8116bcaf5e7e',
'src/third_party/ced/src': {
'url': 'https://chromium.googlesource.com/external/github.com/google/compact_enc_det.git@ba412eaaacd3186085babcd901679a48863c7dd5',
},
@@ -191,9 +202,9 @@ deps = {
'src/third_party/crc32c/src':
'https://chromium.googlesource.com/external/github.com/google/crc32c.git@fa5ade41ee480003d9c5af6f43567ba22e4e17e6',
'src/third_party/depot_tools':
- 'https://chromium.googlesource.com/chromium/tools/depot_tools.git@9f3b33a275e7a5b19d8ce4aba7960d2a38858681',
+ 'https://chromium.googlesource.com/chromium/tools/depot_tools.git@b5393e57bb81eb1b6fbecbd7f466abcb61d278b4',
'src/third_party/ffmpeg':
- 'https://chromium.googlesource.com/chromium/third_party/ffmpeg.git@e1ca3f06adec15150a171bc38f550058b4bbb23b',
+ 'https://chromium.googlesource.com/chromium/third_party/ffmpeg.git@866768f35c2226f4c805844207fd11c049ebe962',
'src/third_party/flatbuffers/src':
'https://chromium.googlesource.com/external/github.com/google/flatbuffers.git@bcb9ef187628fe07514e57756d05e6a6296f7dc5',
'src/third_party/grpc/src': {
@@ -205,9 +216,9 @@ deps = {
'condition': 'checkout_linux',
},
'src/third_party/freetype/src':
- 'https://chromium.googlesource.com/chromium/src/third_party/freetype2.git@55d0287cfc31115760cb13caa346b407ef0c0ceb',
+ 'https://chromium.googlesource.com/chromium/src/third_party/freetype2.git@8f255c89e14219ca2489043f699797ee106ec6e9',
'src/third_party/harfbuzz-ng/src':
- 'https://chromium.googlesource.com/external/github.com/harfbuzz/harfbuzz.git@f26fd69d858642d76413b8f4068eaf9b57c40a5f',
+ 'https://chromium.googlesource.com/external/github.com/harfbuzz/harfbuzz.git@920c40cd43dd7b10b7ecba3d82a46f5fea88536f',
'src/third_party/google_benchmark/src': {
'url': 'https://chromium.googlesource.com/external/github.com/google/benchmark.git@b177433f3ee2513b1075140c723d73ab8901790f',
},
@@ -256,7 +267,7 @@ deps = {
'packages': [
{
'package': 'chromium/third_party/kotlin_stdlib',
- 'version': 'ZwEhbBOU3zJ8iFzea34zthR0d1a1LlfSPjfsblxKbSgC',
+ 'version': 'QEHg036Jc2HWG4-ao7usl1QUexRidGFFSgqqWUpmK-YC',
},
],
'condition': 'checkout_android',
@@ -267,7 +278,7 @@ deps = {
'packages': [
{
'package': 'chromium/third_party/kotlinc',
- 'version': '6Hdj5fkzcomS1cNTWnXoeTZj0wvCG4zdyLtZ23eK-U4C',
+ 'version': 'WKNG-_aQcnsBG-F7SS-yUGLlN9roxcWYt1K_8uw27zQC',
},
],
'condition': 'checkout_android',
@@ -276,6 +287,8 @@ deps = {
# Used for building libFuzzers (only supports Linux).
'src/third_party/libFuzzer/src':
'https://chromium.googlesource.com/external/github.com/llvm/llvm-project/compiler-rt/lib/fuzzer.git@758bd21f103a501b362b1ca46fa8fcb692eaa303',
+ 'src/third_party/fuzztest/src':
+ 'https://chromium.googlesource.com/external/github.com/google/fuzztest.git@9e3dbc646516772c70f7a100be53967323d310cb',
'src/third_party/libjpeg_turbo':
'https://chromium.googlesource.com/chromium/deps/libjpeg_turbo.git@9b894306ec3b28cea46e84c32b56773a98c483da',
'src/third_party/libsrtp':
@@ -283,15 +296,15 @@ deps = {
'src/third_party/dav1d/libdav1d':
'https://chromium.googlesource.com/external/github.com/videolan/dav1d.git@47107e384bd1dc25674acf04d000a8cdc6195234',
'src/third_party/libaom/source/libaom':
- 'https://aomedia.googlesource.com/aom.git@1dbe1c7fae2456f91ccc79fecb919e9ffea0727a',
+ 'https://aomedia.googlesource.com/aom.git@af3d2a707b5a89d5ffc77260698230505d9bcd35',
'src/third_party/libunwindstack': {
'url': 'https://chromium.googlesource.com/chromium/src/third_party/libunwindstack.git@4dbfa0e8c844c8e243b297bc185e54a99ff94f9e',
'condition': 'checkout_android',
},
'src/third_party/perfetto':
- 'https://android.googlesource.com/platform/external/perfetto.git@cefa83de08a0851cc0b0edee8801cf860a3bc1ed',
+ 'https://android.googlesource.com/platform/external/perfetto.git@d8a8260e8a08b166547eecd5b6ffcbdb30421109',
'src/third_party/libvpx/source/libvpx':
- 'https://chromium.googlesource.com/webm/libvpx.git@424723dc025ce451dab9568239a46b18d0919b4d',
+ 'https://chromium.googlesource.com/webm/libvpx.git@741b8f6228984e888c99849d7675ea4132eaf268',
'src/third_party/libyuv':
'https://chromium.googlesource.com/libyuv/libyuv.git@04821d1e7d60845525e8db55c7bcd41ef5be9406',
'src/third_party/lss': {
@@ -310,11 +323,15 @@ deps = {
'src/third_party/openh264/src':
'https://chromium.googlesource.com/external/github.com/cisco/openh264@09a4f3ec842a8932341b195c5b01e141c8a16eb7',
+
+ 'src/third_party/re2/src':
+ 'https://chromium.googlesource.com/external/github.com/google/re2.git@7e0c1a9e2417e70e5f0efc323267ac71d1fa0685',
+
'src/third_party/r8': {
'packages': [
{
'package': 'chromium/third_party/r8',
- 'version': 'jj098_uPn3EKB7YisD1VAQXkZWNtSa6Qxz3vpMQkPR4C',
+ 'version': 'wtFJRWzGTig_UR3UW82YW63l-sTznrAPEatq-o7zNqYC',
},
],
'condition': 'checkout_android',
@@ -338,7 +355,7 @@ deps = {
'condition': 'checkout_android',
},
'src/tools':
- 'https://chromium.googlesource.com/chromium/src/tools@d7f60c3fd236aee6695f04187b6c128536a2bc9f',
+ 'https://chromium.googlesource.com/chromium/src/tools@bcc6c5bc9871bcf8842e6a42397939235fa04860',
'src/third_party/accessibility_test_framework': {
'packages': [
@@ -351,28 +368,6 @@ deps = {
'dep_type': 'cipd',
},
- 'src/third_party/byte_buddy': {
- 'packages': [
- {
- 'package': 'chromium/third_party/byte_buddy',
- 'version': 'c9b53316603fc2d997c899c7ca1707f809b918cd',
- },
- ],
- 'condition': 'checkout_android',
- 'dep_type': 'cipd',
- },
-
- 'src/third_party/byte_buddy/android_sdk_build_tools_25_0_2': {
- 'packages': [
- {
- 'package': 'chromium/third_party/android_sdk/public/build-tools',
- 'version': 'kwIs2vdfTm93yEP8LG5aSnchN4BVEdVxbqQtF4XpPdkC',
- },
- ],
- 'condition': 'checkout_android',
- 'dep_type': 'cipd',
- },
-
'src/third_party/espresso': {
'packages': [
{
@@ -410,7 +405,7 @@ deps = {
'packages': [
{
'package': 'chromium/third_party/androidx',
- 'version': 'F-habe4EUUBiRQmzyGAB5oOUtnTNQkhvpoUe4vVZuegC',
+ 'version': 'fBcslNfNCVI61lUhYka626dfmzui_5hT7AWrfFSdkgMC',
},
],
'condition': 'checkout_android',
@@ -421,7 +416,7 @@ deps = {
'packages': [
{
'package': 'chromium/third_party/android_build_tools/manifest_merger',
- 'version': 'V90mMwKNdDvQaZ-2eMjmdkHQdGrDn3w4DxA-fGMA8y0C',
+ 'version': 'SdNR04V227YL22FMmKoS4AdLYwv6MJe8HBAZKNhXoCsC',
},
],
'condition': 'checkout_android',
@@ -439,10 +434,6 @@ deps = {
'version': '9lGp8nTUCRRWGMnI_96HcKfzjnxEJKUcfvfwmA3wXNkC',
},
{
- 'package': 'chromium/third_party/android_sdk/public/patcher',
- 'version': 'I6FNMhrXlpB-E1lOhMlvld7xt9lBVNOO83KIluXDyA0C',
- },
- {
'package': 'chromium/third_party/android_sdk/public/platform-tools',
'version': 'HWVsGs2HCKgSVv41FsOcsfJbNcB0UFiNrF6Tc4yRArYC',
},
@@ -451,14 +442,6 @@ deps = {
'version': 'u-bhWbTME6u-DjypTgr3ZikCyeAeU6txkR9ET6Uudc8C',
},
{
- 'package': 'chromium/third_party/android_sdk/public/platforms/android-tiramisuprivacysandbox',
- 'version': 'YWMYkzyxGBgVsty0GhXL1oxbY0pGXQIgFc0Rh7ZMRPYC',
- },
- {
- 'package': 'chromium/third_party/android_sdk/public/sources/android-31',
- 'version': '_a_BcnANjPYw5mSKlNHa7GFY8yc1kdqj2rmQgac7yUcC',
- },
- {
'package': 'chromium/third_party/android_sdk/public/cmdline-tools',
'version': 'Sy00LuyBIUJdRGYKwg0zjWH8eAIUvgnnNiPkI8etaZYC',
},
@@ -515,7 +498,7 @@ deps = {
'packages': [
{
'package': 'chromium/third_party/turbine',
- 'version': 'VRQ9UNP0lvjDXJ4DhORCj66go0TLg5uuGnHWkNN_hgUC',
+ 'version': 'e8ccyNXO5wVjI0vv5W8kfA101BaaLNjNiVH1JddpdWkC',
},
],
'condition': 'checkout_android',
@@ -526,11 +509,11 @@ deps = {
'packages': [
{
'package': 'infra/tools/luci/isolate/${{platform}}',
- 'version': 'git_revision:924cfd2323a9192361b765f81fffc135026c1fee',
+ 'version': 'git_revision:1ea45c1829514ff20c476f083462e7b8fdfaf9ae',
},
{
'package': 'infra/tools/luci/swarming/${{platform}}',
- 'version': 'git_revision:924cfd2323a9192361b765f81fffc135026c1fee',
+ 'version': 'git_revision:1ea45c1829514ff20c476f083462e7b8fdfaf9ae',
},
],
'dep_type': 'cipd',
@@ -1152,7 +1135,7 @@ deps = {
'packages': [
{
'package': 'chromium/third_party/android_deps/libs/com_google_android_material_material',
- 'version': 'version:2@1.7.0-alpha02.cr1',
+ 'version': 'version:2@1.11.0-beta01.cr1',
},
],
'condition': 'checkout_android',
@@ -1218,7 +1201,7 @@ deps = {
'packages': [
{
'package': 'chromium/third_party/android_deps/libs/com_google_auto_value_auto_value_annotations',
- 'version': 'version:2@1.10.1.cr1',
+ 'version': 'version:2@1.10.4.cr1',
},
],
'condition': 'checkout_android',
@@ -1295,7 +1278,7 @@ deps = {
'packages': [
{
'package': 'chromium/third_party/android_deps/libs/com_google_errorprone_error_prone_annotation',
- 'version': 'version:2@2.11.0.cr1',
+ 'version': 'version:2@2.19.1.cr1',
},
],
'condition': 'checkout_android',
@@ -1515,7 +1498,7 @@ deps = {
'packages': [
{
'package': 'chromium/third_party/android_deps/libs/com_google_guava_guava',
- 'version': 'version:2@31.1-jre.cr1',
+ 'version': 'version:2@32.1.3-jre.cr1',
},
],
'condition': 'checkout_android',
@@ -1526,18 +1509,7 @@ deps = {
'packages': [
{
'package': 'chromium/third_party/android_deps/libs/com_google_guava_guava_android',
- 'version': 'version:2@31.1-android.cr1',
- },
- ],
- 'condition': 'checkout_android',
- 'dep_type': 'cipd',
- },
-
- 'src/third_party/android_deps/libs/com_google_guava_listenablefuture': {
- 'packages': [
- {
- 'package': 'chromium/third_party/android_deps/libs/com_google_guava_listenablefuture',
- 'version': 'version:2@1.0.cr1',
+ 'version': 'version:2@32.1.3-android.cr1',
},
],
'condition': 'checkout_android',
@@ -1548,7 +1520,7 @@ deps = {
'packages': [
{
'package': 'chromium/third_party/android_deps/libs/com_google_j2objc_j2objc_annotations',
- 'version': 'version:2@1.3.cr1',
+ 'version': 'version:2@2.8.cr1',
},
],
'condition': 'checkout_android',
@@ -1790,7 +1762,7 @@ deps = {
'packages': [
{
'package': 'chromium/third_party/android_deps/libs/org_bouncycastle_bcprov_jdk18on',
- 'version': 'version:2@1.72.cr1',
+ 'version': 'version:2@1.76.cr1',
},
],
'condition': 'checkout_android',
@@ -1823,7 +1795,7 @@ deps = {
'packages': [
{
'package': 'chromium/third_party/android_deps/libs/org_checkerframework_checker_qual',
- 'version': 'version:2@3.25.0.cr1',
+ 'version': 'version:2@3.37.0.cr1',
},
],
'condition': 'checkout_android',
@@ -2021,7 +1993,7 @@ deps = {
'packages': [
{
'package': 'chromium/third_party/android_deps/libs/org_ow2_asm_asm',
- 'version': 'version:2@9.5.cr1',
+ 'version': 'version:2@9.6.cr1',
},
],
'condition': 'checkout_android',
@@ -2032,7 +2004,7 @@ deps = {
'packages': [
{
'package': 'chromium/third_party/android_deps/libs/org_ow2_asm_asm_analysis',
- 'version': 'version:2@9.5.cr1',
+ 'version': 'version:2@9.6.cr1',
},
],
'condition': 'checkout_android',
@@ -2043,7 +2015,7 @@ deps = {
'packages': [
{
'package': 'chromium/third_party/android_deps/libs/org_ow2_asm_asm_commons',
- 'version': 'version:2@9.5.cr1',
+ 'version': 'version:2@9.6.cr1',
},
],
'condition': 'checkout_android',
@@ -2054,7 +2026,7 @@ deps = {
'packages': [
{
'package': 'chromium/third_party/android_deps/libs/org_ow2_asm_asm_tree',
- 'version': 'version:2@9.5.cr1',
+ 'version': 'version:2@9.6.cr1',
},
],
'condition': 'checkout_android',
@@ -2065,7 +2037,7 @@ deps = {
'packages': [
{
'package': 'chromium/third_party/android_deps/libs/org_ow2_asm_asm_util',
- 'version': 'version:2@9.5.cr1',
+ 'version': 'version:2@9.6.cr1',
},
],
'condition': 'checkout_android',
@@ -2087,7 +2059,7 @@ deps = {
'packages': [
{
'package': 'chromium/third_party/android_deps/libs/org_robolectric_annotations',
- 'version': 'version:2@4.10.3.cr1',
+ 'version': 'version:2@4.11.1.cr1',
},
],
'condition': 'checkout_android',
@@ -2098,7 +2070,7 @@ deps = {
'packages': [
{
'package': 'chromium/third_party/android_deps/libs/org_robolectric_junit',
- 'version': 'version:2@4.10.3.cr1',
+ 'version': 'version:2@4.11.1.cr1',
},
],
'condition': 'checkout_android',
@@ -2109,7 +2081,7 @@ deps = {
'packages': [
{
'package': 'chromium/third_party/android_deps/libs/org_robolectric_nativeruntime',
- 'version': 'version:2@4.10.3.cr1',
+ 'version': 'version:2@4.11.1.cr1',
},
],
'condition': 'checkout_android',
@@ -2120,7 +2092,7 @@ deps = {
'packages': [
{
'package': 'chromium/third_party/android_deps/libs/org_robolectric_nativeruntime_dist_compat',
- 'version': 'version:2@1.0.1.cr1',
+ 'version': 'version:2@1.0.2.cr1',
},
],
'condition': 'checkout_android',
@@ -2131,7 +2103,7 @@ deps = {
'packages': [
{
'package': 'chromium/third_party/android_deps/libs/org_robolectric_pluginapi',
- 'version': 'version:2@4.10.3.cr1',
+ 'version': 'version:2@4.11.1.cr1',
},
],
'condition': 'checkout_android',
@@ -2142,7 +2114,7 @@ deps = {
'packages': [
{
'package': 'chromium/third_party/android_deps/libs/org_robolectric_plugins_maven_dependency_resolver',
- 'version': 'version:2@4.10.3.cr1',
+ 'version': 'version:2@4.11.1.cr1',
},
],
'condition': 'checkout_android',
@@ -2153,7 +2125,7 @@ deps = {
'packages': [
{
'package': 'chromium/third_party/android_deps/libs/org_robolectric_resources',
- 'version': 'version:2@4.10.3.cr1',
+ 'version': 'version:2@4.11.1.cr1',
},
],
'condition': 'checkout_android',
@@ -2164,7 +2136,7 @@ deps = {
'packages': [
{
'package': 'chromium/third_party/android_deps/libs/org_robolectric_robolectric',
- 'version': 'version:2@4.10.3.cr1',
+ 'version': 'version:2@4.11.1.cr1',
},
],
'condition': 'checkout_android',
@@ -2175,7 +2147,7 @@ deps = {
'packages': [
{
'package': 'chromium/third_party/android_deps/libs/org_robolectric_sandbox',
- 'version': 'version:2@4.10.3.cr1',
+ 'version': 'version:2@4.11.1.cr1',
},
],
'condition': 'checkout_android',
@@ -2186,7 +2158,7 @@ deps = {
'packages': [
{
'package': 'chromium/third_party/android_deps/libs/org_robolectric_shadowapi',
- 'version': 'version:2@4.10.3.cr1',
+ 'version': 'version:2@4.11.1.cr1',
},
],
'condition': 'checkout_android',
@@ -2197,7 +2169,18 @@ deps = {
'packages': [
{
'package': 'chromium/third_party/android_deps/libs/org_robolectric_shadows_framework',
- 'version': 'version:2@4.10.3.cr1',
+ 'version': 'version:2@4.11.1.cr1',
+ },
+ ],
+ 'condition': 'checkout_android',
+ 'dep_type': 'cipd',
+ },
+
+ 'src/third_party/android_deps/libs/org_robolectric_shadows_versioning': {
+ 'packages': [
+ {
+ 'package': 'chromium/third_party/android_deps/libs/org_robolectric_shadows_versioning',
+ 'version': 'version:2@4.11.1.cr1',
},
],
'condition': 'checkout_android',
@@ -2208,7 +2191,7 @@ deps = {
'packages': [
{
'package': 'chromium/third_party/android_deps/libs/org_robolectric_utils',
- 'version': 'version:2@4.10.3.cr1',
+ 'version': 'version:2@4.11.1.cr1',
},
],
'condition': 'checkout_android',
@@ -2219,7 +2202,7 @@ deps = {
'packages': [
{
'package': 'chromium/third_party/android_deps/libs/org_robolectric_utils_reflector',
- 'version': 'version:2@4.10.3.cr1',
+ 'version': 'version:2@4.11.1.cr1',
},
],
'condition': 'checkout_android',
diff --git a/third_party/libwebrtc/README.moz-ff-commit b/third_party/libwebrtc/README.moz-ff-commit
index 90b20483fb..4a3b8c67ea 100644
--- a/third_party/libwebrtc/README.moz-ff-commit
+++ b/third_party/libwebrtc/README.moz-ff-commit
@@ -26853,3 +26853,840 @@ c3b7a50720
# MOZ_LIBWEBRTC_SRC=/home/mfroman/mozilla/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
# base of lastest vendoring
b0cc68e612
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+56d45b35f1
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+783f1d850e
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+51b93a5417
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+e920073a68
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+9682f4be7d
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+d6bac61b64
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+082cb56ee7
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+e8a2b3c834
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+86f09ae3f6
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+cf2fe18daa
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+3ee8117856
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+d7fb7e4a5f
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+93214073f1
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+7d1693f1c5
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+766e658665
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+efeeba0864
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+554f7db01c
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+4f4ae8a8f9
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+8039cdbe48
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+b78e6a9305
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+a6ce338a2c
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+166111da62
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+7e21b0ca9d
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+9aa115358e
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+78f905e5cc
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+b29ff000da
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+779c9dede9
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+c68da75d04
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+414ffac4b7
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+23cecc1d43
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+d2098933e1
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+37f5172f6e
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+c63120a092
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+4fd1cc70da
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+97f3fb08a0
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+2931ddd2e9
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+4be5927dc7
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+37df1c07b2
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+386873b7cc
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+7fbcc8cef7
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+99ea7c3eaa
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+2bb48727a0
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+24f1ff77e5
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+9272771418
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+0873faae00
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+2b58ec2938
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+0a33589db1
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+be04c98d64
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+eba274a63b
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+5dbfe9621a
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+7946be7429
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+bd523afd3a
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+e925db88c1
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+03457f6891
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+03bc3a0fa6
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+795a2ca30f
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+ad9c4773f4
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+644025c51f
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+4397482d71
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+8ed4b4d314
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+3e436d4ee4
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+80056062f5
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+977b56c9e9
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+916ec25eed
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+bd396fdffa
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+ce627d4f44
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+d3414d9688
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+534374ad57
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+0ece6706c8
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+698e0bec17
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+36ed560339
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+3ea9fc4cd8
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+2d43014acb
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+be23ea4bb9
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+fa4d7c92b7
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+e567d8a112
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+6324919690
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+283a5fd7ec
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+3e3881ae3c
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+cc7861afb3
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+32052ed002
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+176eda9019
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+7d62fe5702
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+2baa7ae9e0
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+2d43ab8508
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+b237f1be56
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+1b4a91d8bb
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+dd45ab650e
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+f52faf2114
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+93ec8048b2
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+369111bf30
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+0cb9b28e5b
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+9c69c4625b
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+d28b6fdf49
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+5820a7f6f6
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+82cbbcc179
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+dfe62c8ad4
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+9f0d10b1f4
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+a6544377bc
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+393cc11ca1
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+9c91e48599
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+d431156c0e
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+e75cd0c704
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+9305b108bd
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+2a33e0ae84
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+8a99fdaed5
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+e76f837c8d
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+5a6f022c93
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+7d2e616968
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+f268afd791
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+1ae700a923
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+59eced6244
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+26e5a82ec7
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+6f4e34472e
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+25c2344b06
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+d0ce0a69aa
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+0552f8f7f6
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+86944c40b3
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+117d847901
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+eb6106e9d0
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+8b54e37cac
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+4ac371883e
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+02ce5887b6
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+ebc4d3edd5
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+12c502428d
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+9dac048f17
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+ad44ceb52e
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+4cc7ff65c0
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+68ea746993
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+9c5ea7e64c
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+6e8e4c43a5
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+75a3ba216e
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+e5251ab6a8
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+0c501a1302
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+72defe459b
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+b6d69708fe
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+496893e89e
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+d07900c848
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+db1d4281d4
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+cf2e08b798
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+20724ae1b7
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+6e956053b7
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+988179a4c3
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+4e1e5eae9e
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+0967247662
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+e35daf2978
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+35d3656ec6
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+ee80ad28c5
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+1568f1b133
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+7e6315a619
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+f0907c6f5b
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+2d86b258e0
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+c09cc4f961
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+f124572ec0
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+136d3b0df3
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+a9d497b52d
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+0418e55761
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+8f36855c6c
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+a37b29f3a9
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+f7cdcbd477
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+c4f8eeb213
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+15b12dc96c
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+8acabc7039
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+6f72008cd6
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+a39206020c
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+baaedc624e
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+f5ea66eea6
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+572502c2ab
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+2f7512819b
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+502afbf510
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+3a15ba6fbf
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+7dd6ea234d
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+f1df16ceea
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+d0269937a0
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+08d81bd4ee
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+0b78094234
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+14630a7e37
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+e7b48a1e80
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+0322493aed
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+518e181ab5
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+8dea465dfd
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+28ea9ba80d
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+254e23071c
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+84c016a024
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+23ac2c0133
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+2e3a2a8392
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+8c0f55857e
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+b803e851fc
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+15c04f98e9
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+7a841ce116
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+01859d41bf
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+e0ec125dae
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+cd42e347c1
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+5116ad58b1
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+211daadb66
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+2561dd307e
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+92fcb56797
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+b65913024f
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+62aa821c2d
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+5c9caa310b
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+1a5d474825
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+d0f0f38f72
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+264547d084
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+f921d25320
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+9fdceb80b5
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+a1a05e259d
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+50a238fbd4
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+abc5066bd9
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+fa1e7d2bae
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+7eaa9dc170
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+9d9d03b3f9
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+b202bc1db2
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+357947f2f0
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+3c5850148e
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+680f103baa
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+db329edf40
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+4b196371ab
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+75aa7e94dd
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+702820d087
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+2798c2bfe6
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+09b2fb65da
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+8490273226
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+69d1d3ec40
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+bac9ed1dcd
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+6417e7b721
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+f99c355a75
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+96e14c82b9
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+526187708d
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+ad3e66138d
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+49c35d377b
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+2e3152654a
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+fc60c7836f
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+1a82d31cb5
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+ee46340054
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+1131f24d2b
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+c534a94bf2
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+8eeb8facb9
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+7b4b29a13f
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+59d0b8de33
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+13834cfacd
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+3bdb49b483
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+4a2ea71a0f
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+a5c8ee1672
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+530b243a1d
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+1939c43355
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+f665f7faf4
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+924f716b7a
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+24510d43dc
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+b506d68f2a
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+c0eac979ca
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+3a530abb0e
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+3ff90f3580
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+c03d8b6cf3
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+034120ea70
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+cd1bd57f5f
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+5b11df789f
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+8f530e8d78
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+ad631f0d35
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+09aa812968
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+2ca1d0f809
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+032805068c
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+1682a7f411
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+623bcd7daa
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+be02328f0e
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+c925f50c1c
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+8f16ce98c2
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+fe66dda733
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+9abc4865a4
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+7b5741c94d
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+c176175f01
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+9a2e32b9f2
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+c93f4f98a5
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+ece5cb8371
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+004a624023
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+9f1e1925f3
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+3df661f798
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+0f741da200
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+79ba9b0b2b
+# MOZ_LIBWEBRTC_SRC=/Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc MOZ_LIBWEBRTC_BRANCH=mozpatches bash dom/media/webrtc/third_party_build/fast-forward-libwebrtc.sh
+# base of lastest vendoring
+6713461a2f
diff --git a/third_party/libwebrtc/README.mozilla b/third_party/libwebrtc/README.mozilla
index 40802a149a..655cb25566 100644
--- a/third_party/libwebrtc/README.mozilla
+++ b/third_party/libwebrtc/README.mozilla
@@ -17926,3 +17926,561 @@ libwebrtc updated from /home/mfroman/mozilla/elm/.moz-fast-forward/moz-libwebrtc
libwebrtc updated from /home/mfroman/mozilla/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-01-03T19:00:15.797265.
# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /home/mfroman/mozilla/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
libwebrtc updated from /home/mfroman/mozilla/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-01-03T19:01:15.317447.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-02T19:44:55.905747.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-02T19:46:00.218563.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-02T19:47:00.554472.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-02T19:48:03.058947.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-02T19:49:07.378452.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-02T19:50:11.223493.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-02T20:14:48.156793.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-02T22:51:20.902176.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-02T22:53:06.976918.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-02T22:54:08.744534.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-02T22:55:13.713193.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-02T22:56:13.179594.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-02T22:57:15.505232.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-02T22:58:48.903961.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-02T22:59:54.059123.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-02T23:00:58.984053.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-02T23:19:35.877061.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-02T23:21:14.008453.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-07T16:54:34.731867.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-07T17:14:13.319492.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-07T17:15:43.952896.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-07T17:16:46.383099.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-07T17:18:16.542611.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-07T17:19:51.976286.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-08T14:05:27.919737.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-08T14:07:09.764953.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T20:30:40.483326.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T20:34:37.811983.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T20:35:42.451804.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T20:36:45.776017.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T21:03:27.507278.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T21:04:33.199342.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T21:06:12.178279.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T21:07:47.462618.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T21:09:24.487307.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T21:10:28.051579.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T21:11:34.384700.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T21:12:42.269503.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T21:13:48.443983.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T21:14:54.098587.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T21:15:57.458981.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T21:17:02.899884.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T21:18:04.109660.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T21:19:07.119444.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T21:20:07.820590.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T21:21:12.334777.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T21:22:21.225889.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T21:23:25.842835.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T21:24:30.353108.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T21:25:34.136165.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T21:26:41.125388.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T21:27:46.210328.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T21:28:51.881032.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T21:29:56.850952.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T21:31:07.072922.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T21:32:14.581346.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T21:33:15.921734.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T21:34:20.156659.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T21:35:25.608669.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T21:37:06.230592.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T21:38:10.130917.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T21:39:21.184661.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T21:40:34.458900.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T21:41:44.885527.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T21:42:48.110996.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T21:44:25.982264.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T21:45:26.028417.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T21:46:27.486850.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T21:47:30.570708.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T21:48:35.069291.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T21:50:12.044318.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T21:51:15.757430.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T21:52:22.840516.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T21:53:24.281023.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T21:54:29.104315.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T21:55:50.315392.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T21:56:56.308309.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T21:57:59.268469.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T21:59:01.465622.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T22:00:26.228912.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T22:01:31.179251.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T22:02:38.024425.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T22:03:53.673864.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T22:05:35.448595.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T22:06:38.082845.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T22:08:16.461695.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T22:09:19.597718.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T22:10:23.082366.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T22:11:28.732540.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T22:12:37.369857.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T22:13:41.929042.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T22:14:46.743557.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T22:16:02.359613.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T22:17:07.085453.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T22:18:12.974920.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T22:19:15.818173.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T22:20:57.910033.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T22:22:01.389791.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T22:23:07.163864.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T22:24:13.040228.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T22:25:16.187893.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T22:26:23.021272.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T22:28:04.537226.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T22:29:16.110238.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T22:30:22.802688.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T22:31:27.080327.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T22:32:31.787228.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T22:33:35.803190.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T22:34:43.250760.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T22:35:51.647400.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T22:56:38.527287.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T22:57:47.725151.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T22:58:50.386708.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T23:00:26.884209.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T23:01:28.874686.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T23:02:30.831910.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T23:03:34.052591.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T23:04:38.490163.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-09T23:05:40.553204.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T16:01:06.679978.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T16:02:16.128634.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T16:03:23.311677.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T16:05:01.742803.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T16:06:04.737338.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T16:07:45.746730.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T17:22:51.975611.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T17:23:52.833620.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T17:24:56.715603.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T17:26:00.310209.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T17:27:00.594852.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T17:28:03.070897.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T17:29:02.405355.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T17:30:04.659375.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T17:31:05.384516.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T17:32:08.881315.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T17:33:18.252033.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T17:34:21.783122.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T17:35:24.498277.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T17:37:03.791567.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T17:38:09.533883.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T17:39:48.752393.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T17:50:30.621653.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T17:51:50.774808.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T17:52:51.219018.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T17:53:53.946569.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T17:55:00.777858.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T17:56:07.745554.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T17:57:12.151550.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T17:58:18.944762.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T18:05:57.939568.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T18:07:34.720882.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T18:08:42.041512.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T18:10:21.702355.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T18:11:23.739544.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T18:12:24.626609.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T18:13:21.737726.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T18:14:23.269926.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T18:15:22.882396.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T18:16:21.928697.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T18:17:23.632655.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T18:18:26.350293.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T18:19:26.456307.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T18:20:26.466246.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T18:21:25.413538.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T18:22:26.115572.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T18:23:25.913622.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T18:24:27.037421.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T18:25:28.464175.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T18:26:34.740807.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T18:27:33.896142.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T18:28:34.845082.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T18:29:41.616614.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T18:30:45.923126.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T18:31:48.931338.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T18:32:48.261373.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T18:33:49.172661.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T18:34:51.240146.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T18:36:23.909708.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T18:37:57.381599.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T18:46:32.922731.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T18:47:32.390920.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T18:48:31.858797.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T18:49:37.383099.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T18:50:57.261186.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T18:52:03.681372.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T18:53:03.746754.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T18:54:03.690925.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T18:55:05.072694.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T18:56:34.420581.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T18:58:04.932566.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T18:59:05.555881.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T19:00:02.965760.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T19:01:14.107916.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T19:02:15.144558.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T19:03:19.471572.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T19:04:23.269430.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T19:05:24.629526.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T19:06:26.398447.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T19:07:28.902297.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T19:08:29.177907.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T19:09:30.301669.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T19:10:29.786641.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T19:11:30.859152.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T19:12:33.101461.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T19:14:05.732783.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T19:15:36.859926.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T19:16:39.272510.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T19:17:39.373346.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T19:18:41.661121.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T19:19:47.115457.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T19:21:17.270156.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T19:22:16.336640.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T19:23:28.993582.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T19:24:30.970337.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T21:32:08.538342.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T21:33:42.399359.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T21:34:50.675233.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T21:35:52.943079.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T21:37:26.872946.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T21:38:58.467500.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T21:40:00.943671.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T21:41:03.279379.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T21:42:04.436699.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T21:43:07.535471.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T21:44:07.891957.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T21:45:08.249396.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T21:46:40.800251.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T21:47:46.308158.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T21:48:48.273644.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T21:49:51.183686.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T21:50:54.851520.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T21:51:59.267702.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T21:53:03.386824.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T21:54:35.846993.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T21:55:42.595744.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T21:56:42.806910.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T21:57:42.568386.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T21:58:45.401496.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T21:59:49.225676.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T22:01:22.077332.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T22:02:25.351470.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T22:03:56.958382.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T22:05:26.681111.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T22:06:32.718403.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T22:08:07.441258.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T22:09:15.383300.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T22:10:18.957781.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T22:11:21.604091.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T22:33:14.259514.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T22:34:15.688491.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T22:35:19.870407.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T22:36:24.337450.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T22:37:27.076521.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T22:38:59.734968.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T22:40:03.492276.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T22:41:07.365560.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T22:42:10.815028.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T22:43:14.914049.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T22:44:20.092341.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T22:45:21.888312.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T22:46:50.576297.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T22:47:52.285966.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T22:48:55.690166.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T22:50:29.893614.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T22:51:31.146312.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T22:52:35.581207.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T22:53:42.375552.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T22:55:15.720276.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T22:56:16.127843.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T22:57:51.742036.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T22:58:51.252021.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T22:59:52.445909.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T23:01:26.358981.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T23:02:30.081484.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-10T23:04:03.473809.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-11T17:10:57.292037.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-11T17:12:06.916591.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-12T16:42:26.420193.
+# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
+libwebrtc updated from /Users/jan-ivar/moz/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2024-02-12T16:43:32.839122.
diff --git a/third_party/libwebrtc/api/BUILD.gn b/third_party/libwebrtc/api/BUILD.gn
index d2b7f06c87..10a4c8c95f 100644
--- a/third_party/libwebrtc/api/BUILD.gn
+++ b/third_party/libwebrtc/api/BUILD.gn
@@ -35,21 +35,62 @@ rtc_source_set("callfactory_api") {
]
}
+rtc_source_set("enable_media") {
+ visibility = [ "*" ]
+ sources = [
+ "enable_media.cc",
+ "enable_media.h",
+ ]
+ deps = [
+ ":libjingle_peerconnection_api",
+ "../call",
+ "../media:rtc_audio_video",
+ "../pc:media_factory",
+ "../rtc_base/system:rtc_export",
+ "environment",
+ ]
+ if (build_with_mozilla) {
+ deps -= [ "../pc:media_factory" ]
+ }
+}
+
+rtc_source_set("enable_media_with_defaults") {
+ visibility = [ "*" ]
+ allow_poison = [
+ "audio_codecs",
+ "environment_construction",
+ "software_video_codecs",
+ ]
+ sources = [
+ "enable_media_with_defaults.cc",
+ "enable_media_with_defaults.h",
+ ]
+ deps = [
+ ":enable_media",
+ ":libjingle_peerconnection_api",
+ "../modules/audio_processing:api",
+ "../rtc_base/system:rtc_export",
+ "audio_codecs:builtin_audio_decoder_factory",
+ "audio_codecs:builtin_audio_encoder_factory",
+ "task_queue:default_task_queue_factory",
+ "video_codecs:builtin_video_decoder_factory",
+ "video_codecs:builtin_video_encoder_factory",
+ ]
+}
+
if (!build_with_chromium && !build_with_mozilla) {
rtc_library("create_peerconnection_factory") {
visibility = [ "*" ]
- allow_poison = [ "default_task_queue" ]
+ allow_poison = [ "environment_construction" ]
sources = [
"create_peerconnection_factory.cc",
"create_peerconnection_factory.h",
]
deps = [
- ":callfactory_api",
+ ":enable_media",
":libjingle_peerconnection_api",
":scoped_refptr",
"../api/rtc_event_log:rtc_event_log_factory",
- "../media:rtc_audio_video",
- "../media:rtc_media_base",
"../modules/audio_device:audio_device_api",
"../modules/audio_processing:api",
"../pc:peer_connection_factory",
@@ -127,13 +168,14 @@ rtc_library("media_stream_interface") {
deps = [
":audio_options_api",
":make_ref_counted",
+ ":ref_count",
+ ":ref_count",
":rtp_parameters",
":scoped_refptr",
":sequence_checker",
":video_track_source_constraints",
"../modules/audio_processing:audio_processing_statistics",
"../rtc_base:checks",
- "../rtc_base:refcount",
"../rtc_base/system:no_unique_address",
"../rtc_base/system:rtc_export",
"video:recordable_encoded_frame",
@@ -181,9 +223,9 @@ rtc_source_set("ice_transport_interface") {
deps = [
":async_dns_resolver",
":packet_socket_factory",
+ ":ref_count",
":rtc_error",
":scoped_refptr",
- "../rtc_base:refcount",
"rtc_event_log:rtc_event_log",
]
}
@@ -201,9 +243,9 @@ if (!build_with_mozilla) {
]
deps = [
":ice_transport_interface",
+ ":ref_count",
":rtc_error",
":scoped_refptr",
- "../rtc_base:refcount",
"../rtc_base:ssl",
"../rtc_base/system:rtc_export",
]
@@ -217,7 +259,7 @@ rtc_library("dtmf_sender_interface") {
sources = [ "dtmf_sender_interface.h" ]
deps = [
":media_stream_interface",
- "../rtc_base:refcount",
+ ":ref_count",
]
}
@@ -237,11 +279,12 @@ if (!build_with_mozilla) {
":dtmf_sender_interface",
":frame_transformer_interface",
":media_stream_interface",
+ ":ref_count",
+ ":rtc_error",
":rtp_parameters",
":rtp_sender_setparameters_callback",
":scoped_refptr",
"../rtc_base:checks",
- "../rtc_base:refcount",
"../rtc_base/system:rtc_export",
"crypto:frame_encryptor_interface",
"video_codecs:video_codecs_api",
@@ -303,7 +346,6 @@ if (!build_with_mozilla) {
":array_view",
":async_dns_resolver",
":audio_options_api",
- ":callfactory_api",
":candidate",
":dtls_transport_interface",
":fec_controller_api",
@@ -317,6 +359,7 @@ if (!build_with_mozilla) {
":network_state_predictor_api",
":packet_socket_factory",
":priority",
+ ":ref_count",
":rtc_error",
":rtc_stats_api",
":rtp_packet_info",
@@ -328,11 +371,11 @@ if (!build_with_mozilla) {
":turn_customizer",
"../call:rtp_interfaces",
"../p2p:rtc_p2p",
+ "../pc:media_factory",
"../rtc_base:copy_on_write_buffer",
"../rtc_base:logging",
"../rtc_base:network",
"../rtc_base:network_constants",
- "../rtc_base:refcount",
"../rtc_base:rtc_certificate_generator",
"../rtc_base:ssl",
"../rtc_base:stringutils",
@@ -387,8 +430,8 @@ rtc_source_set("frame_transformer_interface") {
sources = [ "frame_transformer_interface.h" ]
deps = [
":make_ref_counted",
+ ":ref_count",
":scoped_refptr",
- "../rtc_base:refcount",
"video:encoded_frame",
"video:video_frame_metadata",
]
@@ -414,15 +457,10 @@ rtc_library("rtc_error") {
rtc_source_set("packet_socket_factory") {
visibility = [ "*" ]
- sources = [
- "async_resolver_factory.h",
- "packet_socket_factory.h",
- ]
+ sources = [ "packet_socket_factory.h" ]
deps = [
":async_dns_resolver",
- ":wrapping_async_dns_resolver",
"../rtc_base:async_packet_socket",
- "../rtc_base:async_resolver_interface",
"../rtc_base:proxy_info",
"../rtc_base:socket_address",
"../rtc_base/system:rtc_export",
@@ -440,26 +478,9 @@ rtc_source_set("async_dns_resolver") {
absl_deps = [ "//third_party/abseil-cpp/absl/functional:any_invocable" ]
}
-rtc_source_set("wrapping_async_dns_resolver") {
- visibility = [
- ":*",
- "../p2p:rtc_p2p",
- ]
- sources = [
- "wrapping_async_dns_resolver.cc",
- "wrapping_async_dns_resolver.h",
- ]
- deps = [
- ":async_dns_resolver",
- ":sequence_checker",
- "../rtc_base:async_resolver_interface",
- "../rtc_base:checks",
- "../rtc_base:macromagic",
- "../rtc_base:socket_address",
- "../rtc_base:threading",
- "../rtc_base/third_party/sigslot",
- ]
- absl_deps = [ "//third_party/abseil-cpp/absl/memory" ]
+rtc_source_set("ref_count") {
+ visibility = [ "*" ]
+ sources = [ "ref_count.h" ]
}
rtc_source_set("scoped_refptr") {
@@ -470,7 +491,10 @@ rtc_source_set("scoped_refptr") {
rtc_source_set("make_ref_counted") {
visibility = [ "*" ]
sources = [ "make_ref_counted.h" ]
- deps = [ "../rtc_base:refcount" ]
+ deps = [
+ ":ref_count",
+ "../rtc_base:refcount",
+ ]
}
rtc_source_set("video_quality_analyzer_api") {
@@ -795,6 +819,7 @@ rtc_source_set("rtc_stats_api") {
deps = [
":make_ref_counted",
+ ":ref_count",
":scoped_refptr",
"../api:refcountedbase",
"../rtc_base:checks",
@@ -927,6 +952,7 @@ rtc_source_set("refcountedbase") {
visibility = [ "*" ]
sources = [ "ref_counted_base.h" ]
deps = [
+ ":ref_count",
"../rtc_base:macromagic",
"../rtc_base:refcount",
]
@@ -1052,24 +1078,6 @@ if (rtc_include_tests) {
absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ]
}
- rtc_library("video_codec_stats_api") {
- visibility = [ "*" ]
- testonly = true
- sources = [
- "test/video_codec_stats.cc",
- "test/video_codec_stats.h",
- ]
- deps = [
- "../api/numerics:numerics",
- "../api/units:data_rate",
- "../api/units:data_size",
- "../api/units:frequency",
- "test/metrics:metric",
- "test/metrics:metrics_logger",
- ]
- absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ]
- }
-
rtc_library("videocodec_test_fixture_api") {
visibility = [ "*" ]
testonly = true
@@ -1081,23 +1089,6 @@ if (rtc_include_tests) {
]
}
- rtc_library("video_codec_tester_api") {
- visibility = [ "*" ]
- testonly = true
- sources = [ "test/video_codec_tester.h" ]
- deps = [
- ":video_codec_stats_api",
- "../modules/video_coding/svc:scalability_mode_util",
- "video:encoded_image",
- "video:resolution",
- "video:video_frame",
- ]
- absl_deps = [
- "//third_party/abseil-cpp/absl/functional:any_invocable",
- "//third_party/abseil-cpp/absl/types:optional",
- ]
- }
-
rtc_library("create_videocodec_test_fixture_api") {
visibility = [ "*" ]
testonly = true
@@ -1113,19 +1104,6 @@ if (rtc_include_tests) {
]
}
- rtc_library("create_video_codec_tester_api") {
- visibility = [ "*" ]
- testonly = true
- sources = [
- "test/create_video_codec_tester.cc",
- "test/create_video_codec_tester.h",
- ]
- deps = [
- ":video_codec_tester_api",
- "../modules/video_coding:video_codec_tester",
- ]
- }
-
rtc_source_set("mock_audio_mixer") {
visibility = [ "*" ]
testonly = true
@@ -1227,6 +1205,7 @@ if (rtc_include_tests) {
":array_view",
":libjingle_peerconnection_api",
":make_ref_counted",
+ ":ref_count",
":rtp_parameters",
"../rtc_base:checks",
"../rtc_base:refcount",
@@ -1280,6 +1259,7 @@ if (rtc_include_tests) {
deps = [
":libjingle_peerconnection_api",
+ ":ref_count",
"../api:scoped_refptr",
"../rtc_base:refcount",
"../test:test_support",
@@ -1307,6 +1287,18 @@ if (rtc_include_tests) {
]
}
+ rtc_source_set("mock_transformable_frame") {
+ visibility = [ "*" ]
+ testonly = true
+ sources = [ "test/mock_transformable_frame.h" ]
+ deps = [
+ ":array_view",
+ ":frame_transformer_interface",
+ "../test:test_support",
+ "units:timestamp",
+ ]
+ }
+
rtc_source_set("mock_async_dns_resolver") {
visibility = [ "*" ]
testonly = true
@@ -1420,6 +1412,7 @@ if (rtc_include_tests) {
sources = [ "test/mock_video_track.h" ]
deps = [
+ ":ref_count",
"../api:media_stream_interface",
"../api:scoped_refptr",
"../rtc_base:refcount",
@@ -1436,13 +1429,19 @@ if (rtc_include_tests) {
]
deps = [
- ":callfactory_api",
+ ":enable_media_with_defaults",
+ ":libjingle_peerconnection_api",
":time_controller",
"../call",
"../call:call_interfaces",
"../call:rtp_interfaces",
+ "../pc:media_factory",
+ "../rtc_base:checks",
+ "../system_wrappers",
"../test/time_controller",
+ "environment",
]
+ absl_deps = [ "//third_party/abseil-cpp/absl/base:nullability" ]
}
rtc_library("rtc_api_unittests") {
@@ -1492,6 +1491,7 @@ if (rtc_include_tests) {
"../test:fileutils",
"../test:rtc_expect_death",
"../test:test_support",
+ "environment:environment_unittests",
"task_queue:task_queue_default_factory_unittests",
"test/pclf:media_configuration",
"test/video:video_frame_writer",
@@ -1531,6 +1531,7 @@ if (rtc_include_tests) {
":mock_rtp",
":mock_session_description_interface",
":mock_transformable_audio_frame",
+ ":mock_transformable_frame",
":mock_transformable_video_frame",
":mock_video_bitrate_allocator",
":mock_video_bitrate_allocator_factory",
@@ -1600,10 +1601,10 @@ rtc_library("frame_transformer_factory") {
]
deps = [
":frame_transformer_interface",
+ ":ref_count",
":scoped_refptr",
"../audio:audio",
"../modules/rtp_rtcp",
- "../rtc_base:refcount",
"video:encoded_frame",
"video:video_frame_metadata",
]
diff --git a/third_party/libwebrtc/api/DEPS b/third_party/libwebrtc/api/DEPS
index bcfd705741..3a650b6253 100644
--- a/third_party/libwebrtc/api/DEPS
+++ b/third_party/libwebrtc/api/DEPS
@@ -81,7 +81,6 @@ specific_include_rules = {
"data_channel_interface\.h": [
"+rtc_base/copy_on_write_buffer.h",
- "+rtc_base/ref_count.h",
],
"data_channel_transport_interface\.h": [
@@ -89,33 +88,15 @@ specific_include_rules = {
],
"dtls_transport_interface\.h": [
- "+rtc_base/ref_count.h",
"+rtc_base/ssl_certificate.h",
],
- "dtmf_sender_interface\.h": [
- "+rtc_base/ref_count.h",
- ],
-
"fec_controller\.h": [
"+modules/include/module_fec_types.h",
],
- "frame_transformer_interface\.h": [
- "+rtc_base/ref_count.h",
- ],
-
- "ice_transport_interface\.h": [
- "+rtc_base/ref_count.h",
- ],
-
- "jsep\.h": [
- "+rtc_base/ref_count.h",
- ],
-
"media_stream_interface\.h": [
"+modules/audio_processing/include/audio_processing_statistics.h",
- "+rtc_base/ref_count.h",
],
"packet_socket_factory\.h": [
@@ -132,7 +113,6 @@ specific_include_rules = {
"+rtc_base/network.h",
"+rtc_base/network_constants.h",
"+rtc_base/network_monitor_factory.h",
- "+rtc_base/ref_count.h",
"+rtc_base/rtc_certificate.h",
"+rtc_base/rtc_certificate_generator.h",
"+rtc_base/socket_address.h",
@@ -148,7 +128,6 @@ specific_include_rules = {
],
"ref_counted_base\.h": [
- "+rtc_base/ref_count.h",
"+rtc_base/ref_counter.h",
],
@@ -159,74 +138,19 @@ specific_include_rules = {
# For private member and constructor.
"+rtc_base/system/file_wrapper.h",
],
- "rtp_receiver_interface\.h": [
- "+rtc_base/ref_count.h",
- ],
-
- "rtp_sender_interface\.h": [
- "+rtc_base/ref_count.h",
- ],
-
- "rtp_transceiver_interface\.h": [
- "+rtc_base/ref_count.h",
- ],
-
- "sctp_transport_interface\.h": [
- "+rtc_base/ref_count.h",
- ],
-
- "set_local_description_observer_interface\.h": [
- "+rtc_base/ref_count.h",
- ],
- "set_remote_description_observer_interface\.h": [
- "+rtc_base/ref_count.h",
- ],
"legacy_stats_types\.h": [
- "+rtc_base/ref_count.h",
"+rtc_base/thread_checker.h",
],
- "uma_metrics\.h": [
- "+rtc_base/ref_count.h",
- ],
-
- "audio_mixer\.h": [
- "+rtc_base/ref_count.h",
- ],
-
"audio_decoder\.h": [
"+rtc_base/buffer.h",
],
- "audio_decoder_factory\.h": [
- "+rtc_base/ref_count.h",
- ],
-
"audio_encoder\.h": [
"+rtc_base/buffer.h",
],
- "audio_encoder_factory\.h": [
- "+rtc_base/ref_count.h",
- ],
-
- "frame_decryptor_interface\.h": [
- "+rtc_base/ref_count.h",
- ],
-
- "frame_encryptor_interface\.h": [
- "+rtc_base/ref_count.h",
- ],
-
- "rtc_stats_collector_callback\.h": [
- "+rtc_base/ref_count.h",
- ],
-
- "rtc_stats_report\.h": [
- "+rtc_base/ref_count.h",
- ],
-
"audioproc_float\.h": [
"+modules/audio_processing/include/audio_processing.h",
],
@@ -279,10 +203,6 @@ specific_include_rules = {
"+modules/video_coding/include/video_codec_interface.h"
],
- "video_encoder_config\.h": [
- "+rtc_base/ref_count.h",
- ],
-
"sequence_checker\.h": [
"+rtc_base/synchronization/sequence_checker_internal.h",
"+rtc_base/thread_annotations.h",
diff --git a/third_party/libwebrtc/api/adaptation/resource_adaptation_api_gn/moz.build b/third_party/libwebrtc/api/adaptation/resource_adaptation_api_gn/moz.build
index c06f943d1b..b0671b5d4a 100644
--- a/third_party/libwebrtc/api/adaptation/resource_adaptation_api_gn/moz.build
+++ b/third_party/libwebrtc/api/adaptation/resource_adaptation_api_gn/moz.build
@@ -188,7 +188,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -198,10 +197,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/array_view_gn/moz.build b/third_party/libwebrtc/api/array_view_gn/moz.build
index e89811c36f..e090bcdfc5 100644
--- a/third_party/libwebrtc/api/array_view_gn/moz.build
+++ b/third_party/libwebrtc/api/array_view_gn/moz.build
@@ -180,16 +180,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/async_dns_resolver_gn/moz.build b/third_party/libwebrtc/api/async_dns_resolver_gn/moz.build
index 5e31fd1734..6a2d55b776 100644
--- a/third_party/libwebrtc/api/async_dns_resolver_gn/moz.build
+++ b/third_party/libwebrtc/api/async_dns_resolver_gn/moz.build
@@ -188,16 +188,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/async_resolver_factory.h b/third_party/libwebrtc/api/async_resolver_factory.h
deleted file mode 100644
index 997fe5ce57..0000000000
--- a/third_party/libwebrtc/api/async_resolver_factory.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright 2018 The WebRTC Project Authors. All rights reserved.
- *
- * Use of this source code is governed by a BSD-style license
- * that can be found in the LICENSE file in the root of the source
- * tree. An additional intellectual property rights grant can be found
- * in the file PATENTS. All contributing project authors may
- * be found in the AUTHORS file in the root of the source tree.
- */
-
-#ifndef API_ASYNC_RESOLVER_FACTORY_H_
-#define API_ASYNC_RESOLVER_FACTORY_H_
-
-#include "rtc_base/async_resolver_interface.h"
-
-namespace webrtc {
-
-// An abstract factory for creating AsyncResolverInterfaces. This allows
-// client applications to provide WebRTC with their own mechanism for
-// performing DNS resolution.
-// TODO(bugs.webrtc.org/12598): Deprecate and remove.
-class [[deprecated("Use AsyncDnsResolverFactory")]] AsyncResolverFactory {
- public:
- AsyncResolverFactory() = default;
- virtual ~AsyncResolverFactory() = default;
-
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
- // The caller should call Destroy on the returned object to delete it.
- virtual rtc::AsyncResolverInterface* Create() = 0;
-#pragma clang diagnostic pop
-};
-
-} // namespace webrtc
-
-#endif // API_ASYNC_RESOLVER_FACTORY_H_
diff --git a/third_party/libwebrtc/api/audio/aec3_config_gn/moz.build b/third_party/libwebrtc/api/audio/aec3_config_gn/moz.build
index 4b96910919..ee6df1d36f 100644
--- a/third_party/libwebrtc/api/audio/aec3_config_gn/moz.build
+++ b/third_party/libwebrtc/api/audio/aec3_config_gn/moz.build
@@ -188,7 +188,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -198,10 +197,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/audio/aec3_factory_gn/moz.build b/third_party/libwebrtc/api/audio/aec3_factory_gn/moz.build
index de044719cc..0c3ce503ad 100644
--- a/third_party/libwebrtc/api/audio/aec3_factory_gn/moz.build
+++ b/third_party/libwebrtc/api/audio/aec3_factory_gn/moz.build
@@ -200,7 +200,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -210,10 +209,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/audio/audio_frame_api_gn/moz.build b/third_party/libwebrtc/api/audio/audio_frame_api_gn/moz.build
index ca2c90ecfa..1317ad89b0 100644
--- a/third_party/libwebrtc/api/audio/audio_frame_api_gn/moz.build
+++ b/third_party/libwebrtc/api/audio/audio_frame_api_gn/moz.build
@@ -196,7 +196,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -206,10 +205,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/audio/audio_frame_processor_gn/moz.build b/third_party/libwebrtc/api/audio/audio_frame_processor_gn/moz.build
index 87847bb863..e04682bee3 100644
--- a/third_party/libwebrtc/api/audio/audio_frame_processor_gn/moz.build
+++ b/third_party/libwebrtc/api/audio/audio_frame_processor_gn/moz.build
@@ -176,16 +176,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/audio/audio_mixer_api_gn/moz.build b/third_party/libwebrtc/api/audio/audio_mixer_api_gn/moz.build
index 27baf1a796..c74637daa3 100644
--- a/third_party/libwebrtc/api/audio/audio_mixer_api_gn/moz.build
+++ b/third_party/libwebrtc/api/audio/audio_mixer_api_gn/moz.build
@@ -187,16 +187,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/audio/echo_control_gn/moz.build b/third_party/libwebrtc/api/audio/echo_control_gn/moz.build
index 6a5ce44f46..eb9e4ec669 100644
--- a/third_party/libwebrtc/api/audio/echo_control_gn/moz.build
+++ b/third_party/libwebrtc/api/audio/echo_control_gn/moz.build
@@ -180,16 +180,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/audio_codecs/BUILD.gn b/third_party/libwebrtc/api/audio_codecs/BUILD.gn
index 82ed31a5da..158ab74cce 100644
--- a/third_party/libwebrtc/api/audio_codecs/BUILD.gn
+++ b/third_party/libwebrtc/api/audio_codecs/BUILD.gn
@@ -32,6 +32,7 @@ rtc_library("audio_codecs_api") {
"..:array_view",
"..:bitrate_allocation",
"..:make_ref_counted",
+ "..:ref_count",
"..:scoped_refptr",
"../../api:field_trials_view",
"../../rtc_base:buffer",
diff --git a/third_party/libwebrtc/api/audio_codecs/L16/audio_decoder_L16_gn/moz.build b/third_party/libwebrtc/api/audio_codecs/L16/audio_decoder_L16_gn/moz.build
index 9ab87e6a0e..9dcc9826de 100644
--- a/third_party/libwebrtc/api/audio_codecs/L16/audio_decoder_L16_gn/moz.build
+++ b/third_party/libwebrtc/api/audio_codecs/L16/audio_decoder_L16_gn/moz.build
@@ -195,7 +195,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -205,10 +204,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/audio_codecs/L16/audio_encoder_L16_gn/moz.build b/third_party/libwebrtc/api/audio_codecs/L16/audio_encoder_L16_gn/moz.build
index 0efa8c28a2..b5c7fe8b37 100644
--- a/third_party/libwebrtc/api/audio_codecs/L16/audio_encoder_L16_gn/moz.build
+++ b/third_party/libwebrtc/api/audio_codecs/L16/audio_encoder_L16_gn/moz.build
@@ -195,7 +195,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -205,10 +204,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/audio_codecs/audio_codecs_api_gn/moz.build b/third_party/libwebrtc/api/audio_codecs/audio_codecs_api_gn/moz.build
index 6c8b6b3b2b..8eaebf875c 100644
--- a/third_party/libwebrtc/api/audio_codecs/audio_codecs_api_gn/moz.build
+++ b/third_party/libwebrtc/api/audio_codecs/audio_codecs_api_gn/moz.build
@@ -198,7 +198,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -208,10 +207,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/audio_codecs/audio_decoder_factory.h b/third_party/libwebrtc/api/audio_codecs/audio_decoder_factory.h
index 2811f6704b..19b921a97f 100644
--- a/third_party/libwebrtc/api/audio_codecs/audio_decoder_factory.h
+++ b/third_party/libwebrtc/api/audio_codecs/audio_decoder_factory.h
@@ -18,12 +18,12 @@
#include "api/audio_codecs/audio_codec_pair_id.h"
#include "api/audio_codecs/audio_decoder.h"
#include "api/audio_codecs/audio_format.h"
-#include "rtc_base/ref_count.h"
+#include "api/ref_count.h"
namespace webrtc {
// A factory that creates AudioDecoders.
-class AudioDecoderFactory : public rtc::RefCountInterface {
+class AudioDecoderFactory : public RefCountInterface {
public:
virtual std::vector<AudioCodecSpec> GetSupportedDecoders() = 0;
diff --git a/third_party/libwebrtc/api/audio_codecs/builtin_audio_decoder_factory_gn/moz.build b/third_party/libwebrtc/api/audio_codecs/builtin_audio_decoder_factory_gn/moz.build
index f64e3e3340..3ce5ad2d5d 100644
--- a/third_party/libwebrtc/api/audio_codecs/builtin_audio_decoder_factory_gn/moz.build
+++ b/third_party/libwebrtc/api/audio_codecs/builtin_audio_decoder_factory_gn/moz.build
@@ -201,7 +201,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -211,10 +210,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/audio_codecs/builtin_audio_encoder_factory_gn/moz.build b/third_party/libwebrtc/api/audio_codecs/builtin_audio_encoder_factory_gn/moz.build
index 6965c4298f..e8acc1186e 100644
--- a/third_party/libwebrtc/api/audio_codecs/builtin_audio_encoder_factory_gn/moz.build
+++ b/third_party/libwebrtc/api/audio_codecs/builtin_audio_encoder_factory_gn/moz.build
@@ -201,7 +201,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -211,10 +210,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/audio_codecs/g711/audio_decoder_g711_gn/moz.build b/third_party/libwebrtc/api/audio_codecs/g711/audio_decoder_g711_gn/moz.build
index e0dcf8f032..2dbe0fd6bc 100644
--- a/third_party/libwebrtc/api/audio_codecs/g711/audio_decoder_g711_gn/moz.build
+++ b/third_party/libwebrtc/api/audio_codecs/g711/audio_decoder_g711_gn/moz.build
@@ -195,7 +195,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -205,10 +204,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/audio_codecs/g711/audio_encoder_g711_gn/moz.build b/third_party/libwebrtc/api/audio_codecs/g711/audio_encoder_g711_gn/moz.build
index 708744cf3b..a1b1f8e9fc 100644
--- a/third_party/libwebrtc/api/audio_codecs/g711/audio_encoder_g711_gn/moz.build
+++ b/third_party/libwebrtc/api/audio_codecs/g711/audio_encoder_g711_gn/moz.build
@@ -195,7 +195,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -205,10 +204,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/audio_codecs/g722/audio_decoder_g722_gn/moz.build b/third_party/libwebrtc/api/audio_codecs/g722/audio_decoder_g722_gn/moz.build
index 4b96ef2068..838977ba10 100644
--- a/third_party/libwebrtc/api/audio_codecs/g722/audio_decoder_g722_gn/moz.build
+++ b/third_party/libwebrtc/api/audio_codecs/g722/audio_decoder_g722_gn/moz.build
@@ -195,7 +195,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -205,10 +204,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/audio_codecs/g722/audio_encoder_g722_config_gn/moz.build b/third_party/libwebrtc/api/audio_codecs/g722/audio_encoder_g722_config_gn/moz.build
index bddf7d5571..2b1c0d220c 100644
--- a/third_party/libwebrtc/api/audio_codecs/g722/audio_encoder_g722_config_gn/moz.build
+++ b/third_party/libwebrtc/api/audio_codecs/g722/audio_encoder_g722_config_gn/moz.build
@@ -187,16 +187,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/audio_codecs/g722/audio_encoder_g722_gn/moz.build b/third_party/libwebrtc/api/audio_codecs/g722/audio_encoder_g722_gn/moz.build
index e35ace4e0a..367caa077f 100644
--- a/third_party/libwebrtc/api/audio_codecs/g722/audio_encoder_g722_gn/moz.build
+++ b/third_party/libwebrtc/api/audio_codecs/g722/audio_encoder_g722_gn/moz.build
@@ -195,7 +195,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -205,10 +204,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/audio_codecs/ilbc/audio_decoder_ilbc_gn/moz.build b/third_party/libwebrtc/api/audio_codecs/ilbc/audio_decoder_ilbc_gn/moz.build
index 123ba8eb1c..b8bf808d3d 100644
--- a/third_party/libwebrtc/api/audio_codecs/ilbc/audio_decoder_ilbc_gn/moz.build
+++ b/third_party/libwebrtc/api/audio_codecs/ilbc/audio_decoder_ilbc_gn/moz.build
@@ -199,7 +199,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -209,10 +208,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/audio_codecs/ilbc/audio_encoder_ilbc_config_gn/moz.build b/third_party/libwebrtc/api/audio_codecs/ilbc/audio_encoder_ilbc_config_gn/moz.build
index 843a9aee3b..909cc8c26f 100644
--- a/third_party/libwebrtc/api/audio_codecs/ilbc/audio_encoder_ilbc_config_gn/moz.build
+++ b/third_party/libwebrtc/api/audio_codecs/ilbc/audio_encoder_ilbc_config_gn/moz.build
@@ -176,16 +176,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/audio_codecs/ilbc/audio_encoder_ilbc_gn/moz.build b/third_party/libwebrtc/api/audio_codecs/ilbc/audio_encoder_ilbc_gn/moz.build
index a01bbe78d5..08b426b9ab 100644
--- a/third_party/libwebrtc/api/audio_codecs/ilbc/audio_encoder_ilbc_gn/moz.build
+++ b/third_party/libwebrtc/api/audio_codecs/ilbc/audio_encoder_ilbc_gn/moz.build
@@ -199,7 +199,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -209,10 +208,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/audio_codecs/opus/audio_decoder_multiopus_gn/moz.build b/third_party/libwebrtc/api/audio_codecs/opus/audio_decoder_multiopus_gn/moz.build
index fec5701696..c36b4324e4 100644
--- a/third_party/libwebrtc/api/audio_codecs/opus/audio_decoder_multiopus_gn/moz.build
+++ b/third_party/libwebrtc/api/audio_codecs/opus/audio_decoder_multiopus_gn/moz.build
@@ -196,7 +196,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -206,10 +205,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/audio_codecs/opus/audio_decoder_opus_config_gn/moz.build b/third_party/libwebrtc/api/audio_codecs/opus/audio_decoder_opus_config_gn/moz.build
index 41887d1871..a9896dd203 100644
--- a/third_party/libwebrtc/api/audio_codecs/opus/audio_decoder_opus_config_gn/moz.build
+++ b/third_party/libwebrtc/api/audio_codecs/opus/audio_decoder_opus_config_gn/moz.build
@@ -187,16 +187,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/audio_codecs/opus/audio_decoder_opus_gn/moz.build b/third_party/libwebrtc/api/audio_codecs/opus/audio_decoder_opus_gn/moz.build
index 9c9bbb415b..7c7ed25cbc 100644
--- a/third_party/libwebrtc/api/audio_codecs/opus/audio_decoder_opus_gn/moz.build
+++ b/third_party/libwebrtc/api/audio_codecs/opus/audio_decoder_opus_gn/moz.build
@@ -200,7 +200,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -210,10 +209,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/audio_codecs/opus/audio_encoder_multiopus_gn/moz.build b/third_party/libwebrtc/api/audio_codecs/opus/audio_encoder_multiopus_gn/moz.build
index ec36454e9f..7a8b6dc2e6 100644
--- a/third_party/libwebrtc/api/audio_codecs/opus/audio_encoder_multiopus_gn/moz.build
+++ b/third_party/libwebrtc/api/audio_codecs/opus/audio_encoder_multiopus_gn/moz.build
@@ -196,7 +196,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -206,10 +205,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/audio_codecs/opus/audio_encoder_opus_config_gn/moz.build b/third_party/libwebrtc/api/audio_codecs/opus/audio_encoder_opus_config_gn/moz.build
index 6c061ce58f..f68dce0d9d 100644
--- a/third_party/libwebrtc/api/audio_codecs/opus/audio_encoder_opus_config_gn/moz.build
+++ b/third_party/libwebrtc/api/audio_codecs/opus/audio_encoder_opus_config_gn/moz.build
@@ -189,7 +189,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -199,10 +198,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/audio_codecs/opus/audio_encoder_opus_gn/moz.build b/third_party/libwebrtc/api/audio_codecs/opus/audio_encoder_opus_gn/moz.build
index b5c0f484ad..f44d8836ef 100644
--- a/third_party/libwebrtc/api/audio_codecs/opus/audio_encoder_opus_gn/moz.build
+++ b/third_party/libwebrtc/api/audio_codecs/opus/audio_encoder_opus_gn/moz.build
@@ -200,7 +200,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -210,10 +209,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/audio_options_api_gn/moz.build b/third_party/libwebrtc/api/audio_options_api_gn/moz.build
index f31b230650..5e169a3ffe 100644
--- a/third_party/libwebrtc/api/audio_options_api_gn/moz.build
+++ b/third_party/libwebrtc/api/audio_options_api_gn/moz.build
@@ -188,7 +188,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -198,10 +197,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/bitrate_allocation_gn/moz.build b/third_party/libwebrtc/api/bitrate_allocation_gn/moz.build
index dcf9b27ca3..530662eb2f 100644
--- a/third_party/libwebrtc/api/bitrate_allocation_gn/moz.build
+++ b/third_party/libwebrtc/api/bitrate_allocation_gn/moz.build
@@ -180,16 +180,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/call/call_factory_interface.h b/third_party/libwebrtc/api/call/call_factory_interface.h
index fde8cba66e..db53d724a6 100644
--- a/third_party/libwebrtc/api/call/call_factory_interface.h
+++ b/third_party/libwebrtc/api/call/call_factory_interface.h
@@ -24,6 +24,9 @@ struct CallConfig;
// This interface exists to allow webrtc to be optionally built without media
// support (i.e., if only being used for data channels). PeerConnectionFactory
// is constructed with a CallFactoryInterface, which may or may not be null.
+// TODO(bugs.webrtc.org/15574): Delete this interface when
+// `PeerConnectionFactoryDependencies::call_factory` is removed in favor of
+// `PeerConnectionFactoryDependencies::media_factory`.
class CallFactoryInterface {
public:
virtual ~CallFactoryInterface() = default;
@@ -31,7 +34,9 @@ class CallFactoryInterface {
virtual std::unique_ptr<Call> CreateCall(const CallConfig& config) = 0;
};
-RTC_EXPORT std::unique_ptr<CallFactoryInterface> CreateCallFactory();
+[[deprecated("bugs.webrtc.org/15574")]] //
+RTC_EXPORT std::unique_ptr<CallFactoryInterface>
+CreateCallFactory();
} // namespace webrtc
diff --git a/third_party/libwebrtc/api/call_api_gn/moz.build b/third_party/libwebrtc/api/call_api_gn/moz.build
index 5c4a7f29a0..5656999ecb 100644
--- a/third_party/libwebrtc/api/call_api_gn/moz.build
+++ b/third_party/libwebrtc/api/call_api_gn/moz.build
@@ -176,16 +176,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/callfactory_api_gn/moz.build b/third_party/libwebrtc/api/callfactory_api_gn/moz.build
index a671079943..157a34ec8e 100644
--- a/third_party/libwebrtc/api/callfactory_api_gn/moz.build
+++ b/third_party/libwebrtc/api/callfactory_api_gn/moz.build
@@ -191,16 +191,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/create_peerconnection_factory.cc b/third_party/libwebrtc/api/create_peerconnection_factory.cc
index b7f9eb7f30..5d3aace05f 100644
--- a/third_party/libwebrtc/api/create_peerconnection_factory.cc
+++ b/third_party/libwebrtc/api/create_peerconnection_factory.cc
@@ -13,14 +13,12 @@
#include <memory>
#include <utility>
-#include "api/call/call_factory_interface.h"
+#include "api/enable_media.h"
#include "api/peer_connection_interface.h"
#include "api/rtc_event_log/rtc_event_log_factory.h"
#include "api/scoped_refptr.h"
#include "api/task_queue/default_task_queue_factory.h"
#include "api/transport/field_trial_based_config.h"
-#include "media/base/media_engine.h"
-#include "media/engine/webrtc_media_engine.h"
#include "modules/audio_device/include/audio_device.h"
#include "modules/audio_processing/include/audio_processing.h"
#include "rtc_base/thread.h"
@@ -38,8 +36,7 @@ rtc::scoped_refptr<PeerConnectionFactoryInterface> CreatePeerConnectionFactory(
std::unique_ptr<VideoDecoderFactory> video_decoder_factory,
rtc::scoped_refptr<AudioMixer> audio_mixer,
rtc::scoped_refptr<AudioProcessing> audio_processing,
- AudioFrameProcessor* audio_frame_processor,
- std::unique_ptr<AudioFrameProcessor> owned_audio_frame_processor,
+ std::unique_ptr<AudioFrameProcessor> audio_frame_processor,
std::unique_ptr<FieldTrialsView> field_trials) {
if (!field_trials) {
field_trials = std::make_unique<webrtc::FieldTrialBasedConfig>();
@@ -51,7 +48,6 @@ rtc::scoped_refptr<PeerConnectionFactoryInterface> CreatePeerConnectionFactory(
dependencies.signaling_thread = signaling_thread;
dependencies.task_queue_factory =
CreateDefaultTaskQueueFactory(field_trials.get());
- dependencies.call_factory = CreateCallFactory();
dependencies.event_log_factory = std::make_unique<RtcEventLogFactory>(
dependencies.task_queue_factory.get());
dependencies.trials = std::move(field_trials);
@@ -60,70 +56,21 @@ rtc::scoped_refptr<PeerConnectionFactoryInterface> CreatePeerConnectionFactory(
// TODO(bugs.webrtc.org/13145): Add an rtc::SocketFactory* argument.
dependencies.socket_factory = network_thread->socketserver();
}
- cricket::MediaEngineDependencies media_dependencies;
- media_dependencies.task_queue_factory = dependencies.task_queue_factory.get();
- media_dependencies.adm = std::move(default_adm);
- media_dependencies.audio_encoder_factory = std::move(audio_encoder_factory);
- media_dependencies.audio_decoder_factory = std::move(audio_decoder_factory);
- if (audio_frame_processor) {
- media_dependencies.audio_frame_processor = audio_frame_processor;
- } else if (owned_audio_frame_processor) {
- media_dependencies.owned_audio_frame_processor =
- std::move(owned_audio_frame_processor);
- }
+ dependencies.adm = std::move(default_adm);
+ dependencies.audio_encoder_factory = std::move(audio_encoder_factory);
+ dependencies.audio_decoder_factory = std::move(audio_decoder_factory);
+ dependencies.audio_frame_processor = std::move(audio_frame_processor);
if (audio_processing) {
- media_dependencies.audio_processing = std::move(audio_processing);
+ dependencies.audio_processing = std::move(audio_processing);
} else {
- media_dependencies.audio_processing = AudioProcessingBuilder().Create();
+ dependencies.audio_processing = AudioProcessingBuilder().Create();
}
- media_dependencies.audio_mixer = std::move(audio_mixer);
- media_dependencies.video_encoder_factory = std::move(video_encoder_factory);
- media_dependencies.video_decoder_factory = std::move(video_decoder_factory);
- media_dependencies.trials = dependencies.trials.get();
- dependencies.media_engine =
- cricket::CreateMediaEngine(std::move(media_dependencies));
+ dependencies.audio_mixer = std::move(audio_mixer);
+ dependencies.video_encoder_factory = std::move(video_encoder_factory);
+ dependencies.video_decoder_factory = std::move(video_decoder_factory);
+ EnableMedia(dependencies);
return CreateModularPeerConnectionFactory(std::move(dependencies));
}
-rtc::scoped_refptr<PeerConnectionFactoryInterface> CreatePeerConnectionFactory(
- rtc::Thread* network_thread,
- rtc::Thread* worker_thread,
- rtc::Thread* signaling_thread,
- rtc::scoped_refptr<AudioDeviceModule> default_adm,
- rtc::scoped_refptr<AudioEncoderFactory> audio_encoder_factory,
- rtc::scoped_refptr<AudioDecoderFactory> audio_decoder_factory,
- std::unique_ptr<VideoEncoderFactory> video_encoder_factory,
- std::unique_ptr<VideoDecoderFactory> video_decoder_factory,
- rtc::scoped_refptr<AudioMixer> audio_mixer,
- rtc::scoped_refptr<AudioProcessing> audio_processing,
- AudioFrameProcessor* audio_frame_processor) {
- return CreatePeerConnectionFactory(
- network_thread, worker_thread, signaling_thread, default_adm,
- audio_encoder_factory, audio_decoder_factory,
- std::move(video_encoder_factory), std::move(video_decoder_factory),
- audio_mixer, audio_processing, audio_frame_processor, nullptr, nullptr);
-}
-
-rtc::scoped_refptr<PeerConnectionFactoryInterface> CreatePeerConnectionFactory(
- rtc::Thread* network_thread,
- rtc::Thread* worker_thread,
- rtc::Thread* signaling_thread,
- rtc::scoped_refptr<AudioDeviceModule> default_adm,
- rtc::scoped_refptr<AudioEncoderFactory> audio_encoder_factory,
- rtc::scoped_refptr<AudioDecoderFactory> audio_decoder_factory,
- std::unique_ptr<VideoEncoderFactory> video_encoder_factory,
- std::unique_ptr<VideoDecoderFactory> video_decoder_factory,
- rtc::scoped_refptr<AudioMixer> audio_mixer,
- rtc::scoped_refptr<AudioProcessing> audio_processing,
- std::unique_ptr<AudioFrameProcessor> owned_audio_frame_processor,
- std::unique_ptr<FieldTrialsView> field_trials) {
- return CreatePeerConnectionFactory(
- network_thread, worker_thread, signaling_thread, default_adm,
- audio_encoder_factory, audio_decoder_factory,
- std::move(video_encoder_factory), std::move(video_decoder_factory),
- audio_mixer, audio_processing, nullptr,
- std::move(owned_audio_frame_processor), std::move(field_trials));
-}
-
} // namespace webrtc
diff --git a/third_party/libwebrtc/api/create_peerconnection_factory.h b/third_party/libwebrtc/api/create_peerconnection_factory.h
index f8f52a0869..d829bc19be 100644
--- a/third_party/libwebrtc/api/create_peerconnection_factory.h
+++ b/third_party/libwebrtc/api/create_peerconnection_factory.h
@@ -37,9 +37,6 @@ class AudioProcessing;
// Create a new instance of PeerConnectionFactoryInterface with optional video
// codec factories. These video factories represents all video codecs, i.e. no
// extra internal video codecs will be added.
-// TODO(bugs.webrtc.org/15111):
-// Remove the method with the raw AudioFrameProcessor pointer in the
-// follow-up.
RTC_EXPORT rtc::scoped_refptr<PeerConnectionFactoryInterface>
CreatePeerConnectionFactory(
rtc::Thread* network_thread,
@@ -52,21 +49,7 @@ CreatePeerConnectionFactory(
std::unique_ptr<VideoDecoderFactory> video_decoder_factory,
rtc::scoped_refptr<AudioMixer> audio_mixer,
rtc::scoped_refptr<AudioProcessing> audio_processing,
- AudioFrameProcessor* audio_frame_processor = nullptr);
-
-RTC_EXPORT rtc::scoped_refptr<PeerConnectionFactoryInterface>
-CreatePeerConnectionFactory(
- rtc::Thread* network_thread,
- rtc::Thread* worker_thread,
- rtc::Thread* signaling_thread,
- rtc::scoped_refptr<AudioDeviceModule> default_adm,
- rtc::scoped_refptr<AudioEncoderFactory> audio_encoder_factory,
- rtc::scoped_refptr<AudioDecoderFactory> audio_decoder_factory,
- std::unique_ptr<VideoEncoderFactory> video_encoder_factory,
- std::unique_ptr<VideoDecoderFactory> video_decoder_factory,
- rtc::scoped_refptr<AudioMixer> audio_mixer,
- rtc::scoped_refptr<AudioProcessing> audio_processing,
- std::unique_ptr<AudioFrameProcessor> owned_audio_frame_processor,
+ std::unique_ptr<AudioFrameProcessor> audio_frame_processor = nullptr,
std::unique_ptr<FieldTrialsView> field_trials = nullptr);
} // namespace webrtc
diff --git a/third_party/libwebrtc/api/crypto/frame_decryptor_interface_gn/moz.build b/third_party/libwebrtc/api/crypto/frame_decryptor_interface_gn/moz.build
index 65794fbdd2..4147b3868b 100644
--- a/third_party/libwebrtc/api/crypto/frame_decryptor_interface_gn/moz.build
+++ b/third_party/libwebrtc/api/crypto/frame_decryptor_interface_gn/moz.build
@@ -180,16 +180,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/crypto/frame_encryptor_interface_gn/moz.build b/third_party/libwebrtc/api/crypto/frame_encryptor_interface_gn/moz.build
index 19352a6da4..93034ef6e2 100644
--- a/third_party/libwebrtc/api/crypto/frame_encryptor_interface_gn/moz.build
+++ b/third_party/libwebrtc/api/crypto/frame_encryptor_interface_gn/moz.build
@@ -180,16 +180,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/crypto/options_gn/moz.build b/third_party/libwebrtc/api/crypto/options_gn/moz.build
index 3219fce47f..9b5cca4f8b 100644
--- a/third_party/libwebrtc/api/crypto/options_gn/moz.build
+++ b/third_party/libwebrtc/api/crypto/options_gn/moz.build
@@ -184,7 +184,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -194,10 +193,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/data_channel_interface.h b/third_party/libwebrtc/api/data_channel_interface.h
index bf27c6c4f3..d2deace2e6 100644
--- a/third_party/libwebrtc/api/data_channel_interface.h
+++ b/third_party/libwebrtc/api/data_channel_interface.h
@@ -22,10 +22,10 @@
#include "absl/functional/any_invocable.h"
#include "absl/types/optional.h"
#include "api/priority.h"
+#include "api/ref_count.h"
#include "api/rtc_error.h"
#include "rtc_base/checks.h"
#include "rtc_base/copy_on_write_buffer.h"
-#include "rtc_base/ref_count.h"
#include "rtc_base/system/rtc_export.h"
namespace webrtc {
@@ -116,7 +116,7 @@ class DataChannelObserver {
virtual ~DataChannelObserver() = default;
};
-class RTC_EXPORT DataChannelInterface : public rtc::RefCountInterface {
+class RTC_EXPORT DataChannelInterface : public RefCountInterface {
public:
// C++ version of: https://www.w3.org/TR/webrtc/#idl-def-rtcdatachannelstate
// Unlikely to change, but keep in sync with DataChannel.java:State and
diff --git a/third_party/libwebrtc/api/dtls_transport_interface.h b/third_party/libwebrtc/api/dtls_transport_interface.h
index 7b0151249c..fe64fb1947 100644
--- a/third_party/libwebrtc/api/dtls_transport_interface.h
+++ b/third_party/libwebrtc/api/dtls_transport_interface.h
@@ -16,9 +16,9 @@
#include "absl/types/optional.h"
#include "api/ice_transport_interface.h"
+#include "api/ref_count.h"
#include "api/rtc_error.h"
#include "api/scoped_refptr.h"
-#include "rtc_base/ref_count.h"
#include "rtc_base/ssl_certificate.h"
#include "rtc_base/system/rtc_export.h"
@@ -107,7 +107,7 @@ class DtlsTransportObserverInterface {
// accessed on that thread, except for functions explicitly marked otherwise.
// References can be held by other threads, and destruction can therefore
// be initiated by other threads.
-class DtlsTransportInterface : public rtc::RefCountInterface {
+class DtlsTransportInterface : public webrtc::RefCountInterface {
public:
// Returns a pointer to the ICE transport that is owned by the DTLS transport.
virtual rtc::scoped_refptr<IceTransportInterface> ice_transport() = 0;
diff --git a/third_party/libwebrtc/api/dtmf_sender_interface.h b/third_party/libwebrtc/api/dtmf_sender_interface.h
index d63e66bbf7..4aed3382ed 100644
--- a/third_party/libwebrtc/api/dtmf_sender_interface.h
+++ b/third_party/libwebrtc/api/dtmf_sender_interface.h
@@ -14,7 +14,7 @@
#include <string>
#include "api/media_stream_interface.h"
-#include "rtc_base/ref_count.h"
+#include "api/ref_count.h"
namespace webrtc {
@@ -42,7 +42,7 @@ class DtmfSenderObserverInterface {
// The interface of native implementation of the RTCDTMFSender defined by the
// WebRTC W3C Editor's Draft.
// See: https://www.w3.org/TR/webrtc/#peer-to-peer-dtmf
-class DtmfSenderInterface : public rtc::RefCountInterface {
+class DtmfSenderInterface : public webrtc::RefCountInterface {
public:
// Provides the spec compliant default 2 second delay for the ',' character.
static const int kDtmfDefaultCommaDelayMs = 2000;
diff --git a/third_party/libwebrtc/api/enable_media.cc b/third_party/libwebrtc/api/enable_media.cc
new file mode 100644
index 0000000000..a05b1b328a
--- /dev/null
+++ b/third_party/libwebrtc/api/enable_media.cc
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2023 The WebRTC Project Authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "api/enable_media.h"
+
+#include <memory>
+#include <utility>
+
+#include "api/environment/environment.h"
+#include "api/peer_connection_interface.h"
+#include "call/call_factory.h"
+#include "media/engine/webrtc_media_engine.h"
+#include "media/engine/webrtc_video_engine.h"
+#include "media/engine/webrtc_voice_engine.h"
+#include "pc/media_factory.h"
+
+namespace webrtc {
+namespace {
+
+using ::cricket::CompositeMediaEngine;
+using ::cricket::MediaEngineInterface;
+using ::cricket::WebRtcVideoEngine;
+using ::cricket::WebRtcVoiceEngine;
+
+class MediaFactoryImpl : public MediaFactory {
+ public:
+ MediaFactoryImpl() = default;
+ MediaFactoryImpl(const MediaFactoryImpl&) = delete;
+ MediaFactoryImpl& operator=(const MediaFactoryImpl&) = delete;
+ ~MediaFactoryImpl() override = default;
+
+ std::unique_ptr<Call> CreateCall(const CallConfig& config) override {
+ CallFactory call_factory;
+ return static_cast<CallFactoryInterface&>(call_factory).CreateCall(config);
+ }
+
+ std::unique_ptr<MediaEngineInterface> CreateMediaEngine(
+ const Environment& env,
+ PeerConnectionFactoryDependencies& deps) override {
+ auto audio_engine = std::make_unique<WebRtcVoiceEngine>(
+ &env.task_queue_factory(), deps.adm.get(),
+ std::move(deps.audio_encoder_factory),
+ std::move(deps.audio_decoder_factory), std::move(deps.audio_mixer),
+ std::move(deps.audio_processing), std::move(deps.audio_frame_processor),
+ env.field_trials());
+ auto video_engine = std::make_unique<WebRtcVideoEngine>(
+ std::move(deps.video_encoder_factory),
+ std::move(deps.video_decoder_factory), env.field_trials());
+ return std::make_unique<CompositeMediaEngine>(std::move(audio_engine),
+ std::move(video_engine));
+ }
+};
+
+} // namespace
+
+void EnableMedia(PeerConnectionFactoryDependencies& deps) {
+ if (deps.media_factory != nullptr) {
+ // Do nothing if media is already enabled. Overwriting media_factory can be
+ // harmful when a different (e.g. test-only) implementation is used.
+ return;
+ }
+ deps.media_factory = std::make_unique<MediaFactoryImpl>();
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/api/enable_media.h b/third_party/libwebrtc/api/enable_media.h
new file mode 100644
index 0000000000..85183963cf
--- /dev/null
+++ b/third_party/libwebrtc/api/enable_media.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2023 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef API_ENABLE_MEDIA_H_
+#define API_ENABLE_MEDIA_H_
+
+#include "api/peer_connection_interface.h"
+#include "rtc_base/system/rtc_export.h"
+
+namespace webrtc {
+
+// Enables media support for PeerConnnectionFactory created from `deps`
+// This function is located in its own build target to allow webrtc users that
+// do not need any media to avoid linking media specific code and thus to reduce
+// binary size.
+RTC_EXPORT void EnableMedia(PeerConnectionFactoryDependencies& deps);
+
+} // namespace webrtc
+
+#endif // API_ENABLE_MEDIA_H_
diff --git a/third_party/libwebrtc/api/enable_media_with_defaults.cc b/third_party/libwebrtc/api/enable_media_with_defaults.cc
new file mode 100644
index 0000000000..81462f703e
--- /dev/null
+++ b/third_party/libwebrtc/api/enable_media_with_defaults.cc
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2023 The WebRTC Project Authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "api/enable_media_with_defaults.h"
+
+#include "api/audio_codecs/builtin_audio_decoder_factory.h"
+#include "api/audio_codecs/builtin_audio_encoder_factory.h"
+#include "api/enable_media.h"
+#include "api/task_queue/default_task_queue_factory.h"
+#include "api/video_codecs/builtin_video_decoder_factory.h"
+#include "api/video_codecs/builtin_video_encoder_factory.h"
+#include "modules/audio_processing/include/audio_processing.h"
+
+namespace webrtc {
+
+void EnableMediaWithDefaults(PeerConnectionFactoryDependencies& deps) {
+ if (deps.task_queue_factory == nullptr) {
+ deps.task_queue_factory = CreateDefaultTaskQueueFactory();
+ }
+ if (deps.audio_encoder_factory == nullptr) {
+ deps.audio_encoder_factory = CreateBuiltinAudioEncoderFactory();
+ }
+ if (deps.audio_decoder_factory == nullptr) {
+ deps.audio_decoder_factory = CreateBuiltinAudioDecoderFactory();
+ }
+ if (deps.audio_processing == nullptr) {
+ deps.audio_processing = AudioProcessingBuilder().Create();
+ }
+
+ if (deps.video_encoder_factory == nullptr) {
+ deps.video_encoder_factory = CreateBuiltinVideoEncoderFactory();
+ }
+ if (deps.video_decoder_factory == nullptr) {
+ deps.video_decoder_factory = CreateBuiltinVideoDecoderFactory();
+ }
+ EnableMedia(deps);
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/api/enable_media_with_defaults.h b/third_party/libwebrtc/api/enable_media_with_defaults.h
new file mode 100644
index 0000000000..1b13a98fc9
--- /dev/null
+++ b/third_party/libwebrtc/api/enable_media_with_defaults.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2023 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef API_ENABLE_MEDIA_WITH_DEFAULTS_H_
+#define API_ENABLE_MEDIA_WITH_DEFAULTS_H_
+
+#include "api/peer_connection_interface.h"
+#include "rtc_base/system/rtc_export.h"
+
+namespace webrtc {
+
+// Fills unset media related dependencies in `deps` and enables media support
+// for a PeerConnectionFactory created from `deps`.
+// This function is located in its own build target as it pulls additional
+// dependencies compared to `EnableMedia`, and thus may add extra binary size.
+RTC_EXPORT void EnableMediaWithDefaults(
+ PeerConnectionFactoryDependencies& deps);
+
+} // namespace webrtc
+
+#endif // API_ENABLE_MEDIA_WITH_DEFAULTS_H_
diff --git a/third_party/libwebrtc/api/environment/BUILD.gn b/third_party/libwebrtc/api/environment/BUILD.gn
new file mode 100644
index 0000000000..c2b73b327e
--- /dev/null
+++ b/third_party/libwebrtc/api/environment/BUILD.gn
@@ -0,0 +1,63 @@
+# Copyright (c) 2023 The WebRTC project authors. All Rights Reserved.
+#
+# Use of this source code is governed by a BSD-style license
+# that can be found in the LICENSE file in the root of the source
+# tree. An additional intellectual property rights grant can be found
+# in the file PATENTS. All contributing project authors may
+# be found in the AUTHORS file in the root of the source tree.
+
+import("../../webrtc.gni")
+
+rtc_source_set("environment") {
+ visibility = [ "*" ]
+ sources = [ "environment.h" ]
+ deps = [
+ "..:refcountedbase",
+ "..:scoped_refptr",
+ "../../rtc_base/system:rtc_export",
+ ]
+ absl_deps = [ "//third_party/abseil-cpp/absl/base:nullability" ]
+}
+
+rtc_library("environment_factory") {
+ visibility = [ "*" ]
+ poisonous = [ "environment_construction" ]
+ sources = [
+ "environment_factory.cc",
+ "environment_factory.h",
+ ]
+ deps = [
+ ":environment",
+ "..:make_ref_counted",
+ "..:refcountedbase",
+ "..:scoped_refptr",
+ "../../rtc_base:checks",
+ "../../rtc_base/system:rtc_export",
+ "../../system_wrappers",
+ "../rtc_event_log",
+ "../task_queue:default_task_queue_factory",
+ "../transport:field_trial_based_config",
+ ]
+ absl_deps = [ "//third_party/abseil-cpp/absl/base:nullability" ]
+}
+
+if (rtc_include_tests) {
+ rtc_library("environment_unittests") {
+ testonly = true
+ sources = [ "environment_unittest.cc" ]
+ deps = [
+ ":environment",
+ ":environment_factory",
+ "..:field_trials_view",
+ "../../system_wrappers",
+ "../../test:test_support",
+ "../rtc_event_log",
+ "../task_queue",
+ "../units:timestamp",
+ ]
+ absl_deps = [
+ "//third_party/abseil-cpp/absl/functional:any_invocable",
+ "//third_party/abseil-cpp/absl/types:optional",
+ ]
+ }
+}
diff --git a/third_party/libwebrtc/api/environment/OWNERS b/third_party/libwebrtc/api/environment/OWNERS
new file mode 100644
index 0000000000..a8af6b5b26
--- /dev/null
+++ b/third_party/libwebrtc/api/environment/OWNERS
@@ -0,0 +1,15 @@
+# Environment has a limited visibility for stronger control what utilities are
+# exposed through it.
+# Utilities exposed through environemnt
+# - should be helpful for various WebRTC sub components.
+# - should be thread safe.
+# - should have a default implementation.
+# - should provide functionality different to existing utilities in the
+# environemnt.
+# - should need at most one instance per peer connection.
+set noparent
+include ../../OWNERS_INFRA
+
+danilchap@webrtc.org
+hta@webrtc.org
+mbonadei@webrtc.org
diff --git a/third_party/libwebrtc/api/environment/environment.h b/third_party/libwebrtc/api/environment/environment.h
new file mode 100644
index 0000000000..d86b7ae780
--- /dev/null
+++ b/third_party/libwebrtc/api/environment/environment.h
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2023 The WebRTC Project Authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+// This header file provides wrapper for common WebRTC utilities.
+// Different application may need different implementations of these utilities,
+// Moreover, single application may need to use WebRTC for multiple purposes,
+// and thus would need to provide different utilities implementations for
+// different peer connections.
+// The main purpose of the `Environment` class below is to propagate references
+// to those utilities to all WebRTC classes that need them.
+
+#ifndef API_ENVIRONMENT_ENVIRONMENT_H_
+#define API_ENVIRONMENT_ENVIRONMENT_H_
+
+#include <utility>
+
+#include "absl/base/nullability.h"
+#include "api/ref_counted_base.h"
+#include "api/scoped_refptr.h"
+#include "rtc_base/system/rtc_export.h"
+
+namespace webrtc {
+
+// These classes are forward declared to keep Environment dependencies
+// lightweight. Users who need any of the types below should include their
+// header explicitely.
+class Clock;
+class TaskQueueFactory;
+class FieldTrialsView;
+class RtcEventLog;
+
+// Contains references to WebRTC utilities. Object of this class should be
+// passed as a construction parameter and saved by value in each class that
+// needs it. Most classes shouldn't create a new instance of the `Environment`,
+// but instead should use a propagated copy.
+// Usually Environment should be the first parameter in a constructor or a
+// factory, and the first member in the class. Keeping Environment as the first
+// member in the class ensures utilities (e.g. clock) are still valid during
+// destruction of other members.
+//
+// Example:
+// class PeerConnection {
+// public:
+// PeerConnection(const Environment& env, ...)
+// : env_(env),
+// log_duration_on_destruction_(&env_.clock()),
+// rtp_manager_(env_, ...),
+// ...
+//
+// const FieldTrialsView& trials() const { return env_.field_trials(); }
+//
+// scoped_refptr<RtpTransceiverInterface> AddTransceiver(...) {
+// return make_ref_counted<RtpTransceiverImpl>(env_, ...);
+// }
+//
+// private:
+// const Environment env_;
+// Stats log_duration_on_destruction_;
+// RtpTransmissionManager rtp_manager_;
+// };
+// This class is thread safe.
+class RTC_EXPORT Environment final {
+ public:
+ // Default constructor is deleted in favor of creating this object using
+ // `EnvironmentFactory`. To create the default environment use
+ // `EnvironmentFactory().Create()` or `CreateEnvironment()`.
+ Environment() = delete;
+
+ Environment(const Environment&) = default;
+ Environment(Environment&&) = default;
+ Environment& operator=(const Environment&) = default;
+ Environment& operator=(Environment&&) = default;
+
+ ~Environment() = default;
+
+ // Provides means to alter behavior, mostly for A/B testing new features.
+ // See ../../g3doc/field-trials.md
+ const FieldTrialsView& field_trials() const;
+
+ // Provides an interface to query current time.
+ // See ../../g3doc/implementation_basics.md#time
+ Clock& clock() const;
+
+ // Provides a factory for task queues, WebRTC threading primitives.
+ // See ../../g3doc/implementation_basics.md#threads
+ TaskQueueFactory& task_queue_factory() const;
+
+ // Provides an interface for collecting structured logs.
+ // See ../../logging/g3doc/rtc_event_log.md
+ RtcEventLog& event_log() const;
+
+ private:
+ friend class EnvironmentFactory;
+ Environment(scoped_refptr<const rtc::RefCountedBase> storage,
+ absl::Nonnull<const FieldTrialsView*> field_trials,
+ absl::Nonnull<Clock*> clock,
+ absl::Nonnull<TaskQueueFactory*> task_queue_factory,
+ absl::Nonnull<RtcEventLog*> event_log)
+ : storage_(std::move(storage)),
+ field_trials_(field_trials),
+ clock_(clock),
+ task_queue_factory_(task_queue_factory),
+ event_log_(event_log) {}
+
+ // Container that keeps ownership of the utilities below.
+ // Defining this as a RefCountedBase allows `Environment` to share this
+ // storage with another `Environment`, in particular allows `Environment` to
+ // be copyable. It is up to the `EnvironmentFactory` to provide an object that
+ // ensures references to utilties below are valid while object in the
+ // `storage_` is alive.
+ scoped_refptr<const rtc::RefCountedBase> storage_;
+
+ absl::Nonnull<const FieldTrialsView*> field_trials_;
+ absl::Nonnull<Clock*> clock_;
+ absl::Nonnull<TaskQueueFactory*> task_queue_factory_;
+ absl::Nonnull<RtcEventLog*> event_log_;
+};
+
+//------------------------------------------------------------------------------
+// Implementation details follow
+//------------------------------------------------------------------------------
+
+inline const FieldTrialsView& Environment::field_trials() const {
+ return *field_trials_;
+}
+
+inline Clock& Environment::clock() const {
+ return *clock_;
+}
+
+inline TaskQueueFactory& Environment::task_queue_factory() const {
+ return *task_queue_factory_;
+}
+
+inline RtcEventLog& Environment::event_log() const {
+ return *event_log_;
+}
+
+} // namespace webrtc
+
+#endif // API_ENVIRONMENT_ENVIRONMENT_H_
diff --git a/third_party/libwebrtc/api/environment/environment_factory.cc b/third_party/libwebrtc/api/environment/environment_factory.cc
new file mode 100644
index 0000000000..c0b681aa08
--- /dev/null
+++ b/third_party/libwebrtc/api/environment/environment_factory.cc
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2023 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "api/environment/environment_factory.h"
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "api/make_ref_counted.h"
+#include "api/rtc_event_log/rtc_event_log.h"
+#include "api/task_queue/default_task_queue_factory.h"
+#include "api/transport/field_trial_based_config.h"
+#include "rtc_base/checks.h"
+#include "system_wrappers/include/clock.h"
+
+namespace webrtc {
+namespace {
+
+template <typename T>
+void Store(absl::Nonnull<std::unique_ptr<T>> value,
+ scoped_refptr<const rtc::RefCountedBase>& leaf) {
+ class StorageNode : public rtc::RefCountedBase {
+ public:
+ StorageNode(scoped_refptr<const rtc::RefCountedBase> parent,
+ absl::Nonnull<std::unique_ptr<T>> value)
+ : parent_(std::move(parent)), value_(std::move(value)) {}
+
+ StorageNode(const StorageNode&) = delete;
+ StorageNode& operator=(const StorageNode&) = delete;
+
+ ~StorageNode() override = default;
+
+ private:
+ scoped_refptr<const rtc::RefCountedBase> parent_;
+ absl::Nonnull<std::unique_ptr<T>> value_;
+ };
+
+ // Utilities provided with ownership form a tree:
+ // Root is nullptr, each node keeps an ownership of one utility.
+ // Each child node has a link to the parent, but parent is unaware of its
+ // children. Each `EnvironmentFactory` and `Environment` keep a reference to a
+ // 'leaf_' - node with the last provided utility. This way `Environment` keeps
+ // ownership of a single branch of the storage tree with each used utiltity
+ // owned by one of the nodes on that branch.
+ leaf = rtc::make_ref_counted<StorageNode>(std::move(leaf), std::move(value));
+}
+
+} // namespace
+
+EnvironmentFactory::EnvironmentFactory(const Environment& env)
+ : leaf_(env.storage_),
+ field_trials_(env.field_trials_),
+ clock_(env.clock_),
+ task_queue_factory_(env.task_queue_factory_),
+ event_log_(env.event_log_) {}
+
+void EnvironmentFactory::Set(
+ absl::Nullable<std::unique_ptr<const FieldTrialsView>> utility) {
+ if (utility != nullptr) {
+ field_trials_ = utility.get();
+ Store(std::move(utility), leaf_);
+ }
+}
+
+void EnvironmentFactory::Set(absl::Nullable<std::unique_ptr<Clock>> utility) {
+ if (utility != nullptr) {
+ clock_ = utility.get();
+ Store(std::move(utility), leaf_);
+ }
+}
+
+void EnvironmentFactory::Set(
+ absl::Nullable<std::unique_ptr<TaskQueueFactory>> utility) {
+ if (utility != nullptr) {
+ task_queue_factory_ = utility.get();
+ Store(std::move(utility), leaf_);
+ }
+}
+
+void EnvironmentFactory::Set(
+ absl::Nullable<std::unique_ptr<RtcEventLog>> utility) {
+ if (utility != nullptr) {
+ event_log_ = utility.get();
+ Store(std::move(utility), leaf_);
+ }
+}
+
+Environment EnvironmentFactory::CreateWithDefaults() && {
+ if (field_trials_ == nullptr) {
+ Set(std::make_unique<FieldTrialBasedConfig>());
+ }
+ if (clock_ == nullptr) {
+ Set(Clock::GetRealTimeClock());
+ }
+ if (task_queue_factory_ == nullptr) {
+ Set(CreateDefaultTaskQueueFactory(field_trials_));
+ }
+ if (event_log_ == nullptr) {
+ Set(std::make_unique<RtcEventLogNull>());
+ }
+
+ RTC_DCHECK(field_trials_ != nullptr);
+ RTC_DCHECK(clock_ != nullptr);
+ RTC_DCHECK(task_queue_factory_ != nullptr);
+ RTC_DCHECK(event_log_ != nullptr);
+ return Environment(std::move(leaf_), //
+ field_trials_, clock_, task_queue_factory_, event_log_);
+}
+
+Environment EnvironmentFactory::Create() const {
+ // Create a temporary copy to avoid mutating `this` with default utilities.
+ return EnvironmentFactory(*this).CreateWithDefaults();
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/api/environment/environment_factory.h b/third_party/libwebrtc/api/environment/environment_factory.h
new file mode 100644
index 0000000000..a0fc3effdb
--- /dev/null
+++ b/third_party/libwebrtc/api/environment/environment_factory.h
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2023 The WebRTC Project Authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef API_ENVIRONMENT_ENVIRONMENT_FACTORY_H_
+#define API_ENVIRONMENT_ENVIRONMENT_FACTORY_H_
+
+#include <memory>
+#include <utility>
+
+#include "absl/base/nullability.h"
+#include "api/environment/environment.h"
+#include "api/ref_counted_base.h"
+#include "api/scoped_refptr.h"
+#include "rtc_base/system/rtc_export.h"
+
+namespace webrtc {
+
+// These classes are forward declared to reduce amount of headers exposed
+// through api header.
+class Clock;
+class TaskQueueFactory;
+class FieldTrialsView;
+class RtcEventLog;
+
+// Constructs `Environment`.
+// Individual utilities are provided using one of the `Set` functions.
+// `Set` functions do nothing when nullptr value is passed.
+// Creates default implementations for utilities that are not provided.
+//
+// Examples:
+// Environment default_env = EnvironmentFactory().Create();
+//
+// EnvironmentFactory factory;
+// factory.Set(std::make_unique<CustomTaskQueueFactory>());
+// factory.Set(std::make_unique<CustomFieldTrials>());
+// Environment custom_env = factory.Create();
+//
+class RTC_EXPORT EnvironmentFactory final {
+ public:
+ EnvironmentFactory() = default;
+ explicit EnvironmentFactory(const Environment& env);
+
+ EnvironmentFactory(const EnvironmentFactory&) = default;
+ EnvironmentFactory(EnvironmentFactory&&) = default;
+ EnvironmentFactory& operator=(const EnvironmentFactory&) = default;
+ EnvironmentFactory& operator=(EnvironmentFactory&&) = default;
+
+ ~EnvironmentFactory() = default;
+
+ void Set(absl::Nullable<std::unique_ptr<const FieldTrialsView>> utility);
+ void Set(absl::Nullable<std::unique_ptr<Clock>> utility);
+ void Set(absl::Nullable<std::unique_ptr<TaskQueueFactory>> utility);
+ void Set(absl::Nullable<std::unique_ptr<RtcEventLog>> utility);
+
+ void Set(absl::Nullable<const FieldTrialsView*> utility);
+ void Set(absl::Nullable<Clock*> utility);
+ void Set(absl::Nullable<TaskQueueFactory*> utility);
+ void Set(absl::Nullable<RtcEventLog*> utility);
+
+ Environment Create() const;
+
+ private:
+ Environment CreateWithDefaults() &&;
+
+ scoped_refptr<const rtc::RefCountedBase> leaf_;
+
+ absl::Nullable<const FieldTrialsView*> field_trials_ = nullptr;
+ absl::Nullable<Clock*> clock_ = nullptr;
+ absl::Nullable<TaskQueueFactory*> task_queue_factory_ = nullptr;
+ absl::Nullable<RtcEventLog*> event_log_ = nullptr;
+};
+
+// Helper for concise way to create an environment.
+// `Environment env = CreateEnvironment(utility1, utility2)` is a shortcut to
+// `EnvironmentFactory factory;
+// factory.Set(utility1);
+// factory.Set(utility2);
+// Environment env = factory.Create();`
+//
+// Examples:
+// Environment default_env = CreateEnvironment();
+// Environment custom_env =
+// CreateEnvironment(std::make_unique<CustomTaskQueueFactory>(),
+// std::make_unique<CustomFieldTrials>());
+template <typename... Utilities>
+Environment CreateEnvironment(Utilities&&... utilities);
+
+//------------------------------------------------------------------------------
+// Implementation details follow
+//------------------------------------------------------------------------------
+
+inline void EnvironmentFactory::Set(
+ absl::Nullable<const FieldTrialsView*> utility) {
+ if (utility != nullptr) {
+ field_trials_ = utility;
+ }
+}
+
+inline void EnvironmentFactory::Set(absl::Nullable<Clock*> utility) {
+ if (utility != nullptr) {
+ clock_ = utility;
+ }
+}
+
+inline void EnvironmentFactory::Set(absl::Nullable<TaskQueueFactory*> utility) {
+ if (utility != nullptr) {
+ task_queue_factory_ = utility;
+ }
+}
+
+inline void EnvironmentFactory::Set(absl::Nullable<RtcEventLog*> utility) {
+ if (utility != nullptr) {
+ event_log_ = utility;
+ }
+}
+
+namespace webrtc_create_environment_internal {
+
+inline void Set(EnvironmentFactory& factory) {}
+
+template <typename FirstUtility, typename... Utilities>
+void Set(EnvironmentFactory& factory,
+ FirstUtility&& first,
+ Utilities&&... utilities) {
+ factory.Set(std::forward<FirstUtility>(first));
+ Set(factory, std::forward<Utilities>(utilities)...);
+}
+
+} // namespace webrtc_create_environment_internal
+
+template <typename... Utilities>
+Environment CreateEnvironment(Utilities&&... utilities) {
+ EnvironmentFactory factory;
+ webrtc_create_environment_internal::Set(
+ factory, std::forward<Utilities>(utilities)...);
+ return factory.Create();
+}
+
+} // namespace webrtc
+
+#endif // API_ENVIRONMENT_ENVIRONMENT_FACTORY_H_
diff --git a/third_party/libwebrtc/rtc_base/async_resolver_interface_gn/moz.build b/third_party/libwebrtc/api/environment/environment_gn/moz.build
index 485ef7ae1c..e7105ff573 100644
--- a/third_party/libwebrtc/rtc_base/async_resolver_interface_gn/moz.build
+++ b/third_party/libwebrtc/api/environment/environment_gn/moz.build
@@ -30,10 +30,6 @@ LOCAL_INCLUDES += [
"/tools/profiler/public"
]
-UNIFIED_SOURCES += [
- "/third_party/libwebrtc/rtc_base/async_resolver_interface.cc"
-]
-
if not CONFIG["MOZ_DEBUG"]:
DEFINES["DYNAMIC_ANNOTATIONS_ENABLED"] = "0"
@@ -58,11 +54,6 @@ if CONFIG["OS_TARGET"] == "Android":
DEFINES["__STDC_CONSTANT_MACROS"] = True
DEFINES["__STDC_FORMAT_MACROS"] = True
- OS_LIBS += [
- "GLESv2",
- "log"
- ]
-
if CONFIG["OS_TARGET"] == "Darwin":
DEFINES["WEBRTC_MAC"] = True
@@ -129,13 +120,6 @@ if CONFIG["OS_TARGET"] == "WINNT":
DEFINES["_WINDOWS"] = True
DEFINES["__STD_C"] = True
- OS_LIBS += [
- "crypt32",
- "iphlpapi",
- "secur32",
- "winmm"
- ]
-
if CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["WEBRTC_ARCH_ARM64"] = True
@@ -143,10 +127,6 @@ if CONFIG["TARGET_CPU"] == "aarch64":
if CONFIG["TARGET_CPU"] == "arm":
- CXXFLAGS += [
- "-mfpu=neon"
- ]
-
DEFINES["WEBRTC_ARCH_ARM"] = True
DEFINES["WEBRTC_ARCH_ARM_V7"] = True
DEFINES["WEBRTC_HAS_NEON"] = True
@@ -196,20 +176,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- CXXFLAGS += [
- "-msse2"
- ]
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
@@ -220,14 +189,10 @@ if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "arm":
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "x86":
- CXXFLAGS += [
- "-msse2"
- ]
-
DEFINES["_GNU_SOURCE"] = True
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "x86_64":
DEFINES["_GNU_SOURCE"] = True
-Library("async_resolver_interface_gn")
+Library("environment_gn")
diff --git a/third_party/libwebrtc/api/environment/environment_unittest.cc b/third_party/libwebrtc/api/environment/environment_unittest.cc
new file mode 100644
index 0000000000..07bd8793bc
--- /dev/null
+++ b/third_party/libwebrtc/api/environment/environment_unittest.cc
@@ -0,0 +1,275 @@
+/*
+ * Copyright (c) 2023 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "api/environment/environment.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "absl/functional/any_invocable.h"
+#include "absl/types/optional.h"
+#include "api/environment/environment_factory.h"
+#include "api/field_trials_view.h"
+#include "api/rtc_event_log/rtc_event_log.h"
+#include "api/task_queue/task_queue_factory.h"
+#include "api/units/timestamp.h"
+#include "system_wrappers/include/clock.h"
+#include "test/gmock.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+namespace {
+
+using ::testing::ElementsAre;
+using ::testing::IsEmpty;
+using ::testing::Not;
+using ::testing::NotNull;
+using ::testing::Ref;
+
+class FakeEvent : public RtcEvent {
+ public:
+ Type GetType() const override { return RtcEvent::Type::FakeEvent; }
+ bool IsConfigEvent() const override { return false; }
+};
+
+class FakeFieldTrials : public FieldTrialsView {
+ public:
+ explicit FakeFieldTrials(absl::AnyInvocable<void() &&> on_destroyed = nullptr)
+ : on_destroyed_(std::move(on_destroyed)) {}
+ ~FakeFieldTrials() override {
+ if (on_destroyed_ != nullptr) {
+ std::move(on_destroyed_)();
+ }
+ }
+
+ std::string Lookup(absl::string_view key) const override { return "fake"; }
+
+ private:
+ absl::AnyInvocable<void() &&> on_destroyed_;
+};
+
+class FakeTaskQueueFactory : public TaskQueueFactory {
+ public:
+ explicit FakeTaskQueueFactory(
+ absl::AnyInvocable<void() &&> on_destroyed = nullptr)
+ : on_destroyed_(std::move(on_destroyed)) {}
+ ~FakeTaskQueueFactory() override {
+ if (on_destroyed_ != nullptr) {
+ std::move(on_destroyed_)();
+ }
+ }
+
+ std::unique_ptr<TaskQueueBase, TaskQueueDeleter> CreateTaskQueue(
+ absl::string_view name,
+ Priority priority) const override {
+ return nullptr;
+ }
+
+ private:
+ absl::AnyInvocable<void() &&> on_destroyed_;
+};
+
+TEST(EnvironmentTest, DefaultEnvironmentHasAllUtilities) {
+ Environment env = EnvironmentFactory().Create();
+
+ // Try to use each utility, expect no crashes.
+ env.clock().CurrentTime();
+ EXPECT_THAT(env.task_queue_factory().CreateTaskQueue(
+ "test", TaskQueueFactory::Priority::NORMAL),
+ NotNull());
+ env.event_log().Log(std::make_unique<FakeEvent>());
+ env.field_trials().Lookup("WebRTC-Debugging-RtpDump");
+}
+
+TEST(EnvironmentTest, UsesProvidedUtilitiesWithOwnership) {
+ auto owned_field_trials = std::make_unique<FakeFieldTrials>();
+ auto owned_task_queue_factory = std::make_unique<FakeTaskQueueFactory>();
+ auto owned_clock = std::make_unique<SimulatedClock>(Timestamp::Zero());
+ auto owned_event_log = std::make_unique<RtcEventLogNull>();
+
+ FieldTrialsView& field_trials = *owned_field_trials;
+ TaskQueueFactory& task_queue_factory = *owned_task_queue_factory;
+ Clock& clock = *owned_clock;
+ RtcEventLog& event_log = *owned_event_log;
+
+ Environment env = CreateEnvironment(
+ std::move(owned_field_trials), std::move(owned_clock),
+ std::move(owned_task_queue_factory), std::move(owned_event_log));
+
+ EXPECT_THAT(env.field_trials(), Ref(field_trials));
+ EXPECT_THAT(env.task_queue_factory(), Ref(task_queue_factory));
+ EXPECT_THAT(env.clock(), Ref(clock));
+ EXPECT_THAT(env.event_log(), Ref(event_log));
+}
+
+TEST(EnvironmentTest, UsesProvidedUtilitiesWithoutOwnership) {
+ FakeFieldTrials field_trials;
+ FakeTaskQueueFactory task_queue_factory;
+ SimulatedClock clock(Timestamp::Zero());
+ RtcEventLogNull event_log;
+
+ Environment env =
+ CreateEnvironment(&field_trials, &clock, &task_queue_factory, &event_log);
+
+ EXPECT_THAT(env.field_trials(), Ref(field_trials));
+ EXPECT_THAT(env.task_queue_factory(), Ref(task_queue_factory));
+ EXPECT_THAT(env.clock(), Ref(clock));
+ EXPECT_THAT(env.event_log(), Ref(event_log));
+}
+
+TEST(EnvironmentTest, UsesLastProvidedUtility) {
+ auto owned_field_trials1 = std::make_unique<FakeFieldTrials>();
+ auto owned_field_trials2 = std::make_unique<FakeFieldTrials>();
+ FieldTrialsView& field_trials2 = *owned_field_trials2;
+
+ Environment env = CreateEnvironment(std::move(owned_field_trials1),
+ std::move(owned_field_trials2));
+
+ EXPECT_THAT(env.field_trials(), Ref(field_trials2));
+}
+
+// Utilities can be provided from different sources, and when some source
+// choose not to provide an utility, it is usually expressed with nullptr.
+// When utility is not provided, it is natural to use previously set one.
+// E.g. Both PeerConnectionFactoryDependencies and PeerConnectionDependencies
+// provide field trials. When PeerConnectionDependencies::trials == nullptr,
+// then trials from the PeerConnectionFactoryDependencies should be used.
+// With nullptr accepted and ignored this can be expressed by
+// `Environemt env = CreateEnvironment(pcf_deps.trials, pc_deps.trials);`
+// That would use pc_deps.trials when not nullptr, pcf_deps.trials when
+// pc_deps.trials is nullptr, but pcf_deps.trials is not, and default field
+// trials when both are nullptr.
+TEST(EnvironmentTest, IgnoresProvidedNullptrUtility) {
+ auto owned_field_trials = std::make_unique<FakeFieldTrials>();
+ std::unique_ptr<FieldTrialsView> null_field_trials = nullptr;
+ FieldTrialsView& field_trials = *owned_field_trials;
+
+ Environment env = CreateEnvironment(std::move(owned_field_trials),
+ std::move(null_field_trials));
+
+ EXPECT_THAT(env.field_trials(), Ref(field_trials));
+}
+
+TEST(EnvironmentTest, KeepsUtilityAliveWhileEnvironmentIsAlive) {
+ bool utility_destroyed = false;
+ auto field_trials = std::make_unique<FakeFieldTrials>(
+ /*on_destroyed=*/[&] { utility_destroyed = true; });
+
+ // Wrap Environment into optional to have explicit control when it is deleted.
+ absl::optional<Environment> env = CreateEnvironment(std::move(field_trials));
+
+ EXPECT_FALSE(utility_destroyed);
+ env = absl::nullopt;
+ EXPECT_TRUE(utility_destroyed);
+}
+
+TEST(EnvironmentTest, KeepsUtilityAliveWhileCopyOfEnvironmentIsAlive) {
+ bool utility_destroyed = false;
+ auto field_trials = std::make_unique<FakeFieldTrials>(
+ /*on_destroyed=*/[&] { utility_destroyed = true; });
+
+ absl::optional<Environment> env1 = CreateEnvironment(std::move(field_trials));
+ absl::optional<Environment> env2 = env1;
+
+ EXPECT_FALSE(utility_destroyed);
+ env1 = absl::nullopt;
+ EXPECT_FALSE(utility_destroyed);
+ env2 = absl::nullopt;
+ EXPECT_TRUE(utility_destroyed);
+}
+
+TEST(EnvironmentTest, FactoryCanBeReusedToCreateDifferentEnvironments) {
+ auto owned_task_queue_factory = std::make_unique<FakeTaskQueueFactory>();
+ auto owned_field_trials1 = std::make_unique<FakeFieldTrials>();
+ auto owned_field_trials2 = std::make_unique<FakeFieldTrials>();
+ TaskQueueFactory& task_queue_factory = *owned_task_queue_factory;
+ FieldTrialsView& field_trials1 = *owned_field_trials1;
+ FieldTrialsView& field_trials2 = *owned_field_trials2;
+
+ EnvironmentFactory factory;
+ factory.Set(std::move(owned_task_queue_factory));
+ factory.Set(std::move(owned_field_trials1));
+ Environment env1 = factory.Create();
+ factory.Set(std::move(owned_field_trials2));
+ Environment env2 = factory.Create();
+
+ // Environments share the same custom task queue factory.
+ EXPECT_THAT(env1.task_queue_factory(), Ref(task_queue_factory));
+ EXPECT_THAT(env2.task_queue_factory(), Ref(task_queue_factory));
+
+ // Environments have different field trials.
+ EXPECT_THAT(env1.field_trials(), Ref(field_trials1));
+ EXPECT_THAT(env2.field_trials(), Ref(field_trials2));
+}
+
+TEST(EnvironmentTest, FactoryCanCreateNewEnvironmentFromExistingOne) {
+ Environment env1 =
+ CreateEnvironment(std::make_unique<FakeTaskQueueFactory>());
+ EnvironmentFactory factory(env1);
+ factory.Set(std::make_unique<FakeFieldTrials>());
+ Environment env2 = factory.Create();
+
+ // Environments share the same default clock.
+ EXPECT_THAT(env2.clock(), Ref(env1.clock()));
+
+ // Environments share the same custom task queue factory.
+ EXPECT_THAT(env2.task_queue_factory(), Ref(env1.task_queue_factory()));
+
+ // Environments have different field trials.
+ EXPECT_THAT(env2.field_trials(), Not(Ref(env1.field_trials())));
+}
+
+TEST(EnvironmentTest, KeepsOwnershipsWhenCreateNewEnvironmentFromExistingOne) {
+ bool utility1_destroyed = false;
+ bool utility2_destroyed = false;
+ absl::optional<Environment> env1 =
+ CreateEnvironment(std::make_unique<FakeTaskQueueFactory>(
+ /*on_destroyed=*/[&] { utility1_destroyed = true; }));
+
+ absl::optional<EnvironmentFactory> factory = EnvironmentFactory(*env1);
+
+ // Destroy env1, check utility1 it was using is still alive.
+ env1 = absl::nullopt;
+ EXPECT_FALSE(utility1_destroyed);
+
+ factory->Set(std::make_unique<FakeFieldTrials>(
+ /*on_destroyed=*/[&] { utility2_destroyed = true; }));
+ absl::optional<Environment> env2 = factory->Create();
+
+ // Destroy the factory, check all utilities used by env2 are alive.
+ factory = absl::nullopt;
+ EXPECT_FALSE(utility1_destroyed);
+ EXPECT_FALSE(utility2_destroyed);
+
+ // Once last Environment object is deleted, utilties should be deleted too.
+ env2 = absl::nullopt;
+ EXPECT_TRUE(utility1_destroyed);
+ EXPECT_TRUE(utility2_destroyed);
+}
+
+TEST(EnvironmentTest, DestroysUtilitiesInReverseProvidedOrder) {
+ std::vector<std::string> destroyed;
+ auto field_trials = std::make_unique<FakeFieldTrials>(
+ /*on_destroyed=*/[&] { destroyed.push_back("field_trials"); });
+ auto task_queue_factory = std::make_unique<FakeTaskQueueFactory>(
+ /*on_destroyed=*/[&] { destroyed.push_back("task_queue_factory"); });
+
+ absl::optional<Environment> env =
+ CreateEnvironment(std::move(field_trials), std::move(task_queue_factory));
+
+ ASSERT_THAT(destroyed, IsEmpty());
+ env = absl::nullopt;
+ EXPECT_THAT(destroyed, ElementsAre("task_queue_factory", "field_trials"));
+}
+
+} // namespace
+} // namespace webrtc
diff --git a/third_party/libwebrtc/api/fec_controller_api_gn/moz.build b/third_party/libwebrtc/api/fec_controller_api_gn/moz.build
index a0129cce5a..619ea937f0 100644
--- a/third_party/libwebrtc/api/fec_controller_api_gn/moz.build
+++ b/third_party/libwebrtc/api/fec_controller_api_gn/moz.build
@@ -180,16 +180,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/field_trials_registry_gn/moz.build b/third_party/libwebrtc/api/field_trials_registry_gn/moz.build
index 5133134fc2..01ce20c5a6 100644
--- a/third_party/libwebrtc/api/field_trials_registry_gn/moz.build
+++ b/third_party/libwebrtc/api/field_trials_registry_gn/moz.build
@@ -195,7 +195,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -205,10 +204,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/field_trials_view_gn/moz.build b/third_party/libwebrtc/api/field_trials_view_gn/moz.build
index 0e5ed09f70..967e416aad 100644
--- a/third_party/libwebrtc/api/field_trials_view_gn/moz.build
+++ b/third_party/libwebrtc/api/field_trials_view_gn/moz.build
@@ -176,16 +176,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/frame_transformer_interface.h b/third_party/libwebrtc/api/frame_transformer_interface.h
index d1ea15a54e..afa79b92ea 100644
--- a/third_party/libwebrtc/api/frame_transformer_interface.h
+++ b/third_party/libwebrtc/api/frame_transformer_interface.h
@@ -13,12 +13,11 @@
#include <memory>
#include <string>
-#include <vector>
+#include "api/ref_count.h"
#include "api/scoped_refptr.h"
#include "api/video/encoded_frame.h"
#include "api/video/video_frame_metadata.h"
-#include "rtc_base/ref_count.h"
namespace webrtc {
@@ -54,11 +53,7 @@ class TransformableFrameInterface {
// sender frames to allow received frames to be directly re-transmitted on
// other PeerConnectionss.
virtual Direction GetDirection() const { return Direction::kUnknown; }
- virtual std::string GetMimeType() const {
- // TODO(bugs.webrtc.org/15579): Change this to pure virtual after it
- // is implemented everywhere.
- return "unknown/unknown";
- }
+ virtual std::string GetMimeType() const = 0;
};
class TransformableVideoFrameInterface : public TransformableFrameInterface {
@@ -100,6 +95,12 @@ class TransformedFrameCallback : public rtc::RefCountInterface {
virtual void OnTransformedFrame(
std::unique_ptr<TransformableFrameInterface> frame) = 0;
+ // Request to no longer be called on each frame, instead having frames be
+ // sent directly to OnTransformedFrame without additional work.
+ // TODO(crbug.com/1502781): Make pure virtual once all mocks have
+ // implementations.
+ virtual void StartShortCircuiting() {}
+
protected:
~TransformedFrameCallback() override = default;
};
diff --git a/third_party/libwebrtc/api/frame_transformer_interface_gn/moz.build b/third_party/libwebrtc/api/frame_transformer_interface_gn/moz.build
index 20da3e9cdb..ea4cac1276 100644
--- a/third_party/libwebrtc/api/frame_transformer_interface_gn/moz.build
+++ b/third_party/libwebrtc/api/frame_transformer_interface_gn/moz.build
@@ -187,16 +187,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/function_view_gn/moz.build b/third_party/libwebrtc/api/function_view_gn/moz.build
index 0854c1f771..980c9d9ca6 100644
--- a/third_party/libwebrtc/api/function_view_gn/moz.build
+++ b/third_party/libwebrtc/api/function_view_gn/moz.build
@@ -180,16 +180,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/ice_transport_interface.h b/third_party/libwebrtc/api/ice_transport_interface.h
index 001395c215..f4c7613fa8 100644
--- a/third_party/libwebrtc/api/ice_transport_interface.h
+++ b/third_party/libwebrtc/api/ice_transport_interface.h
@@ -14,11 +14,10 @@
#include <string>
#include "api/async_dns_resolver.h"
-#include "api/async_resolver_factory.h"
+#include "api/ref_count.h"
#include "api/rtc_error.h"
#include "api/rtc_event_log/rtc_event_log.h"
#include "api/scoped_refptr.h"
-#include "rtc_base/ref_count.h"
namespace cricket {
class IceTransportInternal;
@@ -33,7 +32,7 @@ class FieldTrialsView;
// An ICE transport, as represented to the outside world.
// This object is refcounted, and is therefore alive until the
// last holder has released it.
-class IceTransportInterface : public rtc::RefCountInterface {
+class IceTransportInterface : public webrtc::RefCountInterface {
public:
// Accessor for the internal representation of an ICE transport.
// The returned object can only be safely used on the signalling thread.
@@ -61,19 +60,8 @@ struct IceTransportInit final {
}
void set_async_dns_resolver_factory(
AsyncDnsResolverFactoryInterface* async_dns_resolver_factory) {
- RTC_DCHECK(!async_resolver_factory_);
async_dns_resolver_factory_ = async_dns_resolver_factory;
}
- [[deprecated("Use async_dns_resolver_factory")]] AsyncResolverFactory*
- async_resolver_factory() {
- return async_resolver_factory_;
- }
- ABSL_DEPRECATED("bugs.webrtc.org/12598")
- void set_async_resolver_factory(
- AsyncResolverFactory* async_resolver_factory) {
- RTC_DCHECK(!async_dns_resolver_factory_);
- async_resolver_factory_ = async_resolver_factory;
- }
RtcEventLog* event_log() { return event_log_; }
void set_event_log(RtcEventLog* event_log) { event_log_ = event_log; }
@@ -115,11 +103,6 @@ struct IceTransportInit final {
private:
cricket::PortAllocator* port_allocator_ = nullptr;
AsyncDnsResolverFactoryInterface* async_dns_resolver_factory_ = nullptr;
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
- // For backwards compatibility. Only one resolver factory can be set.
- AsyncResolverFactory* async_resolver_factory_ = nullptr;
-#pragma clang diagnostic pop
RtcEventLog* event_log_ = nullptr;
cricket::IceControllerFactoryInterface* ice_controller_factory_ = nullptr;
cricket::ActiveIceControllerFactoryInterface* active_ice_controller_factory_ =
diff --git a/third_party/libwebrtc/api/jsep.h b/third_party/libwebrtc/api/jsep.h
index d2aa57c784..643a99d315 100644
--- a/third_party/libwebrtc/api/jsep.h
+++ b/third_party/libwebrtc/api/jsep.h
@@ -27,8 +27,8 @@
#include <vector>
#include "absl/types/optional.h"
+#include "api/ref_count.h"
#include "api/rtc_error.h"
-#include "rtc_base/ref_count.h"
#include "rtc_base/system/rtc_export.h"
namespace cricket {
@@ -219,7 +219,7 @@ std::unique_ptr<SessionDescriptionInterface> CreateSessionDescription(
// CreateOffer and CreateAnswer callback interface.
class RTC_EXPORT CreateSessionDescriptionObserver
- : public rtc::RefCountInterface {
+ : public webrtc::RefCountInterface {
public:
// This callback transfers the ownership of the `desc`.
// TODO(deadbeef): Make this take an std::unique_ptr<> to avoid confusion
@@ -238,7 +238,8 @@ class RTC_EXPORT CreateSessionDescriptionObserver
};
// SetLocalDescription and SetRemoteDescription callback interface.
-class RTC_EXPORT SetSessionDescriptionObserver : public rtc::RefCountInterface {
+class RTC_EXPORT SetSessionDescriptionObserver
+ : public webrtc::RefCountInterface {
public:
virtual void OnSuccess() = 0;
// See description in CreateSessionDescriptionObserver for OnFailure.
diff --git a/third_party/libwebrtc/api/legacy_stats_types.h b/third_party/libwebrtc/api/legacy_stats_types.h
index e49cb6d6dd..70f21d4ad9 100644
--- a/third_party/libwebrtc/api/legacy_stats_types.h
+++ b/third_party/libwebrtc/api/legacy_stats_types.h
@@ -20,9 +20,9 @@
#include <string>
#include <vector>
+#include "api/ref_count.h"
#include "api/scoped_refptr.h"
#include "api/sequence_checker.h"
-#include "rtc_base/ref_count.h"
#include "rtc_base/system/rtc_export.h"
namespace webrtc {
@@ -240,7 +240,7 @@ class RTC_EXPORT StatsReport {
kStatsValueNameLocalCandidateRelayProtocol,
};
- class RTC_EXPORT IdBase : public rtc::RefCountInterface {
+ class RTC_EXPORT IdBase : public webrtc::RefCountInterface {
public:
~IdBase() override;
StatsType type() const;
diff --git a/third_party/libwebrtc/api/libjingle_logging_api_gn/moz.build b/third_party/libwebrtc/api/libjingle_logging_api_gn/moz.build
index 3a6a8537c2..b3a3b0ee1b 100644
--- a/third_party/libwebrtc/api/libjingle_logging_api_gn/moz.build
+++ b/third_party/libwebrtc/api/libjingle_logging_api_gn/moz.build
@@ -176,16 +176,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/libjingle_peerconnection_api_gn/moz.build b/third_party/libwebrtc/api/libjingle_peerconnection_api_gn/moz.build
index b4ff24a0b5..fbbb882ebf 100644
--- a/third_party/libwebrtc/api/libjingle_peerconnection_api_gn/moz.build
+++ b/third_party/libwebrtc/api/libjingle_peerconnection_api_gn/moz.build
@@ -176,16 +176,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/location_gn/moz.build b/third_party/libwebrtc/api/location_gn/moz.build
index 5e7de58f06..dcdca04077 100644
--- a/third_party/libwebrtc/api/location_gn/moz.build
+++ b/third_party/libwebrtc/api/location_gn/moz.build
@@ -176,16 +176,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/make_ref_counted_gn/moz.build b/third_party/libwebrtc/api/make_ref_counted_gn/moz.build
index 2eb6226bfb..f90e274916 100644
--- a/third_party/libwebrtc/api/make_ref_counted_gn/moz.build
+++ b/third_party/libwebrtc/api/make_ref_counted_gn/moz.build
@@ -176,16 +176,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/media_stream_interface.h b/third_party/libwebrtc/api/media_stream_interface.h
index 9d336739e4..4f9bffac85 100644
--- a/third_party/libwebrtc/api/media_stream_interface.h
+++ b/third_party/libwebrtc/api/media_stream_interface.h
@@ -23,6 +23,7 @@
#include "absl/types/optional.h"
#include "api/audio_options.h"
+#include "api/ref_count.h"
#include "api/scoped_refptr.h"
#include "api/video/recordable_encoded_frame.h"
#include "api/video/video_frame.h"
@@ -30,7 +31,6 @@
#include "api/video/video_source_interface.h"
#include "api/video_track_source_constraints.h"
#include "modules/audio_processing/include/audio_processing_statistics.h"
-#include "rtc_base/ref_count.h"
#include "rtc_base/system/rtc_export.h"
namespace webrtc {
@@ -54,7 +54,7 @@ class NotifierInterface {
// Base class for sources. A MediaStreamTrack has an underlying source that
// provides media. A source can be shared by multiple tracks.
-class RTC_EXPORT MediaSourceInterface : public rtc::RefCountInterface,
+class RTC_EXPORT MediaSourceInterface : public webrtc::RefCountInterface,
public NotifierInterface {
public:
enum SourceState { kInitializing, kLive, kEnded, kMuted };
@@ -69,7 +69,7 @@ class RTC_EXPORT MediaSourceInterface : public rtc::RefCountInterface,
// C++ version of MediaStreamTrack.
// See: https://www.w3.org/TR/mediacapture-streams/#mediastreamtrack
-class RTC_EXPORT MediaStreamTrackInterface : public rtc::RefCountInterface,
+class RTC_EXPORT MediaStreamTrackInterface : public webrtc::RefCountInterface,
public NotifierInterface {
public:
enum TrackState {
@@ -267,7 +267,7 @@ class RTC_EXPORT AudioSourceInterface : public MediaSourceInterface {
// Interface of the audio processor used by the audio track to collect
// statistics.
-class AudioProcessorInterface : public rtc::RefCountInterface {
+class AudioProcessorInterface : public webrtc::RefCountInterface {
public:
struct AudioProcessorStatistics {
bool typing_noise_detected = false;
@@ -321,7 +321,7 @@ typedef std::vector<rtc::scoped_refptr<VideoTrackInterface> > VideoTrackVector;
// must be pushed down.
//
// Thus, this interface acts as simply a container for tracks.
-class MediaStreamInterface : public rtc::RefCountInterface,
+class MediaStreamInterface : public webrtc::RefCountInterface,
public NotifierInterface {
public:
virtual std::string id() const = 0;
diff --git a/third_party/libwebrtc/api/media_stream_interface_gn/moz.build b/third_party/libwebrtc/api/media_stream_interface_gn/moz.build
index b066645cd2..759c156fb7 100644
--- a/third_party/libwebrtc/api/media_stream_interface_gn/moz.build
+++ b/third_party/libwebrtc/api/media_stream_interface_gn/moz.build
@@ -195,7 +195,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -205,10 +204,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/metronome/metronome.cc b/third_party/libwebrtc/api/metronome/metronome.cc
deleted file mode 100644
index 8d74f928a0..0000000000
--- a/third_party/libwebrtc/api/metronome/metronome.cc
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved.
- *
- * Use of this source code is governed by a BSD-style license
- * that can be found in the LICENSE file in the root of the source
- * tree. An additional intellectual property rights grant can be found
- * in the file PATENTS. All contributing project authors may
- * be found in the AUTHORS file in the root of the source tree.
- */
-
-#include "api/metronome/metronome.h"
-
-namespace webrtc {
-
-// TODO(crbug.com/1381982): Remove outdated methods.
-void Metronome::AddListener(TickListener* listener) {}
-void Metronome::RemoveListener(TickListener* listener) {}
-
-} // namespace webrtc
diff --git a/third_party/libwebrtc/api/metronome/metronome_gn/moz.build b/third_party/libwebrtc/api/metronome/metronome_gn/moz.build
index d89c86ef4b..ede22e4998 100644
--- a/third_party/libwebrtc/api/metronome/metronome_gn/moz.build
+++ b/third_party/libwebrtc/api/metronome/metronome_gn/moz.build
@@ -180,16 +180,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/neteq/default_neteq_controller_factory_gn/moz.build b/third_party/libwebrtc/api/neteq/default_neteq_controller_factory_gn/moz.build
index 75602ac512..5d18a89d19 100644
--- a/third_party/libwebrtc/api/neteq/default_neteq_controller_factory_gn/moz.build
+++ b/third_party/libwebrtc/api/neteq/default_neteq_controller_factory_gn/moz.build
@@ -199,7 +199,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -209,10 +208,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/neteq/neteq_api_gn/moz.build b/third_party/libwebrtc/api/neteq/neteq_api_gn/moz.build
index 7c4e1c550a..7ed92b8183 100644
--- a/third_party/libwebrtc/api/neteq/neteq_api_gn/moz.build
+++ b/third_party/libwebrtc/api/neteq/neteq_api_gn/moz.build
@@ -199,7 +199,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -209,10 +208,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/neteq/neteq_controller_api_gn/moz.build b/third_party/libwebrtc/api/neteq/neteq_controller_api_gn/moz.build
index 617ffd753e..c08d59e8ac 100644
--- a/third_party/libwebrtc/api/neteq/neteq_controller_api_gn/moz.build
+++ b/third_party/libwebrtc/api/neteq/neteq_controller_api_gn/moz.build
@@ -191,16 +191,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/neteq/tick_timer_gn/moz.build b/third_party/libwebrtc/api/neteq/tick_timer_gn/moz.build
index 8106b395ca..ef3938ecf2 100644
--- a/third_party/libwebrtc/api/neteq/tick_timer_gn/moz.build
+++ b/third_party/libwebrtc/api/neteq/tick_timer_gn/moz.build
@@ -188,7 +188,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -198,10 +197,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/network_state_predictor_api_gn/moz.build b/third_party/libwebrtc/api/network_state_predictor_api_gn/moz.build
index 0ffd6a2406..1cfd2c0837 100644
--- a/third_party/libwebrtc/api/network_state_predictor_api_gn/moz.build
+++ b/third_party/libwebrtc/api/network_state_predictor_api_gn/moz.build
@@ -176,16 +176,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/packet_socket_factory.h b/third_party/libwebrtc/api/packet_socket_factory.h
index e389ccb2ff..78b93b539c 100644
--- a/third_party/libwebrtc/api/packet_socket_factory.h
+++ b/third_party/libwebrtc/api/packet_socket_factory.h
@@ -16,7 +16,6 @@
#include <vector>
#include "api/async_dns_resolver.h"
-#include "api/wrapping_async_dns_resolver.h"
#include "rtc_base/async_packet_socket.h"
#include "rtc_base/proxy_info.h"
#include "rtc_base/system/rtc_export.h"
@@ -72,26 +71,8 @@ class RTC_EXPORT PacketSocketFactory {
const std::string& user_agent,
const PacketSocketTcpOptions& tcp_options) = 0;
- // The AsyncResolverInterface is deprecated; users are encouraged
- // to switch to the AsyncDnsResolverInterface.
- // TODO(bugs.webrtc.org/12598): Remove once all downstream users
- // are converted.
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
- [[deprecated]] virtual AsyncResolverInterface* CreateAsyncResolver() {
- // Default implementation, so that downstream users can remove this
- // immediately after changing to CreateAsyncDnsResolver
- RTC_DCHECK_NOTREACHED();
- return nullptr;
- }
-
virtual std::unique_ptr<webrtc::AsyncDnsResolverInterface>
- CreateAsyncDnsResolver() {
- // Default implementation, to aid in transition to AsyncDnsResolverInterface
- return std::make_unique<webrtc::WrappingAsyncDnsResolver>(
- CreateAsyncResolver());
- }
-#pragma clang diagnostic pop
+ CreateAsyncDnsResolver() = 0;
private:
PacketSocketFactory(const PacketSocketFactory&) = delete;
diff --git a/third_party/libwebrtc/api/peer_connection_interface.cc b/third_party/libwebrtc/api/peer_connection_interface.cc
index 050b61da95..dedfd355d6 100644
--- a/third_party/libwebrtc/api/peer_connection_interface.cc
+++ b/third_party/libwebrtc/api/peer_connection_interface.cc
@@ -12,6 +12,8 @@
#include <utility>
+#include "pc/media_factory.h"
+
namespace webrtc {
PeerConnectionInterface::IceServer::IceServer() = default;
diff --git a/third_party/libwebrtc/api/peer_connection_interface.h b/third_party/libwebrtc/api/peer_connection_interface.h
index 9243190c10..74c4702cd2 100644
--- a/third_party/libwebrtc/api/peer_connection_interface.h
+++ b/third_party/libwebrtc/api/peer_connection_interface.h
@@ -80,12 +80,10 @@
#include "absl/types/optional.h"
#include "api/adaptation/resource.h"
#include "api/async_dns_resolver.h"
-#include "api/async_resolver_factory.h"
#include "api/audio/audio_mixer.h"
#include "api/audio_codecs/audio_decoder_factory.h"
#include "api/audio_codecs/audio_encoder_factory.h"
#include "api/audio_options.h"
-#include "api/call/call_factory_interface.h"
#include "api/candidate.h"
#include "api/crypto/crypto_options.h"
#include "api/data_channel_interface.h"
@@ -120,17 +118,19 @@
#include "api/transport/sctp_transport_factory_interface.h"
#include "api/turn_customizer.h"
#include "api/video/video_bitrate_allocator_factory.h"
+#include "api/video_codecs/video_decoder_factory.h"
+#include "api/video_codecs/video_encoder_factory.h"
#include "call/rtp_transport_controller_send_factory_interface.h"
#include "media/base/media_config.h"
#include "media/base/media_engine.h"
// TODO(bugs.webrtc.org/7447): We plan to provide a way to let applications
// inject a PacketSocketFactory and/or NetworkManager, and not expose
// PortAllocator in the PeerConnection api.
+#include "api/ref_count.h"
#include "p2p/base/port_allocator.h"
#include "rtc_base/network.h"
#include "rtc_base/network_constants.h"
#include "rtc_base/network_monitor_factory.h"
-#include "rtc_base/ref_count.h"
#include "rtc_base/rtc_certificate.h"
#include "rtc_base/rtc_certificate_generator.h"
#include "rtc_base/socket_address.h"
@@ -145,8 +145,11 @@ class Thread;
namespace webrtc {
+// MediaFactory class definition is not part of the api.
+class MediaFactory;
+
// MediaStream container interface.
-class StreamCollectionInterface : public rtc::RefCountInterface {
+class StreamCollectionInterface : public webrtc::RefCountInterface {
public:
// TODO(ronghuawu): Update the function names to c++ style, e.g. find -> Find.
virtual size_t count() = 0;
@@ -160,7 +163,7 @@ class StreamCollectionInterface : public rtc::RefCountInterface {
~StreamCollectionInterface() override = default;
};
-class StatsObserver : public rtc::RefCountInterface {
+class StatsObserver : public webrtc::RefCountInterface {
public:
virtual void OnComplete(const StatsReports& reports) = 0;
@@ -175,7 +178,7 @@ enum class SdpSemantics {
kUnifiedPlan,
};
-class RTC_EXPORT PeerConnectionInterface : public rtc::RefCountInterface {
+class RTC_EXPORT PeerConnectionInterface : public webrtc::RefCountInterface {
public:
// See https://w3c.github.io/webrtc-pc/#dom-rtcsignalingstate
enum SignalingState {
@@ -683,6 +686,7 @@ class RTC_EXPORT PeerConnectionInterface : public rtc::RefCountInterface {
PortAllocatorConfig port_allocator_config;
// The burst interval of the pacer, see TaskQueuePacedSender constructor.
+ // TODO(hbos): Deprecated, Remove once Chromium is not setting it.
absl::optional<TimeDelta> pacer_burst_interval;
//
@@ -1382,13 +1386,6 @@ struct RTC_EXPORT PeerConnectionDependencies final {
// Factory for creating resolvers that look up hostnames in DNS
std::unique_ptr<webrtc::AsyncDnsResolverFactoryInterface>
async_dns_resolver_factory;
- // Deprecated - use async_dns_resolver_factory
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
- [[deprecated("Use async_dns_resolver_factory")]] std::unique_ptr<
- webrtc::AsyncResolverFactory>
- async_resolver_factory;
-#pragma clang diagnostic pop
std::unique_ptr<webrtc::IceTransportFactory> ice_transport_factory;
std::unique_ptr<rtc::RTCCertificateGeneratorInterface> cert_generator;
std::unique_ptr<rtc::SSLCertificateVerifier> tls_cert_verifier;
@@ -1427,8 +1424,6 @@ struct RTC_EXPORT PeerConnectionFactoryDependencies final {
// called without a `port_allocator`.
std::unique_ptr<rtc::PacketSocketFactory> packet_socket_factory;
std::unique_ptr<TaskQueueFactory> task_queue_factory;
- std::unique_ptr<cricket::MediaEngineInterface> media_engine;
- std::unique_ptr<CallFactoryInterface> call_factory;
std::unique_ptr<RtcEventLogFactoryInterface> event_log_factory;
std::unique_ptr<FecControllerFactoryInterface> fec_controller_factory;
std::unique_ptr<NetworkStatePredictorFactoryInterface>
@@ -1447,6 +1442,23 @@ struct RTC_EXPORT PeerConnectionFactoryDependencies final {
std::unique_ptr<RtpTransportControllerSendFactoryInterface>
transport_controller_send_factory;
std::unique_ptr<Metronome> metronome;
+
+ // Media specific dependencies. Unused when `media_factory == nullptr`.
+ rtc::scoped_refptr<AudioDeviceModule> adm;
+ rtc::scoped_refptr<AudioEncoderFactory> audio_encoder_factory;
+ rtc::scoped_refptr<AudioDecoderFactory> audio_decoder_factory;
+ rtc::scoped_refptr<AudioMixer> audio_mixer;
+ rtc::scoped_refptr<AudioProcessing> audio_processing;
+ std::unique_ptr<AudioFrameProcessor> audio_frame_processor;
+ std::unique_ptr<VideoEncoderFactory> video_encoder_factory;
+ std::unique_ptr<VideoDecoderFactory> video_decoder_factory;
+
+ // The `media_factory` members allows webrtc to be optionally built without
+ // media support (i.e., if only being used for data channels).
+ // By default media is disabled. To enable media call
+ // `EnableMedia(PeerConnectionFactoryDependencies&)`. Definition of the
+ // `MediaFactory` interface is a webrtc implementation detail.
+ std::unique_ptr<MediaFactory> media_factory;
};
// PeerConnectionFactoryInterface is the factory interface used for creating
@@ -1463,7 +1475,7 @@ struct RTC_EXPORT PeerConnectionFactoryDependencies final {
// CreatePeerConnectionFactory method which accepts threads as input, and use
// the CreatePeerConnection version that takes a PortAllocator as an argument.
class RTC_EXPORT PeerConnectionFactoryInterface
- : public rtc::RefCountInterface {
+ : public webrtc::RefCountInterface {
public:
class Options {
public:
diff --git a/third_party/libwebrtc/api/priority_gn/moz.build b/third_party/libwebrtc/api/priority_gn/moz.build
index 090d23dd76..5cac655d90 100644
--- a/third_party/libwebrtc/api/priority_gn/moz.build
+++ b/third_party/libwebrtc/api/priority_gn/moz.build
@@ -176,16 +176,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/ref_count.h b/third_party/libwebrtc/api/ref_count.h
new file mode 100644
index 0000000000..5209277038
--- /dev/null
+++ b/third_party/libwebrtc/api/ref_count.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2011 The WebRTC Project Authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+#ifndef API_REF_COUNT_H_
+#define API_REF_COUNT_H_
+
+namespace webrtc {
+
+// Refcounted objects should implement the following informal interface:
+//
+// void AddRef() const ;
+// RefCountReleaseStatus Release() const;
+//
+// You may access members of a reference-counted object, including the AddRef()
+// and Release() methods, only if you already own a reference to it, or if
+// you're borrowing someone else's reference. (A newly created object is a
+// special case: the reference count is zero on construction, and the code that
+// creates the object should immediately call AddRef(), bringing the reference
+// count from zero to one, e.g., by constructing an rtc::scoped_refptr).
+//
+// AddRef() creates a new reference to the object.
+//
+// Release() releases a reference to the object; the caller now has one less
+// reference than before the call. Returns kDroppedLastRef if the number of
+// references dropped to zero because of this (in which case the object destroys
+// itself). Otherwise, returns kOtherRefsRemained, to signal that at the precise
+// time the caller's reference was dropped, other references still remained (but
+// if other threads own references, this may of course have changed by the time
+// Release() returns).
+//
+// The caller of Release() must treat it in the same way as a delete operation:
+// Regardless of the return value from Release(), the caller mustn't access the
+// object. The object might still be alive, due to references held by other
+// users of the object, but the object can go away at any time, e.g., as the
+// result of another thread calling Release().
+//
+// Calling AddRef() and Release() manually is discouraged. It's recommended to
+// use rtc::scoped_refptr to manage all pointers to reference counted objects.
+// Note that rtc::scoped_refptr depends on compile-time duck-typing; formally
+// implementing the below RefCountInterface is not required.
+
+enum class RefCountReleaseStatus { kDroppedLastRef, kOtherRefsRemained };
+
+// Interfaces where refcounting is part of the public api should
+// inherit this abstract interface. The implementation of these
+// methods is usually provided by the RefCountedObject template class,
+// applied as a leaf in the inheritance tree.
+class RefCountInterface {
+ public:
+ virtual void AddRef() const = 0;
+ virtual RefCountReleaseStatus Release() const = 0;
+
+ // Non-public destructor, because Release() has exclusive responsibility for
+ // destroying the object.
+ protected:
+ virtual ~RefCountInterface() {}
+};
+
+} // namespace webrtc
+
+#endif // API_REF_COUNT_H_
diff --git a/third_party/libwebrtc/api/ref_count_gn/moz.build b/third_party/libwebrtc/api/ref_count_gn/moz.build
new file mode 100644
index 0000000000..0fcde97e6b
--- /dev/null
+++ b/third_party/libwebrtc/api/ref_count_gn/moz.build
@@ -0,0 +1,198 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+ ### This moz.build was AUTOMATICALLY GENERATED from a GN config, ###
+ ### DO NOT edit it by hand. ###
+
+COMPILE_FLAGS["OS_INCLUDES"] = []
+AllowCompilerWarnings()
+
+DEFINES["ABSL_ALLOCATOR_NOTHROW"] = "1"
+DEFINES["RTC_DAV1D_IN_INTERNAL_DECODER_FACTORY"] = True
+DEFINES["RTC_ENABLE_VP9"] = True
+DEFINES["WEBRTC_ENABLE_PROTOBUF"] = "0"
+DEFINES["WEBRTC_LIBRARY_IMPL"] = True
+DEFINES["WEBRTC_MOZILLA_BUILD"] = True
+DEFINES["WEBRTC_NON_STATIC_TRACE_EVENT_HANDLERS"] = "0"
+DEFINES["WEBRTC_STRICT_FIELD_TRIALS"] = "0"
+
+FINAL_LIBRARY = "webrtc"
+
+
+LOCAL_INCLUDES += [
+ "!/ipc/ipdl/_ipdlheaders",
+ "!/third_party/libwebrtc/gen",
+ "/ipc/chromium/src",
+ "/third_party/libwebrtc/",
+ "/third_party/libwebrtc/third_party/abseil-cpp/",
+ "/tools/profiler/public"
+]
+
+if not CONFIG["MOZ_DEBUG"]:
+
+ DEFINES["DYNAMIC_ANNOTATIONS_ENABLED"] = "0"
+ DEFINES["NDEBUG"] = True
+ DEFINES["NVALGRIND"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1":
+
+ DEFINES["DYNAMIC_ANNOTATIONS_ENABLED"] = "1"
+
+if CONFIG["OS_TARGET"] == "Android":
+
+ DEFINES["ANDROID"] = True
+ DEFINES["ANDROID_NDK_VERSION_ROLL"] = "r22_1"
+ DEFINES["HAVE_SYS_UIO_H"] = True
+ DEFINES["WEBRTC_ANDROID"] = True
+ DEFINES["WEBRTC_ANDROID_OPENSLES"] = True
+ DEFINES["WEBRTC_ENABLE_LIBEVENT"] = True
+ DEFINES["WEBRTC_LINUX"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_GNU_SOURCE"] = True
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+if CONFIG["OS_TARGET"] == "Darwin":
+
+ DEFINES["WEBRTC_MAC"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_LIBCPP_HAS_NO_ALIGNED_ALLOCATION"] = True
+ DEFINES["__ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES"] = "0"
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+if CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["USE_AURA"] = "1"
+ DEFINES["USE_GLIB"] = "1"
+ DEFINES["USE_NSS_CERTS"] = "1"
+ DEFINES["USE_OZONE"] = "1"
+ DEFINES["USE_UDEV"] = True
+ DEFINES["WEBRTC_ENABLE_LIBEVENT"] = True
+ DEFINES["WEBRTC_LINUX"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_FILE_OFFSET_BITS"] = "64"
+ DEFINES["_LARGEFILE64_SOURCE"] = True
+ DEFINES["_LARGEFILE_SOURCE"] = True
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+if CONFIG["OS_TARGET"] == "OpenBSD":
+
+ DEFINES["USE_GLIB"] = "1"
+ DEFINES["USE_OZONE"] = "1"
+ DEFINES["USE_X11"] = "1"
+ DEFINES["WEBRTC_BSD"] = True
+ DEFINES["WEBRTC_ENABLE_LIBEVENT"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_FILE_OFFSET_BITS"] = "64"
+ DEFINES["_LARGEFILE64_SOURCE"] = True
+ DEFINES["_LARGEFILE_SOURCE"] = True
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+if CONFIG["OS_TARGET"] == "WINNT":
+
+ DEFINES["CERT_CHAIN_PARA_HAS_EXTRA_FIELDS"] = True
+ DEFINES["NOMINMAX"] = True
+ DEFINES["NTDDI_VERSION"] = "0x0A000000"
+ DEFINES["PSAPI_VERSION"] = "2"
+ DEFINES["RTC_ENABLE_WIN_WGC"] = True
+ DEFINES["UNICODE"] = True
+ DEFINES["USE_AURA"] = "1"
+ DEFINES["WEBRTC_WIN"] = True
+ DEFINES["WIN32"] = True
+ DEFINES["WIN32_LEAN_AND_MEAN"] = True
+ DEFINES["WINAPI_FAMILY"] = "WINAPI_FAMILY_DESKTOP_APP"
+ DEFINES["WINVER"] = "0x0A00"
+ DEFINES["_ATL_NO_OPENGL"] = True
+ DEFINES["_CRT_RAND_S"] = True
+ DEFINES["_CRT_SECURE_NO_DEPRECATE"] = True
+ DEFINES["_ENABLE_EXTENDED_ALIGNED_STORAGE"] = True
+ DEFINES["_HAS_EXCEPTIONS"] = "0"
+ DEFINES["_HAS_NODISCARD"] = True
+ DEFINES["_SCL_SECURE_NO_DEPRECATE"] = True
+ DEFINES["_SECURE_ATL"] = True
+ DEFINES["_UNICODE"] = True
+ DEFINES["_WIN32_WINNT"] = "0x0A00"
+ DEFINES["_WINDOWS"] = True
+ DEFINES["__STD_C"] = True
+
+if CONFIG["TARGET_CPU"] == "aarch64":
+
+ DEFINES["WEBRTC_ARCH_ARM64"] = True
+ DEFINES["WEBRTC_HAS_NEON"] = True
+
+if CONFIG["TARGET_CPU"] == "arm":
+
+ DEFINES["WEBRTC_ARCH_ARM"] = True
+ DEFINES["WEBRTC_ARCH_ARM_V7"] = True
+ DEFINES["WEBRTC_HAS_NEON"] = True
+
+if CONFIG["TARGET_CPU"] == "mips32":
+
+ DEFINES["MIPS32_LE"] = True
+ DEFINES["MIPS_FPU_LE"] = True
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["TARGET_CPU"] == "mips64":
+
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["TARGET_CPU"] == "x86":
+
+ DEFINES["WEBRTC_ENABLE_AVX2"] = True
+
+if CONFIG["TARGET_CPU"] == "x86_64":
+
+ DEFINES["WEBRTC_ENABLE_AVX2"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "Android":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "Darwin":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "OpenBSD":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "WINNT":
+
+ DEFINES["_HAS_ITERATOR_DEBUGGING"] = "0"
+
+if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["USE_X11"] = "1"
+
+if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
+
+ OS_LIBS += [
+ "unwind"
+ ]
+
+if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
+
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "arm":
+
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "x86":
+
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "x86_64":
+
+ DEFINES["_GNU_SOURCE"] = True
+
+Library("ref_count_gn")
diff --git a/third_party/libwebrtc/api/refcountedbase_gn/moz.build b/third_party/libwebrtc/api/refcountedbase_gn/moz.build
index c18c17de99..96c04d1258 100644
--- a/third_party/libwebrtc/api/refcountedbase_gn/moz.build
+++ b/third_party/libwebrtc/api/refcountedbase_gn/moz.build
@@ -176,16 +176,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/rtc_error_gn/moz.build b/third_party/libwebrtc/api/rtc_error_gn/moz.build
index 19bee50072..3c9d5f7f9b 100644
--- a/third_party/libwebrtc/api/rtc_error_gn/moz.build
+++ b/third_party/libwebrtc/api/rtc_error_gn/moz.build
@@ -195,7 +195,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -205,10 +204,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/rtc_event_log/BUILD.gn b/third_party/libwebrtc/api/rtc_event_log/BUILD.gn
index 158dc06a7b..073c91a5e8 100644
--- a/third_party/libwebrtc/api/rtc_event_log/BUILD.gn
+++ b/third_party/libwebrtc/api/rtc_event_log/BUILD.gn
@@ -22,8 +22,10 @@ rtc_library("rtc_event_log") {
"..:libjingle_logging_api",
"../../rtc_base:checks",
"../../rtc_base:timeutils",
+ "../environment",
"../task_queue",
]
+ absl_deps = [ "//third_party/abseil-cpp/absl/base:nullability" ]
}
rtc_library("rtc_event_log_factory") {
@@ -35,12 +37,14 @@ rtc_library("rtc_event_log_factory") {
deps = [
":rtc_event_log",
- "../../rtc_base:checks",
+ "..:field_trials_view",
"../../rtc_base/system:rtc_export",
- "../../system_wrappers:field_trial",
+ "../environment",
"../task_queue",
]
+ absl_deps = [ "//third_party/abseil-cpp/absl/base:nullability" ]
+
if (rtc_enable_protobuf) {
defines = [ "WEBRTC_ENABLE_RTC_EVENT_LOG" ]
deps += [ "../../logging:rtc_event_log_impl" ]
diff --git a/third_party/libwebrtc/api/rtc_event_log/rtc_event_log_factory.cc b/third_party/libwebrtc/api/rtc_event_log/rtc_event_log_factory.cc
index a3cb68cf54..30fc6f126f 100644
--- a/third_party/libwebrtc/api/rtc_event_log/rtc_event_log_factory.cc
+++ b/third_party/libwebrtc/api/rtc_event_log/rtc_event_log_factory.cc
@@ -11,10 +11,11 @@
#include "api/rtc_event_log/rtc_event_log_factory.h"
#include <memory>
-#include <utility>
-#include "rtc_base/checks.h"
-#include "system_wrappers/include/field_trial.h"
+#include "absl/base/nullability.h"
+#include "api/environment/environment.h"
+#include "api/field_trials_view.h"
+#include "api/rtc_event_log/rtc_event_log.h"
#ifdef WEBRTC_ENABLE_RTC_EVENT_LOG
#include "logging/rtc_event_log/rtc_event_log_impl.h"
@@ -22,27 +23,21 @@
namespace webrtc {
-RtcEventLogFactory::RtcEventLogFactory(TaskQueueFactory* task_queue_factory)
- : task_queue_factory_(task_queue_factory) {
- RTC_DCHECK(task_queue_factory_);
-}
-
-std::unique_ptr<RtcEventLog> RtcEventLogFactory::Create(
- RtcEventLog::EncodingType encoding_type) const {
-#ifdef WEBRTC_ENABLE_RTC_EVENT_LOG
- if (field_trial::IsEnabled("WebRTC-RtcEventLogKillSwitch")) {
+absl::Nonnull<std::unique_ptr<RtcEventLog>> RtcEventLogFactory::Create(
+ const Environment& env) const {
+#ifndef WEBRTC_ENABLE_RTC_EVENT_LOG
+ return std::make_unique<RtcEventLogNull>();
+#else
+ if (env.field_trials().IsEnabled("WebRTC-RtcEventLogKillSwitch")) {
return std::make_unique<RtcEventLogNull>();
}
+ RtcEventLog::EncodingType encoding_type =
+ env.field_trials().IsDisabled("WebRTC-RtcEventLogNewFormat")
+ ? RtcEventLog::EncodingType::Legacy
+ : RtcEventLog::EncodingType::NewFormat;
return std::make_unique<RtcEventLogImpl>(
- RtcEventLogImpl::CreateEncoder(encoding_type), task_queue_factory_);
-#else
- return std::make_unique<RtcEventLogNull>();
+ RtcEventLogImpl::CreateEncoder(encoding_type), &env.task_queue_factory());
#endif
}
-std::unique_ptr<RtcEventLog> RtcEventLogFactory::CreateRtcEventLog(
- RtcEventLog::EncodingType encoding_type) {
- return Create(encoding_type);
-}
-
} // namespace webrtc
diff --git a/third_party/libwebrtc/api/rtc_event_log/rtc_event_log_factory.h b/third_party/libwebrtc/api/rtc_event_log/rtc_event_log_factory.h
index fd1db3c728..21a670e1a7 100644
--- a/third_party/libwebrtc/api/rtc_event_log/rtc_event_log_factory.h
+++ b/third_party/libwebrtc/api/rtc_event_log/rtc_event_log_factory.h
@@ -13,6 +13,8 @@
#include <memory>
+#include "absl/base/nullability.h"
+#include "api/environment/environment.h"
#include "api/rtc_event_log/rtc_event_log.h"
#include "api/rtc_event_log/rtc_event_log_factory_interface.h"
#include "api/task_queue/task_queue_factory.h"
@@ -22,16 +24,16 @@ namespace webrtc {
class RTC_EXPORT RtcEventLogFactory : public RtcEventLogFactoryInterface {
public:
- explicit RtcEventLogFactory(TaskQueueFactory* task_queue_factory);
- ~RtcEventLogFactory() override {}
+ RtcEventLogFactory() = default;
- std::unique_ptr<RtcEventLog> Create(
- RtcEventLog::EncodingType encoding_type) const override;
- std::unique_ptr<RtcEventLog> CreateRtcEventLog(
- RtcEventLog::EncodingType encoding_type) override;
+ // TODO(bugs.webrtc.org/15656): deprecate and delete constructor taking
+ // task queue factory in favor of using task queue factory provided through
+ // the Environment parameter in Create function.
+ explicit RtcEventLogFactory(TaskQueueFactory* task_queue_factory) {}
+ ~RtcEventLogFactory() override = default;
- private:
- TaskQueueFactory* const task_queue_factory_;
+ absl::Nonnull<std::unique_ptr<RtcEventLog>> Create(
+ const Environment& env) const override;
};
} // namespace webrtc
diff --git a/third_party/libwebrtc/api/rtc_event_log/rtc_event_log_factory_interface.h b/third_party/libwebrtc/api/rtc_event_log/rtc_event_log_factory_interface.h
index a6f4dee92f..3135584966 100644
--- a/third_party/libwebrtc/api/rtc_event_log/rtc_event_log_factory_interface.h
+++ b/third_party/libwebrtc/api/rtc_event_log/rtc_event_log_factory_interface.h
@@ -13,6 +13,8 @@
#include <memory>
+#include "absl/base/nullability.h"
+#include "api/environment/environment.h"
#include "api/rtc_event_log/rtc_event_log.h"
namespace webrtc {
@@ -24,10 +26,8 @@ class RtcEventLogFactoryInterface {
public:
virtual ~RtcEventLogFactoryInterface() = default;
- virtual std::unique_ptr<RtcEventLog> Create(
- RtcEventLog::EncodingType encoding_type) const = 0;
- [[deprecated]] virtual std::unique_ptr<RtcEventLog> CreateRtcEventLog(
- RtcEventLog::EncodingType encoding_type) = 0;
+ virtual absl::Nonnull<std::unique_ptr<RtcEventLog>> Create(
+ const Environment& env) const = 0;
};
} // namespace webrtc
diff --git a/third_party/libwebrtc/api/rtc_event_log/rtc_event_log_gn/moz.build b/third_party/libwebrtc/api/rtc_event_log/rtc_event_log_gn/moz.build
index 1965bc7a12..991c0366b6 100644
--- a/third_party/libwebrtc/api/rtc_event_log/rtc_event_log_gn/moz.build
+++ b/third_party/libwebrtc/api/rtc_event_log/rtc_event_log_gn/moz.build
@@ -196,7 +196,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -206,10 +205,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/rtp_headers_gn/moz.build b/third_party/libwebrtc/api/rtp_headers_gn/moz.build
index 7328eb81c0..24f4f8fb24 100644
--- a/third_party/libwebrtc/api/rtp_headers_gn/moz.build
+++ b/third_party/libwebrtc/api/rtp_headers_gn/moz.build
@@ -195,7 +195,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -205,10 +204,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/rtp_packet_info_gn/moz.build b/third_party/libwebrtc/api/rtp_packet_info_gn/moz.build
index 92eb6cb369..fda93e184e 100644
--- a/third_party/libwebrtc/api/rtp_packet_info_gn/moz.build
+++ b/third_party/libwebrtc/api/rtp_packet_info_gn/moz.build
@@ -195,7 +195,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -205,10 +204,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/rtp_parameters_gn/moz.build b/third_party/libwebrtc/api/rtp_parameters_gn/moz.build
index 480114ad07..09590e547d 100644
--- a/third_party/libwebrtc/api/rtp_parameters_gn/moz.build
+++ b/third_party/libwebrtc/api/rtp_parameters_gn/moz.build
@@ -189,7 +189,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -199,10 +198,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/rtp_receiver_interface.h b/third_party/libwebrtc/api/rtp_receiver_interface.h
index e4ec9b5986..0bf1af972b 100644
--- a/third_party/libwebrtc/api/rtp_receiver_interface.h
+++ b/third_party/libwebrtc/api/rtp_receiver_interface.h
@@ -22,10 +22,10 @@
#include "api/frame_transformer_interface.h"
#include "api/media_stream_interface.h"
#include "api/media_types.h"
+#include "api/ref_count.h"
#include "api/rtp_parameters.h"
#include "api/scoped_refptr.h"
#include "api/transport/rtp/rtp_source.h"
-#include "rtc_base/ref_count.h"
#include "rtc_base/system/rtc_export.h"
namespace webrtc {
@@ -44,7 +44,7 @@ class RtpReceiverObserverInterface {
virtual ~RtpReceiverObserverInterface() {}
};
-class RTC_EXPORT RtpReceiverInterface : public rtc::RefCountInterface {
+class RTC_EXPORT RtpReceiverInterface : public webrtc::RefCountInterface {
public:
virtual rtc::scoped_refptr<MediaStreamTrackInterface> track() const = 0;
diff --git a/third_party/libwebrtc/api/rtp_sender_interface.h b/third_party/libwebrtc/api/rtp_sender_interface.h
index 619b601f1f..7090c233cd 100644
--- a/third_party/libwebrtc/api/rtp_sender_interface.h
+++ b/third_party/libwebrtc/api/rtp_sender_interface.h
@@ -25,18 +25,18 @@
#include "api/frame_transformer_interface.h"
#include "api/media_stream_interface.h"
#include "api/media_types.h"
+#include "api/ref_count.h"
#include "api/rtc_error.h"
#include "api/rtp_parameters.h"
#include "api/scoped_refptr.h"
#include "api/video_codecs/video_encoder_factory.h"
-#include "rtc_base/ref_count.h"
#include "rtc_base/system/rtc_export.h"
#include "api/rtp_sender_setparameters_callback.h"
namespace webrtc {
-class RTC_EXPORT RtpSenderInterface : public rtc::RefCountInterface {
+class RTC_EXPORT RtpSenderInterface : public webrtc::RefCountInterface {
public:
// Returns true if successful in setting the track.
// Fails if an audio track is set on a video RtpSender, or vice-versa.
diff --git a/third_party/libwebrtc/api/rtp_sender_interface_gn/moz.build b/third_party/libwebrtc/api/rtp_sender_interface_gn/moz.build
index 9938ecca35..e7259b40e0 100644
--- a/third_party/libwebrtc/api/rtp_sender_interface_gn/moz.build
+++ b/third_party/libwebrtc/api/rtp_sender_interface_gn/moz.build
@@ -176,16 +176,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/rtp_sender_setparameters_callback_gn/moz.build b/third_party/libwebrtc/api/rtp_sender_setparameters_callback_gn/moz.build
index f8cd38576d..c493f010c8 100644
--- a/third_party/libwebrtc/api/rtp_sender_setparameters_callback_gn/moz.build
+++ b/third_party/libwebrtc/api/rtp_sender_setparameters_callback_gn/moz.build
@@ -195,7 +195,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -205,10 +204,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/rtp_transceiver_direction_gn/moz.build b/third_party/libwebrtc/api/rtp_transceiver_direction_gn/moz.build
index 9f3a7424ae..66b863defb 100644
--- a/third_party/libwebrtc/api/rtp_transceiver_direction_gn/moz.build
+++ b/third_party/libwebrtc/api/rtp_transceiver_direction_gn/moz.build
@@ -176,16 +176,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/rtp_transceiver_interface.h b/third_party/libwebrtc/api/rtp_transceiver_interface.h
index 7d0d1a18bf..940264ef51 100644
--- a/third_party/libwebrtc/api/rtp_transceiver_interface.h
+++ b/third_party/libwebrtc/api/rtp_transceiver_interface.h
@@ -18,12 +18,12 @@
#include "absl/types/optional.h"
#include "api/array_view.h"
#include "api/media_types.h"
+#include "api/ref_count.h"
#include "api/rtp_parameters.h"
#include "api/rtp_receiver_interface.h"
#include "api/rtp_sender_interface.h"
#include "api/rtp_transceiver_direction.h"
#include "api/scoped_refptr.h"
-#include "rtc_base/ref_count.h"
#include "rtc_base/system/rtc_export.h"
namespace webrtc {
@@ -58,7 +58,7 @@ struct RTC_EXPORT RtpTransceiverInit final {
//
// WebRTC specification for RTCRtpTransceiver, the JavaScript analog:
// https://w3c.github.io/webrtc-pc/#dom-rtcrtptransceiver
-class RTC_EXPORT RtpTransceiverInterface : public rtc::RefCountInterface {
+class RTC_EXPORT RtpTransceiverInterface : public webrtc::RefCountInterface {
public:
// Media type of the transceiver. Any sender(s)/receiver(s) will have this
// type as well.
diff --git a/third_party/libwebrtc/api/scoped_refptr.h b/third_party/libwebrtc/api/scoped_refptr.h
index e145509127..61b2eb1f20 100644
--- a/third_party/libwebrtc/api/scoped_refptr.h
+++ b/third_party/libwebrtc/api/scoped_refptr.h
@@ -66,7 +66,7 @@
#include <memory>
#include <utility>
-namespace rtc {
+namespace webrtc {
template <class T>
class scoped_refptr {
@@ -162,61 +162,66 @@ class scoped_refptr {
};
template <typename T, typename U>
-bool operator==(const rtc::scoped_refptr<T>& a,
- const rtc::scoped_refptr<U>& b) {
+bool operator==(const scoped_refptr<T>& a, const scoped_refptr<U>& b) {
return a.get() == b.get();
}
template <typename T, typename U>
-bool operator!=(const rtc::scoped_refptr<T>& a,
- const rtc::scoped_refptr<U>& b) {
+bool operator!=(const scoped_refptr<T>& a, const scoped_refptr<U>& b) {
return !(a == b);
}
template <typename T>
-bool operator==(const rtc::scoped_refptr<T>& a, std::nullptr_t) {
+bool operator==(const scoped_refptr<T>& a, std::nullptr_t) {
return a.get() == nullptr;
}
template <typename T>
-bool operator!=(const rtc::scoped_refptr<T>& a, std::nullptr_t) {
+bool operator!=(const scoped_refptr<T>& a, std::nullptr_t) {
return !(a == nullptr);
}
template <typename T>
-bool operator==(std::nullptr_t, const rtc::scoped_refptr<T>& a) {
+bool operator==(std::nullptr_t, const scoped_refptr<T>& a) {
return a.get() == nullptr;
}
template <typename T>
-bool operator!=(std::nullptr_t, const rtc::scoped_refptr<T>& a) {
+bool operator!=(std::nullptr_t, const scoped_refptr<T>& a) {
return !(a == nullptr);
}
// Comparison with raw pointer.
template <typename T, typename U>
-bool operator==(const rtc::scoped_refptr<T>& a, const U* b) {
+bool operator==(const scoped_refptr<T>& a, const U* b) {
return a.get() == b;
}
template <typename T, typename U>
-bool operator!=(const rtc::scoped_refptr<T>& a, const U* b) {
+bool operator!=(const scoped_refptr<T>& a, const U* b) {
return !(a == b);
}
template <typename T, typename U>
-bool operator==(const T* a, const rtc::scoped_refptr<U>& b) {
+bool operator==(const T* a, const scoped_refptr<U>& b) {
return a == b.get();
}
template <typename T, typename U>
-bool operator!=(const T* a, const rtc::scoped_refptr<U>& b) {
+bool operator!=(const T* a, const scoped_refptr<U>& b) {
return !(a == b);
}
// Ordered comparison, needed for use as a std::map key.
template <typename T, typename U>
-bool operator<(const rtc::scoped_refptr<T>& a, const rtc::scoped_refptr<U>& b) {
+bool operator<(const scoped_refptr<T>& a, const scoped_refptr<U>& b) {
return a.get() < b.get();
}
+} // namespace webrtc
+
+namespace rtc {
+// Backwards compatible alias.
+// TODO(bugs.webrtc.org/15622): Deprecate and remove.
+template <typename T>
+using scoped_refptr = webrtc::scoped_refptr<T>;
} // namespace rtc
#endif // API_SCOPED_REFPTR_H_
diff --git a/third_party/libwebrtc/api/scoped_refptr_gn/moz.build b/third_party/libwebrtc/api/scoped_refptr_gn/moz.build
index c2f167533d..6593008ce6 100644
--- a/third_party/libwebrtc/api/scoped_refptr_gn/moz.build
+++ b/third_party/libwebrtc/api/scoped_refptr_gn/moz.build
@@ -176,16 +176,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/sctp_transport_interface.h b/third_party/libwebrtc/api/sctp_transport_interface.h
index 7080889fcf..23169ddb2b 100644
--- a/third_party/libwebrtc/api/sctp_transport_interface.h
+++ b/third_party/libwebrtc/api/sctp_transport_interface.h
@@ -13,9 +13,9 @@
#include "absl/types/optional.h"
#include "api/dtls_transport_interface.h"
+#include "api/ref_count.h"
#include "api/rtc_error.h"
#include "api/scoped_refptr.h"
-#include "rtc_base/ref_count.h"
namespace webrtc {
@@ -75,7 +75,7 @@ class SctpTransportObserverInterface {
// accessed on that thread, except for functions explicitly marked otherwise.
// References can be held by other threads, and destruction can therefore
// be initiated by other threads.
-class SctpTransportInterface : public rtc::RefCountInterface {
+class SctpTransportInterface : public webrtc::RefCountInterface {
public:
// This function can be called from other threads.
virtual rtc::scoped_refptr<DtlsTransportInterface> dtls_transport() const = 0;
diff --git a/third_party/libwebrtc/api/sequence_checker_gn/moz.build b/third_party/libwebrtc/api/sequence_checker_gn/moz.build
index a45bc8e9c1..d50c0f4f32 100644
--- a/third_party/libwebrtc/api/sequence_checker_gn/moz.build
+++ b/third_party/libwebrtc/api/sequence_checker_gn/moz.build
@@ -180,16 +180,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/set_local_description_observer_interface.h b/third_party/libwebrtc/api/set_local_description_observer_interface.h
index 8e7b6258d3..40f85b944e 100644
--- a/third_party/libwebrtc/api/set_local_description_observer_interface.h
+++ b/third_party/libwebrtc/api/set_local_description_observer_interface.h
@@ -11,15 +11,15 @@
#ifndef API_SET_LOCAL_DESCRIPTION_OBSERVER_INTERFACE_H_
#define API_SET_LOCAL_DESCRIPTION_OBSERVER_INTERFACE_H_
+#include "api/ref_count.h"
#include "api/rtc_error.h"
-#include "rtc_base/ref_count.h"
namespace webrtc {
// OnSetLocalDescriptionComplete() invokes as soon as
// PeerConnectionInterface::SetLocalDescription() operation completes, allowing
// the observer to examine the effects of the operation without delay.
-class SetLocalDescriptionObserverInterface : public rtc::RefCountInterface {
+class SetLocalDescriptionObserverInterface : public webrtc::RefCountInterface {
public:
// On success, `error.ok()` is true.
virtual void OnSetLocalDescriptionComplete(RTCError error) = 0;
diff --git a/third_party/libwebrtc/api/set_remote_description_observer_interface.h b/third_party/libwebrtc/api/set_remote_description_observer_interface.h
index d1c075309f..c1625410df 100644
--- a/third_party/libwebrtc/api/set_remote_description_observer_interface.h
+++ b/third_party/libwebrtc/api/set_remote_description_observer_interface.h
@@ -11,8 +11,8 @@
#ifndef API_SET_REMOTE_DESCRIPTION_OBSERVER_INTERFACE_H_
#define API_SET_REMOTE_DESCRIPTION_OBSERVER_INTERFACE_H_
+#include "api/ref_count.h"
#include "api/rtc_error.h"
-#include "rtc_base/ref_count.h"
namespace webrtc {
@@ -20,7 +20,7 @@ namespace webrtc {
// callback is invoked such that the state of the peer connection can be
// examined to accurately reflect the effects of the SetRemoteDescription
// operation.
-class SetRemoteDescriptionObserverInterface : public rtc::RefCountInterface {
+class SetRemoteDescriptionObserverInterface : public webrtc::RefCountInterface {
public:
// On success, `error.ok()` is true.
virtual void OnSetRemoteDescriptionComplete(RTCError error) = 0;
diff --git a/third_party/libwebrtc/api/simulated_network_api_gn/moz.build b/third_party/libwebrtc/api/simulated_network_api_gn/moz.build
index b46ec1656b..1f52ba37d7 100644
--- a/third_party/libwebrtc/api/simulated_network_api_gn/moz.build
+++ b/third_party/libwebrtc/api/simulated_network_api_gn/moz.build
@@ -180,16 +180,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/stats/rtc_stats_report.h b/third_party/libwebrtc/api/stats/rtc_stats_report.h
index 1dce4d89b2..632226ef0d 100644
--- a/third_party/libwebrtc/api/stats/rtc_stats_report.h
+++ b/third_party/libwebrtc/api/stats/rtc_stats_report.h
@@ -124,7 +124,7 @@ class RTC_EXPORT RTCStatsReport final
std::string ToJson() const;
protected:
- friend class rtc::RefCountedNonVirtual<RTCStatsReport>;
+ friend class RefCountedNonVirtual<RTCStatsReport>;
~RTCStatsReport() = default;
private:
diff --git a/third_party/libwebrtc/api/task_queue/BUILD.gn b/third_party/libwebrtc/api/task_queue/BUILD.gn
index 760ceaa3ef..9b2f747e78 100644
--- a/third_party/libwebrtc/api/task_queue/BUILD.gn
+++ b/third_party/libwebrtc/api/task_queue/BUILD.gn
@@ -90,7 +90,10 @@ rtc_library("task_queue_test") {
rtc_library("default_task_queue_factory") {
visibility = [ "*" ]
if (!is_ios && !is_android) {
- poisonous = [ "default_task_queue" ]
+ # Internally webrtc shouldn't rely on any specific TaskQueue implementation
+ # and should create TaskQueue using TaskQueueFactory interface.
+ # TaskQueueFactory interface can be propagated with Environment.
+ poisonous = [ "environment_construction" ]
}
sources = [ "default_task_queue_factory.h" ]
deps = [
diff --git a/third_party/libwebrtc/api/task_queue/pending_task_safety_flag_gn/moz.build b/third_party/libwebrtc/api/task_queue/pending_task_safety_flag_gn/moz.build
index 52a2e5003b..43229b22ed 100644
--- a/third_party/libwebrtc/api/task_queue/pending_task_safety_flag_gn/moz.build
+++ b/third_party/libwebrtc/api/task_queue/pending_task_safety_flag_gn/moz.build
@@ -188,7 +188,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -198,10 +197,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/task_queue/task_queue_gn/moz.build b/third_party/libwebrtc/api/task_queue/task_queue_gn/moz.build
index 73630a2ad9..8c04d40cf5 100644
--- a/third_party/libwebrtc/api/task_queue/task_queue_gn/moz.build
+++ b/third_party/libwebrtc/api/task_queue/task_queue_gn/moz.build
@@ -188,7 +188,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -198,10 +197,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/task_queue/task_queue_test.cc b/third_party/libwebrtc/api/task_queue/task_queue_test.cc
index b02333ec58..cac7cd77cc 100644
--- a/third_party/libwebrtc/api/task_queue/task_queue_test.cc
+++ b/third_party/libwebrtc/api/task_queue/task_queue_test.cc
@@ -268,7 +268,7 @@ TEST_P(TaskQueueTest, PostALot) {
explicit BlockingCounter(int initial_count) : count_(initial_count) {}
void DecrementCount() {
- if (count_.DecRef() == rtc::RefCountReleaseStatus::kDroppedLastRef) {
+ if (count_.DecRef() == webrtc::RefCountReleaseStatus::kDroppedLastRef) {
event_.Set();
}
}
diff --git a/third_party/libwebrtc/api/test/compile_all_headers.cc b/third_party/libwebrtc/api/test/compile_all_headers.cc
index 1fcf63e97b..9d375a19f2 100644
--- a/third_party/libwebrtc/api/test/compile_all_headers.cc
+++ b/third_party/libwebrtc/api/test/compile_all_headers.cc
@@ -43,6 +43,7 @@
#include "api/test/mock_rtpreceiver.h"
#include "api/test/mock_rtpsender.h"
#include "api/test/mock_session_description_interface.h"
+#include "api/test/mock_transformable_frame.h"
#include "api/test/mock_transformable_video_frame.h"
#include "api/test/mock_video_bitrate_allocator.h"
#include "api/test/mock_video_bitrate_allocator_factory.h"
diff --git a/third_party/libwebrtc/api/test/create_time_controller.cc b/third_party/libwebrtc/api/test/create_time_controller.cc
index 2c356cb887..7523e05208 100644
--- a/third_party/libwebrtc/api/test/create_time_controller.cc
+++ b/third_party/libwebrtc/api/test/create_time_controller.cc
@@ -11,10 +11,18 @@
#include "api/test/create_time_controller.h"
#include <memory>
+#include <utility>
+#include "absl/base/nullability.h"
+#include "api/enable_media_with_defaults.h"
+#include "api/environment/environment.h"
+#include "api/peer_connection_interface.h"
#include "call/call.h"
#include "call/rtp_transport_config.h"
#include "call/rtp_transport_controller_send_factory_interface.h"
+#include "pc/media_factory.h"
+#include "rtc_base/checks.h"
+#include "system_wrappers/include/clock.h"
#include "test/time_controller/external_time_controller.h"
#include "test/time_controller/simulated_time_controller.h"
@@ -30,24 +38,37 @@ std::unique_ptr<TimeController> CreateSimulatedTimeController() {
Timestamp::Seconds(10000));
}
-std::unique_ptr<CallFactoryInterface> CreateTimeControllerBasedCallFactory(
- TimeController* time_controller) {
- class TimeControllerBasedCallFactory : public CallFactoryInterface {
+void EnableMediaWithDefaultsAndTimeController(
+ TimeController& time_controller,
+ PeerConnectionFactoryDependencies& deps) {
+ class TimeControllerBasedFactory : public MediaFactory {
public:
- explicit TimeControllerBasedCallFactory(TimeController* time_controller)
- : time_controller_(time_controller) {}
- std::unique_ptr<Call> CreateCall(const CallConfig& config) override {
- RtpTransportConfig transportConfig = config.ExtractTransportConfig();
+ TimeControllerBasedFactory(
+ absl::Nonnull<Clock*> clock,
+ absl::Nonnull<std::unique_ptr<MediaFactory>> media_factory)
+ : clock_(clock), media_factory_(std::move(media_factory)) {}
- return Call::Create(config, time_controller_->GetClock(),
+ std::unique_ptr<Call> CreateCall(const CallConfig& config) override {
+ return Call::Create(config, clock_,
config.rtp_transport_controller_send_factory->Create(
- transportConfig, time_controller_->GetClock()));
+ config.ExtractTransportConfig(), clock_));
+ }
+
+ std::unique_ptr<cricket::MediaEngineInterface> CreateMediaEngine(
+ const Environment& env,
+ PeerConnectionFactoryDependencies& dependencies) override {
+ return media_factory_->CreateMediaEngine(env, dependencies);
}
private:
- TimeController* time_controller_;
+ absl::Nonnull<Clock*> clock_;
+ absl::Nonnull<std::unique_ptr<MediaFactory>> media_factory_;
};
- return std::make_unique<TimeControllerBasedCallFactory>(time_controller);
+
+ EnableMediaWithDefaults(deps);
+ RTC_CHECK(deps.media_factory);
+ deps.media_factory = std::make_unique<TimeControllerBasedFactory>(
+ time_controller.GetClock(), std::move(deps.media_factory));
}
} // namespace webrtc
diff --git a/third_party/libwebrtc/api/test/create_time_controller.h b/third_party/libwebrtc/api/test/create_time_controller.h
index e7bc9cb465..c8257da650 100644
--- a/third_party/libwebrtc/api/test/create_time_controller.h
+++ b/third_party/libwebrtc/api/test/create_time_controller.h
@@ -12,7 +12,7 @@
#include <memory>
-#include "api/call/call_factory_interface.h"
+#include "api/peer_connection_interface.h"
#include "api/test/time_controller.h"
namespace webrtc {
@@ -24,10 +24,12 @@ std::unique_ptr<TimeController> CreateTimeController(
// Creates a time controller that runs in simulated time.
std::unique_ptr<TimeController> CreateSimulatedTimeController();
-// This is creates a call factory that creates Call instances that are backed by
-// a time controller.
-std::unique_ptr<CallFactoryInterface> CreateTimeControllerBasedCallFactory(
- TimeController* time_controller);
+// Adjusts media `deps` to use clock `time_controller` provides, fills media
+// related dependencies, and enables media support for a PeerConnectionFactory
+// created from `deps`.
+void EnableMediaWithDefaultsAndTimeController(
+ TimeController& time_controller,
+ PeerConnectionFactoryDependencies& deps);
} // namespace webrtc
diff --git a/third_party/libwebrtc/api/test/create_video_codec_tester.cc b/third_party/libwebrtc/api/test/create_video_codec_tester.cc
deleted file mode 100644
index a1efefdb48..0000000000
--- a/third_party/libwebrtc/api/test/create_video_codec_tester.cc
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved.
- *
- * Use of this source code is governed by a BSD-style license
- * that can be found in the LICENSE file in the root of the source
- * tree. An additional intellectual property rights grant can be found
- * in the file PATENTS. All contributing project authors may
- * be found in the AUTHORS file in the root of the source tree.
- */
-
-#include "api/test/create_video_codec_tester.h"
-
-#include <memory>
-#include <utility>
-
-#include "api/test/video_codec_tester.h"
-#include "modules/video_coding/codecs/test/video_codec_tester_impl.h"
-
-namespace webrtc {
-namespace test {
-
-std::unique_ptr<VideoCodecTester> CreateVideoCodecTester() {
- return std::make_unique<VideoCodecTesterImpl>();
-}
-
-} // namespace test
-} // namespace webrtc
diff --git a/third_party/libwebrtc/api/test/create_video_codec_tester.h b/third_party/libwebrtc/api/test/create_video_codec_tester.h
deleted file mode 100644
index c68864ce85..0000000000
--- a/third_party/libwebrtc/api/test/create_video_codec_tester.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved.
- *
- * Use of this source code is governed by a BSD-style license
- * that can be found in the LICENSE file in the root of the source
- * tree. An additional intellectual property rights grant can be found
- * in the file PATENTS. All contributing project authors may
- * be found in the AUTHORS file in the root of the source tree.
- */
-
-#ifndef API_TEST_CREATE_VIDEO_CODEC_TESTER_H_
-#define API_TEST_CREATE_VIDEO_CODEC_TESTER_H_
-
-#include <memory>
-
-#include "api/test/video_codec_tester.h"
-
-namespace webrtc {
-namespace test {
-
-std::unique_ptr<VideoCodecTester> CreateVideoCodecTester();
-
-} // namespace test
-} // namespace webrtc
-
-#endif // API_TEST_CREATE_VIDEO_CODEC_TESTER_H_
diff --git a/third_party/libwebrtc/api/test/mock_transformable_audio_frame.h b/third_party/libwebrtc/api/test/mock_transformable_audio_frame.h
index be703006ea..584c77fa54 100644
--- a/third_party/libwebrtc/api/test/mock_transformable_audio_frame.h
+++ b/third_party/libwebrtc/api/test/mock_transformable_audio_frame.h
@@ -11,6 +11,8 @@
#ifndef API_TEST_MOCK_TRANSFORMABLE_AUDIO_FRAME_H_
#define API_TEST_MOCK_TRANSFORMABLE_AUDIO_FRAME_H_
+#include <string>
+
#include "api/frame_transformer_interface.h"
#include "test/gmock.h"
@@ -24,6 +26,7 @@ class MockTransformableAudioFrame : public TransformableAudioFrameInterface {
MOCK_METHOD(uint8_t, GetPayloadType, (), (const, override));
MOCK_METHOD(uint32_t, GetSsrc, (), (const, override));
MOCK_METHOD(uint32_t, GetTimestamp, (), (const, override));
+ MOCK_METHOD(std::string, GetMimeType, (), (const, override));
MOCK_METHOD(rtc::ArrayView<const uint32_t>,
GetContributingSources,
(),
diff --git a/third_party/libwebrtc/api/test/mock_transformable_frame.h b/third_party/libwebrtc/api/test/mock_transformable_frame.h
new file mode 100644
index 0000000000..df20b62295
--- /dev/null
+++ b/third_party/libwebrtc/api/test/mock_transformable_frame.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2023 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef API_TEST_MOCK_TRANSFORMABLE_FRAME_H_
+#define API_TEST_MOCK_TRANSFORMABLE_FRAME_H_
+
+#include <stdint.h>
+
+#include <optional>
+#include <string>
+
+#include "api/array_view.h"
+#include "api/frame_transformer_interface.h"
+#include "api/units/timestamp.h"
+#include "test/gmock.h"
+
+namespace webrtc {
+
+class MockTransformableFrame : public webrtc::TransformableFrameInterface {
+ public:
+ MOCK_METHOD(rtc::ArrayView<const uint8_t>, GetData, (), (const, override));
+ MOCK_METHOD(void, SetData, (rtc::ArrayView<const uint8_t>), (override));
+ MOCK_METHOD(uint8_t, GetPayloadType, (), (const, override));
+ MOCK_METHOD(uint32_t, GetSsrc, (), (const, override));
+ MOCK_METHOD(uint32_t, GetTimestamp, (), (const, override));
+ MOCK_METHOD(void, SetRTPTimestamp, (uint32_t), (override));
+ MOCK_METHOD(std::optional<webrtc::Timestamp>,
+ GetCaptureTimeIdentifier,
+ (),
+ (const, override));
+ MOCK_METHOD(std::string, GetMimeType, (), (const, override));
+};
+
+static_assert(!std::is_abstract_v<MockTransformableFrame>, "");
+
+} // namespace webrtc
+
+#endif // API_TEST_MOCK_TRANSFORMABLE_FRAME_H_
diff --git a/third_party/libwebrtc/api/test/pclf/media_configuration.h b/third_party/libwebrtc/api/test/pclf/media_configuration.h
index 5bcb308c83..5c3440c293 100644
--- a/third_party/libwebrtc/api/test/pclf/media_configuration.h
+++ b/third_party/libwebrtc/api/test/pclf/media_configuration.h
@@ -24,7 +24,6 @@
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
#include "api/array_view.h"
-#include "api/async_resolver_factory.h"
#include "api/audio/audio_mixer.h"
#include "api/audio_options.h"
#include "api/call/call_factory_interface.h"
diff --git a/third_party/libwebrtc/api/test/pclf/media_quality_test_params.h b/third_party/libwebrtc/api/test/pclf/media_quality_test_params.h
index b2ccdf18c5..aad04c3eb6 100644
--- a/third_party/libwebrtc/api/test/pclf/media_quality_test_params.h
+++ b/third_party/libwebrtc/api/test/pclf/media_quality_test_params.h
@@ -49,8 +49,6 @@ struct PeerConnectionFactoryComponents {
std::unique_ptr<NetworkControllerFactoryInterface> network_controller_factory;
std::unique_ptr<NetEqFactory> neteq_factory;
- // Will be passed to MediaEngineInterface, that will be used in
- // PeerConnectionFactory.
std::unique_ptr<VideoEncoderFactory> video_encoder_factory;
std::unique_ptr<VideoDecoderFactory> video_decoder_factory;
rtc::scoped_refptr<webrtc::AudioEncoderFactory> audio_encoder_factory;
diff --git a/third_party/libwebrtc/api/test/peerconnection_quality_test_fixture.h b/third_party/libwebrtc/api/test/peerconnection_quality_test_fixture.h
index 74470cdf86..034e13ff3b 100644
--- a/third_party/libwebrtc/api/test/peerconnection_quality_test_fixture.h
+++ b/third_party/libwebrtc/api/test/peerconnection_quality_test_fixture.h
@@ -25,7 +25,6 @@
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
#include "api/array_view.h"
-#include "api/async_resolver_factory.h"
#include "api/audio/audio_mixer.h"
#include "api/call/call_factory_interface.h"
#include "api/fec_controller.h"
diff --git a/third_party/libwebrtc/api/test/video_codec_stats.cc b/third_party/libwebrtc/api/test/video_codec_stats.cc
deleted file mode 100644
index fb7226701e..0000000000
--- a/third_party/libwebrtc/api/test/video_codec_stats.cc
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (c) 2023 The WebRTC project authors. All Rights Reserved.
- *
- * Use of this source code is governed by a BSD-style license
- * that can be found in the LICENSE file in the root of the source
- * tree. An additional intellectual property rights grant can be found
- * in the file PATENTS. All contributing project authors may
- * be found in the AUTHORS file in the root of the source tree.
- */
-
-#include "api/test/video_codec_stats.h"
-
-namespace webrtc {
-namespace test {
-
-void VideoCodecStats::Stream::LogMetrics(
- MetricsLogger* logger,
- std::string test_case_name,
- std::map<std::string, std::string> metadata) const {
- logger->LogMetric("width", test_case_name, width, Unit::kCount,
- webrtc::test::ImprovementDirection::kBiggerIsBetter,
- metadata);
-
- logger->LogMetric("height", test_case_name, height, Unit::kCount,
- webrtc::test::ImprovementDirection::kBiggerIsBetter,
- metadata);
-
- logger->LogMetric(
- "frame_size_bytes", test_case_name, frame_size_bytes, Unit::kBytes,
- webrtc::test::ImprovementDirection::kNeitherIsBetter, metadata);
-
- logger->LogMetric("keyframe", test_case_name, keyframe, Unit::kCount,
- webrtc::test::ImprovementDirection::kSmallerIsBetter,
- metadata);
-
- logger->LogMetric("qp", test_case_name, qp, Unit::kUnitless,
- webrtc::test::ImprovementDirection::kSmallerIsBetter,
- metadata);
-
- logger->LogMetric(
- "encode_time_ms", test_case_name, encode_time_ms, Unit::kMilliseconds,
- webrtc::test::ImprovementDirection::kSmallerIsBetter, metadata);
-
- logger->LogMetric(
- "decode_time_ms", test_case_name, decode_time_ms, Unit::kMilliseconds,
- webrtc::test::ImprovementDirection::kSmallerIsBetter, metadata);
-
- logger->LogMetric("target_bitrate_kbps", test_case_name, target_bitrate_kbps,
- Unit::kKilobitsPerSecond,
- webrtc::test::ImprovementDirection::kBiggerIsBetter,
- metadata);
-
- logger->LogMetric("target_framerate_fps", test_case_name,
- target_framerate_fps, Unit::kHertz,
- webrtc::test::ImprovementDirection::kBiggerIsBetter,
- metadata);
-
- logger->LogMetric("encoded_bitrate_kbps", test_case_name,
- encoded_bitrate_kbps, Unit::kKilobitsPerSecond,
- webrtc::test::ImprovementDirection::kBiggerIsBetter,
- metadata);
-
- logger->LogMetric("encoded_framerate_fps", test_case_name,
- encoded_framerate_fps, Unit::kHertz,
- webrtc::test::ImprovementDirection::kBiggerIsBetter,
- metadata);
-
- logger->LogMetric("bitrate_mismatch_pct", test_case_name,
- bitrate_mismatch_pct, Unit::kPercent,
- webrtc::test::ImprovementDirection::kSmallerIsBetter,
- metadata);
-
- logger->LogMetric("framerate_mismatch_pct", test_case_name,
- framerate_mismatch_pct, Unit::kPercent,
- webrtc::test::ImprovementDirection::kSmallerIsBetter,
- metadata);
-
- logger->LogMetric("transmission_time_ms", test_case_name,
- transmission_time_ms, Unit::kMilliseconds,
- webrtc::test::ImprovementDirection::kSmallerIsBetter,
- metadata);
-
- logger->LogMetric("psnr_y_db", test_case_name, psnr.y, Unit::kUnitless,
- webrtc::test::ImprovementDirection::kBiggerIsBetter,
- metadata);
-
- logger->LogMetric("psnr_u_db", test_case_name, psnr.u, Unit::kUnitless,
- webrtc::test::ImprovementDirection::kBiggerIsBetter,
- metadata);
-
- logger->LogMetric("psnr_v_db", test_case_name, psnr.v, Unit::kUnitless,
- webrtc::test::ImprovementDirection::kBiggerIsBetter,
- metadata);
-}
-
-} // namespace test
-} // namespace webrtc
diff --git a/third_party/libwebrtc/api/test/video_codec_stats.h b/third_party/libwebrtc/api/test/video_codec_stats.h
deleted file mode 100644
index 80f8287848..0000000000
--- a/third_party/libwebrtc/api/test/video_codec_stats.h
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (c) 2023 The WebRTC project authors. All Rights Reserved.
- *
- * Use of this source code is governed by a BSD-style license
- * that can be found in the LICENSE file in the root of the source
- * tree. An additional intellectual property rights grant can be found
- * in the file PATENTS. All contributing project authors may
- * be found in the AUTHORS file in the root of the source tree.
- */
-
-#ifndef API_TEST_VIDEO_CODEC_STATS_H_
-#define API_TEST_VIDEO_CODEC_STATS_H_
-
-#include <map>
-#include <string>
-#include <vector>
-
-#include "absl/types/optional.h"
-#include "api/numerics/samples_stats_counter.h"
-#include "api/test/metrics/metric.h"
-#include "api/test/metrics/metrics_logger.h"
-#include "api/units/data_rate.h"
-#include "api/units/data_size.h"
-#include "api/units/frequency.h"
-
-namespace webrtc {
-namespace test {
-
-// Interface for encoded and/or decoded video frame and stream statistics.
-class VideoCodecStats {
- public:
- // Filter for slicing frames.
- struct Filter {
- absl::optional<int> first_frame;
- absl::optional<int> last_frame;
- absl::optional<int> spatial_idx;
- absl::optional<int> temporal_idx;
- };
-
- struct Frame {
- int frame_num = 0;
- uint32_t timestamp_rtp = 0;
-
- int spatial_idx = 0;
- int temporal_idx = 0;
-
- int width = 0;
- int height = 0;
- DataSize frame_size = DataSize::Zero();
- bool keyframe = false;
- absl::optional<int> qp;
- absl::optional<int> base_spatial_idx;
-
- Timestamp encode_start = Timestamp::Zero();
- TimeDelta encode_time = TimeDelta::Zero();
- Timestamp decode_start = Timestamp::Zero();
- TimeDelta decode_time = TimeDelta::Zero();
-
- struct Psnr {
- double y = 0.0;
- double u = 0.0;
- double v = 0.0;
- };
- absl::optional<Psnr> psnr;
-
- absl::optional<DataRate> target_bitrate;
- absl::optional<Frequency> target_framerate;
-
- bool encoded = false;
- bool decoded = false;
- };
-
- struct Stream {
- SamplesStatsCounter width;
- SamplesStatsCounter height;
- SamplesStatsCounter frame_size_bytes;
- SamplesStatsCounter keyframe;
- SamplesStatsCounter qp;
-
- SamplesStatsCounter encode_time_ms;
- SamplesStatsCounter decode_time_ms;
-
- SamplesStatsCounter target_bitrate_kbps;
- SamplesStatsCounter target_framerate_fps;
-
- SamplesStatsCounter encoded_bitrate_kbps;
- SamplesStatsCounter encoded_framerate_fps;
-
- SamplesStatsCounter bitrate_mismatch_pct;
- SamplesStatsCounter framerate_mismatch_pct;
-
- SamplesStatsCounter transmission_time_ms;
-
- struct Psnr {
- SamplesStatsCounter y;
- SamplesStatsCounter u;
- SamplesStatsCounter v;
- } psnr;
-
- // Logs `Stream` metrics to provided `MetricsLogger`.
- void LogMetrics(MetricsLogger* logger,
- std::string test_case_name,
- std::map<std::string, std::string> metadata = {}) const;
- };
-
- virtual ~VideoCodecStats() = default;
-
- // Returns frames from interval, spatial and temporal layer specified by given
- // `filter`.
- virtual std::vector<Frame> Slice(
- absl::optional<Filter> filter = absl::nullopt) const = 0;
-
- // Returns video statistics aggregated for given `frames`.
- virtual Stream Aggregate(const std::vector<Frame>& frames) const = 0;
-};
-
-} // namespace test
-} // namespace webrtc
-
-#endif // API_TEST_VIDEO_CODEC_STATS_H_
diff --git a/third_party/libwebrtc/api/test/video_codec_tester.h b/third_party/libwebrtc/api/test/video_codec_tester.h
deleted file mode 100644
index c2fb89e2cb..0000000000
--- a/third_party/libwebrtc/api/test/video_codec_tester.h
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved.
- *
- * Use of this source code is governed by a BSD-style license
- * that can be found in the LICENSE file in the root of the source
- * tree. An additional intellectual property rights grant can be found
- * in the file PATENTS. All contributing project authors may
- * be found in the AUTHORS file in the root of the source tree.
- */
-
-#ifndef API_TEST_VIDEO_CODEC_TESTER_H_
-#define API_TEST_VIDEO_CODEC_TESTER_H_
-
-#include <memory>
-#include <string>
-
-#include "absl/functional/any_invocable.h"
-#include "absl/types/optional.h"
-#include "api/test/video_codec_stats.h"
-#include "api/video/encoded_image.h"
-#include "api/video/resolution.h"
-#include "api/video/video_frame.h"
-
-namespace webrtc {
-namespace test {
-
-// Interface for a video codec tester. The interface provides minimalistic set
-// of data structures that enables implementation of decode-only, encode-only
-// and encode-decode tests.
-class VideoCodecTester {
- public:
- // Pacing settings for codec input.
- struct PacingSettings {
- enum PacingMode {
- // Pacing is not used. Frames are sent to codec back-to-back.
- kNoPacing,
- // Pace with the rate equal to the target video frame rate. Pacing time is
- // derived from RTP timestamp.
- kRealTime,
- // Pace with the explicitly provided rate.
- kConstantRate,
- };
- PacingMode mode = PacingMode::kNoPacing;
- // Pacing rate for `kConstantRate` mode.
- Frequency constant_rate = Frequency::Zero();
- };
-
- struct DecoderSettings {
- PacingSettings pacing;
- absl::optional<std::string> decoder_input_base_path;
- absl::optional<std::string> decoder_output_base_path;
- };
-
- struct EncoderSettings {
- PacingSettings pacing;
- absl::optional<std::string> encoder_input_base_path;
- absl::optional<std::string> encoder_output_base_path;
- };
-
- virtual ~VideoCodecTester() = default;
-
- // Interface for a raw video frames source.
- class RawVideoSource {
- public:
- virtual ~RawVideoSource() = default;
-
- // Returns next frame. If no more frames to pull, returns `absl::nullopt`.
- // For analysis and pacing purposes, frame must have RTP timestamp set. The
- // timestamp must represent the target video frame rate and be unique.
- virtual absl::optional<VideoFrame> PullFrame() = 0;
-
- // Returns early pulled frame with RTP timestamp equal to `timestamp_rtp`.
- virtual VideoFrame GetFrame(uint32_t timestamp_rtp,
- Resolution resolution) = 0;
- };
-
- // Interface for a coded video frames source.
- class CodedVideoSource {
- public:
- virtual ~CodedVideoSource() = default;
-
- // Returns next frame. If no more frames to pull, returns `absl::nullopt`.
- // For analysis and pacing purposes, frame must have RTP timestamp set. The
- // timestamp must represent the target video frame rate and be unique.
- virtual absl::optional<EncodedImage> PullFrame() = 0;
- };
-
- // Interface for a video encoder.
- class Encoder {
- public:
- using EncodeCallback =
- absl::AnyInvocable<void(const EncodedImage& encoded_frame)>;
-
- virtual ~Encoder() = default;
-
- virtual void Initialize() = 0;
-
- virtual void Encode(const VideoFrame& frame, EncodeCallback callback) = 0;
-
- virtual void Flush() = 0;
- };
-
- // Interface for a video decoder.
- class Decoder {
- public:
- using DecodeCallback =
- absl::AnyInvocable<void(const VideoFrame& decoded_frame)>;
-
- virtual ~Decoder() = default;
-
- virtual void Initialize() = 0;
-
- virtual void Decode(const EncodedImage& frame, DecodeCallback callback) = 0;
-
- virtual void Flush() = 0;
- };
-
- // Pulls coded video frames from `video_source` and passes them to `decoder`.
- // Returns `VideoCodecTestStats` object that contains collected per-frame
- // metrics.
- virtual std::unique_ptr<VideoCodecStats> RunDecodeTest(
- CodedVideoSource* video_source,
- Decoder* decoder,
- const DecoderSettings& decoder_settings) = 0;
-
- // Pulls raw video frames from `video_source` and passes them to `encoder`.
- // Returns `VideoCodecTestStats` object that contains collected per-frame
- // metrics.
- virtual std::unique_ptr<VideoCodecStats> RunEncodeTest(
- RawVideoSource* video_source,
- Encoder* encoder,
- const EncoderSettings& encoder_settings) = 0;
-
- // Pulls raw video frames from `video_source`, passes them to `encoder` and
- // then passes encoded frames to `decoder`. Returns `VideoCodecTestStats`
- // object that contains collected per-frame metrics.
- virtual std::unique_ptr<VideoCodecStats> RunEncodeDecodeTest(
- RawVideoSource* video_source,
- Encoder* encoder,
- Decoder* decoder,
- const EncoderSettings& encoder_settings,
- const DecoderSettings& decoder_settings) = 0;
-};
-
-} // namespace test
-} // namespace webrtc
-
-#endif // API_TEST_VIDEO_CODEC_TESTER_H_
diff --git a/third_party/libwebrtc/api/transport/bitrate_settings_gn/moz.build b/third_party/libwebrtc/api/transport/bitrate_settings_gn/moz.build
index d99d149fa3..2b732ca51e 100644
--- a/third_party/libwebrtc/api/transport/bitrate_settings_gn/moz.build
+++ b/third_party/libwebrtc/api/transport/bitrate_settings_gn/moz.build
@@ -184,7 +184,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -194,10 +193,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/transport/datagram_transport_interface_gn/moz.build b/third_party/libwebrtc/api/transport/datagram_transport_interface_gn/moz.build
index 9168cf9156..dba9cee6bd 100644
--- a/third_party/libwebrtc/api/transport/datagram_transport_interface_gn/moz.build
+++ b/third_party/libwebrtc/api/transport/datagram_transport_interface_gn/moz.build
@@ -187,16 +187,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/transport/field_trial_based_config_gn/moz.build b/third_party/libwebrtc/api/transport/field_trial_based_config_gn/moz.build
index fd2b2dd12b..dd3370838d 100644
--- a/third_party/libwebrtc/api/transport/field_trial_based_config_gn/moz.build
+++ b/third_party/libwebrtc/api/transport/field_trial_based_config_gn/moz.build
@@ -195,7 +195,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -205,10 +204,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/transport/goog_cc_gn/moz.build b/third_party/libwebrtc/api/transport/goog_cc_gn/moz.build
index 4a7c84915c..80dee942e0 100644
--- a/third_party/libwebrtc/api/transport/goog_cc_gn/moz.build
+++ b/third_party/libwebrtc/api/transport/goog_cc_gn/moz.build
@@ -200,7 +200,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -210,10 +209,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/transport/network_control_gn/moz.build b/third_party/libwebrtc/api/transport/network_control_gn/moz.build
index 76dd117a86..e11b34ba71 100644
--- a/third_party/libwebrtc/api/transport/network_control_gn/moz.build
+++ b/third_party/libwebrtc/api/transport/network_control_gn/moz.build
@@ -195,7 +195,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -205,10 +204,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/transport/rtp/dependency_descriptor_gn/moz.build b/third_party/libwebrtc/api/transport/rtp/dependency_descriptor_gn/moz.build
index a2fe75e60e..568c7d9768 100644
--- a/third_party/libwebrtc/api/transport/rtp/dependency_descriptor_gn/moz.build
+++ b/third_party/libwebrtc/api/transport/rtp/dependency_descriptor_gn/moz.build
@@ -188,7 +188,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -198,10 +197,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/transport/rtp/rtp_source_gn/moz.build b/third_party/libwebrtc/api/transport/rtp/rtp_source_gn/moz.build
index 9a7b0b69bf..9d993f6f68 100644
--- a/third_party/libwebrtc/api/transport/rtp/rtp_source_gn/moz.build
+++ b/third_party/libwebrtc/api/transport/rtp/rtp_source_gn/moz.build
@@ -187,16 +187,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/transport/stun.cc b/third_party/libwebrtc/api/transport/stun.cc
index 35a65fd8e8..7ef6852260 100644
--- a/third_party/libwebrtc/api/transport/stun.cc
+++ b/third_party/libwebrtc/api/transport/stun.cc
@@ -41,7 +41,9 @@ uint32_t ReduceTransactionId(absl::string_view transaction_id) {
RTC_DCHECK(transaction_id.length() == cricket::kStunTransactionIdLength ||
transaction_id.length() == cricket::kStunLegacyTransactionIdLength)
<< transaction_id.length();
- ByteBufferReader reader(transaction_id.data(), transaction_id.size());
+ ByteBufferReader reader(rtc::MakeArrayView(
+ reinterpret_cast<const uint8_t*>(transaction_id.data()),
+ transaction_id.size()));
uint32_t result = 0;
uint32_t next;
while (reader.ReadUInt32(&next)) {
@@ -912,7 +914,8 @@ bool StunAddressAttribute::Read(ByteBufferReader* buf) {
if (length() != SIZE_IP4) {
return false;
}
- if (!buf->ReadBytes(reinterpret_cast<char*>(&v4addr), sizeof(v4addr))) {
+ if (!buf->ReadBytes(rtc::MakeArrayView(reinterpret_cast<uint8_t*>(&v4addr),
+ sizeof(v4addr)))) {
return false;
}
rtc::IPAddress ipaddr(v4addr);
@@ -922,7 +925,8 @@ bool StunAddressAttribute::Read(ByteBufferReader* buf) {
if (length() != SIZE_IP6) {
return false;
}
- if (!buf->ReadBytes(reinterpret_cast<char*>(&v6addr), sizeof(v6addr))) {
+ if (!buf->ReadBytes(rtc::MakeArrayView(reinterpret_cast<uint8_t*>(&v6addr),
+ sizeof(v6addr)))) {
return false;
}
rtc::IPAddress ipaddr(v6addr);
@@ -1128,13 +1132,13 @@ StunAttributeValueType StunByteStringAttribute::value_type() const {
}
void StunByteStringAttribute::CopyBytes(absl::string_view bytes) {
- char* new_bytes = new char[bytes.size()];
+ uint8_t* new_bytes = new uint8_t[bytes.size()];
memcpy(new_bytes, bytes.data(), bytes.size());
SetBytes(new_bytes, bytes.size());
}
void StunByteStringAttribute::CopyBytes(const void* bytes, size_t length) {
- char* new_bytes = new char[length];
+ uint8_t* new_bytes = new uint8_t[length];
memcpy(new_bytes, bytes, length);
SetBytes(new_bytes, length);
}
@@ -1142,7 +1146,7 @@ void StunByteStringAttribute::CopyBytes(const void* bytes, size_t length) {
uint8_t StunByteStringAttribute::GetByte(size_t index) const {
RTC_DCHECK(bytes_ != NULL);
RTC_DCHECK(index < length());
- return static_cast<uint8_t>(bytes_[index]);
+ return bytes_[index];
}
void StunByteStringAttribute::SetByte(size_t index, uint8_t value) {
@@ -1152,8 +1156,8 @@ void StunByteStringAttribute::SetByte(size_t index, uint8_t value) {
}
bool StunByteStringAttribute::Read(ByteBufferReader* buf) {
- bytes_ = new char[length()];
- if (!buf->ReadBytes(bytes_, length())) {
+ bytes_ = new uint8_t[length()];
+ if (!buf->ReadBytes(rtc::ArrayView<uint8_t>(bytes_, length()))) {
return false;
}
@@ -1166,12 +1170,12 @@ bool StunByteStringAttribute::Write(ByteBufferWriter* buf) const {
if (!LengthValid(type(), length())) {
return false;
}
- buf->WriteBytes(bytes_, length());
+ buf->WriteBytes(reinterpret_cast<const char*>(bytes_), length());
WritePadding(buf);
return true;
}
-void StunByteStringAttribute::SetBytes(char* bytes, size_t length) {
+void StunByteStringAttribute::SetBytes(uint8_t* bytes, size_t length) {
delete[] bytes_;
bytes_ = bytes;
SetLength(static_cast<uint16_t>(length));
diff --git a/third_party/libwebrtc/api/transport/stun.h b/third_party/libwebrtc/api/transport/stun.h
index 4a04db33cf..62d98f71e0 100644
--- a/third_party/libwebrtc/api/transport/stun.h
+++ b/third_party/libwebrtc/api/transport/stun.h
@@ -519,13 +519,22 @@ class StunByteStringAttribute : public StunAttribute {
StunAttributeValueType value_type() const override;
- const char* bytes() const { return bytes_; }
+ [[deprecated("Use array_view")]] const char* bytes() const {
+ return reinterpret_cast<const char*>(bytes_);
+ }
+ // Returns the attribute value as a string.
+ // Use this for attributes that are text or text-compatible.
absl::string_view string_view() const {
- return absl::string_view(bytes_, length());
+ return absl::string_view(reinterpret_cast<const char*>(bytes_), length());
+ }
+ // Returns the attribute value as an uint8_t view.
+ // Use this function for values that are not text.
+ rtc::ArrayView<uint8_t> array_view() const {
+ return rtc::MakeArrayView(bytes_, length());
}
[[deprecated]] std::string GetString() const {
- return std::string(bytes_, length());
+ return std::string(reinterpret_cast<const char*>(bytes_), length());
}
void CopyBytes(const void* bytes, size_t length);
@@ -538,9 +547,9 @@ class StunByteStringAttribute : public StunAttribute {
bool Write(rtc::ByteBufferWriter* buf) const override;
private:
- void SetBytes(char* bytes, size_t length);
+ void SetBytes(uint8_t* bytes, size_t length);
- char* bytes_;
+ uint8_t* bytes_;
};
// Implements STUN attributes that record an error code.
diff --git a/third_party/libwebrtc/api/transport/stun_types_gn/moz.build b/third_party/libwebrtc/api/transport/stun_types_gn/moz.build
index 36750a2f18..ad873a1796 100644
--- a/third_party/libwebrtc/api/transport/stun_types_gn/moz.build
+++ b/third_party/libwebrtc/api/transport/stun_types_gn/moz.build
@@ -176,16 +176,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/transport/stun_unittest.cc b/third_party/libwebrtc/api/transport/stun_unittest.cc
index 96ad45843b..9ad4da9a4c 100644
--- a/third_party/libwebrtc/api/transport/stun_unittest.cc
+++ b/third_party/libwebrtc/api/transport/stun_unittest.cc
@@ -35,7 +35,7 @@ class StunTest : public ::testing::Test {
}
void CheckStunTransactionID(const StunMessage& msg,
- const unsigned char* expectedID,
+ const uint8_t* expectedID,
size_t length) {
ASSERT_EQ(length, msg.transaction_id().size());
ASSERT_EQ(length == kStunTransactionIdLength + 4, msg.IsLegacy());
@@ -65,10 +65,9 @@ class StunTest : public ::testing::Test {
}
size_t ReadStunMessageTestCase(StunMessage* msg,
- const unsigned char* testcase,
+ const uint8_t* testcase,
size_t size) {
- const char* input = reinterpret_cast<const char*>(testcase);
- rtc::ByteBufferReader buf(input, size);
+ rtc::ByteBufferReader buf(rtc::MakeArrayView(testcase, size));
if (msg->Read(&buf)) {
// Returns the size the stun message should report itself as being
return (size - 20);
@@ -85,7 +84,7 @@ class StunTest : public ::testing::Test {
// clang-format off
// clang formatting doesn't respect inline comments.
-static const unsigned char kStunMessageWithIPv6MappedAddress[] = {
+static const uint8_t kStunMessageWithIPv6MappedAddress[] = {
0x00, 0x01, 0x00, 0x18, // message header
0x21, 0x12, 0xa4, 0x42, // transaction id
0x29, 0x1f, 0xcd, 0x7c,
@@ -99,7 +98,7 @@ static const unsigned char kStunMessageWithIPv6MappedAddress[] = {
0xfe, 0xe5, 0x00, 0xc3
};
-static const unsigned char kStunMessageWithIPv4MappedAddress[] = {
+static const uint8_t kStunMessageWithIPv4MappedAddress[] = {
0x01, 0x01, 0x00, 0x0c, // binding response, length 12
0x21, 0x12, 0xa4, 0x42, // magic cookie
0x29, 0x1f, 0xcd, 0x7c, // transaction ID
@@ -111,7 +110,7 @@ static const unsigned char kStunMessageWithIPv4MappedAddress[] = {
};
// Test XOR-mapped IP addresses:
-static const unsigned char kStunMessageWithIPv6XorMappedAddress[] = {
+static const uint8_t kStunMessageWithIPv6XorMappedAddress[] = {
0x01, 0x01, 0x00, 0x18, // message header (binding response)
0x21, 0x12, 0xa4, 0x42, // magic cookie (rfc5389)
0xe3, 0xa9, 0x46, 0xe1, // transaction ID
@@ -125,7 +124,7 @@ static const unsigned char kStunMessageWithIPv6XorMappedAddress[] = {
0xaa, 0xed, 0x01, 0xc3
};
-static const unsigned char kStunMessageWithIPv4XorMappedAddress[] = {
+static const uint8_t kStunMessageWithIPv4XorMappedAddress[] = {
0x01, 0x01, 0x00, 0x0c, // message header (binding response)
0x21, 0x12, 0xa4, 0x42, // magic cookie
0x29, 0x1f, 0xcd, 0x7c, // transaction ID
@@ -137,7 +136,7 @@ static const unsigned char kStunMessageWithIPv4XorMappedAddress[] = {
};
// ByteString Attribute (username)
-static const unsigned char kStunMessageWithByteStringAttribute[] = {
+static const uint8_t kStunMessageWithByteStringAttribute[] = {
0x00, 0x01, 0x00, 0x0c,
0x21, 0x12, 0xa4, 0x42,
0xe3, 0xa9, 0x46, 0xe1,
@@ -150,7 +149,7 @@ static const unsigned char kStunMessageWithByteStringAttribute[] = {
// Message with an unknown but comprehensible optional attribute.
// Parsing should succeed despite this unknown attribute.
-static const unsigned char kStunMessageWithUnknownAttribute[] = {
+static const uint8_t kStunMessageWithUnknownAttribute[] = {
0x00, 0x01, 0x00, 0x14,
0x21, 0x12, 0xa4, 0x42,
0xe3, 0xa9, 0x46, 0xe1,
@@ -164,7 +163,7 @@ static const unsigned char kStunMessageWithUnknownAttribute[] = {
};
// ByteString Attribute (username) with padding byte
-static const unsigned char kStunMessageWithPaddedByteStringAttribute[] = {
+static const uint8_t kStunMessageWithPaddedByteStringAttribute[] = {
0x00, 0x01, 0x00, 0x08,
0x21, 0x12, 0xa4, 0x42,
0xe3, 0xa9, 0x46, 0xe1,
@@ -175,7 +174,7 @@ static const unsigned char kStunMessageWithPaddedByteStringAttribute[] = {
};
// Message with an Unknown Attributes (uint16_t list) attribute.
-static const unsigned char kStunMessageWithUInt16ListAttribute[] = {
+static const uint8_t kStunMessageWithUInt16ListAttribute[] = {
0x00, 0x01, 0x00, 0x0c,
0x21, 0x12, 0xa4, 0x42,
0xe3, 0xa9, 0x46, 0xe1,
@@ -187,7 +186,7 @@ static const unsigned char kStunMessageWithUInt16ListAttribute[] = {
};
// Error response message (unauthorized)
-static const unsigned char kStunMessageWithErrorAttribute[] = {
+static const uint8_t kStunMessageWithErrorAttribute[] = {
0x01, 0x11, 0x00, 0x14,
0x21, 0x12, 0xa4, 0x42,
0x29, 0x1f, 0xcd, 0x7c,
@@ -205,7 +204,7 @@ static const unsigned char kStunMessageWithErrorAttribute[] = {
// The actual length in bytes of the invalid messages (including STUN header)
static const int kRealLengthOfInvalidLengthTestCases = 32;
-static const unsigned char kStunMessageWithZeroLength[] = {
+static const uint8_t kStunMessageWithZeroLength[] = {
0x00, 0x01, 0x00, 0x00, // length of 0 (last 2 bytes)
0x21, 0x12, 0xA4, 0x42, // magic cookie
'0', '1', '2', '3', // transaction id
@@ -216,7 +215,7 @@ static const unsigned char kStunMessageWithZeroLength[] = {
0x21, 0x12, 0xA4, 0x53,
};
-static const unsigned char kStunMessageWithExcessLength[] = {
+static const uint8_t kStunMessageWithExcessLength[] = {
0x00, 0x01, 0x00, 0x55, // length of 85
0x21, 0x12, 0xA4, 0x42, // magic cookie
'0', '1', '2', '3', // transaction id
@@ -227,7 +226,7 @@ static const unsigned char kStunMessageWithExcessLength[] = {
0x21, 0x12, 0xA4, 0x53,
};
-static const unsigned char kStunMessageWithSmallLength[] = {
+static const uint8_t kStunMessageWithSmallLength[] = {
0x00, 0x01, 0x00, 0x03, // length of 3
0x21, 0x12, 0xA4, 0x42, // magic cookie
'0', '1', '2', '3', // transaction id
@@ -238,7 +237,7 @@ static const unsigned char kStunMessageWithSmallLength[] = {
0x21, 0x12, 0xA4, 0x53,
};
-static const unsigned char kStunMessageWithBadHmacAtEnd[] = {
+static const uint8_t kStunMessageWithBadHmacAtEnd[] = {
0x00, 0x01, 0x00, 0x14, // message length exactly 20
0x21, 0x12, 0xA4, 0x42, // magic cookie
'0', '1', '2', '3', // transaction ID
@@ -253,7 +252,7 @@ static const unsigned char kStunMessageWithBadHmacAtEnd[] = {
// RTCP packet, for testing we correctly ignore non stun packet types.
// V=2, P=false, RC=0, Type=200, Len=6, Sender-SSRC=85, etc
-static const unsigned char kRtcpPacket[] = {
+static const uint8_t kRtcpPacket[] = {
0x80, 0xc8, 0x00, 0x06, 0x00, 0x00, 0x00, 0x55,
0xce, 0xa5, 0x18, 0x3a, 0x39, 0xcc, 0x7d, 0x09,
0x23, 0xed, 0x19, 0x07, 0x00, 0x00, 0x01, 0x56,
@@ -266,7 +265,7 @@ static const unsigned char kRtcpPacket[] = {
// Software name (response): "test vector" (without quotes)
// Username: "evtj:h6vY" (without quotes)
// Password: "VOkJxbRl1RmTxUk/WvJxBt" (without quotes)
-static const unsigned char kRfc5769SampleMsgTransactionId[] = {
+static const uint8_t kRfc5769SampleMsgTransactionId[] = {
0xb7, 0xe7, 0xa7, 0x01, 0xbc, 0x34, 0xd6, 0x86, 0xfa, 0x87, 0xdf, 0xae
};
static const char kRfc5769SampleMsgClientSoftware[] = "STUN test client";
@@ -278,7 +277,7 @@ static const rtc::SocketAddress kRfc5769SampleMsgMappedAddress(
static const rtc::SocketAddress kRfc5769SampleMsgIPv6MappedAddress(
"2001:db8:1234:5678:11:2233:4455:6677", 32853);
-static const unsigned char kRfc5769SampleMsgWithAuthTransactionId[] = {
+static const uint8_t kRfc5769SampleMsgWithAuthTransactionId[] = {
0x78, 0xad, 0x34, 0x33, 0xc6, 0xad, 0x72, 0xc0, 0x29, 0xda, 0x41, 0x2e
};
static const char kRfc5769SampleMsgWithAuthUsername[] =
@@ -289,7 +288,7 @@ static const char kRfc5769SampleMsgWithAuthNonce[] =
static const char kRfc5769SampleMsgWithAuthRealm[] = "example.org";
// 2.1. Sample Request
-static const unsigned char kRfc5769SampleRequest[] = {
+static const uint8_t kRfc5769SampleRequest[] = {
0x00, 0x01, 0x00, 0x58, // Request type and message length
0x21, 0x12, 0xa4, 0x42, // Magic cookie
0xb7, 0xe7, 0xa7, 0x01, // }
@@ -320,7 +319,7 @@ static const unsigned char kRfc5769SampleRequest[] = {
};
// 2.1. Sample Request
-static const unsigned char kSampleRequestMI32[] = {
+static const uint8_t kSampleRequestMI32[] = {
0x00, 0x01, 0x00, 0x48, // Request type and message length
0x21, 0x12, 0xa4, 0x42, // Magic cookie
0xb7, 0xe7, 0xa7, 0x01, // }
@@ -347,7 +346,7 @@ static const unsigned char kSampleRequestMI32[] = {
};
// 2.2. Sample IPv4 Response
-static const unsigned char kRfc5769SampleResponse[] = {
+static const uint8_t kRfc5769SampleResponse[] = {
0x01, 0x01, 0x00, 0x3c, // Response type and message length
0x21, 0x12, 0xa4, 0x42, // Magic cookie
0xb7, 0xe7, 0xa7, 0x01, // }
@@ -371,7 +370,7 @@ static const unsigned char kRfc5769SampleResponse[] = {
};
// 2.3. Sample IPv6 Response
-static const unsigned char kRfc5769SampleResponseIPv6[] = {
+static const uint8_t kRfc5769SampleResponseIPv6[] = {
0x01, 0x01, 0x00, 0x48, // Response type and message length
0x21, 0x12, 0xa4, 0x42, // Magic cookie
0xb7, 0xe7, 0xa7, 0x01, // }
@@ -398,7 +397,7 @@ static const unsigned char kRfc5769SampleResponseIPv6[] = {
};
// 2.4. Sample Request with Long-Term Authentication
-static const unsigned char kRfc5769SampleRequestLongTermAuth[] = {
+static const uint8_t kRfc5769SampleRequestLongTermAuth[] = {
0x00, 0x01, 0x00, 0x60, // Request type and message length
0x21, 0x12, 0xa4, 0x42, // Magic cookie
0x78, 0xad, 0x34, 0x33, // }
@@ -433,7 +432,7 @@ static const unsigned char kRfc5769SampleRequestLongTermAuth[] = {
// Length parameter is changed to 0x38 from 0x58.
// AddMessageIntegrity will add MI information and update the length param
// accordingly.
-static const unsigned char kRfc5769SampleRequestWithoutMI[] = {
+static const uint8_t kRfc5769SampleRequestWithoutMI[] = {
0x00, 0x01, 0x00, 0x38, // Request type and message length
0x21, 0x12, 0xa4, 0x42, // Magic cookie
0xb7, 0xe7, 0xa7, 0x01, // }
@@ -457,7 +456,7 @@ static const unsigned char kRfc5769SampleRequestWithoutMI[] = {
// This HMAC differs from the RFC 5769 SampleRequest message. This differs
// because spec uses 0x20 for the padding where as our implementation uses 0.
-static const unsigned char kCalculatedHmac1[] = {
+static const uint8_t kCalculatedHmac1[] = {
0x79, 0x07, 0xc2, 0xd2, // }
0xed, 0xbf, 0xea, 0x48, // }
0x0e, 0x4c, 0x76, 0xd8, // } HMAC-SHA1 fingerprint
@@ -469,14 +468,14 @@ static const unsigned char kCalculatedHmac1[] = {
// above since the sum is computed including header
// and the header is different since the message is shorter
// than when MESSAGE-INTEGRITY is used.
-static const unsigned char kCalculatedHmac1_32[] = {
+static const uint8_t kCalculatedHmac1_32[] = {
0xda, 0x39, 0xde, 0x5d, // }
};
// Length parameter is changed to 0x1c from 0x3c.
// AddMessageIntegrity will add MI information and update the length param
// accordingly.
-static const unsigned char kRfc5769SampleResponseWithoutMI[] = {
+static const uint8_t kRfc5769SampleResponseWithoutMI[] = {
0x01, 0x01, 0x00, 0x1c, // Response type and message length
0x21, 0x12, 0xa4, 0x42, // Magic cookie
0xb7, 0xe7, 0xa7, 0x01, // }
@@ -493,7 +492,7 @@ static const unsigned char kRfc5769SampleResponseWithoutMI[] = {
// This HMAC differs from the RFC 5769 SampleResponse message. This differs
// because spec uses 0x20 for the padding where as our implementation uses 0.
-static const unsigned char kCalculatedHmac2[] = {
+static const uint8_t kCalculatedHmac2[] = {
0x5d, 0x6b, 0x58, 0xbe, // }
0xad, 0x94, 0xe0, 0x7e, // }
0xef, 0x0d, 0xfc, 0x12, // } HMAC-SHA1 fingerprint
@@ -505,7 +504,7 @@ static const unsigned char kCalculatedHmac2[] = {
// above since the sum is computed including header
// and the header is different since the message is shorter
// than when MESSAGE-INTEGRITY is used.
-static const unsigned char kCalculatedHmac2_32[] = {
+static const uint8_t kCalculatedHmac2_32[] = {
0xe7, 0x5c, 0xd3, 0x16, // }
};
@@ -513,14 +512,14 @@ static const unsigned char kCalculatedHmac2_32[] = {
// A transaction ID without the 'magic cookie' portion
// pjnat's test programs use this transaction ID a lot.
-const unsigned char kTestTransactionId1[] = {0x029, 0x01f, 0x0cd, 0x07c,
- 0x0ba, 0x058, 0x0ab, 0x0d7,
- 0x0f2, 0x041, 0x001, 0x000};
+const uint8_t kTestTransactionId1[] = {0x029, 0x01f, 0x0cd, 0x07c,
+ 0x0ba, 0x058, 0x0ab, 0x0d7,
+ 0x0f2, 0x041, 0x001, 0x000};
// They use this one sometimes too.
-const unsigned char kTestTransactionId2[] = {0x0e3, 0x0a9, 0x046, 0x0e1,
- 0x07c, 0x000, 0x0c2, 0x062,
- 0x054, 0x008, 0x001, 0x000};
+const uint8_t kTestTransactionId2[] = {0x0e3, 0x0a9, 0x046, 0x0e1,
+ 0x07c, 0x000, 0x0c2, 0x062,
+ 0x054, 0x008, 0x001, 0x000};
const in6_addr kIPv6TestAddress1 = {
{{0x24, 0x01, 0xfa, 0x00, 0x00, 0x04, 0x10, 0x00, 0xbe, 0x30, 0x5b, 0xff,
@@ -743,7 +742,7 @@ TEST_F(StunTest, ReadRfc5769RequestMessageLongTermAuth) {
// kStunMessageWithIPv4MappedAddress, but with a different value where the
// magic cookie was.
TEST_F(StunTest, ReadLegacyMessage) {
- unsigned char rfc3489_packet[sizeof(kStunMessageWithIPv4MappedAddress)];
+ uint8_t rfc3489_packet[sizeof(kStunMessageWithIPv4MappedAddress)];
memcpy(rfc3489_packet, kStunMessageWithIPv4MappedAddress,
sizeof(kStunMessageWithIPv4MappedAddress));
// Overwrite the magic cookie here.
@@ -1122,10 +1121,9 @@ TEST_F(StunTest, WriteMessageWithAUInt16ListAttribute) {
}
// Test that we fail to read messages with invalid lengths.
-void CheckFailureToRead(const unsigned char* testcase, size_t length) {
+void CheckFailureToRead(const uint8_t* testcase, size_t length) {
StunMessage msg;
- const char* input = reinterpret_cast<const char*>(testcase);
- rtc::ByteBufferReader buf(input, length);
+ rtc::ByteBufferReader buf(rtc::MakeArrayView(testcase, length));
ASSERT_FALSE(msg.Read(&buf));
}
@@ -1228,16 +1226,14 @@ TEST_F(StunTest, ValidateMessageIntegrity) {
// the RFC5769 test messages used include attributes not found in basic STUN.
TEST_F(StunTest, AddMessageIntegrity) {
IceMessage msg;
- rtc::ByteBufferReader buf(
- reinterpret_cast<const char*>(kRfc5769SampleRequestWithoutMI),
- sizeof(kRfc5769SampleRequestWithoutMI));
+ rtc::ByteBufferReader buf(kRfc5769SampleRequestWithoutMI);
EXPECT_TRUE(msg.Read(&buf));
EXPECT_TRUE(msg.AddMessageIntegrity(kRfc5769SampleMsgPassword));
const StunByteStringAttribute* mi_attr =
msg.GetByteString(STUN_ATTR_MESSAGE_INTEGRITY);
EXPECT_EQ(20U, mi_attr->length());
- EXPECT_EQ(
- 0, memcmp(mi_attr->bytes(), kCalculatedHmac1, sizeof(kCalculatedHmac1)));
+ EXPECT_EQ(0, memcmp(mi_attr->array_view().data(), kCalculatedHmac1,
+ sizeof(kCalculatedHmac1)));
rtc::ByteBufferWriter buf1;
EXPECT_TRUE(msg.Write(&buf1));
@@ -1246,16 +1242,14 @@ TEST_F(StunTest, AddMessageIntegrity) {
kRfc5769SampleMsgPassword));
IceMessage msg2;
- rtc::ByteBufferReader buf2(
- reinterpret_cast<const char*>(kRfc5769SampleResponseWithoutMI),
- sizeof(kRfc5769SampleResponseWithoutMI));
+ rtc::ByteBufferReader buf2(kRfc5769SampleResponseWithoutMI);
EXPECT_TRUE(msg2.Read(&buf2));
EXPECT_TRUE(msg2.AddMessageIntegrity(kRfc5769SampleMsgPassword));
const StunByteStringAttribute* mi_attr2 =
msg2.GetByteString(STUN_ATTR_MESSAGE_INTEGRITY);
EXPECT_EQ(20U, mi_attr2->length());
- EXPECT_EQ(
- 0, memcmp(mi_attr2->bytes(), kCalculatedHmac2, sizeof(kCalculatedHmac2)));
+ EXPECT_EQ(0, memcmp(mi_attr2->array_view().data(), kCalculatedHmac2,
+ sizeof(kCalculatedHmac2)));
rtc::ByteBufferWriter buf3;
EXPECT_TRUE(msg2.Write(&buf3));
@@ -1321,15 +1315,13 @@ TEST_F(StunTest, ValidateMessageIntegrity32) {
// Validate that we generate correct MESSAGE-INTEGRITY-32 attributes.
TEST_F(StunTest, AddMessageIntegrity32) {
IceMessage msg;
- rtc::ByteBufferReader buf(
- reinterpret_cast<const char*>(kRfc5769SampleRequestWithoutMI),
- sizeof(kRfc5769SampleRequestWithoutMI));
+ rtc::ByteBufferReader buf(kRfc5769SampleRequestWithoutMI);
EXPECT_TRUE(msg.Read(&buf));
EXPECT_TRUE(msg.AddMessageIntegrity32(kRfc5769SampleMsgPassword));
const StunByteStringAttribute* mi_attr =
msg.GetByteString(STUN_ATTR_GOOG_MESSAGE_INTEGRITY_32);
EXPECT_EQ(4U, mi_attr->length());
- EXPECT_EQ(0, memcmp(mi_attr->bytes(), kCalculatedHmac1_32,
+ EXPECT_EQ(0, memcmp(mi_attr->array_view().data(), kCalculatedHmac1_32,
sizeof(kCalculatedHmac1_32)));
rtc::ByteBufferWriter buf1;
@@ -1339,15 +1331,13 @@ TEST_F(StunTest, AddMessageIntegrity32) {
kRfc5769SampleMsgPassword));
IceMessage msg2;
- rtc::ByteBufferReader buf2(
- reinterpret_cast<const char*>(kRfc5769SampleResponseWithoutMI),
- sizeof(kRfc5769SampleResponseWithoutMI));
+ rtc::ByteBufferReader buf2(kRfc5769SampleResponseWithoutMI);
EXPECT_TRUE(msg2.Read(&buf2));
EXPECT_TRUE(msg2.AddMessageIntegrity32(kRfc5769SampleMsgPassword));
const StunByteStringAttribute* mi_attr2 =
msg2.GetByteString(STUN_ATTR_GOOG_MESSAGE_INTEGRITY_32);
EXPECT_EQ(4U, mi_attr2->length());
- EXPECT_EQ(0, memcmp(mi_attr2->bytes(), kCalculatedHmac2_32,
+ EXPECT_EQ(0, memcmp(mi_attr2->array_view().data(), kCalculatedHmac2_32,
sizeof(kCalculatedHmac2_32)));
rtc::ByteBufferWriter buf3;
@@ -1420,9 +1410,7 @@ TEST_F(StunTest, ValidateFingerprint) {
TEST_F(StunTest, AddFingerprint) {
IceMessage msg;
- rtc::ByteBufferReader buf(
- reinterpret_cast<const char*>(kRfc5769SampleRequestWithoutMI),
- sizeof(kRfc5769SampleRequestWithoutMI));
+ rtc::ByteBufferReader buf(kRfc5769SampleRequestWithoutMI);
EXPECT_TRUE(msg.Read(&buf));
EXPECT_TRUE(msg.AddFingerprint());
@@ -1435,7 +1423,7 @@ TEST_F(StunTest, AddFingerprint) {
// Sample "GTURN" relay message.
// clang-format off
// clang formatting doesn't respect inline comments.
-static const unsigned char kRelayMessage[] = {
+static const uint8_t kRelayMessage[] = {
0x00, 0x01, 0x00, 88, // message header
0x21, 0x12, 0xA4, 0x42, // magic cookie
'0', '1', '2', '3', // transaction id
@@ -1470,13 +1458,11 @@ static const unsigned char kRelayMessage[] = {
TEST_F(StunTest, ReadRelayMessage) {
RelayMessage msg;
- const char* input = reinterpret_cast<const char*>(kRelayMessage);
- size_t size = sizeof(kRelayMessage);
- rtc::ByteBufferReader buf(input, size);
+ rtc::ByteBufferReader buf(kRelayMessage);
EXPECT_TRUE(msg.Read(&buf));
EXPECT_EQ(STUN_BINDING_REQUEST, msg.type());
- EXPECT_EQ(size - 20, msg.length());
+ EXPECT_EQ(sizeof(kRelayMessage) - 20, msg.length());
EXPECT_EQ("0123456789ab", msg.transaction_id());
RelayMessage msg2(STUN_BINDING_REQUEST, "0123456789ab");
@@ -1516,7 +1502,7 @@ TEST_F(StunTest, ReadRelayMessage) {
bytes = msg.GetByteString(STUN_ATTR_MAGIC_COOKIE);
ASSERT_TRUE(bytes != NULL);
EXPECT_EQ(4U, bytes->length());
- EXPECT_EQ(0, memcmp(bytes->bytes(), TURN_MAGIC_COOKIE_VALUE,
+ EXPECT_EQ(0, memcmp(bytes->array_view().data(), TURN_MAGIC_COOKIE_VALUE,
sizeof(TURN_MAGIC_COOKIE_VALUE)));
bytes2 = StunAttribute::CreateByteString(STUN_ATTR_MAGIC_COOKIE);
@@ -1565,21 +1551,21 @@ TEST_F(StunTest, ReadRelayMessage) {
rtc::ByteBufferWriter out;
EXPECT_TRUE(msg.Write(&out));
- EXPECT_EQ(size, out.Length());
+ EXPECT_EQ(sizeof(kRelayMessage), out.Length());
size_t len1 = out.Length();
rtc::ByteBufferReader read_buf(out);
std::string outstring;
read_buf.ReadString(&outstring, len1);
- EXPECT_EQ(0, memcmp(outstring.c_str(), input, len1));
+ EXPECT_EQ(0, memcmp(outstring.c_str(), kRelayMessage, len1));
rtc::ByteBufferWriter out2;
EXPECT_TRUE(msg2.Write(&out2));
- EXPECT_EQ(size, out2.Length());
+ EXPECT_EQ(sizeof(kRelayMessage), out2.Length());
size_t len2 = out2.Length();
rtc::ByteBufferReader read_buf2(out2);
std::string outstring2;
read_buf2.ReadString(&outstring2, len2);
- EXPECT_EQ(0, memcmp(outstring2.c_str(), input, len2));
+ EXPECT_EQ(0, memcmp(outstring2.c_str(), kRelayMessage, len2));
}
// Test that we can remove attribute from a message.
@@ -1600,8 +1586,9 @@ TEST_F(StunTest, RemoveAttribute) {
auto attr = msg.RemoveAttribute(STUN_ATTR_USERNAME);
ASSERT_NE(attr, nullptr);
EXPECT_EQ(attr->type(), STUN_ATTR_USERNAME);
- EXPECT_STREQ("kes",
- static_cast<StunByteStringAttribute*>(attr.get())->bytes());
+ EXPECT_STREQ("kes", static_cast<StunByteStringAttribute*>(attr.get())
+ ->string_view()
+ .data());
EXPECT_LT(msg.length(), len);
}
@@ -1623,8 +1610,9 @@ TEST_F(StunTest, RemoveAttribute) {
auto attr = msg.RemoveAttribute(STUN_ATTR_USERNAME);
ASSERT_NE(attr, nullptr);
EXPECT_EQ(attr->type(), STUN_ATTR_USERNAME);
- EXPECT_STREQ("kenta",
- static_cast<StunByteStringAttribute*>(attr.get())->bytes());
+ EXPECT_STREQ("kenta", static_cast<StunByteStringAttribute*>(attr.get())
+ ->string_view()
+ .data());
}
// Remove should remove the last added occurrence.
@@ -1632,8 +1620,9 @@ TEST_F(StunTest, RemoveAttribute) {
auto attr = msg.RemoveAttribute(STUN_ATTR_USERNAME);
ASSERT_NE(attr, nullptr);
EXPECT_EQ(attr->type(), STUN_ATTR_USERNAME);
- EXPECT_STREQ("kes",
- static_cast<StunByteStringAttribute*>(attr.get())->bytes());
+ EXPECT_STREQ("kes", static_cast<StunByteStringAttribute*>(attr.get())
+ ->string_view()
+ .data());
}
// Removing something that does exist should return nullptr.
@@ -1666,8 +1655,9 @@ TEST_F(StunTest, CopyAttribute) {
auto copy = CopyStunAttribute(*attr.get(), buffer_ptr);
ASSERT_EQ(copy->value_type(), STUN_VALUE_BYTE_STRING);
- EXPECT_STREQ("kes",
- static_cast<StunByteStringAttribute*>(copy.get())->bytes());
+ EXPECT_STREQ("kes", static_cast<StunByteStringAttribute*>(copy.get())
+ ->string_view()
+ .data());
}
{ // Test StunAddressAttribute.
@@ -1826,7 +1816,7 @@ TEST_F(StunTest, GoogMiscInfo) {
ASSERT_EQ(size, out.Length());
size_t read_size = ReadStunMessageTestCase(
- &msg, reinterpret_cast<const unsigned char*>(out.Data()), out.Length());
+ &msg, reinterpret_cast<const uint8_t*>(out.Data()), out.Length());
ASSERT_EQ(read_size + 20, size);
CheckStunHeader(msg, STUN_BINDING_REQUEST, read_size);
const StunUInt16ListAttribute* types =
@@ -1860,9 +1850,7 @@ TEST_F(StunTest, ValidateMessageIntegrityWithParser) {
webrtc::metrics::Reset(); // Ensure counters start from zero.
// Try the messages from RFC 5769.
StunMessage message;
- rtc::ByteBufferReader reader(
- reinterpret_cast<const char*>(kRfc5769SampleRequest),
- sizeof(kRfc5769SampleRequest));
+ rtc::ByteBufferReader reader(kRfc5769SampleRequest);
EXPECT_TRUE(message.Read(&reader));
EXPECT_EQ(message.ValidateMessageIntegrity(kRfc5769SampleMsgPassword),
StunMessage::IntegrityStatus::kIntegrityOk);
diff --git a/third_party/libwebrtc/api/transport_api_gn/moz.build b/third_party/libwebrtc/api/transport_api_gn/moz.build
index af080bee0b..506cb2f24d 100644
--- a/third_party/libwebrtc/api/transport_api_gn/moz.build
+++ b/third_party/libwebrtc/api/transport_api_gn/moz.build
@@ -188,7 +188,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -198,10 +197,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/units/data_rate_gn/moz.build b/third_party/libwebrtc/api/units/data_rate_gn/moz.build
index 4964c3e05f..b2bb9b7c5f 100644
--- a/third_party/libwebrtc/api/units/data_rate_gn/moz.build
+++ b/third_party/libwebrtc/api/units/data_rate_gn/moz.build
@@ -188,7 +188,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -198,10 +197,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/units/data_size_gn/moz.build b/third_party/libwebrtc/api/units/data_size_gn/moz.build
index 0fe0bc100b..ceaa9da9a2 100644
--- a/third_party/libwebrtc/api/units/data_size_gn/moz.build
+++ b/third_party/libwebrtc/api/units/data_size_gn/moz.build
@@ -188,7 +188,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -198,10 +197,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/units/frequency_gn/moz.build b/third_party/libwebrtc/api/units/frequency_gn/moz.build
index 413a57a8f3..8b0607cf75 100644
--- a/third_party/libwebrtc/api/units/frequency_gn/moz.build
+++ b/third_party/libwebrtc/api/units/frequency_gn/moz.build
@@ -188,7 +188,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -198,10 +197,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/units/time_delta_gn/moz.build b/third_party/libwebrtc/api/units/time_delta_gn/moz.build
index 39355ed588..6220551614 100644
--- a/third_party/libwebrtc/api/units/time_delta_gn/moz.build
+++ b/third_party/libwebrtc/api/units/time_delta_gn/moz.build
@@ -188,7 +188,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -198,10 +197,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/units/timestamp_gn/moz.build b/third_party/libwebrtc/api/units/timestamp_gn/moz.build
index da68eea160..083b864ff0 100644
--- a/third_party/libwebrtc/api/units/timestamp_gn/moz.build
+++ b/third_party/libwebrtc/api/units/timestamp_gn/moz.build
@@ -188,7 +188,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -198,10 +197,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/video/BUILD.gn b/third_party/libwebrtc/api/video/BUILD.gn
index 807fdcc3a9..5ec689c096 100644
--- a/third_party/libwebrtc/api/video/BUILD.gn
+++ b/third_party/libwebrtc/api/video/BUILD.gn
@@ -67,6 +67,7 @@ rtc_library("video_frame") {
":video_rtp_headers",
"..:array_view",
"..:make_ref_counted",
+ "..:ref_count",
"..:rtp_packet_info",
"..:scoped_refptr",
"..:video_track_source_constraints",
diff --git a/third_party/libwebrtc/api/video/builtin_video_bitrate_allocator_factory_gn/moz.build b/third_party/libwebrtc/api/video/builtin_video_bitrate_allocator_factory_gn/moz.build
index cb32b05fa6..620fba65aa 100644
--- a/third_party/libwebrtc/api/video/builtin_video_bitrate_allocator_factory_gn/moz.build
+++ b/third_party/libwebrtc/api/video/builtin_video_bitrate_allocator_factory_gn/moz.build
@@ -200,7 +200,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -210,10 +209,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/video/encoded_frame_gn/moz.build b/third_party/libwebrtc/api/video/encoded_frame_gn/moz.build
index fdb34bf903..4c92d824ae 100644
--- a/third_party/libwebrtc/api/video/encoded_frame_gn/moz.build
+++ b/third_party/libwebrtc/api/video/encoded_frame_gn/moz.build
@@ -195,7 +195,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -205,10 +204,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/video/encoded_image_gn/moz.build b/third_party/libwebrtc/api/video/encoded_image_gn/moz.build
index 3bc012ad28..25d2d0998e 100644
--- a/third_party/libwebrtc/api/video/encoded_image_gn/moz.build
+++ b/third_party/libwebrtc/api/video/encoded_image_gn/moz.build
@@ -195,7 +195,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -205,10 +204,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/video/frame_buffer_gn/moz.build b/third_party/libwebrtc/api/video/frame_buffer_gn/moz.build
index 2614e67133..048097ce50 100644
--- a/third_party/libwebrtc/api/video/frame_buffer_gn/moz.build
+++ b/third_party/libwebrtc/api/video/frame_buffer_gn/moz.build
@@ -200,7 +200,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -210,10 +209,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/video/recordable_encoded_frame_gn/moz.build b/third_party/libwebrtc/api/video/recordable_encoded_frame_gn/moz.build
index ace02623f7..a5b4399e4a 100644
--- a/third_party/libwebrtc/api/video/recordable_encoded_frame_gn/moz.build
+++ b/third_party/libwebrtc/api/video/recordable_encoded_frame_gn/moz.build
@@ -187,16 +187,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/video/render_resolution_gn/moz.build b/third_party/libwebrtc/api/video/render_resolution_gn/moz.build
index f27e4cc944..6ee89b6451 100644
--- a/third_party/libwebrtc/api/video/render_resolution_gn/moz.build
+++ b/third_party/libwebrtc/api/video/render_resolution_gn/moz.build
@@ -176,16 +176,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/video/resolution_gn/moz.build b/third_party/libwebrtc/api/video/resolution_gn/moz.build
index 673bb4f1c9..de79a64a66 100644
--- a/third_party/libwebrtc/api/video/resolution_gn/moz.build
+++ b/third_party/libwebrtc/api/video/resolution_gn/moz.build
@@ -176,16 +176,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/video/video_adaptation_gn/moz.build b/third_party/libwebrtc/api/video/video_adaptation_gn/moz.build
index ffff5639ee..4483b3eaef 100644
--- a/third_party/libwebrtc/api/video/video_adaptation_gn/moz.build
+++ b/third_party/libwebrtc/api/video/video_adaptation_gn/moz.build
@@ -188,7 +188,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -198,10 +197,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/video/video_bitrate_allocation_gn/moz.build b/third_party/libwebrtc/api/video/video_bitrate_allocation_gn/moz.build
index be63dc8252..1196221e63 100644
--- a/third_party/libwebrtc/api/video/video_bitrate_allocation_gn/moz.build
+++ b/third_party/libwebrtc/api/video/video_bitrate_allocation_gn/moz.build
@@ -188,7 +188,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -198,10 +197,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/video/video_bitrate_allocator_factory_gn/moz.build b/third_party/libwebrtc/api/video/video_bitrate_allocator_factory_gn/moz.build
index 222bcaf251..ff4934b7b5 100644
--- a/third_party/libwebrtc/api/video/video_bitrate_allocator_factory_gn/moz.build
+++ b/third_party/libwebrtc/api/video/video_bitrate_allocator_factory_gn/moz.build
@@ -187,16 +187,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/video/video_bitrate_allocator_gn/moz.build b/third_party/libwebrtc/api/video/video_bitrate_allocator_gn/moz.build
index e7f3f5a4de..758cbb7521 100644
--- a/third_party/libwebrtc/api/video/video_bitrate_allocator_gn/moz.build
+++ b/third_party/libwebrtc/api/video/video_bitrate_allocator_gn/moz.build
@@ -188,7 +188,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -198,10 +197,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/video/video_codec_constants_gn/moz.build b/third_party/libwebrtc/api/video/video_codec_constants_gn/moz.build
index 403c521a1f..2275aa4aa1 100644
--- a/third_party/libwebrtc/api/video/video_codec_constants_gn/moz.build
+++ b/third_party/libwebrtc/api/video/video_codec_constants_gn/moz.build
@@ -176,16 +176,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/video/video_frame.h b/third_party/libwebrtc/api/video/video_frame.h
index 2608f9aa42..5b77bcca23 100644
--- a/third_party/libwebrtc/api/video/video_frame.h
+++ b/third_party/libwebrtc/api/video/video_frame.h
@@ -33,10 +33,10 @@ class RTC_EXPORT VideoFrame {
static constexpr uint16_t kNotSetId = 0;
struct RTC_EXPORT UpdateRect {
- int offset_x;
- int offset_y;
- int width;
- int height;
+ int offset_x = 0;
+ int offset_y = 0;
+ int width = 0;
+ int height = 0;
// Makes this UpdateRect a bounding box of this and other rect.
void Union(const UpdateRect& other);
diff --git a/third_party/libwebrtc/api/video/video_frame_buffer.h b/third_party/libwebrtc/api/video/video_frame_buffer.h
index aaf786699f..ca6e9067db 100644
--- a/third_party/libwebrtc/api/video/video_frame_buffer.h
+++ b/third_party/libwebrtc/api/video/video_frame_buffer.h
@@ -14,8 +14,8 @@
#include <stdint.h>
#include "api/array_view.h"
+#include "api/ref_count.h"
#include "api/scoped_refptr.h"
-#include "rtc_base/ref_count.h"
#include "rtc_base/system/rtc_export.h"
namespace webrtc {
@@ -44,7 +44,7 @@ class NV12BufferInterface;
// performance by providing an optimized path without intermediate conversions.
// Frame metadata such as rotation and timestamp are stored in
// webrtc::VideoFrame, and not here.
-class RTC_EXPORT VideoFrameBuffer : public rtc::RefCountInterface {
+class RTC_EXPORT VideoFrameBuffer : public webrtc::RefCountInterface {
public:
// New frame buffer types will be added conservatively when there is an
// opportunity to optimize the path between some pair of video source and
diff --git a/third_party/libwebrtc/api/video/video_frame_gn/moz.build b/third_party/libwebrtc/api/video/video_frame_gn/moz.build
index b0fc90582b..203b03a0a3 100644
--- a/third_party/libwebrtc/api/video/video_frame_gn/moz.build
+++ b/third_party/libwebrtc/api/video/video_frame_gn/moz.build
@@ -206,7 +206,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -216,10 +215,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/video/video_frame_i010_gn/moz.build b/third_party/libwebrtc/api/video/video_frame_i010_gn/moz.build
index 7fe6e4df95..1243513335 100644
--- a/third_party/libwebrtc/api/video/video_frame_i010_gn/moz.build
+++ b/third_party/libwebrtc/api/video/video_frame_i010_gn/moz.build
@@ -202,7 +202,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -212,10 +211,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/video/video_frame_metadata_gn/moz.build b/third_party/libwebrtc/api/video/video_frame_metadata_gn/moz.build
index d80dda9178..6616ea0dd6 100644
--- a/third_party/libwebrtc/api/video/video_frame_metadata_gn/moz.build
+++ b/third_party/libwebrtc/api/video/video_frame_metadata_gn/moz.build
@@ -195,7 +195,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -205,10 +204,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/video/video_frame_type_gn/moz.build b/third_party/libwebrtc/api/video/video_frame_type_gn/moz.build
index 8fcbef76e8..ab7548fe8d 100644
--- a/third_party/libwebrtc/api/video/video_frame_type_gn/moz.build
+++ b/third_party/libwebrtc/api/video/video_frame_type_gn/moz.build
@@ -180,16 +180,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/video/video_layers_allocation_gn/moz.build b/third_party/libwebrtc/api/video/video_layers_allocation_gn/moz.build
index b8ba6ec54b..7f88b15c07 100644
--- a/third_party/libwebrtc/api/video/video_layers_allocation_gn/moz.build
+++ b/third_party/libwebrtc/api/video/video_layers_allocation_gn/moz.build
@@ -180,16 +180,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/video/video_rtp_headers_gn/moz.build b/third_party/libwebrtc/api/video/video_rtp_headers_gn/moz.build
index f65965f80b..2f02f285f2 100644
--- a/third_party/libwebrtc/api/video/video_rtp_headers_gn/moz.build
+++ b/third_party/libwebrtc/api/video/video_rtp_headers_gn/moz.build
@@ -198,7 +198,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -208,10 +207,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/video/video_stream_encoder_gn/moz.build b/third_party/libwebrtc/api/video/video_stream_encoder_gn/moz.build
index 7b8a329463..f75eac803f 100644
--- a/third_party/libwebrtc/api/video/video_stream_encoder_gn/moz.build
+++ b/third_party/libwebrtc/api/video/video_stream_encoder_gn/moz.build
@@ -187,16 +187,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/video_codecs/bitstream_parser_api_gn/moz.build b/third_party/libwebrtc/api/video_codecs/bitstream_parser_api_gn/moz.build
index c3642c46d0..866df39c97 100644
--- a/third_party/libwebrtc/api/video_codecs/bitstream_parser_api_gn/moz.build
+++ b/third_party/libwebrtc/api/video_codecs/bitstream_parser_api_gn/moz.build
@@ -180,16 +180,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/video_codecs/rtc_software_fallback_wrappers_gn/moz.build b/third_party/libwebrtc/api/video_codecs/rtc_software_fallback_wrappers_gn/moz.build
index 4e928c91a4..e8139cb6f2 100644
--- a/third_party/libwebrtc/api/video_codecs/rtc_software_fallback_wrappers_gn/moz.build
+++ b/third_party/libwebrtc/api/video_codecs/rtc_software_fallback_wrappers_gn/moz.build
@@ -201,7 +201,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -211,10 +210,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/video_codecs/scalability_mode_gn/moz.build b/third_party/libwebrtc/api/video_codecs/scalability_mode_gn/moz.build
index d63795ed94..491c4880cc 100644
--- a/third_party/libwebrtc/api/video_codecs/scalability_mode_gn/moz.build
+++ b/third_party/libwebrtc/api/video_codecs/scalability_mode_gn/moz.build
@@ -188,7 +188,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -198,10 +197,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/video_codecs/video_codecs_api_gn/moz.build b/third_party/libwebrtc/api/video_codecs/video_codecs_api_gn/moz.build
index 89cce4215b..c6c127e5b6 100644
--- a/third_party/libwebrtc/api/video_codecs/video_codecs_api_gn/moz.build
+++ b/third_party/libwebrtc/api/video_codecs/video_codecs_api_gn/moz.build
@@ -205,7 +205,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -215,10 +214,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/video_codecs/vp8_temporal_layers_factory_gn/moz.build b/third_party/libwebrtc/api/video_codecs/vp8_temporal_layers_factory_gn/moz.build
index e757e9ed51..7131057c2f 100644
--- a/third_party/libwebrtc/api/video_codecs/vp8_temporal_layers_factory_gn/moz.build
+++ b/third_party/libwebrtc/api/video_codecs/vp8_temporal_layers_factory_gn/moz.build
@@ -200,7 +200,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -210,10 +209,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/video_track_source_constraints_gn/moz.build b/third_party/libwebrtc/api/video_track_source_constraints_gn/moz.build
index 8213e3088f..9e9852b71f 100644
--- a/third_party/libwebrtc/api/video_track_source_constraints_gn/moz.build
+++ b/third_party/libwebrtc/api/video_track_source_constraints_gn/moz.build
@@ -176,16 +176,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/api/wrapping_async_dns_resolver.h b/third_party/libwebrtc/api/wrapping_async_dns_resolver.h
deleted file mode 100644
index b384f97652..0000000000
--- a/third_party/libwebrtc/api/wrapping_async_dns_resolver.h
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright 2021 The WebRTC Project Authors. All rights reserved.
- *
- * Use of this source code is governed by a BSD-style license
- * that can be found in the LICENSE file in the root of the source
- * tree. An additional intellectual property rights grant can be found
- * in the file PATENTS. All contributing project authors may
- * be found in the AUTHORS file in the root of the source tree.
- */
-
-#ifndef API_WRAPPING_ASYNC_DNS_RESOLVER_H_
-#define API_WRAPPING_ASYNC_DNS_RESOLVER_H_
-
-#include <functional>
-#include <memory>
-#include <utility>
-
-#include "absl/memory/memory.h"
-#include "api/async_dns_resolver.h"
-#include "api/sequence_checker.h"
-#include "rtc_base/async_resolver.h"
-#include "rtc_base/async_resolver_interface.h"
-#include "rtc_base/checks.h"
-#include "rtc_base/socket_address.h"
-#include "rtc_base/third_party/sigslot/sigslot.h"
-#include "rtc_base/thread_annotations.h"
-
-// This file defines a DNS resolver that wraps an old-style
-// AsyncResolver.
-// It is part of the conversion to the newer interface, and will go away
-// once conversion is finished.
-// TODO(bugs.webrtc.org/12598): Delete this API.
-
-namespace webrtc {
-
-class [[deprecated("Use AsyncDnsResolver directly")]] WrappingAsyncDnsResolver;
-
-class [[deprecated(
- "Use AsyncDnsResolver directly")]] RTC_EXPORT WrappingAsyncDnsResolverResult
- : public AsyncDnsResolverResult {
- public:
- explicit WrappingAsyncDnsResolverResult(WrappingAsyncDnsResolver* owner)
- : owner_(owner) {}
- ~WrappingAsyncDnsResolverResult() {}
-
- // Note: Inline declaration not possible, since it refers to
- // WrappingAsyncDnsResolver.
- bool GetResolvedAddress(int family, rtc::SocketAddress* addr) const override;
- int GetError() const override;
-
- private:
- WrappingAsyncDnsResolver* const owner_;
-};
-
-class RTC_EXPORT WrappingAsyncDnsResolver : public AsyncDnsResolverInterface,
- public sigslot::has_slots<> {
- public:
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
- explicit WrappingAsyncDnsResolver(rtc::AsyncResolverInterface* wrapped)
- : wrapped_(absl::WrapUnique(wrapped)), result_(this) {}
-
- ~WrappingAsyncDnsResolver() override {
- // Workaround to get around the fact that sigslot-using objects can't be
- // destroyed from within their callback: Alert class users early.
- // TODO(bugs.webrtc.org/12651): Delete this class once the sigslot users are
- // gone.
- RTC_CHECK(!within_resolve_result_);
- wrapped_.release()->Destroy(false);
- }
-
- void Start(const rtc::SocketAddress& addr,
- absl::AnyInvocable<void()> callback) override {
- RTC_DCHECK_RUN_ON(&sequence_checker_);
- PrepareToResolve(std::move(callback));
- wrapped_->Start(addr);
- }
-
- void Start(const rtc::SocketAddress& addr,
- int family,
- absl::AnyInvocable<void()> callback) override {
- RTC_DCHECK_RUN_ON(&sequence_checker_);
- PrepareToResolve(std::move(callback));
- wrapped_->Start(addr, family);
- }
-
- const AsyncDnsResolverResult& result() const override {
- RTC_DCHECK_RUN_ON(&sequence_checker_);
- RTC_DCHECK_EQ(State::kResolved, state_);
- return result_;
- }
-
- private:
- enum class State { kNotStarted, kStarted, kResolved };
-
- friend class WrappingAsyncDnsResolverResult;
- // For use by WrappingAsyncDnsResolverResult
- rtc::AsyncResolverInterface* wrapped() const {
- RTC_DCHECK_RUN_ON(&sequence_checker_);
- return wrapped_.get();
- }
-
- void PrepareToResolve(absl::AnyInvocable<void()> callback) {
- RTC_DCHECK_RUN_ON(&sequence_checker_);
- RTC_DCHECK_EQ(State::kNotStarted, state_);
- state_ = State::kStarted;
- callback_ = std::move(callback);
- wrapped_->SignalDone.connect(this,
- &WrappingAsyncDnsResolver::OnResolveResult);
- }
-
- void OnResolveResult(rtc::AsyncResolverInterface* ref) {
- RTC_DCHECK_RUN_ON(&sequence_checker_);
- RTC_DCHECK(state_ == State::kStarted);
- RTC_DCHECK_EQ(ref, wrapped_.get());
- state_ = State::kResolved;
- within_resolve_result_ = true;
- callback_();
- within_resolve_result_ = false;
- }
-
- // The class variables need to be accessed on a single thread.
- SequenceChecker sequence_checker_;
- absl::AnyInvocable<void()> callback_ RTC_GUARDED_BY(sequence_checker_);
- std::unique_ptr<rtc::AsyncResolverInterface> wrapped_
- RTC_GUARDED_BY(sequence_checker_);
- State state_ RTC_GUARDED_BY(sequence_checker_) = State::kNotStarted;
- WrappingAsyncDnsResolverResult result_ RTC_GUARDED_BY(sequence_checker_);
- bool within_resolve_result_ RTC_GUARDED_BY(sequence_checker_) = false;
-#pragma clang diagnostic pop
-};
-
-} // namespace webrtc
-
-#endif // API_WRAPPING_ASYNC_DNS_RESOLVER_H_
diff --git a/third_party/libwebrtc/audio/BUILD.gn b/third_party/libwebrtc/audio/BUILD.gn
index ec09e5a350..09562b9131 100644
--- a/third_party/libwebrtc/audio/BUILD.gn
+++ b/third_party/libwebrtc/audio/BUILD.gn
@@ -223,6 +223,7 @@ if (rtc_include_tests) {
"utility:utility_tests",
"//testing/gtest",
]
+ absl_deps = [ "//third_party/abseil-cpp/absl/memory" ]
}
rtc_library("channel_receive_unittest") {
@@ -241,6 +242,7 @@ if (rtc_include_tests) {
"../rtc_base:logging",
"../rtc_base:threading",
"../test:audio_codec_mocks",
+ "../test:mock_frame_transformer",
"../test:mock_transport",
"../test:test_support",
"../test/time_controller",
diff --git a/third_party/libwebrtc/audio/audio_gn/moz.build b/third_party/libwebrtc/audio/audio_gn/moz.build
index e81a4f673b..da615157b2 100644
--- a/third_party/libwebrtc/audio/audio_gn/moz.build
+++ b/third_party/libwebrtc/audio/audio_gn/moz.build
@@ -212,7 +212,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -222,10 +221,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/audio/audio_send_stream.cc b/third_party/libwebrtc/audio/audio_send_stream.cc
index bffb910832..e7ebb2bf4e 100644
--- a/third_party/libwebrtc/audio/audio_send_stream.cc
+++ b/third_party/libwebrtc/audio/audio_send_stream.cc
@@ -637,12 +637,14 @@ bool AudioSendStream::SetupSendCodec(const Config& new_config) {
}
// Wrap the encoder in a RED encoder, if RED is enabled.
+ SdpAudioFormat format = spec.format;
if (spec.red_payload_type) {
AudioEncoderCopyRed::Config red_config;
red_config.payload_type = *spec.red_payload_type;
red_config.speech_encoder = std::move(encoder);
encoder = std::make_unique<AudioEncoderCopyRed>(std::move(red_config),
field_trials_);
+ format.name = cricket::kRedCodecName;
}
// Set currently known overhead (used in ANA, opus only).
@@ -656,7 +658,7 @@ bool AudioSendStream::SetupSendCodec(const Config& new_config) {
}
StoreEncoderProperties(encoder->SampleRateHz(), encoder->NumChannels());
- channel_send_->SetEncoder(new_config.send_codec_spec->payload_type,
+ channel_send_->SetEncoder(new_config.send_codec_spec->payload_type, format,
std::move(encoder));
return true;
diff --git a/third_party/libwebrtc/audio/audio_send_stream_unittest.cc b/third_party/libwebrtc/audio/audio_send_stream_unittest.cc
index d842afdfe5..c854f734b5 100644
--- a/third_party/libwebrtc/audio/audio_send_stream_unittest.cc
+++ b/third_party/libwebrtc/audio/audio_send_stream_unittest.cc
@@ -242,11 +242,11 @@ struct ConfigHelper {
void SetupMockForSetupSendCodec(bool expect_set_encoder_call) {
if (expect_set_encoder_call) {
EXPECT_CALL(*channel_send_, SetEncoder)
- .WillOnce(
- [this](int payload_type, std::unique_ptr<AudioEncoder> encoder) {
- this->audio_encoder_ = std::move(encoder);
- return true;
- });
+ .WillOnce([this](int payload_type, const SdpAudioFormat& format,
+ std::unique_ptr<AudioEncoder> encoder) {
+ this->audio_encoder_ = std::move(encoder);
+ return true;
+ });
}
}
@@ -595,6 +595,7 @@ TEST(AudioSendStreamTest, SendCodecCanApplyVad) {
std::unique_ptr<AudioEncoder> stolen_encoder;
EXPECT_CALL(*helper.channel_send(), SetEncoder)
.WillOnce([&stolen_encoder](int payload_type,
+ const SdpAudioFormat& format,
std::unique_ptr<AudioEncoder> encoder) {
stolen_encoder = std::move(encoder);
return true;
diff --git a/third_party/libwebrtc/audio/channel_receive.cc b/third_party/libwebrtc/audio/channel_receive.cc
index c714b1dd4d..aff21fa72a 100644
--- a/third_party/libwebrtc/audio/channel_receive.cc
+++ b/third_party/libwebrtc/audio/channel_receive.cc
@@ -47,6 +47,7 @@
#include "rtc_base/numerics/safe_minmax.h"
#include "rtc_base/numerics/sequence_number_unwrapper.h"
#include "rtc_base/race_checker.h"
+#include "rtc_base/strings/string_builder.h"
#include "rtc_base/synchronization/mutex.h"
#include "rtc_base/system/no_unique_address.h"
#include "rtc_base/time_utils.h"
@@ -313,6 +314,8 @@ class ChannelReceive : public ChannelReceiveInterface,
mutable Mutex rtcp_counter_mutex_;
RtcpPacketTypeCounter rtcp_packet_type_counter_
RTC_GUARDED_BY(rtcp_counter_mutex_);
+
+ std::map<int, SdpAudioFormat> payload_type_map_;
};
void ChannelReceive::OnReceivedPayloadData(
@@ -639,6 +642,7 @@ void ChannelReceive::SetReceiveCodecs(
RTC_DCHECK_GE(kv.second.clockrate_hz, 1000);
payload_type_frequencies_[kv.first] = kv.second.clockrate_hz;
}
+ payload_type_map_ = codecs;
acm_receiver_.SetCodecs(codecs);
}
@@ -725,7 +729,14 @@ void ChannelReceive::ReceivePacket(const uint8_t* packet,
if (frame_transformer_delegate_) {
// Asynchronously transform the received payload. After the payload is
// transformed, the delegate will call OnReceivedPayloadData to handle it.
- frame_transformer_delegate_->Transform(payload_data, header, remote_ssrc_);
+ char buf[1024];
+ rtc::SimpleStringBuilder mime_type(buf);
+ auto it = payload_type_map_.find(header.payloadType);
+ mime_type << MediaTypeToString(cricket::MEDIA_TYPE_AUDIO) << "/"
+ << (it != payload_type_map_.end() ? it->second.name
+ : "x-unknown");
+ frame_transformer_delegate_->Transform(payload_data, header, remote_ssrc_,
+ mime_type.str());
} else {
OnReceivedPayloadData(payload_data, header);
}
@@ -917,12 +928,19 @@ void ChannelReceive::SetAssociatedSendChannel(
void ChannelReceive::SetDepacketizerToDecoderFrameTransformer(
rtc::scoped_refptr<webrtc::FrameTransformerInterface> frame_transformer) {
RTC_DCHECK_RUN_ON(&worker_thread_checker_);
- // Depending on when the channel is created, the transformer might be set
- // twice. Don't replace the delegate if it was already initialized.
- if (!frame_transformer || frame_transformer_delegate_) {
+ if (!frame_transformer) {
RTC_DCHECK_NOTREACHED() << "Not setting the transformer?";
return;
}
+ if (frame_transformer_delegate_) {
+ // Depending on when the channel is created, the transformer might be set
+ // twice. Don't replace the delegate if it was already initialized.
+ // TODO(crbug.com/webrtc/15674): Prevent multiple calls during
+ // reconfiguration.
+ RTC_CHECK_EQ(frame_transformer_delegate_->FrameTransformer(),
+ frame_transformer);
+ return;
+ }
InitFrameTransformerDelegate(std::move(frame_transformer));
}
diff --git a/third_party/libwebrtc/audio/channel_receive_frame_transformer_delegate.cc b/third_party/libwebrtc/audio/channel_receive_frame_transformer_delegate.cc
index 2d2893b8f7..8a7dda826e 100644
--- a/third_party/libwebrtc/audio/channel_receive_frame_transformer_delegate.cc
+++ b/third_party/libwebrtc/audio/channel_receive_frame_transformer_delegate.cc
@@ -10,6 +10,7 @@
#include "audio/channel_receive_frame_transformer_delegate.h"
+#include <string>
#include <utility>
#include "rtc_base/buffer.h"
@@ -22,10 +23,12 @@ class TransformableIncomingAudioFrame
public:
TransformableIncomingAudioFrame(rtc::ArrayView<const uint8_t> payload,
const RTPHeader& header,
- uint32_t ssrc)
+ uint32_t ssrc,
+ const std::string& codec_mime_type)
: payload_(payload.data(), payload.size()),
header_(header),
- ssrc_(ssrc) {}
+ ssrc_(ssrc),
+ codec_mime_type_(codec_mime_type) {}
~TransformableIncomingAudioFrame() override = default;
rtc::ArrayView<const uint8_t> GetData() const override { return payload_; }
@@ -45,6 +48,7 @@ class TransformableIncomingAudioFrame
}
Direction GetDirection() const override { return Direction::kReceiver; }
+ std::string GetMimeType() const override { return codec_mime_type_; }
const absl::optional<uint16_t> SequenceNumber() const override {
return header_.sequenceNumber;
}
@@ -65,6 +69,7 @@ class TransformableIncomingAudioFrame
rtc::Buffer payload_;
RTPHeader header_;
uint32_t ssrc_;
+ std::string codec_mime_type_;
};
} // namespace
@@ -92,10 +97,16 @@ void ChannelReceiveFrameTransformerDelegate::Reset() {
void ChannelReceiveFrameTransformerDelegate::Transform(
rtc::ArrayView<const uint8_t> packet,
const RTPHeader& header,
- uint32_t ssrc) {
+ uint32_t ssrc,
+ const std::string& codec_mime_type) {
RTC_DCHECK_RUN_ON(&sequence_checker_);
- frame_transformer_->Transform(
- std::make_unique<TransformableIncomingAudioFrame>(packet, header, ssrc));
+ if (short_circuit_) {
+ receive_frame_callback_(packet, header);
+ } else {
+ frame_transformer_->Transform(
+ std::make_unique<TransformableIncomingAudioFrame>(packet, header, ssrc,
+ codec_mime_type));
+ }
}
void ChannelReceiveFrameTransformerDelegate::OnTransformedFrame(
@@ -107,6 +118,14 @@ void ChannelReceiveFrameTransformerDelegate::OnTransformedFrame(
});
}
+void ChannelReceiveFrameTransformerDelegate::StartShortCircuiting() {
+ rtc::scoped_refptr<ChannelReceiveFrameTransformerDelegate> delegate(this);
+ channel_receive_thread_->PostTask([delegate = std::move(delegate)]() mutable {
+ RTC_DCHECK_RUN_ON(&delegate->sequence_checker_);
+ delegate->short_circuit_ = true;
+ });
+}
+
void ChannelReceiveFrameTransformerDelegate::ReceiveFrame(
std::unique_ptr<TransformableFrameInterface> frame) const {
RTC_DCHECK_RUN_ON(&sequence_checker_);
@@ -138,4 +157,11 @@ void ChannelReceiveFrameTransformerDelegate::ReceiveFrame(
// originally from this receiver.
receive_frame_callback_(frame->GetData(), header);
}
+
+rtc::scoped_refptr<FrameTransformerInterface>
+ChannelReceiveFrameTransformerDelegate::FrameTransformer() {
+ RTC_DCHECK_RUN_ON(&sequence_checker_);
+ return frame_transformer_;
+}
+
} // namespace webrtc
diff --git a/third_party/libwebrtc/audio/channel_receive_frame_transformer_delegate.h b/third_party/libwebrtc/audio/channel_receive_frame_transformer_delegate.h
index 04ad7c4695..37ff75c2e9 100644
--- a/third_party/libwebrtc/audio/channel_receive_frame_transformer_delegate.h
+++ b/third_party/libwebrtc/audio/channel_receive_frame_transformer_delegate.h
@@ -12,6 +12,7 @@
#define AUDIO_CHANNEL_RECEIVE_FRAME_TRANSFORMER_DELEGATE_H_
#include <memory>
+#include <string>
#include "api/frame_transformer_interface.h"
#include "api/sequence_checker.h"
@@ -48,16 +49,21 @@ class ChannelReceiveFrameTransformerDelegate : public TransformedFrameCallback {
// the frame asynchronously.
void Transform(rtc::ArrayView<const uint8_t> packet,
const RTPHeader& header,
- uint32_t ssrc);
+ uint32_t ssrc,
+ const std::string& codec_mime_type);
// Implements TransformedFrameCallback. Can be called on any thread.
void OnTransformedFrame(
std::unique_ptr<TransformableFrameInterface> frame) override;
+ void StartShortCircuiting() override;
+
// Delegates the call to ChannelReceive::OnReceivedPayloadData on the
// `channel_receive_thread_`, by calling `receive_frame_callback_`.
void ReceiveFrame(std::unique_ptr<TransformableFrameInterface> frame) const;
+ rtc::scoped_refptr<FrameTransformerInterface> FrameTransformer();
+
protected:
~ChannelReceiveFrameTransformerDelegate() override = default;
@@ -68,6 +74,7 @@ class ChannelReceiveFrameTransformerDelegate : public TransformedFrameCallback {
rtc::scoped_refptr<FrameTransformerInterface> frame_transformer_
RTC_GUARDED_BY(sequence_checker_);
TaskQueueBase* const channel_receive_thread_;
+ bool short_circuit_ RTC_GUARDED_BY(sequence_checker_) = false;
};
} // namespace webrtc
diff --git a/third_party/libwebrtc/audio/channel_receive_frame_transformer_delegate_unittest.cc b/third_party/libwebrtc/audio/channel_receive_frame_transformer_delegate_unittest.cc
index 38ceb6d96d..8bdf217d5a 100644
--- a/third_party/libwebrtc/audio/channel_receive_frame_transformer_delegate_unittest.cc
+++ b/third_party/libwebrtc/audio/channel_receive_frame_transformer_delegate_unittest.cc
@@ -93,7 +93,7 @@ TEST(ChannelReceiveFrameTransformerDelegateTest,
[&callback](std::unique_ptr<TransformableFrameInterface> frame) {
callback->OnTransformedFrame(std::move(frame));
});
- delegate->Transform(packet, header, 1111 /*ssrc*/);
+ delegate->Transform(packet, header, /*ssrc=*/1111, /*mimeType=*/"audio/opus");
rtc::ThreadManager::ProcessAllMessageQueuesForTesting();
}
@@ -126,7 +126,7 @@ TEST(ChannelReceiveFrameTransformerDelegateTest,
static_cast<TransformableAudioFrameInterface*>(frame.get());
callback->OnTransformedFrame(CloneSenderAudioFrame(transformed_frame));
});
- delegate->Transform(packet, header, 1111 /*ssrc*/);
+ delegate->Transform(packet, header, /*ssrc=*/1111, /*mimeType=*/"audio/opus");
rtc::ThreadManager::ProcessAllMessageQueuesForTesting();
}
@@ -150,5 +150,29 @@ TEST(ChannelReceiveFrameTransformerDelegateTest,
rtc::ThreadManager::ProcessAllMessageQueuesForTesting();
}
+TEST(ChannelReceiveFrameTransformerDelegateTest,
+ ShortCircuitingSkipsTransform) {
+ rtc::AutoThread main_thread;
+ rtc::scoped_refptr<MockFrameTransformer> mock_frame_transformer =
+ rtc::make_ref_counted<testing::NiceMock<MockFrameTransformer>>();
+ MockChannelReceive mock_channel;
+ rtc::scoped_refptr<ChannelReceiveFrameTransformerDelegate> delegate =
+ rtc::make_ref_counted<ChannelReceiveFrameTransformerDelegate>(
+ mock_channel.callback(), mock_frame_transformer,
+ rtc::Thread::Current());
+ const uint8_t data[] = {1, 2, 3, 4};
+ rtc::ArrayView<const uint8_t> packet(data, sizeof(data));
+ RTPHeader header;
+
+ delegate->StartShortCircuiting();
+ rtc::ThreadManager::ProcessAllMessageQueuesForTesting();
+
+ // Will not call the actual transformer.
+ EXPECT_CALL(*mock_frame_transformer, Transform).Times(0);
+ // Will pass the frame straight to the channel.
+ EXPECT_CALL(mock_channel, ReceiveFrame);
+ delegate->Transform(packet, header, /*ssrc=*/1111, /*mimeType=*/"audio/opus");
+}
+
} // namespace
} // namespace webrtc
diff --git a/third_party/libwebrtc/audio/channel_receive_unittest.cc b/third_party/libwebrtc/audio/channel_receive_unittest.cc
index 4b7b7c0231..aab8a95d8b 100644
--- a/third_party/libwebrtc/audio/channel_receive_unittest.cc
+++ b/third_party/libwebrtc/audio/channel_receive_unittest.cc
@@ -29,6 +29,7 @@
#include "test/gmock.h"
#include "test/gtest.h"
#include "test/mock_audio_decoder_factory.h"
+#include "test/mock_frame_transformer.h"
#include "test/mock_transport.h"
#include "test/time_controller/simulated_time_controller.h"
@@ -226,6 +227,41 @@ TEST_F(ChannelReceiveTest, CaptureStartTimeBecomesValid) {
EXPECT_NE(ProbeCaptureStartNtpTime(*channel), -1);
}
+TEST_F(ChannelReceiveTest, SettingFrameTransformer) {
+ auto channel = CreateTestChannelReceive();
+
+ rtc::scoped_refptr<MockFrameTransformer> mock_frame_transformer =
+ rtc::make_ref_counted<MockFrameTransformer>();
+
+ EXPECT_CALL(*mock_frame_transformer, RegisterTransformedFrameCallback);
+ channel->SetDepacketizerToDecoderFrameTransformer(mock_frame_transformer);
+
+ // Must start playout, otherwise packet is discarded.
+ channel->StartPlayout();
+
+ RtpPacketReceived packet = CreateRtpPacket();
+
+ // Receive one RTP packet, this should be transformed.
+ EXPECT_CALL(*mock_frame_transformer, Transform);
+ channel->OnRtpPacket(packet);
+}
+
+TEST_F(ChannelReceiveTest, SettingFrameTransformerMultipleTimes) {
+ auto channel = CreateTestChannelReceive();
+
+ rtc::scoped_refptr<MockFrameTransformer> mock_frame_transformer =
+ rtc::make_ref_counted<MockFrameTransformer>();
+
+ EXPECT_CALL(*mock_frame_transformer, RegisterTransformedFrameCallback);
+ channel->SetDepacketizerToDecoderFrameTransformer(mock_frame_transformer);
+
+ // Set the same transformer again, shouldn't cause any additional callback
+ // registration calls.
+ EXPECT_CALL(*mock_frame_transformer, RegisterTransformedFrameCallback)
+ .Times(0);
+ channel->SetDepacketizerToDecoderFrameTransformer(mock_frame_transformer);
+}
+
} // namespace
} // namespace voe
} // namespace webrtc
diff --git a/third_party/libwebrtc/audio/channel_send.cc b/third_party/libwebrtc/audio/channel_send.cc
index ee94760b6f..3c59be52b4 100644
--- a/third_party/libwebrtc/audio/channel_send.cc
+++ b/third_party/libwebrtc/audio/channel_send.cc
@@ -37,6 +37,7 @@
#include "rtc_base/numerics/safe_conversions.h"
#include "rtc_base/race_checker.h"
#include "rtc_base/rate_limiter.h"
+#include "rtc_base/strings/string_builder.h"
#include "rtc_base/synchronization/mutex.h"
#include "rtc_base/task_queue.h"
#include "rtc_base/time_utils.h"
@@ -104,6 +105,7 @@ class ChannelSend : public ChannelSendInterface,
// Send using this encoder, with this payload type.
void SetEncoder(int payload_type,
+ const SdpAudioFormat& encoder_format,
std::unique_ptr<AudioEncoder> encoder) override;
void ModifyEncoder(rtc::FunctionView<void(std::unique_ptr<AudioEncoder>*)>
modifier) override;
@@ -192,7 +194,8 @@ class ChannelSend : public ChannelSendInterface,
uint8_t payloadType,
uint32_t rtp_timestamp_without_offset,
rtc::ArrayView<const uint8_t> payload,
- int64_t absolute_capture_timestamp_ms)
+ int64_t absolute_capture_timestamp_ms,
+ rtc::ArrayView<const uint32_t> csrcs)
RTC_RUN_ON(encoder_queue_);
void OnReceivedRtt(int64_t rtt_ms);
@@ -264,6 +267,8 @@ class ChannelSend : public ChannelSendInterface,
// Defined last to ensure that there are no running tasks when the other
// members are destroyed.
rtc::TaskQueue encoder_queue_;
+
+ SdpAudioFormat encoder_format_;
};
const int kTelephoneEventAttenuationdB = 10;
@@ -310,21 +315,26 @@ int32_t ChannelSend::SendData(AudioFrameType frameType,
if (frame_transformer_delegate_) {
// Asynchronously transform the payload before sending it. After the payload
// is transformed, the delegate will call SendRtpAudio to send it.
+ char buf[1024];
+ rtc::SimpleStringBuilder mime_type(buf);
+ mime_type << MediaTypeToString(cricket::MEDIA_TYPE_AUDIO) << "/"
+ << encoder_format_.name;
frame_transformer_delegate_->Transform(
frameType, payloadType, rtp_timestamp + rtp_rtcp_->StartTimestamp(),
payloadData, payloadSize, absolute_capture_timestamp_ms,
- rtp_rtcp_->SSRC());
+ rtp_rtcp_->SSRC(), mime_type.str());
return 0;
}
return SendRtpAudio(frameType, payloadType, rtp_timestamp, payload,
- absolute_capture_timestamp_ms);
+ absolute_capture_timestamp_ms, /*csrcs=*/{});
}
int32_t ChannelSend::SendRtpAudio(AudioFrameType frameType,
uint8_t payloadType,
uint32_t rtp_timestamp_without_offset,
rtc::ArrayView<const uint8_t> payload,
- int64_t absolute_capture_timestamp_ms) {
+ int64_t absolute_capture_timestamp_ms,
+ rtc::ArrayView<const uint32_t> csrcs) {
// E2EE Custom Audio Frame Encryption (This is optional).
// Keep this buffer around for the lifetime of the send call.
rtc::Buffer encrypted_audio_payload;
@@ -386,7 +396,8 @@ int32_t ChannelSend::SendRtpAudio(AudioFrameType frameType,
.payload = payload,
.payload_id = payloadType,
.rtp_timestamp =
- rtp_timestamp_without_offset + rtp_rtcp_->StartTimestamp()};
+ rtp_timestamp_without_offset + rtp_rtcp_->StartTimestamp(),
+ .csrcs = csrcs};
if (absolute_capture_timestamp_ms > 0) {
frame.capture_time = Timestamp::Millis(absolute_capture_timestamp_ms);
}
@@ -426,7 +437,8 @@ ChannelSend::ChannelSend(
crypto_options_(crypto_options),
encoder_queue_(task_queue_factory->CreateTaskQueue(
"AudioEncoder",
- TaskQueueFactory::Priority::NORMAL)) {
+ TaskQueueFactory::Priority::NORMAL)),
+ encoder_format_("x-unknown", 0, 0) {
audio_coding_ = AudioCodingModule::Create();
RtpRtcpInterface::Configuration configuration;
@@ -527,6 +539,7 @@ void ChannelSend::StopSend() {
}
void ChannelSend::SetEncoder(int payload_type,
+ const SdpAudioFormat& encoder_format,
std::unique_ptr<AudioEncoder> encoder) {
RTC_DCHECK_RUN_ON(&worker_thread_checker_);
RTC_DCHECK_GE(payload_type, 0);
@@ -540,6 +553,7 @@ void ChannelSend::SetEncoder(int payload_type,
encoder->RtpTimestampRateHz(),
encoder->NumChannels(), 0);
+ encoder_format_ = encoder_format;
audio_coding_->SetEncoder(std::move(encoder));
}
@@ -881,12 +895,13 @@ void ChannelSend::InitFrameTransformerDelegate(
[this](AudioFrameType frameType, uint8_t payloadType,
uint32_t rtp_timestamp_with_offset,
rtc::ArrayView<const uint8_t> payload,
- int64_t absolute_capture_timestamp_ms) {
+ int64_t absolute_capture_timestamp_ms,
+ rtc::ArrayView<const uint32_t> csrcs) {
RTC_DCHECK_RUN_ON(&encoder_queue_);
return SendRtpAudio(
frameType, payloadType,
rtp_timestamp_with_offset - rtp_rtcp_->StartTimestamp(), payload,
- absolute_capture_timestamp_ms);
+ absolute_capture_timestamp_ms, csrcs);
};
frame_transformer_delegate_ =
rtc::make_ref_counted<ChannelSendFrameTransformerDelegate>(
diff --git a/third_party/libwebrtc/audio/channel_send.h b/third_party/libwebrtc/audio/channel_send.h
index f0c9232296..f36085c1fa 100644
--- a/third_party/libwebrtc/audio/channel_send.h
+++ b/third_party/libwebrtc/audio/channel_send.h
@@ -63,6 +63,7 @@ class ChannelSendInterface {
virtual CallSendStatistics GetRTCPStatistics() const = 0;
virtual void SetEncoder(int payload_type,
+ const SdpAudioFormat& encoder_format,
std::unique_ptr<AudioEncoder> encoder) = 0;
virtual void ModifyEncoder(
rtc::FunctionView<void(std::unique_ptr<AudioEncoder>*)> modifier) = 0;
diff --git a/third_party/libwebrtc/audio/channel_send_frame_transformer_delegate.cc b/third_party/libwebrtc/audio/channel_send_frame_transformer_delegate.cc
index 0f85216e92..2eea0d2387 100644
--- a/third_party/libwebrtc/audio/channel_send_frame_transformer_delegate.cc
+++ b/third_party/libwebrtc/audio/channel_send_frame_transformer_delegate.cc
@@ -11,6 +11,7 @@
#include "audio/channel_send_frame_transformer_delegate.h"
#include <utility>
+#include <vector>
namespace webrtc {
namespace {
@@ -55,13 +56,17 @@ class TransformableOutgoingAudioFrame
const uint8_t* payload_data,
size_t payload_size,
absl::optional<uint64_t> absolute_capture_timestamp_ms,
- uint32_t ssrc)
+ uint32_t ssrc,
+ std::vector<uint32_t> csrcs,
+ const std::string& codec_mime_type)
: frame_type_(frame_type),
payload_type_(payload_type),
rtp_timestamp_with_offset_(rtp_timestamp_with_offset),
payload_(payload_data, payload_size),
absolute_capture_timestamp_ms_(absolute_capture_timestamp_ms),
- ssrc_(ssrc) {}
+ ssrc_(ssrc),
+ csrcs_(std::move(csrcs)),
+ codec_mime_type_(codec_mime_type) {}
~TransformableOutgoingAudioFrame() override = default;
rtc::ArrayView<const uint8_t> GetData() const override { return payload_; }
void SetData(rtc::ArrayView<const uint8_t> data) override {
@@ -76,9 +81,10 @@ class TransformableOutgoingAudioFrame
uint8_t GetPayloadType() const override { return payload_type_; }
Direction GetDirection() const override { return Direction::kSender; }
+ std::string GetMimeType() const override { return codec_mime_type_; }
rtc::ArrayView<const uint32_t> GetContributingSources() const override {
- return {};
+ return csrcs_;
}
const absl::optional<uint16_t> SequenceNumber() const override {
@@ -100,6 +106,8 @@ class TransformableOutgoingAudioFrame
rtc::Buffer payload_;
absl::optional<uint64_t> absolute_capture_timestamp_ms_;
uint32_t ssrc_;
+ std::vector<uint32_t> csrcs_;
+ std::string codec_mime_type_;
};
} // namespace
@@ -131,11 +139,23 @@ void ChannelSendFrameTransformerDelegate::Transform(
const uint8_t* payload_data,
size_t payload_size,
int64_t absolute_capture_timestamp_ms,
- uint32_t ssrc) {
+ uint32_t ssrc,
+ const std::string& codec_mimetype) {
+ {
+ MutexLock lock(&send_lock_);
+ if (short_circuit_) {
+ send_frame_callback_(
+ frame_type, payload_type, rtp_timestamp,
+ rtc::ArrayView<const uint8_t>(payload_data, payload_size),
+ absolute_capture_timestamp_ms, /*csrcs=*/{});
+ return;
+ }
+ }
frame_transformer_->Transform(
std::make_unique<TransformableOutgoingAudioFrame>(
frame_type, payload_type, rtp_timestamp, payload_data, payload_size,
- absolute_capture_timestamp_ms, ssrc));
+ absolute_capture_timestamp_ms, ssrc,
+ /*csrcs=*/std::vector<uint32_t>(), codec_mimetype));
}
void ChannelSendFrameTransformerDelegate::OnTransformedFrame(
@@ -150,6 +170,11 @@ void ChannelSendFrameTransformerDelegate::OnTransformedFrame(
});
}
+void ChannelSendFrameTransformerDelegate::StartShortCircuiting() {
+ MutexLock lock(&send_lock_);
+ short_circuit_ = true;
+}
+
void ChannelSendFrameTransformerDelegate::SendFrame(
std::unique_ptr<TransformableFrameInterface> frame) const {
MutexLock lock(&send_lock_);
@@ -164,16 +189,21 @@ void ChannelSendFrameTransformerDelegate::SendFrame(
transformed_frame->GetData(),
transformed_frame->AbsoluteCaptureTimestamp()
? *transformed_frame->AbsoluteCaptureTimestamp()
- : 0);
+ : 0,
+ transformed_frame->GetContributingSources());
}
std::unique_ptr<TransformableAudioFrameInterface> CloneSenderAudioFrame(
TransformableAudioFrameInterface* original) {
+ std::vector<uint32_t> csrcs;
+ csrcs.assign(original->GetContributingSources().begin(),
+ original->GetContributingSources().end());
return std::make_unique<TransformableOutgoingAudioFrame>(
InterfaceFrameTypeToInternalFrameType(original->Type()),
original->GetPayloadType(), original->GetTimestamp(),
original->GetData().data(), original->GetData().size(),
- original->AbsoluteCaptureTimestamp(), original->GetSsrc());
+ original->AbsoluteCaptureTimestamp(), original->GetSsrc(),
+ std::move(csrcs), original->GetMimeType());
}
} // namespace webrtc
diff --git a/third_party/libwebrtc/audio/channel_send_frame_transformer_delegate.h b/third_party/libwebrtc/audio/channel_send_frame_transformer_delegate.h
index eb0027e4c8..97fc14f737 100644
--- a/third_party/libwebrtc/audio/channel_send_frame_transformer_delegate.h
+++ b/third_party/libwebrtc/audio/channel_send_frame_transformer_delegate.h
@@ -12,6 +12,7 @@
#define AUDIO_CHANNEL_SEND_FRAME_TRANSFORMER_DELEGATE_H_
#include <memory>
+#include <string>
#include "api/frame_transformer_interface.h"
#include "api/sequence_checker.h"
@@ -34,7 +35,8 @@ class ChannelSendFrameTransformerDelegate : public TransformedFrameCallback {
uint8_t payloadType,
uint32_t rtp_timestamp_with_offset,
rtc::ArrayView<const uint8_t> payload,
- int64_t absolute_capture_timestamp_ms)>;
+ int64_t absolute_capture_timestamp_ms,
+ rtc::ArrayView<const uint32_t> csrcs)>;
ChannelSendFrameTransformerDelegate(
SendFrameCallback send_frame_callback,
rtc::scoped_refptr<FrameTransformerInterface> frame_transformer,
@@ -57,12 +59,15 @@ class ChannelSendFrameTransformerDelegate : public TransformedFrameCallback {
const uint8_t* payload_data,
size_t payload_size,
int64_t absolute_capture_timestamp_ms,
- uint32_t ssrc);
+ uint32_t ssrc,
+ const std::string& codec_mime_type);
// Implements TransformedFrameCallback. Can be called on any thread.
void OnTransformedFrame(
std::unique_ptr<TransformableFrameInterface> frame) override;
+ void StartShortCircuiting() override;
+
// Delegates the call to ChannelSend::SendRtpAudio on the `encoder_queue_`,
// by calling `send_audio_callback_`.
void SendFrame(std::unique_ptr<TransformableFrameInterface> frame) const;
@@ -75,6 +80,7 @@ class ChannelSendFrameTransformerDelegate : public TransformedFrameCallback {
SendFrameCallback send_frame_callback_ RTC_GUARDED_BY(send_lock_);
rtc::scoped_refptr<FrameTransformerInterface> frame_transformer_;
rtc::TaskQueue* encoder_queue_ RTC_GUARDED_BY(send_lock_);
+ bool short_circuit_ RTC_GUARDED_BY(send_lock_) = false;
};
std::unique_ptr<TransformableAudioFrameInterface> CloneSenderAudioFrame(
diff --git a/third_party/libwebrtc/audio/channel_send_frame_transformer_delegate_unittest.cc b/third_party/libwebrtc/audio/channel_send_frame_transformer_delegate_unittest.cc
index f75d4a8ab7..4dcd15cd95 100644
--- a/third_party/libwebrtc/audio/channel_send_frame_transformer_delegate_unittest.cc
+++ b/third_party/libwebrtc/audio/channel_send_frame_transformer_delegate_unittest.cc
@@ -12,7 +12,9 @@
#include <memory>
#include <utility>
+#include <vector>
+#include "absl/memory/memory.h"
#include "rtc_base/task_queue_for_test.h"
#include "test/gmock.h"
#include "test/gtest.h"
@@ -24,10 +26,13 @@ namespace {
using ::testing::_;
using ::testing::ElementsAre;
+using ::testing::ElementsAreArray;
using ::testing::NiceMock;
using ::testing::Return;
using ::testing::SaveArg;
+const uint8_t mock_data[] = {1, 2, 3, 4};
+
class MockChannelSend {
public:
MockChannelSend() = default;
@@ -39,30 +44,56 @@ class MockChannelSend {
uint8_t payloadType,
uint32_t rtp_timestamp,
rtc::ArrayView<const uint8_t> payload,
- int64_t absolute_capture_timestamp_ms));
+ int64_t absolute_capture_timestamp_ms,
+ rtc::ArrayView<const uint32_t> csrcs));
ChannelSendFrameTransformerDelegate::SendFrameCallback callback() {
return [this](AudioFrameType frameType, uint8_t payloadType,
uint32_t rtp_timestamp, rtc::ArrayView<const uint8_t> payload,
- int64_t absolute_capture_timestamp_ms) {
+ int64_t absolute_capture_timestamp_ms,
+ rtc::ArrayView<const uint32_t> csrcs) {
return SendFrame(frameType, payloadType, rtp_timestamp, payload,
- absolute_capture_timestamp_ms);
+ absolute_capture_timestamp_ms, csrcs);
};
}
};
-std::unique_ptr<MockTransformableAudioFrame> CreateMockReceiverFrame() {
- const uint8_t mock_data[] = {1, 2, 3, 4};
+std::unique_ptr<TransformableAudioFrameInterface> CreateMockReceiverFrame(
+ std::vector<const uint32_t> csrcs) {
std::unique_ptr<MockTransformableAudioFrame> mock_frame =
- std::make_unique<MockTransformableAudioFrame>();
+ std::make_unique<NiceMock<MockTransformableAudioFrame>>();
rtc::ArrayView<const uint8_t> payload(mock_data);
ON_CALL(*mock_frame, GetData).WillByDefault(Return(payload));
ON_CALL(*mock_frame, GetPayloadType).WillByDefault(Return(0));
ON_CALL(*mock_frame, GetDirection)
.WillByDefault(Return(TransformableFrameInterface::Direction::kReceiver));
+ ON_CALL(*mock_frame, GetContributingSources).WillByDefault(Return(csrcs));
return mock_frame;
}
+std::unique_ptr<TransformableAudioFrameInterface> CreateFrame() {
+ TaskQueueForTest channel_queue("channel_queue");
+ rtc::scoped_refptr<MockFrameTransformer> mock_frame_transformer =
+ rtc::make_ref_counted<NiceMock<MockFrameTransformer>>();
+ MockChannelSend mock_channel;
+ rtc::scoped_refptr<ChannelSendFrameTransformerDelegate> delegate =
+ rtc::make_ref_counted<ChannelSendFrameTransformerDelegate>(
+ mock_channel.callback(), mock_frame_transformer, &channel_queue);
+
+ std::unique_ptr<TransformableFrameInterface> frame;
+ ON_CALL(*mock_frame_transformer, Transform)
+ .WillByDefault(
+ [&frame](
+ std::unique_ptr<TransformableFrameInterface> transform_frame) {
+ frame = std::move(transform_frame);
+ });
+ delegate->Transform(AudioFrameType::kEmptyFrame, 0, 0, mock_data,
+ sizeof(mock_data), 0,
+ /*ssrc=*/0, /*mimeType=*/"audio/opus");
+ return absl::WrapUnique(
+ static_cast<webrtc::TransformableAudioFrameInterface*>(frame.release()));
+}
+
// Test that the delegate registers itself with the frame transformer on Init().
TEST(ChannelSendFrameTransformerDelegateTest,
RegisterTransformedFrameCallbackOnInit) {
@@ -115,7 +146,7 @@ TEST(ChannelSendFrameTransformerDelegateTest,
callback->OnTransformedFrame(std::move(frame));
});
delegate->Transform(AudioFrameType::kEmptyFrame, 0, 0, data, sizeof(data), 0,
- 0);
+ /*ssrc=*/0, /*mimeType=*/"audio/opus");
channel_queue.WaitForPreviouslyPostedTasks();
}
@@ -136,16 +167,17 @@ TEST(ChannelSendFrameTransformerDelegateTest,
delegate->Init();
ASSERT_TRUE(callback);
- const uint8_t data[] = {1, 2, 3, 4};
+ std::vector<const uint32_t> csrcs = {123, 234, 345, 456};
EXPECT_CALL(mock_channel, SendFrame).Times(0);
- EXPECT_CALL(mock_channel, SendFrame(_, 0, 0, ElementsAre(1, 2, 3, 4), _));
+ EXPECT_CALL(mock_channel, SendFrame(_, 0, 0, ElementsAreArray(mock_data), _,
+ ElementsAreArray(csrcs)));
ON_CALL(*mock_frame_transformer, Transform)
- .WillByDefault(
- [&callback](std::unique_ptr<TransformableFrameInterface> frame) {
- callback->OnTransformedFrame(CreateMockReceiverFrame());
- });
- delegate->Transform(AudioFrameType::kEmptyFrame, 0, 0, data, sizeof(data), 0,
- 0);
+ .WillByDefault([&](std::unique_ptr<TransformableFrameInterface> frame) {
+ callback->OnTransformedFrame(CreateMockReceiverFrame(csrcs));
+ });
+ delegate->Transform(AudioFrameType::kEmptyFrame, 0, 0, mock_data,
+ sizeof(mock_data), 0,
+ /*ssrc=*/0, /*mimeType=*/"audio/opus");
channel_queue.WaitForPreviouslyPostedTasks();
}
@@ -168,5 +200,59 @@ TEST(ChannelSendFrameTransformerDelegateTest,
channel_queue.WaitForPreviouslyPostedTasks();
}
+TEST(ChannelSendFrameTransformerDelegateTest, ShortCircuitingSkipsTransform) {
+ TaskQueueForTest channel_queue("channel_queue");
+ rtc::scoped_refptr<MockFrameTransformer> mock_frame_transformer =
+ rtc::make_ref_counted<testing::NiceMock<MockFrameTransformer>>();
+ MockChannelSend mock_channel;
+ rtc::scoped_refptr<ChannelSendFrameTransformerDelegate> delegate =
+ rtc::make_ref_counted<ChannelSendFrameTransformerDelegate>(
+ mock_channel.callback(), mock_frame_transformer, &channel_queue);
+
+ delegate->StartShortCircuiting();
+
+ // Will not call the actual transformer.
+ EXPECT_CALL(*mock_frame_transformer, Transform).Times(0);
+ // Will pass the frame straight to the channel.
+ EXPECT_CALL(mock_channel, SendFrame);
+ const uint8_t data[] = {1, 2, 3, 4};
+ delegate->Transform(AudioFrameType::kEmptyFrame, 0, 0, data, sizeof(data), 0,
+ /*ssrc=*/0, /*mimeType=*/"audio/opus");
+}
+
+TEST(ChannelSendFrameTransformerDelegateTest,
+ CloningSenderFramePreservesInformation) {
+ std::unique_ptr<TransformableAudioFrameInterface> frame = CreateFrame();
+ std::unique_ptr<TransformableAudioFrameInterface> cloned_frame =
+ CloneSenderAudioFrame(frame.get());
+
+ EXPECT_EQ(cloned_frame->GetTimestamp(), frame->GetTimestamp());
+ EXPECT_EQ(cloned_frame->GetSsrc(), frame->GetSsrc());
+ EXPECT_EQ(cloned_frame->Type(), frame->Type());
+ EXPECT_EQ(cloned_frame->GetPayloadType(), frame->GetPayloadType());
+ EXPECT_EQ(cloned_frame->GetMimeType(), frame->GetMimeType());
+ EXPECT_THAT(cloned_frame->GetContributingSources(),
+ ElementsAreArray(frame->GetContributingSources()));
+}
+
+TEST(ChannelSendFrameTransformerDelegateTest, CloningReceiverFrameWithCsrcs) {
+ std::unique_ptr<TransformableAudioFrameInterface> frame =
+ CreateMockReceiverFrame(/*csrcs=*/{123, 234, 345});
+ std::unique_ptr<TransformableAudioFrameInterface> cloned_frame =
+ CloneSenderAudioFrame(frame.get());
+
+ EXPECT_EQ(cloned_frame->GetTimestamp(), frame->GetTimestamp());
+ EXPECT_EQ(cloned_frame->GetSsrc(), frame->GetSsrc());
+ EXPECT_EQ(cloned_frame->Type(), frame->Type());
+ EXPECT_EQ(cloned_frame->GetPayloadType(), frame->GetPayloadType());
+ EXPECT_EQ(cloned_frame->GetMimeType(), frame->GetMimeType());
+ EXPECT_EQ(cloned_frame->AbsoluteCaptureTimestamp(),
+ frame->AbsoluteCaptureTimestamp());
+
+ ASSERT_NE(frame->GetContributingSources().size(), 0u);
+ EXPECT_THAT(cloned_frame->GetContributingSources(),
+ ElementsAreArray(frame->GetContributingSources()));
+}
+
} // namespace
} // namespace webrtc
diff --git a/third_party/libwebrtc/audio/channel_send_unittest.cc b/third_party/libwebrtc/audio/channel_send_unittest.cc
index b9406e1523..58d7c93c1e 100644
--- a/third_party/libwebrtc/audio/channel_send_unittest.cc
+++ b/third_party/libwebrtc/audio/channel_send_unittest.cc
@@ -66,9 +66,10 @@ class ChannelSendTest : public ::testing::Test {
&transport_, nullptr, &event_log_, nullptr, crypto_options_, false,
kRtcpIntervalMs, kSsrc, nullptr, &transport_controller_, field_trials_);
encoder_factory_ = CreateBuiltinAudioEncoderFactory();
- std::unique_ptr<AudioEncoder> encoder = encoder_factory_->MakeAudioEncoder(
- kPayloadType, SdpAudioFormat("opus", kRtpRateHz, 2), {});
- channel_->SetEncoder(kPayloadType, std::move(encoder));
+ SdpAudioFormat opus = SdpAudioFormat("opus", kRtpRateHz, 2);
+ std::unique_ptr<AudioEncoder> encoder =
+ encoder_factory_->MakeAudioEncoder(kPayloadType, opus, {});
+ channel_->SetEncoder(kPayloadType, opus, std::move(encoder));
transport_controller_.EnsureStarted();
channel_->RegisterSenderCongestionControlObjects(&transport_controller_);
ON_CALL(transport_, SendRtcp).WillByDefault(Return(true));
diff --git a/third_party/libwebrtc/audio/mock_voe_channel_proxy.h b/third_party/libwebrtc/audio/mock_voe_channel_proxy.h
index 29005173df..71ef5d12fb 100644
--- a/third_party/libwebrtc/audio/mock_voe_channel_proxy.h
+++ b/third_party/libwebrtc/audio/mock_voe_channel_proxy.h
@@ -113,7 +113,9 @@ class MockChannelSend : public voe::ChannelSendInterface {
public:
MOCK_METHOD(void,
SetEncoder,
- (int payload_type, std::unique_ptr<AudioEncoder> encoder),
+ (int payload_type,
+ const SdpAudioFormat& encoder_format,
+ std::unique_ptr<AudioEncoder> encoder),
(override));
MOCK_METHOD(
void,
diff --git a/third_party/libwebrtc/audio/utility/audio_frame_operations_gn/moz.build b/third_party/libwebrtc/audio/utility/audio_frame_operations_gn/moz.build
index e215792f64..6489497db4 100644
--- a/third_party/libwebrtc/audio/utility/audio_frame_operations_gn/moz.build
+++ b/third_party/libwebrtc/audio/utility/audio_frame_operations_gn/moz.build
@@ -201,7 +201,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -211,10 +210,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/build/config/android/BUILD.gn b/third_party/libwebrtc/build/config/android/BUILD.gn
index a77a628be4..85d27ea89e 100644
--- a/third_party/libwebrtc/build/config/android/BUILD.gn
+++ b/third_party/libwebrtc/build/config/android/BUILD.gn
@@ -89,14 +89,6 @@ config("runtime_library") {
libs = []
- # On 64-bit platforms, the only symbols provided by libandroid_support.a are
- # strto{d,f,l,ul}_l. These symbols are not used by our libc++, and newer NDKs
- # don't provide a libandroid_support.a on 64-bit platforms, so we only depend
- # on this library on 32-bit platforms.
- if (target_cpu == "arm" || target_cpu == "x86") {
- libs += [ "android_support" ]
- }
-
# arm builds of libc++ starting in NDK r12 depend on unwind.
if (target_cpu == "arm") {
libs += [ "unwind" ]
diff --git a/third_party/libwebrtc/call/BUILD.gn b/third_party/libwebrtc/call/BUILD.gn
index 47018a570a..626ed95066 100644
--- a/third_party/libwebrtc/call/BUILD.gn
+++ b/third_party/libwebrtc/call/BUILD.gn
@@ -61,6 +61,7 @@ rtc_library("call_interfaces") {
"../api/audio_codecs:audio_codecs_api",
"../api/crypto:frame_encryptor_interface",
"../api/crypto:options",
+ "../api/environment",
"../api/metronome",
"../api/neteq:neteq_api",
"../api/task_queue",
@@ -500,8 +501,8 @@ if (rtc_include_tests) {
"../api:rtp_parameters",
"../api:transport_api",
"../api/audio_codecs:builtin_audio_decoder_factory",
- "../api/rtc_event_log",
- "../api/task_queue:default_task_queue_factory",
+ "../api/environment",
+ "../api/environment:environment_factory",
"../api/test/video:function_video_factory",
"../api/transport:field_trial_based_config",
"../api/units:timestamp",
@@ -582,7 +583,6 @@ if (rtc_include_tests) {
"../api/rtc_event_log",
"../api/rtc_event_log:rtc_event_log_factory",
"../api/task_queue",
- "../api/task_queue:default_task_queue_factory",
"../api/task_queue:pending_task_safety_flag",
"../api/test/metrics:global_metrics_logger_and_exporter",
"../api/test/metrics:metric",
diff --git a/third_party/libwebrtc/call/adaptation/resource_adaptation_gn/moz.build b/third_party/libwebrtc/call/adaptation/resource_adaptation_gn/moz.build
index d8893a7341..79a2aa9def 100644
--- a/third_party/libwebrtc/call/adaptation/resource_adaptation_gn/moz.build
+++ b/third_party/libwebrtc/call/adaptation/resource_adaptation_gn/moz.build
@@ -209,7 +209,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -219,10 +218,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/call/audio_sender_interface_gn/moz.build b/third_party/libwebrtc/call/audio_sender_interface_gn/moz.build
index 2b42e8ebf9..a53966813f 100644
--- a/third_party/libwebrtc/call/audio_sender_interface_gn/moz.build
+++ b/third_party/libwebrtc/call/audio_sender_interface_gn/moz.build
@@ -187,16 +187,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/call/bitrate_allocator_gn/moz.build b/third_party/libwebrtc/call/bitrate_allocator_gn/moz.build
index a56b55faf3..db772f8d39 100644
--- a/third_party/libwebrtc/call/bitrate_allocator_gn/moz.build
+++ b/third_party/libwebrtc/call/bitrate_allocator_gn/moz.build
@@ -199,7 +199,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -209,10 +208,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/call/bitrate_configurator_gn/moz.build b/third_party/libwebrtc/call/bitrate_configurator_gn/moz.build
index e6f73025aa..f7c51b4a7c 100644
--- a/third_party/libwebrtc/call/bitrate_configurator_gn/moz.build
+++ b/third_party/libwebrtc/call/bitrate_configurator_gn/moz.build
@@ -199,7 +199,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -209,10 +208,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/call/bitrate_estimator_tests.cc b/third_party/libwebrtc/call/bitrate_estimator_tests.cc
index f17a037ed2..d945dfccca 100644
--- a/third_party/libwebrtc/call/bitrate_estimator_tests.cc
+++ b/third_party/libwebrtc/call/bitrate_estimator_tests.cc
@@ -178,13 +178,13 @@ class BitrateEstimatorTest : public test::CallTest {
RTC_DCHECK_EQ(1, test_->GetVideoEncoderConfig()->number_of_streams);
frame_generator_capturer_ =
std::make_unique<test::FrameGeneratorCapturer>(
- test->clock_,
+ &test->env().clock(),
test::CreateSquareFrameGenerator(
test::VideoTestConstants::kDefaultWidth,
test::VideoTestConstants::kDefaultHeight, absl::nullopt,
absl::nullopt),
test::VideoTestConstants::kDefaultFramerate,
- *test->task_queue_factory_);
+ test->env().task_queue_factory());
frame_generator_capturer_->Init();
frame_generator_capturer_->Start();
send_stream_->SetSource(frame_generator_capturer_.get(),
diff --git a/third_party/libwebrtc/call/call.cc b/third_party/libwebrtc/call/call.cc
index 0f3699501e..63dc370f1a 100644
--- a/third_party/libwebrtc/call/call.cc
+++ b/third_party/libwebrtc/call/call.cc
@@ -453,7 +453,7 @@ class Call final : public webrtc::Call,
bool is_started_ RTC_GUARDED_BY(worker_thread_) = false;
// Sequence checker for outgoing network traffic. Could be the network thread.
- // Could also be a pacer owned thread or TQ such as the TaskQueuePacedSender.
+ // Could also be a pacer owned thread or TQ such as the TaskQueueSender.
RTC_NO_UNIQUE_ADDRESS SequenceChecker sent_packet_sequence_checker_;
absl::optional<rtc::SentPacket> last_sent_packet_
RTC_GUARDED_BY(sent_packet_sequence_checker_);
@@ -462,7 +462,8 @@ class Call final : public webrtc::Call,
/* Mozilla: Avoid this since it could use GetRealTimeClock().
std::unique_ptr<Call> Call::Create(const CallConfig& config) {
- Clock* clock = Clock::GetRealTimeClock();
+ Clock* clock =
+ config.env.has_value() ? &config.env->clock() : Clock::GetRealTimeClock();
return Create(config, clock,
RtpTransportControllerSendFactory().Create(
config.ExtractTransportConfig(), clock));
diff --git a/third_party/libwebrtc/call/call_config.cc b/third_party/libwebrtc/call/call_config.cc
index 93f6b1aec4..5832969b9c 100644
--- a/third_party/libwebrtc/call/call_config.cc
+++ b/third_party/libwebrtc/call/call_config.cc
@@ -14,6 +14,14 @@
namespace webrtc {
+CallConfig::CallConfig(const Environment& env,
+ TaskQueueBase* network_task_queue)
+ : env(env),
+ event_log(&env.event_log()),
+ task_queue_factory(&env.task_queue_factory()),
+ trials(&env.field_trials()),
+ network_task_queue_(network_task_queue) {}
+
CallConfig::CallConfig(RtcEventLog* event_log,
TaskQueueBase* network_task_queue /* = nullptr*/)
: event_log(event_log), network_task_queue_(network_task_queue) {
@@ -31,7 +39,6 @@ RtpTransportConfig CallConfig::ExtractTransportConfig() const {
network_state_predictor_factory;
transportConfig.task_queue_factory = task_queue_factory;
transportConfig.trials = trials;
- transportConfig.pacer_burst_interval = pacer_burst_interval;
return transportConfig;
}
diff --git a/third_party/libwebrtc/call/call_config.h b/third_party/libwebrtc/call/call_config.h
index 918c077435..1b1f696fee 100644
--- a/third_party/libwebrtc/call/call_config.h
+++ b/third_party/libwebrtc/call/call_config.h
@@ -10,6 +10,8 @@
#ifndef CALL_CALL_CONFIG_H_
#define CALL_CALL_CONFIG_H_
+#include "absl/types/optional.h"
+#include "api/environment/environment.h"
#include "api/fec_controller.h"
#include "api/field_trials_view.h"
#include "api/metronome/metronome.h"
@@ -32,12 +34,23 @@ struct CallConfig {
// If `network_task_queue` is set to nullptr, Call will assume that network
// related callbacks will be made on the same TQ as the Call instance was
// constructed on.
+ explicit CallConfig(const Environment& env,
+ TaskQueueBase* network_task_queue = nullptr);
+
+ // TODO(bugs.webrtc.org/15656): Deprecate and delete constructor below.
explicit CallConfig(RtcEventLog* event_log,
TaskQueueBase* network_task_queue = nullptr);
+
CallConfig(const CallConfig&);
- RtpTransportConfig ExtractTransportConfig() const;
+
~CallConfig();
+ RtpTransportConfig ExtractTransportConfig() const;
+
+ // TODO(bugs.webrtc.org/15656): Make non-optional when constructor that
+ // doesn't pass Environment is removed.
+ absl::optional<Environment> env;
+
// Bitrate config used until valid bitrate estimates are calculated. Also
// used to cap total bitrate used. This comes from the remote connection.
BitrateConstraints bitrate_config;
@@ -79,9 +92,6 @@ struct CallConfig {
Metronome* metronome = nullptr;
- // The burst interval of the pacer, see TaskQueuePacedSender constructor.
- absl::optional<TimeDelta> pacer_burst_interval;
-
// Enables send packet batching from the egress RTP sender.
bool enable_send_packet_batching = false;
};
diff --git a/third_party/libwebrtc/call/call_gn/moz.build b/third_party/libwebrtc/call/call_gn/moz.build
index 25c1961b89..20f1f973a5 100644
--- a/third_party/libwebrtc/call/call_gn/moz.build
+++ b/third_party/libwebrtc/call/call_gn/moz.build
@@ -202,7 +202,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -212,10 +211,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/call/call_interfaces_gn/moz.build b/third_party/libwebrtc/call/call_interfaces_gn/moz.build
index a5796666d8..a7db90d471 100644
--- a/third_party/libwebrtc/call/call_interfaces_gn/moz.build
+++ b/third_party/libwebrtc/call/call_interfaces_gn/moz.build
@@ -206,7 +206,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -216,10 +215,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/call/call_perf_tests.cc b/third_party/libwebrtc/call/call_perf_tests.cc
index 0ba6d05b19..e3939e1371 100644
--- a/third_party/libwebrtc/call/call_perf_tests.cc
+++ b/third_party/libwebrtc/call/call_perf_tests.cc
@@ -212,7 +212,7 @@ void CallPerfTest::TestAudioVideoSync(FecMode fec,
metrics::Reset();
rtc::scoped_refptr<AudioDeviceModule> fake_audio_device =
TestAudioDeviceModule::Create(
- task_queue_factory_.get(),
+ &env().task_queue_factory(),
TestAudioDeviceModule::CreatePulsedNoiseCapturer(256, 48000),
TestAudioDeviceModule::CreateDiscardRenderer(48000),
audio_rtp_speed);
@@ -223,12 +223,12 @@ void CallPerfTest::TestAudioVideoSync(FecMode fec,
send_audio_state_config.audio_processing =
AudioProcessingBuilder().Create();
send_audio_state_config.audio_device_module = fake_audio_device;
- CallConfig sender_config(send_event_log_.get());
+ CallConfig sender_config = SendCallConfig();
auto audio_state = AudioState::Create(send_audio_state_config);
fake_audio_device->RegisterAudioCallback(audio_state->audio_transport());
sender_config.audio_state = audio_state;
- CallConfig receiver_config(recv_event_log_.get());
+ CallConfig receiver_config = RecvCallConfig();
receiver_config.audio_state = audio_state;
CreateCalls(sender_config, receiver_config);
@@ -319,7 +319,8 @@ void CallPerfTest::TestAudioVideoSync(FecMode fec,
}
EXPECT_EQ(1u, video_receive_streams_.size());
observer->set_receive_stream(video_receive_streams_[0]);
- drifting_clock = std::make_unique<DriftingClock>(clock_, video_ntp_speed);
+ drifting_clock =
+ std::make_unique<DriftingClock>(&env().clock(), video_ntp_speed);
CreateFrameGeneratorCapturerWithDrift(
drifting_clock.get(), video_rtp_speed,
test::VideoTestConstants::kDefaultFramerate,
diff --git a/third_party/libwebrtc/call/call_unittest.cc b/third_party/libwebrtc/call/call_unittest.cc
index 886a15aaf0..41394b9689 100644
--- a/third_party/libwebrtc/call/call_unittest.cc
+++ b/third_party/libwebrtc/call/call_unittest.cc
@@ -17,12 +17,11 @@
#include "absl/strings/string_view.h"
#include "api/audio_codecs/builtin_audio_decoder_factory.h"
+#include "api/environment/environment.h"
+#include "api/environment/environment_factory.h"
#include "api/media_types.h"
-#include "api/rtc_event_log/rtc_event_log.h"
-#include "api/task_queue/default_task_queue_factory.h"
#include "api/test/mock_audio_mixer.h"
#include "api/test/video/function_video_encoder_factory.h"
-#include "api/transport/field_trial_based_config.h"
#include "api/units/timestamp.h"
#include "api/video/builtin_video_bitrate_allocator_factory.h"
#include "audio/audio_receive_stream.h"
@@ -54,7 +53,6 @@ using ::webrtc::test::RunLoop;
struct CallHelper {
explicit CallHelper(bool use_null_audio_processing) {
- task_queue_factory_ = CreateDefaultTaskQueueFactory();
AudioState::Config audio_state_config;
audio_state_config.audio_mixer = rtc::make_ref_counted<MockAudioMixer>();
audio_state_config.audio_processing =
@@ -63,10 +61,8 @@ struct CallHelper {
: rtc::make_ref_counted<NiceMock<MockAudioProcessing>>();
audio_state_config.audio_device_module =
rtc::make_ref_counted<MockAudioDeviceModule>();
- CallConfig config(&event_log_);
+ CallConfig config(CreateEnvironment());
config.audio_state = AudioState::Create(audio_state_config);
- config.task_queue_factory = task_queue_factory_.get();
- config.trials = &field_trials_;
call_ = Call::Create(config);
}
@@ -74,9 +70,6 @@ struct CallHelper {
private:
RunLoop loop_;
- RtcEventLogNull event_log_;
- FieldTrialBasedConfig field_trials_;
- std::unique_ptr<TaskQueueFactory> task_queue_factory_;
std::unique_ptr<Call> call_;
};
diff --git a/third_party/libwebrtc/call/rampup_tests.cc b/third_party/libwebrtc/call/rampup_tests.cc
index 232fe0b3fe..66553f2674 100644
--- a/third_party/libwebrtc/call/rampup_tests.cc
+++ b/third_party/libwebrtc/call/rampup_tests.cc
@@ -16,9 +16,7 @@
#include "absl/strings/string_view.h"
#include "api/rtc_event_log/rtc_event_log_factory.h"
#include "api/rtc_event_log_output_file.h"
-#include "api/task_queue/default_task_queue_factory.h"
#include "api/task_queue/task_queue_base.h"
-#include "api/task_queue/task_queue_factory.h"
#include "api/test/metrics/global_metrics_logger_and_exporter.h"
#include "api/test/metrics/metric.h"
#include "call/fake_network_pipe.h"
@@ -565,30 +563,29 @@ void RampUpDownUpTester::EvolveTestState(int bitrate_bps, bool suspended) {
class RampUpTest : public test::CallTest {
public:
- RampUpTest()
- : task_queue_factory_(CreateDefaultTaskQueueFactory()),
- rtc_event_log_factory_(task_queue_factory_.get()) {
+ RampUpTest() {
std::string dump_name(absl::GetFlag(FLAGS_ramp_dump_name));
if (!dump_name.empty()) {
- send_event_log_ = rtc_event_log_factory_.CreateRtcEventLog(
- RtcEventLog::EncodingType::Legacy);
- recv_event_log_ = rtc_event_log_factory_.CreateRtcEventLog(
- RtcEventLog::EncodingType::Legacy);
+ std::unique_ptr<RtcEventLog> send_event_log =
+ rtc_event_log_factory_.Create(env());
+ std::unique_ptr<RtcEventLog> recv_event_log =
+ rtc_event_log_factory_.Create(env());
bool event_log_started =
- send_event_log_->StartLogging(
+ send_event_log->StartLogging(
std::make_unique<RtcEventLogOutputFile>(
dump_name + ".send.rtc.dat", RtcEventLog::kUnlimitedOutput),
RtcEventLog::kImmediateOutput) &&
- recv_event_log_->StartLogging(
+ recv_event_log->StartLogging(
std::make_unique<RtcEventLogOutputFile>(
dump_name + ".recv.rtc.dat", RtcEventLog::kUnlimitedOutput),
RtcEventLog::kImmediateOutput);
RTC_DCHECK(event_log_started);
+ SetSendEventLog(std::move(send_event_log));
+ SetRecvEventLog(std::move(recv_event_log));
}
}
private:
- const std::unique_ptr<TaskQueueFactory> task_queue_factory_;
RtcEventLogFactory rtc_event_log_factory_;
};
diff --git a/third_party/libwebrtc/call/receive_stream_interface_gn/moz.build b/third_party/libwebrtc/call/receive_stream_interface_gn/moz.build
index 92973e6d7b..f259414835 100644
--- a/third_party/libwebrtc/call/receive_stream_interface_gn/moz.build
+++ b/third_party/libwebrtc/call/receive_stream_interface_gn/moz.build
@@ -191,16 +191,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/call/rtp_demuxer.cc b/third_party/libwebrtc/call/rtp_demuxer.cc
index 5c53f48144..f5c4186871 100644
--- a/third_party/libwebrtc/call/rtp_demuxer.cc
+++ b/third_party/libwebrtc/call/rtp_demuxer.cc
@@ -255,6 +255,19 @@ bool RtpDemuxer::RemoveSink(const RtpPacketSinkInterface* sink) {
return num_removed > 0;
}
+flat_set<uint32_t> RtpDemuxer::GetSsrcsForSink(
+ const RtpPacketSinkInterface* sink) const {
+ flat_set<uint32_t> ssrcs;
+ if (sink) {
+ for (const auto& it : sink_by_ssrc_) {
+ if (it.second == sink) {
+ ssrcs.insert(it.first);
+ }
+ }
+ }
+ return ssrcs;
+}
+
bool RtpDemuxer::OnRtpPacket(const RtpPacketReceived& packet) {
RtpPacketSinkInterface* sink = ResolveSink(packet);
if (sink != nullptr) {
diff --git a/third_party/libwebrtc/call/rtp_demuxer.h b/third_party/libwebrtc/call/rtp_demuxer.h
index 53eeb0b6b6..80427b82a7 100644
--- a/third_party/libwebrtc/call/rtp_demuxer.h
+++ b/third_party/libwebrtc/call/rtp_demuxer.h
@@ -151,6 +151,9 @@ class RtpDemuxer {
// Null pointer is not allowed.
bool RemoveSink(const RtpPacketSinkInterface* sink);
+ // Returns the set of SSRCs associated with a sink.
+ flat_set<uint32_t> GetSsrcsForSink(const RtpPacketSinkInterface* sink) const;
+
// Demuxes the given packet and forwards it to the chosen sink. Returns true
// if the packet was forwarded and false if the packet was dropped.
bool OnRtpPacket(const RtpPacketReceived& packet);
diff --git a/third_party/libwebrtc/call/rtp_interfaces_gn/moz.build b/third_party/libwebrtc/call/rtp_interfaces_gn/moz.build
index c83031d5b5..d5223f0b8b 100644
--- a/third_party/libwebrtc/call/rtp_interfaces_gn/moz.build
+++ b/third_party/libwebrtc/call/rtp_interfaces_gn/moz.build
@@ -199,7 +199,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -209,10 +208,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/call/rtp_receiver_gn/moz.build b/third_party/libwebrtc/call/rtp_receiver_gn/moz.build
index 8809c7664f..b6b43b0afa 100644
--- a/third_party/libwebrtc/call/rtp_receiver_gn/moz.build
+++ b/third_party/libwebrtc/call/rtp_receiver_gn/moz.build
@@ -202,7 +202,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -212,10 +211,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/call/rtp_sender_gn/moz.build b/third_party/libwebrtc/call/rtp_sender_gn/moz.build
index 09560bbaab..54d1115417 100644
--- a/third_party/libwebrtc/call/rtp_sender_gn/moz.build
+++ b/third_party/libwebrtc/call/rtp_sender_gn/moz.build
@@ -202,7 +202,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -212,10 +211,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/call/rtp_transport_config.h b/third_party/libwebrtc/call/rtp_transport_config.h
index 6c94f7d911..f2030b3672 100644
--- a/third_party/libwebrtc/call/rtp_transport_config.h
+++ b/third_party/libwebrtc/call/rtp_transport_config.h
@@ -44,9 +44,6 @@ struct RtpTransportConfig {
// Key-value mapping of internal configurations to apply,
// e.g. field trials.
const FieldTrialsView* trials = nullptr;
-
- // The burst interval of the pacer, see TaskQueuePacedSender constructor.
- absl::optional<TimeDelta> pacer_burst_interval;
};
} // namespace webrtc
diff --git a/third_party/libwebrtc/call/rtp_transport_controller_send.cc b/third_party/libwebrtc/call/rtp_transport_controller_send.cc
index 556a4dd89a..8d24f7551e 100644
--- a/third_party/libwebrtc/call/rtp_transport_controller_send.cc
+++ b/third_party/libwebrtc/call/rtp_transport_controller_send.cc
@@ -80,12 +80,7 @@ RtpTransportControllerSend::RtpTransportControllerSend(
task_queue_(TaskQueueBase::Current()),
bitrate_configurator_(config.bitrate_config),
pacer_started_(false),
- pacer_(clock,
- &packet_router_,
- *config.trials,
- TimeDelta::Millis(5),
- 3,
- config.pacer_burst_interval),
+ pacer_(clock, &packet_router_, *config.trials, TimeDelta::Millis(5), 3),
observer_(nullptr),
controller_factory_override_(config.network_controller_factory),
controller_factory_fallback_(
diff --git a/third_party/libwebrtc/call/rtp_video_sender_unittest.cc b/third_party/libwebrtc/call/rtp_video_sender_unittest.cc
index cd2f1efbcf..9646a81cfd 100644
--- a/third_party/libwebrtc/call/rtp_video_sender_unittest.cc
+++ b/third_party/libwebrtc/call/rtp_video_sender_unittest.cc
@@ -1092,7 +1092,7 @@ TEST(RtpVideoSenderTest, ClearsPendingPacketsOnInactivation) {
// Set a very low bitrate.
test.router()->OnBitrateUpdated(
- CreateBitrateAllocationUpdate(/*rate_bps=*/30'000),
+ CreateBitrateAllocationUpdate(/*rate_bps=*/10'000),
/*framerate=*/30);
// Create and send a large keyframe.
@@ -1119,7 +1119,7 @@ TEST(RtpVideoSenderTest, ClearsPendingPacketsOnInactivation) {
EXPECT_FALSE(packet.Marker());
}
EXPECT_GT(transmittedPayload, DataSize::Zero());
- EXPECT_LT(transmittedPayload, DataSize::Bytes(kImageSizeBytes / 4));
+ EXPECT_LT(transmittedPayload, DataSize::Bytes(kImageSizeBytes / 3));
// Record the RTP timestamp of the first frame.
const uint32_t first_frame_timestamp = sent_packets[0].Timestamp();
diff --git a/third_party/libwebrtc/call/simulated_network.h b/third_party/libwebrtc/call/simulated_network.h
index 8597367add..02a37a3c43 100644
--- a/third_party/libwebrtc/call/simulated_network.h
+++ b/third_party/libwebrtc/call/simulated_network.h
@@ -36,7 +36,7 @@ namespace webrtc {
// - Extra delay with or without packets reorder
// - Packet overhead
// - Queue max capacity
-class SimulatedNetwork : public SimulatedNetworkInterface {
+class RTC_EXPORT SimulatedNetwork : public SimulatedNetworkInterface {
public:
using Config = BuiltInNetworkBehaviorConfig;
explicit SimulatedNetwork(Config config, uint64_t random_seed = 1);
diff --git a/third_party/libwebrtc/call/version.cc b/third_party/libwebrtc/call/version.cc
index 44c8c77156..5770253625 100644
--- a/third_party/libwebrtc/call/version.cc
+++ b/third_party/libwebrtc/call/version.cc
@@ -13,7 +13,7 @@
namespace webrtc {
// The timestamp is always in UTC.
-const char* const kSourceTimestamp = "WebRTC source stamp 2023-10-30T04:03:42";
+const char* const kSourceTimestamp = "WebRTC source stamp 2023-12-03T04:02:06";
void LoadWebRTCVersionInRegister() {
// Using volatile to instruct the compiler to not optimize `p` away even
diff --git a/third_party/libwebrtc/call/version_gn/moz.build b/third_party/libwebrtc/call/version_gn/moz.build
index e2e087a17f..28745e4fc6 100644
--- a/third_party/libwebrtc/call/version_gn/moz.build
+++ b/third_party/libwebrtc/call/version_gn/moz.build
@@ -184,7 +184,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -194,10 +193,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/call/video_send_stream.h b/third_party/libwebrtc/call/video_send_stream.h
index 1a0261be1b..2b4ea5b66a 100644
--- a/third_party/libwebrtc/call/video_send_stream.h
+++ b/third_party/libwebrtc/call/video_send_stream.h
@@ -113,6 +113,7 @@ class VideoSendStream {
uint64_t total_encoded_bytes_target = 0;
uint32_t frames = 0;
uint32_t frames_dropped_by_capturer = 0;
+ uint32_t frames_dropped_by_bad_timestamp = 0;
uint32_t frames_dropped_by_encoder_queue = 0;
uint32_t frames_dropped_by_rate_limiter = 0;
uint32_t frames_dropped_by_congestion_window = 0;
diff --git a/third_party/libwebrtc/call/video_stream_api_gn/moz.build b/third_party/libwebrtc/call/video_stream_api_gn/moz.build
index f2ec65de01..cf58d3748e 100644
--- a/third_party/libwebrtc/call/video_stream_api_gn/moz.build
+++ b/third_party/libwebrtc/call/video_stream_api_gn/moz.build
@@ -200,7 +200,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -210,10 +209,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/common_audio/common_audio_avx2_gn/moz.build b/third_party/libwebrtc/common_audio/common_audio_avx2_gn/moz.build
index 390c83ec43..708eb92e1e 100644
--- a/third_party/libwebrtc/common_audio/common_audio_avx2_gn/moz.build
+++ b/third_party/libwebrtc/common_audio/common_audio_avx2_gn/moz.build
@@ -177,10 +177,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "x86":
CXXFLAGS += [
diff --git a/third_party/libwebrtc/common_audio/common_audio_c_arm_asm_gn/moz.build b/third_party/libwebrtc/common_audio/common_audio_c_arm_asm_gn/moz.build
index ec4329a9cc..deb2dbe301 100644
--- a/third_party/libwebrtc/common_audio/common_audio_c_arm_asm_gn/moz.build
+++ b/third_party/libwebrtc/common_audio/common_audio_c_arm_asm_gn/moz.build
@@ -181,16 +181,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/common_audio/common_audio_c_gn/moz.build b/third_party/libwebrtc/common_audio/common_audio_c_gn/moz.build
index 1c3cdc1624..77ad77a1d5 100644
--- a/third_party/libwebrtc/common_audio/common_audio_c_gn/moz.build
+++ b/third_party/libwebrtc/common_audio/common_audio_c_gn/moz.build
@@ -292,7 +292,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "aarch64":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -302,10 +301,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
UNIFIED_SOURCES += [
"/third_party/libwebrtc/common_audio/signal_processing/complex_bit_reverse.c",
"/third_party/libwebrtc/common_audio/signal_processing/filter_ar_fast_q12.c"
diff --git a/third_party/libwebrtc/common_audio/common_audio_cc_gn/moz.build b/third_party/libwebrtc/common_audio/common_audio_cc_gn/moz.build
index 31757c2b89..9fc98aac37 100644
--- a/third_party/libwebrtc/common_audio/common_audio_cc_gn/moz.build
+++ b/third_party/libwebrtc/common_audio/common_audio_cc_gn/moz.build
@@ -199,7 +199,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -209,10 +208,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/common_audio/common_audio_gn/moz.build b/third_party/libwebrtc/common_audio/common_audio_gn/moz.build
index b6c5dc57c8..ee91cec775 100644
--- a/third_party/libwebrtc/common_audio/common_audio_gn/moz.build
+++ b/third_party/libwebrtc/common_audio/common_audio_gn/moz.build
@@ -212,7 +212,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -222,10 +221,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/common_audio/common_audio_neon_c_gn/moz.build b/third_party/libwebrtc/common_audio/common_audio_neon_c_gn/moz.build
index f2ef55667b..3890cd0f94 100644
--- a/third_party/libwebrtc/common_audio/common_audio_neon_c_gn/moz.build
+++ b/third_party/libwebrtc/common_audio/common_audio_neon_c_gn/moz.build
@@ -186,7 +186,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "aarch64":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
diff --git a/third_party/libwebrtc/common_audio/common_audio_neon_gn/moz.build b/third_party/libwebrtc/common_audio/common_audio_neon_gn/moz.build
index 2b5a1cf4cc..b8b06ffc74 100644
--- a/third_party/libwebrtc/common_audio/common_audio_neon_gn/moz.build
+++ b/third_party/libwebrtc/common_audio/common_audio_neon_gn/moz.build
@@ -185,7 +185,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "aarch64":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
diff --git a/third_party/libwebrtc/common_audio/common_audio_sse2_gn/moz.build b/third_party/libwebrtc/common_audio/common_audio_sse2_gn/moz.build
index 298c08b418..7f0b17b287 100644
--- a/third_party/libwebrtc/common_audio/common_audio_sse2_gn/moz.build
+++ b/third_party/libwebrtc/common_audio/common_audio_sse2_gn/moz.build
@@ -181,10 +181,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86_64":
CXXFLAGS += [
diff --git a/third_party/libwebrtc/common_audio/fir_filter_factory_gn/moz.build b/third_party/libwebrtc/common_audio/fir_filter_factory_gn/moz.build
index 699fdd0267..e5cef24832 100644
--- a/third_party/libwebrtc/common_audio/fir_filter_factory_gn/moz.build
+++ b/third_party/libwebrtc/common_audio/fir_filter_factory_gn/moz.build
@@ -200,7 +200,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -210,10 +209,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/common_audio/fir_filter_gn/moz.build b/third_party/libwebrtc/common_audio/fir_filter_gn/moz.build
index b0236d1067..4140a35292 100644
--- a/third_party/libwebrtc/common_audio/fir_filter_gn/moz.build
+++ b/third_party/libwebrtc/common_audio/fir_filter_gn/moz.build
@@ -176,16 +176,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/common_audio/sinc_resampler_gn/moz.build b/third_party/libwebrtc/common_audio/sinc_resampler_gn/moz.build
index cda88c03f9..ec0b21c2fc 100644
--- a/third_party/libwebrtc/common_audio/sinc_resampler_gn/moz.build
+++ b/third_party/libwebrtc/common_audio/sinc_resampler_gn/moz.build
@@ -191,16 +191,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/common_audio/third_party/ooura/fft_size_128_gn/moz.build b/third_party/libwebrtc/common_audio/third_party/ooura/fft_size_128_gn/moz.build
index 328c77410c..6efbe87f02 100644
--- a/third_party/libwebrtc/common_audio/third_party/ooura/fft_size_128_gn/moz.build
+++ b/third_party/libwebrtc/common_audio/third_party/ooura/fft_size_128_gn/moz.build
@@ -219,7 +219,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -230,10 +229,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86_64":
CXXFLAGS += [
diff --git a/third_party/libwebrtc/common_audio/third_party/ooura/fft_size_256_gn/moz.build b/third_party/libwebrtc/common_audio/third_party/ooura/fft_size_256_gn/moz.build
index e65c7c572f..d1e512c383 100644
--- a/third_party/libwebrtc/common_audio/third_party/ooura/fft_size_256_gn/moz.build
+++ b/third_party/libwebrtc/common_audio/third_party/ooura/fft_size_256_gn/moz.build
@@ -184,7 +184,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -194,10 +193,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/common_audio/third_party/spl_sqrt_floor/spl_sqrt_floor_gn/moz.build b/third_party/libwebrtc/common_audio/third_party/spl_sqrt_floor/spl_sqrt_floor_gn/moz.build
index 618af60da3..718ca3b4e8 100644
--- a/third_party/libwebrtc/common_audio/third_party/spl_sqrt_floor/spl_sqrt_floor_gn/moz.build
+++ b/third_party/libwebrtc/common_audio/third_party/spl_sqrt_floor/spl_sqrt_floor_gn/moz.build
@@ -212,7 +212,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "aarch64":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -222,10 +221,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
UNIFIED_SOURCES += [
"/third_party/libwebrtc/common_audio/third_party/spl_sqrt_floor/spl_sqrt_floor.c"
]
diff --git a/third_party/libwebrtc/common_video/common_video_gn/moz.build b/third_party/libwebrtc/common_video/common_video_gn/moz.build
index a767c9c765..4572d78fab 100644
--- a/third_party/libwebrtc/common_video/common_video_gn/moz.build
+++ b/third_party/libwebrtc/common_video/common_video_gn/moz.build
@@ -207,7 +207,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -217,10 +216,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/common_video/frame_counts_gn/moz.build b/third_party/libwebrtc/common_video/frame_counts_gn/moz.build
index 0ccbf9ac76..0727032aea 100644
--- a/third_party/libwebrtc/common_video/frame_counts_gn/moz.build
+++ b/third_party/libwebrtc/common_video/frame_counts_gn/moz.build
@@ -176,16 +176,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/common_video/generic_frame_descriptor/generic_frame_descriptor_gn/moz.build b/third_party/libwebrtc/common_video/generic_frame_descriptor/generic_frame_descriptor_gn/moz.build
index 7aa4e9bfff..1b9792fe56 100644
--- a/third_party/libwebrtc/common_video/generic_frame_descriptor/generic_frame_descriptor_gn/moz.build
+++ b/third_party/libwebrtc/common_video/generic_frame_descriptor/generic_frame_descriptor_gn/moz.build
@@ -188,7 +188,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -198,10 +197,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/common_video/h265/h265_bitstream_parser.cc b/third_party/libwebrtc/common_video/h265/h265_bitstream_parser.cc
index 1093add102..f8dc242c7d 100644
--- a/third_party/libwebrtc/common_video/h265/h265_bitstream_parser.cc
+++ b/third_party/libwebrtc/common_video/h265/h265_bitstream_parser.cc
@@ -475,8 +475,8 @@ void H265BitstreamParser::ParseSlice(const uint8_t* slice, size_t length) {
case H265::NaluType::kAud:
case H265::NaluType::kPrefixSei:
case H265::NaluType::kSuffixSei:
- case H265::NaluType::kAP:
- case H265::NaluType::kFU:
+ case H265::NaluType::kAp:
+ case H265::NaluType::kFu:
break;
default:
Result res = ParseNonParameterSetNalu(slice, length, nalu_type);
diff --git a/third_party/libwebrtc/common_video/h265/h265_common.h b/third_party/libwebrtc/common_video/h265/h265_common.h
index fcb97815ff..643726f701 100644
--- a/third_party/libwebrtc/common_video/h265/h265_common.h
+++ b/third_party/libwebrtc/common_video/h265/h265_common.h
@@ -55,8 +55,8 @@ enum NaluType : uint8_t {
kAud = 35,
kPrefixSei = 39,
kSuffixSei = 40,
- kAP = 48,
- kFU = 49
+ kAp = 48,
+ kFu = 49
};
// Slice type definition. See table 7-7 of the H265 spec
diff --git a/third_party/libwebrtc/examples/BUILD.gn b/third_party/libwebrtc/examples/BUILD.gn
index 458205cea7..4ace29bb7e 100644
--- a/third_party/libwebrtc/examples/BUILD.gn
+++ b/third_party/libwebrtc/examples/BUILD.gn
@@ -489,6 +489,7 @@ if (is_ios || (is_mac && target_cpu != "x86")) {
]
deps = [
+ "../api:enable_media",
"../api:libjingle_peerconnection_api",
"../api:scoped_refptr",
"../api:sequence_checker",
@@ -496,7 +497,6 @@ if (is_ios || (is_mac && target_cpu != "x86")) {
"../api/audio_codecs:builtin_audio_encoder_factory",
"../api/rtc_event_log:rtc_event_log_factory",
"../api/task_queue:default_task_queue_factory",
- "../media:rtc_audio_video",
"../modules/audio_processing",
"../modules/audio_processing:api",
"../pc:libjingle_peerconnection",
diff --git a/third_party/libwebrtc/examples/androidnativeapi/BUILD.gn b/third_party/libwebrtc/examples/androidnativeapi/BUILD.gn
index e0eb6d8b24..d1088b3e61 100644
--- a/third_party/libwebrtc/examples/androidnativeapi/BUILD.gn
+++ b/third_party/libwebrtc/examples/androidnativeapi/BUILD.gn
@@ -47,6 +47,7 @@ if (is_android) {
deps = [
":generated_jni",
+ "../../api:enable_media_with_defaults",
"../../api:scoped_refptr",
"../../api:sequence_checker",
"../../rtc_base:ssl",
@@ -56,7 +57,6 @@ if (is_android) {
"//api/task_queue:default_task_queue_factory",
"//media:rtc_audio_video",
"//media:rtc_internal_video_codecs",
- "//media:rtc_media_engine_defaults",
"//modules/utility",
"//pc:libjingle_peerconnection",
"//sdk/android:native_api_base",
diff --git a/third_party/libwebrtc/examples/androidnativeapi/jni/android_call_client.cc b/third_party/libwebrtc/examples/androidnativeapi/jni/android_call_client.cc
index 2713a563cd..40af78cdac 100644
--- a/third_party/libwebrtc/examples/androidnativeapi/jni/android_call_client.cc
+++ b/third_party/libwebrtc/examples/androidnativeapi/jni/android_call_client.cc
@@ -13,6 +13,7 @@
#include <memory>
#include <utility>
+#include "api/enable_media_with_defaults.h"
#include "api/peer_connection_interface.h"
#include "api/rtc_event_log/rtc_event_log_factory.h"
#include "api/task_queue/default_task_queue_factory.h"
@@ -20,7 +21,6 @@
#include "media/engine/internal_decoder_factory.h"
#include "media/engine/internal_encoder_factory.h"
#include "media/engine/webrtc_media_engine.h"
-#include "media/engine/webrtc_media_engine_defaults.h"
#include "sdk/android/native_api/jni/java_types.h"
#include "sdk/android/native_api/video/wrapper.h"
@@ -154,19 +154,14 @@ void AndroidCallClient::CreatePeerConnectionFactory() {
pcf_deps.worker_thread = worker_thread_.get();
pcf_deps.signaling_thread = signaling_thread_.get();
pcf_deps.task_queue_factory = webrtc::CreateDefaultTaskQueueFactory();
- pcf_deps.call_factory = webrtc::CreateCallFactory();
pcf_deps.event_log_factory = std::make_unique<webrtc::RtcEventLogFactory>(
pcf_deps.task_queue_factory.get());
- cricket::MediaEngineDependencies media_deps;
- media_deps.task_queue_factory = pcf_deps.task_queue_factory.get();
- media_deps.video_encoder_factory =
+ pcf_deps.video_encoder_factory =
std::make_unique<webrtc::InternalEncoderFactory>();
- media_deps.video_decoder_factory =
+ pcf_deps.video_decoder_factory =
std::make_unique<webrtc::InternalDecoderFactory>();
- webrtc::SetMediaEngineDefaults(&media_deps);
- pcf_deps.media_engine = cricket::CreateMediaEngine(std::move(media_deps));
- RTC_LOG(LS_INFO) << "Media engine created: " << pcf_deps.media_engine.get();
+ EnableMediaWithDefaults(pcf_deps);
pcf_ = CreateModularPeerConnectionFactory(std::move(pcf_deps));
RTC_LOG(LS_INFO) << "PeerConnectionFactory created: " << pcf_.get();
diff --git a/third_party/libwebrtc/examples/objcnativeapi/objc/objc_call_client.mm b/third_party/libwebrtc/examples/objcnativeapi/objc/objc_call_client.mm
index 90bcfcc35b..996c6a9c7f 100644
--- a/third_party/libwebrtc/examples/objcnativeapi/objc/objc_call_client.mm
+++ b/third_party/libwebrtc/examples/objcnativeapi/objc/objc_call_client.mm
@@ -20,10 +20,10 @@
#include "api/audio_codecs/builtin_audio_decoder_factory.h"
#include "api/audio_codecs/builtin_audio_encoder_factory.h"
+#include "api/enable_media.h"
#include "api/peer_connection_interface.h"
#include "api/rtc_event_log/rtc_event_log_factory.h"
#include "api/task_queue/default_task_queue_factory.h"
-#include "media/engine/webrtc_media_engine.h"
#include "modules/audio_processing/include/audio_processing.h"
#include "sdk/objc/native/api/video_capturer.h"
#include "sdk/objc/native/api/video_decoder_factory.h"
@@ -118,18 +118,14 @@ void ObjCCallClient::CreatePeerConnectionFactory() {
dependencies.worker_thread = worker_thread_.get();
dependencies.signaling_thread = signaling_thread_.get();
dependencies.task_queue_factory = webrtc::CreateDefaultTaskQueueFactory();
- cricket::MediaEngineDependencies media_deps;
- media_deps.task_queue_factory = dependencies.task_queue_factory.get();
- media_deps.audio_encoder_factory = webrtc::CreateBuiltinAudioEncoderFactory();
- media_deps.audio_decoder_factory = webrtc::CreateBuiltinAudioDecoderFactory();
- media_deps.video_encoder_factory = webrtc::ObjCToNativeVideoEncoderFactory(
+ dependencies.audio_encoder_factory = webrtc::CreateBuiltinAudioEncoderFactory();
+ dependencies.audio_decoder_factory = webrtc::CreateBuiltinAudioDecoderFactory();
+ dependencies.video_encoder_factory = webrtc::ObjCToNativeVideoEncoderFactory(
[[RTC_OBJC_TYPE(RTCDefaultVideoEncoderFactory) alloc] init]);
- media_deps.video_decoder_factory = webrtc::ObjCToNativeVideoDecoderFactory(
+ dependencies.video_decoder_factory = webrtc::ObjCToNativeVideoDecoderFactory(
[[RTC_OBJC_TYPE(RTCDefaultVideoDecoderFactory) alloc] init]);
- media_deps.audio_processing = webrtc::AudioProcessingBuilder().Create();
- dependencies.media_engine = cricket::CreateMediaEngine(std::move(media_deps));
- RTC_LOG(LS_INFO) << "Media engine created: " << dependencies.media_engine.get();
- dependencies.call_factory = webrtc::CreateCallFactory();
+ dependencies.audio_processing = webrtc::AudioProcessingBuilder().Create();
+ webrtc::EnableMedia(dependencies);
dependencies.event_log_factory =
std::make_unique<webrtc::RtcEventLogFactory>(dependencies.task_queue_factory.get());
pcf_ = webrtc::CreateModularPeerConnectionFactory(std::move(dependencies));
diff --git a/third_party/libwebrtc/examples/peerconnection/client/peer_connection_client.cc b/third_party/libwebrtc/examples/peerconnection/client/peer_connection_client.cc
index 48d5bb6545..488d2f04ed 100644
--- a/third_party/libwebrtc/examples/peerconnection/client/peer_connection_client.cc
+++ b/third_party/libwebrtc/examples/peerconnection/client/peer_connection_client.cc
@@ -16,6 +16,7 @@
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
#include "rtc_base/net_helpers.h"
+#include "rtc_base/thread.h"
namespace {
diff --git a/third_party/libwebrtc/examples/stunserver/stunserver_main.cc b/third_party/libwebrtc/examples/stunserver/stunserver_main.cc
index 8180069bf0..ecf6c81ff1 100644
--- a/third_party/libwebrtc/examples/stunserver/stunserver_main.cc
+++ b/third_party/libwebrtc/examples/stunserver/stunserver_main.cc
@@ -29,7 +29,8 @@ int main(int argc, char* argv[]) {
return 1;
}
- rtc::Thread* pthMain = rtc::Thread::Current();
+ rtc::Thread* pthMain = rtc::ThreadManager::Instance()->WrapCurrentThread();
+ RTC_DCHECK(pthMain);
rtc::AsyncUDPSocket* server_socket =
rtc::AsyncUDPSocket::Create(pthMain->socketserver(), server_addr);
diff --git a/third_party/libwebrtc/experiments/field_trials.py b/third_party/libwebrtc/experiments/field_trials.py
index e39b53eb47..567cafc058 100755
--- a/third_party/libwebrtc/experiments/field_trials.py
+++ b/third_party/libwebrtc/experiments/field_trials.py
@@ -107,9 +107,9 @@ ACTIVE_FIELD_TRIALS: FrozenSet[FieldTrial] = frozenset([
FieldTrial('WebRTC-SendPacketsOnWorkerThread',
'webrtc:14502',
date(2024, 4, 1)),
- FieldTrial('WebRTC-Stats-RtxReceiveStats',
- 'webrtc:15096',
- date(2024, 4, 1)),
+ FieldTrial('WebRTC-SrtpRemoveReceiveStream',
+ 'webrtc:15604',
+ date(2024, 10, 1)),
FieldTrial('WebRTC-TaskQueue-ReplaceLibeventWithStdlib',
'webrtc:14389',
date(2024, 4, 1)),
diff --git a/third_party/libwebrtc/experiments/registered_field_trials_gn/moz.build b/third_party/libwebrtc/experiments/registered_field_trials_gn/moz.build
index 023dd46dd1..f261435853 100644
--- a/third_party/libwebrtc/experiments/registered_field_trials_gn/moz.build
+++ b/third_party/libwebrtc/experiments/registered_field_trials_gn/moz.build
@@ -176,16 +176,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/g3doc/style-guide.md b/third_party/libwebrtc/g3doc/style-guide.md
index b32163f906..57cb85c466 100644
--- a/third_party/libwebrtc/g3doc/style-guide.md
+++ b/third_party/libwebrtc/g3doc/style-guide.md
@@ -65,7 +65,7 @@ Follow the [Google styleguide for `TODO` comments][goog-style-todo]. When
referencing a WebRTC bug, prefer using the URL form (excluding the scheme part):
```cpp
-// TODO(bugs.webrtc.org/12345): Delete the hack when blocking bugs are resolved.
+// TODO: bugs.webrtc.org/12345 - Delete the hack when blocking bugs are resolved.
```
The short form used in commit messages, e.g. `webrtc:12345`, is discouraged.
@@ -132,7 +132,8 @@ docs.
WebRTC uses std::string, with content assumed to be UTF-8. Note that this
has to be verified whenever accepting external input.
-For concatenation of strings, use rtc::SimpleStringBuilder.
+For concatenation of strings, use webrtc::StrJoin or rtc::SimpleStringBuilder
+directly.
The following string building tools are NOT recommended:
* The + operator. See https://abseil.io/tips/3 for why not.
diff --git a/third_party/libwebrtc/infra/specs/client.webrtc.json b/third_party/libwebrtc/infra/specs/client.webrtc.json
index 5f8adfc40e..6f8bfb5ba5 100644
--- a/third_party/libwebrtc/infra/specs/client.webrtc.json
+++ b/third_party/libwebrtc/infra/specs/client.webrtc.json
@@ -8034,10 +8034,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_13c100",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -8083,10 +8084,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -8132,10 +8134,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -8180,10 +8183,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_13c100",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -8228,10 +8232,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -8276,10 +8281,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -8324,10 +8330,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_13c100",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -8372,10 +8379,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -8420,10 +8428,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -8468,10 +8477,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_13c100",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -8516,10 +8526,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -8564,10 +8575,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -8612,10 +8624,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_13c100",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -8660,10 +8673,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -8708,10 +8722,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -8756,10 +8771,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_13c100",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -8805,10 +8821,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -8854,10 +8871,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -8903,10 +8921,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_13c100",
"cores": "12",
"cpu": "x86-64",
"os": "Mac-12"
@@ -8953,10 +8972,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cores": "12",
"cpu": "x86-64",
"os": "Mac-12"
@@ -9003,10 +9023,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cores": "12",
"cpu": "x86-64",
"os": "Mac-12"
@@ -9053,10 +9074,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_13c100",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -9101,10 +9123,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -9149,10 +9172,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -9197,10 +9221,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_13c100",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -9245,10 +9270,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -9293,10 +9319,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -9341,10 +9368,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_13c100",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -9389,10 +9417,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -9437,10 +9466,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -9485,10 +9515,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_13c100",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -9534,10 +9565,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -9583,10 +9615,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -9633,10 +9666,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_13c100",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -9682,10 +9716,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -9731,10 +9766,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -9780,10 +9816,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_13c100",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -9829,10 +9866,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -9878,10 +9916,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -9926,10 +9965,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_13c100",
"cores": "12",
"cpu": "x86-64",
"os": "Mac-12"
@@ -9976,10 +10016,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cores": "12",
"cpu": "x86-64",
"os": "Mac-12"
@@ -10026,10 +10067,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cores": "12",
"cpu": "x86-64",
"os": "Mac-12"
@@ -10076,10 +10118,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_13c100",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -10124,10 +10167,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -10172,10 +10216,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -10220,10 +10265,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_13c100",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -10268,10 +10314,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -10316,10 +10363,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -10364,10 +10412,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_13c100",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -10412,10 +10461,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -10460,10 +10510,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -10508,10 +10559,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_13c100",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -10556,10 +10608,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -10604,10 +10657,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -10652,10 +10706,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_13c100",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -10701,10 +10756,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -10750,10 +10806,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -10799,10 +10856,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_13c100",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -10847,10 +10905,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -10895,10 +10954,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -10943,10 +11003,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_13c100",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -10991,10 +11052,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -11039,10 +11101,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
diff --git a/third_party/libwebrtc/infra/specs/internal.client.webrtc.json b/third_party/libwebrtc/infra/specs/internal.client.webrtc.json
index 1e3a147201..59547fc132 100644
--- a/third_party/libwebrtc/infra/specs/internal.client.webrtc.json
+++ b/third_party/libwebrtc/infra/specs/internal.client.webrtc.json
@@ -7,7 +7,7 @@
"args": [
"--xctest",
"--xcode-build-version",
- "15a240d",
+ "15a507",
"--out-dir",
"${ISOLATED_OUTDIR}"
],
@@ -24,7 +24,7 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
@@ -34,7 +34,7 @@
},
"named_caches": [
{
- "name": "xcode_ios_15a240d",
+ "name": "xcode_ios_15a507",
"path": "Xcode.app"
}
],
@@ -47,7 +47,7 @@
"args": [
"--xctest",
"--xcode-build-version",
- "15a240d",
+ "15a507",
"--out-dir",
"${ISOLATED_OUTDIR}"
],
@@ -64,7 +64,7 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
@@ -74,7 +74,7 @@
},
"named_caches": [
{
- "name": "xcode_ios_15a240d",
+ "name": "xcode_ios_15a507",
"path": "Xcode.app"
}
],
@@ -88,7 +88,7 @@
"--readline-timeout=1200",
"--xctest",
"--xcode-build-version",
- "15a240d",
+ "15a507",
"--out-dir",
"${ISOLATED_OUTDIR}"
],
@@ -105,7 +105,7 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
@@ -117,7 +117,7 @@
"io_timeout": 7200,
"named_caches": [
{
- "name": "xcode_ios_15a240d",
+ "name": "xcode_ios_15a507",
"path": "Xcode.app"
}
],
@@ -131,7 +131,7 @@
"args": [
"--xctest",
"--xcode-build-version",
- "15a240d",
+ "15a507",
"--out-dir",
"${ISOLATED_OUTDIR}"
],
@@ -148,7 +148,7 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
@@ -158,7 +158,7 @@
},
"named_caches": [
{
- "name": "xcode_ios_15a240d",
+ "name": "xcode_ios_15a507",
"path": "Xcode.app"
}
],
@@ -172,7 +172,7 @@
"args": [
"--xctest",
"--xcode-build-version",
- "15a240d",
+ "15a507",
"--out-dir",
"${ISOLATED_OUTDIR}"
],
@@ -189,7 +189,7 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
@@ -199,7 +199,7 @@
},
"named_caches": [
{
- "name": "xcode_ios_15a240d",
+ "name": "xcode_ios_15a507",
"path": "Xcode.app"
}
],
@@ -212,7 +212,7 @@
"args": [
"--xctest",
"--xcode-build-version",
- "15a240d",
+ "15a507",
"--out-dir",
"${ISOLATED_OUTDIR}"
],
@@ -229,7 +229,7 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
@@ -239,7 +239,7 @@
},
"named_caches": [
{
- "name": "xcode_ios_15a240d",
+ "name": "xcode_ios_15a507",
"path": "Xcode.app"
}
],
@@ -252,7 +252,7 @@
"args": [
"--xctest",
"--xcode-build-version",
- "15a240d",
+ "15a507",
"--out-dir",
"${ISOLATED_OUTDIR}"
],
@@ -269,7 +269,7 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
@@ -279,7 +279,7 @@
},
"named_caches": [
{
- "name": "xcode_ios_15a240d",
+ "name": "xcode_ios_15a507",
"path": "Xcode.app"
}
],
@@ -292,7 +292,7 @@
"args": [
"--xctest",
"--xcode-build-version",
- "15a240d",
+ "15a507",
"--out-dir",
"${ISOLATED_OUTDIR}"
],
@@ -309,7 +309,7 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
@@ -319,7 +319,7 @@
},
"named_caches": [
{
- "name": "xcode_ios_15a240d",
+ "name": "xcode_ios_15a507",
"path": "Xcode.app"
}
],
@@ -332,7 +332,7 @@
"args": [
"--xctest",
"--xcode-build-version",
- "15a240d",
+ "15a507",
"--out-dir",
"${ISOLATED_OUTDIR}"
],
@@ -349,7 +349,7 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
@@ -359,7 +359,7 @@
},
"named_caches": [
{
- "name": "xcode_ios_15a240d",
+ "name": "xcode_ios_15a507",
"path": "Xcode.app"
}
],
@@ -372,7 +372,7 @@
"args": [
"--xctest",
"--xcode-build-version",
- "15a240d",
+ "15a507",
"--out-dir",
"${ISOLATED_OUTDIR}"
],
@@ -389,7 +389,7 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
@@ -399,7 +399,7 @@
},
"named_caches": [
{
- "name": "xcode_ios_15a240d",
+ "name": "xcode_ios_15a507",
"path": "Xcode.app"
}
],
@@ -412,7 +412,7 @@
"args": [
"--xctest",
"--xcode-build-version",
- "15a240d",
+ "15a507",
"--out-dir",
"${ISOLATED_OUTDIR}"
],
@@ -429,7 +429,7 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
@@ -439,7 +439,7 @@
},
"named_caches": [
{
- "name": "xcode_ios_15a240d",
+ "name": "xcode_ios_15a507",
"path": "Xcode.app"
}
],
@@ -480,10 +480,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"device_status": "available",
"id": "mac-254-e504",
"os": "iOS-15.7.8",
@@ -511,7 +512,7 @@
"args": [
"--xctest",
"--xcode-build-version",
- "15a240d",
+ "15a507",
"--out-dir",
"${ISOLATED_OUTDIR}"
],
@@ -528,7 +529,7 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
@@ -538,7 +539,7 @@
},
"named_caches": [
{
- "name": "xcode_ios_15a240d",
+ "name": "xcode_ios_15a507",
"path": "Xcode.app"
}
],
@@ -551,7 +552,7 @@
"args": [
"--xctest",
"--xcode-build-version",
- "15a240d",
+ "15a507",
"--out-dir",
"${ISOLATED_OUTDIR}"
],
@@ -568,7 +569,7 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
@@ -578,7 +579,7 @@
},
"named_caches": [
{
- "name": "xcode_ios_15a240d",
+ "name": "xcode_ios_15a507",
"path": "Xcode.app"
}
],
@@ -592,7 +593,7 @@
"--readline-timeout=1200",
"--xctest",
"--xcode-build-version",
- "15a240d",
+ "15a507",
"--out-dir",
"${ISOLATED_OUTDIR}"
],
@@ -609,7 +610,7 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
@@ -621,7 +622,7 @@
"io_timeout": 7200,
"named_caches": [
{
- "name": "xcode_ios_15a240d",
+ "name": "xcode_ios_15a507",
"path": "Xcode.app"
}
],
@@ -635,7 +636,7 @@
"args": [
"--xctest",
"--xcode-build-version",
- "15a240d",
+ "15a507",
"--out-dir",
"${ISOLATED_OUTDIR}"
],
@@ -652,7 +653,7 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
@@ -662,7 +663,7 @@
},
"named_caches": [
{
- "name": "xcode_ios_15a240d",
+ "name": "xcode_ios_15a507",
"path": "Xcode.app"
}
],
@@ -676,7 +677,7 @@
"args": [
"--xctest",
"--xcode-build-version",
- "15a240d",
+ "15a507",
"--out-dir",
"${ISOLATED_OUTDIR}"
],
@@ -693,7 +694,7 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
@@ -703,7 +704,7 @@
},
"named_caches": [
{
- "name": "xcode_ios_15a240d",
+ "name": "xcode_ios_15a507",
"path": "Xcode.app"
}
],
@@ -716,7 +717,7 @@
"args": [
"--xctest",
"--xcode-build-version",
- "15a240d",
+ "15a507",
"--out-dir",
"${ISOLATED_OUTDIR}"
],
@@ -733,7 +734,7 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
@@ -743,7 +744,7 @@
},
"named_caches": [
{
- "name": "xcode_ios_15a240d",
+ "name": "xcode_ios_15a507",
"path": "Xcode.app"
}
],
@@ -756,7 +757,7 @@
"args": [
"--xctest",
"--xcode-build-version",
- "15a240d",
+ "15a507",
"--out-dir",
"${ISOLATED_OUTDIR}"
],
@@ -773,7 +774,7 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
@@ -783,7 +784,7 @@
},
"named_caches": [
{
- "name": "xcode_ios_15a240d",
+ "name": "xcode_ios_15a507",
"path": "Xcode.app"
}
],
@@ -796,7 +797,7 @@
"args": [
"--xctest",
"--xcode-build-version",
- "15a240d",
+ "15a507",
"--out-dir",
"${ISOLATED_OUTDIR}"
],
@@ -813,7 +814,7 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
@@ -823,7 +824,7 @@
},
"named_caches": [
{
- "name": "xcode_ios_15a240d",
+ "name": "xcode_ios_15a507",
"path": "Xcode.app"
}
],
@@ -836,7 +837,7 @@
"args": [
"--xctest",
"--xcode-build-version",
- "15a240d",
+ "15a507",
"--out-dir",
"${ISOLATED_OUTDIR}"
],
@@ -853,7 +854,7 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
@@ -863,7 +864,7 @@
},
"named_caches": [
{
- "name": "xcode_ios_15a240d",
+ "name": "xcode_ios_15a507",
"path": "Xcode.app"
}
],
@@ -876,7 +877,7 @@
"args": [
"--xctest",
"--xcode-build-version",
- "15a240d",
+ "15a507",
"--out-dir",
"${ISOLATED_OUTDIR}"
],
@@ -893,7 +894,7 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
@@ -903,7 +904,7 @@
},
"named_caches": [
{
- "name": "xcode_ios_15a240d",
+ "name": "xcode_ios_15a507",
"path": "Xcode.app"
}
],
@@ -916,7 +917,7 @@
"args": [
"--xctest",
"--xcode-build-version",
- "15a240d",
+ "15a507",
"--out-dir",
"${ISOLATED_OUTDIR}"
],
@@ -933,7 +934,7 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
@@ -943,7 +944,7 @@
},
"named_caches": [
{
- "name": "xcode_ios_15a240d",
+ "name": "xcode_ios_15a507",
"path": "Xcode.app"
}
],
diff --git a/third_party/libwebrtc/infra/specs/mixins.pyl b/third_party/libwebrtc/infra/specs/mixins.pyl
index cbf9b2472f..e436846ef0 100644
--- a/third_party/libwebrtc/infra/specs/mixins.pyl
+++ b/third_party/libwebrtc/infra/specs/mixins.pyl
@@ -176,7 +176,7 @@
'location':
'.',
'revision':
- 'git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb'
+ 'git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce'
}]
}
},
@@ -309,6 +309,9 @@
'xcode_13_main': {
'args': ['--xcode-build-version', '13c100'],
'swarming': {
+ 'dimensions': {
+ 'caches': 'xcode_ios_13c100'
+ },
'named_caches': [{
'name': 'xcode_ios_13c100',
'path': 'Xcode.app'
@@ -318,6 +321,9 @@
'xcode_14_main': {
'args': ['--xcode-build-version', '14c18'],
'swarming': {
+ 'dimensions': {
+ 'caches': 'xcode_ios_14c18'
+ },
'named_caches': [{
'name': 'xcode_ios_14c18',
'path': 'Xcode.app'
@@ -325,10 +331,10 @@
}
},
'xcode_15_main': {
- 'args': ['--xcode-build-version', '15a240d'],
+ 'args': ['--xcode-build-version', '15a507'],
'swarming': {
'named_caches': [{
- 'name': 'xcode_ios_15a240d',
+ 'name': 'xcode_ios_15a507',
'path': 'Xcode.app'
}]
}
diff --git a/third_party/libwebrtc/infra/specs/mixins_webrtc.pyl b/third_party/libwebrtc/infra/specs/mixins_webrtc.pyl
index f8fa66b27f..443a4450eb 100644
--- a/third_party/libwebrtc/infra/specs/mixins_webrtc.pyl
+++ b/third_party/libwebrtc/infra/specs/mixins_webrtc.pyl
@@ -224,6 +224,9 @@
'xcode_13_main': {
'args': ['--xcode-build-version', '13c100'],
'swarming': {
+ 'dimensions': {
+ 'caches': 'xcode_ios_13c100',
+ },
'named_caches': [{
'name': 'xcode_ios_13c100',
'path': 'Xcode.app'
@@ -233,6 +236,9 @@
'xcode_14_main': {
'args': ['--xcode-build-version', '14c18'],
'swarming': {
+ 'dimensions': {
+ 'caches': 'xcode_ios_14c18',
+ },
'named_caches': [{
'name': 'xcode_ios_14c18',
'path': 'Xcode.app'
diff --git a/third_party/libwebrtc/infra/specs/tryserver.webrtc.json b/third_party/libwebrtc/infra/specs/tryserver.webrtc.json
index 61f4221970..61e47fd0f8 100644
--- a/third_party/libwebrtc/infra/specs/tryserver.webrtc.json
+++ b/third_party/libwebrtc/infra/specs/tryserver.webrtc.json
@@ -2242,10 +2242,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_13c100",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -2291,10 +2292,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -2340,10 +2342,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -2388,10 +2391,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_13c100",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -2436,10 +2440,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -2484,10 +2489,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -2532,10 +2538,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_13c100",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -2580,10 +2587,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -2628,10 +2636,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -2676,10 +2685,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_13c100",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -2724,10 +2734,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -2772,10 +2783,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -2820,10 +2832,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_13c100",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -2868,10 +2881,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -2916,10 +2930,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -2964,10 +2979,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_13c100",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -3013,10 +3029,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -3062,10 +3079,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -3111,10 +3129,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_13c100",
"cores": "12",
"cpu": "x86-64",
"os": "Mac-12"
@@ -3161,10 +3180,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cores": "12",
"cpu": "x86-64",
"os": "Mac-12"
@@ -3211,10 +3231,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cores": "12",
"cpu": "x86-64",
"os": "Mac-12"
@@ -3261,10 +3282,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_13c100",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -3309,10 +3331,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -3357,10 +3380,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -3405,10 +3429,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_13c100",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -3453,10 +3478,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -3501,10 +3527,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -3549,10 +3576,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_13c100",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -3597,10 +3625,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -3645,10 +3674,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -3693,10 +3723,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_13c100",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -3742,10 +3773,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -3791,10 +3823,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -3841,10 +3874,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_13c100",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -3890,10 +3924,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -3939,10 +3974,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -3988,10 +4024,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_13c100",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -4037,10 +4074,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -4086,10 +4124,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -4134,10 +4173,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_13c100",
"cores": "12",
"cpu": "x86-64",
"os": "Mac-12"
@@ -4184,10 +4224,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cores": "12",
"cpu": "x86-64",
"os": "Mac-12"
@@ -4234,10 +4275,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cores": "12",
"cpu": "x86-64",
"os": "Mac-12"
@@ -4284,10 +4326,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_13c100",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -4332,10 +4375,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -4380,10 +4424,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -4428,10 +4473,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_13c100",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -4476,10 +4522,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -4524,10 +4571,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -4572,10 +4620,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_13c100",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -4620,10 +4669,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -4668,10 +4718,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -4716,10 +4767,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_13c100",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -4764,10 +4816,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -4812,10 +4865,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -4860,10 +4914,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_13c100",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -4909,10 +4964,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -4958,10 +5014,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -5007,10 +5064,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_13c100",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -5055,10 +5113,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -5103,10 +5162,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -5151,10 +5211,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_13c100",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -5199,10 +5260,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
@@ -5247,10 +5309,11 @@
{
"cipd_package": "infra/tools/mac_toolchain/${platform}",
"location": ".",
- "revision": "git_revision:59ddedfe3849abf560cbe0b41bb8e431041cd2bb"
+ "revision": "git_revision:32d81d877ee07af07bf03b7f70ce597e323b80ce"
}
],
"dimensions": {
+ "caches": "xcode_ios_14c18",
"cpu": "x86-64",
"os": "Mac-12"
},
diff --git a/third_party/libwebrtc/infra/specs/variants.pyl b/third_party/libwebrtc/infra/specs/variants.pyl
index cf050c671e..e764f698e3 100644
--- a/third_party/libwebrtc/infra/specs/variants.pyl
+++ b/third_party/libwebrtc/infra/specs/variants.pyl
@@ -15,10 +15,7 @@
'14.5',
],
'identifier': 'iPhone X 14.5',
- 'mixins': [
- 'xcode_13_main',
- 'ios_runtime_cache_14_5',
- ],
+ 'mixins': ['xcode_13_main', 'ios_runtime_cache_14_5'],
},
'SIM_IPHONE_X_15_5': {
'args': [
@@ -28,10 +25,7 @@
'15.5',
],
'identifier': 'iPhone X 15.5',
- 'mixins': [
- 'xcode_14_main',
- 'ios_runtime_cache_15_5',
- ],
+ 'mixins': ['xcode_14_main', 'ios_runtime_cache_15_5'],
},
'SIM_IPHONE_X_16_2': {
'args': [
@@ -41,9 +35,6 @@
'16.2',
],
'identifier': 'iPhone X 16.2',
- 'mixins': [
- 'xcode_14_main',
- 'ios_runtime_cache_16_2',
- ],
+ 'mixins': ['xcode_14_main', 'ios_runtime_cache_16_2'],
},
}
diff --git a/third_party/libwebrtc/logging/BUILD.gn b/third_party/libwebrtc/logging/BUILD.gn
index ab1fbbc52b..92f55edfa0 100644
--- a/third_party/libwebrtc/logging/BUILD.gn
+++ b/third_party/libwebrtc/logging/BUILD.gn
@@ -281,7 +281,6 @@ rtc_library("rtc_event_number_encodings") {
"../rtc_base:bit_buffer",
"../rtc_base:bitstream_reader",
"../rtc_base:checks",
- "../rtc_base:ignore_wundef",
"../rtc_base:macromagic",
]
absl_deps = [
@@ -355,7 +354,6 @@ rtc_library("rtc_event_log_impl_encoder") {
"../rtc_base:bitstream_reader",
"../rtc_base:buffer",
"../rtc_base:checks",
- "../rtc_base:ignore_wundef",
"../rtc_base:logging",
"../rtc_base:safe_conversions",
"../system_wrappers:field_trial",
@@ -427,10 +425,7 @@ if (rtc_enable_protobuf) {
if (rtc_enable_protobuf) {
rtc_source_set("rtc_event_log2_proto_include") {
sources = [ "rtc_event_log/rtc_event_log2_proto_include.h" ]
- deps = [
- ":rtc_event_log2_proto",
- "../rtc_base:ignore_wundef",
- ]
+ deps = [ ":rtc_event_log2_proto" ]
}
}
@@ -487,10 +482,12 @@ rtc_library("fake_rtc_event_log") {
]
deps = [
+ "../api/environment",
"../api/rtc_event_log",
"../rtc_base:macromagic",
"../rtc_base/synchronization:mutex",
]
+ absl_deps = [ "//third_party/abseil-cpp/absl/base:nullability" ]
}
if (rtc_enable_protobuf) {
@@ -549,7 +546,6 @@ if (rtc_enable_protobuf) {
"../modules/rtp_rtcp:rtp_rtcp_format",
"../rtc_base:checks",
"../rtc_base:copy_on_write_buffer",
- "../rtc_base:ignore_wundef",
"../rtc_base:logging",
"../rtc_base:protobuf_utils",
"../rtc_base:rtc_numerics",
@@ -610,9 +606,10 @@ if (rtc_enable_protobuf) {
"../api:rtc_event_log_output_file",
"../api:rtp_headers",
"../api:rtp_parameters",
+ "../api/environment",
+ "../api/environment:environment_factory",
"../api/rtc_event_log",
"../api/rtc_event_log:rtc_event_log_factory",
- "../api/task_queue:default_task_queue_factory",
"../api/units:time_delta",
"../api/units:timestamp",
"../call",
@@ -627,6 +624,7 @@ if (rtc_enable_protobuf) {
"../rtc_base:timeutils",
"../system_wrappers",
"../system_wrappers:field_trial",
+ "../test:explicit_key_value_config",
"../test:field_trial",
"../test:fileutils",
"../test:test_support",
diff --git a/third_party/libwebrtc/logging/rtc_event_audio_gn/moz.build b/third_party/libwebrtc/logging/rtc_event_audio_gn/moz.build
index f4f41e48ae..806981ed68 100644
--- a/third_party/libwebrtc/logging/rtc_event_audio_gn/moz.build
+++ b/third_party/libwebrtc/logging/rtc_event_audio_gn/moz.build
@@ -199,7 +199,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -209,10 +208,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/logging/rtc_event_bwe_gn/moz.build b/third_party/libwebrtc/logging/rtc_event_bwe_gn/moz.build
index 72e02793f7..d1f1deac9a 100644
--- a/third_party/libwebrtc/logging/rtc_event_bwe_gn/moz.build
+++ b/third_party/libwebrtc/logging/rtc_event_bwe_gn/moz.build
@@ -200,7 +200,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -210,10 +209,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/logging/rtc_event_field_gn/moz.build b/third_party/libwebrtc/logging/rtc_event_field_gn/moz.build
index 6a37b25112..2c6740f28f 100644
--- a/third_party/libwebrtc/logging/rtc_event_field_gn/moz.build
+++ b/third_party/libwebrtc/logging/rtc_event_field_gn/moz.build
@@ -198,7 +198,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -208,10 +207,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder_legacy.cc b/third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder_legacy.cc
index 5619827246..2c1444af07 100644
--- a/third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder_legacy.cc
+++ b/third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder_legacy.cc
@@ -53,17 +53,14 @@
#include "modules/rtp_rtcp/source/rtcp_packet/sender_report.h"
#include "modules/rtp_rtcp/source/rtp_packet.h"
#include "rtc_base/checks.h"
-#include "rtc_base/ignore_wundef.h"
#include "rtc_base/logging.h"
// *.pb.h files are generated at build-time by the protobuf compiler.
-RTC_PUSH_IGNORING_WUNDEF()
#ifdef WEBRTC_ANDROID_PLATFORM_BUILD
#include "external/webrtc/webrtc/logging/rtc_event_log/rtc_event_log.pb.h"
#else
#include "logging/rtc_event_log/rtc_event_log.pb.h"
#endif
-RTC_POP_IGNORING_WUNDEF()
namespace webrtc {
diff --git a/third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder_new_format.cc b/third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder_new_format.cc
index 2c9e42e064..01bd89718d 100644
--- a/third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder_new_format.cc
+++ b/third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder_new_format.cc
@@ -61,18 +61,15 @@
#include "modules/rtp_rtcp/source/rtp_header_extensions.h"
#include "modules/rtp_rtcp/source/rtp_packet.h"
#include "rtc_base/checks.h"
-#include "rtc_base/ignore_wundef.h"
#include "rtc_base/logging.h"
#include "system_wrappers/include/field_trial.h"
// *.pb.h files are generated at build-time by the protobuf compiler.
-RTC_PUSH_IGNORING_WUNDEF()
#ifdef WEBRTC_ANDROID_PLATFORM_BUILD
#include "external/webrtc/webrtc/logging/rtc_event_log/rtc_event_log2.pb.h"
#else
#include "logging/rtc_event_log/rtc_event_log2.pb.h"
#endif
-RTC_POP_IGNORING_WUNDEF()
using webrtc_event_logging::ToUnsigned;
diff --git a/third_party/libwebrtc/logging/rtc_event_log/fake_rtc_event_log_factory.cc b/third_party/libwebrtc/logging/rtc_event_log/fake_rtc_event_log_factory.cc
index 47db40c9f4..bacc3cd1cb 100644
--- a/third_party/libwebrtc/logging/rtc_event_log/fake_rtc_event_log_factory.cc
+++ b/third_party/libwebrtc/logging/rtc_event_log/fake_rtc_event_log_factory.cc
@@ -17,17 +17,11 @@
namespace webrtc {
-std::unique_ptr<RtcEventLog> FakeRtcEventLogFactory::Create(
- RtcEventLog::EncodingType /*encoding_type*/) const {
+absl::Nonnull<std::unique_ptr<RtcEventLog>> FakeRtcEventLogFactory::Create(
+ const Environment& /*env*/) const {
auto fake_event_log = std::make_unique<FakeRtcEventLog>();
- const_cast<FakeRtcEventLogFactory*>(this)->last_log_created_ =
- fake_event_log.get();
+ const_cast<FakeRtcEventLog*&>(last_log_created_) = fake_event_log.get();
return fake_event_log;
}
-std::unique_ptr<RtcEventLog> FakeRtcEventLogFactory::CreateRtcEventLog(
- RtcEventLog::EncodingType encoding_type) {
- return Create(encoding_type);
-}
-
} // namespace webrtc
diff --git a/third_party/libwebrtc/logging/rtc_event_log/fake_rtc_event_log_factory.h b/third_party/libwebrtc/logging/rtc_event_log/fake_rtc_event_log_factory.h
index c7ff33dee4..0d6d076038 100644
--- a/third_party/libwebrtc/logging/rtc_event_log/fake_rtc_event_log_factory.h
+++ b/third_party/libwebrtc/logging/rtc_event_log/fake_rtc_event_log_factory.h
@@ -13,6 +13,8 @@
#include <memory>
+#include "absl/base/nullability.h"
+#include "api/environment/environment.h"
#include "api/rtc_event_log/rtc_event_log_factory_interface.h"
#include "logging/rtc_event_log/fake_rtc_event_log.h"
@@ -23,16 +25,13 @@ class FakeRtcEventLogFactory : public RtcEventLogFactoryInterface {
FakeRtcEventLogFactory() = default;
~FakeRtcEventLogFactory() override = default;
- std::unique_ptr<RtcEventLog> Create(
- RtcEventLog::EncodingType encoding_type) const override;
+ absl::Nonnull<std::unique_ptr<RtcEventLog>> Create(
+ const Environment& env) const override;
- std::unique_ptr<RtcEventLog> CreateRtcEventLog(
- RtcEventLog::EncodingType encoding_type) override;
-
- webrtc::FakeRtcEventLog* last_log_created() { return last_log_created_; }
+ FakeRtcEventLog* last_log_created() { return last_log_created_; }
private:
- webrtc::FakeRtcEventLog* last_log_created_;
+ FakeRtcEventLog* last_log_created_ = nullptr;
};
} // namespace webrtc
diff --git a/third_party/libwebrtc/logging/rtc_event_log/rtc_event_log2_proto_include.h b/third_party/libwebrtc/logging/rtc_event_log/rtc_event_log2_proto_include.h
index 3e43103897..43b914fd0c 100644
--- a/third_party/libwebrtc/logging/rtc_event_log/rtc_event_log2_proto_include.h
+++ b/third_party/libwebrtc/logging/rtc_event_log/rtc_event_log2_proto_include.h
@@ -11,15 +11,11 @@
#ifndef LOGGING_RTC_EVENT_LOG_RTC_EVENT_LOG2_PROTO_INCLUDE_H_
#define LOGGING_RTC_EVENT_LOG_RTC_EVENT_LOG2_PROTO_INCLUDE_H_
-#include "rtc_base/ignore_wundef.h"
-
// *.pb.h files are generated at build-time by the protobuf compiler.
-RTC_PUSH_IGNORING_WUNDEF()
#ifdef WEBRTC_ANDROID_PLATFORM_BUILD
#include "external/webrtc/webrtc/logging/rtc_event_log/rtc_event_log2.pb.h"
#else
#include "logging/rtc_event_log/rtc_event_log2.pb.h"
#endif
-RTC_POP_IGNORING_WUNDEF()
#endif // LOGGING_RTC_EVENT_LOG_RTC_EVENT_LOG2_PROTO_INCLUDE_H_
diff --git a/third_party/libwebrtc/logging/rtc_event_log/rtc_event_log_parser.h b/third_party/libwebrtc/logging/rtc_event_log/rtc_event_log_parser.h
index 9ad96274d3..def9f0ab35 100644
--- a/third_party/libwebrtc/logging/rtc_event_log/rtc_event_log_parser.h
+++ b/third_party/libwebrtc/logging/rtc_event_log/rtc_event_log_parser.h
@@ -54,10 +54,8 @@
#include "logging/rtc_event_log/events/rtc_event_video_send_stream_config.h"
#include "modules/rtp_rtcp/include/rtp_header_extension_map.h"
#include "modules/rtp_rtcp/source/rtcp_packet/common_header.h"
-#include "rtc_base/ignore_wundef.h"
// Files generated at build-time by the protobuf compiler.
-RTC_PUSH_IGNORING_WUNDEF()
#ifdef WEBRTC_ANDROID_PLATFORM_BUILD
#include "external/webrtc/webrtc/logging/rtc_event_log/rtc_event_log.pb.h"
#include "external/webrtc/webrtc/logging/rtc_event_log/rtc_event_log2.pb.h"
@@ -65,7 +63,6 @@ RTC_PUSH_IGNORING_WUNDEF()
#include "logging/rtc_event_log/rtc_event_log.pb.h"
#include "logging/rtc_event_log/rtc_event_log2.pb.h"
#endif
-RTC_POP_IGNORING_WUNDEF()
namespace webrtc {
diff --git a/third_party/libwebrtc/logging/rtc_event_log/rtc_event_log_unittest.cc b/third_party/libwebrtc/logging/rtc_event_log/rtc_event_log_unittest.cc
index 3730a080dd..a3005729ab 100644
--- a/third_party/libwebrtc/logging/rtc_event_log/rtc_event_log_unittest.cc
+++ b/third_party/libwebrtc/logging/rtc_event_log/rtc_event_log_unittest.cc
@@ -19,8 +19,9 @@
#include <utility>
#include <vector>
+#include "api/environment/environment.h"
+#include "api/environment/environment_factory.h"
#include "api/rtc_event_log/rtc_event_log_factory.h"
-#include "api/task_queue/default_task_queue_factory.h"
#include "logging/rtc_event_log/events/rtc_event_audio_network_adaptation.h"
#include "logging/rtc_event_log/events/rtc_event_audio_playout.h"
#include "logging/rtc_event_log/events/rtc_event_audio_receive_stream_config.h"
@@ -50,6 +51,7 @@
#include "rtc_base/checks.h"
#include "rtc_base/fake_clock.h"
#include "rtc_base/random.h"
+#include "test/explicit_key_value_config.h"
#include "test/gtest.h"
#include "test/logging/memory_log_writer.h"
#include "test/testsupport/file_utils.h"
@@ -58,6 +60,8 @@ namespace webrtc {
namespace {
+using test::ExplicitKeyValueConfig;
+
struct EventCounts {
size_t audio_send_streams = 0;
size_t audio_recv_streams = 0;
@@ -105,6 +109,21 @@ struct EventCounts {
}
};
+std::unique_ptr<FieldTrialsView> CreateFieldTrialsFor(
+ RtcEventLog::EncodingType encoding_type) {
+ switch (encoding_type) {
+ case RtcEventLog::EncodingType::Legacy:
+ return std::make_unique<ExplicitKeyValueConfig>(
+ "WebRTC-RtcEventLogNewFormat/Disabled/");
+ case RtcEventLog::EncodingType::NewFormat:
+ return std::make_unique<ExplicitKeyValueConfig>(
+ "WebRTC-RtcEventLogNewFormat/Enabled/");
+ case RtcEventLog::EncodingType::ProtoFree:
+ RTC_CHECK(false);
+ return nullptr;
+ }
+}
+
class RtcEventLogSession
: public ::testing::TestWithParam<
std::tuple<uint64_t, int64_t, RtcEventLog::EncodingType>> {
@@ -336,14 +355,13 @@ void RtcEventLogSession::WriteVideoSendConfigs(size_t video_send_streams,
void RtcEventLogSession::WriteLog(EventCounts count,
size_t num_events_before_start) {
- // TODO(terelius): Allow test to run with either a real or a fake clock_.
- // Maybe always use the ScopedFakeClock, but conditionally SleepMs()?
+ // TODO(terelius): Allow test to run with either a real or a fake clock_
+ // e.g. by using clock and task_queue_factory from TimeController
+ // when RtcEventLogImpl switches to use injected clock from the environment.
- auto task_queue_factory = CreateDefaultTaskQueueFactory();
- RtcEventLogFactory rtc_event_log_factory(task_queue_factory.get());
// The log will be flushed to output when the event_log goes out of scope.
- std::unique_ptr<RtcEventLog> event_log =
- rtc_event_log_factory.CreateRtcEventLog(encoding_type_);
+ std::unique_ptr<RtcEventLog> event_log = RtcEventLogFactory().Create(
+ CreateEnvironment(CreateFieldTrialsFor(encoding_type_)));
// We can't send or receive packets without configured streams.
RTC_CHECK_GE(count.video_recv_streams, 1);
@@ -934,12 +952,9 @@ TEST_P(RtcEventLogCircularBufferTest, KeepsMostRecentEvents) {
int64_t start_time_us, utc_start_time_us, stop_time_us;
{
- auto task_queue_factory = CreateDefaultTaskQueueFactory();
- RtcEventLogFactory rtc_event_log_factory(task_queue_factory.get());
- // When `log` goes out of scope, the contents are flushed
- // to the output.
- std::unique_ptr<RtcEventLog> log =
- rtc_event_log_factory.CreateRtcEventLog(encoding_type_);
+ // When `log` goes out of scope, the contents are flushed to the output.
+ std::unique_ptr<RtcEventLog> log = RtcEventLogFactory().Create(
+ CreateEnvironment(CreateFieldTrialsFor(encoding_type_)));
for (size_t i = 0; i < kNumEvents; i++) {
// The purpose of the test is to verify that the log can handle
diff --git a/third_party/libwebrtc/logging/rtc_event_log_parse_status_gn/moz.build b/third_party/libwebrtc/logging/rtc_event_log_parse_status_gn/moz.build
index 5ad6834dff..2936d8ef48 100644
--- a/third_party/libwebrtc/logging/rtc_event_log_parse_status_gn/moz.build
+++ b/third_party/libwebrtc/logging/rtc_event_log_parse_status_gn/moz.build
@@ -180,16 +180,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/logging/rtc_event_number_encodings_gn/moz.build b/third_party/libwebrtc/logging/rtc_event_number_encodings_gn/moz.build
index af4c0bf656..6ecf9077cf 100644
--- a/third_party/libwebrtc/logging/rtc_event_number_encodings_gn/moz.build
+++ b/third_party/libwebrtc/logging/rtc_event_number_encodings_gn/moz.build
@@ -190,7 +190,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -200,10 +199,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/logging/rtc_event_pacing_gn/moz.build b/third_party/libwebrtc/logging/rtc_event_pacing_gn/moz.build
index abf04bb09f..f0e8d5d4e8 100644
--- a/third_party/libwebrtc/logging/rtc_event_pacing_gn/moz.build
+++ b/third_party/libwebrtc/logging/rtc_event_pacing_gn/moz.build
@@ -195,7 +195,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -205,10 +204,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/logging/rtc_event_rtp_rtcp_gn/moz.build b/third_party/libwebrtc/logging/rtc_event_rtp_rtcp_gn/moz.build
index b003d287bd..082924c2d0 100644
--- a/third_party/libwebrtc/logging/rtc_event_rtp_rtcp_gn/moz.build
+++ b/third_party/libwebrtc/logging/rtc_event_rtp_rtcp_gn/moz.build
@@ -202,7 +202,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -212,10 +211,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/logging/rtc_event_video_gn/moz.build b/third_party/libwebrtc/logging/rtc_event_video_gn/moz.build
index 264fc2cf4b..6e74aef635 100644
--- a/third_party/libwebrtc/logging/rtc_event_video_gn/moz.build
+++ b/third_party/libwebrtc/logging/rtc_event_video_gn/moz.build
@@ -196,7 +196,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -206,10 +205,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/logging/rtc_stream_config_gn/moz.build b/third_party/libwebrtc/logging/rtc_stream_config_gn/moz.build
index eb3342a926..935bf8bbf9 100644
--- a/third_party/libwebrtc/logging/rtc_stream_config_gn/moz.build
+++ b/third_party/libwebrtc/logging/rtc_stream_config_gn/moz.build
@@ -195,7 +195,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -205,10 +204,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/media/BUILD.gn b/third_party/libwebrtc/media/BUILD.gn
index 97ad4a889a..055bf75a19 100644
--- a/third_party/libwebrtc/media/BUILD.gn
+++ b/third_party/libwebrtc/media/BUILD.gn
@@ -262,7 +262,6 @@ rtc_library("codec") {
]
deps = [
":media_constants",
- "../api:field_trials_view",
"../api:rtp_parameters",
"../api/audio_codecs:audio_codecs_api",
"../api/video_codecs:video_codecs_api",
@@ -546,7 +545,6 @@ rtc_library("rtc_audio_video") {
"../rtc_base:copy_on_write_buffer",
"../rtc_base:dscp",
"../rtc_base:event_tracer",
- "../rtc_base:ignore_wundef",
"../rtc_base:logging",
"../rtc_base:macromagic",
"../rtc_base:network_route",
@@ -611,33 +609,6 @@ rtc_library("rtc_audio_video") {
}
}
-# Heavy but optional helper for unittests and webrtc users who prefer to use
-# defaults factories or do not worry about extra dependencies and binary size.
-rtc_library("rtc_media_engine_defaults") {
- visibility = [ "*" ]
- allow_poison = [
- "audio_codecs",
- "default_task_queue",
- "software_video_codecs",
- ]
- sources = [
- "engine/webrtc_media_engine_defaults.cc",
- "engine/webrtc_media_engine_defaults.h",
- ]
- deps = [
- ":rtc_audio_video",
- "../api/audio_codecs:builtin_audio_decoder_factory",
- "../api/audio_codecs:builtin_audio_encoder_factory",
- "../api/task_queue:default_task_queue_factory",
- "../api/video:builtin_video_bitrate_allocator_factory",
- "../api/video_codecs:builtin_video_decoder_factory",
- "../api/video_codecs:builtin_video_encoder_factory",
- "../modules/audio_processing:api",
- "../rtc_base:checks",
- "../rtc_base/system:rtc_export",
- ]
-}
-
rtc_source_set("rtc_data_sctp_transport_internal") {
sources = [ "sctp/sctp_transport_internal.h" ]
deps = [
@@ -840,7 +811,6 @@ if (rtc_include_tests) {
":rtc_internal_video_codecs",
":rtc_media",
":rtc_media_base",
- ":rtc_media_engine_defaults",
":rtc_media_tests_utils",
":rtc_sdp_video_format_utils",
":rtc_simulcast_encoder_adapter",
@@ -860,6 +830,8 @@ if (rtc_include_tests) {
"../api:simulcast_test_fixture_api",
"../api/audio_codecs:builtin_audio_decoder_factory",
"../api/audio_codecs:builtin_audio_encoder_factory",
+ "../api/environment",
+ "../api/environment:environment_factory",
"../api/rtc_event_log",
"../api/task_queue",
"../api/task_queue:default_task_queue_factory",
diff --git a/third_party/libwebrtc/media/base/codec.cc b/third_party/libwebrtc/media/base/codec.cc
index b819707702..c4e1c6f1f3 100644
--- a/third_party/libwebrtc/media/base/codec.cc
+++ b/third_party/libwebrtc/media/base/codec.cc
@@ -158,8 +158,7 @@ bool Codec::operator==(const Codec& c) const {
: (packetization == c.packetization));
}
-bool Codec::Matches(const Codec& codec,
- const webrtc::FieldTrialsView* field_trials) const {
+bool Codec::Matches(const Codec& codec) const {
// Match the codec id/name based on the typical static/dynamic name rules.
// Matching is case-insensitive.
diff --git a/third_party/libwebrtc/media/base/codec.h b/third_party/libwebrtc/media/base/codec.h
index 228acad07a..bd4239b251 100644
--- a/third_party/libwebrtc/media/base/codec.h
+++ b/third_party/libwebrtc/media/base/codec.h
@@ -20,7 +20,6 @@
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
#include "api/audio_codecs/audio_format.h"
-#include "api/field_trials_view.h"
#include "api/rtp_parameters.h"
#include "api/video_codecs/sdp_video_format.h"
#include "media/base/media_constants.h"
@@ -112,8 +111,7 @@ struct RTC_EXPORT Codec {
// Indicates if this codec is compatible with the specified codec by
// checking the assigned id and profile values for the relevant video codecs.
// H264 levels are not compared.
- bool Matches(const Codec& codec,
- const webrtc::FieldTrialsView* field_trials = nullptr) const;
+ bool Matches(const Codec& codec) const;
bool MatchesRtpCodec(const webrtc::RtpCodec& capability) const;
// Find the parameter for `name` and write the value to `out`.
diff --git a/third_party/libwebrtc/media/codec_gn/moz.build b/third_party/libwebrtc/media/codec_gn/moz.build
index a6fa3b4063..b5ebd454d3 100644
--- a/third_party/libwebrtc/media/codec_gn/moz.build
+++ b/third_party/libwebrtc/media/codec_gn/moz.build
@@ -195,7 +195,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -205,10 +204,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/media/engine/null_webrtc_video_engine_unittest.cc b/third_party/libwebrtc/media/engine/null_webrtc_video_engine_unittest.cc
index 31c442d53d..9515d44be9 100644
--- a/third_party/libwebrtc/media/engine/null_webrtc_video_engine_unittest.cc
+++ b/third_party/libwebrtc/media/engine/null_webrtc_video_engine_unittest.cc
@@ -37,7 +37,7 @@ TEST(NullWebRtcVideoEngineTest, CheckInterface) {
task_queue_factory.get(), adm.get(),
webrtc::MockAudioEncoderFactory::CreateUnusedFactory(),
webrtc::MockAudioDecoderFactory::CreateUnusedFactory(), nullptr,
- webrtc::AudioProcessingBuilder().Create(), nullptr, nullptr, trials);
+ webrtc::AudioProcessingBuilder().Create(), nullptr, trials);
CompositeMediaEngine engine(std::move(audio_engine),
std::make_unique<NullWebRtcVideoEngine>());
diff --git a/third_party/libwebrtc/media/engine/webrtc_media_engine.cc b/third_party/libwebrtc/media/engine/webrtc_media_engine.cc
index 99d7dd2704..463ed29109 100644
--- a/third_party/libwebrtc/media/engine/webrtc_media_engine.cc
+++ b/third_party/libwebrtc/media/engine/webrtc_media_engine.cc
@@ -46,7 +46,6 @@ std::unique_ptr<MediaEngineInterface> CreateMediaEngine(
std::move(dependencies.audio_decoder_factory),
std::move(dependencies.audio_mixer),
std::move(dependencies.audio_processing),
- dependencies.audio_frame_processor,
std::move(dependencies.owned_audio_frame_processor), trials);
#ifdef HAVE_WEBRTC_VIDEO
auto video_engine = std::make_unique<WebRtcVideoEngine>(
diff --git a/third_party/libwebrtc/media/engine/webrtc_media_engine.h b/third_party/libwebrtc/media/engine/webrtc_media_engine.h
index 0f6dce35b5..863db9f278 100644
--- a/third_party/libwebrtc/media/engine/webrtc_media_engine.h
+++ b/third_party/libwebrtc/media/engine/webrtc_media_engine.h
@@ -49,9 +49,6 @@ struct MediaEngineDependencies {
rtc::scoped_refptr<webrtc::AudioDecoderFactory> audio_decoder_factory;
rtc::scoped_refptr<webrtc::AudioMixer> audio_mixer;
rtc::scoped_refptr<webrtc::AudioProcessing> audio_processing;
- // TODO(bugs.webrtc.org/15111):
- // Remove the raw AudioFrameProcessor pointer in the follow-up.
- webrtc::AudioFrameProcessor* audio_frame_processor = nullptr;
std::unique_ptr<webrtc::AudioFrameProcessor> owned_audio_frame_processor;
std::unique_ptr<webrtc::VideoEncoderFactory> video_encoder_factory;
@@ -63,8 +60,9 @@ struct MediaEngineDependencies {
// CreateMediaEngine may be called on any thread, though the engine is
// only expected to be used on one thread, internally called the "worker
// thread". This is the thread Init must be called on.
-RTC_EXPORT std::unique_ptr<MediaEngineInterface> CreateMediaEngine(
- MediaEngineDependencies dependencies);
+[[deprecated("bugs.webrtc.org/15574")]] //
+RTC_EXPORT std::unique_ptr<MediaEngineInterface>
+CreateMediaEngine(MediaEngineDependencies dependencies);
// Verify that extension IDs are within 1-byte extension range and are not
// overlapping, and that they form a legal change from previously registerd
diff --git a/third_party/libwebrtc/media/engine/webrtc_media_engine_defaults.cc b/third_party/libwebrtc/media/engine/webrtc_media_engine_defaults.cc
deleted file mode 100644
index 1660873e8b..0000000000
--- a/third_party/libwebrtc/media/engine/webrtc_media_engine_defaults.cc
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
- *
- * Use of this source code is governed by a BSD-style license
- * that can be found in the LICENSE file in the root of the source
- * tree. An additional intellectual property rights grant can be found
- * in the file PATENTS. All contributing project authors may
- * be found in the AUTHORS file in the root of the source tree.
- */
-#include "media/engine/webrtc_media_engine_defaults.h"
-
-#include "api/audio_codecs/builtin_audio_decoder_factory.h"
-#include "api/audio_codecs/builtin_audio_encoder_factory.h"
-#include "api/task_queue/default_task_queue_factory.h"
-#include "api/video/builtin_video_bitrate_allocator_factory.h"
-#include "api/video_codecs/builtin_video_decoder_factory.h"
-#include "api/video_codecs/builtin_video_encoder_factory.h"
-#include "modules/audio_processing/include/audio_processing.h"
-#include "rtc_base/checks.h"
-
-namespace webrtc {
-
-void SetMediaEngineDefaults(cricket::MediaEngineDependencies* deps) {
- RTC_DCHECK(deps);
- if (deps->task_queue_factory == nullptr) {
- static TaskQueueFactory* const task_queue_factory =
- CreateDefaultTaskQueueFactory().release();
- deps->task_queue_factory = task_queue_factory;
- }
- if (deps->audio_encoder_factory == nullptr)
- deps->audio_encoder_factory = CreateBuiltinAudioEncoderFactory();
- if (deps->audio_decoder_factory == nullptr)
- deps->audio_decoder_factory = CreateBuiltinAudioDecoderFactory();
- if (deps->audio_processing == nullptr)
- deps->audio_processing = AudioProcessingBuilder().Create();
-
- if (deps->video_encoder_factory == nullptr)
- deps->video_encoder_factory = CreateBuiltinVideoEncoderFactory();
- if (deps->video_decoder_factory == nullptr)
- deps->video_decoder_factory = CreateBuiltinVideoDecoderFactory();
-}
-
-} // namespace webrtc
diff --git a/third_party/libwebrtc/media/engine/webrtc_media_engine_defaults.h b/third_party/libwebrtc/media/engine/webrtc_media_engine_defaults.h
deleted file mode 100644
index 16b1d462e3..0000000000
--- a/third_party/libwebrtc/media/engine/webrtc_media_engine_defaults.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
- *
- * Use of this source code is governed by a BSD-style license
- * that can be found in the LICENSE file in the root of the source
- * tree. An additional intellectual property rights grant can be found
- * in the file PATENTS. All contributing project authors may
- * be found in the AUTHORS file in the root of the source tree.
- */
-
-#ifndef MEDIA_ENGINE_WEBRTC_MEDIA_ENGINE_DEFAULTS_H_
-#define MEDIA_ENGINE_WEBRTC_MEDIA_ENGINE_DEFAULTS_H_
-
-#include "media/engine/webrtc_media_engine.h"
-#include "rtc_base/system/rtc_export.h"
-
-namespace webrtc {
-
-// Sets required but null dependencies with default factories.
-RTC_EXPORT void SetMediaEngineDefaults(cricket::MediaEngineDependencies* deps);
-
-} // namespace webrtc
-
-#endif // MEDIA_ENGINE_WEBRTC_MEDIA_ENGINE_DEFAULTS_H_
diff --git a/third_party/libwebrtc/media/engine/webrtc_media_engine_unittest.cc b/third_party/libwebrtc/media/engine/webrtc_media_engine_unittest.cc
index 4615f03deb..40cad75701 100644
--- a/third_party/libwebrtc/media/engine/webrtc_media_engine_unittest.cc
+++ b/third_party/libwebrtc/media/engine/webrtc_media_engine_unittest.cc
@@ -14,7 +14,6 @@
#include <string>
#include <utility>
-#include "media/engine/webrtc_media_engine_defaults.h"
#include "test/gtest.h"
#include "test/scoped_key_value_config.h"
@@ -322,16 +321,4 @@ TEST(WebRtcMediaEngineTest, FilterRtpExtensionsRemoveRedundantBwe3) {
EXPECT_EQ(RtpExtension::kTimestampOffsetUri, filtered[0].uri);
}
-TEST(WebRtcMediaEngineTest, Create) {
- MediaEngineDependencies deps;
- webrtc::SetMediaEngineDefaults(&deps);
- webrtc::test::ScopedKeyValueConfig trials;
- deps.trials = &trials;
-
- std::unique_ptr<MediaEngineInterface> engine =
- CreateMediaEngine(std::move(deps));
-
- EXPECT_TRUE(engine);
-}
-
} // namespace cricket
diff --git a/third_party/libwebrtc/media/engine/webrtc_video_engine_unittest.cc b/third_party/libwebrtc/media/engine/webrtc_video_engine_unittest.cc
index e8b7ee4b2d..f5736679be 100644
--- a/third_party/libwebrtc/media/engine/webrtc_video_engine_unittest.cc
+++ b/third_party/libwebrtc/media/engine/webrtc_video_engine_unittest.cc
@@ -20,9 +20,9 @@
#include "absl/algorithm/container.h"
#include "absl/strings/match.h"
-#include "api/rtc_event_log/rtc_event_log.h"
+#include "api/environment/environment.h"
+#include "api/environment/environment_factory.h"
#include "api/rtp_parameters.h"
-#include "api/task_queue/default_task_queue_factory.h"
#include "api/test/mock_encoder_selector.h"
#include "api/test/mock_video_bitrate_allocator.h"
#include "api/test/mock_video_bitrate_allocator_factory.h"
@@ -101,6 +101,8 @@ using ::testing::WithArg;
using ::webrtc::BitrateConstraints;
using ::webrtc::Call;
using ::webrtc::CallConfig;
+using ::webrtc::CreateEnvironment;
+using ::webrtc::Environment;
using ::webrtc::kDefaultScalabilityModeStr;
using ::webrtc::RtpExtension;
using ::webrtc::RtpPacket;
@@ -355,13 +357,10 @@ class WebRtcVideoEngineTest : public ::testing::Test {
explicit WebRtcVideoEngineTest(const std::string& field_trials)
: field_trials_(field_trials),
time_controller_(webrtc::Timestamp::Millis(4711)),
- task_queue_factory_(time_controller_.CreateTaskQueueFactory()),
- call_(Call::Create([&] {
- CallConfig call_config(&event_log_);
- call_config.task_queue_factory = task_queue_factory_.get();
- call_config.trials = &field_trials_;
- return call_config;
- }())),
+ env_(CreateEnvironment(&field_trials_,
+ time_controller_.CreateTaskQueueFactory(),
+ time_controller_.GetClock())),
+ call_(Call::Create(CallConfig(env_))),
encoder_factory_(new cricket::FakeWebRtcVideoEncoderFactory),
decoder_factory_(new cricket::FakeWebRtcVideoDecoderFactory),
video_bitrate_allocator_factory_(
@@ -398,8 +397,7 @@ class WebRtcVideoEngineTest : public ::testing::Test {
webrtc::test::ScopedKeyValueConfig field_trials_;
webrtc::GlobalSimulatedTimeController time_controller_;
- webrtc::RtcEventLogNull event_log_;
- std::unique_ptr<webrtc::TaskQueueFactory> task_queue_factory_;
+ Environment env_;
// Used in WebRtcVideoEngineVoiceTest, but defined here so it's properly
// initialized when the constructor is called.
std::unique_ptr<Call> call_;
@@ -1479,14 +1477,10 @@ TEST(WebRtcVideoEngineNewVideoCodecFactoryTest, Vp8) {
EXPECT_CALL(*decoder_factory, CreateVideoDecoder(format)).Times(0);
// Create a call.
- webrtc::RtcEventLogNull event_log;
webrtc::GlobalSimulatedTimeController time_controller(
webrtc::Timestamp::Millis(4711));
- auto task_queue_factory = time_controller.CreateTaskQueueFactory();
- CallConfig call_config(&event_log);
- webrtc::FieldTrialBasedConfig field_trials;
- call_config.trials = &field_trials;
- call_config.task_queue_factory = task_queue_factory.get();
+ CallConfig call_config(CreateEnvironment(
+ time_controller.CreateTaskQueueFactory(), time_controller.GetClock()));
const std::unique_ptr<Call> call = Call::Create(call_config);
// Create send channel.
@@ -1615,18 +1609,11 @@ TEST_F(WebRtcVideoEngineTest, SetVideoRtxEnabled) {
class WebRtcVideoChannelEncodedFrameCallbackTest : public ::testing::Test {
protected:
- CallConfig GetCallConfig(webrtc::RtcEventLogNull* event_log,
- webrtc::TaskQueueFactory* task_queue_factory) {
- CallConfig call_config(event_log);
- call_config.task_queue_factory = task_queue_factory;
- call_config.trials = &field_trials_;
- return call_config;
- }
-
WebRtcVideoChannelEncodedFrameCallbackTest()
- : task_queue_factory_(time_controller_.CreateTaskQueueFactory()),
- call_(Call::Create(
- GetCallConfig(&event_log_, task_queue_factory_.get()))),
+ : env_(CreateEnvironment(&field_trials_,
+ time_controller_.CreateTaskQueueFactory(),
+ time_controller_.GetClock())),
+ call_(Call::Create(CallConfig(env_))),
video_bitrate_allocator_factory_(
webrtc::CreateBuiltinVideoBitrateAllocatorFactory()),
engine_(
@@ -1676,8 +1663,7 @@ class WebRtcVideoChannelEncodedFrameCallbackTest : public ::testing::Test {
webrtc::GlobalSimulatedTimeController time_controller_{
Timestamp::Seconds(1000)};
webrtc::test::ScopedKeyValueConfig field_trials_;
- webrtc::RtcEventLogNull event_log_;
- std::unique_ptr<webrtc::TaskQueueFactory> task_queue_factory_;
+ Environment env_;
std::unique_ptr<Call> call_;
std::unique_ptr<webrtc::VideoBitrateAllocatorFactory>
video_bitrate_allocator_factory_;
@@ -1796,7 +1782,9 @@ TEST_F(WebRtcVideoChannelEncodedFrameCallbackTest, DoesNotDecodeWhenDisabled) {
class WebRtcVideoChannelBaseTest : public ::testing::Test {
protected:
WebRtcVideoChannelBaseTest()
- : task_queue_factory_(time_controller_.CreateTaskQueueFactory()),
+ : env_(CreateEnvironment(&field_trials_,
+ time_controller_.CreateTaskQueueFactory(),
+ time_controller_.GetClock())),
video_bitrate_allocator_factory_(
webrtc::CreateBuiltinVideoBitrateAllocatorFactory()),
engine_(std::make_unique<webrtc::VideoEncoderFactoryTemplate<
@@ -1814,10 +1802,7 @@ class WebRtcVideoChannelBaseTest : public ::testing::Test {
void SetUp() override {
// One testcase calls SetUp in a loop, only create call_ once.
if (!call_) {
- CallConfig call_config(&event_log_);
- call_config.task_queue_factory = task_queue_factory_.get();
- call_config.trials = &field_trials_;
- call_ = Call::Create(call_config);
+ call_ = Call::Create(CallConfig(env_));
}
cricket::MediaConfig media_config;
@@ -2016,10 +2001,9 @@ class WebRtcVideoChannelBaseTest : public ::testing::Test {
webrtc::GlobalSimulatedTimeController time_controller_{
Timestamp::Seconds(1000)};
- webrtc::RtcEventLogNull event_log_;
webrtc::test::ScopedKeyValueConfig field_trials_;
std::unique_ptr<webrtc::test::ScopedKeyValueConfig> override_field_trials_;
- std::unique_ptr<webrtc::TaskQueueFactory> task_queue_factory_;
+ Environment env_;
std::unique_ptr<Call> call_;
std::unique_ptr<webrtc::VideoBitrateAllocatorFactory>
video_bitrate_allocator_factory_;
@@ -4378,7 +4362,7 @@ TEST_F(WebRtcVideoChannelTest, SetDefaultSendCodecs) {
absl::optional<VideoCodec> codec = send_channel_->GetSendCodec();
ASSERT_TRUE(codec);
- EXPECT_TRUE(codec->Matches(engine_.send_codecs()[0], &field_trials_));
+ EXPECT_TRUE(codec->Matches(engine_.send_codecs()[0]));
// Using a RTX setup to verify that the default RTX payload type is good.
const std::vector<uint32_t> ssrcs = MAKE_VECTOR(kSsrcs1);
@@ -9863,7 +9847,6 @@ class WebRtcVideoChannelSimulcastTest : public ::testing::Test {
}
webrtc::test::ScopedKeyValueConfig field_trials_;
- webrtc::RtcEventLogNull event_log_;
FakeCall fake_call_;
cricket::FakeWebRtcVideoEncoderFactory* encoder_factory_;
cricket::FakeWebRtcVideoDecoderFactory* decoder_factory_;
diff --git a/third_party/libwebrtc/media/engine/webrtc_voice_engine.cc b/third_party/libwebrtc/media/engine/webrtc_voice_engine.cc
index adf8b5c51d..adf662074d 100644
--- a/third_party/libwebrtc/media/engine/webrtc_voice_engine.cc
+++ b/third_party/libwebrtc/media/engine/webrtc_voice_engine.cc
@@ -66,7 +66,6 @@
#include "rtc_base/checks.h"
#include "rtc_base/dscp.h"
#include "rtc_base/experiments/struct_parameters_parser.h"
-#include "rtc_base/ignore_wundef.h"
#include "rtc_base/logging.h"
#include "rtc_base/race_checker.h"
#include "rtc_base/string_encode.h"
@@ -79,13 +78,12 @@
#include "system_wrappers/include/metrics.h"
#if WEBRTC_ENABLE_PROTOBUF
-RTC_PUSH_IGNORING_WUNDEF()
#ifdef WEBRTC_ANDROID_PLATFORM_BUILD
#include "external/webrtc/webrtc/modules/audio_coding/audio_network_adaptor/config.pb.h"
#else
#include "modules/audio_coding/audio_network_adaptor/config.pb.h"
#endif
-RTC_POP_IGNORING_WUNDEF()
+
#endif
namespace cricket {
@@ -147,12 +145,10 @@ bool IsCodec(const AudioCodec& codec, const char* ref_name) {
return absl::EqualsIgnoreCase(codec.name, ref_name);
}
-absl::optional<AudioCodec> FindCodec(
- const std::vector<AudioCodec>& codecs,
- const AudioCodec& codec,
- const webrtc::FieldTrialsView* field_trials) {
+absl::optional<AudioCodec> FindCodec(const std::vector<AudioCodec>& codecs,
+ const AudioCodec& codec) {
for (const AudioCodec& c : codecs) {
- if (c.Matches(codec, field_trials)) {
+ if (c.Matches(codec)) {
return c;
}
}
@@ -344,10 +340,7 @@ WebRtcVoiceEngine::WebRtcVoiceEngine(
const rtc::scoped_refptr<webrtc::AudioDecoderFactory>& decoder_factory,
rtc::scoped_refptr<webrtc::AudioMixer> audio_mixer,
rtc::scoped_refptr<webrtc::AudioProcessing> audio_processing,
- // TODO(bugs.webrtc.org/15111):
- // Remove the raw AudioFrameProcessor pointer in the follow-up.
- webrtc::AudioFrameProcessor* audio_frame_processor,
- std::unique_ptr<webrtc::AudioFrameProcessor> owned_audio_frame_processor,
+ std::unique_ptr<webrtc::AudioFrameProcessor> audio_frame_processor,
const webrtc::FieldTrialsView& trials)
: task_queue_factory_(task_queue_factory),
adm_(adm),
@@ -355,8 +348,7 @@ WebRtcVoiceEngine::WebRtcVoiceEngine(
decoder_factory_(decoder_factory),
audio_mixer_(audio_mixer),
apm_(audio_processing),
- audio_frame_processor_(audio_frame_processor),
- owned_audio_frame_processor_(std::move(owned_audio_frame_processor)),
+ audio_frame_processor_(std::move(audio_frame_processor)),
minimized_remsampling_on_mobile_trial_enabled_(
IsEnabled(trials, "WebRTC-Audio-MinimizeResamplingOnMobile")) {
RTC_LOG(LS_INFO) << "WebRtcVoiceEngine::WebRtcVoiceEngine";
@@ -425,11 +417,7 @@ void WebRtcVoiceEngine::Init() {
if (audio_frame_processor_) {
config.async_audio_processing_factory =
rtc::make_ref_counted<webrtc::AsyncAudioProcessing::Factory>(
- *audio_frame_processor_, *task_queue_factory_);
- } else if (owned_audio_frame_processor_) {
- config.async_audio_processing_factory =
- rtc::make_ref_counted<webrtc::AsyncAudioProcessing::Factory>(
- std::move(owned_audio_frame_processor_), *task_queue_factory_);
+ std::move(audio_frame_processor_), *task_queue_factory_);
}
audio_state_ = webrtc::AudioState::Create(config);
}
@@ -2151,8 +2139,7 @@ bool WebRtcVoiceReceiveChannel::SetRecvCodecs(
for (const AudioCodec& codec : codecs) {
// Log a warning if a codec's payload type is changing. This used to be
// treated as an error. It's abnormal, but not really illegal.
- absl::optional<AudioCodec> old_codec =
- FindCodec(recv_codecs_, codec, &call_->trials());
+ absl::optional<AudioCodec> old_codec = FindCodec(recv_codecs_, codec);
if (old_codec && old_codec->id != codec.id) {
RTC_LOG(LS_WARNING) << codec.name << " mapped to a second payload type ("
<< codec.id << ", was already mapped to "
diff --git a/third_party/libwebrtc/media/engine/webrtc_voice_engine.h b/third_party/libwebrtc/media/engine/webrtc_voice_engine.h
index a3e6d3acab..ed71667525 100644
--- a/third_party/libwebrtc/media/engine/webrtc_voice_engine.h
+++ b/third_party/libwebrtc/media/engine/webrtc_voice_engine.h
@@ -90,9 +90,6 @@ class WebRtcVoiceEngine final : public VoiceEngineInterface {
const rtc::scoped_refptr<webrtc::AudioDecoderFactory>& decoder_factory,
rtc::scoped_refptr<webrtc::AudioMixer> audio_mixer,
rtc::scoped_refptr<webrtc::AudioProcessing> audio_processing,
- // TODO(bugs.webrtc.org/15111):
- // Remove the raw AudioFrameProcessor pointer in the follow-up.
- webrtc::AudioFrameProcessor* audio_frame_processor,
std::unique_ptr<webrtc::AudioFrameProcessor> owned_audio_frame_processor,
const webrtc::FieldTrialsView& trials);
@@ -166,10 +163,7 @@ class WebRtcVoiceEngine final : public VoiceEngineInterface {
// The audio processing module.
rtc::scoped_refptr<webrtc::AudioProcessing> apm_;
// Asynchronous audio processing.
- // TODO(bugs.webrtc.org/15111):
- // Remove the raw AudioFrameProcessor pointer in the follow-up.
- webrtc::AudioFrameProcessor* const audio_frame_processor_;
- std::unique_ptr<webrtc::AudioFrameProcessor> owned_audio_frame_processor_;
+ std::unique_ptr<webrtc::AudioFrameProcessor> audio_frame_processor_;
// The primary instance of WebRtc VoiceEngine.
rtc::scoped_refptr<webrtc::AudioState> audio_state_;
std::vector<AudioCodec> send_codecs_;
diff --git a/third_party/libwebrtc/media/engine/webrtc_voice_engine_unittest.cc b/third_party/libwebrtc/media/engine/webrtc_voice_engine_unittest.cc
index b1393eec74..4d6580631d 100644
--- a/third_party/libwebrtc/media/engine/webrtc_voice_engine_unittest.cc
+++ b/third_party/libwebrtc/media/engine/webrtc_voice_engine_unittest.cc
@@ -18,8 +18,9 @@
#include "absl/types/optional.h"
#include "api/audio_codecs/builtin_audio_decoder_factory.h"
#include "api/audio_codecs/builtin_audio_encoder_factory.h"
+#include "api/environment/environment.h"
+#include "api/environment/environment_factory.h"
#include "api/media_types.h"
-#include "api/rtc_event_log/rtc_event_log.h"
#include "api/rtp_parameters.h"
#include "api/scoped_refptr.h"
#include "api/task_queue/default_task_queue_factory.h"
@@ -45,6 +46,7 @@
#include "test/mock_audio_encoder_factory.h"
#include "test/scoped_key_value_config.h"
+namespace {
using ::testing::_;
using ::testing::ContainerEq;
using ::testing::Contains;
@@ -55,11 +57,11 @@ using ::testing::ReturnPointee;
using ::testing::SaveArg;
using ::testing::StrictMock;
using ::testing::UnorderedElementsAreArray;
+using ::webrtc::BitrateConstraints;
using ::webrtc::Call;
using ::webrtc::CallConfig;
-
-namespace {
-using webrtc::BitrateConstraints;
+using ::webrtc::CreateEnvironment;
+using ::webrtc::Environment;
constexpr uint32_t kMaxUnsignaledRecvStreams = 4;
@@ -174,7 +176,7 @@ TEST(WebRtcVoiceEngineTestStubLibrary, StartupShutdown) {
task_queue_factory.get(), adm.get(),
webrtc::MockAudioEncoderFactory::CreateUnusedFactory(),
webrtc::MockAudioDecoderFactory::CreateUnusedFactory(), nullptr, apm,
- nullptr, nullptr, trials);
+ nullptr, trials);
engine.Init();
}
}
@@ -220,7 +222,7 @@ class WebRtcVoiceEngineTestFake : public ::testing::TestWithParam<bool> {
auto decoder_factory = webrtc::CreateBuiltinAudioDecoderFactory();
engine_.reset(new cricket::WebRtcVoiceEngine(
task_queue_factory_.get(), adm_.get(), encoder_factory, decoder_factory,
- nullptr, apm_, nullptr, nullptr, field_trials_));
+ nullptr, apm_, nullptr, field_trials_));
engine_->Init();
send_parameters_.codecs.push_back(kPcmuCodec);
recv_parameters_.codecs.push_back(kPcmuCodec);
@@ -3678,24 +3680,18 @@ TEST(WebRtcVoiceEngineTest, StartupShutdown) {
for (bool use_null_apm : {false, true}) {
// If the VoiceEngine wants to gather available codecs early, that's fine
// but we never want it to create a decoder at this stage.
- std::unique_ptr<webrtc::TaskQueueFactory> task_queue_factory =
- webrtc::CreateDefaultTaskQueueFactory();
+ Environment env = CreateEnvironment();
rtc::scoped_refptr<webrtc::test::MockAudioDeviceModule> adm =
webrtc::test::MockAudioDeviceModule::CreateNice();
rtc::scoped_refptr<webrtc::AudioProcessing> apm =
use_null_apm ? nullptr : webrtc::AudioProcessingBuilder().Create();
- webrtc::FieldTrialBasedConfig field_trials;
cricket::WebRtcVoiceEngine engine(
- task_queue_factory.get(), adm.get(),
+ &env.task_queue_factory(), adm.get(),
webrtc::MockAudioEncoderFactory::CreateUnusedFactory(),
webrtc::MockAudioDecoderFactory::CreateUnusedFactory(), nullptr, apm,
- nullptr, nullptr, field_trials);
+ nullptr, env.field_trials());
engine.Init();
- webrtc::RtcEventLogNull event_log;
- CallConfig call_config(&event_log);
- call_config.trials = &field_trials;
- call_config.task_queue_factory = task_queue_factory.get();
- std::unique_ptr<Call> call = Call::Create(call_config);
+ std::unique_ptr<Call> call = Call::Create(CallConfig(env));
std::unique_ptr<cricket::VoiceMediaSendChannelInterface> send_channel =
engine.CreateSendChannel(
call.get(), cricket::MediaConfig(), cricket::AudioOptions(),
@@ -3713,25 +3709,19 @@ TEST(WebRtcVoiceEngineTest, StartupShutdown) {
TEST(WebRtcVoiceEngineTest, StartupShutdownWithExternalADM) {
rtc::AutoThread main_thread;
for (bool use_null_apm : {false, true}) {
- std::unique_ptr<webrtc::TaskQueueFactory> task_queue_factory =
- webrtc::CreateDefaultTaskQueueFactory();
+ Environment env = CreateEnvironment();
auto adm = rtc::make_ref_counted<
::testing::NiceMock<webrtc::test::MockAudioDeviceModule>>();
{
rtc::scoped_refptr<webrtc::AudioProcessing> apm =
use_null_apm ? nullptr : webrtc::AudioProcessingBuilder().Create();
- webrtc::FieldTrialBasedConfig field_trials;
cricket::WebRtcVoiceEngine engine(
- task_queue_factory.get(), adm.get(),
+ &env.task_queue_factory(), adm.get(),
webrtc::MockAudioEncoderFactory::CreateUnusedFactory(),
webrtc::MockAudioDecoderFactory::CreateUnusedFactory(), nullptr, apm,
- nullptr, nullptr, field_trials);
+ nullptr, env.field_trials());
engine.Init();
- webrtc::RtcEventLogNull event_log;
- CallConfig call_config(&event_log);
- call_config.trials = &field_trials;
- call_config.task_queue_factory = task_queue_factory.get();
- std::unique_ptr<Call> call = Call::Create(call_config);
+ std::unique_ptr<Call> call = Call::Create(CallConfig(env));
std::unique_ptr<cricket::VoiceMediaSendChannelInterface> send_channel =
engine.CreateSendChannel(
call.get(), cricket::MediaConfig(), cricket::AudioOptions(),
@@ -3765,7 +3755,7 @@ TEST(WebRtcVoiceEngineTest, HasCorrectPayloadTypeMapping) {
task_queue_factory.get(), adm.get(),
webrtc::MockAudioEncoderFactory::CreateUnusedFactory(),
webrtc::MockAudioDecoderFactory::CreateUnusedFactory(), nullptr, apm,
- nullptr, nullptr, field_trials);
+ nullptr, field_trials);
engine.Init();
for (const cricket::AudioCodec& codec : engine.send_codecs()) {
auto is_codec = [&codec](const char* name, int clockrate = 0) {
@@ -3804,24 +3794,18 @@ TEST(WebRtcVoiceEngineTest, HasCorrectPayloadTypeMapping) {
TEST(WebRtcVoiceEngineTest, Has32Channels) {
rtc::AutoThread main_thread;
for (bool use_null_apm : {false, true}) {
- std::unique_ptr<webrtc::TaskQueueFactory> task_queue_factory =
- webrtc::CreateDefaultTaskQueueFactory();
+ Environment env = CreateEnvironment();
rtc::scoped_refptr<webrtc::test::MockAudioDeviceModule> adm =
webrtc::test::MockAudioDeviceModule::CreateNice();
rtc::scoped_refptr<webrtc::AudioProcessing> apm =
use_null_apm ? nullptr : webrtc::AudioProcessingBuilder().Create();
- webrtc::FieldTrialBasedConfig field_trials;
cricket::WebRtcVoiceEngine engine(
- task_queue_factory.get(), adm.get(),
+ &env.task_queue_factory(), adm.get(),
webrtc::MockAudioEncoderFactory::CreateUnusedFactory(),
webrtc::MockAudioDecoderFactory::CreateUnusedFactory(), nullptr, apm,
- nullptr, nullptr, field_trials);
+ nullptr, env.field_trials());
engine.Init();
- webrtc::RtcEventLogNull event_log;
- CallConfig call_config(&event_log);
- call_config.trials = &field_trials;
- call_config.task_queue_factory = task_queue_factory.get();
- std::unique_ptr<Call> call = Call::Create(call_config);
+ std::unique_ptr<Call> call = Call::Create(CallConfig(env));
std::vector<std::unique_ptr<cricket::VoiceMediaSendChannelInterface>>
channels;
@@ -3843,8 +3827,7 @@ TEST(WebRtcVoiceEngineTest, Has32Channels) {
TEST(WebRtcVoiceEngineTest, SetRecvCodecs) {
rtc::AutoThread main_thread;
for (bool use_null_apm : {false, true}) {
- std::unique_ptr<webrtc::TaskQueueFactory> task_queue_factory =
- webrtc::CreateDefaultTaskQueueFactory();
+ Environment env = CreateEnvironment();
// TODO(ossu): I'm not sure of the intent of this test. It's either:
// - Check that our builtin codecs are usable by Channel.
// - The codecs provided by the engine is usable by Channel.
@@ -3856,18 +3839,13 @@ TEST(WebRtcVoiceEngineTest, SetRecvCodecs) {
webrtc::test::MockAudioDeviceModule::CreateNice();
rtc::scoped_refptr<webrtc::AudioProcessing> apm =
use_null_apm ? nullptr : webrtc::AudioProcessingBuilder().Create();
- webrtc::FieldTrialBasedConfig field_trials;
cricket::WebRtcVoiceEngine engine(
- task_queue_factory.get(), adm.get(),
+ &env.task_queue_factory(), adm.get(),
webrtc::MockAudioEncoderFactory::CreateUnusedFactory(),
webrtc::CreateBuiltinAudioDecoderFactory(), nullptr, apm, nullptr,
- nullptr, field_trials);
+ env.field_trials());
engine.Init();
- webrtc::RtcEventLogNull event_log;
- CallConfig call_config(&event_log);
- call_config.trials = &field_trials;
- call_config.task_queue_factory = task_queue_factory.get();
- std::unique_ptr<Call> call = Call::Create(call_config);
+ std::unique_ptr<Call> call = Call::Create(CallConfig(env));
cricket::WebRtcVoiceReceiveChannel channel(
&engine, cricket::MediaConfig(), cricket::AudioOptions(),
webrtc::CryptoOptions(), call.get(),
@@ -3880,22 +3858,17 @@ TEST(WebRtcVoiceEngineTest, SetRecvCodecs) {
TEST(WebRtcVoiceEngineTest, SetRtpSendParametersMaxBitrate) {
rtc::AutoThread main_thread;
- std::unique_ptr<webrtc::TaskQueueFactory> task_queue_factory =
- webrtc::CreateDefaultTaskQueueFactory();
+ Environment env = CreateEnvironment();
rtc::scoped_refptr<webrtc::test::MockAudioDeviceModule> adm =
webrtc::test::MockAudioDeviceModule::CreateNice();
- webrtc::FieldTrialBasedConfig field_trials;
FakeAudioSource source;
- cricket::WebRtcVoiceEngine engine(task_queue_factory.get(), adm.get(),
+ cricket::WebRtcVoiceEngine engine(&env.task_queue_factory(), adm.get(),
webrtc::CreateBuiltinAudioEncoderFactory(),
webrtc::CreateBuiltinAudioDecoderFactory(),
- nullptr, nullptr, nullptr, nullptr,
- field_trials);
+ nullptr, nullptr, nullptr,
+ env.field_trials());
engine.Init();
- webrtc::RtcEventLogNull event_log;
- CallConfig call_config(&event_log);
- call_config.trials = &field_trials;
- call_config.task_queue_factory = task_queue_factory.get();
+ CallConfig call_config(env);
{
webrtc::AudioState::Config config;
config.audio_mixer = webrtc::AudioMixerImpl::Create();
@@ -3965,7 +3938,7 @@ TEST(WebRtcVoiceEngineTest, CollectRecvCodecs) {
webrtc::FieldTrialBasedConfig field_trials;
cricket::WebRtcVoiceEngine engine(
task_queue_factory.get(), adm.get(), unused_encoder_factory,
- mock_decoder_factory, nullptr, apm, nullptr, nullptr, field_trials);
+ mock_decoder_factory, nullptr, apm, nullptr, field_trials);
engine.Init();
auto codecs = engine.recv_codecs();
EXPECT_EQ(11u, codecs.size());
diff --git a/third_party/libwebrtc/media/media_channel_gn/moz.build b/third_party/libwebrtc/media/media_channel_gn/moz.build
index 1bedb41bf2..c665368568 100644
--- a/third_party/libwebrtc/media/media_channel_gn/moz.build
+++ b/third_party/libwebrtc/media/media_channel_gn/moz.build
@@ -192,16 +192,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/media/media_channel_impl_gn/moz.build b/third_party/libwebrtc/media/media_channel_impl_gn/moz.build
index 7d0a4bd650..27bfa53fff 100644
--- a/third_party/libwebrtc/media/media_channel_impl_gn/moz.build
+++ b/third_party/libwebrtc/media/media_channel_impl_gn/moz.build
@@ -176,16 +176,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/media/media_constants_gn/moz.build b/third_party/libwebrtc/media/media_constants_gn/moz.build
index af4cd6b257..95a0c3a056 100644
--- a/third_party/libwebrtc/media/media_constants_gn/moz.build
+++ b/third_party/libwebrtc/media/media_constants_gn/moz.build
@@ -184,7 +184,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -194,10 +193,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/media/rid_description_gn/moz.build b/third_party/libwebrtc/media/rid_description_gn/moz.build
index 61afeec945..944901a1ca 100644
--- a/third_party/libwebrtc/media/rid_description_gn/moz.build
+++ b/third_party/libwebrtc/media/rid_description_gn/moz.build
@@ -176,16 +176,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/media/rtc_media_base_gn/moz.build b/third_party/libwebrtc/media/rtc_media_base_gn/moz.build
index cfff6f3411..a5b3661adc 100644
--- a/third_party/libwebrtc/media/rtc_media_base_gn/moz.build
+++ b/third_party/libwebrtc/media/rtc_media_base_gn/moz.build
@@ -203,7 +203,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -213,10 +212,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/media/rtc_media_config_gn/moz.build b/third_party/libwebrtc/media/rtc_media_config_gn/moz.build
index 17afebe8da..8f3f81cc5b 100644
--- a/third_party/libwebrtc/media/rtc_media_config_gn/moz.build
+++ b/third_party/libwebrtc/media/rtc_media_config_gn/moz.build
@@ -176,16 +176,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/media/rtc_simulcast_encoder_adapter_gn/moz.build b/third_party/libwebrtc/media/rtc_simulcast_encoder_adapter_gn/moz.build
index c09703ddd6..6b1032e1b0 100644
--- a/third_party/libwebrtc/media/rtc_simulcast_encoder_adapter_gn/moz.build
+++ b/third_party/libwebrtc/media/rtc_simulcast_encoder_adapter_gn/moz.build
@@ -200,7 +200,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -210,10 +209,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/media/rtp_utils_gn/moz.build b/third_party/libwebrtc/media/rtp_utils_gn/moz.build
index 1aaa347151..e2e5c11695 100644
--- a/third_party/libwebrtc/media/rtp_utils_gn/moz.build
+++ b/third_party/libwebrtc/media/rtp_utils_gn/moz.build
@@ -176,16 +176,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/media/stream_params_gn/moz.build b/third_party/libwebrtc/media/stream_params_gn/moz.build
index 71875c4e01..1582a42c0d 100644
--- a/third_party/libwebrtc/media/stream_params_gn/moz.build
+++ b/third_party/libwebrtc/media/stream_params_gn/moz.build
@@ -176,16 +176,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/async_audio_processing/async_audio_processing_gn/moz.build b/third_party/libwebrtc/modules/async_audio_processing/async_audio_processing_gn/moz.build
index 347559a342..dfff987043 100644
--- a/third_party/libwebrtc/modules/async_audio_processing/async_audio_processing_gn/moz.build
+++ b/third_party/libwebrtc/modules/async_audio_processing/async_audio_processing_gn/moz.build
@@ -195,7 +195,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -205,10 +204,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_coding/BUILD.gn b/third_party/libwebrtc/modules/audio_coding/BUILD.gn
index 3e4d7e0c25..ddd1fd2656 100644
--- a/third_party/libwebrtc/modules/audio_coding/BUILD.gn
+++ b/third_party/libwebrtc/modules/audio_coding/BUILD.gn
@@ -618,7 +618,6 @@ rtc_library("audio_network_adaptor") {
"../../common_audio",
"../../logging:rtc_event_audio",
"../../rtc_base:checks",
- "../../rtc_base:ignore_wundef",
"../../rtc_base:logging",
"../../rtc_base:protobuf_utils",
"../../rtc_base:safe_conversions",
@@ -957,7 +956,6 @@ rtc_library("audio_coding_modules_tests_shared") {
"../../api/audio_codecs:builtin_audio_encoder_factory",
"../../api/neteq:neteq_api",
"../../rtc_base:checks",
- "../../rtc_base:ignore_wundef",
"../../rtc_base:ssl",
"../../rtc_base:stringutils",
"../../system_wrappers",
@@ -1644,6 +1642,7 @@ if (rtc_include_tests) {
"neteq/mock/mock_expand.h",
"neteq/mock/mock_histogram.h",
"neteq/mock/mock_neteq_controller.h",
+ "neteq/mock/mock_packet_arrival_history.h",
"neteq/mock/mock_packet_buffer.h",
"neteq/mock/mock_red_payload_splitter.h",
"neteq/mock/mock_statistics_calculator.h",
@@ -1717,7 +1716,6 @@ if (rtc_include_tests) {
"../../logging:rtc_event_audio",
"../../modules/rtp_rtcp:rtp_rtcp_format",
"../../rtc_base:checks",
- "../../rtc_base:ignore_wundef",
"../../rtc_base:macromagic",
"../../rtc_base:platform_thread",
"../../rtc_base:refcount",
diff --git a/third_party/libwebrtc/modules/audio_coding/acm2/audio_coding_module_unittest.cc b/third_party/libwebrtc/modules/audio_coding/acm2/audio_coding_module_unittest.cc
index 210244154a..2d9ea91106 100644
--- a/third_party/libwebrtc/modules/audio_coding/acm2/audio_coding_module_unittest.cc
+++ b/third_party/libwebrtc/modules/audio_coding/acm2/audio_coding_module_unittest.cc
@@ -707,7 +707,7 @@ class AcmSenderBitExactnessNewApi : public AcmSenderBitExactnessOldApi {};
TEST_F(AcmSenderBitExactnessOldApi, Pcm16_8000khz_10ms) {
ASSERT_NO_FATAL_FAILURE(SetUpTest("L16", 8000, 1, 107, 80, 80));
- Run(/*audio_checksum_ref=*/"69118ed438ac76252d023e0463819471",
+ Run(/*audio_checksum_ref=*/"3e43fd5d3c73a59e8118e68fbfafe2c7",
/*payload_checksum_ref=*/"c1edd36339ce0326cc4550041ad719a0",
/*expected_packets=*/100,
/*expected_channels=*/test::AcmReceiveTestOldApi::kMonoOutput);
@@ -715,7 +715,7 @@ TEST_F(AcmSenderBitExactnessOldApi, Pcm16_8000khz_10ms) {
TEST_F(AcmSenderBitExactnessOldApi, Pcm16_16000khz_10ms) {
ASSERT_NO_FATAL_FAILURE(SetUpTest("L16", 16000, 1, 108, 160, 160));
- Run(/*audio_checksum_ref=*/"f95c87bdd33f631bcf80f4b19445bbd2",
+ Run(/*audio_checksum_ref=*/"608750138315cbab33d76d38e8367807",
/*payload_checksum_ref=*/"ad786526383178b08d80d6eee06e9bad",
/*expected_packets=*/100,
/*expected_channels=*/test::AcmReceiveTestOldApi::kMonoOutput);
@@ -723,7 +723,7 @@ TEST_F(AcmSenderBitExactnessOldApi, Pcm16_16000khz_10ms) {
TEST_F(AcmSenderBitExactnessOldApi, Pcm16_32000khz_10ms) {
ASSERT_NO_FATAL_FAILURE(SetUpTest("L16", 32000, 1, 109, 320, 320));
- Run(/*audio_checksum_ref=*/"c50244419c5c3a2f04cc69a022c266a2",
+ Run(/*audio_checksum_ref=*/"02e9927ef5e4d2cd792a5df0bdee5e19",
/*payload_checksum_ref=*/"5ef82ea885e922263606c6fdbc49f651",
/*expected_packets=*/100,
/*expected_channels=*/test::AcmReceiveTestOldApi::kMonoOutput);
@@ -731,7 +731,7 @@ TEST_F(AcmSenderBitExactnessOldApi, Pcm16_32000khz_10ms) {
TEST_F(AcmSenderBitExactnessOldApi, Pcm16_stereo_8000khz_10ms) {
ASSERT_NO_FATAL_FAILURE(SetUpTest("L16", 8000, 2, 111, 80, 80));
- Run(/*audio_checksum_ref=*/"4fccf4cc96f1e8e8de4b9fadf62ded9e",
+ Run(/*audio_checksum_ref=*/"4ff38de045b19f64de9c7e229ba36317",
/*payload_checksum_ref=*/"62ce5adb0d4965d0a52ec98ae7f98974",
/*expected_packets=*/100,
/*expected_channels=*/test::AcmReceiveTestOldApi::kStereoOutput);
@@ -739,7 +739,7 @@ TEST_F(AcmSenderBitExactnessOldApi, Pcm16_stereo_8000khz_10ms) {
TEST_F(AcmSenderBitExactnessOldApi, Pcm16_stereo_16000khz_10ms) {
ASSERT_NO_FATAL_FAILURE(SetUpTest("L16", 16000, 2, 112, 160, 160));
- Run(/*audio_checksum_ref=*/"e15e388d9d4af8c02a59fe1552fedee3",
+ Run(/*audio_checksum_ref=*/"1ee35394cfca78ad6d55468441af36fa",
/*payload_checksum_ref=*/"41ca8edac4b8c71cd54fd9f25ec14870",
/*expected_packets=*/100,
/*expected_channels=*/test::AcmReceiveTestOldApi::kStereoOutput);
@@ -747,7 +747,7 @@ TEST_F(AcmSenderBitExactnessOldApi, Pcm16_stereo_16000khz_10ms) {
TEST_F(AcmSenderBitExactnessOldApi, Pcm16_stereo_32000khz_10ms) {
ASSERT_NO_FATAL_FAILURE(SetUpTest("L16", 32000, 2, 113, 320, 320));
- Run(/*audio_checksum_ref=*/"b240520c0d05003fde7a174ae5957286",
+ Run(/*audio_checksum_ref=*/"19cae34730a0f6a17cf4e76bf21b69d6",
/*payload_checksum_ref=*/"50e58502fb04421bf5b857dda4c96879",
/*expected_packets=*/100,
/*expected_channels=*/test::AcmReceiveTestOldApi::kStereoOutput);
@@ -763,7 +763,7 @@ TEST_F(AcmSenderBitExactnessOldApi, Pcmu_20ms) {
TEST_F(AcmSenderBitExactnessOldApi, Pcma_20ms) {
ASSERT_NO_FATAL_FAILURE(SetUpTest("PCMA", 8000, 1, 8, 160, 160));
- Run(/*audio_checksum_ref=*/"47eb60e855eb12d1b0e6da9c975754a4",
+ Run(/*audio_checksum_ref=*/"ae259cab624095270b7369e53a7b53a3",
/*payload_checksum_ref=*/"6ad745e55aa48981bfc790d0eeef2dd1",
/*expected_packets=*/50,
/*expected_channels=*/test::AcmReceiveTestOldApi::kMonoOutput);
@@ -779,7 +779,7 @@ TEST_F(AcmSenderBitExactnessOldApi, Pcmu_stereo_20ms) {
TEST_F(AcmSenderBitExactnessOldApi, Pcma_stereo_20ms) {
ASSERT_NO_FATAL_FAILURE(SetUpTest("PCMA", 8000, 2, 118, 160, 160));
- Run(/*audio_checksum_ref=*/"a84d75e098d87ab6b260687eb4b612a2",
+ Run(/*audio_checksum_ref=*/"f2e81d2531a805c40e61da5106b50006",
/*payload_checksum_ref=*/"92b282c83efd20e7eeef52ba40842cf7",
/*expected_packets=*/50,
/*expected_channels=*/test::AcmReceiveTestOldApi::kStereoOutput);
@@ -789,7 +789,7 @@ TEST_F(AcmSenderBitExactnessOldApi, Pcma_stereo_20ms) {
defined(WEBRTC_ARCH_X86_64)
TEST_F(AcmSenderBitExactnessOldApi, Ilbc_30ms) {
ASSERT_NO_FATAL_FAILURE(SetUpTest("ILBC", 8000, 1, 102, 240, 240));
- Run(/*audio_checksum_ref=*/"b14dba0de36efa5ec88a32c0b320b70f",
+ Run(/*audio_checksum_ref=*/"a739434bec8a754e9356ce2115603ce5",
/*payload_checksum_ref=*/"cfae2e9f6aba96e145f2bcdd5050ce78",
/*expected_packets=*/33,
/*expected_channels=*/test::AcmReceiveTestOldApi::kMonoOutput);
@@ -799,7 +799,7 @@ TEST_F(AcmSenderBitExactnessOldApi, Ilbc_30ms) {
#if defined(WEBRTC_LINUX) && defined(WEBRTC_ARCH_X86_64)
TEST_F(AcmSenderBitExactnessOldApi, G722_20ms) {
ASSERT_NO_FATAL_FAILURE(SetUpTest("G722", 16000, 1, 9, 320, 160));
- Run(/*audio_checksum_ref=*/"f5264affff25cf2cbd2e1e8a5217f9a3",
+ Run(/*audio_checksum_ref=*/"b875d9a3e41f5470857bdff02e3b368f",
/*payload_checksum_ref=*/"fc68a87e1380614e658087cb35d5ca10",
/*expected_packets=*/50,
/*expected_channels=*/test::AcmReceiveTestOldApi::kMonoOutput);
@@ -809,7 +809,7 @@ TEST_F(AcmSenderBitExactnessOldApi, G722_20ms) {
#if defined(WEBRTC_LINUX) && defined(WEBRTC_ARCH_X86_64)
TEST_F(AcmSenderBitExactnessOldApi, G722_stereo_20ms) {
ASSERT_NO_FATAL_FAILURE(SetUpTest("G722", 16000, 2, 119, 320, 160));
- Run(/*audio_checksum_ref=*/"be0b8528ff9db3a2219f55ddd36faf7f",
+ Run(/*audio_checksum_ref=*/"02c427d73363b2f37853a0dd17fe1aba",
/*payload_checksum_ref=*/"66516152eeaa1e650ad94ff85f668dac",
/*expected_packets=*/50,
/*expected_channels=*/test::AcmReceiveTestOldApi::kStereoOutput);
@@ -897,8 +897,8 @@ TEST_F(AcmSenderBitExactnessNewApi, OpusFromFormat_stereo_20ms_voip) {
ASSERT_NO_FATAL_FAILURE(SetUpTestExternalEncoder(
AudioEncoderOpus::MakeAudioEncoder(*config, 120), 120));
const std::string audio_maybe_sse =
- "1010e60ad34cee73c939edaf563d0593"
- "|c05b4523d4c3fad2bab96d2a56baa2d0";
+ "cb644fc17d9666a0f5986eef24818159"
+ "|4a74024473c7c729543c2790829b1e42";
const std::string payload_maybe_sse =
"ea48d94e43217793af9b7e15ece94e54"
diff --git a/third_party/libwebrtc/modules/audio_coding/audio_coding_gn/moz.build b/third_party/libwebrtc/modules/audio_coding/audio_coding_gn/moz.build
index 4dad1217d0..88fa77a0e2 100644
--- a/third_party/libwebrtc/modules/audio_coding/audio_coding_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_coding/audio_coding_gn/moz.build
@@ -203,7 +203,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -213,10 +212,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_coding/audio_coding_module_typedefs_gn/moz.build b/third_party/libwebrtc/modules/audio_coding/audio_coding_module_typedefs_gn/moz.build
index 704026c845..851dd7b58e 100644
--- a/third_party/libwebrtc/modules/audio_coding/audio_coding_module_typedefs_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_coding/audio_coding_module_typedefs_gn/moz.build
@@ -176,16 +176,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_coding/audio_coding_opus_common_gn/moz.build b/third_party/libwebrtc/modules/audio_coding/audio_coding_opus_common_gn/moz.build
index bbb1557baa..e509916cfd 100644
--- a/third_party/libwebrtc/modules/audio_coding/audio_coding_opus_common_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_coding/audio_coding_opus_common_gn/moz.build
@@ -195,7 +195,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -205,10 +204,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_coding/audio_encoder_cng_gn/moz.build b/third_party/libwebrtc/modules/audio_coding/audio_encoder_cng_gn/moz.build
index 75153f3221..7829419065 100644
--- a/third_party/libwebrtc/modules/audio_coding/audio_encoder_cng_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_coding/audio_encoder_cng_gn/moz.build
@@ -199,7 +199,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -209,10 +208,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_coding/audio_network_adaptor/controller_manager.cc b/third_party/libwebrtc/modules/audio_coding/audio_network_adaptor/controller_manager.cc
index 42dd8a8786..793c73a380 100644
--- a/third_party/libwebrtc/modules/audio_coding/audio_network_adaptor/controller_manager.cc
+++ b/third_party/libwebrtc/modules/audio_coding/audio_network_adaptor/controller_manager.cc
@@ -24,18 +24,16 @@
#include "modules/audio_coding/audio_network_adaptor/frame_length_controller.h"
#include "modules/audio_coding/audio_network_adaptor/frame_length_controller_v2.h"
#include "modules/audio_coding/audio_network_adaptor/util/threshold_curve.h"
-#include "rtc_base/ignore_wundef.h"
#include "rtc_base/logging.h"
#include "rtc_base/time_utils.h"
#if WEBRTC_ENABLE_PROTOBUF
-RTC_PUSH_IGNORING_WUNDEF()
#ifdef WEBRTC_ANDROID_PLATFORM_BUILD
#include "external/webrtc/webrtc/modules/audio_coding/audio_network_adaptor/config.pb.h"
#else
#include "modules/audio_coding/audio_network_adaptor/config.pb.h"
#endif
-RTC_POP_IGNORING_WUNDEF()
+
#endif
namespace webrtc {
diff --git a/third_party/libwebrtc/modules/audio_coding/audio_network_adaptor/controller_manager_unittest.cc b/third_party/libwebrtc/modules/audio_coding/audio_network_adaptor/controller_manager_unittest.cc
index 3e6ecf6def..f399511757 100644
--- a/third_party/libwebrtc/modules/audio_coding/audio_network_adaptor/controller_manager_unittest.cc
+++ b/third_party/libwebrtc/modules/audio_coding/audio_network_adaptor/controller_manager_unittest.cc
@@ -17,17 +17,14 @@
#include "modules/audio_coding/audio_network_adaptor/mock/mock_controller.h"
#include "modules/audio_coding/audio_network_adaptor/mock/mock_debug_dump_writer.h"
#include "rtc_base/fake_clock.h"
-#include "rtc_base/ignore_wundef.h"
#include "test/gtest.h"
#if WEBRTC_ENABLE_PROTOBUF
-RTC_PUSH_IGNORING_WUNDEF()
#ifdef WEBRTC_ANDROID_PLATFORM_BUILD
#include "external/webrtc/webrtc/modules/audio_coding/audio_network_adaptor/config.pb.h"
#else
#include "modules/audio_coding/audio_network_adaptor/config.pb.h"
#endif
-RTC_POP_IGNORING_WUNDEF()
#endif
namespace webrtc {
diff --git a/third_party/libwebrtc/modules/audio_coding/audio_network_adaptor/debug_dump_writer.cc b/third_party/libwebrtc/modules/audio_coding/audio_network_adaptor/debug_dump_writer.cc
index 2616706ee5..5ffbee219c 100644
--- a/third_party/libwebrtc/modules/audio_coding/audio_network_adaptor/debug_dump_writer.cc
+++ b/third_party/libwebrtc/modules/audio_coding/audio_network_adaptor/debug_dump_writer.cc
@@ -14,18 +14,15 @@
#include "absl/types/optional.h"
#include "rtc_base/checks.h"
-#include "rtc_base/ignore_wundef.h"
#include "rtc_base/numerics/safe_conversions.h"
#include "rtc_base/system/file_wrapper.h"
#if WEBRTC_ENABLE_PROTOBUF
-RTC_PUSH_IGNORING_WUNDEF()
#ifdef WEBRTC_ANDROID_PLATFORM_BUILD
#include "external/webrtc/webrtc/modules/audio_coding/audio_network_adaptor/debug_dump.pb.h"
#else
#include "modules/audio_coding/audio_network_adaptor/debug_dump.pb.h"
#endif
-RTC_POP_IGNORING_WUNDEF()
#endif
namespace webrtc {
diff --git a/third_party/libwebrtc/modules/audio_coding/audio_network_adaptor/debug_dump_writer.h b/third_party/libwebrtc/modules/audio_coding/audio_network_adaptor/debug_dump_writer.h
index 8fdf2f7728..fd3a64dbb1 100644
--- a/third_party/libwebrtc/modules/audio_coding/audio_network_adaptor/debug_dump_writer.h
+++ b/third_party/libwebrtc/modules/audio_coding/audio_network_adaptor/debug_dump_writer.h
@@ -15,16 +15,14 @@
#include "modules/audio_coding/audio_network_adaptor/controller.h"
#include "modules/audio_coding/audio_network_adaptor/include/audio_network_adaptor.h"
-#include "rtc_base/ignore_wundef.h"
#include "rtc_base/system/file_wrapper.h"
+
#if WEBRTC_ENABLE_PROTOBUF
-RTC_PUSH_IGNORING_WUNDEF()
#ifdef WEBRTC_ANDROID_PLATFORM_BUILD
#include "external/webrtc/webrtc/modules/audio_coding/audio_network_adaptor/config.pb.h"
#else
#include "modules/audio_coding/audio_network_adaptor/config.pb.h"
#endif
-RTC_POP_IGNORING_WUNDEF()
#endif
namespace webrtc {
diff --git a/third_party/libwebrtc/modules/audio_coding/audio_network_adaptor_config_gn/moz.build b/third_party/libwebrtc/modules/audio_coding/audio_network_adaptor_config_gn/moz.build
index b9d3c55453..de87e8b033 100644
--- a/third_party/libwebrtc/modules/audio_coding/audio_network_adaptor_config_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_coding/audio_network_adaptor_config_gn/moz.build
@@ -184,7 +184,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -194,10 +193,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_coding/audio_network_adaptor_gn/moz.build b/third_party/libwebrtc/modules/audio_coding/audio_network_adaptor_gn/moz.build
index 7d446965f1..8a371a9aaf 100644
--- a/third_party/libwebrtc/modules/audio_coding/audio_network_adaptor_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_coding/audio_network_adaptor_gn/moz.build
@@ -209,7 +209,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -219,10 +218,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_coding/default_neteq_factory_gn/moz.build b/third_party/libwebrtc/modules/audio_coding/default_neteq_factory_gn/moz.build
index aea0a80ed4..d7928549d7 100644
--- a/third_party/libwebrtc/modules/audio_coding/default_neteq_factory_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_coding/default_neteq_factory_gn/moz.build
@@ -199,7 +199,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -209,10 +208,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_coding/g711_c_gn/moz.build b/third_party/libwebrtc/modules/audio_coding/g711_c_gn/moz.build
index 575478702e..bedb8fc477 100644
--- a/third_party/libwebrtc/modules/audio_coding/g711_c_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_coding/g711_c_gn/moz.build
@@ -184,7 +184,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -194,10 +193,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_coding/g711_gn/moz.build b/third_party/libwebrtc/modules/audio_coding/g711_gn/moz.build
index fa25fde0bd..103d89c6d8 100644
--- a/third_party/libwebrtc/modules/audio_coding/g711_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_coding/g711_gn/moz.build
@@ -196,7 +196,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -206,10 +205,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_coding/g722_c_gn/moz.build b/third_party/libwebrtc/modules/audio_coding/g722_c_gn/moz.build
index 4821c2bd82..48137ada85 100644
--- a/third_party/libwebrtc/modules/audio_coding/g722_c_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_coding/g722_c_gn/moz.build
@@ -184,7 +184,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -194,10 +193,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_coding/g722_gn/moz.build b/third_party/libwebrtc/modules/audio_coding/g722_gn/moz.build
index 0a56f32af0..81eb870466 100644
--- a/third_party/libwebrtc/modules/audio_coding/g722_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_coding/g722_gn/moz.build
@@ -196,7 +196,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -206,10 +205,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_coding/ilbc_c_gn/moz.build b/third_party/libwebrtc/modules/audio_coding/ilbc_c_gn/moz.build
index 43d69c7662..d3aa4e0018 100644
--- a/third_party/libwebrtc/modules/audio_coding/ilbc_c_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_coding/ilbc_c_gn/moz.build
@@ -267,7 +267,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -277,10 +276,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_coding/ilbc_gn/moz.build b/third_party/libwebrtc/modules/audio_coding/ilbc_gn/moz.build
index c4b3b4cd13..9a397a1fdc 100644
--- a/third_party/libwebrtc/modules/audio_coding/ilbc_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_coding/ilbc_gn/moz.build
@@ -200,7 +200,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -210,10 +209,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_coding/isac_bwinfo_gn/moz.build b/third_party/libwebrtc/modules/audio_coding/isac_bwinfo_gn/moz.build
index 4f4a5c0e7e..fdfc4fc855 100644
--- a/third_party/libwebrtc/modules/audio_coding/isac_bwinfo_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_coding/isac_bwinfo_gn/moz.build
@@ -176,16 +176,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_coding/isac_vad_gn/moz.build b/third_party/libwebrtc/modules/audio_coding/isac_vad_gn/moz.build
index a5cc52279a..1b599c5e51 100644
--- a/third_party/libwebrtc/modules/audio_coding/isac_vad_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_coding/isac_vad_gn/moz.build
@@ -187,7 +187,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -197,10 +196,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_coding/legacy_encoded_audio_frame_gn/moz.build b/third_party/libwebrtc/modules/audio_coding/legacy_encoded_audio_frame_gn/moz.build
index 78b7338ddd..b884cb8d99 100644
--- a/third_party/libwebrtc/modules/audio_coding/legacy_encoded_audio_frame_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_coding/legacy_encoded_audio_frame_gn/moz.build
@@ -195,7 +195,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -205,10 +204,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_coding/neteq/decision_logic.cc b/third_party/libwebrtc/modules/audio_coding/neteq/decision_logic.cc
index fd4f2f5a20..6648fd8709 100644
--- a/third_party/libwebrtc/modules/audio_coding/neteq/decision_logic.cc
+++ b/third_party/libwebrtc/modules/audio_coding/neteq/decision_logic.cc
@@ -95,10 +95,14 @@ DecisionLogic::DecisionLogic(NetEqController::Config config)
DecisionLogic::DecisionLogic(
NetEqController::Config config,
std::unique_ptr<DelayManager> delay_manager,
- std::unique_ptr<BufferLevelFilter> buffer_level_filter)
+ std::unique_ptr<BufferLevelFilter> buffer_level_filter,
+ std::unique_ptr<PacketArrivalHistory> packet_arrival_history)
: delay_manager_(std::move(delay_manager)),
buffer_level_filter_(std::move(buffer_level_filter)),
- packet_arrival_history_(config_.packet_history_size_ms),
+ packet_arrival_history_(packet_arrival_history
+ ? std::move(packet_arrival_history)
+ : std::make_unique<PacketArrivalHistory>(
+ config_.packet_history_size_ms)),
tick_timer_(config.tick_timer),
disallow_time_stretching_(!config.allow_time_stretching),
timescale_countdown_(
@@ -115,7 +119,7 @@ void DecisionLogic::SoftReset() {
time_stretched_cn_samples_ = 0;
delay_manager_->Reset();
buffer_level_filter_->Reset();
- packet_arrival_history_.Reset();
+ packet_arrival_history_->Reset();
}
void DecisionLogic::SetSampleRate(int fs_hz, size_t output_size_samples) {
@@ -124,7 +128,7 @@ void DecisionLogic::SetSampleRate(int fs_hz, size_t output_size_samples) {
fs_hz == 48000);
sample_rate_khz_ = fs_hz / 1000;
output_size_samples_ = output_size_samples;
- packet_arrival_history_.set_sample_rate(fs_hz);
+ packet_arrival_history_->set_sample_rate(fs_hz);
}
NetEq::Operation DecisionLogic::GetDecision(const NetEqStatus& status,
@@ -218,15 +222,15 @@ absl::optional<int> DecisionLogic::PacketArrived(
delay_manager_->SetPacketAudioLength(packet_length_samples_ * 1000 / fs_hz);
}
int64_t time_now_ms = tick_timer_->ticks() * tick_timer_->ms_per_tick();
- packet_arrival_history_.Insert(info.main_timestamp, time_now_ms);
- if (packet_arrival_history_.size() < 2) {
+ packet_arrival_history_->Insert(info.main_timestamp, time_now_ms);
+ if (packet_arrival_history_->size() < 2) {
// No meaningful delay estimate unless at least 2 packets have arrived.
return absl::nullopt;
}
int arrival_delay_ms =
- packet_arrival_history_.GetDelayMs(info.main_timestamp, time_now_ms);
+ packet_arrival_history_->GetDelayMs(info.main_timestamp, time_now_ms);
bool reordered =
- !packet_arrival_history_.IsNewestRtpTimestamp(info.main_timestamp);
+ !packet_arrival_history_->IsNewestRtpTimestamp(info.main_timestamp);
delay_manager_->Update(arrival_delay_ms, reordered);
return arrival_delay_ms;
}
@@ -306,10 +310,10 @@ NetEq::Operation DecisionLogic::ExpectedPacketAvailable(
!status.play_dtmf) {
if (config_.enable_stable_delay_mode) {
const int playout_delay_ms = GetPlayoutDelayMs(status);
- const int low_limit = TargetLevelMs();
- const int high_limit = low_limit +
- packet_arrival_history_.GetMaxDelayMs() +
- kDelayAdjustmentGranularityMs;
+ const int64_t low_limit = TargetLevelMs();
+ const int64_t high_limit = low_limit +
+ packet_arrival_history_->GetMaxDelayMs() +
+ kDelayAdjustmentGranularityMs;
if (playout_delay_ms >= high_limit * 4) {
return NetEq::Operation::kFastAccelerate;
}
@@ -460,7 +464,7 @@ int DecisionLogic::GetPlayoutDelayMs(
NetEqController::NetEqStatus status) const {
uint32_t playout_timestamp =
status.target_timestamp - status.sync_buffer_samples;
- return packet_arrival_history_.GetDelayMs(
+ return packet_arrival_history_->GetDelayMs(
playout_timestamp, tick_timer_->ticks() * tick_timer_->ms_per_tick());
}
diff --git a/third_party/libwebrtc/modules/audio_coding/neteq/decision_logic.h b/third_party/libwebrtc/modules/audio_coding/neteq/decision_logic.h
index d96fbecd6a..a6b02c69cd 100644
--- a/third_party/libwebrtc/modules/audio_coding/neteq/decision_logic.h
+++ b/third_party/libwebrtc/modules/audio_coding/neteq/decision_logic.h
@@ -27,9 +27,11 @@ namespace webrtc {
class DecisionLogic : public NetEqController {
public:
DecisionLogic(NetEqController::Config config);
- DecisionLogic(NetEqController::Config config,
- std::unique_ptr<DelayManager> delay_manager,
- std::unique_ptr<BufferLevelFilter> buffer_level_filter);
+ DecisionLogic(
+ NetEqController::Config config,
+ std::unique_ptr<DelayManager> delay_manager,
+ std::unique_ptr<BufferLevelFilter> buffer_level_filter,
+ std::unique_ptr<PacketArrivalHistory> packet_arrival_history = nullptr);
~DecisionLogic() override;
@@ -154,16 +156,16 @@ class DecisionLogic : public NetEqController {
struct Config {
Config();
- bool enable_stable_delay_mode = false;
- bool combine_concealment_decision = false;
+ bool enable_stable_delay_mode = true;
+ bool combine_concealment_decision = true;
int deceleration_target_level_offset_ms = 85;
int packet_history_size_ms = 2000;
- absl::optional<int> cng_timeout_ms;
+ absl::optional<int> cng_timeout_ms = 1000;
};
Config config_;
std::unique_ptr<DelayManager> delay_manager_;
std::unique_ptr<BufferLevelFilter> buffer_level_filter_;
- PacketArrivalHistory packet_arrival_history_;
+ std::unique_ptr<PacketArrivalHistory> packet_arrival_history_;
const TickTimer* tick_timer_;
int sample_rate_khz_;
size_t output_size_samples_;
diff --git a/third_party/libwebrtc/modules/audio_coding/neteq/decision_logic_unittest.cc b/third_party/libwebrtc/modules/audio_coding/neteq/decision_logic_unittest.cc
index 97e20dd883..9e9902af50 100644
--- a/third_party/libwebrtc/modules/audio_coding/neteq/decision_logic_unittest.cc
+++ b/third_party/libwebrtc/modules/audio_coding/neteq/decision_logic_unittest.cc
@@ -18,6 +18,7 @@
#include "modules/audio_coding/neteq/delay_manager.h"
#include "modules/audio_coding/neteq/mock/mock_buffer_level_filter.h"
#include "modules/audio_coding/neteq/mock/mock_delay_manager.h"
+#include "modules/audio_coding/neteq/mock/mock_packet_arrival_history.h"
#include "test/field_trial.h"
#include "test/gtest.h"
@@ -47,6 +48,7 @@ NetEqController::NetEqStatus CreateNetEqStatus(NetEq::Mode last_mode,
return status;
}
+using ::testing::_;
using ::testing::Return;
} // namespace
@@ -54,8 +56,6 @@ using ::testing::Return;
class DecisionLogicTest : public ::testing::Test {
protected:
DecisionLogicTest() {
- test::ScopedFieldTrials trials(
- "WebRTC-Audio-NetEqDecisionLogicConfig/cng_timeout_ms:1000/");
NetEqController::Config config;
config.tick_timer = &tick_timer_;
config.allow_time_stretching = true;
@@ -64,8 +64,11 @@ class DecisionLogicTest : public ::testing::Test {
mock_delay_manager_ = delay_manager.get();
auto buffer_level_filter = std::make_unique<MockBufferLevelFilter>();
mock_buffer_level_filter_ = buffer_level_filter.get();
+ auto packet_arrival_history = std::make_unique<MockPacketArrivalHistory>();
+ mock_packet_arrival_history_ = packet_arrival_history.get();
decision_logic_ = std::make_unique<DecisionLogic>(
- config, std::move(delay_manager), std::move(buffer_level_filter));
+ config, std::move(delay_manager), std::move(buffer_level_filter),
+ std::move(packet_arrival_history));
decision_logic_->SetSampleRate(kSampleRate, kOutputSizeSamples);
}
@@ -73,13 +76,16 @@ class DecisionLogicTest : public ::testing::Test {
std::unique_ptr<DecisionLogic> decision_logic_;
MockDelayManager* mock_delay_manager_;
MockBufferLevelFilter* mock_buffer_level_filter_;
+ MockPacketArrivalHistory* mock_packet_arrival_history_;
};
TEST_F(DecisionLogicTest, NormalOperation) {
EXPECT_CALL(*mock_delay_manager_, TargetDelayMs())
.WillRepeatedly(Return(100));
- EXPECT_CALL(*mock_buffer_level_filter_, filtered_current_level())
- .WillRepeatedly(Return(90 * kSamplesPerMs));
+ EXPECT_CALL(*mock_packet_arrival_history_, GetDelayMs(_, _))
+ .WillRepeatedly(Return(100));
+ EXPECT_CALL(*mock_packet_arrival_history_, GetMaxDelayMs())
+ .WillRepeatedly(Return(0));
bool reset_decoder = false;
tick_timer_.Increment(kMinTimescaleInterval + 1);
@@ -92,8 +98,10 @@ TEST_F(DecisionLogicTest, NormalOperation) {
TEST_F(DecisionLogicTest, Accelerate) {
EXPECT_CALL(*mock_delay_manager_, TargetDelayMs())
.WillRepeatedly(Return(100));
- EXPECT_CALL(*mock_buffer_level_filter_, filtered_current_level())
- .WillRepeatedly(Return(110 * kSamplesPerMs));
+ EXPECT_CALL(*mock_packet_arrival_history_, GetDelayMs(_, _))
+ .WillRepeatedly(Return(150));
+ EXPECT_CALL(*mock_packet_arrival_history_, GetMaxDelayMs())
+ .WillRepeatedly(Return(0));
bool reset_decoder = false;
tick_timer_.Increment(kMinTimescaleInterval + 1);
@@ -106,8 +114,10 @@ TEST_F(DecisionLogicTest, Accelerate) {
TEST_F(DecisionLogicTest, FastAccelerate) {
EXPECT_CALL(*mock_delay_manager_, TargetDelayMs())
.WillRepeatedly(Return(100));
- EXPECT_CALL(*mock_buffer_level_filter_, filtered_current_level())
- .WillRepeatedly(Return(400 * kSamplesPerMs));
+ EXPECT_CALL(*mock_packet_arrival_history_, GetDelayMs(_, _))
+ .WillRepeatedly(Return(500));
+ EXPECT_CALL(*mock_packet_arrival_history_, GetMaxDelayMs())
+ .WillRepeatedly(Return(0));
bool reset_decoder = false;
tick_timer_.Increment(kMinTimescaleInterval + 1);
@@ -120,8 +130,10 @@ TEST_F(DecisionLogicTest, FastAccelerate) {
TEST_F(DecisionLogicTest, PreemptiveExpand) {
EXPECT_CALL(*mock_delay_manager_, TargetDelayMs())
.WillRepeatedly(Return(100));
- EXPECT_CALL(*mock_buffer_level_filter_, filtered_current_level())
- .WillRepeatedly(Return(50 * kSamplesPerMs));
+ EXPECT_CALL(*mock_packet_arrival_history_, GetDelayMs(_, _))
+ .WillRepeatedly(Return(50));
+ EXPECT_CALL(*mock_packet_arrival_history_, GetMaxDelayMs())
+ .WillRepeatedly(Return(0));
bool reset_decoder = false;
tick_timer_.Increment(kMinTimescaleInterval + 1);
@@ -131,20 +143,6 @@ TEST_F(DecisionLogicTest, PreemptiveExpand) {
EXPECT_FALSE(reset_decoder);
}
-TEST_F(DecisionLogicTest, DecelerationTargetLevelOffset) {
- EXPECT_CALL(*mock_delay_manager_, TargetDelayMs())
- .WillRepeatedly(Return(500));
- EXPECT_CALL(*mock_buffer_level_filter_, filtered_current_level())
- .WillRepeatedly(Return(400 * kSamplesPerMs));
-
- bool reset_decoder = false;
- tick_timer_.Increment(kMinTimescaleInterval + 1);
- EXPECT_EQ(decision_logic_->GetDecision(
- CreateNetEqStatus(NetEq::Mode::kNormal, 400), &reset_decoder),
- NetEq::Operation::kPreemptiveExpand);
- EXPECT_FALSE(reset_decoder);
-}
-
TEST_F(DecisionLogicTest, PostponeDecodeAfterExpand) {
EXPECT_CALL(*mock_delay_manager_, TargetDelayMs())
.WillRepeatedly(Return(500));
@@ -170,7 +168,7 @@ TEST_F(DecisionLogicTest, TimeStrechComfortNoise) {
{
bool reset_decoder = false;
// Below target window.
- auto status = CreateNetEqStatus(NetEq::Mode::kCodecInternalCng, 400);
+ auto status = CreateNetEqStatus(NetEq::Mode::kCodecInternalCng, 200);
status.generated_noise_samples = 400 * kSamplesPerMs;
status.next_packet->timestamp =
status.target_timestamp + 400 * kSamplesPerMs;
@@ -189,18 +187,6 @@ TEST_F(DecisionLogicTest, TimeStrechComfortNoise) {
EXPECT_EQ(decision_logic_->GetDecision(status, &reset_decoder),
NetEq::Operation::kNormal);
EXPECT_FALSE(reset_decoder);
-
- // The buffer level filter should be adjusted with the number of samples
- // that was skipped.
- int timestamp_leap = status.next_packet->timestamp -
- status.target_timestamp -
- status.generated_noise_samples;
- EXPECT_CALL(*mock_buffer_level_filter_,
- Update(400 * kSamplesPerMs, timestamp_leap));
- EXPECT_EQ(decision_logic_->GetDecision(
- CreateNetEqStatus(NetEq::Mode::kNormal, 400), &reset_decoder),
- NetEq::Operation::kNormal);
- EXPECT_FALSE(reset_decoder);
}
}
diff --git a/third_party/libwebrtc/modules/audio_coding/neteq/mock/mock_packet_arrival_history.h b/third_party/libwebrtc/modules/audio_coding/neteq/mock/mock_packet_arrival_history.h
new file mode 100644
index 0000000000..1b2080cd94
--- /dev/null
+++ b/third_party/libwebrtc/modules/audio_coding/neteq/mock/mock_packet_arrival_history.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2023 The WebRTC Project Authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_AUDIO_CODING_NETEQ_MOCK_MOCK_PACKET_ARRIVAL_HISTORY_H_
+#define MODULES_AUDIO_CODING_NETEQ_MOCK_MOCK_PACKET_ARRIVAL_HISTORY_H_
+
+#include "modules/audio_coding/neteq/packet_arrival_history.h"
+#include "test/gmock.h"
+
+namespace webrtc {
+
+class MockPacketArrivalHistory : public PacketArrivalHistory {
+ public:
+ MockPacketArrivalHistory() : PacketArrivalHistory(0) {}
+
+ MOCK_METHOD(int,
+ GetDelayMs,
+ (uint32_t rtp_timestamp, int64_t time_ms),
+ (const override));
+ MOCK_METHOD(int, GetMaxDelayMs, (), (const override));
+};
+
+} // namespace webrtc
+
+#endif // MODULES_AUDIO_CODING_NETEQ_MOCK_MOCK_PACKET_ARRIVAL_HISTORY_H_
diff --git a/third_party/libwebrtc/modules/audio_coding/neteq/mock/mock_packet_buffer.h b/third_party/libwebrtc/modules/audio_coding/neteq/mock/mock_packet_buffer.h
index 48357ea466..fa44f606fc 100644
--- a/third_party/libwebrtc/modules/audio_coding/neteq/mock/mock_packet_buffer.h
+++ b/third_party/libwebrtc/modules/audio_coding/neteq/mock/mock_packet_buffer.h
@@ -18,39 +18,15 @@ namespace webrtc {
class MockPacketBuffer : public PacketBuffer {
public:
- MockPacketBuffer(size_t max_number_of_packets, const TickTimer* tick_timer)
- : PacketBuffer(max_number_of_packets, tick_timer) {}
+ MockPacketBuffer(size_t max_number_of_packets,
+ const TickTimer* tick_timer,
+ StatisticsCalculator* stats)
+ : PacketBuffer(max_number_of_packets, tick_timer, stats) {}
~MockPacketBuffer() override { Die(); }
MOCK_METHOD(void, Die, ());
- MOCK_METHOD(void, Flush, (StatisticsCalculator * stats), (override));
- MOCK_METHOD(void,
- PartialFlush,
- (int target_level_ms,
- size_t sample_rate,
- size_t last_decoded_length,
- StatisticsCalculator* stats),
- (override));
+ MOCK_METHOD(void, Flush, (), (override));
MOCK_METHOD(bool, Empty, (), (const, override));
- MOCK_METHOD(int,
- InsertPacket,
- (Packet && packet,
- StatisticsCalculator* stats,
- size_t last_decoded_length,
- size_t sample_rate,
- int target_level_ms,
- const DecoderDatabase& decoder_database),
- (override));
- MOCK_METHOD(int,
- InsertPacketList,
- (PacketList * packet_list,
- const DecoderDatabase& decoder_database,
- absl::optional<uint8_t>* current_rtp_payload_type,
- absl::optional<uint8_t>* current_cng_rtp_payload_type,
- StatisticsCalculator* stats,
- size_t last_decoded_length,
- size_t sample_rate,
- int target_level_ms),
- (override));
+ MOCK_METHOD(int, InsertPacket, (Packet && packet), (override));
MOCK_METHOD(int,
NextTimestamp,
(uint32_t * next_timestamp),
@@ -61,19 +37,14 @@ class MockPacketBuffer : public PacketBuffer {
(const, override));
MOCK_METHOD(const Packet*, PeekNextPacket, (), (const, override));
MOCK_METHOD(absl::optional<Packet>, GetNextPacket, (), (override));
- MOCK_METHOD(int,
- DiscardNextPacket,
- (StatisticsCalculator * stats),
- (override));
+ MOCK_METHOD(int, DiscardNextPacket, (), (override));
MOCK_METHOD(void,
DiscardOldPackets,
- (uint32_t timestamp_limit,
- uint32_t horizon_samples,
- StatisticsCalculator* stats),
+ (uint32_t timestamp_limit, uint32_t horizon_samples),
(override));
MOCK_METHOD(void,
DiscardAllOldPackets,
- (uint32_t timestamp_limit, StatisticsCalculator* stats),
+ (uint32_t timestamp_limit),
(override));
MOCK_METHOD(size_t, NumPacketsInBuffer, (), (const, override));
};
diff --git a/third_party/libwebrtc/modules/audio_coding/neteq/neteq_impl.cc b/third_party/libwebrtc/modules/audio_coding/neteq/neteq_impl.cc
index 52e8cbad3a..e5c8bf6c08 100644
--- a/third_party/libwebrtc/modules/audio_coding/neteq/neteq_impl.cc
+++ b/third_party/libwebrtc/modules/audio_coding/neteq/neteq_impl.cc
@@ -70,6 +70,62 @@ std::unique_ptr<NetEqController> CreateNetEqController(
return controller_factory.CreateNetEqController(config);
}
+void SetAudioFrameActivityAndType(bool vad_enabled,
+ NetEqImpl::OutputType type,
+ AudioFrame::VADActivity last_vad_activity,
+ AudioFrame* audio_frame) {
+ switch (type) {
+ case NetEqImpl::OutputType::kNormalSpeech: {
+ audio_frame->speech_type_ = AudioFrame::kNormalSpeech;
+ audio_frame->vad_activity_ = AudioFrame::kVadActive;
+ break;
+ }
+ case NetEqImpl::OutputType::kVadPassive: {
+ // This should only be reached if the VAD is enabled.
+ RTC_DCHECK(vad_enabled);
+ audio_frame->speech_type_ = AudioFrame::kNormalSpeech;
+ audio_frame->vad_activity_ = AudioFrame::kVadPassive;
+ break;
+ }
+ case NetEqImpl::OutputType::kCNG: {
+ audio_frame->speech_type_ = AudioFrame::kCNG;
+ audio_frame->vad_activity_ = AudioFrame::kVadPassive;
+ break;
+ }
+ case NetEqImpl::OutputType::kPLC: {
+ audio_frame->speech_type_ = AudioFrame::kPLC;
+ audio_frame->vad_activity_ = last_vad_activity;
+ break;
+ }
+ case NetEqImpl::OutputType::kPLCCNG: {
+ audio_frame->speech_type_ = AudioFrame::kPLCCNG;
+ audio_frame->vad_activity_ = AudioFrame::kVadPassive;
+ break;
+ }
+ case NetEqImpl::OutputType::kCodecPLC: {
+ audio_frame->speech_type_ = AudioFrame::kCodecPLC;
+ audio_frame->vad_activity_ = last_vad_activity;
+ break;
+ }
+ default:
+ RTC_DCHECK_NOTREACHED();
+ }
+ if (!vad_enabled) {
+ // Always set kVadUnknown when receive VAD is inactive.
+ audio_frame->vad_activity_ = AudioFrame::kVadUnknown;
+ }
+}
+
+// Returns true if both payload types are known to the decoder database, and
+// have the same sample rate.
+bool EqualSampleRates(uint8_t pt1,
+ uint8_t pt2,
+ const DecoderDatabase& decoder_database) {
+ auto* di1 = decoder_database.GetDecoderInfo(pt1);
+ auto* di2 = decoder_database.GetDecoderInfo(pt2);
+ return di1 && di2 && di1->SampleRateHz() == di2->SampleRateHz();
+}
+
} // namespace
NetEqImpl::Dependencies::Dependencies(
@@ -84,8 +140,9 @@ NetEqImpl::Dependencies::Dependencies(
new DecoderDatabase(decoder_factory, config.codec_pair_id)),
dtmf_buffer(new DtmfBuffer(config.sample_rate_hz)),
dtmf_tone_generator(new DtmfToneGenerator),
- packet_buffer(
- new PacketBuffer(config.max_packets_in_buffer, tick_timer.get())),
+ packet_buffer(new PacketBuffer(config.max_packets_in_buffer,
+ tick_timer.get(),
+ stats.get())),
neteq_controller(
CreateNetEqController(controller_factory,
config.min_delay_ms,
@@ -182,54 +239,6 @@ void NetEqImpl::InsertEmptyPacket(const RTPHeader& rtp_header) {
controller_->RegisterEmptyPacket();
}
-namespace {
-void SetAudioFrameActivityAndType(bool vad_enabled,
- NetEqImpl::OutputType type,
- AudioFrame::VADActivity last_vad_activity,
- AudioFrame* audio_frame) {
- switch (type) {
- case NetEqImpl::OutputType::kNormalSpeech: {
- audio_frame->speech_type_ = AudioFrame::kNormalSpeech;
- audio_frame->vad_activity_ = AudioFrame::kVadActive;
- break;
- }
- case NetEqImpl::OutputType::kVadPassive: {
- // This should only be reached if the VAD is enabled.
- RTC_DCHECK(vad_enabled);
- audio_frame->speech_type_ = AudioFrame::kNormalSpeech;
- audio_frame->vad_activity_ = AudioFrame::kVadPassive;
- break;
- }
- case NetEqImpl::OutputType::kCNG: {
- audio_frame->speech_type_ = AudioFrame::kCNG;
- audio_frame->vad_activity_ = AudioFrame::kVadPassive;
- break;
- }
- case NetEqImpl::OutputType::kPLC: {
- audio_frame->speech_type_ = AudioFrame::kPLC;
- audio_frame->vad_activity_ = last_vad_activity;
- break;
- }
- case NetEqImpl::OutputType::kPLCCNG: {
- audio_frame->speech_type_ = AudioFrame::kPLCCNG;
- audio_frame->vad_activity_ = AudioFrame::kVadPassive;
- break;
- }
- case NetEqImpl::OutputType::kCodecPLC: {
- audio_frame->speech_type_ = AudioFrame::kCodecPLC;
- audio_frame->vad_activity_ = last_vad_activity;
- break;
- }
- default:
- RTC_DCHECK_NOTREACHED();
- }
- if (!vad_enabled) {
- // Always set kVadUnknown when receive VAD is inactive.
- audio_frame->vad_activity_ = AudioFrame::kVadUnknown;
- }
-}
-} // namespace
-
int NetEqImpl::GetAudio(AudioFrame* audio_frame,
bool* muted,
int* current_sample_rate_hz,
@@ -265,7 +274,7 @@ void NetEqImpl::SetCodecs(const std::map<int, SdpAudioFormat>& codecs) {
const std::vector<int> changed_payload_types =
decoder_database_->SetCodecs(codecs);
for (const int pt : changed_payload_types) {
- packet_buffer_->DiscardPacketsWithPayloadType(pt, stats_.get());
+ packet_buffer_->DiscardPacketsWithPayloadType(pt);
}
}
@@ -283,8 +292,7 @@ int NetEqImpl::RemovePayloadType(uint8_t rtp_payload_type) {
MutexLock lock(&mutex_);
int ret = decoder_database_->Remove(rtp_payload_type);
if (ret == DecoderDatabase::kOK || ret == DecoderDatabase::kDecoderNotFound) {
- packet_buffer_->DiscardPacketsWithPayloadType(rtp_payload_type,
- stats_.get());
+ packet_buffer_->DiscardPacketsWithPayloadType(rtp_payload_type);
return kOK;
}
return kFail;
@@ -441,7 +449,7 @@ absl::optional<NetEq::DecoderFormat> NetEqImpl::GetDecoderFormat(
void NetEqImpl::FlushBuffers() {
MutexLock lock(&mutex_);
RTC_LOG(LS_VERBOSE) << "FlushBuffers";
- packet_buffer_->Flush(stats_.get());
+ packet_buffer_->Flush();
RTC_DCHECK(sync_buffer_.get());
RTC_DCHECK(expand_.get());
sync_buffer_->Flush();
@@ -542,7 +550,7 @@ int NetEqImpl::InsertPacketInternal(const RTPHeader& rtp_header,
// the packet has been successfully inserted into the packet buffer.
// Flush the packet buffer and DTMF buffer.
- packet_buffer_->Flush(stats_.get());
+ packet_buffer_->Flush();
dtmf_buffer_->Flush();
// Update audio buffer timestamp.
@@ -681,26 +689,25 @@ int NetEqImpl::InsertPacketInternal(const RTPHeader& rtp_header,
number_of_primary_packets);
}
- // Insert packets in buffer.
- const int target_level_ms = controller_->TargetLevelMs();
- const int ret = packet_buffer_->InsertPacketList(
- &parsed_packet_list, *decoder_database_, &current_rtp_payload_type_,
- &current_cng_rtp_payload_type_, stats_.get(), decoder_frame_length_,
- last_output_sample_rate_hz_, target_level_ms);
bool buffer_flush_occured = false;
- if (ret == PacketBuffer::kFlushed) {
+ for (Packet& packet : parsed_packet_list) {
+ if (MaybeChangePayloadType(packet.payload_type)) {
+ packet_buffer_->Flush();
+ buffer_flush_occured = true;
+ }
+ int return_val = packet_buffer_->InsertPacket(std::move(packet));
+ if (return_val == PacketBuffer::kFlushed) {
+ buffer_flush_occured = true;
+ } else if (return_val != PacketBuffer::kOK) {
+ // An error occurred.
+ return kOtherError;
+ }
+ }
+
+ if (buffer_flush_occured) {
// Reset DSP timestamp etc. if packet buffer flushed.
new_codec_ = true;
update_sample_rate_and_channels = true;
- buffer_flush_occured = true;
- } else if (ret == PacketBuffer::kPartialFlush) {
- // Forward sync buffer timestamp
- timestamp_ = packet_buffer_->PeekNextPacket()->timestamp;
- sync_buffer_->IncreaseEndTimestamp(timestamp_ -
- sync_buffer_->end_timestamp());
- buffer_flush_occured = true;
- } else if (ret != PacketBuffer::kOK) {
- return kOtherError;
}
if (first_packet_) {
@@ -767,6 +774,31 @@ int NetEqImpl::InsertPacketInternal(const RTPHeader& rtp_header,
return 0;
}
+bool NetEqImpl::MaybeChangePayloadType(uint8_t payload_type) {
+ bool changed = false;
+ if (decoder_database_->IsComfortNoise(payload_type)) {
+ if (current_cng_rtp_payload_type_ &&
+ *current_cng_rtp_payload_type_ != payload_type) {
+ // New CNG payload type implies new codec type.
+ current_rtp_payload_type_ = absl::nullopt;
+ changed = true;
+ }
+ current_cng_rtp_payload_type_ = payload_type;
+ } else if (!decoder_database_->IsDtmf(payload_type)) {
+ // This must be speech.
+ if ((current_rtp_payload_type_ &&
+ *current_rtp_payload_type_ != payload_type) ||
+ (current_cng_rtp_payload_type_ &&
+ !EqualSampleRates(payload_type, *current_cng_rtp_payload_type_,
+ *decoder_database_))) {
+ current_cng_rtp_payload_type_ = absl::nullopt;
+ changed = true;
+ }
+ current_rtp_payload_type_ = payload_type;
+ }
+ return changed;
+}
+
int NetEqImpl::GetAudioInternal(AudioFrame* audio_frame,
bool* muted,
absl::optional<Operation> action_override) {
@@ -1037,8 +1069,7 @@ int NetEqImpl::GetDecision(Operation* operation,
uint32_t end_timestamp = sync_buffer_->end_timestamp();
if (!new_codec_) {
const uint32_t five_seconds_samples = 5 * fs_hz_;
- packet_buffer_->DiscardOldPackets(end_timestamp, five_seconds_samples,
- stats_.get());
+ packet_buffer_->DiscardOldPackets(end_timestamp, five_seconds_samples);
}
const Packet* packet = packet_buffer_->PeekNextPacket();
@@ -1058,14 +1089,12 @@ int NetEqImpl::GetDecision(Operation* operation,
(end_timestamp >= packet->timestamp ||
end_timestamp + generated_noise_samples > packet->timestamp)) {
// Don't use this packet, discard it.
- if (packet_buffer_->DiscardNextPacket(stats_.get()) !=
- PacketBuffer::kOK) {
+ if (packet_buffer_->DiscardNextPacket() != PacketBuffer::kOK) {
RTC_DCHECK_NOTREACHED(); // Must be ok by design.
}
// Check buffer again.
if (!new_codec_) {
- packet_buffer_->DiscardOldPackets(end_timestamp, 5 * fs_hz_,
- stats_.get());
+ packet_buffer_->DiscardOldPackets(end_timestamp, 5 * fs_hz_);
}
packet = packet_buffer_->PeekNextPacket();
}
@@ -2024,7 +2053,7 @@ int NetEqImpl::ExtractPackets(size_t required_samples,
// we could end up in the situation where we never decode anything, since
// all incoming packets are considered too old but the buffer will also
// never be flooded and flushed.
- packet_buffer_->DiscardAllOldPackets(timestamp_, stats_.get());
+ packet_buffer_->DiscardAllOldPackets(timestamp_);
}
return rtc::dchecked_cast<int>(extracted_samples);
diff --git a/third_party/libwebrtc/modules/audio_coding/neteq/neteq_impl.h b/third_party/libwebrtc/modules/audio_coding/neteq/neteq_impl.h
index f27738bcbf..f8f2b06410 100644
--- a/third_party/libwebrtc/modules/audio_coding/neteq/neteq_impl.h
+++ b/third_party/libwebrtc/modules/audio_coding/neteq/neteq_impl.h
@@ -27,6 +27,7 @@
#include "modules/audio_coding/neteq/audio_multi_vector.h"
#include "modules/audio_coding/neteq/expand_uma_logger.h"
#include "modules/audio_coding/neteq/packet.h"
+#include "modules/audio_coding/neteq/packet_buffer.h"
#include "modules/audio_coding/neteq/random_vector.h"
#include "modules/audio_coding/neteq/statistics_calculator.h"
#include "rtc_base/synchronization/mutex.h"
@@ -46,7 +47,6 @@ class Expand;
class Merge;
class NackTracker;
class Normal;
-class PacketBuffer;
class RedPayloadSplitter;
class PostDecodeVad;
class PreemptiveExpand;
@@ -215,6 +215,12 @@ class NetEqImpl : public webrtc::NetEq {
rtc::ArrayView<const uint8_t> payload)
RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
+ // Returns true if the payload type changed (this should be followed by
+ // resetting various state). Returns false if the current payload type is
+ // unknown or equal to `payload_type`.
+ bool MaybeChangePayloadType(uint8_t payload_type)
+ RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
+
// Delivers 10 ms of audio data. The data is written to `audio_frame`.
// Returns 0 on success, otherwise an error code.
int GetAudioInternal(AudioFrame* audio_frame,
diff --git a/third_party/libwebrtc/modules/audio_coding/neteq/neteq_impl_unittest.cc b/third_party/libwebrtc/modules/audio_coding/neteq/neteq_impl_unittest.cc
index e61cd52502..8309dafb58 100644
--- a/third_party/libwebrtc/modules/audio_coding/neteq/neteq_impl_unittest.cc
+++ b/third_party/libwebrtc/modules/audio_coding/neteq/neteq_impl_unittest.cc
@@ -108,8 +108,8 @@ class NetEqImplTest : public ::testing::Test {
dtmf_tone_generator_ = deps.dtmf_tone_generator.get();
if (use_mock_packet_buffer_) {
- std::unique_ptr<MockPacketBuffer> mock(
- new MockPacketBuffer(config_.max_packets_in_buffer, tick_timer_));
+ std::unique_ptr<MockPacketBuffer> mock(new MockPacketBuffer(
+ config_.max_packets_in_buffer, tick_timer_, deps.stats.get()));
mock_packet_buffer_ = mock.get();
deps.packet_buffer = std::move(mock);
}
@@ -120,7 +120,6 @@ class NetEqImplTest : public ::testing::Test {
mock_neteq_controller_ = mock.get();
deps.neteq_controller = std::move(mock);
} else {
- deps.stats = std::make_unique<StatisticsCalculator>();
NetEqController::Config controller_config;
controller_config.tick_timer = tick_timer_;
controller_config.base_min_delay_ms = config_.min_delay_ms;
@@ -329,15 +328,10 @@ TEST_F(NetEqImplTest, InsertPacket) {
// Expectations for packet buffer.
EXPECT_CALL(*mock_packet_buffer_, Empty())
.WillOnce(Return(false)); // Called once after first packet is inserted.
- EXPECT_CALL(*mock_packet_buffer_, Flush(_)).Times(1);
- EXPECT_CALL(*mock_packet_buffer_, InsertPacketList(_, _, _, _, _, _, _, _))
+ EXPECT_CALL(*mock_packet_buffer_, Flush()).Times(1);
+ EXPECT_CALL(*mock_packet_buffer_, InsertPacket(_))
.Times(2)
- .WillRepeatedly(DoAll(SetArgPointee<2>(kPayloadType),
- WithArg<0>(Invoke(DeletePacketsAndReturnOk))));
- // SetArgPointee<2>(kPayloadType) means that the third argument (zero-based
- // index) is a pointer, and the variable pointed to is set to kPayloadType.
- // Also invoke the function DeletePacketsAndReturnOk to properly delete all
- // packets in the list (to avoid memory leaks in the test).
+ .WillRepeatedly(Return(PacketBuffer::kOK));
EXPECT_CALL(*mock_packet_buffer_, PeekNextPacket())
.Times(1)
.WillOnce(Return(&fake_packet));
@@ -1246,12 +1240,15 @@ TEST_F(NetEqImplTest, UnsupportedDecoder) {
EXPECT_EQ(kChannels, output.num_channels_);
EXPECT_THAT(output.packet_infos_, IsEmpty());
- // Second call to GetAudio will decode the packet that is ok. No errors are
- // expected.
- EXPECT_EQ(NetEq::kOK, neteq_->GetAudio(&output, &muted));
- EXPECT_EQ(kExpectedOutputSize, output.samples_per_channel_ * kChannels);
- EXPECT_EQ(kChannels, output.num_channels_);
- EXPECT_THAT(output.packet_infos_, SizeIs(1));
+ // Call GetAudio until the next packet is decoded.
+ int calls = 0;
+ int kTimeout = 10;
+ while (output.packet_infos_.empty() && calls < kTimeout) {
+ EXPECT_EQ(NetEq::kOK, neteq_->GetAudio(&output, &muted));
+ EXPECT_EQ(kExpectedOutputSize, output.samples_per_channel_ * kChannels);
+ EXPECT_EQ(kChannels, output.num_channels_);
+ }
+ EXPECT_LT(calls, kTimeout);
// Die isn't called through NiceMock (since it's called by the
// MockAudioDecoder constructor), so it needs to be mocked explicitly.
@@ -1640,6 +1637,74 @@ TEST_F(NetEqImplTest, NoCrashWith1000Channels) {
}
}
+// The test first inserts a packet with narrow-band CNG, then a packet with
+// wide-band speech. The expected behavior is to detect a change in sample rate,
+// even though no speech packet has been inserted before, and flush out the CNG
+// packet.
+TEST_F(NetEqImplTest, CngFirstThenSpeechWithNewSampleRate) {
+ UseNoMocks();
+ CreateInstance();
+ constexpr int kCnPayloadType = 7;
+ neteq_->RegisterPayloadType(kCnPayloadType, SdpAudioFormat("cn", 8000, 1));
+ constexpr int kSpeechPayloadType = 8;
+ neteq_->RegisterPayloadType(kSpeechPayloadType,
+ SdpAudioFormat("l16", 16000, 1));
+
+ RTPHeader header;
+ header.payloadType = kCnPayloadType;
+ uint8_t payload[320] = {0};
+
+ EXPECT_EQ(neteq_->InsertPacket(header, payload), NetEq::kOK);
+ EXPECT_EQ(neteq_->GetLifetimeStatistics().packets_discarded, 0u);
+
+ header.payloadType = kSpeechPayloadType;
+ header.timestamp += 160;
+ EXPECT_EQ(neteq_->InsertPacket(header, payload), NetEq::kOK);
+ // CN packet should be discarded, since it does not match the
+ // new speech sample rate.
+ EXPECT_EQ(neteq_->GetLifetimeStatistics().packets_discarded, 1u);
+
+ // Next decoded packet should be speech.
+ AudioFrame audio_frame;
+ bool muted;
+ EXPECT_EQ(neteq_->GetAudio(&audio_frame, &muted), NetEq::kOK);
+ EXPECT_EQ(audio_frame.sample_rate_hz(), 16000);
+ EXPECT_EQ(audio_frame.speech_type_, AudioFrame::SpeechType::kNormalSpeech);
+}
+
+TEST_F(NetEqImplTest, InsertPacketChangePayloadType) {
+ UseNoMocks();
+ CreateInstance();
+ constexpr int kPcmuPayloadType = 7;
+ neteq_->RegisterPayloadType(kPcmuPayloadType,
+ SdpAudioFormat("pcmu", 8000, 1));
+ constexpr int kPcmaPayloadType = 8;
+ neteq_->RegisterPayloadType(kPcmaPayloadType,
+ SdpAudioFormat("pcma", 8000, 1));
+
+ RTPHeader header;
+ header.payloadType = kPcmuPayloadType;
+ header.timestamp = 1234;
+ uint8_t payload[160] = {0};
+
+ EXPECT_EQ(neteq_->InsertPacket(header, payload), NetEq::kOK);
+ EXPECT_EQ(neteq_->GetLifetimeStatistics().packets_discarded, 0u);
+
+ header.payloadType = kPcmaPayloadType;
+ header.timestamp += 80;
+ EXPECT_EQ(neteq_->InsertPacket(header, payload), NetEq::kOK);
+ // The previous packet should be discarded since the codec changed.
+ EXPECT_EQ(neteq_->GetLifetimeStatistics().packets_discarded, 1u);
+
+ // Next decoded packet should be speech.
+ AudioFrame audio_frame;
+ bool muted;
+ EXPECT_EQ(neteq_->GetAudio(&audio_frame, &muted), NetEq::kOK);
+ EXPECT_EQ(audio_frame.sample_rate_hz(), 8000);
+ EXPECT_EQ(audio_frame.speech_type_, AudioFrame::SpeechType::kNormalSpeech);
+ // TODO(jakobi): check active decoder.
+}
+
class Decoder120ms : public AudioDecoder {
public:
Decoder120ms(int sample_rate_hz, SpeechType speech_type)
diff --git a/third_party/libwebrtc/modules/audio_coding/neteq/neteq_network_stats_unittest.cc b/third_party/libwebrtc/modules/audio_coding/neteq/neteq_network_stats_unittest.cc
index a669ad727e..da516982c7 100644
--- a/third_party/libwebrtc/modules/audio_coding/neteq/neteq_network_stats_unittest.cc
+++ b/third_party/libwebrtc/modules/audio_coding/neteq/neteq_network_stats_unittest.cc
@@ -273,15 +273,16 @@ class NetEqNetworkStatsTest {
// Next we introduce packet losses.
SetPacketLossRate(0.1);
- expects.stats_ref.expand_rate = expects.stats_ref.speech_expand_rate = 898;
+ expects.expand_rate = expects.speech_expand_rate = kLargerThan;
RunTest(50, expects);
// Next we enable FEC.
decoder_->set_fec_enabled(true);
// If FEC fills in the lost packets, no packet loss will be counted.
+ expects.expand_rate = expects.speech_expand_rate = kEqual;
expects.stats_ref.expand_rate = expects.stats_ref.speech_expand_rate = 0;
- expects.stats_ref.secondary_decoded_rate = 2006;
- expects.stats_ref.secondary_discarded_rate = 14336;
+ expects.secondary_decoded_rate = kLargerThan;
+ expects.secondary_discarded_rate = kLargerThan;
RunTest(50, expects);
}
diff --git a/third_party/libwebrtc/modules/audio_coding/neteq/neteq_unittest.cc b/third_party/libwebrtc/modules/audio_coding/neteq/neteq_unittest.cc
index 77bd5b5035..aec7e580ec 100644
--- a/third_party/libwebrtc/modules/audio_coding/neteq/neteq_unittest.cc
+++ b/third_party/libwebrtc/modules/audio_coding/neteq/neteq_unittest.cc
@@ -31,7 +31,6 @@
#include "modules/include/module_common_types_public.h"
#include "modules/rtp_rtcp/include/rtcp_statistics.h"
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
-#include "rtc_base/ignore_wundef.h"
#include "rtc_base/message_digest.h"
#include "rtc_base/numerics/safe_conversions.h"
#include "rtc_base/strings/string_builder.h"
@@ -77,11 +76,11 @@ TEST_F(NetEqDecodingTest, MAYBE_TestOpusBitExactness) {
webrtc::test::ResourcePath("audio_coding/neteq_opus", "rtp");
const std::string output_checksum =
- "fec6827bb9ee0b21770bbbb4a3a6f8823bf537dc|"
- "3610cc7be4b3407b9c273b1299ab7f8f47cca96b";
+ "2efdbea92c3fb2383c59f89d881efec9f94001d0|"
+ "a6831b946b59913852ae3e53f99fa8f209bb23cd";
const std::string network_stats_checksum =
- "3d043e47e5f4bb81d37e7bce8c44bf802965c853|"
+ "dfaf4399fd60293405290476ccf1c05c807c71a0|"
"076662525572dba753b11578330bd491923f7f5e";
DecodeAndCompare(input_rtp_file, output_checksum, network_stats_checksum,
@@ -99,11 +98,11 @@ TEST_F(NetEqDecodingTest, MAYBE_TestOpusDtxBitExactness) {
webrtc::test::ResourcePath("audio_coding/neteq_opus_dtx", "rtp");
const std::string output_checksum =
- "b3c4899eab5378ef5e54f2302948872149f6ad5e|"
- "589e975ec31ea13f302457fea1425be9380ffb96";
+ "7eddce841cbfa500964c91cdae78b01b9f448948|"
+ "5d13affec87bf4cc8c7667f0cd0d25e1ad09c7c3";
const std::string network_stats_checksum =
- "dc8447b9fee1a21fd5d1f4045d62b982a3fb0215";
+ "92b0fdcbf8bb9354d40140b7312f2fb76a078555";
DecodeAndCompare(input_rtp_file, output_checksum, network_stats_checksum,
absl::GetFlag(FLAGS_gen_ref));
@@ -165,7 +164,7 @@ TEST_F(NetEqDecodingTest, LongCngWithNegativeClockDrift) {
const double kDriftFactor = 1000.0 / (1000.0 + 25.0);
const double kNetworkFreezeTimeMs = 0.0;
const bool kGetAudioDuringFreezeRecovery = false;
- const int kDelayToleranceMs = 20;
+ const int kDelayToleranceMs = 60;
const int kMaxTimeToSpeechMs = 100;
LongCngWithClockDrift(kDriftFactor, kNetworkFreezeTimeMs,
kGetAudioDuringFreezeRecovery, kDelayToleranceMs,
@@ -495,7 +494,7 @@ TEST_F(NetEqDecodingTest, DiscardDuplicateCng) {
timestamp += kCngPeriodSamples;
uint32_t first_speech_timestamp = timestamp;
// Insert speech again.
- for (int i = 0; i < 3; ++i) {
+ for (int i = 0; i < 4; ++i) {
PopulateRtpInfo(seq_no, timestamp, &rtp_info);
ASSERT_EQ(0, neteq_->InsertPacket(rtp_info, payload));
++seq_no;
@@ -700,8 +699,7 @@ TEST_F(NetEqDecodingTestWithMutedState, MutedStateOldPacket) {
for (int i = 0; i < 5; ++i) {
InsertPacket(kSamples * (i - 1000));
}
- EXPECT_FALSE(GetAudioReturnMuted());
- EXPECT_EQ(AudioFrame::kNormalSpeech, out_frame_.speech_type_);
+ GetAudioUntilNormal();
}
// Verifies that NetEq doesn't enter muted state when CNG mode is active and the
diff --git a/third_party/libwebrtc/modules/audio_coding/neteq/packet_arrival_history.h b/third_party/libwebrtc/modules/audio_coding/neteq/packet_arrival_history.h
index cad362b469..722caf5688 100644
--- a/third_party/libwebrtc/modules/audio_coding/neteq/packet_arrival_history.h
+++ b/third_party/libwebrtc/modules/audio_coding/neteq/packet_arrival_history.h
@@ -26,6 +26,7 @@ namespace webrtc {
class PacketArrivalHistory {
public:
explicit PacketArrivalHistory(int window_size_ms);
+ virtual ~PacketArrivalHistory() = default;
// Insert packet with `rtp_timestamp` and `arrival_time_ms` into the history.
void Insert(uint32_t rtp_timestamp, int64_t arrival_time_ms);
@@ -34,10 +35,10 @@ class PacketArrivalHistory {
// `(time_ms - p.arrival_time_ms) - (rtp_timestamp - p.rtp_timestamp)`
// where `p` is chosen as the packet arrival in the history that maximizes the
// delay.
- int GetDelayMs(uint32_t rtp_timestamp, int64_t time_ms) const;
+ virtual int GetDelayMs(uint32_t rtp_timestamp, int64_t time_ms) const;
// Get the maximum packet arrival delay observed in the history.
- int GetMaxDelayMs() const;
+ virtual int GetMaxDelayMs() const;
bool IsNewestRtpTimestamp(uint32_t rtp_timestamp) const;
diff --git a/third_party/libwebrtc/modules/audio_coding/neteq/packet_buffer.cc b/third_party/libwebrtc/modules/audio_coding/neteq/packet_buffer.cc
index 9bfa908ab9..47c391a18f 100644
--- a/third_party/libwebrtc/modules/audio_coding/neteq/packet_buffer.cc
+++ b/third_party/libwebrtc/modules/audio_coding/neteq/packet_buffer.cc
@@ -44,53 +44,14 @@ class NewTimestampIsLarger {
const Packet& new_packet_;
};
-// Returns true if both payload types are known to the decoder database, and
-// have the same sample rate.
-bool EqualSampleRates(uint8_t pt1,
- uint8_t pt2,
- const DecoderDatabase& decoder_database) {
- auto* di1 = decoder_database.GetDecoderInfo(pt1);
- auto* di2 = decoder_database.GetDecoderInfo(pt2);
- return di1 && di2 && di1->SampleRateHz() == di2->SampleRateHz();
-}
-
-void LogPacketDiscarded(int codec_level, StatisticsCalculator* stats) {
- RTC_CHECK(stats);
- if (codec_level > 0) {
- stats->SecondaryPacketsDiscarded(1);
- } else {
- stats->PacketsDiscarded(1);
- }
-}
-
-absl::optional<SmartFlushingConfig> GetSmartflushingConfig() {
- absl::optional<SmartFlushingConfig> result;
- std::string field_trial_string =
- field_trial::FindFullName("WebRTC-Audio-NetEqSmartFlushing");
- result = SmartFlushingConfig();
- bool enabled = false;
- auto parser = StructParametersParser::Create(
- "enabled", &enabled, "target_level_threshold_ms",
- &result->target_level_threshold_ms, "target_level_multiplier",
- &result->target_level_multiplier);
- parser->Parse(field_trial_string);
- if (!enabled) {
- return absl::nullopt;
- }
- RTC_LOG(LS_INFO) << "Using smart flushing, target_level_threshold_ms: "
- << result->target_level_threshold_ms
- << ", target_level_multiplier: "
- << result->target_level_multiplier;
- return result;
-}
-
} // namespace
PacketBuffer::PacketBuffer(size_t max_number_of_packets,
- const TickTimer* tick_timer)
- : smart_flushing_config_(GetSmartflushingConfig()),
- max_number_of_packets_(max_number_of_packets),
- tick_timer_(tick_timer) {}
+ const TickTimer* tick_timer,
+ StatisticsCalculator* stats)
+ : max_number_of_packets_(max_number_of_packets),
+ tick_timer_(tick_timer),
+ stats_(stats) {}
// Destructor. All packets in the buffer will be destroyed.
PacketBuffer::~PacketBuffer() {
@@ -98,45 +59,19 @@ PacketBuffer::~PacketBuffer() {
}
// Flush the buffer. All packets in the buffer will be destroyed.
-void PacketBuffer::Flush(StatisticsCalculator* stats) {
+void PacketBuffer::Flush() {
for (auto& p : buffer_) {
- LogPacketDiscarded(p.priority.codec_level, stats);
+ LogPacketDiscarded(p.priority.codec_level);
}
buffer_.clear();
- stats->FlushedPacketBuffer();
-}
-
-void PacketBuffer::PartialFlush(int target_level_ms,
- size_t sample_rate,
- size_t last_decoded_length,
- StatisticsCalculator* stats) {
- // Make sure that at least half the packet buffer capacity will be available
- // after the flush. This is done to avoid getting stuck if the target level is
- // very high.
- int target_level_samples =
- std::min(target_level_ms * sample_rate / 1000,
- max_number_of_packets_ * last_decoded_length / 2);
- // We should avoid flushing to very low levels.
- target_level_samples = std::max(
- target_level_samples, smart_flushing_config_->target_level_threshold_ms);
- while (GetSpanSamples(last_decoded_length, sample_rate, false) >
- static_cast<size_t>(target_level_samples) ||
- buffer_.size() > max_number_of_packets_ / 2) {
- LogPacketDiscarded(PeekNextPacket()->priority.codec_level, stats);
- buffer_.pop_front();
- }
+ stats_->FlushedPacketBuffer();
}
bool PacketBuffer::Empty() const {
return buffer_.empty();
}
-int PacketBuffer::InsertPacket(Packet&& packet,
- StatisticsCalculator* stats,
- size_t last_decoded_length,
- size_t sample_rate,
- int target_level_ms,
- const DecoderDatabase& decoder_database) {
+int PacketBuffer::InsertPacket(Packet&& packet) {
if (packet.empty()) {
RTC_LOG(LS_WARNING) << "InsertPacket invalid packet";
return kInvalidPacket;
@@ -149,32 +84,11 @@ int PacketBuffer::InsertPacket(Packet&& packet,
packet.waiting_time = tick_timer_->GetNewStopwatch();
- // Perform a smart flush if the buffer size exceeds a multiple of the target
- // level.
- const size_t span_threshold =
- smart_flushing_config_
- ? smart_flushing_config_->target_level_multiplier *
- std::max(smart_flushing_config_->target_level_threshold_ms,
- target_level_ms) *
- sample_rate / 1000
- : 0;
- const bool smart_flush =
- smart_flushing_config_.has_value() &&
- GetSpanSamples(last_decoded_length, sample_rate, false) >= span_threshold;
- if (buffer_.size() >= max_number_of_packets_ || smart_flush) {
- size_t buffer_size_before_flush = buffer_.size();
- if (smart_flushing_config_.has_value()) {
- // Flush down to the target level.
- PartialFlush(target_level_ms, sample_rate, last_decoded_length, stats);
- return_val = kPartialFlush;
- } else {
- // Buffer is full.
- Flush(stats);
- return_val = kFlushed;
- }
- RTC_LOG(LS_WARNING) << "Packet buffer flushed, "
- << (buffer_size_before_flush - buffer_.size())
- << " packets discarded.";
+ if (buffer_.size() >= max_number_of_packets_) {
+ // Buffer is full.
+ Flush();
+ return_val = kFlushed;
+ RTC_LOG(LS_WARNING) << "Packet buffer flushed.";
}
// Get an iterator pointing to the place in the buffer where the new packet
@@ -187,7 +101,7 @@ int PacketBuffer::InsertPacket(Packet&& packet,
// timestamp as `rit`, which has a higher priority, do not insert the new
// packet to list.
if (rit != buffer_.rend() && packet.timestamp == rit->timestamp) {
- LogPacketDiscarded(packet.priority.codec_level, stats);
+ LogPacketDiscarded(packet.priority.codec_level);
return return_val;
}
@@ -196,7 +110,7 @@ int PacketBuffer::InsertPacket(Packet&& packet,
// packet.
PacketList::iterator it = rit.base();
if (it != buffer_.end() && packet.timestamp == it->timestamp) {
- LogPacketDiscarded(it->priority.codec_level, stats);
+ LogPacketDiscarded(it->priority.codec_level);
it = buffer_.erase(it);
}
buffer_.insert(it, std::move(packet)); // Insert the packet at that position.
@@ -204,57 +118,6 @@ int PacketBuffer::InsertPacket(Packet&& packet,
return return_val;
}
-int PacketBuffer::InsertPacketList(
- PacketList* packet_list,
- const DecoderDatabase& decoder_database,
- absl::optional<uint8_t>* current_rtp_payload_type,
- absl::optional<uint8_t>* current_cng_rtp_payload_type,
- StatisticsCalculator* stats,
- size_t last_decoded_length,
- size_t sample_rate,
- int target_level_ms) {
- RTC_DCHECK(stats);
- bool flushed = false;
- for (auto& packet : *packet_list) {
- if (decoder_database.IsComfortNoise(packet.payload_type)) {
- if (*current_cng_rtp_payload_type &&
- **current_cng_rtp_payload_type != packet.payload_type) {
- // New CNG payload type implies new codec type.
- *current_rtp_payload_type = absl::nullopt;
- Flush(stats);
- flushed = true;
- }
- *current_cng_rtp_payload_type = packet.payload_type;
- } else if (!decoder_database.IsDtmf(packet.payload_type)) {
- // This must be speech.
- if ((*current_rtp_payload_type &&
- **current_rtp_payload_type != packet.payload_type) ||
- (*current_cng_rtp_payload_type &&
- !EqualSampleRates(packet.payload_type,
- **current_cng_rtp_payload_type,
- decoder_database))) {
- *current_cng_rtp_payload_type = absl::nullopt;
- Flush(stats);
- flushed = true;
- }
- *current_rtp_payload_type = packet.payload_type;
- }
- int return_val =
- InsertPacket(std::move(packet), stats, last_decoded_length, sample_rate,
- target_level_ms, decoder_database);
- if (return_val == kFlushed) {
- // The buffer flushed, but this is not an error. We can still continue.
- flushed = true;
- } else if (return_val != kOK) {
- // An error occurred. Delete remaining packets in list and return.
- packet_list->clear();
- return return_val;
- }
- }
- packet_list->clear();
- return flushed ? kFlushed : kOK;
-}
-
int PacketBuffer::NextTimestamp(uint32_t* next_timestamp) const {
if (Empty()) {
return kBufferEmpty;
@@ -303,43 +166,40 @@ absl::optional<Packet> PacketBuffer::GetNextPacket() {
return packet;
}
-int PacketBuffer::DiscardNextPacket(StatisticsCalculator* stats) {
+int PacketBuffer::DiscardNextPacket() {
if (Empty()) {
return kBufferEmpty;
}
// Assert that the packet sanity checks in InsertPacket method works.
const Packet& packet = buffer_.front();
RTC_DCHECK(!packet.empty());
- LogPacketDiscarded(packet.priority.codec_level, stats);
+ LogPacketDiscarded(packet.priority.codec_level);
buffer_.pop_front();
return kOK;
}
void PacketBuffer::DiscardOldPackets(uint32_t timestamp_limit,
- uint32_t horizon_samples,
- StatisticsCalculator* stats) {
- buffer_.remove_if([timestamp_limit, horizon_samples, stats](const Packet& p) {
+ uint32_t horizon_samples) {
+ buffer_.remove_if([this, timestamp_limit, horizon_samples](const Packet& p) {
if (timestamp_limit == p.timestamp ||
!IsObsoleteTimestamp(p.timestamp, timestamp_limit, horizon_samples)) {
return false;
}
- LogPacketDiscarded(p.priority.codec_level, stats);
+ LogPacketDiscarded(p.priority.codec_level);
return true;
});
}
-void PacketBuffer::DiscardAllOldPackets(uint32_t timestamp_limit,
- StatisticsCalculator* stats) {
- DiscardOldPackets(timestamp_limit, 0, stats);
+void PacketBuffer::DiscardAllOldPackets(uint32_t timestamp_limit) {
+ DiscardOldPackets(timestamp_limit, 0);
}
-void PacketBuffer::DiscardPacketsWithPayloadType(uint8_t payload_type,
- StatisticsCalculator* stats) {
- buffer_.remove_if([payload_type, stats](const Packet& p) {
+void PacketBuffer::DiscardPacketsWithPayloadType(uint8_t payload_type) {
+ buffer_.remove_if([this, payload_type](const Packet& p) {
if (p.payload_type != payload_type) {
return false;
}
- LogPacketDiscarded(p.priority.codec_level, stats);
+ LogPacketDiscarded(p.priority.codec_level);
return true;
});
}
@@ -404,4 +264,12 @@ bool PacketBuffer::ContainsDtxOrCngPacket(
return false;
}
+void PacketBuffer::LogPacketDiscarded(int codec_level) {
+ if (codec_level > 0) {
+ stats_->SecondaryPacketsDiscarded(1);
+ } else {
+ stats_->PacketsDiscarded(1);
+ }
+}
+
} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/audio_coding/neteq/packet_buffer.h b/third_party/libwebrtc/modules/audio_coding/neteq/packet_buffer.h
index 1eef64a02c..795dd4e812 100644
--- a/third_party/libwebrtc/modules/audio_coding/neteq/packet_buffer.h
+++ b/third_party/libwebrtc/modules/audio_coding/neteq/packet_buffer.h
@@ -21,14 +21,6 @@ namespace webrtc {
class DecoderDatabase;
class StatisticsCalculator;
class TickTimer;
-struct SmartFlushingConfig {
- // When calculating the flushing threshold, the maximum between the target
- // level and this value is used.
- int target_level_threshold_ms = 500;
- // A smart flush is triggered when the packet buffer contains a multiple of
- // the target level.
- int target_level_multiplier = 3;
-};
// This is the actual buffer holding the packets before decoding.
class PacketBuffer {
@@ -36,7 +28,6 @@ class PacketBuffer {
enum BufferReturnCodes {
kOK = 0,
kFlushed,
- kPartialFlush,
kNotFound,
kBufferEmpty,
kInvalidPacket,
@@ -45,7 +36,9 @@ class PacketBuffer {
// Constructor creates a buffer which can hold a maximum of
// `max_number_of_packets` packets.
- PacketBuffer(size_t max_number_of_packets, const TickTimer* tick_timer);
+ PacketBuffer(size_t max_number_of_packets,
+ const TickTimer* tick_timer,
+ StatisticsCalculator* stats);
// Deletes all packets in the buffer before destroying the buffer.
virtual ~PacketBuffer();
@@ -54,13 +47,7 @@ class PacketBuffer {
PacketBuffer& operator=(const PacketBuffer&) = delete;
// Flushes the buffer and deletes all packets in it.
- virtual void Flush(StatisticsCalculator* stats);
-
- // Partial flush. Flush packets but leave some packets behind.
- virtual void PartialFlush(int target_level_ms,
- size_t sample_rate,
- size_t last_decoded_length,
- StatisticsCalculator* stats);
+ virtual void Flush();
// Returns true for an empty buffer.
virtual bool Empty() const;
@@ -69,30 +56,7 @@ class PacketBuffer {
// the packet object.
// Returns PacketBuffer::kOK on success, PacketBuffer::kFlushed if the buffer
// was flushed due to overfilling.
- virtual int InsertPacket(Packet&& packet,
- StatisticsCalculator* stats,
- size_t last_decoded_length,
- size_t sample_rate,
- int target_level_ms,
- const DecoderDatabase& decoder_database);
-
- // Inserts a list of packets into the buffer. The buffer will take over
- // ownership of the packet objects.
- // Returns PacketBuffer::kOK if all packets were inserted successfully.
- // If the buffer was flushed due to overfilling, only a subset of the list is
- // inserted, and PacketBuffer::kFlushed is returned.
- // The last three parameters are included for legacy compatibility.
- // TODO(hlundin): Redesign to not use current_*_payload_type and
- // decoder_database.
- virtual int InsertPacketList(
- PacketList* packet_list,
- const DecoderDatabase& decoder_database,
- absl::optional<uint8_t>* current_rtp_payload_type,
- absl::optional<uint8_t>* current_cng_rtp_payload_type,
- StatisticsCalculator* stats,
- size_t last_decoded_length,
- size_t sample_rate,
- int target_level_ms);
+ virtual int InsertPacket(Packet&& packet);
// Gets the timestamp for the first packet in the buffer and writes it to the
// output variable `next_timestamp`.
@@ -119,7 +83,7 @@ class PacketBuffer {
// Discards the first packet in the buffer. The packet is deleted.
// Returns PacketBuffer::kBufferEmpty if the buffer is empty,
// PacketBuffer::kOK otherwise.
- virtual int DiscardNextPacket(StatisticsCalculator* stats);
+ virtual int DiscardNextPacket();
// Discards all packets that are (strictly) older than timestamp_limit,
// but newer than timestamp_limit - horizon_samples. Setting horizon_samples
@@ -127,16 +91,13 @@ class PacketBuffer {
// is, if a packet is more than 2^31 timestamps into the future compared with
// timestamp_limit (including wrap-around), it is considered old.
virtual void DiscardOldPackets(uint32_t timestamp_limit,
- uint32_t horizon_samples,
- StatisticsCalculator* stats);
+ uint32_t horizon_samples);
// Discards all packets that are (strictly) older than timestamp_limit.
- virtual void DiscardAllOldPackets(uint32_t timestamp_limit,
- StatisticsCalculator* stats);
+ virtual void DiscardAllOldPackets(uint32_t timestamp_limit);
// Removes all packets with a specific payload type from the buffer.
- virtual void DiscardPacketsWithPayloadType(uint8_t payload_type,
- StatisticsCalculator* stats);
+ virtual void DiscardPacketsWithPayloadType(uint8_t payload_type);
// Returns the number of packets in the buffer, including duplicates and
// redundant packets.
@@ -171,10 +132,12 @@ class PacketBuffer {
}
private:
- absl::optional<SmartFlushingConfig> smart_flushing_config_;
+ void LogPacketDiscarded(int codec_level);
+
size_t max_number_of_packets_;
PacketList buffer_;
const TickTimer* tick_timer_;
+ StatisticsCalculator* stats_;
};
} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/audio_coding/neteq/packet_buffer_unittest.cc b/third_party/libwebrtc/modules/audio_coding/neteq/packet_buffer_unittest.cc
index b0079645ff..8f307a9eaf 100644
--- a/third_party/libwebrtc/modules/audio_coding/neteq/packet_buffer_unittest.cc
+++ b/third_party/libwebrtc/modules/audio_coding/neteq/packet_buffer_unittest.cc
@@ -108,26 +108,23 @@ namespace webrtc {
TEST(PacketBuffer, CreateAndDestroy) {
TickTimer tick_timer;
- PacketBuffer* buffer = new PacketBuffer(10, &tick_timer); // 10 packets.
+ StrictMock<MockStatisticsCalculator> mock_stats;
+ PacketBuffer* buffer =
+ new PacketBuffer(10, &tick_timer, &mock_stats); // 10 packets.
EXPECT_TRUE(buffer->Empty());
delete buffer;
}
TEST(PacketBuffer, InsertPacket) {
TickTimer tick_timer;
- PacketBuffer buffer(10, &tick_timer); // 10 packets.
- PacketGenerator gen(17u, 4711u, 0, 10);
StrictMock<MockStatisticsCalculator> mock_stats;
+ PacketBuffer buffer(10, &tick_timer, &mock_stats); // 10 packets.
+ PacketGenerator gen(17u, 4711u, 0, 10);
MockDecoderDatabase decoder_database;
const int payload_len = 100;
const Packet packet = gen.NextPacket(payload_len, nullptr);
- EXPECT_EQ(0, buffer.InsertPacket(/*packet=*/packet.Clone(),
- /*stats=*/&mock_stats,
- /*last_decoded_length=*/payload_len,
- /*sample_rate=*/10000,
- /*target_level_ms=*/60,
- /*decoder_database=*/decoder_database));
+ EXPECT_EQ(0, buffer.InsertPacket(/*packet=*/packet.Clone()));
uint32_t next_ts;
EXPECT_EQ(PacketBuffer::kOK, buffer.NextTimestamp(&next_ts));
EXPECT_EQ(4711u, next_ts);
@@ -144,28 +141,22 @@ TEST(PacketBuffer, InsertPacket) {
// Test to flush buffer.
TEST(PacketBuffer, FlushBuffer) {
TickTimer tick_timer;
- PacketBuffer buffer(10, &tick_timer); // 10 packets.
+ StrictMock<MockStatisticsCalculator> mock_stats;
+ PacketBuffer buffer(10, &tick_timer, &mock_stats); // 10 packets.
PacketGenerator gen(0, 0, 0, 10);
const int payload_len = 10;
- StrictMock<MockStatisticsCalculator> mock_stats;
MockDecoderDatabase decoder_database;
// Insert 10 small packets; should be ok.
for (int i = 0; i < 10; ++i) {
- EXPECT_EQ(
- PacketBuffer::kOK,
- buffer.InsertPacket(/*packet=*/gen.NextPacket(payload_len, nullptr),
- /*stats=*/&mock_stats,
- /*last_decoded_length=*/payload_len,
- /*sample_rate=*/1000,
- /*target_level_ms=*/60,
- /*decoder_database=*/decoder_database));
+ EXPECT_EQ(PacketBuffer::kOK, buffer.InsertPacket(/*packet=*/gen.NextPacket(
+ payload_len, nullptr)));
}
EXPECT_EQ(10u, buffer.NumPacketsInBuffer());
EXPECT_FALSE(buffer.Empty());
EXPECT_CALL(mock_stats, PacketsDiscarded(1)).Times(10);
- buffer.Flush(&mock_stats);
+ buffer.Flush();
// Buffer should delete the payloads itself.
EXPECT_EQ(0u, buffer.NumPacketsInBuffer());
EXPECT_TRUE(buffer.Empty());
@@ -175,23 +166,17 @@ TEST(PacketBuffer, FlushBuffer) {
// Test to fill the buffer over the limits, and verify that it flushes.
TEST(PacketBuffer, OverfillBuffer) {
TickTimer tick_timer;
- PacketBuffer buffer(10, &tick_timer); // 10 packets.
- PacketGenerator gen(0, 0, 0, 10);
StrictMock<MockStatisticsCalculator> mock_stats;
+ PacketBuffer buffer(10, &tick_timer, &mock_stats); // 10 packets.
+ PacketGenerator gen(0, 0, 0, 10);
MockDecoderDatabase decoder_database;
// Insert 10 small packets; should be ok.
const int payload_len = 10;
int i;
for (i = 0; i < 10; ++i) {
- EXPECT_EQ(
- PacketBuffer::kOK,
- buffer.InsertPacket(/*packet=*/gen.NextPacket(payload_len, nullptr),
- /*stats=*/&mock_stats,
- /*last_decoded_length=*/payload_len,
- /*sample_rate=*/1000,
- /*target_level_ms=*/60,
- /*decoder_database=*/decoder_database));
+ EXPECT_EQ(PacketBuffer::kOK, buffer.InsertPacket(/*packet=*/gen.NextPacket(
+ payload_len, nullptr)));
}
EXPECT_EQ(10u, buffer.NumPacketsInBuffer());
uint32_t next_ts;
@@ -202,12 +187,7 @@ TEST(PacketBuffer, OverfillBuffer) {
const Packet packet = gen.NextPacket(payload_len, nullptr);
// Insert 11th packet; should flush the buffer and insert it after flushing.
EXPECT_EQ(PacketBuffer::kFlushed,
- buffer.InsertPacket(/*packet=*/packet.Clone(),
- /*stats=*/&mock_stats,
- /*last_decoded_length=*/payload_len,
- /*sample_rate=*/1000,
- /*target_level_ms=*/60,
- /*decoder_database=*/decoder_database));
+ buffer.InsertPacket(/*packet=*/packet.Clone()));
EXPECT_EQ(1u, buffer.NumPacketsInBuffer());
EXPECT_EQ(PacketBuffer::kOK, buffer.NextTimestamp(&next_ts));
// Expect last inserted packet to be first in line.
@@ -216,190 +196,11 @@ TEST(PacketBuffer, OverfillBuffer) {
EXPECT_CALL(decoder_database, Die()); // Called when object is deleted.
}
-// Test a partial buffer flush.
-TEST(PacketBuffer, PartialFlush) {
- // Use a field trial to configure smart flushing.
- test::ScopedFieldTrials field_trials(
- "WebRTC-Audio-NetEqSmartFlushing/enabled:true,"
- "target_level_threshold_ms:0,target_level_multiplier:2/");
- TickTimer tick_timer;
- PacketBuffer buffer(10, &tick_timer); // 10 packets.
- PacketGenerator gen(0, 0, 0, 10);
- const int payload_len = 10;
- StrictMock<MockStatisticsCalculator> mock_stats;
- MockDecoderDatabase decoder_database;
-
- // Insert 10 small packets; should be ok.
- for (int i = 0; i < 10; ++i) {
- EXPECT_EQ(
- PacketBuffer::kOK,
- buffer.InsertPacket(/*packet=*/gen.NextPacket(payload_len, nullptr),
- /*stats=*/&mock_stats,
- /*last_decoded_length=*/payload_len,
- /*sample_rate=*/1000,
- /*target_level_ms=*/100,
- /*decoder_database=*/decoder_database));
- }
- EXPECT_EQ(10u, buffer.NumPacketsInBuffer());
- EXPECT_FALSE(buffer.Empty());
-
- EXPECT_CALL(mock_stats, PacketsDiscarded(1)).Times(7);
- buffer.PartialFlush(/*target_level_ms=*/30,
- /*sample_rate=*/1000,
- /*last_decoded_length=*/payload_len,
- /*stats=*/&mock_stats);
- // There should still be some packets left in the buffer.
- EXPECT_EQ(3u, buffer.NumPacketsInBuffer());
- EXPECT_FALSE(buffer.Empty());
- EXPECT_CALL(decoder_database, Die()); // Called when object is deleted.
-}
-
-// Test to fill the buffer over the limits, and verify that the smart flush
-// functionality works as expected.
-TEST(PacketBuffer, SmartFlushOverfillBuffer) {
- // Use a field trial to configure smart flushing.
- test::ScopedFieldTrials field_trials(
- "WebRTC-Audio-NetEqSmartFlushing/enabled:true,"
- "target_level_threshold_ms:0,target_level_multiplier:2/");
- TickTimer tick_timer;
- PacketBuffer buffer(10, &tick_timer); // 10 packets.
- PacketGenerator gen(0, 0, 0, 10);
- StrictMock<MockStatisticsCalculator> mock_stats;
- MockDecoderDatabase decoder_database;
-
- // Insert 10 small packets; should be ok.
- const int payload_len = 10;
- int i;
- for (i = 0; i < 10; ++i) {
- EXPECT_EQ(
- PacketBuffer::kOK,
- buffer.InsertPacket(/*packet=*/gen.NextPacket(payload_len, nullptr),
- /*stats=*/&mock_stats,
- /*last_decoded_length=*/payload_len,
- /*sample_rate=*/1000,
- /*target_level_ms=*/100,
- /*decoder_database=*/decoder_database));
- }
- EXPECT_EQ(10u, buffer.NumPacketsInBuffer());
- uint32_t next_ts;
- EXPECT_EQ(PacketBuffer::kOK, buffer.NextTimestamp(&next_ts));
- EXPECT_EQ(0u, next_ts); // Expect first inserted packet to be first in line.
-
- const Packet packet = gen.NextPacket(payload_len, nullptr);
- EXPECT_CALL(mock_stats, PacketsDiscarded(1)).Times(6);
- // Insert 11th packet; should cause a partial flush and insert the packet
- // after flushing.
- EXPECT_EQ(PacketBuffer::kPartialFlush,
- buffer.InsertPacket(/*packet=*/packet.Clone(),
- /*stats=*/&mock_stats,
- /*last_decoded_length=*/payload_len,
- /*sample_rate=*/1000,
- /*target_level_ms=*/40,
- /*decoder_database=*/decoder_database));
- EXPECT_EQ(5u, buffer.NumPacketsInBuffer());
- EXPECT_CALL(decoder_database, Die()); // Called when object is deleted.
-}
-
-// Test inserting a list of packets.
-TEST(PacketBuffer, InsertPacketList) {
- TickTimer tick_timer;
- PacketBuffer buffer(10, &tick_timer); // 10 packets.
- PacketGenerator gen(0, 0, 0, 10);
- PacketList list;
- const int payload_len = 10;
-
- // Insert 10 small packets.
- for (int i = 0; i < 10; ++i) {
- list.push_back(gen.NextPacket(payload_len, nullptr));
- }
-
- MockDecoderDatabase decoder_database;
- auto factory = CreateBuiltinAudioDecoderFactory();
- const DecoderDatabase::DecoderInfo info(SdpAudioFormat("pcmu", 8000, 1),
- absl::nullopt, factory.get());
- EXPECT_CALL(decoder_database, GetDecoderInfo(0))
- .WillRepeatedly(Return(&info));
-
- StrictMock<MockStatisticsCalculator> mock_stats;
-
- absl::optional<uint8_t> current_pt;
- absl::optional<uint8_t> current_cng_pt;
- EXPECT_EQ(
- PacketBuffer::kOK,
- buffer.InsertPacketList(/*packet_list=*/&list,
- /*decoder_database=*/decoder_database,
- /*current_rtp_payload_type=*/&current_pt,
- /*current_cng_rtp_payload_type=*/&current_cng_pt,
- /*stats=*/&mock_stats,
- /*last_decoded_length=*/payload_len,
- /*sample_rate=*/1000,
- /*target_level_ms=*/30));
- EXPECT_TRUE(list.empty()); // The PacketBuffer should have depleted the list.
- EXPECT_EQ(10u, buffer.NumPacketsInBuffer());
- EXPECT_EQ(0, current_pt); // Current payload type changed to 0.
- EXPECT_EQ(absl::nullopt, current_cng_pt); // CNG payload type not changed.
-
- EXPECT_CALL(decoder_database, Die()); // Called when object is deleted.
-}
-
-// Test inserting a list of packets. Last packet is of a different payload type.
-// Expecting the buffer to flush.
-// TODO(hlundin): Remove this test when legacy operation is no longer needed.
-TEST(PacketBuffer, InsertPacketListChangePayloadType) {
- TickTimer tick_timer;
- PacketBuffer buffer(10, &tick_timer); // 10 packets.
- PacketGenerator gen(0, 0, 0, 10);
- PacketList list;
- const int payload_len = 10;
-
- // Insert 10 small packets.
- for (int i = 0; i < 10; ++i) {
- list.push_back(gen.NextPacket(payload_len, nullptr));
- }
- // Insert 11th packet of another payload type (not CNG).
- {
- Packet packet = gen.NextPacket(payload_len, nullptr);
- packet.payload_type = 1;
- list.push_back(std::move(packet));
- }
-
- MockDecoderDatabase decoder_database;
- auto factory = CreateBuiltinAudioDecoderFactory();
- const DecoderDatabase::DecoderInfo info0(SdpAudioFormat("pcmu", 8000, 1),
- absl::nullopt, factory.get());
- EXPECT_CALL(decoder_database, GetDecoderInfo(0))
- .WillRepeatedly(Return(&info0));
- const DecoderDatabase::DecoderInfo info1(SdpAudioFormat("pcma", 8000, 1),
- absl::nullopt, factory.get());
- EXPECT_CALL(decoder_database, GetDecoderInfo(1))
- .WillRepeatedly(Return(&info1));
-
- StrictMock<MockStatisticsCalculator> mock_stats;
-
- absl::optional<uint8_t> current_pt;
- absl::optional<uint8_t> current_cng_pt;
- EXPECT_CALL(mock_stats, PacketsDiscarded(1)).Times(10);
- EXPECT_EQ(
- PacketBuffer::kFlushed,
- buffer.InsertPacketList(/*packet_list=*/&list,
- /*decoder_database=*/decoder_database,
- /*current_rtp_payload_type=*/&current_pt,
- /*current_cng_rtp_payload_type=*/&current_cng_pt,
- /*stats=*/&mock_stats,
- /*last_decoded_length=*/payload_len,
- /*sample_rate=*/1000,
- /*target_level_ms=*/30));
- EXPECT_TRUE(list.empty()); // The PacketBuffer should have depleted the list.
- EXPECT_EQ(1u, buffer.NumPacketsInBuffer()); // Only the last packet.
- EXPECT_EQ(1, current_pt); // Current payload type changed to 1.
- EXPECT_EQ(absl::nullopt, current_cng_pt); // CNG payload type not changed.
-
- EXPECT_CALL(decoder_database, Die()); // Called when object is deleted.
-}
TEST(PacketBuffer, ExtractOrderRedundancy) {
TickTimer tick_timer;
- PacketBuffer buffer(100, &tick_timer); // 100 packets.
+ StrictMock<MockStatisticsCalculator> mock_stats;
+ PacketBuffer buffer(100, &tick_timer, &mock_stats); // 100 packets.
const int kPackets = 18;
const int kFrameSize = 10;
const int kPayloadLength = 10;
@@ -423,8 +224,6 @@ TEST(PacketBuffer, ExtractOrderRedundancy) {
PacketGenerator gen(0, 0, 0, kFrameSize);
- StrictMock<MockStatisticsCalculator> mock_stats;
-
// Interleaving the EXPECT_CALL sequence with expectations on the MockFunction
// check ensures that exactly one call to PacketsDiscarded happens in each
// DiscardNextPacket call.
@@ -444,12 +243,7 @@ TEST(PacketBuffer, ExtractOrderRedundancy) {
}
EXPECT_CALL(check, Call(i));
EXPECT_EQ(PacketBuffer::kOK,
- buffer.InsertPacket(/*packet=*/packet.Clone(),
- /*stats=*/&mock_stats,
- /*last_decoded_length=*/kPayloadLength,
- /*sample_rate=*/1000,
- /*target_level_ms=*/60,
- /*decoder_database=*/decoder_database));
+ buffer.InsertPacket(/*packet=*/packet.Clone()));
if (packet_facts[i].extract_order >= 0) {
expect_order[packet_facts[i].extract_order] = std::move(packet);
}
@@ -468,25 +262,20 @@ TEST(PacketBuffer, ExtractOrderRedundancy) {
TEST(PacketBuffer, DiscardPackets) {
TickTimer tick_timer;
- PacketBuffer buffer(100, &tick_timer); // 100 packets.
+ StrictMock<MockStatisticsCalculator> mock_stats;
+ PacketBuffer buffer(100, &tick_timer, &mock_stats); // 100 packets.
const uint16_t start_seq_no = 17;
const uint32_t start_ts = 4711;
const uint32_t ts_increment = 10;
PacketGenerator gen(start_seq_no, start_ts, 0, ts_increment);
PacketList list;
const int payload_len = 10;
- StrictMock<MockStatisticsCalculator> mock_stats;
MockDecoderDatabase decoder_database;
constexpr int kTotalPackets = 10;
// Insert 10 small packets.
for (int i = 0; i < kTotalPackets; ++i) {
- buffer.InsertPacket(/*packet=*/gen.NextPacket(payload_len, nullptr),
- /*stats=*/&mock_stats,
- /*last_decoded_length=*/payload_len,
- /*sample_rate=*/1000,
- /*target_level_ms=*/60,
- /*decoder_database=*/decoder_database);
+ buffer.InsertPacket(/*packet=*/gen.NextPacket(payload_len, nullptr));
}
EXPECT_EQ(10u, buffer.NumPacketsInBuffer());
@@ -507,7 +296,7 @@ TEST(PacketBuffer, DiscardPackets) {
EXPECT_EQ(current_ts, ts);
EXPECT_CALL(mock_stats, PacketsDiscarded(1));
EXPECT_CALL(check, Call(i));
- EXPECT_EQ(PacketBuffer::kOK, buffer.DiscardNextPacket(&mock_stats));
+ EXPECT_EQ(PacketBuffer::kOK, buffer.DiscardNextPacket());
current_ts += ts_increment;
check.Call(i);
}
@@ -520,7 +309,7 @@ TEST(PacketBuffer, DiscardPackets) {
.Times(kRemainingPackets - kSkipPackets);
EXPECT_CALL(check, Call(17)); // Arbitrary id number.
buffer.DiscardOldPackets(start_ts + kTotalPackets * ts_increment,
- kRemainingPackets * ts_increment, &mock_stats);
+ kRemainingPackets * ts_increment);
check.Call(17); // Same arbitrary id number.
EXPECT_EQ(kSkipPackets, buffer.NumPacketsInBuffer());
@@ -530,8 +319,7 @@ TEST(PacketBuffer, DiscardPackets) {
// Discard all remaining packets.
EXPECT_CALL(mock_stats, PacketsDiscarded(kSkipPackets));
- buffer.DiscardAllOldPackets(start_ts + kTotalPackets * ts_increment,
- &mock_stats);
+ buffer.DiscardAllOldPackets(start_ts + kTotalPackets * ts_increment);
EXPECT_TRUE(buffer.Empty());
EXPECT_CALL(decoder_database, Die()); // Called when object is deleted.
@@ -539,7 +327,8 @@ TEST(PacketBuffer, DiscardPackets) {
TEST(PacketBuffer, Reordering) {
TickTimer tick_timer;
- PacketBuffer buffer(100, &tick_timer); // 100 packets.
+ StrictMock<MockStatisticsCalculator> mock_stats;
+ PacketBuffer buffer(100, &tick_timer, &mock_stats); // 100 packets.
const uint16_t start_seq_no = 17;
const uint32_t start_ts = 4711;
const uint32_t ts_increment = 10;
@@ -559,27 +348,9 @@ TEST(PacketBuffer, Reordering) {
}
}
- MockDecoderDatabase decoder_database;
- auto factory = CreateBuiltinAudioDecoderFactory();
- const DecoderDatabase::DecoderInfo info(SdpAudioFormat("pcmu", 8000, 1),
- absl::nullopt, factory.get());
- EXPECT_CALL(decoder_database, GetDecoderInfo(0))
- .WillRepeatedly(Return(&info));
- absl::optional<uint8_t> current_pt;
- absl::optional<uint8_t> current_cng_pt;
-
- StrictMock<MockStatisticsCalculator> mock_stats;
-
- EXPECT_EQ(
- PacketBuffer::kOK,
- buffer.InsertPacketList(/*packet_list=*/&list,
- /*decoder_database=*/decoder_database,
- /*current_rtp_payload_type=*/&current_pt,
- /*current_cng_rtp_payload_type=*/&current_cng_pt,
- /*stats=*/&mock_stats,
- /*last_decoded_length=*/payload_len,
- /*sample_rate=*/1000,
- /*target_level_ms=*/30));
+ for (Packet& packet : list) {
+ EXPECT_EQ(PacketBuffer::kOK, buffer.InsertPacket(std::move(packet)));
+ }
EXPECT_EQ(10u, buffer.NumPacketsInBuffer());
// Extract them and make sure that come out in the right order.
@@ -591,86 +362,6 @@ TEST(PacketBuffer, Reordering) {
current_ts += ts_increment;
}
EXPECT_TRUE(buffer.Empty());
-
- EXPECT_CALL(decoder_database, Die()); // Called when object is deleted.
-}
-
-// The test first inserts a packet with narrow-band CNG, then a packet with
-// wide-band speech. The expected behavior of the packet buffer is to detect a
-// change in sample rate, even though no speech packet has been inserted before,
-// and flush out the CNG packet.
-TEST(PacketBuffer, CngFirstThenSpeechWithNewSampleRate) {
- TickTimer tick_timer;
- PacketBuffer buffer(10, &tick_timer); // 10 packets.
- const uint8_t kCngPt = 13;
- const int kPayloadLen = 10;
- const uint8_t kSpeechPt = 100;
-
- MockDecoderDatabase decoder_database;
- auto factory = CreateBuiltinAudioDecoderFactory();
- const DecoderDatabase::DecoderInfo info_cng(SdpAudioFormat("cn", 8000, 1),
- absl::nullopt, factory.get());
- EXPECT_CALL(decoder_database, GetDecoderInfo(kCngPt))
- .WillRepeatedly(Return(&info_cng));
- const DecoderDatabase::DecoderInfo info_speech(
- SdpAudioFormat("l16", 16000, 1), absl::nullopt, factory.get());
- EXPECT_CALL(decoder_database, GetDecoderInfo(kSpeechPt))
- .WillRepeatedly(Return(&info_speech));
-
- // Insert first packet, which is narrow-band CNG.
- PacketGenerator gen(0, 0, kCngPt, 10);
- PacketList list;
- list.push_back(gen.NextPacket(kPayloadLen, nullptr));
- absl::optional<uint8_t> current_pt;
- absl::optional<uint8_t> current_cng_pt;
-
- StrictMock<MockStatisticsCalculator> mock_stats;
-
- EXPECT_EQ(
- PacketBuffer::kOK,
- buffer.InsertPacketList(/*packet_list=*/&list,
- /*decoder_database=*/decoder_database,
- /*current_rtp_payload_type=*/&current_pt,
- /*current_cng_rtp_payload_type=*/&current_cng_pt,
- /*stats=*/&mock_stats,
- /*last_decoded_length=*/kPayloadLen,
- /*sample_rate=*/1000,
- /*target_level_ms=*/30));
- EXPECT_TRUE(list.empty());
- EXPECT_EQ(1u, buffer.NumPacketsInBuffer());
- ASSERT_TRUE(buffer.PeekNextPacket());
- EXPECT_EQ(kCngPt, buffer.PeekNextPacket()->payload_type);
- EXPECT_EQ(current_pt, absl::nullopt); // Current payload type not set.
- EXPECT_EQ(kCngPt, current_cng_pt); // CNG payload type set.
-
- // Insert second packet, which is wide-band speech.
- {
- Packet packet = gen.NextPacket(kPayloadLen, nullptr);
- packet.payload_type = kSpeechPt;
- list.push_back(std::move(packet));
- }
- // Expect the buffer to flush out the CNG packet, since it does not match the
- // new speech sample rate.
- EXPECT_CALL(mock_stats, PacketsDiscarded(1));
- EXPECT_EQ(
- PacketBuffer::kFlushed,
- buffer.InsertPacketList(/*packet_list=*/&list,
- /*decoder_database=*/decoder_database,
- /*current_rtp_payload_type=*/&current_pt,
- /*current_cng_rtp_payload_type=*/&current_cng_pt,
- /*stats=*/&mock_stats,
- /*last_decoded_length=*/kPayloadLen,
- /*sample_rate=*/1000,
- /*target_level_ms=*/30));
- EXPECT_TRUE(list.empty());
- EXPECT_EQ(1u, buffer.NumPacketsInBuffer());
- ASSERT_TRUE(buffer.PeekNextPacket());
- EXPECT_EQ(kSpeechPt, buffer.PeekNextPacket()->payload_type);
-
- EXPECT_EQ(kSpeechPt, current_pt); // Current payload type set.
- EXPECT_EQ(absl::nullopt, current_cng_pt); // CNG payload type reset.
-
- EXPECT_CALL(decoder_database, Die()); // Called when object is deleted.
}
TEST(PacketBuffer, Failures) {
@@ -681,80 +372,26 @@ TEST(PacketBuffer, Failures) {
PacketGenerator gen(start_seq_no, start_ts, 0, ts_increment);
TickTimer tick_timer;
StrictMock<MockStatisticsCalculator> mock_stats;
- MockDecoderDatabase decoder_database;
- PacketBuffer* buffer = new PacketBuffer(100, &tick_timer); // 100 packets.
+ PacketBuffer buffer(100, &tick_timer, &mock_stats); // 100 packets.
{
Packet packet = gen.NextPacket(payload_len, nullptr);
packet.payload.Clear();
EXPECT_EQ(PacketBuffer::kInvalidPacket,
- buffer->InsertPacket(/*packet=*/std::move(packet),
- /*stats=*/&mock_stats,
- /*last_decoded_length=*/payload_len,
- /*sample_rate=*/1000,
- /*target_level_ms=*/60,
- /*decoder_database=*/decoder_database));
+ buffer.InsertPacket(/*packet=*/std::move(packet)));
}
// Buffer should still be empty. Test all empty-checks.
uint32_t temp_ts;
- EXPECT_EQ(PacketBuffer::kBufferEmpty, buffer->NextTimestamp(&temp_ts));
+ EXPECT_EQ(PacketBuffer::kBufferEmpty, buffer.NextTimestamp(&temp_ts));
EXPECT_EQ(PacketBuffer::kBufferEmpty,
- buffer->NextHigherTimestamp(0, &temp_ts));
- EXPECT_EQ(NULL, buffer->PeekNextPacket());
- EXPECT_FALSE(buffer->GetNextPacket());
+ buffer.NextHigherTimestamp(0, &temp_ts));
+ EXPECT_EQ(NULL, buffer.PeekNextPacket());
+ EXPECT_FALSE(buffer.GetNextPacket());
// Discarding packets will not invoke mock_stats.PacketDiscarded() because the
// packet buffer is empty.
- EXPECT_EQ(PacketBuffer::kBufferEmpty, buffer->DiscardNextPacket(&mock_stats));
- buffer->DiscardAllOldPackets(0, &mock_stats);
-
- // Insert one packet to make the buffer non-empty.
- EXPECT_EQ(
- PacketBuffer::kOK,
- buffer->InsertPacket(/*packet=*/gen.NextPacket(payload_len, nullptr),
- /*stats=*/&mock_stats,
- /*last_decoded_length=*/payload_len,
- /*sample_rate=*/1000,
- /*target_level_ms=*/60,
- /*decoder_database=*/decoder_database));
- EXPECT_EQ(PacketBuffer::kInvalidPointer, buffer->NextTimestamp(NULL));
- EXPECT_EQ(PacketBuffer::kInvalidPointer,
- buffer->NextHigherTimestamp(0, NULL));
- delete buffer;
-
- // Insert packet list of three packets, where the second packet has an invalid
- // payload. Expect first packet to be inserted, and the remaining two to be
- // discarded.
- buffer = new PacketBuffer(100, &tick_timer); // 100 packets.
- PacketList list;
- list.push_back(gen.NextPacket(payload_len, nullptr)); // Valid packet.
- {
- Packet packet = gen.NextPacket(payload_len, nullptr);
- packet.payload.Clear(); // Invalid.
- list.push_back(std::move(packet));
- }
- list.push_back(gen.NextPacket(payload_len, nullptr)); // Valid packet.
- auto factory = CreateBuiltinAudioDecoderFactory();
- const DecoderDatabase::DecoderInfo info(SdpAudioFormat("pcmu", 8000, 1),
- absl::nullopt, factory.get());
- EXPECT_CALL(decoder_database, GetDecoderInfo(0))
- .WillRepeatedly(Return(&info));
- absl::optional<uint8_t> current_pt;
- absl::optional<uint8_t> current_cng_pt;
- EXPECT_EQ(
- PacketBuffer::kInvalidPacket,
- buffer->InsertPacketList(/*packet_list=*/&list,
- /*decoder_database=*/decoder_database,
- /*current_rtp_payload_type=*/&current_pt,
- /*current_cng_rtp_payload_type=*/&current_cng_pt,
- /*stats=*/&mock_stats,
- /*last_decoded_length=*/payload_len,
- /*sample_rate=*/1000,
- /*target_level_ms=*/30));
- EXPECT_TRUE(list.empty()); // The PacketBuffer should have depleted the list.
- EXPECT_EQ(1u, buffer->NumPacketsInBuffer());
- delete buffer;
- EXPECT_CALL(decoder_database, Die()); // Called when object is deleted.
+ EXPECT_EQ(PacketBuffer::kBufferEmpty, buffer.DiscardNextPacket());
+ buffer.DiscardAllOldPackets(0);
}
// Test packet comparison function.
@@ -873,9 +510,9 @@ TEST(PacketBuffer, GetSpanSamples) {
constexpr int kSampleRateHz = 48000;
constexpr bool kCountWaitingTime = false;
TickTimer tick_timer;
- PacketBuffer buffer(3, &tick_timer);
- PacketGenerator gen(0, kStartTimeStamp, 0, kFrameSizeSamples);
StrictMock<MockStatisticsCalculator> mock_stats;
+ PacketBuffer buffer(3, &tick_timer, &mock_stats);
+ PacketGenerator gen(0, kStartTimeStamp, 0, kFrameSizeSamples);
MockDecoderDatabase decoder_database;
Packet packet_1 = gen.NextPacket(kPayloadSizeBytes, nullptr);
@@ -891,12 +528,7 @@ TEST(PacketBuffer, GetSpanSamples) {
packet_2.timestamp); // Tmestamp wrapped around.
EXPECT_EQ(PacketBuffer::kOK,
- buffer.InsertPacket(/*packet=*/std::move(packet_1),
- /*stats=*/&mock_stats,
- /*last_decoded_length=*/kFrameSizeSamples,
- /*sample_rate=*/1000,
- /*target_level_ms=*/60,
- /*decoder_database=*/decoder_database));
+ buffer.InsertPacket(/*packet=*/std::move(packet_1)));
constexpr size_t kLastDecodedSizeSamples = 2;
// packet_1 has no access to duration, and relies last decoded duration as
@@ -906,12 +538,7 @@ TEST(PacketBuffer, GetSpanSamples) {
kCountWaitingTime));
EXPECT_EQ(PacketBuffer::kOK,
- buffer.InsertPacket(/*packet=*/std::move(packet_2),
- /*stats=*/&mock_stats,
- /*last_decoded_length=*/kFrameSizeSamples,
- /*sample_rate=*/1000,
- /*target_level_ms=*/60,
- /*decoder_database=*/decoder_database));
+ buffer.InsertPacket(/*packet=*/std::move(packet_2)));
EXPECT_EQ(kFrameSizeSamples * 2,
buffer.GetSpanSamples(0, kSampleRateHz, kCountWaitingTime));
@@ -931,20 +558,15 @@ TEST(PacketBuffer, GetSpanSamplesCountWaitingTime) {
constexpr bool kCountWaitingTime = true;
constexpr size_t kLastDecodedSizeSamples = 0;
TickTimer tick_timer;
- PacketBuffer buffer(3, &tick_timer);
- PacketGenerator gen(0, kStartTimeStamp, 0, kFrameSizeSamples);
StrictMock<MockStatisticsCalculator> mock_stats;
+ PacketBuffer buffer(3, &tick_timer, &mock_stats);
+ PacketGenerator gen(0, kStartTimeStamp, 0, kFrameSizeSamples);
MockDecoderDatabase decoder_database;
Packet packet = gen.NextPacket(kPayloadSizeBytes, nullptr);
EXPECT_EQ(PacketBuffer::kOK,
- buffer.InsertPacket(/*packet=*/std::move(packet),
- /*stats=*/&mock_stats,
- /*last_decoded_length=*/kFrameSizeSamples,
- /*sample_rate=*/kSampleRateHz,
- /*target_level_ms=*/60,
- /*decoder_database=*/decoder_database));
+ buffer.InsertPacket(/*packet=*/std::move(packet)));
EXPECT_EQ(0u, buffer.GetSpanSamples(kLastDecodedSizeSamples, kSampleRateHz,
kCountWaitingTime));
diff --git a/third_party/libwebrtc/modules/audio_coding/neteq/test/neteq_decoding_test.cc b/third_party/libwebrtc/modules/audio_coding/neteq/test/neteq_decoding_test.cc
index e6c1809fb6..e626d09c99 100644
--- a/third_party/libwebrtc/modules/audio_coding/neteq/test/neteq_decoding_test.cc
+++ b/third_party/libwebrtc/modules/audio_coding/neteq/test/neteq_decoding_test.cc
@@ -19,13 +19,13 @@
#include "test/testsupport/file_utils.h"
#ifdef WEBRTC_NETEQ_UNITTEST_BITEXACT
-RTC_PUSH_IGNORING_WUNDEF()
+
#ifdef WEBRTC_ANDROID_PLATFORM_BUILD
#include "external/webrtc/webrtc/modules/audio_coding/neteq/neteq_unittest.pb.h"
#else
#include "modules/audio_coding/neteq/neteq_unittest.pb.h"
#endif
-RTC_POP_IGNORING_WUNDEF()
+
#endif
namespace webrtc {
diff --git a/third_party/libwebrtc/modules/audio_coding/neteq/test/result_sink.cc b/third_party/libwebrtc/modules/audio_coding/neteq/test/result_sink.cc
index f5d50dc859..fee7b49eb3 100644
--- a/third_party/libwebrtc/modules/audio_coding/neteq/test/result_sink.cc
+++ b/third_party/libwebrtc/modules/audio_coding/neteq/test/result_sink.cc
@@ -13,19 +13,18 @@
#include <string>
#include "absl/strings/string_view.h"
-#include "rtc_base/ignore_wundef.h"
#include "rtc_base/message_digest.h"
#include "rtc_base/string_encode.h"
#include "test/gtest.h"
#ifdef WEBRTC_NETEQ_UNITTEST_BITEXACT
-RTC_PUSH_IGNORING_WUNDEF()
+
#ifdef WEBRTC_ANDROID_PLATFORM_BUILD
#include "external/webrtc/webrtc/modules/audio_coding/neteq/neteq_unittest.pb.h"
#else
#include "modules/audio_coding/neteq/neteq_unittest.pb.h"
#endif
-RTC_POP_IGNORING_WUNDEF()
+
#endif
namespace webrtc {
diff --git a/third_party/libwebrtc/modules/audio_coding/neteq_gn/moz.build b/third_party/libwebrtc/modules/audio_coding/neteq_gn/moz.build
index 04dbb03279..834a8d1265 100644
--- a/third_party/libwebrtc/modules/audio_coding/neteq_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_coding/neteq_gn/moz.build
@@ -234,7 +234,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -244,10 +243,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_coding/pcm16b_c_gn/moz.build b/third_party/libwebrtc/modules/audio_coding/pcm16b_c_gn/moz.build
index 41f722069c..ef0c150cb8 100644
--- a/third_party/libwebrtc/modules/audio_coding/pcm16b_c_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_coding/pcm16b_c_gn/moz.build
@@ -184,7 +184,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -194,10 +193,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_coding/pcm16b_gn/moz.build b/third_party/libwebrtc/modules/audio_coding/pcm16b_gn/moz.build
index ed96e7c0f8..a1d9c8009d 100644
--- a/third_party/libwebrtc/modules/audio_coding/pcm16b_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_coding/pcm16b_gn/moz.build
@@ -197,7 +197,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -207,10 +206,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_coding/red_gn/moz.build b/third_party/libwebrtc/modules/audio_coding/red_gn/moz.build
index 479cf67a2a..ab0d8129bb 100644
--- a/third_party/libwebrtc/modules/audio_coding/red_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_coding/red_gn/moz.build
@@ -199,7 +199,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -209,10 +208,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_coding/webrtc_cng_gn/moz.build b/third_party/libwebrtc/modules/audio_coding/webrtc_cng_gn/moz.build
index a8a6c576e2..d077aaa930 100644
--- a/third_party/libwebrtc/modules/audio_coding/webrtc_cng_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_coding/webrtc_cng_gn/moz.build
@@ -199,7 +199,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -209,10 +208,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_coding/webrtc_multiopus_gn/moz.build b/third_party/libwebrtc/modules/audio_coding/webrtc_multiopus_gn/moz.build
index 491f0cc543..d48fd68174 100644
--- a/third_party/libwebrtc/modules/audio_coding/webrtc_multiopus_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_coding/webrtc_multiopus_gn/moz.build
@@ -200,7 +200,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -210,10 +209,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_coding/webrtc_opus_gn/moz.build b/third_party/libwebrtc/modules/audio_coding/webrtc_opus_gn/moz.build
index e2c57b99af..02986beaa4 100644
--- a/third_party/libwebrtc/modules/audio_coding/webrtc_opus_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_coding/webrtc_opus_gn/moz.build
@@ -204,7 +204,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -214,10 +213,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_coding/webrtc_opus_wrapper_gn/moz.build b/third_party/libwebrtc/modules/audio_coding/webrtc_opus_wrapper_gn/moz.build
index 268854264f..e6c31b48b5 100644
--- a/third_party/libwebrtc/modules/audio_coding/webrtc_opus_wrapper_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_coding/webrtc_opus_wrapper_gn/moz.build
@@ -199,7 +199,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -209,10 +208,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_device/BUILD.gn b/third_party/libwebrtc/modules/audio_device/BUILD.gn
index 4726f93279..a135f042db 100644
--- a/third_party/libwebrtc/modules/audio_device/BUILD.gn
+++ b/third_party/libwebrtc/modules/audio_device/BUILD.gn
@@ -50,6 +50,7 @@ rtc_source_set("audio_device_api") {
"include/audio_device_defines.h",
]
deps = [
+ "../../api:ref_count",
"../../api:scoped_refptr",
"../../api/task_queue",
"../../rtc_base:checks",
@@ -490,7 +491,6 @@ if (rtc_include_tests && !build_with_chromium && !build_with_mozilla) {
"../../common_audio",
"../../rtc_base:buffer",
"../../rtc_base:checks",
- "../../rtc_base:ignore_wundef",
"../../rtc_base:logging",
"../../rtc_base:macromagic",
"../../rtc_base:race_checker",
diff --git a/third_party/libwebrtc/modules/audio_device/audio_device_gn/moz.build b/third_party/libwebrtc/modules/audio_device/audio_device_gn/moz.build
index df00e056c6..4128efbbf8 100644
--- a/third_party/libwebrtc/modules/audio_device/audio_device_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_device/audio_device_gn/moz.build
@@ -176,16 +176,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_device/include/audio_device.h b/third_party/libwebrtc/modules/audio_device/include/audio_device.h
index 936ee6cb04..47d2aecfa7 100644
--- a/third_party/libwebrtc/modules/audio_device/include/audio_device.h
+++ b/third_party/libwebrtc/modules/audio_device/include/audio_device.h
@@ -12,16 +12,16 @@
#define MODULES_AUDIO_DEVICE_INCLUDE_AUDIO_DEVICE_H_
#include "absl/types/optional.h"
+#include "api/ref_count.h"
#include "api/scoped_refptr.h"
#include "api/task_queue/task_queue_factory.h"
#include "modules/audio_device/include/audio_device_defines.h"
-#include "rtc_base/ref_count.h"
namespace webrtc {
class AudioDeviceModuleForTest;
-class AudioDeviceModule : public rtc::RefCountInterface {
+class AudioDeviceModule : public webrtc::RefCountInterface {
public:
enum AudioLayer {
kPlatformDefaultAudio = 0,
diff --git a/third_party/libwebrtc/modules/audio_device/include/fake_audio_device.h b/third_party/libwebrtc/modules/audio_device/include/fake_audio_device.h
index 2322ce0263..2a303173e9 100644
--- a/third_party/libwebrtc/modules/audio_device/include/fake_audio_device.h
+++ b/third_party/libwebrtc/modules/audio_device/include/fake_audio_device.h
@@ -23,8 +23,8 @@ class FakeAudioDeviceModule
// references using scoped_refptr. Current code doesn't always use refcounting
// for this class.
void AddRef() const override {}
- rtc::RefCountReleaseStatus Release() const override {
- return rtc::RefCountReleaseStatus::kDroppedLastRef;
+ webrtc::RefCountReleaseStatus Release() const override {
+ return webrtc::RefCountReleaseStatus::kDroppedLastRef;
}
};
diff --git a/third_party/libwebrtc/modules/audio_mixer/audio_frame_manipulator_gn/moz.build b/third_party/libwebrtc/modules/audio_mixer/audio_frame_manipulator_gn/moz.build
index edfac56a3a..cc60512cda 100644
--- a/third_party/libwebrtc/modules/audio_mixer/audio_frame_manipulator_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_mixer/audio_frame_manipulator_gn/moz.build
@@ -199,7 +199,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -209,10 +208,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_mixer/audio_mixer_impl_gn/moz.build b/third_party/libwebrtc/modules/audio_mixer/audio_mixer_impl_gn/moz.build
index 7108d9fbe1..6595939941 100644
--- a/third_party/libwebrtc/modules/audio_mixer/audio_mixer_impl_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_mixer/audio_mixer_impl_gn/moz.build
@@ -202,7 +202,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -212,10 +211,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_processing/BUILD.gn b/third_party/libwebrtc/modules/audio_processing/BUILD.gn
index 2b81427da9..6aca7dee46 100644
--- a/third_party/libwebrtc/modules/audio_processing/BUILD.gn
+++ b/third_party/libwebrtc/modules/audio_processing/BUILD.gn
@@ -29,6 +29,7 @@ rtc_library("api") {
":audio_frame_view",
":audio_processing_statistics",
"../../api:array_view",
+ "../../api:ref_count",
"../../api:scoped_refptr",
"../../api/audio:aec3_config",
"../../api/audio:audio_frame_api",
@@ -190,7 +191,6 @@ rtc_library("audio_processing") {
"../../rtc_base:checks",
"../../rtc_base:event_tracer",
"../../rtc_base:gtest_prod",
- "../../rtc_base:ignore_wundef",
"../../rtc_base:logging",
"../../rtc_base:macromagic",
"../../rtc_base:safe_minmax",
@@ -397,7 +397,6 @@ if (rtc_include_tests) {
"../../common_audio:common_audio_c",
"../../rtc_base:checks",
"../../rtc_base:gtest_prod",
- "../../rtc_base:ignore_wundef",
"../../rtc_base:macromagic",
"../../rtc_base:platform_thread",
"../../rtc_base:protobuf_utils",
@@ -573,7 +572,6 @@ if (rtc_include_tests) {
"../../api/audio:echo_detector_creator",
"../../common_audio",
"../../rtc_base:checks",
- "../../rtc_base:ignore_wundef",
"../../rtc_base:logging",
"../../rtc_base:protobuf_utils",
"../../rtc_base:rtc_json",
@@ -613,7 +611,6 @@ if (rtc_include_tests) {
deps = [
":audioproc_debug_proto",
"../../rtc_base:checks",
- "../../rtc_base:ignore_wundef",
"../../rtc_base:protobuf_utils",
"../../rtc_base/system:arch",
]
diff --git a/third_party/libwebrtc/modules/audio_processing/aec3/adaptive_fir_filter_erl_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/aec3/adaptive_fir_filter_erl_gn/moz.build
index f21e65fb4a..7435b6a457 100644
--- a/third_party/libwebrtc/modules/audio_processing/aec3/adaptive_fir_filter_erl_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_processing/aec3/adaptive_fir_filter_erl_gn/moz.build
@@ -180,16 +180,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_processing/aec3/adaptive_fir_filter_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/aec3/adaptive_fir_filter_gn/moz.build
index b9c819893f..0d2471073d 100644
--- a/third_party/libwebrtc/modules/audio_processing/aec3/adaptive_fir_filter_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_processing/aec3/adaptive_fir_filter_gn/moz.build
@@ -191,16 +191,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_processing/aec3/aec3_avx2_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/aec3/aec3_avx2_gn/moz.build
index 097e67bbe5..f9844b6521 100644
--- a/third_party/libwebrtc/modules/audio_processing/aec3/aec3_avx2_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_processing/aec3/aec3_avx2_gn/moz.build
@@ -181,10 +181,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "x86":
CXXFLAGS += [
diff --git a/third_party/libwebrtc/modules/audio_processing/aec3/aec3_common_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/aec3/aec3_common_gn/moz.build
index 955fe2022f..8d9c4e6bd7 100644
--- a/third_party/libwebrtc/modules/audio_processing/aec3/aec3_common_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_processing/aec3/aec3_common_gn/moz.build
@@ -176,16 +176,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_processing/aec3/aec3_fft_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/aec3/aec3_fft_gn/moz.build
index 154d9f4406..d403ae8b96 100644
--- a/third_party/libwebrtc/modules/audio_processing/aec3/aec3_fft_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_processing/aec3/aec3_fft_gn/moz.build
@@ -191,16 +191,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_processing/aec3/aec3_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/aec3/aec3_gn/moz.build
index 7ad4cffedf..85e5654231 100644
--- a/third_party/libwebrtc/modules/audio_processing/aec3/aec3_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_processing/aec3/aec3_gn/moz.build
@@ -256,7 +256,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -266,10 +265,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_processing/aec3/fft_data_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/aec3/fft_data_gn/moz.build
index 0084077435..aa1aaf15d9 100644
--- a/third_party/libwebrtc/modules/audio_processing/aec3/fft_data_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_processing/aec3/fft_data_gn/moz.build
@@ -180,16 +180,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_processing/aec3/matched_filter_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/aec3/matched_filter_gn/moz.build
index be2c3bbf56..0ebdb0798f 100644
--- a/third_party/libwebrtc/modules/audio_processing/aec3/matched_filter_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_processing/aec3/matched_filter_gn/moz.build
@@ -180,16 +180,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_processing/aec3/render_buffer_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/aec3/render_buffer_gn/moz.build
index 2bd3ae0c01..6444c3137f 100644
--- a/third_party/libwebrtc/modules/audio_processing/aec3/render_buffer_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_processing/aec3/render_buffer_gn/moz.build
@@ -180,16 +180,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_processing/aec3/vector_math_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/aec3/vector_math_gn/moz.build
index e40fdb1cf1..9cf3a7842a 100644
--- a/third_party/libwebrtc/modules/audio_processing/aec3/vector_math_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_processing/aec3/vector_math_gn/moz.build
@@ -180,16 +180,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_processing/aec_dump/BUILD.gn b/third_party/libwebrtc/modules/audio_processing/aec_dump/BUILD.gn
index 38d8776258..78bae56835 100644
--- a/third_party/libwebrtc/modules/audio_processing/aec_dump/BUILD.gn
+++ b/third_party/libwebrtc/modules/audio_processing/aec_dump/BUILD.gn
@@ -66,7 +66,6 @@ if (rtc_enable_protobuf) {
"../../../api/audio:audio_frame_api",
"../../../api/task_queue",
"../../../rtc_base:checks",
- "../../../rtc_base:ignore_wundef",
"../../../rtc_base:logging",
"../../../rtc_base:macromagic",
"../../../rtc_base:protobuf_utils",
diff --git a/third_party/libwebrtc/modules/audio_processing/aec_dump/aec_dump_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/aec_dump/aec_dump_gn/moz.build
index f1280fed0d..13420467de 100644
--- a/third_party/libwebrtc/modules/audio_processing/aec_dump/aec_dump_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_processing/aec_dump/aec_dump_gn/moz.build
@@ -187,16 +187,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_processing/aec_dump/aec_dump_impl.h b/third_party/libwebrtc/modules/audio_processing/aec_dump/aec_dump_impl.h
index fac3712b7a..429808f9af 100644
--- a/third_party/libwebrtc/modules/audio_processing/aec_dump/aec_dump_impl.h
+++ b/third_party/libwebrtc/modules/audio_processing/aec_dump/aec_dump_impl.h
@@ -17,20 +17,17 @@
#include "modules/audio_processing/aec_dump/capture_stream_info.h"
#include "modules/audio_processing/include/aec_dump.h"
-#include "rtc_base/ignore_wundef.h"
#include "rtc_base/race_checker.h"
#include "rtc_base/system/file_wrapper.h"
#include "rtc_base/task_queue.h"
#include "rtc_base/thread_annotations.h"
// Files generated at build-time by the protobuf compiler.
-RTC_PUSH_IGNORING_WUNDEF()
#ifdef WEBRTC_ANDROID_PLATFORM_BUILD
#include "external/webrtc/webrtc/modules/audio_processing/debug.pb.h"
#else
#include "modules/audio_processing/debug.pb.h"
#endif
-RTC_POP_IGNORING_WUNDEF()
namespace webrtc {
diff --git a/third_party/libwebrtc/modules/audio_processing/aec_dump/capture_stream_info.h b/third_party/libwebrtc/modules/audio_processing/aec_dump/capture_stream_info.h
index 0819bbcb23..572990c150 100644
--- a/third_party/libwebrtc/modules/audio_processing/aec_dump/capture_stream_info.h
+++ b/third_party/libwebrtc/modules/audio_processing/aec_dump/capture_stream_info.h
@@ -15,16 +15,13 @@
#include <utility>
#include "modules/audio_processing/include/aec_dump.h"
-#include "rtc_base/ignore_wundef.h"
// Files generated at build-time by the protobuf compiler.
-RTC_PUSH_IGNORING_WUNDEF()
#ifdef WEBRTC_ANDROID_PLATFORM_BUILD
#include "external/webrtc/webrtc/modules/audio_processing/debug.pb.h"
#else
#include "modules/audio_processing/debug.pb.h"
#endif
-RTC_POP_IGNORING_WUNDEF()
namespace webrtc {
diff --git a/third_party/libwebrtc/modules/audio_processing/aec_dump/null_aec_dump_factory_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/aec_dump/null_aec_dump_factory_gn/moz.build
index 2966151ab6..4e8b16442d 100644
--- a/third_party/libwebrtc/modules/audio_processing/aec_dump/null_aec_dump_factory_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_processing/aec_dump/null_aec_dump_factory_gn/moz.build
@@ -195,7 +195,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -205,10 +204,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_processing/aec_dump_interface_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/aec_dump_interface_gn/moz.build
index 02b847ed76..89ae508073 100644
--- a/third_party/libwebrtc/modules/audio_processing/aec_dump_interface_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_processing/aec_dump_interface_gn/moz.build
@@ -195,7 +195,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -205,10 +204,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_processing/aecm/aecm_core_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/aecm/aecm_core_gn/moz.build
index 9ff36991fe..ca3ffeb81e 100644
--- a/third_party/libwebrtc/modules/audio_processing/aecm/aecm_core_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_processing/aecm/aecm_core_gn/moz.build
@@ -238,7 +238,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -248,10 +247,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_processing/agc/agc_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/agc/agc_gn/moz.build
index f6f4442cfc..f26489f413 100644
--- a/third_party/libwebrtc/modules/audio_processing/agc/agc_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_processing/agc/agc_gn/moz.build
@@ -200,7 +200,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -210,10 +209,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_processing/agc/gain_control_interface_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/agc/gain_control_interface_gn/moz.build
index be6b4f9b27..ebf241f7a5 100644
--- a/third_party/libwebrtc/modules/audio_processing/agc/gain_control_interface_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_processing/agc/gain_control_interface_gn/moz.build
@@ -176,16 +176,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_processing/agc/legacy_agc_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/agc/legacy_agc_gn/moz.build
index 4e6e295d34..aa5c6835cc 100644
--- a/third_party/libwebrtc/modules/audio_processing/agc/legacy_agc_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_processing/agc/legacy_agc_gn/moz.build
@@ -200,7 +200,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -210,10 +209,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_processing/agc/level_estimation_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/agc/level_estimation_gn/moz.build
index 64ffa75960..a272555662 100644
--- a/third_party/libwebrtc/modules/audio_processing/agc/level_estimation_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_processing/agc/level_estimation_gn/moz.build
@@ -201,7 +201,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -211,10 +210,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_processing/agc2/adaptive_digital_gain_controller_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/agc2/adaptive_digital_gain_controller_gn/moz.build
index 9473ac62f5..5e3b5801ad 100644
--- a/third_party/libwebrtc/modules/audio_processing/agc2/adaptive_digital_gain_controller_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_processing/agc2/adaptive_digital_gain_controller_gn/moz.build
@@ -200,7 +200,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -210,10 +209,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_processing/agc2/biquad_filter_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/agc2/biquad_filter_gn/moz.build
index c7a2f6d215..d9520efe2f 100644
--- a/third_party/libwebrtc/modules/audio_processing/agc2/biquad_filter_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_processing/agc2/biquad_filter_gn/moz.build
@@ -188,7 +188,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -198,10 +197,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_processing/agc2/clipping_predictor_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/agc2/clipping_predictor_gn/moz.build
index e70e3f68e9..dbf53e8e8e 100644
--- a/third_party/libwebrtc/modules/audio_processing/agc2/clipping_predictor_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_processing/agc2/clipping_predictor_gn/moz.build
@@ -200,7 +200,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -210,10 +209,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_processing/agc2/common_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/agc2/common_gn/moz.build
index 8690613542..44307f4147 100644
--- a/third_party/libwebrtc/modules/audio_processing/agc2/common_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_processing/agc2/common_gn/moz.build
@@ -176,16 +176,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_processing/agc2/cpu_features_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/agc2/cpu_features_gn/moz.build
index 4b0431db1a..e842cac9c3 100644
--- a/third_party/libwebrtc/modules/audio_processing/agc2/cpu_features_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_processing/agc2/cpu_features_gn/moz.build
@@ -199,7 +199,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -209,10 +208,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_processing/agc2/fixed_digital_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/agc2/fixed_digital_gn/moz.build
index 1b8da82f58..60614d4cc1 100644
--- a/third_party/libwebrtc/modules/audio_processing/agc2/fixed_digital_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_processing/agc2/fixed_digital_gn/moz.build
@@ -202,7 +202,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -212,10 +211,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_processing/agc2/gain_applier_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/agc2/gain_applier_gn/moz.build
index bea71dcee3..691900e356 100644
--- a/third_party/libwebrtc/modules/audio_processing/agc2/gain_applier_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_processing/agc2/gain_applier_gn/moz.build
@@ -188,7 +188,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -198,10 +197,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_processing/agc2/gain_map_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/agc2/gain_map_gn/moz.build
index 03eb1fb3a1..ee04e973fb 100644
--- a/third_party/libwebrtc/modules/audio_processing/agc2/gain_map_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_processing/agc2/gain_map_gn/moz.build
@@ -176,16 +176,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_processing/agc2/input_volume_controller_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/agc2/input_volume_controller_gn/moz.build
index f1a841d5ae..0bde4db9d4 100644
--- a/third_party/libwebrtc/modules/audio_processing/agc2/input_volume_controller_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_processing/agc2/input_volume_controller_gn/moz.build
@@ -201,7 +201,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -211,10 +210,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_processing/agc2/input_volume_stats_reporter_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/agc2/input_volume_stats_reporter_gn/moz.build
index 40448f68a9..b7d0a9ba88 100644
--- a/third_party/libwebrtc/modules/audio_processing/agc2/input_volume_stats_reporter_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_processing/agc2/input_volume_stats_reporter_gn/moz.build
@@ -195,7 +195,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -205,10 +204,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_processing/agc2/noise_level_estimator_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/agc2/noise_level_estimator_gn/moz.build
index 9d4629e9ab..210539ab46 100644
--- a/third_party/libwebrtc/modules/audio_processing/agc2/noise_level_estimator_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_processing/agc2/noise_level_estimator_gn/moz.build
@@ -200,7 +200,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -210,10 +209,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_processing/agc2/rnn_vad/rnn_vad_auto_correlation_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/agc2/rnn_vad/rnn_vad_auto_correlation_gn/moz.build
index 134ffac5fd..7965a026ef 100644
--- a/third_party/libwebrtc/modules/audio_processing/agc2/rnn_vad/rnn_vad_auto_correlation_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_processing/agc2/rnn_vad/rnn_vad_auto_correlation_gn/moz.build
@@ -199,7 +199,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -209,10 +208,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_processing/agc2/rnn_vad/rnn_vad_common_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/agc2/rnn_vad/rnn_vad_common_gn/moz.build
index cf3de48a57..bdfe90cf16 100644
--- a/third_party/libwebrtc/modules/audio_processing/agc2/rnn_vad/rnn_vad_common_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_processing/agc2/rnn_vad/rnn_vad_common_gn/moz.build
@@ -191,16 +191,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_processing/agc2/rnn_vad/rnn_vad_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/agc2/rnn_vad/rnn_vad_gn/moz.build
index dbb926c5fc..6a73ce96e4 100644
--- a/third_party/libwebrtc/modules/audio_processing/agc2/rnn_vad/rnn_vad_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_processing/agc2/rnn_vad/rnn_vad_gn/moz.build
@@ -200,7 +200,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -210,10 +209,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_processing/agc2/rnn_vad/rnn_vad_layers_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/agc2/rnn_vad/rnn_vad_layers_gn/moz.build
index 92da260f90..27b40f13a5 100644
--- a/third_party/libwebrtc/modules/audio_processing/agc2/rnn_vad/rnn_vad_layers_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_processing/agc2/rnn_vad/rnn_vad_layers_gn/moz.build
@@ -200,7 +200,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -210,10 +209,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_processing/agc2/rnn_vad/rnn_vad_lp_residual_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/agc2/rnn_vad/rnn_vad_lp_residual_gn/moz.build
index cedb17bc22..d66ed412b2 100644
--- a/third_party/libwebrtc/modules/audio_processing/agc2/rnn_vad/rnn_vad_lp_residual_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_processing/agc2/rnn_vad/rnn_vad_lp_residual_gn/moz.build
@@ -188,7 +188,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -198,10 +197,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_processing/agc2/rnn_vad/rnn_vad_pitch_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/agc2/rnn_vad/rnn_vad_pitch_gn/moz.build
index d45bc78ff6..0ddc85f5ac 100644
--- a/third_party/libwebrtc/modules/audio_processing/agc2/rnn_vad/rnn_vad_pitch_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_processing/agc2/rnn_vad/rnn_vad_pitch_gn/moz.build
@@ -200,7 +200,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -210,10 +209,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_processing/agc2/rnn_vad/rnn_vad_ring_buffer_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/agc2/rnn_vad/rnn_vad_ring_buffer_gn/moz.build
index 20da5f3615..25e813a226 100644
--- a/third_party/libwebrtc/modules/audio_processing/agc2/rnn_vad/rnn_vad_ring_buffer_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_processing/agc2/rnn_vad/rnn_vad_ring_buffer_gn/moz.build
@@ -180,16 +180,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_processing/agc2/rnn_vad/rnn_vad_sequence_buffer_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/agc2/rnn_vad/rnn_vad_sequence_buffer_gn/moz.build
index b0ba79562e..f54dd88a23 100644
--- a/third_party/libwebrtc/modules/audio_processing/agc2/rnn_vad/rnn_vad_sequence_buffer_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_processing/agc2/rnn_vad/rnn_vad_sequence_buffer_gn/moz.build
@@ -180,16 +180,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_processing/agc2/rnn_vad/rnn_vad_spectral_features_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/agc2/rnn_vad/rnn_vad_spectral_features_gn/moz.build
index 2d8396fa2a..d8b88047d7 100644
--- a/third_party/libwebrtc/modules/audio_processing/agc2/rnn_vad/rnn_vad_spectral_features_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_processing/agc2/rnn_vad/rnn_vad_spectral_features_gn/moz.build
@@ -200,7 +200,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -210,10 +209,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_processing/agc2/rnn_vad/rnn_vad_symmetric_matrix_buffer_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/agc2/rnn_vad/rnn_vad_symmetric_matrix_buffer_gn/moz.build
index 143ba6960c..4a3c5bf28b 100644
--- a/third_party/libwebrtc/modules/audio_processing/agc2/rnn_vad/rnn_vad_symmetric_matrix_buffer_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_processing/agc2/rnn_vad/rnn_vad_symmetric_matrix_buffer_gn/moz.build
@@ -180,16 +180,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_processing/agc2/rnn_vad/vector_math_avx2_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/agc2/rnn_vad/vector_math_avx2_gn/moz.build
index d4dd169f15..01313fa460 100644
--- a/third_party/libwebrtc/modules/audio_processing/agc2/rnn_vad/vector_math_avx2_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_processing/agc2/rnn_vad/vector_math_avx2_gn/moz.build
@@ -176,10 +176,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "x86":
CXXFLAGS += [
diff --git a/third_party/libwebrtc/modules/audio_processing/agc2/rnn_vad/vector_math_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/agc2/rnn_vad/vector_math_gn/moz.build
index 09fe0c3d24..3f88913309 100644
--- a/third_party/libwebrtc/modules/audio_processing/agc2/rnn_vad/vector_math_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_processing/agc2/rnn_vad/vector_math_gn/moz.build
@@ -191,16 +191,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_processing/agc2/saturation_protector_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/agc2/saturation_protector_gn/moz.build
index 6b8def8650..6562d840b7 100644
--- a/third_party/libwebrtc/modules/audio_processing/agc2/saturation_protector_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_processing/agc2/saturation_protector_gn/moz.build
@@ -201,7 +201,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -211,10 +210,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_processing/agc2/speech_level_estimator_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/agc2/speech_level_estimator_gn/moz.build
index 8f2996fa26..3afaa88450 100644
--- a/third_party/libwebrtc/modules/audio_processing/agc2/speech_level_estimator_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_processing/agc2/speech_level_estimator_gn/moz.build
@@ -200,7 +200,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -210,10 +209,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_processing/agc2/vad_wrapper_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/agc2/vad_wrapper_gn/moz.build
index 55cfbb60e7..3aa09832b2 100644
--- a/third_party/libwebrtc/modules/audio_processing/agc2/vad_wrapper_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_processing/agc2/vad_wrapper_gn/moz.build
@@ -199,7 +199,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -209,10 +208,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_processing/api_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/api_gn/moz.build
index 7a02b7e10c..37e50af014 100644
--- a/third_party/libwebrtc/modules/audio_processing/api_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_processing/api_gn/moz.build
@@ -195,7 +195,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -205,10 +204,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_processing/apm_logging_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/apm_logging_gn/moz.build
index 992376cd8a..53fd9d9f94 100644
--- a/third_party/libwebrtc/modules/audio_processing/apm_logging_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_processing/apm_logging_gn/moz.build
@@ -200,7 +200,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -210,10 +209,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_processing/audio_buffer_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/audio_buffer_gn/moz.build
index 88031a747d..2087aeb909 100644
--- a/third_party/libwebrtc/modules/audio_processing/audio_buffer_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_processing/audio_buffer_gn/moz.build
@@ -202,7 +202,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -212,10 +211,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_processing/audio_frame_proxies_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/audio_frame_proxies_gn/moz.build
index 7e73b70483..737ca5e834 100644
--- a/third_party/libwebrtc/modules/audio_processing/audio_frame_proxies_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_processing/audio_frame_proxies_gn/moz.build
@@ -195,7 +195,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -205,10 +204,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_processing/audio_frame_view_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/audio_frame_view_gn/moz.build
index 0f81755091..b7391a78b1 100644
--- a/third_party/libwebrtc/modules/audio_processing/audio_frame_view_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_processing/audio_frame_view_gn/moz.build
@@ -180,16 +180,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_processing/audio_processing_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/audio_processing_gn/moz.build
index 7dc22bcf2b..5b4f4d5d54 100644
--- a/third_party/libwebrtc/modules/audio_processing/audio_processing_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_processing/audio_processing_gn/moz.build
@@ -206,7 +206,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -216,10 +215,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_processing/audio_processing_impl.h b/third_party/libwebrtc/modules/audio_processing/audio_processing_impl.h
index fe80e0d912..1e058b5a32 100644
--- a/third_party/libwebrtc/modules/audio_processing/audio_processing_impl.h
+++ b/third_party/libwebrtc/modules/audio_processing/audio_processing_impl.h
@@ -43,7 +43,6 @@
#include "modules/audio_processing/rms_level.h"
#include "modules/audio_processing/transient/transient_suppressor.h"
#include "rtc_base/gtest_prod_util.h"
-#include "rtc_base/ignore_wundef.h"
#include "rtc_base/swap_queue.h"
#include "rtc_base/synchronization/mutex.h"
#include "rtc_base/thread_annotations.h"
diff --git a/third_party/libwebrtc/modules/audio_processing/audio_processing_impl_unittest.cc b/third_party/libwebrtc/modules/audio_processing/audio_processing_impl_unittest.cc
index 9e50f994b1..e03f966b06 100644
--- a/third_party/libwebrtc/modules/audio_processing/audio_processing_impl_unittest.cc
+++ b/third_party/libwebrtc/modules/audio_processing/audio_processing_impl_unittest.cc
@@ -48,7 +48,7 @@ class MockInitialize : public AudioProcessingImpl {
}
MOCK_METHOD(void, AddRef, (), (const, override));
- MOCK_METHOD(rtc::RefCountReleaseStatus, Release, (), (const, override));
+ MOCK_METHOD(RefCountReleaseStatus, Release, (), (const, override));
};
// Creates MockEchoControl instances and provides a raw pointer access to
diff --git a/third_party/libwebrtc/modules/audio_processing/audio_processing_statistics_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/audio_processing_statistics_gn/moz.build
index 6d174505ed..6b3e54c3f7 100644
--- a/third_party/libwebrtc/modules/audio_processing/audio_processing_statistics_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_processing/audio_processing_statistics_gn/moz.build
@@ -184,7 +184,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -194,10 +193,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_processing/audio_processing_unittest.cc b/third_party/libwebrtc/modules/audio_processing/audio_processing_unittest.cc
index e320e71405..c2bedb2da4 100644
--- a/third_party/libwebrtc/modules/audio_processing/audio_processing_unittest.cc
+++ b/third_party/libwebrtc/modules/audio_processing/audio_processing_unittest.cc
@@ -38,7 +38,6 @@
#include "rtc_base/checks.h"
#include "rtc_base/fake_clock.h"
#include "rtc_base/gtest_prod_util.h"
-#include "rtc_base/ignore_wundef.h"
#include "rtc_base/numerics/safe_conversions.h"
#include "rtc_base/numerics/safe_minmax.h"
#include "rtc_base/protobuf_utils.h"
@@ -51,14 +50,13 @@
#include "test/gtest.h"
#include "test/testsupport/file_utils.h"
-RTC_PUSH_IGNORING_WUNDEF()
-#include "modules/audio_processing/debug.pb.h"
#ifdef WEBRTC_ANDROID_PLATFORM_BUILD
+#include "external/webrtc/webrtc/modules/audio_processing/debug.pb.h"
#include "external/webrtc/webrtc/modules/audio_processing/test/unittest.pb.h"
#else
+#include "modules/audio_processing/debug.pb.h"
#include "modules/audio_processing/test/unittest.pb.h"
#endif
-RTC_POP_IGNORING_WUNDEF()
ABSL_FLAG(bool,
write_apm_ref_data,
diff --git a/third_party/libwebrtc/modules/audio_processing/capture_levels_adjuster/capture_levels_adjuster_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/capture_levels_adjuster/capture_levels_adjuster_gn/moz.build
index d80a3bb1c6..ad198344e2 100644
--- a/third_party/libwebrtc/modules/audio_processing/capture_levels_adjuster/capture_levels_adjuster_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_processing/capture_levels_adjuster/capture_levels_adjuster_gn/moz.build
@@ -200,7 +200,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -210,10 +209,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_processing/gain_controller2_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/gain_controller2_gn/moz.build
index d6d9d3658b..ab31e68564 100644
--- a/third_party/libwebrtc/modules/audio_processing/gain_controller2_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_processing/gain_controller2_gn/moz.build
@@ -200,7 +200,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -210,10 +209,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_processing/high_pass_filter_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/high_pass_filter_gn/moz.build
index 8769a3a318..af06d4142f 100644
--- a/third_party/libwebrtc/modules/audio_processing/high_pass_filter_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_processing/high_pass_filter_gn/moz.build
@@ -199,7 +199,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -209,10 +208,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_processing/include/audio_processing.h b/third_party/libwebrtc/modules/audio_processing/include/audio_processing.h
index f613a38de1..e3223513af 100644
--- a/third_party/libwebrtc/modules/audio_processing/include/audio_processing.h
+++ b/third_party/libwebrtc/modules/audio_processing/include/audio_processing.h
@@ -28,10 +28,10 @@
#include "api/array_view.h"
#include "api/audio/echo_canceller3_config.h"
#include "api/audio/echo_control.h"
+#include "api/ref_count.h"
#include "api/scoped_refptr.h"
#include "modules/audio_processing/include/audio_processing_statistics.h"
#include "rtc_base/arraysize.h"
-#include "rtc_base/ref_count.h"
#include "rtc_base/system/file_wrapper.h"
#include "rtc_base/system/rtc_export.h"
@@ -127,7 +127,7 @@ class CustomProcessing;
// // Close the application...
// apm.reset();
//
-class RTC_EXPORT AudioProcessing : public rtc::RefCountInterface {
+class RTC_EXPORT AudioProcessing : public RefCountInterface {
public:
// The struct below constitutes the new parameter scheme for the audio
// processing. It is being introduced gradually and until it is fully
@@ -912,7 +912,7 @@ class CustomProcessing {
};
// Interface for an echo detector submodule.
-class EchoDetector : public rtc::RefCountInterface {
+class EchoDetector : public RefCountInterface {
public:
// (Re-)Initializes the submodule.
virtual void Initialize(int capture_sample_rate_hz,
diff --git a/third_party/libwebrtc/modules/audio_processing/ns/ns_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/ns/ns_gn/moz.build
index 14595abaf9..ac1c19134a 100644
--- a/third_party/libwebrtc/modules/audio_processing/ns/ns_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_processing/ns/ns_gn/moz.build
@@ -212,7 +212,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -222,10 +221,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_processing/optionally_built_submodule_creators_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/optionally_built_submodule_creators_gn/moz.build
index af79a781f6..da2a1b7ae3 100644
--- a/third_party/libwebrtc/modules/audio_processing/optionally_built_submodule_creators_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_processing/optionally_built_submodule_creators_gn/moz.build
@@ -199,7 +199,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -209,10 +208,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_processing/rms_level_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/rms_level_gn/moz.build
index 23f52652ae..d0c4b2bd8e 100644
--- a/third_party/libwebrtc/modules/audio_processing/rms_level_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_processing/rms_level_gn/moz.build
@@ -188,7 +188,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -198,10 +197,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_processing/test/aec_dump_based_simulator.h b/third_party/libwebrtc/modules/audio_processing/test/aec_dump_based_simulator.h
index e2c1f3e4ba..4713c800ec 100644
--- a/third_party/libwebrtc/modules/audio_processing/test/aec_dump_based_simulator.h
+++ b/third_party/libwebrtc/modules/audio_processing/test/aec_dump_based_simulator.h
@@ -15,15 +15,12 @@
#include <string>
#include "modules/audio_processing/test/audio_processing_simulator.h"
-#include "rtc_base/ignore_wundef.h"
-RTC_PUSH_IGNORING_WUNDEF()
#ifdef WEBRTC_ANDROID_PLATFORM_BUILD
#include "external/webrtc/webrtc/modules/audio_processing/debug.pb.h"
#else
#include "modules/audio_processing/debug.pb.h"
#endif
-RTC_POP_IGNORING_WUNDEF()
namespace webrtc {
namespace test {
diff --git a/third_party/libwebrtc/modules/audio_processing/test/debug_dump_replayer.h b/third_party/libwebrtc/modules/audio_processing/test/debug_dump_replayer.h
index be21c68663..077147eb68 100644
--- a/third_party/libwebrtc/modules/audio_processing/test/debug_dump_replayer.h
+++ b/third_party/libwebrtc/modules/audio_processing/test/debug_dump_replayer.h
@@ -16,11 +16,9 @@
#include "absl/strings/string_view.h"
#include "common_audio/channel_buffer.h"
#include "modules/audio_processing/include/audio_processing.h"
-#include "rtc_base/ignore_wundef.h"
-RTC_PUSH_IGNORING_WUNDEF()
+// Generated at build-time by the protobuf compiler.
#include "modules/audio_processing/debug.pb.h"
-RTC_POP_IGNORING_WUNDEF()
namespace webrtc {
namespace test {
diff --git a/third_party/libwebrtc/modules/audio_processing/test/protobuf_utils.h b/third_party/libwebrtc/modules/audio_processing/test/protobuf_utils.h
index b9c2e819f9..eb93383f5a 100644
--- a/third_party/libwebrtc/modules/audio_processing/test/protobuf_utils.h
+++ b/third_party/libwebrtc/modules/audio_processing/test/protobuf_utils.h
@@ -14,12 +14,10 @@
#include <memory>
#include <sstream> // no-presubmit-check TODO(webrtc:8982)
-#include "rtc_base/ignore_wundef.h"
#include "rtc_base/protobuf_utils.h"
-RTC_PUSH_IGNORING_WUNDEF()
+// Generated at build-time by the protobuf compiler.
#include "modules/audio_processing/debug.pb.h"
-RTC_POP_IGNORING_WUNDEF()
namespace webrtc {
diff --git a/third_party/libwebrtc/modules/audio_processing/transient/transient_suppressor_api_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/transient/transient_suppressor_api_gn/moz.build
index 6310b948ac..44571715b8 100644
--- a/third_party/libwebrtc/modules/audio_processing/transient/transient_suppressor_api_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_processing/transient/transient_suppressor_api_gn/moz.build
@@ -176,16 +176,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_processing/transient/transient_suppressor_impl_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/transient/transient_suppressor_impl_gn/moz.build
index 31e0736f30..d700fc1a32 100644
--- a/third_party/libwebrtc/modules/audio_processing/transient/transient_suppressor_impl_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_processing/transient/transient_suppressor_impl_gn/moz.build
@@ -203,7 +203,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -213,10 +212,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_processing/transient/voice_probability_delay_unit_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/transient/voice_probability_delay_unit_gn/moz.build
index 52587c0890..c67675f431 100644
--- a/third_party/libwebrtc/modules/audio_processing/transient/voice_probability_delay_unit_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_processing/transient/voice_probability_delay_unit_gn/moz.build
@@ -188,7 +188,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -198,10 +197,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_processing/utility/cascaded_biquad_filter_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/utility/cascaded_biquad_filter_gn/moz.build
index 02813d2513..b6566a8950 100644
--- a/third_party/libwebrtc/modules/audio_processing/utility/cascaded_biquad_filter_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_processing/utility/cascaded_biquad_filter_gn/moz.build
@@ -188,7 +188,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -198,10 +197,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_processing/utility/legacy_delay_estimator_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/utility/legacy_delay_estimator_gn/moz.build
index 67c6a218f6..c20d5b6189 100644
--- a/third_party/libwebrtc/modules/audio_processing/utility/legacy_delay_estimator_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_processing/utility/legacy_delay_estimator_gn/moz.build
@@ -189,7 +189,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -199,10 +198,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_processing/utility/pffft_wrapper_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/utility/pffft_wrapper_gn/moz.build
index 3213706005..936decab70 100644
--- a/third_party/libwebrtc/modules/audio_processing/utility/pffft_wrapper_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_processing/utility/pffft_wrapper_gn/moz.build
@@ -188,7 +188,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -198,10 +197,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/audio_processing/vad/vad_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/vad/vad_gn/moz.build
index 3739bbef8a..0ae31f5a2e 100644
--- a/third_party/libwebrtc/modules/audio_processing/vad/vad_gn/moz.build
+++ b/third_party/libwebrtc/modules/audio_processing/vad/vad_gn/moz.build
@@ -206,7 +206,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -216,10 +215,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/congestion_controller/congestion_controller_gn/moz.build b/third_party/libwebrtc/modules/congestion_controller/congestion_controller_gn/moz.build
index b5bcafa45f..1190193b94 100644
--- a/third_party/libwebrtc/modules/congestion_controller/congestion_controller_gn/moz.build
+++ b/third_party/libwebrtc/modules/congestion_controller/congestion_controller_gn/moz.build
@@ -202,7 +202,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -212,10 +211,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/alr_detector_gn/moz.build b/third_party/libwebrtc/modules/congestion_controller/goog_cc/alr_detector_gn/moz.build
index b48fc38c39..40fd1189aa 100644
--- a/third_party/libwebrtc/modules/congestion_controller/goog_cc/alr_detector_gn/moz.build
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/alr_detector_gn/moz.build
@@ -195,7 +195,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -205,10 +204,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/delay_based_bwe_gn/moz.build b/third_party/libwebrtc/modules/congestion_controller/goog_cc/delay_based_bwe_gn/moz.build
index 31d8c420f6..e2087c6126 100644
--- a/third_party/libwebrtc/modules/congestion_controller/goog_cc/delay_based_bwe_gn/moz.build
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/delay_based_bwe_gn/moz.build
@@ -202,7 +202,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -212,10 +211,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/estimators_gn/moz.build b/third_party/libwebrtc/modules/congestion_controller/goog_cc/estimators_gn/moz.build
index e233806b43..7b77d3dc86 100644
--- a/third_party/libwebrtc/modules/congestion_controller/goog_cc/estimators_gn/moz.build
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/estimators_gn/moz.build
@@ -205,7 +205,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -215,10 +214,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/goog_cc_gn/moz.build b/third_party/libwebrtc/modules/congestion_controller/goog_cc/goog_cc_gn/moz.build
index 147a08113b..0e5182a469 100644
--- a/third_party/libwebrtc/modules/congestion_controller/goog_cc/goog_cc_gn/moz.build
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/goog_cc_gn/moz.build
@@ -201,7 +201,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -211,10 +210,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/link_capacity_estimator_gn/moz.build b/third_party/libwebrtc/modules/congestion_controller/goog_cc/link_capacity_estimator_gn/moz.build
index 0ee8a34df8..04b78b5988 100644
--- a/third_party/libwebrtc/modules/congestion_controller/goog_cc/link_capacity_estimator_gn/moz.build
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/link_capacity_estimator_gn/moz.build
@@ -188,7 +188,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -198,10 +197,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/loss_based_bwe_v1_gn/moz.build b/third_party/libwebrtc/modules/congestion_controller/goog_cc/loss_based_bwe_v1_gn/moz.build
index 5931292efe..d290fbe9ec 100644
--- a/third_party/libwebrtc/modules/congestion_controller/goog_cc/loss_based_bwe_v1_gn/moz.build
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/loss_based_bwe_v1_gn/moz.build
@@ -196,7 +196,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -206,10 +205,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/loss_based_bwe_v2.cc b/third_party/libwebrtc/modules/congestion_controller/goog_cc/loss_based_bwe_v2.cc
index ef200869a6..8e1a3c4698 100644
--- a/third_party/libwebrtc/modules/congestion_controller/goog_cc/loss_based_bwe_v2.cc
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/loss_based_bwe_v2.cc
@@ -132,7 +132,7 @@ LossBasedBweV2::LossBasedBweV2(const FieldTrialsView* key_value_config)
instant_upper_bound_temporal_weights_.resize(
config_->observation_window_size);
CalculateTemporalWeights();
- hold_duration_ = kInitHoldDuration;
+ last_hold_info_.duration = kInitHoldDuration;
}
bool LossBasedBweV2::IsEnabled() const {
@@ -149,6 +149,10 @@ bool LossBasedBweV2::ReadyToUseInStartPhase() const {
return IsReady() && config_->use_in_start_phase;
}
+bool LossBasedBweV2::UseInStartPhase() const {
+ return config_->use_in_start_phase;
+}
+
LossBasedBweV2::Result LossBasedBweV2::GetLossBasedResult() const {
if (!IsReady()) {
if (!IsEnabled()) {
@@ -289,50 +293,69 @@ void LossBasedBweV2::UpdateBandwidthEstimate(
/*new_estimate=*/best_candidate.loss_limited_bandwidth);
// Bound the best candidate by the acked bitrate.
if (increasing_when_loss_limited && IsValid(acknowledged_bitrate_)) {
+ double rampup_factor = config_->bandwidth_rampup_upper_bound_factor;
+ if (IsValid(last_hold_info_.rate) &&
+ acknowledged_bitrate_ <
+ config_->bandwidth_rampup_hold_threshold * last_hold_info_.rate) {
+ rampup_factor = config_->bandwidth_rampup_upper_bound_factor_in_hold;
+ }
+
best_candidate.loss_limited_bandwidth =
std::max(current_best_estimate_.loss_limited_bandwidth,
std::min(best_candidate.loss_limited_bandwidth,
- config_->bandwidth_rampup_upper_bound_factor *
- (*acknowledged_bitrate_)));
+ rampup_factor * (*acknowledged_bitrate_)));
+ // Increase current estimate by at least 1kbps to make sure that the state
+ // will be switched to kIncreasing, thus padding is triggered.
+ if (loss_based_result_.state == LossBasedState::kDecreasing &&
+ best_candidate.loss_limited_bandwidth ==
+ current_best_estimate_.loss_limited_bandwidth) {
+ best_candidate.loss_limited_bandwidth =
+ current_best_estimate_.loss_limited_bandwidth +
+ DataRate::BitsPerSec(1);
+ }
}
}
- current_best_estimate_ = best_candidate;
- UpdateResult();
-
- if (IsInLossLimitedState() &&
- (recovering_after_loss_timestamp_.IsInfinite() ||
- recovering_after_loss_timestamp_ + config_->delayed_increase_window <
- last_send_time_most_recent_observation_)) {
- bandwidth_limit_in_current_window_ =
- std::max(kCongestionControllerMinBitrate,
- current_best_estimate_.loss_limited_bandwidth *
- config_->max_increase_factor);
- recovering_after_loss_timestamp_ = last_send_time_most_recent_observation_;
- }
-}
-
-void LossBasedBweV2::UpdateResult() {
DataRate bounded_bandwidth_estimate = DataRate::PlusInfinity();
if (IsValid(delay_based_estimate_)) {
bounded_bandwidth_estimate =
std::max(GetInstantLowerBound(),
- std::min({current_best_estimate_.loss_limited_bandwidth,
+ std::min({best_candidate.loss_limited_bandwidth,
GetInstantUpperBound(), delay_based_estimate_}));
} else {
- bounded_bandwidth_estimate =
- std::max(GetInstantLowerBound(),
- std::min(current_best_estimate_.loss_limited_bandwidth,
- GetInstantUpperBound()));
+ bounded_bandwidth_estimate = std::max(
+ GetInstantLowerBound(), std::min(best_candidate.loss_limited_bandwidth,
+ GetInstantUpperBound()));
+ }
+ if (config_->bound_best_candidate &&
+ bounded_bandwidth_estimate < best_candidate.loss_limited_bandwidth) {
+ RTC_LOG(LS_INFO) << "Resetting loss based BWE to "
+ << bounded_bandwidth_estimate.kbps()
+ << "due to loss. Avg loss rate: "
+ << GetAverageReportedLossRatio();
+ current_best_estimate_.loss_limited_bandwidth = bounded_bandwidth_estimate;
+ current_best_estimate_.inherent_loss = 0;
+ } else {
+ current_best_estimate_ = best_candidate;
+ if (config_->lower_bound_by_acked_rate_factor > 0.0) {
+ current_best_estimate_.loss_limited_bandwidth =
+ std::max(current_best_estimate_.loss_limited_bandwidth,
+ GetInstantLowerBound());
+ }
}
if (loss_based_result_.state == LossBasedState::kDecreasing &&
- last_hold_timestamp_ > last_send_time_most_recent_observation_ &&
+ last_hold_info_.timestamp > last_send_time_most_recent_observation_ &&
bounded_bandwidth_estimate < delay_based_estimate_) {
- // BWE is not allowed to increase during the HOLD duration. The purpose of
+ // Ensure that acked rate is the lower bound of HOLD rate.
+ if (config_->lower_bound_by_acked_rate_factor > 0.0) {
+ last_hold_info_.rate =
+ std::max(GetInstantLowerBound(), last_hold_info_.rate);
+ }
+ // BWE is not allowed to increase above the HOLD rate. The purpose of
// HOLD is to not immediately ramp up BWE to a rate that may cause loss.
- loss_based_result_.bandwidth_estimate = std::min(
- loss_based_result_.bandwidth_estimate, bounded_bandwidth_estimate);
+ loss_based_result_.bandwidth_estimate =
+ std::min(last_hold_info_.rate, bounded_bandwidth_estimate);
return;
}
@@ -359,22 +382,38 @@ void LossBasedBweV2::UpdateResult() {
RTC_LOG(LS_INFO) << this << " "
<< "Switch to HOLD. Bounded BWE: "
<< bounded_bandwidth_estimate.kbps()
- << ", duration: " << hold_duration_.seconds();
- last_hold_timestamp_ =
- last_send_time_most_recent_observation_ + hold_duration_;
- hold_duration_ = std::min(kMaxHoldDuration,
- hold_duration_ * config_->hold_duration_factor);
+ << ", duration: " << last_hold_info_.duration.ms();
+ last_hold_info_ = {
+ .timestamp = last_send_time_most_recent_observation_ +
+ last_hold_info_.duration,
+ .duration =
+ std::min(kMaxHoldDuration, last_hold_info_.duration *
+ config_->hold_duration_factor),
+ .rate = bounded_bandwidth_estimate};
}
last_padding_info_ = PaddingInfo();
loss_based_result_.state = LossBasedState::kDecreasing;
} else {
- // Reset the HOLD duration if delay based estimate works to avoid getting
+ // Reset the HOLD info if delay based estimate works to avoid getting
// stuck in low bitrate.
- hold_duration_ = kInitHoldDuration;
+ last_hold_info_ = {.timestamp = Timestamp::MinusInfinity(),
+ .duration = kInitHoldDuration,
+ .rate = DataRate::PlusInfinity()};
last_padding_info_ = PaddingInfo();
loss_based_result_.state = LossBasedState::kDelayBasedEstimate;
}
loss_based_result_.bandwidth_estimate = bounded_bandwidth_estimate;
+
+ if (IsInLossLimitedState() &&
+ (recovering_after_loss_timestamp_.IsInfinite() ||
+ recovering_after_loss_timestamp_ + config_->delayed_increase_window <
+ last_send_time_most_recent_observation_)) {
+ bandwidth_limit_in_current_window_ =
+ std::max(kCongestionControllerMinBitrate,
+ current_best_estimate_.loss_limited_bandwidth *
+ config_->max_increase_factor);
+ recovering_after_loss_timestamp_ = last_send_time_most_recent_observation_;
+ }
}
bool LossBasedBweV2::IsEstimateIncreasingWhenLossLimited(
@@ -394,6 +433,10 @@ absl::optional<LossBasedBweV2::Config> LossBasedBweV2::CreateConfig(
FieldTrialParameter<bool> enabled("Enabled", true);
FieldTrialParameter<double> bandwidth_rampup_upper_bound_factor(
"BwRampupUpperBoundFactor", 1000000.0);
+ FieldTrialParameter<double> bandwidth_rampup_upper_bound_factor_in_hold(
+ "BwRampupUpperBoundInHoldFactor", 1000000.0);
+ FieldTrialParameter<double> bandwidth_rampup_hold_threshold(
+ "BwRampupUpperBoundHoldThreshold", 1.3);
FieldTrialParameter<double> rampup_acceleration_max_factor(
"BwRampupAccelMaxFactor", 0.0);
FieldTrialParameter<TimeDelta> rampup_acceleration_maxout_time(
@@ -445,12 +488,6 @@ absl::optional<LossBasedBweV2::Config> LossBasedBweV2::CreateConfig(
FieldTrialParameter<bool>
not_increase_if_inherent_loss_less_than_average_loss(
"NotIncreaseIfInherentLossLessThanAverageLoss", true);
- FieldTrialParameter<double> high_loss_rate_threshold("HighLossRateThreshold",
- 1.0);
- FieldTrialParameter<DataRate> bandwidth_cap_at_high_loss_rate(
- "BandwidthCapAtHighLossRate", DataRate::KilobitsPerSec(500.0));
- FieldTrialParameter<double> slope_of_bwe_high_loss_func(
- "SlopeOfBweHighLossFunc", 1000);
FieldTrialParameter<bool> not_use_acked_rate_in_alr("NotUseAckedRateInAlr",
true);
FieldTrialParameter<bool> use_in_start_phase("UseInStartPhase", false);
@@ -461,9 +498,12 @@ absl::optional<LossBasedBweV2::Config> LossBasedBweV2::CreateConfig(
FieldTrialParameter<bool> use_byte_loss_rate("UseByteLossRate", false);
FieldTrialParameter<TimeDelta> padding_duration("PaddingDuration",
TimeDelta::Zero());
+ FieldTrialParameter<bool> bound_best_candidate("BoundBestCandidate", false);
if (key_value_config) {
ParseFieldTrial({&enabled,
&bandwidth_rampup_upper_bound_factor,
+ &bandwidth_rampup_upper_bound_factor_in_hold,
+ &bandwidth_rampup_hold_threshold,
&rampup_acceleration_max_factor,
&rampup_acceleration_maxout_time,
&candidate_factors,
@@ -491,16 +531,14 @@ absl::optional<LossBasedBweV2::Config> LossBasedBweV2::CreateConfig(
&max_increase_factor,
&delayed_increase_window,
&not_increase_if_inherent_loss_less_than_average_loss,
- &high_loss_rate_threshold,
- &bandwidth_cap_at_high_loss_rate,
- &slope_of_bwe_high_loss_func,
&not_use_acked_rate_in_alr,
&use_in_start_phase,
&min_num_observations,
&lower_bound_by_acked_rate_factor,
&hold_duration_factor,
&use_byte_loss_rate,
- &padding_duration},
+ &padding_duration,
+ &bound_best_candidate},
key_value_config->Lookup("WebRTC-Bwe-LossBasedBweV2"));
}
@@ -511,6 +549,10 @@ absl::optional<LossBasedBweV2::Config> LossBasedBweV2::CreateConfig(
config.emplace(Config());
config->bandwidth_rampup_upper_bound_factor =
bandwidth_rampup_upper_bound_factor.Get();
+ config->bandwidth_rampup_upper_bound_factor_in_hold =
+ bandwidth_rampup_upper_bound_factor_in_hold.Get();
+ config->bandwidth_rampup_hold_threshold =
+ bandwidth_rampup_hold_threshold.Get();
config->rampup_acceleration_max_factor = rampup_acceleration_max_factor.Get();
config->rampup_acceleration_maxout_time =
rampup_acceleration_maxout_time.Get();
@@ -553,10 +595,6 @@ absl::optional<LossBasedBweV2::Config> LossBasedBweV2::CreateConfig(
config->delayed_increase_window = delayed_increase_window.Get();
config->not_increase_if_inherent_loss_less_than_average_loss =
not_increase_if_inherent_loss_less_than_average_loss.Get();
- config->high_loss_rate_threshold = high_loss_rate_threshold.Get();
- config->bandwidth_cap_at_high_loss_rate =
- bandwidth_cap_at_high_loss_rate.Get();
- config->slope_of_bwe_high_loss_func = slope_of_bwe_high_loss_func.Get();
config->not_use_acked_rate_in_alr = not_use_acked_rate_in_alr.Get();
config->use_in_start_phase = use_in_start_phase.Get();
config->min_num_observations = min_num_observations.Get();
@@ -565,7 +603,7 @@ absl::optional<LossBasedBweV2::Config> LossBasedBweV2::CreateConfig(
config->hold_duration_factor = hold_duration_factor.Get();
config->use_byte_loss_rate = use_byte_loss_rate.Get();
config->padding_duration = padding_duration.Get();
-
+ config->bound_best_candidate = bound_best_candidate.Get();
return config;
}
@@ -582,6 +620,18 @@ bool LossBasedBweV2::IsConfigValid() const {
<< config_->bandwidth_rampup_upper_bound_factor;
valid = false;
}
+ if (config_->bandwidth_rampup_upper_bound_factor_in_hold <= 1.0) {
+ RTC_LOG(LS_WARNING) << "The bandwidth rampup upper bound factor in hold "
+ "must be greater than 1: "
+ << config_->bandwidth_rampup_upper_bound_factor_in_hold;
+ valid = false;
+ }
+ if (config_->bandwidth_rampup_hold_threshold < 0.0) {
+ RTC_LOG(LS_WARNING) << "The bandwidth rampup hold threshold must"
+ "must be non-negative.: "
+ << config_->bandwidth_rampup_hold_threshold;
+ valid = false;
+ }
if (config_->rampup_acceleration_max_factor < 0.0) {
RTC_LOG(LS_WARNING)
<< "The rampup acceleration max factor must be non-negative.: "
@@ -739,12 +789,6 @@ bool LossBasedBweV2::IsConfigValid() const {
<< config_->delayed_increase_window.ms();
valid = false;
}
- if (config_->high_loss_rate_threshold <= 0.0 ||
- config_->high_loss_rate_threshold > 1.0) {
- RTC_LOG(LS_WARNING) << "The high loss rate threshold must be in (0, 1]: "
- << config_->high_loss_rate_threshold;
- valid = false;
- }
if (config_->min_num_observations <= 0) {
RTC_LOG(LS_WARNING) << "The min number of observations must be positive: "
<< config_->min_num_observations;
@@ -834,15 +878,19 @@ DataRate LossBasedBweV2::GetCandidateBandwidthUpperBound() const {
std::vector<LossBasedBweV2::ChannelParameters> LossBasedBweV2::GetCandidates(
bool in_alr) const {
+ ChannelParameters best_estimate = current_best_estimate_;
std::vector<DataRate> bandwidths;
for (double candidate_factor : config_->candidate_factors) {
bandwidths.push_back(candidate_factor *
- current_best_estimate_.loss_limited_bandwidth);
+ best_estimate.loss_limited_bandwidth);
}
if (acknowledged_bitrate_.has_value() &&
config_->append_acknowledged_rate_candidate) {
- if (!(config_->not_use_acked_rate_in_alr && in_alr)) {
+ if (!(config_->not_use_acked_rate_in_alr && in_alr) ||
+ (config_->padding_duration > TimeDelta::Zero() &&
+ last_padding_info_.padding_timestamp + config_->padding_duration >=
+ last_send_time_most_recent_observation_)) {
bandwidths.push_back(*acknowledged_bitrate_ *
config_->bandwidth_backoff_lower_bound_factor);
}
@@ -850,13 +898,13 @@ std::vector<LossBasedBweV2::ChannelParameters> LossBasedBweV2::GetCandidates(
if (IsValid(delay_based_estimate_) &&
config_->append_delay_based_estimate_candidate) {
- if (delay_based_estimate_ > current_best_estimate_.loss_limited_bandwidth) {
+ if (delay_based_estimate_ > best_estimate.loss_limited_bandwidth) {
bandwidths.push_back(delay_based_estimate_);
}
}
if (in_alr && config_->append_upper_bound_candidate_in_alr &&
- current_best_estimate_.loss_limited_bandwidth > GetInstantUpperBound()) {
+ best_estimate.loss_limited_bandwidth > GetInstantUpperBound()) {
bandwidths.push_back(GetInstantUpperBound());
}
@@ -866,10 +914,10 @@ std::vector<LossBasedBweV2::ChannelParameters> LossBasedBweV2::GetCandidates(
std::vector<ChannelParameters> candidates;
candidates.resize(bandwidths.size());
for (size_t i = 0; i < bandwidths.size(); ++i) {
- ChannelParameters candidate = current_best_estimate_;
- candidate.loss_limited_bandwidth = std::min(
- bandwidths[i], std::max(current_best_estimate_.loss_limited_bandwidth,
- candidate_bandwidth_upper_bound));
+ ChannelParameters candidate = best_estimate;
+ candidate.loss_limited_bandwidth =
+ std::min(bandwidths[i], std::max(best_estimate.loss_limited_bandwidth,
+ candidate_bandwidth_upper_bound));
candidate.inherent_loss = GetFeasibleInherentLoss(candidate);
candidates[i] = candidate;
}
@@ -1037,14 +1085,6 @@ void LossBasedBweV2::CalculateInstantUpperBound() {
instant_limit = config_->instant_upper_bound_bandwidth_balance /
(average_reported_loss_ratio -
config_->instant_upper_bound_loss_offset);
- if (average_reported_loss_ratio > config_->high_loss_rate_threshold) {
- instant_limit = std::min(
- instant_limit, DataRate::KilobitsPerSec(std::max(
- static_cast<double>(min_bitrate_.kbps()),
- config_->bandwidth_cap_at_high_loss_rate.kbps() -
- config_->slope_of_bwe_high_loss_func *
- average_reported_loss_ratio)));
- }
}
cached_instant_upper_bound_ = instant_limit;
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/loss_based_bwe_v2.h b/third_party/libwebrtc/modules/congestion_controller/goog_cc/loss_based_bwe_v2.h
index 425ca2a0c8..9afbb11f1f 100644
--- a/third_party/libwebrtc/modules/congestion_controller/goog_cc/loss_based_bwe_v2.h
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/loss_based_bwe_v2.h
@@ -62,6 +62,9 @@ class LossBasedBweV2 {
// Returns true if loss based BWE is ready to be used in the start phase.
bool ReadyToUseInStartPhase() const;
+ // Returns true if loss based BWE can be used in the start phase.
+ bool UseInStartPhase() const;
+
// Returns `DataRate::PlusInfinity` if no BWE can be calculated.
Result GetLossBasedResult() const;
@@ -83,6 +86,8 @@ class LossBasedBweV2 {
struct Config {
double bandwidth_rampup_upper_bound_factor = 0.0;
+ double bandwidth_rampup_upper_bound_factor_in_hold = 0;
+ double bandwidth_rampup_hold_threshold = 0;
double rampup_acceleration_max_factor = 0.0;
TimeDelta rampup_acceleration_maxout_time = TimeDelta::Zero();
std::vector<double> candidate_factors;
@@ -111,9 +116,6 @@ class LossBasedBweV2 {
double max_increase_factor = 0.0;
TimeDelta delayed_increase_window = TimeDelta::Zero();
bool not_increase_if_inherent_loss_less_than_average_loss = false;
- double high_loss_rate_threshold = 1.0;
- DataRate bandwidth_cap_at_high_loss_rate = DataRate::MinusInfinity();
- double slope_of_bwe_high_loss_func = 1000.0;
bool not_use_acked_rate_in_alr = false;
bool use_in_start_phase = false;
int min_num_observations = 0;
@@ -121,6 +123,7 @@ class LossBasedBweV2 {
double hold_duration_factor = 0.0;
bool use_byte_loss_rate = false;
TimeDelta padding_duration = TimeDelta::Zero();
+ bool bound_best_candidate = false;
};
struct Derivatives {
@@ -152,6 +155,12 @@ class LossBasedBweV2 {
Timestamp padding_timestamp = Timestamp::MinusInfinity();
};
+ struct HoldInfo {
+ Timestamp timestamp = Timestamp::MinusInfinity();
+ TimeDelta duration = TimeDelta::Zero();
+ DataRate rate = DataRate::PlusInfinity();
+ };
+
static absl::optional<Config> CreateConfig(
const FieldTrialsView* key_value_config);
bool IsConfigValid() const;
@@ -180,7 +189,6 @@ class LossBasedBweV2 {
// Returns false if no observation was created.
bool PushBackObservation(rtc::ArrayView<const PacketResult> packet_results);
- void UpdateResult();
bool IsEstimateIncreasingWhenLossLimited(DataRate old_estimate,
DataRate new_estimate);
bool IsInLossLimitedState() const;
@@ -204,8 +212,7 @@ class LossBasedBweV2 {
DataRate max_bitrate_ = DataRate::PlusInfinity();
DataRate delay_based_estimate_ = DataRate::PlusInfinity();
LossBasedBweV2::Result loss_based_result_ = LossBasedBweV2::Result();
- Timestamp last_hold_timestamp_ = Timestamp::MinusInfinity();
- TimeDelta hold_duration_ = TimeDelta::Zero();
+ HoldInfo last_hold_info_ = HoldInfo();
PaddingInfo last_padding_info_ = PaddingInfo();
};
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/loss_based_bwe_v2_gn/moz.build b/third_party/libwebrtc/modules/congestion_controller/goog_cc/loss_based_bwe_v2_gn/moz.build
index ca9f20ab87..709bcdb937 100644
--- a/third_party/libwebrtc/modules/congestion_controller/goog_cc/loss_based_bwe_v2_gn/moz.build
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/loss_based_bwe_v2_gn/moz.build
@@ -199,7 +199,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -209,10 +208,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/loss_based_bwe_v2_test.cc b/third_party/libwebrtc/modules/congestion_controller/goog_cc/loss_based_bwe_v2_test.cc
index 347e2a86d1..9b7ad03148 100644
--- a/third_party/libwebrtc/modules/congestion_controller/goog_cc/loss_based_bwe_v2_test.cc
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/loss_based_bwe_v2_test.cc
@@ -795,7 +795,7 @@ TEST_F(LossBasedBweV2Test,
// The estimate is capped by acked_bitrate * BwRampupUpperBoundFactor.
EXPECT_EQ(result.bandwidth_estimate, estimate_1 * 0.9 * 1.2);
- // But if acked bitrate decrease, BWE does not decrease when there is no
+ // But if acked bitrate decreases, BWE does not decrease when there is no
// loss.
loss_based_bandwidth_estimator.SetAcknowledgedBitrate(estimate_1 * 0.9);
loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
@@ -809,6 +809,53 @@ TEST_F(LossBasedBweV2Test,
result.bandwidth_estimate);
}
+// Ensure that the state can switch to kIncrease even when the bandwidth is
+// bounded by acked bitrate.
+TEST_F(LossBasedBweV2Test, EnsureIncreaseEvenIfAckedBitrateBound) {
+ ExplicitKeyValueConfig key_value_config(ShortObservationConfig(
+ "LossThresholdOfHighBandwidthPreference:0.99,"
+ "BwRampupUpperBoundFactor:1.2,"
+ // Set InstantUpperBoundBwBalance high to disable InstantUpperBound cap.
+ "InstantUpperBoundBwBalance:10000kbps,"));
+ std::vector<PacketResult> enough_feedback_1 =
+ CreatePacketResultsWith100pLossRate(
+ /*first_packet_timestamp=*/Timestamp::Zero());
+ LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config);
+ DataRate delay_based_estimate = DataRate::KilobitsPerSec(5000);
+
+ loss_based_bandwidth_estimator.SetBandwidthEstimate(
+ DataRate::KilobitsPerSec(600));
+ loss_based_bandwidth_estimator.SetAcknowledgedBitrate(
+ DataRate::KilobitsPerSec(300));
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(enough_feedback_1,
+ delay_based_estimate,
+ /*in_alr=*/false);
+ ASSERT_EQ(loss_based_bandwidth_estimator.GetLossBasedResult().state,
+ LossBasedState::kDecreasing);
+ LossBasedBweV2::Result result =
+ loss_based_bandwidth_estimator.GetLossBasedResult();
+ DataRate estimate_1 = result.bandwidth_estimate;
+ ASSERT_LT(estimate_1.kbps(), 600);
+
+ // Set a low acked bitrate.
+ loss_based_bandwidth_estimator.SetAcknowledgedBitrate(estimate_1 / 2);
+
+ int feedback_count = 1;
+ while (feedback_count < 5 && result.state != LossBasedState::kIncreasing) {
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ CreatePacketResultsWithReceivedPackets(
+ /*first_packet_timestamp=*/Timestamp::Zero() +
+ feedback_count++ * kObservationDurationLowerBound),
+ delay_based_estimate,
+ /*in_alr=*/false);
+ result = loss_based_bandwidth_estimator.GetLossBasedResult();
+ }
+
+ ASSERT_EQ(result.state, LossBasedState::kIncreasing);
+ // The estimate increases by 1kbps.
+ EXPECT_EQ(result.bandwidth_estimate, estimate_1 + DataRate::BitsPerSec(1));
+}
+
// After loss based bwe backs off, the estimate is bounded during the delayed
// window.
TEST_F(LossBasedBweV2Test,
@@ -1007,164 +1054,6 @@ TEST_F(LossBasedBweV2Test,
DataRate::KilobitsPerSec(600));
}
-TEST_F(LossBasedBweV2Test,
- StricterBoundUsingHighLossRateThresholdAt10pLossRate) {
- ExplicitKeyValueConfig key_value_config(
- ShortObservationConfig("HighLossRateThreshold:0.09"));
- LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config);
- loss_based_bandwidth_estimator.SetMinMaxBitrate(
- /*min_bitrate=*/DataRate::KilobitsPerSec(10),
- /*max_bitrate=*/DataRate::KilobitsPerSec(1000000));
- DataRate delay_based_estimate = DataRate::KilobitsPerSec(5000);
- loss_based_bandwidth_estimator.SetBandwidthEstimate(
- DataRate::KilobitsPerSec(600));
-
- std::vector<PacketResult> enough_feedback_10p_loss_1 =
- CreatePacketResultsWith10pPacketLossRate(
- /*first_packet_timestamp=*/Timestamp::Zero());
- loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
- enough_feedback_10p_loss_1, delay_based_estimate,
-
- /*in_alr=*/false);
-
- std::vector<PacketResult> enough_feedback_10p_loss_2 =
- CreatePacketResultsWith10pPacketLossRate(
- /*first_packet_timestamp=*/Timestamp::Zero() +
- kObservationDurationLowerBound);
- loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
- enough_feedback_10p_loss_2, delay_based_estimate,
-
- /*in_alr=*/false);
-
- // At 10% loss rate and high loss rate threshold to be 10%, cap the estimate
- // to be 500 * 1000-0.1 = 400kbps.
- EXPECT_EQ(
- loss_based_bandwidth_estimator.GetLossBasedResult().bandwidth_estimate,
- DataRate::KilobitsPerSec(400));
-}
-
-TEST_F(LossBasedBweV2Test,
- StricterBoundUsingHighLossRateThresholdAt50pLossRate) {
- ExplicitKeyValueConfig key_value_config(
- ShortObservationConfig("HighLossRateThreshold:0.3"));
- LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config);
- loss_based_bandwidth_estimator.SetMinMaxBitrate(
- /*min_bitrate=*/DataRate::KilobitsPerSec(10),
- /*max_bitrate=*/DataRate::KilobitsPerSec(1000000));
- DataRate delay_based_estimate = DataRate::KilobitsPerSec(5000);
- loss_based_bandwidth_estimator.SetBandwidthEstimate(
- DataRate::KilobitsPerSec(600));
-
- std::vector<PacketResult> enough_feedback_50p_loss_1 =
- CreatePacketResultsWith50pPacketLossRate(
- /*first_packet_timestamp=*/Timestamp::Zero());
- loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
- enough_feedback_50p_loss_1, delay_based_estimate,
-
- /*in_alr=*/false);
-
- std::vector<PacketResult> enough_feedback_50p_loss_2 =
- CreatePacketResultsWith50pPacketLossRate(
- /*first_packet_timestamp=*/Timestamp::Zero() +
- kObservationDurationLowerBound);
- loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
- enough_feedback_50p_loss_2, delay_based_estimate,
-
- /*in_alr=*/false);
-
- // At 50% loss rate and high loss rate threshold to be 30%, cap the estimate
- // to be the min bitrate.
- EXPECT_EQ(
- loss_based_bandwidth_estimator.GetLossBasedResult().bandwidth_estimate,
- DataRate::KilobitsPerSec(10));
-}
-
-TEST_F(LossBasedBweV2Test,
- StricterBoundUsingHighLossRateThresholdAt100pLossRate) {
- ExplicitKeyValueConfig key_value_config(
- ShortObservationConfig("HighLossRateThreshold:0.3"));
- LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config);
- loss_based_bandwidth_estimator.SetMinMaxBitrate(
- /*min_bitrate=*/DataRate::KilobitsPerSec(10),
- /*max_bitrate=*/DataRate::KilobitsPerSec(1000000));
- DataRate delay_based_estimate = DataRate::KilobitsPerSec(5000);
- loss_based_bandwidth_estimator.SetBandwidthEstimate(
- DataRate::KilobitsPerSec(600));
-
- std::vector<PacketResult> enough_feedback_100p_loss_1 =
- CreatePacketResultsWith100pLossRate(
- /*first_packet_timestamp=*/Timestamp::Zero());
- loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
- enough_feedback_100p_loss_1, delay_based_estimate,
-
- /*in_alr=*/false);
-
- std::vector<PacketResult> enough_feedback_100p_loss_2 =
- CreatePacketResultsWith100pLossRate(
- /*first_packet_timestamp=*/Timestamp::Zero() +
- kObservationDurationLowerBound);
- loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
- enough_feedback_100p_loss_2, delay_based_estimate,
-
- /*in_alr=*/false);
-
- // At 100% loss rate and high loss rate threshold to be 30%, cap the estimate
- // to be the min bitrate.
- EXPECT_EQ(
- loss_based_bandwidth_estimator.GetLossBasedResult().bandwidth_estimate,
- DataRate::KilobitsPerSec(10));
-}
-
-TEST_F(LossBasedBweV2Test, EstimateRecoversAfterHighLoss) {
- ExplicitKeyValueConfig key_value_config(
- ShortObservationConfig("HighLossRateThreshold:0.3"));
- LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config);
- loss_based_bandwidth_estimator.SetMinMaxBitrate(
- /*min_bitrate=*/DataRate::KilobitsPerSec(10),
- /*max_bitrate=*/DataRate::KilobitsPerSec(1000000));
- DataRate delay_based_estimate = DataRate::KilobitsPerSec(5000);
- loss_based_bandwidth_estimator.SetBandwidthEstimate(
- DataRate::KilobitsPerSec(600));
-
- std::vector<PacketResult> enough_feedback_100p_loss_1 =
- CreatePacketResultsWith100pLossRate(
- /*first_packet_timestamp=*/Timestamp::Zero());
- loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
- enough_feedback_100p_loss_1, delay_based_estimate,
-
- /*in_alr=*/false);
-
- // Make sure that the estimate is set to min bitrate because of 100% loss
- // rate.
- EXPECT_EQ(
- loss_based_bandwidth_estimator.GetLossBasedResult().bandwidth_estimate,
- DataRate::KilobitsPerSec(10));
-
- // Create some feedbacks with 0 loss rate to simulate network recovering.
- std::vector<PacketResult> enough_feedback_0p_loss_1 =
- CreatePacketResultsWithReceivedPackets(
- /*first_packet_timestamp=*/Timestamp::Zero() +
- kObservationDurationLowerBound);
- loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
- enough_feedback_0p_loss_1, delay_based_estimate,
-
- /*in_alr=*/false);
-
- std::vector<PacketResult> enough_feedback_0p_loss_2 =
- CreatePacketResultsWithReceivedPackets(
- /*first_packet_timestamp=*/Timestamp::Zero() +
- kObservationDurationLowerBound * 2);
- loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
- enough_feedback_0p_loss_2, delay_based_estimate,
-
- /*in_alr=*/false);
-
- // The estimate increases as network recovers.
- EXPECT_GT(
- loss_based_bandwidth_estimator.GetLossBasedResult().bandwidth_estimate,
- DataRate::KilobitsPerSec(10));
-}
-
TEST_F(LossBasedBweV2Test, EstimateIsNotHigherThanMaxBitrate) {
ExplicitKeyValueConfig key_value_config(
Config(/*enabled=*/true, /*valid=*/true));
@@ -1494,6 +1383,92 @@ TEST_F(LossBasedBweV2Test, IncreaseUsingPaddingStateIfFieldTrial) {
LossBasedState::kIncreaseUsingPadding);
}
+TEST_F(LossBasedBweV2Test, BestCandidateResetsToUpperBoundInFieldTrial) {
+ ExplicitKeyValueConfig key_value_config(
+ ShortObservationConfig("PaddingDuration:1000ms,BoundBestCandidate:true"));
+ LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config);
+ loss_based_bandwidth_estimator.SetBandwidthEstimate(
+ DataRate::KilobitsPerSec(2500));
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ CreatePacketResultsWith50pPacketLossRate(
+ /*first_packet_timestamp=*/Timestamp::Zero()),
+ /*delay_based_estimate=*/DataRate::PlusInfinity(),
+ /*in_alr=*/true);
+ LossBasedBweV2::Result result_after_loss =
+ loss_based_bandwidth_estimator.GetLossBasedResult();
+ ASSERT_EQ(result_after_loss.state, LossBasedState::kDecreasing);
+
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ CreatePacketResultsWithReceivedPackets(
+ /*first_packet_timestamp=*/Timestamp::Zero() +
+ kObservationDurationLowerBound),
+ /*delay_based_estimate=*/DataRate::PlusInfinity(),
+ /*in_alr=*/true);
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ CreatePacketResultsWithReceivedPackets(
+ /*first_packet_timestamp=*/Timestamp::Zero() +
+ 2 * kObservationDurationLowerBound),
+ /*delay_based_estimate=*/DataRate::PlusInfinity(),
+ /*in_alr=*/true);
+ // After a BWE decrease due to large loss, BWE is expected to ramp up slowly
+ // and follow the acked bitrate.
+ EXPECT_EQ(loss_based_bandwidth_estimator.GetLossBasedResult().state,
+ LossBasedState::kIncreaseUsingPadding);
+ EXPECT_NEAR(loss_based_bandwidth_estimator.GetLossBasedResult()
+ .bandwidth_estimate.kbps(),
+ result_after_loss.bandwidth_estimate.kbps(), 100);
+}
+
+TEST_F(LossBasedBweV2Test, DecreaseToAckedCandidateIfPaddingInAlr) {
+ ExplicitKeyValueConfig key_value_config(ShortObservationConfig(
+ "PaddingDuration:1000ms,"
+ // Set InstantUpperBoundBwBalance high to disable InstantUpperBound cap.
+ "InstantUpperBoundBwBalance:10000kbps"));
+ LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config);
+ loss_based_bandwidth_estimator.SetBandwidthEstimate(
+ DataRate::KilobitsPerSec(1000));
+ int feedback_id = 0;
+ while (loss_based_bandwidth_estimator.GetLossBasedResult().state !=
+ LossBasedState::kDecreasing) {
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ CreatePacketResultsWith100pLossRate(
+ /*first_packet_timestamp=*/Timestamp::Zero() +
+ kObservationDurationLowerBound * feedback_id),
+ /*delay_based_estimate=*/DataRate::PlusInfinity(),
+ /*in_alr=*/true);
+ feedback_id++;
+ }
+
+ while (loss_based_bandwidth_estimator.GetLossBasedResult().state !=
+ LossBasedState::kIncreaseUsingPadding) {
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ CreatePacketResultsWithReceivedPackets(
+ /*first_packet_timestamp=*/Timestamp::Zero() +
+ kObservationDurationLowerBound * feedback_id),
+ /*delay_based_estimate=*/DataRate::PlusInfinity(),
+ /*in_alr=*/true);
+ feedback_id++;
+ }
+ ASSERT_GT(
+ loss_based_bandwidth_estimator.GetLossBasedResult().bandwidth_estimate,
+ DataRate::KilobitsPerSec(900));
+
+ loss_based_bandwidth_estimator.SetAcknowledgedBitrate(
+ DataRate::KilobitsPerSec(100));
+ // Padding is sent now, create some lost packets.
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ CreatePacketResultsWith100pLossRate(
+ /*first_packet_timestamp=*/Timestamp::Zero() +
+ kObservationDurationLowerBound * feedback_id),
+ /*delay_based_estimate=*/DataRate::PlusInfinity(),
+ /*in_alr=*/true);
+ EXPECT_EQ(loss_based_bandwidth_estimator.GetLossBasedResult().state,
+ LossBasedState::kDecreasing);
+ EXPECT_EQ(
+ loss_based_bandwidth_estimator.GetLossBasedResult().bandwidth_estimate,
+ DataRate::KilobitsPerSec(100));
+}
+
TEST_F(LossBasedBweV2Test, DecreaseAfterPadding) {
ExplicitKeyValueConfig key_value_config(ShortObservationConfig(
"PaddingDuration:1000ms,BwRampupUpperBoundFactor:2.0"));
@@ -1580,7 +1555,7 @@ TEST_F(LossBasedBweV2Test, IncreaseEstimateIfNotHold) {
TEST_F(LossBasedBweV2Test, IncreaseEstimateAfterHoldDuration) {
ExplicitKeyValueConfig key_value_config(
- ShortObservationConfig("HoldDurationFactor:3"));
+ ShortObservationConfig("HoldDurationFactor:10"));
LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config);
loss_based_bandwidth_estimator.SetBandwidthEstimate(
DataRate::KilobitsPerSec(2500));
@@ -1629,36 +1604,126 @@ TEST_F(LossBasedBweV2Test, IncreaseEstimateAfterHoldDuration) {
/*in_alr=*/false);
EXPECT_EQ(loss_based_bandwidth_estimator.GetLossBasedResult().state,
LossBasedState::kDecreasing);
- estimate =
+ DataRate estimate_at_hold =
loss_based_bandwidth_estimator.GetLossBasedResult().bandwidth_estimate;
- // During the hold duration, e.g. next 900ms, the estimate cannot increase.
+ // In the hold duration, e.g. next 3s, the estimate cannot increase above the
+ // hold rate. Get some lost packets to get lower estimate than the HOLD rate.
for (int i = 4; i <= 6; ++i) {
loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
- CreatePacketResultsWithReceivedPackets(
+ CreatePacketResultsWith100pLossRate(
/*first_packet_timestamp=*/Timestamp::Zero() +
kObservationDurationLowerBound * i),
/*delay_based_estimate=*/DataRate::PlusInfinity(),
/*in_alr=*/false);
EXPECT_EQ(loss_based_bandwidth_estimator.GetLossBasedResult().state,
LossBasedState::kDecreasing);
- EXPECT_EQ(
+ EXPECT_LT(
loss_based_bandwidth_estimator.GetLossBasedResult().bandwidth_estimate,
- estimate);
+ estimate_at_hold);
+ }
+
+ int feedback_id = 7;
+ while (loss_based_bandwidth_estimator.GetLossBasedResult().state !=
+ LossBasedState::kIncreasing) {
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ CreatePacketResultsWithReceivedPackets(
+ /*first_packet_timestamp=*/Timestamp::Zero() +
+ kObservationDurationLowerBound * feedback_id),
+ /*delay_based_estimate=*/DataRate::PlusInfinity(),
+ /*in_alr=*/false);
+ if (loss_based_bandwidth_estimator.GetLossBasedResult().state ==
+ LossBasedState::kDecreasing) {
+ // In the hold duration, the estimate can not go higher than estimate at
+ // hold.
+ EXPECT_LE(loss_based_bandwidth_estimator.GetLossBasedResult()
+ .bandwidth_estimate,
+ estimate_at_hold);
+ } else if (loss_based_bandwidth_estimator.GetLossBasedResult().state ==
+ LossBasedState::kIncreasing) {
+ // After the hold duration, the estimate can increase again.
+ EXPECT_GT(loss_based_bandwidth_estimator.GetLossBasedResult()
+ .bandwidth_estimate,
+ estimate_at_hold);
+ }
+ feedback_id++;
}
+}
- // After the hold duration, the estimate can increase again.
+TEST_F(LossBasedBweV2Test, HoldRateNotLowerThanAckedRate) {
+ ExplicitKeyValueConfig key_value_config(ShortObservationConfig(
+ "HoldDurationFactor:10,LowerBoundByAckedRateFactor:1.0"));
+ LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config);
+ loss_based_bandwidth_estimator.SetBandwidthEstimate(
+ DataRate::KilobitsPerSec(2500));
loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
- CreatePacketResultsWithReceivedPackets(
+ CreatePacketResultsWith50pPacketLossRate(
+ /*first_packet_timestamp=*/Timestamp::Zero()),
+ /*delay_based_estimate=*/DataRate::PlusInfinity(),
+ /*in_alr=*/false);
+ ASSERT_EQ(loss_based_bandwidth_estimator.GetLossBasedResult().state,
+ LossBasedState::kDecreasing);
+
+ // During the hold duration, hold rate is not lower than the acked rate.
+ loss_based_bandwidth_estimator.SetAcknowledgedBitrate(
+ DataRate::KilobitsPerSec(1000));
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ CreatePacketResultsWith50pPacketLossRate(
/*first_packet_timestamp=*/Timestamp::Zero() +
- kObservationDurationLowerBound * 7),
+ kObservationDurationLowerBound),
/*delay_based_estimate=*/DataRate::PlusInfinity(),
/*in_alr=*/false);
EXPECT_EQ(loss_based_bandwidth_estimator.GetLossBasedResult().state,
- LossBasedState::kIncreasing);
- EXPECT_GE(
+ LossBasedState::kDecreasing);
+ EXPECT_EQ(
loss_based_bandwidth_estimator.GetLossBasedResult().bandwidth_estimate,
- estimate);
+ DataRate::KilobitsPerSec(1000));
+}
+
+TEST_F(LossBasedBweV2Test, EstimateNotLowerThanAckedRate) {
+ ExplicitKeyValueConfig key_value_config(
+ ShortObservationConfig("LowerBoundByAckedRateFactor:1.0"));
+ LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config);
+ loss_based_bandwidth_estimator.SetBandwidthEstimate(
+ DataRate::KilobitsPerSec(2500));
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ CreatePacketResultsWith100pLossRate(
+ /*first_packet_timestamp=*/Timestamp::Zero()),
+ /*delay_based_estimate=*/DataRate::PlusInfinity(),
+ /*in_alr=*/false);
+ ASSERT_LT(
+ loss_based_bandwidth_estimator.GetLossBasedResult().bandwidth_estimate,
+ DataRate::KilobitsPerSec(1000));
+
+ loss_based_bandwidth_estimator.SetAcknowledgedBitrate(
+ DataRate::KilobitsPerSec(1000));
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ CreatePacketResultsWith100pLossRate(
+ /*first_packet_timestamp=*/Timestamp::Zero() +
+ kObservationDurationLowerBound),
+ /*delay_based_estimate=*/DataRate::PlusInfinity(),
+ /*in_alr=*/false);
+ EXPECT_EQ(
+ loss_based_bandwidth_estimator.GetLossBasedResult().bandwidth_estimate,
+ DataRate::KilobitsPerSec(1000));
+
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ CreatePacketResultsWithReceivedPackets(
+ /*first_packet_timestamp=*/Timestamp::Zero() +
+ kObservationDurationLowerBound * 2),
+ /*delay_based_estimate=*/DataRate::PlusInfinity(),
+ /*in_alr=*/false);
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ CreatePacketResultsWithReceivedPackets(
+ /*first_packet_timestamp=*/Timestamp::Zero() +
+ kObservationDurationLowerBound * 3),
+ /*delay_based_estimate=*/DataRate::PlusInfinity(),
+ /*in_alr=*/false);
+
+ // Verify that the estimate recovers from the acked rate.
+ EXPECT_GT(
+ loss_based_bandwidth_estimator.GetLossBasedResult().bandwidth_estimate,
+ DataRate::KilobitsPerSec(1000));
}
TEST_F(LossBasedBweV2Test, EndHoldDurationIfDelayBasedEstimateWorks) {
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/probe_controller_gn/moz.build b/third_party/libwebrtc/modules/congestion_controller/goog_cc/probe_controller_gn/moz.build
index 703c22a590..049ac6f477 100644
--- a/third_party/libwebrtc/modules/congestion_controller/goog_cc/probe_controller_gn/moz.build
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/probe_controller_gn/moz.build
@@ -195,7 +195,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -205,10 +204,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/pushback_controller_gn/moz.build b/third_party/libwebrtc/modules/congestion_controller/goog_cc/pushback_controller_gn/moz.build
index 291502c95a..6e1d0acff5 100644
--- a/third_party/libwebrtc/modules/congestion_controller/goog_cc/pushback_controller_gn/moz.build
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/pushback_controller_gn/moz.build
@@ -195,7 +195,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -205,10 +204,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.cc b/third_party/libwebrtc/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.cc
index b09cb22f49..22693d67e9 100644
--- a/third_party/libwebrtc/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.cc
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.cc
@@ -203,9 +203,9 @@ TimeDelta RttBasedBackoff::CorrectedRtt() const {
RttBasedBackoff::~RttBasedBackoff() = default;
SendSideBandwidthEstimation::SendSideBandwidthEstimation(
- const FieldTrialsView* key_value_config,
- RtcEventLog* event_log)
- : rtt_backoff_(key_value_config),
+ const FieldTrialsView* key_value_config, RtcEventLog* event_log)
+ : key_value_config_(key_value_config),
+ rtt_backoff_(key_value_config),
lost_packets_since_last_loss_update_(0),
expected_packets_since_last_loss_update_(0),
current_target_(DataRate::Zero()),
@@ -234,7 +234,7 @@ SendSideBandwidthEstimation::SendSideBandwidthEstimation(
high_loss_threshold_(kDefaultHighLossThreshold),
bitrate_threshold_(kDefaultBitrateThreshold),
loss_based_bandwidth_estimator_v1_(key_value_config),
- loss_based_bandwidth_estimator_v2_(key_value_config),
+ loss_based_bandwidth_estimator_v2_(new LossBasedBweV2(key_value_config)),
loss_based_state_(LossBasedState::kDelayBasedEstimate),
disable_receiver_limit_caps_only_("Disabled") {
RTC_DCHECK(event_log);
@@ -252,7 +252,7 @@ SendSideBandwidthEstimation::SendSideBandwidthEstimation(
ParseFieldTrial({&disable_receiver_limit_caps_only_},
key_value_config->Lookup("WebRTC-Bwe-ReceiverLimitCapsOnly"));
if (LossBasedBandwidthEstimatorV2Enabled()) {
- loss_based_bandwidth_estimator_v2_.SetMinMaxBitrate(
+ loss_based_bandwidth_estimator_v2_->SetMinMaxBitrate(
min_bitrate_configured_, max_bitrate_configured_);
}
}
@@ -281,6 +281,10 @@ void SendSideBandwidthEstimation::OnRouteChange() {
uma_update_state_ = kNoUpdate;
uma_rtt_state_ = kNoUpdate;
last_rtc_event_log_ = Timestamp::MinusInfinity();
+ if (loss_based_bandwidth_estimator_v2_->UseInStartPhase()) {
+ loss_based_bandwidth_estimator_v2_.reset(
+ new LossBasedBweV2(key_value_config_));
+ }
}
void SendSideBandwidthEstimation::SetBitrates(
@@ -315,8 +319,8 @@ void SendSideBandwidthEstimation::SetMinMaxBitrate(DataRate min_bitrate,
} else {
max_bitrate_configured_ = kDefaultMaxBitrate;
}
- loss_based_bandwidth_estimator_v2_.SetMinMaxBitrate(min_bitrate_configured_,
- max_bitrate_configured_);
+ loss_based_bandwidth_estimator_v2_->SetMinMaxBitrate(min_bitrate_configured_,
+ max_bitrate_configured_);
}
int SendSideBandwidthEstimation::GetMinBitrate() const {
@@ -371,7 +375,7 @@ void SendSideBandwidthEstimation::SetAcknowledgedRate(
*acknowledged_rate, at_time);
}
if (LossBasedBandwidthEstimatorV2Enabled()) {
- loss_based_bandwidth_estimator_v2_.SetAcknowledgedBitrate(
+ loss_based_bandwidth_estimator_v2_->SetAcknowledgedBitrate(
*acknowledged_rate);
}
}
@@ -386,7 +390,7 @@ void SendSideBandwidthEstimation::UpdateLossBasedEstimator(
report.packet_feedbacks, report.feedback_time);
}
if (LossBasedBandwidthEstimatorV2Enabled()) {
- loss_based_bandwidth_estimator_v2_.UpdateBandwidthEstimate(
+ loss_based_bandwidth_estimator_v2_->UpdateBandwidthEstimate(
report.packet_feedbacks, delay_based_limit_, in_alr);
UpdateEstimate(report.feedback_time);
}
@@ -492,7 +496,7 @@ void SendSideBandwidthEstimation::UpdateEstimate(Timestamp at_time) {
// We trust the REMB and/or delay-based estimate during the first 2 seconds if
// we haven't had any packet loss reported, to allow startup bitrate probing.
if (last_fraction_loss_ == 0 && IsInStartPhase(at_time) &&
- !loss_based_bandwidth_estimator_v2_.ReadyToUseInStartPhase()) {
+ !loss_based_bandwidth_estimator_v2_->ReadyToUseInStartPhase()) {
DataRate new_bitrate = current_target_;
// TODO(srte): We should not allow the new_bitrate to be larger than the
// receiver limit here.
@@ -534,7 +538,7 @@ void SendSideBandwidthEstimation::UpdateEstimate(Timestamp at_time) {
if (LossBasedBandwidthEstimatorV2ReadyForUse()) {
LossBasedBweV2::Result result =
- loss_based_bandwidth_estimator_v2_.GetLossBasedResult();
+ loss_based_bandwidth_estimator_v2_->GetLossBasedResult();
loss_based_state_ = result.state;
UpdateTargetBitrate(result.bandwidth_estimate, at_time);
return;
@@ -690,13 +694,13 @@ bool SendSideBandwidthEstimation::LossBasedBandwidthEstimatorV1ReadyForUse()
}
bool SendSideBandwidthEstimation::LossBasedBandwidthEstimatorV2Enabled() const {
- return loss_based_bandwidth_estimator_v2_.IsEnabled();
+ return loss_based_bandwidth_estimator_v2_->IsEnabled();
}
bool SendSideBandwidthEstimation::LossBasedBandwidthEstimatorV2ReadyForUse()
const {
return LossBasedBandwidthEstimatorV2Enabled() &&
- loss_based_bandwidth_estimator_v2_.IsReady();
+ loss_based_bandwidth_estimator_v2_->IsReady();
}
} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.h b/third_party/libwebrtc/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.h
index 3a4efc47c7..dd4d25a236 100644
--- a/third_party/libwebrtc/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.h
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.h
@@ -16,6 +16,7 @@
#include <stdint.h>
#include <deque>
+#include <memory>
#include <utility>
#include <vector>
@@ -167,6 +168,7 @@ class SendSideBandwidthEstimation {
bool LossBasedBandwidthEstimatorV1ReadyForUse() const;
bool LossBasedBandwidthEstimatorV2ReadyForUse() const;
+ const FieldTrialsView* key_value_config_;
RttBasedBackoff rtt_backoff_;
LinkCapacityTracker link_capacity_;
@@ -208,7 +210,7 @@ class SendSideBandwidthEstimation {
float high_loss_threshold_;
DataRate bitrate_threshold_;
LossBasedBandwidthEstimation loss_based_bandwidth_estimator_v1_;
- LossBasedBweV2 loss_based_bandwidth_estimator_v2_;
+ std::unique_ptr<LossBasedBweV2> loss_based_bandwidth_estimator_v2_;
LossBasedState loss_based_state_;
FieldTrialFlag disable_receiver_limit_caps_only_;
};
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/send_side_bwe_gn/moz.build b/third_party/libwebrtc/modules/congestion_controller/goog_cc/send_side_bwe_gn/moz.build
index d83d51f985..08cfdec69b 100644
--- a/third_party/libwebrtc/modules/congestion_controller/goog_cc/send_side_bwe_gn/moz.build
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/send_side_bwe_gn/moz.build
@@ -200,7 +200,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -210,10 +209,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/congestion_controller/rtp/control_handler_gn/moz.build b/third_party/libwebrtc/modules/congestion_controller/rtp/control_handler_gn/moz.build
index 7e8cb87820..62800e263d 100644
--- a/third_party/libwebrtc/modules/congestion_controller/rtp/control_handler_gn/moz.build
+++ b/third_party/libwebrtc/modules/congestion_controller/rtp/control_handler_gn/moz.build
@@ -200,7 +200,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -210,10 +209,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/congestion_controller/rtp/transport_feedback_gn/moz.build b/third_party/libwebrtc/modules/congestion_controller/rtp/transport_feedback_gn/moz.build
index 40ead5619c..41f64326b2 100644
--- a/third_party/libwebrtc/modules/congestion_controller/rtp/transport_feedback_gn/moz.build
+++ b/third_party/libwebrtc/modules/congestion_controller/rtp/transport_feedback_gn/moz.build
@@ -200,7 +200,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -210,10 +209,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/wayland/base_capturer_pipewire.cc b/third_party/libwebrtc/modules/desktop_capture/linux/wayland/base_capturer_pipewire.cc
index 40764de7ae..81caa9bd2d 100644
--- a/third_party/libwebrtc/modules/desktop_capture/linux/wayland/base_capturer_pipewire.cc
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/wayland/base_capturer_pipewire.cc
@@ -112,6 +112,7 @@ void BaseCapturerPipeWire::OnScreenCastSessionClosed() {
if (!capturer_failed_) {
options_.screencast_stream()->StopScreenCastStream();
}
+ capturer_failed_ = true;
}
void BaseCapturerPipeWire::UpdateResolution(uint32_t width, uint32_t height) {
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/wayland/shared_screencast_stream.cc b/third_party/libwebrtc/modules/desktop_capture/linux/wayland/shared_screencast_stream.cc
index 61c6957d27..473f913466 100644
--- a/third_party/libwebrtc/modules/desktop_capture/linux/wayland/shared_screencast_stream.cc
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/wayland/shared_screencast_stream.cc
@@ -14,7 +14,6 @@
#include <libdrm/drm_fourcc.h>
#include <pipewire/pipewire.h>
#include <spa/param/video/format-utils.h>
-#include <sys/mman.h>
#include <vector>
@@ -49,33 +48,6 @@ constexpr int CursorMetaSize(int w, int h) {
constexpr PipeWireVersion kDmaBufModifierMinVersion = {0, 3, 33};
constexpr PipeWireVersion kDropSingleModifierMinVersion = {0, 3, 40};
-class ScopedBuf {
- public:
- ScopedBuf() {}
- ScopedBuf(uint8_t* map, int map_size, int fd)
- : map_(map), map_size_(map_size), fd_(fd) {}
- ~ScopedBuf() {
- if (map_ != MAP_FAILED) {
- munmap(map_, map_size_);
- }
- }
-
- explicit operator bool() { return map_ != MAP_FAILED; }
-
- void initialize(uint8_t* map, int map_size, int fd) {
- map_ = map;
- map_size_ = map_size;
- fd_ = fd;
- }
-
- uint8_t* get() { return map_; }
-
- protected:
- uint8_t* map_ = static_cast<uint8_t*>(MAP_FAILED);
- int map_size_;
- int fd_;
-};
-
class SharedScreenCastStreamPrivate {
public:
SharedScreenCastStreamPrivate();
diff --git a/third_party/libwebrtc/modules/desktop_capture/mac/desktop_frame_provider.h b/third_party/libwebrtc/modules/desktop_capture/mac/desktop_frame_provider.h
index aad28d2f30..64ef5750ec 100644
--- a/third_party/libwebrtc/modules/desktop_capture/mac/desktop_frame_provider.h
+++ b/third_party/libwebrtc/modules/desktop_capture/mac/desktop_frame_provider.h
@@ -46,6 +46,8 @@ class DesktopFrameProvider {
// Expected to be called before stopping the CGDisplayStreamRef streams.
void Release();
+ bool allow_iosurface() const { return allow_iosurface_; }
+
private:
SequenceChecker thread_checker_;
const bool allow_iosurface_;
diff --git a/third_party/libwebrtc/modules/desktop_capture/mac/screen_capturer_mac.mm b/third_party/libwebrtc/modules/desktop_capture/mac/screen_capturer_mac.mm
index 1f4a62f7cd..785a15dfa4 100644
--- a/third_party/libwebrtc/modules/desktop_capture/mac/screen_capturer_mac.mm
+++ b/third_party/libwebrtc/modules/desktop_capture/mac/screen_capturer_mac.mm
@@ -442,6 +442,10 @@ void ScreenCapturerMac::ScreenConfigurationChanged() {
bool ScreenCapturerMac::RegisterRefreshAndMoveHandlers() {
RTC_DCHECK(thread_checker_.IsCurrent());
+ if (!desktop_frame_provider_.allow_iosurface()) {
+ return true;
+ }
+
desktop_config_ = desktop_config_monitor_->desktop_configuration();
for (const auto& config : desktop_config_.displays) {
size_t pixel_width = config.pixel_bounds.width();
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/dxgi_duplicator_controller.h b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_duplicator_controller.h
index 2b1e0ab041..815986f680 100644
--- a/third_party/libwebrtc/modules/desktop_capture/win/dxgi_duplicator_controller.h
+++ b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_duplicator_controller.h
@@ -132,7 +132,7 @@ class RTC_EXPORT DxgiDuplicatorController {
// scoped_refptr<DxgiDuplicatorController> accesses private AddRef() and
// Release() functions.
- friend class rtc::scoped_refptr<DxgiDuplicatorController>;
+ friend class webrtc::scoped_refptr<DxgiDuplicatorController>;
// A private constructor to ensure consumers to use
// DxgiDuplicatorController::Instance().
diff --git a/third_party/libwebrtc/modules/module_api_gn/moz.build b/third_party/libwebrtc/modules/module_api_gn/moz.build
index d61cca4a48..7613736af2 100644
--- a/third_party/libwebrtc/modules/module_api_gn/moz.build
+++ b/third_party/libwebrtc/modules/module_api_gn/moz.build
@@ -176,16 +176,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/module_api_public_gn/moz.build b/third_party/libwebrtc/modules/module_api_public_gn/moz.build
index 45518d1a8a..c40e3cf5e9 100644
--- a/third_party/libwebrtc/modules/module_api_public_gn/moz.build
+++ b/third_party/libwebrtc/modules/module_api_public_gn/moz.build
@@ -176,16 +176,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/module_fec_api_gn/moz.build b/third_party/libwebrtc/modules/module_fec_api_gn/moz.build
index 7b4274f1b8..86a280e5cc 100644
--- a/third_party/libwebrtc/modules/module_fec_api_gn/moz.build
+++ b/third_party/libwebrtc/modules/module_fec_api_gn/moz.build
@@ -176,16 +176,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/pacing/interval_budget_gn/moz.build b/third_party/libwebrtc/modules/pacing/interval_budget_gn/moz.build
index a528123ae0..8bb44ecf62 100644
--- a/third_party/libwebrtc/modules/pacing/interval_budget_gn/moz.build
+++ b/third_party/libwebrtc/modules/pacing/interval_budget_gn/moz.build
@@ -188,7 +188,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -198,10 +197,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/pacing/pacing_controller.cc b/third_party/libwebrtc/modules/pacing/pacing_controller.cc
index 13ff9a2a95..5b81207d56 100644
--- a/third_party/libwebrtc/modules/pacing/pacing_controller.cc
+++ b/third_party/libwebrtc/modules/pacing/pacing_controller.cc
@@ -73,7 +73,7 @@ PacingController::PacingController(Clock* clock,
keyframe_flushing_(
IsEnabled(field_trials_, "WebRTC-Pacer-KeyframeFlushing")),
transport_overhead_per_packet_(DataSize::Zero()),
- send_burst_interval_(TimeDelta::Zero()),
+ send_burst_interval_(kDefaultBurstInterval),
last_timestamp_(clock_->CurrentTime()),
paused_(false),
media_debt_(DataSize::Zero()),
diff --git a/third_party/libwebrtc/modules/pacing/pacing_controller.h b/third_party/libwebrtc/modules/pacing/pacing_controller.h
index dd5636ccef..04e0a820f9 100644
--- a/third_party/libwebrtc/modules/pacing/pacing_controller.h
+++ b/third_party/libwebrtc/modules/pacing/pacing_controller.h
@@ -25,6 +25,7 @@
#include "api/transport/field_trial_based_config.h"
#include "api/transport/network_types.h"
#include "api/units/data_size.h"
+#include "api/units/time_delta.h"
#include "modules/pacing/bitrate_prober.h"
#include "modules/pacing/interval_budget.h"
#include "modules/pacing/prioritized_packet_queue.h"
@@ -92,6 +93,10 @@ class PacingController {
// the send burst interval.
// Ex: max send burst interval = 63Kb / 10Mbit/s = 50ms.
static constexpr DataSize kMaxBurstSize = DataSize::Bytes(63 * 1000);
+ // The pacer is allowed to send enqued packets in bursts and can build up a
+ // packet "debt" that correspond to approximately the send rate during
+ // the burst interval.
+ static constexpr TimeDelta kDefaultBurstInterval = TimeDelta::Millis(40);
PacingController(Clock* clock,
PacketSender* packet_sender,
diff --git a/third_party/libwebrtc/modules/pacing/pacing_controller_unittest.cc b/third_party/libwebrtc/modules/pacing/pacing_controller_unittest.cc
index ba93d05bb7..9e6ede6dc0 100644
--- a/third_party/libwebrtc/modules/pacing/pacing_controller_unittest.cc
+++ b/third_party/libwebrtc/modules/pacing/pacing_controller_unittest.cc
@@ -427,6 +427,7 @@ TEST_F(PacingControllerTest, BudgetAffectsAudioInTrial) {
DataRate pacing_rate =
DataRate::BitsPerSec(kPacketSize / 3 * 8 * kProcessIntervalsPerSecond);
pacer.SetPacingRates(pacing_rate, DataRate::Zero());
+ pacer.SetSendBurstInterval(TimeDelta::Zero());
// Video fills budget for following process periods.
pacer.EnqueuePacket(video_.BuildNextPacket(kPacketSize));
EXPECT_CALL(callback_, SendPacket).Times(1);
@@ -484,7 +485,7 @@ TEST_F(PacingControllerTest, FirstSentPacketTimeIsSet) {
EXPECT_EQ(kStartTime, pacer->FirstSentPacketTime());
}
-TEST_F(PacingControllerTest, QueueAndPacePackets) {
+TEST_F(PacingControllerTest, QueueAndPacePacketsWithZeroBurstPeriod) {
const uint32_t kSsrc = 12345;
uint16_t sequence_number = 1234;
const DataSize kPackeSize = DataSize::Bytes(250);
@@ -495,6 +496,7 @@ TEST_F(PacingControllerTest, QueueAndPacePackets) {
const size_t kPacketsToSend = (kSendInterval * kTargetRate).bytes() *
kPaceMultiplier / kPackeSize.bytes();
auto pacer = std::make_unique<PacingController>(&clock_, &callback_, trials_);
+ pacer->SetSendBurstInterval(TimeDelta::Zero());
pacer->SetPacingRates(kTargetRate * kPaceMultiplier, DataRate::Zero());
for (size_t i = 0; i < kPacketsToSend; ++i) {
@@ -536,30 +538,30 @@ TEST_F(PacingControllerTest, PaceQueuedPackets) {
auto pacer = std::make_unique<PacingController>(&clock_, &callback_, trials_);
pacer->SetPacingRates(kTargetRate * kPaceMultiplier, DataRate::Zero());
- // Due to the multiplicative factor we can send 5 packets during a send
- // interval. (network capacity * multiplier / (8 bits per byte *
- // (packet size * #send intervals per second)
- const size_t packets_to_send_per_interval =
- kTargetRate.bps() * kPaceMultiplier / (8 * kPacketSize * 200);
- for (size_t i = 0; i < packets_to_send_per_interval; ++i) {
+ const size_t packets_to_send_per_burst_interval =
+ (kTargetRate * kPaceMultiplier * PacingController::kDefaultBurstInterval)
+ .bytes() /
+ kPacketSize;
+ for (size_t i = 0; i < packets_to_send_per_burst_interval; ++i) {
SendAndExpectPacket(pacer.get(), RtpPacketMediaType::kVideo, ssrc,
sequence_number++, clock_.TimeInMilliseconds(),
kPacketSize);
}
- for (size_t j = 0; j < packets_to_send_per_interval * 10; ++j) {
+ for (size_t j = 0; j < packets_to_send_per_burst_interval * 10; ++j) {
pacer->EnqueuePacket(BuildPacket(RtpPacketMediaType::kVideo, ssrc,
sequence_number++,
clock_.TimeInMilliseconds(), kPacketSize));
}
- EXPECT_EQ(packets_to_send_per_interval + packets_to_send_per_interval * 10,
+ EXPECT_EQ(packets_to_send_per_burst_interval +
+ packets_to_send_per_burst_interval * 10,
pacer->QueueSizePackets());
- while (pacer->QueueSizePackets() > packets_to_send_per_interval * 10) {
+ while (pacer->QueueSizePackets() > packets_to_send_per_burst_interval * 10) {
AdvanceTimeUntil(pacer->NextSendTime());
pacer->ProcessPackets();
}
- EXPECT_EQ(pacer->QueueSizePackets(), packets_to_send_per_interval * 10);
+ EXPECT_EQ(pacer->QueueSizePackets(), packets_to_send_per_burst_interval * 10);
EXPECT_CALL(callback_, SendPadding).Times(0);
EXPECT_CALL(callback_, SendPacket(ssrc, _, _, false, false))
@@ -582,12 +584,12 @@ TEST_F(PacingControllerTest, PaceQueuedPackets) {
pacer->ProcessPackets();
// Send some more packet, just show that we can..?
- for (size_t i = 0; i < packets_to_send_per_interval; ++i) {
+ for (size_t i = 0; i < packets_to_send_per_burst_interval; ++i) {
SendAndExpectPacket(pacer.get(), RtpPacketMediaType::kVideo, ssrc,
sequence_number++, clock_.TimeInMilliseconds(), 250);
}
- EXPECT_EQ(packets_to_send_per_interval, pacer->QueueSizePackets());
- for (size_t i = 0; i < packets_to_send_per_interval; ++i) {
+ EXPECT_EQ(packets_to_send_per_burst_interval, pacer->QueueSizePackets());
+ for (size_t i = 0; i < packets_to_send_per_burst_interval; ++i) {
AdvanceTimeUntil(pacer->NextSendTime());
pacer->ProcessPackets();
}
@@ -641,19 +643,23 @@ TEST_F(PacingControllerTest,
TEST_F(PacingControllerTest, Padding) {
uint32_t ssrc = 12345;
uint16_t sequence_number = 1234;
- const size_t kPacketSize = 250;
+ const size_t kPacketSize = 1000;
auto pacer = std::make_unique<PacingController>(&clock_, &callback_, trials_);
pacer->SetPacingRates(kTargetRate * kPaceMultiplier, kTargetRate);
- const size_t kPacketsToSend = 20;
+ const size_t kPacketsToSend = 30;
for (size_t i = 0; i < kPacketsToSend; ++i) {
SendAndExpectPacket(pacer.get(), RtpPacketMediaType::kVideo, ssrc,
sequence_number++, clock_.TimeInMilliseconds(),
kPacketSize);
}
+
+ int expected_bursts =
+ floor(DataSize::Bytes(pacer->QueueSizePackets() * kPacketSize) /
+ (kPaceMultiplier * kTargetRate) /
+ PacingController::kDefaultBurstInterval);
const TimeDelta expected_pace_time =
- DataSize::Bytes(pacer->QueueSizePackets() * kPacketSize) /
- (kPaceMultiplier * kTargetRate);
+ (expected_bursts - 1) * PacingController::kDefaultBurstInterval;
EXPECT_CALL(callback_, SendPadding).Times(0);
// Only the media packets should be sent.
Timestamp start_time = clock_.CurrentTime();
@@ -663,7 +669,7 @@ TEST_F(PacingControllerTest, Padding) {
}
const TimeDelta actual_pace_time = clock_.CurrentTime() - start_time;
EXPECT_LE((actual_pace_time - expected_pace_time).Abs(),
- PacingController::kMinSleepTime);
+ PacingController::kDefaultBurstInterval);
// Pacing media happens at 2.5x, but padding was configured with 1.0x
// factor. We have to wait until the padding debt is gone before we start
@@ -766,8 +772,8 @@ TEST_F(PacingControllerTest, VerifyAverageBitrateVaryingMediaPayload) {
media_payload));
media_bytes += media_payload;
}
-
- AdvanceTimeUntil(pacer->NextSendTime());
+ AdvanceTimeUntil(std::min(clock_.CurrentTime() + TimeDelta::Millis(20),
+ pacer->NextSendTime()));
pacer->ProcessPackets();
}
@@ -805,20 +811,18 @@ TEST_F(PacingControllerTest, Priority) {
// Expect all high and normal priority to be sent out first.
EXPECT_CALL(callback_, SendPadding).Times(0);
+ testing::Sequence s;
EXPECT_CALL(callback_, SendPacket(ssrc, _, capture_time_ms, _, _))
- .Times(packets_to_send_per_interval + 1);
+ .Times(packets_to_send_per_interval + 1)
+ .InSequence(s);
+ EXPECT_CALL(callback_, SendPacket(ssrc_low_priority, _,
+ capture_time_ms_low_priority, _, _))
+ .InSequence(s);
- while (pacer->QueueSizePackets() > 1) {
+ while (pacer->QueueSizePackets() > 0) {
AdvanceTimeUntil(pacer->NextSendTime());
pacer->ProcessPackets();
}
-
- EXPECT_EQ(1u, pacer->QueueSizePackets());
-
- EXPECT_CALL(callback_, SendPacket(ssrc_low_priority, _,
- capture_time_ms_low_priority, _, _));
- AdvanceTimeUntil(pacer->NextSendTime());
- pacer->ProcessPackets();
}
TEST_F(PacingControllerTest, RetransmissionPriority) {
@@ -829,23 +833,22 @@ TEST_F(PacingControllerTest, RetransmissionPriority) {
auto pacer = std::make_unique<PacingController>(&clock_, &callback_, trials_);
pacer->SetPacingRates(kTargetRate * kPaceMultiplier, DataRate::Zero());
- // Due to the multiplicative factor we can send 5 packets during a send
- // interval. (network capacity * multiplier / (8 bits per byte *
- // (packet size * #send intervals per second)
- const size_t packets_to_send_per_interval =
- kTargetRate.bps() * kPaceMultiplier / (8 * 250 * 200);
+ const size_t packets_to_send_per_burst_interval =
+ (kTargetRate * kPaceMultiplier * PacingController::kDefaultBurstInterval)
+ .bytes() /
+ 250;
pacer->ProcessPackets();
EXPECT_EQ(0u, pacer->QueueSizePackets());
// Alternate retransmissions and normal packets.
- for (size_t i = 0; i < packets_to_send_per_interval; ++i) {
+ for (size_t i = 0; i < packets_to_send_per_burst_interval; ++i) {
pacer->EnqueuePacket(BuildPacket(RtpPacketMediaType::kVideo, ssrc,
sequence_number++, capture_time_ms, 250));
pacer->EnqueuePacket(BuildPacket(RtpPacketMediaType::kRetransmission, ssrc,
sequence_number++,
capture_time_ms_retransmission, 250));
}
- EXPECT_EQ(2 * packets_to_send_per_interval, pacer->QueueSizePackets());
+ EXPECT_EQ(2 * packets_to_send_per_burst_interval, pacer->QueueSizePackets());
// Expect all retransmissions to be sent out first despite having a later
// capture time.
@@ -853,19 +856,19 @@ TEST_F(PacingControllerTest, RetransmissionPriority) {
EXPECT_CALL(callback_, SendPacket(_, _, _, false, _)).Times(0);
EXPECT_CALL(callback_,
SendPacket(ssrc, _, capture_time_ms_retransmission, true, _))
- .Times(packets_to_send_per_interval);
+ .Times(packets_to_send_per_burst_interval);
- while (pacer->QueueSizePackets() > packets_to_send_per_interval) {
+ while (pacer->QueueSizePackets() > packets_to_send_per_burst_interval) {
AdvanceTimeUntil(pacer->NextSendTime());
pacer->ProcessPackets();
}
- EXPECT_EQ(packets_to_send_per_interval, pacer->QueueSizePackets());
+ EXPECT_EQ(packets_to_send_per_burst_interval, pacer->QueueSizePackets());
// Expect the remaining (non-retransmission) packets to be sent.
EXPECT_CALL(callback_, SendPadding).Times(0);
EXPECT_CALL(callback_, SendPacket(_, _, _, true, _)).Times(0);
EXPECT_CALL(callback_, SendPacket(ssrc, _, capture_time_ms, false, _))
- .Times(packets_to_send_per_interval);
+ .Times(packets_to_send_per_burst_interval);
while (pacer->QueueSizePackets() > 0) {
AdvanceTimeUntil(pacer->NextSendTime());
@@ -890,13 +893,13 @@ TEST_F(PacingControllerTest, HighPrioDoesntAffectBudget) {
sequence_number++, capture_time_ms, kPacketSize);
}
pacer->ProcessPackets();
+ EXPECT_EQ(pacer->QueueSizePackets(), 0u);
// Low prio packets does affect the budget.
- // Due to the multiplicative factor we can send 5 packets during a send
- // interval. (network capacity * multiplier / (8 bits per byte *
- // (packet size * #send intervals per second)
- const size_t kPacketsToSendPerInterval =
- kTargetRate.bps() * kPaceMultiplier / (8 * kPacketSize * 200);
- for (size_t i = 0; i < kPacketsToSendPerInterval; ++i) {
+ const size_t kPacketsToSendPerBurstInterval =
+ (kTargetRate * kPaceMultiplier * PacingController::kDefaultBurstInterval)
+ .bytes() /
+ kPacketSize;
+ for (size_t i = 0; i < kPacketsToSendPerBurstInterval; ++i) {
SendAndExpectPacket(pacer.get(), RtpPacketMediaType::kVideo, ssrc,
sequence_number++, clock_.TimeInMilliseconds(),
kPacketSize);
@@ -904,16 +907,16 @@ TEST_F(PacingControllerTest, HighPrioDoesntAffectBudget) {
// Send all packets and measure pace time.
Timestamp start_time = clock_.CurrentTime();
+ EXPECT_EQ(pacer->NextSendTime(), clock_.CurrentTime());
while (pacer->QueueSizePackets() > 0) {
AdvanceTimeUntil(pacer->NextSendTime());
pacer->ProcessPackets();
}
- // Measure pacing time. Expect only low-prio packets to affect this.
+ // Measure pacing time.
TimeDelta pacing_time = clock_.CurrentTime() - start_time;
- TimeDelta expected_pacing_time =
- DataSize::Bytes(kPacketsToSendPerInterval * kPacketSize) /
- (kTargetRate * kPaceMultiplier);
+ // All packets sent in one burst since audio packets are not accounted for.
+ TimeDelta expected_pacing_time = TimeDelta::Zero();
EXPECT_NEAR(pacing_time.us<double>(), expected_pacing_time.us<double>(),
PacingController::kMinSleepTime.us<double>());
}
@@ -965,6 +968,7 @@ TEST_F(PacingControllerTest, DoesNotAllowOveruseAfterCongestion) {
auto now_ms = [this] { return clock_.TimeInMilliseconds(); };
auto pacer = std::make_unique<PacingController>(&clock_, &callback_, trials_);
pacer->SetPacingRates(kTargetRate * kPaceMultiplier, DataRate::Zero());
+ pacer->SetSendBurstInterval(TimeDelta::Zero());
EXPECT_CALL(callback_, SendPadding).Times(0);
// The pacing rate is low enough that the budget should not allow two packets
// to be sent in a row.
@@ -1853,6 +1857,7 @@ TEST_F(PacingControllerTest, AccountsForAudioEnqueueTime) {
// Audio not paced, but still accounted for in budget.
pacer->SetAccountForAudioPackets(true);
pacer->SetPacingRates(kPacingDataRate, kPaddingDataRate);
+ pacer->SetSendBurstInterval(TimeDelta::Zero());
// Enqueue two audio packets, advance clock to where one packet
// should have drained the buffer already, has they been sent
@@ -1898,13 +1903,12 @@ TEST_F(PacingControllerTest, NextSendTimeAccountsForPadding) {
EXPECT_EQ(pacer->NextSendTime() - clock_.CurrentTime(),
PacingController::kPausedProcessInterval);
- // Enqueue a new packet, that can't be sent until previous buffer has
- // drained.
+ // Enqueue a new packet, that can be sent immediately due to default burst
+ // rate is 40ms.
SendAndExpectPacket(pacer.get(), RtpPacketMediaType::kVideo, kSsrc,
sequnce_number++, clock_.TimeInMilliseconds(),
kPacketSize.bytes());
- EXPECT_EQ(pacer->NextSendTime() - clock_.CurrentTime(), kPacketPacingTime);
- clock_.AdvanceTime(kPacketPacingTime);
+ EXPECT_EQ(pacer->NextSendTime() - clock_.CurrentTime(), TimeDelta::Zero());
pacer->ProcessPackets();
::testing::Mock::VerifyAndClearExpectations(&callback_);
@@ -1916,11 +1920,13 @@ TEST_F(PacingControllerTest, NextSendTimeAccountsForPadding) {
// previous debt has cleared. Since padding was disabled before, there
// currently is no padding debt.
pacer->SetPacingRates(kPacingDataRate, kPacingDataRate / 2);
- EXPECT_EQ(pacer->NextSendTime() - clock_.CurrentTime(), kPacketPacingTime);
+ EXPECT_EQ(pacer->QueueSizePackets(), 0u);
+ EXPECT_LT(pacer->NextSendTime() - clock_.CurrentTime(),
+ PacingController::kDefaultBurstInterval);
// Advance time, expect padding.
EXPECT_CALL(callback_, SendPadding).WillOnce(Return(kPacketSize.bytes()));
- clock_.AdvanceTime(kPacketPacingTime);
+ clock_.AdvanceTime(pacer->NextSendTime() - clock_.CurrentTime());
pacer->ProcessPackets();
::testing::Mock::VerifyAndClearExpectations(&callback_);
@@ -1933,7 +1939,7 @@ TEST_F(PacingControllerTest, NextSendTimeAccountsForPadding) {
pacer->EnqueuePacket(
BuildPacket(RtpPacketMediaType::kVideo, kSsrc, sequnce_number++,
clock_.TimeInMilliseconds(), kPacketSize.bytes()));
- EXPECT_EQ(pacer->NextSendTime() - clock_.CurrentTime(), kPacketPacingTime);
+ EXPECT_EQ(pacer->NextSendTime(), clock_.CurrentTime());
}
TEST_F(PacingControllerTest, PaddingTargetAccountsForPaddingRate) {
@@ -2011,8 +2017,8 @@ TEST_F(PacingControllerTest, SendsFecPackets) {
TEST_F(PacingControllerTest, GapInPacingDoesntAccumulateBudget) {
const uint32_t kSsrc = 12345;
uint16_t sequence_number = 1234;
- const DataSize kPackeSize = DataSize::Bytes(250);
- const TimeDelta kPacketSendTime = TimeDelta::Millis(15);
+ const DataSize kPackeSize = DataSize::Bytes(1000);
+ const TimeDelta kPacketSendTime = TimeDelta::Millis(25);
auto pacer = std::make_unique<PacingController>(&clock_, &callback_, trials_);
pacer->SetPacingRates(kPackeSize / kPacketSendTime,
@@ -2028,15 +2034,20 @@ TEST_F(PacingControllerTest, GapInPacingDoesntAccumulateBudget) {
// Advance time kPacketSendTime past where the media debt should be 0.
clock_.AdvanceTime(2 * kPacketSendTime);
- // Enqueue two new packets. Expect only one to be sent one ProcessPackets().
+ // Enqueue three new packets. Expect only two to be sent one ProcessPackets()
+ // since the default burst interval is 40ms.
+ SendAndExpectPacket(pacer.get(), RtpPacketMediaType::kVideo, kSsrc,
+ sequence_number++, clock_.TimeInMilliseconds(),
+ kPackeSize.bytes());
+ SendAndExpectPacket(pacer.get(), RtpPacketMediaType::kVideo, kSsrc,
+ sequence_number++, clock_.TimeInMilliseconds(),
+ kPackeSize.bytes());
+ EXPECT_CALL(callback_, SendPacket(kSsrc, sequence_number + 1, _, _, _))
+ .Times(0);
pacer->EnqueuePacket(
BuildPacket(RtpPacketMediaType::kVideo, kSsrc, sequence_number + 1,
clock_.TimeInMilliseconds(), kPackeSize.bytes()));
- pacer->EnqueuePacket(
- BuildPacket(RtpPacketMediaType::kVideo, kSsrc, sequence_number + 2,
- clock_.TimeInMilliseconds(), kPackeSize.bytes()));
- EXPECT_CALL(callback_, SendPacket(kSsrc, sequence_number + 1,
- clock_.TimeInMilliseconds(), false, false));
+
pacer->ProcessPackets();
}
@@ -2044,6 +2055,7 @@ TEST_F(PacingControllerTest, HandlesSubMicrosecondSendIntervals) {
static constexpr DataSize kPacketSize = DataSize::Bytes(1);
static constexpr TimeDelta kPacketSendTime = TimeDelta::Micros(1);
auto pacer = std::make_unique<PacingController>(&clock_, &callback_, trials_);
+ pacer->SetSendBurstInterval(TimeDelta::Zero());
// Set pacing rate such that a packet is sent in 0.5us.
pacer->SetPacingRates(/*pacing_rate=*/2 * kPacketSize / kPacketSendTime,
diff --git a/third_party/libwebrtc/modules/pacing/pacing_gn/moz.build b/third_party/libwebrtc/modules/pacing/pacing_gn/moz.build
index 6b7f69865f..353f876c55 100644
--- a/third_party/libwebrtc/modules/pacing/pacing_gn/moz.build
+++ b/third_party/libwebrtc/modules/pacing/pacing_gn/moz.build
@@ -207,7 +207,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -217,10 +216,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/pacing/task_queue_paced_sender.cc b/third_party/libwebrtc/modules/pacing/task_queue_paced_sender.cc
index afa36ea88d..f7218e48a1 100644
--- a/third_party/libwebrtc/modules/pacing/task_queue_paced_sender.cc
+++ b/third_party/libwebrtc/modules/pacing/task_queue_paced_sender.cc
@@ -17,35 +17,19 @@
#include "api/task_queue/pending_task_safety_flag.h"
#include "api/transport/network_types.h"
#include "rtc_base/checks.h"
-#include "rtc_base/experiments/field_trial_parser.h"
-#include "rtc_base/experiments/field_trial_units.h"
#include "rtc_base/trace_event.h"
namespace webrtc {
-namespace {
-
-constexpr const char* kBurstyPacerFieldTrial = "WebRTC-BurstyPacer";
-
-} // namespace
-
const int TaskQueuePacedSender::kNoPacketHoldback = -1;
-TaskQueuePacedSender::BurstyPacerFlags::BurstyPacerFlags(
- const FieldTrialsView& field_trials)
- : burst("burst") {
- ParseFieldTrial({&burst}, field_trials.Lookup(kBurstyPacerFieldTrial));
-}
-
TaskQueuePacedSender::TaskQueuePacedSender(
Clock* clock,
PacingController::PacketSender* packet_sender,
const FieldTrialsView& field_trials,
TimeDelta max_hold_back_window,
- int max_hold_back_window_in_packets,
- absl::optional<TimeDelta> burst_interval)
+ int max_hold_back_window_in_packets)
: clock_(clock),
- bursty_pacer_flags_(field_trials),
max_hold_back_window_(max_hold_back_window),
max_hold_back_window_in_packets_(max_hold_back_window_in_packets),
pacing_controller_(clock, packet_sender, field_trials),
@@ -56,17 +40,6 @@ TaskQueuePacedSender::TaskQueuePacedSender(
include_overhead_(false),
task_queue_(TaskQueueBase::Current()) {
RTC_DCHECK_GE(max_hold_back_window_, PacingController::kMinSleepTime);
- // There are multiple field trials that can affect burst. If multiple bursts
- // are specified we pick the largest of the values.
- absl::optional<TimeDelta> burst = bursty_pacer_flags_.burst.GetOptional();
- // If not overriden by an experiment, the burst is specified by the
- // `burst_interval` argument.
- if (!burst.has_value()) {
- burst = burst_interval;
- }
- if (burst.has_value()) {
- pacing_controller_.SetSendBurstInterval(burst.value());
- }
}
TaskQueuePacedSender::~TaskQueuePacedSender() {
@@ -74,6 +47,11 @@ TaskQueuePacedSender::~TaskQueuePacedSender() {
is_shutdown_ = true;
}
+void TaskQueuePacedSender::SetSendBurstInterval(TimeDelta burst_interval) {
+ RTC_DCHECK_RUN_ON(task_queue_);
+ pacing_controller_.SetSendBurstInterval(burst_interval);
+}
+
void TaskQueuePacedSender::EnsureStarted() {
RTC_DCHECK_RUN_ON(task_queue_);
is_started_ = true;
diff --git a/third_party/libwebrtc/modules/pacing/task_queue_paced_sender.h b/third_party/libwebrtc/modules/pacing/task_queue_paced_sender.h
index fd71be1654..e29acdf878 100644
--- a/third_party/libwebrtc/modules/pacing/task_queue_paced_sender.h
+++ b/third_party/libwebrtc/modules/pacing/task_queue_paced_sender.h
@@ -45,23 +45,21 @@ class TaskQueuePacedSender : public RtpPacketPacer, public RtpPacketSender {
// processed. Increasing this reduces thread wakeups at the expense of higher
// latency.
//
- // If the `burst_interval` parameter is set, the pacer is allowed to build up
- // a packet "debt" that correspond to approximately the send rate during the
- // specified interval. This greatly reduced wake ups by not pacing packets
- // within the allowed burst budget.
- //
// The taskqueue used when constructing a TaskQueuePacedSender will also be
// used for pacing.
- TaskQueuePacedSender(
- Clock* clock,
- PacingController::PacketSender* packet_sender,
- const FieldTrialsView& field_trials,
- TimeDelta max_hold_back_window,
- int max_hold_back_window_in_packets,
- absl::optional<TimeDelta> burst_interval = absl::nullopt);
+ TaskQueuePacedSender(Clock* clock,
+ PacingController::PacketSender* packet_sender,
+ const FieldTrialsView& field_trials,
+ TimeDelta max_hold_back_window,
+ int max_hold_back_window_in_packets);
~TaskQueuePacedSender() override;
+ // The pacer is allowed to send enqued packets in bursts and can build up a
+ // packet "debt" that correspond to approximately the send rate during
+ // 'burst_interval'.
+ void SetSendBurstInterval(TimeDelta burst_interval);
+
// Ensure that necessary delayed tasks are scheduled.
void EnsureStarted();
@@ -145,15 +143,6 @@ class TaskQueuePacedSender : public RtpPacketPacer, public RtpPacketSender {
Stats GetStats() const;
Clock* const clock_;
- struct BurstyPacerFlags {
- // Parses `kBurstyPacerFieldTrial`. Example:
- // --force-fieldtrials=WebRTC-BurstyPacer/burst:20ms/
- explicit BurstyPacerFlags(const FieldTrialsView& field_trials);
- // If set, the pacer is allowed to build up a packet "debt" that correspond
- // to approximately the send rate during the specified interval.
- FieldTrialOptional<TimeDelta> burst;
- };
- const BurstyPacerFlags bursty_pacer_flags_;
// The holdback window prevents too frequent delayed MaybeProcessPackets()
// calls. These are only applicable if `allow_low_precision` is false.
diff --git a/third_party/libwebrtc/modules/pacing/task_queue_paced_sender_unittest.cc b/third_party/libwebrtc/modules/pacing/task_queue_paced_sender_unittest.cc
index 54347493e7..f0a9ad78c2 100644
--- a/third_party/libwebrtc/modules/pacing/task_queue_paced_sender_unittest.cc
+++ b/third_party/libwebrtc/modules/pacing/task_queue_paced_sender_unittest.cc
@@ -11,6 +11,7 @@
#include "modules/pacing/task_queue_paced_sender.h"
#include <algorithm>
+#include <any>
#include <atomic>
#include <list>
#include <memory>
@@ -24,6 +25,7 @@
#include "api/units/data_rate.h"
#include "api/units/data_size.h"
#include "api/units/time_delta.h"
+#include "modules/pacing/pacing_controller.h"
#include "modules/pacing/packet_router.h"
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
#include "test/gmock.h"
@@ -33,6 +35,9 @@
using ::testing::_;
using ::testing::AtLeast;
+using ::testing::AtMost;
+using ::testing::Lt;
+using ::testing::NiceMock;
using ::testing::Return;
using ::testing::SaveArg;
@@ -167,9 +172,10 @@ TEST(TaskQueuePacedSenderTest, PacesPacketsWithBurst) {
TaskQueuePacedSender pacer(time_controller.GetClock(), &packet_router, trials,
PacingController::kMinSleepTime,
- TaskQueuePacedSender::kNoPacketHoldback,
- // Half a second of bursting.
- TimeDelta::Seconds(0.5));
+ TaskQueuePacedSender::kNoPacketHoldback);
+ pacer.SetSendBurstInterval(
+ // Half a second of bursting.
+ TimeDelta::Seconds(0.5));
// Insert a number of packets, covering one second.
static constexpr size_t kPacketsToSend = 42;
@@ -262,7 +268,7 @@ TEST(TaskQueuePacedSenderTest, ReschedulesProcessOnRateChange) {
TEST(TaskQueuePacedSenderTest, SendsAudioImmediately) {
GlobalSimulatedTimeController time_controller(Timestamp::Millis(1234));
- MockPacketRouter packet_router;
+ NiceMock<MockPacketRouter> packet_router;
ScopedKeyValueConfig trials;
TaskQueuePacedSender pacer(time_controller.GetClock(), &packet_router, trials,
@@ -270,21 +276,16 @@ TEST(TaskQueuePacedSenderTest, SendsAudioImmediately) {
TaskQueuePacedSender::kNoPacketHoldback);
const DataRate kPacingDataRate = DataRate::KilobitsPerSec(125);
- const DataSize kPacketSize = DataSize::Bytes(kDefaultPacketSize);
- const TimeDelta kPacketPacingTime = kPacketSize / kPacingDataRate;
pacer.SetPacingRates(kPacingDataRate, DataRate::Zero());
pacer.EnsureStarted();
- // Add some initial video packets, only one should be sent.
- EXPECT_CALL(packet_router, SendPacket);
+ // Add some initial video packets. Not all should be sent immediately.
+ EXPECT_CALL(packet_router, SendPacket).Times(AtMost(9));
pacer.EnqueuePackets(GeneratePackets(RtpPacketMediaType::kVideo, 10));
time_controller.AdvanceTime(TimeDelta::Zero());
::testing::Mock::VerifyAndClearExpectations(&packet_router);
- // Advance time, but still before next packet should be sent.
- time_controller.AdvanceTime(kPacketPacingTime / 2);
-
// Insert an audio packet, it should be sent immediately.
EXPECT_CALL(packet_router, SendPacket);
pacer.EnqueuePackets(GeneratePackets(RtpPacketMediaType::kAudio, 1));
@@ -295,12 +296,13 @@ TEST(TaskQueuePacedSenderTest, SendsAudioImmediately) {
TEST(TaskQueuePacedSenderTest, SleepsDuringCoalscingWindow) {
const TimeDelta kCoalescingWindow = TimeDelta::Millis(5);
GlobalSimulatedTimeController time_controller(Timestamp::Millis(1234));
- MockPacketRouter packet_router;
+ NiceMock<MockPacketRouter> packet_router;
ScopedKeyValueConfig trials;
TaskQueuePacedSender pacer(time_controller.GetClock(), &packet_router, trials,
kCoalescingWindow,
TaskQueuePacedSender::kNoPacketHoldback);
+ pacer.SetSendBurstInterval(TimeDelta::Zero());
// Set rates so one packet adds one ms of buffer level.
const DataSize kPacketSize = DataSize::Bytes(kDefaultPacketSize);
@@ -310,9 +312,9 @@ TEST(TaskQueuePacedSenderTest, SleepsDuringCoalscingWindow) {
pacer.SetPacingRates(kPacingDataRate, DataRate::Zero());
pacer.EnsureStarted();
- // Add 10 packets. The first should be sent immediately since the buffers
- // are clear.
- EXPECT_CALL(packet_router, SendPacket);
+ // Add 10 packets. The first burst should be sent immediately since the
+ // buffers are clear.
+ EXPECT_CALL(packet_router, SendPacket).Times(AtMost(9));
pacer.EnqueuePackets(GeneratePackets(RtpPacketMediaType::kVideo, 10));
time_controller.AdvanceTime(TimeDelta::Zero());
::testing::Mock::VerifyAndClearExpectations(&packet_router);
@@ -370,11 +372,12 @@ TEST(TaskQueuePacedSenderTest, SchedulesProbeAtSentTime) {
ScopedKeyValueConfig trials(
"WebRTC-Bwe-ProbingBehavior/min_probe_delta:1ms/");
GlobalSimulatedTimeController time_controller(Timestamp::Millis(1234));
- MockPacketRouter packet_router;
+ NiceMock<MockPacketRouter> packet_router;
TaskQueuePacedSender pacer(time_controller.GetClock(), &packet_router, trials,
PacingController::kMinSleepTime,
TaskQueuePacedSender::kNoPacketHoldback);
+ pacer.SetSendBurstInterval(TimeDelta::Zero());
// Set rates so one packet adds 4ms of buffer level.
const DataSize kPacketSize = DataSize::Bytes(kDefaultPacketSize);
@@ -504,11 +507,12 @@ TEST(TaskQueuePacedSenderTest, PacketBasedCoalescing) {
const int kPacketBasedHoldback = 5;
GlobalSimulatedTimeController time_controller(Timestamp::Millis(1234));
- MockPacketRouter packet_router;
+ NiceMock<MockPacketRouter> packet_router;
ScopedKeyValueConfig trials;
TaskQueuePacedSender pacer(time_controller.GetClock(), &packet_router, trials,
kFixedCoalescingWindow, kPacketBasedHoldback);
+ pacer.SetSendBurstInterval(TimeDelta::Zero());
// Set rates so one packet adds one ms of buffer level.
const DataSize kPacketSize = DataSize::Bytes(kDefaultPacketSize);
@@ -559,6 +563,7 @@ TEST(TaskQueuePacedSenderTest, FixedHoldBackHasPriorityOverPackets) {
TaskQueuePacedSender pacer(time_controller.GetClock(), &packet_router, trials,
kFixedCoalescingWindow, kPacketBasedHoldback);
+ pacer.SetSendBurstInterval(TimeDelta::Zero());
// Set rates so one packet adds one ms of buffer level.
const DataSize kPacketSize = DataSize::Bytes(kDefaultPacketSize);
@@ -691,7 +696,7 @@ TEST(TaskQueuePacedSenderTest, PostedPacketsNotSendFromRemovePacketsForSsrc) {
TEST(TaskQueuePacedSenderTest, Stats) {
static constexpr Timestamp kStartTime = Timestamp::Millis(1234);
GlobalSimulatedTimeController time_controller(kStartTime);
- MockPacketRouter packet_router;
+ NiceMock<MockPacketRouter> packet_router;
ScopedKeyValueConfig trials;
TaskQueuePacedSender pacer(time_controller.GetClock(), &packet_router, trials,
@@ -708,7 +713,8 @@ TEST(TaskQueuePacedSenderTest, Stats) {
// Allowed `QueueSizeData` and `ExpectedQueueTime` deviation.
static constexpr size_t kAllowedPacketsDeviation = 1;
static constexpr DataSize kAllowedQueueSizeDeviation =
- DataSize::Bytes(kDefaultPacketSize * kAllowedPacketsDeviation);
+ DataSize::Bytes(kDefaultPacketSize * kAllowedPacketsDeviation) +
+ kPacingRate * PacingController::kDefaultBurstInterval;
static constexpr TimeDelta kAllowedQueueTimeDeviation =
kAllowedQueueSizeDeviation / kPacingRate;
diff --git a/third_party/libwebrtc/modules/portal/pipewire_utils.h b/third_party/libwebrtc/modules/portal/pipewire_utils.h
index 8344a8cefb..c1327b85c9 100644
--- a/third_party/libwebrtc/modules/portal/pipewire_utils.h
+++ b/third_party/libwebrtc/modules/portal/pipewire_utils.h
@@ -11,6 +11,21 @@
#ifndef MODULES_PORTAL_PIPEWIRE_UTILS_H_
#define MODULES_PORTAL_PIPEWIRE_UTILS_H_
+#include <errno.h>
+#include <stdint.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+
+// static
+struct dma_buf_sync {
+ uint64_t flags;
+};
+#define DMA_BUF_SYNC_READ (1 << 0)
+#define DMA_BUF_SYNC_START (0 << 2)
+#define DMA_BUF_SYNC_END (1 << 2)
+#define DMA_BUF_BASE 'b'
+#define DMA_BUF_IOCTL_SYNC _IOW(DMA_BUF_BASE, 0, struct dma_buf_sync)
+
struct pw_thread_loop;
namespace webrtc {
@@ -32,6 +47,66 @@ class PipeWireThreadLoopLock {
pw_thread_loop* const loop_;
};
+// We should synchronize DMA Buffer object access from CPU to avoid potential
+// cache incoherency and data loss.
+// See
+// https://01.org/linuxgraphics/gfx-docs/drm/driver-api/dma-buf.html#cpu-access-to-dma-buffer-objects
+static bool SyncDmaBuf(int fd, uint64_t start_or_end) {
+ struct dma_buf_sync sync = {0};
+
+ sync.flags = start_or_end | DMA_BUF_SYNC_READ;
+
+ while (true) {
+ int ret;
+ ret = ioctl(fd, DMA_BUF_IOCTL_SYNC, &sync);
+ if (ret == -1 && errno == EINTR) {
+ continue;
+ } else if (ret == -1) {
+ return false;
+ } else {
+ break;
+ }
+ }
+
+ return true;
+}
+
+class ScopedBuf {
+ public:
+ ScopedBuf() {}
+ ScopedBuf(uint8_t* map, int map_size, int fd, bool is_dma_buf = false)
+ : map_(map), map_size_(map_size), fd_(fd), is_dma_buf_(is_dma_buf) {}
+ ~ScopedBuf() {
+ if (map_ != MAP_FAILED) {
+ if (is_dma_buf_) {
+ SyncDmaBuf(fd_, DMA_BUF_SYNC_END);
+ }
+ munmap(map_, map_size_);
+ }
+ }
+
+ explicit operator bool() { return map_ != MAP_FAILED; }
+
+ void initialize(uint8_t* map, int map_size, int fd, bool is_dma_buf = false) {
+ map_ = map;
+ map_size_ = map_size;
+ is_dma_buf_ = is_dma_buf;
+ fd_ = fd;
+
+ if (is_dma_buf_) {
+ SyncDmaBuf(fd_, DMA_BUF_SYNC_START);
+ }
+ }
+
+ uint8_t* get() { return map_; }
+
+ protected:
+ uint8_t* map_ = static_cast<uint8_t*>(MAP_FAILED);
+ int map_size_;
+ int fd_;
+ bool is_dma_buf_;
+};
+
} // namespace webrtc
#endif // MODULES_PORTAL_PIPEWIRE_UTILS_H_
diff --git a/third_party/libwebrtc/modules/remote_bitrate_estimator/aimd_rate_control.h b/third_party/libwebrtc/modules/remote_bitrate_estimator/aimd_rate_control.h
index 97fa490adf..c9edc4f551 100644
--- a/third_party/libwebrtc/modules/remote_bitrate_estimator/aimd_rate_control.h
+++ b/third_party/libwebrtc/modules/remote_bitrate_estimator/aimd_rate_control.h
@@ -108,7 +108,7 @@ class AimdRateControl {
// If "Disabled", estimated link capacity is not used as upper bound.
FieldTrialFlag disable_estimate_bounded_increase_{"Disabled"};
FieldTrialParameter<bool> use_current_estimate_as_min_upper_bound_{"c_upper",
- false};
+ true};
absl::optional<DataRate> last_decrease_;
};
} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/remote_bitrate_estimator/aimd_rate_control_unittest.cc b/third_party/libwebrtc/modules/remote_bitrate_estimator/aimd_rate_control_unittest.cc
index f26afe995c..401e87e310 100644
--- a/third_party/libwebrtc/modules/remote_bitrate_estimator/aimd_rate_control_unittest.cc
+++ b/third_party/libwebrtc/modules/remote_bitrate_estimator/aimd_rate_control_unittest.cc
@@ -208,6 +208,7 @@ TEST(AimdRateControlTest, SetEstimateIncreaseBweInAlr) {
TEST(AimdRateControlTest, SetEstimateUpperLimitedByNetworkEstimate) {
AimdRateControl aimd_rate_control(ExplicitKeyValueConfig(""),
/*send_side=*/true);
+ aimd_rate_control.SetEstimate(DataRate::BitsPerSec(300'000), kInitialTime);
NetworkStateEstimate network_estimate;
network_estimate.link_capacity_upper = DataRate::BitsPerSec(400'000);
aimd_rate_control.SetNetworkStateEstimate(network_estimate);
@@ -217,11 +218,9 @@ TEST(AimdRateControlTest, SetEstimateUpperLimitedByNetworkEstimate) {
}
TEST(AimdRateControlTest,
- SetEstimateUpperLimitedByCurrentBitrateIfNetworkEstimateIsLow) {
- AimdRateControl aimd_rate_control(
- ExplicitKeyValueConfig(
- "WebRTC-Bwe-EstimateBoundedIncrease/c_upper:true/"),
- /*send_side=*/true);
+ SetEstimateDefaultUpperLimitedByCurrentBitrateIfNetworkEstimateIsLow) {
+ AimdRateControl aimd_rate_control(ExplicitKeyValueConfig(""),
+ /*send_side=*/true);
aimd_rate_control.SetEstimate(DataRate::BitsPerSec(500'000), kInitialTime);
ASSERT_EQ(aimd_rate_control.LatestEstimate(), DataRate::BitsPerSec(500'000));
@@ -233,9 +232,12 @@ TEST(AimdRateControlTest,
}
TEST(AimdRateControlTest,
- SetEstimateDefaultNotUpperLimitedByCurrentBitrateIfNetworkEstimateIsLow) {
- AimdRateControl aimd_rate_control(ExplicitKeyValueConfig(""),
- /*send_side=*/true);
+ SetEstimateNotUpperLimitedByCurrentBitrateIfNetworkEstimateIsLowIf) {
+ AimdRateControl aimd_rate_control(
+ ExplicitKeyValueConfig(
+ "WebRTC-Bwe-EstimateBoundedIncrease/c_upper:false/"),
+ /*send_side=*/true);
+
aimd_rate_control.SetEstimate(DataRate::BitsPerSec(500'000), kInitialTime);
ASSERT_EQ(aimd_rate_control.LatestEstimate(), DataRate::BitsPerSec(500'000));
diff --git a/third_party/libwebrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_gn/moz.build b/third_party/libwebrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_gn/moz.build
index 2876755e91..45104d15ca 100644
--- a/third_party/libwebrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_gn/moz.build
+++ b/third_party/libwebrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_gn/moz.build
@@ -211,7 +211,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -221,10 +220,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/BUILD.gn b/third_party/libwebrtc/modules/rtp_rtcp/BUILD.gn
index 0fc9931f39..b471c2fa76 100644
--- a/third_party/libwebrtc/modules/rtp_rtcp/BUILD.gn
+++ b/third_party/libwebrtc/modules/rtp_rtcp/BUILD.gn
@@ -258,6 +258,13 @@ rtc_library("rtp_rtcp") {
"source/video_rtp_depacketizer_vp9.h",
]
+ if (rtc_use_h265) {
+ sources += [
+ "source/rtp_packetizer_h265.cc",
+ "source/rtp_packetizer_h265.h",
+ ]
+ }
+
if (rtc_enable_bwe_test_logging) {
defines = [ "BWE_TEST_LOGGING_COMPILE_TIME_ENABLE=1" ]
} else {
@@ -624,6 +631,10 @@ if (rtc_include_tests) {
"source/video_rtp_depacketizer_vp8_unittest.cc",
"source/video_rtp_depacketizer_vp9_unittest.cc",
]
+ if (rtc_use_h265) {
+ sources += [ "source/rtp_packetizer_h265_unittest.cc" ]
+ }
+
deps = [
":fec_test_helper",
":frame_transformer_factory_unittest",
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/leb128_gn/moz.build b/third_party/libwebrtc/modules/rtp_rtcp/leb128_gn/moz.build
index 88f2cb22e0..e42ea18507 100644
--- a/third_party/libwebrtc/modules/rtp_rtcp/leb128_gn/moz.build
+++ b/third_party/libwebrtc/modules/rtp_rtcp/leb128_gn/moz.build
@@ -184,7 +184,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -194,10 +193,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/rtp_rtcp_format_gn/moz.build b/third_party/libwebrtc/modules/rtp_rtcp/rtp_rtcp_format_gn/moz.build
index da304ae5a4..33d8799fb2 100644
--- a/third_party/libwebrtc/modules/rtp_rtcp/rtp_rtcp_format_gn/moz.build
+++ b/third_party/libwebrtc/modules/rtp_rtcp/rtp_rtcp_format_gn/moz.build
@@ -241,7 +241,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -251,10 +250,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/rtp_rtcp_gn/moz.build b/third_party/libwebrtc/modules/rtp_rtcp/rtp_rtcp_gn/moz.build
index 382194837b..8c49736436 100644
--- a/third_party/libwebrtc/modules/rtp_rtcp/rtp_rtcp_gn/moz.build
+++ b/third_party/libwebrtc/modules/rtp_rtcp/rtp_rtcp_gn/moz.build
@@ -255,7 +255,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -265,10 +264,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/rtp_video_header_gn/moz.build b/third_party/libwebrtc/modules/rtp_rtcp/rtp_video_header_gn/moz.build
index 2c8b5e2321..d2a102cfe3 100644
--- a/third_party/libwebrtc/modules/rtp_rtcp/rtp_video_header_gn/moz.build
+++ b/third_party/libwebrtc/modules/rtp_rtcp/rtp_video_header_gn/moz.build
@@ -195,7 +195,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -205,10 +204,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/flexfec_header_reader_writer.cc b/third_party/libwebrtc/modules/rtp_rtcp/source/flexfec_header_reader_writer.cc
index cfca7cb066..3e6d04d59c 100644
--- a/third_party/libwebrtc/modules/rtp_rtcp/source/flexfec_header_reader_writer.cc
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/flexfec_header_reader_writer.cc
@@ -138,9 +138,9 @@ bool FlexfecHeaderReader::ReadFecHeader(
mask_part0 <<= 1;
ByteWriter<uint16_t>::WriteBigEndian(&data[byte_index], mask_part0);
byte_index += kFlexfecPacketMaskSizes[0];
- if (k_bit0) {
- // The first K-bit is set, and the packet mask is thus only 2 bytes long.
- // We have finished reading the properties for current ssrc.
+ if (!k_bit0) {
+ // The first K-bit is clear, and the packet mask is thus only 2 bytes
+ // long. We have finished reading the properties for current ssrc.
fec_packet->protected_streams[i].packet_mask_size =
kFlexfecPacketMaskSizes[0];
} else {
@@ -162,8 +162,8 @@ bool FlexfecHeaderReader::ReadFecHeader(
mask_part1 <<= 2;
ByteWriter<uint32_t>::WriteBigEndian(&data[byte_index], mask_part1);
byte_index += kFlexfecPacketMaskSizes[1] - kFlexfecPacketMaskSizes[0];
- if (k_bit1) {
- // The first K-bit is clear, but the second K-bit is set. The packet
+ if (!k_bit1) {
+ // The first K-bit is set, but the second K-bit is clear. The packet
// mask is thus 6 bytes long. We have finished reading the properties
// for current ssrc.
fec_packet->protected_streams[i].packet_mask_size =
@@ -273,8 +273,9 @@ void FlexfecHeaderWriter::FinalizeFecHeader(
tmp_mask_part0 >>= 1; // Shift, thus clearing K-bit 0.
ByteWriter<uint16_t>::WriteBigEndian(write_at, tmp_mask_part0);
+ *write_at |= 0x80; // Set K-bit 0.
write_at += kFlexfecPacketMaskSizes[0];
- tmp_mask_part1 >>= 2; // Shift, thus clearing K-bit 1 and bit 15.
+ tmp_mask_part1 >>= 2; // Shift twice, thus clearing K-bit 1 and bit 15.
ByteWriter<uint32_t>::WriteBigEndian(write_at, tmp_mask_part1);
bool bit15 = (protected_stream.packet_mask[1] & 0x01) != 0;
@@ -284,9 +285,9 @@ void FlexfecHeaderWriter::FinalizeFecHeader(
bool bit46 = (protected_stream.packet_mask[5] & 0x02) != 0;
bool bit47 = (protected_stream.packet_mask[5] & 0x01) != 0;
if (!bit46 && !bit47) {
- *write_at |= 0x80; // Set K-bit 1.
write_at += kFlexfecPacketMaskSizes[1] - kFlexfecPacketMaskSizes[0];
} else {
+ *write_at |= 0x80; // Set K-bit 1.
write_at += kFlexfecPacketMaskSizes[1] - kFlexfecPacketMaskSizes[0];
// Clear all trailing bits.
memset(write_at, 0,
@@ -307,14 +308,13 @@ void FlexfecHeaderWriter::FinalizeFecHeader(
ByteWriter<uint16_t>::WriteBigEndian(write_at, tmp_mask_part0);
bool bit15 = (protected_stream.packet_mask[1] & 0x01) != 0;
if (!bit15) {
- *write_at |= 0x80; // Set K-bit 0.
write_at += kFlexfecPacketMaskSizes[0];
} else {
+ *write_at |= 0x80; // Set K-bit 0.
write_at += kFlexfecPacketMaskSizes[0];
// Clear all trailing bits.
memset(write_at, 0U,
kFlexfecPacketMaskSizes[1] - kFlexfecPacketMaskSizes[0]);
- *write_at |= 0x80; // Set K-bit 1.
*write_at |= 0x40; // Set bit 15.
write_at += kFlexfecPacketMaskSizes[1] - kFlexfecPacketMaskSizes[0];
}
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/flexfec_header_reader_writer_unittest.cc b/third_party/libwebrtc/modules/rtp_rtcp/source/flexfec_header_reader_writer_unittest.cc
index 6995ba3871..f25e0d8d2a 100644
--- a/third_party/libwebrtc/modules/rtp_rtcp/source/flexfec_header_reader_writer_unittest.cc
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/flexfec_header_reader_writer_unittest.cc
@@ -36,11 +36,12 @@ using ReceivedFecPacket = ForwardErrorCorrection::ReceivedFecPacket;
using ::testing::Each;
using ::testing::ElementsAreArray;
-constexpr uint8_t kMask0[] = {0xAB, 0xCD}; // First K bit is set.
-constexpr uint8_t kMask1[] = {0x12, 0x34, // First K bit cleared.
- 0xF6, 0x78, 0x9A, 0xBC}; // Second K bit set.
-constexpr uint8_t kMask2[] = {0x12, 0x34, // First K bit cleared.
- 0x56, 0x78, 0x9A, 0xBC, // Second K bit cleared.
+constexpr uint8_t kKBit = 1 << 7;
+constexpr uint8_t kMask0[] = {0x2B, 0xCD}; // First K bit is cleared.
+constexpr uint8_t kMask1[] = {0x92, 0x34, // First K bit set.
+ 0x76, 0x78, 0x9A, 0xBC}; // Second K bit cleared.
+constexpr uint8_t kMask2[] = {0x92, 0x34, // First K bit set.
+ 0xD6, 0x78, 0x9A, 0xBC, // Second K bit set.
0xDE, 0xF0, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC};
constexpr size_t kMediaPacketLength = 1234;
@@ -186,11 +187,10 @@ void VerifyWrittenAndReadHeaders(
} // namespace
-TEST(FlexfecHeaderReaderTest, ReadsHeaderWithKBit0SetSingleStream) {
- constexpr uint8_t kKBit0 = 1 << 7;
+TEST(FlexfecHeaderReaderTest, ReadsHeaderWithKBit0ClearSingleStream) {
constexpr size_t kExpectedFecHeaderSize = 12;
constexpr uint16_t kSnBase = 0x0102;
- constexpr uint8_t kFlexfecPktMask[] = {kKBit0 | 0x08, 0x81};
+ constexpr uint8_t kFlexfecPktMask[] = {0x08, 0x81};
constexpr uint8_t kUlpfecPacketMask[] = {0x11, 0x02};
constexpr uint8_t kPacketData[] = {
kFlexible, kPtRecovery, kLengthRecovery[0], kLengthRecovery[1],
@@ -215,13 +215,11 @@ TEST(FlexfecHeaderReaderTest, ReadsHeaderWithKBit0SetSingleStream) {
VerifyReadHeaders(kExpectedFecHeaderSize, read_packet, expected);
}
-TEST(FlexfecHeaderReaderTest, ReadsHeaderWithKBit1SetSingleStream) {
- constexpr uint8_t kKBit0 = 0 << 7;
- constexpr uint8_t kKBit1 = 1 << 7;
+TEST(FlexfecHeaderReaderTest, ReadsHeaderWithKBit1ClearSingleStream) {
constexpr size_t kExpectedFecHeaderSize = 16;
constexpr uint16_t kSnBase = 0x0102;
- constexpr uint8_t kFlexfecPktMask[] = {kKBit0 | 0x48, 0x81, //
- kKBit1 | 0x02, 0x11, 0x00, 0x21};
+ constexpr uint8_t kFlexfecPktMask[] = {kKBit | 0x48, 0x81, //
+ 0x02, 0x11, 0x00, 0x21};
constexpr uint8_t kUlpfecPacketMask[] = {0x91, 0x02, //
0x08, 0x44, 0x00, 0x84};
constexpr uint8_t kPacketData[] = {
@@ -250,15 +248,13 @@ TEST(FlexfecHeaderReaderTest, ReadsHeaderWithKBit1SetSingleStream) {
VerifyReadHeaders(kExpectedFecHeaderSize, read_packet, expected);
}
-TEST(FlexfecHeaderReaderTest, ReadsHeaderWithNoKBitsSetSingleStream) {
- constexpr uint8_t kKBit0 = 0 << 7;
- constexpr uint8_t kKBit1 = 0 << 7;
+TEST(FlexfecHeaderReaderTest, ReadsHeaderWithBothKBitsSetSingleStream) {
constexpr size_t kExpectedFecHeaderSize = 24;
constexpr uint16_t kSnBase = 0x0102;
- constexpr uint8_t kFlexfecPacketMask[] = {kKBit0 | 0x48, 0x81, //
- kKBit1 | 0x02, 0x11, 0x00, 0x21, //
- 0x01, 0x11, 0x11, 0x11,
- 0x11, 0x11, 0x11, 0x11};
+ constexpr uint8_t kFlexfecPacketMask[] = {kKBit | 0x48, 0x81, //
+ kKBit | 0x02, 0x11, 0x00, 0x21, //
+ 0x01, 0x11, 0x11, 0x11,
+ 0x11, 0x11, 0x11, 0x11};
constexpr uint8_t kUlpfecPacketMask[] = {0x91, 0x02, //
0x08, 0x44, 0x00, 0x84, //
0x04, 0x44, 0x44, 0x44,
@@ -309,14 +305,13 @@ TEST(FlexfecHeaderReaderTest, ReadsHeaderWithNoKBitsSetSingleStream) {
VerifyReadHeaders(kExpectedFecHeaderSize, read_packet, expected);
}
-TEST(FlexfecHeaderReaderTest, ReadsHeaderWithKBit0Set2Streams) {
- constexpr uint8_t kKBit0 = 1 << 7;
+TEST(FlexfecHeaderReaderTest, ReadsHeaderWithKBit0Clear2Streams) {
constexpr size_t kExpectedFecHeaderSize = 16;
constexpr uint16_t kSnBase0 = 0x0102;
constexpr uint16_t kSnBase1 = 0x0304;
- constexpr uint8_t kFlexfecPktMask1[] = {kKBit0 | 0x08, 0x81};
+ constexpr uint8_t kFlexfecPktMask1[] = {0x08, 0x81};
constexpr uint8_t kUlpfecPacketMask1[] = {0x11, 0x02};
- constexpr uint8_t kFlexfecPktMask2[] = {kKBit0 | 0x04, 0x41};
+ constexpr uint8_t kFlexfecPktMask2[] = {0x04, 0x41};
constexpr uint8_t kUlpfecPacketMask2[] = {0x08, 0x82};
constexpr uint8_t kPacketData[] = {
@@ -349,18 +344,16 @@ TEST(FlexfecHeaderReaderTest, ReadsHeaderWithKBit0Set2Streams) {
VerifyReadHeaders(kExpectedFecHeaderSize, read_packet, expected);
}
-TEST(FlexfecHeaderReaderTest, ReadsHeaderWithKBit1Set2Streams) {
- constexpr uint8_t kKBit0 = 0 << 7;
- constexpr uint8_t kKBit1 = 1 << 7;
+TEST(FlexfecHeaderReaderTest, ReadsHeaderWithKBit1Clear2Streams) {
constexpr size_t kExpectedFecHeaderSize = 24;
constexpr uint16_t kSnBase0 = 0x0102;
constexpr uint16_t kSnBase1 = 0x0304;
- constexpr uint8_t kFlexfecPktMask1[] = {kKBit0 | 0x48, 0x81, //
- kKBit1 | 0x02, 0x11, 0x00, 0x21};
+ constexpr uint8_t kFlexfecPktMask1[] = {kKBit | 0x48, 0x81, //
+ 0x02, 0x11, 0x00, 0x21};
constexpr uint8_t kUlpfecPacketMask1[] = {0x91, 0x02, //
0x08, 0x44, 0x00, 0x84};
- constexpr uint8_t kFlexfecPktMask2[] = {kKBit0 | 0x57, 0x82, //
- kKBit1 | 0x04, 0x33, 0x00, 0x51};
+ constexpr uint8_t kFlexfecPktMask2[] = {kKBit | 0x57, 0x82, //
+ 0x04, 0x33, 0x00, 0x51};
constexpr uint8_t kUlpfecPacketMask2[] = {0xAF, 0x04, //
0x10, 0xCC, 0x01, 0x44};
constexpr uint8_t kPacketData[] = {
@@ -398,24 +391,22 @@ TEST(FlexfecHeaderReaderTest, ReadsHeaderWithKBit1Set2Streams) {
VerifyReadHeaders(kExpectedFecHeaderSize, read_packet, expected);
}
-TEST(FlexfecHeaderReaderTest, ReadsHeaderWithNoKBitsSet2Streams) {
- constexpr uint8_t kKBit0 = 0 << 7;
- constexpr uint8_t kKBit1 = 0 << 7;
+TEST(FlexfecHeaderReaderTest, ReadsHeaderWithBothKBitsSet2Streams) {
constexpr size_t kExpectedFecHeaderSize = 40;
constexpr uint16_t kSnBase0 = 0x0102;
constexpr uint16_t kSnBase1 = 0x0304;
- constexpr uint8_t kFlexfecPktMask1[] = {kKBit0 | 0x48, 0x81, //
- kKBit1 | 0x02, 0x11, 0x00, 0x21, //
- 0x01, 0x11, 0x11, 0x11,
- 0x11, 0x11, 0x11, 0x11};
+ constexpr uint8_t kFlexfecPktMask1[] = {kKBit | 0x48, 0x81, //
+ kKBit | 0x02, 0x11, 0x00, 0x21, //
+ 0x01, 0x11, 0x11, 0x11,
+ 0x11, 0x11, 0x11, 0x11};
constexpr uint8_t kUlpfecPacketMask1[] = {0x91, 0x02, //
0x08, 0x44, 0x00, 0x84, //
0x04, 0x44, 0x44, 0x44,
0x44, 0x44, 0x44, 0x44};
- constexpr uint8_t kFlexfecPktMask2[] = {kKBit0 | 0x32, 0x84, //
- kKBit1 | 0x05, 0x23, 0x00, 0x55, //
- 0xA3, 0x22, 0x22, 0x22,
- 0x22, 0x22, 0x22, 0x35};
+ constexpr uint8_t kFlexfecPktMask2[] = {kKBit | 0x32, 0x84, //
+ kKBit | 0x05, 0x23, 0x00, 0x55, //
+ 0xA3, 0x22, 0x22, 0x22,
+ 0x22, 0x22, 0x22, 0x35};
constexpr uint8_t kUlpfecPacketMask2[] = {0x65, 0x08, //
0x14, 0x8C, 0x01, 0x56, //
0x8C, 0x88, 0x88, 0x88,
@@ -490,29 +481,27 @@ TEST(FlexfecHeaderReaderTest, ReadsHeaderWithNoKBitsSet2Streams) {
}
TEST(FlexfecHeaderReaderTest, ReadsHeaderWithMultipleStreamsMultipleMasks) {
- constexpr uint8_t kBit0 = 0 << 7;
- constexpr uint8_t kBit1 = 1 << 7;
constexpr size_t kExpectedFecHeaderSize = 44;
constexpr uint16_t kSnBase0 = 0x0102;
constexpr uint16_t kSnBase1 = 0x0304;
constexpr uint16_t kSnBase2 = 0x0506;
constexpr uint16_t kSnBase3 = 0x0708;
- constexpr uint8_t kFlexfecPacketMask1[] = {kBit1 | 0x29, 0x91};
+ constexpr uint8_t kFlexfecPacketMask1[] = {0x29, 0x91};
constexpr uint8_t kUlpfecPacketMask1[] = {0x53, 0x22};
- constexpr uint8_t kFlexfecPacketMask2[] = {kBit0 | 0x32, 0xA1, //
- kBit1 | 0x02, 0x11, 0x00, 0x21};
+ constexpr uint8_t kFlexfecPacketMask2[] = {kKBit | 0x32, 0xA1, //
+ 0x02, 0x11, 0x00, 0x21};
constexpr uint8_t kUlpfecPacketMask2[] = {0x65, 0x42, //
0x08, 0x44, 0x00, 0x84};
- constexpr uint8_t kFlexfecPacketMask3[] = {kBit0 | 0x48, 0x81, //
- kBit0 | 0x02, 0x11, 0x00, 0x21, //
+ constexpr uint8_t kFlexfecPacketMask3[] = {kKBit | 0x48, 0x81, //
+ kKBit | 0x02, 0x11, 0x00, 0x21, //
0x01, 0x11, 0x11, 0x11,
0x11, 0x11, 0x11, 0x11};
constexpr uint8_t kUlpfecPacketMask3[] = {0x91, 0x02, //
0x08, 0x44, 0x00, 0x84, //
0x04, 0x44, 0x44, 0x44,
0x44, 0x44, 0x44, 0x44};
- constexpr uint8_t kFlexfecPacketMask4[] = {kBit0 | 0x32, 0x84, //
- kBit1 | 0x05, 0x23, 0x00, 0x55};
+ constexpr uint8_t kFlexfecPacketMask4[] = {kKBit | 0x32, 0x84, //
+ 0x05, 0x23, 0x00, 0x55};
constexpr uint8_t kUlpfecPacketMask4[] = {0x65, 0x08, //
0x14, 0x8C, 0x01, 0x54};
constexpr uint8_t kPacketData[] = {kFlexible,
@@ -642,7 +631,7 @@ TEST(FlexfecHeaderReaderTest, ReadShortPacketWithKBit0SetShouldFail) {
EXPECT_FALSE(reader.ReadFecHeader(&read_packet));
}
-TEST(FlexfecHeaderReaderTest, ReadShortPacketWithKBit1SetShouldFail) {
+TEST(FlexfecHeaderReaderTest, ReadShortPacketWithKBit1ClearShouldFail) {
// Simulate short received packet.
constexpr uint8_t kPacketData[] = {
kFlexible, kPtRecovery, kLengthRecovery[0], kLengthRecovery[1],
@@ -659,7 +648,7 @@ TEST(FlexfecHeaderReaderTest, ReadShortPacketWithKBit1SetShouldFail) {
EXPECT_FALSE(reader.ReadFecHeader(&read_packet));
}
-TEST(FlexfecHeaderReaderTest, ReadShortPacketWithKBit1ClearedShouldFail) {
+TEST(FlexfecHeaderReaderTest, ReadShortPacketWithKBit1SetShouldFail) {
// Simulate short received packet.
constexpr uint8_t kPacketData[] = {
kFlexible, kPtRecovery, kLengthRecovery[0], kLengthRecovery[1],
@@ -698,8 +687,8 @@ TEST(FlexfecHeaderReaderTest, ReadShortPacketMultipleStreamsShouldFail) {
EXPECT_FALSE(reader.ReadFecHeader(&read_packet));
}
-TEST(FlexfecHeaderWriterTest, FinalizesHeaderWithKBit0SetSingleStream) {
- constexpr uint8_t kFlexfecPacketMask[] = {0x88, 0x81};
+TEST(FlexfecHeaderWriterTest, FinalizesHeaderWithKBit0ClearSingleStream) {
+ constexpr uint8_t kFlexfecPacketMask[] = {0x08, 0x81};
constexpr uint8_t kUlpfecPacketMask[] = {0x11, 0x02};
constexpr uint16_t kMediaStartSeqNum = 1234;
Packet written_packet = WritePacket({{.ssrc = 0x01,
@@ -714,8 +703,8 @@ TEST(FlexfecHeaderWriterTest, FinalizesHeaderWithKBit0SetSingleStream) {
VerifyFinalizedHeaders(written_packet, expected);
}
-TEST(FlexfecHeaderWriterTest, FinalizesHeaderWithKBit1SetSingleStream) {
- constexpr uint8_t kFlexfecPacketMask[] = {0x48, 0x81, 0x82, 0x11, 0x00, 0x21};
+TEST(FlexfecHeaderWriterTest, FinalizesHeaderWithKBit1ClearSingleStream) {
+ constexpr uint8_t kFlexfecPacketMask[] = {0xC8, 0x81, 0x02, 0x11, 0x00, 0x21};
constexpr uint8_t kUlpfecPacketMask[] = {0x91, 0x02, 0x08, 0x44, 0x00, 0x84};
constexpr uint16_t kMediaStartSeqNum = 1234;
Packet written_packet = WritePacket({{.ssrc = 0x01,
@@ -730,10 +719,10 @@ TEST(FlexfecHeaderWriterTest, FinalizesHeaderWithKBit1SetSingleStream) {
VerifyFinalizedHeaders(written_packet, expected);
}
-TEST(FlexfecHeaderWriterTest, FinalizesHeaderWithNoKBitsSetSingleStream) {
+TEST(FlexfecHeaderWriterTest, FinalizesHeaderWithBothKBitsSetSingleStream) {
constexpr uint8_t kFlexfecPacketMask[] = {
- 0x11, 0x11, // K-bit 0 clear.
- 0x11, 0x11, 0x11, 0x10, // K-bit 1 clear.
+ 0x91, 0x11, // K-bit 0 set.
+ 0x91, 0x11, 0x11, 0x10, // K-bit 1 set.
0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 //
};
constexpr uint8_t kUlpfecPacketMask[] = {0x22, 0x22, 0x44, 0x44, 0x44, 0x41};
@@ -752,22 +741,22 @@ TEST(FlexfecHeaderWriterTest, FinalizesHeaderWithNoKBitsSetSingleStream) {
TEST(FlexfecHeaderWriterTest, FinalizesHeaderMultipleStreamsMultipleMasks) {
constexpr uint8_t kFlexfecPacketMask1[] = {
- 0x11, 0x11, // K-bit 0 clear.
- 0x11, 0x11, 0x11, 0x10, // K-bit 1 clear.
+ 0x91, 0x11, // K-bit 0 set.
+ 0x91, 0x11, 0x11, 0x10, // K-bit 1 set.
0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 //
};
constexpr uint8_t kUlpfecPacketMask1[] = {0x22, 0x22, 0x44, 0x44, 0x44, 0x41};
constexpr uint16_t kMediaStartSeqNum1 = 1234;
- constexpr uint8_t kFlexfecPacketMask2[] = {0x88, 0x81};
+ constexpr uint8_t kFlexfecPacketMask2[] = {0x08, 0x81};
constexpr uint8_t kUlpfecPacketMask2[] = {0x11, 0x02};
constexpr uint16_t kMediaStartSeqNum2 = 2345;
- constexpr uint8_t kFlexfecPacketMask3[] = {0x48, 0x81, 0x82,
+ constexpr uint8_t kFlexfecPacketMask3[] = {0xC8, 0x81, 0x02,
0x11, 0x00, 0x21};
constexpr uint8_t kUlpfecPacketMask3[] = {0x91, 0x02, 0x08, 0x44, 0x00, 0x84};
constexpr uint16_t kMediaStartSeqNum3 = 3456;
constexpr uint8_t kFlexfecPacketMask4[] = {
- 0x55, 0xAA, // K-bit 0 clear.
- 0x22, 0xAB, 0xCD, 0xEF, // K-bit 1 clear.
+ 0xD5, 0xAA, // K-bit 0 set.
+ 0xA2, 0xAB, 0xCD, 0xEF, // K-bit 1 set.
0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 //
};
constexpr uint8_t kUlpfecPacketMask4[] = {0xAB, 0x54, 0x8A, 0xAF, 0x37, 0xBF};
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtp_format.cc b/third_party/libwebrtc/modules/rtp_rtcp/source/rtp_format.cc
index 2c11a29bfa..c7534dee40 100644
--- a/third_party/libwebrtc/modules/rtp_rtcp/source/rtp_format.cc
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtp_format.cc
@@ -22,6 +22,9 @@
#include "modules/video_coding/codecs/vp8/include/vp8_globals.h"
#include "modules/video_coding/codecs/vp9/include/vp9_globals.h"
#include "rtc_base/checks.h"
+#ifdef RTC_ENABLE_H265
+#include "modules/rtp_rtcp/source/rtp_packetizer_h265.h"
+#endif
namespace webrtc {
@@ -57,7 +60,11 @@ std::unique_ptr<RtpPacketizer> RtpPacketizer::Create(
return std::make_unique<RtpPacketizerAv1>(
payload, limits, rtp_video_header.frame_type,
rtp_video_header.is_last_frame_in_picture);
- // TODO(bugs.webrtc.org/13485): Implement RtpPacketizerH265.
+#ifdef RTC_ENABLE_H265
+ case kVideoCodecH265: {
+ return std::make_unique<RtpPacketizerH265>(payload, limits);
+ }
+#endif
default: {
return std::make_unique<RtpPacketizerGeneric>(payload, limits,
rtp_video_header);
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtp_packetizer_av1.cc b/third_party/libwebrtc/modules/rtp_rtcp/source/rtp_packetizer_av1.cc
index 95dbaf364c..859b529a47 100644
--- a/third_party/libwebrtc/modules/rtp_rtcp/source/rtp_packetizer_av1.cc
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtp_packetizer_av1.cc
@@ -74,8 +74,7 @@ RtpPacketizerAv1::RtpPacketizerAv1(rtc::ArrayView<const uint8_t> payload,
std::vector<RtpPacketizerAv1::Obu> RtpPacketizerAv1::ParseObus(
rtc::ArrayView<const uint8_t> payload) {
std::vector<Obu> result;
- rtc::ByteBufferReader payload_reader(
- reinterpret_cast<const char*>(payload.data()), payload.size());
+ rtc::ByteBufferReader payload_reader(payload);
while (payload_reader.Length() > 0) {
Obu obu;
payload_reader.ReadUInt8(&obu.header);
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtp_packetizer_h265.cc b/third_party/libwebrtc/modules/rtp_rtcp/source/rtp_packetizer_h265.cc
new file mode 100644
index 0000000000..313680cc87
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtp_packetizer_h265.cc
@@ -0,0 +1,350 @@
+/*
+ * Copyright (c) 2023 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/rtp_rtcp/source/rtp_packetizer_h265.h"
+
+#include <vector>
+
+#include "absl/types/optional.h"
+#include "common_video/h264/h264_common.h"
+#include "common_video/h265/h265_common.h"
+#include "modules/rtp_rtcp/source/byte_io.h"
+#include "rtc_base/logging.h"
+
+namespace webrtc {
+namespace {
+
+// The payload header consists of the same
+// fields (F, Type, LayerId and TID) as the NAL unit header. Refer to
+// section 4.2 in RFC 7798.
+constexpr size_t kH265PayloadHeaderSize = 2;
+// Unlike H.264, H265 NAL header is 2-bytes.
+constexpr size_t kH265NalHeaderSize = 2;
+// H265's FU is constructed of 2-byte payload header, 1-byte FU header and FU
+// payload.
+constexpr size_t kH265FuHeaderSize = 1;
+// The NALU size for H265 RTP aggregated packet indicates the size of the NAL
+// unit is 2-bytes.
+constexpr size_t kH265LengthFieldSize = 2;
+
+enum H265NalHdrMasks {
+ kH265FBit = 0x80,
+ kH265TypeMask = 0x7E,
+ kH265LayerIDHMask = 0x1,
+ kH265LayerIDLMask = 0xF8,
+ kH265TIDMask = 0x7,
+ kH265TypeMaskN = 0x81,
+ kH265TypeMaskInFuHeader = 0x3F
+};
+
+// Bit masks for FU headers.
+enum H265FuBitmasks {
+ kH265SBitMask = 0x80,
+ kH265EBitMask = 0x40,
+ kH265FuTypeBitMask = 0x3F
+};
+
+} // namespace
+
+RtpPacketizerH265::RtpPacketizerH265(rtc::ArrayView<const uint8_t> payload,
+ PayloadSizeLimits limits)
+ : limits_(limits), num_packets_left_(0) {
+ for (const auto& nalu :
+ H264::FindNaluIndices(payload.data(), payload.size())) {
+ input_fragments_.push_back(
+ payload.subview(nalu.payload_start_offset, nalu.payload_size));
+ }
+
+ if (!GeneratePackets()) {
+ // If failed to generate all the packets, discard already generated
+ // packets in case the caller would ignore return value and still try to
+ // call NextPacket().
+ num_packets_left_ = 0;
+ while (!packets_.empty()) {
+ packets_.pop();
+ }
+ }
+}
+
+RtpPacketizerH265::~RtpPacketizerH265() = default;
+
+size_t RtpPacketizerH265::NumPackets() const {
+ return num_packets_left_;
+}
+
+bool RtpPacketizerH265::GeneratePackets() {
+ for (size_t i = 0; i < input_fragments_.size();) {
+ int fragment_len = input_fragments_[i].size();
+ int single_packet_capacity = limits_.max_payload_len;
+ if (input_fragments_.size() == 1) {
+ single_packet_capacity -= limits_.single_packet_reduction_len;
+ } else if (i == 0) {
+ single_packet_capacity -= limits_.first_packet_reduction_len;
+ } else if (i + 1 == input_fragments_.size()) {
+ // Pretend that last fragment is larger instead of making last packet
+ // smaller.
+ single_packet_capacity -= limits_.last_packet_reduction_len;
+ }
+ if (fragment_len > single_packet_capacity) {
+ if (!PacketizeFu(i)) {
+ return false;
+ }
+ ++i;
+ } else {
+ i = PacketizeAp(i);
+ }
+ }
+ return true;
+}
+
+bool RtpPacketizerH265::PacketizeFu(size_t fragment_index) {
+ // Fragment payload into packets (FU).
+ // Strip out the original header and leave room for the FU header.
+ rtc::ArrayView<const uint8_t> fragment = input_fragments_[fragment_index];
+ PayloadSizeLimits limits = limits_;
+ // Refer to section 4.4.3 in RFC7798, each FU fragment will have a 2-bytes
+ // payload header and a one-byte FU header. DONL is not supported so ignore
+ // its size when calculating max_payload_len.
+ limits.max_payload_len -= kH265FuHeaderSize + kH265PayloadHeaderSize;
+
+ // Update single/first/last packet reductions unless it is single/first/last
+ // fragment.
+ if (input_fragments_.size() != 1) {
+ // if this fragment is put into a single packet, it might still be the
+ // first or the last packet in the whole sequence of packets.
+ if (fragment_index == input_fragments_.size() - 1) {
+ limits.single_packet_reduction_len = limits_.last_packet_reduction_len;
+ } else if (fragment_index == 0) {
+ limits.single_packet_reduction_len = limits_.first_packet_reduction_len;
+ } else {
+ limits.single_packet_reduction_len = 0;
+ }
+ }
+ if (fragment_index != 0) {
+ limits.first_packet_reduction_len = 0;
+ }
+ if (fragment_index != input_fragments_.size() - 1) {
+ limits.last_packet_reduction_len = 0;
+ }
+
+ // Strip out the original header.
+ size_t payload_left = fragment.size() - kH265NalHeaderSize;
+ int offset = kH265NalHeaderSize;
+
+ std::vector<int> payload_sizes = SplitAboutEqually(payload_left, limits);
+ if (payload_sizes.empty()) {
+ return false;
+ }
+
+ for (size_t i = 0; i < payload_sizes.size(); ++i) {
+ int packet_length = payload_sizes[i];
+ RTC_CHECK_GT(packet_length, 0);
+ uint16_t header = (fragment[0] << 8) | fragment[1];
+ packets_.push({.source_fragment = fragment.subview(offset, packet_length),
+ .first_fragment = (i == 0),
+ .last_fragment = (i == payload_sizes.size() - 1),
+ .aggregated = false,
+ .header = header});
+ offset += packet_length;
+ payload_left -= packet_length;
+ }
+ num_packets_left_ += payload_sizes.size();
+ RTC_CHECK_EQ(payload_left, 0);
+ return true;
+}
+
+int RtpPacketizerH265::PacketizeAp(size_t fragment_index) {
+ // Aggregate fragments into one packet.
+ size_t payload_size_left = limits_.max_payload_len;
+ if (input_fragments_.size() == 1) {
+ payload_size_left -= limits_.single_packet_reduction_len;
+ } else if (fragment_index == 0) {
+ payload_size_left -= limits_.first_packet_reduction_len;
+ }
+ int aggregated_fragments = 0;
+ size_t fragment_headers_length = 0;
+ rtc::ArrayView<const uint8_t> fragment = input_fragments_[fragment_index];
+ RTC_CHECK_GE(payload_size_left, fragment.size());
+ ++num_packets_left_;
+
+ auto payload_size_needed = [&] {
+ size_t fragment_size = fragment.size() + fragment_headers_length;
+ if (input_fragments_.size() == 1) {
+ // Single fragment, single packet, payload_size_left already adjusted
+ // with limits_.single_packet_reduction_len.
+ return fragment_size;
+ }
+ if (fragment_index == input_fragments_.size() - 1) {
+ // Last fragment, so this might be the last packet.
+ return fragment_size + limits_.last_packet_reduction_len;
+ }
+ return fragment_size;
+ };
+
+ while (payload_size_left >= payload_size_needed()) {
+ RTC_CHECK_GT(fragment.size(), 0);
+ packets_.push({.source_fragment = fragment,
+ .first_fragment = (aggregated_fragments == 0),
+ .last_fragment = false,
+ .aggregated = true,
+ .header = fragment[0]});
+ payload_size_left -= fragment.size();
+ payload_size_left -= fragment_headers_length;
+
+ fragment_headers_length = kH265LengthFieldSize;
+ // If we are going to try to aggregate more fragments into this packet
+ // we need to add the AP NALU header and a length field for the first
+ // NALU of this packet.
+ if (aggregated_fragments == 0) {
+ fragment_headers_length += kH265PayloadHeaderSize + kH265LengthFieldSize;
+ }
+ ++aggregated_fragments;
+
+ // Next fragment.
+ ++fragment_index;
+ if (fragment_index == input_fragments_.size()) {
+ break;
+ }
+ fragment = input_fragments_[fragment_index];
+ }
+ RTC_CHECK_GT(aggregated_fragments, 0);
+ packets_.back().last_fragment = true;
+ return fragment_index;
+}
+
+bool RtpPacketizerH265::NextPacket(RtpPacketToSend* rtp_packet) {
+ RTC_DCHECK(rtp_packet);
+
+ if (packets_.empty()) {
+ return false;
+ }
+
+ PacketUnit packet = packets_.front();
+
+ if (packet.first_fragment && packet.last_fragment) {
+ // Single NAL unit packet. Do not support DONL for single NAL unit packets,
+ // DONL field is not present.
+ size_t bytes_to_send = packet.source_fragment.size();
+ uint8_t* buffer = rtp_packet->AllocatePayload(bytes_to_send);
+ memcpy(buffer, packet.source_fragment.data(), bytes_to_send);
+ packets_.pop();
+ input_fragments_.pop_front();
+ } else if (packet.aggregated) {
+ NextAggregatePacket(rtp_packet);
+ } else {
+ NextFragmentPacket(rtp_packet);
+ }
+ rtp_packet->SetMarker(packets_.empty());
+ --num_packets_left_;
+ return true;
+}
+
+void RtpPacketizerH265::NextAggregatePacket(RtpPacketToSend* rtp_packet) {
+ size_t payload_capacity = rtp_packet->FreeCapacity();
+ RTC_CHECK_GE(payload_capacity, kH265PayloadHeaderSize);
+ uint8_t* buffer = rtp_packet->AllocatePayload(payload_capacity);
+ RTC_CHECK(buffer);
+ PacketUnit* packet = &packets_.front();
+ RTC_CHECK(packet->first_fragment);
+
+ /*
+ +---------------+---------------+
+ |0|1|2|3|4|5|6|7|0|1|2|3|4|5|6|7|
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ |F| Type | LayerId | TID |
+ +-------------+-----------------+
+ */
+ // Refer to section section 4.4.2 for aggregation packets and modify type to
+ // 48 in PayloadHdr for aggregate packet. Do not support DONL for aggregation
+ // packets, DONL field is not present.
+ uint8_t payload_hdr_h = packet->header >> 8;
+ uint8_t payload_hdr_l = packet->header & 0xFF;
+ uint8_t layer_id_h = payload_hdr_h & kH265LayerIDHMask;
+ payload_hdr_h = (payload_hdr_h & kH265TypeMaskN) |
+ (H265::NaluType::kAp << 1) | layer_id_h;
+ buffer[0] = payload_hdr_h;
+ buffer[1] = payload_hdr_l;
+
+ int index = kH265PayloadHeaderSize;
+ bool is_last_fragment = packet->last_fragment;
+ while (packet->aggregated) {
+ // Add NAL unit length field.
+ rtc::ArrayView<const uint8_t> fragment = packet->source_fragment;
+ ByteWriter<uint16_t>::WriteBigEndian(&buffer[index], fragment.size());
+ index += kH265LengthFieldSize;
+ // Add NAL unit.
+ memcpy(&buffer[index], fragment.data(), fragment.size());
+ index += fragment.size();
+ packets_.pop();
+ input_fragments_.pop_front();
+ if (is_last_fragment) {
+ break;
+ }
+ packet = &packets_.front();
+ is_last_fragment = packet->last_fragment;
+ }
+ RTC_CHECK(is_last_fragment);
+ rtp_packet->SetPayloadSize(index);
+}
+
+void RtpPacketizerH265::NextFragmentPacket(RtpPacketToSend* rtp_packet) {
+ PacketUnit* packet = &packets_.front();
+ // NAL unit fragmented over multiple packets (FU).
+ // We do not send original NALU header, so it will be replaced by the
+ // PayloadHdr of the first packet.
+ /*
+ +---------------+---------------+
+ |0|1|2|3|4|5|6|7|0|1|2|3|4|5|6|7|
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ |F| Type | LayerId | TID |
+ +-------------+-----------------+
+ */
+ // Refer to section section 4.4.3 for aggregation packets and modify type to
+ // 49 in PayloadHdr for aggregate packet.
+ uint8_t payload_hdr_h =
+ packet->header >> 8; // 1-bit F, 6-bit type, 1-bit layerID highest-bit
+ uint8_t payload_hdr_l = packet->header & 0xFF;
+ uint8_t layer_id_h = payload_hdr_h & kH265LayerIDHMask;
+ uint8_t fu_header = 0;
+ /*
+ +---------------+
+ |0|1|2|3|4|5|6|7|
+ +-+-+-+-+-+-+-+-+
+ |S|E| FuType |
+ +---------------+
+ */
+ // S bit indicates the start of a fragmented NAL unit.
+ // E bit indicates the end of a fragmented NAL unit.
+ // FuType must be equal to the field type value of the fragmented NAL unit.
+ fu_header |= (packet->first_fragment ? kH265SBitMask : 0);
+ fu_header |= (packet->last_fragment ? kH265EBitMask : 0);
+ uint8_t type = (payload_hdr_h & kH265TypeMask) >> 1;
+ fu_header |= type;
+ // Now update payload_hdr_h with FU type.
+ payload_hdr_h = (payload_hdr_h & kH265TypeMaskN) |
+ (H265::NaluType::kFu << 1) | layer_id_h;
+ rtc::ArrayView<const uint8_t> fragment = packet->source_fragment;
+ uint8_t* buffer = rtp_packet->AllocatePayload(
+ kH265FuHeaderSize + kH265PayloadHeaderSize + fragment.size());
+ RTC_CHECK(buffer);
+ buffer[0] = payload_hdr_h;
+ buffer[1] = payload_hdr_l;
+ buffer[2] = fu_header;
+
+ // Do not support DONL for fragmentation units, DONL field is not present.
+ memcpy(buffer + kH265FuHeaderSize + kH265PayloadHeaderSize, fragment.data(),
+ fragment.size());
+ if (packet->last_fragment) {
+ input_fragments_.pop_front();
+ }
+ packets_.pop();
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtp_packetizer_h265.h b/third_party/libwebrtc/modules/rtp_rtcp/source/rtp_packetizer_h265.h
new file mode 100644
index 0000000000..95442f795c
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtp_packetizer_h265.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2023 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_RTP_RTCP_SOURCE_RTP_PACKETIZER_H265_H_
+#define MODULES_RTP_RTCP_SOURCE_RTP_PACKETIZER_H265_H_
+
+#include <deque>
+#include <queue>
+#include <string>
+
+#include "api/array_view.h"
+#include "modules/rtp_rtcp/source/rtp_format.h"
+#include "modules/rtp_rtcp/source/rtp_packet_to_send.h"
+
+namespace webrtc {
+
+class RtpPacketizerH265 : public RtpPacketizer {
+ public:
+ // Initialize with payload from encoder.
+ // The payload_data must be exactly one encoded H.265 frame.
+ // For H265 we only support tx-mode SRST.
+ RtpPacketizerH265(rtc::ArrayView<const uint8_t> payload,
+ PayloadSizeLimits limits);
+
+ RtpPacketizerH265(const RtpPacketizerH265&) = delete;
+ RtpPacketizerH265& operator=(const RtpPacketizerH265&) = delete;
+
+ ~RtpPacketizerH265() override;
+
+ size_t NumPackets() const override;
+
+ // Get the next payload with H.265 payload header.
+ // Write payload and set marker bit of the `packet`.
+ // Returns true on success or false if there was no payload to packetize.
+ bool NextPacket(RtpPacketToSend* rtp_packet) override;
+
+ private:
+ struct PacketUnit {
+ rtc::ArrayView<const uint8_t> source_fragment;
+ bool first_fragment = false;
+ bool last_fragment = false;
+ bool aggregated = false;
+ uint16_t header = 0;
+ };
+ std::deque<rtc::ArrayView<const uint8_t>> input_fragments_;
+ std::queue<PacketUnit> packets_;
+
+ bool GeneratePackets();
+ bool PacketizeFu(size_t fragment_index);
+ int PacketizeAp(size_t fragment_index);
+
+ void NextAggregatePacket(RtpPacketToSend* rtp_packet);
+ void NextFragmentPacket(RtpPacketToSend* rtp_packet);
+
+ const PayloadSizeLimits limits_;
+ size_t num_packets_left_ = 0;
+};
+} // namespace webrtc
+#endif // MODULES_RTP_RTCP_SOURCE_RTP_PACKETIZER_H265_H_
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtp_packetizer_h265_unittest.cc b/third_party/libwebrtc/modules/rtp_rtcp/source/rtp_packetizer_h265_unittest.cc
new file mode 100644
index 0000000000..cb1de334c0
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtp_packetizer_h265_unittest.cc
@@ -0,0 +1,525 @@
+/*
+ * Copyright (c) 2023 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/rtp_rtcp/source/rtp_packetizer_h265.h"
+
+#include <vector>
+
+#include "common_video/h265/h265_common.h"
+#include "modules/rtp_rtcp/mocks/mock_rtp_rtcp.h"
+#include "modules/rtp_rtcp/source/byte_io.h"
+#include "test/gmock.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+namespace {
+
+using ::testing::Each;
+using ::testing::ElementsAre;
+using ::testing::ElementsAreArray;
+using ::testing::Eq;
+using ::testing::IsEmpty;
+using ::testing::SizeIs;
+
+constexpr RtpPacketToSend::ExtensionManager* kNoExtensions = nullptr;
+constexpr size_t kMaxPayloadSize = 1200;
+constexpr size_t kLengthFieldLength = 2;
+constexpr RtpPacketizer::PayloadSizeLimits kNoLimits;
+
+constexpr size_t kNalHeaderSize = 2;
+constexpr size_t kFuHeaderSize = 3;
+
+constexpr uint8_t kNaluTypeMask = 0x7E;
+
+// Bit masks for FU headers.
+constexpr uint8_t kH265SBit = 0x80;
+constexpr uint8_t kH265EBit = 0x40;
+
+// Creates Buffer that looks like nal unit of given size.
+rtc::Buffer GenerateNalUnit(size_t size) {
+ RTC_CHECK_GT(size, 0);
+ rtc::Buffer buffer(size);
+ // Set some valid header with type TRAIL_R and temporal id
+ buffer[0] = 2;
+ buffer[1] = 2;
+ for (size_t i = 2; i < size; ++i) {
+ buffer[i] = static_cast<uint8_t>(i);
+ }
+ // Last byte shouldn't be 0, or it may be counted as part of next 4-byte start
+ // sequence.
+ buffer[size - 1] |= 0x10;
+ return buffer;
+}
+
+// Create frame consisting of nalus of given size.
+rtc::Buffer CreateFrame(std::initializer_list<size_t> nalu_sizes) {
+ static constexpr int kStartCodeSize = 3;
+ rtc::Buffer frame(absl::c_accumulate(nalu_sizes, size_t{0}) +
+ kStartCodeSize * nalu_sizes.size());
+ size_t offset = 0;
+ for (size_t nalu_size : nalu_sizes) {
+ EXPECT_GE(nalu_size, 1u);
+ // Insert nalu start code
+ frame[offset] = 0;
+ frame[offset + 1] = 0;
+ frame[offset + 2] = 1;
+ // Set some valid header.
+ frame[offset + 3] = 2;
+ // Fill payload avoiding accidental start codes
+ if (nalu_size > 1) {
+ memset(frame.data() + offset + 4, 0x3f, nalu_size - 1);
+ }
+ offset += (kStartCodeSize + nalu_size);
+ }
+ return frame;
+}
+
+// Create frame consisting of given nalus.
+rtc::Buffer CreateFrame(rtc::ArrayView<const rtc::Buffer> nalus) {
+ static constexpr int kStartCodeSize = 3;
+ int frame_size = 0;
+ for (const rtc::Buffer& nalu : nalus) {
+ frame_size += (kStartCodeSize + nalu.size());
+ }
+ rtc::Buffer frame(frame_size);
+ size_t offset = 0;
+ for (const rtc::Buffer& nalu : nalus) {
+ // Insert nalu start code
+ frame[offset] = 0;
+ frame[offset + 1] = 0;
+ frame[offset + 2] = 1;
+ // Copy the nalu unit.
+ memcpy(frame.data() + offset + 3, nalu.data(), nalu.size());
+ offset += (kStartCodeSize + nalu.size());
+ }
+ return frame;
+}
+
+std::vector<RtpPacketToSend> FetchAllPackets(RtpPacketizerH265* packetizer) {
+ std::vector<RtpPacketToSend> result;
+ size_t num_packets = packetizer->NumPackets();
+ result.reserve(num_packets);
+ RtpPacketToSend packet(kNoExtensions);
+ while (packetizer->NextPacket(&packet)) {
+ result.push_back(packet);
+ }
+ EXPECT_THAT(result, SizeIs(num_packets));
+ return result;
+}
+
+// Single nalu tests.
+TEST(RtpPacketizerH265Test, SingleNalu) {
+ const uint8_t frame[] = {0, 0, 1, H265::kIdrWRadl, 0xFF};
+
+ RtpPacketizerH265 packetizer(frame, kNoLimits);
+ std::vector<RtpPacketToSend> packets = FetchAllPackets(&packetizer);
+
+ ASSERT_THAT(packets, SizeIs(1));
+ EXPECT_THAT(packets[0].payload(), ElementsAre(H265::kIdrWRadl, 0xFF));
+}
+
+TEST(RtpPacketizerH265Test, SingleNaluTwoPackets) {
+ RtpPacketizer::PayloadSizeLimits limits;
+ limits.max_payload_len = kMaxPayloadSize;
+ rtc::Buffer nalus[] = {GenerateNalUnit(kMaxPayloadSize),
+ GenerateNalUnit(100)};
+ rtc::Buffer frame = CreateFrame(nalus);
+
+ RtpPacketizerH265 packetizer(frame, limits);
+ std::vector<RtpPacketToSend> packets = FetchAllPackets(&packetizer);
+
+ ASSERT_THAT(packets, SizeIs(2));
+ EXPECT_THAT(packets[0].payload(), ElementsAreArray(nalus[0]));
+ EXPECT_THAT(packets[1].payload(), ElementsAreArray(nalus[1]));
+}
+
+TEST(RtpPacketizerH265Test,
+ SingleNaluFirstPacketReductionAppliesOnlyToFirstFragment) {
+ RtpPacketizer::PayloadSizeLimits limits;
+ limits.max_payload_len = 200;
+ limits.first_packet_reduction_len = 5;
+ rtc::Buffer nalus[] = {GenerateNalUnit(/*size=*/195),
+ GenerateNalUnit(/*size=*/200),
+ GenerateNalUnit(/*size=*/200)};
+ rtc::Buffer frame = CreateFrame(nalus);
+
+ RtpPacketizerH265 packetizer(frame, limits);
+ std::vector<RtpPacketToSend> packets = FetchAllPackets(&packetizer);
+
+ ASSERT_THAT(packets, SizeIs(3));
+ EXPECT_THAT(packets[0].payload(), ElementsAreArray(nalus[0]));
+ EXPECT_THAT(packets[1].payload(), ElementsAreArray(nalus[1]));
+ EXPECT_THAT(packets[2].payload(), ElementsAreArray(nalus[2]));
+}
+
+TEST(RtpPacketizerH265Test,
+ SingleNaluLastPacketReductionAppliesOnlyToLastFragment) {
+ RtpPacketizer::PayloadSizeLimits limits;
+ limits.max_payload_len = 200;
+ limits.last_packet_reduction_len = 5;
+ rtc::Buffer nalus[] = {GenerateNalUnit(/*size=*/200),
+ GenerateNalUnit(/*size=*/200),
+ GenerateNalUnit(/*size=*/195)};
+ rtc::Buffer frame = CreateFrame(nalus);
+
+ RtpPacketizerH265 packetizer(frame, limits);
+ std::vector<RtpPacketToSend> packets = FetchAllPackets(&packetizer);
+
+ ASSERT_THAT(packets, SizeIs(3));
+ EXPECT_THAT(packets[0].payload(), ElementsAreArray(nalus[0]));
+ EXPECT_THAT(packets[1].payload(), ElementsAreArray(nalus[1]));
+ EXPECT_THAT(packets[2].payload(), ElementsAreArray(nalus[2]));
+}
+
+TEST(RtpPacketizerH265Test,
+ SingleNaluFirstAndLastPacketReductionSumsForSinglePacket) {
+ RtpPacketizer::PayloadSizeLimits limits;
+ limits.max_payload_len = 200;
+ limits.first_packet_reduction_len = 20;
+ limits.last_packet_reduction_len = 30;
+ rtc::Buffer frame = CreateFrame({150});
+
+ RtpPacketizerH265 packetizer(frame, limits);
+ std::vector<RtpPacketToSend> packets = FetchAllPackets(&packetizer);
+
+ EXPECT_THAT(packets, SizeIs(1));
+}
+
+// Aggregation tests.
+TEST(RtpPacketizerH265Test, ApRespectsNoPacketReduction) {
+ rtc::Buffer nalus[] = {GenerateNalUnit(/*size=*/2),
+ GenerateNalUnit(/*size=*/2),
+ GenerateNalUnit(/*size=*/0x123)};
+ rtc::Buffer frame = CreateFrame(nalus);
+
+ RtpPacketizerH265 packetizer(frame, kNoLimits);
+ std::vector<RtpPacketToSend> packets = FetchAllPackets(&packetizer);
+
+ ASSERT_THAT(packets, SizeIs(1));
+ auto payload = packets[0].payload();
+ int type = H265::ParseNaluType(payload[0]);
+ EXPECT_EQ(payload.size(),
+ kNalHeaderSize + 3 * kLengthFieldLength + 2 + 2 + 0x123);
+
+ EXPECT_EQ(type, H265::NaluType::kAp);
+ payload = payload.subview(kNalHeaderSize);
+ // 1st fragment.
+ EXPECT_THAT(payload.subview(0, kLengthFieldLength),
+ ElementsAre(0, 2)); // Size.
+ EXPECT_THAT(payload.subview(kLengthFieldLength, 2),
+ ElementsAreArray(nalus[0]));
+ payload = payload.subview(kLengthFieldLength + 2);
+ // 2nd fragment.
+ EXPECT_THAT(payload.subview(0, kLengthFieldLength),
+ ElementsAre(0, 2)); // Size.
+ EXPECT_THAT(payload.subview(kLengthFieldLength, 2),
+ ElementsAreArray(nalus[1]));
+ payload = payload.subview(kLengthFieldLength + 2);
+ // 3rd fragment.
+ EXPECT_THAT(payload.subview(0, kLengthFieldLength),
+ ElementsAre(0x1, 0x23)); // Size.
+ EXPECT_THAT(payload.subview(kLengthFieldLength), ElementsAreArray(nalus[2]));
+}
+
+TEST(RtpPacketizerH265Test, ApRespectsFirstPacketReduction) {
+ RtpPacketizer::PayloadSizeLimits limits;
+ limits.max_payload_len = 1000;
+ limits.first_packet_reduction_len = 100;
+ const size_t kFirstFragmentSize =
+ limits.max_payload_len - limits.first_packet_reduction_len;
+ rtc::Buffer nalus[] = {GenerateNalUnit(/*size=*/kFirstFragmentSize),
+ GenerateNalUnit(/*size=*/2),
+ GenerateNalUnit(/*size=*/2)};
+ rtc::Buffer frame = CreateFrame(nalus);
+
+ RtpPacketizerH265 packetizer(frame, limits);
+ std::vector<RtpPacketToSend> packets = FetchAllPackets(&packetizer);
+
+ ASSERT_THAT(packets, SizeIs(2));
+ // Expect 1st packet is single nalu.
+ EXPECT_THAT(packets[0].payload(), ElementsAreArray(nalus[0]));
+ // Expect 2nd packet is aggregate of last two fragments.
+ // The size of H265 nal_unit_header is 2 bytes, according to 7.3.1.2
+ // in H265 spec. Aggregation packet type is 48, and nuh_temporal_id_plus1
+ // is 2, so the nal_unit_header should be "01100000 00000010",
+ // which is 96 and 2.
+ EXPECT_THAT(packets[1].payload(),
+ ElementsAre(96, 2, //
+ 0, 2, nalus[1][0], nalus[1][1], //
+ 0, 2, nalus[2][0], nalus[2][1]));
+}
+
+TEST(RtpPacketizerH265Test, ApRespectsLastPacketReduction) {
+ RtpPacketizer::PayloadSizeLimits limits;
+ limits.max_payload_len = 1000;
+ limits.last_packet_reduction_len = 100;
+ const size_t kLastFragmentSize =
+ limits.max_payload_len - limits.last_packet_reduction_len;
+ rtc::Buffer nalus[] = {GenerateNalUnit(/*size=*/2),
+ GenerateNalUnit(/*size=*/2),
+ GenerateNalUnit(/*size=*/kLastFragmentSize)};
+ rtc::Buffer frame = CreateFrame(nalus);
+
+ RtpPacketizerH265 packetizer(frame, limits);
+ std::vector<RtpPacketToSend> packets = FetchAllPackets(&packetizer);
+
+ ASSERT_THAT(packets, SizeIs(2));
+ // Expect 1st packet is aggregate of 1st two fragments.
+ EXPECT_THAT(packets[0].payload(),
+ ElementsAre(96, 2, //
+ 0, 2, nalus[0][0], nalus[0][1], //
+ 0, 2, nalus[1][0], nalus[1][1]));
+ // Expect 2nd packet is single nalu.
+ EXPECT_THAT(packets[1].payload(), ElementsAreArray(nalus[2]));
+}
+
+TEST(RtpPacketizerH265Test, TooSmallForApHeaders) {
+ RtpPacketizer::PayloadSizeLimits limits;
+ limits.max_payload_len = 1000;
+ const size_t kLastFragmentSize =
+ limits.max_payload_len - 3 * kLengthFieldLength - 4;
+ rtc::Buffer nalus[] = {GenerateNalUnit(/*size=*/2),
+ GenerateNalUnit(/*size=*/2),
+ GenerateNalUnit(/*size=*/kLastFragmentSize)};
+ rtc::Buffer frame = CreateFrame(nalus);
+
+ RtpPacketizerH265 packetizer(frame, limits);
+ std::vector<RtpPacketToSend> packets = FetchAllPackets(&packetizer);
+
+ ASSERT_THAT(packets, SizeIs(2));
+ // Expect 1st packet is aggregate of 1st two fragments.
+ EXPECT_THAT(packets[0].payload(),
+ ElementsAre(96, 2, //
+ 0, 2, nalus[0][0], nalus[0][1], //
+ 0, 2, nalus[1][0], nalus[1][1]));
+ // Expect 2nd packet is single nalu.
+ EXPECT_THAT(packets[1].payload(), ElementsAreArray(nalus[2]));
+}
+
+TEST(RtpPacketizerH265Test, LastFragmentFitsInSingleButNotLastPacket) {
+ RtpPacketizer::PayloadSizeLimits limits;
+ limits.max_payload_len = 1178;
+ limits.first_packet_reduction_len = 0;
+ limits.last_packet_reduction_len = 20;
+ limits.single_packet_reduction_len = 20;
+ // Actual sizes, which triggered this bug.
+ rtc::Buffer frame = CreateFrame({20, 8, 18, 1161});
+
+ RtpPacketizerH265 packetizer(frame, limits);
+ std::vector<RtpPacketToSend> packets = FetchAllPackets(&packetizer);
+
+ // Last packet has to be of correct size.
+ // Incorrect implementation might miss this constraint and not split the last
+ // fragment in two packets.
+ EXPECT_LE(static_cast<int>(packets.back().payload_size()),
+ limits.max_payload_len - limits.last_packet_reduction_len);
+}
+
+// Splits frame with payload size `frame_payload_size` without fragmentation,
+// Returns sizes of the payloads excluding FU headers.
+std::vector<int> TestFu(size_t frame_payload_size,
+ const RtpPacketizer::PayloadSizeLimits& limits) {
+ rtc::Buffer nalu[] = {GenerateNalUnit(kNalHeaderSize + frame_payload_size)};
+ rtc::Buffer frame = CreateFrame(nalu);
+
+ RtpPacketizerH265 packetizer(frame, limits);
+ std::vector<RtpPacketToSend> packets = FetchAllPackets(&packetizer);
+
+ EXPECT_GE(packets.size(), 2u); // Single packet indicates it is not FU.
+ std::vector<uint16_t> fu_header;
+ std::vector<int> payload_sizes;
+
+ for (const RtpPacketToSend& packet : packets) {
+ auto payload = packet.payload();
+ EXPECT_GT(payload.size(), kFuHeaderSize);
+ // FU header is after the 2-bytes size PayloadHdr according to 4.4.3 in spec
+ fu_header.push_back(payload[2]);
+ payload_sizes.push_back(payload.size() - kFuHeaderSize);
+ }
+
+ EXPECT_TRUE(fu_header.front() & kH265SBit);
+ EXPECT_TRUE(fu_header.back() & kH265EBit);
+ // Clear S and E bits before testing all are duplicating same original header.
+ fu_header.front() &= ~kH265SBit;
+ fu_header.back() &= ~kH265EBit;
+ uint8_t nalu_type = (nalu[0][0] & kNaluTypeMask) >> 1;
+ EXPECT_THAT(fu_header, Each(Eq(nalu_type)));
+
+ return payload_sizes;
+}
+
+// Fragmentation tests.
+TEST(RtpPacketizerH265Test, FuOddSize) {
+ RtpPacketizer::PayloadSizeLimits limits;
+ limits.max_payload_len = 1200;
+ EXPECT_THAT(TestFu(1200, limits), ElementsAre(600, 600));
+}
+
+TEST(RtpPacketizerH265Test, FuWithFirstPacketReduction) {
+ RtpPacketizer::PayloadSizeLimits limits;
+ limits.max_payload_len = 1200;
+ limits.first_packet_reduction_len = 4;
+ limits.single_packet_reduction_len = 4;
+ EXPECT_THAT(TestFu(1198, limits), ElementsAre(597, 601));
+}
+
+TEST(RtpPacketizerH265Test, FuWithLastPacketReduction) {
+ RtpPacketizer::PayloadSizeLimits limits;
+ limits.max_payload_len = 1200;
+ limits.last_packet_reduction_len = 4;
+ limits.single_packet_reduction_len = 4;
+ EXPECT_THAT(TestFu(1198, limits), ElementsAre(601, 597));
+}
+
+TEST(RtpPacketizerH265Test, FuWithSinglePacketReduction) {
+ RtpPacketizer::PayloadSizeLimits limits;
+ limits.max_payload_len = 1199;
+ limits.single_packet_reduction_len = 200;
+ EXPECT_THAT(TestFu(1000, limits), ElementsAre(500, 500));
+}
+
+TEST(RtpPacketizerH265Test, FuEvenSize) {
+ RtpPacketizer::PayloadSizeLimits limits;
+ limits.max_payload_len = 1200;
+ EXPECT_THAT(TestFu(1201, limits), ElementsAre(600, 601));
+}
+
+TEST(RtpPacketizerH265Test, FuRounding) {
+ RtpPacketizer::PayloadSizeLimits limits;
+ limits.max_payload_len = 1448;
+ EXPECT_THAT(TestFu(10123, limits),
+ ElementsAre(1265, 1265, 1265, 1265, 1265, 1266, 1266, 1266));
+}
+
+TEST(RtpPacketizerH265Test, FuBig) {
+ RtpPacketizer::PayloadSizeLimits limits;
+ limits.max_payload_len = 1200;
+ // Generate 10 full sized packets, leave room for FU headers.
+ EXPECT_THAT(
+ TestFu(10 * (1200 - kFuHeaderSize), limits),
+ ElementsAre(1197, 1197, 1197, 1197, 1197, 1197, 1197, 1197, 1197, 1197));
+}
+
+struct PacketInfo {
+ bool first_fragment = false;
+ bool last_fragment = false;
+ bool aggregated = false;
+ int nalu_index = 0;
+ int nalu_number = 0;
+ int payload_size = 0;
+ int start_offset = 0;
+};
+
+struct MixedApFuTestParams {
+ std::vector<int> nalus;
+ int expect_packetsSize = 0;
+ std::vector<PacketInfo> expected_packets;
+};
+
+class RtpPacketizerH265ParametrizedTest
+ : public ::testing::TestWithParam<MixedApFuTestParams> {};
+
+// Fragmentation + aggregation mixed testing.
+TEST_P(RtpPacketizerH265ParametrizedTest, MixedApFu) {
+ RtpPacketizer::PayloadSizeLimits limits;
+ const MixedApFuTestParams params = GetParam();
+ limits.max_payload_len = 100;
+ std::vector<rtc::Buffer> nalus;
+ nalus.reserve(params.nalus.size());
+
+ // Generate nalus according to size specified in paramters
+ for (size_t index = 0; index < params.nalus.size(); index++) {
+ nalus.push_back(GenerateNalUnit(params.nalus[index]));
+ }
+ rtc::Buffer frame = CreateFrame(nalus);
+
+ RtpPacketizerH265 packetizer(frame, limits);
+ std::vector<RtpPacketToSend> packets = FetchAllPackets(&packetizer);
+
+ ASSERT_THAT(packets, SizeIs(params.expect_packetsSize));
+ for (int i = 0; i < params.expect_packetsSize; i++) {
+ PacketInfo expected_packet = params.expected_packets[i];
+ if (expected_packet.aggregated) {
+ int type = H265::ParseNaluType(packets[i].payload()[0]);
+ EXPECT_THAT(type, H265::NaluType::kAp);
+ auto payload = packets[i].payload().subview(kNalHeaderSize);
+ int offset = 0;
+ // Generated AP packet header and payload align
+ for (int j = expected_packet.nalu_index; j < expected_packet.nalu_number;
+ j++) {
+ EXPECT_THAT(payload.subview(0, kLengthFieldLength),
+ ElementsAre(0, nalus[j].size()));
+ EXPECT_THAT(
+ payload.subview(offset + kLengthFieldLength, nalus[j].size()),
+ ElementsAreArray(nalus[j]));
+ offset += kLengthFieldLength + nalus[j].size();
+ }
+ } else {
+ uint8_t fu_header = 0;
+ fu_header |= (expected_packet.first_fragment ? kH265SBit : 0);
+ fu_header |= (expected_packet.last_fragment ? kH265EBit : 0);
+ fu_header |= H265::NaluType::kTrailR;
+ EXPECT_THAT(packets[i].payload().subview(0, kFuHeaderSize),
+ ElementsAre(98, 2, fu_header));
+ EXPECT_THAT(
+ packets[i].payload().subview(kFuHeaderSize),
+ ElementsAreArray(nalus[expected_packet.nalu_index].data() +
+ kNalHeaderSize + expected_packet.start_offset,
+ expected_packet.payload_size));
+ }
+ }
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ RtpPacketizerH265Test,
+ RtpPacketizerH265ParametrizedTest,
+ testing::Values(
+ // FU + AP + FU.
+ // GenerateNalUnit will include 2 bytes nalu header, for FU packet split
+ // calculation, this 2-byte nalu header length should be excluded.
+ MixedApFuTestParams{.nalus = {140, 20, 20, 160},
+ .expect_packetsSize = 5,
+ .expected_packets = {{.first_fragment = true,
+ .nalu_index = 0,
+ .payload_size = 69,
+ .start_offset = 0},
+ {.last_fragment = true,
+ .nalu_index = 0,
+ .payload_size = 69,
+ .start_offset = 69},
+ {.aggregated = true,
+ .nalu_index = 1,
+ .nalu_number = 2},
+ {.first_fragment = true,
+ .nalu_index = 3,
+ .payload_size = 79,
+ .start_offset = 0},
+ {.last_fragment = true,
+ .nalu_index = 3,
+ .payload_size = 79,
+ .start_offset = 79}}},
+ // AP + FU + AP
+ MixedApFuTestParams{
+ .nalus = {20, 20, 160, 30, 30},
+ .expect_packetsSize = 4,
+ .expected_packets = {
+ {.aggregated = true, .nalu_index = 0, .nalu_number = 2},
+ {.first_fragment = true,
+ .nalu_index = 2,
+ .payload_size = 79,
+ .start_offset = 0},
+ {.last_fragment = true,
+ .nalu_index = 2,
+ .payload_size = 79,
+ .start_offset = 79},
+ {.aggregated = true, .nalu_index = 3, .nalu_number = 2}}}));
+
+} // namespace
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtp_rtcp_impl2.cc b/third_party/libwebrtc/modules/rtp_rtcp/source/rtp_rtcp_impl2.cc
index ff482b39b6..31e8b71117 100644
--- a/third_party/libwebrtc/modules/rtp_rtcp/source/rtp_rtcp_impl2.cc
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtp_rtcp_impl2.cc
@@ -53,8 +53,8 @@ RTCPSender::Configuration AddRtcpSendEvaluationCallback(
RtpPacketHistory::PaddingMode GetPaddingMode(
const FieldTrialsView* field_trials) {
- if (field_trials &&
- field_trials->IsEnabled("WebRTC-PaddingMode-RecentLargePacket")) {
+ if (!field_trials ||
+ !field_trials->IsDisabled("WebRTC-PaddingMode-RecentLargePacket")) {
return RtpPacketHistory::PaddingMode::kRecentLargePacket;
}
return RtpPacketHistory::PaddingMode::kPriority;
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtp_sender_audio.cc b/third_party/libwebrtc/modules/rtp_rtcp/source/rtp_sender_audio.cc
index b826c30e07..9d2258dc66 100644
--- a/third_party/libwebrtc/modules/rtp_rtcp/source/rtp_sender_audio.cc
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtp_sender_audio.cc
@@ -254,7 +254,8 @@ bool RTPSenderAudio::SendAudio(const RtpAudioFrame& frame) {
return false;
}
- std::unique_ptr<RtpPacketToSend> packet = rtp_sender_->AllocatePacket();
+ std::unique_ptr<RtpPacketToSend> packet =
+ rtp_sender_->AllocatePacket(frame.csrcs);
packet->SetMarker(MarkerBit(frame.type, frame.payload_id));
packet->SetPayloadType(frame.payload_id);
packet->SetTimestamp(frame.rtp_timestamp);
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtp_sender_audio.h b/third_party/libwebrtc/modules/rtp_rtcp/source/rtp_sender_audio.h
index 662f908216..83a2cb211f 100644
--- a/third_party/libwebrtc/modules/rtp_rtcp/source/rtp_sender_audio.h
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtp_sender_audio.h
@@ -61,6 +61,9 @@ class RTPSenderAudio {
// header-extension-for-audio-level-indication.
// Valid range is [0,127]. Actual value is negative.
absl::optional<int> audio_level_dbov;
+
+ // Contributing sources list.
+ rtc::ArrayView<const uint32_t> csrcs;
};
bool SendAudio(const RtpAudioFrame& frame);
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtp_sender_audio_unittest.cc b/third_party/libwebrtc/modules/rtp_rtcp/source/rtp_sender_audio_unittest.cc
index 0db610c149..724cd3a5e0 100644
--- a/third_party/libwebrtc/modules/rtp_rtcp/source/rtp_sender_audio_unittest.cc
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtp_sender_audio_unittest.cc
@@ -222,4 +222,19 @@ TEST_F(RtpSenderAudioTest, CheckMarkerBitForTelephoneEvents) {
EXPECT_FALSE(transport_.last_sent_packet().Marker());
}
+TEST_F(RtpSenderAudioTest, SendsCsrcs) {
+ const char payload_name[] = "audio";
+ const uint8_t payload_type = 127;
+ ASSERT_EQ(0, rtp_sender_audio_->RegisterAudioPayload(
+ payload_name, payload_type, 48000, 0, 1500));
+ uint8_t payload[] = {47, 11, 32, 93, 89};
+
+ std::vector<uint32_t> csrcs({123, 456, 789});
+
+ ASSERT_TRUE(rtp_sender_audio_->SendAudio(
+ {.payload = payload, .payload_id = payload_type, .csrcs = csrcs}));
+
+ EXPECT_EQ(transport_.last_sent_packet().Csrcs(), csrcs);
+}
+
} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate.cc b/third_party/libwebrtc/modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate.cc
index 9d7c58d19a..ae9eb6b4bd 100644
--- a/third_party/libwebrtc/modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate.cc
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate.cc
@@ -19,10 +19,17 @@
#include "modules/rtp_rtcp/source/rtp_descriptor_authentication.h"
#include "rtc_base/checks.h"
#include "rtc_base/event.h"
+#include "rtc_base/logging.h"
namespace webrtc {
namespace {
+// Using a reasonable default of 10ms for the retransmission delay for frames
+// not coming from this sender's encoder. This is usually taken from an
+// estimate of the RTT of the link,so 10ms should be a reasonable estimate for
+// frames being re-transmitted to a peer, probably on the same network.
+const TimeDelta kDefaultRetransmissionsTime = TimeDelta::Millis(10);
+
class TransformableVideoSenderFrame : public TransformableVideoFrameInterface {
public:
TransformableVideoSenderFrame(const EncodedImage& encoded_image,
@@ -155,6 +162,17 @@ bool RTPSenderVideoFrameTransformerDelegate::TransformFrame(
const EncodedImage& encoded_image,
RTPVideoHeader video_header,
TimeDelta expected_retransmission_time) {
+ {
+ MutexLock lock(&sender_lock_);
+ if (short_circuit_) {
+ sender_->SendVideo(payload_type, codec_type, rtp_timestamp,
+ encoded_image.CaptureTime(),
+ *encoded_image.GetEncodedData(), encoded_image.size(),
+ video_header, expected_retransmission_time,
+ /*csrcs=*/{});
+ return true;
+ }
+ }
frame_transformer_->Transform(std::make_unique<TransformableVideoSenderFrame>(
encoded_image, video_header, payload_type, codec_type, rtp_timestamp,
expected_retransmission_time, ssrc_,
@@ -177,6 +195,11 @@ void RTPSenderVideoFrameTransformerDelegate::OnTransformedFrame(
});
}
+void RTPSenderVideoFrameTransformerDelegate::StartShortCircuiting() {
+ MutexLock lock(&sender_lock_);
+ short_circuit_ = true;
+}
+
void RTPSenderVideoFrameTransformerDelegate::SendVideo(
std::unique_ptr<TransformableFrameInterface> transformed_frame) const {
RTC_DCHECK_RUN_ON(transformation_queue_.get());
@@ -200,15 +223,17 @@ void RTPSenderVideoFrameTransformerDelegate::SendVideo(
auto* transformed_video_frame =
static_cast<TransformableVideoFrameInterface*>(transformed_frame.get());
VideoFrameMetadata metadata = transformed_video_frame->Metadata();
- sender_->SendVideo(
- transformed_video_frame->GetPayloadType(), metadata.GetCodec(),
- transformed_video_frame->GetTimestamp(),
- /*capture_time=*/Timestamp::MinusInfinity(),
- transformed_video_frame->GetData(),
- transformed_video_frame->GetData().size(),
- RTPVideoHeader::FromMetadata(metadata),
- /*expected_retransmission_time=*/TimeDelta::PlusInfinity(),
- metadata.GetCsrcs());
+ // TODO(bugs.webrtc.org/14708): Use an actual RTT estimate for the
+ // retransmission time instead of a const default, in the same way as a
+ // locally encoded frame.
+ sender_->SendVideo(transformed_video_frame->GetPayloadType(),
+ metadata.GetCodec(),
+ transformed_video_frame->GetTimestamp(),
+ /*capture_time=*/Timestamp::MinusInfinity(),
+ transformed_video_frame->GetData(),
+ transformed_video_frame->GetData().size(),
+ RTPVideoHeader::FromMetadata(metadata),
+ kDefaultRetransmissionsTime, metadata.GetCsrcs());
}
}
@@ -253,13 +278,14 @@ std::unique_ptr<TransformableVideoFrameInterface> CloneSenderVideoFrame(
? VideoFrameType::kVideoFrameKey
: VideoFrameType::kVideoFrameDelta;
// TODO(bugs.webrtc.org/14708): Fill in other EncodedImage parameters
-
+ // TODO(bugs.webrtc.org/14708): Use an actual RTT estimate for the
+ // retransmission time instead of a const default, in the same way as a
+ // locally encoded frame.
VideoFrameMetadata metadata = original->Metadata();
RTPVideoHeader new_header = RTPVideoHeader::FromMetadata(metadata);
return std::make_unique<TransformableVideoSenderFrame>(
encoded_image, new_header, original->GetPayloadType(), new_header.codec,
- original->GetTimestamp(),
- /*expected_retransmission_time=*/TimeDelta::PlusInfinity(),
+ original->GetTimestamp(), kDefaultRetransmissionsTime,
original->GetSsrc(), metadata.GetCsrcs(), original->GetRid());
}
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate.h b/third_party/libwebrtc/modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate.h
index 3379ead364..1f70a23ccc 100644
--- a/third_party/libwebrtc/modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate.h
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate.h
@@ -76,6 +76,8 @@ class RTPSenderVideoFrameTransformerDelegate : public TransformedFrameCallback {
void OnTransformedFrame(
std::unique_ptr<TransformableFrameInterface> frame) override;
+ void StartShortCircuiting() override;
+
// Delegates the call to RTPSendVideo::SendVideo on the `encoder_queue_`.
void SendVideo(std::unique_ptr<TransformableFrameInterface> frame) const
RTC_RUN_ON(transformation_queue_);
@@ -109,6 +111,7 @@ class RTPSenderVideoFrameTransformerDelegate : public TransformedFrameCallback {
// Used when the encoded frames arrives without a current task queue. This can
// happen if a hardware encoder was used.
std::unique_ptr<TaskQueueBase, TaskQueueDeleter> transformation_queue_;
+ bool short_circuit_ RTC_GUARDED_BY(sender_lock_) = false;
};
// Method to support cloning a Sender frame from another frame
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate_unittest.cc b/third_party/libwebrtc/modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate_unittest.cc
index a376be77b4..6790fc3a71 100644
--- a/third_party/libwebrtc/modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate_unittest.cc
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate_unittest.cc
@@ -83,7 +83,7 @@ class RtpSenderVideoFrameTransformerDelegateTest : public ::testing::Test {
delegate->TransformFrame(
/*payload_type=*/1, VideoCodecType::kVideoCodecVP8, /*rtp_timestamp=*/2,
encoded_image, RTPVideoHeader::FromMetadata(metadata),
- /*expected_retransmission_time=*/TimeDelta::PlusInfinity());
+ /*expected_retransmission_time=*/TimeDelta::Millis(10));
return frame;
}
@@ -123,7 +123,7 @@ TEST_F(RtpSenderVideoFrameTransformerDelegateTest,
delegate->TransformFrame(
/*payload_type=*/1, VideoCodecType::kVideoCodecVP8, /*rtp_timestamp=*/2,
encoded_image, RTPVideoHeader(),
- /*expected_retransmission_time=*/TimeDelta::PlusInfinity());
+ /*expected_retransmission_time=*/TimeDelta::Millis(10));
}
TEST_F(RtpSenderVideoFrameTransformerDelegateTest,
@@ -260,7 +260,7 @@ TEST_F(RtpSenderVideoFrameTransformerDelegateTest,
test_sender_,
SendVideo(payload_type, absl::make_optional(kVideoCodecVP8), timestamp,
/*capture_time=*/Timestamp::MinusInfinity(), buffer, _, _,
- /*expected_retransmission_time=*/TimeDelta::PlusInfinity(),
+ /*expected_retransmission_time=*/TimeDelta::Millis(10),
frame_csrcs))
.WillOnce(WithoutArgs([&] {
event.Set();
@@ -289,5 +289,29 @@ TEST_F(RtpSenderVideoFrameTransformerDelegateTest, SettingRTPTimestamp) {
EXPECT_EQ(video_frame.GetTimestamp(), rtp_timestamp);
}
+TEST_F(RtpSenderVideoFrameTransformerDelegateTest,
+ ShortCircuitingSkipsTransform) {
+ auto delegate = rtc::make_ref_counted<RTPSenderVideoFrameTransformerDelegate>(
+ &test_sender_, frame_transformer_,
+ /*ssrc=*/1111, time_controller_.CreateTaskQueueFactory().get());
+ EXPECT_CALL(*frame_transformer_,
+ RegisterTransformedFrameSinkCallback(_, 1111));
+ delegate->Init();
+
+ delegate->StartShortCircuiting();
+
+ // Will not call the actual transformer.
+ EXPECT_CALL(*frame_transformer_, Transform).Times(0);
+ // Will pass the frame straight to the reciever.
+ EXPECT_CALL(test_sender_, SendVideo);
+
+ EncodedImage encoded_image;
+ encoded_image.SetEncodedData(EncodedImageBuffer::Create(1));
+ delegate->TransformFrame(
+ /*payload_type=*/1, VideoCodecType::kVideoCodecVP8, /*rtp_timestamp=*/2,
+ encoded_image, RTPVideoHeader(),
+ /*expected_retransmission_time=*/TimeDelta::Millis(10));
+}
+
} // namespace
} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtp_video_stream_receiver_frame_transformer_delegate.cc b/third_party/libwebrtc/modules/rtp_rtcp/source/rtp_video_stream_receiver_frame_transformer_delegate.cc
index 94c9249e16..7af945c623 100644
--- a/third_party/libwebrtc/modules/rtp_rtcp/source/rtp_video_stream_receiver_frame_transformer_delegate.cc
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtp_video_stream_receiver_frame_transformer_delegate.cc
@@ -17,6 +17,7 @@
#include "absl/memory/memory.h"
#include "modules/rtp_rtcp/source/rtp_descriptor_authentication.h"
#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
#include "rtc_base/thread.h"
namespace webrtc {
@@ -123,9 +124,14 @@ void RtpVideoStreamReceiverFrameTransformerDelegate::Reset() {
void RtpVideoStreamReceiverFrameTransformerDelegate::TransformFrame(
std::unique_ptr<RtpFrameObject> frame) {
RTC_DCHECK_RUN_ON(&network_sequence_checker_);
- frame_transformer_->Transform(
- std::make_unique<TransformableVideoReceiverFrame>(std::move(frame), ssrc_,
- receiver_));
+ if (short_circuit_) {
+ // Just pass the frame straight back.
+ receiver_->ManageFrame(std::move(frame));
+ } else {
+ frame_transformer_->Transform(
+ std::make_unique<TransformableVideoReceiverFrame>(std::move(frame),
+ ssrc_, receiver_));
+ }
}
void RtpVideoStreamReceiverFrameTransformerDelegate::OnTransformedFrame(
@@ -138,6 +144,20 @@ void RtpVideoStreamReceiverFrameTransformerDelegate::OnTransformedFrame(
});
}
+void RtpVideoStreamReceiverFrameTransformerDelegate::StartShortCircuiting() {
+ rtc::scoped_refptr<RtpVideoStreamReceiverFrameTransformerDelegate> delegate(
+ this);
+ network_thread_->PostTask([delegate = std::move(delegate)]() mutable {
+ delegate->StartShortCircuitingOnNetworkSequence();
+ });
+}
+
+void RtpVideoStreamReceiverFrameTransformerDelegate::
+ StartShortCircuitingOnNetworkSequence() {
+ RTC_DCHECK_RUN_ON(&network_sequence_checker_);
+ short_circuit_ = true;
+}
+
void RtpVideoStreamReceiverFrameTransformerDelegate::ManageFrame(
std::unique_ptr<TransformableFrameInterface> frame) {
RTC_DCHECK_RUN_ON(&network_sequence_checker_);
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtp_video_stream_receiver_frame_transformer_delegate.h b/third_party/libwebrtc/modules/rtp_rtcp/source/rtp_video_stream_receiver_frame_transformer_delegate.h
index 20f9a5caa9..02f2e53923 100644
--- a/third_party/libwebrtc/modules/rtp_rtcp/source/rtp_video_stream_receiver_frame_transformer_delegate.h
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtp_video_stream_receiver_frame_transformer_delegate.h
@@ -54,6 +54,8 @@ class RtpVideoStreamReceiverFrameTransformerDelegate
void OnTransformedFrame(
std::unique_ptr<TransformableFrameInterface> frame) override;
+ void StartShortCircuiting() override;
+
// Delegates the call to RtpVideoFrameReceiver::ManageFrame on the
// `network_thread_`.
void ManageFrame(std::unique_ptr<TransformableFrameInterface> frame);
@@ -62,6 +64,8 @@ class RtpVideoStreamReceiverFrameTransformerDelegate
~RtpVideoStreamReceiverFrameTransformerDelegate() override = default;
private:
+ void StartShortCircuitingOnNetworkSequence();
+
RTC_NO_UNIQUE_ADDRESS SequenceChecker network_sequence_checker_;
RtpVideoFrameReceiver* receiver_ RTC_GUARDED_BY(network_sequence_checker_);
rtc::scoped_refptr<FrameTransformerInterface> frame_transformer_
@@ -69,6 +73,7 @@ class RtpVideoStreamReceiverFrameTransformerDelegate
TaskQueueBase* const network_thread_;
const uint32_t ssrc_;
Clock* const clock_;
+ bool short_circuit_ RTC_GUARDED_BY(network_sequence_checker_) = false;
};
} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtp_video_stream_receiver_frame_transformer_delegate_unittest.cc b/third_party/libwebrtc/modules/rtp_rtcp/source/rtp_video_stream_receiver_frame_transformer_delegate_unittest.cc
index f403c91a74..cf3062610f 100644
--- a/third_party/libwebrtc/modules/rtp_rtcp/source/rtp_video_stream_receiver_frame_transformer_delegate_unittest.cc
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtp_video_stream_receiver_frame_transformer_delegate_unittest.cc
@@ -349,5 +349,28 @@ TEST(RtpVideoStreamReceiverFrameTransformerDelegateTest,
rtc::ThreadManager::ProcessAllMessageQueuesForTesting();
}
+TEST(RtpVideoStreamReceiverFrameTransformerDelegateTest,
+ ShortCircuitingSkipsTransform) {
+ rtc::AutoThread main_thread_;
+ TestRtpVideoFrameReceiver receiver;
+ auto mock_frame_transformer =
+ rtc::make_ref_counted<NiceMock<MockFrameTransformer>>();
+ SimulatedClock clock(0);
+ auto delegate =
+ rtc::make_ref_counted<RtpVideoStreamReceiverFrameTransformerDelegate>(
+ &receiver, &clock, mock_frame_transformer, rtc::Thread::Current(),
+ 1111);
+ delegate->Init();
+
+ delegate->StartShortCircuiting();
+ rtc::ThreadManager::ProcessAllMessageQueuesForTesting();
+
+ // Will not call the actual transformer.
+ EXPECT_CALL(*mock_frame_transformer, Transform).Times(0);
+ // Will pass the frame straight to the reciever.
+ EXPECT_CALL(receiver, ManageFrame);
+ delegate->TransformFrame(CreateRtpFrameObject());
+}
+
} // namespace
} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/video_rtp_depacketizer_av1.cc b/third_party/libwebrtc/modules/rtp_rtcp/source/video_rtp_depacketizer_av1.cc
index 870f788538..30bbbc5000 100644
--- a/third_party/libwebrtc/modules/rtp_rtcp/source/video_rtp_depacketizer_av1.cc
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/video_rtp_depacketizer_av1.cc
@@ -188,8 +188,7 @@ VectorObuInfo ParseObus(
VectorObuInfo obu_infos;
bool expect_continues_obu = false;
for (rtc::ArrayView<const uint8_t> rtp_payload : rtp_payloads) {
- rtc::ByteBufferReader payload(
- reinterpret_cast<const char*>(rtp_payload.data()), rtp_payload.size());
+ rtc::ByteBufferReader payload(rtp_payload);
uint8_t aggregation_header;
if (!payload.ReadUInt8(&aggregation_header)) {
RTC_DLOG(LS_WARNING)
diff --git a/third_party/libwebrtc/modules/third_party/fft/fft_gn/moz.build b/third_party/libwebrtc/modules/third_party/fft/fft_gn/moz.build
index d2e3ea0128..c260743e28 100644
--- a/third_party/libwebrtc/modules/third_party/fft/fft_gn/moz.build
+++ b/third_party/libwebrtc/modules/third_party/fft/fft_gn/moz.build
@@ -184,7 +184,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -194,10 +193,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/third_party/g711/g711_3p_gn/moz.build b/third_party/libwebrtc/modules/third_party/g711/g711_3p_gn/moz.build
index aa7a21a680..c2d2597a21 100644
--- a/third_party/libwebrtc/modules/third_party/g711/g711_3p_gn/moz.build
+++ b/third_party/libwebrtc/modules/third_party/g711/g711_3p_gn/moz.build
@@ -184,7 +184,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -194,10 +193,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/third_party/g722/g722_3p_gn/moz.build b/third_party/libwebrtc/modules/third_party/g722/g722_3p_gn/moz.build
index 41a8c05bae..468cc88c65 100644
--- a/third_party/libwebrtc/modules/third_party/g722/g722_3p_gn/moz.build
+++ b/third_party/libwebrtc/modules/third_party/g722/g722_3p_gn/moz.build
@@ -188,7 +188,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -198,10 +197,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/utility/utility_gn/moz.build b/third_party/libwebrtc/modules/utility/utility_gn/moz.build
index b6921b7626..6c17ac236e 100644
--- a/third_party/libwebrtc/modules/utility/utility_gn/moz.build
+++ b/third_party/libwebrtc/modules/utility/utility_gn/moz.build
@@ -188,7 +188,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
]
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -198,10 +197,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/video_capture/linux/camera_portal.cc b/third_party/libwebrtc/modules/video_capture/linux/camera_portal.cc
index 85b9f20228..106ca1682c 100644
--- a/third_party/libwebrtc/modules/video_capture/linux/camera_portal.cc
+++ b/third_party/libwebrtc/modules/video_capture/linux/camera_portal.cc
@@ -15,6 +15,7 @@
#include "modules/portal/pipewire_utils.h"
#include "modules/portal/xdg_desktop_portal_utils.h"
+#include "rtc_base/synchronization/mutex.h"
namespace webrtc {
@@ -54,7 +55,9 @@ class CameraPortalPrivate {
GAsyncResult* result,
gpointer user_data);
- CameraPortal::PortalNotifier* notifier_ = nullptr;
+ webrtc::Mutex notifier_lock_;
+ CameraPortal::PortalNotifier* notifier_ RTC_GUARDED_BY(&notifier_lock_) =
+ nullptr;
GDBusConnection* connection_ = nullptr;
GDBusProxy* proxy_ = nullptr;
@@ -66,6 +69,11 @@ CameraPortalPrivate::CameraPortalPrivate(CameraPortal::PortalNotifier* notifier)
: notifier_(notifier) {}
CameraPortalPrivate::~CameraPortalPrivate() {
+ {
+ webrtc::MutexLock lock(&notifier_lock_);
+ notifier_ = nullptr;
+ }
+
if (access_request_signal_id_) {
g_dbus_connection_signal_unsubscribe(connection_,
access_request_signal_id_);
@@ -229,7 +237,11 @@ void CameraPortalPrivate::OnOpenResponse(GDBusProxy* proxy,
}
void CameraPortalPrivate::OnPortalDone(RequestResponse result, int fd) {
- notifier_->OnCameraRequestResult(result, fd);
+ webrtc::MutexLock lock(&notifier_lock_);
+ if (notifier_) {
+ notifier_->OnCameraRequestResult(result, fd);
+ notifier_ = nullptr;
+ }
}
CameraPortal::CameraPortal(PortalNotifier* notifier)
diff --git a/third_party/libwebrtc/modules/video_capture/linux/device_info_pipewire.cc b/third_party/libwebrtc/modules/video_capture/linux/device_info_pipewire.cc
index ad6cea57b8..31d922035b 100644
--- a/third_party/libwebrtc/modules/video_capture/linux/device_info_pipewire.cc
+++ b/third_party/libwebrtc/modules/video_capture/linux/device_info_pipewire.cc
@@ -20,10 +20,10 @@
#include <vector>
+#include "modules/video_capture/linux/pipewire_session.h"
#include "modules/video_capture/video_capture.h"
#include "modules/video_capture/video_capture_defines.h"
#include "modules/video_capture/video_capture_impl.h"
-#include "modules/video_capture/video_capture_options.h"
#include "rtc_base/logging.h"
namespace webrtc {
@@ -38,6 +38,8 @@ int32_t DeviceInfoPipeWire::Init() {
DeviceInfoPipeWire::~DeviceInfoPipeWire() = default;
uint32_t DeviceInfoPipeWire::NumberOfDevices() {
+ RTC_CHECK(pipewire_session_);
+
return pipewire_session_->nodes().size();
}
@@ -50,6 +52,8 @@ int32_t DeviceInfoPipeWire::GetDeviceName(uint32_t deviceNumber,
uint32_t productUniqueIdUTF8Length,
pid_t* pid,
bool* deviceIsPlaceholder) {
+ RTC_CHECK(pipewire_session_);
+
if (deviceNumber >= NumberOfDevices())
return -1;
@@ -85,6 +89,8 @@ int32_t DeviceInfoPipeWire::GetDeviceName(uint32_t deviceNumber,
int32_t DeviceInfoPipeWire::CreateCapabilityMap(
const char* deviceUniqueIdUTF8) {
+ RTC_CHECK(pipewire_session_);
+
for (auto& node : pipewire_session_->nodes()) {
if (node.unique_id().compare(deviceUniqueIdUTF8) != 0)
continue;
diff --git a/third_party/libwebrtc/modules/video_capture/linux/device_info_pipewire.h b/third_party/libwebrtc/modules/video_capture/linux/device_info_pipewire.h
index 1a1324e92b..00715c94bc 100644
--- a/third_party/libwebrtc/modules/video_capture/linux/device_info_pipewire.h
+++ b/third_party/libwebrtc/modules/video_capture/linux/device_info_pipewire.h
@@ -14,7 +14,7 @@
#include <stdint.h>
#include "modules/video_capture/device_info_impl.h"
-#include "modules/video_capture/linux/pipewire_session.h"
+#include "modules/video_capture/video_capture_options.h"
namespace webrtc {
namespace videocapturemodule {
diff --git a/third_party/libwebrtc/modules/video_capture/linux/device_info_v4l2.cc b/third_party/libwebrtc/modules/video_capture/linux/device_info_v4l2.cc
index eaeed26b7c..401c38f9c5 100644
--- a/third_party/libwebrtc/modules/video_capture/linux/device_info_v4l2.cc
+++ b/third_party/libwebrtc/modules/video_capture/linux/device_info_v4l2.cc
@@ -57,24 +57,6 @@
#define BUF_LEN ( 1024 * ( EVENT_SIZE + 16 ) )
#endif
-// These defines are here to support building on kernel 3.16 which some
-// downstream projects, e.g. Firefox, use.
-// TODO(apehrson): Remove them and their undefs when no longer needed.
-#ifndef V4L2_PIX_FMT_ABGR32
-#define ABGR32_OVERRIDE 1
-#define V4L2_PIX_FMT_ABGR32 v4l2_fourcc('A', 'R', '2', '4')
-#endif
-
-#ifndef V4L2_PIX_FMT_ARGB32
-#define ARGB32_OVERRIDE 1
-#define V4L2_PIX_FMT_ARGB32 v4l2_fourcc('B', 'A', '2', '4')
-#endif
-
-#ifndef V4L2_PIX_FMT_RGBA32
-#define RGBA32_OVERRIDE 1
-#define V4L2_PIX_FMT_RGBA32 v4l2_fourcc('A', 'B', '2', '4')
-#endif
-
namespace webrtc {
namespace videocapturemodule {
#ifdef WEBRTC_LINUX
diff --git a/third_party/libwebrtc/modules/video_capture/linux/video_capture_pipewire.cc b/third_party/libwebrtc/modules/video_capture/linux/video_capture_pipewire.cc
index 8af483636a..319824d3c5 100644
--- a/third_party/libwebrtc/modules/video_capture/linux/video_capture_pipewire.cc
+++ b/third_party/libwebrtc/modules/video_capture/linux/video_capture_pipewire.cc
@@ -178,8 +178,7 @@ int32_t VideoCaptureModulePipeWire::StartCapture(
int res = pw_stream_connect(
stream_, PW_DIRECTION_INPUT, node_id_,
static_cast<enum pw_stream_flags>(PW_STREAM_FLAG_AUTOCONNECT |
- PW_STREAM_FLAG_DONT_RECONNECT |
- PW_STREAM_FLAG_MAP_BUFFERS),
+ PW_STREAM_FLAG_DONT_RECONNECT),
params.data(), params.size());
if (res != 0) {
RTC_LOG(LS_ERROR) << "Could not connect to camera stream: "
@@ -312,11 +311,11 @@ void VideoCaptureModulePipeWire::OnFormatChanged(const struct spa_pod* format) {
0);
}
+ const int buffer_types =
+ (1 << SPA_DATA_DmaBuf) | (1 << SPA_DATA_MemFd) | (1 << SPA_DATA_MemPtr);
spa_pod_builder_add(
&builder, SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(8, 1, 32),
- SPA_PARAM_BUFFERS_dataType,
- SPA_POD_CHOICE_FLAGS_Int((1 << SPA_DATA_MemFd) | (1 << SPA_DATA_MemPtr)),
- 0);
+ SPA_PARAM_BUFFERS_dataType, SPA_POD_CHOICE_FLAGS_Int(buffer_types), 0);
params.push_back(
static_cast<spa_pod*>(spa_pod_builder_pop(&builder, &frame)));
@@ -384,14 +383,15 @@ void VideoCaptureModulePipeWire::ProcessBuffers() {
RTC_CHECK_RUNS_SERIALIZED(&capture_checker_);
while (pw_buffer* buffer = pw_stream_dequeue_buffer(stream_)) {
+ spa_buffer* spaBuffer = buffer->buffer;
struct spa_meta_header* h;
h = static_cast<struct spa_meta_header*>(
- spa_buffer_find_meta_data(buffer->buffer, SPA_META_Header, sizeof(*h)));
+ spa_buffer_find_meta_data(spaBuffer, SPA_META_Header, sizeof(*h)));
struct spa_meta_videotransform* videotransform;
videotransform =
static_cast<struct spa_meta_videotransform*>(spa_buffer_find_meta_data(
- buffer->buffer, SPA_META_VideoTransform, sizeof(*videotransform)));
+ spaBuffer, SPA_META_VideoTransform, sizeof(*videotransform)));
if (videotransform) {
VideoRotation rotation =
VideorotationFromPipeWireTransform(videotransform->transform);
@@ -401,11 +401,35 @@ void VideoCaptureModulePipeWire::ProcessBuffers() {
if (h->flags & SPA_META_HEADER_FLAG_CORRUPTED) {
RTC_LOG(LS_INFO) << "Dropping corruped frame.";
- } else {
- IncomingFrame(static_cast<unsigned char*>(buffer->buffer->datas[0].data),
- buffer->buffer->datas[0].chunk->size,
- configured_capability_);
+ pw_stream_queue_buffer(stream_, buffer);
+ continue;
+ }
+
+ if (spaBuffer->datas[0].type == SPA_DATA_DmaBuf ||
+ spaBuffer->datas[0].type == SPA_DATA_MemFd) {
+ ScopedBuf frame;
+ frame.initialize(
+ static_cast<uint8_t*>(
+ mmap(nullptr,
+ spaBuffer->datas[0].maxsize + spaBuffer->datas[0].mapoffset,
+ PROT_READ, MAP_PRIVATE, spaBuffer->datas[0].fd, 0)),
+ spaBuffer->datas[0].maxsize + spaBuffer->datas[0].mapoffset,
+ spaBuffer->datas[0].fd, spaBuffer->datas[0].type == SPA_DATA_DmaBuf);
+
+ if (!frame) {
+ RTC_LOG(LS_ERROR) << "Failed to mmap the memory: "
+ << std::strerror(errno);
+ return;
+ }
+
+ IncomingFrame(
+ SPA_MEMBER(frame.get(), spaBuffer->datas[0].mapoffset, uint8_t),
+ spaBuffer->datas[0].chunk->size, configured_capability_);
+ } else { // SPA_DATA_MemPtr
+ IncomingFrame(static_cast<uint8_t*>(spaBuffer->datas[0].data),
+ spaBuffer->datas[0].chunk->size, configured_capability_);
}
+
pw_stream_queue_buffer(stream_, buffer);
}
}
diff --git a/third_party/libwebrtc/modules/video_capture/linux/video_capture_v4l2.cc b/third_party/libwebrtc/modules/video_capture/linux/video_capture_v4l2.cc
index c887683dc8..08d23f7f58 100644
--- a/third_party/libwebrtc/modules/video_capture/linux/video_capture_v4l2.cc
+++ b/third_party/libwebrtc/modules/video_capture/linux/video_capture_v4l2.cc
@@ -294,7 +294,7 @@ int32_t VideoCaptureModuleV4L2::StartCapture(
if (_captureThread.empty()) {
quit_ = false;
_captureThread = rtc::PlatformThread::SpawnJoinable(
- [self = rtc::scoped_refptr(this)] {
+ [self = scoped_refptr(this)] {
while (self->CaptureProcess()) {
}
},
diff --git a/third_party/libwebrtc/modules/video_capture/video_capture.h b/third_party/libwebrtc/modules/video_capture/video_capture.h
index 378a53b4d2..f59c34f8b2 100644
--- a/third_party/libwebrtc/modules/video_capture/video_capture.h
+++ b/third_party/libwebrtc/modules/video_capture/video_capture.h
@@ -34,7 +34,7 @@ protected:
virtual ~VideoInputFeedBack(){}
};
-class VideoCaptureModule : public rtc::RefCountInterface {
+class VideoCaptureModule : public RefCountInterface {
public:
// Interface for receiving information about available camera devices.
class DeviceInfo {
diff --git a/third_party/libwebrtc/modules/video_capture/video_capture_internal_impl_gn/moz.build b/third_party/libwebrtc/modules/video_capture/video_capture_internal_impl_gn/moz.build
index 24988a1ffc..f58aa8e782 100644
--- a/third_party/libwebrtc/modules/video_capture/video_capture_internal_impl_gn/moz.build
+++ b/third_party/libwebrtc/modules/video_capture/video_capture_internal_impl_gn/moz.build
@@ -267,7 +267,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -277,10 +276,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["WEBRTC_USE_PIPEWIRE"] = True
diff --git a/third_party/libwebrtc/modules/video_capture/video_capture_module_gn/moz.build b/third_party/libwebrtc/modules/video_capture/video_capture_module_gn/moz.build
index 49c62d5cf6..820d5655df 100644
--- a/third_party/libwebrtc/modules/video_capture/video_capture_module_gn/moz.build
+++ b/third_party/libwebrtc/modules/video_capture/video_capture_module_gn/moz.build
@@ -204,7 +204,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -214,10 +213,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/video_coding/BUILD.gn b/third_party/libwebrtc/modules/video_coding/BUILD.gn
index cca9d8883a..d9e614ff81 100644
--- a/third_party/libwebrtc/modules/video_coding/BUILD.gn
+++ b/third_party/libwebrtc/modules/video_coding/BUILD.gn
@@ -757,7 +757,6 @@ if (rtc_include_tests) {
"../../api/video_codecs:video_codecs_api",
"../../modules/utility:utility",
"../../rtc_base:checks",
- "../../rtc_base:ignore_wundef",
"../../sdk/android:internal_jni",
"../../sdk/android:native_api_base",
"../../sdk/android:native_api_codecs",
@@ -852,8 +851,6 @@ if (rtc_include_tests) {
"../../api:frame_generator_api",
"../../api:scoped_refptr",
"../../api:sequence_checker",
- "../../api:video_codec_stats_api",
- "../../api:video_codec_tester_api",
"../../api:videocodec_test_fixture_api",
"../../api/numerics:numerics",
"../../api/task_queue",
@@ -995,46 +992,6 @@ if (rtc_include_tests) {
]
}
- rtc_library("video_codec_tester") {
- testonly = true
- sources = [
- "codecs/test/video_codec_analyzer.cc",
- "codecs/test/video_codec_analyzer.h",
- "codecs/test/video_codec_stats_impl.cc",
- "codecs/test/video_codec_stats_impl.h",
- "codecs/test/video_codec_tester_impl.cc",
- "codecs/test/video_codec_tester_impl.h",
- ]
-
- deps = [
- ":video_coding_utility",
- "../../api:sequence_checker",
- "../../api:video_codec_stats_api",
- "../../api:video_codec_tester_api",
- "../../api/numerics:numerics",
- "../../api/task_queue:default_task_queue_factory",
- "../../api/test/metrics:metrics_logger",
- "../../api/units:data_rate",
- "../../api/units:frequency",
- "../../api/units:time_delta",
- "../../api/units:timestamp",
- "../../api/video:encoded_image",
- "../../api/video:resolution",
- "../../api/video:video_codec_constants",
- "../../api/video:video_frame",
- "../../rtc_base:checks",
- "../../rtc_base:rtc_event",
- "../../rtc_base:task_queue_for_test",
- "../../rtc_base:timeutils",
- "../../rtc_base/system:no_unique_address",
- "../../system_wrappers",
- "../../test:video_test_support",
- "//third_party/libyuv",
- ]
-
- absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ]
- }
-
rtc_test("video_codec_perf_tests") {
testonly = true
@@ -1042,28 +999,20 @@ if (rtc_include_tests) {
deps = [
":video_codec_interface",
- ":video_codec_tester",
- "../../api:create_video_codec_tester_api",
- "../../api:video_codec_tester_api",
- "../../api:videocodec_test_stats_api",
"../../api/test/metrics:global_metrics_logger_and_exporter",
"../../api/units:data_rate",
"../../api/units:frequency",
- "../../api/video:encoded_image",
"../../api/video:resolution",
- "../../api/video:video_frame",
- "../../api/video_codecs:scalability_mode",
- "../../api/video_codecs:video_codecs_api",
- "../../media:rtc_internal_video_codecs",
+ "../../api/video_codecs:builtin_video_decoder_factory",
+ "../../api/video_codecs:builtin_video_encoder_factory",
+ "../../modules/video_coding/svc:scalability_mode_util",
"../../rtc_base:logging",
+ "../../rtc_base:stringutils",
"../../test:fileutils",
"../../test:test_flags",
"../../test:test_main",
"../../test:test_support",
- "../../test:video_test_support",
- "../rtp_rtcp:rtp_rtcp_format",
- "svc:scalability_mode_util",
- "//third_party/libyuv",
+ "../../test:video_codec_tester",
]
if (is_android) {
@@ -1191,9 +1140,6 @@ if (rtc_include_tests) {
sources = [
"chain_diff_calculator_unittest.cc",
- "codecs/test/video_codec_analyzer_unittest.cc",
- "codecs/test/video_codec_stats_impl_unittest.cc",
- "codecs/test/video_codec_tester_impl_unittest.cc",
"codecs/test/videocodec_test_fixture_config_unittest.cc",
"codecs/test/videocodec_test_stats_impl_unittest.cc",
"codecs/test/videoprocessor_unittest.cc",
@@ -1248,7 +1194,6 @@ if (rtc_include_tests) {
":packet_buffer",
":simulcast_test_fixture_impl",
":video_codec_interface",
- ":video_codec_tester",
":video_codecs_test_framework",
":video_coding",
":video_coding_legacy",
@@ -1271,7 +1216,6 @@ if (rtc_include_tests) {
"../../api:rtp_packet_info",
"../../api:scoped_refptr",
"../../api:simulcast_test_fixture_api",
- "../../api:video_codec_tester_api",
"../../api:videocodec_test_fixture_api",
"../../api/task_queue",
"../../api/task_queue:default_task_queue_factory",
@@ -1297,6 +1241,7 @@ if (rtc_include_tests) {
"../../common_video/generic_frame_descriptor",
"../../common_video/test:utilities",
"../../media:media_constants",
+ "../../media:rtc_internal_video_codecs",
"../../media:rtc_media_base",
"../../rtc_base:checks",
"../../rtc_base:gunit_helpers",
diff --git a/third_party/libwebrtc/modules/video_coding/chain_diff_calculator_gn/moz.build b/third_party/libwebrtc/modules/video_coding/chain_diff_calculator_gn/moz.build
index dd8e979e41..144097f87a 100644
--- a/third_party/libwebrtc/modules/video_coding/chain_diff_calculator_gn/moz.build
+++ b/third_party/libwebrtc/modules/video_coding/chain_diff_calculator_gn/moz.build
@@ -195,7 +195,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -205,10 +204,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/video_coding/codec_globals_headers_gn/moz.build b/third_party/libwebrtc/modules/video_coding/codec_globals_headers_gn/moz.build
index 73fce5bf02..cf74ae964c 100644
--- a/third_party/libwebrtc/modules/video_coding/codec_globals_headers_gn/moz.build
+++ b/third_party/libwebrtc/modules/video_coding/codec_globals_headers_gn/moz.build
@@ -180,16 +180,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/video_coding/codecs/av1/av1_svc_config_gn/moz.build b/third_party/libwebrtc/modules/video_coding/codecs/av1/av1_svc_config_gn/moz.build
index e67bb6616d..bfe37b935d 100644
--- a/third_party/libwebrtc/modules/video_coding/codecs/av1/av1_svc_config_gn/moz.build
+++ b/third_party/libwebrtc/modules/video_coding/codecs/av1/av1_svc_config_gn/moz.build
@@ -195,7 +195,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -205,10 +204,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/video_coding/codecs/test/video_codec_analyzer.cc b/third_party/libwebrtc/modules/video_coding/codecs/test/video_codec_analyzer.cc
deleted file mode 100644
index 772c15734a..0000000000
--- a/third_party/libwebrtc/modules/video_coding/codecs/test/video_codec_analyzer.cc
+++ /dev/null
@@ -1,193 +0,0 @@
-/*
- * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved.
- *
- * Use of this source code is governed by a BSD-style license
- * that can be found in the LICENSE file in the root of the source
- * tree. An additional intellectual property rights grant can be found
- * in the file PATENTS. All contributing project authors may
- * be found in the AUTHORS file in the root of the source tree.
- */
-
-#include "modules/video_coding/codecs/test/video_codec_analyzer.h"
-
-#include <memory>
-
-#include "api/task_queue/default_task_queue_factory.h"
-#include "api/video/i420_buffer.h"
-#include "api/video/video_codec_constants.h"
-#include "api/video/video_frame.h"
-#include "rtc_base/checks.h"
-#include "rtc_base/event.h"
-#include "rtc_base/time_utils.h"
-#include "third_party/libyuv/include/libyuv/compare.h"
-
-namespace webrtc {
-namespace test {
-
-namespace {
-using Psnr = VideoCodecStats::Frame::Psnr;
-
-Psnr CalcPsnr(const I420BufferInterface& ref_buffer,
- const I420BufferInterface& dec_buffer) {
- RTC_CHECK_EQ(ref_buffer.width(), dec_buffer.width());
- RTC_CHECK_EQ(ref_buffer.height(), dec_buffer.height());
-
- uint64_t sse_y = libyuv::ComputeSumSquareErrorPlane(
- dec_buffer.DataY(), dec_buffer.StrideY(), ref_buffer.DataY(),
- ref_buffer.StrideY(), dec_buffer.width(), dec_buffer.height());
-
- uint64_t sse_u = libyuv::ComputeSumSquareErrorPlane(
- dec_buffer.DataU(), dec_buffer.StrideU(), ref_buffer.DataU(),
- ref_buffer.StrideU(), dec_buffer.width() / 2, dec_buffer.height() / 2);
-
- uint64_t sse_v = libyuv::ComputeSumSquareErrorPlane(
- dec_buffer.DataV(), dec_buffer.StrideV(), ref_buffer.DataV(),
- ref_buffer.StrideV(), dec_buffer.width() / 2, dec_buffer.height() / 2);
-
- int num_y_samples = dec_buffer.width() * dec_buffer.height();
- Psnr psnr;
- psnr.y = libyuv::SumSquareErrorToPsnr(sse_y, num_y_samples);
- psnr.u = libyuv::SumSquareErrorToPsnr(sse_u, num_y_samples / 4);
- psnr.v = libyuv::SumSquareErrorToPsnr(sse_v, num_y_samples / 4);
-
- return psnr;
-}
-
-} // namespace
-
-VideoCodecAnalyzer::VideoCodecAnalyzer(
- ReferenceVideoSource* reference_video_source)
- : reference_video_source_(reference_video_source), num_frames_(0) {
- sequence_checker_.Detach();
-}
-
-void VideoCodecAnalyzer::StartEncode(const VideoFrame& input_frame) {
- int64_t encode_start_us = rtc::TimeMicros();
- task_queue_.PostTask(
- [this, timestamp_rtp = input_frame.timestamp(), encode_start_us]() {
- RTC_DCHECK_RUN_ON(&sequence_checker_);
-
- RTC_CHECK(frame_num_.find(timestamp_rtp) == frame_num_.end());
- frame_num_[timestamp_rtp] = num_frames_++;
-
- stats_.AddFrame({.frame_num = frame_num_[timestamp_rtp],
- .timestamp_rtp = timestamp_rtp,
- .encode_start = Timestamp::Micros(encode_start_us)});
- });
-}
-
-void VideoCodecAnalyzer::FinishEncode(const EncodedImage& frame) {
- int64_t encode_finished_us = rtc::TimeMicros();
-
- task_queue_.PostTask([this, timestamp_rtp = frame.RtpTimestamp(),
- spatial_idx = frame.SpatialIndex().value_or(0),
- temporal_idx = frame.TemporalIndex().value_or(0),
- width = frame._encodedWidth,
- height = frame._encodedHeight,
- frame_type = frame._frameType,
- frame_size_bytes = frame.size(), qp = frame.qp_,
- encode_finished_us]() {
- RTC_DCHECK_RUN_ON(&sequence_checker_);
-
- if (spatial_idx > 0) {
- VideoCodecStats::Frame* base_frame =
- stats_.GetFrame(timestamp_rtp, /*spatial_idx=*/0);
-
- stats_.AddFrame({.frame_num = base_frame->frame_num,
- .timestamp_rtp = timestamp_rtp,
- .spatial_idx = spatial_idx,
- .encode_start = base_frame->encode_start});
- }
-
- VideoCodecStats::Frame* fs = stats_.GetFrame(timestamp_rtp, spatial_idx);
- fs->spatial_idx = spatial_idx;
- fs->temporal_idx = temporal_idx;
- fs->width = width;
- fs->height = height;
- fs->frame_size = DataSize::Bytes(frame_size_bytes);
- fs->qp = qp;
- fs->keyframe = frame_type == VideoFrameType::kVideoFrameKey;
- fs->encode_time = Timestamp::Micros(encode_finished_us) - fs->encode_start;
- fs->encoded = true;
- });
-}
-
-void VideoCodecAnalyzer::StartDecode(const EncodedImage& frame) {
- int64_t decode_start_us = rtc::TimeMicros();
- task_queue_.PostTask([this, timestamp_rtp = frame.RtpTimestamp(),
- spatial_idx = frame.SpatialIndex().value_or(0),
- frame_size_bytes = frame.size(), decode_start_us]() {
- RTC_DCHECK_RUN_ON(&sequence_checker_);
-
- VideoCodecStats::Frame* fs = stats_.GetFrame(timestamp_rtp, spatial_idx);
- if (fs == nullptr) {
- if (frame_num_.find(timestamp_rtp) == frame_num_.end()) {
- frame_num_[timestamp_rtp] = num_frames_++;
- }
- stats_.AddFrame({.frame_num = frame_num_[timestamp_rtp],
- .timestamp_rtp = timestamp_rtp,
- .spatial_idx = spatial_idx,
- .frame_size = DataSize::Bytes(frame_size_bytes)});
- fs = stats_.GetFrame(timestamp_rtp, spatial_idx);
- }
-
- fs->decode_start = Timestamp::Micros(decode_start_us);
- });
-}
-
-void VideoCodecAnalyzer::FinishDecode(const VideoFrame& frame,
- int spatial_idx) {
- int64_t decode_finished_us = rtc::TimeMicros();
- task_queue_.PostTask([this, timestamp_rtp = frame.timestamp(), spatial_idx,
- width = frame.width(), height = frame.height(),
- decode_finished_us]() {
- RTC_DCHECK_RUN_ON(&sequence_checker_);
- VideoCodecStats::Frame* fs = stats_.GetFrame(timestamp_rtp, spatial_idx);
- fs->decode_time = Timestamp::Micros(decode_finished_us) - fs->decode_start;
-
- if (!fs->encoded) {
- fs->width = width;
- fs->height = height;
- }
-
- fs->decoded = true;
- });
-
- if (reference_video_source_ != nullptr) {
- // Copy hardware-backed frame into main memory to release output buffers
- // which number may be limited in hardware decoders.
- rtc::scoped_refptr<I420BufferInterface> decoded_buffer =
- frame.video_frame_buffer()->ToI420();
-
- task_queue_.PostTask([this, decoded_buffer,
- timestamp_rtp = frame.timestamp(), spatial_idx]() {
- RTC_DCHECK_RUN_ON(&sequence_checker_);
- VideoFrame ref_frame = reference_video_source_->GetFrame(
- timestamp_rtp, {.width = decoded_buffer->width(),
- .height = decoded_buffer->height()});
- rtc::scoped_refptr<I420BufferInterface> ref_buffer =
- ref_frame.video_frame_buffer()->ToI420();
-
- Psnr psnr = CalcPsnr(*decoded_buffer, *ref_buffer);
-
- VideoCodecStats::Frame* fs =
- this->stats_.GetFrame(timestamp_rtp, spatial_idx);
- fs->psnr = psnr;
- });
- }
-}
-
-std::unique_ptr<VideoCodecStats> VideoCodecAnalyzer::GetStats() {
- std::unique_ptr<VideoCodecStats> stats;
- rtc::Event ready;
- task_queue_.PostTask([this, &stats, &ready]() mutable {
- RTC_DCHECK_RUN_ON(&sequence_checker_);
- stats.reset(new VideoCodecStatsImpl(stats_));
- ready.Set();
- });
- ready.Wait(rtc::Event::kForever);
- return stats;
-}
-
-} // namespace test
-} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/video_coding/codecs/test/video_codec_analyzer.h b/third_party/libwebrtc/modules/video_coding/codecs/test/video_codec_analyzer.h
deleted file mode 100644
index 29ca8ee2ff..0000000000
--- a/third_party/libwebrtc/modules/video_coding/codecs/test/video_codec_analyzer.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved.
- *
- * Use of this source code is governed by a BSD-style license
- * that can be found in the LICENSE file in the root of the source
- * tree. An additional intellectual property rights grant can be found
- * in the file PATENTS. All contributing project authors may
- * be found in the AUTHORS file in the root of the source tree.
- */
-
-#ifndef MODULES_VIDEO_CODING_CODECS_TEST_VIDEO_CODEC_ANALYZER_H_
-#define MODULES_VIDEO_CODING_CODECS_TEST_VIDEO_CODEC_ANALYZER_H_
-
-#include <map>
-#include <memory>
-
-#include "absl/types/optional.h"
-#include "api/sequence_checker.h"
-#include "api/test/video_codec_tester.h"
-#include "api/video/encoded_image.h"
-#include "api/video/resolution.h"
-#include "api/video/video_frame.h"
-#include "modules/video_coding/codecs/test/video_codec_stats_impl.h"
-#include "rtc_base/system/no_unique_address.h"
-#include "rtc_base/task_queue_for_test.h"
-
-namespace webrtc {
-namespace test {
-
-// Analyzer measures and collects metrics necessary for evaluation of video
-// codec quality and performance. This class is thread-safe.
-class VideoCodecAnalyzer {
- public:
- // An interface that provides reference frames for spatial quality analysis.
- class ReferenceVideoSource {
- public:
- virtual ~ReferenceVideoSource() = default;
-
- virtual VideoFrame GetFrame(uint32_t timestamp_rtp,
- Resolution resolution) = 0;
- };
-
- explicit VideoCodecAnalyzer(
- ReferenceVideoSource* reference_video_source = nullptr);
-
- void StartEncode(const VideoFrame& frame);
-
- void FinishEncode(const EncodedImage& frame);
-
- void StartDecode(const EncodedImage& frame);
-
- void FinishDecode(const VideoFrame& frame, int spatial_idx);
-
- std::unique_ptr<VideoCodecStats> GetStats();
-
- protected:
- TaskQueueForTest task_queue_;
-
- ReferenceVideoSource* const reference_video_source_;
-
- VideoCodecStatsImpl stats_ RTC_GUARDED_BY(sequence_checker_);
-
- // Map from RTP timestamp to frame number.
- std::map<uint32_t, int> frame_num_ RTC_GUARDED_BY(sequence_checker_);
-
- // Processed frames counter.
- int num_frames_ RTC_GUARDED_BY(sequence_checker_);
-
- RTC_NO_UNIQUE_ADDRESS SequenceChecker sequence_checker_;
-};
-
-} // namespace test
-} // namespace webrtc
-
-#endif // MODULES_VIDEO_CODING_CODECS_TEST_VIDEO_CODEC_ANALYZER_H_
diff --git a/third_party/libwebrtc/modules/video_coding/codecs/test/video_codec_analyzer_unittest.cc b/third_party/libwebrtc/modules/video_coding/codecs/test/video_codec_analyzer_unittest.cc
deleted file mode 100644
index 03146417da..0000000000
--- a/third_party/libwebrtc/modules/video_coding/codecs/test/video_codec_analyzer_unittest.cc
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved.
- *
- * Use of this source code is governed by a BSD-style license
- * that can be found in the LICENSE file in the root of the source
- * tree. An additional intellectual property rights grant can be found
- * in the file PATENTS. All contributing project authors may
- * be found in the AUTHORS file in the root of the source tree.
- */
-
-#include "modules/video_coding/codecs/test/video_codec_analyzer.h"
-
-#include "absl/types/optional.h"
-#include "api/video/i420_buffer.h"
-#include "test/gmock.h"
-#include "test/gtest.h"
-#include "third_party/libyuv/include/libyuv/planar_functions.h"
-
-namespace webrtc {
-namespace test {
-
-namespace {
-using ::testing::Return;
-using ::testing::Values;
-using Psnr = VideoCodecStats::Frame::Psnr;
-
-const uint32_t kTimestamp = 3000;
-const int kSpatialIdx = 2;
-
-class MockReferenceVideoSource
- : public VideoCodecAnalyzer::ReferenceVideoSource {
- public:
- MOCK_METHOD(VideoFrame, GetFrame, (uint32_t, Resolution), (override));
-};
-
-VideoFrame CreateVideoFrame(uint32_t timestamp_rtp,
- uint8_t y = 0,
- uint8_t u = 0,
- uint8_t v = 0) {
- rtc::scoped_refptr<I420Buffer> buffer(I420Buffer::Create(2, 2));
-
- libyuv::I420Rect(buffer->MutableDataY(), buffer->StrideY(),
- buffer->MutableDataU(), buffer->StrideU(),
- buffer->MutableDataV(), buffer->StrideV(), 0, 0,
- buffer->width(), buffer->height(), y, u, v);
-
- return VideoFrame::Builder()
- .set_video_frame_buffer(buffer)
- .set_timestamp_rtp(timestamp_rtp)
- .build();
-}
-
-EncodedImage CreateEncodedImage(uint32_t timestamp_rtp, int spatial_idx = 0) {
- EncodedImage encoded_image;
- encoded_image.SetRtpTimestamp(timestamp_rtp);
- encoded_image.SetSpatialIndex(spatial_idx);
- return encoded_image;
-}
-} // namespace
-
-TEST(VideoCodecAnalyzerTest, StartEncode) {
- VideoCodecAnalyzer analyzer;
- analyzer.StartEncode(CreateVideoFrame(kTimestamp));
-
- auto fs = analyzer.GetStats()->Slice();
- EXPECT_EQ(1u, fs.size());
- EXPECT_EQ(fs[0].timestamp_rtp, kTimestamp);
-}
-
-TEST(VideoCodecAnalyzerTest, FinishEncode) {
- VideoCodecAnalyzer analyzer;
- analyzer.StartEncode(CreateVideoFrame(kTimestamp));
-
- EncodedImage encoded_frame = CreateEncodedImage(kTimestamp, kSpatialIdx);
- analyzer.FinishEncode(encoded_frame);
-
- auto fs = analyzer.GetStats()->Slice();
- EXPECT_EQ(2u, fs.size());
- EXPECT_EQ(kSpatialIdx, fs[1].spatial_idx);
-}
-
-TEST(VideoCodecAnalyzerTest, StartDecode) {
- VideoCodecAnalyzer analyzer;
- analyzer.StartDecode(CreateEncodedImage(kTimestamp, kSpatialIdx));
-
- auto fs = analyzer.GetStats()->Slice();
- EXPECT_EQ(1u, fs.size());
- EXPECT_EQ(kTimestamp, fs[0].timestamp_rtp);
-}
-
-TEST(VideoCodecAnalyzerTest, FinishDecode) {
- VideoCodecAnalyzer analyzer;
- analyzer.StartDecode(CreateEncodedImage(kTimestamp, kSpatialIdx));
- VideoFrame decoded_frame = CreateVideoFrame(kTimestamp);
- analyzer.FinishDecode(decoded_frame, kSpatialIdx);
-
- auto fs = analyzer.GetStats()->Slice();
- EXPECT_EQ(1u, fs.size());
- EXPECT_EQ(decoded_frame.width(), fs[0].width);
- EXPECT_EQ(decoded_frame.height(), fs[0].height);
-}
-
-TEST(VideoCodecAnalyzerTest, ReferenceVideoSource) {
- MockReferenceVideoSource reference_video_source;
- VideoCodecAnalyzer analyzer(&reference_video_source);
- analyzer.StartDecode(CreateEncodedImage(kTimestamp, kSpatialIdx));
-
- EXPECT_CALL(reference_video_source, GetFrame)
- .WillOnce(Return(CreateVideoFrame(kTimestamp, /*y=*/0,
- /*u=*/0, /*v=*/0)));
-
- analyzer.FinishDecode(
- CreateVideoFrame(kTimestamp, /*value_y=*/1, /*value_u=*/2, /*value_v=*/3),
- kSpatialIdx);
-
- auto fs = analyzer.GetStats()->Slice();
- EXPECT_EQ(1u, fs.size());
- EXPECT_TRUE(fs[0].psnr.has_value());
-
- const Psnr& psnr = *fs[0].psnr;
- EXPECT_NEAR(psnr.y, 48, 1);
- EXPECT_NEAR(psnr.u, 42, 1);
- EXPECT_NEAR(psnr.v, 38, 1);
-}
-
-} // namespace test
-} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/video_coding/codecs/test/video_codec_stats_impl.cc b/third_party/libwebrtc/modules/video_coding/codecs/test/video_codec_stats_impl.cc
deleted file mode 100644
index 9808e2a601..0000000000
--- a/third_party/libwebrtc/modules/video_coding/codecs/test/video_codec_stats_impl.cc
+++ /dev/null
@@ -1,278 +0,0 @@
-/*
- * Copyright (c) 2023 The WebRTC project authors. All Rights Reserved.
- *
- * Use of this source code is governed by a BSD-style license
- * that can be found in the LICENSE file in the root of the source
- * tree. An additional intellectual property rights grant can be found
- * in the file PATENTS. All contributing project authors may
- * be found in the AUTHORS file in the root of the source tree.
- */
-
-#include "modules/video_coding/codecs/test/video_codec_stats_impl.h"
-
-#include <algorithm>
-
-#include "api/numerics/samples_stats_counter.h"
-#include "api/test/metrics/metrics_logger.h"
-#include "rtc_base/checks.h"
-#include "rtc_base/time_utils.h"
-
-namespace webrtc {
-namespace test {
-namespace {
-using Frame = VideoCodecStats::Frame;
-using Stream = VideoCodecStats::Stream;
-
-constexpr Frequency k90kHz = Frequency::Hertz(90000);
-
-class LeakyBucket {
- public:
- LeakyBucket() : level_bits_(0) {}
-
- // Updates bucket level and returns its current level in bits. Data is remove
- // from bucket with rate equal to target bitrate of previous frame. Bucket
- // level is tracked with floating point precision. Returned value of bucket
- // level is rounded up.
- int Update(const Frame& frame) {
- RTC_CHECK(frame.target_bitrate) << "Bitrate must be specified.";
-
- if (prev_frame_) {
- RTC_CHECK_GT(frame.timestamp_rtp, prev_frame_->timestamp_rtp)
- << "Timestamp must increase.";
- TimeDelta passed =
- (frame.timestamp_rtp - prev_frame_->timestamp_rtp) / k90kHz;
- level_bits_ -=
- prev_frame_->target_bitrate->bps() * passed.us() / 1000000.0;
- level_bits_ = std::max(level_bits_, 0.0);
- }
-
- prev_frame_ = frame;
-
- level_bits_ += frame.frame_size.bytes() * 8;
- return static_cast<int>(std::ceil(level_bits_));
- }
-
- private:
- absl::optional<Frame> prev_frame_;
- double level_bits_;
-};
-
-// Merges spatial layer frames into superframes.
-std::vector<Frame> Merge(const std::vector<Frame>& frames) {
- std::vector<Frame> superframes;
- // Map from frame timestamp to index in `superframes` vector.
- std::map<uint32_t, int> index;
-
- for (const auto& f : frames) {
- if (index.find(f.timestamp_rtp) == index.end()) {
- index[f.timestamp_rtp] = static_cast<int>(superframes.size());
- superframes.push_back(f);
- continue;
- }
-
- Frame& sf = superframes[index[f.timestamp_rtp]];
-
- sf.width = std::max(sf.width, f.width);
- sf.height = std::max(sf.height, f.height);
- sf.frame_size += f.frame_size;
- sf.keyframe |= f.keyframe;
-
- sf.encode_time = std::max(sf.encode_time, f.encode_time);
- sf.decode_time = std::max(sf.decode_time, f.decode_time);
-
- if (f.spatial_idx > sf.spatial_idx) {
- if (f.qp) {
- sf.qp = f.qp;
- }
- if (f.psnr) {
- sf.psnr = f.psnr;
- }
- }
-
- sf.spatial_idx = std::max(sf.spatial_idx, f.spatial_idx);
- sf.temporal_idx = std::max(sf.temporal_idx, f.temporal_idx);
-
- sf.encoded |= f.encoded;
- sf.decoded |= f.decoded;
- }
-
- return superframes;
-}
-
-Timestamp RtpToTime(uint32_t timestamp_rtp) {
- return Timestamp::Micros((timestamp_rtp / k90kHz).us());
-}
-
-SamplesStatsCounter::StatsSample StatsSample(double value, Timestamp time) {
- return SamplesStatsCounter::StatsSample{value, time};
-}
-
-TimeDelta CalcTotalDuration(const std::vector<Frame>& frames) {
- RTC_CHECK(!frames.empty());
- TimeDelta duration = TimeDelta::Zero();
- if (frames.size() > 1) {
- duration +=
- (frames.rbegin()->timestamp_rtp - frames.begin()->timestamp_rtp) /
- k90kHz;
- }
-
- // Add last frame duration. If target frame rate is provided, calculate frame
- // duration from it. Otherwise, assume duration of last frame is the same as
- // duration of preceding frame.
- if (frames.rbegin()->target_framerate) {
- duration += 1 / *frames.rbegin()->target_framerate;
- } else {
- RTC_CHECK_GT(frames.size(), 1u);
- duration += (frames.rbegin()->timestamp_rtp -
- std::next(frames.rbegin())->timestamp_rtp) /
- k90kHz;
- }
-
- return duration;
-}
-} // namespace
-
-std::vector<Frame> VideoCodecStatsImpl::Slice(
- absl::optional<Filter> filter) const {
- std::vector<Frame> frames;
- for (const auto& [frame_id, f] : frames_) {
- if (filter.has_value()) {
- if (filter->first_frame.has_value() &&
- f.frame_num < *filter->first_frame) {
- continue;
- }
- if (filter->last_frame.has_value() && f.frame_num > *filter->last_frame) {
- continue;
- }
- if (filter->spatial_idx.has_value() &&
- f.spatial_idx != *filter->spatial_idx) {
- continue;
- }
- if (filter->temporal_idx.has_value() &&
- f.temporal_idx > *filter->temporal_idx) {
- continue;
- }
- }
- frames.push_back(f);
- }
- return frames;
-}
-
-Stream VideoCodecStatsImpl::Aggregate(const std::vector<Frame>& frames) const {
- std::vector<Frame> superframes = Merge(frames);
- RTC_CHECK(!superframes.empty());
-
- LeakyBucket leacky_bucket;
- Stream stream;
- for (size_t i = 0; i < superframes.size(); ++i) {
- Frame& f = superframes[i];
- Timestamp time = RtpToTime(f.timestamp_rtp);
-
- if (!f.frame_size.IsZero()) {
- stream.width.AddSample(StatsSample(f.width, time));
- stream.height.AddSample(StatsSample(f.height, time));
- stream.frame_size_bytes.AddSample(
- StatsSample(f.frame_size.bytes(), time));
- stream.keyframe.AddSample(StatsSample(f.keyframe, time));
- if (f.qp) {
- stream.qp.AddSample(StatsSample(*f.qp, time));
- }
- }
-
- if (f.encoded) {
- stream.encode_time_ms.AddSample(StatsSample(f.encode_time.ms(), time));
- }
-
- if (f.decoded) {
- stream.decode_time_ms.AddSample(StatsSample(f.decode_time.ms(), time));
- }
-
- if (f.psnr) {
- stream.psnr.y.AddSample(StatsSample(f.psnr->y, time));
- stream.psnr.u.AddSample(StatsSample(f.psnr->u, time));
- stream.psnr.v.AddSample(StatsSample(f.psnr->v, time));
- }
-
- if (f.target_framerate) {
- stream.target_framerate_fps.AddSample(
- StatsSample(f.target_framerate->millihertz() / 1000.0, time));
- }
-
- if (f.target_bitrate) {
- stream.target_bitrate_kbps.AddSample(
- StatsSample(f.target_bitrate->bps() / 1000.0, time));
-
- int buffer_level_bits = leacky_bucket.Update(f);
- stream.transmission_time_ms.AddSample(
- StatsSample(buffer_level_bits * rtc::kNumMillisecsPerSec /
- f.target_bitrate->bps(),
- RtpToTime(f.timestamp_rtp)));
- }
- }
-
- TimeDelta duration = CalcTotalDuration(superframes);
- DataRate encoded_bitrate =
- DataSize::Bytes(stream.frame_size_bytes.GetSum()) / duration;
-
- int num_encoded_frames = stream.frame_size_bytes.NumSamples();
- Frequency encoded_framerate = num_encoded_frames / duration;
-
- absl::optional<double> bitrate_mismatch_pct;
- if (auto target_bitrate = superframes.begin()->target_bitrate;
- target_bitrate) {
- bitrate_mismatch_pct = 100.0 *
- (encoded_bitrate.bps() - target_bitrate->bps()) /
- target_bitrate->bps();
- }
-
- absl::optional<double> framerate_mismatch_pct;
- if (auto target_framerate = superframes.begin()->target_framerate;
- target_framerate) {
- framerate_mismatch_pct =
- 100.0 *
- (encoded_framerate.millihertz() - target_framerate->millihertz()) /
- target_framerate->millihertz();
- }
-
- for (auto& f : superframes) {
- Timestamp time = RtpToTime(f.timestamp_rtp);
- stream.encoded_bitrate_kbps.AddSample(
- StatsSample(encoded_bitrate.bps() / 1000.0, time));
-
- stream.encoded_framerate_fps.AddSample(
- StatsSample(encoded_framerate.millihertz() / 1000.0, time));
-
- if (bitrate_mismatch_pct) {
- stream.bitrate_mismatch_pct.AddSample(
- StatsSample(*bitrate_mismatch_pct, time));
- }
-
- if (framerate_mismatch_pct) {
- stream.framerate_mismatch_pct.AddSample(
- StatsSample(*framerate_mismatch_pct, time));
- }
- }
-
- return stream;
-}
-
-void VideoCodecStatsImpl::AddFrame(const Frame& frame) {
- FrameId frame_id{.timestamp_rtp = frame.timestamp_rtp,
- .spatial_idx = frame.spatial_idx};
- RTC_CHECK(frames_.find(frame_id) == frames_.end())
- << "Frame with timestamp_rtp=" << frame.timestamp_rtp
- << " and spatial_idx=" << frame.spatial_idx << " already exists";
-
- frames_[frame_id] = frame;
-}
-
-Frame* VideoCodecStatsImpl::GetFrame(uint32_t timestamp_rtp, int spatial_idx) {
- FrameId frame_id{.timestamp_rtp = timestamp_rtp, .spatial_idx = spatial_idx};
- if (frames_.find(frame_id) == frames_.end()) {
- return nullptr;
- }
- return &frames_.find(frame_id)->second;
-}
-
-} // namespace test
-} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/video_coding/codecs/test/video_codec_stats_impl.h b/third_party/libwebrtc/modules/video_coding/codecs/test/video_codec_stats_impl.h
deleted file mode 100644
index 77471d2ecd..0000000000
--- a/third_party/libwebrtc/modules/video_coding/codecs/test/video_codec_stats_impl.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (c) 2023 The WebRTC project authors. All Rights Reserved.
- *
- * Use of this source code is governed by a BSD-style license
- * that can be found in the LICENSE file in the root of the source
- * tree. An additional intellectual property rights grant can be found
- * in the file PATENTS. All contributing project authors may
- * be found in the AUTHORS file in the root of the source tree.
- */
-
-#ifndef MODULES_VIDEO_CODING_CODECS_TEST_VIDEO_CODEC_STATS_IMPL_H_
-#define MODULES_VIDEO_CODING_CODECS_TEST_VIDEO_CODEC_STATS_IMPL_H_
-
-#include <map>
-#include <string>
-#include <vector>
-
-#include "absl/types/optional.h"
-#include "api/test/video_codec_stats.h"
-
-namespace webrtc {
-namespace test {
-
-// Implementation of `VideoCodecStats`. This class is not thread-safe.
-class VideoCodecStatsImpl : public VideoCodecStats {
- public:
- std::vector<Frame> Slice(
- absl::optional<Filter> filter = absl::nullopt) const override;
-
- Stream Aggregate(const std::vector<Frame>& frames) const override;
-
- void AddFrame(const Frame& frame);
-
- // Returns raw pointers to previously added frame. If frame does not exist,
- // returns `nullptr`.
- Frame* GetFrame(uint32_t timestamp_rtp, int spatial_idx);
-
- private:
- struct FrameId {
- uint32_t timestamp_rtp;
- int spatial_idx;
-
- bool operator==(const FrameId& o) const {
- return timestamp_rtp == o.timestamp_rtp && spatial_idx == o.spatial_idx;
- }
-
- bool operator<(const FrameId& o) const {
- if (timestamp_rtp < o.timestamp_rtp)
- return true;
- if (timestamp_rtp == o.timestamp_rtp && spatial_idx < o.spatial_idx)
- return true;
- return false;
- }
- };
-
- std::map<FrameId, Frame> frames_;
-};
-
-} // namespace test
-} // namespace webrtc
-
-#endif // MODULES_VIDEO_CODING_CODECS_TEST_VIDEO_CODEC_STATS_IMPL_H_
diff --git a/third_party/libwebrtc/modules/video_coding/codecs/test/video_codec_stats_impl_unittest.cc b/third_party/libwebrtc/modules/video_coding/codecs/test/video_codec_stats_impl_unittest.cc
deleted file mode 100644
index ce11d5abe6..0000000000
--- a/third_party/libwebrtc/modules/video_coding/codecs/test/video_codec_stats_impl_unittest.cc
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright (c) 2023 The WebRTC project authors. All Rights Reserved.
- *
- * Use of this source code is governed by a BSD-style license
- * that can be found in the LICENSE file in the root of the source
- * tree. An additional intellectual property rights grant can be found
- * in the file PATENTS. All contributing project authors may
- * be found in the AUTHORS file in the root of the source tree.
- */
-
-#include "modules/video_coding/codecs/test/video_codec_stats_impl.h"
-
-#include <tuple>
-
-#include "absl/types/optional.h"
-#include "test/gmock.h"
-#include "test/gtest.h"
-
-namespace webrtc {
-namespace test {
-
-namespace {
-using ::testing::Return;
-using ::testing::Values;
-using Filter = VideoCodecStats::Filter;
-using Frame = VideoCodecStatsImpl::Frame;
-using Stream = VideoCodecStats::Stream;
-} // namespace
-
-TEST(VideoCodecStatsImpl, AddAndGetFrame) {
- VideoCodecStatsImpl stats;
- stats.AddFrame({.timestamp_rtp = 0, .spatial_idx = 0});
- stats.AddFrame({.timestamp_rtp = 0, .spatial_idx = 1});
- stats.AddFrame({.timestamp_rtp = 1, .spatial_idx = 0});
-
- Frame* fs = stats.GetFrame(/*timestamp_rtp=*/0, /*spatial_idx=*/0);
- ASSERT_NE(fs, nullptr);
- EXPECT_EQ(fs->timestamp_rtp, 0u);
- EXPECT_EQ(fs->spatial_idx, 0);
-
- fs = stats.GetFrame(/*timestamp_rtp=*/0, /*spatial_idx=*/1);
- ASSERT_NE(fs, nullptr);
- EXPECT_EQ(fs->timestamp_rtp, 0u);
- EXPECT_EQ(fs->spatial_idx, 1);
-
- fs = stats.GetFrame(/*timestamp_rtp=*/1, /*spatial_idx=*/0);
- ASSERT_NE(fs, nullptr);
- EXPECT_EQ(fs->timestamp_rtp, 1u);
- EXPECT_EQ(fs->spatial_idx, 0);
-
- fs = stats.GetFrame(/*timestamp_rtp=*/1, /*spatial_idx=*/1);
- EXPECT_EQ(fs, nullptr);
-}
-
-class VideoCodecStatsImplSlicingTest
- : public ::testing::TestWithParam<std::tuple<Filter, std::vector<int>>> {};
-
-TEST_P(VideoCodecStatsImplSlicingTest, Slice) {
- Filter filter = std::get<0>(GetParam());
- std::vector<int> expected_frames = std::get<1>(GetParam());
- std::vector<VideoCodecStats::Frame> frames = {
- {.frame_num = 0, .timestamp_rtp = 0, .spatial_idx = 0, .temporal_idx = 0},
- {.frame_num = 0, .timestamp_rtp = 0, .spatial_idx = 1, .temporal_idx = 0},
- {.frame_num = 1, .timestamp_rtp = 1, .spatial_idx = 0, .temporal_idx = 1},
- {.frame_num = 1,
- .timestamp_rtp = 1,
- .spatial_idx = 1,
- .temporal_idx = 1}};
-
- VideoCodecStatsImpl stats;
- stats.AddFrame(frames[0]);
- stats.AddFrame(frames[1]);
- stats.AddFrame(frames[2]);
- stats.AddFrame(frames[3]);
-
- std::vector<VideoCodecStats::Frame> slice = stats.Slice(filter);
- ASSERT_EQ(slice.size(), expected_frames.size());
- for (size_t i = 0; i < expected_frames.size(); ++i) {
- Frame& expected = frames[expected_frames[i]];
- EXPECT_EQ(slice[i].frame_num, expected.frame_num);
- EXPECT_EQ(slice[i].timestamp_rtp, expected.timestamp_rtp);
- EXPECT_EQ(slice[i].spatial_idx, expected.spatial_idx);
- EXPECT_EQ(slice[i].temporal_idx, expected.temporal_idx);
- }
-}
-
-INSTANTIATE_TEST_SUITE_P(
- All,
- VideoCodecStatsImplSlicingTest,
- ::testing::Values(
- std::make_tuple(Filter{}, std::vector<int>{0, 1, 2, 3}),
- std::make_tuple(Filter{.first_frame = 1}, std::vector<int>{2, 3}),
- std::make_tuple(Filter{.last_frame = 0}, std::vector<int>{0, 1}),
- std::make_tuple(Filter{.spatial_idx = 0}, std::vector<int>{0, 2}),
- std::make_tuple(Filter{.temporal_idx = 1},
- std::vector<int>{0, 1, 2, 3})));
-
-TEST(VideoCodecStatsImpl, AggregateBitrate) {
- std::vector<VideoCodecStats::Frame> frames = {
- {.frame_num = 0,
- .timestamp_rtp = 0,
- .frame_size = DataSize::Bytes(1000),
- .target_bitrate = DataRate::BytesPerSec(1000)},
- {.frame_num = 1,
- .timestamp_rtp = 90000,
- .frame_size = DataSize::Bytes(2000),
- .target_bitrate = DataRate::BytesPerSec(1000)}};
-
- Stream stream = VideoCodecStatsImpl().Aggregate(frames);
- EXPECT_EQ(stream.encoded_bitrate_kbps.GetAverage(), 12.0);
- EXPECT_EQ(stream.bitrate_mismatch_pct.GetAverage(), 50.0);
-}
-
-TEST(VideoCodecStatsImpl, AggregateFramerate) {
- std::vector<VideoCodecStats::Frame> frames = {
- {.frame_num = 0,
- .timestamp_rtp = 0,
- .frame_size = DataSize::Bytes(1),
- .target_framerate = Frequency::Hertz(1)},
- {.frame_num = 1,
- .timestamp_rtp = 90000,
- .frame_size = DataSize::Zero(),
- .target_framerate = Frequency::Hertz(1)}};
-
- Stream stream = VideoCodecStatsImpl().Aggregate(frames);
- EXPECT_EQ(stream.encoded_framerate_fps.GetAverage(), 0.5);
- EXPECT_EQ(stream.framerate_mismatch_pct.GetAverage(), -50.0);
-}
-
-TEST(VideoCodecStatsImpl, AggregateTransmissionTime) {
- std::vector<VideoCodecStats::Frame> frames = {
- {.frame_num = 0,
- .timestamp_rtp = 0,
- .frame_size = DataSize::Bytes(2),
- .target_bitrate = DataRate::BytesPerSec(1)},
- {.frame_num = 1,
- .timestamp_rtp = 90000,
- .frame_size = DataSize::Bytes(3),
- .target_bitrate = DataRate::BytesPerSec(1)}};
-
- Stream stream = VideoCodecStatsImpl().Aggregate(frames);
- ASSERT_EQ(stream.transmission_time_ms.NumSamples(), 2);
- ASSERT_EQ(stream.transmission_time_ms.GetSamples()[0], 2000);
- ASSERT_EQ(stream.transmission_time_ms.GetSamples()[1], 4000);
-}
-
-} // namespace test
-} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/video_coding/codecs/test/video_codec_test.cc b/third_party/libwebrtc/modules/video_coding/codecs/test/video_codec_test.cc
index 1c8fe97e84..60c2fcbb6e 100644
--- a/third_party/libwebrtc/modules/video_coding/codecs/test/video_codec_test.cc
+++ b/third_party/libwebrtc/modules/video_coding/codecs/test/video_codec_test.cc
@@ -8,41 +8,62 @@
* be found in the AUTHORS file in the root of the source tree.
*/
-#include "api/video_codecs/video_codec.h"
-
-#include <cstddef>
#include <memory>
#include <string>
#include <vector>
#include "absl/flags/flag.h"
#include "absl/functional/any_invocable.h"
-#include "api/test/create_video_codec_tester.h"
#include "api/test/metrics/global_metrics_logger_and_exporter.h"
-#include "api/test/video_codec_tester.h"
-#include "api/test/videocodec_test_stats.h"
#include "api/units/data_rate.h"
#include "api/units/frequency.h"
-#include "api/video/encoded_image.h"
-#include "api/video/i420_buffer.h"
#include "api/video/resolution.h"
-#include "api/video/video_frame.h"
-#include "api/video_codecs/scalability_mode.h"
-#include "api/video_codecs/video_decoder.h"
-#include "api/video_codecs/video_encoder.h"
-#include "media/engine/internal_decoder_factory.h"
-#include "media/engine/internal_encoder_factory.h"
-#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
-#include "modules/video_coding/include/video_error_codes.h"
-#include "modules/video_coding/svc/scalability_mode_util.h"
+#include "api/video_codecs/builtin_video_decoder_factory.h"
+#include "api/video_codecs/builtin_video_encoder_factory.h"
#if defined(WEBRTC_ANDROID)
#include "modules/video_coding/codecs/test/android_codec_factory_helper.h"
#endif
+#include "modules/video_coding/svc/scalability_mode_util.h"
#include "rtc_base/logging.h"
+#include "rtc_base/strings/string_builder.h"
#include "test/gtest.h"
#include "test/test_flags.h"
#include "test/testsupport/file_utils.h"
-#include "test/testsupport/frame_reader.h"
+#include "test/video_codec_tester.h"
+
+ABSL_FLAG(std::string,
+ video_name,
+ "FourPeople_1280x720_30",
+ "Name of input video sequence.");
+ABSL_FLAG(std::string,
+ encoder,
+ "libaom-av1",
+ "Encoder: libaom-av1, libvpx-vp9, libvpx-vp8, openh264, hw-vp8, "
+ "hw-vp9, hw-av1, hw-h264, hw-h265");
+ABSL_FLAG(std::string,
+ decoder,
+ "dav1d",
+ "Decoder: dav1d, libvpx-vp9, libvpx-vp8, ffmpeg-h264, hw-vp8, "
+ "hw-vp9, hw-av1, hw-h264, hw-h265");
+ABSL_FLAG(std::string, scalability_mode, "L1T1", "Scalability mode.");
+ABSL_FLAG(int, width, 1280, "Width.");
+ABSL_FLAG(int, height, 720, "Height.");
+ABSL_FLAG(std::vector<std::string>,
+ bitrate_kbps,
+ {"1024"},
+ "Encode target bitrate per layer (l0t0,l0t1,...l1t0,l1t1 and so on) "
+ "in kbps.");
+ABSL_FLAG(double,
+ framerate_fps,
+ 30.0,
+ "Encode target frame rate of the top temporal layer in fps.");
+ABSL_FLAG(int, num_frames, 300, "Number of frames to encode and/or decode.");
+ABSL_FLAG(std::string, test_name, "", "Test name.");
+ABSL_FLAG(bool, dump_decoder_input, false, "Dump decoder input.");
+ABSL_FLAG(bool, dump_decoder_output, false, "Dump decoder output.");
+ABSL_FLAG(bool, dump_encoder_input, false, "Dump encoder input.");
+ABSL_FLAG(bool, dump_encoder_output, false, "Dump encoder output.");
+ABSL_FLAG(bool, write_csv, false, "Write metrics to a CSV file.");
namespace webrtc {
namespace test {
@@ -50,6 +71,10 @@ namespace test {
namespace {
using ::testing::Combine;
using ::testing::Values;
+using VideoSourceSettings = VideoCodecTester::VideoSourceSettings;
+using EncodingSettings = VideoCodecTester::EncodingSettings;
+using VideoCodecStats = VideoCodecTester::VideoCodecStats;
+using Filter = VideoCodecStats::Filter;
using PacingMode = VideoCodecTester::PacingSettings::PacingMode;
struct VideoInfo {
@@ -58,405 +83,93 @@ struct VideoInfo {
Frequency framerate;
};
-struct LayerId {
- int spatial_idx;
- int temporal_idx;
-
- bool operator==(const LayerId& o) const {
- return spatial_idx == o.spatial_idx && temporal_idx == o.temporal_idx;
- }
-
- bool operator<(const LayerId& o) const {
- if (spatial_idx < o.spatial_idx)
- return true;
- if (spatial_idx == o.spatial_idx && temporal_idx < o.temporal_idx)
- return true;
- return false;
- }
-};
-
-struct EncodingSettings {
- ScalabilityMode scalability_mode;
- struct LayerSettings {
- Resolution resolution;
- Frequency framerate;
- DataRate bitrate;
- };
- std::map<LayerId, LayerSettings> layer_settings;
-
- bool IsSameSettings(const EncodingSettings& other) const {
- if (scalability_mode != other.scalability_mode) {
- return false;
- }
-
- for (auto [layer_id, layer] : layer_settings) {
- const auto& other_layer = other.layer_settings.at(layer_id);
- if (layer.resolution != other_layer.resolution) {
- return false;
- }
- }
-
- return true;
- }
-
- bool IsSameRate(const EncodingSettings& other) const {
- for (auto [layer_id, layer] : layer_settings) {
- const auto& other_layer = other.layer_settings.at(layer_id);
- if (layer.bitrate != other_layer.bitrate ||
- layer.framerate != other_layer.framerate) {
- return false;
- }
- }
-
- return true;
- }
-};
-
-const VideoInfo kFourPeople_1280x720_30 = {
- .name = "FourPeople_1280x720_30",
- .resolution = {.width = 1280, .height = 720},
- .framerate = Frequency::Hertz(30)};
-
-class TestRawVideoSource : public VideoCodecTester::RawVideoSource {
- public:
- static constexpr Frequency k90kHz = Frequency::Hertz(90000);
-
- TestRawVideoSource(VideoInfo video_info,
- const std::map<int, EncodingSettings>& frame_settings,
- int num_frames)
- : video_info_(video_info),
- frame_settings_(frame_settings),
- num_frames_(num_frames),
- frame_num_(0),
- // Start with non-zero timestamp to force using frame RTP timestamps in
- // IvfFrameWriter.
- timestamp_rtp_(90000) {
- // Ensure settings for the first frame are provided.
- RTC_CHECK_GT(frame_settings_.size(), 0u);
- RTC_CHECK_EQ(frame_settings_.begin()->first, 0);
-
- frame_reader_ = CreateYuvFrameReader(
- ResourcePath(video_info_.name, "yuv"), video_info_.resolution,
- YuvFrameReaderImpl::RepeatMode::kPingPong);
- RTC_CHECK(frame_reader_);
- }
-
- // Pulls next frame. Frame RTP timestamp is set accordingly to
- // `EncodingSettings::framerate`.
- absl::optional<VideoFrame> PullFrame() override {
- if (frame_num_ >= num_frames_) {
- return absl::nullopt; // End of stream.
- }
-
- const EncodingSettings& encoding_settings =
- std::prev(frame_settings_.upper_bound(frame_num_))->second;
-
- Resolution resolution =
- encoding_settings.layer_settings.begin()->second.resolution;
- Frequency framerate =
- encoding_settings.layer_settings.begin()->second.framerate;
-
- int pulled_frame;
- auto buffer = frame_reader_->PullFrame(
- &pulled_frame, resolution,
- {.num = static_cast<int>(framerate.millihertz()),
- .den = static_cast<int>(video_info_.framerate.millihertz())});
- RTC_CHECK(buffer) << "Cannot pull frame " << frame_num_;
-
- auto frame = VideoFrame::Builder()
- .set_video_frame_buffer(buffer)
- .set_timestamp_rtp(timestamp_rtp_)
- .set_timestamp_us((timestamp_rtp_ / k90kHz).us())
- .build();
-
- pulled_frames_[timestamp_rtp_] = pulled_frame;
- timestamp_rtp_ += k90kHz / framerate;
- ++frame_num_;
-
- return frame;
- }
-
- // Reads frame specified by `timestamp_rtp`, scales it to `resolution` and
- // returns. Frame with the given `timestamp_rtp` is expected to be pulled
- // before.
- VideoFrame GetFrame(uint32_t timestamp_rtp, Resolution resolution) override {
- RTC_CHECK(pulled_frames_.find(timestamp_rtp) != pulled_frames_.end())
- << "Frame with RTP timestamp " << timestamp_rtp
- << " was not pulled before";
- auto buffer =
- frame_reader_->ReadFrame(pulled_frames_[timestamp_rtp], resolution);
- return VideoFrame::Builder()
- .set_video_frame_buffer(buffer)
- .set_timestamp_rtp(timestamp_rtp)
- .build();
- }
-
- protected:
- VideoInfo video_info_;
- std::unique_ptr<FrameReader> frame_reader_;
- const std::map<int, EncodingSettings>& frame_settings_;
- int num_frames_;
- int frame_num_;
- uint32_t timestamp_rtp_;
- std::map<uint32_t, int> pulled_frames_;
-};
-
-class TestEncoder : public VideoCodecTester::Encoder,
- public EncodedImageCallback {
- public:
- TestEncoder(std::unique_ptr<VideoEncoder> encoder,
- const std::string codec_type,
- const std::map<int, EncodingSettings>& frame_settings)
- : encoder_(std::move(encoder)),
- codec_type_(codec_type),
- frame_settings_(frame_settings),
- frame_num_(0) {
- // Ensure settings for the first frame is provided.
- RTC_CHECK_GT(frame_settings_.size(), 0u);
- RTC_CHECK_EQ(frame_settings_.begin()->first, 0);
-
- encoder_->RegisterEncodeCompleteCallback(this);
- }
-
- void Initialize() override {
- const EncodingSettings& first_frame_settings = frame_settings_.at(0);
- Configure(first_frame_settings);
- SetRates(first_frame_settings);
- }
-
- void Encode(const VideoFrame& frame, EncodeCallback callback) override {
- {
- MutexLock lock(&mutex_);
- callbacks_[frame.timestamp()] = std::move(callback);
- }
-
- if (auto fs = frame_settings_.find(frame_num_);
- fs != frame_settings_.begin() && fs != frame_settings_.end()) {
- if (!fs->second.IsSameSettings(std::prev(fs)->second)) {
- Configure(fs->second);
- } else if (!fs->second.IsSameRate(std::prev(fs)->second)) {
- SetRates(fs->second);
- }
- }
-
- encoder_->Encode(frame, nullptr);
- ++frame_num_;
- }
-
- void Flush() override {
- // TODO(webrtc:14852): For codecs which buffer frames we need a to
- // flush them to get last frames. Add such functionality to VideoEncoder
- // API. On Android it will map directly to `MediaCodec.flush()`.
- encoder_->Release();
- }
-
- VideoEncoder* encoder() { return encoder_.get(); }
-
- protected:
- Result OnEncodedImage(const EncodedImage& encoded_image,
- const CodecSpecificInfo* codec_specific_info) override {
- MutexLock lock(&mutex_);
- auto cb = callbacks_.find(encoded_image.RtpTimestamp());
- RTC_CHECK(cb != callbacks_.end());
- cb->second(encoded_image);
-
- callbacks_.erase(callbacks_.begin(), cb);
- return Result(Result::Error::OK);
- }
-
- void Configure(const EncodingSettings& es) {
- VideoCodec vc;
- const EncodingSettings::LayerSettings& layer_settings =
- es.layer_settings.begin()->second;
- vc.width = layer_settings.resolution.width;
- vc.height = layer_settings.resolution.height;
- const DataRate& bitrate = layer_settings.bitrate;
- vc.startBitrate = bitrate.kbps();
- vc.maxBitrate = bitrate.kbps();
- vc.minBitrate = 0;
- vc.maxFramerate = static_cast<uint32_t>(layer_settings.framerate.hertz());
- vc.active = true;
- vc.qpMax = 63;
- vc.numberOfSimulcastStreams = 0;
- vc.mode = webrtc::VideoCodecMode::kRealtimeVideo;
- vc.SetFrameDropEnabled(true);
- vc.SetScalabilityMode(es.scalability_mode);
-
- vc.codecType = PayloadStringToCodecType(codec_type_);
- if (vc.codecType == kVideoCodecVP8) {
- *(vc.VP8()) = VideoEncoder::GetDefaultVp8Settings();
- } else if (vc.codecType == kVideoCodecVP9) {
- *(vc.VP9()) = VideoEncoder::GetDefaultVp9Settings();
- } else if (vc.codecType == kVideoCodecH264) {
- *(vc.H264()) = VideoEncoder::GetDefaultH264Settings();
- }
-
- VideoEncoder::Settings ves(
- VideoEncoder::Capabilities(/*loss_notification=*/false),
- /*number_of_cores=*/1,
- /*max_payload_size=*/1440);
-
- int result = encoder_->InitEncode(&vc, ves);
- ASSERT_EQ(result, WEBRTC_VIDEO_CODEC_OK);
-
- SetRates(es);
- }
-
- void SetRates(const EncodingSettings& es) {
- VideoEncoder::RateControlParameters rc;
- int num_spatial_layers =
- ScalabilityModeToNumSpatialLayers(es.scalability_mode);
- int num_temporal_layers =
- ScalabilityModeToNumSpatialLayers(es.scalability_mode);
- for (int sidx = 0; sidx < num_spatial_layers; ++sidx) {
- for (int tidx = 0; tidx < num_temporal_layers; ++tidx) {
- auto layer_settings =
- es.layer_settings.find({.spatial_idx = sidx, .temporal_idx = tidx});
- RTC_CHECK(layer_settings != es.layer_settings.end())
- << "Bitrate for layer S=" << sidx << " T=" << tidx << " is not set";
- rc.bitrate.SetBitrate(sidx, tidx, layer_settings->second.bitrate.bps());
- }
- }
-
- rc.framerate_fps =
- es.layer_settings.begin()->second.framerate.millihertz() / 1000.0;
- encoder_->SetRates(rc);
- }
-
- std::unique_ptr<VideoEncoder> encoder_;
- const std::string codec_type_;
- const std::map<int, EncodingSettings>& frame_settings_;
- int frame_num_;
- std::map<uint32_t, EncodeCallback> callbacks_ RTC_GUARDED_BY(mutex_);
- Mutex mutex_;
-};
-
-class TestDecoder : public VideoCodecTester::Decoder,
- public DecodedImageCallback {
- public:
- TestDecoder(std::unique_ptr<VideoDecoder> decoder,
- const std::string codec_type)
- : decoder_(std::move(decoder)), codec_type_(codec_type) {
- decoder_->RegisterDecodeCompleteCallback(this);
- }
-
- void Initialize() override {
- VideoDecoder::Settings ds;
- ds.set_codec_type(PayloadStringToCodecType(codec_type_));
- ds.set_number_of_cores(1);
- ds.set_max_render_resolution({1280, 720});
-
- bool result = decoder_->Configure(ds);
- ASSERT_TRUE(result);
- }
-
- void Decode(const EncodedImage& frame, DecodeCallback callback) override {
- {
- MutexLock lock(&mutex_);
- callbacks_[frame.RtpTimestamp()] = std::move(callback);
- }
-
- decoder_->Decode(frame, /*render_time_ms=*/0);
- }
-
- void Flush() override {
- // TODO(webrtc:14852): For codecs which buffer frames we need a to
- // flush them to get last frames. Add such functionality to VideoDecoder
- // API. On Android it will map directly to `MediaCodec.flush()`.
- decoder_->Release();
- }
-
- VideoDecoder* decoder() { return decoder_.get(); }
-
- protected:
- int Decoded(VideoFrame& decoded_frame) override {
- MutexLock lock(&mutex_);
- auto cb = callbacks_.find(decoded_frame.timestamp());
- RTC_CHECK(cb != callbacks_.end());
- cb->second(decoded_frame);
+const std::map<std::string, VideoInfo> kRawVideos = {
+ {"FourPeople_1280x720_30",
+ {.name = "FourPeople_1280x720_30",
+ .resolution = {.width = 1280, .height = 720},
+ .framerate = Frequency::Hertz(30)}},
+ {"vidyo1_1280x720_30",
+ {.name = "vidyo1_1280x720_30",
+ .resolution = {.width = 1280, .height = 720},
+ .framerate = Frequency::Hertz(30)}},
+ {"vidyo4_1280x720_30",
+ {.name = "vidyo4_1280x720_30",
+ .resolution = {.width = 1280, .height = 720},
+ .framerate = Frequency::Hertz(30)}},
+ {"KristenAndSara_1280x720_30",
+ {.name = "KristenAndSara_1280x720_30",
+ .resolution = {.width = 1280, .height = 720},
+ .framerate = Frequency::Hertz(30)}},
+ {"Johnny_1280x720_30",
+ {.name = "Johnny_1280x720_30",
+ .resolution = {.width = 1280, .height = 720},
+ .framerate = Frequency::Hertz(30)}}};
+
+static constexpr Frequency k90kHz = Frequency::Hertz(90000);
+
+std::string CodecNameToCodecType(std::string name) {
+ if (name.find("av1") != std::string::npos) {
+ return "AV1";
+ }
+ if (name.find("vp9") != std::string::npos) {
+ return "VP9";
+ }
+ if (name.find("vp8") != std::string::npos) {
+ return "VP8";
+ }
+ if (name.find("h264") != std::string::npos) {
+ return "H264";
+ }
+ if (name.find("h265") != std::string::npos) {
+ return "H265";
+ }
+ RTC_CHECK_NOTREACHED();
+}
- callbacks_.erase(callbacks_.begin(), cb);
- return WEBRTC_VIDEO_CODEC_OK;
+// TODO(webrtc:14852): Make Create[Encoder,Decoder]Factory to work with codec
+// name directly.
+std::string CodecNameToCodecImpl(std::string name) {
+ if (name.find("hw") != std::string::npos) {
+ return "mediacodec";
}
-
- std::unique_ptr<VideoDecoder> decoder_;
- const std::string codec_type_;
- std::map<uint32_t, DecodeCallback> callbacks_ RTC_GUARDED_BY(mutex_);
- Mutex mutex_;
-};
-
-std::unique_ptr<TestRawVideoSource> CreateVideoSource(
- const VideoInfo& video,
- const std::map<int, EncodingSettings>& frame_settings,
- int num_frames) {
- return std::make_unique<TestRawVideoSource>(video, frame_settings,
- num_frames);
+ return "builtin";
}
-std::unique_ptr<TestEncoder> CreateEncoder(
- std::string type,
- std::string impl,
- const std::map<int, EncodingSettings>& frame_settings) {
- std::unique_ptr<VideoEncoderFactory> factory;
+std::unique_ptr<VideoEncoderFactory> CreateEncoderFactory(std::string impl) {
if (impl == "builtin") {
- factory = std::make_unique<InternalEncoderFactory>();
- } else if (impl == "mediacodec") {
+ return CreateBuiltinVideoEncoderFactory();
+ }
#if defined(WEBRTC_ANDROID)
- InitializeAndroidObjects();
- factory = CreateAndroidEncoderFactory();
+ InitializeAndroidObjects();
+ return CreateAndroidEncoderFactory();
+#else
+ return nullptr;
#endif
- }
- std::unique_ptr<VideoEncoder> encoder =
- factory->CreateVideoEncoder(SdpVideoFormat(type));
- if (encoder == nullptr) {
- return nullptr;
- }
- return std::make_unique<TestEncoder>(std::move(encoder), type,
- frame_settings);
}
-std::unique_ptr<TestDecoder> CreateDecoder(std::string type, std::string impl) {
- std::unique_ptr<VideoDecoderFactory> factory;
+std::unique_ptr<VideoDecoderFactory> CreateDecoderFactory(std::string impl) {
if (impl == "builtin") {
- factory = std::make_unique<InternalDecoderFactory>();
- } else if (impl == "mediacodec") {
+ return CreateBuiltinVideoDecoderFactory();
+ }
#if defined(WEBRTC_ANDROID)
- InitializeAndroidObjects();
- factory = CreateAndroidDecoderFactory();
+ InitializeAndroidObjects();
+ return CreateAndroidDecoderFactory();
+#else
+ return nullptr;
#endif
- }
- std::unique_ptr<VideoDecoder> decoder =
- factory->CreateVideoDecoder(SdpVideoFormat(type));
- if (decoder == nullptr) {
- return nullptr;
- }
- return std::make_unique<TestDecoder>(std::move(decoder), type);
}
-void SetTargetRates(const std::map<int, EncodingSettings>& frame_settings,
- std::vector<VideoCodecStats::Frame>& frames) {
- for (VideoCodecStats::Frame& f : frames) {
- const EncodingSettings& encoding_settings =
- std::prev(frame_settings.upper_bound(f.frame_num))->second;
- LayerId layer_id = {.spatial_idx = f.spatial_idx,
- .temporal_idx = f.temporal_idx};
- RTC_CHECK(encoding_settings.layer_settings.find(layer_id) !=
- encoding_settings.layer_settings.end())
- << "Frame frame_num=" << f.frame_num
- << " belongs to spatial_idx=" << f.spatial_idx
- << " temporal_idx=" << f.temporal_idx
- << " but settings for this layer are not provided.";
- const EncodingSettings::LayerSettings& layer_settings =
- encoding_settings.layer_settings.at(layer_id);
- f.target_bitrate = layer_settings.bitrate;
- f.target_framerate = layer_settings.framerate;
+std::string TestName() {
+ std::string test_name = absl::GetFlag(FLAGS_test_name);
+ if (!test_name.empty()) {
+ return test_name;
}
+ return ::testing::UnitTest::GetInstance()->current_test_info()->name();
}
std::string TestOutputPath() {
std::string output_path =
- OutputPath() +
- ::testing::UnitTest::GetInstance()->current_test_info()->name();
+ (rtc::StringBuilder() << OutputPath() << TestName()).str();
std::string output_dir = DirName(output_path);
bool result = CreateDir(output_dir);
RTC_CHECK(result) << "Cannot create " << output_dir;
@@ -465,116 +178,120 @@ std::string TestOutputPath() {
} // namespace
std::unique_ptr<VideoCodecStats> RunEncodeDecodeTest(
- std::string codec_type,
std::string codec_impl,
const VideoInfo& video_info,
- const std::map<int, EncodingSettings>& frame_settings,
- int num_frames,
- bool save_codec_input,
- bool save_codec_output) {
- std::unique_ptr<TestRawVideoSource> video_source =
- CreateVideoSource(video_info, frame_settings, num_frames);
-
- std::unique_ptr<TestEncoder> encoder =
- CreateEncoder(codec_type, codec_impl, frame_settings);
- if (encoder == nullptr) {
+ const std::map<uint32_t, EncodingSettings>& encoding_settings) {
+ VideoSourceSettings source_settings{
+ .file_path = ResourcePath(video_info.name, "yuv"),
+ .resolution = video_info.resolution,
+ .framerate = video_info.framerate};
+
+ const SdpVideoFormat& sdp_video_format =
+ encoding_settings.begin()->second.sdp_video_format;
+
+ std::unique_ptr<VideoEncoderFactory> encoder_factory =
+ CreateEncoderFactory(codec_impl);
+ if (!encoder_factory
+ ->QueryCodecSupport(sdp_video_format,
+ /*scalability_mode=*/absl::nullopt)
+ .is_supported) {
+ RTC_LOG(LS_WARNING) << "No encoder for video format "
+ << sdp_video_format.ToString();
return nullptr;
}
- std::unique_ptr<TestDecoder> decoder = CreateDecoder(codec_type, codec_impl);
- if (decoder == nullptr) {
- // If platform decoder is not available try built-in one.
- if (codec_impl == "builtin") {
- return nullptr;
- }
-
- decoder = CreateDecoder(codec_type, "builtin");
- if (decoder == nullptr) {
+ std::unique_ptr<VideoDecoderFactory> decoder_factory =
+ CreateDecoderFactory(codec_impl);
+ if (!decoder_factory
+ ->QueryCodecSupport(sdp_video_format,
+ /*reference_scaling=*/false)
+ .is_supported) {
+ decoder_factory = CreateDecoderFactory("builtin");
+ if (!decoder_factory
+ ->QueryCodecSupport(sdp_video_format,
+ /*reference_scaling=*/false)
+ .is_supported) {
+ RTC_LOG(LS_WARNING) << "No decoder for video format "
+ << sdp_video_format.ToString();
return nullptr;
}
}
- RTC_LOG(LS_INFO) << "Encoder implementation: "
- << encoder->encoder()->GetEncoderInfo().implementation_name;
- RTC_LOG(LS_INFO) << "Decoder implementation: "
- << decoder->decoder()->GetDecoderInfo().implementation_name;
+ std::string output_path = TestOutputPath();
VideoCodecTester::EncoderSettings encoder_settings;
- encoder_settings.pacing.mode =
- encoder->encoder()->GetEncoderInfo().is_hardware_accelerated
- ? PacingMode::kRealTime
- : PacingMode::kNoPacing;
+ encoder_settings.pacing_settings.mode =
+ codec_impl == "builtin" ? PacingMode::kNoPacing : PacingMode::kRealTime;
+ if (absl::GetFlag(FLAGS_dump_encoder_input)) {
+ encoder_settings.encoder_input_base_path = output_path + "_enc_input";
+ }
+ if (absl::GetFlag(FLAGS_dump_encoder_output)) {
+ encoder_settings.encoder_output_base_path = output_path + "_enc_output";
+ }
VideoCodecTester::DecoderSettings decoder_settings;
- decoder_settings.pacing.mode =
- decoder->decoder()->GetDecoderInfo().is_hardware_accelerated
- ? PacingMode::kRealTime
- : PacingMode::kNoPacing;
-
- std::string output_path = TestOutputPath();
- if (save_codec_input) {
- encoder_settings.encoder_input_base_path = output_path + "_enc_input";
+ decoder_settings.pacing_settings.mode =
+ codec_impl == "builtin" ? PacingMode::kNoPacing : PacingMode::kRealTime;
+ if (absl::GetFlag(FLAGS_dump_decoder_input)) {
decoder_settings.decoder_input_base_path = output_path + "_dec_input";
}
- if (save_codec_output) {
- encoder_settings.encoder_output_base_path = output_path + "_enc_output";
+ if (absl::GetFlag(FLAGS_dump_decoder_output)) {
decoder_settings.decoder_output_base_path = output_path + "_dec_output";
}
- std::unique_ptr<VideoCodecTester> tester = CreateVideoCodecTester();
- return tester->RunEncodeDecodeTest(video_source.get(), encoder.get(),
- decoder.get(), encoder_settings,
- decoder_settings);
+ return VideoCodecTester::RunEncodeDecodeTest(
+ source_settings, encoder_factory.get(), decoder_factory.get(),
+ encoder_settings, decoder_settings, encoding_settings);
}
std::unique_ptr<VideoCodecStats> RunEncodeTest(
std::string codec_type,
std::string codec_impl,
const VideoInfo& video_info,
- const std::map<int, EncodingSettings>& frame_settings,
- int num_frames,
- bool save_codec_input,
- bool save_codec_output) {
- std::unique_ptr<TestRawVideoSource> video_source =
- CreateVideoSource(video_info, frame_settings, num_frames);
-
- std::unique_ptr<TestEncoder> encoder =
- CreateEncoder(codec_type, codec_impl, frame_settings);
- if (encoder == nullptr) {
+ const std::map<uint32_t, EncodingSettings>& encoding_settings) {
+ VideoSourceSettings source_settings{
+ .file_path = ResourcePath(video_info.name, "yuv"),
+ .resolution = video_info.resolution,
+ .framerate = video_info.framerate};
+
+ const SdpVideoFormat& sdp_video_format =
+ encoding_settings.begin()->second.sdp_video_format;
+
+ std::unique_ptr<VideoEncoderFactory> encoder_factory =
+ CreateEncoderFactory(codec_impl);
+ if (!encoder_factory
+ ->QueryCodecSupport(sdp_video_format,
+ /*scalability_mode=*/absl::nullopt)
+ .is_supported) {
+ RTC_LOG(LS_WARNING) << "No encoder for video format "
+ << sdp_video_format.ToString();
return nullptr;
}
- RTC_LOG(LS_INFO) << "Encoder implementation: "
- << encoder->encoder()->GetEncoderInfo().implementation_name;
-
- VideoCodecTester::EncoderSettings encoder_settings;
- encoder_settings.pacing.mode =
- encoder->encoder()->GetEncoderInfo().is_hardware_accelerated
- ? PacingMode::kRealTime
- : PacingMode::kNoPacing;
-
std::string output_path = TestOutputPath();
- if (save_codec_input) {
+ VideoCodecTester::EncoderSettings encoder_settings;
+ encoder_settings.pacing_settings.mode =
+ codec_impl == "builtin" ? PacingMode::kNoPacing : PacingMode::kRealTime;
+ if (absl::GetFlag(FLAGS_dump_encoder_input)) {
encoder_settings.encoder_input_base_path = output_path + "_enc_input";
}
- if (save_codec_output) {
+ if (absl::GetFlag(FLAGS_dump_encoder_output)) {
encoder_settings.encoder_output_base_path = output_path + "_enc_output";
}
- std::unique_ptr<VideoCodecTester> tester = CreateVideoCodecTester();
- return tester->RunEncodeTest(video_source.get(), encoder.get(),
- encoder_settings);
+ return VideoCodecTester::RunEncodeTest(source_settings, encoder_factory.get(),
+ encoder_settings, encoding_settings);
}
-class SpatialQualityTest : public ::testing::TestWithParam<
- std::tuple</*codec_type=*/std::string,
- /*codec_impl=*/std::string,
- VideoInfo,
- std::tuple</*width=*/int,
- /*height=*/int,
- /*framerate_fps=*/double,
- /*bitrate_kbps=*/int,
- /*min_psnr=*/double>>> {
+class SpatialQualityTest : public ::testing::TestWithParam<std::tuple<
+ /*codec_type=*/std::string,
+ /*codec_impl=*/std::string,
+ VideoInfo,
+ std::tuple</*width=*/int,
+ /*height=*/int,
+ /*framerate_fps=*/double,
+ /*bitrate_kbps=*/int,
+ /*expected_min_psnr=*/double>>> {
public:
static std::string TestParamsToString(
const ::testing::TestParamInfo<SpatialQualityTest::ParamType>& info) {
@@ -590,41 +307,35 @@ class SpatialQualityTest : public ::testing::TestWithParam<
TEST_P(SpatialQualityTest, SpatialQuality) {
auto [codec_type, codec_impl, video_info, coding_settings] = GetParam();
- auto [width, height, framerate_fps, bitrate_kbps, psnr] = coding_settings;
-
- std::map<int, EncodingSettings> frame_settings = {
- {0,
- {.scalability_mode = ScalabilityMode::kL1T1,
- .layer_settings = {
- {LayerId{.spatial_idx = 0, .temporal_idx = 0},
- {.resolution = {.width = width, .height = height},
- .framerate = Frequency::MilliHertz(1000 * framerate_fps),
- .bitrate = DataRate::KilobitsPerSec(bitrate_kbps)}}}}}};
-
+ auto [width, height, framerate_fps, bitrate_kbps, expected_min_psnr] =
+ coding_settings;
int duration_s = 10;
int num_frames = duration_s * framerate_fps;
- std::unique_ptr<VideoCodecStats> stats = RunEncodeDecodeTest(
- codec_type, codec_impl, video_info, frame_settings, num_frames,
- /*save_codec_input=*/false, /*save_codec_output=*/false);
+ std::map<uint32_t, EncodingSettings> frames_settings =
+ VideoCodecTester::CreateEncodingSettings(
+ codec_type, /*scalability_mode=*/"L1T1", width, height,
+ {bitrate_kbps}, framerate_fps, num_frames);
+
+ std::unique_ptr<VideoCodecStats> stats =
+ RunEncodeDecodeTest(codec_impl, video_info, frames_settings);
VideoCodecStats::Stream stream;
if (stats != nullptr) {
- std::vector<VideoCodecStats::Frame> frames = stats->Slice();
- SetTargetRates(frame_settings, frames);
- stream = stats->Aggregate(frames);
+ stream = stats->Aggregate(Filter{});
if (absl::GetFlag(FLAGS_webrtc_quick_perf_test)) {
- EXPECT_GE(stream.psnr.y.GetAverage(), psnr);
+ EXPECT_GE(stream.psnr.y.GetAverage(), expected_min_psnr);
}
}
stream.LogMetrics(
GetGlobalMetricsLogger(),
::testing::UnitTest::GetInstance()->current_test_info()->name(),
+ /*prefix=*/"",
/*metadata=*/
- {{"codec_type", codec_type},
- {"codec_impl", codec_impl},
- {"video_name", video_info.name}});
+ {{"video_name", video_info.name},
+ {"codec_type", codec_type},
+ {"codec_impl", codec_impl}});
}
INSTANTIATE_TEST_SUITE_P(
@@ -636,7 +347,7 @@ INSTANTIATE_TEST_SUITE_P(
#else
Values("builtin"),
#endif
- Values(kFourPeople_1280x720_30),
+ Values(kRawVideos.at("FourPeople_1280x720_30")),
Values(std::make_tuple(320, 180, 30, 32, 28),
std::make_tuple(320, 180, 30, 64, 30),
std::make_tuple(320, 180, 30, 128, 33),
@@ -671,33 +382,32 @@ TEST_P(BitrateAdaptationTest, BitrateAdaptation) {
auto [codec_type, codec_impl, video_info, bitrate_kbps] = GetParam();
int duration_s = 10; // Duration of fixed rate interval.
- int first_frame = duration_s * video_info.framerate.millihertz() / 1000;
- int num_frames = 2 * duration_s * video_info.framerate.millihertz() / 1000;
-
- std::map<int, EncodingSettings> frame_settings = {
- {0,
- {.layer_settings = {{LayerId{.spatial_idx = 0, .temporal_idx = 0},
- {.resolution = {.width = 640, .height = 360},
- .framerate = video_info.framerate,
- .bitrate = DataRate::KilobitsPerSec(
- bitrate_kbps.first)}}}}},
- {first_frame,
- {.layer_settings = {
- {LayerId{.spatial_idx = 0, .temporal_idx = 0},
- {.resolution = {.width = 640, .height = 360},
- .framerate = video_info.framerate,
- .bitrate = DataRate::KilobitsPerSec(bitrate_kbps.second)}}}}}};
-
- std::unique_ptr<VideoCodecStats> stats = RunEncodeTest(
- codec_type, codec_impl, video_info, frame_settings, num_frames,
- /*save_codec_input=*/false, /*save_codec_output=*/false);
+ int num_frames =
+ static_cast<int>(duration_s * video_info.framerate.hertz<double>());
+
+ std::map<uint32_t, EncodingSettings> encoding_settings =
+ VideoCodecTester::CreateEncodingSettings(
+ codec_type, /*scalability_mode=*/"L1T1",
+ /*width=*/640, /*height=*/360, {bitrate_kbps.first},
+ /*framerate_fps=*/30, num_frames);
+
+ uint32_t initial_timestamp_rtp =
+ encoding_settings.rbegin()->first + k90kHz / Frequency::Hertz(30);
+
+ std::map<uint32_t, EncodingSettings> encoding_settings2 =
+ VideoCodecTester::CreateEncodingSettings(
+ codec_type, /*scalability_mode=*/"L1T1",
+ /*width=*/640, /*height=*/360, {bitrate_kbps.second},
+ /*framerate_fps=*/30, num_frames, initial_timestamp_rtp);
+
+ encoding_settings.merge(encoding_settings2);
+
+ std::unique_ptr<VideoCodecStats> stats =
+ RunEncodeTest(codec_type, codec_impl, video_info, encoding_settings);
VideoCodecStats::Stream stream;
if (stats != nullptr) {
- std::vector<VideoCodecStats::Frame> frames =
- stats->Slice(VideoCodecStats::Filter{.first_frame = first_frame});
- SetTargetRates(frame_settings, frames);
- stream = stats->Aggregate(frames);
+ stream = stats->Aggregate({.min_timestamp_rtp = initial_timestamp_rtp});
if (absl::GetFlag(FLAGS_webrtc_quick_perf_test)) {
EXPECT_NEAR(stream.bitrate_mismatch_pct.GetAverage(), 0, 10);
EXPECT_NEAR(stream.framerate_mismatch_pct.GetAverage(), 0, 10);
@@ -707,6 +417,7 @@ TEST_P(BitrateAdaptationTest, BitrateAdaptation) {
stream.LogMetrics(
GetGlobalMetricsLogger(),
::testing::UnitTest::GetInstance()->current_test_info()->name(),
+ /*prefix=*/"",
/*metadata=*/
{{"codec_type", codec_type},
{"codec_impl", codec_impl},
@@ -715,18 +426,18 @@ TEST_P(BitrateAdaptationTest, BitrateAdaptation) {
std::to_string(bitrate_kbps.second)}});
}
-INSTANTIATE_TEST_SUITE_P(All,
- BitrateAdaptationTest,
- Combine(Values("AV1", "VP9", "VP8", "H264", "H265"),
+INSTANTIATE_TEST_SUITE_P(
+ All,
+ BitrateAdaptationTest,
+ Combine(Values("AV1", "VP9", "VP8", "H264", "H265"),
#if defined(WEBRTC_ANDROID)
- Values("builtin", "mediacodec"),
+ Values("builtin", "mediacodec"),
#else
- Values("builtin"),
+ Values("builtin"),
#endif
- Values(kFourPeople_1280x720_30),
- Values(std::pair(1024, 512),
- std::pair(512, 1024))),
- BitrateAdaptationTest::TestParamsToString);
+ Values(kRawVideos.at("FourPeople_1280x720_30")),
+ Values(std::pair(1024, 512), std::pair(512, 1024))),
+ BitrateAdaptationTest::TestParamsToString);
class FramerateAdaptationTest
: public ::testing::TestWithParam<std::tuple</*codec_type=*/std::string,
@@ -749,34 +460,34 @@ TEST_P(FramerateAdaptationTest, FramerateAdaptation) {
auto [codec_type, codec_impl, video_info, framerate_fps] = GetParam();
int duration_s = 10; // Duration of fixed rate interval.
- int first_frame = static_cast<int>(duration_s * framerate_fps.first);
- int num_frames = static_cast<int>(
- duration_s * (framerate_fps.first + framerate_fps.second));
-
- std::map<int, EncodingSettings> frame_settings = {
- {0,
- {.layer_settings = {{LayerId{.spatial_idx = 0, .temporal_idx = 0},
- {.resolution = {.width = 640, .height = 360},
- .framerate = Frequency::MilliHertz(
- 1000 * framerate_fps.first),
- .bitrate = DataRate::KilobitsPerSec(512)}}}}},
- {first_frame,
- {.layer_settings = {
- {LayerId{.spatial_idx = 0, .temporal_idx = 0},
- {.resolution = {.width = 640, .height = 360},
- .framerate = Frequency::MilliHertz(1000 * framerate_fps.second),
- .bitrate = DataRate::KilobitsPerSec(512)}}}}}};
-
- std::unique_ptr<VideoCodecStats> stats = RunEncodeTest(
- codec_type, codec_impl, video_info, frame_settings, num_frames,
- /*save_codec_input=*/false, /*save_codec_output=*/false);
+
+ std::map<uint32_t, EncodingSettings> encoding_settings =
+ VideoCodecTester::CreateEncodingSettings(
+ codec_type, /*scalability_mode=*/"L1T1",
+ /*width=*/640, /*height=*/360,
+ /*layer_bitrates_kbps=*/{512}, framerate_fps.first,
+ static_cast<int>(duration_s * framerate_fps.first));
+
+ uint32_t initial_timestamp_rtp =
+ encoding_settings.rbegin()->first +
+ k90kHz / Frequency::Hertz(framerate_fps.first);
+
+ std::map<uint32_t, EncodingSettings> encoding_settings2 =
+ VideoCodecTester::CreateEncodingSettings(
+ codec_type, /*scalability_mode=*/"L1T1", /*width=*/640,
+ /*height=*/360,
+ /*layer_bitrates_kbps=*/{512}, framerate_fps.second,
+ static_cast<int>(duration_s * framerate_fps.second),
+ initial_timestamp_rtp);
+
+ encoding_settings.merge(encoding_settings2);
+
+ std::unique_ptr<VideoCodecStats> stats =
+ RunEncodeTest(codec_type, codec_impl, video_info, encoding_settings);
VideoCodecStats::Stream stream;
if (stats != nullptr) {
- std::vector<VideoCodecStats::Frame> frames =
- stats->Slice(VideoCodecStats::Filter{.first_frame = first_frame});
- SetTargetRates(frame_settings, frames);
- stream = stats->Aggregate(frames);
+ stream = stats->Aggregate({.min_timestamp_rtp = initial_timestamp_rtp});
if (absl::GetFlag(FLAGS_webrtc_quick_perf_test)) {
EXPECT_NEAR(stream.bitrate_mismatch_pct.GetAverage(), 0, 10);
EXPECT_NEAR(stream.framerate_mismatch_pct.GetAverage(), 0, 10);
@@ -786,6 +497,7 @@ TEST_P(FramerateAdaptationTest, FramerateAdaptation) {
stream.LogMetrics(
GetGlobalMetricsLogger(),
::testing::UnitTest::GetInstance()->current_test_info()->name(),
+ /*prefix=*/"",
/*metadata=*/
{{"codec_type", codec_type},
{"codec_impl", codec_impl},
@@ -794,17 +506,71 @@ TEST_P(FramerateAdaptationTest, FramerateAdaptation) {
std::to_string(framerate_fps.second)}});
}
-INSTANTIATE_TEST_SUITE_P(All,
- FramerateAdaptationTest,
- Combine(Values("AV1", "VP9", "VP8", "H264", "H265"),
+INSTANTIATE_TEST_SUITE_P(
+ All,
+ FramerateAdaptationTest,
+ Combine(Values("AV1", "VP9", "VP8", "H264", "H265"),
#if defined(WEBRTC_ANDROID)
- Values("builtin", "mediacodec"),
+ Values("builtin", "mediacodec"),
#else
- Values("builtin"),
+ Values("builtin"),
#endif
- Values(kFourPeople_1280x720_30),
- Values(std::pair(30, 15), std::pair(15, 30))),
- FramerateAdaptationTest::TestParamsToString);
+ Values(kRawVideos.at("FourPeople_1280x720_30")),
+ Values(std::pair(30, 15), std::pair(15, 30))),
+ FramerateAdaptationTest::TestParamsToString);
+
+TEST(VideoCodecTest, DISABLED_EncodeDecode) {
+ std::vector<std::string> bitrate_str = absl::GetFlag(FLAGS_bitrate_kbps);
+ std::vector<int> bitrate_kbps;
+ std::transform(bitrate_str.begin(), bitrate_str.end(),
+ std::back_inserter(bitrate_kbps),
+ [](const std::string& str) { return std::stoi(str); });
+
+ std::map<uint32_t, EncodingSettings> frames_settings =
+ VideoCodecTester::CreateEncodingSettings(
+ CodecNameToCodecType(absl::GetFlag(FLAGS_encoder)),
+ absl::GetFlag(FLAGS_scalability_mode), absl::GetFlag(FLAGS_width),
+ absl::GetFlag(FLAGS_height), {bitrate_kbps},
+ absl::GetFlag(FLAGS_framerate_fps), absl::GetFlag(FLAGS_num_frames));
+
+ // TODO(webrtc:14852): Pass encoder and decoder names directly, and update
+ // logged test name (implies lossing history in the chromeperf dashboard).
+ // Sync with changes in Stream::LogMetrics (see TODOs there).
+ std::unique_ptr<VideoCodecStats> stats = RunEncodeDecodeTest(
+ CodecNameToCodecImpl(absl::GetFlag(FLAGS_encoder)),
+ kRawVideos.at(absl::GetFlag(FLAGS_video_name)), frames_settings);
+ ASSERT_NE(nullptr, stats);
+
+ // Log unsliced metrics.
+ VideoCodecStats::Stream stream = stats->Aggregate(Filter{});
+ stream.LogMetrics(GetGlobalMetricsLogger(), TestName(), /*prefix=*/"",
+ /*metadata=*/{});
+
+ // Log metrics sliced on spatial and temporal layer.
+ ScalabilityMode scalability_mode =
+ *ScalabilityModeFromString(absl::GetFlag(FLAGS_scalability_mode));
+ int num_spatial_layers = ScalabilityModeToNumSpatialLayers(scalability_mode);
+ int num_temporal_layers =
+ ScalabilityModeToNumTemporalLayers(scalability_mode);
+ for (int sidx = 0; sidx < num_spatial_layers; ++sidx) {
+ for (int tidx = 0; tidx < num_temporal_layers; ++tidx) {
+ std::string metric_name_prefix =
+ (rtc::StringBuilder() << "s" << sidx << "t" << tidx << "_").str();
+ stream = stats->Aggregate(
+ {.layer_id = {{.spatial_idx = sidx, .temporal_idx = tidx}}});
+ stream.LogMetrics(GetGlobalMetricsLogger(), TestName(),
+ metric_name_prefix,
+ /*metadata=*/{});
+ }
+ }
+
+ if (absl::GetFlag(FLAGS_write_csv)) {
+ stats->LogMetrics(
+ (rtc::StringBuilder() << TestOutputPath() << ".csv").str(),
+ stats->Slice(Filter{}, /*merge=*/false), /*metadata=*/
+ {{"test_name", TestName()}});
+ }
+}
} // namespace test
diff --git a/third_party/libwebrtc/modules/video_coding/codecs/test/video_codec_tester_impl.cc b/third_party/libwebrtc/modules/video_coding/codecs/test/video_codec_tester_impl.cc
deleted file mode 100644
index f15b1b35f3..0000000000
--- a/third_party/libwebrtc/modules/video_coding/codecs/test/video_codec_tester_impl.cc
+++ /dev/null
@@ -1,437 +0,0 @@
-/*
- * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved.
- *
- * Use of this source code is governed by a BSD-style license
- * that can be found in the LICENSE file in the root of the source
- * tree. An additional intellectual property rights grant can be found
- * in the file PATENTS. All contributing project authors may
- * be found in the AUTHORS file in the root of the source tree.
- */
-
-#include "modules/video_coding/codecs/test/video_codec_tester_impl.h"
-
-#include <map>
-#include <memory>
-#include <string>
-#include <utility>
-
-#include "api/task_queue/default_task_queue_factory.h"
-#include "api/units/frequency.h"
-#include "api/units/time_delta.h"
-#include "api/units/timestamp.h"
-#include "api/video/encoded_image.h"
-#include "api/video/i420_buffer.h"
-#include "api/video/video_codec_type.h"
-#include "api/video/video_frame.h"
-#include "modules/video_coding/codecs/test/video_codec_analyzer.h"
-#include "modules/video_coding/utility/ivf_file_writer.h"
-#include "rtc_base/event.h"
-#include "rtc_base/time_utils.h"
-#include "system_wrappers/include/sleep.h"
-#include "test/testsupport/video_frame_writer.h"
-
-namespace webrtc {
-namespace test {
-
-namespace {
-using RawVideoSource = VideoCodecTester::RawVideoSource;
-using CodedVideoSource = VideoCodecTester::CodedVideoSource;
-using Decoder = VideoCodecTester::Decoder;
-using Encoder = VideoCodecTester::Encoder;
-using EncoderSettings = VideoCodecTester::EncoderSettings;
-using DecoderSettings = VideoCodecTester::DecoderSettings;
-using PacingSettings = VideoCodecTester::PacingSettings;
-using PacingMode = PacingSettings::PacingMode;
-
-constexpr Frequency k90kHz = Frequency::Hertz(90000);
-
-// A thread-safe wrapper for video source to be shared with the quality analyzer
-// that reads reference frames from a separate thread.
-class SyncRawVideoSource : public VideoCodecAnalyzer::ReferenceVideoSource {
- public:
- explicit SyncRawVideoSource(RawVideoSource* video_source)
- : video_source_(video_source) {}
-
- absl::optional<VideoFrame> PullFrame() {
- MutexLock lock(&mutex_);
- return video_source_->PullFrame();
- }
-
- VideoFrame GetFrame(uint32_t timestamp_rtp, Resolution resolution) override {
- MutexLock lock(&mutex_);
- return video_source_->GetFrame(timestamp_rtp, resolution);
- }
-
- protected:
- RawVideoSource* const video_source_ RTC_GUARDED_BY(mutex_);
- Mutex mutex_;
-};
-
-// Pacer calculates delay necessary to keep frame encode or decode call spaced
-// from the previous calls by the pacing time. `Delay` is expected to be called
-// as close as possible to posting frame encode or decode task. This class is
-// not thread safe.
-class Pacer {
- public:
- explicit Pacer(PacingSettings settings)
- : settings_(settings), delay_(TimeDelta::Zero()) {}
- Timestamp Schedule(Timestamp timestamp) {
- Timestamp now = Timestamp::Micros(rtc::TimeMicros());
- if (settings_.mode == PacingMode::kNoPacing) {
- return now;
- }
-
- Timestamp scheduled = now;
- if (prev_scheduled_) {
- scheduled = *prev_scheduled_ + PacingTime(timestamp);
- if (scheduled < now) {
- scheduled = now;
- }
- }
-
- prev_timestamp_ = timestamp;
- prev_scheduled_ = scheduled;
- return scheduled;
- }
-
- private:
- TimeDelta PacingTime(Timestamp timestamp) {
- if (settings_.mode == PacingMode::kRealTime) {
- return timestamp - *prev_timestamp_;
- }
- RTC_CHECK_EQ(PacingMode::kConstantRate, settings_.mode);
- return 1 / settings_.constant_rate;
- }
-
- PacingSettings settings_;
- absl::optional<Timestamp> prev_timestamp_;
- absl::optional<Timestamp> prev_scheduled_;
- TimeDelta delay_;
-};
-
-// Task queue that keeps the number of queued tasks below a certain limit. If
-// the limit is reached, posting of a next task is blocked until execution of a
-// previously posted task starts. This class is not thread-safe.
-class LimitedTaskQueue {
- public:
- // The codec tester reads frames from video source in the main thread.
- // Encoding and decoding are done in separate threads. If encoding or
- // decoding is slow, the reading may go far ahead and may buffer too many
- // frames in memory. To prevent this we limit the encoding/decoding queue
- // size. When the queue is full, the main thread and, hence, reading frames
- // from video source is blocked until a previously posted encoding/decoding
- // task starts.
- static constexpr int kMaxTaskQueueSize = 3;
-
- LimitedTaskQueue() : queue_size_(0) {}
-
- void PostScheduledTask(absl::AnyInvocable<void() &&> task, Timestamp start) {
- ++queue_size_;
- task_queue_.PostTask([this, task = std::move(task), start]() mutable {
- int wait_ms = static_cast<int>(start.ms() - rtc::TimeMillis());
- if (wait_ms > 0) {
- SleepMs(wait_ms);
- }
-
- std::move(task)();
- --queue_size_;
- task_executed_.Set();
- });
-
- task_executed_.Reset();
- if (queue_size_ > kMaxTaskQueueSize) {
- task_executed_.Wait(rtc::Event::kForever);
- }
- RTC_CHECK(queue_size_ <= kMaxTaskQueueSize);
- }
-
- void WaitForPreviouslyPostedTasks() {
- task_queue_.SendTask([] {});
- }
-
- TaskQueueForTest task_queue_;
- std::atomic_int queue_size_;
- rtc::Event task_executed_;
-};
-
-class TesterY4mWriter {
- public:
- explicit TesterY4mWriter(absl::string_view base_path)
- : base_path_(base_path) {}
-
- ~TesterY4mWriter() {
- task_queue_.SendTask([] {});
- }
-
- void Write(const VideoFrame& frame, int spatial_idx) {
- task_queue_.PostTask([this, frame, spatial_idx] {
- if (y4m_writers_.find(spatial_idx) == y4m_writers_.end()) {
- std::string file_path =
- base_path_ + "_s" + std::to_string(spatial_idx) + ".y4m";
-
- Y4mVideoFrameWriterImpl* y4m_writer = new Y4mVideoFrameWriterImpl(
- file_path, frame.width(), frame.height(), /*fps=*/30);
- RTC_CHECK(y4m_writer);
-
- y4m_writers_[spatial_idx] =
- std::unique_ptr<VideoFrameWriter>(y4m_writer);
- }
-
- y4m_writers_.at(spatial_idx)->WriteFrame(frame);
- });
- }
-
- protected:
- std::string base_path_;
- std::map<int, std::unique_ptr<VideoFrameWriter>> y4m_writers_;
- TaskQueueForTest task_queue_;
-};
-
-class TesterIvfWriter {
- public:
- explicit TesterIvfWriter(absl::string_view base_path)
- : base_path_(base_path) {}
-
- ~TesterIvfWriter() {
- task_queue_.SendTask([] {});
- }
-
- void Write(const EncodedImage& encoded_frame) {
- task_queue_.PostTask([this, encoded_frame] {
- int spatial_idx = encoded_frame.SpatialIndex().value_or(0);
- if (ivf_file_writers_.find(spatial_idx) == ivf_file_writers_.end()) {
- std::string ivf_path =
- base_path_ + "_s" + std::to_string(spatial_idx) + ".ivf";
-
- FileWrapper ivf_file = FileWrapper::OpenWriteOnly(ivf_path);
- RTC_CHECK(ivf_file.is_open());
-
- std::unique_ptr<IvfFileWriter> ivf_writer =
- IvfFileWriter::Wrap(std::move(ivf_file), /*byte_limit=*/0);
- RTC_CHECK(ivf_writer);
-
- ivf_file_writers_[spatial_idx] = std::move(ivf_writer);
- }
-
- // To play: ffplay -vcodec vp8|vp9|av1|hevc|h264 filename
- ivf_file_writers_.at(spatial_idx)
- ->WriteFrame(encoded_frame, VideoCodecType::kVideoCodecGeneric);
- });
- }
-
- protected:
- std::string base_path_;
- std::map<int, std::unique_ptr<IvfFileWriter>> ivf_file_writers_;
- TaskQueueForTest task_queue_;
-};
-
-class TesterDecoder {
- public:
- TesterDecoder(Decoder* decoder,
- VideoCodecAnalyzer* analyzer,
- const DecoderSettings& settings)
- : decoder_(decoder),
- analyzer_(analyzer),
- settings_(settings),
- pacer_(settings.pacing) {
- RTC_CHECK(analyzer_) << "Analyzer must be provided";
-
- if (settings.decoder_input_base_path) {
- input_writer_ =
- std::make_unique<TesterIvfWriter>(*settings.decoder_input_base_path);
- }
-
- if (settings.decoder_output_base_path) {
- output_writer_ =
- std::make_unique<TesterY4mWriter>(*settings.decoder_output_base_path);
- }
- }
-
- void Initialize() {
- task_queue_.PostScheduledTask([this] { decoder_->Initialize(); },
- Timestamp::Zero());
- task_queue_.WaitForPreviouslyPostedTasks();
- }
-
- void Decode(const EncodedImage& input_frame) {
- Timestamp timestamp =
- Timestamp::Micros((input_frame.RtpTimestamp() / k90kHz).us());
-
- task_queue_.PostScheduledTask(
- [this, input_frame] {
- analyzer_->StartDecode(input_frame);
-
- decoder_->Decode(
- input_frame,
- [this, spatial_idx = input_frame.SpatialIndex().value_or(0)](
- const VideoFrame& output_frame) {
- analyzer_->FinishDecode(output_frame, spatial_idx);
-
- if (output_writer_) {
- output_writer_->Write(output_frame, spatial_idx);
- }
- });
-
- if (input_writer_) {
- input_writer_->Write(input_frame);
- }
- },
- pacer_.Schedule(timestamp));
- }
-
- void Flush() {
- task_queue_.PostScheduledTask([this] { decoder_->Flush(); },
- Timestamp::Zero());
- task_queue_.WaitForPreviouslyPostedTasks();
- }
-
- protected:
- Decoder* const decoder_;
- VideoCodecAnalyzer* const analyzer_;
- const DecoderSettings& settings_;
- Pacer pacer_;
- LimitedTaskQueue task_queue_;
- std::unique_ptr<TesterIvfWriter> input_writer_;
- std::unique_ptr<TesterY4mWriter> output_writer_;
-};
-
-class TesterEncoder {
- public:
- TesterEncoder(Encoder* encoder,
- TesterDecoder* decoder,
- VideoCodecAnalyzer* analyzer,
- const EncoderSettings& settings)
- : encoder_(encoder),
- decoder_(decoder),
- analyzer_(analyzer),
- settings_(settings),
- pacer_(settings.pacing) {
- RTC_CHECK(analyzer_) << "Analyzer must be provided";
- if (settings.encoder_input_base_path) {
- input_writer_ =
- std::make_unique<TesterY4mWriter>(*settings.encoder_input_base_path);
- }
-
- if (settings.encoder_output_base_path) {
- output_writer_ =
- std::make_unique<TesterIvfWriter>(*settings.encoder_output_base_path);
- }
- }
-
- void Initialize() {
- task_queue_.PostScheduledTask([this] { encoder_->Initialize(); },
- Timestamp::Zero());
- task_queue_.WaitForPreviouslyPostedTasks();
- }
-
- void Encode(const VideoFrame& input_frame) {
- Timestamp timestamp =
- Timestamp::Micros((input_frame.timestamp() / k90kHz).us());
-
- task_queue_.PostScheduledTask(
- [this, input_frame] {
- analyzer_->StartEncode(input_frame);
- encoder_->Encode(input_frame,
- [this](const EncodedImage& encoded_frame) {
- analyzer_->FinishEncode(encoded_frame);
-
- if (decoder_ != nullptr) {
- decoder_->Decode(encoded_frame);
- }
-
- if (output_writer_ != nullptr) {
- output_writer_->Write(encoded_frame);
- }
- });
-
- if (input_writer_) {
- input_writer_->Write(input_frame, /*spatial_idx=*/0);
- }
- },
- pacer_.Schedule(timestamp));
- }
-
- void Flush() {
- task_queue_.PostScheduledTask([this] { encoder_->Flush(); },
- Timestamp::Zero());
- task_queue_.WaitForPreviouslyPostedTasks();
- }
-
- protected:
- Encoder* const encoder_;
- TesterDecoder* const decoder_;
- VideoCodecAnalyzer* const analyzer_;
- const EncoderSettings& settings_;
- std::unique_ptr<TesterY4mWriter> input_writer_;
- std::unique_ptr<TesterIvfWriter> output_writer_;
- Pacer pacer_;
- LimitedTaskQueue task_queue_;
-};
-
-} // namespace
-
-std::unique_ptr<VideoCodecStats> VideoCodecTesterImpl::RunDecodeTest(
- CodedVideoSource* video_source,
- Decoder* decoder,
- const DecoderSettings& decoder_settings) {
- VideoCodecAnalyzer perf_analyzer;
- TesterDecoder tester_decoder(decoder, &perf_analyzer, decoder_settings);
-
- tester_decoder.Initialize();
-
- while (auto frame = video_source->PullFrame()) {
- tester_decoder.Decode(*frame);
- }
-
- tester_decoder.Flush();
-
- return perf_analyzer.GetStats();
-}
-
-std::unique_ptr<VideoCodecStats> VideoCodecTesterImpl::RunEncodeTest(
- RawVideoSource* video_source,
- Encoder* encoder,
- const EncoderSettings& encoder_settings) {
- SyncRawVideoSource sync_source(video_source);
- VideoCodecAnalyzer perf_analyzer;
- TesterEncoder tester_encoder(encoder, /*decoder=*/nullptr, &perf_analyzer,
- encoder_settings);
-
- tester_encoder.Initialize();
-
- while (auto frame = sync_source.PullFrame()) {
- tester_encoder.Encode(*frame);
- }
-
- tester_encoder.Flush();
-
- return perf_analyzer.GetStats();
-}
-
-std::unique_ptr<VideoCodecStats> VideoCodecTesterImpl::RunEncodeDecodeTest(
- RawVideoSource* video_source,
- Encoder* encoder,
- Decoder* decoder,
- const EncoderSettings& encoder_settings,
- const DecoderSettings& decoder_settings) {
- SyncRawVideoSource sync_source(video_source);
- VideoCodecAnalyzer perf_analyzer(&sync_source);
- TesterDecoder tester_decoder(decoder, &perf_analyzer, decoder_settings);
- TesterEncoder tester_encoder(encoder, &tester_decoder, &perf_analyzer,
- encoder_settings);
-
- tester_encoder.Initialize();
- tester_decoder.Initialize();
-
- while (auto frame = sync_source.PullFrame()) {
- tester_encoder.Encode(*frame);
- }
-
- tester_encoder.Flush();
- tester_decoder.Flush();
-
- return perf_analyzer.GetStats();
-}
-
-} // namespace test
-} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/video_coding/codecs/test/video_codec_tester_impl.h b/third_party/libwebrtc/modules/video_coding/codecs/test/video_codec_tester_impl.h
deleted file mode 100644
index 32191b5a98..0000000000
--- a/third_party/libwebrtc/modules/video_coding/codecs/test/video_codec_tester_impl.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved.
- *
- * Use of this source code is governed by a BSD-style license
- * that can be found in the LICENSE file in the root of the source
- * tree. An additional intellectual property rights grant can be found
- * in the file PATENTS. All contributing project authors may
- * be found in the AUTHORS file in the root of the source tree.
- */
-
-#ifndef MODULES_VIDEO_CODING_CODECS_TEST_VIDEO_CODEC_TESTER_IMPL_H_
-#define MODULES_VIDEO_CODING_CODECS_TEST_VIDEO_CODEC_TESTER_IMPL_H_
-
-#include <memory>
-
-#include "api/test/video_codec_tester.h"
-
-namespace webrtc {
-namespace test {
-
-// A stateless implementation of `VideoCodecTester`. This class is thread safe.
-class VideoCodecTesterImpl : public VideoCodecTester {
- public:
- std::unique_ptr<VideoCodecStats> RunDecodeTest(
- CodedVideoSource* video_source,
- Decoder* decoder,
- const DecoderSettings& decoder_settings) override;
-
- std::unique_ptr<VideoCodecStats> RunEncodeTest(
- RawVideoSource* video_source,
- Encoder* encoder,
- const EncoderSettings& encoder_settings) override;
-
- std::unique_ptr<VideoCodecStats> RunEncodeDecodeTest(
- RawVideoSource* video_source,
- Encoder* encoder,
- Decoder* decoder,
- const EncoderSettings& encoder_settings,
- const DecoderSettings& decoder_settings) override;
-};
-
-} // namespace test
-} // namespace webrtc
-
-#endif // MODULES_VIDEO_CODING_CODECS_TEST_VIDEO_CODEC_TESTER_IMPL_H_
diff --git a/third_party/libwebrtc/modules/video_coding/codecs/test/video_codec_tester_impl_unittest.cc b/third_party/libwebrtc/modules/video_coding/codecs/test/video_codec_tester_impl_unittest.cc
deleted file mode 100644
index a8c118ef20..0000000000
--- a/third_party/libwebrtc/modules/video_coding/codecs/test/video_codec_tester_impl_unittest.cc
+++ /dev/null
@@ -1,205 +0,0 @@
-/*
- * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved.
- *
- * Use of this source code is governed by a BSD-style license
- * that can be found in the LICENSE file in the root of the source
- * tree. An additional intellectual property rights grant can be found
- * in the file PATENTS. All contributing project authors may
- * be found in the AUTHORS file in the root of the source tree.
- */
-
-#include "modules/video_coding/codecs/test/video_codec_tester_impl.h"
-
-#include <memory>
-#include <tuple>
-#include <utility>
-#include <vector>
-
-#include "api/units/frequency.h"
-#include "api/units/time_delta.h"
-#include "api/video/encoded_image.h"
-#include "api/video/i420_buffer.h"
-#include "api/video/video_frame.h"
-#include "rtc_base/fake_clock.h"
-#include "rtc_base/gunit.h"
-#include "rtc_base/task_queue_for_test.h"
-#include "rtc_base/time_utils.h"
-#include "test/gmock.h"
-#include "test/gtest.h"
-
-namespace webrtc {
-namespace test {
-
-namespace {
-using ::testing::_;
-using ::testing::Invoke;
-using ::testing::InvokeWithoutArgs;
-using ::testing::Return;
-
-using Decoder = VideoCodecTester::Decoder;
-using Encoder = VideoCodecTester::Encoder;
-using CodedVideoSource = VideoCodecTester::CodedVideoSource;
-using RawVideoSource = VideoCodecTester::RawVideoSource;
-using DecoderSettings = VideoCodecTester::DecoderSettings;
-using EncoderSettings = VideoCodecTester::EncoderSettings;
-using PacingSettings = VideoCodecTester::PacingSettings;
-using PacingMode = PacingSettings::PacingMode;
-
-constexpr Frequency k90kHz = Frequency::Hertz(90000);
-
-struct PacingTestParams {
- PacingSettings pacing_settings;
- Frequency framerate;
- int num_frames;
- std::vector<int> expected_delta_ms;
-};
-
-VideoFrame CreateVideoFrame(uint32_t timestamp_rtp) {
- rtc::scoped_refptr<I420Buffer> buffer(I420Buffer::Create(2, 2));
- return VideoFrame::Builder()
- .set_video_frame_buffer(buffer)
- .set_timestamp_rtp(timestamp_rtp)
- .build();
-}
-
-EncodedImage CreateEncodedImage(uint32_t timestamp_rtp) {
- EncodedImage encoded_image;
- encoded_image.SetRtpTimestamp(timestamp_rtp);
- return encoded_image;
-}
-
-class MockRawVideoSource : public RawVideoSource {
- public:
- MockRawVideoSource(int num_frames, Frequency framerate)
- : num_frames_(num_frames), frame_num_(0), framerate_(framerate) {}
-
- absl::optional<VideoFrame> PullFrame() override {
- if (frame_num_ >= num_frames_) {
- return absl::nullopt;
- }
- uint32_t timestamp_rtp = frame_num_ * k90kHz / framerate_;
- ++frame_num_;
- return CreateVideoFrame(timestamp_rtp);
- }
-
- MOCK_METHOD(VideoFrame,
- GetFrame,
- (uint32_t timestamp_rtp, Resolution),
- (override));
-
- private:
- int num_frames_;
- int frame_num_;
- Frequency framerate_;
-};
-
-class MockCodedVideoSource : public CodedVideoSource {
- public:
- MockCodedVideoSource(int num_frames, Frequency framerate)
- : num_frames_(num_frames), frame_num_(0), framerate_(framerate) {}
-
- absl::optional<EncodedImage> PullFrame() override {
- if (frame_num_ >= num_frames_) {
- return absl::nullopt;
- }
- uint32_t timestamp_rtp = frame_num_ * k90kHz / framerate_;
- ++frame_num_;
- return CreateEncodedImage(timestamp_rtp);
- }
-
- private:
- int num_frames_;
- int frame_num_;
- Frequency framerate_;
-};
-
-class MockDecoder : public Decoder {
- public:
- MOCK_METHOD(void, Initialize, (), (override));
- MOCK_METHOD(void,
- Decode,
- (const EncodedImage& frame, DecodeCallback callback),
- (override));
- MOCK_METHOD(void, Flush, (), (override));
-};
-
-class MockEncoder : public Encoder {
- public:
- MOCK_METHOD(void, Initialize, (), (override));
- MOCK_METHOD(void,
- Encode,
- (const VideoFrame& frame, EncodeCallback callback),
- (override));
- MOCK_METHOD(void, Flush, (), (override));
-};
-
-} // namespace
-
-class VideoCodecTesterImplPacingTest
- : public ::testing::TestWithParam<PacingTestParams> {
- public:
- VideoCodecTesterImplPacingTest() : test_params_(GetParam()) {}
-
- protected:
- PacingTestParams test_params_;
-};
-
-TEST_P(VideoCodecTesterImplPacingTest, PaceEncode) {
- MockRawVideoSource video_source(test_params_.num_frames,
- test_params_.framerate);
- MockEncoder encoder;
- EncoderSettings encoder_settings;
- encoder_settings.pacing = test_params_.pacing_settings;
-
- VideoCodecTesterImpl tester;
- auto fs =
- tester.RunEncodeTest(&video_source, &encoder, encoder_settings)->Slice();
- ASSERT_EQ(static_cast<int>(fs.size()), test_params_.num_frames);
-
- for (size_t i = 1; i < fs.size(); ++i) {
- int delta_ms = (fs[i].encode_start - fs[i - 1].encode_start).ms();
- EXPECT_NEAR(delta_ms, test_params_.expected_delta_ms[i - 1], 10);
- }
-}
-
-TEST_P(VideoCodecTesterImplPacingTest, PaceDecode) {
- MockCodedVideoSource video_source(test_params_.num_frames,
- test_params_.framerate);
- MockDecoder decoder;
- DecoderSettings decoder_settings;
- decoder_settings.pacing = test_params_.pacing_settings;
-
- VideoCodecTesterImpl tester;
- auto fs =
- tester.RunDecodeTest(&video_source, &decoder, decoder_settings)->Slice();
- ASSERT_EQ(static_cast<int>(fs.size()), test_params_.num_frames);
-
- for (size_t i = 1; i < fs.size(); ++i) {
- int delta_ms = (fs[i].decode_start - fs[i - 1].decode_start).ms();
- EXPECT_NEAR(delta_ms, test_params_.expected_delta_ms[i - 1], 20);
- }
-}
-
-INSTANTIATE_TEST_SUITE_P(
- DISABLED_All,
- VideoCodecTesterImplPacingTest,
- ::testing::ValuesIn(
- {// No pacing.
- PacingTestParams({.pacing_settings = {.mode = PacingMode::kNoPacing},
- .framerate = Frequency::Hertz(10),
- .num_frames = 3,
- .expected_delta_ms = {0, 0}}),
- // Real-time pacing.
- PacingTestParams({.pacing_settings = {.mode = PacingMode::kRealTime},
- .framerate = Frequency::Hertz(10),
- .num_frames = 3,
- .expected_delta_ms = {100, 100}}),
- // Pace with specified constant rate.
- PacingTestParams(
- {.pacing_settings = {.mode = PacingMode::kConstantRate,
- .constant_rate = Frequency::Hertz(20)},
- .framerate = Frequency::Hertz(10),
- .num_frames = 3,
- .expected_delta_ms = {50, 50}})}));
-} // namespace test
-} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/video_coding/encoded_frame_gn/moz.build b/third_party/libwebrtc/modules/video_coding/encoded_frame_gn/moz.build
index 9b8e33b7d5..31e83f9c31 100644
--- a/third_party/libwebrtc/modules/video_coding/encoded_frame_gn/moz.build
+++ b/third_party/libwebrtc/modules/video_coding/encoded_frame_gn/moz.build
@@ -199,7 +199,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -209,10 +208,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/video_coding/frame_dependencies_calculator_gn/moz.build b/third_party/libwebrtc/modules/video_coding/frame_dependencies_calculator_gn/moz.build
index 487fc5b4d6..1ad9c574ad 100644
--- a/third_party/libwebrtc/modules/video_coding/frame_dependencies_calculator_gn/moz.build
+++ b/third_party/libwebrtc/modules/video_coding/frame_dependencies_calculator_gn/moz.build
@@ -195,7 +195,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -205,10 +204,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/video_coding/frame_helpers_gn/moz.build b/third_party/libwebrtc/modules/video_coding/frame_helpers_gn/moz.build
index dd901a5371..ccac90f50d 100644
--- a/third_party/libwebrtc/modules/video_coding/frame_helpers_gn/moz.build
+++ b/third_party/libwebrtc/modules/video_coding/frame_helpers_gn/moz.build
@@ -195,7 +195,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -205,10 +204,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/video_coding/generic_decoder.cc b/third_party/libwebrtc/modules/video_coding/generic_decoder.cc
index fc356e7a44..00585abbc9 100644
--- a/third_party/libwebrtc/modules/video_coding/generic_decoder.cc
+++ b/third_party/libwebrtc/modules/video_coding/generic_decoder.cc
@@ -329,18 +329,7 @@ int32_t VCMGenericDecoder::Decode(const EncodedImage& frame,
}
_callback->OnDecoderInfoChanged(std::move(decoder_info));
}
- if (ret < WEBRTC_VIDEO_CODEC_OK) {
- const absl::optional<uint32_t> ssrc =
- !frame_info.packet_infos.empty()
- ? absl::make_optional(frame_info.packet_infos[0].ssrc())
- : absl::nullopt;
- RTC_LOG(LS_WARNING) << "Failed to decode frame with timestamp "
- << frame.RtpTimestamp() << ", ssrc "
- << (ssrc ? rtc::ToString(*ssrc) : "<not set>")
- << ", error code: " << ret;
- _callback->ClearTimestampMap();
- } else if (ret == WEBRTC_VIDEO_CODEC_NO_OUTPUT) {
- // No output.
+ if (ret < WEBRTC_VIDEO_CODEC_OK || ret == WEBRTC_VIDEO_CODEC_NO_OUTPUT) {
_callback->ClearTimestampMap();
}
return ret;
diff --git a/third_party/libwebrtc/modules/video_coding/include/video_codec_interface.h b/third_party/libwebrtc/modules/video_coding/include/video_codec_interface.h
index c6522fcc6b..987e1b623e 100644
--- a/third_party/libwebrtc/modules/video_coding/include/video_codec_interface.h
+++ b/third_party/libwebrtc/modules/video_coding/include/video_codec_interface.h
@@ -50,7 +50,9 @@ struct CodecSpecificInfoVP8 {
size_t updatedBuffers[kBuffersCount];
size_t updatedBuffersCount;
};
-static_assert(std::is_pod<CodecSpecificInfoVP8>::value, "");
+static_assert(std::is_trivial_v<CodecSpecificInfoVP8> &&
+ std::is_standard_layout_v<CodecSpecificInfoVP8>,
+ "");
// Hack alert - the code assumes that thisstruct is memset when constructed.
struct CodecSpecificInfoVP9 {
@@ -79,7 +81,9 @@ struct CodecSpecificInfoVP9 {
uint8_t num_ref_pics;
uint8_t p_diff[kMaxVp9RefPics];
};
-static_assert(std::is_pod<CodecSpecificInfoVP9>::value, "");
+static_assert(std::is_trivial_v<CodecSpecificInfoVP9> &&
+ std::is_standard_layout_v<CodecSpecificInfoVP9>,
+ "");
// Hack alert - the code assumes that thisstruct is memset when constructed.
struct CodecSpecificInfoH264 {
@@ -88,14 +92,18 @@ struct CodecSpecificInfoH264 {
bool base_layer_sync;
bool idr_frame;
};
-static_assert(std::is_pod<CodecSpecificInfoH264>::value, "");
+static_assert(std::is_trivial_v<CodecSpecificInfoH264> &&
+ std::is_standard_layout_v<CodecSpecificInfoH264>,
+ "");
union CodecSpecificInfoUnion {
CodecSpecificInfoVP8 VP8;
CodecSpecificInfoVP9 VP9;
CodecSpecificInfoH264 H264;
};
-static_assert(std::is_pod<CodecSpecificInfoUnion>::value, "");
+static_assert(std::is_trivial_v<CodecSpecificInfoUnion> &&
+ std::is_standard_layout_v<CodecSpecificInfoUnion>,
+ "");
// Note: if any pointers are added to this struct or its sub-structs, it
// must be fitted with a copy-constructor. This is because it is copied
diff --git a/third_party/libwebrtc/modules/video_coding/nack_requester_gn/moz.build b/third_party/libwebrtc/modules/video_coding/nack_requester_gn/moz.build
index 0f6654f1ab..d50ed75a00 100644
--- a/third_party/libwebrtc/modules/video_coding/nack_requester_gn/moz.build
+++ b/third_party/libwebrtc/modules/video_coding/nack_requester_gn/moz.build
@@ -200,7 +200,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -210,10 +209,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/video_coding/packet_buffer_gn/moz.build b/third_party/libwebrtc/modules/video_coding/packet_buffer_gn/moz.build
index f3f85aacaa..2c161989c1 100644
--- a/third_party/libwebrtc/modules/video_coding/packet_buffer_gn/moz.build
+++ b/third_party/libwebrtc/modules/video_coding/packet_buffer_gn/moz.build
@@ -199,7 +199,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -209,10 +208,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/video_coding/svc/scalability_mode_util_gn/moz.build b/third_party/libwebrtc/modules/video_coding/svc/scalability_mode_util_gn/moz.build
index 8a1dfd6377..80eb00a991 100644
--- a/third_party/libwebrtc/modules/video_coding/svc/scalability_mode_util_gn/moz.build
+++ b/third_party/libwebrtc/modules/video_coding/svc/scalability_mode_util_gn/moz.build
@@ -195,7 +195,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -205,10 +204,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/video_coding/svc/scalability_structures_gn/moz.build b/third_party/libwebrtc/modules/video_coding/svc/scalability_structures_gn/moz.build
index a3ea8b3495..931dfe8d89 100644
--- a/third_party/libwebrtc/modules/video_coding/svc/scalability_structures_gn/moz.build
+++ b/third_party/libwebrtc/modules/video_coding/svc/scalability_structures_gn/moz.build
@@ -202,7 +202,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -212,10 +211,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/video_coding/svc/scalable_video_controller_gn/moz.build b/third_party/libwebrtc/modules/video_coding/svc/scalable_video_controller_gn/moz.build
index a285154a79..18aa68e696 100644
--- a/third_party/libwebrtc/modules/video_coding/svc/scalable_video_controller_gn/moz.build
+++ b/third_party/libwebrtc/modules/video_coding/svc/scalable_video_controller_gn/moz.build
@@ -188,7 +188,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -198,10 +197,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/video_coding/svc/svc_rate_allocator_gn/moz.build b/third_party/libwebrtc/modules/video_coding/svc/svc_rate_allocator_gn/moz.build
index 412f719d18..bbb5a75959 100644
--- a/third_party/libwebrtc/modules/video_coding/svc/svc_rate_allocator_gn/moz.build
+++ b/third_party/libwebrtc/modules/video_coding/svc/svc_rate_allocator_gn/moz.build
@@ -195,7 +195,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -205,10 +204,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/video_coding/timing/decode_time_percentile_filter_gn/moz.build b/third_party/libwebrtc/modules/video_coding/timing/decode_time_percentile_filter_gn/moz.build
index 36867642c7..2347b0937c 100644
--- a/third_party/libwebrtc/modules/video_coding/timing/decode_time_percentile_filter_gn/moz.build
+++ b/third_party/libwebrtc/modules/video_coding/timing/decode_time_percentile_filter_gn/moz.build
@@ -188,7 +188,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -198,10 +197,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/video_coding/timing/frame_delay_variation_kalman_filter_gn/moz.build b/third_party/libwebrtc/modules/video_coding/timing/frame_delay_variation_kalman_filter_gn/moz.build
index caf0efc165..274023c6e7 100644
--- a/third_party/libwebrtc/modules/video_coding/timing/frame_delay_variation_kalman_filter_gn/moz.build
+++ b/third_party/libwebrtc/modules/video_coding/timing/frame_delay_variation_kalman_filter_gn/moz.build
@@ -188,7 +188,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -198,10 +197,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/video_coding/timing/inter_frame_delay_variation_calculator_gn/moz.build b/third_party/libwebrtc/modules/video_coding/timing/inter_frame_delay_variation_calculator_gn/moz.build
index 8c6e826a4a..d4ec330ed1 100644
--- a/third_party/libwebrtc/modules/video_coding/timing/inter_frame_delay_variation_calculator_gn/moz.build
+++ b/third_party/libwebrtc/modules/video_coding/timing/inter_frame_delay_variation_calculator_gn/moz.build
@@ -188,7 +188,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -198,10 +197,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/video_coding/timing/jitter_estimator_gn/moz.build b/third_party/libwebrtc/modules/video_coding/timing/jitter_estimator_gn/moz.build
index c7ca3c7fd8..e540f00f8c 100644
--- a/third_party/libwebrtc/modules/video_coding/timing/jitter_estimator_gn/moz.build
+++ b/third_party/libwebrtc/modules/video_coding/timing/jitter_estimator_gn/moz.build
@@ -199,7 +199,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -209,10 +208,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/video_coding/timing/rtt_filter_gn/moz.build b/third_party/libwebrtc/modules/video_coding/timing/rtt_filter_gn/moz.build
index f3993a17b1..18a30a6ede 100644
--- a/third_party/libwebrtc/modules/video_coding/timing/rtt_filter_gn/moz.build
+++ b/third_party/libwebrtc/modules/video_coding/timing/rtt_filter_gn/moz.build
@@ -188,7 +188,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -198,10 +197,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/video_coding/timing/timestamp_extrapolator_gn/moz.build b/third_party/libwebrtc/modules/video_coding/timing/timestamp_extrapolator_gn/moz.build
index ad8a6874e4..4c2a6eed62 100644
--- a/third_party/libwebrtc/modules/video_coding/timing/timestamp_extrapolator_gn/moz.build
+++ b/third_party/libwebrtc/modules/video_coding/timing/timestamp_extrapolator_gn/moz.build
@@ -188,7 +188,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -198,10 +197,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/video_coding/timing/timing_module_gn/moz.build b/third_party/libwebrtc/modules/video_coding/timing/timing_module_gn/moz.build
index 60cc81a229..76c4cfe664 100644
--- a/third_party/libwebrtc/modules/video_coding/timing/timing_module_gn/moz.build
+++ b/third_party/libwebrtc/modules/video_coding/timing/timing_module_gn/moz.build
@@ -199,7 +199,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -209,10 +208,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/video_coding/video_codec_interface_gn/moz.build b/third_party/libwebrtc/modules/video_coding/video_codec_interface_gn/moz.build
index b14bef2dec..141def9090 100644
--- a/third_party/libwebrtc/modules/video_coding/video_codec_interface_gn/moz.build
+++ b/third_party/libwebrtc/modules/video_coding/video_codec_interface_gn/moz.build
@@ -196,7 +196,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -206,10 +205,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/video_coding/video_coding_gn/moz.build b/third_party/libwebrtc/modules/video_coding/video_coding_gn/moz.build
index 5af51f1238..923ac7785a 100644
--- a/third_party/libwebrtc/modules/video_coding/video_coding_gn/moz.build
+++ b/third_party/libwebrtc/modules/video_coding/video_coding_gn/moz.build
@@ -214,7 +214,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -224,10 +223,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/video_coding/video_coding_utility_gn/moz.build b/third_party/libwebrtc/modules/video_coding/video_coding_utility_gn/moz.build
index d42eb284cd..bc1510e0ba 100644
--- a/third_party/libwebrtc/modules/video_coding/video_coding_utility_gn/moz.build
+++ b/third_party/libwebrtc/modules/video_coding/video_coding_utility_gn/moz.build
@@ -211,7 +211,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -221,10 +220,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/video_coding/webrtc_libvpx_interface_gn/moz.build b/third_party/libwebrtc/modules/video_coding/webrtc_libvpx_interface_gn/moz.build
index 81c9b9d404..8cb4b64625 100644
--- a/third_party/libwebrtc/modules/video_coding/webrtc_libvpx_interface_gn/moz.build
+++ b/third_party/libwebrtc/modules/video_coding/webrtc_libvpx_interface_gn/moz.build
@@ -191,7 +191,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -201,10 +200,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/video_coding/webrtc_vp8_gn/moz.build b/third_party/libwebrtc/modules/video_coding/webrtc_vp8_gn/moz.build
index 82a4d24e97..21d5eeee9f 100644
--- a/third_party/libwebrtc/modules/video_coding/webrtc_vp8_gn/moz.build
+++ b/third_party/libwebrtc/modules/video_coding/webrtc_vp8_gn/moz.build
@@ -206,7 +206,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -216,10 +215,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/video_coding/webrtc_vp8_scalability_gn/moz.build b/third_party/libwebrtc/modules/video_coding/webrtc_vp8_scalability_gn/moz.build
index 6799224dff..92fd7cf630 100644
--- a/third_party/libwebrtc/modules/video_coding/webrtc_vp8_scalability_gn/moz.build
+++ b/third_party/libwebrtc/modules/video_coding/webrtc_vp8_scalability_gn/moz.build
@@ -188,7 +188,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -198,10 +197,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/video_coding/webrtc_vp8_temporal_layers_gn/moz.build b/third_party/libwebrtc/modules/video_coding/webrtc_vp8_temporal_layers_gn/moz.build
index 2423950ba5..caf91a5d2c 100644
--- a/third_party/libwebrtc/modules/video_coding/webrtc_vp8_temporal_layers_gn/moz.build
+++ b/third_party/libwebrtc/modules/video_coding/webrtc_vp8_temporal_layers_gn/moz.build
@@ -205,7 +205,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -215,10 +214,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/video_coding/webrtc_vp9_gn/moz.build b/third_party/libwebrtc/modules/video_coding/webrtc_vp9_gn/moz.build
index 5bb64f3412..707d563559 100644
--- a/third_party/libwebrtc/modules/video_coding/webrtc_vp9_gn/moz.build
+++ b/third_party/libwebrtc/modules/video_coding/webrtc_vp9_gn/moz.build
@@ -208,7 +208,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -218,10 +217,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/modules/video_coding/webrtc_vp9_helpers_gn/moz.build b/third_party/libwebrtc/modules/video_coding/webrtc_vp9_helpers_gn/moz.build
index 6f1575870e..883e5c70b2 100644
--- a/third_party/libwebrtc/modules/video_coding/webrtc_vp9_helpers_gn/moz.build
+++ b/third_party/libwebrtc/modules/video_coding/webrtc_vp9_helpers_gn/moz.build
@@ -200,7 +200,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -210,10 +209,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/moz-patch-stack/0001.patch b/third_party/libwebrtc/moz-patch-stack/0001.patch
index 31a12dd98b..6547c47b40 100644
--- a/third_party/libwebrtc/moz-patch-stack/0001.patch
+++ b/third_party/libwebrtc/moz-patch-stack/0001.patch
@@ -413,7 +413,7 @@ index 54132bcdbb..cf8b3ad3dc 100644
bool RtpExtension::IsSupportedForVideo(absl::string_view uri) {
diff --git a/call/BUILD.gn b/call/BUILD.gn
-index 4cc42fd99f..58473dc1ea 100644
+index 9a7be88892..66c8b2011a 100644
--- a/call/BUILD.gn
+++ b/call/BUILD.gn
@@ -20,6 +20,7 @@ rtc_library("call_interfaces") {
@@ -1233,7 +1233,7 @@ index fb95a6020d..95432a509d 100644
} // namespace videocapturemodule
} // namespace webrtc
diff --git a/modules/video_capture/video_capture.h b/modules/video_capture/video_capture.h
-index eddc31414a..e207598d68 100644
+index 48920b27c0..f31b239c71 100644
--- a/modules/video_capture/video_capture.h
+++ b/modules/video_capture/video_capture.h
@@ -15,15 +15,44 @@
@@ -1256,7 +1256,7 @@ index eddc31414a..e207598d68 100644
+ virtual ~VideoInputFeedBack(){}
+};
+
- class VideoCaptureModule : public rtc::RefCountInterface {
+ class VideoCaptureModule : public RefCountInterface {
public:
// Interface for receiving information about available camera devices.
class DeviceInfo {
@@ -1281,7 +1281,7 @@ index eddc31414a..e207598d68 100644
// Returns the available capture devices.
// deviceNumber - Index of capture device.
-@@ -38,7 +67,8 @@ class VideoCaptureModule : public rtc::RefCountInterface {
+@@ -38,7 +67,8 @@ class VideoCaptureModule : public RefCountInterface {
char* deviceUniqueIdUTF8,
uint32_t deviceUniqueIdUTF8Length,
char* productUniqueIdUTF8 = 0,
@@ -1291,7 +1291,7 @@ index eddc31414a..e207598d68 100644
// Returns the number of capabilities this device.
virtual int32_t NumberOfCapabilities(const char* deviceUniqueIdUTF8) = 0;
-@@ -70,6 +100,8 @@ class VideoCaptureModule : public rtc::RefCountInterface {
+@@ -70,6 +100,8 @@ class VideoCaptureModule : public RefCountInterface {
uint32_t positionY) = 0;
virtual ~DeviceInfo() {}
@@ -1300,7 +1300,7 @@ index eddc31414a..e207598d68 100644
};
// Register capture data callback
-@@ -79,11 +111,16 @@ class VideoCaptureModule : public rtc::RefCountInterface {
+@@ -79,11 +111,16 @@ class VideoCaptureModule : public RefCountInterface {
RawVideoSinkInterface* dataCallback) = 0;
// Remove capture data callback
@@ -1560,10 +1560,10 @@ index 0474e7bc17..1953923f81 100644
std::unique_ptr<ScalableVideoController> svc_controller_;
absl::optional<ScalabilityMode> scalability_mode_;
diff --git a/rtc_base/BUILD.gn b/rtc_base/BUILD.gn
-index 6abae807fe..99d8e48e39 100644
+index 3011d6a797..9f5f0aad56 100644
--- a/rtc_base/BUILD.gn
+++ b/rtc_base/BUILD.gn
-@@ -458,6 +458,12 @@ rtc_library("logging") {
+@@ -463,6 +463,12 @@ rtc_library("logging") {
"//third_party/abseil-cpp/absl/types:optional",
]
@@ -1679,7 +1679,7 @@ index 0a9226ef6f..620c1c02f3 100644
vcm_ = nullptr;
}
diff --git a/webrtc.gni b/webrtc.gni
-index 173d66c791..912b9b4ef0 100644
+index 447aae4096..277c12c054 100644
--- a/webrtc.gni
+++ b/webrtc.gni
@@ -126,7 +126,7 @@ declare_args() {
diff --git a/third_party/libwebrtc/moz-patch-stack/0002.patch b/third_party/libwebrtc/moz-patch-stack/0002.patch
index eb8cb95b8d..1354066675 100644
--- a/third_party/libwebrtc/moz-patch-stack/0002.patch
+++ b/third_party/libwebrtc/moz-patch-stack/0002.patch
@@ -30,7 +30,7 @@ index a4e3e897fd..e777a45f92 100644
enum class CaptureType { kWindow, kScreen, kAnyScreenContent };
diff --git a/modules/video_capture/video_capture.h b/modules/video_capture/video_capture.h
-index e207598d68..58485f28e9 100644
+index f31b239c71..7279bed476 100644
--- a/modules/video_capture/video_capture.h
+++ b/modules/video_capture/video_capture.h
@@ -13,6 +13,7 @@
diff --git a/third_party/libwebrtc/moz-patch-stack/0005.patch b/third_party/libwebrtc/moz-patch-stack/0005.patch
index a7b1f9b1f5..3d925a62e2 100644
--- a/third_party/libwebrtc/moz-patch-stack/0005.patch
+++ b/third_party/libwebrtc/moz-patch-stack/0005.patch
@@ -17,10 +17,10 @@ Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/b3ba8452e77105c72
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/call/rtp_demuxer.cc b/call/rtp_demuxer.cc
-index 0b74f2ac0a..5c53f48144 100644
+index 841d7e3b94..f5c4186871 100644
--- a/call/rtp_demuxer.cc
+++ b/call/rtp_demuxer.cc
-@@ -272,13 +272,17 @@ RtpPacketSinkInterface* RtpDemuxer::ResolveSink(
+@@ -285,13 +285,17 @@ RtpPacketSinkInterface* RtpDemuxer::ResolveSink(
// RSID and RRID are routed to the same sinks. If an RSID is specified on a
// repair packet, it should be ignored and the RRID should be used.
std::string packet_mid, packet_rsid;
@@ -39,7 +39,7 @@ index 0b74f2ac0a..5c53f48144 100644
// The BUNDLE spec says to drop any packets with unknown MIDs, even if the
// SSRC is known/latched.
if (has_mid && known_mids_.find(packet_mid) == known_mids_.end()) {
-@@ -352,6 +356,7 @@ RtpPacketSinkInterface* RtpDemuxer::ResolveSink(
+@@ -365,6 +369,7 @@ RtpPacketSinkInterface* RtpDemuxer::ResolveSink(
}
}
diff --git a/third_party/libwebrtc/moz-patch-stack/0006.patch b/third_party/libwebrtc/moz-patch-stack/0006.patch
index 70d4576a93..7decaa705c 100644
--- a/third_party/libwebrtc/moz-patch-stack/0006.patch
+++ b/third_party/libwebrtc/moz-patch-stack/0006.patch
@@ -93,7 +93,7 @@ index d298081432..dd706e569d 100644
// interest to statistics. Used to implement RTCRemoteInboundRtpStreamStats.
// Within this list, the `ReportBlockData::source_ssrc()`, which is the SSRC
diff --git a/modules/rtp_rtcp/source/rtp_rtcp_impl2.cc b/modules/rtp_rtcp/source/rtp_rtcp_impl2.cc
-index 800ec77d3e..0df8543497 100644
+index 3dbbc77086..1d73bc3d87 100644
--- a/modules/rtp_rtcp/source/rtp_rtcp_impl2.cc
+++ b/modules/rtp_rtcp/source/rtp_rtcp_impl2.cc
@@ -503,6 +503,11 @@ void ModuleRtpRtcpImpl2::GetSendStreamDataCounters(
diff --git a/third_party/libwebrtc/moz-patch-stack/0007.patch b/third_party/libwebrtc/moz-patch-stack/0007.patch
index b217db4f34..052a4431bf 100644
--- a/third_party/libwebrtc/moz-patch-stack/0007.patch
+++ b/third_party/libwebrtc/moz-patch-stack/0007.patch
@@ -96,7 +96,7 @@ index dd706e569d..742a69cce3 100644
// A snapshot of the most recent Report Block with additional data of
// interest to statistics. Used to implement RTCRemoteInboundRtpStreamStats.
diff --git a/modules/rtp_rtcp/source/rtp_rtcp_impl2.cc b/modules/rtp_rtcp/source/rtp_rtcp_impl2.cc
-index 0df8543497..31a1b764b3 100644
+index 1d73bc3d87..d7e3c50f82 100644
--- a/modules/rtp_rtcp/source/rtp_rtcp_impl2.cc
+++ b/modules/rtp_rtcp/source/rtp_rtcp_impl2.cc
@@ -504,8 +504,10 @@ void ModuleRtpRtcpImpl2::GetSendStreamDataCounters(
diff --git a/third_party/libwebrtc/moz-patch-stack/0008.patch b/third_party/libwebrtc/moz-patch-stack/0008.patch
index 7d97e4804e..4b7bb94bd6 100644
--- a/third_party/libwebrtc/moz-patch-stack/0008.patch
+++ b/third_party/libwebrtc/moz-patch-stack/0008.patch
@@ -29,10 +29,10 @@ index 7e38b5bd08..e4a2cc2fc9 100644
} // namespace webrtc
diff --git a/modules/desktop_capture/mac/screen_capturer_mac.mm b/modules/desktop_capture/mac/screen_capturer_mac.mm
-index 28cc410573..b082136e76 100644
+index 60089fd0f2..a2370ed695 100644
--- a/modules/desktop_capture/mac/screen_capturer_mac.mm
+++ b/modules/desktop_capture/mac/screen_capturer_mac.mm
-@@ -182,6 +182,7 @@ void ScreenCapturerMac::Start(Callback* callback) {
+@@ -182,6 +182,7 @@ DesktopRect GetExcludedWindowPixelBounds(CGWindowID window, float dip_to_pixel_s
"webrtc", "ScreenCapturermac::Start", "target display id ", current_display_);
callback_ = callback;
@@ -40,7 +40,7 @@ index 28cc410573..b082136e76 100644
// Start and operate CGDisplayStream handler all from capture thread.
if (!RegisterRefreshAndMoveHandlers()) {
RTC_LOG(LS_ERROR) << "Failed to register refresh and move handlers.";
-@@ -202,7 +203,8 @@ void ScreenCapturerMac::CaptureFrame() {
+@@ -202,7 +203,8 @@ DesktopRect GetExcludedWindowPixelBounds(CGWindowID window, float dip_to_pixel_s
}
MacDesktopConfiguration new_config = desktop_config_monitor_->desktop_configuration();
@@ -54,7 +54,7 @@ diff --git a/modules/desktop_capture/mouse_cursor_monitor_mac.mm b/modules/deskt
index 3db4332cd1..512103ab5e 100644
--- a/modules/desktop_capture/mouse_cursor_monitor_mac.mm
+++ b/modules/desktop_capture/mouse_cursor_monitor_mac.mm
-@@ -133,7 +133,7 @@ void MouseCursorMonitorMac::CaptureImage(float scale) {
+@@ -133,7 +133,7 @@ void DisplaysReconfigured(CGDirectDisplayID display,
NSSize nssize = [nsimage size]; // DIP size
// No need to caputre cursor image if it's unchanged since last capture.
diff --git a/third_party/libwebrtc/moz-patch-stack/0009.patch b/third_party/libwebrtc/moz-patch-stack/0009.patch
index 793bf996b8..58b2ab9994 100644
--- a/third_party/libwebrtc/moz-patch-stack/0009.patch
+++ b/third_party/libwebrtc/moz-patch-stack/0009.patch
@@ -16,10 +16,10 @@ Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/9314046d89ebc0836
2 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/modules/desktop_capture/mac/screen_capturer_mac.mm b/modules/desktop_capture/mac/screen_capturer_mac.mm
-index b082136e76..1f4a62f7cd 100644
+index a2370ed695..785a15dfa4 100644
--- a/modules/desktop_capture/mac/screen_capturer_mac.mm
+++ b/modules/desktop_capture/mac/screen_capturer_mac.mm
-@@ -276,7 +276,8 @@ bool ScreenCapturerMac::GetSourceList(SourceList* screens) {
+@@ -276,7 +276,8 @@ DesktopRect GetExcludedWindowPixelBounds(CGWindowID window, float dip_to_pixel_s
for (MacDisplayConfigurations::iterator it = desktop_config_.displays.begin();
it != desktop_config_.displays.end();
++it) {
diff --git a/third_party/libwebrtc/moz-patch-stack/0030.patch b/third_party/libwebrtc/moz-patch-stack/0030.patch
index c5c638459c..8c6652af46 100644
--- a/third_party/libwebrtc/moz-patch-stack/0030.patch
+++ b/third_party/libwebrtc/moz-patch-stack/0030.patch
@@ -55,13 +55,21 @@ Bug 1774628 - re-enable support for Windows.Graphics.Capture APIs in libwebrtc.
Differential Revision: https://phabricator.services.mozilla.com/D186862
Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/08567f4539a12b54202aecbf554ec6540fb99ab2
+
+Bug 1876843 - (fix-082cb56ee7) remove mozilla dependency on pc:media_factory.
+
+Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/136b3fc0377be6dcaa302469d27968f445e0355e
+
+Bug 1876843 - (fix-b29ff000da) remove mozilla dependency on api:enable_media
+
+Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/7f403ee038e9797a1aff6161fc70a2d92769851f
---
.gn | 2 +
- BUILD.gn | 45 ++++++++++++++++++-
- api/BUILD.gn | 34 +++++++++++++-
+ BUILD.gn | 46 ++++++++++++++++++-
+ api/BUILD.gn | 36 ++++++++++++++-
api/rtp_sender_interface.h | 4 +-
api/rtp_sender_setparameters_callback.cc | 27 +++++++++++
- api/rtp_sender_setparameters_callback.h | 28 ++++++++++++
+ api/rtp_sender_setparameters_callback.h | 28 +++++++++++
api/task_queue/BUILD.gn | 2 +
api/transport/BUILD.gn | 2 +
call/BUILD.gn | 14 +++++-
@@ -70,24 +78,24 @@ Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/08567f4539a12b542
common_audio/BUILD.gn | 4 --
common_audio/fir_filter_avx2.cc | 2 +
common_audio/intrin.h | 8 ++++
- media/BUILD.gn | 35 ++++++++++++++-
+ media/BUILD.gn | 35 +++++++++++++-
media/base/media_channel.h | 4 --
media/base/media_channel_impl.cc | 13 ------
modules/audio_coding/BUILD.gn | 2 +-
modules/audio_device/BUILD.gn | 17 +++++--
- modules/audio_processing/aec3/BUILD.gn | 13 +++---
+ modules/audio_processing/aec3/BUILD.gn | 13 ++----
.../aec3/adaptive_fir_filter_avx2.cc | 2 +-
.../audio_processing/agc2/rnn_vad/BUILD.gn | 2 +-
modules/desktop_capture/BUILD.gn | 29 +-----------
modules/portal/BUILD.gn | 24 ++++++++++
modules/utility/BUILD.gn | 4 ++
modules/video_capture/BUILD.gn | 11 +----
- rtc_base/BUILD.gn | 30 ++++++++++++-
+ rtc_base/BUILD.gn | 30 +++++++++++-
rtc_base/system/BUILD.gn | 2 +-
- test/BUILD.gn | 10 +++++
+ test/BUILD.gn | 10 ++++
video/BUILD.gn | 4 +-
webrtc.gni | 32 ++++++++-----
- 31 files changed, 311 insertions(+), 99 deletions(-)
+ 31 files changed, 315 insertions(+), 98 deletions(-)
create mode 100644 api/rtp_sender_setparameters_callback.cc
create mode 100644 api/rtp_sender_setparameters_callback.h
create mode 100644 common_audio/intrin.h
@@ -106,7 +114,7 @@ index b9948d2fcd..77cfa94d8a 100644
# TODO(https://bugs.webrtc.org/14437): Remove this section if general
# Chromium fix resolves the problem.
diff --git a/BUILD.gn b/BUILD.gn
-index 7e8325e306..f61f8965b9 100644
+index 571049f3e4..f393179bbb 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -33,7 +33,7 @@ if (is_android) {
@@ -141,13 +149,14 @@ index 7e8325e306..f61f8965b9 100644
if (!build_with_chromium) {
# Target to build all the WebRTC production code.
-@@ -532,6 +538,33 @@ if (!build_with_chromium) {
+@@ -533,6 +539,34 @@ if (!build_with_chromium) {
"sdk",
"video",
]
+ if (build_with_mozilla) {
+ deps -= [
+ "api:create_peerconnection_factory",
++ "api:enable_media",
+ "api:rtc_error",
+ "api:transport_api",
+ "api/crypto",
@@ -175,7 +184,7 @@ index 7e8325e306..f61f8965b9 100644
if (rtc_include_builtin_audio_codecs) {
deps += [
-@@ -544,6 +577,16 @@ if (!build_with_chromium) {
+@@ -545,6 +579,16 @@ if (!build_with_chromium) {
deps += [
"api/video:video_frame",
"api/video:video_rtp_headers",
@@ -193,10 +202,20 @@ index 7e8325e306..f61f8965b9 100644
} else {
deps += [
diff --git a/api/BUILD.gn b/api/BUILD.gn
-index 46703f93ce..d2b7f06c87 100644
+index ee162577c8..10a4c8c95f 100644
--- a/api/BUILD.gn
+++ b/api/BUILD.gn
-@@ -35,7 +35,7 @@ rtc_source_set("callfactory_api") {
+@@ -49,6 +49,9 @@ rtc_source_set("enable_media") {
+ "../rtc_base/system:rtc_export",
+ "environment",
+ ]
++ if (build_with_mozilla) {
++ deps -= [ "../pc:media_factory" ]
++ }
+ }
+
+ rtc_source_set("enable_media_with_defaults") {
+@@ -75,7 +78,7 @@ rtc_source_set("enable_media_with_defaults") {
]
}
@@ -204,8 +223,8 @@ index 46703f93ce..d2b7f06c87 100644
+if (!build_with_chromium && !build_with_mozilla) {
rtc_library("create_peerconnection_factory") {
visibility = [ "*" ]
- allow_poison = [ "default_task_queue" ]
-@@ -189,6 +189,10 @@ rtc_source_set("ice_transport_interface") {
+ allow_poison = [ "environment_construction" ]
+@@ -228,6 +231,10 @@ rtc_source_set("ice_transport_interface") {
}
rtc_library("dtls_transport_interface") {
@@ -216,7 +235,7 @@ index 46703f93ce..d2b7f06c87 100644
visibility = [ "*" ]
sources = [
-@@ -205,6 +209,7 @@ rtc_library("dtls_transport_interface") {
+@@ -244,6 +251,7 @@ rtc_library("dtls_transport_interface") {
]
absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ]
}
@@ -224,7 +243,7 @@ index 46703f93ce..d2b7f06c87 100644
rtc_library("dtmf_sender_interface") {
visibility = [ "*" ]
-@@ -217,6 +222,10 @@ rtc_library("dtmf_sender_interface") {
+@@ -256,6 +264,10 @@ rtc_library("dtmf_sender_interface") {
}
rtc_library("rtp_sender_interface") {
@@ -235,17 +254,14 @@ index 46703f93ce..d2b7f06c87 100644
visibility = [ "*" ]
sources = [
-@@ -228,8 +237,8 @@ rtc_library("rtp_sender_interface") {
- ":dtmf_sender_interface",
- ":frame_transformer_interface",
- ":media_stream_interface",
-- ":rtc_error",
+@@ -270,16 +282,31 @@ rtc_library("rtp_sender_interface") {
+ ":ref_count",
+ ":rtc_error",
":rtp_parameters",
+ ":rtp_sender_setparameters_callback",
":scoped_refptr",
"../rtc_base:checks",
- "../rtc_base:refcount",
-@@ -237,10 +246,24 @@ rtc_library("rtp_sender_interface") {
+ "../rtc_base/system:rtc_export",
"crypto:frame_encryptor_interface",
"video_codecs:video_codecs_api",
]
@@ -270,7 +286,7 @@ index 46703f93ce..d2b7f06c87 100644
visibility = [ "*" ]
cflags = []
sources = [
-@@ -357,6 +380,7 @@ rtc_library("libjingle_peerconnection_api") {
+@@ -396,6 +423,7 @@ rtc_library("libjingle_peerconnection_api") {
"//third_party/abseil-cpp/absl/types:optional",
]
}
@@ -278,7 +294,7 @@ index 46703f93ce..d2b7f06c87 100644
rtc_source_set("frame_transformer_interface") {
visibility = [ "*" ]
-@@ -548,6 +572,7 @@ rtc_source_set("peer_network_dependencies") {
+@@ -568,6 +596,7 @@ rtc_source_set("peer_network_dependencies") {
}
rtc_source_set("peer_connection_quality_test_fixture_api") {
@@ -286,7 +302,7 @@ index 46703f93ce..d2b7f06c87 100644
visibility = [ "*" ]
testonly = true
sources = [ "test/peerconnection_quality_test_fixture.h" ]
-@@ -598,6 +623,7 @@ rtc_source_set("peer_connection_quality_test_fixture_api") {
+@@ -618,6 +647,7 @@ rtc_source_set("peer_connection_quality_test_fixture_api") {
"//third_party/abseil-cpp/absl/types:optional",
]
}
@@ -294,7 +310,7 @@ index 46703f93ce..d2b7f06c87 100644
rtc_source_set("frame_generator_api") {
visibility = [ "*" ]
-@@ -716,6 +742,7 @@ rtc_library("create_frame_generator") {
+@@ -736,6 +766,7 @@ rtc_library("create_frame_generator") {
absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ]
}
@@ -302,7 +318,7 @@ index 46703f93ce..d2b7f06c87 100644
rtc_library("create_peer_connection_quality_test_frame_generator") {
visibility = [ "*" ]
testonly = true
-@@ -732,6 +759,7 @@ rtc_library("create_peer_connection_quality_test_frame_generator") {
+@@ -752,6 +783,7 @@ rtc_library("create_peer_connection_quality_test_frame_generator") {
]
absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ]
}
@@ -310,7 +326,7 @@ index 46703f93ce..d2b7f06c87 100644
rtc_source_set("libjingle_logging_api") {
visibility = [ "*" ]
-@@ -904,6 +932,7 @@ rtc_source_set("refcountedbase") {
+@@ -926,6 +958,7 @@ rtc_source_set("refcountedbase") {
]
}
@@ -318,7 +334,7 @@ index 46703f93ce..d2b7f06c87 100644
rtc_library("ice_transport_factory") {
visibility = [ "*" ]
sources = [
-@@ -922,6 +951,7 @@ rtc_library("ice_transport_factory") {
+@@ -944,6 +977,7 @@ rtc_library("ice_transport_factory") {
"rtc_event_log:rtc_event_log",
]
}
@@ -327,11 +343,11 @@ index 46703f93ce..d2b7f06c87 100644
rtc_library("neteq_simulator_api") {
visibility = [ "*" ]
diff --git a/api/rtp_sender_interface.h b/api/rtp_sender_interface.h
-index 41d35bc287..619b601f1f 100644
+index 7b8ab135c8..7090c233cd 100644
--- a/api/rtp_sender_interface.h
+++ b/api/rtp_sender_interface.h
@@ -32,9 +32,9 @@
- #include "rtc_base/ref_count.h"
+ #include "api/video_codecs/video_encoder_factory.h"
#include "rtc_base/system/rtc_export.h"
-namespace webrtc {
@@ -340,7 +356,7 @@ index 41d35bc287..619b601f1f 100644
-using SetParametersCallback = absl::AnyInvocable<void(RTCError) &&>;
+namespace webrtc {
- class RTC_EXPORT RtpSenderInterface : public rtc::RefCountInterface {
+ class RTC_EXPORT RtpSenderInterface : public webrtc::RefCountInterface {
public:
diff --git a/api/rtp_sender_setparameters_callback.cc b/api/rtp_sender_setparameters_callback.cc
new file mode 100644
@@ -410,7 +426,7 @@ index 0000000000..45194f5ace
+
+#endif // API_RTP_SENDER_SETPARAMETERS_CALLBACK_H_
diff --git a/api/task_queue/BUILD.gn b/api/task_queue/BUILD.gn
-index e0e2e50514..8d845e2735 100644
+index 246164c68b..d557d8f100 100644
--- a/api/task_queue/BUILD.gn
+++ b/api/task_queue/BUILD.gn
@@ -31,6 +31,7 @@ rtc_library("task_queue") {
@@ -450,7 +466,7 @@ index 84a0968ee9..c0209bf0d0 100644
if (rtc_include_tests) {
rtc_source_set("test_feedback_generator_interface") {
diff --git a/call/BUILD.gn b/call/BUILD.gn
-index 58473dc1ea..825097e8d4 100644
+index 66c8b2011a..fa733a67b9 100644
--- a/call/BUILD.gn
+++ b/call/BUILD.gn
@@ -46,7 +46,7 @@ rtc_library("call_interfaces") {
@@ -462,7 +478,7 @@ index 58473dc1ea..825097e8d4 100644
"../api:scoped_refptr",
"../api:transport_api",
"../api/adaptation:resource_adaptation_api",
-@@ -346,6 +346,16 @@ rtc_library("call") {
+@@ -347,6 +347,16 @@ rtc_library("call") {
"//third_party/abseil-cpp/absl/strings",
"//third_party/abseil-cpp/absl/types:optional",
]
@@ -479,7 +495,7 @@ index 58473dc1ea..825097e8d4 100644
}
rtc_source_set("receive_stream_interface") {
-@@ -373,7 +383,7 @@ rtc_library("video_stream_api") {
+@@ -374,7 +384,7 @@ rtc_library("video_stream_api") {
"../api:frame_transformer_interface",
"../api:rtp_headers",
"../api:rtp_parameters",
@@ -502,7 +518,7 @@ index 9c2fad652f..f9e49db574 100644
#include "call/audio_sender.h"
#include "call/rtp_config.h"
diff --git a/call/video_send_stream.h b/call/video_send_stream.h
-index 5fde44a719..1a0261be1b 100644
+index c305d60bcc..2b4ea5b66a 100644
--- a/call/video_send_stream.h
+++ b/call/video_send_stream.h
@@ -23,7 +23,7 @@
@@ -561,7 +577,7 @@ index 0000000000..f6ff7f218f
+ #endif
+#endif
diff --git a/media/BUILD.gn b/media/BUILD.gn
-index cf7c523201..97ad4a889a 100644
+index 295a748802..055bf75a19 100644
--- a/media/BUILD.gn
+++ b/media/BUILD.gn
@@ -64,7 +64,7 @@ rtc_library("rtc_media_base") {
@@ -601,7 +617,7 @@ index cf7c523201..97ad4a889a 100644
rtc_source_set("media_channel") {
sources = [ "base/media_channel.h" ]
-@@ -269,6 +280,7 @@ rtc_library("codec") {
+@@ -268,6 +279,7 @@ rtc_library("codec") {
}
rtc_library("rtp_utils") {
@@ -609,7 +625,7 @@ index cf7c523201..97ad4a889a 100644
sources = [
"base/rtp_utils.cc",
"base/rtp_utils.h",
-@@ -285,8 +297,10 @@ rtc_library("rtp_utils") {
+@@ -284,8 +296,10 @@ rtc_library("rtp_utils") {
]
absl_deps = [ "//third_party/abseil-cpp/absl/strings" ]
}
@@ -620,7 +636,7 @@ index cf7c523201..97ad4a889a 100644
sources = [
"base/stream_params.cc",
"base/stream_params.h",
-@@ -299,6 +313,7 @@ rtc_library("stream_params") {
+@@ -298,6 +312,7 @@ rtc_library("stream_params") {
]
absl_deps = [ "//third_party/abseil-cpp/absl/algorithm:container" ]
}
@@ -628,7 +644,7 @@ index cf7c523201..97ad4a889a 100644
rtc_library("media_constants") {
sources = [
-@@ -309,6 +324,7 @@ rtc_library("media_constants") {
+@@ -308,6 +323,7 @@ rtc_library("media_constants") {
}
rtc_library("turn_utils") {
@@ -636,7 +652,7 @@ index cf7c523201..97ad4a889a 100644
sources = [
"base/turn_utils.cc",
"base/turn_utils.h",
-@@ -319,14 +335,17 @@ rtc_library("turn_utils") {
+@@ -318,14 +334,17 @@ rtc_library("turn_utils") {
"../rtc_base/system:rtc_export",
]
}
@@ -654,7 +670,7 @@ index cf7c523201..97ad4a889a 100644
rtc_library("rtc_simulcast_encoder_adapter") {
visibility = [ "*" ]
-@@ -400,6 +419,12 @@ rtc_library("rtc_internal_video_codecs") {
+@@ -399,6 +418,12 @@ rtc_library("rtc_internal_video_codecs") {
"../system_wrappers:field_trial",
"../test:fake_video_codecs",
]
@@ -667,7 +683,7 @@ index cf7c523201..97ad4a889a 100644
if (enable_libaom) {
defines += [ "RTC_USE_LIBAOM_AV1_ENCODER" ]
-@@ -425,6 +450,14 @@ rtc_library("rtc_internal_video_codecs") {
+@@ -424,6 +449,14 @@ rtc_library("rtc_internal_video_codecs") {
"engine/multiplex_codec_factory.cc",
"engine/multiplex_codec_factory.h",
]
@@ -722,7 +738,7 @@ index e7e84c781c..5b41a9ccda 100644
using webrtc::FrameDecryptorInterface;
using webrtc::FrameEncryptorInterface;
diff --git a/modules/audio_coding/BUILD.gn b/modules/audio_coding/BUILD.gn
-index 61fecea691..3e4d7e0c25 100644
+index 5de99efa45..ddd1fd2656 100644
--- a/modules/audio_coding/BUILD.gn
+++ b/modules/audio_coding/BUILD.gn
@@ -553,7 +553,7 @@ rtc_library("webrtc_opus_wrapper") {
@@ -735,7 +751,7 @@ index 61fecea691..3e4d7e0c25 100644
}
diff --git a/modules/audio_device/BUILD.gn b/modules/audio_device/BUILD.gn
-index f1ab09d154..4726f93279 100644
+index 6f52cf8af1..a135f042db 100644
--- a/modules/audio_device/BUILD.gn
+++ b/modules/audio_device/BUILD.gn
@@ -30,6 +30,7 @@ rtc_source_set("audio_device_default") {
@@ -754,7 +770,7 @@ index f1ab09d154..4726f93279 100644
rtc_source_set("audio_device_api") {
visibility = [ "*" ]
-@@ -62,6 +64,7 @@ rtc_library("audio_device_config") {
+@@ -63,6 +65,7 @@ rtc_library("audio_device_config") {
}
rtc_library("audio_device_buffer") {
@@ -762,7 +778,7 @@ index f1ab09d154..4726f93279 100644
sources = [
"audio_device_buffer.cc",
"audio_device_buffer.h",
-@@ -88,6 +91,7 @@ rtc_library("audio_device_buffer") {
+@@ -89,6 +92,7 @@ rtc_library("audio_device_buffer") {
"../../system_wrappers:metrics",
]
}
@@ -770,7 +786,7 @@ index f1ab09d154..4726f93279 100644
rtc_library("audio_device_generic") {
sources = [
-@@ -265,6 +269,7 @@ if (!build_with_chromium) {
+@@ -266,6 +270,7 @@ if (!build_with_chromium) {
# Contains default implementations of webrtc::AudioDeviceModule for Windows,
# Linux, Mac, iOS and Android.
rtc_library("audio_device_impl") {
@@ -778,7 +794,7 @@ index f1ab09d154..4726f93279 100644
visibility = [ "*" ]
deps = [
":audio_device_api",
-@@ -314,9 +319,9 @@ rtc_library("audio_device_impl") {
+@@ -315,9 +320,9 @@ rtc_library("audio_device_impl") {
sources = [ "include/fake_audio_device.h" ]
if (build_with_mozilla) {
@@ -791,7 +807,7 @@ index f1ab09d154..4726f93279 100644
]
}
-@@ -421,6 +426,7 @@ rtc_library("audio_device_impl") {
+@@ -422,6 +427,7 @@ rtc_library("audio_device_impl") {
sources += [ "dummy/file_audio_device_factory.h" ]
}
}
@@ -799,7 +815,7 @@ index f1ab09d154..4726f93279 100644
if (is_mac) {
rtc_source_set("audio_device_impl_frameworks") {
-@@ -438,6 +444,7 @@ if (is_mac) {
+@@ -439,6 +445,7 @@ if (is_mac) {
}
}
@@ -807,7 +823,7 @@ index f1ab09d154..4726f93279 100644
rtc_source_set("mock_audio_device") {
visibility = [ "*" ]
testonly = true
-@@ -454,8 +461,10 @@ rtc_source_set("mock_audio_device") {
+@@ -455,8 +462,10 @@ rtc_source_set("mock_audio_device") {
"../../test:test_support",
]
}
@@ -1012,10 +1028,10 @@ index 730ec9bfdd..d473dbb74c 100644
"/config/external/nspr",
"/nsprpub/lib/ds",
diff --git a/rtc_base/BUILD.gn b/rtc_base/BUILD.gn
-index 99d8e48e39..b411811b70 100644
+index 9f5f0aad56..089b9971a3 100644
--- a/rtc_base/BUILD.gn
+++ b/rtc_base/BUILD.gn
-@@ -323,6 +323,7 @@ rtc_library("sample_counter") {
+@@ -327,6 +327,7 @@ rtc_library("sample_counter") {
absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ]
}
@@ -1023,7 +1039,7 @@ index 99d8e48e39..b411811b70 100644
rtc_library("timestamp_aligner") {
visibility = [ "*" ]
sources = [
-@@ -336,6 +337,7 @@ rtc_library("timestamp_aligner") {
+@@ -340,6 +341,7 @@ rtc_library("timestamp_aligner") {
"system:rtc_export",
]
}
@@ -1031,7 +1047,7 @@ index 99d8e48e39..b411811b70 100644
rtc_library("zero_memory") {
visibility = [ "*" ]
-@@ -869,7 +871,9 @@ rtc_library("rtc_json") {
+@@ -875,7 +877,9 @@ rtc_library("rtc_json") {
"strings/json.h",
]
deps = [ ":stringutils" ]
@@ -1041,7 +1057,7 @@ index 99d8e48e39..b411811b70 100644
if (rtc_build_json) {
deps += [ "//third_party/jsoncpp" ]
} else {
-@@ -1234,6 +1238,7 @@ if (!build_with_chromium) {
+@@ -1223,6 +1227,7 @@ if (!build_with_chromium) {
}
rtc_library("network") {
@@ -1049,7 +1065,7 @@ index 99d8e48e39..b411811b70 100644
visibility = [ "*" ]
sources = [
"network.cc",
-@@ -1272,16 +1277,20 @@ rtc_library("network") {
+@@ -1261,16 +1266,20 @@ rtc_library("network") {
deps += [ ":win32" ]
}
}
@@ -1070,7 +1086,7 @@ index 99d8e48e39..b411811b70 100644
visibility = [ "*" ]
sources = [
"net_helper.cc",
-@@ -1290,8 +1299,10 @@ rtc_library("net_helper") {
+@@ -1279,8 +1288,10 @@ rtc_library("net_helper") {
absl_deps = [ "//third_party/abseil-cpp/absl/strings" ]
deps = [ "system:rtc_export" ]
}
@@ -1081,7 +1097,7 @@ index 99d8e48e39..b411811b70 100644
visibility = [ "*" ]
sources = [
"socket_adapters.cc",
-@@ -1311,6 +1322,7 @@ rtc_library("socket_adapters") {
+@@ -1300,6 +1311,7 @@ rtc_library("socket_adapters") {
]
absl_deps = [ "//third_party/abseil-cpp/absl/strings" ]
}
@@ -1089,7 +1105,7 @@ index 99d8e48e39..b411811b70 100644
rtc_library("network_route") {
sources = [
-@@ -1325,6 +1337,7 @@ rtc_library("network_route") {
+@@ -1314,6 +1326,7 @@ rtc_library("network_route") {
}
rtc_library("async_tcp_socket") {
@@ -1097,7 +1113,7 @@ index 99d8e48e39..b411811b70 100644
sources = [
"async_tcp_socket.cc",
"async_tcp_socket.h",
-@@ -1342,8 +1355,10 @@ rtc_library("async_tcp_socket") {
+@@ -1331,8 +1344,10 @@ rtc_library("async_tcp_socket") {
"network:sent_packet",
]
}
@@ -1108,7 +1124,7 @@ index 99d8e48e39..b411811b70 100644
visibility = [ "*" ]
sources = [
"async_udp_socket.cc",
-@@ -1365,8 +1380,10 @@ rtc_library("async_udp_socket") {
+@@ -1354,8 +1369,10 @@ rtc_library("async_udp_socket") {
]
absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ]
}
@@ -1119,15 +1135,15 @@ index 99d8e48e39..b411811b70 100644
visibility = [ "*" ]
sources = [
"async_packet_socket.cc",
-@@ -1384,6 +1401,7 @@ rtc_library("async_packet_socket") {
+@@ -1375,6 +1392,7 @@ rtc_library("async_packet_socket") {
"third_party/sigslot",
]
}
+}
- rtc_library("mdns_responder_interface") {
- sources = [ "mdns_responder_interface.h" ]
-@@ -1396,6 +1414,7 @@ rtc_library("dscp") {
+ if (rtc_include_tests) {
+ rtc_library("async_packet_socket_unittest") {
+@@ -1402,6 +1420,7 @@ rtc_library("dscp") {
}
rtc_library("proxy_info") {
@@ -1135,7 +1151,7 @@ index 99d8e48e39..b411811b70 100644
visibility = [ "*" ]
sources = [
"proxy_info.cc",
-@@ -1406,6 +1425,7 @@ rtc_library("proxy_info") {
+@@ -1412,6 +1431,7 @@ rtc_library("proxy_info") {
":socket_address",
]
}
@@ -1143,7 +1159,7 @@ index 99d8e48e39..b411811b70 100644
rtc_library("file_rotating_stream") {
sources = [
-@@ -1434,6 +1454,7 @@ rtc_library("data_rate_limiter") {
+@@ -1440,6 +1460,7 @@ rtc_library("data_rate_limiter") {
}
rtc_library("unique_id_generator") {
@@ -1151,7 +1167,7 @@ index 99d8e48e39..b411811b70 100644
sources = [
"unique_id_generator.cc",
"unique_id_generator.h",
-@@ -1448,6 +1469,7 @@ rtc_library("unique_id_generator") {
+@@ -1454,6 +1475,7 @@ rtc_library("unique_id_generator") {
]
absl_deps = [ "//third_party/abseil-cpp/absl/strings" ]
}
@@ -1159,7 +1175,7 @@ index 99d8e48e39..b411811b70 100644
rtc_library("crc32") {
sources = [
-@@ -1475,6 +1497,7 @@ rtc_library("stream") {
+@@ -1481,6 +1503,7 @@ rtc_library("stream") {
}
rtc_library("rtc_certificate_generator") {
@@ -1167,7 +1183,7 @@ index 99d8e48e39..b411811b70 100644
visibility = [ "*" ]
sources = [
"rtc_certificate_generator.cc",
-@@ -1492,8 +1515,10 @@ rtc_library("rtc_certificate_generator") {
+@@ -1498,8 +1521,10 @@ rtc_library("rtc_certificate_generator") {
"//third_party/abseil-cpp/absl/types:optional",
]
}
@@ -1178,7 +1194,7 @@ index 99d8e48e39..b411811b70 100644
visibility = [ "*" ]
sources = [
"helpers.cc",
-@@ -1594,6 +1619,7 @@ rtc_library("ssl") {
+@@ -1600,6 +1625,7 @@ rtc_library("ssl") {
deps += [ ":win32" ]
}
}
@@ -1186,7 +1202,7 @@ index 99d8e48e39..b411811b70 100644
rtc_library("crypt_string") {
sources = [
-@@ -1603,6 +1629,7 @@ rtc_library("crypt_string") {
+@@ -1609,6 +1635,7 @@ rtc_library("crypt_string") {
}
rtc_library("http_common") {
@@ -1194,7 +1210,7 @@ index 99d8e48e39..b411811b70 100644
sources = [
"http_common.cc",
"http_common.h",
-@@ -1619,6 +1646,7 @@ rtc_library("http_common") {
+@@ -1625,6 +1652,7 @@ rtc_library("http_common") {
absl_deps = [ "//third_party/abseil-cpp/absl/strings" ]
}
@@ -1202,7 +1218,7 @@ index 99d8e48e39..b411811b70 100644
rtc_source_set("gtest_prod") {
sources = [ "gtest_prod_util.h" ]
-@@ -2176,7 +2204,7 @@ if (rtc_include_tests) {
+@@ -2189,7 +2217,7 @@ if (rtc_include_tests) {
}
}
@@ -1225,7 +1241,7 @@ index 77f5139a2f..486b37590c 100644
deps += [
"..:logging",
diff --git a/test/BUILD.gn b/test/BUILD.gn
-index be8ee1684e..f7980f941d 100644
+index d313c6d82d..854530c01e 100644
--- a/test/BUILD.gn
+++ b/test/BUILD.gn
@@ -241,6 +241,7 @@ rtc_library("audio_test_common") {
@@ -1267,7 +1283,7 @@ index be8ee1684e..f7980f941d 100644
sources += [ "testsupport/jpeg_frame_writer.cc" ]
} else {
sources += [ "testsupport/jpeg_frame_writer_ios.cc" ]
-@@ -1322,6 +1330,7 @@ if (!build_with_chromium) {
+@@ -1331,6 +1339,7 @@ if (!build_with_chromium) {
}
}
@@ -1275,7 +1291,7 @@ index be8ee1684e..f7980f941d 100644
if (!build_with_chromium && is_android) {
rtc_android_library("native_test_java") {
testonly = true
-@@ -1335,6 +1344,7 @@ if (!build_with_chromium && is_android) {
+@@ -1344,6 +1353,7 @@ if (!build_with_chromium && is_android) {
]
}
}
@@ -1284,7 +1300,7 @@ index be8ee1684e..f7980f941d 100644
rtc_library("call_config_utils") {
testonly = true
diff --git a/video/BUILD.gn b/video/BUILD.gn
-index 1722ad4f6e..e84b73a352 100644
+index 204c6b66f4..0a930053c0 100644
--- a/video/BUILD.gn
+++ b/video/BUILD.gn
@@ -17,7 +17,7 @@ rtc_library("video_stream_encoder_interface") {
@@ -1306,7 +1322,7 @@ index 1722ad4f6e..e84b73a352 100644
"../api/adaptation:resource_adaptation_api",
"../api/task_queue:pending_task_safety_flag",
diff --git a/webrtc.gni b/webrtc.gni
-index 912b9b4ef0..1ee647b69d 100644
+index 277c12c054..c84464e84a 100644
--- a/webrtc.gni
+++ b/webrtc.gni
@@ -35,6 +35,11 @@ if (is_mac) {
@@ -1406,7 +1422,7 @@ index 912b9b4ef0..1ee647b69d 100644
# Make it possible to provide custom locations for some libraries (move these
# up into declare_args should we need to actually use them for the GN build).
rtc_libvpx_dir = "//third_party/libvpx"
-@@ -1144,7 +1154,7 @@ if (is_mac || is_ios) {
+@@ -1146,7 +1156,7 @@ if (is_mac || is_ios) {
}
}
diff --git a/third_party/libwebrtc/moz-patch-stack/0031.patch b/third_party/libwebrtc/moz-patch-stack/0031.patch
index 4602f40c31..0b1372a1dd 100644
--- a/third_party/libwebrtc/moz-patch-stack/0031.patch
+++ b/third_party/libwebrtc/moz-patch-stack/0031.patch
@@ -9,10 +9,10 @@ Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/7163801a480d60700
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/audio/channel_send.cc b/audio/channel_send.cc
-index 08dd74591d..81d5c66652 100644
+index ecf2cb5175..310e0517cf 100644
--- a/audio/channel_send.cc
+++ b/audio/channel_send.cc
-@@ -625,9 +625,9 @@ void ChannelSend::SetSendAudioLevelIndicationStatus(bool enable, int id) {
+@@ -639,9 +639,9 @@ void ChannelSend::SetSendAudioLevelIndicationStatus(bool enable, int id) {
RTC_DCHECK_RUN_ON(&worker_thread_checker_);
include_audio_level_indication_.store(enable);
if (enable) {
diff --git a/third_party/libwebrtc/moz-patch-stack/0033.patch b/third_party/libwebrtc/moz-patch-stack/0033.patch
index 2742e376b0..5c69ef0bce 100644
--- a/third_party/libwebrtc/moz-patch-stack/0033.patch
+++ b/third_party/libwebrtc/moz-patch-stack/0033.patch
@@ -15,7 +15,7 @@ Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/d380a43d59f4f7cbc
4 files changed, 35 insertions(+)
diff --git a/audio/audio_send_stream.cc b/audio/audio_send_stream.cc
-index 0caf59a20e..bffb910832 100644
+index c9dc42c04e..e7ebb2bf4e 100644
--- a/audio/audio_send_stream.cc
+++ b/audio/audio_send_stream.cc
@@ -431,6 +431,7 @@ webrtc::AudioSendStream::Stats AudioSendStream::GetStats(
@@ -27,10 +27,10 @@ index 0caf59a20e..bffb910832 100644
stats.header_and_padding_bytes_sent =
call_stats.header_and_padding_bytes_sent;
diff --git a/audio/channel_send.cc b/audio/channel_send.cc
-index 81d5c66652..ddc3323df9 100644
+index 310e0517cf..549e65a59c 100644
--- a/audio/channel_send.cc
+++ b/audio/channel_send.cc
-@@ -55,6 +55,31 @@ constexpr int64_t kMinRetransmissionWindowMs = 30;
+@@ -56,6 +56,31 @@ constexpr int64_t kMinRetransmissionWindowMs = 30;
class RtpPacketSenderProxy;
class TransportSequenceNumberProxy;
@@ -62,7 +62,7 @@ index 81d5c66652..ddc3323df9 100644
class ChannelSend : public ChannelSendInterface,
public AudioPacketizationCallback, // receive encoded
// packets from the ACM
-@@ -207,6 +232,8 @@ class ChannelSend : public ChannelSendInterface,
+@@ -210,6 +235,8 @@ class ChannelSend : public ChannelSendInterface,
bool input_mute_ RTC_GUARDED_BY(volume_settings_mutex_) = false;
bool previous_frame_muted_ RTC_GUARDED_BY(encoder_queue_) = false;
@@ -71,7 +71,7 @@ index 81d5c66652..ddc3323df9 100644
PacketRouter* packet_router_ RTC_GUARDED_BY(&worker_thread_checker_) =
nullptr;
const std::unique_ptr<RtpPacketSenderProxy> rtp_packet_pacer_proxy_;
-@@ -387,6 +414,7 @@ ChannelSend::ChannelSend(
+@@ -398,6 +425,7 @@ ChannelSend::ChannelSend(
const FieldTrialsView& field_trials)
: ssrc_(ssrc),
event_log_(rtc_event_log),
@@ -79,7 +79,7 @@ index 81d5c66652..ddc3323df9 100644
rtp_packet_pacer_proxy_(new RtpPacketSenderProxy()),
retransmission_rate_limiter_(
new RateLimiter(clock, kMaxRetransmissionWindowMs)),
-@@ -411,6 +439,8 @@ ChannelSend::ChannelSend(
+@@ -423,6 +451,8 @@ ChannelSend::ChannelSend(
configuration.event_log = event_log_;
configuration.rtt_stats = rtcp_rtt_stats;
@@ -88,7 +88,7 @@ index 81d5c66652..ddc3323df9 100644
if (field_trials.IsDisabled("WebRTC-DisableRtxRateLimiter")) {
configuration.retransmission_rate_limiter =
retransmission_rate_limiter_.get();
-@@ -673,6 +703,7 @@ CallSendStatistics ChannelSend::GetRTCPStatistics() const {
+@@ -687,6 +717,7 @@ CallSendStatistics ChannelSend::GetRTCPStatistics() const {
RTC_DCHECK_RUN_ON(&worker_thread_checker_);
CallSendStatistics stats = {0};
stats.rttMs = GetRTT();
@@ -97,7 +97,7 @@ index 81d5c66652..ddc3323df9 100644
StreamDataCounters rtp_stats;
StreamDataCounters rtx_stats;
diff --git a/audio/channel_send.h b/audio/channel_send.h
-index 00d954c952..f0c9232296 100644
+index b6a6a37bf5..f36085c1fa 100644
--- a/audio/channel_send.h
+++ b/audio/channel_send.h
@@ -43,6 +43,7 @@ struct CallSendStatistics {
diff --git a/third_party/libwebrtc/moz-patch-stack/0041.patch b/third_party/libwebrtc/moz-patch-stack/0041.patch
index dc73538fe0..44fb08ed83 100644
--- a/third_party/libwebrtc/moz-patch-stack/0041.patch
+++ b/third_party/libwebrtc/moz-patch-stack/0041.patch
@@ -20,7 +20,7 @@ Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/58f47eacaf10d12e2
11 files changed, 27 insertions(+), 27 deletions(-)
diff --git a/BUILD.gn b/BUILD.gn
-index f61f8965b9..e514b4d6a7 100644
+index f393179bbb..d2ede84941 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -427,12 +427,12 @@ config("common_config") {
@@ -276,7 +276,7 @@ index 344c8a1531..dcc1b3c6ac 100644
config("x11_config") {
if (rtc_use_x11_extensions) {
diff --git a/webrtc.gni b/webrtc.gni
-index 1ee647b69d..44eaa2db04 100644
+index c84464e84a..ad69bf8f8e 100644
--- a/webrtc.gni
+++ b/webrtc.gni
@@ -175,13 +175,13 @@ declare_args() {
diff --git a/third_party/libwebrtc/moz-patch-stack/0042.patch b/third_party/libwebrtc/moz-patch-stack/0042.patch
index be51a5a8b6..42bc15e1f6 100644
--- a/third_party/libwebrtc/moz-patch-stack/0042.patch
+++ b/third_party/libwebrtc/moz-patch-stack/0042.patch
@@ -43,10 +43,10 @@ index 978bbb25b2..655b2761ac 100644
} // namespace
diff --git a/audio/channel_receive.cc b/audio/channel_receive.cc
-index f5d214f672..c714b1dd4d 100644
+index 8367b00113..aff21fa72a 100644
--- a/audio/channel_receive.cc
+++ b/audio/channel_receive.cc
-@@ -104,7 +104,8 @@ class ChannelReceive : public ChannelReceiveInterface,
+@@ -105,7 +105,8 @@ class ChannelReceive : public ChannelReceiveInterface,
absl::optional<AudioCodecPairId> codec_pair_id,
rtc::scoped_refptr<FrameDecryptorInterface> frame_decryptor,
const webrtc::CryptoOptions& crypto_options,
@@ -56,7 +56,7 @@ index f5d214f672..c714b1dd4d 100644
~ChannelReceive() override;
void SetSink(AudioSinkInterface* sink) override;
-@@ -538,7 +539,8 @@ ChannelReceive::ChannelReceive(
+@@ -541,7 +542,8 @@ ChannelReceive::ChannelReceive(
absl::optional<AudioCodecPairId> codec_pair_id,
rtc::scoped_refptr<FrameDecryptorInterface> frame_decryptor,
const webrtc::CryptoOptions& crypto_options,
@@ -66,7 +66,7 @@ index f5d214f672..c714b1dd4d 100644
: worker_thread_(TaskQueueBase::Current()),
event_log_(rtc_event_log),
rtp_receive_statistics_(ReceiveStatistics::Create(clock)),
-@@ -584,6 +586,7 @@ ChannelReceive::ChannelReceive(
+@@ -587,6 +589,7 @@ ChannelReceive::ChannelReceive(
configuration.local_media_ssrc = local_ssrc;
configuration.rtcp_packet_type_counter_observer = this;
configuration.non_sender_rtt_measurement = enable_non_sender_rtt;
@@ -74,7 +74,7 @@ index f5d214f672..c714b1dd4d 100644
if (frame_transformer)
InitFrameTransformerDelegate(std::move(frame_transformer));
-@@ -1111,13 +1114,15 @@ std::unique_ptr<ChannelReceiveInterface> CreateChannelReceive(
+@@ -1129,13 +1132,15 @@ std::unique_ptr<ChannelReceiveInterface> CreateChannelReceive(
absl::optional<AudioCodecPairId> codec_pair_id,
rtc::scoped_refptr<FrameDecryptorInterface> frame_decryptor,
const webrtc::CryptoOptions& crypto_options,
diff --git a/third_party/libwebrtc/moz-patch-stack/0044.patch b/third_party/libwebrtc/moz-patch-stack/0044.patch
index ec7206b4fa..7fdc82d1da 100644
--- a/third_party/libwebrtc/moz-patch-stack/0044.patch
+++ b/third_party/libwebrtc/moz-patch-stack/0044.patch
@@ -53,11 +53,11 @@ index 0178355262..be8bce770f 100644
// Implements RtpVideoFrameReceiver.
void ManageFrame(std::unique_ptr<RtpFrameObject> frame) override;
diff --git a/video/video_receive_stream2.cc b/video/video_receive_stream2.cc
-index 6c1df7d874..f50c3d0775 100644
+index af25c364de..776c49042b 100644
--- a/video/video_receive_stream2.cc
+++ b/video/video_receive_stream2.cc
-@@ -573,6 +573,14 @@ VideoReceiveStreamInterface::Stats VideoReceiveStream2::GetStats() const {
- }
+@@ -570,6 +570,14 @@ VideoReceiveStreamInterface::Stats VideoReceiveStream2::GetStats() const {
+ stats.rtx_rtp_stats = rtx_statistician->GetStats();
}
}
+
diff --git a/third_party/libwebrtc/moz-patch-stack/0049.patch b/third_party/libwebrtc/moz-patch-stack/0049.patch
index 3074531967..e94ab52a26 100644
--- a/third_party/libwebrtc/moz-patch-stack/0049.patch
+++ b/third_party/libwebrtc/moz-patch-stack/0049.patch
@@ -13,7 +13,7 @@ diff --git a/modules/desktop_capture/window_capturer_mac.mm b/modules/desktop_ca
index 56c94baada..8f5dc25516 100644
--- a/modules/desktop_capture/window_capturer_mac.mm
+++ b/modules/desktop_capture/window_capturer_mac.mm
-@@ -169,8 +169,9 @@ void WindowCapturerMac::CaptureFrame() {
+@@ -169,8 +169,9 @@ explicit WindowCapturerMac(
return webrtc::GetWindowList(
[sources](CFDictionaryRef window) {
WindowId window_id = GetWindowId(window);
diff --git a/third_party/libwebrtc/moz-patch-stack/0050.patch b/third_party/libwebrtc/moz-patch-stack/0050.patch
index 3c72c60db4..e36c3b83c6 100644
--- a/third_party/libwebrtc/moz-patch-stack/0050.patch
+++ b/third_party/libwebrtc/moz-patch-stack/0050.patch
@@ -106,7 +106,7 @@ index 742a69cce3..0b1266a2db 100644
// A snapshot of the most recent Report Block with additional data of
// interest to statistics. Used to implement RTCRemoteInboundRtpStreamStats.
diff --git a/modules/rtp_rtcp/source/rtp_rtcp_impl2.cc b/modules/rtp_rtcp/source/rtp_rtcp_impl2.cc
-index 31a1b764b3..ff482b39b6 100644
+index d7e3c50f82..31e8b71117 100644
--- a/modules/rtp_rtcp/source/rtp_rtcp_impl2.cc
+++ b/modules/rtp_rtcp/source/rtp_rtcp_impl2.cc
@@ -503,11 +503,11 @@ void ModuleRtpRtcpImpl2::GetSendStreamDataCounters(
@@ -190,10 +190,10 @@ index be8bce770f..0e96d7f2cd 100644
private:
// Implements RtpVideoFrameReceiver.
diff --git a/video/video_receive_stream2.cc b/video/video_receive_stream2.cc
-index f50c3d0775..2067f255f7 100644
+index 776c49042b..c04b43a1d1 100644
--- a/video/video_receive_stream2.cc
+++ b/video/video_receive_stream2.cc
-@@ -579,7 +579,8 @@ VideoReceiveStreamInterface::Stats VideoReceiveStream2::GetStats() const {
+@@ -576,7 +576,8 @@ VideoReceiveStreamInterface::Stats VideoReceiveStream2::GetStats() const {
// seem to be any support for these stats right now. So, we hack this in.
rtp_video_stream_receiver_.RemoteRTCPSenderInfo(
&stats.rtcp_sender_packets_sent, &stats.rtcp_sender_octets_sent,
diff --git a/third_party/libwebrtc/moz-patch-stack/0052.patch b/third_party/libwebrtc/moz-patch-stack/0052.patch
index 7835e41a3c..616b4fdcc7 100644
--- a/third_party/libwebrtc/moz-patch-stack/0052.patch
+++ b/third_party/libwebrtc/moz-patch-stack/0052.patch
@@ -24,10 +24,10 @@ Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/0744d68b8c944e699
10 files changed, 18 insertions(+), 6 deletions(-)
diff --git a/audio/channel_send.cc b/audio/channel_send.cc
-index ddc3323df9..777cc6d4c1 100644
+index 549e65a59c..8080f4a3b8 100644
--- a/audio/channel_send.cc
+++ b/audio/channel_send.cc
-@@ -431,7 +431,7 @@ ChannelSend::ChannelSend(
+@@ -443,7 +443,7 @@ ChannelSend::ChannelSend(
transport_controller->GetRtcpObserver();
configuration.transport_feedback_callback =
transport_controller->transport_feedback_observer();
@@ -37,17 +37,18 @@ index ddc3323df9..777cc6d4c1 100644
configuration.outgoing_transport = rtp_transport;
diff --git a/call/call.cc b/call/call.cc
-index 6b975edc6a..c0ee5a92f4 100644
+index 42b3b988ea..d2ac705274 100644
--- a/call/call.cc
+++ b/call/call.cc
-@@ -473,12 +473,14 @@ std::string Call::Stats::ToString(int64_t time_ms) const {
+@@ -473,6 +473,7 @@ std::string Call::Stats::ToString(int64_t time_ms) const {
return ss.str();
}
+/* Mozilla: Avoid this since it could use GetRealTimeClock().
std::unique_ptr<Call> Call::Create(const CallConfig& config) {
- Clock* clock = Clock::GetRealTimeClock();
- return Create(config, clock,
+ Clock* clock =
+ config.env.has_value() ? &config.env->clock() : Clock::GetRealTimeClock();
+@@ -480,6 +481,7 @@ std::unique_ptr<Call> Call::Create(const CallConfig& config) {
RtpTransportControllerSendFactory().Create(
config.ExtractTransportConfig(), clock));
}
diff --git a/third_party/libwebrtc/moz-patch-stack/0053.patch b/third_party/libwebrtc/moz-patch-stack/0053.patch
index 38f35ba6e0..56c0b13b8e 100644
--- a/third_party/libwebrtc/moz-patch-stack/0053.patch
+++ b/third_party/libwebrtc/moz-patch-stack/0053.patch
@@ -18,7 +18,7 @@ Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/0300b32b7de70fb89
5 files changed, 10 insertions(+), 3 deletions(-)
diff --git a/BUILD.gn b/BUILD.gn
-index e514b4d6a7..9fd472f10b 100644
+index d2ede84941..f595a2951a 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -221,6 +221,9 @@ config("common_inherited_config") {
@@ -74,7 +74,7 @@ index d64ea689bb..c3c6955a7b 100644
#endif // defined(WEBRTC_POSIX)
}
diff --git a/webrtc.gni b/webrtc.gni
-index 44eaa2db04..bacd158140 100644
+index ad69bf8f8e..e339ba25fe 100644
--- a/webrtc.gni
+++ b/webrtc.gni
@@ -360,7 +360,7 @@ rtc_opus_dir = "//third_party/opus"
diff --git a/third_party/libwebrtc/moz-patch-stack/0054.patch b/third_party/libwebrtc/moz-patch-stack/0054.patch
index a3f95bca6c..928198e5a4 100644
--- a/third_party/libwebrtc/moz-patch-stack/0054.patch
+++ b/third_party/libwebrtc/moz-patch-stack/0054.patch
@@ -15,7 +15,7 @@ Subject: Bug 1766646 - (fix) breakout Call::Stats and SharedModuleThread into
create mode 100644 call/call_basic_stats.h
diff --git a/call/BUILD.gn b/call/BUILD.gn
-index 825097e8d4..47018a570a 100644
+index fa733a67b9..626ed95066 100644
--- a/call/BUILD.gn
+++ b/call/BUILD.gn
@@ -33,6 +33,12 @@ rtc_library("call_interfaces") {
@@ -32,7 +32,7 @@ index 825097e8d4..47018a570a 100644
deps = [
":audio_sender_interface",
diff --git a/call/call.cc b/call/call.cc
-index c0ee5a92f4..0f3699501e 100644
+index d2ac705274..63dc370f1a 100644
--- a/call/call.cc
+++ b/call/call.cc
@@ -460,19 +460,6 @@ class Call final : public webrtc::Call,
@@ -54,7 +54,7 @@ index c0ee5a92f4..0f3699501e 100644
-
/* Mozilla: Avoid this since it could use GetRealTimeClock().
std::unique_ptr<Call> Call::Create(const CallConfig& config) {
- Clock* clock = Clock::GetRealTimeClock();
+ Clock* clock =
diff --git a/call/call.h b/call/call.h
index 6f8e4cd6d7..b36872f5b5 100644
--- a/call/call.h
diff --git a/third_party/libwebrtc/moz-patch-stack/0059.patch b/third_party/libwebrtc/moz-patch-stack/0059.patch
index a5825e03b3..018d4ac255 100644
--- a/third_party/libwebrtc/moz-patch-stack/0059.patch
+++ b/third_party/libwebrtc/moz-patch-stack/0059.patch
@@ -8,10 +8,10 @@ Subject: Bug 1766646 - (fix-f137b75a4d) specify default constructor on
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/modules/congestion_controller/goog_cc/loss_based_bwe_v2.cc b/modules/congestion_controller/goog_cc/loss_based_bwe_v2.cc
-index e26fcc6cb1..ef200869a6 100644
+index 95f821352a..8e1a3c4698 100644
--- a/modules/congestion_controller/goog_cc/loss_based_bwe_v2.cc
+++ b/modules/congestion_controller/goog_cc/loss_based_bwe_v2.cc
-@@ -508,7 +508,7 @@ absl::optional<LossBasedBweV2::Config> LossBasedBweV2::CreateConfig(
+@@ -546,7 +546,7 @@ absl::optional<LossBasedBweV2::Config> LossBasedBweV2::CreateConfig(
if (!enabled.Get()) {
return config;
}
@@ -19,4 +19,4 @@ index e26fcc6cb1..ef200869a6 100644
+ config.emplace(Config());
config->bandwidth_rampup_upper_bound_factor =
bandwidth_rampup_upper_bound_factor.Get();
- config->rampup_acceleration_max_factor = rampup_acceleration_max_factor.Get();
+ config->bandwidth_rampup_upper_bound_factor_in_hold =
diff --git a/third_party/libwebrtc/moz-patch-stack/0061.patch b/third_party/libwebrtc/moz-patch-stack/0061.patch
index 8b12fb66f9..cc24d61d85 100644
--- a/third_party/libwebrtc/moz-patch-stack/0061.patch
+++ b/third_party/libwebrtc/moz-patch-stack/0061.patch
@@ -2,12 +2,15 @@ From: Michael Froman <mjfroman@mac.com>
Date: Tue, 21 Jun 2022 11:17:46 -0500
Subject: Bug 1772380 - to upstream - ref count this in lambda capture
+Bug 1876843 - (fix-23cecc1d43) drop rtc:: prefix on scoped_ptr
+
+Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/2e4867d8cc9813869bd80f5201d3f7d84afcd412
---
modules/video_capture/linux/video_capture_v4l2.cc | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/modules/video_capture/linux/video_capture_v4l2.cc b/modules/video_capture/linux/video_capture_v4l2.cc
-index 00cede01cb..d6813b13fd 100644
+index 00cede01cb..97cd9a70eb 100644
--- a/modules/video_capture/linux/video_capture_v4l2.cc
+++ b/modules/video_capture/linux/video_capture_v4l2.cc
@@ -296,8 +296,8 @@ int32_t VideoCaptureModuleV4L2::StartCapture(
@@ -16,7 +19,7 @@ index 00cede01cb..d6813b13fd 100644
_captureThread = rtc::PlatformThread::SpawnJoinable(
- [this] {
- while (CaptureProcess()) {
-+ [self = rtc::scoped_refptr(this)] {
++ [self = scoped_refptr(this)] {
+ while (self->CaptureProcess()) {
}
},
diff --git a/third_party/libwebrtc/moz-patch-stack/0063.patch b/third_party/libwebrtc/moz-patch-stack/0063.patch
index a61bf11937..12bf71ee22 100644
--- a/third_party/libwebrtc/moz-patch-stack/0063.patch
+++ b/third_party/libwebrtc/moz-patch-stack/0063.patch
@@ -10,7 +10,7 @@ Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/e826dfadfe1264c59
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/modules/video_capture/video_capture.h b/modules/video_capture/video_capture.h
-index 58485f28e9..04203033b0 100644
+index 7279bed476..db07580921 100644
--- a/modules/video_capture/video_capture.h
+++ b/modules/video_capture/video_capture.h
@@ -16,6 +16,8 @@
@@ -22,7 +22,7 @@ index 58485f28e9..04203033b0 100644
#include <set>
#if defined(ANDROID)
-@@ -40,15 +42,18 @@ class VideoCaptureModule : public rtc::RefCountInterface {
+@@ -40,15 +42,18 @@ class VideoCaptureModule : public RefCountInterface {
virtual uint32_t NumberOfDevices() = 0;
virtual int32_t Refresh() = 0;
virtual void DeviceChange() {
@@ -41,7 +41,7 @@ index 58485f28e9..04203033b0 100644
auto it = _inputCallBacks.find(callBack);
if (it != _inputCallBacks.end()) {
_inputCallBacks.erase(it);
-@@ -102,7 +107,8 @@ class VideoCaptureModule : public rtc::RefCountInterface {
+@@ -102,7 +107,8 @@ class VideoCaptureModule : public RefCountInterface {
virtual ~DeviceInfo() {}
private:
diff --git a/third_party/libwebrtc/moz-patch-stack/0064.patch b/third_party/libwebrtc/moz-patch-stack/0064.patch
index c285381dbb..4d9c0c6550 100644
--- a/third_party/libwebrtc/moz-patch-stack/0064.patch
+++ b/third_party/libwebrtc/moz-patch-stack/0064.patch
@@ -13,7 +13,7 @@ Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/f097eb8cbd8b7686c
2 files changed, 9 insertions(+)
diff --git a/api/task_queue/BUILD.gn b/api/task_queue/BUILD.gn
-index 8d845e2735..760ceaa3ef 100644
+index d557d8f100..9b2f747e78 100644
--- a/api/task_queue/BUILD.gn
+++ b/api/task_queue/BUILD.gn
@@ -31,6 +31,11 @@ rtc_library("task_queue") {
@@ -29,10 +29,10 @@ index 8d845e2735..760ceaa3ef 100644
rtc_library("task_queue_test") {
visibility = [ "*" ]
diff --git a/rtc_base/BUILD.gn b/rtc_base/BUILD.gn
-index b411811b70..3d57e3bfb4 100644
+index 089b9971a3..5392e5f472 100644
--- a/rtc_base/BUILD.gn
+++ b/rtc_base/BUILD.gn
-@@ -737,10 +737,14 @@ if (is_mac || is_ios) {
+@@ -743,10 +743,14 @@ if (is_mac || is_ios) {
if (is_win) {
rtc_library("rtc_task_queue_win") {
visibility = [ "../api/task_queue:default_task_queue_factory" ]
diff --git a/third_party/libwebrtc/moz-patch-stack/0066.patch b/third_party/libwebrtc/moz-patch-stack/0066.patch
index 6c4bb19816..01f4a41c95 100644
--- a/third_party/libwebrtc/moz-patch-stack/0066.patch
+++ b/third_party/libwebrtc/moz-patch-stack/0066.patch
@@ -11,10 +11,10 @@ Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/b0658888969395dca
2 files changed, 10 insertions(+)
diff --git a/BUILD.gn b/BUILD.gn
-index 9fd472f10b..612483cef8 100644
+index f595a2951a..7feca08e60 100644
--- a/BUILD.gn
+++ b/BUILD.gn
-@@ -601,6 +601,10 @@ if (!build_with_chromium) {
+@@ -603,6 +603,10 @@ if (!build_with_chromium) {
]
}
@@ -26,7 +26,7 @@ index 9fd472f10b..612483cef8 100644
deps += [ "logging:rtc_event_log_proto" ]
}
diff --git a/sdk/BUILD.gn b/sdk/BUILD.gn
-index 4f5ceb5ed3..eea26dc31d 100644
+index e28bdcc5a2..586d4d1911 100644
--- a/sdk/BUILD.gn
+++ b/sdk/BUILD.gn
@@ -533,6 +533,7 @@ if (is_ios || is_mac) {
diff --git a/third_party/libwebrtc/moz-patch-stack/0067.patch b/third_party/libwebrtc/moz-patch-stack/0067.patch
index c7b6230bcc..52302d9a8d 100644
--- a/third_party/libwebrtc/moz-patch-stack/0067.patch
+++ b/third_party/libwebrtc/moz-patch-stack/0067.patch
@@ -13,10 +13,10 @@ Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/a7362238c9e6fbe0d
1 file changed, 3 insertions(+)
diff --git a/modules/video_capture/video_capture.h b/modules/video_capture/video_capture.h
-index 04203033b0..6614032299 100644
+index db07580921..43a6a7f832 100644
--- a/modules/video_capture/video_capture.h
+++ b/modules/video_capture/video_capture.h
-@@ -154,6 +154,9 @@ class VideoCaptureModule : public rtc::RefCountInterface {
+@@ -154,6 +154,9 @@ class VideoCaptureModule : public RefCountInterface {
// Return whether the rotation is applied or left pending.
virtual bool GetApplyRotation() = 0;
diff --git a/third_party/libwebrtc/moz-patch-stack/0068.patch b/third_party/libwebrtc/moz-patch-stack/0068.patch
index 93e486f645..ab46127e4f 100644
--- a/third_party/libwebrtc/moz-patch-stack/0068.patch
+++ b/third_party/libwebrtc/moz-patch-stack/0068.patch
@@ -273,7 +273,7 @@ index 0e96d7f2cd..10329005ba 100644
RTC_GUARDED_BY(packet_sequence_checker_);
UniqueTimestampCounter frame_counter_
diff --git a/video/video_receive_stream2.cc b/video/video_receive_stream2.cc
-index 2067f255f7..85cc0aa0dc 100644
+index c04b43a1d1..33e2f39ced 100644
--- a/video/video_receive_stream2.cc
+++ b/video/video_receive_stream2.cc
@@ -210,6 +210,7 @@ VideoReceiveStream2::VideoReceiveStream2(
diff --git a/third_party/libwebrtc/moz-patch-stack/0078.patch b/third_party/libwebrtc/moz-patch-stack/0078.patch
index b6198f6e9c..c7d811ffd7 100644
--- a/third_party/libwebrtc/moz-patch-stack/0078.patch
+++ b/third_party/libwebrtc/moz-patch-stack/0078.patch
@@ -1,60 +1,57 @@
-From: Andreas Pehrson <apehrson@mozilla.com>
-Date: Wed, 10 May 2023 07:06:00 +0000
-Subject: Bug 1810949 - cherry-pick upstream libwebrtc commit 91d5fc2ed6.
- r=webrtc-reviewers,mjf
+From: Nico Grunbaum <na-g@nostrum.com>
+Date: Tue, 6 Jun 2023 16:37:00 -0700
+Subject: Bug 1833237 - (fix-f0be3bee1f) remove reference to
+ portal:pipewire_base;r?pehrsons
-Upstream commit: https://webrtc.googlesource.com/src/+/91d5fc2ed6ef347d90182868320267d45cf9525b
- Support more pixel formats in v4l2 camera backend
-
- These were tested with gstreamer and v4l2loopback, example setup:
- $ sudo v4l2loopback-ctl add -n BGRA 10
- $ gst-launch-1.0 videotestsrc pattern=smpte-rp-219 ! \
- video/x-raw,format=BGRA ! v4l2sink device=/dev/video10 > /dev/null &
-
- Then conversion was confirmed with video_loopback:
- $ ./video_loopback --capture_device_index=3 --logs 2>&1 | grep -i \
- capture
-
- Bug: webrtc:14830
- Change-Id: I35c8e453cf7f9a2923935b0ad82477a3144e8c12
- Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/291532
- Commit-Queue: Stefan Holmer <stefan@webrtc.org>
- Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org>
- Reviewed-by: Stefan Holmer <stefan@webrtc.org>
- Cr-Commit-Position: refs/heads/main@{#39979}
-
-Differential Revision: https://phabricator.services.mozilla.com/D177232
-Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/92dc582fdcf3a2fdb3fcdbcd96080d081de8f8d5
+Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/8ff886a4d366b4be35b329d1ef733a6df542067c
---
- .../video_capture/linux/device_info_v4l2.cc | 18 ++++++++++++++++++
- 1 file changed, 18 insertions(+)
+ modules/video_capture/BUILD.gn | 4 ++++
+ modules/video_capture/linux/device_info_pipewire.cc | 4 ++--
+ modules/video_capture/linux/device_info_pipewire.h | 3 ++-
+ 3 files changed, 8 insertions(+), 3 deletions(-)
-diff --git a/modules/video_capture/linux/device_info_v4l2.cc b/modules/video_capture/linux/device_info_v4l2.cc
-index 04caaea592..abd2886f85 100644
---- a/modules/video_capture/linux/device_info_v4l2.cc
-+++ b/modules/video_capture/linux/device_info_v4l2.cc
-@@ -57,6 +57,24 @@
- #define BUF_LEN ( 1024 * ( EVENT_SIZE + 16 ) )
- #endif
+diff --git a/modules/video_capture/BUILD.gn b/modules/video_capture/BUILD.gn
+index 8f89918359..45a0272eee 100644
+--- a/modules/video_capture/BUILD.gn
++++ b/modules/video_capture/BUILD.gn
+@@ -104,6 +104,10 @@ if (!build_with_chromium || is_linux || is_chromeos) {
+ "../../media:rtc_media_base",
+ "../portal",
+ ]
++ if (build_with_mozilla) {
++ configs -= [ "../portal:pipewire_base" ]
++ public_deps = [ "//third_party/pipewire" ]
++ }
+ }
+ }
+ if (is_win) {
+diff --git a/modules/video_capture/linux/device_info_pipewire.cc b/modules/video_capture/linux/device_info_pipewire.cc
+index e9f7e6a869..fc0554f384 100644
+--- a/modules/video_capture/linux/device_info_pipewire.cc
++++ b/modules/video_capture/linux/device_info_pipewire.cc
+@@ -49,9 +49,9 @@ int32_t DeviceInfoPipeWire::GetDeviceName(uint32_t deviceNumber,
+ char* deviceUniqueIdUTF8,
+ uint32_t deviceUniqueIdUTF8Length,
+ char* productUniqueIdUTF8,
+- uint32_t productUniqueIdUTF8Length) {
++ uint32_t productUniqueIdUTF8Length,
++ pid_t* pid) {
+ RTC_CHECK(pipewire_session_);
+-
+ if (deviceNumber >= NumberOfDevices())
+ return -1;
-+// These defines are here to support building on kernel 3.16 which some
-+// downstream projects, e.g. Firefox, use.
-+// TODO(apehrson): Remove them and their undefs when no longer needed.
-+#ifndef V4L2_PIX_FMT_ABGR32
-+#define ABGR32_OVERRIDE 1
-+#define V4L2_PIX_FMT_ABGR32 v4l2_fourcc('A', 'R', '2', '4')
-+#endif
-+
-+#ifndef V4L2_PIX_FMT_ARGB32
-+#define ARGB32_OVERRIDE 1
-+#define V4L2_PIX_FMT_ARGB32 v4l2_fourcc('B', 'A', '2', '4')
-+#endif
-+
-+#ifndef V4L2_PIX_FMT_RGBA32
-+#define RGBA32_OVERRIDE 1
-+#define V4L2_PIX_FMT_RGBA32 v4l2_fourcc('A', 'B', '2', '4')
-+#endif
-+
- namespace webrtc {
- namespace videocapturemodule {
- #ifdef WEBRTC_LINUX
+diff --git a/modules/video_capture/linux/device_info_pipewire.h b/modules/video_capture/linux/device_info_pipewire.h
+index 4da0c7a90b..8a33d75892 100644
+--- a/modules/video_capture/linux/device_info_pipewire.h
++++ b/modules/video_capture/linux/device_info_pipewire.h
+@@ -29,7 +29,8 @@ class DeviceInfoPipeWire : public DeviceInfoImpl {
+ char* deviceUniqueIdUTF8,
+ uint32_t deviceUniqueIdUTF8Length,
+ char* productUniqueIdUTF8 = nullptr,
+- uint32_t productUniqueIdUTF8Length = 0) override;
++ uint32_t productUniqueIdUTF8Length = 0,
++ pid_t* pid = 0) override;
+ /*
+ * Fills the membervariable _captureCapabilities with capabilites for the
+ * given device name.
diff --git a/third_party/libwebrtc/moz-patch-stack/0079.patch b/third_party/libwebrtc/moz-patch-stack/0079.patch
index e7b81ca2ba..1e8257408f 100644
--- a/third_party/libwebrtc/moz-patch-stack/0079.patch
+++ b/third_party/libwebrtc/moz-patch-stack/0079.patch
@@ -1,55 +1,29 @@
-From: Nico Grunbaum <na-g@nostrum.com>
-Date: Tue, 6 Jun 2023 16:37:00 -0700
-Subject: Bug 1833237 - (fix-f0be3bee1f) remove reference to
- portal:pipewire_base;r?pehrsons
+From: Jan-Ivar Bruaroey <jib@mozilla.com>
+Date: Wed, 28 Jun 2023 20:45:00 -0400
+Subject: Bug 1839451 - (fix-0f43da2248) Keep mozilla's
+ RTCPReceiver::RemoteRTCPSenderInfo function working.
-Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/8ff886a4d366b4be35b329d1ef733a6df542067c
+Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/154c9cdb386d0f50c5e1549270e1af6ab4969602
---
- modules/video_capture/BUILD.gn | 4 ++++
- modules/video_capture/linux/device_info_pipewire.cc | 3 ++-
- modules/video_capture/linux/device_info_pipewire.h | 3 ++-
- 3 files changed, 8 insertions(+), 2 deletions(-)
+ modules/rtp_rtcp/source/rtcp_receiver.cc | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
-diff --git a/modules/video_capture/BUILD.gn b/modules/video_capture/BUILD.gn
-index 8f89918359..45a0272eee 100644
---- a/modules/video_capture/BUILD.gn
-+++ b/modules/video_capture/BUILD.gn
-@@ -104,6 +104,10 @@ if (!build_with_chromium || is_linux || is_chromeos) {
- "../../media:rtc_media_base",
- "../portal",
- ]
-+ if (build_with_mozilla) {
-+ configs -= [ "../portal:pipewire_base" ]
-+ public_deps = [ "//third_party/pipewire" ]
-+ }
- }
- }
- if (is_win) {
-diff --git a/modules/video_capture/linux/device_info_pipewire.cc b/modules/video_capture/linux/device_info_pipewire.cc
-index 1dee78f5ee..2cb6161514 100644
---- a/modules/video_capture/linux/device_info_pipewire.cc
-+++ b/modules/video_capture/linux/device_info_pipewire.cc
-@@ -47,7 +47,8 @@ int32_t DeviceInfoPipeWire::GetDeviceName(uint32_t deviceNumber,
- char* deviceUniqueIdUTF8,
- uint32_t deviceUniqueIdUTF8Length,
- char* productUniqueIdUTF8,
-- uint32_t productUniqueIdUTF8Length) {
-+ uint32_t productUniqueIdUTF8Length,
-+ pid_t* pid) {
- if (deviceNumber >= NumberOfDevices())
- return -1;
+diff --git a/modules/rtp_rtcp/source/rtcp_receiver.cc b/modules/rtp_rtcp/source/rtcp_receiver.cc
+index 94de316421..bda6ad9a52 100644
+--- a/modules/rtp_rtcp/source/rtcp_receiver.cc
++++ b/modules/rtp_rtcp/source/rtcp_receiver.cc
+@@ -368,10 +368,10 @@ void RTCPReceiver::RemoteRTCPSenderInfo(uint32_t* packet_count,
+ int64_t* ntp_timestamp_ms,
+ int64_t* remote_ntp_timestamp_ms) const {
+ MutexLock lock(&rtcp_receiver_lock_);
+- *packet_count = remote_sender_packet_count_;
+- *octet_count = remote_sender_octet_count_;
+- *ntp_timestamp_ms = last_received_sr_ntp_.ToMs();
+- *remote_ntp_timestamp_ms = remote_sender_ntp_time_.ToMs();
++ *packet_count = remote_sender_.packets_sent;
++ *octet_count = remote_sender_.bytes_sent;
++ *ntp_timestamp_ms = remote_sender_.last_arrival_timestamp.ToMs();
++ *remote_ntp_timestamp_ms = remote_sender_.last_remote_timestamp.ToMs();
+ }
-diff --git a/modules/video_capture/linux/device_info_pipewire.h b/modules/video_capture/linux/device_info_pipewire.h
-index a006c85d1b..724717be5e 100644
---- a/modules/video_capture/linux/device_info_pipewire.h
-+++ b/modules/video_capture/linux/device_info_pipewire.h
-@@ -29,7 +29,8 @@ class DeviceInfoPipeWire : public DeviceInfoImpl {
- char* deviceUniqueIdUTF8,
- uint32_t deviceUniqueIdUTF8Length,
- char* productUniqueIdUTF8 = nullptr,
-- uint32_t productUniqueIdUTF8Length = 0) override;
-+ uint32_t productUniqueIdUTF8Length = 0,
-+ pid_t* pid = 0) override;
- /*
- * Fills the membervariable _captureCapabilities with capabilites for the
- * given device name.
+ std::vector<ReportBlockData> RTCPReceiver::GetLatestReportBlockData() const {
diff --git a/third_party/libwebrtc/moz-patch-stack/0080.patch b/third_party/libwebrtc/moz-patch-stack/0080.patch
index 1e8257408f..25beff2915 100644
--- a/third_party/libwebrtc/moz-patch-stack/0080.patch
+++ b/third_party/libwebrtc/moz-patch-stack/0080.patch
@@ -1,29 +1,33 @@
-From: Jan-Ivar Bruaroey <jib@mozilla.com>
-Date: Wed, 28 Jun 2023 20:45:00 -0400
-Subject: Bug 1839451 - (fix-0f43da2248) Keep mozilla's
- RTCPReceiver::RemoteRTCPSenderInfo function working.
+From: Nico Grunbaum <na-g@nostrum.com>
+Date: Thu, 22 Jun 2023 16:23:00 +0000
+Subject: Bug 1837918 - libwebrtc update broke the build on
+ OpenBSD;r=mjf,webrtc-reviewers
-Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/154c9cdb386d0f50c5e1549270e1af6ab4969602
+Differential Revision: https://phabricator.services.mozilla.com/D181791
+Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/2a6a838b7021bb285f9485c2ceda6ba2543e0d6f
---
- modules/rtp_rtcp/source/rtcp_receiver.cc | 8 ++++----
- 1 file changed, 4 insertions(+), 4 deletions(-)
+ modules/video_capture/video_capture_options.h | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
-diff --git a/modules/rtp_rtcp/source/rtcp_receiver.cc b/modules/rtp_rtcp/source/rtcp_receiver.cc
-index 94de316421..bda6ad9a52 100644
---- a/modules/rtp_rtcp/source/rtcp_receiver.cc
-+++ b/modules/rtp_rtcp/source/rtcp_receiver.cc
-@@ -368,10 +368,10 @@ void RTCPReceiver::RemoteRTCPSenderInfo(uint32_t* packet_count,
- int64_t* ntp_timestamp_ms,
- int64_t* remote_ntp_timestamp_ms) const {
- MutexLock lock(&rtcp_receiver_lock_);
-- *packet_count = remote_sender_packet_count_;
-- *octet_count = remote_sender_octet_count_;
-- *ntp_timestamp_ms = last_received_sr_ntp_.ToMs();
-- *remote_ntp_timestamp_ms = remote_sender_ntp_time_.ToMs();
-+ *packet_count = remote_sender_.packets_sent;
-+ *octet_count = remote_sender_.bytes_sent;
-+ *ntp_timestamp_ms = remote_sender_.last_arrival_timestamp.ToMs();
-+ *remote_ntp_timestamp_ms = remote_sender_.last_remote_timestamp.ToMs();
- }
+diff --git a/modules/video_capture/video_capture_options.h b/modules/video_capture/video_capture_options.h
+index 6f72f7927e..37965305d9 100644
+--- a/modules/video_capture/video_capture_options.h
++++ b/modules/video_capture/video_capture_options.h
+@@ -55,7 +55,7 @@ class RTC_EXPORT VideoCaptureOptions {
- std::vector<ReportBlockData> RTCPReceiver::GetLatestReportBlockData() const {
+ void Init(Callback* callback);
+
+-#if defined(WEBRTC_LINUX)
++#if defined(WEBRTC_LINUX) || defined(WEBRTC_BSD)
+ bool allow_v4l2() const { return allow_v4l2_; }
+ void set_allow_v4l2(bool allow) { allow_v4l2_ = allow; }
+ #endif
+@@ -68,7 +68,7 @@ class RTC_EXPORT VideoCaptureOptions {
+ #endif
+
+ private:
+-#if defined(WEBRTC_LINUX)
++#if defined(WEBRTC_LINUX) || defined(WEBRTC_BSD)
+ bool allow_v4l2_ = false;
+ #endif
+ #if defined(WEBRTC_USE_PIPEWIRE)
diff --git a/third_party/libwebrtc/moz-patch-stack/0081.patch b/third_party/libwebrtc/moz-patch-stack/0081.patch
index 25beff2915..8a60e356af 100644
--- a/third_party/libwebrtc/moz-patch-stack/0081.patch
+++ b/third_party/libwebrtc/moz-patch-stack/0081.patch
@@ -1,33 +1,24 @@
-From: Nico Grunbaum <na-g@nostrum.com>
-Date: Thu, 22 Jun 2023 16:23:00 +0000
-Subject: Bug 1837918 - libwebrtc update broke the build on
- OpenBSD;r=mjf,webrtc-reviewers
+From: Michael Froman <mfroman@mozilla.com>
+Date: Wed, 5 Jul 2023 19:15:00 +0000
+Subject: Bug 1841864 - upstream commit 4baea5b07f should properly check size
+ of encoder_config_.simulcast_layers. r=jib
-Differential Revision: https://phabricator.services.mozilla.com/D181791
-Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/2a6a838b7021bb285f9485c2ceda6ba2543e0d6f
+Differential Revision: https://phabricator.services.mozilla.com/D182813
+Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/a7179d8d75313b6c9c76a496e10d102da019ff4f
---
- modules/video_capture/video_capture_options.h | 4 ++--
- 1 file changed, 2 insertions(+), 2 deletions(-)
+ video/video_stream_encoder.cc | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
-diff --git a/modules/video_capture/video_capture_options.h b/modules/video_capture/video_capture_options.h
-index 6f72f7927e..37965305d9 100644
---- a/modules/video_capture/video_capture_options.h
-+++ b/modules/video_capture/video_capture_options.h
-@@ -55,7 +55,7 @@ class RTC_EXPORT VideoCaptureOptions {
+diff --git a/video/video_stream_encoder.cc b/video/video_stream_encoder.cc
+index 552463e143..669f165635 100644
+--- a/video/video_stream_encoder.cc
++++ b/video/video_stream_encoder.cc
+@@ -1375,7 +1375,7 @@ void VideoStreamEncoder::ReconfigureEncoder() {
- void Init(Callback* callback);
-
--#if defined(WEBRTC_LINUX)
-+#if defined(WEBRTC_LINUX) || defined(WEBRTC_BSD)
- bool allow_v4l2() const { return allow_v4l2_; }
- void set_allow_v4l2(bool allow) { allow_v4l2_ = allow; }
- #endif
-@@ -68,7 +68,7 @@ class RTC_EXPORT VideoCaptureOptions {
- #endif
-
- private:
--#if defined(WEBRTC_LINUX)
-+#if defined(WEBRTC_LINUX) || defined(WEBRTC_BSD)
- bool allow_v4l2_ = false;
- #endif
- #if defined(WEBRTC_USE_PIPEWIRE)
+ bool is_svc = false;
+ bool single_stream_or_non_first_inactive = true;
+- for (size_t i = 1; i < encoder_config_.number_of_streams; ++i) {
++ for (size_t i = 1; i < encoder_config_.simulcast_layers.size(); ++i) {
+ if (encoder_config_.simulcast_layers[i].active) {
+ single_stream_or_non_first_inactive = false;
+ break;
diff --git a/third_party/libwebrtc/moz-patch-stack/0082.patch b/third_party/libwebrtc/moz-patch-stack/0082.patch
index d54ddf9d75..1d9ff301d6 100644
--- a/third_party/libwebrtc/moz-patch-stack/0082.patch
+++ b/third_party/libwebrtc/moz-patch-stack/0082.patch
@@ -1,24 +1,24 @@
-From: Michael Froman <mfroman@mozilla.com>
-Date: Wed, 5 Jul 2023 19:15:00 +0000
-Subject: Bug 1841864 - upstream commit 4baea5b07f should properly check size
- of encoder_config_.simulcast_layers. r=jib
+From: Mike Hommey <mh+mozilla@glandium.org>
+Date: Fri, 7 Jul 2023 00:58:00 +0000
+Subject: Bug 1841577 - Don't set WEBRTC_ENABLE_AVX2 on platforms that don't
+ have AVX2. r=mjf,webrtc-reviewers
-Differential Revision: https://phabricator.services.mozilla.com/D182813
-Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/a7179d8d75313b6c9c76a496e10d102da019ff4f
+Differential Revision: https://phabricator.services.mozilla.com/D182695
+Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/46fb51c90709be64c35946a8cf69195121441024
---
- video/video_stream_encoder.cc | 2 +-
+ webrtc.gni | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
-diff --git a/video/video_stream_encoder.cc b/video/video_stream_encoder.cc
-index 2e5a120eed..c5f086e155 100644
---- a/video/video_stream_encoder.cc
-+++ b/video/video_stream_encoder.cc
-@@ -1392,7 +1392,7 @@ void VideoStreamEncoder::ReconfigureEncoder() {
+diff --git a/webrtc.gni b/webrtc.gni
+index e339ba25fe..e1273475b9 100644
+--- a/webrtc.gni
++++ b/webrtc.gni
+@@ -310,7 +310,7 @@ declare_args() {
- bool is_svc = false;
- bool single_stream_or_non_first_inactive = true;
-- for (size_t i = 1; i < encoder_config_.number_of_streams; ++i) {
-+ for (size_t i = 1; i < encoder_config_.simulcast_layers.size(); ++i) {
- if (encoder_config_.simulcast_layers[i].active) {
- single_stream_or_non_first_inactive = false;
- break;
+ # Set this to true to enable the avx2 support in webrtc.
+ # TODO: Make sure that AVX2 works also for non-clang compilers.
+- if (is_clang == true) {
++ if (is_clang == true && (target_cpu == "x86" || target_cpu == "x64")) {
+ rtc_enable_avx2 = true
+ } else {
+ rtc_enable_avx2 = false
diff --git a/third_party/libwebrtc/moz-patch-stack/0083.patch b/third_party/libwebrtc/moz-patch-stack/0083.patch
index 6da77ca4ba..181d1439da 100644
--- a/third_party/libwebrtc/moz-patch-stack/0083.patch
+++ b/third_party/libwebrtc/moz-patch-stack/0083.patch
@@ -1,24 +1,26 @@
-From: Mike Hommey <mh+mozilla@glandium.org>
-Date: Fri, 7 Jul 2023 00:58:00 +0000
-Subject: Bug 1841577 - Don't set WEBRTC_ENABLE_AVX2 on platforms that don't
- have AVX2. r=mjf,webrtc-reviewers
+From: Byron Campen <docfaraday@gmail.com>
+Date: Thu, 20 Jul 2023 14:24:00 +0000
+Subject: Bug 1838080: Remove this duplicate init (that's also on the wrong
+ thread). r=pehrsons,webrtc-reviewers
-Differential Revision: https://phabricator.services.mozilla.com/D182695
-Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/46fb51c90709be64c35946a8cf69195121441024
+This was causing assertions.
+
+Differential Revision: https://phabricator.services.mozilla.com/D179731
+Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/6ac6592a04a839a6152d5ad5f0778f63dbbd6b1b
---
- webrtc.gni | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
+ audio/channel_send.cc | 2 --
+ 1 file changed, 2 deletions(-)
-diff --git a/webrtc.gni b/webrtc.gni
-index bacd158140..931a0a24e5 100644
---- a/webrtc.gni
-+++ b/webrtc.gni
-@@ -310,7 +310,7 @@ declare_args() {
+diff --git a/audio/channel_send.cc b/audio/channel_send.cc
+index 8080f4a3b8..61e68d19df 100644
+--- a/audio/channel_send.cc
++++ b/audio/channel_send.cc
+@@ -474,8 +474,6 @@ ChannelSend::ChannelSend(
+
+ int error = audio_coding_->RegisterTransportCallback(this);
+ RTC_DCHECK_EQ(0, error);
+- if (frame_transformer)
+- InitFrameTransformerDelegate(std::move(frame_transformer));
+ }
- # Set this to true to enable the avx2 support in webrtc.
- # TODO: Make sure that AVX2 works also for non-clang compilers.
-- if (is_clang == true) {
-+ if (is_clang == true && (target_cpu == "x86" || target_cpu == "x64")) {
- rtc_enable_avx2 = true
- } else {
- rtc_enable_avx2 = false
+ ChannelSend::~ChannelSend() {
diff --git a/third_party/libwebrtc/moz-patch-stack/0084.patch b/third_party/libwebrtc/moz-patch-stack/0084.patch
index c2888e89a3..8a565cc5d3 100644
--- a/third_party/libwebrtc/moz-patch-stack/0084.patch
+++ b/third_party/libwebrtc/moz-patch-stack/0084.patch
@@ -1,26 +1,39 @@
From: Byron Campen <docfaraday@gmail.com>
Date: Thu, 20 Jul 2023 14:24:00 +0000
-Subject: Bug 1838080: Remove this duplicate init (that's also on the wrong
- thread). r=pehrsons,webrtc-reviewers
+Subject: Bug 1838080: Work around a race in
+ ChannelSendFrameTransformerDelegate. r=pehrsons,webrtc-reviewers
-This was causing assertions.
+This variable can be null when a ChannelSendFrameTransformerDelegate is in use,
+because that does an async dispatch to the encoder queue in the handling for
+transformed frames. If this is unset while that dispatch is in flight, we
+nullptr crash.
-Differential Revision: https://phabricator.services.mozilla.com/D179731
-Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/6ac6592a04a839a6152d5ad5f0778f63dbbd6b1b
+Differential Revision: https://phabricator.services.mozilla.com/D180735
+Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/56555ecee7f36ae73abff1cbbd06807c2b65fc19
---
- audio/channel_send.cc | 2 --
- 1 file changed, 2 deletions(-)
+ audio/channel_send.cc | 8 ++++++--
+ 1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/audio/channel_send.cc b/audio/channel_send.cc
-index 777cc6d4c1..2b64569fdd 100644
+index 61e68d19df..3c59be52b4 100644
--- a/audio/channel_send.cc
+++ b/audio/channel_send.cc
-@@ -462,8 +462,6 @@ ChannelSend::ChannelSend(
+@@ -286,12 +286,16 @@ class RtpPacketSenderProxy : public RtpPacketSender {
+ void EnqueuePackets(
+ std::vector<std::unique_ptr<RtpPacketToSend>> packets) override {
+ MutexLock lock(&mutex_);
+- rtp_packet_pacer_->EnqueuePackets(std::move(packets));
++ if (rtp_packet_pacer_) {
++ rtp_packet_pacer_->EnqueuePackets(std::move(packets));
++ }
+ }
- int error = audio_coding_->RegisterTransportCallback(this);
- RTC_DCHECK_EQ(0, error);
-- if (frame_transformer)
-- InitFrameTransformerDelegate(std::move(frame_transformer));
- }
+ void RemovePacketsForSsrc(uint32_t ssrc) override {
+ MutexLock lock(&mutex_);
+- rtp_packet_pacer_->RemovePacketsForSsrc(ssrc);
++ if (rtp_packet_pacer_) {
++ rtp_packet_pacer_->RemovePacketsForSsrc(ssrc);
++ }
+ }
- ChannelSend::~ChannelSend() {
+ private:
diff --git a/third_party/libwebrtc/moz-patch-stack/0085.patch b/third_party/libwebrtc/moz-patch-stack/0085.patch
index fe0fd9a95c..62d24fdc20 100644
--- a/third_party/libwebrtc/moz-patch-stack/0085.patch
+++ b/third_party/libwebrtc/moz-patch-stack/0085.patch
@@ -1,39 +1,66 @@
From: Byron Campen <docfaraday@gmail.com>
Date: Thu, 20 Jul 2023 14:24:00 +0000
-Subject: Bug 1838080: Work around a race in
- ChannelSendFrameTransformerDelegate. r=pehrsons,webrtc-reviewers
+Subject: Bug 1838080: Use the current TaskQueue, instead of the current
+ thread, to init this. r=pehrsons,webrtc-reviewers
-This variable can be null when a ChannelSendFrameTransformerDelegate is in use,
-because that does an async dispatch to the encoder queue in the handling for
-transformed frames. If this is unset while that dispatch is in flight, we
-nullptr crash.
+There are situations where the current thread is not set, but the current
+TaskQueue is (but not vice versa).
-Differential Revision: https://phabricator.services.mozilla.com/D180735
-Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/56555ecee7f36ae73abff1cbbd06807c2b65fc19
+Differential Revision: https://phabricator.services.mozilla.com/D180736
+Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/62e71a2f745c4b98d5ee7ce9e6386aa1b657be9b
---
- audio/channel_send.cc | 8 ++++++--
- 1 file changed, 6 insertions(+), 2 deletions(-)
+ .../rtp_video_stream_receiver_frame_transformer_delegate.cc | 3 +--
+ .../rtp_video_stream_receiver_frame_transformer_delegate.h | 5 ++---
+ video/rtp_video_stream_receiver2.cc | 2 +-
+ 3 files changed, 4 insertions(+), 6 deletions(-)
-diff --git a/audio/channel_send.cc b/audio/channel_send.cc
-index 2b64569fdd..ee94760b6f 100644
---- a/audio/channel_send.cc
-+++ b/audio/channel_send.cc
-@@ -281,12 +281,16 @@ class RtpPacketSenderProxy : public RtpPacketSender {
- void EnqueuePackets(
- std::vector<std::unique_ptr<RtpPacketToSend>> packets) override {
- MutexLock lock(&mutex_);
-- rtp_packet_pacer_->EnqueuePackets(std::move(packets));
-+ if (rtp_packet_pacer_) {
-+ rtp_packet_pacer_->EnqueuePackets(std::move(packets));
-+ }
- }
+diff --git a/modules/rtp_rtcp/source/rtp_video_stream_receiver_frame_transformer_delegate.cc b/modules/rtp_rtcp/source/rtp_video_stream_receiver_frame_transformer_delegate.cc
+index fbd10c4c7b..ad3aa86c79 100644
+--- a/modules/rtp_rtcp/source/rtp_video_stream_receiver_frame_transformer_delegate.cc
++++ b/modules/rtp_rtcp/source/rtp_video_stream_receiver_frame_transformer_delegate.cc
+@@ -96,8 +96,7 @@ RtpVideoStreamReceiverFrameTransformerDelegate::
+ RtpVideoFrameReceiver* receiver,
+ Clock* clock,
+ rtc::scoped_refptr<FrameTransformerInterface> frame_transformer,
+- rtc::Thread* network_thread,
+- uint32_t ssrc)
++ TaskQueueBase* network_thread, uint32_t ssrc)
+ : receiver_(receiver),
+ frame_transformer_(std::move(frame_transformer)),
+ network_thread_(network_thread),
+diff --git a/modules/rtp_rtcp/source/rtp_video_stream_receiver_frame_transformer_delegate.h b/modules/rtp_rtcp/source/rtp_video_stream_receiver_frame_transformer_delegate.h
+index f08fc692dd..02f2e53923 100644
+--- a/modules/rtp_rtcp/source/rtp_video_stream_receiver_frame_transformer_delegate.h
++++ b/modules/rtp_rtcp/source/rtp_video_stream_receiver_frame_transformer_delegate.h
+@@ -41,8 +41,7 @@ class RtpVideoStreamReceiverFrameTransformerDelegate
+ RtpVideoFrameReceiver* receiver,
+ Clock* clock,
+ rtc::scoped_refptr<FrameTransformerInterface> frame_transformer,
+- rtc::Thread* network_thread,
+- uint32_t ssrc);
++ TaskQueueBase* network_thread, uint32_t ssrc);
- void RemovePacketsForSsrc(uint32_t ssrc) override {
- MutexLock lock(&mutex_);
-- rtp_packet_pacer_->RemovePacketsForSsrc(ssrc);
-+ if (rtp_packet_pacer_) {
-+ rtp_packet_pacer_->RemovePacketsForSsrc(ssrc);
-+ }
+ void Init();
+ void Reset();
+@@ -71,7 +70,7 @@ class RtpVideoStreamReceiverFrameTransformerDelegate
+ RtpVideoFrameReceiver* receiver_ RTC_GUARDED_BY(network_sequence_checker_);
+ rtc::scoped_refptr<FrameTransformerInterface> frame_transformer_
+ RTC_GUARDED_BY(network_sequence_checker_);
+- rtc::Thread* const network_thread_;
++ TaskQueueBase* const network_thread_;
+ const uint32_t ssrc_;
+ Clock* const clock_;
+ bool short_circuit_ RTC_GUARDED_BY(network_sequence_checker_) = false;
+diff --git a/video/rtp_video_stream_receiver2.cc b/video/rtp_video_stream_receiver2.cc
+index db0b87c736..c4a021d6c0 100644
+--- a/video/rtp_video_stream_receiver2.cc
++++ b/video/rtp_video_stream_receiver2.cc
+@@ -341,7 +341,7 @@ RtpVideoStreamReceiver2::RtpVideoStreamReceiver2(
+ if (frame_transformer) {
+ frame_transformer_delegate_ =
+ rtc::make_ref_counted<RtpVideoStreamReceiverFrameTransformerDelegate>(
+- this, clock_, std::move(frame_transformer), rtc::Thread::Current(),
++ this, clock_, std::move(frame_transformer), TaskQueueBase::Current(),
+ config_.rtp.remote_ssrc);
+ frame_transformer_delegate_->Init();
}
-
- private:
diff --git a/third_party/libwebrtc/moz-patch-stack/0086.patch b/third_party/libwebrtc/moz-patch-stack/0086.patch
index 10657e6049..cb6695d69d 100644
--- a/third_party/libwebrtc/moz-patch-stack/0086.patch
+++ b/third_party/libwebrtc/moz-patch-stack/0086.patch
@@ -1,66 +1,166 @@
-From: Byron Campen <docfaraday@gmail.com>
-Date: Thu, 20 Jul 2023 14:24:00 +0000
-Subject: Bug 1838080: Use the current TaskQueue, instead of the current
- thread, to init this. r=pehrsons,webrtc-reviewers
+From: Michael Froman <mjfroman@mac.com>
+Date: Thu, 27 Jul 2023 12:42:44 -0500
+Subject: Bug 1838080: Store the rid in TransformableVideoSenderFrame.
+ r=ng,webrtc-reviewers
-There are situations where the current thread is not set, but the current
-TaskQueue is (but not vice versa).
+This is necessary to reliably detect what rid a given keyframe is for, for the
+purposes of resolving promises from RTCRtpScriptTransformer.generateKeyFrame.
-Differential Revision: https://phabricator.services.mozilla.com/D180736
-Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/62e71a2f745c4b98d5ee7ce9e6386aa1b657be9b
+Differential Revision: https://phabricator.services.mozilla.com/D180737
+Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/2f1a0ba74bf71cfa0bc4e77714b8a5276a70cc36
---
- .../rtp_video_stream_receiver_frame_transformer_delegate.cc | 3 +--
- .../rtp_video_stream_receiver_frame_transformer_delegate.h | 5 ++---
- video/rtp_video_stream_receiver2.cc | 2 +-
- 3 files changed, 4 insertions(+), 6 deletions(-)
+ api/frame_transformer_interface.h | 1 +
+ modules/rtp_rtcp/source/rtp_sender.h | 4 ++++
+ modules/rtp_rtcp/source/rtp_sender_video.cc | 1 +
+ ...rtp_sender_video_frame_transformer_delegate.cc | 15 +++++++++++----
+ .../rtp_sender_video_frame_transformer_delegate.h | 2 ++
+ ..._stream_receiver_frame_transformer_delegate.cc | 5 +++++
+ 6 files changed, 24 insertions(+), 4 deletions(-)
-diff --git a/modules/rtp_rtcp/source/rtp_video_stream_receiver_frame_transformer_delegate.cc b/modules/rtp_rtcp/source/rtp_video_stream_receiver_frame_transformer_delegate.cc
-index ddbd22e34a..6f9aa6ae09 100644
---- a/modules/rtp_rtcp/source/rtp_video_stream_receiver_frame_transformer_delegate.cc
-+++ b/modules/rtp_rtcp/source/rtp_video_stream_receiver_frame_transformer_delegate.cc
-@@ -95,8 +95,7 @@ RtpVideoStreamReceiverFrameTransformerDelegate::
- RtpVideoFrameReceiver* receiver,
- Clock* clock,
- rtc::scoped_refptr<FrameTransformerInterface> frame_transformer,
-- rtc::Thread* network_thread,
-- uint32_t ssrc)
-+ TaskQueueBase* network_thread, uint32_t ssrc)
- : receiver_(receiver),
+diff --git a/api/frame_transformer_interface.h b/api/frame_transformer_interface.h
+index 5bbcffe28e..afa79b92ea 100644
+--- a/api/frame_transformer_interface.h
++++ b/api/frame_transformer_interface.h
+@@ -60,6 +60,7 @@ class TransformableVideoFrameInterface : public TransformableFrameInterface {
+ public:
+ virtual ~TransformableVideoFrameInterface() = default;
+ virtual bool IsKeyFrame() const = 0;
++ virtual const std::string& GetRid() const = 0;
+
+ virtual VideoFrameMetadata Metadata() const = 0;
+
+diff --git a/modules/rtp_rtcp/source/rtp_sender.h b/modules/rtp_rtcp/source/rtp_sender.h
+index a398f16d46..8136730e4c 100644
+--- a/modules/rtp_rtcp/source/rtp_sender.h
++++ b/modules/rtp_rtcp/source/rtp_sender.h
+@@ -140,6 +140,10 @@ class RTPSender {
+
+ uint32_t SSRC() const RTC_LOCKS_EXCLUDED(send_mutex_) { return ssrc_; }
+
++ const std::string& Rid() const RTC_LOCKS_EXCLUDED(send_mutex_) {
++ return rid_;
++ }
++
+ absl::optional<uint32_t> FlexfecSsrc() const RTC_LOCKS_EXCLUDED(send_mutex_) {
+ return flexfec_ssrc_;
+ }
+diff --git a/modules/rtp_rtcp/source/rtp_sender_video.cc b/modules/rtp_rtcp/source/rtp_sender_video.cc
+index ebca7aaa75..ede8fdc3d6 100644
+--- a/modules/rtp_rtcp/source/rtp_sender_video.cc
++++ b/modules/rtp_rtcp/source/rtp_sender_video.cc
+@@ -158,6 +158,7 @@ RTPSenderVideo::RTPSenderVideo(const Config& config)
+ this,
+ config.frame_transformer,
+ rtp_sender_->SSRC(),
++ rtp_sender_->Rid(),
+ config.task_queue_factory)
+ : nullptr) {
+ if (frame_transformer_delegate_)
+diff --git a/modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate.cc b/modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate.cc
+index 2bb71941f9..ff15840529 100644
+--- a/modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate.cc
++++ b/modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate.cc
+@@ -38,7 +38,8 @@ class TransformableVideoSenderFrame : public TransformableVideoFrameInterface {
+ uint32_t rtp_timestamp,
+ TimeDelta expected_retransmission_time,
+ uint32_t ssrc,
+- std::vector<uint32_t> csrcs)
++ std::vector<uint32_t> csrcs,
++ const std::string& rid)
+ : encoded_data_(encoded_image.GetEncodedData()),
+ pre_transform_payload_size_(encoded_image.size()),
+ header_(video_header),
+@@ -50,7 +51,8 @@ class TransformableVideoSenderFrame : public TransformableVideoFrameInterface {
+ capture_time_identifier_(encoded_image.CaptureTimeIdentifier()),
+ expected_retransmission_time_(expected_retransmission_time),
+ ssrc_(ssrc),
+- csrcs_(csrcs) {
++ csrcs_(csrcs),
++ rid_(rid) {
+ RTC_DCHECK_GE(payload_type_, 0);
+ RTC_DCHECK_LE(payload_type_, 127);
+ }
+@@ -113,6 +115,8 @@ class TransformableVideoSenderFrame : public TransformableVideoFrameInterface {
+ return mime_type + CodecTypeToPayloadString(*codec_type_);
+ }
+
++ const std::string& GetRid() const override { return rid_; }
++
+ private:
+ rtc::scoped_refptr<EncodedImageBufferInterface> encoded_data_;
+ const size_t pre_transform_payload_size_;
+@@ -127,6 +131,7 @@ class TransformableVideoSenderFrame : public TransformableVideoFrameInterface {
+
+ uint32_t ssrc_;
+ std::vector<uint32_t> csrcs_;
++ const std::string rid_;
+ };
+ } // namespace
+
+@@ -134,10 +139,12 @@ RTPSenderVideoFrameTransformerDelegate::RTPSenderVideoFrameTransformerDelegate(
+ RTPVideoFrameSenderInterface* sender,
+ rtc::scoped_refptr<FrameTransformerInterface> frame_transformer,
+ uint32_t ssrc,
++ const std::string& rid,
+ TaskQueueFactory* task_queue_factory)
+ : sender_(sender),
frame_transformer_(std::move(frame_transformer)),
- network_thread_(network_thread),
-diff --git a/modules/rtp_rtcp/source/rtp_video_stream_receiver_frame_transformer_delegate.h b/modules/rtp_rtcp/source/rtp_video_stream_receiver_frame_transformer_delegate.h
-index 62a42fdddf..20f9a5caa9 100644
---- a/modules/rtp_rtcp/source/rtp_video_stream_receiver_frame_transformer_delegate.h
-+++ b/modules/rtp_rtcp/source/rtp_video_stream_receiver_frame_transformer_delegate.h
-@@ -41,8 +41,7 @@ class RtpVideoStreamReceiverFrameTransformerDelegate
- RtpVideoFrameReceiver* receiver,
- Clock* clock,
+ ssrc_(ssrc),
++ rid_(rid),
+ transformation_queue_(task_queue_factory->CreateTaskQueue(
+ "video_frame_transformer",
+ TaskQueueFactory::Priority::NORMAL)) {}
+@@ -168,7 +175,7 @@ bool RTPSenderVideoFrameTransformerDelegate::TransformFrame(
+ frame_transformer_->Transform(std::make_unique<TransformableVideoSenderFrame>(
+ encoded_image, video_header, payload_type, codec_type, rtp_timestamp,
+ expected_retransmission_time, ssrc_,
+- /*csrcs=*/std::vector<uint32_t>()));
++ /*csrcs=*/std::vector<uint32_t>(), rid_));
+ return true;
+ }
+
+@@ -270,7 +277,7 @@ std::unique_ptr<TransformableVideoFrameInterface> CloneSenderVideoFrame(
+ return std::make_unique<TransformableVideoSenderFrame>(
+ encoded_image, new_header, original->GetPayloadType(), new_header.codec,
+ original->GetTimestamp(), kDefaultRetransmissionsTime,
+- original->GetSsrc(), metadata.GetCsrcs());
++ original->GetSsrc(), metadata.GetCsrcs(), original->GetRid());
+ }
+
+ } // namespace webrtc
+diff --git a/modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate.h b/modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate.h
+index 243f22ca9f..1f70a23ccc 100644
+--- a/modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate.h
++++ b/modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate.h
+@@ -58,6 +58,7 @@ class RTPSenderVideoFrameTransformerDelegate : public TransformedFrameCallback {
+ RTPVideoFrameSenderInterface* sender,
rtc::scoped_refptr<FrameTransformerInterface> frame_transformer,
-- rtc::Thread* network_thread,
-- uint32_t ssrc);
-+ TaskQueueBase* network_thread, uint32_t ssrc);
+ uint32_t ssrc,
++ const std::string& rid,
+ TaskQueueFactory* send_transport_queue);
void Init();
- void Reset();
-@@ -67,7 +66,7 @@ class RtpVideoStreamReceiverFrameTransformerDelegate
- RtpVideoFrameReceiver* receiver_ RTC_GUARDED_BY(network_sequence_checker_);
- rtc::scoped_refptr<FrameTransformerInterface> frame_transformer_
- RTC_GUARDED_BY(network_sequence_checker_);
-- rtc::Thread* const network_thread_;
-+ TaskQueueBase* const network_thread_;
+@@ -106,6 +107,7 @@ class RTPSenderVideoFrameTransformerDelegate : public TransformedFrameCallback {
+ RTPVideoFrameSenderInterface* sender_ RTC_GUARDED_BY(sender_lock_);
+ rtc::scoped_refptr<FrameTransformerInterface> frame_transformer_;
const uint32_t ssrc_;
- Clock* const clock_;
- };
-diff --git a/video/rtp_video_stream_receiver2.cc b/video/rtp_video_stream_receiver2.cc
-index db0b87c736..c4a021d6c0 100644
---- a/video/rtp_video_stream_receiver2.cc
-+++ b/video/rtp_video_stream_receiver2.cc
-@@ -341,7 +341,7 @@ RtpVideoStreamReceiver2::RtpVideoStreamReceiver2(
- if (frame_transformer) {
- frame_transformer_delegate_ =
- rtc::make_ref_counted<RtpVideoStreamReceiverFrameTransformerDelegate>(
-- this, clock_, std::move(frame_transformer), rtc::Thread::Current(),
-+ this, clock_, std::move(frame_transformer), TaskQueueBase::Current(),
- config_.rtp.remote_ssrc);
- frame_transformer_delegate_->Init();
++ const std::string rid_;
+ // Used when the encoded frames arrives without a current task queue. This can
+ // happen if a hardware encoder was used.
+ std::unique_ptr<TaskQueueBase, TaskQueueDeleter> transformation_queue_;
+diff --git a/modules/rtp_rtcp/source/rtp_video_stream_receiver_frame_transformer_delegate.cc b/modules/rtp_rtcp/source/rtp_video_stream_receiver_frame_transformer_delegate.cc
+index ad3aa86c79..7af945c623 100644
+--- a/modules/rtp_rtcp/source/rtp_video_stream_receiver_frame_transformer_delegate.cc
++++ b/modules/rtp_rtcp/source/rtp_video_stream_receiver_frame_transformer_delegate.cc
+@@ -58,6 +58,11 @@ class TransformableVideoReceiverFrame
+ return frame_->FrameType() == VideoFrameType::kVideoFrameKey;
}
+
++ const std::string& GetRid() const override {
++ static const std::string empty;
++ return empty;
++ }
++
+ VideoFrameMetadata Metadata() const override { return metadata_; }
+
+ void SetMetadata(const VideoFrameMetadata& metadata) override {
diff --git a/third_party/libwebrtc/moz-patch-stack/0087.patch b/third_party/libwebrtc/moz-patch-stack/0087.patch
index 241fb871b6..d6aca49a9b 100644
--- a/third_party/libwebrtc/moz-patch-stack/0087.patch
+++ b/third_party/libwebrtc/moz-patch-stack/0087.patch
@@ -1,166 +1,38 @@
-From: Michael Froman <mjfroman@mac.com>
-Date: Thu, 27 Jul 2023 12:42:44 -0500
-Subject: Bug 1838080: Store the rid in TransformableVideoSenderFrame.
- r=ng,webrtc-reviewers
+From: Byron Campen <docfaraday@gmail.com>
+Date: Thu, 20 Jul 2023 14:24:00 +0000
+Subject: Bug 1838080: Ensure that last ref to transformation_queue_ is not
+ released on itself. r=pehrsons,webrtc-reviewers
-This is necessary to reliably detect what rid a given keyframe is for, for the
-purposes of resolving promises from RTCRtpScriptTransformer.generateKeyFrame.
-
-Differential Revision: https://phabricator.services.mozilla.com/D180737
-Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/2f1a0ba74bf71cfa0bc4e77714b8a5276a70cc36
+Differential Revision: https://phabricator.services.mozilla.com/D181699
+Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/91d9e8b6a5c430a73561ffd2330865f04fcb1a6d
---
- api/frame_transformer_interface.h | 1 +
- modules/rtp_rtcp/source/rtp_sender.h | 4 ++++
- modules/rtp_rtcp/source/rtp_sender_video.cc | 1 +
- ...rtp_sender_video_frame_transformer_delegate.cc | 15 +++++++++++----
- .../rtp_sender_video_frame_transformer_delegate.h | 2 ++
- ..._stream_receiver_frame_transformer_delegate.cc | 5 +++++
- 6 files changed, 24 insertions(+), 4 deletions(-)
+ .../rtp_sender_video_frame_transformer_delegate.cc | 9 +++++++++
+ 1 file changed, 9 insertions(+)
-diff --git a/api/frame_transformer_interface.h b/api/frame_transformer_interface.h
-index 9024988db6..d1ea15a54e 100644
---- a/api/frame_transformer_interface.h
-+++ b/api/frame_transformer_interface.h
-@@ -65,6 +65,7 @@ class TransformableVideoFrameInterface : public TransformableFrameInterface {
- public:
- virtual ~TransformableVideoFrameInterface() = default;
- virtual bool IsKeyFrame() const = 0;
-+ virtual const std::string& GetRid() const = 0;
-
- virtual VideoFrameMetadata Metadata() const = 0;
-
-diff --git a/modules/rtp_rtcp/source/rtp_sender.h b/modules/rtp_rtcp/source/rtp_sender.h
-index a398f16d46..8136730e4c 100644
---- a/modules/rtp_rtcp/source/rtp_sender.h
-+++ b/modules/rtp_rtcp/source/rtp_sender.h
-@@ -140,6 +140,10 @@ class RTPSender {
-
- uint32_t SSRC() const RTC_LOCKS_EXCLUDED(send_mutex_) { return ssrc_; }
-
-+ const std::string& Rid() const RTC_LOCKS_EXCLUDED(send_mutex_) {
-+ return rid_;
-+ }
-+
- absl::optional<uint32_t> FlexfecSsrc() const RTC_LOCKS_EXCLUDED(send_mutex_) {
- return flexfec_ssrc_;
- }
-diff --git a/modules/rtp_rtcp/source/rtp_sender_video.cc b/modules/rtp_rtcp/source/rtp_sender_video.cc
-index ebca7aaa75..ede8fdc3d6 100644
---- a/modules/rtp_rtcp/source/rtp_sender_video.cc
-+++ b/modules/rtp_rtcp/source/rtp_sender_video.cc
-@@ -158,6 +158,7 @@ RTPSenderVideo::RTPSenderVideo(const Config& config)
- this,
- config.frame_transformer,
- rtp_sender_->SSRC(),
-+ rtp_sender_->Rid(),
- config.task_queue_factory)
- : nullptr) {
- if (frame_transformer_delegate_)
diff --git a/modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate.cc b/modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate.cc
-index d255ef4aa9..f24c91ae35 100644
+index ff15840529..ae9eb6b4bd 100644
--- a/modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate.cc
+++ b/modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate.cc
-@@ -31,7 +31,8 @@ class TransformableVideoSenderFrame : public TransformableVideoFrameInterface {
- uint32_t rtp_timestamp,
- TimeDelta expected_retransmission_time,
- uint32_t ssrc,
-- std::vector<uint32_t> csrcs)
-+ std::vector<uint32_t> csrcs,
-+ const std::string& rid)
- : encoded_data_(encoded_image.GetEncodedData()),
- pre_transform_payload_size_(encoded_image.size()),
- header_(video_header),
-@@ -43,7 +44,8 @@ class TransformableVideoSenderFrame : public TransformableVideoFrameInterface {
- capture_time_identifier_(encoded_image.CaptureTimeIdentifier()),
- expected_retransmission_time_(expected_retransmission_time),
- ssrc_(ssrc),
-- csrcs_(csrcs) {
-+ csrcs_(csrcs),
-+ rid_(rid) {
- RTC_DCHECK_GE(payload_type_, 0);
- RTC_DCHECK_LE(payload_type_, 127);
+@@ -18,6 +18,7 @@
+ #include "api/task_queue/task_queue_factory.h"
+ #include "modules/rtp_rtcp/source/rtp_descriptor_authentication.h"
+ #include "rtc_base/checks.h"
++#include "rtc_base/event.h"
+ #include "rtc_base/logging.h"
+
+ namespace webrtc {
+@@ -257,6 +258,14 @@ void RTPSenderVideoFrameTransformerDelegate::Reset() {
+ MutexLock lock(&sender_lock_);
+ sender_ = nullptr;
}
-@@ -106,6 +108,8 @@ class TransformableVideoSenderFrame : public TransformableVideoFrameInterface {
- return mime_type + CodecTypeToPayloadString(*codec_type_);
- }
-
-+ const std::string& GetRid() const override { return rid_; }
-+
- private:
- rtc::scoped_refptr<EncodedImageBufferInterface> encoded_data_;
- const size_t pre_transform_payload_size_;
-@@ -120,6 +124,7 @@ class TransformableVideoSenderFrame : public TransformableVideoFrameInterface {
-
- uint32_t ssrc_;
- std::vector<uint32_t> csrcs_;
-+ const std::string rid_;
- };
- } // namespace
-
-@@ -127,10 +132,12 @@ RTPSenderVideoFrameTransformerDelegate::RTPSenderVideoFrameTransformerDelegate(
- RTPVideoFrameSenderInterface* sender,
- rtc::scoped_refptr<FrameTransformerInterface> frame_transformer,
- uint32_t ssrc,
-+ const std::string& rid,
- TaskQueueFactory* task_queue_factory)
- : sender_(sender),
- frame_transformer_(std::move(frame_transformer)),
- ssrc_(ssrc),
-+ rid_(rid),
- transformation_queue_(task_queue_factory->CreateTaskQueue(
- "video_frame_transformer",
- TaskQueueFactory::Priority::NORMAL)) {}
-@@ -150,7 +157,7 @@ bool RTPSenderVideoFrameTransformerDelegate::TransformFrame(
- frame_transformer_->Transform(std::make_unique<TransformableVideoSenderFrame>(
- encoded_image, video_header, payload_type, codec_type, rtp_timestamp,
- expected_retransmission_time, ssrc_,
-- /*csrcs=*/std::vector<uint32_t>()));
-+ /*csrcs=*/std::vector<uint32_t>(), rid_));
- return true;
- }
-
-@@ -244,7 +251,7 @@ std::unique_ptr<TransformableVideoFrameInterface> CloneSenderVideoFrame(
- encoded_image, new_header, original->GetPayloadType(), new_header.codec,
- original->GetTimestamp(),
- /*expected_retransmission_time=*/TimeDelta::PlusInfinity(),
-- original->GetSsrc(), metadata.GetCsrcs());
-+ original->GetSsrc(), metadata.GetCsrcs(), original->GetRid());
++ // Wait until all pending tasks are executed, to ensure that the last ref
++ // standing is not on the transformation queue.
++ rtc::Event flush;
++ transformation_queue_->PostTask([this, &flush]() {
++ RTC_DCHECK_RUN_ON(transformation_queue_.get());
++ flush.Set();
++ });
++ flush.Wait(rtc::Event::kForever);
}
- } // namespace webrtc
-diff --git a/modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate.h b/modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate.h
-index a333db235a..3379ead364 100644
---- a/modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate.h
-+++ b/modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate.h
-@@ -58,6 +58,7 @@ class RTPSenderVideoFrameTransformerDelegate : public TransformedFrameCallback {
- RTPVideoFrameSenderInterface* sender,
- rtc::scoped_refptr<FrameTransformerInterface> frame_transformer,
- uint32_t ssrc,
-+ const std::string& rid,
- TaskQueueFactory* send_transport_queue);
-
- void Init();
-@@ -104,6 +105,7 @@ class RTPSenderVideoFrameTransformerDelegate : public TransformedFrameCallback {
- RTPVideoFrameSenderInterface* sender_ RTC_GUARDED_BY(sender_lock_);
- rtc::scoped_refptr<FrameTransformerInterface> frame_transformer_;
- const uint32_t ssrc_;
-+ const std::string rid_;
- // Used when the encoded frames arrives without a current task queue. This can
- // happen if a hardware encoder was used.
- std::unique_ptr<TaskQueueBase, TaskQueueDeleter> transformation_queue_;
-diff --git a/modules/rtp_rtcp/source/rtp_video_stream_receiver_frame_transformer_delegate.cc b/modules/rtp_rtcp/source/rtp_video_stream_receiver_frame_transformer_delegate.cc
-index 6f9aa6ae09..94c9249e16 100644
---- a/modules/rtp_rtcp/source/rtp_video_stream_receiver_frame_transformer_delegate.cc
-+++ b/modules/rtp_rtcp/source/rtp_video_stream_receiver_frame_transformer_delegate.cc
-@@ -57,6 +57,11 @@ class TransformableVideoReceiverFrame
- return frame_->FrameType() == VideoFrameType::kVideoFrameKey;
- }
-
-+ const std::string& GetRid() const override {
-+ static const std::string empty;
-+ return empty;
-+ }
-+
- VideoFrameMetadata Metadata() const override { return metadata_; }
-
- void SetMetadata(const VideoFrameMetadata& metadata) override {
+ std::unique_ptr<TransformableVideoFrameInterface> CloneSenderVideoFrame(
diff --git a/third_party/libwebrtc/moz-patch-stack/0088.patch b/third_party/libwebrtc/moz-patch-stack/0088.patch
index d969596186..1a2541f93f 100644
--- a/third_party/libwebrtc/moz-patch-stack/0088.patch
+++ b/third_party/libwebrtc/moz-patch-stack/0088.patch
@@ -1,38 +1,38 @@
-From: Byron Campen <docfaraday@gmail.com>
-Date: Thu, 20 Jul 2023 14:24:00 +0000
-Subject: Bug 1838080: Ensure that last ref to transformation_queue_ is not
- released on itself. r=pehrsons,webrtc-reviewers
+From: stransky <stransky@redhat.com>
+Date: Tue, 29 Aug 2023 12:43:00 +0000
+Subject: Bug 1821629 [DMABuf] Don't use DMABuf if it's disabled by Firefox gfx
+ config r=ng,webrtc-reviewers
-Differential Revision: https://phabricator.services.mozilla.com/D181699
-Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/91d9e8b6a5c430a73561ffd2330865f04fcb1a6d
+Differential Revision: https://phabricator.services.mozilla.com/D172224
+Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/158a888cad8869a2f0026fa7cfaaa13ecbfcf2ed
---
- .../rtp_sender_video_frame_transformer_delegate.cc | 9 +++++++++
- 1 file changed, 9 insertions(+)
+ .../linux/wayland/shared_screencast_stream.cc | 9 ++++++++-
+ 1 file changed, 8 insertions(+), 1 deletion(-)
-diff --git a/modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate.cc b/modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate.cc
-index f24c91ae35..9d7c58d19a 100644
---- a/modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate.cc
-+++ b/modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate.cc
-@@ -18,6 +18,7 @@
- #include "api/task_queue/task_queue_factory.h"
- #include "modules/rtp_rtcp/source/rtp_descriptor_authentication.h"
- #include "rtc_base/checks.h"
-+#include "rtc_base/event.h"
+diff --git a/modules/desktop_capture/linux/wayland/shared_screencast_stream.cc b/modules/desktop_capture/linux/wayland/shared_screencast_stream.cc
+index 0c26e7a7d5..22aa355e44 100644
+--- a/modules/desktop_capture/linux/wayland/shared_screencast_stream.cc
++++ b/modules/desktop_capture/linux/wayland/shared_screencast_stream.cc
+@@ -28,6 +28,13 @@
+ #include "rtc_base/synchronization/mutex.h"
+ #include "rtc_base/time_utils.h"
++// Wrapper for gfxVars::UseDMABuf() as we can't include gfxVars here.
++// We don't want to use dmabuf of known broken systems.
++// See FEATURE_DMABUF for details.
++namespace mozilla::gfx {
++bool IsDMABufEnabled();
++}
++
namespace webrtc {
- namespace {
-@@ -232,6 +233,14 @@ void RTPSenderVideoFrameTransformerDelegate::Reset() {
- MutexLock lock(&sender_lock_);
- sender_ = nullptr;
- }
-+ // Wait until all pending tasks are executed, to ensure that the last ref
-+ // standing is not on the transformation queue.
-+ rtc::Event flush;
-+ transformation_queue_->PostTask([this, &flush]() {
-+ RTC_DCHECK_RUN_ON(transformation_queue_.get());
-+ flush.Set();
-+ });
-+ flush.Wait(rtc::Event::kForever);
- }
- std::unique_ptr<TransformableVideoFrameInterface> CloneSenderVideoFrame(
+ const int kBytesPerPixel = 4;
+@@ -294,7 +301,7 @@ void SharedScreenCastStreamPrivate::OnStreamParamChanged(
+ that->modifier_ =
+ has_modifier ? that->spa_video_format_.modifier : DRM_FORMAT_MOD_INVALID;
+ std::vector<const spa_pod*> params;
+- const int buffer_types = has_modifier
++ const int buffer_types = has_modifier && mozilla::gfx::IsDMABufEnabled()
+ ? (1 << SPA_DATA_DmaBuf) | (1 << SPA_DATA_MemFd)
+ : (1 << SPA_DATA_MemFd);
+
diff --git a/third_party/libwebrtc/moz-patch-stack/0089.patch b/third_party/libwebrtc/moz-patch-stack/0089.patch
index 1a2541f93f..dfd2faa87d 100644
--- a/third_party/libwebrtc/moz-patch-stack/0089.patch
+++ b/third_party/libwebrtc/moz-patch-stack/0089.patch
@@ -1,38 +1,47 @@
From: stransky <stransky@redhat.com>
Date: Tue, 29 Aug 2023 12:43:00 +0000
-Subject: Bug 1821629 [DMABuf] Don't use DMABuf if it's disabled by Firefox gfx
- config r=ng,webrtc-reviewers
+Subject: Bug 1821629 [Pipewire/DMABuf] Don't create dmabuf backend if it's
+ disabled r=ng,webrtc-reviewers
-Differential Revision: https://phabricator.services.mozilla.com/D172224
-Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/158a888cad8869a2f0026fa7cfaaa13ecbfcf2ed
+Depends on D172224
+
+Differential Revision: https://phabricator.services.mozilla.com/D172229
+Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/237d19fe96dd7d25b6a817415ee4e6854678d648
---
- .../linux/wayland/shared_screencast_stream.cc | 9 ++++++++-
- 1 file changed, 8 insertions(+), 1 deletion(-)
+ .../linux/wayland/shared_screencast_stream.cc | 9 ++++++---
+ 1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/modules/desktop_capture/linux/wayland/shared_screencast_stream.cc b/modules/desktop_capture/linux/wayland/shared_screencast_stream.cc
-index 0c26e7a7d5..22aa355e44 100644
+index 22aa355e44..61c6957d27 100644
--- a/modules/desktop_capture/linux/wayland/shared_screencast_stream.cc
+++ b/modules/desktop_capture/linux/wayland/shared_screencast_stream.cc
-@@ -28,6 +28,13 @@
- #include "rtc_base/synchronization/mutex.h"
- #include "rtc_base/time_utils.h"
+@@ -419,7 +419,9 @@ bool SharedScreenCastStreamPrivate::StartScreenCastStream(
+ RTC_LOG(LS_ERROR) << "Unable to open PipeWire library";
+ return false;
+ }
+- egl_dmabuf_ = std::make_unique<EglDmaBuf>();
++ if (mozilla::gfx::IsDMABufEnabled()) {
++ egl_dmabuf_ = std::make_unique<EglDmaBuf>();
++ }
+
+ pw_stream_node_id_ = stream_node_id;
+
+@@ -508,7 +510,8 @@ bool SharedScreenCastStreamPrivate::StartScreenCastStream(
+ for (uint32_t format : {SPA_VIDEO_FORMAT_BGRA, SPA_VIDEO_FORMAT_RGBA,
+ SPA_VIDEO_FORMAT_BGRx, SPA_VIDEO_FORMAT_RGBx}) {
+ // Modifiers can be used with PipeWire >= 0.3.33
+- if (has_required_pw_client_version && has_required_pw_server_version) {
++ if (egl_dmabuf_ &&
++ has_required_pw_client_version && has_required_pw_server_version) {
+ modifiers_ = egl_dmabuf_->QueryDmaBufModifiers(format);
+
+ if (!modifiers_.empty()) {
+@@ -927,7 +930,7 @@ bool SharedScreenCastStreamPrivate::ProcessDMABuffer(
-+// Wrapper for gfxVars::UseDMABuf() as we can't include gfxVars here.
-+// We don't want to use dmabuf of known broken systems.
-+// See FEATURE_DMABUF for details.
-+namespace mozilla::gfx {
-+bool IsDMABufEnabled();
-+}
-+
- namespace webrtc {
+ const uint n_planes = spa_buffer->n_datas;
- const int kBytesPerPixel = 4;
-@@ -294,7 +301,7 @@ void SharedScreenCastStreamPrivate::OnStreamParamChanged(
- that->modifier_ =
- has_modifier ? that->spa_video_format_.modifier : DRM_FORMAT_MOD_INVALID;
- std::vector<const spa_pod*> params;
-- const int buffer_types = has_modifier
-+ const int buffer_types = has_modifier && mozilla::gfx::IsDMABufEnabled()
- ? (1 << SPA_DATA_DmaBuf) | (1 << SPA_DATA_MemFd)
- : (1 << SPA_DATA_MemFd);
+- if (!n_planes) {
++ if (!n_planes || !egl_dmabuf_) {
+ return false;
+ }
diff --git a/third_party/libwebrtc/moz-patch-stack/0090.patch b/third_party/libwebrtc/moz-patch-stack/0090.patch
index dfd2faa87d..b38361e516 100644
--- a/third_party/libwebrtc/moz-patch-stack/0090.patch
+++ b/third_party/libwebrtc/moz-patch-stack/0090.patch
@@ -1,47 +1,56 @@
-From: stransky <stransky@redhat.com>
-Date: Tue, 29 Aug 2023 12:43:00 +0000
-Subject: Bug 1821629 [Pipewire/DMABuf] Don't create dmabuf backend if it's
- disabled r=ng,webrtc-reviewers
+From: Michael Froman <mfroman@mozilla.com>
+Date: Thu, 28 Sep 2023 14:12:00 +0000
+Subject: Bug 1832465 - remove libXtst usage from libwebrtc.
+ r=ng,webrtc-reviewers
-Depends on D172224
-
-Differential Revision: https://phabricator.services.mozilla.com/D172229
-Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/237d19fe96dd7d25b6a817415ee4e6854678d648
+Differential Revision: https://phabricator.services.mozilla.com/D189386
+Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/0ec1b33b95dbb2d39355f28b2812fe25b4ad9f20
---
- .../linux/wayland/shared_screencast_stream.cc | 9 ++++++---
- 1 file changed, 6 insertions(+), 3 deletions(-)
+ modules/desktop_capture/BUILD.gn | 3 +++
+ modules/desktop_capture/linux/x11/shared_x_display.cc | 4 ++++
+ 2 files changed, 7 insertions(+)
-diff --git a/modules/desktop_capture/linux/wayland/shared_screencast_stream.cc b/modules/desktop_capture/linux/wayland/shared_screencast_stream.cc
-index 22aa355e44..61c6957d27 100644
---- a/modules/desktop_capture/linux/wayland/shared_screencast_stream.cc
-+++ b/modules/desktop_capture/linux/wayland/shared_screencast_stream.cc
-@@ -419,7 +419,9 @@ bool SharedScreenCastStreamPrivate::StartScreenCastStream(
- RTC_LOG(LS_ERROR) << "Unable to open PipeWire library";
- return false;
+diff --git a/modules/desktop_capture/BUILD.gn b/modules/desktop_capture/BUILD.gn
+index dcc1b3c6ac..0634e0e9c4 100644
+--- a/modules/desktop_capture/BUILD.gn
++++ b/modules/desktop_capture/BUILD.gn
+@@ -384,6 +384,9 @@ rtc_library("desktop_capture") {
+ "Xrandr",
+ "Xtst",
+ ]
++ if (build_with_mozilla) {
++ libs -= [ "Xtst" ]
++ }
}
-- egl_dmabuf_ = std::make_unique<EglDmaBuf>();
-+ if (mozilla::gfx::IsDMABufEnabled()) {
-+ egl_dmabuf_ = std::make_unique<EglDmaBuf>();
-+ }
- pw_stream_node_id_ = stream_node_id;
+ if (!is_win && !is_mac && !rtc_use_x11_extensions && !rtc_use_pipewire &&
+diff --git a/modules/desktop_capture/linux/x11/shared_x_display.cc b/modules/desktop_capture/linux/x11/shared_x_display.cc
+index d690b0e2ba..3f3617b074 100644
+--- a/modules/desktop_capture/linux/x11/shared_x_display.cc
++++ b/modules/desktop_capture/linux/x11/shared_x_display.cc
+@@ -11,7 +11,9 @@
+ #include "modules/desktop_capture/linux/x11/shared_x_display.h"
-@@ -508,7 +510,8 @@ bool SharedScreenCastStreamPrivate::StartScreenCastStream(
- for (uint32_t format : {SPA_VIDEO_FORMAT_BGRA, SPA_VIDEO_FORMAT_RGBA,
- SPA_VIDEO_FORMAT_BGRx, SPA_VIDEO_FORMAT_RGBx}) {
- // Modifiers can be used with PipeWire >= 0.3.33
-- if (has_required_pw_client_version && has_required_pw_server_version) {
-+ if (egl_dmabuf_ &&
-+ has_required_pw_client_version && has_required_pw_server_version) {
- modifiers_ = egl_dmabuf_->QueryDmaBufModifiers(format);
+ #include <X11/Xlib.h>
++#if !defined(WEBRTC_MOZILLA_BUILD)
+ #include <X11/extensions/XTest.h>
++#endif
- if (!modifiers_.empty()) {
-@@ -927,7 +930,7 @@ bool SharedScreenCastStreamPrivate::ProcessDMABuffer(
+ #include <algorithm>
- const uint n_planes = spa_buffer->n_datas;
+@@ -95,6 +97,7 @@ void SharedXDisplay::ProcessPendingXEvents() {
+ }
-- if (!n_planes) {
-+ if (!n_planes || !egl_dmabuf_) {
- return false;
+ void SharedXDisplay::IgnoreXServerGrabs() {
++#if !defined(WEBRTC_MOZILLA_BUILD)
+ int test_event_base = 0;
+ int test_error_base = 0;
+ int major = 0;
+@@ -103,6 +106,7 @@ void SharedXDisplay::IgnoreXServerGrabs() {
+ &minor)) {
+ XTestGrabControl(display(), true);
}
++#endif
+ }
+ } // namespace webrtc
diff --git a/third_party/libwebrtc/moz-patch-stack/0091.patch b/third_party/libwebrtc/moz-patch-stack/0091.patch
index b38361e516..51b0c620b0 100644
--- a/third_party/libwebrtc/moz-patch-stack/0091.patch
+++ b/third_party/libwebrtc/moz-patch-stack/0091.patch
@@ -1,56 +1,91 @@
-From: Michael Froman <mfroman@mozilla.com>
-Date: Thu, 28 Sep 2023 14:12:00 +0000
-Subject: Bug 1832465 - remove libXtst usage from libwebrtc.
- r=ng,webrtc-reviewers
+From: Dan Baker <dbaker@mozilla.com>
+Date: Mon, 2 Oct 2023 17:17:00 +0000
+Subject: Bug 1851693 - (fix-279a05475d) Revert addition of race checker which
+ is causing tsan failurs until better fix is landed upstream.r=pehrsons
-Differential Revision: https://phabricator.services.mozilla.com/D189386
-Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/0ec1b33b95dbb2d39355f28b2812fe25b4ad9f20
+This merely reverses a race checker that is too strict for our use, maintaining the code we had before, so we can fix upstream in the meantime tracked with Bug 1856392.
+
+Differential Revision: https://phabricator.services.mozilla.com/D189396
+Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/e626ce7279e6575e68d0e43de3dfd3ed59b00a75
---
- modules/desktop_capture/BUILD.gn | 3 +++
- modules/desktop_capture/linux/x11/shared_x_display.cc | 4 ++++
- 2 files changed, 7 insertions(+)
+ modules/video_capture/linux/video_capture_v4l2.cc | 7 -------
+ modules/video_capture/linux/video_capture_v4l2.h | 7 +++----
+ 2 files changed, 3 insertions(+), 11 deletions(-)
-diff --git a/modules/desktop_capture/BUILD.gn b/modules/desktop_capture/BUILD.gn
-index dcc1b3c6ac..0634e0e9c4 100644
---- a/modules/desktop_capture/BUILD.gn
-+++ b/modules/desktop_capture/BUILD.gn
-@@ -384,6 +384,9 @@ rtc_library("desktop_capture") {
- "Xrandr",
- "Xtst",
- ]
-+ if (build_with_mozilla) {
-+ libs -= [ "Xtst" ]
-+ }
+diff --git a/modules/video_capture/linux/video_capture_v4l2.cc b/modules/video_capture/linux/video_capture_v4l2.cc
+index 97cd9a70eb..08d23f7f58 100644
+--- a/modules/video_capture/linux/video_capture_v4l2.cc
++++ b/modules/video_capture/linux/video_capture_v4l2.cc
+@@ -110,7 +110,6 @@ int32_t VideoCaptureModuleV4L2::Init(const char* deviceUniqueIdUTF8) {
+
+ VideoCaptureModuleV4L2::~VideoCaptureModuleV4L2() {
+ RTC_DCHECK_RUN_ON(&api_checker_);
+- RTC_CHECK_RUNS_SERIALIZED(&capture_checker_);
+
+ StopCapture();
+ if (_deviceFd != -1)
+@@ -120,7 +119,6 @@ VideoCaptureModuleV4L2::~VideoCaptureModuleV4L2() {
+ int32_t VideoCaptureModuleV4L2::StartCapture(
+ const VideoCaptureCapability& capability) {
+ RTC_DCHECK_RUN_ON(&api_checker_);
+- RTC_CHECK_RUNS_SERIALIZED(&capture_checker_);
+
+ if (_captureStarted) {
+ if (capability == _requestedCapability) {
+@@ -318,7 +316,6 @@ int32_t VideoCaptureModuleV4L2::StopCapture() {
+ _captureThread.Finalize();
}
- if (!is_win && !is_mac && !rtc_use_x11_extensions && !rtc_use_pipewire &&
-diff --git a/modules/desktop_capture/linux/x11/shared_x_display.cc b/modules/desktop_capture/linux/x11/shared_x_display.cc
-index d690b0e2ba..3f3617b074 100644
---- a/modules/desktop_capture/linux/x11/shared_x_display.cc
-+++ b/modules/desktop_capture/linux/x11/shared_x_display.cc
-@@ -11,7 +11,9 @@
- #include "modules/desktop_capture/linux/x11/shared_x_display.h"
+- RTC_CHECK_RUNS_SERIALIZED(&capture_checker_);
+ MutexLock lock(&capture_lock_);
+ if (_captureStarted) {
+ _captureStarted = false;
+@@ -336,7 +333,6 @@ int32_t VideoCaptureModuleV4L2::StopCapture() {
+ // critical section protected by the caller
- #include <X11/Xlib.h>
-+#if !defined(WEBRTC_MOZILLA_BUILD)
- #include <X11/extensions/XTest.h>
-+#endif
+ bool VideoCaptureModuleV4L2::AllocateVideoBuffers() {
+- RTC_CHECK_RUNS_SERIALIZED(&capture_checker_);
+ struct v4l2_requestbuffers rbuffer;
+ memset(&rbuffer, 0, sizeof(v4l2_requestbuffers));
- #include <algorithm>
+@@ -387,7 +383,6 @@ bool VideoCaptureModuleV4L2::AllocateVideoBuffers() {
+ }
-@@ -95,6 +97,7 @@ void SharedXDisplay::ProcessPendingXEvents() {
+ bool VideoCaptureModuleV4L2::DeAllocateVideoBuffers() {
+- RTC_CHECK_RUNS_SERIALIZED(&capture_checker_);
+ // unmap buffers
+ for (int i = 0; i < _buffersAllocatedByDevice; i++)
+ munmap(_pool[i].start, _pool[i].length);
+@@ -405,12 +400,10 @@ bool VideoCaptureModuleV4L2::DeAllocateVideoBuffers() {
}
- void SharedXDisplay::IgnoreXServerGrabs() {
-+#if !defined(WEBRTC_MOZILLA_BUILD)
- int test_event_base = 0;
- int test_error_base = 0;
- int major = 0;
-@@ -103,6 +106,7 @@ void SharedXDisplay::IgnoreXServerGrabs() {
- &minor)) {
- XTestGrabControl(display(), true);
- }
-+#endif
+ bool VideoCaptureModuleV4L2::CaptureStarted() {
+- RTC_CHECK_RUNS_SERIALIZED(&capture_checker_);
+ return _captureStarted;
}
- } // namespace webrtc
+ bool VideoCaptureModuleV4L2::CaptureProcess() {
+- RTC_CHECK_RUNS_SERIALIZED(&capture_checker_);
+
+ int retVal = 0;
+ struct pollfd rSet;
+diff --git a/modules/video_capture/linux/video_capture_v4l2.h b/modules/video_capture/linux/video_capture_v4l2.h
+index 0191e41876..61358d0325 100644
+--- a/modules/video_capture/linux/video_capture_v4l2.h
++++ b/modules/video_capture/linux/video_capture_v4l2.h
+@@ -45,12 +45,11 @@ class VideoCaptureModuleV4L2 : public VideoCaptureImpl {
+ Mutex capture_lock_ RTC_ACQUIRED_BEFORE(api_lock_);
+ bool quit_ RTC_GUARDED_BY(capture_lock_);
+ int32_t _deviceId RTC_GUARDED_BY(api_checker_);
+- int32_t _deviceFd RTC_GUARDED_BY(capture_checker_);
++ int32_t _deviceFd;
+
+ int32_t _buffersAllocatedByDevice RTC_GUARDED_BY(capture_lock_);
+- VideoCaptureCapability configured_capability_
+- RTC_GUARDED_BY(capture_checker_);
+- bool _captureStarted RTC_GUARDED_BY(capture_checker_);
++ VideoCaptureCapability configured_capability_;
++ bool _captureStarted;
+ struct Buffer {
+ void* start;
+ size_t length;
diff --git a/third_party/libwebrtc/moz-patch-stack/0092.patch b/third_party/libwebrtc/moz-patch-stack/0092.patch
index 6160d6eecb..3fcbcd0f94 100644
--- a/third_party/libwebrtc/moz-patch-stack/0092.patch
+++ b/third_party/libwebrtc/moz-patch-stack/0092.patch
@@ -1,91 +1,30 @@
-From: Dan Baker <dbaker@mozilla.com>
-Date: Mon, 2 Oct 2023 17:17:00 +0000
-Subject: Bug 1851693 - (fix-279a05475d) Revert addition of race checker which
- is causing tsan failurs until better fix is landed upstream.r=pehrsons
+From: Michael Froman <mfroman@mozilla.com>
+Date: Thu, 5 Oct 2023 14:21:00 +0000
+Subject: Bug 1857037 - pt1 - add shim gni files to limit BUILD.gn changes.
+ r=ng,webrtc-reviewers
-This merely reverses a race checker that is too strict for our use, maintaining the code we had before, so we can fix upstream in the meantime tracked with Bug 1856392.
-
-Differential Revision: https://phabricator.services.mozilla.com/D189396
-Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/e626ce7279e6575e68d0e43de3dfd3ed59b00a75
+Differential Revision: https://phabricator.services.mozilla.com/D190104
+Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/a84d39db037cbe34aa19588b0d18335eb5e2d79b
---
- modules/video_capture/linux/video_capture_v4l2.cc | 7 -------
- modules/video_capture/linux/video_capture_v4l2.h | 7 +++----
- 2 files changed, 3 insertions(+), 11 deletions(-)
+ testing/libfuzzer/fuzzer_test.gni | 2 ++
+ testing/test.gni | 2 ++
+ 2 files changed, 4 insertions(+)
+ create mode 100644 testing/libfuzzer/fuzzer_test.gni
+ create mode 100644 testing/test.gni
-diff --git a/modules/video_capture/linux/video_capture_v4l2.cc b/modules/video_capture/linux/video_capture_v4l2.cc
-index d6813b13fd..c887683dc8 100644
---- a/modules/video_capture/linux/video_capture_v4l2.cc
-+++ b/modules/video_capture/linux/video_capture_v4l2.cc
-@@ -110,7 +110,6 @@ int32_t VideoCaptureModuleV4L2::Init(const char* deviceUniqueIdUTF8) {
-
- VideoCaptureModuleV4L2::~VideoCaptureModuleV4L2() {
- RTC_DCHECK_RUN_ON(&api_checker_);
-- RTC_CHECK_RUNS_SERIALIZED(&capture_checker_);
-
- StopCapture();
- if (_deviceFd != -1)
-@@ -120,7 +119,6 @@ VideoCaptureModuleV4L2::~VideoCaptureModuleV4L2() {
- int32_t VideoCaptureModuleV4L2::StartCapture(
- const VideoCaptureCapability& capability) {
- RTC_DCHECK_RUN_ON(&api_checker_);
-- RTC_CHECK_RUNS_SERIALIZED(&capture_checker_);
-
- if (_captureStarted) {
- if (capability == _requestedCapability) {
-@@ -318,7 +316,6 @@ int32_t VideoCaptureModuleV4L2::StopCapture() {
- _captureThread.Finalize();
- }
-
-- RTC_CHECK_RUNS_SERIALIZED(&capture_checker_);
- MutexLock lock(&capture_lock_);
- if (_captureStarted) {
- _captureStarted = false;
-@@ -336,7 +333,6 @@ int32_t VideoCaptureModuleV4L2::StopCapture() {
- // critical section protected by the caller
-
- bool VideoCaptureModuleV4L2::AllocateVideoBuffers() {
-- RTC_CHECK_RUNS_SERIALIZED(&capture_checker_);
- struct v4l2_requestbuffers rbuffer;
- memset(&rbuffer, 0, sizeof(v4l2_requestbuffers));
-
-@@ -387,7 +383,6 @@ bool VideoCaptureModuleV4L2::AllocateVideoBuffers() {
- }
-
- bool VideoCaptureModuleV4L2::DeAllocateVideoBuffers() {
-- RTC_CHECK_RUNS_SERIALIZED(&capture_checker_);
- // unmap buffers
- for (int i = 0; i < _buffersAllocatedByDevice; i++)
- munmap(_pool[i].start, _pool[i].length);
-@@ -405,12 +400,10 @@ bool VideoCaptureModuleV4L2::DeAllocateVideoBuffers() {
- }
-
- bool VideoCaptureModuleV4L2::CaptureStarted() {
-- RTC_CHECK_RUNS_SERIALIZED(&capture_checker_);
- return _captureStarted;
- }
-
- bool VideoCaptureModuleV4L2::CaptureProcess() {
-- RTC_CHECK_RUNS_SERIALIZED(&capture_checker_);
-
- int retVal = 0;
- struct pollfd rSet;
-diff --git a/modules/video_capture/linux/video_capture_v4l2.h b/modules/video_capture/linux/video_capture_v4l2.h
-index 0191e41876..61358d0325 100644
---- a/modules/video_capture/linux/video_capture_v4l2.h
-+++ b/modules/video_capture/linux/video_capture_v4l2.h
-@@ -45,12 +45,11 @@ class VideoCaptureModuleV4L2 : public VideoCaptureImpl {
- Mutex capture_lock_ RTC_ACQUIRED_BEFORE(api_lock_);
- bool quit_ RTC_GUARDED_BY(capture_lock_);
- int32_t _deviceId RTC_GUARDED_BY(api_checker_);
-- int32_t _deviceFd RTC_GUARDED_BY(capture_checker_);
-+ int32_t _deviceFd;
-
- int32_t _buffersAllocatedByDevice RTC_GUARDED_BY(capture_lock_);
-- VideoCaptureCapability configured_capability_
-- RTC_GUARDED_BY(capture_checker_);
-- bool _captureStarted RTC_GUARDED_BY(capture_checker_);
-+ VideoCaptureCapability configured_capability_;
-+ bool _captureStarted;
- struct Buffer {
- void* start;
- size_t length;
+diff --git a/testing/libfuzzer/fuzzer_test.gni b/testing/libfuzzer/fuzzer_test.gni
+new file mode 100644
+index 0000000000..8fdf3cdad2
+--- /dev/null
++++ b/testing/libfuzzer/fuzzer_test.gni
+@@ -0,0 +1,2 @@
++# "empty" file in place of importing new testing/libfuzzer
++# to allow BUILD.gn imports to succeed.
+diff --git a/testing/test.gni b/testing/test.gni
+new file mode 100644
+index 0000000000..f46fa82778
+--- /dev/null
++++ b/testing/test.gni
+@@ -0,0 +1,2 @@
++# "empty" file in place of importing new testing/test.gni
++# to allow BUILD.gn imports to succeed.
diff --git a/third_party/libwebrtc/moz-patch-stack/0093.patch b/third_party/libwebrtc/moz-patch-stack/0093.patch
index 3fcbcd0f94..b0cdd2d540 100644
--- a/third_party/libwebrtc/moz-patch-stack/0093.patch
+++ b/third_party/libwebrtc/moz-patch-stack/0093.patch
@@ -1,30 +1,40 @@
From: Michael Froman <mfroman@mozilla.com>
-Date: Thu, 5 Oct 2023 14:21:00 +0000
-Subject: Bug 1857037 - pt1 - add shim gni files to limit BUILD.gn changes.
- r=ng,webrtc-reviewers
+Date: Tue, 14 Feb 2023 03:27:00 +0000
+Subject: Bug 1816173 - pt12 - add shim config for
+ third_party/libwebrtc/testing/{gmock|gtest} r=ng
-Differential Revision: https://phabricator.services.mozilla.com/D190104
-Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/a84d39db037cbe34aa19588b0d18335eb5e2d79b
+We don't vendor third_party/libwebrtc/third_party/gmock
+ third_party/libwebrtc/third_party/gtest, so:
+- add BUILD.gn to avoid scattered BUILD.gn changes
+
+Differential Revision: https://phabricator.services.mozilla.com/D169674
+Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/4ea9d2db79c42a144235e45c47c71adf1dd01fdc
---
- testing/libfuzzer/fuzzer_test.gni | 2 ++
- testing/test.gni | 2 ++
- 2 files changed, 4 insertions(+)
- create mode 100644 testing/libfuzzer/fuzzer_test.gni
- create mode 100644 testing/test.gni
+ testing/gmock/BUILD.gn | 5 +++++
+ testing/gtest/BUILD.gn | 5 +++++
+ 2 files changed, 10 insertions(+)
+ create mode 100644 testing/gmock/BUILD.gn
+ create mode 100644 testing/gtest/BUILD.gn
-diff --git a/testing/libfuzzer/fuzzer_test.gni b/testing/libfuzzer/fuzzer_test.gni
+diff --git a/testing/gmock/BUILD.gn b/testing/gmock/BUILD.gn
new file mode 100644
-index 0000000000..8fdf3cdad2
+index 0000000000..a2a1efdea9
--- /dev/null
-+++ b/testing/libfuzzer/fuzzer_test.gni
-@@ -0,0 +1,2 @@
-+# "empty" file in place of importing new testing/libfuzzer
-+# to allow BUILD.gn imports to succeed.
-diff --git a/testing/test.gni b/testing/test.gni
++++ b/testing/gmock/BUILD.gn
+@@ -0,0 +1,5 @@
++import("//third_party/libaom/options.gni")
++import("../../webrtc.gni")
++
++rtc_library("gmock") {
++}
+diff --git a/testing/gtest/BUILD.gn b/testing/gtest/BUILD.gn
new file mode 100644
-index 0000000000..f46fa82778
+index 0000000000..c9c2703c37
--- /dev/null
-+++ b/testing/test.gni
-@@ -0,0 +1,2 @@
-+# "empty" file in place of importing new testing/test.gni
-+# to allow BUILD.gn imports to succeed.
++++ b/testing/gtest/BUILD.gn
+@@ -0,0 +1,5 @@
++import("//third_party/libaom/options.gni")
++import("../../webrtc.gni")
++
++rtc_library("gtest") {
++}
diff --git a/third_party/libwebrtc/moz-patch-stack/0094.patch b/third_party/libwebrtc/moz-patch-stack/0094.patch
index b0cdd2d540..36e7778d1a 100644
--- a/third_party/libwebrtc/moz-patch-stack/0094.patch
+++ b/third_party/libwebrtc/moz-patch-stack/0094.patch
@@ -1,40 +1,28 @@
-From: Michael Froman <mfroman@mozilla.com>
-Date: Tue, 14 Feb 2023 03:27:00 +0000
-Subject: Bug 1816173 - pt12 - add shim config for
- third_party/libwebrtc/testing/{gmock|gtest} r=ng
+From: Andreas Pehrson <apehrson@mozilla.com>
+Date: Wed, 18 Oct 2023 17:25:00 +0000
+Subject: Bug 1857862 - (fix-32a8169a65) Don't call non-constexpr
+ RTC_CHECK_NOTREACHED from constexpr VideoFrameTypeToString under gcc-8.
+ r=webrtc-reviewers,mjf
-We don't vendor third_party/libwebrtc/third_party/gmock
- third_party/libwebrtc/third_party/gtest, so:
-- add BUILD.gn to avoid scattered BUILD.gn changes
-
-Differential Revision: https://phabricator.services.mozilla.com/D169674
-Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/4ea9d2db79c42a144235e45c47c71adf1dd01fdc
+Differential Revision: https://phabricator.services.mozilla.com/D191308
+Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/8a4449ba24fa3192b44863ed8ba96f6f94a6e88d
---
- testing/gmock/BUILD.gn | 5 +++++
- testing/gtest/BUILD.gn | 5 +++++
- 2 files changed, 10 insertions(+)
- create mode 100644 testing/gmock/BUILD.gn
- create mode 100644 testing/gtest/BUILD.gn
+ api/video/video_frame_type.h | 4 ++++
+ 1 file changed, 4 insertions(+)
-diff --git a/testing/gmock/BUILD.gn b/testing/gmock/BUILD.gn
-new file mode 100644
-index 0000000000..a2a1efdea9
---- /dev/null
-+++ b/testing/gmock/BUILD.gn
-@@ -0,0 +1,5 @@
-+import("//third_party/libaom/options.gni")
-+import("../../webrtc.gni")
-+
-+rtc_library("gmock") {
-+}
-diff --git a/testing/gtest/BUILD.gn b/testing/gtest/BUILD.gn
-new file mode 100644
-index 0000000000..c9c2703c37
---- /dev/null
-+++ b/testing/gtest/BUILD.gn
-@@ -0,0 +1,5 @@
-+import("//third_party/libaom/options.gni")
-+import("../../webrtc.gni")
-+
-+rtc_library("gtest") {
-+}
+diff --git a/api/video/video_frame_type.h b/api/video/video_frame_type.h
+index 9079829ff8..3665a80cd8 100644
+--- a/api/video/video_frame_type.h
++++ b/api/video/video_frame_type.h
+@@ -34,7 +34,11 @@ inline constexpr absl::string_view VideoFrameTypeToString(
+ case VideoFrameType::kVideoFrameDelta:
+ return "delta";
+ }
++// Mozilla:
++// gcc-8 complains about a constexpr function calling a non-constexpr ditto.
++#if defined(__clang__) || (defined(__GNUC__) && __GNUC__ >= 9)
+ RTC_CHECK_NOTREACHED();
++#endif
+ return "";
+ }
+
diff --git a/third_party/libwebrtc/moz-patch-stack/0095.patch b/third_party/libwebrtc/moz-patch-stack/0095.patch
index 36e7778d1a..3b8608fa76 100644
--- a/third_party/libwebrtc/moz-patch-stack/0095.patch
+++ b/third_party/libwebrtc/moz-patch-stack/0095.patch
@@ -1,28 +1,41 @@
From: Andreas Pehrson <apehrson@mozilla.com>
-Date: Wed, 18 Oct 2023 17:25:00 +0000
-Subject: Bug 1857862 - (fix-32a8169a65) Don't call non-constexpr
- RTC_CHECK_NOTREACHED from constexpr VideoFrameTypeToString under gcc-8.
- r=webrtc-reviewers,mjf
+Date: Wed, 18 Oct 2023 17:21:00 +0000
+Subject: Bug 1859786 - Fix lock annotation warning in Mozilla-specific edit on
+ top of video_capture_impl.cc. r=webrtc-reviewers,mjf
-Differential Revision: https://phabricator.services.mozilla.com/D191308
-Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/8a4449ba24fa3192b44863ed8ba96f6f94a6e88d
+The annotations were added in M116:
+https://hg.mozilla.org/mozilla-central/rev/9cd372df013948ad822ae936752d725d77474fb5
+
+Note that this was never unsafe, since _dataCallbacks is only written on the
+same thread that we are patching here. This patch however, adds helpful static
+analysis.
+
+Differential Revision: https://phabricator.services.mozilla.com/D191301
+Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/56ff441b644400f09d2d0453dbd8991ea25db7b1
---
- api/video/video_frame_type.h | 4 ++++
- 1 file changed, 4 insertions(+)
+ modules/video_capture/video_capture_impl.cc | 11 +++++++----
+ 1 file changed, 7 insertions(+), 4 deletions(-)
-diff --git a/api/video/video_frame_type.h b/api/video/video_frame_type.h
-index 9079829ff8..3665a80cd8 100644
---- a/api/video/video_frame_type.h
-+++ b/api/video/video_frame_type.h
-@@ -34,7 +34,11 @@ inline constexpr absl::string_view VideoFrameTypeToString(
- case VideoFrameType::kVideoFrameDelta:
- return "delta";
+diff --git a/modules/video_capture/video_capture_impl.cc b/modules/video_capture/video_capture_impl.cc
+index 02404697ad..1bddaf824d 100644
+--- a/modules/video_capture/video_capture_impl.cc
++++ b/modules/video_capture/video_capture_impl.cc
+@@ -119,11 +119,14 @@ void VideoCaptureImpl::DeRegisterCaptureDataCallback(
+ }
+
+ int32_t VideoCaptureImpl::StopCaptureIfAllClientsClose() {
+- if (_dataCallBacks.empty()) {
+- return StopCapture();
+- } else {
+- return 0;
++ RTC_DCHECK_RUN_ON(&api_checker_);
++ {
++ MutexLock lock(&api_lock_);
++ if (!_dataCallBacks.empty()) {
++ return 0;
++ }
}
-+// Mozilla:
-+// gcc-8 complains about a constexpr function calling a non-constexpr ditto.
-+#if defined(__clang__) || (defined(__GNUC__) && __GNUC__ >= 9)
- RTC_CHECK_NOTREACHED();
-+#endif
- return "";
++ return StopCapture();
}
+ int32_t VideoCaptureImpl::DeliverCapturedFrame(VideoFrame& captureFrame) {
diff --git a/third_party/libwebrtc/moz-patch-stack/0096.patch b/third_party/libwebrtc/moz-patch-stack/0096.patch
index 3b8608fa76..983834acba 100644
--- a/third_party/libwebrtc/moz-patch-stack/0096.patch
+++ b/third_party/libwebrtc/moz-patch-stack/0096.patch
@@ -1,41 +1,27 @@
From: Andreas Pehrson <apehrson@mozilla.com>
Date: Wed, 18 Oct 2023 17:21:00 +0000
-Subject: Bug 1859786 - Fix lock annotation warning in Mozilla-specific edit on
- top of video_capture_impl.cc. r=webrtc-reviewers,mjf
+Subject: Bug 1859786 - Fix clang-tidy warning in video_capture_impl.cc.
+ r=webrtc-reviewers,mjf
-The annotations were added in M116:
-https://hg.mozilla.org/mozilla-central/rev/9cd372df013948ad822ae936752d725d77474fb5
+clang-tidy says:
+ 'auto dataCallBack' can be declared as 'auto *dataCallBack'
-Note that this was never unsafe, since _dataCallbacks is only written on the
-same thread that we are patching here. This patch however, adds helpful static
-analysis.
-
-Differential Revision: https://phabricator.services.mozilla.com/D191301
-Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/56ff441b644400f09d2d0453dbd8991ea25db7b1
+Differential Revision: https://phabricator.services.mozilla.com/D191302
+Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/26c84d214137a1b0de0902c7038756964e5786f4
---
- modules/video_capture/video_capture_impl.cc | 11 +++++++----
- 1 file changed, 7 insertions(+), 4 deletions(-)
+ modules/video_capture/video_capture_impl.cc | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/modules/video_capture/video_capture_impl.cc b/modules/video_capture/video_capture_impl.cc
-index 02404697ad..1bddaf824d 100644
+index 1bddaf824d..15dfb7fe1f 100644
--- a/modules/video_capture/video_capture_impl.cc
+++ b/modules/video_capture/video_capture_impl.cc
-@@ -119,11 +119,14 @@ void VideoCaptureImpl::DeRegisterCaptureDataCallback(
- }
+@@ -134,7 +134,7 @@ int32_t VideoCaptureImpl::DeliverCapturedFrame(VideoFrame& captureFrame) {
+
+ UpdateFrameCount(); // frame count used for local frame rate callback.
- int32_t VideoCaptureImpl::StopCaptureIfAllClientsClose() {
-- if (_dataCallBacks.empty()) {
-- return StopCapture();
-- } else {
-- return 0;
-+ RTC_DCHECK_RUN_ON(&api_checker_);
-+ {
-+ MutexLock lock(&api_lock_);
-+ if (!_dataCallBacks.empty()) {
-+ return 0;
-+ }
+- for (auto dataCallBack : _dataCallBacks) {
++ for (auto* dataCallBack : _dataCallBacks) {
+ dataCallBack->OnFrame(captureFrame);
}
-+ return StopCapture();
- }
- int32_t VideoCaptureImpl::DeliverCapturedFrame(VideoFrame& captureFrame) {
diff --git a/third_party/libwebrtc/moz-patch-stack/0097.patch b/third_party/libwebrtc/moz-patch-stack/0097.patch
index 983834acba..56c8dca72b 100644
--- a/third_party/libwebrtc/moz-patch-stack/0097.patch
+++ b/third_party/libwebrtc/moz-patch-stack/0097.patch
@@ -1,27 +1,2186 @@
-From: Andreas Pehrson <apehrson@mozilla.com>
-Date: Wed, 18 Oct 2023 17:21:00 +0000
-Subject: Bug 1859786 - Fix clang-tidy warning in video_capture_impl.cc.
- r=webrtc-reviewers,mjf
+From: Dan Minor <dminor@mozilla.com>
+Date: Thu, 24 Sep 2020 18:28:00 +0000
+Subject: Bug 1665166 - Move media/webrtc/trunk/* to third-party/libwebrtc;
+ r=ng
-clang-tidy says:
- 'auto dataCallBack' can be declared as 'auto *dataCallBack'
-
-Differential Revision: https://phabricator.services.mozilla.com/D191302
-Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/26c84d214137a1b0de0902c7038756964e5786f4
+Differential Revision: https://phabricator.services.mozilla.com/D91317
+Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/57e3c54bd7b9a0203e19ff1df272d24bb551ed29
---
- modules/video_capture/video_capture_impl.cc | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
+ tools/clang/OWNERS | 2 +
+ tools/clang/plugins/ChromeClassTester.cpp | 294 ++++++++++++
+ tools/clang/plugins/ChromeClassTester.h | 84 ++++
+ tools/clang/plugins/FindBadConstructs.cpp | 435 ++++++++++++++++++
+ tools/clang/plugins/Makefile | 19 +
+ tools/clang/plugins/OWNERS | 1 +
+ tools/clang/plugins/README.chromium | 4 +
+ tools/clang/plugins/tests/base_refcounted.cpp | 72 +++
+ tools/clang/plugins/tests/base_refcounted.h | 121 +++++
+ tools/clang/plugins/tests/base_refcounted.txt | 23 +
+ .../clang/plugins/tests/inline_copy_ctor.cpp | 5 +
+ tools/clang/plugins/tests/inline_copy_ctor.h | 12 +
+ .../clang/plugins/tests/inline_copy_ctor.txt | 5 +
+ tools/clang/plugins/tests/inline_ctor.cpp | 25 +
+ tools/clang/plugins/tests/inline_ctor.h | 21 +
+ tools/clang/plugins/tests/inline_ctor.txt | 8 +
+ tools/clang/plugins/tests/missing_ctor.cpp | 23 +
+ tools/clang/plugins/tests/missing_ctor.h | 19 +
+ tools/clang/plugins/tests/missing_ctor.txt | 6 +
+ .../tests/nested_class_inline_ctor.cpp | 5 +
+ .../plugins/tests/nested_class_inline_ctor.h | 22 +
+ .../tests/nested_class_inline_ctor.txt | 8 +
+ .../plugins/tests/overridden_methods.cpp | 38 ++
+ .../clang/plugins/tests/overridden_methods.h | 54 +++
+ .../plugins/tests/overridden_methods.txt | 20 +
+ tools/clang/plugins/tests/test.sh | 72 +++
+ tools/clang/plugins/tests/virtual_methods.cpp | 36 ++
+ tools/clang/plugins/tests/virtual_methods.h | 39 ++
+ tools/clang/plugins/tests/virtual_methods.txt | 8 +
+ tools/clang/scripts/package.sh | 87 ++++
+ tools/clang/scripts/plugin_flags.sh | 24 +
+ tools/clang/scripts/update.py | 34 ++
+ tools/clang/scripts/update.sh | 286 ++++++++++++
+ 33 files changed, 1912 insertions(+)
+ create mode 100644 tools/clang/OWNERS
+ create mode 100644 tools/clang/plugins/ChromeClassTester.cpp
+ create mode 100644 tools/clang/plugins/ChromeClassTester.h
+ create mode 100644 tools/clang/plugins/FindBadConstructs.cpp
+ create mode 100644 tools/clang/plugins/Makefile
+ create mode 100644 tools/clang/plugins/OWNERS
+ create mode 100644 tools/clang/plugins/README.chromium
+ create mode 100644 tools/clang/plugins/tests/base_refcounted.cpp
+ create mode 100644 tools/clang/plugins/tests/base_refcounted.h
+ create mode 100644 tools/clang/plugins/tests/base_refcounted.txt
+ create mode 100644 tools/clang/plugins/tests/inline_copy_ctor.cpp
+ create mode 100644 tools/clang/plugins/tests/inline_copy_ctor.h
+ create mode 100644 tools/clang/plugins/tests/inline_copy_ctor.txt
+ create mode 100644 tools/clang/plugins/tests/inline_ctor.cpp
+ create mode 100644 tools/clang/plugins/tests/inline_ctor.h
+ create mode 100644 tools/clang/plugins/tests/inline_ctor.txt
+ create mode 100644 tools/clang/plugins/tests/missing_ctor.cpp
+ create mode 100644 tools/clang/plugins/tests/missing_ctor.h
+ create mode 100644 tools/clang/plugins/tests/missing_ctor.txt
+ create mode 100644 tools/clang/plugins/tests/nested_class_inline_ctor.cpp
+ create mode 100644 tools/clang/plugins/tests/nested_class_inline_ctor.h
+ create mode 100644 tools/clang/plugins/tests/nested_class_inline_ctor.txt
+ create mode 100644 tools/clang/plugins/tests/overridden_methods.cpp
+ create mode 100644 tools/clang/plugins/tests/overridden_methods.h
+ create mode 100644 tools/clang/plugins/tests/overridden_methods.txt
+ create mode 100755 tools/clang/plugins/tests/test.sh
+ create mode 100644 tools/clang/plugins/tests/virtual_methods.cpp
+ create mode 100644 tools/clang/plugins/tests/virtual_methods.h
+ create mode 100644 tools/clang/plugins/tests/virtual_methods.txt
+ create mode 100755 tools/clang/scripts/package.sh
+ create mode 100755 tools/clang/scripts/plugin_flags.sh
+ create mode 100755 tools/clang/scripts/update.py
+ create mode 100755 tools/clang/scripts/update.sh
-diff --git a/modules/video_capture/video_capture_impl.cc b/modules/video_capture/video_capture_impl.cc
-index 1bddaf824d..15dfb7fe1f 100644
---- a/modules/video_capture/video_capture_impl.cc
-+++ b/modules/video_capture/video_capture_impl.cc
-@@ -134,7 +134,7 @@ int32_t VideoCaptureImpl::DeliverCapturedFrame(VideoFrame& captureFrame) {
-
- UpdateFrameCount(); // frame count used for local frame rate callback.
-
-- for (auto dataCallBack : _dataCallBacks) {
-+ for (auto* dataCallBack : _dataCallBacks) {
- dataCallBack->OnFrame(captureFrame);
- }
-
+diff --git a/tools/clang/OWNERS b/tools/clang/OWNERS
+new file mode 100644
+index 0000000000..d86ef9424a
+--- /dev/null
++++ b/tools/clang/OWNERS
+@@ -0,0 +1,2 @@
++hans@chromium.org
++thakis@chromium.org
+diff --git a/tools/clang/plugins/ChromeClassTester.cpp b/tools/clang/plugins/ChromeClassTester.cpp
+new file mode 100644
+index 0000000000..055866c5c5
+--- /dev/null
++++ b/tools/clang/plugins/ChromeClassTester.cpp
+@@ -0,0 +1,294 @@
++// Copyright (c) 2012 The Chromium Authors. All rights reserved.
++// Use of this source code is governed by a BSD-style license that can be
++// found in the LICENSE file.
++
++// A general interface for filtering and only acting on classes in Chromium C++
++// code.
++
++#include "ChromeClassTester.h"
++
++#include <sys/param.h>
++
++#include "clang/AST/AST.h"
++#include "clang/Basic/FileManager.h"
++#include "clang/Basic/SourceManager.h"
++
++using namespace clang;
++
++namespace {
++
++bool starts_with(const std::string& one, const std::string& two) {
++ return one.compare(0, two.size(), two) == 0;
++}
++
++std::string lstrip(const std::string& one, const std::string& two) {
++ if (starts_with(one, two))
++ return one.substr(two.size());
++ return one;
++}
++
++bool ends_with(const std::string& one, const std::string& two) {
++ if (two.size() > one.size())
++ return false;
++
++ return one.compare(one.size() - two.size(), two.size(), two) == 0;
++}
++
++} // namespace
++
++ChromeClassTester::ChromeClassTester(CompilerInstance& instance)
++ : instance_(instance),
++ diagnostic_(instance.getDiagnostics()) {
++ BuildBannedLists();
++}
++
++ChromeClassTester::~ChromeClassTester() {}
++
++void ChromeClassTester::HandleTagDeclDefinition(TagDecl* tag) {
++ pending_class_decls_.push_back(tag);
++}
++
++bool ChromeClassTester::HandleTopLevelDecl(DeclGroupRef group_ref) {
++ for (size_t i = 0; i < pending_class_decls_.size(); ++i)
++ CheckTag(pending_class_decls_[i]);
++ pending_class_decls_.clear();
++
++ return true; // true means continue parsing.
++}
++
++void ChromeClassTester::CheckTag(TagDecl* tag) {
++ // We handle class types here where we have semantic information. We can only
++ // check structs/classes/enums here, but we get a bunch of nice semantic
++ // information instead of just parsing information.
++
++ if (CXXRecordDecl* record = dyn_cast<CXXRecordDecl>(tag)) {
++ // If this is a POD or a class template or a type dependent on a
++ // templated class, assume there's no ctor/dtor/virtual method
++ // optimization that we can do.
++ if (record->isPOD() ||
++ record->getDescribedClassTemplate() ||
++ record->getTemplateSpecializationKind() ||
++ record->isDependentType())
++ return;
++
++ if (InBannedNamespace(record))
++ return;
++
++ SourceLocation record_location = record->getInnerLocStart();
++ if (InBannedDirectory(record_location))
++ return;
++
++ // We sadly need to maintain a blacklist of types that violate these
++ // rules, but do so for good reason or due to limitations of this
++ // checker (i.e., we don't handle extern templates very well).
++ std::string base_name = record->getNameAsString();
++ if (IsIgnoredType(base_name))
++ return;
++
++ // We ignore all classes that end with "Matcher" because they're probably
++ // GMock artifacts.
++ if (ends_with(base_name, "Matcher"))
++ return;
++
++ CheckChromeClass(record_location, record);
++ }
++}
++
++void ChromeClassTester::emitWarning(SourceLocation loc,
++ const char* raw_error) {
++ FullSourceLoc full(loc, instance().getSourceManager());
++ std::string err;
++ err = "[chromium-style] ";
++ err += raw_error;
++ DiagnosticsEngine::Level level =
++ diagnostic().getWarningsAsErrors() ?
++ DiagnosticsEngine::Error :
++ DiagnosticsEngine::Warning;
++ unsigned id = diagnostic().getCustomDiagID(level, err);
++ DiagnosticBuilder builder = diagnostic().Report(full, id);
++}
++
++bool ChromeClassTester::InBannedNamespace(const Decl* record) {
++ std::string n = GetNamespace(record);
++ if (!n.empty()) {
++ return std::find(banned_namespaces_.begin(), banned_namespaces_.end(), n)
++ != banned_namespaces_.end();
++ }
++
++ return false;
++}
++
++std::string ChromeClassTester::GetNamespace(const Decl* record) {
++ return GetNamespaceImpl(record->getDeclContext(), "");
++}
++
++bool ChromeClassTester::InImplementationFile(SourceLocation record_location) {
++ std::string filename;
++ if (!GetFilename(record_location, &filename))
++ return false;
++
++ if (ends_with(filename, ".cc") || ends_with(filename, ".cpp") ||
++ ends_with(filename, ".mm")) {
++ return true;
++ }
++
++ return false;
++}
++
++void ChromeClassTester::BuildBannedLists() {
++ banned_namespaces_.push_back("std");
++ banned_namespaces_.push_back("__gnu_cxx");
++ banned_namespaces_.push_back("WebKit");
++
++ banned_directories_.push_back("third_party/");
++ banned_directories_.push_back("native_client/");
++ banned_directories_.push_back("breakpad/");
++ banned_directories_.push_back("courgette/");
++ banned_directories_.push_back("pdf/");
++ banned_directories_.push_back("ppapi/");
++ banned_directories_.push_back("usr/");
++ banned_directories_.push_back("testing/");
++ banned_directories_.push_back("googleurl/");
++ banned_directories_.push_back("v8/");
++ banned_directories_.push_back("dart/");
++ banned_directories_.push_back("sdch/");
++ banned_directories_.push_back("icu4c/");
++ banned_directories_.push_back("frameworks/");
++
++ // Don't check autogenerated headers.
++ // Make puts them below $(builddir_name)/.../gen and geni.
++ // Ninja puts them below OUTPUT_DIR/.../gen
++ // Xcode has a fixed output directory for everything.
++ banned_directories_.push_back("gen/");
++ banned_directories_.push_back("geni/");
++ banned_directories_.push_back("xcodebuild/");
++
++ // You are standing in a mazy of twisty dependencies, all resolved by
++ // putting everything in the header.
++ banned_directories_.push_back("automation/");
++
++ // Don't check system headers.
++ banned_directories_.push_back("/Developer/");
++
++ // Used in really low level threading code that probably shouldn't be out of
++ // lined.
++ ignored_record_names_.insert("ThreadLocalBoolean");
++
++ // A complicated pickle derived struct that is all packed integers.
++ ignored_record_names_.insert("Header");
++
++ // Part of the GPU system that uses multiple included header
++ // weirdness. Never getting this right.
++ ignored_record_names_.insert("Validators");
++
++ // Has a UNIT_TEST only constructor. Isn't *terribly* complex...
++ ignored_record_names_.insert("AutocompleteController");
++ ignored_record_names_.insert("HistoryURLProvider");
++
++ // Because of chrome frame
++ ignored_record_names_.insert("ReliabilityTestSuite");
++
++ // Used over in the net unittests. A large enough bundle of integers with 1
++ // non-pod class member. Probably harmless.
++ ignored_record_names_.insert("MockTransaction");
++
++ // Used heavily in ui_unittests and once in views_unittests. Fixing this
++ // isn't worth the overhead of an additional library.
++ ignored_record_names_.insert("TestAnimationDelegate");
++
++ // Part of our public interface that nacl and friends use. (Arguably, this
++ // should mean that this is a higher priority but fixing this looks hard.)
++ ignored_record_names_.insert("PluginVersionInfo");
++}
++
++std::string ChromeClassTester::GetNamespaceImpl(const DeclContext* context,
++ const std::string& candidate) {
++ switch (context->getDeclKind()) {
++ case Decl::TranslationUnit: {
++ return candidate;
++ }
++ case Decl::Namespace: {
++ const NamespaceDecl* decl = dyn_cast<NamespaceDecl>(context);
++ std::string name_str;
++ llvm::raw_string_ostream OS(name_str);
++ if (decl->isAnonymousNamespace())
++ OS << "<anonymous namespace>";
++ else
++ OS << *decl;
++ return GetNamespaceImpl(context->getParent(),
++ OS.str());
++ }
++ default: {
++ return GetNamespaceImpl(context->getParent(), candidate);
++ }
++ }
++}
++
++bool ChromeClassTester::InBannedDirectory(SourceLocation loc) {
++ std::string filename;
++ if (!GetFilename(loc, &filename)) {
++ // If the filename cannot be determined, simply treat this as a banned
++ // location, instead of going through the full lookup process.
++ return true;
++ }
++
++ // We need to special case scratch space; which is where clang does its
++ // macro expansion. We explicitly want to allow people to do otherwise bad
++ // things through macros that were defined due to third party libraries.
++ if (filename == "<scratch space>")
++ return true;
++
++ // Don't complain about autogenerated protobuf files.
++ if (ends_with(filename, ".pb.h")) {
++ return true;
++ }
++
++ // We need to munge the paths so that they are relative to the repository
++ // srcroot. We first resolve the symlinktastic relative path and then
++ // remove our known srcroot from it if needed.
++ char resolvedPath[MAXPATHLEN];
++ if (realpath(filename.c_str(), resolvedPath)) {
++ filename = resolvedPath;
++ }
++
++ // On linux, chrome is often checked out to /usr/local/google. Due to the
++ // "usr" rule in banned_directories_, all diagnostics would be suppressed
++ // in that case. As a workaround, strip that prefix.
++ filename = lstrip(filename, "/usr/local/google");
++
++ for (std::vector<std::string>::const_iterator it =
++ banned_directories_.begin();
++ it != banned_directories_.end(); ++it) {
++ // If we can find any of the banned path components in this path, then
++ // this file is rejected.
++ size_t index = filename.find(*it);
++ if (index != std::string::npos) {
++ bool matches_full_dir_name = index == 0 || filename[index - 1] == '/';
++ if ((*it)[0] == '/')
++ matches_full_dir_name = true;
++ if (matches_full_dir_name)
++ return true;
++ }
++ }
++
++ return false;
++}
++
++bool ChromeClassTester::IsIgnoredType(const std::string& base_name) {
++ return ignored_record_names_.find(base_name) != ignored_record_names_.end();
++}
++
++bool ChromeClassTester::GetFilename(SourceLocation loc,
++ std::string* filename) {
++ const SourceManager& source_manager = instance_.getSourceManager();
++ SourceLocation spelling_location = source_manager.getSpellingLoc(loc);
++ PresumedLoc ploc = source_manager.getPresumedLoc(spelling_location);
++ if (ploc.isInvalid()) {
++ // If we're in an invalid location, we're looking at things that aren't
++ // actually stated in the source.
++ return false;
++ }
++
++ *filename = ploc.getFilename();
++ return true;
++}
+diff --git a/tools/clang/plugins/ChromeClassTester.h b/tools/clang/plugins/ChromeClassTester.h
+new file mode 100644
+index 0000000000..588ae9cae5
+--- /dev/null
++++ b/tools/clang/plugins/ChromeClassTester.h
+@@ -0,0 +1,84 @@
++// Copyright (c) 2012 The Chromium Authors. All rights reserved.
++// Use of this source code is governed by a BSD-style license that can be
++// found in the LICENSE file.
++
++#ifndef TOOLS_CLANG_PLUGINS_CHROMECLASSTESTER_H_
++#define TOOLS_CLANG_PLUGINS_CHROMECLASSTESTER_H_
++
++#include <set>
++#include <vector>
++
++#include "clang/AST/ASTConsumer.h"
++#include "clang/AST/TypeLoc.h"
++#include "clang/Frontend/CompilerInstance.h"
++
++// A class on top of ASTConsumer that forwards classes defined in Chromium
++// headers to subclasses which implement CheckChromeClass().
++class ChromeClassTester : public clang::ASTConsumer {
++ public:
++ explicit ChromeClassTester(clang::CompilerInstance& instance);
++ virtual ~ChromeClassTester();
++
++ // clang::ASTConsumer:
++ virtual void HandleTagDeclDefinition(clang::TagDecl* tag);
++ virtual bool HandleTopLevelDecl(clang::DeclGroupRef group_ref);
++
++ protected:
++ clang::CompilerInstance& instance() { return instance_; }
++ clang::DiagnosticsEngine& diagnostic() { return diagnostic_; }
++
++ // Emits a simple warning; this shouldn't be used if you require printf-style
++ // printing.
++ void emitWarning(clang::SourceLocation loc, const char* error);
++
++ // Utility method for subclasses to check if this class is in a banned
++ // namespace.
++ bool InBannedNamespace(const clang::Decl* record);
++
++ // Utility method for subclasses to determine the namespace of the
++ // specified record, if any. Unnamed namespaces will be identified as
++ // "<anonymous namespace>".
++ std::string GetNamespace(const clang::Decl* record);
++
++ // Utility method for subclasses to check if this class is within an
++ // implementation (.cc, .cpp, .mm) file.
++ bool InImplementationFile(clang::SourceLocation location);
++
++ private:
++ void BuildBannedLists();
++
++ void CheckTag(clang::TagDecl*);
++
++ // Filtered versions of tags that are only called with things defined in
++ // chrome header files.
++ virtual void CheckChromeClass(clang::SourceLocation record_location,
++ clang::CXXRecordDecl* record) = 0;
++
++ // Utility methods used for filtering out non-chrome classes (and ones we
++ // deliberately ignore) in HandleTagDeclDefinition().
++ std::string GetNamespaceImpl(const clang::DeclContext* context,
++ const std::string& candidate);
++ bool InBannedDirectory(clang::SourceLocation loc);
++ bool IsIgnoredType(const std::string& base_name);
++
++ // Attempts to determine the filename for the given SourceLocation.
++ // Returns false if the filename could not be determined.
++ bool GetFilename(clang::SourceLocation loc, std::string* filename);
++
++ clang::CompilerInstance& instance_;
++ clang::DiagnosticsEngine& diagnostic_;
++
++ // List of banned namespaces.
++ std::vector<std::string> banned_namespaces_;
++
++ // List of banned directories.
++ std::vector<std::string> banned_directories_;
++
++ // List of types that we don't check.
++ std::set<std::string> ignored_record_names_;
++
++ // List of decls to check once the current top-level decl is parsed.
++ std::vector<clang::TagDecl*> pending_class_decls_;
++};
++
++#endif // TOOLS_CLANG_PLUGINS_CHROMECLASSTESTER_H_
+diff --git a/tools/clang/plugins/FindBadConstructs.cpp b/tools/clang/plugins/FindBadConstructs.cpp
+new file mode 100644
+index 0000000000..b79a64dbd1
+--- /dev/null
++++ b/tools/clang/plugins/FindBadConstructs.cpp
+@@ -0,0 +1,435 @@
++// Copyright (c) 2012 The Chromium Authors. All rights reserved.
++// Use of this source code is governed by a BSD-style license that can be
++// found in the LICENSE file.
++
++// This file defines a bunch of recurring problems in the Chromium C++ code.
++//
++// Checks that are implemented:
++// - Constructors/Destructors should not be inlined if they are of a complex
++// class type.
++// - Missing "virtual" keywords on methods that should be virtual.
++// - Non-annotated overriding virtual methods.
++// - Virtual methods with nonempty implementations in their headers.
++// - Classes that derive from base::RefCounted / base::RefCountedThreadSafe
++// should have protected or private destructors.
++
++#include "clang/Frontend/FrontendPluginRegistry.h"
++#include "clang/AST/ASTConsumer.h"
++#include "clang/AST/AST.h"
++#include "clang/AST/CXXInheritance.h"
++#include "clang/AST/TypeLoc.h"
++#include "clang/Basic/SourceManager.h"
++#include "clang/Frontend/CompilerInstance.h"
++#include "llvm/Support/raw_ostream.h"
++
++#include "ChromeClassTester.h"
++
++using namespace clang;
++
++namespace {
++
++bool TypeHasNonTrivialDtor(const Type* type) {
++ if (const CXXRecordDecl* cxx_r = type->getCXXRecordDeclForPointerType())
++ return cxx_r->hasTrivialDestructor();
++
++ return false;
++}
++
++// Returns the underlying Type for |type| by expanding typedefs and removing
++// any namespace qualifiers.
++const Type* UnwrapType(const Type* type) {
++ if (const ElaboratedType* elaborated = dyn_cast<ElaboratedType>(type))
++ return UnwrapType(elaborated->getNamedType().getTypePtr());
++ if (const TypedefType* typedefed = dyn_cast<TypedefType>(type))
++ return UnwrapType(typedefed->desugar().getTypePtr());
++ return type;
++}
++
++// Searches for constructs that we know we don't want in the Chromium code base.
++class FindBadConstructsConsumer : public ChromeClassTester {
++ public:
++ FindBadConstructsConsumer(CompilerInstance& instance,
++ bool check_refcounted_dtors,
++ bool check_virtuals_in_implementations)
++ : ChromeClassTester(instance),
++ check_refcounted_dtors_(check_refcounted_dtors),
++ check_virtuals_in_implementations_(check_virtuals_in_implementations) {
++ }
++
++ virtual void CheckChromeClass(SourceLocation record_location,
++ CXXRecordDecl* record) {
++ bool implementation_file = InImplementationFile(record_location);
++
++ if (!implementation_file) {
++ // Only check for "heavy" constructors/destructors in header files;
++ // within implementation files, there is no performance cost.
++ CheckCtorDtorWeight(record_location, record);
++ }
++
++ if (!implementation_file || check_virtuals_in_implementations_) {
++ bool warn_on_inline_bodies = !implementation_file;
++
++ // Check that all virtual methods are marked accordingly with both
++ // virtual and OVERRIDE.
++ CheckVirtualMethods(record_location, record, warn_on_inline_bodies);
++ }
++
++ if (check_refcounted_dtors_)
++ CheckRefCountedDtors(record_location, record);
++ }
++
++ private:
++ bool check_refcounted_dtors_;
++ bool check_virtuals_in_implementations_;
++
++ // Returns true if |base| specifies one of the Chromium reference counted
++ // classes (base::RefCounted / base::RefCountedThreadSafe). |user_data| is
++ // ignored.
++ static bool IsRefCountedCallback(const CXXBaseSpecifier* base,
++ CXXBasePath& path,
++ void* user_data) {
++ FindBadConstructsConsumer* self =
++ static_cast<FindBadConstructsConsumer*>(user_data);
++
++ const TemplateSpecializationType* base_type =
++ dyn_cast<TemplateSpecializationType>(
++ UnwrapType(base->getType().getTypePtr()));
++ if (!base_type) {
++ // Base-most definition is not a template, so this cannot derive from
++ // base::RefCounted. However, it may still be possible to use with a
++ // scoped_refptr<> and support ref-counting, so this is not a perfect
++ // guarantee of safety.
++ return false;
++ }
++
++ TemplateName name = base_type->getTemplateName();
++ if (TemplateDecl* decl = name.getAsTemplateDecl()) {
++ std::string base_name = decl->getNameAsString();
++
++ // Check for both base::RefCounted and base::RefCountedThreadSafe.
++ if (base_name.compare(0, 10, "RefCounted") == 0 &&
++ self->GetNamespace(decl) == "base") {
++ return true;
++ }
++ }
++ return false;
++ }
++
++ // Prints errors if the destructor of a RefCounted class is public.
++ void CheckRefCountedDtors(SourceLocation record_location,
++ CXXRecordDecl* record) {
++ // Skip anonymous structs.
++ if (record->getIdentifier() == NULL)
++ return;
++
++ CXXBasePaths paths;
++ if (!record->lookupInBases(
++ &FindBadConstructsConsumer::IsRefCountedCallback, this, paths)) {
++ return; // Class does not derive from a ref-counted base class.
++ }
++
++ if (!record->hasUserDeclaredDestructor()) {
++ emitWarning(
++ record_location,
++ "Classes that are ref-counted should have explicit "
++ "destructors that are protected or private.");
++ } else if (CXXDestructorDecl* dtor = record->getDestructor()) {
++ if (dtor->getAccess() == AS_public) {
++ emitWarning(
++ dtor->getInnerLocStart(),
++ "Classes that are ref-counted should not have "
++ "public destructors.");
++ }
++ }
++ }
++
++ // Prints errors if the constructor/destructor weight is too heavy.
++ void CheckCtorDtorWeight(SourceLocation record_location,
++ CXXRecordDecl* record) {
++ // We don't handle anonymous structs. If this record doesn't have a
++ // name, it's of the form:
++ //
++ // struct {
++ // ...
++ // } name_;
++ if (record->getIdentifier() == NULL)
++ return;
++
++ // Count the number of templated base classes as a feature of whether the
++ // destructor can be inlined.
++ int templated_base_classes = 0;
++ for (CXXRecordDecl::base_class_const_iterator it = record->bases_begin();
++ it != record->bases_end(); ++it) {
++ if (it->getTypeSourceInfo()->getTypeLoc().getTypeLocClass() ==
++ TypeLoc::TemplateSpecialization) {
++ ++templated_base_classes;
++ }
++ }
++
++ // Count the number of trivial and non-trivial member variables.
++ int trivial_member = 0;
++ int non_trivial_member = 0;
++ int templated_non_trivial_member = 0;
++ for (RecordDecl::field_iterator it = record->field_begin();
++ it != record->field_end(); ++it) {
++ CountType(it->getType().getTypePtr(),
++ &trivial_member,
++ &non_trivial_member,
++ &templated_non_trivial_member);
++ }
++
++ // Check to see if we need to ban inlined/synthesized constructors. Note
++ // that the cutoffs here are kind of arbitrary. Scores over 10 break.
++ int dtor_score = 0;
++ // Deriving from a templated base class shouldn't be enough to trigger
++ // the ctor warning, but if you do *anything* else, it should.
++ //
++ // TODO(erg): This is motivated by templated base classes that don't have
++ // any data members. Somehow detect when templated base classes have data
++ // members and treat them differently.
++ dtor_score += templated_base_classes * 9;
++ // Instantiating a template is an insta-hit.
++ dtor_score += templated_non_trivial_member * 10;
++ // The fourth normal class member should trigger the warning.
++ dtor_score += non_trivial_member * 3;
++
++ int ctor_score = dtor_score;
++ // You should be able to have 9 ints before we warn you.
++ ctor_score += trivial_member;
++
++ if (ctor_score >= 10) {
++ if (!record->hasUserDeclaredConstructor()) {
++ emitWarning(record_location,
++ "Complex class/struct needs an explicit out-of-line "
++ "constructor.");
++ } else {
++ // Iterate across all the constructors in this file and yell if we
++ // find one that tries to be inline.
++ for (CXXRecordDecl::ctor_iterator it = record->ctor_begin();
++ it != record->ctor_end(); ++it) {
++ if (it->hasInlineBody()) {
++ if (it->isCopyConstructor() &&
++ !record->hasUserDeclaredCopyConstructor()) {
++ emitWarning(record_location,
++ "Complex class/struct needs an explicit out-of-line "
++ "copy constructor.");
++ } else {
++ emitWarning(it->getInnerLocStart(),
++ "Complex constructor has an inlined body.");
++ }
++ }
++ }
++ }
++ }
++
++ // The destructor side is equivalent except that we don't check for
++ // trivial members; 20 ints don't need a destructor.
++ if (dtor_score >= 10 && !record->hasTrivialDestructor()) {
++ if (!record->hasUserDeclaredDestructor()) {
++ emitWarning(
++ record_location,
++ "Complex class/struct needs an explicit out-of-line "
++ "destructor.");
++ } else if (CXXDestructorDecl* dtor = record->getDestructor()) {
++ if (dtor->hasInlineBody()) {
++ emitWarning(dtor->getInnerLocStart(),
++ "Complex destructor has an inline body.");
++ }
++ }
++ }
++ }
++
++ void CheckVirtualMethod(const CXXMethodDecl* method,
++ bool warn_on_inline_bodies) {
++ if (!method->isVirtual())
++ return;
++
++ if (!method->isVirtualAsWritten()) {
++ SourceLocation loc = method->getTypeSpecStartLoc();
++ if (isa<CXXDestructorDecl>(method))
++ loc = method->getInnerLocStart();
++ emitWarning(loc, "Overriding method must have \"virtual\" keyword.");
++ }
++
++ // Virtual methods should not have inline definitions beyond "{}". This
++ // only matters for header files.
++ if (warn_on_inline_bodies && method->hasBody() &&
++ method->hasInlineBody()) {
++ if (CompoundStmt* cs = dyn_cast<CompoundStmt>(method->getBody())) {
++ if (cs->size()) {
++ emitWarning(
++ cs->getLBracLoc(),
++ "virtual methods with non-empty bodies shouldn't be "
++ "declared inline.");
++ }
++ }
++ }
++ }
++
++ bool InTestingNamespace(const Decl* record) {
++ return GetNamespace(record).find("testing") != std::string::npos;
++ }
++
++ bool IsMethodInBannedNamespace(const CXXMethodDecl* method) {
++ if (InBannedNamespace(method))
++ return true;
++ for (CXXMethodDecl::method_iterator i = method->begin_overridden_methods();
++ i != method->end_overridden_methods();
++ ++i) {
++ const CXXMethodDecl* overridden = *i;
++ if (IsMethodInBannedNamespace(overridden))
++ return true;
++ }
++
++ return false;
++ }
++
++ void CheckOverriddenMethod(const CXXMethodDecl* method) {
++ if (!method->size_overridden_methods() || method->getAttr<OverrideAttr>())
++ return;
++
++ if (isa<CXXDestructorDecl>(method) || method->isPure())
++ return;
++
++ if (IsMethodInBannedNamespace(method))
++ return;
++
++ SourceLocation loc = method->getTypeSpecStartLoc();
++ emitWarning(loc, "Overriding method must be marked with OVERRIDE.");
++ }
++
++ // Makes sure there is a "virtual" keyword on virtual methods.
++ //
++ // Gmock objects trigger these for each MOCK_BLAH() macro used. So we have a
++ // trick to get around that. If a class has member variables whose types are
++ // in the "testing" namespace (which is how gmock works behind the scenes),
++ // there's a really high chance we won't care about these errors
++ void CheckVirtualMethods(SourceLocation record_location,
++ CXXRecordDecl* record,
++ bool warn_on_inline_bodies) {
++ for (CXXRecordDecl::field_iterator it = record->field_begin();
++ it != record->field_end(); ++it) {
++ CXXRecordDecl* record_type =
++ it->getTypeSourceInfo()->getTypeLoc().getTypePtr()->
++ getAsCXXRecordDecl();
++ if (record_type) {
++ if (InTestingNamespace(record_type)) {
++ return;
++ }
++ }
++ }
++
++ for (CXXRecordDecl::method_iterator it = record->method_begin();
++ it != record->method_end(); ++it) {
++ if (it->isCopyAssignmentOperator() || isa<CXXConstructorDecl>(*it)) {
++ // Ignore constructors and assignment operators.
++ } else if (isa<CXXDestructorDecl>(*it) &&
++ !record->hasUserDeclaredDestructor()) {
++ // Ignore non-user-declared destructors.
++ } else {
++ CheckVirtualMethod(*it, warn_on_inline_bodies);
++ CheckOverriddenMethod(*it);
++ }
++ }
++ }
++
++ void CountType(const Type* type,
++ int* trivial_member,
++ int* non_trivial_member,
++ int* templated_non_trivial_member) {
++ switch (type->getTypeClass()) {
++ case Type::Record: {
++ // Simplifying; the whole class isn't trivial if the dtor is, but
++ // we use this as a signal about complexity.
++ if (TypeHasNonTrivialDtor(type))
++ (*trivial_member)++;
++ else
++ (*non_trivial_member)++;
++ break;
++ }
++ case Type::TemplateSpecialization: {
++ TemplateName name =
++ dyn_cast<TemplateSpecializationType>(type)->getTemplateName();
++ bool whitelisted_template = false;
++
++ // HACK: I'm at a loss about how to get the syntax checker to get
++ // whether a template is exterened or not. For the first pass here,
++ // just do retarded string comparisons.
++ if (TemplateDecl* decl = name.getAsTemplateDecl()) {
++ std::string base_name = decl->getNameAsString();
++ if (base_name == "basic_string")
++ whitelisted_template = true;
++ }
++
++ if (whitelisted_template)
++ (*non_trivial_member)++;
++ else
++ (*templated_non_trivial_member)++;
++ break;
++ }
++ case Type::Elaborated: {
++ CountType(
++ dyn_cast<ElaboratedType>(type)->getNamedType().getTypePtr(),
++ trivial_member, non_trivial_member, templated_non_trivial_member);
++ break;
++ }
++ case Type::Typedef: {
++ while (const TypedefType* TT = dyn_cast<TypedefType>(type)) {
++ type = TT->getDecl()->getUnderlyingType().getTypePtr();
++ }
++ CountType(type, trivial_member, non_trivial_member,
++ templated_non_trivial_member);
++ break;
++ }
++ default: {
++ // Stupid assumption: anything we see that isn't the above is one of
++ // the 20 integer types.
++ (*trivial_member)++;
++ break;
++ }
++ }
++ }
++};
++
++class FindBadConstructsAction : public PluginASTAction {
++ public:
++ FindBadConstructsAction()
++ : check_refcounted_dtors_(true),
++ check_virtuals_in_implementations_(true) {
++ }
++
++ protected:
++ // Overridden from PluginASTAction:
++ virtual ASTConsumer* CreateASTConsumer(CompilerInstance& instance,
++ llvm::StringRef ref) {
++ return new FindBadConstructsConsumer(
++ instance, check_refcounted_dtors_, check_virtuals_in_implementations_);
++ }
++
++ virtual bool ParseArgs(const CompilerInstance& instance,
++ const std::vector<std::string>& args) {
++ bool parsed = true;
++
++ for (size_t i = 0; i < args.size() && parsed; ++i) {
++ if (args[i] == "skip-refcounted-dtors") {
++ check_refcounted_dtors_ = false;
++ } else if (args[i] == "skip-virtuals-in-implementations") {
++ check_virtuals_in_implementations_ = false;
++ } else {
++ parsed = false;
++ llvm::errs() << "Unknown argument: " << args[i] << "\n";
++ }
++ }
++
++ return parsed;
++ }
++
++ private:
++ bool check_refcounted_dtors_;
++ bool check_virtuals_in_implementations_;
++};
++
++} // namespace
++
++static FrontendPluginRegistry::Add<FindBadConstructsAction>
++X("find-bad-constructs", "Finds bad C++ constructs");
+diff --git a/tools/clang/plugins/Makefile b/tools/clang/plugins/Makefile
+new file mode 100644
+index 0000000000..0cfec71159
+--- /dev/null
++++ b/tools/clang/plugins/Makefile
+@@ -0,0 +1,19 @@
++# This file requires the clang build system, at least for now. So to use this
++# Makefile, you should execute the following commands to copy this directory
++# into a clang checkout:
++#
++# cp -R <this directory> third_party/llvm/tools/clang/tools/chrome-plugin
++# cd third_party/llvm/tools/clang/tools/chrome-plugin
++# make
++
++CLANG_LEVEL := ../..
++LIBRARYNAME = FindBadConstructs
++
++LINK_LIBS_IN_SHARED = 0
++SHARED_LIBRARY = 1
++
++include $(CLANG_LEVEL)/Makefile
++
++ifeq ($(OS),Darwin)
++ LDFLAGS=-Wl,-undefined,dynamic_lookup
++endif
+diff --git a/tools/clang/plugins/OWNERS b/tools/clang/plugins/OWNERS
+new file mode 100644
+index 0000000000..4733a4f06b
+--- /dev/null
++++ b/tools/clang/plugins/OWNERS
+@@ -0,0 +1 @@
++erg@chromium.org
+diff --git a/tools/clang/plugins/README.chromium b/tools/clang/plugins/README.chromium
+new file mode 100644
+index 0000000000..a2ce0ff557
+--- /dev/null
++++ b/tools/clang/plugins/README.chromium
+@@ -0,0 +1,4 @@
++Documentation for this code is:
++
++- http://code.google.com/p/chromium/wiki/Clang
++- http://code.google.com/p/chromium/wiki/WritingClangPlugins
+diff --git a/tools/clang/plugins/tests/base_refcounted.cpp b/tools/clang/plugins/tests/base_refcounted.cpp
+new file mode 100644
+index 0000000000..364a3e888c
+--- /dev/null
++++ b/tools/clang/plugins/tests/base_refcounted.cpp
+@@ -0,0 +1,72 @@
++// Copyright (c) 2012 The Chromium Authors. All rights reserved.
++// Use of this source code is governed by a BSD-style license that can be
++// found in the LICENSE file.
++
++#include "base_refcounted.h"
++
++#include <cstddef>
++
++namespace {
++
++// Unsafe; should error.
++class AnonymousDerivedProtectedToPublicInImpl
++ : public ProtectedRefCountedDtorInHeader {
++ public:
++ AnonymousDerivedProtectedToPublicInImpl() {}
++ ~AnonymousDerivedProtectedToPublicInImpl() {}
++};
++
++} // namespace
++
++// Unsafe; should error.
++class PublicRefCountedDtorInImpl
++ : public base::RefCounted<PublicRefCountedDtorInImpl> {
++ public:
++ PublicRefCountedDtorInImpl() {}
++ ~PublicRefCountedDtorInImpl() {}
++
++ private:
++ friend class base::RefCounted<PublicRefCountedDtorInImpl>;
++};
++
++class Foo {
++ public:
++ class BarInterface {
++ protected:
++ virtual ~BarInterface() {}
++ };
++
++ typedef base::RefCounted<BarInterface> RefCountedBar;
++ typedef RefCountedBar AnotherTypedef;
++};
++
++class Baz {
++ public:
++ typedef typename Foo::AnotherTypedef MyLocalTypedef;
++};
++
++// Unsafe; should error.
++class UnsafeTypedefChainInImpl : public Baz::MyLocalTypedef {
++ public:
++ UnsafeTypedefChainInImpl() {}
++ ~UnsafeTypedefChainInImpl() {}
++};
++
++int main() {
++ PublicRefCountedDtorInHeader bad;
++ PublicRefCountedDtorInImpl also_bad;
++
++ ProtectedRefCountedDtorInHeader* protected_ok = NULL;
++ PrivateRefCountedDtorInHeader* private_ok = NULL;
++
++ DerivedProtectedToPublicInHeader still_bad;
++ PublicRefCountedThreadSafeDtorInHeader another_bad_variation;
++ AnonymousDerivedProtectedToPublicInImpl and_this_is_bad_too;
++ ImplicitDerivedProtectedToPublicInHeader bad_yet_again;
++ UnsafeTypedefChainInImpl and_again_this_is_bad;
++
++ WebKitPublicDtorInHeader ignored;
++ WebKitDerivedPublicDtorInHeader still_ignored;
++
++ return 0;
++}
+diff --git a/tools/clang/plugins/tests/base_refcounted.h b/tools/clang/plugins/tests/base_refcounted.h
+new file mode 100644
+index 0000000000..1e53215997
+--- /dev/null
++++ b/tools/clang/plugins/tests/base_refcounted.h
+@@ -0,0 +1,121 @@
++// Copyright (c) 2012 The Chromium Authors. All rights reserved.
++// Use of this source code is governed by a BSD-style license that can be
++// found in the LICENSE file.
++
++#ifndef BASE_REFCOUNTED_H_
++#define BASE_REFCOUNTED_H_
++
++namespace base {
++
++template <typename T>
++class RefCounted {
++ public:
++ RefCounted() {}
++ ~RefCounted() {}
++};
++
++template <typename T>
++class RefCountedThreadSafe {
++ public:
++ RefCountedThreadSafe() {}
++ ~RefCountedThreadSafe() {}
++};
++
++} // namespace base
++
++// Ignore classes whose inheritance tree ends in WebKit's RefCounted base
++// class. Though prone to error, this pattern is very prevalent in WebKit
++// code, so do not issue any warnings.
++namespace WebKit {
++
++template <typename T>
++class RefCounted {
++ public:
++ RefCounted() {}
++ ~RefCounted() {}
++};
++
++} // namespace WebKit
++
++// Unsafe; should error.
++class PublicRefCountedDtorInHeader
++ : public base::RefCounted<PublicRefCountedDtorInHeader> {
++ public:
++ PublicRefCountedDtorInHeader() {}
++ ~PublicRefCountedDtorInHeader() {}
++
++ private:
++ friend class base::RefCounted<PublicRefCountedDtorInHeader>;
++};
++
++// Unsafe; should error.
++class PublicRefCountedThreadSafeDtorInHeader
++ : public base::RefCountedThreadSafe<
++ PublicRefCountedThreadSafeDtorInHeader> {
++ public:
++ PublicRefCountedThreadSafeDtorInHeader() {}
++ ~PublicRefCountedThreadSafeDtorInHeader() {}
++
++ private:
++ friend class base::RefCountedThreadSafe<
++ PublicRefCountedThreadSafeDtorInHeader>;
++};
++
++// Safe; should not have errors.
++class ProtectedRefCountedDtorInHeader
++ : public base::RefCounted<ProtectedRefCountedDtorInHeader> {
++ public:
++ ProtectedRefCountedDtorInHeader() {}
++
++ protected:
++ ~ProtectedRefCountedDtorInHeader() {}
++
++ private:
++ friend class base::RefCounted<ProtectedRefCountedDtorInHeader>;
++};
++
++// Safe; should not have errors.
++class PrivateRefCountedDtorInHeader
++ : public base::RefCounted<PrivateRefCountedDtorInHeader> {
++ public:
++ PrivateRefCountedDtorInHeader() {}
++
++ private:
++ ~PrivateRefCountedDtorInHeader() {}
++ friend class base::RefCounted<PrivateRefCountedDtorInHeader>;
++};
++
++// Unsafe; A grandchild class ends up exposing their parent and grandparent's
++// destructors.
++class DerivedProtectedToPublicInHeader
++ : public ProtectedRefCountedDtorInHeader {
++ public:
++ DerivedProtectedToPublicInHeader() {}
++ ~DerivedProtectedToPublicInHeader() {}
++};
++
++// Unsafe; A grandchild ends up implicitly exposing their parent and
++// grantparent's destructors.
++class ImplicitDerivedProtectedToPublicInHeader
++ : public ProtectedRefCountedDtorInHeader {
++ public:
++ ImplicitDerivedProtectedToPublicInHeader() {}
++};
++
++// Unsafe-but-ignored; should not have errors.
++class WebKitPublicDtorInHeader
++ : public WebKit::RefCounted<WebKitPublicDtorInHeader> {
++ public:
++ WebKitPublicDtorInHeader() {}
++ ~WebKitPublicDtorInHeader() {}
++};
++
++// Unsafe-but-ignored; should not have errors.
++class WebKitDerivedPublicDtorInHeader
++ : public WebKitPublicDtorInHeader {
++ public:
++ WebKitDerivedPublicDtorInHeader() {}
++ ~WebKitDerivedPublicDtorInHeader() {}
++};
++
++#endif // BASE_REFCOUNTED_H_
+diff --git a/tools/clang/plugins/tests/base_refcounted.txt b/tools/clang/plugins/tests/base_refcounted.txt
+new file mode 100644
+index 0000000000..4626424177
+--- /dev/null
++++ b/tools/clang/plugins/tests/base_refcounted.txt
+@@ -0,0 +1,23 @@
++In file included from base_refcounted.cpp:5:
++./base_refcounted.h:45:3: warning: [chromium-style] Classes that are ref-counted should not have public destructors.
++ ~PublicRefCountedDtorInHeader() {}
++ ^
++./base_refcounted.h:57:3: warning: [chromium-style] Classes that are ref-counted should not have public destructors.
++ ~PublicRefCountedThreadSafeDtorInHeader() {}
++ ^
++./base_refcounted.h:94:3: warning: [chromium-style] Classes that are ref-counted should not have public destructors.
++ ~DerivedProtectedToPublicInHeader() {}
++ ^
++./base_refcounted.h:99:1: warning: [chromium-style] Classes that are ref-counted should have explicit destructors that are protected or private.
++class ImplicitDerivedProtectedToPublicInHeader
++^
++base_refcounted.cpp:16:3: warning: [chromium-style] Classes that are ref-counted should not have public destructors.
++ ~AnonymousDerivedProtectedToPublicInImpl() {}
++ ^
++base_refcounted.cpp:26:3: warning: [chromium-style] Classes that are ref-counted should not have public destructors.
++ ~PublicRefCountedDtorInImpl() {}
++ ^
++base_refcounted.cpp:52:3: warning: [chromium-style] Classes that are ref-counted should not have public destructors.
++ ~UnsafeTypedefChainInImpl() {}
++ ^
++7 warnings generated.
+diff --git a/tools/clang/plugins/tests/inline_copy_ctor.cpp b/tools/clang/plugins/tests/inline_copy_ctor.cpp
+new file mode 100644
+index 0000000000..dcd90020c5
+--- /dev/null
++++ b/tools/clang/plugins/tests/inline_copy_ctor.cpp
+@@ -0,0 +1,5 @@
++// Copyright (c) 2012 The Chromium Authors. All rights reserved.
++// Use of this source code is governed by a BSD-style license that can be
++// found in the LICENSE file.
++
++#include "inline_copy_ctor.h"
+diff --git a/tools/clang/plugins/tests/inline_copy_ctor.h b/tools/clang/plugins/tests/inline_copy_ctor.h
+new file mode 100644
+index 0000000000..619a18392b
+--- /dev/null
++++ b/tools/clang/plugins/tests/inline_copy_ctor.h
+@@ -0,0 +1,12 @@
++// Copyright (c) 2012 The Chromium Authors. All rights reserved.
++// Use of this source code is governed by a BSD-style license that can be
++// found in the LICENSE file.
++
++struct C {
++ C();
++ ~C();
++
++ static C foo() { return C(); }
++
++ int a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p , q, r, s, t, u, v, w, x;
++};
+diff --git a/tools/clang/plugins/tests/inline_copy_ctor.txt b/tools/clang/plugins/tests/inline_copy_ctor.txt
+new file mode 100644
+index 0000000000..bc4bd8911e
+--- /dev/null
++++ b/tools/clang/plugins/tests/inline_copy_ctor.txt
+@@ -0,0 +1,5 @@
++In file included from inline_copy_ctor.cpp:5:
++./inline_copy_ctor.h:5:1: warning: [chromium-style] Complex class/struct needs an explicit out-of-line copy constructor.
++struct C {
++^
++1 warning generated.
+diff --git a/tools/clang/plugins/tests/inline_ctor.cpp b/tools/clang/plugins/tests/inline_ctor.cpp
+new file mode 100644
+index 0000000000..6a751fb405
+--- /dev/null
++++ b/tools/clang/plugins/tests/inline_ctor.cpp
+@@ -0,0 +1,25 @@
++// Copyright (c) 2011 The Chromium Authors. All rights reserved.
++// Use of this source code is governed by a BSD-style license that can be
++// found in the LICENSE file.
++
++#include "inline_ctor.h"
++
++#include <string>
++#include <vector>
++
++// We don't warn on classes that are in CPP files.
++class InlineInCPPOK {
++ public:
++ InlineInCPPOK() {}
++ ~InlineInCPPOK() {}
++
++ private:
++ std::vector<int> one_;
++ std::vector<std::string> two_;
++};
++
++int main() {
++ InlineInCPPOK one;
++ InlineCtorsArentOKInHeader two;
++ return 0;
++}
+diff --git a/tools/clang/plugins/tests/inline_ctor.h b/tools/clang/plugins/tests/inline_ctor.h
+new file mode 100644
+index 0000000000..d053b2f57d
+--- /dev/null
++++ b/tools/clang/plugins/tests/inline_ctor.h
+@@ -0,0 +1,21 @@
++// Copyright (c) 2012 The Chromium Authors. All rights reserved.
++// Use of this source code is governed by a BSD-style license that can be
++// found in the LICENSE file.
++
++#ifndef INLINE_CTOR_H_
++#define INLINE_CTOR_H_
++
++#include <string>
++#include <vector>
++
++class InlineCtorsArentOKInHeader {
++ public:
++ InlineCtorsArentOKInHeader() {}
++ ~InlineCtorsArentOKInHeader() {}
++
++ private:
++ std::vector<int> one_;
++ std::vector<std::string> two_;
++};
++
++#endif // INLINE_CTOR_H_
+diff --git a/tools/clang/plugins/tests/inline_ctor.txt b/tools/clang/plugins/tests/inline_ctor.txt
+new file mode 100644
+index 0000000000..caa0cb4e3b
+--- /dev/null
++++ b/tools/clang/plugins/tests/inline_ctor.txt
+@@ -0,0 +1,8 @@
++In file included from inline_ctor.cpp:5:
++./inline_ctor.h:13:3: warning: [chromium-style] Complex constructor has an inlined body.
++ InlineCtorsArentOKInHeader() {}
++ ^
++./inline_ctor.h:14:3: warning: [chromium-style] Complex destructor has an inline body.
++ ~InlineCtorsArentOKInHeader() {}
++ ^
++2 warnings generated.
+diff --git a/tools/clang/plugins/tests/missing_ctor.cpp b/tools/clang/plugins/tests/missing_ctor.cpp
+new file mode 100644
+index 0000000000..8ee2fb2ac8
+--- /dev/null
++++ b/tools/clang/plugins/tests/missing_ctor.cpp
+@@ -0,0 +1,23 @@
++// Copyright (c) 2011 The Chromium Authors. All rights reserved.
++// Use of this source code is governed by a BSD-style license that can be
++// found in the LICENSE file.
++
++#include "missing_ctor.h"
++
++#include <string>
++#include <vector>
++
++// We don't warn on classes that use default ctors in cpp files.
++class MissingInCPPOK {
++ public:
++
++ private:
++ std::vector<int> one_;
++ std::vector<std::string> two_;
++};
++
++int main() {
++ MissingInCPPOK one;
++ MissingCtorsArentOKInHeader two;
++ return 0;
++}
+diff --git a/tools/clang/plugins/tests/missing_ctor.h b/tools/clang/plugins/tests/missing_ctor.h
+new file mode 100644
+index 0000000000..1050457a1a
+--- /dev/null
++++ b/tools/clang/plugins/tests/missing_ctor.h
+@@ -0,0 +1,19 @@
++// Copyright (c) 2011 The Chromium Authors. All rights reserved.
++// Use of this source code is governed by a BSD-style license that can be
++// found in the LICENSE file.
++
++#ifndef MISSING_CTOR_H_
++#define MISSING_CTOR_H_
++
++#include <string>
++#include <vector>
++
++class MissingCtorsArentOKInHeader {
++ public:
++
++ private:
++ std::vector<int> one_;
++ std::vector<std::string> two_;
++};
++
++#endif // MISSING_CTOR_H_
+diff --git a/tools/clang/plugins/tests/missing_ctor.txt b/tools/clang/plugins/tests/missing_ctor.txt
+new file mode 100644
+index 0000000000..301449c4ac
+--- /dev/null
++++ b/tools/clang/plugins/tests/missing_ctor.txt
+@@ -0,0 +1,6 @@
++In file included from missing_ctor.cpp:5:
++./missing_ctor.h:11:1: warning: [chromium-style] Complex class/struct needs an explicit out-of-line constructor.
++class MissingCtorsArentOKInHeader {
++^
++./missing_ctor.h:11:1: warning: [chromium-style] Complex class/struct needs an explicit out-of-line destructor.
++2 warnings generated.
+diff --git a/tools/clang/plugins/tests/nested_class_inline_ctor.cpp b/tools/clang/plugins/tests/nested_class_inline_ctor.cpp
+new file mode 100644
+index 0000000000..aa90a95eb3
+--- /dev/null
++++ b/tools/clang/plugins/tests/nested_class_inline_ctor.cpp
+@@ -0,0 +1,5 @@
++// Copyright (c) 2012 The Chromium Authors. All rights reserved.
++// Use of this source code is governed by a BSD-style license that can be
++// found in the LICENSE file.
++
++#include "nested_class_inline_ctor.h"
+diff --git a/tools/clang/plugins/tests/nested_class_inline_ctor.h b/tools/clang/plugins/tests/nested_class_inline_ctor.h
+new file mode 100644
+index 0000000000..01cfea9232
+--- /dev/null
++++ b/tools/clang/plugins/tests/nested_class_inline_ctor.h
+@@ -0,0 +1,22 @@
++// Copyright (c) 2012 The Chromium Authors. All rights reserved.
++// Use of this source code is governed by a BSD-style license that can be
++// found in the LICENSE file.
++
++#ifndef NESTED_CLASS_INLINE_CTOR_H_
++#define NESTED_CLASS_INLINE_CTOR_H_
++
++#include <string>
++#include <vector>
++
++// See crbug.com/136863.
++
++class Foo {
++ class Bar {
++ Bar() {}
++ ~Bar() {}
++
++ std::vector<std::string> a;
++ };
++};
++
++#endif // NESTED_CLASS_INLINE_CTOR_H_
+diff --git a/tools/clang/plugins/tests/nested_class_inline_ctor.txt b/tools/clang/plugins/tests/nested_class_inline_ctor.txt
+new file mode 100644
+index 0000000000..39bd6e1dce
+--- /dev/null
++++ b/tools/clang/plugins/tests/nested_class_inline_ctor.txt
+@@ -0,0 +1,8 @@
++In file included from nested_class_inline_ctor.cpp:5:
++./nested_class_inline_ctor.h:15:5: warning: [chromium-style] Complex constructor has an inlined body.
++ Bar() {}
++ ^
++./nested_class_inline_ctor.h:16:5: warning: [chromium-style] Complex destructor has an inline body.
++ ~Bar() {}
++ ^
++2 warnings generated.
+diff --git a/tools/clang/plugins/tests/overridden_methods.cpp b/tools/clang/plugins/tests/overridden_methods.cpp
+new file mode 100644
+index 0000000000..f572a41733
+--- /dev/null
++++ b/tools/clang/plugins/tests/overridden_methods.cpp
+@@ -0,0 +1,38 @@
++// Copyright (c) 2012 The Chromium Authors. All rights reserved.
++// Use of this source code is governed by a BSD-style license that can be
++// found in the LICENSE file.
++
++#include "overridden_methods.h"
++
++// Fill in the implementations
++void DerivedClass::SomeMethod() {}
++void DerivedClass::SomeOtherMethod() {}
++void DerivedClass::WebKitModifiedSomething() {}
++
++class ImplementationInterimClass : public BaseClass {
++ public:
++ // Should not warn about pure virtual methods.
++ virtual void SomeMethod() = 0;
++};
++
++class ImplementationDerivedClass : public ImplementationInterimClass,
++ public webkit_glue::WebKitObserverImpl {
++ public:
++ // Should not warn about destructors.
++ virtual ~ImplementationDerivedClass() {}
++ // Should warn.
++ virtual void SomeMethod();
++ // Should not warn if marked as override.
++ virtual void SomeOtherMethod() override;
++ // Should not warn for inline implementations in implementation files.
++ virtual void SomeInlineMethod() {}
++ // Should not warn if overriding a method whose origin is WebKit.
++ virtual void WebKitModifiedSomething();
++ // Should warn if overridden method isn't pure.
++ virtual void SomeNonPureBaseMethod() {}
++};
++
++int main() {
++ DerivedClass something;
++ ImplementationDerivedClass something_else;
++}
+diff --git a/tools/clang/plugins/tests/overridden_methods.h b/tools/clang/plugins/tests/overridden_methods.h
+new file mode 100644
+index 0000000000..150c79913f
+--- /dev/null
++++ b/tools/clang/plugins/tests/overridden_methods.h
+@@ -0,0 +1,54 @@
++// Copyright (c) 2011 The Chromium Authors. All rights reserved.
++// Use of this source code is governed by a BSD-style license that can be
++// found in the LICENSE file.
++
++#ifndef OVERRIDDEN_METHODS_H_
++#define OVERRIDDEN_METHODS_H_
++
++// Should warn about overriding of methods.
++class BaseClass {
++ public:
++ virtual ~BaseClass() {}
++ virtual void SomeMethod() = 0;
++ virtual void SomeOtherMethod() = 0;
++ virtual void SomeInlineMethod() = 0;
++ virtual void SomeNonPureBaseMethod() {}
++};
++
++class InterimClass : public BaseClass {
++ // Should not warn about pure virtual methods.
++ virtual void SomeMethod() = 0;
++};
++
++namespace WebKit {
++class WebKitObserver {
++ public:
++ virtual void WebKitModifiedSomething() {};
++};
++} // namespace WebKit
++
++namespace webkit_glue {
++class WebKitObserverImpl : WebKit::WebKitObserver {
++ public:
++ virtual void WebKitModifiedSomething() {};
++};
++} // namespace webkit_glue
++
++class DerivedClass : public InterimClass,
++ public webkit_glue::WebKitObserverImpl {
++ public:
++ // Should not warn about destructors.
++ virtual ~DerivedClass() {}
++ // Should warn.
++ virtual void SomeMethod();
++ // Should not warn if marked as override.
++ virtual void SomeOtherMethod() override;
++ // Should warn for inline implementations.
++ virtual void SomeInlineMethod() {}
++ // Should not warn if overriding a method whose origin is WebKit.
++ virtual void WebKitModifiedSomething();
++ // Should warn if overridden method isn't pure.
++ virtual void SomeNonPureBaseMethod() {}
++};
++
++#endif // OVERRIDDEN_METHODS_H_
+diff --git a/tools/clang/plugins/tests/overridden_methods.txt b/tools/clang/plugins/tests/overridden_methods.txt
+new file mode 100644
+index 0000000000..7553ade70e
+--- /dev/null
++++ b/tools/clang/plugins/tests/overridden_methods.txt
+@@ -0,0 +1,20 @@
++In file included from overridden_methods.cpp:5:
++./overridden_methods.h:43:11: warning: [chromium-style] Overriding method must be marked with OVERRIDE.
++ virtual void SomeMethod();
++ ^
++./overridden_methods.h:47:11: warning: [chromium-style] Overriding method must be marked with OVERRIDE.
++ virtual void SomeInlineMethod() {}
++ ^
++./overridden_methods.h:51:11: warning: [chromium-style] Overriding method must be marked with OVERRIDE.
++ virtual void SomeNonPureBaseMethod() {}
++ ^
++overridden_methods.cpp:24:11: warning: [chromium-style] Overriding method must be marked with OVERRIDE.
++ virtual void SomeMethod();
++ ^
++overridden_methods.cpp:28:11: warning: [chromium-style] Overriding method must be marked with OVERRIDE.
++ virtual void SomeInlineMethod() {}
++ ^
++overridden_methods.cpp:32:11: warning: [chromium-style] Overriding method must be marked with OVERRIDE.
++ virtual void SomeNonPureBaseMethod() {}
++ ^
++6 warnings generated.
+diff --git a/tools/clang/plugins/tests/test.sh b/tools/clang/plugins/tests/test.sh
+new file mode 100755
+index 0000000000..262ebbba29
+--- /dev/null
++++ b/tools/clang/plugins/tests/test.sh
+@@ -0,0 +1,72 @@
++#!/bin/bash
++#
++# Copyright (c) 2011 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++#
++# Hacky, primitive testing: This runs the style plugin for a set of input files
++# and compares the output with golden result files.
++
++E_BADARGS=65
++E_FAILEDTEST=1
++
++failed_any_test=
++
++# Prints usage information.
++usage() {
++ echo "Usage: $(basename "${0}")" \
++ "<Path to the llvm build dir, usually Release+Asserts>"
++ echo ""
++ echo " Runs all the libFindBadConstructs unit tests"
++ echo ""
++}
++
++# Runs a single test case.
++do_testcase() {
++ local output="$("${CLANG_DIR}"/bin/clang -c -Wno-c++11-extensions \
++ -Xclang -load -Xclang "${CLANG_DIR}"/lib/libFindBadConstructs.${LIB} \
++ -Xclang -plugin -Xclang find-bad-constructs ${1} 2>&1)"
++ local diffout="$(echo "${output}" | diff - "${2}")"
++ if [ "${diffout}" = "" ]; then
++ echo "PASS: ${1}"
++ else
++ failed_any_test=yes
++ echo "FAIL: ${1}"
++ echo "Output of compiler:"
++ echo "${output}"
++ echo "Expected output:"
++ cat "${2}"
++ echo
++ fi
++}
++
++# Validate input to the script.
++if [[ -z "${1}" ]]; then
++ usage
++ exit ${E_BADARGS}
++elif [[ ! -d "${1}" ]]; then
++ echo "${1} is not a directory."
++ usage
++ exit ${E_BADARGS}
++else
++ export CLANG_DIR="${PWD}/${1}"
++ echo "Using clang directory ${CLANG_DIR}..."
++
++ # The golden files assume that the cwd is this directory. To make the script
++ # work no matter what the cwd is, explicitly cd to there.
++ cd "$(dirname "${0}")"
++
++ if [ "$(uname -s)" = "Linux" ]; then
++ export LIB=so
++ elif [ "$(uname -s)" = "Darwin" ]; then
++ export LIB=dylib
++ fi
++fi
++
++for input in *.cpp; do
++ do_testcase "${input}" "${input%cpp}txt"
++done
++
++if [[ "${failed_any_test}" ]]; then
++ exit ${E_FAILEDTEST}
++fi
+diff --git a/tools/clang/plugins/tests/virtual_methods.cpp b/tools/clang/plugins/tests/virtual_methods.cpp
+new file mode 100644
+index 0000000000..a07cbe4875
+--- /dev/null
++++ b/tools/clang/plugins/tests/virtual_methods.cpp
+@@ -0,0 +1,36 @@
++// Copyright (c) 2012 The Chromium Authors. All rights reserved.
++// Use of this source code is governed by a BSD-style license that can be
++// found in the LICENSE file.
++
++#include "virtual_methods.h"
++
++// Shouldn't warn about method usage in the implementation file.
++class VirtualMethodsInImplementation {
++ public:
++ virtual void MethodIsAbstract() = 0;
++ virtual void MethodHasNoArguments();
++ virtual void MethodHasEmptyDefaultImpl() {}
++ virtual bool ComplainAboutThis() { return true; }
++};
++
++// Stubs to fill in the abstract method
++class ConcreteVirtualMethodsInHeaders : public VirtualMethodsInHeaders {
++ public:
++ virtual void MethodIsAbstract() override {}
++};
++
++class ConcreteVirtualMethodsInImplementation
++ : public VirtualMethodsInImplementation {
++ public:
++ virtual void MethodIsAbstract() override {}
++};
++
++// Fill in the implementations
++void VirtualMethodsInHeaders::MethodHasNoArguments() {}
++void WarnOnMissingVirtual::MethodHasNoArguments() {}
++void VirtualMethodsInImplementation::MethodHasNoArguments() {}
++
++int main() {
++ ConcreteVirtualMethodsInHeaders one;
++ ConcreteVirtualMethodsInImplementation two;
++}
+diff --git a/tools/clang/plugins/tests/virtual_methods.h b/tools/clang/plugins/tests/virtual_methods.h
+new file mode 100644
+index 0000000000..d9fbf96ed3
+--- /dev/null
++++ b/tools/clang/plugins/tests/virtual_methods.h
+@@ -0,0 +1,39 @@
++// Copyright (c) 2011 The Chromium Authors. All rights reserved.
++// Use of this source code is governed by a BSD-style license that can be
++// found in the LICENSE file.
++
++#ifndef VIRTUAL_METHODS_H_
++#define VIRTUAL_METHODS_H_
++
++// Should warn about virtual method usage.
++class VirtualMethodsInHeaders {
++ public:
++ // Don't complain about these.
++ virtual void MethodIsAbstract() = 0;
++ virtual void MethodHasNoArguments();
++ virtual void MethodHasEmptyDefaultImpl() {}
++
++ // But complain about this:
++ virtual bool ComplainAboutThis() { return true; }
++};
++
++// Complain on missing 'virtual' keyword in overrides.
++class WarnOnMissingVirtual : public VirtualMethodsInHeaders {
++ public:
++ void MethodHasNoArguments() override;
++};
++
++// Don't complain about things in a 'testing' namespace.
++namespace testing {
++struct TestStruct {};
++} // namespace testing
++
++class VirtualMethodsInHeadersTesting : public VirtualMethodsInHeaders {
++ public:
++ // Don't complain about no virtual testing methods.
++ void MethodHasNoArguments();
++ private:
++ testing::TestStruct tester_;
++};
++
++#endif // VIRTUAL_METHODS_H_
+diff --git a/tools/clang/plugins/tests/virtual_methods.txt b/tools/clang/plugins/tests/virtual_methods.txt
+new file mode 100644
+index 0000000000..571d6d667d
+--- /dev/null
++++ b/tools/clang/plugins/tests/virtual_methods.txt
+@@ -0,0 +1,8 @@
++In file included from virtual_methods.cpp:5:
++./virtual_methods.h:17:36: warning: [chromium-style] virtual methods with non-empty bodies shouldn't be declared inline.
++ virtual bool ComplainAboutThis() { return true; }
++ ^
++./virtual_methods.h:23:3: warning: [chromium-style] Overriding method must have "virtual" keyword.
++ void MethodHasNoArguments() override;
++ ^
++2 warnings generated.
+diff --git a/tools/clang/scripts/package.sh b/tools/clang/scripts/package.sh
+new file mode 100755
+index 0000000000..eb345810b9
+--- /dev/null
++++ b/tools/clang/scripts/package.sh
+@@ -0,0 +1,87 @@
++#!/bin/bash
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++# This script will check out llvm and clang, and then package the results up
++# to a tgz file.
++
++THIS_DIR="$(dirname "${0}")"
++LLVM_DIR="${THIS_DIR}/../../../third_party/llvm"
++LLVM_BOOTSTRAP_DIR="${THIS_DIR}/../../../third_party/llvm-bootstrap"
++LLVM_BUILD_DIR="${THIS_DIR}/../../../third_party/llvm-build"
++LLVM_BIN_DIR="${LLVM_BUILD_DIR}/Release+Asserts/bin"
++LLVM_LIB_DIR="${LLVM_BUILD_DIR}/Release+Asserts/lib"
++
++echo "Diff in llvm:" | tee buildlog.txt
++svn stat "${LLVM_DIR}" 2>&1 | tee -a buildlog.txt
++svn diff "${LLVM_DIR}" 2>&1 | tee -a buildlog.txt
++echo "Diff in llvm/tools/clang:" | tee -a buildlog.txt
++svn stat "${LLVM_DIR}/tools/clang" 2>&1 | tee -a buildlog.txt
++svn diff "${LLVM_DIR}/tools/clang" 2>&1 | tee -a buildlog.txt
++echo "Diff in llvm/projects/compiler-rt:" | tee -a buildlog.txt
++svn stat "${LLVM_DIR}/projects/compiler-rt" 2>&1 | tee -a buildlog.txt
++svn diff "${LLVM_DIR}/projects/compiler-rt" 2>&1 | tee -a buildlog.txt
++
++echo "Starting build" | tee -a buildlog.txt
++
++set -ex
++
++# Do a clobber build.
++rm -rf "${LLVM_BOOTSTRAP_DIR}"
++rm -rf "${LLVM_BUILD_DIR}"
++"${THIS_DIR}"/update.sh --run-tests --bootstrap --force-local-build 2>&1 | \
++ tee -a buildlog.txt
++
++R=$("${LLVM_BIN_DIR}/clang" --version | \
++ sed -ne 's/clang version .*(trunk \([0-9]*\))/\1/p')
++
++PDIR=clang-$R
++rm -rf $PDIR
++mkdir $PDIR
++mkdir $PDIR/bin
++mkdir $PDIR/lib
++
++# Copy buildlog over.
++cp buildlog.txt $PDIR/
++
++# Copy clang into pdir, symlink clang++ to it.
++cp "${LLVM_BIN_DIR}/clang" $PDIR/bin/
++(cd $PDIR/bin && ln -sf clang clang++ && cd -)
++
++# Copy plugins. Some of the dylibs are pretty big, so copy only the ones we
++# care about.
++if [ "$(uname -s)" = "Darwin" ]; then
++ cp "${LLVM_LIB_DIR}/libFindBadConstructs.dylib" $PDIR/lib
++else
++ cp "${LLVM_LIB_DIR}/libFindBadConstructs.so" $PDIR/lib
++fi
++
++# Copy built-in headers (lib/clang/3.2/include).
++# libcompiler-rt puts all kinds of libraries there too, but we want only ASan.
++if [ "$(uname -s)" = "Darwin" ]; then
++ # Keep only Release+Asserts/lib/clang/3.2/lib/darwin/libclang_rt.asan_osx.a
++ find "${LLVM_LIB_DIR}/clang" -type f -path '*lib/darwin*' | grep -v asan | \
++ xargs rm
++else
++ # Keep only
++ # Release+Asserts/lib/clang/3.2/lib/linux/libclang_rt.{asan,tsan}-x86_64.a
++ # TODO(thakis): Make sure the 32bit version of ASan runtime is kept too once
++ # that's built. TSan runtime exists only for 64 bits.
++ find "${LLVM_LIB_DIR}/clang" -type f -path '*lib/linux*' | \
++ grep -v "asan\|tsan" | xargs rm
++fi
++
++cp -R "${LLVM_LIB_DIR}/clang" $PDIR/lib
++
++tar zcf $PDIR.tgz -C $PDIR bin lib buildlog.txt
++
++if [ "$(uname -s)" = "Darwin" ]; then
++ PLATFORM=Mac
++else
++ PLATFORM=Linux_x64
++fi
++
++echo To upload, run:
++echo gsutil cp -a public-read $PDIR.tgz \
++ gs://chromium-browser-clang/$PLATFORM/$PDIR.tgz
+diff --git a/tools/clang/scripts/plugin_flags.sh b/tools/clang/scripts/plugin_flags.sh
+new file mode 100755
+index 0000000000..217c5c3bd6
+--- /dev/null
++++ b/tools/clang/scripts/plugin_flags.sh
+@@ -0,0 +1,24 @@
++#!/bin/bash
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++# This script returns the flags that should be used when GYP_DEFINES contains
++# clang_use_chrome_plugins. The flags are stored in a script so that they can
++# be changed on the bots without requiring a master restart.
++
++THIS_ABS_DIR=$(cd $(dirname $0) && echo $PWD)
++CLANG_LIB_PATH=$THIS_ABS_DIR/../../../third_party/llvm-build/Release+Asserts/lib
++
++if uname -s | grep -q Darwin; then
++ LIBSUFFIX=dylib
++else
++ LIBSUFFIX=so
++fi
++
++echo -Xclang -load -Xclang $CLANG_LIB_PATH/libFindBadConstructs.$LIBSUFFIX \
++ -Xclang -add-plugin -Xclang find-bad-constructs \
++ -Xclang -plugin-arg-find-bad-constructs \
++ -Xclang skip-virtuals-in-implementations \
++ -Xclang -plugin-arg-find-bad-constructs \
++ -Xclang check-cc-directory
+diff --git a/tools/clang/scripts/update.py b/tools/clang/scripts/update.py
+new file mode 100755
+index 0000000000..bdc781f715
+--- /dev/null
++++ b/tools/clang/scripts/update.py
+@@ -0,0 +1,34 @@
++#!/usr/bin/env python
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++"""Windows can't run .sh files, so this is a small python wrapper around
++update.sh.
++"""
++
++import os
++import subprocess
++import sys
++
++
++def main():
++ if sys.platform in ['win32', 'cygwin']:
++ return 0
++
++ # This script is called by gclient. gclient opens its hooks subprocesses with
++ # (stdout=subprocess.PIPE, stderr=subprocess.STDOUT) and then does custom
++ # output processing that breaks printing '\r' characters for single-line
++ # updating status messages as printed by curl and wget.
++ # Work around this by setting stderr of the update.sh process to stdin (!):
++ # gclient doesn't redirect stdin, and while stdin itself is read-only, a
++ # dup()ed sys.stdin is writable, try
++ # fd2 = os.dup(sys.stdin.fileno()); os.write(fd2, 'hi')
++ # TODO: Fix gclient instead, http://crbug.com/95350
++ return subprocess.call(
++ [os.path.join(os.path.dirname(__file__), 'update.sh')] + sys.argv[1:],
++ stderr=os.fdopen(os.dup(sys.stdin.fileno())))
++
++
++if __name__ == '__main__':
++ sys.exit(main())
+diff --git a/tools/clang/scripts/update.sh b/tools/clang/scripts/update.sh
+new file mode 100755
+index 0000000000..e9448236c8
+--- /dev/null
++++ b/tools/clang/scripts/update.sh
+@@ -0,0 +1,286 @@
++#!/usr/bin/env bash
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++# This script will check out llvm and clang into third_party/llvm and build it.
++
++# Do NOT CHANGE this if you don't know what you're doing -- see
++# https://code.google.com/p/chromium/wiki/UpdatingClang
++# Reverting problematic clang rolls is safe, though.
++CLANG_REVISION=163674
++
++THIS_DIR="$(dirname "${0}")"
++LLVM_DIR="${THIS_DIR}/../../../third_party/llvm"
++LLVM_BUILD_DIR="${LLVM_DIR}/../llvm-build"
++LLVM_BOOTSTRAP_DIR="${LLVM_DIR}/../llvm-bootstrap"
++CLANG_DIR="${LLVM_DIR}/tools/clang"
++COMPILER_RT_DIR="${LLVM_DIR}/projects/compiler-rt"
++STAMP_FILE="${LLVM_BUILD_DIR}/cr_build_revision"
++
++# ${A:-a} returns $A if it's set, a else.
++LLVM_REPO_URL=${LLVM_URL:-https://llvm.org/svn/llvm-project}
++
++# Die if any command dies.
++set -e
++
++OS="$(uname -s)"
++
++# Parse command line options.
++force_local_build=
++mac_only=
++run_tests=
++bootstrap=
++while [[ $# > 0 ]]; do
++ case $1 in
++ --bootstrap)
++ bootstrap=yes
++ ;;
++ --force-local-build)
++ force_local_build=yes
++ ;;
++ --mac-only)
++ mac_only=yes
++ ;;
++ --run-tests)
++ run_tests=yes
++ ;;
++ --help)
++ echo "usage: $0 [--force-local-build] [--mac-only] [--run-tests] "
++ echo "--bootstrap: First build clang with CC, then with itself."
++ echo "--force-local-build: Don't try to download prebuilt binaries."
++ echo "--mac-only: Do initial download only on Mac systems."
++ echo "--run-tests: Run tests after building. Only for local builds."
++ exit 1
++ ;;
++ esac
++ shift
++done
++
++# --mac-only prevents the initial download on non-mac systems, but if clang has
++# already been downloaded in the past, this script keeps it up to date even if
++# --mac-only is passed in and the system isn't a mac. People who don't like this
++# can just delete their third_party/llvm-build directory.
++if [[ -n "$mac_only" ]] && [[ "${OS}" != "Darwin" ]] &&
++ [[ "$GYP_DEFINES" != *clang=1* ]] && ! [[ -d "${LLVM_BUILD_DIR}" ]]; then
++ exit 0
++fi
++
++# Xcode and clang don't get along when predictive compilation is enabled.
++# http://crbug.com/96315
++if [[ "${OS}" = "Darwin" ]] && xcodebuild -version | grep -q 'Xcode 3.2' ; then
++ XCONF=com.apple.Xcode
++ if [[ "${GYP_GENERATORS}" != "make" ]] && \
++ [ "$(defaults read "${XCONF}" EnablePredictiveCompilation)" != "0" ]; then
++ echo
++ echo " HEARKEN!"
++ echo "You're using Xcode3 and you have 'Predictive Compilation' enabled."
++ echo "This does not work well with clang (http://crbug.com/96315)."
++ echo "Disable it in Preferences->Building (lower right), or run"
++ echo " defaults write ${XCONF} EnablePredictiveCompilation -boolean NO"
++ echo "while Xcode is not running."
++ echo
++ fi
++
++ SUB_VERSION=$(xcodebuild -version | sed -Ene 's/Xcode 3\.2\.([0-9]+)/\1/p')
++ if [[ "${SUB_VERSION}" < 6 ]]; then
++ echo
++ echo " YOUR LD IS BUGGY!"
++ echo "Please upgrade Xcode to at least 3.2.6."
++ echo
++ fi
++fi
++
++
++# Check if there's anything to be done, exit early if not.
++if [[ -f "${STAMP_FILE}" ]]; then
++ PREVIOUSLY_BUILT_REVISON=$(cat "${STAMP_FILE}")
++ if [[ -z "$force_local_build" ]] && \
++ [[ "${PREVIOUSLY_BUILT_REVISON}" = "${CLANG_REVISION}" ]]; then
++ echo "Clang already at ${CLANG_REVISION}"
++ exit 0
++ fi
++fi
++# To always force a new build if someone interrupts their build half way.
++rm -f "${STAMP_FILE}"
++
++# Clobber pch files, since they only work with the compiler version that
++# created them. Also clobber .o files, to make sure everything will be built
++# with the new compiler.
++if [[ "${OS}" = "Darwin" ]]; then
++ XCODEBUILD_DIR="${THIS_DIR}/../../../xcodebuild"
++
++ # Xcode groups .o files by project first, configuration second.
++ if [[ -d "${XCODEBUILD_DIR}" ]]; then
++ echo "Clobbering .o files for Xcode build"
++ find "${XCODEBUILD_DIR}" -name '*.o' -exec rm {} +
++ fi
++fi
++
++if [ -f "${THIS_DIR}/../../../WebKit.gyp" ]; then
++ # We're inside a WebKit checkout.
++ # TODO(thakis): try to unify the directory layout of the xcode- and
++ # make-based builds. http://crbug.com/110455
++ MAKE_DIR="${THIS_DIR}/../../../../../../out"
++else
++ # We're inside a Chromium checkout.
++ MAKE_DIR="${THIS_DIR}/../../../out"
++fi
++
++for CONFIG in Debug Release; do
++ if [[ -d "${MAKE_DIR}/${CONFIG}/obj.target" ||
++ -d "${MAKE_DIR}/${CONFIG}/obj.host" ]]; then
++ echo "Clobbering ${CONFIG} PCH and .o files for make build"
++ if [[ -d "${MAKE_DIR}/${CONFIG}/obj.target" ]]; then
++ find "${MAKE_DIR}/${CONFIG}/obj.target" -name '*.gch' -exec rm {} +
++ find "${MAKE_DIR}/${CONFIG}/obj.target" -name '*.o' -exec rm {} +
++ fi
++ if [[ -d "${MAKE_DIR}/${CONFIG}/obj.host" ]]; then
++ find "${MAKE_DIR}/${CONFIG}/obj.host" -name '*.o' -exec rm {} +
++ fi
++ fi
++
++ # ninja puts its output below ${MAKE_DIR} as well.
++ if [[ -d "${MAKE_DIR}/${CONFIG}/obj" ]]; then
++ echo "Clobbering ${CONFIG} PCH and .o files for ninja build"
++ find "${MAKE_DIR}/${CONFIG}/obj" -name '*.gch' -exec rm {} +
++ find "${MAKE_DIR}/${CONFIG}/obj" -name '*.o' -exec rm {} +
++ find "${MAKE_DIR}/${CONFIG}/obj" -name '*.o.d' -exec rm {} +
++ fi
++
++ if [[ "${OS}" = "Darwin" ]]; then
++ if [[ -d "${XCODEBUILD_DIR}/${CONFIG}/SharedPrecompiledHeaders" ]]; then
++ echo "Clobbering ${CONFIG} PCH files for Xcode build"
++ rm -rf "${XCODEBUILD_DIR}/${CONFIG}/SharedPrecompiledHeaders"
++ fi
++ fi
++done
++
++if [[ -z "$force_local_build" ]]; then
++ # Check if there's a prebuilt binary and if so just fetch that. That's faster,
++ # and goma relies on having matching binary hashes on client and server too.
++ CDS_URL=https://commondatastorage.googleapis.com/chromium-browser-clang
++ CDS_FILE="clang-${CLANG_REVISION}.tgz"
++ CDS_OUT_DIR=$(mktemp -d -t clang_download.XXXXXX)
++ CDS_OUTPUT="${CDS_OUT_DIR}/${CDS_FILE}"
++ if [ "${OS}" = "Linux" ]; then
++ CDS_FULL_URL="${CDS_URL}/Linux_x64/${CDS_FILE}"
++ elif [ "${OS}" = "Darwin" ]; then
++ CDS_FULL_URL="${CDS_URL}/Mac/${CDS_FILE}"
++ fi
++ echo Trying to download prebuilt clang
++ if which curl > /dev/null; then
++ curl -L --fail "${CDS_FULL_URL}" -o "${CDS_OUTPUT}" || \
++ rm -rf "${CDS_OUT_DIR}"
++ elif which wget > /dev/null; then
++ wget "${CDS_FULL_URL}" -O "${CDS_OUTPUT}" || rm -rf "${CDS_OUT_DIR}"
++ else
++ echo "Neither curl nor wget found. Please install one of these."
++ exit 1
++ fi
++ if [ -f "${CDS_OUTPUT}" ]; then
++ rm -rf "${LLVM_BUILD_DIR}/Release+Asserts"
++ mkdir -p "${LLVM_BUILD_DIR}/Release+Asserts"
++ tar -xzf "${CDS_OUTPUT}" -C "${LLVM_BUILD_DIR}/Release+Asserts"
++ echo clang "${CLANG_REVISION}" unpacked
++ echo "${CLANG_REVISION}" > "${STAMP_FILE}"
++ rm -rf "${CDS_OUT_DIR}"
++ exit 0
++ else
++ echo Did not find prebuilt clang at r"${CLANG_REVISION}", building
++ fi
++fi
++
++echo Getting LLVM r"${CLANG_REVISION}" in "${LLVM_DIR}"
++if ! svn co --force "${LLVM_REPO_URL}/llvm/trunk@${CLANG_REVISION}" \
++ "${LLVM_DIR}"; then
++ echo Checkout failed, retrying
++ rm -rf "${LLVM_DIR}"
++ svn co --force "${LLVM_REPO_URL}/llvm/trunk@${CLANG_REVISION}" "${LLVM_DIR}"
++fi
++
++echo Getting clang r"${CLANG_REVISION}" in "${CLANG_DIR}"
++svn co --force "${LLVM_REPO_URL}/cfe/trunk@${CLANG_REVISION}" "${CLANG_DIR}"
++
++echo Getting compiler-rt r"${CLANG_REVISION}" in "${COMPILER_RT_DIR}"
++svn co --force "${LLVM_REPO_URL}/compiler-rt/trunk@${CLANG_REVISION}" \
++ "${COMPILER_RT_DIR}"
++
++# Echo all commands.
++set -x
++
++NUM_JOBS=3
++if [[ "${OS}" = "Linux" ]]; then
++ NUM_JOBS="$(grep -c "^processor" /proc/cpuinfo)"
++elif [ "${OS}" = "Darwin" ]; then
++ NUM_JOBS="$(sysctl -n hw.ncpu)"
++fi
++
++# Build bootstrap clang if requested.
++if [[ -n "${bootstrap}" ]]; then
++ echo "Building bootstrap compiler"
++ mkdir -p "${LLVM_BOOTSTRAP_DIR}"
++ cd "${LLVM_BOOTSTRAP_DIR}"
++ if [[ ! -f ./config.status ]]; then
++ # The bootstrap compiler only needs to be able to build the real compiler,
++ # so it needs no cross-compiler output support. In general, the host
++ # compiler should be as similar to the final compiler as possible, so do
++ # keep --disable-threads & co.
++ ../llvm/configure \
++ --enable-optimized \
++ --enable-targets=host-only \
++ --disable-threads \
++ --disable-pthreads \
++ --without-llvmgcc \
++ --without-llvmgxx
++ MACOSX_DEPLOYMENT_TARGET=10.5 make -j"${NUM_JOBS}"
++ fi
++ if [[ -n "${run_tests}" ]]; then
++ make check-all
++ fi
++ cd -
++ export CC="${PWD}/${LLVM_BOOTSTRAP_DIR}/Release+Asserts/bin/clang"
++ export CXX="${PWD}/${LLVM_BOOTSTRAP_DIR}/Release+Asserts/bin/clang++"
++ echo "Building final compiler"
++fi
++
++# Build clang (in a separate directory).
++# The clang bots have this path hardcoded in built/scripts/slave/compile.py,
++# so if you change it you also need to change these links.
++mkdir -p "${LLVM_BUILD_DIR}"
++cd "${LLVM_BUILD_DIR}"
++if [[ ! -f ./config.status ]]; then
++ ../llvm/configure \
++ --enable-optimized \
++ --disable-threads \
++ --disable-pthreads \
++ --without-llvmgcc \
++ --without-llvmgxx
++fi
++
++MACOSX_DEPLOYMENT_TARGET=10.5 make -j"${NUM_JOBS}"
++cd -
++
++# Build plugin.
++# Copy it into the clang tree and use clang's build system to compile the
++# plugin.
++PLUGIN_SRC_DIR="${THIS_DIR}/../plugins"
++PLUGIN_DST_DIR="${LLVM_DIR}/tools/clang/tools/chrome-plugin"
++PLUGIN_BUILD_DIR="${LLVM_BUILD_DIR}/tools/clang/tools/chrome-plugin"
++rm -rf "${PLUGIN_DST_DIR}"
++cp -R "${PLUGIN_SRC_DIR}" "${PLUGIN_DST_DIR}"
++rm -rf "${PLUGIN_BUILD_DIR}"
++mkdir -p "${PLUGIN_BUILD_DIR}"
++cp "${PLUGIN_SRC_DIR}/Makefile" "${PLUGIN_BUILD_DIR}"
++MACOSX_DEPLOYMENT_TARGET=10.5 make -j"${NUM_JOBS}" -C "${PLUGIN_BUILD_DIR}"
++
++if [[ -n "$run_tests" ]]; then
++ # Run a few tests.
++ "${PLUGIN_SRC_DIR}/tests/test.sh" "${LLVM_BUILD_DIR}/Release+Asserts"
++ cd "${LLVM_BUILD_DIR}"
++ make check-all
++ cd -
++fi
++
++# After everything is done, log success for this revision.
++echo "${CLANG_REVISION}" > "${STAMP_FILE}"
diff --git a/third_party/libwebrtc/moz-patch-stack/0098.patch b/third_party/libwebrtc/moz-patch-stack/0098.patch
index 56c8dca72b..1faafdf8cf 100644
--- a/third_party/libwebrtc/moz-patch-stack/0098.patch
+++ b/third_party/libwebrtc/moz-patch-stack/0098.patch
@@ -1,2186 +1,35362 @@
-From: Dan Minor <dminor@mozilla.com>
-Date: Thu, 24 Sep 2020 18:28:00 +0000
-Subject: Bug 1665166 - Move media/webrtc/trunk/* to third-party/libwebrtc;
- r=ng
+From: Nico Grunbaum <na-g@nostrum.com>
+Date: Fri, 30 Apr 2021 21:51:00 +0000
+Subject: Bug 1654112 - Add grit dep for building webrtc on android; r=mjf
-Differential Revision: https://phabricator.services.mozilla.com/D91317
-Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/57e3c54bd7b9a0203e19ff1df272d24bb551ed29
+Differential Revision: https://phabricator.services.mozilla.com/D114027
+Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/3cce5e6938f0df87bd9ab12a5f556aceb93dfa1d
---
- tools/clang/OWNERS | 2 +
- tools/clang/plugins/ChromeClassTester.cpp | 294 ++++++++++++
- tools/clang/plugins/ChromeClassTester.h | 84 ++++
- tools/clang/plugins/FindBadConstructs.cpp | 435 ++++++++++++++++++
- tools/clang/plugins/Makefile | 19 +
- tools/clang/plugins/OWNERS | 1 +
- tools/clang/plugins/README.chromium | 4 +
- tools/clang/plugins/tests/base_refcounted.cpp | 72 +++
- tools/clang/plugins/tests/base_refcounted.h | 121 +++++
- tools/clang/plugins/tests/base_refcounted.txt | 23 +
- .../clang/plugins/tests/inline_copy_ctor.cpp | 5 +
- tools/clang/plugins/tests/inline_copy_ctor.h | 12 +
- .../clang/plugins/tests/inline_copy_ctor.txt | 5 +
- tools/clang/plugins/tests/inline_ctor.cpp | 25 +
- tools/clang/plugins/tests/inline_ctor.h | 21 +
- tools/clang/plugins/tests/inline_ctor.txt | 8 +
- tools/clang/plugins/tests/missing_ctor.cpp | 23 +
- tools/clang/plugins/tests/missing_ctor.h | 19 +
- tools/clang/plugins/tests/missing_ctor.txt | 6 +
- .../tests/nested_class_inline_ctor.cpp | 5 +
- .../plugins/tests/nested_class_inline_ctor.h | 22 +
- .../tests/nested_class_inline_ctor.txt | 8 +
- .../plugins/tests/overridden_methods.cpp | 38 ++
- .../clang/plugins/tests/overridden_methods.h | 54 +++
- .../plugins/tests/overridden_methods.txt | 20 +
- tools/clang/plugins/tests/test.sh | 72 +++
- tools/clang/plugins/tests/virtual_methods.cpp | 36 ++
- tools/clang/plugins/tests/virtual_methods.h | 39 ++
- tools/clang/plugins/tests/virtual_methods.txt | 8 +
- tools/clang/scripts/package.sh | 87 ++++
- tools/clang/scripts/plugin_flags.sh | 24 +
- tools/clang/scripts/update.py | 34 ++
- tools/clang/scripts/update.sh | 286 ++++++++++++
- 33 files changed, 1912 insertions(+)
- create mode 100644 tools/clang/OWNERS
- create mode 100644 tools/clang/plugins/ChromeClassTester.cpp
- create mode 100644 tools/clang/plugins/ChromeClassTester.h
- create mode 100644 tools/clang/plugins/FindBadConstructs.cpp
- create mode 100644 tools/clang/plugins/Makefile
- create mode 100644 tools/clang/plugins/OWNERS
- create mode 100644 tools/clang/plugins/README.chromium
- create mode 100644 tools/clang/plugins/tests/base_refcounted.cpp
- create mode 100644 tools/clang/plugins/tests/base_refcounted.h
- create mode 100644 tools/clang/plugins/tests/base_refcounted.txt
- create mode 100644 tools/clang/plugins/tests/inline_copy_ctor.cpp
- create mode 100644 tools/clang/plugins/tests/inline_copy_ctor.h
- create mode 100644 tools/clang/plugins/tests/inline_copy_ctor.txt
- create mode 100644 tools/clang/plugins/tests/inline_ctor.cpp
- create mode 100644 tools/clang/plugins/tests/inline_ctor.h
- create mode 100644 tools/clang/plugins/tests/inline_ctor.txt
- create mode 100644 tools/clang/plugins/tests/missing_ctor.cpp
- create mode 100644 tools/clang/plugins/tests/missing_ctor.h
- create mode 100644 tools/clang/plugins/tests/missing_ctor.txt
- create mode 100644 tools/clang/plugins/tests/nested_class_inline_ctor.cpp
- create mode 100644 tools/clang/plugins/tests/nested_class_inline_ctor.h
- create mode 100644 tools/clang/plugins/tests/nested_class_inline_ctor.txt
- create mode 100644 tools/clang/plugins/tests/overridden_methods.cpp
- create mode 100644 tools/clang/plugins/tests/overridden_methods.h
- create mode 100644 tools/clang/plugins/tests/overridden_methods.txt
- create mode 100755 tools/clang/plugins/tests/test.sh
- create mode 100644 tools/clang/plugins/tests/virtual_methods.cpp
- create mode 100644 tools/clang/plugins/tests/virtual_methods.h
- create mode 100644 tools/clang/plugins/tests/virtual_methods.txt
- create mode 100755 tools/clang/scripts/package.sh
- create mode 100755 tools/clang/scripts/plugin_flags.sh
- create mode 100755 tools/clang/scripts/update.py
- create mode 100755 tools/clang/scripts/update.sh
+ tools/grit/.gitignore | 1 +
+ tools/grit/BUILD.gn | 48 +
+ tools/grit/MANIFEST.in | 3 +
+ tools/grit/OWNERS | 8 +
+ tools/grit/PRESUBMIT.py | 22 +
+ tools/grit/README.md | 19 +
+ tools/grit/grit.py | 31 +
+ tools/grit/grit/__init__.py | 19 +
+ tools/grit/grit/clique.py | 491 +++
+ tools/grit/grit/clique_unittest.py | 265 ++
+ tools/grit/grit/constants.py | 23 +
+ tools/grit/grit/exception.py | 139 +
+ tools/grit/grit/extern/BogoFP.py | 22 +
+ tools/grit/grit/extern/FP.py | 72 +
+ tools/grit/grit/extern/__init__.py | 0
+ tools/grit/grit/extern/tclib.py | 503 +++
+ tools/grit/grit/format/__init__.py | 8 +
+ tools/grit/grit/format/android_xml.py | 212 ++
+ .../grit/grit/format/android_xml_unittest.py | 149 +
+ tools/grit/grit/format/c_format.py | 95 +
+ tools/grit/grit/format/c_format_unittest.py | 81 +
+ .../grit/grit/format/chrome_messages_json.py | 59 +
+ .../format/chrome_messages_json_unittest.py | 190 +
+ tools/grit/grit/format/data_pack.py | 321 ++
+ tools/grit/grit/format/data_pack_unittest.py | 102 +
+ .../grit/grit/format/gen_predetermined_ids.py | 144 +
+ .../format/gen_predetermined_ids_unittest.py | 46 +
+ tools/grit/grit/format/gzip_string.py | 46 +
+ .../grit/grit/format/gzip_string_unittest.py | 65 +
+ tools/grit/grit/format/html_inline.py | 602 ++++
+ .../grit/grit/format/html_inline_unittest.py | 927 +++++
+ tools/grit/grit/format/minifier.py | 45 +
+ .../grit/grit/format/policy_templates_json.py | 26 +
+ .../format/policy_templates_json_unittest.py | 207 ++
+ tools/grit/grit/format/rc.py | 474 +++
+ tools/grit/grit/format/rc_header.py | 48 +
+ tools/grit/grit/format/rc_header_unittest.py | 138 +
+ tools/grit/grit/format/rc_unittest.py | 415 +++
+ tools/grit/grit/format/resource_map.py | 159 +
+ .../grit/grit/format/resource_map_unittest.py | 345 ++
+ tools/grit/grit/gather/__init__.py | 8 +
+ tools/grit/grit/gather/admin_template.py | 62 +
+ .../grit/gather/admin_template_unittest.py | 115 +
+ tools/grit/grit/gather/chrome_html.py | 377 ++
+ .../grit/grit/gather/chrome_html_unittest.py | 610 ++++
+ tools/grit/grit/gather/chrome_scaled_image.py | 157 +
+ .../gather/chrome_scaled_image_unittest.py | 209 ++
+ tools/grit/grit/gather/interface.py | 172 +
+ tools/grit/grit/gather/json_loader.py | 27 +
+ tools/grit/grit/gather/policy_json.py | 325 ++
+ .../grit/grit/gather/policy_json_unittest.py | 347 ++
+ tools/grit/grit/gather/rc.py | 343 ++
+ tools/grit/grit/gather/rc_unittest.py | 372 ++
+ tools/grit/grit/gather/regexp.py | 82 +
+ tools/grit/grit/gather/skeleton_gatherer.py | 149 +
+ tools/grit/grit/gather/tr_html.py | 743 ++++
+ tools/grit/grit/gather/tr_html_unittest.py | 524 +++
+ tools/grit/grit/gather/txt.py | 38 +
+ tools/grit/grit/gather/txt_unittest.py | 35 +
+ tools/grit/grit/grd_reader.py | 238 ++
+ tools/grit/grit/grd_reader_unittest.py | 346 ++
+ tools/grit/grit/grit-todo.xml | 62 +
+ tools/grit/grit/grit_runner.py | 334 ++
+ tools/grit/grit/grit_runner_unittest.py | 42 +
+ tools/grit/grit/lazy_re.py | 46 +
+ tools/grit/grit/lazy_re_unittest.py | 40 +
+ tools/grit/grit/node/__init__.py | 8 +
+ tools/grit/grit/node/base.py | 670 ++++
+ tools/grit/grit/node/base_unittest.py | 259 ++
+ tools/grit/grit/node/brotli_util.py | 29 +
+ tools/grit/grit/node/custom/__init__.py | 8 +
+ tools/grit/grit/node/custom/filename.py | 29 +
+ .../grit/node/custom/filename_unittest.py | 34 +
+ tools/grit/grit/node/empty.py | 64 +
+ tools/grit/grit/node/include.py | 170 +
+ tools/grit/grit/node/include_unittest.py | 134 +
+ tools/grit/grit/node/mapping.py | 60 +
+ tools/grit/grit/node/message.py | 362 ++
+ tools/grit/grit/node/message_unittest.py | 380 ++
+ tools/grit/grit/node/misc.py | 707 ++++
+ tools/grit/grit/node/misc_unittest.py | 590 ++++
+ tools/grit/grit/node/mock_brotli.py | 10 +
+ tools/grit/grit/node/node_io.py | 117 +
+ tools/grit/grit/node/node_io_unittest.py | 182 +
+ tools/grit/grit/node/structure.py | 375 ++
+ tools/grit/grit/node/structure_unittest.py | 178 +
+ tools/grit/grit/node/variant.py | 41 +
+ tools/grit/grit/pseudo.py | 129 +
+ tools/grit/grit/pseudo_rtl.py | 104 +
+ tools/grit/grit/pseudo_unittest.py | 55 +
+ tools/grit/grit/shortcuts.py | 93 +
+ tools/grit/grit/shortcuts_unittest.py | 79 +
+ tools/grit/grit/tclib.py | 246 ++
+ tools/grit/grit/tclib_unittest.py | 180 +
+ tools/grit/grit/test_suite_all.py | 34 +
+ tools/grit/grit/testdata/GoogleDesktop.adm | 945 +++++
+ tools/grit/grit/testdata/README.txt | 87 +
+ tools/grit/grit/testdata/about.html | 45 +
+ tools/grit/grit/testdata/android.xml | 24 +
+ tools/grit/grit/testdata/bad_browser.html | 16 +
+ tools/grit/grit/testdata/browser.html | 42 +
+ tools/grit/grit/testdata/buildinfo.grd | 46 +
+ tools/grit/grit/testdata/cache_prefix.html | 24 +
+ .../grit/grit/testdata/cache_prefix_file.html | 25 +
+ tools/grit/grit/testdata/chat_result.html | 24 +
+ .../chrome/app/generated_resources.grd | 199 ++
+ tools/grit/grit/testdata/chrome_html.html | 6 +
+ .../grit/testdata/default_100_percent/a.png | Bin 0 -> 159 bytes
+ .../grit/testdata/default_100_percent/b.png | 1 +
+ tools/grit/grit/testdata/del_footer.html | 8 +
+ tools/grit/grit/testdata/del_header.html | 60 +
+ tools/grit/grit/testdata/deleted.html | 21 +
+ tools/grit/grit/testdata/depfile.grd | 18 +
+ tools/grit/grit/testdata/details.html | 10 +
+ .../grit/testdata/duplicate-name-input.xml | 26 +
+ tools/grit/grit/testdata/email_result.html | 34 +
+ tools/grit/grit/testdata/email_thread.html | 10 +
+ tools/grit/grit/testdata/error.html | 8 +
+ tools/grit/grit/testdata/explicit_web.html | 11 +
+ tools/grit/grit/testdata/footer.html | 14 +
+ .../grit/testdata/generated_resources_fr.xtb | 3079 +++++++++++++++++
+ .../grit/testdata/generated_resources_iw.xtb | 4 +
+ .../grit/testdata/generated_resources_no.xtb | 4 +
+ tools/grit/grit/testdata/grit_part.grdp | 5 +
+ tools/grit/grit/testdata/header.html | 39 +
+ tools/grit/grit/testdata/homepage.html | 37 +
+ tools/grit/grit/testdata/hover.html | 177 +
+ tools/grit/grit/testdata/include_test.html | 31 +
+ tools/grit/grit/testdata/included_sample.html | 1 +
+ tools/grit/grit/testdata/indexing_speed.html | 58 +
+ tools/grit/grit/testdata/install_prefs.html | 92 +
+ tools/grit/grit/testdata/install_prefs2.html | 52 +
+ .../grit/testdata/klonk-alternate-skeleton.rc | Bin 0 -> 1088 bytes
+ tools/grit/grit/testdata/klonk.ico | Bin 0 -> 766 bytes
+ tools/grit/grit/testdata/klonk.rc | Bin 0 -> 9824 bytes
+ .../grit/grit/testdata/ko_oem_enable_bug.html | 1 +
+ .../grit/testdata/ko_oem_non_admin_bug.html | 1 +
+ tools/grit/grit/testdata/mini.html | 36 +
+ tools/grit/grit/testdata/oem_enable.html | 106 +
+ tools/grit/grit/testdata/oem_non_admin.html | 39 +
+ tools/grit/grit/testdata/onebox.html | 21 +
+ tools/grit/grit/testdata/oneclick.html | 34 +
+ tools/grit/grit/testdata/password.html | 37 +
+ tools/grit/grit/testdata/preferences.html | 234 ++
+ tools/grit/grit/testdata/preprocess_test.html | 7 +
+ tools/grit/grit/testdata/privacy.html | 35 +
+ tools/grit/grit/testdata/quit_apps.html | 49 +
+ tools/grit/grit/testdata/recrawl.html | 30 +
+ tools/grit/grit/testdata/resource_ids | 13 +
+ tools/grit/grit/testdata/script.html | 38 +
+ tools/grit/grit/testdata/searchbox.html | 22 +
+ tools/grit/grit/testdata/sidebar_h.html | 82 +
+ tools/grit/grit/testdata/sidebar_v.html | 267 ++
+ tools/grit/grit/testdata/simple-input.xml | 52 +
+ tools/grit/grit/testdata/simple.html | 3 +
+ tools/grit/grit/testdata/source.rc | 57 +
+ .../grit/testdata/special_100_percent/a.png | Bin 0 -> 159 bytes
+ tools/grit/grit/testdata/status.html | 44 +
+ .../grit/testdata/structure_variables.html | 4 +
+ tools/grit/grit/testdata/substitute.grd | 31 +
+ tools/grit/grit/testdata/substitute.xmb | 10 +
+ .../grit/grit/testdata/substitute_no_ids.grd | 31 +
+ tools/grit/grit/testdata/substitute_tmpl.grd | 31 +
+ tools/grit/grit/testdata/test_css.css | 1 +
+ tools/grit/grit/testdata/test_html.html | 1 +
+ tools/grit/grit/testdata/test_js.js | 1 +
+ tools/grit/grit/testdata/test_svg.svg | 1 +
+ tools/grit/grit/testdata/test_text.txt | 1 +
+ tools/grit/grit/testdata/time_related.html | 11 +
+ tools/grit/grit/testdata/toolbar_about.html | 138 +
+ .../grit/testdata/tools/grit/resource_ids | 176 +
+ tools/grit/grit/testdata/transl.rc | 56 +
+ tools/grit/grit/testdata/versions.html | 7 +
+ tools/grit/grit/testdata/whitelist.txt | 4 +
+ .../grit/testdata/whitelist_resources.grd | 54 +
+ .../grit/grit/testdata/whitelist_strings.grd | 23 +
+ tools/grit/grit/tool/__init__.py | 8 +
+ tools/grit/grit/tool/android2grd.py | 484 +++
+ tools/grit/grit/tool/android2grd_unittest.py | 181 +
+ tools/grit/grit/tool/build.py | 556 +++
+ tools/grit/grit/tool/build_unittest.py | 341 ++
+ tools/grit/grit/tool/buildinfo.py | 78 +
+ tools/grit/grit/tool/buildinfo_unittest.py | 90 +
+ tools/grit/grit/tool/count.py | 52 +
+ tools/grit/grit/tool/diff_structures.py | 119 +
+ .../grit/tool/diff_structures_unittest.py | 46 +
+ tools/grit/grit/tool/interface.py | 62 +
+ tools/grit/grit/tool/menu_from_parts.py | 79 +
+ tools/grit/grit/tool/newgrd.py | 85 +
+ tools/grit/grit/tool/newgrd_unittest.py | 51 +
+ tools/grit/grit/tool/postprocess_interface.py | 29 +
+ tools/grit/grit/tool/postprocess_unittest.py | 64 +
+ tools/grit/grit/tool/preprocess_interface.py | 25 +
+ tools/grit/grit/tool/preprocess_unittest.py | 50 +
+ tools/grit/grit/tool/rc2grd.py | 418 +++
+ tools/grit/grit/tool/rc2grd_unittest.py | 163 +
+ tools/grit/grit/tool/resize.py | 295 ++
+ tools/grit/grit/tool/test.py | 24 +
+ tools/grit/grit/tool/transl2tc.py | 251 ++
+ tools/grit/grit/tool/transl2tc_unittest.py | 133 +
+ tools/grit/grit/tool/unit.py | 43 +
+ .../grit/tool/update_resource_ids/__init__.py | 305 ++
+ .../grit/tool/update_resource_ids/assigner.py | 286 ++
+ .../update_resource_ids/assigner_unittest.py | 154 +
+ .../grit/tool/update_resource_ids/common.py | 101 +
+ .../grit/tool/update_resource_ids/parser.py | 231 ++
+ .../grit/tool/update_resource_ids/reader.py | 83 +
+ tools/grit/grit/tool/xmb.py | 295 ++
+ tools/grit/grit/tool/xmb_unittest.py | 132 +
+ tools/grit/grit/util.py | 691 ++++
+ tools/grit/grit/util_unittest.py | 118 +
+ tools/grit/grit/xtb_reader.py | 140 +
+ tools/grit/grit/xtb_reader_unittest.py | 110 +
+ tools/grit/grit_info.py | 173 +
+ tools/grit/grit_rule.gni | 485 +++
+ tools/grit/minify_with_uglify.py | 44 +
+ tools/grit/minimize_css.py | 105 +
+ tools/grit/minimize_css_unittest.py | 58 +
+ tools/grit/pak_util.py | 223 ++
+ tools/grit/repack.gni | 189 +
+ tools/grit/setup.py | 46 +
+ tools/grit/stamp_grit_sources.py | 57 +
+ tools/grit/third_party/six/LICENSE | 18 +
+ tools/grit/third_party/six/README | 16 +
+ tools/grit/third_party/six/README.chromium | 13 +
+ tools/grit/third_party/six/__init__.py | 868 +++++
+ 226 files changed, 33440 insertions(+)
+ create mode 100644 tools/grit/.gitignore
+ create mode 100644 tools/grit/BUILD.gn
+ create mode 100644 tools/grit/MANIFEST.in
+ create mode 100644 tools/grit/OWNERS
+ create mode 100644 tools/grit/PRESUBMIT.py
+ create mode 100644 tools/grit/README.md
+ create mode 100644 tools/grit/grit.py
+ create mode 100644 tools/grit/grit/__init__.py
+ create mode 100644 tools/grit/grit/clique.py
+ create mode 100644 tools/grit/grit/clique_unittest.py
+ create mode 100644 tools/grit/grit/constants.py
+ create mode 100644 tools/grit/grit/exception.py
+ create mode 100644 tools/grit/grit/extern/BogoFP.py
+ create mode 100644 tools/grit/grit/extern/FP.py
+ create mode 100644 tools/grit/grit/extern/__init__.py
+ create mode 100644 tools/grit/grit/extern/tclib.py
+ create mode 100644 tools/grit/grit/format/__init__.py
+ create mode 100644 tools/grit/grit/format/android_xml.py
+ create mode 100644 tools/grit/grit/format/android_xml_unittest.py
+ create mode 100644 tools/grit/grit/format/c_format.py
+ create mode 100644 tools/grit/grit/format/c_format_unittest.py
+ create mode 100644 tools/grit/grit/format/chrome_messages_json.py
+ create mode 100644 tools/grit/grit/format/chrome_messages_json_unittest.py
+ create mode 100644 tools/grit/grit/format/data_pack.py
+ create mode 100644 tools/grit/grit/format/data_pack_unittest.py
+ create mode 100644 tools/grit/grit/format/gen_predetermined_ids.py
+ create mode 100644 tools/grit/grit/format/gen_predetermined_ids_unittest.py
+ create mode 100644 tools/grit/grit/format/gzip_string.py
+ create mode 100644 tools/grit/grit/format/gzip_string_unittest.py
+ create mode 100644 tools/grit/grit/format/html_inline.py
+ create mode 100644 tools/grit/grit/format/html_inline_unittest.py
+ create mode 100644 tools/grit/grit/format/minifier.py
+ create mode 100644 tools/grit/grit/format/policy_templates_json.py
+ create mode 100644 tools/grit/grit/format/policy_templates_json_unittest.py
+ create mode 100644 tools/grit/grit/format/rc.py
+ create mode 100644 tools/grit/grit/format/rc_header.py
+ create mode 100644 tools/grit/grit/format/rc_header_unittest.py
+ create mode 100644 tools/grit/grit/format/rc_unittest.py
+ create mode 100644 tools/grit/grit/format/resource_map.py
+ create mode 100644 tools/grit/grit/format/resource_map_unittest.py
+ create mode 100644 tools/grit/grit/gather/__init__.py
+ create mode 100644 tools/grit/grit/gather/admin_template.py
+ create mode 100644 tools/grit/grit/gather/admin_template_unittest.py
+ create mode 100644 tools/grit/grit/gather/chrome_html.py
+ create mode 100644 tools/grit/grit/gather/chrome_html_unittest.py
+ create mode 100644 tools/grit/grit/gather/chrome_scaled_image.py
+ create mode 100644 tools/grit/grit/gather/chrome_scaled_image_unittest.py
+ create mode 100644 tools/grit/grit/gather/interface.py
+ create mode 100644 tools/grit/grit/gather/json_loader.py
+ create mode 100644 tools/grit/grit/gather/policy_json.py
+ create mode 100644 tools/grit/grit/gather/policy_json_unittest.py
+ create mode 100644 tools/grit/grit/gather/rc.py
+ create mode 100644 tools/grit/grit/gather/rc_unittest.py
+ create mode 100644 tools/grit/grit/gather/regexp.py
+ create mode 100644 tools/grit/grit/gather/skeleton_gatherer.py
+ create mode 100644 tools/grit/grit/gather/tr_html.py
+ create mode 100644 tools/grit/grit/gather/tr_html_unittest.py
+ create mode 100644 tools/grit/grit/gather/txt.py
+ create mode 100644 tools/grit/grit/gather/txt_unittest.py
+ create mode 100644 tools/grit/grit/grd_reader.py
+ create mode 100644 tools/grit/grit/grd_reader_unittest.py
+ create mode 100644 tools/grit/grit/grit-todo.xml
+ create mode 100644 tools/grit/grit/grit_runner.py
+ create mode 100644 tools/grit/grit/grit_runner_unittest.py
+ create mode 100644 tools/grit/grit/lazy_re.py
+ create mode 100644 tools/grit/grit/lazy_re_unittest.py
+ create mode 100644 tools/grit/grit/node/__init__.py
+ create mode 100644 tools/grit/grit/node/base.py
+ create mode 100644 tools/grit/grit/node/base_unittest.py
+ create mode 100644 tools/grit/grit/node/brotli_util.py
+ create mode 100644 tools/grit/grit/node/custom/__init__.py
+ create mode 100644 tools/grit/grit/node/custom/filename.py
+ create mode 100644 tools/grit/grit/node/custom/filename_unittest.py
+ create mode 100644 tools/grit/grit/node/empty.py
+ create mode 100644 tools/grit/grit/node/include.py
+ create mode 100644 tools/grit/grit/node/include_unittest.py
+ create mode 100644 tools/grit/grit/node/mapping.py
+ create mode 100644 tools/grit/grit/node/message.py
+ create mode 100644 tools/grit/grit/node/message_unittest.py
+ create mode 100644 tools/grit/grit/node/misc.py
+ create mode 100644 tools/grit/grit/node/misc_unittest.py
+ create mode 100644 tools/grit/grit/node/mock_brotli.py
+ create mode 100644 tools/grit/grit/node/node_io.py
+ create mode 100644 tools/grit/grit/node/node_io_unittest.py
+ create mode 100644 tools/grit/grit/node/structure.py
+ create mode 100644 tools/grit/grit/node/structure_unittest.py
+ create mode 100644 tools/grit/grit/node/variant.py
+ create mode 100644 tools/grit/grit/pseudo.py
+ create mode 100644 tools/grit/grit/pseudo_rtl.py
+ create mode 100644 tools/grit/grit/pseudo_unittest.py
+ create mode 100644 tools/grit/grit/shortcuts.py
+ create mode 100644 tools/grit/grit/shortcuts_unittest.py
+ create mode 100644 tools/grit/grit/tclib.py
+ create mode 100644 tools/grit/grit/tclib_unittest.py
+ create mode 100644 tools/grit/grit/test_suite_all.py
+ create mode 100644 tools/grit/grit/testdata/GoogleDesktop.adm
+ create mode 100644 tools/grit/grit/testdata/README.txt
+ create mode 100644 tools/grit/grit/testdata/about.html
+ create mode 100644 tools/grit/grit/testdata/android.xml
+ create mode 100644 tools/grit/grit/testdata/bad_browser.html
+ create mode 100644 tools/grit/grit/testdata/browser.html
+ create mode 100644 tools/grit/grit/testdata/buildinfo.grd
+ create mode 100644 tools/grit/grit/testdata/cache_prefix.html
+ create mode 100644 tools/grit/grit/testdata/cache_prefix_file.html
+ create mode 100644 tools/grit/grit/testdata/chat_result.html
+ create mode 100644 tools/grit/grit/testdata/chrome/app/generated_resources.grd
+ create mode 100644 tools/grit/grit/testdata/chrome_html.html
+ create mode 100644 tools/grit/grit/testdata/default_100_percent/a.png
+ create mode 100644 tools/grit/grit/testdata/default_100_percent/b.png
+ create mode 100644 tools/grit/grit/testdata/del_footer.html
+ create mode 100644 tools/grit/grit/testdata/del_header.html
+ create mode 100644 tools/grit/grit/testdata/deleted.html
+ create mode 100644 tools/grit/grit/testdata/depfile.grd
+ create mode 100644 tools/grit/grit/testdata/details.html
+ create mode 100644 tools/grit/grit/testdata/duplicate-name-input.xml
+ create mode 100644 tools/grit/grit/testdata/email_result.html
+ create mode 100644 tools/grit/grit/testdata/email_thread.html
+ create mode 100644 tools/grit/grit/testdata/error.html
+ create mode 100644 tools/grit/grit/testdata/explicit_web.html
+ create mode 100644 tools/grit/grit/testdata/footer.html
+ create mode 100644 tools/grit/grit/testdata/generated_resources_fr.xtb
+ create mode 100644 tools/grit/grit/testdata/generated_resources_iw.xtb
+ create mode 100644 tools/grit/grit/testdata/generated_resources_no.xtb
+ create mode 100644 tools/grit/grit/testdata/grit_part.grdp
+ create mode 100644 tools/grit/grit/testdata/header.html
+ create mode 100644 tools/grit/grit/testdata/homepage.html
+ create mode 100644 tools/grit/grit/testdata/hover.html
+ create mode 100644 tools/grit/grit/testdata/include_test.html
+ create mode 100644 tools/grit/grit/testdata/included_sample.html
+ create mode 100644 tools/grit/grit/testdata/indexing_speed.html
+ create mode 100644 tools/grit/grit/testdata/install_prefs.html
+ create mode 100644 tools/grit/grit/testdata/install_prefs2.html
+ create mode 100644 tools/grit/grit/testdata/klonk-alternate-skeleton.rc
+ create mode 100644 tools/grit/grit/testdata/klonk.ico
+ create mode 100644 tools/grit/grit/testdata/klonk.rc
+ create mode 100644 tools/grit/grit/testdata/ko_oem_enable_bug.html
+ create mode 100644 tools/grit/grit/testdata/ko_oem_non_admin_bug.html
+ create mode 100644 tools/grit/grit/testdata/mini.html
+ create mode 100644 tools/grit/grit/testdata/oem_enable.html
+ create mode 100644 tools/grit/grit/testdata/oem_non_admin.html
+ create mode 100644 tools/grit/grit/testdata/onebox.html
+ create mode 100644 tools/grit/grit/testdata/oneclick.html
+ create mode 100644 tools/grit/grit/testdata/password.html
+ create mode 100644 tools/grit/grit/testdata/preferences.html
+ create mode 100644 tools/grit/grit/testdata/preprocess_test.html
+ create mode 100644 tools/grit/grit/testdata/privacy.html
+ create mode 100644 tools/grit/grit/testdata/quit_apps.html
+ create mode 100644 tools/grit/grit/testdata/recrawl.html
+ create mode 100644 tools/grit/grit/testdata/resource_ids
+ create mode 100644 tools/grit/grit/testdata/script.html
+ create mode 100644 tools/grit/grit/testdata/searchbox.html
+ create mode 100644 tools/grit/grit/testdata/sidebar_h.html
+ create mode 100644 tools/grit/grit/testdata/sidebar_v.html
+ create mode 100644 tools/grit/grit/testdata/simple-input.xml
+ create mode 100644 tools/grit/grit/testdata/simple.html
+ create mode 100644 tools/grit/grit/testdata/source.rc
+ create mode 100644 tools/grit/grit/testdata/special_100_percent/a.png
+ create mode 100644 tools/grit/grit/testdata/status.html
+ create mode 100644 tools/grit/grit/testdata/structure_variables.html
+ create mode 100644 tools/grit/grit/testdata/substitute.grd
+ create mode 100644 tools/grit/grit/testdata/substitute.xmb
+ create mode 100644 tools/grit/grit/testdata/substitute_no_ids.grd
+ create mode 100644 tools/grit/grit/testdata/substitute_tmpl.grd
+ create mode 100644 tools/grit/grit/testdata/test_css.css
+ create mode 100644 tools/grit/grit/testdata/test_html.html
+ create mode 100644 tools/grit/grit/testdata/test_js.js
+ create mode 100644 tools/grit/grit/testdata/test_svg.svg
+ create mode 100644 tools/grit/grit/testdata/test_text.txt
+ create mode 100644 tools/grit/grit/testdata/time_related.html
+ create mode 100644 tools/grit/grit/testdata/toolbar_about.html
+ create mode 100644 tools/grit/grit/testdata/tools/grit/resource_ids
+ create mode 100644 tools/grit/grit/testdata/transl.rc
+ create mode 100644 tools/grit/grit/testdata/versions.html
+ create mode 100644 tools/grit/grit/testdata/whitelist.txt
+ create mode 100644 tools/grit/grit/testdata/whitelist_resources.grd
+ create mode 100644 tools/grit/grit/testdata/whitelist_strings.grd
+ create mode 100644 tools/grit/grit/tool/__init__.py
+ create mode 100644 tools/grit/grit/tool/android2grd.py
+ create mode 100644 tools/grit/grit/tool/android2grd_unittest.py
+ create mode 100644 tools/grit/grit/tool/build.py
+ create mode 100644 tools/grit/grit/tool/build_unittest.py
+ create mode 100644 tools/grit/grit/tool/buildinfo.py
+ create mode 100644 tools/grit/grit/tool/buildinfo_unittest.py
+ create mode 100644 tools/grit/grit/tool/count.py
+ create mode 100644 tools/grit/grit/tool/diff_structures.py
+ create mode 100644 tools/grit/grit/tool/diff_structures_unittest.py
+ create mode 100644 tools/grit/grit/tool/interface.py
+ create mode 100644 tools/grit/grit/tool/menu_from_parts.py
+ create mode 100644 tools/grit/grit/tool/newgrd.py
+ create mode 100644 tools/grit/grit/tool/newgrd_unittest.py
+ create mode 100644 tools/grit/grit/tool/postprocess_interface.py
+ create mode 100644 tools/grit/grit/tool/postprocess_unittest.py
+ create mode 100644 tools/grit/grit/tool/preprocess_interface.py
+ create mode 100644 tools/grit/grit/tool/preprocess_unittest.py
+ create mode 100644 tools/grit/grit/tool/rc2grd.py
+ create mode 100644 tools/grit/grit/tool/rc2grd_unittest.py
+ create mode 100644 tools/grit/grit/tool/resize.py
+ create mode 100644 tools/grit/grit/tool/test.py
+ create mode 100644 tools/grit/grit/tool/transl2tc.py
+ create mode 100644 tools/grit/grit/tool/transl2tc_unittest.py
+ create mode 100644 tools/grit/grit/tool/unit.py
+ create mode 100644 tools/grit/grit/tool/update_resource_ids/__init__.py
+ create mode 100644 tools/grit/grit/tool/update_resource_ids/assigner.py
+ create mode 100644 tools/grit/grit/tool/update_resource_ids/assigner_unittest.py
+ create mode 100644 tools/grit/grit/tool/update_resource_ids/common.py
+ create mode 100644 tools/grit/grit/tool/update_resource_ids/parser.py
+ create mode 100644 tools/grit/grit/tool/update_resource_ids/reader.py
+ create mode 100644 tools/grit/grit/tool/xmb.py
+ create mode 100644 tools/grit/grit/tool/xmb_unittest.py
+ create mode 100644 tools/grit/grit/util.py
+ create mode 100644 tools/grit/grit/util_unittest.py
+ create mode 100644 tools/grit/grit/xtb_reader.py
+ create mode 100644 tools/grit/grit/xtb_reader_unittest.py
+ create mode 100644 tools/grit/grit_info.py
+ create mode 100644 tools/grit/grit_rule.gni
+ create mode 100644 tools/grit/minify_with_uglify.py
+ create mode 100644 tools/grit/minimize_css.py
+ create mode 100644 tools/grit/minimize_css_unittest.py
+ create mode 100644 tools/grit/pak_util.py
+ create mode 100644 tools/grit/repack.gni
+ create mode 100644 tools/grit/setup.py
+ create mode 100644 tools/grit/stamp_grit_sources.py
+ create mode 100644 tools/grit/third_party/six/LICENSE
+ create mode 100644 tools/grit/third_party/six/README
+ create mode 100644 tools/grit/third_party/six/README.chromium
+ create mode 100644 tools/grit/third_party/six/__init__.py
-diff --git a/tools/clang/OWNERS b/tools/clang/OWNERS
+diff --git a/tools/grit/.gitignore b/tools/grit/.gitignore
new file mode 100644
-index 0000000000..d86ef9424a
+index 0000000000..0d20b6487c
--- /dev/null
-+++ b/tools/clang/OWNERS
-@@ -0,0 +1,2 @@
-+hans@chromium.org
-+thakis@chromium.org
-diff --git a/tools/clang/plugins/ChromeClassTester.cpp b/tools/clang/plugins/ChromeClassTester.cpp
++++ b/tools/grit/.gitignore
+@@ -0,0 +1 @@
++*.pyc
+diff --git a/tools/grit/BUILD.gn b/tools/grit/BUILD.gn
new file mode 100644
-index 0000000000..055866c5c5
+index 0000000000..1cd3c75b55
--- /dev/null
-+++ b/tools/clang/plugins/ChromeClassTester.cpp
-@@ -0,0 +1,294 @@
-+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+// Use of this source code is governed by a BSD-style license that can be
-+// found in the LICENSE file.
-+
-+// A general interface for filtering and only acting on classes in Chromium C++
-+// code.
++++ b/tools/grit/BUILD.gn
+@@ -0,0 +1,48 @@
++# Copyright 2014 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
+
-+#include "ChromeClassTester.h"
++# This target creates a stamp file that depends on all the sources in the grit
++# directory. By depending on this, a target can force itself to be rebuilt if
++# grit itself changes.
+
-+#include <sys/param.h>
++import("//build/config/sanitizers/sanitizers.gni")
+
-+#include "clang/AST/AST.h"
-+#include "clang/Basic/FileManager.h"
-+#include "clang/Basic/SourceManager.h"
++action("grit_sources") {
++ depfile = "$target_out_dir/grit_sources.d"
++ script = "stamp_grit_sources.py"
+
-+using namespace clang;
++ inputs = [ "grit.py" ]
+
-+namespace {
++ # Note that we can't call this "grit_sources.stamp" because that file is
++ # implicitly created by GN for script actions.
++ outputs = [ "$target_out_dir/grit_sources.script.stamp" ]
+
-+bool starts_with(const std::string& one, const std::string& two) {
-+ return one.compare(0, two.size(), two) == 0;
++ args = [
++ rebase_path("//tools/grit", root_build_dir),
++ rebase_path(outputs[0], root_build_dir),
++ rebase_path(depfile, root_build_dir),
++ ]
+}
+
-+std::string lstrip(const std::string& one, const std::string& two) {
-+ if (starts_with(one, two))
-+ return one.substr(two.size());
-+ return one;
++group("grit_python_unittests") {
++ testonly = true
++
++ data = [
++ "//testing/scripts/common.py",
++ "//testing/scripts/run_isolated_script_test.py",
++ "//testing/xvfb.py",
++ "//tools/grit/",
++ "//third_party/catapult/third_party/typ/",
++ ]
+}
+
-+bool ends_with(const std::string& one, const std::string& two) {
-+ if (two.size() > one.size())
-+ return false;
++# See https://crbug.com/983200
++if (is_mac && is_asan) {
++ create_bundle("brotli_mac_asan_workaround") {
++ bundle_root_dir = "$target_out_dir/$target_name"
++ bundle_executable_dir = bundle_root_dir
+
-+ return one.compare(one.size() - two.size(), two.size(), two) == 0;
++ public_deps = [ "//third_party/brotli:brotli($host_toolchain)" ]
++ }
+}
+diff --git a/tools/grit/MANIFEST.in b/tools/grit/MANIFEST.in
+new file mode 100644
+index 0000000000..1cbff42400
+--- /dev/null
++++ b/tools/grit/MANIFEST.in
+@@ -0,0 +1,3 @@
++exclude grit/test_suite_all.py
++exclude grit/tool/test.py
++global-exclude *_unittest.py
+diff --git a/tools/grit/OWNERS b/tools/grit/OWNERS
+new file mode 100644
+index 0000000000..6a8f447b82
+--- /dev/null
++++ b/tools/grit/OWNERS
+@@ -0,0 +1,8 @@
++agrieve@chromium.org
++flackr@chromium.org
++thakis@chromium.org
++thestig@chromium.org
+
-+} // namespace
++# Admin policy related grit tools.
++per-file *policy*=file://components/policy/tools/OWNERS
++per-file *admin_template*=file://components/policy/tools/OWNERS
+diff --git a/tools/grit/PRESUBMIT.py b/tools/grit/PRESUBMIT.py
+new file mode 100644
+index 0000000000..03b7188551
+--- /dev/null
++++ b/tools/grit/PRESUBMIT.py
+@@ -0,0 +1,22 @@
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
+
-+ChromeClassTester::ChromeClassTester(CompilerInstance& instance)
-+ : instance_(instance),
-+ diagnostic_(instance.getDiagnostics()) {
-+ BuildBannedLists();
-+}
++"""grit unittests presubmit script.
+
-+ChromeClassTester::~ChromeClassTester() {}
++See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts for
++details on the presubmit API built into gcl.
++"""
+
-+void ChromeClassTester::HandleTagDeclDefinition(TagDecl* tag) {
-+ pending_class_decls_.push_back(tag);
-+}
+
-+bool ChromeClassTester::HandleTopLevelDecl(DeclGroupRef group_ref) {
-+ for (size_t i = 0; i < pending_class_decls_.size(); ++i)
-+ CheckTag(pending_class_decls_[i]);
-+ pending_class_decls_.clear();
++def RunUnittests(input_api, output_api):
++ return input_api.canned_checks.RunUnitTests(input_api, output_api,
++ [input_api.os_path.join('grit', 'test_suite_all.py')])
+
-+ return true; // true means continue parsing.
-+}
+
-+void ChromeClassTester::CheckTag(TagDecl* tag) {
-+ // We handle class types here where we have semantic information. We can only
-+ // check structs/classes/enums here, but we get a bunch of nice semantic
-+ // information instead of just parsing information.
++def CheckChangeOnUpload(input_api, output_api):
++ return RunUnittests(input_api, output_api)
+
-+ if (CXXRecordDecl* record = dyn_cast<CXXRecordDecl>(tag)) {
-+ // If this is a POD or a class template or a type dependent on a
-+ // templated class, assume there's no ctor/dtor/virtual method
-+ // optimization that we can do.
-+ if (record->isPOD() ||
-+ record->getDescribedClassTemplate() ||
-+ record->getTemplateSpecializationKind() ||
-+ record->isDependentType())
-+ return;
+
-+ if (InBannedNamespace(record))
-+ return;
++def CheckChangeOnCommit(input_api, output_api):
++ return RunUnittests(input_api, output_api)
+diff --git a/tools/grit/README.md b/tools/grit/README.md
+new file mode 100644
+index 0000000000..b5c3f4b51b
+--- /dev/null
++++ b/tools/grit/README.md
+@@ -0,0 +1,19 @@
++# GRIT (Google Resource and Internationalization Tool)
+
-+ SourceLocation record_location = record->getInnerLocStart();
-+ if (InBannedDirectory(record_location))
-+ return;
++This is a tool for projects to manage resources and simplify the localization
++workflow.
+
-+ // We sadly need to maintain a blacklist of types that violate these
-+ // rules, but do so for good reason or due to limitations of this
-+ // checker (i.e., we don't handle extern templates very well).
-+ std::string base_name = record->getNameAsString();
-+ if (IsIgnoredType(base_name))
-+ return;
++See the user guide for more details on using this project:
++https://dev.chromium.org/developers/tools-we-use-in-chromium/grit/grit-users-guide
+
-+ // We ignore all classes that end with "Matcher" because they're probably
-+ // GMock artifacts.
-+ if (ends_with(base_name, "Matcher"))
-+ return;
++## History
+
-+ CheckChromeClass(record_location, record);
-+ }
-+}
++This code previously used to live at
++https://code.google.com/p/grit-i18n/source/checkout which still contains the
++project's history. https://chromium.googlesource.com/external/grit-i18n/ is
++a git mirror of the SVN repository that's identical except for the last two
++commits. The project is now developed in the Chromium project directly.
+
-+void ChromeClassTester::emitWarning(SourceLocation loc,
-+ const char* raw_error) {
-+ FullSourceLoc full(loc, instance().getSourceManager());
-+ std::string err;
-+ err = "[chromium-style] ";
-+ err += raw_error;
-+ DiagnosticsEngine::Level level =
-+ diagnostic().getWarningsAsErrors() ?
-+ DiagnosticsEngine::Error :
-+ DiagnosticsEngine::Warning;
-+ unsigned id = diagnostic().getCustomDiagID(level, err);
-+ DiagnosticBuilder builder = diagnostic().Report(full, id);
-+}
-+
-+bool ChromeClassTester::InBannedNamespace(const Decl* record) {
-+ std::string n = GetNamespace(record);
-+ if (!n.empty()) {
-+ return std::find(banned_namespaces_.begin(), banned_namespaces_.end(), n)
-+ != banned_namespaces_.end();
-+ }
++There is a read-only mirror of just this directory at
++https://chromium.googlesource.com/chromium/src/tools/grit/ if you don't want to
++check out all of Chromium.
+diff --git a/tools/grit/grit.py b/tools/grit/grit.py
+new file mode 100644
+index 0000000000..abd1ab6449
+--- /dev/null
++++ b/tools/grit/grit.py
+@@ -0,0 +1,31 @@
++#!/usr/bin/env python
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
+
-+ return false;
-+}
++'''Bootstrapping for GRIT.
++'''
++
++from __future__ import print_function
++
++import os
++import sys
++
++import grit.grit_runner
++
++sys.path.append(
++ os.path.join(
++ os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
++ 'diagnosis'))
++try:
++ import crbug_1001171
++except ImportError:
++ crbug_1001171 = None
++
++
++if __name__ == '__main__':
++ if crbug_1001171:
++ with crbug_1001171.DumpStateOnLookupError():
++ sys.exit(grit.grit_runner.Main(sys.argv[1:]))
++ else:
++ sys.exit(grit.grit_runner.Main(sys.argv[1:]))
+diff --git a/tools/grit/grit/__init__.py b/tools/grit/grit/__init__.py
+new file mode 100644
+index 0000000000..91ac9ee896
+--- /dev/null
++++ b/tools/grit/grit/__init__.py
+@@ -0,0 +1,19 @@
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++'''Package 'grit'
++'''
++
++from __future__ import print_function
++
++import os
++import sys
++
++
++_CUR_DIR = os.path.abspath(os.path.dirname(__file__))
++_GRIT_DIR = os.path.dirname(_CUR_DIR)
++_THIRD_PARTY_DIR = os.path.join(_GRIT_DIR, 'third_party')
++
++if _THIRD_PARTY_DIR not in sys.path:
++ sys.path.insert(0, _THIRD_PARTY_DIR)
+diff --git a/tools/grit/grit/clique.py b/tools/grit/grit/clique.py
+new file mode 100644
+index 0000000000..e7be3ec164
+--- /dev/null
++++ b/tools/grit/grit/clique.py
+@@ -0,0 +1,491 @@
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++'''Collections of messages and their translations, called cliques. Also
++collections of cliques (uber-cliques).
++'''
++
++from __future__ import print_function
++
++import re
++
++import six
++
++from grit import constants
++from grit import exception
++from grit import lazy_re
++from grit import pseudo
++from grit import pseudo_rtl
++from grit import tclib
++
++
++class UberClique(object):
++ '''A factory (NOT a singleton factory) for making cliques. It has several
++ methods for working with the cliques created using the factory.
++ '''
++
++ def __init__(self):
++ # A map from message ID to list of cliques whose source messages have
++ # that ID. This will contain all cliques created using this factory.
++ # Different messages can have the same ID because they have the
++ # same translateable portion and placeholder names, but occur in different
++ # places in the resource tree.
++ #
++ # Each list of cliques is kept sorted by description, to achieve
++ # stable results from the BestClique method, see below.
++ self.cliques_ = {}
++
++ # A map of clique IDs to list of languages to indicate translations where we
++ # fell back to English.
++ self.fallback_translations_ = {}
++
++ # A map of clique IDs to list of languages to indicate missing translations.
++ self.missing_translations_ = {}
++
++ def _AddMissingTranslation(self, lang, clique, is_error):
++ tl = self.fallback_translations_
++ if is_error:
++ tl = self.missing_translations_
++ id = clique.GetId()
++ if id not in tl:
++ tl[id] = {}
++ if lang not in tl[id]:
++ tl[id][lang] = 1
++
++ def HasMissingTranslations(self):
++ return len(self.missing_translations_) > 0
++
++ def MissingTranslationsReport(self):
++ '''Returns a string suitable for printing to report missing
++ and fallback translations to the user.
++ '''
++ def ReportTranslation(clique, langs):
++ text = clique.GetMessage().GetPresentableContent()
++ # The text 'error' (usually 'Error:' but we are conservative)
++ # can trigger some build environments (Visual Studio, we're
++ # looking at you) to consider invocation of grit to have failed,
++ # so we make sure never to output that word.
++ extract = re.sub(r'(?i)error', 'REDACTED', text[0:40])[0:40]
++ ellipsis = ''
++ if len(text) > 40:
++ ellipsis = '...'
++ langs_extract = langs[0:6]
++ describe_langs = ','.join(langs_extract)
++ if len(langs) > 6:
++ describe_langs += " and %d more" % (len(langs) - 6)
++ return " %s \"%s%s\" %s" % (clique.GetId(), extract, ellipsis,
++ describe_langs)
++ lines = []
++ if len(self.fallback_translations_):
++ lines.append(
++ "WARNING: Fell back to English for the following translations:")
++ for (id, langs) in self.fallback_translations_.items():
++ lines.append(
++ ReportTranslation(self.cliques_[id][0], list(langs.keys())))
++ if len(self.missing_translations_):
++ lines.append("ERROR: The following translations are MISSING:")
++ for (id, langs) in self.missing_translations_.items():
++ lines.append(
++ ReportTranslation(self.cliques_[id][0], list(langs.keys())))
++ return '\n'.join(lines)
++
++ def MakeClique(self, message, translateable=True):
++ '''Create a new clique initialized with a message.
++
++ Args:
++ message: tclib.Message()
++ translateable: True | False
++ '''
++ clique = MessageClique(self, message, translateable)
++
++ # Enable others to find this clique by its message ID
++ if message.GetId() in self.cliques_:
++ presentable_text = clique.GetMessage().GetPresentableContent()
++ if not message.HasAssignedId():
++ for c in self.cliques_[message.GetId()]:
++ assert c.GetMessage().GetPresentableContent() == presentable_text
++ self.cliques_[message.GetId()].append(clique)
++ # We need to keep each list of cliques sorted by description, to
++ # achieve stable results from the BestClique method, see below.
++ self.cliques_[message.GetId()].sort(
++ key=lambda c:c.GetMessage().GetDescription())
++ else:
++ self.cliques_[message.GetId()] = [clique]
++
++ return clique
++
++ def FindCliqueAndAddTranslation(self, translation, language):
++ '''Adds the specified translation to the clique with the source message
++ it is a translation of.
++
++ Args:
++ translation: tclib.Translation()
++ language: 'en' | 'fr' ...
++
++ Return:
++ True if the source message was found, otherwise false.
++ '''
++ if translation.GetId() in self.cliques_:
++ for clique in self.cliques_[translation.GetId()]:
++ clique.AddTranslation(translation, language)
++ return True
++ else:
++ return False
++
++ def BestClique(self, id):
++ '''Returns the "best" clique from a list of cliques. All the cliques
++ must have the same ID. The "best" clique is chosen in the following
++ order of preference:
++ - The first clique that has a non-ID-based description.
++ - If no such clique found, the first clique with an ID-based description.
++ - Otherwise the first clique.
++
++ This method is stable in terms of always returning a clique with
++ an identical description (on different runs of GRIT on the same
++ data) because self.cliques_ is sorted by description.
++ '''
++ clique_list = self.cliques_[id]
++ clique_with_id = None
++ clique_default = None
++ for clique in clique_list:
++ if not clique_default:
++ clique_default = clique
++
++ description = clique.GetMessage().GetDescription()
++ if description and len(description) > 0:
++ if not description.startswith('ID:'):
++ # this is the preferred case so we exit right away
++ return clique
++ elif not clique_with_id:
++ clique_with_id = clique
++ if clique_with_id:
++ return clique_with_id
++ else:
++ return clique_default
++
++ def BestCliquePerId(self):
++ '''Iterates over the list of all cliques and returns the best clique for
++ each ID. This will be the first clique with a source message that has a
++ non-empty description, or an arbitrary clique if none of them has a
++ description.
++ '''
++ for id in self.cliques_:
++ yield self.BestClique(id)
++
++ def BestCliqueByOriginalText(self, text, meaning):
++ '''Finds the "best" (as in BestClique()) clique that has original text
++ 'text' and meaning 'meaning'. Returns None if there is no such clique.
++ '''
++ # If needed, this can be optimized by maintaining a map of
++ # fingerprints of original text+meaning to cliques.
++ for c in self.BestCliquePerId():
++ msg = c.GetMessage()
++ if msg.GetRealContent() == text and msg.GetMeaning() == meaning:
++ return msg
++ return None
++
++ def AllMessageIds(self):
++ '''Returns a list of all defined message IDs.
++ '''
++ return list(self.cliques_.keys())
++
++ def AllCliques(self):
++ '''Iterates over all cliques. Note that this can return multiple cliques
++ with the same ID.
++ '''
++ for cliques in self.cliques_.values():
++ for c in cliques:
++ yield c
++
++ def GenerateXtbParserCallback(self, lang, debug=False):
++ '''Creates a callback function as required by grit.xtb_reader.Parse().
++ This callback will create Translation objects for each message from
++ the XTB that exists in this uberclique, and add them as translations for
++ the relevant cliques. The callback will add translations to the language
++ specified by 'lang'
++
++ Args:
++ lang: 'fr'
++ debug: True | False
++ '''
++ def Callback(id, structure):
++ if id not in self.cliques_:
++ if debug:
++ print("Ignoring translation #%s" % id)
++ return
++
++ if debug:
++ print("Adding translation #%s" % id)
++
++ # We fetch placeholder information from the original message (the XTB file
++ # only contains placeholder names).
++ original_msg = self.BestClique(id).GetMessage()
++
++ translation = tclib.Translation(id=id)
++ for is_ph,text in structure:
++ if not is_ph:
++ translation.AppendText(text)
++ else:
++ found_placeholder = False
++ for ph in original_msg.GetPlaceholders():
++ if ph.GetPresentation() == text:
++ translation.AppendPlaceholder(tclib.Placeholder(
++ ph.GetPresentation(), ph.GetOriginal(), ph.GetExample()))
++ found_placeholder = True
++ break
++ if not found_placeholder:
++ raise exception.MismatchingPlaceholders(
++ 'Translation for message ID %s had <ph name="%s"/>, no match\n'
++ 'in original message' % (id, text))
++ self.FindCliqueAndAddTranslation(translation, lang)
++ return Callback
++
++
++class CustomType(object):
++ '''A base class you should implement if you wish to specify a custom type
++ for a message clique (i.e. custom validation and optional modification of
++ translations).'''
++
++ def Validate(self, message):
++ '''Returns true if the message (a tclib.Message object) is valid,
++ otherwise false.
++ '''
++ raise NotImplementedError()
++
++ def ValidateAndModify(self, lang, translation):
++ '''Returns true if the translation (a tclib.Translation object) is valid,
++ otherwise false. The language is also passed in. This method may modify
++ the translation that is passed in, if it so wishes.
++ '''
++ raise NotImplementedError()
++
++ def ModifyTextPart(self, lang, text):
++ '''If you call ModifyEachTextPart, it will turn around and call this method
++ for each text part of the translation. You should return the modified
++ version of the text, or just the original text to not change anything.
++ '''
++ raise NotImplementedError()
++
++ def ModifyEachTextPart(self, lang, translation):
++ '''Call this to easily modify one or more of the textual parts of a
++ translation. It will call ModifyTextPart for each part of the
++ translation.
++ '''
++ contents = translation.GetContent()
++ for ix in range(len(contents)):
++ if (isinstance(contents[ix], six.string_types)):
++ contents[ix] = self.ModifyTextPart(lang, contents[ix])
++
++
++class OneOffCustomType(CustomType):
++ '''A very simple custom type that performs the validation expressed by
++ the input expression on all languages including the source language.
++ The expression can access the variables 'lang', 'msg' and 'text()' where
++ 'lang' is the language of 'msg', 'msg' is the message or translation being
++ validated and 'text()' returns the real contents of 'msg' (for shorthand).
++ '''
++ def __init__(self, expression):
++ self.expr = expression
++ def Validate(self, message):
++ return self.ValidateAndModify(MessageClique.source_language, message)
++ def ValidateAndModify(self, lang, msg):
++ def text():
++ return msg.GetRealContent()
++ return eval(self.expr, {},
++ {'lang' : lang,
++ 'text' : text,
++ 'msg' : msg,
++ })
++
++
++class MessageClique(object):
++ '''A message along with all of its translations. Also code to bring
++ translations together with their original message.'''
++
++ # change this to the language code of Messages you add to cliques_.
++ # TODO(joi) Actually change this based on the <grit> node's source language
++ source_language = 'en'
++
++ # A constant translation we use when asked for a translation into the
++ # special language constants.CONSTANT_LANGUAGE.
++ CONSTANT_TRANSLATION = tclib.Translation(text='TTTTTT')
++
++ # A pattern to match messages that are empty or whitespace only.
++ WHITESPACE_MESSAGE = lazy_re.compile(r'^\s*$')
++
++ def __init__(self, uber_clique, message, translateable=True,
++ custom_type=None):
++ '''Create a new clique initialized with just a message.
++
++ Note that messages with a body comprised only of whitespace will implicitly
++ be marked non-translatable.
++
++ Args:
++ uber_clique: Our uber-clique (collection of cliques)
++ message: tclib.Message()
++ translateable: True | False
++ custom_type: instance of clique.CustomType interface
++ '''
++ # Our parent
++ self.uber_clique = uber_clique
++ # If not translateable, we only store the original message.
++ self.translateable = translateable
++
++ # We implicitly mark messages that have a whitespace-only body as
++ # non-translateable.
++ if MessageClique.WHITESPACE_MESSAGE.match(message.GetRealContent()):
++ self.translateable = False
++
++ # A mapping of language identifiers to tclib.BaseMessage and its
++ # subclasses (i.e. tclib.Message and tclib.Translation).
++ self.clique = { MessageClique.source_language : message }
++ # A list of the "shortcut groups" this clique is
++ # part of. Within any given shortcut group, no shortcut key (e.g. &J)
++ # must appear more than once in each language for all cliques that
++ # belong to the group.
++ self.shortcut_groups = []
++ # An instance of the CustomType interface, or None. If this is set, it will
++ # be used to validate the original message and translations thereof, and
++ # will also get a chance to modify translations of the message.
++ self.SetCustomType(custom_type)
++
++ def GetMessage(self):
++ '''Retrieves the tclib.Message that is the source for this clique.'''
++ return self.clique[MessageClique.source_language]
++
++ def GetId(self):
++ '''Retrieves the message ID of the messages in this clique.'''
++ return self.GetMessage().GetId()
++
++ def IsTranslateable(self):
++ return self.translateable
++
++ def AddToShortcutGroup(self, group):
++ self.shortcut_groups.append(group)
++
++ def SetCustomType(self, custom_type):
++ '''Makes this clique use custom_type for validating messages and
++ translations, and optionally modifying translations.
++ '''
++ self.custom_type = custom_type
++ if custom_type and not custom_type.Validate(self.GetMessage()):
++ raise exception.InvalidMessage(self.GetMessage().GetRealContent())
++
++ def MessageForLanguage(self, lang, pseudo_if_no_match=True,
++ fallback_to_english=False):
++ '''Returns the message/translation for the specified language, providing
++ a pseudotranslation if there is no available translation and a pseudo-
++ translation is requested.
++
++ The translation of any message whatsoever in the special language
++ 'x_constant' is the message "TTTTTT".
++
++ Args:
++ lang: 'en'
++ pseudo_if_no_match: True
++ fallback_to_english: False
++
++ Return:
++ tclib.BaseMessage
++ '''
++ if not self.translateable:
++ return self.GetMessage()
++
++ if lang == constants.CONSTANT_LANGUAGE:
++ return self.CONSTANT_TRANSLATION
++
++ for msglang in self.clique:
++ if lang == msglang:
++ return self.clique[msglang]
++
++ if lang == constants.FAKE_BIDI:
++ return pseudo_rtl.PseudoRTLMessage(self.GetMessage())
++
++ if fallback_to_english:
++ self.uber_clique._AddMissingTranslation(lang, self, is_error=False)
++ return self.GetMessage()
++
++ # If we're not supposed to generate pseudotranslations, we add an error
++ # report to a list of errors, then fail at a higher level, so that we
++ # get a list of all messages that are missing translations.
++ if not pseudo_if_no_match:
++ self.uber_clique._AddMissingTranslation(lang, self, is_error=True)
++
++ return pseudo.PseudoMessage(self.GetMessage())
++
++ def AllMessagesThatMatch(self, lang_re, include_pseudo = True):
++ '''Returns a map of all messages that match 'lang', including the pseudo
++ translation if requested.
++
++ Args:
++ lang_re: re.compile(r'fr|en')
++ include_pseudo: True
++
++ Return:
++ { 'en' : tclib.Message,
++ 'fr' : tclib.Translation,
++ pseudo.PSEUDO_LANG : tclib.Translation }
++ '''
++ if not self.translateable:
++ return [self.GetMessage()]
++
++ matches = {}
++ for msglang in self.clique:
++ if lang_re.match(msglang):
++ matches[msglang] = self.clique[msglang]
++
++ if include_pseudo:
++ matches[pseudo.PSEUDO_LANG] = pseudo.PseudoMessage(self.GetMessage())
++
++ return matches
++
++ def AddTranslation(self, translation, language):
++ '''Add a translation to this clique. The translation must have the same
++ ID as the message that is the source for this clique.
++
++ If this clique is not translateable, the function just returns.
++
++ Args:
++ translation: tclib.Translation()
++ language: 'en'
++
++ Throws:
++ grit.exception.InvalidTranslation if the translation you're trying to add
++ doesn't have the same message ID as the source message of this clique.
++ '''
++ if not self.translateable:
++ return
++ if translation.GetId() != self.GetId():
++ raise exception.InvalidTranslation(
++ 'Msg ID %s, transl ID %s' % (self.GetId(), translation.GetId()))
++
++ assert not language in self.clique
++
++ # Because two messages can differ in the original content of their
++ # placeholders yet share the same ID (because they are otherwise the
++ # same), the translation we are getting may have different original
++ # content for placeholders than our message, yet it is still the right
++ # translation for our message (because it is for the same ID). We must
++ # therefore fetch the original content of placeholders from our original
++ # English message.
++ #
++ # See grit.clique_unittest.MessageCliqueUnittest.testSemiIdenticalCliques
++ # for a concrete explanation of why this is necessary.
++
++ original = self.MessageForLanguage(self.source_language, False)
++ if len(original.GetPlaceholders()) != len(translation.GetPlaceholders()):
++ print("ERROR: '%s' translation of message id %s does not match" %
++ (language, translation.GetId()))
++ assert False
++
++ transl_msg = tclib.Translation(id=self.GetId(),
++ text=translation.GetPresentableContent(),
++ placeholders=original.GetPlaceholders())
++
++ if (self.custom_type and
++ not self.custom_type.ValidateAndModify(language, transl_msg)):
++ print("WARNING: %s translation failed validation: %s" %
++ (language, transl_msg.GetId()))
++
++ self.clique[language] = transl_msg
+diff --git a/tools/grit/grit/clique_unittest.py b/tools/grit/grit/clique_unittest.py
+new file mode 100644
+index 0000000000..7d2d7318ba
+--- /dev/null
++++ b/tools/grit/grit/clique_unittest.py
+@@ -0,0 +1,265 @@
++#!/usr/bin/env python
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++'''Unit tests for grit.clique'''
++
++from __future__ import print_function
++
++import os
++import sys
++if __name__ == '__main__':
++ sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
++
++import re
++import unittest
++
++from six import StringIO
++
++from grit import clique
++from grit import exception
++from grit import pseudo
++from grit import tclib
++from grit import grd_reader
++from grit import util
++
++class MessageCliqueUnittest(unittest.TestCase):
++ def testClique(self):
++ factory = clique.UberClique()
++ msg = tclib.Message(text='Hello USERNAME, how are you?',
++ placeholders=[
++ tclib.Placeholder('USERNAME', '%s', 'Joi')])
++ c = factory.MakeClique(msg)
++
++ self.failUnless(c.GetMessage() == msg)
++ self.failUnless(c.GetId() == msg.GetId())
++
++ msg_fr = tclib.Translation(text='Bonjour USERNAME, comment ca va?',
++ id=msg.GetId(), placeholders=[
++ tclib.Placeholder('USERNAME', '%s', 'Joi')])
++ msg_de = tclib.Translation(text='Guten tag USERNAME, wie geht es dir?',
++ id=msg.GetId(), placeholders=[
++ tclib.Placeholder('USERNAME', '%s', 'Joi')])
++
++ c.AddTranslation(msg_fr, 'fr')
++ factory.FindCliqueAndAddTranslation(msg_de, 'de')
++
++ # sort() sorts lists in-place and does not return them
++ for lang in ('en', 'fr', 'de'):
++ self.failUnless(lang in c.clique)
++
++ self.failUnless(c.MessageForLanguage('fr').GetRealContent() ==
++ msg_fr.GetRealContent())
++
++ try:
++ c.MessageForLanguage('zh-CN', False)
++ self.fail('Should have gotten exception')
++ except:
++ pass
++
++ self.failUnless(c.MessageForLanguage('zh-CN', True) != None)
++
++ rex = re.compile('fr|de|bingo')
++ self.failUnless(len(c.AllMessagesThatMatch(rex, False)) == 2)
++ self.failUnless(
++ c.AllMessagesThatMatch(rex, True)[pseudo.PSEUDO_LANG] is not None)
++
++ def testBestClique(self):
++ factory = clique.UberClique()
++ factory.MakeClique(tclib.Message(text='Alfur', description='alfaholl'))
++ factory.MakeClique(tclib.Message(text='Alfur', description=''))
++ factory.MakeClique(tclib.Message(text='Vaettur', description=''))
++ factory.MakeClique(tclib.Message(text='Vaettur', description=''))
++ factory.MakeClique(tclib.Message(text='Troll', description=''))
++ factory.MakeClique(tclib.Message(text='Gryla', description='ID: IDS_GRYLA'))
++ factory.MakeClique(tclib.Message(text='Gryla', description='vondakerling'))
++ factory.MakeClique(tclib.Message(text='Leppaludi', description='ID: IDS_LL'))
++ factory.MakeClique(tclib.Message(text='Leppaludi', description=''))
++
++ count_best_cliques = 0
++ for c in factory.BestCliquePerId():
++ count_best_cliques += 1
++ msg = c.GetMessage()
++ text = msg.GetRealContent()
++ description = msg.GetDescription()
++ if text == 'Alfur':
++ self.failUnless(description == 'alfaholl')
++ elif text == 'Gryla':
++ self.failUnless(description == 'vondakerling')
++ elif text == 'Leppaludi':
++ self.failUnless(description == 'ID: IDS_LL')
++ self.failUnless(count_best_cliques == 5)
++
++ def testAllInUberClique(self):
++ resources = grd_reader.Parse(
++ StringIO(u'''<?xml version="1.0" encoding="UTF-8"?>
++<grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
++ <release seq="3">
++ <messages>
++ <message name="IDS_GREETING" desc="Printed to greet the currently logged in user">
++ Hello <ph name="USERNAME">%s<ex>Joi</ex></ph>, how are you doing today?
++ </message>
++ </messages>
++ <structures>
++ <structure type="dialog" name="IDD_ABOUTBOX" encoding="utf-16" file="grit/testdata/klonk.rc" />
++ <structure type="tr_html" name="ID_HTML" file="grit/testdata/simple.html" />
++ </structures>
++ </release>
++</grit>'''), util.PathFromRoot('.'))
++ resources.SetOutputLanguage('en')
++ resources.RunGatherers()
++ content_list = []
++ for clique_list in resources.UberClique().cliques_.values():
++ for clique in clique_list:
++ content_list.append(clique.GetMessage().GetRealContent())
++ self.failUnless('Hello %s, how are you doing today?' in content_list)
++ self.failUnless('Jack "Black" Daniels' in content_list)
++ self.failUnless('Hello!' in content_list)
++
++ def testCorrectExceptionIfWrongEncodingOnResourceFile(self):
++ '''This doesn't really belong in this unittest file, but what the heck.'''
++ resources = grd_reader.Parse(
++ StringIO(u'''<?xml version="1.0" encoding="UTF-8"?>
++<grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
++ <release seq="3">
++ <structures>
++ <structure type="dialog" name="IDD_ABOUTBOX" file="grit/testdata/klonk.rc" />
++ </structures>
++ </release>
++</grit>'''), util.PathFromRoot('.'))
++ self.assertRaises(exception.SectionNotFound, resources.RunGatherers)
++
++ def testSemiIdenticalCliques(self):
++ messages = [
++ tclib.Message(text='Hello USERNAME',
++ placeholders=[tclib.Placeholder('USERNAME', '$1', 'Joi')]),
++ tclib.Message(text='Hello USERNAME',
++ placeholders=[tclib.Placeholder('USERNAME', '%s', 'Joi')]),
++ ]
++ self.failUnless(messages[0].GetId() == messages[1].GetId())
++
++ # Both of the above would share a translation.
++ translation = tclib.Translation(id=messages[0].GetId(),
++ text='Bonjour USERNAME',
++ placeholders=[tclib.Placeholder(
++ 'USERNAME', '$1', 'Joi')])
++
++ factory = clique.UberClique()
++ cliques = [factory.MakeClique(msg) for msg in messages]
++
++ for clq in cliques:
++ clq.AddTranslation(translation, 'fr')
++
++ self.failUnless(cliques[0].MessageForLanguage('fr').GetRealContent() ==
++ 'Bonjour $1')
++ self.failUnless(cliques[1].MessageForLanguage('fr').GetRealContent() ==
++ 'Bonjour %s')
++
++ def testMissingTranslations(self):
++ messages = [ tclib.Message(text='Hello'), tclib.Message(text='Goodbye') ]
++ factory = clique.UberClique()
++ cliques = [factory.MakeClique(msg) for msg in messages]
++
++ cliques[1].MessageForLanguage('fr', False, True)
++
++ self.failUnless(not factory.HasMissingTranslations())
++
++ cliques[0].MessageForLanguage('de', False, False)
++
++ self.failUnless(factory.HasMissingTranslations())
++
++ report = factory.MissingTranslationsReport()
++ self.failUnless(report.count('WARNING') == 1)
++ self.failUnless(report.count('8053599568341804890 "Goodbye" fr') == 1)
++ self.failUnless(report.count('ERROR') == 1)
++ self.failUnless(report.count('800120468867715734 "Hello" de') == 1)
++
++ def testCustomTypes(self):
++ factory = clique.UberClique()
++ message = tclib.Message(text='Bingo bongo')
++ c = factory.MakeClique(message)
++ try:
++ c.SetCustomType(DummyCustomType())
++ self.fail()
++ except:
++ pass # expected case - 'Bingo bongo' does not start with 'jjj'
++
++ message = tclib.Message(text='jjjBingo bongo')
++ c = factory.MakeClique(message)
++ c.SetCustomType(util.NewClassInstance(
++ 'grit.clique_unittest.DummyCustomType', clique.CustomType))
++ translation = tclib.Translation(id=message.GetId(), text='Bilingo bolongo')
++ c.AddTranslation(translation, 'fr')
++ self.failUnless(c.MessageForLanguage('fr').GetRealContent().startswith('jjj'))
++
++ def testWhitespaceMessagesAreNontranslateable(self):
++ factory = clique.UberClique()
++
++ message = tclib.Message(text=' \t')
++ c = factory.MakeClique(message, translateable=True)
++ self.failIf(c.IsTranslateable())
++
++ message = tclib.Message(text='\n \n ')
++ c = factory.MakeClique(message, translateable=True)
++ self.failIf(c.IsTranslateable())
++
++ message = tclib.Message(text='\n hello')
++ c = factory.MakeClique(message, translateable=True)
++ self.failUnless(c.IsTranslateable())
++
++ def testEachCliqueKeptSorted(self):
++ factory = clique.UberClique()
++ msg_a = tclib.Message(text='hello', description='a')
++ msg_b = tclib.Message(text='hello', description='b')
++ msg_c = tclib.Message(text='hello', description='c')
++ # Insert out of order
++ clique_b = factory.MakeClique(msg_b, translateable=True)
++ clique_a = factory.MakeClique(msg_a, translateable=True)
++ clique_c = factory.MakeClique(msg_c, translateable=True)
++ clique_list = factory.cliques_[clique_a.GetId()]
++ self.failUnless(len(clique_list) == 3)
++ self.failUnless(clique_list[0] == clique_a)
++ self.failUnless(clique_list[1] == clique_b)
++ self.failUnless(clique_list[2] == clique_c)
++
++ def testBestCliqueSortIsStable(self):
++ factory = clique.UberClique()
++ text = 'hello'
++ msg_no_description = tclib.Message(text=text)
++ msg_id_description_a = tclib.Message(text=text, description='ID: a')
++ msg_id_description_b = tclib.Message(text=text, description='ID: b')
++ msg_description_x = tclib.Message(text=text, description='x')
++ msg_description_y = tclib.Message(text=text, description='y')
++ clique_id = msg_no_description.GetId()
++
++ # Insert in an order that tests all outcomes.
++ clique_no_description = factory.MakeClique(msg_no_description,
++ translateable=True)
++ self.failUnless(factory.BestClique(clique_id) == clique_no_description)
++ clique_id_description_b = factory.MakeClique(msg_id_description_b,
++ translateable=True)
++ self.failUnless(factory.BestClique(clique_id) == clique_id_description_b)
++ clique_id_description_a = factory.MakeClique(msg_id_description_a,
++ translateable=True)
++ self.failUnless(factory.BestClique(clique_id) == clique_id_description_a)
++ clique_description_y = factory.MakeClique(msg_description_y,
++ translateable=True)
++ self.failUnless(factory.BestClique(clique_id) == clique_description_y)
++ clique_description_x = factory.MakeClique(msg_description_x,
++ translateable=True)
++ self.failUnless(factory.BestClique(clique_id) == clique_description_x)
++
++
++class DummyCustomType(clique.CustomType):
++ def Validate(self, message):
++ return message.GetRealContent().startswith('jjj')
++ def ValidateAndModify(self, lang, translation):
++ is_ok = self.Validate(translation)
++ self.ModifyEachTextPart(lang, translation)
++ def ModifyTextPart(self, lang, text):
++ return 'jjj%s' % text
++
++
++if __name__ == '__main__':
++ unittest.main()
+diff --git a/tools/grit/grit/constants.py b/tools/grit/grit/constants.py
+new file mode 100644
+index 0000000000..8229c94b09
+--- /dev/null
++++ b/tools/grit/grit/constants.py
+@@ -0,0 +1,23 @@
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++'''Constant definitions for GRIT.
++'''
++
++from __future__ import print_function
++
++# This is the Icelandic noun meaning "grit" and is used to check that our
++# input files are in the correct encoding. The middle character gets encoded
++# as two bytes in UTF-8, so this is sufficient to detect incorrect encoding.
++ENCODING_CHECK = u'm\u00f6l'
++
++# A special language, translations into which are always "TTTTTT".
++CONSTANT_LANGUAGE = 'x_constant'
++
++FAKE_BIDI = 'fake-bidi'
++
++# Magic number added to the header of resources brotli compressed by grit. Used
++# to easily identify resources as being brotli compressed. See
++# ui/base/resource/resource_bundle.h for decompression usage.
++BROTLI_CONST = b'\x1e\x9b'
+diff --git a/tools/grit/grit/exception.py b/tools/grit/grit/exception.py
+new file mode 100644
+index 0000000000..2a363fb077
+--- /dev/null
++++ b/tools/grit/grit/exception.py
+@@ -0,0 +1,139 @@
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++'''Exception types for GRIT.
++'''
++
++from __future__ import print_function
++
++class Base(Exception):
++ '''A base exception that uses the class's docstring in addition to any
++ user-provided message as the body of the Base.
++ '''
++ def __init__(self, msg=''):
++ if len(msg):
++ if self.__doc__:
++ msg = self.__doc__ + ': ' + msg
++ else:
++ msg = self.__doc__
++ super(Base, self).__init__(msg)
++
++
++class Parsing(Base):
++ '''An error occurred parsing a GRD or XTB file.'''
++ pass
++
++
++class UnknownElement(Parsing):
++ '''An unknown node type was encountered.'''
++ pass
++
++
++class MissingElement(Parsing):
++ '''An expected element was missing.'''
++ pass
++
++
++class UnexpectedChild(Parsing):
++ '''An unexpected child element was encountered (on a leaf node).'''
++ pass
++
++
++class UnexpectedAttribute(Parsing):
++ '''The attribute was not expected'''
++ pass
++
++
++class UnexpectedContent(Parsing):
++ '''This element should not have content'''
++ pass
++
++class MissingMandatoryAttribute(Parsing):
++ '''This element is missing a mandatory attribute'''
++ pass
++
++
++class MutuallyExclusiveMandatoryAttribute(Parsing):
++ '''This element has 2 mutually exclusive mandatory attributes'''
++ pass
++
++
++class DuplicateKey(Parsing):
++ '''A duplicate key attribute was found.'''
++ pass
++
++
++class TooManyExamples(Parsing):
++ '''Only one <ex> element is allowed for each <ph> element.'''
++ pass
++
++
++class FileNotFound(Parsing):
++ '''The resource file was not found.'''
++ pass
++
++
++class InvalidMessage(Base):
++ '''The specified message failed validation.'''
++ pass
++
++
++class InvalidTranslation(Base):
++ '''Attempt to add an invalid translation to a clique.'''
++ pass
++
++
++class NoSuchTranslation(Base):
++ '''Requested translation not available'''
++ pass
++
++
++class NotReady(Base):
++ '''Attempt to use an object before it is ready, or attempt to translate \
++an empty document.'''
++ pass
++
++
++class MismatchingPlaceholders(Base):
++ '''Placeholders do not match.'''
++ pass
++
++
++class InvalidPlaceholderName(Base):
++ '''Placeholder name can only contain A-Z, a-z, 0-9 and underscore.'''
++ pass
++
++
++class BlockTagInTranslateableChunk(Base):
++ '''A block tag was encountered where it wasn't expected.'''
++ pass
++
++
++class SectionNotFound(Base):
++ '''The section you requested was not found in the RC file. Make \
++sure the section ID is correct (matches the section's ID in the RC file). \
++Also note that you may need to specify the RC file's encoding (using the \
++encoding="" attribute) if it is not in the default Windows-1252 encoding. \
++'''
++ pass
++
++
++class IdRangeOverlap(Base):
++ '''ID range overlap.'''
++ pass
++
++
++class ReservedHeaderCollision(Base):
++ '''Resource included with first 3 bytes matching reserved header.'''
++ pass
++
++
++class PlaceholderNotInsidePhNode(Base):
++ '''Placeholder formatters should be inside <ph> element.'''
++ pass
++
++
++class InvalidCharactersInsidePhNode(Base):
++ '''Invalid characters found inside <ph> element.'''
++ pass
+diff --git a/tools/grit/grit/extern/BogoFP.py b/tools/grit/grit/extern/BogoFP.py
+new file mode 100644
+index 0000000000..fc90145833
+--- /dev/null
++++ b/tools/grit/grit/extern/BogoFP.py
+@@ -0,0 +1,22 @@
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++"""Bogus fingerprint implementation, do not use for production,
++provided only as an example.
++
++Usage:
++ grit.py -h grit.extern.BogoFP xmb /tmp/foo
++"""
++
++from __future__ import print_function
++
++import grit.extern.FP
++
++
++def UnsignedFingerPrint(str, encoding='utf-8'):
++ """Generate a fingerprint not intended for production from str (it
++ reduces the precision of the production fingerprint by one bit).
++ """
++ return (0xFFFFF7FFFFFFFFFF &
++ grit.extern.FP._UnsignedFingerPrintImpl(str, encoding))
+diff --git a/tools/grit/grit/extern/FP.py b/tools/grit/grit/extern/FP.py
+new file mode 100644
+index 0000000000..f4ec4d943f
+--- /dev/null
++++ b/tools/grit/grit/extern/FP.py
+@@ -0,0 +1,72 @@
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++from __future__ import print_function
++
++try:
++ import hashlib
++ _new_md5 = hashlib.md5
++except ImportError:
++ import md5
++ _new_md5 = md5.new
++
++
++"""64-bit fingerprint support for strings.
++
++Usage:
++ from extern import FP
++ print('Fingerprint is %ld' % FP.FingerPrint('Hello world!'))
++"""
++
++
++def _UnsignedFingerPrintImpl(str, encoding='utf-8'):
++ """Generate a 64-bit fingerprint by taking the first half of the md5
++ of the string.
++ """
++ hex128 = _new_md5(str.encode(encoding)).hexdigest()
++ int64 = int(hex128[:16], 16)
++ return int64
++
++
++def UnsignedFingerPrint(str, encoding='utf-8'):
++ """Generate a 64-bit fingerprint.
++
++ The default implementation uses _UnsignedFingerPrintImpl, which
++ takes the first half of the md5 of the string, but the
++ implementation may be switched using SetUnsignedFingerPrintImpl.
++ """
++ return _UnsignedFingerPrintImpl(str, encoding)
++
++
++def FingerPrint(str, encoding='utf-8'):
++ fp = UnsignedFingerPrint(str, encoding=encoding)
++ # interpret fingerprint as signed longs
++ if fp & 0x8000000000000000:
++ fp = -((~fp & 0xFFFFFFFFFFFFFFFF) + 1)
++ return fp
++
++
++def UseUnsignedFingerPrintFromModule(module_name):
++ """Imports module_name and replaces UnsignedFingerPrint in the
++ current module with the function of the same name from the imported
++ module.
++
++ Returns the function object previously known as
++ grit.extern.FP.UnsignedFingerPrint.
++ """
++ hash_module = __import__(module_name, fromlist=[module_name])
++ return SetUnsignedFingerPrint(hash_module.UnsignedFingerPrint)
++
++
++def SetUnsignedFingerPrint(function_object):
++ """Sets grit.extern.FP.UnsignedFingerPrint to point to
++ function_object.
+
-+std::string ChromeClassTester::GetNamespace(const Decl* record) {
-+ return GetNamespaceImpl(record->getDeclContext(), "");
++ Returns the function object previously known as
++ grit.extern.FP.UnsignedFingerPrint.
++ """
++ global UnsignedFingerPrint
++ original_function_object = UnsignedFingerPrint
++ UnsignedFingerPrint = function_object
++ return original_function_object
+diff --git a/tools/grit/grit/extern/__init__.py b/tools/grit/grit/extern/__init__.py
+new file mode 100644
+index 0000000000..e69de29bb2
+diff --git a/tools/grit/grit/extern/tclib.py b/tools/grit/grit/extern/tclib.py
+new file mode 100644
+index 0000000000..9952a87c11
+--- /dev/null
++++ b/tools/grit/grit/extern/tclib.py
+@@ -0,0 +1,503 @@
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++# The tclib module contains tools for aggregating, verifying, and storing
++# messages destined for the Translation Console, as well as for reading
++# translations back and outputting them in some desired format.
++#
++# This has been stripped down to include only the functionality needed by grit
++# for creating Windows .rc and .h files. These are the only parts needed by
++# the Chrome build process.
++
++from __future__ import print_function
++
++from grit.extern import FP
++
++# This module assumes that within a bundle no two messages can have the
++# same id unless they're identical.
++
++# The basic classes defined here for external use are Message and Translation,
++# where the former is used for English messages and the latter for
++# translations. These classes have a lot of common functionality, as expressed
++# by the common parent class BaseMessage. Perhaps the most important
++# distinction is that translated text is stored in UTF-8, whereas original text
++# is stored in whatever encoding the client uses (presumably Latin-1).
++
++# --------------------
++# The public interface
++# --------------------
++
++# Generate message id from message text and meaning string (optional),
++# both in utf-8 encoding
++#
++def GenerateMessageId(message, meaning=''):
++ fp = FP.FingerPrint(message)
++ if meaning:
++ # combine the fingerprints of message and meaning
++ fp2 = FP.FingerPrint(meaning)
++ if fp < 0:
++ fp = fp2 + (fp << 1) + 1
++ else:
++ fp = fp2 + (fp << 1)
++ # To avoid negative ids we strip the high-order bit
++ return str(fp & 0x7fffffffffffffff)
++
++# -------------------------------------------------------------------------
++# The MessageTranslationError class is used to signal tclib-specific errors.
++
++
++class MessageTranslationError(Exception):
++
++ def __init__(self, args = ''):
++ self.args = args
++
++
++# -----------------------------------------------------------
++# The Placeholder class represents a placeholder in a message.
++
++class Placeholder(object):
++ # String representation
++ def __str__(self):
++ return '%s, "%s", "%s"' % \
++ (self.__presentation, self.__original, self.__example)
++
++ # Getters
++ def GetOriginal(self):
++ return self.__original
++
++ def GetPresentation(self):
++ return self.__presentation
++
++ def GetExample(self):
++ return self.__example
++
++ def __eq__(self, other):
++ return self.EqualTo(other, strict=1, ignore_trailing_spaces=0)
++
++ # Equality test
++ #
++ # ignore_trailing_spaces: TC is using varchar to store the
++ # phrwr fields, as a result of that, the trailing spaces
++ # are removed by MySQL when the strings are stored into TC:-(
++ # ignore_trailing_spaces parameter is used to ignore
++ # trailing spaces during equivalence comparison.
++ #
++ def EqualTo(self, other, strict = 1, ignore_trailing_spaces = 1):
++ if type(other) is not Placeholder:
++ return 0
++ if StringEquals(self.__presentation, other.__presentation,
++ ignore_trailing_spaces):
++ if not strict or (StringEquals(self.__original, other.__original,
++ ignore_trailing_spaces) and
++ StringEquals(self.__example, other.__example,
++ ignore_trailing_spaces)):
++ return 1
++ return 0
++
++
++# -----------------------------------------------------------------
++# BaseMessage is the common parent class of Message and Translation.
++# It is not meant for direct use.
++
++class BaseMessage(object):
++ # Three types of message construction is supported. If the message text is a
++ # simple string with no dynamic content, you can pass it to the constructor
++ # as the "text" parameter. Otherwise, you can omit "text" and assemble the
++ # message step by step using AppendText() and AppendPlaceholder(). Or, as an
++ # alternative, you can give the constructor the "presentable" version of the
++ # message and a list of placeholders; it will then parse the presentation and
++ # build the message accordingly. For example:
++ # Message(text = "There are NUM_BUGS bugs in your code",
++ # placeholders = [Placeholder("NUM_BUGS", "%d", "33")],
++ # description = "Bla bla bla")
++ def __eq__(self, other):
++ # "source encoding" is nonsense, so ignore it
++ return _ObjectEquals(self, other, ['_BaseMessage__source_encoding'])
++
++ def GetName(self):
++ return self.__name
++
++ def GetSourceEncoding(self):
++ return self.__source_encoding
++
++ # Append a placeholder to the message
++ def AppendPlaceholder(self, placeholder):
++ if not isinstance(placeholder, Placeholder):
++ raise MessageTranslationError("Invalid message placeholder %s in "
++ "message %s" % (placeholder, self.GetId()))
++ # Are there other placeholders with the same presentation?
++ # If so, they need to be the same.
++ for other in self.GetPlaceholders():
++ if placeholder.GetPresentation() == other.GetPresentation():
++ if not placeholder.EqualTo(other):
++ raise MessageTranslationError(
++ "Conflicting declarations of %s within message" %
++ placeholder.GetPresentation())
++ # update placeholder list
++ dup = 0
++ for item in self.__content:
++ if isinstance(item, Placeholder) and placeholder.EqualTo(item):
++ dup = 1
++ break
++ if not dup:
++ self.__placeholders.append(placeholder)
++
++ # update content
++ self.__content.append(placeholder)
++
++ # Strips leading and trailing whitespace, and returns a tuple
++ # containing the leading and trailing space that was removed.
++ def Strip(self):
++ leading = trailing = ''
++ if len(self.__content) > 0:
++ s0 = self.__content[0]
++ if not isinstance(s0, Placeholder):
++ s = s0.lstrip()
++ leading = s0[:-len(s)]
++ self.__content[0] = s
++
++ s0 = self.__content[-1]
++ if not isinstance(s0, Placeholder):
++ s = s0.rstrip()
++ trailing = s0[len(s):]
++ self.__content[-1] = s
++ return leading, trailing
++
++ # Return the id of this message
++ def GetId(self):
++ if self.__id is None:
++ return self.GenerateId()
++ return self.__id
++
++ # Set the id of this message
++ def SetId(self, id):
++ if id is None:
++ self.__id = None
++ else:
++ self.__id = str(id) # Treat numerical ids as strings
++
++ # Return content of this message as a list (internal use only)
++ def GetContent(self):
++ return self.__content
++
++ # Return a human-readable version of this message
++ def GetPresentableContent(self):
++ presentable_content = ""
++ for item in self.__content:
++ if isinstance(item, Placeholder):
++ presentable_content += item.GetPresentation()
++ else:
++ presentable_content += item
++
++ return presentable_content
++
++ # Return a fragment of a message in escaped format
++ def EscapeFragment(self, fragment):
++ return fragment.replace('%', '%%')
++
++ # Return the "original" version of this message, doing %-escaping
++ # properly. If source_msg is specified, the placeholder original
++ # information inside source_msg will be used instead.
++ def GetOriginalContent(self, source_msg = None):
++ original_content = ""
++ for item in self.__content:
++ if isinstance(item, Placeholder):
++ if source_msg:
++ ph = source_msg.GetPlaceholder(item.GetPresentation())
++ if not ph:
++ raise MessageTranslationError(
++ "Placeholder %s doesn't exist in message: %s" %
++ (item.GetPresentation(), source_msg))
++ original_content += ph.GetOriginal()
++ else:
++ original_content += item.GetOriginal()
++ else:
++ original_content += self.EscapeFragment(item)
++ return original_content
++
++ # Return the example of this message
++ def GetExampleContent(self):
++ example_content = ""
++ for item in self.__content:
++ if isinstance(item, Placeholder):
++ example_content += item.GetExample()
++ else:
++ example_content += item
++ return example_content
++
++ # Return a list of all unique placeholders in this message
++ def GetPlaceholders(self):
++ return self.__placeholders
++
++ # Return a placeholder in this message
++ def GetPlaceholder(self, presentation):
++ for item in self.__content:
++ if (isinstance(item, Placeholder) and
++ item.GetPresentation() == presentation):
++ return item
++ return None
++
++ # Return this message's description
++ def GetDescription(self):
++ return self.__description
++
++ # Add a message source
++ def AddSource(self, source):
++ self.__sources.append(source)
++
++ # Return this message's sources as a list
++ def GetSources(self):
++ return self.__sources
++
++ # Return this message's sources as a string
++ def GetSourcesAsText(self, delimiter = "; "):
++ return delimiter.join(self.__sources)
++
++ # Set the obsolete flag for a message (internal use only)
++ def SetObsolete(self):
++ self.__obsolete = 1
++
++ # Get the obsolete flag for a message (internal use only)
++ def IsObsolete(self):
++ return self.__obsolete
++
++ # Get the sequence number (0 by default)
++ def GetSequenceNumber(self):
++ return self.__sequence_number
++
++ # Set the sequence number
++ def SetSequenceNumber(self, number):
++ self.__sequence_number = number
++
++ # Increment instance counter
++ def AddInstance(self):
++ self.__num_instances += 1
++
++ # Return instance count
++ def GetNumInstances(self):
++ return self.__num_instances
++
++ def GetErrors(self, from_tc=0):
++ """
++ Returns a description of the problem if the message is not
++ syntactically valid, or None if everything is fine.
++
++ Args:
++ from_tc: indicates whether this message came from the TC. We let
++ the TC get away with some things we normally wouldn't allow for
++ historical reasons.
++ """
++ # check that placeholders are unambiguous
++ pos = 0
++ phs = {}
++ for item in self.__content:
++ if isinstance(item, Placeholder):
++ phs[pos] = item
++ pos += len(item.GetPresentation())
++ else:
++ pos += len(item)
++ presentation = self.GetPresentableContent()
++ for ph in self.GetPlaceholders():
++ for pos in FindOverlapping(presentation, ph.GetPresentation()):
++ # message contains the same text as a placeholder presentation
++ other_ph = phs.get(pos)
++ if ((not other_ph
++ and not IsSubstringInPlaceholder(pos, len(ph.GetPresentation()), phs))
++ or
++ (other_ph and len(other_ph.GetPresentation()) < len(ph.GetPresentation()))):
++ return "message contains placeholder name '%s':\n%s" % (
++ ph.GetPresentation(), presentation)
++ return None
++
++
++ def __CopyTo(self, other):
++ """
++ Returns a copy of this BaseMessage.
++ """
++ assert isinstance(other, self.__class__) or isinstance(self, other.__class__)
++ other.__source_encoding = self.__source_encoding
++ other.__content = self.__content[:]
++ other.__description = self.__description
++ other.__id = self.__id
++ other.__num_instances = self.__num_instances
++ other.__obsolete = self.__obsolete
++ other.__name = self.__name
++ other.__placeholders = self.__placeholders[:]
++ other.__sequence_number = self.__sequence_number
++ other.__sources = self.__sources[:]
++
++ return other
++
++ def HasText(self):
++ """Returns true iff this message has anything other than placeholders."""
++ for item in self.__content:
++ if not isinstance(item, Placeholder):
++ return True
++ return False
++
++# --------------------------------------------------------
++# The Message class represents original (English) messages
++
++class Message(BaseMessage):
++ # See BaseMessage constructor
++ def __init__(self, source_encoding, text=None, id=None,
++ description=None, meaning="", placeholders=None,
++ source=None, sequence_number=0, clone_from=None,
++ time_created=0, name=None, is_hidden = 0):
++
++ if clone_from is not None:
++ BaseMessage.__init__(self, None, clone_from=clone_from)
++ self.__meaning = clone_from.__meaning
++ self.__time_created = clone_from.__time_created
++ self.__is_hidden = clone_from.__is_hidden
++ return
++
++ BaseMessage.__init__(self, source_encoding, text, id, description,
++ placeholders, source, sequence_number,
++ name=name)
++ self.__meaning = meaning
++ self.__time_created = time_created
++ self.SetIsHidden(is_hidden)
++
++ # String representation
++ def __str__(self):
++ s = 'source: %s, id: %s, content: "%s", meaning: "%s", ' \
++ 'description: "%s"' % \
++ (self.GetSourcesAsText(), self.GetId(), self.GetPresentableContent(),
++ self.__meaning, self.GetDescription())
++ if self.GetName() is not None:
++ s += ', name: "%s"' % self.GetName()
++ placeholders = self.GetPlaceholders()
++ for i in range(len(placeholders)):
++ s += ", placeholder[%d]: %s" % (i, placeholders[i])
++ return s
++
++ # Strips leading and trailing whitespace, and returns a tuple
++ # containing the leading and trailing space that was removed.
++ def Strip(self):
++ leading = trailing = ''
++ content = self.GetContent()
++ if len(content) > 0:
++ s0 = content[0]
++ if not isinstance(s0, Placeholder):
++ s = s0.lstrip()
++ leading = s0[:-len(s)]
++ content[0] = s
++
++ s0 = content[-1]
++ if not isinstance(s0, Placeholder):
++ s = s0.rstrip()
++ trailing = s0[len(s):]
++ content[-1] = s
++ return leading, trailing
++
++ # Generate an id by hashing message content
++ def GenerateId(self):
++ self.SetId(GenerateMessageId(self.GetPresentableContent(),
++ self.__meaning))
++ return self.GetId()
++
++ def GetMeaning(self):
++ return self.__meaning
++
++ def GetTimeCreated(self):
++ return self.__time_created
++
++ # Equality operator
++ def EqualTo(self, other, strict = 1):
++ # Check id, meaning, content
++ if self.GetId() != other.GetId():
++ return 0
++ if self.__meaning != other.__meaning:
++ return 0
++ if self.GetPresentableContent() != other.GetPresentableContent():
++ return 0
++ # Check descriptions if comparison is strict
++ if (strict and
++ self.GetDescription() is not None and
++ other.GetDescription() is not None and
++ self.GetDescription() != other.GetDescription()):
++ return 0
++ # Check placeholders
++ ph1 = self.GetPlaceholders()
++ ph2 = other.GetPlaceholders()
++ if len(ph1) != len(ph2):
++ return 0
++ for i in range(len(ph1)):
++ if not ph1[i].EqualTo(ph2[i], strict):
++ return 0
++
++ return 1
++
++ def Copy(self):
++ """
++ Returns a copy of this Message.
++ """
++ assert isinstance(self, Message)
++ return Message(None, clone_from=self)
++
++ def SetIsHidden(self, is_hidden):
++ """Sets whether this message should be hidden.
++
++ Args:
++ is_hidden : 0 or 1 - if the message should be hidden, 0 otherwise
++ """
++ if is_hidden not in [0, 1]:
++ raise MessageTranslationError("is_hidden must be 0 or 1, got %s")
++ self.__is_hidden = is_hidden
++
++ def IsHidden(self):
++ """Returns 1 if this message is hidden, and 0 otherwise."""
++ return self.__is_hidden
++
++# ----------------------------------------------------
++# The Translation class represents translated messages
++
++class Translation(BaseMessage):
++ # See BaseMessage constructor
++ def __init__(self, source_encoding, text=None, id=None,
++ description=None, placeholders=None, source=None,
++ sequence_number=0, clone_from=None, ignore_ph_errors=0,
++ name=None):
++ if clone_from is not None:
++ BaseMessage.__init__(self, None, clone_from=clone_from)
++ return
++
++ BaseMessage.__init__(self, source_encoding, text, id, description,
++ placeholders, source, sequence_number,
++ ignore_ph_errors=ignore_ph_errors, name=name)
++
++ # String representation
++ def __str__(self):
++ s = 'source: %s, id: %s, content: "%s", description: "%s"' % \
++ (self.GetSourcesAsText(), self.GetId(), self.GetPresentableContent(),
++ self.GetDescription());
++ placeholders = self.GetPlaceholders()
++ for i in range(len(placeholders)):
++ s += ", placeholder[%d]: %s" % (i, placeholders[i])
++ return s
++
++ # Equality operator
++ def EqualTo(self, other, strict=1):
++ # Check id and content
++ if self.GetId() != other.GetId():
++ return 0
++ if self.GetPresentableContent() != other.GetPresentableContent():
++ return 0
++ # Check placeholders
++ ph1 = self.GetPlaceholders()
++ ph2 = other.GetPlaceholders()
++ if len(ph1) != len(ph2):
++ return 0
++ for i in range(len(ph1)):
++ if not ph1[i].EqualTo(ph2[i], strict):
++ return 0
++
++ return 1
++
++ def Copy(self):
++ """
++ Returns a copy of this Translation.
++ """
++ return Translation(None, clone_from=self)
+diff --git a/tools/grit/grit/format/__init__.py b/tools/grit/grit/format/__init__.py
+new file mode 100644
+index 0000000000..55d56b8cfd
+--- /dev/null
++++ b/tools/grit/grit/format/__init__.py
+@@ -0,0 +1,8 @@
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++'''Module grit.format
++'''
++
++pass
+diff --git a/tools/grit/grit/format/android_xml.py b/tools/grit/grit/format/android_xml.py
+new file mode 100644
+index 0000000000..7eb288891f
+--- /dev/null
++++ b/tools/grit/grit/format/android_xml.py
+@@ -0,0 +1,212 @@
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++"""Produces localized strings.xml files for Android.
++
++In cases where an "android" type output file is requested in a grd, the classes
++in android_xml will process the messages and translations to produce a valid
++strings.xml that is properly localized with the specified language.
++
++For example if the following output tag were to be included in a grd file
++ <outputs>
++ ...
++ <output filename="values-es/strings.xml" type="android" lang="es" />
++ ...
++ </outputs>
++
++for a grd file with the following messages:
++
++ <message name="IDS_HELLO" desc="Simple greeting">Hello</message>
++ <message name="IDS_WORLD" desc="The world">world</message>
++
++and there existed an appropriate xtb file containing the Spanish translations,
++then the output would be:
++
++ <?xml version="1.0" encoding="utf-8"?>
++ <resources xmlns:android="http://schemas.android.com/apk/res/android">
++ <string name="hello">"Hola"</string>
++ <string name="world">"mundo"</string>
++ </resources>
++
++which would be written to values-es/strings.xml and usable by the Android
++resource framework.
++
++Advanced usage
++--------------
++
++To process only certain messages in a grd file, tag each desired message by
++adding "android_java" to formatter_data. Then set the environmental variable
++ANDROID_JAVA_TAGGED_ONLY to "true" when building the grd file. For example:
++
++ <message name="IDS_HELLO" formatter_data="android_java">Hello</message>
++
++To generate Android plurals (aka "quantity strings"), use the ICU plural syntax
++in the grd file. This will automatically be transformed into a <purals> element
++in the output xml file. For example:
++
++ <message name="IDS_CATS">
++ {NUM_CATS, plural,
++ =1 {1 cat}
++ other {# cats}}
++ </message>
++
++ will produce
++
++ <plurals name="cats">
++ <item quantity="one">1 Katze</item>
++ <item quantity="other">%d Katzen</item>
++ </plurals>
++"""
++
++from __future__ import print_function
++
++import os
++import re
++import xml.sax.saxutils
++
++from grit import lazy_re
++from grit.node import message
++
++
++# When this environmental variable has value "true", only tagged messages will
++# be outputted.
++_TAGGED_ONLY_ENV_VAR = 'ANDROID_JAVA_TAGGED_ONLY'
++_TAGGED_ONLY_DEFAULT = False
++
++# In tagged-only mode, only messages with this tag will be ouputted.
++_EMIT_TAG = 'android_java'
++
++_NAME_PATTERN = lazy_re.compile(r'IDS_(?P<name>[A-Z0-9_]+)\Z')
++
++# Most strings are output as a <string> element. Note the double quotes
++# around the value to preserve whitespace.
++_STRING_TEMPLATE = u'<string name="%s">"%s"</string>\n'
++
++# Some strings are output as a <plurals> element.
++_PLURALS_TEMPLATE = '<plurals name="%s">\n%s</plurals>\n'
++_PLURALS_ITEM_TEMPLATE = ' <item quantity="%s">%s</item>\n'
++
++# Matches e.g. "{HELLO, plural, HOW ARE YOU DOING}", while capturing
++# "HOW ARE YOU DOING" in <items>.
++_PLURALS_PATTERN = lazy_re.compile(r'\{[A-Z_]+,\s*plural,(?P<items>.*)\}$',
++ flags=re.S)
++
++# Repeatedly matched against the <items> capture in _PLURALS_PATTERN,
++# to match "<quantity>{<value>}".
++_PLURALS_ITEM_PATTERN = lazy_re.compile(r'(?P<quantity>\S+?)\s*'
++ r'\{(?P<value>.*?)\}')
++_PLURALS_QUANTITY_MAP = {
++ '=0': 'zero',
++ 'zero': 'zero',
++ '=1': 'one',
++ 'one': 'one',
++ '=2': 'two',
++ 'two': 'two',
++ 'few': 'few',
++ 'many': 'many',
++ 'other': 'other',
+}
+
-+bool ChromeClassTester::InImplementationFile(SourceLocation record_location) {
-+ std::string filename;
-+ if (!GetFilename(record_location, &filename))
-+ return false;
+
-+ if (ends_with(filename, ".cc") || ends_with(filename, ".cpp") ||
-+ ends_with(filename, ".mm")) {
-+ return true;
++def Format(root, lang='en', output_dir='.'):
++ yield ('<?xml version="1.0" encoding="utf-8"?>\n'
++ '<resources '
++ 'xmlns:android="http://schemas.android.com/apk/res/android">\n')
++
++ tagged_only = _TAGGED_ONLY_DEFAULT
++ if _TAGGED_ONLY_ENV_VAR in os.environ:
++ tagged_only = os.environ[_TAGGED_ONLY_ENV_VAR].lower()
++ if tagged_only == 'true':
++ tagged_only = True
++ elif tagged_only == 'false':
++ tagged_only = False
++ else:
++ raise Exception('env variable ANDROID_JAVA_TAGGED_ONLY must have value '
++ 'true or false. Invalid value: %s' % tagged_only)
++
++ for item in root.ActiveDescendants():
++ with item:
++ if ShouldOutputNode(item, tagged_only):
++ yield _FormatMessage(item, lang)
++
++ yield '</resources>\n'
++
++
++def ShouldOutputNode(node, tagged_only):
++ """Returns true if node should be outputted.
++
++ Args:
++ node: a Node from the grd dom
++ tagged_only: true, if only tagged messages should be outputted
++ """
++ return (isinstance(node, message.MessageNode) and
++ (not tagged_only or _EMIT_TAG in node.formatter_data))
++
++
++def _FormatPluralMessage(message):
++ """Compiles ICU plural syntax to the body of an Android <plurals> element.
++
++ 1. In a .grd file, we can write a plural string like this:
++
++ <message name="IDS_THINGS">
++ {NUM_THINGS, plural,
++ =1 {1 thing}
++ other {# things}}
++ </message>
++
++ 2. The Android equivalent looks like this:
++
++ <plurals name="things">
++ <item quantity="one">1 thing</item>
++ <item quantity="other">%d things</item>
++ </plurals>
++
++ This method takes the body of (1) and converts it to the body of (2).
++
++ If the message is *not* a plural string, this function returns `None`.
++ If the message includes quantities without an equivalent format in Android,
++ it raises an exception.
++ """
++ ret = {}
++ plural_match = _PLURALS_PATTERN.match(message)
++ if not plural_match:
++ return None
++ body_in = plural_match.group('items').strip()
++ lines = []
++ quantities_so_far = set()
++ for item_match in _PLURALS_ITEM_PATTERN.finditer(body_in):
++ quantity_in = item_match.group('quantity')
++ quantity_out = _PLURALS_QUANTITY_MAP.get(quantity_in)
++ value_in = item_match.group('value')
++ value_out = '"' + value_in.replace('#', '%d') + '"'
++ if quantity_out:
++ # only one line per quantity out (https://crbug.com/787488)
++ if quantity_out not in quantities_so_far:
++ quantities_so_far.add(quantity_out)
++ lines.append(_PLURALS_ITEM_TEMPLATE % (quantity_out, value_out))
++ else:
++ raise Exception('Unsupported plural quantity for android '
++ 'strings.xml: %s' % quantity_in)
++ return ''.join(lines)
++
++
++def _FormatMessage(item, lang):
++ """Writes out a single string as a <resource/> element."""
++
++ mangled_name = item.GetTextualIds()[0]
++ match = _NAME_PATTERN.match(mangled_name)
++ if not match:
++ raise Exception('Unexpected resource name: %s' % mangled_name)
++ name = match.group('name').lower()
++
++ value = item.ws_at_start + item.Translate(lang) + item.ws_at_end
++ # Replace < > & with &lt; &gt; &amp; to ensure we generate valid XML and
++ # replace ' " with \' \" to conform to Android's string formatting rules.
++ value = xml.sax.saxutils.escape(value, {"'": "\\'", '"': '\\"'})
++
++ plurals = _FormatPluralMessage(value)
++ if plurals:
++ return _PLURALS_TEMPLATE % (name, plurals)
++ else:
++ return _STRING_TEMPLATE % (name, value)
+diff --git a/tools/grit/grit/format/android_xml_unittest.py b/tools/grit/grit/format/android_xml_unittest.py
+new file mode 100644
+index 0000000000..d9f476fddf
+--- /dev/null
++++ b/tools/grit/grit/format/android_xml_unittest.py
+@@ -0,0 +1,149 @@
++#!/usr/bin/env python
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++"""Unittest for android_xml.py."""
++
++from __future__ import print_function
++
++import os
++import sys
++import unittest
++
++if __name__ == '__main__':
++ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
++
++from six import StringIO
++
++from grit import util
++from grit.format import android_xml
++from grit.node import message
++from grit.tool import build
++
++
++class AndroidXmlUnittest(unittest.TestCase):
++
++ def testMessages(self):
++ root = util.ParseGrdForUnittest(r"""
++ <messages>
++ <message name="IDS_SIMPLE" desc="A vanilla string">
++ Martha
++ </message>
++ <message name="IDS_ONE_LINE" desc="On one line">sat and wondered</message>
++ <message name="IDS_QUOTES" desc="A string with quotation marks">
++ out loud, "Why don't I build a flying car?"
++ </message>
++ <message name="IDS_MULTILINE" desc="A string split over several lines">
++ She gathered
++wood, charcoal, and
++a sledge hammer.
++ </message>
++ <message name="IDS_WHITESPACE" desc="A string with extra whitespace.">
++ ''' How old fashioned -- she thought. '''
++ </message>
++ <message name="IDS_PLACEHOLDERS" desc="A string with placeholders">
++ I'll buy a <ph name="WAVELENGTH">%d<ex>200</ex></ph> nm laser at <ph name="STORE_NAME">%s<ex>the grocery store</ex></ph>.
++ </message>
++ <message name="IDS_PLURALS" desc="A string using the ICU plural format">
++ {NUM_THINGS, plural,
++ =1 {Maybe I'll get one laser.}
++ other {Maybe I'll get # lasers.}}
++ </message>
++ <message name="IDS_PLURALS_NO_SPACE" desc="A string using the ICU plural format with no space">
++ {NUM_MISSISSIPPIS, plural,
++ =1{OneMississippi}other{ManyMississippis}}
++ </message>
++ </messages>
++ """)
++
++ buf = StringIO()
++ build.RcBuilder.ProcessNode(root, DummyOutput('android', 'en'), buf)
++ output = buf.getvalue()
++ expected = r"""
++<?xml version="1.0" encoding="utf-8"?>
++<resources xmlns:android="http://schemas.android.com/apk/res/android">
++<string name="simple">"Martha"</string>
++<string name="one_line">"sat and wondered"</string>
++<string name="quotes">"out loud, \"Why don\'t I build a flying car?\""</string>
++<string name="multiline">"She gathered
++wood, charcoal, and
++a sledge hammer."</string>
++<string name="whitespace">" How old fashioned -- she thought. "</string>
++<string name="placeholders">"I\'ll buy a %d nm laser at %s."</string>
++<plurals name="plurals">
++ <item quantity="one">"Maybe I\'ll get one laser."</item>
++ <item quantity="other">"Maybe I\'ll get %d lasers."</item>
++</plurals>
++<plurals name="plurals_no_space">
++ <item quantity="one">"OneMississippi"</item>
++ <item quantity="other">"ManyMississippis"</item>
++</plurals>
++</resources>
++"""
++ self.assertEqual(output.strip(), expected.strip())
++
++
++ def testConflictingPlurals(self):
++ root = util.ParseGrdForUnittest(r"""
++ <messages>
++ <message name="IDS_PLURALS" desc="A string using the ICU plural format">
++ {NUM_THINGS, plural,
++ =1 {Maybe I'll get one laser.}
++ one {Maybe I'll get one laser.}
++ other {Maybe I'll get # lasers.}}
++ </message>
++ </messages>
++ """)
++
++ buf = StringIO()
++ build.RcBuilder.ProcessNode(root, DummyOutput('android', 'en'), buf)
++ output = buf.getvalue()
++ expected = r"""
++<?xml version="1.0" encoding="utf-8"?>
++<resources xmlns:android="http://schemas.android.com/apk/res/android">
++<plurals name="plurals">
++ <item quantity="one">"Maybe I\'ll get one laser."</item>
++ <item quantity="other">"Maybe I\'ll get %d lasers."</item>
++</plurals>
++</resources>
++"""
++ self.assertEqual(output.strip(), expected.strip())
++
++
++ def testTaggedOnly(self):
++ root = util.ParseGrdForUnittest(r"""
++ <messages>
++ <message name="IDS_HELLO" desc="" formatter_data="android_java">
++ Hello
++ </message>
++ <message name="IDS_WORLD" desc="">
++ world
++ </message>
++ </messages>
++ """)
++
++ msg_hello, msg_world = root.GetChildrenOfType(message.MessageNode)
++ self.assertTrue(android_xml.ShouldOutputNode(msg_hello, tagged_only=True))
++ self.assertFalse(android_xml.ShouldOutputNode(msg_world, tagged_only=True))
++ self.assertTrue(android_xml.ShouldOutputNode(msg_hello, tagged_only=False))
++ self.assertTrue(android_xml.ShouldOutputNode(msg_world, tagged_only=False))
++
++
++class DummyOutput(object):
++
++ def __init__(self, type, language):
++ self.type = type
++ self.language = language
++
++ def GetType(self):
++ return self.type
++
++ def GetLanguage(self):
++ return self.language
++
++ def GetOutputFilename(self):
++ return 'hello.gif'
++
++if __name__ == '__main__':
++ unittest.main()
+diff --git a/tools/grit/grit/format/c_format.py b/tools/grit/grit/format/c_format.py
+new file mode 100644
+index 0000000000..16809a9f70
+--- /dev/null
++++ b/tools/grit/grit/format/c_format.py
+@@ -0,0 +1,95 @@
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++"""Formats as a .C file for compilation.
++"""
++
++from __future__ import print_function
++
++import codecs
++import os
++import re
++
++import six
++
++from grit import util
++
++
++def _FormatHeader(root, output_dir):
++ """Returns the required preamble for C files."""
++ # Find the location of the resource header file, so that we can include
++ # it.
++ resource_header = 'resource.h' # fall back to this
++ for output in root.GetOutputFiles():
++ if output.attrs['type'] == 'rc_header':
++ resource_header = os.path.abspath(output.GetOutputFilename())
++ resource_header = util.MakeRelativePath(output_dir, resource_header)
++ return """// This file is automatically generated by GRIT. Do not edit.
++
++#include "%s"
++
++// All strings are UTF-8
++""" % (resource_header)
++# end _FormatHeader() function
++
++
++def Format(root, lang='en', output_dir='.'):
++ """Outputs a C switch statement representing the string table."""
++ from grit.node import message
++ assert isinstance(lang, six.string_types)
++
++ yield _FormatHeader(root, output_dir)
++
++ yield 'const char* GetString(int id) {\n switch (id) {'
++
++ for item in root.ActiveDescendants():
++ with item:
++ if isinstance(item, message.MessageNode):
++ yield _FormatMessage(item, lang)
++
++ yield '\n default:\n return 0;\n }\n}\n'
++
++
++def _HexToOct(match):
++ "Return the octal form of the hex numbers"
++ hex = match.group("hex")
++ result = ""
++ while len(hex):
++ next_num = int(hex[2:4], 16)
++ result += "\\" + '%03o' % next_num
++ hex = hex[4:]
++ return match.group("escaped_backslashes") + result
++
++
++def _FormatMessage(item, lang):
++ """Format a single <message> element."""
++
++ message = item.ws_at_start + item.Translate(lang) + item.ws_at_end
++ # Output message with non-ascii chars escaped as octal numbers C's grammar
++ # allows escaped hexadecimal numbers to be infinite, but octal is always of
++ # the form \OOO. Python 3 doesn't support string-escape, so we have to jump
++ # through some hoops here via codecs.escape_encode.
++ # This basically does:
++ # - message - the starting string
++ # - message.encode(...) - convert to bytes
++ # - codecs.escape_encode(...) - convert non-ASCII bytes to \x## escapes
++ # - (...).decode() - convert bytes back to a string
++ message = codecs.escape_encode(message.encode('utf-8'))[0].decode('utf-8')
++ # an escaped char is (\xHH)+ but only if the initial
++ # backslash is not escaped.
++ not_a_backslash = r"(^|[^\\])" # beginning of line or a non-backslash char
++ escaped_backslashes = not_a_backslash + r"(\\\\)*"
++ hex_digits = r"((\\x)[0-9a-f]{2})+"
++ two_digit_hex_num = re.compile(
++ r"(?P<escaped_backslashes>%s)(?P<hex>%s)"
++ % (escaped_backslashes, hex_digits))
++ message = two_digit_hex_num.sub(_HexToOct, message)
++ # unescape \ (convert \\ back to \)
++ message = message.replace('\\\\', '\\')
++ message = message.replace('"', '\\"')
++ message = util.LINEBREAKS.sub(r'\\n', message)
++
++ name_attr = item.GetTextualIds()[0]
++
++ return '\n case %s:\n return "%s";' % (name_attr, message)
+diff --git a/tools/grit/grit/format/c_format_unittest.py b/tools/grit/grit/format/c_format_unittest.py
+new file mode 100644
+index 0000000000..380120c42f
+--- /dev/null
++++ b/tools/grit/grit/format/c_format_unittest.py
+@@ -0,0 +1,81 @@
++#!/usr/bin/env python
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++"""Unittest for c_format.py.
++"""
++
++from __future__ import print_function
++
++import os
++import sys
++if __name__ == '__main__':
++ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
++
++import unittest
++
++from six import StringIO
++
++from grit import util
++from grit.tool import build
++
++
++class CFormatUnittest(unittest.TestCase):
++
++ def testMessages(self):
++ root = util.ParseGrdForUnittest(u"""
++ <messages>
++ <message name="IDS_QUESTIONS">Do you want to play questions?</message>
++ <message name="IDS_QUOTES">
++ "What's in a name, <ph name="NAME">%s<ex>Brandon</ex></ph>?"
++ </message>
++ <message name="IDS_LINE_BREAKS">
++ Was that rhetoric?
++No.
++Statement. Two all. Game point.
++</message>
++ <message name="IDS_NON_ASCII">
++ \u00f5\\xc2\\xa4\\\u00a4\\\\xc3\\xb5\u4924
++ </message>
++ </messages>
++ """)
++
++ buf = StringIO()
++ build.RcBuilder.ProcessNode(root, DummyOutput('c_format', 'en'), buf)
++ output = util.StripBlankLinesAndComments(buf.getvalue())
++ self.assertEqual(u"""\
++#include "resource.h"
++const char* GetString(int id) {
++ switch (id) {
++ case IDS_QUESTIONS:
++ return "Do you want to play questions?";
++ case IDS_QUOTES:
++ return "\\"What\\'s in a name, %s?\\"";
++ case IDS_LINE_BREAKS:
++ return "Was that rhetoric?\\nNo.\\nStatement. Two all. Game point.";
++ case IDS_NON_ASCII:
++ return "\\303\\265\\xc2\\xa4\\\\302\\244\\\\xc3\\xb5\\344\\244\\244";
++ default:
++ return 0;
+ }
++}""", output)
+
-+ return false;
-+}
+
-+void ChromeClassTester::BuildBannedLists() {
-+ banned_namespaces_.push_back("std");
-+ banned_namespaces_.push_back("__gnu_cxx");
-+ banned_namespaces_.push_back("WebKit");
-+
-+ banned_directories_.push_back("third_party/");
-+ banned_directories_.push_back("native_client/");
-+ banned_directories_.push_back("breakpad/");
-+ banned_directories_.push_back("courgette/");
-+ banned_directories_.push_back("pdf/");
-+ banned_directories_.push_back("ppapi/");
-+ banned_directories_.push_back("usr/");
-+ banned_directories_.push_back("testing/");
-+ banned_directories_.push_back("googleurl/");
-+ banned_directories_.push_back("v8/");
-+ banned_directories_.push_back("dart/");
-+ banned_directories_.push_back("sdch/");
-+ banned_directories_.push_back("icu4c/");
-+ banned_directories_.push_back("frameworks/");
-+
-+ // Don't check autogenerated headers.
-+ // Make puts them below $(builddir_name)/.../gen and geni.
-+ // Ninja puts them below OUTPUT_DIR/.../gen
-+ // Xcode has a fixed output directory for everything.
-+ banned_directories_.push_back("gen/");
-+ banned_directories_.push_back("geni/");
-+ banned_directories_.push_back("xcodebuild/");
-+
-+ // You are standing in a mazy of twisty dependencies, all resolved by
-+ // putting everything in the header.
-+ banned_directories_.push_back("automation/");
-+
-+ // Don't check system headers.
-+ banned_directories_.push_back("/Developer/");
-+
-+ // Used in really low level threading code that probably shouldn't be out of
-+ // lined.
-+ ignored_record_names_.insert("ThreadLocalBoolean");
-+
-+ // A complicated pickle derived struct that is all packed integers.
-+ ignored_record_names_.insert("Header");
-+
-+ // Part of the GPU system that uses multiple included header
-+ // weirdness. Never getting this right.
-+ ignored_record_names_.insert("Validators");
-+
-+ // Has a UNIT_TEST only constructor. Isn't *terribly* complex...
-+ ignored_record_names_.insert("AutocompleteController");
-+ ignored_record_names_.insert("HistoryURLProvider");
-+
-+ // Because of chrome frame
-+ ignored_record_names_.insert("ReliabilityTestSuite");
-+
-+ // Used over in the net unittests. A large enough bundle of integers with 1
-+ // non-pod class member. Probably harmless.
-+ ignored_record_names_.insert("MockTransaction");
-+
-+ // Used heavily in ui_unittests and once in views_unittests. Fixing this
-+ // isn't worth the overhead of an additional library.
-+ ignored_record_names_.insert("TestAnimationDelegate");
-+
-+ // Part of our public interface that nacl and friends use. (Arguably, this
-+ // should mean that this is a higher priority but fixing this looks hard.)
-+ ignored_record_names_.insert("PluginVersionInfo");
-+}
-+
-+std::string ChromeClassTester::GetNamespaceImpl(const DeclContext* context,
-+ const std::string& candidate) {
-+ switch (context->getDeclKind()) {
-+ case Decl::TranslationUnit: {
-+ return candidate;
-+ }
-+ case Decl::Namespace: {
-+ const NamespaceDecl* decl = dyn_cast<NamespaceDecl>(context);
-+ std::string name_str;
-+ llvm::raw_string_ostream OS(name_str);
-+ if (decl->isAnonymousNamespace())
-+ OS << "<anonymous namespace>";
-+ else
-+ OS << *decl;
-+ return GetNamespaceImpl(context->getParent(),
-+ OS.str());
-+ }
-+ default: {
-+ return GetNamespaceImpl(context->getParent(), candidate);
++class DummyOutput(object):
++
++ def __init__(self, type, language):
++ self.type = type
++ self.language = language
++
++ def GetType(self):
++ return self.type
++
++ def GetLanguage(self):
++ return self.language
++
++ def GetOutputFilename(self):
++ return 'hello.gif'
++
++if __name__ == '__main__':
++ unittest.main()
+diff --git a/tools/grit/grit/format/chrome_messages_json.py b/tools/grit/grit/format/chrome_messages_json.py
+new file mode 100644
+index 0000000000..88ec1d914b
+--- /dev/null
++++ b/tools/grit/grit/format/chrome_messages_json.py
+@@ -0,0 +1,59 @@
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++"""Formats as a .json file that can be used to localize Google Chrome
++extensions."""
++
++from __future__ import print_function
++
++from json import JSONEncoder
++
++from grit import constants
++from grit.node import message
++
++def Format(root, lang='en', output_dir='.'):
++ """Format the messages as JSON."""
++ yield '{'
++
++ encoder = JSONEncoder(ensure_ascii=False)
++ format = '"%s":{"message":%s%s}'
++ placeholder_format = '"%i":{"content":"$%i"}'
++ first = True
++ for child in root.ActiveDescendants():
++ if isinstance(child, message.MessageNode):
++ id = child.attrs['name']
++ if id.startswith('IDR_') or id.startswith('IDS_'):
++ id = id[4:]
++
++ translation_missing = child.GetCliques()[0].clique.get(lang) is None;
++ if (child.ShouldFallbackToEnglish() and translation_missing and
++ lang != constants.FAKE_BIDI):
++ # Skip the string if it's not translated. Chrome will fallback
++ # to English automatically.
++ continue
++
++ loc_message = encoder.encode(child.ws_at_start + child.Translate(lang) +
++ child.ws_at_end)
++
++ # Replace $n place-holders with $n$ and add an appropriate "placeholders"
++ # entry. Note that chrome.i18n.getMessage only supports 9 placeholders:
++ # https://developer.chrome.com/extensions/i18n#method-getMessage
++ placeholders = ''
++ for i in range(1, 10):
++ if loc_message.find('$%d' % i) == -1:
++ break
++ loc_message = loc_message.replace('$%d' % i, '$%d$' % i)
++ if placeholders:
++ placeholders += ','
++ placeholders += placeholder_format % (i, i)
++
++ if not first:
++ yield ','
++ first = False
++
++ if placeholders:
++ placeholders = ',"placeholders":{%s}' % placeholders
++ yield format % (id, loc_message, placeholders)
++
++ yield '}'
+diff --git a/tools/grit/grit/format/chrome_messages_json_unittest.py b/tools/grit/grit/format/chrome_messages_json_unittest.py
+new file mode 100644
+index 0000000000..a54e6bdc1c
+--- /dev/null
++++ b/tools/grit/grit/format/chrome_messages_json_unittest.py
+@@ -0,0 +1,190 @@
++#!/usr/bin/env python
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++"""Unittest for chrome_messages_json.py.
++"""
++
++from __future__ import print_function
++
++import json
++import os
++import sys
++if __name__ == '__main__':
++ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
++
++import unittest
++
++from six import StringIO
++
++from grit import grd_reader
++from grit import util
++from grit.tool import build
++
++class ChromeMessagesJsonFormatUnittest(unittest.TestCase):
++
++ # The default unittest diff limit is too low for our unittests.
++ # Allow the framework to show the full diff output all the time.
++ maxDiff = None
++
++ def testMessages(self):
++ root = util.ParseGrdForUnittest(u"""
++ <messages>
++ <message name="IDS_SIMPLE_MESSAGE">
++ Simple message.
++ </message>
++ <message name="IDS_QUOTES">
++ element\u2019s \u201c<ph name="NAME">%s<ex>name</ex></ph>\u201d attribute
++ </message>
++ <message name="IDS_PLACEHOLDERS">
++ <ph name="ERROR_COUNT">%1$d<ex>1</ex></ph> error, <ph name="WARNING_COUNT">%2$d<ex>1</ex></ph> warning
++ </message>
++ <message name="IDS_PLACEHOLDERS_SUBSTITUTED_BY_GETMESSAGE">
++ <ph name="BEGIN">$1<ex>a</ex></ph>test<ph name="END">$2<ex>b</ex></ph>
++ </message>
++ <message name="IDS_STARTS_WITH_SPACE">
++ ''' (<ph name="COUNT">%d<ex>2</ex></ph>)
++ </message>
++ <message name="IDS_ENDS_WITH_SPACE">
++ (<ph name="COUNT">%d<ex>2</ex></ph>) '''
++ </message>
++ <message name="IDS_SPACE_AT_BOTH_ENDS">
++ ''' (<ph name="COUNT">%d<ex>2</ex></ph>) '''
++ </message>
++ <message name="IDS_DOUBLE_QUOTES">
++ A "double quoted" message.
++ </message>
++ <message name="IDS_BACKSLASH">
++ \\
++ </message>
++ </messages>
++ """)
++
++ buf = StringIO()
++ build.RcBuilder.ProcessNode(root, DummyOutput('chrome_messages_json', 'en'),
++ buf)
++ output = buf.getvalue()
++ test = u"""
++{
++ "SIMPLE_MESSAGE": {
++ "message": "Simple message."
++ },
++ "QUOTES": {
++ "message": "element\u2019s \u201c%s\u201d attribute"
++ },
++ "PLACEHOLDERS": {
++ "message": "%1$d error, %2$d warning"
++ },
++ "PLACEHOLDERS_SUBSTITUTED_BY_GETMESSAGE": {
++ "message": "$1$test$2$",
++ "placeholders": {
++ "1": {
++ "content": "$1"
++ },
++ "2": {
++ "content": "$2"
++ }
+ }
++ },
++ "STARTS_WITH_SPACE": {
++ "message": " (%d)"
++ },
++ "ENDS_WITH_SPACE": {
++ "message": "(%d) "
++ },
++ "SPACE_AT_BOTH_ENDS": {
++ "message": " (%d) "
++ },
++ "DOUBLE_QUOTES": {
++ "message": "A \\"double quoted\\" message."
++ },
++ "BACKSLASH": {
++ "message": "\\\\"
+ }
+}
-+
-+bool ChromeClassTester::InBannedDirectory(SourceLocation loc) {
-+ std::string filename;
-+ if (!GetFilename(loc, &filename)) {
-+ // If the filename cannot be determined, simply treat this as a banned
-+ // location, instead of going through the full lookup process.
-+ return true;
++"""
++ self.assertEqual(json.loads(test), json.loads(output))
++
++ def testTranslations(self):
++ root = util.ParseGrdForUnittest("""
++ <messages>
++ <message name="ID_HELLO">Hello!</message>
++ <message name="ID_HELLO_USER">Hello <ph name="USERNAME">%s<ex>
++ Joi</ex></ph></message>
++ </messages>
++ """)
++
++ buf = StringIO()
++ build.RcBuilder.ProcessNode(root, DummyOutput('chrome_messages_json', 'fr'),
++ buf)
++ output = buf.getvalue()
++ test = u"""
++{
++ "ID_HELLO": {
++ "message": "H\u00e9P\u00e9ll\u00f4P\u00f4!"
++ },
++ "ID_HELLO_USER": {
++ "message": "H\u00e9P\u00e9ll\u00f4P\u00f4 %s"
+ }
++}
++"""
++ self.assertEqual(json.loads(test), json.loads(output))
++
++ def testSkipMissingTranslations(self):
++ grd = """<?xml version="1.0" encoding="UTF-8"?>
++<grit latest_public_release="2" current_release="3" source_lang_id="en"
++ base_dir="%s">
++ <outputs>
++ </outputs>
++ <release seq="3" allow_pseudo="False">
++ <messages fallback_to_english="true">
++ <message name="ID_HELLO_NO_TRANSLATION">Hello not translated</message>
++ </messages>
++ </release>
++</grit>"""
++ root = grd_reader.Parse(StringIO(grd), dir=".")
++
++ buf = StringIO()
++ build.RcBuilder.ProcessNode(root, DummyOutput('chrome_messages_json', 'fr'),
++ buf)
++ output = buf.getvalue()
++ test = u'{}'
++ self.assertEqual(test, output)
++
++ def testVerifyMinification(self):
++ root = util.ParseGrdForUnittest(u"""
++ <messages>
++ <message name="IDS">
++ <ph name="BEGIN">$1<ex>a</ex></ph>test<ph name="END">$2<ex>b</ex></ph>
++ </message>
++ </messages>
++ """)
++
++ buf = StringIO()
++ build.RcBuilder.ProcessNode(root, DummyOutput('chrome_messages_json', 'en'),
++ buf)
++ output = buf.getvalue()
++ test = (u'{"IDS":{"message":"$1$test$2$","placeholders":'
++ u'{"1":{"content":"$1"},"2":{"content":"$2"}}}}')
++ self.assertEqual(test, output)
++
++
++class DummyOutput(object):
++
++ def __init__(self, type, language):
++ self.type = type
++ self.language = language
++
++ def GetType(self):
++ return self.type
++
++ def GetLanguage(self):
++ return self.language
++
++ def GetOutputFilename(self):
++ return 'hello.gif'
+
-+ // We need to special case scratch space; which is where clang does its
-+ // macro expansion. We explicitly want to allow people to do otherwise bad
-+ // things through macros that were defined due to third party libraries.
-+ if (filename == "<scratch space>")
-+ return true;
+
-+ // Don't complain about autogenerated protobuf files.
-+ if (ends_with(filename, ".pb.h")) {
-+ return true;
-+ }
++if __name__ == '__main__':
++ unittest.main()
+diff --git a/tools/grit/grit/format/data_pack.py b/tools/grit/grit/format/data_pack.py
+new file mode 100644
+index 0000000000..f7128a4725
+--- /dev/null
++++ b/tools/grit/grit/format/data_pack.py
+@@ -0,0 +1,321 @@
++#!/usr/bin/env python
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
+
-+ // We need to munge the paths so that they are relative to the repository
-+ // srcroot. We first resolve the symlinktastic relative path and then
-+ // remove our known srcroot from it if needed.
-+ char resolvedPath[MAXPATHLEN];
-+ if (realpath(filename.c_str(), resolvedPath)) {
-+ filename = resolvedPath;
-+ }
++"""Support for formatting a data pack file used for platform agnostic resource
++files.
++"""
++
++from __future__ import print_function
++
++import collections
++import os
++import struct
++import sys
++if __name__ == '__main__':
++ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
+
-+ // On linux, chrome is often checked out to /usr/local/google. Due to the
-+ // "usr" rule in banned_directories_, all diagnostics would be suppressed
-+ // in that case. As a workaround, strip that prefix.
-+ filename = lstrip(filename, "/usr/local/google");
-+
-+ for (std::vector<std::string>::const_iterator it =
-+ banned_directories_.begin();
-+ it != banned_directories_.end(); ++it) {
-+ // If we can find any of the banned path components in this path, then
-+ // this file is rejected.
-+ size_t index = filename.find(*it);
-+ if (index != std::string::npos) {
-+ bool matches_full_dir_name = index == 0 || filename[index - 1] == '/';
-+ if ((*it)[0] == '/')
-+ matches_full_dir_name = true;
-+ if (matches_full_dir_name)
-+ return true;
++import six
++
++from grit import util
++from grit.node import include
++from grit.node import message
++from grit.node import structure
++
++
++PACK_FILE_VERSION = 5
++BINARY, UTF8, UTF16 = range(3)
++
++
++GrdInfoItem = collections.namedtuple('GrdInfoItem',
++ ['textual_id', 'id', 'path'])
++
++
++class WrongFileVersion(Exception):
++ pass
++
++
++class CorruptDataPack(Exception):
++ pass
++
++
++class DataPackSizes(object):
++ def __init__(self, header, id_table, alias_table, data):
++ self.header = header
++ self.id_table = id_table
++ self.alias_table = alias_table
++ self.data = data
++
++ @property
++ def total(self):
++ return sum(v for v in self.__dict__.values())
++
++ def __iter__(self):
++ yield ('header', self.header)
++ yield ('id_table', self.id_table)
++ yield ('alias_table', self.alias_table)
++ yield ('data', self.data)
++
++ def __eq__(self, other):
++ return self.__dict__ == other.__dict__
++
++ def __repr__(self):
++ return self.__class__.__name__ + repr(self.__dict__)
++
++
++class DataPackContents(object):
++ def __init__(self, resources, encoding, version, aliases, sizes):
++ # Map of resource_id -> str.
++ self.resources = resources
++ # Encoding (int).
++ self.encoding = encoding
++ # Version (int).
++ self.version = version
++ # Map of resource_id->canonical_resource_id
++ self.aliases = aliases
++ # DataPackSizes instance.
++ self.sizes = sizes
++
++
++def Format(root, lang='en', output_dir='.'):
++ """Writes out the data pack file format (platform agnostic resource file)."""
++ id_map = root.GetIdMap()
++ data = {}
++ root.info = []
++ for node in root.ActiveDescendants():
++ with node:
++ if isinstance(node, (include.IncludeNode, message.MessageNode,
++ structure.StructureNode)):
++ value = node.GetDataPackValue(lang, util.BINARY)
++ if value is not None:
++ resource_id = id_map[node.GetTextualIds()[0]]
++ data[resource_id] = value
++ root.info.append('{},{},{}'.format(
++ node.attrs.get('name'), resource_id, node.source))
++ return WriteDataPackToString(data, UTF8)
++
++
++def ReadDataPack(input_file):
++ return ReadDataPackFromString(util.ReadFile(input_file, util.BINARY))
++
++
++def ReadDataPackFromString(data):
++ """Reads a data pack file and returns a dictionary."""
++ # Read the header.
++ version = struct.unpack('<I', data[:4])[0]
++ if version == 4:
++ resource_count, encoding = struct.unpack('<IB', data[4:9])
++ alias_count = 0
++ header_size = 9
++ elif version == 5:
++ encoding, resource_count, alias_count = struct.unpack('<BxxxHH', data[4:12])
++ header_size = 12
++ else:
++ raise WrongFileVersion('Found version: ' + str(version))
++
++ resources = {}
++ kIndexEntrySize = 2 + 4 # Each entry is a uint16 and a uint32.
++ def entry_at_index(idx):
++ offset = header_size + idx * kIndexEntrySize
++ return struct.unpack('<HI', data[offset:offset + kIndexEntrySize])
++
++ prev_resource_id, prev_offset = entry_at_index(0)
++ for i in range(1, resource_count + 1):
++ resource_id, offset = entry_at_index(i)
++ resources[prev_resource_id] = data[prev_offset:offset]
++ prev_resource_id, prev_offset = resource_id, offset
++
++ id_table_size = (resource_count + 1) * kIndexEntrySize
++ # Read the alias table.
++ kAliasEntrySize = 2 + 2 # uint16, uint16
++ def alias_at_index(idx):
++ offset = header_size + id_table_size + idx * kAliasEntrySize
++ return struct.unpack('<HH', data[offset:offset + kAliasEntrySize])
++
++ aliases = {}
++ for i in range(alias_count):
++ resource_id, index = alias_at_index(i)
++ aliased_id = entry_at_index(index)[0]
++ aliases[resource_id] = aliased_id
++ resources[resource_id] = resources[aliased_id]
++
++ alias_table_size = kAliasEntrySize * alias_count
++ sizes = DataPackSizes(
++ header_size, id_table_size, alias_table_size,
++ len(data) - header_size - id_table_size - alias_table_size)
++ assert sizes.total == len(data), 'original={} computed={}'.format(
++ len(data), sizes.total)
++ return DataPackContents(resources, encoding, version, aliases, sizes)
++
++
++def WriteDataPackToString(resources, encoding):
++ """Returns bytes with a map of id=>data in the data pack format."""
++ ret = []
++
++ # Compute alias map.
++ resource_ids = sorted(resources)
++ # Use reversed() so that for duplicates lower IDs clobber higher ones.
++ id_by_data = {resources[k]: k for k in reversed(resource_ids)}
++ # Map of resource_id -> resource_id, where value < key.
++ alias_map = {k: id_by_data[v] for k, v in resources.items()
++ if id_by_data[v] != k}
++
++ # Write file header.
++ resource_count = len(resources) - len(alias_map)
++ # Padding bytes added for alignment.
++ ret.append(struct.pack('<IBxxxHH', PACK_FILE_VERSION, encoding,
++ resource_count, len(alias_map)))
++ HEADER_LENGTH = 4 + 4 + 2 + 2
++
++ # Each main table entry is: uint16 + uint32 (and an extra entry at the end).
++ # Each alias table entry is: uint16 + uint16.
++ data_offset = HEADER_LENGTH + (resource_count + 1) * 6 + len(alias_map) * 4
++
++ # Write main table.
++ index_by_id = {}
++ deduped_data = []
++ index = 0
++ for resource_id in resource_ids:
++ if resource_id in alias_map:
++ continue
++ data = resources[resource_id]
++ if isinstance(data, six.text_type):
++ data = data.encode('utf-8')
++ index_by_id[resource_id] = index
++ ret.append(struct.pack('<HI', resource_id, data_offset))
++ data_offset += len(data)
++ deduped_data.append(data)
++ index += 1
++
++ assert index == resource_count
++ # Add an extra entry at the end.
++ ret.append(struct.pack('<HI', 0, data_offset))
++
++ # Write alias table.
++ for resource_id in sorted(alias_map):
++ index = index_by_id[alias_map[resource_id]]
++ ret.append(struct.pack('<HH', resource_id, index))
++
++ # Write data.
++ ret.extend(deduped_data)
++ return b''.join(ret)
++
++
++def WriteDataPack(resources, output_file, encoding):
++ """Writes a map of id=>data into output_file as a data pack."""
++ content = WriteDataPackToString(resources, encoding)
++ with open(output_file, 'wb') as file:
++ file.write(content)
++
++
++def ReadGrdInfo(grd_file):
++ info_dict = {}
++ with open(grd_file + '.info', 'rt') as f:
++ for line in f:
++ item = GrdInfoItem._make(line.strip().split(','))
++ info_dict[int(item.id)] = item
++ return info_dict
++
++
++def RePack(output_file, input_files, whitelist_file=None,
++ suppress_removed_key_output=False,
++ output_info_filepath=None):
++ """Write a new data pack file by combining input pack files.
++
++ Args:
++ output_file: path to the new data pack file.
++ input_files: a list of paths to the data pack files to combine.
++ whitelist_file: path to the file that contains the list of resource IDs
++ that should be kept in the output file or None to include
++ all resources.
++ suppress_removed_key_output: allows the caller to suppress the output from
++ RePackFromDataPackStrings.
++ output_info_file: If not None, specify the output .info filepath.
++
++ Raises:
++ KeyError: if there are duplicate keys or resource encoding is
++ inconsistent.
++ """
++ input_data_packs = [ReadDataPack(filename) for filename in input_files]
++ input_info_files = [filename + '.info' for filename in input_files]
++ whitelist = None
++ if whitelist_file:
++ lines = util.ReadFile(whitelist_file, 'utf-8').strip().splitlines()
++ if not lines:
++ raise Exception('Whitelist file should not be empty')
++ whitelist = set(int(x) for x in lines)
++ inputs = [(p.resources, p.encoding) for p in input_data_packs]
++ resources, encoding = RePackFromDataPackStrings(
++ inputs, whitelist, suppress_removed_key_output)
++ WriteDataPack(resources, output_file, encoding)
++ if output_info_filepath is None:
++ output_info_filepath = output_file + '.info'
++ with open(output_info_filepath, 'w') as output_info_file:
++ for filename in input_info_files:
++ with open(filename, 'r') as info_file:
++ output_info_file.writelines(info_file.readlines())
++
++
++def RePackFromDataPackStrings(inputs, whitelist,
++ suppress_removed_key_output=False):
++ """Combines all inputs into one.
++
++ Args:
++ inputs: a list of (resources_by_id, encoding) tuples to be combined.
++ whitelist: a list of resource IDs that should be kept in the output string
++ or None to include all resources.
++ suppress_removed_key_output: Do not print removed keys.
++
++ Returns:
++ Returns (resources_by_id, encoding).
++
++ Raises:
++ KeyError: if there are duplicate keys or resource encoding is
++ inconsistent.
++ """
++ resources = {}
++ encoding = None
++ for input_resources, input_encoding in inputs:
++ # Make sure we have no dups.
++ duplicate_keys = set(input_resources.keys()) & set(resources.keys())
++ if duplicate_keys:
++ raise KeyError('Duplicate keys: ' + str(list(duplicate_keys)))
++
++ # Make sure encoding is consistent.
++ if encoding in (None, BINARY):
++ encoding = input_encoding
++ elif input_encoding not in (BINARY, encoding):
++ raise KeyError('Inconsistent encodings: ' + str(encoding) +
++ ' vs ' + str(input_encoding))
++
++ if whitelist:
++ whitelisted_resources = dict([(key, input_resources[key])
++ for key in input_resources.keys()
++ if key in whitelist])
++ resources.update(whitelisted_resources)
++ removed_keys = [key for key in input_resources.keys()
++ if key not in whitelist]
++ if not suppress_removed_key_output:
++ for key in removed_keys:
++ print('RePackFromDataPackStrings Removed Key:', key)
++ else:
++ resources.update(input_resources)
++
++ # Encoding is 0 for BINARY, 1 for UTF8 and 2 for UTF16
++ if encoding is None:
++ encoding = BINARY
++ return resources, encoding
++
++
++def main():
++ # Write a simple file.
++ data = {1: '', 4: 'this is id 4', 6: 'this is id 6', 10: ''}
++ WriteDataPack(data, 'datapack1.pak', UTF8)
++ data2 = {1000: 'test', 5: 'five'}
++ WriteDataPack(data2, 'datapack2.pak', UTF8)
++ print('wrote datapack1 and datapack2 to current directory.')
++
++
++if __name__ == '__main__':
++ main()
+diff --git a/tools/grit/grit/format/data_pack_unittest.py b/tools/grit/grit/format/data_pack_unittest.py
+new file mode 100644
+index 0000000000..fcd7035473
+--- /dev/null
++++ b/tools/grit/grit/format/data_pack_unittest.py
+@@ -0,0 +1,102 @@
++#!/usr/bin/env python
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++'''Unit tests for grit.format.data_pack'''
++
++from __future__ import print_function
++
++import os
++import sys
++if __name__ == '__main__':
++ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
++
++import unittest
++
++from grit.format import data_pack
++
++
++class FormatDataPackUnittest(unittest.TestCase):
++ def testReadDataPackV4(self):
++ expected_data = (
++ b'\x04\x00\x00\x00' # header(version
++ b'\x04\x00\x00\x00' # no. entries,
++ b'\x01' # encoding)
++ b'\x01\x00\x27\x00\x00\x00' # index entry 1
++ b'\x04\x00\x27\x00\x00\x00' # index entry 4
++ b'\x06\x00\x33\x00\x00\x00' # index entry 6
++ b'\x0a\x00\x3f\x00\x00\x00' # index entry 10
++ b'\x00\x00\x3f\x00\x00\x00' # extra entry for the size of last
++ b'this is id 4this is id 6') # data
++ expected_data_pack = data_pack.DataPackContents(
++ {
++ 1: b'',
++ 4: b'this is id 4',
++ 6: b'this is id 6',
++ 10: b'',
++ }, data_pack.UTF8, 4, {}, data_pack.DataPackSizes(9, 30, 0, 24))
++ loaded = data_pack.ReadDataPackFromString(expected_data)
++ self.assertDictEqual(expected_data_pack.__dict__, loaded.__dict__)
++
++ def testReadWriteDataPackV5(self):
++ expected_data = (
++ b'\x05\x00\x00\x00' # version
++ b'\x01\x00\x00\x00' # encoding & padding
++ b'\x03\x00' # resource_count
++ b'\x01\x00' # alias_count
++ b'\x01\x00\x28\x00\x00\x00' # index entry 1
++ b'\x04\x00\x28\x00\x00\x00' # index entry 4
++ b'\x06\x00\x34\x00\x00\x00' # index entry 6
++ b'\x00\x00\x40\x00\x00\x00' # extra entry for the size of last
++ b'\x0a\x00\x01\x00' # alias table
++ b'this is id 4this is id 6') # data
++ input_resources = {
++ 1: b'',
++ 4: b'this is id 4',
++ 6: b'this is id 6',
++ 10: b'this is id 4',
+ }
-+ }
++ data = data_pack.WriteDataPackToString(input_resources, data_pack.UTF8)
++ self.assertEquals(data, expected_data)
++
++ expected_data_pack = data_pack.DataPackContents({
++ 1: b'',
++ 4: input_resources[4],
++ 6: input_resources[6],
++ 10: input_resources[4],
++ }, data_pack.UTF8, 5, {10: 4}, data_pack.DataPackSizes(12, 24, 4, 24))
++ loaded = data_pack.ReadDataPackFromString(expected_data)
++ self.assertDictEqual(expected_data_pack.__dict__, loaded.__dict__)
++
++ def testRePackUnittest(self):
++ expected_with_whitelist = {
++ 1: 'Never gonna', 10: 'give you up', 20: 'Never gonna let',
++ 30: 'you down', 40: 'Never', 50: 'gonna run around and',
++ 60: 'desert you'}
++ expected_without_whitelist = {
++ 1: 'Never gonna', 10: 'give you up', 20: 'Never gonna let', 65: 'Close',
++ 30: 'you down', 40: 'Never', 50: 'gonna run around and', 4: 'click',
++ 60: 'desert you', 6: 'chirr', 32: 'oops, try again', 70: 'Awww, snap!'}
++ inputs = [{1: 'Never gonna', 4: 'click', 6: 'chirr', 10: 'give you up'},
++ {20: 'Never gonna let', 30: 'you down', 32: 'oops, try again'},
++ {40: 'Never', 50: 'gonna run around and', 60: 'desert you'},
++ {65: 'Close', 70: 'Awww, snap!'}]
++ whitelist = [1, 10, 20, 30, 40, 50, 60]
++ inputs = [(i, data_pack.UTF8) for i in inputs]
++
++ # RePack using whitelist
++ output, _ = data_pack.RePackFromDataPackStrings(
++ inputs, whitelist, suppress_removed_key_output=True)
++ self.assertDictEqual(expected_with_whitelist, output,
++ 'Incorrect resource output')
++
++ # RePack a None whitelist
++ output, _ = data_pack.RePackFromDataPackStrings(
++ inputs, None, suppress_removed_key_output=True)
++ self.assertDictEqual(expected_without_whitelist, output,
++ 'Incorrect resource output')
+
-+ return false;
-+}
+
-+bool ChromeClassTester::IsIgnoredType(const std::string& base_name) {
-+ return ignored_record_names_.find(base_name) != ignored_record_names_.end();
-+}
++if __name__ == '__main__':
++ unittest.main()
+diff --git a/tools/grit/grit/format/gen_predetermined_ids.py b/tools/grit/grit/format/gen_predetermined_ids.py
+new file mode 100644
+index 0000000000..9b2aa7b1a5
+--- /dev/null
++++ b/tools/grit/grit/format/gen_predetermined_ids.py
+@@ -0,0 +1,144 @@
++#!/usr/bin/env python
++# Copyright 2017 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
+
-+bool ChromeClassTester::GetFilename(SourceLocation loc,
-+ std::string* filename) {
-+ const SourceManager& source_manager = instance_.getSourceManager();
-+ SourceLocation spelling_location = source_manager.getSpellingLoc(loc);
-+ PresumedLoc ploc = source_manager.getPresumedLoc(spelling_location);
-+ if (ploc.isInvalid()) {
-+ // If we're in an invalid location, we're looking at things that aren't
-+ // actually stated in the source.
-+ return false;
-+ }
++"""
++A tool to generate a predetermined resource ids file that can be used as an
++input to grit via the -p option. This is meant to be run manually every once in
++a while and its output checked in. See tools/gritsettings/README.md for details.
++"""
+
-+ *filename = ploc.getFilename();
-+ return true;
-+}
-diff --git a/tools/clang/plugins/ChromeClassTester.h b/tools/clang/plugins/ChromeClassTester.h
++from __future__ import print_function
++
++import os
++import re
++import sys
++
++# Regular expression for parsing the #define macro format. Matches both the
++# version of the macro with whitelist support and the one without. For example,
++# Without generate whitelist flag:
++# #define IDS_FOO_MESSAGE 1234
++# With generate whitelist flag:
++# #define IDS_FOO_MESSAGE (::ui::WhitelistedResource<1234>(), 1234)
++RESOURCE_EXTRACT_REGEX = re.compile(r'^#define (\S*).* (\d+)\)?$', re.MULTILINE)
++
++ORDERED_RESOURCE_IDS_REGEX = re.compile(r'^Resource=(\d*)$', re.MULTILINE)
++
++
++def _GetResourceNameIdPairsIter(string_to_scan):
++ """Gets an iterator of the resource name and id pairs of the given string.
++
++ Scans the input string for lines of the form "#define NAME ID" and returns
++ an iterator over all matching (NAME, ID) pairs.
++
++ Args:
++ string_to_scan: The input string to scan.
++
++ Yields:
++ A tuple of name and id.
++ """
++ for match in RESOURCE_EXTRACT_REGEX.finditer(string_to_scan):
++ yield match.group(1, 2)
++
++
++def _ReadOrderedResourceIds(path):
++ """Reads ordered resource ids from the given file.
++
++ The resources are expected to be of the format produced by running Chrome
++ with --print-resource-ids command line.
++
++ Args:
++ path: File path to read resource ids from.
++
++ Returns:
++ An array of ordered resource ids.
++ """
++ ordered_resource_ids = []
++ with open(path, "r") as f:
++ for match in ORDERED_RESOURCE_IDS_REGEX.finditer(f.read()):
++ ordered_resource_ids.append(int(match.group(1)))
++ return ordered_resource_ids
++
++
++def GenerateResourceMapping(original_resources, ordered_resource_ids):
++ """Generates a resource mapping from the ordered ids and the original mapping.
++
++ The returned dict will assign new ids to ordered_resource_ids numerically
++ increasing from 101.
++
++ Args:
++ original_resources: A dict of original resource ids to resource names.
++ ordered_resource_ids: An array of ordered resource ids.
++
++ Returns:
++ A dict of resource ids to resource names.
++ """
++ output_resource_map = {}
++ # 101 is used as the starting value since other parts of GRIT require it to be
++ # the minimum (e.g. rc_header.py) based on Windows resource numbering.
++ next_id = 101
++ for original_id in ordered_resource_ids:
++ resource_name = original_resources[original_id]
++ output_resource_map[next_id] = resource_name
++ next_id += 1
++ return output_resource_map
++
++
++def ReadResourceIdsFromFile(file, original_resources):
++ """Reads resource ids from a GRIT-produced header file.
++
++ Args:
++ file: File to a GRIT-produced header file to read from.
++ original_resources: Dict of resource ids to resource names to add to.
++ """
++ for resource_name, resource_id in _GetResourceNameIdPairsIter(file.read()):
++ original_resources[int(resource_id)] = resource_name
++
++
++def _ReadOriginalResourceIds(out_dir):
++ """Reads resource ids from GRIT header files in the specified directory.
++
++ Args:
++ out_dir: A Chrome build output directory (e.g. out/gn) to scan.
++
++ Returns:
++ A dict of resource ids to resource names.
++ """
++ original_resources = {}
++ for root, dirnames, filenames in os.walk(out_dir + '/gen'):
++ for filename in filenames:
++ if filename.endswith(('_resources.h', '_settings.h', '_strings.h')):
++ with open(os.path.join(root, filename), "r") as f:
++ ReadResourceIdsFromFile(f, original_resources)
++ return original_resources
++
++
++def _GeneratePredeterminedIdsFile(ordered_resources_file, out_dir):
++ """Generates a predetermined ids file.
++
++ Args:
++ ordered_resources_file: File path to read ordered resource ids from.
++ out_dir: A Chrome build output directory (e.g. out/gn) to scan.
++
++ Returns:
++ A dict of resource ids to resource names.
++ """
++ original_resources = _ReadOriginalResourceIds(out_dir)
++ ordered_resource_ids = _ReadOrderedResourceIds(ordered_resources_file)
++ output_resource_map = GenerateResourceMapping(original_resources,
++ ordered_resource_ids)
++ for res_id in sorted(output_resource_map.keys()):
++ print(output_resource_map[res_id], res_id)
++
++
++def main(argv):
++ if len(argv) != 2:
++ print("usage: gen_predetermined_ids.py <ordered_resources_file> <out_dir>")
++ sys.exit(1)
++ ordered_resources_file, out_dir = argv[0], argv[1]
++ _GeneratePredeterminedIdsFile(ordered_resources_file, out_dir)
++
++
++if '__main__' == __name__:
++ main(sys.argv[1:])
+diff --git a/tools/grit/grit/format/gen_predetermined_ids_unittest.py b/tools/grit/grit/format/gen_predetermined_ids_unittest.py
+new file mode 100644
+index 0000000000..bd0331adb4
+--- /dev/null
++++ b/tools/grit/grit/format/gen_predetermined_ids_unittest.py
+@@ -0,0 +1,46 @@
++#!/usr/bin/env python
++# Copyright 2017 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++'''Unit tests for the gen_predetermined_ids module.'''
++
++from __future__ import print_function
++
++import os
++import sys
++if __name__ == '__main__':
++ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
++
++import unittest
++
++from six import StringIO
++
++from grit.format import gen_predetermined_ids
++
++class GenPredeterminedIdsUnittest(unittest.TestCase):
++ def testGenerateResourceMapping(self):
++ original_resources = {200: 'A', 201: 'B', 300: 'C', 350: 'D', 370: 'E'}
++ ordered_resource_ids = [300, 201, 370]
++ mapping = gen_predetermined_ids.GenerateResourceMapping(
++ original_resources, ordered_resource_ids)
++ self.assertEqual({101: 'C', 102: 'B', 103: 'E'}, mapping)
++
++ def testReadResourceIdsFromFile(self):
++ f = StringIO('''
++// This file is automatically generated by GRIT. Do not edit.
++
++#pragma once
++
++#define IDS_BOOKMARKS_NO_ITEMS 12500
++#define IDS_BOOKMARK_BAR_IMPORT_LINK (::ui::WhitelistedResource<12501>(), 12501)
++#define IDS_BOOKMARK_X (::ui::WhitelistedResource<12502>(), 12502)
++''')
++ resources = {}
++ gen_predetermined_ids.ReadResourceIdsFromFile(f, resources)
++ self.assertEqual({12500: 'IDS_BOOKMARKS_OPEN_ALL',
++ 12501: 'IDS_BOOKMARKS_OPEN_ALL_INCOGNITO',
++ 12502: 'IDS_BOOKMARK_X'}, resources)
++
++if __name__ == '__main__':
++ unittest.main()
+diff --git a/tools/grit/grit/format/gzip_string.py b/tools/grit/grit/format/gzip_string.py
new file mode 100644
-index 0000000000..588ae9cae5
+index 0000000000..3cd17185c9
--- /dev/null
-+++ b/tools/clang/plugins/ChromeClassTester.h
-@@ -0,0 +1,84 @@
-+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+// Use of this source code is governed by a BSD-style license that can be
-+// found in the LICENSE file.
++++ b/tools/grit/grit/format/gzip_string.py
+@@ -0,0 +1,46 @@
++# Copyright (c) 2016 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++"""Provides gzip utilities for strings.
++"""
+
-+#ifndef TOOLS_CLANG_PLUGINS_CHROMECLASSTESTER_H_
-+#define TOOLS_CLANG_PLUGINS_CHROMECLASSTESTER_H_
++from __future__ import print_function
+
-+#include <set>
-+#include <vector>
++import gzip
++import io
++import subprocess
+
-+#include "clang/AST/ASTConsumer.h"
-+#include "clang/AST/TypeLoc.h"
-+#include "clang/Frontend/CompilerInstance.h"
+
-+// A class on top of ASTConsumer that forwards classes defined in Chromium
-+// headers to subclasses which implement CheckChromeClass().
-+class ChromeClassTester : public clang::ASTConsumer {
-+ public:
-+ explicit ChromeClassTester(clang::CompilerInstance& instance);
-+ virtual ~ChromeClassTester();
++def GzipStringRsyncable(data):
++ # Make call to host system's gzip to get access to --rsyncable option. This
++ # option makes updates much smaller - if one line is changed in the resource,
++ # it won't have to push the entire compressed resource with the update.
++ # Instead, --rsyncable breaks the file into small chunks, so that one doesn't
++ # affect the other in compression, and then only that chunk will have to be
++ # updated.
++ gzip_proc = subprocess.Popen(['gzip', '--stdout', '--rsyncable',
++ '--best', '--no-name'],
++ stdin=subprocess.PIPE,
++ stdout=subprocess.PIPE,
++ stderr=subprocess.PIPE)
++ data, stderr = gzip_proc.communicate(data)
++ if gzip_proc.returncode != 0:
++ raise subprocess.CalledProcessError(gzip_proc.returncode, 'gzip',
++ stderr)
++ return data
++
++
++def GzipString(data):
++ # Gzipping using Python's built in gzip: Windows doesn't ship with gzip, and
++ # OSX's gzip does not have an --rsyncable option built in. Although this is
++ # not preferable to --rsyncable, it is an option for the systems that do
++ # not have --rsyncable. If used over GzipStringRsyncable, the primary
++ # difference of this function's compression will be larger updates every time
++ # a compressed resource is changed.
++ gzip_output = io.BytesIO()
++ with gzip.GzipFile(mode='wb', compresslevel=9, fileobj=gzip_output,
++ mtime=0) as gzip_file:
++ gzip_file.write(data)
++ data = gzip_output.getvalue()
++ gzip_output.close()
++ return data
+diff --git a/tools/grit/grit/format/gzip_string_unittest.py b/tools/grit/grit/format/gzip_string_unittest.py
+new file mode 100644
+index 0000000000..c0cfbe1837
+--- /dev/null
++++ b/tools/grit/grit/format/gzip_string_unittest.py
+@@ -0,0 +1,65 @@
++#!/usr/bin/env python
++# Copyright (c) 2016 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
+
-+ // clang::ASTConsumer:
-+ virtual void HandleTagDeclDefinition(clang::TagDecl* tag);
-+ virtual bool HandleTopLevelDecl(clang::DeclGroupRef group_ref);
++'''Unit tests for grit.format.gzip_string'''
+
-+ protected:
-+ clang::CompilerInstance& instance() { return instance_; }
-+ clang::DiagnosticsEngine& diagnostic() { return diagnostic_; }
++from __future__ import print_function
+
-+ // Emits a simple warning; this shouldn't be used if you require printf-style
-+ // printing.
-+ void emitWarning(clang::SourceLocation loc, const char* error);
++import gzip
++import io
++import os
++import sys
++if __name__ == '__main__':
++ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
+
-+ // Utility method for subclasses to check if this class is in a banned
-+ // namespace.
-+ bool InBannedNamespace(const clang::Decl* record);
++import unittest
+
-+ // Utility method for subclasses to determine the namespace of the
-+ // specified record, if any. Unnamed namespaces will be identified as
-+ // "<anonymous namespace>".
-+ std::string GetNamespace(const clang::Decl* record);
++from grit.format import gzip_string
+
-+ // Utility method for subclasses to check if this class is within an
-+ // implementation (.cc, .cpp, .mm) file.
-+ bool InImplementationFile(clang::SourceLocation location);
+
-+ private:
-+ void BuildBannedLists();
++class FormatGzipStringUnittest(unittest.TestCase):
+
-+ void CheckTag(clang::TagDecl*);
++ def testGzipStringRsyncable(self):
++ # Can only test the rsyncable version on platforms which support rsyncable,
++ # which at the moment is Linux.
++ if sys.platform == 'linux2':
++ header_begin = (b'\x1f\x8b') # gzip first two bytes
++ input = (b'TEST STRING STARTING NOW'
++ b'continuing'
++ b'<even more>'
++ b'<finished NOW>')
+
-+ // Filtered versions of tags that are only called with things defined in
-+ // chrome header files.
-+ virtual void CheckChromeClass(clang::SourceLocation record_location,
-+ clang::CXXRecordDecl* record) = 0;
++ compressed = gzip_string.GzipStringRsyncable(input)
++ self.failUnless(header_begin == compressed[:2])
+
-+ // Utility methods used for filtering out non-chrome classes (and ones we
-+ // deliberately ignore) in HandleTagDeclDefinition().
-+ std::string GetNamespaceImpl(const clang::DeclContext* context,
-+ const std::string& candidate);
-+ bool InBannedDirectory(clang::SourceLocation loc);
-+ bool IsIgnoredType(const std::string& base_name);
++ compressed_file = io.BytesIO()
++ compressed_file.write(compressed)
++ compressed_file.seek(0)
+
-+ // Attempts to determine the filename for the given SourceLocation.
-+ // Returns false if the filename could not be determined.
-+ bool GetFilename(clang::SourceLocation loc, std::string* filename);
++ with gzip.GzipFile(mode='rb', fileobj=compressed_file) as f:
++ output = f.read()
++ self.failUnless(output == input)
+
-+ clang::CompilerInstance& instance_;
-+ clang::DiagnosticsEngine& diagnostic_;
++ def testGzipString(self):
++ header_begin = b'\x1f\x8b' # gzip first two bytes
++ input = (b'TEST STRING STARTING NOW'
++ b'continuing'
++ b'<even more>'
++ b'<finished NOW>')
+
-+ // List of banned namespaces.
-+ std::vector<std::string> banned_namespaces_;
++ compressed = gzip_string.GzipString(input)
++ self.failUnless(header_begin == compressed[:2])
+
-+ // List of banned directories.
-+ std::vector<std::string> banned_directories_;
++ compressed_file = io.BytesIO()
++ compressed_file.write(compressed)
++ compressed_file.seek(0)
+
-+ // List of types that we don't check.
-+ std::set<std::string> ignored_record_names_;
++ with gzip.GzipFile(mode='rb', fileobj=compressed_file) as f:
++ output = f.read()
++ self.failUnless(output == input)
+
-+ // List of decls to check once the current top-level decl is parsed.
-+ std::vector<clang::TagDecl*> pending_class_decls_;
-+};
+
-+#endif // TOOLS_CLANG_PLUGINS_CHROMECLASSTESTER_H_
-diff --git a/tools/clang/plugins/FindBadConstructs.cpp b/tools/clang/plugins/FindBadConstructs.cpp
++if __name__ == '__main__':
++ unittest.main()
+diff --git a/tools/grit/grit/format/html_inline.py b/tools/grit/grit/format/html_inline.py
new file mode 100644
-index 0000000000..b79a64dbd1
+index 0000000000..da55216ea4
--- /dev/null
-+++ b/tools/clang/plugins/FindBadConstructs.cpp
-@@ -0,0 +1,435 @@
-+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+// Use of this source code is governed by a BSD-style license that can be
-+// found in the LICENSE file.
++++ b/tools/grit/grit/format/html_inline.py
+@@ -0,0 +1,602 @@
++#!/usr/bin/env python
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
+
-+// This file defines a bunch of recurring problems in the Chromium C++ code.
-+//
-+// Checks that are implemented:
-+// - Constructors/Destructors should not be inlined if they are of a complex
-+// class type.
-+// - Missing "virtual" keywords on methods that should be virtual.
-+// - Non-annotated overriding virtual methods.
-+// - Virtual methods with nonempty implementations in their headers.
-+// - Classes that derive from base::RefCounted / base::RefCountedThreadSafe
-+// should have protected or private destructors.
++"""Flattens a HTML file by inlining its external resources.
+
-+#include "clang/Frontend/FrontendPluginRegistry.h"
-+#include "clang/AST/ASTConsumer.h"
-+#include "clang/AST/AST.h"
-+#include "clang/AST/CXXInheritance.h"
-+#include "clang/AST/TypeLoc.h"
-+#include "clang/Basic/SourceManager.h"
-+#include "clang/Frontend/CompilerInstance.h"
-+#include "llvm/Support/raw_ostream.h"
++This is a small script that takes a HTML file, looks for src attributes
++and inlines the specified file, producing one HTML file with no external
++dependencies. It recursively inlines the included files.
++"""
+
-+#include "ChromeClassTester.h"
++from __future__ import print_function
+
-+using namespace clang;
++import os
++import re
++import sys
++import base64
++import mimetypes
++
++from grit import lazy_re
++from grit import util
++from grit.format import minifier
++
++# There is a python bug that makes mimetypes crash if the Windows
++# registry contains non-Latin keys ( http://bugs.python.org/issue9291
++# ). Initing manually and blocking external mime-type databases will
++# prevent that bug and if we add svg manually, it will still give us
++# the data we need.
++mimetypes.init([])
++mimetypes.add_type('image/svg+xml', '.svg')
++
++# webm video type is not always available if mimetype package is outdated.
++mimetypes.add_type('video/webm', '.webm')
++
++DIST_DEFAULT = 'chromium'
++DIST_ENV_VAR = 'CHROMIUM_BUILD'
++DIST_SUBSTR = '%DISTRIBUTION%'
++
++# Matches beginning of an "if" block.
++_BEGIN_IF_BLOCK = lazy_re.compile(
++ r'<if [^>]*?expr=("(?P<expr1>[^">]*)"|\'(?P<expr2>[^\'>]*)\')[^>]*?>')
++
++# Matches ending of an "if" block.
++_END_IF_BLOCK = lazy_re.compile(r'</if>')
++
++# Used by DoInline to replace various links with inline content.
++_STYLESHEET_RE = lazy_re.compile(
++ r'<link rel="stylesheet"[^>]+?href="(?P<filename>[^"]*)".*?>(\s*</link>)?',
++ re.DOTALL)
++_INCLUDE_RE = lazy_re.compile(
++ r'(?P<comment>\/\/ )?<include[^>]+?'
++ r'src=("(?P<file1>[^">]*)"|\'(?P<file2>[^\'>]*)\').*?>(\s*</include>)?',
++ re.DOTALL)
++_SRC_RE = lazy_re.compile(
++ r'<(?!script)(?:[^>]+?\s)src="(?!\[\[|{{)(?P<filename>[^"\']*)"',
++ re.MULTILINE)
++# This re matches '<img srcset="..."' or '<source srcset="..."'
++_SRCSET_RE = lazy_re.compile(
++ r'<(img|source)\b(?:[^>]*?\s)srcset="(?!\[\[|{{|\$i18n{)'
++ r'(?P<srcset>[^"\']*)"',
++ re.MULTILINE)
++# This re is for splitting srcset value string into "image candidate strings".
++# Notes:
++# - HTML 5.2 states that URL cannot start or end with comma.
++# - the "descriptor" is either "width descriptor" or "pixel density descriptor".
++# The first one consists of "valid non-negative integer + letter 'x'",
++# the second one is formed of "positive valid floating-point number +
++# letter 'w'". As a reasonable compromise, we match a list of characters
++# that form both of them.
++# Matches for example "img2.png 2x" or "img9.png 11E-2w".
++_SRCSET_ENTRY_RE = lazy_re.compile(
++ r'\s*(?P<url>[^,\s]\S+[^,\s])'
++ r'(?:\s+(?P<descriptor>[\deE.-]+[wx]))?\s*'
++ r'(?P<separator>,|$)',
++ re.MULTILINE)
++_ICON_RE = lazy_re.compile(
++ r'<link rel="icon"\s(?:[^>]+?\s)?'
++ r'href=(?P<quote>")(?P<filename>[^"\']*)\1',
++ re.MULTILINE)
++
++
++def GetDistribution():
++ """Helper function that gets the distribution we are building.
++
++ Returns:
++ string
++ """
++ distribution = DIST_DEFAULT
++ if DIST_ENV_VAR in os.environ:
++ distribution = os.environ[DIST_ENV_VAR]
++ if len(distribution) > 1 and distribution[0] == '_':
++ distribution = distribution[1:].lower()
++ return distribution
++
++def ConvertFileToDataURL(filename, base_path, distribution, inlined_files,
++ names_only):
++ """Convert filename to inlined data URI.
++
++ Takes a filename from ether "src" or "srcset", and attempts to read the file
++ at 'filename'. Returns data URI as string with given file inlined.
++ If it finds DIST_SUBSTR string in file name, replaces it with distribution.
++ If filename contains ':', it is considered URL and not translated.
++
++ Args:
++ filename: filename string from ether src or srcset attributes.
++ base_path: path that to look for files in
++ distribution: string that should replace DIST_SUBSTR
++ inlined_files: The name of the opened file is appended to this list.
++ names_only: If true, the function will not read the file but just return "".
++ It will still add the filename to |inlined_files|.
++
++ Returns:
++ string
++ """
++ if filename.find(':') != -1:
++ # filename is probably a URL, which we don't want to bother inlining
++ return filename
++
++ filename = filename.replace(DIST_SUBSTR , distribution)
++ filepath = os.path.normpath(os.path.join(base_path, filename))
++ inlined_files.add(filepath)
++
++ if names_only:
++ return ""
++
++ mimetype = mimetypes.guess_type(filename)[0]
++ if mimetype is None:
++ raise Exception('%s is of an an unknown type and '
++ 'cannot be stored in a data url.' % filename)
++ inline_data = base64.standard_b64encode(util.ReadFile(filepath, util.BINARY))
++ return 'data:%s;base64,%s' % (mimetype, inline_data.decode('utf-8'))
++
++
++def SrcInlineAsDataURL(
++ src_match, base_path, distribution, inlined_files, names_only=False,
++ filename_expansion_function=None):
++ """regex replace function.
++
++ Takes a regex match for src="filename", attempts to read the file
++ at 'filename' and returns the src attribute with the file inlined
++ as a data URI. If it finds DIST_SUBSTR string in file name, replaces
++ it with distribution.
++
++ Args:
++ src_match: regex match object with 'filename' named capturing group
++ base_path: path that to look for files in
++ distribution: string that should replace DIST_SUBSTR
++ inlined_files: The name of the opened file is appended to this list.
++ names_only: If true, the function will not read the file but just return "".
++ It will still add the filename to |inlined_files|.
++
++ Returns:
++ string
++ """
++ filename = src_match.group('filename')
++ if filename_expansion_function:
++ filename = filename_expansion_function(filename)
++
++ data_url = ConvertFileToDataURL(filename, base_path, distribution,
++ inlined_files, names_only)
++
++ if not data_url:
++ return data_url
++
++ prefix = src_match.string[src_match.start():src_match.start('filename')]
++ suffix = src_match.string[src_match.end('filename'):src_match.end()]
++ return prefix + data_url + suffix
++
++def SrcsetInlineAsDataURL(
++ srcset_match, base_path, distribution, inlined_files, names_only=False,
++ filename_expansion_function=None):
++ """regex replace function to inline files in srcset="..." attributes
++
++ Takes a regex match for srcset="filename 1x, filename 2x, ...", attempts to
++ read the files referenced by filenames and returns the srcset attribute with
++ the files inlined as a data URI. If it finds DIST_SUBSTR string in file name,
++ replaces it with distribution.
++
++ Args:
++ srcset_match: regex match object with 'srcset' named capturing group
++ base_path: path that to look for files in
++ distribution: string that should replace DIST_SUBSTR
++ inlined_files: The name of the opened file is appended to this list.
++ names_only: If true, the function will not read the file but just return "".
++ It will still add the filename to |inlined_files|.
++
++ Returns:
++ string
++ """
++ srcset = srcset_match.group('srcset')
++
++ if not srcset:
++ return srcset_match.group(0)
++
++ # HTML 5.2 defines srcset as a list of "image candidate strings".
++ # Each of them consists of URL and descriptor.
++ # _SRCSET_ENTRY_RE splits srcset into a list of URLs, descriptors and
++ # commas.
++ # The descriptor part will be None if that optional regex didn't match
++ parts = _SRCSET_ENTRY_RE.split(srcset)
++
++ if not parts:
++ return srcset_match.group(0)
++
++ # List of image candidate strings that will form new srcset="..."
++ new_candidates = []
++
++ # When iterating over split srcset we fill this parts of a single image
++ # candidate string: [url, descriptor]
++ candidate = [];
++
++ # Each entry should consist of some text before the entry, the url,
++ # the descriptor or None if the entry has no descriptor, a comma separator or
++ # the end of the line, and finally some text after the entry (which is the
++ # same as the text before the next entry).
++ for i in range(0, len(parts) - 1, 4):
++ before, url, descriptor, separator, after = parts[i:i+5]
++
++ # There must be a comma-separated next entry or this must be the last entry.
++ assert separator == "," or (separator == "" and i == len(parts) - 5), (
++ "Bad srcset format in {}".format(srcset_match.group(0)))
++ # Both before and after the entry must be empty
++ assert before == after == "", (
++ "Bad srcset format in {}".format(srcset_match.group(0)))
++
++ if filename_expansion_function:
++ filename = filename_expansion_function(url)
++ else:
++ filename = url
++
++ data_url = ConvertFileToDataURL(filename, base_path, distribution,
++ inlined_files, names_only)
++
++ # This is not "names_only" mode
++ if data_url:
++ candidate = [data_url]
++ if descriptor:
++ candidate.append(descriptor)
++
++ new_candidates.append(" ".join(candidate))
++
++ prefix = srcset_match.string[srcset_match.start():
++ srcset_match.start('srcset')]
++ suffix = srcset_match.string[srcset_match.end('srcset'):srcset_match.end()]
++ return prefix + ','.join(new_candidates) + suffix
++
++class InlinedData:
++ """Helper class holding the results from DoInline().
++
++ Holds the inlined data and the set of filenames of all the inlined
++ files.
++ """
++ def __init__(self, inlined_data, inlined_files):
++ self.inlined_data = inlined_data
++ self.inlined_files = inlined_files
++
++def DoInline(
++ input_filename, grd_node, allow_external_script=False,
++ preprocess_only=False, names_only=False, strip_whitespace=False,
++ rewrite_function=None, filename_expansion_function=None):
++ """Helper function that inlines the resources in a specified file.
++
++ Reads input_filename, finds all the src attributes and attempts to
++ inline the files they are referring to, then returns the result and
++ the set of inlined files.
++
++ Args:
++ input_filename: name of file to read in
++ grd_node: html node from the grd file for this include tag
++ preprocess_only: Skip all HTML processing, only handle <if> and <include>.
++ names_only: |nil| will be returned for the inlined contents (faster).
++ strip_whitespace: remove whitespace and comments in the input files.
++ rewrite_function: function(filepath, text, distribution) which will be
++ called to rewrite html content before inlining images.
++ filename_expansion_function: function(filename) which will be called to
++ rewrite filenames before attempting to read them.
++ Returns:
++ a tuple of the inlined data as a string and the set of filenames
++ of all the inlined files
++ """
++ if filename_expansion_function:
++ input_filename = filename_expansion_function(input_filename)
++ input_filepath = os.path.dirname(input_filename)
++ distribution = GetDistribution()
++
++ # Keep track of all the files we inline.
++ inlined_files = set()
++
++ def SrcReplace(src_match, filepath=input_filepath,
++ inlined_files=inlined_files):
++ """Helper function to provide SrcInlineAsDataURL with the base file path"""
++ return SrcInlineAsDataURL(
++ src_match, filepath, distribution, inlined_files, names_only=names_only,
++ filename_expansion_function=filename_expansion_function)
++
++ def SrcsetReplace(srcset_match, filepath=input_filepath,
++ inlined_files=inlined_files):
++ """Helper function to provide SrcsetInlineAsDataURL with the base file
++ path.
++ """
++ return SrcsetInlineAsDataURL(
++ srcset_match, filepath, distribution, inlined_files,
++ names_only=names_only,
++ filename_expansion_function=filename_expansion_function)
++
++ def GetFilepath(src_match, base_path = input_filepath):
++ filename = [v for k, v in src_match.groupdict().items()
++ if k.startswith('file') and v][0]
++
++ if filename.find(':') != -1:
++ # filename is probably a URL, which we don't want to bother inlining
++ return None
++
++ filename = filename.replace('%DISTRIBUTION%', distribution)
++ if filename_expansion_function:
++ filename = filename_expansion_function(filename)
++ return os.path.normpath(os.path.join(base_path, filename))
++
++ def IsConditionSatisfied(src_match):
++ expr1 = src_match.group('expr1') or ''
++ expr2 = src_match.group('expr2') or ''
++ return grd_node is None or grd_node.EvaluateCondition(expr1 + expr2)
++
++ def CheckConditionalElements(str):
++ """Helper function to conditionally inline inner elements"""
++ while True:
++ begin_if = _BEGIN_IF_BLOCK.search(str)
++ if begin_if is None:
++ if _END_IF_BLOCK.search(str) is not None:
++ raise Exception('Unmatched </if>')
++ return str
++
++ condition_satisfied = IsConditionSatisfied(begin_if)
++ leading = str[0:begin_if.start()]
++ content_start = begin_if.end()
++
++ # Find matching "if" block end.
++ count = 1
++ pos = begin_if.end()
++ while True:
++ end_if = _END_IF_BLOCK.search(str, pos)
++ if end_if is None:
++ raise Exception('Unmatched <if>')
++
++ next_if = _BEGIN_IF_BLOCK.search(str, pos)
++ if next_if is None or next_if.start() >= end_if.end():
++ count = count - 1
++ if count == 0:
++ break
++ pos = end_if.end()
++ else:
++ count = count + 1
++ pos = next_if.end()
++
++ content = str[content_start:end_if.start()]
++ trailing = str[end_if.end():]
++
++ if condition_satisfied:
++ str = leading + CheckConditionalElements(content) + trailing
++ else:
++ str = leading + trailing
++
++ def InlineFileContents(src_match,
++ pattern,
++ inlined_files=inlined_files,
++ strip_whitespace=False):
++ """Helper function to inline external files of various types"""
++ filepath = GetFilepath(src_match)
++ if filepath is None:
++ return src_match.group(0)
++ inlined_files.add(filepath)
++
++ if names_only:
++ inlined_files.update(GetResourceFilenames(
++ filepath,
++ grd_node,
++ allow_external_script,
++ rewrite_function,
++ filename_expansion_function=filename_expansion_function))
++ return ""
++ # To recursively save inlined files, we need InlinedData instance returned
++ # by DoInline.
++ inlined_data_inst=DoInline(filepath, grd_node,
++ allow_external_script=allow_external_script,
++ preprocess_only=preprocess_only,
++ strip_whitespace=strip_whitespace,
++ filename_expansion_function=filename_expansion_function)
++
++ inlined_files.update(inlined_data_inst.inlined_files)
++
++ return pattern % inlined_data_inst.inlined_data;
++
++
++ def InlineIncludeFiles(src_match):
++ """Helper function to directly inline generic external files (without
++ wrapping them with any kind of tags).
++ """
++ return InlineFileContents(src_match, '%s')
++
++ def InlineScript(match):
++ """Helper function to inline external script files"""
++ attrs = (match.group('attrs1') + match.group('attrs2')).strip()
++ if attrs:
++ attrs = ' ' + attrs
++ return InlineFileContents(match, '<script' + attrs + '>%s</script>',
++ strip_whitespace=True)
++
++ def InlineCSSText(text, css_filepath):
++ """Helper function that inlines external resources in CSS text"""
++ filepath = os.path.dirname(css_filepath)
++ # Allow custom modifications before inlining images.
++ if rewrite_function:
++ text = rewrite_function(filepath, text, distribution)
++ text = InlineCSSImages(text, filepath)
++ return InlineCSSImports(text, filepath)
++
++ def InlineCSSFile(src_match, pattern, base_path=input_filepath):
++ """Helper function to inline external CSS files.
++
++ Args:
++ src_match: A regular expression match with a named group named "filename".
++ pattern: The pattern to replace with the contents of the CSS file.
++ base_path: The base path to use for resolving the CSS file.
++
++ Returns:
++ The text that should replace the reference to the CSS file.
++ """
++ filepath = GetFilepath(src_match, base_path)
++ if filepath is None:
++ return src_match.group(0)
++
++ # Even if names_only is set, the CSS file needs to be opened, because it
++ # can link to images that need to be added to the file set.
++ inlined_files.add(filepath)
++
++ # Inline stylesheets included in this css file.
++ text = _INCLUDE_RE.sub(InlineIncludeFiles, util.ReadFile(filepath, 'utf-8'))
++ # When resolving CSS files we need to pass in the path so that relative URLs
++ # can be resolved.
++
++ return pattern % InlineCSSText(text, filepath)
++
++ def GetUrlRegexString(postfix=''):
++ """Helper function that returns a string for a regex that matches url('')
++ but not url([[ ]]) or url({{ }}). Appends |postfix| to group names.
++ """
++ url_re = (r'url\((?!\[\[|{{)(?P<q%s>"|\'|)(?P<filename%s>[^"\'()]*)'
++ r'(?P=q%s)\)')
++ return url_re % (postfix, postfix, postfix)
++
++ def InlineCSSImages(text, filepath=input_filepath):
++ """Helper function that inlines external images in CSS backgrounds."""
++ # Replace contents of url() for css attributes: content, background,
++ # or *-image.
++ property_re = r'(content|background|[\w-]*-image):[^;]*'
++ # Replace group names to prevent duplicates when forming value_re.
++ image_set_value_re = (r'image-set\(([ ]*' + GetUrlRegexString('2') +
++ r'[ ]*[0-9.]*x[ ]*(,[ ]*)?)+\)')
++ value_re = '(%s|%s)' % (GetUrlRegexString(), image_set_value_re)
++ css_re = property_re + value_re
++ return re.sub(css_re, lambda m: InlineCSSUrls(m, filepath), text)
++
++ def InlineCSSUrls(src_match, filepath=input_filepath):
++ """Helper function that inlines each url on a CSS image rule match."""
++ # Replace contents of url() references in matches.
++ return re.sub(GetUrlRegexString(),
++ lambda m: SrcReplace(m, filepath),
++ src_match.group(0))
++
++ def InlineCSSImports(text, filepath=input_filepath):
++ """Helper function that inlines CSS files included via the @import
++ directive.
++ """
++ return re.sub(r'@import\s+' + GetUrlRegexString() + r';',
++ lambda m: InlineCSSFile(m, '%s', filepath),
++ text)
++
++
++ flat_text = util.ReadFile(input_filename, 'utf-8')
++
++ # Check conditional elements, remove unsatisfied ones from the file. We do
++ # this twice. The first pass is so that we don't even bother calling
++ # InlineScript, InlineCSSFile and InlineIncludeFiles on text we're eventually
++ # going to throw out anyway.
++ flat_text = CheckConditionalElements(flat_text)
++
++ flat_text = _INCLUDE_RE.sub(InlineIncludeFiles, flat_text)
++
++ if not preprocess_only:
++ if strip_whitespace:
++ flat_text = minifier.Minify(flat_text.encode('utf-8'),
++ input_filename).decode('utf-8')
++
++ if not allow_external_script:
++ # We need to inline css and js before we inline images so that image
++ # references gets inlined in the css and js
++ flat_text = re.sub(r'<script (?P<attrs1>.*?)src="(?P<filename>[^"\']*)"'
++ r'(?P<attrs2>.*?)></script>',
++ InlineScript,
++ flat_text)
++
++ flat_text = _STYLESHEET_RE.sub(
++ lambda m: InlineCSSFile(m, '<style>%s</style>'),
++ flat_text)
++
++ # Check conditional elements, second pass. This catches conditionals in any
++ # of the text we just inlined.
++ flat_text = CheckConditionalElements(flat_text)
++
++ # Allow custom modifications before inlining images.
++ if rewrite_function:
++ flat_text = rewrite_function(input_filepath, flat_text, distribution)
++
++ if not preprocess_only:
++ flat_text = _SRC_RE.sub(SrcReplace, flat_text)
++ flat_text = _SRCSET_RE.sub(SrcsetReplace, flat_text)
++
++ # TODO(arv): Only do this inside <style> tags.
++ flat_text = InlineCSSImages(flat_text)
++
++ flat_text = _ICON_RE.sub(SrcReplace, flat_text)
++
++ if names_only:
++ flat_text = None # Will contains garbage if the flag is set anyway.
++ return InlinedData(flat_text, inlined_files)
++
++
++def InlineToString(input_filename, grd_node, preprocess_only = False,
++ allow_external_script=False, strip_whitespace=False,
++ rewrite_function=None, filename_expansion_function=None):
++ """Inlines the resources in a specified file and returns it as a string.
++
++ Args:
++ input_filename: name of file to read in
++ grd_node: html node from the grd file for this include tag
++ Returns:
++ the inlined data as a string
++ """
++ try:
++ return DoInline(
++ input_filename,
++ grd_node,
++ preprocess_only=preprocess_only,
++ allow_external_script=allow_external_script,
++ strip_whitespace=strip_whitespace,
++ rewrite_function=rewrite_function,
++ filename_expansion_function=filename_expansion_function).inlined_data
++ except IOError as e:
++ raise Exception("Failed to open %s while trying to flatten %s. (%s)" %
++ (e.filename, input_filename, e.strerror))
++
++
++def InlineToFile(input_filename, output_filename, grd_node):
++ """Inlines the resources in a specified file and writes it.
++
++ Reads input_filename, finds all the src attributes and attempts to
++ inline the files they are referring to, then writes the result
++ to output_filename.
++
++ Args:
++ input_filename: name of file to read in
++ output_filename: name of file to be written to
++ grd_node: html node from the grd file for this include tag
++ Returns:
++ a set of filenames of all the inlined files
++ """
++ inlined_data = InlineToString(input_filename, grd_node)
++ with open(output_filename, 'wb') as out_file:
++ out_file.write(inlined_data)
++
++
++def GetResourceFilenames(filename,
++ grd_node,
++ allow_external_script=False,
++ rewrite_function=None,
++ filename_expansion_function=None):
++ """For a grd file, returns a set of all the files that would be inline."""
++ try:
++ return DoInline(
++ filename,
++ grd_node,
++ names_only=True,
++ preprocess_only=False,
++ allow_external_script=allow_external_script,
++ strip_whitespace=False,
++ rewrite_function=rewrite_function,
++ filename_expansion_function=filename_expansion_function).inlined_files
++ except IOError as e:
++ raise Exception("Failed to open %s while trying to flatten %s. (%s)" %
++ (e.filename, filename, e.strerror))
+
-+namespace {
+
-+bool TypeHasNonTrivialDtor(const Type* type) {
-+ if (const CXXRecordDecl* cxx_r = type->getCXXRecordDeclForPointerType())
-+ return cxx_r->hasTrivialDestructor();
++def main():
++ if len(sys.argv) <= 2:
++ print("Flattens a HTML file by inlining its external resources.\n")
++ print("html_inline.py inputfile outputfile")
++ else:
++ InlineToFile(sys.argv[1], sys.argv[2], None)
+
-+ return false;
-+}
++if __name__ == '__main__':
++ main()
+diff --git a/tools/grit/grit/format/html_inline_unittest.py b/tools/grit/grit/format/html_inline_unittest.py
+new file mode 100644
+index 0000000000..1b11e9e476
+--- /dev/null
++++ b/tools/grit/grit/format/html_inline_unittest.py
+@@ -0,0 +1,927 @@
++#!/usr/bin/env python
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
+
-+// Returns the underlying Type for |type| by expanding typedefs and removing
-+// any namespace qualifiers.
-+const Type* UnwrapType(const Type* type) {
-+ if (const ElaboratedType* elaborated = dyn_cast<ElaboratedType>(type))
-+ return UnwrapType(elaborated->getNamedType().getTypePtr());
-+ if (const TypedefType* typedefed = dyn_cast<TypedefType>(type))
-+ return UnwrapType(typedefed->desugar().getTypePtr());
-+ return type;
-+}
-+
-+// Searches for constructs that we know we don't want in the Chromium code base.
-+class FindBadConstructsConsumer : public ChromeClassTester {
-+ public:
-+ FindBadConstructsConsumer(CompilerInstance& instance,
-+ bool check_refcounted_dtors,
-+ bool check_virtuals_in_implementations)
-+ : ChromeClassTester(instance),
-+ check_refcounted_dtors_(check_refcounted_dtors),
-+ check_virtuals_in_implementations_(check_virtuals_in_implementations) {
-+ }
++'''Unit tests for grit.format.html_inline'''
+
-+ virtual void CheckChromeClass(SourceLocation record_location,
-+ CXXRecordDecl* record) {
-+ bool implementation_file = InImplementationFile(record_location);
++from __future__ import print_function
+
-+ if (!implementation_file) {
-+ // Only check for "heavy" constructors/destructors in header files;
-+ // within implementation files, there is no performance cost.
-+ CheckCtorDtorWeight(record_location, record);
-+ }
++import os
++import re
++import sys
++if __name__ == '__main__':
++ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
++
++import unittest
++
++from grit import util
++from grit.format import html_inline
++
++
++class HtmlInlineUnittest(unittest.TestCase):
++ '''Unit tests for HtmlInline.'''
++
++ def testGetResourceFilenames(self):
++ '''Tests that all included files are returned by GetResourceFilenames.'''
++
++ files = {
++ 'index.html': '''
++ <!DOCTYPE HTML>
++ <html>
++ <head>
++ <link rel="stylesheet" href="test.css">
++ <link rel="stylesheet"
++ href="really-long-long-long-long-long-test.css">
++ </head>
++ <body>
++ <include src='test.html'>
++ <include
++ src="really-long-long-long-long-long-test-file-omg-so-long.html">
++ </body>
++ </html>
++ ''',
++
++ 'test.html': '''
++ <include src="test2.html">
++ ''',
++
++ 'really-long-long-long-long-long-test-file-omg-so-long.html': '''
++ <!-- This really long named resource should be included. -->
++ ''',
++
++ 'test2.html': '''
++ <!-- This second level resource should also be included. -->
++ ''',
++
++ 'test.css': '''
++ .image {
++ background: url('test.png');
++ }
++ ''',
++
++ 'really-long-long-long-long-long-test.css': '''
++ a:hover {
++ font-weight: bold; /* Awesome effect is awesome! */
++ }
++ ''',
+
-+ if (!implementation_file || check_virtuals_in_implementations_) {
-+ bool warn_on_inline_bodies = !implementation_file;
++ 'test.png': 'PNG DATA',
++ }
+
-+ // Check that all virtual methods are marked accordingly with both
-+ // virtual and OVERRIDE.
-+ CheckVirtualMethods(record_location, record, warn_on_inline_bodies);
++ source_resources = set()
++ tmp_dir = util.TempDir(files)
++ for filename in files:
++ source_resources.add(tmp_dir.GetPath(filename))
++
++ resources = html_inline.GetResourceFilenames(tmp_dir.GetPath('index.html'),
++ None)
++ resources.add(tmp_dir.GetPath('index.html'))
++ self.failUnlessEqual(resources, source_resources)
++ tmp_dir.CleanUp()
++
++ def testUnmatchedEndIfBlock(self):
++ '''Tests that an unmatched </if> raises an exception.'''
++
++ files = {
++ 'index.html': '''
++ <!DOCTYPE HTML>
++ <html>
++ <if expr="lang == 'fr'">
++ bonjour
++ </if>
++ <if expr='lang == "de"'>
++ hallo
++ </if>
++ </if>
++ </html>
++ ''',
+ }
+
-+ if (check_refcounted_dtors_)
-+ CheckRefCountedDtors(record_location, record);
-+ }
++ tmp_dir = util.TempDir(files)
++
++ with self.assertRaises(Exception) as cm:
++ html_inline.GetResourceFilenames(tmp_dir.GetPath('index.html'), None)
++ self.failUnlessEqual(str(cm.exception), 'Unmatched </if>')
++ tmp_dir.CleanUp()
++
++ def testCompressedJavaScript(self):
++ '''Tests that ".src=" doesn't treat as a tag.'''
+
-+ private:
-+ bool check_refcounted_dtors_;
-+ bool check_virtuals_in_implementations_;
-+
-+ // Returns true if |base| specifies one of the Chromium reference counted
-+ // classes (base::RefCounted / base::RefCountedThreadSafe). |user_data| is
-+ // ignored.
-+ static bool IsRefCountedCallback(const CXXBaseSpecifier* base,
-+ CXXBasePath& path,
-+ void* user_data) {
-+ FindBadConstructsConsumer* self =
-+ static_cast<FindBadConstructsConsumer*>(user_data);
-+
-+ const TemplateSpecializationType* base_type =
-+ dyn_cast<TemplateSpecializationType>(
-+ UnwrapType(base->getType().getTypePtr()));
-+ if (!base_type) {
-+ // Base-most definition is not a template, so this cannot derive from
-+ // base::RefCounted. However, it may still be possible to use with a
-+ // scoped_refptr<> and support ref-counting, so this is not a perfect
-+ // guarantee of safety.
-+ return false;
++ files = {
++ 'index.js': '''
++ if(i<j)a.src="hoge.png";
++ ''',
+ }
+
-+ TemplateName name = base_type->getTemplateName();
-+ if (TemplateDecl* decl = name.getAsTemplateDecl()) {
-+ std::string base_name = decl->getNameAsString();
++ source_resources = set()
++ tmp_dir = util.TempDir(files)
++ for filename in files:
++ source_resources.add(tmp_dir.GetPath(filename))
++
++ resources = html_inline.GetResourceFilenames(tmp_dir.GetPath('index.js'),
++ None)
++ resources.add(tmp_dir.GetPath('index.js'))
++ self.failUnlessEqual(resources, source_resources)
++ tmp_dir.CleanUp()
++
++ def testInlineCSSImports(self):
++ '''Tests that @import directives in inlined CSS files are inlined too.
++ '''
++
++ files = {
++ 'index.html': '''
++ <html>
++ <head>
++ <link rel="stylesheet" href="css/test.css">
++ </head>
++ </html>
++ ''',
++
++ 'css/test.css': '''
++ @import url('test2.css');
++ blink {
++ display: none;
++ }
++ ''',
+
-+ // Check for both base::RefCounted and base::RefCountedThreadSafe.
-+ if (base_name.compare(0, 10, "RefCounted") == 0 &&
-+ self->GetNamespace(decl) == "base") {
-+ return true;
++ 'css/test2.css': '''
++ .image {
++ background: url('../images/test.png');
+ }
-+ }
-+ return false;
-+ }
++ '''.strip(),
+
-+ // Prints errors if the destructor of a RefCounted class is public.
-+ void CheckRefCountedDtors(SourceLocation record_location,
-+ CXXRecordDecl* record) {
-+ // Skip anonymous structs.
-+ if (record->getIdentifier() == NULL)
-+ return;
-+
-+ CXXBasePaths paths;
-+ if (!record->lookupInBases(
-+ &FindBadConstructsConsumer::IsRefCountedCallback, this, paths)) {
-+ return; // Class does not derive from a ref-counted base class.
++ 'images/test.png': 'PNG DATA'
+ }
+
-+ if (!record->hasUserDeclaredDestructor()) {
-+ emitWarning(
-+ record_location,
-+ "Classes that are ref-counted should have explicit "
-+ "destructors that are protected or private.");
-+ } else if (CXXDestructorDecl* dtor = record->getDestructor()) {
-+ if (dtor->getAccess() == AS_public) {
-+ emitWarning(
-+ dtor->getInnerLocStart(),
-+ "Classes that are ref-counted should not have "
-+ "public destructors.");
++ expected_inlined = '''
++ <html>
++ <head>
++ <style>
++ .image {
++ background: url('data:image/png;base64,UE5HIERBVEE=');
++ }
++ blink {
++ display: none;
++ }
++ </style>
++ </head>
++ </html>
++ '''
++
++ source_resources = set()
++ tmp_dir = util.TempDir(files)
++ for filename in files:
++ source_resources.add(tmp_dir.GetPath(util.normpath(filename)))
++
++ result = html_inline.DoInline(tmp_dir.GetPath('index.html'), None)
++ resources = result.inlined_files
++ resources.add(tmp_dir.GetPath('index.html'))
++ self.failUnlessEqual(resources, source_resources)
++ self.failUnlessEqual(expected_inlined,
++ util.FixLineEnd(result.inlined_data, '\n'))
++
++ tmp_dir.CleanUp()
++
++ def testInlineIgnoresPolymerBindings(self):
++ '''Tests that polymer bindings are ignored when inlining.
++ '''
++
++ files = {
++ 'index.html': '''
++ <html>
++ <head>
++ <link rel="stylesheet" href="test.css">
++ </head>
++ <body>
++ <iron-icon src="[[icon]]"></iron-icon><!-- Should be ignored. -->
++ <iron-icon src="{{src}}"></iron-icon><!-- Also ignored. -->
++ <!-- [[image]] should be ignored. -->
++ <div style="background: url([[image]]),
++ url('test.png');">
++ </div>
++ <div style="background: url('test.png'),
++ url([[image]]);">
++ </div>
++ </body>
++ </html>
++ ''',
++
++ 'test.css': '''
++ .image {
++ background: url('test.png');
++ background-image: url([[ignoreMe]]);
++ background-image: image-set(url({{alsoMe}}), 1x);
++ background-image: image-set(
++ url({{ignore}}) 1x,
++ url('test.png') 2x);
+ }
++ ''',
++
++ 'test.png': 'PNG DATA'
+ }
-+ }
+
-+ // Prints errors if the constructor/destructor weight is too heavy.
-+ void CheckCtorDtorWeight(SourceLocation record_location,
-+ CXXRecordDecl* record) {
-+ // We don't handle anonymous structs. If this record doesn't have a
-+ // name, it's of the form:
-+ //
-+ // struct {
-+ // ...
-+ // } name_;
-+ if (record->getIdentifier() == NULL)
-+ return;
-+
-+ // Count the number of templated base classes as a feature of whether the
-+ // destructor can be inlined.
-+ int templated_base_classes = 0;
-+ for (CXXRecordDecl::base_class_const_iterator it = record->bases_begin();
-+ it != record->bases_end(); ++it) {
-+ if (it->getTypeSourceInfo()->getTypeLoc().getTypeLocClass() ==
-+ TypeLoc::TemplateSpecialization) {
-+ ++templated_base_classes;
++ expected_inlined = '''
++ <html>
++ <head>
++ <style>
++ .image {
++ background: url('data:image/png;base64,UE5HIERBVEE=');
++ background-image: url([[ignoreMe]]);
++ background-image: image-set(url({{alsoMe}}), 1x);
++ background-image: image-set(
++ url({{ignore}}) 1x,
++ url('data:image/png;base64,UE5HIERBVEE=') 2x);
++ }
++ </style>
++ </head>
++ <body>
++ <iron-icon src="[[icon]]"></iron-icon><!-- Should be ignored. -->
++ <iron-icon src="{{src}}"></iron-icon><!-- Also ignored. -->
++ <!-- [[image]] should be ignored. -->
++ <div style="background: url([[image]]),
++ url('data:image/png;base64,UE5HIERBVEE=');">
++ </div>
++ <div style="background: url('data:image/png;base64,UE5HIERBVEE='),
++ url([[image]]);">
++ </div>
++ </body>
++ </html>
++ '''
++
++ source_resources = set()
++ tmp_dir = util.TempDir(files)
++ for filename in files:
++ source_resources.add(tmp_dir.GetPath(util.normpath(filename)))
++
++ result = html_inline.DoInline(tmp_dir.GetPath('index.html'), None)
++ resources = result.inlined_files
++ resources.add(tmp_dir.GetPath('index.html'))
++ self.failUnlessEqual(resources, source_resources)
++ self.failUnlessEqual(expected_inlined,
++ util.FixLineEnd(result.inlined_data, '\n'))
++
++ tmp_dir.CleanUp()
++
++ def testInlineCSSWithIncludeDirective(self):
++ '''Tests that include directive in external css files also inlined'''
++
++ files = {
++ 'index.html': '''
++ <html>
++ <head>
++ <link rel="stylesheet" href="foo.css">
++ </head>
++ </html>
++ ''',
++
++ 'foo.css': '''<include src="style.css">''',
++
++ 'style.css': '''
++ <include src="style2.css">
++ blink {
++ display: none;
+ }
++ ''',
++ 'style2.css': '''h1 {}''',
+ }
+
-+ // Count the number of trivial and non-trivial member variables.
-+ int trivial_member = 0;
-+ int non_trivial_member = 0;
-+ int templated_non_trivial_member = 0;
-+ for (RecordDecl::field_iterator it = record->field_begin();
-+ it != record->field_end(); ++it) {
-+ CountType(it->getType().getTypePtr(),
-+ &trivial_member,
-+ &non_trivial_member,
-+ &templated_non_trivial_member);
++ expected_inlined = '''
++ <html>
++ <head>
++ <style>
++ h1 {}
++ blink {
++ display: none;
++ }
++ </style>
++ </head>
++ </html>
++ '''
++
++ source_resources = set()
++ tmp_dir = util.TempDir(files)
++ for filename in files:
++ source_resources.add(tmp_dir.GetPath(filename))
++
++ result = html_inline.DoInline(tmp_dir.GetPath('index.html'), None)
++ resources = result.inlined_files
++ resources.add(tmp_dir.GetPath('index.html'))
++ self.failUnlessEqual(resources, source_resources)
++ self.failUnlessEqual(expected_inlined,
++ util.FixLineEnd(result.inlined_data, '\n'))
++ tmp_dir.CleanUp()
++
++ def testCssIncludedFileNames(self):
++ '''Tests that all included files from css are returned'''
++
++ files = {
++ 'index.html': '''
++ <!DOCTYPE HTML>
++ <html>
++ <head>
++ <link rel="stylesheet" href="test.css">
++ </head>
++ <body>
++ </body>
++ </html>
++ ''',
++
++ 'test.css': '''
++ <include src="test2.css">
++ ''',
++
++ 'test2.css': '''
++ <include src="test3.css">
++ .image {
++ background: url('test.png');
++ }
++ ''',
++
++ 'test3.css': '''h1 {}''',
++
++ 'test.png': 'PNG DATA'
+ }
+
-+ // Check to see if we need to ban inlined/synthesized constructors. Note
-+ // that the cutoffs here are kind of arbitrary. Scores over 10 break.
-+ int dtor_score = 0;
-+ // Deriving from a templated base class shouldn't be enough to trigger
-+ // the ctor warning, but if you do *anything* else, it should.
-+ //
-+ // TODO(erg): This is motivated by templated base classes that don't have
-+ // any data members. Somehow detect when templated base classes have data
-+ // members and treat them differently.
-+ dtor_score += templated_base_classes * 9;
-+ // Instantiating a template is an insta-hit.
-+ dtor_score += templated_non_trivial_member * 10;
-+ // The fourth normal class member should trigger the warning.
-+ dtor_score += non_trivial_member * 3;
-+
-+ int ctor_score = dtor_score;
-+ // You should be able to have 9 ints before we warn you.
-+ ctor_score += trivial_member;
-+
-+ if (ctor_score >= 10) {
-+ if (!record->hasUserDeclaredConstructor()) {
-+ emitWarning(record_location,
-+ "Complex class/struct needs an explicit out-of-line "
-+ "constructor.");
-+ } else {
-+ // Iterate across all the constructors in this file and yell if we
-+ // find one that tries to be inline.
-+ for (CXXRecordDecl::ctor_iterator it = record->ctor_begin();
-+ it != record->ctor_end(); ++it) {
-+ if (it->hasInlineBody()) {
-+ if (it->isCopyConstructor() &&
-+ !record->hasUserDeclaredCopyConstructor()) {
-+ emitWarning(record_location,
-+ "Complex class/struct needs an explicit out-of-line "
-+ "copy constructor.");
-+ } else {
-+ emitWarning(it->getInnerLocStart(),
-+ "Complex constructor has an inlined body.");
-+ }
-+ }
-+ }
++ source_resources = set()
++ tmp_dir = util.TempDir(files)
++ for filename in files:
++ source_resources.add(tmp_dir.GetPath(filename))
++
++ resources = html_inline.GetResourceFilenames(tmp_dir.GetPath('index.html'),
++ None)
++ resources.add(tmp_dir.GetPath('index.html'))
++ self.failUnlessEqual(resources, source_resources)
++ tmp_dir.CleanUp()
++
++ def testInlineCSSLinks(self):
++ '''Tests that only CSS files referenced via relative URLs are inlined.'''
++
++ files = {
++ 'index.html': '''
++ <html>
++ <head>
++ <link rel="stylesheet" href="foo.css">
++ <link rel="stylesheet" href="chrome://resources/bar.css">
++ </head>
++ </html>
++ ''',
++
++ 'foo.css': '''
++ @import url(chrome://resources/blurp.css);
++ blink {
++ display: none;
+ }
++ ''',
+ }
+
-+ // The destructor side is equivalent except that we don't check for
-+ // trivial members; 20 ints don't need a destructor.
-+ if (dtor_score >= 10 && !record->hasTrivialDestructor()) {
-+ if (!record->hasUserDeclaredDestructor()) {
-+ emitWarning(
-+ record_location,
-+ "Complex class/struct needs an explicit out-of-line "
-+ "destructor.");
-+ } else if (CXXDestructorDecl* dtor = record->getDestructor()) {
-+ if (dtor->hasInlineBody()) {
-+ emitWarning(dtor->getInnerLocStart(),
-+ "Complex destructor has an inline body.");
-+ }
++ expected_inlined = '''
++ <html>
++ <head>
++ <style>
++ @import url(chrome://resources/blurp.css);
++ blink {
++ display: none;
+ }
++ </style>
++ <link rel="stylesheet" href="chrome://resources/bar.css">
++ </head>
++ </html>
++ '''
++
++ source_resources = set()
++ tmp_dir = util.TempDir(files)
++ for filename in files:
++ source_resources.add(tmp_dir.GetPath(filename))
++
++ result = html_inline.DoInline(tmp_dir.GetPath('index.html'), None)
++ resources = result.inlined_files
++ resources.add(tmp_dir.GetPath('index.html'))
++ self.failUnlessEqual(resources, source_resources)
++ self.failUnlessEqual(expected_inlined,
++ util.FixLineEnd(result.inlined_data, '\n'))
++ tmp_dir.CleanUp()
++
++ def testFilenameVariableExpansion(self):
++ '''Tests that variables are expanded in filenames before inlining.'''
++
++ files = {
++ 'index.html': '''
++ <html>
++ <head>
++ <link rel="stylesheet" href="style[WHICH].css">
++ <script src="script[WHICH].js"></script>
++ </head>
++ <include src="tmpl[WHICH].html">
++ <img src="img[WHICH].png">
++ </html>
++ ''',
++ 'style1.css': '''h1 {}''',
++ 'tmpl1.html': '''<h1></h1>''',
++ 'script1.js': '''console.log('hello');''',
++ 'img1.png': '''abc''',
+ }
-+ }
+
-+ void CheckVirtualMethod(const CXXMethodDecl* method,
-+ bool warn_on_inline_bodies) {
-+ if (!method->isVirtual())
-+ return;
++ expected_inlined = '''
++ <html>
++ <head>
++ <style>h1 {}</style>
++ <script>console.log('hello');</script>
++ </head>
++ <h1></h1>
++ <img src="data:image/png;base64,YWJj">
++ </html>
++ '''
++
++ source_resources = set()
++ tmp_dir = util.TempDir(files)
++ for filename in files:
++ source_resources.add(tmp_dir.GetPath(filename))
++
++ def replacer(var, repl):
++ return lambda filename: filename.replace('[%s]' % var, repl)
++
++ # Test normal inlining.
++ result = html_inline.DoInline(
++ tmp_dir.GetPath('index.html'),
++ None,
++ filename_expansion_function=replacer('WHICH', '1'))
++ resources = result.inlined_files
++ resources.add(tmp_dir.GetPath('index.html'))
++ self.failUnlessEqual(resources, source_resources)
++ self.failUnlessEqual(expected_inlined,
++ util.FixLineEnd(result.inlined_data, '\n'))
++
++ # Test names-only inlining.
++ result = html_inline.DoInline(
++ tmp_dir.GetPath('index.html'),
++ None,
++ names_only=True,
++ filename_expansion_function=replacer('WHICH', '1'))
++ resources = result.inlined_files
++ resources.add(tmp_dir.GetPath('index.html'))
++ self.failUnlessEqual(resources, source_resources)
++ tmp_dir.CleanUp()
++
++ def testWithCloseTags(self):
++ '''Tests that close tags are removed.'''
++
++ files = {
++ 'index.html': '''
++ <html>
++ <head>
++ <link rel="stylesheet" href="style1.css"></link>
++ <link rel="stylesheet" href="style2.css">
++ </link>
++ <link rel="stylesheet" href="style2.css"
++ >
++ </link>
++ <script src="script1.js"></script>
++ </head>
++ <include src="tmpl1.html"></include>
++ <include src="tmpl2.html">
++ </include>
++ <include src="tmpl2.html"
++ >
++ </include>
++ <img src="img1.png">
++ <include src='single-double-quotes.html"></include>
++ <include src="double-single-quotes.html'></include>
++ </html>
++ ''',
++ 'style1.css': '''h1 {}''',
++ 'style2.css': '''h2 {}''',
++ 'tmpl1.html': '''<h1></h1>''',
++ 'tmpl2.html': '''<h2></h2>''',
++ 'script1.js': '''console.log('hello');''',
++ 'img1.png': '''abc''',
++ }
+
-+ if (!method->isVirtualAsWritten()) {
-+ SourceLocation loc = method->getTypeSpecStartLoc();
-+ if (isa<CXXDestructorDecl>(method))
-+ loc = method->getInnerLocStart();
-+ emitWarning(loc, "Overriding method must have \"virtual\" keyword.");
++ expected_inlined = '''
++ <html>
++ <head>
++ <style>h1 {}</style>
++ <style>h2 {}</style>
++ <style>h2 {}</style>
++ <script>console.log('hello');</script>
++ </head>
++ <h1></h1>
++ <h2></h2>
++ <h2></h2>
++ <img src="data:image/png;base64,YWJj">
++ <include src='single-double-quotes.html"></include>
++ <include src="double-single-quotes.html'></include>
++ </html>
++ '''
++
++ source_resources = set()
++ tmp_dir = util.TempDir(files)
++ for filename in files:
++ source_resources.add(tmp_dir.GetPath(filename))
++
++ # Test normal inlining.
++ result = html_inline.DoInline(
++ tmp_dir.GetPath('index.html'),
++ None)
++ resources = result.inlined_files
++ resources.add(tmp_dir.GetPath('index.html'))
++ self.failUnlessEqual(resources, source_resources)
++ self.failUnlessEqual(expected_inlined,
++ util.FixLineEnd(result.inlined_data, '\n'))
++ tmp_dir.CleanUp()
++
++ def testCommentedJsInclude(self):
++ '''Tests that <include> works inside a comment.'''
++
++ files = {
++ 'include.js': '// <include src="other.js">',
++ 'other.js': '// Copyright somebody\nalert(1);',
+ }
+
-+ // Virtual methods should not have inline definitions beyond "{}". This
-+ // only matters for header files.
-+ if (warn_on_inline_bodies && method->hasBody() &&
-+ method->hasInlineBody()) {
-+ if (CompoundStmt* cs = dyn_cast<CompoundStmt>(method->getBody())) {
-+ if (cs->size()) {
-+ emitWarning(
-+ cs->getLBracLoc(),
-+ "virtual methods with non-empty bodies shouldn't be "
-+ "declared inline.");
-+ }
-+ }
++ expected_inlined = '// Copyright somebody\nalert(1);'
++
++ source_resources = set()
++ tmp_dir = util.TempDir(files)
++ for filename in files:
++ source_resources.add(tmp_dir.GetPath(filename))
++
++ result = html_inline.DoInline(tmp_dir.GetPath('include.js'), None)
++ resources = result.inlined_files
++ resources.add(tmp_dir.GetPath('include.js'))
++ self.failUnlessEqual(resources, source_resources)
++ self.failUnlessEqual(expected_inlined,
++ util.FixLineEnd(result.inlined_data, '\n'))
++ tmp_dir.CleanUp()
++
++ def testCommentedJsIf(self):
++ '''Tests that <if> works inside a comment.'''
++
++ files = {
++ 'if.js': '''
++ // <if expr="True">
++ yep();
++ // </if>
++
++ // <if expr="False">
++ nope();
++ // </if>
++ ''',
+ }
-+ }
+
-+ bool InTestingNamespace(const Decl* record) {
-+ return GetNamespace(record).find("testing") != std::string::npos;
-+ }
++ expected_inlined = '''
++ //
++ yep();
++ //
++
++ //
++ '''
++
++ source_resources = set()
++ tmp_dir = util.TempDir(files)
++ for filename in files:
++ source_resources.add(tmp_dir.GetPath(filename))
++
++ class FakeGrdNode(object):
++ def EvaluateCondition(self, cond):
++ return eval(cond)
++
++ result = html_inline.DoInline(tmp_dir.GetPath('if.js'), FakeGrdNode())
++ resources = result.inlined_files
++
++ resources.add(tmp_dir.GetPath('if.js'))
++ self.failUnlessEqual(resources, source_resources)
++ self.failUnlessEqual(expected_inlined,
++ util.FixLineEnd(result.inlined_data, '\n'))
++ tmp_dir.CleanUp()
++
++ def testImgSrcset(self):
++ '''Tests that img srcset="" attributes are converted.'''
++
++ # Note that there is no space before "img10.png" and that
++ # "img11.png" has no descriptor.
++ files = {
++ 'index.html': '''
++ <html>
++ <img src="img1.png" srcset="img2.png 1x, img3.png 2x">
++ <img src="img4.png" srcset=" img5.png 1x , img6.png 2x ">
++ <img src="chrome://theme/img11.png" srcset="img7.png 1x, '''\
++ '''chrome://theme/img13.png 2x">
++ <img srcset="img8.png 300w, img9.png 11E-2w,img10.png -1e2w">
++ <img srcset="img11.png">
++ <img srcset="img11.png, img2.png 1x">
++ <img srcset="img2.png 1x, img11.png">
++ </html>
++ ''',
++ 'img1.png': '''a1''',
++ 'img2.png': '''a2''',
++ 'img3.png': '''a3''',
++ 'img4.png': '''a4''',
++ 'img5.png': '''a5''',
++ 'img6.png': '''a6''',
++ 'img7.png': '''a7''',
++ 'img8.png': '''a8''',
++ 'img9.png': '''a9''',
++ 'img10.png': '''a10''',
++ 'img11.png': '''a11''',
++ }
+
-+ bool IsMethodInBannedNamespace(const CXXMethodDecl* method) {
-+ if (InBannedNamespace(method))
-+ return true;
-+ for (CXXMethodDecl::method_iterator i = method->begin_overridden_methods();
-+ i != method->end_overridden_methods();
-+ ++i) {
-+ const CXXMethodDecl* overridden = *i;
-+ if (IsMethodInBannedNamespace(overridden))
-+ return true;
++ expected_inlined = '''
++ <html>
++ <img src="data:image/png;base64,YTE=" srcset="data:image/png;base64,'''\
++ '''YTI= 1x,data:image/png;base64,YTM= 2x">
++ <img src="data:image/png;base64,YTQ=" srcset="data:image/png;base64,'''\
++ '''YTU= 1x,data:image/png;base64,YTY= 2x">
++ <img src="chrome://theme/img11.png" srcset="data:image/png;base64,'''\
++ '''YTc= 1x,chrome://theme/img13.png 2x">
++ <img srcset="data:image/png;base64,YTg= 300w,data:image/png;base64,'''\
++ '''YTk= 11E-2w,data:image/png;base64,YTEw -1e2w">
++ <img srcset="data:image/png;base64,YTEx">
++ <img srcset="data:image/png;base64,YTEx,data:image/png;base64,YTI= 1x">
++ <img srcset="data:image/png;base64,YTI= 1x,data:image/png;base64,YTEx">
++ </html>
++ '''
++
++ source_resources = set()
++ tmp_dir = util.TempDir(files)
++ for filename in files:
++ source_resources.add(tmp_dir.GetPath(filename))
++
++ # Test normal inlining.
++ result = html_inline.DoInline(
++ tmp_dir.GetPath('index.html'),
++ None)
++ resources = result.inlined_files
++ resources.add(tmp_dir.GetPath('index.html'))
++ self.failUnlessEqual(resources, source_resources)
++ self.failUnlessEqual(expected_inlined,
++ util.FixLineEnd(result.inlined_data, '\n'))
++ tmp_dir.CleanUp()
++
++ def testImgSrcsetIgnoresI18n(self):
++ '''Tests that $i18n{...} strings are ignored when inlining.
++ '''
++
++ src_html = '''
++ <html>
++ <head></head>
++ <body>
++ <img srcset="$i18n{foo}">
++ </body>
++ </html>
++ '''
++
++ files = {
++ 'index.html': src_html,
+ }
+
-+ return false;
-+ }
++ expected_inlined = src_html
++
++ source_resources = set()
++ tmp_dir = util.TempDir(files)
++ for filename in files:
++ source_resources.add(tmp_dir.GetPath(util.normpath(filename)))
++
++ result = html_inline.DoInline(tmp_dir.GetPath('index.html'), None)
++ resources = result.inlined_files
++ resources.add(tmp_dir.GetPath('index.html'))
++ self.failUnlessEqual(resources, source_resources)
++ self.failUnlessEqual(expected_inlined,
++ util.FixLineEnd(result.inlined_data, '\n'))
++ tmp_dir.CleanUp()
++
++ def testSourceSrcset(self):
++ '''Tests that source srcset="" attributes are converted.'''
++
++ # Note that there is no space before "img10.png" and that
++ # "img11.png" has no descriptor.
++ files = {
++ 'index.html': '''
++ <html>
++ <source src="img1.png" srcset="img2.png 1x, img3.png 2x">
++ <source src="img4.png" srcset=" img5.png 1x , img6.png 2x ">
++ <source src="chrome://theme/img11.png" srcset="img7.png 1x, '''\
++ '''chrome://theme/img13.png 2x">
++ <source srcset="img8.png 300w, img9.png 11E-2w,img10.png -1e2w">
++ <source srcset="img11.png">
++ </html>
++ ''',
++ 'img1.png': '''a1''',
++ 'img2.png': '''a2''',
++ 'img3.png': '''a3''',
++ 'img4.png': '''a4''',
++ 'img5.png': '''a5''',
++ 'img6.png': '''a6''',
++ 'img7.png': '''a7''',
++ 'img8.png': '''a8''',
++ 'img9.png': '''a9''',
++ 'img10.png': '''a10''',
++ 'img11.png': '''a11''',
++ }
++
++ expected_inlined = '''
++ <html>
++ <source src="data:image/png;base64,YTE=" srcset="data:image/png;'''\
++ '''base64,YTI= 1x,data:image/png;base64,YTM= 2x">
++ <source src="data:image/png;base64,YTQ=" srcset="data:image/png;'''\
++ '''base64,YTU= 1x,data:image/png;base64,YTY= 2x">
++ <source src="chrome://theme/img11.png" srcset="data:image/png;'''\
++ '''base64,YTc= 1x,chrome://theme/img13.png 2x">
++ <source srcset="data:image/png;base64,YTg= 300w,data:image/png;'''\
++ '''base64,YTk= 11E-2w,data:image/png;base64,YTEw -1e2w">
++ <source srcset="data:image/png;base64,YTEx">
++ </html>
++ '''
++
++ source_resources = set()
++ tmp_dir = util.TempDir(files)
++ for filename in files:
++ source_resources.add(tmp_dir.GetPath(filename))
++
++ # Test normal inlining.
++ result = html_inline.DoInline(tmp_dir.GetPath('index.html'), None)
++ resources = result.inlined_files
++ resources.add(tmp_dir.GetPath('index.html'))
++ self.failUnlessEqual(resources, source_resources)
++ self.failUnlessEqual(expected_inlined,
++ util.FixLineEnd(result.inlined_data, '\n'))
++ tmp_dir.CleanUp()
++
++ def testConditionalInclude(self):
++ '''Tests that output and dependency generation includes only files not'''\
++ ''' blocked by <if> macros.'''
++
++ files = {
++ 'index.html': '''
++ <html>
++ <if expr="True">
++ <img src="img1.png" srcset="img2.png 1x, img3.png 2x">
++ </if>
++ <if expr="False">
++ <img src="img4.png" srcset=" img5.png 1x, img6.png 2x ">
++ </if>
++ <if expr="True">
++ <img src="chrome://theme/img11.png" srcset="img7.png 1x, '''\
++ '''chrome://theme/img13.png 2x">
++ </if>
++ <img srcset="img8.png 300w, img9.png 11E-2w,img10.png -1e2w">
++ </html>
++ ''',
++ 'img1.png': '''a1''',
++ 'img2.png': '''a2''',
++ 'img3.png': '''a3''',
++ 'img4.png': '''a4''',
++ 'img5.png': '''a5''',
++ 'img6.png': '''a6''',
++ 'img7.png': '''a7''',
++ 'img8.png': '''a8''',
++ 'img9.png': '''a9''',
++ 'img10.png': '''a10''',
++ }
+
-+ void CheckOverriddenMethod(const CXXMethodDecl* method) {
-+ if (!method->size_overridden_methods() || method->getAttr<OverrideAttr>())
-+ return;
++ expected_inlined = '''
++ <html>
++ <img src="data:image/png;base64,YTE=" srcset="data:image/png;base64,'''\
++ '''YTI= 1x,data:image/png;base64,YTM= 2x">
++ <img src="chrome://theme/img11.png" srcset="data:image/png;base64,'''\
++ '''YTc= 1x,chrome://theme/img13.png 2x">
++ <img srcset="data:image/png;base64,YTg= 300w,data:image/png;base64,'''\
++ '''YTk= 11E-2w,data:image/png;base64,YTEw -1e2w">
++ </html>
++ '''
++
++ expected_files = [
++ 'index.html',
++ 'img1.png',
++ 'img2.png',
++ 'img3.png',
++ 'img7.png',
++ 'img8.png',
++ 'img9.png',
++ 'img10.png'
++ ]
++
++ source_resources = set()
++ tmp_dir = util.TempDir(files)
++ for filename in expected_files:
++ source_resources.add(tmp_dir.GetPath(filename))
++
++ class FakeGrdNode(object):
++ def EvaluateCondition(self, cond):
++ return eval(cond)
++
++ # Test normal inlining.
++ result = html_inline.DoInline(
++ tmp_dir.GetPath('index.html'),
++ FakeGrdNode())
++ resources = result.inlined_files
++ resources.add(tmp_dir.GetPath('index.html'))
++ self.failUnlessEqual(resources, source_resources)
++
++ # ignore whitespace
++ expected_inlined = re.sub(r'\s+', ' ', expected_inlined)
++ actually_inlined = re.sub(r'\s+', ' ',
++ util.FixLineEnd(result.inlined_data, '\n'))
++ self.failUnlessEqual(expected_inlined, actually_inlined);
++ tmp_dir.CleanUp()
++
++ def testPreprocessOnlyEvaluatesIncludeAndIf(self):
++ '''Tests that preprocess_only=true evaluates <include> and <if> only. '''
++
++ files = {
++ 'index.html': '''
++ <html>
++ <head>
++ <link rel="stylesheet" href="not_inlined.css">
++ <script src="also_not_inlined.js">
++ </head>
++ <body>
++ <include src="inline_this.html">
++ <if expr="True">
++ <p>'if' should be evaluated.</p>
++ </if>
++ </body>
++ </html>
++ ''',
++ 'not_inlined.css': ''' /* <link> should not be inlined. */ ''',
++ 'also_not_inlined.js': ''' // <script> should not be inlined. ''',
++ 'inline_this.html': ''' <p>'include' should be inlined.</p> '''
++ }
++
++ expected_inlined = '''
++ <html>
++ <head>
++ <link rel="stylesheet" href="not_inlined.css">
++ <script src="also_not_inlined.js">
++ </head>
++ <body>
++ <p>'include' should be inlined.</p>
++ <p>'if' should be evaluated.</p>
++ </body>
++ </html>
++ '''
++
++ source_resources = set()
++ tmp_dir = util.TempDir(files)
++ source_resources.add(tmp_dir.GetPath('index.html'))
++ source_resources.add(tmp_dir.GetPath('inline_this.html'))
++
++ result = html_inline.DoInline(tmp_dir.GetPath('index.html'), None,
++ preprocess_only=True)
++ resources = result.inlined_files
++ resources.add(tmp_dir.GetPath('index.html'))
++ self.failUnlessEqual(resources, source_resources)
++
++ # Ignore whitespace
++ expected_inlined = re.sub(r'\s+', ' ', expected_inlined)
++ actually_inlined = re.sub(r'\s+', ' ',
++ util.FixLineEnd(result.inlined_data, '\n'))
++ self.failUnlessEqual(expected_inlined, actually_inlined)
++
++ tmp_dir.CleanUp()
++
++ def testPreprocessOnlyAppliesRecursively(self):
++ '''Tests that preprocess_only=true propagates to included files. '''
++
++ files = {
++ 'index.html': '''
++ <html>
++ <include src="outer_include.html">
++ </html>
++ ''',
++ 'outer_include.html': '''
++ <include src="inner_include.html">
++ <link rel="stylesheet" href="not_inlined.css">
++ ''',
++ 'inner_include.html': ''' <p>This should be inlined in index.html</p> ''',
++ 'not_inlined.css': ''' /* This should not be inlined. */ '''
++ }
+
-+ if (isa<CXXDestructorDecl>(method) || method->isPure())
-+ return;
++ expected_inlined = '''
++ <html>
++ <p>This should be inlined in index.html</p>
++ <link rel="stylesheet" href="not_inlined.css">
++ </html>
++ '''
++
++ source_resources = set()
++ tmp_dir = util.TempDir(files)
++ source_resources.add(tmp_dir.GetPath('index.html'))
++ source_resources.add(tmp_dir.GetPath('outer_include.html'))
++ source_resources.add(tmp_dir.GetPath('inner_include.html'))
++
++ result = html_inline.DoInline(tmp_dir.GetPath('index.html'), None,
++ preprocess_only=True)
++ resources = result.inlined_files
++ resources.add(tmp_dir.GetPath('index.html'))
++ self.failUnlessEqual(resources, source_resources)
++
++ # Ignore whitespace
++ expected_inlined = re.sub(r'\s+', ' ', expected_inlined)
++ actually_inlined = re.sub(r'\s+', ' ',
++ util.FixLineEnd(result.inlined_data, '\n'))
++ self.failUnlessEqual(expected_inlined, actually_inlined)
++
++ tmp_dir.CleanUp()
+
-+ if (IsMethodInBannedNamespace(method))
-+ return;
++if __name__ == '__main__':
++ unittest.main()
+diff --git a/tools/grit/grit/format/minifier.py b/tools/grit/grit/format/minifier.py
+new file mode 100644
+index 0000000000..1a0ea34e49
+--- /dev/null
++++ b/tools/grit/grit/format/minifier.py
+@@ -0,0 +1,45 @@
++# Copyright 2016 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++"""Framework for stripping whitespace and comments from resource files"""
+
-+ SourceLocation loc = method->getTypeSpecStartLoc();
-+ emitWarning(loc, "Overriding method must be marked with OVERRIDE.");
++from __future__ import print_function
++
++from os import path
++import subprocess
++import sys
++
++import six
++
++__js_minifier = None
++__css_minifier = None
++
++def SetJsMinifier(minifier):
++ global __js_minifier
++ __js_minifier = minifier.split()
++
++def SetCssMinifier(minifier):
++ global __css_minifier
++ __css_minifier = minifier.split()
++
++def Minify(source, filename):
++ """Minify |source| (bytes) from |filename| and return bytes."""
++ file_type = path.splitext(filename)[1]
++ minifier = None
++ if file_type == '.js':
++ minifier = __js_minifier
++ elif file_type == '.css':
++ minifier = __css_minifier
++ if not minifier:
++ return source
++ p = subprocess.Popen(
++ minifier,
++ stdin=subprocess.PIPE,
++ stdout=subprocess.PIPE,
++ stderr=subprocess.PIPE)
++ (stdout, stderr) = p.communicate(source)
++ if p.returncode != 0:
++ print('Minification failed for %s' % filename)
++ print(stderr)
++ sys.exit(p.returncode)
++ return stdout
+diff --git a/tools/grit/grit/format/policy_templates_json.py b/tools/grit/grit/format/policy_templates_json.py
+new file mode 100644
+index 0000000000..2f9330bb9a
+--- /dev/null
++++ b/tools/grit/grit/format/policy_templates_json.py
+@@ -0,0 +1,26 @@
++# Copyright 2017 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++"""Translates policy_templates.json files.
++"""
++
++from __future__ import print_function
++
++from grit.node import structure
++
++
++def Format(root, lang='en', output_dir='.'):
++ policy_json = None
++ for item in root.ActiveDescendants():
++ with item:
++ if (isinstance(item, structure.StructureNode) and
++ item.attrs['type'] == 'policy_template_metafile'):
++ json_text = item.gatherer.Translate(
++ lang,
++ pseudo_if_not_available=item.PseudoIsAllowed(),
++ fallback_to_english=item.ShouldFallbackToEnglish())
++ # We're only expecting one node of this kind.
++ assert not policy_json
++ policy_json = json_text
++ return policy_json
+diff --git a/tools/grit/grit/format/policy_templates_json_unittest.py b/tools/grit/grit/format/policy_templates_json_unittest.py
+new file mode 100644
+index 0000000000..e252c94e2c
+--- /dev/null
++++ b/tools/grit/grit/format/policy_templates_json_unittest.py
+@@ -0,0 +1,207 @@
++#!/usr/bin/env python
++# coding: utf-8
++# Copyright 2017 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++"""Unittest for policy_templates_json.py.
++"""
++
++from __future__ import print_function
++
++import os
++import sys
++if __name__ == '__main__':
++ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
++
++import grit.extern.tclib
++import tempfile
++import unittest
++
++from six import StringIO
++
++from grit import grd_reader
++from grit.tool import build
++
++
++class PolicyTemplatesJsonUnittest(unittest.TestCase):
++
++ def testPolicyTranslation(self):
++ # Create test policy_templates.json data.
++ caption = "The main policy"
++ caption_translation = "Die Hauptrichtlinie"
++
++ message = \
++ "Red cabbage stays red cabbage and wedding dress stays wedding dress"
++ message_translation = \
++ "Blaukraut bleibt Blaukraut und Brautkleid bleibt Brautkleid"
++
++ schema_key_description = "Number of users"
++ schema_key_description_translation = "Anzahl der Nutzer"
++
++ policy_json = """
++ {
++ "policy_definitions": [
++ {
++ 'name': 'MainPolicy',
++ 'type': 'main',
++ 'owners': ['foo@bar.com'],
++ 'schema': {
++ 'properties': {
++ 'default_launch_container': {
++ 'enum': [
++ 'tab',
++ 'window',
++ ],
++ 'type': 'string',
++ },
++ 'users_number': {
++ 'description': '''%s''',
++ 'type': 'integer',
++ },
++ },
++ 'type': 'object',
++ },
++ 'supported_on': ['chrome_os:29-'],
++ 'features': {
++ 'can_be_recommended': True,
++ 'dynamic_refresh': True,
++ },
++ 'example_value': True,
++ 'caption': '''%s''',
++ 'tags': [],
++ 'desc': '''This policy does stuff.'''
++ },
++ ],
++ "policy_atomic_group_definitions": [],
++ "placeholders": [],
++ "messages": {
++ 'message_string_id': {
++ 'desc': '''The description is removed from the grit output''',
++ 'text': '''%s'''
++ }
++ }
++ }""" % (schema_key_description, caption, message)
++
++ # Create translations. The translation IDs are hashed from the English text.
++ caption_id = grit.extern.tclib.GenerateMessageId(caption);
++ message_id = grit.extern.tclib.GenerateMessageId(message);
++ schema_key_description_id = grit.extern.tclib.GenerateMessageId(
++ schema_key_description)
++ policy_xtb = """
++<?xml version="1.0" ?>
++<!DOCTYPE translationbundle>
++<translationbundle lang="de">
++<translation id="%s">%s</translation>
++<translation id="%s">%s</translation>
++<translation id="%s">%s</translation>
++</translationbundle>""" % (caption_id, caption_translation,
++ message_id, message_translation,
++ schema_key_description_id,
++ schema_key_description_translation)
++
++ # Write both to a temp file.
++ tmp_dir_name = tempfile.gettempdir()
++
++ json_file_path = os.path.join(tmp_dir_name, 'test.json')
++ with open(json_file_path, 'w') as f:
++ f.write(policy_json.strip())
++
++ xtb_file_path = os.path.join(tmp_dir_name, 'test.xtb')
++ with open(xtb_file_path, 'w') as f:
++ f.write(policy_xtb.strip())
++
++ # Assemble a test grit tree, similar to policy_templates.grd.
++ grd_text = '''
++ <grit base_dir="." latest_public_release="0" current_release="1" source_lang_id="en">
++ <translations>
++ <file path="%s" lang="de" />
++ </translations>
++ <release seq="1">
++ <structures>
++ <structure name="IDD_POLICY_SOURCE_FILE" file="%s" type="policy_template_metafile" />
++ </structures>
++ </release>
++ </grit>''' % (xtb_file_path, json_file_path)
++ grd_string_io = StringIO(grd_text)
++
++ # Parse the grit tree and load the policies' JSON with a gatherer.
++ grd = grd_reader.Parse(grd_string_io, dir=tmp_dir_name, defines={'_google_chrome': True})
++ grd.SetOutputLanguage('en')
++ grd.RunGatherers()
++
++ # Remove the temp files.
++ os.unlink(xtb_file_path)
++ os.unlink(json_file_path)
++
++ # Run grit with en->de translation.
++ env_lang = 'en'
++ out_lang = 'de'
++ env_defs = {'_google_chrome': '1'}
++
++ grd.SetOutputLanguage(env_lang)
++ grd.SetDefines(env_defs)
++ buf = StringIO()
++ build.RcBuilder.ProcessNode(grd, DummyOutput('policy_templates', out_lang), buf)
++ output = buf.getvalue()
++
++ # Caption and message texts get taken from xtb.
++ # desc is 'translated' to some pseudo-English
++ # 'ThïPïs pôPôlïPïcýPý dôéPôés stüPüff'.
++ expected = u"""{
++ "policy_definitions": [
++ {
++ "caption": "%s",
++ "desc": "Th\xefP\xefs p\xf4P\xf4l\xefP\xefc\xfdP\xfd d\xf4\xe9P\xf4\xe9s st\xfcP\xfcff.",
++ "example_value": true,
++ "features": {"can_be_recommended": true, "dynamic_refresh": true},
++ "name": "MainPolicy",
++ "owners": ["foo@bar.com"],
++ "schema": {
++ "properties": {
++ "default_launch_container": {
++ "enum": [
++ "tab",
++ "window"
++ ],
++ "type": "string"
++ },
++ "users_number": {
++ "description": "%s",
++ "type": "integer"
++ }
++ },
++ "type": "object"
++ },
++ "supported_on": ["chrome_os:29-"],
++ "tags": [],
++ "type": "main"
++ }
++ ],
++ "policy_atomic_group_definitions": [
++ ],
++ "messages": {
++ "message_string_id": {
++ "text": "%s"
++ }
+ }
+
-+ // Makes sure there is a "virtual" keyword on virtual methods.
-+ //
-+ // Gmock objects trigger these for each MOCK_BLAH() macro used. So we have a
-+ // trick to get around that. If a class has member variables whose types are
-+ // in the "testing" namespace (which is how gmock works behind the scenes),
-+ // there's a really high chance we won't care about these errors
-+ void CheckVirtualMethods(SourceLocation record_location,
-+ CXXRecordDecl* record,
-+ bool warn_on_inline_bodies) {
-+ for (CXXRecordDecl::field_iterator it = record->field_begin();
-+ it != record->field_end(); ++it) {
-+ CXXRecordDecl* record_type =
-+ it->getTypeSourceInfo()->getTypeLoc().getTypePtr()->
-+ getAsCXXRecordDecl();
-+ if (record_type) {
-+ if (InTestingNamespace(record_type)) {
-+ return;
-+ }
++}""" % (caption_translation, schema_key_description_translation,
++ message_translation)
++ self.assertEqual(expected, output)
++
++
++class DummyOutput(object):
++
++ def __init__(self, type, language):
++ self.type = type
++ self.language = language
++
++ def GetType(self):
++ return self.type
++
++ def GetLanguage(self):
++ return self.language
++
++ def GetOutputFilename(self):
++ return 'hello.gif'
+diff --git a/tools/grit/grit/format/rc.py b/tools/grit/grit/format/rc.py
+new file mode 100644
+index 0000000000..ed32bb809e
+--- /dev/null
++++ b/tools/grit/grit/format/rc.py
+@@ -0,0 +1,474 @@
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++'''Support for formatting an RC file for compilation.
++'''
++
++from __future__ import print_function
++
++import os
++import re
++from functools import partial
++
++import six
++
++from grit import util
++from grit.node import misc
++
++
++def Format(root, lang='en', output_dir='.'):
++ from grit.node import empty, include, message, structure
++
++ yield _FormatHeader(root, lang, output_dir)
++
++ for item in root.ActiveDescendants():
++ if isinstance(item, empty.MessagesNode):
++ # Write one STRINGTABLE per <messages> container.
++ # This is hacky: it iterates over the children twice.
++ yield 'STRINGTABLE\nBEGIN\n'
++ for subitem in item.ActiveDescendants():
++ if isinstance(subitem, message.MessageNode):
++ with subitem:
++ yield FormatMessage(subitem, lang)
++ yield 'END\n\n'
++ elif isinstance(item, include.IncludeNode):
++ with item:
++ yield FormatInclude(item, lang, output_dir)
++ elif isinstance(item, structure.StructureNode):
++ with item:
++ yield FormatStructure(item, lang, output_dir)
++
++
++'''
++This dictionary defines the language charset pair lookup table, which is used
++for replacing the GRIT expand variables for language info in Product Version
++resource. The key is the language ISO country code, and the value
++is the language and character-set pair, which is a hexadecimal string
++consisting of the concatenation of the language and character-set identifiers.
++The first 4 digit of the value is the hex value of LCID, the remaining
++4 digits is the hex value of character-set id(code page)of the language.
++
++LCID resource: http://msdn.microsoft.com/en-us/library/ms776294.aspx
++Codepage resource: http://www.science.co.il/language/locale-codes.asp
++
++We have defined three GRIT expand_variables to be used in the version resource
++file to set the language info. Here is an example how they should be used in
++the VS_VERSION_INFO section of the resource file to allow GRIT to localize
++the language info correctly according to product locale.
++
++VS_VERSION_INFO VERSIONINFO
++...
++BEGIN
++ BLOCK "StringFileInfo"
++ BEGIN
++ BLOCK "[GRITVERLANGCHARSETHEX]"
++ BEGIN
++ ...
++ END
++ END
++ BLOCK "VarFileInfo"
++ BEGIN
++ VALUE "Translation", [GRITVERLANGID], [GRITVERCHARSETID]
++ END
++END
++
++'''
++
++_LANGUAGE_CHARSET_PAIR = {
++ # Language neutral LCID, unicode(1200) code page.
++ 'neutral' : '000004b0',
++ # LANG_USER_DEFAULT LCID, unicode(1200) code page.
++ 'userdefault' : '040004b0',
++ 'ar' : '040104e8',
++ 'fi' : '040b04e4',
++ 'ko' : '041203b5',
++ 'es' : '0c0a04e4',
++ 'bg' : '040204e3',
++ # No codepage for filipino, use unicode(1200).
++ 'fil' : '046404e4',
++ 'fr' : '040c04e4',
++ 'lv' : '042604e9',
++ 'sv' : '041d04e4',
++ 'ca' : '040304e4',
++ 'de' : '040704e4',
++ 'lt' : '042704e9',
++ # Do not use! This is only around for backwards
++ # compatibility and will be removed - use fil instead
++ 'tl' : '0c0004b0',
++ 'zh-CN' : '080403a8',
++ 'zh-TW' : '040403b6',
++ 'zh-HK' : '0c0403b6',
++ 'el' : '040804e5',
++ 'no' : '001404e4',
++ 'nb' : '041404e4',
++ 'nn' : '081404e4',
++ 'th' : '041e036a',
++ 'he' : '040d04e7',
++ 'iw' : '040d04e7',
++ 'pl' : '041504e2',
++ 'tr' : '041f04e6',
++ 'hr' : '041a04e4',
++ # No codepage for Hindi, use unicode(1200).
++ 'hi' : '043904b0',
++ 'pt-PT' : '081604e4',
++ 'pt-BR' : '041604e4',
++ 'uk' : '042204e3',
++ 'cs' : '040504e2',
++ 'hu' : '040e04e2',
++ 'ro' : '041804e2',
++ # No codepage for Urdu, use unicode(1200).
++ 'ur' : '042004b0',
++ 'da' : '040604e4',
++ 'is' : '040f04e4',
++ 'ru' : '041904e3',
++ 'vi' : '042a04ea',
++ 'nl' : '041304e4',
++ 'id' : '042104e4',
++ 'sr' : '081a04e2',
++ 'en-GB' : '0809040e',
++ 'it' : '041004e4',
++ 'sk' : '041b04e2',
++ 'et' : '042504e9',
++ 'ja' : '041103a4',
++ 'sl' : '042404e2',
++ 'en' : '040904b0',
++ # LCID for Mexico; Windows does not support L.A. LCID.
++ 'es-419' : '080a04e4',
++ # No codepage for Bengali, use unicode(1200).
++ 'bn' : '044504b0',
++ 'fa' : '042904e8',
++ # No codepage for Gujarati, use unicode(1200).
++ 'gu' : '044704b0',
++ # No codepage for Kannada, use unicode(1200).
++ 'kn' : '044b04b0',
++ # Malay (Malaysia) [ms-MY]
++ 'ms' : '043e04e4',
++ # No codepage for Malayalam, use unicode(1200).
++ 'ml' : '044c04b0',
++ # No codepage for Marathi, use unicode(1200).
++ 'mr' : '044e04b0',
++ # No codepage for Oriya , use unicode(1200).
++ 'or' : '044804b0',
++ # No codepage for Tamil, use unicode(1200).
++ 'ta' : '044904b0',
++ # No codepage for Telugu, use unicode(1200).
++ 'te' : '044a04b0',
++ # No codepage for Amharic, use unicode(1200). >= Vista.
++ 'am' : '045e04b0',
++ 'sw' : '044104e4',
++ 'af' : '043604e4',
++ 'eu' : '042d04e4',
++ 'fr-CA' : '0c0c04e4',
++ 'gl' : '045604e4',
++ # No codepage for Zulu, use unicode(1200).
++ 'zu' : '043504b0',
++ 'fake-bidi' : '040d04e7',
++}
++
++# Language ID resource: http://msdn.microsoft.com/en-us/library/ms776294.aspx
++#
++# There is no appropriate sublang for Spanish (Latin America) [es-419], so we
++# use Mexico. SUBLANG_DEFAULT would incorrectly map to Spain. Unlike other
++# Latin American countries, Mexican Spanish is supported by VERSIONINFO:
++# http://msdn.microsoft.com/en-us/library/aa381058.aspx
++
++_LANGUAGE_DIRECTIVE_PAIR = {
++ 'neutral' : 'LANG_NEUTRAL, SUBLANG_NEUTRAL',
++ 'userdefault' : 'LANG_NEUTRAL, SUBLANG_DEFAULT',
++ 'ar' : 'LANG_ARABIC, SUBLANG_DEFAULT',
++ 'fi' : 'LANG_FINNISH, SUBLANG_DEFAULT',
++ 'ko' : 'LANG_KOREAN, SUBLANG_KOREAN',
++ 'es' : 'LANG_SPANISH, SUBLANG_SPANISH_MODERN',
++ 'bg' : 'LANG_BULGARIAN, SUBLANG_DEFAULT',
++ # LANG_FILIPINO (100) not in VC 7 winnt.h.
++ 'fil' : '100, SUBLANG_DEFAULT',
++ 'fr' : 'LANG_FRENCH, SUBLANG_FRENCH',
++ 'lv' : 'LANG_LATVIAN, SUBLANG_DEFAULT',
++ 'sv' : 'LANG_SWEDISH, SUBLANG_SWEDISH',
++ 'ca' : 'LANG_CATALAN, SUBLANG_DEFAULT',
++ 'de' : 'LANG_GERMAN, SUBLANG_GERMAN',
++ 'lt' : 'LANG_LITHUANIAN, SUBLANG_LITHUANIAN',
++ # Do not use! See above.
++ 'tl' : 'LANG_NEUTRAL, SUBLANG_DEFAULT',
++ 'zh-CN' : 'LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED',
++ 'zh-TW' : 'LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL',
++ 'zh-HK' : 'LANG_CHINESE, SUBLANG_CHINESE_HONGKONG',
++ 'el' : 'LANG_GREEK, SUBLANG_DEFAULT',
++ 'no' : 'LANG_NORWEGIAN, SUBLANG_DEFAULT',
++ 'nb' : 'LANG_NORWEGIAN, SUBLANG_NORWEGIAN_BOKMAL',
++ 'nn' : 'LANG_NORWEGIAN, SUBLANG_NORWEGIAN_NYNORSK',
++ 'th' : 'LANG_THAI, SUBLANG_DEFAULT',
++ 'he' : 'LANG_HEBREW, SUBLANG_DEFAULT',
++ 'iw' : 'LANG_HEBREW, SUBLANG_DEFAULT',
++ 'pl' : 'LANG_POLISH, SUBLANG_DEFAULT',
++ 'tr' : 'LANG_TURKISH, SUBLANG_DEFAULT',
++ 'hr' : 'LANG_CROATIAN, SUBLANG_DEFAULT',
++ 'hi' : 'LANG_HINDI, SUBLANG_DEFAULT',
++ 'pt-PT' : 'LANG_PORTUGUESE, SUBLANG_PORTUGUESE',
++ 'pt-BR' : 'LANG_PORTUGUESE, SUBLANG_DEFAULT',
++ 'uk' : 'LANG_UKRAINIAN, SUBLANG_DEFAULT',
++ 'cs' : 'LANG_CZECH, SUBLANG_DEFAULT',
++ 'hu' : 'LANG_HUNGARIAN, SUBLANG_DEFAULT',
++ 'ro' : 'LANG_ROMANIAN, SUBLANG_DEFAULT',
++ 'ur' : 'LANG_URDU, SUBLANG_DEFAULT',
++ 'da' : 'LANG_DANISH, SUBLANG_DEFAULT',
++ 'is' : 'LANG_ICELANDIC, SUBLANG_DEFAULT',
++ 'ru' : 'LANG_RUSSIAN, SUBLANG_DEFAULT',
++ 'vi' : 'LANG_VIETNAMESE, SUBLANG_DEFAULT',
++ 'nl' : 'LANG_DUTCH, SUBLANG_DEFAULT',
++ 'id' : 'LANG_INDONESIAN, SUBLANG_DEFAULT',
++ 'sr' : 'LANG_SERBIAN, SUBLANG_SERBIAN_LATIN',
++ 'en-GB' : 'LANG_ENGLISH, SUBLANG_ENGLISH_UK',
++ 'it' : 'LANG_ITALIAN, SUBLANG_DEFAULT',
++ 'sk' : 'LANG_SLOVAK, SUBLANG_DEFAULT',
++ 'et' : 'LANG_ESTONIAN, SUBLANG_DEFAULT',
++ 'ja' : 'LANG_JAPANESE, SUBLANG_DEFAULT',
++ 'sl' : 'LANG_SLOVENIAN, SUBLANG_DEFAULT',
++ 'en' : 'LANG_ENGLISH, SUBLANG_ENGLISH_US',
++ # No L.A. sublang exists.
++ 'es-419' : 'LANG_SPANISH, SUBLANG_SPANISH_MEXICAN',
++ 'bn' : 'LANG_BENGALI, SUBLANG_DEFAULT',
++ 'fa' : 'LANG_PERSIAN, SUBLANG_DEFAULT',
++ 'gu' : 'LANG_GUJARATI, SUBLANG_DEFAULT',
++ 'kn' : 'LANG_KANNADA, SUBLANG_DEFAULT',
++ 'ms' : 'LANG_MALAY, SUBLANG_DEFAULT',
++ 'ml' : 'LANG_MALAYALAM, SUBLANG_DEFAULT',
++ 'mr' : 'LANG_MARATHI, SUBLANG_DEFAULT',
++ 'or' : 'LANG_ORIYA, SUBLANG_DEFAULT',
++ 'ta' : 'LANG_TAMIL, SUBLANG_DEFAULT',
++ 'te' : 'LANG_TELUGU, SUBLANG_DEFAULT',
++ 'am' : 'LANG_AMHARIC, SUBLANG_DEFAULT',
++ 'sw' : 'LANG_SWAHILI, SUBLANG_DEFAULT',
++ 'af' : 'LANG_AFRIKAANS, SUBLANG_DEFAULT',
++ 'eu' : 'LANG_BASQUE, SUBLANG_DEFAULT',
++ 'fr-CA' : 'LANG_FRENCH, SUBLANG_FRENCH_CANADIAN',
++ 'gl' : 'LANG_GALICIAN, SUBLANG_DEFAULT',
++ 'zu' : 'LANG_ZULU, SUBLANG_DEFAULT',
++ 'pa' : 'LANG_PUNJABI, SUBLANG_PUNJABI_INDIA',
++ 'sa' : 'LANG_SANSKRIT, SUBLANG_SANSKRIT_INDIA',
++ 'si' : 'LANG_SINHALESE, SUBLANG_SINHALESE_SRI_LANKA',
++ 'ne' : 'LANG_NEPALI, SUBLANG_NEPALI_NEPAL',
++ 'ti' : 'LANG_TIGRIGNA, SUBLANG_TIGRIGNA_ERITREA',
++ 'fake-bidi' : 'LANG_HEBREW, SUBLANG_DEFAULT',
++}
++
++# A note on 'no-specific-language' in the following few functions:
++# Some build systems may wish to call GRIT to scan for dependencies in
++# a language-agnostic way, and can then specify this fake language as
++# the output context. It should never be used when output is actually
++# being generated.
++
++def GetLangCharsetPair(language):
++ if language in _LANGUAGE_CHARSET_PAIR:
++ return _LANGUAGE_CHARSET_PAIR[language]
++ if language != 'no-specific-language':
++ print('Warning:GetLangCharsetPair() found undefined language %s' % language)
++ return ''
++
++def GetLangDirectivePair(language):
++ if language in _LANGUAGE_DIRECTIVE_PAIR:
++ return _LANGUAGE_DIRECTIVE_PAIR[language]
++
++ # We don't check for 'no-specific-language' here because this
++ # function should only get called when output is being formatted,
++ # and at that point we would not want to get
++ # 'no-specific-language' passed as the language.
++ print('Warning:GetLangDirectivePair() found undefined language %s' % language)
++ return 'unknown language: see tools/grit/format/rc.py'
++
++def GetLangIdHex(language):
++ if language in _LANGUAGE_CHARSET_PAIR:
++ langcharset = _LANGUAGE_CHARSET_PAIR[language]
++ lang_id = '0x' + langcharset[0:4]
++ return lang_id
++ if language != 'no-specific-language':
++ print('Warning:GetLangIdHex() found undefined language %s' % language)
++ return ''
++
++
++def GetCharsetIdDecimal(language):
++ if language in _LANGUAGE_CHARSET_PAIR:
++ langcharset = _LANGUAGE_CHARSET_PAIR[language]
++ charset_decimal = int(langcharset[4:], 16)
++ return str(charset_decimal)
++ if language != 'no-specific-language':
++ print('Warning:GetCharsetIdDecimal() found undefined language %s' % language)
++ return ''
++
++
++def GetUnifiedLangCode(language) :
++ r = re.compile('([a-z]{1,2})_([a-z]{1,2})')
++ if r.match(language) :
++ underscore = language.find('_')
++ return language[0:underscore] + '-' + language[underscore + 1:].upper()
++ return language
++
++
++def RcSubstitutions(substituter, lang):
++ '''Add language-based substitutions for Rc files to the substitutor.'''
++ unified_lang_code = GetUnifiedLangCode(lang)
++ substituter.AddSubstitutions({
++ 'GRITVERLANGCHARSETHEX': GetLangCharsetPair(unified_lang_code),
++ 'GRITVERLANGID': GetLangIdHex(unified_lang_code),
++ 'GRITVERCHARSETID': GetCharsetIdDecimal(unified_lang_code)})
++
++
++def _FormatHeader(root, lang, output_dir):
++ '''Returns the required preamble for RC files.'''
++ assert isinstance(lang, six.string_types)
++ assert isinstance(root, misc.GritNode)
++ # Find the location of the resource header file, so that we can include
++ # it.
++ resource_header = 'resource.h' # fall back to this
++ language_directive = ''
++ for output in root.GetOutputFiles():
++ if output.attrs['type'] == 'rc_header':
++ resource_header = os.path.abspath(output.GetOutputFilename())
++ resource_header = util.MakeRelativePath(output_dir, resource_header)
++ if output.attrs['lang'] != lang:
++ continue
++ if output.attrs['language_section'] == '':
++ # If no language_section is requested, no directive is added
++ # (Used when the generated rc will be included from another rc
++ # file that will have the appropriate language directive)
++ language_directive = ''
++ elif output.attrs['language_section'] == 'neutral':
++ # If a neutral language section is requested (default), add a
++ # neutral language directive
++ language_directive = 'LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL'
++ elif output.attrs['language_section'] == 'lang':
++ language_directive = 'LANGUAGE %s' % GetLangDirectivePair(lang)
++ resource_header = resource_header.replace('\\', '\\\\')
++ return '''// This file is automatically generated by GRIT. Do not edit.
++
++#include "%s"
++#include <winresrc.h>
++#ifdef IDC_STATIC
++#undef IDC_STATIC
++#endif
++#define IDC_STATIC (-1)
++
++%s
++
++
++''' % (resource_header, language_directive)
++# end _FormatHeader() function
++
++
++def FormatMessage(item, lang):
++ '''Returns a single message of a string table.'''
++ message = item.ws_at_start + item.Translate(lang) + item.ws_at_end
++ # Escape quotation marks (RC format uses doubling-up
++ message = message.replace('"', '""')
++ # Replace linebreaks with a \n escape
++ message = util.LINEBREAKS.sub(r'\\n', message)
++ if hasattr(item.GetRoot(), 'GetSubstituter'):
++ substituter = item.GetRoot().GetSubstituter()
++ message = substituter.Substitute(message)
++
++ name_attr = item.GetTextualIds()[0]
++
++ return ' %-15s "%s"\n' % (name_attr, message)
++
++
++def _FormatSection(item, lang, output_dir):
++ '''Writes out an .rc file section.'''
++ assert isinstance(lang, six.string_types)
++ from grit.node import structure
++ assert isinstance(item, structure.StructureNode)
++
++ if item.IsExcludedFromRc():
++ return ''
++
++ text = item.gatherer.Translate(
++ lang, skeleton_gatherer=item.GetSkeletonGatherer(),
++ pseudo_if_not_available=item.PseudoIsAllowed(),
++ fallback_to_english=item.ShouldFallbackToEnglish()) + '\n\n'
++
++ # Replace the language expand_variables in version rc info.
++ if item.ExpandVariables() and hasattr(item.GetRoot(), 'GetSubstituter'):
++ substituter = item.GetRoot().GetSubstituter()
++ text = substituter.Substitute(text)
++ return text
++
++
++def FormatInclude(item, lang, output_dir, type=None, process_html=False):
++ '''Formats an item that is included in an .rc file (e.g. an ICON).
++
++ Args:
++ item: an IncludeNode or StructureNode
++ lang, output_dir: standard formatter parameters
++ type: .rc file resource type, e.g. 'ICON' (ignored unless item is a
++ StructureNode)
++ process_html: False/True (ignored unless item is a StructureNode)
++ '''
++ assert isinstance(lang, six.string_types)
++ from grit.node import structure
++ from grit.node import include
++ assert isinstance(item, (structure.StructureNode, include.IncludeNode))
++
++ if isinstance(item, include.IncludeNode):
++ type = item.attrs['type'].upper()
++ process_html = item.attrs['flattenhtml'] == 'true'
++ filename_only = item.attrs['filenameonly'] == 'true'
++ relative_path = item.attrs['relativepath'] == 'true'
++ else:
++ assert (isinstance(item, structure.StructureNode) and item.attrs['type'] in
++ ['admin_template', 'chrome_html', 'chrome_scaled_image',
++ 'tr_html', 'txt'])
++ filename_only = False
++ relative_path = False
++
++ # By default, we use relative pathnames to included resources so that
++ # sharing the resulting .rc files is possible.
++ #
++ # The FileForLanguage() Function has the side effect of generating the file
++ # if needed (e.g. if it is an HTML file include).
++ file_for_lang = item.FileForLanguage(lang, output_dir)
++ if file_for_lang is None:
++ return ''
++
++ filename = os.path.abspath(file_for_lang)
++ if process_html:
++ filename = item.Process(output_dir)
++ elif filename_only:
++ filename = os.path.basename(filename)
++ elif relative_path:
++ filename = util.MakeRelativePath(output_dir, filename)
++
++ filename = filename.replace('\\', '\\\\') # escape for the RC format
++
++ if isinstance(item, structure.StructureNode) and item.IsExcludedFromRc():
++ return ''
++
++ name = item.attrs['name']
++ item_id = item.GetRoot().GetIdMap()[name]
++ return '// ID: %d\n%-18s %-18s "%s"\n' % (item_id, name, type, filename)
++
++
++def _DoNotFormat(item, lang, output_dir):
++ return ''
++
++
++# Formatter instance to use for each type attribute
++# when formatting Structure nodes.
++_STRUCTURE_FORMATTERS = {
++ 'accelerators' : _FormatSection,
++ 'dialog' : _FormatSection,
++ 'menu' : _FormatSection,
++ 'rcdata' : _FormatSection,
++ 'version' : _FormatSection,
++ 'admin_template' : partial(FormatInclude, type='ADM'),
++ 'chrome_html' : partial(FormatInclude, type='BINDATA',
++ process_html=True),
++ 'chrome_scaled_image' : partial(FormatInclude, type='BINDATA'),
++ 'tr_html' : partial(FormatInclude, type='HTML'),
++ 'txt' : partial(FormatInclude, type='TXT'),
++ 'policy_template_metafile': _DoNotFormat,
++}
++
++
++def FormatStructure(item, lang, output_dir):
++ formatter = _STRUCTURE_FORMATTERS[item.attrs['type']]
++ return formatter(item, lang, output_dir)
+diff --git a/tools/grit/grit/format/rc_header.py b/tools/grit/grit/format/rc_header.py
+new file mode 100644
+index 0000000000..ea2c217f53
+--- /dev/null
++++ b/tools/grit/grit/format/rc_header.py
+@@ -0,0 +1,48 @@
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++'''Item formatters for RC headers.
++'''
++
++from __future__ import print_function
++
++
++def Format(root, lang='en', output_dir='.'):
++ yield '''\
++// This file is automatically generated by GRIT. Do not edit.
++
++#pragma once
++'''
++ # Check for emit nodes under the rc_header. If any emit node
++ # is present, we assume it means the GRD file wants to override
++ # the default header, with no includes.
++ default_includes = ['#include <atlres.h>', '']
++ emit_lines = []
++ for output_node in root.GetOutputFiles():
++ if output_node.GetType() == 'rc_header':
++ for child in output_node.children:
++ if child.name == 'emit' and child.attrs['emit_type'] == 'prepend':
++ emit_lines.append(child.GetCdata())
++ for line in emit_lines or default_includes:
++ yield line + '\n'
++ if root.IsWhitelistSupportEnabled():
++ yield '#include "ui/base/resource/whitelist.h"\n'
++ for line in FormatDefines(root):
++ yield line
++
++
++def FormatDefines(root):
++ '''Yields #define SYMBOL 1234 lines.
++
++ Args:
++ root: A GritNode.
++ '''
++ tids = root.GetIdMap()
++ rc_header_format = '#define {0} {1}\n'
++ if root.IsWhitelistSupportEnabled():
++ rc_header_format = '#define {0} (::ui::WhitelistedResource<{1}>(), {1})\n'
++ for item in root.ActiveDescendants():
++ with item:
++ for tid in item.GetTextualIds():
++ yield rc_header_format.format(tid, tids[tid])
+diff --git a/tools/grit/grit/format/rc_header_unittest.py b/tools/grit/grit/format/rc_header_unittest.py
+new file mode 100644
+index 0000000000..eed4d70a99
+--- /dev/null
++++ b/tools/grit/grit/format/rc_header_unittest.py
+@@ -0,0 +1,138 @@
++#!/usr/bin/env python
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++'''Unit tests for the rc_header formatter'''
++
++# GRD samples exceed the 80 character limit.
++# pylint: disable-msg=C6310
++
++from __future__ import print_function
++
++import os
++import sys
++import unittest
++
++if __name__ == '__main__':
++ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
++
++from grit import util
++from grit.format import rc_header
++
++
++class RcHeaderFormatterUnittest(unittest.TestCase):
++ def FormatAll(self, grd):
++ output = rc_header.FormatDefines(grd)
++ return ''.join(output).replace(' ', '')
++
++ def testFormatter(self):
++ grd = util.ParseGrdForUnittest('''
++ <includes first_id="300" comment="bingo">
++ <include type="gif" name="ID_LOGO" file="images/logo.gif" />
++ </includes>
++ <messages first_id="10000">
++ <message name="IDS_GREETING" desc="Printed to greet the currently logged in user">
++ Hello <ph name="USERNAME">%s<ex>Joi</ex></ph>, how are you doing today?
++ </message>
++ <message name="IDS_BONGO">
++ Bongo!
++ </message>
++ </messages>
++ <structures>
++ <structure type="dialog" name="IDD_NARROW_DIALOG" file="rc_files/dialogs.rc" />
++ <structure type="version" name="VS_VERSION_INFO" file="rc_files/version.rc" />
++ </structures>''')
++ output = self.FormatAll(grd)
++ self.failUnless(output.count('IDS_GREETING10000'))
++ self.failUnless(output.count('ID_LOGO300'))
++
++ def testOnlyDefineResourcesThatSatisfyOutputCondition(self):
++ grd = util.ParseGrdForUnittest('''
++ <includes first_id="300" comment="bingo">
++ <include type="gif" name="ID_LOGO" file="images/logo.gif" />
++ </includes>
++ <messages first_id="10000">
++ <message name="IDS_FIRSTPRESENTSTRING" desc="Present in .rc file.">
++ I will appear in the .rc file.
++ </message>
++ <if expr="False"> <!--Do not include in the .rc files until used.-->
++ <message name="IDS_MISSINGSTRING" desc="Not present in .rc file.">
++ I will not appear in the .rc file.
++ </message>
++ </if>
++ <if expr="lang != 'es'">
++ <message name="IDS_LANGUAGESPECIFICSTRING" desc="Present in .rc file.">
++ Hello.
++ </message>
++ </if>
++ <if expr="lang == 'es'">
++ <message name="IDS_LANGUAGESPECIFICSTRING" desc="Present in .rc file.">
++ Hola.
++ </message>
++ </if>
++ <message name="IDS_THIRDPRESENTSTRING" desc="Present in .rc file.">
++ I will also appear in the .rc file.
++ </message>
++ </messages>''')
++ output = self.FormatAll(grd)
++ self.failUnless(output.count('IDS_FIRSTPRESENTSTRING10000'))
++ self.failIf(output.count('IDS_MISSINGSTRING'))
++ self.failUnless(output.count('IDS_LANGUAGESPECIFICSTRING10002'))
++ self.failUnless(output.count('IDS_THIRDPRESENTSTRING10003'))
++
++ def testEmit(self):
++ grd = util.ParseGrdForUnittest('''
++ <outputs>
++ <output type="rc_all" filename="dummy">
++ <emit emit_type="prepend">Wrong</emit>
++ </output>
++ <if expr="False">
++ <output type="rc_header" filename="dummy">
++ <emit emit_type="prepend">No</emit>
++ </output>
++ </if>
++ <output type="rc_header" filename="dummy">
++ <emit emit_type="append">Error</emit>
++ </output>
++ <output type="rc_header" filename="dummy">
++ <emit emit_type="prepend">Bingo</emit>
++ </output>
++ </outputs>''')
++ output = ''.join(rc_header.Format(grd, 'en', '.'))
++ output = util.StripBlankLinesAndComments(output)
++ self.assertEqual('#pragma once\nBingo', output)
++
++ def testRcHeaderFormat(self):
++ grd = util.ParseGrdForUnittest('''
++ <includes first_id="300" comment="bingo">
++ <include type="gif" name="IDR_LOGO" file="images/logo.gif" />
++ </includes>
++ <messages first_id="10000">
++ <message name="IDS_GREETING" desc="Printed to greet the currently logged in user">
++ Hello <ph name="USERNAME">%s<ex>Joi</ex></ph>, how are you doing today?
++ </message>
++ <message name="IDS_BONGO">
++ Bongo!
++ </message>
++ </messages>''')
++
++ # Using the default settings.
++ output = rc_header.FormatDefines(grd)
++ self.assertEqual(('#define IDR_LOGO 300\n'
++ '#define IDS_GREETING 10000\n'
++ '#define IDS_BONGO 10001\n'), ''.join(output))
++
++ # Using resource whitelist support.
++ grd.SetWhitelistSupportEnabled(True)
++ output = rc_header.FormatDefines(grd)
++ self.assertEqual(('#define IDR_LOGO '
++ '(::ui::WhitelistedResource<300>(), 300)\n'
++ '#define IDS_GREETING '
++ '(::ui::WhitelistedResource<10000>(), 10000)\n'
++ '#define IDS_BONGO '
++ '(::ui::WhitelistedResource<10001>(), 10001)\n'),
++ ''.join(output))
++
++if __name__ == '__main__':
++ unittest.main()
+diff --git a/tools/grit/grit/format/rc_unittest.py b/tools/grit/grit/format/rc_unittest.py
+new file mode 100644
+index 0000000000..d23f063596
+--- /dev/null
++++ b/tools/grit/grit/format/rc_unittest.py
+@@ -0,0 +1,415 @@
++#!/usr/bin/env python
++# Copyright (c) 2011 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++'''Unit tests for grit.format.rc'''
++
++from __future__ import print_function
++
++import os
++import re
++import sys
++if __name__ == '__main__':
++ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
++
++import tempfile
++import unittest
++
++from six import StringIO
++
++from grit import grd_reader
++from grit import util
++from grit.node import structure
++from grit.tool import build
++
++
++_PREAMBLE = '''\
++#include "resource.h"
++#include <winresrc.h>
++#ifdef IDC_STATIC
++#undef IDC_STATIC
++#endif
++#define IDC_STATIC (-1)
++'''
++
++
++class DummyOutput(object):
++ def __init__(self, type, language, file = 'hello.gif'):
++ self.type = type
++ self.language = language
++ self.file = file
++
++ def GetType(self):
++ return self.type
++
++ def GetLanguage(self):
++ return self.language
++
++ def GetOutputFilename(self):
++ return self.file
++
++
++class FormatRcUnittest(unittest.TestCase):
++ def testMessages(self):
++ root = util.ParseGrdForUnittest("""
++ <messages>
++ <message name="IDS_BTN_GO" desc="Button text" meaning="verb">Go!</message>
++ <message name="IDS_GREETING" desc="Printed to greet the currently logged in user">
++ Hello <ph name="USERNAME">%s<ex>Joi</ex></ph>, how are you doing today?
++ </message>
++ <message name="BONGO" desc="Flippo nippo">
++ Howdie "Mr. Elephant", how are you doing? '''
++ </message>
++ <message name="IDS_WITH_LINEBREAKS">
++Good day sir,
++I am a bee
++Sting sting
++ </message>
++ </messages>
++ """)
++
++ buf = StringIO()
++ build.RcBuilder.ProcessNode(root, DummyOutput('rc_all', 'en'), buf)
++ output = util.StripBlankLinesAndComments(buf.getvalue())
++ self.assertEqual(_PREAMBLE + u'''\
++STRINGTABLE
++BEGIN
++ IDS_BTN_GO "Go!"
++ IDS_GREETING "Hello %s, how are you doing today?"
++ BONGO "Howdie ""Mr. Elephant"", how are you doing? "
++ IDS_WITH_LINEBREAKS "Good day sir,\\nI am a bee\\nSting sting"
++END''', output)
++
++ def testRcSection(self):
++ root = util.ParseGrdForUnittest(r'''
++ <structures>
++ <structure type="menu" name="IDC_KLONKMENU" file="grit\testdata\klonk.rc" encoding="utf-16" />
++ <structure type="dialog" name="IDD_ABOUTBOX" file="grit\testdata\klonk.rc" encoding="utf-16" />
++ <structure type="version" name="VS_VERSION_INFO" file="grit\testdata\klonk.rc" encoding="utf-16" />
++ </structures>''')
++ root.SetOutputLanguage('en')
++ root.RunGatherers()
++
++ buf = StringIO()
++ build.RcBuilder.ProcessNode(root, DummyOutput('rc_all', 'en'), buf)
++ output = util.StripBlankLinesAndComments(buf.getvalue())
++ expected = _PREAMBLE + u'''\
++IDC_KLONKMENU MENU
++BEGIN
++ POPUP "&File"
++ BEGIN
++ MENUITEM "E&xit", IDM_EXIT
++ MENUITEM "This be ""Klonk"" me like", ID_FILE_THISBE
++ POPUP "gonk"
++ BEGIN
++ MENUITEM "Klonk && is [good]", ID_GONK_KLONKIS
++ END
++ END
++ POPUP "&Help"
++ BEGIN
++ MENUITEM "&About ...", IDM_ABOUT
++ END
++END
++
++IDD_ABOUTBOX DIALOGEX 22, 17, 230, 75
++STYLE DS_SETFONT | DS_MODALFRAME | WS_CAPTION | WS_SYSMENU
++CAPTION "About"
++FONT 8, "System", 0, 0, 0x0
++BEGIN
++ ICON IDI_KLONK,IDC_MYICON,14,9,20,20
++ LTEXT "klonk Version ""yibbee"" 1.0",IDC_STATIC,49,10,119,8,
++ SS_NOPREFIX
++ LTEXT "Copyright (C) 2005",IDC_STATIC,49,20,119,8
++ DEFPUSHBUTTON "OK",IDOK,195,6,30,11,WS_GROUP
++ CONTROL "Jack ""Black"" Daniels",IDC_RADIO1,"Button",
++ BS_AUTORADIOBUTTON,46,51,84,10
++END
++
++VS_VERSION_INFO VERSIONINFO
++ FILEVERSION 1,0,0,1
++ PRODUCTVERSION 1,0,0,1
++ FILEFLAGSMASK 0x17L
++#ifdef _DEBUG
++ FILEFLAGS 0x1L
++#else
++ FILEFLAGS 0x0L
++#endif
++ FILEOS 0x4L
++ FILETYPE 0x1L
++ FILESUBTYPE 0x0L
++BEGIN
++ BLOCK "StringFileInfo"
++ BEGIN
++ BLOCK "040904b0"
++ BEGIN
++ VALUE "FileDescription", "klonk Application"
++ VALUE "FileVersion", "1, 0, 0, 1"
++ VALUE "InternalName", "klonk"
++ VALUE "LegalCopyright", "Copyright (C) 2005"
++ VALUE "OriginalFilename", "klonk.exe"
++ VALUE "ProductName", " klonk Application"
++ VALUE "ProductVersion", "1, 0, 0, 1"
++ END
++ END
++ BLOCK "VarFileInfo"
++ BEGIN
++ VALUE "Translation", 0x409, 1200
++ END
++END'''.strip()
++ for expected_line, output_line in zip(expected.split(), output.split()):
++ self.assertEqual(expected_line, output_line)
++
++ def testRcIncludeStructure(self):
++ root = util.ParseGrdForUnittest('''
++ <structures>
++ <structure type="tr_html" name="IDR_HTML" file="bingo.html"/>
++ <structure type="tr_html" name="IDR_HTML2" file="bingo2.html"/>
++ </structures>''', base_dir = '/temp')
++ # We do not run gatherers as it is not needed and wouldn't find the file
++
++ buf = StringIO()
++ build.RcBuilder.ProcessNode(root, DummyOutput('rc_all', 'en'), buf)
++ output = util.StripBlankLinesAndComments(buf.getvalue())
++ expected = (_PREAMBLE +
++ u'IDR_HTML HTML "%s"\n'
++ u'IDR_HTML2 HTML "%s"'
++ % (util.normpath('/temp/bingo.html').replace('\\', '\\\\'),
++ util.normpath('/temp/bingo2.html').replace('\\', '\\\\')))
++ # hackety hack to work on win32&lin
++ output = re.sub(r'"[c-zC-Z]:', '"', output)
++ self.assertEqual(expected, output)
++
++ def testRcIncludeFile(self):
++ root = util.ParseGrdForUnittest('''
++ <includes>
++ <include type="TXT" name="TEXT_ONE" file="bingo.txt"/>
++ <include type="TXT" name="TEXT_TWO" file="bingo2.txt" filenameonly="true" />
++ </includes>''', base_dir = '/temp')
++
++ buf = StringIO()
++ build.RcBuilder.ProcessNode(root, DummyOutput('rc_all', 'en'), buf)
++ output = util.StripBlankLinesAndComments(buf.getvalue())
++ expected = (_PREAMBLE +
++ u'TEXT_ONE TXT "%s"\n'
++ u'TEXT_TWO TXT "%s"'
++ % (util.normpath('/temp/bingo.txt').replace('\\', '\\\\'),
++ 'bingo2.txt'))
++ # hackety hack to work on win32&lin
++ output = re.sub(r'"[c-zC-Z]:', '"', output)
++ self.assertEqual(expected, output)
++
++ def testRcIncludeFlattenedHtmlFile(self):
++ input_file = util.PathFromRoot('grit/testdata/include_test.html')
++ output_file = '%s/HTML_FILE1_include_test.html' % tempfile.gettempdir()
++ root = util.ParseGrdForUnittest('''
++ <includes>
++ <include name="HTML_FILE1" flattenhtml="true" file="%s" type="BINDATA" />
++ </includes>''' % input_file)
++
++ buf = StringIO()
++ build.RcBuilder.ProcessNode(root, DummyOutput('rc_all', 'en', output_file),
++ buf)
++ output = util.StripBlankLinesAndComments(buf.getvalue())
++
++ expected = (_PREAMBLE +
++ u'HTML_FILE1 BINDATA "HTML_FILE1_include_test.html"')
++ # hackety hack to work on win32&lin
++ output = re.sub(r'"[c-zC-Z]:', '"', output)
++ self.assertEqual(expected, output)
++
++ file_contents = util.ReadFile(output_file, 'utf-8')
++
++ # Check for the content added by the <include> tag.
++ self.failUnless(file_contents.find('Hello Include!') != -1)
++ # Check for the content that was removed by if tag.
++ self.failUnless(file_contents.find('should be removed') == -1)
++ # Check for the content that was kept in place by if.
++ self.failUnless(file_contents.find('should be kept') != -1)
++ self.failUnless(file_contents.find('in the middle...') != -1)
++ self.failUnless(file_contents.find('at the end...') != -1)
++ # Check for nested content that was kept
++ self.failUnless(file_contents.find('nested true should be kept') != -1)
++ self.failUnless(file_contents.find('silbing true should be kept') != -1)
++ # Check for removed "<if>" and "</if>" tags.
++ self.failUnless(file_contents.find('<if expr=') == -1)
++ self.failUnless(file_contents.find('</if>') == -1)
++ os.remove(output_file)
++
++ def testStructureNodeOutputfile(self):
++ input_file = util.PathFromRoot('grit/testdata/simple.html')
++ root = util.ParseGrdForUnittest('''
++ <structures>
++ <structure type="tr_html" name="IDR_HTML" file="%s" />
++ </structures>''' % input_file)
++ struct, = root.GetChildrenOfType(structure.StructureNode)
++ # We must run the gatherer since we'll be wanting the translation of the
++ # file. The file exists in the location pointed to.
++ root.SetOutputLanguage('en')
++ root.RunGatherers()
++
++ output_dir = tempfile.gettempdir()
++ en_file = struct.FileForLanguage('en', output_dir)
++ self.failUnless(en_file == input_file)
++ fr_file = struct.FileForLanguage('fr', output_dir)
++ self.failUnless(fr_file == os.path.join(output_dir, 'fr_simple.html'))
++
++ contents = util.ReadFile(fr_file, 'utf-8')
++
++ self.failUnless(contents.find('<p>') != -1) # should contain the markup
++ self.failUnless(contents.find('Hello!') == -1) # should be translated
++ os.remove(fr_file)
++
++ def testChromeHtmlNodeOutputfile(self):
++ input_file = util.PathFromRoot('grit/testdata/chrome_html.html')
++ output_file = '%s/HTML_FILE1_chrome_html.html' % tempfile.gettempdir()
++ root = util.ParseGrdForUnittest('''
++ <structures>
++ <structure type="chrome_html" name="HTML_FILE1" file="%s" flattenhtml="true" />
++ </structures>''' % input_file)
++ struct, = root.GetChildrenOfType(structure.StructureNode)
++ struct.gatherer.SetDefines({'scale_factors': '2x'})
++ # We must run the gatherers since we'll be wanting the chrome_html output.
++ # The file exists in the location pointed to.
++ root.SetOutputLanguage('en')
++ root.RunGatherers()
++
++ buf = StringIO()
++ build.RcBuilder.ProcessNode(root, DummyOutput('rc_all', 'en', output_file),
++ buf)
++ output = util.StripBlankLinesAndComments(buf.getvalue())
++ expected = (_PREAMBLE +
++ u'HTML_FILE1 BINDATA "HTML_FILE1_chrome_html.html"')
++ # hackety hack to work on win32&lin
++ output = re.sub(r'"[c-zC-Z]:', '"', output)
++ self.assertEqual(expected, output)
++
++ file_contents = util.ReadFile(output_file, 'utf-8')
++
++ # Check for the content added by the <include> tag.
++ self.failUnless(file_contents.find('Hello Include!') != -1)
++ # Check for inserted -webkit-image-set.
++ self.failUnless(file_contents.find('content: -webkit-image-set') != -1)
++ os.remove(output_file)
++
++ def testSubstitutionHtml(self):
++ input_file = util.PathFromRoot('grit/testdata/toolbar_about.html')
++ root = grd_reader.Parse(StringIO('''<?xml version="1.0" encoding="UTF-8"?>
++ <grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
++ <release seq="1" allow_pseudo="False">
++ <structures fallback_to_english="True">
++ <structure type="tr_html" name="IDR_HTML" file="%s" expand_variables="true"/>
++ </structures>
++ </release>
++ </grit>
++ ''' % input_file), util.PathFromRoot('.'))
++ root.SetOutputLanguage('ar')
++ # We must run the gatherers since we'll be wanting the translation of the
++ # file. The file exists in the location pointed to.
++ root.RunGatherers()
++
++ output_dir = tempfile.gettempdir()
++ struct, = root.GetChildrenOfType(structure.StructureNode)
++ ar_file = struct.FileForLanguage('ar', output_dir)
++ self.failUnless(ar_file == os.path.join(output_dir,
++ 'ar_toolbar_about.html'))
++
++ contents = util.ReadFile(ar_file, 'utf-8')
++
++ self.failUnless(contents.find('dir="RTL"') != -1)
++ os.remove(ar_file)
++
++ def testFallbackToEnglish(self):
++ root = util.ParseGrdForUnittest(r'''
++ <structures fallback_to_english="True">
++ <structure type="dialog" name="IDD_ABOUTBOX" file="grit\testdata\klonk.rc" encoding="utf-16" />
++ </structures>''', base_dir=util.PathFromRoot('.'))
++ root.SetOutputLanguage('en')
++ root.RunGatherers()
++
++ buf = StringIO()
++ formatter = build.RcBuilder.ProcessNode(
++ root, DummyOutput('rc_all', 'bingobongo'), buf)
++ output = util.StripBlankLinesAndComments(buf.getvalue())
++ self.assertEqual(_PREAMBLE + '''\
++IDD_ABOUTBOX DIALOGEX 22, 17, 230, 75
++STYLE DS_SETFONT | DS_MODALFRAME | WS_CAPTION | WS_SYSMENU
++CAPTION "About"
++FONT 8, "System", 0, 0, 0x0
++BEGIN
++ ICON IDI_KLONK,IDC_MYICON,14,9,20,20
++ LTEXT "klonk Version ""yibbee"" 1.0",IDC_STATIC,49,10,119,8,
++ SS_NOPREFIX
++ LTEXT "Copyright (C) 2005",IDC_STATIC,49,20,119,8
++ DEFPUSHBUTTON "OK",IDOK,195,6,30,11,WS_GROUP
++ CONTROL "Jack ""Black"" Daniels",IDC_RADIO1,"Button",
++ BS_AUTORADIOBUTTON,46,51,84,10
++END''', output)
++
++
++ def testSubstitutionRc(self):
++ root = grd_reader.Parse(StringIO(r'''<?xml version="1.0" encoding="UTF-8"?>
++ <grit latest_public_release="2" source_lang_id="en-US" current_release="3"
++ base_dir=".">
++ <outputs>
++ <output lang="en" type="rc_all" filename="grit\testdata\klonk_resources.rc"/>
++ </outputs>
++ <release seq="1" allow_pseudo="False">
++ <structures>
++ <structure type="menu" name="IDC_KLONKMENU"
++ file="grit\testdata\klonk.rc" encoding="utf-16"
++ expand_variables="true" />
++ </structures>
++ <messages>
++ <message name="good" sub_variable="true">
++ excellent
++ </message>
++ </messages>
++ </release>
++ </grit>
++ '''), util.PathFromRoot('.'))
++ root.SetOutputLanguage('en')
++ root.RunGatherers()
++
++ buf = StringIO()
++ build.RcBuilder.ProcessNode(root, DummyOutput('rc_all', 'en'), buf)
++ output = buf.getvalue()
++ self.assertEqual('''
++// This file is automatically generated by GRIT. Do not edit.
++
++#include "resource.h"
++#include <winresrc.h>
++#ifdef IDC_STATIC
++#undef IDC_STATIC
++#endif
++#define IDC_STATIC (-1)
++
++LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
++
++
++IDC_KLONKMENU MENU
++BEGIN
++ POPUP "&File"
++ BEGIN
++ MENUITEM "E&xit", IDM_EXIT
++ MENUITEM "This be ""Klonk"" me like", ID_FILE_THISBE
++ POPUP "gonk"
++ BEGIN
++ MENUITEM "Klonk && is excellent", ID_GONK_KLONKIS
++ END
++ END
++ POPUP "&Help"
++ BEGIN
++ MENUITEM "&About ...", IDM_ABOUT
++ END
++END
++
++STRINGTABLE
++BEGIN
++ good "excellent"
++END
++'''.strip(), output.strip())
++
++
++if __name__ == '__main__':
++ unittest.main()
+diff --git a/tools/grit/grit/format/resource_map.py b/tools/grit/grit/format/resource_map.py
+new file mode 100644
+index 0000000000..95a8b83160
+--- /dev/null
++++ b/tools/grit/grit/format/resource_map.py
+@@ -0,0 +1,159 @@
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++'''This file contains item formatters for resource_map_header and
++resource_map_source files. A resource map is a mapping between resource names
++(string) and the internal resource ID.'''
++
++from __future__ import print_function
++
++import os
++from functools import partial
++
++from grit import util
++
++
++def GetFormatter(type):
++ if type == 'resource_map_header':
++ return _FormatHeader
++ if type == 'resource_file_map_source':
++ return partial(_FormatSource, _GetItemPath)
++ if type == 'resource_map_source':
++ return partial(_FormatSource, _GetItemName)
++
++
++def GetMapName(root):
++ '''Get the name of the resource map based on the header file name. E.g.,
++ if our header filename is theme_resources.h, we name our resource map
++ kThemeResourcesMap.
++
++ |root| is the grd file root.'''
++ outputs = root.GetOutputFiles()
++ rc_header_file = None
++ for output in outputs:
++ if 'rc_header' == output.GetType():
++ rc_header_file = output.GetFilename()
++ if not rc_header_file:
++ raise Exception('unable to find resource header filename')
++ filename = os.path.splitext(os.path.split(rc_header_file)[1])[0]
++ filename = filename[0].upper() + filename[1:]
++ while True:
++ pos = filename.find('_')
++ if pos == -1 or pos >= len(filename):
++ break
++ filename = filename[:pos] + filename[pos + 1].upper() + filename[pos + 2:]
++ return 'k' + filename
++
++
++def _FormatHeader(root, lang='en', output_dir='.'):
++ '''Create the header file for the resource mapping. This file just declares
++ an array of name/value pairs.'''
++ return '''\
++// This file is automatically generated by GRIT. Do not edit.
++
++#include <stddef.h>
++
++#ifndef GRIT_RESOURCE_MAP_STRUCT_
++#define GRIT_RESOURCE_MAP_STRUCT_
++struct GritResourceMap {
++ const char* const name;
++ int value;
++};
++#endif // GRIT_RESOURCE_MAP_STRUCT_
++
++extern const GritResourceMap %(map_name)s[];
++extern const size_t %(map_name)sSize;
++''' % { 'map_name': GetMapName(root) }
++
++
++def _FormatSourceHeader(root, output_dir):
++ '''Create the header of the C++ source file for the resource mapping.'''
++ rc_header_file = None
++ map_header_file = None
++ for output in root.GetOutputFiles():
++ type = output.GetType()
++ if 'rc_header' == type:
++ rc_header_file = util.MakeRelativePath(output_dir,
++ output.GetOutputFilename())
++ elif 'resource_map_header' == type:
++ map_header_file = util.MakeRelativePath(output_dir,
++ output.GetOutputFilename())
++ if not rc_header_file or not map_header_file:
++ raise Exception('resource_map_source output type requires '
++ 'a resource_map_header and rc_header outputs')
++ return '''\
++// This file is automatically generated by GRIT. Do not edit.
++
++#include "%(map_header_file)s"
++
++#include <stddef.h>
++
++#include "base/stl_util.h"
++
++#include "%(rc_header_file)s"
++
++const GritResourceMap %(map_name)s[] = {
++''' % { 'map_header_file': map_header_file,
++ 'rc_header_file': rc_header_file,
++ 'map_name': GetMapName(root),
+ }
-+ }
+
-+ for (CXXRecordDecl::method_iterator it = record->method_begin();
-+ it != record->method_end(); ++it) {
-+ if (it->isCopyAssignmentOperator() || isa<CXXConstructorDecl>(*it)) {
-+ // Ignore constructors and assignment operators.
-+ } else if (isa<CXXDestructorDecl>(*it) &&
-+ !record->hasUserDeclaredDestructor()) {
-+ // Ignore non-user-declared destructors.
-+ } else {
-+ CheckVirtualMethod(*it, warn_on_inline_bodies);
-+ CheckOverriddenMethod(*it);
++
++def _FormatSourceFooter(root):
++ # Return the footer text.
++ return '''\
++};
++
++const size_t %(map_name)sSize = base::size(%(map_name)s);
++''' % { 'map_name': GetMapName(root) }
++
++
++def _FormatSource(get_key, root, lang, output_dir):
++ from grit.node import include, structure, message
++ id_map = root.GetIdMap()
++ yield _FormatSourceHeader(root, output_dir)
++ seen = set()
++ for item in root.ActiveDescendants():
++ if not item.IsResourceMapSource():
++ continue
++ key = get_key(item)
++ tid = item.attrs['name']
++ if tid not in id_map or key in seen:
++ continue
++ seen.add(key)
++ yield ' {"%s", %s},\n' % (key, tid)
++ yield _FormatSourceFooter(root)
++
++
++def _GetItemName(item):
++ return item.attrs['name']
++
++# Check if |path2| is a subpath of |path1|.
++def _IsSubpath(path1, path2):
++ path1_abs = os.path.abspath(path1)
++ path2_abs = os.path.abspath(path2)
++ common = os.path.commonprefix([path1_abs, path2_abs])
++ return path1_abs == common
++
++def _GetItemPath(item):
++ path = item.GetInputPath().replace("\\", "/")
++
++ # Handle the case where the file resides within the output folder,
++ # by expanding any variables as well as replacing the output folder name with
++ # a fixed string such that the key added to the map does not depend on a given
++ # developer's setup.
++ #
++ # For example this will convert the following path:
++ # ../../out/gchrome/${root_gen_dir}/ui/webui/resources/js/foo.js
++ # to:
++ # @out_folder@/gen/ui/webui/resources/js/foo.js
++
++ real_path = item.ToRealPath(item.GetInputPath())
++ if (item.attrs.get('use_base_dir', 'true') != 'true' and
++ _IsSubpath(os.path.curdir, real_path)):
++ path = os.path.join(
++ '@out_folder@', os.path.relpath(real_path)).replace("\\", "/")
++
++ assert '$' not in path, 'all variables should have been expanded'
++ return path
+diff --git a/tools/grit/grit/format/resource_map_unittest.py b/tools/grit/grit/format/resource_map_unittest.py
+new file mode 100644
+index 0000000000..3499b321ef
+--- /dev/null
++++ b/tools/grit/grit/format/resource_map_unittest.py
+@@ -0,0 +1,345 @@
++#!/usr/bin/env python
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++'''Unit tests for grit.format.resource_map'''
++
++from __future__ import print_function
++
++import os
++import sys
++if __name__ == '__main__':
++ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
++
++import unittest
++
++from grit import util
++from grit.format import resource_map
++
++
++class FormatResourceMapUnittest(unittest.TestCase):
++ def testFormatResourceMap(self):
++ grd = util.ParseGrdForUnittest('''
++ <outputs>
++ <output type="rc_header" filename="the_rc_header.h" />
++ <output type="resource_map_header"
++ filename="the_resource_map_header.h" />
++ </outputs>
++ <release seq="3">
++ <structures first_id="300">
++ <structure type="menu" name="IDC_KLONKMENU"
++ file="grit\\testdata\\klonk.rc" encoding="utf-16" />
++ </structures>
++ <includes first_id="10000">
++ <include type="foo" file="abc" name="IDS_FIRSTPRESENT" />
++ <if expr="False">
++ <include type="foo" file="def" name="IDS_MISSING" />
++ </if>
++ <if expr="lang != 'es'">
++ <include type="foo" file="ghi" name="IDS_LANGUAGESPECIFIC" />
++ </if>
++ <if expr="lang == 'es'">
++ <include type="foo" file="jkl" name="IDS_LANGUAGESPECIFIC" />
++ </if>
++ <include type="foo" file="mno" name="IDS_THIRDPRESENT" />
++ <include type="foo" file="opq" name="IDS_FOURTHPRESENT"
++ skip_in_resource_map="true" />
++ </includes>
++ </release>''', run_gatherers=True)
++ output = util.StripBlankLinesAndComments(''.join(
++ resource_map.GetFormatter('resource_map_header')(grd, 'en', '.')))
++ self.assertEqual('''\
++#include <stddef.h>
++#ifndef GRIT_RESOURCE_MAP_STRUCT_
++#define GRIT_RESOURCE_MAP_STRUCT_
++struct GritResourceMap {
++ const char* const name;
++ int value;
++};
++#endif // GRIT_RESOURCE_MAP_STRUCT_
++extern const GritResourceMap kTheRcHeader[];
++extern const size_t kTheRcHeaderSize;''', output)
++ output = util.StripBlankLinesAndComments(''.join(
++ resource_map.GetFormatter('resource_map_source')(grd, 'en', '.')))
++ self.assertEqual('''\
++#include "the_resource_map_header.h"
++#include <stddef.h>
++#include "base/stl_util.h"
++#include "the_rc_header.h"
++const GritResourceMap kTheRcHeader[] = {
++ {"IDC_KLONKMENU", IDC_KLONKMENU},
++ {"IDS_FIRSTPRESENT", IDS_FIRSTPRESENT},
++ {"IDS_LANGUAGESPECIFIC", IDS_LANGUAGESPECIFIC},
++ {"IDS_THIRDPRESENT", IDS_THIRDPRESENT},
++};
++const size_t kTheRcHeaderSize = base::size(kTheRcHeader);''', output)
++ output = util.StripBlankLinesAndComments(''.join(
++ resource_map.GetFormatter('resource_file_map_source')(grd, 'en', '.')))
++ self.assertEqual('''\
++#include "the_resource_map_header.h"
++#include <stddef.h>
++#include "base/stl_util.h"
++#include "the_rc_header.h"
++const GritResourceMap kTheRcHeader[] = {
++ {"grit/testdata/klonk.rc", IDC_KLONKMENU},
++ {"abc", IDS_FIRSTPRESENT},
++ {"ghi", IDS_LANGUAGESPECIFIC},
++ {"mno", IDS_THIRDPRESENT},
++};
++const size_t kTheRcHeaderSize = base::size(kTheRcHeader);''', output)
++
++ def testFormatResourceMapWithGeneratedFile(self):
++ os.environ["root_gen_dir"] = "gen"
++
++ grd = util.ParseGrdForUnittest('''\
++ <outputs>
++ <output type="rc_header" filename="the_rc_header.h" />
++ <output type="resource_map_header"
++ filename="resource_map_header.h" />
++ </outputs>
++ <release seq="3">
++ <includes first_id="10000">
++ <include type="BINDATA"
++ file="${root_gen_dir}/foo/bar/baz.js"
++ name="IDR_FOO_BAR_BAZ_JS"
++ use_base_dir="false"
++ compress="gzip" />
++ </includes>
++ </release>''', run_gatherers=True)
++
++ formatter = resource_map.GetFormatter('resource_file_map_source')
++ output = util.StripBlankLinesAndComments(''.join(formatter(grd, 'en', '.')))
++ expected = '''\
++#include "resource_map_header.h"
++#include <stddef.h>
++#include "base/stl_util.h"
++#include "the_rc_header.h"
++const GritResourceMap kTheRcHeader[] = {
++ {"@out_folder@/gen/foo/bar/baz.js", IDR_FOO_BAR_BAZ_JS},
++};
++const size_t kTheRcHeaderSize = base::size(kTheRcHeader);'''
++ self.assertEqual(expected, output)
++
++ def testFormatResourceMapWithOutputAllEqualsFalseForStructures(self):
++ grd = util.ParseGrdForUnittest('''
++ <outputs>
++ <output type="rc_header" filename="the_rc_header.h" />
++ <output type="resource_map_header"
++ filename="the_resource_map_header.h" />
++ <output type="resource_map_source"
++ filename="the_resource_map_header.cc" />
++ </outputs>
++ <release seq="3">
++ <structures first_id="300">
++ <structure type="chrome_scaled_image" name="IDR_KLONKMENU"
++ file="foo.png" />
++ <if expr="False">
++ <structure type="chrome_scaled_image" name="IDR_MISSING"
++ file="bar.png" />
++ </if>
++ <if expr="True">
++ <structure type="chrome_scaled_image" name="IDR_BLOB"
++ file="blob.png" />
++ </if>
++ <if expr="True">
++ <then>
++ <structure type="chrome_scaled_image" name="IDR_METEOR"
++ file="meteor.png" />
++ </then>
++ <else>
++ <structure type="chrome_scaled_image" name="IDR_METEOR"
++ file="roetem.png" />
++ </else>
++ </if>
++ <if expr="False">
++ <structure type="chrome_scaled_image" name="IDR_LAST"
++ file="zyx.png" />
++ </if>
++ <if expr="True">
++ <structure type="chrome_scaled_image" name="IDR_LAST"
++ file="xyz.png" />
++ </if>
++ </structures>
++ </release>''', run_gatherers=True)
++ output = util.StripBlankLinesAndComments(''.join(
++ resource_map.GetFormatter('resource_map_header')(grd, 'en', '.')))
++ self.assertEqual('''\
++#include <stddef.h>
++#ifndef GRIT_RESOURCE_MAP_STRUCT_
++#define GRIT_RESOURCE_MAP_STRUCT_
++struct GritResourceMap {
++ const char* const name;
++ int value;
++};
++#endif // GRIT_RESOURCE_MAP_STRUCT_
++extern const GritResourceMap kTheRcHeader[];
++extern const size_t kTheRcHeaderSize;''', output)
++ output = util.StripBlankLinesAndComments(''.join(
++ resource_map.GetFormatter('resource_map_source')(grd, 'en', '.')))
++ self.assertEqual('''\
++#include "the_resource_map_header.h"
++#include <stddef.h>
++#include "base/stl_util.h"
++#include "the_rc_header.h"
++const GritResourceMap kTheRcHeader[] = {
++ {"IDR_KLONKMENU", IDR_KLONKMENU},
++ {"IDR_BLOB", IDR_BLOB},
++ {"IDR_METEOR", IDR_METEOR},
++ {"IDR_LAST", IDR_LAST},
++};
++const size_t kTheRcHeaderSize = base::size(kTheRcHeader);''', output)
++ output = util.StripBlankLinesAndComments(''.join(
++ resource_map.GetFormatter('resource_map_source')(grd, 'en', '.')))
++ self.assertEqual('''\
++#include "the_resource_map_header.h"
++#include <stddef.h>
++#include "base/stl_util.h"
++#include "the_rc_header.h"
++const GritResourceMap kTheRcHeader[] = {
++ {"IDR_KLONKMENU", IDR_KLONKMENU},
++ {"IDR_BLOB", IDR_BLOB},
++ {"IDR_METEOR", IDR_METEOR},
++ {"IDR_LAST", IDR_LAST},
++};
++const size_t kTheRcHeaderSize = base::size(kTheRcHeader);''', output)
++
++ def testFormatResourceMapWithOutputAllEqualsFalseForIncludes(self):
++ grd = util.ParseGrdForUnittest('''
++ <outputs>
++ <output type="rc_header" filename="the_rc_header.h" />
++ <output type="resource_map_header"
++ filename="the_resource_map_header.h" />
++ </outputs>
++ <release seq="3">
++ <structures first_id="300">
++ <structure type="menu" name="IDC_KLONKMENU"
++ file="grit\\testdata\\klonk.rc" encoding="utf-16" />
++ </structures>
++ <includes first_id="10000">
++ <include type="foo" file="abc" name="IDS_FIRSTPRESENT" />
++ <if expr="False">
++ <include type="foo" file="def" name="IDS_MISSING" />
++ </if>
++ <include type="foo" file="mno" name="IDS_THIRDPRESENT" />
++ <if expr="True">
++ <include type="foo" file="blob" name="IDS_BLOB" />
++ </if>
++ <if expr="True">
++ <then>
++ <include type="foo" file="meteor" name="IDS_METEOR" />
++ </then>
++ <else>
++ <include type="foo" file="roetem" name="IDS_METEOR" />
++ </else>
++ </if>
++ <if expr="False">
++ <include type="foo" file="zyx" name="IDS_LAST" />
++ </if>
++ <if expr="True">
++ <include type="foo" file="xyz" name="IDS_LAST" />
++ </if>
++ </includes>
++ </release>''', run_gatherers=True)
++ output = util.StripBlankLinesAndComments(''.join(
++ resource_map.GetFormatter('resource_map_header')(grd, 'en', '.')))
++ self.assertEqual('''\
++#include <stddef.h>
++#ifndef GRIT_RESOURCE_MAP_STRUCT_
++#define GRIT_RESOURCE_MAP_STRUCT_
++struct GritResourceMap {
++ const char* const name;
++ int value;
++};
++#endif // GRIT_RESOURCE_MAP_STRUCT_
++extern const GritResourceMap kTheRcHeader[];
++extern const size_t kTheRcHeaderSize;''', output)
++ output = util.StripBlankLinesAndComments(''.join(
++ resource_map.GetFormatter('resource_map_source')(grd, 'en', '.')))
++ self.assertEqual('''\
++#include "the_resource_map_header.h"
++#include <stddef.h>
++#include "base/stl_util.h"
++#include "the_rc_header.h"
++const GritResourceMap kTheRcHeader[] = {
++ {"IDC_KLONKMENU", IDC_KLONKMENU},
++ {"IDS_FIRSTPRESENT", IDS_FIRSTPRESENT},
++ {"IDS_THIRDPRESENT", IDS_THIRDPRESENT},
++ {"IDS_BLOB", IDS_BLOB},
++ {"IDS_METEOR", IDS_METEOR},
++ {"IDS_LAST", IDS_LAST},
++};
++const size_t kTheRcHeaderSize = base::size(kTheRcHeader);''', output)
++ output = util.StripBlankLinesAndComments(''.join(
++ resource_map.GetFormatter('resource_file_map_source')(grd, 'en', '.')))
++ self.assertEqual('''\
++#include "the_resource_map_header.h"
++#include <stddef.h>
++#include "base/stl_util.h"
++#include "the_rc_header.h"
++const GritResourceMap kTheRcHeader[] = {
++ {"grit/testdata/klonk.rc", IDC_KLONKMENU},
++ {"abc", IDS_FIRSTPRESENT},
++ {"mno", IDS_THIRDPRESENT},
++ {"blob", IDS_BLOB},
++ {"meteor", IDS_METEOR},
++ {"xyz", IDS_LAST},
++};
++const size_t kTheRcHeaderSize = base::size(kTheRcHeader);''', output)
++
++ def testFormatStringResourceMap(self):
++ grd = util.ParseGrdForUnittest('''
++ <outputs>
++ <output type="rc_header" filename="the_rc_header.h" />
++ <output type="resource_map_header" filename="the_rc_map_header.h" />
++ <output type="resource_map_source" filename="the_rc_map_source.cc" />
++ </outputs>
++ <release seq="1" allow_pseudo="false">
++ <messages fallback_to_english="true">
++ <message name="IDS_PRODUCT_NAME" desc="The application name">
++ Application
++ </message>
++ <if expr="True">
++ <message name="IDS_DEFAULT_TAB_TITLE_TITLE_CASE"
++ desc="In Title Case: The default title in a tab.">
++ New Tab
++ </message>
++ </if>
++ <if expr="False">
++ <message name="IDS_DEFAULT_TAB_TITLE"
++ desc="The default title in a tab.">
++ New tab
++ </message>
++ </if>
++ </messages>
++ </release>''', run_gatherers=True)
++ grd.InitializeIds()
++ output = util.StripBlankLinesAndComments(''.join(
++ resource_map.GetFormatter('resource_map_header')(grd, 'en', '.')))
++ self.assertEqual('''\
++#include <stddef.h>
++#ifndef GRIT_RESOURCE_MAP_STRUCT_
++#define GRIT_RESOURCE_MAP_STRUCT_
++struct GritResourceMap {
++ const char* const name;
++ int value;
++};
++#endif // GRIT_RESOURCE_MAP_STRUCT_
++extern const GritResourceMap kTheRcHeader[];
++extern const size_t kTheRcHeaderSize;''', output)
++ output = util.StripBlankLinesAndComments(''.join(
++ resource_map.GetFormatter('resource_map_source')(grd, 'en', '.')))
++ self.assertEqual('''\
++#include "the_rc_map_header.h"
++#include <stddef.h>
++#include "base/stl_util.h"
++#include "the_rc_header.h"
++const GritResourceMap kTheRcHeader[] = {
++ {"IDS_PRODUCT_NAME", IDS_PRODUCT_NAME},
++ {"IDS_DEFAULT_TAB_TITLE_TITLE_CASE", IDS_DEFAULT_TAB_TITLE_TITLE_CASE},
++};
++const size_t kTheRcHeaderSize = base::size(kTheRcHeader);''', output)
++
++
++if __name__ == '__main__':
++ unittest.main()
+diff --git a/tools/grit/grit/gather/__init__.py b/tools/grit/grit/gather/__init__.py
+new file mode 100644
+index 0000000000..2d578f5643
+--- /dev/null
++++ b/tools/grit/grit/gather/__init__.py
+@@ -0,0 +1,8 @@
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++'''Module grit.gather
++'''
++
++pass
+diff --git a/tools/grit/grit/gather/admin_template.py b/tools/grit/grit/gather/admin_template.py
+new file mode 100644
+index 0000000000..c26b6a88d7
+--- /dev/null
++++ b/tools/grit/grit/gather/admin_template.py
+@@ -0,0 +1,62 @@
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++'''Gatherer for administrative template files.
++'''
++
++from __future__ import print_function
++
++import re
++
++from grit.gather import regexp
++from grit import exception
++from grit import lazy_re
++
++
++class MalformedAdminTemplateException(exception.Base):
++ '''This file doesn't look like a .adm file to me.'''
++ pass
++
++
++class AdmGatherer(regexp.RegexpGatherer):
++ '''Gatherer for the translateable portions of an admin template.
++
++ This gatherer currently makes the following assumptions:
++ - there is only one [strings] section and it is always the last section
++ of the file
++ - translateable strings do not need to be escaped.
++ '''
++
++ # Finds the strings section as the group named 'strings'
++ _STRINGS_SECTION = lazy_re.compile(
++ r'(?P<first_part>.+^\[strings\])(?P<strings>.+)\Z',
++ re.MULTILINE | re.DOTALL)
++
++ # Finds the translateable sections from within the [strings] section.
++ _TRANSLATEABLES = lazy_re.compile(
++ r'^\s*[A-Za-z0-9_]+\s*=\s*"(?P<text>.+)"\s*$',
++ re.MULTILINE)
++
++ def Escape(self, text):
++ return text.replace('\n', '\\n')
++
++ def UnEscape(self, text):
++ return text.replace('\\n', '\n')
++
++ def Parse(self):
++ if self.have_parsed_:
++ return
++ self.have_parsed_ = True
++
++ self.text_ = self._LoadInputFile().strip()
++ m = self._STRINGS_SECTION.match(self.text_)
++ if not m:
++ raise MalformedAdminTemplateException()
++ # Add the first part, which is all nontranslateable, to the skeleton
++ self._AddNontranslateableChunk(m.group('first_part'))
++ # Then parse the rest using the _TRANSLATEABLES regexp.
++ self._RegExpParse(self._TRANSLATEABLES, m.group('strings'))
++
++ def GetTextualIds(self):
++ return [self.extkey]
+diff --git a/tools/grit/grit/gather/admin_template_unittest.py b/tools/grit/grit/gather/admin_template_unittest.py
+new file mode 100644
+index 0000000000..c637af3a75
+--- /dev/null
++++ b/tools/grit/grit/gather/admin_template_unittest.py
+@@ -0,0 +1,115 @@
++#!/usr/bin/env python
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++'''Unit tests for the admin template gatherer.'''
++
++from __future__ import print_function
++
++import os
++import sys
++if __name__ == '__main__':
++ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
++
++import unittest
++
++from six import StringIO
++
++from grit.gather import admin_template
++from grit import util
++from grit import grd_reader
++from grit import grit_runner
++from grit.tool import build
++
++
++class AdmGathererUnittest(unittest.TestCase):
++ def testParsingAndTranslating(self):
++ pseudofile = StringIO(
++ 'bingo bongo\n'
++ 'ding dong\n'
++ '[strings] \n'
++ 'whatcha="bingo bongo"\n'
++ 'gotcha = "bingolabongola "the wise" fingulafongula" \n')
++ gatherer = admin_template.AdmGatherer(pseudofile)
++ gatherer.Parse()
++ self.failUnless(len(gatherer.GetCliques()) == 2)
++ self.failUnless(gatherer.GetCliques()[1].GetMessage().GetRealContent() ==
++ 'bingolabongola "the wise" fingulafongula')
++
++ translation = gatherer.Translate('en')
++ self.failUnless(translation == gatherer.GetText().strip())
++
++ def testErrorHandling(self):
++ pseudofile = StringIO(
++ 'bingo bongo\n'
++ 'ding dong\n'
++ 'whatcha="bingo bongo"\n'
++ 'gotcha = "bingolabongola "the wise" fingulafongula" \n')
++ gatherer = admin_template.AdmGatherer(pseudofile)
++ self.assertRaises(admin_template.MalformedAdminTemplateException,
++ gatherer.Parse)
++
++ _TRANSLATABLES_FROM_FILE = (
++ 'Google', 'Google Desktop', 'Preferences',
++ 'Controls Google Desktop preferences',
++ 'Indexing and Capture Control',
++ 'Controls what files, web pages, and other content will be indexed by Google Desktop.',
++ 'Prevent indexing of email',
++ # there are lots more but we don't check any further
++ )
++
++ def VerifyCliquesFromAdmFile(self, cliques):
++ self.failUnless(len(cliques) > 20)
++ for clique, expected in zip(cliques, self._TRANSLATABLES_FROM_FILE):
++ text = clique.GetMessage().GetRealContent()
++ self.failUnless(text == expected)
++
++ def testFromFile(self):
++ fname = util.PathFromRoot('grit/testdata/GoogleDesktop.adm')
++ gatherer = admin_template.AdmGatherer(fname)
++ gatherer.Parse()
++ cliques = gatherer.GetCliques()
++ self.VerifyCliquesFromAdmFile(cliques)
++
++ def MakeGrd(self):
++ grd = grd_reader.Parse(StringIO('''<?xml version="1.0" encoding="UTF-8"?>
++ <grit latest_public_release="2" source_lang_id="en-US" current_release="3">
++ <release seq="3">
++ <structures>
++ <structure type="admin_template" name="IDAT_GOOGLE_DESKTOP_SEARCH"
++ file="GoogleDesktop.adm" exclude_from_rc="true" />
++ <structure type="txt" name="BINGOBONGO"
++ file="README.txt" exclude_from_rc="true" />
++ </structures>
++ </release>
++ <outputs>
++ <output filename="de_res.rc" type="rc_all" lang="de" />
++ </outputs>
++ </grit>'''), util.PathFromRoot('grit/testdata'))
++ grd.SetOutputLanguage('en')
++ grd.RunGatherers()
++ return grd
++
++ def testInGrd(self):
++ grd = self.MakeGrd()
++ cliques = grd.children[0].children[0].children[0].GetCliques()
++ self.VerifyCliquesFromAdmFile(cliques)
++
++ def testFileIsOutput(self):
++ grd = self.MakeGrd()
++ dirname = util.TempDir({})
++ try:
++ tool = build.RcBuilder()
++ tool.o = grit_runner.Options()
++ tool.output_directory = dirname.GetPath()
++ tool.res = grd
++ tool.Process()
++
++ self.failUnless(os.path.isfile(dirname.GetPath('de_GoogleDesktop.adm')))
++ self.failUnless(os.path.isfile(dirname.GetPath('de_README.txt')))
++ finally:
++ dirname.CleanUp()
++
++if __name__ == '__main__':
++ unittest.main()
+diff --git a/tools/grit/grit/gather/chrome_html.py b/tools/grit/grit/gather/chrome_html.py
+new file mode 100644
+index 0000000000..71c1332d66
+--- /dev/null
++++ b/tools/grit/grit/gather/chrome_html.py
+@@ -0,0 +1,377 @@
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++"""Prepares a Chrome HTML file by inlining resources and adding references to
++high DPI resources and removing references to unsupported scale factors.
++
++This is a small gatherer that takes a HTML file, looks for src attributes
++and inlines the specified file, producing one HTML file with no external
++dependencies. It recursively inlines the included files. When inlining CSS
++image files this script also checks for the existence of high DPI versions
++of the inlined file including those on relevant platforms. Unsupported scale
++factors are also removed from existing image sets to support explicitly
++referencing all available images.
++"""
++
++from __future__ import print_function
++
++import os
++import re
++
++from grit import lazy_re
++from grit import util
++from grit.format import html_inline
++from grit.gather import interface
++
++
++# Distribution string to replace with distribution.
++DIST_SUBSTR = '%DISTRIBUTION%'
++
++
++# Matches a chrome theme source URL.
++_THEME_SOURCE = lazy_re.compile(
++ r'(?P<baseurl>chrome://theme/IDR_[A-Z0-9_]*)(?P<query>\?.*)?')
++# Pattern for matching CSS url() function.
++_CSS_URL_PATTERN = r'url\((?P<quote>"|\'|)(?P<filename>[^"\'()]*)(?P=quote)\)'
++# Matches CSS url() functions with the capture group 'filename'.
++_CSS_URL = lazy_re.compile(_CSS_URL_PATTERN)
++# Matches one or more CSS image urls used in given properties.
++_CSS_IMAGE_URLS = lazy_re.compile(
++ r'(?P<attribute>content|background|[\w-]*-image):\s*'
++ r'(?P<urls>(' + _CSS_URL_PATTERN + r'\s*,?\s*)+)')
++# Matches CSS image sets.
++_CSS_IMAGE_SETS = lazy_re.compile(
++ r'(?P<attribute>content|background|[\w-]*-image):[ ]*'
++ r'-webkit-image-set\((?P<images>'
++ r'(\s*,?\s*url\((?P<quote>"|\'|)[^"\'()]*(?P=quote)\)[ ]*[0-9.]*x)*)\)',
++ re.MULTILINE)
++# Matches a single image in a CSS image set with the capture group scale.
++_CSS_IMAGE_SET_IMAGE = lazy_re.compile(r'\s*,?\s*'
++ r'url\((?P<quote>"|\'|)[^"\'()]*(?P=quote)\)[ ]*(?P<scale>[0-9.]*x)',
++ re.MULTILINE)
++_HTML_IMAGE_SRC = lazy_re.compile(
++ r'<img[^>]+src=\"(?P<filename>[^">]*)\"[^>]*>')
++
++def GetImageList(
++ base_path, filename, scale_factors, distribution,
++ filename_expansion_function=None):
++ """Generate the list of images which match the provided scale factors.
++
++ Takes an image filename and checks for files of the same name in folders
++ corresponding to the supported scale factors. If the file is from a
++ chrome://theme/ source, inserts supported @Nx scale factors as high DPI
++ versions.
++
++ Args:
++ base_path: path to look for relative file paths in
++ filename: name of the base image file
++ scale_factors: a list of the supported scale factors (i.e. ['2x'])
++ distribution: string that should replace %DISTRIBUTION%
++
++ Returns:
++ array of tuples containing scale factor and image (i.e.
++ [('1x', 'image.png'), ('2x', '2x/image.png')]).
++ """
++ # Any matches for which a chrome URL handler will serve all scale factors
++ # can simply request all scale factors.
++ theme_match = _THEME_SOURCE.match(filename)
++ if theme_match:
++ images = [('1x', filename)]
++ for scale_factor in scale_factors:
++ scale_filename = "%s@%s" % (theme_match.group('baseurl'), scale_factor)
++ if theme_match.group('query'):
++ scale_filename += theme_match.group('query')
++ images.append((scale_factor, scale_filename))
++ return images
++
++ if filename.find(':') != -1:
++ # filename is probably a URL, only return filename itself.
++ return [('1x', filename)]
++
++ filename = filename.replace(DIST_SUBSTR, distribution)
++ if filename_expansion_function:
++ filename = filename_expansion_function(filename)
++ filepath = os.path.join(base_path, filename)
++ images = [('1x', filename)]
++
++ for scale_factor in scale_factors:
++ # Check for existence of file and add to image set.
++ scale_path = os.path.split(os.path.join(base_path, filename))
++ scale_image_path = os.path.join(scale_path[0], scale_factor, scale_path[1])
++ if os.path.isfile(scale_image_path):
++ # HTML/CSS always uses forward slashed paths.
++ parts = filename.rsplit('/', 1)
++ if len(parts) == 1:
++ path = ''
++ else:
++ path = parts[0] + '/'
++ scale_image_name = path + scale_factor + '/' + parts[-1]
++ images.append((scale_factor, scale_image_name))
++ return images
++
++
++def GenerateImageSet(images, quote):
++ """Generates a -webkit-image-set for the provided list of images.
++
++ Args:
++ images: an array of tuples giving scale factor and file path
++ (i.e. [('1x', 'image.png'), ('2x', '2x/image.png')]).
++ quote: a string giving the quotation character to use (i.e. "'")
++
++ Returns:
++ string giving a -webkit-image-set rule referencing the provided images.
++ (i.e. '-webkit-image-set(url('image.png') 1x, url('2x/image.png') 2x)')
++ """
++ imageset = []
++ for (scale_factor, filename) in images:
++ imageset.append("url(%s%s%s) %s" % (quote, filename, quote, scale_factor))
++ return "-webkit-image-set(%s)" % (', '.join(imageset))
++
++
++def UrlToImageSet(
++ src_match, base_path, scale_factors, distribution,
++ filename_expansion_function=None):
++ """Regex replace function which replaces url() with -webkit-image-set.
++
++ Takes a regex match for url('path'). If the file is local, checks for
++ files of the same name in folders corresponding to the supported scale
++ factors. If the file is from a chrome://theme/ source, inserts the
++ supported @Nx scale factor request. In either case inserts a
++ -webkit-image-set rule to fetch the appropriate image for the current
++ scale factor.
++
++ Args:
++ src_match: regex match object from _CSS_URLS
++ base_path: path to look for relative file paths in
++ scale_factors: a list of the supported scale factors (i.e. ['2x'])
++ distribution: string that should replace %DISTRIBUTION%.
++
++ Returns:
++ string
++ """
++ quote = src_match.group('quote')
++ filename = src_match.group('filename')
++ image_list = GetImageList(
++ base_path, filename, scale_factors, distribution,
++ filename_expansion_function=filename_expansion_function)
++
++ # Don't modify the source if there is only one image.
++ if len(image_list) == 1:
++ return src_match.group(0)
++
++ return GenerateImageSet(image_list, quote)
++
++
++def InsertImageSet(
++ src_match, base_path, scale_factors, distribution,
++ filename_expansion_function=None):
++ """Regex replace function which inserts -webkit-image-set rules.
++
++ Takes a regex match for `property: url('path')[, url('path')]+`.
++ Replaces one or more occurances of the match with image set rules.
++
++ Args:
++ src_match: regex match object from _CSS_IMAGE_URLS
++ base_path: path to look for relative file paths in
++ scale_factors: a list of the supported scale factors (i.e. ['2x'])
++ distribution: string that should replace %DISTRIBUTION%.
++
++ Returns:
++ string
++ """
++ attr = src_match.group('attribute')
++ urls = _CSS_URL.sub(
++ lambda m: UrlToImageSet(m, base_path, scale_factors, distribution,
++ filename_expansion_function),
++ src_match.group('urls'))
++
++ return "%s: %s" % (attr, urls)
++
++
++def InsertImageStyle(
++ src_match, base_path, scale_factors, distribution,
++ filename_expansion_function=None):
++ """Regex replace function which adds a content style to an <img>.
++
++ Takes a regex match from _HTML_IMAGE_SRC and replaces the attribute with a CSS
++ style which defines the image set.
++ """
++ filename = src_match.group('filename')
++ image_list = GetImageList(
++ base_path, filename, scale_factors, distribution,
++ filename_expansion_function=filename_expansion_function)
++
++ # Don't modify the source if there is only one image or image already defines
++ # a style.
++ if src_match.group(0).find(" style=\"") != -1 or len(image_list) == 1:
++ return src_match.group(0)
++
++ return "%s style=\"content: %s;\">" % (src_match.group(0)[:-1],
++ GenerateImageSet(image_list, "'"))
++
++
++def InsertImageSets(
++ filepath, text, scale_factors, distribution,
++ filename_expansion_function=None):
++ """Helper function that adds references to external images available in any of
++ scale_factors in CSS backgrounds.
++ """
++ # Add high DPI urls for css attributes: content, background,
++ # or *-image or <img src="foo">.
++ return _CSS_IMAGE_URLS.sub(
++ lambda m: InsertImageSet(
++ m, filepath, scale_factors, distribution,
++ filename_expansion_function=filename_expansion_function),
++ _HTML_IMAGE_SRC.sub(
++ lambda m: InsertImageStyle(
++ m, filepath, scale_factors, distribution,
++ filename_expansion_function=filename_expansion_function),
++ text))
++
++
++def RemoveImagesNotIn(scale_factors, src_match):
++ """Regex replace function which removes images for scale factors not in
++ scale_factors.
++
++ Takes a regex match for _CSS_IMAGE_SETS. For each image in the group images,
++ checks if this scale factor is in scale_factors and if not, removes it.
++
++ Args:
++ scale_factors: a list of the supported scale factors (i.e. ['1x', '2x'])
++ src_match: regex match object from _CSS_IMAGE_SETS
++
++ Returns:
++ string
++ """
++ attr = src_match.group('attribute')
++ images = _CSS_IMAGE_SET_IMAGE.sub(
++ lambda m: m.group(0) if m.group('scale') in scale_factors else '',
++ src_match.group('images'))
++ return "%s: -webkit-image-set(%s)" % (attr, images)
++
++
++def RemoveImageSetImages(text, scale_factors):
++ """Helper function which removes images in image sets not in the list of
++ supported scale_factors.
++ """
++ return _CSS_IMAGE_SETS.sub(
++ lambda m: RemoveImagesNotIn(scale_factors, m), text)
++
++
++def ProcessImageSets(
++ filepath, text, scale_factors, distribution,
++ filename_expansion_function=None):
++ """Helper function that adds references to external images available in other
++ scale_factors and removes images from image-sets in unsupported scale_factors.
++ """
++ # Explicitly add 1x to supported scale factors so that it is not removed.
++ supported_scale_factors = ['1x']
++ supported_scale_factors.extend(scale_factors)
++ return InsertImageSets(
++ filepath,
++ RemoveImageSetImages(text, supported_scale_factors),
++ scale_factors,
++ distribution,
++ filename_expansion_function=filename_expansion_function)
++
++
++class ChromeHtml(interface.GathererBase):
++ """Represents an HTML document processed for Chrome WebUI.
++
++ HTML documents used in Chrome WebUI have local resources inlined and
++ automatically insert references to high DPI assets used in CSS properties
++ with the use of the -webkit-image-set value. References to unsupported scale
++ factors in image sets are also removed. This does not generate any
++ translateable messages and instead generates a single DataPack resource.
++ """
++
++ def __init__(self, *args, **kwargs):
++ super(ChromeHtml, self).__init__(*args, **kwargs)
++ self.allow_external_script_ = False
++ self.flatten_html_ = False
++ self.preprocess_only_ = False
++ # 1x resources are implicitly already in the source and do not need to be
++ # added.
++ self.scale_factors_ = []
++ self.filename_expansion_function = None
++
++ def SetAttributes(self, attrs):
++ self.allow_external_script_ = ('allowexternalscript' in attrs and
++ attrs['allowexternalscript'] == 'true')
++ self.preprocess_only_ = ('preprocess' in attrs and
++ attrs['preprocess'] == 'true')
++ self.flatten_html_ = (self.preprocess_only_ or ('flattenhtml' in attrs and
++ attrs['flattenhtml'] == 'true'))
++
++ def SetDefines(self, defines):
++ if 'scale_factors' in defines:
++ self.scale_factors_ = defines['scale_factors'].split(',')
++
++ def GetText(self):
++ """Returns inlined text of the HTML document."""
++ return self.inlined_text_
++
++ def GetTextualIds(self):
++ return [self.extkey]
++
++ def GetData(self, lang, encoding):
++ """Returns inlined text of the HTML document."""
++ ret = self.inlined_text_
++ if encoding == util.BINARY:
++ ret = ret.encode('utf-8')
++ return ret
++
++ def GetHtmlResourceFilenames(self):
++ """Returns a set of all filenames inlined by this file."""
++ if self.flatten_html_:
++ return html_inline.GetResourceFilenames(
++ self.grd_node.ToRealPath(self.GetInputPath()),
++ self.grd_node,
++ allow_external_script=self.allow_external_script_,
++ rewrite_function=lambda fp, t, d: ProcessImageSets(
++ fp, t, self.scale_factors_, d,
++ filename_expansion_function=self.filename_expansion_function),
++ filename_expansion_function=self.filename_expansion_function)
++ return []
++
++ def Translate(self, lang, pseudo_if_not_available=True,
++ skeleton_gatherer=None, fallback_to_english=False):
++ """Returns this document translated."""
++ return self.inlined_text_
++
++ def SetFilenameExpansionFunction(self, fn):
++ self.filename_expansion_function = fn
++
++ def Parse(self):
++ """Parses and inlines the represented file."""
++
++ filename = self.GetInputPath()
++ # If there is a grd_node, prefer its GetInputPath(), as that may do more
++ # processing to make the call to ToRealPath() below work correctly.
++ if self.grd_node:
++ filename = self.grd_node.GetInputPath()
++ if self.filename_expansion_function:
++ filename = self.filename_expansion_function(filename)
++ # Hack: some unit tests supply an absolute path and no root node.
++ if not os.path.isabs(filename):
++ filename = self.grd_node.ToRealPath(filename)
++ if self.flatten_html_:
++ self.inlined_text_ = html_inline.InlineToString(
++ filename,
++ self.grd_node,
++ allow_external_script = self.allow_external_script_,
++ strip_whitespace=True,
++ preprocess_only = self.preprocess_only_,
++ rewrite_function=lambda fp, t, d: ProcessImageSets(
++ fp, t, self.scale_factors_, d,
++ filename_expansion_function=self.filename_expansion_function),
++ filename_expansion_function=self.filename_expansion_function)
++ else:
++ distribution = html_inline.GetDistribution()
++ self.inlined_text_ = ProcessImageSets(
++ os.path.dirname(filename),
++ util.ReadFile(filename, 'utf-8'),
++ self.scale_factors_,
++ distribution,
++ filename_expansion_function=self.filename_expansion_function)
+diff --git a/tools/grit/grit/gather/chrome_html_unittest.py b/tools/grit/grit/gather/chrome_html_unittest.py
+new file mode 100644
+index 0000000000..8c75ee5bf4
+--- /dev/null
++++ b/tools/grit/grit/gather/chrome_html_unittest.py
+@@ -0,0 +1,610 @@
++#!/usr/bin/env python
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++'''Unit tests for grit.gather.chrome_html'''
++
++from __future__ import print_function
++
++import os
++import re
++import sys
++if __name__ == '__main__':
++ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
++
++import unittest
++
++from grit import lazy_re
++from grit import util
++from grit.gather import chrome_html
++
++
++_NEW_LINE = lazy_re.compile('(\r\n|\r|\n)', re.MULTILINE)
++
++
++def StandardizeHtml(text):
++ '''Standardizes the newline format and png mime type in Html text.'''
++ return _NEW_LINE.sub('\n', text).replace('data:image/x-png;',
++ 'data:image/png;')
++
++
++class ChromeHtmlUnittest(unittest.TestCase):
++ '''Unit tests for ChromeHtml.'''
++
++ def testFileResources(self):
++ '''Tests inlined image file resources with available high DPI assets.'''
++
++ tmp_dir = util.TempDir({
++ 'index.html': '''
++ <!DOCTYPE HTML>
++ <html>
++ <head>
++ <link rel="stylesheet" href="test.css">
++ </head>
++ <body>
++ <!-- Don't need a body. -->
++ </body>
++ </html>
++ ''',
++
++ 'test.css': '''
++ .image {
++ background: url('test.png');
+ }
-+ }
-+ }
++ ''',
++
++ 'test.png': 'PNG DATA',
++
++ '1.4x/test.png': '1.4x PNG DATA',
++
++ '1.8x/test.png': '1.8x PNG DATA',
++ })
++
++ html = chrome_html.ChromeHtml(tmp_dir.GetPath('index.html'))
++ html.SetDefines({'scale_factors': '1.4x,1.8x'})
++ html.SetAttributes({'flattenhtml': 'true'})
++ html.Parse()
++ self.failUnlessEqual(StandardizeHtml(html.GetData('en', 'utf-8')),
++ StandardizeHtml('''
++ <!DOCTYPE HTML>
++ <html>
++ <head>
++ <style>
++ .image {
++ background: -webkit-image-set(url('data:image/png;base64,UE5HIERBVEE=') 1x, url('data:image/png;base64,MS40eCBQTkcgREFUQQ==') 1.4x, url('data:image/png;base64,MS44eCBQTkcgREFUQQ==') 1.8x);
++ }
++ </style>
++ </head>
++ <body>
++ <!-- Don't need a body. -->
++ </body>
++ </html>
++ '''))
++ tmp_dir.CleanUp()
++
++ def testFileResourcesImageTag(self):
++ '''Tests inlined image file resources with available high DPI assets on
++ an image tag.'''
++
++ tmp_dir = util.TempDir({
++ 'index.html': '''
++ <!DOCTYPE HTML>
++ <html>
++ <body>
++ <img id="foo" src="test.png">
++ </body>
++ </html>
++ ''',
++
++ 'test.png': 'PNG DATA',
++
++ '2x/test.png': '2x PNG DATA',
++ })
++
++ html = chrome_html.ChromeHtml(tmp_dir.GetPath('index.html'))
++ html.SetDefines({'scale_factors': '2x'})
++ html.SetAttributes({'flattenhtml': 'true'})
++ html.Parse()
++ self.failUnlessEqual(StandardizeHtml(html.GetData('en', 'utf-8')),
++ StandardizeHtml('''
++ <!DOCTYPE HTML>
++ <html>
++ <body>
++ <img id="foo" src="data:image/png;base64,UE5HIERBVEE=" style="content: -webkit-image-set(url('data:image/png;base64,UE5HIERBVEE=') 1x, url('data:image/png;base64,MnggUE5HIERBVEE=') 2x);">
++ </body>
++ </html>
++ '''))
++ tmp_dir.CleanUp()
++
++ def testFileResourcesNoFlatten(self):
++ '''Tests non-inlined image file resources with available high DPI assets.'''
++
++ tmp_dir = util.TempDir({
++ 'test.css': '''
++ .image {
++ background: url('test.png');
++ }
++ ''',
++
++ 'test.png': 'PNG DATA',
++
++ '1.4x/test.png': '1.4x PNG DATA',
+
-+ void CountType(const Type* type,
-+ int* trivial_member,
-+ int* non_trivial_member,
-+ int* templated_non_trivial_member) {
-+ switch (type->getTypeClass()) {
-+ case Type::Record: {
-+ // Simplifying; the whole class isn't trivial if the dtor is, but
-+ // we use this as a signal about complexity.
-+ if (TypeHasNonTrivialDtor(type))
-+ (*trivial_member)++;
-+ else
-+ (*non_trivial_member)++;
-+ break;
++ '1.8x/test.png': '1.8x PNG DATA',
++ })
++
++ html = chrome_html.ChromeHtml(tmp_dir.GetPath('test.css'))
++ html.SetDefines({'scale_factors': '1.4x,1.8x'})
++ html.SetAttributes({'flattenhtml': 'false'})
++ html.Parse()
++ self.failUnlessEqual(StandardizeHtml(html.GetData('en', 'utf-8')),
++ StandardizeHtml('''
++ .image {
++ background: -webkit-image-set(url('test.png') 1x, url('1.4x/test.png') 1.4x, url('1.8x/test.png') 1.8x);
+ }
-+ case Type::TemplateSpecialization: {
-+ TemplateName name =
-+ dyn_cast<TemplateSpecializationType>(type)->getTemplateName();
-+ bool whitelisted_template = false;
-+
-+ // HACK: I'm at a loss about how to get the syntax checker to get
-+ // whether a template is exterened or not. For the first pass here,
-+ // just do retarded string comparisons.
-+ if (TemplateDecl* decl = name.getAsTemplateDecl()) {
-+ std::string base_name = decl->getNameAsString();
-+ if (base_name == "basic_string")
-+ whitelisted_template = true;
-+ }
++ '''))
++ tmp_dir.CleanUp()
+
-+ if (whitelisted_template)
-+ (*non_trivial_member)++;
-+ else
-+ (*templated_non_trivial_member)++;
-+ break;
++ def testFileResourcesNoFlattenSubdir(self):
++ '''Tests non-inlined image file resources w/high DPI assets in subdirs.'''
++
++ tmp_dir = util.TempDir({
++ 'test.css': '''
++ .image {
++ background: url('sub/test.png');
+ }
-+ case Type::Elaborated: {
-+ CountType(
-+ dyn_cast<ElaboratedType>(type)->getNamedType().getTypePtr(),
-+ trivial_member, non_trivial_member, templated_non_trivial_member);
-+ break;
++ ''',
++
++ 'sub/test.png': 'PNG DATA',
++
++ 'sub/1.4x/test.png': '1.4x PNG DATA',
++
++ 'sub/1.8x/test.png': '1.8x PNG DATA',
++ })
++
++ html = chrome_html.ChromeHtml(tmp_dir.GetPath('test.css'))
++ html.SetDefines({'scale_factors': '1.4x,1.8x'})
++ html.SetAttributes({'flattenhtml': 'false'})
++ html.Parse()
++ self.failUnlessEqual(StandardizeHtml(html.GetData('en', 'utf-8')),
++ StandardizeHtml('''
++ .image {
++ background: -webkit-image-set(url('sub/test.png') 1x, url('sub/1.4x/test.png') 1.4x, url('sub/1.8x/test.png') 1.8x);
+ }
-+ case Type::Typedef: {
-+ while (const TypedefType* TT = dyn_cast<TypedefType>(type)) {
-+ type = TT->getDecl()->getUnderlyingType().getTypePtr();
-+ }
-+ CountType(type, trivial_member, non_trivial_member,
-+ templated_non_trivial_member);
-+ break;
++ '''))
++ tmp_dir.CleanUp()
++
++ def testFileResourcesPreprocess(self):
++ '''Tests preprocessed image file resources with available high DPI
++ assets.'''
++
++ tmp_dir = util.TempDir({
++ 'test.css': '''
++ .image {
++ background: url('test.png');
+ }
-+ default: {
-+ // Stupid assumption: anything we see that isn't the above is one of
-+ // the 20 integer types.
-+ (*trivial_member)++;
-+ break;
++ ''',
++
++ 'test.png': 'PNG DATA',
++
++ '1.4x/test.png': '1.4x PNG DATA',
++
++ '1.8x/test.png': '1.8x PNG DATA',
++ })
++
++ html = chrome_html.ChromeHtml(tmp_dir.GetPath('test.css'))
++ html.SetDefines({'scale_factors': '1.4x,1.8x'})
++ html.SetAttributes({'flattenhtml': 'false', 'preprocess': 'true'})
++ html.Parse()
++ self.failUnlessEqual(StandardizeHtml(html.GetData('en', 'utf-8')),
++ StandardizeHtml('''
++ .image {
++ background: -webkit-image-set(url('test.png') 1x, url('1.4x/test.png') 1.4x, url('1.8x/test.png') 1.8x);
+ }
-+ }
-+ }
-+};
++ '''))
++ tmp_dir.CleanUp()
+
-+class FindBadConstructsAction : public PluginASTAction {
-+ public:
-+ FindBadConstructsAction()
-+ : check_refcounted_dtors_(true),
-+ check_virtuals_in_implementations_(true) {
-+ }
++ def testFileResourcesDoubleQuotes(self):
++ '''Tests inlined image file resources if url() filename is double quoted.'''
++
++ tmp_dir = util.TempDir({
++ 'test.css': '''
++ .image {
++ background: url("test.png");
++ }
++ ''',
++
++ 'test.png': 'PNG DATA',
++
++ '2x/test.png': '2x PNG DATA',
++ })
++
++ html = chrome_html.ChromeHtml(tmp_dir.GetPath('test.css'))
++ html.SetDefines({'scale_factors': '2x'})
++ html.SetAttributes({'flattenhtml': 'true'})
++ html.Parse()
++ self.failUnlessEqual(StandardizeHtml(html.GetData('en', 'utf-8')),
++ StandardizeHtml('''
++ .image {
++ background: -webkit-image-set(url("data:image/png;base64,UE5HIERBVEE=") 1x, url("data:image/png;base64,MnggUE5HIERBVEE=") 2x);
++ }
++ '''))
++ tmp_dir.CleanUp()
++
++ def testFileResourcesNoQuotes(self):
++ '''Tests inlined image file resources when url() filename is unquoted.'''
++
++ tmp_dir = util.TempDir({
++ 'test.css': '''
++ .image {
++ background: url(test.png);
++ }
++ ''',
++
++ 'test.png': 'PNG DATA',
+
-+ protected:
-+ // Overridden from PluginASTAction:
-+ virtual ASTConsumer* CreateASTConsumer(CompilerInstance& instance,
-+ llvm::StringRef ref) {
-+ return new FindBadConstructsConsumer(
-+ instance, check_refcounted_dtors_, check_virtuals_in_implementations_);
++ '2x/test.png': '2x PNG DATA',
++ })
++
++ html = chrome_html.ChromeHtml(tmp_dir.GetPath('test.css'))
++ html.SetDefines({'scale_factors': '2x'})
++ html.SetAttributes({'flattenhtml': 'true'})
++ html.Parse()
++ self.failUnlessEqual(StandardizeHtml(html.GetData('en', 'utf-8')),
++ StandardizeHtml('''
++ .image {
++ background: -webkit-image-set(url(data:image/png;base64,UE5HIERBVEE=) 1x, url(data:image/png;base64,MnggUE5HIERBVEE=) 2x);
++ }
++ '''))
++ tmp_dir.CleanUp()
++
++ def testFileResourcesSubdirs(self):
++ '''Tests inlined image file resources if url() filename is in a subdir.'''
++
++ tmp_dir = util.TempDir({
++ 'test.css': '''
++ .image {
++ background: url('some/sub/path/test.png');
++ }
++ ''',
++
++ 'some/sub/path/test.png': 'PNG DATA',
++
++ 'some/sub/path/2x/test.png': '2x PNG DATA',
++ })
++
++ html = chrome_html.ChromeHtml(tmp_dir.GetPath('test.css'))
++ html.SetDefines({'scale_factors': '2x'})
++ html.SetAttributes({'flattenhtml': 'true'})
++ html.Parse()
++ self.failUnlessEqual(StandardizeHtml(html.GetData('en', 'utf-8')),
++ StandardizeHtml('''
++ .image {
++ background: -webkit-image-set(url('data:image/png;base64,UE5HIERBVEE=') 1x, url('data:image/png;base64,MnggUE5HIERBVEE=') 2x);
++ }
++ '''))
++ tmp_dir.CleanUp()
++
++ def testFileResourcesNoFile(self):
++ '''Tests inlined image file resources without available high DPI assets.'''
++
++ tmp_dir = util.TempDir({
++ 'index.html': '''
++ <!DOCTYPE HTML>
++ <html>
++ <head>
++ <link rel="stylesheet" href="test.css">
++ </head>
++ <body>
++ <!-- Don't need a body. -->
++ </body>
++ </html>
++ ''',
++
++ 'test.css': '''
++ .image {
++ background: url('test.png');
++ }
++ ''',
++
++ 'test.png': 'PNG DATA',
++ })
++
++ html = chrome_html.ChromeHtml(tmp_dir.GetPath('index.html'))
++ html.SetDefines({'scale_factors': '2x'})
++ html.SetAttributes({'flattenhtml': 'true'})
++ html.Parse()
++ self.failUnlessEqual(StandardizeHtml(html.GetData('en', 'utf-8')),
++ StandardizeHtml('''
++ <!DOCTYPE HTML>
++ <html>
++ <head>
++ <style>
++ .image {
++ background: url('data:image/png;base64,UE5HIERBVEE=');
++ }
++ </style>
++ </head>
++ <body>
++ <!-- Don't need a body. -->
++ </body>
++ </html>
++ '''))
++ tmp_dir.CleanUp()
++
++ def testFileResourcesMultipleBackgrounds(self):
++ '''Tests inlined image file resources with two url()s.'''
++
++ tmp_dir = util.TempDir({
++ 'test.css': '''
++ .image {
++ background: url(test.png), url(test.png);
++ }
++ ''',
++
++ 'test.png': 'PNG DATA',
++
++ '2x/test.png': '2x PNG DATA',
++ })
++
++ html = chrome_html.ChromeHtml(tmp_dir.GetPath('test.css'))
++ html.SetDefines({'scale_factors': '2x'})
++ html.SetAttributes({'flattenhtml': 'true'})
++ html.Parse()
++ self.failUnlessEqual(StandardizeHtml(html.GetData('en', 'utf-8')),
++ StandardizeHtml('''
++ .image {
++ background: -webkit-image-set(url(data:image/png;base64,UE5HIERBVEE=) 1x, url(data:image/png;base64,MnggUE5HIERBVEE=) 2x), -webkit-image-set(url(data:image/png;base64,UE5HIERBVEE=) 1x, url(data:image/png;base64,MnggUE5HIERBVEE=) 2x);
++ }
++ '''))
++ tmp_dir.CleanUp()
++
++ def testFileResourcesMultipleBackgroundsWithNewline1(self):
++ '''Tests inlined image file resources with line break after first url().'''
++
++ tmp_dir = util.TempDir({
++ 'test.css': '''
++ .image {
++ background: url(test.png),
++ url(test.png);
++ }
++ ''',
++
++ 'test.png': 'PNG DATA',
++
++ '2x/test.png': '2x PNG DATA',
++ })
++
++ html = chrome_html.ChromeHtml(tmp_dir.GetPath('test.css'))
++ html.SetDefines({'scale_factors': '2x'})
++ html.SetAttributes({'flattenhtml': 'true'})
++ html.Parse()
++ self.failUnlessEqual(StandardizeHtml(html.GetData('en', 'utf-8')),
++ StandardizeHtml('''
++ .image {
++ background: -webkit-image-set(url(data:image/png;base64,UE5HIERBVEE=) 1x, url(data:image/png;base64,MnggUE5HIERBVEE=) 2x),
++ -webkit-image-set(url(data:image/png;base64,UE5HIERBVEE=) 1x, url(data:image/png;base64,MnggUE5HIERBVEE=) 2x);
++ }
++ '''))
++ tmp_dir.CleanUp()
++
++ def testFileResourcesMultipleBackgroundsWithNewline2(self):
++ '''Tests inlined image file resources with line break before first url()
++ and before second url().'''
++
++ tmp_dir = util.TempDir({
++ 'test.css': '''
++ .image {
++ background:
++ url(test.png),
++ url(test.png);
++ }
++ ''',
++
++ 'test.png': 'PNG DATA',
++
++ '2x/test.png': '2x PNG DATA',
++ })
++
++ html = chrome_html.ChromeHtml(tmp_dir.GetPath('test.css'))
++ html.SetDefines({'scale_factors': '2x'})
++ html.SetAttributes({'flattenhtml': 'true'})
++ html.Parse()
++ self.failUnlessEqual(StandardizeHtml(html.GetData('en', 'utf-8')),
++ StandardizeHtml('''
++ .image {
++ background: -webkit-image-set(url(data:image/png;base64,UE5HIERBVEE=) 1x, url(data:image/png;base64,MnggUE5HIERBVEE=) 2x),
++ -webkit-image-set(url(data:image/png;base64,UE5HIERBVEE=) 1x, url(data:image/png;base64,MnggUE5HIERBVEE=) 2x);
++ }
++ '''))
++ tmp_dir.CleanUp()
++
++ def testFileResourcesCRLF(self):
++ '''Tests inlined image file resource when url() is preceded by a Windows
++ style line break.'''
++
++ tmp_dir = util.TempDir({
++ 'test.css': '''
++ .image {
++ background:\r\nurl(test.png);
++ }
++ ''',
++
++ 'test.png': 'PNG DATA',
++
++ '2x/test.png': '2x PNG DATA',
++ })
++
++ html = chrome_html.ChromeHtml(tmp_dir.GetPath('test.css'))
++ html.SetDefines({'scale_factors': '2x'})
++ html.SetAttributes({'flattenhtml': 'true'})
++ html.Parse()
++ self.failUnlessEqual(StandardizeHtml(html.GetData('en', 'utf-8')),
++ StandardizeHtml('''
++ .image {
++ background: -webkit-image-set(url(data:image/png;base64,UE5HIERBVEE=) 1x, url(data:image/png;base64,MnggUE5HIERBVEE=) 2x);
++ }
++ '''))
++ tmp_dir.CleanUp()
++
++ def testThemeResources(self):
++ '''Tests inserting high DPI chrome://theme references.'''
++
++ tmp_dir = util.TempDir({
++ 'index.html': '''
++ <!DOCTYPE HTML>
++ <html>
++ <head>
++ <link rel="stylesheet" href="test.css">
++ </head>
++ <body>
++ <!-- Don't need a body. -->
++ </body>
++ </html>
++ ''',
++
++ 'test.css': '''
++ .image {
++ background: url('chrome://theme/IDR_RESOURCE_NAME');
++ content: url('chrome://theme/IDR_RESOURCE_NAME_WITH_Q?$1');
++ }
++ ''',
++ })
++
++ html = chrome_html.ChromeHtml(tmp_dir.GetPath('index.html'))
++ html.SetDefines({'scale_factors': '2x'})
++ html.SetAttributes({'flattenhtml': 'true'})
++ html.Parse()
++ self.failUnlessEqual(StandardizeHtml(html.GetData('en', 'utf-8')),
++ StandardizeHtml('''
++ <!DOCTYPE HTML>
++ <html>
++ <head>
++ <style>
++ .image {
++ background: -webkit-image-set(url('chrome://theme/IDR_RESOURCE_NAME') 1x, url('chrome://theme/IDR_RESOURCE_NAME@2x') 2x);
++ content: -webkit-image-set(url('chrome://theme/IDR_RESOURCE_NAME_WITH_Q?$1') 1x, url('chrome://theme/IDR_RESOURCE_NAME_WITH_Q@2x?$1') 2x);
++ }
++ </style>
++ </head>
++ <body>
++ <!-- Don't need a body. -->
++ </body>
++ </html>
++ '''))
++ tmp_dir.CleanUp()
++
++ def testRemoveUnsupportedScale(self):
++ '''Tests removing an unsupported scale factor from an explicit image-set.'''
++
++ tmp_dir = util.TempDir({
++ 'index.html': '''
++ <!DOCTYPE HTML>
++ <html>
++ <head>
++ <link rel="stylesheet" href="test.css">
++ </head>
++ <body>
++ <!-- Don't need a body. -->
++ </body>
++ </html>
++ ''',
++
++ 'test.css': '''
++ .image {
++ background: -webkit-image-set(url('test.png') 1x,
++ url('test1.4.png') 1.4x,
++ url('test1.8.png') 1.8x);
++ }
++ ''',
++
++ 'test.png': 'PNG DATA',
++
++ 'test1.4.png': '1.4x PNG DATA',
++
++ 'test1.8.png': '1.8x PNG DATA',
++ })
++
++ html = chrome_html.ChromeHtml(tmp_dir.GetPath('index.html'))
++ html.SetDefines({'scale_factors': '1.8x'})
++ html.SetAttributes({'flattenhtml': 'true'})
++ html.Parse()
++ self.failUnlessEqual(StandardizeHtml(html.GetData('en', 'utf-8')),
++ StandardizeHtml('''
++ <!DOCTYPE HTML>
++ <html>
++ <head>
++ <style>
++ .image {
++ background: -webkit-image-set(url('data:image/png;base64,UE5HIERBVEE=') 1x,
++ url('data:image/png;base64,MS44eCBQTkcgREFUQQ==') 1.8x);
++ }
++ </style>
++ </head>
++ <body>
++ <!-- Don't need a body. -->
++ </body>
++ </html>
++ '''))
++ tmp_dir.CleanUp()
++
++ def testExpandVariablesInFilename(self):
++ '''
++ Tests variable substitution in filenames while flattening images
++ with multiple scale factors.
++ '''
++
++ tmp_dir = util.TempDir({
++ 'index.html': '''
++ <!DOCTYPE HTML>
++ <html>
++ <head>
++ <link rel="stylesheet" href="test.css">
++ </head>
++ <body>
++ <!-- Don't need a body. -->
++ </body>
++ </html>
++ ''',
++
++ 'test.css': '''
++ .image {
++ background: url('test[WHICH].png');
++ }
++ ''',
++
++ 'test1.png': 'PNG DATA',
++ '1.4x/test1.png': '1.4x PNG DATA',
++ '1.8x/test1.png': '1.8x PNG DATA',
++ })
++
++ def replacer(var, repl):
++ return lambda filename: filename.replace('[%s]' % var, repl)
++
++ html = chrome_html.ChromeHtml(tmp_dir.GetPath('index.html'))
++ html.SetDefines({'scale_factors': '1.4x,1.8x'})
++ html.SetAttributes({'flattenhtml': 'true'})
++ html.SetFilenameExpansionFunction(replacer('WHICH', '1'));
++ html.Parse()
++ self.failUnlessEqual(StandardizeHtml(html.GetData('en', 'utf-8')),
++ StandardizeHtml('''
++ <!DOCTYPE HTML>
++ <html>
++ <head>
++ <style>
++ .image {
++ background: -webkit-image-set(url('data:image/png;base64,UE5HIERBVEE=') 1x, url('data:image/png;base64,MS40eCBQTkcgREFUQQ==') 1.4x, url('data:image/png;base64,MS44eCBQTkcgREFUQQ==') 1.8x);
++ }
++ </style>
++ </head>
++ <body>
++ <!-- Don't need a body. -->
++ </body>
++ </html>
++ '''))
++ tmp_dir.CleanUp()
++
++
++if __name__ == '__main__':
++ unittest.main()
+diff --git a/tools/grit/grit/gather/chrome_scaled_image.py b/tools/grit/grit/gather/chrome_scaled_image.py
+new file mode 100644
+index 0000000000..44f98cbcf0
+--- /dev/null
++++ b/tools/grit/grit/gather/chrome_scaled_image.py
+@@ -0,0 +1,157 @@
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++'''Gatherer for <structure type="chrome_scaled_image">.
++'''
++
++from __future__ import print_function
++
++import os
++import struct
++
++from grit import exception
++from grit import lazy_re
++from grit import util
++from grit.gather import interface
++
++
++_PNG_SCALE_CHUNK = b'\0\0\0\0csCl\xc1\x30\x60\x4d'
++
++
++def _RescaleImage(data, from_scale, to_scale):
++ if from_scale != to_scale:
++ assert from_scale == 100
++ # Rather than rescaling the image we add a custom chunk directing Chrome to
++ # rescale it on load. Just append it to the PNG data since
++ # _MoveSpecialChunksToFront will move it later anyway.
++ data += _PNG_SCALE_CHUNK
++ return data
++
++
++_PNG_MAGIC = b'\x89PNG\r\n\x1a\n'
++
++'''Mandatory first chunk in order for the png to be valid.'''
++_FIRST_CHUNK = b'IHDR'
++
++'''Special chunks to move immediately after the IHDR chunk. (so that the PNG
++remains valid.)
++'''
++_SPECIAL_CHUNKS = frozenset(b'csCl npTc'.split())
++
++'''Any ancillary chunk not in this list is deleted from the PNG.'''
++_ANCILLARY_CHUNKS_TO_LEAVE = frozenset(
++ b'bKGD cHRM gAMA iCCP pHYs sBIT sRGB tRNS acTL fcTL fdAT'.split())
++
++
++def _MoveSpecialChunksToFront(data):
++ '''Move special chunks immediately after the IHDR chunk (so that the PNG
++ remains valid). Also delete ancillary chunks that are not on our whitelist.
++ '''
++ first = [_PNG_MAGIC]
++ special_chunks = []
++ rest = []
++ for chunk in _ChunkifyPNG(data):
++ type = chunk[4:8]
++ critical = type < b'a'
++ if type == _FIRST_CHUNK:
++ first.append(chunk)
++ elif type in _SPECIAL_CHUNKS:
++ special_chunks.append(chunk)
++ elif critical or type in _ANCILLARY_CHUNKS_TO_LEAVE:
++ rest.append(chunk)
++ return b''.join(first + special_chunks + rest)
++
++
++def _ChunkifyPNG(data):
++ '''Given a PNG image, yield its chunks in order.'''
++ assert data.startswith(_PNG_MAGIC)
++ pos = 8
++ while pos != len(data):
++ length = 12 + struct.unpack_from('>I', data, pos)[0]
++ assert 12 <= length <= len(data) - pos
++ yield data[pos:pos+length]
++ pos += length
++
++
++def _MakeBraceGlob(strings):
++ '''Given ['foo', 'bar'], return '{foo,bar}', for error reporting.
++ '''
++ if len(strings) == 1:
++ return strings[0]
++ else:
++ return '{' + ','.join(strings) + '}'
++
++
++class ChromeScaledImage(interface.GathererBase):
++ '''Represents an image that exists in multiple layout variants
++ (e.g. "default", "touch") and multiple scale variants
++ (e.g. "100_percent", "200_percent").
++ '''
++
++ split_context_re_ = lazy_re.compile(r'(.+)_(\d+)_percent\Z')
++
++ def _FindInputFile(self):
++ output_context = self.grd_node.GetRoot().output_context
++ match = self.split_context_re_.match(output_context)
++ if not match:
++ raise exception.MissingMandatoryAttribute(
++ 'All <output> nodes must have an appropriate context attribute'
++ ' (e.g. context="touch_200_percent")')
++ req_layout, req_scale = match.group(1), int(match.group(2))
++
++ layouts = [req_layout]
++ try_default_layout = self.grd_node.GetRoot().fallback_to_default_layout
++ if try_default_layout and 'default' not in layouts:
++ layouts.append('default')
++
++ scales = [req_scale]
++ try_low_res = self.grd_node.FindBooleanAttribute(
++ 'fallback_to_low_resolution', default=False, skip_self=False)
++ if try_low_res and 100 not in scales:
++ scales.append(100)
++
++ for layout in layouts:
++ for scale in scales:
++ dir = '%s_%s_percent' % (layout, scale)
++ path = os.path.join(dir, self.rc_file)
++ if os.path.exists(self.grd_node.ToRealPath(path)):
++ return path, scale, req_scale
++
++ if not try_default_layout:
++ # The file was not found in the specified output context and it was
++ # explicitly indicated that the default context should not be searched
++ # as a fallback, so return an empty path.
++ return None, 100, req_scale
++
++ # The file was found in neither the specified context nor the default
++ # context, so raise an exception.
++ dir = "%s_%s_percent" % (_MakeBraceGlob(layouts),
++ _MakeBraceGlob([str(x) for x in scales]))
++ raise exception.FileNotFound(
++ 'Tried ' + self.grd_node.ToRealPath(os.path.join(dir, self.rc_file)))
++
++ def GetInputPath(self):
++ path, scale, req_scale = self._FindInputFile()
++ return path
++
++ def Parse(self):
++ pass
++
++ def GetTextualIds(self):
++ return [self.extkey]
++
++ def GetData(self, lang, encoding):
++ assert encoding == util.BINARY
++
++ path, scale, req_scale = self._FindInputFile()
++ if path is None:
++ return None
++
++ data = util.ReadFile(self.grd_node.ToRealPath(path), util.BINARY)
++ data = _RescaleImage(data, scale, req_scale)
++ data = _MoveSpecialChunksToFront(data)
++ return data
++
++ def Translate(self, *args, **kwargs):
++ return self.GetData()
+diff --git a/tools/grit/grit/gather/chrome_scaled_image_unittest.py b/tools/grit/grit/gather/chrome_scaled_image_unittest.py
+new file mode 100644
+index 0000000000..1cebfc6de2
+--- /dev/null
++++ b/tools/grit/grit/gather/chrome_scaled_image_unittest.py
+@@ -0,0 +1,209 @@
++#!/usr/bin/env python
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++'''Unit tests for ChromeScaledImage.'''
++
++from __future__ import print_function
++
++import os
++import sys
++if __name__ == '__main__':
++ sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__),
++ '../..')))
++
++import re
++import struct
++import unittest
++import zlib
++
++from grit import exception
++from grit import util
++from grit.format import data_pack
++from grit.tool import build
++
++
++_OUTFILETYPES = [
++ ('.h', 'rc_header'),
++ ('_map.cc', 'resource_map_source'),
++ ('_map.h', 'resource_map_header'),
++ ('.pak', 'data_package'),
++ ('.rc', 'rc_all'),
++]
++
++
++_PNG_HEADER = (
++ b'\x89\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52'
++ b'\x00\x00\x00\x01\x00\x00\x00\x01\x08\x02\x00\x00\x00\x90\x77\x53'
++ b'\xde')
++_PNG_FOOTER = (
++ b'\x00\x00\x00\x0c\x49\x44\x41\x54\x18\x57\x63\xf8\xff\xff\x3f\x00'
++ b'\x05\xfe\x02\xfe\xa7\x35\x81\x84\x00\x00\x00\x00\x49\x45\x4e\x44'
++ b'\xae\x42\x60\x82')
++
++
++def _MakePNG(chunks):
++ # Python 3 changed the return value of zlib.crc32 to an unsigned int.
++ format = 'i' if sys.version_info.major < 3 else 'I'
++ pack_int32 = struct.Struct('>' + format).pack
++ chunks = [pack_int32(len(payload)) + type + payload +
++ pack_int32(zlib.crc32(type + payload))
++ for type, payload in chunks]
++ return _PNG_HEADER + b''.join(chunks) + _PNG_FOOTER
++
++
++def _GetFilesInPak(pakname):
++ '''Get a set of the files that were actually included in the .pak output.
++ '''
++ return set(data_pack.ReadDataPack(pakname).resources.values())
++
++
++def _GetFilesInRc(rcname, tmp_dir, contents):
++ '''Get a set of the files that were actually included in the .rc output.
++ '''
++ data = util.ReadFile(rcname, util.BINARY).decode('utf-16')
++ contents = dict((tmp_dir.GetPath(k), v) for k, v in contents.items())
++ return set(contents[os.path.normpath(m.group(1))]
++ for m in re.finditer(r'(?m)^\w+\s+BINDATA\s+"([^"]+)"$', data))
++
++
++def _MakeFallbackAttr(fallback):
++ if fallback is None:
++ return ''
++ else:
++ return ' fallback_to_low_resolution="%s"' % ('false', 'true')[fallback]
++
++
++def _Structures(fallback, *body):
++ return '<structures%s>\n%s\n</structures>' % (
++ _MakeFallbackAttr(fallback), '\n'.join(body))
++
++
++def _Structure(name, file, fallback=None):
++ return '<structure name="%s" file="%s" type="chrome_scaled_image"%s />' % (
++ name, file, _MakeFallbackAttr(fallback))
++
++
++def _If(expr, *body):
++ return '<if expr="%s">\n%s\n</if>' % (expr, '\n'.join(body))
++
++
++def _RunBuildTest(self, structures, inputs, expected_outputs, skip_rc=False,
++ layout_fallback=''):
++ outputs = '\n'.join('<output filename="out/%s%s" type="%s" context="%s"%s />'
++ % (context, ext, type, context, layout_fallback)
++ for ext, type in _OUTFILETYPES
++ for context in expected_outputs)
++
++ infiles = {
++ 'in/in.grd': ('''<?xml version="1.0" encoding="UTF-8"?>
++ <grit latest_public_release="0" current_release="1">
++ <outputs>
++ %s
++ </outputs>
++ <release seq="1">
++ %s
++ </release>
++ </grit>
++ ''' % (outputs, structures)).encode('utf-8'),
+ }
++ for pngpath, pngdata in inputs.items():
++ normpath = os.path.normpath('in/' + pngpath)
++ infiles[normpath] = pngdata
++ class Options(object):
++ pass
++
++ with util.TempDir(infiles, mode='wb') as tmp_dir:
++ with tmp_dir.AsCurrentDir():
++ options = Options()
++ options.input = tmp_dir.GetPath('in/in.grd')
++ options.verbose = False
++ options.extra_verbose = False
++ build.RcBuilder().Run(options, [])
++ for context, expected_data in expected_outputs.items():
++ self.assertEquals(expected_data,
++ _GetFilesInPak(tmp_dir.GetPath('out/%s.pak' % context)))
++ if not skip_rc:
++ self.assertEquals(expected_data,
++ _GetFilesInRc(tmp_dir.GetPath('out/%s.rc' % context),
++ tmp_dir, infiles))
++
++
++class ChromeScaledImageUnittest(unittest.TestCase):
++ def testNormalFallback(self):
++ d123a = _MakePNG([(b'AbCd', b'')])
++ t123a = _MakePNG([(b'EfGh', b'')])
++ d123b = _MakePNG([(b'IjKl', b'')])
++ _RunBuildTest(self,
++ _Structures(None,
++ _Structure('IDR_A', 'a.png'),
++ _Structure('IDR_B', 'b.png'),
++ ),
++ {'default_123_percent/a.png': d123a,
++ 'tactile_123_percent/a.png': t123a,
++ 'default_123_percent/b.png': d123b,
++ },
++ {'default_123_percent': set([d123a, d123b]),
++ 'tactile_123_percent': set([t123a, d123b]),
++ })
++
++ def testNormalFallbackFailure(self):
++ self.assertRaises(
++ exception.FileNotFound, _RunBuildTest, self,
++ _Structures(
++ None,
++ _Structure('IDR_A', 'a.png'),
++ ), {
++ 'default_100_percent/a.png': _MakePNG([(b'AbCd', b'')]),
++ 'tactile_100_percent/a.png': _MakePNG([(b'EfGh', b'')]),
++ }, {'tactile_123_percent': 'should fail before using this'})
++
++ def testLowresFallback(self):
++ png = _MakePNG([(b'Abcd', b'')])
++ png_with_csCl = _MakePNG([(b'csCl', b''), (b'Abcd', b'')])
++ for outer in (None, False, True):
++ for inner in (None, False, True):
++ args = (
++ self,
++ _Structures(outer,
++ _Structure('IDR_A', 'a.png', inner),
++ ),
++ {'default_100_percent/a.png': png},
++ {'tactile_200_percent': set([png_with_csCl])})
++ if inner or (inner is None and outer):
++ # should fall back to 100%
++ _RunBuildTest(*args, skip_rc=True)
++ else:
++ # shouldn't fall back
++ self.assertRaises(exception.FileNotFound, _RunBuildTest, *args)
++
++ # Test fallback failure with fallback_to_low_resolution=True
++ self.assertRaises(exception.FileNotFound,
++ _RunBuildTest, self,
++ _Structures(True,
++ _Structure('IDR_A', 'a.png'),
++ ),
++ {}, # no files
++ {'tactile_123_percent': 'should fail before using this'})
++
++ def testNoFallbackToDefaultLayout(self):
++ d123a = _MakePNG([(b'AbCd', b'')])
++ t123a = _MakePNG([(b'EfGh', b'')])
++ d123b = _MakePNG([(b'IjKl', b'')])
++ _RunBuildTest(self,
++ _Structures(None,
++ _Structure('IDR_A', 'a.png'),
++ _Structure('IDR_B', 'b.png'),
++ ),
++ {'default_123_percent/a.png': d123a,
++ 'tactile_123_percent/a.png': t123a,
++ 'default_123_percent/b.png': d123b,
++ },
++ {'default_123_percent': set([d123a, d123b]),
++ 'tactile_123_percent': set([t123a]),
++ },
++ layout_fallback=' fallback_to_default_layout="false"')
+
-+ virtual bool ParseArgs(const CompilerInstance& instance,
-+ const std::vector<std::string>& args) {
-+ bool parsed = true;
++if __name__ == '__main__':
++ unittest.main()
+diff --git a/tools/grit/grit/gather/interface.py b/tools/grit/grit/gather/interface.py
+new file mode 100644
+index 0000000000..15d64f9326
+--- /dev/null
++++ b/tools/grit/grit/gather/interface.py
+@@ -0,0 +1,172 @@
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
+
-+ for (size_t i = 0; i < args.size() && parsed; ++i) {
-+ if (args[i] == "skip-refcounted-dtors") {
-+ check_refcounted_dtors_ = false;
-+ } else if (args[i] == "skip-virtuals-in-implementations") {
-+ check_virtuals_in_implementations_ = false;
-+ } else {
-+ parsed = false;
-+ llvm::errs() << "Unknown argument: " << args[i] << "\n";
++'''Interface for all gatherers.
++'''
++
++from __future__ import print_function
++
++import os.path
++
++import six
++
++from grit import clique
++from grit import util
++
++
++class GathererBase(object):
++ '''Interface for all gatherer implementations. Subclasses must implement
++ all methods that raise NotImplemented.'''
++
++ def __init__(self, rc_file, extkey=None, encoding='cp1252', is_skeleton=False):
++ '''Initializes the gatherer object's attributes, but does not attempt to
++ read the input file.
++
++ Args:
++ rc_file: The 'file' attribute of the <structure> node (usually the
++ relative path to the source file).
++ extkey: e.g. 'ID_MY_DIALOG'
++ encoding: e.g. 'utf-8'
++ is_skeleton: Indicates whether this gatherer is a skeleton gatherer, in
++ which case we should not do some types of processing on the
++ translateable bits.
++ '''
++ self.rc_file = rc_file
++ self.extkey = extkey
++ self.encoding = encoding
++ # A default uberclique that is local to this object. Users can override
++ # this with the uberclique they are using.
++ self.uberclique = clique.UberClique()
++ # Indicates whether this gatherer is a skeleton gatherer, in which case
++ # we should not do some types of processing on the translateable bits.
++ self.is_skeleton = is_skeleton
++ # Stores the grd node on which this gatherer is running. This allows
++ # evaluating expressions.
++ self.grd_node = None
++
++ def SetAttributes(self, attrs):
++ '''Sets node attributes used by the gatherer.
++
++ By default, this does nothing. If special handling is desired, it should be
++ overridden by the child gatherer.
++
++ Args:
++ attrs: The mapping of node attributes.
++ '''
++ pass
++
++ def SetDefines(self, defines):
++ '''Sets global defines used by the gatherer.
++
++ By default, this does nothing. If special handling is desired, it should be
++ overridden by the child gatherer.
++
++ Args:
++ defines: The mapping of define values.
++ '''
++ pass
++
++ def SetGrdNode(self, node):
++ '''Sets the grd node on which this gatherer is running.
++ '''
++ self.grd_node = node
++
++ def SetUberClique(self, uberclique):
++ '''Overrides the default uberclique so that cliques created by this object
++ become part of the uberclique supplied by the user.
++ '''
++ self.uberclique = uberclique
++
++ def Parse(self):
++ '''Reads and parses the contents of what is being gathered.'''
++ raise NotImplementedError()
++
++ def GetData(self, lang, encoding):
++ '''Returns the data to be added to the DataPack for this node or None if
++ this node does not add a DataPack entry.
++ '''
++ return None
++
++ def GetText(self):
++ '''Returns the text of what is being gathered.'''
++ raise NotImplementedError()
++
++ def GetTextualIds(self):
++ '''Returns the mnemonic IDs that need to be defined for the resource
++ being gathered to compile correctly.'''
++ return []
++
++ def GetCliques(self):
++ '''Returns the MessageClique objects for all translateable portions.'''
++ return []
++
++ def GetInputPath(self):
++ return self.rc_file
++
++ def GetHtmlResourceFilenames(self):
++ """Returns a set of all filenames inlined by this gatherer."""
++ return []
++
++ def Translate(self, lang, pseudo_if_not_available=True,
++ skeleton_gatherer=None, fallback_to_english=False):
++ '''Returns the resource being gathered, with translateable portions filled
++ with the translation for language 'lang'.
++
++ If pseudo_if_not_available is true, a pseudotranslation will be used for any
++ message that doesn't have a real translation available.
++
++ If no translation is available and pseudo_if_not_available is false,
++ fallback_to_english controls the behavior. If it is false, throw an error.
++ If it is true, use the English version of the message as its own
++ "translation".
++
++ If skeleton_gatherer is specified, the translation will use the nontranslateable
++ parts from the gatherer 'skeleton_gatherer', which must be of the same type
++ as 'self'.
++
++ If fallback_to_english
++
++ Args:
++ lang: 'en'
++ pseudo_if_not_available: True | False
++ skeleton_gatherer: other_gatherer
++ fallback_to_english: True | False
++
++ Return:
++ e.g. 'ID_THIS_SECTION TYPE\n...BEGIN\n "Translated message"\n......\nEND'
++
++ Raises:
++ grit.exception.NotReady() if used before Parse() has been successfully
++ called.
++ grit.exception.NoSuchTranslation() if 'pseudo_if_not_available' and
++ fallback_to_english are both false and there is no translation for the
++ requested language.
++ '''
++ raise NotImplementedError()
++
++ def SubstituteMessages(self, substituter):
++ '''Applies substitutions to all messages in the gatherer.
++
++ Args:
++ substituter: a grit.util.Substituter object.
++ '''
++ pass
++
++ def SetFilenameExpansionFunction(self, fn):
++ '''Sets a function for rewriting filenames before gathering.'''
++ pass
++
++ # TODO(benrg): Move this elsewhere, since it isn't part of the interface.
++ def _LoadInputFile(self):
++ '''A convenience function for subclasses that loads the contents of the
++ input file.
++ '''
++ if isinstance(self.rc_file, six.string_types):
++ path = self.GetInputPath()
++ # Hack: some unit tests supply an absolute path and no root node.
++ if not os.path.isabs(path):
++ path = self.grd_node.ToRealPath(path)
++ return util.ReadFile(path, self.encoding)
++ else:
++ return self.rc_file.read()
+diff --git a/tools/grit/grit/gather/json_loader.py b/tools/grit/grit/gather/json_loader.py
+new file mode 100644
+index 0000000000..058e5f17ae
+--- /dev/null
++++ b/tools/grit/grit/gather/json_loader.py
+@@ -0,0 +1,27 @@
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++from __future__ import print_function
++
++from grit.gather import interface
++
++
++class JsonLoader(interface.GathererBase):
++ '''A simple gatherer that loads and parses a JSON file.'''
++
++ def Parse(self):
++ '''Reads and parses the text of self._json_text into the data structure in
++ self._data.
++ '''
++ self._json_text = self._LoadInputFile()
++ self._data = None
++
++ globs = {}
++ exec('data = ' + self._json_text, globs)
++ self._data = globs['data']
++
++ def GetData(self, lang, encoding):
++ '''Returns the parsed JSON data.'''
++ assert encoding == 'utf-8'
++ return self._data
+diff --git a/tools/grit/grit/gather/policy_json.py b/tools/grit/grit/gather/policy_json.py
+new file mode 100644
+index 0000000000..6621c5f3c4
+--- /dev/null
++++ b/tools/grit/grit/gather/policy_json.py
+@@ -0,0 +1,325 @@
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++'''Support for "policy_templates.json" format used by the policy template
++generator as a source for generating ADM,ADMX,etc files.'''
++
++from __future__ import print_function
++
++import json
++import sys
++
++import six
++
++from grit.gather import skeleton_gatherer
++from grit import util
++from grit import tclib
++from xml.dom import minidom
++from xml.parsers.expat import ExpatError
++
++
++class PolicyJson(skeleton_gatherer.SkeletonGatherer):
++ '''Collects and translates the following strings from policy_templates.json:
++ - captions, descriptions, labels and Android app support details of policies
++ - captions of enumeration items
++ - misc strings from the 'messages' section
++ Translatable strings may have untranslateable placeholders with the same
++ format that is used in .grd files.
++ '''
++
++ def _AddEndline(self, add_comma):
++ '''Adds an endline to the skeleton tree. If add_comma is true, adds a
++ comma before the endline.
++
++ Args:
++ add_comma: A boolean to add a comma or not.
++ '''
++ self._AddNontranslateableChunk(',\n' if add_comma else '\n')
++
++ def _ParsePlaceholder(self, placeholder, msg):
++ '''Extracts a placeholder from a DOM node and adds it to a tclib Message.
++
++ Args:
++ placeholder: A DOM node of the form:
++ <ph name="PLACEHOLDER_NAME">Placeholder text<ex>Example value</ex></ph>
++ msg: The placeholder is added to this message.
++ '''
++ text = []
++ example_text = []
++ for node1 in placeholder.childNodes:
++ if (node1.nodeType == minidom.Node.TEXT_NODE):
++ text.append(node1.data)
++ elif (node1.nodeType == minidom.Node.ELEMENT_NODE and
++ node1.tagName == 'ex'):
++ for node2 in node1.childNodes:
++ example_text.append(node2.toxml())
++ else:
++ raise Exception('Unexpected element inside a placeholder: ' +
++ node2.toxml())
++ if example_text == []:
++ # In such cases the original text is okay for an example.
++ example_text = text
++
++ replaced_text = self.Escape(''.join(text).strip())
++ replaced_text = replaced_text.replace('$1', self._config['app_name'])
++ replaced_text = replaced_text.replace('$2', self._config['os_name'])
++ replaced_text = replaced_text.replace('$3', self._config['frame_name'])
++
++ msg.AppendPlaceholder(tclib.Placeholder(
++ placeholder.attributes['name'].value,
++ replaced_text,
++ ''.join(example_text).strip()))
++
++ def _ParseMessage(self, string, desc):
++ '''Parses a given string and adds it to the output as a translatable chunk
++ with a given description.
++
++ Args:
++ string: The message string to parse.
++ desc: The description of the message (for the translators).
++ '''
++ msg = tclib.Message(description=desc)
++ xml = '<msg>' + string + '</msg>'
++ try:
++ node = minidom.parseString(xml).childNodes[0]
++ except ExpatError:
++ reason = '''Input isn't valid XML (has < & > been escaped?): ''' + string
++ six.reraise(Exception, reason, sys.exc_info()[2])
++
++ for child in node.childNodes:
++ if child.nodeType == minidom.Node.TEXT_NODE:
++ msg.AppendText(child.data)
++ elif child.nodeType == minidom.Node.ELEMENT_NODE:
++ if child.tagName == 'ph':
++ self._ParsePlaceholder(child, msg)
++ else:
++ raise Exception("Not implemented.")
++ else:
++ raise Exception("Not implemented.")
++ self.skeleton_.append(self.uberclique.MakeClique(msg))
++
++ def _ParseNode(self, node):
++ '''Traverses the subtree of a DOM node, and register a tclib message for
++ all the <message> nodes.
++ '''
++ att_text = []
++ if node.attributes:
++ for key, value in sorted(node.attributes.items()):
++ att_text.append(' %s=\"%s\"' % (key, value))
++ self._AddNontranslateableChunk("<%s%s>" %
++ (node.tagName, ''.join(att_text)))
++ if node.tagName == 'message':
++ msg = tclib.Message(description=node.attributes['desc'])
++ for child in node.childNodes:
++ if child.nodeType == minidom.Node.TEXT_NODE:
++ if msg == None:
++ self._AddNontranslateableChunk(child.data)
++ else:
++ msg.AppendText(child.data)
++ elif child.nodeType == minidom.Node.ELEMENT_NODE:
++ if child.tagName == 'ph':
++ self._ParsePlaceholder(child, msg)
++ else:
++ assert False
++ self.skeleton_.append(self.uberclique.MakeClique(msg))
++ else:
++ for child in node.childNodes:
++ if child.nodeType == minidom.Node.TEXT_NODE:
++ self._AddNontranslateableChunk(child.data)
++ elif node.nodeType == minidom.Node.ELEMENT_NODE:
++ self._ParseNode(child)
++
++ self._AddNontranslateableChunk("</%s>" % node.tagName)
++
++ def _AddIndentedNontranslateableChunk(self, depth, string):
++ '''Adds a nontranslateable chunk of text to the internally stored output.
++
++ Args:
++ depth: The number of double spaces to prepend to the next argument string.
++ string: The chunk of text to add.
++ '''
++ result = []
++ while depth > 0:
++ result.append(' ')
++ depth = depth - 1
++ result.append(string)
++ self._AddNontranslateableChunk(''.join(result))
++
++ def _GetDescription(self, item, item_type, parent_item, key):
++ '''Creates a description for a translatable message. The description gives
++ some context for the person who will translate this message.
++
++ Args:
++ item: A policy or an enumeration item.
++ item_type: 'enum_item' | 'policy'
++ parent_item: The owner of item. (A policy of type group or enum.)
++ key: The name of the key to parse.
++ depth: The level of indentation.
++ '''
++ key_map = {
++ 'desc': 'Description',
++ 'caption': 'Caption',
++ 'label': 'Label',
++ 'arc_support': 'Information about the effect on Android apps'
++ }
++ if item_type == 'policy':
++ return ('%s of the policy named %s [owner(s): %s]' %
++ (key_map[key], item['name'],
++ ','.join(item['owners'] if 'owners' in item else 'unknown')))
++ if item_type == 'enum_item':
++ return ('%s of the option named %s in policy %s [owner(s): %s]' %
++ (key_map[key], item['name'], parent_item['name'],
++ ','.join(parent_item['owners'] if 'owners' in parent_item else 'unknown')))
++ raise Exception('Unexpected type %s' % item_type)
++
++ def _AddSchemaKeys(self, obj, depth):
++ obj_type = type(obj)
++ if obj_type == dict:
++ self._AddNontranslateableChunk('{\n')
++ keys = sorted(obj.keys())
++ for count, (key) in enumerate(keys, 1):
++ json_key = "%s: " % json.dumps(key)
++ self._AddIndentedNontranslateableChunk(depth + 1, json_key)
++ if key == 'description' and type(obj[key]) == str:
++ self._AddNontranslateableChunk("\"")
++ self._ParseMessage(obj[key], 'Description of schema property')
++ self._AddNontranslateableChunk("\"")
++ elif type(obj[key]) in (bool, int, str):
++ self._AddSchemaKeys(obj[key], 0)
++ else:
++ self._AddSchemaKeys(obj[key], depth + 1)
++ self._AddEndline(count < len(keys))
++ self._AddIndentedNontranslateableChunk(depth, '}')
++ elif obj_type == list:
++ self._AddNontranslateableChunk('[\n')
++ for count, (item) in enumerate(obj, 1):
++ self._AddSchemaKeys(item, depth + 1)
++ self._AddEndline(count < len(obj))
++ self._AddIndentedNontranslateableChunk(depth, ']')
++ elif obj_type in (bool, int, str):
++ self._AddIndentedNontranslateableChunk(depth, json.dumps(obj))
++ else:
++ raise Exception('Invalid schema object: %s' % obj)
++
++ def _AddPolicyKey(self, item, item_type, parent_item, key, depth):
++ '''Given a policy/enumeration item and a key, adds that key and its value
++ into the output.
++ E.g.:
++ 'example_value': 123
++ If key indicates that the value is a translatable string, then it is parsed
++ as a translatable string.
++
++ Args:
++ item: A policy or an enumeration item.
++ item_type: 'enum_item' | 'policy'
++ parent_item: The owner of item. (A policy of type group or enum.)
++ key: The name of the key to parse.
++ depth: The level of indentation.
++ '''
++ self._AddIndentedNontranslateableChunk(depth, "%s: " % json.dumps(key))
++ if key in ('desc', 'caption', 'label', 'arc_support'):
++ self._AddNontranslateableChunk("\"")
++ self._ParseMessage(
++ item[key],
++ self._GetDescription(item, item_type, parent_item, key))
++ self._AddNontranslateableChunk("\"")
++ elif key in ('schema', 'validation_schema', 'description_schema'):
++ self._AddSchemaKeys(item[key], depth)
++ else:
++ self._AddNontranslateableChunk(json.dumps(item[key], ensure_ascii=False))
++
++ def _AddItems(self, items, item_type, parent_item, depth):
++ '''Parses and adds a list of items from the JSON file. Items can be policies
++ or parts of an enum policy.
++
++ Args:
++ items: Either a list of policies or a list of dictionaries.
++ item_type: 'enum_item' | 'policy'
++ parent_item: If items contains a list of policies, then this is the policy
++ group that owns them. If items contains a list of enumeration items,
++ then this is the enum policy that holds them.
++ depth: Indicates the depth of our position in the JSON hierarchy. Used to
++ add nice line-indent to the output.
++ '''
++ for item_count, (item1) in enumerate(items, 1):
++ self._AddIndentedNontranslateableChunk(depth, "{\n")
++ keys = sorted(item1.keys())
++ for keys_count, (key) in enumerate(keys, 1):
++ if key == 'items':
++ self._AddIndentedNontranslateableChunk(depth + 1, "\"items\": [\n")
++ self._AddItems(item1['items'], 'enum_item', item1, depth + 2)
++ self._AddIndentedNontranslateableChunk(depth + 1, "]")
++ elif key == 'policies' and all(not isinstance(x, str)
++ for x in item1['policies']):
++ self._AddIndentedNontranslateableChunk(depth + 1, "\"policies\": [\n")
++ self._AddItems(item1['policies'], 'policy', item1, depth + 2)
++ self._AddIndentedNontranslateableChunk(depth + 1, "]")
++ else:
++ self._AddPolicyKey(item1, item_type, parent_item, key, depth + 1)
++ self._AddEndline(keys_count < len(keys))
++ self._AddIndentedNontranslateableChunk(depth, "}")
++ self._AddEndline(item_count < len(items))
++
++ def _AddMessages(self):
++ '''Processed and adds the 'messages' section to the output.'''
++ self._AddNontranslateableChunk(" \"messages\": {\n")
++ messages = self.data['messages'].items()
++ for count, (name, message) in enumerate(messages, 1):
++ self._AddNontranslateableChunk(" %s: {\n" % json.dumps(name))
++ self._AddNontranslateableChunk(" \"text\": \"")
++ self._ParseMessage(message['text'], message['desc'])
++ self._AddNontranslateableChunk("\"\n")
++ self._AddNontranslateableChunk(" }")
++ self._AddEndline(count < len(self.data['messages']))
++ self._AddNontranslateableChunk(" }\n")
++
++ # Although we use the RegexpGatherer base class, we do not use the
++ # _RegExpParse method of that class to implement Parse(). Instead, we
++ # parse using a DOM parser.
++ def Parse(self):
++ if self.have_parsed_:
++ return
++ self.have_parsed_ = True
++
++ self.text_ = self._LoadInputFile()
++ if util.IsExtraVerbose():
++ print(self.text_)
++
++ self.data = eval(self.text_)
++
++ self._AddNontranslateableChunk('{\n')
++ self._AddNontranslateableChunk(" \"policy_definitions\": [\n")
++ self._AddItems(self.data['policy_definitions'], 'policy', None, 2)
++ self._AddNontranslateableChunk(" ],\n")
++ self._AddNontranslateableChunk(" \"policy_atomic_group_definitions\": [\n")
++ if 'policy_atomic_group_definitions' in self.data:
++ self._AddItems(self.data['policy_atomic_group_definitions'],
++ 'policy', None, 2)
++ self._AddNontranslateableChunk(" ],\n")
++ self._AddMessages()
++ self._AddNontranslateableChunk('\n}')
++
++ def Escape(self, text):
++ return json.dumps(text, ensure_ascii=False)[1:-1]
++
++ def SetDefines(self, defines):
++ if not defines:
++ raise Exception('Must pass valid defines')
++
++ if '_chromium' in defines:
++ self._config = {
++ 'build': 'chromium',
++ 'app_name': 'Chromium',
++ 'frame_name': 'Chromium Frame',
++ 'os_name': 'Chromium OS',
+ }
++ elif '_google_chrome' in defines:
++ self._config = {
++ 'build': 'chrome',
++ 'app_name': 'Google Chrome',
++ 'frame_name': 'Google Chrome Frame',
++ 'os_name': 'Google Chrome OS',
++ }
++ else:
++ raise Exception('Unknown build')
+diff --git a/tools/grit/grit/gather/policy_json_unittest.py b/tools/grit/grit/gather/policy_json_unittest.py
+new file mode 100644
+index 0000000000..214cd276aa
+--- /dev/null
++++ b/tools/grit/grit/gather/policy_json_unittest.py
+@@ -0,0 +1,347 @@
++#!/usr/bin/env python
++# Copyright (c) 2011 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++'''Unit tests for grit.gather.policy_json'''
++
++from __future__ import print_function
++
++import json
++import os
++import re
++import sys
++if __name__ == '__main__':
++ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
++
++import unittest
++
++from six import StringIO
++
++from grit.gather import policy_json
++
++class PolicyJsonUnittest(unittest.TestCase):
++
++ def GetExpectedOutput(self, original):
++ expected = eval(original)
++ for key, message in expected['messages'].items():
++ del message['desc']
++ return expected
++
++ def testEmpty(self):
++ original = """{
++ 'policy_definitions': [],
++ 'policy_atomic_group_definitions': [],
++ 'messages': {}
++ }"""
++ gatherer = policy_json.PolicyJson(StringIO(original))
++ gatherer.Parse()
++ self.failUnless(len(gatherer.GetCliques()) == 0)
++ self.failUnless(eval(original) == json.loads(gatherer.Translate('en')))
++
++ def testGeneralPolicy(self):
++ original = (
++ "{"
++ " 'policy_definitions': ["
++ " {"
++ " 'name': 'HomepageLocation',"
++ " 'type': 'string',"
++ " 'owners': ['foo@bar.com'],"
++ " 'supported_on': ['chrome.*:8-'],"
++ " 'features': {'dynamic_refresh': 1},"
++ " 'example_value': 'http://chromium.org',"
++ " 'caption': 'nothing special 1',"
++ " 'desc': 'nothing special 2',"
++ " 'label': 'nothing special 3',"
++ " },"
++ " ],"
++ " 'policy_atomic_group_definitions': [],"
++ " 'messages': {"
++ " 'msg_identifier': {"
++ " 'text': 'nothing special 3',"
++ " 'desc': 'nothing special descr 3',"
++ " }"
++ " }"
++ "}")
++ gatherer = policy_json.PolicyJson(StringIO(original))
++ gatherer.Parse()
++ self.failUnless(len(gatherer.GetCliques()) == 4)
++ expected = self.GetExpectedOutput(original)
++ self.failUnless(expected == json.loads(gatherer.Translate('en')))
++
++ def testEnum(self):
++ original = (
++ "{"
++ " 'policy_definitions': ["
++ " {"
++ " 'name': 'Policy1',"
++ " 'owners': ['a@b'],"
++ " 'items': ["
++ " {"
++ " 'name': 'Item1',"
++ " 'caption': 'nothing special',"
++ " }"
++ " ]"
++ " },"
++ " ],"
++ " 'policy_atomic_group_definitions': [],"
++ " 'messages': {}"
++ "}")
++ gatherer = policy_json.PolicyJson(StringIO(original))
++ gatherer.Parse()
++ self.failUnless(len(gatherer.GetCliques()) == 1)
++ expected = self.GetExpectedOutput(original)
++ self.failUnless(expected == json.loads(gatherer.Translate('en')))
++
++ def testSchema(self):
++ original = ("{"
++ " 'policy_definitions': ["
++ " {"
++ " 'name': 'Policy1',"
++ " 'schema': {"
++ " 'type': 'object',"
++ " 'properties': {"
++ " 'outer': {"
++ " 'description': 'outer description',"
++ " 'type': 'object',"
++ " 'inner': {"
++ " 'description': 'inner description',"
++ " 'type': 'integer', 'minimum': 0, 'maximum': 100"
++ " },"
++ " 'inner2': {"
++ " 'description': 'inner2 description',"
++ " 'type': 'integer',"
++ " 'enum': [ 1, 2, 3 ],"
++ " 'sensitiveValue': True"
++ " },"
++ " },"
++ " },"
++ " },"
++ " 'caption': 'nothing special',"
++ " 'owners': ['a@b']"
++ " },"
++ " ],"
++ " 'policy_atomic_group_definitions': [],"
++ " 'messages': {}"
++ "}")
++ gatherer = policy_json.PolicyJson(StringIO(original))
++ gatherer.Parse()
++ self.failUnless(len(gatherer.GetCliques()) == 4)
++ expected = self.GetExpectedOutput(original)
++ self.failUnless(expected == json.loads(gatherer.Translate('en')))
++
++ def testValidationSchema(self):
++ original = ("{"
++ " 'policy_definitions': ["
++ " {"
++ " 'name': 'Policy1',"
++ " 'owners': ['a@b'],"
++ " 'validation_schema': {"
++ " 'type': 'object',"
++ " 'properties': {"
++ " 'description': 'properties description',"
++ " 'type': 'object',"
++ " },"
++ " },"
++ " },"
++ " ],"
++ " 'policy_atomic_group_definitions': [],"
++ " 'messages': {}"
++ "}")
++ gatherer = policy_json.PolicyJson(StringIO(original))
++ gatherer.Parse()
++ self.failUnless(len(gatherer.GetCliques()) == 1)
++ expected = self.GetExpectedOutput(original)
++ self.failUnless(expected == json.loads(gatherer.Translate('en')))
++
++ def testDescriptionSchema(self):
++ original = ("{"
++ " 'policy_definitions': ["
++ " {"
++ " 'name': 'Policy1',"
++ " 'owners': ['a@b'],"
++ " 'description_schema': {"
++ " 'type': 'object',"
++ " 'properties': {"
++ " 'description': 'properties description',"
++ " 'type': 'object',"
++ " },"
++ " },"
++ " },"
++ " ],"
++ " 'policy_atomic_group_definitions': [],"
++ " 'messages': {}"
++ "}")
++ gatherer = policy_json.PolicyJson(StringIO(original))
++ gatherer.Parse()
++ self.failUnless(len(gatherer.GetCliques()) == 1)
++ expected = self.GetExpectedOutput(original)
++ self.failUnless(expected == json.loads(gatherer.Translate('en')))
++
++ # Keeping for backwards compatibility.
++ def testSubPolicyOldFormat(self):
++ original = (
++ "{"
++ " 'policy_definitions': ["
++ " {"
++ " 'type': 'group',"
++ " 'policies': ["
++ " {"
++ " 'name': 'Policy1',"
++ " 'caption': 'nothing special',"
++ " 'owners': ['a@b']"
++ " }"
++ " ]"
++ " }"
++ " ],"
++ " 'policy_atomic_group_definitions': [],"
++ " 'messages': {}"
++ "}")
++ gatherer = policy_json.PolicyJson(StringIO(original))
++ gatherer.Parse()
++ self.failUnless(len(gatherer.GetCliques()) == 1)
++ expected = self.GetExpectedOutput(original)
++ self.failUnless(expected == json.loads(gatherer.Translate('en')))
++
++ def testSubPolicyNewFormat(self):
++ original = (
++ "{"
++ " 'policy_definitions': ["
++ " {"
++ " 'type': 'group',"
++ " 'policies': ['Policy1']"
++ " },"
++ " {"
++ " 'name': 'Policy1',"
++ " 'caption': 'nothing special',"
++ " 'owners': ['a@b']"
++ " }"
++ " ],"
++ " 'policy_atomic_group_definitions': [],"
++ " 'messages': {}"
++ "}")
++ gatherer = policy_json.PolicyJson(StringIO(original))
++ gatherer.Parse()
++ self.failUnless(len(gatherer.GetCliques()) == 1)
++ expected = self.GetExpectedOutput(original)
++ self.failUnless(expected == json.loads(gatherer.Translate('en')))
++
++ def testEscapingAndLineBreaks(self):
++ original = """{
++ 'policy_definitions': [],
++ 'policy_atomic_group_definitions': [],
++ 'messages': {
++ 'msg1': {
++ # The following line will contain two backslash characters when it
++ # ends up in eval().
++ 'text': '''backslashes, Sir? \\\\''',
++ 'desc': ''
++ },
++ 'msg2': {
++ 'text': '''quotes, Madam? "''',
++ 'desc': ''
++ },
++ 'msg3': {
++ # The following line will contain two backslash characters when it
++ # ends up in eval().
++ 'text': 'backslashes, Sir? \\\\',
++ 'desc': ''
++ },
++ 'msg4': {
++ 'text': "quotes, Madam? '",
++ 'desc': ''
++ },
++ 'msg5': {
++ 'text': '''what happens
++with a newline?''',
++ 'desc': ''
++ },
++ 'msg6': {
++ # The following line will contain a backslash+n when it ends up in
++ # eval().
++ 'text': 'what happens\\nwith a newline? (Episode 1)',
++ 'desc': ''
++ }
++ }
++}"""
++ gatherer = policy_json.PolicyJson(StringIO(original))
++ gatherer.Parse()
++ self.failUnless(len(gatherer.GetCliques()) == 6)
++ expected = self.GetExpectedOutput(original)
++ self.failUnless(expected == json.loads(gatherer.Translate('en')))
++
++ def testPlaceholdersChromium(self):
++ original = """{
++ "policy_definitions": [
++ {
++ "name": "Policy1",
++ "caption": "Please install\\n<ph name=\\"PRODUCT_NAME\\">$1<ex>Google Chrome</ex></ph>.",
++ "owners": "a@b"
++ }
++ ],
++ "policy_atomic_group_definitions": [],
++ "messages": {}
++}"""
++ gatherer = policy_json.PolicyJson(StringIO(original))
++ gatherer.SetDefines({'_chromium': True})
++ gatherer.Parse()
++ self.failUnless(len(gatherer.GetCliques()) == 1)
++ expected = json.loads(re.sub('<ph.*ph>', 'Chromium', original))
++ self.failUnless(expected == json.loads(gatherer.Translate('en')))
++ self.failUnless(gatherer.GetCliques()[0].translateable)
++ msg = gatherer.GetCliques()[0].GetMessage()
++ self.failUnless(len(msg.GetPlaceholders()) == 1)
++ ph = msg.GetPlaceholders()[0]
++ self.failUnless(ph.GetOriginal() == 'Chromium')
++ self.failUnless(ph.GetPresentation() == 'PRODUCT_NAME')
++ self.failUnless(ph.GetExample() == 'Google Chrome')
++
++ def testPlaceholdersChrome(self):
++ original = """{
++ "policy_definitions": [
++ {
++ "name": "Policy1",
++ "caption": "Please install\\n<ph name=\\"PRODUCT_NAME\\">$1<ex>Google Chrome</ex></ph>.",
++ "owners": "a@b"
++ }
++ ],
++ "policy_atomic_group_definitions": [],
++ "messages": {}
++}"""
++ gatherer = policy_json.PolicyJson(StringIO(original))
++ gatherer.SetDefines({'_google_chrome': True})
++ gatherer.Parse()
++ self.failUnless(len(gatherer.GetCliques()) == 1)
++ expected = json.loads(re.sub('<ph.*ph>', 'Google Chrome', original))
++ self.failUnless(expected == json.loads(gatherer.Translate('en')))
++ self.failUnless(gatherer.GetCliques()[0].translateable)
++ msg = gatherer.GetCliques()[0].GetMessage()
++ self.failUnless(len(msg.GetPlaceholders()) == 1)
++ ph = msg.GetPlaceholders()[0]
++ self.failUnless(ph.GetOriginal() == 'Google Chrome')
++ self.failUnless(ph.GetPresentation() == 'PRODUCT_NAME')
++ self.failUnless(ph.GetExample() == 'Google Chrome')
++
++ def testGetDescription(self):
++ gatherer = policy_json.PolicyJson({})
++ gatherer.SetDefines({'_google_chrome': True})
++ self.assertEquals(
++ gatherer._GetDescription({'name': 'Policy1', 'owners': ['a@b']},
++ 'policy', None, 'desc'),
++ 'Description of the policy named Policy1 [owner(s): a@b]')
++ self.assertEquals(
++ gatherer._GetDescription({'name': 'Plcy2', 'owners': ['a@b', 'c@d']},
++ 'policy', None, 'caption'),
++ 'Caption of the policy named Plcy2 [owner(s): a@b,c@d]')
++ self.assertEquals(
++ gatherer._GetDescription({'name': 'Plcy3', 'owners': ['a@b']},
++ 'policy', None, 'label'),
++ 'Label of the policy named Plcy3 [owner(s): a@b]')
++ self.assertEquals(
++ gatherer._GetDescription({'name': 'Item'}, 'enum_item',
++ {'name': 'Plcy', 'owners': ['a@b']}, 'caption'),
++ 'Caption of the option named Item in policy Plcy [owner(s): a@b]')
++
++
++if __name__ == '__main__':
++ unittest.main()
+diff --git a/tools/grit/grit/gather/rc.py b/tools/grit/grit/gather/rc.py
+new file mode 100644
+index 0000000000..dd091d1e18
+--- /dev/null
++++ b/tools/grit/grit/gather/rc.py
+@@ -0,0 +1,343 @@
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++'''Support for gathering resources from RC files.
++'''
++
++from __future__ import print_function
++
++import re
++
++from grit import exception
++from grit import lazy_re
++from grit import tclib
++
++from grit.gather import regexp
++
++
++# Find portions that need unescaping in resource strings. We need to be
++# careful that a \\n is matched _first_ as a \\ rather than matching as
++# a \ followed by a \n.
++# TODO(joi) Handle ampersands if we decide to change them into <ph>
++# TODO(joi) May need to handle other control characters than \n
++_NEED_UNESCAPE = lazy_re.compile(r'""|\\\\|\\n|\\t')
++
++# Find portions that need escaping to encode string as a resource string.
++_NEED_ESCAPE = lazy_re.compile(r'"|\n|\t|\\|\&nbsp\;')
++
++# How to escape certain characters
++_ESCAPE_CHARS = {
++ '"' : '""',
++ '\n' : '\\n',
++ '\t' : '\\t',
++ '\\' : '\\\\',
++ '&nbsp;' : ' '
++}
++
++# How to unescape certain strings
++_UNESCAPE_CHARS = dict([[value, key] for key, value in _ESCAPE_CHARS.items()])
++
++
++
++class Section(regexp.RegexpGatherer):
++ '''A section from a resource file.'''
++
++ @staticmethod
++ def Escape(text):
++ '''Returns a version of 'text' with characters escaped that need to be
++ for inclusion in a resource section.'''
++ def Replace(match):
++ return _ESCAPE_CHARS[match.group()]
++ return _NEED_ESCAPE.sub(Replace, text)
++
++ @staticmethod
++ def UnEscape(text):
++ '''Returns a version of 'text' with escaped characters unescaped.'''
++ def Replace(match):
++ return _UNESCAPE_CHARS[match.group()]
++ return _NEED_UNESCAPE.sub(Replace, text)
++
++ def _RegExpParse(self, rexp, text_to_parse):
++ '''Overrides _RegExpParse to add shortcut group handling. Otherwise
++ the same.
++ '''
++ super(Section, self)._RegExpParse(rexp, text_to_parse)
++
++ if not self.is_skeleton and len(self.GetTextualIds()) > 0:
++ group_name = self.GetTextualIds()[0]
++ for c in self.GetCliques():
++ c.AddToShortcutGroup(group_name)
++
++ def ReadSection(self):
++ rc_text = self._LoadInputFile()
++
++ out = ''
++ begin_count = 0
++ assert self.extkey
++ first_line_re = re.compile(r'\s*' + self.extkey + r'\b')
++ for line in rc_text.splitlines(True):
++ if out or first_line_re.match(line):
++ out += line
++
++ # we stop once we reach the END for the outermost block.
++ begin_count_was = begin_count
++ if len(out) > 0 and line.strip() == 'BEGIN':
++ begin_count += 1
++ elif len(out) > 0 and line.strip() == 'END':
++ begin_count -= 1
++ if begin_count_was == 1 and begin_count == 0:
++ break
++
++ if len(out) == 0:
++ raise exception.SectionNotFound('%s in file %s' % (self.extkey, self.rc_file))
++
++ self.text_ = out.strip()
++
++
++class Dialog(Section):
++ '''A resource section that contains a dialog resource.'''
++
++ # A typical dialog resource section looks like this:
++ #
++ # IDD_ABOUTBOX DIALOGEX 22, 17, 230, 75
++ # STYLE DS_SETFONT | DS_MODALFRAME | WS_CAPTION | WS_SYSMENU
++ # CAPTION "About"
++ # FONT 8, "System", 0, 0, 0x0
++ # BEGIN
++ # ICON IDI_KLONK,IDC_MYICON,14,9,20,20
++ # LTEXT "klonk Version ""yibbee"" 1.0",IDC_STATIC,49,10,119,8,
++ # SS_NOPREFIX
++ # LTEXT "Copyright (C) 2005",IDC_STATIC,49,20,119,8
++ # DEFPUSHBUTTON "OK",IDOK,195,6,30,11,WS_GROUP
++ # CONTROL "Jack ""Black"" Daniels",IDC_RADIO1,"Button",
++ # BS_AUTORADIOBUTTON,46,51,84,10
++ # END
++
++ # We are using a sorted set of keys, and we assume that the
++ # group name used for descriptions (type) will come after the "text"
++ # group in alphabetical order. We also assume that there cannot be
++ # more than one description per regular expression match.
++ # If that's not the case some descriptions will be clobbered.
++ dialog_re_ = lazy_re.compile(r'''
++ # The dialog's ID in the first line
++ (?P<id1>[A-Z0-9_]+)\s+DIALOG(EX)?
++ |
++ # The caption of the dialog
++ (?P<type1>CAPTION)\s+"(?P<text1>.*?([^"]|""))"\s
++ |
++ # Lines for controls that have text and an ID
++ \s+(?P<type2>[A-Z]+)\s+"(?P<text2>.*?([^"]|"")?)"\s*,\s*(?P<id2>[A-Z0-9_]+)\s*,
++ |
++ # Lines for controls that have text only
++ \s+(?P<type3>[A-Z]+)\s+"(?P<text3>.*?([^"]|"")?)"\s*,
++ |
++ # Lines for controls that reference other resources
++ \s+[A-Z]+\s+[A-Z0-9_]+\s*,\s*(?P<id3>[A-Z0-9_]*[A-Z][A-Z0-9_]*)
++ |
++ # This matches "NOT SOME_STYLE" so that it gets consumed and doesn't get
++ # matched by the next option (controls that have only an ID and then just
++ # numbers)
++ \s+NOT\s+[A-Z][A-Z0-9_]+
++ |
++ # Lines for controls that have only an ID and then just numbers
++ \s+[A-Z]+\s+(?P<id4>[A-Z0-9_]*[A-Z][A-Z0-9_]*)\s*,
++ ''', re.MULTILINE | re.VERBOSE)
++
++ def Parse(self):
++ '''Knows how to parse dialog resource sections.'''
++ self.ReadSection()
++ self._RegExpParse(self.dialog_re_, self.text_)
++
++
++class Menu(Section):
++ '''A resource section that contains a menu resource.'''
++
++ # A typical menu resource section looks something like this:
++ #
++ # IDC_KLONK MENU
++ # BEGIN
++ # POPUP "&File"
++ # BEGIN
++ # MENUITEM "E&xit", IDM_EXIT
++ # MENUITEM "This be ""Klonk"" me like", ID_FILE_THISBE
++ # POPUP "gonk"
++ # BEGIN
++ # MENUITEM "Klonk && is ""good""", ID_GONK_KLONKIS
++ # END
++ # END
++ # POPUP "&Help"
++ # BEGIN
++ # MENUITEM "&About ...", IDM_ABOUT
++ # END
++ # END
++
++ # Description used for the messages generated for menus, to explain to
++ # the translators how to handle them.
++ MENU_MESSAGE_DESCRIPTION = (
++ 'This message represents a menu. Each of the items appears in sequence '
++ '(some possibly within sub-menus) in the menu. The XX01XX placeholders '
++ 'serve to separate items. Each item contains an & (ampersand) character '
++ 'in front of the keystroke that should be used as a shortcut for that item '
++ 'in the menu. Please make sure that no two items in the same menu share '
++ 'the same shortcut.'
++ )
++
++ # A dandy regexp to suck all the IDs and translateables out of a menu
++ # resource
++ menu_re_ = lazy_re.compile(r'''
++ # Match the MENU ID on the first line
++ ^(?P<id1>[A-Z0-9_]+)\s+MENU
++ |
++ # Match the translateable caption for a popup menu
++ POPUP\s+"(?P<text1>.*?([^"]|""))"\s
++ |
++ # Match the caption & ID of a MENUITEM
++ MENUITEM\s+"(?P<text2>.*?([^"]|""))"\s*,\s*(?P<id2>[A-Z0-9_]+)
++ ''', re.MULTILINE | re.VERBOSE)
++
++ def Parse(self):
++ '''Knows how to parse menu resource sections. Because it is important that
++ menu shortcuts are unique within the menu, we return each menu as a single
++ message with placeholders to break up the different menu items, rather than
++ return a single message per menu item. we also add an automatic description
++ with instructions for the translators.'''
++ self.ReadSection()
++ self.single_message_ = tclib.Message(description=self.MENU_MESSAGE_DESCRIPTION)
++ self._RegExpParse(self.menu_re_, self.text_)
++
++
++class Version(Section):
++ '''A resource section that contains a VERSIONINFO resource.'''
++
++ # A typical version info resource can look like this:
++ #
++ # VS_VERSION_INFO VERSIONINFO
++ # FILEVERSION 1,0,0,1
++ # PRODUCTVERSION 1,0,0,1
++ # FILEFLAGSMASK 0x3fL
++ # #ifdef _DEBUG
++ # FILEFLAGS 0x1L
++ # #else
++ # FILEFLAGS 0x0L
++ # #endif
++ # FILEOS 0x4L
++ # FILETYPE 0x2L
++ # FILESUBTYPE 0x0L
++ # BEGIN
++ # BLOCK "StringFileInfo"
++ # BEGIN
++ # BLOCK "040904e4"
++ # BEGIN
++ # VALUE "CompanyName", "TODO: <Company name>"
++ # VALUE "FileDescription", "TODO: <File description>"
++ # VALUE "FileVersion", "1.0.0.1"
++ # VALUE "LegalCopyright", "TODO: (c) <Company name>. All rights reserved."
++ # VALUE "InternalName", "res_format_test.dll"
++ # VALUE "OriginalFilename", "res_format_test.dll"
++ # VALUE "ProductName", "TODO: <Product name>"
++ # VALUE "ProductVersion", "1.0.0.1"
++ # END
++ # END
++ # BLOCK "VarFileInfo"
++ # BEGIN
++ # VALUE "Translation", 0x409, 1252
++ # END
++ # END
++ #
++ #
++ # In addition to the above fields, VALUE fields named "Comments" and
++ # "LegalTrademarks" may also be translateable.
++
++ version_re_ = lazy_re.compile(r'''
++ # Match the ID on the first line
++ ^(?P<id1>[A-Z0-9_]+)\s+VERSIONINFO
++ |
++ # Match all potentially translateable VALUE sections
++ \s+VALUE\s+"
++ (
++ CompanyName|FileDescription|LegalCopyright|
++ ProductName|Comments|LegalTrademarks
++ )",\s+"(?P<text1>.*?([^"]|""))"\s
++ ''', re.MULTILINE | re.VERBOSE)
++
++ def Parse(self):
++ '''Knows how to parse VERSIONINFO resource sections.'''
++ self.ReadSection()
++ self._RegExpParse(self.version_re_, self.text_)
++
++ # TODO(joi) May need to override the Translate() method to change the
++ # "Translation" VALUE block to indicate the correct language code.
++
++
++class RCData(Section):
++ '''A resource section that contains some data .'''
++
++ # A typical rcdataresource section looks like this:
++ #
++ # IDR_BLAH RCDATA { 1, 2, 3, 4 }
++
++ dialog_re_ = lazy_re.compile(r'''
++ ^(?P<id1>[A-Z0-9_]+)\s+RCDATA\s+(DISCARDABLE)?\s+\{.*?\}
++ ''', re.MULTILINE | re.VERBOSE | re.DOTALL)
++
++ def Parse(self):
++ '''Implementation for resource types w/braces (not BEGIN/END)
++ '''
++ rc_text = self._LoadInputFile()
++
++ out = ''
++ begin_count = 0
++ openbrace_count = 0
++ assert self.extkey
++ first_line_re = re.compile(r'\s*' + self.extkey + r'\b')
++ for line in rc_text.splitlines(True):
++ if out or first_line_re.match(line):
++ out += line
++
++ # We stop once the braces balance (could happen in one line).
++ begin_count_was = begin_count
++ if len(out) > 0:
++ openbrace_count += line.count('{')
++ begin_count += line.count('{')
++ begin_count -= line.count('}')
++ if ((begin_count_was == 1 and begin_count == 0) or
++ (openbrace_count > 0 and begin_count == 0)):
++ break
++
++ if len(out) == 0:
++ raise exception.SectionNotFound('%s in file %s' % (self.extkey, self.rc_file))
++
++ self.text_ = out
++
++ self._RegExpParse(self.dialog_re_, out)
++
++
++class Accelerators(Section):
++ '''An ACCELERATORS table.
++ '''
++
++ # A typical ACCELERATORS section looks like this:
++ #
++ # IDR_ACCELERATOR1 ACCELERATORS
++ # BEGIN
++ # "^C", ID_ACCELERATOR32770, ASCII, NOINVERT
++ # "^V", ID_ACCELERATOR32771, ASCII, NOINVERT
++ # VK_INSERT, ID_ACCELERATOR32772, VIRTKEY, CONTROL, NOINVERT
++ # END
++
++ accelerators_re_ = lazy_re.compile(r'''
++ # Match the ID on the first line
++ ^(?P<id1>[A-Z0-9_]+)\s+ACCELERATORS\s+
++ |
++ # Match accelerators specified as VK_XXX
++ \s+VK_[A-Z0-9_]+,\s*(?P<id2>[A-Z0-9_]+)\s*,
++ |
++ # Match accelerators specified as e.g. "^C"
++ \s+"[^"]*",\s+(?P<id3>[A-Z0-9_]+)\s*,
++ ''', re.MULTILINE | re.VERBOSE)
++
++ def Parse(self):
++ '''Knows how to parse ACCELERATORS resource sections.'''
++ self.ReadSection()
++ self._RegExpParse(self.accelerators_re_, self.text_)
+diff --git a/tools/grit/grit/gather/rc_unittest.py b/tools/grit/grit/gather/rc_unittest.py
+new file mode 100644
+index 0000000000..3c26a4342a
+--- /dev/null
++++ b/tools/grit/grit/gather/rc_unittest.py
+@@ -0,0 +1,372 @@
++#!/usr/bin/env python
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++'''Unit tests for grit.gather.rc'''
++
++from __future__ import print_function
++
++import os
++import sys
++if __name__ == '__main__':
++ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
++
++import unittest
++
++from six import StringIO
++
++from grit.gather import rc
++from grit import util
++
++
++class RcUnittest(unittest.TestCase):
++
++ part_we_want = '''IDC_KLONKACC ACCELERATORS
++BEGIN
++ "?", IDM_ABOUT, ASCII, ALT
++ "/", IDM_ABOUT, ASCII, ALT
++END'''
++
++ def testSectionFromFile(self):
++ buf = '''IDC_SOMETHINGELSE BINGO
++BEGIN
++ BLA BLA
++ BLA BLA
++END
++%s
++
++IDC_KLONK BINGOBONGO
++BEGIN
++ HONGO KONGO
++END
++''' % self.part_we_want
++
++ f = StringIO(buf)
++
++ out = rc.Section(f, 'IDC_KLONKACC')
++ out.ReadSection()
++ self.failUnless(out.GetText() == self.part_we_want)
++
++ out = rc.Section(util.PathFromRoot(r'grit/testdata/klonk.rc'),
++ 'IDC_KLONKACC',
++ encoding='utf-16')
++ out.ReadSection()
++ out_text = out.GetText().replace('\t', '')
++ out_text = out_text.replace(' ', '')
++ self.part_we_want = self.part_we_want.replace(' ', '')
++ self.failUnless(out_text.strip() == self.part_we_want.strip())
++
++
++ def testDialog(self):
++ dlg = rc.Dialog(StringIO('''IDD_ABOUTBOX DIALOGEX 22, 17, 230, 75
++STYLE DS_SETFONT | DS_MODALFRAME | WS_CAPTION | WS_SYSMENU
++CAPTION "About"
++FONT 8, "System", 0, 0, 0x0
++BEGIN
++ ICON IDI_KLONK,IDC_MYICON,14,9,20,20
++ LTEXT "klonk Version ""yibbee"" 1.0",IDC_STATIC,49,10,119,8,
++ SS_NOPREFIX
++ LTEXT "Copyright (C) 2005",IDC_STATIC,49,20,119,8
++ DEFPUSHBUTTON "OK",IDOK,195,6,30,11,WS_GROUP
++ CONTROL "Jack ""Black"" Daniels",IDC_RADIO1,"Button",
++ BS_AUTORADIOBUTTON,46,51,84,10
++ // try a line where the ID is on the continuation line
++ LTEXT "blablablabla blablabla blablablablablablablabla blablabla",
++ ID_SMURF, whatever...
++END
++'''), 'IDD_ABOUTBOX')
++ dlg.Parse()
++ self.failUnless(len(dlg.GetTextualIds()) == 7)
++ self.failUnless(len(dlg.GetCliques()) == 6)
++ self.failUnless(dlg.GetCliques()[1].GetMessage().GetRealContent() ==
++ 'klonk Version "yibbee" 1.0')
++
++ transl = dlg.Translate('en')
++ self.failUnless(transl.strip() == dlg.GetText().strip())
++
++ def testAlternateSkeleton(self):
++ dlg = rc.Dialog(StringIO('''IDD_ABOUTBOX DIALOGEX 22, 17, 230, 75
++STYLE DS_SETFONT | DS_MODALFRAME | WS_CAPTION | WS_SYSMENU
++CAPTION "About"
++FONT 8, "System", 0, 0, 0x0
++BEGIN
++ LTEXT "Yipee skippy",IDC_STATIC,49,10,119,8,
++ SS_NOPREFIX
++END
++'''), 'IDD_ABOUTBOX')
++ dlg.Parse()
++
++ alt_dlg = rc.Dialog(StringIO('''IDD_ABOUTBOX DIALOGEX 040704, 17, 230, 75
++STYLE DS_SETFONT | DS_MODALFRAME | WS_CAPTION | WS_SYSMENU
++CAPTION "XXXXXXXXX"
++FONT 8, "System", 0, 0, 0x0
++BEGIN
++ LTEXT "XXXXXXXXXXXXXXXXX",IDC_STATIC,110978,10,119,8,
++ SS_NOPREFIX
++END
++'''), 'IDD_ABOUTBOX')
++ alt_dlg.Parse()
++
++ transl = dlg.Translate('en', skeleton_gatherer=alt_dlg)
++ self.failUnless(transl.count('040704') and
++ transl.count('110978'))
++ self.failUnless(transl.count('Yipee skippy'))
++
++ def testMenu(self):
++ menu = rc.Menu(StringIO('''IDC_KLONK MENU
++BEGIN
++ POPUP "&File """
++ BEGIN
++ MENUITEM "E&xit", IDM_EXIT
++ MENUITEM "This be ""Klonk"" me like", ID_FILE_THISBE
++ POPUP "gonk"
++ BEGIN
++ MENUITEM "Klonk && is ""good""", ID_GONK_KLONKIS
++ END
++ MENUITEM "This is a very long menu caption to try to see if we can make the ID go to a continuation line, blablabla blablabla bla blabla blablabla blablabla blablabla blablabla...",
++ ID_FILE_THISISAVERYLONGMENUCAPTIONTOTRYTOSEEIFWECANMAKETHEIDGOTOACONTINUATIONLINE
++ END
++ POPUP "&Help"
++ BEGIN
++ MENUITEM "&About ...", IDM_ABOUT
++ END
++END'''), 'IDC_KLONK')
++
++ menu.Parse()
++ self.failUnless(len(menu.GetTextualIds()) == 6)
++ self.failUnless(len(menu.GetCliques()) == 1)
++ self.failUnless(len(menu.GetCliques()[0].GetMessage().GetPlaceholders()) ==
++ 9)
++
++ transl = menu.Translate('en')
++ self.failUnless(transl.strip() == menu.GetText().strip())
++
++ def testVersion(self):
++ version = rc.Version(StringIO('''
++VS_VERSION_INFO VERSIONINFO
++ FILEVERSION 1,0,0,1
++ PRODUCTVERSION 1,0,0,1
++ FILEFLAGSMASK 0x3fL
++#ifdef _DEBUG
++ FILEFLAGS 0x1L
++#else
++ FILEFLAGS 0x0L
++#endif
++ FILEOS 0x4L
++ FILETYPE 0x2L
++ FILESUBTYPE 0x0L
++BEGIN
++ BLOCK "StringFileInfo"
++ BEGIN
++ BLOCK "040904e4"
++ BEGIN
++ VALUE "CompanyName", "TODO: <Company name>"
++ VALUE "FileDescription", "TODO: <File description>"
++ VALUE "FileVersion", "1.0.0.1"
++ VALUE "LegalCopyright", "TODO: (c) <Company name>. All rights reserved."
++ VALUE "InternalName", "res_format_test.dll"
++ VALUE "OriginalFilename", "res_format_test.dll"
++ VALUE "ProductName", "TODO: <Product name>"
++ VALUE "ProductVersion", "1.0.0.1"
++ END
++ END
++ BLOCK "VarFileInfo"
++ BEGIN
++ VALUE "Translation", 0x409, 1252
++ END
++END
++'''.strip()), 'VS_VERSION_INFO')
++ version.Parse()
++ self.failUnless(len(version.GetTextualIds()) == 1)
++ self.failUnless(len(version.GetCliques()) == 4)
++
++ transl = version.Translate('en')
++ self.failUnless(transl.strip() == version.GetText().strip())
++
++
++ def testRegressionDialogBox(self):
++ dialog = rc.Dialog(StringIO('''
++IDD_SIDEBAR_WEATHER_PANEL_PROPPAGE DIALOGEX 0, 0, 205, 157
++STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD
++FONT 8, "MS Shell Dlg", 400, 0, 0x1
++BEGIN
++ EDITTEXT IDC_SIDEBAR_WEATHER_NEW_CITY,3,27,112,14,ES_AUTOHSCROLL
++ DEFPUSHBUTTON "Add Location",IDC_SIDEBAR_WEATHER_ADD,119,27,50,14
++ LISTBOX IDC_SIDEBAR_WEATHER_CURR_CITIES,3,48,127,89,
++ LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP
++ PUSHBUTTON "Move Up",IDC_SIDEBAR_WEATHER_MOVE_UP,134,104,50,14
++ PUSHBUTTON "Move Down",IDC_SIDEBAR_WEATHER_MOVE_DOWN,134,121,50,14
++ PUSHBUTTON "Remove",IDC_SIDEBAR_WEATHER_DELETE,134,48,50,14
++ LTEXT "To see current weather conditions and forecasts in the USA, enter the zip code (example: 94043) or city and state (example: Mountain View, CA).",
++ IDC_STATIC,3,0,199,25
++ CONTROL "Fahrenheit",IDC_SIDEBAR_WEATHER_FAHRENHEIT,"Button",
++ BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,3,144,51,10
++ CONTROL "Celsius",IDC_SIDEBAR_WEATHER_CELSIUS,"Button",
++ BS_AUTORADIOBUTTON,57,144,38,10
++END'''.strip()), 'IDD_SIDEBAR_WEATHER_PANEL_PROPPAGE')
++ dialog.Parse()
++ self.failUnless(len(dialog.GetTextualIds()) == 10)
++
++
++ def testRegressionDialogBox2(self):
++ dialog = rc.Dialog(StringIO('''
++IDD_SIDEBAR_EMAIL_PANEL_PROPPAGE DIALOG DISCARDABLE 0, 0, 264, 220
++STYLE WS_CHILD
++FONT 8, "MS Shell Dlg"
++BEGIN
++ GROUPBOX "Email Filters",IDC_STATIC,7,3,250,190
++ LTEXT "Click Add Filter to create the email filter.",IDC_STATIC,16,41,130,9
++ PUSHBUTTON "Add Filter...",IDC_SIDEBAR_EMAIL_ADD_FILTER,196,38,50,14
++ PUSHBUTTON "Remove",IDC_SIDEBAR_EMAIL_REMOVE,196,174,50,14
++ PUSHBUTTON "", IDC_SIDEBAR_EMAIL_HIDDEN, 200, 178, 5, 5, NOT WS_VISIBLE
++ LISTBOX IDC_SIDEBAR_EMAIL_LIST,16,60,230,108,
++ LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP
++ LTEXT "You can prevent certain emails from showing up in the sidebar with a filter.",
++ IDC_STATIC,16,18,234,18
++END'''.strip()), 'IDD_SIDEBAR_EMAIL_PANEL_PROPPAGE')
++ dialog.Parse()
++ self.failUnless('IDC_SIDEBAR_EMAIL_HIDDEN' in dialog.GetTextualIds())
++
++
++ def testRegressionMenuId(self):
++ menu = rc.Menu(StringIO('''
++IDR_HYPERMENU_FOLDER MENU
++BEGIN
++ POPUP "HyperFolder"
++ BEGIN
++ MENUITEM "Open Containing Folder", IDM_OPENFOLDER
++ END
++END'''.strip()), 'IDR_HYPERMENU_FOLDER')
++ menu.Parse()
++ self.failUnless(len(menu.GetTextualIds()) == 2)
++
++ def testRegressionNewlines(self):
++ menu = rc.Menu(StringIO('''
++IDR_HYPERMENU_FOLDER MENU
++BEGIN
++ POPUP "Hyper\\nFolder"
++ BEGIN
++ MENUITEM "Open Containing Folder", IDM_OPENFOLDER
++ END
++END'''.strip()), 'IDR_HYPERMENU_FOLDER')
++ menu.Parse()
++ transl = menu.Translate('en')
++ # Shouldn't find \\n (the \n shouldn't be changed to \\n)
++ self.failUnless(transl.find('\\\\n') == -1)
++
++ def testRegressionTabs(self):
++ menu = rc.Menu(StringIO('''
++IDR_HYPERMENU_FOLDER MENU
++BEGIN
++ POPUP "Hyper\\tFolder"
++ BEGIN
++ MENUITEM "Open Containing Folder", IDM_OPENFOLDER
++ END
++END'''.strip()), 'IDR_HYPERMENU_FOLDER')
++ menu.Parse()
++ transl = menu.Translate('en')
++ # Shouldn't find \\t (the \t shouldn't be changed to \\t)
++ self.failUnless(transl.find('\\\\t') == -1)
++
++ def testEscapeUnescape(self):
++ original = 'Hello "bingo"\n How\\are\\you\\n?'
++ escaped = rc.Section.Escape(original)
++ self.failUnless(escaped == 'Hello ""bingo""\\n How\\\\are\\\\you\\\\n?')
++ unescaped = rc.Section.UnEscape(escaped)
++ self.failUnless(unescaped == original)
++
++ def testRegressionPathsWithSlashN(self):
++ original = '..\\\\..\\\\trs\\\\res\\\\nav_first.gif'
++ unescaped = rc.Section.UnEscape(original)
++ self.failUnless(unescaped == '..\\..\\trs\\res\\nav_first.gif')
++
++ def testRegressionDialogItemsTextOnly(self):
++ dialog = rc.Dialog(StringIO('''IDD_OPTIONS_SEARCH DIALOGEX 0, 0, 280, 292
++STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP |
++ WS_DISABLED | WS_CAPTION | WS_SYSMENU
++CAPTION "Search"
++FONT 8, "MS Shell Dlg", 400, 0, 0x1
++BEGIN
++ GROUPBOX "Select search buttons and options",-1,7,5,266,262
++ CONTROL "",IDC_OPTIONS,"SysTreeView32",TVS_DISABLEDRAGDROP |
++ WS_BORDER | WS_TABSTOP | 0x800,16,19,248,218
++ LTEXT "Use Google site:",-1,26,248,52,8
++ COMBOBOX IDC_GOOGLE_HOME,87,245,177,256,CBS_DROPDOWNLIST |
++ WS_VSCROLL | WS_TABSTOP
++ PUSHBUTTON "Restore Defaults...",IDC_RESET,187,272,86,14
++END'''), 'IDD_OPTIONS_SEARCH')
++ dialog.Parse()
++ translateables = [c.GetMessage().GetRealContent()
++ for c in dialog.GetCliques()]
++ self.failUnless('Select search buttons and options' in translateables)
++ self.failUnless('Use Google site:' in translateables)
++
++ def testAccelerators(self):
++ acc = rc.Accelerators(StringIO('''\
++IDR_ACCELERATOR1 ACCELERATORS
++BEGIN
++ "^C", ID_ACCELERATOR32770, ASCII, NOINVERT
++ "^V", ID_ACCELERATOR32771, ASCII, NOINVERT
++ VK_INSERT, ID_ACCELERATOR32772, VIRTKEY, CONTROL, NOINVERT
++END
++'''), 'IDR_ACCELERATOR1')
++ acc.Parse()
++ self.failUnless(len(acc.GetTextualIds()) == 4)
++ self.failUnless(len(acc.GetCliques()) == 0)
++
++ transl = acc.Translate('en')
++ self.failUnless(transl.strip() == acc.GetText().strip())
++
++
++ def testRegressionEmptyString(self):
++ dlg = rc.Dialog(StringIO('''\
++IDD_CONFIRM_QUIT_GD_DLG DIALOGEX 0, 0, 267, 108
++STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP |
++ WS_CAPTION
++EXSTYLE WS_EX_TOPMOST
++CAPTION "Google Desktop"
++FONT 8, "MS Shell Dlg", 400, 0, 0x1
++BEGIN
++ DEFPUSHBUTTON "&Yes",IDYES,82,87,50,14
++ PUSHBUTTON "&No",IDNO,136,87,50,14
++ ICON 32514,IDC_STATIC,7,9,21,20
++ EDITTEXT IDC_TEXTBOX,34,7,231,60,ES_MULTILINE | ES_READONLY | NOT WS_BORDER
++ CONTROL "",
++ IDC_ENABLE_GD_AUTOSTART,"Button",BS_AUTOCHECKBOX |
++ WS_TABSTOP,33,70,231,10
++END'''), 'IDD_CONFIRM_QUIT_GD_DLG')
++ dlg.Parse()
++
++ def Check():
++ self.failUnless(transl.count('IDC_ENABLE_GD_AUTOSTART'))
++ self.failUnless(transl.count('END'))
++
++ transl = dlg.Translate('de', pseudo_if_not_available=True,
++ fallback_to_english=True)
++ Check()
++ transl = dlg.Translate('de', pseudo_if_not_available=True,
++ fallback_to_english=False)
++ Check()
++ transl = dlg.Translate('de', pseudo_if_not_available=False,
++ fallback_to_english=True)
++ Check()
++ transl = dlg.Translate('de', pseudo_if_not_available=False,
++ fallback_to_english=False)
++ Check()
++ transl = dlg.Translate('en', pseudo_if_not_available=True,
++ fallback_to_english=True)
++ Check()
++ transl = dlg.Translate('en', pseudo_if_not_available=True,
++ fallback_to_english=False)
++ Check()
++ transl = dlg.Translate('en', pseudo_if_not_available=False,
++ fallback_to_english=True)
++ Check()
++ transl = dlg.Translate('en', pseudo_if_not_available=False,
++ fallback_to_english=False)
++ Check()
++
++
++if __name__ == '__main__':
++ unittest.main()
+diff --git a/tools/grit/grit/gather/regexp.py b/tools/grit/grit/gather/regexp.py
+new file mode 100644
+index 0000000000..97ce2cfbf7
+--- /dev/null
++++ b/tools/grit/grit/gather/regexp.py
+@@ -0,0 +1,82 @@
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++'''A baseclass for simple gatherers based on regular expressions.
++'''
++
++from __future__ import print_function
++
++from grit.gather import skeleton_gatherer
++
++
++class RegexpGatherer(skeleton_gatherer.SkeletonGatherer):
++ '''Common functionality of gatherers based on parsing using a single
++ regular expression.
++ '''
++
++ DescriptionMapping_ = {
++ 'CAPTION' : 'This is a caption for a dialog',
++ 'CHECKBOX' : 'This is a label for a checkbox',
++ 'CONTROL': 'This is the text on a control',
++ 'CTEXT': 'This is a label for a control',
++ 'DEFPUSHBUTTON': 'This is a button definition',
++ 'GROUPBOX': 'This is a label for a grouping',
++ 'ICON': 'This is a label for an icon',
++ 'LTEXT': 'This is the text for a label',
++ 'PUSHBUTTON': 'This is the text for a button',
+ }
+
-+ return parsed;
-+ }
++ # Contextualization elements. Used for adding additional information
++ # to the message bundle description string from RC files.
++ def AddDescriptionElement(self, string):
++ if string in self.DescriptionMapping_:
++ description = self.DescriptionMapping_[string]
++ else:
++ description = string
++ if self.single_message_:
++ self.single_message_.SetDescription(description)
++ else:
++ if (self.translatable_chunk_):
++ message = self.skeleton_[len(self.skeleton_) - 1].GetMessage()
++ message.SetDescription(description)
++
++ def _RegExpParse(self, regexp, text_to_parse):
++ '''An implementation of Parse() that can be used for resource sections that
++ can be parsed using a single multi-line regular expression.
++
++ All translateables must be in named groups that have names starting with
++ 'text'. All textual IDs must be in named groups that have names starting
++ with 'id'. All type definitions that can be included in the description
++ field for contextualization purposes should have a name that starts with
++ 'type'.
++
++ Args:
++ regexp: re.compile('...', re.MULTILINE)
++ text_to_parse:
++ '''
++ chunk_start = 0
++ for match in regexp.finditer(text_to_parse):
++ groups = match.groupdict()
++ keys = sorted(groups.keys())
++ self.translatable_chunk_ = False
++ for group in keys:
++ if group.startswith('id') and groups[group]:
++ self._AddTextualId(groups[group])
++ elif group.startswith('text') and groups[group]:
++ self._AddNontranslateableChunk(
++ text_to_parse[chunk_start : match.start(group)])
++ chunk_start = match.end(group) # Next chunk will start after the match
++ self._AddTranslateableChunk(groups[group])
++ elif group.startswith('type') and groups[group]:
++ # Add the description to the skeleton_ list. This works because
++ # we are using a sort set of keys, and because we assume that the
++ # group name used for descriptions (type) will come after the "text"
++ # group in alphabetical order. We also assume that there cannot be
++ # more than one description per regular expression match.
++ self.AddDescriptionElement(groups[group])
++
++ self._AddNontranslateableChunk(text_to_parse[chunk_start:])
++
++ if self.single_message_:
++ self.skeleton_.append(self.uberclique.MakeClique(self.single_message_))
+diff --git a/tools/grit/grit/gather/skeleton_gatherer.py b/tools/grit/grit/gather/skeleton_gatherer.py
+new file mode 100644
+index 0000000000..b11862b314
+--- /dev/null
++++ b/tools/grit/grit/gather/skeleton_gatherer.py
+@@ -0,0 +1,149 @@
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
+
-+ private:
-+ bool check_refcounted_dtors_;
-+ bool check_virtuals_in_implementations_;
-+};
++'''A baseclass for simple gatherers that store their gathered resource in a
++list.
++'''
++
++from __future__ import print_function
++
++import six
++
++from grit.gather import interface
++from grit import clique
++from grit import exception
++from grit import tclib
++
++
++class SkeletonGatherer(interface.GathererBase):
++ '''Common functionality of gatherers that parse their input as a skeleton of
++ translatable and nontranslatable chunks.
++ '''
++
++ def __init__(self, *args, **kwargs):
++ super(SkeletonGatherer, self).__init__(*args, **kwargs)
++ # List of parts of the document. Translateable parts are
++ # clique.MessageClique objects, nontranslateable parts are plain strings.
++ # Translated messages are inserted back into the skeleton using the quoting
++ # rules defined by self.Escape()
++ self.skeleton_ = []
++ # A list of the names of IDs that need to be defined for this resource
++ # section to compile correctly.
++ self.ids_ = []
++ # True if Parse() has already been called.
++ self.have_parsed_ = False
++ # True if a translatable chunk has been added
++ self.translatable_chunk_ = False
++ # If not None, all parts of the document will be put into this single
++ # message; otherwise the normal skeleton approach is used.
++ self.single_message_ = None
++ # Number to use for the next placeholder name. Used only if single_message
++ # is not None
++ self.ph_counter_ = 1
++
++ def GetText(self):
++ '''Returns the original text of the section'''
++ return self.text_
++
++ def Escape(self, text):
++ '''Subclasses can override. Base impl is identity.
++ '''
++ return text
++
++ def UnEscape(self, text):
++ '''Subclasses can override. Base impl is identity.
++ '''
++ return text
++
++ def GetTextualIds(self):
++ '''Returns the list of textual IDs that need to be defined for this
++ resource section to compile correctly.'''
++ return self.ids_
++
++ def _AddTextualId(self, id):
++ self.ids_.append(id)
++
++ def GetCliques(self):
++ '''Returns the message cliques for each translateable message in the
++ resource section.'''
++ return [x for x in self.skeleton_ if isinstance(x, clique.MessageClique)]
++
++ def Translate(self, lang, pseudo_if_not_available=True,
++ skeleton_gatherer=None, fallback_to_english=False):
++ if len(self.skeleton_) == 0:
++ raise exception.NotReady()
++ if skeleton_gatherer:
++ assert len(skeleton_gatherer.skeleton_) == len(self.skeleton_)
++
++ out = []
++ for ix in range(len(self.skeleton_)):
++ if isinstance(self.skeleton_[ix], six.string_types):
++ if skeleton_gatherer:
++ # Make sure the skeleton is like the original
++ assert(isinstance(skeleton_gatherer.skeleton_[ix], six.string_types))
++ out.append(skeleton_gatherer.skeleton_[ix])
++ else:
++ out.append(self.skeleton_[ix])
++ else:
++ if skeleton_gatherer: # Make sure the skeleton is like the original
++ assert(not isinstance(skeleton_gatherer.skeleton_[ix],
++ six.string_types))
++ msg = self.skeleton_[ix].MessageForLanguage(lang,
++ pseudo_if_not_available,
++ fallback_to_english)
++
++ def MyEscape(text):
++ return self.Escape(text)
++ text = msg.GetRealContent(escaping_function=MyEscape)
++ out.append(text)
++ return ''.join(out)
++
++ def Parse(self):
++ '''Parses the section. Implemented by subclasses. Idempotent.'''
++ raise NotImplementedError()
++
++ def _AddNontranslateableChunk(self, chunk):
++ '''Adds a nontranslateable chunk.'''
++ if self.single_message_:
++ ph = tclib.Placeholder('XX%02dXX' % self.ph_counter_, chunk, chunk)
++ self.ph_counter_ += 1
++ self.single_message_.AppendPlaceholder(ph)
++ else:
++ self.skeleton_.append(chunk)
++
++ def _AddTranslateableChunk(self, chunk):
++ '''Adds a translateable chunk. It will be unescaped before being added.'''
++ # We don't want empty messages since they are redundant and the TC
++ # doesn't allow them.
++ if chunk == '':
++ return
++
++ unescaped_text = self.UnEscape(chunk)
++ if self.single_message_:
++ self.single_message_.AppendText(unescaped_text)
++ else:
++ self.skeleton_.append(self.uberclique.MakeClique(
++ tclib.Message(text=unescaped_text)))
++ self.translatable_chunk_ = True
++
++ def SubstituteMessages(self, substituter):
++ '''Applies substitutions to all messages in the tree.
++
++ Goes through the skeleton and finds all MessageCliques.
++
++ Args:
++ substituter: a grit.util.Substituter object.
++ '''
++ if self.single_message_:
++ self.single_message_ = substituter.SubstituteMessage(self.single_message_)
++ new_skel = []
++ for chunk in self.skeleton_:
++ if isinstance(chunk, clique.MessageClique):
++ old_message = chunk.GetMessage()
++ new_message = substituter.SubstituteMessage(old_message)
++ if new_message is not old_message:
++ new_skel.append(self.uberclique.MakeClique(new_message))
++ continue
++ new_skel.append(chunk)
++ self.skeleton_ = new_skel
+diff --git a/tools/grit/grit/gather/tr_html.py b/tools/grit/grit/gather/tr_html.py
+new file mode 100644
+index 0000000000..60a9bfaf4e
+--- /dev/null
++++ b/tools/grit/grit/gather/tr_html.py
+@@ -0,0 +1,743 @@
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++'''A gatherer for the TotalRecall brand of HTML templates with replaceable
++portions. We wanted to reuse extern.tclib.api.handlers.html.TCHTMLParser
++but this proved impossible due to the fact that the TotalRecall HTML templates
++are in general quite far from parseable HTML and the TCHTMLParser derives
++
++from HTMLParser.HTMLParser which requires relatively well-formed HTML. Some
++examples of "HTML" from the TotalRecall HTML templates that wouldn't be
++parseable include things like:
++
++ <a [PARAMS]>blabla</a> (not parseable because attributes are invalid)
++
++ <table><tr><td>[LOTSOFSTUFF]</tr></table> (not parseable because closing
++ </td> is in the HTML [LOTSOFSTUFF]
++ is replaced by)
++
++The other problem with using general parsers (such as TCHTMLParser) is that
++we want to make sure we output the TotalRecall template with as little changes
++as possible in terms of whitespace characters, layout etc. With any parser
++that generates a parse tree, and generates output by dumping the parse tree,
++we would always have little inconsistencies which could cause bugs (the
++TotalRecall template stuff is quite brittle and can break if e.g. a tab
++character is replaced with spaces).
++
++The solution, which may be applicable to some other HTML-like template
++languages floating around Google, is to create a parser with a simple state
++machine that keeps track of what kind of tag it's inside, and whether it's in
++a translateable section or not. Translateable sections are:
++
++a) text (including [BINGO] replaceables) inside of tags that
++ can contain translateable text (which is all tags except
++ for a few)
++
++b) text inside of an 'alt' attribute in an <image> element, or
++ the 'value' attribute of a <submit>, <button> or <text>
++ element.
++
++The parser does not build up a parse tree but rather a "skeleton" which
++is a list of nontranslateable strings intermingled with grit.clique.MessageClique
++objects. This simplifies the parser considerably compared to a regular HTML
++parser. To output a translated document, each item in the skeleton is
++printed out, with the relevant Translation from each MessageCliques being used
++for the requested language.
++
++This implementation borrows some code, constants and ideas from
++extern.tclib.api.handlers.html.TCHTMLParser.
++'''
++
++from __future__ import print_function
++
++import re
++
++import six
++
++from grit import clique
++from grit import exception
++from grit import lazy_re
++from grit import util
++from grit import tclib
++
++from grit.gather import interface
++
++
++# HTML tags which break (separate) chunks.
++_BLOCK_TAGS = ['script', 'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'br',
++ 'body', 'style', 'head', 'title', 'table', 'tr', 'td', 'th',
++ 'ul', 'ol', 'dl', 'nl', 'li', 'div', 'object', 'center',
++ 'html', 'link', 'form', 'select', 'textarea',
++ 'button', 'option', 'map', 'area', 'blockquote', 'pre',
++ 'meta', 'xmp', 'noscript', 'label', 'tbody', 'thead',
++ 'script', 'style', 'pre', 'iframe', 'img', 'input', 'nowrap',
++ 'fieldset', 'legend']
++
++# HTML tags which may appear within a chunk.
++_INLINE_TAGS = ['b', 'i', 'u', 'tt', 'code', 'font', 'a', 'span', 'small',
++ 'key', 'nobr', 'url', 'em', 's', 'sup', 'strike',
++ 'strong']
++
++# HTML tags within which linebreaks are significant.
++_PREFORMATTED_TAGS = ['textarea', 'xmp', 'pre']
++
++# An array mapping some of the inline HTML tags to more meaningful
++# names for those tags. This will be used when generating placeholders
++# representing these tags.
++_HTML_PLACEHOLDER_NAMES = { 'a' : 'link', 'br' : 'break', 'b' : 'bold',
++ 'i' : 'italic', 'li' : 'item', 'ol' : 'ordered_list', 'p' : 'paragraph',
++ 'ul' : 'unordered_list', 'img' : 'image', 'em' : 'emphasis' }
++
++# We append each of these characters in sequence to distinguish between
++# different placeholders with basically the same name (e.g. BOLD1, BOLD2).
++# Keep in mind that a placeholder name must not be a substring of any other
++# placeholder name in the same message, so we can't simply count (BOLD_1
++# would be a substring of BOLD_10).
++_SUFFIXES = '123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
++
++# Matches whitespace in an HTML document. Also matches HTML comments, which are
++# treated as whitespace.
++_WHITESPACE = lazy_re.compile(r'(\s|&nbsp;|\\n|\\r|<!--\s*desc\s*=.*?-->)+',
++ re.DOTALL)
++
++# Matches whitespace sequences which can be folded into a single whitespace
++# character. This matches single characters so that non-spaces are replaced
++# with spaces.
++_FOLD_WHITESPACE = lazy_re.compile(r'\s+')
++
++# Finds a non-whitespace character
++_NON_WHITESPACE = lazy_re.compile(r'\S')
++
++# Matches two or more &nbsp; in a row (a single &nbsp is not changed into
++# placeholders because different languages require different numbers of spaces
++# and placeholders must match exactly; more than one is probably a "special"
++# whitespace sequence and should be turned into a placeholder).
++_NBSP = lazy_re.compile(r'&nbsp;(&nbsp;)+')
++
++# Matches nontranslateable chunks of the document
++_NONTRANSLATEABLES = lazy_re.compile(r'''
++ <\s*script.+?<\s*/\s*script\s*>
++ |
++ <\s*style.+?<\s*/\s*style\s*>
++ |
++ <!--.+?-->
++ |
++ <\?IMPORT\s.+?> # import tag
++ |
++ <\s*[a-zA-Z_]+:.+?> # custom tag (open)
++ |
++ <\s*/\s*[a-zA-Z_]+:.+?> # custom tag (close)
++ |
++ <!\s*[A-Z]+\s*([^>]+|"[^"]+"|'[^']+')*?>
++ ''', re.MULTILINE | re.DOTALL | re.VERBOSE | re.IGNORECASE)
++
++# Matches a tag and its attributes
++_ELEMENT = lazy_re.compile(r'''
++ # Optional closing /, element name
++ <\s*(?P<closing>/)?\s*(?P<element>[a-zA-Z0-9]+)\s*
++ # Attributes and/or replaceables inside the tag, if any
++ (?P<atts>(
++ \s*([a-zA-Z_][-:.a-zA-Z_0-9]*) # Attribute name
++ (\s*=\s*(\'[^\']*\'|"[^"]*"|[-a-zA-Z0-9./,:;+*%?!&$\(\)_#=~\'"@]*))?
++ |
++ \s*\[(\$?\~)?([A-Z0-9-_]+?)(\~\$?)?\]
++ )*)
++ \s*(?P<empty>/)?\s*> # Optional empty-tag closing /, and tag close
++ ''',
++ re.MULTILINE | re.DOTALL | re.VERBOSE)
++
++# Matches elements that may have translateable attributes. The value of these
++# special attributes is given by group 'value1' or 'value2'. Note that this
++# regexp demands that the attribute value be quoted; this is necessary because
++# the non-tree-building nature of the parser means we don't know when we're
++# writing out attributes, so we wouldn't know to escape spaces.
++_SPECIAL_ELEMENT = lazy_re.compile(r'''
++ <\s*(
++ input[^>]+?value\s*=\s*(\'(?P<value3>[^\']*)\'|"(?P<value4>[^"]*)")
++ [^>]+type\s*=\s*"?'?(button|reset|text|submit)'?"?
++ |
++ (
++ table[^>]+?title\s*=
++ |
++ img[^>]+?alt\s*=
++ |
++ input[^>]+?type\s*=\s*"?'?(button|reset|text|submit)'?"?[^>]+?value\s*=
++ )
++ \s*(\'(?P<value1>[^\']*)\'|"(?P<value2>[^"]*)")
++ )[^>]*?>
++ ''', re.MULTILINE | re.DOTALL | re.VERBOSE | re.IGNORECASE)
++
++# Matches stuff that is translateable if it occurs in the right context
++# (between tags). This includes all characters and character entities.
++# Note that this also matches &nbsp; which needs to be handled as whitespace
++# before this regexp is applied.
++_CHARACTERS = lazy_re.compile(r'''
++ (
++ \w
++ |
++ [\!\@\#\$\%\^\*\(\)\-\=\_\+\[\]\{\}\\\|\;\:\'\"\,\.\/\?\`\~]
++ |
++ &(\#[0-9]+|\#x[0-9a-fA-F]+|[A-Za-z0-9]+);
++ )+
++ ''', re.MULTILINE | re.DOTALL | re.VERBOSE)
++
++# Matches Total Recall's "replaceable" tags, which are just any text
++# in capitals enclosed by delimiters like [] or [~~] or [$~~$] (e.g. [HELLO],
++# [~HELLO~] and [$~HELLO~$]).
++_REPLACEABLE = lazy_re.compile(r'\[(\$?\~)?(?P<name>[A-Z0-9-_]+?)(\~\$?)?\]',
++ re.MULTILINE)
++
++
++# Matches the silly [!]-prefixed "header" that is used in some TotalRecall
++# templates.
++_SILLY_HEADER = lazy_re.compile(r'\[!\]\ntitle\t(?P<title>[^\n]+?)\n.+?\n\n',
++ re.MULTILINE | re.DOTALL)
++
++
++# Matches a comment that provides a description for the message it occurs in.
++_DESCRIPTION_COMMENT = lazy_re.compile(
++ r'<!--\s*desc\s*=\s*(?P<description>.+?)\s*-->', re.DOTALL)
++
++# Matches a comment which is used to break apart multiple messages.
++_MESSAGE_BREAK_COMMENT = lazy_re.compile(r'<!--\s*message-break\s*-->',
++ re.DOTALL)
++
++# Matches a comment which is used to prevent block tags from splitting a message
++_MESSAGE_NO_BREAK_COMMENT = re.compile(r'<!--\s*message-no-break\s*-->',
++ re.DOTALL)
++
++
++_DEBUG = 0
++def _DebugPrint(text):
++ if _DEBUG:
++ print(text.encode('utf-8'))
++
++
++class HtmlChunks(object):
++ '''A parser that knows how to break an HTML-like document into a list of
++ chunks, where each chunk is either translateable or non-translateable.
++ The chunks are unmodified sections of the original document, so concatenating
++ the text of all chunks would result in the original document.'''
++
++ def InTranslateable(self):
++ return self.last_translateable != -1
++
++ def Rest(self):
++ return self.text_[self.current:]
++
++ def StartTranslateable(self):
++ assert not self.InTranslateable()
++ if self.current != 0:
++ # Append a nontranslateable chunk
++ chunk_text = self.text_[self.chunk_start : self.last_nontranslateable + 1]
++ # Needed in the case where document starts with a translateable.
++ if len(chunk_text) > 0:
++ self.AddChunk(False, chunk_text)
++ self.chunk_start = self.last_nontranslateable + 1
++ self.last_translateable = self.current
++ self.last_nontranslateable = -1
++
++ def EndTranslateable(self):
++ assert self.InTranslateable()
++ # Append a translateable chunk
++ self.AddChunk(True,
++ self.text_[self.chunk_start : self.last_translateable + 1])
++ self.chunk_start = self.last_translateable + 1
++ self.last_translateable = -1
++ self.last_nontranslateable = self.current
++
++ def AdvancePast(self, match):
++ self.current += match.end()
++
++ def AddChunk(self, translateable, text):
++ '''Adds a chunk to self, removing linebreaks and duplicate whitespace
++ if appropriate.
++ '''
++ m = _DESCRIPTION_COMMENT.search(text)
++ if m:
++ self.last_description = m.group('description')
++ # Remove the description from the output text
++ text = _DESCRIPTION_COMMENT.sub('', text)
++
++ m = _MESSAGE_BREAK_COMMENT.search(text)
++ if m:
++ # Remove the coment from the output text. It should already effectively
++ # break apart messages.
++ text = _MESSAGE_BREAK_COMMENT.sub('', text)
++
++ if translateable and not self.last_element_ in _PREFORMATTED_TAGS:
++ if self.fold_whitespace_:
++ # Fold whitespace sequences if appropriate. This is optional because it
++ # alters the output strings.
++ text = _FOLD_WHITESPACE.sub(' ', text)
++ else:
++ text = text.replace('\n', ' ')
++ text = text.replace('\r', ' ')
++ # This whitespace folding doesn't work in all cases, thus the
++ # fold_whitespace flag to support backwards compatibility.
++ text = text.replace(' ', ' ')
++ text = text.replace(' ', ' ')
++
++ if translateable:
++ description = self.last_description
++ self.last_description = ''
++ else:
++ description = ''
++
++ if text != '':
++ self.chunks_.append((translateable, text, description))
++
++ def Parse(self, text, fold_whitespace):
++ '''Parses self.text_ into an intermediate format stored in self.chunks_
++ which is translateable and nontranslateable chunks. Also returns
++ self.chunks_
++
++ Args:
++ text: The HTML for parsing.
++ fold_whitespace: Whether whitespace sequences should be folded into a
++ single space.
++
++ Return:
++ [chunk1, chunk2, chunk3, ...] (instances of class Chunk)
++ '''
++ #
++ # Chunker state
++ #
++
++ self.text_ = text
++ self.fold_whitespace_ = fold_whitespace
++
++ # A list of tuples (is_translateable, text) which represents the document
++ # after chunking.
++ self.chunks_ = []
++
++ # Start index of the last chunk, whether translateable or not
++ self.chunk_start = 0
++
++ # Index of the last for-sure translateable character if we are parsing
++ # a translateable chunk, -1 to indicate we are not in a translateable chunk.
++ # This is needed so that we don't include trailing whitespace in the
++ # translateable chunk (whitespace is neutral).
++ self.last_translateable = -1
++
++ # Index of the last for-sure nontranslateable character if we are parsing
++ # a nontranslateable chunk, -1 if we are not in a nontranslateable chunk.
++ # This is needed to make sure we can group e.g. "<b>Hello</b> there"
++ # together instead of just "Hello</b> there" which would be much worse
++ # for translation.
++ self.last_nontranslateable = -1
++
++ # Index of the character we're currently looking at.
++ self.current = 0
++
++ # The name of the last block element parsed.
++ self.last_element_ = ''
++
++ # The last explicit description we found.
++ self.last_description = ''
++
++ # Whether no-break was the last chunk seen
++ self.last_nobreak = False
++
++ while self.current < len(self.text_):
++ _DebugPrint('REST: %s' % self.text_[self.current:self.current+60])
++
++ m = _MESSAGE_NO_BREAK_COMMENT.match(self.Rest())
++ if m:
++ self.AdvancePast(m)
++ self.last_nobreak = True
++ continue
++
++ # Try to match whitespace
++ m = _WHITESPACE.match(self.Rest())
++ if m:
++ # Whitespace is neutral, it just advances 'current' and does not switch
++ # between translateable/nontranslateable. If we are in a
++ # nontranslateable section that extends to the current point, we extend
++ # it to include the whitespace. If we are in a translateable section,
++ # we do not extend it until we find
++ # more translateable parts, because we never want a translateable chunk
++ # to end with whitespace.
++ if (not self.InTranslateable() and
++ self.last_nontranslateable == self.current - 1):
++ self.last_nontranslateable = self.current + m.end() - 1
++ self.AdvancePast(m)
++ continue
++
++ # Then we try to match nontranslateables
++ m = _NONTRANSLATEABLES.match(self.Rest())
++ if m:
++ if self.InTranslateable():
++ self.EndTranslateable()
++ self.last_nontranslateable = self.current + m.end() - 1
++ self.AdvancePast(m)
++ continue
++
++ # Now match all other HTML element tags (opening, closing, or empty, we
++ # don't care).
++ m = _ELEMENT.match(self.Rest())
++ if m:
++ element_name = m.group('element').lower()
++ if element_name in _BLOCK_TAGS:
++ self.last_element_ = element_name
++ if self.InTranslateable():
++ if self.last_nobreak:
++ self.last_nobreak = False
++ else:
++ self.EndTranslateable()
++
++ # Check for "special" elements, i.e. ones that have a translateable
++ # attribute, and handle them correctly. Note that all of the
++ # "special" elements are block tags, so no need to check for this
++ # if the tag is not a block tag.
++ sm = _SPECIAL_ELEMENT.match(self.Rest())
++ if sm:
++ # Get the appropriate group name
++ for group in sm.groupdict():
++ if sm.groupdict()[group]:
++ break
++
++ # First make a nontranslateable chunk up to and including the
++ # quote before the translateable attribute value
++ self.AddChunk(False, self.text_[
++ self.chunk_start : self.current + sm.start(group)])
++ # Then a translateable for the translateable bit
++ self.AddChunk(True, self.Rest()[sm.start(group) : sm.end(group)])
++ # Finally correct the data invariant for the parser
++ self.chunk_start = self.current + sm.end(group)
++
++ self.last_nontranslateable = self.current + m.end() - 1
++ elif self.InTranslateable():
++ # We're in a translateable and the tag is an inline tag, so we
++ # need to include it in the translateable.
++ self.last_translateable = self.current + m.end() - 1
++ self.AdvancePast(m)
++ continue
++
++ # Anything else we find must be translateable, so we advance one character
++ # at a time until one of the above matches.
++ if not self.InTranslateable():
++ self.StartTranslateable()
++ else:
++ self.last_translateable = self.current
++ self.current += 1
++
++ # Close the final chunk
++ if self.InTranslateable():
++ self.AddChunk(True, self.text_[self.chunk_start : ])
++ else:
++ self.AddChunk(False, self.text_[self.chunk_start : ])
++
++ return self.chunks_
++
++
++def HtmlToMessage(html, include_block_tags=False, description=''):
++ '''Takes a bit of HTML, which must contain only "inline" HTML elements,
++ and changes it into a tclib.Message. This involves escaping any entities and
++ replacing any HTML code with placeholders.
++
++ If include_block_tags is true, no error will be given if block tags (e.g.
++ <p> or <br>) are included in the HTML.
++
++ Args:
++ html: 'Hello <b>[USERNAME]</b>, how&nbsp;<i>are</i> you?'
++ include_block_tags: False
++
++ Return:
++ tclib.Message('Hello START_BOLD1USERNAMEEND_BOLD, '
++ 'howNBSPSTART_ITALICareEND_ITALIC you?',
++ [ Placeholder('START_BOLD', '<b>', ''),
++ Placeholder('USERNAME', '[USERNAME]', ''),
++ Placeholder('END_BOLD', '</b>', ''),
++ Placeholder('START_ITALIC', '<i>', ''),
++ Placeholder('END_ITALIC', '</i>', ''), ])
++ '''
++ # Approach is:
++ # - first placeholderize, finding <elements>, [REPLACEABLES] and &nbsp;
++ # - then escape all character entities in text in-between placeholders
++
++ parts = [] # List of strings (for text chunks) and tuples (ID, original)
++ # for placeholders
++
++ count_names = {} # Map of base names to number of times used
++ end_names = {} # Map of base names to stack of end tags (for correct nesting)
++
++ def MakeNameClosure(base, type = ''):
++ '''Returns a closure that can be called once all names have been allocated
++ to return the final name of the placeholder. This allows us to minimally
++ number placeholders for non-overlap.
++
++ Also ensures that END_XXX_Y placeholders have the same Y as the
++ corresponding BEGIN_XXX_Y placeholder when we have nested tags of the same
++ type.
++
++ Args:
++ base: 'phname'
++ type: '' | 'begin' | 'end'
++
++ Return:
++ Closure()
++ '''
++ name = base.upper()
++ if type != '':
++ name = ('%s_%s' % (type, base)).upper()
++
++ count_names.setdefault(name, 0)
++ count_names[name] += 1
++
++ def MakeFinalName(name_ = name, index = count_names[name] - 1):
++ if type.lower() == 'end' and end_names.get(base):
++ return end_names[base].pop(-1) # For correct nesting
++ if count_names[name_] != 1:
++ name_ = '%s_%s' % (name_, _SUFFIXES[index])
++ # We need to use a stack to ensure that the end-tag suffixes match
++ # the begin-tag suffixes. Only needed when more than one tag of the
++ # same type.
++ if type == 'begin':
++ end_name = ('END_%s_%s' % (base, _SUFFIXES[index])).upper()
++ if base in end_names:
++ end_names[base].append(end_name)
++ else:
++ end_names[base] = [end_name]
++
++ return name_
++
++ return MakeFinalName
++
++ current = 0
++ last_nobreak = False
++
++ while current < len(html):
++ m = _MESSAGE_NO_BREAK_COMMENT.match(html[current:])
++ if m:
++ last_nobreak = True
++ current += m.end()
++ continue
++
++ m = _NBSP.match(html[current:])
++ if m:
++ parts.append((MakeNameClosure('SPACE'), m.group()))
++ current += m.end()
++ continue
++
++ m = _REPLACEABLE.match(html[current:])
++ if m:
++ # Replaceables allow - but placeholders don't, so replace - with _
++ ph_name = MakeNameClosure('X_%s_X' % m.group('name').replace('-', '_'))
++ parts.append((ph_name, m.group()))
++ current += m.end()
++ continue
++
++ m = _SPECIAL_ELEMENT.match(html[current:])
++ if m:
++ if not include_block_tags:
++ if last_nobreak:
++ last_nobreak = False
++ else:
++ raise exception.BlockTagInTranslateableChunk(html)
++ element_name = 'block' # for simplification
++ # Get the appropriate group name
++ for group in m.groupdict():
++ if m.groupdict()[group]:
++ break
++ parts.append((MakeNameClosure(element_name, 'begin'),
++ html[current : current + m.start(group)]))
++ parts.append(m.group(group))
++ parts.append((MakeNameClosure(element_name, 'end'),
++ html[current + m.end(group) : current + m.end()]))
++ current += m.end()
++ continue
++
++ m = _ELEMENT.match(html[current:])
++ if m:
++ element_name = m.group('element').lower()
++ if not include_block_tags and not element_name in _INLINE_TAGS:
++ if last_nobreak:
++ last_nobreak = False
++ else:
++ raise exception.BlockTagInTranslateableChunk(html[current:])
++ if element_name in _HTML_PLACEHOLDER_NAMES: # use meaningful names
++ element_name = _HTML_PLACEHOLDER_NAMES[element_name]
++
++ # Make a name for the placeholder
++ type = ''
++ if not m.group('empty'):
++ if m.group('closing'):
++ type = 'end'
++ else:
++ type = 'begin'
++ parts.append((MakeNameClosure(element_name, type), m.group()))
++ current += m.end()
++ continue
++
++ if len(parts) and isinstance(parts[-1], six.string_types):
++ parts[-1] += html[current]
++ else:
++ parts.append(html[current])
++ current += 1
++
++ msg_text = ''
++ placeholders = []
++ for part in parts:
++ if isinstance(part, tuple):
++ final_name = part[0]()
++ original = part[1]
++ msg_text += final_name
++ placeholders.append(tclib.Placeholder(final_name, original, '(HTML code)'))
++ else:
++ msg_text += part
++
++ msg = tclib.Message(text=msg_text, placeholders=placeholders,
++ description=description)
++ content = msg.GetContent()
++ for ix in range(len(content)):
++ if isinstance(content[ix], six.string_types):
++ content[ix] = util.UnescapeHtml(content[ix], replace_nbsp=False)
++
++ return msg
++
++
++class TrHtml(interface.GathererBase):
++ '''Represents a document or message in the template format used by
++ Total Recall for HTML documents.'''
++
++ def __init__(self, *args, **kwargs):
++ super(TrHtml, self).__init__(*args, **kwargs)
++ self.have_parsed_ = False
++ self.skeleton_ = [] # list of strings and MessageClique objects
++ self.fold_whitespace_ = False
++
++ def SetAttributes(self, attrs):
++ '''Sets node attributes used by the gatherer.
++
++ This checks the fold_whitespace attribute.
++
++ Args:
++ attrs: The mapping of node attributes.
++ '''
++ self.fold_whitespace_ = ('fold_whitespace' in attrs and
++ attrs['fold_whitespace'] == 'true')
++
++ def GetText(self):
++ '''Returns the original text of the HTML document'''
++ return self.text_
++
++ def GetTextualIds(self):
++ return [self.extkey]
++
++ def GetCliques(self):
++ '''Returns the message cliques for each translateable message in the
++ document.'''
++ return [x for x in self.skeleton_ if isinstance(x, clique.MessageClique)]
++
++ def Translate(self, lang, pseudo_if_not_available=True,
++ skeleton_gatherer=None, fallback_to_english=False):
++ '''Returns this document with translateable messages filled with
++ the translation for language 'lang'.
++
++ Args:
++ lang: 'en'
++ pseudo_if_not_available: True
++
++ Return:
++ 'ID_THIS_SECTION TYPE\n...BEGIN\n "Translated message"\n......\nEND
++
++ Raises:
++ grit.exception.NotReady() if used before Parse() has been successfully
++ called.
++ grit.exception.NoSuchTranslation() if 'pseudo_if_not_available' is false
++ and there is no translation for the requested language.
++ '''
++ if len(self.skeleton_) == 0:
++ raise exception.NotReady()
++
++ # TODO(joi) Implement support for skeleton gatherers here.
++
++ out = []
++ for item in self.skeleton_:
++ if isinstance(item, six.string_types):
++ out.append(item)
++ else:
++ msg = item.MessageForLanguage(lang,
++ pseudo_if_not_available,
++ fallback_to_english)
++ for content in msg.GetContent():
++ if isinstance(content, tclib.Placeholder):
++ out.append(content.GetOriginal())
++ else:
++ # We escape " characters to increase the chance that attributes
++ # will be properly escaped.
++ out.append(util.EscapeHtml(content, True))
++
++ return ''.join(out)
++
++ def Parse(self):
++ if self.have_parsed_:
++ return
++ self.have_parsed_ = True
++
++ text = self._LoadInputFile()
++
++ # Ignore the BOM character if the document starts with one.
++ if text.startswith(u'\ufeff'):
++ text = text[1:]
++
++ self.text_ = text
++
++ # Parsing is done in two phases: First, we break the document into
++ # translateable and nontranslateable chunks. Second, we run through each
++ # translateable chunk and insert placeholders for any HTML elements,
++ # unescape escaped characters, etc.
++
++ # First handle the silly little [!]-prefixed header because it's not
++ # handled by our HTML parsers.
++ m = _SILLY_HEADER.match(text)
++ if m:
++ self.skeleton_.append(text[:m.start('title')])
++ self.skeleton_.append(self.uberclique.MakeClique(
++ tclib.Message(text=text[m.start('title'):m.end('title')])))
++ self.skeleton_.append(text[m.end('title') : m.end()])
++ text = text[m.end():]
++
++ chunks = HtmlChunks().Parse(text, self.fold_whitespace_)
++
++ for chunk in chunks:
++ if chunk[0]: # Chunk is translateable
++ self.skeleton_.append(self.uberclique.MakeClique(
++ HtmlToMessage(chunk[1], description=chunk[2])))
++ else:
++ self.skeleton_.append(chunk[1])
++
++ # Go through the skeleton and change any messages that consist solely of
++ # placeholders and whitespace into nontranslateable strings.
++ for ix in range(len(self.skeleton_)):
++ got_text = False
++ if isinstance(self.skeleton_[ix], clique.MessageClique):
++ msg = self.skeleton_[ix].GetMessage()
++ for item in msg.GetContent():
++ if (isinstance(item, six.string_types)
++ and _NON_WHITESPACE.search(item) and item != '&nbsp;'):
++ got_text = True
++ break
++ if not got_text:
++ self.skeleton_[ix] = msg.GetRealContent()
++
++ def SubstituteMessages(self, substituter):
++ '''Applies substitutions to all messages in the tree.
++
++ Goes through the skeleton and finds all MessageCliques.
++
++ Args:
++ substituter: a grit.util.Substituter object.
++ '''
++ new_skel = []
++ for chunk in self.skeleton_:
++ if isinstance(chunk, clique.MessageClique):
++ old_message = chunk.GetMessage()
++ new_message = substituter.SubstituteMessage(old_message)
++ if new_message is not old_message:
++ new_skel.append(self.uberclique.MakeClique(new_message))
++ continue
++ new_skel.append(chunk)
++ self.skeleton_ = new_skel
+diff --git a/tools/grit/grit/gather/tr_html_unittest.py b/tools/grit/grit/gather/tr_html_unittest.py
+new file mode 100644
+index 0000000000..1194853d9a
+--- /dev/null
++++ b/tools/grit/grit/gather/tr_html_unittest.py
+@@ -0,0 +1,524 @@
++#!/usr/bin/env python
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
+
-+} // namespace
++'''Unit tests for grit.gather.tr_html'''
+
-+static FrontendPluginRegistry::Add<FindBadConstructsAction>
-+X("find-bad-constructs", "Finds bad C++ constructs");
-diff --git a/tools/clang/plugins/Makefile b/tools/clang/plugins/Makefile
++from __future__ import print_function
++
++import os
++import sys
++if __name__ == '__main__':
++ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
++
++import unittest
++
++import six
++from six import StringIO
++
++from grit.gather import tr_html
++from grit import clique
++from grit import util
++
++
++class ParserUnittest(unittest.TestCase):
++ def testChunkingWithoutFoldWhitespace(self):
++ self.VerifyChunking(False)
++
++ def testChunkingWithFoldWhitespace(self):
++ self.VerifyChunking(True)
++
++ def VerifyChunking(self, fold_whitespace):
++ """Use a single function to run all chunking testing.
++
++ This makes it easier to run chunking with fold_whitespace both on and off,
++ to make sure the outputs are the same.
++
++ Args:
++ fold_whitespace: Whether whitespace sequences should be folded into a
++ single space.
++ """
++ self.VerifyChunkingBasic(fold_whitespace)
++ self.VerifyChunkingDescriptions(fold_whitespace)
++ self.VerifyChunkingReplaceables(fold_whitespace)
++ self.VerifyChunkingLineBreaks(fold_whitespace)
++ self.VerifyChunkingMessageBreak(fold_whitespace)
++ self.VerifyChunkingMessageNoBreak(fold_whitespace)
++
++ def VerifyChunkingBasic(self, fold_whitespace):
++ p = tr_html.HtmlChunks()
++ chunks = p.Parse('<p>Hello <b>dear</b> how <i>are</i>you?<p>Fine!',
++ fold_whitespace)
++ self.failUnlessEqual(chunks, [
++ (False, '<p>', ''), (True, 'Hello <b>dear</b> how <i>are</i>you?', ''),
++ (False, '<p>', ''), (True, 'Fine!', '')])
++
++ chunks = p.Parse('<p> Hello <b>dear</b> how <i>are</i>you? <p>Fine!',
++ fold_whitespace)
++ self.failUnlessEqual(chunks, [
++ (False, '<p> ', ''), (True, 'Hello <b>dear</b> how <i>are</i>you?', ''),
++ (False, ' <p>', ''), (True, 'Fine!', '')])
++
++ chunks = p.Parse('<p> Hello <b>dear how <i>are you? <p> Fine!',
++ fold_whitespace)
++ self.failUnlessEqual(chunks, [
++ (False, '<p> ', ''), (True, 'Hello <b>dear how <i>are you?', ''),
++ (False, ' <p> ', ''), (True, 'Fine!', '')])
++
++ # Ensure translateable sections that start with inline tags contain
++ # the starting inline tag.
++ chunks = p.Parse('<b>Hello!</b> how are you?<p><i>I am fine.</i>',
++ fold_whitespace)
++ self.failUnlessEqual(chunks, [
++ (True, '<b>Hello!</b> how are you?', ''), (False, '<p>', ''),
++ (True, '<i>I am fine.</i>', '')])
++
++ # Ensure translateable sections that end with inline tags contain
++ # the ending inline tag.
++ chunks = p.Parse("Hello! How are <b>you?</b><p><i>I'm fine!</i>",
++ fold_whitespace)
++ self.failUnlessEqual(chunks, [
++ (True, 'Hello! How are <b>you?</b>', ''), (False, '<p>', ''),
++ (True, "<i>I'm fine!</i>", '')])
++
++ def VerifyChunkingDescriptions(self, fold_whitespace):
++ p = tr_html.HtmlChunks()
++ # Check capitals and explicit descriptions
++ chunks = p.Parse('<!-- desc=bingo! --><B>Hello!</B> how are you?<P>'
++ '<I>I am fine.</I>', fold_whitespace)
++ self.failUnlessEqual(chunks, [
++ (True, '<B>Hello!</B> how are you?', 'bingo!'), (False, '<P>', ''),
++ (True, '<I>I am fine.</I>', '')])
++ chunks = p.Parse('<B><!-- desc=bingo! -->Hello!</B> how are you?<P>'
++ '<I>I am fine.</I>', fold_whitespace)
++ self.failUnlessEqual(chunks, [
++ (True, '<B>Hello!</B> how are you?', 'bingo!'), (False, '<P>', ''),
++ (True, '<I>I am fine.</I>', '')])
++ # Linebreaks get handled by the tclib message.
++ chunks = p.Parse('<B>Hello!</B> <!-- desc=bi\nngo\n! -->how are you?<P>'
++ '<I>I am fine.</I>', fold_whitespace)
++ self.failUnlessEqual(chunks, [
++ (True, '<B>Hello!</B> how are you?', 'bi\nngo\n!'), (False, '<P>', ''),
++ (True, '<I>I am fine.</I>', '')])
++
++ # In this case, because the explicit description appears after the first
++ # translateable, it will actually apply to the second translateable.
++ chunks = p.Parse('<B>Hello!</B> how are you?<!-- desc=bingo! --><P>'
++ '<I>I am fine.</I>', fold_whitespace)
++ self.failUnlessEqual(chunks, [
++ (True, '<B>Hello!</B> how are you?', ''), (False, '<P>', ''),
++ (True, '<I>I am fine.</I>', 'bingo!')])
++
++ def VerifyChunkingReplaceables(self, fold_whitespace):
++ # Check that replaceables within block tags (where attributes would go) are
++ # handled correctly.
++ p = tr_html.HtmlChunks()
++ chunks = p.Parse('<b>Hello!</b> how are you?<p [BINGO] [$~BONGO~$]>'
++ '<i>I am fine.</i>', fold_whitespace)
++ self.failUnlessEqual(chunks, [
++ (True, '<b>Hello!</b> how are you?', ''),
++ (False, '<p [BINGO] [$~BONGO~$]>', ''),
++ (True, '<i>I am fine.</i>', '')])
++
++ def VerifyChunkingLineBreaks(self, fold_whitespace):
++ # Check that the contents of preformatted tags preserve line breaks.
++ p = tr_html.HtmlChunks()
++ chunks = p.Parse('<textarea>Hello\nthere\nhow\nare\nyou?</textarea>',
++ fold_whitespace)
++ self.failUnlessEqual(chunks, [(False, '<textarea>', ''),
++ (True, 'Hello\nthere\nhow\nare\nyou?', ''), (False, '</textarea>', '')])
++
++ # ...and that other tags' line breaks are converted to spaces
++ chunks = p.Parse('<p>Hello\nthere\nhow\nare\nyou?</p>', fold_whitespace)
++ self.failUnlessEqual(chunks, [(False, '<p>', ''),
++ (True, 'Hello there how are you?', ''), (False, '</p>', '')])
++
++ def VerifyChunkingMessageBreak(self, fold_whitespace):
++ p = tr_html.HtmlChunks()
++ # Make sure that message-break comments work properly.
++ chunks = p.Parse('Break<!-- message-break --> apart '
++ '<!--message-break-->messages', fold_whitespace)
++ self.failUnlessEqual(chunks, [(True, 'Break', ''),
++ (False, ' ', ''),
++ (True, 'apart', ''),
++ (False, ' ', ''),
++ (True, 'messages', '')])
++
++ # Make sure message-break comments work in an inline tag.
++ chunks = p.Parse('<a href=\'google.com\'><!-- message-break -->Google'
++ '<!--message-break--></a>', fold_whitespace)
++ self.failUnlessEqual(chunks, [(False, '<a href=\'google.com\'>', ''),
++ (True, 'Google', ''),
++ (False, '</a>', '')])
++
++ def VerifyChunkingMessageNoBreak(self, fold_whitespace):
++ p = tr_html.HtmlChunks()
++ # Make sure that message-no-break comments work properly.
++ chunks = p.Parse('Please <!-- message-no-break --> <br />don\'t break',
++ fold_whitespace)
++ self.failUnlessEqual(chunks, [(True, 'Please <!-- message-no-break --> '
++ '<br />don\'t break', '')])
++
++ chunks = p.Parse('Please <br /> break. <!-- message-no-break --> <br /> '
++ 'But not this time.', fold_whitespace)
++ self.failUnlessEqual(chunks, [(True, 'Please', ''),
++ (False, ' <br /> ', ''),
++ (True, 'break. <!-- message-no-break --> '
++ '<br /> But not this time.', '')])
++
++ def testTranslateableAttributes(self):
++ p = tr_html.HtmlChunks()
++
++ # Check that the translateable attributes in <img>, <submit>, <button> and
++ # <text> elements buttons are handled correctly.
++ chunks = p.Parse('<img src=bingo.jpg alt="hello there">'
++ '<input type=submit value="hello">'
++ '<input type="button" value="hello">'
++ '<input type=\'text\' value=\'Howdie\'>', False)
++ self.failUnlessEqual(chunks, [
++ (False, '<img src=bingo.jpg alt="', ''), (True, 'hello there', ''),
++ (False, '"><input type=submit value="', ''), (True, 'hello', ''),
++ (False, '"><input type="button" value="', ''), (True, 'hello', ''),
++ (False, '"><input type=\'text\' value=\'', ''), (True, 'Howdie', ''),
++ (False, '\'>', '')])
++
++
++ def testTranslateableHtmlToMessage(self):
++ msg = tr_html.HtmlToMessage(
++ 'Hello <b>[USERNAME]</b>, &lt;how&gt;&nbsp;<i>are</i> you?')
++ pres = msg.GetPresentableContent()
++ self.failUnless(pres ==
++ 'Hello BEGIN_BOLDX_USERNAME_XEND_BOLD, '
++ '<how>&nbsp;BEGIN_ITALICareEND_ITALIC you?')
++
++ msg = tr_html.HtmlToMessage('<b>Hello</b><I>Hello</I><b>Hello</b>')
++ pres = msg.GetPresentableContent()
++ self.failUnless(pres ==
++ 'BEGIN_BOLD_1HelloEND_BOLD_1BEGIN_ITALICHelloEND_ITALIC'
++ 'BEGIN_BOLD_2HelloEND_BOLD_2')
++
++ # Check that nesting (of the <font> tags) is handled correctly - i.e. that
++ # the closing placeholder numbers match the opening placeholders.
++ msg = tr_html.HtmlToMessage(
++ '''<font size=-1><font color=#FF0000>Update!</font> '''
++ '''<a href='http://desktop.google.com/whatsnew.html?hl=[$~LANG~$]'>'''
++ '''New Features</a>: Now search PDFs, MP3s, Firefox web history, and '''
++ '''more</font>''')
++ pres = msg.GetPresentableContent()
++ self.failUnless(pres ==
++ 'BEGIN_FONT_1BEGIN_FONT_2Update!END_FONT_2 BEGIN_LINK'
++ 'New FeaturesEND_LINK: Now search PDFs, MP3s, Firefox '
++ 'web history, and moreEND_FONT_1')
++
++ msg = tr_html.HtmlToMessage('''<a href='[$~URL~$]'><b>[NUM][CAT]</b></a>''')
++ pres = msg.GetPresentableContent()
++ self.failUnless(pres == 'BEGIN_LINKBEGIN_BOLDX_NUM_XX_CAT_XEND_BOLDEND_LINK')
++
++ msg = tr_html.HtmlToMessage(
++ '''<font size=-1><a class=q onClick='return window.qs?qs(this):1' '''
++ '''href='http://[WEBSERVER][SEARCH_URI]'>Desktop</a></font>&nbsp;&nbsp;'''
++ '''&nbsp;&nbsp;''')
++ pres = msg.GetPresentableContent()
++ self.failUnless(pres ==
++ '''BEGIN_FONTBEGIN_LINKDesktopEND_LINKEND_FONTSPACE''')
++
++ msg = tr_html.HtmlToMessage(
++ '''<br><br><center><font size=-2>&copy;2005 Google </font></center>''', 1)
++ pres = msg.GetPresentableContent()
++ self.failUnless(pres ==
++ u'BEGIN_BREAK_1BEGIN_BREAK_2BEGIN_CENTERBEGIN_FONT\xa92005'
++ u' Google END_FONTEND_CENTER')
++
++ msg = tr_html.HtmlToMessage(
++ '''&nbsp;-&nbsp;<a class=c href=[$~CACHE~$]>Cached</a>''')
++ pres = msg.GetPresentableContent()
++ self.failUnless(pres ==
++ '&nbsp;-&nbsp;BEGIN_LINKCachedEND_LINK')
++
++ # Check that upper-case tags are handled correctly.
++ msg = tr_html.HtmlToMessage(
++ '''You can read the <A HREF='http://desktop.google.com/privacypolicy.'''
++ '''html?hl=[LANG_CODE]'>Privacy Policy</A> and <A HREF='http://desktop'''
++ '''.google.com/privacyfaq.html?hl=[LANG_CODE]'>Privacy FAQ</A> online.''')
++ pres = msg.GetPresentableContent()
++ self.failUnless(pres ==
++ 'You can read the BEGIN_LINK_1Privacy PolicyEND_LINK_1 and '
++ 'BEGIN_LINK_2Privacy FAQEND_LINK_2 online.')
++
++ # Check that tags with linebreaks immediately preceding them are handled
++ # correctly.
++ msg = tr_html.HtmlToMessage(
++ '''You can read the
++<A HREF='http://desktop.google.com/privacypolicy.html?hl=[LANG_CODE]'>Privacy Policy</A>
++and <A HREF='http://desktop.google.com/privacyfaq.html?hl=[LANG_CODE]'>Privacy FAQ</A> online.''')
++ pres = msg.GetPresentableContent()
++ self.failUnless(pres == '''You can read the
++BEGIN_LINK_1Privacy PolicyEND_LINK_1
++and BEGIN_LINK_2Privacy FAQEND_LINK_2 online.''')
++
++ # Check that message-no-break comments are handled correctly.
++ msg = tr_html.HtmlToMessage('''Please <!-- message-no-break --><br /> don't break''')
++ pres = msg.GetPresentableContent()
++ self.failUnlessEqual(pres, '''Please BREAK don't break''')
++
++class TrHtmlUnittest(unittest.TestCase):
++ def testSetAttributes(self):
++ html = tr_html.TrHtml(StringIO(''))
++ self.failUnlessEqual(html.fold_whitespace_, False)
++ html.SetAttributes({})
++ self.failUnlessEqual(html.fold_whitespace_, False)
++ html.SetAttributes({'fold_whitespace': 'false'})
++ self.failUnlessEqual(html.fold_whitespace_, False)
++ html.SetAttributes({'fold_whitespace': 'true'})
++ self.failUnlessEqual(html.fold_whitespace_, True)
++
++ def testFoldWhitespace(self):
++ text = '<td> Test Message </td>'
++
++ html = tr_html.TrHtml(StringIO(text))
++ html.Parse()
++ self.failUnlessEqual(html.skeleton_[1].GetMessage().GetPresentableContent(),
++ 'Test Message')
++
++ html = tr_html.TrHtml(StringIO(text))
++ html.fold_whitespace_ = True
++ html.Parse()
++ self.failUnlessEqual(html.skeleton_[1].GetMessage().GetPresentableContent(),
++ 'Test Message')
++
++ def testTable(self):
++ html = tr_html.TrHtml(StringIO('''<table class="shaded-header"><tr>
++<td class="header-element b expand">Preferences</td>
++<td class="header-element s">
++<a href="http://desktop.google.com/preferences.html">Preferences&nbsp;Help</a>
++</td>
++</tr></table>'''))
++ html.Parse()
++ self.failUnless(html.skeleton_[3].GetMessage().GetPresentableContent() ==
++ 'BEGIN_LINKPreferences&nbsp;HelpEND_LINK')
++
++ def testSubmitAttribute(self):
++ html = tr_html.TrHtml(StringIO('''</td>
++<td class="header-element"><input type=submit value="Save Preferences"
++name=submit2></td>
++</tr></table>'''))
++ html.Parse()
++ self.failUnless(html.skeleton_[1].GetMessage().GetPresentableContent() ==
++ 'Save Preferences')
++
++ def testWhitespaceAfterInlineTag(self):
++ '''Test that even if there is whitespace after an inline tag at the start
++ of a translateable section the inline tag will be included.
++ '''
++ html = tr_html.TrHtml(
++ StringIO('''<label for=DISPLAYNONE><font size=-1> Hello</font>'''))
++ html.Parse()
++ self.failUnless(html.skeleton_[1].GetMessage().GetRealContent() ==
++ '<font size=-1> Hello</font>')
++
++ def testSillyHeader(self):
++ html = tr_html.TrHtml(StringIO('''[!]
++title\tHello
++bingo
++bongo
++bla
++
++<p>Other stuff</p>'''))
++ html.Parse()
++ content = html.skeleton_[1].GetMessage().GetRealContent()
++ self.failUnless(content == 'Hello')
++ self.failUnless(html.skeleton_[-1] == '</p>')
++ # Right after the translateable the nontranslateable should start with
++ # a linebreak (this catches a bug we had).
++ self.failUnless(html.skeleton_[2][0] == '\n')
++
++
++ def testExplicitDescriptions(self):
++ html = tr_html.TrHtml(
++ StringIO('Hello [USER]<br/><!-- desc=explicit -->'
++ '<input type="button">Go!</input>'))
++ html.Parse()
++ msg = html.GetCliques()[1].GetMessage()
++ self.failUnlessEqual(msg.GetDescription(), 'explicit')
++ self.failUnlessEqual(msg.GetRealContent(), 'Go!')
++
++ html = tr_html.TrHtml(
++ StringIO('Hello [USER]<br/><!-- desc=explicit\nmultiline -->'
++ '<input type="button">Go!</input>'))
++ html.Parse()
++ msg = html.GetCliques()[1].GetMessage()
++ self.failUnlessEqual(msg.GetDescription(), 'explicit multiline')
++ self.failUnlessEqual(msg.GetRealContent(), 'Go!')
++
++
++ def testRegressionInToolbarAbout(self):
++ html = tr_html.TrHtml(util.PathFromRoot(r'grit/testdata/toolbar_about.html'))
++ html.Parse()
++ cliques = html.GetCliques()
++ for cl in cliques:
++ content = cl.GetMessage().GetRealContent()
++ if content.count('De parvis grandis acervus erit'):
++ self.failIf(content.count('$/translate'))
++
++
++ def HtmlFromFileWithManualCheck(self, f):
++ html = tr_html.TrHtml(f)
++ html.Parse()
++
++ # For manual results inspection only...
++ list = []
++ for item in html.skeleton_:
++ if isinstance(item, six.string_types):
++ list.append(item)
++ else:
++ list.append(item.GetMessage().GetPresentableContent())
++
++ return html
++
++
++ def testPrivacyHtml(self):
++ html = self.HtmlFromFileWithManualCheck(
++ util.PathFromRoot(r'grit/testdata/privacy.html'))
++
++ self.failUnless(html.skeleton_[1].GetMessage().GetRealContent() ==
++ 'Privacy and Google Desktop Search')
++ self.failUnless(html.skeleton_[3].startswith('<'))
++ self.failUnless(len(html.skeleton_) > 10)
++
++
++ def testPreferencesHtml(self):
++ html = self.HtmlFromFileWithManualCheck(
++ util.PathFromRoot(r'grit/testdata/preferences.html'))
++
++ # Verify that we don't get '[STATUS-MESSAGE]' as the original content of
++ # one of the MessageClique objects (it would be a placeholder-only message
++ # and we're supposed to have stripped those).
++
++ for item in [x for x in html.skeleton_
++ if isinstance(x, clique.MessageClique)]:
++ if (item.GetMessage().GetRealContent() == '[STATUS-MESSAGE]' or
++ item.GetMessage().GetRealContent() == '[ADDIN-DO] [ADDIN-OPTIONS]'):
++ self.fail()
++
++ self.failUnless(len(html.skeleton_) > 100)
++
++ def AssertNumberOfTranslateables(self, files, num):
++ '''Fails if any of the files in files don't have exactly
++ num translateable sections.
++
++ Args:
++ files: ['file1', 'file2']
++ num: 3
++ '''
++ for f in files:
++ f = util.PathFromRoot(r'grit/testdata/%s' % f)
++ html = self.HtmlFromFileWithManualCheck(f)
++ self.failUnless(len(html.GetCliques()) == num)
++
++ def testFewTranslateables(self):
++ self.AssertNumberOfTranslateables(['browser.html', 'email_thread.html',
++ 'header.html', 'mini.html',
++ 'oneclick.html', 'script.html',
++ 'time_related.html', 'versions.html'], 0)
++ self.AssertNumberOfTranslateables(['footer.html', 'hover.html'], 1)
++
++ def testOtherHtmlFilesForManualInspection(self):
++ files = [
++ 'about.html', 'bad_browser.html', 'cache_prefix.html',
++ 'cache_prefix_file.html', 'chat_result.html', 'del_footer.html',
++ 'del_header.html', 'deleted.html', 'details.html', 'email_result.html',
++ 'error.html', 'explicit_web.html', 'footer.html',
++ 'homepage.html', 'indexing_speed.html',
++ 'install_prefs.html', 'install_prefs2.html',
++ 'oem_enable.html', 'oem_non_admin.html', 'onebox.html',
++ 'password.html', 'quit_apps.html', 'recrawl.html',
++ 'searchbox.html', 'sidebar_h.html', 'sidebar_v.html', 'status.html',
++ ]
++ for f in files:
++ self.HtmlFromFileWithManualCheck(
++ util.PathFromRoot(r'grit/testdata/%s' % f))
++
++ def testTranslate(self):
++ # Note that the English translation of documents that use character
++ # literals (e.g. &copy;) will not be the same as the original document
++ # because the character literal will be transformed into the Unicode
++ # character itself. So for this test we choose some relatively complex
++ # HTML without character entities (but with &nbsp; because that's handled
++ # specially).
++ html = tr_html.TrHtml(StringIO(''' <script>
++ <!--
++ function checkOffice() { var w = document.getElementById("h7");
++ var e = document.getElementById("h8"); var o = document.getElementById("h10");
++ if (!(w.checked || e.checked)) { o.checked=0;o.disabled=1;} else {o.disabled=0;} }
++ // -->
++ </script>
++ <input type=checkbox [CHECK-DOC] name=DOC id=h7 onclick='checkOffice()'>
++ <label for=h7> Word</label><br>
++ <input type=checkbox [CHECK-XLS] name=XLS id=h8 onclick='checkOffice()'>
++ <label for=h8> Excel</label><br>
++ <input type=checkbox [CHECK-PPT] name=PPT id=h9>
++ <label for=h9> PowerPoint</label><br>
++ </span></td><td nowrap valign=top><span class="s">
++ <input type=checkbox [CHECK-PDF] name=PDF id=hpdf>
++ <label for=hpdf> PDF</label><br>
++ <input type=checkbox [CHECK-TXT] name=TXT id=h6>
++ <label for=h6> Text, media, and other files</label><br>
++ </tr>&nbsp;&nbsp;
++ <tr><td nowrap valign=top colspan=3><span class="s"><br />
++ <input type=checkbox [CHECK-SECUREOFFICE] name=SECUREOFFICE id=h10>
++ <label for=h10> Password-protected Office documents (Word, Excel)</label><br />
++ <input type=checkbox [DISABLED-HTTPS] [CHECK-HTTPS] name=HTTPS id=h12><label
++ for=h12> Secure pages (HTTPS) in web history</label></span></td></tr>
++ </table>'''))
++ html.Parse()
++ trans = html.Translate('en')
++ if (html.GetText() != trans):
++ self.fail()
++
++
++ def testHtmlToMessageWithBlockTags(self):
++ msg = tr_html.HtmlToMessage(
++ 'Hello<p>Howdie<img alt="bingo" src="image.gif">', True)
++ result = msg.GetPresentableContent()
++ self.failUnless(
++ result == 'HelloBEGIN_PARAGRAPHHowdieBEGIN_BLOCKbingoEND_BLOCK')
++
++ msg = tr_html.HtmlToMessage(
++ 'Hello<p>Howdie<input type="button" value="bingo">', True)
++ result = msg.GetPresentableContent()
++ self.failUnless(
++ result == 'HelloBEGIN_PARAGRAPHHowdieBEGIN_BLOCKbingoEND_BLOCK')
++
++
++ def testHtmlToMessageRegressions(self):
++ msg = tr_html.HtmlToMessage(' - ', True)
++ result = msg.GetPresentableContent()
++ self.failUnless(result == ' - ')
++
++
++ def testEscapeUnescaped(self):
++ text = '&copy;&nbsp; & &quot;&lt;hello&gt;&quot;'
++ unescaped = util.UnescapeHtml(text)
++ self.failUnless(unescaped == u'\u00a9\u00a0 & "<hello>"')
++ escaped_unescaped = util.EscapeHtml(unescaped, True)
++ self.failUnless(escaped_unescaped ==
++ u'\u00a9\u00a0 &amp; &quot;&lt;hello&gt;&quot;')
++
++ def testRegressionCjkHtmlFile(self):
++ # TODO(joi) Fix this problem where unquoted attributes that
++ # have a value that is CJK characters causes the regular expression
++ # match never to return. (culprit is the _ELEMENT regexp(
++ if False:
++ html = self.HtmlFromFileWithManualCheck(util.PathFromRoot(
++ r'grit/testdata/ko_oem_enable_bug.html'))
++ self.failUnless(True)
++
++ def testRegressionCpuHang(self):
++ # If this regression occurs, the unit test will never return
++ html = tr_html.TrHtml(StringIO(
++ '''<input type=text size=12 id=advFileTypeEntry [~SHOW-FILETYPE-BOX~] value="[EXT]" name=ext>'''))
++ html.Parse()
++
++if __name__ == '__main__':
++ unittest.main()
+diff --git a/tools/grit/grit/gather/txt.py b/tools/grit/grit/gather/txt.py
new file mode 100644
-index 0000000000..0cfec71159
+index 0000000000..e5c10abc28
--- /dev/null
-+++ b/tools/clang/plugins/Makefile
-@@ -0,0 +1,19 @@
-+# This file requires the clang build system, at least for now. So to use this
-+# Makefile, you should execute the following commands to copy this directory
-+# into a clang checkout:
-+#
-+# cp -R <this directory> third_party/llvm/tools/clang/tools/chrome-plugin
-+# cd third_party/llvm/tools/clang/tools/chrome-plugin
-+# make
++++ b/tools/grit/grit/gather/txt.py
+@@ -0,0 +1,38 @@
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
+
-+CLANG_LEVEL := ../..
-+LIBRARYNAME = FindBadConstructs
++'''Supports making amessage from a text file.
++'''
+
-+LINK_LIBS_IN_SHARED = 0
-+SHARED_LIBRARY = 1
++from __future__ import print_function
+
-+include $(CLANG_LEVEL)/Makefile
++from grit.gather import interface
++from grit import tclib
+
-+ifeq ($(OS),Darwin)
-+ LDFLAGS=-Wl,-undefined,dynamic_lookup
-+endif
-diff --git a/tools/clang/plugins/OWNERS b/tools/clang/plugins/OWNERS
++
++class TxtFile(interface.GathererBase):
++ '''A text file gatherer. Very simple, all text from the file becomes a
++ single clique.
++ '''
++
++ def Parse(self):
++ self.text_ = self._LoadInputFile()
++ self.clique_ = self.uberclique.MakeClique(tclib.Message(text=self.text_))
++
++ def GetText(self):
++ '''Returns the text of what is being gathered.'''
++ return self.text_
++
++ def GetTextualIds(self):
++ return [self.extkey]
++
++ def GetCliques(self):
++ '''Returns the MessageClique objects for all translateable portions.'''
++ return [self.clique_]
++
++ def Translate(self, lang, pseudo_if_not_available=True,
++ skeleton_gatherer=None, fallback_to_english=False):
++ return self.clique_.MessageForLanguage(lang,
++ pseudo_if_not_available,
++ fallback_to_english).GetRealContent()
+diff --git a/tools/grit/grit/gather/txt_unittest.py b/tools/grit/grit/gather/txt_unittest.py
new file mode 100644
-index 0000000000..4733a4f06b
+index 0000000000..abb9ed98d7
--- /dev/null
-+++ b/tools/clang/plugins/OWNERS
-@@ -0,0 +1 @@
-+erg@chromium.org
-diff --git a/tools/clang/plugins/README.chromium b/tools/clang/plugins/README.chromium
++++ b/tools/grit/grit/gather/txt_unittest.py
+@@ -0,0 +1,35 @@
++#!/usr/bin/env python
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++'''Unit tests for TxtFile gatherer'''
++
++from __future__ import print_function
++
++import os
++import sys
++if __name__ == '__main__':
++ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
++
++
++import unittest
++
++from six import StringIO
++
++from grit.gather import txt
++
++
++class TxtUnittest(unittest.TestCase):
++ def testGather(self):
++ input = StringIO('Hello there\nHow are you?')
++ gatherer = txt.TxtFile(input)
++ gatherer.Parse()
++ self.failUnless(gatherer.GetText() == input.getvalue())
++ self.failUnless(len(gatherer.GetCliques()) == 1)
++ self.failUnless(gatherer.GetCliques()[0].GetMessage().GetRealContent() ==
++ input.getvalue())
++
++
++if __name__ == '__main__':
++ unittest.main()
+diff --git a/tools/grit/grit/grd_reader.py b/tools/grit/grit/grd_reader.py
new file mode 100644
-index 0000000000..a2ce0ff557
+index 0000000000..b7bb782977
--- /dev/null
-+++ b/tools/clang/plugins/README.chromium
-@@ -0,0 +1,4 @@
-+Documentation for this code is:
++++ b/tools/grit/grit/grd_reader.py
+@@ -0,0 +1,238 @@
++#!/usr/bin/env python
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++'''Class for reading GRD files into memory, without processing them.
++'''
++
++from __future__ import print_function
++
++import os.path
++import sys
++import xml.sax
++import xml.sax.handler
++
++import six
++
++from grit import exception
++from grit import util
++from grit.node import mapping
++from grit.node import misc
++
++
++class StopParsingException(Exception):
++ '''An exception used to stop parsing.'''
++ pass
++
++
++class GrdContentHandler(xml.sax.handler.ContentHandler):
++ def __init__(self, stop_after, debug, dir, defines, tags_to_ignore,
++ target_platform, source):
++ # Invariant of data:
++ # 'root' is the root of the parse tree being created, or None if we haven't
++ # parsed out any elements.
++ # 'stack' is the a stack of elements that we push new nodes onto and
++ # pop from when they finish parsing, or [] if we are not currently parsing.
++ # 'stack[-1]' is the top of the stack.
++ self.root = None
++ self.stack = []
++ self.stop_after = stop_after
++ self.debug = debug
++ self.dir = dir
++ self.defines = defines
++ self.tags_to_ignore = tags_to_ignore or set()
++ self.ignore_depth = 0
++ self.target_platform = target_platform
++ self.source = source
++
++ def startElement(self, name, attrs):
++ if self.ignore_depth or name in self.tags_to_ignore:
++ if self.debug and self.ignore_depth == 0:
++ print("Ignoring element %s and its children" % name)
++ self.ignore_depth += 1
++ return
++
++ if self.debug:
++ attr_list = ' '.join('%s="%s"' % kv for kv in attrs.items())
++ print("Starting parsing of element %s with attributes %r" %
++ (name, attr_list or '(none)'))
++
++ typeattr = attrs.get('type')
++ node = mapping.ElementToClass(name, typeattr)()
++ node.source = self.source
++
++ if self.stack:
++ self.stack[-1].AddChild(node)
++ node.StartParsing(name, self.stack[-1])
++ else:
++ assert self.root is None
++ self.root = node
++ if isinstance(self.root, misc.GritNode):
++ if self.target_platform:
++ self.root.SetTargetPlatform(self.target_platform)
++ node.StartParsing(name, None)
++ if self.defines:
++ node.SetDefines(self.defines)
++ self.stack.append(node)
++
++ for attr, attrval in attrs.items():
++ node.HandleAttribute(attr, attrval)
++
++ def endElement(self, name):
++ if self.ignore_depth:
++ self.ignore_depth -= 1
++ return
++
++ if name == 'part':
++ partnode = self.stack[-1]
++ partnode.started_inclusion = True
++ # Add the contents of the sub-grd file as children of the <part> node.
++ partname = os.path.join(self.dir, partnode.GetInputPath())
++ # Check the GRDP file exists.
++ if not os.path.exists(partname):
++ raise exception.FileNotFound(partname)
++ # Exceptions propagate to the handler in grd_reader.Parse().
++ oldsource = self.source
++ try:
++ self.source = partname
++ xml.sax.parse(partname, GrdPartContentHandler(self))
++ finally:
++ self.source = oldsource
++
++ if self.debug:
++ print("End parsing of element %s" % name)
++ self.stack.pop().EndParsing()
++
++ if name == self.stop_after:
++ raise StopParsingException()
++
++ def characters(self, content):
++ if self.ignore_depth == 0:
++ if self.stack[-1]:
++ self.stack[-1].AppendContent(content)
++
++ def ignorableWhitespace(self, whitespace):
++ # TODO(joi): This is not supported by expat. Should use a different XML
++ # parser?
++ pass
++
++
++class GrdPartContentHandler(xml.sax.handler.ContentHandler):
++ def __init__(self, parent):
++ self.parent = parent
++ self.depth = 0
++
++ def startElement(self, name, attrs):
++ if self.depth:
++ self.parent.startElement(name, attrs)
++ else:
++ if name != 'grit-part':
++ raise exception.MissingElement("root tag must be <grit-part>")
++ if attrs:
++ raise exception.UnexpectedAttribute(
++ "<grit-part> tag must not have attributes")
++ self.depth += 1
++
++ def endElement(self, name):
++ self.depth -= 1
++ if self.depth:
++ self.parent.endElement(name)
++
++ def characters(self, content):
++ self.parent.characters(content)
++
++ def ignorableWhitespace(self, whitespace):
++ self.parent.ignorableWhitespace(whitespace)
++
++
++def Parse(filename_or_stream, dir=None, stop_after=None, first_ids_file=None,
++ debug=False, defines=None, tags_to_ignore=None, target_platform=None,
++ predetermined_ids_file=None):
++ '''Parses a GRD file into a tree of nodes (from grit.node).
++
++ If filename_or_stream is a stream, 'dir' should point to the directory
++ notionally containing the stream (this feature is only used in unit tests).
++
++ If 'stop_after' is provided, the parsing will stop once the first node
++ with this name has been fully parsed (including all its contents).
++
++ If 'debug' is true, lots of information about the parsing events will be
++ printed out during parsing of the file.
++
++ If 'first_ids_file' is non-empty, it is used to override the setting for the
++ first_ids_file attribute of the <grit> root node. Note that the first_ids_file
++ parameter should be relative to the cwd, even though the first_ids_file
++ attribute of the <grit> node is relative to the grd file.
++
++ If 'target_platform' is set, this is used to determine the target
++ platform of builds, instead of using |sys.platform|.
++
++ Args:
++ filename_or_stream: './bla.xml'
++ dir: None (if filename_or_stream is a filename) or '.'
++ stop_after: 'inputs'
++ first_ids_file: 'GRIT_DIR/../gritsettings/resource_ids'
++ debug: False
++ defines: dictionary of defines, like {'chromeos': '1'}
++ target_platform: None or the value that would be returned by sys.platform
++ on your target platform.
++ predetermined_ids_file: File path to a file containing a pre-determined
++ mapping from resource names to resource ids which will be used to assign
++ resource ids to those resources.
++
++ Return:
++ Subclass of grit.node.base.Node
++
++ Throws:
++ grit.exception.Parsing
++ '''
++
++ if isinstance(filename_or_stream, six.string_types):
++ source = filename_or_stream
++ if dir is None:
++ dir = util.dirname(filename_or_stream)
++ else:
++ source = None
++
++ handler = GrdContentHandler(stop_after=stop_after, debug=debug, dir=dir,
++ defines=defines, tags_to_ignore=tags_to_ignore,
++ target_platform=target_platform, source=source)
++ try:
++ xml.sax.parse(filename_or_stream, handler)
++ except StopParsingException:
++ assert stop_after
++ pass
++ except:
++ if not debug:
++ print("parse exception: run GRIT with the -x flag to debug .grd problems")
++ raise
++
++ if handler.root.name != 'grit':
++ raise exception.MissingElement("root tag must be <grit>")
++
++ if hasattr(handler.root, 'SetOwnDir'):
++ # Fix up the base_dir so it is relative to the input file.
++ assert dir is not None
++ handler.root.SetOwnDir(dir)
++
++ if isinstance(handler.root, misc.GritNode):
++ handler.root.SetPredeterminedIdsFile(predetermined_ids_file)
++ if first_ids_file:
++ # Make the path to the first_ids_file relative to the grd file,
++ # unless it begins with GRIT_DIR.
++ GRIT_DIR_PREFIX = 'GRIT_DIR'
++ if not (first_ids_file.startswith(GRIT_DIR_PREFIX)
++ and first_ids_file[len(GRIT_DIR_PREFIX)] in ['/', '\\']):
++ rel_dir = os.path.relpath(os.getcwd(), dir)
++ first_ids_file = util.normpath(os.path.join(rel_dir, first_ids_file))
++ handler.root.attrs['first_ids_file'] = first_ids_file
++ # Assign first ids to the nodes that don't have them.
++ handler.root.AssignFirstIds(filename_or_stream, defines)
++
++ return handler.root
+
-+- http://code.google.com/p/chromium/wiki/Clang
-+- http://code.google.com/p/chromium/wiki/WritingClangPlugins
-diff --git a/tools/clang/plugins/tests/base_refcounted.cpp b/tools/clang/plugins/tests/base_refcounted.cpp
++
++if __name__ == '__main__':
++ util.ChangeStdoutEncoding()
++ print(six.text_type(Parse(sys.argv[1])))
+diff --git a/tools/grit/grit/grd_reader_unittest.py b/tools/grit/grit/grd_reader_unittest.py
new file mode 100644
-index 0000000000..364a3e888c
+index 0000000000..920a92f9c0
--- /dev/null
-+++ b/tools/clang/plugins/tests/base_refcounted.cpp
-@@ -0,0 +1,72 @@
-+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+// Use of this source code is governed by a BSD-style license that can be
-+// found in the LICENSE file.
++++ b/tools/grit/grit/grd_reader_unittest.py
+@@ -0,0 +1,346 @@
++#!/usr/bin/env python
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
+
-+#include "base_refcounted.h"
++'''Unit tests for grd_reader package'''
+
-+#include <cstddef>
++from __future__ import print_function
+
-+namespace {
++import os
++import sys
++if __name__ == '__main__':
++ sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
++
++import unittest
++
++import six
++from six import StringIO
++
++from grit import exception
++from grit import grd_reader
++from grit import util
++from grit.node import empty
++from grit.node import message
++
++
++class GrdReaderUnittest(unittest.TestCase):
++ def testParsingAndXmlOutput(self):
++ input = u'''<?xml version="1.0" encoding="UTF-8"?>
++<grit base_dir="." current_release="3" latest_public_release="2" source_lang_id="en-US">
++ <release seq="3">
++ <includes>
++ <include file="images/logo.gif" name="ID_LOGO" type="gif" />
++ </includes>
++ <messages>
++ <if expr="True">
++ <message desc="Printed to greet the currently logged in user" name="IDS_GREETING">
++ Hello <ph name="USERNAME">%s<ex>Joi</ex></ph>, how are you doing today?
++ </message>
++ </if>
++ </messages>
++ <structures>
++ <structure file="rc_files/dialogs.rc" name="IDD_NARROW_DIALOG" type="dialog">
++ <skeleton expr="lang == 'fr-FR'" file="bla.rc" variant_of_revision="3" />
++ </structure>
++ <structure file="rc_files/version.rc" name="VS_VERSION_INFO" type="version" />
++ </structures>
++ </release>
++ <translations>
++ <file lang="nl" path="nl_translations.xtb" />
++ </translations>
++ <outputs>
++ <output filename="resource.h" type="rc_header" />
++ <output filename="resource.rc" lang="en-US" type="rc_all" />
++ </outputs>
++</grit>'''
++ pseudo_file = StringIO(input)
++ tree = grd_reader.Parse(pseudo_file, '.')
++ output = six.text_type(tree)
++ expected_output = input.replace(u' base_dir="."', u'')
++ self.assertEqual(expected_output, output)
++ self.failUnless(tree.GetNodeById('IDS_GREETING'))
++
++
++ def testStopAfter(self):
++ input = u'''<?xml version="1.0" encoding="UTF-8"?>
++<grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
++ <outputs>
++ <output filename="resource.h" type="rc_header" />
++ <output filename="resource.rc" lang="en-US" type="rc_all" />
++ </outputs>
++ <release seq="3">
++ <includes>
++ <include type="gif" name="ID_LOGO" file="images/logo.gif"/>
++ </includes>
++ </release>
++</grit>'''
++ pseudo_file = StringIO(input)
++ tree = grd_reader.Parse(pseudo_file, '.', stop_after='outputs')
++ # only an <outputs> child
++ self.failUnless(len(tree.children) == 1)
++ self.failUnless(tree.children[0].name == 'outputs')
++
++ def testLongLinesWithComments(self):
++ input = u'''<?xml version="1.0" encoding="UTF-8"?>
++<grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
++ <release seq="3">
++ <messages>
++ <message name="IDS_GREETING" desc="Printed to greet the currently logged in user">
++ This is a very long line with no linebreaks yes yes it stretches on <!--
++ -->and on <!--
++ -->and on!
++ </message>
++ </messages>
++ </release>
++</grit>'''
++ pseudo_file = StringIO(input)
++ tree = grd_reader.Parse(pseudo_file, '.')
++
++ greeting = tree.GetNodeById('IDS_GREETING')
++ self.failUnless(greeting.GetCliques()[0].GetMessage().GetRealContent() ==
++ 'This is a very long line with no linebreaks yes yes it '
++ 'stretches on and on and on!')
++
++ def doTestAssignFirstIds(self, first_ids_path):
++ input = u'''<?xml version="1.0" encoding="UTF-8"?>
++<grit latest_public_release="2" source_lang_id="en-US" current_release="3"
++ base_dir="." first_ids_file="%s">
++ <release seq="3">
++ <messages>
++ <message name="IDS_TEST" desc="test">
++ test
++ </message>
++ </messages>
++ </release>
++</grit>''' % first_ids_path
++ pseudo_file = StringIO(input)
++ grit_root_dir = os.path.join(os.path.abspath(os.path.dirname(__file__)),
++ '..')
++ fake_input_path = os.path.join(
++ grit_root_dir, "grit/testdata/chrome/app/generated_resources.grd")
++ root = grd_reader.Parse(pseudo_file, os.path.split(fake_input_path)[0])
++ root.AssignFirstIds(fake_input_path, {})
++ messages_node = root.children[0].children[0]
++ self.failUnless(isinstance(messages_node, empty.MessagesNode))
++ self.failUnless(messages_node.attrs["first_id"] !=
++ empty.MessagesNode().DefaultAttributes()["first_id"])
++
++ def testAssignFirstIds(self):
++ self.doTestAssignFirstIds("../../tools/grit/resource_ids")
++
++ def testAssignFirstIdsUseGritDir(self):
++ self.doTestAssignFirstIds("GRIT_DIR/grit/testdata/tools/grit/resource_ids")
++
++ def testAssignFirstIdsMultipleMessages(self):
++ """If there are multiple messages sections, the resource_ids file
++ needs to list multiple first_id values."""
++ input = u'''<?xml version="1.0" encoding="UTF-8"?>
++<grit latest_public_release="2" source_lang_id="en-US" current_release="3"
++ base_dir="." first_ids_file="resource_ids">
++ <release seq="3">
++ <messages>
++ <message name="IDS_TEST" desc="test">
++ test
++ </message>
++ </messages>
++ <messages>
++ <message name="IDS_TEST2" desc="test">
++ test2
++ </message>
++ </messages>
++ </release>
++</grit>'''
++ pseudo_file = StringIO(input)
++ grit_root_dir = os.path.join(os.path.abspath(os.path.dirname(__file__)),
++ '..')
++ fake_input_path = os.path.join(grit_root_dir, "grit/testdata/test.grd")
++
++ root = grd_reader.Parse(pseudo_file, os.path.split(fake_input_path)[0])
++ root.AssignFirstIds(fake_input_path, {})
++ messages_node = root.children[0].children[0]
++ self.assertTrue(isinstance(messages_node, empty.MessagesNode))
++ self.assertEqual('100', messages_node.attrs["first_id"])
++ messages_node = root.children[0].children[1]
++ self.assertTrue(isinstance(messages_node, empty.MessagesNode))
++ self.assertEqual('10000', messages_node.attrs["first_id"])
++
++ def testUseNameForIdAndPpIfdef(self):
++ input = u'''<?xml version="1.0" encoding="UTF-8"?>
++<grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
++ <release seq="3">
++ <messages>
++ <if expr="pp_ifdef('hello')">
++ <message name="IDS_HELLO" use_name_for_id="true">
++ Hello!
++ </message>
++ </if>
++ </messages>
++ </release>
++</grit>'''
++ pseudo_file = StringIO(input)
++ root = grd_reader.Parse(pseudo_file, '.', defines={'hello': '1'})
++
++ # Check if the ID is set to the name. In the past, there was a bug
++ # that caused the ID to be a generated number.
++ hello = root.GetNodeById('IDS_HELLO')
++ self.failUnless(hello.GetCliques()[0].GetId() == 'IDS_HELLO')
++
++ def testUseNameForIdWithIfElse(self):
++ input = u'''<?xml version="1.0" encoding="UTF-8"?>
++<grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
++ <release seq="3">
++ <messages>
++ <if expr="pp_ifdef('hello')">
++ <then>
++ <message name="IDS_HELLO" use_name_for_id="true">
++ Hello!
++ </message>
++ </then>
++ <else>
++ <message name="IDS_HELLO" use_name_for_id="true">
++ Yellow!
++ </message>
++ </else>
++ </if>
++ </messages>
++ </release>
++</grit>'''
++ pseudo_file = StringIO(input)
++ root = grd_reader.Parse(pseudo_file, '.', defines={'hello': '1'})
++
++ # Check if the ID is set to the name. In the past, there was a bug
++ # that caused the ID to be a generated number.
++ hello = root.GetNodeById('IDS_HELLO')
++ self.failUnless(hello.GetCliques()[0].GetId() == 'IDS_HELLO')
++
++ def testPartInclusionAndCorrectSource(self):
++ arbitrary_path_grd = u'''\
++ <grit-part>
++ <message name="IDS_TEST5" desc="test5">test5</message>
++ </grit-part>'''
++ tmp_dir = util.TempDir({'arbitrary_path.grp': arbitrary_path_grd})
++ arbitrary_path_grd_file = tmp_dir.GetPath('arbitrary_path.grp')
++ top_grd = u'''\
++ <grit latest_public_release="2" current_release="3">
++ <release seq="3">
++ <messages>
++ <message name="IDS_TEST" desc="test">
++ test
++ </message>
++ <part file="sub.grp" />
++ <part file="%s" />
++ </messages>
++ </release>
++ </grit>''' % arbitrary_path_grd_file
++ sub_grd = u'''\
++ <grit-part>
++ <message name="IDS_TEST2" desc="test2">test2</message>
++ <part file="subsub.grp" />
++ <message name="IDS_TEST3" desc="test3">test3</message>
++ </grit-part>'''
++ subsub_grd = u'''\
++ <grit-part>
++ <message name="IDS_TEST4" desc="test4">test4</message>
++ </grit-part>'''
++ expected_output = u'''\
++ <grit current_release="3" latest_public_release="2">
++ <release seq="3">
++ <messages>
++ <message desc="test" name="IDS_TEST">
++ test
++ </message>
++ <part file="sub.grp">
++ <message desc="test2" name="IDS_TEST2">
++ test2
++ </message>
++ <part file="subsub.grp">
++ <message desc="test4" name="IDS_TEST4">
++ test4
++ </message>
++ </part>
++ <message desc="test3" name="IDS_TEST3">
++ test3
++ </message>
++ </part>
++ <part file="%s">
++ <message desc="test5" name="IDS_TEST5">
++ test5
++ </message>
++ </part>
++ </messages>
++ </release>
++ </grit>''' % arbitrary_path_grd_file
++
++ with util.TempDir({'sub.grp': sub_grd,
++ 'subsub.grp': subsub_grd}) as tmp_sub_dir:
++ output = grd_reader.Parse(StringIO(top_grd),
++ tmp_sub_dir.GetPath())
++ correct_sources = {
++ 'IDS_TEST': None,
++ 'IDS_TEST2': tmp_sub_dir.GetPath('sub.grp'),
++ 'IDS_TEST3': tmp_sub_dir.GetPath('sub.grp'),
++ 'IDS_TEST4': tmp_sub_dir.GetPath('subsub.grp'),
++ 'IDS_TEST5': arbitrary_path_grd_file,
++ }
+
-+// Unsafe; should error.
-+class AnonymousDerivedProtectedToPublicInImpl
-+ : public ProtectedRefCountedDtorInHeader {
-+ public:
-+ AnonymousDerivedProtectedToPublicInImpl() {}
-+ ~AnonymousDerivedProtectedToPublicInImpl() {}
-+};
++ for node in output.ActiveDescendants():
++ with node:
++ if isinstance(node, message.MessageNode):
++ self.assertEqual(correct_sources[node.attrs.get('name')], node.source)
++ self.assertEqual(expected_output.split(), output.FormatXml().split())
++ tmp_dir.CleanUp()
++
++ def testPartInclusionFailure(self):
++ template = u'''
++ <grit latest_public_release="2" current_release="3">
++ <outputs>
++ %s
++ </outputs>
++ </grit>'''
++
++ part_failures = [
++ (exception.UnexpectedContent, u'<part file="x">fnord</part>'),
++ (exception.UnexpectedChild,
++ u'<part file="x"><output filename="x" type="y" /></part>'),
++ (exception.FileNotFound, u'<part file="yet_created_x" />'),
++ ]
++ for raises, data in part_failures:
++ data = StringIO(template % data)
++ self.assertRaises(raises, grd_reader.Parse, data, '.')
++
++ gritpart_failures = [
++ (exception.UnexpectedAttribute, u'<grit-part file="xyz"></grit-part>'),
++ (exception.MissingElement, u'<output filename="x" type="y" />'),
++ ]
++ for raises, data in gritpart_failures:
++ top_grd = StringIO(template % u'<part file="bad.grp" />')
++ with util.TempDir({'bad.grp': data}) as temp_dir:
++ self.assertRaises(raises, grd_reader.Parse, top_grd, temp_dir.GetPath())
++
++ def testEarlyEnoughPlatformSpecification(self):
++ # This is a regression test for issue
++ # https://code.google.com/p/grit-i18n/issues/detail?id=23
++ grd_text = u'''<?xml version="1.0" encoding="UTF-8"?>
++ <grit latest_public_release="1" current_release="1">
++ <release seq="1">
++ <messages>
++ <if expr="not pp_ifdef('use_titlecase')">
++ <message name="IDS_XYZ">foo</message>
++ </if>
++ <!-- The assumption is that use_titlecase is never true for
++ this platform. When the platform isn't set to 'android'
++ early enough, we get a duplicate message name. -->
++ <if expr="os == '%s'">
++ <message name="IDS_XYZ">boo</message>
++ </if>
++ </messages>
++ </release>
++ </grit>''' % sys.platform
++ with util.TempDir({}) as temp_dir:
++ grd_reader.Parse(StringIO(grd_text), temp_dir.GetPath(),
++ target_platform='android')
+
-+} // namespace
+
-+// Unsafe; should error.
-+class PublicRefCountedDtorInImpl
-+ : public base::RefCounted<PublicRefCountedDtorInImpl> {
-+ public:
-+ PublicRefCountedDtorInImpl() {}
-+ ~PublicRefCountedDtorInImpl() {}
++if __name__ == '__main__':
++ unittest.main()
+diff --git a/tools/grit/grit/grit-todo.xml b/tools/grit/grit/grit-todo.xml
+new file mode 100644
+index 0000000000..b8c20fdfad
+--- /dev/null
++++ b/tools/grit/grit/grit-todo.xml
+@@ -0,0 +1,62 @@
++<?xml version="1.0" encoding="windows-1252"?>
++<TODOLIST FILEFORMAT="6" PROJECTNAME="GRIT" NEXTUNIQUEID="56" FILEVERSION="69" LASTMODIFIED="2005-08-19">
++ <TASK STARTDATESTRING="2005-04-08" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38453.49975694" TITLE="check 'name' attribute is unique" TIMEESTUNITS="H" ID="2" PERCENTDONE="100" STARTDATE="38450.00000000" DONEDATESTRING="2005-04-11" POS="22" DONEDATE="38453.00000000"/>
++ <TASK STARTDATESTRING="2005-04-08" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38488.48189815" TITLE="import id-calculating code" TIMEESTUNITS="H" ID="3" PERCENTDONE="100" STARTDATE="38450.00000000" DONEDATESTRING="2005-05-16" POS="13" DONEDATE="38488.00000000"/>
++ <TASK STARTDATESTRING="2005-05-16" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38488.48209491" TITLE="Import tool for existing translations" TIMEESTUNITS="H" ID="6" PERCENTDONE="100" STARTDATE="38488.00000000" DONEDATESTRING="2005-06-16" POS="12" DONEDATE="38519.00000000"/>
++ <TASK STARTDATESTRING="2005-05-16" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38511.00805556" TITLE="Export XMBs" TIMEESTUNITS="H" ID="8" PERCENTDONE="100" STARTDATE="38488.00000000" DONEDATESTRING="2005-06-08" POS="20" DONEDATE="38511.00000000"/>
++ <TASK STARTDATESTRING="2005-05-16" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38511.00924769" TITLE="Initial Integration" TIMEESTUNITS="H" ID="10" PERCENTDONE="100" STARTDATE="38488.00000000" DONEDATESTRING="2005-06-08" POS="10" DONEDATE="38511.00000000">
++ <TASK STARTDATESTRING="2005-05-16" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38496.54048611" TITLE="parser for %s strings" TIMEESTUNITS="H" ID="4" PERCENTDONE="100" STARTDATE="38488.00000000" DONEDATESTRING="2005-05-24" POS="2" DONEDATE="38496.00000000"/>
++ <TASK STARTDATESTRING="2005-05-16" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38497.00261574" TITLE="import tool for existing RC files" TIMEESTUNITS="H" ID="5" PERCENTDONE="100" STARTDATE="38488.00000000" DONEDATESTRING="2005-05-25" POS="4" DONEDATE="38497.00000000">
++ <TASK STARTDATESTRING="2005-05-24" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38496.92990741" TITLE="handle button value= and img alt= in message HTML text" TIMEESTUNITS="H" ID="22" PERCENTDONE="100" STARTDATE="38496.00000000" DONEDATESTRING="2005-05-24" POS="1" DONEDATE="38496.00000000"/>
++ <TASK STARTDATESTRING="2005-05-24" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38497.00258102" TITLE="&amp;nbsp; bug" TIMEESTUNITS="H" ID="23" PERCENTDONE="100" STARTDATE="38496.00000000" DONEDATESTRING="2005-05-25" POS="2" DONEDATE="38497.00000000"/>
++ </TASK>
++ <TASK STARTDATESTRING="2005-05-16" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38490.61171296" TITLE="grit build" TIMEESTUNITS="H" ID="7" PERCENTDONE="100" STARTDATE="38488.00000000" DONEDATESTRING="2005-05-18" POS="6" DONEDATE="38490.00000000">
++ <TASK STARTDATESTRING="2005-05-16" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38490.61168981" TITLE="use IDs gathered from gatherers for .h file" TIMEESTUNITS="H" ID="20" PERCENTDONE="100" STARTDATE="38488.00000000" DONEDATESTRING="2005-05-18" POS="1" DONEDATE="38490.00000000"/>
++ </TASK>
++ <TASK STARTDATESTRING="2005-05-16" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38504.55199074" TITLE="SCons Integration" TIMEESTUNITS="H" ID="9" PERCENTDONE="100" STARTDATE="38488.00000000" DONEDATESTRING="2005-06-01" POS="1" DONEDATE="38504.00000000"/>
++ <TASK STARTDATESTRING="2005-05-16" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38490.61181713" TITLE="handle includes" TIMEESTUNITS="H" ID="12" PERCENTDONE="100" STARTDATE="38488.00000000" DONEDATESTRING="2005-05-18" POS="5" DONEDATE="38490.00000000"/>
++ <TASK STARTDATESTRING="2005-05-24" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38507.98567130" TITLE="output translated HTML templates" TIMEESTUNITS="H" ID="25" PERCENTDONE="100" STARTDATE="38496.00000000" DONEDATESTRING="2005-06-04" POS="3" DONEDATE="38507.00000000"/>
++ <TASK STARTDATESTRING="2005-05-31" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38507.99394676" TITLE="bug: re-escape too much in RC dialogs etc." TIMEESTUNITS="H" ID="38" PERCENTDONE="100" STARTDATE="38503.00000000" DONEDATESTRING="2005-06-04" POS="7" DONEDATE="38507.00000000"/>
++ </TASK>
++ <TASK STARTDATESTRING="2005-05-16" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38489.46444444" TITLE="handle structure variants" TIMEESTUNITS="H" ID="11" PERCENTDONE="100" STARTDATE="38488.00000000" DONEDATESTRING="2005-06-16" POS="15" DONEDATE="38519.00000000"/>
++ <TASK STARTDATESTRING="2005-05-16" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38489.46456019" TITLE="handle include variants" TIMEESTUNITS="H" ID="13" PERCENTDONE="100" STARTDATE="38488.00000000" DONEDATESTRING="2005-06-16" POS="17" DONEDATE="38519.00000000"/>
++ <TASK STARTDATESTRING="2005-05-16" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38489.46537037" TITLE="handle translateable text for includes (e.g. image text)" TIMEESTUNITS="H" ID="14" PERCENTDONE="100" STARTDATE="38488.00000000" DONEDATESTRING="2005-06-16" POS="14" DONEDATE="38519.00000000"/>
++ <TASK STARTDATESTRING="2005-05-16" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38489.46712963" TITLE="ddoc" TIMEESTUNITS="H" ID="15" STARTDATE="38488.00000000" POS="4">
++ <TASK STARTDATESTRING="2005-05-16" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38489.46718750" TITLE="review comments miket" TIMEESTUNITS="H" ID="16" STARTDATE="38488.00000000" POS="2"/>
++ <TASK STARTDATESTRING="2005-05-16" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38489.46722222" TITLE="review comments pdoyle" TIMEESTUNITS="H" ID="17" STARTDATE="38488.00000000" POS="1"/>
++ <TASK STARTDATESTRING="2005-05-16" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38489.46732639" TITLE="remove 'extkey' from structure" TIMEESTUNITS="H" ID="18" STARTDATE="38488.00000000" POS="3"/>
++ <TASK STARTDATESTRING="2005-05-16" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38489.53537037" TITLE="add 'encoding' to structure" TIMEESTUNITS="H" ID="19" STARTDATE="38488.00000000" POS="6"/>
++ <TASK STARTDATESTRING="2005-05-31" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38503.55304398" TITLE="document limitation: emitter doesn't emit the translated HTML templates" TIMEESTUNITS="H" ID="30" STARTDATE="38503.00000000" POS="4"/>
++ <TASK STARTDATESTRING="2005-05-31" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38504.58541667" TITLE="add 'internal_comment' to &lt;message&gt;" TIMEESTUNITS="H" ID="32" STARTDATE="38503.00000000" POS="5"/>
++ <TASK STARTDATESTRING="2005-05-31" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38505.73391204" TITLE="&lt;outputs&gt; can not have paths (because of SCons integration - goes to build dir)" TIMEESTUNITS="H" ID="36" STARTDATE="38503.00000000" POS="9"/>
++ <TASK STARTDATESTRING="2005-05-31" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38506.64265046" TITLE="&lt;identifers&gt; and &lt;identifier&gt; nodes" TIMEESTUNITS="H" ID="37" STARTDATE="38503.00000000" POS="10"/>
++ <TASK STARTDATESTRING="2005-06-23" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38526.62344907" TITLE="&lt;structure&gt; can have 'exclude_from_rc' attribute (default false)" TIMEESTUNITS="H" ID="47" STARTDATE="38526.00000000" POS="8"/>
++ <TASK STARTDATESTRING="2005-06-23" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38531.94135417" TITLE="add 'enc_check' to &lt;grit&gt;" TIMEESTUNITS="H" ID="48" STARTDATE="38526.00000000" POS="7"/>
++ </TASK>
++ <TASK STARTDATESTRING="2005-05-18" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38492.51549769" TITLE="handle nontranslateable messages (in MessageClique?)" TIMEESTUNITS="H" ID="21" PERCENTDONE="100" STARTDATE="38490.00000000" DONEDATESTRING="2005-06-16" POS="16" DONEDATE="38519.00000000"/>
++ <TASK STARTDATESTRING="2005-05-24" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38505.70454861" TITLE="ask cprince about SCons builder in new mk system" TIMEESTUNITS="H" ID="24" PERCENTDONE="100" STARTDATE="38496.00000000" DONEDATESTRING="2005-06-02" POS="25" DONEDATE="38505.00000000"/>
++ <TASK STARTDATESTRING="2005-05-24" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38504.57436343" TITLE="fix AOL resource in trunk (&quot;???????&quot;)" TIMEESTUNITS="H" ID="26" PERCENTDONE="100" STARTDATE="38496.00000000" DONEDATESTRING="2005-06-01" POS="19" DONEDATE="38504.00000000"/>
++ <TASK STARTDATESTRING="2005-05-24" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38498.53893519" TITLE="rc_all vs. rc_translateable vs. rc_nontranslateable" TIMEESTUNITS="H" ID="27" PERCENTDONE="100" STARTDATE="38496.00000000" DONEDATESTRING="2005-06-16" POS="6" DONEDATE="38519.00000000"/>
++ <TASK STARTDATESTRING="2005-05-31" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38509.45532407" TITLE="make separate .grb &quot;outputs&quot; file (and change SCons integ) (??)" TIMEESTUNITS="H" ID="28" PERCENTDONE="100" STARTDATE="38503.00000000" DONEDATESTRING="2005-06-06" POS="8" DONEDATE="38509.00000000"/>
++ <TASK STARTDATESTRING="2005-05-31" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38511.00939815" TITLE="fix unit tests so they run from any directory" TIMEESTUNITS="H" ID="33" PERCENTDONE="100" STARTDATE="38503.00000000" DONEDATESTRING="2005-06-08" POS="18" DONEDATE="38511.00000000"/>
++ <TASK STARTDATESTRING="2005-05-31" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38508.96640046" TITLE="Change R4 tool to CC correct team(s) on GRIT changes" TIMEESTUNITS="H" ID="39" PERCENTDONE="100" STARTDATE="38503.00000000" DONEDATESTRING="2005-06-05" POS="23" DONEDATE="38508.00000000"/>
++ <TASK STARTDATESTRING="2005-06-07" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38511.00881944" TITLE="Document why wrapper.rc" TIMEESTUNITS="H" ID="40" PERCENTDONE="100" STARTDATE="38510.00000000" DONEDATESTRING="2005-06-08" POS="21" DONEDATE="38511.00000000"/>
++ <TASK STARTDATESTRING="2005-06-08" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38511.00804398" TITLE="import XTBs" TIMEESTUNITS="H" ID="41" PERCENTDONE="100" STARTDATE="38511.00000000" DONEDATESTRING="2005-06-16" POS="11" DONEDATE="38519.00000000"/>
++ <TASK STARTDATESTRING="2005-06-08" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38511.00875000" TITLE="Nightly build integration" TIMEESTUNITS="H" ID="42" STARTDATE="38511.00000000" POS="3"/>
++ <TASK STARTDATESTRING="2005-06-08" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38511.00891204" TITLE="BUGS" TIMEESTUNITS="H" ID="43" STARTDATE="38511.00000000" POS="24">
++ <TASK STARTDATESTRING="2005-06-08" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38513.03375000" TITLE="Should report error if RC-section structure refers to does not exist" TIMEESTUNITS="H" ID="44" PERCENTDONE="100" STARTDATE="38511.00000000" DONEDATESTRING="2005-06-10" POS="1" DONEDATE="38513.00000000"/>
++ </TASK>
++ <TASK STARTDATESTRING="2005-06-08" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38511.00981481" TITLE="NEW FEATURES" TIMEESTUNITS="H" ID="45" PERCENTDONE="100" STARTDATE="38511.00000000" DONEDATESTRING="2005-06-16" POS="7" DONEDATE="38519.00000000">
++ <TASK STARTDATESTRING="2005-05-31" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38505.70077546" TITLE="Implement line-continuation feature (\ at end of line?)" TIMEESTUNITS="H" ID="34" PERCENTDONE="100" STARTDATE="38503.00000000" DONEDATESTRING="2005-06-16" POS="1" DONEDATE="38519.00000000"/>
++ <TASK STARTDATESTRING="2005-05-31" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38505.70262731" TITLE="Implement conditional inclusion &amp; reflect the conditionals from R3 RC file" TIMEESTUNITS="H" ID="35" PERCENTDONE="100" STARTDATE="38503.00000000" DONEDATESTRING="2005-06-16" POS="2" DONEDATE="38519.00000000"/>
++ </TASK>
++ <TASK STARTDATESTRING="2005-06-08" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38511.01046296" TITLE="TC integration (one-way TO the TC)" TIMEESTUNITS="H" ID="46" PERCENTDONE="100" STARTDATE="38511.00000000" DONEDATESTRING="2005-06-16" POS="5" DONEDATE="38519.00000000"/>
++ <TASK STARTDATESTRING="2005-06-30" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38533.59072917" TITLE="bazaar20 ad for GRIT help" TIMEESTUNITS="H" ID="49" STARTDATE="38533.00000000" POS="2">
++ <TASK STARTDATESTRING="2005-08-19" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38583.72346065" TITLE="bazaar20 ideas" TIMEESTUNITS="H" ID="51" STARTDATE="38583.00000000" POS="1">
++ <TASK STARTDATESTRING="2005-08-19" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38583.72354167" TITLE="GUI for adding/editing messages" TIMEESTUNITS="H" ID="52" STARTDATE="38583.00000000" POS="2"/>
++ <TASK STARTDATESTRING="2005-08-19" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38583.72365741" TITLE="XLIFF import/export" TIMEESTUNITS="H" ID="54" STARTDATE="38583.00000000" POS="1"/>
++ </TASK>
++ </TASK>
++ <TASK STARTDATESTRING="2005-06-30" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38583.73721065" TITLE="internal_comment for all resource nodes (not just &lt;message&gt;)" TIMEESTUNITS="H" ID="50" PERCENTDONE="100" STARTDATE="38533.00000000" DONEDATESTRING="2005-08-19" POS="9" DONEDATE="38583.73721065"/>
++ <TASK STARTDATESTRING="2005-08-19" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38583.73743056" TITLE="Preserve XML comments - this gives us line continuation and more" TIMEESTUNITS="H" ID="55" STARTDATE="38583.72326389" POS="1"/>
++</TODOLIST>
+diff --git a/tools/grit/grit/grit_runner.py b/tools/grit/grit/grit_runner.py
+new file mode 100644
+index 0000000000..26aa0d58c4
+--- /dev/null
++++ b/tools/grit/grit/grit_runner.py
+@@ -0,0 +1,334 @@
++#!/usr/bin/env python
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
+
-+ private:
-+ friend class base::RefCounted<PublicRefCountedDtorInImpl>;
-+};
++"""Command processor for GRIT. This is the script you invoke to run the various
++GRIT tools.
++"""
+
-+class Foo {
-+ public:
-+ class BarInterface {
-+ protected:
-+ virtual ~BarInterface() {}
-+ };
++from __future__ import print_function
+
-+ typedef base::RefCounted<BarInterface> RefCountedBar;
-+ typedef RefCountedBar AnotherTypedef;
-+};
++import os
++import sys
++if __name__ == '__main__':
++ sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
++
++import getopt
++
++from grit import util
++
++import grit.extern.FP
++
++# Tool info factories; these import only within each factory to avoid
++# importing most of the GRIT code until required.
++def ToolFactoryBuild():
++ import grit.tool.build
++ return grit.tool.build.RcBuilder()
++
++def ToolFactoryBuildInfo():
++ import grit.tool.buildinfo
++ return grit.tool.buildinfo.DetermineBuildInfo()
++
++def ToolFactoryCount():
++ import grit.tool.count
++ return grit.tool.count.CountMessage()
++
++def ToolFactoryDiffStructures():
++ import grit.tool.diff_structures
++ return grit.tool.diff_structures.DiffStructures()
++
++def ToolFactoryMenuTranslationsFromParts():
++ import grit.tool.menu_from_parts
++ return grit.tool.menu_from_parts.MenuTranslationsFromParts()
++
++def ToolFactoryNewGrd():
++ import grit.tool.newgrd
++ return grit.tool.newgrd.NewGrd()
++
++def ToolFactoryResizeDialog():
++ import grit.tool.resize
++ return grit.tool.resize.ResizeDialog()
++
++def ToolFactoryRc2Grd():
++ import grit.tool.rc2grd
++ return grit.tool.rc2grd.Rc2Grd()
++
++def ToolFactoryTest():
++ import grit.tool.test
++ return grit.tool.test.TestTool()
++
++def ToolFactoryTranslationToTc():
++ import grit.tool.transl2tc
++ return grit.tool.transl2tc.TranslationToTc()
++
++def ToolFactoryUnit():
++ import grit.tool.unit
++ return grit.tool.unit.UnitTestTool()
++
++
++def ToolFactoryUpdateResourceIds():
++ import grit.tool.update_resource_ids
++ return grit.tool.update_resource_ids.UpdateResourceIds()
++
++
++def ToolFactoryXmb():
++ import grit.tool.xmb
++ return grit.tool.xmb.OutputXmb()
++
++def ToolAndroid2Grd():
++ import grit.tool.android2grd
++ return grit.tool.android2grd.Android2Grd()
++
++# Keys for the following map
++_FACTORY = 1
++_REQUIRES_INPUT = 2
++_HIDDEN = 3 # optional key - presence indicates tool is hidden
++
++# Maps tool names to the tool's module. Done as a list of (key, value) tuples
++# instead of a map to preserve ordering.
++_TOOLS = [
++ ['android2grd', {
++ _FACTORY: ToolAndroid2Grd,
++ _REQUIRES_INPUT: False
++ }],
++ ['build', {
++ _FACTORY: ToolFactoryBuild,
++ _REQUIRES_INPUT: True
++ }],
++ ['buildinfo', {
++ _FACTORY: ToolFactoryBuildInfo,
++ _REQUIRES_INPUT: True
++ }],
++ ['count', {
++ _FACTORY: ToolFactoryCount,
++ _REQUIRES_INPUT: True
++ }],
++ [
++ 'menufromparts',
++ {
++ _FACTORY: ToolFactoryMenuTranslationsFromParts,
++ _REQUIRES_INPUT: True,
++ _HIDDEN: True
++ }
++ ],
++ ['newgrd', {
++ _FACTORY: ToolFactoryNewGrd,
++ _REQUIRES_INPUT: False
++ }],
++ ['rc2grd', {
++ _FACTORY: ToolFactoryRc2Grd,
++ _REQUIRES_INPUT: False
++ }],
++ ['resize', {
++ _FACTORY: ToolFactoryResizeDialog,
++ _REQUIRES_INPUT: True
++ }],
++ ['sdiff', {
++ _FACTORY: ToolFactoryDiffStructures,
++ _REQUIRES_INPUT: False
++ }],
++ ['test', {
++ _FACTORY: ToolFactoryTest,
++ _REQUIRES_INPUT: True,
++ _HIDDEN: True
++ }],
++ [
++ 'transl2tc',
++ {
++ _FACTORY: ToolFactoryTranslationToTc,
++ _REQUIRES_INPUT: False
++ }
++ ],
++ ['unit', {
++ _FACTORY: ToolFactoryUnit,
++ _REQUIRES_INPUT: False
++ }],
++ [
++ 'update_resource_ids',
++ {
++ _FACTORY: ToolFactoryUpdateResourceIds,
++ _REQUIRES_INPUT: False
++ }
++ ],
++ ['xmb', {
++ _FACTORY: ToolFactoryXmb,
++ _REQUIRES_INPUT: True
++ }],
++]
++
++
++def PrintUsage():
++ tool_list = ''
++ for (tool, info) in _TOOLS:
++ if not _HIDDEN in info:
++ tool_list += ' %-12s %s\n' % (
++ tool, info[_FACTORY]().ShortDescription())
++
++ print("""GRIT - the Google Resource and Internationalization Tool
++
++Usage: grit [GLOBALOPTIONS] TOOL [args to tool]
++
++Global options:
++
++ -i INPUT Specifies the INPUT file to use (a .grd file). If this is not
++ specified, GRIT will look for the environment variable GRIT_INPUT.
++ If it is not present either, GRIT will try to find an input file
++ named 'resource.grd' in the current working directory.
++
++ -h MODULE Causes GRIT to use MODULE.UnsignedFingerPrint instead of
++ grit.extern.FP.UnsignedFingerprint. MODULE must be
++ available somewhere in the PYTHONPATH search path.
++
++ -v Print more verbose runtime information.
++
++ -x Print extremely verbose runtime information. Implies -v
++
++ -p FNAME Specifies that GRIT should profile its execution and output the
++ results to the file FNAME.
++
++Tools:
++
++ TOOL can be one of the following:
++%s
++ For more information on how to use a particular tool, and the specific
++ arguments you can send to that tool, execute 'grit help TOOL'
++""" % (tool_list))
++
++
++class Options(object):
++ """Option storage and parsing."""
++
++ def __init__(self):
++ self.hash = None
++ self.input = None
++ self.verbose = False
++ self.extra_verbose = False
++ self.output_stream = sys.stdout
++ self.profile_dest = None
++
++ def ReadOptions(self, args):
++ """Reads options from the start of args and returns the remainder."""
++ (opts, args) = getopt.getopt(args, 'vxi:p:h:', ('help',))
++ for (key, val) in opts:
++ if key == '-h': self.hash = val
++ elif key == '-i': self.input = val
++ elif key == '-v':
++ self.verbose = True
++ util.verbose = True
++ elif key == '-x':
++ self.verbose = True
++ util.verbose = True
++ self.extra_verbose = True
++ util.extra_verbose = True
++ elif key == '-p': self.profile_dest = val
++ elif key == '--help':
++ PrintUsage()
++ sys.exit(0)
++
++ if not self.input:
++ if 'GRIT_INPUT' in os.environ:
++ self.input = os.environ['GRIT_INPUT']
++ else:
++ self.input = 'resource.grd'
++
++ return args
++
++ def __repr__(self):
++ return '(verbose: %d, input: %s)' % (
++ self.verbose, self.input)
++
++
++def _GetToolInfo(tool):
++ """Returns the info map for the tool named 'tool' or None if there is no
++ such tool."""
++ matches = [t for t in _TOOLS if t[0] == tool]
++ if not matches:
++ return None
++ else:
++ return matches[0][1]
++
++
++def Main(args=None):
++ """Parses arguments and does the appropriate thing."""
++ util.ChangeStdoutEncoding()
++
++ # Support for setuptools console wrappers.
++ if args is None:
++ args = sys.argv[1:]
++
++ options = Options()
++ try:
++ args = options.ReadOptions(args) # args may be shorter after this
++ except getopt.GetoptError as e:
++ print("grit:", str(e))
++ print("Try running 'grit help' for valid options.")
++ return 1
++ if not args:
++ print("No tool provided. Try running 'grit help' for a list of tools.")
++ return 2
++
++ tool = args[0]
++ if tool == 'help':
++ if len(args) == 1:
++ PrintUsage()
++ return 0
++ else:
++ tool = args[1]
++ if not _GetToolInfo(tool):
++ print("No such tool. Try running 'grit help' for a list of tools.")
++ return 2
++
++ print("Help for 'grit %s' (for general help, run 'grit help'):\n" %
++ (tool,))
++ _GetToolInfo(tool)[_FACTORY]().ShowUsage()
++ return 0
++ if not _GetToolInfo(tool):
++ print("No such tool. Try running 'grit help' for a list of tools.")
++ return 2
++
++ try:
++ if _GetToolInfo(tool)[_REQUIRES_INPUT]:
++ os.stat(options.input)
++ except OSError:
++ print('Input file %s not found.\n'
++ 'To specify a different input file:\n'
++ ' 1. Use the GRIT_INPUT environment variable.\n'
++ ' 2. Use the -i command-line option. This overrides '
++ 'GRIT_INPUT.\n'
++ ' 3. Specify neither GRIT_INPUT or -i and GRIT will try to load '
++ "'resource.grd'\n"
++ ' from the current directory.' % options.input)
++ return 2
++
++ if options.hash:
++ grit.extern.FP.UseUnsignedFingerPrintFromModule(options.hash)
++
++ try:
++ toolobject = _GetToolInfo(tool)[_FACTORY]()
++ if options.profile_dest:
++ import hotshot
++ prof = hotshot.Profile(options.profile_dest)
++ return prof.runcall(toolobject.Run, options, args[1:])
++ else:
++ return toolobject.Run(options, args[1:])
++ except getopt.GetoptError as e:
++ print("grit: %s: %s" % (tool, str(e)))
++ print("Try running 'grit help %s' for valid options." % (tool,))
++ return 1
+
-+class Baz {
-+ public:
-+ typedef typename Foo::AnotherTypedef MyLocalTypedef;
-+};
+
-+// Unsafe; should error.
-+class UnsafeTypedefChainInImpl : public Baz::MyLocalTypedef {
-+ public:
-+ UnsafeTypedefChainInImpl() {}
-+ ~UnsafeTypedefChainInImpl() {}
-+};
++if __name__ == '__main__':
++ sys.path.append(
++ os.path.join(
++ os.path.dirname(
++ os.path.dirname(os.path.dirname(os.path.abspath(__file__)))),
++ 'diagnosis'))
++ try:
++ import crbug_1001171
++ with crbug_1001171.DumpStateOnLookupError():
++ sys.exit(Main(sys.argv[1:]))
++ except ImportError:
++ pass
++
++ sys.exit(Main(sys.argv[1:]))
+diff --git a/tools/grit/grit/grit_runner_unittest.py b/tools/grit/grit/grit_runner_unittest.py
+new file mode 100644
+index 0000000000..1487001d81
+--- /dev/null
++++ b/tools/grit/grit/grit_runner_unittest.py
+@@ -0,0 +1,42 @@
++#!/usr/bin/env python
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++'''Unit tests for grit.py'''
++
++from __future__ import print_function
+
-+int main() {
-+ PublicRefCountedDtorInHeader bad;
-+ PublicRefCountedDtorInImpl also_bad;
++import os
++import sys
++if __name__ == '__main__':
++ sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
+
-+ ProtectedRefCountedDtorInHeader* protected_ok = NULL;
-+ PrivateRefCountedDtorInHeader* private_ok = NULL;
++import unittest
+
-+ DerivedProtectedToPublicInHeader still_bad;
-+ PublicRefCountedThreadSafeDtorInHeader another_bad_variation;
-+ AnonymousDerivedProtectedToPublicInImpl and_this_is_bad_too;
-+ ImplicitDerivedProtectedToPublicInHeader bad_yet_again;
-+ UnsafeTypedefChainInImpl and_again_this_is_bad;
++from six import StringIO
+
-+ WebKitPublicDtorInHeader ignored;
-+ WebKitDerivedPublicDtorInHeader still_ignored;
++from grit import util
++import grit.grit_runner
+
-+ return 0;
-+}
-diff --git a/tools/clang/plugins/tests/base_refcounted.h b/tools/clang/plugins/tests/base_refcounted.h
++class OptionArgsUnittest(unittest.TestCase):
++ def setUp(self):
++ self.buf = StringIO()
++ self.old_stdout = sys.stdout
++ sys.stdout = self.buf
++
++ def tearDown(self):
++ sys.stdout = self.old_stdout
++
++ def testSimple(self):
++ grit.grit_runner.Main(['-i',
++ util.PathFromRoot('grit/testdata/simple-input.xml'),
++ 'test', 'bla', 'voff', 'ga'])
++ output = self.buf.getvalue()
++ self.failUnless(output.count("'test'") == 0) # tool name doesn't occur
++ self.failUnless(output.count('bla'))
++ self.failUnless(output.count('simple-input.xml'))
++
++
++if __name__ == '__main__':
++ unittest.main()
+diff --git a/tools/grit/grit/lazy_re.py b/tools/grit/grit/lazy_re.py
new file mode 100644
-index 0000000000..1e53215997
+index 0000000000..5c461e87e7
--- /dev/null
-+++ b/tools/clang/plugins/tests/base_refcounted.h
-@@ -0,0 +1,121 @@
-+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+// Use of this source code is governed by a BSD-style license that can be
-+// found in the LICENSE file.
++++ b/tools/grit/grit/lazy_re.py
+@@ -0,0 +1,46 @@
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
+
-+#ifndef BASE_REFCOUNTED_H_
-+#define BASE_REFCOUNTED_H_
++'''In GRIT, we used to compile a lot of regular expressions at parse
++time. Since many of them never get used, we use lazy_re to compile
++them on demand the first time they are used, thus speeding up startup
++time in some cases.
++'''
++
++from __future__ import print_function
++
++import re
++
++
++class LazyRegexObject(object):
++ '''This object creates a RegexObject with the arguments passed in
++ its constructor, the first time any attribute except the several on
++ the class itself is accessed. This accomplishes lazy compilation of
++ the regular expression while maintaining a nearly-identical
++ interface.
++ '''
++
++ def __init__(self, *args, **kwargs):
++ self._stash_args = args
++ self._stash_kwargs = kwargs
++ self._lazy_re = None
++
++ def _LazyInit(self):
++ if not self._lazy_re:
++ self._lazy_re = re.compile(*self._stash_args, **self._stash_kwargs)
++
++ def __getattribute__(self, name):
++ if name in ('_LazyInit', '_lazy_re', '_stash_args', '_stash_kwargs'):
++ return object.__getattribute__(self, name)
++ else:
++ self._LazyInit()
++ return getattr(self._lazy_re, name)
++
++
++def compile(*args, **kwargs):
++ '''Creates a LazyRegexObject that, when invoked on, will compile a
++ re.RegexObject (via re.compile) with the same arguments passed to
++ this function, and delegate almost all of its methods to it.
++ '''
++ return LazyRegexObject(*args, **kwargs)
+diff --git a/tools/grit/grit/lazy_re_unittest.py b/tools/grit/grit/lazy_re_unittest.py
+new file mode 100644
+index 0000000000..8488b454ee
+--- /dev/null
++++ b/tools/grit/grit/lazy_re_unittest.py
+@@ -0,0 +1,40 @@
++#!/usr/bin/env python
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
+
-+namespace base {
++'''Unit test for lazy_re.
++'''
+
-+template <typename T>
-+class RefCounted {
-+ public:
-+ RefCounted() {}
-+ ~RefCounted() {}
-+};
++from __future__ import print_function
+
-+template <typename T>
-+class RefCountedThreadSafe {
-+ public:
-+ RefCountedThreadSafe() {}
-+ ~RefCountedThreadSafe() {}
-+};
++import os
++import sys
++if __name__ == '__main__':
++ sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
+
-+} // namespace base
++import re
++import unittest
+
-+// Ignore classes whose inheritance tree ends in WebKit's RefCounted base
-+// class. Though prone to error, this pattern is very prevalent in WebKit
-+// code, so do not issue any warnings.
-+namespace WebKit {
++from grit import lazy_re
+
-+template <typename T>
-+class RefCounted {
-+ public:
-+ RefCounted() {}
-+ ~RefCounted() {}
-+};
+
-+} // namespace WebKit
++class LazyReUnittest(unittest.TestCase):
+
-+// Unsafe; should error.
-+class PublicRefCountedDtorInHeader
-+ : public base::RefCounted<PublicRefCountedDtorInHeader> {
-+ public:
-+ PublicRefCountedDtorInHeader() {}
-+ ~PublicRefCountedDtorInHeader() {}
++ def testCreatedOnlyOnDemand(self):
++ rex = lazy_re.compile('bingo')
++ self.assertEqual(None, rex._lazy_re)
++ self.assertTrue(rex.match('bingo'))
++ self.assertNotEqual(None, rex._lazy_re)
+
-+ private:
-+ friend class base::RefCounted<PublicRefCountedDtorInHeader>;
-+};
++ def testJustKwargsWork(self):
++ rex = lazy_re.compile(flags=re.I, pattern='BiNgO')
++ self.assertTrue(rex.match('bingo'))
++
++ def testPositionalAndKwargsWork(self):
++ rex = lazy_re.compile('BiNgO', flags=re.I)
++ self.assertTrue(rex.match('bingo'))
+
-+// Unsafe; should error.
-+class PublicRefCountedThreadSafeDtorInHeader
-+ : public base::RefCountedThreadSafe<
-+ PublicRefCountedThreadSafeDtorInHeader> {
-+ public:
-+ PublicRefCountedThreadSafeDtorInHeader() {}
-+ ~PublicRefCountedThreadSafeDtorInHeader() {}
-+
-+ private:
-+ friend class base::RefCountedThreadSafe<
-+ PublicRefCountedThreadSafeDtorInHeader>;
-+};
+
-+// Safe; should not have errors.
-+class ProtectedRefCountedDtorInHeader
-+ : public base::RefCounted<ProtectedRefCountedDtorInHeader> {
-+ public:
-+ ProtectedRefCountedDtorInHeader() {}
++if __name__ == '__main__':
++ unittest.main()
+diff --git a/tools/grit/grit/node/__init__.py b/tools/grit/grit/node/__init__.py
+new file mode 100644
+index 0000000000..2fc0d3360c
+--- /dev/null
++++ b/tools/grit/grit/node/__init__.py
+@@ -0,0 +1,8 @@
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
+
-+ protected:
-+ ~ProtectedRefCountedDtorInHeader() {}
++'''Package 'grit.node'
++'''
+
-+ private:
-+ friend class base::RefCounted<ProtectedRefCountedDtorInHeader>;
-+};
++pass
+diff --git a/tools/grit/grit/node/base.py b/tools/grit/grit/node/base.py
+new file mode 100644
+index 0000000000..40859d301d
+--- /dev/null
++++ b/tools/grit/grit/node/base.py
+@@ -0,0 +1,670 @@
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
+
-+// Safe; should not have errors.
-+class PrivateRefCountedDtorInHeader
-+ : public base::RefCounted<PrivateRefCountedDtorInHeader> {
-+ public:
-+ PrivateRefCountedDtorInHeader() {}
++'''Base types for nodes in a GRIT resource tree.
++'''
+
-+ private:
-+ ~PrivateRefCountedDtorInHeader() {}
-+ friend class base::RefCounted<PrivateRefCountedDtorInHeader>;
-+};
++from __future__ import print_function
+
-+// Unsafe; A grandchild class ends up exposing their parent and grandparent's
-+// destructors.
-+class DerivedProtectedToPublicInHeader
-+ : public ProtectedRefCountedDtorInHeader {
-+ public:
-+ DerivedProtectedToPublicInHeader() {}
-+ ~DerivedProtectedToPublicInHeader() {}
-+};
++import ast
++import os
++import struct
++import sys
++from xml.sax import saxutils
++
++import six
++
++from grit import constants
++from grit import clique
++from grit import exception
++from grit import util
++from grit.node import brotli_util
++import grit.format.gzip_string
++
++
++class Node(object):
++ '''An item in the tree that has children.'''
++
++ # Valid content types that can be returned by _ContentType()
++ _CONTENT_TYPE_NONE = 0 # No CDATA content but may have children
++ _CONTENT_TYPE_CDATA = 1 # Only CDATA, no children.
++ _CONTENT_TYPE_MIXED = 2 # CDATA and children, possibly intermingled
++
++ # Types of files to be compressed by default.
++ _COMPRESS_BY_DEFAULT_EXTENSIONS = ('.js', '.html', '.css', '.svg')
++
++ # Default nodes to not whitelist skipped
++ _whitelist_marked_as_skip = False
++
++ # A class-static cache to speed up EvaluateExpression().
++ # Keys are expressions (e.g. 'is_ios and lang == "fr"'). Values are tuples
++ # (code, variables_in_expr) where code is the compiled expression and can be
++ # directly eval'd, and variables_in_expr is the list of variable and method
++ # names used in the expression (e.g. ['is_ios', 'lang']).
++ eval_expr_cache = {}
++
++ def __init__(self):
++ self.children = [] # A list of child elements
++ self.mixed_content = [] # A list of u'' and/or child elements (this
++ # duplicates 'children' but
++ # is needed to preserve markup-type content).
++ self.name = u'' # The name of this element
++ self.attrs = {} # The set of attributes (keys to values)
++ self.parent = None # Our parent unless we are the root element.
++ self.uberclique = None # Allows overriding uberclique for parts of tree
++ self.source = None # File that this node was parsed from
++
++ # This context handler allows you to write "with node:" and get a
++ # line identifying the offending node if an exception escapes from the body
++ # of the with statement.
++ def __enter__(self):
++ return self
++
++ def __exit__(self, exc_type, exc_value, traceback):
++ if exc_type is not None:
++ print(u'Error processing node %s: %s' % (six.text_type(self), exc_value))
++
++ def __iter__(self):
++ '''A preorder iteration through the tree that this node is the root of.'''
++ return self.Preorder()
++
++ def Preorder(self):
++ '''Generator that generates first this node, then the same generator for
++ any child nodes.'''
++ yield self
++ for child in self.children:
++ for iterchild in child.Preorder():
++ yield iterchild
++
++ def ActiveChildren(self):
++ '''Returns the children of this node that should be included in the current
++ configuration. Overridden by <if>.'''
++ return [node for node in self.children if not node.WhitelistMarkedAsSkip()]
++
++ def ActiveDescendants(self):
++ '''Yields the current node and all descendants that should be included in
++ the current configuration, in preorder.'''
++ yield self
++ for child in self.ActiveChildren():
++ for descendant in child.ActiveDescendants():
++ yield descendant
++
++ def GetRoot(self):
++ '''Returns the root Node in the tree this Node belongs to.'''
++ curr = self
++ while curr.parent:
++ curr = curr.parent
++ return curr
++
++ # TODO(joi) Use this (currently untested) optimization?:
++ #if hasattr(self, '_root'):
++ # return self._root
++ #curr = self
++ #while curr.parent and not hasattr(curr, '_root'):
++ # curr = curr.parent
++ #if curr.parent:
++ # self._root = curr._root
++ #else:
++ # self._root = curr
++ #return self._root
++
++ def StartParsing(self, name, parent):
++ '''Called at the start of parsing.
++
++ Args:
++ name: u'elementname'
++ parent: grit.node.base.Node or subclass or None
++ '''
++ assert isinstance(name, six.string_types)
++ assert not parent or isinstance(parent, Node)
++ self.name = name
++ self.parent = parent
++
++ def AddChild(self, child):
++ '''Adds a child to the list of children of this node, if it is a valid
++ child for the node.'''
++ assert isinstance(child, Node)
++ if (not self._IsValidChild(child) or
++ self._ContentType() == self._CONTENT_TYPE_CDATA):
++ explanation = 'invalid child %s for parent %s' % (str(child), self.name)
++ raise exception.UnexpectedChild(explanation)
++ self.children.append(child)
++ self.mixed_content.append(child)
++
++ def RemoveChild(self, child_id):
++ '''Removes the first node that has a "name" attribute which
++ matches "child_id" in the list of immediate children of
++ this node.
++
++ Args:
++ child_id: String identifying the child to be removed
++ '''
++ index = 0
++ # Safe not to copy since we only remove the first element found
++ for child in self.children:
++ name_attr = child.attrs['name']
++ if name_attr == child_id:
++ self.children.pop(index)
++ self.mixed_content.pop(index)
++ break
++ index += 1
++
++ def AppendContent(self, content):
++ '''Appends a chunk of text as content of this node.
++
++ Args:
++ content: u'hello'
++
++ Return:
++ None
++ '''
++ assert isinstance(content, six.string_types)
++ if self._ContentType() != self._CONTENT_TYPE_NONE:
++ self.mixed_content.append(content)
++ elif content.strip() != '':
++ raise exception.UnexpectedContent()
++
++ def HandleAttribute(self, attrib, value):
++ '''Informs the node of an attribute that was parsed out of the GRD file
++ for it.
++
++ Args:
++ attrib: 'name'
++ value: 'fooblat'
++
++ Return:
++ None
++ '''
++ assert isinstance(attrib, six.string_types)
++ assert isinstance(value, six.string_types)
++ if self._IsValidAttribute(attrib, value):
++ self.attrs[attrib] = value
++ else:
++ raise exception.UnexpectedAttribute(attrib)
++
++ def EndParsing(self):
++ '''Called at the end of parsing.'''
++
++ # TODO(joi) Rewrite this, it's extremely ugly!
++ if len(self.mixed_content):
++ if isinstance(self.mixed_content[0], six.string_types):
++ # Remove leading and trailing chunks of pure whitespace.
++ while (len(self.mixed_content) and
++ isinstance(self.mixed_content[0], six.string_types) and
++ self.mixed_content[0].strip() == ''):
++ self.mixed_content = self.mixed_content[1:]
++ # Strip leading and trailing whitespace from mixed content chunks
++ # at front and back.
++ if (len(self.mixed_content) and
++ isinstance(self.mixed_content[0], six.string_types)):
++ self.mixed_content[0] = self.mixed_content[0].lstrip()
++ # Remove leading and trailing ''' (used to demarcate whitespace)
++ if (len(self.mixed_content) and
++ isinstance(self.mixed_content[0], six.string_types)):
++ if self.mixed_content[0].startswith("'''"):
++ self.mixed_content[0] = self.mixed_content[0][3:]
++ if len(self.mixed_content):
++ if isinstance(self.mixed_content[-1], six.string_types):
++ # Same stuff all over again for the tail end.
++ while (len(self.mixed_content) and
++ isinstance(self.mixed_content[-1], six.string_types) and
++ self.mixed_content[-1].strip() == ''):
++ self.mixed_content = self.mixed_content[:-1]
++ if (len(self.mixed_content) and
++ isinstance(self.mixed_content[-1], six.string_types)):
++ self.mixed_content[-1] = self.mixed_content[-1].rstrip()
++ if (len(self.mixed_content) and
++ isinstance(self.mixed_content[-1], six.string_types)):
++ if self.mixed_content[-1].endswith("'''"):
++ self.mixed_content[-1] = self.mixed_content[-1][:-3]
++
++ # Check that all mandatory attributes are there.
++ for node_mandatt in self.MandatoryAttributes():
++ mandatt_list = []
++ if node_mandatt.find('|') >= 0:
++ mandatt_list = node_mandatt.split('|')
++ else:
++ mandatt_list.append(node_mandatt)
++
++ mandatt_option_found = False
++ for mandatt in mandatt_list:
++ assert mandatt not in self.DefaultAttributes()
++ if mandatt in self.attrs:
++ if not mandatt_option_found:
++ mandatt_option_found = True
++ else:
++ raise exception.MutuallyExclusiveMandatoryAttribute(mandatt)
++
++ if not mandatt_option_found:
++ raise exception.MissingMandatoryAttribute(mandatt)
++
++ # Add default attributes if not specified in input file.
++ for defattr in self.DefaultAttributes():
++ if not defattr in self.attrs:
++ self.attrs[defattr] = self.DefaultAttributes()[defattr]
++
++ def GetCdata(self):
++ '''Returns all CDATA of this element, concatenated into a single
++ string. Note that this ignores any elements embedded in CDATA.'''
++ return ''.join([c for c in self.mixed_content
++ if isinstance(c, six.string_types)])
++
++ def __str__(self):
++ '''Returns this node and all nodes below it as an XML document in a Unicode
++ string.'''
++ header = u'<?xml version="1.0" encoding="UTF-8"?>\n'
++ return header + self.FormatXml()
++
++ # Some Python 2 glue.
++ __unicode__ = __str__
++
++ def FormatXml(self, indent = u'', one_line = False):
++ '''Returns this node and all nodes below it as an XML
++ element in a Unicode string. This differs from __unicode__ in that it does
++ not include the <?xml> stuff at the top of the string. If one_line is true,
++ children and CDATA are layed out in a way that preserves internal
++ whitespace.
++ '''
++ assert isinstance(indent, six.string_types)
++
++ content_one_line = (one_line or
++ self._ContentType() == self._CONTENT_TYPE_MIXED)
++ inside_content = self.ContentsAsXml(indent, content_one_line)
++
++ # Then the attributes for this node.
++ attribs = u''
++ default_attribs = self.DefaultAttributes()
++ for attrib, value in sorted(self.attrs.items()):
++ # Only print an attribute if it is other than the default value.
++ if attrib not in default_attribs or value != default_attribs[attrib]:
++ attribs += u' %s=%s' % (attrib, saxutils.quoteattr(value))
++
++ # Finally build the XML for our node and return it
++ if len(inside_content) > 0:
++ if one_line:
++ return u'<%s%s>%s</%s>' % (self.name, attribs, inside_content,
++ self.name)
++ elif content_one_line:
++ return u'%s<%s%s>\n%s %s\n%s</%s>' % (
++ indent, self.name, attribs,
++ indent, inside_content,
++ indent, self.name)
++ else:
++ return u'%s<%s%s>\n%s\n%s</%s>' % (
++ indent, self.name, attribs,
++ inside_content,
++ indent, self.name)
++ else:
++ return u'%s<%s%s />' % (indent, self.name, attribs)
++
++ def ContentsAsXml(self, indent, one_line):
++ '''Returns the contents of this node (CDATA and child elements) in XML
++ format. If 'one_line' is true, the content will be laid out on one line.'''
++ assert isinstance(indent, six.string_types)
++
++ # Build the contents of the element.
++ inside_parts = []
++ last_item = None
++ for mixed_item in self.mixed_content:
++ if isinstance(mixed_item, Node):
++ inside_parts.append(mixed_item.FormatXml(indent + u' ', one_line))
++ if not one_line:
++ inside_parts.append(u'\n')
++ else:
++ message = mixed_item
++ # If this is the first item and it starts with whitespace, we add
++ # the ''' delimiter.
++ if not last_item and message.lstrip() != message:
++ message = u"'''" + message
++ inside_parts.append(util.EncodeCdata(message))
++ last_item = mixed_item
++
++ # If there are only child nodes and no cdata, there will be a spurious
++ # trailing \n
++ if len(inside_parts) and inside_parts[-1] == '\n':
++ inside_parts = inside_parts[:-1]
++
++ # If the last item is a string (not a node) and ends with whitespace,
++ # we need to add the ''' delimiter.
++ if (isinstance(last_item, six.string_types) and
++ last_item.rstrip() != last_item):
++ inside_parts[-1] = inside_parts[-1] + u"'''"
++
++ return u''.join(inside_parts)
++
++ def SubstituteMessages(self, substituter):
++ '''Applies substitutions to all messages in the tree.
++
++ Called as a final step of RunGatherers.
++
++ Args:
++ substituter: a grit.util.Substituter object.
++ '''
++ for child in self.children:
++ child.SubstituteMessages(substituter)
++
++ def _IsValidChild(self, child):
++ '''Returns true if 'child' is a valid child of this node.
++ Overridden by subclasses.'''
++ return False
++
++ def _IsValidAttribute(self, name, value):
++ '''Returns true if 'name' is the name of a valid attribute of this element
++ and 'value' is a valid value for that attribute. Overriden by
++ subclasses unless they have only mandatory attributes.'''
++ return (name in self.MandatoryAttributes() or
++ name in self.DefaultAttributes())
++
++ def _ContentType(self):
++ '''Returns the type of content this element can have. Overridden by
++ subclasses. The content type can be one of the _CONTENT_TYPE_XXX constants
++ above.'''
++ return self._CONTENT_TYPE_NONE
++
++ def MandatoryAttributes(self):
++ '''Returns a list of attribute names that are mandatory (non-optional)
++ on the current element. One can specify a list of
++ "mutually exclusive mandatory" attributes by specifying them as one
++ element in the list, separated by a "|" character.
++ '''
++ return []
++
++ def DefaultAttributes(self):
++ '''Returns a dictionary of attribute names that have defaults, mapped to
++ the default value. Overridden by subclasses.'''
++ return {}
++
++ def GetCliques(self):
++ '''Returns all MessageClique objects belonging to this node. Overridden
++ by subclasses.
++
++ Return:
++ [clique1, clique2] or []
++ '''
++ return []
++
++ def ToRealPath(self, path_from_basedir):
++ '''Returns a real path (which can be absolute or relative to the current
++ working directory), given a path that is relative to the base directory
++ set for the GRIT input file.
++
++ Args:
++ path_from_basedir: '..'
++
++ Return:
++ 'resource'
++ '''
++ return util.normpath(os.path.join(self.GetRoot().GetBaseDir(),
++ os.path.expandvars(path_from_basedir)))
++
++ def GetInputPath(self):
++ '''Returns a path, relative to the base directory set for the grd file,
++ that points to the file the node refers to.
++ '''
++ # This implementation works for most nodes that have an input file.
++ return self.attrs['file']
++
++ def UberClique(self):
++ '''Returns the uberclique that should be used for messages originating in
++ a given node. If the node itself has its uberclique set, that is what we
++ use, otherwise we search upwards until we find one. If we do not find one
++ even at the root node, we set the root node's uberclique to a new
++ uberclique instance.
++ '''
++ node = self
++ while not node.uberclique and node.parent:
++ node = node.parent
++ if not node.uberclique:
++ node.uberclique = clique.UberClique()
++ return node.uberclique
++
++ def IsTranslateable(self):
++ '''Returns false if the node has contents that should not be translated,
++ otherwise returns false (even if the node has no contents).
++ '''
++ if not 'translateable' in self.attrs:
++ return True
++ else:
++ return self.attrs['translateable'] == 'true'
++
++ def IsAccessibilityWithNoUI(self):
++ '''Returns true if the node is marked as an accessibility label and the
++ message isn't shown in the UI. Otherwise returns false. This label is
++ used to determine if the text requires screenshots.'''
++ if not 'is_accessibility_with_no_ui' in self.attrs:
++ return False
++ else:
++ return self.attrs['is_accessibility_with_no_ui'] == 'true'
++
++ def GetNodeById(self, id):
++ '''Returns the node in the subtree parented by this node that has a 'name'
++ attribute matching 'id'. Returns None if no such node is found.
++ '''
++ for node in self:
++ if 'name' in node.attrs and node.attrs['name'] == id:
++ return node
++ return None
++
++ def GetChildrenOfType(self, type):
++ '''Returns a list of all subnodes (recursing to all leaves) of this node
++ that are of the indicated type (or tuple of types).
++
++ Args:
++ type: A type you could use with isinstance().
++
++ Return:
++ A list, possibly empty.
++ '''
++ return [child for child in self if isinstance(child, type)]
++
++ def GetTextualIds(self):
++ '''Returns a list of the textual ids of this node.
++ '''
++ if 'name' in self.attrs:
++ return [self.attrs['name']]
++ return []
++
++ @classmethod
++ def EvaluateExpression(cls, expr, defs, target_platform, extra_variables={}):
++ '''Worker for EvaluateCondition (below) and conditions in XTB files.'''
++ if expr in cls.eval_expr_cache:
++ code, variables_in_expr = cls.eval_expr_cache[expr]
++ else:
++ # Get a list of all variable and method names used in the expression.
++ syntax_tree = ast.parse(expr, mode='eval')
++ variables_in_expr = [node.id for node in ast.walk(syntax_tree) if
++ isinstance(node, ast.Name) and node.id not in ('True', 'False')]
++ code = compile(syntax_tree, filename='<string>', mode='eval')
++ cls.eval_expr_cache[expr] = code, variables_in_expr
++
++ # Set values only for variables that are needed to eval the expression.
++ variable_map = {}
++ for name in variables_in_expr:
++ if name == 'os':
++ value = target_platform
++ elif name == 'defs':
++ value = defs
++
++ elif name == 'is_linux':
++ value = target_platform.startswith('linux')
++ elif name == 'is_macosx':
++ value = target_platform == 'darwin'
++ elif name == 'is_win':
++ value = target_platform in ('cygwin', 'win32')
++ elif name == 'is_android':
++ value = target_platform == 'android'
++ elif name == 'is_ios':
++ value = target_platform == 'ios'
++ elif name == 'is_bsd':
++ value = 'bsd' in target_platform
++ elif name == 'is_posix':
++ value = (target_platform in ('darwin', 'linux2', 'linux3', 'sunos5',
++ 'android', 'ios')
++ or 'bsd' in target_platform)
++
++ elif name == 'pp_ifdef':
++ def pp_ifdef(symbol):
++ return symbol in defs
++ value = pp_ifdef
++ elif name == 'pp_if':
++ def pp_if(symbol):
++ return defs.get(symbol, False)
++ value = pp_if
++
++ elif name in defs:
++ value = defs[name]
++ elif name in extra_variables:
++ value = extra_variables[name]
++ else:
++ # Undefined variables default to False.
++ value = False
++
++ variable_map[name] = value
++
++ eval_result = eval(code, {}, variable_map)
++ assert isinstance(eval_result, bool)
++ return eval_result
++
++ def EvaluateCondition(self, expr):
++ '''Returns true if and only if the Python expression 'expr' evaluates
++ to true.
++
++ The expression is given a few local variables:
++ - 'lang' is the language currently being output
++ (the 'lang' attribute of the <output> element).
++ - 'context' is the current output context
++ (the 'context' attribute of the <output> element).
++ - 'defs' is a map of C preprocessor-style symbol names to their values.
++ - 'os' is the current platform (likely 'linux2', 'win32' or 'darwin').
++ - 'pp_ifdef(symbol)' is a shorthand for "symbol in defs".
++ - 'pp_if(symbol)' is a shorthand for "symbol in defs and defs[symbol]".
++ - 'is_linux', 'is_macosx', 'is_win', 'is_posix' are true if 'os'
++ matches the given platform.
++ '''
++ root = self.GetRoot()
++ lang = getattr(root, 'output_language', '')
++ context = getattr(root, 'output_context', '')
++ defs = getattr(root, 'defines', {})
++ target_platform = getattr(root, 'target_platform', '')
++ extra_variables = {
++ 'lang': lang,
++ 'context': context,
++ }
++ return Node.EvaluateExpression(
++ expr, defs, target_platform, extra_variables)
++
++ def OnlyTheseTranslations(self, languages):
++ '''Turns off loading of translations for languages not in the provided list.
++
++ Attrs:
++ languages: ['fr', 'zh_cn']
++ '''
++ for node in self:
++ if (hasattr(node, 'IsTranslation') and
++ node.IsTranslation() and
++ node.GetLang() not in languages):
++ node.DisableLoading()
++
++ def FindBooleanAttribute(self, attr, default, skip_self):
++ '''Searches all ancestors of the current node for the nearest enclosing
++ definition of the given boolean attribute.
++
++ Args:
++ attr: 'fallback_to_english'
++ default: What to return if no node defines the attribute.
++ skip_self: Don't check the current node, only its parents.
++ '''
++ p = self.parent if skip_self else self
++ while p:
++ value = p.attrs.get(attr, 'default').lower()
++ if value != 'default':
++ return (value == 'true')
++ p = p.parent
++ return default
++
++ def PseudoIsAllowed(self):
++ '''Returns true if this node is allowed to use pseudo-translations. This
++ is true by default, unless this node is within a <release> node that has
++ the allow_pseudo attribute set to false.
++ '''
++ return self.FindBooleanAttribute('allow_pseudo',
++ default=True, skip_self=True)
++
++ def ShouldFallbackToEnglish(self):
++ '''Returns true iff this node should fall back to English when
++ pseudotranslations are disabled and no translation is available for a
++ given message.
++ '''
++ return self.FindBooleanAttribute('fallback_to_english',
++ default=False, skip_self=True)
++
++ def WhitelistMarkedAsSkip(self):
++ '''Returns true if the node is marked to be skipped in the output by a
++ whitelist.
++ '''
++ return self._whitelist_marked_as_skip
++
++ def SetWhitelistMarkedAsSkip(self, mark_skipped):
++ '''Sets WhitelistMarkedAsSkip.
++ '''
++ self._whitelist_marked_as_skip = mark_skipped
++
++ def ExpandVariables(self):
++ '''Whether we need to expand variables on a given node.'''
++ return False
++
++ def IsResourceMapSource(self):
++ '''Whether this node is a resource map source.'''
++ return False
++
++ def CompressDataIfNeeded(self, data):
++ '''Compress data using the format specified in the compress attribute.
++
++ Args:
++ data: The data to compressed.
++ Returns:
++ The data in gzipped or brotli compressed format. If the format is
++ unspecified then this returns the data uncompressed.
++ '''
++
++ compress = self.attrs.get('compress')
++
++ # Compress JS, HTML, CSS and SVG files by default (gzip), unless |compress|
++ # is explicitly specified.
++ compress_by_default = (compress == 'default'
++ and self.attrs.get('file').endswith(
++ self._COMPRESS_BY_DEFAULT_EXTENSIONS))
++
++ if compress == 'gzip' or compress_by_default:
++ # We only use rsyncable compression on Linux.
++ # We exclude ChromeOS since ChromeOS bots are Linux based but do not have
++ # the --rsyncable option built in for gzip. See crbug.com/617950.
++ if sys.platform == 'linux2' and 'chromeos' not in self.GetRoot().defines:
++ return grit.format.gzip_string.GzipStringRsyncable(data)
++ return grit.format.gzip_string.GzipString(data)
++
++ if compress == 'brotli':
++ # The length of the uncompressed data as 8 bytes little-endian.
++ size_bytes = struct.pack("<q", len(data))
++ data = brotli_util.BrotliCompress(data)
++ # BROTLI_CONST is prepended to brotli decompressed data in order to
++ # easily check if a resource has been brotli compressed.
++ # The length of the uncompressed data is also appended to the start,
++ # truncated to 6 bytes, little-endian. size_bytes is 8 bytes,
++ # need to truncate further to 6.
++ formatter = b'%ds %dx %ds' % (6, 2, len(size_bytes) - 8)
++ return (constants.BROTLI_CONST +
++ b''.join(struct.unpack(formatter, size_bytes)) +
++ data)
++
++ if compress == 'false' or compress == 'default':
++ return data
++
++ raise Exception('Invalid value for compression')
++
++
++class ContentNode(Node):
++ '''Convenience baseclass for nodes that can have content.'''
++ def _ContentType(self):
++ return self._CONTENT_TYPE_MIXED
+diff --git a/tools/grit/grit/node/base_unittest.py b/tools/grit/grit/node/base_unittest.py
+new file mode 100644
+index 0000000000..32a5a0ca59
+--- /dev/null
++++ b/tools/grit/grit/node/base_unittest.py
+@@ -0,0 +1,259 @@
++#!/usr/bin/env python
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
+
-+// Unsafe; A grandchild ends up implicitly exposing their parent and
-+// grantparent's destructors.
-+class ImplicitDerivedProtectedToPublicInHeader
-+ : public ProtectedRefCountedDtorInHeader {
-+ public:
-+ ImplicitDerivedProtectedToPublicInHeader() {}
-+};
++'''Unit tests for base.Node functionality (as used in various subclasses)'''
+
-+// Unsafe-but-ignored; should not have errors.
-+class WebKitPublicDtorInHeader
-+ : public WebKit::RefCounted<WebKitPublicDtorInHeader> {
-+ public:
-+ WebKitPublicDtorInHeader() {}
-+ ~WebKitPublicDtorInHeader() {}
-+};
++from __future__ import print_function
+
-+// Unsafe-but-ignored; should not have errors.
-+class WebKitDerivedPublicDtorInHeader
-+ : public WebKitPublicDtorInHeader {
-+ public:
-+ WebKitDerivedPublicDtorInHeader() {}
-+ ~WebKitDerivedPublicDtorInHeader() {}
-+};
++import os
++import sys
++if __name__ == '__main__':
++ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
++
++import unittest
++
++from six import StringIO
++
++from grit import grd_reader
++from grit import util
++from grit.node import base
++from grit.node import message
++
++
++def MakePlaceholder(phname='BINGO'):
++ ph = message.PhNode()
++ ph.StartParsing(u'ph', None)
++ ph.HandleAttribute(u'name', phname)
++ ph.AppendContent(u'bongo')
++ ph.EndParsing()
++ return ph
++
++
++class NodeUnittest(unittest.TestCase):
++ def testWhitespaceHandling(self):
++ # We test using the Message node type.
++ node = message.MessageNode()
++ node.StartParsing(u'hello', None)
++ node.HandleAttribute(u'name', u'bla')
++ node.AppendContent(u" ''' two spaces ")
++ node.EndParsing()
++ self.failUnless(node.GetCdata() == u' two spaces')
++
++ node = message.MessageNode()
++ node.StartParsing(u'message', None)
++ node.HandleAttribute(u'name', u'bla')
++ node.AppendContent(u" two spaces ''' ")
++ node.EndParsing()
++ self.failUnless(node.GetCdata() == u'two spaces ')
++
++ def testWhitespaceHandlingWithChildren(self):
++ # We test using the Message node type.
++ node = message.MessageNode()
++ node.StartParsing(u'message', None)
++ node.HandleAttribute(u'name', u'bla')
++ node.AppendContent(u" ''' two spaces ")
++ node.AddChild(MakePlaceholder())
++ node.AppendContent(u' space before and after ')
++ node.AddChild(MakePlaceholder('BONGO'))
++ node.AppendContent(u" space before two after '''")
++ node.EndParsing()
++ self.failUnless(node.mixed_content[0] == u' two spaces ')
++ self.failUnless(node.mixed_content[2] == u' space before and after ')
++ self.failUnless(node.mixed_content[-1] == u' space before two after ')
++
++ def testXmlFormatMixedContent(self):
++ # Again test using the Message node type, because it is the only mixed
++ # content node.
++ node = message.MessageNode()
++ node.StartParsing(u'message', None)
++ node.HandleAttribute(u'name', u'name')
++ node.AppendContent(u'Hello <young> ')
++
++ ph = message.PhNode()
++ ph.StartParsing(u'ph', None)
++ ph.HandleAttribute(u'name', u'USERNAME')
++ ph.AppendContent(u'$1')
++ ex = message.ExNode()
++ ex.StartParsing(u'ex', None)
++ ex.AppendContent(u'Joi')
++ ex.EndParsing()
++ ph.AddChild(ex)
++ ph.EndParsing()
++
++ node.AddChild(ph)
++ node.EndParsing()
++
++ non_indented_xml = node.FormatXml()
++ self.failUnless(non_indented_xml == u'<message name="name">\n Hello '
++ u'&lt;young&gt; <ph name="USERNAME">$1<ex>Joi</ex></ph>'
++ u'\n</message>')
++
++ indented_xml = node.FormatXml(u' ')
++ self.failUnless(indented_xml == u' <message name="name">\n Hello '
++ u'&lt;young&gt; <ph name="USERNAME">$1<ex>Joi</ex></ph>'
++ u'\n </message>')
++
++ def testXmlFormatMixedContentWithLeadingWhitespace(self):
++ # Again test using the Message node type, because it is the only mixed
++ # content node.
++ node = message.MessageNode()
++ node.StartParsing(u'message', None)
++ node.HandleAttribute(u'name', u'name')
++ node.AppendContent(u"''' Hello <young> ")
++
++ ph = message.PhNode()
++ ph.StartParsing(u'ph', None)
++ ph.HandleAttribute(u'name', u'USERNAME')
++ ph.AppendContent(u'$1')
++ ex = message.ExNode()
++ ex.StartParsing(u'ex', None)
++ ex.AppendContent(u'Joi')
++ ex.EndParsing()
++ ph.AddChild(ex)
++ ph.EndParsing()
++
++ node.AddChild(ph)
++ node.AppendContent(u" yessiree '''")
++ node.EndParsing()
++
++ non_indented_xml = node.FormatXml()
++ self.failUnless(non_indented_xml ==
++ u"<message name=\"name\">\n ''' Hello"
++ u' &lt;young&gt; <ph name="USERNAME">$1<ex>Joi</ex></ph>'
++ u" yessiree '''\n</message>")
++
++ indented_xml = node.FormatXml(u' ')
++ self.failUnless(indented_xml ==
++ u" <message name=\"name\">\n ''' Hello"
++ u' &lt;young&gt; <ph name="USERNAME">$1<ex>Joi</ex></ph>'
++ u" yessiree '''\n </message>")
++
++ self.failUnless(node.GetNodeById('name'))
++
++ def testXmlFormatContentWithEntities(self):
++ '''Tests a bug where &nbsp; would not be escaped correctly.'''
++ from grit import tclib
++ msg_node = message.MessageNode.Construct(None, tclib.Message(
++ text = 'BEGIN_BOLDHelloWHITESPACEthere!END_BOLD Bingo!',
++ placeholders = [
++ tclib.Placeholder('BEGIN_BOLD', '<b>', 'bla'),
++ tclib.Placeholder('WHITESPACE', '&nbsp;', 'bla'),
++ tclib.Placeholder('END_BOLD', '</b>', 'bla')]),
++ 'BINGOBONGO')
++ xml = msg_node.FormatXml()
++ self.failUnless(xml.find('&nbsp;') == -1, 'should have no entities')
++
++ def testIter(self):
++ # First build a little tree of message and ph nodes.
++ node = message.MessageNode()
++ node.StartParsing(u'message', None)
++ node.HandleAttribute(u'name', u'bla')
++ node.AppendContent(u" ''' two spaces ")
++ node.AppendContent(u' space before and after ')
++ ph = message.PhNode()
++ ph.StartParsing(u'ph', None)
++ ph.AddChild(message.ExNode())
++ ph.HandleAttribute(u'name', u'BINGO')
++ ph.AppendContent(u'bongo')
++ node.AddChild(ph)
++ node.AddChild(message.PhNode())
++ node.AppendContent(u" space before two after '''")
++
++ order = [message.MessageNode, message.PhNode, message.ExNode, message.PhNode]
++ for n in node:
++ self.failUnless(type(n) == order[0])
++ order = order[1:]
++ self.failUnless(len(order) == 0)
++
++ def testGetChildrenOfType(self):
++ xml = '''<?xml version="1.0" encoding="UTF-8"?>
++ <grit latest_public_release="2" source_lang_id="en-US"
++ current_release="3" base_dir=".">
++ <outputs>
++ <output filename="resource.h" type="rc_header" />
++ <output filename="en/generated_resources.rc" type="rc_all"
++ lang="en" />
++ <if expr="pp_if('NOT_TRUE')">
++ <output filename="de/generated_resources.rc" type="rc_all"
++ lang="de" />
++ </if>
++ </outputs>
++ <release seq="3">
++ <messages>
++ <message name="ID_HELLO">Hello!</message>
++ </messages>
++ </release>
++ </grit>'''
++ grd = grd_reader.Parse(StringIO(xml),
++ util.PathFromRoot('grit/test/data'))
++ from grit.node import node_io
++ output_nodes = grd.GetChildrenOfType(node_io.OutputNode)
++ self.failUnlessEqual(len(output_nodes), 3)
++ self.failUnlessEqual(output_nodes[2].attrs['filename'],
++ 'de/generated_resources.rc')
++
++ def testEvaluateExpression(self):
++ def AssertExpr(expected_value, expr, defs, target_platform,
++ extra_variables):
++ self.failUnlessEqual(expected_value, base.Node.EvaluateExpression(
++ expr, defs, target_platform, extra_variables))
++
++ AssertExpr(True, "True", {}, 'linux', {})
++ AssertExpr(False, "False", {}, 'linux', {})
++ AssertExpr(True, "True or False", {}, 'linux', {})
++ AssertExpr(False, "True and False", {}, 'linux', {})
++ AssertExpr(True, "os == 'linux'", {}, 'linux', {})
++ AssertExpr(False, "os == 'linux'", {}, 'ios', {})
++ AssertExpr(True, "'foo' in defs", {'foo': 'bar'}, 'ios', {})
++ AssertExpr(False, "'foo' in defs", {'baz': 'bar'}, 'ios', {})
++ AssertExpr(False, "'foo' in defs", {}, 'ios', {})
++ AssertExpr(True, "is_linux", {}, 'linux2', {})
++ AssertExpr(False, "is_linux", {}, 'win32', {})
++ AssertExpr(True, "is_macosx", {}, 'darwin', {})
++ AssertExpr(False, "is_macosx", {}, 'ios', {})
++ AssertExpr(True, "is_win", {}, 'win32', {})
++ AssertExpr(False, "is_win", {}, 'darwin', {})
++ AssertExpr(True, "is_android", {}, 'android', {})
++ AssertExpr(False, "is_android", {}, 'linux3', {})
++ AssertExpr(True, "is_ios", {}, 'ios', {})
++ AssertExpr(False, "is_ios", {}, 'darwin', {})
++ AssertExpr(True, "is_posix", {}, 'linux2', {})
++ AssertExpr(True, "is_posix", {}, 'darwin', {})
++ AssertExpr(True, "is_posix", {}, 'android', {})
++ AssertExpr(True, "is_posix", {}, 'ios', {})
++ AssertExpr(True, "is_posix", {}, 'freebsd7', {})
++ AssertExpr(False, "is_posix", {}, 'win32', {})
++ AssertExpr(True, "pp_ifdef('foo')", {'foo': True}, 'win32', {})
++ AssertExpr(True, "pp_ifdef('foo')", {'foo': False}, 'win32', {})
++ AssertExpr(False, "pp_ifdef('foo')", {'bar': True}, 'win32', {})
++ AssertExpr(True, "pp_if('foo')", {'foo': True}, 'win32', {})
++ AssertExpr(False, "pp_if('foo')", {'foo': False}, 'win32', {})
++ AssertExpr(False, "pp_if('foo')", {'bar': True}, 'win32', {})
++ AssertExpr(True, "foo", {'foo': True}, 'win32', {})
++ AssertExpr(False, "foo", {'foo': False}, 'win32', {})
++ AssertExpr(False, "foo", {'bar': True}, 'win32', {})
++ AssertExpr(True, "foo == 'baz'", {'foo': 'baz'}, 'win32', {})
++ AssertExpr(False, "foo == 'baz'", {'foo': True}, 'win32', {})
++ AssertExpr(False, "foo == 'baz'", {}, 'win32', {})
++ AssertExpr(True, "lang == 'de'", {}, 'win32', {'lang': 'de'})
++ AssertExpr(False, "lang == 'de'", {}, 'win32', {'lang': 'fr'})
++ AssertExpr(False, "lang == 'de'", {}, 'win32', {})
++
++ # Test a couple more complex expressions for good measure.
++ AssertExpr(True, "is_ios and (lang in ['de', 'fr'] or foo)",
++ {'foo': 'bar'}, 'ios', {'lang': 'fr', 'context': 'today'})
++ AssertExpr(False, "is_ios and (lang in ['de', 'fr'] or foo)",
++ {'foo': False}, 'linux2', {'lang': 'fr', 'context': 'today'})
++ AssertExpr(False, "is_ios and (lang in ['de', 'fr'] or foo)",
++ {'baz': 'bar'}, 'ios', {'lang': 'he', 'context': 'today'})
++ AssertExpr(True, "foo == 'bar' or not baz",
++ {'foo': 'bar', 'fun': True}, 'ios', {'lang': 'en'})
++ AssertExpr(True, "foo == 'bar' or not baz",
++ {}, 'ios', {'lang': 'en', 'context': 'java'})
++ AssertExpr(False, "foo == 'bar' or not baz",
++ {'foo': 'ruz', 'baz': True}, 'ios', {'lang': 'en'})
+
-+#endif // BASE_REFCOUNTED_H_
-diff --git a/tools/clang/plugins/tests/base_refcounted.txt b/tools/clang/plugins/tests/base_refcounted.txt
++if __name__ == '__main__':
++ unittest.main()
+diff --git a/tools/grit/grit/node/brotli_util.py b/tools/grit/grit/node/brotli_util.py
new file mode 100644
-index 0000000000..4626424177
+index 0000000000..77f70e49d5
--- /dev/null
-+++ b/tools/clang/plugins/tests/base_refcounted.txt
-@@ -0,0 +1,23 @@
-+In file included from base_refcounted.cpp:5:
-+./base_refcounted.h:45:3: warning: [chromium-style] Classes that are ref-counted should not have public destructors.
-+ ~PublicRefCountedDtorInHeader() {}
-+ ^
-+./base_refcounted.h:57:3: warning: [chromium-style] Classes that are ref-counted should not have public destructors.
-+ ~PublicRefCountedThreadSafeDtorInHeader() {}
-+ ^
-+./base_refcounted.h:94:3: warning: [chromium-style] Classes that are ref-counted should not have public destructors.
-+ ~DerivedProtectedToPublicInHeader() {}
-+ ^
-+./base_refcounted.h:99:1: warning: [chromium-style] Classes that are ref-counted should have explicit destructors that are protected or private.
-+class ImplicitDerivedProtectedToPublicInHeader
-+^
-+base_refcounted.cpp:16:3: warning: [chromium-style] Classes that are ref-counted should not have public destructors.
-+ ~AnonymousDerivedProtectedToPublicInImpl() {}
-+ ^
-+base_refcounted.cpp:26:3: warning: [chromium-style] Classes that are ref-counted should not have public destructors.
-+ ~PublicRefCountedDtorInImpl() {}
-+ ^
-+base_refcounted.cpp:52:3: warning: [chromium-style] Classes that are ref-counted should not have public destructors.
-+ ~UnsafeTypedefChainInImpl() {}
-+ ^
-+7 warnings generated.
-diff --git a/tools/clang/plugins/tests/inline_copy_ctor.cpp b/tools/clang/plugins/tests/inline_copy_ctor.cpp
-new file mode 100644
-index 0000000000..dcd90020c5
---- /dev/null
-+++ b/tools/clang/plugins/tests/inline_copy_ctor.cpp
-@@ -0,0 +1,5 @@
-+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+// Use of this source code is governed by a BSD-style license that can be
-+// found in the LICENSE file.
++++ b/tools/grit/grit/node/brotli_util.py
+@@ -0,0 +1,29 @@
++# 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.
++
++"""Framework for compressing resources using Brotli."""
++
++import subprocess
++
++__brotli_executable = None
+
-+#include "inline_copy_ctor.h"
-diff --git a/tools/clang/plugins/tests/inline_copy_ctor.h b/tools/clang/plugins/tests/inline_copy_ctor.h
++
++def SetBrotliCommand(brotli):
++ # brotli is a list. In production it contains the path to the Brotli executable.
++ # During testing it contains [python, mock_brotli.py] for testing on Windows.
++ global __brotli_executable
++ __brotli_executable = brotli
++
++
++def BrotliCompress(data):
++ if not __brotli_executable:
++ raise Exception('Add "use_brotli = true" to you GN grit(...) target ' +
++ 'if you want to use brotli.')
++ compress = subprocess.Popen(__brotli_executable + ['-', '-f'],
++ stdin=subprocess.PIPE, stdout=subprocess.PIPE)
++ return compress.communicate(data)[0]
++
++def IsInitialized():
++ global __brotli_executable
++ return __brotli_executable is not None
+diff --git a/tools/grit/grit/node/custom/__init__.py b/tools/grit/grit/node/custom/__init__.py
new file mode 100644
-index 0000000000..619a18392b
+index 0000000000..e179cf7730
--- /dev/null
-+++ b/tools/clang/plugins/tests/inline_copy_ctor.h
-@@ -0,0 +1,12 @@
-+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+// Use of this source code is governed by a BSD-style license that can be
-+// found in the LICENSE file.
++++ b/tools/grit/grit/node/custom/__init__.py
+@@ -0,0 +1,8 @@
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
+
-+struct C {
-+ C();
-+ ~C();
++'''Package 'grit.node.custom'
++'''
+
-+ static C foo() { return C(); }
++pass
+diff --git a/tools/grit/grit/node/custom/filename.py b/tools/grit/grit/node/custom/filename.py
+new file mode 100644
+index 0000000000..55a27e58c1
+--- /dev/null
++++ b/tools/grit/grit/node/custom/filename.py
+@@ -0,0 +1,29 @@
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
+
-+ int a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p , q, r, s, t, u, v, w, x;
-+};
-diff --git a/tools/clang/plugins/tests/inline_copy_ctor.txt b/tools/clang/plugins/tests/inline_copy_ctor.txt
++'''A CustomType for filenames.'''
++
++from __future__ import print_function
++
++from grit import clique
++from grit import lazy_re
++
++
++class WindowsFilename(clique.CustomType):
++ '''Validates that messages can be used as Windows filenames, and strips
++ illegal characters out of translations.
++ '''
++
++ BANNED = lazy_re.compile(r'\+|:|\/|\\\\|\*|\?|\"|\<|\>|\|')
++
++ def Validate(self, message):
++ return not self.BANNED.search(message.GetPresentableContent())
++
++ def ValidateAndModify(self, lang, translation):
++ is_ok = self.Validate(translation)
++ self.ModifyEachTextPart(lang, translation)
++ return is_ok
++
++ def ModifyTextPart(self, lang, text):
++ return self.BANNED.sub(' ', text)
+diff --git a/tools/grit/grit/node/custom/filename_unittest.py b/tools/grit/grit/node/custom/filename_unittest.py
new file mode 100644
-index 0000000000..bc4bd8911e
+index 0000000000..8e2a6dd64a
--- /dev/null
-+++ b/tools/clang/plugins/tests/inline_copy_ctor.txt
-@@ -0,0 +1,5 @@
-+In file included from inline_copy_ctor.cpp:5:
-+./inline_copy_ctor.h:5:1: warning: [chromium-style] Complex class/struct needs an explicit out-of-line copy constructor.
-+struct C {
-+^
-+1 warning generated.
-diff --git a/tools/clang/plugins/tests/inline_ctor.cpp b/tools/clang/plugins/tests/inline_ctor.cpp
++++ b/tools/grit/grit/node/custom/filename_unittest.py
+@@ -0,0 +1,34 @@
++#!/usr/bin/env python
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++'''Unit tests for grit.node.custom.filename'''
++
++from __future__ import print_function
++
++import os
++import sys
++if __name__ == '__main__':
++ sys.path.append(os.path.join(os.path.dirname(__file__), '../../..'))
++
++import unittest
++from grit.node.custom import filename
++from grit import clique
++from grit import tclib
++
++
++class WindowsFilenameUnittest(unittest.TestCase):
++
++ def testValidate(self):
++ factory = clique.UberClique()
++ msg = tclib.Message(text='Bingo bongo')
++ c = factory.MakeClique(msg)
++ c.SetCustomType(filename.WindowsFilename())
++ translation = tclib.Translation(id=msg.GetId(), text='Bilingo bolongo:')
++ c.AddTranslation(translation, 'fr')
++ self.failUnless(c.MessageForLanguage('fr').GetRealContent() == 'Bilingo bolongo ')
++
++
++if __name__ == '__main__':
++ unittest.main()
+diff --git a/tools/grit/grit/node/empty.py b/tools/grit/grit/node/empty.py
new file mode 100644
-index 0000000000..6a751fb405
+index 0000000000..e19d2c4ddb
--- /dev/null
-+++ b/tools/clang/plugins/tests/inline_ctor.cpp
-@@ -0,0 +1,25 @@
-+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
-+// Use of this source code is governed by a BSD-style license that can be
-+// found in the LICENSE file.
++++ b/tools/grit/grit/node/empty.py
+@@ -0,0 +1,64 @@
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
+
-+#include "inline_ctor.h"
++'''Container nodes that don't have any logic.
++'''
+
-+#include <string>
-+#include <vector>
++from __future__ import print_function
+
-+// We don't warn on classes that are in CPP files.
-+class InlineInCPPOK {
-+ public:
-+ InlineInCPPOK() {}
-+ ~InlineInCPPOK() {}
++from grit.node import base
++from grit.node import include
++from grit.node import message
++from grit.node import misc
++from grit.node import node_io
++from grit.node import structure
+
-+ private:
-+ std::vector<int> one_;
-+ std::vector<std::string> two_;
-+};
+
-+int main() {
-+ InlineInCPPOK one;
-+ InlineCtorsArentOKInHeader two;
-+ return 0;
++class GroupingNode(base.Node):
++ '''Base class for all the grouping elements (<structures>, <includes>,
++ <messages> and <identifiers>).'''
++ def DefaultAttributes(self):
++ return {
++ 'first_id' : '',
++ 'comment' : '',
++ 'fallback_to_english' : 'false',
++ 'fallback_to_low_resolution' : 'false',
++ }
++
++
++class IncludesNode(GroupingNode):
++ '''The <includes> element.'''
++ def _IsValidChild(self, child):
++ return isinstance(child, (include.IncludeNode, misc.IfNode, misc.PartNode))
++
++
++class MessagesNode(GroupingNode):
++ '''The <messages> element.'''
++ def _IsValidChild(self, child):
++ return isinstance(child, (message.MessageNode, misc.IfNode, misc.PartNode))
++
++
++class StructuresNode(GroupingNode):
++ '''The <structures> element.'''
++ def _IsValidChild(self, child):
++ return isinstance(child, (structure.StructureNode,
++ misc.IfNode, misc.PartNode))
++
++
++class TranslationsNode(base.Node):
++ '''The <translations> element.'''
++ def _IsValidChild(self, child):
++ return isinstance(child, (node_io.FileNode, misc.IfNode, misc.PartNode))
++
++
++class OutputsNode(base.Node):
++ '''The <outputs> element.'''
++ def _IsValidChild(self, child):
++ return isinstance(child, (node_io.OutputNode, misc.IfNode, misc.PartNode))
++
++
++class IdentifiersNode(GroupingNode):
++ '''The <identifiers> element.'''
++ def _IsValidChild(self, child):
++ return isinstance(child, misc.IdentifierNode)
+diff --git a/tools/grit/grit/node/include.py b/tools/grit/grit/node/include.py
+new file mode 100644
+index 0000000000..b06b9889bb
+--- /dev/null
++++ b/tools/grit/grit/node/include.py
+@@ -0,0 +1,170 @@
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++"""Handling of the <include> element.
++"""
++
++from __future__ import print_function
++
++import os
++
++from grit import util
++import grit.format.html_inline
++import grit.format.rc
++from grit.format import minifier
++from grit.node import base
++
++class IncludeNode(base.Node):
++ """An <include> element."""
++
++ def __init__(self):
++ super(IncludeNode, self).__init__()
++
++ # Cache flattened data so that we don't flatten the same file
++ # multiple times.
++ self._flattened_data = None
++ # Also keep track of the last filename we flattened to, so we can
++ # avoid doing it more than once.
++ self._last_flat_filename = None
++
++ def _IsValidChild(self, child):
++ return False
++
++ def _GetFlattenedData(
++ self, allow_external_script=False, preprocess_only=False):
++ if not self._flattened_data:
++ filename = self.ToRealPath(self.GetInputPath())
++ self._flattened_data = (
++ grit.format.html_inline.InlineToString(filename, self,
++ preprocess_only=preprocess_only,
++ allow_external_script=allow_external_script))
++ return self._flattened_data.encode('utf-8')
++
++ def MandatoryAttributes(self):
++ return ['name', 'type', 'file']
++
++ def DefaultAttributes(self):
++ """Attributes:
++ translateable: False if the node has contents that should not be
++ translated.
++ preprocess: Takes the same code path as flattenhtml, but it
++ disables any processing/inlining outside of <if>
++ and <include>.
++ compress: The format to compress the data with, e.g. 'gzip'
++ or 'false' if data should not be compressed.
++ skip_minify: If true, skips minifying the node's contents.
++ skip_in_resource_map: If true, do not add to the resource map.
++ """
++ return {
++ 'translateable': 'true',
++ 'generateid': 'true',
++ 'filenameonly': 'false',
++ 'mkoutput': 'false',
++ 'preprocess': 'false',
++ 'flattenhtml': 'false',
++ 'compress': 'default',
++ 'allowexternalscript': 'false',
++ 'relativepath': 'false',
++ 'use_base_dir': 'true',
++ 'skip_minify': 'false',
++ 'skip_in_resource_map': 'false',
++ }
++
++ def GetInputPath(self):
++ # Do not mess with absolute paths, that would make them invalid.
++ if os.path.isabs(os.path.expandvars(self.attrs['file'])):
++ return self.attrs['file']
++
++ # We have no control over code that calls ToRealPath later, so convert
++ # the path to be relative against our basedir.
++ if self.attrs.get('use_base_dir', 'true') != 'true':
++ # Normalize the directory path to use the appropriate OS separator.
++ # GetBaseDir() may return paths\like\this or paths/like/this, since it is
++ # read from the base_dir attribute in the grd file.
++ norm_base_dir = util.normpath(self.GetRoot().GetBaseDir())
++ return os.path.relpath(self.attrs['file'], norm_base_dir)
++
++ return self.attrs['file']
++
++ def FileForLanguage(self, lang, output_dir):
++ """Returns the file for the specified language. This allows us to return
++ different files for different language variants of the include file.
++ """
++ input_path = self.GetInputPath()
++ if input_path is None:
++ return None
++
++ return self.ToRealPath(input_path)
++
++ def GetDataPackValue(self, lang, encoding):
++ '''Returns bytes or a str represenation for a data_pack entry.'''
++ filename = self.ToRealPath(self.GetInputPath())
++ if self.attrs['flattenhtml'] == 'true':
++ allow_external_script = self.attrs['allowexternalscript'] == 'true'
++ data = self._GetFlattenedData(allow_external_script=allow_external_script)
++ elif self.attrs['preprocess'] == 'true':
++ data = self._GetFlattenedData(preprocess_only=True)
++ else:
++ data = util.ReadFile(filename, util.BINARY)
++
++ if self.attrs['skip_minify'] != 'true':
++ # Note that the minifier will only do anything if a minifier command
++ # has been set in the command line.
++ data = minifier.Minify(data, filename)
++
++ # Include does not care about the encoding, because it only returns binary
++ # data.
++ return self.CompressDataIfNeeded(data)
++
++ def Process(self, output_dir):
++ """Rewrite file references to be base64 encoded data URLs. The new file
++ will be written to output_dir and the name of the new file is returned."""
++ filename = self.ToRealPath(self.GetInputPath())
++ flat_filename = os.path.join(output_dir,
++ self.attrs['name'] + '_' + os.path.basename(filename))
++
++ if self._last_flat_filename == flat_filename:
++ return
++
++ with open(flat_filename, 'wb') as outfile:
++ outfile.write(self._GetFlattenedData())
++
++ self._last_flat_filename = flat_filename
++ return os.path.basename(flat_filename)
++
++ def GetHtmlResourceFilenames(self):
++ """Returns a set of all filenames inlined by this file."""
++ allow_external_script = self.attrs['allowexternalscript'] == 'true'
++ return grit.format.html_inline.GetResourceFilenames(
++ self.ToRealPath(self.GetInputPath()),
++ self,
++ allow_external_script=allow_external_script)
++
++ def IsResourceMapSource(self):
++ skip = self.attrs.get('skip_in_resource_map', 'false') == 'true'
++ return not skip
++
++ @staticmethod
++ def Construct(parent, name, type, file, translateable=True,
++ filenameonly=False, mkoutput=False, relativepath=False):
++ """Creates a new node which is a child of 'parent', with attributes set
++ by parameters of the same name.
++ """
++ # Convert types to appropriate strings
++ translateable = util.BoolToString(translateable)
++ filenameonly = util.BoolToString(filenameonly)
++ mkoutput = util.BoolToString(mkoutput)
++ relativepath = util.BoolToString(relativepath)
++
++ node = IncludeNode()
++ node.StartParsing('include', parent)
++ node.HandleAttribute('name', name)
++ node.HandleAttribute('type', type)
++ node.HandleAttribute('file', file)
++ node.HandleAttribute('translateable', translateable)
++ node.HandleAttribute('filenameonly', filenameonly)
++ node.HandleAttribute('mkoutput', mkoutput)
++ node.HandleAttribute('relativepath', relativepath)
++ node.EndParsing()
++ return node
+diff --git a/tools/grit/grit/node/include_unittest.py b/tools/grit/grit/node/include_unittest.py
+new file mode 100644
+index 0000000000..4c658f1ffe
+--- /dev/null
++++ b/tools/grit/grit/node/include_unittest.py
+@@ -0,0 +1,134 @@
++#!/usr/bin/env python
++# Copyright (c) 2013 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++'''Unit tests for include.IncludeNode'''
++
++from __future__ import print_function
++
++import os
++import sys
++import unittest
++import zlib
++
++if __name__ == '__main__':
++ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
++
++from grit.node import misc
++from grit.node import include
++from grit.node import empty
++from grit import util
++
++
++def checkIsGzipped(filename, compress_attr):
++ test_data_root = util.PathFromRoot('grit/testdata')
++ root = util.ParseGrdForUnittest(
++ '''
++ <includes>
++ <include name="TEST_TXT" file="%s" %s type="BINDATA"/>
++ </includes>''' % (filename, compress_attr),
++ base_dir=test_data_root)
++ node, = root.GetChildrenOfType(include.IncludeNode)
++ compressed = node.GetDataPackValue(lang='en', encoding=util.BINARY)
++
++ decompressed_data = zlib.decompress(compressed, 16 + zlib.MAX_WBITS)
++ expected = util.ReadFile(os.path.join(test_data_root, filename), util.BINARY)
++ return expected == decompressed_data
++
++
++class IncludeNodeUnittest(unittest.TestCase):
++ def testGetPath(self):
++ root = misc.GritNode()
++ root.StartParsing(u'grit', None)
++ root.HandleAttribute(u'latest_public_release', u'0')
++ root.HandleAttribute(u'current_release', u'1')
++ root.HandleAttribute(u'base_dir', r'..\resource')
++ release = misc.ReleaseNode()
++ release.StartParsing(u'release', root)
++ release.HandleAttribute(u'seq', u'1')
++ root.AddChild(release)
++ includes = empty.IncludesNode()
++ includes.StartParsing(u'includes', release)
++ release.AddChild(includes)
++ include_node = include.IncludeNode()
++ include_node.StartParsing(u'include', includes)
++ include_node.HandleAttribute(u'file', r'flugel\kugel.pdf')
++ includes.AddChild(include_node)
++ root.EndParsing()
++
++ self.assertEqual(root.ToRealPath(include_node.GetInputPath()),
++ util.normpath(
++ os.path.join(r'../resource', r'flugel/kugel.pdf')))
++
++ def testGetPathNoBasedir(self):
++ root = misc.GritNode()
++ root.StartParsing(u'grit', None)
++ root.HandleAttribute(u'latest_public_release', u'0')
++ root.HandleAttribute(u'current_release', u'1')
++ root.HandleAttribute(u'base_dir', r'..\resource')
++ release = misc.ReleaseNode()
++ release.StartParsing(u'release', root)
++ release.HandleAttribute(u'seq', u'1')
++ root.AddChild(release)
++ includes = empty.IncludesNode()
++ includes.StartParsing(u'includes', release)
++ release.AddChild(includes)
++ include_node = include.IncludeNode()
++ include_node.StartParsing(u'include', includes)
++ include_node.HandleAttribute(u'file', r'flugel\kugel.pdf')
++ include_node.HandleAttribute(u'use_base_dir', u'false')
++ includes.AddChild(include_node)
++ root.EndParsing()
++
++ last_dir = os.path.basename(os.getcwd())
++ expected_path = util.normpath(os.path.join(
++ u'..', last_dir, u'flugel/kugel.pdf'))
++ self.assertEqual(root.ToRealPath(include_node.GetInputPath()),
++ expected_path)
++
++ def testCompressGzip(self):
++ self.assertTrue(checkIsGzipped('test_text.txt', 'compress="gzip"'))
++
++ def testCompressGzipByDefault(self):
++ self.assertTrue(checkIsGzipped('test_html.html', ''))
++ self.assertTrue(checkIsGzipped('test_js.js', ''))
++ self.assertTrue(checkIsGzipped('test_css.css', ''))
++ self.assertTrue(checkIsGzipped('test_svg.svg', ''))
++
++ self.assertTrue(checkIsGzipped('test_html.html', 'compress="default"'))
++ self.assertTrue(checkIsGzipped('test_js.js', 'compress="default"'))
++ self.assertTrue(checkIsGzipped('test_css.css', 'compress="default"'))
++ self.assertTrue(checkIsGzipped('test_svg.svg', 'compress="default"'))
++
++ def testSkipInResourceMap(self):
++ root = util.ParseGrdForUnittest('''
++ <includes>
++ <include name="TEST1_TXT" file="test1_text.txt" type="BINDATA"/>
++ <include name="TEST2_TXT" file="test1_text.txt" type="BINDATA"
++ skip_in_resource_map="true"/>
++ <include name="TEST3_TXT" file="test1_text.txt" type="BINDATA"
++ skip_in_resource_map="false"/>
++ </includes>''', base_dir = util.PathFromRoot('grit/testdata'))
++ inc = root.GetChildrenOfType(include.IncludeNode)
++ self.assertTrue(inc[0].IsResourceMapSource())
++ self.assertFalse(inc[1].IsResourceMapSource())
++ self.assertTrue(inc[2].IsResourceMapSource())
++
++ def testAcceptsPreprocess(self):
++ root = util.ParseGrdForUnittest(
++ '''
++ <includes>
++ <include name="PREPROCESS_TEST" file="preprocess_test.html"
++ preprocess="true" compress="false" type="chrome_html"/>
++ </includes>''',
++ base_dir=util.PathFromRoot('grit/testdata'))
++ inc, = root.GetChildrenOfType(include.IncludeNode)
++ result = inc.GetDataPackValue(lang='en', encoding=util.BINARY)
++ self.assertIn(b'should be kept', result)
++ self.assertIn(b'in the middle...', result)
++ self.assertNotIn(b'should be removed', result)
++
++
++if __name__ == '__main__':
++ unittest.main()
+diff --git a/tools/grit/grit/node/mapping.py b/tools/grit/grit/node/mapping.py
+new file mode 100644
+index 0000000000..6297f0b666
+--- /dev/null
++++ b/tools/grit/grit/node/mapping.py
+@@ -0,0 +1,60 @@
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++'''Maps each node type to an implementation class.
++When adding a new node type, you add to this mapping.
++'''
++
++from __future__ import print_function
++
++from grit import exception
++
++from grit.node import empty
++from grit.node import include
++from grit.node import message
++from grit.node import misc
++from grit.node import node_io
++from grit.node import structure
++from grit.node import variant
++
++
++_ELEMENT_TO_CLASS = {
++ 'identifiers' : empty.IdentifiersNode,
++ 'includes' : empty.IncludesNode,
++ 'messages' : empty.MessagesNode,
++ 'outputs' : empty.OutputsNode,
++ 'structures' : empty.StructuresNode,
++ 'translations' : empty.TranslationsNode,
++ 'include' : include.IncludeNode,
++ 'emit' : node_io.EmitNode,
++ 'file' : node_io.FileNode,
++ 'output' : node_io.OutputNode,
++ 'ex' : message.ExNode,
++ 'message' : message.MessageNode,
++ 'ph' : message.PhNode,
++ 'else' : misc.ElseNode,
++ 'grit' : misc.GritNode,
++ 'identifier' : misc.IdentifierNode,
++ 'if' : misc.IfNode,
++ 'part' : misc.PartNode,
++ 'release' : misc.ReleaseNode,
++ 'then' : misc.ThenNode,
++ 'structure' : structure.StructureNode,
++ 'skeleton' : variant.SkeletonNode,
+}
-diff --git a/tools/clang/plugins/tests/inline_ctor.h b/tools/clang/plugins/tests/inline_ctor.h
++
++
++def ElementToClass(name, typeattr):
++ '''Maps an element to a class that handles the element.
++
++ Args:
++ name: 'element' (the name of the element)
++ typeattr: 'type' (the value of the type attribute, if present, else None)
++
++ Return:
++ type
++ '''
++ if name not in _ELEMENT_TO_CLASS:
++ raise exception.UnknownElement()
++ return _ELEMENT_TO_CLASS[name]
+diff --git a/tools/grit/grit/node/message.py b/tools/grit/grit/node/message.py
new file mode 100644
-index 0000000000..d053b2f57d
+index 0000000000..4fa83cf26b
--- /dev/null
-+++ b/tools/clang/plugins/tests/inline_ctor.h
-@@ -0,0 +1,21 @@
-+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+// Use of this source code is governed by a BSD-style license that can be
-+// found in the LICENSE file.
++++ b/tools/grit/grit/node/message.py
+@@ -0,0 +1,362 @@
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
+
-+#ifndef INLINE_CTOR_H_
-+#define INLINE_CTOR_H_
++'''Handling of the <message> element.
++'''
++
++from __future__ import print_function
++
++import re
++
++import six
++
++from grit.node import base
++
++from grit import clique
++from grit import exception
++from grit import lazy_re
++from grit import tclib
++from grit import util
++
++
++# Matches exactly three dots ending a line or followed by whitespace.
++_ELLIPSIS_PATTERN = lazy_re.compile(r'(?<!\.)\.\.\.(?=$|\s)')
++_ELLIPSIS_SYMBOL = u'\u2026' # Ellipsis
++
++# Finds whitespace at the start and end of a string which can be multiline.
++_WHITESPACE = lazy_re.compile(r'(?P<start>\s*)(?P<body>.+?)(?P<end>\s*)\Z',
++ re.DOTALL | re.MULTILINE)
++
++# <ph> placeholder elements should contain the special character formatters
++# used to format <ph> element content.
++# Android format.
++_ANDROID_FORMAT = (r'%[1-9]+\$'
++ r'([-#+ 0,(]*)([0-9]+)?(\.[0-9]+)?'
++ r'([bBhHsScCdoxXeEfgGaAtT%n])')
++# Chrome l10n format.
++_CHROME_FORMAT = r'\$+\d'
++# Windows EWT numeric and GRIT %s %d formats.
++_OTHER_FORMAT = r'%[0-9sd]'
++
++# Finds formatters that must be in a placeholder (<ph>) element.
++_FORMATTERS = lazy_re.compile(
++ '(%s)|(%s)|(%s)' % (_ANDROID_FORMAT, _CHROME_FORMAT, _OTHER_FORMAT))
++_BAD_PLACEHOLDER_MSG = ('ERROR: Placeholder formatter found outside of <ph> '
++ 'tag in message "%s" in %s.')
++_INVALID_PH_CHAR_MSG = ('ERROR: Invalid format characters found in message '
++ '"%s" <ph> tag in %s.')
++
++# Finds HTML tag tokens.
++_HTMLTOKEN = lazy_re.compile(r'<[/]?[a-z][a-z0-9]*[^>]*>', re.I)
++
++# Finds HTML entities.
++_HTMLENTITY = lazy_re.compile(r'&[^\s]*;')
++
++
++class MessageNode(base.ContentNode):
++ '''A <message> element.'''
++
++ # For splitting a list of things that can be separated by commas or
++ # whitespace
++ _SPLIT_RE = lazy_re.compile(r'\s*,\s*|\s+')
++
++ def __init__(self):
++ super(MessageNode, self).__init__()
++ # Valid after EndParsing, this is the MessageClique that contains the
++ # source message and any translations of it that have been loaded.
++ self.clique = None
++
++ # We don't send leading and trailing whitespace into the translation
++ # console, but rather tack it onto the source message and any
++ # translations when formatting them into RC files or what have you.
++ self.ws_at_start = '' # Any whitespace characters at the start of the text
++ self.ws_at_end = '' # --"-- at the end of the text
++
++ # A list of "shortcut groups" this message is in. We check to make sure
++ # that shortcut keys (e.g. &J) within each shortcut group are unique.
++ self.shortcut_groups_ = []
++
++ # Formatter-specific data used to control the output of individual strings.
++ # formatter_data is a space separated list of C preprocessor-style
++ # definitions. Names without values are given the empty string value.
++ # Example: "foo=5 bar baz=100"
++ self.formatter_data = {}
++
++ # Whether or not to convert ... -> U+2026 within Translate().
++ self._replace_ellipsis = False
++
++ def _IsValidChild(self, child):
++ return isinstance(child, (PhNode))
++
++ def _IsValidAttribute(self, name, value):
++ if name not in [
++ 'name', 'offset', 'translateable', 'desc', 'meaning',
++ 'internal_comment', 'shortcut_groups', 'custom_type', 'validation_expr',
++ 'use_name_for_id', 'sub_variable', 'formatter_data',
++ 'is_accessibility_with_no_ui'
++ ]:
++ return False
++ if (name in ('translateable', 'sub_variable') and
++ value not in ['true', 'false']):
++ return False
++ return True
++
++ def SetReplaceEllipsis(self, value):
++ r'''Sets whether to replace ... with \u2026.
++ '''
++ self._replace_ellipsis = value
++
++ def MandatoryAttributes(self):
++ return ['name|offset']
++
++ def DefaultAttributes(self):
++ return {
++ 'custom_type': '',
++ 'desc': '',
++ 'formatter_data': '',
++ 'internal_comment': '',
++ 'is_accessibility_with_no_ui': 'false',
++ 'meaning': '',
++ 'shortcut_groups': '',
++ 'sub_variable': 'false',
++ 'translateable': 'true',
++ 'use_name_for_id': 'false',
++ 'validation_expr': '',
++ }
+
-+#include <string>
-+#include <vector>
++ def HandleAttribute(self, attrib, value):
++ base.ContentNode.HandleAttribute(self, attrib, value)
++ if attrib != 'formatter_data':
++ return
++
++ # Parse value, a space-separated list of defines, into a dict.
++ # Example: "foo=5 bar" -> {'foo':'5', 'bar':''}
++ for item in value.split():
++ name, _, val = item.partition('=')
++ self.formatter_data[name] = val
++
++ def GetTextualIds(self):
++ '''
++ Returns the concatenation of the parent's node first_id and
++ this node's offset if it has one, otherwise just call the
++ superclass' implementation
++ '''
++ if 'offset' not in self.attrs:
++ return super(MessageNode, self).GetTextualIds()
++
++ # we search for the first grouping node in the parents' list
++ # to take care of the case where the first parent is an <if> node
++ grouping_parent = self.parent
++ import grit.node.empty
++ while grouping_parent and not isinstance(grouping_parent,
++ grit.node.empty.GroupingNode):
++ grouping_parent = grouping_parent.parent
++
++ assert 'first_id' in grouping_parent.attrs
++ return [grouping_parent.attrs['first_id'] + '_' + self.attrs['offset']]
++
++ def IsTranslateable(self):
++ return self.attrs['translateable'] == 'true'
++
++ def EndParsing(self):
++ super(MessageNode, self).EndParsing()
++
++ # Make the text (including placeholder references) and list of placeholders,
++ # verify placeholder formats, then strip and store leading and trailing
++ # whitespace and create the tclib.Message() and a clique to contain it.
++
++ text = ''
++ placeholders = []
++
++ for item in self.mixed_content:
++ if isinstance(item, six.string_types):
++ # Not a <ph> element: fail if any <ph> formatters are detected.
++ if _FORMATTERS.search(item):
++ print(_BAD_PLACEHOLDER_MSG % (item, self.source))
++ raise exception.PlaceholderNotInsidePhNode
++ text += item
++ else:
++ # Extract the <ph> element components.
++ presentation = item.attrs['name'].upper()
++ text += presentation
++ ex = ' ' # <ex> example element cdata if present.
++ if len(item.children):
++ ex = item.children[0].GetCdata()
++ original = item.GetCdata()
++
++ # Sanity check the <ph> element content.
++ cdata = original
++ # Replace all HTML tag tokens in cdata.
++ match = _HTMLTOKEN.search(cdata)
++ while match:
++ cdata = cdata.replace(match.group(0), '_')
++ match = _HTMLTOKEN.search(cdata)
++ # Replace all HTML entities in cdata.
++ match = _HTMLENTITY.search(cdata)
++ while match:
++ cdata = cdata.replace(match.group(0), '_')
++ match = _HTMLENTITY.search(cdata)
++ # Remove first matching formatter from cdata.
++ match = _FORMATTERS.search(cdata)
++ if match:
++ cdata = cdata.replace(match.group(0), '')
++ # Fail if <ph> special chars remain in cdata.
++ if re.search(r'[%\$]', cdata):
++ message_id = self.attrs['name'] + ' ' + original;
++ print(_INVALID_PH_CHAR_MSG % (message_id, self.source))
++ raise exception.InvalidCharactersInsidePhNode
++
++ # Otherwise, accept this <ph> placeholder.
++ placeholders.append(tclib.Placeholder(presentation, original, ex))
++
++ m = _WHITESPACE.match(text)
++ if m:
++ self.ws_at_start = m.group('start')
++ self.ws_at_end = m.group('end')
++ text = m.group('body')
++
++ self.shortcut_groups_ = self._SPLIT_RE.split(self.attrs['shortcut_groups'])
++ self.shortcut_groups_ = [i for i in self.shortcut_groups_ if i != '']
++
++ description_or_id = self.attrs['desc']
++ if description_or_id == '' and 'name' in self.attrs:
++ description_or_id = 'ID: %s' % self.attrs['name']
++
++ assigned_id = None
++ if self.attrs['use_name_for_id'] == 'true':
++ assigned_id = self.attrs['name']
++ message = tclib.Message(text=text, placeholders=placeholders,
++ description=description_or_id,
++ meaning=self.attrs['meaning'],
++ assigned_id=assigned_id)
++ self.InstallMessage(message)
++
++ def InstallMessage(self, message):
++ '''Sets this node's clique from a tclib.Message instance.
++
++ Args:
++ message: A tclib.Message.
++ '''
++ self.clique = self.UberClique().MakeClique(message, self.IsTranslateable())
++ for group in self.shortcut_groups_:
++ self.clique.AddToShortcutGroup(group)
++ if self.attrs['custom_type'] != '':
++ self.clique.SetCustomType(util.NewClassInstance(self.attrs['custom_type'],
++ clique.CustomType))
++ elif self.attrs['validation_expr'] != '':
++ self.clique.SetCustomType(
++ clique.OneOffCustomType(self.attrs['validation_expr']))
++
++ def SubstituteMessages(self, substituter):
++ '''Applies substitution to this message.
++
++ Args:
++ substituter: a grit.util.Substituter object.
++ '''
++ message = substituter.SubstituteMessage(self.clique.GetMessage())
++ if message is not self.clique.GetMessage():
++ self.InstallMessage(message)
++
++ def GetCliques(self):
++ return [self.clique] if self.clique else []
++
++ def Translate(self, lang):
++ '''Returns a translated version of this message.
++ '''
++ assert self.clique
++ msg = self.clique.MessageForLanguage(lang,
++ self.PseudoIsAllowed(),
++ self.ShouldFallbackToEnglish()
++ ).GetRealContent()
++ if self._replace_ellipsis:
++ msg = _ELLIPSIS_PATTERN.sub(_ELLIPSIS_SYMBOL, msg)
++ # Always remove all byte order marks (\uFEFF) https://crbug.com/1033305
++ msg = msg.replace(u'\uFEFF','')
++ return msg.replace('[GRITLANGCODE]', lang)
++
++ def NameOrOffset(self):
++ key = 'name' if 'name' in self.attrs else 'offset'
++ return self.attrs[key]
++
++ def ExpandVariables(self):
++ '''We always expand variables on Messages.'''
++ return True
++
++ def GetDataPackValue(self, lang, encoding):
++ '''Returns a str represenation for a data_pack entry.'''
++ message = self.ws_at_start + self.Translate(lang) + self.ws_at_end
++ return util.Encode(message, encoding)
++
++ def IsResourceMapSource(self):
++ return True
++
++ @staticmethod
++ def Construct(parent, message, name, desc='', meaning='', translateable=True):
++ '''Constructs a new message node that is a child of 'parent', with the
++ name, desc, meaning and translateable attributes set using the same-named
++ parameters and the text of the message and any placeholders taken from
++ 'message', which must be a tclib.Message() object.'''
++ # Convert type to appropriate string
++ translateable = 'true' if translateable else 'false'
++
++ node = MessageNode()
++ node.StartParsing('message', parent)
++ node.HandleAttribute('name', name)
++ node.HandleAttribute('desc', desc)
++ node.HandleAttribute('meaning', meaning)
++ node.HandleAttribute('translateable', translateable)
++
++ items = message.GetContent()
++ for ix, item in enumerate(items):
++ if isinstance(item, six.string_types):
++ # Ensure whitespace at front and back of message is correctly handled.
++ if ix == 0:
++ item = "'''" + item
++ if ix == len(items) - 1:
++ item = item + "'''"
++
++ node.AppendContent(item)
++ else:
++ phnode = PhNode()
++ phnode.StartParsing('ph', node)
++ phnode.HandleAttribute('name', item.GetPresentation())
++ phnode.AppendContent(item.GetOriginal())
++
++ if len(item.GetExample()) and item.GetExample() != ' ':
++ exnode = ExNode()
++ exnode.StartParsing('ex', phnode)
++ exnode.AppendContent(item.GetExample())
++ exnode.EndParsing()
++ phnode.AddChild(exnode)
++
++ phnode.EndParsing()
++ node.AddChild(phnode)
++
++ node.EndParsing()
++ return node
++
++
++class PhNode(base.ContentNode):
++ '''A <ph> element.'''
++
++ def _IsValidChild(self, child):
++ return isinstance(child, ExNode)
++
++ def MandatoryAttributes(self):
++ return ['name']
++
++ def EndParsing(self):
++ super(PhNode, self).EndParsing()
++ # We only allow a single example for each placeholder
++ if len(self.children) > 1:
++ raise exception.TooManyExamples()
++
++ def GetTextualIds(self):
++ # The 'name' attribute is not an ID.
++ return []
++
++
++class ExNode(base.ContentNode):
++ '''An <ex> element.'''
++ pass
+diff --git a/tools/grit/grit/node/message_unittest.py b/tools/grit/grit/node/message_unittest.py
+new file mode 100644
+index 0000000000..7a4cbbedc2
+--- /dev/null
++++ b/tools/grit/grit/node/message_unittest.py
+@@ -0,0 +1,380 @@
++#!/usr/bin/env python
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
+
-+class InlineCtorsArentOKInHeader {
-+ public:
-+ InlineCtorsArentOKInHeader() {}
-+ ~InlineCtorsArentOKInHeader() {}
++'''Unit tests for grit.node.message'''
+
-+ private:
-+ std::vector<int> one_;
-+ std::vector<std::string> two_;
-+};
++from __future__ import print_function
+
-+#endif // INLINE_CTOR_H_
-diff --git a/tools/clang/plugins/tests/inline_ctor.txt b/tools/clang/plugins/tests/inline_ctor.txt
++import os
++import sys
++import unittest
++
++if __name__ == '__main__':
++ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
++
++from grit import exception
++from grit import tclib
++from grit import util
++from grit.node import message
++
++class MessageUnittest(unittest.TestCase):
++ def testMessage(self):
++ root = util.ParseGrdForUnittest('''
++ <messages>
++ <message name="IDS_GREETING"
++ desc="Printed to greet the currently logged in user">
++ Hello <ph name="USERNAME">%s<ex>Joi</ex></ph>, how are you doing today?
++ </message>
++ </messages>''')
++ msg, = root.GetChildrenOfType(message.MessageNode)
++ cliques = msg.GetCliques()
++ content = cliques[0].GetMessage().GetPresentableContent()
++ self.failUnless(content == 'Hello USERNAME, how are you doing today?')
++
++ def testMessageWithWhitespace(self):
++ root = util.ParseGrdForUnittest("""\
++ <messages>
++ <message name="IDS_BLA" desc="">
++ ''' Hello there <ph name="USERNAME">%s</ph> '''
++ </message>
++ </messages>""")
++ msg, = root.GetChildrenOfType(message.MessageNode)
++ content = msg.GetCliques()[0].GetMessage().GetPresentableContent()
++ self.failUnless(content == 'Hello there USERNAME')
++ self.failUnless(msg.ws_at_start == ' ')
++ self.failUnless(msg.ws_at_end == ' ')
++
++ def testConstruct(self):
++ msg = tclib.Message(text=" Hello USERNAME, how are you? BINGO\t\t",
++ placeholders=[tclib.Placeholder('USERNAME', '%s', 'Joi'),
++ tclib.Placeholder('BINGO', '%d', '11')])
++ msg_node = message.MessageNode.Construct(None, msg, 'BINGOBONGO')
++ self.failUnless(msg_node.children[0].name == 'ph')
++ self.failUnless(msg_node.children[0].children[0].name == 'ex')
++ self.failUnless(msg_node.children[0].children[0].GetCdata() == 'Joi')
++ self.failUnless(msg_node.children[1].children[0].GetCdata() == '11')
++ self.failUnless(msg_node.ws_at_start == ' ')
++ self.failUnless(msg_node.ws_at_end == '\t\t')
++
++ def testUnicodeConstruct(self):
++ text = u'Howdie \u00fe'
++ msg = tclib.Message(text=text)
++ msg_node = message.MessageNode.Construct(None, msg, 'BINGOBONGO')
++ msg_from_node = msg_node.GetCdata()
++ self.failUnless(msg_from_node == text)
++
++ def testFormatterData(self):
++ root = util.ParseGrdForUnittest("""\
++ <messages>
++ <message name="IDS_BLA" desc="" formatter_data=" foo=123 bar qux=low">
++ Text
++ </message>
++ </messages>""")
++ msg, = root.GetChildrenOfType(message.MessageNode)
++ expected_formatter_data = {
++ 'foo': '123',
++ 'bar': '',
++ 'qux': 'low'}
++
++ # Can't use assertDictEqual, not available in Python 2.6, so do it
++ # by hand.
++ self.failUnlessEqual(len(expected_formatter_data),
++ len(msg.formatter_data))
++ for key in expected_formatter_data:
++ self.failUnlessEqual(expected_formatter_data[key],
++ msg.formatter_data[key])
++
++ def testReplaceEllipsis(self):
++ root = util.ParseGrdForUnittest('''
++ <messages>
++ <message name="IDS_GREETING" desc="">
++ A...B.... <ph name="PH">%s<ex>A</ex></ph>... B... C...
++ </message>
++ </messages>''')
++ msg, = root.GetChildrenOfType(message.MessageNode)
++ msg.SetReplaceEllipsis(True)
++ content = msg.Translate('en')
++ self.failUnlessEqual(u'A...B.... %s\u2026 B\u2026 C\u2026', content)
++
++ def testRemoveByteOrderMark(self):
++ root = util.ParseGrdForUnittest(u'''
++ <messages>
++ <message name="IDS_HAS_BOM" desc="">
++ \uFEFFThis\uFEFF i\uFEFFs OK\uFEFF
++ </message>
++ </messages>''')
++ msg, = root.GetChildrenOfType(message.MessageNode)
++ content = msg.Translate('en')
++ self.failUnlessEqual(u'This is OK', content)
++
++ def testPlaceholderHasTooManyExamples(self):
++ try:
++ util.ParseGrdForUnittest("""\
++ <messages>
++ <message name="IDS_FOO" desc="foo">
++ Hi <ph name="NAME">$1<ex>Joi</ex><ex>Joy</ex></ph>
++ </message>
++ </messages>""")
++ except exception.TooManyExamples:
++ return
++ self.fail('Should have gotten exception')
++
++ def testPlaceholderHasInvalidName(self):
++ try:
++ util.ParseGrdForUnittest("""\
++ <messages>
++ <message name="IDS_FOO" desc="foo">
++ Hi <ph name="ABC!">$1</ph>
++ </message>
++ </messages>""")
++ except exception.InvalidPlaceholderName:
++ return
++ self.fail('Should have gotten exception')
++
++ def testChromeLocalizedFormatIsInsidePhNode(self):
++ try:
++ util.ParseGrdForUnittest("""\
++ <messages>
++ <message name="IDS_CHROME_L10N" desc="l10n format">
++ This message is missing the ph node: $1
++ </message>
++ </messages>""")
++ except exception.PlaceholderNotInsidePhNode:
++ return
++ self.fail('Should have gotten exception')
++
++ def testAndroidStringFormatIsInsidePhNode(self):
++ try:
++ util.ParseGrdForUnittest("""\
++ <messages>
++ <message name="IDS_ANDROID" desc="string format">
++ This message is missing a ph node: %1$s
++ </message>
++ </messages>""")
++ except exception.PlaceholderNotInsidePhNode:
++ return
++ self.fail('Should have gotten exception')
++
++ def testAndroidIntegerFormatIsInsidePhNode(self):
++ try:
++ util.ParseGrdForUnittest("""\
++ <messages>
++ <message name="IDS_ANDROID" desc="integer format">
++ This message is missing a ph node: %2$d
++ </message>
++ </messages>""")
++ except exception.PlaceholderNotInsidePhNode:
++ return
++ self.fail('Should have gotten exception')
++
++ def testAndroidIntegerWidthFormatIsInsidePhNode(self):
++ try:
++ util.ParseGrdForUnittest("""\
++ <messages>
++ <message name="IDS_ANDROID" desc="integer width format">
++ This message is missing a ph node: %2$3d
++ </message>
++ </messages>""")
++ except exception.PlaceholderNotInsidePhNode:
++ return
++ self.fail('Should have gotten exception')
++
++ def testValidAndroidIntegerWidthFormatInPhNode(self):
++ try:
++ util.ParseGrdForUnittest("""\
++ <messages>
++ <message name="IDS_ANDROID_WIDTH">
++ <ph name="VALID">%2$3d<ex>042</ex></ph>
++ </message>
++ </messages>""")
++ except:
++ self.fail('Should not have gotten exception')
++
++ def testAndroidFloatFormatIsInsidePhNode(self):
++ try:
++ util.ParseGrdForUnittest("""\
++ <messages>
++ <message name="IDS_ANDROID" desc="float number format">
++ This message is missing a ph node: %3$4.5f
++ </message>
++ </messages>""")
++ except exception.PlaceholderNotInsidePhNode:
++ return
++ self.fail('Should have gotten exception')
++
++ def testGritStringFormatIsInsidePhNode(self):
++ try:
++ util.ParseGrdForUnittest("""\
++ <messages>
++ <message name="IDS_GRIT_STRING" desc="grit string format">
++ This message is missing the ph node: %s
++ </message>
++ </messages>""")
++ except exception.PlaceholderNotInsidePhNode:
++ return
++ self.fail('Should have gotten exception')
++
++ def testGritIntegerFormatIsInsidePhNode(self):
++ try:
++ util.ParseGrdForUnittest("""\
++ <messages>
++ <message name="IDS_GRIT_INTEGER" desc="grit integer format">
++ This message is missing the ph node: %d
++ </message>
++ </messages>""")
++ except exception.PlaceholderNotInsidePhNode:
++ return
++ self.fail('Should have gotten exception')
++
++ def testWindowsETWIntegerFormatIsInsidePhNode(self):
++ try:
++ util.ParseGrdForUnittest("""\
++ <messages>
++ <message name="IDS_WINDOWS_ETW" desc="ETW tracing integer">
++ This message is missing the ph node: %1
++ </message>
++ </messages>""")
++ except exception.PlaceholderNotInsidePhNode:
++ return
++ self.fail('Should have gotten exception')
++
++ def testValidMultipleFormattersInsidePhNodes(self):
++ root = util.ParseGrdForUnittest("""\
++ <messages>
++ <message name="IDS_MULTIPLE_FORMATTERS">
++ <ph name="ERROR_COUNT">%1$d<ex>1</ex></ph> error, <ph name="WARNING_COUNT">%2$d<ex>1</ex></ph> warning
++ </message>
++ </messages>""")
++ msg, = root.GetChildrenOfType(message.MessageNode)
++ cliques = msg.GetCliques()
++ content = cliques[0].GetMessage().GetPresentableContent()
++ self.failUnless(content == 'ERROR_COUNT error, WARNING_COUNT warning')
++
++ def testMultipleFormattersAreInsidePhNodes(self):
++ failed = True
++ try:
++ util.ParseGrdForUnittest("""\
++ <messages>
++ <message name="IDS_MULTIPLE_FORMATTERS">
++ %1$d error, %2$d warning
++ </message>
++ </messages>""")
++ except exception.PlaceholderNotInsidePhNode:
++ failed = False
++ if failed:
++ self.fail('Should have gotten exception')
++ return
++
++ failed = True
++ try:
++ util.ParseGrdForUnittest("""\
++ <messages>
++ <message name="IDS_MULTIPLE_FORMATTERS">
++ <ph name="ERROR_COUNT">%1$d<ex>1</ex></ph> error, %2$d warning
++ </message>
++ </messages>""")
++ except exception.PlaceholderNotInsidePhNode:
++ failed = False
++ if failed:
++ self.fail('Should have gotten exception')
++ return
++
++ failed = True
++ try:
++ util.ParseGrdForUnittest("""\
++ <messages>
++ <message name="IDS_MULTIPLE_FORMATTERS">
++ <ph name="INVALID">%1$d %2$d</ph>
++ </message>
++ </messages>""")
++ except exception.InvalidCharactersInsidePhNode:
++ failed = False
++ if failed:
++ self.fail('Should have gotten exception')
++ return
++
++ def testValidHTMLFormatInsidePhNode(self):
++ try:
++ util.ParseGrdForUnittest("""\
++ <messages>
++ <message name="IDS_HTML">
++ <ph name="VALID">&lt;span&gt;$1&lt;/span&gt;<ex>1</ex></ph>
++ </message>
++ </messages>""")
++ except:
++ self.fail('Should not have gotten exception')
++
++ def testValidHTMLWithAttributesFormatInsidePhNode(self):
++ try:
++ util.ParseGrdForUnittest("""\
++ <messages>
++ <message name="IDS_HTML_ATTRIBUTE">
++ <ph name="VALID">&lt;span attribute="js:$this %"&gt;$2&lt;/span&gt;<ex>2</ex></ph>
++ </message>
++ </messages>""")
++ except:
++ self.fail('Should not have gotten exception')
++
++ def testValidHTMLEntityFormatInsidePhNode(self):
++ try:
++ util.ParseGrdForUnittest("""\
++ <messages>
++ <message name="IDS_ENTITY">
++ <ph name="VALID">&gt;%1$d&lt;<ex>1</ex></ph>
++ </message>
++ </messages>""")
++ except:
++ self.fail('Should not have gotten exception')
++
++ def testValidMultipleDollarFormatInsidePhNode(self):
++ try:
++ util.ParseGrdForUnittest("""\
++ <messages>
++ <message name="IDS_DOLLARS" desc="l10n dollars format">
++ <ph name="VALID">$$1</ph>
++ </message>
++ </messages>""")
++ except:
++ self.fail('Should not have gotten exception')
++
++ def testInvalidDollarCharacterInsidePhNode(self):
++ try:
++ util.ParseGrdForUnittest("""\
++ <messages>
++ <message name="IDS_BAD_DOLLAR">
++ <ph name="INVALID">%1$d $</ph>
++ </message>
++ </messages>""")
++ except exception.InvalidCharactersInsidePhNode:
++ return
++ self.fail('Should have gotten exception')
++
++ def testInvalidPercentCharacterInsidePhNode(self):
++ try:
++ util.ParseGrdForUnittest("""\
++ <messages>
++ <message name="IDS_BAD_PERCENT">
++ <ph name="INVALID">%1$d %</ph>
++ </message>
++ </messages>""")
++ except exception.InvalidCharactersInsidePhNode:
++ return
++ self.fail('Should have gotten exception')
++
++ def testInvalidMixedFormatCharactersInsidePhNode(self):
++ try:
++ util.ParseGrdForUnittest("""\
++ <messages>
++ <message name="IDS_MIXED_FORMATS">
++ <ph name="INVALID">%1$2</ph>
++ </message>
++ </messages>""")
++ except exception.InvalidCharactersInsidePhNode:
++ return
++ self.fail('Should have gotten exception')
++
++
++if __name__ == '__main__':
++ unittest.main()
+diff --git a/tools/grit/grit/node/misc.py b/tools/grit/grit/node/misc.py
new file mode 100644
-index 0000000000..caa0cb4e3b
+index 0000000000..2d8b06d6a5
--- /dev/null
-+++ b/tools/clang/plugins/tests/inline_ctor.txt
-@@ -0,0 +1,8 @@
-+In file included from inline_ctor.cpp:5:
-+./inline_ctor.h:13:3: warning: [chromium-style] Complex constructor has an inlined body.
-+ InlineCtorsArentOKInHeader() {}
-+ ^
-+./inline_ctor.h:14:3: warning: [chromium-style] Complex destructor has an inline body.
-+ ~InlineCtorsArentOKInHeader() {}
-+ ^
-+2 warnings generated.
-diff --git a/tools/clang/plugins/tests/missing_ctor.cpp b/tools/clang/plugins/tests/missing_ctor.cpp
-new file mode 100644
-index 0000000000..8ee2fb2ac8
---- /dev/null
-+++ b/tools/clang/plugins/tests/missing_ctor.cpp
-@@ -0,0 +1,23 @@
-+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
-+// Use of this source code is governed by a BSD-style license that can be
-+// found in the LICENSE file.
++++ b/tools/grit/grit/node/misc.py
+@@ -0,0 +1,707 @@
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
+
-+#include "missing_ctor.h"
++"""Miscellaneous node types.
++"""
+
-+#include <string>
-+#include <vector>
++from __future__ import print_function
+
-+// We don't warn on classes that use default ctors in cpp files.
-+class MissingInCPPOK {
-+ public:
++import os.path
++import re
++import sys
+
-+ private:
-+ std::vector<int> one_;
-+ std::vector<std::string> two_;
-+};
++import six
++
++from grit import constants
++from grit import exception
++from grit import util
++from grit.extern import FP
++from grit.node import base
++from grit.node import message
++from grit.node import node_io
++
++
++# Python 3 doesn't have long() as int() works everywhere. But we really do need
++# the long() behavior on Python 2 as our ids are much too large for int().
++try:
++ long
++except NameError:
++ long = int
++
++
++# RTL languages
++# TODO(jennyz): remove this fixed set of RTL language array
++# now that generic expand_variable code exists.
++_RTL_LANGS = (
++ 'ar', # Arabic
++ 'fa', # Farsi
++ 'iw', # Hebrew
++ 'ks', # Kashmiri
++ 'ku', # Kurdish
++ 'ps', # Pashto
++ 'ur', # Urdu
++ 'yi', # Yiddish
++)
++
++
++def _ReadFirstIdsFromFile(filename, defines):
++ """Read the starting resource id values from |filename|. We also
++ expand variables of the form <(FOO) based on defines passed in on
++ the command line.
++
++ Returns a tuple, the absolute path of SRCDIR followed by the
++ first_ids dictionary.
++ """
++ first_ids_dict = eval(util.ReadFile(filename, 'utf-8'))
++ src_root_dir = os.path.abspath(os.path.join(os.path.dirname(filename),
++ first_ids_dict['SRCDIR']))
++
++ def ReplaceVariable(matchobj):
++ for key, value in defines.items():
++ if matchobj.group(1) == key:
++ return value
++ return ''
++
++ renames = []
++ for grd_filename in first_ids_dict:
++ new_grd_filename = re.sub(r'<\(([A-Za-z_]+)\)', ReplaceVariable,
++ grd_filename)
++ if new_grd_filename != grd_filename:
++ abs_grd_filename = os.path.abspath(new_grd_filename)
++ if abs_grd_filename[:len(src_root_dir)] != src_root_dir:
++ new_grd_filename = os.path.basename(abs_grd_filename)
++ else:
++ new_grd_filename = abs_grd_filename[len(src_root_dir) + 1:]
++ new_grd_filename = new_grd_filename.replace('\\', '/')
++ renames.append((grd_filename, new_grd_filename))
++
++ for grd_filename, new_grd_filename in renames:
++ first_ids_dict[new_grd_filename] = first_ids_dict[grd_filename]
++ del(first_ids_dict[grd_filename])
++
++ return (src_root_dir, first_ids_dict)
++
++
++def _ComputeIds(root, predetermined_tids):
++ """Returns a dict of textual id -> numeric id for all nodes in root.
++
++ IDs are mostly assigned sequentially, but will vary based on:
++ * first_id node attribute (from first_ids_file)
++ * hash of textual id (if not first_id is defined)
++ * offset node attribute
++ * whether the textual id matches a system id
++ * whether the node generates its own ID via GetId()
++
++ Args:
++ predetermined_tids: Dict of textual id -> numeric id to use in return dict.
++ """
++ from grit.node import empty, include, misc, structure
++
++ ids = {} # Maps numeric id to textual id
++ tids = {} # Maps textual id to numeric id
++ id_reasons = {} # Maps numeric id to text id and a human-readable explanation
++ group = None
++ last_id = None
++ predetermined_ids = {value: key
++ for key, value in predetermined_tids.items()}
++
++ for item in root:
++ if isinstance(item, empty.GroupingNode):
++ # Note: this won't work if any GroupingNode can be contained inside
++ # another.
++ group = item
++ last_id = None
++ continue
++
++ assert not item.GetTextualIds() or isinstance(item,
++ (include.IncludeNode, message.MessageNode,
++ misc.IdentifierNode, structure.StructureNode))
++
++ # Resources that use the RES protocol don't need
++ # any numerical ids generated, so we skip them altogether.
++ # This is accomplished by setting the flag 'generateid' to false
++ # in the GRD file.
++ if item.attrs.get('generateid', 'true') == 'false':
++ continue
++
++ for tid in item.GetTextualIds():
++ if util.SYSTEM_IDENTIFIERS.match(tid):
++ # Don't emit a new ID for predefined IDs
++ continue
++
++ if tid in tids:
++ continue
++
++ if predetermined_tids and tid in predetermined_tids:
++ id = predetermined_tids[tid]
++ reason = "from predetermined_tids map"
++
++ # Some identifier nodes can provide their own id,
++ # and we use that id in the generated header in that case.
++ elif hasattr(item, 'GetId') and item.GetId():
++ id = long(item.GetId())
++ reason = 'returned by GetId() method'
++
++ elif ('offset' in item.attrs and group and
++ group.attrs.get('first_id', '') != ''):
++ offset_text = item.attrs['offset']
++ parent_text = group.attrs['first_id']
++
++ try:
++ offset_id = long(offset_text)
++ except ValueError:
++ offset_id = tids[offset_text]
++
++ try:
++ parent_id = long(parent_text)
++ except ValueError:
++ parent_id = tids[parent_text]
++
++ id = parent_id + offset_id
++ reason = 'first_id %d + offset %d' % (parent_id, offset_id)
++
++ # We try to allocate IDs sequentially for blocks of items that might
++ # be related, for instance strings in a stringtable (as their IDs might be
++ # used e.g. as IDs for some radio buttons, in which case the IDs must
++ # be sequential).
++ #
++ # We do this by having the first item in a section store its computed ID
++ # (computed from a fingerprint) in its parent object. Subsequent children
++ # of the same parent will then try to get IDs that sequentially follow
++ # the currently stored ID (on the parent) and increment it.
++ elif last_id is None:
++ # First check if the starting ID is explicitly specified by the parent.
++ if group and group.attrs.get('first_id', '') != '':
++ id = long(group.attrs['first_id'])
++ reason = "from parent's first_id attribute"
++ else:
++ # Automatically generate the ID based on the first clique from the
++ # first child of the first child node of our parent (i.e. when we
++ # first get to this location in the code).
++
++ # According to
++ # http://msdn.microsoft.com/en-us/library/t2zechd4(VS.71).aspx
++ # the safe usable range for resource IDs in Windows is from decimal
++ # 101 to 0x7FFF.
++
++ id = FP.UnsignedFingerPrint(tid)
++ id = id % (0x7FFF - 101) + 101
++ reason = 'chosen by random fingerprint -- use first_id to override'
++
++ last_id = id
++ else:
++ id = last_id = last_id + 1
++ reason = 'sequentially assigned'
++
++ reason = "%s (%s)" % (tid, reason)
++ # Don't fail when 'offset' is specified, as the base and the 0th
++ # offset will have the same ID.
++ if id in id_reasons and not 'offset' in item.attrs:
++ raise exception.IdRangeOverlap('ID %d was assigned to both %s and %s.'
++ % (id, id_reasons[id], reason))
++
++ if id < 101:
++ print('WARNING: Numeric resource IDs should be greater than 100 to\n'
++ 'avoid conflicts with system-defined resource IDs.')
++
++ if tid not in predetermined_tids and id in predetermined_ids:
++ raise exception.IdRangeOverlap('ID %d overlaps between %s and %s'
++ % (id, tid, predetermined_ids[tid]))
++
++ ids[id] = tid
++ tids[tid] = id
++ id_reasons[id] = reason
++
++ return tids
++
++class SplicingNode(base.Node):
++ """A node whose children should be considered to be at the same level as
++ its siblings for most purposes. This includes <if> and <part> nodes.
++ """
++
++ def _IsValidChild(self, child):
++ assert self.parent, '<%s> node should never be root.' % self.name
++ if isinstance(child, SplicingNode):
++ return True # avoid O(n^2) behavior
++ return self.parent._IsValidChild(child)
++
++
++class IfNode(SplicingNode):
++ """A node for conditional inclusion of resources.
++ """
++
++ def MandatoryAttributes(self):
++ return ['expr']
++
++ def _IsValidChild(self, child):
++ return (isinstance(child, (ThenNode, ElseNode)) or
++ super(IfNode, self)._IsValidChild(child))
++
++ def EndParsing(self):
++ children = self.children
++ self.if_then_else = False
++ if any(isinstance(node, (ThenNode, ElseNode)) for node in children):
++ if (len(children) != 2 or not isinstance(children[0], ThenNode) or
++ not isinstance(children[1], ElseNode)):
++ raise exception.UnexpectedChild(
++ '<if> element must be <if><then>...</then><else>...</else></if>')
++ self.if_then_else = True
++
++ def ActiveChildren(self):
++ cond = self.EvaluateCondition(self.attrs['expr'])
++ if self.if_then_else:
++ return self.children[0 if cond else 1].ActiveChildren()
++ else:
++ # Equivalent to having all children inside <then> with an empty <else>
++ return super(IfNode, self).ActiveChildren() if cond else []
++
++
++class ThenNode(SplicingNode):
++ """A <then> node. Can only appear directly inside an <if> node."""
++ pass
++
++
++class ElseNode(SplicingNode):
++ """An <else> node. Can only appear directly inside an <if> node."""
++ pass
++
++
++class PartNode(SplicingNode):
++ """A node for inclusion of sub-grd (*.grp) files.
++ """
++
++ def __init__(self):
++ super(PartNode, self).__init__()
++ self.started_inclusion = False
++
++ def MandatoryAttributes(self):
++ return ['file']
++
++ def _IsValidChild(self, child):
++ return self.started_inclusion and super(PartNode, self)._IsValidChild(child)
++
++
++class ReleaseNode(base.Node):
++ """The <release> element."""
++
++ def _IsValidChild(self, child):
++ from grit.node import empty
++ return isinstance(child, (empty.IncludesNode, empty.MessagesNode,
++ empty.StructuresNode, empty.IdentifiersNode))
++
++ def _IsValidAttribute(self, name, value):
++ return (
++ (name == 'seq' and int(value) <= self.GetRoot().GetCurrentRelease()) or
++ name == 'allow_pseudo'
++ )
++
++ def MandatoryAttributes(self):
++ return ['seq']
++
++ def DefaultAttributes(self):
++ return { 'allow_pseudo' : 'true' }
++
++
++class GritNode(base.Node):
++ """The <grit> root element."""
++
++ def __init__(self):
++ super(GritNode, self).__init__()
++ self.output_language = ''
++ self.defines = {}
++ self.substituter = None
++ self.target_platform = sys.platform
++ self.whitelist_support = False
++ self._predetermined_ids_file = None
++ self._id_map = None # Dict of textual_id -> numeric_id.
++
++ def _IsValidChild(self, child):
++ from grit.node import empty
++ return isinstance(child, (ReleaseNode, empty.TranslationsNode,
++ empty.OutputsNode))
++
++ def _IsValidAttribute(self, name, value):
++ if name not in ['base_dir', 'first_ids_file', 'source_lang_id',
++ 'latest_public_release', 'current_release',
++ 'enc_check', 'tc_project', 'grit_version',
++ 'output_all_resource_defines']:
++ return False
++ if name in ['latest_public_release', 'current_release'] and value.strip(
++ '0123456789') != '':
++ return False
++ return True
++
++ def MandatoryAttributes(self):
++ return ['latest_public_release', 'current_release']
++
++ def DefaultAttributes(self):
++ return {
++ 'base_dir' : '.',
++ 'first_ids_file': '',
++ 'grit_version': 1,
++ 'source_lang_id' : 'en',
++ 'enc_check' : constants.ENCODING_CHECK,
++ 'tc_project' : 'NEED_TO_SET_tc_project_ATTRIBUTE',
++ }
++
++ def EndParsing(self):
++ super(GritNode, self).EndParsing()
++ if (int(self.attrs['latest_public_release'])
++ > int(self.attrs['current_release'])):
++ raise exception.Parsing('latest_public_release cannot have a greater '
++ 'value than current_release')
++
++ self.ValidateUniqueIds()
++
++ # Add the encoding check if it's not present (should ensure that it's always
++ # present in all .grd files generated by GRIT). If it's present, assert if
++ # it's not correct.
++ if 'enc_check' not in self.attrs or self.attrs['enc_check'] == '':
++ self.attrs['enc_check'] = constants.ENCODING_CHECK
++ else:
++ assert self.attrs['enc_check'] == constants.ENCODING_CHECK, (
++ 'Are you sure your .grd file is in the correct encoding (UTF-8)?')
++
++ def ValidateUniqueIds(self):
++ """Validate that 'name' attribute is unique in all nodes in this tree
++ except for nodes that are children of <if> nodes.
++ """
++ unique_names = {}
++ duplicate_names = []
++ # To avoid false positives from mutually exclusive <if> clauses, check
++ # against whatever the output condition happens to be right now.
++ # TODO(benrg): do something better.
++ for node in self.ActiveDescendants():
++ if node.attrs.get('generateid', 'true') == 'false':
++ continue # Duplication not relevant in that case
++
++ for node_id in node.GetTextualIds():
++ if util.SYSTEM_IDENTIFIERS.match(node_id):
++ continue # predefined IDs are sometimes used more than once
++
++ if node_id in unique_names and node_id not in duplicate_names:
++ duplicate_names.append(node_id)
++ unique_names[node_id] = 1
++
++ if len(duplicate_names):
++ raise exception.DuplicateKey(', '.join(duplicate_names))
++
++
++ def GetCurrentRelease(self):
++ """Returns the current release number."""
++ return int(self.attrs['current_release'])
++
++ def GetLatestPublicRelease(self):
++ """Returns the latest public release number."""
++ return int(self.attrs['latest_public_release'])
++
++ def GetSourceLanguage(self):
++ """Returns the language code of the source language."""
++ return self.attrs['source_lang_id']
++
++ def GetTcProject(self):
++ """Returns the name of this project in the TranslationConsole, or
++ 'NEED_TO_SET_tc_project_ATTRIBUTE' if it is not defined."""
++ return self.attrs['tc_project']
++
++ def SetOwnDir(self, dir):
++ """Informs the 'grit' element of the directory the file it is in resides.
++ This allows it to calculate relative paths from the input file, which is
++ what we desire (rather than from the current path).
++
++ Args:
++ dir: r'c:\bla'
++
++ Return:
++ None
++ """
++ assert dir
++ self.base_dir = os.path.normpath(os.path.join(dir, self.attrs['base_dir']))
++
++ def GetBaseDir(self):
++ """Returns the base directory, relative to the working directory. To get
++ the base directory as set in the .grd file, use GetOriginalBaseDir()
++ """
++ if hasattr(self, 'base_dir'):
++ return self.base_dir
++ else:
++ return self.GetOriginalBaseDir()
++
++ def GetOriginalBaseDir(self):
++ """Returns the base directory, as set in the .grd file.
++ """
++ return self.attrs['base_dir']
++
++ def IsWhitelistSupportEnabled(self):
++ return self.whitelist_support
++
++ def SetWhitelistSupportEnabled(self, whitelist_support):
++ self.whitelist_support = whitelist_support
++
++ def GetInputFiles(self):
++ """Returns the list of files that are read to produce the output."""
++
++ # Importing this here avoids a circular dependency in the imports.
++ # pylint: disable-msg=C6204
++ from grit.node import include
++ from grit.node import misc
++ from grit.node import structure
++ from grit.node import variant
++
++ # Check if the input is required for any output configuration.
++ input_files = set()
++ # Collect even inactive PartNodes since they affect ID assignments.
++ for node in self:
++ if isinstance(node, misc.PartNode):
++ input_files.add(self.ToRealPath(node.GetInputPath()))
++
++ old_output_language = self.output_language
++ for lang, ctx, fallback in self.GetConfigurations():
++ self.SetOutputLanguage(lang or self.GetSourceLanguage())
++ self.SetOutputContext(ctx)
++ self.SetFallbackToDefaultLayout(fallback)
++
++ for node in self.ActiveDescendants():
++ if isinstance(node, (node_io.FileNode, include.IncludeNode,
++ structure.StructureNode, variant.SkeletonNode)):
++ input_path = node.GetInputPath()
++ if input_path is not None:
++ input_files.add(self.ToRealPath(input_path))
++
++ # If it's a flattened node, grab inlined resources too.
++ if ((node.name == 'structure' or node.name == 'include')
++ and node.attrs['flattenhtml'] == 'true'):
++ if node.name == 'structure':
++ node.RunPreSubstitutionGatherer()
++ input_files.update(node.GetHtmlResourceFilenames())
++
++ self.SetOutputLanguage(old_output_language)
++ return sorted(input_files)
++
++ def GetFirstIdsFile(self):
++ """Returns a usable path to the first_ids file, if set, otherwise
++ returns None.
++
++ The first_ids_file attribute is by default relative to the
++ base_dir of the .grd file, but may be prefixed by GRIT_DIR/,
++ which makes it relative to the directory of grit.py
++ (e.g. GRIT_DIR/../gritsettings/resource_ids).
++ """
++ if not self.attrs['first_ids_file']:
++ return None
++
++ path = self.attrs['first_ids_file']
++ GRIT_DIR_PREFIX = 'GRIT_DIR'
++ if (path.startswith(GRIT_DIR_PREFIX)
++ and path[len(GRIT_DIR_PREFIX)] in ['/', '\\']):
++ return util.PathFromRoot(path[len(GRIT_DIR_PREFIX) + 1:])
++ else:
++ return self.ToRealPath(path)
++
++ def GetOutputFiles(self):
++ """Returns the list of <output> nodes that are descendants of this node's
++ <outputs> child and are not enclosed by unsatisfied <if> conditionals.
++ """
++ for child in self.children:
++ if child.name == 'outputs':
++ return [node for node in child.ActiveDescendants()
++ if node.name == 'output']
++ raise exception.MissingElement()
++
++ def GetConfigurations(self):
++ """Returns the distinct (language, context, fallback_to_default_layout)
++ triples from the output nodes.
++ """
++ return set((n.GetLanguage(), n.GetContext(), n.GetFallbackToDefaultLayout())
++ for n in self.GetOutputFiles())
++
++ def GetSubstitutionMessages(self):
++ """Returns the list of <message sub_variable="true"> nodes."""
++ return [n for n in self.ActiveDescendants()
++ if isinstance(n, message.MessageNode)
++ and n.attrs['sub_variable'] == 'true']
++
++ def SetOutputLanguage(self, output_language):
++ """Set the output language. Prepares substitutions.
++
++ The substitutions are reset every time the language is changed.
++ They include messages designated as variables, and language codes for html
++ and rc files.
++
++ Args:
++ output_language: a two-letter language code (eg: 'en', 'ar'...) or ''
++ """
++ if not output_language:
++ # We do not specify the output language for .grh files,
++ # so we get an empty string as the default.
++ # The value should match grit.clique.MessageClique.source_language.
++ output_language = self.GetSourceLanguage()
++ if output_language != self.output_language:
++ self.output_language = output_language
++ self.substituter = None # force recalculate
++
++ def SetOutputContext(self, output_context):
++ self.output_context = output_context
++ self.substituter = None # force recalculate
++
++ def SetFallbackToDefaultLayout(self, fallback_to_default_layout):
++ self.fallback_to_default_layout = fallback_to_default_layout
++ self.substituter = None # force recalculate
++
++ def SetDefines(self, defines):
++ self.defines = defines
++ self.substituter = None # force recalculate
++
++ def SetTargetPlatform(self, target_platform):
++ self.target_platform = target_platform
++
++ def GetSubstituter(self):
++ if self.substituter is None:
++ self.substituter = util.Substituter()
++ self.substituter.AddMessages(self.GetSubstitutionMessages(),
++ self.output_language)
++ if self.output_language in _RTL_LANGS:
++ direction = 'dir="RTL"'
++ else:
++ direction = 'dir="LTR"'
++ self.substituter.AddSubstitutions({
++ 'GRITLANGCODE': self.output_language,
++ 'GRITDIR': direction,
++ })
++ from grit.format import rc # avoid circular dep
++ rc.RcSubstitutions(self.substituter, self.output_language)
++ return self.substituter
++
++ def AssignFirstIds(self, filename_or_stream, defines):
++ """Assign first ids to each grouping node based on values from the
++ first_ids file (if specified on the <grit> node).
++ """
++ assert self._id_map is None, 'AssignFirstIds() after InitializeIds()'
++ # If the input is a stream, then we're probably in a unit test and
++ # should skip this step.
++ if not isinstance(filename_or_stream, six.string_types):
++ return
++
++ # Nothing to do if the first_ids_filename attribute isn't set.
++ first_ids_filename = self.GetFirstIdsFile()
++ if not first_ids_filename:
++ return
++
++ src_root_dir, first_ids = _ReadFirstIdsFromFile(first_ids_filename,
++ defines)
++ from grit.node import empty
++ for node in self.Preorder():
++ if isinstance(node, empty.GroupingNode):
++ abs_filename = os.path.abspath(filename_or_stream)
++ if abs_filename[:len(src_root_dir)] != src_root_dir:
++ filename = os.path.basename(filename_or_stream)
++ else:
++ filename = abs_filename[len(src_root_dir) + 1:]
++ filename = filename.replace('\\', '/')
++
++ if node.attrs['first_id'] != '':
++ raise Exception(
++ "Don't set the first_id attribute when using the first_ids_file "
++ "attribute on the <grit> node, update %s instead." %
++ first_ids_filename)
++
++ try:
++ id_list = first_ids[filename][node.name]
++ except KeyError as e:
++ print('-' * 78)
++ print('Resource id not set for %s (%s)!' % (filename, node.name))
++ print('Please update %s to include an entry for %s. See the '
++ 'comments in resource_ids for information on why you need to '
++ 'update that file.' % (first_ids_filename, filename))
++ print('-' * 78)
++ raise e
++
++ try:
++ node.attrs['first_id'] = str(id_list.pop(0))
++ except IndexError as e:
++ raise Exception('Please update %s and add a first id for %s (%s).'
++ % (first_ids_filename, filename, node.name))
++
++ def GetIdMap(self):
++ '''Return a dictionary mapping textual ids to numeric ids.'''
++ return self._id_map
++
++ def SetPredeterminedIdsFile(self, predetermined_ids_file):
++ assert self._id_map is None, (
++ 'SetPredeterminedIdsFile() after InitializeIds()')
++ self._predetermined_ids_file = predetermined_ids_file
++
++ def InitializeIds(self):
++ '''Initializes the text ID -> numeric ID mapping.'''
++ predetermined_id_map = {}
++ if self._predetermined_ids_file:
++ with open(self._predetermined_ids_file) as f:
++ for line in f:
++ tid, nid = line.split()
++ predetermined_id_map[tid] = int(nid)
++ self._id_map = _ComputeIds(self, predetermined_id_map)
++
++ def RunGatherers(self, debug=False):
++ '''Call RunPreSubstitutionGatherer() on every node of the tree, then apply
++ substitutions, then call RunPostSubstitutionGatherer() on every node.
++
++ The substitutions step requires that the output language has been set.
++ Locally, get the Substitution messages and add them to the substituter.
++ Also add substitutions for language codes in the Rc.
++
++ Args:
++ debug: will print information while running gatherers.
++ '''
++ for node in self.ActiveDescendants():
++ if hasattr(node, 'RunPreSubstitutionGatherer'):
++ with node:
++ node.RunPreSubstitutionGatherer(debug=debug)
++
++ assert self.output_language
++ self.SubstituteMessages(self.GetSubstituter())
++
++ for node in self.ActiveDescendants():
++ if hasattr(node, 'RunPostSubstitutionGatherer'):
++ with node:
++ node.RunPostSubstitutionGatherer(debug=debug)
++
++
++class IdentifierNode(base.Node):
++ """A node for specifying identifiers that should appear in the resource
++ header file, and be unique amongst all other resource identifiers, but don't
++ have any other attributes or reference any resources.
++ """
++
++ def MandatoryAttributes(self):
++ return ['name']
++
++ def DefaultAttributes(self):
++ return { 'comment' : '', 'id' : '', 'systemid': 'false' }
++
++ def GetId(self):
++ """Returns the id of this identifier if it has one, None otherwise
++ """
++ if 'id' in self.attrs:
++ return self.attrs['id']
++ return None
++
++ def EndParsing(self):
++ """Handles system identifiers."""
++ super(IdentifierNode, self).EndParsing()
++ if self.attrs['systemid'] == 'true':
++ util.SetupSystemIdentifiers((self.attrs['name'],))
++
++ @staticmethod
++ def Construct(parent, name, id, comment, systemid='false'):
++ """Creates a new node which is a child of 'parent', with attributes set
++ by parameters of the same name.
++ """
++ node = IdentifierNode()
++ node.StartParsing('identifier', parent)
++ node.HandleAttribute('name', name)
++ node.HandleAttribute('id', id)
++ node.HandleAttribute('comment', comment)
++ node.HandleAttribute('systemid', systemid)
++ node.EndParsing()
++ return node
+diff --git a/tools/grit/grit/node/misc_unittest.py b/tools/grit/grit/node/misc_unittest.py
+new file mode 100644
+index 0000000000..c192b096f4
+--- /dev/null
++++ b/tools/grit/grit/node/misc_unittest.py
+@@ -0,0 +1,590 @@
++#!/usr/bin/env python
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++'''Unit tests for misc.GritNode'''
++
++from __future__ import print_function
++
++import contextlib
++import os
++import sys
++import tempfile
++import unittest
++
++if __name__ == '__main__':
++ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
++
++from six import StringIO
++
++from grit import grd_reader
++import grit.exception
++from grit import util
++from grit.format import rc
++from grit.format import rc_header
++from grit.node import misc
++
++
++@contextlib.contextmanager
++def _MakeTempPredeterminedIdsFile(content):
++ """Write the |content| string to a temporary file.
++
++ The temporary file must be deleted by the caller.
++
++ Example:
++ with _MakeTempPredeterminedIdsFile('foo') as path:
++ ...
++ os.remove(path)
++
++ Args:
++ content: The string to write.
++
++ Yields:
++ The name of the temporary file.
++ """
++ with tempfile.NamedTemporaryFile(mode='w', delete=False) as f:
++ f.write(content)
++ f.flush()
++ f.close()
++ yield f.name
++
++
++class GritNodeUnittest(unittest.TestCase):
++ def testUniqueNameAttribute(self):
++ try:
++ restree = grd_reader.Parse(
++ util.PathFromRoot('grit/testdata/duplicate-name-input.xml'))
++ self.fail('Expected parsing exception because of duplicate names.')
++ except grit.exception.Parsing:
++ pass # Expected case
++
++ def testReadFirstIdsFromFile(self):
++ test_resource_ids = os.path.join(os.path.dirname(__file__), '..',
++ 'testdata', 'resource_ids')
++ base_dir = os.path.dirname(test_resource_ids)
++
++ src_dir, id_dict = misc._ReadFirstIdsFromFile(
++ test_resource_ids,
++ {
++ 'FOO': os.path.join(base_dir, 'bar'),
++ 'SHARED_INTERMEDIATE_DIR': os.path.join(base_dir,
++ 'out/Release/obj/gen'),
++ })
++ self.assertEqual({}, id_dict.get('bar/file.grd', None))
++ self.assertEqual({},
++ id_dict.get('out/Release/obj/gen/devtools/devtools.grd', None))
++
++ src_dir, id_dict = misc._ReadFirstIdsFromFile(
++ test_resource_ids,
++ {
++ 'SHARED_INTERMEDIATE_DIR': '/outside/src_dir',
++ })
++ self.assertEqual({}, id_dict.get('devtools.grd', None))
++
++ # Verifies that GetInputFiles() returns the correct list of files
++ # corresponding to ChromeScaledImage nodes when assets are missing.
++ def testGetInputFilesChromeScaledImage(self):
++ chrome_html_path = util.PathFromRoot('grit/testdata/chrome_html.html')
++ xml = '''<?xml version="1.0" encoding="utf-8"?>
++ <grit latest_public_release="0" current_release="1">
++ <outputs>
++ <output filename="default.pak" type="data_package" context="default_100_percent" />
++ <output filename="special.pak" type="data_package" context="special_100_percent" fallback_to_default_layout="false" />
++ </outputs>
++ <release seq="1">
++ <structures fallback_to_low_resolution="true">
++ <structure type="chrome_scaled_image" name="IDR_A" file="a.png" />
++ <structure type="chrome_scaled_image" name="IDR_B" file="b.png" />
++ <structure type="chrome_html" name="HTML_FILE1" file="%s" flattenhtml="true" />
++ </structures>
++ </release>
++ </grit>''' % chrome_html_path
++
++ grd = grd_reader.Parse(StringIO(xml),
++ util.PathFromRoot('grit/testdata'))
++ expected = ['chrome_html.html', 'default_100_percent/a.png',
++ 'default_100_percent/b.png', 'included_sample.html',
++ 'special_100_percent/a.png']
++ actual = [os.path.relpath(path, util.PathFromRoot('grit/testdata')) for
++ path in grd.GetInputFiles()]
++ # Convert path separator for Windows paths.
++ actual = [path.replace('\\', '/') for path in actual]
++ self.assertEquals(expected, actual)
++
++ # Verifies that GetInputFiles() returns the correct list of files
++ # when files include other files.
++ def testGetInputFilesFromIncludes(self):
++ chrome_html_path = util.PathFromRoot('grit/testdata/chrome_html.html')
++ xml = '''<?xml version="1.0" encoding="utf-8"?>
++ <grit latest_public_release="0" current_release="1">
++ <outputs>
++ <output filename="default.pak" type="data_package" context="default_100_percent" />
++ <output filename="special.pak" type="data_package" context="special_100_percent" fallback_to_default_layout="false" />
++ </outputs>
++ <release seq="1">
++ <includes>
++ <include name="IDR_TESTDATA_CHROME_HTML" file="%s" flattenhtml="true"
++ allowexternalscript="true" type="BINDATA" />
++ </includes>
++ </release>
++ </grit>''' % chrome_html_path
++
++ grd = grd_reader.Parse(StringIO(xml), util.PathFromRoot('grit/testdata'))
++ expected = ['chrome_html.html', 'included_sample.html']
++ actual = [os.path.relpath(path, util.PathFromRoot('grit/testdata')) for
++ path in grd.GetInputFiles()]
++ # Convert path separator for Windows paths.
++ actual = [path.replace('\\', '/') for path in actual]
++ self.assertEquals(expected, actual)
++
++ def testNonDefaultEntry(self):
++ grd = util.ParseGrdForUnittest('''
++ <messages>
++ <message name="IDS_A" desc="foo">bar</message>
++ <if expr="lang == 'fr'">
++ <message name="IDS_B" desc="foo">bar</message>
++ </if>
++ </messages>''')
++ grd.SetOutputLanguage('fr')
++ output = ''.join(rc_header.Format(grd, 'fr', '.'))
++ self.assertIn('#define IDS_A 2378\n#define IDS_B 2379', output)
++
++ def testExplicitFirstIdOverlaps(self):
++ # second first_id will overlap preexisting range
++ self.assertRaises(grit.exception.IdRangeOverlap,
++ util.ParseGrdForUnittest, '''
++ <includes first_id="300" comment="bingo">
++ <include type="gif" name="ID_LOGO" file="images/logo.gif" />
++ <include type="gif" name="ID_LOGO2" file="images/logo2.gif" />
++ </includes>
++ <messages first_id="301">
++ <message name="IDS_GREETING" desc="Printed to greet the currently logged in user">
++ Hello <ph name="USERNAME">%s<ex>Joi</ex></ph>, how are you doing today?
++ </message>
++ <message name="IDS_SMURFGEBURF">Frubegfrums</message>
++ </messages>''')
++
++ def testImplicitOverlapsPreexisting(self):
++ # second message in <messages> will overlap preexisting range
++ self.assertRaises(grit.exception.IdRangeOverlap,
++ util.ParseGrdForUnittest, '''
++ <includes first_id="301" comment="bingo">
++ <include type="gif" name="ID_LOGO" file="images/logo.gif" />
++ <include type="gif" name="ID_LOGO2" file="images/logo2.gif" />
++ </includes>
++ <messages first_id="300">
++ <message name="IDS_GREETING" desc="Printed to greet the currently logged in user">
++ Hello <ph name="USERNAME">%s<ex>Joi</ex></ph>, how are you doing today?
++ </message>
++ <message name="IDS_SMURFGEBURF">Frubegfrums</message>
++ </messages>''')
++
++ def testPredeterminedIds(self):
++ with _MakeTempPredeterminedIdsFile('IDS_A 101\nIDS_B 102') as ids_file:
++ grd = util.ParseGrdForUnittest('''
++ <includes first_id="300" comment="bingo">
++ <include type="gif" name="IDS_B" file="images/logo.gif" />
++ </includes>
++ <messages first_id="10000">
++ <message name="IDS_GREETING" desc="Printed to greet the currently logged in user">
++ Hello <ph name="USERNAME">%s<ex>Joi</ex></ph>, how are you doing today?
++ </message>
++ <message name="IDS_A">
++ Bongo!
++ </message>
++ </messages>''', predetermined_ids_file=ids_file)
++ output = rc_header.FormatDefines(grd)
++ self.assertEqual(('#define IDS_B 102\n'
++ '#define IDS_GREETING 10000\n'
++ '#define IDS_A 101\n'), ''.join(output))
++ os.remove(ids_file)
++
++ def testPredeterminedIdsOverlap(self):
++ with _MakeTempPredeterminedIdsFile('ID_LOGO 10000') as ids_file:
++ self.assertRaises(grit.exception.IdRangeOverlap,
++ util.ParseGrdForUnittest, '''
++ <includes first_id="300" comment="bingo">
++ <include type="gif" name="ID_LOGO" file="images/logo.gif" />
++ </includes>
++ <messages first_id="10000">
++ <message name="IDS_GREETING" desc="Printed to greet the currently logged in user">
++ Hello <ph name="USERNAME">%s<ex>Joi</ex></ph>, how are you doing today?
++ </message>
++ <message name="IDS_BONGO">
++ Bongo!
++ </message>
++ </messages>''', predetermined_ids_file=ids_file)
++ os.remove(ids_file)
++
++
++class IfNodeUnittest(unittest.TestCase):
++ def testIffyness(self):
++ grd = grd_reader.Parse(StringIO('''
++ <grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
++ <release seq="3">
++ <messages>
++ <if expr="'bingo' in defs">
++ <message name="IDS_BINGO">
++ Bingo!
++ </message>
++ </if>
++ <if expr="'hello' in defs">
++ <message name="IDS_HELLO">
++ Hello!
++ </message>
++ </if>
++ <if expr="lang == 'fr' or 'FORCE_FRENCH' in defs">
++ <message name="IDS_HELLO" internal_comment="French version">
++ Good morning
++ </message>
++ </if>
++ <if expr="is_win">
++ <message name="IDS_ISWIN">is_win</message>
++ </if>
++ </messages>
++ </release>
++ </grit>'''), dir='.')
++
++ messages_node = grd.children[0].children[0]
++ bingo_message = messages_node.children[0].children[0]
++ hello_message = messages_node.children[1].children[0]
++ french_message = messages_node.children[2].children[0]
++ is_win_message = messages_node.children[3].children[0]
++
++ self.assertTrue(bingo_message.name == 'message')
++ self.assertTrue(hello_message.name == 'message')
++ self.assertTrue(french_message.name == 'message')
++
++ grd.SetOutputLanguage('fr')
++ grd.SetDefines({'hello': '1'})
++ active = set(grd.ActiveDescendants())
++ self.failUnless(bingo_message not in active)
++ self.failUnless(hello_message in active)
++ self.failUnless(french_message in active)
++
++ grd.SetOutputLanguage('en')
++ grd.SetDefines({'bingo': 1})
++ active = set(grd.ActiveDescendants())
++ self.failUnless(bingo_message in active)
++ self.failUnless(hello_message not in active)
++ self.failUnless(french_message not in active)
++
++ grd.SetOutputLanguage('en')
++ grd.SetDefines({'FORCE_FRENCH': '1', 'bingo': '1'})
++ active = set(grd.ActiveDescendants())
++ self.failUnless(bingo_message in active)
++ self.failUnless(hello_message not in active)
++ self.failUnless(french_message in active)
++
++ grd.SetOutputLanguage('en')
++ grd.SetDefines({})
++ self.failUnless(grd.target_platform == sys.platform)
++ grd.SetTargetPlatform('darwin')
++ active = set(grd.ActiveDescendants())
++ self.failUnless(is_win_message not in active)
++ grd.SetTargetPlatform('win32')
++ active = set(grd.ActiveDescendants())
++ self.failUnless(is_win_message in active)
++
++ def testElsiness(self):
++ grd = util.ParseGrdForUnittest('''
++ <messages>
++ <if expr="True">
++ <then> <message name="IDS_YES1"></message> </then>
++ <else> <message name="IDS_NO1"></message> </else>
++ </if>
++ <if expr="True">
++ <then> <message name="IDS_YES2"></message> </then>
++ <else> </else>
++ </if>
++ <if expr="True">
++ <then> </then>
++ <else> <message name="IDS_NO2"></message> </else>
++ </if>
++ <if expr="True">
++ <then> </then>
++ <else> </else>
++ </if>
++ <if expr="False">
++ <then> <message name="IDS_NO3"></message> </then>
++ <else> <message name="IDS_YES3"></message> </else>
++ </if>
++ <if expr="False">
++ <then> <message name="IDS_NO4"></message> </then>
++ <else> </else>
++ </if>
++ <if expr="False">
++ <then> </then>
++ <else> <message name="IDS_YES4"></message> </else>
++ </if>
++ <if expr="False">
++ <then> </then>
++ <else> </else>
++ </if>
++ </messages>''')
++ included = [msg.attrs['name'] for msg in grd.ActiveDescendants()
++ if msg.name == 'message']
++ self.assertEqual(['IDS_YES1', 'IDS_YES2', 'IDS_YES3', 'IDS_YES4'], included)
++
++ def testIffynessWithOutputNodes(self):
++ grd = grd_reader.Parse(StringIO('''
++ <grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
++ <outputs>
++ <output filename="uncond1.rc" type="rc_data" />
++ <if expr="lang == 'fr' or 'hello' in defs">
++ <output filename="only_fr.adm" type="adm" />
++ <output filename="only_fr.plist" type="plist" />
++ </if>
++ <if expr="lang == 'ru'">
++ <output filename="doc.html" type="document" />
++ </if>
++ <output filename="uncond2.adm" type="adm" />
++ <output filename="iftest.h" type="rc_header">
++ <emit emit_type='prepend'></emit>
++ </output>
++ </outputs>
++ </grit>'''), dir='.')
++
++ outputs_node = grd.children[0]
++ uncond1_output = outputs_node.children[0]
++ only_fr_adm_output = outputs_node.children[1].children[0]
++ only_fr_plist_output = outputs_node.children[1].children[1]
++ doc_output = outputs_node.children[2].children[0]
++ uncond2_output = outputs_node.children[0]
++ self.assertTrue(uncond1_output.name == 'output')
++ self.assertTrue(only_fr_adm_output.name == 'output')
++ self.assertTrue(only_fr_plist_output.name == 'output')
++ self.assertTrue(doc_output.name == 'output')
++ self.assertTrue(uncond2_output.name == 'output')
++
++ grd.SetOutputLanguage('ru')
++ grd.SetDefines({'hello': '1'})
++ outputs = [output.GetFilename() for output in grd.GetOutputFiles()]
++ self.assertEquals(
++ outputs,
++ ['uncond1.rc', 'only_fr.adm', 'only_fr.plist', 'doc.html',
++ 'uncond2.adm', 'iftest.h'])
++
++ grd.SetOutputLanguage('ru')
++ grd.SetDefines({'bingo': '2'})
++ outputs = [output.GetFilename() for output in grd.GetOutputFiles()]
++ self.assertEquals(
++ outputs,
++ ['uncond1.rc', 'doc.html', 'uncond2.adm', 'iftest.h'])
++
++ grd.SetOutputLanguage('fr')
++ grd.SetDefines({'hello': '1'})
++ outputs = [output.GetFilename() for output in grd.GetOutputFiles()]
++ self.assertEquals(
++ outputs,
++ ['uncond1.rc', 'only_fr.adm', 'only_fr.plist', 'uncond2.adm',
++ 'iftest.h'])
++
++ grd.SetOutputLanguage('en')
++ grd.SetDefines({'bingo': '1'})
++ outputs = [output.GetFilename() for output in grd.GetOutputFiles()]
++ self.assertEquals(outputs, ['uncond1.rc', 'uncond2.adm', 'iftest.h'])
++
++ grd.SetOutputLanguage('fr')
++ grd.SetDefines({'bingo': '1'})
++ outputs = [output.GetFilename() for output in grd.GetOutputFiles()]
++ self.assertNotEquals(outputs, ['uncond1.rc', 'uncond2.adm', 'iftest.h'])
++
++ def testChildrenAccepted(self):
++ grd_reader.Parse(StringIO(r'''<?xml version="1.0"?>
++ <grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
++ <release seq="3">
++ <includes>
++ <if expr="'bingo' in defs">
++ <include type="gif" name="ID_LOGO2" file="images/logo2.gif" />
++ </if>
++ <if expr="'bingo' in defs">
++ <if expr="'hello' in defs">
++ <include type="gif" name="ID_LOGO2" file="images/logo2.gif" />
++ </if>
++ </if>
++ </includes>
++ <structures>
++ <if expr="'bingo' in defs">
++ <structure type="dialog" name="IDD_ABOUTBOX" file="grit\test\data\klonk.rc" encoding="utf-16" />
++ </if>
++ <if expr="'bingo' in defs">
++ <if expr="'hello' in defs">
++ <structure type="dialog" name="IDD_ABOUTBOX" file="grit\test\data\klonk.rc" encoding="utf-16" />
++ </if>
++ </if>
++ </structures>
++ <messages>
++ <if expr="'bingo' in defs">
++ <message name="IDS_BINGO">Bingo!</message>
++ </if>
++ <if expr="'bingo' in defs">
++ <if expr="'hello' in defs">
++ <message name="IDS_BINGO">Bingo!</message>
++ </if>
++ </if>
++ </messages>
++ </release>
++ <translations>
++ <if expr="'bingo' in defs">
++ <file lang="nl" path="nl_translations.xtb" />
++ </if>
++ <if expr="'bingo' in defs">
++ <if expr="'hello' in defs">
++ <file lang="nl" path="nl_translations.xtb" />
++ </if>
++ </if>
++ </translations>
++ </grit>'''), dir='.')
++
++ def testIfBadChildrenNesting(self):
++ # includes
++ xml = StringIO(r'''<?xml version="1.0"?>
++ <grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
++ <release seq="3">
++ <includes>
++ <if expr="'bingo' in defs">
++ <structure type="dialog" name="IDD_ABOUTBOX" file="grit\test\data\klonk.rc" encoding="utf-16" />
++ </if>
++ </includes>
++ </release>
++ </grit>''')
++ self.assertRaises(grit.exception.UnexpectedChild, grd_reader.Parse, xml)
++ # messages
++ xml = StringIO(r'''<?xml version="1.0"?>
++ <grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
++ <release seq="3">
++ <messages>
++ <if expr="'bingo' in defs">
++ <structure type="dialog" name="IDD_ABOUTBOX" file="grit\test\data\klonk.rc" encoding="utf-16" />
++ </if>
++ </messages>
++ </release>
++ </grit>''')
++ self.assertRaises(grit.exception.UnexpectedChild, grd_reader.Parse, xml)
++ # structures
++ xml = StringIO('''<?xml version="1.0"?>
++ <grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
++ <release seq="3">
++ <structures>
++ <if expr="'bingo' in defs">
++ <message name="IDS_BINGO">Bingo!</message>
++ </if>
++ </structures>
++ </release>
++ </grit>''')
++ # translations
++ self.assertRaises(grit.exception.UnexpectedChild, grd_reader.Parse, xml)
++ xml = StringIO('''<?xml version="1.0"?>
++ <grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
++ <translations>
++ <if expr="'bingo' in defs">
++ <message name="IDS_BINGO">Bingo!</message>
++ </if>
++ </translations>
++ </grit>''')
++ self.assertRaises(grit.exception.UnexpectedChild, grd_reader.Parse, xml)
++ # same with nesting
++ xml = StringIO(r'''<?xml version="1.0"?>
++ <grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
++ <release seq="3">
++ <includes>
++ <if expr="'bingo' in defs">
++ <if expr="'hello' in defs">
++ <structure type="dialog" name="IDD_ABOUTBOX" file="grit\test\data\klonk.rc" encoding="utf-16" />
++ </if>
++ </if>
++ </includes>
++ </release>
++ </grit>''')
++ self.assertRaises(grit.exception.UnexpectedChild, grd_reader.Parse, xml)
++ xml = StringIO(r'''<?xml version="1.0"?>
++ <grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
++ <release seq="3">
++ <messages>
++ <if expr="'bingo' in defs">
++ <if expr="'hello' in defs">
++ <structure type="dialog" name="IDD_ABOUTBOX" file="grit\test\data\klonk.rc" encoding="utf-16" />
++ </if>
++ </if>
++ </messages>
++ </release>
++ </grit>''')
++ self.assertRaises(grit.exception.UnexpectedChild, grd_reader.Parse, xml)
++ xml = StringIO('''<?xml version="1.0"?>
++ <grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
++ <release seq="3">
++ <structures>
++ <if expr="'bingo' in defs">
++ <if expr="'hello' in defs">
++ <message name="IDS_BINGO">Bingo!</message>
++ </if>
++ </if>
++ </structures>
++ </release>
++ </grit>''')
++ self.assertRaises(grit.exception.UnexpectedChild, grd_reader.Parse, xml)
++ xml = StringIO('''<?xml version="1.0"?>
++ <grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
++ <translations>
++ <if expr="'bingo' in defs">
++ <if expr="'hello' in defs">
++ <message name="IDS_BINGO">Bingo!</message>
++ </if>
++ </if>
++ </translations>
++ </grit>''')
++ self.assertRaises(grit.exception.UnexpectedChild, grd_reader.Parse, xml)
++
++
++class ReleaseNodeUnittest(unittest.TestCase):
++ def testPseudoControl(self):
++ grd = grd_reader.Parse(StringIO('''<?xml version="1.0" encoding="UTF-8"?>
++ <grit latest_public_release="1" source_lang_id="en-US" current_release="2" base_dir=".">
++ <release seq="1" allow_pseudo="false">
++ <messages>
++ <message name="IDS_HELLO">
++ Hello
++ </message>
++ </messages>
++ <structures>
++ <structure type="dialog" name="IDD_ABOUTBOX" encoding="utf-16" file="klonk.rc" />
++ </structures>
++ </release>
++ <release seq="2">
++ <messages>
++ <message name="IDS_BINGO">
++ Bingo
++ </message>
++ </messages>
++ <structures>
++ <structure type="menu" name="IDC_KLONKMENU" encoding="utf-16" file="klonk.rc" />
++ </structures>
++ </release>
++ </grit>'''), util.PathFromRoot('grit/testdata'))
++ grd.SetOutputLanguage('en')
++ grd.RunGatherers()
++
++ hello = grd.GetNodeById('IDS_HELLO')
++ aboutbox = grd.GetNodeById('IDD_ABOUTBOX')
++ bingo = grd.GetNodeById('IDS_BINGO')
++ menu = grd.GetNodeById('IDC_KLONKMENU')
++
++ for node in [hello, aboutbox]:
++ self.failUnless(not node.PseudoIsAllowed())
++
++ for node in [bingo, menu]:
++ self.failUnless(node.PseudoIsAllowed())
++
++ # TODO(benrg): There was a test here that formatting hello and aboutbox with
++ # a pseudo language should fail, but they do not fail and the test was
++ # broken and failed to catch it. Fix this.
++
++ # Should not raise an exception since pseudo is allowed
++ rc.FormatMessage(bingo, 'xyz-pseudo')
++ rc.FormatStructure(menu, 'xyz-pseudo', '.')
++
++
++if __name__ == '__main__':
++ unittest.main()
+diff --git a/tools/grit/grit/node/mock_brotli.py b/tools/grit/grit/node/mock_brotli.py
+new file mode 100644
+index 0000000000..14237aab20
+--- /dev/null
++++ b/tools/grit/grit/node/mock_brotli.py
+@@ -0,0 +1,10 @@
++#!/usr/bin/env python
++# 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.
++
++"""Mock Brotli Executable for testing purposes."""
+
-+int main() {
-+ MissingInCPPOK one;
-+ MissingCtorsArentOKInHeader two;
-+ return 0;
++import sys
++
++sys.stdout.write('This has been mock compressed!')
+diff --git a/tools/grit/grit/node/node_io.py b/tools/grit/grit/node/node_io.py
+new file mode 100644
+index 0000000000..ccbc2c0647
+--- /dev/null
++++ b/tools/grit/grit/node/node_io.py
+@@ -0,0 +1,117 @@
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++'''The <output> and <file> elements.
++'''
++
++from __future__ import print_function
++
++import os
++
++from grit import xtb_reader
++from grit.node import base
++
++
++class FileNode(base.Node):
++ '''A <file> element.'''
++
++ def __init__(self):
++ super(FileNode, self).__init__()
++ self.re = None
++ self.should_load_ = True
++
++ def IsTranslation(self):
++ return True
++
++ def GetLang(self):
++ return self.attrs['lang']
++
++ def DisableLoading(self):
++ self.should_load_ = False
++
++ def MandatoryAttributes(self):
++ return ['path', 'lang']
++
++ def RunPostSubstitutionGatherer(self, debug=False):
++ if not self.should_load_:
++ return
++
++ root = self.GetRoot()
++ defs = getattr(root, 'defines', {})
++ target_platform = getattr(root, 'target_platform', '')
++
++ xtb_file = open(self.ToRealPath(self.GetInputPath()), 'rb')
++ try:
++ lang = xtb_reader.Parse(xtb_file,
++ self.UberClique().GenerateXtbParserCallback(
++ self.attrs['lang'], debug=debug),
++ defs=defs,
++ target_platform=target_platform)
++ except:
++ print("Exception during parsing of %s" % self.GetInputPath())
++ raise
++ # Translation console uses non-standard language codes 'iw' and 'no' for
++ # Hebrew and Norwegian Bokmal instead of 'he' and 'nb' used in Chrome.
++ # Note that some Chrome's .grd still use 'no' instead of 'nb', but 'nb' is
++ # always used for generated .pak files.
++ ALTERNATIVE_LANG_CODE_MAP = { 'he': 'iw', 'nb': 'no' }
++ assert (lang == self.attrs['lang'] or
++ lang == ALTERNATIVE_LANG_CODE_MAP[self.attrs['lang']]), (
++ 'The XTB file you reference must contain messages in the language '
++ 'specified\nby the \'lang\' attribute.')
++
++ def GetInputPath(self):
++ return os.path.expandvars(self.attrs['path'])
++
++
++class OutputNode(base.Node):
++ '''An <output> element.'''
++
++ def MandatoryAttributes(self):
++ return ['filename', 'type']
++
++ def DefaultAttributes(self):
++ return {
++ 'lang' : '', # empty lang indicates all languages
++ 'language_section' : 'neutral', # defines a language neutral section
++ 'context' : '',
++ 'fallback_to_default_layout' : 'true',
++ }
++
++ def GetType(self):
++ return self.attrs['type']
++
++ def GetLanguage(self):
++ '''Returns the language ID, default 'en'.'''
++ return self.attrs['lang']
++
++ def GetContext(self):
++ return self.attrs['context']
++
++ def GetFilename(self):
++ return self.attrs['filename']
++
++ def GetOutputFilename(self):
++ path = None
++ if hasattr(self, 'output_filename'):
++ path = self.output_filename
++ else:
++ path = self.attrs['filename']
++ return os.path.expandvars(path)
++
++ def GetFallbackToDefaultLayout(self):
++ return self.attrs['fallback_to_default_layout'].lower() == 'true'
++
++ def _IsValidChild(self, child):
++ return isinstance(child, EmitNode)
++
++class EmitNode(base.ContentNode):
++ ''' An <emit> element.'''
++
++ def DefaultAttributes(self):
++ return { 'emit_type' : 'prepend'}
++
++ def GetEmitType(self):
++ '''Returns the emit_type for this node. Default is 'append'.'''
++ return self.attrs['emit_type']
+diff --git a/tools/grit/grit/node/node_io_unittest.py b/tools/grit/grit/node/node_io_unittest.py
+new file mode 100644
+index 0000000000..1f45e51af8
+--- /dev/null
++++ b/tools/grit/grit/node/node_io_unittest.py
+@@ -0,0 +1,182 @@
++#!/usr/bin/env python
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++'''Unit tests for node_io.FileNode'''
++
++from __future__ import print_function
++
++import os
++import sys
++import unittest
++
++if __name__ == '__main__':
++ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
++
++from six import StringIO
++
++from grit.node import misc
++from grit.node import node_io
++from grit.node import empty
++from grit import grd_reader
++from grit import util
++
++
++def _GetAllCliques(root_node):
++ """Return all cliques in the |root_node| tree."""
++ ret = []
++ for node in root_node:
++ ret.extend(node.GetCliques())
++ return ret
++
++
++class FileNodeUnittest(unittest.TestCase):
++ def testGetPath(self):
++ root = misc.GritNode()
++ root.StartParsing(u'grit', None)
++ root.HandleAttribute(u'latest_public_release', u'0')
++ root.HandleAttribute(u'current_release', u'1')
++ root.HandleAttribute(u'base_dir', r'..\resource')
++ translations = empty.TranslationsNode()
++ translations.StartParsing(u'translations', root)
++ root.AddChild(translations)
++ file_node = node_io.FileNode()
++ file_node.StartParsing(u'file', translations)
++ file_node.HandleAttribute(u'path', r'flugel\kugel.pdf')
++ translations.AddChild(file_node)
++ root.EndParsing()
++
++ self.failUnless(root.ToRealPath(file_node.GetInputPath()) ==
++ util.normpath(
++ os.path.join(r'../resource', r'flugel/kugel.pdf')))
++
++ def VerifyCliquesContainEnglishAndFrenchAndNothingElse(self, cliques):
++ self.assertEqual(2, len(cliques))
++ for clique in cliques:
++ self.assertEqual({'en', 'fr'}, set(clique.clique.keys()))
++
++ def testLoadTranslations(self):
++ xml = '''<?xml version="1.0" encoding="UTF-8"?>
++ <grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
++ <translations>
++ <file path="generated_resources_fr.xtb" lang="fr" />
++ </translations>
++ <release seq="3">
++ <messages>
++ <message name="ID_HELLO">Hello!</message>
++ <message name="ID_HELLO_USER">Hello <ph name="USERNAME">%s<ex>Joi</ex></ph></message>
++ </messages>
++ </release>
++ </grit>'''
++ grd = grd_reader.Parse(StringIO(xml),
++ util.PathFromRoot('grit/testdata'))
++ grd.SetOutputLanguage('en')
++ grd.RunGatherers()
++ self.VerifyCliquesContainEnglishAndFrenchAndNothingElse(_GetAllCliques(grd))
++
++ def testIffyness(self):
++ grd = grd_reader.Parse(StringIO('''<?xml version="1.0" encoding="UTF-8"?>
++ <grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
++ <translations>
++ <if expr="lang == 'fr'">
++ <file path="generated_resources_fr.xtb" lang="fr" />
++ </if>
++ </translations>
++ <release seq="3">
++ <messages>
++ <message name="ID_HELLO">Hello!</message>
++ <message name="ID_HELLO_USER">Hello <ph name="USERNAME">%s<ex>Joi</ex></ph></message>
++ </messages>
++ </release>
++ </grit>'''), util.PathFromRoot('grit/testdata'))
++ grd.SetOutputLanguage('en')
++ grd.RunGatherers()
++ cliques = _GetAllCliques(grd)
++ self.assertEqual(2, len(cliques))
++ for clique in cliques:
++ self.assertEqual({'en'}, set(clique.clique.keys()))
++
++ grd.SetOutputLanguage('fr')
++ grd.RunGatherers()
++ self.VerifyCliquesContainEnglishAndFrenchAndNothingElse(_GetAllCliques(grd))
++
++ def testConditionalLoadTranslations(self):
++ xml = '''<?xml version="1.0" encoding="UTF-8"?>
++ <grit latest_public_release="2" source_lang_id="en-US" current_release="3"
++ base_dir=".">
++ <translations>
++ <if expr="True">
++ <file path="generated_resources_fr.xtb" lang="fr" />
++ </if>
++ <if expr="False">
++ <file path="no_such_file.xtb" lang="de" />
++ </if>
++ </translations>
++ <release seq="3">
++ <messages>
++ <message name="ID_HELLO">Hello!</message>
++ <message name="ID_HELLO_USER">Hello <ph name="USERNAME">%s<ex>
++ Joi</ex></ph></message>
++ </messages>
++ </release>
++ </grit>'''
++ grd = grd_reader.Parse(StringIO(xml),
++ util.PathFromRoot('grit/testdata'))
++ grd.SetOutputLanguage('en')
++ grd.RunGatherers()
++ self.VerifyCliquesContainEnglishAndFrenchAndNothingElse(_GetAllCliques(grd))
++
++ def testConditionalOutput(self):
++ xml = '''<?xml version="1.0" encoding="UTF-8"?>
++ <grit latest_public_release="2" source_lang_id="en-US" current_release="3"
++ base_dir=".">
++ <outputs>
++ <output filename="resource.h" type="rc_header" />
++ <output filename="en/generated_resources.rc" type="rc_all"
++ lang="en" />
++ <if expr="pp_if('NOT_TRUE')">
++ <output filename="de/generated_resources.rc" type="rc_all"
++ lang="de" />
++ </if>
++ </outputs>
++ <release seq="3">
++ <messages>
++ <message name="ID_HELLO">Hello!</message>
++ </messages>
++ </release>
++ </grit>'''
++ grd = grd_reader.Parse(StringIO(xml),
++ util.PathFromRoot('grit/test/data'),
++ defines={})
++ grd.SetOutputLanguage('en')
++ grd.RunGatherers()
++ outputs = grd.GetChildrenOfType(node_io.OutputNode)
++ active = set(grd.ActiveDescendants())
++ self.failUnless(outputs[0] in active)
++ self.failUnless(outputs[0].GetType() == 'rc_header')
++ self.failUnless(outputs[1] in active)
++ self.failUnless(outputs[1].GetType() == 'rc_all')
++ self.failUnless(outputs[2] not in active)
++ self.failUnless(outputs[2].GetType() == 'rc_all')
++
++ # Verify that 'iw' and 'no' language codes in xtb files are mapped to 'he' and
++ # 'nb'.
++ def testLangCodeMapping(self):
++ grd = grd_reader.Parse(StringIO('''<?xml version="1.0" encoding="UTF-8"?>
++ <grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
++ <translations>
++ <file path="generated_resources_no.xtb" lang="nb" />
++ <file path="generated_resources_iw.xtb" lang="he" />
++ </translations>
++ <release seq="3">
++ <messages></messages>
++ </release>
++ </grit>'''), util.PathFromRoot('grit/testdata'))
++ grd.SetOutputLanguage('en')
++ grd.RunGatherers()
++ self.assertEqual([], _GetAllCliques(grd))
++
++
++if __name__ == '__main__':
++ unittest.main()
+diff --git a/tools/grit/grit/node/structure.py b/tools/grit/grit/node/structure.py
+new file mode 100644
+index 0000000000..ec170faebb
+--- /dev/null
++++ b/tools/grit/grit/node/structure.py
+@@ -0,0 +1,375 @@
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++'''The <structure> element.
++'''
++
++from __future__ import print_function
++
++import os
++import platform
++import re
++
++from grit import exception
++from grit import util
++from grit.node import base
++from grit.node import variant
++
++import grit.gather.admin_template
++import grit.gather.chrome_html
++import grit.gather.chrome_scaled_image
++import grit.gather.policy_json
++import grit.gather.rc
++import grit.gather.tr_html
++import grit.gather.txt
++
++import grit.format.rc
++
++# Type of the gatherer to use for each type attribute
++_GATHERERS = {
++ 'accelerators' : grit.gather.rc.Accelerators,
++ 'admin_template' : grit.gather.admin_template.AdmGatherer,
++ 'chrome_html' : grit.gather.chrome_html.ChromeHtml,
++ 'chrome_scaled_image' : grit.gather.chrome_scaled_image.ChromeScaledImage,
++ 'dialog' : grit.gather.rc.Dialog,
++ 'menu' : grit.gather.rc.Menu,
++ 'rcdata' : grit.gather.rc.RCData,
++ 'tr_html' : grit.gather.tr_html.TrHtml,
++ 'txt' : grit.gather.txt.TxtFile,
++ 'version' : grit.gather.rc.Version,
++ 'policy_template_metafile' : grit.gather.policy_json.PolicyJson,
+}
-diff --git a/tools/clang/plugins/tests/missing_ctor.h b/tools/clang/plugins/tests/missing_ctor.h
++
++
++# TODO(joi) Print a warning if the 'variant_of_revision' attribute indicates
++# that a skeleton variant is older than the original file.
++
++
++class StructureNode(base.Node):
++ '''A <structure> element.'''
++
++ # Regular expression for a local variable definition. Each definition
++ # is of the form NAME=VALUE, where NAME cannot contain '=' or ',' and
++ # VALUE must escape all commas: ',' -> ',,'. Each variable definition
++ # should be separated by a comma with no extra whitespace.
++ # Example: THING1=foo,THING2=bar
++ variable_pattern = re.compile(r'([^,=\s]+)=((?:,,|[^,])*)')
++
++ def __init__(self):
++ super(StructureNode, self).__init__()
++
++ # Keep track of the last filename we flattened to, so we can
++ # avoid doing it more than once.
++ self._last_flat_filename = None
++
++ # See _Substitute; this substituter is used for local variables and
++ # the root substituter is used for global variables.
++ self.substituter = None
++
++ def _IsValidChild(self, child):
++ return isinstance(child, variant.SkeletonNode)
++
++ def _ParseVariables(self, variables):
++ '''Parse a variable string into a dictionary.'''
++ matches = StructureNode.variable_pattern.findall(variables)
++ return dict((name, value.replace(',,', ',')) for name, value in matches)
++
++ def EndParsing(self):
++ super(StructureNode, self).EndParsing()
++
++ # Now that we have attributes and children, instantiate the gatherers.
++ gathertype = _GATHERERS[self.attrs['type']]
++
++ self.gatherer = gathertype(self.attrs['file'],
++ self.attrs['name'],
++ self.attrs['encoding'])
++ self.gatherer.SetGrdNode(self)
++ self.gatherer.SetUberClique(self.UberClique())
++ if hasattr(self.GetRoot(), 'defines'):
++ self.gatherer.SetDefines(self.GetRoot().defines)
++ self.gatherer.SetAttributes(self.attrs)
++ if self.ExpandVariables():
++ self.gatherer.SetFilenameExpansionFunction(self._Substitute)
++
++ # Parse local variables and instantiate the substituter.
++ if self.attrs['variables']:
++ variables = self.attrs['variables']
++ self.substituter = util.Substituter()
++ self.substituter.AddSubstitutions(self._ParseVariables(variables))
++
++ self.skeletons = {} # Maps expressions to skeleton gatherers
++ for child in self.children:
++ assert isinstance(child, variant.SkeletonNode)
++ skel = gathertype(child.attrs['file'],
++ self.attrs['name'],
++ child.GetEncodingToUse(),
++ is_skeleton=True)
++ skel.SetGrdNode(self) # TODO(benrg): Or child? Only used for ToRealPath
++ skel.SetUberClique(self.UberClique())
++ if hasattr(self.GetRoot(), 'defines'):
++ skel.SetDefines(self.GetRoot().defines)
++ if self.ExpandVariables():
++ skel.SetFilenameExpansionFunction(self._Substitute)
++ self.skeletons[child.attrs['expr']] = skel
++
++ def MandatoryAttributes(self):
++ return ['type', 'name', 'file']
++
++ def DefaultAttributes(self):
++ return {
++ 'encoding': 'cp1252',
++ 'exclude_from_rc': 'false',
++ 'line_end': 'unix',
++ 'output_encoding': 'utf-8',
++ 'generateid': 'true',
++ 'expand_variables': 'false',
++ 'output_filename': '',
++ 'fold_whitespace': 'false',
++ # Run an arbitrary command after translation is complete
++ # so that it doesn't interfere with what's in translation
++ # console.
++ 'run_command': '',
++ # Leave empty to run on all platforms, comma-separated
++ # for one or more specific platforms. Values must match
++ # output of platform.system().
++ 'run_command_on_platforms': '',
++ 'allowexternalscript': 'false',
++ # preprocess takes the same code path as flattenhtml, but it
++ # disables any processing/inlining outside of <if> and <include>.
++ 'preprocess': 'false',
++ 'flattenhtml': 'false',
++ 'fallback_to_low_resolution': 'default',
++ 'variables': '',
++ 'compress': 'default',
++ 'use_base_dir': 'true',
++ }
++
++ def IsExcludedFromRc(self):
++ return self.attrs['exclude_from_rc'] == 'true'
++
++ def Process(self, output_dir):
++ """Writes the processed data to output_dir. In the case of a chrome_html
++ structure this will add references to other scale factors. If flattening
++ this will also write file references to be base64 encoded data URLs. The
++ name of the new file is returned."""
++ filename = self.ToRealPath(self.GetInputPath())
++ flat_filename = os.path.join(output_dir,
++ self.attrs['name'] + '_' + os.path.basename(filename))
++
++ if self._last_flat_filename == flat_filename:
++ return
++
++ with open(flat_filename, 'wb') as outfile:
++ if self.ExpandVariables():
++ text = self.gatherer.GetText()
++ file_contents = self._Substitute(text)
++ else:
++ file_contents = self.gatherer.GetData('', 'utf-8')
++ outfile.write(file_contents.encode('utf-8'))
++
++ self._last_flat_filename = flat_filename
++ return os.path.basename(flat_filename)
++
++ def GetLineEnd(self):
++ '''Returns the end-of-line character or characters for files output because
++ of this node ('\r\n', '\n', or '\r' depending on the 'line_end' attribute).
++ '''
++ if self.attrs['line_end'] == 'unix':
++ return '\n'
++ elif self.attrs['line_end'] == 'windows':
++ return '\r\n'
++ elif self.attrs['line_end'] == 'mac':
++ return '\r'
++ else:
++ raise exception.UnexpectedAttribute(
++ "Attribute 'line_end' must be one of 'unix' (default), 'windows' or "
++ "'mac'")
++
++ def GetCliques(self):
++ return self.gatherer.GetCliques()
++
++ def GetDataPackValue(self, lang, encoding):
++ """Returns a bytes representation for a data_pack entry."""
++ if self.ExpandVariables():
++ text = self.gatherer.GetText()
++ data = util.Encode(self._Substitute(text), encoding)
++ else:
++ data = self.gatherer.GetData(lang, encoding)
++ if encoding != util.BINARY:
++ data = data.encode(encoding)
++ return self.CompressDataIfNeeded(data)
++
++ def GetHtmlResourceFilenames(self):
++ """Returns a set of all filenames inlined by this node."""
++ return self.gatherer.GetHtmlResourceFilenames()
++
++ def GetInputPath(self):
++ path = self.gatherer.GetInputPath()
++ if path is None:
++ return path
++
++ # Do not mess with absolute paths, that would make them invalid.
++ if os.path.isabs(os.path.expandvars(path)):
++ return path
++
++ # We have no control over code that calls ToRealPath later, so convert
++ # the path to be relative against our basedir.
++ if self.attrs.get('use_base_dir', 'true') != 'true':
++ # Normalize the directory path to use the appropriate OS separator.
++ # GetBaseDir() may return paths\like\this or paths/like/this, since it is
++ # read from the base_dir attribute in the grd file.
++ norm_base_dir = util.normpath(self.GetRoot().GetBaseDir())
++ return os.path.relpath(path, norm_base_dir)
++
++ return path
++
++ def GetTextualIds(self):
++ if not hasattr(self, 'gatherer'):
++ # This case is needed because this method is called by
++ # GritNode.ValidateUniqueIds before RunGatherers has been called.
++ # TODO(benrg): Fix this?
++ return [self.attrs['name']]
++ return self.gatherer.GetTextualIds()
++
++ def RunPreSubstitutionGatherer(self, debug=False):
++ if debug:
++ print('Running gatherer %s for file %s' %
++ (type(self.gatherer), self.GetInputPath()))
++
++ # Note: Parse() is idempotent, therefore this method is also.
++ self.gatherer.Parse()
++ for skel in self.skeletons.values():
++ skel.Parse()
++
++ def GetSkeletonGatherer(self):
++ '''Returns the gatherer for the alternate skeleton that should be used,
++ based on the expressions for selecting skeletons, or None if the skeleton
++ from the English version of the structure should be used.
++ '''
++ for expr in self.skeletons:
++ if self.EvaluateCondition(expr):
++ return self.skeletons[expr]
++ return None
++
++ def HasFileForLanguage(self):
++ return self.attrs['type'] in ['tr_html', 'admin_template', 'txt',
++ 'chrome_scaled_image',
++ 'chrome_html']
++
++ def ExpandVariables(self):
++ '''Variable expansion on structures is controlled by an XML attribute.
++
++ However, old files assume that expansion is always on for Rc files.
++
++ Returns:
++ A boolean.
++ '''
++ attrs = self.GetRoot().attrs
++ if 'grit_version' in attrs and attrs['grit_version'] > 1:
++ return self.attrs['expand_variables'] == 'true'
++ else:
++ return (self.attrs['expand_variables'] == 'true' or
++ self.attrs['file'].lower().endswith('.rc'))
++
++ def _Substitute(self, text):
++ '''Perform local and global variable substitution.'''
++ if self.substituter:
++ text = self.substituter.Substitute(text)
++ return self.GetRoot().GetSubstituter().Substitute(text)
++
++ def RunCommandOnCurrentPlatform(self):
++ if self.attrs['run_command_on_platforms'] == '':
++ return True
++ else:
++ target_platforms = self.attrs['run_command_on_platforms'].split(',')
++ return platform.system() in target_platforms
++
++ def FileForLanguage(self, lang, output_dir, create_file=True,
++ return_if_not_generated=True):
++ '''Returns the filename of the file associated with this structure,
++ for the specified language.
++
++ Args:
++ lang: 'fr'
++ output_dir: 'c:\temp'
++ create_file: True
++ '''
++ assert self.HasFileForLanguage()
++ # If the source language is requested, and no extra changes are requested,
++ # use the existing file.
++ if ((not lang or lang == self.GetRoot().GetSourceLanguage()) and
++ self.attrs['expand_variables'] != 'true' and
++ (not self.attrs['run_command'] or
++ not self.RunCommandOnCurrentPlatform())):
++ if return_if_not_generated:
++ input_path = self.GetInputPath()
++ if input_path is None:
++ return None
++ return self.ToRealPath(input_path)
++ else:
++ return None
++
++ if self.attrs['output_filename'] != '':
++ filename = self.attrs['output_filename']
++ else:
++ filename = os.path.basename(self.attrs['file'])
++ assert len(filename)
++ filename = '%s_%s' % (lang, filename)
++ filename = os.path.join(output_dir, filename)
++
++ # Only create the output if it was requested by the call.
++ if create_file:
++ text = self.gatherer.Translate(
++ lang,
++ pseudo_if_not_available=self.PseudoIsAllowed(),
++ fallback_to_english=self.ShouldFallbackToEnglish(),
++ skeleton_gatherer=self.GetSkeletonGatherer())
++
++ file_contents = util.FixLineEnd(text, self.GetLineEnd())
++ if self.ExpandVariables():
++ # Note that we reapply substitution a second time here.
++ # This is because a) we need to look inside placeholders
++ # b) the substitution values are language-dependent
++ file_contents = self._Substitute(file_contents)
++
++ with open(filename, 'wb') as file_object:
++ output_stream = util.WrapOutputStream(file_object,
++ self.attrs['output_encoding'])
++ output_stream.write(file_contents)
++
++ if self.attrs['run_command'] and self.RunCommandOnCurrentPlatform():
++ # Run arbitrary commands after translation is complete so that it
++ # doesn't interfere with what's in translation console.
++ command = self.attrs['run_command'] % {'filename': filename}
++ result = os.system(command)
++ assert result == 0, '"%s" failed.' % command
++
++ return filename
++
++ def IsResourceMapSource(self):
++ return True
++
++ @staticmethod
++ def Construct(parent, name, type, file, encoding='cp1252'):
++ '''Creates a new node which is a child of 'parent', with attributes set
++ by parameters of the same name.
++ '''
++ node = StructureNode()
++ node.StartParsing('structure', parent)
++ node.HandleAttribute('name', name)
++ node.HandleAttribute('type', type)
++ node.HandleAttribute('file', file)
++ node.HandleAttribute('encoding', encoding)
++ node.EndParsing()
++ return node
++
++ def SubstituteMessages(self, substituter):
++ '''Propagates substitution to gatherer.
++
++ Args:
++ substituter: a grit.util.Substituter object.
++ '''
++ assert hasattr(self, 'gatherer')
++ if self.ExpandVariables():
++ self.gatherer.SubstituteMessages(substituter)
+diff --git a/tools/grit/grit/node/structure_unittest.py b/tools/grit/grit/node/structure_unittest.py
new file mode 100644
-index 0000000000..1050457a1a
+index 0000000000..0e66dce37a
--- /dev/null
-+++ b/tools/clang/plugins/tests/missing_ctor.h
-@@ -0,0 +1,19 @@
-+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
-+// Use of this source code is governed by a BSD-style license that can be
-+// found in the LICENSE file.
++++ b/tools/grit/grit/node/structure_unittest.py
+@@ -0,0 +1,178 @@
++#!/usr/bin/env python
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++'''Unit tests for <structure> nodes.
++'''
+
-+#ifndef MISSING_CTOR_H_
-+#define MISSING_CTOR_H_
++from __future__ import print_function
+
-+#include <string>
-+#include <vector>
++import os
++import os.path
++import sys
++import zlib
++if __name__ == '__main__':
++ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
++
++import platform
++import tempfile
++import unittest
++import struct
++
++from grit import constants
++from grit import util
++from grit.node import brotli_util
++from grit.node import structure
++from grit.format import rc
++
++
++def checkIsGzipped(filename, compress_attr):
++ test_data_root = util.PathFromRoot('grit/testdata')
++ root = util.ParseGrdForUnittest(
++ '''
++ <structures>
++ <structure name="TEST_TXT" file="%s" %s type="chrome_html"/>
++ </structures>''' % (filename, compress_attr),
++ base_dir=test_data_root)
++ node, = root.GetChildrenOfType(structure.StructureNode)
++ node.RunPreSubstitutionGatherer()
++ compressed = node.GetDataPackValue(lang='en', encoding=util.BINARY)
++
++ decompressed_data = zlib.decompress(compressed, 16 + zlib.MAX_WBITS)
++ expected = util.ReadFile(os.path.join(test_data_root, filename), util.BINARY)
++ return expected == decompressed_data
++
++
++class StructureUnittest(unittest.TestCase):
++ def testSkeleton(self):
++ grd = util.ParseGrdForUnittest('''
++ <structures>
++ <structure type="dialog" name="IDD_ABOUTBOX" file="klonk.rc" encoding="utf-16-le">
++ <skeleton expr="lang == 'fr'" variant_of_revision="1" file="klonk-alternate-skeleton.rc" />
++ </structure>
++ </structures>''', base_dir=util.PathFromRoot('grit/testdata'))
++ grd.SetOutputLanguage('fr')
++ grd.RunGatherers()
++ transl = ''.join(rc.Format(grd, 'fr', '.'))
++ self.failUnless(transl.count('040704') and transl.count('110978'))
++ self.failUnless(transl.count('2005",IDC_STATIC'))
++
++ def testRunCommandOnCurrentPlatform(self):
++ node = structure.StructureNode()
++ node.attrs = node.DefaultAttributes()
++ self.failUnless(node.RunCommandOnCurrentPlatform())
++ node.attrs['run_command_on_platforms'] = 'Nosuch'
++ self.failIf(node.RunCommandOnCurrentPlatform())
++ node.attrs['run_command_on_platforms'] = (
++ 'Nosuch,%s,Othernot' % platform.system())
++ self.failUnless(node.RunCommandOnCurrentPlatform())
++
++ def testVariables(self):
++ grd = util.ParseGrdForUnittest('''
++ <structures>
++ <structure type="chrome_html" name="hello_tmpl" file="structure_variables.html" expand_variables="true" variables="GREETING=Hello,THINGS=foo,, bar,, baz,EQUATION=2+2==4,filename=simple" flattenhtml="true"></structure>
++ </structures>''', base_dir=util.PathFromRoot('grit/testdata'))
++ grd.SetOutputLanguage('en')
++ grd.RunGatherers()
++ node, = grd.GetChildrenOfType(structure.StructureNode)
++ filename = node.Process(tempfile.gettempdir())
++ filepath = os.path.join(tempfile.gettempdir(), filename)
++ with open(filepath) as f:
++ result = f.read()
++ self.failUnlessEqual(('<h1>Hello!</h1>\n'
++ 'Some cool things are foo, bar, baz.\n'
++ 'Did you know that 2+2==4?\n'
++ '<p>\n'
++ ' Hello!\n'
++ '</p>\n'), result)
++ os.remove(filepath)
++
++ def testGetPath(self):
++ base_dir = util.PathFromRoot('grit/testdata')
++ grd = util.ParseGrdForUnittest('''
++ <structures>
++ <structure type="chrome_html" name="hello_tmpl" file="structure_variables.html" expand_variables="true" variables="GREETING=Hello,THINGS=foo,, bar,, baz,EQUATION=2+2==4,filename=simple" flattenhtml="true" use_base_dir="true"></structure>
++ </structures>''', base_dir)
++ grd.SetOutputLanguage('en')
++ grd.RunGatherers()
++ node, = grd.GetChildrenOfType(structure.StructureNode)
++ self.assertEqual(grd.ToRealPath(node.GetInputPath()),
++ os.path.abspath(os.path.join(
++ base_dir, r'structure_variables.html')))
++
++ def testGetPathNoBasedir(self):
++ base_dir = util.PathFromRoot('grit/testdata')
++ abs_path = os.path.join(base_dir, r'structure_variables.html')
++ rel_path = os.path.relpath(abs_path, os.getcwd())
++ grd = util.ParseGrdForUnittest('''
++ <structures>
++ <structure type="chrome_html" name="hello_tmpl" file="''' + rel_path + '''" expand_variables="true" variables="GREETING=Hello,THINGS=foo,, bar,, baz,EQUATION=2+2==4,filename=simple" flattenhtml="true" use_base_dir="false"></structure>
++ </structures>''', util.PathFromRoot('grit/testdata'))
++ grd.SetOutputLanguage('en')
++ grd.RunGatherers()
++ node, = grd.GetChildrenOfType(structure.StructureNode)
++ self.assertEqual(grd.ToRealPath(node.GetInputPath()),
++ os.path.abspath(os.path.join(
++ base_dir, r'structure_variables.html')))
++
++ def testCompressGzip(self):
++ self.assertTrue(checkIsGzipped('test_text.txt', 'compress="gzip"'))
++
++ def testCompressGzipByDefault(self):
++ self.assertTrue(checkIsGzipped('test_html.html', ''))
++ self.assertTrue(checkIsGzipped('test_js.js', ''))
++ self.assertTrue(checkIsGzipped('test_css.css', ''))
++ self.assertTrue(checkIsGzipped('test_svg.svg', ''))
++
++ self.assertTrue(checkIsGzipped('test_html.html', 'compress="default"'))
++ self.assertTrue(checkIsGzipped('test_js.js', 'compress="default"'))
++ self.assertTrue(checkIsGzipped('test_css.css', 'compress="default"'))
++ self.assertTrue(checkIsGzipped('test_svg.svg', 'compress="default"'))
++
++ def testCompressBrotli(self):
++ test_data_root = util.PathFromRoot('grit/testdata')
++ root = util.ParseGrdForUnittest(
++ '''
++ <structures>
++ <structure name="TEST_TXT" file="test_text.txt"
++ compress="brotli" type="chrome_html" />
++ </structures>''',
++ base_dir=test_data_root)
++ node, = root.GetChildrenOfType(structure.StructureNode)
++ node.RunPreSubstitutionGatherer()
++
++ # Using the mock brotli decompression executable.
++ brotli_util.SetBrotliCommand([sys.executable,
++ os.path.join(os.path.dirname(__file__),
++ 'mock_brotli.py')])
++ compressed = node.GetDataPackValue(lang='en', encoding=util.BINARY)
++ # Assert that the first two bytes in compressed format is BROTLI_CONST.
++ self.assertEqual(constants.BROTLI_CONST, compressed[0:2])
++
++ # Compare the actual size of the uncompressed test data with
++ # the size appended during compression.
++ actual_size = len(util.ReadFile(
++ os.path.join(test_data_root, 'test_text.txt'), util.BINARY))
++ uncompress_size = struct.unpack('<i', compressed[2:6])[0]
++ uncompress_size += struct.unpack('<h', compressed[6:8])[0] << 4*8
++ self.assertEqual(actual_size, uncompress_size)
++
++ self.assertEqual(b'This has been mock compressed!', compressed[8:])
++
++ def testNotCompressed(self):
++ test_data_root = util.PathFromRoot('grit/testdata')
++ root = util.ParseGrdForUnittest('''
++ <structures>
++ <structure name="TEST_TXT" file="test_text.txt" type="chrome_html" />
++ </structures>''', base_dir=test_data_root)
++ node, = root.GetChildrenOfType(structure.StructureNode)
++ node.RunPreSubstitutionGatherer()
++ data = node.GetDataPackValue(lang='en', encoding=util.BINARY)
++
++ self.assertEqual(util.ReadFile(
++ os.path.join(test_data_root, 'test_text.txt'), util.BINARY), data)
+
-+class MissingCtorsArentOKInHeader {
-+ public:
+
-+ private:
-+ std::vector<int> one_;
-+ std::vector<std::string> two_;
-+};
++if __name__ == '__main__':
++ unittest.main()
+diff --git a/tools/grit/grit/node/variant.py b/tools/grit/grit/node/variant.py
+new file mode 100644
+index 0000000000..9f5845f954
+--- /dev/null
++++ b/tools/grit/grit/node/variant.py
+@@ -0,0 +1,41 @@
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++'''The <skeleton> element.
++'''
++
++from __future__ import print_function
++
++from grit.node import base
++
++
++class SkeletonNode(base.Node):
++ '''A <skeleton> element.'''
++
++ # TODO(joi) Support inline skeleton variants as CDATA instead of requiring
++ # a 'file' attribute.
++
++ def MandatoryAttributes(self):
++ return ['expr', 'variant_of_revision', 'file']
++
++ def DefaultAttributes(self):
++ '''If not specified, 'encoding' will actually default to the parent node's
++ encoding.
++ '''
++ return {'encoding' : ''}
++
++ def _ContentType(self):
++ if 'file' in self.attrs:
++ return self._CONTENT_TYPE_NONE
++ else:
++ return self._CONTENT_TYPE_CDATA
++
++ def GetEncodingToUse(self):
++ if self.attrs['encoding'] == '':
++ return self.parent.attrs['encoding']
++ else:
++ return self.attrs['encoding']
++
++ def GetInputPath(self):
++ return self.attrs['file']
+diff --git a/tools/grit/grit/pseudo.py b/tools/grit/grit/pseudo.py
+new file mode 100644
+index 0000000000..b607bfc6bb
+--- /dev/null
++++ b/tools/grit/grit/pseudo.py
+@@ -0,0 +1,129 @@
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++'''Pseudotranslation support. Our pseudotranslations are based on the
++P-language, which is a simple vowel-extending language. Examples of P:
++ - "hello" becomes "hepellopo"
++ - "howdie" becomes "hopowdiepie"
++ - "because" becomes "bepecaupause" (but in our implementation we don't
++ handle the silent e at the end so it actually would return "bepecaupausepe"
++
++The P-language has the excellent quality of increasing the length of text
++by around 30-50% which is great for pseudotranslations, to stress test any
++GUI layouts etc.
++
++To make the pseudotranslations more obviously "not a translation" and to make
++them exercise any code that deals with encodings, we also transform all English
++vowels into equivalent vowels with diacriticals on them (rings, acutes,
++diaresis, and circumflex), and we write the "p" in the P-language as a Hebrew
++character Qof. It looks sort of like a latin character "p" but it is outside
++the latin-1 character set which will stress character encoding bugs.
++'''
++
++from __future__ import print_function
++
++from grit import lazy_re
++from grit import tclib
++
++
++# An RFC language code for the P pseudolanguage.
++PSEUDO_LANG = 'x-P-pseudo'
++
++# Hebrew character Qof. It looks kind of like a 'p' but is outside
++# the latin-1 character set which is good for our purposes.
++# TODO(joi) For now using P instead of Qof, because of some bugs it used. Find
++# a better solution, i.e. one that introduces a non-latin1 character into the
++# pseudotranslation.
++#_QOF = u'\u05e7'
++_QOF = u'P'
++
++# How we map each vowel.
++_VOWELS = {
++ u'a' : u'\u00e5', # a with ring
++ u'e' : u'\u00e9', # e acute
++ u'i' : u'\u00ef', # i diaresis
++ u'o' : u'\u00f4', # o circumflex
++ u'u' : u'\u00fc', # u diaresis
++ u'y' : u'\u00fd', # y acute
++ u'A' : u'\u00c5', # A with ring
++ u'E' : u'\u00c9', # E acute
++ u'I' : u'\u00cf', # I diaresis
++ u'O' : u'\u00d4', # O circumflex
++ u'U' : u'\u00dc', # U diaresis
++ u'Y' : u'\u00dd', # Y acute
++}
++_VOWELS_KEYS = set(_VOWELS.keys())
++
++# Matches vowels and P
++_PSUB_RE = lazy_re.compile("(%s)" % '|'.join(_VOWELS_KEYS | {'P'}))
++
++
++# Pseudotranslations previously created. This is important for performance
++# reasons, especially since we routinely pseudotranslate the whole project
++# several or many different times for each build.
++_existing_translations = {}
++
++
++def MapVowels(str, also_p = False):
++ '''Returns a copy of 'str' where characters that exist as keys in _VOWELS
++ have been replaced with the corresponding value. If also_p is true, this
++ function will also change capital P characters into a Hebrew character Qof.
++ '''
++ def Repl(match):
++ if match.group() == 'p':
++ if also_p:
++ return _QOF
++ else:
++ return 'p'
++ else:
++ return _VOWELS[match.group()]
++ return _PSUB_RE.sub(Repl, str)
++
++
++def PseudoString(str):
++ '''Returns a pseudotranslation of the provided string, in our enhanced
++ P-language.'''
++ if str in _existing_translations:
++ return _existing_translations[str]
++
++ outstr = u''
++ ix = 0
++ while ix < len(str):
++ if str[ix] not in _VOWELS_KEYS:
++ outstr += str[ix]
++ ix += 1
++ else:
++ # We want to treat consecutive vowels as one composite vowel. This is not
++ # always accurate e.g. in composite words but good enough.
++ consecutive_vowels = u''
++ while ix < len(str) and str[ix] in _VOWELS_KEYS:
++ consecutive_vowels += str[ix]
++ ix += 1
++ changed_vowels = MapVowels(consecutive_vowels)
++ outstr += changed_vowels
++ outstr += _QOF
++ outstr += changed_vowels
++
++ _existing_translations[str] = outstr
++ return outstr
++
++
++def PseudoMessage(message):
++ '''Returns a pseudotranslation of the provided message.
++
++ Args:
++ message: tclib.Message()
++
++ Return:
++ tclib.Translation()
++ '''
++ transl = tclib.Translation()
++
++ for part in message.GetContent():
++ if isinstance(part, tclib.Placeholder):
++ transl.AppendPlaceholder(part)
++ else:
++ transl.AppendText(PseudoString(part))
++
++ return transl
+diff --git a/tools/grit/grit/pseudo_rtl.py b/tools/grit/grit/pseudo_rtl.py
+new file mode 100644
+index 0000000000..2240b571de
+--- /dev/null
++++ b/tools/grit/grit/pseudo_rtl.py
+@@ -0,0 +1,104 @@
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++'''Pseudo RTL, (aka Fake Bidi) support. It simply wraps each word with
++Unicode RTL overrides.
++More info at https://sites.google.com/a/chromium.org/dev/Home/fake-bidi
++'''
++
++from __future__ import print_function
++
++import re
++
++from grit import lazy_re
++from grit import tclib
++
++ACCENTED_STRINGS = {
++ 'a': u"\u00e5", 'e': u"\u00e9", 'i': u"\u00ee", 'o': u"\u00f6",
++ 'u': u"\u00fb", 'A': u"\u00c5", 'E': u"\u00c9", 'I': u"\u00ce",
++ 'O': u"\u00d6", 'U': u"\u00db", 'c': u"\u00e7", 'd': u"\u00f0",
++ 'n': u"\u00f1", 'p': u"\u00fe", 'y': u"\u00fd", 'C': u"\u00c7",
++ 'D': u"\u00d0", 'N': u"\u00d1", 'P': u"\u00de", 'Y': u"\u00dd",
++ 'f': u"\u0192", 's': u"\u0161", 'S': u"\u0160", 'z': u"\u017e",
++ 'Z': u"\u017d", 'g': u"\u011d", 'G': u"\u011c", 'h': u"\u0125",
++ 'H': u"\u0124", 'j': u"\u0135", 'J': u"\u0134", 'k': u"\u0137",
++ 'K': u"\u0136", 'l': u"\u013c", 'L': u"\u013b", 't': u"\u0163",
++ 'T': u"\u0162", 'w': u"\u0175", 'W': u"\u0174",
++ '$': u"\u20ac", '?': u"\u00bf", 'R': u"\u00ae", r'!': u"\u00a1",
++}
++
++# a character set containing the keys in ACCENTED_STRINGS
++# We should not accent characters in an escape sequence such as "\n".
++# To be safe, we assume every character following a backslash is an escaped
++# character. We also need to consider the case like "\\n", which means
++# a blackslash and a character "n", we will accent the character "n".
++TO_ACCENT = lazy_re.compile(
++ r'[%s]|\\[a-z\\]' % ''.join(ACCENTED_STRINGS.keys()))
++
++# Lex text so that we don't interfere with html tokens and entities.
++# This lexing scheme will handle all well formed tags and entities, html or
++# xhtml. It will not handle comments, CDATA sections, or the unescaping tags:
++# script, style, xmp or listing. If any of those appear in messages,
++# something is wrong.
++TOKENS = [ lazy_re.compile(
++ '^%s' % pattern, # match at the beginning of input
++ re.I | re.S # html tokens are case-insensitive
++ )
++ for pattern in
++ (
++ # a run of non html special characters
++ r'[^<&]+',
++ # a tag
++ (r'</?[a-z]\w*' # beginning of tag
++ r'(?:\s+\w+(?:\s*=\s*' # attribute start
++ r'(?:[^\s"\'>]+|"[^\"]*"|\'[^\']*\'))?' # attribute value
++ r')*\s*/?>'),
++ # an entity
++ r'&(?:[a-z]\w+|#\d+|#x[\da-f]+);',
++ # an html special character not part of a special sequence
++ r'.'
++ ) ]
++
++ALPHABETIC_RUN = lazy_re.compile(r'([^\W0-9_]+)')
++
++RLO = u'\u202e'
++PDF = u'\u202c'
++
++def PseudoRTLString(text):
++ '''Returns a fake bidirectional version of the source string. This code is
++ based on accentString above, in turn copied from Frank Tang.
++ '''
++ parts = []
++ while text:
++ m = None
++ for token in TOKENS:
++ m = token.search(text)
++ if m:
++ part = m.group(0)
++ text = text[len(part):]
++ if part[0] not in ('<', '&'):
++ # not a tag or entity, so accent
++ part = ALPHABETIC_RUN.sub(lambda run: RLO + run.group() + PDF, part)
++ parts.append(part)
++ break
++ return ''.join(parts)
++
++
++def PseudoRTLMessage(message):
++ '''Returns a pseudo-RTL (aka Fake-Bidi) translation of the provided message.
++
++ Args:
++ message: tclib.Message()
++
++ Return:
++ tclib.Translation()
++ '''
++ transl = tclib.Translation()
++ for part in message.GetContent():
++ if isinstance(part, tclib.Placeholder):
++ transl.AppendPlaceholder(part)
++ else:
++ transl.AppendText(PseudoRTLString(part))
++
++ return transl
+diff --git a/tools/grit/grit/pseudo_unittest.py b/tools/grit/grit/pseudo_unittest.py
+new file mode 100644
+index 0000000000..b1d53ff401
+--- /dev/null
++++ b/tools/grit/grit/pseudo_unittest.py
+@@ -0,0 +1,55 @@
++#!/usr/bin/env python
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++'''Unit tests for grit.pseudo'''
++
++from __future__ import print_function
++
++import os
++import sys
++if __name__ == '__main__':
++ sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
++
++import unittest
++
++from grit import pseudo
++from grit import tclib
++
++
++class PseudoUnittest(unittest.TestCase):
++ def testVowelMapping(self):
++ self.failUnless(pseudo.MapVowels('abebibobuby') ==
++ u'\u00e5b\u00e9b\u00efb\u00f4b\u00fcb\u00fd')
++ self.failUnless(pseudo.MapVowels('ABEBIBOBUBY') ==
++ u'\u00c5B\u00c9B\u00cfB\u00d4B\u00dcB\u00dd')
++
++ def testPseudoString(self):
++ out = pseudo.PseudoString('hello')
++ self.failUnless(out == pseudo.MapVowels(u'hePelloPo', True))
++
++ def testConsecutiveVowels(self):
++ out = pseudo.PseudoString("beautiful weather, ain't it?")
++ self.failUnless(out == pseudo.MapVowels(
++ u"beauPeautiPifuPul weaPeathePer, aiPain't iPit?", 1))
++
++ def testCapitals(self):
++ out = pseudo.PseudoString("HOWDIE DOODIE, DR. JONES")
++ self.failUnless(out == pseudo.MapVowels(
++ u"HOPOWDIEPIE DOOPOODIEPIE, DR. JOPONEPES", 1))
++
++ def testPseudoMessage(self):
++ msg = tclib.Message(text='Hello USERNAME, how are you?',
++ placeholders=[
++ tclib.Placeholder('USERNAME', '%s', 'Joi')])
++ trans = pseudo.PseudoMessage(msg)
++ # TODO(joi) It would be nicer if 'you' -> 'youPou' instead of
++ # 'you' -> 'youPyou' and if we handled the silent e in 'are'
++ self.failUnless(trans.GetPresentableContent() ==
++ pseudo.MapVowels(
++ u'HePelloPo USERNAME, hoPow aParePe youPyou?', 1))
++
++
++if __name__ == '__main__':
++ unittest.main()
+diff --git a/tools/grit/grit/shortcuts.py b/tools/grit/grit/shortcuts.py
+new file mode 100644
+index 0000000000..0db2ce436c
+--- /dev/null
++++ b/tools/grit/grit/shortcuts.py
+@@ -0,0 +1,93 @@
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++'''Stuff to prevent conflicting shortcuts.
++'''
++
++from __future__ import print_function
++
++from grit import lazy_re
++
++
++class ShortcutGroup(object):
++ '''Manages a list of cliques that belong together in a single shortcut
++ group. Knows how to detect conflicting shortcut keys.
++ '''
++
++ # Matches shortcut keys, e.g. &J
++ SHORTCUT_RE = lazy_re.compile('([^&]|^)(&[A-Za-z])')
++
++ def __init__(self, name):
++ self.name = name
++ # Map of language codes to shortcut keys used (which is a map of
++ # shortcut keys to counts).
++ self.keys_by_lang = {}
++ # List of cliques in this group
++ self.cliques = []
++
++ def AddClique(self, c):
++ for existing_clique in self.cliques:
++ if existing_clique.GetId() == c.GetId():
++ # This happens e.g. when we have e.g.
++ # <if expr1><structure 1></if> <if expr2><structure 2></if>
++ # where only one will really be included in the output.
++ return
++
++ self.cliques.append(c)
++ for (lang, msg) in c.clique.items():
++ if lang not in self.keys_by_lang:
++ self.keys_by_lang[lang] = {}
++ keymap = self.keys_by_lang[lang]
++
++ content = msg.GetRealContent()
++ keys = [groups[1] for groups in self.SHORTCUT_RE.findall(content)]
++ for key in keys:
++ key = key.upper()
++ if key in keymap:
++ keymap[key] += 1
++ else:
++ keymap[key] = 1
++
++ def GenerateWarnings(self, tc_project):
++ # For any language that has more than one occurrence of any shortcut,
++ # make a list of the conflicting shortcuts.
++ problem_langs = {}
++ for (lang, keys) in self.keys_by_lang.items():
++ for (key, count) in keys.items():
++ if count > 1:
++ if lang not in problem_langs:
++ problem_langs[lang] = []
++ problem_langs[lang].append(key)
++
++ warnings = []
++ if len(problem_langs):
++ warnings.append("WARNING - duplicate keys exist in shortcut group %s" %
++ self.name)
++ for (lang,keys) in problem_langs.items():
++ warnings.append(" %6s duplicates: %s" % (lang, ', '.join(keys)))
++ return warnings
++
++
++def GenerateDuplicateShortcutsWarnings(uberclique, tc_project):
++ '''Given an UberClique and a project name, will print out helpful warnings
++ if there are conflicting shortcuts within shortcut groups in the provided
++ UberClique.
++
++ Args:
++ uberclique: clique.UberClique()
++ tc_project: 'MyProjectNameInTheTranslationConsole'
++
++ Returns:
++ ['warning line 1', 'warning line 2', ...]
++ '''
++ warnings = []
++ groups = {}
++ for c in uberclique.AllCliques():
++ for group in c.shortcut_groups:
++ if group not in groups:
++ groups[group] = ShortcutGroup(group)
++ groups[group].AddClique(c)
++ for group in groups.values():
++ warnings += group.GenerateWarnings(tc_project)
++ return warnings
+diff --git a/tools/grit/grit/shortcuts_unittest.py b/tools/grit/grit/shortcuts_unittest.py
+new file mode 100644
+index 0000000000..30e7c4f758
+--- /dev/null
++++ b/tools/grit/grit/shortcuts_unittest.py
+@@ -0,0 +1,79 @@
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++'''Unit tests for grit.shortcuts
++'''
++
++from __future__ import print_function
++
++import os
++import sys
++if __name__ == '__main__':
++ sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
++
++import unittest
++
++from six import StringIO
++
++from grit import shortcuts
++from grit import clique
++from grit import tclib
++from grit.gather import rc
++
++class ShortcutsUnittest(unittest.TestCase):
++
++ def setUp(self):
++ self.uq = clique.UberClique()
++
++ def testFunctionality(self):
++ c = self.uq.MakeClique(tclib.Message(text="Hello &there"))
++ c.AddToShortcutGroup('group_name')
++ c = self.uq.MakeClique(tclib.Message(text="Howdie &there partner"))
++ c.AddToShortcutGroup('group_name')
++
++ warnings = shortcuts.GenerateDuplicateShortcutsWarnings(self.uq, 'PROJECT')
++ self.failUnless(warnings)
++
++ def testAmpersandEscaping(self):
++ c = self.uq.MakeClique(tclib.Message(text="Hello &there"))
++ c.AddToShortcutGroup('group_name')
++ c = self.uq.MakeClique(tclib.Message(text="S&&T are the &letters S and T"))
++ c.AddToShortcutGroup('group_name')
++
++ warnings = shortcuts.GenerateDuplicateShortcutsWarnings(self.uq, 'PROJECT')
++ self.failUnless(len(warnings) == 0)
++
++ def testDialog(self):
++ dlg = rc.Dialog(StringIO('''\
++IDD_SIDEBAR_RSS_PANEL_PROPPAGE DIALOGEX 0, 0, 239, 221
++STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD
++FONT 8, "MS Shell Dlg", 400, 0, 0x1
++BEGIN
++ PUSHBUTTON "Add &URL",IDC_SIDEBAR_RSS_ADD_URL,182,53,57,14
++ EDITTEXT IDC_SIDEBAR_RSS_NEW_URL,0,53,178,15,ES_AUTOHSCROLL
++ PUSHBUTTON "&Remove",IDC_SIDEBAR_RSS_REMOVE,183,200,56,14
++ PUSHBUTTON "&Edit",IDC_SIDEBAR_RSS_EDIT,123,200,56,14
++ CONTROL "&Automatically add commonly viewed clips",
++ IDC_SIDEBAR_RSS_AUTO_ADD,"Button",BS_AUTOCHECKBOX |
++ BS_MULTILINE | WS_TABSTOP,0,200,120,17
++ PUSHBUTTON "",IDC_SIDEBAR_RSS_HIDDEN,179,208,6,6,NOT WS_VISIBLE
++ LTEXT "You can display clips from blogs, news sites, and other online sources.",
++ IDC_STATIC,0,0,239,10
++ LISTBOX IDC_SIDEBAR_DISPLAYED_FEED_LIST,0,69,239,127,LBS_SORT |
++ LBS_OWNERDRAWFIXED | LBS_HASSTRINGS |
++ LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_HSCROLL |
++ WS_TABSTOP
++ LTEXT "Add a clip from a recently viewed website by clicking Add Recent Clips.",
++ IDC_STATIC,0,13,141,19
++ LTEXT "Or, if you know a site supports RSS or Atom, you can enter the RSS or Atom URL below and add it to your list of Web Clips.",
++ IDC_STATIC,0,33,239,18
++ PUSHBUTTON "Add Recent &Clips (10)...",
++ IDC_SIDEBAR_RSS_ADD_RECENT_CLIPS,146,14,93,14
++END'''), 'IDD_SIDEBAR_RSS_PANEL_PROPPAGE')
++ dlg.SetUberClique(self.uq)
++ dlg.Parse()
++
++ warnings = shortcuts.GenerateDuplicateShortcutsWarnings(self.uq, 'PROJECT')
++ self.failUnless(len(warnings) == 0)
++
+diff --git a/tools/grit/grit/tclib.py b/tools/grit/grit/tclib.py
+new file mode 100644
+index 0000000000..27ba366924
+--- /dev/null
++++ b/tools/grit/grit/tclib.py
+@@ -0,0 +1,246 @@
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++'''Adaptation of the extern.tclib classes for our needs.
++'''
++
++from __future__ import print_function
++
++import functools
++import re
++
++import six
++
++from grit import exception
++from grit import lazy_re
++import grit.extern.tclib
++
++
++# Matches whitespace sequences which can be folded into a single whitespace
++# character. This matches single characters so that non-spaces are replaced
++# with spaces.
++_FOLD_WHITESPACE = re.compile(r'\s+')
++
++# Caches compiled regexp used to split tags in BaseMessage.__init__()
++_RE_CACHE = {}
++
++def Identity(i):
++ return i
++
++
++class BaseMessage(object):
++ '''Base class with methods shared by Message and Translation.
++ '''
++
++ def __init__(self, text='', placeholders=[], description='', meaning=''):
++ self.parts = []
++ self.placeholders = []
++ self.meaning = meaning
++ self.dirty = True # True if self.id is (or might be) wrong
++ self.id = 0
++ self.SetDescription(description)
++
++ if text != '':
++ if not placeholders or placeholders == []:
++ self.AppendText(text)
++ else:
++ tag_map = {}
++ for placeholder in placeholders:
++ tag_map[placeholder.GetPresentation()] = [placeholder, 0]
++ # This creates a regexp like '(TAG1|TAG2|TAG3)'.
++ # The tags have to be sorted in order of decreasing length, so that
++ # longer tags are substituted before shorter tags that happen to be
++ # substrings of the longer tag.
++ # E.g. "EXAMPLE_FOO_NAME" must be matched before "EXAMPLE_FOO",
++ # otherwise "EXAMPLE_FOO" splits "EXAMPLE_FOO_NAME" too.
++ tags = sorted(tag_map.keys(),
++ key=functools.cmp_to_key(
++ lambda x, y: len(x) - len(y) or ((x > y) - (x < y))),
++ reverse=True)
++ tag_re = '(' + '|'.join(tags) + ')'
++
++ # This caching improves the time to build
++ # chrome/app:generated_resources from 21.562s to 17.672s on Linux.
++ compiled_re = _RE_CACHE.get(tag_re, None)
++ if compiled_re is None:
++ compiled_re = re.compile(tag_re)
++ _RE_CACHE[tag_re] = compiled_re
++
++ chunked_text = compiled_re.split(text)
++
++ for chunk in chunked_text:
++ if chunk: # ignore empty chunk
++ if chunk in tag_map:
++ self.AppendPlaceholder(tag_map[chunk][0])
++ tag_map[chunk][1] += 1 # increase placeholder use count
++ else:
++ self.AppendText(chunk)
++ for key in tag_map:
++ assert tag_map[key][1] != 0
++
++ def GetRealContent(self, escaping_function=Identity):
++ '''Returns the original content, i.e. what your application and users
++ will see.
++
++ Specify a function to escape each translateable bit, if you like.
++ '''
++ bits = []
++ for item in self.parts:
++ if isinstance(item, six.string_types):
++ bits.append(escaping_function(item))
++ else:
++ bits.append(item.GetOriginal())
++ return ''.join(bits)
++
++ def GetPresentableContent(self):
++ presentable_content = []
++ for part in self.parts:
++ if isinstance(part, Placeholder):
++ presentable_content.append(part.GetPresentation())
++ else:
++ presentable_content.append(part)
++ return ''.join(presentable_content)
++
++ def AppendPlaceholder(self, placeholder):
++ assert isinstance(placeholder, Placeholder)
++ dup = False
++ for other in self.GetPlaceholders():
++ if other.presentation == placeholder.presentation:
++ assert other.original == placeholder.original
++ dup = True
++
++ if not dup:
++ self.placeholders.append(placeholder)
++ self.parts.append(placeholder)
++ self.dirty = True
++
++ def AppendText(self, text):
++ assert isinstance(text, six.string_types)
++ assert text != ''
++
++ self.parts.append(text)
++ self.dirty = True
++
++ def GetContent(self):
++ '''Returns the parts of the message. You may modify parts if you wish.
++ Note that you must not call GetId() on this object until you have finished
++ modifying the contents.
++ '''
++ self.dirty = True # user might modify content
++ return self.parts
++
++ def GetDescription(self):
++ return self.description
++
++ def SetDescription(self, description):
++ self.description = _FOLD_WHITESPACE.sub(' ', description)
++
++ def GetMeaning(self):
++ return self.meaning
++
++ def GetId(self):
++ if self.dirty:
++ self.id = self.GenerateId()
++ self.dirty = False
++ return self.id
++
++ def GenerateId(self):
++ return grit.extern.tclib.GenerateMessageId(self.GetPresentableContent(),
++ self.meaning)
++
++ def GetPlaceholders(self):
++ return self.placeholders
++
++ def FillTclibBaseMessage(self, msg):
++ msg.SetDescription(self.description.encode('utf-8'))
++
++ for part in self.parts:
++ if isinstance(part, Placeholder):
++ ph = grit.extern.tclib.Placeholder(
++ part.presentation.encode('utf-8'),
++ part.original.encode('utf-8'),
++ part.example.encode('utf-8'))
++ msg.AppendPlaceholder(ph)
++ else:
++ msg.AppendText(part.encode('utf-8'))
++
++
++class Message(BaseMessage):
++ '''A message.'''
++
++ def __init__(self, text='', placeholders=[], description='', meaning='',
++ assigned_id=None):
++ super(Message, self).__init__(text, placeholders, description, meaning)
++ self.assigned_id = assigned_id
++
++ def ToTclibMessage(self):
++ msg = grit.extern.tclib.Message('utf-8', meaning=self.meaning)
++ self.FillTclibBaseMessage(msg)
++ return msg
++
++ def GetId(self):
++ '''Use the assigned id if we have one.'''
++ if self.assigned_id:
++ return self.assigned_id
++
++ return super(Message, self).GetId()
++
++ def HasAssignedId(self):
++ '''Returns True if this message has an assigned id.'''
++ return bool(self.assigned_id)
++
++
++class Translation(BaseMessage):
++ '''A translation.'''
++
++ def __init__(self, text='', id='', placeholders=[], description='', meaning=''):
++ super(Translation, self).__init__(text, placeholders, description, meaning)
++ self.id = id
++
++ def GetId(self):
++ assert id != '', "ID has not been set."
++ return self.id
++
++ def SetId(self, id):
++ self.id = id
++
++ def ToTclibMessage(self):
++ msg = grit.extern.tclib.Message(
++ 'utf-8', id=self.id, meaning=self.meaning)
++ self.FillTclibBaseMessage(msg)
++ return msg
++
++
++class Placeholder(grit.extern.tclib.Placeholder):
++ '''Modifies constructor to accept a Unicode string
++ '''
++
++ # Must match placeholder presentation names
++ _NAME_RE = lazy_re.compile('^[A-Za-z0-9_]+$')
++
++ def __init__(self, presentation, original, example):
++ '''Creates a new placeholder.
++
++ Args:
++ presentation: 'USERNAME'
++ original: '%s'
++ example: 'Joi'
++ '''
++ assert presentation != ''
++ assert original != ''
++ assert example != ''
++ if not self._NAME_RE.match(presentation):
++ raise exception.InvalidPlaceholderName(presentation)
++ self.presentation = presentation
++ self.original = original
++ self.example = example
++
++ def GetPresentation(self):
++ return self.presentation
++
++ def GetOriginal(self):
++ return self.original
++
++ def GetExample(self):
++ return self.example
+diff --git a/tools/grit/grit/tclib_unittest.py b/tools/grit/grit/tclib_unittest.py
+new file mode 100644
+index 0000000000..7a08654e1b
+--- /dev/null
++++ b/tools/grit/grit/tclib_unittest.py
+@@ -0,0 +1,180 @@
++#!/usr/bin/env python
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++'''Unit tests for grit.tclib'''
++
++from __future__ import print_function
++
++import sys
++import os.path
++if __name__ == '__main__':
++ sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
++
++import unittest
++
++import six
++
++from grit import tclib
++
++from grit import exception
++import grit.extern.tclib
++
++
++class TclibUnittest(unittest.TestCase):
++ def testInit(self):
++ msg = tclib.Message(text=u'Hello Earthlings',
++ description='Greetings\n\t message')
++ self.failUnlessEqual(msg.GetPresentableContent(), 'Hello Earthlings')
++ self.failUnless(isinstance(msg.GetPresentableContent(), six.string_types))
++ self.failUnlessEqual(msg.GetDescription(), 'Greetings message')
++
++ def testGetAttr(self):
++ msg = tclib.Message()
++ msg.AppendText(u'Hello') # Tests __getattr__
++ self.failUnless(msg.GetPresentableContent() == 'Hello')
++ self.failUnless(isinstance(msg.GetPresentableContent(), six.string_types))
++
++ def testAll(self):
++ text = u'Howdie USERNAME'
++ phs = [tclib.Placeholder(u'USERNAME', u'%s', 'Joi')]
++ msg = tclib.Message(text=text, placeholders=phs)
++ self.failUnless(msg.GetPresentableContent() == 'Howdie USERNAME')
++
++ trans = tclib.Translation(text=text, placeholders=phs)
++ self.failUnless(trans.GetPresentableContent() == 'Howdie USERNAME')
++ self.failUnless(isinstance(trans.GetPresentableContent(), six.string_types))
++
++ def testUnicodeReturn(self):
++ text = u'\u00fe'
++ msg = tclib.Message(text=text)
++ self.failUnless(msg.GetPresentableContent() == text)
++ from_list = msg.GetContent()[0]
++ self.failUnless(from_list == text)
++
++ def testRegressionTranslationInherited(self):
++ '''Regression tests a bug that was caused by grit.tclib.Translation
++ inheriting from the translation console's Translation object
++ instead of only owning an instance of it.
++ '''
++ msg = tclib.Message(text=u"BLA1\r\nFrom: BLA2 \u00fe BLA3",
++ placeholders=[
++ tclib.Placeholder('BLA1', '%s', '%s'),
++ tclib.Placeholder('BLA2', '%s', '%s'),
++ tclib.Placeholder('BLA3', '%s', '%s')])
++ transl = tclib.Translation(text=msg.GetPresentableContent(),
++ placeholders=msg.GetPlaceholders())
++ content = transl.GetContent()
++ self.failUnless(isinstance(content[3], six.string_types))
++
++ def testFingerprint(self):
++ # This has Windows line endings. That is on purpose.
++ id = grit.extern.tclib.GenerateMessageId(
++ 'Google Desktop for Enterprise\r\n'
++ 'All Rights Reserved\r\n'
++ '\r\n'
++ '---------\r\n'
++ 'Contents\r\n'
++ '---------\r\n'
++ 'This distribution contains the following files:\r\n'
++ '\r\n'
++ 'GoogleDesktopSetup.msi - Installation and setup program\r\n'
++ 'GoogleDesktop.adm - Group Policy administrative template file\r\n'
++ 'AdminGuide.pdf - Google Desktop for Enterprise administrative guide\r\n'
++ '\r\n'
++ '\r\n'
++ '--------------\r\n'
++ 'Documentation\r\n'
++ '--------------\r\n'
++ 'Full documentation and installation instructions are in the \r\n'
++ 'administrative guide, and also online at \r\n'
++ 'http://desktop.google.com/enterprise/adminguide.html.\r\n'
++ '\r\n'
++ '\r\n'
++ '------------------------\r\n'
++ 'IBM Lotus Notes Plug-In\r\n'
++ '------------------------\r\n'
++ 'The Lotus Notes plug-in is included in the release of Google \r\n'
++ 'Desktop for Enterprise. The IBM Lotus Notes Plug-in for Google \r\n'
++ 'Desktop indexes mail, calendar, task, contact and journal \r\n'
++ 'documents from Notes. Discussion documents including those from \r\n'
++ 'the discussion and team room templates can also be indexed by \r\n'
++ 'selecting an option from the preferences. Once indexed, this data\r\n'
++ 'will be returned in Google Desktop searches. The corresponding\r\n'
++ 'document can be opened in Lotus Notes from the Google Desktop \r\n'
++ 'results page.\r\n'
++ '\r\n'
++ 'Install: The plug-in will install automatically during the Google \r\n'
++ 'Desktop setup process if Lotus Notes is already installed. Lotus \r\n'
++ 'Notes must not be running in order for the install to occur. \r\n'
++ '\r\n'
++ 'Preferences: Preferences and selection of databases to index are\r\n'
++ 'set in the \'Google Desktop for Notes\' dialog reached through the \r\n'
++ '\'Actions\' menu.\r\n'
++ '\r\n'
++ 'Reindexing: Selecting \'Reindex all databases\' will index all the \r\n'
++ 'documents in each database again.\r\n'
++ '\r\n'
++ '\r\n'
++ 'Notes Plug-in Known Issues\r\n'
++ '---------------------------\r\n'
++ '\r\n'
++ 'If the \'Google Desktop for Notes\' item is not available from the \r\n'
++ 'Lotus Notes Actions menu, then installation was not successful. \r\n'
++ 'Installation consists of writing one file, notesgdsplugin.dll, to \r\n'
++ 'the Notes application directory and a setting to the notes.ini \r\n'
++ 'configuration file. The most likely cause of an unsuccessful \r\n'
++ 'installation is that the installer was not able to locate the \r\n'
++ 'notes.ini file. Installation will complete if the user closes Notes\r\n'
++ 'and manually adds the following setting to this file on a new line:\r\n'
++ 'AddinMenus=notegdsplugin.dll\r\n'
++ '\r\n'
++ 'If the notesgdsplugin.dll file is not in the application directory\r\n'
++ r'(e.g., C:\Program Files\Lotus\Notes) after Google Desktop \r\n'
++ 'installation, it is likely that Notes was not installed correctly. \r\n'
++ '\r\n'
++ 'Only local databases can be indexed. If they can be determined, \r\n'
++ 'the user\'s local mail file and address book will be included in the\r\n'
++ 'list automatically. Mail archives and other databases must be \r\n'
++ 'added with the \'Add\' button.\r\n'
++ '\r\n'
++ 'Some users may experience performance issues during the initial \r\n'
++ 'indexing of a database. The \'Perform the initial index of a \r\n'
++ 'database only when I\'m idle\' option will limit the indexing process\r\n'
++ 'to times when the user is not using the machine. If this does not \r\n'
++ 'alleviate the problem or the user would like to continually index \r\n'
++ 'but just do so more slowly or quickly, the GoogleWaitTime notes.ini\r\n'
++ 'value can be set. Increasing the GoogleWaitTime value will slow \r\n'
++ 'down the indexing process, and lowering the value will speed it up.\r\n'
++ 'A value of zero causes the fastest possible indexing. Removing the\r\n'
++ 'ini parameter altogether returns it to the default (20).\r\n'
++ '\r\n'
++ 'Crashes have been known to occur with certain types of history \r\n'
++ 'bookmarks. If the Notes client seems to crash randomly, try \r\n'
++ 'disabling the \'Index note history\' option. If it crashes before,\r\n'
++ 'you can get to the preferences, add the following line to your \r\n'
++ 'notes.ini file:\r\n'
++ 'GDSNoIndexHistory=1\r\n')
++ self.assertEqual(id, '7660964495923572726')
++
++ def testPlaceholderNameChecking(self):
++ try:
++ ph = tclib.Placeholder('BINGO BONGO', 'bla', 'bla')
++ raise Exception("We shouldn't get here")
++ except exception.InvalidPlaceholderName:
++ pass # Expect exception to be thrown because presentation contained space
++
++ def testTagsWithCommonSubstring(self):
++ word = 'ABCDEFGHIJ'
++ text = ' '.join([word[:i] for i in range(1, 11)])
++ phs = [tclib.Placeholder(word[:i], str(i), str(i)) for i in range(1, 11)]
++ try:
++ msg = tclib.Message(text=text, placeholders=phs)
++ self.failUnless(msg.GetRealContent() == '1 2 3 4 5 6 7 8 9 10')
++ except:
++ self.fail('tclib.Message() should handle placeholders that are '
++ 'substrings of each other')
++
++if __name__ == '__main__':
++ unittest.main()
+diff --git a/tools/grit/grit/test_suite_all.py b/tools/grit/grit/test_suite_all.py
+new file mode 100644
+index 0000000000..3bfe2a79d5
+--- /dev/null
++++ b/tools/grit/grit/test_suite_all.py
+@@ -0,0 +1,34 @@
++#!/usr/bin/env python3
++# Copyright (c) 2011 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++'''Unit test suite that collects all test cases for GRIT.'''
++
++from __future__ import print_function
+
-+#endif // MISSING_CTOR_H_
-diff --git a/tools/clang/plugins/tests/missing_ctor.txt b/tools/clang/plugins/tests/missing_ctor.txt
++import os
++import sys
++
++
++CUR_DIR = os.path.dirname(os.path.realpath(__file__))
++SRC_DIR = os.path.dirname(os.path.dirname(os.path.dirname(CUR_DIR)))
++TYP_DIR = os.path.join(
++ SRC_DIR, 'third_party', 'catapult', 'third_party', 'typ')
++
++if TYP_DIR not in sys.path:
++ sys.path.insert(0, TYP_DIR)
++
++
++import typ # pylint: disable=import-error,unused-import
++
++
++def main(args):
++ return typ.main(
++ top_level_dirs=[os.path.join(CUR_DIR, '..')],
++ skip=['grit.format.gen_predetermined_ids_unittest.*',
++ 'grit.pseudo_unittest.*']
++ )
++
++if __name__ == '__main__':
++ sys.exit(main(sys.argv[1:]))
+diff --git a/tools/grit/grit/testdata/GoogleDesktop.adm b/tools/grit/grit/testdata/GoogleDesktop.adm
+new file mode 100644
+index 0000000000..082f56bb1a
+--- /dev/null
++++ b/tools/grit/grit/testdata/GoogleDesktop.adm
+@@ -0,0 +1,945 @@
++CLASS MACHINE
++ CATEGORY !!Cat_Google
++ CATEGORY !!Cat_GoogleDesktopSearch
++ KEYNAME "Software\Policies\Google\Google Desktop"
++
++ CATEGORY !!Cat_Preferences
++ KEYNAME "Software\Policies\Google\Google Desktop\Preferences"
++
++ CATEGORY !!Cat_IndexAndCaptureControl
++ POLICY !!Blacklist_Email
++ EXPLAIN !!Explain_Blacklist_Email
++ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-1"
++ VALUENAME "1"
++ END POLICY
++
++ POLICY !!Blacklist_Gmail
++ EXPLAIN !!Explain_Blacklist_Gmail
++ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-pop"
++ VALUENAME "gmail"
++ END POLICY
++
++ POLICY !!Blacklist_WebHistory
++ EXPLAIN !!Explain_Blacklist_WebHistory
++ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-1"
++ VALUENAME "2"
++ END POLICY
++
++ POLICY !!Blacklist_Chat
++ EXPLAIN !!Explain_Blacklist_Chat
++ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-1"
++ ACTIONLISTON
++ VALUENAME "3" VALUE NUMERIC 1
++ END ACTIONLISTON
++ END POLICY
++
++ POLICY !!Blacklist_Text
++ EXPLAIN !!Explain_Blacklist_Text
++ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-1"
++ ACTIONLISTON
++ VALUENAME "4" VALUE NUMERIC 1
++ END ACTIONLISTON
++ END POLICY
++
++ POLICY !!Blacklist_Media
++ EXPLAIN !!Explain_Blacklist_Media
++ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-1"
++ ACTIONLISTON
++ VALUENAME "5" VALUE NUMERIC 1
++ END ACTIONLISTON
++ END POLICY
++
++ POLICY !!Blacklist_Contact
++ EXPLAIN !!Explain_Blacklist_Contact
++ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-1"
++ ACTIONLISTON
++ VALUENAME "9" VALUE NUMERIC 1
++ END ACTIONLISTON
++ END POLICY
++
++ POLICY !!Blacklist_Calendar
++ EXPLAIN !!Explain_Blacklist_Calendar
++ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-1"
++ ACTIONLISTON
++ VALUENAME "10" VALUE NUMERIC 1
++ END ACTIONLISTON
++ END POLICY
++
++ POLICY !!Blacklist_Task
++ EXPLAIN !!Explain_Blacklist_Task
++ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-1"
++ ACTIONLISTON
++ VALUENAME "11" VALUE NUMERIC 1
++ END ACTIONLISTON
++ END POLICY
++
++ POLICY !!Blacklist_Note
++ EXPLAIN !!Explain_Blacklist_Note
++ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-1"
++ ACTIONLISTON
++ VALUENAME "12" VALUE NUMERIC 1
++ END ACTIONLISTON
++ END POLICY
++
++ POLICY !!Blacklist_Journal
++ EXPLAIN !!Explain_Blacklist_Journal
++ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-1"
++ ACTIONLISTON
++ VALUENAME "13" VALUE NUMERIC 1
++ END ACTIONLISTON
++ END POLICY
++
++ POLICY !!Blacklist_Word
++ EXPLAIN !!Explain_Blacklist_Word
++ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-2"
++ VALUENAME "DOC"
++ END POLICY
++
++ POLICY !!Blacklist_Excel
++ EXPLAIN !!Explain_Blacklist_Excel
++ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-2"
++ VALUENAME "XLS"
++ END POLICY
++
++ POLICY !!Blacklist_Powerpoint
++ EXPLAIN !!Explain_Blacklist_Powerpoint
++ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-2"
++ VALUENAME "PPT"
++ END POLICY
++
++ POLICY !!Blacklist_PDF
++ EXPLAIN !!Explain_Blacklist_PDF
++ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-2"
++ VALUENAME "PDF"
++ END POLICY
++
++ POLICY !!Blacklist_ZIP
++ EXPLAIN !!Explain_Blacklist_ZIP
++ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-2"
++ VALUENAME "ZIP"
++ END POLICY
++
++ POLICY !!Blacklist_HTTPS
++ EXPLAIN !!Explain_Blacklist_HTTPS
++ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-3"
++ VALUENAME "HTTPS"
++ END POLICY
++
++ POLICY !!Blacklist_PasswordProtectedOffice
++ EXPLAIN !!Explain_Blacklist_PasswordProtectedOffice
++ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-13"
++ VALUENAME "SECUREOFFICE"
++ END POLICY
++
++ POLICY !!Blacklist_URI_Contains
++ EXPLAIN !!Explain_Blacklist_URI_Contains
++ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-6"
++ PART !!Blacklist_URI_Contains LISTBOX
++ END PART
++ END POLICY
++
++ POLICY !!Blacklist_Extensions
++ EXPLAIN !!Explain_Blacklist_Extensions
++ PART !!Blacklist_Extensions EDITTEXT
++ VALUENAME "file_extensions_to_skip"
++ END PART
++ END POLICY
++
++ POLICY !!Pol_Disallow_UserSearchLocations
++ EXPLAIN !!Explain_Disallow_UserSearchLocations
++ VALUENAME user_search_locations
++ VALUEON NUMERIC 1
++ END POLICY
++
++ POLICY !!Pol_Search_Location_Whitelist
++ EXPLAIN !!Explain_Search_Location_Whitelist
++ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\policy_search_location_whitelist"
++ PART !!Search_Locations_Whitelist LISTBOX
++ END PART
++ END POLICY
++
++ POLICY !!Email_Retention
++ EXPLAIN !!Explain_Email_Retention
++ PART !!Email_Retention_Edit NUMERIC
++ VALUENAME "email_days_to_retain"
++ MIN 1 MAX 65535 DEFAULT 30 SPIN 1
++ END PART
++ END POLICY
++
++ POLICY !!Webpage_Retention
++ EXPLAIN !!Explain_Webpage_Retention
++ PART !!Webpage_Retention_Edit NUMERIC
++ VALUENAME "webpage_days_to_retain"
++ MIN 1 MAX 65535 DEFAULT 30 SPIN 1
++ END PART
++ END POLICY
++
++ POLICY !!File_Retention
++ EXPLAIN !!Explain_File_Retention
++ PART !!File_Retention_Edit NUMERIC
++ VALUENAME "file_days_to_retain"
++ MIN 1 MAX 65535 DEFAULT 30 SPIN 1
++ END PART
++ END POLICY
++
++ POLICY !!IM_Retention
++ EXPLAIN !!Explain_IM_Retention
++ PART !!IM_Retention_Edit NUMERIC
++ VALUENAME "im_days_to_retain"
++ MIN 1 MAX 65535 DEFAULT 30 SPIN 1
++ END PART
++ END POLICY
++
++ POLICY !!Pol_Remove_Deleted_Items
++ EXPLAIN !!Explain_Remove_Deleted_Items
++ VALUENAME remove_deleted_items
++ VALUEON NUMERIC 1
++ END POLICY
++
++ POLICY !!Pol_Allow_Simultaneous_Indexing
++ EXPLAIN !!Explain_Allow_Simultaneous_Indexing
++ VALUENAME simultaneous_indexing
++ VALUEON NUMERIC 1
++ END POLICY
++
++ END CATEGORY
++
++ POLICY !!Pol_TurnOffAdvancedFeatures
++ EXPLAIN !!Explain_TurnOffAdvancedFeatures
++ VALUENAME error_report_on
++ VALUEON NUMERIC 0
++ END POLICY
++
++ POLICY !!Pol_TurnOffImproveGd
++ EXPLAIN !!Explain_TurnOffImproveGd
++ VALUENAME improve_gd
++ VALUEON NUMERIC 0
++ VALUEOFF NUMERIC 1
++ END POLICY
++
++ POLICY !!Pol_NoPersonalizationInfo
++ EXPLAIN !!Explain_NoPersonalizationInfo
++ VALUENAME send_personalization_info
++ VALUEON NUMERIC 0
++ VALUEOFF NUMERIC 1
++ END POLICY
++
++ POLICY !!Pol_OneBoxMode
++ EXPLAIN !!Explain_OneBoxMode
++ VALUENAME onebox_mode
++ VALUEON NUMERIC 0
++ END POLICY
++
++ POLICY !!Pol_EncryptIndex
++ EXPLAIN !!Explain_EncryptIndex
++ VALUENAME encrypt_index
++ VALUEON NUMERIC 1
++ END POLICY
++
++ POLICY !!Pol_Hyper
++ EXPLAIN !!Explain_Hyper
++ VALUENAME hyper_off
++ VALUEON NUMERIC 1
++ END POLICY
++
++ POLICY !!Pol_Display_Mode
++ EXPLAIN !!Explain_Display_Mode
++ PART !!Pol_Display_Mode DROPDOWNLIST
++ VALUENAME display_mode
++ ITEMLIST
++ NAME !!Sidebar VALUE NUMERIC 1
++ NAME !!Deskbar VALUE NUMERIC 8
++ NAME !!FloatingDeskbar VALUE NUMERIC 4
++ NAME !!None VALUE NUMERIC 0
++ END ITEMLIST
++ END PART
++ END POLICY
++
++ END CATEGORY ; Preferences
++
++ CATEGORY !!Cat_Enterprise
++ KEYNAME "Software\Policies\Google\Google Desktop\Enterprise"
++
++ POLICY !!Pol_Autoupdate
++ EXPLAIN !!Explain_Autoupdate
++ VALUENAME autoupdate_host
++ VALUEON ""
++ END POLICY
++
++ POLICY !!Pol_AutoupdateAsSystem
++ EXPLAIN !!Explain_AutoupdateAsSystem
++ VALUENAME autoupdate_impersonate_user
++ VALUEON NUMERIC 0
++ VALUEOFF NUMERIC 1
++ END POLICY
++
++ POLICY !!Pol_EnterpriseTab
++ EXPLAIN !!Explain_EnterpriseTab
++ PART !!EnterpriseTabText EDITTEXT
++ VALUENAME enterprise_tab_text
++ END PART
++ PART !!EnterpriseTabHomepage EDITTEXT
++ VALUENAME enterprise_tab_homepage
++ END PART
++ PART !!EnterpriseTabHomepageQuery CHECKBOX
++ VALUENAME enterprise_tab_homepage_query
++ END PART
++ PART !!EnterpriseTabResults EDITTEXT
++ VALUENAME enterprise_tab_results
++ END PART
++ PART !!EnterpriseTabResultsQuery CHECKBOX
++ VALUENAME enterprise_tab_results_query
++ END PART
++ END POLICY
++
++ POLICY !!Pol_GSAHosts
++ EXPLAIN !!Explain_GSAHosts
++ KEYNAME "Software\Policies\Google\Google Desktop\Enterprise\GSAHosts"
++ PART !!Pol_GSAHosts LISTBOX
++ END PART
++ END POLICY
++
++ POLICY !!Pol_PolicyUnawareClientProhibitedFlag
++ EXPLAIN !!Explain_PolicyUnawareClientProhibitedFlag
++ KEYNAME "Software\Policies\Google\Google Desktop"
++ VALUENAME PolicyUnawareClientProhibitedFlag
++ END POLICY
++
++ POLICY !!Pol_MinimumAllowedVersion
++ EXPLAIN !!Explain_MinimumAllowedVersion
++ PART !!Pol_MinimumAllowedVersion EDITTEXT
++ VALUENAME minimum_allowed_version
++ END PART
++ END POLICY
++
++ POLICY !!Pol_MaximumAllowedVersion
++ EXPLAIN !!Explain_MaximumAllowedVersion
++ PART !!Pol_MaximumAllowedVersion EDITTEXT
++ VALUENAME maximum_allowed_version
++ END PART
++ END POLICY
++
++ POLICY !!Pol_Disallow_Gadgets
++ EXPLAIN !!Explain_Disallow_Gadgets
++ VALUENAME disallow_gadgets
++ VALUEON NUMERIC 1
++ PART !!Disallow_Only_Non_Builtin_Gadgets CHECKBOX DEFCHECKED
++ VALUENAME disallow_only_non_builtin_gadgets
++ VALUEON NUMERIC 1
++ VALUEOFF NUMERIC 0
++ END PART
++ END POLICY
++
++ POLICY !!Pol_Gadget_Whitelist
++ EXPLAIN !!Explain_Gadget_Whitelist
++ KEYNAME "Software\Policies\Google\Google Desktop\Enterprise\gadget_whitelist"
++ PART !!Pol_Gadget_Whitelist LISTBOX
++ END PART
++ END POLICY
++
++ POLICY !!Pol_Gadget_Install_Confirmation_Whitelist
++ EXPLAIN !!Explain_Gadget_Install_Confirmation_Whitelist
++ KEYNAME "Software\Policies\Google\Google Desktop\Enterprise\install_confirmation_whitelist"
++ PART !!Pol_Gadget_Install_Confirmation_Whitelist LISTBOX
++ END PART
++ END POLICY
++
++ POLICY !!Pol_Alternate_User_Data_Dir
++ EXPLAIN !!Explain_Alternate_User_Data_Dir
++ PART !!Pol_Alternate_User_Data_Dir EDITTEXT
++ VALUENAME alternate_user_data_dir
++ END PART
++ END POLICY
++
++ POLICY !!Pol_MaxAllowedOutlookConnections
++ EXPLAIN !!Explain_MaxAllowedOutlookConnections
++ PART !!Pol_MaxAllowedOutlookConnections NUMERIC
++ VALUENAME max_allowed_outlook_connections
++ MIN 1 MAX 65535 DEFAULT 400 SPIN 1
++ END PART
++ END POLICY
++
++ POLICY !!Pol_DisallowSsdService
++ EXPLAIN !!Explain_DisallowSsdService
++ VALUENAME disallow_ssd_service
++ VALUEON NUMERIC 1
++ END POLICY
++
++ POLICY !!Pol_DisallowSsdOutbound
++ EXPLAIN !!Explain_DisallowSsdOutbound
++ VALUENAME disallow_ssd_outbound
++ VALUEON NUMERIC 1
++ END POLICY
++
++ POLICY !!Pol_Disallow_Store_Gadget_Service
++ EXPLAIN !!Explain_Disallow_Store_Gadget_Service
++ VALUENAME disallow_store_gadget_service
++ VALUEON NUMERIC 1
++ END POLICY
++
++ POLICY !!Pol_MaxExchangeIndexingRate
++ EXPLAIN !!Explain_MaxExchangeIndexingRate
++ PART !!Pol_MaxExchangeIndexingRate NUMERIC
++ VALUENAME max_exchange_indexing_rate
++ MIN 1 MAX 1000 DEFAULT 60 SPIN 1
++ END PART
++ END POLICY
++
++ POLICY !!Pol_EnableSafeweb
++ EXPLAIN !!Explain_Safeweb
++ VALUENAME safe_browsing
++ VALUEON NUMERIC 1
++ VALUEOFF NUMERIC 0
++ END POLICY
++
++ END CATEGORY ; Enterprise
++
++ END CATEGORY ; GoogleDesktopSearch
++ END CATEGORY ; Google
++
++
++CLASS USER
++ CATEGORY !!Cat_Google
++ CATEGORY !!Cat_GoogleDesktopSearch
++ KEYNAME "Software\Policies\Google\Google Desktop"
++
++ CATEGORY !!Cat_Preferences
++ KEYNAME "Software\Policies\Google\Google Desktop\Preferences"
++
++ CATEGORY !!Cat_IndexAndCaptureControl
++ POLICY !!Blacklist_Email
++ EXPLAIN !!Explain_Blacklist_Email
++ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-1"
++ VALUENAME "1"
++ END POLICY
++
++ POLICY !!Blacklist_Gmail
++ EXPLAIN !!Explain_Blacklist_Gmail
++ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-pop"
++ VALUENAME "gmail"
++ END POLICY
++
++ POLICY !!Blacklist_WebHistory
++ EXPLAIN !!Explain_Blacklist_WebHistory
++ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-1"
++ VALUENAME "2"
++ END POLICY
++
++ POLICY !!Blacklist_Chat
++ EXPLAIN !!Explain_Blacklist_Chat
++ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-1"
++ ACTIONLISTON
++ VALUENAME "3" VALUE NUMERIC 1
++ END ACTIONLISTON
++ END POLICY
++
++ POLICY !!Blacklist_Text
++ EXPLAIN !!Explain_Blacklist_Text
++ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-1"
++ ACTIONLISTON
++ VALUENAME "4" VALUE NUMERIC 1
++ END ACTIONLISTON
++ END POLICY
++
++ POLICY !!Blacklist_Media
++ EXPLAIN !!Explain_Blacklist_Media
++ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-1"
++ ACTIONLISTON
++ VALUENAME "5" VALUE NUMERIC 1
++ END ACTIONLISTON
++ END POLICY
++
++ POLICY !!Blacklist_Contact
++ EXPLAIN !!Explain_Blacklist_Contact
++ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-1"
++ ACTIONLISTON
++ VALUENAME "9" VALUE NUMERIC 1
++ END ACTIONLISTON
++ END POLICY
++
++ POLICY !!Blacklist_Calendar
++ EXPLAIN !!Explain_Blacklist_Calendar
++ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-1"
++ ACTIONLISTON
++ VALUENAME "10" VALUE NUMERIC 1
++ END ACTIONLISTON
++ END POLICY
++
++ POLICY !!Blacklist_Task
++ EXPLAIN !!Explain_Blacklist_Task
++ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-1"
++ ACTIONLISTON
++ VALUENAME "11" VALUE NUMERIC 1
++ END ACTIONLISTON
++ END POLICY
++
++ POLICY !!Blacklist_Note
++ EXPLAIN !!Explain_Blacklist_Note
++ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-1"
++ ACTIONLISTON
++ VALUENAME "12" VALUE NUMERIC 1
++ END ACTIONLISTON
++ END POLICY
++
++ POLICY !!Blacklist_Journal
++ EXPLAIN !!Explain_Blacklist_Journal
++ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-1"
++ ACTIONLISTON
++ VALUENAME "13" VALUE NUMERIC 1
++ END ACTIONLISTON
++ END POLICY
++
++ POLICY !!Blacklist_Word
++ EXPLAIN !!Explain_Blacklist_Word
++ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-2"
++ VALUENAME "DOC"
++ END POLICY
++
++ POLICY !!Blacklist_Excel
++ EXPLAIN !!Explain_Blacklist_Excel
++ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-2"
++ VALUENAME "XLS"
++ END POLICY
++
++ POLICY !!Blacklist_Powerpoint
++ EXPLAIN !!Explain_Blacklist_Powerpoint
++ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-2"
++ VALUENAME "PPT"
++ END POLICY
++
++ POLICY !!Blacklist_PDF
++ EXPLAIN !!Explain_Blacklist_PDF
++ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-2"
++ VALUENAME "PDF"
++ END POLICY
++
++ POLICY !!Blacklist_ZIP
++ EXPLAIN !!Explain_Blacklist_ZIP
++ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-2"
++ VALUENAME "ZIP"
++ END POLICY
++
++ POLICY !!Blacklist_HTTPS
++ EXPLAIN !!Explain_Blacklist_HTTPS
++ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-3"
++ VALUENAME "HTTPS"
++ END POLICY
++
++ POLICY !!Blacklist_PasswordProtectedOffice
++ EXPLAIN !!Explain_Blacklist_PasswordProtectedOffice
++ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-13"
++ VALUENAME "SECUREOFFICE"
++ END POLICY
++
++ POLICY !!Blacklist_URI_Contains
++ EXPLAIN !!Explain_Blacklist_URI_Contains
++ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-6"
++ PART !!Blacklist_URI_Contains LISTBOX
++ END PART
++ END POLICY
++
++ POLICY !!Blacklist_Extensions
++ EXPLAIN !!Explain_Blacklist_Extensions
++ PART !!Blacklist_Extensions EDITTEXT
++ VALUENAME "file_extensions_to_skip"
++ END PART
++ END POLICY
++
++ POLICY !!Pol_Disallow_UserSearchLocations
++ EXPLAIN !!Explain_Disallow_UserSearchLocations
++ VALUENAME user_search_locations
++ VALUEON NUMERIC 1
++ END POLICY
++
++ POLICY !!Pol_Search_Location_Whitelist
++ EXPLAIN !!Explain_Search_Location_Whitelist
++ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\policy_search_location_whitelist"
++ PART !!Search_Locations_Whitelist LISTBOX
++ END PART
++ END POLICY
++
++ POLICY !!Email_Retention
++ EXPLAIN !!Explain_Email_Retention
++ PART !!Email_Retention_Edit NUMERIC
++ VALUENAME "email_days_to_retain"
++ MIN 1 MAX 65535 DEFAULT 30 SPIN 1
++ END PART
++ END POLICY
++
++ POLICY !!Webpage_Retention
++ EXPLAIN !!Explain_Webpage_Retention
++ PART !!Webpage_Retention_Edit NUMERIC
++ VALUENAME "webpage_days_to_retain"
++ MIN 1 MAX 65535 DEFAULT 30 SPIN 1
++ END PART
++ END POLICY
++
++ POLICY !!File_Retention
++ EXPLAIN !!Explain_File_Retention
++ PART !!File_Retention_Edit NUMERIC
++ VALUENAME "file_days_to_retain"
++ MIN 1 MAX 65535 DEFAULT 30 SPIN 1
++ END PART
++ END POLICY
++
++ POLICY !!IM_Retention
++ EXPLAIN !!Explain_IM_Retention
++ PART !!IM_Retention_Edit NUMERIC
++ VALUENAME "im_days_to_retain"
++ MIN 1 MAX 65535 DEFAULT 30 SPIN 1
++ END PART
++ END POLICY
++
++ POLICY !!Pol_Remove_Deleted_Items
++ EXPLAIN !!Explain_Remove_Deleted_Items
++ VALUENAME remove_deleted_items
++ VALUEON NUMERIC 1
++ END POLICY
++
++ POLICY !!Pol_Allow_Simultaneous_Indexing
++ EXPLAIN !!Explain_Allow_Simultaneous_Indexing
++ VALUENAME simultaneous_indexing
++ VALUEON NUMERIC 1
++ END POLICY
++
++ END CATEGORY
++
++ POLICY !!Pol_TurnOffAdvancedFeatures
++ EXPLAIN !!Explain_TurnOffAdvancedFeatures
++ VALUENAME error_report_on
++ VALUEON NUMERIC 0
++ END POLICY
++
++ POLICY !!Pol_TurnOffImproveGd
++ EXPLAIN !!Explain_TurnOffImproveGd
++ VALUENAME improve_gd
++ VALUEON NUMERIC 0
++ VALUEOFF NUMERIC 1
++ END POLICY
++
++ POLICY !!Pol_NoPersonalizationInfo
++ EXPLAIN !!Explain_NoPersonalizationInfo
++ VALUENAME send_personalization_info
++ VALUEON NUMERIC 0
++ VALUEOFF NUMERIC 1
++ END POLICY
++
++ POLICY !!Pol_OneBoxMode
++ EXPLAIN !!Explain_OneBoxMode
++ VALUENAME onebox_mode
++ VALUEON NUMERIC 0
++ END POLICY
++
++ POLICY !!Pol_EncryptIndex
++ EXPLAIN !!Explain_EncryptIndex
++ VALUENAME encrypt_index
++ VALUEON NUMERIC 1
++ END POLICY
++
++ POLICY !!Pol_Hyper
++ EXPLAIN !!Explain_Hyper
++ VALUENAME hyper_off
++ VALUEON NUMERIC 1
++ END POLICY
++
++ POLICY !!Pol_Display_Mode
++ EXPLAIN !!Explain_Display_Mode
++ PART !!Pol_Display_Mode DROPDOWNLIST
++ VALUENAME display_mode
++ ITEMLIST
++ NAME !!Sidebar VALUE NUMERIC 1
++ NAME !!Deskbar VALUE NUMERIC 8
++ NAME !!FloatingDeskbar VALUE NUMERIC 4
++ NAME !!None VALUE NUMERIC 0
++ END ITEMLIST
++ END PART
++ END POLICY
++
++ END CATEGORY ; Preferences
++
++ CATEGORY !!Cat_Enterprise
++ KEYNAME "Software\Policies\Google\Google Desktop\Enterprise"
++
++ POLICY !!Pol_Autoupdate
++ EXPLAIN !!Explain_Autoupdate
++ VALUENAME autoupdate_host
++ VALUEON ""
++ END POLICY
++
++ POLICY !!Pol_AutoupdateAsSystem
++ EXPLAIN !!Explain_AutoupdateAsSystem
++ VALUENAME autoupdate_impersonate_user
++ VALUEON NUMERIC 0
++ VALUEOFF NUMERIC 1
++ END POLICY
++
++ POLICY !!Pol_EnterpriseTab
++ EXPLAIN !!Explain_EnterpriseTab
++ PART !!EnterpriseTabText EDITTEXT
++ VALUENAME enterprise_tab_text
++ END PART
++ PART !!EnterpriseTabHomepage EDITTEXT
++ VALUENAME enterprise_tab_homepage
++ END PART
++ PART !!EnterpriseTabHomepageQuery CHECKBOX
++ VALUENAME enterprise_tab_homepage_query
++ END PART
++ PART !!EnterpriseTabResults EDITTEXT
++ VALUENAME enterprise_tab_results
++ END PART
++ PART !!EnterpriseTabResultsQuery CHECKBOX
++ VALUENAME enterprise_tab_results_query
++ END PART
++ END POLICY
++
++ POLICY !!Pol_GSAHosts
++ EXPLAIN !!Explain_GSAHosts
++ KEYNAME "Software\Policies\Google\Google Desktop\Enterprise\GSAHosts"
++ PART !!Pol_GSAHosts LISTBOX
++ END PART
++ END POLICY
++
++ POLICY !!Pol_Disallow_Gadgets
++ EXPLAIN !!Explain_Disallow_Gadgets
++ VALUENAME disallow_gadgets
++ VALUEON NUMERIC 1
++ PART !!Disallow_Only_Non_Builtin_Gadgets CHECKBOX DEFCHECKED
++ VALUENAME disallow_only_non_builtin_gadgets
++ VALUEON NUMERIC 1
++ VALUEOFF NUMERIC 0
++ END PART
++ END POLICY
++
++ POLICY !!Pol_Gadget_Whitelist
++ EXPLAIN !!Explain_Gadget_Whitelist
++ KEYNAME "Software\Policies\Google\Google Desktop\Enterprise\gadget_whitelist"
++ PART !!Pol_Gadget_Whitelist LISTBOX
++ END PART
++ END POLICY
++
++ POLICY !!Pol_Gadget_Install_Confirmation_Whitelist
++ EXPLAIN !!Explain_Gadget_Install_Confirmation_Whitelist
++ KEYNAME "Software\Policies\Google\Google Desktop\Enterprise\install_confirmation_whitelist"
++ PART !!Pol_Gadget_Install_Confirmation_Whitelist LISTBOX
++ END PART
++ END POLICY
++
++ POLICY !!Pol_Alternate_User_Data_Dir
++ EXPLAIN !!Explain_Alternate_User_Data_Dir
++ PART !!Pol_Alternate_User_Data_Dir EDITTEXT
++ VALUENAME alternate_user_data_dir
++ END PART
++ END POLICY
++
++ POLICY !!Pol_MaxAllowedOutlookConnections
++ EXPLAIN !!Explain_MaxAllowedOutlookConnections
++ PART !!Pol_MaxAllowedOutlookConnections NUMERIC
++ VALUENAME max_allowed_outlook_connections
++ MIN 1 MAX 65535 DEFAULT 400 SPIN 1
++ END PART
++ END POLICY
++
++ POLICY !!Pol_DisallowSsdService
++ EXPLAIN !!Explain_DisallowSsdService
++ VALUENAME disallow_ssd_service
++ VALUEON NUMERIC 1
++ END POLICY
++
++ POLICY !!Pol_DisallowSsdOutbound
++ EXPLAIN !!Explain_DisallowSsdOutbound
++ VALUENAME disallow_ssd_outbound
++ VALUEON NUMERIC 1
++ END POLICY
++
++ POLICY !!Pol_Disallow_Store_Gadget_Service
++ EXPLAIN !!Explain_Disallow_Store_Gadget_Service
++ VALUENAME disallow_store_gadget_service
++ VALUEON NUMERIC 1
++ END POLICY
++
++ POLICY !!Pol_MaxExchangeIndexingRate
++ EXPLAIN !!Explain_MaxExchangeIndexingRate
++ PART !!Pol_MaxExchangeIndexingRate NUMERIC
++ VALUENAME max_exchange_indexing_rate
++ MIN 1 MAX 1000 DEFAULT 60 SPIN 1
++ END PART
++ END POLICY
++
++ POLICY !!Pol_EnableSafeweb
++ EXPLAIN !!Explain_Safeweb
++ VALUENAME safe_browsing
++ VALUEON NUMERIC 1
++ VALUEOFF NUMERIC 0
++ END POLICY
++
++ END CATEGORY ; Enterprise
++
++ END CATEGORY ; GoogleDesktopSearch
++ END CATEGORY ; Google
++
++;------------------------------------------------------------------------------
++
++[strings]
++Cat_Google="Google"
++Cat_GoogleDesktopSearch="Google Desktop"
++
++;------------------------------------------------------------------------------
++; Preferences
++;------------------------------------------------------------------------------
++Cat_Preferences="Preferences"
++Explain_Preferences="Controls Google Desktop preferences"
++
++Cat_IndexAndCaptureControl="Indexing and Capture Control"
++Explain_IndexAndCaptureControl="Controls what files, web pages, and other content will be indexed by Google Desktop."
++
++Blacklist_Email="Prevent indexing of email"
++Explain_Blacklist_Email="Enabling this policy will prevent Google Desktop from indexing emails.\n\nIf this policy is not configured, the user can choose whether or not to index emails."
++Blacklist_Gmail="Prevent indexing of Gmail"
++Explain_Blacklist_Gmail="Enabling this policy prevents Google Desktop from indexing Gmail messages.\n\nThis policy is in effect only when the policy "Prevent indexing of email" is disabled. When that policy is enabled, all email indexing is disabled, including Gmail indexing.\n\nIf both this policy and "Prevent indexing of email" are disabled or not configured, a user can choose whether or not to index Gmail messages."
++Blacklist_WebHistory="Prevent indexing of web pages"
++Explain_Blacklist_WebHistory="Enabling this policy will prevent Google Desktop from indexing web pages.\n\nIf this policy is disabled or not configured, the user can choose whether or not to index web pages."
++Blacklist_Text="Prevent indexing of text files"
++Explain_Blacklist_Text="Enabling this policy will prevent Google Desktop from indexing text files.\n\nIf this policy is disabled or not configured, the user can choose whether or not to index text files."
++Blacklist_Media="Prevent indexing of media files"
++Explain_Blacklist_Media="Enabling this policy will prevent Google Desktop from indexing media files.\n\nIf this policy is disabled or not configured, the user can choose whether or not to index media files."
++Blacklist_Contact="Prevent indexing of contacts"
++Explain_Blacklist_Contact="Enabling this policy will prevent Google Desktop from indexing contacts.\n\nIf this policy is disabled or not configured, the user can choose whether or not to index contacts."
++Blacklist_Calendar="Prevent indexing of calendar entries"
++Explain_Blacklist_Calendar="Enabling this policy will prevent Google Desktop from indexing calendar entries.\n\nIf this policy is disabled or not configured, the user can choose whether or not to index calendar entries."
++Blacklist_Task="Prevent indexing of tasks"
++Explain_Blacklist_Task="Enabling this policy will prevent Google Desktop from indexing tasks.\n\nIf this policy is disabled or not configured, the user can choose whether or not to index tasks."
++Blacklist_Note="Prevent indexing of notes"
++Explain_Blacklist_Note="Enabling this policy will prevent Google Desktop from indexing notes.\n\nIf this policy is disabled or not configured, the user can choose whether or not to index notes."
++Blacklist_Journal="Prevent indexing of journal entries"
++Explain_Blacklist_Journal="Enabling this policy will prevent Google Desktop from indexing journal entries.\n\nIf this policy is disabled or not configured, the user can choose whether or not to index journal entries."
++Blacklist_Word="Prevent indexing of Word documents"
++Explain_Blacklist_Word="Enabling this policy will prevent Google Desktop from indexing Word documents.\n\nIf this policy is disabled or not configured, the user can choose whether or not to index Word documents."
++Blacklist_Excel="Prevent indexing of Excel documents"
++Explain_Blacklist_Excel="Enabling this policy will prevent Google Desktop from indexing Excel documents.\n\nIf this policy is disabled or not configured, the user can choose whether or not to index Excel documents."
++Blacklist_Powerpoint="Prevent indexing of PowerPoint documents"
++Explain_Blacklist_Powerpoint="Enabling this policy will prevent Google Desktop from indexing PowerPoint documents.\n\nIf this policy is disabled or not configured, the user can choose whether or not to index PowerPoint documents."
++Blacklist_PDF="Prevent indexing of PDF documents"
++Explain_Blacklist_PDF="Enabling this policy will prevent Google Desktop from indexing PDF documents.\n\nIf this policy is disabled or not configured, the user can choose whether or not to index PDF documents."
++Blacklist_ZIP="Prevent indexing of ZIP files"
++Explain_Blacklist_ZIP="Enabling this policy will prevent Google Desktop from indexing ZIP files.\n\nIf this policy is disabled or not configured, the user can choose whether or not to index ZIP files."
++Blacklist_HTTPS="Prevent indexing of secure web pages"
++Explain_Blacklist_HTTPS="Enabling this policy will prevent Google Desktop from indexing secure web pages (pages with HTTPS in the URL).\n\nIf this policy is disabled or not configured, the user can choose whether or not to index secure web pages."
++Blacklist_URI_Contains="Prevent indexing of specific web sites and folders"
++Explain_Blacklist_URI_Contains="This policy allows you to prevent Google Desktop from indexing specific websites or folders. If an item's URL or path name contains any of these specified strings, it will not be indexed. These restrictions will be applied in addition to any websites or folders that the user has specified.\n\nThis policy has no effect when disabled or not configured."
++Blacklist_Chat="Prevent indexing of IM chats"
++Explain_Blacklist_Chat="Enabling this policy will prevent Google Desktop from indexing IM chat conversations.\n\nIf this policy is disabled or not configured, the user can choose whether or not to index IM chat conversations."
++Blacklist_PasswordProtectedOffice="Prevent indexing of password-protected Office documents (Word, Excel)"
++Explain_Blacklist_PasswordProtectedOffice="Enabling this policy will prevent Google Desktop from indexing password-protected office documents.\n\nIf this policy is disabled or not configured, the user can choose whether or not to index password-protected office documents."
++Blacklist_Extensions="Prevent indexing of specific file extensions"
++Explain_Blacklist_Extensions="This policy allows you to prevent Google Desktop from indexing files with specific extensions. Enter a list of file extensions, separated by commas, that you wish to exclude from indexing.\n\nThis policy has no effect when disabled or not configured."
++Pol_Disallow_UserSearchLocations="Disallow adding search locations for indexing"
++Explain_Disallow_UserSearchLocations="Enabling this policy will prevent the user from specifying additional drives or networked folders to be indexed by Google Desktop.\n\nIf this policy is disabled or not configured, users may specify additional drives and networked folders to be indexed."
++Pol_Search_Location_Whitelist="Allow indexing of specific folders"
++Explain_Search_Location_Whitelist="This policy allows you to add additional drives and networked folders to index."
++Search_Locations_Whitelist="Search these locations"
++Email_Retention="Only retain emails that are less than x days old"
++Explain_Email_Retention="This policy allows you to configure Google Desktop to only retain emails that are less than the specified number of days old in the index. Enter the number of days to retain emails for\n\nThis policy has no effect when disabled or not configured."
++Email_Retention_Edit="Number of days to retain emails"
++Webpage_Retention="Only retain webpages that are less than x days old"
++Explain_Webpage_Retention="This policy allows you to configure Google Desktop to only retain webpages that are less than the specified number of days old in the index. Enter the number of days to retain webpages for\n\nThis policy has no effect when disabled or not configured."
++Webpage_Retention_Edit="Number of days to retain webpages"
++File_Retention="Only retain files that are less than x days old"
++Explain_File_Retention="This policy allows you to configure Google Desktop to only retain files that are less than the specified number of days old in the index. Enter the number of days to retain files for\n\nThis policy has no effect when disabled or not configured."
++File_Retention_Edit="Number of days to retain files"
++IM_Retention="Only retain IM that are less than x days old"
++Explain_IM_Retention="This policy allows you to configure Google Desktop to only retain IM that are less than the specified number of days old in the index. Enter the number of days to retain IM for\n\nThis policy has no effect when disabled or not configured."
++IM_Retention_Edit="Number of days to retain IM"
++
++Pol_Remove_Deleted_Items="Remove deleted items from the index."
++Explain_Remove_Deleted_Items="Enabling this policy will remove all deleted items from the index and cache. Any items that are deleted will no longer be searchable."
++
++Pol_Allow_Simultaneous_Indexing="Allow historical indexing for multiple users simultaneously."
++Explain_Allow_Simultaneous_Indexing="Enabling this policy will allow a computer to generate first-time indexes for multiple users simultaneously. \n\nIf this policy is disabled or not configured, historical indexing will happen only for the logged-in user that was connected last; historical indexing for any other logged-in user will happen the next time that other user connects."
++
++Pol_TurnOffAdvancedFeatures="Turn off Advanced Features options"
++Explain_TurnOffAdvancedFeatures="Enabling this policy will prevent Google Desktop from sending Advanced Features data to Google (for either improvements or personalization), and users won't be able to change these options. Enabling this policy also prevents older versions of Google Desktop from sending data.\n\nIf this policy is disabled or not configured and the user has a pre-5.5 version of Google Desktop, the user can choose whether or not to enable sending data to Google. If the user has version 5.5 or later, the 'Turn off Improve Google Desktop option' and 'Do not send personalization info' policies will be used instead."
++
++Pol_TurnOffImproveGd="Turn off Improve Google Desktop option"
++Explain_TurnOffImproveGd="Enabling this policy will prevent Google Desktop from sending improvement data, including crash reports and anonymous usage data, to Google.\n\nIf this policy is disabled, improvement data will be sent to Google and the user won't be able to change the option.\n\nIf this policy is not configured, the user can choose whether or not to enable the Improve Google Desktop option.\n\nNote that this policy applies only to version 5.5 or later and doesn't affect previous versions of Google Desktop.\n\nAlso note that this policy can be overridden by the 'Turn off Advanced Features options' policy."
++
++Pol_NoPersonalizationInfo="Do not send personalization info"
++Explain_NoPersonalizationInfo="Enabling this policy will prevent Google Desktop from displaying personalized content, such as news that reflects the user's past interest in articles. Personalized content is derived from anonymous usage data sent to Google.\n\nIf this policy is disabled, personalized content will be displayed for all users, and users won't be able to disable this feature.\n\nIf this policy is not configured, users can choose whether or not to enable personalization in each gadget that supports this feature.\n\nNote that this policy applies only to version 5.5 or later and doesn't affect previous versions of Google Desktop.\n\nAlso note that this policy can be overridden by the 'Turn off Advanced Features options' policy."
++
++Pol_OneBoxMode="Turn off Google Web Search Integration"
++Explain_OneBoxMode="Enabling this policy will prevent Google Desktop from displaying Desktop Search results in queries to google.com.\n\nIf this policy is disabled or not configured, the user can choose whether or not to include Desktop Search results in queries to google.com."
++
++Pol_EncryptIndex="Encrypt index data"
++Explain_EncryptIndex="Enabling this policy will cause Google Desktop to turn on Windows file encryption for the folder containing the Google Desktop index and related user data the next time it is run.\n\nNote that Windows EFS is only available on NTFS volumes. If the user's data is stored on a FAT volume, this policy will have no effect.\n\nThis policy has no effect when disabled or not configured."
++
++Pol_Hyper="Turn off Quick Find"
++Explain_Hyper="Enabling this policy will cause Google Desktop to turn off Quick Find feature. Quick Find allows you to see results as you type.\n\nIf this policy is disabled or not configured, the user can choose whether or not to enable it."
++
++Pol_Display_Mode="Choose display option"
++Explain_Display_Mode="This policy sets the Google Desktop display option: Sidebar, Deskbar, Floating Deskbar or none.\n\nNote that on 64-bit systems, a setting of Deskbar will be interpreted as Floating Deskbar.\n\nIf this policy is disabled or not configured, the user can choose a display option."
++Sidebar="Sidebar"
++Deskbar="Deskbar"
++FloatingDeskbar="Floating Deskbar"
++None="None"
++
++;------------------------------------------------------------------------------
++; Enterprise
++;------------------------------------------------------------------------------
++Cat_Enterprise="Enterprise Integration"
++Explain_Enterprise="Controls features specific to Enterprise installations of Google Desktop"
++
++Pol_Autoupdate="Block Auto-update"
++Explain_Autoupdate="Enabling this policy prevents Google Desktop from automatically checking for and installing updates from google.com.\n\nIf you enable this policy, you must distribute updates to Google Desktop using Group Policy, SMS, or a similar enterprise software distribution mechanism. You should check http://desktop.google.com/enterprise/ for updates.\n\nIf this policy is disabled or not configured, Google Desktop will periodically check for updates from desktop.google.com."
++
++Pol_AutoupdateAsSystem="Use system proxy settings when auto-updating"
++Explain_AutoupdateAsSystem="Enabling this policy makes Google Desktop use the machine-wide proxy settings (as specified using e.g. proxycfg.exe) when performing autoupdates (if enabled).\n\nIf this policy is disabled or not configured, Google Desktop will use the logged-on user's Internet Explorer proxy settings when checking for auto-updates (if enabled)."
++
++Pol_EnterpriseTab="Enterprise search tab"
++Explain_EnterpriseTab="This policy allows you to add a search tab for your Google Search Appliance to Google Desktop and google.com web pages.\n\nYou must provide the name of the tab, such as "Intranet", as well as URLs for the search homepage and for retrieving search results. Use [DISP_QUERY] in place of the query term for the search results URL.\n\nSee the administrator's guide for more details."
++EnterpriseTabText="Tab name"
++EnterpriseTabHomepage="Search homepage URL"
++EnterpriseTabHomepageQuery="Check if search homepage supports '&&q=<query>'"
++EnterpriseTabResults="Search results URL"
++EnterpriseTabResultsQuery="Check if search results page supports '&&q=<query>'"
++
++Pol_GSAHosts="Google Search Appliances"
++Explain_GSAHosts="This policy allows you to list any Google Search Appliances in your intranet. When properly configured, Google Desktop will insert Google Desktop results into the results of queries on the Google Search Appliance"
++
++Pol_PolicyUnawareClientProhibitedFlag="Prohibit Policy-Unaware versions"
++Explain_PolicyUnawareClientProhibitedFlag="Prohibits installation and execution of versions of Google Desktop that are unaware of group policy.\n\nEnabling this policy will prevent users from installing or running version 1.0 of Google Desktop.\n\nThis policy has no effect when disabled or not configured."
++
++Pol_MinimumAllowedVersion="Minimum allowed version"
++Explain_MinimumAllowedVersion="This policy allows you to prevent installation and/or execution of older versions of Google Desktop by specifying the minimum version you wish to allow. When enabling this policy, you should also enable the "Prohibit Policy-Unaware versions" policy to block versions of Google Desktop that did not support group policy.\n\nThis policy has no effect when disabled or not configured."
++
++Pol_MaximumAllowedVersion="Maximum allowed version"
++Explain_MaximumAllowedVersion="This policy allows you to prevent installation and/or execution of newer versions of Google Desktop by specifying the maximum version you wish to allow.\n\nThis policy has no effect when disabled or not configured."
++
++Pol_Disallow_Gadgets="Disallow gadgets and indexing plug-ins"
++Explain_Disallow_Gadgets="This policy prevents the use of all Google Desktop gadgets and indexing plug-ins. The policy applies to gadgets that are included in the Google Desktop installation package (built-in gadgets), built-in indexing plug-ins (currently only the Lotus Notes plug-in), and to gadgets or indexing plug-ins that a user might want to add later (non-built-in gadgets and indexing plug-ins).\n\nYou can prohibit use of all non-built-in gadgets and indexing plug-ins, but allow use of built-in gadgets and indexing plug-ins. To do so, enable this policy and then select the option "Disallow only non-built-in gadgets and indexing plug-ins.\n\nYou can supersede this policy to allow specified built-in and non-built-in gadgets and indexing plug-ins. To do so, enable this policy and then specify the gadgets and/or indexing plug-ins you want to allow under "Gadget and Plug-in Whitelist.""
++Disallow_Only_Non_Builtin_Gadgets="Disallow only non-built-in gadgets and indexing plug-ins"
++
++Pol_Gadget_Whitelist="Gadget and plug-in whitelist"
++Explain_Gadget_Whitelist="This policy specifies a list of Google Desktop gadgets and indexing plug-ins that you want to allow, as exceptions to the "Disallow gadgets and indexing plug-ins" policy. This policy is valid only when the "Disallow gadgets and indexing plug-ins" policy is enabled.\n\nFor each gadget or indexing plug-in you wish to allow, add the CLSID or PROGID of the gadget or indexing plug-in (see the administrator's guide for more details).\n\nThis policy has no effect when disabled or not configured."
++
++Pol_Gadget_Install_Confirmation_Whitelist="Allow silent installation of gadgets"
++Explain_Gadget_Install_Confirmation_Whitelist="Enabling this policy lets you specify a list of Google Desktop gadgets or indexing plug-ins that can be installed without confirmation from the user.\n\nAdd a gadget or indexing plug-in by placing its class ID (CLSID) or program identifier (PROGID) in the list, surrounded with curly braces ({ }).\n\nThis policy has no effect when disabled or not configured."
++
++Pol_Alternate_User_Data_Dir="Alternate user data directory"
++Explain_Alternate_User_Data_Dir="This policy allows you to specify a directory to be used to store user data for Google Desktop (such as index data and cached documents).\n\nYou may use [USER_NAME] or [DOMAIN_NAME] in the path to specify the current user's name or domain. If [USER_NAME] is not specified, the user name will be appended at the end of the path.\n\nThis policy has no effect when disabled or not configured."
++
++Pol_MaxAllowedOutlookConnections="Maximum allowed Outlook connections"
++Explain_MaxAllowedOutlookConnections="This policy specifies the maximum number of open connections that Google Desktop maintains with the Exchange server. Google Desktop opens a connection for each email folder that it indexes. If insufficient connections are allowed, Google Desktop cannot index all the user email folders.\n\nThe default value is 400. Because users rarely have as many as 400 email folders, Google Desktop rarely reaches the limit.\n\nIf you set this policy's value above 400, you must also configure the number of open connections between Outlook and the Exchange server. By default, approximately 400 connections are allowed. If Google Desktop uses too many of these connections, Outlook might be unable to access email.\n\nThis policy has no effect when disabled or not configured."
++
++Pol_DisallowSsdService="Disallow sharing and receiving of web history and documents across computers"
++Explain_DisallowSsdService="Enabling this policy will prevent Google Desktop from sharing the user's web history and document contents across the user's different Google Desktop installations, and will also prevent it from receiving such shared items from the user's other machines. To allow reception but disallow sharing, use DisallowSsdOutbound.\nThis policy has no effect when disabled or not configured."
++
++Pol_DisallowSsdOutbound="Disallow sharing of web history and documents to user's other computers."
++Explain_DisallowSsdOutbound="Enabling this policy will prevent Google Desktop from sending the user's web history and document contents from this machine to the user's other machines. It does not prevent reception of items from the user's other machines; to disallow both, use DisallowSsdService.\nThis policy has no effect when disabled or not configured."
++
++Pol_Disallow_Store_Gadget_Service="Disallow storage of gadget content and settings."
++Explain_Disallow_Store_Gadget_Service="Enabling this policy will prevent users from storing their gadget content and settings with Google. Users will be unable to access their gadget content and settings from other computers and all content and settings will be lost if Google Desktop is uninstalled."
++
++Pol_MaxExchangeIndexingRate="Maximum allowed Exchange indexing rate"
++Explain_MaxExchangeIndexingRate="This policy allows you to specify the maximum number of emails that are indexed per minute. \n\nThis policy has no effect when disabled or not configured."
++
++Pol_EnableSafeweb="Enable or disable safe browsing"
++Explain_Safeweb="Google Desktop safe browsing informs the user whenever they visit any site which is a suspected forgery site or may harm their computer. Enabling this policy turns on safe browsing; disabling the policy turns it off. \n\nIf this policy is not configured, the user can select whether to turn on safe browsing."
+\ No newline at end of file
+diff --git a/tools/grit/grit/testdata/README.txt b/tools/grit/grit/testdata/README.txt
+new file mode 100644
+index 0000000000..a683b3b9e3
+--- /dev/null
++++ b/tools/grit/grit/testdata/README.txt
+@@ -0,0 +1,87 @@
++Google Desktop for Enterprise
++Copyright (C) 2007 Google Inc.
++All Rights Reserved
++
++---------
++Contents
++---------
++This distribution contains the following files:
++
++GoogleDesktopSetup.msi - Installation and setup program
++GoogleDesktop.adm - Group Policy administrative template file
++AdminGuide.pdf - Google Desktop for Enterprise administrative guide
++
++
++--------------
++Documentation
++--------------
++Full documentation and installation instructions are in the
++administrative guide, and also online at
++http://desktop.google.com/enterprise/adminguide.html.
++
++
++------------------------
++IBM Lotus Notes Plug-In
++------------------------
++The Lotus Notes plug-in is included in the release of Google
++Desktop for Enterprise. The IBM Lotus Notes Plug-in for Google
++Desktop indexes mail, calendar, task, contact and journal
++documents from Notes. Discussion documents including those from
++the discussion and team room templates can also be indexed by
++selecting an option from the preferences. Once indexed, this data
++will be returned in Google Desktop searches. The corresponding
++document can be opened in Lotus Notes from the Google Desktop
++results page.
++
++Install: The plug-in will install automatically during the Google
++Desktop setup process if Lotus Notes is already installed. Lotus
++Notes must not be running in order for the install to occur. The
++Class ID for this plug-in is {8F42BDFB-33E8-427B-AFDC-A04E046D3F07}.
++
++Preferences: Preferences and selection of databases to index are
++set in the 'Google Desktop for Notes' dialog reached through the
++'Actions' menu.
++
++Reindexing: Selecting 'Reindex all databases' will index all the
++documents in each database again.
++
++
++Notes Plug-in Known Issues
++---------------------------
++
++If the 'Google Desktop for Notes' item is not available from the
++Lotus Notes Actions menu, then installation was not successful.
++Installation consists of writing one file, notesgdsplugin.dll, to
++the Notes application directory and a setting to the notes.ini
++configuration file. The most likely cause of an unsuccessful
++installation is that the installer was not able to locate the
++notes.ini file. Installation will complete if the user closes Notes
++and manually adds the following setting to this file on a new line:
++AddinMenus=notesgdsplugin.dll
++
++If the notesgdsplugin.dll file is not in the application directory
++(e.g., C:\Program Files\Lotus\Notes) after Google Desktop
++installation, it is likely that Notes was not installed correctly.
++
++Only local databases can be indexed. If they can be determined,
++the user's local mail file and address book will be included in the
++list automatically. Mail archives and other databases must be
++added with the 'Add' button.
++
++Some users may experience performance issues during the initial
++indexing of a database. The 'Perform the initial index of a
++database only when I'm idle' option will limit the indexing process
++to times when the user is not using the machine. If this does not
++alleviate the problem or the user would like to continually index
++but just do so more slowly or quickly, the GoogleWaitTime notes.ini
++value can be set. Increasing the GoogleWaitTime value will slow
++down the indexing process, and lowering the value will speed it up.
++A value of zero causes the fastest possible indexing. Removing the
++ini parameter altogether returns it to the default (20).
++
++Crashes have been known to occur with certain types of history
++bookmarks. If the Notes client seems to crash randomly, try
++disabling the 'Index note history' option. If it crashes before,
++you can get to the preferences, add the following line to your
++notes.ini file:
++GDSNoIndexHistory=1
+diff --git a/tools/grit/grit/testdata/about.html b/tools/grit/grit/testdata/about.html
+new file mode 100644
+index 0000000000..8e5fad7b2b
+--- /dev/null
++++ b/tools/grit/grit/testdata/about.html
+@@ -0,0 +1,45 @@
++[HEADER]
++<table cellspacing=0 cellPadding=0 width="100%" border=0><tr bgcolor=#3399cc><td align=middle height=1><img height=1 width=1></td></tr></table>
++<table cellspacing=0 cellPadding=1 width="100%" bgcolor=#e8f4f7 border=0><tr><td height=20><font size=+1 color=#000000>&nbsp;<b>[TITLE]</b></font></td></tr></table>
++<br><center><span style="line-height:16pt"><font color=#335cec><B>Google Desktop Search: Search your own computer.</B></font></span></center><br>
++
++<table cellspacing=1 cellpadding=0 width=300 align=center border=0>
++<tr><td nowrap><font size=-1><img style="vertical-align:middle" height=16 src="outlook.gif" width=16>&nbsp;&nbsp;Outlook Email</font></td>
++<td nowrap>&nbsp;</td>
++<td nowrap><font size=-1><img style="vertical-align:middle" height=16 src="netscape.gif" width=16>&nbsp;&nbsp;Netscape Mail / Thunderbird</font></td></tr>
++
++<tr><td nowrap><font size=-1><img style="vertical-align:middle" height=16 src="oe.gif" width=16>&nbsp;&nbsp;Outlook Express</font></td>
++<td nowrap>&nbsp;</td>
++<td nowrap><font size=-1><img style="vertical-align:middle" height=16 src="ff.gif" width=16>&nbsp;&nbsp;Netscape / Firefox / Mozilla</font></td></tr>
++
++<tr><td nowrap><font size=-1><img style="vertical-align:middle" height=16 src="doc.gif" width=16>&nbsp;&nbsp;Word</font></td>
++<td nowrap>&nbsp;</td>
++<td nowrap><font size=-1><img style="vertical-align:middle" height=16 src="pdf.gif" width=16>&nbsp;&nbsp;PDF</font></td></tr>
++
++<tr><td nowrap><font size=-1><img style="vertical-align:middle" height=16 src="xls.gif" width=16>&nbsp;&nbsp;Excel</font></td>
++<td nowrap>&nbsp;</td>
++<td nowrap><font size=-1><img style="vertical-align:middle" height=16 src="mus.gif" width=16>&nbsp;&nbsp;Music</font></td></tr>
++
++<tr><td nowrap><font size=-1><img style="vertical-align:middle" height=16 src="ppt.gif" width=16>&nbsp;&nbsp;PowerPoint</font></td>
++<td nowrap>&nbsp;</td>
++<td nowrap><font size=-1><img style="vertical-align:middle" height=16 src="jpg.gif" width=16>&nbsp;&nbsp;Images</font></td></tr>
++
++<tr><td nowrap><font size=-1><img style="vertical-align:middle" height=16 src="ie.gif" width=16>&nbsp;&nbsp;Internet Explorer</font></td>
++<td nowrap>&nbsp;</td>
++<td nowrap><font size=-1><img style="vertical-align:middle" height=16 src="mov.gif" width=16>&nbsp;&nbsp;Video</font></td></tr>
++
++<tr><td nowrap><font size=-1><img style="vertical-align:middle" height=16 src="aim.gif" width=16>&nbsp;&nbsp;AOL Instant Messenger</font></td>
++<td nowrap>&nbsp;</td>
++<td nowrap><font size=-1><img style="vertical-align:middle" height=16 src="other.gif" width=16>&nbsp;&nbsp;Even more with <a href="http://desktop.google.com/plugins.html">these plug-ins</A></font></td></tr>
++
++<tr><td nowrap><font size=-1><img style="vertical-align:middle" height=16 src="txt.gif" width=16>&nbsp;&nbsp;Text and others</font></td></tr>
++</table>
++<center>
++<p><table cellpadding=1>
++<tr><td><a href="http://desktop.google.com/gettingstarted.html?hl=[LANG_CODE]"><B>Getting Started</B></A> - Learn more about using Google Desktop Search</td></tr>
++<tr><td><a href="http://desktop.google.com/help.html?hl=[LANG_CODE]"><B>Online Help</B></A> - Up-to-date answers to your questions</td></tr>
++<tr><td><a href="[$~PRIVACY~$]"><B>Privacy</B></A> - A few words about privacy and Google Desktop Search</td></tr>
++<tr><td><a href="http://desktop.google.com/uninstall.html?hl=[LANG_CODE]"><B>Uninstall</B></A> - How to uninstall Google Desktop Search</td></tr>
++<tr><td><a href="http://desktop.google.com/feedback.html?hl=[LANG_CODE]"><B>Submit Feedback</B></A> - Send us your comments and ideas</td></tr>
++</table><br><font size=-2>Google Desktop Search [$~BUILDNUMBER~$]</font><br><br>
++[FOOTER]
+\ No newline at end of file
+diff --git a/tools/grit/grit/testdata/android.xml b/tools/grit/grit/testdata/android.xml
+new file mode 100644
+index 0000000000..cc3b141f70
+--- /dev/null
++++ b/tools/grit/grit/testdata/android.xml
+@@ -0,0 +1,24 @@
++<!--
++ Copyright (c) 2012 The Chromium Authors. All rights reserved.
++ Use of this source code is governed by a BSD-style license that can be
++ found in the LICENSE file.
++-->
++
++<resources>
++ <!-- A string with placeholder. -->
++ <string xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2" name="placeholders">
++ Open <xliff:g id="FILENAME" example="internet.html">%s</xliff:g>?
++ </string>
++
++ <!-- A simple string. -->
++ <string name="simple">A simple string.</string>
++
++ <!-- A string with a comment. -->
++ <string name="comment">Contains a <!-- ignore this --> comment. </string>
++
++ <!-- A second simple string. -->
++ <string name="simple2"> Another simple string. </string>
++
++ <!-- A non-translatable string. -->
++ <string name="constant" translatable="false">Do not translate me.</string>
++</resources>
+diff --git a/tools/grit/grit/testdata/bad_browser.html b/tools/grit/grit/testdata/bad_browser.html
+new file mode 100644
+index 0000000000..e8cf34664d
+--- /dev/null
++++ b/tools/grit/grit/testdata/bad_browser.html
+@@ -0,0 +1,16 @@
++<p><b>We're sorry, but we don't seem to be compatible.</b></p>
++<p><font size="-1">Our software suggests that you're using a browser incompatible with Google Desktop Search.
++ Google Desktop Search currently supports the following:</font></p>
++<ul><font size="-1">
++ <li>Microsoft IE 5 and newer (<a href="http://www.microsoft.com/windows/ie/downloads/default.asp">Download</a>)</li>
++ <li>Mozilla (<a href="http://www.mozilla.org/products/mozilla1.x/">Download</a>)</li>
++ <li>Mozilla Firefox (<a href="http://www.mozilla.org/products/firefox/">Download</a>)</li>
++ <li>Netscape 7 and newer (<a href="http://channels.netscape.com/ns/browsers/download.jsp">Download</a>)</li>
++</font></ul>
++
++<p><font size="-1">You may <a href="[REDIR]">click here</a> to use your
++ unsupported browser, though you likely will encounter some areas that don't
++ work as expected. You need to have Javascript enabled, regardless of the
++ browser you use.</font>
++<p><font size="-1">We hope to expand this list in the near future and announce new
++ browsers as they become available.
+diff --git a/tools/grit/grit/testdata/browser.html b/tools/grit/grit/testdata/browser.html
+new file mode 100644
+index 0000000000..45d364d56f
+--- /dev/null
++++ b/tools/grit/grit/testdata/browser.html
+@@ -0,0 +1,42 @@
++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
++<html><head><title>[$~TITLE~$]</title>
++<style>
++BODY { MARGIN-LEFT: 1em; MARGIN-RIGHT: 1em }
++BODY, TD, DIV, A { FONT-FAMILY: arial,sans-serif}
++DIV, TD { COLOR: #000}
++A:link { COLOR: #00c}
++A:visited { COLOR: #551a8b}
++A:active { COLOR: #f00 }
++</style>
++</head>
++
++<body bgcolor="#ffffff" text="#000000" link="#0000cc" vlink="#800080" alink="#ff0000" topmargin=2>
++
++<table cellspacing=2 cellpadding=0 width="99%" border=0>
++<tr>
++ <td width="1%" rowspan=2>[$~IMAGE~$]
++ <td>&nbsp;</td>
++ <td rowspan=2>
++ <table cellspacing=0 cellpadding=0 width="100%" border=0>
++ <tr>
++ <td bgcolor=#3399cc><img height=1 width=1></td>
++ </tr>
++ </table>
++ <table cellspacing=0 cellpadding=0 width="100%" border=0 bgcolor=#efefef>
++ <tr>
++ <td nowrap bgcolor=#E8F4F7><font face=arial,sans-serif color=#000000 size=+1><b>&nbsp;[$~CHROME_TITLE~$]</b></font></td>
++ </tr>
++ </table>
++ </td>
++</tr>
++</table>
++
++<table cellpadding=3 width="94%" align="center" cellspacing=0 border=0>
++<tr valign="middle">
++ <td valign="top">
++ [$~BODY~$]
++ </td>
++ </tr>
++</table>
++[$~FOOTER~$]
++</body></html>
+\ No newline at end of file
+diff --git a/tools/grit/grit/testdata/buildinfo.grd b/tools/grit/grit/testdata/buildinfo.grd
+new file mode 100644
+index 0000000000..80458a8265
+--- /dev/null
++++ b/tools/grit/grit/testdata/buildinfo.grd
+@@ -0,0 +1,46 @@
++<?xml version="1.0" encoding="UTF-8"?> <!-- -*- XML -*- -->
++<grit
++ base_dir="."
++ source_lang_id="en"
++ tc_project="GoogleDesktopWindowsClient"
++ latest_public_release="0"
++ current_release="1"
++ enc_check="möl">
++ <outputs>
++ <output filename="resource.h" type="rc_header" />
++ <output filename="en_generated_resources.rc" type="rc_all" lang="en" />
++ <output filename="sv_generated_resources.rc" type="rc_all" lang="sv" />
++ </outputs>
++ <translations>
++ <file path="substitute.xmb" lang="sv" />
++ </translations>
++ <release seq="1" allow_pseudo="false">
++ <includes>
++ <include type="BITMAP" name="IDB_PR" file="pr.bmp" />
++ <if expr="lang == 'sv'">
++ <include type="BITMAP" name="IDB_PR2" file="pr2.bmp" />
++ </if>
++ </includes>
++ <structures>
++ <structure name="SIDEBAR_LOADING.HTML" encoding="utf-8" file="sidebar_loading.html" type="tr_html" generateid="false" expand_variables="false"/>
++ <structure name="IDS_PLACEHOLDER" file="transl.rc" type="dialog" >
++ <skeleton expr="lang == 'sv'" file="transl1.rc" variant_of_revision="1"/>
++ </structure>
++ <if expr="lang != 'sv'">
++ <structure name="WELCOME_TOAST.HTML" encoding="utf-8" file="welcome_toast.html" type="tr_html" generateid="false" expand_variables="true"/>
++ </if>
++ </structures>
++ <messages first_id="8192">
++ <message name="IDS_COPYRIGHT_GOOGLE_LONG" sub_variable="true" desc="Gadget copyright notice. Needs to be updated every year.">
++ Copyright 2008 Google Inc. All Rights Reserved.
++ </message>
++ <message name="IDS_NEWS_PANEL_COPYRIGHT">
++ Google Desktop News gadget
++[IDS_COPYRIGHT_GOOGLE_LONG]
++View news that is personalized based on the articles you read.
++
++For example, if you read lots of sports news, you'll see more sports articles. If you read technology news less often, you'll see fewer of those articles.
++ </message>
++ </messages>
++ </release>
++</grit>
+diff --git a/tools/grit/grit/testdata/cache_prefix.html b/tools/grit/grit/testdata/cache_prefix.html
+new file mode 100644
+index 0000000000..b1f91dd82b
+--- /dev/null
++++ b/tools/grit/grit/testdata/cache_prefix.html
+@@ -0,0 +1,24 @@
++<head>
++<meta http-equiv="cache-control" content="no-cache">
++<meta http-equiv="pragma" content="no-cache">
++<meta http-equiv="expires" content="-1">
++</head>
++<body onload="[ONLOAD]">
++<table width="100%" border=1><tr><td>
++<table cellspacing=0 cellpadding=10 width="100%" bgcolor=#ffffff border=1 color="#ffffff">
++<tr><td><font face="arial,sans-serif" color=black size=-1>This is one version of <a href="[$~URL~$]">
++<font color="blue">[URL-DISP]</font></a> from your personal <a href="http://desktop.google.com/webcache.html"><font color=blue>cache</font></a>.<br>
++The page may have changed since that time. Click here for the <a href="[$~URL~$]"><font color="blue">current page</font></a>.<br>
++Since this page is stored on your computer, publicly linking to this page will not work.[$~EXTRA~$]<br><br>
++<font size="-2"><i>Google may not be affiliated with the authors of this page nor responsible for its content. This page may be protected by copyright.</i></font>
++</td>
++</tr></table></td></tr></table>
++<style>
++.hl { color:black; background-color:#ffff88}
++</style>
++<script>
++[$~HIGHLIGHT_SCRIPT~$]
++window.onerror=new Function(';');
++</script>
++<hr id=gg_1>
++</body>
+\ No newline at end of file
+diff --git a/tools/grit/grit/testdata/cache_prefix_file.html b/tools/grit/grit/testdata/cache_prefix_file.html
+new file mode 100644
+index 0000000000..f3eb8e0f11
+--- /dev/null
++++ b/tools/grit/grit/testdata/cache_prefix_file.html
+@@ -0,0 +1,25 @@
++<head>
++<meta http-equiv="cache-control" content="no-cache">
++<meta http-equiv="pragma" content="no-cache">
++<meta http-equiv="expires" content="-1"></head>
++<body onload="[ONLOAD]">
++<table width="100%" border=1>
++<tr><td>
++<table cellspacing=0 cellpadding=10 width="100%" bgcolor=#ffffff border=1 color="#ffffff">
++<tr><td><font face=arial,sans-serif color=black size=-1>This is one version of <a href="[$~URL~$]"><font color=blue>[URL-DISP]</font></a>
++from your personal <a href="http://desktop.google.com/filecache.html"><font color=blue>cache</font></a>.<br>
++The file may have changed since that time. Click here for the <a href="[$~URL~$]"><font color=blue>current file</font></a>.<br>
++Since this file is stored on your computer, publicly linking to it will not work.[$~EXTRA~$]<br><br>
++<font size="-2"><i>Google may not be affiliated with the authors of this page nor responsible for its content. This page may be protected by copyright.</i></font>
++</td></tr>
++</table>
++</td></tr></table>
++<style>
++.hl { color:black; background-color:#ffff88}
++</style>
++<script>
++[$~HIGHLIGHT_SCRIPT~$]
++window.onerror=new Function(';');
++</script>
++<hr id=gg_1>
++</body>
+\ No newline at end of file
+diff --git a/tools/grit/grit/testdata/chat_result.html b/tools/grit/grit/testdata/chat_result.html
+new file mode 100644
+index 0000000000..318078bc3d
+--- /dev/null
++++ b/tools/grit/grit/testdata/chat_result.html
+@@ -0,0 +1,24 @@
++[HEADER]
++[CHROME]
++<table border=0 cellpadding=2 cellspacing=2>
++<tr><td>[$~STARTCHAT~$]</td></tr>
++</table>
++<blockquote id=gg_1>
++<table bgcolor=#f0f8ff width=80% cellpadding=5><tr><td>
++<img style="vertical-align:middle;" height=16 src="16x16_chat.gif" width=16> &nbsp; <b>[$~TITLE~$]</b>
++<font size=-1><br><br>Participants: [USERNAME], [BUDDYNAME]<br>
++Date: [TIME]</font></td></tr></table>
++<br id=contents>
++<label>[CONTENTS]</label>
++</blockquote>
++<table border=0 cellpadding=2 cellspacing=2>
++<tr><td>[$~STARTCHAT~$]</td></tr>
++</table>
++<style>
++.hl { color:black; background-color:#ffff88}
++</style>
++<script>
++[$~HIGHLIGHT_SCRIPT~$]
++[ONLOAD]
++</script>
++[FOOTER]
+diff --git a/tools/grit/grit/testdata/chrome/app/generated_resources.grd b/tools/grit/grit/testdata/chrome/app/generated_resources.grd
+new file mode 100644
+index 0000000000..c2efb77fd8
+--- /dev/null
++++ b/tools/grit/grit/testdata/chrome/app/generated_resources.grd
+@@ -0,0 +1,199 @@
++<?xml version="1.0" encoding="UTF-8"?>
++
++<!--
++This file contains definitions of resources that will be translated for each
++locale. The variables is_win, is_macosx, is_linux, and is_posix are available
++for making strings OS specific. Other platform defines such as use_titlecase
++are declared in build/common.gypi.
++-->
++
++<grit base_dir="." latest_public_release="0" current_release="1"
++ source_lang_id="en" enc_check="möl">
++ <outputs>
++ <output filename="grit/generated_resources.h" type="rc_header">
++ <emit emit_type='prepend'></emit>
++ </output>
++ <output filename="generated_resources_am.pak" type="data_package" lang="am" />
++ <output filename="generated_resources_ar.pak" type="data_package" lang="ar" />
++ <if expr="pp_ifdef('use_third_party_translations')">
++ <output filename="generated_resources_ast.pak" type="data_package" lang="ast" />
++ </if>
++ <output filename="generated_resources_bg.pak" type="data_package" lang="bg" />
++ <output filename="generated_resources_bn.pak" type="data_package" lang="bn" />
++ <if expr="pp_ifdef('use_third_party_translations')">
++ <output filename="generated_resources_bs.pak" type="data_package" lang="bs" />
++ </if>
++ <output filename="generated_resources_ca.pak" type="data_package" lang="ca" />
++ <if expr="pp_ifdef('use_third_party_translations')">
++ <output filename="generated_resources_ca@valencia.pak" type="data_package" lang="ca@valencia" />
++ </if>
++ <output filename="generated_resources_cs.pak" type="data_package" lang="cs" />
++ <output filename="generated_resources_da.pak" type="data_package" lang="da" />
++ <output filename="generated_resources_de.pak" type="data_package" lang="de" />
++ <output filename="generated_resources_el.pak" type="data_package" lang="el" />
++ <if expr="pp_ifdef('use_third_party_translations')">
++ <output filename="generated_resources_en-AU.pak" type="data_package" lang="en-AU" />
++ </if>
++ <output filename="generated_resources_en-GB.pak" type="data_package" lang="en-GB" />
++ <output filename="generated_resources_en-US.pak" type="data_package" lang="en" />
++ <if expr="pp_ifdef('use_third_party_translations')">
++ <output filename="generated_resources_eo.pak" type="data_package" lang="eo" />
++ </if>
++ <output filename="generated_resources_es.pak" type="data_package" lang="es" />
++ <output filename="generated_resources_es-419.pak" type="data_package" lang="es-419" />
++ <output filename="generated_resources_et.pak" type="data_package" lang="et" />
++ <if expr="pp_ifdef('use_third_party_translations')">
++ <output filename="generated_resources_eu.pak" type="data_package" lang="eu" />
++ </if>
++ <output filename="generated_resources_fa.pak" type="data_package" lang="fa" />
++ <output filename="generated_resources_fake-bidi.pak" type="data_package" lang="fake-bidi" />
++ <output filename="generated_resources_fi.pak" type="data_package" lang="fi" />
++ <output filename="generated_resources_fil.pak" type="data_package" lang="fil" />
++ <output filename="generated_resources_fr.pak" type="data_package" lang="fr" />
++ <if expr="pp_ifdef('use_third_party_translations')">
++ <output filename="generated_resources_gl.pak" type="data_package" lang="gl" />
++ </if>
++ <output filename="generated_resources_gu.pak" type="data_package" lang="gu" />
++ <output filename="generated_resources_he.pak" type="data_package" lang="he" />
++ <output filename="generated_resources_hi.pak" type="data_package" lang="hi" />
++ <output filename="generated_resources_hr.pak" type="data_package" lang="hr" />
++ <output filename="generated_resources_hu.pak" type="data_package" lang="hu" />
++ <if expr="pp_ifdef('use_third_party_translations')">
++ <output filename="generated_resources_hy.pak" type="data_package" lang="hy" />
++ <output filename="generated_resources_ia.pak" type="data_package" lang="ia" />
++ </if>
++ <output filename="generated_resources_id.pak" type="data_package" lang="id" />
++ <output filename="generated_resources_it.pak" type="data_package" lang="it" />
++ <output filename="generated_resources_ja.pak" type="data_package" lang="ja" />
++ <if expr="pp_ifdef('use_third_party_translations')">
++ <output filename="generated_resources_ka.pak" type="data_package" lang="ka" />
++ </if>
++ <output filename="generated_resources_kn.pak" type="data_package" lang="kn" />
++ <output filename="generated_resources_ko.pak" type="data_package" lang="ko" />
++ <if expr="pp_ifdef('use_third_party_translations')">
++ <output filename="generated_resources_ku.pak" type="data_package" lang="ku" />
++ <output filename="generated_resources_kw.pak" type="data_package" lang="kw" />
++ </if>
++ <output filename="generated_resources_lt.pak" type="data_package" lang="lt" />
++ <output filename="generated_resources_lv.pak" type="data_package" lang="lv" />
++ <output filename="generated_resources_ml.pak" type="data_package" lang="ml" />
++ <output filename="generated_resources_mr.pak" type="data_package" lang="mr" />
++ <if expr="pp_ifdef('use_third_party_translations')">
++ <output filename="generated_resources_ms.pak" type="data_package" lang="ms" />
++ </if>
++ <output filename="generated_resources_nl.pak" type="data_package" lang="nl" />
++ <!-- The translation console uses 'no' for Norwegian Bokmål. It should
++ be 'nb'. -->
++ <output filename="generated_resources_nb.pak" type="data_package" lang="no" />
++ <output filename="generated_resources_pl.pak" type="data_package" lang="pl" />
++ <output filename="generated_resources_pt-BR.pak" type="data_package" lang="pt-BR" />
++ <output filename="generated_resources_pt-PT.pak" type="data_package" lang="pt-PT" />
++ <output filename="generated_resources_ro.pak" type="data_package" lang="ro" />
++ <output filename="generated_resources_ru.pak" type="data_package" lang="ru" />
++ <output filename="generated_resources_sk.pak" type="data_package" lang="sk" />
++ <output filename="generated_resources_sl.pak" type="data_package" lang="sl" />
++ <output filename="generated_resources_sr.pak" type="data_package" lang="sr" />
++ <output filename="generated_resources_sv.pak" type="data_package" lang="sv" />
++ <output filename="generated_resources_sw.pak" type="data_package" lang="sw" />
++ <output filename="generated_resources_ta.pak" type="data_package" lang="ta" />
++ <output filename="generated_resources_te.pak" type="data_package" lang="te" />
++ <output filename="generated_resources_th.pak" type="data_package" lang="th" />
++ <output filename="generated_resources_tr.pak" type="data_package" lang="tr" />
++ <if expr="pp_ifdef('use_third_party_translations')">
++ <output filename="generated_resources_ug.pak" type="data_package" lang="ug" />
++ </if>
++ <output filename="generated_resources_uk.pak" type="data_package" lang="uk" />
++ <output filename="generated_resources_vi.pak" type="data_package" lang="vi" />
++ <output filename="generated_resources_zh-CN.pak" type="data_package" lang="zh-CN" />
++ <output filename="generated_resources_zh-TW.pak" type="data_package" lang="zh-TW" />
++ </outputs>
++ <translations>
++ <file path="resources/generated_resources_am.xtb" lang="am" />
++ <file path="resources/generated_resources_ar.xtb" lang="ar" />
++ <file path="../../third_party/launchpad_translations/generated_resources_ast.xtb" lang="ast" />
++ <file path="resources/generated_resources_bg.xtb" lang="bg" />
++ <file path="resources/generated_resources_bn.xtb" lang="bn" />
++ <file path="../../third_party/launchpad_translations/generated_resources_bs.xtb" lang="bs" />
++ <file path="resources/generated_resources_ca.xtb" lang="ca" />
++ <file path="../../third_party/launchpad_translations/generated_resources_ca-valencia.xtb" lang="ca@valencia" />
++ <file path="resources/generated_resources_cs.xtb" lang="cs" />
++ <file path="resources/generated_resources_da.xtb" lang="da" />
++ <file path="resources/generated_resources_de.xtb" lang="de" />
++ <file path="resources/generated_resources_el.xtb" lang="el" />
++ <file path="../../third_party/launchpad_translations/generated_resources_en-AU.xtb" lang="en-AU" />
++ <file path="resources/generated_resources_en-GB.xtb" lang="en-GB" />
++ <file path="../../third_party/launchpad_translations/generated_resources_eo.xtb" lang="eo" />
++ <file path="resources/generated_resources_es.xtb" lang="es" />
++ <file path="resources/generated_resources_es-419.xtb" lang="es-419" />
++ <file path="resources/generated_resources_et.xtb" lang="et" />
++ <file path="../../third_party/launchpad_translations/generated_resources_eu.xtb" lang="eu" />
++ <file path="resources/generated_resources_fa.xtb" lang="fa" />
++ <file path="resources/generated_resources_fi.xtb" lang="fi" />
++ <file path="resources/generated_resources_fil.xtb" lang="fil" />
++ <file path="resources/generated_resources_fr.xtb" lang="fr" />
++ <file path="../../third_party/launchpad_translations/generated_resources_gl.xtb" lang="gl" />
++ <file path="resources/generated_resources_gu.xtb" lang="gu" />
++ <file path="resources/generated_resources_hi.xtb" lang="hi" />
++ <file path="resources/generated_resources_hr.xtb" lang="hr" />
++ <file path="resources/generated_resources_hu.xtb" lang="hu" />
++ <file path="../../third_party/launchpad_translations/generated_resources_hy.xtb" lang="hy" />
++ <file path="../../third_party/launchpad_translations/generated_resources_ia.xtb" lang="ia" />
++ <file path="resources/generated_resources_id.xtb" lang="id" />
++ <file path="resources/generated_resources_it.xtb" lang="it" />
++ <!-- The translation console uses 'iw' for Hebrew, but we use 'he'. -->
++ <file path="resources/generated_resources_iw.xtb" lang="he" />
++ <file path="resources/generated_resources_ja.xtb" lang="ja" />
++ <file path="../../third_party/launchpad_translations/generated_resources_ka.xtb" lang="ka" />
++ <file path="resources/generated_resources_kn.xtb" lang="kn" />
++ <file path="resources/generated_resources_ko.xtb" lang="ko" />
++ <file path="../../third_party/launchpad_translations/generated_resources_ku.xtb" lang="ku" />
++ <file path="../../third_party/launchpad_translations/generated_resources_kw.xtb" lang="kw" />
++ <file path="resources/generated_resources_lt.xtb" lang="lt" />
++ <file path="resources/generated_resources_lv.xtb" lang="lv" />
++ <file path="resources/generated_resources_ml.xtb" lang="ml" />
++ <file path="resources/generated_resources_mr.xtb" lang="mr" />
++ <file path="../../third_party/launchpad_translations/generated_resources_ms.xtb" lang="ms" />
++ <file path="resources/generated_resources_nl.xtb" lang="nl" />
++ <file path="resources/generated_resources_no.xtb" lang="no" />
++ <file path="resources/generated_resources_pl.xtb" lang="pl" />
++ <file path="resources/generated_resources_pt-BR.xtb" lang="pt-BR" />
++ <file path="resources/generated_resources_pt-PT.xtb" lang="pt-PT" />
++ <file path="resources/generated_resources_ro.xtb" lang="ro" />
++ <file path="resources/generated_resources_ru.xtb" lang="ru" />
++ <file path="resources/generated_resources_sk.xtb" lang="sk" />
++ <file path="resources/generated_resources_sl.xtb" lang="sl" />
++ <file path="resources/generated_resources_sr.xtb" lang="sr" />
++ <file path="resources/generated_resources_sv.xtb" lang="sv" />
++ <file path="resources/generated_resources_sw.xtb" lang="sw" />
++ <file path="resources/generated_resources_ta.xtb" lang="ta" />
++ <file path="resources/generated_resources_te.xtb" lang="te" />
++ <file path="resources/generated_resources_th.xtb" lang="th" />
++ <file path="resources/generated_resources_tr.xtb" lang="tr" />
++ <file path="../../third_party/launchpad_translations/generated_resources_ug.xtb" lang="ug" />
++ <file path="resources/generated_resources_uk.xtb" lang="uk" />
++ <file path="resources/generated_resources_vi.xtb" lang="vi" />
++ <file path="resources/generated_resources_zh-CN.xtb" lang="zh-CN" />
++ <file path="resources/generated_resources_zh-TW.xtb" lang="zh-TW" />
++ </translations>
++ <release seq="1" allow_pseudo="false">
++ <messages fallback_to_english="true">
++ <!-- TODO add all of your "string table" messages here. Remember to
++ change nontranslateable parts of the messages into placeholders (using the
++ <ph> element). You can also use the 'grit add' tool to help you identify
++ nontranslateable parts and create placeholders for them. -->
++ <message name="IDS_BACKGROUND_APP_INSTALLED_BALLOON_TITLE" desc="The title of the balloon that is displayed when a background app is installed">
++ New background app installed
++ </message>
++ <message name="IDS_BACKGROUND_APP_INSTALLED_BALLOON_BODY" desc="The contents of the balloon that is displayed when a background app is installed">
++ <ph name="APP_NAME">$1<ex>Background App</ex></ph> will launch at system startup and continue to run in the background even once you've closed all other <ph name="PRODUCT_NAME">$2<ex>Google Chrome</ex></ph> windows.
++ </message>
++ </messages>
++ <structures fallback_to_english="true">
++ <!-- Make sure these stay in sync with the structures in generated_resources.grd. -->
++ <structure name="IDD_CHROME_FRAME_FIND_DIALOG" file="cf_resources.rc" type="dialog" >
++ </structure>
++ <structure name="IDD_CHROME_FRAME_READY_PROMPT" file="cf_resources.rc" type="dialog" >
++ </structure>
++ </structures>
++ </release>
++</grit>
+diff --git a/tools/grit/grit/testdata/chrome_html.html b/tools/grit/grit/testdata/chrome_html.html
new file mode 100644
-index 0000000000..301449c4ac
+index 0000000000..7f7633c5cf
--- /dev/null
-+++ b/tools/clang/plugins/tests/missing_ctor.txt
++++ b/tools/grit/grit/testdata/chrome_html.html
@@ -0,0 +1,6 @@
-+In file included from missing_ctor.cpp:5:
-+./missing_ctor.h:11:1: warning: [chromium-style] Complex class/struct needs an explicit out-of-line constructor.
-+class MissingCtorsArentOKInHeader {
-+^
-+./missing_ctor.h:11:1: warning: [chromium-style] Complex class/struct needs an explicit out-of-line destructor.
-+2 warnings generated.
-diff --git a/tools/clang/plugins/tests/nested_class_inline_ctor.cpp b/tools/clang/plugins/tests/nested_class_inline_ctor.cpp
++<include src="included_sample.html">
++<style type="text/css">
++#image {
++ content: url('chrome://theme/IDR_SOME_FILE');
++}
++</style>
+diff --git a/tools/grit/grit/testdata/default_100_percent/a.png b/tools/grit/grit/testdata/default_100_percent/a.png
+new file mode 100644
+index 0000000000000000000000000000000000000000..5d5089038ca71172e95db9e7aae1e1fa5cebd505
+GIT binary patch
+literal 159
+zcmeAS@N?(olHy`uVBq!ia0vp^oFL4>0wld=oSO}#(mY)pLnNjq|2Y3)zGGzYPN&L+
+zMSC}CcCfp=Dtxv4%6W%G#Q=|R|L;6pCCLUWO)Z<5eoL%TkDTw=s4X!^d(Qa<2khAN
+zZPy!XToBAic1Ss}vcWiD27B3&`Zj^H6CO>7R1{ToQ;=ggdEYbV=IISvfHpFCy85}S
+Ib4q9e0O9jEh5!Hn
+
+literal 0
+HcmV?d00001
+
+diff --git a/tools/grit/grit/testdata/default_100_percent/b.png b/tools/grit/grit/testdata/default_100_percent/b.png
+new file mode 100644
+index 0000000000..6178079822
+--- /dev/null
++++ b/tools/grit/grit/testdata/default_100_percent/b.png
+@@ -0,0 +1 @@
++b
+diff --git a/tools/grit/grit/testdata/del_footer.html b/tools/grit/grit/testdata/del_footer.html
+new file mode 100644
+index 0000000000..4e19950bfc
+--- /dev/null
++++ b/tools/grit/grit/testdata/del_footer.html
+@@ -0,0 +1,8 @@
++<table cellspacing=0 cellpadding=2 width="100%" border=0>
++<tr bgcolor=#EFEFEF><td><font size=-1>&nbsp;<b>Remove</b> checked results and <b>return to search</b>.</font></td>
++<td align=right><font size=-1><a onClick='checkall(1)' href="#">Check all</a> - <a onClick='checkall(0)' href="#">Uncheck all</a>&nbsp;&nbsp;</font></td>
++<td align=right width=1% nowrap><font size=-1>
++<input onclick=deleting() type=submit value="Remove checked results" name=submit2>
++</font></td></tr></table>
++<center><br><font size=-1>[$~BOTTOMLINE~$] - &copy;2005 Google </font></center>
++</body></html>
+diff --git a/tools/grit/grit/testdata/del_header.html b/tools/grit/grit/testdata/del_header.html
+new file mode 100644
+index 0000000000..72bc6756eb
+--- /dev/null
++++ b/tools/grit/grit/testdata/del_header.html
+@@ -0,0 +1,60 @@
++<body bgcolor="#ffffff" topmargin="2" marginheight="2">
++<table cellSpacing="2" cellPadding="0" width="100%" border="0">
++<form action='[$~DELETE~$]' method="post" name="delform">
++<input name="redir" type="hidden" value="[REDIR]">
++<script>
++<!--
++function deleting() {
++f=document.getElementsByName("del");
++var num = 0;
++if (f.length)
++ for(i=0;i<f.length; i++)
++ if(f[i].checked) num++;
++ if (num == 1) alert("One checked result has been removed");
++ else if (num > 1) alert(num + " checked results have been removed");
++ else alert("No results were checked, so no results have been removed");
++}
++function checkall(v) {
++ f=document.getElementsByName("del");
++ if (f.length)
++ for(i=0;i<f.length; i++)
++ f[i].checked=v;
++}
++//-->
++</script>
++<tr>
++<td vAlign="top" width="1%"><A href='[$~HOMEPAGE~$]'> <img alt="Go to Google Desktop Search" width="150" height="55" src="/logo3.gif" border="0" vspace="12"></A></td>
++<td>&nbsp;</td>
++<td noWrap>
++ <table cellSpacing="0" cellPadding="0" width="100%" border="0">
++ <tr>
++ <td bgColor="#DD0000"><img height="1" alt="" width="1"></td>
++ </tr>
++ </table>
++ <table cellSpacing="0" cellPadding="0" width="100%" border="0">
++ <tr>
++ <td noWrap bgColor="#efefef"><font size="+1"><b>&nbsp;Remove Specific Items</b></font></td>
++ <td noWrap align="right" bgColor="#efefef"><font size="-1"><a href="http://desktop.google.com/remove.html">Help</a>&nbsp;&nbsp;</font></td>
++ </tr>
++ </table>
++</td>
++</tr>
++</table>
++<table cellSpacing="0" cellPadding="2" width="100%" border="0">
++<tr bgColor="#EFEFEF">
++<td><font size="-1">&nbsp;<B>Remove</B> checked results and <B>return to search</B>.</font></td>
++<td align="right"><font size="-1"><a onClick='checkall(1)' href="#">Check all</a> - <a onClick='checkall(0)' href="#">
++Uncheck all</a>&nbsp;&nbsp;</font></td>
++<td align="right" width="1%" nowrap><font size="-1"><input onclick="deleting()" type="submit" value="Remove checked results" name="submit2"></font></td>
++</tr>
++</table>
++<br>
++<table cellspacing="0" cellpadding="2" width="100%" border="0">
++<tr>
++<td colSpan="3" bgcolor="#FFFFFF" style="border:solid; border-width:1px; border-color:#DD0000"><font size="-1">&nbsp;<b>Remove
++checked items from Google Desktop Search. Other copies of the same items will not be
++affected.<br>
++&nbsp;If you view the item again, it will be added back to Google Desktop Search.</b></font></td>
++</tr>
++</table>
++<br>
+\ No newline at end of file
+diff --git a/tools/grit/grit/testdata/deleted.html b/tools/grit/grit/testdata/deleted.html
+new file mode 100644
+index 0000000000..5ae5f355fa
+--- /dev/null
++++ b/tools/grit/grit/testdata/deleted.html
+@@ -0,0 +1,21 @@
++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
++<html><head><title>Database Deleted</title>
++<meta http-equiv="content-type" content="text/html; charset=utf-8">
++<meta http-equiv="cache-control" content="no-cache">
++<meta http-equiv="pragma" content="no-cache">
++<meta http-equiv="expires" content="-1">
++<style>
++BODY,TD,A,P {FONT-FAMILY: arial,sans-serif}
++.q {COLOR: #0000cc}
++</style>
++</head>
++<BODY text=#000000 vLink=#551a8b aLink=#ff0000 link=#0000cc bgColor=#ffffff onload=sf()>
++<center>
++<TABLE cellSpacing=0 cellPadding=0 border=0>
++<tr><td><a href="[$~HOMEPAGE~$]"><IMG border=0 height=110 alt="Google Desktop Search" src="hp_logo.gif" width=276></a>
++</td></tr></table><BR>
++<center>The database has been deleted. Click <a href="[$~HOMEPAGE~$]">here</a> to continue.</center>
++</td></tr>
++</table>
++<br><FONT size=-1>[$~BOTTOMLINE~$]</font></p>
++<p><FONT size=-2>&copy;2005 Google</font></p></center></body></html>
+\ No newline at end of file
+diff --git a/tools/grit/grit/testdata/depfile.grd b/tools/grit/grit/testdata/depfile.grd
+new file mode 100644
+index 0000000000..e2f7191218
+--- /dev/null
++++ b/tools/grit/grit/testdata/depfile.grd
+@@ -0,0 +1,18 @@
++<?xml version="1.0" encoding="UTF-8"?> <!-- -*- XML -*- -->
++<grit
++ base_dir="."
++ latest_public_release="0"
++ current_release="1">
++ <outputs>
++ <output filename="default_100_percent.pak" lang="en" type="data_package" context="default_100_percent" />
++ <output filename="special_100_percent.pak" lang="en" type="data_package" context="special_100_percent" />
++ </outputs>
++ <release seq="1">
++ <structures fallback_to_low_resolution="true">
++ <if expr="False">
++ <part file="grit_part.grdp" />
++ </if>
++ <structure type="chrome_scaled_image" name="IDR_A" file="a.png" />
++ </structures>
++ </release>
++</grit>
+diff --git a/tools/grit/grit/testdata/details.html b/tools/grit/grit/testdata/details.html
+new file mode 100644
+index 0000000000..0ab0e2a90c
+--- /dev/null
++++ b/tools/grit/grit/testdata/details.html
+@@ -0,0 +1,10 @@
++[!]
++title Improve Google Desktop Search by Sending Non-Personal Information
++template
++bottomline
++hp_image
++
++<p><strong>This documentation is not yet available</strong></p>
++<center><br>
++<font size=-1>[$~BOTTOMLINE~$] - &copy;2005 Google </font>
++</center>
+diff --git a/tools/grit/grit/testdata/duplicate-name-input.xml b/tools/grit/grit/testdata/duplicate-name-input.xml
+new file mode 100644
+index 0000000000..cc4d1d65c5
+--- /dev/null
++++ b/tools/grit/grit/testdata/duplicate-name-input.xml
+@@ -0,0 +1,26 @@
++<?xml version="1.0" encoding="UTF-8"?>
++<grit base_dir="." latest_public_release="2" current_release="3" source_lang_id="en-US">
++ <release seq="3">
++ <messages>
++ <message name="IDS_GREETING" desc="Printed to greet the currently logged in user">
++ Hello <ph name="USERNAME">%s<ex>Joi</ex></ph>, how are you doing today?
++ </message>
++ </messages>
++ <structures>
++ <!-- Duplicate name here -->
++ <structure type="version" name="IDS_GREETING" file="rc_files/bla.rc" />
++ </structures>
++ </release>
++ <translations>
++ <file path="figs_nl_translations.xml" />
++ <file path="cjk_translations.xml" />
++ </translations>
++ <outputs>
++ <output filename="resource.h" type="rc_header" />
++ <output filename="resource_en.rc" type="rc_all" lang="en-US" />
++ <output filename="resource_fr.rc" type="rc_all" lang="fr-FR" />
++ <output filename="resource_it.rc" type="rc_translateable" lang="it-IT" />
++ <output filename="resource_zh_cn.rc" type="rc_translateable" lang="zh-CN" />
++ <output filename="nontranslateable.rc" type="rc_nontranslateable" />
++ </outputs>
++</grit>
+diff --git a/tools/grit/grit/testdata/email_result.html b/tools/grit/grit/testdata/email_result.html
+new file mode 100644
+index 0000000000..8bb04b988c
+--- /dev/null
++++ b/tools/grit/grit/testdata/email_result.html
+@@ -0,0 +1,34 @@
++[HEADER]
++[CHROME]
++<table border=0 cellpadding=2 cellspacing=2 width='100%'>
++<tr><td><font size=-1>[CONV]
++<a href='[$~REPLY_URL~$]'>Reply</a> | <a href='[$~REPLYALL_URL~$]'>Reply&nbsp;to&nbsp;All</a>[$~FORWARD_URL~$] | <a href='mailto:'>Compose</a>[$~OUTLOOKVIEW~$]
++</font></td></tr>
++</table>
++<blockquote id=gg_1>
++<table bgcolor=#f0f8ff width=80% cellpadding=5><tr><td>
++<img style="vertical-align:middle;" height=16 src='/email.gif' width=16> &nbsp; <b>[SUBJECT]</b>
++<p><font size=-1>[FROM-DISP]
++[TO-DISP]
++[CC-DISP]
++[BCC-DISP]
++[REPLYTO-DISP]
++[DATE-DISP]
++[VIEW-DISP]
++[$~ATTACH~$]
++</font></td></tr></table>
++<p class=g><span style="width:500;"><font size=-1><label>[MESSAGE]</label></span></p>
++</font>
++</blockquote>
++<table border=0 cellpadding=2 cellspacing=2 width='100%'>
++<tr><td><font size=-1>[CONV]
++<a href='[$~REPLY_URL~$]'>Reply</a> | <a href='[$~REPLYALL_URL~$]'>Reply&nbsp;to&nbsp;All</a>[$~FORWARD_URL~$] | <a href='mailto:'>Compose</a>[$~OUTLOOKVIEW~$]
++</font></td></tr></table>
++<style>
++.hl { color:black; background-color:#ffff88}
++</style>
++<script>
++[$~HIGHLIGHT_SCRIPT~$]
++[ONLOAD]
++</script>
++[FOOTER]
+\ No newline at end of file
+diff --git a/tools/grit/grit/testdata/email_thread.html b/tools/grit/grit/testdata/email_thread.html
+new file mode 100644
+index 0000000000..3c7279b841
+--- /dev/null
++++ b/tools/grit/grit/testdata/email_thread.html
+@@ -0,0 +1,10 @@
++[HEADER]
++[CHROME]
++<blockquote [MAXWIDTH]>
++<b><img src=email.gif style="vertical-align:middle;" width=16 height=16> &nbsp; [SUBJECT]</b><br><br>
++<TABLE cellSpacing=0 cellPadding=3 border=0>
++[CONTENTS]
++</table>
++</blockquote>
++[NEXT_PREV]
++[FOOTER]
+\ No newline at end of file
+diff --git a/tools/grit/grit/testdata/error.html b/tools/grit/grit/testdata/error.html
+new file mode 100644
+index 0000000000..66875a234c
+--- /dev/null
++++ b/tools/grit/grit/testdata/error.html
+@@ -0,0 +1,8 @@
++[HEADER]
++[CHROME]
++<br>
++<blockquote>
++[ERROR]<br><br>
++If you think this is an error, please <a href="http://desktop.google.com/feedback.html?version=[VERSION]">contact us</a>.
++</blockquote>
++[FOOTER]
+\ No newline at end of file
+diff --git a/tools/grit/grit/testdata/explicit_web.html b/tools/grit/grit/testdata/explicit_web.html
+new file mode 100644
+index 0000000000..1424adc617
+--- /dev/null
++++ b/tools/grit/grit/testdata/explicit_web.html
+@@ -0,0 +1,11 @@
++[HEADER]
++<style>
++.image {BORDER: #0000cc 1px solid;}
++.imageh {BORDER: #0000cc 1px solid;}
++</style>
++[WEB_TOP_CHROME]
++[$~STATUS~$]
++[$~MESSAGE~$]
++[WEB_FILES]
++<br>[NEXT_PREV]
++[FOOTER]
+\ No newline at end of file
+diff --git a/tools/grit/grit/testdata/footer.html b/tools/grit/grit/testdata/footer.html
+new file mode 100644
+index 0000000000..3372d6afac
+--- /dev/null
++++ b/tools/grit/grit/testdata/footer.html
+@@ -0,0 +1,14 @@
++<center><br clear=all><br>
++<table cellspacing=0 cellpadding=0 width="100%" border=0><tr bgcolor=#3399CC><td align=middle height=1><img height=1 width=1></td></tr></table>
++<table cellspacing=0 cellpadding=0 width="100%" bgcolor=#e8f4f7 border=0>
++<tr bgcolor=#e8f4f7>
++<td><br>
++<table cellpadding=1 align=center border=0 cellspacing=0 bgcolor=#e8f4f7>
++<form action='[$~SEARCHURL~$]' method=get>
++<tr><td noWrap>[$~BOTTOM~$]</td></tr></form>
++</table><br>
++</td></tr></table>
++<table cellspacing=0 cellpadding=0 width="100%" border=0><tr bgcolor=#3399CC><td align=middle height=1><img height=1 width=1></td></tr></table><br>
++<font size=-1>[$~BOTTOMLINE~$] - &copy;2005 Google </font></center>
++[SCRIPT]
++</body></html>
+diff --git a/tools/grit/grit/testdata/generated_resources_fr.xtb b/tools/grit/grit/testdata/generated_resources_fr.xtb
+new file mode 100644
+index 0000000000..373c40feea
+--- /dev/null
++++ b/tools/grit/grit/testdata/generated_resources_fr.xtb
+@@ -0,0 +1,3079 @@
++<?xml version="1.0" ?>
++<!DOCTYPE translationbundle>
++<translationbundle lang="fr">
++<translation id="1525924600121678168">Salut!</translation>
++<translation id="5335090254790956485">Salut <ph name="USERNAME"/></translation>
++<translation id="6779164083355903755">Supprime&amp;r</translation>
++<translation id="6879617193011158416">Activer la barre de favoris</translation>
++<translation id="8130276680150879341">Déconnexion du réseau privé</translation>
++<translation id="1058418043520174283"><ph name="INDEX"/> sur <ph name="COUNT"/></translation>
++<translation id="4480627574828695486">Déconnecter ce compte...</translation>
++<translation id="7040807039050164757">&amp;Vérifier l'orthographe dans ce champ</translation>
++<translation id="778579833039460630">Aucune donnée reçue.</translation>
++<translation id="1852799913675865625">Une erreur s'est produite lors de la tentative de lecture du fichier : <ph name="ERROR_TEXT"/>.</translation>
++<translation id="3828924085048779000">Le mot de passe multiterme est obligatoire.</translation>
++<translation id="8265562484034134517">Importer les données d'un autre navigateur...</translation>
++<translation id="2709516037105925701">Saisie automatique</translation>
++<translation id="4857138207355690859">API P2P</translation>
++<translation id="250599269244456932">Exécuter automatiquement (recommandé)</translation>
++<translation id="3581034179710640788">Le certificat de sécurité du site a expiré !</translation>
++<translation id="2825758591930162672">Clé publique de l'objet</translation>
++<translation id="8275038454117074363">Importer</translation>
++<translation id="8418445294933751433">Afficher dan&amp;s un onglet</translation>
++<translation id="6985276906761169321">ID :</translation>
++<translation id="859285277496340001">Le certificat n'indique aucun mécanisme permettant de vérifier s'il a été révoqué.</translation>
++<translation id="2010799328026760191">Touches de modification...</translation>
++<translation id="3300394989536077382">Signé par :</translation>
++<translation id="654233263479157500">Utiliser un service Web pour résoudre les erreurs de navigation</translation>
++<translation id="4940047036413029306">Guillemet</translation>
++<translation id="1526811905352917883">Une nouvelle tentative de connexion avec SSL 3.0 a dû être effectuée. Cette opération indique généralement que le serveur utilise un logiciel très ancien et qu'il est susceptible de présenter d'autres problèmes de sécurité.</translation>
++<translation id="1497897566809397301">Autoriser le stockage des données locales (recommandé)</translation>
++<translation id="3275778913554317645">Ouvrir dans une fenêtre</translation>
++<translation id="4553117311324416101">Google pense qu'un logiciel malveillant pourrait être installé sur votre ordinateur si vous continuez. Nous vous conseillons de ne pas continuer, même si vous avez déjà consulté ce site auparavant ou si vous avez confiance en celui-ci. Il se peut qu'il ait été piraté récemment. Réessayez demain ou utilisez un autre site.</translation>
++<translation id="509988127256758334">&amp;Rechercher :</translation>
++<translation id="1420684932347524586">Échec de génération de clé privée RSA aléatoire</translation>
++<translation id="2501173422421700905">Certificat en attente</translation>
++<translation id="2313634973119803790">Technologie réseau :</translation>
++<translation id="2382901536325590843">Le certificat du serveur ne figure pas dans le DNS.</translation>
++<translation id="2833791489321462313">Demander le mot de passe au retour de veille</translation>
++<translation id="3850258314292525915">Désactiver la synchronisation</translation>
++<translation id="2721561274224027017">Base de données indexée</translation>
++<translation id="8208216423136871611">Ne pas enregistrer</translation>
++<translation id="684587995079587263"><ph name="PRODUCT_NAME"/> synchronise vos données avec votre compte Google en toute sécurité. Synchronisez toutes vos données ou personnalisez les types de données synchronisées et les options de chiffrement.</translation>
++<translation id="4405141258442788789">Le délai imparti à l'opération est dépassé.</translation>
++<translation id="5048179823246820836">Nordique</translation>
++<translation id="1763046204212875858">Créer des raccourcis vers des applications</translation>
++<translation id="2105006017282194539">Pas encore chargé</translation>
++<translation id="524759338601046922">Confirmer le nouveau code PIN :</translation>
++<translation id="688547603556380205">L2TP/IPSec + Certificat utilisateur</translation>
++<translation id="777702478322588152">Préfecture</translation>
++<translation id="6562437808764959486">Extraction de l'image de récupération...</translation>
++<translation id="561349411957324076">Terminé</translation>
++<translation id="4764776831041365478">Il se peut que la page Web à l'adresse <ph name="URL"/> soit temporairement inaccessible ou qu'elle ait été déplacée de façon permanente à une autre adresse Web.</translation>
++<translation id="6156863943908443225">Cache des scripts</translation>
++<translation id="4610656722473172270">Barre d'outils Google</translation>
++<translation id="151501797353681931">Importés depuis Safari</translation>
++<translation id="6706684875496318067">Le plug-in <ph name="PLUGIN_NAME"/> n'est pas autorisé.</translation>
++<translation id="586567932979200359">Vous exécutez <ph name="PRODUCT_NAME"/> à partir de son image disque. Si vous l'installez sur votre ordinateur, vous pourrez l'utiliser sans image disque et bénéficierez de mises à jour automatiques.</translation>
++<translation id="3775432569830822555">Certificat du serveur SSL</translation>
++<translation id="1829192082282182671">Z&amp;oom arrière</translation>
++<translation id="6102827823267795198">Indique si la suggestion du moteur de recherche doit être entrée immédiatement via la saisie semi-automatique lorsque la fonctionnalité Recherche instantanée est activée.</translation>
++<translation id="1467071896935429871">Mise à jour du système : <ph name="PERCENT"/> % téléchargés</translation>
++<translation id="7881267037441701396">Les informations d'identification associées au partage de vos imprimantes via <ph name="CLOUD_PRINT_NAME"/> sont arrivées à expiration. Cliquez ici pour saisir à nouveau votre nom d'utilisateur et votre mot de passe.</translation>
++<translation id="816055135686411707">Erreur de définition du paramètre de confiance du certificat</translation>
++<translation id="4714531393479055912"><ph name="PRODUCT_NAME"/> peut maintenant synchroniser vos mots de passe.</translation>
++<translation id="5704565838965461712">Sélectionnez le certificat à présenter pour l'identification :</translation>
++<translation id="2025632980034333559"><ph name="APP_NAME"/> a planté. Cliquez sur cette info-bulle pour actualiser l'extension.</translation>
++<translation id="4059593000330943833">Compatibilité expérimentale avec des méthodes Wi-Fi Extensible Authentication Protocol supplémentaires, telles que EAP-TLS et LEAP.</translation>
++<translation id="6322279351188361895">Échec de lecture de la clé privée</translation>
++<translation id="3781072658385678636">Les plug-ins suivants ont été bloqués sur cette page :</translation>
++<translation id="4428782877951507641">Configuration de la synchronisation</translation>
++<translation id="3648460724479383440">Case d'option cochée</translation>
++<translation id="4654488276758583406">Très petite</translation>
++<translation id="6647228709620733774">URL de révocation de l'autorité de certification Netscape</translation>
++<translation id="546411240573627095">Style de pavé numérique</translation>
++<translation id="7663002797281767775">Active les feuilles de style CSS 3D et la composition graphique haute performance des pages Web via le processeur graphique.</translation>
++<translation id="2972581237482394796">&amp;Rétablir</translation>
++<translation id="5895138241574237353">Redémarrer</translation>
++<translation id="1858072074757584559">La connexion n'est pas compressée.</translation>
++<translation id="528468243742722775">Fin</translation>
++<translation id="1723824996674794290">&amp;Nouvelle fenêtre</translation>
++<translation id="1313405956111467313">Configuration automatique du proxy</translation>
++<translation id="1589055389569595240">Afficher l'orthographe et la grammaire</translation>
++<translation id="4364779374839574930">Aucune imprimante n'a été trouvée. Veuillez en installer une.</translation>
++<translation id="7017587484910029005">Saisissez les caractères visibles dans l'image ci-dessous.</translation>
++<translation id="9013589315497579992">Certificat d'authentification de client SSL incorrect</translation>
++<translation id="8595062045771121608">Le certificat du serveur ou un certificat AC intermédiaire présenté au navigateur a été signé avec un algorithme de signature faible tel que RSA-MD2. D'après des études récentes menées par des informaticiens, les algorithmes de signature seraient plus faibles qu'on ne le pensait jusqu'alors. Aujourd'hui, ils sont très rarement utilisés par les sites Web jugés dignes de confiance. Ce certificat a peut-être été contrefait. Nous vous déconseillons vivement de continuer.</translation>
++<translation id="8666632926482119393">Rechercher le précédent</translation>
++<translation id="7567293639574541773">I&amp;nspecter l'élément</translation>
++<translation id="8392896330146417149">État d'itinérance :</translation>
++<translation id="6813971406343552491">&amp;Non</translation>
++<translation id="36224234498066874">Effacer les données de navigation...</translation>
++<translation id="3384773155383850738">Nombre maximal de suggestions</translation>
++<translation id="8331498498435985864">L'accessibilité est désactivée.</translation>
++<translation id="8530339740589765688">Sélectionner par domaine</translation>
++<translation id="8677212948402625567">Tout réduire...</translation>
++<translation id="7600965453749440009">Ne jamais traduire les pages rédigées en <ph name="LANGUAGE"/> </translation>
++<translation id="3208703785962634733">Non confirmé</translation>
++<translation id="6523841952727744497">Avant de vous connecter, démarrez une session en tant qu'invité afin d'activer le réseau <ph name="NETWORK_ID"/>.</translation>
++<translation id="7450044767321666434">La gravure de l'image est terminée.</translation>
++<translation id="2653266418988778031">Si vous supprimez le certificat d'une autorité de certification, votre navigateur ne fera plus confiance aux certificats émis par cette autorité de certification.</translation>
++<translation id="298068999958468740">Synchronisez toutes les données de cet ordinateur ou sélectionnez celles que vous souhaitez synchroniser.</translation>
++<translation id="5341849548509163798"><ph name="NUMBER_MANY"/> hours ago</translation>
++<translation id="4422428420715047158">Domaine :</translation>
++<translation id="3602290021589620013">Aperçu</translation>
++<translation id="7516602544578411747">Associe chaque fenêtre du navigateur à un profil et ajoute une option de sélection des profils en haut à droite. Chaque profil possède ses propres favoris, extensions, applications, etc.</translation>
++<translation id="7082055294850503883">Ignorer le verrouillage des majuscules et saisir des minuscules par défaut</translation>
++<translation id="1800124151523561876">Aucune parole détectée</translation>
++<translation id="7814266509351532385">Changer de moteur de recherche par défaut</translation>
++<translation id="5376169624176189338">Cliquer pour revenir en arrière, maintenir pour voir l'historique</translation>
++<translation id="6310545596129886942"><ph name="NUMBER_FEW"/> secondes restantes</translation>
++<translation id="9181716872983600413">Unicode</translation>
++<translation id="1383861834909034572">Ouverture à la fin du téléchargement</translation>
++<translation id="5727728807527375859">Les extensions, les applications et les thèmes peuvent endommager votre ordinateur. Voulez-vous vraiment continuer ?</translation>
++<translation id="3857272004253733895">Schéma du pinyin double</translation>
++<translation id="1636842079139032947">Déconnecter ce compte...</translation>
++<translation id="6721972322305477112">&amp;Fichier</translation>
++<translation id="1076818208934827215">Microsoft Internet Explorer</translation>
++<translation id="9056810968620647706">Aucune correspondance trouvée</translation>
++<translation id="1901494098092085382">État de votre commentaire</translation>
++<translation id="2861301611394761800">Mise à jour terminée. Veuillez redémarrer le système.</translation>
++<translation id="2231238007119540260">Lorsque vous supprimez un certificat de serveur, vous rétablissez les contrôles de sécurité habituels du serveur et un certificat valide lui est demandé.</translation>
++<translation id="5463582782056205887">Essayez d'ajouter
++ <ph name="PRODUCT_NAME"/>
++ aux programmes autorisés dans les paramètres de votre pare-feu ou de votre antivirus. S'il
++ est déjà autorisé, tentez de le supprimer de la liste et de l'ajouter à nouveau à
++ la liste des programmes autorisés.</translation>
++<translation id="7624154074265342755">Réseaux sans fil</translation>
++<translation id="3315158641124845231">Masquer <ph name="PRODUCT_NAME"/></translation>
++<translation id="3496213124478423963">Zoom arrière</translation>
++<translation id="2296019197782308739">Méthode EAP :</translation>
++<translation id="42981349822642051">Développer</translation>
++<translation id="4013794286379809233">Veuillez vous connecter</translation>
++<translation id="7693221960936265065">de n'importe quand</translation>
++<translation id="1763138995382273070">Désactiver la validation des formulaires interactifs HTML5</translation>
++<translation id="4920887663447894854">Le suivi de votre position géographique sur cette page a été bloqué pour les sites suivants :</translation>
++<translation id="7690346658388844119">La gravure de l'image a été interrompue.</translation>
++<translation id="8133676275609324831">&amp;Afficher dans le dossier</translation>
++<translation id="645705751491738698">Continuer à bloquer JavaScript</translation>
++<translation id="4780321648949301421">Enregistrer la page sous...</translation>
++<translation id="9154072353677278078">Le serveur <ph name="DOMAIN"/> à l'adresse <ph name="REALM"/> requiert un nom d'utilisateur et un mot de passe.</translation>
++<translation id="2551191967044410069">Exceptions de géolocalisation</translation>
++<translation id="4092066334306401966">13px</translation>
++<translation id="8178665534778830238">Contenu :</translation>
++<translation id="153384433402665971">Le plug-in <ph name="PLUGIN_NAME"/> a été bloqué, car il n'est plus à jour.</translation>
++<translation id="2610260699262139870">Taille ré&amp;elle</translation>
++<translation id="4535734014498033861">Échec de la connexion au serveur proxy.</translation>
++<translation id="558170650521898289">Vérification de pilote matériel Microsoft Windows</translation>
++<translation id="98515147261107953">Paysage</translation>
++<translation id="8974161578568356045">Détecter automatiquement</translation>
++<translation id="1818606096021558659">Page</translation>
++<translation id="5388588172257446328">Nom d'utilisateur :</translation>
++<translation id="1657406563541664238">Nous aider à améliorer <ph name="PRODUCT_NAME"/> en envoyant automatiquement les statistiques d'utilisation et les rapports d'erreur à Google</translation>
++<translation id="7982789257301363584">Réseau</translation>
++<translation id="8528962588711550376">Connexion en cours</translation>
++<translation id="2336228925368920074">Ajouter tous les onglets aux favoris...</translation>
++<translation id="4985312428111449076">Onglets ou fenêtres</translation>
++<translation id="7481475534986701730">Sites récemment consultés</translation>
++<translation id="4260722247480053581">Ouvrir dans une fenêtre de navigation privée</translation>
++<translation id="8503758797520866434">Préférences de saisie automatique...</translation>
++<translation id="2757031529886297178">Compteur d'images par seconde</translation>
++<translation id="6657585470893396449">Mot de passe</translation>
++<translation id="7881483672146086348">Afficher le compte</translation>
++<translation id="1776883657531386793"><ph name="OID"/> : <ph name="INFO"/></translation>
++<translation id="1510030919967934016">Le suivi de votre position géographique a été bloqué pour cette page.</translation>
++<translation id="4640525840053037973">Connexion à l'aide de votre compte Google</translation>
++<translation id="5255315797444241226">Le mot de passe multiterme entré est incorrect.</translation>
++<translation id="6242054993434749861">télécopie : #<ph name="FAX"/></translation>
++<translation id="762917759028004464">Le navigateur par défaut est actuellement <ph name="BROWSER_NAME"/>.</translation>
++<translation id="9213479837033539041"><ph name="NUMBER_MANY"/> secondes restantes</translation>
++<translation id="300544934591011246">Mot de passe précédent</translation>
++<translation id="5078796286268621944">Code PIN incorrect</translation>
++<translation id="989988560359834682">Modifier l'adresse</translation>
++<translation id="8487678622945914333">Zoom avant</translation>
++<translation id="2972557485845626008">Micrologiciel</translation>
++<translation id="735327918767574393">Une erreur s'est produite lors de l'affichage de cette page Web. Pour continuer, actualisez cette page ou ouvrez-en une autre.</translation>
++<translation id="8028060951694135607">Récupération de clé Microsoft</translation>
++<translation id="9187657844611842955">recto verso</translation>
++<translation id="6391832066170725637">Fichier ou répertoire introuvable</translation>
++<translation id="4694445829210540512">Aucun forfait de données <ph name="NETWORK"/> actif</translation>
++<translation id="5494920125229734069">Tout sélectionner</translation>
++<translation id="2857834222104759979">Le fichier manifeste est incorrect.</translation>
++<translation id="7931071620596053769">Les pages suivantes ne répondent plus. Vous pouvez attendre qu'elles soient de nouveau accessibles ou les supprimer.</translation>
++<translation id="1209866192426315618"><ph name="NUMBER_DEFAULT"/> minutes restantes</translation>
++<translation id="7938958445268990899">Le certificat du serveur n'est pas encore valide.</translation>
++<translation id="4569998400745857585">Menu contenant des extensions masquées</translation>
++<translation id="4081383687659939437">Enregistrer les infos</translation>
++<translation id="5786805320574273267">Configuration de l'accès à distance à cet ordinateur.</translation>
++<translation id="1801827354178857021">Point</translation>
++<translation id="2179052183774520942">Ajouter un moteur de recherche</translation>
++<translation id="5498951625591520696">Impossible d'atteindre le serveur.</translation>
++<translation id="2956948609882871496">Importer mes favoris...</translation>
++<translation id="1621207256975573490">Enregistrer le &amp;cadre sous...</translation>
++<translation id="4681260323810445443">Vous n'êtes pas autorisé à accéder à la page Web <ph name="URL"/>. Votre connexion peut être requise.</translation>
++<translation id="2176444992480806665">Envoyer la capture d'écran du dernier onglet actif</translation>
++<translation id="1165039591588034296">Erreur</translation>
++<translation id="2064942105849061141">Utiliser le thème GTK+</translation>
++<translation id="2278562042389100163">Ouvrir une fenêtre du navigateur</translation>
++<translation id="5246282308050205996"><ph name="APP_NAME"/> a planté. Cliquez sur cette info-bulle pour redémarrer l'application.</translation>
++<translation id="9218430445555521422">Définir comme navigateur par défaut</translation>
++<translation id="5027550639139316293">Certificat de courrier électronique</translation>
++<translation id="938582441709398163">Clavier en superposition</translation>
++<translation id="427208986916971462">La connexion est compressée avec <ph name="COMPRESSION"/>.</translation>
++<translation id="4589279373639964403">Exporter mes favoris...</translation>
++<translation id="8876215549894133151">Format :</translation>
++<translation id="5234764350956374838">Ignorer</translation>
++<translation id="40027638859996362">Déplacer un mot</translation>
++<translation id="5463275305984126951">Index de <ph name="LOCATION"/></translation>
++<translation id="5154917547274118687">Mémoire</translation>
++<translation id="1493492096534259649">Impossible d'utiliser cette langue pour corriger l'orthographe.</translation>
++<translation id="6628463337424475685">Recherche <ph name="ENGINE"/></translation>
++<translation id="2502105862509471425">Ajouter une autre carte de paiement...</translation>
++<translation id="4037618776454394829">Envoyer la dernière capture d'écran enregistrée</translation>
++<translation id="8987670145726065238">Ce fichier contient du code malveillant. Voulez-vous vraiment continuer ?</translation>
++<translation id="182729337634291014">Erreur de synchronisation...</translation>
++<translation id="4465830120256509958">Clavier brésilien</translation>
++<translation id="2459861677908225199">Utiliser TLS 1.0</translation>
++<translation id="4792711294155034829">&amp;Signaler un problème...</translation>
++<translation id="5819484510464120153">Créer des raccourci&amp;s vers des applications...</translation>
++<translation id="6845180713465955339">Le certificat &quot;<ph name="CERTIFICATE_NAME"/>&quot; a été émis par :</translation>
++<translation id="7531238562312180404"><ph name="PRODUCT_NAME"/> ne contrôlant pas la façon dont les extensions gèrent vos données personnelles, toutes les extensions sont désactivées dans les fenêtres de navigation privée. Vous pouvez les réactiver individuellement dans le <ph name="BEGIN_LINK"/>gestionnaire des extensions<ph name="END_LINK"/>.</translation>
++<translation id="5667293444945855280">Logiciels malveillants</translation>
++<translation id="3435845180011337502">Mise en page ou mise en forme de la page</translation>
++<translation id="3838186299160040975">Acheter davantage...</translation>
++<translation id="6831043979455480757">Traduire</translation>
++<translation id="3587482841069643663">Tout</translation>
++<translation id="6698381487523150993">Créé :</translation>
++<translation id="4684748086689879921">Annuler l'importation</translation>
++<translation id="9130015405878219958">Le mode indiqué est incorrect.</translation>
++<translation id="6615807189585243369"><ph name="BURNT_AMOUNT"/> copié(s) sur <ph name="TOTAL_SIZE"/></translation>
++<translation id="8518425453349204360">L'accès à distance à cet ordinateur est activé pour <ph name="USER_EMAIL_ADDRESS"/>.</translation>
++<translation id="4950138595962845479">Options...</translation>
++<translation id="4653235815000740718">Un problème est survenu lors de la création du support de récupération du système d'exploitation. Le périphérique de stockage utilisé est introuvable.</translation>
++<translation id="5516565854418269276">Toujours &amp;afficher la barre de favoris</translation>
++<translation id="6426222199977479699">Erreur SSL</translation>
++<translation id="7104784605502674932">Confirmer les préférences de synchronisation</translation>
++<translation id="1788636309517085411">Utiliser les valeurs par défaut</translation>
++<translation id="1661867754829461514">Code secret manquant</translation>
++<translation id="7406714851119047430">L'accès à distance à cet ordinateur est désactivé.</translation>
++<translation id="8589311641140863898">API des extensions expérimentales</translation>
++<translation id="2804922931795102237">Inclure les informations système</translation>
++<translation id="869891660844655955">Date d'expiration</translation>
++<translation id="2178614541317717477">Autorité de certification compromise</translation>
++<translation id="4449935293120761385">À propos de la saisie automatique</translation>
++<translation id="4194570336751258953">Activer la fonction &quot;Taper pour cliquer&quot;</translation>
++<translation id="6066742401428748382">Accès à la page Web refusé</translation>
++<translation id="5111692334209731439">&amp;Gestionnaire de favoris</translation>
++<translation id="8295070100601117548">Erreur serveur</translation>
++<translation id="5661272705528507004">Cette carte SIM est désactivée et ne peut être utilisée. Veuillez demander à votre fournisseur de services de la remplacer.</translation>
++<translation id="443008484043213881">Outils</translation>
++<translation id="2529657954821696995">Clavier néerlandais</translation>
++<translation id="1128128132059598906">EAP-TTLS</translation>
++<translation id="6585234750898046415">Choisissez une image à associer à votre compte. Celle-ci s'affichera sur l'écran de connexion.</translation>
++<translation id="7957054228628133943">Configurer le blocage des fenêtres pop-up...</translation>
++<translation id="179767530217573436">des 4 dernières semaines</translation>
++<translation id="2279770628980885996">Une situation inattendue s'est produite tandis que le serveur tentait de traiter la demande.</translation>
++<translation id="8079135502601738761">Impossible d'afficher certaines parties de ce document PDF. Souhaitez-vous l'ouvrir dans Adobe Reader ?</translation>
++<translation id="9123413579398459698">Proxy FTP</translation>
++<translation id="3887875461425980041">Si vous utilisez la version PPAPI de Flash, exécutez-la dans chaque processus de moteur du rendu plutôt que dans un processus de plug-in dédié.</translation>
++<translation id="8534801226027872331">Cela signifie que le certificat présenté à votre navigateur contient des erreurs et qu'il ne peut pas être compris. Il est possible que les informations sur l'identité du certificat ou que d'autres informations du certificat relatives à la sécurité de la connexion soient incompréhensibles. Ne poursuivez pas.</translation>
++<translation id="3608527593787258723">Activer l'onglet 1</translation>
++<translation id="4497369307931735818">Communication à distance</translation>
++<translation id="3855676282923585394">Importer les favoris et les paramètres...</translation>
++<translation id="1116694919640316211">À propos</translation>
++<translation id="4422347585044846479">Modifier le favori de cette page</translation>
++<translation id="1684638090259711957">Ajouter un format d'exception</translation>
++<translation id="4925481733100738363">Configurer l'accès à distance...</translation>
++<translation id="1880905663253319515">Supprimer le certificat &quot;<ph name="CERTIFICATE_NAME"/>&quot; ?</translation>
++<translation id="8546306075665861288">Cache des images</translation>
++<translation id="5904093760909470684">Configuration du proxy</translation>
++<translation id="2874027208508018603">En l'absence de connexion Wi-Fi, Google Chrome utilise les données 3G.</translation>
++<translation id="4558734465070698159">Appuyez sur <ph name="HOTKEY_NAME"/> pour sélectionner le mode de saisie précédent.</translation>
++<translation id="3348643303702027858">La création du support de récupération du système d'exploitation a été annulée.</translation>
++<translation id="3391060940042023865">Le plug-in suivant est bloqué : <ph name="PLUGIN_NAME"/></translation>
++<translation id="4237016987259239829">Erreur de connexion réseau</translation>
++<translation id="9050666287014529139">Mot de passe multiterme</translation>
++<translation id="5197255632782567636">Internet</translation>
++<translation id="4755860829306298968">Configurer les paramètres de blocage des plug-ins...</translation>
++<translation id="8879284080359814990">Afficher dan&amp;s un onglet</translation>
++<translation id="2786847742169026523">Synchroniser vos mots de passe</translation>
++<translation id="41293960377217290">Le serveur proxy agit comme un intermédiaire entre votre ordinateur et les autres serveurs. Votre configuration système utilise actuellement un proxy, mais
++ <ph name="PRODUCT_NAME"/>
++ ne parvient pas à s'y connecter.</translation>
++<translation id="4520722934040288962">Sélectionner par type d'application</translation>
++<translation id="3873139305050062481">Procéder à l'i&amp;nspection de l'élément</translation>
++<translation id="7445762425076701745">Impossible de valider entièrement l'identité du serveur auquel vous êtes connecté. Le nom utilisé pour cette connexion n'est valide que sur votre réseau et aucune autorité de certification externe ne peut en vérifier la propriété. Certaines autorités de certification délivrent tout de même des certificats pour ces types de nom, par conséquent nous ne sommes pas en mesure de vérifier que vous êtes connecté au site voulu et non à un site malveillant.</translation>
++<translation id="1556537182262721003">Impossible de déplacer le répertoire d'extensions dans le profil.</translation>
++<translation id="5866557323934807206">Supprimer ces paramètres pour les prochaines visites</translation>
++<translation id="6506104645588011859">L'accessibilité est activée.</translation>
++<translation id="5355351445385646029">Appuyer sur la touche Espace pour sélectionner la suggestion</translation>
++<translation id="6978622699095559061">Vos favoris</translation>
++<translation id="6370820475163108109"><ph name="ORGANIZATION_NAME"/> (<ph name="DOMAIN_NAME"/>)</translation>
++<translation id="5453029940327926427">Fermer les onglets</translation>
++<translation id="406070391919917862">Applications en arrière-plan</translation>
++<translation id="8820817407110198400">Favoris</translation>
++<translation id="3214837514330816581">Supprimer les données synchronisées de Google Dashboard</translation>
++<translation id="2580170710466019930">Veuillez patienter pendant que <ph name="PRODUCT_NAME"/> installe les dernières mises à jour système.</translation>
++<translation id="7428061718435085649">Utilisez les touches Maj gauche et droite pour sélectionner les 2e et 3e propositions</translation>
++<translation id="1070066693520972135">WEP</translation>
++<translation id="206683469794463668">Mode Zhuyin complet. La sélection automatique de la suggestion et les options associées sont désactivées ou ignorées.</translation>
++<translation id="5191625995327478163">&amp;Paramètres linguistiques...</translation>
++<translation id="8833054222610756741">CRX-less Web Apps</translation>
++<translation id="4031729365043810780">Connexion au réseau</translation>
++<translation id="3332115613788466465">Reliure bord long</translation>
++<translation id="1985136186573666099"><ph name="PRODUCT_NAME"/> utilise les paramètres proxy du système pour se connecter au réseau.</translation>
++<translation id="6508261954199872201">Application : <ph name="APP_NAME"/></translation>
++<translation id="5585645215698205895">&amp;Descendre</translation>
++<translation id="8366757838691703947">? Toutes les données présentes sur le périphérique seront supprimées.</translation>
++<translation id="6596816719288285829">Adresse IP</translation>
++<translation id="7747704580171477003">Active le nouveau design de la page &quot;Nouvel onglet&quot; (en cours de développement).</translation>
++<translation id="4508265954913339219">Échec de l'activation</translation>
++<translation id="8656768832129462377">Ne pas vérifier</translation>
++<translation id="715487527529576698">Le chinois simplifié est le mode de saisie initial</translation>
++<translation id="1674989413181946727">Paramètres SSL sur tout l'ordinateur :</translation>
++<translation id="8703575177326907206">Votre connexion à <ph name="DOMAIN"/> n'est pas chiffrée.</translation>
++<translation id="8472623782143987204">matériel requis</translation>
++<translation id="4865571580044923428">Gérer les exceptions...</translation>
++<translation id="2526619973349913024">Rechercher des mises à jour</translation>
++<translation id="3874070094967379652">Utiliser un mot de passe multiterme pour chiffrer mes données de synchronisation</translation>
++<translation id="4864369630010738180">Connexion en cours...</translation>
++<translation id="6500116422101723010">Le serveur ne parvient pas à traiter la demande pour le moment. Ce code indique une situation temporaire. Le serveur sera de nouveau opérationnel ultérieurement.</translation>
++<translation id="1644574205037202324">Historique</translation>
++<translation id="1297175357211070620">Destination</translation>
++<translation id="6219983382864672018">Web audio</translation>
++<translation id="479280082949089240">Cookies placés par cette page</translation>
++<translation id="4198861010405014042">Accès partagé</translation>
++<translation id="6204930791202015665">Afficher...</translation>
++<translation id="5941343993301164315">Veuillez vous connecter à <ph name="TOKEN_NAME"/>.</translation>
++<translation id="4417229845571722044">Ajouter un nouvel e-mail</translation>
++<translation id="8049151370369915255">Personnaliser les polices...</translation>
++<translation id="2886862922374605295">Matériel :</translation>
++<translation id="4497097279402334319">Erreur de connexion au réseau.</translation>
++<translation id="5303618139271450299">Cette page Web est introuvable.</translation>
++<translation id="4256316378292851214">En&amp;registrer la vidéo sous...</translation>
++<translation id="3528171143076753409">Le certificat du serveur n'est pas approuvé.</translation>
++<translation id="6518014396551869914">Cop&amp;ier l'image</translation>
++<translation id="3236997602556743698">Sebeol-sik 390</translation>
++<translation id="542155483965056918"><ph name="NUMBER_ZERO"/> mins ago</translation>
++<translation id="289426338439836048">Autre réseau mobile...</translation>
++<translation id="3986287159189541211">Erreur HTTP <ph name="ERROR_NUMBER"/> (<ph name="ERROR_NAME"/>) : <ph name="ERROR_TEXT"/></translation>
++<translation id="3225319735946384299">Signature du code</translation>
++<translation id="3118319026408854581">Aide <ph name="PRODUCT_NAME"/></translation>
++<translation id="2422426094670600218">&lt;sans nom&gt;</translation>
++<translation id="2012766523151663935">Version du micrologiciel :</translation>
++<translation id="4120898696391891645">La page ne se charge pas</translation>
++<translation id="6060685159320643512">Attention, ces fonctionnalités expérimentales peuvent mordre.</translation>
++<translation id="5829990587040054282">Verrouiller l'écran ou éteindre</translation>
++<translation id="7800304661137206267">La connexion est chiffrée au moyen de <ph name="CIPHER"/>, avec <ph name="MAC"/> pour l'authentification des messages et <ph name="KX"/> pour la méthode d'échange de clés.</translation>
++<translation id="7706319470528945664">Clavier portugais</translation>
++<translation id="5584537427775243893">Importation</translation>
++<translation id="9128870381267983090">Connexion au réseau</translation>
++<translation id="4181841719683918333">Langues</translation>
++<translation id="6535131196824081346">Cette erreur peut se produire lors de la connexion à un serveur sécurisé (HTTPS).
++ Elle indique que le serveur tente d'établir une connexion sécurisée, mais
++ que celle-ci ne sera pas du tout sécurisée en raison d'une grave erreur de configuration.
++ <ph name="LINE_BREAK"/> Dans ce cas, une intervention
++ est requise sur le serveur.
++ <ph name="PRODUCT_NAME"/>
++ n'utilise pas de connexion non sécurisée pour protéger la confidentialité
++ de vos données.</translation>
++<translation id="5235889404533735074">La synchronisation de <ph name="PRODUCT_NAME"/> vous permet de partager vos données (favoris, préférences) sur vos ordinateurs en toute simplicité. Pour ce faire, <ph name="PRODUCT_NAME"/> enregistre vos données en ligne via Google lorsque vous vous connectez à votre compte.</translation>
++<translation id="6533668113756472185">Format ou mise en forme de la page</translation>
++<translation id="5640179856859982418">Clavier suisse</translation>
++<translation id="5910363049092958439">En&amp;registrer l'image sous...</translation>
++<translation id="1363055550067308502">Basculer en mode pleine chasse ou demi-chasse</translation>
++<translation id="3108967419958202225">Sélectionner...</translation>
++<translation id="6451650035642342749">Effacer les paramètres d'ouverture automatique</translation>
++<translation id="5948544841277865110">Ajouter un réseau privé</translation>
++<translation id="7121570032414343252"><ph name="NUMBER_TWO"/> secondes</translation>
++<translation id="1378451347523657898">Ne pas envoyer de capture d'écran</translation>
++<translation id="5098629044894065541">Hébreu</translation>
++<translation id="7751559664766943798">Toujours afficher la barre de favoris</translation>
++<translation id="5098647635849512368">Impossible de trouver le chemin d'accès absolu du répertoire à empaqueter.</translation>
++<translation id="780617032715125782">Créer un profil</translation>
++<translation id="933712198907837967">Diners Club</translation>
++<translation id="6380224340023442078">Paramètres de contenu...</translation>
++<translation id="950108145290971791">Activer la recherche instantanée pour accélérer la recherche et la navigation ?</translation>
++<translation id="144136026008224475">Plus d'extensions &gt;&gt;</translation>
++<translation id="5486326529110362464">La valeur d'entrée de la clé privée est obligatoire.</translation>
++<translation id="9039663905644212491">PEAP</translation>
++<translation id="62780591024586043">Fonctionnalités de localisation expérimentales</translation>
++<translation id="8584280235376696778">Ou&amp;vrir la vidéo dans un nouvel onglet</translation>
++<translation id="2845382757467349449">Toujours afficher la barre de favoris</translation>
++<translation id="2516384155283419848">Reliure</translation>
++<translation id="3053013834507634016">Utilisation de la clé du certificat</translation>
++<translation id="4487088045714738411">Clavier belge</translation>
++<translation id="7511635910912978956"><ph name="NUMBER_FEW"/> heures restantes</translation>
++<translation id="2152580633399033274">Afficher toutes les images (recommandé)</translation>
++<translation id="7894567402659809897">Cliquez sur
++ <ph name="BEGIN_BOLD"/>Démarrer<ph name="END_BOLD"/>,
++ puis sur
++ <ph name="BEGIN_BOLD"/>Exécuter<ph name="END_BOLD"/>.
++ Saisissez
++ et cliquez sur
++ <ph name="BEGIN_BOLD"/>OK<ph name="END_BOLD"/>.</translation>
++<translation id="2934952234745269935">Nom de volume</translation>
++<translation id="7960533875494434480">Reliure bord court</translation>
++<translation id="6431347207794742960"><ph name="PRODUCT_NAME"/> va configurer les mises à jour automatiques pour tous les utilisateurs de cet ordinateur.</translation>
++<translation id="4973698491777102067">Effacer les éléments datant :</translation>
++<translation id="6074963268421707432">Interdire à tous les sites d'afficher des notifications sur le Bureau</translation>
++<translation id="8508050303181238566">Appuyez sur <ph name="HOTKEY_NAME"/> pour passer d'un mode de saisie à l'autre.</translation>
++<translation id="6273404661268779365">Ajouter un nouveau fax</translation>
++<translation id="1995173078718234136">Recherche de contenu en cours...</translation>
++<translation id="4735819417216076266">Style d'entrée avec Espace</translation>
++<translation id="2977095037388048586">Vous avez tenté d'accéder à <ph name="DOMAIN"/>, mais, au lieu de cela, vous communiquez actuellement avec un serveur identifié comme <ph name="DOMAIN2"/>. Cela est peut-être dû à un défaut de configuration du serveur ou à quelque chose de plus grave. Un pirate informatique sur votre réseau cherche peut-être à vous faire visiter une version falsifiée de <ph name="DOMAIN3"/>, donc potentiellement préjudiciable. Nous vous déconseillons vivement de continuer.</translation>
++<translation id="220138918934036434">Masquer le bouton</translation>
++<translation id="5374359983950678924">Modifier l'image</translation>
++<translation id="5158548125608505876">Ne pas synchroniser mes mots de passe</translation>
++<translation id="2167276631610992935">JavaScript</translation>
++<translation id="6974306300279582256">Activer les notifications de <ph name="SITE"/></translation>
++<translation id="492914099844938733">Afficher les incompatibilités</translation>
++<translation id="5233638681132016545">Nouvel onglet</translation>
++<translation id="6567688344210276845">Impossible de charger l'icône &quot;<ph name="ICON"/>&quot; d'action de page.</translation>
++<translation id="5210365745912300556">Fermer l'onglet</translation>
++<translation id="8628085465172583869">Nom d'hôte du serveur :</translation>
++<translation id="498765271601821113">Ajouter une carte de paiement</translation>
++<translation id="7694379099184430148"><ph name="FILENAME"/> : type de fichier inconnu</translation>
++<translation id="1992397118740194946">Non défini</translation>
++<translation id="7966826846893205925">Gérer les paramètres de saisie automatique...</translation>
++<translation id="8556732995053816225">Respecter la &amp;casse</translation>
++<translation id="3314070176311241517">Autoriser tous les sites à exécuter JavaScript (recommandé)</translation>
++<translation id="2406911946387278693">Gérer vos périphériques depuis le cloud</translation>
++<translation id="7419631653042041064">Clavier catalan</translation>
++<translation id="5710740561465385694">Me demander lorsqu'un site essaie de stocker des données</translation>
++<translation id="3897092660631435901">Menu</translation>
++<translation id="7024867552176634416">Sélectionnez le périphérique de stockage amovible à utiliser.</translation>
++<translation id="8553075262323480129">La traduction a échoué, car nous n'avons pas pu déterminer la langue de la page.</translation>
++<translation id="5910680277043747137">Vous pouvez créer des profils supplémentaires pour autoriser plusieurs personnes à utiliser et personnaliser Google Chrome.</translation>
++<translation id="4381849418013903196">Deux-points</translation>
++<translation id="1103523840287552314">Toujours traduire en <ph name="LANGUAGE"/></translation>
++<translation id="2263497240924215535">(désactivée)</translation>
++<translation id="2159087636560291862">Cela signifie que le certificat n'a pas été vérifié par un tiers reconnu par votre ordinateur. N'importe qui peut émettre un certificat en se faisant passer pour un autre site Web. Ce certificat doit donc être vérifié par un tiers approuvé. Sans cette vérification, les informations sur l'identité du certificat sont sans intérêt. Par conséquent, il nous est impossible de vérifier que vous communiquez bien avec <ph name="DOMAIN"/> et non avec un pirate informatique ayant émis son propre certificat en prétendant être <ph name="DOMAIN2"/>. Nous vous déconseillons vivement de continuer.</translation>
++<translation id="58625595078799656"><ph name="PRODUCT_NAME"/> requiert que vous cryptiez vos données à l'aide de votre mot de passe Google ou de votre propre mot de passe multiterme.</translation>
++<translation id="8017335670460187064"><ph name="LABEL"/></translation>
++<translation id="6840184929775541289">N'est pas une autorité de certification</translation>
++<translation id="6099520380851856040">Date et heure : <ph name="CRASH_TIME"/></translation>
++<translation id="144518587530125858">Impossible de charger &quot;<ph name="IMAGE_PATH"/>&quot; pour le thème.</translation>
++<translation id="5355097969896547230">Rechercher à nouveau</translation>
++<translation id="7925285046818567682">En attente de <ph name="HOST_NAME"/>...</translation>
++<translation id="2553440850688409052">Masquer ce plug-in</translation>
++<translation id="3280237271814976245">Enregistrer &amp;sous...</translation>
++<translation id="8301162128839682420">Ajouter une langue :</translation>
++<translation id="7658239707568436148">Annuler</translation>
++<translation id="8695825812785969222">Ouvrir une &amp;adresse...</translation>
++<translation id="4538417792467843292">Supprimer le mot</translation>
++<translation id="8412392972487953978">Vous devez saisir deux fois le même mot de passe multiterme.</translation>
++<translation id="9121814364785106365">Ouvrir dans un onglet épinglé</translation>
++<translation id="6996264303975215450">Page Web, tout type de contenu</translation>
++<translation id="3435896845095436175">Activer</translation>
++<translation id="1891668193654680795">Considérer ce certificat comme fiable pour identifier les développeurs de logiciels.</translation>
++<translation id="5078638979202084724">Ajouter tous les onglets aux favoris</translation>
++<translation id="5585118885427931890">Impossible de créer le dossier de favoris.</translation>
++<translation id="2154710561487035718">Copier l'URL</translation>
++<translation id="8163672774605900272">Si vous pensez ne pas avoir à utiliser de serveur proxy, procédez comme suit :
++ <ph name="PLATFORM_TEXT"/></translation>
++<translation id="5510687173983454382">Définir les utilisateurs autorisés à se connecter à un périphérique et autoriser les sessions de navigation en tant qu'invité</translation>
++<translation id="3241680850019875542">Sélectionnez le répertoire racine de l'extension à empaqueter. Pour mettre à jour une extension, sélectionnez également le fichier de clé privée à réutiliser.</translation>
++<translation id="7500424997253660722">Pool restreint :</translation>
++<translation id="657402800789773160">&amp;Rafraîchir cette page</translation>
++<translation id="6163363155248589649">&amp;Normal</translation>
++<translation id="7972714317346275248">PKCS #1 SHA-384 avec chiffrement RSA</translation>
++<translation id="3020990233660977256">Numéro de série : <ph name="SERIAL_NUMBER"/></translation>
++<translation id="8426519927982004547">HTTPS/SSL</translation>
++<translation id="8216781342946147825">Toutes les données de votre ordinateur et des sites Web que vous visitez</translation>
++<translation id="5548207786079516019">Ceci est une installation secondaire de <ph name="PRODUCT_NAME"/> et ce dernier ne peut pas être défini comme navigateur par défaut.</translation>
++<translation id="3984413272403535372">Erreur lors de la signature de l'extension</translation>
++<translation id="8807083958935897582"><ph name="PRODUCT_NAME"/> permet d'effectuer des recherches sur Internet à l'aide du champ polyvalent. Sélectionnez le moteur de recherche à utiliser :</translation>
++<translation id="6629104427484407292">Sécurité : <ph name="SECURITY"/></translation>
++<translation id="9208886416788010685">Adobe Reader n'est pas à jour</translation>
++<translation id="3373604799988099680">Extensions ou applications</translation>
++<translation id="318408932946428277">Effacer les cookies et autres données de site et de plug-in lorsque je ferme le navigateur</translation>
++<translation id="314141447227043789">Téléchargement de l'image terminé.</translation>
++<translation id="8725178340343806893">Favoris</translation>
++<translation id="5177526793333269655">Afficher les vignettes</translation>
++<translation id="8926389886865778422">Ne plus afficher ce message</translation>
++<translation id="6985235333261347343">Agent de récupération de clé Microsoft</translation>
++<translation id="3605499851022050619">Page de diagnostic de navigation sécurisée</translation>
++<translation id="4417271111203525803">Adresse ligne 2</translation>
++<translation id="7617095560120859490">Décrivez-nous le problème recontré. (Champ obligatoire)</translation>
++<translation id="5618333180342767515">Cela peut prendre quelques minutes.</translation>
++<translation id="4307992518367153382">Options de base</translation>
++<translation id="8480417584335382321">Niveau de zoom par défaut :</translation>
++<translation id="3872166400289564527">Stockage externe</translation>
++<translation id="5912378097832178659">Modifi&amp;er les moteurs de recherche...</translation>
++<translation id="8272426682713568063">Cartes de paiement</translation>
++<translation id="3749289110408117711">Nom du fichier</translation>
++<translation id="3173397526570909331">Arrêter la synchronisation</translation>
++<translation id="5538092967727216836">Actualiser le cadre</translation>
++<translation id="4813345808229079766">Connexion</translation>
++<translation id="411666854932687641">Mémoire privée</translation>
++<translation id="119944043368869598">Tout effacer</translation>
++<translation id="3467848195100883852">Activer la correction orthographique automatique</translation>
++<translation id="1336254985736398701">Afficher les &amp;infos sur la page</translation>
++<translation id="7550830279652415241">favoris_<ph name="DATESTAMP"/>.html</translation>
++<translation id="6828153365543658583">Autoriser uniquement les utilisateurs suivants à se connecter :</translation>
++<translation id="1652965563555864525">&amp;Muet</translation>
++<translation id="4200983522494130825">Nouvel ongle&amp;t</translation>
++<translation id="7979036127916589816">Erreur de synchronisation</translation>
++<translation id="1029317248976101138">Zoom</translation>
++<translation id="5455790498993699893"><ph name="ACTIVE_MATCH"/> sur <ph name="TOTAL_MATCHCOUNT"/></translation>
++<translation id="8890069497175260255">Type de clavier</translation>
++<translation id="1202290638211552064">Délai d'expiration atteint au niveau de la passerelle ou du serveur proxy en attente d'une réponse d'un serveur en amont.</translation>
++<translation id="7765158879357617694">Déplacer</translation>
++<translation id="5731751937436428514">Mode de saisie du vietnamien (VIQR)</translation>
++<translation id="8412144371993786373">Ajouter la page actuelle aux favoris</translation>
++<translation id="7615851733760445951">&lt;aucun cookie sélectionné&gt;</translation>
++<translation id="469553822757430352">Le mot de passe de l'application est incorrect.</translation>
++<translation id="2493021387995458222">Sélectionner &quot;un mot à la fois&quot;</translation>
++<translation id="5279600392753459966">Tout bloquer</translation>
++<translation id="6846298663435243399">Chargement en cours…</translation>
++<translation id="7392915005464253525">&amp;Rouvrir la fenêtre fermée</translation>
++<translation id="1144684570366564048">Gérer les exceptions...</translation>
++<translation id="7400418766976504921">URL</translation>
++<translation id="1541725072327856736">Katakana demi-chasse</translation>
++<translation id="7456847797759667638">Ouvrir une adresse</translation>
++<translation id="1388866984373351434">Données de navigation</translation>
++<translation id="3754634516926225076">Code PIN incorrect. Veuillez réessayer.</translation>
++<translation id="7378627244592794276">Non</translation>
++<translation id="2800537048826676660">Utiliser cette langue pour corriger l'orthographe</translation>
++<translation id="68541483639528434">Fermer les autres onglets</translation>
++<translation id="941543339607623937">Clé privée non valide.</translation>
++<translation id="6499058468232888609">Une erreur réseau s'est produite pendant la communication avec le service de gestion des périphériques.</translation>
++<translation id="4433862206975946675">Importer les données d'un autre navigateur...</translation>
++<translation id="4022426551683927403">&amp;Ajouter au dictionnaire</translation>
++<translation id="2897878306272793870">Voulez-vous vraiment ouvrir <ph name="TAB_COUNT"/> onglets ?</translation>
++<translation id="312759608736432009">Fabricant du périphérique :</translation>
++<translation id="362276910939193118">Afficher l'historique complet</translation>
++<translation id="6079696972035130497">Illimité</translation>
++<translation id="4365411729367255048">Clavier Neo2 allemand</translation>
++<translation id="6348657800373377022">Liste déroulante</translation>
++<translation id="8064671687106936412">Clé :</translation>
++<translation id="2218515861914035131">Coller en texte brut</translation>
++<translation id="1725149567830788547">Afficher les &amp;commandes</translation>
++<translation id="3528033729920178817">Cette page suit votre position géographique.</translation>
++<translation id="5518584115117143805">Certificat de chiffrement de courrier électronique</translation>
++<translation id="9203398526606335860">&amp;Profilage activé</translation>
++<translation id="4307281933914537745">En savoir plus sur la récupération du système</translation>
++<translation id="2849936225196189499">Essentielle</translation>
++<translation id="9001035236599590379">Type MIME</translation>
++<translation id="5612754943696799373">Autoriser le téléchargement ?</translation>
++<translation id="6353618411602605519">Clavier croate</translation>
++<translation id="5515810278159179124">Interdire à tous les sites de suivre ma position géographique</translation>
++<translation id="5999606216064768721">Utiliser la barre de titre du système et les bordures de la fenêtre</translation>
++<translation id="904752364881701675">En bas à gauche</translation>
++<translation id="3398951731874728419">Informations sur l'erreur :</translation>
++<translation id="1464570622807304272">Essayez : saisissez &quot;orchidées&quot;, puis appuyez sur Entrée.</translation>
++<translation id="8026684114486203427">Pour utiliser Chrome Web Store, vous devez être connecté à un compte Google.</translation>
++<translation id="8417276187983054885">Configurer <ph name="CLOUD_PRINT_NAME"/></translation>
++<translation id="3056462238804545033">Petit problème... Nous n'avons pas réussi à vous authentifier. Veuillez vérifier vos identifiants de connexion puis réessayer.</translation>
++<translation id="5298420986276701358">Pour gérer à distance la configuration de ce périphérique <ph name="PRODUCT_NAME"/> depuis le cloud, connectez-vous avec votre compte Google Apps.</translation>
++<translation id="2678063897982469759">Réactiver</translation>
++<translation id="1779766957982586368">Fermer la fenêtre</translation>
++<translation id="4850886885716139402">Présentation</translation>
++<translation id="89217462949994770">Vous avez saisi un trop grand nombre de codes PIN incorrects. Veuillez contacter <ph name="CARRIER_ID"/> pour obtenir une nouvelle clé de déverrouillage du code PIN à 8 chiffres.</translation>
++<translation id="5920618722884262402">Bloquer le contenu inapproprié</translation>
++<translation id="5120247199412907247">Configuration avancée</translation>
++<translation id="5922220455727404691">Utiliser SSL 3.0</translation>
++<translation id="1368352873613152012">Règles de confidentialité liées à la navigation sécurisée</translation>
++<translation id="5105859138906591953">Vous devez être connecté à votre compte Google pour importer les favoris de la barre d'outils Google dans Google Chrome. Connectez-vous et relancez l'importation.</translation>
++<translation id="8899851313684471736">Ouvrir le lien dans une nouvelle &amp;fenêtre</translation>
++<translation id="4110342520124362335">Les cookies de <ph name="DOMAIN"/> ont été bloqués.</translation>
++<translation id="3303818374450886607">Copies</translation>
++<translation id="2019718679933488176">&amp;Ouvrir le fichier audio dans un nouvel onglet</translation>
++<translation id="4138267921960073861">Afficher les noms d'utilisateurs et leur photo sur la page de connexion</translation>
++<translation id="7465778193084373987">URL de révocation de certificat Netscape</translation>
++<translation id="5976690834266782200">Ajoute des options de regroupement des onglets dans le menu contextuel des onglets.</translation>
++<translation id="4755240240651974342">Clavier finnois</translation>
++<translation id="7049893973755373474">Vérifiez votre connexion Internet. Redémarrez votre routeur, votre modem
++ ou tout autre périphérique réseau que vous utilisez.</translation>
++<translation id="7421925624202799674">&amp;Afficher le code source de la page</translation>
++<translation id="3940082421246752453">Le serveur ne prend pas en charge la version HTTP utilisée dans la demande.</translation>
++<translation id="661719348160586794">Vos mots de passe enregistrés s'afficheront ici.</translation>
++<translation id="6686490380836145850">Fermer les onglets sur la droite</translation>
++<translation id="5608669887400696928"><ph name="NUMBER_DEFAULT"/> heures</translation>
++<translation id="8844709414456935411"><ph name="PRODUCT_NAME"/>
++ indique qu'un produit ESET intercepte les connexions sécurisées.
++ En général, cela ne constitue pas un problème de sécurité car le
++ logiciel ESET s'exécute souvent sur le même ordinateur. Toutefois, en raison
++ de certaines incompatibilités avec les connexions sécurisées
++ <ph name="PRODUCT_NAME"/>,
++ vous devez configurer les produits ESET de manière à éviter ces
++ interceptions. Cliquez sur le lien En savoir plus pour obtenir des instructions.</translation>
++<translation id="3936877246852975078">Les requêtes adressées au serveur ont été temporairement limitées.</translation>
++<translation id="2600306978737826651">Impossible de télécharger l'image. Gravure annulée.</translation>
++<translation id="609978099044725181">Activer/désactiver le mode Hanja</translation>
++<translation id="1829483195200467833">Effacer les paramètres d'ouverture automatique</translation>
++<translation id="2738771556149464852">Pas après le</translation>
++<translation id="5774515636230743468">Manifeste :</translation>
++<translation id="719464814642662924">Visa</translation>
++<translation id="7474889694310679759">Clavier anglais canadien</translation>
++<translation id="1817871734039893258">Récupération de fichier Microsoft</translation>
++<translation id="2423578206845792524">En&amp;registrer l'image sous...</translation>
++<translation id="6839929833149231406">Zone</translation>
++<translation id="9068931793451030927">Chemin :</translation>
++<translation id="283278805979278081">Prendre la photo</translation>
++<translation id="7320906967354320621">Inactif</translation>
++<translation id="1407050882688520094">Certains de vos certificats enregistrés identifient ces autorités de certification :</translation>
++<translation id="4287689875748136217">Impossible d'afficher la page Web, car le serveur n'a envoyé aucune donnée.</translation>
++<translation id="1634788685286903402">Considérer ce certificat comme fiable pour identifier les utilisateurs de messageries.</translation>
++<translation id="7052402604161570346">Ce type de fichier peut endommager votre ordinateur. Voulez-vous vraiment télécharger <ph name="FILE_NAME"/> ?</translation>
++<translation id="8642489171979176277">Importés depuis la barre d'outils Google</translation>
++<translation id="4142744419835627535">Recherche instantanée et saisie semi-automatique</translation>
++<translation id="4684427112815847243">Tout synchroniser</translation>
++<translation id="1125520545229165057">Dvorak (Hsu)</translation>
++<translation id="8940229512486821554">Exécuter la commande <ph name="EXTENSION_NAME"/> : <ph name="SEARCH_TERMS"/></translation>
++<translation id="2232876851878324699">Le fichier contenait un certificat, qui n'a pas été importé :</translation>
++<translation id="7787129790495067395">Vous utilisez actuellement un mot de passe multiterme. Si vous l'oubliez, vous pouvez réinitialiser la synchronisation afin de supprimer vos données des serveurs Google à l'aide de Google Dashboard.</translation>
++<translation id="2686759344028411998">Impossible de détecter les modules chargés.</translation>
++<translation id="572525680133754531">Cette fonctionnalité affiche une bordure autour des couches de rendu afin de déboguer et d'étudier leur composition.</translation>
++<translation id="2011110593081822050">Processus de traitement Web : <ph name="WORKER_NAME"/></translation>
++<translation id="3294437725009624529">Invité</translation>
++<translation id="350069200438440499">Nom du fichier :</translation>
++<translation id="9058204152876341570">Un élément est manquant.</translation>
++<translation id="8494979374722910010">Échec de la tentative de connexion au serveur.</translation>
++<translation id="7810202088502699111">Des fenêtres pop-up ont été bloquées sur cette page.</translation>
++<translation id="8190698733819146287">Personnaliser les langues et la saisie...</translation>
++<translation id="646727171725540434">Proxy HTTP</translation>
++<translation id="1006316751839332762">Mot de passe multiterme de chiffrement</translation>
++<translation id="8795916974678578410">Nouvelle fenêtre</translation>
++<translation id="2733275712367076659">Certains certificats provenant de ces organisations vous identifient :</translation>
++<translation id="4801512016965057443">Autoriser l'itinérance des données mobiles</translation>
++<translation id="2515586267016047495">Alt</translation>
++<translation id="2046040965693081040">Utiliser les pages actuelles</translation>
++<translation id="3798449238516105146">Version</translation>
++<translation id="5764483294734785780">En&amp;registrer le fichier audio sous...</translation>
++<translation id="5252456968953390977">Itinérance</translation>
++<translation id="8744641000906923997">Romaji</translation>
++<translation id="8507996248087185956"><ph name="NUMBER_DEFAULT"/> minutes</translation>
++<translation id="4845656988780854088">Synchroniser uniquement les paramètres et\ndonnées qui ont changé depuis la dernière connexion\n(requiert votre mot de passe précédent)</translation>
++<translation id="348620396154188443">Autoriser tous les sites à afficher des notifications sur le Bureau</translation>
++<translation id="8214489666383623925">Ouvrir le fichier...</translation>
++<translation id="5376120287135475614">Changer de fenêtre</translation>
++<translation id="5230160809118287008">Chèvres téléportées</translation>
++<translation id="1701567960725324452">Si vous arrêtez la synchronisation, les données stockées sur cet ordinateur et dans votre compte Google demeureront à ces deux emplacements. Toutefois, les nouvelles données ou les modifications apportées au contenu existant ne seront pas synchronisées.</translation>
++<translation id="7761701407923456692">Le certificat du serveur ne correspond pas à l'URL.</translation>
++<translation id="3885155851504623709">Commune</translation>
++<translation id="4910171858422458941">Impossible d'activer les plug-ins désactivés par une stratégie d'entreprise.</translation>
++<translation id="4495419450179050807">Ne pas afficher sur cette page</translation>
++<translation id="4745800796303246012">Méthodes EAP en Wi-Fi expérimentales</translation>
++<translation id="1225544122210684390">Disque dur</translation>
++<translation id="939736085109172342">Nouveau dossier</translation>
++<translation id="4933484234309072027">intégration sur <ph name="URL"/></translation>
++<translation id="5554720593229208774">Autorité de certification de messagerie</translation>
++<translation id="862750493060684461">Cache CSS</translation>
++<translation id="2832519330402637498">En haut à gauche</translation>
++<translation id="6749695674681934117">Saisissez le nom du nouveau dossier.</translation>
++<translation id="6204994989617056362">L'extension de renégociation SSL était introuvable lors de la négociation sécurisée. Avec certains sites, connus pour leur prise en charge de l'extension de renégociation, Google Chrome exige une négociation mieux sécurisée afin de prévenir certaines attaques. L'absence de cette extension suggère que votre connexion a été interceptée et manipulée au cours du transfert.</translation>
++<translation id="6679492495854441399">Petit problème... Une erreur de communication avec le réseau est survenue lors de la tentative d'inscription de ce périphérique. Veuillez vérifier votre connexion réseau et réessayer.</translation>
++<translation id="7789962463072032349">pause</translation>
++<translation id="121827551500866099">Afficher tous les téléchargements...</translation>
++<translation id="1562633988311880769">Connexion à <ph name="CLOUD_PRINT_NAME"/></translation>
++<translation id="5949910269212525572">Impossible de résoudre l'adresse DNS du serveur.</translation>
++<translation id="3115147772012638511">En attente de l'affichage du cache</translation>
++<translation id="257088987046510401">Thèmes</translation>
++<translation id="6771079623344431310">Impossible de se connecter au serveur proxy.</translation>
++<translation id="2200129049109201305">Ignorer la synchronisation des données chiffrées ?</translation>
++<translation id="1426410128494586442">Oui</translation>
++<translation id="6725970970008349185">Nombre de suggestions par page</translation>
++<translation id="6198252989419008588">Modifier le code PIN</translation>
++<translation id="5749483996735055937">Un problème est survenu lors de la copie de l'image de récupération sur le périphérique.</translation>
++<translation id="3520476450377425184"><ph name="NUMBER_MANY"/> jours restants</translation>
++<translation id="7643817847124207232">La connexion Internet a été interrompue.</translation>
++<translation id="932327136139879170">Début</translation>
++<translation id="4764675709794295630">« Précédent</translation>
++<translation id="2560794850818211873">C&amp;opier l'URL de la vidéo</translation>
++<translation id="6042708169578999844">Vos données sur <ph name="WEBSITE_1"/> et <ph name="WEBSITE_2"/></translation>
++<translation id="5302048478445481009">Langue</translation>
++<translation id="5553089923092577885">Mappages des stratégies de certificat</translation>
++<translation id="5600907569873192868"><ph name="NUMBER_MANY"/> minutes restantes</translation>
++<translation id="1519704592140256923">Sélectionner la position</translation>
++<translation id="1275018677838892971">Le site Web à l'adresse <ph name="HOST_NAME"/> contient des éléments provenant de sites signalés comme étant des sites de phishing. Ces derniers incitent les internautes à divulguer leurs informations personnelles en se faisant passer pour des institutions de confiance, telles que des banques.</translation>
++<translation id="702455272205692181"><ph name="EXTENSION_NAME"/></translation>
++<translation id="7170041865419449892">Hors de portée</translation>
++<translation id="908263542783690259">Effacer l'historique de navigation</translation>
++<translation id="7518003948725431193">Aucune page Web trouvée à l'adresse :<ph name="URL"/></translation>
++<translation id="745602119385594863">Nouveau moteur de recherche :</translation>
++<translation id="7484645889979462775">Jamais pour ce site</translation>
++<translation id="8666066831007952346"><ph name="NUMBER_TWO"/> jours restants</translation>
++<translation id="9086455579313502267">Impossible d'accéder au réseau.</translation>
++<translation id="5595485650161345191">Modifier l'adresse</translation>
++<translation id="2374144379568843525">&amp;Masquer le panneau de la vérification orthographique</translation>
++<translation id="2694026874607847549">1 cookie</translation>
++<translation id="4393664266930911253">Activer ces fonctionnalités...</translation>
++<translation id="6390842777729054533"><ph name="NUMBER_ZERO"/> secondes restantes</translation>
++<translation id="3909791450649380159">Cou&amp;per</translation>
++<translation id="2955913368246107853">Fermer la barre de recherche</translation>
++<translation id="5642508497713047">Signataire de la liste de révocation de certificats</translation>
++<translation id="813082847718468539">Afficher des informations à propos du site</translation>
++<translation id="3122464029669770682">UC</translation>
++<translation id="1684861821302948641">Fermer les pages</translation>
++<translation id="6092270396854197260">MSPY</translation>
++<translation id="6802031077390104172"><ph name="USAGE"/> (<ph name="OID"/>)</translation>
++<translation id="4052120076834320548">Très petite</translation>
++<translation id="6623138136890659562">Afficher les réseaux privés dans le menu Réseau pour activer la connexion à un VPN</translation>
++<translation id="8969837897925075737">Vérification de la mise à jour du système...</translation>
++<translation id="3393716657345709557">L'entrée demandée est introuvable dans le cache.</translation>
++<translation id="7241389281993241388">Connectez-vous à <ph name="TOKEN_NAME"/> pour importer le certificat client.</translation>
++<translation id="40334469106837974">Modifier la mise en page</translation>
++<translation id="4804818685124855865">Se déconnecter</translation>
++<translation id="2617919205928008385">Espace insuffisant.</translation>
++<translation id="210445503571712769">Préférences synchronisées</translation>
++<translation id="1608306110678187802">Imp&amp;rimer le cadre...</translation>
++<translation id="7427315641433634153">MSCHAP</translation>
++<translation id="6622980291894852883">Continuer à bloquer les images</translation>
++<translation id="5937837224523037661">Lorsqu'un site utilise des plug-ins :</translation>
++<translation id="4988792151665380515">Échec d'exportation de la clé publique</translation>
++<translation id="6333049849394141510">Choisir les éléments à synchroniser</translation>
++<translation id="446322110108864323">Paramètres de saisie du Pinyin</translation>
++<translation id="4948468046837535074">Ouvrir les pages suivantes :</translation>
++<translation id="5222676887888702881">Déconnexion</translation>
++<translation id="6978121630131642226">Moteurs de recherche</translation>
++<translation id="6839225236531462745">Erreur de suppression de certificat</translation>
++<translation id="6745994589677103306">Ne rien faire</translation>
++<translation id="855081842937141170">Épingler l'onglet</translation>
++<translation id="6263541650532042179">réinitialiser la synchronisation</translation>
++<translation id="6055392876709372977">PKCS #1 SHA-256 avec chiffrement RSA</translation>
++<translation id="7903984238293908205">Katakana</translation>
++<translation id="3781488789734864345">Choisir un réseau mobile</translation>
++<translation id="268053382412112343">&amp;Historique</translation>
++<translation id="2723893843198727027">Mode développeur :</translation>
++<translation id="1722567105086139392">Lien</translation>
++<translation id="2620436844016719705">Système</translation>
++<translation id="5362741141255528695">Sélectionnez le fichier de clé privée.</translation>
++<translation id="5292890015345653304">Insérez une carte SD ou une carte mémoire USB.</translation>
++<translation id="5583370583559395927">Temps restant : <ph name="TIME_REMAINING"/></translation>
++<translation id="8065982201906486420">Cliquez ici pour exécuter le plug-in <ph name="PLUGIN_NAME"/>.</translation>
++<translation id="6219717821796422795">Hanyu</translation>
++<translation id="3725367690636977613">pages</translation>
++<translation id="2688477613306174402">Configuration en cours</translation>
++<translation id="1195447618553298278">Erreur inconnue</translation>
++<translation id="3353284378027041011"><ph name="NUMBER_FEW"/> days ago</translation>
++<translation id="8811462119186190367">La langue utilisée pour Google Chrome est passée de &quot;<ph name="FROM_LOCALE"/>&quot; à &quot;<ph name="TO_LOCALE"/>&quot; après la synchronisation de vos paramètres.</translation>
++<translation id="1087119889335281750">&amp;Aucune suggestion orthographique</translation>
++<translation id="5228309736894624122">Erreur de protocole SSL</translation>
++<translation id="8216170236829567922">Mode de saisie du thaï (clavier Pattachote)</translation>
++<translation id="8464132254133862871">Ce compte utilisateur n'est pas compatible avec ce service.</translation>
++<translation id="6812349420832218321"><ph name="PRODUCT_NAME"/> ne peut pas être exécuté en tant que root.</translation>
++<translation id="5076340679995252485">C&amp;oller</translation>
++<translation id="2904348843321044456">Paramètres de contenu...</translation>
++<translation id="1055216403268280980">Dimensions de l'image</translation>
++<translation id="1784284518684746740">Sélectionner le fichier à enregistrer sous</translation>
++<translation id="7032947513385578725">Disque Flash</translation>
++<translation id="5518442882456325299">Moteur de recherche actuel :</translation>
++<translation id="14171126816530869">L'identité de <ph name="ORGANIZATION"/> situé à <ph name="LOCALITY"/> a été vérifiée par <ph name="ISSUER"/>.</translation>
++<translation id="6263082573641595914">Version de l'autorité de certification Microsoft</translation>
++<translation id="3105917916468784889">Enregistrer une capture d'écran</translation>
++<translation id="1741763547273950878">Page sur <ph name="SITE"/></translation>
++<translation id="1587275751631642843">Console &amp;JavaScript</translation>
++<translation id="8460696843433742627">Réponse reçue incorrecte lors de la tentative de chargement de <ph name="URL"/>.
++ Cela peut être dû à une opération de maintenance ou à une configuration incorrecte sur le serveur.</translation>
++<translation id="297870353673992530">Serveur DNS :</translation>
++<translation id="3222066309010235055">Pré-rendu : <ph name="PRERENDER_CONTENTS_NAME"/></translation>
++<translation id="6410063390789552572">Impossible d'accéder à la bibliothèque réseau.</translation>
++<translation id="6880587130513028875">Des images ont été bloquées sur cette page.</translation>
++<translation id="851263357009351303">Toujours autoriser <ph name="HOST"/> à afficher les images</translation>
++<translation id="3511307672085573050">Copier l'adr&amp;esse du lien</translation>
++<translation id="1134009406053225289">Ouvrir dans une fenêtre de navigation privée</translation>
++<translation id="6655190889273724601">Mode développeur</translation>
++<translation id="1071917609930274619">Chiffrement des données</translation>
++<translation id="3473105180351527598">Activer la protection contre le phishing et les logiciels malveillants</translation>
++<translation id="6151323131516309312">Appuyez sur <ph name="SEARCH_KEY"/> pour rechercher sur <ph name="SITE_NAME"/></translation>
++<translation id="3753317529742723206">Voulez-vous utiliser <ph name="HANDLER_TITLE"/> (<ph name="HANDLER_HOSTNAME"/>) au lieu de <ph name="REPLACED_HANDLER_TITLE"/> pour gérer les liens <ph name="PROTOCOL"/>:// à partir de maintenant ?</translation>
++<translation id="6216679966696797604">Démarrer une session en tant qu'invité</translation>
++<translation id="5456397824015721611">Nombre maximal de caractères chinois dans la mémoire tampon de pré-édition, notamment les entrées de symboles Zhuyin</translation>
++<translation id="2055443983279698110">Barre de menus GNOME expérimentale disponible</translation>
++<translation id="2342959293776168129">Effacer l'historique des téléchargements</translation>
++<translation id="2503522102815150840">Navigateur bloqué...</translation>
++<translation id="7201354769043018523">Parenthèse drte</translation>
++<translation id="425878420164891689">Calcul du temps de chargement</translation>
++<translation id="508794495705880051">Ajouter une carte de paiement...</translation>
++<translation id="1425975335069981043">Itinérance :</translation>
++<translation id="1272079795634619415">Arrêter</translation>
++<translation id="5442787703230926158">Erreur de synchronisation...</translation>
++<translation id="2462724976360937186">ID de clé de l'autorité de certification</translation>
++<translation id="6786747875388722282">Extensions</translation>
++<translation id="3944384147860595744">Imprimez où que vous soyez.</translation>
++<translation id="2570648609346224037">Un problème est survenu lors du téléchargement de l'image de récupération.</translation>
++<translation id="4306718255138772973">Cloud Print Proxy</translation>
++<translation id="9053965862400494292">Une erreur s'est produite lors de la configuration de la synchronisation.</translation>
++<translation id="8596540852772265699">Fichiers personnalisés</translation>
++<translation id="7017354871202642555">Impossible de définir le mode une fois la fenêtre créée.</translation>
++<translation id="3101709781009526431">Date et heure</translation>
++<translation id="69375245706918574">Personnaliser les préférences de synchronisation</translation>
++<translation id="833853299050699606">Aucune information disponible sur le forfait</translation>
++<translation id="1737968601308870607">Signaler un problème</translation>
++<translation id="4571852245489094179">Importer mes favoris et paramètres</translation>
++<translation id="99648783926443049">Sélectionnez le <ph name="BEGIN_BOLD"/>menu clé à molette &gt; Paramètres &gt; Options avancées &gt; Modifier les paramètres du proxy<ph name="END_BOLD"/> et vérifiez que vos paramètres sont définis sur &quot;sans proxy&quot; ou &quot;direct&quot;.</translation>
++<translation id="4421917670248123270">Fermer et annuler les téléchargements</translation>
++<translation id="5605623530403479164">Autres moteurs de recherche</translation>
++<translation id="8887243200615092733"><ph name="PRODUCT_NAME"/> peut maintenant synchroniser vos mots de passe. Pour protéger vos données, vous devez confirmer les informations relatives à votre compte.</translation>
++<translation id="4740663705480958372">Cette fonctionnalité active les API P2P Pepper et P2P JavaScript. L'API est en cours de développement et n'est pas encore opérationnelle.</translation>
++<translation id="5710435578057952990">L'identité de ce site Web n'a pas été vérifiée.</translation>
++<translation id="1421046588786494306">Sessions à l'étranger</translation>
++<translation id="1661245713600520330">Cette page répertorie tous les modules chargés dans le processus principal et les modules enregistrés de manière à être chargés ultérieurement.</translation>
++<translation id="5451646087589576080">Afficher les &amp;infos sur le cadre</translation>
++<translation id="3368922792935385530">Connecté</translation>
++<translation id="3498309188699715599">Paramètres d'entrée en Chewing</translation>
++<translation id="8486154204771389705">Conserver sur cette page</translation>
++<translation id="3866443872548686097">Votre support de récupération est prêt. Vous pouvez le retirer du système.</translation>
++<translation id="6824564591481349393">Copi&amp;er l'adresse e-mail</translation>
++<translation id="907148966137935206">Interdire à tous les sites d'afficher des fenêtres pop-up (recommandé)</translation>
++<translation id="5184063094292164363">Console &amp;JavaScript</translation>
++<translation id="333371639341676808">Empêcher cette page de générer des boîtes de dialogue supplémentaires</translation>
++<translation id="7632380866023782514">En haut à droite</translation>
++<translation id="4925520021222027859">Entrez le mot de passe associé à votre application :</translation>
++<translation id="3494768541638400973">Mode de saisie Google du japonais (pour clavier japonais)</translation>
++<translation id="5844183150118566785"><ph name="PRODUCT_NAME"/> est à jour (<ph name="VERSION"/>)</translation>
++<translation id="3118046075435288765">Le serveur a mis fin à la connexion de manière inattendue.</translation>
++<translation id="8041140688818013446">Il est possible que le serveur hébergeant la page Web soit surchargé ou ait rencontré une erreur. Pour éviter de générer
++ trop de trafic et d'aggraver la situation,
++ <ph name="PRODUCT_NAME"/> a temporairement
++ bloqué l'acceptation des requêtes adressées au serveur.
++ <ph name="LINE_BREAK"/>
++ Si vous pensez que ce comportement n'est pas souhaitable, (par exemple, dans le cas où vous déboguez votre propre site Web), vous pouvez
++ consulter la page <ph name="NET_INTERNALS_PAGE"/>,
++ sur laquelle vous pourrez trouver plus d'informations ou désactiver cette fonctionnalité.</translation>
++<translation id="1725068750138367834">Gestionnaire de &amp;fichiers</translation>
++<translation id="4254921211241441775">Arrêter la synchronisation du compte</translation>
++<translation id="7791543448312431591">Ajouter</translation>
++<translation id="8569764466147087991">Sélectionnez le fichier à ouvrir</translation>
++<translation id="5449451542704866098">Aucun forfait de données</translation>
++<translation id="307505906468538196">Créer un compte Google</translation>
++<translation id="2053553514270667976">Code postal</translation>
++<translation id="48838266408104654">&amp;Gestionnaire de tâches</translation>
++<translation id="4378154925671717803">Téléphone</translation>
++<translation id="3694027410380121301">Sélectionner l'onglet précédent</translation>
++<translation id="6178664161104547336">Sélectionner un certificat</translation>
++<translation id="1375321115329958930">Mots de passe enregistrés</translation>
++<translation id="3341703758641437857">Autoriser l'accès aux URL de fichier</translation>
++<translation id="5702898740348134351">Modifi&amp;er les moteurs de recherche...</translation>
++<translation id="734303607351427494">Gérer les moteurs de recherche...</translation>
++<translation id="8326478304147373412">PKCS #7, chaîne de certificats</translation>
++<translation id="3242765319725186192">Clé pré-partagée :</translation>
++<translation id="8089798106823170468">Contrôlez et partagez l'accès à vos imprimantes depuis n'importe quel compte Google.</translation>
++<translation id="5984992849064510607">Ajoute l'option &quot;Utiliser les onglets latéraux&quot; au menu contextuel de la barre d'onglets. Utilisez cette option pour déplacer les onglets du haut de l'écran (affichage par défaut) vers le côté. Particulièrement utile sur les grands écrans.</translation>
++<translation id="839736845446313156">S'inscrire</translation>
++<translation id="4668929960204016307">,</translation>
++<translation id="2409527877874991071">Saisissez un nouveau nom.</translation>
++<translation id="4240069395079660403"><ph name="PRODUCT_NAME"/> ne peut pas être affiché dans cette langue.</translation>
++<translation id="747114903913869239">Erreur : impossible de décoder l'extension.</translation>
++<translation id="5412637665001827670">Clavier bulgare</translation>
++<translation id="2113921862428609753">Accès aux informations de l'autorité</translation>
++<translation id="5227536357203429560">Ajouter un réseau privé...</translation>
++<translation id="732677191631732447">C&amp;opier l'URL du fichier audio</translation>
++<translation id="7224023051066864079">Masque de sous-réseau :</translation>
++<translation id="2401813394437822086">Impossible d'accéder à votre compte ?</translation>
++<translation id="2344262275956902282">Utiliser les touches - et = pour paginer une liste d'entrées</translation>
++<translation id="3609138628363401169">Le serveur ne prend pas en charge l'extension de renégociation TLS.</translation>
++<translation id="3369624026883419694">Résolution de l'hôte...</translation>
++<translation id="8870413625673593573">Récemment fermés</translation>
++<translation id="9145357542626308749">Le certificat de sécurité du site a été signé avec un algorithme de signature faible.</translation>
++<translation id="8502803898357295528">Votre mot de passe a été modifié</translation>
++<translation id="4064488613268730704">Gérer les paramètres de saisie automatique...</translation>
++<translation id="6830600606572693159">La page Web <ph name="URL"/> n'est pas disponible pour le moment. Cela peut être dû à une surcharge ou à une opération de maintenance.</translation>
++<translation id="4145797339181155891">Éjecter</translation>
++<translation id="7886793013438592140">Impossible de lancer le processus de service.</translation>
++<translation id="8417944620073548444"><ph name="MEGABYTES"/> Mo restants</translation>
++<translation id="7339898014177206373">Nouvelle fenêtre</translation>
++<translation id="3026202950002788510">Sélectionnez
++ <ph name="BEGIN_BOLD"/>
++ Applications &gt; Préférences système &gt; Réseau &gt; Avancé &gt; Proxys
++ <ph name="END_BOLD"/>
++ et désélectionnez les serveurs proxy sélectionnés.</translation>
++<translation id="7033648024564583278">Gravure en cours d'initialisation...</translation>
++<translation id="2246340272688122454">Téléchargement de l'image de récupération...</translation>
++<translation id="7770995925463083016">il y a <ph name="NUMBER_TWO"/> minutes</translation>
++<translation id="2816269189405906839">Mode de saisie du chinois (cangjie)</translation>
++<translation id="7087282848513945231">Comté</translation>
++<translation id="2149951639139208969">Ouvrir l'adresse dans un nouvel onglet</translation>
++<translation id="175196451752279553">&amp;Rouvrir l'onglet fermé</translation>
++<translation id="5992618901488170220">Impossible d'afficher la page Web, car votre ordinateur est passé en mode
++ veille ou hibernation. Dans ce cas, les connexions réseau sont
++ coupées et les requêtes réseau échouent. L'actualisation de la page
++ devrait permettre de résoudre ce problème.</translation>
++<translation id="2655386581175833247">Certificat utilisateur :</translation>
++<translation id="5039804452771397117">Autoriser</translation>
++<translation id="5435964418642993308">Appuyer sur Entrée pour revenir en arrière et sur la touche de menu contextuel pour afficher l'historique</translation>
++<translation id="81686154743329117">ZRM</translation>
++<translation id="7564146504836211400">Cookies et autres données</translation>
++<translation id="2266011376676382776">Page(s) ne répondant pas</translation>
++<translation id="2714313179822741882">Paramètres d'entrée hangûl</translation>
++<translation id="8658163650946386262">Configurer la synchronisation...</translation>
++<translation id="3100609564180505575">Modules (<ph name="TOTAL_COUNT"/>). Conflits connus : <ph name="BAD_COUNT"/>, conflits probables : <ph name="SUSPICIOUS_COUNT"/></translation>
++<translation id="3627671146180677314">Date de renouvellement du certificat Netscape</translation>
++<translation id="1319824869167805246">Ouvrir tous les favoris dans une nouvelle fenêtre</translation>
++<translation id="8652487083013326477">bouton radio concernant l'étendue de pages</translation>
++<translation id="5204967432542742771">Saisissez votre mot de passe</translation>
++<translation id="4388712255200933062"><ph name="CLOUD_PRINT_NAME"/> est conçu pour rendre l'impression plus intuitive, accessible et utile. <ph name="CLOUD_PRINT_NAME"/> vous permet de rendre vos imprimantes accessibles depuis n'importe quelle application Web ou mobile associée à <ph name="CLOUD_PRINT_NAME"/>.</translation>
++<translation id="2932611376188126394">Dictionnaire de kanji unique</translation>
++<translation id="5485754497697573575">Rétablir tous les onglets</translation>
++<translation id="3371861036502301517">Échec de l'installation de l'extension</translation>
++<translation id="644038709730536388">En savoir plus sur la manière de se protéger des logiciels malveillants en ligne</translation>
++<translation id="2155931291251286316">Toujours afficher les fenêtres pop-up de <ph name="HOST"/></translation>
++<translation id="3445830502289589282">Authentification phase 2 :</translation>
++<translation id="5650551054760837876">Aucun résultat de recherche trouvé</translation>
++<translation id="5494362494988149300">Ouvrir une fois le téléchargement &amp;terminé</translation>
++<translation id="2956763290572484660"><ph name="COOKIES"/> cookies</translation>
++<translation id="6989836856146457314">Mode de saisie du japonais (pour clavier américain)</translation>
++<translation id="9187787570099877815">Continuer à bloquer les plug-ins</translation>
++<translation id="8425492902634685834">Épingler sur la barre des tâches</translation>
++<translation id="825608351287166772">Les certificats ont une période de validité, comme tous les documents relatifs à votre identité (tel qu'un passeport). Le certificat présenté à votre navigateur n'est pas encore valide ! Lorsqu'un certificat est en dehors de sa période de validité, il n'est pas nécessaire d'assurer la maintenance de certaines informations relatives à son état (s'il a été révoqué ou s'il n'est plus approuvé). Par conséquent, il est impossible de vérifier que le certificat est fiable. Ne poursuivez pas.</translation>
++<translation id="741630086309232721">Fermer la session d'invité</translation>
++<translation id="7309459761865060639">Contrôlez vos tâches d'impression et l'état de connexion de vos imprimantes en ligne.</translation>
++<translation id="4803909571878637176">Désinstallation</translation>
++<translation id="5209518306177824490">Empreinte SHA-1</translation>
++<translation id="3300768886937313568">Modifier le code PIN de la carte SIM</translation>
++<translation id="7447657194129453603">État du réseau :</translation>
++<translation id="1553538517812678578">sans limite</translation>
++<translation id="7947315300197525319">(Choisir une autre capture d'écran)</translation>
++<translation id="3612070600336666959">Désactivation</translation>
++<translation id="3759461132968374835">Aucune erreur n'a été signalée récemment. Les erreurs n'apparaissent ici que lorsque l'envoi de rapports d'erreur est activé.</translation>
++<translation id="1516602185768225813">Rouvrir les dernières pages ouvertes</translation>
++<translation id="189210018541388520">Ouvrir en mode plein écran</translation>
++<translation id="8795668016723474529">Ajouter une carte de paiement</translation>
++<translation id="5860033963881614850">Désactivé</translation>
++<translation id="3956882961292411849">Chargement des informations sur votre forfait Internet mobile, veuillez patienter...</translation>
++<translation id="689050928053557380">Acheter un forfait de données...</translation>
++<translation id="4235618124995926194">Inclure cet e-mail :</translation>
++<translation id="4874539263382920044">Le titre doit comporter au moins un caractère.</translation>
++<translation id="798525203920325731">Espaces de noms réseau</translation>
++<translation id="263325223718984101"><ph name="PRODUCT_NAME"/> n'a pas pu terminer l'installation, mais va poursuivre son exécution à partir de son image disque.</translation>
++<translation id="7025190659207909717">Gestion des services Internet mobiles</translation>
++<translation id="8265096285667890932">Utiliser les onglets latéraux</translation>
++<translation id="4250680216510889253">Non</translation>
++<translation id="3949593566929137881">Saisir le code PIN de la carte SIM</translation>
++<translation id="6291953229176937411">&amp;Afficher dans le Finder</translation>
++<translation id="2476990193835943955">Maintenez la touche Ctrl, Alt ou Maj enfoncée&lt;br&gt;pour afficher le raccourci clavier qui lui est associé.</translation>
++<translation id="9187827965378254003">Vraiment désolé, aucun prototype n'est disponible pour le moment.</translation>
++<translation id="8933960630081805351">&amp;Afficher dans le Finder</translation>
++<translation id="3041612393474885105">Informations relatives au certificat</translation>
++<translation id="7378810950367401542">/</translation>
++<translation id="4611079913162790275">La synchronisation des mots de passe requiert votre attention.</translation>
++<translation id="6562758426028728553">Veuillez saisir l'ancien et le nouveau code PIN.</translation>
++<translation id="614161640521680948">Langue :</translation>
++<translation id="3665650519256633768">Résultats de recherche</translation>
++<translation id="3733127536501031542">Serveur SSL avec fonction d'optimisation</translation>
++<translation id="3614837889828516995">Enregistrer en PDF</translation>
++<translation id="5745056705311424885">Mémoire USB détectée</translation>
++<translation id="5895875028328858187">M'avertir lorsque le flux de données est faible ou presque inexistant</translation>
++<translation id="939598580284253335">Saisir le mot de passe multiterme</translation>
++<translation id="7917972308273378936">Clavier lituanien</translation>
++<translation id="8371806639176876412">Les éléments saisis dans le champ polyvalent peuvent être enregistrés.</translation>
++<translation id="4216499942524365685">Les informations de connexion à votre compte sont obsolètes. Cliquez ici pour saisir à nouveau votre mot de passe.</translation>
++<translation id="8899388739470541164">Vietnamien</translation>
++<translation id="4091434297613116013">feuilles de papier</translation>
++<translation id="7475671414023905704">URL de mot de passe perdu Netscape</translation>
++<translation id="3335947283844343239">Rouvrir l'onglet fermé</translation>
++<translation id="4089663545127310568">Effacer les mots de passe enregistrés</translation>
++<translation id="6500444002471948304">Créer un nouveau dossier...</translation>
++<translation id="2480626392695177423">Basculer en mode ponctuation pleine chasse ou demi-chasse</translation>
++<translation id="5830410401012830739">Gérer les paramètres de localisation...</translation>
++<translation id="8977410484919641907">Synchronisé...</translation>
++<translation id="2794293857160098038">Options de recherche par défaut</translation>
++<translation id="3947376313153737208">Aucune sélection</translation>
++<translation id="1346104802985271895">Mode de saisie du vietnamien (TELEX)</translation>
++<translation id="5935630983280450497"><ph name="NUMBER_ONE"/> minute restante</translation>
++<translation id="5889282057229379085">Le nombre maximal d'autorités de certification intermédiaires a été dépassé : <ph name="NUM_INTERMEDIATE_CA"/></translation>
++<translation id="3180365125572747493">Saisissez un mot de passe pour chiffrer ce fichier de certificat.</translation>
++<translation id="5496587651328244253">Organiser</translation>
++<translation id="4821086771593057290">Votre mot de passe a changé. Veuillez réessayer avec votre nouveau mot de passe.</translation>
++<translation id="7075513071073410194">PKCS #1 MD5 avec chiffrement RSA</translation>
++<translation id="4378727699507047138">Utiliser le thème classique</translation>
++<translation id="7124398136655728606">Échap efface toute la mémoire tampon de pré-édition</translation>
++<translation id="8293206222192510085">Ajouter aux favoris</translation>
++<translation id="2592884116796016067">Un incident est survenu sur une partie de cette page (HTML WebWorker). Elle risque de ne pas fonctionner correctement.</translation>
++<translation id="2529133382850673012">Clavier américain</translation>
++<translation id="4411578466613447185">Signataire de code</translation>
++<translation id="1354868058853714482">Adobe Reader n'est pas à jour et risque de ne plus être sécurisé.</translation>
++<translation id="6252594924928912846">Personnaliser les paramètres de synchronisation...</translation>
++<translation id="8425755597197517046">Co&amp;ller et rechercher</translation>
++<translation id="1093148655619282731">Détails du certificat sélectionné :</translation>
++<translation id="5568069709869097550">Impossible de se connecter</translation>
++<translation id="2743322561779022895">Activation :</translation>
++<translation id="4181898366589410653">Système de révocation introuvable dans le certificat du serveur</translation>
++<translation id="8705331520020532516">Numéro de série</translation>
++<translation id="1665770420914915777">Afficher la page &quot;Nouvel onglet&quot;</translation>
++<translation id="2629089419211541119">il y a <ph name="NUMBER_ONE"/> heure</translation>
++<translation id="1691063574428301566">Votre ordinateur redémarrera une fois la mise à jour effectuée.</translation>
++<translation id="131364520783682672">Verr. maj.</translation>
++<translation id="6259308910735500867">L'accès au répertoire de l'hôte de communication à distance a été refusé. Essayez avec un autre compte.</translation>
++<translation id="3415261598051655619">Accessible aux scripts :</translation>
++<translation id="2335122562899522968">Cette page place des cookies.</translation>
++<translation id="8461914792118322307">Proxy</translation>
++<translation id="4089521618207933045">Avec sous-menu</translation>
++<translation id="1936157145127842922">Afficher dans le dossier</translation>
++<translation id="6982279413068714821">il y a <ph name="NUMBER_DEFAULT"/> minutes</translation>
++<translation id="7977590112176369853">&lt;saisir une requête&gt;</translation>
++<translation id="3449839693241009168">Appuyez sur <ph name="SEARCH_KEY"/> pour envoyer des commandes à <ph name="EXTENSION_NAME"/>.</translation>
++<translation id="7443484992065838938">Prévisualiser le rapport</translation>
++<translation id="5714678912774000384">Activer le dernier onglet</translation>
++<translation id="3799598397265899467">Lorsque je quitte le navigateur</translation>
++<translation id="2125314715136825419">Continuer sans mettre à jour Adobe Reader (non recommandé)</translation>
++<translation id="1120026268649657149">Le champ de mot clé doit être vide ou comporter un mot unique</translation>
++<translation id="542318722822983047">Déplacer le curseur automatiquement au caractère suivant</translation>
++<translation id="5317780077021120954">Enregistrer</translation>
++<translation id="9027459031423301635">Ouvrir le lien dans un nouvel ongle&amp;t</translation>
++<translation id="2251809247798634662">Nouvelle fenêtre de navigation privée</translation>
++<translation id="358344266898797651">Celtique</translation>
++<translation id="3625870480639975468">Réinitialiser le zoom</translation>
++<translation id="5199729219167945352">Prototypes</translation>
++<translation id="5055518462594137986">Mémoriser mes choix pour tous les liens de ce type</translation>
++<translation id="246059062092993255">Les plug-ins de cette page ont été bloqués.</translation>
++<translation id="2870560284913253234">Site</translation>
++<translation id="6945221475159498467">Sélectionner</translation>
++<translation id="7724603315864178912">Couper</translation>
++<translation id="4164507027399414915">Restaurer toutes les miniatures supprimées</translation>
++<translation id="917051065831856788">Utiliser les onglets latéraux</translation>
++<translation id="1976150099241323601">Se connecter au dispositif de sécurité</translation>
++<translation id="6620110761915583480">Enregistrer le fichier</translation>
++<translation id="4988526792673242964">Pages</translation>
++<translation id="7543025879977230179">Options de <ph name="PRODUCT_NAME"/></translation>
++<translation id="2175607476662778685">Barre de lancement rapide</translation>
++<translation id="6434309073475700221">Annuler</translation>
++<translation id="1367951781824006909">Choisir un fichier</translation>
++<translation id="1425127764082410430">&amp;Rechercher <ph name="SEARCH_TERMS"/> avec <ph name="SEARCH_ENGINE"/></translation>
++<translation id="684265517037058883">(pas encore valide)</translation>
++<translation id="2027538664690697700">Mettre à jour le plug-in...</translation>
++<translation id="8205333955675906842">Police Sans-Serif</translation>
++<translation id="39964277676607559">Impossible de charger le JavaScript &quot;<ph name="RELATIVE_PATH"/>&quot; du script de contenu.</translation>
++<translation id="4378551569595875038">Connexion...</translation>
++<translation id="7029809446516969842">Mots de passe</translation>
++<translation id="8053278772142718589">Fichiers PKCS #12</translation>
++<translation id="3129020372442395066">Options de saisie automatique de <ph name="PRODUCT_NAME_SHORT"/></translation>
++<translation id="4114360727879906392">Fenêtre précédente</translation>
++<translation id="8238649969398088015">Astuce</translation>
++<translation id="5958418293370246440"><ph name="SAVED_FILES"/> / <ph name="TOTAL_FILES"/> fichiers</translation>
++<translation id="2350172092385603347">Localisation utilisée, mais les paramètres régionaux par défaut (default_locale) n'ont pas été indiqués dans le manifeste. </translation>
++<translation id="8221729492052686226">Si vous n'êtes pas à l'origine de cette requête, il s'agit probablement d'une attaque contre votre système. Si vous n'avez pas lancé cette requête de manière intentionnelle, cliquez sur Ne rien faire.</translation>
++<translation id="5894314466642127212">Votre commentaire a bien été envoyé.</translation>
++<translation id="894360074127026135">Fonction d'optimisation internationale Netscape </translation>
++<translation id="6025294537656405544">Taille de police minimale</translation>
++<translation id="1201402288615127009">Suivant</translation>
++<translation id="1335588927966684346">Utilitaire :</translation>
++<translation id="7857823885309308051">Cette opération peut prendre une minute...</translation>
++<translation id="662870454757950142">Le format du mot de passe est incorrect.</translation>
++<translation id="370665806235115550">Chargement...</translation>
++<translation id="1808792122276977615">Ajouter la page...</translation>
++<translation id="2076269580855484719">Masquer ce plug-in</translation>
++<translation id="254416073296957292">&amp;Paramètres linguistiques...</translation>
++<translation id="6652975592920847366">Créer un support de récupération du système d'exploitation</translation>
++<translation id="52912272896845572">Le fichier de clé privée est incorrect.</translation>
++<translation id="3232318083971127729">Valeur :</translation>
++<translation id="8807632654848257479">Stable</translation>
++<translation id="4209092469652827314">Grande</translation>
++<translation id="4222982218026733335">Certificat serveur invalide</translation>
++<translation id="152234381334907219">Jamais enregistrés</translation>
++<translation id="5600599436595580114">Cette page a été préchargée.</translation>
++<translation id="8926468725336609312">Google Chrome ne peut pas afficher l'aperçu avant impression lorsque la visionneuse de documents PDF intégrée est désactivée. Pour l'afficher, veuillez accéder à <ph name="BEGIN_LINK"/>chrome://plugins<ph name="END_LINK"/>, activer &quot;Chrome PDF Viewer&quot; et réessayer.</translation>
++<translation id="8494214181322051417">Nouveau !</translation>
++<translation id="7762841930144642410"><ph name="BEGIN_BOLD"/>Vous êtes passé en navigation privée<ph name="END_BOLD"/>. Les pages que vous consultez dans cette fenêtre n'apparaîtront ni dans l'historique de votre navigateur, ni dans l'historique des recherches, et ne laisseront aucune trace (comme les cookies) sur votre ordinateur une fois que vous aurez fermé la fenêtre de navigation privée. Tous les fichiers téléchargés et les favoris créés seront toutefois conservés. <ph name="LINE_BREAK"/> <ph name="BEGIN_BOLD"/>Passer en navigation privée n'a aucun effet sur les autres utilisateurs, serveurs ou logiciels. Méfiez-vous :<ph name="END_BOLD"/> <ph name="BEGIN_LIST"/> <ph name="BEGIN_LIST_ITEM"/>Des sites Web qui collectent ou partagent des informations vous concernant<ph name="END_LIST_ITEM"/> <ph name="BEGIN_LIST_ITEM"/>Des fournisseurs d'accès Internet ou des employeurs qui conservent une trace des pages que vous visitez<ph name="END_LIST_ITEM"/> <ph name="BEGIN_LIST_ITEM"/>Des programmes indésirables qui enregistrent vos frappes en échange d'émoticônes gratuites<ph name="END_LIST_ITEM"/> <ph name="BEGIN_LIST_ITEM"/>Des personnes qui pourraient surveiller vos activités<ph name="END_LIST_ITEM"/> <ph name="BEGIN_LIST_ITEM"/>Des personnes qui se tiennent derrière vous<ph name="END_LIST_ITEM"/> <ph name="END_LIST"/> <ph name="BEGIN_LINK"/>En savoir plus sur la navigation privée<ph name="END_LINK"/></translation>
++<translation id="2386255080630008482">Le certificat du serveur a été révoqué.</translation>
++<translation id="2135787500304447609">&amp;Reprendre</translation>
++<translation id="8309505303672555187">Sélectionnez un réseau :</translation>
++<translation id="6143635259298204954">Impossible d'extraire les fichiers de l'extension. Pour effectuer cette opération en toute sécurité, vous devez disposer d'un chemin d'accès à votre répertoire de profils ne contenant pas de lien symbolique. Aucun chemin de ce type n'existe pour votre profil.</translation>
++<translation id="1813414402673211292">Effacer les données de navigation</translation>
++<translation id="4062903950301992112">Si vous êtes conscient que la visite de ce site peut être préjudiciable à votre ordinateur, vous pouvez <ph name="PROCEED_LINK"/>.</translation>
++<translation id="32330993344203779">Votre périphérique est inscrit pour bénéficier de la gestion d'entreprise.</translation>
++<translation id="2356762928523809690">Serveur de mise à jour non disponible (erreur : <ph name="ERROR_NUMBER"/>)</translation>
++<translation id="219008588003277019">Module client natif : <ph name="NEXE_NAME"/></translation>
++<translation id="5436510242972373446">Rechercher sur <ph name="SITE_NAME"/> :</translation>
++<translation id="3800764353337460026">Style de symboles</translation>
++<translation id="6719684875142564568"><ph name="NUMBER_ZERO"/> hours</translation>
++<translation id="2096368010154057602">Département</translation>
++<translation id="1036561994998035917">Continuer à utiliser <ph name="ENGINE_NAME"/></translation>
++<translation id="8730621377337864115">OK</translation>
++<translation id="665757950158579497">Essayez de désactiver les prédictions d'actions du réseau en procédant comme suit :
++ Sélectionnez le
++ <ph name="BEGIN_BOLD"/>
++ menu clé à molette &gt;
++ <ph name="SETTINGS_TITLE"/>
++ &gt;
++ <ph name="ADVANCED_TITLE"/>
++ <ph name="END_BOLD"/>
++ et désélectionnez &quot;<ph name="NO_PREFETCH_DESCRIPTION"/>&quot;.
++ Si le problème n'est pas résolu, nous vous conseillons de sélectionner de nouveau
++ cette option pour améliorer les performances.</translation>
++<translation id="4932733599132424254">Date</translation>
++<translation id="6267166720438879315">Sélectionnez un certificat pour vous authentifier sur <ph name="HOST_NAME"/>.</translation>
++<translation id="2422927186524098759">Barre latérale</translation>
++<translation id="7839809549045544450">La clé publique éphémère Diffie-Hellman associée au serveur est peu sûre.</translation>
++<translation id="5515806255487262353">Rechercher dans Dictionnaire</translation>
++<translation id="350048665517711141">Sélectionnez un moteur de recherche</translation>
++<translation id="2790805296069989825">Clavier russe</translation>
++<translation id="5708171344853220004">Nom Microsoft principal</translation>
++<translation id="5464696796438641524">Clavier polonais</translation>
++<translation id="2080010875307505892">Clavier serbe</translation>
++<translation id="2953767478223974804"><ph name="NUMBER_ONE"/> minute</translation>
++<translation id="201192063813189384">Erreur lors de la lecture des données du cache.</translation>
++<translation id="7851768487828137624">Canary</translation>
++<translation id="6129938384427316298">Commentaire du certificat Netscape</translation>
++<translation id="8210608804940886430">Page suivante</translation>
++<translation id="9065596142905430007"><ph name="PRODUCT_NAME"/> est à jour.</translation>
++<translation id="1035650339541835006">Paramètres de saisie automatique...</translation>
++<translation id="6315493146179903667">Tout ramener au premier plan</translation>
++<translation id="1000498691615767391">Sélectionner le dossier à ouvrir</translation>
++<translation id="3593152357631900254">Activer le mode Pinyin fuzzy</translation>
++<translation id="5015344424288992913">Résolution du proxy...</translation>
++<translation id="8506299468868975633">Le téléchargement de l'image a été interrompu.</translation>
++<translation id="4724168406730866204">Eten 26</translation>
++<translation id="308268297242056490">URI</translation>
++<translation id="4479812471636796472">Clavier Dvorak américain</translation>
++<translation id="8673026256276578048">Rechercher sur le Web...</translation>
++<translation id="1437307674059038925">Si vous utilisez un serveur proxy, vérifiez les paramètres associés ou demandez à votre administrateur réseau
++ si ce serveur fonctionne.</translation>
++<translation id="149347756975725155">Impossible de charger l'icône de l'extension &quot;<ph name="ICON"/>&quot;.</translation>
++<translation id="3675321783533846350">Définir un proxy pour se connecter au réseau</translation>
++<translation id="5451285724299252438">zone de texte concernant l'étendue de pages</translation>
++<translation id="5669267381087807207">Activation</translation>
++<translation id="7434823369735508263">Clavier Dvorak britannique</translation>
++<translation id="1572103024875503863"><ph name="NUMBER_MANY"/> jours</translation>
++<translation id="2084978867795361905">MS-IME</translation>
++<translation id="7227669995306390694">Aucun forfait de données <ph name="NETWORK"/></translation>
++<translation id="3481915276125965083">Les fenêtres pop-up suivantes ont été bloquées sur cette page :</translation>
++<translation id="7163503212501929773"><ph name="NUMBER_MANY"/> heures restantes</translation>
++<translation id="7705276765467986571">Impossible de charger le modèle du favori.</translation>
++<translation id="1196338895211115272">Échec d'exportation de la clé privée</translation>
++<translation id="5586329397967040209">Utiliser comme page d'accueil</translation>
++<translation id="629730747756840877">Compte</translation>
++<translation id="8525306231823319788">Plein écran</translation>
++<translation id="9054208318010838">Autoriser tous les sites à suivre ma position géographique</translation>
++<translation id="3058212636943679650">Si la restauration du système d'exploitation de votre ordinateur s'avère nécessaire, une carte SD ou une clé USB de récupération vous sera demandée.</translation>
++<translation id="2815382244540487333">Les cookies suivants ont été bloqués :</translation>
++<translation id="8882395288517865445">Inclure les adresses de ma fiche de Carnet d’adresses</translation>
++<translation id="374530189620960299">Le certificat de sécurité du site n'est pas approuvé !</translation>
++<translation id="8852407435047342287">Votre liste d'applications, d'extensions et de thèmes installés</translation>
++<translation id="5647283451836752568">Exécuter tous les plug-ins de cette page</translation>
++<translation id="8642947597466641025">Augmente la taille du texte</translation>
++<translation id="5188181431048702787">Accepter et continuer »</translation>
++<translation id="1293556467332435079">Fichiers
++</translation>
++<translation id="2490270303663597841">Appliquer uniquement à cette session de navigation privée</translation>
++<translation id="1757915090001272240">Latin large</translation>
++<translation id="8496717697661868878">Exécuter ce plug-in</translation>
++<translation id="3450660100078934250">MasterCard</translation>
++<translation id="2916073183900451334">Sur le Web, Tab permet de sélectionner les liens, ainsi que les champs de formulaire.</translation>
++<translation id="7772127298218883077">À propos de <ph name="PRODUCT_NAME"/></translation>
++<translation id="2090876986345970080">Paramètres de sécurité du système</translation>
++<translation id="9219103736887031265">Images</translation>
++<translation id="5453632173748266363">Cyrillique</translation>
++<translation id="1008557486741366299">Pas maintenant</translation>
++<translation id="8415351664471761088">Attendre la fin du téléchargement</translation>
++<translation id="1545775234664667895">Thème &quot;<ph name="THEME_NAME"/>&quot; installé</translation>
++<translation id="5329858601952122676">&amp;Supprimer</translation>
++<translation id="6100736666660498114">Menu Démarrer</translation>
++<translation id="3994878504415702912">&amp;Zoom</translation>
++<translation id="9009369504041480176">Transfert en cours (<ph name="PROGRESS_PERCENT"/> %)...</translation>
++<translation id="8995603266996330174">Géré par <ph name="DOMAIN"/></translation>
++<translation id="5602600725402519729">&amp;Rafraîchir</translation>
++<translation id="172612876728038702">Configuration du module de plate-forme sécurisée (TPM) en cours. Veuillez patienter, cela peut prendre quelques minutes.</translation>
++<translation id="1362165759943288856">Vous avez acheté une quantité illimitée de données le <ph name="DATE"/>.</translation>
++<translation id="2078019350989722914">Confirmer avant de quitter (<ph name="KEY_EQUIVALENT"/>)</translation>
++<translation id="7965010376480416255">Mémoire partagée</translation>
++<translation id="6248988683584659830">Rech. dans les paramètres</translation>
++<translation id="8323232699731382745">mot de passe d'accès au réseau</translation>
++<translation id="6588399906604251380">Activer la vérification orthographique</translation>
++<translation id="7167621057293532233">Types de données</translation>
++<translation id="7053983685419859001">Bloquer</translation>
++<translation id="2485056306054380289">Certificat de l'autorité de certification du serveur :</translation>
++<translation id="6462109140674788769">Clavier grec</translation>
++<translation id="2727712005121231835">Taille réelle</translation>
++<translation id="8887733174653581061">Toujours en haut</translation>
++<translation id="5581211282705227543">Aucun plug-in installé.</translation>
++<translation id="610886263749567451">Alerte JavaScript</translation>
++<translation id="5488468185303821006">Autoriser en mode navigation privée</translation>
++<translation id="6556866813142980365">Rétablir</translation>
++<translation id="2107287771748948380"><ph name="OBFUSCATED_CC_NUMBER"/>, expire le : <ph name="CC_EXPIRATION_DATE"/></translation>
++<translation id="6584811624537923135">Confirmer la désinstallation</translation>
++<translation id="7429235532957570505">Impossible de désactiver les plug-ins ayant été activés par une stratégie d'entreprise.</translation>
++<translation id="7866522434127619318">Cette fonctionnalité active l'option &quot;Lire en un clic&quot; dans les paramètres de contenu du plug-in.</translation>
++<translation id="8860923508273563464">Attendre la fin des téléchargements</translation>
++<translation id="6406506848690869874">Synchronisation</translation>
++<translation id="5288678174502918605">&amp;Rouvrir l'onglet fermé</translation>
++<translation id="7238461040709361198">Votre mot de passe de compte Google a changé depuis votre dernière connexion à partir de cet ordinateur.</translation>
++<translation id="1956050014111002555">Le fichier contenait plusieurs certificats, aucun d'eux n'a été importé :</translation>
++<translation id="302620147503052030">Afficher le bouton</translation>
++<translation id="5512074755152723588">La saisie dans le champ polyvalent d'une URL déjà ouverte dans un autre onglet entraîne l'affichage de l'onglet en question, et non l'affichage de l'URL dans l'onglet actuel.</translation>
++<translation id="9157595877708044936">Configuration en cours...</translation>
++<translation id="4475552974751346499">Rechercher dans les téléchargements</translation>
++<translation id="3021256392995617989">Me demander lorsqu'un site tente de suivre ma position géographique (recommandé)</translation>
++<translation id="5185386675596372454">La nouvelle version de &quot;<ph name="EXTENSION_NAME"/>&quot; a été désactivée, car elle nécessite davantage d'autorisations.</translation>
++<translation id="4285669636069255873">Clavier phonétique russe</translation>
++<translation id="4148925816941278100">American Express</translation>
++<translation id="2320435940785160168">Ce serveur exige un certificat d'authentification et n'a pas accepté celui envoyé par le navigateur.
++Votre certificat a peut-être expiré ou le serveur n'a pas approuvé l'émetteur.
++Réessayez avec un autre certificat si vous en avez un.
++Sinon, vous devrez en obtenir un nouveau d'un autre émetteur.</translation>
++<translation id="6295228342562451544">Lorsque vous vous connectez à un site Web sécurisé, le serveur hébergeant ce site présente à votre navigateur un &quot;certificat&quot; afin de vérifier l'identité du site. Ce certificat contient des informations d'identité, telles que l'adresse du site Web, laquelle est vérifiée par un tiers approuvé par votre ordinateur. En vérifiant que l'adresse du certificat correspond à l'adresse du site Web, il est possible de s'assurer que vous êtes connecté de façon sécurisée avec le site Web souhaité et non pas avec un tiers (tel qu'un pirate informatique sur votre réseau).</translation>
++<translation id="6342069812937806050">À l'instant</translation>
++<translation id="5605716740717446121">Votre carte SIM sera définitivement désactivée si vous ne saisissez pas correctement la clé de déverrouillage du code PIN. Nombre de tentatives restantes : <ph name="TRIES_COUNT"/></translation>
++<translation id="8836712291807476944"><ph name="SAVED_BYTES"/> / <ph name="TOTAL_BYTES"/> octets, Interrompu</translation>
++<translation id="5502500733115278303">Importés depuis Firefox</translation>
++<translation id="569109051430110155">Détection automatique</translation>
++<translation id="4408599188496843485">&amp;Aide</translation>
++<translation id="5399158067281117682">Les codes PIN sont différents !</translation>
++<translation id="8494234776635784157">Contenu Web</translation>
++<translation id="2681441671465314329">Vider le cache</translation>
++<translation id="3646789916214779970">Rétablir le thème par défaut</translation>
++<translation id="1592960452683145077">Le service de communication à distance a démarré correctement. Vous devriez maintenant pouvoir vous connecter à distance à cet ordinateur.</translation>
++<translation id="1679068421605151609">Outils de développement</translation>
++<translation id="6648524591329069940">Police Serif</translation>
++<translation id="6896758677409633944">Copier</translation>
++<translation id="5260508466980570042">Adresse e-mail ou mot de passe incorrect. Veuillez réessayer.</translation>
++<translation id="7887998671651498201">Le plug-in suivant ne répond pas : souhaitez-vous interrompre <ph name="PLUGIN_NAME"/> ?</translation>
++<translation id="173188813625889224">Sens</translation>
++<translation id="8088823334188264070"><ph name="NUMBER_MANY"/> secondes</translation>
++<translation id="1337036551624197047">Clavier tchèque</translation>
++<translation id="4212108296677106246">Voulez-vous que &quot;<ph name="CERTIFICATE_NAME"/>&quot; soit considérée comme une autorité de certification fiable ?</translation>
++<translation id="2861941300086904918">Gestionnaire de sécurité natif du client</translation>
++<translation id="6991443949605114807">&lt;p&gt;Lorsque vous exécutez <ph name="PRODUCT_NAME"/> dans un environnement de bureau pris en charge, les paramètres proxy du système sont utilisés. Toutefois, soit votre système n'est pas pris en charge, soit un problème est survenu lors du lancement de votre configuration système.&lt;/p&gt;
++
++ &lt;p&gt;Vous avez toujours la possibilité d'effectuer la configuration via la ligne de commande. Pour plus d'informations sur les indicateurs et les variables d'environnement, veuillez vous reporter à &lt;code&gt;man <ph name="PRODUCT_BINARY_NAME"/>&lt;/code&gt;.&lt;/p&gt;</translation>
++<translation id="9071590393348537582">La page Web à l'adresse <ph name="URL"/> a déclenché trop de redirections. Pour résoudre le problème, effacez les cookies de ce site ou autorisez les cookies tiers. Si le problème persiste, il peut être dû à une mauvaise configuration du serveur et n'être aucunement lié à votre ordinateur.</translation>
++<translation id="7205869271332034173">SSID :</translation>
++<translation id="7084579131203911145">Nom du forfait :</translation>
++<translation id="5815645614496570556">Adresse X.400</translation>
++<translation id="3551320343578183772">Fermer l'onglet</translation>
++<translation id="3345886924813989455">Impossible de trouver un navigateur pris en charge.</translation>
++<translation id="74354239584446316">Le compte associé à la boutique en ligne est le suivant : <ph name="EMAIL_ADDRESS"/>. L'utilisation d'un autre compte pour la synchronisation provoque des erreurs.</translation>
++<translation id="3712897371525859903">Enregistrer la p&amp;age sous...</translation>
++<translation id="7926251226597967072"><ph name="PRODUCT_NAME"/> importe actuellement les éléments suivants à partir de <ph name="IMPORT_BROWSER_NAME"/> :</translation>
++<translation id="2767649238005085901">Appuyez sur Entrée pour avancer et sur la touche de menu contextuel pour afficher l'historique</translation>
++<translation id="8580634710208701824">Actualiser le cadre</translation>
++<translation id="1018656279737460067">Annulé</translation>
++<translation id="7606992457248886637">Autorités</translation>
++<translation id="707392107419594760">Sélectionnez votre clavier :</translation>
++<translation id="2007404777272201486">Signaler un problème...</translation>
++<translation id="2390045462562521613">Ignorer ce réseau</translation>
++<translation id="3348038390189153836">Nouveau matériel détecté</translation>
++<translation id="1666788816626221136">Vous disposez de certificats qui n'appartiennent à aucune autre catégorie :</translation>
++<translation id="4821935166599369261">&amp;Profilage activé</translation>
++<translation id="1603914832182249871">(Navigation privée)</translation>
++<translation id="7910768399700579500">&amp;Nouveau dossier</translation>
++<translation id="7472639616520044048">Types MIME :</translation>
++<translation id="2307164895203900614">Afficher les pages en arrière-plan (<ph name="NUM_BACKGROUND_APPS"/>)</translation>
++<translation id="3192947282887913208">Fichiers audio</translation>
++<translation id="6295535972717341389">Plug-ins</translation>
++<translation id="8116190140324504026">Plus d'informations...</translation>
++<translation id="7469894403370665791">Se connecter automatiquement à ce réseau</translation>
++<translation id="4807098396393229769">Titulaire de la carte</translation>
++<translation id="4094130554533891764">Elle peut désormais accéder à :</translation>
++<translation id="4131410914670010031">Noir et blanc</translation>
++<translation id="3800503346337426623">Ignorer la connexion et naviguer en tant qu'invité</translation>
++<translation id="2615413226240911668">Toutefois, cette page inclut d'autres ressources qui ne sont pas sécurisées. Ces ressources peuvent être consultées par des tiers pendant leur transfert, et modifiées par un pirate informatique dans le but de changer l'aspect et le comportement de cette page.</translation>
++<translation id="5880867612172997051">Accès réseau interrompu</translation>
++<translation id="7842346819602959665">La dernière version de l'extension &quot;<ph name="EXTENSION_NAME"/>&quot; requiert d'autres permissions. Elle a donc été désactivée.</translation>
++<translation id="3776667127601582921">Dans ce cas, le certificat du serveur ou un certificat d'autorité intermédiaire présenté à votre navigateur n'est pas valide. Cela peut signifier que le certificat est incorrect, qu'il contient des champs non valides ou qu'il n'est pas compatible.</translation>
++<translation id="2412835451908901523">Veuillez saisir la clé de déverrouillage du code PIN à 8 chiffres fournie par <ph name="CARRIER_ID"/>.</translation>
++<translation id="6979448128170032817">Exceptions...</translation>
++<translation id="7584802760054545466">Connexion à <ph name="NETWORK_ID"/></translation>
++<translation id="208047771235602537">Voulez-vous vraiment quitter <ph name="PRODUCT_NAME"/> alors qu'un téléchargement est en cours ?</translation>
++<translation id="4060383410180771901">Le site Web ne parvient pas à gérer la demande associée à <ph name="URL"/>.</translation>
++<translation id="6710213216561001401">Précédent</translation>
++<translation id="1108600514891325577">&amp;Arrêter</translation>
++<translation id="6035087343161522833">Lorsque l'option permettant de bloquer l'enregistrement des cookies tiers est activée, la lecture de ces cookies est également bloquée.</translation>
++<translation id="8619892228487928601"><ph name="CERTIFICATE_NAME"/> : <ph name="ERROR"/></translation>
++<translation id="1567993339577891801">Console JavaScript</translation>
++<translation id="1548132948283577726">Les sites pour lesquels vos mots de passe ne seront jamais enregistrés s'afficheront ici.</translation>
++<translation id="583281660410589416">Inconnu</translation>
++<translation id="3774278775728862009">Mode de saisie du thaï (clavier TIS-820.2538)</translation>
++<translation id="9115675100829699941">&amp;Favoris</translation>
++<translation id="2485422356828889247">Désinstaller</translation>
++<translation id="2621889926470140926">Voulez-vous vraiment quitter <ph name="PRODUCT_NAME"/> alors que <ph name="DOWNLOAD_COUNT"/> téléchargements sont en cours ?</translation>
++<translation id="7279701417129455881">Configurer le blocage des cookies...</translation>
++<translation id="1166359541137214543">ABC</translation>
++<translation id="5412713837047574330">L'application hébergée par <ph name="HOST_NAME"/> est inaccessible, car vous êtes déconnecté du réseau. Cette page s'affichera dès que la connexion réseau sera rétablie. &lt;br&gt;</translation>
++<translation id="5528368756083817449">Gestionnaire de favoris</translation>
++<translation id="7275974018215686543"><ph name="NUMBER_MANY"/> secs ago</translation>
++<translation id="215753907730220065">Quitter le mode plein écran</translation>
++<translation id="7849264908733290972">Ouvrir l'&amp;image dans un nouvel onglet</translation>
++<translation id="1560991001553749272">Favori ajouté !</translation>
++<translation id="3966072572894326936">Choisir un autre dossier...</translation>
++<translation id="8766796754185931010">Kotoeri</translation>
++<translation id="7781829728241885113">Hier</translation>
++<translation id="2762402405578816341">Synchroniser automatiquement les éléments suivants :</translation>
++<translation id="1623661092385839831">Votre ordinateur intègre un périphérique de sécurité TPM (module de plate-forme sécurisée) qui permet de mettre en œuvre plusieurs fonctionnalités de sécurité critiques dans Google Chrome OS.</translation>
++<translation id="3359256513598016054">Contraintes des stratégies de certificat</translation>
++<translation id="4433914671537236274">Créer un support de récupération</translation>
++<translation id="4509345063551561634">Emplacement :</translation>
++<translation id="7596288230018319236">Toutes les pages que vous consultez apparaîtront ici à moins que vous ne les ouvriez dans une fenêtre en navigation privée. Vous pouvez utiliser le bouton Rechercher de cette page pour rechercher dans toutes les pages de votre historique.</translation>
++<translation id="7434509671034404296">Options pour les développeurs</translation>
++<translation id="6447842834002726250">Cookies</translation>
++<translation id="2609371827041010694">Toujours exécuter pour ce site</translation>
++<translation id="5170568018924773124">Afficher le dossier</translation>
++<translation id="883848425547221593">Autres favoris</translation>
++<translation id="6054173164583630569">Clavier français</translation>
++<translation id="4870177177395420201"><ph name="PRODUCT_NAME"/> ne parvient pas à déterminer ou à définir le navigateur par défaut.</translation>
++<translation id="8898786835233784856">Sélectionner l'onglet suivant</translation>
++<translation id="2674170444375937751">Voulez-vous vraiment supprimer ces pages de votre historique ?</translation>
++<translation id="9111102763498581341">Déverrouiller</translation>
++<translation id="289695669188700754">ID de clé : <ph name="KEY_ID"/></translation>
++<translation id="3067198360141518313">Exécuter ce plug-in</translation>
++<translation id="8767072502252310690">Utilisateurs</translation>
++<translation id="683526731807555621">Ajouter un moteur</translation>
++<translation id="6871644448911473373">Répondeur OCSP : <ph name="LOCATION"/></translation>
++<translation id="8281886186245836920">Ignorer</translation>
++<translation id="3867944738977021751">Champs de certificat</translation>
++<translation id="2114224913786726438">Modules (<ph name="TOTAL_COUNT"/>) : aucun conflit détecté.</translation>
++<translation id="7629827748548208700">Onglet : <ph name="TAB_NAME"/></translation>
++<translation id="388442998277590542">Impossible de charger la page d'options &quot;<ph name="OPTIONS_PAGE"/>&quot;.</translation>
++<translation id="8449008133205184768">Coller en adaptant le style</translation>
++<translation id="9114223350847410618">Veuillez ajouter une autre langue avant de supprimer celle-ci.</translation>
++<translation id="4408427661507229495">nom du réseau</translation>
++<translation id="8886960478266132308"><ph name="PRODUCT_NAME"/> synchronise de manière sécurisée vos données avec votre compte Google.</translation>
++<translation id="8028993641010258682">Taille</translation>
++<translation id="5031603669928715570">Activer...</translation>
++<translation id="1383876407941801731">Recherche</translation>
++<translation id="8398877366907290961">Poursuivre quand même</translation>
++<translation id="5063180925553000800">Nouveau code PIN :</translation>
++<translation id="2496540304887968742">La capacité du périphérique doit être d'au moins 4 Go.</translation>
++<translation id="6974053822202609517">De droite à gauche</translation>
++<translation id="2370882663124746154">Activer le mode Pinyin double</translation>
++<translation id="5463856536939868464">Menu contenant des favoris masqués</translation>
++<translation id="8286227656784970313">Utiliser le dictionnaire système</translation>
++<translation id="5431084084184068621">Vous avez choisi de chiffrer les données à l'aide de votre mot de passe Google. Vous pouvez modifier vos paramètres de synchronisation à tout moment, si vous changez d'avis.</translation>
++<translation id="1493263392339817010">Personnaliser les polices...</translation>
++<translation id="5352033265844765294">Enregistrement des informations de date</translation>
++<translation id="6449085810994685586">&amp;Vérifier l'orthographe du texte de ce champ</translation>
++<translation id="3621320549246006887">Ceci est un modèle expérimental qui permet aux enregistrements DNS (utilisant le protocole de sécurité DNSSEC) d'autoriser ou de refuser des certificats HTTPS. Ce message s'affiche lorsque vous activez des fonctionnalités expérimentales via des options de ligne de commande. Vous pouvez supprimer ces options de ligne de commande pour ignorer cette erreur.</translation>
++<translation id="50960180632766478"><ph name="NUMBER_FEW"/> minutes restantes</translation>
++<translation id="3174168572213147020">Île</translation>
++<translation id="748138892655239008">Contraintes de base du certificat</translation>
++<translation id="457386861538956877">Autres...</translation>
++<translation id="8063491445163840780">Activer l'onglet 4</translation>
++<translation id="5966654788342289517">Données personnelles</translation>
++<translation id="9137013805542155359">Afficher l'original</translation>
++<translation id="4792385443586519711">Nom de la société</translation>
++<translation id="6423731501149634044">Définir Adobe Reader comme visionneuse de documents PDF par défaut ?</translation>
++<translation id="8839907368860424444">Pour gérer les extensions installées, cliquez sur Extensions dans le menu Fenêtre.</translation>
++<translation id="2461687051570989462">Accédez à vos imprimantes depuis n'importe quel ordinateur ou smartphone. <ph name="BEGIN_LINK"/>En savoir plus<ph name="END_LINK"/></translation>
++<translation id="7194430665029924274">Me &amp;le rappeler plus tard</translation>
++<translation id="5790085346892983794">Opération réussie !</translation>
++<translation id="1901769927849168791">Carte SD détectée.</translation>
++<translation id="818454486170715660"><ph name="NAME"/> - Propriétaire</translation>
++<translation id="1358032944105037487">Clavier japonais</translation>
++<translation id="8201956630388867069">WPA</translation>
++<translation id="603890000178803545">janv.^févr.^mars^avr.^mai^juin^juil.^août^sept.^oct.^nov.^déc.</translation>
++<translation id="8302838426652833913">Sélectionnez
++ <ph name="BEGIN_BOLD"/>
++ Applications &gt; Préférences système &gt; Réseau &gt; Assistant
++ <ph name="END_BOLD"/>
++ pour tester votre connexion.</translation>
++<translation id="8664389313780386848">&amp;Afficher le code source de la page</translation>
++<translation id="8970407809569722516">Micrologiciel :</translation>
++<translation id="1180549724812639004">Créer un profil</translation>
++<translation id="57646104491463491">Date de modification</translation>
++<translation id="5992752872167177798">Sandbox seccomp</translation>
++<translation id="6362853299801475928">Signale&amp;r un problème...</translation>
++<translation id="3289566588497100676">Entrée de symboles simplifiée</translation>
++<translation id="6507969014813375884">Chinois simplifié</translation>
++<translation id="7314244761674113881">Hôte SOCKS</translation>
++<translation id="5285794783728826432">Considérer ce certificat comme fiable pour identifier les sites Web.</translation>
++<translation id="4224803122026931301">Exceptions de localisation</translation>
++<translation id="749452993132003881">Hiragana</translation>
++<translation id="8226742006292257240">Le mot de passe TPM ci-dessous, généré de façon aléatoire, a été attribué à votre ordinateur :</translation>
++<translation id="8487693399751278191">Importer mes favoris maintenant...</translation>
++<translation id="7985242821674907985"><ph name="PRODUCT_NAME"/></translation>
++<translation id="7484580869648358686">Avertissement : Un problème a été détecté sur cette page.</translation>
++<translation id="2074739700630368799">Avec Google Chrome OS for business, vous pouvez connecter votre périphérique à Google Apps, ce qui vous permet de le rechercher et de le contrôler depuis le panneau de configuration de Google Apps.</translation>
++<translation id="4474155171896946103">Ajouter tous les onglets aux favoris...</translation>
++<translation id="5895187275912066135">Émis le</translation>
++<translation id="1190844492833803334">Lorsque je ferme le navigateur</translation>
++<translation id="5646376287012673985">Localisation</translation>
++<translation id="1110155001042129815">Attendre</translation>
++<translation id="2607101320794533334">Infos sur la clé publique de l'objet</translation>
++<translation id="7071586181848220801">Plug-in inconnu</translation>
++<translation id="3354601307791487577">Connexion en mode invité</translation>
++<translation id="4419409365248380979">Toujours autoriser <ph name="HOST"/> à paramétrer les cookies</translation>
++<translation id="2956070106555335453">Résumé</translation>
++<translation id="917450738466192189">Le certificat du serveur n'est pas valide.</translation>
++<translation id="2649045351178520408">Chaîne de certificats codés Base 64 ASCII</translation>
++<translation id="7424526482660971538">Choisir mon propre mot de passe multiterme</translation>
++<translation id="380271916710942399">Certificat de serveur non répertorié</translation>
++<translation id="6459488832681039634">Rechercher la sélection</translation>
++<translation id="2392369802118427583">Activer</translation>
++<translation id="9040421302519041149">L'accès à ce réseau est protégé.</translation>
++<translation id="5659593005791499971">E-mail</translation>
++<translation id="8235325155053717782">Erreur <ph name="ERROR_NUMBER"/> (<ph name="ERROR_NAME"/>) : <ph name="ERROR_TEXT"/></translation>
++<translation id="6584878029876017575">Signature permanente Microsoft</translation>
++<translation id="562901740552630300">Sélectionnez
++ <ph name="BEGIN_BOLD"/>
++ Démarrer &gt; Panneau de configuration &gt; Réseau et Internet &gt; Centre Réseau et partage &gt; Résolution des problèmes (en bas) &gt; Connexions Internet.
++ <ph name="END_BOLD"/></translation>
++<translation id="8816996941061600321">Gestionnaire de &amp;fichiers</translation>
++<translation id="2773223079752808209">Service client</translation>
++<translation id="4585473702689066695">Impossible de se connecter au réseau &quot;<ph name="NAME"/>&quot;.</translation>
++<translation id="4647175434312795566">J'accepte ces termes</translation>
++<translation id="1084824384139382525">Copier l'adr&amp;esse du lien</translation>
++<translation id="1221462285898798023">Veuillez démarrer <ph name="PRODUCT_NAME"/> en tant qu'utilisateur normal. Pour l'exécuter en tant que root, vous devez indiquer un autre répertoire de données utilisateur pour stocker les informations du profil.</translation>
++<translation id="3220586366024592812">Le processus du connecteur <ph name="CLOUD_PRINT_NAME"/> est bloqué. Voulez-vous le redémarrer ?</translation>
++<translation id="5042992464904238023">Contenu Web</translation>
++<translation id="6254503684448816922">Clé compromise</translation>
++<translation id="1181037720776840403">Supprimer</translation>
++<translation id="4006726980536015530">Si vous fermez <ph name="PRODUCT_NAME"/> maintenant, ces téléchargements seront annulés.</translation>
++<translation id="4194415033234465088">Dachen 26</translation>
++<translation id="1664712100580477121">Voulez-vous vraiment graver l'image sur le périphérique suivant :</translation>
++<translation id="6639554308659482635">Mémoire SQLite</translation>
++<translation id="8141503649579618569"><ph name="DOWNLOAD_RECEIVED"/>/<ph name="DOWNLOAD_TOTAL"/>, <ph name="TIME_LEFT"/></translation>
++<translation id="7650701856438921772"><ph name="PRODUCT_NAME"/> est affiché dans cette langue.</translation>
++<translation id="740624631517654988">Fenêtre pop-up bloquée</translation>
++<translation id="3738924763801731196"><ph name="OID"/> :</translation>
++<translation id="6550769511678490130">Ouvrir tous les favoris</translation>
++<translation id="1847961471583915783">Effacer les cookies et autres données de site et de plug-in lorsque je ferme le navigateur</translation>
++<translation id="8870318296973696995">Page d'accueil</translation>
++<translation id="6659594942844771486">Onglet</translation>
++<translation id="6575134580692778371">Non configuré</translation>
++<translation id="4624768044135598934">Opération réussie !</translation>
++<translation id="6014776969142880350">Relancez <ph name="PRODUCT_NAME"/> pour terminer la mise à jour.</translation>
++<translation id="5582768900447355629">Chiffrer toutes mes données</translation>
++<translation id="6122365914076864562">Veuillez patienter pendant que nous configurons votre réseau pour mobile.</translation>
++<translation id="1974043046396539880">Points de distribution de listes de révocation des certificats</translation>
++<translation id="7049357003967926684">Association</translation>
++<translation id="8641392906089904981">Appuyez sur Maj+Alt pour changer la disposition du clavier.</translation>
++<translation id="3024374909719388945">Utiliser l'horloge au format 24 heures</translation>
++<translation id="1867780286110144690"><ph name="PRODUCT_NAME"/> est prêt à terminer l'installation.</translation>
++<translation id="5316814419223884568">Lancez votre recherche à partir d'ici</translation>
++<translation id="8142732521333266922">OK, synchroniser tout</translation>
++<translation id="965674096648379287">Afin d'être correctement affichée, cette page requiert des données que vous avez précédemment entrées. Vous pouvez de nouveau transmettre ces données, mais, en procédant ainsi, vous devrez répéter chaque action que cette page a effectuée auparavant. Cliquez sur Rafraîchir pour transmettre de nouveau ces données et pour afficher cette page.</translation>
++<translation id="43742617823094120">Cela signifie que le certificat présenté à votre navigateur a été révoqué par son émetteur. L'intégrité de ce certificat a certainement été compromise, et il ne doit donc pas être approuvé. Ne poursuivez pas.</translation>
++<translation id="9019654278847959325">Clavier slovaque</translation>
++<translation id="18139523105317219">Nom de partie EDI</translation>
++<translation id="6657193944556309583">Vous avez déjà chiffré des données avec un mot de passe multiterme. Saisissez-le ci-dessous.</translation>
++<translation id="3328801116991980348">Informations sur le site</translation>
++<translation id="1205605488412590044">Créer un raccourci vers l'application...</translation>
++<translation id="2065985942032347596">Authentification requise</translation>
++<translation id="2553340429761841190"><ph name="PRODUCT_NAME"/> n'est pas parvenu à se connecter à <ph name="NETWORK_ID"/>. Sélectionnez un autre réseau ou réessayez.</translation>
++<translation id="2086712242472027775">Votre compte n'est pas compatible avec <ph name="PRODUCT_NAME"/>. Contactez l'administrateur de votre domaine ou utilisez un compte Google standard pour vous connecter.</translation>
++<translation id="7222232353993864120">Adresse e-mail</translation>
++<translation id="2128531968068887769">Client natif</translation>
++<translation id="7175353351958621980">Chargé depuis :</translation>
++<translation id="4590074117005971373">Active les balises canvas hautes performances dans un contexte 2D, pour effectuer le rendu via le processeur graphique.</translation>
++<translation id="7186367841673660872">Cette page en<ph name="ORIGINAL_LANGUAGE"/>a été traduite en<ph name="LANGUAGE_LANGUAGE"/></translation>
++<translation id="8448695406146523553">Seule une personne en possession de votre mot de passe multiterme peut lire vos données chiffrées. Google ne reçoit ni n'enregistre votre mot de passe multiterme. Si vous oubliez votre mot de passe multiterme, vous devrez</translation>
++<translation id="6052976518993719690">Autorité de certification SSL</translation>
++<translation id="1636959874332483835"><ph name="HOST_NAME"/> contient un logiciel malveillant. Votre ordinateur pourrait être infecté par un virus si vous consultez ce site.</translation>
++<translation id="8050783156231782848">Aucune donnée disponible.</translation>
++<translation id="1175364870820465910">Im&amp;primer...</translation>
++<translation id="3866249974567520381">Description</translation>
++<translation id="2900139581179749587">Voix non reconnue.</translation>
++<translation id="953692523250483872">Aucun fichier sélectionné</translation>
++<translation id="2294358108254308676">Souhaitez-vous installer <ph name="PRODUCT_NAME"/> ?</translation>
++<translation id="6549689063733911810">Activité récente</translation>
++<translation id="1529968269513889022">de la dernière semaine</translation>
++<translation id="5542132724887566711">Profil</translation>
++<translation id="5196117515621749903">Actualiser sans utiliser le cache</translation>
++<translation id="5552632479093547648">Logiciels malveillants et sites de phishing détectés !</translation>
++<translation id="4310537301481716192">Onglet fermé !</translation>
++<translation id="4988273303304146523">il y a <ph name="NUMBER_DEFAULT"/> jours</translation>
++<translation id="8428213095426709021">Paramètres</translation>
++<translation id="1588343679702972132">Ce site exige que vous vous identifiiez avec un certificat :</translation>
++<translation id="7211994749225247711">Supprimer...</translation>
++<translation id="2819994928625218237">&amp;Aucune suggestion orthographique</translation>
++<translation id="1065449928621190041">Clavier franco-canadien</translation>
++<translation id="8327626790128680264">Clavier étendu américain</translation>
++<translation id="2950186680359523359">Le serveur a mis fin à la connexion sans envoyer de données.</translation>
++<translation id="9142623379911037913">Autoriser <ph name="SITE"/> à afficher des notifications sur le Bureau ?</translation>
++<translation id="4196320913210960460">Pour gérer les extensions installées, cliquez sur Extensions dans le menu Outils.</translation>
++<translation id="3449494395612243720">Erreur de synchronisation, veuillez vous connecter à nouveau.</translation>
++<translation id="9118804773997839291">La liste suivante fait état des éléments dangereux détectés sur la page. Cliquez sur le lien &quot;Diagnostic&quot; pour obtenir plus d'informations sur un élément particulier.</translation>
++<translation id="7139724024395191329">Émirat</translation>
++<translation id="1761265592227862828">Synchroniser tous les paramètres et toutes les données\n(peut prendre un certain temps)</translation>
++<translation id="7754704193130578113">Toujours demander où enregistrer les fichiers</translation>
++<translation id="204914487372604757">Créer un raccourci</translation>
++<translation id="2497284189126895209">Tous les fichiers</translation>
++<translation id="696036063053180184">Sebeol-sik No-shift</translation>
++<translation id="452785312504541111">Anglais (pleine chasse)</translation>
++<translation id="945332329539165145">2D avec canvas et accélération matérielle</translation>
++<translation id="5220797120063118010">Cette fonctionnalité autorise l'installation d'applications Google Chrome déployées à partir d'un manifeste situé sur une page Web, plutôt qu'avec un fichier crx contenant le manifeste et les icônes.</translation>
++<translation id="9148126808321036104">Nouvelle connexion</translation>
++<translation id="2282146716419988068">GPU</translation>
++<translation id="428771275901304970">Moins de 1 Mo disponible</translation>
++<translation id="1682548588986054654">Nouvelle fenêtre de navigation privée</translation>
++<translation id="6833901631330113163">Europe du Sud</translation>
++<translation id="8691262314411702087">Sélectionner les éléments à synchroniser</translation>
++<translation id="6065289257230303064">Attributs du répertoire de l'objet du certificat</translation>
++<translation id="2423017480076849397">Accédez à vos imprimantes et partagez-les en ligne via <ph name="CLOUD_PRINT_NAME"/>.</translation>
++<translation id="569520194956422927">&amp;Ajouter...</translation>
++<translation id="4018133169783460046">Afficher <ph name="PRODUCT_NAME"/> dans cette langue</translation>
++<translation id="5110450810124758964">il y a <ph name="NUMBER_ONE"/> jour</translation>
++<translation id="3264544094376351444">Police Sans-Serif</translation>
++<translation id="5586942249556966598">Ne rien faire</translation>
++<translation id="2820806154655529776"><ph name="NUMBER_ONE"/> seconde</translation>
++<translation id="1077946062898560804">Configurer les mises à jour automatiques pour tous les utilisateurs</translation>
++<translation id="3122496702278727796">Échec de la création du répertoire des données</translation>
++<translation id="4517036173149081027">Fermer et annuler le chargement</translation>
++<translation id="7150146631451105528"><ph name="DATE"/></translation>
++<translation id="3166547286524371413">Adresse :</translation>
++<translation id="4522570452068850558">Détails</translation>
++<translation id="59659456909144943">Notification : <ph name="NOTIFICATION_NAME"/></translation>
++<translation id="6731320427842222405">Cette opération peut prendre quelques minutes.</translation>
++<translation id="4806525999832945986">Géré par <ph name="DOMAIN"/> (<ph name="STATUS"/>)</translation>
++<translation id="7503191893372251637">Type de certificat Netscape</translation>
++<translation id="1502960562739459116">Impossible d'afficher certaines parties de ce document PDF. Souhaitez-vous installer Adobe Reader ?</translation>
++<translation id="4135450933899346655">Vos certificats</translation>
++<translation id="4731578803613910821">Vos données personnelles sur <ph name="WEBSITE_1"/>, <ph name="WEBSITE_2"/> et <ph name="WEBSITE_3"/></translation>
++<translation id="7716781361494605745">URL de stratégie de l'autorité de certification Netscape</translation>
++<translation id="2881966438216424900">Dernier accès :</translation>
++<translation id="7552203043556919163">Synchroniser les mots de passe</translation>
++<translation id="630065524203833229">&amp;Quitter</translation>
++<translation id="4647090755847581616">&amp;Fermer l'onglet</translation>
++<translation id="2649204054376361687"><ph name="CITY"/>, <ph name="COUNTRY"/></translation>
++<translation id="7886758531743562066">Le site Web à l'adresse <ph name="HOST_NAME"/> contient des éléments provenant de sites qui semblent héberger des logiciels malveillants. Ces derniers peuvent nuire à votre ordinateur ou agir à votre insu. Le simple fait de visiter un site hébergeant ce type de logiciels peut infecter votre ordinateur.</translation>
++<translation id="2064746092913005102">Total : <ph name="NUMBER_OF_PAGES"/> <ph name="PAGE_OR_PAGES_LABEL"/> <ph name="TWO_SIDED"/> <ph name="TIMES"/> <ph name="NUMBER_OF_COPIES"/> <ph name="COPIES_LABEL"/> <ph name="EQUAL_SIGN"/> <ph name="NUMBER_OF_SHEETS"/> <ph name="SHEETS_LABEL"/></translation>
++<translation id="7538227655922918841">Les cookies de plusieurs sites ont été autorisés pour la session uniquement.</translation>
++<translation id="2385700042425247848">Nom du service :</translation>
++<translation id="7751005832163144684">Imprimer une page de test</translation>
++<translation id="3638865692466101147">Aperçu avant impression - <ph name="PREVIEW_TAB_TITLE"/></translation>
++<translation id="1471300011765310414"><ph name="PRODUCT_NAME"/>
++ ne peut pas à afficher la page Web, car votre ordinateur n'est pas connecté à Internet.</translation>
++<translation id="5464632865477611176">Exécuter cette fois</translation>
++<translation id="4268025649754414643">Chiffrement de la clé</translation>
++<translation id="7925247922861151263">Échec de la vérification AAA</translation>
++<translation id="1168020859489941584">Ouverture dans <ph name="TIME_REMAINING"/>...</translation>
++<translation id="7814458197256864873">&amp;Copier</translation>
++<translation id="8186706823560132848">Logiciel</translation>
++<translation id="4692623383562244444">Moteurs de recherche</translation>
++<translation id="567760371929988174">&amp;Méthodes d'entrée</translation>
++<translation id="10614374240317010">Jamais enregistrés</translation>
++<translation id="5116300307302421503">Impossible d'analyser le fichier.</translation>
++<translation id="2745080116229976798">Subordination qualifiée Microsoft</translation>
++<translation id="2526590354069164005">Bureau</translation>
++<translation id="7983301409776629893">Toujours traduire en <ph name="TARGET_LANGUAGE"/> les pages en <ph name="ORIGINAL_LANGUAGE"/></translation>
++<translation id="4890284164788142455">Thaï</translation>
++<translation id="4312207540304900419">Activer l'onglet suivant</translation>
++<translation id="8456362689280298700"><ph name="HOUR"/>:<ph name="MINUTE"/> de chargement</translation>
++<translation id="7648048654005891115">Style de mappage du clavier</translation>
++<translation id="539295039523818097">Un problème lié à votre microphone s'est produit.</translation>
++<translation id="4033319557821527966"><ph name="CLOUD_PRINT_NAME"/> vous permet d'accéder aux imprimantes de cet ordinateur, où que vous soyez. Connectez-vous pour l'activer.</translation>
++<translation id="6970216967273061347">District</translation>
++<translation id="4479639480957787382">Ethernet</translation>
++<translation id="6312403991423642364">Erreur de réseau inconnue.</translation>
++<translation id="751377616343077236">Nom du certificat</translation>
++<translation id="7154108546743862496">Plus d'informations</translation>
++<translation id="8637688295594795546">Mise à jour du système disponible. Préparation du téléchargement…</translation>
++<translation id="5167270755190684957">Galerie des thèmes Google Chrome</translation>
++<translation id="8382913212082956454">Copi&amp;er l'adresse e-mail</translation>
++<translation id="7447930227192971403">Activer l'onglet 3</translation>
++<translation id="2903493209154104877">Adresses</translation>
++<translation id="2056143100006548702">Plug-in : <ph name="PLUGIN_NAME"/> (<ph name="PLUGIN_VERSION"/>)</translation>
++<translation id="3479552764303398839">Pas maintenant</translation>
++<translation id="6445051938772793705">Pays</translation>
++<translation id="3251759466064201842">&lt;Ne fait pas partie du certificat&gt;</translation>
++<translation id="4229495110203539533">il y a <ph name="NUMBER_ONE"/> seconde</translation>
++<translation id="6410257289063177456">Fichiers image</translation>
++<translation id="6419902127459849040">Europe centrale</translation>
++<translation id="6707389671160270963">Certificat client SSL</translation>
++<translation id="6083557600037991373">Pour accélérer l'affichage des pages Web,
++ <ph name="PRODUCT_NAME"/>
++ enregistre temporairement les fichiers téléchargés sur le disque. Si
++ <ph name="PRODUCT_NAME"/>
++ ne s'arrête pas correctement, ces fichiers peuvent être endommagés, ce qui
++ génère cette erreur. L'actualisation de la page devrait permettre de résoudre
++ ce problème ; celui-ci ne se reproduira vraisemblablement plus si l'arrêt s'effectue
++ correctement.
++ <ph name="LINE_BREAK"/>
++ Si le problème persiste, essayez de supprimer le contenu du cache. Cette
++ erreur peut aussi indiquer que le matériel est sur le point de tomber
++ en panne.</translation>
++<translation id="5298219193514155779">Thème créé par</translation>
++<translation id="7366909168761621528">Données de navigation</translation>
++<translation id="1047726139967079566">Ajouter cette page aux favoris</translation>
++<translation id="9020142588544155172">Le serveur a refusé la connexion.</translation>
++<translation id="6113225828180044308">Module (<ph name="MODULUS_NUM_BITS"/> bits) :\n<ph name="MODULUS_HEX_DUMP"/>\n\nExposant public (<ph name="PUBLIC_EXPONENT_NUM_BITS"/> bits) :\n<ph name="EXPONENT_HEX_DUMP"/></translation>
++<translation id="2544782972264605588"><ph name="NUMBER_DEFAULT"/> secondes restantes</translation>
++<translation id="8871696467337989339">Vous utilisez un indicateur de ligne de commande non pris en charge : <ph name="BAD_FLAG"/>. La stabilité et la sécurité en seront affectées.</translation>
++<translation id="4767443964295394154">Emplacement de téléchargement</translation>
++<translation id="5031870354684148875">À propos de Google Traduction</translation>
++<translation id="720658115504386855">Les lettres ne sont pas sensibles à la casse.</translation>
++<translation id="2454247629720664989">Mot clé</translation>
++<translation id="3950820424414687140">Connexion</translation>
++<translation id="4626106357471783850">Redémarrez <ph name="PRODUCT_NAME"/> pour appliquer la mise à jour.</translation>
++<translation id="1697068104427956555">Sélectionner un carré dans l'image</translation>
++<translation id="2840798130349147766">Bases de données Web</translation>
++<translation id="1628736721748648976">Codage</translation>
++<translation id="1198271701881992799">Mise en route</translation>
++<translation id="782590969421016895">Utiliser les pages actuelles</translation>
++<translation id="6521850982405273806">Signaler une erreur</translation>
++<translation id="736515969993332243">Recherche de réseaux en cours</translation>
++<translation id="8026334261755873520">Effacer les données de navigation</translation>
++<translation id="1769104665586091481">Ouvrir le lien dans une nouvelle &amp;fenêtre</translation>
++<translation id="8503813439785031346">Nom d'utilisateur</translation>
++<translation id="5319782540886810524">Clavier letton</translation>
++<translation id="8651585100578802546">Forcer l'actualisation de cette page</translation>
++<translation id="685714579710025096">Disposition du clavier :</translation>
++<translation id="1361655923249334273">Non utilisé</translation>
++<translation id="290555789621781773"><ph name="NUMBER_TWO"/> minutes</translation>
++<translation id="5434065355175441495">Chiffrement RSA PKCS #1</translation>
++<translation id="7073704676847768330">Ce n'est probablement pas le site que vous recherchez !</translation>
++<translation id="8477384620836102176">&amp;Général</translation>
++<translation id="1074663319790387896">Configurer la synchronisation</translation>
++<translation id="4302315780171881488">État de connexion :</translation>
++<translation id="3391392691301057522">Ancien code PIN :</translation>
++<translation id="1344519653668879001">Désactiver le contrôle des liens hypertexte</translation>
++<translation id="6463795194797719782">&amp;Modifier</translation>
++<translation id="4262113024799883061">Chinois</translation>
++<translation id="4775879719735953715">Navigateur par défaut</translation>
++<translation id="5575473780076478375">Extension en mode navigation privée :<ph name="EXTENSION_NAME"/></translation>
++<translation id="4188026131102273494">Mot clé :</translation>
++<translation id="2930644991850369934">Un problème est survenu lors du téléchargement de l'image de récupération. La connexion réseau a été perdue.</translation>
++<translation id="3461610253915486539">Votre administrateur a désactivé certaines préférences.</translation>
++<translation id="5750053751252005701">Forfait de données <ph name="NETWORK"/> épuisé</translation>
++<translation id="8858939932848080433">Veuillez indiquer à quel niveau vous rencontrez des problèmes avant d'envoyer vos commentaires.</translation>
++<translation id="1720318856472900922">Authentification du serveur WWW TLS</translation>
++<translation id="8550022383519221471">Le service de synchronisation n'est pas disponible pour votre domaine.</translation>
++<translation id="3355823806454867987">Modifier les paramètres du proxy...</translation>
++<translation id="4780374166989101364">Cette fonctionnalité active les API des extensions expérimentales. Notez que vous ne pouvez pas mettre en ligne des extensions qui font appel aux API expérimentales dans la galerie d'extensions.</translation>
++<translation id="7227780179130368205">Un logiciel malveillant a été détecté !</translation>
++<translation id="435243347905038008">Forfait de données <ph name="NETWORK"/> presque épuisé</translation>
++<translation id="2489428929217601177">des dernières 24 heures</translation>
++<translation id="7418490403869327287">Une fois activée, la recherche instantanée charge la plupart des pages Web dès que vous saisissez l'URL dans le champ polyvalent, avant même que vous n'appuyiez sur Entrée. Si votre moteur de recherche par défaut est compatible, toute lettre saisie dans ce champ offre de nouveaux résultats et les prédictions intégrées vous guident dans vos recherches.\n\nChaque touche utilisée fait l'objet d'une requête, par conséquent il se peut que les éléments saisies dans le champ polyvalent soient enregistrés par votre moteur de recherche par défaut.\n</translation>
++<translation id="5149131957118398098"><ph name="NUMBER_ZERO"/> hours left</translation>
++<translation id="2541913031883863396">poursuivre quand même</translation>
++<translation id="4278390842282768270">Autorisé</translation>
++<translation id="2074527029802029717">Retirer l'onglet</translation>
++<translation id="1533897085022183721">Moins de <ph name="MINUTES"/></translation>
++<translation id="7503821294401948377">Impossible de charger l'icône &quot;<ph name="ICON"/>&quot; d'action du navigateur.</translation>
++<translation id="5539694491979265537">Consulter Google Dashboard</translation>
++<translation id="3942946088478181888">Plus d'informations</translation>
++<translation id="3722396466546931176">Ajoutez des langues puis faites-les glisser pour les classer dans l'ordre souhaité.</translation>
++<translation id="7396845648024431313"><ph name="APP_NAME"/> sera lancé au démarrage du système et continuera de s'exécuter en arrière-plan, même toutes les fenêtres de <ph name="PRODUCT_NAME"/> sont fermées.</translation>
++<translation id="8539727552378197395">Non (HttpOnly)</translation>
++<translation id="4519351128520996510">Saisir votre mot de passe multiterme pour la synchronisation</translation>
++<translation id="2391419135980381625">Police standard</translation>
++<translation id="7893393459573308604"><ph name="ENGINE_NAME"/> (par défaut)</translation>
++<translation id="5392544185395226057">Cette fonctionnalité active la prise en charge du client natif.</translation>
++<translation id="5400640815024374115">La puce du module de plate-forme sécurisée (TPM) est désactivée ou inexistante.</translation>
++<translation id="2151576029659734873">L'index de l'onglet indiqué est incorrect.</translation>
++<translation id="5150254825601720210">Nom du serveur SSL du certificat Netscape</translation>
++<translation id="6771503742377376720">Est une autorité de certification</translation>
++<translation id="8814190375133053267">Wi-Fi</translation>
++<translation id="2040078585890208937">Connexion à <ph name="NAME"/></translation>
++<translation id="8410619858754994443">Confirmer le mot de passe :</translation>
++<translation id="2210840298541351314">Aperçu avant impression</translation>
++<translation id="3858678421048828670">Clavier italien</translation>
++<translation id="4938277090904056629">Impossible d'établir une connexion sécurisée à cause de l'antivirus ESET.</translation>
++<translation id="4521805507184738876">(expiré)</translation>
++<translation id="111844081046043029">Voulez-vous vraiment quitter cette page ?</translation>
++<translation id="1951615167417147110">Faire défiler d'une page vers le haut</translation>
++<translation id="4154664944169082762">Empreintes</translation>
++<translation id="3202578601642193415">Le plus récent</translation>
++<translation id="8112886015144590373"><ph name="NUMBER_FEW"/> heures</translation>
++<translation id="1398853756734560583">Agrandir</translation>
++<translation id="8988255471271407508">La page Web est introuvable dans le cache. Certaines ressources ne sont restituées fidèlement que si elles sont extraites du cache, notamment les pages générées à partir de données que vous avez envoyées. <ph name="LINE_BREAK"/> Cette erreur peut également être due à un cache endommagé lors d'une fermeture incorrecte. <ph name="LINE_BREAK"/> Si le problème persiste, essayez d'effacer le cache.</translation>
++<translation id="1195977189444203128">Le plug-in <ph name="PLUGIN_NAME"/> n'est plus à jour.</translation>
++<translation id="3878562341724547165">Vous avez changé de position. Souhaitez-vous utiliser <ph name="NEW_GOOGLE_URL"/> ?</translation>
++<translation id="1758018619400202187">EAP-TLS</translation>
++<translation id="6690744523875189208"><ph name="NUMBER_TWO"/> heures</translation>
++<translation id="8053390638574070785">Rafraîchir cette page</translation>
++<translation id="5507756662695126555">Non-répudiation</translation>
++<translation id="3678156199662914018">Extension : <ph name="EXTENSION_NAME"/></translation>
++<translation id="9194519262242876737">Active l'API Web audio.</translation>
++<translation id="3531250013160506608">Zone de saisie de mot de passe</translation>
++<translation id="8314066201485587418">Effacer les cookies et autres données de site lorsque je quitte le navigateur</translation>
++<translation id="4094105377635924481">Ajouter l'option de regroupement au menu contextuel des onglets</translation>
++<translation id="8655295600908251630">Version</translation>
++<translation id="8250690786522693009">Latin</translation>
++<translation id="2119721408814495896">Le connecteur <ph name="CLOUD_PRINT_NAME"/> requiert l'installation du pack Microsoft XML Paper Specification Essentials.</translation>
++<translation id="7624267205732106503">Effacer les cookies et autres données de site lorsque je ferme le navigateur</translation>
++<translation id="8401363965527883709">Case décochée</translation>
++<translation id="7771452384635174008">Mise en page</translation>
++<translation id="6188939051578398125">Saisir un nom ou une adresse</translation>
++<translation id="8443621894987748190">Choix de l'image du compte</translation>
++<translation id="10122177803156699">Me montrer</translation>
++<translation id="5260878308685146029"><ph name="NUMBER_TWO"/> minutes restantes</translation>
++<translation id="2192505247865591433">De :</translation>
++<translation id="238391805422906964">Ouvrir un rapport de phishing</translation>
++<translation id="5921544176073914576">Page de phishing</translation>
++<translation id="3727187387656390258">Inspecter le pop-up</translation>
++<translation id="569068482611873351">Importer...</translation>
++<translation id="6571070086367343653">Modifier la carte de paiement</translation>
++<translation id="1204242529756846967">Cette langue est utilisée pour corriger l'orthographe.</translation>
++<translation id="3981760180856053153">Le type d'enregistrement indiqué est incorrect.</translation>
++<translation id="8464591670878858520">Forfait de données <ph name="NETWORK"/> arrivé à expiration</translation>
++<translation id="4568660204877256194">Exporter mes favoris...</translation>
++<translation id="3116361045094675131">Clavier britannique</translation>
++<translation id="4577070033074325641">Importer des favoris...</translation>
++<translation id="1641504961675316934"><ph name="CLOUD_PRINT_NAME"/></translation>
++<translation id="1715941336038158809">Nom d'utilisateur ou mot de passe incorrect</translation>
++<translation id="1901303067676059328">&amp;Tout sélectionner</translation>
++<translation id="674375294223700098">Erreur inconnue liée au certificat du serveur.</translation>
++<translation id="7780428956635859355">Envoyer une capture d'écran enregistrée</translation>
++<translation id="2850961597638370327">Émis pour : <ph name="NAME"/></translation>
++<translation id="2168039046890040389">Page précédente</translation>
++<translation id="1767519210550978135">Hsu</translation>
++<translation id="2498539833203011245">Réduire</translation>
++<translation id="2893168226686371498">Navigateur par défaut</translation>
++<translation id="2435457462613246316">Afficher le mot de passe</translation>
++<translation id="7988355189918024273">Activer les fonctionnalités d'accessibilité</translation>
++<translation id="5438653034651341183">Inclure la capture d'écran actuelle :</translation>
++<translation id="1899708097738826574"><ph name="OPTIONS_TITLE"/> - <ph name="SUBPAGE_TITLE"/></translation>
++<translation id="1765313842989969521">(cette extension est gérée et ne peut être désinstallée ni désactivée)</translation>
++<translation id="6983783921975806247">OID enregistré</translation>
++<translation id="394984172568887996">Importés depuis IE</translation>
++<translation id="5311260548612583999">Fichier de clé privée (facultatif) :</translation>
++<translation id="2430043402233747791">Autoriser pour la session uniquement</translation>
++<translation id="7363290921156020669"><ph name="NUMBER_ZERO"/> mins</translation>
++<translation id="7568790562536448087">Mise à jour en cours</translation>
++<translation id="4856408283021169561">Aucun microphone trouvé.</translation>
++<translation id="8190193592390505034">Connexion à <ph name="PROVIDER_NAME"/></translation>
++<translation id="6144890426075165477"><ph name="PRODUCT_NAME"/> n'est pas votre navigateur par défaut.</translation>
++<translation id="823241703361685511">Forfait</translation>
++<translation id="4068506536726151626">Cette page contient des éléments des sites ci-dessous qui suivent votre position géographique :</translation>
++<translation id="4721475475128190282">Plusieurs profils</translation>
++<translation id="4220128509585149162">Plantages</translation>
++<translation id="8798099450830957504">Par défaut</translation>
++<translation id="9107059250669762581"><ph name="NUMBER_DEFAULT"/> jours</translation>
++<translation id="1640283014264083726">PKCS #1 MD4 avec chiffrement RSA</translation>
++<translation id="872451400847464257">Modifier le moteur de recherche</translation>
++<translation id="6463061331681402734"><ph name="NUMBER_MANY"/> minutes</translation>
++<translation id="2466804342846034717">Indiquez le mot de passe approprié ci-dessus, puis saisissez les caractères figurant dans l'image ci-dessous.</translation>
++<translation id="3881435075661337013">Expiration de <ph name="NETWORK"/> imminente</translation>
++<translation id="5681833099441553262">Activer l'onglet précédent</translation>
++<translation id="4792057643643237295">Désactiver l'accès à distance</translation>
++<translation id="1681614449735360921">Afficher les incompatibilités</translation>
++<translation id="19094784437781028">Carte de débit Solo</translation>
++<translation id="2657327428424666237"><ph name="BEGIN_LINK"/>Actualisez<ph name="END_LINK"/> cette page Web ultérieurement.</translation>
++<translation id="7347751611463936647">Pour utiliser cette extension, saisissez &quot;<ph name="EXTENSION_KEYWORD"/>&quot;, TAB, puis votre commande ou votre recherche.</translation>
++<translation id="659432221160402784"><ph name="PRODUCT_NAME"/> synchronisera les applications installées, afin que vous puissiez y accéder en vous connectant depuis tout navigateur <ph name="PRODUCT_NAME"/>.</translation>
++<translation id="892464165639979917">Langues et paramètres du correcteur orthographique...</translation>
++<translation id="5645845270586517071">Erreur de sécurité</translation>
++<translation id="2805756323405976993">Applications</translation>
++<translation id="3651020361689274926">La ressource demandée n'existe plus et aucune adresse de transfert n'est disponible. Il semble que cet état de fait soit permanent.</translation>
++<translation id="2989786307324390836">Certificat unique binaire codé DER</translation>
++<translation id="3827774300009121996">&amp;Plein écran</translation>
++<translation id="3771294271822695279">Fichiers vidéo</translation>
++<translation id="6704875430222476107"><ph name="PRODUCT_NAME"/> indique que
++ NetNanny intercepte les connexions sécurisées. En général, cela
++ ne constitue pas un problème de sécurité, car le logiciel NetNanny s'exécute souvent
++ sur le même ordinateur. Toutefois, en raison de certaines incompatibilités avec
++ les connexions sécurisées Google Chrome, vous devez configurer NetNanny
++ de manière à éviter ces interceptions. Cliquez sur le lien En savoir plus pour obtenir des instructions.</translation>
++<translation id="3388026114049080752">Vos onglets et activités de navigation</translation>
++<translation id="7525067979554623046">Créer</translation>
++<translation id="4711094779914110278">Turc</translation>
++<translation id="1031460590482534116">Une erreur s'est produite lors de la tentative d'enregistrement du certificat client. Erreur <ph name="ERROR_NUMBER"/> (<ph name="ERROR_NAME"/>)</translation>
++<translation id="7136984461011502314">Bienvenue dans <ph name="PRODUCT_NAME"/></translation>
++<translation id="1594030484168838125">Sélectionner</translation>
++<translation id="204497730941176055">Nom du modèle de certificat Microsoft</translation>
++<translation id="6705264787989366486">Configuration de l'adresse IP pour <ph name="NAME"/></translation>
++<translation id="8970721300630048025">Immortalisez votre plus beau sourire et utilisez la photo comme image de compte.</translation>
++<translation id="4087089424473531098">Extension créée :
++
++<ph name="EXTENSION_FILE"/></translation>
++<translation id="16620462294541761">Mot de passe incorrect. Veuillez réessayer.</translation>
++<translation id="5017508259293544172">LEAP</translation>
++<translation id="1394630846966197578">Échec de la connexion aux serveurs de reconnaissance vocale.</translation>
++<translation id="2498765460639677199">Très grande</translation>
++<translation id="2378982052244864789">Sélectionner le répertoire de l'extension</translation>
++<translation id="7861215335140947162">&amp;Téléchargements</translation>
++<translation id="4778630024246633221">Gestionnaire des certificats</translation>
++<translation id="6705050455568279082"><ph name="URL"/> souhaite suivre votre position géographique</translation>
++<translation id="4708849949179781599">Quitter <ph name="PRODUCT_NAME"/></translation>
++<translation id="2505402373176859469"><ph name="RECEIVED_AMOUNT"/> sur <ph name="TOTAL_SIZE"/></translation>
++<translation id="6644512095122093795">Proposer d'enregistrer les mots de passe</translation>
++<translation id="4724450788351008910">Modification de l'affiliation</translation>
++<translation id="2249605167705922988">par exemple : 1-5, 8, 11-13</translation>
++<translation id="8691686986795184760">(Activé par une stratégie d'entreprise)</translation>
++<translation id="1911483096198679472">Qu'est-ce que c'est ?</translation>
++<translation id="1976323404609382849">Les cookies de plusieurs sites ont été bloqués.</translation>
++<translation id="2662952950313424742">Serveur DNS spécifié par l'utilisateur et utilisé par Google Chrome, à la place du paramètre système par défaut, pour les résolutions DNS.</translation>
++<translation id="4176463684765177261">Désactivé</translation>
++<translation id="2079545284768500474">Annuler</translation>
++<translation id="114140604515785785">Répertoire racine de l'extension :</translation>
++<translation id="4788968718241181184">Mode de saisie du vietnamien (TCVN6064)</translation>
++<translation id="1512064327686280138">Échec de l'activation</translation>
++<translation id="3254409185687681395">Ajouter cette page aux favoris</translation>
++<translation id="1384616079544830839">L'identité de ce site Web a été vérifiée par <ph name="ISSUER"/>.</translation>
++<translation id="8710160868773349942">Adresse e-mail : <ph name="EMAIL_ADDRESSES"/></translation>
++<translation id="4057991113334098539">Activation...</translation>
++<translation id="9073281213608662541">PAP</translation>
++<translation id="1800035677272595847">Sites de phishing</translation>
++<translation id="8448317557906454022"><ph name="NUMBER_ZERO"/> secs ago</translation>
++<translation id="402759845255257575">Interdire à tous les sites d'exécuter JavaScript</translation>
++<translation id="4610637590575890427">Vouliez-vous accéder à <ph name="SITE"/> ?</translation>
++<translation id="7723779034587221017">La connexion avec le service de configuration a été perdue. Veuillez réinitialiser votre périphérique ou contacter votre représentant de l'assistance technique.</translation>
++<translation id="3046388203776734202">Paramètres des fenêtres pop-up :</translation>
++<translation id="3437994698969764647">Tout exporter...</translation>
++<translation id="8349305172487531364">Barre de favoris</translation>
++<translation id="1898064240243672867">Stocké dans : <ph name="CERT_LOCATION"/></translation>
++<translation id="444134486829715816">Développer...</translation>
++<translation id="1401874662068168819">Gin Yieh</translation>
++<translation id="7208899522964477531">Rechercher <ph name="SEARCH_TERMS"/> sur <ph name="SITE_NAME"/></translation>
++<translation id="6255097610484507482">Modifier la carte de paiement</translation>
++<translation id="5584091888252706332">Au démarrage</translation>
++<translation id="8960795431111723921">Nous examinons actuellement le problème.</translation>
++<translation id="2482878487686419369">Notifications</translation>
++<translation id="8004582292198964060">Navigateur</translation>
++<translation id="695755122858488207">Case d'option décochée</translation>
++<translation id="6357135709975569075"><ph name="NUMBER_ZERO"/> days</translation>
++<translation id="8666678546361132282">Anglais</translation>
++<translation id="2224551243087462610">Modifier le nom du dossier</translation>
++<translation id="1358741672408003399">Grammaire et orthographe</translation>
++<translation id="4910673011243110136">Réseaux privés</translation>
++<translation id="2527167509808613699">Toutes sortes de connexions</translation>
++<translation id="9095710730982563314">Exceptions liées aux notifications</translation>
++<translation id="8072988827236813198">Épingler les onglets</translation>
++<translation id="1234466194727942574">Barre d'onglets</translation>
++<translation id="7974087985088771286">Activer l'onglet 6</translation>
++<translation id="4035758313003622889">Gestionnaire de &amp;tâches</translation>
++<translation id="6356936121715252359">Paramètres de stockage d'Adobe Flash Player...</translation>
++<translation id="5885996401168273077">Connexion au réseau</translation>
++<translation id="7313804056609272439">Mode de saisie du vietnamien (VNI)</translation>
++<translation id="1768211415369530011">L'application suivante va être lancée si vous acceptez cette requête :\n\n<ph name="APPLICATION"/></translation>
++<translation id="8793043992023823866">Importation...</translation>
++<translation id="8106211421800660735">N° de carte</translation>
++<translation id="2550839177807794974">Gérer les moteurs de recherche...</translation>
++<translation id="7031711645186424727">Utiliser un moniteur externe</translation>
++<translation id="6316768948917110108">Gravure de l'image en cours...</translation>
++<translation id="5089810972385038852">État</translation>
++<translation id="2872961005593481000">Éteindre</translation>
++<translation id="8986267729801483565">Enregistrer les fichiers dans le dossier :</translation>
++<translation id="4322394346347055525">Fermer les autres onglets</translation>
++<translation id="4411770745820968260">Répertoire de fichiers</translation>
++<translation id="881799181680267069">Masquer les autres</translation>
++<translation id="1812631533912615985">Annuler l'épinglage des onglets</translation>
++<translation id="6042308850641462728">Plus</translation>
++<translation id="8318945219881683434">Échec de la vérification de la révocation</translation>
++<translation id="1650709179466243265">Ajouter www. et .com, puis ouvrir la page</translation>
++<translation id="3524079319150349823">Pour inspecter un pop-up, cliquez avec le bouton droit sur la page ou sur l'icône d'action du navigateur, puis sélectionnez Inspecter le pop-up.</translation>
++<translation id="994289308992179865">&amp;Répéter</translation>
++<translation id="7793343764764530903"><ph name="CLOUD_PRINT_NAME"/> est à présent activé. <ph name="PRODUCT_NAME"/> a enregistré les imprimantes installées sur cette machine en les associant à &lt;b&gt;<ph name="EMAIL_ADDRESSES"/>&lt;/b&gt;. Vous pouvez désormais utiliser vos imprimantes depuis n'importe quelle application Web ou mobile associée à <ph name="CLOUD_PRINT_NAME"/>.</translation>
++<translation id="1703490097606704369">Le serveur de <ph name="HOST_NAME"/>
++ est introuvable, car la résolution DNS a échoué. DNS est le service Web qui
++ traduit les noms de site Web en adresses Internet. Cette erreur est
++ généralement due à l'absence de connexion Internet ou à une configuration
++ incorrecte du réseau. Cela peut également venir d'un serveur DNS qui ne
++ répond pas ou d'un pare-feu interdisant l'accès de
++ <ph name="PRODUCT_NAME"/>
++ au réseau.</translation>
++<translation id="8887090188469175989">ZGPY</translation>
++<translation id="3302709122321372472">Impossible de charger le fichier css &quot;<ph name="RELATIVE_PATH"/>&quot; du script de contenu.</translation>
++<translation id="305803244554250778">Créer des raccourcis vers des applications aux emplacements suivants :</translation>
++<translation id="574392208103952083">Moyenne</translation>
++<translation id="3745810751851099214">Envoyé pour :</translation>
++<translation id="3937609171782005782">Aider Google à détecter les logiciels malveillants en envoyant des données supplémentaires concernant les sites pour lesquels cet avertissement s'affiche. Ces données seront gérées conformément aux règles définies sur la page <ph name="PRIVACY_PAGE_LINK"/>.</translation>
++<translation id="8877448029301136595">[répertoire parent]</translation>
++<translation id="7301360164412453905">Touches de sélection du clavier Hsu</translation>
++<translation id="8631271110654520730">Copie de l'image de récupération...</translation>
++<translation id="1963227389609234879">Tout supprimer</translation>
++<translation id="7779140087128114262">Seule une personne en possession de votre mot de passe multiterme peut lire vos données chiffrées. Google ne reçoit ni n'enregistre votre mot de passe multiterme. Si vous oubliez votre mot de passe multiterme, vous devrez réinitialiser la synchronisation.</translation>
++<translation id="8027581147000338959">Ouvrir dans une nouvelle fenêtre</translation>
++<translation id="8019305344918958688">Dommage... Aucune extension n'est installée. :-(</translation>
++<translation id="7466861475611330213">Style de ponctuation</translation>
++<translation id="2496180316473517155">Historique de navigation</translation>
++<translation id="602251597322198729">Ce site tente de télécharger plusieurs fichiers. Voulez-vous autoriser le chargement ?</translation>
++<translation id="5843685321177053287">Établissement de la liaison avec le service de gestion des périphériques en attente...</translation>
++<translation id="2052389551707911401"><ph name="NUMBER_MANY"/> heures</translation>
++<translation id="5411472733320185105">Ne pas utiliser les paramètres du proxy pour les hôtes et domaines suivants :</translation>
++<translation id="6691936601825168937">&amp;Avancer</translation>
++<translation id="6566142449942033617">Impossible de charger &quot;<ph name="PLUGIN_PATH"/>&quot; pour le plug-in.</translation>
++<translation id="7065534935986314333">À propos du système</translation>
++<translation id="45025857977132537">Utilisation de la clé du certificat : <ph name="USAGES"/></translation>
++<translation id="6454421252317455908">Mode de saisie du chinois (quick)</translation>
++<translation id="368789413795732264">Une erreur s'est produite lors de la tentative d'écriture du fichier : <ph name="ERROR_TEXT"/>.</translation>
++<translation id="1173894706177603556">Renommer</translation>
++<translation id="5670032673361607750">La synchronisation requiert votre attention.</translation>
++<translation id="2148716181193084225">Aujourd'hui</translation>
++<translation id="1002064594444093641">Imp&amp;rimer le cadre...</translation>
++<translation id="7234674978021619913">Le site <ph name="HOST_NAME"/> a déjà été informé qu'un logiciel malveillant a été détecté sur ses pages. Pour plus d'informations concernant les problèmes rencontrés sur <ph name="HOST_NAME2"/>, consultez notre <ph name="DIAGNOSTIC_PAGE"/> Google.</translation>
++<translation id="8202390211066742724">Adresse de serveur DNS spécifiée par l'utilisateur.</translation>
++<translation id="4608500690299898628">&amp;Rechercher...</translation>
++<translation id="3574305903863751447"><ph name="CITY"/>, <ph name="STATE"/> <ph name="COUNTRY"/></translation>
++<translation id="8724859055372736596">&amp;Afficher dans le dossier</translation>
++<translation id="4605399136610325267">Non connecté à Internet.</translation>
++<translation id="978407797571588532">Sélectionnez
++ <ph name="BEGIN_BOLD"/>
++ Démarrer &gt; Panneau de configuration &gt; Connexions réseau &gt; Assistant Nouvelle connexion
++ <ph name="END_BOLD"/>
++ pour tester votre connexion.</translation>
++<translation id="5554489410841842733">Cette icône s'affiche lorsque l'extension peut agir sur la page active.</translation>
++<translation id="579702532610384533">Reconnexion</translation>
++<translation id="4862642413395066333">Réponses OCSP de signature</translation>
++<translation id="5266113311903163739">Erreur d'importation de l'autorité de certification</translation>
++<translation id="9563164493805065">Gravure de l'image terminée.</translation>
++<translation id="4756388243121344051">&amp;Historique</translation>
++<translation id="3789841737615482174">Installer</translation>
++<translation id="4320697033624943677">Ajouter des utilisateurs</translation>
++<translation id="9153934054460603056">Enregistrer l'authentification et le mot de passe</translation>
++<translation id="1455548678241328678">Clavier norvégien</translation>
++<translation id="2520481907516975884">Basculer en mode chinois/anglais</translation>
++<translation id="8571890674111243710">Traduction de la page en <ph name="LANGUAGE"/>...</translation>
++<translation id="4789872672210757069">À propos de &amp;<ph name="PRODUCT_NAME"/></translation>
++<translation id="4056561919922437609"><ph name="TAB_COUNT"/> onglets</translation>
++<translation id="4373894838514502496">il y a <ph name="NUMBER_FEW"/> minutes</translation>
++<translation id="6358450015545214790">Qu'est-ce que c'est ?</translation>
++<translation id="6264365405983206840">Tout &amp;sélectionner</translation>
++<translation id="1017280919048282932">&amp;Ajouter au dictionnaire</translation>
++<translation id="8319414634934645341">Utilisation étendue de la clé</translation>
++<translation id="4563210852471260509">Le chinois est la langue de saisie initiale</translation>
++<translation id="6897140037006041989">Agent utilisateur</translation>
++<translation id="3413122095806433232">Émetteurs de l'autorité de certification : <ph name="LOCATION"/></translation>
++<translation id="4115153316875436289"><ph name="NUMBER_TWO"/> jours</translation>
++<translation id="701080569351381435">Code source</translation>
++<translation id="3286538390144397061">Redémarrer maintenant</translation>
++<translation id="163309982320328737">La largeur de caractères initiale est Complète</translation>
++<translation id="5107325588313356747">Pour masquer l'accès à ce programme, vous devez le désinstaller au moyen de \n<ph name="CONTROL_PANEL_APPLET_NAME"/> du Panneau de configuration.\n\nSouhaitez-vous exécuter <ph name="CONTROL_PANEL_APPLET_NAME"/> ?</translation>
++<translation id="4841055638263130507">Paramètres du microphone</translation>
++<translation id="6965648386495488594">Port</translation>
++<translation id="7631887513477658702">&amp;Toujours ouvrir les fichiers de ce type</translation>
++<translation id="8627795981664801467">Uniquement les connexions sécurisées</translation>
++<translation id="8680787084697685621">Les informations de connexion au compte sont obsolètes.</translation>
++<translation id="3228969707346345236">La traduction a échoué, car la page est déjà en <ph name="LANGUAGE"/>.</translation>
++<translation id="1873879463550486830">Sandbox SUID</translation>
++<translation id="2190355936436201913">(vide)</translation>
++<translation id="8515737884867295000">Échec de l'authentification basée sur le certificat</translation>
++<translation id="5868426874618963178">Envoyer le code source de la page actuelle</translation>
++<translation id="1269138312169077280">Votre administrateur a désactivé certains paramètres.</translation>
++<translation id="5818003990515275822">Coréen</translation>
++<translation id="4182252350869425879">Avertissement : Il s'agit peut-être d'un site de phishing !</translation>
++<translation id="5458214261780477893">Dvorak</translation>
++<translation id="5353719617589986386">Étendue de pages incorrecte</translation>
++<translation id="1164369517022005061"><ph name="NUMBER_DEFAULT"/> heures restantes</translation>
++<translation id="5943260032016910017">Exceptions liées aux cookies et aux données de site</translation>
++<translation id="2214283295778284209"><ph name="SITE"/> n'est pas accessible</translation>
++<translation id="8755376271068075440">P&amp;lus grand</translation>
++<translation id="8132793192354020517">Connecté à <ph name="NAME"/></translation>
++<translation id="8187473050234053012">Le certificat de sécurité du site a été révoqué !</translation>
++<translation id="7444983668544353857">Désactiver <ph name="NETWORKDEVICE"/></translation>
++<translation id="6003177993629630467"><ph name="PRODUCT_NAME"/> risque de ne pas rester à jour.</translation>
++<translation id="421577943854572179">intégré sur tout autre site</translation>
++<translation id="580886651983547002"><ph name="PRODUCT_NAME"/>
++ ne parvient pas à atteindre le site Web. Cela vient probablement d'un problème de réseau,
++ mais peut également être dû à un pare-feu ou à un serveur proxy mal configuré.</translation>
++<translation id="5445557969380904478">À propos de la reconnaissance vocale</translation>
++<translation id="3093473105505681231">Langues et paramètres du correcteur orthographique...</translation>
++<translation id="152482086482215392"><ph name="NUMBER_ONE"/> seconde restante</translation>
++<translation id="529172024324796256">Nom d'utilisateur :</translation>
++<translation id="3308116878371095290">Le stockage des cookies n'est pas autorisé pour cette page.</translation>
++<translation id="7521387064766892559">JavaScript</translation>
++<translation id="7219179957768738017">La connexion utilise <ph name="SSL_VERSION"/>.</translation>
++<translation id="7014174261166285193">Échec de l'installation</translation>
++<translation id="1970746430676306437">Afficher les &amp;infos sur la page</translation>
++<translation id="3199127022143353223">Serveurs</translation>
++<translation id="2805646850212350655">Système de fichiers de chiffrement Microsoft </translation>
++<translation id="8053959338015477773">Un plug-in supplémentaire est requis pour afficher certains éléments sur cette page.</translation>
++<translation id="3541661933757219855">Appuyez sur Ctrl+Alt+/ ou sur Échap pour masquer</translation>
++<translation id="8813873272012220470">Cette fonctionnalité effectue des vérifications en arrière-plan et vous avertit en cas d'incompatibilité logicielle (modules tiers bloquant le navigateur, par exemple).</translation>
++<translation id="5020734739305654865">Connexion avec</translation>
++<translation id="2679385451463308372">Imprimer depuis la boîte de dialogue système…</translation>
++<translation id="7414887922320653780"><ph name="NUMBER_ONE"/> heure restante</translation>
++<translation id="121632099317611328">Échec de l'initialisation de l'appareil photo</translation>
++<translation id="399179161741278232">Importés</translation>
++<translation id="3829932584934971895">Type de fournisseur :</translation>
++<translation id="462288279674432182">IP restreinte :</translation>
++<translation id="3927932062596804919">Refuser</translation>
++<translation id="3524915994314972210">Démarrage du téléchargement en cours...</translation>
++<translation id="6484929352454160200">Une nouvelle version de <ph name="PRODUCT_NAME"/> est disponible.</translation>
++<translation id="3187212781151025377">Clavier hébreu</translation>
++<translation id="351152300840026870">Police à largeur fixe</translation>
++<translation id="5827266244928330802">Safari</translation>
++<translation id="778881183694837592">Les champs obligatoires ne doivent pas rester vides.</translation>
++<translation id="2371076942591664043">Ouvrir une fois le téléchargement &amp;terminé</translation>
++<translation id="3920504717067627103">Stratégies de certificat</translation>
++<translation id="155865706765934889">Pavé tactile</translation>
++<translation id="7701040980221191251">Aucun</translation>
++<translation id="5917011688104426363">Activer la barre d'adresse en mode recherche</translation>
++<translation id="6910239454641394402">Exceptions pour JavaScript</translation>
++<translation id="2979639724566107830">Ouvrir dans une nouvelle fenêtre</translation>
++<translation id="3269101346657272573">Veuillez saisir le code PIN.</translation>
++<translation id="9204065299849069896">Options de saisie automatique...</translation>
++<translation id="2822854841007275488">Arabe</translation>
++<translation id="5857090052475505287">Nouveau dossier</translation>
++<translation id="7450732239874446337">E/S réseau interrompue</translation>
++<translation id="5178667623289523808">Rechercher le précédent</translation>
++<translation id="2815448242176260024">Ne jamais enregistrer les mots de passe</translation>
++<translation id="2989805286512600854">Ouvrir dans un nouvel onglet</translation>
++<translation id="8687485617085920635">Fenêtre suivante</translation>
++<translation id="4122118036811378575">&amp;Rechercher le suivant</translation>
++<translation id="6008256403891681546">JCB</translation>
++<translation id="2610780100389066815">Signature de liste d'approbation Microsoft</translation>
++<translation id="8289811203643526145">Gérer les certificats...</translation>
++<translation id="2788575669734834343">Sélectionnez le fichier de certificat.</translation>
++<translation id="8404409224170843728">Fabricant :</translation>
++<translation id="8267453826113867474">Bloquer le contenu inapproprié</translation>
++<translation id="7959074893852789871">Le fichier contenait plusieurs certificats, dont certains n'ont pas été importés :</translation>
++<translation id="1213999834285861200">Exceptions pour les images</translation>
++<translation id="2805707493867224476">Autoriser tous les sites à afficher des fenêtres pop-up</translation>
++<translation id="3561217442734750519">Vous devez indiquer un chemin valide comme valeur de clé privée.</translation>
++<translation id="2444609190341826949">Sans mot de passe multiterme, vos mots de passe et autres données chiffrées ne seront pas synchronisés sur cet ordinateur.</translation>
++<translation id="77221669950527621">Extensions ou applications</translation>
++<translation id="6650142020817594541">Ce site recommande Google Chrome Frame (déjà installé).</translation>
++<translation id="6503077044568424649">Les plus visités</translation>
++<translation id="4625904365165566833">Vous n'êtes pas autorisé à vous connecter. Consultez le propriétaire de cet ordinateur portable.</translation>
++<translation id="7450633916678972976">Remarque : Lorsque vous cliquez sur &quot;Envoyer&quot;, Google Chrome joint à votre
++ envoi un journal indiquant votre version de Google Chrome et celle du système
++ d'exploitation utilisé, ainsi que l'URL associée à votre rapport. Vous pouvez
++ également joindre une capture d'écran. Ces informations nous
++ permettent de diagnostiquer les problèmes et d'améliorer les performances de
++ Google Chrome. Les informations personnelles fournies sciemment dans vos
++ commentaires ou involontairement dans le journal, l'URL ou la capture
++ d'écran sont protégées conformément à nos règles de
++ confidentialité. Si vous ne souhaitez pas indiquer d'URL et/ou de capture
++ d'écran, décochez les cases &quot;Inclure cette URL&quot; et/ou &quot;Inclure cette capture d'écran&quot;. Vous acceptez que Google utilise vos commentaires pour améliorer ses produits ou services.</translation>
++<translation id="465365366590259328">Vos modifications seront prises en compte au prochain démarrage de <ph name="PRODUCT_NAME"/>.</translation>
++<translation id="7168109975831002660">Taille de police minimale</translation>
++<translation id="7070804685954057874">Entrée directe</translation>
++<translation id="3265459715026181080">Fermer la fenêtre</translation>
++<translation id="6074871234879228294">Mode de saisie du japonais (pour clavier japonais)</translation>
++<translation id="7855296476260297092">Inscription réussie</translation>
++<translation id="907841381057066561">Échec de création du fichier zip temporaire lors de la création du pack</translation>
++<translation id="1294298200424241932">Modifier les paramètres de confiance :</translation>
++<translation id="1384617406392001144">Votre historique de navigation</translation>
++<translation id="3831099738707437457">&amp;Masquer le panneau de la vérification orthographique</translation>
++<translation id="1040471547130882189">Plug-in ne répondant pas</translation>
++<translation id="5473075389972733037">IBM</translation>
++<translation id="8307664665247532435">Les paramètres seront effacés lors de la prochaine actualisation.</translation>
++<translation id="790025292736025802"><ph name="URL"/> introuvable</translation>
++<translation id="895347679606913382">Démarrage...</translation>
++<translation id="3319048459796106952">Nouvelle fenêtre de nav&amp;igation privée</translation>
++<translation id="5832669303303483065">Ajouter une adresse postale...</translation>
++<translation id="3127919023693423797">Authentification en cours...</translation>
++<translation id="4195643157523330669">Ouvrir dans un nouvel onglet</translation>
++<translation id="8030169304546394654">Déconnecté</translation>
++<translation id="4010065515774514159">Action du navigateur</translation>
++<translation id="4286563808063000730">Le mot de passe multiterme saisi ne peut pas être utilisé, car vous avez déjà chiffré des données avec un mot de passe multiterme. Entrez ci-dessous le mot de passe multiterme actuellement défini pour la synchronisation.</translation>
++<translation id="1154228249304313899">Ouvrir cette page :</translation>
++<translation id="9074348188580488499">Voulez-vous vraiment supprimer tous les mots de passe ?</translation>
++<translation id="6635491740861629599">Sélectionner par domaine</translation>
++<translation id="3627588569887975815">Ouvrir le lien dans une fenêtre en navi&amp;gation privée</translation>
++<translation id="5851868085455377790">Émetteur</translation>
++<translation id="8223496248037436966">Options de saisie automatique</translation>
++<translation id="1470719357688513792">Les nouveaux paramètres des cookies seront appliqués quand vous aurez actualisé la page.</translation>
++<translation id="5578327870501192725">Votre connexion à <ph name="DOMAIN"/> est sécurisée par un chiffrement <ph name="BIT_COUNT"/> bits.</translation>
++<translation id="869884720829132584">Menu Applications</translation>
++<translation id="7764209408768029281">Outi&amp;ls</translation>
++<translation id="1139892513581762545">Onglets latéraux</translation>
++<translation id="7634357567062076565">Reprendre</translation>
++<translation id="4779083564647765204">Zoom</translation>
++<translation id="3282430104564575032">Inspecteur de DOM</translation>
++<translation id="1526560967942511387">Document sans titre</translation>
++<translation id="1291144580684226670">Police standard</translation>
++<translation id="3979748722126423326">Activer <ph name="NETWORKDEVICE"/></translation>
++<translation id="5538307496474303926">Opération en cours...</translation>
++<translation id="4367133129601245178">C&amp;opier l'URL de l'image</translation>
++<translation id="7542995811387359312">La saisie automatique des numéros de carte de paiement est désactivée, car la connexion utilisée par ce formulaire n'est pas sécurisée.</translation>
++<translation id="3494444535872870968">Enregistrer le &amp;cadre sous...</translation>
++<translation id="987264212798334818">Général</translation>
++<translation id="7005812687360380971">Défaillance</translation>
++<translation id="2356070529366658676">Demander</translation>
++<translation id="5731247495086897348">Coller l'URL et y a&amp;ccéder</translation>
++<translation id="8467548439852845758">Pour plus de sécurité, <ph name="PRODUCT_NAME"/> va chiffrer vos mots de passe.</translation>
++<translation id="2524947000814989347">Si vous avez oublié votre mot de passe multiterme, vous devrez arrêter la synchronisation via Google Dashboard.</translation>
++<translation id="8018154597338652331"><ph name="BURNT_AMOUNT"/> sur <ph name="TOTAL_SIZE"/></translation>
++<translation id="7635741716790924709">Adresse ligne 1</translation>
++<translation id="5135533361271311778">Impossible de créer le favori.</translation>
++<translation id="5271247532544265821">Basculer en mode chinois simplifié/traditionnel</translation>
++<translation id="2052610617971448509">Votre système Sandbox n'est pas correctement configuré.</translation>
++<translation id="7384913436093989340">Sélectionnez le <ph name="BEGIN_BOLD"/>menu clé à molette &gt; Préférences &gt; Options avancées &gt; Modifier les paramètres du proxy<ph name="END_BOLD"/> et vérifiez que vos paramètres sont définis sur &quot;sans proxy&quot; ou &quot;direct&quot;.</translation>
++<translation id="6417515091412812850">Impossible de vérifier si le certificat a été révoqué.</translation>
++<translation id="7282743297697561153">Stockage des données</translation>
++<translation id="3363332416643747536"><ph name="DOWNLOAD_RECEIVED"/>/<ph name="DOWNLOAD_TOTAL"/>, Interrompu</translation>
++<translation id="7347702518873971555">Acheter un forfait</translation>
++<translation id="5285267187067365830">Installer le plug-in...</translation>
++<translation id="5334844597069022743">Afficher le code source</translation>
++<translation id="1166212789817575481">Fermer les onglets sur la droite</translation>
++<translation id="6472893788822429178">Afficher le bouton &quot;Accueil&quot;</translation>
++<translation id="4270393598798225102">Version <ph name="NUMBER"/></translation>
++<translation id="534916491091036097">Parenthèse gche</translation>
++<translation id="4157869833395312646">Microsoft Server Gated Cryptography</translation>
++<translation id="8903921497873541725">Zoom avant</translation>
++<translation id="2195729137168608510">Protection du courrier électronique</translation>
++<translation id="1425734930786274278">Les cookies suivants ont été bloqués (tous les cookies tiers sont bloqués, sans exception) :</translation>
++<translation id="6805647936811177813">Connectez-vous à <ph name="TOKEN_NAME"/> pour importer le certificat client de <ph name="HOST_NAME"/></translation>
++<translation id="3437016096396740659">La batterie est chargée.</translation>
++<translation id="6916146760805488559">Créer un nouveau profil...</translation>
++<translation id="1199232041627643649">Maintenez la touche <ph name="KEY_EQUIVALENT"/> enfoncée pour quitter.</translation>
++<translation id="5428562714029661924">Masquer ce plug-in</translation>
++<translation id="7907591526440419938">Ouvrir le fichier</translation>
++<translation id="2568774940984945469">Conteneur de barres d'infos</translation>
++<translation id="8971063699422889582">Le certificat du serveur a expiré.</translation>
++<translation id="8281596639154340028">Utiliser <ph name="HANDLER_TITLE"/></translation>
++<translation id="7134098520442464001">Réduit la taille du texte</translation>
++<translation id="21133533946938348">Épingler l'onglet</translation>
++<translation id="1325040735987616223">Mise à jour du système</translation>
++<translation id="2864069933652346933"><ph name="NUMBER_ZERO"/> days left</translation>
++<translation id="9090669887503413452">Inclure les informations système</translation>
++<translation id="3084771660770137092">Google Chrome n'avait pas suffisamment de mémoire ou le processus de la page Web a été arrêté pour une autre raison. Pour continuer, actualisez la page ou ouvrez-en une autre.</translation>
++<translation id="1114901192629963971">Impossible de valider votre mot de passe sur le réseau actuel. Sélectionnez un autre réseau.</translation>
++<translation id="5179510805599951267">Cette page n'est pas rédigée en <ph name="ORIGINAL_LANGUAGE"/> ? Signaler l'erreur</translation>
++<translation id="6430814529589430811">Certificat unique codé Base 64 ASCII</translation>
++<translation id="5143712164865402236">Activer le mode plein écran</translation>
++<translation id="8434177709403049435">Codag&amp;e</translation>
++<translation id="4051923669149193910"><ph name="HANDLER_TITLE"/> est déjà utilisé pour gérer les liens <ph name="PROTOCOL"/>://.</translation>
++<translation id="2722201176532936492">Touches de sélection</translation>
++<translation id="385120052649200804">Clavier international américain</translation>
++<translation id="9012607008263791152">Je comprends que la visite de ce site peut être préjudiciable à mon ordinateur.</translation>
++<translation id="6640442327198413730">Ressource cache manquante.</translation>
++<translation id="1441458099223378239">Impossible d'accéder à mon compte</translation>
++<translation id="5793220536715630615">C&amp;opier l'URL de la vidéo</translation>
++<translation id="523397668577733901">Vous préférez <ph name="BEGIN_LINK"/>parcourir la galerie<ph name="END_LINK"/> ?</translation>
++<translation id="2922350208395188000">Impossible de vérifier le certificat du serveur.</translation>
++<translation id="3778740492972734840">Outils de &amp;développement</translation>
++<translation id="8335971947739877923">Exporter...</translation>
++<translation id="5680966941935662618">Paramètres de saisie automatique</translation>
++<translation id="38385141699319881">Téléchargement de l'image en cours...</translation>
++<translation id="6004539838376062211">&amp;Options du vérificateur d'orthographe</translation>
++<translation id="5350198318881239970">Impossible d'ouvrir votre profil correctement.\n\nIl est possible que certaines fonctionnalités ne soient pas disponibles. Vérifiez que ce profil existe et que vous disposez d'une autorisation d'accès à son contenu en lecture et en écriture.</translation>
++<translation id="4058793769387728514">Vérifier le document maintenant</translation>
++<translation id="1810107444790159527">Zone de liste</translation>
++<translation id="3338239663705455570">Clavier slovène</translation>
++<translation id="1859234291848436338">Sens de l'écriture</translation>
++<translation id="4567836003335927027">Vos données sur <ph name="WEBSITE_1"/></translation>
++<translation id="756445078718366910">Ouvrir une fenêtre du navigateur</translation>
++<translation id="4126154898592630571">Conversion de la date et de l'heure</translation>
++<translation id="5088534251099454936">PKCS #1 SHA-512 avec chiffrement RSA</translation>
++<translation id="6392373519963504642">Clavier coréen</translation>
++<translation id="7887334752153342268">Dupliquer</translation>
++<translation id="4980691186726139495">Ne pas conserver sur cette page</translation>
++<translation id="3081523290047420375">Désactiver <ph name="CLOUD_PRINT_NAME"/></translation>
++<translation id="9207194316435230304">ATOK</translation>
++<translation id="9026731007018893674">téléchargement</translation>
++<translation id="7646591409235458998">E-mail :</translation>
++<translation id="703748601351783580">Ouvrir tous les favoris dans une nouvelle &amp;fenêtre</translation>
++<translation id="6199775032047436064">Rafraîchir la page actuelle</translation>
++<translation id="6981982820502123353">Accessibilité</translation>
++<translation id="112343676265501403">Exceptions pour les plug-ins</translation>
++<translation id="770273299705142744">Remplissage automatique des formulaires</translation>
++<translation id="7210998213739223319">Nom d'utilisateur</translation>
++<translation id="4478664379124702289">Enregistrer le lie&amp;n sous...</translation>
++<translation id="8725066075913043281">Réessayer</translation>
++<translation id="8502249598105294518">Personnaliser et configurer <ph name="PRODUCT_NAME"/></translation>
++<translation id="7392089327262158658">Préférences de saisie automatique <ph name="PRODUCT_NAME_SHORT"/></translation>
++<translation id="4163521619127344201">Votre position géographique</translation>
++<translation id="3797008485206955964">Afficher les pages en arrière-plan (<ph name="NUM_BACKGROUND_APPS"/>)</translation>
++<translation id="8590375307970699841">Configurer les mises à jour automatiques</translation>
++<translation id="2797524280730715045">il y a <ph name="NUMBER_DEFAULT"/> heures</translation>
++<translation id="265390580714150011">Valeur du champ</translation>
++<translation id="9073247318500677671">Les dernières versions d'Unity et GNOME (ainsi que la prochaine version d'Ubuntu, Natty Narwhal) affichent une barre de menus de type OSX sur toute la largeur supérieure de l'écran.</translation>
++<translation id="3869917919960562512">Index erroné.</translation>
++<translation id="7031962166228839643">Préparation du module de plate-forme sécurisée (TPM) en cours. Veuillez patienter, l'opération peut prendre quelques minutes.</translation>
++<translation id="4250377793615429299">Nombre de copies incorrect</translation>
++<translation id="7180865173735832675">Personnaliser</translation>
++<translation id="5737306429639033676">Prédire les actions du réseau pour améliorer les performances de chargement des pages</translation>
++<translation id="8123426182923614874">Données restantes :</translation>
++<translation id="3707020109030358290">N'est pas une autorité de certification.</translation>
++<translation id="2115926821277323019">L'URL doit être valide.</translation>
++<translation id="8986494364107987395">Envoyer automatiquement les statistiques d'utilisation et les rapports d'erreur à Google</translation>
++<translation id="7070714457904110559">Cette fonctionnalité active la géolocalisation dans les extensions expérimentales. Cela implique l'utilisation des API de localisation du système d'exploitation (si disponibles) et l'envoi de données sur la configuration réseau locale au service de localisation de Google afin de déterminer une position précise.</translation>
++<translation id="6701535245008341853">Impossible de charger le profil.</translation>
++<translation id="527605982717517565">Toujours exécuter JavaScript sur <ph name="HOST"/></translation>
++<translation id="702373420751953740">Version PRL :</translation>
++<translation id="1307041843857566458">Confirmer la réactivation</translation>
++<translation id="8314308967132194952">Ajouter une adresse postale...</translation>
++<translation id="1221024147024329929">PKCS #1 MD2 avec chiffrement RSA</translation>
++<translation id="853265131227167869">Dim.^Lun.^Mar.^Mer.^Jeu.^Ven.^Sam.</translation>
++<translation id="3323447499041942178">Zone de saisie</translation>
++<translation id="580571955903695899">Trier par nom</translation>
++<translation id="5230516054153933099">Fenêtre</translation>
++<translation id="7554791636758816595">Nouvel onglet</translation>
++<translation id="5503844897713343920">Vous tentez d'accéder au site <ph name="DOMAIN"/>, mais le certificat présenté par le serveur a été révoqué par son émetteur. Cela signifie que les informations d'identification présentées par le serveur ne sont pas approuvées. Vous communiquez peut-être avec un pirate informatique. Nous vous déconseillons vivement de continuer.</translation>
++<translation id="6928853950228839340">Composition hors écran</translation>
++<translation id="1308727876662951186"><ph name="NUMBER_ZERO"/> mins left</translation>
++<translation id="7671576867600624">Technologie :</translation>
++<translation id="1103966635949043187">Accédez à la page d'accueil du site :</translation>
++<translation id="1951332921786364801">Configurer la communication à distance</translation>
++<translation id="1086613338090581534">L'émetteur d'un certificat n'ayant pas expiré est tenu d'assurer la maintenance de ce qui s'appelle &quot;une liste de révocation&quot;. Si un certificat est compromis, l'émetteur peut le révoquer en l'ajoutant à la liste de révocation. Ce certificat n'est alors plus approuvé par votre navigateur. Il n'est pas nécessaire d'assurer la maintenance de l'état &quot;révoqué&quot; des certificats expirés. Donc, bien qu'un certificat ait été qualifié de valide pour le site Web que vous visitez actuellement, il est impossible de déterminer s'il a été, depuis, compromis puis révoqué ou s'il est toujours valide. Par conséquent, il n'est pas possible de s'assurer si vous communiquez avec un site Web légitime ou si le certificat a été compromis et se trouve maintenant en la possession d'un pirate informatique avec lequel vous communiquez. Ne poursuivez pas.</translation>
++<translation id="2645575947416143543">Néanmoins, si vous travaillez dans une entreprise qui génère ses propres certificats, et que vous essayez de vous connecter au site Web interne de l'entreprise avec un certificat de ce type, vous pouvez résoudre ce problème en toute sécurité. Pour ce faire, importez le certificat racine de l'entreprise en tant que &quot;certificat racine&quot;. Par la suite, les certificats émis ou vérifiés par votre entreprise seront approuvés et vous ne verrez plus cette erreur lorsque vous tenterez de vous connecter à nouveau au site Web interne. Contactez le support informatique de votre entreprise pour savoir comment ajouter un nouveau certificat racine sur votre ordinateur.</translation>
++<translation id="376466258076168640">Définir <ph name="PRODUCT_NAME"/> en tant que navigateur par défaut</translation>
++<translation id="1056898198331236512">Avertissement</translation>
++<translation id="8630826211403662855">Préférences de recherche</translation>
++<translation id="8432745813735585631">Clavier Colemak américain</translation>
++<translation id="8151639108075998630">Activer la navigation en tant qu'invité</translation>
++<translation id="2608770217409477136">Utiliser les paramètres par défaut</translation>
++<translation id="3157931365184549694">Rétablir</translation>
++<translation id="7426243339717063209">Désinstaller &quot;<ph name="EXTENSION_NAME"/>&quot; ?</translation>
++<translation id="996250603853062861">Établissement de la connexion sécurisée...</translation>
++<translation id="6059232451013891645">Dossier :</translation>
++<translation id="4274292172790327596">Erreur non reconnue</translation>
++<translation id="760537465793895946">Consultez les conflits connus avec des modules tiers.</translation>
++<translation id="7042418530779813870">Co&amp;ller et rechercher</translation>
++<translation id="9110447413660189038">&amp;Remonter</translation>
++<translation id="375403751935624634">Échec de la traduction en raison d'une erreur de serveur</translation>
++<translation id="2101225219012730419">Version :</translation>
++<translation id="1570242578492689919">Polices et codage</translation>
++<translation id="3082374807674020857"><ph name="PAGE_TITLE"/> - <ph name="PAGE_URL"/></translation>
++<translation id="8050038245906040378">Signature du code commercial Microsoft</translation>
++<translation id="3031557471081358569">Sélectionnez les éléments à importer :</translation>
++<translation id="1368832886055348810">De gauche à droite</translation>
++<translation id="3031433885594348982">Votre connexion à <ph name="DOMAIN"/> est sécurisée par le biais d'un faible chiffrement.</translation>
++<translation id="4047345532928475040">sans objet</translation>
++<translation id="5604324414379907186">Toujours afficher la barre de favoris</translation>
++<translation id="3220630151624181591">Activer l'onglet 2</translation>
++<translation id="8898139864468905752">Aperçu des onglets</translation>
++<translation id="2799223571221894425">Redémarrer</translation>
++<translation id="5771816112378578655">Configuration en cours...</translation>
++<translation id="1197979282329025000">Une erreur s'est produite lors de la récupération des fonctions de l'imprimante <ph name="PRINTER_NAME"/>. Cette imprimante n'a pas pu être enregistrée dans <ph name="CLOUD_PRINT_NAME"/>.</translation>
++<translation id="8820901253980281117">Exceptions pour les fenêtres pop-up</translation>
++<translation id="1143142264369994168">Signataire du certificat </translation>
++<translation id="904949795138183864">La page Web <ph name="URL"/> n'existe plus.</translation>
++<translation id="3228279582454007836">Vous n'avez jamais visité ce site auparavant.</translation>
++<translation id="2159017110205600596">Personnaliser...</translation>
++<translation id="5449716055534515760">Fe&amp;rmer la fenêtre</translation>
++<translation id="2814489978934728345">Arrêter le chargement de cette page</translation>
++<translation id="2354001756790975382">Autres favoris</translation>
++<translation id="8561574028787046517"><ph name="PRODUCT_NAME"/> a été mis à jour.</translation>
++<translation id="5234325087306733083">Mode hors connexion</translation>
++<translation id="1779392088388639487">Erreur d'importation de fichier PKCS #12</translation>
++<translation id="166278006618318542">Algorithme de clé publique de l'objet</translation>
++<translation id="5759272020525228995">Le site Web a rencontré une erreur lors de l'extraction de <ph name="URL"/>.
++ Cela peut être dû à une opération de maintenance ou à une configuration incorrecte.</translation>
++<translation id="641480858134062906">Échec du chargement de la page <ph name="URL"/></translation>
++<translation id="3693415264595406141">Mot de passe :</translation>
++<translation id="74568296546932365">Conserver <ph name="PAGE_TITLE"/> en tant que moteur de recherche par défaut</translation>
++<translation id="8602184400052594090">Fichier manifeste absent ou illisible</translation>
++<translation id="2784949926578158345">La connexion a été réinitialisée.</translation>
++<translation id="6663792236418322902">Le mot de passe choisi vous sera demandé pour restaurer le fichier. Veillez à le conserver en lieu sûr.</translation>
++<translation id="4532822216683966758">La vérification de la provenance du certificat DNS est activée, ce qui peut entraîner l'envoi d'informations privées à Google.</translation>
++<translation id="6321196148033717308">À propos de la reconnaissance vocale</translation>
++<translation id="3412265149091626468">Aller à la sélection</translation>
++<translation id="8167737133281862792">Ajouter un certificat</translation>
++<translation id="2911372483530471524">Espaces de noms PID</translation>
++<translation id="6093374025603915876">Préférences de saisie automatique</translation>
++<translation id="8584134039559266300">Activer l'onglet 8</translation>
++<translation id="5189060859917252173">Le certificat &quot;<ph name="CERTIFICATE_NAME"/>&quot; représente une autorité de certification.</translation>
++<translation id="3785852283863272759">Envoyer par e-mail l'emplacement de la page</translation>
++<translation id="2255317897038918278">Enregistrement des informations de date Microsoft</translation>
++<translation id="3493881266323043047">Validité</translation>
++<translation id="5979421442488174909">&amp;Traduire en <ph name="LANGUAGE"/></translation>
++<translation id="7326526699920221209">Batterie : <ph name="PRECENTAGE"/> %</translation>
++<translation id="952992212772159698">Désactivé</translation>
++<translation id="8299269255470343364">Japonais</translation>
++<translation id="5187826826541650604"><ph name="KEY_NAME"/> (<ph name="DEVICE"/>)</translation>
++<translation id="6429639049555216915">L'application est actuellement inaccessible.</translation>
++<translation id="2144536955299248197">Lecteur du certificat : <ph name="CERTIFICATE_NAME"/></translation>
++<translation id="50030952220075532"><ph name="NUMBER_ONE"/> jour restant</translation>
++<translation id="2885378588091291677">Gestionnaire de tâches</translation>
++<translation id="5792852254658380406">Gérer les extensions...</translation>
++<translation id="2359808026110333948">Continuer</translation>
++<translation id="176759384517330673">Synchronisation avec <ph name="USER_EMAIL_ADDRESS"/> effectuée. Dernière synchronisation : <ph name="LAST_SYNC_TIME"/></translation>
++<translation id="1618661679583408047">Le certificat de sécurité du site n'est pas encore valide !</translation>
++<translation id="7039912931802252762">Ouverture de session par carte à puce Microsoft</translation>
++<translation id="6285074077487067719">Format</translation>
++<translation id="3065140616557457172">Tapez votre requête ou saisissez une URL pour commencer la navigation : c'est à vous de choisir.</translation>
++<translation id="5509693895992845810">Enregistrer &amp;sous...</translation>
++<translation id="5986279928654338866">Le serveur <ph name="DOMAIN"/> requiert un nom d'utilisateur et un mot de passe.</translation>
++<translation id="521467793286158632">Supprimer tous les mots de passe</translation>
++<translation id="2491120439723279231">Le certificat du serveur contient des erreurs.</translation>
++<translation id="4448844063988177157">Recherche de réseaux Wi-Fi...</translation>
++<translation id="5765780083710877561">Description :</translation>
++<translation id="338583716107319301">Séparateur</translation>
++<translation id="2079053412993822885">Si vous supprimez l'un de vos propres certificats, vous ne pouvez plus l'utiliser pour vous identifier.</translation>
++<translation id="7221869452894271364">Rafraîchir cette page</translation>
++<translation id="6791443592650989371">État d'activation :</translation>
++<translation id="4801257000660565496">Créer des raccourcis vers des applications</translation>
++<translation id="6503256918647795660">Clavier franco-suisse</translation>
++<translation id="6175314957787328458">GUID de domaine Microsoft</translation>
++<translation id="8179976553408161302">Entrer</translation>
++<translation id="8261506727792406068">Supprimer</translation>
++<translation id="4404805853119650018">Échec de l'enregistrement de cet ordinateur pour l'accès à distance.</translation>
++<translation id="345693547134384690">Ouvrir l'&amp;image dans un nouvel onglet</translation>
++<translation id="7422192691352527311">Préférences...</translation>
++<translation id="354211537509721945">L'administrateur a désactivé les mises à jour.</translation>
++<translation id="1375198122581997741">À propos de la version</translation>
++<translation id="7915471803647590281">Veuillez nous indiquer ce qu'il se passe avant d'envoyer votre rapport.</translation>
++<translation id="5725124651280963564">Connectez-vous à <ph name="TOKEN_NAME"/> afin de générer une clé pour <ph name="HOST_NAME"/>.</translation>
++<translation id="8418113698656761985">Clavier roumain</translation>
++<translation id="5976160379964388480">Autres</translation>
++<translation id="3665842570601375360">Sécurité :</translation>
++<translation id="1430915738399379752">Imprimer</translation>
++<translation id="7999087758969799248">Mode de saisie standard</translation>
++<translation id="2635276683026132559">Signature</translation>
++<translation id="4835836146030131423">Erreur lors de la connexion</translation>
++<translation id="7715454002193035316">Pour cette session uniquement</translation>
++<translation id="2475982808118771221">Une erreur s'est produite.</translation>
++<translation id="3324684065575061611">(Désactivé par une stratégie d'entreprise)</translation>
++<translation id="7385854874724088939">Erreur lors de la tentative d'impression. Vérifiez votre imprimante et réessayez.</translation>
++<translation id="770015031906360009">Grec</translation>
++<translation id="3834901049798243128">Ignorer les exceptions et bloquer l'enregistrement des cookies tiers</translation>
++<translation id="8116152017593700047">Cet outil vous permet de sélectionner une capture d'écran enregistrée. Aucune capture d'écran n'est disponible pour le moment. Appuyez simultanément sur Ctrl et sur la touche &quot;Mode Présentation&quot; pour enregistrer une capture d'écran. Vos trois dernières captures apparaissent ici.</translation>
++<translation id="3454157711543303649">Activation effectuée</translation>
++<translation id="884923133447025588">Aucun système de révocation trouvé</translation>
++<translation id="556042886152191864">Bouton</translation>
++<translation id="1352060938076340443">Interrompu</translation>
++<translation id="8571226144504132898">Dictionnaire de symboles</translation>
++<translation id="7229570126336867161">Technologie EvDo requise</translation>
++<translation id="7582844466922312471">Internet mobile</translation>
++<translation id="945522503751344254">Envoyer le commentaire</translation>
++<translation id="4539401194496451708">Associé au profil Chrome <ph name="USER_EMAIL_ADDRESS"/>. Dernière synchronisation : <ph name="LAST_SYNC_TIME"/></translation>
++<translation id="7369847606959702983">Carte de crédit (autre)</translation>
++<translation id="6867459744367338172">Langues et saisie</translation>
++<translation id="7671130400130574146">Utiliser la barre de titre et les bordures de fenêtre du système</translation>
++<translation id="9170848237812810038">Ann&amp;uler</translation>
++<translation id="284970761985428403"><ph name="ASCII_NAME"/> (<ph name="UNICODE_NAME"/>)</translation>
++<translation id="3903912596042358459">Le serveur a refusé d'exécuter la demande.</translation>
++<translation id="8135557862853121765"><ph name="NUM_KILOBYTES"/> Ko</translation>
++<translation id="4444364671565852729"><ph name="PRODUCT_NAME"/> a été mis à jour vers la version <ph name="VERSION"/>.</translation>
++<translation id="5819890516935349394">Navigateur de contenu</translation>
++<translation id="2731392572903530958">&amp;Rouvrir la fenêtre fermée</translation>
++<translation id="1254593899333212300">Se connecter directement à Internet</translation>
++<translation id="6107012941649240045">Émis pour</translation>
++<translation id="6483805311199035658">Ouverture de <ph name="FILE"/> en cours</translation>
++<translation id="3576278878016363465">Cibles disponibles pour l'image</translation>
++<translation id="895541991026785598">Signaler un problème</translation>
++<translation id="940425055435005472">Taille de police :</translation>
++<translation id="494286511941020793">Aide pour la configuration de proxy</translation>
++<translation id="2765217105034171413">Petite</translation>
++<translation id="1285266685456062655"><ph name="NUMBER_FEW"/> hours ago</translation>
++<translation id="9154176715500758432">Rester sur cette page</translation>
++<translation id="5875565123733157100">Type de bug :</translation>
++<translation id="6988771638657196063">Inclure cette URL :</translation>
++<translation id="5717920936024713315">Cookies et données de site...</translation>
++<translation id="3842552989725514455">Police Serif</translation>
++<translation id="1949795154112250744"><ph name="BEGIN_BOLD"/>Avertissement :<ph name="END_BOLD"/> <ph name="PRODUCT_NAME"/> ne peut pas empêcher les extensions d'enregistrer votre historique de navigation. Pour désactiver cette extension en mode navigation privée, désélectionnez-la.</translation>
++<translation id="4440967101351338638">ChromiumOs Image Burn</translation>
++<translation id="1813278315230285598">Services</translation>
++<translation id="6860097299815761905">Paramètres du proxy...</translation>
++<translation id="373572798843615002">1 onglet</translation>
++<translation id="4162393307849942816"><ph name="BEGIN_BOLD"/>Vous naviguez en tant qu'invité<ph name="END_BOLD"/>. Les pages que vous consultez dans cette fenêtre n'apparaîtront pas dans l'historique de votre navigateur ni dans votre historique des recherches. Les autres traces telles que les cookies seront supprimées de l'ordinateur à la fin de votre session. En revanche, les fichiers téléchargés et les favoris créés seront conservés.
++ <ph name="LINE_BREAK"/>
++ <ph name="BEGIN_LINK"/>En savoir plus<ph name="END_LINK"/> sur le mode invité</translation>
++<translation id="827924395145979961">Chargement des pages impossible</translation>
++<translation id="3092544800441494315">Inclure cette capture d'écran :</translation>
++<translation id="7714464543167945231">Certificat</translation>
++<translation id="3616741288025931835">&amp;Effacer les données de navigation...</translation>
++<translation id="3313622045786997898">Valeur de signature du certificat</translation>
++<translation id="8535005006684281994">URL de renouvellement du certificat Netscape</translation>
++<translation id="2440604414813129000">Afficher la s&amp;ource</translation>
++<translation id="816095449251911490"><ph name="SPEED"/> - <ph name="RECEIVED_AMOUNT"/>, <ph name="TIME_REMAINING"/></translation>
++<translation id="8200772114523450471">Reprendre</translation>
++<translation id="6358975074282722691"><ph name="NUMBER_TWO"/> secs ago</translation>
++<translation id="5423849171846380976">Activé</translation>
++<translation id="6748105842970712833">Carte SIM désactivée</translation>
++<translation id="7323391064335160098">Compatibilité avec VPN</translation>
++<translation id="3929673387302322681">Développement - Instable</translation>
++<translation id="4251486191409116828">Échec de création du raccourci vers l'application</translation>
++<translation id="5190835502935405962">Barre de favoris</translation>
++<translation id="7828272290962178636">Le serveur est en mesure de répondre à la demande.</translation>
++<translation id="7823073559911777904">Modifier les paramètres du proxy...</translation>
++<translation id="5438430601586617544">(non empaquetée)</translation>
++<translation id="6460601847208524483">Rechercher le suivant</translation>
++<translation id="8433186206711564395">Paramètres réseau</translation>
++<translation id="4856478137399998590">Votre service Internet mobile est activé et prêt à l'emploi.</translation>
++<translation id="1676388805288306495">Modifier la police et la langue par défaut des pages Web</translation>
++<translation id="8969761905474557563">Composition graphique avec accélération matérielle</translation>
++<translation id="3937640725563832867">Autre nom de l'émetteur du certificat</translation>
++<translation id="4701488924964507374"><ph name="SENTENCE1"/> <ph name="SENTENCE2"/></translation>
++<translation id="1163931534039071049">&amp;Afficher le code source du cadre</translation>
++<translation id="8770196827482281187">Mode de saisie du persan (clavier ISIRI 2901)</translation>
++<translation id="6423239382391657905">OpenVPN</translation>
++<translation id="7564847347806291057">Arrêter le processus</translation>
++<translation id="1607220950420093847">Votre compte a peut-être été supprimé ou désactivé. Veuillez vous déconnecter.</translation>
++<translation id="5613695965848159202">Authentification anonyme :</translation>
++<translation id="2233320200890047564">Bases de données indexées</translation>
++<translation id="7063412606254013905">En savoir plus sur les escroqueries par phishing</translation>
++<translation id="307767688111441685">Page à l'apparence anormale</translation>
++<translation id="9076523132036239772">Adresse e-mail ou mot de passe incorrect. Essayez tout d'abord de vous connecter à un réseau.</translation>
++<translation id="6965978654500191972">Périphérique</translation>
++<translation id="1242521815104806351">Informations sur la connexion</translation>
++<translation id="5295309862264981122">Confirmer la navigation</translation>
++<translation id="1492817554256909552">Nom du point d'accès :</translation>
++<translation id="5546865291508181392">Rechercher</translation>
++<translation id="1999115740519098545">Au démarrage</translation>
++<translation id="2983818520079887040">Paramètres...</translation>
++<translation id="1465619815762735808">Lire en un clic</translation>
++<translation id="6941937518557314510">Connectez-vous à <ph name="TOKEN_NAME"/> pour vous authentifier auprès de <ph name="HOST_NAME"/> avec votre certificat.</translation>
++<translation id="2783600004153937501">Votre administrateur informatique a désactivé certaines options.</translation>
++<translation id="2099686503067610784">Supprimer le certificat de serveur &quot;<ph name="CERTIFICATE_NAME"/>&quot;?</translation>
++<translation id="9027603907212475920">Configurer la synchronisation...</translation>
++<translation id="6873213799448839504">Valider automatiquement une chaîne</translation>
++<translation id="7238585580608191973">Empreinte SHA-256</translation>
++<translation id="2501278716633472235">Retour</translation>
++<translation id="131461803491198646">Réseau domestique, sans itinérance</translation>
++<translation id="7377249249140280793"><ph name="RELATIVE_DATE"/> - <ph name="FULL_DATE"/></translation>
++<translation id="5679279978772703611">Gérer les mots de passe enregistrés...</translation>
++<translation id="4551440281920791563">Sélectionnez
++ <ph name="BEGIN_BOLD"/>
++ Menu clé à molette &gt; Options &gt; Options avancées &gt; Modifier les paramètres du proxy &gt; Paramètres réseau
++ <ph name="END_BOLD"/>
++ et désélectionnez l'option &quot;Utiliser un serveur proxy pour votre réseau local&quot;.</translation>
++<translation id="1285320974508926690">Ne jamais traduire ce site</translation>
++<translation id="8954894007019320973">(suite)</translation>
++<translation id="3748412725338508953">Trop de redirections</translation>
++<translation id="5833726373896279253">Ces paramètres ne peuvent être modifiés que par le propriétaire :</translation>
++<translation id="6858960932090176617">Active la protection XSS Auditor de WebKit (protection contre le Cross-site Scripting), une fonctionnalité qui vous protège de certaines attaques de sites malveillants et offre une sécurité accrue, mais qui n'est pas compatible avec tous les sites Web.</translation>
++<translation id="6005282720244019462">Clavier latino-américain</translation>
++<translation id="8831104962952173133">Phishing détecté !</translation>
++<translation id="5141720258550370428">Voulez-vous utiliser <ph name="HANDLER_TITLE"/> (<ph name="HANDLER_HOSTNAME"/>) pour gérer les liens <ph name="PROTOCOL"/>:// à partir de maintenant ?</translation>
++<translation id="6751344591405861699"><ph name="WINDOW_TITLE"/> (Navigation privée)</translation>
++<translation id="6681668084120808868">Prendre une photo</translation>
++<translation id="780301667611848630">Non merci</translation>
++<translation id="2812989263793994277">Ne pas afficher les images</translation>
++<translation id="7190251665563814471">Toujours autoriser ces plug-ins sur <ph name="HOST"/></translation>
++<translation id="6845383723252244143">Sélectionner un dossier</translation>
++<translation id="8925458182817574960">&amp;Paramètres</translation>
++<translation id="6361850914223837199">Informations sur l'erreur :</translation>
++<translation id="8948393169621400698">Toujours autoriser les plug-ins sur <ph name="HOST"/></translation>
++<translation id="3865082058368813534">Effacer les données de saisie automatique enregistrées</translation>
++<translation id="8288345061925649502">Changer de moteur de recherche</translation>
++<translation id="5436492226391861498">En attente du tunnel proxy...</translation>
++<translation id="3803991353670408298">Veuillez ajouter un autre mode de saisie avant de supprimer celui-ci.</translation>
++<translation id="1095623615273566396"><ph name="NUMBER_FEW"/> secondes</translation>
++<translation id="7006788746334555276">Paramètres de contenu</translation>
++<translation id="3369521687965833290">Impossible d'extraire les fichiers de l'extension. Pour effectuer cette opération en toute sécurité, vous devez disposer d'un chemin d'accès à votre répertoire de profils commençant par une lettre de lecteur et ne contenant ni jonction, ni point de montage, ni lien symbolique. Aucun chemin de ce type n'existe pour votre profil.</translation>
++<translation id="337920581046691015"><ph name="PRODUCT_NAME"/> va être installé.</translation>
++<translation id="6282194474023008486">Code postal</translation>
++<translation id="7733107687644253241">En bas à droite</translation>
++<translation id="5139955368427980650">&amp;Ouvrir</translation>
++<translation id="8136149669168180907"><ph name="DOWNLOADED_AMOUNT"/> téléchargé(s) sur <ph name="TOTAL_SIZE"/></translation>
++<translation id="7375268158414503514">Commentaires d'ordre général/Autres</translation>
++<translation id="4643612240819915418">Ou&amp;vrir la vidéo dans un nouvel onglet</translation>
++<translation id="7997479212858899587">Identité :</translation>
++<translation id="8300849813060516376">Échec de l'opération OTASP</translation>
++<translation id="2213819743710253654">Action sur la page</translation>
++<translation id="1317130519471511503">Modifier des éléments...</translation>
++<translation id="6391538222494443604">Le répertoire d'extensions est obligatoire.</translation>
++<translation id="8051695050440594747"><ph name="MEGABYTES"/> Mo disponibles</translation>
++<translation id="7088615885725309056">Ancien</translation>
++<translation id="461656879692943278"><ph name="HOST_NAME"/> fournit du contenu provenant de <ph name="ELEMENTS_HOST_NAME"/>, un site connu pour distribuer des logiciels malveillants. Votre ordinateur pourrait être infecté par un virus si vous consultez ce site.</translation>
++<translation id="1387022316521171484">Ces fonctionnalités expérimentales sont susceptibles d'être modifiées, interrompues ou supprimées à tout moment. Nous ne fournissons aucune garantie quant aux effets de leur activation. Votre navigateur pourrait bien prendre feu. Trêve de plaisanterie, il est possible que votre navigateur supprime toutes vos données ou que votre sécurité et votre vie privée soient compromises de manière inattendue. Nous vous prions d'agir avec précaution.</translation>
++<translation id="2143778271340628265">Configuration manuelle du proxy</translation>
++<translation id="5294529402252479912">Mettre à jour Adobe Reader maintenant</translation>
++<translation id="5263972071113911534"><ph name="NUMBER_MANY"/> days ago</translation>
++<translation id="7461850476009326849">Désactiver les plug-ins individuels...</translation>
++<translation id="4097411759948332224">Envoyer une capture d'écran de la page en cours</translation>
++<translation id="2231990265377706070">Point d'exclamation</translation>
++<translation id="7199540622786492483"><ph name="PRODUCT_NAME"/> n'est plus à jour, car il n'a pas été relancé depuis quelque temps. La mise à jour disponible sera installée dès que vous le relancerez.</translation>
++<translation id="8652722422052983852">Petit problème... Tentons de le résoudre.</translation>
++<translation id="5970231080121144965">Cette fonctionnalité permet d'établir des correspondances entre les sous-chaînes et plusieurs fragments d'URL figurant dans l'historique.</translation>
++<translation id="4665674675433053715">Page &quot;Nouvel onglet&quot; expérimentale</translation>
++<translation id="3726527440140411893">Les cookies suivants étaient autorisés lorsque vous avez consulté cette page :</translation>
++<translation id="3320859581025497771">votre opérateur</translation>
++<translation id="8828781037212165374">Activer ces fonctionnalités...</translation>
++<translation id="8562413501751825163">Quitter Firefox avant l'importation</translation>
++<translation id="3435541101098866721">Ajouter un nouveau téléphone</translation>
++<translation id="2448046586580826824">Proxy HTTP sécurisé</translation>
++<translation id="4032534284272647190">Accès à <ph name="URL"/> refusé.</translation>
++<translation id="4928569512886388887">Finalisation de la mise à jour du système...</translation>
++<translation id="8258002508340330928">Voulez-vous continuer ?</translation>
++<translation id="4309420042698375243"><ph name="NUM_KILOBYTES"/> Ko (<ph name="NUM_KILOBYTES_LIVE"/> Ko effectifs)</translation>
++<translation id="5554573843028719904">Autre réseau Wi-Fi...</translation>
++<translation id="5034259512732355072">Choisir un autre répertoire...</translation>
++<translation id="8885905466771744233">L'extension indiquée est déjà associée à une clé privée. Utilisez cette clé ou supprimez-la.</translation>
++<translation id="7831504847856284956">Ajouter une adresse</translation>
++<translation id="7505152414826719222">Stockage local</translation>
++<translation id="2541423446708352368">Afficher tous les téléchargements</translation>
++<translation id="4381021079159453506">Navigateur de contenu</translation>
++<translation id="8109246889182548008">Magasin de certificats</translation>
++<translation id="5030338702439866405">Émis par</translation>
++<translation id="5280833172404792470">Quitter le mode plein écran (<ph name="ACCELERATOR"/>)</translation>
++<translation id="2728127805433021124">Le certificat du serveur a été signé avec un algorithme de signature faible.</translation>
++<translation id="2137808486242513288">Ajouter un utilisateur</translation>
++<translation id="6193618946302416945">Me proposer de traduire les pages qui sont écrites dans une langue que je ne sais pas lire</translation>
++<translation id="129553762522093515">Récemment fermés</translation>
++<translation id="4287167099933143704">Saisir la clé de déverrouillage du code PIN</translation>
++<translation id="8355915647418390920"><ph name="NUMBER_FEW"/> jours</translation>
++<translation id="3129140854689651517">Rechercher du texte</translation>
++<translation id="7221585318879598658">Sans-Serif</translation>
++<translation id="5558129378926964177">Zoom &amp;avant</translation>
++<translation id="6451458296329894277">Confirmer le nouvel envoi du formulaire</translation>
++<translation id="5116333507878097773"><ph name="NUMBER_ONE"/> heure</translation>
++<translation id="8028152732786498049">Cet élément doit être installé depuis <ph name="CHROME_WEB_STORE"/>.</translation>
++<translation id="9199258761842902152">Mise en veille ou reprise</translation>
++<translation id="1851266746056575977">Mettre à jour maintenant</translation>
++<translation id="7017219178341817193">Ajouter une page</translation>
++<translation id="1038168778161626396">Chiffrer seulement</translation>
++<translation id="2756651186786928409">Ne jamais intervertir les touches de modification</translation>
++<translation id="1217515703261622005">Conversion des numéros spéciaux</translation>
++<translation id="7179921470347911571">Relancer maintenant</translation>
++<translation id="3715099868207290855">Synchronisation avec <ph name="USER_EMAIL_ADDRESS"/> effectuée</translation>
++<translation id="2679312662830811292">il y a <ph name="NUMBER_ONE"/> minute</translation>
++<translation id="9065203028668620118">Édition</translation>
++<translation id="4718464510840275738">Préférences synchronisées</translation>
++<translation id="8788572795284305350"><ph name="NUMBER_ZERO"/> hours ago</translation>
++<translation id="1177863135347784049">Personnalisé</translation>
++<translation id="8236028464988198644">Rechercher à partir de la barre d'adresse</translation>
++<translation id="4881695831933465202">Ouvrir</translation>
++<translation id="5988520580879236902">Inspecter les vues actives :</translation>
++<translation id="3593965109698325041">Contraintes de nom du certificat</translation>
++<translation id="4358697938732213860">Ajouter une adresse</translation>
++<translation id="8396532978067103567">Mot de passe incorrect.</translation>
++<translation id="5981759340456370804">Statistiques avancées</translation>
++<translation id="8160015581537295331">Clavier espagnol</translation>
++<translation id="3505920073976671674">Sélectionnez votre réseau</translation>
++<translation id="6644971472240498405"><ph name="NUMBER_ONE"/> jour</translation>
++<translation id="1782924894173027610">Le serveur de synchronisation est occupé. Veuillez réessayer ultérieurement.</translation>
++<translation id="6512448926095770873">Quitter cette page</translation>
++<translation id="5457599981699367932">Naviguer en tant qu'invité</translation>
++<translation id="3169472444629675720">Discover</translation>
++<translation id="6294193300318171613">&amp;Toujours afficher la barre de favoris</translation>
++<translation id="4088820693488683766">Options de recherche</translation>
++<translation id="3414952576877147120">Taille :</translation>
++<translation id="9098468523912235228">il y a <ph name="NUMBER_DEFAULT"/> secondes</translation>
++<translation id="7009102566764819240">La liste suivante fait état des éléments dangereux détectés sur la page. Cliquez sur le lien &quot;Diagnostic&quot; pour obtenir plus d'informations sur une ressource particulière. Si une ressource a été signalée comme site de phishing alors que vous êtes certain de sa fiabilité, cliquez sur le lien &quot;Signaler une erreur&quot;.</translation>
++<translation id="4923417429809017348">Cette page rédigée dans une langue non identifiée a été traduite en <ph name="LANGUAGE_LANGUAGE"/>.</translation>
++<translation id="3631337165634322335">Les exceptions ci-dessous s'appliquent uniquement à la session de navigation privée actuelle.</translation>
++<translation id="676327646545845024">Ne plus afficher la boîte de dialogue pour les liens de ce type</translation>
++<translation id="494645311413743213"><ph name="NUMBER_TWO"/> secondes restantes</translation>
++<translation id="1485146213770915382">Insérez <ph name="SEARCH_TERMS_LITERAL"/> dans l'URL où les termes de recherche devraient apparaître.</translation>
++<translation id="4839303808932127586">En&amp;registrer la vidéo sous...</translation>
++<translation id="5626134646977739690">Nom :</translation>
++<translation id="5854409662653665676">Si vous rencontrez des problèmes fréquents avec ce module, vous pouvez tenter d'y remédier en suivant la procédure ci-après :</translation>
++<translation id="3681007416295224113">Informations relatives au certificat</translation>
++<translation id="3046084099139788433">Activer l'onglet 7</translation>
++<translation id="721197778055552897"><ph name="BEGIN_LINK"/>En savoir plus<ph name="END_LINK"/> sur ce problème.</translation>
++<translation id="1699395855685456105">Version du matériel :</translation>
++<translation id="212464871579942993">Le site Web à l'adresse <ph name="HOST_NAME"/> contient des éléments provenant de sites qui semblent héberger des logiciels malveillants. Ces derniers peuvent nuire à votre ordinateur ou agir à votre insu. Le simple fait de visiter un site hébergeant ce type de logiciels peut infecter votre ordinateur. Ce site héberge également des informations provenant de sites signalés comme étant des sites de phishing. Ces derniers incitent les internautes à divulguer des informations personnelles en se faisant passer pour des institutions de confiance, telles que des banques.</translation>
++<translation id="8156020606310233796">Afficher la liste</translation>
++<translation id="957120528631539888">Désactivez l'affichage des messages de confirmation et le blocage de l'envoi des formulaires.</translation>
++<translation id="146000042969587795">Ce cadre a été bloqué, car il contient des éléments non sécurisés.</translation>
++<translation id="8112223930265703044">Tout</translation>
++<translation id="3968739731834770921">Kana</translation>
++<translation id="3729920814805072072">Gérer les mots de passe enregistrés...</translation>
++<translation id="7387829944233909572">Boîte de dialogue &quot;Effacer les données de navigation&quot;</translation>
++<translation id="8023801379949507775">Mettre à jour les extensions maintenant</translation>
++<translation id="6549677549082720666">Nouvelle application en arrière-plan installée</translation>
++<translation id="1983108933174595844">Envoyer une capture d'écran de la page actuelle</translation>
++<translation id="3298789223962368867">L'URL indiquée est incorrecte.</translation>
++<translation id="2202898655984161076">Un problème est survenu lors de l'affichage de la liste des imprimantes. Certaines de vos imprimantes ne sont peut-être pas correctement enregistrées dans <ph name="CLOUD_PRINT_NAME"/>.</translation>
++<translation id="6154697846084421647">Actuellement connecté</translation>
++<translation id="8241707690549784388">La page que vous recherchez a utilisé des informations que vous avez envoyées. Si vous revenez sur cette page, chaque action précédemment effectuée sera répétée. Souhaitez-vous continuer ?</translation>
++<translation id="5359419173856026110">Cette fonctionnalité indique la vitesse d'affichage réelle d'une page, en images par seconde, lorsque l'accélération matérielle est active.</translation>
++<translation id="4104163789986725820">E&amp;xporter...</translation>
++<translation id="2113479184312716848">&amp;Ouvrir un fichier...</translation>
++<translation id="8412709057120877195">Configurer le contrôle d'accès pour vos périphériques</translation>
++<translation id="486595306984036763">Ouvrir un rapport de phishing</translation>
++<translation id="3140353188828248647">Activer la barre d'adresse</translation>
++<translation id="4860787810836767172"><ph name="NUMBER_FEW"/> secs ago</translation>
++<translation id="5565871407246142825">Cartes de paiement</translation>
++<translation id="2587203970400270934">Code opérateur :</translation>
++<translation id="3355936511340229503">Erreur de connexion</translation>
++<translation id="1824910108648426227">Vous avez la possibilité de désactiver ces services.</translation>
++<translation id="3092040396860056776">Essayer d'afficher la page malgré tout</translation>
++<translation id="4350711002179453268">Impossible d'établir une connexion sécurisée avec le serveur. Le serveur a peut-être rencontré un problème ou exige un certificat d'authentification du client dont vous ne disposez pas.</translation>
++<translation id="91731790394942114">Ajouter un nouveau nom</translation>
++<translation id="5963026469094486319">Obtenir d'autres thèmes</translation>
++<translation id="2441719842399509963">Rétablir les valeurs par défaut</translation>
++<translation id="1893137424981664888">Aucun Plug-in installé.</translation>
++<translation id="3718288130002896473">Action</translation>
++<translation id="2168725742002792683">Extensions de fichier</translation>
++<translation id="1753905327828125965">Les plus visités</translation>
++<translation id="8116972784401310538">&amp;Gestionnaire de favoris</translation>
++<translation id="1849632043866553433">Caches des applications</translation>
++<translation id="3591607774768458617">Cette langue est actuellement utilisée par <ph name="PRODUCT_NAME"/>.</translation>
++<translation id="621638399744152264"><ph name="VALUE"/> %</translation>
++<translation id="4927301649992043040">Empaqueter l'extension</translation>
++<translation id="8679658258416378906">Activer l'onglet 5</translation>
++<translation id="4763816722366148126">Sélectionner le mode de saisie précédent</translation>
++<translation id="6458308652667395253">Configurer le blocage de JavaScript...</translation>
++<translation id="8435334418765210033">Réseaux mémorisés</translation>
++<translation id="6516193643535292276">Impossible de se connecter à Internet.</translation>
++<translation id="5125751979347152379">URL incorrecte</translation>
++<translation id="2791364193466153585">Informations sur la sécurité</translation>
++<translation id="4673916386520338632">Impossible d'installer l'application, car elle est en conflit avec &quot;<ph name="APP_NAME"/>&quot;, qui est déjà installé.</translation>
++<translation id="2024918351532495204">Votre périphérique n'est pas connecté.</translation>
++<translation id="6040143037577758943">Fermer</translation>
++<translation id="5787146423283493983">Accord de la clé</translation>
++<translation id="1101671447232096497"><ph name="NUMBER_MANY"/> mins ago</translation>
++<translation id="4265682251887479829">Vous ne trouvez pas ce que vous recherchez ?</translation>
++<translation id="1804251416207250805">Désactivez l'envoi des pings de contrôle des liens hypertexte.</translation>
++<translation id="5116628073786783676">En&amp;registrer le fichier audio sous...</translation>
++<translation id="2557899542277210112">Accédez rapidement à vos favoris en les ajoutant à la barre de favoris.</translation>
++<translation id="2749881179542288782">Vérifier la grammaire et l'orthographe</translation>
++<translation id="5105855035535475848">Épingler les onglets</translation>
++<translation id="6892450194319317066">Sélectionner par type d'application</translation>
++<translation id="3549436232897695316">assembler</translation>
++<translation id="5414121716219514204"><ph name="ENGINE_HOST_NAME"/> souhaite devenir votre moteur de recherche.</translation>
++<translation id="2752805177271551234">Utiliser l'historique d'entrée</translation>
++<translation id="7268365133021434339">Fermer les onglets</translation>
++<translation id="4910619056351738551">Voici quelques suggestions :</translation>
++<translation id="9131598836763251128">Sélectionnez un ou plusieurs fichiers</translation>
++<translation id="5489059749897101717">Afficher le panneau de la &amp;vérification orthographique</translation>
++<translation id="3423858849633684918">Veuillez relancer <ph name="PRODUCT_NAME"/>.</translation>
++<translation id="1232569758102978740">Sans titre</translation>
++<translation id="1903219944620007795">Pour saisir du texte, sélectionnez une langue et consultez la liste des modes de saisie disponibles.</translation>
++<translation id="4362187533051781987">Ville</translation>
++<translation id="9149866541089851383">Modifier...</translation>
++<translation id="7608619752233383356">Réinitialiser la synchronisation</translation>
++<translation id="1065245965611933814">Inclure une capture d'écran enregistrée :</translation>
++<translation id="7876243839304621966">Tout supprimer</translation>
++<translation id="5663459693447872156">Passer automatiquement en demi-chasse</translation>
++<translation id="4593021220803146968">&amp;Accéder à <ph name="URL"/></translation>
++<translation id="1128987120443782698">La capacité de ce périphérique de stockage est de <ph name="DEVICE_CAPACITY"/>. Veuillez insérer une carte SD ou une clé USB d'au moins 4 Go.</translation>
++<translation id="869257642790614972">Rouvrir le dernier onglet fermé</translation>
++<translation id="3978267865113951599">(blocage)</translation>
++<translation id="8412145213513410671">Erreurs (<ph name="CRASH_COUNT"/>)</translation>
++<translation id="560602183358579978">Traitement de la sélection...</translation>
++<translation id="7649070708921625228">Aide</translation>
++<translation id="5994107996638824097">Désolé ! La visionneuse de documents PDF intégrée à Google Chrome, nécessaire à l'affichage de l'aperçu avant impression, n'est pas incluse dans Chromium.</translation>
++<translation id="976526967778596630">Impossible d'ouvrir <ph name="HOST_NAME"/>, car vous êtes déconnecté du réseau. Cette page s'affichera dès que la connexion réseau sera rétablie. &lt;br&gt;</translation>
++<translation id="1734072960870006811">Télécopie</translation>
++<translation id="3095995014811312755">version</translation>
++<translation id="7052500709156631672">La passerelle ou le serveur proxy a reçu une réponse incorrecte d'un serveur en amont.</translation>
++<translation id="281133045296806353">Nouvelle fenêtre ouverte dans la session du navigateur</translation>
++<translation id="7144878232160441200">Réessayer</translation>
++<translation id="2860002559146138960"><ph name="PRODUCT_NAME"/> peut maintenant synchroniser vos mots de passe. Vos données seront chiffrées avec le mot de passe de votre compte Google ou le mot de passe multiterme de votre choix.</translation>
++<translation id="3951872452847539732">Les paramètres réseau de votre proxy sont gérés par une extension.</translation>
++<translation id="6442697326824312960">Retirer l'onglet</translation>
++<translation id="6382612843547381371">Valable du <ph name="START_DATE_TIME"/> au <ph name="END_DATE_TIME"/></translation>
++<translation id="6869402422344886127">Case cochée</translation>
++<translation id="5637380810526272785">Mode de saisie</translation>
++<translation id="404928562651467259">AVERTISSEMENT</translation>
++<translation id="7172053773111046550">Clavier estonien</translation>
++<translation id="497490572025913070">Ajout de bordures aux couches de rendu composées</translation>
++<translation id="9002707937526687073">Imp&amp;rimer...</translation>
++<translation id="5953934840931207585">Paramètres de saisie automatique <ph name="PRODUCT_NAME_SHORT"/></translation>
++<translation id="5556459405103347317">Rafraîchir</translation>
++<translation id="8000020256436988724">Barre d'outils</translation>
++<translation id="8326395326942127023">Nom de la base de données :</translation>
++<translation id="7507930499305566459">Certificat du répondeur d'état</translation>
++<translation id="2689915906323125315">Utiliser le mot de passe de mon compte Google</translation>
++<translation id="6440205424473899061">Vos favoris sont maintenant synchronisés avec Google Documents !
++Pour fusionner et synchroniser vos favoris dans <ph name="PRODUCT_NAME"/> sur un autre ordinateur, procédez de la même manière que précédemment sur l'ordinateur voulu.</translation>
++<translation id="7727721885715384408">Renommer...</translation>
++<translation id="2604243255129603442"><ph name="NAME_OF_EXTENSION"/> a été désactivé. Si vous arrêtez la synchronisation des favoris, vous pouvez la réactiver sur la page des extensions, via le menu Outils.</translation>
++<translation id="2024621544377454980">Affichage des pages impossible</translation>
++<translation id="7136694880210472378">Utiliser par défaut</translation>
++<translation id="7681202901521675750">La carte SIM est verrouillée. Veuillez saisir votre code PIN. Nombre de tentatives restantes : <ph name="TRIES_COUNT"/></translation>
++<translation id="1731346223650886555">Point-virgule</translation>
++<translation id="158849752021629804">Réseau domestique requis</translation>
++<translation id="7339763383339757376">PKCS #7, certificat unique</translation>
++<translation id="7587108133605326224">Langues baltes</translation>
++<translation id="3991936620356087075">Vous avez saisi un trop grand nombre de clés de verrouillage du code PIN incorrectes. Votre carte SIM est définitivement désactivée.</translation>
++<translation id="936801553271523408">Données de diagnostic système</translation>
++<translation id="6389701355360299052">Page Web, contenu HTML uniquement</translation>
++<translation id="8067791725177197206">Continuer »</translation>
++<translation id="9009144784540995197">Gérez vos imprimantes.</translation>
++<translation id="1055006259534905434">(Choisir un problème dans la liste ci-dessous)</translation>
++<translation id="3021678814754966447">&amp;Afficher le code source du cadre</translation>
++<translation id="8601206103050338563">Authentification du client WWW TLS</translation>
++<translation id="1692799361700686467">Les cookies de plusieurs sites sont autorisés.</translation>
++<translation id="7074488040076962230">Impossible d'afficher la page de la barre latérale &quot;<ph name="SIDEBAR_PAGE"/>&quot;.</translation>
++<translation id="529232389703829405">Vous avez acheté <ph name="DATA_AMOUNT"/> de données le <ph name="DATE"/>.</translation>
++<translation id="5271549068863921519">Enregistrer le mot de passe</translation>
++<translation id="4345587454538109430">Configurer...</translation>
++<translation id="8148264977957212129">Mode de saisie du pinyin</translation>
++<translation id="5787378733537687553">Intervertir les touches Ctrl et Alt de gauche</translation>
++<translation id="7772032839648071052">Confirmer le mot de passe multiterme</translation>
++<translation id="6857811139397017780">Activer <ph name="NETWORKSERVICE"/></translation>
++<translation id="3251855518428926750">Ajouter...</translation>
++<translation id="4120075327926916474">Voulez-vous que Google Chrome enregistre ces informations de carte de paiement pour le remplissage de formulaires Web ?</translation>
++<translation id="6929555043669117778">Continuer à bloquer les fenêtres pop-up</translation>
++<translation id="5864471791310927901">Échec de la vérification DHCP</translation>
++<translation id="3508920295779105875">Choisir un autre dossier...</translation>
++<translation id="2503458975635466059">Le profil semble être utilisé par le processus <ph name="PROCESS_ID"/> sur l'hôte <ph name="HOST_NAME"/>. Si vous êtes certain qu'aucun autre processus n'utilise ce profil, supprimez le fichier <ph name="LOCK_FILE"/> et relancez <ph name="PRODUCT_NAME"/>.</translation>
++<translation id="2987775926667433828">Chinois traditionnel</translation>
++<translation id="6684737638449364721">Effacer les données de navigation...</translation>
++<translation id="3954582159466790312">Ré&amp;activer le son</translation>
++<translation id="1110772031432362678">Aucun réseau trouvé.</translation>
++<translation id="3936390757709632190">&amp;Ouvrir le fichier audio dans un nouvel onglet</translation>
++<translation id="7297622089831776169">&amp;Méthodes d'entrée</translation>
++<translation id="5731698828607291678">Onglets ou fenêtres</translation>
++<translation id="1152775729948968688">Toutefois, cette page inclut d'autres ressources qui ne sont pas sécurisées. Ces ressources peuvent être consultées par des tiers pendant leur transfert, et modifiées par un pirate informatique dans le but de changer le comportement de cette page.</translation>
++<translation id="604124094241169006">Automatique</translation>
++<translation id="862542460444371744">&amp;Extensions</translation>
++<translation id="8045462269890919536">Roumain</translation>
++<translation id="6320286250305104236">Paramètres du réseau...</translation>
++<translation id="2927657246008729253">Changer...</translation>
++<translation id="7978412674231730200">Clé privée</translation>
++<translation id="464745974361668466">Format :</translation>
++<translation id="5308380583665731573">Se connecter</translation>
++<translation id="9111395131601239814"><ph name="NETWORKDEVICE"/> : <ph name="STATUS"/></translation>
++<translation id="9049981332609050619">Vous avez tenté de contacter <ph name="DOMAIN"/>, mais le certificat présenté par le serveur est incorrect.</translation>
++<translation id="4414232939543644979">Nouvelle fenêtre de nav&amp;igation privée</translation>
++<translation id="1693754753824026215">La page à l'adresse <ph name="SITE"/> indique :</translation>
++<translation id="7148804936871729015">Le serveur associé à <ph name="URL"/> n'a pas répondu à temps. Cela peut être dû à une surcharge.</translation>
++<translation id="5950967683057767490">L2TP/IPSec + Clé pré-partagée</translation>
++<translation id="8108473539339615591">XSS Auditor</translation>
++<translation id="1902576642799138955">Durée de validité</translation>
++<translation id="4910021444507283344">WebGL</translation>
++<translation id="6692173217867674490">Mot de passe multiterme erroné</translation>
++<translation id="5550431144454300634">Corriger automatiquement la saisie</translation>
++<translation id="3308006649705061278">Unité d'organisation</translation>
++<translation id="8912362522468806198">Compte Google</translation>
++<translation id="4443536555189480885">&amp;Aide</translation>
++<translation id="340485819826776184">Utiliser un service de prédiction afin de compléter les recherches et les URL saisies dans la barre d'adresse</translation>
++<translation id="4074900173531346617">Certificat du signataire de courrier électronique</translation>
++<translation id="6165508094623778733">En savoir plus</translation>
++<translation id="9052208328806230490">Vous avez enregistré vos imprimantes sur <ph name="CLOUD_PRINT_NAME"/> via le compte <ph name="EMAIL"/>.</translation>
++<translation id="822618367988303761">il y a <ph name="NUMBER_TWO"/> jours</translation>
++<translation id="7928333295097642153"><ph name="HOUR"/>:<ph name="MINUTE"/> restantes</translation>
++<translation id="7568593326407688803">Cette page est en<ph name="ORIGINAL_LANGUAGE"/>Voulez-vous la traduire ?</translation>
++<translation id="563969276220951735">Saisie automatique des formulaires</translation>
++<translation id="6870130893560916279">Clavier ukrainien</translation>
++<translation id="8629974950076222828">Ouvrir tous les favoris dans une fenêtre de navigation privée</translation>
++<translation id="3126026824346185272">Ctrl</translation>
++<translation id="4745438305783437565"><ph name="NUMBER_FEW"/> minutes</translation>
++<translation id="2649911884196340328">Le certificat de sécurité du serveur contient des erreurs !</translation>
++<translation id="6666647326143344290">avec votre compte Google</translation>
++<translation id="3828029223314399057">Rechercher dans les favoris</translation>
++<translation id="4885705234041587624">MSCHAPv2</translation>
++<translation id="5614190747811328134">Avertissement utilisateur</translation>
++<translation id="8906421963862390172">&amp;Options du vérificateur d'orthographe</translation>
++<translation id="9046895021617826162">Échec de la connexion</translation>
++<translation id="1492188167929010410">Identifiant de l'erreur <ph name="CRASH_ID"/></translation>
++<translation id="1963692530539281474"><ph name="NUMBER_DEFAULT"/> jours restants</translation>
++<translation id="4470270245053809099">Émis par : <ph name="NAME"/></translation>
++<translation id="5365539031341696497">Mode de saisie du thaï (clavier Kesmanee)</translation>
++<translation id="2403091441537561402">Passerelle :</translation>
++<translation id="6337234675334993532">Chiffrement</translation>
++<translation id="668171684555832681">Autre...</translation>
++<translation id="1932098463447129402">Pas avant le</translation>
++<translation id="7845920762538502375"><ph name="PRODUCT_NAME"/> n'a pas pu synchroniser vos données, car la connexion avec le serveur de synchronisation n'a pas pu être établie. Nouvel essai...</translation>
++<translation id="2192664328428693215">Me demander lorsqu'un site souhaite afficher des notifications sur le Bureau (recommandé)</translation>
++<translation id="6708242697268981054">Source :</translation>
++<translation id="4786993863723020412">Erreur de lecture du cache</translation>
++<translation id="6630452975878488444">Raccourci de sélection</translation>
++<translation id="8709969075297564489">Vérifier la révocation du certificat serveur</translation>
++<translation id="8698171900303917290">Vous rencontrez des problèmes lors de l'installation ?</translation>
++<translation id="830868413617744215">Bêta</translation>
++<translation id="5925147183566400388">Pointeur de la déclaration CPS (Certification Practice Statement)</translation>
++<translation id="1497270430858433901">Le <ph name="DATE"/>, vous avez reçu <ph name="DATA_AMOUNT"/> à utiliser librement.</translation>
++<translation id="8150167929304790980">Nom complet</translation>
++<translation id="636850387210749493">Inscription d'entreprise</translation>
++<translation id="1947424002851288782">Clavier allemand</translation>
++<translation id="932508678520956232">Impossible de lancer l'impression.</translation>
++<translation id="4861833787540810454">&amp;Lire</translation>
++<translation id="2552545117464357659">Récent</translation>
++<translation id="7269802741830436641">Cette page Web présente une boucle de redirection.</translation>
++<translation id="4180788401304023883">Supprimer le certificat &quot;<ph name="CERTIFICATE_NAME"/>&quot; émis par l'autorité de certification ?</translation>
++<translation id="5869522115854928033">Mots de passe enregistrés</translation>
++<translation id="2089090684895656482">Moins</translation>
++<translation id="1709220265083931213">Options avancées</translation>
++<translation id="5748266869826978907">Vérifiez vos paramètres DNS. Contactez votre administrateur réseau si vous n'êtes pas sûr de vous.</translation>
++<translation id="4771973620359291008">Une erreur inconnue s'est produite.</translation>
++<translation id="5509914365760201064">Émetteur : <ph name="CERTIFICATE_AUTHORITY"/></translation>
++<translation id="7073385929680664879">Passer d'un mode de saisie à l'autre</translation>
++<translation id="6898699227549475383">Organisation (O)</translation>
++<translation id="4333854382783149454">PKCS #1 SHA-1 avec chiffrement RSA</translation>
++<translation id="762904068808419792">Entrez la requête de recherche ici.</translation>
++<translation id="8615618338313291042">Application en mode navigation privée : <ph name="APP_NAME"/></translation>
++<translation id="978146274692397928">La largeur de ponctuation initiale est Complète</translation>
++<translation id="8959027566438633317">Installer <ph name="EXTENSION_NAME"/> ?</translation>
++<translation id="8155798677707647270">Installation d'une nouvelle version...</translation>
++<translation id="6886871292305414135">Ouvrir le lien dans un nouvel ongle&amp;t</translation>
++<translation id="1639192739400715787">Pour accéder aux paramètres de sécurité, saisissez le code PIN de la carte SIM.</translation>
++<translation id="7961015016161918242">Jamais</translation>
++<translation id="3950924596163729246">Impossible d'accéder au réseau.</translation>
++<translation id="2835170189407361413">Effacer le formulaire</translation>
++<translation id="4631110328717267096">Échec de la mise à jour du système</translation>
++<translation id="3695919544155087829">Saisissez le mot de passe utilisé pour chiffrer ce fichier de certificat.</translation>
++<translation id="2230051135190148440">CHAP</translation>
++<translation id="6308937455967653460">Enregistrer le lie&amp;n sous...</translation>
++<translation id="5421136146218899937">Effacer les données de navigation...</translation>
++<translation id="5783059781478674569">Options de reconnaissance vocale</translation>
++<translation id="5441100684135434593">Réseau câblé</translation>
++<translation id="3285322247471302225">Nouvel ongle&amp;t</translation>
++<translation id="3943582379552582368">R&amp;etour</translation>
++<translation id="7607002721634913082">Téléchargement suspendu</translation>
++<translation id="480990236307250886">Ouvrir la page d'accueil</translation>
++<translation id="8286036467436129157">Connexion</translation>
++<translation id="5999940714422617743">L'installation de <ph name="EXTENSION_NAME"/> est terminée.</translation>
++<translation id="1122198203221319518">&amp;Outils</translation>
++<translation id="5757539081890243754">Page d'accueil</translation>
++<translation id="2760009672169282879">Clavier phonétique bulgare</translation>
++<translation id="6608140561353073361">Cookies et données de site...</translation>
++<translation id="8007030362289124303">Batterie faible</translation>
++<translation id="4513946894732546136">Commentaires</translation>
++<translation id="1135328998467923690">Package incorrect : &quot;<ph name="ERROR_CODE"/>&quot;.</translation>
++<translation id="5906719743126878045"><ph name="NUMBER_TWO"/> heures restantes</translation>
++<translation id="1753682364559456262">Configurer les paramètres de blocage des images...</translation>
++<translation id="6550675742724504774">Options</translation>
++<translation id="8959208747503200525"><ph name="NUMBER_TWO"/> hours ago</translation>
++<translation id="431076611119798497">&amp;Détails</translation>
++<translation id="737801893573836157">Masquer la barre de titre du système et utiliser les bordures</translation>
++<translation id="5352235189388345738">Elle peut accéder aux éléments suivants :</translation>
++<translation id="5040262127954254034">Confidentialité</translation>
++<translation id="7666868073052500132">Objets : <ph name="USAGES"/></translation>
++<translation id="6985345720668445131">Paramètres d'entrée du japonais</translation>
++<translation id="3258281577757096226">Sebeol-sik Final</translation>
++<translation id="6906268095242253962">Veuillez vous connecter à Internet pour continuer.</translation>
++<translation id="1908748899139377733">Afficher les &amp;infos sur le cadre</translation>
++<translation id="803771048473350947">Fichier</translation>
++<translation id="6206311232642889873">Cop&amp;ier l'image</translation>
++<translation id="5158983316805876233">Utiliser le même proxy pour tous les protocoles</translation>
++<translation id="7108338896283013870">Masquer</translation>
++<translation id="3366404380928138336">Requête de protocole externe</translation>
++<translation id="5300589172476337783">Afficher</translation>
++<translation id="3160041952246459240">Certains de vos certificats enregistrés identifient ces serveurs :</translation>
++<translation id="566920818739465183">Vous avez visité ce site pour la première fois le <ph name="VISIT_DATE"/>.</translation>
++<translation id="2961695502793809356">Cliquer pour avancer, maintenir pour voir l'historique</translation>
++<translation id="4092878864607680421">La dernière version de l'application &quot;<ph name="APP_NAME"/>&quot; requiert d'autres autorisations. Elle a donc été désactivée.</translation>
++<translation id="8421864404045570940"><ph name="NUMBER_DEFAULT"/> secondes</translation>
++<translation id="5828228029189342317">Vous avez choisi d'ouvrir automatiquement certains types de fichiers après leur téléchargement.</translation>
++<translation id="1416836038590872660">EAP-MD5</translation>
++<translation id="176587472219019965">&amp;Nouvelle fenêtre</translation>
++<translation id="2788135150614412178">+</translation>
++<translation id="4055738107007928968">Vous avez essayé d'accéder au site <ph name="DOMAIN"/>, mais le serveur a présenté un certificat signé avec un algorithme de signature faible. Il se peut que les informations d'identification fournies par le serveur aient été falsifiées. Le serveur n'est peut-être pas celui auquel vous souhaitez accéder (il peut s'agir d'une tentative de piratage). Nous nous déconseillons vivement de continuer.</translation>
++<translation id="5308689395849655368">L'envoi de rapports d'erreur est désactivé.</translation>
++<translation id="8372369524088641025">Clé WEP incorrecte</translation>
++<translation id="8689341121182997459">Date d'expiration :</translation>
++<translation id="899403249577094719">URL de base du certificat Netscape</translation>
++<translation id="2737363922397526254">Réduire...</translation>
++<translation id="4880827082731008257">Rechercher dans l'historique</translation>
++<translation id="8661290697478713397">Ouvrir le lien dans la fenêtre de navi&amp;gation privée</translation>
++<translation id="4197700912384709145"><ph name="NUMBER_ZERO"/> secondes</translation>
++<translation id="7454780465968211330">Historique avancé pour le champ polyvalent</translation>
++<translation id="2158448795143567596">Active l'utilisation de graphismes 3D dans les éléments canvas via l'API WebGL.</translation>
++<translation id="1702534956030472451">Occident</translation>
++<translation id="6636709850131805001">État non reconnu</translation>
++<translation id="6095984072944024315">−</translation>
++<translation id="9141716082071217089">Impossible de vérifier si le certificat du serveur a été révoqué.</translation>
++<translation id="4304224509867189079">Se connecter</translation>
++<translation id="5332624210073556029">Fuseau horaire :</translation>
++<translation id="4799797264838369263">Cette option est soumise à une stratégie d'entreprise. Contactez votre administrateur pour plus d'informations.</translation>
++<translation id="4492190037599258964">Résultats de recherche pour &quot;<ph name="SEARCH_STRING"/>&quot;</translation>
++<translation id="3573179567135747900">Revenir à &quot;<ph name="FROM_LOCALE"/>&quot; (redémarrage requis)</translation>
++<translation id="2238123906478057869"><ph name="PRODUCT_NAME"/> va exécuter les tâches suivantes :</translation>
++<translation id="4042471398575101546">Ajouter la page</translation>
++<translation id="8848709220963126773">Changement de mode via la touche Maj</translation>
++<translation id="4871865824885782245">Options de date et d'heure...</translation>
++<translation id="8828933418460119530">Nom DNS</translation>
++<translation id="988159990683914416">Build de développement</translation>
++<translation id="8026354464835030469"><ph name="BURNT_AMOUNT"/> sur ...</translation>
++<translation id="4114470632216071239">Verrouiller la carte SIM (code PIN obligatoire pour utiliser les données mobiles)</translation>
++<translation id="2183426022964444701">Sélectionnez le répertoire racine de l'extension.</translation>
++<translation id="2517143724531502372">Les cookies de <ph name="DOMAIN"/> sont autorisés uniquement pour cette session.</translation>
++<translation id="9018524897810991865">Confirmer les préférences de synchronisation</translation>
++<translation id="4719905780348837473">RSN</translation>
++<translation id="5212108862377457573">Ajuster la conversion en fonction de l'entrée précédente</translation>
++<translation id="5398353896536222911">Afficher le panneau de la &amp;vérification orthographique</translation>
++<translation id="5811533512835101223">(Revenir à la capture d'écran d'origine)</translation>
++<translation id="5131817835990480221">Mettre à jour &amp;<ph name="PRODUCT_NAME"/></translation>
++<translation id="939519157834106403">SSID</translation>
++<translation id="3705722231355495246">-</translation>
++<translation id="2635102990349508383">Les informations de connexion au compte n'ont pas encore été saisies.</translation>
++<translation id="6902055721023340732">URL de configuration automatique</translation>
++<translation id="4268574628540273656">URL :</translation>
++<translation id="7481312909269577407">Avancer</translation>
++<translation id="3759876923365568382"><ph name="NUMBER_FEW"/> jours restants</translation>
++<translation id="295228163843771014">Vous avez choisi de ne pas synchroniser les mots de passe. Vous pouvez à tout moment modifier vos paramètres de synchronisation, si vous changez d'avis.</translation>
++<translation id="5972826969634861500">Lancer <ph name="PRODUCT_NAME"/></translation>
++<translation id="7828702903116529889"><ph name="PRODUCT_NAME"/>
++ ne parvient pas à accéder au réseau.
++ <ph name="LINE_BREAK"/>
++ Il est possible que votre pare-feu ou votre antivirus considère
++ <ph name="PRODUCT_NAME"/>
++ comme un intrus dans votre ordinateur et qu'il bloque ses tentatives de connexion à Internet.</translation>
++<translation id="878069093594050299">Ce certificat a été vérifié pour les utilisations suivantes :</translation>
++<translation id="5852112051279473187">Petit problème ! Une erreur est survenue lors de l'inscription de ce périphérique. Veuillez réessayer ou contacter votre représentant de l'assistance technique.</translation>
++<translation id="1664314758578115406">Ajouter aux favoris</translation>
++<translation id="7088418943933034707">Gérer les certificats...</translation>
++<translation id="8482183012530311851">Analyse du périphérique...</translation>
++<translation id="3127589841327267804">PYJJ</translation>
++<translation id="8808478386290700967">Web Store</translation>
++<translation id="1732215134274276513">Annuler l'épinglage des onglets</translation>
++<translation id="4084682180776658562">Favori</translation>
++<translation id="8859057652521303089">Sélectionnez votre langue :</translation>
++<translation id="3030138564564344289">Réessayer le téléchargement</translation>
++<translation id="8525552230188318924">Configurer la synchronisation des mots de passe</translation>
++<translation id="4381091992796011497">Nom d'utilisateur :</translation>
++<translation id="5830720307094128296">Enregistrer la p&amp;age sous...</translation>
++<translation id="8114439576766120195">Vos données sur tous les sites Web</translation>
++<translation id="4668954208278016290">Un problème est survenu lors de l'extraction de l'image sur l'ordinateur.</translation>
++<translation id="5822838715583768518">Lancer l'application</translation>
++<translation id="3942974664341190312">Dubeol-sik</translation>
++<translation id="8477241577829954800">Remplacé</translation>
++<translation id="6735304988756581115">Afficher les cookies et autres données de site...</translation>
++<translation id="3048564749795856202">Si vous pensez avoir cerné les risques, vous pouvez <ph name="PROCEED_LINK"/>.</translation>
++<translation id="2433507940547922241">Apparence</translation>
++<translation id="839072384475670817">Créer des raccourci&amp;s vers des applications...</translation>
++<translation id="1478632774608054702">Exécuter le flash PPAPI dans le processus du moteur de rendu</translation>
++<translation id="6756161853376828318">Définir <ph name="PRODUCT_NAME"/> en tant que navigateur par défaut</translation>
++<translation id="9112614144067920641">Veuillez choisir un nouveau code PIN.</translation>
++<translation id="2061855250933714566"><ph name="ENCODING_CATEGORY"/> (<ph name="ENCODING_NAME"/>)</translation>
++<translation id="7138678301420049075">Autre</translation>
++<translation id="9147392381910171771">&amp;Options</translation>
++<translation id="1803557475693955505">Impossible de charger la page d'arrière-plan &quot;<ph name="BACKGROUND_PAGE"/>&quot;.</translation>
++<translation id="5818334088068591797">À quel niveau rencontrez-vous des problèmes ? (Champ obligatoire)</translation>
++<translation id="6264485186158353794">Retour à la sécurité</translation>
++<translation id="5130080518784460891">Eten</translation>
++<translation id="5847724078457510387">Ce site répertorie tous ses certificats valides dans le système DNS. Un certificat non répertorié a cependant été utilisé par le serveur.</translation>
++<translation id="1394853081832053657">Options de reconnaissance vocale</translation>
++<translation id="5037676449506322593">Tout sélectionner</translation>
++<translation id="2785530881066938471">Impossible de charger le fichier &quot;<ph name="RELATIVE_PATH"/>&quot; pour le script de contenu, car ce fichier n'est pas codé en UTF-8.</translation>
++<translation id="3807747707162121253">&amp;Annuler</translation>
++<translation id="3306897190788753224">Désactiver temporairement la personnalisation des conversions, les suggestions basées sur l'historique et le dictionnaire utilisateur</translation>
++<translation id="2574102660421949343">Les cookies de <ph name="DOMAIN"/> sont autorisés.</translation>
++<translation id="77999321721642562">Au fil du temps, la zone ci-dessous affichera les huit sites que vous avez le plus visités.</translation>
++<translation id="1503894213707460512">Le plug-in <ph name="PLUGIN_NAME"/> a besoin de votre autorisation pour s'exécuter.</translation>
++<translation id="471800408830181311">Échec de création de clé privée</translation>
++<translation id="1273291576878293349">Ouvrir tous les favoris dans une fenêtre de navigation privée</translation>
++<translation id="1639058970766796751">Placer dans la file d'attente</translation>
++<translation id="1177437665183591855">Erreur de certificat serveur inconnue</translation>
++<translation id="8467473010914675605">Mode de saisie du coréen</translation>
++<translation id="3819800052061700452">&amp;Plein écran</translation>
++<translation id="5419540894229653647"><ph name="ERROR_DESCRIPTION_TEXT"/>
++ <ph name="LINE_BREAK"/>
++ Vous pouvez essayer de diagnostiquer le problème en procédant comme suit :
++ <ph name="LINE_BREAK"/>
++ <ph name="PLATFORM_TEXT"/></translation>
++<translation id="3533943170037501541">Bienvenue sur votre page d'accueil !</translation>
++<translation id="2333340435262918287">Vos modifications seront prises en compte au prochain démarrage de <ph name="PRODUCT_NAME"/>.</translation>
++<translation id="5906065664303289925">Adresse du matériel :</translation>
++<translation id="3178000186192127858">Lecture seule</translation>
++<translation id="2187895286714876935">Erreur d'importation du certificat serveur</translation>
++<translation id="5460896875189097758">Données stockées localement</translation>
++<translation id="4618990963915449444">Tous les fichiers de <ph name="DEVICE_NAME"/> vont être effacés.</translation>
++<translation id="614998064310228828">Modèle du périphérique :</translation>
++<translation id="1581962803218266616">Afficher dans le Finder</translation>
++<translation id="6096326118418049043">Nom X.500</translation>
++<translation id="6086259540486894113">Vous devez sélectionner au moins un type de données à synchroniser.</translation>
++<translation id="923467487918828349">Tout afficher</translation>
++<translation id="5101042277149003567">Ouvrir tous les favoris</translation>
++<translation id="4298972503445160211">Clavier danois</translation>
++<translation id="6621440228032089700">Cette fonctionnalité permet de réaliser un rendu hors écran de la texture, au lieu d'un affichage direct.</translation>
++<translation id="3488065109653206955">Partiellement activé</translation>
++<translation id="1481244281142949601">Votre système Sandbox est correctement configuré.</translation>
++<translation id="4849517651082200438">Ne pas installer</translation>
++<translation id="8602882075393902833">Activer la recherche instantanée pour accélérer la recherche et la navigation</translation>
++<translation id="6349678711452810642">Utiliser par défaut</translation>
++<translation id="6263284346895336537">Non essentielle</translation>
++<translation id="6409731863280057959">Fenêtres pop-up</translation>
++<translation id="3459774175445953971">Dernière modification :</translation>
++<translation id="73289266812733869">Désélectionné</translation>
++<translation id="3435738964857648380">Sécurité</translation>
++<translation id="9112987648460918699">Rechercher...</translation>
++<translation id="2231233239095101917">Le script de la page utilisait trop de mémoire. Rafraîchissez la page pour réactiver le script.</translation>
++<translation id="870805141700401153">Signature du code individuel Microsoft</translation>
++<translation id="5119173345047096771">Mozilla Firefox</translation>
++<translation id="9020278534503090146">Page Web inaccessible</translation>
++<translation id="4768698601728450387">Recadrer l'image</translation>
++<translation id="6245028464673554252">Si vous fermez <ph name="PRODUCT_NAME"/> maintenant, le téléchargement sera annulé.</translation>
++<translation id="3943857333388298514">Coller</translation>
++<translation id="385051799172605136">Retour</translation>
++<translation id="1742300158964248589">Impossible de graver l'image.</translation>
++<translation id="2670965183549957348">Mode de saisie du Chewing</translation>
++<translation id="5095208057601539847">Province</translation>
++<translation id="4085298594534903246">JavaScript a été bloqué sur cette page.</translation>
++<translation id="5630492933376732170">Remarque : Lorsque vous cliquez sur &quot;Envoyer&quot;, Google Chrome OS
++ joint à votre envoi un journal des événements système de
++ votre périphérique. Ces informations nous permettent de diagnostiquer les
++ problèmes, de comprendre comment vous interagissez avec votre
++ périphérique et d'améliorer les performances de ce dernier. Les
++ informations personnelles fournies sciemment dans vos commentaires ou
++ involontairement dans les journaux système et la capture d'écran sont
++ protégées conformément à nos <ph name="BEGIN_LINK"/>Règles de confidentialité<ph name="END_LINK"/>.
++ Si vous ne souhaitez pas envoyer de journaux système, décochez la case
++ &quot;Inclure les informations système&quot;.</translation>
++<translation id="4341977339441987045">Interdire à tous les sites de stocker des données</translation>
++<translation id="806812017500012252">Trier par nom</translation>
++<translation id="3781751432212184938">Afficher un aperçu des onglets...</translation>
++<translation id="2960316970329790041">Annuler l'importation</translation>
++<translation id="3835522725882634757">Ce serveur envoie des données que <ph name="PRODUCT_NAME"/> ne comprend pas. Veuillez <ph name="BEGIN_LINK"/>signaler un bug<ph name="END_LINK"/> et inclure la <ph name="BEGIN2_LINK"/>liste des raw<ph name="END2_LINK"/>.</translation>
++<translation id="5361734574074701223">Calcul de la durée restante</translation>
++<translation id="6937152069980083337">Mode de saisie Google du japonais (pour clavier américain)</translation>
++<translation id="1731911755844941020">Envoi de la requête...</translation>
++<translation id="8371695176452482769">Parlez maintenant</translation>
++<translation id="2988488679308982380">Impossible d'installer le package : &quot;<ph name="ERROR_CODE"/>&quot;</translation>
++<translation id="2904079386864173492">Modèle :</translation>
++<translation id="3447644283769633681">Bloquer tous les cookies tiers</translation>
++<translation id="8917047707340793412">Remplacer par <ph name="ENGINE_NAME"/></translation>
++<translation id="6129953537138746214">Espace</translation>
++<translation id="3704331259350077894">Arrêt du fonctionnement</translation>
++<translation id="5801568494490449797">Préférences</translation>
++<translation id="1038842779957582377">Nom inconnu</translation>
++<translation id="5327248766486351172">Nom</translation>
++<translation id="5553784454066145694">Choisir un nouveau code PIN</translation>
++<translation id="8989148748219918422"><ph name="ORGANIZATION"/> [<ph name="COUNTRY"/>]</translation>
++<translation id="4664482161435122549">Erreur d'exportation de fichier PKCS #12</translation>
++<translation id="2445081178310039857">Le répertoire racine de l'extension doit être indiqué.</translation>
++<translation id="8251578425305135684">Miniature supprimée</translation>
++<translation id="6163522313638838258">Tout développer...</translation>
++<translation id="3037605927509011580">Aie aie aie</translation>
++<translation id="5803531701633845775">Choisir les expressions en arrière-plan, sans déplacer le pointeur</translation>
++<translation id="1918141783557917887">Plu&amp;s petit</translation>
++<translation id="6996550240668667907">Afficher le clavier en superposition</translation>
++<translation id="4065006016613364460">C&amp;opier l'URL de l'image</translation>
++<translation id="6965382102122355670">OK</translation>
++<translation id="8000066093800657092">Aucun réseau détecté</translation>
++<translation id="4481249487722541506">Charger l'extension non empaquetée...</translation>
++<translation id="8180239481735238521">page</translation>
++<translation id="8321738493186308836">Active l'interface utilisateur et le code de support pour le processus du service de communication à distance, de même que le plug-in client. Avertissement : ce service n'est actuellement disponible que pour les tests de développeurs. Si vous ne faites pas partie de l'équipe de développement et ne figurez pas sur la liste blanche, aucun élément de l'interface utilisateur activée ne fonctionnera.</translation>
++<translation id="2963783323012015985">Clavier turc</translation>
++<translation id="2149973817440762519">Modifier le favori</translation>
++<translation id="5431318178759467895">Couleur</translation>
++<translation id="7064842770504520784">Personnaliser les paramètres de synchronisation...</translation>
++<translation id="2784407158394623927">Activation de votre service Internet mobile</translation>
++<translation id="3679848754951088761"><ph name="SOURCE_ORIGIN"/></translation>
++<translation id="6920989436227028121">Ouvrir dans un onglet standard</translation>
++<translation id="4057041477816018958"><ph name="SPEED"/> - <ph name="RECEIVED_AMOUNT"/></translation>
++<translation id="2050339315714019657">Portrait</translation>
++<translation id="6978839998405419496"><ph name="NUMBER_ZERO"/> days ago</translation>
++<translation id="6139139147415955203">Active un service en arrière-plan qui connecte le service <ph name="CLOUD_PRINT_NAME"/> aux éventuelles imprimantes installées sur cet ordinateur. Une fois ce labo activé, vous pouvez lancer <ph name="CLOUD_PRINT_NAME"/> en vous connectant à votre compte Google via Options/Préférences dans la section Options avancées.</translation>
++<translation id="5112577000029535889">Outils de &amp;développement</translation>
++<translation id="2301382460326681002">Le répertoire racine de l'extension est incorrect.</translation>
++<translation id="7839192898639727867">ID de clé de l'objet du certificat</translation>
++<translation id="4759238208242260848">Téléchargements</translation>
++<translation id="2879560882721503072">Le stockage du certificat client généré par <ph name="ISSUER"/> a réussi.</translation>
++<translation id="1275718070701477396">Sélectionnée</translation>
++<translation id="1178581264944972037">Suspendre</translation>
++<translation id="6492313032770352219">Taille sur le disque :</translation>
++<translation id="5233231016133573565">ID du processus</translation>
++<translation id="5941711191222866238">Réduire</translation>
++<translation id="4121428309786185360">Expire le</translation>
++<translation id="2049137146490122801">Votre administrateur a désactivé l'accès aux fichiers locaux sur votre ordinateur.</translation>
++<translation id="1146498888431277930">Erreur de connexion SSL</translation>
++<translation id="8041089156583427627">Envoyer</translation>
++<translation id="6394627529324717982">Virgule</translation>
++<translation id="253434972992662860">&amp;Pause</translation>
++<translation id="335985608243443814">Parcourir...</translation>
++<translation id="7802488492289385605">Mode de saisie Google du japonais (pour clavier Dvorak américain)</translation>
++<translation id="7452120598248906474">Police à largeur fixe</translation>
++<translation id="3129687551880844787">Stockage de session</translation>
++<translation id="7427348830195639090">Page en arrière-plan : <ph name="BACKGROUND_PAGE_URL"/></translation>
++<translation id="5898154795085152510">Le serveur a renvoyé un certificat client incorrect. Erreur <ph name="ERROR_NUMBER"/> (<ph name="ERROR_NAME"/>)</translation>
++<translation id="2704184184447774363">Signature de document Microsoft</translation>
++<translation id="5677928146339483299">Bloqué</translation>
++<translation id="1474842329983231719">Gérer les paramètres d'impression...</translation>
++<translation id="2455981314101692989">Cette page Web a désactivé la saisie automatique dans ce formulaire.</translation>
++<translation id="1646136617204068573">Clavier hongrois</translation>
++<translation id="5988840637546770870">Les versions en développement permettent de tester de nouvelles idées, mais elles peuvent s'avérer très instables. Nous vous prions d'agir avec précaution.</translation>
++<translation id="3569713929051927529">Ajouter un dossier...</translation>
++<translation id="4032664149172368180">Mode de saisie du japonais (pour clavier Dvorak américain)</translation>
++<translation id="3748706263662799310">Signaler un bug</translation>
++<translation id="7167486101654761064">&amp;Toujours ouvrir les fichiers de ce type</translation>
++<translation id="4283623729247862189">Disque optique</translation>
++<translation id="5826507051599432481">Nom commun</translation>
++<translation id="8914326144705007149">Très grande</translation>
++<translation id="4215444178533108414">Modification terminée</translation>
++<translation id="5154702632169343078">Objet</translation>
++<translation id="2273562597641264981">Opérateur :</translation>
++<translation id="122082903575839559">Algorithme de signature du certificat</translation>
++<translation id="2181257377760181418">Cette fonctionnalité permet d'afficher un onglet d'aperçu avant de lancer une impression.</translation>
++<translation id="7240120331469437312">Autre nom de l'objet du certificat</translation>
++<translation id="6900113680982781280">Activer la saisie automatique pour remplir les formulaires Web d'un simple clic</translation>
++<translation id="1131850611586448366">Le site Web à l'adresse <ph name="HOST_NAME"/> a été signalé comme étant un site de phishing. Ces sites tentent d'amener les internautes à divulguer leurs informations personnelles en se faisant passer pour des institutions de confiance, telles que des banques.</translation>
++<translation id="5413218268059792983">Rechercher directement sur <ph name="SEARCH_ENGINE"/></translation>
++<translation id="1161575384898972166">Connectez-vous à <ph name="TOKEN_NAME"/> pour exporter le certificat client.</translation>
++<translation id="1718559768876751602">Créer un compte Google maintenant</translation>
++<translation id="1884319566525838835">État Sandbox</translation>
++<translation id="2770465223704140727">Retirer de la liste</translation>
++<translation id="3590587280253938212">rapide</translation>
++<translation id="6053401458108962351">&amp;Effacer les données de navigation…</translation>
++<translation id="2339641773402824483">Vérification des mises à jour...</translation>
++<translation id="9111742992492686570">Télécharger les mises à jour de sécurité essentielles</translation>
++<translation id="8636666366616799973">Package incorrect. Détails : &quot;<ph name="ERROR_MESSAGE"/>&quot;.</translation>
++<translation id="2045969484888636535">Continuer à bloquer les cookies</translation>
++<translation id="7353601530677266744">Ligne de commande</translation>
++<translation id="2766006623206032690">Coller l'URL et y a&amp;ccéder</translation>
++<translation id="4394049700291259645">Désactiver</translation>
++<translation id="969892804517981540">Build officiel</translation>
++<translation id="445923051607553918">Se connecter à un réseau Wi-Fi</translation>
++<translation id="100242374795662595">Périphérique inconnu</translation>
++<translation id="9087725134750123268">Supprimer les cookies et autres données de site</translation>
++<translation id="5050255233730056751">URL saisies</translation>
++<translation id="3349155901412833452">Utiliser les touches , et . pour paginer une liste d'entrées</translation>
++<translation id="6872947427305732831">Vider la mémoire</translation>
++<translation id="2742870351467570537">Supprimer les éléments sélectionnés</translation>
++<translation id="7561196759112975576">Toujours</translation>
++<translation id="2116673936380190819">de moins d'une heure</translation>
++<translation id="5765491088802881382">Aucun réseau n'est disponible.</translation>
++<translation id="1971538228422220140">Supprimer les cookies et autres données de site et de plug-in</translation>
++<translation id="883487340845134897">Intervertir les touches Rechercher et Ctrl gauche</translation>
++<translation id="5692957461404855190">Faites glisser trois doigts sur la surface de votre trackpad pour afficher un aperçu de tous vos onglets. Cliquez sur une vignette pour la sélectionner. Idéal en mode plein écran.</translation>
++<translation id="1375215959205954975">Nouveau ! Configurer la synchronisation des mots de passe</translation>
++<translation id="5183088099396036950">Échec de la tentative de connexion au serveur</translation>
++<translation id="4469842253116033348">Désactiver les notifications de <ph name="SITE"/></translation>
++<translation id="7999229196265990314">Les fichiers suivants ont été créés :
++
++Extension : <ph name="EXTENSION_FILE"/>
++Fichier de clé : <ph name="KEY_FILE"/>
++
++Conservez votre fichier de clé en lieu sûr. Vous en aurez besoin lors de la création de nouvelles versions de l'extension.</translation>
++<translation id="1846078536247420691">&amp;Oui</translation>
++<translation id="3036649622769666520">Ouvrir les fichiers</translation>
++<translation id="2966459079597787514">Clavier suédois</translation>
++<translation id="7685049629764448582">Mémoire JavaScript </translation>
++<translation id="6398765197997659313">Quitter le mode plein écran</translation>
++<translation id="6059652578941944813">Hiérarchie des certificats</translation>
++<translation id="4886690096315032939">Afficher l'onglet existant si l'URL associée est demandée dans un autre</translation>
++<translation id="5729712731028706266">&amp;Afficher</translation>
++<translation id="774576312655125744">Vos données personnelles sur <ph name="WEBSITE_1"/>, <ph name="WEBSITE_2"/> et sur <ph name="NUMBER_OF_OTHER_WEBSITES"/> autres sites Web</translation>
++<translation id="6359806961507272919">SMS de <ph name="PHONE_NUMBER"/></translation>
++<translation id="4508765956121923607">Afficher la s&amp;ource</translation>
++<translation id="5975083100439434680">Zoom arrière</translation>
++<translation id="8080048886850452639">C&amp;opier l'URL du fichier audio</translation>
++<translation id="2817109084437064140">Importer et associer au périphérique...</translation>
++<translation id="3331321258768829690">(<ph name="UTCOFFSET"/>) <ph name="LONGTZNAME"/> (<ph name="EXEMPLARCITY"/>)</translation>
++<translation id="619398760000422129">Plug-ins (par ex. Adobe Flash Player, QuickTime, etc.)</translation>
++<translation id="5849869942539715694">Empaqueter l'extension...</translation>
++<translation id="7339785458027436441">Vérifier l'orthographe lors de la frappe</translation>
++<translation id="8308427013383895095">Échec de la traduction en raison d'un problème de connexion réseau</translation>
++<translation id="1801298019027379214">Code PIN incorrect. Veuillez réessayer. Nombre de tentatives restantes : <ph name="TRIES_COUNT"/></translation>
++<translation id="1384721974622518101">Vous pouvez effectuer une recherche directement à partir du champ ci-dessus.</translation>
++<translation id="992543612453727859">Ajouter les expressions au premier plan</translation>
++<translation id="3857773447683694438">Lorem ipsum dolor sit amet, consectetur adipiscing elit.</translation>
++<translation id="1244147615850840081">Opérateur</translation>
++<translation id="8203365863660628138">Confirmer l'installation</translation>
++<translation id="406259880812417922">(Mot clé : <ph name="KEYWORD"/>)</translation>
++<translation id="309628958563171656">Sensibilité :</translation>
++</translationbundle>
+diff --git a/tools/grit/grit/testdata/generated_resources_iw.xtb b/tools/grit/grit/testdata/generated_resources_iw.xtb
+new file mode 100644
+index 0000000000..86b55334c0
+--- /dev/null
++++ b/tools/grit/grit/testdata/generated_resources_iw.xtb
+@@ -0,0 +1,4 @@
++<?xml version="1.0" ?>
++<!DOCTYPE translationbundle>
++<translationbundle lang="iw">
++</translationbundle>
+diff --git a/tools/grit/grit/testdata/generated_resources_no.xtb b/tools/grit/grit/testdata/generated_resources_no.xtb
new file mode 100644
-index 0000000000..aa90a95eb3
+index 0000000000..913638ba4e
--- /dev/null
-+++ b/tools/clang/plugins/tests/nested_class_inline_ctor.cpp
++++ b/tools/grit/grit/testdata/generated_resources_no.xtb
+@@ -0,0 +1,4 @@
++<?xml version="1.0" ?>
++<!DOCTYPE translationbundle>
++<translationbundle lang="no">
++</translationbundle>
+diff --git a/tools/grit/grit/testdata/grit_part.grdp b/tools/grit/grit/testdata/grit_part.grdp
+new file mode 100644
+index 0000000000..c8e9d92692
+--- /dev/null
++++ b/tools/grit/grit/testdata/grit_part.grdp
@@ -0,0 +1,5 @@
-+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+// Use of this source code is governed by a BSD-style license that can be
-+// found in the LICENSE file.
++<?xml version="1.0" encoding="UTF-8"?>
++<grit-part>
++ <!-- Important for test purposes that this file not exist. -->
++ <structure type="chrome_scaled_image" name="IDR_DOES_NOT_EXIST" file="does-not-exist.png" />
++</grit-part>
+diff --git a/tools/grit/grit/testdata/header.html b/tools/grit/grit/testdata/header.html
+new file mode 100644
+index 0000000000..8e9d10ec50
+--- /dev/null
++++ b/tools/grit/grit/testdata/header.html
+@@ -0,0 +1,39 @@
++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
++<html><head><title>[$~TITLE~$]</title>
++<meta http-equiv="cache-control" content="no-cache">
++<meta http-equiv="content-type" content="text/html; charset=utf-8">
++<meta http-equiv="pragma" content="no-cache">
++<meta http-equiv="expires" content="-1">
++[EXTRA_META]
++<style>
++BODY,TD,DIV,.P,A { FONT-FAMILY: arial,sans-serif}
++DIV,TD { COLOR: #000}
++.f { COLOR: #6f6f6f}
++.fl:link { COLOR: #6f6f6f}
++A:link, .w, A.w:link, .w A:link { COLOR: #00c}
++A:visited { COLOR: #551a8b}
++.fl:visited { COLOR: #551a8b}
++A:active, .fl:active { COLOR: #f00}
++.h { COLOR: #3399CC}
++.i { COLOR: #a90a08}
++.i:link { COLOR: #a90a08}
++.a, .a:link, .a:visited { COLOR: #008000}
++DIV.n { MARGIN-TOP: 1ex}
++.n A { FONT-SIZE: 10pt; COLOR: #000}
++.n .i { FONT-WEIGHT: bold; FONT-SIZE: 10pt}
++.q A:visited { COLOR: #00c}
++.q A:link { COLOR: #00c}
++.q A:active { COLOR: #00c}
++.q { COLOR: #00c}
++.b { FONT-WEIGHT: bold; FONT-SIZE: 12pt; COLOR: #00c}
++.ch { CURSOR: hand}
++.e { MARGIN-TOP: 0.75em; MARGIN-BOTTOM: 0.75em}
++.g { MARGIN-TOP: 1em; MARGIN-BOTTOM: 1em}
++.f { MARGIN-TOP: 0.5em; MARGIN-BOTTOM: 0.25em}
++.s { HEIGHT: 10px }
++.c:active { COLOR: #ff0000}
++.c:visited { COLOR: #551a8b}
++.c:link { COLOR: #7777cc}
++.c { COLOR: #7777cc }
++</style>
++</head>
+\ No newline at end of file
+diff --git a/tools/grit/grit/testdata/homepage.html b/tools/grit/grit/testdata/homepage.html
+new file mode 100644
+index 0000000000..cce4f2cf35
+--- /dev/null
++++ b/tools/grit/grit/testdata/homepage.html
+@@ -0,0 +1,37 @@
++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
++<html><head><title>Google Desktop Search</title>
++<meta http-equiv="content-type" content="text/html; charset=utf-8">
++<meta http-equiv="cache-control" content="no-cache">
++<meta http-equiv="pragma" content="no-cache">
++<meta http-equiv="expires" content="-1">
++<style>
++BODY,TD,A,P {FONT-FAMILY: arial,sans-serif}
++.q {COLOR: #0000cc}
++</style>
++<script>
++<!--
++function sf(){document.f.q.focus();}
++// -->
++</script>
++</head>
++<BODY text=#000000 vLink=#551a8b aLink=#ff0000 link=#0000cc bgColor=#ffffff onload=sf()>
++<center>
++<TABLE cellSpacing=0 cellPadding=0 border=0>
++<tr><td><a href="[$~HOMEPAGE~$]"><IMG border=0 height=110 alt="Google Desktop Search" src="hp_logo.gif" width=276></a></td></tr></table><BR>
++<FORM name=f method=GET action='[$~SEARCHURL~$]'>
++<TABLE cellSpacing=0 cellPadding=4 border=0>
++<tr>
++<TD class=q noWrap><FONT size=-1>
++[$~LINKS~$]
++</font></td>
++</tr></table>
++<table cellspacing=0 cellpadding=0>
++<tr vAlign=top>
++<td width=25%>&nbsp;</td>
++<td align=center><input maxlength=256 size=62 name=q value="[DISP_QUERY]"><br><input type=submit value="Search Desktop" name=btnG><INPUT type=submit value="Search the Web" name="redir" accesskey=w></td>
++<td align=left valign=top nowrap width=25%><font size=-2>&nbsp;&nbsp;<A href="[$~PREFERENCES~$]">Desktop&nbsp;Preferences</a></font></td>
++</tr></table></FORM>
++<p><FONT color=#224499><B>Search your own computer.</B></font></p>
++<span style='width:29em'>[$~MESSAGE~$]</span><br>
++<br><FONT size=-1>[$~SETHOMEPAGE~$][$~BOTTOMLINE~$]</font></p>
++<p><FONT size=-2>&copy;2005 Google - Searching [NUM_ITEMS] items</font></p></center></body></html>
+\ No newline at end of file
+diff --git a/tools/grit/grit/testdata/hover.html b/tools/grit/grit/testdata/hover.html
+new file mode 100644
+index 0000000000..b8f9ce0200
+--- /dev/null
++++ b/tools/grit/grit/testdata/hover.html
+@@ -0,0 +1,177 @@
++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
++<html><head>
++<meta http-equiv="content-type" content="text/html; charset=utf-8">
++<meta http-equiv="cache-control" content="no-cache">
++<meta http-equiv="pragma" content="no-cache">
++<meta http-equiv="expires" content="-1">
++<style>
++BODY { FONT-SIZE: 8pt; FONT-FAMILY: verdana,arial,san-serif }
++P { FONT-SIZE: 8pt; FONT-FAMILY: verdana,arial,san-serif }
++TD { FONT-SIZE: 8pt; FONT-FAMILY: verdana,arial,san-serif }
++A { FONT-SIZE: 8pt; FONT-FAMILY: verdana,arial,san-serif }
++DIV { FONT-SIZE: 8pt; TEXT-DECORATION: none }
++A:hover { COLOR: #ffffff }
++.border { BORDER-RIGHT: 0px; BORDER-TOP: 0px; BORDER-LEFT: 0px; BORDER-BOTTOM: 0px }
++</style>
++
++<!-- menu experiment start -->
++
++<style>
++<!--
++.menu1 {
++cursor:default;
++position:absolute;
++left: 10;
++top: 0;
++text-align: left;
++font-family: Arial, Helvetica, sans-serif;
++font-size: 8pt;
++background-color: menu;
++visibility: hidden;
++padding-top: 2px;
++padding-bottom: 2px;
++border: 1 solid;
++border-color: #888888;
++z-index: 100;
++}
++.menuitems {
++padding-left: 5px;
++padding-right: 5px;
++}
++-->
++</style>
++<SCRIPT LANGUAGE="JavaScript1.2">
++<!--
++var menustyle = "menu1";
++
++function showmenu() {
++ // var rightedge = document.body.clientWidth-event.clientX;
++ // var bottomedge = document.body.clientHeight-event.clientY;
++ // if (rightedge < rcmenu.offsetWidth)
++ // rcmenu.style.left = document.body.scrollLeft + event.clientX - rcmenu.offsetWidth;
++ // else
++ // rcmenu.style.left = document.body.scrollLeft + event.clientX;
++ // if (bottomedge < rcmenu.offsetHeight)
++ // rcmenu.style.top = document.body.scrollTop + event.clientY - rcmenu.offsetHeight;
++ // else
++ // rcmenu.style.top = document.body.scrollTop + event.clientY;
++
++ rcmenu.style.visibility = "visible";
++ // rcmenu.style.zindex = 0;
++ // document.all('rcmenu').style.zindex = 20;
++ document.onkeydown=ck;
++ return false;
++}
++
++function hidemenu() {
++ rcmenu.style.visibility = "hidden";
++}
++
++function ck(e){
++ evt=document.all?window.event:e;
++ k=document.all?window.event.keyCode:e.keyCode;
++
++ if(k==27 /*<Esc>*/) {
++ hidemenu();
++ }
++}
++
++function menumouseover() {
++ if (event.srcElement.className == "menuitems") {
++ event.srcElement.style.backgroundColor = "highlight";
++ event.srcElement.style.color = "white";
++ }
++}
++
++function menumouseout() {
++ if (event.srcElement.className == "menuitems") {
++ event.srcElement.style.backgroundColor = "";
++ event.srcElement.style.color = "black";
++ window.status = "";
++ }
++}
++
++function menuselect() {
++ if (event.srcElement.className == "menuitems") {
++ if (event.srcElement.getAttribute("target") != null)
++ window.open(event.srcElement.url, event.srcElement.getAttribute("target"));
++ else if (event.srcElement.url.length)
++ window.location = event.srcElement.url;
++ }
++}
++// -->
++</script>
++
++<!-- menu experiment end -->
++
++</head>
++<BODY bottomMargin=0 bgColor=#3300cc leftMargin=0 topMargin=0 rightMargin=0 marginwidth="0" marginheight="0" border=0 style="border-width:0;" scroll=no>
++
++<!-- <br> -->
++
++<!-- menu experiment start -->
++
++<div id="rcmenu" class="skin0" onMouseover="menumouseover()" onMouseout="menumouseout()" onClick="menuselect();">
++<span class="menuitems" url="[$~SETDISP1~$]">Sidebar</span>
++<span class="menuitems" url="[$~SETDISP4~$]">Minibar</span>
++<span class="menuitems" url="[$~HIDE2~$]">Close</span>
++</div>
++
++<script language="JavaScript1.2">
++if (document.all && window.print) {
++ rcmenu.className = menustyle;
++ document.oncontextmenu = showmenu;
++ document.body.onclick = hidemenu;
++}
++</script>
++
++<!-- menu experiment end -->
++
++<script>
++function hide() {
++ return 1;
++ // return confirm("Are you sure you want to hide the minibar?\nYou can show it again in Google Desktop Search Preferences. ");
++}
++function clear() {
++ document.getElementById('q').value='';
++}
++</script>
++
++<TABLE cellSpacing=0 cellPadding=0 bgColor=#3300cc border=0><TBODY>
++<tr><TD vAlign=top>
++
++<form method=get action="[$~SEARCHURL~$]" [$~SEARCH_TARGET~$] name=f1 ID="f1" onsubmit="window.setTimeout('clear()', 500)">
++<input type=hidden name=src value=3>
++<input type=hidden name=redir value='' ID="redir">
++
++<TABLE cellSpacing=0 cellPadding=0 bgColor=#3300cc border=0><TBODY>
++
++<tr>
++<!-- border-top: #414a4f 0px solid; -->
++<!-- z-index:2; z-order:2; -->
++<TD vAlign=top>&nbsp;<INPUT name=q style="position:relative; height=19px;" class=border size=12>&nbsp;</td>
++
++<TD TABINDEX="2" onkeydown='if(event.keyCode!=16&&event.keyCode!=9)onclick()' onclick="f1.submit();q.value=''" class=ch onmouseover="this.bgColor='6666FF'" style="BORDER-RIGHT: #000000 1px solid; BORDER-TOP: #6666cc 1px solid; BORDER-LEFT: #6666cc 1px solid; BORDER-BOTTOM: #000000 1px solid" onmouseout="this.bgColor='#000099'" vAlign=center align=middle bgColor=#000099><IMG src="logo.gif" align=middle></td>
++
++<TD width=2><IMG height=1 width=1></td>
++
++<TD TABINDEX="3" onkeydown='if(event.keyCode!=16&&event.keyCode!=9)onclick()' onclick="redir.value='google'; f1.submit(); redir.value='';q.value=''" class=ch onmouseover="this.bgColor='6666FF'" style="BORDER-RIGHT: #000000 1px solid; BORDER-TOP: #6666cc 1px solid; BORDER-LEFT: #6666cc 1px solid; BORDER-BOTTOM: #000000 1px solid" onmouseout="this.bgColor='#000099'" vAlign=center align=middle bgColor=#000099><IMG src="gfavicon.ico" align=middle></td></tr>
++</TBODY></table>
++</td>
++
++<TD width=5><IMG height=1 width=1></td>
++
++<TD vAlign=top>
++
++<TABLE cellSpacing=0 cellPadding=1 bgColor=#000099><TBODY>
++<tr><TD TABINDEX="4" onkeydown='if(event.keyCode!=16&&event.keyCode!=9)onclick()' valign=top onclick="location.href='[$~SETDISP1~$]';" class=ch onmouseover="this.bgColor='6666FF'" style="BORDER-RIGHT: #000000 1px solid; BORDER-TOP: #6666cc 1px solid; BORDER-LEFT: #6666cc 1px solid; BORDER-BOTTOM: #000000 1px solid" onmouseout="this.bgColor='#000099'" vAlign=top noWrap bgColor=#000099><IMG src="down.gif"></td></tr></TBODY></table>
++
++</td>
++
++<TD width=1><IMG height=1 width=1></td>
++
++<TD vAlign=top><TABLE cellSpacing=0 cellPadding=1><TBODY>
++<tr><TD TABINDEX="5" onkeydown='if(event.keyCode!=16&&event.keyCode!=9)onclick()' valign=top onclick="if (hide())location.href='[$~HIDE2~$]';" class=ch onmouseover="this.bgColor='6666FF'" style="BORDER-RIGHT: #000000 1px solid; BORDER-TOP: #6666cc 1px solid; BORDER-LEFT: #6666cc 1px solid; BORDER-BOTTOM: #000000 1px solid" onmouseout="this.bgColor='#000099'" vAlign=top noWrap bgColor=#000099><IMG src="close.gif"></td></tr></TBODY></table></td></tr></TBODY></table>
++
++</form>
++</body></html>
+diff --git a/tools/grit/grit/testdata/include_test.html b/tools/grit/grit/testdata/include_test.html
+new file mode 100644
+index 0000000000..e08f2e2e8a
+--- /dev/null
++++ b/tools/grit/grit/testdata/include_test.html
+@@ -0,0 +1,31 @@
++<include src="included_sample.html">
++<if expr="True">
++should be kept
++</if>
++in the middle...
++<if expr="False">
++should be removed
++</if>
++
++<if expr="False">
++should be removed
++ <if expr="True">
++ should be removed because outer expr is False
++ </if>
++should be removed
++</if>
++
++<if expr="True">
++ <if expr="True">
++ <if expr="True">
++ nested true should be kept
++ </if>
++ <if expr="False">
++ should be removed
++ </if>
++ </if>
++ <if expr="True">
++ silbing true should be kept
++ </if>
++</if>
++at the end...
+diff --git a/tools/grit/grit/testdata/included_sample.html b/tools/grit/grit/testdata/included_sample.html
+new file mode 100644
+index 0000000000..7150ffcbea
+--- /dev/null
++++ b/tools/grit/grit/testdata/included_sample.html
+@@ -0,0 +1 @@
++Hello Include!
+diff --git a/tools/grit/grit/testdata/indexing_speed.html b/tools/grit/grit/testdata/indexing_speed.html
+new file mode 100644
+index 0000000000..db1787b0e2
+--- /dev/null
++++ b/tools/grit/grit/testdata/indexing_speed.html
+@@ -0,0 +1,58 @@
++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
++<html><head><title>Google Desktop Search Index Speed</title>
++<meta http-equiv=content-type content="text/html; charset=utf-8">
++<style>
++BODY {
++ MARGIN-LEFT: 3em; MARGIN-RIGHT: 3em; FONT-FAMILY: arial,sans-serif
++}
++</style>
++</head>
++<BODY text=#000000 vLink=#551a8b aLink=#ff0000 link=#0000cc bgColor=#ffffff>
++<TABLE cellSpacing=2 cellPadding=0 width="100%" border=0>
++ <tr>
++ <TD vAlign=top width="1%"><A href="[$~HOMEPAGE~$]">
++ <IMG alt="Go to Google Desktop Search" src="/logo3.gif" border=0></A></td>
++ <td>&nbsp;</td>
++ <TD noWrap>
++ <TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
++ <tr>
++ <TD bgColor=#3399CC><IMG height=1 alt="" width=1></td>
++ </tr>
++ </table>
++ <TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
++ <tr>
++ <TD noWrap bgColor=#efefef><B>&nbsp;Index Speed</B></td>
++ <TD noWrap align=right bgColor=#efefef><FONT size=-1><A href="/customize.html">Index Speed
++ Help</A> | <A href="[$~ABOUT~$]"> About Google Desktop Search</A></font></td>
++ </tr></table></td></tr></table>
++<FONT size=-1>
++<p>
++To make your emails, files, and previously viewed web pages searchable, Google Desktop Search
++needs to index them. This indexing process is currently occuring in the background
++and your computer performance is minimally impacted.
++<p>
++You have the option of speeding up this process.
++<p><B><FONT color=#FF0000>Warning:</font></B> Speeding up indexing will cause your computer
++to become unusable for many minutes, depending on how many items need to be indexed. FAST INDEXING IS NOT
++RECOMMENDED.
++<BR>&nbsp;<BR>
++<FORM action="[$~SETINDEXSPEED~$]" method=GET>
++<input name=url value="[PREVPAGE]" type=hidden>
++<input type=radio name=FAST value="0" [FAST0-CHECKED] id=f0><label for=f0>Use background indexing (recommended)</label><br>
++<input type=radio name=FAST value="1" [FAST1-CHECKED] id=f1><label for=f1>Use fast indexing</label><br><br>
++<input type=submit value="Set Indexing Speed">
++</FORM>
++<BR>
++
++<p>&nbsp;<BR>
++<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
++<TR bgColor=#3399CC>
++ <TD align=middle height=1><IMG height=1 alt="" width=1></td></tr>
++</table>
++
++<TABLE cellSpacing=0 cellPadding=0 width="100%" align=center bgColor=#efefef border=0>
++<tr>
++ <TD align=middle height=20><FONT size=-1><A href="[$~HOMEPAGE~$]">Google Desktop Search&nbsp;Home</A> - <a href="[$~STATUS~$]">Status</a> - <A href="[$~ABOUT~$]">About Google Desktop Search</A> - [$~BUILDNUMBER~$] - &copy;2005 Google </font> </td></tr>
++</table><BR>
++</body>
++</html>
+\ No newline at end of file
+diff --git a/tools/grit/grit/testdata/install_prefs.html b/tools/grit/grit/testdata/install_prefs.html
+new file mode 100644
+index 0000000000..eca0b56de5
+--- /dev/null
++++ b/tools/grit/grit/testdata/install_prefs.html
+@@ -0,0 +1,92 @@
++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
++<html><head><title>Google Desktop Search: Initial Preferences</title>
++<meta http-equiv="cache-control" content="no-cache">
++<meta http-equiv="content-type" content="text/html; charset=utf-8">
++<meta http-equiv="pragma" content="no-cache">
++<meta http-equiv="expires" content="-1">
++<style>
++BODY { FONT-FAMILY: arial,sans-serif }
++.c:active { COLOR: #FF0000 }
++.c:visited { COLOR: #7777CC }
++.c:link { COLOR: #7777CC }
++</style>
++<script>
++<!--
++override = 1;
++function ee() {if (override==1) {(new Image()).src="[COMPLETING]";}}
++// -->
++</script>
++</head><body leftmargin=30 rightmargin=30 onresize="stw()" onunload="ee()">
++<form onsubmit='override=0;return true;' action='[STEP2]' name=f method=post>
++<img src="logo3.gif" border=0>
++<div id=c1 style="width:600px">
++<br><font color=#00218a><b>To continue, please set these initial preferences:</b></font><br><br>
++<table border=0 id=t1 width=100%>
++<tr>
++ <td valign=top><input name=AIM id=chat type=checkbox checked></td>
++ <td>&nbsp;</td><td><label for=chat><font size=-1><B>Enable search over Instant Messenger chats</b><br>
++ <font size=-1>Google Desktop Search will store your chats and make them searchable.
++</font></label></td></tr>
++<tr height=1><td height=10px></td></tr>
++<tr>
++ <td valign=top><input name=HTTPS id=https type=checkbox checked></td>
++ <td>&nbsp;</td><td><label for=https><font size=-1><b>Enable search over secure web pages (HTTPS)</b>
++ <br><font size=-1>Google Desktop Search will store secure web pages that you view and make them
++ searchable.</font></label> </td></tr>
++<tr height=1px><td height=10px></td></tr>
++
++<tr>
++ <td valign=top><input name=SEARCHBOX id=SEARCHBOX type=checkbox checked
++ onclick="handleSBClick(this)"></td>
++ <td>&nbsp;</td><td><label for=searchbox><font size=-1><b>Display search box</b></label>
++ <br><table border=0 cellpadding=0><tr><td valign=top>
++
++<input type="radio" name="SBDISPLAY" id="DISPLAYDB" [DB-CHECKED] value="DISPLAYDB"></td><td>
++<label for=DISPLAYDB><font size=-1>Deskbar - A search box in your taskbar</font></label></td></tr>
++<tr><td></td></tr>
++<tr><td></td><td><img src="deskbar.gif" alt="Deskbar" width="268" height="34"></td></tr>
++<tr><td height=2></td></tr>
++<tr><td valign=top>
++
++<input type="radio" name="SBDISPLAY" id="DISPLAYMB" [MB-CHECKED] VALUE="DISPLAYMB"></td><td>
++<label for=DISPLAYMB><font size=-1>Floating Deskbar - A search box that you can put anywhere on your desktop</font></label></td></tr>
++<tr><td></td></tr>
++<tr><td></td><td><img src="minibar.gif" width="137" height="27"></td></tr>
++<tr><td height=2></td></tr>
++
++</table>
++</td></tr>
++
++<tr>
++ <td valign=top><input name=SENDDATA id=usage type=checkbox checked></td>
++ <td>&nbsp;</td><td><label for=usage><font size=-1><b>Help us improve Google Desktop Search by sending usage data and crash reports</b></label>
++</font></td></tr>
++<tr height=8px><td colspan=3 height=8px></td></tr>
++<tr><td colspan=3><font size=-1>You can change these and other preferences at any time.</font></td></tr>
++</table></div>
++<p><input type=submit value="Set Preferences and Continue" id=s><br>
++</form>
++</center>
++[SCRIPT]
++<script>
++<!--
++function handleSBClick(checkbox) {
++ document.getElementById("DISPLAYDB").disabled = !checkbox.checked;
++ document.getElementById("DISPLAYMB").disabled = !checkbox.checked;
++}
++function stw() {
++if (document.all && document.body.clientWidth < 600) {
++ var w = document.body.clientWidth-35;
++ if (w < 10) { w = 10; }
++ w = w + 'px';
++ document.getElementById('c1').style.width=w;
++ return false;
++}
++document.getElementById('c1').style.width='600px';
++}
++stw();
++document.f.s.focus();
++// -->
++</script>
++<img SRC="http://www.google.com" WIDTH="0" HEIGHT="0" ALIGN="right"></img>
++</body></html>
+\ No newline at end of file
+diff --git a/tools/grit/grit/testdata/install_prefs2.html b/tools/grit/grit/testdata/install_prefs2.html
+new file mode 100644
+index 0000000000..18380397c2
+--- /dev/null
++++ b/tools/grit/grit/testdata/install_prefs2.html
+@@ -0,0 +1,52 @@
++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
++<html><head><title>Indexing has Started</title>
++<meta http-equiv="cache-control" content="no-cache">
++<meta http-equiv="content-type" content="text/html; charset=utf-8">
++<meta http-equiv="pragma" content="no-cache">
++<meta http-equiv="expires" content="-1">
++<style>
++BODY { FONT-FAMILY: arial,sans-serif }
++}
++</style>
++<script>
++<!--
++override = 1;
++function ee() {if (override==1) {(new Image()).src="[COMPLETING]";}}
++// -->
++</script>
++</head><body leftmargin=30 rightmargin=30 onresize="stw()" onunload="ee()">
++<form onsubmit='override=0;return true;' action="[STEP3]" name=f>
++<img src="/logo3.gif" border=0><br><br>
++<div id=c1 style="width:575px">
++<table border=0 id=t1 width=100%><tr><td>
++<font color=#00218a><b>One-time indexing has started.</b></font><br><br>
++<font size=-1>An index is being prepared on your computer to allow you
++to search your information as fast as you can search the web.<br><br>
++<li>This is a one-time process that may take several hours.
++<li>You may continue to use your computer as usual and it is safe to shut down your computer.
++<li>Indexing will be performed only when your computer is idle.
++</ul>
++</font>
++<p><input type=submit value="Go to the Desktop Search homepage" name=s><br>
++</center>
++</td></tr></table>
++</form>
++</div>
++<script>
++<!--
++function stw() {
++if (document.all && document.body.clientWidth < 575) {
++ var w = document.body.clientWidth-35;
++ if (w < 10) { w = 10; }
++ w = w + 'px';
++ document.getElementById('c1').style.width=w;
++ return false;
++}
++document.getElementById('c1').style.width='575px';
++}
++stw();
++// -->
++document.f.s.focus();
++</script>
++[SCRIPT]
++</body></html>
+\ No newline at end of file
+diff --git a/tools/grit/grit/testdata/klonk-alternate-skeleton.rc b/tools/grit/grit/testdata/klonk-alternate-skeleton.rc
+new file mode 100644
+index 0000000000000000000000000000000000000000..5f2c82a55469ddaab246c095826ad9e6743c0015
+GIT binary patch
+literal 1088
+zcmbW0Pfr3t48`AhKZV)z#sGrI5!gjnU?DC>IT2z!82=r_L=!)}zjnmk5b$6oGo9&7
+z+t=4lu9UG-Ujxl_t%b{59ih$9PSBn!lW7`iGrKMm&Q10vTRK5!yRJHlRN`fcWril@
+zv|?uHM))d_NBa7`nW9TQ&PZ3tsax6ojav@U&9TYdHduz6k{G4GFTfpX_hk&`!z0F`
+z!gJ>6WBh&UO&i_oS+VOvUfcD9JR=y&;3OxP2%KT$#JB9W=UtgQpDT@>(E^#^A;oG%
+z4omjIK7rLXcRgmyS+%u_Gl2`MhOxMB{GGM&5o6cXF<vdhEe5Mu-+3OQZF~Ht$8Yl5
+z&=^M*j(xG~y3(sxz{#AtW^kPoyR!dZ9)}Sd$_6;Qjx#V<A+O@5j%7~Al)9jj*71v4
+z<zn{ZUuJA?73tB}iB6fJ)6H}8)1l|&XFq3N%P!P%;Wv{#b&7SVweIxDUCbEh>E~=G
+z`!#F5=z%_bq95y7+aIx?IdcSN`A)xX^vZjCS7lnS#=iZ)E7W%eX8!kr-#RDO36^!o
+Qqn&wY8qX0d7T}2V4SbP+*8l(j
+
+literal 0
+HcmV?d00001
+
+diff --git a/tools/grit/grit/testdata/klonk.ico b/tools/grit/grit/testdata/klonk.ico
+new file mode 100644
+index 0000000000000000000000000000000000000000..d371b214dc366249870efccc5da278d023aa91f1
+GIT binary patch
+literal 766
+zcmeH_F%H5o5CqSFL>Ve71Sxq1;TtsMEB*!Fv6LbmDQFSUL1mXjO7OC0gpl|G?B1ML
+zXS=a1V(2`di0U>FnQ~o{oUDnF5j(}bxAgSuhE8lMu~rkI8Ju%mb%Im^Xd<+ZwEgwd
+zFTg+WQOj5lfvO*4mbEA^bCj9KHh65vjx^zvsKW{eA8|Ah`w&r;%!`QgmEiG3hXCcC
+L+@V2V6oA7MJIRBx
+
+literal 0
+HcmV?d00001
+
+diff --git a/tools/grit/grit/testdata/klonk.rc b/tools/grit/grit/testdata/klonk.rc
+new file mode 100644
+index 0000000000000000000000000000000000000000..35652c4e6dd7cf7b7f62f637e191acf66f487235
+GIT binary patch
+literal 9824
+zcmeI2+j1L48poSUd<zdSObP?FE=Nu{E-aL6%Z`X;t1MXwwb^nxw&R#MQm|was(2h8
+z0*VVRxZyQ;25z?&e*drad1j>1!LS!j6*Z-q>7MTIeClrf{=b{yW=KLKoQA`29(tkA
+z?@<`g*P*W;F2X@LqqP?P!IgxQa2&e)&gmcUJfiQMr{-PocF21|OVCckGsY~31#sNt
+zeuJJaU(OhLWaHAYxy#{kNExfq8uQ5J2xc`jLo2kyURV$HuoL#fZm7|_&ii)Q3SZFE
+z;@$|W^lb4S@e23#yIdxsED4)%Ix5vi$fg&b@^yerB!M>k-sfJ2-!(XtBx>~E;y0>;
+zywqpO@eUBz4c2yv49m3k+_Z88eb3Rg>+A-4?GCk8rmyLEuD7;k@iyBQuQz|u4r}P|
+z1pk!hKgO!w#>STMq~-8ViH-G#etL?RCgF{OzaBBS8aA-k=%+1wau1JP!(#WbwJk2e
+z{FW=3II|6mUA$wTS=-Ei$KrzUMVn6ea?kwXHeRp*%qrtH8Cm5n-|(IYVUu<pe(r=N
+zzO@*)I&s84Ull`c5XBVjPVmJ8W*uVn!oE+xdXM3B1?=zfi}cBtkC36HBDof6y#96&
+zDNK-*c<mwsaiN$Tt;G8iy#LgqQ-aMX7AOxWcPO4D;cMihSg+XijJE^e#f+h-em)#K
+zU}i#pm$ov9MjtR<GnAE-XHJcd#M&7}G3rSx$}4^5MSA<RMTcOD8qE;QGcM((Z-!r=
+z@>HA@wRN;~7h6y+xyz_&R~;+XxM^eZ-_q~|%%b86_{3Asa-8FBk+Z7c-kJgN>UjHR
+zv*J6C_vNv`hUxGkXMvL0T0vKhVQf$p6QjfeUR}fgl_wW2W!gk%O?<jZPZ}19O{d7^
+z*finVDx2ru9D3dIaKoU~fb#-41E46PT;&oc4LDIw7tD-Ohf;>IO<b0BC*h%aN($z?
+zm)50Lg3jeb@}4KgpHn7``|w@I(iDZ;#6d+vaXpT`D6f;D{i-%|`usUfYCfinmyGTN
+zIW7V>a`tbcYLDwE{AY?>BR88vkIj3pcp9ftwy~b;A8i-;T|_p=$nY5yWU!`jTE^ib
+ze*F+mE-Vf$<AuvpIC5FVr`t!>>e;=5g*fg0^w_NUeElxZA2EAWiGRui^1Zl<=<)P1
+zF&Y;=yo$%KVIA>VGwa=@)kgQbrt31jq~WuvvL2VO`$<s`-l~FW4S%T*JzWty@3kqC
+zpB4rFU-(`|ov-8B%D+84yQpbJq|Cy#a=VYFm5(Lg9joHhbBjy*SqUH5^H#VWD)#mP
+zmDd8gX|wiIT+{3pP+PpWiFV4=ZF*H_#xD)})(!p!_EWXI5x?KFnQQblnWI&vvb<)-
+zFIrzJTT2IfU>zNqGSmHCaU;Y2q0yQ$JF7mTwL~ub{sOMb^Vh8GFZ(K1F-x>#wroJR
+z&tF1@??TN-{BD^Hb<bj)tU9hU-SUgid^Mw80(r42u2^L$1ARm5q2(uK*A(fk5cewP
+z9Zr$-B@Y%=OVA@~R*aezo@z;A8C69Z##=4Z+%_6(qSKmXx%;{Kv$<M>gJ;mLeTx&a
+ztSZO1p-!t5NvMLINn_JEi1N%h$mrKfeZ%Sxtv*(<o;FujMW(#py@aoK$>Sq%E`|5`
+zMQa!2rJ*fu!l%|$%^a7pE^XVFE$AM-((pNcct~BK8YqR1Sd~Aqmi*&@D)rQ&bN`YW
+zMPvC%+;<TLnyH+o+P!PzGEPTvj<#1#Q&p3I;<v-i%S09-uHQ3$KQw!lbu5_YDT~KE
+zq3F><0?G_uSf2bldXz_x`Rp$tXUa07m0#5g^O>n*TJE4PW#|}5_jztxOjO*+fAM}<
+zk=Lii5sD#879SKTSIp++>5AlgXumxIv246U-XKqCe;}^ATDIP+P{%8`Yynw2Uilpc
+z$xha}X;{ahB+#YVajq(xJ|2|kCBqoURxZc-PC<V34wS`l@7lObCdzS5sL5l@zQ+BG
+z;+Tl3tUl7t#}1OyYFBw_V3AMzKfW@m<J*t$@OdlXBE$+_TOoq!`H*`aipPX9y8N3z
+zJLpP#o#HyZq-`Au=XaT7{)rj2n4zkrdkJOKOvhNvbdE_@DQ#r;l~PX2VN1f=r#R=S
+z`e>WGR&NeH+c%h>-Yw>z7_{+>=5WWql;yg~F}<jhong+@E{wQv`%$Z$n`LNxVSLVu
+zqX`bJ2rtN9gE2WJxg8d*6Uugv=9gd**I(1S$3)lvXuIe$9VB*sDZi`wUr{S<ASs*o
+zEyw#FTC@PgtLUAvrjGSZrVFRipYc2<9~H+>V-&+XR>jna$uG|ylUKW<KRZ>)Rw*m^
+z_oOjp@vHny>%lMrW)jt@&DG$}J`tOC!twxncz`|R{U9wplS^%1SD7h)zLPS$9KxSJ
+z^(luqF00#DmestFZxDq%2fL5@KE>#H<EVwdOuH`m{4TpYASY`FCbM&`$abwl+vH7a
+za;>I|)#R(g69An@YFD|(_t^K?Y(=LYvGR$s)LKbvaYc(JPp$Xb2G?a>eC9KE-cEhZ
+zHSZ3+_C$Rze-w`BSsn7ZgI%TJO=9FfdDBy)V;pqaYpnOHjNdZ)cZhIWOV;71NPE_b
+z5ZwYd@EV=tI))^?mN>3>KBO~=3-s|NvQu_bO!m`Xy&s`1RS8A9bec9lO`@(ym$)sX
+zMVVq?wjta)kvTJp%Bk>bSh}4@HcmwuW}T<$ta~!gT03ja*d|hI1w9*Uk>}TwPvL12
+z=Q{J$UgQ8RXmu+(2GDd)J#{>6mrEh;W{57|8=6JgB)U>?#`vQXEaBEZgsP}6H0c~I
+zlTn_wQLB~3>U1IQ2y}Rh=cM|##66Rnd!p7F(K=LbM6B`LtO3?OS3Ko>03~gD6g5tu
+zOSRooa>4*SqvO;gSO;d)IuFc4e&rSY3#4arR~e}tmqXie5w!0rzg2#y{KWm2%CD85
+zD?e8LTlv1?UR>st9pKlDtGM^mfuA&df=7MIT`QQ#k8mnxoriygx5#|&^UZ&6F?Nx!
+z2jMH^+zTJm>H?vU#6L!6XLz9~{RHheL_xo4SVUcx*(c|e8ZfVRzJC37^PM7DoUXW9
+zRu0v_b;|ztF`73W!u5N4HWX!l^<O!vStkE0N0PgK{5wU`>ZH1;i+3m{&0Ya4gg*c`
+C>9bG(
+
+literal 0
+HcmV?d00001
+
+diff --git a/tools/grit/grit/testdata/ko_oem_enable_bug.html b/tools/grit/grit/testdata/ko_oem_enable_bug.html
+new file mode 100644
+index 0000000000..f2c199cc15
+--- /dev/null
++++ b/tools/grit/grit/testdata/ko_oem_enable_bug.html
+@@ -0,0 +1 @@
++<IMG style="VERTICAL-ALIGN: middle" height=16 alt=아웃룩 src="/email.gif" width=16>
+\ No newline at end of file
+diff --git a/tools/grit/grit/testdata/ko_oem_non_admin_bug.html b/tools/grit/grit/testdata/ko_oem_non_admin_bug.html
+new file mode 100644
+index 0000000000..b9e8a1f288
+--- /dev/null
++++ b/tools/grit/grit/testdata/ko_oem_non_admin_bug.html
+@@ -0,0 +1 @@
++<INPUT id=s type=submit value="&nbsp;&nbsp;확인&nbsp;&nbsp;">
+\ No newline at end of file
+diff --git a/tools/grit/grit/testdata/mini.html b/tools/grit/grit/testdata/mini.html
+new file mode 100644
+index 0000000000..8ac0a231a0
+--- /dev/null
++++ b/tools/grit/grit/testdata/mini.html
+@@ -0,0 +1,36 @@
++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
++<html><head>
++<meta http-equiv=content-type content="text/html; charset=windows-1252">
++<meta http-equiv="cache-control" content="no-cache">
++<meta http-equiv="pragma" content="no-cache">
++<meta http-equiv="expires" content="-1">
++<style>
++BODY { FONT-SIZE: 8pt; FONT-FAMILY: verdana,arial,san-serif }
++P { FONT-SIZE: 8pt; FONT-FAMILY: verdana,arial,san-serif }
++TD { FONT-SIZE: 8pt; FONT-FAMILY: verdana,arial,san-serif }
++A { FONT-SIZE: 8pt; FONT-FAMILY: verdana,arial,san-serif }
++DIV { FONT-SIZE: 8pt; TEXT-DECORATION: none }
++A:hover { COLOR: #ffffff }
++.border { BORDER-RIGHT: 0px; BORDER-TOP: 0px; BORDER-LEFT: 0px; BORDER-BOTTOM: 0px }
++</style>
++</head>
++
++<BODY bottomMargin=0 bgColor=#3300cc leftMargin=0 topMargin=0 rightMargin=0 marginwidth="0" marginheight="0">
++
++<TABLE cellSpacing=0 cellPadding=0 bgColor=#3300cc border=0><TBODY>
++<tr><TD vAlign=top>
++
++<TABLE cellSpacing=0 cellPadding=0 bgColor=#3300cc border=0><TBODY>
++
++<tr>
++<TD vAlign=top>&nbsp;<INPUT style="position:relative; height=17px;" class=border size=10>&nbsp;</td>
++
++<TD class=ch onmouseover="this.bgColor='6666FF'" style="BORDER-RIGHT: #000000 1px solid; BORDER-TOP: #6666cc 1px solid; BORDER-LEFT: #6666cc 1px solid; BORDER-BOTTOM: #000000 1px solid" onmouseout="this.bgColor='#000099'" vAlign=center align=middle bgColor=#000099><img height=1 width=1><IMG src="logo.gif" align=middle><img height=1 width=1></td>
++
++</TBODY></table>
++</td>
++
++<TD width=2><IMG height=1 width=1></td>
++
++<TD vAlign=top><TABLE cellSpacing=0 cellPadding=1><TBODY>
++<tr><TD class=ch onmouseover="this.bgColor='6666FF'" style="BORDER-RIGHT: #000000 1px solid; BORDER-TOP: #6666cc 1px solid; BORDER-LEFT: #6666cc 1px solid; BORDER-BOTTOM: #000000 1px solid" onmouseout="this.bgColor='#000099'" vAlign=top noWrap bgColor=#000099><IMG src="mini_close.gif"></td></tr></TBODY></table></td></tr></TBODY></table></body></html>
+diff --git a/tools/grit/grit/testdata/oem_enable.html b/tools/grit/grit/testdata/oem_enable.html
+new file mode 100644
+index 0000000000..db6b85eca6
+--- /dev/null
++++ b/tools/grit/grit/testdata/oem_enable.html
+@@ -0,0 +1,106 @@
++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
++<html><head><title>Google Desktop Search Download</title>
++<meta http-equiv=content-type content="text/html; charset=utf-8">
++<style>BODY {
++ FONT-FAMILY: arial,sans-serif
++}
++TD {
++ FONT-FAMILY: arial,sans-serif
++}
++DIV {
++ FONT-FAMILY: arial,sans-serif
++}
++.p {
++ FONT-FAMILY: arial,sans-serif
++}
++A {
++ FONT-FAMILY: arial,sans-serif
++}
++DIV {
++ COLOR: #000
++}
++TD {
++ COLOR: #000
++}
++A:link {
++ COLOR: #00c
++}
++A:visited {
++ COLOR: #551a8b
++}
++</style>
++
++<meta content="mshtml 6.00.2800.1476" name=generator></head>
++<body>
++<center>
++<TABLE cellSpacing=0 cellPadding=0 border=0>
++ <TBODY>
++ <TR vAlign=center>
++ <td>
++ <DIV align=center><IMG height=55 alt="Google Desktop Search"
++ src="/logo3.gif" width=150 border=0 search=""
++ desktop=""></DIV></td>
++ <td>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td>
++ <td><FONT size=+1><B>Search your own
++computer.</B></font></td></tr></TBODY></table><BR>
++<TABLE cellSpacing=0 cellPadding=0 width=630 border=0>
++ <TBODY>
++ <tr>
++ <TD vAlign=top width="53%"><FONT size=-1>
++ <LI>Find your email, files, web history and chats instantly <NOBR>
++ <LI>View web pages you've seen, even when you're not online</NOBR>
++ <LI>Search as easily as you do on Google
++ <p><B>Google Desktop Search finds:</B></p></font>
++ <TABLE cellSpacing=1 cellPadding=0 width=325 border=0 valign="center">
++ <TBODY>
++ <TR vAlign=center>
++ <TD colSpan=3><FONT size=-1><IMG style="VERTICAL-ALIGN: middle"
++ height=16 alt=Outlook src="/email.gif"
++ width=16>&nbsp;&nbsp;Email from Outlook, Outlook Express, &amp;
++ Thunderbird</font></td></tr>
++ <tr>
++ <TD noWrap colSpan=3><FONT size=-1><IMG
++ style="VERTICAL-ALIGN: middle" height=16 alt="Internet Explorer"
++ src="/html.gif" width=16>&nbsp;&nbsp;Web history
++ from IE/Firefox/Mozilla/Netscape</font></td></tr>
++ <tr>
++ <TD noWrap colSpan=3><FONT size=-1><IMG
++ style="VERTICAL-ALIGN: middle" height=16 alt=Text
++ src="/file.gif" width=16>&nbsp;&nbsp;Files in Word,
++ Excel, Powerpoint, PDF, &amp; media formats</font></td></tr>
++ <tr>
++ <TD vAlign=top colSpan=3><FONT size=-1><IMG
++ style="VERTICAL-ALIGN: middle" height=16 alt="AOL IM"
++ src="/aim.gif" width=16>&nbsp;&nbsp;Chats from AOL
++ Instant Messenger</font></td></tr>
++ <tr>
++ <TD noWrap><FONT size=-1>&nbsp;</font></td></tr></TBODY></table><FONT
++ size=-1>&nbsp;</font><FONT size=-1><A
++ href="http://desktop.google.com/about.html">About Desktop
++ Search</A>&nbsp;&nbsp; <A
++ href="http://desktop.google.com/screenshots.html">Screenshots</A>&nbsp;&nbsp;
++ <A href="http://desktop.google.com/support">Help</A>&nbsp;&nbsp; <A
++ href="http://desktop.google.com/feedback.html">Contact
++ Us</A><BR></font></LI></td>
++ <td>&nbsp;&nbsp;&nbsp;</td>
++ <TD vAlign=top width="53%">
++ <TABLE cellPadding=2 width="100%" align=center>
++ <TBODY>
++ <tr>
++ <TD
++ style="BORDER-RIGHT: rgb(204,204,204) 1px solid; BORDER-TOP: rgb(204,204,204) 1px solid; BORDER-LEFT: rgb(204,204,204) 1px solid; BORDER-BOTTOM: rgb(204,204,204) 1px solid"
++ width="100%" bgColor=#e7eff7 blah2="#fff8dd" blah="#e7eaf7"><BR>
++ <center><FONT size=-1>By using, you agree to our <A
++ href="http://desktop.google.com/eula.html"><BR>Terms &amp;
++ Conditions</A> and <A
++ href="http://desktop.google.com/privacypolicy.html">Privacy
++ Policy</A></font></center>
++ <p></p>
++ <FORM action='[STEP2]'>
++ <P align=center><INPUT style="PADDING-RIGHT: 3px; PADDING-LEFT: 3px; FONT-WEIGHT: bold; FONT-SIZE: 17px; PADDING-BOTTOM: 4px; PADDING-TOP: 4px" type=submit value="Agree and Start Using" name=Submit>
++ </p></FORM><FONT size=-2>* Automatically starts when you turn on
++ your computer</font> </td></tr></TBODY></table>
++ <p></p></td></tr></TBODY></table></center>
++<p></p>
++<center><FONT color=#666666 size=-2>©2005 Google</font>
++<p></p></center></body></html>
+diff --git a/tools/grit/grit/testdata/oem_non_admin.html b/tools/grit/grit/testdata/oem_non_admin.html
+new file mode 100644
+index 0000000000..8b7ca13e21
+--- /dev/null
++++ b/tools/grit/grit/testdata/oem_non_admin.html
+@@ -0,0 +1,39 @@
++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
++<html><head><title>Google Desktop Search Preferences</title>
++<meta http-equiv=cache-control content=no-cache>
++<meta http-equiv=content-type content="text/html; charset=utf-8">
++<meta http-equiv=pragma content=no-cache>
++<meta http-equiv=expires content=-1>
++<style>BODY {
++ FONT-FAMILY: arial,sans-serif
++}
++.c:active {
++ COLOR: #ff0000
++}
++.c:visited {
++ COLOR: #7777cc
++}
++.c:link {
++ COLOR: #7777cc
++}
++</style>
++
++<script>
++<!--
++override = 1;
++function ee() {if (override==1) {(new Image()).src="/doneinstallprefs&s=3286011577";}}
++// -->
++</script>
++
++<meta content="mshtml 6.00.2800.1476" name=generator></head>
++<BODY onresize=stw() leftMargin=30 rightMargin=30 onunload=ee()>
++<FORM name=f onsubmit=javascript:window.close();><IMG
++src="/logo3.gif" border=0>
++<DIV id=c1 style="WIDTH: 600px">
++<p><BR><FONT color=#00218a><B>We're sorry, but you need administrator access to
++enable Desktop Search.</B></font></p><FONT size=-1>
++<p>To install or run Google Desktop Search you need administrator access on this
++computer. Please try installing again once you have administrator
++access.</p></font></DIV>
++<p><INPUT id=s type=submit value=&nbsp;&nbsp;OK&nbsp;&nbsp;> <BR></p>
++<center></center></FORM></body></html>
+diff --git a/tools/grit/grit/testdata/onebox.html b/tools/grit/grit/testdata/onebox.html
+new file mode 100644
+index 0000000000..c24ff043a5
+--- /dev/null
++++ b/tools/grit/grit/testdata/onebox.html
+@@ -0,0 +1,21 @@
++<html><head><title>Google Desktop Search Results</title>
++<style><!--
++body,td,div,.p,a{font-family:arial,sans-serif }
++body{ background-color: transparent }
++div,td{color:#000}
++.f,.fl:link{color:#6f6f6f}
++a:link,.w,a.w:link,.w a:link{color:#00c}
++a:visited,.fl:visited{color:#551a8b}
++a:active,.fl:active{color:#f00}
++.t a:link,.t a:active,.t a:visited,.t{color:#000}
++//-->
++</style>
++</head>
++<body>
++<table cellspacing=0 cellpadding=1 border=0 ID="Google Desktop Search">
++<tr><td colspan=2><nobr><a href="http://[WEBSERVER][$~QUERY~$]" target=_parent>[NUMRESULTS] [RESULT-STRING] stored on your computer</a><font size=-1>&nbsp;-&nbsp;<a href="[HIDENOW]" style="color:#7777cc;" target=_parent>Hide</a>&nbsp;-&nbsp;<a href="http://desktop.google.com/integration.html" style="color:#7777cc;" target=_parent>About</a></font></nobr></td></tr>
++<tr><td valign=top width=40><img height=27 style="margin-top:2px;" src="http://[WEBSERVER]/onebox.gif"></td>
++<td valign=top width="99%"><font size=-1>[RESULTS]</font></td></tr>
++</table>
++</body>
++</html>
+diff --git a/tools/grit/grit/testdata/oneclick.html b/tools/grit/grit/testdata/oneclick.html
+new file mode 100644
+index 0000000000..32dc6459dd
+--- /dev/null
++++ b/tools/grit/grit/testdata/oneclick.html
+@@ -0,0 +1,34 @@
++[HEADER]
++
++
++<TABLE cellSpacing=4 cellPadding=0 width="100%" border=0>
++<tr>
++ <TD vAlign=top align=left width=50%>
++ [EMAIL_TOP_CHROME]
++
++ <p class=f>
++ <TABLE cellSpacing=6 cellPadding=0 width="100%" border=0>
++ [EMAIL]
++ </table>
++ </td>
++
++
++ <TD width=1 align=middle bgColor=#cfcfcf><IMG height=1 width=1></td>
++ <TD width=50% vAlign=top align=left>
++ [FREQ_TOP_CHROME]
++ <p class=f>
++ <TABLE cellSpacing=6 cellPadding=0 width="100%" border=0 ID="Table1">
++ [$~FREQ~$]
++ </table>
++ <p class=g>
++ [RECENT_TOP_CHROME]
++ <TABLE cellSpacing=6 cellPadding=0 width="100%" border=0 ID="Table2">
++ [$~RECENT~$]
++ </table>
++ </td>
++ </tr>
++</table>
++<center><BR>
++
++
++[FOOTER]
+diff --git a/tools/grit/grit/testdata/password.html b/tools/grit/grit/testdata/password.html
+new file mode 100644
+index 0000000000..16007a1ac0
+--- /dev/null
++++ b/tools/grit/grit/testdata/password.html
+@@ -0,0 +1,37 @@
++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
++<html><head><title>Password Required</title>
++<meta http-equiv="content-type" content="text/html; charset=utf-8">
++<meta http-equiv="cache-control" content="no-cache">
++<meta http-equiv="pragma" content="no-cache">
++<meta http-equiv="expires" content="-1">
++<style>
++BODY,TD,A,P {FONT-FAMILY: arial,sans-serif}
++.q {COLOR: #0000cc}
++</style>
++<script>
++<!--
++function sf(){document.f.q.focus();}
++// -->
++</script>
++</head>
++<body text=#000000 vLink=#551a8b aLink=#ff0000 link=#0000cc bgColor=#ffffff onload=sf()>
++<center>
++<table cellSpacing=0 cellPadding=0 border=0>
++<tr><td><a href="[$~HOMEPAGE~$]"><IMG border=0 height=110 alt="Google Desktop Search" src="hp_logo.gif" width=276></a></td></tr></table><BR>
++<form name=f method=GET action='/password'>
++<table cellSpacing=0 cellPadding=4 border=0>
++<tr><td class=q noWrap><font size=-1>
++ <table cellSpacing=0 cellPadding=0>
++ <tr vAlign=top>
++ <td align=middle>Password required:&nbsp;&nbsp;<input maxLength=80 size=30 type=password name=pw value="">
++ <script>
++ document.f.q.focus();
++ </script>
++ &nbsp;<input type=submit value="Submit" name=submit>
++ </td></tr>
++ </table>
++ </form>
++</td></tr>
++</table>
++<br><font size=-1>[$~BOTTOMLINE~$]</font></p>
++<p><font size=-2>&copy;2005 Google</font></p></center></body></html>
+\ No newline at end of file
+diff --git a/tools/grit/grit/testdata/preferences.html b/tools/grit/grit/testdata/preferences.html
+new file mode 100644
+index 0000000000..b37412436b
+--- /dev/null
++++ b/tools/grit/grit/testdata/preferences.html
+@@ -0,0 +1,234 @@
++<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
++"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
++<html><head><title>Google Desktop Search Preferences</title>
++<meta http-equiv=content-type content="text/html; charset=utf-8">
++<style>
++body {
++ margin-left: 2em; margin-right: 2em;
++ font-family: arial,sans-serif;
++ color:#000; background-color:#fff;
++}
++a:active { color:#f00 }
++a:visited { color:#551a8b }
++a:link { color:#00c }
++a.c:active { color: #ff0000 }
++a.c:visited { color: #7777cc }
++a.c:link { color: #7777cc }
++.b { font-weight: bold }
++.shaded-header { background-color: #e8f4f7; border-top: 1px solid #39c;
++margin: 0px; padding: 0px }
++.shaded-subheader { background-color: #e8f4f7; margin: 12px 0px 0px 0px;
++ padding: 0px }
++.plain-subheader { background-color: #fff; margin: 12px 0px 0px 0px;
++ padding: 0px }
++.header-element { margin: 0px; padding: 2px}
++.expand { width: 98% }
++.s { font-size: smaller }
++.prefgroup { border: 2px solid #e8f4f7; width: 100% }
++.phead { font-weight: bold; font-size: smaller; vertical-align: top;
++text-transform: capitalize; border-bottom: 2px solid #e8f4f7; margin: 0px;
++padding: 8px}
++.pbody { border-bottom: 2px solid #e8f4f7; margin: 0px;
++padding: 8px}
++.pref-last { border-bottom: 0px }
++.example { color: gray; font-family: monospace; }
++</style>
++<script>
++<!--
++function validate() {}
++function fnOnClickAll() {for (var i = 0; i < document.langform.lr.length; i++) {
++document.langform.lr[i].checked = false;}}
++function fnOnClickSome() {
++var count = 0;for (var i = 0; i < document.langform.lr.length; i++) {
++if (document.langform.lr[i].checked) {count++;}}
++document.langform.lang[0].checked = (count <= 0);
++document.langform.lang[1].checked = (count > 0);}
++// -->
++</script>
++</head>
++<body onload="checkOffice()">
++<form name=prefs action="[$~SETPREFS~$]" method=post><input name=url
++value="[PREVPAGE]" type=hidden>
++<table cellspacing=2 cellpadding=0 width="100%" border=0>
++<tr>
++<td valign=top width="1%"><a href="[$~HOMEPAGE~$]">
++<img alt="Go to Google Desktop Search" src="logo3.gif" border=0></a></td>
++<td>&nbsp;</td>
++<td nowrap>
++
++<table class="shaded-header"><tr>
++<td class="header-element b expand">Preferences</td>
++<td class="header-element s">
++<a href="http://desktop.google.com/preferences.html">Preferences&nbsp;Help</a>
++</td>
++</tr></table>
++
++</tr></table>
++
++<table class="shaded-subheader"><tr>
++<td class="header-element expand s">
++<span class="b">Save</span> your preferences when finished.</td>
++<td class="header-element"><input type=submit value="Save Preferences"
++name=submit2></td>
++</tr></table>
++
++[STATUS-MESSAGE]
++<table class="plain-subheader"><tr>
++<td class="header-element expand"><span class="b">Preferences</span><span
++class="s"> (changes apply to Google Desktop Search application)</span></td>
++</tr></table>
++
++<table class="prefgroup" cellpadding=0 cellspacing=0>
++
++<!-- -->
++<tr>
++<td class="phead">Search types</td>
++<td class="pbody"><div class="s">Index the following items so that you can
++search for them:<br />&nbsp;</div>
++<div>
++ <table border=0>
++ <tr>
++ <td width=150 nowrap valign=top><span class="s">
++ <input type=checkbox [CHECK-EMAIL] name=EMAIL id=h3><label for=h3>
++ Email</label><br>
++ <input type=checkbox [CHECK-AIM] name=AIM id=h5><label for=h5> Chats
++ (AOL/MSN IM)</label><br>
++ <input type=checkbox onclick='if(!this.checked){h12.checked=0;h12.disabled=1;}
++ else {h12.disabled=0;}' [CHECK-WEB] name=WEB id=h11><label for=h11> Web
++ history</label>
++
++ </span></td>
++ <td width=120 nowrap valign=top><span class="s">
++ <script>
++<!--
++function checkOffice() { var w = document.getElementById("h7");
++var e = document.getElementById("h8"); var o = document.getElementById("h10");
++if (!(w.checked || e.checked)) { o.checked=0;o.disabled=1;} else {o.disabled=0;} }
++// -->
++ </script>
++ <input type=checkbox [CHECK-DOC] name=DOC id=h7 onclick='checkOffice()'>
++ <label for=h7> Word</label><br>
++ <input type=checkbox [CHECK-XLS] name=XLS id=h8 onclick='checkOffice()'>
++ <label for=h8> Excel</label><br>
++ <input type=checkbox [CHECK-PPT] name=PPT id=h9>
++ <label for=h9> PowerPoint</label><br>
++ </span></td><td nowrap valign=top><span class="s">
++ <input type=checkbox [CHECK-PDF] name=PDF id=hpdf>
++ <label for=hpdf> PDF</label><br>
++ <input type=checkbox [CHECK-TXT] name=TXT id=h6>
++ <label for=h6> Text, media, and other files</label><br>
++ </tr>
++ <tr><td nowrap valign=top colspan=3><span class="s"><br />
++ <input type=checkbox [CHECK-SECUREOFFICE] name=SECUREOFFICE id=h10>
++ <label for=h10> Password-protected Office documents (Word, Excel)</label><br />
++ <input type=checkbox [DISABLED-HTTPS] [CHECK-HTTPS] name=HTTPS id=h12><label
++ for=h12> Secure pages (HTTPS) in web history</label></span></td></tr>
++</table>
++</div></td></tr>
++</div>
++</td>
++</tr>
++
++<!-- -->
++<tr>
++<td class="phead">Plug-ins</td>
++<td class="pbody"><div class="s"
++style="display:[ADDIN-DISPLAYSTYLE]">Index these additional items:<p>
++[ADDIN-DO]
++[ADDIN-OPTIONS]</div><div class="s">
++To install plug-ins to index other items, visit the
++<a href="http://desktop.google.com/plugins.html">Plug-ins Download page</a>.</div>
++</tr>
++
++<!-- -->
++<tr>
++<td class="phead">Don't search these items</td>
++<td class="pbody"><div class="s">
++<label for=FORBIDDEN>Do not search web sites with the following URLs or files
++with the following paths. Put each entry on a separate line. Examples:</label><br>
++<span class="example">c:\Documents and Settings\username\Private Stuff</span><br>
++<span class="example">http://www.domain.com/</span><br>
++<div>&nbsp;</div>
++<div><TEXTAREA rows=3 cols=65 name=FORBIDDEN id=FORBIDDEN>[FORBIDDEN]
++</TEXTAREA></div>
++</tr>
++
++<!-- -->
++<tr>
++<td class="phead pref">Search Box Display</td>
++<td class="pbody pref" valign=top>
++
++<table border=0 cellpadding=0><tr><td valign=top>
++
++<input type="radio" name="SBDISPLAY" id="DISPLAYDB" [CHECK-DISPLAYDB] value="DISPLAYDB"></td><td>
++<label for=DISPLAYDB><font size=-1>Deskbar - A search box in your taskbar</font></label></td></tr>
++<tr><td></td></tr>
++<tr><td></td><td><img src="deskbar.gif" alt="Deskbar" width="268" height="34"></td></tr>
++<tr><td height=2></td></tr>
++<tr><td valign=top>
++
++<input type="radio" name="SBDISPLAY" id="DISPLAYMB" [CHECK-DISPLAYMB] VALUE="DISPLAYMB"></td><td>
++<label for=DISPLAYMB><font size=-1>Floating Deskbar - A search box you can put anywhere on your desktop</font></label></td></tr>
++<tr><td></td></tr>
++<tr><td></td><td><img src="minibar.gif" width="137" height="27"></td></tr>
++<tr><td height=2></td></tr>
++<tr><td valign=top>
++
++<input type=radio name="SBDISPLAY" id="DISPLAYNONE" [CHECK-DISPLAYNONE] VALUE="DISPLAYNONE"></td><td valign=top>
++<label for=DISPLAYNONE><font size=-1> None</font></label>
++</td></tr>
++</table>
++
++</td></tr>
++
++<!-- -->
++<tr>
++<td class="phead pref">Number of Results</td>
++<td class="pbody pref"><label for=num><span class="s">
++Display <select name=num id="num">
++<option [CHECK-NUM-10]>10
++<option [CHECK-NUM-20]>20
++<option [CHECK-NUM-30]>30
++<option [CHECK-NUM-50]>50
++<option [CHECK-NUM-100]>100</select>
++ results per page</span></label>
++</td>
++</tr>
++
++<!-- -->
++<tr>
++<td class="phead">Google integration</td>
++<td class="pbody">
++<table border=0 cellpadding=0>
++<tr><td><input type=CHECKBOX name=ONEBOX [CHECK-ONEBOX] id=onebox></td>
++<td><label for=onebox>
++ <span class="s">Show Desktop Search results on Google Web Search result pages.
++ </span></label></td></tr>
++ <tr><td></td><td>
++ <span class="s">Your personal results are private from Google.</span>
++ </td></tr></table>
++</td>
++</tr>
++
++<!-- -->
++<tr>
++<td class="phead pref-last">Help us improve</td>
++<td class="pbody pref-last">
++<input type=CHECKBOX name=SENDDATA id="SENDDATA" [CHECK-SENDDATA]><label for=
++SENDDATA> <span class="s">Send non-personal usage data and crash reports to
++Google to help improve Desktop Search.</span></label>
++</td>
++</tr>
++
++</table>
++
++<table class="shaded-subheader"><tr>
++<td class="header-element expand s"><span class="b">Save</span> your preferences
++when finished.</td>
++<td class="header-element"><input type=submit value="Save Preferences"
++name=submit2></td>
++</tr></table>
++
++<p><div align=center>[$~BOTTOMLINE~$]</div>
++<br><center><span class="s">&copy;2005 Google</span></center>
++</form></body></html>
+\ No newline at end of file
+diff --git a/tools/grit/grit/testdata/preprocess_test.html b/tools/grit/grit/testdata/preprocess_test.html
+new file mode 100644
+index 0000000000..13ece9a9f6
+--- /dev/null
++++ b/tools/grit/grit/testdata/preprocess_test.html
+@@ -0,0 +1,7 @@
++<if expr="True">
++should be kept
++</if>
++in the middle...
++<if expr="False">
++should be removed
++</if>
+diff --git a/tools/grit/grit/testdata/privacy.html b/tools/grit/grit/testdata/privacy.html
+new file mode 100644
+index 0000000000..1d45f4a539
+--- /dev/null
++++ b/tools/grit/grit/testdata/privacy.html
+@@ -0,0 +1,35 @@
++[!]
++title Privacy and Google Desktop Search
++template
++privacy_bottomline
++hp_image
++
++<TABLE CELLSPACING=0 CELLPADDING=5 WIDTH="98%" BORDER=0>
++<TR VALIGN=TOP>
++<td>
++<h4>Privacy and Google Desktop Search</h4>
++
++<p><FONT SIZE=-1>Google is committed to making search on your desktop as easy
++as searching the web. We recognize that privacy is an important issue,
++so we designed and built Google Desktop Search with respect for your privacy.
++<p>
++So that you can easily search your computer, the Google Desktop Search application indexes
++and stores versions of your files and other computer activity,
++such as email, chats, and web history. These versions may also be mixed
++with your Web search results to produce
++results pages for you that integrate relevant content from your computer and
++information from the Web.
++<p>
++Your computer's content is not made accessible to Google or anyone else without your explicit permission.
++
++<p>You can read the
++<A HREF='http://desktop.google.com/privacypolicy.html?hl=[LANG_CODE]'>Privacy Policy</A>
++and <A HREF='http://desktop.google.com/privacyfaq.html?hl=[LANG_CODE]'>Privacy FAQ</A> online.
++</font>
++</td></tr></table>
++
++<center><br>
++<TABLE CELLSPACING=0 CELLPADDING=0 WIDTH="100%" BORDER=0>
++<TR BGCOLOR=#3399CC><TD ALIGN=MIDDLE HEIGHT=1><IMG HEIGHT=1 ALT="" WIDTH=1></td></tr></table>
++<FONT SIZE=-1>[$~PRIVACY_BOTTOMLINE~$] - &copy;2005 Google </font>
++</center>
+\ No newline at end of file
+diff --git a/tools/grit/grit/testdata/quit_apps.html b/tools/grit/grit/testdata/quit_apps.html
+new file mode 100644
+index 0000000000..a501b0e2bf
+--- /dev/null
++++ b/tools/grit/grit/testdata/quit_apps.html
+@@ -0,0 +1,49 @@
++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
++<html><head><title>Google Desktop Search Preferences</title>
++<meta http-equiv=cache-control content=no-cache>
++<meta http-equiv=content-type content="text/html; charset=utf-8">
++<meta http-equiv=pragma content=no-cache>
++<meta http-equiv=expires content=-1>
++<style>BODY {
++ FONT-FAMILY: arial,sans-serif
++}
++.c:active {
++ COLOR: #ff0000
++}
++.c:visited {
++ COLOR: #7777cc
++}
++.c:link {
++ COLOR: #7777cc
++}
++</style>
++
++<script>
++<!--
++// -->
++</script>
++
++<meta content="mshtml 6.00.2800.1476" name=generator></head>
++<BODY onresize=stw() leftMargin=30 rightMargin=30>
++<FORM name=f action='[NEXTSTEP]' method=post><IMG src="/logo3.gif"
++border=0>
++<DIV id=c1 style="WIDTH: 600px">
++<p><BR><FONT color=#00218a><B>To start using Google Desktop Search, we may need to close the following programs if they are running:</B></font></p>
++<FONT size=-1><p>You can start these programs once Google Desktop Search is running.</p></font>
++
++<LI><FONT size=-1>AOL Instant Messenger</font>
++<LI><FONT size=-1>Firefox</font>
++<LI><FONT size=-1>Internet Explorer</font>
++<LI><FONT size=-1>Microsoft Excel</font>
++<LI><FONT size=-1>Microsoft Outlook </font>
++<LI><FONT size=-1>Microsoft Word </font>
++<LI><FONT size=-1>Mozilla</font>
++<LI><FONT size=-1>Mozilla Thunderbird</font>
++<LI><FONT size=-1>Netscape</font>
++<LI><FONT size=-1>Opera</font>
++<LI><FONT size=-1>Other web browsers</font>
++<FONT size=-1>
++<p>This will take only a few seconds to complete. </p></font></LI></DIV>
++<p><INPUT id=s type=submit name="quit" value="&nbsp;&nbsp;OK.&nbsp;&nbsp;Close&nbsp;these&nbsp;applications&nbsp;&nbsp;">
++ <INPUT id=s type=submit name="redir" value="&nbsp;&nbsp;Cancel.&nbsp;I'll&nbsp;run&nbsp;this&nbsp;later&nbsp;&nbsp;"><BR></p>
++<center></center></FORM></body></html>
+diff --git a/tools/grit/grit/testdata/recrawl.html b/tools/grit/grit/testdata/recrawl.html
+new file mode 100644
+index 0000000000..0401e7c2b0
+--- /dev/null
++++ b/tools/grit/grit/testdata/recrawl.html
+@@ -0,0 +1,30 @@
++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
++<html><head><title>Refresh index</title>
++<meta http-equiv="content-type" content="text/html; charset=utf-8">
++<meta http-equiv="cache-control" content="no-cache">
++<meta http-equiv="pragma" content="no-cache">
++<meta http-equiv="expires" content="-1">
++<style>
++BODY,TD,A,P {FONT-FAMILY: arial,sans-serif}
++.q {COLOR: #0000cc}
++</style>
++</head>
++<body text="#000000" vLink="#551a8b" aLink="#ff0000" link="#0000cc" bgColor="#ffffff">
++<center>
++<table cellSpacing="0" cellPadding="0" border="0">
++<tr>
++<td><a href="[$~HOMEPAGE~$]"><img border="0" height="110" alt="Google Desktop Search" src="hp_logo.gif" width="276"></a>
++</td>
++</tr>
++</table>
++<br>
++<center>Google Desktop Search is now recrawling your drive to index new files.</center>
++<center>Note that new files are indexed automatically, and this step is generally not needed.</center>
++<center>Click <a href="[$~HOMEPAGE~$]">here</a> to continue.</center>
++</td></tr></table>
++<br>
++<font size="-1">[$~BOTTOMLINE~$]</font>
++<p><font size="-2">&copy;2005 Google</font></p>
++</center>
++</body>
++</html>
+\ No newline at end of file
+diff --git a/tools/grit/grit/testdata/resource_ids b/tools/grit/grit/testdata/resource_ids
+new file mode 100644
+index 0000000000..d5d440d57f
+--- /dev/null
++++ b/tools/grit/grit/testdata/resource_ids
+@@ -0,0 +1,13 @@
++{
++ "SRCDIR": ".",
++ "test.grd": {
++ "messages": [100, 10000],
++ },
++ "substitute_no_ids.grd": {
++ "messages": [10000, 20000],
++ },
++ "<(FOO)/file.grd": {
++ },
++ "<(SHARED_INTERMEDIATE_DIR)/devtools/devtools.grd": {
++ },
++}
+diff --git a/tools/grit/grit/testdata/script.html b/tools/grit/grit/testdata/script.html
+new file mode 100644
+index 0000000000..f177d9c30e
+--- /dev/null
++++ b/tools/grit/grit/testdata/script.html
+@@ -0,0 +1,38 @@
++<script>
++function run(n,cut){
++ var out = "", str = "abcdefghijklmnopqrstuvwxyz 1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ,./:;'\"()*!?-_@[]{}#%`+=|\\>";
++ n.innerHTML = 'aa';
++
++ var base = n.scrollWidth;
++ for(var i=0;i<str.length;i++) {
++ n.innerHTML = 'a'+str.charAt(i)+'a';
++ out += str.charAt(i) + (n.scrollWidth-base) +";";
++
++ if(cut && !i && (n.scrollWidth-base == cut)) {
++ return '\x02'+"0;";
++ }
++ }
++ // extra cases for literals
++ n.innerHTML = 'a&lt;a';
++ out += '<' + (n.scrollWidth-base) +";";
++ n.innerHTML = 'a&amp;a';
++ out += '&' + (n.scrollWidth-base) +";";
++
++ var base_height = n.scrollHeight;
++ n.innerHTML += '<br>a';
++ out += '\x01' + (n.scrollHeight-base_height) +";";
++
++ return out;
++}
+
-+#include "nested_class_inline_ctor.h"
-diff --git a/tools/clang/plugins/tests/nested_class_inline_ctor.h b/tools/clang/plugins/tests/nested_class_inline_ctor.h
++function TEST_WIDTH() {
++ var n = document.getElementById('test');
++ var out = run(n[$~CUT~$]);
++ if (out.length>4){
++ n.style.fontWeight='bold';
++ out += run(n);
++ }
++ n.outerHTML = "";
++ (new Image()).src="[$~SETWIDTH~$]?src=[COMPONENT]&data="+escape(out).replace(/\+/g,"%2B");
++}
++</script>
+diff --git a/tools/grit/grit/testdata/searchbox.html b/tools/grit/grit/testdata/searchbox.html
new file mode 100644
-index 0000000000..01cfea9232
+index 0000000000..9eccba99a5
--- /dev/null
-+++ b/tools/clang/plugins/tests/nested_class_inline_ctor.h
++++ b/tools/grit/grit/testdata/searchbox.html
@@ -0,0 +1,22 @@
-+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+// Use of this source code is governed by a BSD-style license that can be
-+// found in the LICENSE file.
++<body bgcolor=#ffffff topmargin=2 marginheight=2>
++<table border=0 cellpadding=0 cellspacing=0 width=1%>
++<tr>
++<td valign=top><a href='[$~HOMEPAGE~$]'><img width=150 height=55 src="/logo3.gif" alt="Go to Google Desktop Search" border=0 vspace=12></a></td>
++<td>&nbsp;&nbsp;</td>
++<td valign=top>
++<table cellpadding=0 cellspacing=0 border=0><tr><td colspan=2 height=14 valign=bottom>
++<table border=0 cellpadding=4 cellspacing=0>
++<tr><td class=q><font size=-1>
++[$~LINKS~$]
++</tr>
++</table>
++</td>
++</tr>
++<tr><td nowrap><form name=gs method=GET action='[$~SEARCHURL~$]'><input type=text name=q size=41 maxlength=2048 value="[DISP_QUERY]"><input type=hidden name=ie value="UTF-8">
++<font size=-1>[$~FLAGS~$]<input type=submit name="btnG" value="Search Desktop"><span id=hf></span></font></td>
++<td><font size=-2>&nbsp;&nbsp;<a href='[$~PREFERENCES~$]'>Desktop&nbsp;Preferences</a><br>&nbsp;&nbsp;<a [DELETE_EXTRA] href=[DELETE_PAGE]><nobr>[DELETE_NAME]</nobr></a></font></td>
++</tr></table>
++<table cellpadding=0 cellspacing=0 border=0>
++<tr><td><font size=-1>&nbsp;</font></td></tr>
++</table>
++</td></tr></form></table>
+\ No newline at end of file
+diff --git a/tools/grit/grit/testdata/sidebar_h.html b/tools/grit/grit/testdata/sidebar_h.html
+new file mode 100644
+index 0000000000..e103e8f8db
+--- /dev/null
++++ b/tools/grit/grit/testdata/sidebar_h.html
+@@ -0,0 +1,82 @@
++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
++<html><head>
++<meta http-equiv="content-type" content="text/html; charset=utf-8">
++<meta http-equiv="cache-control" content="no-cache">
++<meta http-equiv="pragma" content="no-cache">
++<meta http-equiv="expires" content="-1">
++<style>
++BODY,TD,DIV,A,.p { FONT-FAMILY: arial,sans-serif; SCROLL: no}
++DIV,TD {COLOR: #000}
++.f, .fl:link {COLOR: #6f6f6f}
++A:link { COLOR: #00c}
++A:visited { COLOR: #551a8b}
++A:active { COLOR: #f00}
++.fl:active { COLOR: #f00}
++.h { COLOR: #3399CC}
++.a, .a:link {COLOR: #008000}
++.b { FONT-WEIGHT: bold; FONT-SIZE: 12pt; COLOR: #00c}
++.g { MARGIN-TOP: .5em; MARGIN-BOTTOM: .5em}
++.f { MARGIN-TOP: 0.5em; MARGIN-BOTTOM: 0.25em}
++.c:active, .c:visited, .c:link { COLOR: #6f6f6f}
++.ch {CURSOR: hand}
++</style>
++</head>
++<BODY onload="TEST_WIDTH();" bottomMargin=0 leftMargin=2 topMargin=0 rightMargin=2 marginwidth=0 marginheight=0 SCROLL=NO bgcolor=#E0E0E0 style="border-style:solid; border-width:0;" oncontextmenu="return false;">
++
++<script>
++function hide() {
++ return 1;
++ // return confirm("Are you sure you want to hide the sidebar?\nYou can show it again in Google Desktop Search Preferences. ");
++}
++</script>
+
-+#ifndef NESTED_CLASS_INLINE_CTOR_H_
-+#define NESTED_CLASS_INLINE_CTOR_H_
+
-+#include <string>
-+#include <vector>
++<TABLE border=0 cellPadding=0 cellSpacing=0 width="100%"><tr>
++<TD WIDTH="19%" VALIGN=TOP>
+
-+// See crbug.com/136863.
++<form method=get action="[$~SEARCHURL~$]">
++<input type=hidden name=src value=4>
+
-+class Foo {
-+ class Bar {
-+ Bar() {}
-+ ~Bar() {}
++ <table cellspacing=0 cellpadding=0 width='1%'>
++ <tr><td nowrap align=center valign=middle><nobr><img width=16 height=16 src=logo.gif>&nbsp;<b><i><font color=#6F6F6F>Google Desktop Search</font></i></b><IMG id=ctl src="[CONTROL_IMAGE]" border=0 usemap="#control"></nobr></td></tr>
++ <tr><td nowrap align=center valign=middle><nobr><input TABINDEX="1" style="font-size:10px; width:'100%';" name="q" id="q"></nobr></td></tr>
++ <tr><td nowrap align=center valign=middle><nobr><input TABINDEX="2" style="font-size:10px" type=submit value="Local search" name=btnG> <input TABINDEX="3" style="font-size:9px" type=submit value="Web search" name=redir></nobr></td></tr>
++<MAP name="control">
++<area TABINDEX="4" onkeydown='if(event.keyCode==13)location.href="movesidebar?side=1"' title="Move sidebar to Top" shape="rect" coords="9,0,22,8" href="/movesidebar?side=1" onmouseover="ctl.src='control1.gif'" onmouseout="ctl.src='[CONTROL_IMAGE]'">
++<area TABINDEX="5" onkeydown='if(event.keyCode==13)location.href="movesidebar?side=3"' title="Move sidebar to Bottom" shape="rect" coords="9,9,22,17" href="/movesidebar?side=3" onmouseover="ctl.src='control3.gif'" onmouseout="ctl.src='[CONTROL_IMAGE]'">
++<area TABINDEX="6" onkeydown='if(event.keyCode==13)location.href="movesidebar?side=0"' title="Move sidebar to Left" shape="rect" coords="0,2,8,15" href="/movesidebar?side=0" onmouseover="ctl.src='control0.gif'" onmouseout="ctl.src='[CONTROL_IMAGE]'">
++<area TABINDEX="7" onkeydown='if(event.keyCode==13)location.href="movesidebar?side=2"' title="Move sidebar to Right" shape="rect" coords="23,2,31,15" href="/movesidebar?side=2" onmouseover="ctl.src='control2.gif'" onmouseout="ctl.src='[CONTROL_IMAGE]'">
++</MAP>
++ </table>
++</form>
+
-+ std::vector<std::string> a;
-+ };
-+};
++</td>
++<TD WIDTH="27%" VALIGN=TOP>
++
++[HEADER_SECTION1]
++[CONTENT_INBOX]
++
++</td>
++<TD WIDTH="27%" VALIGN=top>
+
-+#endif // NESTED_CLASS_INLINE_CTOR_H_
-diff --git a/tools/clang/plugins/tests/nested_class_inline_ctor.txt b/tools/clang/plugins/tests/nested_class_inline_ctor.txt
++[HEADER_SECTION2]
++[CONTENT_HIST]
++
++</td>
++<TD WIDTH="27%" VALIGN=top>
++
++[CONTENT_NEWS]
++[CONTENT_OTHER]
++
++</td>
++<TD WIDTH="1%" VALIGN=top>
++
++<a TABINDEX="8" onkeydown='if(event.keyCode!=16&&event.keyCode!=9)onclick()' onclick='return hide()' href='[$~HIDE1~$]' class=ch><img width=12 height=11 style='vertical-align:top;' src=/hide.gif valign=top border=0></a>
++
++</td>
++</tr></table>
++
++<font size=-1><span style="visibility:hidden" id='test'>t</span></font>
++
++[SCRIPT]
++</body></html>
+diff --git a/tools/grit/grit/testdata/sidebar_v.html b/tools/grit/grit/testdata/sidebar_v.html
new file mode 100644
-index 0000000000..39bd6e1dce
+index 0000000000..e040d8ec59
--- /dev/null
-+++ b/tools/clang/plugins/tests/nested_class_inline_ctor.txt
-@@ -0,0 +1,8 @@
-+In file included from nested_class_inline_ctor.cpp:5:
-+./nested_class_inline_ctor.h:15:5: warning: [chromium-style] Complex constructor has an inlined body.
-+ Bar() {}
-+ ^
-+./nested_class_inline_ctor.h:16:5: warning: [chromium-style] Complex destructor has an inline body.
-+ ~Bar() {}
-+ ^
-+2 warnings generated.
-diff --git a/tools/clang/plugins/tests/overridden_methods.cpp b/tools/clang/plugins/tests/overridden_methods.cpp
-new file mode 100644
-index 0000000000..f572a41733
---- /dev/null
-+++ b/tools/clang/plugins/tests/overridden_methods.cpp
-@@ -0,0 +1,38 @@
-+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+// Use of this source code is governed by a BSD-style license that can be
-+// found in the LICENSE file.
++++ b/tools/grit/grit/testdata/sidebar_v.html
+@@ -0,0 +1,267 @@
++<html><head>
++<title>Google Desktop Search Sidebar</title>
++<meta http-equiv="content-type" content="text/html; charset=utf-8">
++<meta http-equiv="cache-control" content="no-cache">
++<meta http-equiv="pragma" content="no-cache">
++<meta http-equiv="expires" content="-1">
++<style>
++BODY,TD,P,A {FONT-FAMILY: verdana,arial,sans-serif;font-size:8pt; color:#fff}
++a:link, a:visited, a:hover, b, q b { color: #ffffff}
++a:active { color: #ff0000}
++a:link,a:active,a:visited,div { text-decoration: none;font-size:8pt }
++.g{margin-top: 1em;}
++.gg{margin-top: .4em;}
++.norepeat { background-repeat: no-repeat }
++.indent{margin-top: 0px; margin-bottom: 0px;margin-left:3px;}
++.c:link, .c:visited, .c:active { color: #959595;font-size:8pt }
++.ch{cursor:pointer;cursor:hand}
++.gap{margin-top: 10px; margin-bottom: 10px;}
++.off {display:none}
++.on {display:on}
++.but { border-top: 1px solid #73787E;border-bottom: 1px solid #000000;border-right: 1px solid #000000;border-left: 1px solid #73787E;margin-top: 0px; margin-bottom: 0px; cursor:pointer;cursor:hand}
++</style>
++<script>
++
++function toggle(i) {
++ var v = document.getElementById(i);
++ var vi = document.getElementById(i+'icon');
++ var c = (v['className'] == 'on');
++ if (c) {
++ v['className'] = 'off';
++ vi.src='up.gif';
++ }
++ else {
++ v['className'] = 'on';
++ vi.src='down.gif';
++ }
++ (new Image()).src="[$~TOGGLE~$]?setting="+i+"&mode="+v['className']+"&rnd="+Math.random();
++
++ if (!c && (v['oclass'] == 'off')) {
++ location.href = location.href;
++ }
++
++ return true;
++}
++function hide() {
++ // return confirm("Are you sure you want to hide the sidebar?\nYou can show it again in Google Desktop Search Preferences.");
++ return 1;
++}
++</script>
++
++<!-- menu experiment start -->
++
++<style>
++<!--
++.menu1 {
++cursor:default;
++position:absolute;
++text-align: left;
++font-family: Arial, Helvetica, sans-serif;
++font-size: 8pt;
++font-color: #000000;
++color: #000000;
++background-color: menu;
++visibility: hidden;
++padding-top: 2px;
++padding-bottom: 2px;
++border: 1 solid;
++border-color: #888888;
++z-index: 100;
++}
++.menuitems {
++padding-left: 5px;
++padding-right: 5px;
++}
++-->
++</style>
++<SCRIPT LANGUAGE="JavaScript1.2">
++<!--
++var menustyle = "menu1";
++
++function showmenu() {
++ var rightedge = document.body.clientWidth-event.clientX;
++ var bottomedge = document.body.clientHeight-event.clientY;
++ // if (rightedge < rcmenu.offsetWidth)
++ // rcmenu.style.left = document.body.scrollLeft + event.clientX - rcmenu.offsetWidth;
++ // else
++ // rcmenu.style.left = document.body.scrollLeft + event.clientX;
++
++ // if (rcmenu.style.left < 0) rcmenu.style.left = 0;
++ rcmenu.style.left = 0;
++
++ if (bottomedge < rcmenu.offsetHeight)
++ rcmenu.style.top = document.body.scrollTop + event.clientY - rcmenu.offsetHeight;
++ else
++ rcmenu.style.top = document.body.scrollTop + event.clientY;
+
-+#include "overridden_methods.h"
++ if (rcmenu.style.top < 0) rcmenu.style.top = 0;
+
-+// Fill in the implementations
-+void DerivedClass::SomeMethod() {}
-+void DerivedClass::SomeOtherMethod() {}
-+void DerivedClass::WebKitModifiedSomething() {}
++ rcmenu.style.visibility = "visible";
++ // rcmenu.style.zindex = 0;
++ // document.all('rcmenu').style.zindex = 20;
++ document.onkeydown=ck;
++ return false;
++}
+
-+class ImplementationInterimClass : public BaseClass {
-+ public:
-+ // Should not warn about pure virtual methods.
-+ virtual void SomeMethod() = 0;
-+};
++function hidemenu() {
++ rcmenu.style.visibility = "hidden";
++}
+
-+class ImplementationDerivedClass : public ImplementationInterimClass,
-+ public webkit_glue::WebKitObserverImpl {
-+ public:
-+ // Should not warn about destructors.
-+ virtual ~ImplementationDerivedClass() {}
-+ // Should warn.
-+ virtual void SomeMethod();
-+ // Should not warn if marked as override.
-+ virtual void SomeOtherMethod() override;
-+ // Should not warn for inline implementations in implementation files.
-+ virtual void SomeInlineMethod() {}
-+ // Should not warn if overriding a method whose origin is WebKit.
-+ virtual void WebKitModifiedSomething();
-+ // Should warn if overridden method isn't pure.
-+ virtual void SomeNonPureBaseMethod() {}
-+};
++function ck(e){
++ evt=document.all?window.event:e;
++ k=document.all?window.event.keyCode:e.keyCode;
++
++ if(k==27 /*<Esc>*/) {
++ hidemenu();
++ }
++}
+
-+int main() {
-+ DerivedClass something;
-+ ImplementationDerivedClass something_else;
++function menumouseover() {
++ if (event.srcElement.className == "menuitems") {
++ event.srcElement.style.backgroundColor = "highlight";
++ event.srcElement.style.color = "white";
++ }
++}
++
++function menumouseout() {
++ if (event.srcElement.className == "menuitems") {
++ event.srcElement.style.backgroundColor = "";
++ event.srcElement.style.color = "black";
++ window.status = "";
++ }
+}
-diff --git a/tools/clang/plugins/tests/overridden_methods.h b/tools/clang/plugins/tests/overridden_methods.h
++
++function menuselect() {
++ if (event.srcElement.className == "menuitems") {
++ if (event.srcElement.getAttribute("target") != null)
++ window.open(event.srcElement.url, event.srcElement.getAttribute("target"));
++ else if (event.srcElement.url.length)
++ window.location = event.srcElement.url;
++ }
++}
++// -->
++</script>
++
++<!-- menu experiment end -->
++
++</head>
++
++<body onload="TEST_WIDTH();" bottommargin=0 leftmargin=0 marginheight=0 marginwidth=0 rightmargin=0 topmargin=0 style="background-color:'#384146'; background-repeat: repeat-y; border-style:solid; border-width:0;" background="greyback.jpg" scroll=NO oncontextmenu="return false;">
++
++<!-- menu experiment start -->
++
++<div id="rcmenu" class="skin0" onMouseover="menumouseover()" onMouseout="menumouseout()" onClick="menuselect();">
++<div class="menuitems" url="[$~SETDISP4~$]">Switch to minibar</div>
++<div class="menuitems" url="[$~SETDISP2~$]">Switch to hoverbar</div>
++<div class="menuitems" url="[$~HIDE1~$]">Close sidebar</div>
++<div class="menuitems" url="">No change</div>
++</div>
++
++<script language="JavaScript1.2">
++if (document.all && window.print) {
++ rcmenu.className = menustyle;
++ document.oncontextmenu = showmenu;
++ document.body.onclick = hidemenu;
++}
++</script>
++
++<!-- menu experiment end -->
++
++<div id="oneliner" style="visibility:hidden; position:absolute; left:0px; top:0px;"></div>
++<script>
++var h = document.getElementById("oneliner").offsetHeight*2;
++document.write("<style type='text/css'>.truncme { overflow:hidden;height: " +h+"px; }</style>");
++</script>
++
++<table cellpadding=0 cellspacing=0 border=0 width='100%'>
++<form method=get action="[$~SEARCHURL~$]" id=f1>
++<input type=hidden name=src value=5>
++<input type=hidden name=redir value=''>
++<tr>
++ <td width='1%'><IMG id=ctl src="[CONTROL_IMAGE]" border=0 usemap="#control"></td>
++ <td width='97%'><input TABINDEX="1" NAME="q" style="width:'100%'; FONT-FAMILY: verdana,arial,sans-serif;font-size:8pt"></td>
++ <td width='1%'><table cellpadding=2 cellspacing=0><tr><td> </td><td TABINDEX="8" onkeydown='if(event.keyCode!=16&&event.keyCode!=9)onclick()' onmouseover="this.bgColor='4C535B'" onmouseout="this.bgColor='#414A4F'" class=but bgcolor=414A4F valign=top onclick="location.href='[$~SETDISP2~$]';"><img src="mini_mini.gif"></td></tr></table></td>
++ <td width='1%'><table cellpadding=2 cellspacing=0><tr><td TABINDEX="9" onkeydown='if(event.keyCode!=16&&event.keyCode!=9)onclick()' onmouseover="this.bgColor='4C535B'" onmouseout="this.bgColor='#414A4F'" class=but bgcolor=414A4F valign=top onclick="if (hide())location.href='[$~HIDE1~$]';"><img src="mini_close.gif"></td></tr></table></td>
++</tr>
++<MAP name="control">
++<area TABINDEX="4" onkeydown='if(event.keyCode==13)location.href="movesidebar?side=1"' title="Move sidebar to Top" shape="rect" coords="9,0,22,8" href="/movesidebar?side=1" onmouseover="ctl.src='control1.gif'" onmouseout="ctl.src='[CONTROL_IMAGE]'">
++<area TABINDEX="5" onkeydown='if(event.keyCode==13)location.href="movesidebar?side=3"' title="Move sidebar to Bottom" shape="rect" coords="9,9,22,17" href="/movesidebar?side=3" onmouseover="ctl.src='control3.gif'" onmouseout="ctl.src='[CONTROL_IMAGE]'">
++<area TABINDEX="6" onkeydown='if(event.keyCode==13)location.href="movesidebar?side=0"' title="Move sidebar to Left" shape="rect" coords="0,2,8,15" href="/movesidebar?side=0" onmouseover="ctl.src='control0.gif'" onmouseout="ctl.src='[CONTROL_IMAGE]'">
++<area TABINDEX="7" onkeydown='if(event.keyCode==13)location.href="movesidebar?side=2"' title="Move sidebar to Right" shape="rect" coords="23,2,31,15" href="/movesidebar?side=2" onmouseover="ctl.src='control2.gif'" onmouseout="ctl.src='[CONTROL_IMAGE]'">
++</MAP>
++</table>
++
++<center>
++<table cellpadding=2 cellspacing=3>
++<tr>
++ <td TABINDEX="2" onkeydown='if(event.keyCode!=16&&event.keyCode!=9)onclick()' onclick="f1.submit()" onmouseover="this.bgColor='4C535B'" onmouseout="this.bgColor='#414A4F'" class=ch nowrap bgcolor=414A4F valign=top style="border-top: 1px solid #73787E;border-bottom: 1px solid #252C30;border-right: 1px solid #252C30;border-left: 1px solid #73787E;"><img src="logo.gif" align="texttop"> <font color=ffffff>Google Desktop Search&nbsp;</td>
++ <td TABINDEX="3" onkeydown='if(event.keyCode!=16&&event.keyCode!=9)onclick()' onclick="redir.value='google'; f1.submit(); redir.value='';" onmouseover="this.bgColor='4C535B'" onmouseout="this.bgColor='#414A4F'" class=ch bgcolor=414A4F nowrap valign=top style="border-top: 1px solid #73787E;border-bottom: 1px solid #252C30;border-right: 1px solid #252C30;border-left: 1px solid #73787E;">&nbsp;<font color=ffffff>Web&nbsp;</td>
++</tr>
++</form>
++</table>
++</center>
++
++<p class=gg>
++<table width=100% cellpadding=0 cellspacing=0><tr><td bgcolor=4E555C><img width=1 height=1></td></tr></table>
++<table class=ch cellpadding=0 cellspacing=0 style="background-color:'#424B50'; background-repeat: repeat-y;" background="section.jpg" width=100% height=18><tr onDblClick='return toggle("news");' onclick='return toggle("news");' onmouseover="this.bgColor='#465055'" onmouseout="this.bgColor=''"><td width=16 align=right><img id=newsicon src="[$~NEWS_MODE~$]" border=0></td><td valign=middle>&nbsp;<font color=ffffff>News</td></tr></table>
++<table width=100% cellpadding=0 cellspacing=0><tr><td bgcolor=313B40><img width=1 height=1></td></tr></table>
++
++<span id="news" class=[$~NEWS_CLASS~$] oclass=[$~NEWS_CLASS~$]>
++[CONTENT_NEWS]
++<p class=g>
++</span>
++
++<table width=100% cellpadding=0 cellspacing=0><tr><td bgcolor=4E555C><img width=1 height=1></td></tr></table>
++<table class=ch cellpadding=0 cellspacing=0 style="background-color:'#424B50'; background-repeat: repeat-y;" background="section.jpg" width=100% height=18><tr onDblClick='return toggle("inbox");' onclick='return toggle("inbox");' onmouseover="this.bgColor='#465055'" onmouseout="this.bgColor=''"><td width=16 align=right><img id=inboxicon src="[$~INBOX_MODE~$]" border=0></td><td valign=middle>&nbsp;<font color=ffffff>Email</td></tr></table>
++<table width=100% cellpadding=0 cellspacing=0><tr><td bgcolor=313B40><img width=1 height=1></td></tr></table>
++
++<span id=inbox class=[$~INBOX_CLASS~$] oclass=[$~INBOX_CLASS~$]>
++[CONTENT_INBOX]
++<p class=g>
++</span>
++
++<table width=100% cellpadding=0 cellspacing=0><tr><td bgcolor=4E555C><img width=1 height=1></td></tr></table>
++<table class=ch cellpadding=0 cellspacing=0 style="background-color:'#424B50'; background-repeat: repeat-y;" background="section.jpg" width=100% height=18><tr onDblClick='return toggle("hist");' onclick='return toggle("hist");' onmouseover="this.bgColor='#465055'" onmouseout="this.bgColor=''"><td width=16 align=right><img id=histicon src="[$~HIST_MODE~$]" border=0></td><td valign=middle>&nbsp;<font color=ffffff>Related History</td></tr></table>
++<table width=100% cellpadding=0 cellspacing=0><tr><td bgcolor=313B40><img width=1 height=1></td></tr></table>
++
++<span id="hist" class=[$~HIST_CLASS~$] oclass=[$~HIST_CLASS~$]>
++[CONTENT_HIST]
++<p class=g>
++</span>
++
++
++<table width=100% cellpadding=0 cellspacing=0><tr><td bgcolor=4E555C><img width=1 height=1></td></tr></table>
++<table class=ch cellpadding=0 cellspacing=0 style="background-color:'#424B50'; background-repeat: repeat-y;" background="section.jpg" width=100% height=18><tr onDblClick='return toggle("recent");' onclick='return toggle("recent");' onmouseover="this.bgColor='#465055'" onmouseout="this.bgColor=''"><td width=16 align=right><img id=recenticon src="[$~RECENT_MODE~$]" border=0></td><td valign=middle>&nbsp;<font color=ffffff>Recent</td></tr></table>
++<table width=100% cellpadding=0 cellspacing=0><tr><td bgcolor=313B40><img width=1 height=1></td></tr></table>
++
++<span id="recent" class=[$~RECENT_CLASS~$] oclass=[$~RECENT_CLASS~$]>
++[CONTENT_RECENT]
++<p class=g>
++</span>
++
++
++<table width=100% cellpadding=0 cellspacing=0><tr><td bgcolor=4E555C><img width=1 height=1></td></tr></table>
++<table class=ch cellpadding=0 cellspacing=0 style="background-color:'#424B50'; background-repeat: repeat-y;" background="section.jpg" width=100% height=18><tr onDblClick='return toggle("popular");' onclick='return toggle("popular");' onmouseover="this.bgColor='#465055'" onmouseout="this.bgColor=''"><td width=16 align=right><img id=popularicon src="[$~POPULAR_MODE~$]" border=0></td><td valign=middle>&nbsp;<font color=ffffff>Frequently Visited</td></tr></table>
++<table width=100% cellpadding=0 cellspacing=0><tr><td bgcolor=313B40><img width=1 height=1></td></tr></table>
++
++<span id="popular" class=[$~POPULAR_CLASS~$] oclass=[$~POPULAR_CLASS~$]>
++[CONTENT_POPULAR]
++<p class=g>
++</span>
++
++
++<table width=100% cellpadding=0 cellspacing=0><tr><td bgcolor=4E555C><img width=1 height=1></td></tr></table>
++<table class=ch cellpadding=0 cellspacing=0 style="background-color:'#424B50'; background-repeat: repeat-y;" background="section.jpg" width=100% height=18><tr onDblClick='return toggle("quib_debug");' onclick='return toggle("quib_debug");' onmouseover="this.bgColor='#465055'" onmouseout="this.bgColor=''"><td width=16 align=right><img id=quib_debugicon src="[$~QUIB_DEBUG_MODE~$]" border=0></td><td valign=middle>&nbsp;<font color=ffffff>Implicit Query Debug</td></tr></table>
++<table width=100% cellpadding=0 cellspacing=0><tr><td bgcolor=313B40><img width=1 height=1></td></tr></table>
++
++<span id="quib_debug" class=[$~QUIB_DEBUG_CLASS~$] oclass=[$~QUIB_DEBUG_CLASS~$]>
++[CONTENT_QUIB_DEBUG]
++</span>
++
++<span style="visibility:hidden" id='test'>t</span>
++
++[CONTENT_OTHER]
++
++[SCRIPT]
++</body>
++</html>
+diff --git a/tools/grit/grit/testdata/simple-input.xml b/tools/grit/grit/testdata/simple-input.xml
+new file mode 100644
+index 0000000000..92827fa4b5
+--- /dev/null
++++ b/tools/grit/grit/testdata/simple-input.xml
+@@ -0,0 +1,52 @@
++<?xml version="1.0" encoding="UTF-8"?>
++<grit base_dir="." latest_public_release="2" current_release="3" source_lang_id="en-US">
++ <release seq="2">
++ <messages>
++ <message name="IDS_OLD_MESSAGE" translateable="true">Hello earthlings!</message>
++ </messages>
++ </release>
++ <release seq="3">
++ <includes>
++ <include name="ID_EDIT_BOX_ICON" type="icon" translateable="false" file="images/edit_box.ico" />
++ <include name="ID_LOGO" type="gif" translateable="true" file="images/logo.gif"/>
++ </includes>
++ <messages>
++ <message name="IDS_BTN_GO" desc="Button text" meaning="verb">Go!</message>
++ <message name="IDS_GREETING" desc="Printed to greet the currently logged in user">
++ Hello <ph name="USERNAME">%s<ex>Joi</ex></ph>, how are you doing today?
++ </message>
++ </messages>
++ <structures>
++ <structure type="menu" name="IDM_FOO" file="rc_files/menus.rc" />
++ <structure type="dialog" name="IDD_BLAT" file="rc_files/dialogs.rc" />
++ <structure type="tr_html" name="IDR_HTML_TEMPLATE" file="templates/homepage.html" />
++ <structure type="dialog" name="IDD_NARROW_DIALOG" file="rc_files/dialogs.rc">
++ <skeleton expr="lang == 'fr-FR'" variant_of_revision="3">
++ <![CDATA[IDD_DIALOG1 DIALOGEX 0, 0, 186, 90
++STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION |
++ WS_SYSMENU
++CAPTION "TRANSLATEABLEPLACEHOLDER1"
++FONT 8, "MS Shell Dlg", 400, 0, 0x1
++BEGIN
++ DEFPUSHBUTTON "TRANSLATEABLEPLACEHOLDER2",IDOK,129,7,50,14
++ PUSHBUTTON "TRANSLATEABLEPLACEHOLDER3",IDCANCEL,129,24,50,14
++ LTEXT "TRANSLATEABLEPLACEHOLDER4",IDC_STATIC,23,31,40,8
++END]]>
++ </skeleton>
++ </structure>
++ <structure type="version" name="VS_VERSION_INFO" file="rc_files/version.rc"/>
++ </structures>
++ </release>
++ <translations>
++ <file path="figs_nl_translations.xml" />
++ <file path="cjk_translations.xml" />
++ </translations>
++ <outputs>
++ <output filename="resource.h" type="rc_header" />
++ <output filename="resource_en.rc" type="rc_all" lang="en-US" />
++ <output filename="resource_fr.rc" type="rc_all" lang="fr-FR" />
++ <output filename="resource_it.rc" type="rc_translateable" lang="it-IT" />
++ <output filename="resource_zh_cn.rc" type="rc_translateable" lang="zh-CN" />
++ <output filename="nontranslateable.rc" type="rc_nontranslateable" />
++ </outputs>
++</grit>
+diff --git a/tools/grit/grit/testdata/simple.html b/tools/grit/grit/testdata/simple.html
new file mode 100644
-index 0000000000..150c79913f
+index 0000000000..4392d23e98
--- /dev/null
-+++ b/tools/clang/plugins/tests/overridden_methods.h
++++ b/tools/grit/grit/testdata/simple.html
+@@ -0,0 +1,3 @@
++<p>
++ Hello!
++</p>
+\ No newline at end of file
+diff --git a/tools/grit/grit/testdata/source.rc b/tools/grit/grit/testdata/source.rc
+new file mode 100644
+index 0000000000..fbc72284e9
+--- /dev/null
++++ b/tools/grit/grit/testdata/source.rc
+@@ -0,0 +1,57 @@
++IDC_KLONKMENU MENU
++BEGIN
++ POPUP "&File"
++ BEGIN
++ MENUITEM "E&xit", IDM_EXIT
++ MENUITEM "This be ""Klonk"" me like", ID_FILE_THISBE
++ POPUP "gonk"
++ BEGIN
++ MENUITEM "Klonk && is [good]", ID_GONK_KLONKIS
++ END
++ END
++ POPUP "&Help"
++ BEGIN
++ MENUITEM "&About ...", IDM_ABOUT
++ END
++END
++
++IDD_ABOUTBOX DIALOGEX 22, 17, 230, 75
++STYLE DS_SETFONT | DS_MODALFRAME | WS_CAPTION | WS_SYSMENU
++CAPTION "About"
++FONT 8, "System", 0, 0, 0x0
++BEGIN
++ ICON IDI_KLONK,IDC_MYICON,14,9,20,20
++ LTEXT "klonk Version ""yibbee"" 1.0",IDC_STATIC,49,10,119,8,
++ SS_NOPREFIX
++ LTEXT "Copyright (C) 2005",IDC_STATIC,49,20,119,8
++ DEFPUSHBUTTON "OK",IDOK,195,6,30,11,WS_GROUP
++ CONTROL "Jack ""Black"" Daniels",IDC_RADIO1,"Button",
++ BS_AUTORADIOBUTTON,46,51,84,10
++END
++
++IDD_DIFFERENT_LENGTH_IN_TRANSL DIALOGEX 22, 17, 230, 75
++STYLE DS_SETFONT | DS_MODALFRAME | WS_CAPTION | WS_SYSMENU
++CAPTION "Bingobobbi"
++FONT 8, "System", 0, 0, 0x0
++BEGIN
++ LTEXT "Howdie dodie!",IDC_STATIC,49,10,119,8,SS_NOPREFIX
++ LTEXT "Yo froodie!",IDC_STATIC,49,20,119,8
++END
++
++STRINGTABLE
++BEGIN
++ IDS_SIMPLE "One"
++ IDS_PLACEHOLDER "%s birds"
++ IDS_PLACEHOLDERS "%d of %d"
++ IDS_REORDERED_PLACEHOLDERS "$1 of $2"
++ // Won't be in translations list because it has changed
++ IDS_CHANGED "This was the old version"
++ IDS_TWIN_1 "Hello"
++ IDS_TWIN_2 "Hello"
++ IDS_NOT_TRANSLATEABLE ":"
++ IDS_LONGER_TRANSLATED "Removed document $1"
++ // Won't appear in the list of translations because it's not in the .grd file
++ IDS_NO_LONGER_USED "Not used"
++ IDS_DIFFERENT_TWIN_1 "Howdie"
++ IDS_DIFFERENT_TWIN_2 "Howdie"
++END
+diff --git a/tools/grit/grit/testdata/special_100_percent/a.png b/tools/grit/grit/testdata/special_100_percent/a.png
+new file mode 100644
+index 0000000000000000000000000000000000000000..5d5089038ca71172e95db9e7aae1e1fa5cebd505
+GIT binary patch
+literal 159
+zcmeAS@N?(olHy`uVBq!ia0vp^oFL4>0wld=oSO}#(mY)pLnNjq|2Y3)zGGzYPN&L+
+zMSC}CcCfp=Dtxv4%6W%G#Q=|R|L;6pCCLUWO)Z<5eoL%TkDTw=s4X!^d(Qa<2khAN
+zZPy!XToBAic1Ss}vcWiD27B3&`Zj^H6CO>7R1{ToQ;=ggdEYbV=IISvfHpFCy85}S
+Ib4q9e0O9jEh5!Hn
+
+literal 0
+HcmV?d00001
+
+diff --git a/tools/grit/grit/testdata/status.html b/tools/grit/grit/testdata/status.html
+new file mode 100644
+index 0000000000..6b997b9369
+--- /dev/null
++++ b/tools/grit/grit/testdata/status.html
+@@ -0,0 +1,44 @@
++[HEADER]
++<table cellspacing=0 cellPadding=0 width="100%" border=0>
++<tr bgcolor=#3399cc><td align=middle height=1><img height=1 width=1></td></tr>
++</table>
++<table cellspacing=0 cellPadding=1 width="100%" bgcolor=#e8f4f7 border=0>
++<tr><td height=20><font size=+1 color=#000000>&nbsp;<b>Desktop Search Status</b></font></td></tr>
++</table>
++<br>
++<center>
++[$~MESSAGE~$]
++<table cellspacing=0 cellPadding=6 width=500 border=0>
++<tr>
++ <td>&nbsp;</td>
++ <td align=right nowrap><i><font size=-1>Number of items</font></i></td>
++ <td align=right nowrap><i><font size=-1>Time of newest item</font></i></td>
++</tr>
++<tr>
++ <td width=1% nowrap><img style="vertical-align:middle" width=16 height=16 src=favicon.ico>&nbsp; Total searchable items</td>
++ <td align=right><b>[TOTAL_COUNT]</b></td>
++ <td align=right><b>[TOTAL_TIME]</b></td>
++</tr>
++<tr>
++ <td><font size=-1>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img style="vertical-align:middle" src=email.gif width=16 height=16>&nbsp; Emails</font></td>
++ <td align=right><font size=-1>[EMAIL_COUNT]</font></td>
++ <td align=right><font size=-1>[EMAIL_TIME]</font></td>
++</tr>
++<tr>
++ <td><font size=-1>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img style="vertical-align:middle" src="16x16_chat.gif" width=16 height=16>&nbsp; Chats</font></td>
++ <td align=right><font size=-1>[IM_COUNT]</font></td>
++ <td align=right><font size=-1>[IM_TIME]</font></td>
++</tr>
++<tr>
++ <td><font size=-1>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img style="vertical-align:middle" src=html.gif width=16 height=16>&nbsp; Web history</font></td>
++ <td align=right><font size=-1>[WEB_COUNT]</font></td>
++ <td align=right><font size=-1>[WEB_TIME]</font></td>
++</tr>
++<tr>
++ <td><font size=-1>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img style="vertical-align:middle" src=file.gif width=16 height=16>&nbsp; Files</font></td>
++ <td align=right><font size=-1>[FILE_COUNT]</font></td>
++ <td align=right><font size=-1>[FILE_TIME]</font></td>
++</tr>
++</table>
++</center>
++[FOOTER]
+\ No newline at end of file
+diff --git a/tools/grit/grit/testdata/structure_variables.html b/tools/grit/grit/testdata/structure_variables.html
+new file mode 100644
+index 0000000000..2a15de8072
+--- /dev/null
++++ b/tools/grit/grit/testdata/structure_variables.html
+@@ -0,0 +1,4 @@
++<h1>[GREETING]!</h1>
++Some cool things are [THINGS].
++Did you know that [EQUATION]?
++<include src="[filename].html">
+diff --git a/tools/grit/grit/testdata/substitute.grd b/tools/grit/grit/testdata/substitute.grd
+new file mode 100644
+index 0000000000..95dcc56e1d
+--- /dev/null
++++ b/tools/grit/grit/testdata/substitute.grd
+@@ -0,0 +1,31 @@
++<?xml version="1.0" encoding="UTF-8"?> <!-- -*- XML -*- -->
++<grit
++ base_dir="."
++ source_lang_id="en"
++ tc_project="GoogleDesktopWindowsClient"
++ latest_public_release="0"
++ current_release="1"
++ enc_check="möl">
++ <outputs>
++ <output filename="resource.h" type="rc_header" />
++ <output filename="en_generated_resources.rc" type="rc_all" lang="en" />
++ <output filename="sv_generated_resources.rc" type="rc_all" lang="sv" />
++ </outputs>
++ <translations>
++ <file path="substitute.xmb" lang="sv" />
++ </translations>
++ <release seq="1" allow_pseudo="false">
++ <messages first_id="8192">
++ <message name="IDS_COPYRIGHT_GOOGLE_LONG" sub_variable="true" desc="Gadget copyright notice. Needs to be updated every year.">
++ Copyright 2008 Google Inc. All Rights Reserved.
++ </message>
++ <message name="IDS_NEWS_PANEL_COPYRIGHT">
++ Google Desktop News gadget
++[IDS_COPYRIGHT_GOOGLE_LONG]
++View news that is personalized based on the articles you read.
++
++For example, if you read lots of sports news, you'll see more sports articles. If you read technology news less often, you'll see fewer of those articles.
++ </message>
++ </messages>
++ </release>
++</grit>
+diff --git a/tools/grit/grit/testdata/substitute.xmb b/tools/grit/grit/testdata/substitute.xmb
+new file mode 100644
+index 0000000000..e592069c8b
+--- /dev/null
++++ b/tools/grit/grit/testdata/substitute.xmb
+@@ -0,0 +1,10 @@
++<?xml version="1.0" encoding="UTF-8"?>
++<!DOCTYPE translationbundle SYSTEM "/home/build/nonconf/google3/i18n/translationbundle.dtd">
++<translationbundle lang="sv">
++<translation id="7239109800378180620">© 2008 Google Inc. Med ensamrätt.</translation>
++<translation id="6212022020330010625">Google Desktop News gadget
++<ph name="IDS_COPYRIGHT_GOOGLE_LONG_1"/>
++Se nyheter som är anpassade till dig, baserat på de artiklar du läser.
++
++Om du t.ex. läser massor av sportnyheter kommer du att se fler sportartiklar. Om du inte läser tekniknyheter lika ofta ser du färre av dessa artiklar.</translation>
++</translationbundle>
+diff --git a/tools/grit/grit/testdata/substitute_no_ids.grd b/tools/grit/grit/testdata/substitute_no_ids.grd
+new file mode 100644
+index 0000000000..d569d1cacd
+--- /dev/null
++++ b/tools/grit/grit/testdata/substitute_no_ids.grd
+@@ -0,0 +1,31 @@
++<?xml version="1.0" encoding="UTF-8"?> <!-- -*- XML -*- -->
++<grit
++ base_dir="."
++ source_lang_id="en"
++ tc_project="GoogleDesktopWindowsClient"
++ latest_public_release="0"
++ current_release="1"
++ enc_check="möl">
++ <outputs>
++ <output filename="resource.h" type="rc_header" />
++ <output filename="en_generated_resources.rc" type="rc_all" lang="en" />
++ <output filename="sv_generated_resources.rc" type="rc_all" lang="sv" />
++ </outputs>
++ <translations>
++ <file path="substitute.xmb" lang="sv" />
++ </translations>
++ <release seq="1" allow_pseudo="false">
++ <messages>
++ <message name="IDS_COPYRIGHT_GOOGLE_LONG" sub_variable="true" desc="Gadget copyright notice. Needs to be updated every year.">
++ Copyright 2008 Google Inc. All Rights Reserved.
++ </message>
++ <message name="IDS_NEWS_PANEL_COPYRIGHT">
++ Google Desktop News gadget
++[IDS_COPYRIGHT_GOOGLE_LONG]
++View news that is personalized based on the articles you read.
++
++For example, if you read lots of sports news, you'll see more sports articles. If you read technology news less often, you'll see fewer of those articles.
++ </message>
++ </messages>
++ </release>
++</grit>
+diff --git a/tools/grit/grit/testdata/substitute_tmpl.grd b/tools/grit/grit/testdata/substitute_tmpl.grd
+new file mode 100644
+index 0000000000..be7b601707
+--- /dev/null
++++ b/tools/grit/grit/testdata/substitute_tmpl.grd
+@@ -0,0 +1,31 @@
++<?xml version="1.0" encoding="UTF-8"?> <!-- -*- XML -*- -->
++<grit
++ base_dir="."
++ source_lang_id="en"
++ tc_project="GoogleDesktopWindowsClient"
++ latest_public_release="0"
++ current_release="1"
++ enc_check="möl">
++ <outputs>
++ <output filename="resource.h" type="rc_header" />
++ <output filename="en_${name}_resources.rc" type="rc_all" lang="en" />
++ <output filename="sv_${name}_resources.rc" type="rc_all" lang="sv" />
++ </outputs>
++ <translations>
++ <file path="substitute.xmb" lang="sv" />
++ </translations>
++ <release seq="1" allow_pseudo="false">
++ <messages first_id="8192">
++ <message name="IDS_COPYRIGHT_GOOGLE_LONG" sub_variable="true" desc="Gadget copyright notice. Needs to be updated every year.">
++ Copyright 2008 Google Inc. All Rights Reserved.
++ </message>
++ <message name="IDS_NEWS_PANEL_COPYRIGHT">
++ Google Desktop News gadget
++[IDS_COPYRIGHT_GOOGLE_LONG]
++View news that is personalized based on the articles you read.
++
++For example, if you read lots of sports news, you'll see more sports articles. If you read technology news less often, you'll see fewer of those articles.
++ </message>
++ </messages>
++ </release>
++</grit>
+diff --git a/tools/grit/grit/testdata/test_css.css b/tools/grit/grit/testdata/test_css.css
+new file mode 100644
+index 0000000000..55d5dd1770
+--- /dev/null
++++ b/tools/grit/grit/testdata/test_css.css
+@@ -0,0 +1 @@
++This is a test!
+diff --git a/tools/grit/grit/testdata/test_html.html b/tools/grit/grit/testdata/test_html.html
+new file mode 100644
+index 0000000000..55d5dd1770
+--- /dev/null
++++ b/tools/grit/grit/testdata/test_html.html
+@@ -0,0 +1 @@
++This is a test!
+diff --git a/tools/grit/grit/testdata/test_js.js b/tools/grit/grit/testdata/test_js.js
+new file mode 100644
+index 0000000000..55d5dd1770
+--- /dev/null
++++ b/tools/grit/grit/testdata/test_js.js
+@@ -0,0 +1 @@
++This is a test!
+diff --git a/tools/grit/grit/testdata/test_svg.svg b/tools/grit/grit/testdata/test_svg.svg
+new file mode 100644
+index 0000000000..55d5dd1770
+--- /dev/null
++++ b/tools/grit/grit/testdata/test_svg.svg
+@@ -0,0 +1 @@
++This is a test!
+diff --git a/tools/grit/grit/testdata/test_text.txt b/tools/grit/grit/testdata/test_text.txt
+new file mode 100644
+index 0000000000..55d5dd1770
+--- /dev/null
++++ b/tools/grit/grit/testdata/test_text.txt
+@@ -0,0 +1 @@
++This is a test!
+diff --git a/tools/grit/grit/testdata/time_related.html b/tools/grit/grit/testdata/time_related.html
+new file mode 100644
+index 0000000000..ee64b1665e
+--- /dev/null
++++ b/tools/grit/grit/testdata/time_related.html
+@@ -0,0 +1,11 @@
++[HEADER]
++[CHROME]
++[NAV_PRE_POST]
++[$~MESSAGE~$]<br>
++<table border=0 cellpadding=2 cellspacing=0 width='100%'>
++[CONTENTS]
++</table><br>
++
++[NAV_PRE_POST]
++[FOOTER]
++
+diff --git a/tools/grit/grit/testdata/toolbar_about.html b/tools/grit/grit/testdata/toolbar_about.html
+new file mode 100644
+index 0000000000..bb4b0eb355
+--- /dev/null
++++ b/tools/grit/grit/testdata/toolbar_about.html
+@@ -0,0 +1,138 @@
++<html id=dlgAbout STYLE="width: 25.8em; height: 17em" [GRITDIR]>
++<head>
++<meta http-equiv="content-type" content="text/html; charset=utf-8">
++<title>About Google Toolbar</title>
++<style>
++.button {
++ width: 7em;
++ height: 2.2em;
++ color: buttontext;
++ font-family: MS Sans Serif;
++ font-size:8pt;
++ cursor: hand;
++}
++</style>
++
++<script> <!--
++ function HandleError(message, url, line) {
++ var L_Dialog_ErrorMessage = "An error has occured in this dialog.";
++ var L_ErrorNumber_Text = "Error: ";
++ var str = L_Dialog_ErrorMessage + "\n\n"
++ + L_ErrorNumber_Text + line + "\n"
++ + message;
++ alert (str);
++ window.close();
++ return true;
++ }
++
++ function OnKeyPress(nCode) {
++ if (nCode == 27) {
++ window.close();
++ return;
++ }
++ }
++
++ function OnLoad() {
++ if ((null != window.dialogArguments) && (window.dialogArguments.indexOf("&") == -1) && (window.dialogArguments.indexOf("<") == -1)) {
++ version.innerHTML = window.dialogArguments;
++ } else {
++ version.innerText = "Version: Unknown";
++ }
++ }
++
++ window.onerror = HandleError;
++ // -->
++</script>
++
++</head>
++
++
++<body bgcolor="#FFFFFF" onload="OnLoad()" onkeydown="OnKeyPress(event.keyCode)" onkeypress="OnKeyPress(event.keyCode)" scroll=no>
++
++<table border=0>
++
++ <tr height=5>
++ <td width=5></td>
++ <td></td>
++ <td></td>
++ <td></td>
++ <td width=5></td>
++ </tr>
++
++ <tr>
++ <td></td>
++ <td colspan=3>
++
++
++<table border="0" cellpadding="0" cellspacing="0" valign="top">
++ <tr>
++ <td valign="top" height="47" width="155">
++ <div align="center"><img src="title_toolbar.gif" width="275" height="59" alt="Google Toolbar"></div>
++ </td>
++ <td valign="middle" height="47" width="713">
++ <hr size=1 color=25479D></td></tr>
++</table>
++
++
++ </td>
++ <!--
++ <TD colspan=2>
++ <span style="COLOR: black; FONT: 18pt Tahoma, MS Shell Dlg"><b>
++ Google Toolbar&trade;</b>
++ </span>
++ </TD>
++ -->
++ <td valign="middle">
++ </td>
++ </tr>
++
++ <tr>
++ <td></td>
++ <td align=center><img src="googly.gif"></td>
++ <td colspan=2 align=left>
++ <span style="WIDTH: 25em; height:6em COLOR: black; FONT: 8pt Tahoma, MS Shell Dlg">
++ <span id=version></span><br>
++ </span>
++ </td>
++ <td></td>
++ </tr>
++
++ <tr height=50>
++ <td></td>
++ <td></td>
++ <td colspan=2 align=left>
++ <span style="WIDTH: 25em; height:6em COLOR: black; FONT: 8pt Tahoma, MS Shell Dlg">
++ <!--$/translate-->
++ <i>De parvis grandis acervus erit</i>
++ <!--$translate-->
++ </span>
++ </td>
++ <td></td>
++ </tr>
++
++ <tr height=40>
++ <td></td>
++ <td></td>
++ <td></td>
++ <td></td>
++ <td></td>
++ </tr>
++
++ <tr>
++ <td></td>
++ <td width=80></td>
++ <td>
++ <!--$/translate-->
++ <span style="WIDTH: 20em; COLOR: black; FONT: 8pt Tahoma, MS Shell Dlg" id="copyright">&copy; 2006 Google</span>
++ <!--$translate-->
++ </td>
++ <td id=ok-button align=right><button tabindex=1 type=submit align=right id="okButton" class=button onClick="window.close();" >OK</button>
++ </td>
++ <td></td>
++ </tr>
++
++</table>
++</span>
++
++</body>
++</html>
+diff --git a/tools/grit/grit/testdata/tools/grit/resource_ids b/tools/grit/grit/testdata/tools/grit/resource_ids
+new file mode 100644
+index 0000000000..8a2b608df1
+--- /dev/null
++++ b/tools/grit/grit/testdata/tools/grit/resource_ids
+@@ -0,0 +1,176 @@
++# Copyright (c) 2011 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++#
++# This file is used to assign starting resource ids for resources and strings
++# used by Chromium. This is done to ensure that resource ids are unique
++# across all the grd files. If you are adding a new grd file, please add
++# a new entry to this file.
++#
++# The first entry in the file, SRCDIR, is special: It is a relative path from
++# this file to the base of your checkout.
++#
++# http://msdn.microsoft.com/en-us/library/t2zechd4(VS.71).aspx says that the
++# range for IDR_ is 1 to 28,671 and the range for IDS_ is 1 to 32,767 and
++# common convention starts practical use of IDs at 100 or 101.
++{
++ "SRCDIR": "../..",
++
++ "chrome/browser/browser_resources.grd": {
++ "includes": [500],
++ },
++ "chrome/browser/resources/component_extension_resources.grd": {
++ "includes": [1000],
++ },
++ "chrome/browser/resources/net_internals_resources.grd": {
++ "includes": [1500],
++ },
++ "chrome/browser/resources/shared_resources.grd": {
++ "includes": [2000],
++ },
++ "chrome/common/common_resources.grd": {
++ "includes": [2500],
++ },
++ "chrome/default_plugin/default_plugin_resources.grd": {
++ "includes": [3000],
++ },
++ "chrome/renderer/renderer_resources.grd": {
++ "includes": [3500],
++ },
++ "net/base/net_resources.grd": {
++ "includes": [4000],
++ },
++ "webkit/glue/webkit_resources.grd": {
++ "includes": [4500],
++ },
++ "webkit/tools/test_shell/test_shell_resources.grd": {
++ "includes": [5000],
++ },
++ "ui/resources/ui_resources.grd": {
++ "includes": [5500],
++ },
++ "chrome/app/theme/theme_resources.grd": {
++ "includes": [6000],
++ },
++ "chrome_frame/resources/chrome_frame_resources.grd": {
++ "includes": [6500],
++ },
++ # WebKit.grd can be in two different places depending on whether we are
++ # in a chromium checkout or a webkit-only checkout.
++ "third_party/WebKit/Source/WebKit/chromium/WebKit.grd": {
++ "includes": [7000],
++ },
++ "WebKit.grd": {
++ "includes": [7000],
++ },
++
++ "ui/base/strings/app_locale_settings.grd": {
++ "META": {"join": 2},
++ "messages": [7500],
++ },
++ "chrome/app/resources/locale_settings.grd": {
++ "includes": [8000],
++ "messages": [8500],
++ },
++ # These each start with the same resource id because we only use one
++ # file for each build (cros, linux, mac, or win).
++ "chrome/app/resources/locale_settings_cros.grd": {
++ "messages": [9000],
++ },
++ "chrome/app/resources/locale_settings_linux.grd": {
++ "messages": [9000],
++ },
++ "chrome/app/resources/locale_settings_mac.grd": {
++ "messages": [9000],
++ },
++ "chrome/app/resources/locale_settings_win.grd": {
++ "messages": [9000],
++ },
++
++ "ui/base/strings/ui_strings.grd": {
++ "META": {"join": 4},
++ "messages": [9500],
++ },
++ # Chromium strings and Google Chrome strings must start at the same id.
++ # We only use one file depending on whether we're building Chromium or
++ # Google Chrome.
++ "chrome/app/chromium_strings.grd": {
++ "messages": [10000],
++ },
++ "chrome/app/google_chrome_strings.grd": {
++ "messages": [10000],
++ },
++ # Leave lots of space for generated_resources since it has most of our
++ # strings.
++ "chrome/app/generated_resources.grd": {
++ "META": {"join": 2},
++ "structures": [10500],
++ "messages": [11000],
++ },
++ # The chrome frame dialogs are also in generated_resources.grd so they
++ # get included by the translation console. We make sure that the ids
++ # for structures here are the same as for generated_resources.grd.
++ "chrome_frame/resources/chrome_frame_dialogs.grd": {
++ "structures": [10500],
++ "includes": [10750],
++ },
++ "webkit/glue/inspector_strings.grd": {
++ "messages": [16000],
++ },
++ "webkit/glue/webkit_strings.grd": {
++ "messages": [16500],
++ },
++
++ "chrome_frame/resources/chrome_frame_resources.grd": {
++ "includes": [17500],
++ "structures": [18000],
++ },
++
++ "ui/gfx/gfx_resources.grd": {
++ "includes": [18500],
++ },
++
++ "chrome/app/policy/policy_templates.grd": {
++ "structures": [19000],
++ "messages": [19010],
++ },
++
++ "chrome/browser/autofill/autofill_resources.grd": {
++ "messages": [19500],
++ },
++ "chrome/browser/resources/sync_internals_resources.grd": {
++ "includes": [20000],
++ },
++ # This file is generated during the build.
++ "<(SHARED_INTERMEDIATE_DIR)/devtools/devtools_resources.grd": {
++ "includes": [20500],
++ },
++ # All standard and large theme resources should have the same IDs.
++ "chrome/app/theme/theme_resources_standard.grd": {
++ "includes": [21000],
++ },
++ "chrome/app/theme/theme_resources_large.grd": {
++ "includes": [21000],
++ },
++ # This file is generated during the build.
++ "chrome/browser/debugger/frontend/devtools_frontend_resources.grd": {
++ "META": {"join": 2},
++ "includes": [21500],
++ },
++ "cloud_print/virtual_driver/win/install/virtual_driver_setup_resources.grd": {
++ "messages": [22500],
++ },
++ "chrome/browser/resources/quota_internals_resources.grd": {
++ "includes": [23000],
++ },
++ "chrome/browser/resources/workers_resources.grd": {
++ "includes": [23500],
++ },
++ # All standard and large theme resources should have the same IDs.
++ "ui/resources/ui_resources_standard.grd": {
++ "includes": [24000],
++ },
++ "ui/resources/ui_resources_large.grd": {
++ "includes": [24000],
++ },
++}
+diff --git a/tools/grit/grit/testdata/transl.rc b/tools/grit/grit/testdata/transl.rc
+new file mode 100644
+index 0000000000..2f2595db3f
+--- /dev/null
++++ b/tools/grit/grit/testdata/transl.rc
+@@ -0,0 +1,56 @@
++IDC_KLONKMENU MENU
++BEGIN
++ POPUP "&Skra"
++ BEGIN
++ MENUITEM "&Haetta", IDM_EXIT
++ MENUITEM "Thetta er ""Klonk"" sem eg fyla", ID_FILE_THISBE
++ POPUP "gonkurinn"
++ BEGIN
++ MENUITEM "Klonk && er [good]", ID_GONK_KLONKIS
++ END
++ END
++ POPUP "&Hjalp"
++ BEGIN
++ MENUITEM "&Um...", IDM_ABOUT
++ END
++END
++
++IDD_ABOUTBOX DIALOGEX 22, 17, 230, 75
++STYLE DS_SETFONT | DS_MODALFRAME | WS_CAPTION | WS_SYSMENU
++CAPTION "Um Klonk"
++FONT 8, "System", 0, 0, 0x0
++BEGIN
++ ICON IDI_KLONK,IDC_MYICON,14,9,20,20
++ LTEXT "klonk utgafa ""jibbi"" 1.0",IDC_STATIC,49,10,119,8,
++ SS_NOPREFIX
++ LTEXT "Hofundarrettur (C) 2005",IDC_STATIC,49,20,119,8
++ DEFPUSHBUTTON "I lagi",IDOK,195,6,30,11,WS_GROUP
++ CONTROL "Jack ""Black"" Daniels",IDC_RADIO1,"Button",
++ BS_AUTORADIOBUTTON,46,51,84,10
++END
++
++IDD_DIFFERENT_LENGTH_IN_TRANSL DIALOGEX 22, 17, 230, 75
++STYLE DS_SETFONT | DS_MODALFRAME | WS_CAPTION | WS_SYSMENU
++CAPTION "Bingobobbi"
++FONT 8, "System", 0, 0, 0x0
++BEGIN
++ LTEXT "Howdie dodie!",IDC_STATIC,49,10,119,8,SS_NOPREFIX
++END
++
++STRINGTABLE
++BEGIN
++ IDS_SIMPLE "Ein"
++ IDS_PLACEHOLDER "%s Vogeln"
++ IDS_PLACEHOLDERS "%d von %d"
++ // Shouldn't be part of translations list because the translation is
++ // reordered so placeholder fixup fails
++ IDS_REORDERED_PLACEHOLDERS "$2 auf $1"
++ IDS_CHANGED "Dass war die alte Version"
++ IDS_TWIN_1 "Hallo"
++ IDS_TWIN_2 "Hallo"
++ IDS_NOT_TRANSLATEABLE ":"
++ IDS_LONGER_TRANSLATED "Dokument $1 ist entfernt worden"
++ IDS_NO_LONGER_USED "Nicht verwendet"
++ IDS_DIFFERENT_TWIN_1 "Howdie"
++ IDS_DIFFERENT_TWIN_2 "Hallo sagt man"
++END
+diff --git a/tools/grit/grit/testdata/versions.html b/tools/grit/grit/testdata/versions.html
+new file mode 100644
+index 0000000000..d1f40d8d72
+--- /dev/null
++++ b/tools/grit/grit/testdata/versions.html
+@@ -0,0 +1,7 @@
++[HEADER]
++
++[TOP_CHROME]
++[CONTENTS]
++
++[NEXT_PREV]
++[FOOTER]
+diff --git a/tools/grit/grit/testdata/whitelist.txt b/tools/grit/grit/testdata/whitelist.txt
+new file mode 100644
+index 0000000000..5b3aca40b5
+--- /dev/null
++++ b/tools/grit/grit/testdata/whitelist.txt
+@@ -0,0 +1,4 @@
++IDS_MESSAGE_WHITELISTED
++IDR_STRUCTURE_WHITELISTED
++IDR_STRUCTURE_IN_TRUE_IF_WHITELISTED
++IDR_INCLUDE_WHITELISTED
+diff --git a/tools/grit/grit/testdata/whitelist_resources.grd b/tools/grit/grit/testdata/whitelist_resources.grd
+new file mode 100644
+index 0000000000..9925688ff5
+--- /dev/null
++++ b/tools/grit/grit/testdata/whitelist_resources.grd
@@ -0,0 +1,54 @@
-+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
-+// Use of this source code is governed by a BSD-style license that can be
-+// found in the LICENSE file.
-+
-+#ifndef OVERRIDDEN_METHODS_H_
-+#define OVERRIDDEN_METHODS_H_
-+
-+// Should warn about overriding of methods.
-+class BaseClass {
-+ public:
-+ virtual ~BaseClass() {}
-+ virtual void SomeMethod() = 0;
-+ virtual void SomeOtherMethod() = 0;
-+ virtual void SomeInlineMethod() = 0;
-+ virtual void SomeNonPureBaseMethod() {}
-+};
++<?xml version="1.0" encoding="UTF-8"?>
++<grit latest_public_release="0"
++ current_release="1"
++ output_all_resource_defines="false">
++ <outputs>
++ <output filename="whitelist_test_resources.h" type="rc_header">
++ <emit emit_type='prepend'></emit>
++ </output>
++ <output filename="whitelist_test_resources_map.cc"
++ type="resource_file_map_source" />
++ <output filename="whitelist_test_resources_map.h"
++ type="resource_map_header" />
++ <output filename="whitelist_test_resources.pak" type="data_package" />
++ </outputs>
++ <translations>
++ <file path="substitute.xmb" lang="sv" />
++ </translations>
++ <release seq="1">
++ <structures>
++ <structure name="IDR_STRUCTURE_WHITELISTED" file="browser.html"
++ type="chrome_html" >
++ </structure>
++ <structure name="IDR_STRUCTURE_NOT_WHITELISTED" file="deleted.html"
++ type="chrome_html" >
++ </structure>
++ <if expr="True">
++ <structure name="IDR_STRUCTURE_IN_TRUE_IF_WHITELISTED"
++ file="details.html"
++ type="chrome_html" >
++ </structure>
++ <structure name="IDR_STRUCTURE_IN_TRUE_IF_NOT_WHITELISTED"
++ file="error.html"
++ type="chrome_html" >
++ </structure>
++ </if>
++ <if expr="False">
++ <structure name="IDR_STRUCTURE_IN_FALSE_IF_WHITELISTED"
++ file="status.html"
++ type="chrome_html" >
++ </structure>
++ <structure name="IDR_STRUCTURE_IN_FALSE_IF_NOT_WHITELISTED"
++ file="simple.html"
++ type="chrome_html" >
++ </structure>
++ </if>
++ </structures>
++ <includes>
++ <include name="IDR_INCLUDE_WHITELISTED" file="klonk.ico"
++ type="BINDATA" />
++ <include name="IDR_INCLUDE_NOT_WHITELISTED" file="klonk.rc"
++ type="BINDATA" />
++ </includes>
++ </release>
++</grit>
+diff --git a/tools/grit/grit/testdata/whitelist_strings.grd b/tools/grit/grit/testdata/whitelist_strings.grd
+new file mode 100644
+index 0000000000..df80f5fd32
+--- /dev/null
++++ b/tools/grit/grit/testdata/whitelist_strings.grd
+@@ -0,0 +1,23 @@
++<?xml version="1.0" encoding="UTF-8"?>
++<grit latest_public_release="0"
++ current_release="1"
++ output_all_resource_defines="false">
++ <outputs >
++ <output filename="whitelist_test_resources.h" type="rc_header">
++ <emit emit_type='prepend'></emit>
++ </output>
++ <output filename="en_whitelist_test_strings.rc" type="rc_all" lang="en" />
++ </outputs>
++ <release seq="1">
++ <messages>
++ <message name="IDS_MESSAGE_WHITELISTED"
++ desc="A message in the whiltelist file.">
++ Whitelisted.
++ </message>
++ <message name="IDS_MESSAGE_NOT_WHITELISTED"
++ desc="A message that isn't in the whiltelist file.">
++ Not whitelisted.
++ </message>
++ </messages>
++ </release>
++</grit>
+diff --git a/tools/grit/grit/tool/__init__.py b/tools/grit/grit/tool/__init__.py
+new file mode 100644
+index 0000000000..cc455b36e7
+--- /dev/null
++++ b/tools/grit/grit/tool/__init__.py
+@@ -0,0 +1,8 @@
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
+
-+class InterimClass : public BaseClass {
-+ // Should not warn about pure virtual methods.
-+ virtual void SomeMethod() = 0;
-+};
++'''Package grit.tool
++'''
+
-+namespace WebKit {
-+class WebKitObserver {
-+ public:
-+ virtual void WebKitModifiedSomething() {};
-+};
-+} // namespace WebKit
++pass
+diff --git a/tools/grit/grit/tool/android2grd.py b/tools/grit/grit/tool/android2grd.py
+new file mode 100644
+index 0000000000..005297bafe
+--- /dev/null
++++ b/tools/grit/grit/tool/android2grd.py
+@@ -0,0 +1,484 @@
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
+
-+namespace webkit_glue {
-+class WebKitObserverImpl : WebKit::WebKitObserver {
-+ public:
-+ virtual void WebKitModifiedSomething() {};
-+};
-+} // namespace webkit_glue
-+
-+class DerivedClass : public InterimClass,
-+ public webkit_glue::WebKitObserverImpl {
-+ public:
-+ // Should not warn about destructors.
-+ virtual ~DerivedClass() {}
-+ // Should warn.
-+ virtual void SomeMethod();
-+ // Should not warn if marked as override.
-+ virtual void SomeOtherMethod() override;
-+ // Should warn for inline implementations.
-+ virtual void SomeInlineMethod() {}
-+ // Should not warn if overriding a method whose origin is WebKit.
-+ virtual void WebKitModifiedSomething();
-+ // Should warn if overridden method isn't pure.
-+ virtual void SomeNonPureBaseMethod() {}
-+};
++"""The 'grit android2grd' tool."""
+
-+#endif // OVERRIDDEN_METHODS_H_
-diff --git a/tools/clang/plugins/tests/overridden_methods.txt b/tools/clang/plugins/tests/overridden_methods.txt
-new file mode 100644
-index 0000000000..7553ade70e
---- /dev/null
-+++ b/tools/clang/plugins/tests/overridden_methods.txt
-@@ -0,0 +1,20 @@
-+In file included from overridden_methods.cpp:5:
-+./overridden_methods.h:43:11: warning: [chromium-style] Overriding method must be marked with OVERRIDE.
-+ virtual void SomeMethod();
-+ ^
-+./overridden_methods.h:47:11: warning: [chromium-style] Overriding method must be marked with OVERRIDE.
-+ virtual void SomeInlineMethod() {}
-+ ^
-+./overridden_methods.h:51:11: warning: [chromium-style] Overriding method must be marked with OVERRIDE.
-+ virtual void SomeNonPureBaseMethod() {}
-+ ^
-+overridden_methods.cpp:24:11: warning: [chromium-style] Overriding method must be marked with OVERRIDE.
-+ virtual void SomeMethod();
-+ ^
-+overridden_methods.cpp:28:11: warning: [chromium-style] Overriding method must be marked with OVERRIDE.
-+ virtual void SomeInlineMethod() {}
-+ ^
-+overridden_methods.cpp:32:11: warning: [chromium-style] Overriding method must be marked with OVERRIDE.
-+ virtual void SomeNonPureBaseMethod() {}
-+ ^
-+6 warnings generated.
-diff --git a/tools/clang/plugins/tests/test.sh b/tools/clang/plugins/tests/test.sh
-new file mode 100755
-index 0000000000..262ebbba29
---- /dev/null
-+++ b/tools/clang/plugins/tests/test.sh
-@@ -0,0 +1,72 @@
-+#!/bin/bash
-+#
-+# Copyright (c) 2011 The Chromium Authors. All rights reserved.
++from __future__ import print_function
++
++import getopt
++import os.path
++import sys
++from xml.dom import Node
++import xml.dom.minidom
++
++import six
++from six import StringIO
++
++import grit.node.empty
++from grit.node import node_io
++from grit.node import message
++
++from grit.tool import interface
++
++from grit import grd_reader
++from grit import lazy_re
++from grit import tclib
++
++
++# The name of a string in strings.xml
++_STRING_NAME = lazy_re.compile(r'[a-z0-9_]+\Z')
++
++# A string's character limit in strings.xml
++_CHAR_LIMIT = lazy_re.compile(r'\[CHAR-LIMIT=(\d+)\]')
++
++# Finds String.Format() style format specifiers such as "%-5.2f".
++_FORMAT_SPECIFIER = lazy_re.compile(
++ r'%'
++ r'([1-9][0-9]*\$|<)?' # argument_index
++ r'([-#+ 0,(]*)' # flags
++ r'([0-9]+)?' # width
++ r'(\.[0-9]+)?' # precision
++ r'([bBhHsScCdoxXeEfgGaAtT%n])') # conversion
++
++
++class Android2Grd(interface.Tool):
++ """Tool for converting Android string.xml files into chrome Grd files.
++
++Usage: grit [global options] android2grd [OPTIONS] STRINGS_XML
++
++The Android2Grd tool will convert an Android strings.xml file (whose path is
++specified by STRINGS_XML) and create a chrome style grd file containing the
++relevant information.
++
++Because grd documents are much richer than strings.xml documents we supplement
++the information required by grds using OPTIONS with sensible defaults.
++
++OPTIONS may be any of the following:
++
++ --name FILENAME Specify the base FILENAME. This should be without
++ any file type suffix. By default
++ "chrome_android_strings" will be used.
++
++ --languages LANGUAGES Comma separated list of ISO language codes (e.g.
++ en-US, en-GB, ru, zh-CN). These codes will be used
++ to determine the names of resource and translations
++ files that will be declared by the output grd file.
++
++ --grd-dir GRD_DIR Specify where the resultant grd file
++ (FILENAME.grd) should be output. By default this
++ will be the present working directory.
++
++ --header-dir HEADER_DIR Specify the location of the directory where grit
++ generated C++ headers (whose name will be
++ FILENAME.h) will be placed. Use an empty string to
++ disable rc generation. Default: empty.
++
++ --rc-dir RC_DIR Specify the directory where resource files will
++ be located relative to grit build's output
++ directory. Use an empty string to disable rc
++ generation. Default: empty.
++
++ --xml-dir XML_DIR Specify where to place localized strings.xml files
++ relative to grit build's output directory. For each
++ language xx a values-xx/strings.xml file will be
++ generated. Use an empty string to disable
++ strings.xml generation. Default: '.'.
++
++ --xtb-dir XTB_DIR Specify where the xtb files containing translations
++ will be located relative to the grd file. Default:
++ '.'.
++"""
++
++ _NAME_FLAG = 'name'
++ _LANGUAGES_FLAG = 'languages'
++ _GRD_DIR_FLAG = 'grd-dir'
++ _RC_DIR_FLAG = 'rc-dir'
++ _HEADER_DIR_FLAG = 'header-dir'
++ _XTB_DIR_FLAG = 'xtb-dir'
++ _XML_DIR_FLAG = 'xml-dir'
++
++ def __init__(self):
++ self.name = 'chrome_android_strings'
++ self.languages = []
++ self.grd_dir = '.'
++ self.rc_dir = None
++ self.xtb_dir = '.'
++ self.xml_res_dir = '.'
++ self.header_dir = None
++
++ def ShortDescription(self):
++ """Returns a short description of the Android2Grd tool.
++
++ Overridden from grit.interface.Tool
++
++ Returns:
++ A string containing a short description of the android2grd tool.
++ """
++ return 'Converts Android string.xml files into Chrome grd files.'
++
++ def ParseOptions(self, args):
++ """Set this objects and return all non-option arguments."""
++ flags = [
++ Android2Grd._NAME_FLAG,
++ Android2Grd._LANGUAGES_FLAG,
++ Android2Grd._GRD_DIR_FLAG,
++ Android2Grd._RC_DIR_FLAG,
++ Android2Grd._HEADER_DIR_FLAG,
++ Android2Grd._XTB_DIR_FLAG,
++ Android2Grd._XML_DIR_FLAG, ]
++ (opts, args) = getopt.getopt(
++ args, None, ['%s=' % o for o in flags] + ['help'])
++
++ for key, val in opts:
++ # Get rid of the preceding hypens.
++ k = key[2:]
++ if k == Android2Grd._NAME_FLAG:
++ self.name = val
++ elif k == Android2Grd._LANGUAGES_FLAG:
++ self.languages = val.split(',')
++ elif k == Android2Grd._GRD_DIR_FLAG:
++ self.grd_dir = val
++ elif k == Android2Grd._RC_DIR_FLAG:
++ self.rc_dir = val
++ elif k == Android2Grd._HEADER_DIR_FLAG:
++ self.header_dir = val
++ elif k == Android2Grd._XTB_DIR_FLAG:
++ self.xtb_dir = val
++ elif k == Android2Grd._XML_DIR_FLAG:
++ self.xml_res_dir = val
++ elif k == 'help':
++ self.ShowUsage()
++ sys.exit(0)
++ return args
++
++ def Run(self, opts, args):
++ """Runs the Android2Grd tool.
++
++ Inherited from grit.interface.Tool.
++
++ Args:
++ opts: List of string arguments that should be parsed.
++ args: String containing the path of the strings.xml file to be converted.
++ """
++ args = self.ParseOptions(args)
++ if len(args) != 1:
++ print('Tool requires one argument, the path to the Android '
++ 'strings.xml resource file to be converted.')
++ return 2
++ self.SetOptions(opts)
++
++ android_path = args[0]
++
++ # Read and parse the Android strings.xml file.
++ with open(android_path) as android_file:
++ android_dom = xml.dom.minidom.parse(android_file)
++
++ # Do the hard work -- convert the Android dom to grd file contents.
++ grd_dom = self.AndroidDomToGrdDom(android_dom)
++ grd_string = six.text_type(grd_dom)
++
++ # Write the grd string to a file in grd_dir.
++ grd_filename = self.name + '.grd'
++ grd_path = os.path.join(self.grd_dir, grd_filename)
++ with open(grd_path, 'w') as grd_file:
++ grd_file.write(grd_string)
++
++ def AndroidDomToGrdDom(self, android_dom):
++ """Converts a strings.xml DOM into a DOM representing the contents of
++ a grd file.
++
++ Args:
++ android_dom: A xml.dom.Document containing the contents of the Android
++ string.xml document.
++ Returns:
++ The DOM for the grd xml document produced by converting the Android DOM.
++ """
++
++ # Start with a basic skeleton for the .grd file.
++ root = grd_reader.Parse(StringIO(
++ '''<?xml version="1.0" encoding="UTF-8"?>
++ <grit base_dir="." latest_public_release="0"
++ current_release="1" source_lang_id="en">
++ <outputs />
++ <translations />
++ <release allow_pseudo="false" seq="1">
++ <messages fallback_to_english="true" />
++ </release>
++ </grit>'''), dir='.')
++ outputs = root.children[0]
++ translations = root.children[1]
++ messages = root.children[2].children[0]
++ assert (isinstance(messages, grit.node.empty.MessagesNode) and
++ isinstance(translations, grit.node.empty.TranslationsNode) and
++ isinstance(outputs, grit.node.empty.OutputsNode))
++
++ if self.header_dir:
++ cpp_header = self.__CreateCppHeaderOutputNode(outputs, self.header_dir)
++ for lang in self.languages:
++ # Create an output element for each language.
++ if self.rc_dir:
++ self.__CreateRcOutputNode(outputs, lang, self.rc_dir)
++ if self.xml_res_dir:
++ self.__CreateAndroidXmlOutputNode(outputs, lang, self.xml_res_dir)
++ if lang != 'en':
++ self.__CreateFileNode(translations, lang)
++ # Convert all the strings.xml strings into grd messages.
++ self.__CreateMessageNodes(messages, android_dom.documentElement)
++
++ return root
++
++ def __CreateMessageNodes(self, messages, resources):
++ """Creates the <message> elements and adds them as children of <messages>.
++
++ Args:
++ messages: the <messages> element in the strings.xml dom.
++ resources: the <resources> element in the grd dom.
++ """
++ # <string> elements contain the definition of the resource.
++ # The description of a <string> element is contained within the comment
++ # node element immediately preceeding the string element in question.
++ description = ''
++ for child in resources.childNodes:
++ if child.nodeType == Node.COMMENT_NODE:
++ # Remove leading/trailing whitespace; collapse consecutive whitespaces.
++ description = ' '.join(child.data.split())
++ elif child.nodeType == Node.ELEMENT_NODE:
++ if child.tagName != 'string':
++ print('Warning: ignoring unknown tag <%s>' % child.tagName)
++ else:
++ translatable = self.IsTranslatable(child)
++ raw_name = child.getAttribute('name')
++ if not _STRING_NAME.match(raw_name):
++ print('Error: illegal string name: %s' % raw_name)
++ grd_name = 'IDS_' + raw_name.upper()
++ # Transform the <string> node contents into a tclib.Message, taking
++ # care to handle whitespace transformations and escaped characters,
++ # and coverting <xliff:g> placeholders into <ph> placeholders.
++ msg = self.CreateTclibMessage(child)
++ msg_node = self.__CreateMessageNode(messages, grd_name, description,
++ msg, translatable)
++ messages.AddChild(msg_node)
++ # Reset the description once a message has been parsed.
++ description = ''
++
++ def CreateTclibMessage(self, android_string):
++ """Transforms a <string/> element from strings.xml into a tclib.Message.
++
++ Interprets whitespace, quotes, and escaped characters in the android_string
++ according to Android's formatting and styling rules for strings. Also
++ converts <xliff:g> placeholders into <ph> placeholders, e.g.:
++
++ <xliff:g id="website" example="google.com">%s</xliff:g>
++ becomes
++ <ph name="website"><ex>google.com</ex>%s</ph>
++
++ Returns:
++ The tclib.Message.
++ """
++ msg = tclib.Message()
++ current_text = '' # Accumulated text that hasn't yet been added to msg.
++ nodes = android_string.childNodes
++
++ for i, node in enumerate(nodes):
++ # Handle text nodes.
++ if node.nodeType in (Node.TEXT_NODE, Node.CDATA_SECTION_NODE):
++ current_text += node.data
++
++ # Handle <xliff:g> and other tags.
++ elif node.nodeType == Node.ELEMENT_NODE:
++ if node.tagName == 'xliff:g':
++ assert node.hasAttribute('id'), 'missing id: ' + node.data()
++ placeholder_id = node.getAttribute('id')
++ placeholder_text = self.__FormatPlaceholderText(node)
++ placeholder_example = node.getAttribute('example')
++ if not placeholder_example:
++ print('Info: placeholder does not contain an example: %s' %
++ node.toxml())
++ placeholder_example = placeholder_id.upper()
++ msg.AppendPlaceholder(tclib.Placeholder(placeholder_id,
++ placeholder_text, placeholder_example))
++ else:
++ print('Warning: removing tag <%s> which must be inside a '
++ 'placeholder: %s' % (node.tagName, node.toxml()))
++ msg.AppendText(self.__FormatPlaceholderText(node))
++
++ # Handle other nodes.
++ elif node.nodeType != Node.COMMENT_NODE:
++ assert False, 'Unknown node type: %s' % node.nodeType
++
++ is_last_node = (i == len(nodes) - 1)
++ if (current_text and
++ (is_last_node or nodes[i + 1].nodeType == Node.ELEMENT_NODE)):
++ # For messages containing just text and comments (no xml tags) Android
++ # strips leading and trailing whitespace. We mimic that behavior.
++ if not msg.GetContent() and is_last_node:
++ current_text = current_text.strip()
++ msg.AppendText(self.__FormatAndroidString(current_text))
++ current_text = ''
++
++ return msg
++
++ def __FormatAndroidString(self, android_string, inside_placeholder=False):
++ r"""Returns android_string formatted for a .grd file.
++
++ * Collapses consecutive whitespaces, except when inside double-quotes.
++ * Replaces \\, \n, \t, \", \' with \, newline, tab, ", '.
++ """
++ backslash_map = {'\\' : '\\', 'n' : '\n', 't' : '\t', '"' : '"', "'" : "'"}
++ is_quoted_section = False # True when we're inside double quotes.
++ is_backslash_sequence = False # True after seeing an unescaped backslash.
++ prev_char = ''
++ output = []
++ for c in android_string:
++ if is_backslash_sequence:
++ # Unescape \\, \n, \t, \", and \'.
++ assert c in backslash_map, 'Illegal escape sequence: \\%s' % c
++ output.append(backslash_map[c])
++ is_backslash_sequence = False
++ elif c == '\\':
++ is_backslash_sequence = True
++ elif c.isspace() and not is_quoted_section:
++ # Turn whitespace into ' ' and collapse consecutive whitespaces.
++ if not prev_char.isspace():
++ output.append(' ')
++ elif c == '"':
++ is_quoted_section = not is_quoted_section
++ else:
++ output.append(c)
++ prev_char = c
++ output = ''.join(output)
++
++ if is_quoted_section:
++ print('Warning: unbalanced quotes in string: %s' % android_string)
++
++ if is_backslash_sequence:
++ print('Warning: trailing backslash in string: %s' % android_string)
++
++ # Check for format specifiers outside of placeholder tags.
++ if not inside_placeholder:
++ format_specifier = _FORMAT_SPECIFIER.search(output)
++ if format_specifier:
++ print('Warning: format specifiers are not inside a placeholder '
++ '<xliff:g/> tag: %s' % output)
++
++ return output
++
++ def __FormatPlaceholderText(self, placeholder_node):
++ """Returns the text inside of an <xliff:g> placeholder node."""
++ text = []
++ for childNode in placeholder_node.childNodes:
++ if childNode.nodeType in (Node.TEXT_NODE, Node.CDATA_SECTION_NODE):
++ text.append(childNode.data)
++ elif childNode.nodeType != Node.COMMENT_NODE:
++ assert False, 'Unknown node type in ' + placeholder_node.toxml()
++ return self.__FormatAndroidString(''.join(text), inside_placeholder=True)
++
++ def __CreateMessageNode(self, messages_node, grd_name, description, msg,
++ translatable):
++ """Creates and initializes a <message> element.
++
++ Message elements correspond to Android <string> elements in that they
++ declare a string resource along with a programmatic id.
++ """
++ if not description:
++ print('Warning: no description for %s' % grd_name)
++ # Check that we actually fit within the character limit we've specified.
++ match = _CHAR_LIMIT.search(description)
++ if match:
++ char_limit = int(match.group(1))
++ msg_content = msg.GetRealContent()
++ if len(msg_content) > char_limit:
++ print('Warning: char-limit for %s is %d, but length is %d: %s' %
++ (grd_name, char_limit, len(msg_content), msg_content))
++ return message.MessageNode.Construct(parent=messages_node,
++ name=grd_name,
++ message=msg,
++ desc=description,
++ translateable=translatable)
++
++ def __CreateFileNode(self, translations_node, lang):
++ """Creates and initializes the <file> elements.
++
++ File elements provide information on the location of translation files
++ (xtbs)
++ """
++ xtb_file = os.path.normpath(os.path.join(
++ self.xtb_dir, '%s_%s.xtb' % (self.name, lang)))
++ fnode = node_io.FileNode()
++ fnode.StartParsing(u'file', translations_node)
++ fnode.HandleAttribute('path', xtb_file)
++ fnode.HandleAttribute('lang', lang)
++ fnode.EndParsing()
++ translations_node.AddChild(fnode)
++ return fnode
++
++ def __CreateCppHeaderOutputNode(self, outputs_node, header_dir):
++ """Creates the <output> element corresponding to the generated c header."""
++ header_file_name = os.path.join(header_dir, self.name + '.h')
++ header_node = node_io.OutputNode()
++ header_node.StartParsing(u'output', outputs_node)
++ header_node.HandleAttribute('filename', header_file_name)
++ header_node.HandleAttribute('type', 'rc_header')
++ emit_node = node_io.EmitNode()
++ emit_node.StartParsing(u'emit', header_node)
++ emit_node.HandleAttribute('emit_type', 'prepend')
++ emit_node.EndParsing()
++ header_node.AddChild(emit_node)
++ header_node.EndParsing()
++ outputs_node.AddChild(header_node)
++ return header_node
++
++ def __CreateRcOutputNode(self, outputs_node, lang, rc_dir):
++ """Creates the <output> element corresponding to various rc file output."""
++ rc_file_name = self.name + '_' + lang + ".rc"
++ rc_path = os.path.join(rc_dir, rc_file_name)
++ node = node_io.OutputNode()
++ node.StartParsing(u'output', outputs_node)
++ node.HandleAttribute('filename', rc_path)
++ node.HandleAttribute('lang', lang)
++ node.HandleAttribute('type', 'rc_all')
++ node.EndParsing()
++ outputs_node.AddChild(node)
++ return node
++
++ def __CreateAndroidXmlOutputNode(self, outputs_node, locale, xml_res_dir):
++ """Creates the <output> element corresponding to various rc file output."""
++ # Need to check to see if the locale has a region, e.g. the GB in en-GB.
++ # When a locale has a region Android expects the region to be prefixed
++ # with an 'r'. For example for en-GB Android expects a values-en-rGB
++ # directory. Also, Android expects nb, tl, in, iw, ji as the language
++ # codes for Norwegian, Tagalog/Filipino, Indonesian, Hebrew, and Yiddish:
++ # http://developer.android.com/reference/java/util/Locale.html
++ if locale == 'es-419':
++ android_locale = 'es-rUS'
++ else:
++ android_lang, dash, region = locale.partition('-')
++ lang_map = {'no': 'nb', 'fil': 'tl', 'id': 'in', 'he': 'iw', 'yi': 'ji'}
++ android_lang = lang_map.get(android_lang, android_lang)
++ android_locale = android_lang + ('-r' + region if region else '')
++ values = 'values-' + android_locale if android_locale != 'en' else 'values'
++ xml_path = os.path.normpath(os.path.join(
++ xml_res_dir, values, 'strings.xml'))
++
++ node = node_io.OutputNode()
++ node.StartParsing(u'output', outputs_node)
++ node.HandleAttribute('filename', xml_path)
++ node.HandleAttribute('lang', locale)
++ node.HandleAttribute('type', 'android')
++ node.EndParsing()
++ outputs_node.AddChild(node)
++ return node
++
++ def IsTranslatable(self, android_string):
++ """Determines if a <string> element is a candidate for translation.
++
++ A <string> element is by default translatable unless otherwise marked.
++ """
++ if android_string.hasAttribute('translatable'):
++ value = android_string.getAttribute('translatable').lower()
++ if value not in ('true', 'false'):
++ print('Warning: translatable attribute has invalid value: %s' % value)
++ return value == 'true'
++ else:
++ return True
+diff --git a/tools/grit/grit/tool/android2grd_unittest.py b/tools/grit/grit/tool/android2grd_unittest.py
+new file mode 100644
+index 0000000000..a6934a707c
+--- /dev/null
++++ b/tools/grit/grit/tool/android2grd_unittest.py
+@@ -0,0 +1,181 @@
++#!/usr/bin/env python
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
-+#
-+# Hacky, primitive testing: This runs the style plugin for a set of input files
-+# and compares the output with golden result files.
-+
-+E_BADARGS=65
-+E_FAILEDTEST=1
-+
-+failed_any_test=
-+
-+# Prints usage information.
-+usage() {
-+ echo "Usage: $(basename "${0}")" \
-+ "<Path to the llvm build dir, usually Release+Asserts>"
-+ echo ""
-+ echo " Runs all the libFindBadConstructs unit tests"
-+ echo ""
-+}
-+
-+# Runs a single test case.
-+do_testcase() {
-+ local output="$("${CLANG_DIR}"/bin/clang -c -Wno-c++11-extensions \
-+ -Xclang -load -Xclang "${CLANG_DIR}"/lib/libFindBadConstructs.${LIB} \
-+ -Xclang -plugin -Xclang find-bad-constructs ${1} 2>&1)"
-+ local diffout="$(echo "${output}" | diff - "${2}")"
-+ if [ "${diffout}" = "" ]; then
-+ echo "PASS: ${1}"
-+ else
-+ failed_any_test=yes
-+ echo "FAIL: ${1}"
-+ echo "Output of compiler:"
-+ echo "${output}"
-+ echo "Expected output:"
-+ cat "${2}"
-+ echo
-+ fi
-+}
-+
-+# Validate input to the script.
-+if [[ -z "${1}" ]]; then
-+ usage
-+ exit ${E_BADARGS}
-+elif [[ ! -d "${1}" ]]; then
-+ echo "${1} is not a directory."
-+ usage
-+ exit ${E_BADARGS}
-+else
-+ export CLANG_DIR="${PWD}/${1}"
-+ echo "Using clang directory ${CLANG_DIR}..."
-+
-+ # The golden files assume that the cwd is this directory. To make the script
-+ # work no matter what the cwd is, explicitly cd to there.
-+ cd "$(dirname "${0}")"
-+
-+ if [ "$(uname -s)" = "Linux" ]; then
-+ export LIB=so
-+ elif [ "$(uname -s)" = "Darwin" ]; then
-+ export LIB=dylib
-+ fi
-+fi
-+
-+for input in *.cpp; do
-+ do_testcase "${input}" "${input%cpp}txt"
-+done
-+
-+if [[ "${failed_any_test}" ]]; then
-+ exit ${E_FAILEDTEST}
-+fi
-diff --git a/tools/clang/plugins/tests/virtual_methods.cpp b/tools/clang/plugins/tests/virtual_methods.cpp
-new file mode 100644
-index 0000000000..a07cbe4875
---- /dev/null
-+++ b/tools/clang/plugins/tests/virtual_methods.cpp
-@@ -0,0 +1,36 @@
-+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+// Use of this source code is governed by a BSD-style license that can be
-+// found in the LICENSE file.
-+
-+#include "virtual_methods.h"
-+
-+// Shouldn't warn about method usage in the implementation file.
-+class VirtualMethodsInImplementation {
-+ public:
-+ virtual void MethodIsAbstract() = 0;
-+ virtual void MethodHasNoArguments();
-+ virtual void MethodHasEmptyDefaultImpl() {}
-+ virtual bool ComplainAboutThis() { return true; }
-+};
+
-+// Stubs to fill in the abstract method
-+class ConcreteVirtualMethodsInHeaders : public VirtualMethodsInHeaders {
-+ public:
-+ virtual void MethodIsAbstract() override {}
-+};
++'''Unit tests for grit.tool.android2grd'''
+
-+class ConcreteVirtualMethodsInImplementation
-+ : public VirtualMethodsInImplementation {
-+ public:
-+ virtual void MethodIsAbstract() override {}
-+};
++from __future__ import print_function
++
++import os
++import sys
++if __name__ == '__main__':
++ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
++
++import unittest
++import xml.dom.minidom
++
++from grit import util
++from grit.node import empty
++from grit.node import message
++from grit.node import misc
++from grit.node import node_io
++from grit.tool import android2grd
++
++
++class Android2GrdUnittest(unittest.TestCase):
++
++ def __Parse(self, xml_string):
++ return xml.dom.minidom.parseString(xml_string).childNodes[0]
++
++ def testCreateTclibMessage(self):
++ tool = android2grd.Android2Grd()
++ msg = tool.CreateTclibMessage(self.__Parse(r'''
++ <string name="simple">A simple string</string>'''))
++ self.assertEqual(msg.GetRealContent(), 'A simple string')
++ msg = tool.CreateTclibMessage(self.__Parse(r'''
++ <string name="outer_whitespace">
++ Strip leading/trailing whitespace
++ </string>'''))
++ self.assertEqual(msg.GetRealContent(), 'Strip leading/trailing whitespace')
++ msg = tool.CreateTclibMessage(self.__Parse(r'''
++ <string name="inner_whitespace">Fold multiple spaces</string>'''))
++ self.assertEqual(msg.GetRealContent(), 'Fold multiple spaces')
++ msg = tool.CreateTclibMessage(self.__Parse(r'''
++ <string name="escaped_spaces">Retain \n escaped\t spaces</string>'''))
++ self.assertEqual(msg.GetRealContent(), 'Retain \n escaped\t spaces')
++ msg = tool.CreateTclibMessage(self.__Parse(r'''
++ <string name="quotes"> " Quotes preserve
++ whitespace" but only for "enclosed elements "
++ </string>'''))
++ self.assertEqual(msg.GetRealContent(), ''' Quotes preserve
++ whitespace but only for enclosed elements ''')
++ msg = tool.CreateTclibMessage(self.__Parse(
++ r'''<string name="escaped_characters">Escaped characters: \"\'\\\t\n'''
++ '</string>'))
++ self.assertEqual(msg.GetRealContent(), '''Escaped characters: "'\\\t\n''')
++ msg = tool.CreateTclibMessage(self.__Parse(
++ '<string xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2" '
++ 'name="placeholders">'
++ 'Open <xliff:g id="FILENAME" example="internet.html">%s</xliff:g>?'
++ '</string>'))
++ self.assertEqual(msg.GetRealContent(), 'Open %s?')
++ self.assertEqual(len(msg.GetPlaceholders()), 1)
++ self.assertEqual(msg.GetPlaceholders()[0].presentation, 'FILENAME')
++ self.assertEqual(msg.GetPlaceholders()[0].original, '%s')
++ self.assertEqual(msg.GetPlaceholders()[0].example, 'internet.html')
++ msg = tool.CreateTclibMessage(self.__Parse(r'''
++ <string name="comment">Contains a <!-- ignore this --> comment
++ </string>'''))
++ self.assertEqual(msg.GetRealContent(), 'Contains a comment')
++
++ def testIsTranslatable(self):
++ tool = android2grd.Android2Grd()
++ string_el = self.__Parse('<string>Hi</string>')
++ self.assertTrue(tool.IsTranslatable(string_el))
++ string_el = self.__Parse(
++ '<string translatable="true">Hi</string>')
++ self.assertTrue(tool.IsTranslatable(string_el))
++ string_el = self.__Parse(
++ '<string translatable="false">Hi</string>')
++ self.assertFalse(tool.IsTranslatable(string_el))
++
++ def __ParseAndroidXml(self, options = []):
++ tool = android2grd.Android2Grd()
++
++ tool.ParseOptions(options)
++
++ android_path = util.PathFromRoot('grit/testdata/android.xml')
++ with open(android_path) as android_file:
++ android_dom = xml.dom.minidom.parse(android_file)
++
++ grd = tool.AndroidDomToGrdDom(android_dom)
++ self.assertTrue(isinstance(grd, misc.GritNode))
++
++ return grd
++
++ def testAndroidDomToGrdDom(self):
++ grd = self.__ParseAndroidXml(['--languages', 'en-US,en-GB,ru'])
++
++ # Check that the structure of the GritNode is as expected.
++ messages = grd.GetChildrenOfType(message.MessageNode)
++ translations = grd.GetChildrenOfType(empty.TranslationsNode)
++ files = grd.GetChildrenOfType(node_io.FileNode)
++
++ self.assertEqual(len(translations), 1)
++ self.assertEqual(len(files), 3)
++ self.assertEqual(len(messages), 5)
++
++ # Check that a message node is constructed correctly.
++ msg = [x for x in messages if x.GetTextualIds()[0] == 'IDS_PLACEHOLDERS']
++ self.assertTrue(msg)
++ msg = msg[0]
++
++ self.assertTrue(msg.IsTranslateable())
++ self.assertEqual(msg.attrs["desc"], "A string with placeholder.")
++
++ def testTranslatableAttribute(self):
++ grd = self.__ParseAndroidXml([])
++ messages = grd.GetChildrenOfType(message.MessageNode)
++ msgs = [x for x in messages if x.GetTextualIds()[0] == 'IDS_CONSTANT']
++ self.assertTrue(msgs)
++ self.assertFalse(msgs[0].IsTranslateable())
++
++ def testTranslations(self):
++ grd = self.__ParseAndroidXml(['--languages', 'en-US,en-GB,ru,id'])
++
++ files = grd.GetChildrenOfType(node_io.FileNode)
++ us_file = [x for x in files if x.attrs['lang'] == 'en-US']
++ self.assertTrue(us_file)
++ self.assertEqual(us_file[0].GetInputPath(),
++ 'chrome_android_strings_en-US.xtb')
++
++ id_file = [x for x in files if x.attrs['lang'] == 'id']
++ self.assertTrue(id_file)
++ self.assertEqual(id_file[0].GetInputPath(),
++ 'chrome_android_strings_id.xtb')
++
++ def testOutputs(self):
++ grd = self.__ParseAndroidXml(['--languages', 'en-US,ru,id',
++ '--rc-dir', 'rc/dir',
++ '--header-dir', 'header/dir',
++ '--xtb-dir', 'xtb/dir',
++ '--xml-dir', 'xml/dir'])
++
++ outputs = grd.GetChildrenOfType(node_io.OutputNode)
++ self.assertEqual(len(outputs), 7)
++
++ header_outputs = [x for x in outputs if x.GetType() == 'rc_header']
++ rc_outputs = [x for x in outputs if x.GetType() == 'rc_all']
++ xml_outputs = [x for x in outputs if x.GetType() == 'android']
++
++ self.assertEqual(len(header_outputs), 1)
++ self.assertEqual(len(rc_outputs), 3)
++ self.assertEqual(len(xml_outputs), 3)
++
++ # The header node should have an "<emit>" child and the proper filename.
++ self.assertTrue(header_outputs[0].GetChildrenOfType(node_io.EmitNode))
++ self.assertEqual(util.normpath(header_outputs[0].GetFilename()),
++ util.normpath('header/dir/chrome_android_strings.h'))
++
++ id_rc = [x for x in rc_outputs if x.GetLanguage() == 'id']
++ id_xml = [x for x in xml_outputs if x.GetLanguage() == 'id']
++ self.assertTrue(id_rc)
++ self.assertTrue(id_xml)
++ self.assertEqual(util.normpath(id_rc[0].GetFilename()),
++ util.normpath('rc/dir/chrome_android_strings_id.rc'))
++ self.assertEqual(util.normpath(id_xml[0].GetFilename()),
++ util.normpath('xml/dir/values-in/strings.xml'))
++
++ us_rc = [x for x in rc_outputs if x.GetLanguage() == 'en-US']
++ us_xml = [x for x in xml_outputs if x.GetLanguage() == 'en-US']
++ self.assertTrue(us_rc)
++ self.assertTrue(us_xml)
++ self.assertEqual(util.normpath(us_rc[0].GetFilename()),
++ util.normpath('rc/dir/chrome_android_strings_en-US.rc'))
++ self.assertEqual(util.normpath(us_xml[0].GetFilename()),
++ util.normpath('xml/dir/values-en-rUS/strings.xml'))
++
++
++if __name__ == '__main__':
++ unittest.main()
+diff --git a/tools/grit/grit/tool/build.py b/tools/grit/grit/tool/build.py
+new file mode 100644
+index 0000000000..204592bf0d
+--- /dev/null
++++ b/tools/grit/grit/tool/build.py
+@@ -0,0 +1,556 @@
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++'''The 'grit build' tool.
++'''
++
++from __future__ import print_function
+
-+// Fill in the implementations
-+void VirtualMethodsInHeaders::MethodHasNoArguments() {}
-+void WarnOnMissingVirtual::MethodHasNoArguments() {}
-+void VirtualMethodsInImplementation::MethodHasNoArguments() {}
++import codecs
++import filecmp
++import getopt
++import gzip
++import os
++import shutil
++import sys
+
-+int main() {
-+ ConcreteVirtualMethodsInHeaders one;
-+ ConcreteVirtualMethodsInImplementation two;
++import six
++
++from grit import grd_reader
++from grit import shortcuts
++from grit import util
++from grit.format import minifier
++from grit.node import brotli_util
++from grit.node import include
++from grit.node import message
++from grit.node import structure
++from grit.tool import interface
++
++
++# It would be cleaner to have each module register itself, but that would
++# require importing all of them on every run of GRIT.
++'''Map from <output> node types to modules under grit.format.'''
++_format_modules = {
++ 'android': 'android_xml',
++ 'c_format': 'c_format',
++ 'chrome_messages_json': 'chrome_messages_json',
++ 'chrome_messages_json_gzip': 'chrome_messages_json',
++ 'data_package': 'data_pack',
++ 'policy_templates': 'policy_templates_json',
++ 'rc_all': 'rc',
++ 'rc_header': 'rc_header',
++ 'rc_nontranslateable': 'rc',
++ 'rc_translateable': 'rc',
++ 'resource_file_map_source': 'resource_map',
++ 'resource_map_header': 'resource_map',
++ 'resource_map_source': 'resource_map',
+}
-diff --git a/tools/clang/plugins/tests/virtual_methods.h b/tools/clang/plugins/tests/virtual_methods.h
++
++def GetFormatter(type):
++ modulename = 'grit.format.' + _format_modules[type]
++ __import__(modulename)
++ module = sys.modules[modulename]
++ try:
++ return module.Format
++ except AttributeError:
++ return module.GetFormatter(type)
++
++
++class RcBuilder(interface.Tool):
++ '''A tool that builds RC files and resource header files for compilation.
++
++Usage: grit build [-o OUTPUTDIR] [-D NAME[=VAL]]*
++
++All output options for this tool are specified in the input file (see
++'grit help' for details on how to specify the input file - it is a global
++option).
++
++Options:
++
++ -a FILE Assert that the given file is an output. There can be
++ multiple "-a" flags listed for multiple outputs. If a "-a"
++ or "--assert-file-list" argument is present, then the list
++ of asserted files must match the output files or the tool
++ will fail. The use-case is for the build system to maintain
++ separate lists of output files and to catch errors if the
++ build system's list and the grit list are out-of-sync.
++
++ --assert-file-list Provide a file listing multiple asserted output files.
++ There is one file name per line. This acts like specifying
++ each file with "-a" on the command line, but without the
++ possibility of running into OS line-length limits for very
++ long lists.
++
++ -o OUTPUTDIR Specify what directory output paths are relative to.
++ Defaults to the current directory.
++
++ -p FILE Specify a file containing a pre-determined mapping from
++ resource names to resource ids which will be used to assign
++ resource ids to those resources. Resources not found in this
++ file will be assigned ids normally. The motivation is to run
++ your app's startup and have it dump the resources it loads,
++ and then pass these via this flag. This will pack startup
++ resources together, thus reducing paging while all other
++ resources are unperturbed. The file should have the format:
++ RESOURCE_ONE_NAME 123
++ RESOURCE_TWO_NAME 124
++
++ -D NAME[=VAL] Specify a C-preprocessor-like define NAME with optional
++ value VAL (defaults to 1) which will be used to control
++ conditional inclusion of resources.
++
++ -E NAME=VALUE Set environment variable NAME to VALUE (within grit).
++
++ -f FIRSTIDSFILE Path to a python file that specifies the first id of
++ value to use for resources. A non-empty value here will
++ override the value specified in the <grit> node's
++ first_ids_file.
++
++ -w WHITELISTFILE Path to a file containing the string names of the
++ resources to include. Anything not listed is dropped.
++
++ -t PLATFORM Specifies the platform the build is targeting; defaults
++ to the value of sys.platform. The value provided via this
++ flag should match what sys.platform would report for your
++ target platform; see grit.node.base.EvaluateCondition.
++
++ --whitelist-support
++ Generate code to support extracting a resource whitelist
++ from executables.
++
++ --write-only-new flag
++ If flag is non-0, write output files to a temporary file
++ first, and copy it to the real output only if the new file
++ is different from the old file. This allows some build
++ systems to realize that dependent build steps might be
++ unnecessary, at the cost of comparing the output data at
++ grit time.
++
++ --depend-on-stamp
++ If specified along with --depfile and --depdir, the depfile
++ generated will depend on a stampfile instead of the first
++ output in the input .grd file.
++
++ --js-minifier A command to run the Javascript minifier. If not set then
++ Javascript won't be minified. The command should read the
++ original Javascript from standard input, and output the
++ minified Javascript to standard output. A non-zero exit
++ status will be taken as indicating failure.
++
++ --css-minifier A command to run the CSS minifier. If not set then CSS won't
++ be minified. The command should read the original CSS from
++ standard input, and output the minified CSS to standard
++ output. A non-zero exit status will be taken as indicating
++ failure.
++
++ --brotli The full path to the brotli executable generated by
++ third_party/brotli/BUILD.gn, required if any entries use
++ compress="brotli".
++
++Conditional inclusion of resources only affects the output of files which
++control which resources get linked into a binary, e.g. it affects .rc files
++meant for compilation but it does not affect resource header files (that define
++IDs). This helps ensure that values of IDs stay the same, that all messages
++are exported to translation interchange files (e.g. XMB files), etc.
++'''
++
++ def ShortDescription(self):
++ return 'A tool that builds RC files for compilation.'
++
++ def Run(self, opts, args):
++ brotli_util.SetBrotliCommand(None)
++ os.environ['cwd'] = os.getcwd()
++ self.output_directory = '.'
++ first_ids_file = None
++ predetermined_ids_file = None
++ whitelist_filenames = []
++ assert_output_files = []
++ target_platform = None
++ depfile = None
++ depdir = None
++ whitelist_support = False
++ write_only_new = False
++ depend_on_stamp = False
++ js_minifier = None
++ css_minifier = None
++ replace_ellipsis = True
++ (own_opts, args) = getopt.getopt(
++ args, 'a:p:o:D:E:f:w:t:',
++ ('depdir=', 'depfile=', 'assert-file-list=', 'help',
++ 'output-all-resource-defines', 'no-output-all-resource-defines',
++ 'no-replace-ellipsis', 'depend-on-stamp', 'js-minifier=',
++ 'css-minifier=', 'write-only-new=', 'whitelist-support', 'brotli='))
++ for (key, val) in own_opts:
++ if key == '-a':
++ assert_output_files.append(val)
++ elif key == '--assert-file-list':
++ with open(val) as f:
++ assert_output_files += f.read().splitlines()
++ elif key == '-o':
++ self.output_directory = val
++ elif key == '-D':
++ name, val = util.ParseDefine(val)
++ self.defines[name] = val
++ elif key == '-E':
++ (env_name, env_value) = val.split('=', 1)
++ os.environ[env_name] = env_value
++ elif key == '-f':
++ # TODO(joi@chromium.org): Remove this override once change
++ # lands in WebKit.grd to specify the first_ids_file in the
++ # .grd itself.
++ first_ids_file = val
++ elif key == '-w':
++ whitelist_filenames.append(val)
++ elif key == '--no-replace-ellipsis':
++ replace_ellipsis = False
++ elif key == '-p':
++ predetermined_ids_file = val
++ elif key == '-t':
++ target_platform = val
++ elif key == '--depdir':
++ depdir = val
++ elif key == '--depfile':
++ depfile = val
++ elif key == '--write-only-new':
++ write_only_new = val != '0'
++ elif key == '--depend-on-stamp':
++ depend_on_stamp = True
++ elif key == '--js-minifier':
++ js_minifier = val
++ elif key == '--css-minifier':
++ css_minifier = val
++ elif key == '--whitelist-support':
++ whitelist_support = True
++ elif key == '--brotli':
++ brotli_util.SetBrotliCommand([os.path.abspath(val)])
++ elif key == '--help':
++ self.ShowUsage()
++ sys.exit(0)
++
++ if len(args):
++ print('This tool takes no tool-specific arguments.')
++ return 2
++ self.SetOptions(opts)
++ self.VerboseOut('Output directory: %s (absolute path: %s)\n' %
++ (self.output_directory,
++ os.path.abspath(self.output_directory)))
++
++ if whitelist_filenames:
++ self.whitelist_names = set()
++ for whitelist_filename in whitelist_filenames:
++ self.VerboseOut('Using whitelist: %s\n' % whitelist_filename);
++ whitelist_contents = util.ReadFile(whitelist_filename, 'utf-8')
++ self.whitelist_names.update(whitelist_contents.strip().split('\n'))
++
++ if js_minifier:
++ minifier.SetJsMinifier(js_minifier)
++
++ if css_minifier:
++ minifier.SetCssMinifier(css_minifier)
++
++ self.write_only_new = write_only_new
++
++ self.res = grd_reader.Parse(opts.input,
++ debug=opts.extra_verbose,
++ first_ids_file=first_ids_file,
++ predetermined_ids_file=predetermined_ids_file,
++ defines=self.defines,
++ target_platform=target_platform)
++
++ # Set an output context so that conditionals can use defines during the
++ # gathering stage; we use a dummy language here since we are not outputting
++ # a specific language.
++ self.res.SetOutputLanguage('en')
++ self.res.SetWhitelistSupportEnabled(whitelist_support)
++ self.res.RunGatherers()
++
++ # Replace ... with the single-character version. http://crbug.com/621772
++ if replace_ellipsis:
++ for node in self.res:
++ if isinstance(node, message.MessageNode):
++ node.SetReplaceEllipsis(True)
++
++ self.Process()
++
++ if assert_output_files:
++ if not self.CheckAssertedOutputFiles(assert_output_files):
++ return 2
++
++ if depfile and depdir:
++ self.GenerateDepfile(depfile, depdir, first_ids_file, depend_on_stamp)
++
++ return 0
++
++ def __init__(self, defines=None):
++ # Default file-creation function is codecs.open(). Only done to allow
++ # overriding by unit test.
++ self.fo_create = codecs.open
++
++ # key/value pairs of C-preprocessor like defines that are used for
++ # conditional output of resources
++ self.defines = defines or {}
++
++ # self.res is a fully-populated resource tree if Run()
++ # has been called, otherwise None.
++ self.res = None
++
++ # The set of names that are whitelisted to actually be included in the
++ # output.
++ self.whitelist_names = None
++
++ # Whether to compare outputs to their old contents before writing.
++ self.write_only_new = False
++
++ @staticmethod
++ def AddWhitelistTags(start_node, whitelist_names):
++ # Walk the tree of nodes added attributes for the nodes that shouldn't
++ # be written into the target files (skip markers).
++ for node in start_node:
++ # Same trick data_pack.py uses to see what nodes actually result in
++ # real items.
++ if (isinstance(node, include.IncludeNode) or
++ isinstance(node, message.MessageNode) or
++ isinstance(node, structure.StructureNode)):
++ text_ids = node.GetTextualIds()
++ # Mark the item to be skipped if it wasn't in the whitelist.
++ if text_ids and text_ids[0] not in whitelist_names:
++ node.SetWhitelistMarkedAsSkip(True)
++
++ @staticmethod
++ def ProcessNode(node, output_node, outfile):
++ '''Processes a node in-order, calling its formatter before and after
++ recursing to its children.
++
++ Args:
++ node: grit.node.base.Node subclass
++ output_node: grit.node.io.OutputNode
++ outfile: open filehandle
++ '''
++ base_dir = util.dirname(output_node.GetOutputFilename())
++
++ formatter = GetFormatter(output_node.GetType())
++ formatted = formatter(node, output_node.GetLanguage(), output_dir=base_dir)
++ # NB: Formatters may be generators or return lists. The writelines API
++ # accepts iterables as a shortcut to calling write directly. That means
++ # you can pass strings (iteration yields characters), but not bytes (as
++ # iteration yields integers). Python 2 worked due to its quirks with
++ # bytes/string implementation, but Python 3 fails. It's also a bit more
++ # inefficient to call write once per character/byte. Handle all of this
++ # ourselves by calling write directly on strings/bytes before falling back
++ # to writelines.
++ if isinstance(formatted, (six.string_types, six.binary_type)):
++ outfile.write(formatted)
++ else:
++ outfile.writelines(formatted)
++ if output_node.GetType() == 'data_package':
++ with open(output_node.GetOutputFilename() + '.info', 'w') as infofile:
++ if node.info:
++ # We terminate with a newline so that when these files are
++ # concatenated later we consistently terminate with a newline so
++ # consumers can account for terminating newlines.
++ infofile.writelines(['\n'.join(node.info), '\n'])
++
++ @staticmethod
++ def _EncodingForOutputType(output_type):
++ # Microsoft's RC compiler can only deal with single-byte or double-byte
++ # files (no UTF-8), so we make all RC files UTF-16 to support all
++ # character sets.
++ if output_type in ('rc_header', 'resource_file_map_source',
++ 'resource_map_header', 'resource_map_source'):
++ return 'cp1252'
++ if output_type in ('android', 'c_format', 'plist', 'plist_strings', 'doc',
++ 'json', 'android_policy', 'chrome_messages_json',
++ 'chrome_messages_json_gzip', 'policy_templates'):
++ return 'utf_8'
++ # TODO(gfeher) modify here to set utf-8 encoding for admx/adml
++ return 'utf_16'
++
++ def Process(self):
++ for output in self.res.GetOutputFiles():
++ output.output_filename = os.path.abspath(os.path.join(
++ self.output_directory, output.GetOutputFilename()))
++
++ # If there are whitelisted names, tag the tree once up front, this way
++ # while looping through the actual output, it is just an attribute check.
++ if self.whitelist_names:
++ self.AddWhitelistTags(self.res, self.whitelist_names)
++
++ for output in self.res.GetOutputFiles():
++ self.VerboseOut('Creating %s...' % output.GetOutputFilename())
++
++ # Set the context, for conditional inclusion of resources
++ self.res.SetOutputLanguage(output.GetLanguage())
++ self.res.SetOutputContext(output.GetContext())
++ self.res.SetFallbackToDefaultLayout(output.GetFallbackToDefaultLayout())
++ self.res.SetDefines(self.defines)
++
++ # Assign IDs only once to ensure that all outputs use the same IDs.
++ if self.res.GetIdMap() is None:
++ self.res.InitializeIds()
++
++ # Make the output directory if it doesn't exist.
++ self.MakeDirectoriesTo(output.GetOutputFilename())
++
++ # Write the results to a temporary file and only overwrite the original
++ # if the file changed. This avoids unnecessary rebuilds.
++ out_filename = output.GetOutputFilename()
++ tmp_filename = out_filename + '.tmp'
++ tmpfile = self.fo_create(tmp_filename, 'wb')
++
++ output_type = output.GetType()
++ if output_type != 'data_package':
++ encoding = self._EncodingForOutputType(output_type)
++ tmpfile = util.WrapOutputStream(tmpfile, encoding)
++
++ # Iterate in-order through entire resource tree, calling formatters on
++ # the entry into a node and on exit out of it.
++ with tmpfile:
++ self.ProcessNode(self.res, output, tmpfile)
++
++ if output_type == 'chrome_messages_json_gzip':
++ gz_filename = tmp_filename + '.gz'
++ with open(tmp_filename, 'rb') as tmpfile, open(gz_filename, 'wb') as f:
++ with gzip.GzipFile(filename='', mode='wb', fileobj=f, mtime=0) as fgz:
++ shutil.copyfileobj(tmpfile, fgz)
++ os.remove(tmp_filename)
++ tmp_filename = gz_filename
++
++ # Now copy from the temp file back to the real output, but on Windows,
++ # only if the real output doesn't exist or the contents of the file
++ # changed. This prevents identical headers from being written and .cc
++ # files from recompiling (which is painful on Windows).
++ if not os.path.exists(out_filename):
++ os.rename(tmp_filename, out_filename)
++ else:
++ # CHROMIUM SPECIFIC CHANGE.
++ # This clashes with gyp + vstudio, which expect the output timestamp
++ # to change on a rebuild, even if nothing has changed, so only do
++ # it when opted in.
++ if not self.write_only_new:
++ write_file = True
++ else:
++ files_match = filecmp.cmp(out_filename, tmp_filename)
++ write_file = not files_match
++ if write_file:
++ shutil.copy2(tmp_filename, out_filename)
++ os.remove(tmp_filename)
++
++ self.VerboseOut(' done.\n')
++
++ # Print warnings if there are any duplicate shortcuts.
++ warnings = shortcuts.GenerateDuplicateShortcutsWarnings(
++ self.res.UberClique(), self.res.GetTcProject())
++ if warnings:
++ print('\n'.join(warnings))
++
++ # Print out any fallback warnings, and missing translation errors, and
++ # exit with an error code if there are missing translations in a non-pseudo
++ # and non-official build.
++ warnings = (self.res.UberClique().MissingTranslationsReport().
++ encode('ascii', 'replace'))
++ if warnings:
++ self.VerboseOut(warnings)
++ if self.res.UberClique().HasMissingTranslations():
++ print(self.res.UberClique().missing_translations_)
++ sys.exit(-1)
++
++
++ def CheckAssertedOutputFiles(self, assert_output_files):
++ '''Checks that the asserted output files are specified in the given list.
++
++ Returns true if the asserted files are present. If they are not, returns
++ False and prints the failure.
++ '''
++ # Compare the absolute path names, sorted.
++ asserted = sorted([os.path.abspath(i) for i in assert_output_files])
++ actual = sorted([
++ os.path.abspath(os.path.join(self.output_directory,
++ i.GetOutputFilename()))
++ for i in self.res.GetOutputFiles()])
++
++ if asserted != actual:
++ missing = list(set(asserted) - set(actual))
++ extra = list(set(actual) - set(asserted))
++ error = '''Asserted file list does not match.
++
++Expected output files:
++%s
++Actual output files:
++%s
++Missing output files:
++%s
++Extra output files:
++%s
++'''
++ print(error % ('\n'.join(asserted), '\n'.join(actual), '\n'.join(missing),
++ ' \n'.join(extra)))
++ return False
++ return True
++
++
++ def GenerateDepfile(self, depfile, depdir, first_ids_file, depend_on_stamp):
++ '''Generate a depfile that contains the imlicit dependencies of the input
++ grd. The depfile will be in the same format as a makefile, and will contain
++ references to files relative to |depdir|. It will be put in |depfile|.
++
++ For example, supposing we have three files in a directory src/
++
++ src/
++ blah.grd <- depends on input{1,2}.xtb
++ input1.xtb
++ input2.xtb
++
++ and we run
++
++ grit -i blah.grd -o ../out/gen \
++ --depdir ../out \
++ --depfile ../out/gen/blah.rd.d
++
++ from the directory src/ we will generate a depfile ../out/gen/blah.grd.d
++ that has the contents
++
++ gen/blah.h: ../src/input1.xtb ../src/input2.xtb
++
++ Where "gen/blah.h" is the first output (Ninja expects the .d file to list
++ the first output in cases where there is more than one). If the flag
++ --depend-on-stamp is specified, "gen/blah.rd.d.stamp" will be used that is
++ 'touched' whenever a new depfile is generated.
++
++ Note that all paths in the depfile are relative to ../out, the depdir.
++ '''
++ depfile = os.path.abspath(depfile)
++ depdir = os.path.abspath(depdir)
++ infiles = self.res.GetInputFiles()
++
++ # We want to trigger a rebuild if the first ids change.
++ if first_ids_file is not None:
++ infiles.append(first_ids_file)
++
++ if (depend_on_stamp):
++ output_file = depfile + ".stamp"
++ # Touch the stamp file before generating the depfile.
++ with open(output_file, 'a'):
++ os.utime(output_file, None)
++ else:
++ # Get the first output file relative to the depdir.
++ outputs = self.res.GetOutputFiles()
++ output_file = os.path.join(self.output_directory,
++ outputs[0].GetOutputFilename())
++
++ output_file = os.path.relpath(output_file, depdir)
++ # The path prefix to prepend to dependencies in the depfile.
++ prefix = os.path.relpath(os.getcwd(), depdir)
++ deps_text = ' '.join([os.path.join(prefix, i) for i in infiles])
++
++ depfile_contents = output_file + ': ' + deps_text
++ self.MakeDirectoriesTo(depfile)
++ outfile = self.fo_create(depfile, 'w', encoding='utf-8')
++ outfile.write(depfile_contents)
++
++ @staticmethod
++ def MakeDirectoriesTo(file):
++ '''Creates directories necessary to contain |file|.'''
++ dir = os.path.split(file)[0]
++ if not os.path.exists(dir):
++ os.makedirs(dir)
+diff --git a/tools/grit/grit/tool/build_unittest.py b/tools/grit/grit/tool/build_unittest.py
new file mode 100644
-index 0000000000..d9fbf96ed3
+index 0000000000..c4a2f2752b
--- /dev/null
-+++ b/tools/clang/plugins/tests/virtual_methods.h
-@@ -0,0 +1,39 @@
-+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
-+// Use of this source code is governed by a BSD-style license that can be
-+// found in the LICENSE file.
-+
-+#ifndef VIRTUAL_METHODS_H_
-+#define VIRTUAL_METHODS_H_
-+
-+// Should warn about virtual method usage.
-+class VirtualMethodsInHeaders {
-+ public:
-+ // Don't complain about these.
-+ virtual void MethodIsAbstract() = 0;
-+ virtual void MethodHasNoArguments();
-+ virtual void MethodHasEmptyDefaultImpl() {}
-+
-+ // But complain about this:
-+ virtual bool ComplainAboutThis() { return true; }
-+};
++++ b/tools/grit/grit/tool/build_unittest.py
+@@ -0,0 +1,341 @@
++#!/usr/bin/env python
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
+
-+// Complain on missing 'virtual' keyword in overrides.
-+class WarnOnMissingVirtual : public VirtualMethodsInHeaders {
-+ public:
-+ void MethodHasNoArguments() override;
-+};
++'''Unit tests for the 'grit build' tool.
++'''
+
-+// Don't complain about things in a 'testing' namespace.
-+namespace testing {
-+struct TestStruct {};
-+} // namespace testing
-+
-+class VirtualMethodsInHeadersTesting : public VirtualMethodsInHeaders {
-+ public:
-+ // Don't complain about no virtual testing methods.
-+ void MethodHasNoArguments();
-+ private:
-+ testing::TestStruct tester_;
-+};
++from __future__ import print_function
++
++import codecs
++import os
++import sys
++if __name__ == '__main__':
++ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
+
-+#endif // VIRTUAL_METHODS_H_
-diff --git a/tools/clang/plugins/tests/virtual_methods.txt b/tools/clang/plugins/tests/virtual_methods.txt
++import unittest
++
++from grit import util
++from grit.tool import build
++
++
++class BuildUnittest(unittest.TestCase):
++
++ # IDs should not change based on whitelisting.
++ # Android WebView currently relies on this.
++ EXPECTED_ID_MAP = {
++ 'IDS_MESSAGE_WHITELISTED': 6889,
++ 'IDR_STRUCTURE_WHITELISTED': 11546,
++ 'IDR_STRUCTURE_IN_TRUE_IF_WHITELISTED': 11548,
++ 'IDR_INCLUDE_WHITELISTED': 15601,
++ }
++
++ def testFindTranslationsWithSubstitutions(self):
++ # This is a regression test; we had a bug where GRIT would fail to find
++ # messages with substitutions e.g. "Hello [IDS_USER]" where IDS_USER is
++ # another <message>.
++ output_dir = util.TempDir({})
++ builder = build.RcBuilder()
++ class DummyOpts(object):
++ def __init__(self):
++ self.input = util.PathFromRoot('grit/testdata/substitute.grd')
++ self.verbose = False
++ self.extra_verbose = False
++ builder.Run(DummyOpts(), ['-o', output_dir.GetPath()])
++ output_dir.CleanUp()
++
++ def testGenerateDepFile(self):
++ output_dir = util.TempDir({})
++ builder = build.RcBuilder()
++ class DummyOpts(object):
++ def __init__(self):
++ self.input = util.PathFromRoot('grit/testdata/depfile.grd')
++ self.verbose = False
++ self.extra_verbose = False
++ expected_dep_file = output_dir.GetPath('substitute.grd.d')
++ builder.Run(DummyOpts(), ['-o', output_dir.GetPath(),
++ '--depdir', output_dir.GetPath(),
++ '--depfile', expected_dep_file])
++
++ self.failUnless(os.path.isfile(expected_dep_file))
++ with open(expected_dep_file) as f:
++ line = f.readline()
++ (dep_output_file, deps_string) = line.split(': ')
++ deps = deps_string.split(' ')
++
++ self.failUnlessEqual("default_100_percent.pak", dep_output_file)
++ self.failUnlessEqual(deps, [
++ util.PathFromRoot('grit/testdata/default_100_percent/a.png'),
++ util.PathFromRoot('grit/testdata/grit_part.grdp'),
++ util.PathFromRoot('grit/testdata/special_100_percent/a.png'),
++ ])
++ output_dir.CleanUp()
++
++ def testGenerateDepFileWithResourceIds(self):
++ output_dir = util.TempDir({})
++ builder = build.RcBuilder()
++ class DummyOpts(object):
++ def __init__(self):
++ self.input = util.PathFromRoot('grit/testdata/substitute_no_ids.grd')
++ self.verbose = False
++ self.extra_verbose = False
++ expected_dep_file = output_dir.GetPath('substitute_no_ids.grd.d')
++ builder.Run(DummyOpts(),
++ ['-f', util.PathFromRoot('grit/testdata/resource_ids'),
++ '-o', output_dir.GetPath(),
++ '--depdir', output_dir.GetPath(),
++ '--depfile', expected_dep_file])
++
++ self.failUnless(os.path.isfile(expected_dep_file))
++ with open(expected_dep_file) as f:
++ line = f.readline()
++ (dep_output_file, deps_string) = line.split(': ')
++ deps = deps_string.split(' ')
++
++ self.failUnlessEqual("resource.h", dep_output_file)
++ self.failUnlessEqual(2, len(deps))
++ self.failUnlessEqual(deps[0],
++ util.PathFromRoot('grit/testdata/substitute.xmb'))
++ self.failUnlessEqual(deps[1],
++ util.PathFromRoot('grit/testdata/resource_ids'))
++ output_dir.CleanUp()
++
++ def testAssertOutputs(self):
++ output_dir = util.TempDir({})
++ class DummyOpts(object):
++ def __init__(self):
++ self.input = util.PathFromRoot('grit/testdata/substitute.grd')
++ self.verbose = False
++ self.extra_verbose = False
++
++ # Incomplete output file list should fail.
++ builder_fail = build.RcBuilder()
++ self.failUnlessEqual(2,
++ builder_fail.Run(DummyOpts(), [
++ '-o', output_dir.GetPath(),
++ '-a', os.path.abspath(
++ output_dir.GetPath('en_generated_resources.rc'))]))
++
++ # Complete output file list should succeed.
++ builder_ok = build.RcBuilder()
++ self.failUnlessEqual(0,
++ builder_ok.Run(DummyOpts(), [
++ '-o', output_dir.GetPath(),
++ '-a', os.path.abspath(
++ output_dir.GetPath('en_generated_resources.rc')),
++ '-a', os.path.abspath(
++ output_dir.GetPath('sv_generated_resources.rc')),
++ '-a', os.path.abspath(output_dir.GetPath('resource.h'))]))
++ output_dir.CleanUp()
++
++ def testAssertTemplateOutputs(self):
++ output_dir = util.TempDir({})
++ class DummyOpts(object):
++ def __init__(self):
++ self.input = util.PathFromRoot('grit/testdata/substitute_tmpl.grd')
++ self.verbose = False
++ self.extra_verbose = False
++
++ # Incomplete output file list should fail.
++ builder_fail = build.RcBuilder()
++ self.failUnlessEqual(2,
++ builder_fail.Run(DummyOpts(), [
++ '-o', output_dir.GetPath(),
++ '-E', 'name=foo',
++ '-a', os.path.abspath(output_dir.GetPath('en_foo_resources.rc'))]))
++
++ # Complete output file list should succeed.
++ builder_ok = build.RcBuilder()
++ self.failUnlessEqual(0,
++ builder_ok.Run(DummyOpts(), [
++ '-o', output_dir.GetPath(),
++ '-E', 'name=foo',
++ '-a', os.path.abspath(output_dir.GetPath('en_foo_resources.rc')),
++ '-a', os.path.abspath(output_dir.GetPath('sv_foo_resources.rc')),
++ '-a', os.path.abspath(output_dir.GetPath('resource.h'))]))
++ output_dir.CleanUp()
++
++ def _verifyWhitelistedOutput(self,
++ filename,
++ whitelisted_ids,
++ non_whitelisted_ids,
++ encoding='utf8'):
++ self.failUnless(os.path.exists(filename))
++ whitelisted_ids_found = []
++ non_whitelisted_ids_found = []
++ with codecs.open(filename, encoding=encoding) as f:
++ for line in f.readlines():
++ for whitelisted_id in whitelisted_ids:
++ if whitelisted_id in line:
++ whitelisted_ids_found.append(whitelisted_id)
++ if filename.endswith('.h'):
++ numeric_id = int(line.split()[2])
++ expected_numeric_id = self.EXPECTED_ID_MAP.get(whitelisted_id)
++ self.assertEqual(
++ expected_numeric_id, numeric_id,
++ 'Numeric ID for {} was {} should be {}'.format(
++ whitelisted_id, numeric_id, expected_numeric_id))
++ for non_whitelisted_id in non_whitelisted_ids:
++ if non_whitelisted_id in line:
++ non_whitelisted_ids_found.append(non_whitelisted_id)
++ self.longMessage = True
++ self.assertEqual(whitelisted_ids,
++ whitelisted_ids_found,
++ '\nin file {}'.format(os.path.basename(filename)))
++ non_whitelisted_msg = ('Non-Whitelisted IDs {} found in {}'
++ .format(non_whitelisted_ids_found, os.path.basename(filename)))
++ self.assertFalse(non_whitelisted_ids_found, non_whitelisted_msg)
++
++ def testWhitelistStrings(self):
++ output_dir = util.TempDir({})
++ builder = build.RcBuilder()
++ class DummyOpts(object):
++ def __init__(self):
++ self.input = util.PathFromRoot('grit/testdata/whitelist_strings.grd')
++ self.verbose = False
++ self.extra_verbose = False
++ whitelist_file = util.PathFromRoot('grit/testdata/whitelist.txt')
++ builder.Run(DummyOpts(), ['-o', output_dir.GetPath(),
++ '-w', whitelist_file])
++ header = output_dir.GetPath('whitelist_test_resources.h')
++ rc = output_dir.GetPath('en_whitelist_test_strings.rc')
++
++ whitelisted_ids = ['IDS_MESSAGE_WHITELISTED']
++ non_whitelisted_ids = ['IDS_MESSAGE_NOT_WHITELISTED']
++ self._verifyWhitelistedOutput(
++ header,
++ whitelisted_ids,
++ non_whitelisted_ids,
++ )
++ self._verifyWhitelistedOutput(
++ rc,
++ whitelisted_ids,
++ non_whitelisted_ids,
++ encoding='utf16'
++ )
++ output_dir.CleanUp()
++
++ def testWhitelistResources(self):
++ output_dir = util.TempDir({})
++ builder = build.RcBuilder()
++ class DummyOpts(object):
++ def __init__(self):
++ self.input = util.PathFromRoot('grit/testdata/whitelist_resources.grd')
++ self.verbose = False
++ self.extra_verbose = False
++ whitelist_file = util.PathFromRoot('grit/testdata/whitelist.txt')
++ builder.Run(DummyOpts(), ['-o', output_dir.GetPath(),
++ '-w', whitelist_file])
++ header = output_dir.GetPath('whitelist_test_resources.h')
++ map_cc = output_dir.GetPath('whitelist_test_resources_map.cc')
++ map_h = output_dir.GetPath('whitelist_test_resources_map.h')
++ pak = output_dir.GetPath('whitelist_test_resources.pak')
++
++ # Ensure the resource map header and .pak files exist, but don't verify
++ # their content.
++ self.failUnless(os.path.exists(map_h))
++ self.failUnless(os.path.exists(pak))
++
++ whitelisted_ids = [
++ 'IDR_STRUCTURE_WHITELISTED',
++ 'IDR_STRUCTURE_IN_TRUE_IF_WHITELISTED',
++ 'IDR_INCLUDE_WHITELISTED',
++ ]
++ non_whitelisted_ids = [
++ 'IDR_STRUCTURE_NOT_WHITELISTED',
++ 'IDR_STRUCTURE_IN_TRUE_IF_NOT_WHITELISTED',
++ 'IDR_STRUCTURE_IN_FALSE_IF_WHITELISTED',
++ 'IDR_STRUCTURE_IN_FALSE_IF_NOT_WHITELISTED',
++ 'IDR_INCLUDE_NOT_WHITELISTED',
++ ]
++ for output_file in (header, map_cc):
++ self._verifyWhitelistedOutput(
++ output_file,
++ whitelisted_ids,
++ non_whitelisted_ids,
++ )
++ output_dir.CleanUp()
++
++ def testWriteOnlyNew(self):
++ output_dir = util.TempDir({})
++ builder = build.RcBuilder()
++ class DummyOpts(object):
++ def __init__(self):
++ self.input = util.PathFromRoot('grit/testdata/substitute.grd')
++ self.verbose = False
++ self.extra_verbose = False
++ UNCHANGED = 10
++ header = output_dir.GetPath('resource.h')
++
++ builder.Run(DummyOpts(), ['-o', output_dir.GetPath()])
++ self.failUnless(os.path.exists(header))
++ first_mtime = os.stat(header).st_mtime
++
++ os.utime(header, (UNCHANGED, UNCHANGED))
++ builder.Run(DummyOpts(),
++ ['-o', output_dir.GetPath(), '--write-only-new', '0'])
++ self.failUnless(os.path.exists(header))
++ second_mtime = os.stat(header).st_mtime
++
++ os.utime(header, (UNCHANGED, UNCHANGED))
++ builder.Run(DummyOpts(),
++ ['-o', output_dir.GetPath(), '--write-only-new', '1'])
++ self.failUnless(os.path.exists(header))
++ third_mtime = os.stat(header).st_mtime
++
++ self.assertTrue(abs(second_mtime - UNCHANGED) > 5)
++ self.assertTrue(abs(third_mtime - UNCHANGED) < 5)
++ output_dir.CleanUp()
++
++ def testGenerateDepFileWithDependOnStamp(self):
++ output_dir = util.TempDir({})
++ builder = build.RcBuilder()
++ class DummyOpts(object):
++ def __init__(self):
++ self.input = util.PathFromRoot('grit/testdata/substitute.grd')
++ self.verbose = False
++ self.extra_verbose = False
++ expected_dep_file_name = 'substitute.grd.d'
++ expected_stamp_file_name = expected_dep_file_name + '.stamp'
++ expected_dep_file = output_dir.GetPath(expected_dep_file_name)
++ expected_stamp_file = output_dir.GetPath(expected_stamp_file_name)
++ if os.path.isfile(expected_stamp_file):
++ os.remove(expected_stamp_file)
++ builder.Run(DummyOpts(), ['-o', output_dir.GetPath(),
++ '--depdir', output_dir.GetPath(),
++ '--depfile', expected_dep_file,
++ '--depend-on-stamp'])
++ self.failUnless(os.path.isfile(expected_stamp_file))
++ first_mtime = os.stat(expected_stamp_file).st_mtime
++
++ # Reset mtime to very old.
++ OLDTIME = 10
++ os.utime(expected_stamp_file, (OLDTIME, OLDTIME))
++
++ builder.Run(DummyOpts(), ['-o', output_dir.GetPath(),
++ '--depdir', output_dir.GetPath(),
++ '--depfile', expected_dep_file,
++ '--depend-on-stamp'])
++ self.failUnless(os.path.isfile(expected_stamp_file))
++ second_mtime = os.stat(expected_stamp_file).st_mtime
++
++ # Some OS have a 2s stat resolution window, so can't do a direct comparison.
++ self.assertTrue((second_mtime - OLDTIME) > 5)
++ self.assertTrue(abs(second_mtime - first_mtime) < 5)
++
++ self.failUnless(os.path.isfile(expected_dep_file))
++ with open(expected_dep_file) as f:
++ line = f.readline()
++ (dep_output_file, deps_string) = line.split(': ')
++ deps = deps_string.split(' ')
++
++ self.failUnlessEqual(expected_stamp_file_name, dep_output_file)
++ self.failUnlessEqual(deps, [
++ util.PathFromRoot('grit/testdata/substitute.xmb'),
++ ])
++ output_dir.CleanUp()
++
++
++if __name__ == '__main__':
++ unittest.main()
+diff --git a/tools/grit/grit/tool/buildinfo.py b/tools/grit/grit/tool/buildinfo.py
new file mode 100644
-index 0000000000..571d6d667d
+index 0000000000..7f8d1a3b04
--- /dev/null
-+++ b/tools/clang/plugins/tests/virtual_methods.txt
-@@ -0,0 +1,8 @@
-+In file included from virtual_methods.cpp:5:
-+./virtual_methods.h:17:36: warning: [chromium-style] virtual methods with non-empty bodies shouldn't be declared inline.
-+ virtual bool ComplainAboutThis() { return true; }
-+ ^
-+./virtual_methods.h:23:3: warning: [chromium-style] Overriding method must have "virtual" keyword.
-+ void MethodHasNoArguments() override;
-+ ^
-+2 warnings generated.
-diff --git a/tools/clang/scripts/package.sh b/tools/clang/scripts/package.sh
-new file mode 100755
-index 0000000000..eb345810b9
---- /dev/null
-+++ b/tools/clang/scripts/package.sh
-@@ -0,0 +1,87 @@
-+#!/bin/bash
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+# This script will check out llvm and clang, and then package the results up
-+# to a tgz file.
-+
-+THIS_DIR="$(dirname "${0}")"
-+LLVM_DIR="${THIS_DIR}/../../../third_party/llvm"
-+LLVM_BOOTSTRAP_DIR="${THIS_DIR}/../../../third_party/llvm-bootstrap"
-+LLVM_BUILD_DIR="${THIS_DIR}/../../../third_party/llvm-build"
-+LLVM_BIN_DIR="${LLVM_BUILD_DIR}/Release+Asserts/bin"
-+LLVM_LIB_DIR="${LLVM_BUILD_DIR}/Release+Asserts/lib"
-+
-+echo "Diff in llvm:" | tee buildlog.txt
-+svn stat "${LLVM_DIR}" 2>&1 | tee -a buildlog.txt
-+svn diff "${LLVM_DIR}" 2>&1 | tee -a buildlog.txt
-+echo "Diff in llvm/tools/clang:" | tee -a buildlog.txt
-+svn stat "${LLVM_DIR}/tools/clang" 2>&1 | tee -a buildlog.txt
-+svn diff "${LLVM_DIR}/tools/clang" 2>&1 | tee -a buildlog.txt
-+echo "Diff in llvm/projects/compiler-rt:" | tee -a buildlog.txt
-+svn stat "${LLVM_DIR}/projects/compiler-rt" 2>&1 | tee -a buildlog.txt
-+svn diff "${LLVM_DIR}/projects/compiler-rt" 2>&1 | tee -a buildlog.txt
-+
-+echo "Starting build" | tee -a buildlog.txt
-+
-+set -ex
-+
-+# Do a clobber build.
-+rm -rf "${LLVM_BOOTSTRAP_DIR}"
-+rm -rf "${LLVM_BUILD_DIR}"
-+"${THIS_DIR}"/update.sh --run-tests --bootstrap --force-local-build 2>&1 | \
-+ tee -a buildlog.txt
-+
-+R=$("${LLVM_BIN_DIR}/clang" --version | \
-+ sed -ne 's/clang version .*(trunk \([0-9]*\))/\1/p')
-+
-+PDIR=clang-$R
-+rm -rf $PDIR
-+mkdir $PDIR
-+mkdir $PDIR/bin
-+mkdir $PDIR/lib
-+
-+# Copy buildlog over.
-+cp buildlog.txt $PDIR/
-+
-+# Copy clang into pdir, symlink clang++ to it.
-+cp "${LLVM_BIN_DIR}/clang" $PDIR/bin/
-+(cd $PDIR/bin && ln -sf clang clang++ && cd -)
-+
-+# Copy plugins. Some of the dylibs are pretty big, so copy only the ones we
-+# care about.
-+if [ "$(uname -s)" = "Darwin" ]; then
-+ cp "${LLVM_LIB_DIR}/libFindBadConstructs.dylib" $PDIR/lib
-+else
-+ cp "${LLVM_LIB_DIR}/libFindBadConstructs.so" $PDIR/lib
-+fi
-+
-+# Copy built-in headers (lib/clang/3.2/include).
-+# libcompiler-rt puts all kinds of libraries there too, but we want only ASan.
-+if [ "$(uname -s)" = "Darwin" ]; then
-+ # Keep only Release+Asserts/lib/clang/3.2/lib/darwin/libclang_rt.asan_osx.a
-+ find "${LLVM_LIB_DIR}/clang" -type f -path '*lib/darwin*' | grep -v asan | \
-+ xargs rm
-+else
-+ # Keep only
-+ # Release+Asserts/lib/clang/3.2/lib/linux/libclang_rt.{asan,tsan}-x86_64.a
-+ # TODO(thakis): Make sure the 32bit version of ASan runtime is kept too once
-+ # that's built. TSan runtime exists only for 64 bits.
-+ find "${LLVM_LIB_DIR}/clang" -type f -path '*lib/linux*' | \
-+ grep -v "asan\|tsan" | xargs rm
-+fi
-+
-+cp -R "${LLVM_LIB_DIR}/clang" $PDIR/lib
-+
-+tar zcf $PDIR.tgz -C $PDIR bin lib buildlog.txt
-+
-+if [ "$(uname -s)" = "Darwin" ]; then
-+ PLATFORM=Mac
-+else
-+ PLATFORM=Linux_x64
-+fi
-+
-+echo To upload, run:
-+echo gsutil cp -a public-read $PDIR.tgz \
-+ gs://chromium-browser-clang/$PLATFORM/$PDIR.tgz
-diff --git a/tools/clang/scripts/plugin_flags.sh b/tools/clang/scripts/plugin_flags.sh
-new file mode 100755
-index 0000000000..217c5c3bd6
---- /dev/null
-+++ b/tools/clang/scripts/plugin_flags.sh
-@@ -0,0 +1,24 @@
-+#!/bin/bash
++++ b/tools/grit/grit/tool/buildinfo.py
+@@ -0,0 +1,78 @@
+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
-+# This script returns the flags that should be used when GYP_DEFINES contains
-+# clang_use_chrome_plugins. The flags are stored in a script so that they can
-+# be changed on the bots without requiring a master restart.
++"""Output the list of files to be generated by GRIT from an input.
++"""
+
-+THIS_ABS_DIR=$(cd $(dirname $0) && echo $PWD)
-+CLANG_LIB_PATH=$THIS_ABS_DIR/../../../third_party/llvm-build/Release+Asserts/lib
++from __future__ import print_function
+
-+if uname -s | grep -q Darwin; then
-+ LIBSUFFIX=dylib
-+else
-+ LIBSUFFIX=so
-+fi
++import getopt
++import os
++import sys
+
-+echo -Xclang -load -Xclang $CLANG_LIB_PATH/libFindBadConstructs.$LIBSUFFIX \
-+ -Xclang -add-plugin -Xclang find-bad-constructs \
-+ -Xclang -plugin-arg-find-bad-constructs \
-+ -Xclang skip-virtuals-in-implementations \
-+ -Xclang -plugin-arg-find-bad-constructs \
-+ -Xclang check-cc-directory
-diff --git a/tools/clang/scripts/update.py b/tools/clang/scripts/update.py
-new file mode 100755
-index 0000000000..bdc781f715
++from grit import grd_reader
++from grit.node import structure
++from grit.tool import interface
++
++class DetermineBuildInfo(interface.Tool):
++ """Determine what files will be read and output by GRIT.
++Outputs the list of generated files and inputs used to stdout.
++
++Usage: grit buildinfo [-o DIR]
++
++The output directory is used for display only.
++"""
++
++ def __init__(self):
++ pass
++
++ def ShortDescription(self):
++ """Describes this tool for the usage message."""
++ return ('Determine what files will be needed and\n'
++ 'output by GRIT with a given input.')
++
++ def Run(self, opts, args):
++ """Main method for the buildinfo tool."""
++ self.output_directory = '.'
++ (own_opts, args) = getopt.getopt(args, 'o:', ('help',))
++ for (key, val) in own_opts:
++ if key == '-o':
++ self.output_directory = val
++ elif key == '--help':
++ self.ShowUsage()
++ sys.exit(0)
++ if len(args) > 0:
++ print('This tool takes exactly one argument: the output directory via -o')
++ return 2
++ self.SetOptions(opts)
++
++ res_tree = grd_reader.Parse(opts.input, debug=opts.extra_verbose)
++
++ langs = {}
++ for output in res_tree.GetOutputFiles():
++ if output.attrs['lang']:
++ langs[output.attrs['lang']] = os.path.dirname(output.GetFilename())
++
++ for lang, dirname in langs.items():
++ old_output_language = res_tree.output_language
++ res_tree.SetOutputLanguage(lang)
++ for node in res_tree.ActiveDescendants():
++ with node:
++ if (isinstance(node, structure.StructureNode) and
++ node.HasFileForLanguage()):
++ path = node.FileForLanguage(lang, dirname, create_file=False,
++ return_if_not_generated=False)
++ if path:
++ path = os.path.join(self.output_directory, path)
++ path = os.path.normpath(path)
++ print('%s|%s' % ('rc_all', path))
++ res_tree.SetOutputLanguage(old_output_language)
++
++ for output in res_tree.GetOutputFiles():
++ path = os.path.join(self.output_directory, output.GetFilename())
++ path = os.path.normpath(path)
++ print('%s|%s' % (output.GetType(), path))
++
++ for infile in res_tree.GetInputFiles():
++ print('input|%s' % os.path.normpath(infile))
+diff --git a/tools/grit/grit/tool/buildinfo_unittest.py b/tools/grit/grit/tool/buildinfo_unittest.py
+new file mode 100644
+index 0000000000..24e9ddf8d8
--- /dev/null
-+++ b/tools/clang/scripts/update.py
-@@ -0,0 +1,34 @@
++++ b/tools/grit/grit/tool/buildinfo_unittest.py
+@@ -0,0 +1,90 @@
+#!/usr/bin/env python
+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
-+"""Windows can't run .sh files, so this is a small python wrapper around
-+update.sh.
++"""Unit tests for the 'grit buildinfo' tool.
+"""
+
++from __future__ import print_function
++
+import os
-+import subprocess
+import sys
++import unittest
++
++# This is needed to find some of the imports below.
++if __name__ == '__main__':
++ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
++
++from six import StringIO
++
++# pylint: disable-msg=C6204
++from grit.tool import buildinfo
++
++
++class BuildInfoUnittest(unittest.TestCase):
++ def setUp(self):
++ self.old_cwd = os.getcwd()
++ # Change CWD to make tests work independently of callers CWD.
++ os.chdir(os.path.dirname(__file__))
++ os.chdir('..')
++ self.buf = StringIO()
++ self.old_stdout = sys.stdout
++ sys.stdout = self.buf
++
++ def tearDown(self):
++ sys.stdout = self.old_stdout
++ os.chdir(self.old_cwd)
++
++ def testBuildOutput(self):
++ """Find all of the inputs and outputs for a GRD file."""
++ info_object = buildinfo.DetermineBuildInfo()
++
++ class DummyOpts(object):
++ def __init__(self):
++ self.input = '../grit/testdata/buildinfo.grd'
++ self.print_header = False
++ self.verbose = False
++ self.extra_verbose = False
++ info_object.Run(DummyOpts(), [])
++ output = self.buf.getvalue().replace('\\', '/')
++ self.failUnless(output.count(r'rc_all|sv_sidebar_loading.html'))
++ self.failUnless(output.count(r'rc_header|resource.h'))
++ self.failUnless(output.count(r'rc_all|en_generated_resources.rc'))
++ self.failUnless(output.count(r'rc_all|sv_generated_resources.rc'))
++ self.failUnless(output.count(r'input|../grit/testdata/substitute.xmb'))
++ self.failUnless(output.count(r'input|../grit/testdata/pr.bmp'))
++ self.failUnless(output.count(r'input|../grit/testdata/pr2.bmp'))
++ self.failUnless(
++ output.count(r'input|../grit/testdata/sidebar_loading.html'))
++ self.failUnless(output.count(r'input|../grit/testdata/transl.rc'))
++ self.failUnless(output.count(r'input|../grit/testdata/transl1.rc'))
++
++ def testBuildOutputWithDir(self):
++ """Find all the inputs and outputs for a GRD file with an output dir."""
++ info_object = buildinfo.DetermineBuildInfo()
++
++ class DummyOpts(object):
++ def __init__(self):
++ self.input = '../grit/testdata/buildinfo.grd'
++ self.print_header = False
++ self.verbose = False
++ self.extra_verbose = False
++ info_object.Run(DummyOpts(), ['-o', '../grit/testdata'])
++ output = self.buf.getvalue().replace('\\', '/')
++ self.failUnless(
++ output.count(r'rc_all|../grit/testdata/sv_sidebar_loading.html'))
++ self.failUnless(output.count(r'rc_header|../grit/testdata/resource.h'))
++ self.failUnless(
++ output.count(r'rc_all|../grit/testdata/en_generated_resources.rc'))
++ self.failUnless(
++ output.count(r'rc_all|../grit/testdata/sv_generated_resources.rc'))
++ self.failUnless(output.count(r'input|../grit/testdata/substitute.xmb'))
++ self.failUnlessEqual(0,
++ output.count(r'rc_all|../grit/testdata/sv_welcome_toast.html'))
++ self.failUnless(
++ output.count(r'rc_all|../grit/testdata/en_welcome_toast.html'))
+
+
-+def main():
-+ if sys.platform in ['win32', 'cygwin']:
++if __name__ == '__main__':
++ unittest.main()
+diff --git a/tools/grit/grit/tool/count.py b/tools/grit/grit/tool/count.py
+new file mode 100644
+index 0000000000..ab37f2ddb3
+--- /dev/null
++++ b/tools/grit/grit/tool/count.py
+@@ -0,0 +1,52 @@
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++'''Count number of occurrences of a given message ID.'''
++
++from __future__ import print_function
++
++import getopt
++import sys
++
++from grit import grd_reader
++from grit.tool import interface
++
++
++class CountMessage(interface.Tool):
++ '''Count the number of times a given message ID is used.'''
++
++ def __init__(self):
++ pass
++
++ def ShortDescription(self):
++ return 'Count the number of times a given message ID is used.'
++
++ def ParseOptions(self, args):
++ """Set this objects and return all non-option arguments."""
++ own_opts, args = getopt.getopt(args, '', ('help',))
++ for key, val in own_opts:
++ if key == '--help':
++ self.ShowUsage()
++ sys.exit(0)
++ return args
++
++ def Run(self, opts, args):
++ args = self.ParseOptions(args)
++ if len(args) != 1:
++ print('This tool takes a single tool-specific argument, the message '
++ 'ID to count.')
++ return 2
++ self.SetOptions(opts)
++
++ id = args[0]
++ res_tree = grd_reader.Parse(opts.input, debug=opts.extra_verbose)
++ res_tree.OnlyTheseTranslations([])
++ res_tree.RunGatherers()
++
++ count = 0
++ for c in res_tree.UberClique().AllCliques():
++ if c.GetId() == id:
++ count += 1
++
++ print("There are %d occurrences of message %s." % (count, id))
+diff --git a/tools/grit/grit/tool/diff_structures.py b/tools/grit/grit/tool/diff_structures.py
+new file mode 100644
+index 0000000000..d69e009b58
+--- /dev/null
++++ b/tools/grit/grit/tool/diff_structures.py
+@@ -0,0 +1,119 @@
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++'''The 'grit sdiff' tool.
++'''
++
++from __future__ import print_function
++
++import os
++import getopt
++import sys
++import tempfile
++
++from grit.node import structure
++from grit.tool import interface
++
++from grit import constants
++from grit import util
++
++# Builds the description for the tool (used as the __doc__
++# for the DiffStructures class).
++_class_doc = """\
++Allows you to view the differences in the structure of two files,
++disregarding their translateable content. Translateable portions of
++each file are changed to the string "TTTTTT" before invoking the diff program
++specified by the P4DIFF environment variable.
++
++Usage: grit sdiff [-t TYPE] [-s SECTION] [-e ENCODING] LEFT RIGHT
++
++LEFT and RIGHT are the files you want to diff. SECTION is required
++for structure types like 'dialog' to identify the part of the file to look at.
++ENCODING indicates the encoding of the left and right files (default 'cp1252').
++TYPE can be one of the following, defaults to 'tr_html':
++"""
++for gatherer in structure._GATHERERS:
++ _class_doc += " - %s\n" % gatherer
++
++
++class DiffStructures(interface.Tool):
++ __doc__ = _class_doc
++
++ def __init__(self):
++ self.section = None
++ self.left_encoding = 'cp1252'
++ self.right_encoding = 'cp1252'
++ self.structure_type = 'tr_html'
++
++ def ShortDescription(self):
++ return 'View differences without regard for translateable portions.'
++
++ def Run(self, global_opts, args):
++ (opts, args) = getopt.getopt(args, 's:e:t:',
++ ('help', 'left_encoding=', 'right_encoding='))
++ for key, val in opts:
++ if key == '-s':
++ self.section = val
++ elif key == '-e':
++ self.left_encoding = val
++ self.right_encoding = val
++ elif key == '-t':
++ self.structure_type = val
++ elif key == '--left_encoding':
++ self.left_encoding = val
++ elif key == '--right_encoding':
++ self.right_encoding == val
++ elif key == '--help':
++ self.ShowUsage()
++ sys.exit(0)
++
++ if len(args) != 2:
++ print("Incorrect usage - 'grit help sdiff' for usage details.")
++ return 2
++
++ if 'P4DIFF' not in os.environ:
++ print("Environment variable P4DIFF not set; defaulting to 'windiff'.")
++ diff_program = 'windiff'
++ else:
++ diff_program = os.environ['P4DIFF']
++
++ left_trans = self.MakeStaticTranslation(args[0], self.left_encoding)
++ try:
++ try:
++ right_trans = self.MakeStaticTranslation(args[1], self.right_encoding)
++
++ os.system('%s %s %s' % (diff_program, left_trans, right_trans))
++ finally:
++ os.unlink(right_trans)
++ finally:
++ os.unlink(left_trans)
++
++ def MakeStaticTranslation(self, original_filename, encoding):
++ """Given the name of the structure type (self.structure_type), the filename
++ of the file holding the original structure, and optionally the "section" key
++ identifying the part of the file to look at (self.section), creates a
++ temporary file holding a "static" translation of the original structure
++ (i.e. one where all translateable parts have been replaced with "TTTTTT")
++ and returns the temporary file name. It is the caller's responsibility to
++ delete the file when finished.
++
++ Args:
++ original_filename: 'c:\\bingo\\bla.rc'
++
++ Return:
++ 'c:\\temp\\werlkjsdf334.tmp'
++ """
++ original = structure._GATHERERS[self.structure_type](original_filename,
++ extkey=self.section,
++ encoding=encoding)
++ original.Parse()
++ translated = original.Translate(constants.CONSTANT_LANGUAGE, False)
++
++ fname = tempfile.mktemp()
++ with util.WrapOutputStream(open(fname, 'wb')) as writer:
++ writer.write("Original filename: %s\n=============\n\n"
++ % original_filename)
++ writer.write(translated) # write in UTF-8
++
++ return fname
+diff --git a/tools/grit/grit/tool/diff_structures_unittest.py b/tools/grit/grit/tool/diff_structures_unittest.py
+new file mode 100644
+index 0000000000..a6d7585761
+--- /dev/null
++++ b/tools/grit/grit/tool/diff_structures_unittest.py
+@@ -0,0 +1,46 @@
++#!/usr/bin/env python
++# 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.
++
++'''Unit tests for the 'grit newgrd' tool.'''
++
++from __future__ import print_function
++
++import os
++import sys
++if __name__ == '__main__':
++ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
++
++import unittest
++
++from grit.tool import diff_structures
++
++
++class DummyOpts(object):
++ """Options needed by NewGrd."""
++
++
++class DiffStructuresUnittest(unittest.TestCase):
++
++ def testMissingFiles(self):
++ """Verify failure w/out file inputs."""
++ tool = diff_structures.DiffStructures()
++ ret = tool.Run(DummyOpts(), [])
++ self.assertIsNotNone(ret)
++ self.assertGreater(ret, 0)
++
++ ret = tool.Run(DummyOpts(), ['left'])
++ self.assertIsNotNone(ret)
++ self.assertGreater(ret, 0)
++
++ def testTooManyArgs(self):
++ """Verify failure w/too many inputs."""
++ tool = diff_structures.DiffStructures()
++ ret = tool.Run(DummyOpts(), ['a', 'b', 'c'])
++ self.assertIsNotNone(ret)
++ self.assertGreater(ret, 0)
++
++
++if __name__ == '__main__':
++ unittest.main()
+diff --git a/tools/grit/grit/tool/interface.py b/tools/grit/grit/tool/interface.py
+new file mode 100644
+index 0000000000..e923205223
+--- /dev/null
++++ b/tools/grit/grit/tool/interface.py
+@@ -0,0 +1,62 @@
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++'''Base class and interface for tools.
++'''
++
++from __future__ import print_function
++
++class Tool(object):
++ '''Base class for all tools. Tools should use their docstring (i.e. the
++ class-level docstring) for the help they want to have printed when they
++ are invoked.'''
++
++ #
++ # Interface (abstract methods)
++ #
++
++ def ShortDescription(self):
++ '''Returns a short description of the functionality of the tool.'''
++ raise NotImplementedError()
++
++ def Run(self, global_options, my_arguments):
++ '''Runs the tool.
++
++ Args:
++ global_options: object grit_runner.Options
++ my_arguments: [arg1 arg2 ...]
++
++ Return:
++ 0 for success, non-0 for error
++ '''
++ raise NotImplementedError()
++
++ #
++ # Base class implementation
++ #
++
++ def __init__(self):
++ self.o = None
++
++ def ShowUsage(self):
++ '''Show usage text for this tool.'''
++ print(self.__doc__)
++
++ def SetOptions(self, opts):
++ self.o = opts
++
++ def Out(self, text):
++ '''Always writes out 'text'.'''
++ self.o.output_stream.write(text)
++
++ def VerboseOut(self, text):
++ '''Writes out 'text' if the verbose option is on.'''
++ if self.o.verbose:
++ self.o.output_stream.write(text)
++
++ def ExtraVerboseOut(self, text):
++ '''Writes out 'text' if the extra-verbose option is on.
++ '''
++ if self.o.extra_verbose:
++ self.o.output_stream.write(text)
+diff --git a/tools/grit/grit/tool/menu_from_parts.py b/tools/grit/grit/tool/menu_from_parts.py
+new file mode 100644
+index 0000000000..fcec26c5b1
+--- /dev/null
++++ b/tools/grit/grit/tool/menu_from_parts.py
+@@ -0,0 +1,79 @@
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++'''The 'grit menufromparts' tool.'''
++
++from __future__ import print_function
++
++import six
++
++from grit import grd_reader
++from grit import util
++from grit import xtb_reader
++from grit.tool import interface
++from grit.tool import transl2tc
++
++import grit.extern.tclib
++
++
++class MenuTranslationsFromParts(interface.Tool):
++ '''One-off tool to generate translated menu messages (where each menu is kept
++in a single message) based on existing translations of the individual menu
++items. Was needed when changing menus from being one message per menu item
++to being one message for the whole menu.'''
++
++ def ShortDescription(self):
++ return ('Create translations of whole menus from existing translations of '
++ 'menu items.')
++
++ def Run(self, globopt, args):
++ self.SetOptions(globopt)
++ assert len(args) == 2, "Need exactly two arguments, the XTB file and the output file"
++
++ xtb_file = args[0]
++ output_file = args[1]
++
++ grd = grd_reader.Parse(self.o.input, debug=self.o.extra_verbose)
++ grd.OnlyTheseTranslations([]) # don't load translations
++ grd.RunGatherers()
++
++ xtb = {}
++ def Callback(msg_id, parts):
++ msg = []
++ for part in parts:
++ if part[0]:
++ msg = []
++ break # it had a placeholder so ignore it
++ else:
++ msg.append(part[1])
++ if len(msg):
++ xtb[msg_id] = ''.join(msg)
++ with open(xtb_file, 'rb') as f:
++ xtb_reader.Parse(f, Callback)
++
++ translations = [] # list of translations as per transl2tc.WriteTranslations
++ for node in grd:
++ if node.name == 'structure' and node.attrs['type'] == 'menu':
++ assert len(node.GetCliques()) == 1
++ message = node.GetCliques()[0].GetMessage()
++ translation = []
++
++ contents = message.GetContent()
++ for part in contents:
++ if isinstance(part, six.string_types):
++ id = grit.extern.tclib.GenerateMessageId(part)
++ if id not in xtb:
++ print("WARNING didn't find all translations for menu %s" %
++ (node.attrs['name'],))
++ translation = []
++ break
++ translation.append(xtb[id])
++ else:
++ translation.append(part.GetPresentation())
++
++ if len(translation):
++ translations.append([message.GetId(), ''.join(translation)])
++
++ with util.WrapOutputStream(open(output_file, 'wb')) as f:
++ transl2tc.TranslationToTc.WriteTranslations(f, translations)
+diff --git a/tools/grit/grit/tool/newgrd.py b/tools/grit/grit/tool/newgrd.py
+new file mode 100644
+index 0000000000..66a18e9c04
+--- /dev/null
++++ b/tools/grit/grit/tool/newgrd.py
+@@ -0,0 +1,85 @@
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++'''Tool to create a new, empty .grd file with all the basic sections.
++'''
++
++from __future__ import print_function
++
++import getopt
++import sys
++
++from grit.tool import interface
++from grit import constants
++from grit import util
++
++# The contents of the new .grd file
++_FILE_CONTENTS = '''\
++<?xml version="1.0" encoding="UTF-8"?>
++<grit base_dir="." latest_public_release="0" current_release="1"
++ source_lang_id="en" enc_check="%s">
++ <outputs>
++ <!-- TODO add each of your output files. Modify the three below, and add
++ your own for your various languages. See the user's guide for more
++ details.
++ Note that all output references are relative to the output directory
++ which is specified at build time. -->
++ <output filename="resource.h" type="rc_header" />
++ <output filename="en_resource.rc" type="rc_all" />
++ <output filename="fr_resource.rc" type="rc_all" />
++ </outputs>
++ <translations>
++ <!-- TODO add references to each of the XTB files (from the Translation
++ Console) that contain translations of messages in your project. Each
++ takes a form like <file path="english.xtb" />. Remember that all file
++ references are relative to this .grd file. -->
++ </translations>
++ <release seq="1">
++ <includes>
++ <!-- TODO add a list of your included resources here, e.g. BMP and GIF
++ resources. -->
++ </includes>
++ <structures>
++ <!-- TODO add a list of all your structured resources here, e.g. HTML
++ templates, menus, dialogs etc. Note that for menus, dialogs and version
++ information resources you reference an .rc file containing them.-->
++ </structures>
++ <messages>
++ <!-- TODO add all of your "string table" messages here. Remember to
++ change nontranslateable parts of the messages into placeholders (using the
++ <ph> element). You can also use the 'grit add' tool to help you identify
++ nontranslateable parts and create placeholders for them. -->
++ </messages>
++ </release>
++</grit>''' % constants.ENCODING_CHECK
++
++
++class NewGrd(interface.Tool):
++ '''Usage: grit newgrd OUTPUT_FILE
++
++Creates a new, empty .grd file OUTPUT_FILE with comments about what to put
++where in the file.'''
++
++ def ShortDescription(self):
++ return 'Create a new empty .grd file.'
++
++ def ParseOptions(self, args):
++ """Set this objects and return all non-option arguments."""
++ own_opts, args = getopt.getopt(args, '', ('help',))
++ for key, val in own_opts:
++ if key == '--help':
++ self.ShowUsage()
++ sys.exit(0)
++ return args
++
++ def Run(self, opts, args):
++ args = self.ParseOptions(args)
++ if len(args) != 1:
++ print('This tool requires exactly one argument, the name of the output '
++ 'file.')
++ return 2
++ filename = args[0]
++ with util.WrapOutputStream(open(filename, 'wb'), 'utf-8') as out:
++ out.write(_FILE_CONTENTS)
++ print("Wrote file %s" % filename)
+diff --git a/tools/grit/grit/tool/newgrd_unittest.py b/tools/grit/grit/tool/newgrd_unittest.py
+new file mode 100644
+index 0000000000..f7c8831df5
+--- /dev/null
++++ b/tools/grit/grit/tool/newgrd_unittest.py
+@@ -0,0 +1,51 @@
++#!/usr/bin/env python
++# 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.
++
++'''Unit tests for the 'grit newgrd' tool.'''
++
++from __future__ import print_function
++
++import os
++import sys
++if __name__ == '__main__':
++ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
++
++import unittest
++
++from grit import util
++from grit.tool import newgrd
++
++
++class DummyOpts(object):
++ """Options needed by NewGrd."""
++
++
++class NewgrdUnittest(unittest.TestCase):
++
++ def testNewFile(self):
++ """Create a new file."""
++ tool = newgrd.NewGrd()
++ with util.TempDir({}) as output_dir:
++ output_file = os.path.join(output_dir.GetPath(), 'new.grd')
++ self.assertIsNone(tool.Run(DummyOpts(), [output_file]))
++ self.assertTrue(os.path.exists(output_file))
++
++ def testMissingFile(self):
++ """Verify failure w/out file output."""
++ tool = newgrd.NewGrd()
++ ret = tool.Run(DummyOpts(), [])
++ self.assertIsNotNone(ret)
++ self.assertGreater(ret, 0)
++
++ def testTooManyArgs(self):
++ """Verify failure w/too many outputs."""
++ tool = newgrd.NewGrd()
++ ret = tool.Run(DummyOpts(), ['a', 'b'])
++ self.assertIsNotNone(ret)
++ self.assertGreater(ret, 0)
++
++
++if __name__ == '__main__':
++ unittest.main()
+diff --git a/tools/grit/grit/tool/postprocess_interface.py b/tools/grit/grit/tool/postprocess_interface.py
+new file mode 100644
+index 0000000000..4bb8c5871f
+--- /dev/null
++++ b/tools/grit/grit/tool/postprocess_interface.py
+@@ -0,0 +1,29 @@
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++''' Base class for postprocessing of RC files.
++'''
++
++from __future__ import print_function
++
++class PostProcessor(object):
++ ''' Base class for postprocessing of the RC file data before being
++ output through the RC2GRD tool. You should implement this class if
++ you want GRIT to do specific things to the RC files after it has
++ converted the data into GRD format, i.e. change the content of the
++ RC file, and put it into a P4 changelist, etc.'''
++
++
++ def Process(self, rctext, rcpath, grdnode):
++ ''' Processes the data in rctext and grdnode.
++ Args:
++ rctext: string containing the contents of the RC file being processed.
++ rcpath: the path used to access the file.
++ grdtext: the root node of the grd xml data generated by
++ the rc2grd tool.
++
++ Return:
++ The root node of the processed GRD tree.
++ '''
++ raise NotImplementedError()
+diff --git a/tools/grit/grit/tool/postprocess_unittest.py b/tools/grit/grit/tool/postprocess_unittest.py
+new file mode 100644
+index 0000000000..77fe228bbe
+--- /dev/null
++++ b/tools/grit/grit/tool/postprocess_unittest.py
+@@ -0,0 +1,64 @@
++#!/usr/bin/env python
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++'''Unit test that checks postprocessing of files.
++ Tests postprocessing by having the postprocessor
++ modify the grd data tree, changing the message name attributes.
++'''
++
++from __future__ import print_function
++
++import os
++import re
++import sys
++if __name__ == '__main__':
++ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
++
++import unittest
++
++import grit.tool.postprocess_interface
++from grit.tool import rc2grd
++
++
++class PostProcessingUnittest(unittest.TestCase):
++
++ def testPostProcessing(self):
++ rctext = '''STRINGTABLE
++BEGIN
++ DUMMY_STRING_1 "String 1"
++ // Some random description
++ DUMMY_STRING_2 "This text was added during preprocessing"
++END
++ '''
++ tool = rc2grd.Rc2Grd()
++ class DummyOpts(object):
++ verbose = False
++ extra_verbose = False
++ tool.o = DummyOpts()
++ tool.post_process = 'grit.tool.postprocess_unittest.DummyPostProcessor'
++ result = tool.Process(rctext, '.\resource.rc')
++
++ self.failUnless(
++ result.children[2].children[2].children[0].attrs['name'] == 'SMART_STRING_1')
++ self.failUnless(
++ result.children[2].children[2].children[1].attrs['name'] == 'SMART_STRING_2')
++
++class DummyPostProcessor(grit.tool.postprocess_interface.PostProcessor):
++ '''
++ Post processing replaces all message name attributes containing "DUMMY" to
++ "SMART".
++ '''
++ def Process(self, rctext, rcpath, grdnode):
++ smarter = re.compile(r'(DUMMY)(.*)')
++ messages = grdnode.children[2].children[2]
++ for node in messages.children:
++ name_attr = node.attrs['name']
++ m = smarter.search(name_attr)
++ if m:
++ node.attrs['name'] = 'SMART' + m.group(2)
++ return grdnode
++
++if __name__ == '__main__':
++ unittest.main()
+diff --git a/tools/grit/grit/tool/preprocess_interface.py b/tools/grit/grit/tool/preprocess_interface.py
+new file mode 100644
+index 0000000000..67974e704e
+--- /dev/null
++++ b/tools/grit/grit/tool/preprocess_interface.py
+@@ -0,0 +1,25 @@
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++''' Base class for preprocessing of RC files.
++'''
++
++from __future__ import print_function
++
++class PreProcessor(object):
++ ''' Base class for preprocessing of the RC file data before being
++ output through the RC2GRD tool. You should implement this class if
++ you have specific constructs in your RC files that GRIT cannot handle.'''
++
++
++ def Process(self, rctext, rcpath):
++ ''' Processes the data in rctext.
++ Args:
++ rctext: string containing the contents of the RC file being processed
++ rcpath: the path used to access the file.
++
++ Return:
++ The processed text.
++ '''
++ raise NotImplementedError()
+diff --git a/tools/grit/grit/tool/preprocess_unittest.py b/tools/grit/grit/tool/preprocess_unittest.py
+new file mode 100644
+index 0000000000..40b95cd6f8
+--- /dev/null
++++ b/tools/grit/grit/tool/preprocess_unittest.py
+@@ -0,0 +1,50 @@
++#!/usr/bin/env python
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++'''Unit test that checks preprocessing of files.
++ Tests preprocessing by adding having the preprocessor
++ provide the actual rctext data.
++'''
++
++from __future__ import print_function
++
++import os
++import sys
++if __name__ == '__main__':
++ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
++
++import unittest
++
++import grit.tool.preprocess_interface
++from grit.tool import rc2grd
++
++
++class PreProcessingUnittest(unittest.TestCase):
++
++ def testPreProcessing(self):
++ tool = rc2grd.Rc2Grd()
++ class DummyOpts(object):
++ verbose = False
++ extra_verbose = False
++ tool.o = DummyOpts()
++ tool.pre_process = 'grit.tool.preprocess_unittest.DummyPreProcessor'
++ result = tool.Process('', '.\resource.rc')
++
++ self.failUnless(
++ result.children[2].children[2].children[0].attrs['name'] == 'DUMMY_STRING_1')
++
++class DummyPreProcessor(grit.tool.preprocess_interface.PreProcessor):
++ def Process(self, rctext, rcpath):
++ rctext = '''STRINGTABLE
++BEGIN
++ DUMMY_STRING_1 "String 1"
++ // Some random description
++ DUMMY_STRING_2 "This text was added during preprocessing"
++END
++ '''
++ return rctext
++
++if __name__ == '__main__':
++ unittest.main()
+diff --git a/tools/grit/grit/tool/rc2grd.py b/tools/grit/grit/tool/rc2grd.py
+new file mode 100644
+index 0000000000..3195b39000
+--- /dev/null
++++ b/tools/grit/grit/tool/rc2grd.py
+@@ -0,0 +1,418 @@
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++'''The 'grit rc2grd' tool.'''
++
++from __future__ import print_function
++
++import os.path
++import getopt
++import re
++import sys
++
++import six
++from six import StringIO
++
++import grit.node.empty
++from grit.node import include
++from grit.node import structure
++from grit.node import message
++
++from grit.gather import rc
++from grit.gather import tr_html
++
++from grit.tool import interface
++from grit.tool import postprocess_interface
++from grit.tool import preprocess_interface
++
++from grit import grd_reader
++from grit import lazy_re
++from grit import tclib
++from grit import util
++
++
++# Matches files referenced from an .rc file
++_FILE_REF = lazy_re.compile(r'''
++ ^(?P<id>[A-Z_0-9.]+)[ \t]+
++ (?P<type>[A-Z_0-9]+)[ \t]+
++ "(?P<file>.*?([^"]|""))"[ \t]*$''', re.VERBOSE | re.MULTILINE)
++
++
++# Matches a dialog section
++_DIALOG = lazy_re.compile(
++ r'^(?P<id>[A-Z0-9_]+)\s+DIALOG(EX)?\s.+?^BEGIN\s*$.+?^END\s*$',
++ re.MULTILINE | re.DOTALL)
++
++
++# Matches a menu section
++_MENU = lazy_re.compile(r'^(?P<id>[A-Z0-9_]+)\s+MENU.+?^BEGIN\s*$.+?^END\s*$',
++ re.MULTILINE | re.DOTALL)
++
++
++# Matches a versioninfo section
++_VERSIONINFO = lazy_re.compile(
++ r'^(?P<id>[A-Z0-9_]+)\s+VERSIONINFO\s.+?^BEGIN\s*$.+?^END\s*$',
++ re.MULTILINE | re.DOTALL)
++
++
++# Matches a stringtable
++_STRING_TABLE = lazy_re.compile(
++ (r'^STRINGTABLE(\s+(PRELOAD|DISCARDABLE|CHARACTERISTICS.+|LANGUAGE.+|'
++ r'VERSION.+))*\s*\nBEGIN\s*$(?P<body>.+?)^END\s*$'),
++ re.MULTILINE | re.DOTALL)
++
++
++# Matches each message inside a stringtable, breaking it up into comments,
++# the ID of the message, and the (RC-escaped) message text.
++_MESSAGE = lazy_re.compile(r'''
++ (?P<comment>(^\s+//.+?)*) # 0 or more lines of comments preceding the message
++ ^\s*
++ (?P<id>[A-Za-z0-9_]+) # id
++ \s+
++ "(?P<text>.*?([^"]|""))"([^"]|$) # The message itself
++ ''', re.MULTILINE | re.DOTALL | re.VERBOSE)
++
++
++# Matches each line of comment text in a multi-line comment.
++_COMMENT_TEXT = lazy_re.compile(r'^\s*//\s*(?P<text>.+?)$', re.MULTILINE)
++
++
++# Matches a string that is empty or all whitespace
++_WHITESPACE_ONLY = lazy_re.compile(r'\A\s*\Z', re.MULTILINE)
++
++
++# Finds printf and FormatMessage style format specifiers
++# Uses non-capturing groups except for the outermost group, so the output of
++# re.split() should include both the normal text and what we intend to
++# replace with placeholders.
++# TODO(joi) Check documentation for printf (and Windows variants) and FormatMessage
++_FORMAT_SPECIFIER = lazy_re.compile(
++ r'(%[-# +]?(?:[0-9]*|\*)(?:\.(?:[0-9]+|\*))?(?:h|l|L)?' # printf up to last char
++ r'(?:d|i|o|u|x|X|e|E|f|F|g|G|c|r|s|ls|ws)' # printf last char
++ r'|\$[1-9][0-9]*)') # FormatMessage
++
++
++class Rc2Grd(interface.Tool):
++ '''A tool for converting .rc files to .grd files. This tool is only for
++converting the source (nontranslated) .rc file to a .grd file. For importing
++existing translations, use the rc2xtb tool.
++
++Usage: grit [global options] rc2grd [OPTIONS] RCFILE
++
++The tool takes a single argument, which is the path to the .rc file to convert.
++It outputs a .grd file with the same name in the same directory as the .rc file.
++The .grd file may have one or more TODO comments for things that have to be
++cleaned up manually.
++
++OPTIONS may be any of the following:
++
++ -e ENCODING Specify the ENCODING of the .rc file. Default is 'cp1252'.
++
++ -h TYPE Specify the TYPE attribute for HTML structures.
++ Default is 'tr_html'.
++
++ -u ENCODING Specify the ENCODING of HTML files. Default is 'utf-8'.
++
++ -n MATCH Specify the regular expression to match in comments that will
++ indicate that the resource the comment belongs to is not
++ translateable. Default is 'Not locali(s|z)able'.
++
++ -r GRDFILE Specify that GRDFILE should be used as a "role model" for
++ any placeholders that otherwise would have had TODO names.
++ This attempts to find an identical message in the GRDFILE
++ and uses that instead of the automatically placeholderized
++ message.
++
++ --pre CLASS Specify an optional, fully qualified classname, which
++ has to be a subclass of grit.tool.PreProcessor, to
++ run on the text of the RC file before conversion occurs.
++ This can be used to support constructs in the RC files
++ that GRIT cannot handle on its own.
++
++ --post CLASS Specify an optional, fully qualified classname, which
++ has to be a subclass of grit.tool.PostProcessor, to
++ run on the text of the converted RC file.
++ This can be used to alter the content of the RC file
++ based on the conversion that occured.
++
++For menus, dialogs and version info, the .grd file will refer to the original
++.rc file. Once conversion is complete, you can strip the original .rc file
++of its string table and all comments as these will be available in the .grd
++file.
++
++Note that this tool WILL NOT obey C preprocessor rules, so even if something
++is #if 0-ed out it will still be included in the output of this tool
++Therefore, if your .rc file contains sections like this, you should run the
++C preprocessor on the .rc file or manually edit it before using this tool.
++'''
++
++ def ShortDescription(self):
++ return 'A tool for converting .rc source files to .grd files.'
++
++ def __init__(self):
++ self.input_encoding = 'cp1252'
++ self.html_type = 'tr_html'
++ self.html_encoding = 'utf-8'
++ self.not_localizable_re = re.compile('Not locali(s|z)able')
++ self.role_model = None
++ self.pre_process = None
++ self.post_process = None
++
++ def ParseOptions(self, args, help_func=None):
++ '''Given a list of arguments, set this object's options and return
++ all non-option arguments.
++ '''
++ (own_opts, args) = getopt.getopt(args, 'e:h:u:n:r',
++ ('help', 'pre=', 'post='))
++ for (key, val) in own_opts:
++ if key == '-e':
++ self.input_encoding = val
++ elif key == '-h':
++ self.html_type = val
++ elif key == '-u':
++ self.html_encoding = val
++ elif key == '-n':
++ self.not_localizable_re = re.compile(val)
++ elif key == '-r':
++ self.role_model = grd_reader.Parse(val)
++ elif key == '--pre':
++ self.pre_process = val
++ elif key == '--post':
++ self.post_process = val
++ elif key == '--help':
++ if help_func is None:
++ self.ShowUsage()
++ else:
++ help_func()
++ sys.exit(0)
++ return args
++
++ def Run(self, opts, args):
++ args = self.ParseOptions(args)
++ if len(args) != 1:
++ print('This tool takes a single tool-specific argument, the path to the\n'
++ '.rc file to process.')
++ return 2
++ self.SetOptions(opts)
++
++ path = args[0]
++ out_path = os.path.join(util.dirname(path),
++ os.path.splitext(os.path.basename(path))[0] + '.grd')
++
++ rctext = util.ReadFile(path, self.input_encoding)
++ grd_text = six.text_type(self.Process(rctext, path))
++ with util.WrapOutputStream(open(out_path, 'wb'), 'utf-8') as outfile:
++ outfile.write(grd_text)
++
++ print('Wrote output file %s.\nPlease check for TODO items in the file.' %
++ (out_path,))
++
++
++ def Process(self, rctext, rc_path):
++ '''Processes 'rctext' and returns a resource tree corresponding to it.
++
++ Args:
++ rctext: complete text of the rc file
++ rc_path: 'resource\resource.rc'
++
++ Return:
++ grit.node.base.Node subclass
++ '''
++
++ if self.pre_process:
++ preprocess_class = util.NewClassInstance(self.pre_process,
++ preprocess_interface.PreProcessor)
++ if preprocess_class:
++ rctext = preprocess_class.Process(rctext, rc_path)
++ else:
++ self.Out(
++ 'PreProcessing class could not be found. Skipping preprocessing.\n')
++
++ # Start with a basic skeleton for the .grd file
++ root = grd_reader.Parse(StringIO(
++ '''<?xml version="1.0" encoding="UTF-8"?>
++ <grit base_dir="." latest_public_release="0"
++ current_release="1" source_lang_id="en">
++ <outputs />
++ <translations />
++ <release seq="1">
++ <includes />
++ <structures />
++ <messages />
++ </release>
++ </grit>'''), util.dirname(rc_path))
++ includes = root.children[2].children[0]
++ structures = root.children[2].children[1]
++ messages = root.children[2].children[2]
++ assert (isinstance(includes, grit.node.empty.IncludesNode) and
++ isinstance(structures, grit.node.empty.StructuresNode) and
++ isinstance(messages, grit.node.empty.MessagesNode))
++
++ self.AddIncludes(rctext, includes)
++ self.AddStructures(rctext, structures, os.path.basename(rc_path))
++ self.AddMessages(rctext, messages)
++
++ self.VerboseOut('Validating that all IDs are unique...\n')
++ root.ValidateUniqueIds()
++ self.ExtraVerboseOut('Done validating that all IDs are unique.\n')
++
++ if self.post_process:
++ postprocess_class = util.NewClassInstance(self.post_process,
++ postprocess_interface.PostProcessor)
++ if postprocess_class:
++ root = postprocess_class.Process(rctext, rc_path, root)
++ else:
++ self.Out(
++ 'PostProcessing class could not be found. Skipping postprocessing.\n')
++
++ return root
++
++
++ def IsHtml(self, res_type, fname):
++ '''Check whether both the type and file extension indicate HTML'''
++ fext = fname.split('.')[-1].lower()
++ return res_type == 'HTML' and fext in ('htm', 'html')
++
++
++ def AddIncludes(self, rctext, node):
++ '''Scans 'rctext' for included resources (e.g. BITMAP, ICON) and
++ adds each included resource as an <include> child node of 'node'.'''
++ for m in _FILE_REF.finditer(rctext):
++ id = m.group('id')
++ res_type = m.group('type').upper()
++ fname = rc.Section.UnEscape(m.group('file'))
++ assert fname.find('\n') == -1
++ if not self.IsHtml(res_type, fname):
++ self.VerboseOut('Processing %s with ID %s (filename: %s)\n' %
++ (res_type, id, fname))
++ node.AddChild(include.IncludeNode.Construct(node, id, res_type, fname))
++
++
++ def AddStructures(self, rctext, node, rc_filename):
++ '''Scans 'rctext' for structured resources (e.g. menus, dialogs, version
++ information resources and HTML templates) and adds each as a <structure>
++ child of 'node'.'''
++ # First add HTML includes
++ for m in _FILE_REF.finditer(rctext):
++ id = m.group('id')
++ res_type = m.group('type').upper()
++ fname = rc.Section.UnEscape(m.group('file'))
++ if self.IsHtml(type, fname):
++ node.AddChild(structure.StructureNode.Construct(
++ node, id, self.html_type, fname, self.html_encoding))
++
++ # Then add all RC includes
++ def AddStructure(res_type, id):
++ self.VerboseOut('Processing %s with ID %s\n' % (res_type, id))
++ node.AddChild(structure.StructureNode.Construct(node, id, res_type,
++ rc_filename,
++ encoding=self.input_encoding))
++ for m in _MENU.finditer(rctext):
++ AddStructure('menu', m.group('id'))
++ for m in _DIALOG.finditer(rctext):
++ AddStructure('dialog', m.group('id'))
++ for m in _VERSIONINFO.finditer(rctext):
++ AddStructure('version', m.group('id'))
++
++
++ def AddMessages(self, rctext, node):
++ '''Scans 'rctext' for all messages in string tables, preprocesses them as
++ much as possible for placeholders (e.g. messages containing $1, $2 or %s, %d
++ type format specifiers get those specifiers replaced with placeholders, and
++ HTML-formatted messages get run through the HTML-placeholderizer). Adds
++ each message as a <message> node child of 'node'.'''
++ for tm in _STRING_TABLE.finditer(rctext):
++ table = tm.group('body')
++ for mm in _MESSAGE.finditer(table):
++ comment_block = mm.group('comment')
++ comment_text = []
++ for cm in _COMMENT_TEXT.finditer(comment_block):
++ comment_text.append(cm.group('text'))
++ comment_text = ' '.join(comment_text)
++
++ id = mm.group('id')
++ text = rc.Section.UnEscape(mm.group('text'))
++
++ self.VerboseOut('Processing message %s (text: "%s")\n' % (id, text))
++
++ msg_obj = self.Placeholderize(text)
++
++ # Messages that contain only placeholders do not need translation.
++ is_translateable = False
++ for item in msg_obj.GetContent():
++ if isinstance(item, six.string_types):
++ if not _WHITESPACE_ONLY.match(item):
++ is_translateable = True
++
++ if self.not_localizable_re.search(comment_text):
++ is_translateable = False
++
++ message_meaning = ''
++ internal_comment = ''
++
++ # If we have a "role model" (existing GRD file) and this node exists
++ # in the role model, use the description, meaning and translateable
++ # attributes from the role model.
++ if self.role_model:
++ role_node = self.role_model.GetNodeById(id)
++ if role_node:
++ is_translateable = role_node.IsTranslateable()
++ message_meaning = role_node.attrs['meaning']
++ comment_text = role_node.attrs['desc']
++ internal_comment = role_node.attrs['internal_comment']
++
++ # For nontranslateable messages, we don't want the complexity of
++ # placeholderizing everything.
++ if not is_translateable:
++ msg_obj = tclib.Message(text=text)
++
++ msg_node = message.MessageNode.Construct(node, msg_obj, id,
++ desc=comment_text,
++ translateable=is_translateable,
++ meaning=message_meaning)
++ msg_node.attrs['internal_comment'] = internal_comment
++
++ node.AddChild(msg_node)
++ self.ExtraVerboseOut('Done processing message %s\n' % id)
++
++
++ def Placeholderize(self, text):
++ '''Creates a tclib.Message object from 'text', attempting to recognize
++ a few different formats of text that can be automatically placeholderized
++ (HTML code, printf-style format strings, and FormatMessage-style format
++ strings).
++ '''
++
++ try:
++ # First try HTML placeholderizing.
++ # TODO(joi) Allow use of non-TotalRecall flavors of HTML placeholderizing
++ msg = tr_html.HtmlToMessage(text, True)
++ for item in msg.GetContent():
++ if not isinstance(item, six.string_types):
++ return msg # Contained at least one placeholder, so we're done
++
++ # HTML placeholderization didn't do anything, so try to find printf or
++ # FormatMessage format specifiers and change them into placeholders.
++ msg = tclib.Message()
++ parts = _FORMAT_SPECIFIER.split(text)
++ todo_counter = 1 # We make placeholder IDs 'TODO_0001' etc.
++ for part in parts:
++ if _FORMAT_SPECIFIER.match(part):
++ msg.AppendPlaceholder(tclib.Placeholder(
++ 'TODO_%04d' % todo_counter, part, 'TODO'))
++ todo_counter += 1
++ elif part != '':
++ msg.AppendText(part)
++
++ if self.role_model and len(parts) > 1: # there are TODO placeholders
++ role_model_msg = self.role_model.UberClique().BestCliqueByOriginalText(
++ msg.GetRealContent(), '')
++ if role_model_msg:
++ # replace wholesale to get placeholder names and examples
++ msg = role_model_msg
++
++ return msg
++ except:
++ print('Exception processing message with text "%s"' % text)
++ raise
+diff --git a/tools/grit/grit/tool/rc2grd_unittest.py b/tools/grit/grit/tool/rc2grd_unittest.py
+new file mode 100644
+index 0000000000..6d53794c27
+--- /dev/null
++++ b/tools/grit/grit/tool/rc2grd_unittest.py
+@@ -0,0 +1,163 @@
++#!/usr/bin/env python
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++'''Unit tests for grit.tool.rc2grd'''
++
++from __future__ import print_function
++
++import os
++import sys
++if __name__ == '__main__':
++ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
++
++import re
++import unittest
++
++from six import StringIO
++
++from grit import grd_reader
++from grit import util
++from grit.node import base
++from grit.tool import rc2grd
++
++
++class Rc2GrdUnittest(unittest.TestCase):
++ def testPlaceholderize(self):
++ tool = rc2grd.Rc2Grd()
++ original = "Hello %s, how are you? I'm $1 years old!"
++ msg = tool.Placeholderize(original)
++ self.failUnless(msg.GetPresentableContent() == "Hello TODO_0001, how are you? I'm TODO_0002 years old!")
++ self.failUnless(msg.GetRealContent() == original)
++
++ def testHtmlPlaceholderize(self):
++ tool = rc2grd.Rc2Grd()
++ original = "Hello <b>[USERNAME]</b>, how are you? I'm [AGE] years old!"
++ msg = tool.Placeholderize(original)
++ self.failUnless(msg.GetPresentableContent() ==
++ "Hello BEGIN_BOLDX_USERNAME_XEND_BOLD, how are you? I'm X_AGE_X years old!")
++ self.failUnless(msg.GetRealContent() == original)
++
++ def testMenuWithoutWhitespaceRegression(self):
++ # There was a problem in the original regular expression for parsing out
++ # menu sections, that would parse the following block of text as a single
++ # menu instead of two.
++ two_menus = '''
++// Hyper context menus
++IDR_HYPERMENU_FOLDER MENU
++BEGIN
++ POPUP "HyperFolder"
++ BEGIN
++ MENUITEM "Open Containing Folder", IDM_OPENFOLDER
++ END
++END
++
++IDR_HYPERMENU_FILE MENU
++BEGIN
++ POPUP "HyperFile"
++ BEGIN
++ MENUITEM "Open Folder", IDM_OPENFOLDER
++ END
++END
++
++'''
++ self.failUnless(len(rc2grd._MENU.findall(two_menus)) == 2)
++
++ def testRegressionScriptWithTranslateable(self):
++ tool = rc2grd.Rc2Grd()
++
++ # test rig
++ class DummyNode(base.Node):
++ def AddChild(self, item):
++ self.node = item
++ verbose = False
++ extra_verbose = False
++ tool.not_localizable_re = re.compile('')
++ tool.o = DummyNode()
++
++ rc_text = '''STRINGTABLE\nBEGIN\nID_BINGO "<SPAN id=hp style='BEHAVIOR: url(#default#homepage)'></SPAN><script>if (!hp.isHomePage('[$~HOMEPAGE~$]')) {document.write(""<a href=\\""[$~SETHOMEPAGEURL~$]\\"" >Set As Homepage</a> - "");}</script>"\nEND\n'''
++ tool.AddMessages(rc_text, tool.o)
++ self.failUnless(tool.o.node.GetCdata().find('Set As Homepage') != -1)
++
++ # TODO(joi) Improve the HTML parser to support translateables inside
++ # <script> blocks?
++ self.failUnless(tool.o.node.attrs['translateable'] == 'false')
++
++ def testRoleModel(self):
++ rc_text = ('STRINGTABLE\n'
++ 'BEGIN\n'
++ ' // This should not show up\n'
++ ' IDS_BINGO "Hello %s, how are you?"\n'
++ ' // The first description\n'
++ ' IDS_BONGO "Hello %s, my name is %s, and yours?"\n'
++ ' IDS_PROGRAMS_SHUTDOWN_TEXT "Google Desktop Search needs to close the following programs:\\n\\n$1\\nThe installation will not proceed if you choose to cancel."\n'
++ 'END\n')
++ tool = rc2grd.Rc2Grd()
++ tool.role_model = grd_reader.Parse(StringIO(
++ '''<?xml version="1.0" encoding="UTF-8"?>
++ <grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
++ <release seq="3">
++ <messages>
++ <message name="IDS_BINGO">
++ Hello <ph name="USERNAME">%s<ex>Joi</ex></ph>, how are you?
++ </message>
++ <message name="IDS_BONGO" desc="The other description">
++ Hello <ph name="USERNAME">%s<ex>Jakob</ex></ph>, my name is <ph name="ADMINNAME">%s<ex>Joi</ex></ph>, and yours?
++ </message>
++ <message name="IDS_PROGRAMS_SHUTDOWN_TEXT" desc="LIST_OF_PROGRAMS is replaced by a bulleted list of program names.">
++ Google Desktop Search needs to close the following programs:
++
++<ph name="LIST_OF_PROGRAMS">$1<ex>Program 1, Program 2</ex></ph>
++The installation will not proceed if you choose to cancel.
++ </message>
++ </messages>
++ </release>
++ </grit>'''), dir='.')
++
++ # test rig
++ class DummyOpts(object):
++ verbose = False
++ extra_verbose = False
++ tool.o = DummyOpts()
++ result = tool.Process(rc_text, '.\resource.rc')
++ self.failUnless(
++ result.children[2].children[2].children[0].attrs['desc'] == '')
++ self.failUnless(
++ result.children[2].children[2].children[0].children[0].attrs['name'] == 'USERNAME')
++ self.failUnless(
++ result.children[2].children[2].children[1].attrs['desc'] == 'The other description')
++ self.failUnless(
++ result.children[2].children[2].children[1].attrs['meaning'] == '')
++ self.failUnless(
++ result.children[2].children[2].children[1].children[0].attrs['name'] == 'USERNAME')
++ self.failUnless(
++ result.children[2].children[2].children[1].children[1].attrs['name'] == 'ADMINNAME')
++ self.failUnless(
++ result.children[2].children[2].children[2].children[0].attrs['name'] == 'LIST_OF_PROGRAMS')
++
++ def testRunOutput(self):
++ """Verify basic correct Run behavior."""
++ tool = rc2grd.Rc2Grd()
++ class DummyOpts(object):
++ verbose = False
++ extra_verbose = False
++ with util.TempDir({}) as output_dir:
++ rcfile = os.path.join(output_dir.GetPath(), 'foo.rc')
++ open(rcfile, 'w').close()
++ self.assertIsNone(tool.Run(DummyOpts(), [rcfile]))
++ self.assertTrue(os.path.exists(os.path.join(output_dir.GetPath(), 'foo.grd')))
++
++ def testMissingOutput(self):
++ """Verify failure with no args."""
++ tool = rc2grd.Rc2Grd()
++ class DummyOpts(object):
++ verbose = False
++ extra_verbose = False
++ ret = tool.Run(DummyOpts(), [])
++ self.assertIsNotNone(ret)
++ self.assertGreater(ret, 0)
++
++
++if __name__ == '__main__':
++ unittest.main()
+diff --git a/tools/grit/grit/tool/resize.py b/tools/grit/grit/tool/resize.py
+new file mode 100644
+index 0000000000..6a897c077e
+--- /dev/null
++++ b/tools/grit/grit/tool/resize.py
+@@ -0,0 +1,295 @@
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++'''The 'grit resize' tool.
++'''
++
++from __future__ import print_function
++
++import getopt
++import os
++import sys
++
++from grit import grd_reader
++from grit import pseudo
++from grit import util
++from grit.format import rc
++from grit.format import rc_header
++from grit.node import include
++from grit.tool import interface
++
++
++# Template for the .vcproj file, with a couple of [[REPLACEABLE]] parts.
++PROJECT_TEMPLATE = '''\
++<?xml version="1.0" encoding="Windows-1252"?>
++<VisualStudioProject
++ ProjectType="Visual C++"
++ Version="7.10"
++ Name="[[DIALOG_NAME]]"
++ ProjectGUID="[[PROJECT_GUID]]"
++ Keyword="Win32Proj">
++ <Platforms>
++ <Platform
++ Name="Win32"/>
++ </Platforms>
++ <Configurations>
++ <Configuration
++ Name="Debug|Win32"
++ OutputDirectory="Debug"
++ IntermediateDirectory="Debug"
++ ConfigurationType="1"
++ CharacterSet="2">
++ </Configuration>
++ </Configurations>
++ <References>
++ </References>
++ <Files>
++ <Filter
++ Name="Resource Files"
++ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"
++ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}">
++ <File
++ RelativePath=".\\[[DIALOG_NAME]].rc">
++ </File>
++ </Filter>
++ </Files>
++ <Globals>
++ </Globals>
++</VisualStudioProject>'''
++
++
++# Template for the .rc file with a couple of [[REPLACEABLE]] parts.
++# TODO(joi) Improve this (and the resource.h template) to allow saving and then
++# reopening of the RC file in Visual Studio. Currently you can only open it
++# once and change it, then after you close it you won't be able to reopen it.
++RC_TEMPLATE = '''\
++// This file is automatically generated by GRIT and intended for editing
++// the layout of the dialogs contained in it. Do not edit anything but the
++// dialogs. Any changes made to translateable portions of the dialogs will
++// be ignored by GRIT.
++
++#include "resource.h"
++#include <winresrc.h>
++#ifdef IDC_STATIC
++#undef IDC_STATIC
++#endif
++#define IDC_STATIC (-1)
++
++LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
++
++#pragma code_page([[CODEPAGE_NUM]])
++
++[[INCLUDES]]
++
++[[DIALOGS]]
++'''
++
++
++# Template for the resource.h file with a couple of [[REPLACEABLE]] parts.
++HEADER_TEMPLATE = '''\
++// This file is automatically generated by GRIT. Do not edit.
++
++#pragma once
++
++// Edit commands
++#define ID_EDIT_CLEAR 0xE120
++#define ID_EDIT_CLEAR_ALL 0xE121
++#define ID_EDIT_COPY 0xE122
++#define ID_EDIT_CUT 0xE123
++#define ID_EDIT_FIND 0xE124
++#define ID_EDIT_PASTE 0xE125
++#define ID_EDIT_PASTE_LINK 0xE126
++#define ID_EDIT_PASTE_SPECIAL 0xE127
++#define ID_EDIT_REPEAT 0xE128
++#define ID_EDIT_REPLACE 0xE129
++#define ID_EDIT_SELECT_ALL 0xE12A
++#define ID_EDIT_UNDO 0xE12B
++#define ID_EDIT_REDO 0xE12C
++
++
++[[DEFINES]]
++'''
++
++
++class ResizeDialog(interface.Tool):
++ '''Generates an RC file, header and Visual Studio project that you can use
++with Visual Studio's GUI resource editor to modify the layout of dialogs for
++the language of your choice. You then use the RC file, after you resize the
++dialog, for the language or languages of your choice, using the <skeleton> child
++of the <structure> node for the dialog. The translateable bits of the dialog
++will be ignored when you use the <skeleton> node (GRIT will instead use the
++translateable bits from the original dialog) but the layout changes you make
++will be used. Note that your layout changes must preserve the order of the
++translateable elements in the RC file.
++
++Usage: grit resize [-f BASEFOLDER] [-l LANG] [-e RCENCODING] DIALOGID*
++
++Arguments:
++ DIALOGID The 'name' attribute of a dialog to output for resizing. Zero
++ or more of these parameters can be used. If none are
++ specified, all dialogs from the input .grd file are output.
++
++Options:
++
++ -f BASEFOLDER The project will be created in a subfolder of BASEFOLDER.
++ The name of the subfolder will be the first DIALOGID you
++ specify. Defaults to '.'
++
++ -l LANG Specifies that the RC file should contain a dialog translated
++ into the language LANG. The default is a cp1252-representable
++ pseudotranslation, because Visual Studio's GUI RC editor only
++ supports single-byte encodings.
++
++ -c CODEPAGE Code page number to indicate to the RC compiler the encoding
++ of the RC file, default is something reasonable for the
++ language you selected (but this does not work for every single
++ language). See details on codepages below. NOTE that you do
++ not need to specify the codepage unless the tool complains
++ that it's not sure which codepage to use. See the following
++ page for codepage numbers supported by Windows:
++ http://www.microsoft.com/globaldev/reference/wincp.mspx
++
++ -D NAME[=VAL] Specify a C-preprocessor-like define NAME with optional
++ value VAL (defaults to 1) which will be used to control
++ conditional inclusion of resources.
++
++
++IMPORTANT NOTE: For now, the tool outputs a UTF-8 encoded file for any language
++that can not be represented in cp1252 (i.e. anything other than Western
++European languages). You will need to open this file in a text editor and
++save it using the codepage indicated in the #pragma code_page(XXXX) command
++near the top of the file, before you open it in Visual Studio.
++
++'''
++
++ # TODO(joi) It would be cool to have this tool note the Perforce revision
++ # of the original RC file somewhere, such that the <skeleton> node could warn
++ # if the original RC file gets updated without the skeleton file being updated.
++
++ # TODO(joi) Would be cool to have option to add the files to Perforce
++
++ def __init__(self):
++ self.lang = pseudo.PSEUDO_LANG
++ self.defines = {}
++ self.base_folder = '.'
++ self.codepage_number = 1252
++ self.codepage_number_specified_explicitly = False
++
++ def SetLanguage(self, lang):
++ '''Sets the language code to output things in.
++ '''
++ self.lang = lang
++ if not self.codepage_number_specified_explicitly:
++ self.codepage_number = util.LanguageToCodepage(lang)
++
++ def GetEncoding(self):
++ if self.codepage_number == 1200:
++ return 'utf_16'
++ if self.codepage_number == 65001:
++ return 'utf_8'
++ return 'cp%d' % self.codepage_number
++
++ def ShortDescription(self):
++ return 'Generate a file where you can resize a given dialog.'
++
++ def Run(self, opts, args):
++ self.SetOptions(opts)
++
++ own_opts, args = getopt.getopt(args, 'l:f:c:D:', ('help',))
++ for key, val in own_opts:
++ if key == '-l':
++ self.SetLanguage(val)
++ if key == '-f':
++ self.base_folder = val
++ if key == '-c':
++ self.codepage_number = int(val)
++ self.codepage_number_specified_explicitly = True
++ if key == '-D':
++ name, val = util.ParseDefine(val)
++ self.defines[name] = val
++ elif key == '--help':
++ self.ShowUsage()
++ sys.exit(0)
++
++ res_tree = grd_reader.Parse(opts.input, debug=opts.extra_verbose)
++ res_tree.OnlyTheseTranslations([self.lang])
++ res_tree.RunGatherers()
++
++ # Dialog IDs are either explicitly listed, or we output all dialogs from the
++ # .grd file
++ dialog_ids = args
++ if not len(dialog_ids):
++ for node in res_tree:
++ if node.name == 'structure' and node.attrs['type'] == 'dialog':
++ dialog_ids.append(node.attrs['name'])
++
++ self.Process(res_tree, dialog_ids)
++
++ def Process(self, grd, dialog_ids):
++ '''Outputs an RC file and header file for the dialog 'dialog_id' stored in
++ resource tree 'grd', to self.base_folder, as discussed in this class's
++ documentation.
++
++ Arguments:
++ grd: grd = grd_reader.Parse(...); grd.RunGatherers()
++ dialog_ids: ['IDD_MYDIALOG', 'IDD_OTHERDIALOG']
++ '''
++ grd.SetOutputLanguage(self.lang)
++ grd.SetDefines(self.defines)
++
++ project_name = dialog_ids[0]
++
++ dir_path = os.path.join(self.base_folder, project_name)
++ if not os.path.isdir(dir_path):
++ os.mkdir(dir_path)
++
++ # If this fails then we're not on Windows (or you don't have the required
++ # win32all Python libraries installed), so what are you doing mucking
++ # about with RC files anyway? :)
++ # pylint: disable=import-error
++ import pythoncom
++
++ # Create the .vcproj file
++ project_text = PROJECT_TEMPLATE.replace(
++ '[[PROJECT_GUID]]', str(pythoncom.CreateGuid())
++ ).replace('[[DIALOG_NAME]]', project_name)
++ fname = os.path.join(dir_path, '%s.vcproj' % project_name)
++ self.WriteFile(fname, project_text)
++ print("Wrote %s" % fname)
++
++ # Create the .rc file
++ # Output all <include> nodes since the dialogs might depend on them (e.g.
++ # for icons and bitmaps).
++ include_items = []
++ for node in grd.ActiveDescendants():
++ if isinstance(node, include.IncludeNode):
++ include_items.append(rc.FormatInclude(node, self.lang, '.'))
++ rc_text = RC_TEMPLATE.replace('[[CODEPAGE_NUM]]',
++ str(self.codepage_number))
++ rc_text = rc_text.replace('[[INCLUDES]]', ''.join(include_items))
++
++ # Then output the dialogs we have been asked to output.
++ dialogs = []
++ for dialog_id in dialog_ids:
++ node = grd.GetNodeById(dialog_id)
++ assert node.name == 'structure' and node.attrs['type'] == 'dialog'
++ # TODO(joi) Add exception handling for better error reporting
++ dialogs.append(rc.FormatStructure(node, self.lang, '.'))
++ rc_text = rc_text.replace('[[DIALOGS]]', ''.join(dialogs))
++
++ fname = os.path.join(dir_path, '%s.rc' % project_name)
++ self.WriteFile(fname, rc_text, self.GetEncoding())
++ print("Wrote %s" % fname)
++
++ # Create the resource.h file
++ header_defines = ''.join(rc_header.FormatDefines(grd))
++ header_text = HEADER_TEMPLATE.replace('[[DEFINES]]', header_defines)
++ fname = os.path.join(dir_path, 'resource.h')
++ self.WriteFile(fname, header_text)
++ print("Wrote %s" % fname)
++
++ def WriteFile(self, filename, contents, encoding='cp1252'):
++ with open(filename, 'wb') as f:
++ writer = util.WrapOutputStream(f, encoding)
++ writer.write(contents)
+diff --git a/tools/grit/grit/tool/test.py b/tools/grit/grit/tool/test.py
+new file mode 100644
+index 0000000000..241a976d74
+--- /dev/null
++++ b/tools/grit/grit/tool/test.py
+@@ -0,0 +1,24 @@
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++from __future__ import print_function
++
++from grit.tool import interface
++
++class TestTool(interface.Tool):
++ '''This tool does nothing except print out the global options and
++tool-specific arguments that it receives. It is intended only for testing,
++hence the name :)
++'''
++
++ def ShortDescription(self):
++ return 'A do-nothing tool for testing command-line parsing.'
++
++ def Run(self, global_options, my_arguments):
++ print('NOTE This tool is only for testing the parsing of global options and')
++ print('tool-specific arguments that it receives. You may have intended to')
++ print('run "grit unit" which is the unit-test suite for GRIT.')
++ print('Options: %s' % repr(global_options))
++ print('Arguments: %s' % repr(my_arguments))
+ return 0
+diff --git a/tools/grit/grit/tool/transl2tc.py b/tools/grit/grit/tool/transl2tc.py
+new file mode 100644
+index 0000000000..45301bbf58
+--- /dev/null
++++ b/tools/grit/grit/tool/transl2tc.py
+@@ -0,0 +1,251 @@
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
+
-+ # This script is called by gclient. gclient opens its hooks subprocesses with
-+ # (stdout=subprocess.PIPE, stderr=subprocess.STDOUT) and then does custom
-+ # output processing that breaks printing '\r' characters for single-line
-+ # updating status messages as printed by curl and wget.
-+ # Work around this by setting stderr of the update.sh process to stdin (!):
-+ # gclient doesn't redirect stdin, and while stdin itself is read-only, a
-+ # dup()ed sys.stdin is writable, try
-+ # fd2 = os.dup(sys.stdin.fileno()); os.write(fd2, 'hi')
-+ # TODO: Fix gclient instead, http://crbug.com/95350
-+ return subprocess.call(
-+ [os.path.join(os.path.dirname(__file__), 'update.sh')] + sys.argv[1:],
-+ stderr=os.fdopen(os.dup(sys.stdin.fileno())))
++'''The 'grit transl2tc' tool.
++'''
++
++from __future__ import print_function
++
++from grit import grd_reader
++from grit import util
++from grit.tool import interface
++from grit.tool import rc2grd
++
++from grit.extern import tclib
++
++
++class TranslationToTc(interface.Tool):
++ '''A tool for importing existing translations in RC format into the
++Translation Console.
++
++Usage:
++
++grit -i GRD transl2tc [-l LIMITS] [RCOPTS] SOURCE_RC TRANSLATED_RC OUT_FILE
++
++The tool needs a "source" RC file, i.e. in English, and an RC file that is a
++translation of precisely the source RC file (not of an older or newer version).
++
++The tool also requires you to provide a .grd file (input file) e.g. using the
++-i global option or the GRIT_INPUT environment variable. The tool uses
++information from your .grd file to correct placeholder names in the
++translations and ensure that only translatable items and translations still
++being used are output.
++
++This tool will accept all the same RCOPTS as the 'grit rc2grd' tool. To get
++a list of these options, run 'grit help rc2grd'.
++
++Additionally, you can use the -l option (which must be the first option to the
++tool) to specify a file containing a list of message IDs to which output should
++be limited. This is only useful if you are limiting the output to your XMB
++files using the 'grit xmb' tool's -l option. See 'grit help xmb' for how to
++generate a file containing a list of the message IDs in an XMB file.
++
++The tool will scan through both of the RC files as well as any HTML files they
++refer to, and match together the source messages and translated messages. It
++will output a file (OUTPUT_FILE) you can import directly into the TC using the
++Bulk Translation Upload tool.
++'''
++
++ def ShortDescription(self):
++ return 'Import existing translations in RC format into the TC'
++
++ def Setup(self, globopt, args):
++ '''Sets the instance up for use.
++ '''
++ self.SetOptions(globopt)
++ self.rc2grd = rc2grd.Rc2Grd()
++ self.rc2grd.SetOptions(globopt)
++ self.limits = None
++ if len(args) and args[0] == '-l':
++ self.limits = util.ReadFile(args[1], 'utf-8').splitlines()
++ args = args[2:]
++ return self.rc2grd.ParseOptions(args, help_func=self.ShowUsage)
++
++ def Run(self, globopt, args):
++ args = self.Setup(globopt, args)
++
++ if len(args) != 3:
++ self.Out('This tool takes exactly three arguments:\n'
++ ' 1. The path to the original RC file\n'
++ ' 2. The path to the translated RC file\n'
++ ' 3. The output file path.\n')
++ return 2
++
++ grd = grd_reader.Parse(self.o.input, debug=self.o.extra_verbose)
++ grd.RunGatherers()
++
++ source_rc = util.ReadFile(args[0], self.rc2grd.input_encoding)
++ transl_rc = util.ReadFile(args[1], self.rc2grd.input_encoding)
++ translations = self.ExtractTranslations(grd,
++ source_rc, args[0],
++ transl_rc, args[1])
++
++ with util.WrapOutputStream(open(args[2], 'wb')) as output_file:
++ self.WriteTranslations(output_file, translations.items())
++
++ self.Out('Wrote output file %s' % args[2])
++
++ def ExtractTranslations(self, current_grd, source_rc, source_path,
++ transl_rc, transl_path):
++ '''Extracts translations from the translated RC file, matching them with
++ translations in the source RC file to calculate their ID, and correcting
++ placeholders, limiting output to translateables, etc. using the supplied
++ .grd file which is the current .grd file for your project.
++
++ If this object's 'limits' attribute is not None but a list, the output of
++ this function will be further limited to include only messages that have
++ message IDs in the 'limits' list.
++
++ Args:
++ current_grd: grit.node.base.Node child, that has had RunGatherers() run
++ on it
++ source_rc: Complete text of source RC file
++ source_path: Path to the source RC file
++ transl_rc: Complete text of translated RC file
++ transl_path: Path to the translated RC file
++
++ Return:
++ { id1 : text1, '12345678' : 'Hello USERNAME, howzit?' }
++ '''
++ source_grd = self.rc2grd.Process(source_rc, source_path)
++ self.VerboseOut('Read %s into GRIT format, running gatherers.\n' % source_path)
++ source_grd.SetOutputLanguage(current_grd.output_language)
++ source_grd.SetDefines(current_grd.defines)
++ source_grd.RunGatherers(debug=self.o.extra_verbose)
++ transl_grd = self.rc2grd.Process(transl_rc, transl_path)
++ transl_grd.SetOutputLanguage(current_grd.output_language)
++ transl_grd.SetDefines(current_grd.defines)
++ self.VerboseOut('Read %s into GRIT format, running gatherers.\n' % transl_path)
++ transl_grd.RunGatherers(debug=self.o.extra_verbose)
++ self.VerboseOut('Done running gatherers for %s.\n' % transl_path)
++
++ # Proceed to create a map from ID to translation, getting the ID from the
++ # source GRD and the translation from the translated GRD.
++ id2transl = {}
++ for source_node in source_grd:
++ source_cliques = source_node.GetCliques()
++ if not len(source_cliques):
++ continue
++
++ assert 'name' in source_node.attrs, 'All nodes with cliques should have an ID'
++ node_id = source_node.attrs['name']
++ self.ExtraVerboseOut('Processing node %s\n' % node_id)
++ transl_node = transl_grd.GetNodeById(node_id)
++
++ if transl_node:
++ transl_cliques = transl_node.GetCliques()
++ if not len(transl_cliques) == len(source_cliques):
++ self.Out(
++ 'Warning: Translation for %s has wrong # of cliques, skipping.\n' %
++ node_id)
++ continue
++ else:
++ self.Out('Warning: No translation for %s, skipping.\n' % node_id)
++ continue
++
++ if source_node.name == 'message':
++ # Fixup placeholders as well as possible based on information from
++ # the current .grd file if they are 'TODO_XXXX' placeholders. We need
++ # to fixup placeholders in the translated message so that it looks right
++ # and we also need to fixup placeholders in the source message so that
++ # its calculated ID will match the current message.
++ current_node = current_grd.GetNodeById(node_id)
++ if current_node:
++ assert len(source_cliques) == len(current_node.GetCliques()) == 1
++
++ source_msg = source_cliques[0].GetMessage()
++ current_msg = current_node.GetCliques()[0].GetMessage()
++
++ # Only do this for messages whose source version has not changed.
++ if (source_msg.GetRealContent() != current_msg.GetRealContent()):
++ self.VerboseOut('Info: Message %s has changed; skipping\n' % node_id)
++ else:
++ transl_msg = transl_cliques[0].GetMessage()
++ transl_content = transl_msg.GetContent()
++ current_content = current_msg.GetContent()
++ source_content = source_msg.GetContent()
++
++ ok_to_fixup = True
++ if (len(transl_content) != len(current_content)):
++ # message structure of translation is different, don't try fixup
++ ok_to_fixup = False
++ if ok_to_fixup:
++ for ix in range(len(transl_content)):
++ if isinstance(transl_content[ix], tclib.Placeholder):
++ if not isinstance(current_content[ix], tclib.Placeholder):
++ ok_to_fixup = False # structure has changed
++ break
++ if (transl_content[ix].GetOriginal() !=
++ current_content[ix].GetOriginal()):
++ ok_to_fixup = False # placeholders have likely been reordered
++ break
++ else: # translated part is not a placeholder but a string
++ if isinstance(current_content[ix], tclib.Placeholder):
++ ok_to_fixup = False # placeholders have likely been reordered
++ break
++
++ if not ok_to_fixup:
++ self.VerboseOut(
++ 'Info: Structure of message %s has changed; skipping.\n' % node_id)
++ else:
++ def Fixup(content, ix):
++ if (isinstance(content[ix], tclib.Placeholder) and
++ content[ix].GetPresentation().startswith('TODO_')):
++ assert isinstance(current_content[ix], tclib.Placeholder)
++ # Get the placeholder ID and example from the current message
++ content[ix] = current_content[ix]
++ for ix in range(len(transl_content)):
++ Fixup(transl_content, ix)
++ Fixup(source_content, ix)
++
++ # Only put each translation once into the map. Warn if translations
++ # for the same message are different.
++ for ix in range(len(transl_cliques)):
++ source_msg = source_cliques[ix].GetMessage()
++ source_msg.GenerateId() # needed to refresh ID based on new placeholders
++ message_id = source_msg.GetId()
++ translated_content = transl_cliques[ix].GetMessage().GetPresentableContent()
++
++ if message_id in id2transl:
++ existing_translation = id2transl[message_id]
++ if existing_translation != translated_content:
++ original_text = source_cliques[ix].GetMessage().GetPresentableContent()
++ self.Out('Warning: Two different translations for "%s":\n'
++ ' Translation 1: "%s"\n'
++ ' Translation 2: "%s"\n' %
++ (original_text, existing_translation, translated_content))
++ else:
++ id2transl[message_id] = translated_content
++
++ # Remove translations for messages that do not occur in the current .grd
++ # or have been marked as not translateable, or do not occur in the 'limits'
++ # list (if it has been set).
++ current_message_ids = current_grd.UberClique().AllMessageIds()
++ for message_id in list(id2transl.keys()):
++ if (message_id not in current_message_ids or
++ not current_grd.UberClique().BestClique(message_id).IsTranslateable() or
++ (self.limits and message_id not in self.limits)):
++ del id2transl[message_id]
++
++ return id2transl
++
++ @staticmethod
++ def WriteTranslations(output_file, translations):
++ '''Writes the provided list of translations to the provided output file
++ in the format used by the TC's Bulk Translation Upload tool. The file
++ must be UTF-8 encoded.
++
++ Args:
++ output_file: util.WrapOutputStream(open('bingo.out', 'wb'))
++ translations: [ [id1, text1], ['12345678', 'Hello USERNAME, howzit?'] ]
++
++ Return:
++ None
++ '''
++ for id, text in translations:
++ text = text.replace('<', '&lt;').replace('>', '&gt;')
++ output_file.write(id)
++ output_file.write(' ')
++ output_file.write(text)
++ output_file.write('\n')
+diff --git a/tools/grit/grit/tool/transl2tc_unittest.py b/tools/grit/grit/tool/transl2tc_unittest.py
+new file mode 100644
+index 0000000000..22e937f9f2
+--- /dev/null
++++ b/tools/grit/grit/tool/transl2tc_unittest.py
+@@ -0,0 +1,133 @@
++#!/usr/bin/env python
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++'''Unit tests for the 'grit transl2tc' tool.'''
++
++from __future__ import print_function
++
++import os
++import sys
++if __name__ == '__main__':
++ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
++
++import unittest
++
++from six import StringIO
++
++from grit.tool import transl2tc
++from grit import grd_reader
++from grit import util
++
++
++def MakeOptions():
++ from grit import grit_runner
++ return grit_runner.Options()
++
++
++class TranslationToTcUnittest(unittest.TestCase):
++
++ def testOutput(self):
++ buf = StringIO()
++ tool = transl2tc.TranslationToTc()
++ translations = [
++ ['1', 'Hello USERNAME, how are you?'],
++ ['12', 'Howdie doodie!'],
++ ['123', 'Hello\n\nthere\n\nhow are you?'],
++ ['1234', 'Hello is > goodbye but < howdie pardner'],
++ ]
++ tool.WriteTranslations(buf, translations)
++ output = buf.getvalue()
++ self.failUnless(output.strip() == '''
++1 Hello USERNAME, how are you?
++12 Howdie doodie!
++123 Hello
++
++there
++
++how are you?
++1234 Hello is &gt; goodbye but &lt; howdie pardner
++'''.strip())
++
++ def testExtractTranslations(self):
++ path = util.PathFromRoot('grit/testdata')
++ current_grd = grd_reader.Parse(StringIO('''<?xml version="1.0" encoding="UTF-8"?>
++ <grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
++ <release seq="3">
++ <messages>
++ <message name="IDS_SIMPLE">
++ One
++ </message>
++ <message name="IDS_PLACEHOLDER">
++ <ph name="NUMBIRDS">%s<ex>3</ex></ph> birds
++ </message>
++ <message name="IDS_PLACEHOLDERS">
++ <ph name="ITEM">%d<ex>1</ex></ph> of <ph name="COUNT">%d<ex>3</ex></ph>
++ </message>
++ <message name="IDS_REORDERED_PLACEHOLDERS">
++ <ph name="ITEM">$1<ex>1</ex></ph> of <ph name="COUNT">$2<ex>3</ex></ph>
++ </message>
++ <message name="IDS_CHANGED">
++ This is the new version
++ </message>
++ <message name="IDS_TWIN_1">Hello</message>
++ <message name="IDS_TWIN_2">Hello</message>
++ <message name="IDS_NOT_TRANSLATEABLE" translateable="false">:</message>
++ <message name="IDS_LONGER_TRANSLATED">
++ Removed document <ph name="FILENAME">$1<ex>c:\temp</ex></ph>
++ </message>
++ <message name="IDS_DIFFERENT_TWIN_1">Howdie</message>
++ <message name="IDS_DIFFERENT_TWIN_2">Howdie</message>
++ </messages>
++ <structures>
++ <structure type="dialog" name="IDD_ABOUTBOX" encoding="utf-16" file="klonk.rc" />
++ <structure type="menu" name="IDC_KLONKMENU" encoding="utf-16" file="klonk.rc" />
++ </structures>
++ </release>
++ </grit>'''), path)
++ current_grd.SetOutputLanguage('en')
++ current_grd.RunGatherers()
++
++ source_rc_path = util.PathFromRoot('grit/testdata/source.rc')
++ source_rc = util.ReadFile(source_rc_path, 'utf-8')
++ transl_rc_path = util.PathFromRoot('grit/testdata/transl.rc')
++ transl_rc = util.ReadFile(transl_rc_path, 'utf-8')
++
++ tool = transl2tc.TranslationToTc()
++ output_buf = StringIO()
++ globopts = MakeOptions()
++ globopts.verbose = True
++ globopts.output_stream = output_buf
++ tool.Setup(globopts, [])
++ translations = tool.ExtractTranslations(current_grd,
++ source_rc, source_rc_path,
++ transl_rc, transl_rc_path)
++
++ values = list(translations.values())
++ output = output_buf.getvalue()
++
++ self.failUnless('Ein' in values)
++ self.failUnless('NUMBIRDS Vogeln' in values)
++ self.failUnless('ITEM von COUNT' in values)
++ self.failUnless(values.count('Hallo') == 1)
++ self.failIf('Dass war die alte Version' in values)
++ self.failIf(':' in values)
++ self.failIf('Dokument FILENAME ist entfernt worden' in values)
++ self.failIf('Nicht verwendet' in values)
++ self.failUnless(('Howdie' in values or 'Hallo sagt man' in values) and not
++ ('Howdie' in values and 'Hallo sagt man' in values))
++
++ self.failUnless('XX01XX&SkraXX02XX&HaettaXX03XXThetta er "Klonk" sem eg fylaXX04XXgonkurinnXX05XXKlonk && er [good]XX06XX&HjalpXX07XX&Um...XX08XX' in values)
++
++ self.failUnless('I lagi' in values)
++
++ self.failUnless(output.count('Structure of message IDS_REORDERED_PLACEHOLDERS has changed'))
++ self.failUnless(output.count('Message IDS_CHANGED has changed'))
++ self.failUnless(output.count('Structure of message IDS_LONGER_TRANSLATED has changed'))
++ self.failUnless(output.count('Two different translations for "Howdie"'))
++ self.failUnless(output.count('IDD_DIFFERENT_LENGTH_IN_TRANSL has wrong # of cliques'))
+
+
+if __name__ == '__main__':
-+ sys.exit(main())
-diff --git a/tools/clang/scripts/update.sh b/tools/clang/scripts/update.sh
-new file mode 100755
-index 0000000000..e9448236c8
++ unittest.main()
+diff --git a/tools/grit/grit/tool/unit.py b/tools/grit/grit/tool/unit.py
+new file mode 100644
+index 0000000000..7e96b699c3
+--- /dev/null
++++ b/tools/grit/grit/tool/unit.py
+@@ -0,0 +1,43 @@
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++'''GRIT tool that runs the unit test suite for GRIT.'''
++
++from __future__ import print_function
++
++import getopt
++import sys
++import unittest
++
++try:
++ import grit.test_suite_all
++except ImportError:
++ pass
++from grit.tool import interface
++
++
++class UnitTestTool(interface.Tool):
++ '''By using this tool (e.g. 'grit unit') you run all the unit tests for GRIT.
++This happens in the environment that is set up by the basic GRIT runner.'''
++
++ def ShortDescription(self):
++ return 'Use this tool to run all the unit tests for GRIT.'
++
++ def ParseOptions(self, args):
++ """Set this objects and return all non-option arguments."""
++ own_opts, args = getopt.getopt(args, '', ('help',))
++ for key, val in own_opts:
++ if key == '--help':
++ self.ShowUsage()
++ sys.exit(0)
++ return args
++
++ def Run(self, opts, args):
++ args = self.ParseOptions(args)
++ if args:
++ print('This tool takes no arguments.')
++ return 2
++
++ return unittest.TextTestRunner(verbosity=2).run(
++ grit.test_suite_all.TestSuiteAll())
+diff --git a/tools/grit/grit/tool/update_resource_ids/__init__.py b/tools/grit/grit/tool/update_resource_ids/__init__.py
+new file mode 100644
+index 0000000000..3006fbffab
+--- /dev/null
++++ b/tools/grit/grit/tool/update_resource_ids/__init__.py
+@@ -0,0 +1,305 @@
++# 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.
++"""Package grit.tool.update_resource_ids
++
++Updates GRID resource_ids from linked GRD files, while preserving structure.
++
++A resource_ids file is a JSON dict (with Python comments) that maps GRD paths
++to *items*. Item order is ignored by GRIT, but is important since it establishes
++a narrative of item dependency (needs non-overlapping IDs) and mutual exclusion
++(allows ID overlap). Example:
++
++{
++ # The first entry in the file, SRCDIR, is special: It is a relative path from
++ # this file to the base of your checkout.
++ "SRCDIR": "../..",
++
++ # First GRD file. This entry is an "Item".
++ "1.grd": {
++ "messages": [400], # "Tag".
++ },
++ # Depends on 1.grd, i.e., 500 >= 400 + (# of IDs used in 1.grd).
++ "2a.grd": {
++ "includes": [500], # "Tag".
++ "structures": [510], # "Tag" (etc.).
++ },
++ # Depends on 2a.grd.
++ "3a.grd": {
++ "includes": [1000],
++ },
++ # Depends on 2a.grd, but overlaps with 3b.grd due to mutually exclusivity.
++ "3b.grd": {
++ "includes": [1000],
++ },
++ # Depends on {3a.grd, 3b.grd}.
++ "4.grd": {
++ "META": {"join": 2}, # Hint for update_resource_ids.
++ "structures": [1500],
++ },
++ # Depends on 1.grd but overlaps with 2a.grd.
++ "2b.grd": {
++ "includes": [500],
++ "structures": [540],
++ },
++ # Depends on {4.grd, 2b.grd}.
++ "5.grd": {
++ "META": {"join": 2}, # Hint for update_resource_ids.
++ "includes": [600],
++ },
++ # Depends on 5.grd. File is generated, so hint is needed for sizes.
++ "<(SHARED_INTERMEDIATE_DIR)/6.grd": {
++ "META": {"sizes": {"includes": [10]}},
++ "includes": [700],
++ },
++}
++
++The "structure" within a resouces_ids file are as follows:
++1. Comments and spacing.
++2. Item ordering, to establish dependency and grouping.
++3. Special provision to allow ID overlaps from mutual exclusion.
++
++This module parses a resource_ids file, reads ID usages from GRD files it refers
++to, and generates an updated version of the resource_ids file while preserving
++structure elements 1-3 stated above.
++"""
++
++from __future__ import print_function
++
++import collections
++import getopt
++import os
++import shutil
++import sys
++import tempfile
++
++from grit.tool import interface
++from grit.tool.update_resource_ids import assigner, common, parser, reader
++
++
++def _ReadData(input_file):
++ if input_file == '-':
++ data = sys.stdin.read()
++ file_dir = os.getcwd()
++ else:
++ with open(input_file, 'rt') as f:
++ data = f.read()
++ file_dir = os.path.dirname(input_file)
++ return data, file_dir
++
++
++def _MultiReplace(data, repl):
++ """Multi-replacement of text |data| by ranges and replacement text.
++
++ Args:
++ data: Original text.
++ repl: List of (lo, hi, s) tuples, specifying that |data[lo:hi]| should be
++ replaced with |s|. The ranges must be inside |data|, and not overlap.
++ Returns: New text.
++ """
++ res = []
++ prev = 0
++ for (lo, hi, s) in sorted(repl):
++ if prev < lo:
++ res.append(data[prev:lo])
++ res.append(s)
++ prev = hi
++ res.append(data[prev:])
++ return ''.join(res)
++
++
++def _WriteFileIfChanged(output, new_data):
++ if not output:
++ sys.stdout.write(new_data)
++ return
++
++ # Avoid touching outputs if file contents has not changed so that ninja
++ # does not rebuild dependent when not necessary.
++ if os.path.exists(output) and _ReadData(output)[0] == new_data:
++ return
++
++ # Write to a temporary file to ensure atomic changes.
++ with tempfile.NamedTemporaryFile('wt', delete=False) as f:
++ f.write(new_data)
++ shutil.move(f.name, output)
++
++
++class _Args:
++ """Encapsulated arguments for this module."""
++ def __init__(self):
++ self.add_header = False
++ self.analyze_inputs = False
++ self.count = False
++ self.depfile = None
++ self.fake = False
++ self.input = None
++ self.naive = False
++ self.output = None
++ self.parse = False
++ self.tokenize = False
++
++ @staticmethod
++ def Parse(raw_args):
++ own_opts, raw_args = getopt.getopt(raw_args, 'o:cpt', [
++ 'add-header',
++ 'analyze-inputs',
++ 'count',
++ 'depfile=',
++ 'fake',
++ 'naive',
++ 'parse',
++ 'tokenize',
++ ])
++ args = _Args();
++ if not len(raw_args) == 1:
++ print('grit update_resource_ids takes exactly one argument, the path to '
++ 'the resource ids file.')
++ return 2
++ args.input = raw_args[0]
++ for (key, val) in own_opts:
++ if key == '-o':
++ args.output = val
++ elif key == '--add-header':
++ args.add_header = True
++ elif key == '--analyze-inputs':
++ args.analyze_inputs = True
++ elif key in ('--count', '-c'):
++ args.count = True
++ elif key == '--depfile':
++ args.depfile = val
++ elif key == '--fake':
++ args.fake = True
++ elif key == '--naive':
++ args.naive = True
++ elif key in ('--parse', '-p'):
++ args.parse = True
++ elif key in ('--tokenize', '-t'):
++ args.tokenize = True
++ return args
++
++
++class UpdateResourceIds(interface.Tool):
++ """Updates all start IDs in an resource_ids file by reading all GRD files it
++refers to, estimating the number of required IDs of each type, then rewrites
++start IDs while preserving structure.
++
++Usage: grit update_resource_ids [--parse|-p] [--read-grd|-r] [--tokenize|-t]
++ [--naive] [--fake] [-o OUTPUT_FILE]
++ [--analyze-inputs] [--depfile DEPFILE]
++ [--add-header] RESOURCE_IDS_FILE
++
++RESOURCE_IDS_FILE is the path of the input resource_ids file.
++
++The -o option redirects output (default stdout) to OUPTUT_FILE, which can also
++be RESOURCE_IDS_FILE.
++
++Other options:
++
++ -E NAME=VALUE Sets environment variable NAME to VALUE (within grit).
++
++ --count|-c Parses RESOURCE_IDS_FILE, reads the GRD files, and prints
++ required sizes.
++
++ --fake For testing: Skips reading GRD files, and assigns 10 as the
++ usage of every tag.
++
++ --naive Use naive coarse assigner.
++
++ --parse|-p Parses RESOURCE_IDS_FILE and dumps its nodes to console.
++
++ --tokenize|-t Tokenizes RESOURCE_IDS_FILE and reprints it as syntax-
++ highlighted output.
++
++ --depfile=DEPFILE Write out a depfile for ninja to know about dependencies.
++ --analyze-inputs Writes dependencies to stdout.
++ --add-header Adds a "THIS FILE IS GENERATED" header to the output.
++"""
++
++ def __init(self):
++ super(UpdateResourceIds, self).__init__()
++
++ def ShortDescription(self):
++ return 'Updates a resource_ids file based on usage, preserving structure'
++
++ def _DumpTokens(self, data, tok_gen):
++ # Reprint |data| with syntax highlight.
++ color_map = {
++ '#': common.Color.GRAY,
++ 'S': common.Color.CYAN,
++ '0': common.Color.RED,
++ '{': common.Color.YELLOW,
++ '}': common.Color.YELLOW,
++ '[': common.Color.GREEN,
++ ']': common.Color.GREEN,
++ ':': common.Color.MAGENTA,
++ ',': common.Color.MAGENTA,
++ }
++ for t, lo, hi in tok_gen:
++ c = color_map.get(t, common.Color.NONE)
++ sys.stdout.write(c(data[lo:hi]))
++
++ def _DumpRootObj(self, root_obj):
++ print(root_obj)
++
++ def _DumpResourceCounts(self, usage_gen):
++ tot = collections.Counter()
++ for item, tag_name_to_usage in usage_gen:
++ c = common.Color.YELLOW if item.grd.startswith('<') else common.Color.CYAN
++ print('%s: %r' % (c(item.grd), dict(tag_name_to_usage)))
++ tot += collections.Counter(tag_name_to_usage)
++ print(common.Color.GRAY('-' * 80))
++ print('%s: %r' % (common.Color.GREEN('Total'), dict(tot)))
++ print('%s: %d' % (common.Color.GREEN('Grand Total'), sum(tot.values())))
++
++ def Run(self, opts, raw_args):
++ self.SetOptions(opts)
++
++ args = _Args.Parse(raw_args)
++ data, file_dir = _ReadData(args.input)
++
++ tok_gen = parser.Tokenize(data)
++ if args.tokenize:
++ return self._DumpTokens(data, tok_gen)
++
++ root_obj = parser.ResourceIdParser(data, tok_gen).Parse()
++ if args.parse:
++ return self._DumpRootObj(root_obj)
++ item_list = common.BuildItemList(root_obj)
++
++ src_dir = os.path.normpath(os.path.join(file_dir, root_obj['SRCDIR'].val))
++ seen_files = set()
++ usage_gen = reader.GenerateResourceUsages(item_list, src_dir, args.fake,
++ seen_files)
++ if args.count:
++ return self._DumpResourceCounts(usage_gen)
++ for item, tag_name_to_usage in usage_gen:
++ item.SetUsages(tag_name_to_usage)
++
++ if args.analyze_inputs:
++ print('\n'.join(sorted(seen_files)))
++ return 0
++
++ new_ids_gen = assigner.GenerateNewIds(item_list, args.naive)
++ # Create replacement specs usable by _MultiReplace().
++ repl = [(tag.lo, tag.hi, str(new_id)) for tag, new_id in new_ids_gen]
++ rel_input_dir = args.input
++ # Update "SRCDIR" entry if output is specified.
++ if args.output:
++ new_srcdir = os.path.relpath(src_dir, os.path.dirname(args.output))
++ repl.append((root_obj['SRCDIR'].lo, root_obj['SRCDIR'].hi,
++ repr(new_srcdir)))
++ rel_input_dir = os.path.join('$SRCDIR',
++ os.path.relpath(rel_input_dir, new_srcdir))
++
++ new_data = _MultiReplace(data, repl)
++ if args.add_header:
++ header = []
++ header.append('# GENERATED FILE.')
++ header.append('# Edit %s instead.' % rel_input_dir)
++ header.append('#' * 80)
++ new_data = '\n'.join(header + ['']) + new_data
++ _WriteFileIfChanged(args.output, new_data)
++
++ if args.depfile:
++ deps_data = '{}: {}'.format(args.output, ' '.join(sorted(seen_files)))
++ _WriteFileIfChanged(args.depfile, deps_data)
+diff --git a/tools/grit/grit/tool/update_resource_ids/assigner.py b/tools/grit/grit/tool/update_resource_ids/assigner.py
+new file mode 100644
+index 0000000000..6cd46031a6
--- /dev/null
-+++ b/tools/clang/scripts/update.sh
++++ b/tools/grit/grit/tool/update_resource_ids/assigner.py
@@ -0,0 +1,286 @@
-+#!/usr/bin/env bash
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+# This script will check out llvm and clang into third_party/llvm and build it.
-+
-+# Do NOT CHANGE this if you don't know what you're doing -- see
-+# https://code.google.com/p/chromium/wiki/UpdatingClang
-+# Reverting problematic clang rolls is safe, though.
-+CLANG_REVISION=163674
-+
-+THIS_DIR="$(dirname "${0}")"
-+LLVM_DIR="${THIS_DIR}/../../../third_party/llvm"
-+LLVM_BUILD_DIR="${LLVM_DIR}/../llvm-build"
-+LLVM_BOOTSTRAP_DIR="${LLVM_DIR}/../llvm-bootstrap"
-+CLANG_DIR="${LLVM_DIR}/tools/clang"
-+COMPILER_RT_DIR="${LLVM_DIR}/projects/compiler-rt"
-+STAMP_FILE="${LLVM_BUILD_DIR}/cr_build_revision"
-+
-+# ${A:-a} returns $A if it's set, a else.
-+LLVM_REPO_URL=${LLVM_URL:-https://llvm.org/svn/llvm-project}
-+
-+# Die if any command dies.
-+set -e
-+
-+OS="$(uname -s)"
-+
-+# Parse command line options.
-+force_local_build=
-+mac_only=
-+run_tests=
-+bootstrap=
-+while [[ $# > 0 ]]; do
-+ case $1 in
-+ --bootstrap)
-+ bootstrap=yes
-+ ;;
-+ --force-local-build)
-+ force_local_build=yes
-+ ;;
-+ --mac-only)
-+ mac_only=yes
-+ ;;
-+ --run-tests)
-+ run_tests=yes
-+ ;;
-+ --help)
-+ echo "usage: $0 [--force-local-build] [--mac-only] [--run-tests] "
-+ echo "--bootstrap: First build clang with CC, then with itself."
-+ echo "--force-local-build: Don't try to download prebuilt binaries."
-+ echo "--mac-only: Do initial download only on Mac systems."
-+ echo "--run-tests: Run tests after building. Only for local builds."
-+ exit 1
-+ ;;
-+ esac
-+ shift
-+done
-+
-+# --mac-only prevents the initial download on non-mac systems, but if clang has
-+# already been downloaded in the past, this script keeps it up to date even if
-+# --mac-only is passed in and the system isn't a mac. People who don't like this
-+# can just delete their third_party/llvm-build directory.
-+if [[ -n "$mac_only" ]] && [[ "${OS}" != "Darwin" ]] &&
-+ [[ "$GYP_DEFINES" != *clang=1* ]] && ! [[ -d "${LLVM_BUILD_DIR}" ]]; then
-+ exit 0
-+fi
-+
-+# Xcode and clang don't get along when predictive compilation is enabled.
-+# http://crbug.com/96315
-+if [[ "${OS}" = "Darwin" ]] && xcodebuild -version | grep -q 'Xcode 3.2' ; then
-+ XCONF=com.apple.Xcode
-+ if [[ "${GYP_GENERATORS}" != "make" ]] && \
-+ [ "$(defaults read "${XCONF}" EnablePredictiveCompilation)" != "0" ]; then
-+ echo
-+ echo " HEARKEN!"
-+ echo "You're using Xcode3 and you have 'Predictive Compilation' enabled."
-+ echo "This does not work well with clang (http://crbug.com/96315)."
-+ echo "Disable it in Preferences->Building (lower right), or run"
-+ echo " defaults write ${XCONF} EnablePredictiveCompilation -boolean NO"
-+ echo "while Xcode is not running."
-+ echo
-+ fi
-+
-+ SUB_VERSION=$(xcodebuild -version | sed -Ene 's/Xcode 3\.2\.([0-9]+)/\1/p')
-+ if [[ "${SUB_VERSION}" < 6 ]]; then
-+ echo
-+ echo " YOUR LD IS BUGGY!"
-+ echo "Please upgrade Xcode to at least 3.2.6."
-+ echo
-+ fi
-+fi
-+
-+
-+# Check if there's anything to be done, exit early if not.
-+if [[ -f "${STAMP_FILE}" ]]; then
-+ PREVIOUSLY_BUILT_REVISON=$(cat "${STAMP_FILE}")
-+ if [[ -z "$force_local_build" ]] && \
-+ [[ "${PREVIOUSLY_BUILT_REVISON}" = "${CLANG_REVISION}" ]]; then
-+ echo "Clang already at ${CLANG_REVISION}"
-+ exit 0
-+ fi
-+fi
-+# To always force a new build if someone interrupts their build half way.
-+rm -f "${STAMP_FILE}"
-+
-+# Clobber pch files, since they only work with the compiler version that
-+# created them. Also clobber .o files, to make sure everything will be built
-+# with the new compiler.
-+if [[ "${OS}" = "Darwin" ]]; then
-+ XCODEBUILD_DIR="${THIS_DIR}/../../../xcodebuild"
-+
-+ # Xcode groups .o files by project first, configuration second.
-+ if [[ -d "${XCODEBUILD_DIR}" ]]; then
-+ echo "Clobbering .o files for Xcode build"
-+ find "${XCODEBUILD_DIR}" -name '*.o' -exec rm {} +
-+ fi
-+fi
-+
-+if [ -f "${THIS_DIR}/../../../WebKit.gyp" ]; then
-+ # We're inside a WebKit checkout.
-+ # TODO(thakis): try to unify the directory layout of the xcode- and
-+ # make-based builds. http://crbug.com/110455
-+ MAKE_DIR="${THIS_DIR}/../../../../../../out"
-+else
-+ # We're inside a Chromium checkout.
-+ MAKE_DIR="${THIS_DIR}/../../../out"
-+fi
-+
-+for CONFIG in Debug Release; do
-+ if [[ -d "${MAKE_DIR}/${CONFIG}/obj.target" ||
-+ -d "${MAKE_DIR}/${CONFIG}/obj.host" ]]; then
-+ echo "Clobbering ${CONFIG} PCH and .o files for make build"
-+ if [[ -d "${MAKE_DIR}/${CONFIG}/obj.target" ]]; then
-+ find "${MAKE_DIR}/${CONFIG}/obj.target" -name '*.gch' -exec rm {} +
-+ find "${MAKE_DIR}/${CONFIG}/obj.target" -name '*.o' -exec rm {} +
-+ fi
-+ if [[ -d "${MAKE_DIR}/${CONFIG}/obj.host" ]]; then
-+ find "${MAKE_DIR}/${CONFIG}/obj.host" -name '*.o' -exec rm {} +
-+ fi
-+ fi
-+
-+ # ninja puts its output below ${MAKE_DIR} as well.
-+ if [[ -d "${MAKE_DIR}/${CONFIG}/obj" ]]; then
-+ echo "Clobbering ${CONFIG} PCH and .o files for ninja build"
-+ find "${MAKE_DIR}/${CONFIG}/obj" -name '*.gch' -exec rm {} +
-+ find "${MAKE_DIR}/${CONFIG}/obj" -name '*.o' -exec rm {} +
-+ find "${MAKE_DIR}/${CONFIG}/obj" -name '*.o.d' -exec rm {} +
-+ fi
-+
-+ if [[ "${OS}" = "Darwin" ]]; then
-+ if [[ -d "${XCODEBUILD_DIR}/${CONFIG}/SharedPrecompiledHeaders" ]]; then
-+ echo "Clobbering ${CONFIG} PCH files for Xcode build"
-+ rm -rf "${XCODEBUILD_DIR}/${CONFIG}/SharedPrecompiledHeaders"
-+ fi
-+ fi
-+done
-+
-+if [[ -z "$force_local_build" ]]; then
-+ # Check if there's a prebuilt binary and if so just fetch that. That's faster,
-+ # and goma relies on having matching binary hashes on client and server too.
-+ CDS_URL=https://commondatastorage.googleapis.com/chromium-browser-clang
-+ CDS_FILE="clang-${CLANG_REVISION}.tgz"
-+ CDS_OUT_DIR=$(mktemp -d -t clang_download.XXXXXX)
-+ CDS_OUTPUT="${CDS_OUT_DIR}/${CDS_FILE}"
-+ if [ "${OS}" = "Linux" ]; then
-+ CDS_FULL_URL="${CDS_URL}/Linux_x64/${CDS_FILE}"
-+ elif [ "${OS}" = "Darwin" ]; then
-+ CDS_FULL_URL="${CDS_URL}/Mac/${CDS_FILE}"
-+ fi
-+ echo Trying to download prebuilt clang
-+ if which curl > /dev/null; then
-+ curl -L --fail "${CDS_FULL_URL}" -o "${CDS_OUTPUT}" || \
-+ rm -rf "${CDS_OUT_DIR}"
-+ elif which wget > /dev/null; then
-+ wget "${CDS_FULL_URL}" -O "${CDS_OUTPUT}" || rm -rf "${CDS_OUT_DIR}"
-+ else
-+ echo "Neither curl nor wget found. Please install one of these."
-+ exit 1
-+ fi
-+ if [ -f "${CDS_OUTPUT}" ]; then
-+ rm -rf "${LLVM_BUILD_DIR}/Release+Asserts"
-+ mkdir -p "${LLVM_BUILD_DIR}/Release+Asserts"
-+ tar -xzf "${CDS_OUTPUT}" -C "${LLVM_BUILD_DIR}/Release+Asserts"
-+ echo clang "${CLANG_REVISION}" unpacked
-+ echo "${CLANG_REVISION}" > "${STAMP_FILE}"
-+ rm -rf "${CDS_OUT_DIR}"
-+ exit 0
-+ else
-+ echo Did not find prebuilt clang at r"${CLANG_REVISION}", building
-+ fi
-+fi
-+
-+echo Getting LLVM r"${CLANG_REVISION}" in "${LLVM_DIR}"
-+if ! svn co --force "${LLVM_REPO_URL}/llvm/trunk@${CLANG_REVISION}" \
-+ "${LLVM_DIR}"; then
-+ echo Checkout failed, retrying
-+ rm -rf "${LLVM_DIR}"
-+ svn co --force "${LLVM_REPO_URL}/llvm/trunk@${CLANG_REVISION}" "${LLVM_DIR}"
-+fi
-+
-+echo Getting clang r"${CLANG_REVISION}" in "${CLANG_DIR}"
-+svn co --force "${LLVM_REPO_URL}/cfe/trunk@${CLANG_REVISION}" "${CLANG_DIR}"
-+
-+echo Getting compiler-rt r"${CLANG_REVISION}" in "${COMPILER_RT_DIR}"
-+svn co --force "${LLVM_REPO_URL}/compiler-rt/trunk@${CLANG_REVISION}" \
-+ "${COMPILER_RT_DIR}"
-+
-+# Echo all commands.
-+set -x
-+
-+NUM_JOBS=3
-+if [[ "${OS}" = "Linux" ]]; then
-+ NUM_JOBS="$(grep -c "^processor" /proc/cpuinfo)"
-+elif [ "${OS}" = "Darwin" ]; then
-+ NUM_JOBS="$(sysctl -n hw.ncpu)"
-+fi
-+
-+# Build bootstrap clang if requested.
-+if [[ -n "${bootstrap}" ]]; then
-+ echo "Building bootstrap compiler"
-+ mkdir -p "${LLVM_BOOTSTRAP_DIR}"
-+ cd "${LLVM_BOOTSTRAP_DIR}"
-+ if [[ ! -f ./config.status ]]; then
-+ # The bootstrap compiler only needs to be able to build the real compiler,
-+ # so it needs no cross-compiler output support. In general, the host
-+ # compiler should be as similar to the final compiler as possible, so do
-+ # keep --disable-threads & co.
-+ ../llvm/configure \
-+ --enable-optimized \
-+ --enable-targets=host-only \
-+ --disable-threads \
-+ --disable-pthreads \
-+ --without-llvmgcc \
-+ --without-llvmgxx
-+ MACOSX_DEPLOYMENT_TARGET=10.5 make -j"${NUM_JOBS}"
-+ fi
-+ if [[ -n "${run_tests}" ]]; then
-+ make check-all
-+ fi
-+ cd -
-+ export CC="${PWD}/${LLVM_BOOTSTRAP_DIR}/Release+Asserts/bin/clang"
-+ export CXX="${PWD}/${LLVM_BOOTSTRAP_DIR}/Release+Asserts/bin/clang++"
-+ echo "Building final compiler"
-+fi
-+
-+# Build clang (in a separate directory).
-+# The clang bots have this path hardcoded in built/scripts/slave/compile.py,
-+# so if you change it you also need to change these links.
-+mkdir -p "${LLVM_BUILD_DIR}"
-+cd "${LLVM_BUILD_DIR}"
-+if [[ ! -f ./config.status ]]; then
-+ ../llvm/configure \
-+ --enable-optimized \
-+ --disable-threads \
-+ --disable-pthreads \
-+ --without-llvmgcc \
-+ --without-llvmgxx
-+fi
-+
-+MACOSX_DEPLOYMENT_TARGET=10.5 make -j"${NUM_JOBS}"
-+cd -
-+
-+# Build plugin.
-+# Copy it into the clang tree and use clang's build system to compile the
-+# plugin.
-+PLUGIN_SRC_DIR="${THIS_DIR}/../plugins"
-+PLUGIN_DST_DIR="${LLVM_DIR}/tools/clang/tools/chrome-plugin"
-+PLUGIN_BUILD_DIR="${LLVM_BUILD_DIR}/tools/clang/tools/chrome-plugin"
-+rm -rf "${PLUGIN_DST_DIR}"
-+cp -R "${PLUGIN_SRC_DIR}" "${PLUGIN_DST_DIR}"
-+rm -rf "${PLUGIN_BUILD_DIR}"
-+mkdir -p "${PLUGIN_BUILD_DIR}"
-+cp "${PLUGIN_SRC_DIR}/Makefile" "${PLUGIN_BUILD_DIR}"
-+MACOSX_DEPLOYMENT_TARGET=10.5 make -j"${NUM_JOBS}" -C "${PLUGIN_BUILD_DIR}"
-+
-+if [[ -n "$run_tests" ]]; then
-+ # Run a few tests.
-+ "${PLUGIN_SRC_DIR}/tests/test.sh" "${LLVM_BUILD_DIR}/Release+Asserts"
-+ cd "${LLVM_BUILD_DIR}"
-+ make check-all
-+ cd -
-+fi
-+
-+# After everything is done, log success for this revision.
-+echo "${CLANG_REVISION}" > "${STAMP_FILE}"
++# 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.
++"""Assign IDs to resource_ids file based on usage, while preserving structure.
++
++resource_ids assignment is divided into two parts:
++(A) Coarse assignment: Assigns start IDs of items.
++(B) Quota assignment: Assigns per-tag ID allotments for a given item, allowing
++ padding for IDs.
++
++These parts are interdependent: Start IDs (A) of an item depends on ID
++allotments (B) of *other* items; and ID allotment (B) of an item depends on its
++start IDs (A) to compute alignment.
++
++(B) hides padding and alignment details of tags so that (A) can be abstracted
++into the graph construction and traversal problem in DagCoarseIdAssigner.
++"""
++
++import math
++
++from grit.tool.update_resource_ids import common
++
++
++class Aligner:
++ """Helper to allot IDs, given start ID and ID usage.
++
++ Args:
++ expand: Scale factor relative to ID usage. Must be >= 1.0.
++ slack: Minimum number of reserved ID at end. Must be >= 0.
++ align: ID alignment of results. Must be >= 1.
++ """
++
++ def __init__(self, expand=1.0, slack=0, align=1):
++ assert expand >= 1.0 and slack >= 0 and align >= 1
++ self._expand = expand
++ self._slack = slack
++ self._align = align
++
++ def Calc(self, cur_id, usage):
++ quota = max(int(math.ceil(usage * self._expand)), usage + self._slack)
++ return common.AlignUp(cur_id + quota, self._align)
++
++
++class QuotaAssigner:
++ """Main class for (B), for ID allotment of tags for an item."""
++
++ def __init__(self, aligner):
++ self._aligner = aligner
++
++ def Gen(self, item, start_id):
++ """Generates per-tag *end* ID in |item|, succeeding |start_id|."""
++ cur_id = start_id
++ for tag in item.tags: # Sorted by |tag.lo|.
++ cur_id = self._aligner.Calc(cur_id, tag.usage)
++ yield tag, cur_id
++
++
++class BaseCoarseIdAssigner(object):
++ """Base class for coarse assignment."""
++
++ def __init__(self, item_list, align):
++ self._item_list = item_list
++ self._align = align
++
++ def GenStartIds(self):
++ """Visits |_item_list| and yields (|item|, new |start_id|).
++
++ Visit follows dependency order: If item B succeeds item A, then A is visited
++ before B. Caller must call FeedWeight() to assign ID allotment.
++ """
++ raise NotImplementedError()
++
++ def FeedWeight(self, item, weight):
++ """Callback to assign number of IDs allotted to |item|."""
++ raise NotImplementedError()
++
++
++class NaiveCoarseIdAssigner(BaseCoarseIdAssigner):
++ """CoarseIdAssigner that assigns item with non-overlapping start IDs."""
++
++ def __init__(self, item_list, align):
++ super(NaiveCoarseIdAssigner, self).__init__(item_list, align)
++ first_id = self._item_list[0].tags[0].id
++ self._cur_id = common.AlignUp(first_id, self._align)
++
++ def GenStartIds(self):
++ """Visits items in array order."""
++ for item in self._item_list:
++ yield item, self._cur_id
++
++ def FeedWeight(self, item, weight):
++ self._cur_id = common.AlignUp(self._cur_id + weight, self._align)
++
++
++class DagCoarseIdAssigner(BaseCoarseIdAssigner):
++ """CoarseIdAssigner that preserves existing structure.
++
++Start ID assignment in resource_ids is structured a Series-Parallel Graph, which
++is a directed, acyclic multi-graph generated by the following:
++* Start: Single directed edge. S <-- T.
++* Operation 1: A <-- B becomes A <-- C <-- B.
++* Operation 2: A <-- B becomes A <== B (add parallel edge).
++
++Each vertex (A, B, ...) is a start ID. S = globally minimal ID. T = \infty is an
++implicit sentinel. Each edge maps to an item, and edge weight is ID allotment.
++The edge A <-- B means "A's ID assignment needs to be determined before B's"
++(i.e., "B depends on A"), and requires A < B.
++
++resource_ids stores a "flattened" representation of the graph, as a list of
++items (with meta data). Thus coarse ID assignment consists of the following:
++(1) Process list of items (with old start ID and meta data) to rebuild graph.
++(2) Traverse graph in an order that satisfies dependencies.
++(3) When vertex A has its ID assigned, the weight of each edge "A <--" (i.e., an
++ item) can have its weight (ID allotment) computed via quota assignment of A.
++(4) New start IDs satisfy A + w <= B for each edge A <-- B with weight w > 0.
++
++The key algorithm challenge is (1). Note that it does not need weight details,
++so we only assume A < B whenever A <-- B. Now we're faced with 2 subproblems:
++(1a) How to represent the graph as a list of integers (with meta data)?
++(1b) Given the list representation, how to recover the graph?
++
++For (1a), we start with DFS traversal of the (transposed, i.e., reversed) graph
++starting from S, and apply the following:
++* For each edge A <-- B traversed, emit A into sequence,
++* Traverse a B <-- Y only when all X <-- B have been traversed.
++
++The resulting sequence has the length as the number of edges, and has the useful
++property that a vertex's dependencies always appear before the vertex itself!
++Note this the sentinel T is omitted.
++
++Example 1:
++ S <-- A <-- B <-- C <-- T => "SABC".
++
++Example 2:
++ S <-- A <-- B <-- C <-- T => "SA|AB|SDEC|SF",
++ | | | | | or "SF|SA|AB|SDEC",
++ | + <-- + | | or "SDE|SA|ABC|SF",
++ | | | or "SF|SDE|SA|ABC".
++ + <---D <-- E <---+ |
++ | |
++ + <-- F <---------------+
++
++Here, "X|Y" denotes backtracking between visiting X and visiting Y. This appears
++if and only if X >= Y, so "|" an optional (but illustrative) character that's
++not in the actual output. We will use it consistently in comments, and so the
++absence of "|" denotes the converse. For example, "XY" implies X < Y.
++
++In terms of the basic operations:
++* Start: S <-- T => "S".
++* Operation 1: "...AB..." => "...ACB..." (or "...A" => "...AB").
++* Operation 2: "...AB..." => "...A|AB..." (or "...A" => "...A|A").
++
++For Example 2, a viable "evolution path" is:
++"S" => "S|S" => "SC|S" => "S|SC|S" => "SA|SC|S" => "SAB|SC|S" => "SA|AB|SC|S"
++ => "SA|AB|SDC|S" => "SA|AB|SDEC|S" => "SA|AB|SDEC|SF".
++(Alternative: "S|S" => "S|SC" => etc.).
++
++Note: {A, ...} are *unlabelled* integers, and "spurious equalities" such as
++A = D or A = F can occur!
++
++For (1b), we wish to build the graph from the sequence. This requires (1a) to be
++injective (up to isomorphism). Unfortunately, this does not always hold.
++Example:
++ S <-- A <-- C <-- D <-- T => "SA|SBCD".
++ | |
++ + <-- B <---+
++vs.
++ S <----- A <----- D <-- T => "SA|SBCD".
++ | |
++ + <-- B <-- C <---+
++
++To fix this, we prepend a "join" label (*) to each vertex that has multiple
++dependencies. With this, the example above produce different results:
++ "SA|SB*CD" != "SA|SBC*D".
++
++Unfortunately, this is also inadequate. Example:
++ S <-------- B <-- T => "S|S|S|S*A*B",
++ | |
++ + <---------+
++ | |
++ + <-- A <---+
++ | |
++ + <---+
++vs.
++ S <-------- B <-- T => "S|S|S|S*A*B".
++ | |
++ + <---A <---+
++ | |
++ + <---+
++ | |
++ + <---+
++
++To fix this, we also label the number of dependencies. In text representation,
++we just show multiple (#dependencies - 1) copies of '*'. Now we have:
++ "S|S|S|S*A**B" != "S|S|S|S**A*B".
++
++The "join" label with count adequately addresses the issue (proof omitted). In
++the resource_ids files, these are stored as the "join" field of an item's meta
++data.
++
++Additional comments for (1b) and other steps are detailed below.
++"""
++
++ class DagNode:
++ """An element of the implicit graph, corresponding to an item.
++
++ This actually represents an edge-vertex pair, corresponding to an item.
++ A vertex is represented by a collection of DagNode that uses |sib| to link
++ to a "representative node". The representative node, in turn, holds the list
++ of all |deps| (dependencies) of the vertex.
++ """
++
++ def __init__(self, item, old_id):
++ self.item = item
++ self.old_id = old_id
++ self.new_id = None
++ self.weight = None
++
++ def __init__(self, item_list, align):
++ super(DagCoarseIdAssigner, self).__init__(item_list, align)
++ self._node_dict = {} # Maps from |lo| to item.
++
++ def GenStartIds(self):
++ """Traverses implicit graph and yields new IDs.
++
++ Algorithm: Process |old_id| of items sequentially. Successive items A and B
++ can be "AB" (A < B), "A*...B" (A < B), or "A|B" (A >= B). "AB" and "A*...B"
++ imply A <-- B, and are accumulated in |trace|. "A|B" are jumps that rewinds
++ |trace| to the latest B (must exist), and A is pushed into |jumps|. A join
++ "A*...B" also pops |num_join - 1| items {X_i} in |jump|, and X_i <-- B. In
++ the end, unprocessed elements in |jumps| all link to sentinel T, and can be
++ ignored.
++ """
++ # DagNode stack of "A" when "AB" is found (increasing |old_id|).
++ trace = []
++ # DagNode stack of "A" when "A|B" jumps is found.
++ jumps = []
++ for item in self._item_list: # Sorted by |lo|.
++ meta = item.meta
++ # |num_join| indicates "*" in "A*...B", and specify B's dependencies: +1
++ # from A, and +count("*") from |jumps|.
++ num_join = meta['join'].val if meta and 'join' in meta else None
++ node = DagCoarseIdAssigner.DagNode(item, item.tags[0].id)
++ self._node_dict[item.lo] = node
++ if trace:
++ if trace[-1].old_id >= node.old_id: # "A|B".
++ if num_join:
++ raise ValueError('Cannot join on jump: %d' % node.old_id)
++ jumps.append(trace[-1]) # Add A to |jumps|, for later join.
++ while trace and trace[-1].old_id > node.old_id: # Rewind to find B.
++ trace.pop()
++ if not trace or trace[-1].old_id != node.old_id: #
++ raise ValueError('Cannot jump to unvisited: %d' % node.old_id)
++ node.new_id = trace.pop().new_id # Copy B & remove. Will re-add B.
++ else: # "AB" or "A*...B".
++ node.new_id = trace[-1].new_id + trace[-1].weight # A --> B
++ if num_join: # "A*...B".
++ for _ in range(1, num_join):
++ t = jumps.pop()
++ node.new_id = max(node.new_id, t.new_id + t.weight) # X_i --> B.
++ else:
++ node.new_id = node.old_id # Initial S.
++ trace.append(node) # Add B.
++ align = meta['align'].val if meta and 'align' in meta else self._align
++ node.new_id = common.AlignUp(node.new_id, align)
++ yield node.item, node.new_id
++ # Expect caller to calling FreedWeight() and update |node.weight|.
++
++ def FeedWeight(self, item, weight):
++ self._node_dict[item.lo].weight = weight
++
++
++def GenerateNewIds(item_list, use_naive):
++ """Visits all tags in |item_list| and generates new ids.
++
++ New ids are generated based on old ids and usages.
++ """
++ Assigner = NaiveCoarseIdAssigner if use_naive else DagCoarseIdAssigner
++ coarse_id_assigner = Assigner(item_list, 10)
++ quota_assigner = QuotaAssigner(Aligner(expand=1.15, slack=3, align=10))
++ for item, start_id in coarse_id_assigner.GenStartIds(): # Topo-sorted.
++ cur_id = start_id
++ for tag, next_id in quota_assigner.Gen(item, start_id): # Sorted by |lo|.
++ yield tag, cur_id
++ cur_id = next_id
++ coarse_id_assigner.FeedWeight(item, next_id - start_id)
+diff --git a/tools/grit/grit/tool/update_resource_ids/assigner_unittest.py b/tools/grit/grit/tool/update_resource_ids/assigner_unittest.py
+new file mode 100644
+index 0000000000..164d820762
+--- /dev/null
++++ b/tools/grit/grit/tool/update_resource_ids/assigner_unittest.py
+@@ -0,0 +1,154 @@
++#!/usr/bin/env python
++# 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.
++
++from __future__ import print_function
++
++import os
++import sys
++import traceback
++import unittest
++if __name__ == '__main__':
++ sys.path.append(os.path.join(os.path.dirname(__file__), '../../..'))
++
++from grit.tool.update_resource_ids import assigner, common, parser
++
++# |spec| format: A comma-separated list of (old) start IDs. Modifiers:
++# * Prefix with n '*' to assign the item's META "join" field to n + 1.
++# * Suffix with "+[usage]" to assign |usage| for the item (else default=10)
++
++
++def _RenderTestResourceId(spec):
++ """Renders barebone resource_ids data based on |spec|."""
++ data = '{"SRCDIR": ".",'
++ for i, tok in enumerate(spec.split(',')):
++ num_star = len(tok) - len(tok.lstrip('*'))
++ tok = tok[num_star:]
++ meta = '"META":{"join": %d},' % (num_star + 1) if num_star else ''
++ start_id = tok.split('+')[0] # Strip '+usage'.
++ data += '"foo%d.grd": {%s"includes": [%s]},' % (i, meta, start_id)
++ data += '}'
++ return data
++
++
++def _CreateTestItemList(spec):
++ """Creates list of ItemInfo based on |spec|."""
++ data = _RenderTestResourceId(spec)
++ item_list = common.BuildItemList(
++ parser.ResourceIdParser(data, parser.Tokenize(data)).Parse())
++ # Assign usages from "id+usage", default to 10.
++ for i, tok in enumerate(spec.split(',')):
++ item_list[i].tags[0].usage = int((tok.split('+') + ['10'])[1])
++ return item_list
++
++
++def _RunCoarseIdAssigner(spec):
++ item_list = _CreateTestItemList(spec)
++ coarse_id_assigner = assigner.DagCoarseIdAssigner(item_list, 1)
++ new_id_list = [] # List of new IDs, to check ID assignment.
++ new_spec_list = [] # List of new tokens to construct new |spec|.
++ for item, start_id in coarse_id_assigner.GenStartIds(): # Topo-sorted..
++ new_id_list.append(str(start_id))
++ meta = item.meta
++ num_join = meta['join'].val if meta and 'join' in meta else 0
++ t = '*' * max(0, num_join - 1)
++ t += str(start_id)
++ t += '' if item.tags[0].usage == 10 else '+' + str(item.tags[0].usage)
++ new_spec_list.append((item.lo, t))
++ coarse_id_assigner.FeedWeight(item, item.tags[0].usage)
++ new_spec = ','.join(s for _, s in sorted(new_spec_list))
++ return ','.join(new_id_list), new_spec
++
++
++class AssignerUnittest(unittest.TestCase):
++
++ def testDagAssigner(self):
++ test_cases = [
++ # Trivial.
++ ('0', '0'),
++ ('137', '137'),
++ ('5,15', '5,6'),
++ ('11,18', '11+7,12'),
++ ('5,5', '5,5'),
++ # Series only.
++ ('0,10,20,30,40', '0,1,2,3,4'),
++ ('5,15,25,35,45,55', '5,6,7,8,9,10'),
++ ('5,15,25,35,45,55', '5,7,100,101,256,1001'),
++ ('0,10,20,45,85', '0,1,2+25,3+40,4'),
++ # Branching with and without join.
++ ('0,0,10,20,20,30,40', '0,0,1,2,2,3,4'),
++ ('0,0,10,20,20,30,40', '0,0,*1,2,2,*3,4'),
++ ('0,0,2,12,12,16,26', '0+4,0+2,1,2+8,2+4,3,4'),
++ ('0,0,4,14,14,22,32', '0+4,0+2,*1,2+8,2+4,*3,4'),
++ # Wide branching with and without join.
++ ('0,10,10,10,10,10,10,20,30', '0,1,1,1,1,1,1,2,3'),
++ ('0,10,10,10,10,10,10,20,30', '0,1,1,1,1,1,1,*****2,3'),
++ ('0,2,2,2,2,2,2,7,17', '0+2,1+4,1+19,1,1+4,1+2,1+5,2,3'),
++ ('0,2,2,2,2,2,2,21,31', '0+2,1+4,1+19,1,1+4,1+2,1+5,*****2,3'),
++ # Expanding different branch, without join.
++ ('0,10,10,10,60,70,80', '0,1+15,1+15,1+50,2,3,4'),
++ ('0,10,10,10,25,35,45', '0,1+15,1+50,1+15,2,3,4'),
++ ('0,10,10,10,25,35,45', '0,1+50,1+15,1+15,2,3,4'),
++ # ... with join.
++ ('0,10,10,10,60,70,80', '0,1+15,1+15,1+50,**2,3,4'),
++ ('0,10,10,10,60,70,80', '0,1+15,1+50,1+15,**2,3,4'),
++ ('0,10,10,10,60,70,80', '0,1+50,1+15,1+15,**2,3,4'),
++ # ... with alternative join.
++ ('0,10,10,10,60,70,80', '0,1+15,1+15,1+50,2,**3,4'),
++ ('0,10,10,10,25,60,70', '0,1+15,1+50,1+15,2,**3,4'),
++ ('0,10,10,10,25,60,70', '0,1+50,1+15,1+15,2,**3,4'),
++ # Examples from assigner.py.
++ ('0,10,10,20,0,10,20,30,0,10',
++ '0,1,1,*2,0,4,5,*6,0,7'), # SA|AB|SDEC|SF
++ ('0,10,0,10,20,30', '0,1,0,2,*3,4'), # SA|SB*CD
++ ('0,10,0,10,20,30', '0,1,0,2,3,*4'), # SA|SBC*D
++ ('0,7,0,5,11,21', '0+7,1+4,0+5,2+3,*3,4'), # SA|SB*CD
++ ('0,7,0,5,8,18', '0+7,1+4,0+5,2+3,3,*4'), # SA|SBC*D
++ ('0,0,0,0,10,20', '0,0,0,0,*1,**2'), # S|S|S|S*A**B
++ ('0,0,0,0,10,20', '0,0,0,0,**1,*2'), # S|S|S|S**A*B
++ ('0,0,0,0,6,16', '0+8,0+7,0+6,0+5,*1,**2'), # S|S|S|S*A**B
++ ('0,0,0,0,7,17', '0+8,0+7,0+6,0+5,**1,*2'), # S|S|S|S**A*B
++ # Long branches without join.
++ ('0,10,0,0,10,20,0,10,20,30', '0,1,0,0,1,2,0,1,2,3'),
++ ('0,30,0,0,20,30,0,10,13,28', '0+30,1,0+50,0+20,1,2+17,0,1+3,2+15,3'),
++ # Long branches with join.
++ ('0,10,0,0,10,20,0,10,20,30', '0,1,0,0,1,2,0,1,2,***3'),
++ ('0,30,0,0,20,30,0,10,13,50',
++ '0+30,1,0+50,0+20,1,2+17,0,1+3,2+15,***3'),
++ # 2-level hierarchy.
++ ('0,10,10,20,0,10,10,20,30', '0,1,1,*2,0,1,1,*2,*3'),
++ ('0,2,2,10,0,3,3,6,34', '0+2,1+5,1+8,*2+24,0+3,1+2,1+3,*2+27,*3'),
++ ('0,2,2,10,0,3,3,6,34', '0+2,1+5,1+8,*2+24,0+3,1+2,1+3,*2+28,*3'),
++ ('0,2,2,10,0,3,3,6,35', '0+2,1+5,1+8,*2+24,0+3,1+2,1+3,*2+29,*3'),
++ # Binary hierarchy.
++ ('0,0,10,0,0,10,20,0,0,10,0,0,10,20,30',
++ '0,0,*1,0,0,*1,*2,0,0,*1,0,0,*1,*2,*3'),
++ ('0,0,2,0,0,5,11,0,0,8,0,0,5,14,18',
++ '0+1,0+2,*1+3,0+4,0+5,*1+6,*2+7,0+8,0+7,*1+6,0+5,0+4,*1+3,*2+2,*3+1'),
++ # Joining from different heads.
++ ('0,10,20,30,40,30,20,10,0,50', '0,1,2,3,4,3,2,1,0,****5'),
++ # Miscellaneous.
++ ('0,1,0,11', '0+1,1,0,*1'),
++ ]
++ for exp, spec in test_cases:
++ try:
++ actual, new_spec = _RunCoarseIdAssigner(spec)
++ self.failUnlessEqual(exp, actual)
++ # Test that assignment is idempotent.
++ actual2, new_spec2 = _RunCoarseIdAssigner(new_spec)
++ self.failUnlessEqual(actual, actual2)
++ self.failUnlessEqual(new_spec, new_spec2)
++ except Exception as e:
++ print(common.Color.RED(traceback.format_exc().rstrip()))
++ print('Failed spec: %s' % common.Color.CYAN(spec))
++ print(' Expected: %s' % common.Color.YELLOW(exp))
++ print(' Actual: %s' % common.Color.YELLOW(actual))
++ if new_spec != new_spec2:
++ print('Not idempotent')
++ if isinstance(e, AssertionError):
++ raise e
++
++
++if __name__ == '__main__':
++ unittest.main()
+diff --git a/tools/grit/grit/tool/update_resource_ids/common.py b/tools/grit/grit/tool/update_resource_ids/common.py
+new file mode 100644
+index 0000000000..004d8aa0e3
+--- /dev/null
++++ b/tools/grit/grit/tool/update_resource_ids/common.py
+@@ -0,0 +1,101 @@
++# 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.
++
++
++def AlignUp(v, align):
++ return (v + align - 1) // align * align
++
++
++def StripPlural(s):
++ assert s.endswith('s'), 'Expect %s to be plural' % s
++ return s[:-1]
++
++
++class Color:
++
++ def _MakeColor(code):
++ t = '\033[' + code + 'm%s\033[0m'
++ return lambda s: t % s
++
++ NONE = staticmethod(lambda s: s)
++ RED = staticmethod(_MakeColor('31'))
++ GREEN = staticmethod(_MakeColor('32'))
++ YELLOW = staticmethod(_MakeColor('33'))
++ BLUE = staticmethod(_MakeColor('34'))
++ MAGENTA = staticmethod(_MakeColor('35'))
++ CYAN = staticmethod(_MakeColor('36'))
++ WHITE = staticmethod(_MakeColor('37'))
++ GRAY = staticmethod(_MakeColor('30;1'))
++
++
++class TagInfo:
++ """Stores resource_ids tag entry (e.g., {"includes": 100} pair)."""
++
++ def __init__(self, raw_key, raw_value):
++ """TagInfo Constructor.
++
++ Args:
++ raw_key: parser.AnnotatedValue for the parsed key, e.g., "includes".
++ raw_value: parser.AnnotatedValue for the parsed value, e.g., 100.
++ """
++ # Tag name, e.g., 'include' (no "s" at end).
++ self.name = StripPlural(raw_key.val)
++ # |len(raw_value) > 1| is possible, e.g., see grd_reader_unittest.py's
++ # testAssignFirstIdsMultipleMessages. This feature seems unused though.
++ # TODO(huangs): Reconcile this (may end up removing multi-value feature).
++ assert len(raw_value) == 1
++ # Inclusive start *position* of the tag's start ID in resource_ids.
++ self.lo = raw_value[0].lo
++ # Exclusive end *position* of the tag's start ID in resource_ids.
++ self.hi = raw_value[0].hi
++ # The tag's start ID. Initially the old value, but may be reassigned to new.
++ self.id = raw_value[0].val
++ # The number of IDs the tag uses, to be assigned by ItemInfo.SetUsages().
++ self.usage = None
++
++
++class ItemInfo:
++ """resource_ids item, containing multiple TagInfo."""
++
++ def __init__(self, lo, grd, raw_item):
++ # Inclusive start position of the item's key. Serve as unique identifier.
++ self.lo = lo
++ # The GRD filename for the item.
++ self.grd = grd
++ # Optional META information for the item.
++ self.meta = None
++ # List of TagInfo associated witih the item.
++ self.tags = []
++ for k, v in raw_item.items():
++ if k.val == 'META':
++ assert self.meta is None
++ self.meta = v # Not flattened.
++ else:
++ self.tags.append(TagInfo(k, v))
++ self.tags.sort(key=lambda tag: tag.lo)
++
++ def SetUsages(self, tag_name_to_usage):
++ for tag in self.tags:
++ tag.usage = tag_name_to_usage.get(tag.name, 0)
++
++
++def BuildItemList(root_obj):
++ """Extracts ID assignments and structure from parsed resource_ids.
++
++ Returns: A list of ItemInfo, ordered by |lo|.
++ """
++ item_list = []
++ grd_seen = set()
++ for raw_key, raw_item in root_obj.items(): # Unordered.
++ grd = raw_key.val
++ if grd == 'SRCDIR':
++ continue
++ if not grd.endswith('.grd'):
++ raise ValueError('Invalid GRD file: %s' % grd)
++ if grd in grd_seen:
++ raise ValueError('Duplicate GRD: %s' % grd)
++ grd_seen.add(grd)
++ item_list.append(ItemInfo(raw_key.lo, grd, raw_item))
++ item_list.sort(key=lambda item: item.lo)
++ return item_list
+diff --git a/tools/grit/grit/tool/update_resource_ids/parser.py b/tools/grit/grit/tool/update_resource_ids/parser.py
+new file mode 100644
+index 0000000000..da956bbd1c
+--- /dev/null
++++ b/tools/grit/grit/tool/update_resource_ids/parser.py
+@@ -0,0 +1,231 @@
++# 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.
++"""Structure-preserving parser for resource_ids files.
++
++Naive usage of eval() destroys resource_ids structure. This module provides a
++custom parser that annotates source byte ranges of "leaf values" (strings and
++integers).
++"""
++
++from __future__ import print_function
++
++_isWhitespace = lambda ch: ch in ' \t\n'
++_isNotNewline = lambda ch: ch != '\n'
++_isDigit = lambda ch: ch.isdigit()
++
++
++def _RenderLineCol(data, pos):
++ """Renders |pos| within text |data| in as text showing line and column."""
++ # This is used to pinpoint fatal parse errors, so okay to be inefficient.
++ new_lines = [i for i in range(pos) if data[i] == '\n']
++ row = 1 + len(new_lines)
++ col = (pos - new_lines[-1]) if new_lines else 1 + pos
++ return 'line %d, column %d' % (row, col)
++
++
++def Tokenize(data):
++ """Generator to split |data| into tokens.
++
++ Each token is specified as |(t, lo, hi)|:
++ * |t|: Type, with '#' = space / comments, '0' = int, 'S' = string, 'E' = end,
++ and other characters denoting themselves.
++ * |lo, hi|: Token's range within |data| (as |data[lo:hi]|).
++ """
++
++ class ctx: # Local context for mutable data shared across inner functions.
++ pos = 0
++
++ def _HasData():
++ return ctx.pos < len(data)
++
++ # Returns True if ended by |not pred()|, or False if ended by EOF.
++ def _EatWhile(pred):
++ while _HasData():
++ if pred(data[ctx.pos]):
++ ctx.pos += 1
++ else:
++ return True
++ return False
++
++ def _NextBlank():
++ lo = ctx.pos
++ while True:
++ if not _EatWhile(_isWhitespace) or data[ctx.pos] != '#':
++ break
++ ctx.pos += 1
++ if not _EatWhile(_isNotNewline):
++ break
++ ctx.pos += 1
++ return None if ctx.pos == lo else (lo, ctx.pos)
++
++ def _EatString():
++ lo = ctx.pos
++ delim = data[ctx.pos]
++ is_escaped = False
++ ctx.pos += 1
++ while _HasData():
++ ch = data[ctx.pos]
++ ctx.pos += 1
++ if is_escaped:
++ is_escaped = False
++ elif ch == '\\':
++ is_escaped = True
++ elif ch == delim:
++ return
++ raise ValueError('Unterminated string at %s' % _RenderLineCol(data, lo))
++
++ while _HasData():
++ blank = _NextBlank()
++ if blank is not None:
++ yield ('#', blank[0], blank[1])
++ if not _HasData():
++ break
++ lo = ctx.pos
++ ch = data[ctx.pos]
++ if ch in '{}[],:':
++ ctx.pos += 1
++ t = ch
++ elif ch.isdigit():
++ _EatWhile(_isDigit)
++ t = '0'
++ elif ch in '+-':
++ ctx.pos += 1
++ if not _HasData() or not data[ctx.pos].isdigit():
++ raise ValueError('Invalid int at %s' % _RenderLineCol(data, lo))
++ _EatWhile(_isDigit)
++ t = '0'
++ elif ch in '"\'':
++ _EatString()
++ t = 'S'
++ else:
++ raise ValueError('Unknown char %s at %s' %
++ (repr(ch), _RenderLineCol(data, lo)))
++ yield (t, lo, ctx.pos)
++ yield ('E', ctx.pos, ctx.pos) # End sentinel.
++
++
++def _SkipBlanks(toks):
++ """Generator to remove whitespace and comments from Tokenize()."""
++ for t, lo, hi in toks:
++ if t != '#':
++ yield t, lo, hi
++
++
++class AnnotatedValue:
++ """Container for leaf values (ints or strings) with an annotated range."""
++
++ def __init__(self, val, lo, hi):
++ self.val = val
++ self.lo = lo
++ self.hi = hi
++
++ def __str__(self):
++ return '<%s@%d:%d>' % (str(self.val), self.lo, self.hi)
++
++ def __repr__(self):
++ return '<%r@%d:%d>' % (self.val, self.lo, self.hi)
++
++ def __hash__(self):
++ return hash(self.val)
++
++ def __eq__(self, other):
++ return self.val == other
++
++
++class ResourceIdParser:
++ """resource_ids parser that stores leaf values as AnnotatedValue.
++
++ Algorithm: Use Tokenize() to split |data| into tokens and _SkipBlanks() to
++ ignore comments and spacing, then apply a recursive parsing, using a one-token
++ look-ahead for decision making.
++ """
++
++ def __init__(self, data, tok_gen):
++ self.data = data
++ self.state = []
++ self.toks = _SkipBlanks(tok_gen)
++ self.tok_look_ahead = None
++
++ def _MakeErr(self, msg, pos):
++ return ValueError(msg + ' at ' + _RenderLineCol(self.data, pos))
++
++ def _PeekTok(self):
++ if self.tok_look_ahead is None:
++ self.tok_look_ahead = next(self.toks)
++ return self.tok_look_ahead
++
++ def _NextTok(self):
++ if self.tok_look_ahead is None:
++ return next(self.toks)
++ ret = self.tok_look_ahead
++ self.tok_look_ahead = None
++ return ret
++
++ def _EatTok(self, exp_t, tok_name=None):
++ t, lo, _ = self._NextTok()
++ if t != exp_t:
++ raise self._MakeErr('Bad token: Expect \'%s\'' % (tok_name or exp_t), lo)
++
++ def _NextIntOrString(self):
++ t, lo, hi = self._NextTok()
++ if t != '0' and t != 'S':
++ raise self._MakeErr('Expected number or string', lo)
++ value = eval(self.data[lo:hi])
++ return AnnotatedValue(value, lo, hi)
++
++ # Consumes separator ',' and returns whether |end_ch| is encountered.
++ def _EatSep(self, end_ch):
++ t, lo, _ = self._PeekTok()
++ if t == ',':
++ self._EatTok(',')
++ # Allow trailing ','.
++ t, _, _ = self._PeekTok()
++ return t == end_ch
++ elif t == end_ch:
++ return True
++ else:
++ raise self._MakeErr('Expect \',\' or \'%s\'' % end_ch, lo)
++
++ def _NextList(self):
++ self._EatTok('[')
++ ret = []
++ t, _, _ = self._PeekTok()
++ if t != ']':
++ while True:
++ ret.append(self._NextObject())
++ if self._EatSep(']'):
++ break
++ self._EatTok(']')
++ return ret
++
++ def _NextDict(self):
++ self._EatTok('{')
++ ret = {}
++ t, _, _ = self._PeekTok()
++ if t != '}':
++ while True:
++ k = self._NextIntOrString()
++ self._EatTok(':')
++ v = self._NextObject()
++ ret[k] = v
++ if self._EatSep('}'):
++ break
++ self._EatTok('}')
++ return ret
++
++ def _NextObject(self):
++ t, lo, _ = self._PeekTok()
++ if t == '[':
++ return self._NextList()
++ elif t == '{':
++ return self._NextDict()
++ elif t == '0' or t == 'S':
++ return self._NextIntOrString()
++ else:
++ raise self._MakeErr('Bad token: Type = %s' % t, lo)
++
++ def Parse(self):
++ root_obj = self._NextObject()
++ self._EatTok('E', 'EOF')
++ return root_obj
+diff --git a/tools/grit/grit/tool/update_resource_ids/reader.py b/tools/grit/grit/tool/update_resource_ids/reader.py
+new file mode 100644
+index 0000000000..0a156d2deb
+--- /dev/null
++++ b/tools/grit/grit/tool/update_resource_ids/reader.py
+@@ -0,0 +1,83 @@
++# 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.
++"""Helpers to read GRD files and estimate resource ID usages.
++
++This module uses grit.grd_reader to estimate resource ID usages in GRD
++(and GRDP) files by counting the occurrences of {include, message, structure}
++tags. This approach avoids the complexties of conditional inclusions, but
++produces a conservative estimate of ID usages.
++"""
++
++from __future__ import print_function
++
++import collections
++import os
++
++from grit import grd_reader
++from grit import util
++from grit.tool.update_resource_ids import common
++
++TAGS_OF_INTEREST = set(['include', 'message', 'structure'])
++
++def _CountResourceUsage(grd, seen_files):
++ tag_name_to_count = {tag: set() for tag in TAGS_OF_INTEREST}
++ # Pass '_chromium', but '_google_chrome' would produce the same result.
++ root = grd_reader.Parse(grd, defines={'_chromium': True})
++ seen_files.add(grd)
++ # Count all descendant tags, regardless of whether they're active.
++ for node in root.Preorder():
++ if node.name in TAGS_OF_INTEREST:
++ tag_name_to_count[node.name].add(node.attrs['name'])
++ elif node.name == 'part':
++ part_path = os.path.join(os.path.dirname(grd), node.GetInputPath())
++ seen_files.add(util.normpath(part_path))
++ return {k: len(v) for k, v in tag_name_to_count.items() if v}
++
++
++def GenerateResourceUsages(item_list, src_dir, fake, seen_files):
++ """Visits a list of ItemInfo to generate maps from tag name to usage.
++
++ Args:
++ root_obj: Root dict of a resource_ids file.
++ src_dir: Absolute directory of Chrome's src/ directory.
++ fake: For testing: Sets 10 as usages for all tags, to avoid reading GRD.
++ seen_files: A set to collect paths of files read.
++ Yields:
++ Tuple (item, tag_name_to_usage), where |item| is from |item_list| and
++ |tag_name_to_usage| is a dict() mapping tag name to (int) usage.
++ """
++ if fake:
++ for item in item_list:
++ tag_name_to_usage = collections.Counter({t.name: 10 for t in item.tags})
++ yield item, tag_name_to_usage
++ return
++ for item in item_list:
++ supported_tag_names = set(tag.name for tag in item.tags)
++ if item.meta and 'sizes' in item.meta:
++ # If META has "sizes" field, use it instead of reading GRD.
++ tag_name_to_usage = collections.Counter()
++ for k, vlist in item.meta['sizes'].items():
++ tag_name_to_usage[common.StripPlural(k.val)] = sum(v.val for v in vlist)
++ tag_names = set(tag_name_to_usage.keys())
++ if tag_names != supported_tag_names:
++ raise ValueError('META "sizes" field have identical fields as actual '
++ '"sizes" field.')
++ else:
++ # Generated GRD start with '<(SHARED_INTERMEDIATE_DIR)'. Just check '<'.
++ if item.grd.startswith('<'):
++ raise ValueError('%s: Generated GRD must use META with "sizes" field '
++ 'to specify size bounds.' % item.grd)
++ grd_file = os.path.join(src_dir, item.grd)
++ if not os.path.exists(grd_file):
++ # Silently skip missing files so that src-internal files do not break
++ # public checkouts.
++ yield item, {}
++ continue
++ tag_name_to_usage = _CountResourceUsage(grd_file, seen_files)
++ tag_names = set(tag_name_to_usage.keys())
++ if not tag_names.issubset(supported_tag_names):
++ missing = [t + 's' for t in tag_names - supported_tag_names]
++ raise ValueError(
++ 'Resource ids for %s needs entry for %s' % (item.grd, missing))
++ yield item, tag_name_to_usage
+diff --git a/tools/grit/grit/tool/xmb.py b/tools/grit/grit/tool/xmb.py
+new file mode 100644
+index 0000000000..b821308369
+--- /dev/null
++++ b/tools/grit/grit/tool/xmb.py
+@@ -0,0 +1,295 @@
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++"""The 'grit xmb' tool.
++"""
++
++from __future__ import print_function
++
++import getopt
++import os
++import sys
++
++from xml.sax import saxutils
++
++import six
++
++from grit import grd_reader
++from grit import lazy_re
++from grit import tclib
++from grit import util
++from grit.tool import interface
++
++
++# Used to collapse presentable content to determine if
++# xml:space="preserve" is needed.
++_WHITESPACES_REGEX = lazy_re.compile(r'\s\s*')
++
++
++# See XmlEscape below.
++_XML_QUOTE_ESCAPES = {
++ u"'": u'&apos;',
++ u'"': u'&quot;',
++}
++
++def _XmlEscape(s):
++ """Returns text escaped for XML in a way compatible with Google's
++ internal Translation Console tool. May be used for attributes as
++ well as for contents.
++ """
++ return saxutils.escape(six.text_type(s), _XML_QUOTE_ESCAPES).encode('utf-8')
++
++
++def _WriteAttribute(file, name, value):
++ """Writes an XML attribute to the specified file.
++
++ Args:
++ file: file to write to
++ name: name of the attribute
++ value: (unescaped) value of the attribute
++ """
++ name = name.encode('utf-8')
++ if value:
++ file.write(b' %s="%s"' % (name, _XmlEscape(value)))
++
++
++def _WriteMessage(file, message):
++ presentable_content = message.GetPresentableContent()
++ assert (isinstance(presentable_content, six.string_types) or
++ (len(message.parts) == 1 and
++ type(message.parts[0] == tclib.Placeholder)))
++ preserve_space = presentable_content != _WHITESPACES_REGEX.sub(
++ u' ', presentable_content.strip())
++
++ file.write(b'<msg')
++ _WriteAttribute(file, 'desc', message.GetDescription())
++ _WriteAttribute(file, 'id', message.GetId())
++ _WriteAttribute(file, 'meaning', message.GetMeaning())
++ if preserve_space:
++ _WriteAttribute(file, 'xml:space', 'preserve')
++ file.write(b'>')
++ if not preserve_space:
++ file.write(b'\n ')
++
++ parts = message.GetContent()
++ for part in parts:
++ if isinstance(part, tclib.Placeholder):
++ file.write(b'<ph')
++ _WriteAttribute(file, 'name', part.GetPresentation())
++ file.write(b'><ex>')
++ file.write(_XmlEscape(part.GetExample()))
++ file.write(b'</ex>')
++ file.write(_XmlEscape(part.GetOriginal()))
++ file.write(b'</ph>')
++ else:
++ file.write(_XmlEscape(part))
++ if not preserve_space:
++ file.write(b'\n')
++ file.write(b'</msg>\n')
++
++
++def WriteXmbFile(file, messages):
++ """Writes the given grit.tclib.Message items to the specified open
++ file-like object in the XMB format.
++ """
++ file.write(b"""<?xml version="1.0" encoding="UTF-8"?>
++<!DOCTYPE messagebundle [
++<!ELEMENT messagebundle (msg)*>
++<!ATTLIST messagebundle class CDATA #IMPLIED>
++
++<!ELEMENT msg (#PCDATA|ph|source)*>
++<!ATTLIST msg id CDATA #IMPLIED>
++<!ATTLIST msg seq CDATA #IMPLIED>
++<!ATTLIST msg name CDATA #IMPLIED>
++<!ATTLIST msg desc CDATA #IMPLIED>
++<!ATTLIST msg meaning CDATA #IMPLIED>
++<!ATTLIST msg obsolete (obsolete) #IMPLIED>
++<!ATTLIST msg xml:space (default|preserve) "default">
++<!ATTLIST msg is_hidden CDATA #IMPLIED>
++
++<!ELEMENT source (#PCDATA)>
++
++<!ELEMENT ph (#PCDATA|ex)*>
++<!ATTLIST ph name CDATA #REQUIRED>
++
++<!ELEMENT ex (#PCDATA)>
++]>
++<messagebundle>
++""")
++ for message in messages:
++ _WriteMessage(file, message)
++ file.write(b'</messagebundle>')
++
++
++class OutputXmb(interface.Tool):
++ """Outputs all translateable messages in the .grd input file to an
++.xmb file, which is the format used to give source messages to
++Google's internal Translation Console tool. The format could easily
++be used for other systems.
++
++Usage: grit xmb [-i|-h] [-l LIMITFILE] OUTPUTPATH
++
++OUTPUTPATH is the path you want to output the .xmb file to.
++
++The -l option can be used to output only some of the resources to the .xmb file.
++LIMITFILE is the path to a file that is used to limit the items output to the
++xmb file. If the filename extension is .grd, the file must be a .grd file
++and the tool only output the contents of nodes from the input file that also
++exist in the limit file (as compared on the 'name' attribute). Otherwise it must
++contain a list of the IDs that output should be limited to, one ID per line, and
++the tool will only output nodes with 'name' attributes that match one of the
++IDs.
++
++The -i option causes 'grit xmb' to output an "IDs only" file instead of an XMB
++file. The "IDs only" file contains the message ID of each message that would
++normally be output to the XMB file, one message ID per line. It is designed for
++use with the 'grit transl2tc' tool's -l option.
++
++Other options:
++
++ -D NAME[=VAL] Specify a C-preprocessor-like define NAME with optional
++ value VAL (defaults to 1) which will be used to control
++ conditional inclusion of resources.
++
++ -E NAME=VALUE Set environment variable NAME to VALUE (within grit).
++
++"""
++ # The different output formats supported by this tool
++ FORMAT_XMB = 0
++ FORMAT_IDS_ONLY = 1
++
++ def __init__(self, defines=None):
++ super(OutputXmb, self).__init__()
++ self.format = self.FORMAT_XMB
++ self.defines = defines or {}
++
++ def ShortDescription(self):
++ return 'Exports all translateable messages into an XMB file.'
++
++ def Run(self, opts, args):
++ os.environ['cwd'] = os.getcwd()
++
++ self.SetOptions(opts)
++
++ limit_file = None
++ limit_is_grd = False
++ limit_file_dir = None
++ own_opts, args = getopt.getopt(args, 'l:D:ih', ('help',))
++ for key, val in own_opts:
++ if key == '-l':
++ limit_file = open(val, 'r')
++ limit_file_dir = util.dirname(val)
++ if not len(limit_file_dir):
++ limit_file_dir = '.'
++ limit_is_grd = os.path.splitext(val)[1] == '.grd'
++ elif key == '-i':
++ self.format = self.FORMAT_IDS_ONLY
++ elif key == '-D':
++ name, val = util.ParseDefine(val)
++ self.defines[name] = val
++ elif key == '-E':
++ (env_name, env_value) = val.split('=', 1)
++ os.environ[env_name] = env_value
++ elif key == '--help':
++ self.ShowUsage()
++ sys.exit(0)
++ if not len(args) == 1:
++ print('grit xmb takes exactly one argument, the path to the XMB file '
++ 'to output.')
++ return 2
++
++ xmb_path = args[0]
++ res_tree = grd_reader.Parse(opts.input, debug=opts.extra_verbose, defines=self.defines)
++ res_tree.SetOutputLanguage('en')
++ res_tree.SetDefines(self.defines)
++ res_tree.OnlyTheseTranslations([])
++ res_tree.RunGatherers()
++
++ with open(xmb_path, 'wb') as output_file:
++ self.Process(
++ res_tree, output_file, limit_file, limit_is_grd, limit_file_dir)
++ if limit_file:
++ limit_file.close()
++ print("Wrote %s" % xmb_path)
++
++ def Process(self, res_tree, output_file, limit_file=None, limit_is_grd=False,
++ dir=None):
++ """Writes a document with the contents of res_tree into output_file,
++ limiting output to the IDs specified in limit_file, which is a GRD file if
++ limit_is_grd is true, otherwise a file with one ID per line.
++
++ The format of the output document depends on this object's format attribute.
++ It can be FORMAT_XMB or FORMAT_IDS_ONLY.
++
++ The FORMAT_IDS_ONLY format causes this function to write just a list
++ of the IDs of all messages that would have been added to the XMB file, one
++ ID per line.
++
++ The FORMAT_XMB format causes this function to output the (default) XMB
++ format.
++
++ Args:
++ res_tree: base.Node()
++ output_file: file open for writing
++ limit_file: None or file open for reading
++ limit_is_grd: True | False
++ dir: Directory of the limit file
++ """
++ if limit_file:
++ if limit_is_grd:
++ limit_list = []
++ limit_tree = grd_reader.Parse(limit_file,
++ dir=dir,
++ debug=self.o.extra_verbose)
++ for node in limit_tree:
++ if 'name' in node.attrs:
++ limit_list.append(node.attrs['name'])
++ else:
++ # Not a GRD file, so it's just a file with one ID per line
++ limit_list = [item.strip() for item in limit_file.read().split('\n')]
++
++ ids_already_done = {}
++ messages = []
++ for node in res_tree:
++ if (limit_file and
++ not ('name' in node.attrs and node.attrs['name'] in limit_list)):
++ continue
++ if not node.IsTranslateable():
++ continue
++
++ for clique in node.GetCliques():
++ if not clique.IsTranslateable():
++ continue
++ if not clique.GetMessage().GetRealContent():
++ continue
++
++ # Some explanation is in order here. Note that we can have
++ # many messages with the same ID.
++ #
++ # The way we work around this is to maintain a list of cliques
++ # per message ID (in the UberClique) and select the "best" one
++ # (the first one that has a description, or an arbitrary one
++ # if there is no description) for inclusion in the XMB file.
++ # The translations are all going to be the same for messages
++ # with the same ID, although the way we replace placeholders
++ # might be slightly different.
++ id = clique.GetMessage().GetId()
++ if id in ids_already_done:
++ continue
++ ids_already_done[id] = 1
++
++ message = node.UberClique().BestClique(id).GetMessage()
++ messages += [message]
++
++ # Ensure a stable order of messages, to help regression testing.
++ messages.sort(key=lambda x:x.GetId())
++
++ if self.format == self.FORMAT_IDS_ONLY:
++ # We just print the list of IDs to the output file.
++ for msg in messages:
++ output_file.write(msg.GetId())
++ output_file.write('\n')
++ else:
++ assert self.format == self.FORMAT_XMB
++ WriteXmbFile(output_file, messages)
+diff --git a/tools/grit/grit/tool/xmb_unittest.py b/tools/grit/grit/tool/xmb_unittest.py
+new file mode 100644
+index 0000000000..3c7e92cee7
+--- /dev/null
++++ b/tools/grit/grit/tool/xmb_unittest.py
+@@ -0,0 +1,132 @@
++#!/usr/bin/env python
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++'''Unit tests for 'grit xmb' tool.'''
++
++from __future__ import print_function
++
++import io
++import os
++import sys
++if __name__ == '__main__':
++ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
++
++import unittest
++import xml.sax
++
++from six import StringIO
++
++from grit import grd_reader
++from grit import util
++from grit.tool import xmb
++
++
++class XmbUnittest(unittest.TestCase):
++ def setUp(self):
++ self.res_tree = grd_reader.Parse(
++ io.BytesIO(u'''<?xml version="1.0" encoding="UTF-8"?>
++ <grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
++ <release seq="3">
++ <includes>
++ <include type="gif" name="ID_LOGO" file="images/logo.gif" />
++ </includes>
++ <messages>
++ <message name="GOOD" desc="sub" sub_variable="true">
++ excellent
++ </message>
++ <message name="IDS_GREETING" desc="Printed to greet the currently logged in user">
++ Hello <ph name="USERNAME">%s<ex>Joi</ex></ph>, are you doing [GOOD] today?
++ </message>
++ <message name="IDS_BONGOBINGO">
++ Yibbee
++ </message>
++ <message name="IDS_UNICODE">
++ Ol\xe1, \u4eca\u65e5\u306f! \U0001F60A
++ </message>
++ </messages>
++ <structures>
++ <structure type="dialog" name="IDD_SPACYBOX" encoding="utf-16" file="grit/testdata/klonk.rc" />
++ </structures>
++ </release>
++ </grit>'''.encode('utf-8')), '.')
++ self.xmb_file = io.BytesIO()
++
++ def testNormalOutput(self):
++ xmb.OutputXmb().Process(self.res_tree, self.xmb_file)
++ output = self.xmb_file.getvalue().decode('utf-8')
++ self.failUnless(output.count('Joi'))
++ self.failUnless(output.count('Yibbee'))
++ self.failUnless(output.count(u'Ol\xe1, \u4eca\u65e5\u306f! \U0001F60A'))
++
++ def testLimitList(self):
++ limit_file = StringIO(
++ 'IDS_BONGOBINGO\nIDS_DOES_NOT_EXIST\nIDS_ALSO_DOES_NOT_EXIST')
++ xmb.OutputXmb().Process(self.res_tree, self.xmb_file, limit_file, False)
++ output = self.xmb_file.getvalue().decode('utf-8')
++ self.failUnless(output.count('Yibbee'))
++ self.failUnless(not output.count('Joi'))
++
++ def testLimitGrd(self):
++ limit_file = StringIO('''<?xml version="1.0" encoding="UTF-8"?>
++ <grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
++ <release seq="3">
++ <messages>
++ <message name="IDS_GREETING" desc="Printed to greet the currently logged in user">
++ Hello <ph name="USERNAME">%s<ex>Joi</ex></ph>, how are you doing today?
++ </message>
++ </messages>
++ </release>
++ </grit>''')
++ tool = xmb.OutputXmb()
++ class DummyOpts(object):
++ extra_verbose = False
++ tool.o = DummyOpts()
++ tool.Process(self.res_tree, self.xmb_file, limit_file, True, dir='.')
++ output = self.xmb_file.getvalue().decode('utf-8')
++ self.failUnless(output.count('Joi'))
++ self.failUnless(not output.count('Yibbee'))
++
++ def testSubstitution(self):
++ self.res_tree.SetOutputLanguage('en')
++ os.chdir(util.PathFromRoot('.')) # so it can find klonk.rc
++ self.res_tree.RunGatherers()
++ xmb.OutputXmb().Process(self.res_tree, self.xmb_file)
++ output = self.xmb_file.getvalue().decode('utf-8')
++ self.failUnless(output.count(
++ '<ph name="GOOD_1"><ex>excellent</ex>[GOOD]</ph>'))
++
++ def testLeadingTrailingWhitespace(self):
++ # Regression test for problems outputting messages with leading or
++ # trailing whitespace (these come in via structures only, as
++ # message nodes already strip and store whitespace).
++ self.res_tree.SetOutputLanguage('en')
++ os.chdir(util.PathFromRoot('.')) # so it can find klonk.rc
++ self.res_tree.RunGatherers()
++ xmb.OutputXmb().Process(self.res_tree, self.xmb_file)
++ output = self.xmb_file.getvalue().decode('utf-8')
++ self.failUnless(output.count('OK ? </msg>'))
++
++ def testDisallowedChars(self):
++ # Validate that the invalid unicode is not accepted. Since it's not valid,
++ # we can't specify it in a string literal, so write as a byte sequence.
++ bad_xml = io.BytesIO()
++ bad_xml.write(b'''<?xml version="1.0" encoding="UTF-8"?>
++ <grit latest_public_release="2" source_lang_id="en-US"
++ current_release="3" base_dir=".">
++ <release seq="3">
++ <messages>
++ <message name="ID_FOO">''')
++ # UTF-8 corresponding to to \U00110000
++ # http://apps.timwhitlock.info/unicode/inspect/hex/110000
++ bad_xml.write(b'\xF4\x90\x80\x80')
++ bad_xml.write(b'''</message>
++ </messages>
++ </release>
++ </grit>''')
++ bad_xml.seek(0)
++ self.assertRaises(xml.sax.SAXParseException, grd_reader.Parse, bad_xml, '.')
++
++if __name__ == '__main__':
++ unittest.main()
+diff --git a/tools/grit/grit/util.py b/tools/grit/grit/util.py
+new file mode 100644
+index 0000000000..98433d154c
+--- /dev/null
++++ b/tools/grit/grit/util.py
+@@ -0,0 +1,691 @@
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++'''Utilities used by GRIT.
++'''
++
++from __future__ import print_function
++
++import codecs
++import io
++import os
++import re
++import shutil
++import sys
++import tempfile
++from xml.sax import saxutils
++
++import six
++from six import StringIO
++from six.moves import html_entities as entities
++
++from grit import lazy_re
++
++_root_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
++
++
++# Unique constants for use by ReadFile().
++BINARY = 0
++
++
++# Unique constants representing data pack encodings.
++_, UTF8, UTF16 = range(3)
++
++
++def Encode(message, encoding):
++ '''Returns a byte stream that represents |message| in the given |encoding|.'''
++ # |message| is a python unicode string, so convert to a byte stream that
++ # has the correct encoding requested for the datapacks. We skip the first
++ # 2 bytes of text resources because it is the BOM.
++ if encoding == UTF8:
++ return message.encode('utf8')
++ if encoding == UTF16:
++ return message.encode('utf16')[2:]
++ # Default is BINARY
++ return message
++
++
++# Matches all different types of linebreaks.
++LINEBREAKS = re.compile('\r\n|\n|\r')
++
++def MakeRelativePath(base_path, path_to_make_relative):
++ """Returns a relative path such from the base_path to
++ the path_to_make_relative.
++
++ In other words, os.join(base_path,
++ MakeRelativePath(base_path, path_to_make_relative))
++ is the same location as path_to_make_relative.
++
++ Args:
++ base_path: the root path
++ path_to_make_relative: an absolute path that is on the same drive
++ as base_path
++ """
++
++ def _GetPathAfterPrefix(prefix_path, path_with_prefix):
++ """Gets the subpath within in prefix_path for the path_with_prefix
++ with no beginning or trailing path separators.
++
++ Args:
++ prefix_path: the base path
++ path_with_prefix: a path that starts with prefix_path
++ """
++ assert path_with_prefix.startswith(prefix_path)
++ path_without_prefix = path_with_prefix[len(prefix_path):]
++ normalized_path = os.path.normpath(path_without_prefix.strip(os.path.sep))
++ if normalized_path == '.':
++ normalized_path = ''
++ return normalized_path
++
++ def _GetCommonBaseDirectory(*args):
++ """Returns the common prefix directory for the given paths
++
++ Args:
++ The list of paths (at least one of which should be a directory)
++ """
++ prefix = os.path.commonprefix(args)
++ # prefix is a character-by-character prefix (i.e. it does not end
++ # on a directory bound, so this code fixes that)
++
++ # if the prefix ends with the separator, then it is prefect.
++ if len(prefix) > 0 and prefix[-1] == os.path.sep:
++ return prefix
++
++ # We need to loop through all paths or else we can get
++ # tripped up by "c:\a" and "c:\abc". The common prefix
++ # is "c:\a" which is a directory and looks good with
++ # respect to the first directory but it is clear that
++ # isn't a common directory when the second path is
++ # examined.
++ for path in args:
++ assert len(path) >= len(prefix)
++ # If the prefix the same length as the path,
++ # then the prefix must be a directory (since one
++ # of the arguements should be a directory).
++ if path == prefix:
++ continue
++ # if the character after the prefix in the path
++ # is the separator, then the prefix appears to be a
++ # valid a directory as well for the given path
++ if path[len(prefix)] == os.path.sep:
++ continue
++ # Otherwise, the prefix is not a directory, so it needs
++ # to be shortened to be one
++ index_sep = prefix.rfind(os.path.sep)
++ # The use "index_sep + 1" because it includes the final sep
++ # and it handles the case when the index_sep is -1 as well
++ prefix = prefix[:index_sep + 1]
++ # At this point we backed up to a directory bound which is
++ # common to all paths, so we can quit going through all of
++ # the paths.
++ break
++ return prefix
++
++ prefix = _GetCommonBaseDirectory(base_path, path_to_make_relative)
++ # If the paths had no commonality at all, then return the absolute path
++ # because it is the best that can be done. If the path had to be relative
++ # then eventually this absolute path will be discovered (when a build breaks)
++ # and an appropriate fix can be made, but having this allows for the best
++ # backward compatibility with the absolute path behavior in the past.
++ if len(prefix) <= 0:
++ return path_to_make_relative
++ # Build a path from the base dir to the common prefix
++ remaining_base_path = _GetPathAfterPrefix(prefix, base_path)
++
++ # The follow handles two case: "" and "foo\\bar"
++ path_pieces = remaining_base_path.split(os.path.sep)
++ base_depth_from_prefix = len([d for d in path_pieces if len(d)])
++ base_to_prefix = (".." + os.path.sep) * base_depth_from_prefix
++
++ # Put add in the path from the prefix to the path_to_make_relative
++ remaining_other_path = _GetPathAfterPrefix(prefix, path_to_make_relative)
++ return base_to_prefix + remaining_other_path
++
++
++KNOWN_SYSTEM_IDENTIFIERS = set()
++
++SYSTEM_IDENTIFIERS = None
++
++def SetupSystemIdentifiers(ids):
++ '''Adds ids to a regexp of known system identifiers.
++
++ Can be called many times, ids will be accumulated.
++
++ Args:
++ ids: an iterable of strings
++ '''
++ KNOWN_SYSTEM_IDENTIFIERS.update(ids)
++ global SYSTEM_IDENTIFIERS
++ SYSTEM_IDENTIFIERS = lazy_re.compile(
++ ' | '.join([r'\b%s\b' % i for i in KNOWN_SYSTEM_IDENTIFIERS]),
++ re.VERBOSE)
++
++
++# Matches all of the resource IDs predefined by Windows.
++SetupSystemIdentifiers((
++ 'IDOK', 'IDCANCEL', 'IDC_STATIC', 'IDYES', 'IDNO',
++ 'ID_FILE_NEW', 'ID_FILE_OPEN', 'ID_FILE_CLOSE', 'ID_FILE_SAVE',
++ 'ID_FILE_SAVE_AS', 'ID_FILE_PAGE_SETUP', 'ID_FILE_PRINT_SETUP',
++ 'ID_FILE_PRINT', 'ID_FILE_PRINT_DIRECT', 'ID_FILE_PRINT_PREVIEW',
++ 'ID_FILE_UPDATE', 'ID_FILE_SAVE_COPY_AS', 'ID_FILE_SEND_MAIL',
++ 'ID_FILE_MRU_FIRST', 'ID_FILE_MRU_LAST',
++ 'ID_EDIT_CLEAR', 'ID_EDIT_CLEAR_ALL', 'ID_EDIT_COPY',
++ 'ID_EDIT_CUT', 'ID_EDIT_FIND', 'ID_EDIT_PASTE', 'ID_EDIT_PASTE_LINK',
++ 'ID_EDIT_PASTE_SPECIAL', 'ID_EDIT_REPEAT', 'ID_EDIT_REPLACE',
++ 'ID_EDIT_SELECT_ALL', 'ID_EDIT_UNDO', 'ID_EDIT_REDO',
++ 'VS_VERSION_INFO', 'IDRETRY',
++ 'ID_APP_ABOUT', 'ID_APP_EXIT',
++ 'ID_NEXT_PANE', 'ID_PREV_PANE',
++ 'ID_WINDOW_NEW', 'ID_WINDOW_ARRANGE', 'ID_WINDOW_CASCADE',
++ 'ID_WINDOW_TILE_HORZ', 'ID_WINDOW_TILE_VERT', 'ID_WINDOW_SPLIT',
++ 'ATL_IDS_SCSIZE', 'ATL_IDS_SCMOVE', 'ATL_IDS_SCMINIMIZE',
++ 'ATL_IDS_SCMAXIMIZE', 'ATL_IDS_SCNEXTWINDOW', 'ATL_IDS_SCPREVWINDOW',
++ 'ATL_IDS_SCCLOSE', 'ATL_IDS_SCRESTORE', 'ATL_IDS_SCTASKLIST',
++ 'ATL_IDS_MDICHILD', 'ATL_IDS_IDLEMESSAGE', 'ATL_IDS_MRU_FILE' ))
++
++
++# Matches character entities, whether specified by name, decimal or hex.
++_HTML_ENTITY = lazy_re.compile(
++ '&(#(?P<decimal>[0-9]+)|#x(?P<hex>[a-fA-F0-9]+)|(?P<named>[a-z0-9]+));',
++ re.IGNORECASE)
++
++# Matches characters that should be HTML-escaped. This is <, > and &, but only
++# if the & is not the start of an HTML character entity.
++_HTML_CHARS_TO_ESCAPE = lazy_re.compile(
++ '"|<|>|&(?!#[0-9]+|#x[0-9a-z]+|[a-z]+;)',
++ re.IGNORECASE | re.MULTILINE)
++
++
++def ReadFile(filename, encoding):
++ '''Reads and returns the entire contents of the given file.
++
++ Args:
++ filename: The path to the file.
++ encoding: A Python codec name or the special value: BINARY to read
++ the file in binary mode.
++ '''
++ if encoding == BINARY:
++ mode = 'rb'
++ encoding = None
++ else:
++ mode = 'rU'
++
++ with io.open(filename, mode, encoding=encoding) as f:
++ return f.read()
++
++
++def WrapOutputStream(stream, encoding = 'utf-8'):
++ '''Returns a stream that wraps the provided stream, making it write
++ characters using the specified encoding.'''
++ return codecs.getwriter(encoding)(stream)
++
++
++def ChangeStdoutEncoding(encoding = 'utf-8'):
++ '''Changes STDOUT to print characters using the specified encoding.'''
++ # If we're unittesting, don't reconfigure.
++ if isinstance(sys.stdout, StringIO):
++ return
++
++ if sys.version_info.major < 3:
++ # Python 2 has binary streams by default, so reconfigure directly.
++ sys.stdout = WrapOutputStream(sys.stdout, encoding)
++ sys.stderr = WrapOutputStream(sys.stderr, encoding)
++ elif sys.version_info < (3, 7):
++ # Python 3 has text streams by default, so we have to detach them first.
++ sys.stdout = WrapOutputStream(sys.stdout.detach(), encoding)
++ sys.stderr = WrapOutputStream(sys.stderr.detach(), encoding)
++ else:
++ # Python 3.7+ provides an API for this specifically.
++ sys.stdout.reconfigure(encoding=encoding)
++ sys.stderr.reconfigure(encoding=encoding)
++
++
++def EscapeHtml(text, escape_quotes = False):
++ '''Returns 'text' with <, > and & (and optionally ") escaped to named HTML
++ entities. Any existing named entity or HTML entity defined by decimal or
++ hex code will be left untouched. This is appropriate for escaping text for
++ inclusion in HTML, but not for XML.
++ '''
++ def Replace(match):
++ if match.group() == '&': return '&amp;'
++ elif match.group() == '<': return '&lt;'
++ elif match.group() == '>': return '&gt;'
++ elif match.group() == '"':
++ if escape_quotes: return '&quot;'
++ else: return match.group()
++ else: assert False
++ out = _HTML_CHARS_TO_ESCAPE.sub(Replace, text)
++ return out
++
++
++def UnescapeHtml(text, replace_nbsp=True):
++ '''Returns 'text' with all HTML character entities (both named character
++ entities and those specified by decimal or hexadecimal Unicode ordinal)
++ replaced by their Unicode characters (or latin1 characters if possible).
++
++ The only exception is that &nbsp; will not be escaped if 'replace_nbsp' is
++ False.
++ '''
++ def Replace(match):
++ groups = match.groupdict()
++ if groups['hex']:
++ return six.unichr(int(groups['hex'], 16))
++ elif groups['decimal']:
++ return six.unichr(int(groups['decimal'], 10))
++ else:
++ name = groups['named']
++ if name == 'nbsp' and not replace_nbsp:
++ return match.group() # Don't replace &nbsp;
++ assert name != None
++ if name in entities.name2codepoint:
++ return six.unichr(entities.name2codepoint[name])
++ else:
++ return match.group() # Unknown HTML character entity - don't replace
++
++ out = _HTML_ENTITY.sub(Replace, text)
++ return out
++
++
++def EncodeCdata(cdata):
++ '''Returns the provided cdata in either escaped format or <![CDATA[xxx]]>
++ format, depending on which is more appropriate for easy editing. The data
++ is escaped for inclusion in an XML element's body.
++
++ Args:
++ cdata: 'If x < y and y < z then x < z'
++
++ Return:
++ '<![CDATA[If x < y and y < z then x < z]]>'
++ '''
++ if cdata.count('<') > 1 or cdata.count('>') > 1 and cdata.count(']]>') == 0:
++ return '<![CDATA[%s]]>' % cdata
++ else:
++ return saxutils.escape(cdata)
++
++
++def FixupNamedParam(function, param_name, param_value):
++ '''Returns a closure that is identical to 'function' but ensures that the
++ named parameter 'param_name' is always set to 'param_value' unless explicitly
++ set by the caller.
++
++ Args:
++ function: callable
++ param_name: 'bingo'
++ param_value: 'bongo' (any type)
++
++ Return:
++ callable
++ '''
++ def FixupClosure(*args, **kw):
++ if not param_name in kw:
++ kw[param_name] = param_value
++ return function(*args, **kw)
++ return FixupClosure
++
++
++def PathFromRoot(path):
++ r'''Takes a path relative to the root directory for GRIT (the one that grit.py
++ resides in) and returns a path that is either absolute or relative to the
++ current working directory (i.e .a path you can use to open the file).
++
++ Args:
++ path: 'rel_dir\file.ext'
++
++ Return:
++ 'c:\src\tools\rel_dir\file.ext
++ '''
++ return os.path.normpath(os.path.join(_root_dir, path))
++
++
++def ParseGrdForUnittest(body, base_dir=None, predetermined_ids_file=None,
++ run_gatherers=False):
++ '''Parse a skeleton .grd file and return it, for use in unit tests.
++
++ Args:
++ body: XML that goes inside the <release> element.
++ base_dir: The base_dir attribute of the <grit> tag.
++ '''
++ from grit import grd_reader
++ if isinstance(body, six.text_type):
++ body = body.encode('utf-8')
++ if base_dir is None:
++ base_dir = PathFromRoot('.')
++ lines = [b'<?xml version="1.0" encoding="UTF-8"?>']
++ lines.append(b'<grit latest_public_release="2" current_release="3" '
++ b'source_lang_id="en" base_dir="%s">' % base_dir.encode('utf-8'))
++ if b'<outputs>' in body:
++ lines.append(body)
++ else:
++ lines.append(b' <outputs></outputs>')
++ lines.append(b' <release seq="3">')
++ lines.append(body)
++ lines.append(b' </release>')
++ lines.append(b'</grit>')
++ ret = grd_reader.Parse(io.BytesIO(b'\n'.join(lines)), dir='.')
++ ret.SetOutputLanguage('en')
++ if run_gatherers:
++ ret.RunGatherers()
++ ret.SetPredeterminedIdsFile(predetermined_ids_file)
++ ret.InitializeIds()
++ return ret
++
++
++def StripBlankLinesAndComments(text):
++ '''Strips blank lines and comments from C source code, for unit tests.'''
++ return '\n'.join(line for line in text.splitlines()
++ if line and not line.startswith('//'))
++
++
++def dirname(filename):
++ '''Version of os.path.dirname() that never returns empty paths (returns
++ '.' if the result of os.path.dirname() is empty).
++ '''
++ ret = os.path.dirname(filename)
++ if ret == '':
++ ret = '.'
++ return ret
++
++
++def normpath(path):
++ '''Version of os.path.normpath that also changes backward slashes to
++ forward slashes when not running on Windows.
++ '''
++ # This is safe to always do because the Windows version of os.path.normpath
++ # will replace forward slashes with backward slashes.
++ path = path.replace('\\', '/')
++ return os.path.normpath(path)
++
++
++_LANGUAGE_SPLIT_RE = lazy_re.compile('-|_|/')
++
++
++def CanonicalLanguage(code):
++ '''Canonicalizes two-part language codes by using a dash and making the
++ second part upper case. Returns one-part language codes unchanged.
++
++ Args:
++ code: 'zh_cn'
++
++ Return:
++ code: 'zh-CN'
++ '''
++ parts = _LANGUAGE_SPLIT_RE.split(code)
++ code = [ parts[0] ]
++ for part in parts[1:]:
++ code.append(part.upper())
++ return '-'.join(code)
++
++
++_LANG_TO_CODEPAGE = {
++ 'en' : 1252,
++ 'fr' : 1252,
++ 'it' : 1252,
++ 'de' : 1252,
++ 'es' : 1252,
++ 'nl' : 1252,
++ 'sv' : 1252,
++ 'no' : 1252,
++ 'da' : 1252,
++ 'fi' : 1252,
++ 'pt-BR' : 1252,
++ 'ru' : 1251,
++ 'ja' : 932,
++ 'zh-TW' : 950,
++ 'zh-CN' : 936,
++ 'ko' : 949,
++}
++
++
++def LanguageToCodepage(lang):
++ '''Returns the codepage _number_ that can be used to represent 'lang', which
++ may be either in formats such as 'en', 'pt_br', 'pt-BR', etc.
++
++ The codepage returned will be one of the 'cpXXXX' codepage numbers.
++
++ Args:
++ lang: 'de'
++
++ Return:
++ 1252
++ '''
++ lang = CanonicalLanguage(lang)
++ if lang in _LANG_TO_CODEPAGE:
++ return _LANG_TO_CODEPAGE[lang]
++ else:
++ print("Not sure which codepage to use for %s, assuming cp1252" % lang)
++ return 1252
++
++def NewClassInstance(class_name, class_type):
++ '''Returns an instance of the class specified in classname
++
++ Args:
++ class_name: the fully qualified, dot separated package + classname,
++ i.e. "my.package.name.MyClass". Short class names are not supported.
++ class_type: the class or superclass this object must implement
++
++ Return:
++ An instance of the class, or None if none was found
++ '''
++ lastdot = class_name.rfind('.')
++ module_name = ''
++ if lastdot >= 0:
++ module_name = class_name[0:lastdot]
++ if module_name:
++ class_name = class_name[lastdot+1:]
++ module = __import__(module_name, globals(), locals(), [''])
++ if hasattr(module, class_name):
++ class_ = getattr(module, class_name)
++ class_instance = class_()
++ if isinstance(class_instance, class_type):
++ return class_instance
++ return None
++
++
++def FixLineEnd(text, line_end):
++ # First normalize
++ text = text.replace('\r\n', '\n')
++ text = text.replace('\r', '\n')
++ # Then fix
++ text = text.replace('\n', line_end)
++ return text
++
++
++def BoolToString(bool):
++ if bool:
++ return 'true'
++ else:
++ return 'false'
++
++
++verbose = False
++extra_verbose = False
++
++def IsVerbose():
++ return verbose
++
++def IsExtraVerbose():
++ return extra_verbose
++
++def ParseDefine(define):
++ '''Parses a define argument and returns the name and value.
++
++ The format is either "NAME=VAL" or "NAME", using True as the default value.
++ Values of "1"/"true" and "0"/"false" are transformed to True and False
++ respectively.
++
++ Args:
++ define: a string of the form "NAME=VAL" or "NAME".
++
++ Returns:
++ A (name, value) pair. name is a string, value a string or boolean.
++ '''
++ parts = [part.strip() for part in define.split('=', 1)]
++ assert len(parts) >= 1
++ name = parts[0]
++ val = True
++ if len(parts) > 1:
++ val = parts[1]
++ if val == "1" or val == "true": val = True
++ elif val == "0" or val == "false": val = False
++ return (name, val)
++
++
++class Substituter(object):
++ '''Finds and substitutes variable names in text strings.
++
++ Given a dictionary of variable names and values, prepares to
++ search for patterns of the form [VAR_NAME] in a text.
++ The value will be substituted back efficiently.
++ Also applies to tclib.Message objects.
++ '''
++
++ def __init__(self):
++ '''Create an empty substituter.'''
++ self.substitutions_ = {}
++ self.dirty_ = True
++
++ def AddSubstitutions(self, subs):
++ '''Add new values to the substitutor.
++
++ Args:
++ subs: A dictionary of new substitutions.
++ '''
++ self.substitutions_.update(subs)
++ self.dirty_ = True
++
++ def AddMessages(self, messages, lang):
++ '''Adds substitutions extracted from node.Message objects.
++
++ Args:
++ messages: a list of node.Message objects.
++ lang: The translation language to use in substitutions.
++ '''
++ subs = [(str(msg.attrs['name']), msg.Translate(lang)) for msg in messages]
++ self.AddSubstitutions(dict(subs))
++ self.dirty_ = True
++
++ def GetExp(self):
++ '''Obtain a regular expression that will find substitution keys in text.
++
++ Create and cache if the substituter has been updated. Use the cached value
++ otherwise. Keys will be enclosed in [square brackets] in text.
++
++ Returns:
++ A regular expression object.
++ '''
++ if self.dirty_:
++ components = [r'\[%s\]' % (k,) for k in self.substitutions_]
++ self.exp = re.compile(r'(%s)' % ('|'.join(components),))
++ self.dirty_ = False
++ return self.exp
++
++ def Substitute(self, text):
++ '''Substitute the variable values in the given text.
++
++ Text of the form [message_name] will be replaced by the message's value.
++
++ Args:
++ text: A string of text.
++
++ Returns:
++ A string of text with substitutions done.
++ '''
++ return ''.join([self._SubFragment(f) for f in self.GetExp().split(text)])
++
++ def _SubFragment(self, fragment):
++ '''Utility function for Substitute.
++
++ Performs a simple substitution if the fragment is exactly of the form
++ [message_name].
++
++ Args:
++ fragment: A simple string.
++
++ Returns:
++ A string with the substitution done.
++ '''
++ if len(fragment) > 2 and fragment[0] == '[' and fragment[-1] == ']':
++ sub = self.substitutions_.get(fragment[1:-1], None)
++ if sub is not None:
++ return sub
++ return fragment
++
++ def SubstituteMessage(self, msg):
++ '''Apply substitutions to a tclib.Message object.
++
++ Text of the form [message_name] will be replaced by a new placeholder,
++ whose presentation will take the form the message_name_{UsageCount}, and
++ whose example will be the message's value. Existing placeholders are
++ not affected.
++
++ Args:
++ msg: A tclib.Message object.
++
++ Returns:
++ A tclib.Message object, with substitutions done.
++ '''
++ from grit import tclib # avoid circular import
++ counts = {}
++ text = msg.GetPresentableContent()
++ placeholders = []
++ newtext = ''
++ for f in self.GetExp().split(text):
++ sub = self._SubFragment(f)
++ if f != sub:
++ f = str(f)
++ count = counts.get(f, 0) + 1
++ counts[f] = count
++ name = "%s_%d" % (f[1:-1], count)
++ placeholders.append(tclib.Placeholder(name, f, sub))
++ newtext += name
++ else:
++ newtext += f
++ if placeholders:
++ return tclib.Message(newtext, msg.GetPlaceholders() + placeholders,
++ msg.GetDescription(), msg.GetMeaning())
++ else:
++ return msg
++
++
++class TempDir(object):
++ '''Creates files with the specified contents in a temporary directory,
++ for unit testing.
++ '''
++
++ def __init__(self, file_data, mode='w'):
++ self._tmp_dir_name = tempfile.mkdtemp()
++ assert not os.listdir(self.GetPath())
++ for name, contents in file_data.items():
++ file_path = self.GetPath(name)
++ dir_path = os.path.split(file_path)[0]
++ if not os.path.exists(dir_path):
++ os.makedirs(dir_path)
++ with open(file_path, mode) as f:
++ f.write(file_data[name])
++
++ def __enter__(self):
++ return self
++
++ def __exit__(self, *exc_info):
++ self.CleanUp()
++
++ def CleanUp(self):
++ shutil.rmtree(self.GetPath())
++
++ def GetPath(self, name=''):
++ name = os.path.join(self._tmp_dir_name, name)
++ assert name.startswith(self._tmp_dir_name)
++ return name
++
++ def AsCurrentDir(self):
++ return self._AsCurrentDirClass(self.GetPath())
++
++ class _AsCurrentDirClass(object):
++ def __init__(self, path):
++ self.path = path
++ def __enter__(self):
++ self.oldpath = os.getcwd()
++ os.chdir(self.path)
++ def __exit__(self, *exc_info):
++ os.chdir(self.oldpath)
+diff --git a/tools/grit/grit/util_unittest.py b/tools/grit/grit/util_unittest.py
+new file mode 100644
+index 0000000000..7d6efaf858
+--- /dev/null
++++ b/tools/grit/grit/util_unittest.py
+@@ -0,0 +1,118 @@
++#!/usr/bin/env python
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++'''Unit test that checks some of util functions.
++'''
++
++from __future__ import print_function
++
++import os
++import sys
++if __name__ == '__main__':
++ sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
++
++import unittest
++
++import six
++
++from grit import util
++
++
++class UtilUnittest(unittest.TestCase):
++ ''' Tests functions from util
++ '''
++
++ def testNewClassInstance(self):
++ # Test short class name with no fully qualified package name
++ # Should fail, it is not supported by the function now (as documented)
++ cls = util.NewClassInstance('grit.util.TestClassToLoad',
++ TestBaseClassToLoad)
++ self.failUnless(cls == None)
++
++ # Test non existent class name
++ cls = util.NewClassInstance('grit.util_unittest.NotExistingClass',
++ TestBaseClassToLoad)
++ self.failUnless(cls == None)
++
++ # Test valid class name and valid base class
++ cls = util.NewClassInstance('grit.util_unittest.TestClassToLoad',
++ TestBaseClassToLoad)
++ self.failUnless(isinstance(cls, TestBaseClassToLoad))
++
++ # Test valid class name with wrong hierarchy
++ cls = util.NewClassInstance('grit.util_unittest.TestClassNoBase',
++ TestBaseClassToLoad)
++ self.failUnless(cls == None)
++
++ def testCanonicalLanguage(self):
++ self.failUnless(util.CanonicalLanguage('en') == 'en')
++ self.failUnless(util.CanonicalLanguage('pt_br') == 'pt-BR')
++ self.failUnless(util.CanonicalLanguage('pt-br') == 'pt-BR')
++ self.failUnless(util.CanonicalLanguage('pt-BR') == 'pt-BR')
++ self.failUnless(util.CanonicalLanguage('pt/br') == 'pt-BR')
++ self.failUnless(util.CanonicalLanguage('pt/BR') == 'pt-BR')
++ self.failUnless(util.CanonicalLanguage('no_no_bokmal') == 'no-NO-BOKMAL')
++
++ def testUnescapeHtml(self):
++ self.failUnless(util.UnescapeHtml('&#1010;') == six.unichr(1010))
++ self.failUnless(util.UnescapeHtml('&#xABcd;') == six.unichr(43981))
++
++ def testRelativePath(self):
++ """ Verify that MakeRelativePath works in some tricky cases."""
++
++ def TestRelativePathCombinations(base_path, other_path, expected_result):
++ """ Verify that the relative path function works for
++ the given paths regardless of whether or not they end with
++ a trailing slash."""
++ for path1 in [base_path, base_path + os.path.sep]:
++ for path2 in [other_path, other_path + os.path.sep]:
++ result = util.MakeRelativePath(path1, path2)
++ self.failUnless(result == expected_result)
++
++ # set-up variables
++ root_dir = 'c:%sa' % os.path.sep
++ result1 = '..%sabc' % os.path.sep
++ path1 = root_dir + 'bc'
++ result2 = 'bc'
++ path2 = '%s%s%s' % (root_dir, os.path.sep, result2)
++ # run the tests
++ TestRelativePathCombinations(root_dir, path1, result1)
++ TestRelativePathCombinations(root_dir, path2, result2)
++
++ def testReadFile(self):
++ def Test(data, encoding, expected_result):
++ with open('testfile', 'wb') as f:
++ f.write(data)
++ self.assertEqual(util.ReadFile('testfile', encoding), expected_result)
++
++ test_std_newline = b'\xEF\xBB\xBFabc\ndef' # EF BB BF is UTF-8 BOM
++ newlines = [b'\n', b'\r\n', b'\r']
++
++ with util.TempDir({}) as tmp_dir:
++ with tmp_dir.AsCurrentDir():
++ for newline in newlines:
++ test = test_std_newline.replace(b'\n', newline)
++ Test(test, util.BINARY, test)
++ # utf-8 doesn't strip BOM
++ Test(test, 'utf-8', test_std_newline.decode('utf-8'))
++ # utf-8-sig strips BOM
++ Test(test, 'utf-8-sig', test_std_newline.decode('utf-8')[1:])
++ # test another encoding
++ Test(test, 'cp1252', test_std_newline.decode('cp1252'))
++ self.assertRaises(UnicodeDecodeError, Test, b'\x80', 'utf-8', None)
++
++
++class TestBaseClassToLoad(object):
++ pass
++
++class TestClassToLoad(TestBaseClassToLoad):
++ pass
++
++class TestClassNoBase(object):
++ pass
++
++
++if __name__ == '__main__':
++ unittest.main()
+diff --git a/tools/grit/grit/xtb_reader.py b/tools/grit/grit/xtb_reader.py
+new file mode 100644
+index 0000000000..e0f842588a
+--- /dev/null
++++ b/tools/grit/grit/xtb_reader.py
+@@ -0,0 +1,140 @@
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++'''Fast and efficient parser for XTB files.
++'''
++
++from __future__ import print_function
++
++import sys
++import xml.sax
++import xml.sax.handler
++
++import grit.node.base
++
++
++class XtbContentHandler(xml.sax.handler.ContentHandler):
++ '''A content handler that calls a given callback function for each
++ translation in the XTB file.
++ '''
++
++ def __init__(self, callback, defs=None, debug=False, target_platform=None):
++ self.callback = callback
++ self.debug = debug
++ # 0 if we are not currently parsing a translation, otherwise the message
++ # ID of that translation.
++ self.current_id = 0
++ # Empty if we are not currently parsing a translation, otherwise the
++ # parts we have for that translation - a list of tuples
++ # (is_placeholder, text)
++ self.current_structure = []
++ # Set to the language ID when we see the <translationbundle> node.
++ self.language = ''
++ # Keep track of the if block we're inside. We can't nest ifs.
++ self.if_expr = None
++ # Root defines to be used with if expr.
++ if defs:
++ self.defines = defs
++ else:
++ self.defines = {}
++ # Target platform for build.
++ if target_platform:
++ self.target_platform = target_platform
++ else:
++ self.target_platform = sys.platform
++
++ def startElement(self, name, attrs):
++ if name == 'translation':
++ assert self.current_id == 0 and len(self.current_structure) == 0, (
++ "Didn't expect a <translation> element here.")
++ self.current_id = attrs.getValue('id')
++ elif name == 'ph':
++ assert self.current_id != 0, "Didn't expect a <ph> element here."
++ self.current_structure.append((True, attrs.getValue('name')))
++ elif name == 'translationbundle':
++ self.language = attrs.getValue('lang')
++ elif name in ('if', 'then', 'else'):
++ assert self.if_expr is None, "Can't nest <if> or use <else> in xtb files"
++ self.if_expr = attrs.getValue('expr')
++
++ def endElement(self, name):
++ if name == 'translation':
++ assert self.current_id != 0
++
++ defs = self.defines
++ def pp_ifdef(define):
++ return define in defs
++ def pp_if(define):
++ return define in defs and defs[define]
++
++ # If we're in an if block, only call the callback (add the translation)
++ # if the expression is True.
++ should_run_callback = True
++ if self.if_expr:
++ should_run_callback = grit.node.base.Node.EvaluateExpression(
++ self.if_expr, self.defines, self.target_platform)
++ if should_run_callback:
++ self.callback(self.current_id, self.current_structure)
++
++ self.current_id = 0
++ self.current_structure = []
++ elif name == 'if':
++ assert self.if_expr is not None
++ self.if_expr = None
++
++ def characters(self, content):
++ if self.current_id != 0:
++ # We are inside a <translation> node so just add the characters to our
++ # structure.
++ #
++ # This naive way of handling characters is OK because in the XTB format,
++ # <ph> nodes are always empty (always <ph name="XXX"/>) and whitespace
++ # inside the <translation> node should be preserved.
++ self.current_structure.append((False, content))
++
++
++class XtbErrorHandler(xml.sax.handler.ErrorHandler):
++ def error(self, exception):
++ pass
++
++ def fatalError(self, exception):
++ raise exception
++
++ def warning(self, exception):
++ pass
++
++
++def Parse(xtb_file, callback_function, defs=None, debug=False,
++ target_platform=None):
++ '''Parse xtb_file, making a call to callback_function for every translation
++ in the XTB file.
++
++ The callback function must have the signature as described below. The 'parts'
++ parameter is a list of tuples (is_placeholder, text). The 'text' part is
++ either the raw text (if is_placeholder is False) or the name of the placeholder
++ (if is_placeholder is True).
++
++ Args:
++ xtb_file: open('fr.xtb', 'rb')
++ callback_function: def Callback(msg_id, parts): pass
++ defs: None, or a dictionary of preprocessor definitions.
++ debug: Default False. Set True for verbose debug output.
++ target_platform: None, or a sys.platform-like identifier of the build
++ target platform.
++
++ Return:
++ The language of the XTB, e.g. 'fr'
++ '''
++ # Start by advancing the file pointer past the DOCTYPE thing, as the TC
++ # uses a path to the DTD that only works in Unix.
++ # TODO(joi) Remove this ugly hack by getting the TC gang to change the
++ # XTB files somehow?
++ front_of_file = xtb_file.read(1024)
++ xtb_file.seek(front_of_file.find(b'<translationbundle'))
++
++ handler = XtbContentHandler(callback=callback_function, defs=defs,
++ debug=debug, target_platform=target_platform)
++ xml.sax.parse(xtb_file, handler)
++ assert handler.language != ''
++ return handler.language
+diff --git a/tools/grit/grit/xtb_reader_unittest.py b/tools/grit/grit/xtb_reader_unittest.py
+new file mode 100644
+index 0000000000..79c0ac9ef1
+--- /dev/null
++++ b/tools/grit/grit/xtb_reader_unittest.py
+@@ -0,0 +1,110 @@
++#!/usr/bin/env python
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++'''Unit tests for grit.xtb_reader'''
++
++from __future__ import print_function
++
++import io
++import os
++import sys
++if __name__ == '__main__':
++ sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
++
++import unittest
++
++from grit import util
++from grit import xtb_reader
++from grit.node import empty
++
++
++class XtbReaderUnittest(unittest.TestCase):
++ def testParsing(self):
++ xtb_file = io.BytesIO(b'''<?xml version="1.0" encoding="UTF-8"?>
++ <!DOCTYPE translationbundle>
++ <translationbundle lang="fr">
++ <translation id="5282608565720904145">Bingo.</translation>
++ <translation id="2955977306445326147">Bongo longo.</translation>
++ <translation id="238824332917605038">Hullo</translation>
++ <translation id="6629135689895381486"><ph name="PROBLEM_REPORT"/> peut <ph name="START_LINK"/>utilisation excessive de majuscules<ph name="END_LINK"/>.</translation>
++ <translation id="7729135689895381486">Hello
++this is another line
++and another
++
++and another after a blank line.</translation>
++ </translationbundle>''')
++
++ messages = []
++ def Callback(id, structure):
++ messages.append((id, structure))
++ xtb_reader.Parse(xtb_file, Callback)
++ self.failUnless(len(messages[0][1]) == 1)
++ self.failUnless(messages[3][1][0]) # PROBLEM_REPORT placeholder
++ self.failUnless(messages[4][0] == '7729135689895381486')
++ self.failUnless(messages[4][1][7][1] == 'and another after a blank line.')
++
++ def testParsingIntoMessages(self):
++ root = util.ParseGrdForUnittest('''
++ <messages>
++ <message name="ID_MEGA">Fantastic!</message>
++ <message name="ID_HELLO_USER">Hello <ph name="USERNAME">%s<ex>Joi</ex></ph></message>
++ </messages>''')
++
++ msgs, = root.GetChildrenOfType(empty.MessagesNode)
++ clique_mega = msgs.children[0].GetCliques()[0]
++ msg_mega = clique_mega.GetMessage()
++ clique_hello_user = msgs.children[1].GetCliques()[0]
++ msg_hello_user = clique_hello_user.GetMessage()
++
++ xtb_file = io.BytesIO(b'''<?xml version="1.0" encoding="UTF-8"?>
++ <!DOCTYPE translationbundle>
++ <translationbundle lang="is">
++ <translation id="%s">Meirihattar!</translation>
++ <translation id="%s">Saelir <ph name="USERNAME"/></translation>
++ </translationbundle>''' % (
++ msg_mega.GetId().encode('utf-8'),
++ msg_hello_user.GetId().encode('utf-8')))
++
++ xtb_reader.Parse(xtb_file,
++ msgs.UberClique().GenerateXtbParserCallback('is'))
++ self.assertEqual('Meirihattar!',
++ clique_mega.MessageForLanguage('is').GetRealContent())
++ self.failUnless('Saelir %s',
++ clique_hello_user.MessageForLanguage('is').GetRealContent())
++
++ def testIfNodesWithUseNameForId(self):
++ root = util.ParseGrdForUnittest('''
++ <messages>
++ <message name="ID_BINGO" use_name_for_id="true">Bingo!</message>
++ </messages>''')
++ msgs, = root.GetChildrenOfType(empty.MessagesNode)
++ clique = msgs.children[0].GetCliques()[0]
++ msg = clique.GetMessage()
++
++ xtb_file = io.BytesIO(b'''<?xml version="1.0" encoding="UTF-8"?>
++ <!DOCTYPE translationbundle>
++ <translationbundle lang="is">
++ <if expr="is_linux">
++ <translation id="ID_BINGO">Bongo!</translation>
++ </if>
++ <if expr="not is_linux">
++ <translation id="ID_BINGO">Congo!</translation>
++ </if>
++ </translationbundle>''')
++ xtb_reader.Parse(xtb_file,
++ msgs.UberClique().GenerateXtbParserCallback('is'),
++ target_platform='darwin')
++ self.assertEqual('Congo!', clique.MessageForLanguage('is').GetRealContent())
++
++ def testParseLargeFile(self):
++ def Callback(id, structure):
++ pass
++ path = util.PathFromRoot('grit/testdata/generated_resources_fr.xtb')
++ with open(path, 'rb') as xtb:
++ xtb_reader.Parse(xtb, Callback)
++
++
++if __name__ == '__main__':
++ unittest.main()
+diff --git a/tools/grit/grit_info.py b/tools/grit/grit_info.py
+new file mode 100644
+index 0000000000..55738f25f6
+--- /dev/null
++++ b/tools/grit/grit_info.py
+@@ -0,0 +1,173 @@
++#!/usr/bin/env python
++# Copyright (c) 2012 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++'''Tool to determine inputs and outputs of a grit file.
++'''
++
++from __future__ import print_function
++
++import optparse
++import os
++import posixpath
++import sys
++
++from grit import grd_reader
++from grit import util
++
++class WrongNumberOfArguments(Exception):
++ pass
++
++
++def Outputs(filename, defines, ids_file, target_platform=None):
++ grd = grd_reader.Parse(
++ filename, defines=defines, tags_to_ignore=set(['messages']),
++ first_ids_file=ids_file, target_platform=target_platform)
++
++ target = []
++ lang_folders = {}
++ # Add all explicitly-specified output files
++ for output in grd.GetOutputFiles():
++ path = output.GetFilename()
++ target.append(path)
++
++ if path.endswith('.h'):
++ path, filename = os.path.split(path)
++ if output.attrs['lang']:
++ lang_folders[output.attrs['lang']] = os.path.dirname(path)
++
++ return [t.replace('\\', '/') for t in target]
++
++
++def GritSourceFiles():
++ files = []
++ grit_root_dir = os.path.relpath(os.path.dirname(__file__), os.getcwd())
++ for root, dirs, filenames in os.walk(grit_root_dir):
++ grit_src = [os.path.join(root, f) for f in filenames
++ if f.endswith('.py') and not f.endswith('_unittest.py')]
++ files.extend(grit_src)
++ return sorted(files)
++
++
++def Inputs(filename, defines, ids_file, target_platform=None):
++ grd = grd_reader.Parse(
++ filename, debug=False, defines=defines, tags_to_ignore=set(['message']),
++ first_ids_file=ids_file, target_platform=target_platform)
++ files = set()
++ for lang, ctx, fallback in grd.GetConfigurations():
++ # TODO(tdanderson): Refactor all places which perform the action of setting
++ # output attributes on the root. See crbug.com/503637.
++ grd.SetOutputLanguage(lang or grd.GetSourceLanguage())
++ grd.SetOutputContext(ctx)
++ grd.SetFallbackToDefaultLayout(fallback)
++ for node in grd.ActiveDescendants():
++ with node:
++ if (node.name == 'structure' or node.name == 'skeleton' or
++ (node.name == 'file' and node.parent and
++ node.parent.name == 'translations')):
++ path = node.GetInputPath()
++ if path is not None:
++ files.add(grd.ToRealPath(path))
++
++ # If it's a flattened node, grab inlined resources too.
++ if node.name == 'structure' and node.attrs['flattenhtml'] == 'true':
++ node.RunPreSubstitutionGatherer()
++ files.update(node.GetHtmlResourceFilenames())
++ elif node.name == 'grit':
++ first_ids_file = node.GetFirstIdsFile()
++ if first_ids_file:
++ files.add(first_ids_file)
++ elif node.name == 'include':
++ files.add(grd.ToRealPath(node.GetInputPath()))
++ # If it's a flattened node, grab inlined resources too.
++ if node.attrs['flattenhtml'] == 'true':
++ files.update(node.GetHtmlResourceFilenames())
++ elif node.name == 'part':
++ files.add(util.normpath(os.path.join(os.path.dirname(filename),
++ node.GetInputPath())))
++
++ cwd = os.getcwd()
++ return [os.path.relpath(f, cwd) for f in sorted(files)]
++
++
++def PrintUsage():
++ print('USAGE: ./grit_info.py --inputs [-D foo] [-f resource_ids] <grd-file>')
++ print(' ./grit_info.py --outputs [-D foo] [-f resource_ids] ' +
++ '<out-prefix> <grd-file>')
++
++
++def DoMain(argv):
++ os.environ['cwd'] = os.getcwd()
++
++ parser = optparse.OptionParser()
++ parser.add_option("--inputs", action="store_true", dest="inputs")
++ parser.add_option("--outputs", action="store_true", dest="outputs")
++ parser.add_option("-D", action="append", dest="defines", default=[])
++ # grit build also supports '-E KEY=VALUE', support that to share command
++ # line flags.
++ parser.add_option("-E", action="append", dest="build_env", default=[])
++ parser.add_option("-p", action="store", dest="predetermined_ids_file")
++ parser.add_option("-w", action="append", dest="whitelist_files", default=[])
++ parser.add_option("-f", dest="ids_file", default="")
++ parser.add_option("-t", dest="target_platform", default=None)
++
++ options, args = parser.parse_args(argv)
++
++ defines = {}
++ for define in options.defines:
++ name, val = util.ParseDefine(define)
++ defines[name] = val
++
++ for env_pair in options.build_env:
++ (env_name, env_value) = env_pair.split('=', 1)
++ os.environ[env_name] = env_value
++
++ if options.inputs:
++ if len(args) > 1:
++ raise WrongNumberOfArguments("Expected 0 or 1 arguments for --inputs.")
++
++ inputs = []
++ if len(args) == 1:
++ filename = args[0]
++ inputs = Inputs(filename, defines, options.ids_file,
++ options.target_platform)
++
++ # Add in the grit source files. If one of these change, we want to re-run
++ # grit.
++ inputs.extend(GritSourceFiles())
++ inputs = [f.replace('\\', '/') for f in inputs]
++
++ if len(args) == 1:
++ # Include grd file as second input (works around gyp expecting it).
++ inputs.insert(1, args[0])
++ if options.whitelist_files:
++ inputs.extend(options.whitelist_files)
++ return '\n'.join(inputs)
++ elif options.outputs:
++ if len(args) != 2:
++ raise WrongNumberOfArguments(
++ "Expected exactly 2 arguments for --outputs.")
++
++ prefix, filename = args
++ outputs = [posixpath.join(prefix, f)
++ for f in Outputs(filename, defines,
++ options.ids_file, options.target_platform)]
++ return '\n'.join(outputs)
++ else:
++ raise WrongNumberOfArguments("Expected --inputs or --outputs.")
++
++
++def main(argv):
++ try:
++ result = DoMain(argv[1:])
++ except WrongNumberOfArguments as e:
++ PrintUsage()
++ print(e)
++ return 1
++ print(result)
++ return 0
++
++
++if __name__ == '__main__':
++ sys.exit(main(sys.argv))
+diff --git a/tools/grit/grit_rule.gni b/tools/grit/grit_rule.gni
+new file mode 100644
+index 0000000000..fb107ef1a3
+--- /dev/null
++++ b/tools/grit/grit_rule.gni
+@@ -0,0 +1,485 @@
++# Copyright 2014 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++# Instantiate grit. This will produce a script target to run grit (named
++# ${target_name}_grit), and a static library that compiles the .cc files.
++#
++# In general, code should depend on the static library. However, if the
++# generated files are only processed by other actions to generate other
++# files, it is possible to depend on the script target directly.
++#
++# Parameters
++#
++# source (required)
++# Path to .grd file.
++#
++# enable_input_discovery_for_gn_analyze (default=true)
++# Runs grit_info.py via exec_script() when compute_inputs_for_analyze=true
++# in order to discover all files that affect this target.
++# Turn this off when the .grd file is generated, or an <include> with
++# flattenhtml=true points to a generated file.
++# For "gn analyze" to be correct with this arg disabled, all inputs
++# must be listed via |inputs|.
++#
++# inputs (optional)
++# List of additional files, required for grit to process source file.
++#
++# outputs (required)
++# List of outputs from grit, relative to the target_gen_dir. Grit will
++# verify at build time that this list is correct and will fail if there
++# is a mismatch between the outputs specified by the .grd file and the
++# outputs list here.
++#
++# To get this list, you can look in the .grd file for
++# <output filename="..." and put those filename here. The base directory
++# of the list in Grit and the output list specified in the GN grit target
++# are the same (the target_gen_dir) so you can generally copy the names
++# exactly.
++#
++# To get the list of outputs programatically, run:
++# python tools/grit/grit_info.py --outputs . path/to/your.grd
++# And strip the leading "./" from the output files.
++#
++# defines (optional)
++# Extra defines to pass to grit (on top of the global grit_defines list).
++#
++# grit_flags (optional)
++# List of strings containing extra command-line flags to pass to Grit.
++#
++# resource_ids (optional)
++# Path to a grit "firstidsfile". Default is
++# //tools/gritsettings/resource_ids. Set to "" to use the value specified
++# in the <grit> nodes of the processed files.
++#
++# output_dir (optional)
++# Directory for generated files. If you specify this, you will often
++# want to specify output_name if the target name is not particularly
++# unique, since this can cause files from multiple grit targets to
++# overwrite each other.
++#
++# output_name (optional)
++# Provide an alternate base name for the generated files, like the .d
++# files. Normally these are based on the target name and go in the
++# output_dir, but if multiple targets with the same name end up in
++# the same output_dir, they can collide.
++#
++# configs (optional)
++# List of additional configs to be applied to the generated target.
++#
++# deps (optional)
++# testonly (optional)
++# visibility (optional)
++# Normal meaning.
++#
++# Example
++#
++# grit("my_resources") {
++# # Source and outputs are required.
++# source = "myfile.grd"
++# outputs = [
++# "foo_strings.h",
++# "foo_strings.pak",
++# ]
++#
++# grit_flags = [ "-E", "foo=bar" ] # Optional extra flags.
++# # You can also put deps here if the grit source depends on generated
++# # files.
++# }
++import("//build/config/chrome_build.gni")
++import("//build/config/chromeos/ui_mode.gni")
++import("//build/config/compiler/compiler.gni")
++import("//build/config/compute_inputs_for_analyze.gni")
++import("//build/config/crypto.gni")
++import("//build/config/features.gni")
++import("//build/config/sanitizers/sanitizers.gni")
++import("//build/config/ui.gni")
++import("//build/toolchain/gcc_toolchain.gni")
++
++declare_args() {
++ enable_resource_whitelist_generation = is_android && is_official_build
++}
++
++if (enable_resource_whitelist_generation) {
++ assert(target_os == "android" || target_os == "win",
++ "unsupported platform for resource whitelist generation")
++ assert(
++ symbol_level > 0 && !strip_debug_info && !is_component_build,
++ "resource whitelist generation only works on non-component builds with debug info enabled.")
++}
++
++grit_defines = []
++
++if (is_mac || is_win || is_linux || is_chromeos || is_ios) {
++ grit_defines += [
++ "-D",
++ "scale_factors=2x",
++ ]
++}
++
++# Mac and iOS want Title Case strings.
++use_titlecase_in_grd_files = is_apple
++if (use_titlecase_in_grd_files) {
++ grit_defines += [
++ "-D",
++ "use_titlecase",
++ ]
++}
++
++if (is_chrome_branded) {
++ grit_defines += [
++ "-D",
++ "_google_chrome",
++ "-E",
++ "CHROMIUM_BUILD=google_chrome",
++ ]
++} else {
++ grit_defines += [
++ "-D",
++ "_chromium",
++ "-E",
++ "CHROMIUM_BUILD=chromium",
++ ]
++}
++
++if (is_chromeos) {
++ grit_defines += [
++ "-D",
++ "chromeos",
++ ]
++}
++
++if (chromeos_is_browser_only) {
++ grit_defines += [
++ "-D",
++ "lacros",
++ ]
++}
++
++if (is_desktop_linux) {
++ grit_defines += [
++ "-D",
++ "desktop_linux",
++ ]
++}
++
++if (toolkit_views) {
++ grit_defines += [
++ "-D",
++ "toolkit_views",
++ ]
++}
++
++if (use_aura) {
++ grit_defines += [
++ "-D",
++ "use_aura",
++ ]
++}
++
++if (use_nss_certs) {
++ grit_defines += [
++ "-D",
++ "use_nss_certs",
++ ]
++}
++
++if (use_ozone) {
++ grit_defines += [
++ "-D",
++ "use_ozone",
++ ]
++}
++
++if (is_android) {
++ grit_defines += [
++ "-E",
++ "ANDROID_JAVA_TAGGED_ONLY=true",
++ ]
++}
++
++# When cross-compiling, explicitly pass the target system to grit.
++if (current_toolchain != host_toolchain) {
++ if (is_android) {
++ grit_defines += [
++ "-t",
++ "android",
++ ]
++ }
++ if (is_ios) {
++ grit_defines += [
++ "-t",
++ "ios",
++ ]
++ }
++ if (is_linux || is_chromeos) {
++ grit_defines += [
++ "-t",
++ "linux2",
++ ]
++ }
++ if (is_mac) {
++ grit_defines += [
++ "-t",
++ "darwin",
++ ]
++ }
++ if (is_win) {
++ grit_defines += [
++ "-t",
++ "win32",
++ ]
++ }
++}
++
++_strip_resource_files = is_android && is_official_build
++_js_minifier = "//tools/grit/minify_with_uglify.py"
++_css_minifier = "//tools/grit/minimize_css.py"
++
++grit_resource_id_target = "//tools/gritsettings:default_resource_ids"
++grit_resource_id_file =
++ get_label_info(grit_resource_id_target, "target_gen_dir") +
++ "/default_resource_ids"
++grit_info_script = "//tools/grit/grit_info.py"
++
++# TODO(asvitkine): Add predetermined ids files for other platforms.
++grit_predetermined_resource_ids_file = ""
++if (is_mac) {
++ grit_predetermined_resource_ids_file =
++ "//tools/gritsettings/startup_resources_mac.txt"
++}
++if (is_win) {
++ grit_predetermined_resource_ids_file =
++ "//tools/gritsettings/startup_resources_win.txt"
++}
++
++template("grit") {
++ if (defined(invoker.output_dir)) {
++ _output_dir = invoker.output_dir
++ } else {
++ _output_dir = target_gen_dir
++ }
++
++ _grit_outputs =
++ get_path_info(rebase_path(invoker.outputs, ".", _output_dir), "abspath")
++
++ # Add .info output for all pak files
++ _pak_info_outputs = []
++ foreach(output, _grit_outputs) {
++ if (get_path_info(output, "extension") == "pak") {
++ _pak_info_outputs += [ output + ".info" ]
++ }
++ }
++
++ if (defined(invoker.output_name)) {
++ _grit_output_name = invoker.output_name
++ } else {
++ _grit_output_name = target_name
++ }
++
++ _grit_custom_target = target_name + "_grit"
++ action(_grit_custom_target) {
++ testonly = defined(invoker.testonly) && invoker.testonly
++
++ script = "//tools/grit/grit.py"
++ depfile = "$target_gen_dir/$target_name.d"
++
++ inputs = [ invoker.source ]
++ deps = [ "//tools/grit:grit_sources" ]
++ outputs = [ "${depfile}.stamp" ] + _grit_outputs + _pak_info_outputs
++
++ _grit_flags = grit_defines
++
++ # Add extra defines with -D flags.
++ if (defined(invoker.defines)) {
++ foreach(i, invoker.defines) {
++ _grit_flags += [
++ "-D",
++ i,
++ ]
++ }
++ }
++
++ if (defined(invoker.grit_flags)) {
++ _grit_flags += invoker.grit_flags
++ }
++
++ _rebased_source_path = rebase_path(invoker.source, root_build_dir)
++ _enable_grit_info =
++ !defined(invoker.enable_input_discovery_for_gn_analyze) ||
++ invoker.enable_input_discovery_for_gn_analyze
++ if (_enable_grit_info && compute_inputs_for_analyze) {
++ # Only call exec_script when the user has explicitly opted into greater
++ # precision at the expense of performance.
++ _rel_inputs = exec_script("//tools/grit/grit_info.py",
++ [
++ "--inputs",
++ _rebased_source_path,
++ ] + _grit_flags,
++ "list lines")
++ inputs += rebase_path(_rel_inputs, ".", root_build_dir)
++ }
++
++ args = [
++ "-i",
++ _rebased_source_path,
++ "build",
++ "-o",
++ rebase_path(_output_dir, root_build_dir),
++ "--depdir",
++ ".",
++ "--depfile",
++ rebase_path(depfile, root_build_dir),
++ "--write-only-new=1",
++ "--depend-on-stamp",
++ ] + _grit_flags
++
++ # Add brotli executable if using brotli.
++ if (defined(invoker.use_brotli) && invoker.use_brotli) {
++ _brotli_target = "//third_party/brotli:brotli($host_toolchain)"
++ _brotli_executable = get_label_info(_brotli_target, "root_out_dir") +
++ "/" + get_label_info(_brotli_target, "name")
++ if (host_os == "win") {
++ _brotli_executable += ".exe"
++ }
++
++ inputs += [ _brotli_executable ]
++ args += [
++ "--brotli",
++ rebase_path(_brotli_executable, root_build_dir),
++ ]
++ }
++
++ _resource_ids = grit_resource_id_file
++ if (defined(invoker.resource_ids)) {
++ _resource_ids = invoker.resource_ids
++ }
++
++ if (_resource_ids != "") {
++ inputs += [ _resource_ids ]
++ args += [
++ "-f",
++ rebase_path(_resource_ids, root_build_dir),
++ ]
++ if (_resource_ids == grit_resource_id_file) {
++ deps += [ grit_resource_id_target ]
++ }
++ }
++ if (grit_predetermined_resource_ids_file != "") {
++ inputs += [ grit_predetermined_resource_ids_file ]
++ args += [
++ "-p",
++ rebase_path(grit_predetermined_resource_ids_file, root_build_dir),
++ ]
++ }
++
++ # We want to make sure the declared outputs actually match what Grit is
++ # writing. We write the list to a file (some of the output lists are long
++ # enough to not fit on a Windows command line) and ask Grit to verify those
++ # are the actual outputs at runtime.
++ _asserted_list_file =
++ "$target_out_dir/${_grit_output_name}_expected_outputs.txt"
++ write_file(_asserted_list_file,
++ rebase_path(invoker.outputs, root_build_dir, _output_dir))
++ inputs += [ _asserted_list_file ]
++ args += [
++ "--assert-file-list",
++ rebase_path(_asserted_list_file, root_build_dir),
++ ]
++
++ if (enable_resource_whitelist_generation) {
++ _rc_grit_outputs = []
++ foreach(output, _grit_outputs) {
++ if (get_path_info(output, "extension") == "rc") {
++ _rc_grit_outputs += [ output ]
++ }
++ }
++
++ if (_rc_grit_outputs != []) {
++ # Resource whitelisting cannot be used with .rc files.
++ # Make sure that there aren't any .pak outputs which would require
++ # whitelist annotations.
++ assert(_pak_info_outputs == [], "can't combine .pak and .rc outputs")
++ } else {
++ args += [ "--whitelist-support" ]
++ }
++ }
++ if (_strip_resource_files) {
++ _js_minifier_command = rebase_path(_js_minifier, root_build_dir)
++ _css_minifier_command = rebase_path(_css_minifier, root_build_dir)
++ args += [
++ "--js-minifier",
++ _js_minifier_command,
++ "--css-minifier",
++ _css_minifier_command,
++ ]
++ inputs += [
++ _js_minifier,
++ _css_minifier,
++ ]
++ }
++
++ if (defined(invoker.visibility)) {
++ # This needs to include both what the invoker specified (since they
++ # probably include generated headers from this target), as well as the
++ # generated source set (since there's no guarantee that the visibility
++ # specified by the invoker includes our target).
++ #
++ # Only define visibility at all if the invoker specified it. Otherwise,
++ # we want to keep the public "no visibility specified" default.
++ visibility = [ ":${invoker.target_name}" ] + invoker.visibility
++ }
++
++ if (defined(invoker.use_brotli) && invoker.use_brotli) {
++ if (is_mac && is_asan) {
++ deps += [ "//tools/grit:brotli_mac_asan_workaround" ]
++ } else {
++ deps += [ "//third_party/brotli:brotli($host_toolchain)" ]
++ }
++ }
++ if (defined(invoker.deps)) {
++ deps += invoker.deps
++ }
++ if (defined(invoker.inputs)) {
++ inputs += invoker.inputs
++ }
++ }
++
++ # This is the thing that people actually link with, it must be named the
++ # same as the argument the template was invoked with.
++ source_set(target_name) {
++ testonly = defined(invoker.testonly) && invoker.testonly
++
++ # Since we generate a file, we need to be run before the targets that
++ # depend on us.
++ sources = []
++ foreach(_output, _grit_outputs) {
++ _extension = get_path_info(_output, "extension")
++ if (_extension != "json" && _extension != "gz" && _extension != "pak" &&
++ _extension != "xml") {
++ sources += [ _output ]
++ }
++ }
++
++ # Deps set on the template invocation will go on the action that runs
++ # grit above rather than this library. This target needs to depend on the
++ # action publicly so other scripts can take the outputs from the grit
++ # script as inputs.
++ public_deps = [ ":$_grit_custom_target" ]
++
++ deps = [ "//base" ]
++
++ if (defined(invoker.public_configs)) {
++ public_configs += invoker.public_configs
++ }
++
++ if (defined(invoker.configs)) {
++ configs += invoker.configs
++ }
++
++ if (defined(invoker.visibility)) {
++ visibility = invoker.visibility
++ }
++ output_name = _grit_output_name
++ }
++}
+diff --git a/tools/grit/minify_with_uglify.py b/tools/grit/minify_with_uglify.py
+new file mode 100644
+index 0000000000..788ffa6a75
+--- /dev/null
++++ b/tools/grit/minify_with_uglify.py
+@@ -0,0 +1,44 @@
++#!/usr/bin/env python
++# 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.
++
++from __future__ import print_function
++
++import os
++import sys
++import tempfile
++
++_HERE_PATH = os.path.dirname(__file__)
++_SRC_PATH = os.path.normpath(os.path.join(_HERE_PATH, '..', '..'))
++sys.path.append(os.path.join(_SRC_PATH, 'third_party', 'node'))
++
++import node
++import node_modules
++
++def Minify(source):
++ # Open two temporary files, so that uglify can read the input from one and
++ # write its output to the other.
++ with tempfile.NamedTemporaryFile(suffix='.js') as infile, \
++ tempfile.NamedTemporaryFile(suffix='.js') as outfile:
++ infile.write(source)
++ infile.flush();
++ node.RunNode([
++ node_modules.PathToUglify(), infile.name, '--output', outfile.name])
++ result = outfile.read()
++ return result
++
++
++def main():
++ orig_stdout = sys.stdout
++ result = ''
++ try:
++ sys.stdout = sys.stderr
++ result = Minify(sys.stdin.read())
++ finally:
++ sys.stdout = orig_stdout
++ print(result)
++
++
++if __name__ == '__main__':
++ main()
+diff --git a/tools/grit/minimize_css.py b/tools/grit/minimize_css.py
+new file mode 100644
+index 0000000000..2c3b8aeb1e
+--- /dev/null
++++ b/tools/grit/minimize_css.py
+@@ -0,0 +1,105 @@
++#!/usr/bin/env python
++# Copyright 2016 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++import re
++import sys
++
++class CSSMinimizer(object):
++
++ INITIAL = 0
++ MAYBE_COMMENT_START = 1
++ INSIDE_COMMENT = 2
++ MAYBE_COMMENT_END = 3
++ INSIDE_SINGLE_QUOTE = 4
++ INSIDE_SINGLE_QUOTE_ESCAPE = 5
++ INSIDE_DOUBLE_QUOTE = 6
++ INSIDE_DOUBLE_QUOTE_ESCAPE = 7
++
++ def __init__(self):
++ self._output = ''
++ self._codeblock = ''
++
++ def flush_codeblock(self):
++ stripped = re.sub(r"\s+", ' ', self._codeblock)
++ stripped = re.sub(r";?\s*(?P<op>[{};])\s*", r'\g<op>', stripped)
++ self._output += stripped
++ self._codeblock = ''
++
++ def parse(self, content):
++ state = self.INITIAL
++ for char in content:
++ if state == self.INITIAL:
++ if char == '/':
++ state = self.MAYBE_COMMENT_START
++ elif char == "'":
++ self.flush_codeblock()
++ self._output += char
++ state = self.INSIDE_SINGLE_QUOTE
++ elif char == '"':
++ self.flush_codeblock()
++ self._output += char
++ state = self.INSIDE_DOUBLE_QUOTE
++ else:
++ self._codeblock += char
++ elif state == self.MAYBE_COMMENT_START:
++ if char == '*':
++ self.flush_codeblock()
++ state = self.INSIDE_COMMENT
++ else:
++ self._codeblock += '/' + char
++ state = self.INITIAL
++ elif state == self.INSIDE_COMMENT:
++ if char == '*':
++ state = self.MAYBE_COMMENT_END
++ else:
++ pass
++ elif state == self.MAYBE_COMMENT_END:
++ if char == '/':
++ state = self.INITIAL
++ else:
++ state = self.INSIDE_COMMENT
++ elif state == self.INSIDE_SINGLE_QUOTE:
++ if char == '\\':
++ self._output += char
++ state = self.INSIDE_SINGLE_QUOTE_ESCAPE
++ elif char == "'":
++ self._output += char
++ state = self.INITIAL
++ else:
++ self._output += char
++ elif state == self.INSIDE_SINGLE_QUOTE_ESCAPE:
++ self._output += char
++ state = self.INSIDE_SINGLE_QUOTE
++ elif state == self.INSIDE_DOUBLE_QUOTE:
++ if char == '\\':
++ self._output += char
++ state = self.INSIDE_DOUBLE_QUOTE_ESCAPE
++ elif char == '"':
++ self._output += char
++ state = self.INITIAL
++ else:
++ self._output += char
++ elif state == self.INSIDE_DOUBLE_QUOTE_ESCAPE:
++ self._output += char
++ state = self.INSIDE_DOUBLE_QUOTE
++
++ self.flush_codeblock()
++ self._output = self._output.strip()
++ return self._output
++
++ @classmethod
++ def minimize_css(cls, content):
++ minimizer = CSSMinimizer()
++ return minimizer.parse(content)
++
++def main():
++ result = ''
++ try:
++ result = CSSMinimizer.minimize_css(sys.stdin.read())
++ finally:
++ print(result)
++
++if __name__ == '__main__':
++ main()
+diff --git a/tools/grit/minimize_css_unittest.py b/tools/grit/minimize_css_unittest.py
+new file mode 100644
+index 0000000000..cddc313078
+--- /dev/null
++++ b/tools/grit/minimize_css_unittest.py
+@@ -0,0 +1,58 @@
++#!/usr/bin/env python
++# Copyright 2016 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++import unittest
++
++import minimize_css
++
++
++class CSSMinimizerTest(unittest.TestCase):
++
++ def test_simple(self):
++ source = """
++ div {
++ color: blue;
++ }
++ """
++ minimized = minimize_css.CSSMinimizer.minimize_css(source)
++ self.assertEquals(minimized, "div{color: blue}")
++
++ def test_attribute_selectors(self):
++ source = """
++ input[type="search" i]::-webkit-textfield-decoration-container {
++ direction: ltr;
++ }
++ """
++ minimized = minimize_css.CSSMinimizer.minimize_css(source)
++ self.assertEquals(
++ minimized,
++ # pylint: disable=line-too-long
++ """input[type="search" i]::-webkit-textfield-decoration-container{direction: ltr}""")
++
++ def test_strip_comment(self):
++ source = """
++ /* header */
++ html {
++ /* inside block */
++ display: block;
++ }
++ /* footer */
++ """
++ minimized = minimize_css.CSSMinimizer.minimize_css(source)
++ self.assertEquals(minimized, "html{ display: block}")
++
++ def test_no_strip_inside_quotes(self):
++ source = """div[foo=' bar ']"""
++ minimized = minimize_css.CSSMinimizer.minimize_css(source)
++ self.assertEquals(minimized, source)
++
++ source = """div[foo=" bar "]"""
++ minimized = minimize_css.CSSMinimizer.minimize_css(source)
++ self.assertEquals(minimized, source)
++
++ def test_escape_string(self):
++ source = """content: " <a onclick=\\\"javascript: alert ( 'foobar' ); \\\">";"""
++ minimized = minimize_css.CSSMinimizer.minimize_css(source)
++ self.assertEquals(minimized, source)
+diff --git a/tools/grit/pak_util.py b/tools/grit/pak_util.py
+new file mode 100644
+index 0000000000..ede638bbe1
+--- /dev/null
++++ b/tools/grit/pak_util.py
+@@ -0,0 +1,223 @@
++#!/usr/bin/env python
++# Copyright 2017 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++"""A tool for interacting with .pak files.
++
++For details on the pak file format, see:
++https://dev.chromium.org/developers/design-documents/linuxresourcesandlocalizedstrings
++"""
++
++from __future__ import print_function
++
++import argparse
++import gzip
++import hashlib
++import os
++import shutil
++import sys
++import tempfile
++
++# Import grit first to get local third_party modules.
++import grit # pylint: disable=ungrouped-imports,unused-import
++
++import six
++
++from grit.format import data_pack
++
++
++def _RepackMain(args):
++ output_info_filepath = args.output_pak_file + '.info'
++ if args.compress:
++ # If the file needs to be compressed, call RePack with a tempfile path,
++ # then compress the tempfile to args.output_pak_file.
++ temp_outfile = tempfile.NamedTemporaryFile()
++ out_path = temp_outfile.name
++ # Strip any non .pak extension from the .info output file path.
++ splitext = os.path.splitext(args.output_pak_file)
++ if splitext[1] != '.pak':
++ output_info_filepath = splitext[0] + '.info'
++ else:
++ out_path = args.output_pak_file
++ data_pack.RePack(out_path, args.input_pak_files, args.whitelist,
++ args.suppress_removed_key_output,
++ output_info_filepath=output_info_filepath)
++ if args.compress:
++ with open(args.output_pak_file, 'wb') as out:
++ with gzip.GzipFile(filename='', mode='wb', fileobj=out, mtime=0) as outgz:
++ shutil.copyfileobj(temp_outfile, outgz)
++
++
++def _ExtractMain(args):
++ pak = data_pack.ReadDataPack(args.pak_file)
++ if args.textual_id:
++ info_dict = data_pack.ReadGrdInfo(args.pak_file)
++ for resource_id, payload in pak.resources.items():
++ filename = (
++ info_dict[resource_id].textual_id
++ if args.textual_id else str(resource_id))
++ path = os.path.join(args.output_dir, filename)
++ with open(path, 'w') as f:
++ f.write(payload)
++
++
++def _CreateMain(args):
++ pak = {}
++ for name in os.listdir(args.input_dir):
++ try:
++ resource_id = int(name)
++ except:
++ continue
++ filename = os.path.join(args.input_dir, name)
++ if os.path.isfile(filename):
++ with open(filename, 'rb') as f:
++ pak[resource_id] = f.read()
++ data_pack.WriteDataPack(pak, args.output_pak_file, data_pack.UTF8)
++
++
++def _PrintMain(args):
++ pak = data_pack.ReadDataPack(args.pak_file)
++ if args.textual_id:
++ info_dict = data_pack.ReadGrdInfo(args.pak_file)
++ output = args.output
++ encoding = 'binary'
++ if pak.encoding == 1:
++ encoding = 'utf-8'
++ elif pak.encoding == 2:
++ encoding = 'utf-16'
++ else:
++ encoding = '?' + str(pak.encoding)
++
++ output.write('version: {}\n'.format(pak.version))
++ output.write('encoding: {}\n'.format(encoding))
++ output.write('num_resources: {}\n'.format(len(pak.resources)))
++ output.write('num_aliases: {}\n'.format(len(pak.aliases)))
++ breakdown = ', '.join('{}: {}'.format(*x) for x in pak.sizes)
++ output.write('total_size: {} ({})\n'.format(pak.sizes.total, breakdown))
++
++ try_decode = args.decode and encoding.startswith('utf')
++ # Print IDs in ascending order, since that's the order in which they appear in
++ # the file (order is lost by Python dict).
++ for resource_id in sorted(pak.resources):
++ data = pak.resources[resource_id]
++ canonical_id = pak.aliases.get(resource_id, resource_id)
++ desc = '<data>'
++ if try_decode:
++ try:
++ desc = six.text_type(data, encoding)
++ if len(desc) > 60:
++ desc = desc[:60] + u'...'
++ desc = desc.replace('\n', '\\n')
++ except UnicodeDecodeError:
++ pass
++ sha1 = hashlib.sha1(data).hexdigest()[:10]
++ if args.textual_id:
++ textual_id = info_dict[resource_id].textual_id
++ canonical_textual_id = info_dict[canonical_id].textual_id
++ output.write(
++ u'Entry(id={}, canonical_id={}, size={}, sha1={}): {}\n'.format(
++ textual_id, canonical_textual_id, len(data), sha1,
++ desc).encode('utf-8'))
++ else:
++ output.write(
++ u'Entry(id={}, canonical_id={}, size={}, sha1={}): {}\n'.format(
++ resource_id, canonical_id, len(data), sha1, desc).encode('utf-8'))
++
++
++def _ListMain(args):
++ pak = data_pack.ReadDataPack(args.pak_file)
++ if args.textual_id or args.path:
++ info_dict = data_pack.ReadGrdInfo(args.pak_file)
++ fmt = ''.join([
++ '{id}', ' = {textual_id}' if args.textual_id else '',
++ ' @ {path}' if args.path else '', '\n'
++ ])
++ for resource_id in sorted(pak.resources):
++ item = info_dict[resource_id]
++ args.output.write(
++ fmt.format(textual_id=item.textual_id, id=item.id, path=item.path))
++ else:
++ for resource_id in sorted(pak.resources):
++ args.output.write('%d\n' % resource_id)
++
++
++def main():
++ parser = argparse.ArgumentParser(
++ description=__doc__, formatter_class=argparse.RawTextHelpFormatter)
++ # Subparsers are required by default under Python 2. Python 3 changed to
++ # not required, but didn't include a required option until 3.7. Setting
++ # the required member works in all versions (and setting dest name).
++ sub_parsers = parser.add_subparsers(dest='action')
++ sub_parsers.required = True
++
++ sub_parser = sub_parsers.add_parser('repack',
++ help='Combines several .pak files into one.')
++ sub_parser.add_argument('output_pak_file', help='File to create.')
++ sub_parser.add_argument('input_pak_files', nargs='+',
++ help='Input .pak files.')
++ sub_parser.add_argument('--whitelist',
++ help='Path to a whitelist used to filter output pak file resource IDs.')
++ sub_parser.add_argument('--suppress-removed-key-output', action='store_true',
++ help='Do not log which keys were removed by the whitelist.')
++ sub_parser.add_argument('--compress', dest='compress', action='store_true',
++ default=False, help='Compress output_pak_file using gzip.')
++ sub_parser.set_defaults(func=_RepackMain)
++
++ sub_parser = sub_parsers.add_parser('extract', help='Extracts pak file')
++ sub_parser.add_argument('pak_file')
++ sub_parser.add_argument('-o', '--output-dir', default='.',
++ help='Directory to extract to.')
++ sub_parser.add_argument(
++ '-t',
++ '--textual-id',
++ action='store_true',
++ help='Use textual resource ID (name) (from .info file) as filenames.')
++ sub_parser.set_defaults(func=_ExtractMain)
++
++ sub_parser = sub_parsers.add_parser('create',
++ help='Creates pak file from extracted directory.')
++ sub_parser.add_argument('output_pak_file', help='File to create.')
++ sub_parser.add_argument('-i', '--input-dir', default='.',
++ help='Directory to create from.')
++ sub_parser.set_defaults(func=_CreateMain)
++
++ sub_parser = sub_parsers.add_parser('print',
++ help='Prints all pak IDs and contents. Useful for diffing.')
++ sub_parser.add_argument('pak_file')
++ sub_parser.add_argument('--output', type=argparse.FileType('w'),
++ default=sys.stdout,
++ help='The resource list path to write (default stdout)')
++ sub_parser.add_argument('--no-decode', dest='decode', action='store_false',
++ default=True, help='Do not print entry data.')
++ sub_parser.add_argument(
++ '-t',
++ '--textual-id',
++ action='store_true',
++ help='Print textual ID (name) (from .info file) instead of the ID.')
++ sub_parser.set_defaults(func=_PrintMain)
++
++ sub_parser = sub_parsers.add_parser('list-id',
++ help='Outputs all resource IDs to a file.')
++ sub_parser.add_argument('pak_file')
++ sub_parser.add_argument('--output', type=argparse.FileType('w'),
++ default=sys.stdout,
++ help='The resource list path to write (default stdout)')
++ sub_parser.add_argument(
++ '-t',
++ '--textual-id',
++ action='store_true',
++ help='Print the textual resource ID (from .info file).')
++ sub_parser.add_argument(
++ '-p',
++ '--path',
++ action='store_true',
++ help='Print the resource path (from .info file).')
++ sub_parser.set_defaults(func=_ListMain)
++
++ args = parser.parse_args()
++ args.func(args)
++
++
++if __name__ == '__main__':
++ main()
+diff --git a/tools/grit/repack.gni b/tools/grit/repack.gni
+new file mode 100644
+index 0000000000..193f2dc43f
+--- /dev/null
++++ b/tools/grit/repack.gni
+@@ -0,0 +1,189 @@
++# Copyright 2014 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++import("//tools/grit/grit_rule.gni")
++
++# This file defines a template to invoke grit repack in a consistent manner.
++#
++# Parameters:
++# sources [required]
++# List of pak files that need to be combined.
++#
++# output [required]
++# File name (single string) of the output file.
++#
++# copy_data_to_bundle [optional]
++# Whether to define a bundle_data() for the resulting pak.
++#
++# bundle_output [optional]
++# Path of the file in the application bundle, defaults to
++# {{bundle_resources_dir}}/{{source_file_part}}.
++#
++# compress [optional]
++# Gzip the resulting bundle (and append .gz to the output name).
++#
++# deps [optional]
++# public_deps [optional]
++# visibility [optional]
++# Normal meaning.
++template("repack") {
++ _copy_data_to_bundle =
++ defined(invoker.copy_data_to_bundle) && invoker.copy_data_to_bundle
++ _repack_target_name = target_name
++ if (_copy_data_to_bundle) {
++ _repack_target_name = "${target_name}__repack"
++ }
++
++ _compress = defined(invoker.compress) && invoker.compress
++
++ action(_repack_target_name) {
++ forward_variables_from(invoker,
++ [
++ "deps",
++ "public_deps",
++ "testonly",
++ "visibility",
++ ])
++ if (defined(visibility) && _copy_data_to_bundle) {
++ visibility += [ ":${invoker.target_name}" ]
++ }
++ assert(defined(invoker.sources), "Need sources for $target_name")
++ assert(defined(invoker.output), "Need output for $target_name")
++
++ script = "//tools/grit/pak_util.py"
++
++ inputs = invoker.sources
++ outputs = [
++ invoker.output,
++ "${invoker.output}.info",
++ ]
++
++ args = [ "repack" ]
++ if (defined(invoker.repack_whitelist)) {
++ inputs += [ invoker.repack_whitelist ]
++ _rebased_whitelist = rebase_path(invoker.repack_whitelist)
++ args += [ "--whitelist=$_rebased_whitelist" ]
++ args += [ "--suppress-removed-key-output" ]
++ }
++ args += [ rebase_path(invoker.output, root_build_dir) ]
++ args += rebase_path(invoker.sources, root_build_dir)
++ if (_compress) {
++ args += [ "--compress" ]
++ }
++ }
++
++ if (_copy_data_to_bundle) {
++ bundle_data(target_name) {
++ forward_variables_from(invoker,
++ [
++ "testonly",
++ "visibility",
++ ])
++
++ public_deps = [ ":$_repack_target_name" ]
++ sources = [ invoker.output ]
++ if (defined(invoker.bundle_output)) {
++ outputs = [ invoker.bundle_output ]
++ } else {
++ outputs = [ "{{bundle_resources_dir}}/{{source_file_part}}" ]
++ }
++ }
++ }
++}
++
++# Repacks a set of .pak files for each locale.
++#
++# Parameters:
++#
++# input_locales [required]
++# List of locale names to use as inputs.
++#
++# output_locales [required]
++# A list containing the corresponding output names for each of the
++# input names. Mac and iOS use different names in some cases.
++#
++# source_patterns [required]
++# The pattern for pak files which need repacked. The filenames always end
++# with "${locale}.pak".
++# E.g.:
++# ${root_gen_dir}/foo_ expands to ${root_gen_dir}/foo_zh-CN.pak
++# when locale is zh-CN.
++#
++# output_dir [optional]
++# Directory in which to put all pak files.
++#
++# deps [optional]
++# visibility [optional]
++# testonly [optional]
++# copy_data_to_bundle [optional]
++# repack_whitelist [optional]
++# Normal meaning.
++template("repack_locales") {
++ if (defined(invoker.output_dir)) {
++ _output_dir = invoker.output_dir
++ } else if (is_ios) {
++ _output_dir = "$target_gen_dir"
++ } else {
++ _output_dir = "$target_gen_dir/$target_name"
++ }
++
++ # GN can't handle invoker.output_locales[foo] (http://crbug.com/614747).
++ _output_locales = invoker.output_locales
++
++ # Collects all targets the loop generates.
++ _locale_targets = []
++
++ # This loop iterates over the input locales and also keeps a counter so it
++ # can simultaneously iterate over the output locales (using GN's very
++ # limited looping capabilities).
++ _current_index = 0
++ foreach(_input_locale, invoker.input_locales) {
++ _output_locale = _output_locales[_current_index]
++
++ # Compute the name of the target for the current file. Save it for the deps.
++ _current_name = "${target_name}_${_input_locale}"
++ _locale_targets += [ ":$_current_name" ]
++
++ repack(_current_name) {
++ forward_variables_from(invoker,
++ [
++ "copy_data_to_bundle",
++ "bundle_output",
++ "compress",
++ "deps",
++ "repack_whitelist",
++ "testonly",
++ ])
++ visibility = [ ":${invoker.target_name}" ]
++ if (is_ios) {
++ output = "$_output_dir/${_output_locale}.lproj/locale.pak"
++ } else {
++ output = "$_output_dir/${_output_locale}.pak"
++ }
++ if (defined(copy_data_to_bundle) && copy_data_to_bundle) {
++ bundle_output =
++ "{{bundle_resources_dir}}/${_output_locale}.lproj/locale.pak"
++ }
++ sources = []
++ foreach(_pattern, invoker.source_patterns) {
++ sources += [ "${_pattern}${_input_locale}.pak" ]
++ }
++ }
++
++ _current_index = _current_index + 1
++ }
++
++ # The group that external targets depend on which collects all deps.
++ group(target_name) {
++ forward_variables_from(invoker,
++ [
++ "visibility",
++ "testonly",
++ ])
++ public_deps = _locale_targets
++ if (!defined(invoker.copy_data_to_bundle) || !invoker.copy_data_to_bundle) {
++ data_deps = public_deps
++ }
++ }
++}
+diff --git a/tools/grit/setup.py b/tools/grit/setup.py
+new file mode 100644
+index 0000000000..5d86dfc2fc
+--- /dev/null
++++ b/tools/grit/setup.py
+@@ -0,0 +1,46 @@
++#!/usr/bin/env python3
++# 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.
++
++"""Install the package!"""
++
++from __future__ import absolute_import
++
++import setuptools
++
++
++setuptools.setup(
++ name='grit',
++ version='0',
++ entry_points={
++ 'console_scripts': ['grit = grit.grit_runner:Main'],
++ },
++ packages=setuptools.find_packages(),
++ install_requires=[
++ 'six >= 1.10',
++ ],
++ author='The Chromium Authors',
++ author_email='chromium-dev@chromium.org',
++ description='Google Resource and Internationalization Tool for managing '
++ 'translations & resource files',
++ license='BSD-3',
++ url='https://chromium.googlesource.com/chromium/src/tools/grit/',
++ classifiers=[
++ 'Development Status :: 6 - Mature',
++ 'Environment :: Console',
++ 'Intended Audience :: Developers',
++ 'License :: OSI Approved :: BSD License',
++ 'Operating System :: MacOS',
++ 'Operating System :: Microsoft :: Windows',
++ 'Operating System :: POSIX :: Linux',
++ 'Programming Language :: Python',
++ 'Programming Language :: Python :: 2.7',
++ 'Programming Language :: Python :: 3',
++ 'Programming Language :: Python :: 3.6',
++ 'Programming Language :: Python :: 3.7',
++ 'Programming Language :: Python :: 3.8',
++ 'Programming Language :: Python :: 3.9',
++ 'Topic :: Utilities',
++ ],
++)
+diff --git a/tools/grit/stamp_grit_sources.py b/tools/grit/stamp_grit_sources.py
+new file mode 100644
+index 0000000000..bc7265c6cb
+--- /dev/null
++++ b/tools/grit/stamp_grit_sources.py
+@@ -0,0 +1,57 @@
++# Copyright 2014 The Chromium Authors. All rights reserved.
++# Use of this source code is governed by a BSD-style license that can be
++# found in the LICENSE file.
++
++# This script enumerates the files in the given directory, writing an empty
++# stamp file and a .d file listing the inputs required to make the stamp. This
++# allows us to dynamically depend on the grit sources without enumerating the
++# grit directory for every invocation of grit (which is what adding the source
++# files to every .grd file's .d file would entail) or shelling out to grit
++# synchronously during GN execution to get the list (which would be slow).
++#
++# Usage:
++# stamp_grit_sources.py <directory> <stamp-file> <.d-file>
++
++from __future__ import print_function
++
++import os
++import sys
++
++def GritSourceFiles(grit_root_dir):
++ files = []
++ for root, _, filenames in os.walk(grit_root_dir):
++ grit_src = [os.path.join(root, f) for f in filenames
++ if f.endswith('.py') and not f.endswith('_unittest.py')]
++ files.extend(grit_src)
++ files = [f.replace('\\', '/') for f in files]
++ return sorted(files)
++
++
++def WriteDepFile(dep_file, stamp_file, source_files):
++ with open(dep_file, "w") as f:
++ f.write(stamp_file)
++ f.write(": ")
++ f.write(' '.join(source_files))
++
++
++def WriteStampFile(stamp_file):
++ with open(stamp_file, "w"):
++ pass
++
++
++def main(argv):
++ if len(argv) != 4:
++ print("Error: expecting 3 args.")
++ return 1
++
++ grit_root_dir = sys.argv[1]
++ stamp_file = sys.argv[2]
++ dep_file = sys.argv[3]
++
++ WriteStampFile(stamp_file)
++ WriteDepFile(dep_file, stamp_file, GritSourceFiles(grit_root_dir))
++ return 0
++
++
++if __name__ == '__main__':
++ sys.exit(main(sys.argv))
+diff --git a/tools/grit/third_party/six/LICENSE b/tools/grit/third_party/six/LICENSE
+new file mode 100644
+index 0000000000..e558f9d494
+--- /dev/null
++++ b/tools/grit/third_party/six/LICENSE
+@@ -0,0 +1,18 @@
++Copyright (c) 2010-2015 Benjamin Peterson
++
++Permission is hereby granted, free of charge, to any person obtaining a copy of
++this software and associated documentation files (the "Software"), to deal in
++the Software without restriction, including without limitation the rights to
++use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
++the Software, and to permit persons to whom the Software is furnished to do so,
++subject to the following conditions:
++
++The above copyright notice and this permission notice shall be included in all
++copies or substantial portions of the Software.
++
++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
++FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
++COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
++IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
++CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+diff --git a/tools/grit/third_party/six/README b/tools/grit/third_party/six/README
+new file mode 100644
+index 0000000000..ee628a9db6
+--- /dev/null
++++ b/tools/grit/third_party/six/README
+@@ -0,0 +1,16 @@
++Six is a Python 2 and 3 compatibility library. It provides utility functions
++for smoothing over the differences between the Python versions with the goal of
++writing Python code that is compatible on both Python versions. See the
++documentation for more information on what is provided.
++
++Six supports every Python version since 2.6. It is contained in only one Python
++file, so it can be easily copied into your project. (The copyright and license
++notice must be retained.)
++
++Online documentation is at https://pythonhosted.org/six/.
++
++Bugs can be reported to https://bitbucket.org/gutworth/six. The code can also
++be found there.
++
++For questions about six or porting in general, email the python-porting mailing
++list: https://mail.python.org/mailman/listinfo/python-porting
+diff --git a/tools/grit/third_party/six/README.chromium b/tools/grit/third_party/six/README.chromium
+new file mode 100644
+index 0000000000..100b24d046
+--- /dev/null
++++ b/tools/grit/third_party/six/README.chromium
+@@ -0,0 +1,13 @@
++Name: six
++Short Name: six
++URL: https://bitbucket.org/gutworth/six/commits/tag/1.10.0
++Version: 1.10.0
++Revision: 403:e5218c3f66a2
++License: Apache License, Version 2.0
++
++Description:
++Six is a Python 2 and 3 compatibility library.
++
++Local Modifications:
++- Copied six.py as __init__.py.
++- Kept LICENSE and README.
+diff --git a/tools/grit/third_party/six/__init__.py b/tools/grit/third_party/six/__init__.py
+new file mode 100644
+index 0000000000..56e4272cb3
+--- /dev/null
++++ b/tools/grit/third_party/six/__init__.py
+@@ -0,0 +1,868 @@
++# Copyright (c) 2010-2015 Benjamin Peterson
++#
++# Permission is hereby granted, free of charge, to any person obtaining a copy
++# of this software and associated documentation files (the "Software"), to deal
++# in the Software without restriction, including without limitation the rights
++# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
++# copies of the Software, and to permit persons to whom the Software is
++# furnished to do so, subject to the following conditions:
++#
++# The above copyright notice and this permission notice shall be included in all
++# copies or substantial portions of the Software.
++#
++# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
++# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
++# SOFTWARE.
++
++"""Utilities for writing code that runs on Python 2 and 3"""
++
++from __future__ import absolute_import
++
++import functools
++import itertools
++import operator
++import sys
++import types
++
++__author__ = "Benjamin Peterson <benjamin@python.org>"
++__version__ = "1.10.0"
++
++
++# Useful for very coarse version differentiation.
++PY2 = sys.version_info[0] == 2
++PY3 = sys.version_info[0] == 3
++PY34 = sys.version_info[0:2] >= (3, 4)
++
++if PY3:
++ string_types = str,
++ integer_types = int,
++ class_types = type,
++ text_type = str
++ binary_type = bytes
++
++ MAXSIZE = sys.maxsize
++else:
++ string_types = basestring,
++ integer_types = (int, long)
++ class_types = (type, types.ClassType)
++ text_type = unicode
++ binary_type = str
++
++ if sys.platform.startswith("java"):
++ # Jython always uses 32 bits.
++ MAXSIZE = int((1 << 31) - 1)
++ else:
++ # It's possible to have sizeof(long) != sizeof(Py_ssize_t).
++ class X(object):
++
++ def __len__(self):
++ return 1 << 31
++ try:
++ len(X())
++ except OverflowError:
++ # 32-bit
++ MAXSIZE = int((1 << 31) - 1)
++ else:
++ # 64-bit
++ MAXSIZE = int((1 << 63) - 1)
++ del X
++
++
++def _add_doc(func, doc):
++ """Add documentation to a function."""
++ func.__doc__ = doc
++
++
++def _import_module(name):
++ """Import module, returning the module after the last dot."""
++ __import__(name)
++ return sys.modules[name]
++
++
++class _LazyDescr(object):
++
++ def __init__(self, name):
++ self.name = name
++
++ def __get__(self, obj, tp):
++ result = self._resolve()
++ setattr(obj, self.name, result) # Invokes __set__.
++ try:
++ # This is a bit ugly, but it avoids running this again by
++ # removing this descriptor.
++ delattr(obj.__class__, self.name)
++ except AttributeError:
++ pass
++ return result
++
++
++class MovedModule(_LazyDescr):
++
++ def __init__(self, name, old, new=None):
++ super(MovedModule, self).__init__(name)
++ if PY3:
++ if new is None:
++ new = name
++ self.mod = new
++ else:
++ self.mod = old
++
++ def _resolve(self):
++ return _import_module(self.mod)
++
++ def __getattr__(self, attr):
++ _module = self._resolve()
++ value = getattr(_module, attr)
++ setattr(self, attr, value)
++ return value
++
++
++class _LazyModule(types.ModuleType):
++
++ def __init__(self, name):
++ super(_LazyModule, self).__init__(name)
++ self.__doc__ = self.__class__.__doc__
++
++ def __dir__(self):
++ attrs = ["__doc__", "__name__"]
++ attrs += [attr.name for attr in self._moved_attributes]
++ return attrs
++
++ # Subclasses should override this
++ _moved_attributes = []
++
++
++class MovedAttribute(_LazyDescr):
++
++ def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None):
++ super(MovedAttribute, self).__init__(name)
++ if PY3:
++ if new_mod is None:
++ new_mod = name
++ self.mod = new_mod
++ if new_attr is None:
++ if old_attr is None:
++ new_attr = name
++ else:
++ new_attr = old_attr
++ self.attr = new_attr
++ else:
++ self.mod = old_mod
++ if old_attr is None:
++ old_attr = name
++ self.attr = old_attr
++
++ def _resolve(self):
++ module = _import_module(self.mod)
++ return getattr(module, self.attr)
++
++
++class _SixMetaPathImporter(object):
++
++ """
++ A meta path importer to import six.moves and its submodules.
++
++ This class implements a PEP302 finder and loader. It should be compatible
++ with Python 2.5 and all existing versions of Python3
++ """
++
++ def __init__(self, six_module_name):
++ self.name = six_module_name
++ self.known_modules = {}
++
++ def _add_module(self, mod, *fullnames):
++ for fullname in fullnames:
++ self.known_modules[self.name + "." + fullname] = mod
++
++ def _get_module(self, fullname):
++ return self.known_modules[self.name + "." + fullname]
++
++ def find_module(self, fullname, path=None):
++ if fullname in self.known_modules:
++ return self
++ return None
++
++ def __get_module(self, fullname):
++ try:
++ return self.known_modules[fullname]
++ except KeyError:
++ raise ImportError("This loader does not know module " + fullname)
++
++ def load_module(self, fullname):
++ try:
++ # in case of a reload
++ return sys.modules[fullname]
++ except KeyError:
++ pass
++ mod = self.__get_module(fullname)
++ if isinstance(mod, MovedModule):
++ mod = mod._resolve()
++ else:
++ mod.__loader__ = self
++ sys.modules[fullname] = mod
++ return mod
++
++ def is_package(self, fullname):
++ """
++ Return true, if the named module is a package.
++
++ We need this method to get correct spec objects with
++ Python 3.4 (see PEP451)
++ """
++ return hasattr(self.__get_module(fullname), "__path__")
++
++ def get_code(self, fullname):
++ """Return None
++
++ Required, if is_package is implemented"""
++ self.__get_module(fullname) # eventually raises ImportError
++ return None
++ get_source = get_code # same as get_code
++
++_importer = _SixMetaPathImporter(__name__)
++
++
++class _MovedItems(_LazyModule):
++
++ """Lazy loading of moved objects"""
++ __path__ = [] # mark as package
++
++
++_moved_attributes = [
++ MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"),
++ MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"),
++ MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"),
++ MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"),
++ MovedAttribute("intern", "__builtin__", "sys"),
++ MovedAttribute("map", "itertools", "builtins", "imap", "map"),
++ MovedAttribute("getcwd", "os", "os", "getcwdu", "getcwd"),
++ MovedAttribute("getcwdb", "os", "os", "getcwd", "getcwdb"),
++ MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"),
++ MovedAttribute("reload_module", "__builtin__", "importlib" if PY34 else "imp", "reload"),
++ MovedAttribute("reduce", "__builtin__", "functools"),
++ MovedAttribute("shlex_quote", "pipes", "shlex", "quote"),
++ MovedAttribute("StringIO", "StringIO", "io"),
++ MovedAttribute("UserDict", "UserDict", "collections"),
++ MovedAttribute("UserList", "UserList", "collections"),
++ MovedAttribute("UserString", "UserString", "collections"),
++ MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"),
++ MovedAttribute("zip", "itertools", "builtins", "izip", "zip"),
++ MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"),
++ MovedModule("builtins", "__builtin__"),
++ MovedModule("configparser", "ConfigParser"),
++ MovedModule("copyreg", "copy_reg"),
++ MovedModule("dbm_gnu", "gdbm", "dbm.gnu"),
++ MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread"),
++ MovedModule("http_cookiejar", "cookielib", "http.cookiejar"),
++ MovedModule("http_cookies", "Cookie", "http.cookies"),
++ MovedModule("html_entities", "htmlentitydefs", "html.entities"),
++ MovedModule("html_parser", "HTMLParser", "html.parser"),
++ MovedModule("http_client", "httplib", "http.client"),
++ MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"),
++ MovedModule("email_mime_nonmultipart", "email.MIMENonMultipart", "email.mime.nonmultipart"),
++ MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"),
++ MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"),
++ MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"),
++ MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"),
++ MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"),
++ MovedModule("cPickle", "cPickle", "pickle"),
++ MovedModule("queue", "Queue"),
++ MovedModule("reprlib", "repr"),
++ MovedModule("socketserver", "SocketServer"),
++ MovedModule("_thread", "thread", "_thread"),
++ MovedModule("tkinter", "Tkinter"),
++ MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"),
++ MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"),
++ MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"),
++ MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"),
++ MovedModule("tkinter_tix", "Tix", "tkinter.tix"),
++ MovedModule("tkinter_ttk", "ttk", "tkinter.ttk"),
++ MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"),
++ MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"),
++ MovedModule("tkinter_colorchooser", "tkColorChooser",
++ "tkinter.colorchooser"),
++ MovedModule("tkinter_commondialog", "tkCommonDialog",
++ "tkinter.commondialog"),
++ MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"),
++ MovedModule("tkinter_font", "tkFont", "tkinter.font"),
++ MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"),
++ MovedModule("tkinter_tksimpledialog", "tkSimpleDialog",
++ "tkinter.simpledialog"),
++ MovedModule("urllib_parse", __name__ + ".moves.urllib_parse", "urllib.parse"),
++ MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"),
++ MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"),
++ MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"),
++ MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"),
++ MovedModule("xmlrpc_server", "SimpleXMLRPCServer", "xmlrpc.server"),
++]
++# Add windows specific modules.
++if sys.platform == "win32":
++ _moved_attributes += [
++ MovedModule("winreg", "_winreg"),
++ ]
++
++for attr in _moved_attributes:
++ setattr(_MovedItems, attr.name, attr)
++ if isinstance(attr, MovedModule):
++ _importer._add_module(attr, "moves." + attr.name)
++del attr
++
++_MovedItems._moved_attributes = _moved_attributes
++
++moves = _MovedItems(__name__ + ".moves")
++_importer._add_module(moves, "moves")
++
++
++class Module_six_moves_urllib_parse(_LazyModule):
++
++ """Lazy loading of moved objects in six.moves.urllib_parse"""
++
++
++_urllib_parse_moved_attributes = [
++ MovedAttribute("ParseResult", "urlparse", "urllib.parse"),
++ MovedAttribute("SplitResult", "urlparse", "urllib.parse"),
++ MovedAttribute("parse_qs", "urlparse", "urllib.parse"),
++ MovedAttribute("parse_qsl", "urlparse", "urllib.parse"),
++ MovedAttribute("urldefrag", "urlparse", "urllib.parse"),
++ MovedAttribute("urljoin", "urlparse", "urllib.parse"),
++ MovedAttribute("urlparse", "urlparse", "urllib.parse"),
++ MovedAttribute("urlsplit", "urlparse", "urllib.parse"),
++ MovedAttribute("urlunparse", "urlparse", "urllib.parse"),
++ MovedAttribute("urlunsplit", "urlparse", "urllib.parse"),
++ MovedAttribute("quote", "urllib", "urllib.parse"),
++ MovedAttribute("quote_plus", "urllib", "urllib.parse"),
++ MovedAttribute("unquote", "urllib", "urllib.parse"),
++ MovedAttribute("unquote_plus", "urllib", "urllib.parse"),
++ MovedAttribute("urlencode", "urllib", "urllib.parse"),
++ MovedAttribute("splitquery", "urllib", "urllib.parse"),
++ MovedAttribute("splittag", "urllib", "urllib.parse"),
++ MovedAttribute("splituser", "urllib", "urllib.parse"),
++ MovedAttribute("uses_fragment", "urlparse", "urllib.parse"),
++ MovedAttribute("uses_netloc", "urlparse", "urllib.parse"),
++ MovedAttribute("uses_params", "urlparse", "urllib.parse"),
++ MovedAttribute("uses_query", "urlparse", "urllib.parse"),
++ MovedAttribute("uses_relative", "urlparse", "urllib.parse"),
++]
++for attr in _urllib_parse_moved_attributes:
++ setattr(Module_six_moves_urllib_parse, attr.name, attr)
++del attr
++
++Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes
++
++_importer._add_module(Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse"),
++ "moves.urllib_parse", "moves.urllib.parse")
++
++
++class Module_six_moves_urllib_error(_LazyModule):
++
++ """Lazy loading of moved objects in six.moves.urllib_error"""
++
++
++_urllib_error_moved_attributes = [
++ MovedAttribute("URLError", "urllib2", "urllib.error"),
++ MovedAttribute("HTTPError", "urllib2", "urllib.error"),
++ MovedAttribute("ContentTooShortError", "urllib", "urllib.error"),
++]
++for attr in _urllib_error_moved_attributes:
++ setattr(Module_six_moves_urllib_error, attr.name, attr)
++del attr
++
++Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes
++
++_importer._add_module(Module_six_moves_urllib_error(__name__ + ".moves.urllib.error"),
++ "moves.urllib_error", "moves.urllib.error")
++
++
++class Module_six_moves_urllib_request(_LazyModule):
++
++ """Lazy loading of moved objects in six.moves.urllib_request"""
++
++
++_urllib_request_moved_attributes = [
++ MovedAttribute("urlopen", "urllib2", "urllib.request"),
++ MovedAttribute("install_opener", "urllib2", "urllib.request"),
++ MovedAttribute("build_opener", "urllib2", "urllib.request"),
++ MovedAttribute("pathname2url", "urllib", "urllib.request"),
++ MovedAttribute("url2pathname", "urllib", "urllib.request"),
++ MovedAttribute("getproxies", "urllib", "urllib.request"),
++ MovedAttribute("Request", "urllib2", "urllib.request"),
++ MovedAttribute("OpenerDirector", "urllib2", "urllib.request"),
++ MovedAttribute("HTTPDefaultErrorHandler", "urllib2", "urllib.request"),
++ MovedAttribute("HTTPRedirectHandler", "urllib2", "urllib.request"),
++ MovedAttribute("HTTPCookieProcessor", "urllib2", "urllib.request"),
++ MovedAttribute("ProxyHandler", "urllib2", "urllib.request"),
++ MovedAttribute("BaseHandler", "urllib2", "urllib.request"),
++ MovedAttribute("HTTPPasswordMgr", "urllib2", "urllib.request"),
++ MovedAttribute("HTTPPasswordMgrWithDefaultRealm", "urllib2", "urllib.request"),
++ MovedAttribute("AbstractBasicAuthHandler", "urllib2", "urllib.request"),
++ MovedAttribute("HTTPBasicAuthHandler", "urllib2", "urllib.request"),
++ MovedAttribute("ProxyBasicAuthHandler", "urllib2", "urllib.request"),
++ MovedAttribute("AbstractDigestAuthHandler", "urllib2", "urllib.request"),
++ MovedAttribute("HTTPDigestAuthHandler", "urllib2", "urllib.request"),
++ MovedAttribute("ProxyDigestAuthHandler", "urllib2", "urllib.request"),
++ MovedAttribute("HTTPHandler", "urllib2", "urllib.request"),
++ MovedAttribute("HTTPSHandler", "urllib2", "urllib.request"),
++ MovedAttribute("FileHandler", "urllib2", "urllib.request"),
++ MovedAttribute("FTPHandler", "urllib2", "urllib.request"),
++ MovedAttribute("CacheFTPHandler", "urllib2", "urllib.request"),
++ MovedAttribute("UnknownHandler", "urllib2", "urllib.request"),
++ MovedAttribute("HTTPErrorProcessor", "urllib2", "urllib.request"),
++ MovedAttribute("urlretrieve", "urllib", "urllib.request"),
++ MovedAttribute("urlcleanup", "urllib", "urllib.request"),
++ MovedAttribute("URLopener", "urllib", "urllib.request"),
++ MovedAttribute("FancyURLopener", "urllib", "urllib.request"),
++ MovedAttribute("proxy_bypass", "urllib", "urllib.request"),
++]
++for attr in _urllib_request_moved_attributes:
++ setattr(Module_six_moves_urllib_request, attr.name, attr)
++del attr
++
++Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes
++
++_importer._add_module(Module_six_moves_urllib_request(__name__ + ".moves.urllib.request"),
++ "moves.urllib_request", "moves.urllib.request")
++
++
++class Module_six_moves_urllib_response(_LazyModule):
++
++ """Lazy loading of moved objects in six.moves.urllib_response"""
++
++
++_urllib_response_moved_attributes = [
++ MovedAttribute("addbase", "urllib", "urllib.response"),
++ MovedAttribute("addclosehook", "urllib", "urllib.response"),
++ MovedAttribute("addinfo", "urllib", "urllib.response"),
++ MovedAttribute("addinfourl", "urllib", "urllib.response"),
++]
++for attr in _urllib_response_moved_attributes:
++ setattr(Module_six_moves_urllib_response, attr.name, attr)
++del attr
++
++Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes
++
++_importer._add_module(Module_six_moves_urllib_response(__name__ + ".moves.urllib.response"),
++ "moves.urllib_response", "moves.urllib.response")
++
++
++class Module_six_moves_urllib_robotparser(_LazyModule):
++
++ """Lazy loading of moved objects in six.moves.urllib_robotparser"""
++
++
++_urllib_robotparser_moved_attributes = [
++ MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser"),
++]
++for attr in _urllib_robotparser_moved_attributes:
++ setattr(Module_six_moves_urllib_robotparser, attr.name, attr)
++del attr
++
++Module_six_moves_urllib_robotparser._moved_attributes = _urllib_robotparser_moved_attributes
++
++_importer._add_module(Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser"),
++ "moves.urllib_robotparser", "moves.urllib.robotparser")
++
++
++class Module_six_moves_urllib(types.ModuleType):
++
++ """Create a six.moves.urllib namespace that resembles the Python 3 namespace"""
++ __path__ = [] # mark as package
++ parse = _importer._get_module("moves.urllib_parse")
++ error = _importer._get_module("moves.urllib_error")
++ request = _importer._get_module("moves.urllib_request")
++ response = _importer._get_module("moves.urllib_response")
++ robotparser = _importer._get_module("moves.urllib_robotparser")
++
++ def __dir__(self):
++ return ['parse', 'error', 'request', 'response', 'robotparser']
++
++_importer._add_module(Module_six_moves_urllib(__name__ + ".moves.urllib"),
++ "moves.urllib")
++
++
++def add_move(move):
++ """Add an item to six.moves."""
++ setattr(_MovedItems, move.name, move)
++
++
++def remove_move(name):
++ """Remove item from six.moves."""
++ try:
++ delattr(_MovedItems, name)
++ except AttributeError:
++ try:
++ del moves.__dict__[name]
++ except KeyError:
++ raise AttributeError("no such move, %r" % (name,))
++
++
++if PY3:
++ _meth_func = "__func__"
++ _meth_self = "__self__"
++
++ _func_closure = "__closure__"
++ _func_code = "__code__"
++ _func_defaults = "__defaults__"
++ _func_globals = "__globals__"
++else:
++ _meth_func = "im_func"
++ _meth_self = "im_self"
++
++ _func_closure = "func_closure"
++ _func_code = "func_code"
++ _func_defaults = "func_defaults"
++ _func_globals = "func_globals"
++
++
++try:
++ advance_iterator = next
++except NameError:
++ def advance_iterator(it):
++ return it.next()
++next = advance_iterator
++
++
++try:
++ callable = callable
++except NameError:
++ def callable(obj):
++ return any("__call__" in klass.__dict__ for klass in type(obj).__mro__)
++
++
++if PY3:
++ def get_unbound_function(unbound):
++ return unbound
++
++ create_bound_method = types.MethodType
++
++ def create_unbound_method(func, cls):
++ return func
++
++ Iterator = object
++else:
++ def get_unbound_function(unbound):
++ return unbound.im_func
++
++ def create_bound_method(func, obj):
++ return types.MethodType(func, obj, obj.__class__)
++
++ def create_unbound_method(func, cls):
++ return types.MethodType(func, None, cls)
++
++ class Iterator(object):
++
++ def next(self):
++ return type(self).__next__(self)
++
++ callable = callable
++_add_doc(get_unbound_function,
++ """Get the function out of a possibly unbound function""")
++
++
++get_method_function = operator.attrgetter(_meth_func)
++get_method_self = operator.attrgetter(_meth_self)
++get_function_closure = operator.attrgetter(_func_closure)
++get_function_code = operator.attrgetter(_func_code)
++get_function_defaults = operator.attrgetter(_func_defaults)
++get_function_globals = operator.attrgetter(_func_globals)
++
++
++if PY3:
++ def iterkeys(d, **kw):
++ return iter(d.keys(**kw))
++
++ def itervalues(d, **kw):
++ return iter(d.values(**kw))
++
++ def iteritems(d, **kw):
++ return iter(d.items(**kw))
++
++ def iterlists(d, **kw):
++ return iter(d.lists(**kw))
++
++ viewkeys = operator.methodcaller("keys")
++
++ viewvalues = operator.methodcaller("values")
++
++ viewitems = operator.methodcaller("items")
++else:
++ def iterkeys(d, **kw):
++ return d.iterkeys(**kw)
++
++ def itervalues(d, **kw):
++ return d.itervalues(**kw)
++
++ def iteritems(d, **kw):
++ return d.iteritems(**kw)
++
++ def iterlists(d, **kw):
++ return d.iterlists(**kw)
++
++ viewkeys = operator.methodcaller("viewkeys")
++
++ viewvalues = operator.methodcaller("viewvalues")
++
++ viewitems = operator.methodcaller("viewitems")
++
++_add_doc(iterkeys, "Return an iterator over the keys of a dictionary.")
++_add_doc(itervalues, "Return an iterator over the values of a dictionary.")
++_add_doc(iteritems,
++ "Return an iterator over the (key, value) pairs of a dictionary.")
++_add_doc(iterlists,
++ "Return an iterator over the (key, [values]) pairs of a dictionary.")
++
++
++if PY3:
++ def b(s):
++ return s.encode("latin-1")
++
++ def u(s):
++ return s
++ unichr = chr
++ import struct
++ int2byte = struct.Struct(">B").pack
++ del struct
++ byte2int = operator.itemgetter(0)
++ indexbytes = operator.getitem
++ iterbytes = iter
++ import io
++ StringIO = io.StringIO
++ BytesIO = io.BytesIO
++ _assertCountEqual = "assertCountEqual"
++ if sys.version_info[1] <= 1:
++ _assertRaisesRegex = "assertRaisesRegexp"
++ _assertRegex = "assertRegexpMatches"
++ else:
++ _assertRaisesRegex = "assertRaisesRegex"
++ _assertRegex = "assertRegex"
++else:
++ def b(s):
++ return s
++ # Workaround for standalone backslash
++
++ def u(s):
++ return unicode(s.replace(r'\\', r'\\\\'), "unicode_escape")
++ unichr = unichr
++ int2byte = chr
++
++ def byte2int(bs):
++ return ord(bs[0])
++
++ def indexbytes(buf, i):
++ return ord(buf[i])
++ iterbytes = functools.partial(itertools.imap, ord)
++ import StringIO
++ StringIO = BytesIO = StringIO.StringIO
++ _assertCountEqual = "assertItemsEqual"
++ _assertRaisesRegex = "assertRaisesRegexp"
++ _assertRegex = "assertRegexpMatches"
++_add_doc(b, """Byte literal""")
++_add_doc(u, """Text literal""")
++
++
++def assertCountEqual(self, *args, **kwargs):
++ return getattr(self, _assertCountEqual)(*args, **kwargs)
++
++
++def assertRaisesRegex(self, *args, **kwargs):
++ return getattr(self, _assertRaisesRegex)(*args, **kwargs)
++
++
++def assertRegex(self, *args, **kwargs):
++ return getattr(self, _assertRegex)(*args, **kwargs)
++
++
++if PY3:
++ exec_ = getattr(moves.builtins, "exec")
++
++ def reraise(tp, value, tb=None):
++ if value is None:
++ value = tp()
++ if value.__traceback__ is not tb:
++ raise value.with_traceback(tb)
++ raise value
++
++else:
++ def exec_(_code_, _globs_=None, _locs_=None):
++ """Execute code in a namespace."""
++ if _globs_ is None:
++ frame = sys._getframe(1)
++ _globs_ = frame.f_globals
++ if _locs_ is None:
++ _locs_ = frame.f_locals
++ del frame
++ elif _locs_ is None:
++ _locs_ = _globs_
++ exec("""exec _code_ in _globs_, _locs_""")
++
++ exec_("""def reraise(tp, value, tb=None):
++ raise tp, value, tb
++""")
++
++
++if sys.version_info[:2] == (3, 2):
++ exec_("""def raise_from(value, from_value):
++ if from_value is None:
++ raise value
++ raise value from from_value
++""")
++elif sys.version_info[:2] > (3, 2):
++ exec_("""def raise_from(value, from_value):
++ raise value from from_value
++""")
++else:
++ def raise_from(value, from_value):
++ raise value
++
++
++print_ = getattr(moves.builtins, "print", None)
++if print_ is None:
++ def print_(*args, **kwargs):
++ """The new-style print function for Python 2.4 and 2.5."""
++ fp = kwargs.pop("file", sys.stdout)
++ if fp is None:
++ return
++
++ def write(data):
++ if not isinstance(data, basestring):
++ data = str(data)
++ # If the file has an encoding, encode unicode with it.
++ if (isinstance(fp, file) and
++ isinstance(data, unicode) and
++ fp.encoding is not None):
++ errors = getattr(fp, "errors", None)
++ if errors is None:
++ errors = "strict"
++ data = data.encode(fp.encoding, errors)
++ fp.write(data)
++ want_unicode = False
++ sep = kwargs.pop("sep", None)
++ if sep is not None:
++ if isinstance(sep, unicode):
++ want_unicode = True
++ elif not isinstance(sep, str):
++ raise TypeError("sep must be None or a string")
++ end = kwargs.pop("end", None)
++ if end is not None:
++ if isinstance(end, unicode):
++ want_unicode = True
++ elif not isinstance(end, str):
++ raise TypeError("end must be None or a string")
++ if kwargs:
++ raise TypeError("invalid keyword arguments to print()")
++ if not want_unicode:
++ for arg in args:
++ if isinstance(arg, unicode):
++ want_unicode = True
++ break
++ if want_unicode:
++ newline = unicode("\n")
++ space = unicode(" ")
++ else:
++ newline = "\n"
++ space = " "
++ if sep is None:
++ sep = space
++ if end is None:
++ end = newline
++ for i, arg in enumerate(args):
++ if i:
++ write(sep)
++ write(arg)
++ write(end)
++if sys.version_info[:2] < (3, 3):
++ _print = print_
++
++ def print_(*args, **kwargs):
++ fp = kwargs.get("file", sys.stdout)
++ flush = kwargs.pop("flush", False)
++ _print(*args, **kwargs)
++ if flush and fp is not None:
++ fp.flush()
++
++_add_doc(reraise, """Reraise an exception.""")
++
++if sys.version_info[0:2] < (3, 4):
++ def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS,
++ updated=functools.WRAPPER_UPDATES):
++ def wrapper(f):
++ f = functools.wraps(wrapped, assigned, updated)(f)
++ f.__wrapped__ = wrapped
++ return f
++ return wrapper
++else:
++ wraps = functools.wraps
++
++
++def with_metaclass(meta, *bases):
++ """Create a base class with a metaclass."""
++ # This requires a bit of explanation: the basic idea is to make a dummy
++ # metaclass for one level of class instantiation that replaces itself with
++ # the actual metaclass.
++ class metaclass(meta):
++
++ def __new__(cls, name, this_bases, d):
++ return meta(name, bases, d)
++ return type.__new__(metaclass, 'temporary_class', (), {})
++
++
++def add_metaclass(metaclass):
++ """Class decorator for creating a class with a metaclass."""
++ def wrapper(cls):
++ orig_vars = cls.__dict__.copy()
++ slots = orig_vars.get('__slots__')
++ if slots is not None:
++ if isinstance(slots, str):
++ slots = [slots]
++ for slots_var in slots:
++ orig_vars.pop(slots_var)
++ orig_vars.pop('__dict__', None)
++ orig_vars.pop('__weakref__', None)
++ return metaclass(cls.__name__, cls.__bases__, orig_vars)
++ return wrapper
++
++
++def python_2_unicode_compatible(klass):
++ """
++ A decorator that defines __unicode__ and __str__ methods under Python 2.
++ Under Python 3 it does nothing.
++
++ To support Python 2 and 3 with a single code base, define a __str__ method
++ returning text and apply this decorator to the class.
++ """
++ if PY2:
++ if '__str__' not in klass.__dict__:
++ raise ValueError("@python_2_unicode_compatible cannot be applied "
++ "to %s because it doesn't define __str__()." %
++ klass.__name__)
++ klass.__unicode__ = klass.__str__
++ klass.__str__ = lambda self: self.__unicode__().encode('utf-8')
++ return klass
++
++
++# Complete the moves implementation.
++# This code is at the end of this module to speed up module loading.
++# Turn this module into a package.
++__path__ = [] # required for PEP 302 and PEP 451
++__package__ = __name__ # see PEP 366 @ReservedAssignment
++if globals().get("__spec__") is not None:
++ __spec__.submodule_search_locations = [] # PEP 451 @UndefinedVariable
++# Remove other six meta path importers, since they cause problems. This can
++# happen if six is removed from sys.modules and then reloaded. (Setuptools does
++# this for some reason.)
++if sys.meta_path:
++ for i, importer in enumerate(sys.meta_path):
++ # Here's some real nastiness: Another "instance" of the six module might
++ # be floating around. Therefore, we can't use isinstance() to check for
++ # the six meta path importer, since the other six instance will have
++ # inserted an importer with different class.
++ if (type(importer).__name__ == "_SixMetaPathImporter" and
++ importer.name == __name__):
++ del sys.meta_path[i]
++ break
++ del i, importer
++# Finally, add the importer to the meta path import hook.
++sys.meta_path.append(_importer)
diff --git a/third_party/libwebrtc/moz-patch-stack/0099.patch b/third_party/libwebrtc/moz-patch-stack/0099.patch
index 1faafdf8cf..dc3cc7ca1a 100644
--- a/third_party/libwebrtc/moz-patch-stack/0099.patch
+++ b/third_party/libwebrtc/moz-patch-stack/0099.patch
@@ -1,35362 +1,20 @@
-From: Nico Grunbaum <na-g@nostrum.com>
-Date: Fri, 30 Apr 2021 21:51:00 +0000
-Subject: Bug 1654112 - Add grit dep for building webrtc on android; r=mjf
+From: Michael Froman <mfroman@mozilla.com>
+Date: Wed, 7 Dec 2022 17:09:00 +0000
+Subject: Bug 1744645 - pt1 - add a couple empty gni files to help with
+ BUILD.gn corrections. r=ng
-Differential Revision: https://phabricator.services.mozilla.com/D114027
-Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/3cce5e6938f0df87bd9ab12a5f556aceb93dfa1d
+Differential Revision: https://phabricator.services.mozilla.com/D163991
+Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/81d86382ee468f3b36deed00d0c9d59eb85524be
---
- tools/grit/.gitignore | 1 +
- tools/grit/BUILD.gn | 48 +
- tools/grit/MANIFEST.in | 3 +
- tools/grit/OWNERS | 8 +
- tools/grit/PRESUBMIT.py | 22 +
- tools/grit/README.md | 19 +
- tools/grit/grit.py | 31 +
- tools/grit/grit/__init__.py | 19 +
- tools/grit/grit/clique.py | 491 +++
- tools/grit/grit/clique_unittest.py | 265 ++
- tools/grit/grit/constants.py | 23 +
- tools/grit/grit/exception.py | 139 +
- tools/grit/grit/extern/BogoFP.py | 22 +
- tools/grit/grit/extern/FP.py | 72 +
- tools/grit/grit/extern/__init__.py | 0
- tools/grit/grit/extern/tclib.py | 503 +++
- tools/grit/grit/format/__init__.py | 8 +
- tools/grit/grit/format/android_xml.py | 212 ++
- .../grit/grit/format/android_xml_unittest.py | 149 +
- tools/grit/grit/format/c_format.py | 95 +
- tools/grit/grit/format/c_format_unittest.py | 81 +
- .../grit/grit/format/chrome_messages_json.py | 59 +
- .../format/chrome_messages_json_unittest.py | 190 +
- tools/grit/grit/format/data_pack.py | 321 ++
- tools/grit/grit/format/data_pack_unittest.py | 102 +
- .../grit/grit/format/gen_predetermined_ids.py | 144 +
- .../format/gen_predetermined_ids_unittest.py | 46 +
- tools/grit/grit/format/gzip_string.py | 46 +
- .../grit/grit/format/gzip_string_unittest.py | 65 +
- tools/grit/grit/format/html_inline.py | 602 ++++
- .../grit/grit/format/html_inline_unittest.py | 927 +++++
- tools/grit/grit/format/minifier.py | 45 +
- .../grit/grit/format/policy_templates_json.py | 26 +
- .../format/policy_templates_json_unittest.py | 207 ++
- tools/grit/grit/format/rc.py | 474 +++
- tools/grit/grit/format/rc_header.py | 48 +
- tools/grit/grit/format/rc_header_unittest.py | 138 +
- tools/grit/grit/format/rc_unittest.py | 415 +++
- tools/grit/grit/format/resource_map.py | 159 +
- .../grit/grit/format/resource_map_unittest.py | 345 ++
- tools/grit/grit/gather/__init__.py | 8 +
- tools/grit/grit/gather/admin_template.py | 62 +
- .../grit/gather/admin_template_unittest.py | 115 +
- tools/grit/grit/gather/chrome_html.py | 377 ++
- .../grit/grit/gather/chrome_html_unittest.py | 610 ++++
- tools/grit/grit/gather/chrome_scaled_image.py | 157 +
- .../gather/chrome_scaled_image_unittest.py | 209 ++
- tools/grit/grit/gather/interface.py | 172 +
- tools/grit/grit/gather/json_loader.py | 27 +
- tools/grit/grit/gather/policy_json.py | 325 ++
- .../grit/grit/gather/policy_json_unittest.py | 347 ++
- tools/grit/grit/gather/rc.py | 343 ++
- tools/grit/grit/gather/rc_unittest.py | 372 ++
- tools/grit/grit/gather/regexp.py | 82 +
- tools/grit/grit/gather/skeleton_gatherer.py | 149 +
- tools/grit/grit/gather/tr_html.py | 743 ++++
- tools/grit/grit/gather/tr_html_unittest.py | 524 +++
- tools/grit/grit/gather/txt.py | 38 +
- tools/grit/grit/gather/txt_unittest.py | 35 +
- tools/grit/grit/grd_reader.py | 238 ++
- tools/grit/grit/grd_reader_unittest.py | 346 ++
- tools/grit/grit/grit-todo.xml | 62 +
- tools/grit/grit/grit_runner.py | 334 ++
- tools/grit/grit/grit_runner_unittest.py | 42 +
- tools/grit/grit/lazy_re.py | 46 +
- tools/grit/grit/lazy_re_unittest.py | 40 +
- tools/grit/grit/node/__init__.py | 8 +
- tools/grit/grit/node/base.py | 670 ++++
- tools/grit/grit/node/base_unittest.py | 259 ++
- tools/grit/grit/node/brotli_util.py | 29 +
- tools/grit/grit/node/custom/__init__.py | 8 +
- tools/grit/grit/node/custom/filename.py | 29 +
- .../grit/node/custom/filename_unittest.py | 34 +
- tools/grit/grit/node/empty.py | 64 +
- tools/grit/grit/node/include.py | 170 +
- tools/grit/grit/node/include_unittest.py | 134 +
- tools/grit/grit/node/mapping.py | 60 +
- tools/grit/grit/node/message.py | 362 ++
- tools/grit/grit/node/message_unittest.py | 380 ++
- tools/grit/grit/node/misc.py | 707 ++++
- tools/grit/grit/node/misc_unittest.py | 590 ++++
- tools/grit/grit/node/mock_brotli.py | 10 +
- tools/grit/grit/node/node_io.py | 117 +
- tools/grit/grit/node/node_io_unittest.py | 182 +
- tools/grit/grit/node/structure.py | 375 ++
- tools/grit/grit/node/structure_unittest.py | 178 +
- tools/grit/grit/node/variant.py | 41 +
- tools/grit/grit/pseudo.py | 129 +
- tools/grit/grit/pseudo_rtl.py | 104 +
- tools/grit/grit/pseudo_unittest.py | 55 +
- tools/grit/grit/shortcuts.py | 93 +
- tools/grit/grit/shortcuts_unittest.py | 79 +
- tools/grit/grit/tclib.py | 246 ++
- tools/grit/grit/tclib_unittest.py | 180 +
- tools/grit/grit/test_suite_all.py | 34 +
- tools/grit/grit/testdata/GoogleDesktop.adm | 945 +++++
- tools/grit/grit/testdata/README.txt | 87 +
- tools/grit/grit/testdata/about.html | 45 +
- tools/grit/grit/testdata/android.xml | 24 +
- tools/grit/grit/testdata/bad_browser.html | 16 +
- tools/grit/grit/testdata/browser.html | 42 +
- tools/grit/grit/testdata/buildinfo.grd | 46 +
- tools/grit/grit/testdata/cache_prefix.html | 24 +
- .../grit/grit/testdata/cache_prefix_file.html | 25 +
- tools/grit/grit/testdata/chat_result.html | 24 +
- .../chrome/app/generated_resources.grd | 199 ++
- tools/grit/grit/testdata/chrome_html.html | 6 +
- .../grit/testdata/default_100_percent/a.png | Bin 0 -> 159 bytes
- .../grit/testdata/default_100_percent/b.png | 1 +
- tools/grit/grit/testdata/del_footer.html | 8 +
- tools/grit/grit/testdata/del_header.html | 60 +
- tools/grit/grit/testdata/deleted.html | 21 +
- tools/grit/grit/testdata/depfile.grd | 18 +
- tools/grit/grit/testdata/details.html | 10 +
- .../grit/testdata/duplicate-name-input.xml | 26 +
- tools/grit/grit/testdata/email_result.html | 34 +
- tools/grit/grit/testdata/email_thread.html | 10 +
- tools/grit/grit/testdata/error.html | 8 +
- tools/grit/grit/testdata/explicit_web.html | 11 +
- tools/grit/grit/testdata/footer.html | 14 +
- .../grit/testdata/generated_resources_fr.xtb | 3079 +++++++++++++++++
- .../grit/testdata/generated_resources_iw.xtb | 4 +
- .../grit/testdata/generated_resources_no.xtb | 4 +
- tools/grit/grit/testdata/grit_part.grdp | 5 +
- tools/grit/grit/testdata/header.html | 39 +
- tools/grit/grit/testdata/homepage.html | 37 +
- tools/grit/grit/testdata/hover.html | 177 +
- tools/grit/grit/testdata/include_test.html | 31 +
- tools/grit/grit/testdata/included_sample.html | 1 +
- tools/grit/grit/testdata/indexing_speed.html | 58 +
- tools/grit/grit/testdata/install_prefs.html | 92 +
- tools/grit/grit/testdata/install_prefs2.html | 52 +
- .../grit/testdata/klonk-alternate-skeleton.rc | Bin 0 -> 1088 bytes
- tools/grit/grit/testdata/klonk.ico | Bin 0 -> 766 bytes
- tools/grit/grit/testdata/klonk.rc | Bin 0 -> 9824 bytes
- .../grit/grit/testdata/ko_oem_enable_bug.html | 1 +
- .../grit/testdata/ko_oem_non_admin_bug.html | 1 +
- tools/grit/grit/testdata/mini.html | 36 +
- tools/grit/grit/testdata/oem_enable.html | 106 +
- tools/grit/grit/testdata/oem_non_admin.html | 39 +
- tools/grit/grit/testdata/onebox.html | 21 +
- tools/grit/grit/testdata/oneclick.html | 34 +
- tools/grit/grit/testdata/password.html | 37 +
- tools/grit/grit/testdata/preferences.html | 234 ++
- tools/grit/grit/testdata/preprocess_test.html | 7 +
- tools/grit/grit/testdata/privacy.html | 35 +
- tools/grit/grit/testdata/quit_apps.html | 49 +
- tools/grit/grit/testdata/recrawl.html | 30 +
- tools/grit/grit/testdata/resource_ids | 13 +
- tools/grit/grit/testdata/script.html | 38 +
- tools/grit/grit/testdata/searchbox.html | 22 +
- tools/grit/grit/testdata/sidebar_h.html | 82 +
- tools/grit/grit/testdata/sidebar_v.html | 267 ++
- tools/grit/grit/testdata/simple-input.xml | 52 +
- tools/grit/grit/testdata/simple.html | 3 +
- tools/grit/grit/testdata/source.rc | 57 +
- .../grit/testdata/special_100_percent/a.png | Bin 0 -> 159 bytes
- tools/grit/grit/testdata/status.html | 44 +
- .../grit/testdata/structure_variables.html | 4 +
- tools/grit/grit/testdata/substitute.grd | 31 +
- tools/grit/grit/testdata/substitute.xmb | 10 +
- .../grit/grit/testdata/substitute_no_ids.grd | 31 +
- tools/grit/grit/testdata/substitute_tmpl.grd | 31 +
- tools/grit/grit/testdata/test_css.css | 1 +
- tools/grit/grit/testdata/test_html.html | 1 +
- tools/grit/grit/testdata/test_js.js | 1 +
- tools/grit/grit/testdata/test_svg.svg | 1 +
- tools/grit/grit/testdata/test_text.txt | 1 +
- tools/grit/grit/testdata/time_related.html | 11 +
- tools/grit/grit/testdata/toolbar_about.html | 138 +
- .../grit/testdata/tools/grit/resource_ids | 176 +
- tools/grit/grit/testdata/transl.rc | 56 +
- tools/grit/grit/testdata/versions.html | 7 +
- tools/grit/grit/testdata/whitelist.txt | 4 +
- .../grit/testdata/whitelist_resources.grd | 54 +
- .../grit/grit/testdata/whitelist_strings.grd | 23 +
- tools/grit/grit/tool/__init__.py | 8 +
- tools/grit/grit/tool/android2grd.py | 484 +++
- tools/grit/grit/tool/android2grd_unittest.py | 181 +
- tools/grit/grit/tool/build.py | 556 +++
- tools/grit/grit/tool/build_unittest.py | 341 ++
- tools/grit/grit/tool/buildinfo.py | 78 +
- tools/grit/grit/tool/buildinfo_unittest.py | 90 +
- tools/grit/grit/tool/count.py | 52 +
- tools/grit/grit/tool/diff_structures.py | 119 +
- .../grit/tool/diff_structures_unittest.py | 46 +
- tools/grit/grit/tool/interface.py | 62 +
- tools/grit/grit/tool/menu_from_parts.py | 79 +
- tools/grit/grit/tool/newgrd.py | 85 +
- tools/grit/grit/tool/newgrd_unittest.py | 51 +
- tools/grit/grit/tool/postprocess_interface.py | 29 +
- tools/grit/grit/tool/postprocess_unittest.py | 64 +
- tools/grit/grit/tool/preprocess_interface.py | 25 +
- tools/grit/grit/tool/preprocess_unittest.py | 50 +
- tools/grit/grit/tool/rc2grd.py | 418 +++
- tools/grit/grit/tool/rc2grd_unittest.py | 163 +
- tools/grit/grit/tool/resize.py | 295 ++
- tools/grit/grit/tool/test.py | 24 +
- tools/grit/grit/tool/transl2tc.py | 251 ++
- tools/grit/grit/tool/transl2tc_unittest.py | 133 +
- tools/grit/grit/tool/unit.py | 43 +
- .../grit/tool/update_resource_ids/__init__.py | 305 ++
- .../grit/tool/update_resource_ids/assigner.py | 286 ++
- .../update_resource_ids/assigner_unittest.py | 154 +
- .../grit/tool/update_resource_ids/common.py | 101 +
- .../grit/tool/update_resource_ids/parser.py | 231 ++
- .../grit/tool/update_resource_ids/reader.py | 83 +
- tools/grit/grit/tool/xmb.py | 295 ++
- tools/grit/grit/tool/xmb_unittest.py | 132 +
- tools/grit/grit/util.py | 691 ++++
- tools/grit/grit/util_unittest.py | 118 +
- tools/grit/grit/xtb_reader.py | 140 +
- tools/grit/grit/xtb_reader_unittest.py | 110 +
- tools/grit/grit_info.py | 173 +
- tools/grit/grit_rule.gni | 485 +++
- tools/grit/minify_with_uglify.py | 44 +
- tools/grit/minimize_css.py | 105 +
- tools/grit/minimize_css_unittest.py | 58 +
- tools/grit/pak_util.py | 223 ++
- tools/grit/repack.gni | 189 +
- tools/grit/setup.py | 46 +
- tools/grit/stamp_grit_sources.py | 57 +
- tools/grit/third_party/six/LICENSE | 18 +
- tools/grit/third_party/six/README | 16 +
- tools/grit/third_party/six/README.chromium | 13 +
- tools/grit/third_party/six/__init__.py | 868 +++++
- 226 files changed, 33440 insertions(+)
- create mode 100644 tools/grit/.gitignore
- create mode 100644 tools/grit/BUILD.gn
- create mode 100644 tools/grit/MANIFEST.in
- create mode 100644 tools/grit/OWNERS
- create mode 100644 tools/grit/PRESUBMIT.py
- create mode 100644 tools/grit/README.md
- create mode 100644 tools/grit/grit.py
- create mode 100644 tools/grit/grit/__init__.py
- create mode 100644 tools/grit/grit/clique.py
- create mode 100644 tools/grit/grit/clique_unittest.py
- create mode 100644 tools/grit/grit/constants.py
- create mode 100644 tools/grit/grit/exception.py
- create mode 100644 tools/grit/grit/extern/BogoFP.py
- create mode 100644 tools/grit/grit/extern/FP.py
- create mode 100644 tools/grit/grit/extern/__init__.py
- create mode 100644 tools/grit/grit/extern/tclib.py
- create mode 100644 tools/grit/grit/format/__init__.py
- create mode 100644 tools/grit/grit/format/android_xml.py
- create mode 100644 tools/grit/grit/format/android_xml_unittest.py
- create mode 100644 tools/grit/grit/format/c_format.py
- create mode 100644 tools/grit/grit/format/c_format_unittest.py
- create mode 100644 tools/grit/grit/format/chrome_messages_json.py
- create mode 100644 tools/grit/grit/format/chrome_messages_json_unittest.py
- create mode 100644 tools/grit/grit/format/data_pack.py
- create mode 100644 tools/grit/grit/format/data_pack_unittest.py
- create mode 100644 tools/grit/grit/format/gen_predetermined_ids.py
- create mode 100644 tools/grit/grit/format/gen_predetermined_ids_unittest.py
- create mode 100644 tools/grit/grit/format/gzip_string.py
- create mode 100644 tools/grit/grit/format/gzip_string_unittest.py
- create mode 100644 tools/grit/grit/format/html_inline.py
- create mode 100644 tools/grit/grit/format/html_inline_unittest.py
- create mode 100644 tools/grit/grit/format/minifier.py
- create mode 100644 tools/grit/grit/format/policy_templates_json.py
- create mode 100644 tools/grit/grit/format/policy_templates_json_unittest.py
- create mode 100644 tools/grit/grit/format/rc.py
- create mode 100644 tools/grit/grit/format/rc_header.py
- create mode 100644 tools/grit/grit/format/rc_header_unittest.py
- create mode 100644 tools/grit/grit/format/rc_unittest.py
- create mode 100644 tools/grit/grit/format/resource_map.py
- create mode 100644 tools/grit/grit/format/resource_map_unittest.py
- create mode 100644 tools/grit/grit/gather/__init__.py
- create mode 100644 tools/grit/grit/gather/admin_template.py
- create mode 100644 tools/grit/grit/gather/admin_template_unittest.py
- create mode 100644 tools/grit/grit/gather/chrome_html.py
- create mode 100644 tools/grit/grit/gather/chrome_html_unittest.py
- create mode 100644 tools/grit/grit/gather/chrome_scaled_image.py
- create mode 100644 tools/grit/grit/gather/chrome_scaled_image_unittest.py
- create mode 100644 tools/grit/grit/gather/interface.py
- create mode 100644 tools/grit/grit/gather/json_loader.py
- create mode 100644 tools/grit/grit/gather/policy_json.py
- create mode 100644 tools/grit/grit/gather/policy_json_unittest.py
- create mode 100644 tools/grit/grit/gather/rc.py
- create mode 100644 tools/grit/grit/gather/rc_unittest.py
- create mode 100644 tools/grit/grit/gather/regexp.py
- create mode 100644 tools/grit/grit/gather/skeleton_gatherer.py
- create mode 100644 tools/grit/grit/gather/tr_html.py
- create mode 100644 tools/grit/grit/gather/tr_html_unittest.py
- create mode 100644 tools/grit/grit/gather/txt.py
- create mode 100644 tools/grit/grit/gather/txt_unittest.py
- create mode 100644 tools/grit/grit/grd_reader.py
- create mode 100644 tools/grit/grit/grd_reader_unittest.py
- create mode 100644 tools/grit/grit/grit-todo.xml
- create mode 100644 tools/grit/grit/grit_runner.py
- create mode 100644 tools/grit/grit/grit_runner_unittest.py
- create mode 100644 tools/grit/grit/lazy_re.py
- create mode 100644 tools/grit/grit/lazy_re_unittest.py
- create mode 100644 tools/grit/grit/node/__init__.py
- create mode 100644 tools/grit/grit/node/base.py
- create mode 100644 tools/grit/grit/node/base_unittest.py
- create mode 100644 tools/grit/grit/node/brotli_util.py
- create mode 100644 tools/grit/grit/node/custom/__init__.py
- create mode 100644 tools/grit/grit/node/custom/filename.py
- create mode 100644 tools/grit/grit/node/custom/filename_unittest.py
- create mode 100644 tools/grit/grit/node/empty.py
- create mode 100644 tools/grit/grit/node/include.py
- create mode 100644 tools/grit/grit/node/include_unittest.py
- create mode 100644 tools/grit/grit/node/mapping.py
- create mode 100644 tools/grit/grit/node/message.py
- create mode 100644 tools/grit/grit/node/message_unittest.py
- create mode 100644 tools/grit/grit/node/misc.py
- create mode 100644 tools/grit/grit/node/misc_unittest.py
- create mode 100644 tools/grit/grit/node/mock_brotli.py
- create mode 100644 tools/grit/grit/node/node_io.py
- create mode 100644 tools/grit/grit/node/node_io_unittest.py
- create mode 100644 tools/grit/grit/node/structure.py
- create mode 100644 tools/grit/grit/node/structure_unittest.py
- create mode 100644 tools/grit/grit/node/variant.py
- create mode 100644 tools/grit/grit/pseudo.py
- create mode 100644 tools/grit/grit/pseudo_rtl.py
- create mode 100644 tools/grit/grit/pseudo_unittest.py
- create mode 100644 tools/grit/grit/shortcuts.py
- create mode 100644 tools/grit/grit/shortcuts_unittest.py
- create mode 100644 tools/grit/grit/tclib.py
- create mode 100644 tools/grit/grit/tclib_unittest.py
- create mode 100644 tools/grit/grit/test_suite_all.py
- create mode 100644 tools/grit/grit/testdata/GoogleDesktop.adm
- create mode 100644 tools/grit/grit/testdata/README.txt
- create mode 100644 tools/grit/grit/testdata/about.html
- create mode 100644 tools/grit/grit/testdata/android.xml
- create mode 100644 tools/grit/grit/testdata/bad_browser.html
- create mode 100644 tools/grit/grit/testdata/browser.html
- create mode 100644 tools/grit/grit/testdata/buildinfo.grd
- create mode 100644 tools/grit/grit/testdata/cache_prefix.html
- create mode 100644 tools/grit/grit/testdata/cache_prefix_file.html
- create mode 100644 tools/grit/grit/testdata/chat_result.html
- create mode 100644 tools/grit/grit/testdata/chrome/app/generated_resources.grd
- create mode 100644 tools/grit/grit/testdata/chrome_html.html
- create mode 100644 tools/grit/grit/testdata/default_100_percent/a.png
- create mode 100644 tools/grit/grit/testdata/default_100_percent/b.png
- create mode 100644 tools/grit/grit/testdata/del_footer.html
- create mode 100644 tools/grit/grit/testdata/del_header.html
- create mode 100644 tools/grit/grit/testdata/deleted.html
- create mode 100644 tools/grit/grit/testdata/depfile.grd
- create mode 100644 tools/grit/grit/testdata/details.html
- create mode 100644 tools/grit/grit/testdata/duplicate-name-input.xml
- create mode 100644 tools/grit/grit/testdata/email_result.html
- create mode 100644 tools/grit/grit/testdata/email_thread.html
- create mode 100644 tools/grit/grit/testdata/error.html
- create mode 100644 tools/grit/grit/testdata/explicit_web.html
- create mode 100644 tools/grit/grit/testdata/footer.html
- create mode 100644 tools/grit/grit/testdata/generated_resources_fr.xtb
- create mode 100644 tools/grit/grit/testdata/generated_resources_iw.xtb
- create mode 100644 tools/grit/grit/testdata/generated_resources_no.xtb
- create mode 100644 tools/grit/grit/testdata/grit_part.grdp
- create mode 100644 tools/grit/grit/testdata/header.html
- create mode 100644 tools/grit/grit/testdata/homepage.html
- create mode 100644 tools/grit/grit/testdata/hover.html
- create mode 100644 tools/grit/grit/testdata/include_test.html
- create mode 100644 tools/grit/grit/testdata/included_sample.html
- create mode 100644 tools/grit/grit/testdata/indexing_speed.html
- create mode 100644 tools/grit/grit/testdata/install_prefs.html
- create mode 100644 tools/grit/grit/testdata/install_prefs2.html
- create mode 100644 tools/grit/grit/testdata/klonk-alternate-skeleton.rc
- create mode 100644 tools/grit/grit/testdata/klonk.ico
- create mode 100644 tools/grit/grit/testdata/klonk.rc
- create mode 100644 tools/grit/grit/testdata/ko_oem_enable_bug.html
- create mode 100644 tools/grit/grit/testdata/ko_oem_non_admin_bug.html
- create mode 100644 tools/grit/grit/testdata/mini.html
- create mode 100644 tools/grit/grit/testdata/oem_enable.html
- create mode 100644 tools/grit/grit/testdata/oem_non_admin.html
- create mode 100644 tools/grit/grit/testdata/onebox.html
- create mode 100644 tools/grit/grit/testdata/oneclick.html
- create mode 100644 tools/grit/grit/testdata/password.html
- create mode 100644 tools/grit/grit/testdata/preferences.html
- create mode 100644 tools/grit/grit/testdata/preprocess_test.html
- create mode 100644 tools/grit/grit/testdata/privacy.html
- create mode 100644 tools/grit/grit/testdata/quit_apps.html
- create mode 100644 tools/grit/grit/testdata/recrawl.html
- create mode 100644 tools/grit/grit/testdata/resource_ids
- create mode 100644 tools/grit/grit/testdata/script.html
- create mode 100644 tools/grit/grit/testdata/searchbox.html
- create mode 100644 tools/grit/grit/testdata/sidebar_h.html
- create mode 100644 tools/grit/grit/testdata/sidebar_v.html
- create mode 100644 tools/grit/grit/testdata/simple-input.xml
- create mode 100644 tools/grit/grit/testdata/simple.html
- create mode 100644 tools/grit/grit/testdata/source.rc
- create mode 100644 tools/grit/grit/testdata/special_100_percent/a.png
- create mode 100644 tools/grit/grit/testdata/status.html
- create mode 100644 tools/grit/grit/testdata/structure_variables.html
- create mode 100644 tools/grit/grit/testdata/substitute.grd
- create mode 100644 tools/grit/grit/testdata/substitute.xmb
- create mode 100644 tools/grit/grit/testdata/substitute_no_ids.grd
- create mode 100644 tools/grit/grit/testdata/substitute_tmpl.grd
- create mode 100644 tools/grit/grit/testdata/test_css.css
- create mode 100644 tools/grit/grit/testdata/test_html.html
- create mode 100644 tools/grit/grit/testdata/test_js.js
- create mode 100644 tools/grit/grit/testdata/test_svg.svg
- create mode 100644 tools/grit/grit/testdata/test_text.txt
- create mode 100644 tools/grit/grit/testdata/time_related.html
- create mode 100644 tools/grit/grit/testdata/toolbar_about.html
- create mode 100644 tools/grit/grit/testdata/tools/grit/resource_ids
- create mode 100644 tools/grit/grit/testdata/transl.rc
- create mode 100644 tools/grit/grit/testdata/versions.html
- create mode 100644 tools/grit/grit/testdata/whitelist.txt
- create mode 100644 tools/grit/grit/testdata/whitelist_resources.grd
- create mode 100644 tools/grit/grit/testdata/whitelist_strings.grd
- create mode 100644 tools/grit/grit/tool/__init__.py
- create mode 100644 tools/grit/grit/tool/android2grd.py
- create mode 100644 tools/grit/grit/tool/android2grd_unittest.py
- create mode 100644 tools/grit/grit/tool/build.py
- create mode 100644 tools/grit/grit/tool/build_unittest.py
- create mode 100644 tools/grit/grit/tool/buildinfo.py
- create mode 100644 tools/grit/grit/tool/buildinfo_unittest.py
- create mode 100644 tools/grit/grit/tool/count.py
- create mode 100644 tools/grit/grit/tool/diff_structures.py
- create mode 100644 tools/grit/grit/tool/diff_structures_unittest.py
- create mode 100644 tools/grit/grit/tool/interface.py
- create mode 100644 tools/grit/grit/tool/menu_from_parts.py
- create mode 100644 tools/grit/grit/tool/newgrd.py
- create mode 100644 tools/grit/grit/tool/newgrd_unittest.py
- create mode 100644 tools/grit/grit/tool/postprocess_interface.py
- create mode 100644 tools/grit/grit/tool/postprocess_unittest.py
- create mode 100644 tools/grit/grit/tool/preprocess_interface.py
- create mode 100644 tools/grit/grit/tool/preprocess_unittest.py
- create mode 100644 tools/grit/grit/tool/rc2grd.py
- create mode 100644 tools/grit/grit/tool/rc2grd_unittest.py
- create mode 100644 tools/grit/grit/tool/resize.py
- create mode 100644 tools/grit/grit/tool/test.py
- create mode 100644 tools/grit/grit/tool/transl2tc.py
- create mode 100644 tools/grit/grit/tool/transl2tc_unittest.py
- create mode 100644 tools/grit/grit/tool/unit.py
- create mode 100644 tools/grit/grit/tool/update_resource_ids/__init__.py
- create mode 100644 tools/grit/grit/tool/update_resource_ids/assigner.py
- create mode 100644 tools/grit/grit/tool/update_resource_ids/assigner_unittest.py
- create mode 100644 tools/grit/grit/tool/update_resource_ids/common.py
- create mode 100644 tools/grit/grit/tool/update_resource_ids/parser.py
- create mode 100644 tools/grit/grit/tool/update_resource_ids/reader.py
- create mode 100644 tools/grit/grit/tool/xmb.py
- create mode 100644 tools/grit/grit/tool/xmb_unittest.py
- create mode 100644 tools/grit/grit/util.py
- create mode 100644 tools/grit/grit/util_unittest.py
- create mode 100644 tools/grit/grit/xtb_reader.py
- create mode 100644 tools/grit/grit/xtb_reader_unittest.py
- create mode 100644 tools/grit/grit_info.py
- create mode 100644 tools/grit/grit_rule.gni
- create mode 100644 tools/grit/minify_with_uglify.py
- create mode 100644 tools/grit/minimize_css.py
- create mode 100644 tools/grit/minimize_css_unittest.py
- create mode 100644 tools/grit/pak_util.py
- create mode 100644 tools/grit/repack.gni
- create mode 100644 tools/grit/setup.py
- create mode 100644 tools/grit/stamp_grit_sources.py
- create mode 100644 tools/grit/third_party/six/LICENSE
- create mode 100644 tools/grit/third_party/six/README
- create mode 100644 tools/grit/third_party/six/README.chromium
- create mode 100644 tools/grit/third_party/six/__init__.py
+ tools/generate_stubs/rules.gni | 2 ++
+ 1 file changed, 2 insertions(+)
+ create mode 100644 tools/generate_stubs/rules.gni
-diff --git a/tools/grit/.gitignore b/tools/grit/.gitignore
+diff --git a/tools/generate_stubs/rules.gni b/tools/generate_stubs/rules.gni
new file mode 100644
-index 0000000000..0d20b6487c
+index 0000000000..1d9f36eb72
--- /dev/null
-+++ b/tools/grit/.gitignore
-@@ -0,0 +1 @@
-+*.pyc
-diff --git a/tools/grit/BUILD.gn b/tools/grit/BUILD.gn
-new file mode 100644
-index 0000000000..1cd3c75b55
---- /dev/null
-+++ b/tools/grit/BUILD.gn
-@@ -0,0 +1,48 @@
-+# Copyright 2014 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+# This target creates a stamp file that depends on all the sources in the grit
-+# directory. By depending on this, a target can force itself to be rebuilt if
-+# grit itself changes.
-+
-+import("//build/config/sanitizers/sanitizers.gni")
-+
-+action("grit_sources") {
-+ depfile = "$target_out_dir/grit_sources.d"
-+ script = "stamp_grit_sources.py"
-+
-+ inputs = [ "grit.py" ]
-+
-+ # Note that we can't call this "grit_sources.stamp" because that file is
-+ # implicitly created by GN for script actions.
-+ outputs = [ "$target_out_dir/grit_sources.script.stamp" ]
-+
-+ args = [
-+ rebase_path("//tools/grit", root_build_dir),
-+ rebase_path(outputs[0], root_build_dir),
-+ rebase_path(depfile, root_build_dir),
-+ ]
-+}
-+
-+group("grit_python_unittests") {
-+ testonly = true
-+
-+ data = [
-+ "//testing/scripts/common.py",
-+ "//testing/scripts/run_isolated_script_test.py",
-+ "//testing/xvfb.py",
-+ "//tools/grit/",
-+ "//third_party/catapult/third_party/typ/",
-+ ]
-+}
-+
-+# See https://crbug.com/983200
-+if (is_mac && is_asan) {
-+ create_bundle("brotli_mac_asan_workaround") {
-+ bundle_root_dir = "$target_out_dir/$target_name"
-+ bundle_executable_dir = bundle_root_dir
-+
-+ public_deps = [ "//third_party/brotli:brotli($host_toolchain)" ]
-+ }
-+}
-diff --git a/tools/grit/MANIFEST.in b/tools/grit/MANIFEST.in
-new file mode 100644
-index 0000000000..1cbff42400
---- /dev/null
-+++ b/tools/grit/MANIFEST.in
-@@ -0,0 +1,3 @@
-+exclude grit/test_suite_all.py
-+exclude grit/tool/test.py
-+global-exclude *_unittest.py
-diff --git a/tools/grit/OWNERS b/tools/grit/OWNERS
-new file mode 100644
-index 0000000000..6a8f447b82
---- /dev/null
-+++ b/tools/grit/OWNERS
-@@ -0,0 +1,8 @@
-+agrieve@chromium.org
-+flackr@chromium.org
-+thakis@chromium.org
-+thestig@chromium.org
-+
-+# Admin policy related grit tools.
-+per-file *policy*=file://components/policy/tools/OWNERS
-+per-file *admin_template*=file://components/policy/tools/OWNERS
-diff --git a/tools/grit/PRESUBMIT.py b/tools/grit/PRESUBMIT.py
-new file mode 100644
-index 0000000000..03b7188551
---- /dev/null
-+++ b/tools/grit/PRESUBMIT.py
-@@ -0,0 +1,22 @@
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+"""grit unittests presubmit script.
-+
-+See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts for
-+details on the presubmit API built into gcl.
-+"""
-+
-+
-+def RunUnittests(input_api, output_api):
-+ return input_api.canned_checks.RunUnitTests(input_api, output_api,
-+ [input_api.os_path.join('grit', 'test_suite_all.py')])
-+
-+
-+def CheckChangeOnUpload(input_api, output_api):
-+ return RunUnittests(input_api, output_api)
-+
-+
-+def CheckChangeOnCommit(input_api, output_api):
-+ return RunUnittests(input_api, output_api)
-diff --git a/tools/grit/README.md b/tools/grit/README.md
-new file mode 100644
-index 0000000000..b5c3f4b51b
---- /dev/null
-+++ b/tools/grit/README.md
-@@ -0,0 +1,19 @@
-+# GRIT (Google Resource and Internationalization Tool)
-+
-+This is a tool for projects to manage resources and simplify the localization
-+workflow.
-+
-+See the user guide for more details on using this project:
-+https://dev.chromium.org/developers/tools-we-use-in-chromium/grit/grit-users-guide
-+
-+## History
-+
-+This code previously used to live at
-+https://code.google.com/p/grit-i18n/source/checkout which still contains the
-+project's history. https://chromium.googlesource.com/external/grit-i18n/ is
-+a git mirror of the SVN repository that's identical except for the last two
-+commits. The project is now developed in the Chromium project directly.
-+
-+There is a read-only mirror of just this directory at
-+https://chromium.googlesource.com/chromium/src/tools/grit/ if you don't want to
-+check out all of Chromium.
-diff --git a/tools/grit/grit.py b/tools/grit/grit.py
-new file mode 100644
-index 0000000000..abd1ab6449
---- /dev/null
-+++ b/tools/grit/grit.py
-@@ -0,0 +1,31 @@
-+#!/usr/bin/env python
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Bootstrapping for GRIT.
-+'''
-+
-+from __future__ import print_function
-+
-+import os
-+import sys
-+
-+import grit.grit_runner
-+
-+sys.path.append(
-+ os.path.join(
-+ os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
-+ 'diagnosis'))
-+try:
-+ import crbug_1001171
-+except ImportError:
-+ crbug_1001171 = None
-+
-+
-+if __name__ == '__main__':
-+ if crbug_1001171:
-+ with crbug_1001171.DumpStateOnLookupError():
-+ sys.exit(grit.grit_runner.Main(sys.argv[1:]))
-+ else:
-+ sys.exit(grit.grit_runner.Main(sys.argv[1:]))
-diff --git a/tools/grit/grit/__init__.py b/tools/grit/grit/__init__.py
-new file mode 100644
-index 0000000000..91ac9ee896
---- /dev/null
-+++ b/tools/grit/grit/__init__.py
-@@ -0,0 +1,19 @@
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Package 'grit'
-+'''
-+
-+from __future__ import print_function
-+
-+import os
-+import sys
-+
-+
-+_CUR_DIR = os.path.abspath(os.path.dirname(__file__))
-+_GRIT_DIR = os.path.dirname(_CUR_DIR)
-+_THIRD_PARTY_DIR = os.path.join(_GRIT_DIR, 'third_party')
-+
-+if _THIRD_PARTY_DIR not in sys.path:
-+ sys.path.insert(0, _THIRD_PARTY_DIR)
-diff --git a/tools/grit/grit/clique.py b/tools/grit/grit/clique.py
-new file mode 100644
-index 0000000000..e7be3ec164
---- /dev/null
-+++ b/tools/grit/grit/clique.py
-@@ -0,0 +1,491 @@
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Collections of messages and their translations, called cliques. Also
-+collections of cliques (uber-cliques).
-+'''
-+
-+from __future__ import print_function
-+
-+import re
-+
-+import six
-+
-+from grit import constants
-+from grit import exception
-+from grit import lazy_re
-+from grit import pseudo
-+from grit import pseudo_rtl
-+from grit import tclib
-+
-+
-+class UberClique(object):
-+ '''A factory (NOT a singleton factory) for making cliques. It has several
-+ methods for working with the cliques created using the factory.
-+ '''
-+
-+ def __init__(self):
-+ # A map from message ID to list of cliques whose source messages have
-+ # that ID. This will contain all cliques created using this factory.
-+ # Different messages can have the same ID because they have the
-+ # same translateable portion and placeholder names, but occur in different
-+ # places in the resource tree.
-+ #
-+ # Each list of cliques is kept sorted by description, to achieve
-+ # stable results from the BestClique method, see below.
-+ self.cliques_ = {}
-+
-+ # A map of clique IDs to list of languages to indicate translations where we
-+ # fell back to English.
-+ self.fallback_translations_ = {}
-+
-+ # A map of clique IDs to list of languages to indicate missing translations.
-+ self.missing_translations_ = {}
-+
-+ def _AddMissingTranslation(self, lang, clique, is_error):
-+ tl = self.fallback_translations_
-+ if is_error:
-+ tl = self.missing_translations_
-+ id = clique.GetId()
-+ if id not in tl:
-+ tl[id] = {}
-+ if lang not in tl[id]:
-+ tl[id][lang] = 1
-+
-+ def HasMissingTranslations(self):
-+ return len(self.missing_translations_) > 0
-+
-+ def MissingTranslationsReport(self):
-+ '''Returns a string suitable for printing to report missing
-+ and fallback translations to the user.
-+ '''
-+ def ReportTranslation(clique, langs):
-+ text = clique.GetMessage().GetPresentableContent()
-+ # The text 'error' (usually 'Error:' but we are conservative)
-+ # can trigger some build environments (Visual Studio, we're
-+ # looking at you) to consider invocation of grit to have failed,
-+ # so we make sure never to output that word.
-+ extract = re.sub(r'(?i)error', 'REDACTED', text[0:40])[0:40]
-+ ellipsis = ''
-+ if len(text) > 40:
-+ ellipsis = '...'
-+ langs_extract = langs[0:6]
-+ describe_langs = ','.join(langs_extract)
-+ if len(langs) > 6:
-+ describe_langs += " and %d more" % (len(langs) - 6)
-+ return " %s \"%s%s\" %s" % (clique.GetId(), extract, ellipsis,
-+ describe_langs)
-+ lines = []
-+ if len(self.fallback_translations_):
-+ lines.append(
-+ "WARNING: Fell back to English for the following translations:")
-+ for (id, langs) in self.fallback_translations_.items():
-+ lines.append(
-+ ReportTranslation(self.cliques_[id][0], list(langs.keys())))
-+ if len(self.missing_translations_):
-+ lines.append("ERROR: The following translations are MISSING:")
-+ for (id, langs) in self.missing_translations_.items():
-+ lines.append(
-+ ReportTranslation(self.cliques_[id][0], list(langs.keys())))
-+ return '\n'.join(lines)
-+
-+ def MakeClique(self, message, translateable=True):
-+ '''Create a new clique initialized with a message.
-+
-+ Args:
-+ message: tclib.Message()
-+ translateable: True | False
-+ '''
-+ clique = MessageClique(self, message, translateable)
-+
-+ # Enable others to find this clique by its message ID
-+ if message.GetId() in self.cliques_:
-+ presentable_text = clique.GetMessage().GetPresentableContent()
-+ if not message.HasAssignedId():
-+ for c in self.cliques_[message.GetId()]:
-+ assert c.GetMessage().GetPresentableContent() == presentable_text
-+ self.cliques_[message.GetId()].append(clique)
-+ # We need to keep each list of cliques sorted by description, to
-+ # achieve stable results from the BestClique method, see below.
-+ self.cliques_[message.GetId()].sort(
-+ key=lambda c:c.GetMessage().GetDescription())
-+ else:
-+ self.cliques_[message.GetId()] = [clique]
-+
-+ return clique
-+
-+ def FindCliqueAndAddTranslation(self, translation, language):
-+ '''Adds the specified translation to the clique with the source message
-+ it is a translation of.
-+
-+ Args:
-+ translation: tclib.Translation()
-+ language: 'en' | 'fr' ...
-+
-+ Return:
-+ True if the source message was found, otherwise false.
-+ '''
-+ if translation.GetId() in self.cliques_:
-+ for clique in self.cliques_[translation.GetId()]:
-+ clique.AddTranslation(translation, language)
-+ return True
-+ else:
-+ return False
-+
-+ def BestClique(self, id):
-+ '''Returns the "best" clique from a list of cliques. All the cliques
-+ must have the same ID. The "best" clique is chosen in the following
-+ order of preference:
-+ - The first clique that has a non-ID-based description.
-+ - If no such clique found, the first clique with an ID-based description.
-+ - Otherwise the first clique.
-+
-+ This method is stable in terms of always returning a clique with
-+ an identical description (on different runs of GRIT on the same
-+ data) because self.cliques_ is sorted by description.
-+ '''
-+ clique_list = self.cliques_[id]
-+ clique_with_id = None
-+ clique_default = None
-+ for clique in clique_list:
-+ if not clique_default:
-+ clique_default = clique
-+
-+ description = clique.GetMessage().GetDescription()
-+ if description and len(description) > 0:
-+ if not description.startswith('ID:'):
-+ # this is the preferred case so we exit right away
-+ return clique
-+ elif not clique_with_id:
-+ clique_with_id = clique
-+ if clique_with_id:
-+ return clique_with_id
-+ else:
-+ return clique_default
-+
-+ def BestCliquePerId(self):
-+ '''Iterates over the list of all cliques and returns the best clique for
-+ each ID. This will be the first clique with a source message that has a
-+ non-empty description, or an arbitrary clique if none of them has a
-+ description.
-+ '''
-+ for id in self.cliques_:
-+ yield self.BestClique(id)
-+
-+ def BestCliqueByOriginalText(self, text, meaning):
-+ '''Finds the "best" (as in BestClique()) clique that has original text
-+ 'text' and meaning 'meaning'. Returns None if there is no such clique.
-+ '''
-+ # If needed, this can be optimized by maintaining a map of
-+ # fingerprints of original text+meaning to cliques.
-+ for c in self.BestCliquePerId():
-+ msg = c.GetMessage()
-+ if msg.GetRealContent() == text and msg.GetMeaning() == meaning:
-+ return msg
-+ return None
-+
-+ def AllMessageIds(self):
-+ '''Returns a list of all defined message IDs.
-+ '''
-+ return list(self.cliques_.keys())
-+
-+ def AllCliques(self):
-+ '''Iterates over all cliques. Note that this can return multiple cliques
-+ with the same ID.
-+ '''
-+ for cliques in self.cliques_.values():
-+ for c in cliques:
-+ yield c
-+
-+ def GenerateXtbParserCallback(self, lang, debug=False):
-+ '''Creates a callback function as required by grit.xtb_reader.Parse().
-+ This callback will create Translation objects for each message from
-+ the XTB that exists in this uberclique, and add them as translations for
-+ the relevant cliques. The callback will add translations to the language
-+ specified by 'lang'
-+
-+ Args:
-+ lang: 'fr'
-+ debug: True | False
-+ '''
-+ def Callback(id, structure):
-+ if id not in self.cliques_:
-+ if debug:
-+ print("Ignoring translation #%s" % id)
-+ return
-+
-+ if debug:
-+ print("Adding translation #%s" % id)
-+
-+ # We fetch placeholder information from the original message (the XTB file
-+ # only contains placeholder names).
-+ original_msg = self.BestClique(id).GetMessage()
-+
-+ translation = tclib.Translation(id=id)
-+ for is_ph,text in structure:
-+ if not is_ph:
-+ translation.AppendText(text)
-+ else:
-+ found_placeholder = False
-+ for ph in original_msg.GetPlaceholders():
-+ if ph.GetPresentation() == text:
-+ translation.AppendPlaceholder(tclib.Placeholder(
-+ ph.GetPresentation(), ph.GetOriginal(), ph.GetExample()))
-+ found_placeholder = True
-+ break
-+ if not found_placeholder:
-+ raise exception.MismatchingPlaceholders(
-+ 'Translation for message ID %s had <ph name="%s"/>, no match\n'
-+ 'in original message' % (id, text))
-+ self.FindCliqueAndAddTranslation(translation, lang)
-+ return Callback
-+
-+
-+class CustomType(object):
-+ '''A base class you should implement if you wish to specify a custom type
-+ for a message clique (i.e. custom validation and optional modification of
-+ translations).'''
-+
-+ def Validate(self, message):
-+ '''Returns true if the message (a tclib.Message object) is valid,
-+ otherwise false.
-+ '''
-+ raise NotImplementedError()
-+
-+ def ValidateAndModify(self, lang, translation):
-+ '''Returns true if the translation (a tclib.Translation object) is valid,
-+ otherwise false. The language is also passed in. This method may modify
-+ the translation that is passed in, if it so wishes.
-+ '''
-+ raise NotImplementedError()
-+
-+ def ModifyTextPart(self, lang, text):
-+ '''If you call ModifyEachTextPart, it will turn around and call this method
-+ for each text part of the translation. You should return the modified
-+ version of the text, or just the original text to not change anything.
-+ '''
-+ raise NotImplementedError()
-+
-+ def ModifyEachTextPart(self, lang, translation):
-+ '''Call this to easily modify one or more of the textual parts of a
-+ translation. It will call ModifyTextPart for each part of the
-+ translation.
-+ '''
-+ contents = translation.GetContent()
-+ for ix in range(len(contents)):
-+ if (isinstance(contents[ix], six.string_types)):
-+ contents[ix] = self.ModifyTextPart(lang, contents[ix])
-+
-+
-+class OneOffCustomType(CustomType):
-+ '''A very simple custom type that performs the validation expressed by
-+ the input expression on all languages including the source language.
-+ The expression can access the variables 'lang', 'msg' and 'text()' where
-+ 'lang' is the language of 'msg', 'msg' is the message or translation being
-+ validated and 'text()' returns the real contents of 'msg' (for shorthand).
-+ '''
-+ def __init__(self, expression):
-+ self.expr = expression
-+ def Validate(self, message):
-+ return self.ValidateAndModify(MessageClique.source_language, message)
-+ def ValidateAndModify(self, lang, msg):
-+ def text():
-+ return msg.GetRealContent()
-+ return eval(self.expr, {},
-+ {'lang' : lang,
-+ 'text' : text,
-+ 'msg' : msg,
-+ })
-+
-+
-+class MessageClique(object):
-+ '''A message along with all of its translations. Also code to bring
-+ translations together with their original message.'''
-+
-+ # change this to the language code of Messages you add to cliques_.
-+ # TODO(joi) Actually change this based on the <grit> node's source language
-+ source_language = 'en'
-+
-+ # A constant translation we use when asked for a translation into the
-+ # special language constants.CONSTANT_LANGUAGE.
-+ CONSTANT_TRANSLATION = tclib.Translation(text='TTTTTT')
-+
-+ # A pattern to match messages that are empty or whitespace only.
-+ WHITESPACE_MESSAGE = lazy_re.compile(r'^\s*$')
-+
-+ def __init__(self, uber_clique, message, translateable=True,
-+ custom_type=None):
-+ '''Create a new clique initialized with just a message.
-+
-+ Note that messages with a body comprised only of whitespace will implicitly
-+ be marked non-translatable.
-+
-+ Args:
-+ uber_clique: Our uber-clique (collection of cliques)
-+ message: tclib.Message()
-+ translateable: True | False
-+ custom_type: instance of clique.CustomType interface
-+ '''
-+ # Our parent
-+ self.uber_clique = uber_clique
-+ # If not translateable, we only store the original message.
-+ self.translateable = translateable
-+
-+ # We implicitly mark messages that have a whitespace-only body as
-+ # non-translateable.
-+ if MessageClique.WHITESPACE_MESSAGE.match(message.GetRealContent()):
-+ self.translateable = False
-+
-+ # A mapping of language identifiers to tclib.BaseMessage and its
-+ # subclasses (i.e. tclib.Message and tclib.Translation).
-+ self.clique = { MessageClique.source_language : message }
-+ # A list of the "shortcut groups" this clique is
-+ # part of. Within any given shortcut group, no shortcut key (e.g. &J)
-+ # must appear more than once in each language for all cliques that
-+ # belong to the group.
-+ self.shortcut_groups = []
-+ # An instance of the CustomType interface, or None. If this is set, it will
-+ # be used to validate the original message and translations thereof, and
-+ # will also get a chance to modify translations of the message.
-+ self.SetCustomType(custom_type)
-+
-+ def GetMessage(self):
-+ '''Retrieves the tclib.Message that is the source for this clique.'''
-+ return self.clique[MessageClique.source_language]
-+
-+ def GetId(self):
-+ '''Retrieves the message ID of the messages in this clique.'''
-+ return self.GetMessage().GetId()
-+
-+ def IsTranslateable(self):
-+ return self.translateable
-+
-+ def AddToShortcutGroup(self, group):
-+ self.shortcut_groups.append(group)
-+
-+ def SetCustomType(self, custom_type):
-+ '''Makes this clique use custom_type for validating messages and
-+ translations, and optionally modifying translations.
-+ '''
-+ self.custom_type = custom_type
-+ if custom_type and not custom_type.Validate(self.GetMessage()):
-+ raise exception.InvalidMessage(self.GetMessage().GetRealContent())
-+
-+ def MessageForLanguage(self, lang, pseudo_if_no_match=True,
-+ fallback_to_english=False):
-+ '''Returns the message/translation for the specified language, providing
-+ a pseudotranslation if there is no available translation and a pseudo-
-+ translation is requested.
-+
-+ The translation of any message whatsoever in the special language
-+ 'x_constant' is the message "TTTTTT".
-+
-+ Args:
-+ lang: 'en'
-+ pseudo_if_no_match: True
-+ fallback_to_english: False
-+
-+ Return:
-+ tclib.BaseMessage
-+ '''
-+ if not self.translateable:
-+ return self.GetMessage()
-+
-+ if lang == constants.CONSTANT_LANGUAGE:
-+ return self.CONSTANT_TRANSLATION
-+
-+ for msglang in self.clique:
-+ if lang == msglang:
-+ return self.clique[msglang]
-+
-+ if lang == constants.FAKE_BIDI:
-+ return pseudo_rtl.PseudoRTLMessage(self.GetMessage())
-+
-+ if fallback_to_english:
-+ self.uber_clique._AddMissingTranslation(lang, self, is_error=False)
-+ return self.GetMessage()
-+
-+ # If we're not supposed to generate pseudotranslations, we add an error
-+ # report to a list of errors, then fail at a higher level, so that we
-+ # get a list of all messages that are missing translations.
-+ if not pseudo_if_no_match:
-+ self.uber_clique._AddMissingTranslation(lang, self, is_error=True)
-+
-+ return pseudo.PseudoMessage(self.GetMessage())
-+
-+ def AllMessagesThatMatch(self, lang_re, include_pseudo = True):
-+ '''Returns a map of all messages that match 'lang', including the pseudo
-+ translation if requested.
-+
-+ Args:
-+ lang_re: re.compile(r'fr|en')
-+ include_pseudo: True
-+
-+ Return:
-+ { 'en' : tclib.Message,
-+ 'fr' : tclib.Translation,
-+ pseudo.PSEUDO_LANG : tclib.Translation }
-+ '''
-+ if not self.translateable:
-+ return [self.GetMessage()]
-+
-+ matches = {}
-+ for msglang in self.clique:
-+ if lang_re.match(msglang):
-+ matches[msglang] = self.clique[msglang]
-+
-+ if include_pseudo:
-+ matches[pseudo.PSEUDO_LANG] = pseudo.PseudoMessage(self.GetMessage())
-+
-+ return matches
-+
-+ def AddTranslation(self, translation, language):
-+ '''Add a translation to this clique. The translation must have the same
-+ ID as the message that is the source for this clique.
-+
-+ If this clique is not translateable, the function just returns.
-+
-+ Args:
-+ translation: tclib.Translation()
-+ language: 'en'
-+
-+ Throws:
-+ grit.exception.InvalidTranslation if the translation you're trying to add
-+ doesn't have the same message ID as the source message of this clique.
-+ '''
-+ if not self.translateable:
-+ return
-+ if translation.GetId() != self.GetId():
-+ raise exception.InvalidTranslation(
-+ 'Msg ID %s, transl ID %s' % (self.GetId(), translation.GetId()))
-+
-+ assert not language in self.clique
-+
-+ # Because two messages can differ in the original content of their
-+ # placeholders yet share the same ID (because they are otherwise the
-+ # same), the translation we are getting may have different original
-+ # content for placeholders than our message, yet it is still the right
-+ # translation for our message (because it is for the same ID). We must
-+ # therefore fetch the original content of placeholders from our original
-+ # English message.
-+ #
-+ # See grit.clique_unittest.MessageCliqueUnittest.testSemiIdenticalCliques
-+ # for a concrete explanation of why this is necessary.
-+
-+ original = self.MessageForLanguage(self.source_language, False)
-+ if len(original.GetPlaceholders()) != len(translation.GetPlaceholders()):
-+ print("ERROR: '%s' translation of message id %s does not match" %
-+ (language, translation.GetId()))
-+ assert False
-+
-+ transl_msg = tclib.Translation(id=self.GetId(),
-+ text=translation.GetPresentableContent(),
-+ placeholders=original.GetPlaceholders())
-+
-+ if (self.custom_type and
-+ not self.custom_type.ValidateAndModify(language, transl_msg)):
-+ print("WARNING: %s translation failed validation: %s" %
-+ (language, transl_msg.GetId()))
-+
-+ self.clique[language] = transl_msg
-diff --git a/tools/grit/grit/clique_unittest.py b/tools/grit/grit/clique_unittest.py
-new file mode 100644
-index 0000000000..7d2d7318ba
---- /dev/null
-+++ b/tools/grit/grit/clique_unittest.py
-@@ -0,0 +1,265 @@
-+#!/usr/bin/env python
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Unit tests for grit.clique'''
-+
-+from __future__ import print_function
-+
-+import os
-+import sys
-+if __name__ == '__main__':
-+ sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
-+
-+import re
-+import unittest
-+
-+from six import StringIO
-+
-+from grit import clique
-+from grit import exception
-+from grit import pseudo
-+from grit import tclib
-+from grit import grd_reader
-+from grit import util
-+
-+class MessageCliqueUnittest(unittest.TestCase):
-+ def testClique(self):
-+ factory = clique.UberClique()
-+ msg = tclib.Message(text='Hello USERNAME, how are you?',
-+ placeholders=[
-+ tclib.Placeholder('USERNAME', '%s', 'Joi')])
-+ c = factory.MakeClique(msg)
-+
-+ self.failUnless(c.GetMessage() == msg)
-+ self.failUnless(c.GetId() == msg.GetId())
-+
-+ msg_fr = tclib.Translation(text='Bonjour USERNAME, comment ca va?',
-+ id=msg.GetId(), placeholders=[
-+ tclib.Placeholder('USERNAME', '%s', 'Joi')])
-+ msg_de = tclib.Translation(text='Guten tag USERNAME, wie geht es dir?',
-+ id=msg.GetId(), placeholders=[
-+ tclib.Placeholder('USERNAME', '%s', 'Joi')])
-+
-+ c.AddTranslation(msg_fr, 'fr')
-+ factory.FindCliqueAndAddTranslation(msg_de, 'de')
-+
-+ # sort() sorts lists in-place and does not return them
-+ for lang in ('en', 'fr', 'de'):
-+ self.failUnless(lang in c.clique)
-+
-+ self.failUnless(c.MessageForLanguage('fr').GetRealContent() ==
-+ msg_fr.GetRealContent())
-+
-+ try:
-+ c.MessageForLanguage('zh-CN', False)
-+ self.fail('Should have gotten exception')
-+ except:
-+ pass
-+
-+ self.failUnless(c.MessageForLanguage('zh-CN', True) != None)
-+
-+ rex = re.compile('fr|de|bingo')
-+ self.failUnless(len(c.AllMessagesThatMatch(rex, False)) == 2)
-+ self.failUnless(
-+ c.AllMessagesThatMatch(rex, True)[pseudo.PSEUDO_LANG] is not None)
-+
-+ def testBestClique(self):
-+ factory = clique.UberClique()
-+ factory.MakeClique(tclib.Message(text='Alfur', description='alfaholl'))
-+ factory.MakeClique(tclib.Message(text='Alfur', description=''))
-+ factory.MakeClique(tclib.Message(text='Vaettur', description=''))
-+ factory.MakeClique(tclib.Message(text='Vaettur', description=''))
-+ factory.MakeClique(tclib.Message(text='Troll', description=''))
-+ factory.MakeClique(tclib.Message(text='Gryla', description='ID: IDS_GRYLA'))
-+ factory.MakeClique(tclib.Message(text='Gryla', description='vondakerling'))
-+ factory.MakeClique(tclib.Message(text='Leppaludi', description='ID: IDS_LL'))
-+ factory.MakeClique(tclib.Message(text='Leppaludi', description=''))
-+
-+ count_best_cliques = 0
-+ for c in factory.BestCliquePerId():
-+ count_best_cliques += 1
-+ msg = c.GetMessage()
-+ text = msg.GetRealContent()
-+ description = msg.GetDescription()
-+ if text == 'Alfur':
-+ self.failUnless(description == 'alfaholl')
-+ elif text == 'Gryla':
-+ self.failUnless(description == 'vondakerling')
-+ elif text == 'Leppaludi':
-+ self.failUnless(description == 'ID: IDS_LL')
-+ self.failUnless(count_best_cliques == 5)
-+
-+ def testAllInUberClique(self):
-+ resources = grd_reader.Parse(
-+ StringIO(u'''<?xml version="1.0" encoding="UTF-8"?>
-+<grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
-+ <release seq="3">
-+ <messages>
-+ <message name="IDS_GREETING" desc="Printed to greet the currently logged in user">
-+ Hello <ph name="USERNAME">%s<ex>Joi</ex></ph>, how are you doing today?
-+ </message>
-+ </messages>
-+ <structures>
-+ <structure type="dialog" name="IDD_ABOUTBOX" encoding="utf-16" file="grit/testdata/klonk.rc" />
-+ <structure type="tr_html" name="ID_HTML" file="grit/testdata/simple.html" />
-+ </structures>
-+ </release>
-+</grit>'''), util.PathFromRoot('.'))
-+ resources.SetOutputLanguage('en')
-+ resources.RunGatherers()
-+ content_list = []
-+ for clique_list in resources.UberClique().cliques_.values():
-+ for clique in clique_list:
-+ content_list.append(clique.GetMessage().GetRealContent())
-+ self.failUnless('Hello %s, how are you doing today?' in content_list)
-+ self.failUnless('Jack "Black" Daniels' in content_list)
-+ self.failUnless('Hello!' in content_list)
-+
-+ def testCorrectExceptionIfWrongEncodingOnResourceFile(self):
-+ '''This doesn't really belong in this unittest file, but what the heck.'''
-+ resources = grd_reader.Parse(
-+ StringIO(u'''<?xml version="1.0" encoding="UTF-8"?>
-+<grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
-+ <release seq="3">
-+ <structures>
-+ <structure type="dialog" name="IDD_ABOUTBOX" file="grit/testdata/klonk.rc" />
-+ </structures>
-+ </release>
-+</grit>'''), util.PathFromRoot('.'))
-+ self.assertRaises(exception.SectionNotFound, resources.RunGatherers)
-+
-+ def testSemiIdenticalCliques(self):
-+ messages = [
-+ tclib.Message(text='Hello USERNAME',
-+ placeholders=[tclib.Placeholder('USERNAME', '$1', 'Joi')]),
-+ tclib.Message(text='Hello USERNAME',
-+ placeholders=[tclib.Placeholder('USERNAME', '%s', 'Joi')]),
-+ ]
-+ self.failUnless(messages[0].GetId() == messages[1].GetId())
-+
-+ # Both of the above would share a translation.
-+ translation = tclib.Translation(id=messages[0].GetId(),
-+ text='Bonjour USERNAME',
-+ placeholders=[tclib.Placeholder(
-+ 'USERNAME', '$1', 'Joi')])
-+
-+ factory = clique.UberClique()
-+ cliques = [factory.MakeClique(msg) for msg in messages]
-+
-+ for clq in cliques:
-+ clq.AddTranslation(translation, 'fr')
-+
-+ self.failUnless(cliques[0].MessageForLanguage('fr').GetRealContent() ==
-+ 'Bonjour $1')
-+ self.failUnless(cliques[1].MessageForLanguage('fr').GetRealContent() ==
-+ 'Bonjour %s')
-+
-+ def testMissingTranslations(self):
-+ messages = [ tclib.Message(text='Hello'), tclib.Message(text='Goodbye') ]
-+ factory = clique.UberClique()
-+ cliques = [factory.MakeClique(msg) for msg in messages]
-+
-+ cliques[1].MessageForLanguage('fr', False, True)
-+
-+ self.failUnless(not factory.HasMissingTranslations())
-+
-+ cliques[0].MessageForLanguage('de', False, False)
-+
-+ self.failUnless(factory.HasMissingTranslations())
-+
-+ report = factory.MissingTranslationsReport()
-+ self.failUnless(report.count('WARNING') == 1)
-+ self.failUnless(report.count('8053599568341804890 "Goodbye" fr') == 1)
-+ self.failUnless(report.count('ERROR') == 1)
-+ self.failUnless(report.count('800120468867715734 "Hello" de') == 1)
-+
-+ def testCustomTypes(self):
-+ factory = clique.UberClique()
-+ message = tclib.Message(text='Bingo bongo')
-+ c = factory.MakeClique(message)
-+ try:
-+ c.SetCustomType(DummyCustomType())
-+ self.fail()
-+ except:
-+ pass # expected case - 'Bingo bongo' does not start with 'jjj'
-+
-+ message = tclib.Message(text='jjjBingo bongo')
-+ c = factory.MakeClique(message)
-+ c.SetCustomType(util.NewClassInstance(
-+ 'grit.clique_unittest.DummyCustomType', clique.CustomType))
-+ translation = tclib.Translation(id=message.GetId(), text='Bilingo bolongo')
-+ c.AddTranslation(translation, 'fr')
-+ self.failUnless(c.MessageForLanguage('fr').GetRealContent().startswith('jjj'))
-+
-+ def testWhitespaceMessagesAreNontranslateable(self):
-+ factory = clique.UberClique()
-+
-+ message = tclib.Message(text=' \t')
-+ c = factory.MakeClique(message, translateable=True)
-+ self.failIf(c.IsTranslateable())
-+
-+ message = tclib.Message(text='\n \n ')
-+ c = factory.MakeClique(message, translateable=True)
-+ self.failIf(c.IsTranslateable())
-+
-+ message = tclib.Message(text='\n hello')
-+ c = factory.MakeClique(message, translateable=True)
-+ self.failUnless(c.IsTranslateable())
-+
-+ def testEachCliqueKeptSorted(self):
-+ factory = clique.UberClique()
-+ msg_a = tclib.Message(text='hello', description='a')
-+ msg_b = tclib.Message(text='hello', description='b')
-+ msg_c = tclib.Message(text='hello', description='c')
-+ # Insert out of order
-+ clique_b = factory.MakeClique(msg_b, translateable=True)
-+ clique_a = factory.MakeClique(msg_a, translateable=True)
-+ clique_c = factory.MakeClique(msg_c, translateable=True)
-+ clique_list = factory.cliques_[clique_a.GetId()]
-+ self.failUnless(len(clique_list) == 3)
-+ self.failUnless(clique_list[0] == clique_a)
-+ self.failUnless(clique_list[1] == clique_b)
-+ self.failUnless(clique_list[2] == clique_c)
-+
-+ def testBestCliqueSortIsStable(self):
-+ factory = clique.UberClique()
-+ text = 'hello'
-+ msg_no_description = tclib.Message(text=text)
-+ msg_id_description_a = tclib.Message(text=text, description='ID: a')
-+ msg_id_description_b = tclib.Message(text=text, description='ID: b')
-+ msg_description_x = tclib.Message(text=text, description='x')
-+ msg_description_y = tclib.Message(text=text, description='y')
-+ clique_id = msg_no_description.GetId()
-+
-+ # Insert in an order that tests all outcomes.
-+ clique_no_description = factory.MakeClique(msg_no_description,
-+ translateable=True)
-+ self.failUnless(factory.BestClique(clique_id) == clique_no_description)
-+ clique_id_description_b = factory.MakeClique(msg_id_description_b,
-+ translateable=True)
-+ self.failUnless(factory.BestClique(clique_id) == clique_id_description_b)
-+ clique_id_description_a = factory.MakeClique(msg_id_description_a,
-+ translateable=True)
-+ self.failUnless(factory.BestClique(clique_id) == clique_id_description_a)
-+ clique_description_y = factory.MakeClique(msg_description_y,
-+ translateable=True)
-+ self.failUnless(factory.BestClique(clique_id) == clique_description_y)
-+ clique_description_x = factory.MakeClique(msg_description_x,
-+ translateable=True)
-+ self.failUnless(factory.BestClique(clique_id) == clique_description_x)
-+
-+
-+class DummyCustomType(clique.CustomType):
-+ def Validate(self, message):
-+ return message.GetRealContent().startswith('jjj')
-+ def ValidateAndModify(self, lang, translation):
-+ is_ok = self.Validate(translation)
-+ self.ModifyEachTextPart(lang, translation)
-+ def ModifyTextPart(self, lang, text):
-+ return 'jjj%s' % text
-+
-+
-+if __name__ == '__main__':
-+ unittest.main()
-diff --git a/tools/grit/grit/constants.py b/tools/grit/grit/constants.py
-new file mode 100644
-index 0000000000..8229c94b09
---- /dev/null
-+++ b/tools/grit/grit/constants.py
-@@ -0,0 +1,23 @@
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Constant definitions for GRIT.
-+'''
-+
-+from __future__ import print_function
-+
-+# This is the Icelandic noun meaning "grit" and is used to check that our
-+# input files are in the correct encoding. The middle character gets encoded
-+# as two bytes in UTF-8, so this is sufficient to detect incorrect encoding.
-+ENCODING_CHECK = u'm\u00f6l'
-+
-+# A special language, translations into which are always "TTTTTT".
-+CONSTANT_LANGUAGE = 'x_constant'
-+
-+FAKE_BIDI = 'fake-bidi'
-+
-+# Magic number added to the header of resources brotli compressed by grit. Used
-+# to easily identify resources as being brotli compressed. See
-+# ui/base/resource/resource_bundle.h for decompression usage.
-+BROTLI_CONST = b'\x1e\x9b'
-diff --git a/tools/grit/grit/exception.py b/tools/grit/grit/exception.py
-new file mode 100644
-index 0000000000..2a363fb077
---- /dev/null
-+++ b/tools/grit/grit/exception.py
-@@ -0,0 +1,139 @@
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Exception types for GRIT.
-+'''
-+
-+from __future__ import print_function
-+
-+class Base(Exception):
-+ '''A base exception that uses the class's docstring in addition to any
-+ user-provided message as the body of the Base.
-+ '''
-+ def __init__(self, msg=''):
-+ if len(msg):
-+ if self.__doc__:
-+ msg = self.__doc__ + ': ' + msg
-+ else:
-+ msg = self.__doc__
-+ super(Base, self).__init__(msg)
-+
-+
-+class Parsing(Base):
-+ '''An error occurred parsing a GRD or XTB file.'''
-+ pass
-+
-+
-+class UnknownElement(Parsing):
-+ '''An unknown node type was encountered.'''
-+ pass
-+
-+
-+class MissingElement(Parsing):
-+ '''An expected element was missing.'''
-+ pass
-+
-+
-+class UnexpectedChild(Parsing):
-+ '''An unexpected child element was encountered (on a leaf node).'''
-+ pass
-+
-+
-+class UnexpectedAttribute(Parsing):
-+ '''The attribute was not expected'''
-+ pass
-+
-+
-+class UnexpectedContent(Parsing):
-+ '''This element should not have content'''
-+ pass
-+
-+class MissingMandatoryAttribute(Parsing):
-+ '''This element is missing a mandatory attribute'''
-+ pass
-+
-+
-+class MutuallyExclusiveMandatoryAttribute(Parsing):
-+ '''This element has 2 mutually exclusive mandatory attributes'''
-+ pass
-+
-+
-+class DuplicateKey(Parsing):
-+ '''A duplicate key attribute was found.'''
-+ pass
-+
-+
-+class TooManyExamples(Parsing):
-+ '''Only one <ex> element is allowed for each <ph> element.'''
-+ pass
-+
-+
-+class FileNotFound(Parsing):
-+ '''The resource file was not found.'''
-+ pass
-+
-+
-+class InvalidMessage(Base):
-+ '''The specified message failed validation.'''
-+ pass
-+
-+
-+class InvalidTranslation(Base):
-+ '''Attempt to add an invalid translation to a clique.'''
-+ pass
-+
-+
-+class NoSuchTranslation(Base):
-+ '''Requested translation not available'''
-+ pass
-+
-+
-+class NotReady(Base):
-+ '''Attempt to use an object before it is ready, or attempt to translate \
-+an empty document.'''
-+ pass
-+
-+
-+class MismatchingPlaceholders(Base):
-+ '''Placeholders do not match.'''
-+ pass
-+
-+
-+class InvalidPlaceholderName(Base):
-+ '''Placeholder name can only contain A-Z, a-z, 0-9 and underscore.'''
-+ pass
-+
-+
-+class BlockTagInTranslateableChunk(Base):
-+ '''A block tag was encountered where it wasn't expected.'''
-+ pass
-+
-+
-+class SectionNotFound(Base):
-+ '''The section you requested was not found in the RC file. Make \
-+sure the section ID is correct (matches the section's ID in the RC file). \
-+Also note that you may need to specify the RC file's encoding (using the \
-+encoding="" attribute) if it is not in the default Windows-1252 encoding. \
-+'''
-+ pass
-+
-+
-+class IdRangeOverlap(Base):
-+ '''ID range overlap.'''
-+ pass
-+
-+
-+class ReservedHeaderCollision(Base):
-+ '''Resource included with first 3 bytes matching reserved header.'''
-+ pass
-+
-+
-+class PlaceholderNotInsidePhNode(Base):
-+ '''Placeholder formatters should be inside <ph> element.'''
-+ pass
-+
-+
-+class InvalidCharactersInsidePhNode(Base):
-+ '''Invalid characters found inside <ph> element.'''
-+ pass
-diff --git a/tools/grit/grit/extern/BogoFP.py b/tools/grit/grit/extern/BogoFP.py
-new file mode 100644
-index 0000000000..fc90145833
---- /dev/null
-+++ b/tools/grit/grit/extern/BogoFP.py
-@@ -0,0 +1,22 @@
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+"""Bogus fingerprint implementation, do not use for production,
-+provided only as an example.
-+
-+Usage:
-+ grit.py -h grit.extern.BogoFP xmb /tmp/foo
-+"""
-+
-+from __future__ import print_function
-+
-+import grit.extern.FP
-+
-+
-+def UnsignedFingerPrint(str, encoding='utf-8'):
-+ """Generate a fingerprint not intended for production from str (it
-+ reduces the precision of the production fingerprint by one bit).
-+ """
-+ return (0xFFFFF7FFFFFFFFFF &
-+ grit.extern.FP._UnsignedFingerPrintImpl(str, encoding))
-diff --git a/tools/grit/grit/extern/FP.py b/tools/grit/grit/extern/FP.py
-new file mode 100644
-index 0000000000..f4ec4d943f
---- /dev/null
-+++ b/tools/grit/grit/extern/FP.py
-@@ -0,0 +1,72 @@
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+from __future__ import print_function
-+
-+try:
-+ import hashlib
-+ _new_md5 = hashlib.md5
-+except ImportError:
-+ import md5
-+ _new_md5 = md5.new
-+
-+
-+"""64-bit fingerprint support for strings.
-+
-+Usage:
-+ from extern import FP
-+ print('Fingerprint is %ld' % FP.FingerPrint('Hello world!'))
-+"""
-+
-+
-+def _UnsignedFingerPrintImpl(str, encoding='utf-8'):
-+ """Generate a 64-bit fingerprint by taking the first half of the md5
-+ of the string.
-+ """
-+ hex128 = _new_md5(str.encode(encoding)).hexdigest()
-+ int64 = int(hex128[:16], 16)
-+ return int64
-+
-+
-+def UnsignedFingerPrint(str, encoding='utf-8'):
-+ """Generate a 64-bit fingerprint.
-+
-+ The default implementation uses _UnsignedFingerPrintImpl, which
-+ takes the first half of the md5 of the string, but the
-+ implementation may be switched using SetUnsignedFingerPrintImpl.
-+ """
-+ return _UnsignedFingerPrintImpl(str, encoding)
-+
-+
-+def FingerPrint(str, encoding='utf-8'):
-+ fp = UnsignedFingerPrint(str, encoding=encoding)
-+ # interpret fingerprint as signed longs
-+ if fp & 0x8000000000000000:
-+ fp = -((~fp & 0xFFFFFFFFFFFFFFFF) + 1)
-+ return fp
-+
-+
-+def UseUnsignedFingerPrintFromModule(module_name):
-+ """Imports module_name and replaces UnsignedFingerPrint in the
-+ current module with the function of the same name from the imported
-+ module.
-+
-+ Returns the function object previously known as
-+ grit.extern.FP.UnsignedFingerPrint.
-+ """
-+ hash_module = __import__(module_name, fromlist=[module_name])
-+ return SetUnsignedFingerPrint(hash_module.UnsignedFingerPrint)
-+
-+
-+def SetUnsignedFingerPrint(function_object):
-+ """Sets grit.extern.FP.UnsignedFingerPrint to point to
-+ function_object.
-+
-+ Returns the function object previously known as
-+ grit.extern.FP.UnsignedFingerPrint.
-+ """
-+ global UnsignedFingerPrint
-+ original_function_object = UnsignedFingerPrint
-+ UnsignedFingerPrint = function_object
-+ return original_function_object
-diff --git a/tools/grit/grit/extern/__init__.py b/tools/grit/grit/extern/__init__.py
-new file mode 100644
-index 0000000000..e69de29bb2
-diff --git a/tools/grit/grit/extern/tclib.py b/tools/grit/grit/extern/tclib.py
-new file mode 100644
-index 0000000000..9952a87c11
---- /dev/null
-+++ b/tools/grit/grit/extern/tclib.py
-@@ -0,0 +1,503 @@
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+# The tclib module contains tools for aggregating, verifying, and storing
-+# messages destined for the Translation Console, as well as for reading
-+# translations back and outputting them in some desired format.
-+#
-+# This has been stripped down to include only the functionality needed by grit
-+# for creating Windows .rc and .h files. These are the only parts needed by
-+# the Chrome build process.
-+
-+from __future__ import print_function
-+
-+from grit.extern import FP
-+
-+# This module assumes that within a bundle no two messages can have the
-+# same id unless they're identical.
-+
-+# The basic classes defined here for external use are Message and Translation,
-+# where the former is used for English messages and the latter for
-+# translations. These classes have a lot of common functionality, as expressed
-+# by the common parent class BaseMessage. Perhaps the most important
-+# distinction is that translated text is stored in UTF-8, whereas original text
-+# is stored in whatever encoding the client uses (presumably Latin-1).
-+
-+# --------------------
-+# The public interface
-+# --------------------
-+
-+# Generate message id from message text and meaning string (optional),
-+# both in utf-8 encoding
-+#
-+def GenerateMessageId(message, meaning=''):
-+ fp = FP.FingerPrint(message)
-+ if meaning:
-+ # combine the fingerprints of message and meaning
-+ fp2 = FP.FingerPrint(meaning)
-+ if fp < 0:
-+ fp = fp2 + (fp << 1) + 1
-+ else:
-+ fp = fp2 + (fp << 1)
-+ # To avoid negative ids we strip the high-order bit
-+ return str(fp & 0x7fffffffffffffff)
-+
-+# -------------------------------------------------------------------------
-+# The MessageTranslationError class is used to signal tclib-specific errors.
-+
-+
-+class MessageTranslationError(Exception):
-+
-+ def __init__(self, args = ''):
-+ self.args = args
-+
-+
-+# -----------------------------------------------------------
-+# The Placeholder class represents a placeholder in a message.
-+
-+class Placeholder(object):
-+ # String representation
-+ def __str__(self):
-+ return '%s, "%s", "%s"' % \
-+ (self.__presentation, self.__original, self.__example)
-+
-+ # Getters
-+ def GetOriginal(self):
-+ return self.__original
-+
-+ def GetPresentation(self):
-+ return self.__presentation
-+
-+ def GetExample(self):
-+ return self.__example
-+
-+ def __eq__(self, other):
-+ return self.EqualTo(other, strict=1, ignore_trailing_spaces=0)
-+
-+ # Equality test
-+ #
-+ # ignore_trailing_spaces: TC is using varchar to store the
-+ # phrwr fields, as a result of that, the trailing spaces
-+ # are removed by MySQL when the strings are stored into TC:-(
-+ # ignore_trailing_spaces parameter is used to ignore
-+ # trailing spaces during equivalence comparison.
-+ #
-+ def EqualTo(self, other, strict = 1, ignore_trailing_spaces = 1):
-+ if type(other) is not Placeholder:
-+ return 0
-+ if StringEquals(self.__presentation, other.__presentation,
-+ ignore_trailing_spaces):
-+ if not strict or (StringEquals(self.__original, other.__original,
-+ ignore_trailing_spaces) and
-+ StringEquals(self.__example, other.__example,
-+ ignore_trailing_spaces)):
-+ return 1
-+ return 0
-+
-+
-+# -----------------------------------------------------------------
-+# BaseMessage is the common parent class of Message and Translation.
-+# It is not meant for direct use.
-+
-+class BaseMessage(object):
-+ # Three types of message construction is supported. If the message text is a
-+ # simple string with no dynamic content, you can pass it to the constructor
-+ # as the "text" parameter. Otherwise, you can omit "text" and assemble the
-+ # message step by step using AppendText() and AppendPlaceholder(). Or, as an
-+ # alternative, you can give the constructor the "presentable" version of the
-+ # message and a list of placeholders; it will then parse the presentation and
-+ # build the message accordingly. For example:
-+ # Message(text = "There are NUM_BUGS bugs in your code",
-+ # placeholders = [Placeholder("NUM_BUGS", "%d", "33")],
-+ # description = "Bla bla bla")
-+ def __eq__(self, other):
-+ # "source encoding" is nonsense, so ignore it
-+ return _ObjectEquals(self, other, ['_BaseMessage__source_encoding'])
-+
-+ def GetName(self):
-+ return self.__name
-+
-+ def GetSourceEncoding(self):
-+ return self.__source_encoding
-+
-+ # Append a placeholder to the message
-+ def AppendPlaceholder(self, placeholder):
-+ if not isinstance(placeholder, Placeholder):
-+ raise MessageTranslationError("Invalid message placeholder %s in "
-+ "message %s" % (placeholder, self.GetId()))
-+ # Are there other placeholders with the same presentation?
-+ # If so, they need to be the same.
-+ for other in self.GetPlaceholders():
-+ if placeholder.GetPresentation() == other.GetPresentation():
-+ if not placeholder.EqualTo(other):
-+ raise MessageTranslationError(
-+ "Conflicting declarations of %s within message" %
-+ placeholder.GetPresentation())
-+ # update placeholder list
-+ dup = 0
-+ for item in self.__content:
-+ if isinstance(item, Placeholder) and placeholder.EqualTo(item):
-+ dup = 1
-+ break
-+ if not dup:
-+ self.__placeholders.append(placeholder)
-+
-+ # update content
-+ self.__content.append(placeholder)
-+
-+ # Strips leading and trailing whitespace, and returns a tuple
-+ # containing the leading and trailing space that was removed.
-+ def Strip(self):
-+ leading = trailing = ''
-+ if len(self.__content) > 0:
-+ s0 = self.__content[0]
-+ if not isinstance(s0, Placeholder):
-+ s = s0.lstrip()
-+ leading = s0[:-len(s)]
-+ self.__content[0] = s
-+
-+ s0 = self.__content[-1]
-+ if not isinstance(s0, Placeholder):
-+ s = s0.rstrip()
-+ trailing = s0[len(s):]
-+ self.__content[-1] = s
-+ return leading, trailing
-+
-+ # Return the id of this message
-+ def GetId(self):
-+ if self.__id is None:
-+ return self.GenerateId()
-+ return self.__id
-+
-+ # Set the id of this message
-+ def SetId(self, id):
-+ if id is None:
-+ self.__id = None
-+ else:
-+ self.__id = str(id) # Treat numerical ids as strings
-+
-+ # Return content of this message as a list (internal use only)
-+ def GetContent(self):
-+ return self.__content
-+
-+ # Return a human-readable version of this message
-+ def GetPresentableContent(self):
-+ presentable_content = ""
-+ for item in self.__content:
-+ if isinstance(item, Placeholder):
-+ presentable_content += item.GetPresentation()
-+ else:
-+ presentable_content += item
-+
-+ return presentable_content
-+
-+ # Return a fragment of a message in escaped format
-+ def EscapeFragment(self, fragment):
-+ return fragment.replace('%', '%%')
-+
-+ # Return the "original" version of this message, doing %-escaping
-+ # properly. If source_msg is specified, the placeholder original
-+ # information inside source_msg will be used instead.
-+ def GetOriginalContent(self, source_msg = None):
-+ original_content = ""
-+ for item in self.__content:
-+ if isinstance(item, Placeholder):
-+ if source_msg:
-+ ph = source_msg.GetPlaceholder(item.GetPresentation())
-+ if not ph:
-+ raise MessageTranslationError(
-+ "Placeholder %s doesn't exist in message: %s" %
-+ (item.GetPresentation(), source_msg))
-+ original_content += ph.GetOriginal()
-+ else:
-+ original_content += item.GetOriginal()
-+ else:
-+ original_content += self.EscapeFragment(item)
-+ return original_content
-+
-+ # Return the example of this message
-+ def GetExampleContent(self):
-+ example_content = ""
-+ for item in self.__content:
-+ if isinstance(item, Placeholder):
-+ example_content += item.GetExample()
-+ else:
-+ example_content += item
-+ return example_content
-+
-+ # Return a list of all unique placeholders in this message
-+ def GetPlaceholders(self):
-+ return self.__placeholders
-+
-+ # Return a placeholder in this message
-+ def GetPlaceholder(self, presentation):
-+ for item in self.__content:
-+ if (isinstance(item, Placeholder) and
-+ item.GetPresentation() == presentation):
-+ return item
-+ return None
-+
-+ # Return this message's description
-+ def GetDescription(self):
-+ return self.__description
-+
-+ # Add a message source
-+ def AddSource(self, source):
-+ self.__sources.append(source)
-+
-+ # Return this message's sources as a list
-+ def GetSources(self):
-+ return self.__sources
-+
-+ # Return this message's sources as a string
-+ def GetSourcesAsText(self, delimiter = "; "):
-+ return delimiter.join(self.__sources)
-+
-+ # Set the obsolete flag for a message (internal use only)
-+ def SetObsolete(self):
-+ self.__obsolete = 1
-+
-+ # Get the obsolete flag for a message (internal use only)
-+ def IsObsolete(self):
-+ return self.__obsolete
-+
-+ # Get the sequence number (0 by default)
-+ def GetSequenceNumber(self):
-+ return self.__sequence_number
-+
-+ # Set the sequence number
-+ def SetSequenceNumber(self, number):
-+ self.__sequence_number = number
-+
-+ # Increment instance counter
-+ def AddInstance(self):
-+ self.__num_instances += 1
-+
-+ # Return instance count
-+ def GetNumInstances(self):
-+ return self.__num_instances
-+
-+ def GetErrors(self, from_tc=0):
-+ """
-+ Returns a description of the problem if the message is not
-+ syntactically valid, or None if everything is fine.
-+
-+ Args:
-+ from_tc: indicates whether this message came from the TC. We let
-+ the TC get away with some things we normally wouldn't allow for
-+ historical reasons.
-+ """
-+ # check that placeholders are unambiguous
-+ pos = 0
-+ phs = {}
-+ for item in self.__content:
-+ if isinstance(item, Placeholder):
-+ phs[pos] = item
-+ pos += len(item.GetPresentation())
-+ else:
-+ pos += len(item)
-+ presentation = self.GetPresentableContent()
-+ for ph in self.GetPlaceholders():
-+ for pos in FindOverlapping(presentation, ph.GetPresentation()):
-+ # message contains the same text as a placeholder presentation
-+ other_ph = phs.get(pos)
-+ if ((not other_ph
-+ and not IsSubstringInPlaceholder(pos, len(ph.GetPresentation()), phs))
-+ or
-+ (other_ph and len(other_ph.GetPresentation()) < len(ph.GetPresentation()))):
-+ return "message contains placeholder name '%s':\n%s" % (
-+ ph.GetPresentation(), presentation)
-+ return None
-+
-+
-+ def __CopyTo(self, other):
-+ """
-+ Returns a copy of this BaseMessage.
-+ """
-+ assert isinstance(other, self.__class__) or isinstance(self, other.__class__)
-+ other.__source_encoding = self.__source_encoding
-+ other.__content = self.__content[:]
-+ other.__description = self.__description
-+ other.__id = self.__id
-+ other.__num_instances = self.__num_instances
-+ other.__obsolete = self.__obsolete
-+ other.__name = self.__name
-+ other.__placeholders = self.__placeholders[:]
-+ other.__sequence_number = self.__sequence_number
-+ other.__sources = self.__sources[:]
-+
-+ return other
-+
-+ def HasText(self):
-+ """Returns true iff this message has anything other than placeholders."""
-+ for item in self.__content:
-+ if not isinstance(item, Placeholder):
-+ return True
-+ return False
-+
-+# --------------------------------------------------------
-+# The Message class represents original (English) messages
-+
-+class Message(BaseMessage):
-+ # See BaseMessage constructor
-+ def __init__(self, source_encoding, text=None, id=None,
-+ description=None, meaning="", placeholders=None,
-+ source=None, sequence_number=0, clone_from=None,
-+ time_created=0, name=None, is_hidden = 0):
-+
-+ if clone_from is not None:
-+ BaseMessage.__init__(self, None, clone_from=clone_from)
-+ self.__meaning = clone_from.__meaning
-+ self.__time_created = clone_from.__time_created
-+ self.__is_hidden = clone_from.__is_hidden
-+ return
-+
-+ BaseMessage.__init__(self, source_encoding, text, id, description,
-+ placeholders, source, sequence_number,
-+ name=name)
-+ self.__meaning = meaning
-+ self.__time_created = time_created
-+ self.SetIsHidden(is_hidden)
-+
-+ # String representation
-+ def __str__(self):
-+ s = 'source: %s, id: %s, content: "%s", meaning: "%s", ' \
-+ 'description: "%s"' % \
-+ (self.GetSourcesAsText(), self.GetId(), self.GetPresentableContent(),
-+ self.__meaning, self.GetDescription())
-+ if self.GetName() is not None:
-+ s += ', name: "%s"' % self.GetName()
-+ placeholders = self.GetPlaceholders()
-+ for i in range(len(placeholders)):
-+ s += ", placeholder[%d]: %s" % (i, placeholders[i])
-+ return s
-+
-+ # Strips leading and trailing whitespace, and returns a tuple
-+ # containing the leading and trailing space that was removed.
-+ def Strip(self):
-+ leading = trailing = ''
-+ content = self.GetContent()
-+ if len(content) > 0:
-+ s0 = content[0]
-+ if not isinstance(s0, Placeholder):
-+ s = s0.lstrip()
-+ leading = s0[:-len(s)]
-+ content[0] = s
-+
-+ s0 = content[-1]
-+ if not isinstance(s0, Placeholder):
-+ s = s0.rstrip()
-+ trailing = s0[len(s):]
-+ content[-1] = s
-+ return leading, trailing
-+
-+ # Generate an id by hashing message content
-+ def GenerateId(self):
-+ self.SetId(GenerateMessageId(self.GetPresentableContent(),
-+ self.__meaning))
-+ return self.GetId()
-+
-+ def GetMeaning(self):
-+ return self.__meaning
-+
-+ def GetTimeCreated(self):
-+ return self.__time_created
-+
-+ # Equality operator
-+ def EqualTo(self, other, strict = 1):
-+ # Check id, meaning, content
-+ if self.GetId() != other.GetId():
-+ return 0
-+ if self.__meaning != other.__meaning:
-+ return 0
-+ if self.GetPresentableContent() != other.GetPresentableContent():
-+ return 0
-+ # Check descriptions if comparison is strict
-+ if (strict and
-+ self.GetDescription() is not None and
-+ other.GetDescription() is not None and
-+ self.GetDescription() != other.GetDescription()):
-+ return 0
-+ # Check placeholders
-+ ph1 = self.GetPlaceholders()
-+ ph2 = other.GetPlaceholders()
-+ if len(ph1) != len(ph2):
-+ return 0
-+ for i in range(len(ph1)):
-+ if not ph1[i].EqualTo(ph2[i], strict):
-+ return 0
-+
-+ return 1
-+
-+ def Copy(self):
-+ """
-+ Returns a copy of this Message.
-+ """
-+ assert isinstance(self, Message)
-+ return Message(None, clone_from=self)
-+
-+ def SetIsHidden(self, is_hidden):
-+ """Sets whether this message should be hidden.
-+
-+ Args:
-+ is_hidden : 0 or 1 - if the message should be hidden, 0 otherwise
-+ """
-+ if is_hidden not in [0, 1]:
-+ raise MessageTranslationError("is_hidden must be 0 or 1, got %s")
-+ self.__is_hidden = is_hidden
-+
-+ def IsHidden(self):
-+ """Returns 1 if this message is hidden, and 0 otherwise."""
-+ return self.__is_hidden
-+
-+# ----------------------------------------------------
-+# The Translation class represents translated messages
-+
-+class Translation(BaseMessage):
-+ # See BaseMessage constructor
-+ def __init__(self, source_encoding, text=None, id=None,
-+ description=None, placeholders=None, source=None,
-+ sequence_number=0, clone_from=None, ignore_ph_errors=0,
-+ name=None):
-+ if clone_from is not None:
-+ BaseMessage.__init__(self, None, clone_from=clone_from)
-+ return
-+
-+ BaseMessage.__init__(self, source_encoding, text, id, description,
-+ placeholders, source, sequence_number,
-+ ignore_ph_errors=ignore_ph_errors, name=name)
-+
-+ # String representation
-+ def __str__(self):
-+ s = 'source: %s, id: %s, content: "%s", description: "%s"' % \
-+ (self.GetSourcesAsText(), self.GetId(), self.GetPresentableContent(),
-+ self.GetDescription());
-+ placeholders = self.GetPlaceholders()
-+ for i in range(len(placeholders)):
-+ s += ", placeholder[%d]: %s" % (i, placeholders[i])
-+ return s
-+
-+ # Equality operator
-+ def EqualTo(self, other, strict=1):
-+ # Check id and content
-+ if self.GetId() != other.GetId():
-+ return 0
-+ if self.GetPresentableContent() != other.GetPresentableContent():
-+ return 0
-+ # Check placeholders
-+ ph1 = self.GetPlaceholders()
-+ ph2 = other.GetPlaceholders()
-+ if len(ph1) != len(ph2):
-+ return 0
-+ for i in range(len(ph1)):
-+ if not ph1[i].EqualTo(ph2[i], strict):
-+ return 0
-+
-+ return 1
-+
-+ def Copy(self):
-+ """
-+ Returns a copy of this Translation.
-+ """
-+ return Translation(None, clone_from=self)
-diff --git a/tools/grit/grit/format/__init__.py b/tools/grit/grit/format/__init__.py
-new file mode 100644
-index 0000000000..55d56b8cfd
---- /dev/null
-+++ b/tools/grit/grit/format/__init__.py
-@@ -0,0 +1,8 @@
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Module grit.format
-+'''
-+
-+pass
-diff --git a/tools/grit/grit/format/android_xml.py b/tools/grit/grit/format/android_xml.py
-new file mode 100644
-index 0000000000..7eb288891f
---- /dev/null
-+++ b/tools/grit/grit/format/android_xml.py
-@@ -0,0 +1,212 @@
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+"""Produces localized strings.xml files for Android.
-+
-+In cases where an "android" type output file is requested in a grd, the classes
-+in android_xml will process the messages and translations to produce a valid
-+strings.xml that is properly localized with the specified language.
-+
-+For example if the following output tag were to be included in a grd file
-+ <outputs>
-+ ...
-+ <output filename="values-es/strings.xml" type="android" lang="es" />
-+ ...
-+ </outputs>
-+
-+for a grd file with the following messages:
-+
-+ <message name="IDS_HELLO" desc="Simple greeting">Hello</message>
-+ <message name="IDS_WORLD" desc="The world">world</message>
-+
-+and there existed an appropriate xtb file containing the Spanish translations,
-+then the output would be:
-+
-+ <?xml version="1.0" encoding="utf-8"?>
-+ <resources xmlns:android="http://schemas.android.com/apk/res/android">
-+ <string name="hello">"Hola"</string>
-+ <string name="world">"mundo"</string>
-+ </resources>
-+
-+which would be written to values-es/strings.xml and usable by the Android
-+resource framework.
-+
-+Advanced usage
-+--------------
-+
-+To process only certain messages in a grd file, tag each desired message by
-+adding "android_java" to formatter_data. Then set the environmental variable
-+ANDROID_JAVA_TAGGED_ONLY to "true" when building the grd file. For example:
-+
-+ <message name="IDS_HELLO" formatter_data="android_java">Hello</message>
-+
-+To generate Android plurals (aka "quantity strings"), use the ICU plural syntax
-+in the grd file. This will automatically be transformed into a <purals> element
-+in the output xml file. For example:
-+
-+ <message name="IDS_CATS">
-+ {NUM_CATS, plural,
-+ =1 {1 cat}
-+ other {# cats}}
-+ </message>
-+
-+ will produce
-+
-+ <plurals name="cats">
-+ <item quantity="one">1 Katze</item>
-+ <item quantity="other">%d Katzen</item>
-+ </plurals>
-+"""
-+
-+from __future__ import print_function
-+
-+import os
-+import re
-+import xml.sax.saxutils
-+
-+from grit import lazy_re
-+from grit.node import message
-+
-+
-+# When this environmental variable has value "true", only tagged messages will
-+# be outputted.
-+_TAGGED_ONLY_ENV_VAR = 'ANDROID_JAVA_TAGGED_ONLY'
-+_TAGGED_ONLY_DEFAULT = False
-+
-+# In tagged-only mode, only messages with this tag will be ouputted.
-+_EMIT_TAG = 'android_java'
-+
-+_NAME_PATTERN = lazy_re.compile(r'IDS_(?P<name>[A-Z0-9_]+)\Z')
-+
-+# Most strings are output as a <string> element. Note the double quotes
-+# around the value to preserve whitespace.
-+_STRING_TEMPLATE = u'<string name="%s">"%s"</string>\n'
-+
-+# Some strings are output as a <plurals> element.
-+_PLURALS_TEMPLATE = '<plurals name="%s">\n%s</plurals>\n'
-+_PLURALS_ITEM_TEMPLATE = ' <item quantity="%s">%s</item>\n'
-+
-+# Matches e.g. "{HELLO, plural, HOW ARE YOU DOING}", while capturing
-+# "HOW ARE YOU DOING" in <items>.
-+_PLURALS_PATTERN = lazy_re.compile(r'\{[A-Z_]+,\s*plural,(?P<items>.*)\}$',
-+ flags=re.S)
-+
-+# Repeatedly matched against the <items> capture in _PLURALS_PATTERN,
-+# to match "<quantity>{<value>}".
-+_PLURALS_ITEM_PATTERN = lazy_re.compile(r'(?P<quantity>\S+?)\s*'
-+ r'\{(?P<value>.*?)\}')
-+_PLURALS_QUANTITY_MAP = {
-+ '=0': 'zero',
-+ 'zero': 'zero',
-+ '=1': 'one',
-+ 'one': 'one',
-+ '=2': 'two',
-+ 'two': 'two',
-+ 'few': 'few',
-+ 'many': 'many',
-+ 'other': 'other',
-+}
-+
-+
-+def Format(root, lang='en', output_dir='.'):
-+ yield ('<?xml version="1.0" encoding="utf-8"?>\n'
-+ '<resources '
-+ 'xmlns:android="http://schemas.android.com/apk/res/android">\n')
-+
-+ tagged_only = _TAGGED_ONLY_DEFAULT
-+ if _TAGGED_ONLY_ENV_VAR in os.environ:
-+ tagged_only = os.environ[_TAGGED_ONLY_ENV_VAR].lower()
-+ if tagged_only == 'true':
-+ tagged_only = True
-+ elif tagged_only == 'false':
-+ tagged_only = False
-+ else:
-+ raise Exception('env variable ANDROID_JAVA_TAGGED_ONLY must have value '
-+ 'true or false. Invalid value: %s' % tagged_only)
-+
-+ for item in root.ActiveDescendants():
-+ with item:
-+ if ShouldOutputNode(item, tagged_only):
-+ yield _FormatMessage(item, lang)
-+
-+ yield '</resources>\n'
-+
-+
-+def ShouldOutputNode(node, tagged_only):
-+ """Returns true if node should be outputted.
-+
-+ Args:
-+ node: a Node from the grd dom
-+ tagged_only: true, if only tagged messages should be outputted
-+ """
-+ return (isinstance(node, message.MessageNode) and
-+ (not tagged_only or _EMIT_TAG in node.formatter_data))
-+
-+
-+def _FormatPluralMessage(message):
-+ """Compiles ICU plural syntax to the body of an Android <plurals> element.
-+
-+ 1. In a .grd file, we can write a plural string like this:
-+
-+ <message name="IDS_THINGS">
-+ {NUM_THINGS, plural,
-+ =1 {1 thing}
-+ other {# things}}
-+ </message>
-+
-+ 2. The Android equivalent looks like this:
-+
-+ <plurals name="things">
-+ <item quantity="one">1 thing</item>
-+ <item quantity="other">%d things</item>
-+ </plurals>
-+
-+ This method takes the body of (1) and converts it to the body of (2).
-+
-+ If the message is *not* a plural string, this function returns `None`.
-+ If the message includes quantities without an equivalent format in Android,
-+ it raises an exception.
-+ """
-+ ret = {}
-+ plural_match = _PLURALS_PATTERN.match(message)
-+ if not plural_match:
-+ return None
-+ body_in = plural_match.group('items').strip()
-+ lines = []
-+ quantities_so_far = set()
-+ for item_match in _PLURALS_ITEM_PATTERN.finditer(body_in):
-+ quantity_in = item_match.group('quantity')
-+ quantity_out = _PLURALS_QUANTITY_MAP.get(quantity_in)
-+ value_in = item_match.group('value')
-+ value_out = '"' + value_in.replace('#', '%d') + '"'
-+ if quantity_out:
-+ # only one line per quantity out (https://crbug.com/787488)
-+ if quantity_out not in quantities_so_far:
-+ quantities_so_far.add(quantity_out)
-+ lines.append(_PLURALS_ITEM_TEMPLATE % (quantity_out, value_out))
-+ else:
-+ raise Exception('Unsupported plural quantity for android '
-+ 'strings.xml: %s' % quantity_in)
-+ return ''.join(lines)
-+
-+
-+def _FormatMessage(item, lang):
-+ """Writes out a single string as a <resource/> element."""
-+
-+ mangled_name = item.GetTextualIds()[0]
-+ match = _NAME_PATTERN.match(mangled_name)
-+ if not match:
-+ raise Exception('Unexpected resource name: %s' % mangled_name)
-+ name = match.group('name').lower()
-+
-+ value = item.ws_at_start + item.Translate(lang) + item.ws_at_end
-+ # Replace < > & with &lt; &gt; &amp; to ensure we generate valid XML and
-+ # replace ' " with \' \" to conform to Android's string formatting rules.
-+ value = xml.sax.saxutils.escape(value, {"'": "\\'", '"': '\\"'})
-+
-+ plurals = _FormatPluralMessage(value)
-+ if plurals:
-+ return _PLURALS_TEMPLATE % (name, plurals)
-+ else:
-+ return _STRING_TEMPLATE % (name, value)
-diff --git a/tools/grit/grit/format/android_xml_unittest.py b/tools/grit/grit/format/android_xml_unittest.py
-new file mode 100644
-index 0000000000..d9f476fddf
---- /dev/null
-+++ b/tools/grit/grit/format/android_xml_unittest.py
-@@ -0,0 +1,149 @@
-+#!/usr/bin/env python
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+"""Unittest for android_xml.py."""
-+
-+from __future__ import print_function
-+
-+import os
-+import sys
-+import unittest
-+
-+if __name__ == '__main__':
-+ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
-+
-+from six import StringIO
-+
-+from grit import util
-+from grit.format import android_xml
-+from grit.node import message
-+from grit.tool import build
-+
-+
-+class AndroidXmlUnittest(unittest.TestCase):
-+
-+ def testMessages(self):
-+ root = util.ParseGrdForUnittest(r"""
-+ <messages>
-+ <message name="IDS_SIMPLE" desc="A vanilla string">
-+ Martha
-+ </message>
-+ <message name="IDS_ONE_LINE" desc="On one line">sat and wondered</message>
-+ <message name="IDS_QUOTES" desc="A string with quotation marks">
-+ out loud, "Why don't I build a flying car?"
-+ </message>
-+ <message name="IDS_MULTILINE" desc="A string split over several lines">
-+ She gathered
-+wood, charcoal, and
-+a sledge hammer.
-+ </message>
-+ <message name="IDS_WHITESPACE" desc="A string with extra whitespace.">
-+ ''' How old fashioned -- she thought. '''
-+ </message>
-+ <message name="IDS_PLACEHOLDERS" desc="A string with placeholders">
-+ I'll buy a <ph name="WAVELENGTH">%d<ex>200</ex></ph> nm laser at <ph name="STORE_NAME">%s<ex>the grocery store</ex></ph>.
-+ </message>
-+ <message name="IDS_PLURALS" desc="A string using the ICU plural format">
-+ {NUM_THINGS, plural,
-+ =1 {Maybe I'll get one laser.}
-+ other {Maybe I'll get # lasers.}}
-+ </message>
-+ <message name="IDS_PLURALS_NO_SPACE" desc="A string using the ICU plural format with no space">
-+ {NUM_MISSISSIPPIS, plural,
-+ =1{OneMississippi}other{ManyMississippis}}
-+ </message>
-+ </messages>
-+ """)
-+
-+ buf = StringIO()
-+ build.RcBuilder.ProcessNode(root, DummyOutput('android', 'en'), buf)
-+ output = buf.getvalue()
-+ expected = r"""
-+<?xml version="1.0" encoding="utf-8"?>
-+<resources xmlns:android="http://schemas.android.com/apk/res/android">
-+<string name="simple">"Martha"</string>
-+<string name="one_line">"sat and wondered"</string>
-+<string name="quotes">"out loud, \"Why don\'t I build a flying car?\""</string>
-+<string name="multiline">"She gathered
-+wood, charcoal, and
-+a sledge hammer."</string>
-+<string name="whitespace">" How old fashioned -- she thought. "</string>
-+<string name="placeholders">"I\'ll buy a %d nm laser at %s."</string>
-+<plurals name="plurals">
-+ <item quantity="one">"Maybe I\'ll get one laser."</item>
-+ <item quantity="other">"Maybe I\'ll get %d lasers."</item>
-+</plurals>
-+<plurals name="plurals_no_space">
-+ <item quantity="one">"OneMississippi"</item>
-+ <item quantity="other">"ManyMississippis"</item>
-+</plurals>
-+</resources>
-+"""
-+ self.assertEqual(output.strip(), expected.strip())
-+
-+
-+ def testConflictingPlurals(self):
-+ root = util.ParseGrdForUnittest(r"""
-+ <messages>
-+ <message name="IDS_PLURALS" desc="A string using the ICU plural format">
-+ {NUM_THINGS, plural,
-+ =1 {Maybe I'll get one laser.}
-+ one {Maybe I'll get one laser.}
-+ other {Maybe I'll get # lasers.}}
-+ </message>
-+ </messages>
-+ """)
-+
-+ buf = StringIO()
-+ build.RcBuilder.ProcessNode(root, DummyOutput('android', 'en'), buf)
-+ output = buf.getvalue()
-+ expected = r"""
-+<?xml version="1.0" encoding="utf-8"?>
-+<resources xmlns:android="http://schemas.android.com/apk/res/android">
-+<plurals name="plurals">
-+ <item quantity="one">"Maybe I\'ll get one laser."</item>
-+ <item quantity="other">"Maybe I\'ll get %d lasers."</item>
-+</plurals>
-+</resources>
-+"""
-+ self.assertEqual(output.strip(), expected.strip())
-+
-+
-+ def testTaggedOnly(self):
-+ root = util.ParseGrdForUnittest(r"""
-+ <messages>
-+ <message name="IDS_HELLO" desc="" formatter_data="android_java">
-+ Hello
-+ </message>
-+ <message name="IDS_WORLD" desc="">
-+ world
-+ </message>
-+ </messages>
-+ """)
-+
-+ msg_hello, msg_world = root.GetChildrenOfType(message.MessageNode)
-+ self.assertTrue(android_xml.ShouldOutputNode(msg_hello, tagged_only=True))
-+ self.assertFalse(android_xml.ShouldOutputNode(msg_world, tagged_only=True))
-+ self.assertTrue(android_xml.ShouldOutputNode(msg_hello, tagged_only=False))
-+ self.assertTrue(android_xml.ShouldOutputNode(msg_world, tagged_only=False))
-+
-+
-+class DummyOutput(object):
-+
-+ def __init__(self, type, language):
-+ self.type = type
-+ self.language = language
-+
-+ def GetType(self):
-+ return self.type
-+
-+ def GetLanguage(self):
-+ return self.language
-+
-+ def GetOutputFilename(self):
-+ return 'hello.gif'
-+
-+if __name__ == '__main__':
-+ unittest.main()
-diff --git a/tools/grit/grit/format/c_format.py b/tools/grit/grit/format/c_format.py
-new file mode 100644
-index 0000000000..16809a9f70
---- /dev/null
-+++ b/tools/grit/grit/format/c_format.py
-@@ -0,0 +1,95 @@
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+"""Formats as a .C file for compilation.
-+"""
-+
-+from __future__ import print_function
-+
-+import codecs
-+import os
-+import re
-+
-+import six
-+
-+from grit import util
-+
-+
-+def _FormatHeader(root, output_dir):
-+ """Returns the required preamble for C files."""
-+ # Find the location of the resource header file, so that we can include
-+ # it.
-+ resource_header = 'resource.h' # fall back to this
-+ for output in root.GetOutputFiles():
-+ if output.attrs['type'] == 'rc_header':
-+ resource_header = os.path.abspath(output.GetOutputFilename())
-+ resource_header = util.MakeRelativePath(output_dir, resource_header)
-+ return """// This file is automatically generated by GRIT. Do not edit.
-+
-+#include "%s"
-+
-+// All strings are UTF-8
-+""" % (resource_header)
-+# end _FormatHeader() function
-+
-+
-+def Format(root, lang='en', output_dir='.'):
-+ """Outputs a C switch statement representing the string table."""
-+ from grit.node import message
-+ assert isinstance(lang, six.string_types)
-+
-+ yield _FormatHeader(root, output_dir)
-+
-+ yield 'const char* GetString(int id) {\n switch (id) {'
-+
-+ for item in root.ActiveDescendants():
-+ with item:
-+ if isinstance(item, message.MessageNode):
-+ yield _FormatMessage(item, lang)
-+
-+ yield '\n default:\n return 0;\n }\n}\n'
-+
-+
-+def _HexToOct(match):
-+ "Return the octal form of the hex numbers"
-+ hex = match.group("hex")
-+ result = ""
-+ while len(hex):
-+ next_num = int(hex[2:4], 16)
-+ result += "\\" + '%03o' % next_num
-+ hex = hex[4:]
-+ return match.group("escaped_backslashes") + result
-+
-+
-+def _FormatMessage(item, lang):
-+ """Format a single <message> element."""
-+
-+ message = item.ws_at_start + item.Translate(lang) + item.ws_at_end
-+ # Output message with non-ascii chars escaped as octal numbers C's grammar
-+ # allows escaped hexadecimal numbers to be infinite, but octal is always of
-+ # the form \OOO. Python 3 doesn't support string-escape, so we have to jump
-+ # through some hoops here via codecs.escape_encode.
-+ # This basically does:
-+ # - message - the starting string
-+ # - message.encode(...) - convert to bytes
-+ # - codecs.escape_encode(...) - convert non-ASCII bytes to \x## escapes
-+ # - (...).decode() - convert bytes back to a string
-+ message = codecs.escape_encode(message.encode('utf-8'))[0].decode('utf-8')
-+ # an escaped char is (\xHH)+ but only if the initial
-+ # backslash is not escaped.
-+ not_a_backslash = r"(^|[^\\])" # beginning of line or a non-backslash char
-+ escaped_backslashes = not_a_backslash + r"(\\\\)*"
-+ hex_digits = r"((\\x)[0-9a-f]{2})+"
-+ two_digit_hex_num = re.compile(
-+ r"(?P<escaped_backslashes>%s)(?P<hex>%s)"
-+ % (escaped_backslashes, hex_digits))
-+ message = two_digit_hex_num.sub(_HexToOct, message)
-+ # unescape \ (convert \\ back to \)
-+ message = message.replace('\\\\', '\\')
-+ message = message.replace('"', '\\"')
-+ message = util.LINEBREAKS.sub(r'\\n', message)
-+
-+ name_attr = item.GetTextualIds()[0]
-+
-+ return '\n case %s:\n return "%s";' % (name_attr, message)
-diff --git a/tools/grit/grit/format/c_format_unittest.py b/tools/grit/grit/format/c_format_unittest.py
-new file mode 100644
-index 0000000000..380120c42f
---- /dev/null
-+++ b/tools/grit/grit/format/c_format_unittest.py
-@@ -0,0 +1,81 @@
-+#!/usr/bin/env python
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+"""Unittest for c_format.py.
-+"""
-+
-+from __future__ import print_function
-+
-+import os
-+import sys
-+if __name__ == '__main__':
-+ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
-+
-+import unittest
-+
-+from six import StringIO
-+
-+from grit import util
-+from grit.tool import build
-+
-+
-+class CFormatUnittest(unittest.TestCase):
-+
-+ def testMessages(self):
-+ root = util.ParseGrdForUnittest(u"""
-+ <messages>
-+ <message name="IDS_QUESTIONS">Do you want to play questions?</message>
-+ <message name="IDS_QUOTES">
-+ "What's in a name, <ph name="NAME">%s<ex>Brandon</ex></ph>?"
-+ </message>
-+ <message name="IDS_LINE_BREAKS">
-+ Was that rhetoric?
-+No.
-+Statement. Two all. Game point.
-+</message>
-+ <message name="IDS_NON_ASCII">
-+ \u00f5\\xc2\\xa4\\\u00a4\\\\xc3\\xb5\u4924
-+ </message>
-+ </messages>
-+ """)
-+
-+ buf = StringIO()
-+ build.RcBuilder.ProcessNode(root, DummyOutput('c_format', 'en'), buf)
-+ output = util.StripBlankLinesAndComments(buf.getvalue())
-+ self.assertEqual(u"""\
-+#include "resource.h"
-+const char* GetString(int id) {
-+ switch (id) {
-+ case IDS_QUESTIONS:
-+ return "Do you want to play questions?";
-+ case IDS_QUOTES:
-+ return "\\"What\\'s in a name, %s?\\"";
-+ case IDS_LINE_BREAKS:
-+ return "Was that rhetoric?\\nNo.\\nStatement. Two all. Game point.";
-+ case IDS_NON_ASCII:
-+ return "\\303\\265\\xc2\\xa4\\\\302\\244\\\\xc3\\xb5\\344\\244\\244";
-+ default:
-+ return 0;
-+ }
-+}""", output)
-+
-+
-+class DummyOutput(object):
-+
-+ def __init__(self, type, language):
-+ self.type = type
-+ self.language = language
-+
-+ def GetType(self):
-+ return self.type
-+
-+ def GetLanguage(self):
-+ return self.language
-+
-+ def GetOutputFilename(self):
-+ return 'hello.gif'
-+
-+if __name__ == '__main__':
-+ unittest.main()
-diff --git a/tools/grit/grit/format/chrome_messages_json.py b/tools/grit/grit/format/chrome_messages_json.py
-new file mode 100644
-index 0000000000..88ec1d914b
---- /dev/null
-+++ b/tools/grit/grit/format/chrome_messages_json.py
-@@ -0,0 +1,59 @@
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+"""Formats as a .json file that can be used to localize Google Chrome
-+extensions."""
-+
-+from __future__ import print_function
-+
-+from json import JSONEncoder
-+
-+from grit import constants
-+from grit.node import message
-+
-+def Format(root, lang='en', output_dir='.'):
-+ """Format the messages as JSON."""
-+ yield '{'
-+
-+ encoder = JSONEncoder(ensure_ascii=False)
-+ format = '"%s":{"message":%s%s}'
-+ placeholder_format = '"%i":{"content":"$%i"}'
-+ first = True
-+ for child in root.ActiveDescendants():
-+ if isinstance(child, message.MessageNode):
-+ id = child.attrs['name']
-+ if id.startswith('IDR_') or id.startswith('IDS_'):
-+ id = id[4:]
-+
-+ translation_missing = child.GetCliques()[0].clique.get(lang) is None;
-+ if (child.ShouldFallbackToEnglish() and translation_missing and
-+ lang != constants.FAKE_BIDI):
-+ # Skip the string if it's not translated. Chrome will fallback
-+ # to English automatically.
-+ continue
-+
-+ loc_message = encoder.encode(child.ws_at_start + child.Translate(lang) +
-+ child.ws_at_end)
-+
-+ # Replace $n place-holders with $n$ and add an appropriate "placeholders"
-+ # entry. Note that chrome.i18n.getMessage only supports 9 placeholders:
-+ # https://developer.chrome.com/extensions/i18n#method-getMessage
-+ placeholders = ''
-+ for i in range(1, 10):
-+ if loc_message.find('$%d' % i) == -1:
-+ break
-+ loc_message = loc_message.replace('$%d' % i, '$%d$' % i)
-+ if placeholders:
-+ placeholders += ','
-+ placeholders += placeholder_format % (i, i)
-+
-+ if not first:
-+ yield ','
-+ first = False
-+
-+ if placeholders:
-+ placeholders = ',"placeholders":{%s}' % placeholders
-+ yield format % (id, loc_message, placeholders)
-+
-+ yield '}'
-diff --git a/tools/grit/grit/format/chrome_messages_json_unittest.py b/tools/grit/grit/format/chrome_messages_json_unittest.py
-new file mode 100644
-index 0000000000..a54e6bdc1c
---- /dev/null
-+++ b/tools/grit/grit/format/chrome_messages_json_unittest.py
-@@ -0,0 +1,190 @@
-+#!/usr/bin/env python
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+"""Unittest for chrome_messages_json.py.
-+"""
-+
-+from __future__ import print_function
-+
-+import json
-+import os
-+import sys
-+if __name__ == '__main__':
-+ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
-+
-+import unittest
-+
-+from six import StringIO
-+
-+from grit import grd_reader
-+from grit import util
-+from grit.tool import build
-+
-+class ChromeMessagesJsonFormatUnittest(unittest.TestCase):
-+
-+ # The default unittest diff limit is too low for our unittests.
-+ # Allow the framework to show the full diff output all the time.
-+ maxDiff = None
-+
-+ def testMessages(self):
-+ root = util.ParseGrdForUnittest(u"""
-+ <messages>
-+ <message name="IDS_SIMPLE_MESSAGE">
-+ Simple message.
-+ </message>
-+ <message name="IDS_QUOTES">
-+ element\u2019s \u201c<ph name="NAME">%s<ex>name</ex></ph>\u201d attribute
-+ </message>
-+ <message name="IDS_PLACEHOLDERS">
-+ <ph name="ERROR_COUNT">%1$d<ex>1</ex></ph> error, <ph name="WARNING_COUNT">%2$d<ex>1</ex></ph> warning
-+ </message>
-+ <message name="IDS_PLACEHOLDERS_SUBSTITUTED_BY_GETMESSAGE">
-+ <ph name="BEGIN">$1<ex>a</ex></ph>test<ph name="END">$2<ex>b</ex></ph>
-+ </message>
-+ <message name="IDS_STARTS_WITH_SPACE">
-+ ''' (<ph name="COUNT">%d<ex>2</ex></ph>)
-+ </message>
-+ <message name="IDS_ENDS_WITH_SPACE">
-+ (<ph name="COUNT">%d<ex>2</ex></ph>) '''
-+ </message>
-+ <message name="IDS_SPACE_AT_BOTH_ENDS">
-+ ''' (<ph name="COUNT">%d<ex>2</ex></ph>) '''
-+ </message>
-+ <message name="IDS_DOUBLE_QUOTES">
-+ A "double quoted" message.
-+ </message>
-+ <message name="IDS_BACKSLASH">
-+ \\
-+ </message>
-+ </messages>
-+ """)
-+
-+ buf = StringIO()
-+ build.RcBuilder.ProcessNode(root, DummyOutput('chrome_messages_json', 'en'),
-+ buf)
-+ output = buf.getvalue()
-+ test = u"""
-+{
-+ "SIMPLE_MESSAGE": {
-+ "message": "Simple message."
-+ },
-+ "QUOTES": {
-+ "message": "element\u2019s \u201c%s\u201d attribute"
-+ },
-+ "PLACEHOLDERS": {
-+ "message": "%1$d error, %2$d warning"
-+ },
-+ "PLACEHOLDERS_SUBSTITUTED_BY_GETMESSAGE": {
-+ "message": "$1$test$2$",
-+ "placeholders": {
-+ "1": {
-+ "content": "$1"
-+ },
-+ "2": {
-+ "content": "$2"
-+ }
-+ }
-+ },
-+ "STARTS_WITH_SPACE": {
-+ "message": " (%d)"
-+ },
-+ "ENDS_WITH_SPACE": {
-+ "message": "(%d) "
-+ },
-+ "SPACE_AT_BOTH_ENDS": {
-+ "message": " (%d) "
-+ },
-+ "DOUBLE_QUOTES": {
-+ "message": "A \\"double quoted\\" message."
-+ },
-+ "BACKSLASH": {
-+ "message": "\\\\"
-+ }
-+}
-+"""
-+ self.assertEqual(json.loads(test), json.loads(output))
-+
-+ def testTranslations(self):
-+ root = util.ParseGrdForUnittest("""
-+ <messages>
-+ <message name="ID_HELLO">Hello!</message>
-+ <message name="ID_HELLO_USER">Hello <ph name="USERNAME">%s<ex>
-+ Joi</ex></ph></message>
-+ </messages>
-+ """)
-+
-+ buf = StringIO()
-+ build.RcBuilder.ProcessNode(root, DummyOutput('chrome_messages_json', 'fr'),
-+ buf)
-+ output = buf.getvalue()
-+ test = u"""
-+{
-+ "ID_HELLO": {
-+ "message": "H\u00e9P\u00e9ll\u00f4P\u00f4!"
-+ },
-+ "ID_HELLO_USER": {
-+ "message": "H\u00e9P\u00e9ll\u00f4P\u00f4 %s"
-+ }
-+}
-+"""
-+ self.assertEqual(json.loads(test), json.loads(output))
-+
-+ def testSkipMissingTranslations(self):
-+ grd = """<?xml version="1.0" encoding="UTF-8"?>
-+<grit latest_public_release="2" current_release="3" source_lang_id="en"
-+ base_dir="%s">
-+ <outputs>
-+ </outputs>
-+ <release seq="3" allow_pseudo="False">
-+ <messages fallback_to_english="true">
-+ <message name="ID_HELLO_NO_TRANSLATION">Hello not translated</message>
-+ </messages>
-+ </release>
-+</grit>"""
-+ root = grd_reader.Parse(StringIO(grd), dir=".")
-+
-+ buf = StringIO()
-+ build.RcBuilder.ProcessNode(root, DummyOutput('chrome_messages_json', 'fr'),
-+ buf)
-+ output = buf.getvalue()
-+ test = u'{}'
-+ self.assertEqual(test, output)
-+
-+ def testVerifyMinification(self):
-+ root = util.ParseGrdForUnittest(u"""
-+ <messages>
-+ <message name="IDS">
-+ <ph name="BEGIN">$1<ex>a</ex></ph>test<ph name="END">$2<ex>b</ex></ph>
-+ </message>
-+ </messages>
-+ """)
-+
-+ buf = StringIO()
-+ build.RcBuilder.ProcessNode(root, DummyOutput('chrome_messages_json', 'en'),
-+ buf)
-+ output = buf.getvalue()
-+ test = (u'{"IDS":{"message":"$1$test$2$","placeholders":'
-+ u'{"1":{"content":"$1"},"2":{"content":"$2"}}}}')
-+ self.assertEqual(test, output)
-+
-+
-+class DummyOutput(object):
-+
-+ def __init__(self, type, language):
-+ self.type = type
-+ self.language = language
-+
-+ def GetType(self):
-+ return self.type
-+
-+ def GetLanguage(self):
-+ return self.language
-+
-+ def GetOutputFilename(self):
-+ return 'hello.gif'
-+
-+
-+if __name__ == '__main__':
-+ unittest.main()
-diff --git a/tools/grit/grit/format/data_pack.py b/tools/grit/grit/format/data_pack.py
-new file mode 100644
-index 0000000000..f7128a4725
---- /dev/null
-+++ b/tools/grit/grit/format/data_pack.py
-@@ -0,0 +1,321 @@
-+#!/usr/bin/env python
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+"""Support for formatting a data pack file used for platform agnostic resource
-+files.
-+"""
-+
-+from __future__ import print_function
-+
-+import collections
-+import os
-+import struct
-+import sys
-+if __name__ == '__main__':
-+ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
-+
-+import six
-+
-+from grit import util
-+from grit.node import include
-+from grit.node import message
-+from grit.node import structure
-+
-+
-+PACK_FILE_VERSION = 5
-+BINARY, UTF8, UTF16 = range(3)
-+
-+
-+GrdInfoItem = collections.namedtuple('GrdInfoItem',
-+ ['textual_id', 'id', 'path'])
-+
-+
-+class WrongFileVersion(Exception):
-+ pass
-+
-+
-+class CorruptDataPack(Exception):
-+ pass
-+
-+
-+class DataPackSizes(object):
-+ def __init__(self, header, id_table, alias_table, data):
-+ self.header = header
-+ self.id_table = id_table
-+ self.alias_table = alias_table
-+ self.data = data
-+
-+ @property
-+ def total(self):
-+ return sum(v for v in self.__dict__.values())
-+
-+ def __iter__(self):
-+ yield ('header', self.header)
-+ yield ('id_table', self.id_table)
-+ yield ('alias_table', self.alias_table)
-+ yield ('data', self.data)
-+
-+ def __eq__(self, other):
-+ return self.__dict__ == other.__dict__
-+
-+ def __repr__(self):
-+ return self.__class__.__name__ + repr(self.__dict__)
-+
-+
-+class DataPackContents(object):
-+ def __init__(self, resources, encoding, version, aliases, sizes):
-+ # Map of resource_id -> str.
-+ self.resources = resources
-+ # Encoding (int).
-+ self.encoding = encoding
-+ # Version (int).
-+ self.version = version
-+ # Map of resource_id->canonical_resource_id
-+ self.aliases = aliases
-+ # DataPackSizes instance.
-+ self.sizes = sizes
-+
-+
-+def Format(root, lang='en', output_dir='.'):
-+ """Writes out the data pack file format (platform agnostic resource file)."""
-+ id_map = root.GetIdMap()
-+ data = {}
-+ root.info = []
-+ for node in root.ActiveDescendants():
-+ with node:
-+ if isinstance(node, (include.IncludeNode, message.MessageNode,
-+ structure.StructureNode)):
-+ value = node.GetDataPackValue(lang, util.BINARY)
-+ if value is not None:
-+ resource_id = id_map[node.GetTextualIds()[0]]
-+ data[resource_id] = value
-+ root.info.append('{},{},{}'.format(
-+ node.attrs.get('name'), resource_id, node.source))
-+ return WriteDataPackToString(data, UTF8)
-+
-+
-+def ReadDataPack(input_file):
-+ return ReadDataPackFromString(util.ReadFile(input_file, util.BINARY))
-+
-+
-+def ReadDataPackFromString(data):
-+ """Reads a data pack file and returns a dictionary."""
-+ # Read the header.
-+ version = struct.unpack('<I', data[:4])[0]
-+ if version == 4:
-+ resource_count, encoding = struct.unpack('<IB', data[4:9])
-+ alias_count = 0
-+ header_size = 9
-+ elif version == 5:
-+ encoding, resource_count, alias_count = struct.unpack('<BxxxHH', data[4:12])
-+ header_size = 12
-+ else:
-+ raise WrongFileVersion('Found version: ' + str(version))
-+
-+ resources = {}
-+ kIndexEntrySize = 2 + 4 # Each entry is a uint16 and a uint32.
-+ def entry_at_index(idx):
-+ offset = header_size + idx * kIndexEntrySize
-+ return struct.unpack('<HI', data[offset:offset + kIndexEntrySize])
-+
-+ prev_resource_id, prev_offset = entry_at_index(0)
-+ for i in range(1, resource_count + 1):
-+ resource_id, offset = entry_at_index(i)
-+ resources[prev_resource_id] = data[prev_offset:offset]
-+ prev_resource_id, prev_offset = resource_id, offset
-+
-+ id_table_size = (resource_count + 1) * kIndexEntrySize
-+ # Read the alias table.
-+ kAliasEntrySize = 2 + 2 # uint16, uint16
-+ def alias_at_index(idx):
-+ offset = header_size + id_table_size + idx * kAliasEntrySize
-+ return struct.unpack('<HH', data[offset:offset + kAliasEntrySize])
-+
-+ aliases = {}
-+ for i in range(alias_count):
-+ resource_id, index = alias_at_index(i)
-+ aliased_id = entry_at_index(index)[0]
-+ aliases[resource_id] = aliased_id
-+ resources[resource_id] = resources[aliased_id]
-+
-+ alias_table_size = kAliasEntrySize * alias_count
-+ sizes = DataPackSizes(
-+ header_size, id_table_size, alias_table_size,
-+ len(data) - header_size - id_table_size - alias_table_size)
-+ assert sizes.total == len(data), 'original={} computed={}'.format(
-+ len(data), sizes.total)
-+ return DataPackContents(resources, encoding, version, aliases, sizes)
-+
-+
-+def WriteDataPackToString(resources, encoding):
-+ """Returns bytes with a map of id=>data in the data pack format."""
-+ ret = []
-+
-+ # Compute alias map.
-+ resource_ids = sorted(resources)
-+ # Use reversed() so that for duplicates lower IDs clobber higher ones.
-+ id_by_data = {resources[k]: k for k in reversed(resource_ids)}
-+ # Map of resource_id -> resource_id, where value < key.
-+ alias_map = {k: id_by_data[v] for k, v in resources.items()
-+ if id_by_data[v] != k}
-+
-+ # Write file header.
-+ resource_count = len(resources) - len(alias_map)
-+ # Padding bytes added for alignment.
-+ ret.append(struct.pack('<IBxxxHH', PACK_FILE_VERSION, encoding,
-+ resource_count, len(alias_map)))
-+ HEADER_LENGTH = 4 + 4 + 2 + 2
-+
-+ # Each main table entry is: uint16 + uint32 (and an extra entry at the end).
-+ # Each alias table entry is: uint16 + uint16.
-+ data_offset = HEADER_LENGTH + (resource_count + 1) * 6 + len(alias_map) * 4
-+
-+ # Write main table.
-+ index_by_id = {}
-+ deduped_data = []
-+ index = 0
-+ for resource_id in resource_ids:
-+ if resource_id in alias_map:
-+ continue
-+ data = resources[resource_id]
-+ if isinstance(data, six.text_type):
-+ data = data.encode('utf-8')
-+ index_by_id[resource_id] = index
-+ ret.append(struct.pack('<HI', resource_id, data_offset))
-+ data_offset += len(data)
-+ deduped_data.append(data)
-+ index += 1
-+
-+ assert index == resource_count
-+ # Add an extra entry at the end.
-+ ret.append(struct.pack('<HI', 0, data_offset))
-+
-+ # Write alias table.
-+ for resource_id in sorted(alias_map):
-+ index = index_by_id[alias_map[resource_id]]
-+ ret.append(struct.pack('<HH', resource_id, index))
-+
-+ # Write data.
-+ ret.extend(deduped_data)
-+ return b''.join(ret)
-+
-+
-+def WriteDataPack(resources, output_file, encoding):
-+ """Writes a map of id=>data into output_file as a data pack."""
-+ content = WriteDataPackToString(resources, encoding)
-+ with open(output_file, 'wb') as file:
-+ file.write(content)
-+
-+
-+def ReadGrdInfo(grd_file):
-+ info_dict = {}
-+ with open(grd_file + '.info', 'rt') as f:
-+ for line in f:
-+ item = GrdInfoItem._make(line.strip().split(','))
-+ info_dict[int(item.id)] = item
-+ return info_dict
-+
-+
-+def RePack(output_file, input_files, whitelist_file=None,
-+ suppress_removed_key_output=False,
-+ output_info_filepath=None):
-+ """Write a new data pack file by combining input pack files.
-+
-+ Args:
-+ output_file: path to the new data pack file.
-+ input_files: a list of paths to the data pack files to combine.
-+ whitelist_file: path to the file that contains the list of resource IDs
-+ that should be kept in the output file or None to include
-+ all resources.
-+ suppress_removed_key_output: allows the caller to suppress the output from
-+ RePackFromDataPackStrings.
-+ output_info_file: If not None, specify the output .info filepath.
-+
-+ Raises:
-+ KeyError: if there are duplicate keys or resource encoding is
-+ inconsistent.
-+ """
-+ input_data_packs = [ReadDataPack(filename) for filename in input_files]
-+ input_info_files = [filename + '.info' for filename in input_files]
-+ whitelist = None
-+ if whitelist_file:
-+ lines = util.ReadFile(whitelist_file, 'utf-8').strip().splitlines()
-+ if not lines:
-+ raise Exception('Whitelist file should not be empty')
-+ whitelist = set(int(x) for x in lines)
-+ inputs = [(p.resources, p.encoding) for p in input_data_packs]
-+ resources, encoding = RePackFromDataPackStrings(
-+ inputs, whitelist, suppress_removed_key_output)
-+ WriteDataPack(resources, output_file, encoding)
-+ if output_info_filepath is None:
-+ output_info_filepath = output_file + '.info'
-+ with open(output_info_filepath, 'w') as output_info_file:
-+ for filename in input_info_files:
-+ with open(filename, 'r') as info_file:
-+ output_info_file.writelines(info_file.readlines())
-+
-+
-+def RePackFromDataPackStrings(inputs, whitelist,
-+ suppress_removed_key_output=False):
-+ """Combines all inputs into one.
-+
-+ Args:
-+ inputs: a list of (resources_by_id, encoding) tuples to be combined.
-+ whitelist: a list of resource IDs that should be kept in the output string
-+ or None to include all resources.
-+ suppress_removed_key_output: Do not print removed keys.
-+
-+ Returns:
-+ Returns (resources_by_id, encoding).
-+
-+ Raises:
-+ KeyError: if there are duplicate keys or resource encoding is
-+ inconsistent.
-+ """
-+ resources = {}
-+ encoding = None
-+ for input_resources, input_encoding in inputs:
-+ # Make sure we have no dups.
-+ duplicate_keys = set(input_resources.keys()) & set(resources.keys())
-+ if duplicate_keys:
-+ raise KeyError('Duplicate keys: ' + str(list(duplicate_keys)))
-+
-+ # Make sure encoding is consistent.
-+ if encoding in (None, BINARY):
-+ encoding = input_encoding
-+ elif input_encoding not in (BINARY, encoding):
-+ raise KeyError('Inconsistent encodings: ' + str(encoding) +
-+ ' vs ' + str(input_encoding))
-+
-+ if whitelist:
-+ whitelisted_resources = dict([(key, input_resources[key])
-+ for key in input_resources.keys()
-+ if key in whitelist])
-+ resources.update(whitelisted_resources)
-+ removed_keys = [key for key in input_resources.keys()
-+ if key not in whitelist]
-+ if not suppress_removed_key_output:
-+ for key in removed_keys:
-+ print('RePackFromDataPackStrings Removed Key:', key)
-+ else:
-+ resources.update(input_resources)
-+
-+ # Encoding is 0 for BINARY, 1 for UTF8 and 2 for UTF16
-+ if encoding is None:
-+ encoding = BINARY
-+ return resources, encoding
-+
-+
-+def main():
-+ # Write a simple file.
-+ data = {1: '', 4: 'this is id 4', 6: 'this is id 6', 10: ''}
-+ WriteDataPack(data, 'datapack1.pak', UTF8)
-+ data2 = {1000: 'test', 5: 'five'}
-+ WriteDataPack(data2, 'datapack2.pak', UTF8)
-+ print('wrote datapack1 and datapack2 to current directory.')
-+
-+
-+if __name__ == '__main__':
-+ main()
-diff --git a/tools/grit/grit/format/data_pack_unittest.py b/tools/grit/grit/format/data_pack_unittest.py
-new file mode 100644
-index 0000000000..fcd7035473
---- /dev/null
-+++ b/tools/grit/grit/format/data_pack_unittest.py
-@@ -0,0 +1,102 @@
-+#!/usr/bin/env python
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Unit tests for grit.format.data_pack'''
-+
-+from __future__ import print_function
-+
-+import os
-+import sys
-+if __name__ == '__main__':
-+ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
-+
-+import unittest
-+
-+from grit.format import data_pack
-+
-+
-+class FormatDataPackUnittest(unittest.TestCase):
-+ def testReadDataPackV4(self):
-+ expected_data = (
-+ b'\x04\x00\x00\x00' # header(version
-+ b'\x04\x00\x00\x00' # no. entries,
-+ b'\x01' # encoding)
-+ b'\x01\x00\x27\x00\x00\x00' # index entry 1
-+ b'\x04\x00\x27\x00\x00\x00' # index entry 4
-+ b'\x06\x00\x33\x00\x00\x00' # index entry 6
-+ b'\x0a\x00\x3f\x00\x00\x00' # index entry 10
-+ b'\x00\x00\x3f\x00\x00\x00' # extra entry for the size of last
-+ b'this is id 4this is id 6') # data
-+ expected_data_pack = data_pack.DataPackContents(
-+ {
-+ 1: b'',
-+ 4: b'this is id 4',
-+ 6: b'this is id 6',
-+ 10: b'',
-+ }, data_pack.UTF8, 4, {}, data_pack.DataPackSizes(9, 30, 0, 24))
-+ loaded = data_pack.ReadDataPackFromString(expected_data)
-+ self.assertDictEqual(expected_data_pack.__dict__, loaded.__dict__)
-+
-+ def testReadWriteDataPackV5(self):
-+ expected_data = (
-+ b'\x05\x00\x00\x00' # version
-+ b'\x01\x00\x00\x00' # encoding & padding
-+ b'\x03\x00' # resource_count
-+ b'\x01\x00' # alias_count
-+ b'\x01\x00\x28\x00\x00\x00' # index entry 1
-+ b'\x04\x00\x28\x00\x00\x00' # index entry 4
-+ b'\x06\x00\x34\x00\x00\x00' # index entry 6
-+ b'\x00\x00\x40\x00\x00\x00' # extra entry for the size of last
-+ b'\x0a\x00\x01\x00' # alias table
-+ b'this is id 4this is id 6') # data
-+ input_resources = {
-+ 1: b'',
-+ 4: b'this is id 4',
-+ 6: b'this is id 6',
-+ 10: b'this is id 4',
-+ }
-+ data = data_pack.WriteDataPackToString(input_resources, data_pack.UTF8)
-+ self.assertEquals(data, expected_data)
-+
-+ expected_data_pack = data_pack.DataPackContents({
-+ 1: b'',
-+ 4: input_resources[4],
-+ 6: input_resources[6],
-+ 10: input_resources[4],
-+ }, data_pack.UTF8, 5, {10: 4}, data_pack.DataPackSizes(12, 24, 4, 24))
-+ loaded = data_pack.ReadDataPackFromString(expected_data)
-+ self.assertDictEqual(expected_data_pack.__dict__, loaded.__dict__)
-+
-+ def testRePackUnittest(self):
-+ expected_with_whitelist = {
-+ 1: 'Never gonna', 10: 'give you up', 20: 'Never gonna let',
-+ 30: 'you down', 40: 'Never', 50: 'gonna run around and',
-+ 60: 'desert you'}
-+ expected_without_whitelist = {
-+ 1: 'Never gonna', 10: 'give you up', 20: 'Never gonna let', 65: 'Close',
-+ 30: 'you down', 40: 'Never', 50: 'gonna run around and', 4: 'click',
-+ 60: 'desert you', 6: 'chirr', 32: 'oops, try again', 70: 'Awww, snap!'}
-+ inputs = [{1: 'Never gonna', 4: 'click', 6: 'chirr', 10: 'give you up'},
-+ {20: 'Never gonna let', 30: 'you down', 32: 'oops, try again'},
-+ {40: 'Never', 50: 'gonna run around and', 60: 'desert you'},
-+ {65: 'Close', 70: 'Awww, snap!'}]
-+ whitelist = [1, 10, 20, 30, 40, 50, 60]
-+ inputs = [(i, data_pack.UTF8) for i in inputs]
-+
-+ # RePack using whitelist
-+ output, _ = data_pack.RePackFromDataPackStrings(
-+ inputs, whitelist, suppress_removed_key_output=True)
-+ self.assertDictEqual(expected_with_whitelist, output,
-+ 'Incorrect resource output')
-+
-+ # RePack a None whitelist
-+ output, _ = data_pack.RePackFromDataPackStrings(
-+ inputs, None, suppress_removed_key_output=True)
-+ self.assertDictEqual(expected_without_whitelist, output,
-+ 'Incorrect resource output')
-+
-+
-+if __name__ == '__main__':
-+ unittest.main()
-diff --git a/tools/grit/grit/format/gen_predetermined_ids.py b/tools/grit/grit/format/gen_predetermined_ids.py
-new file mode 100644
-index 0000000000..9b2aa7b1a5
---- /dev/null
-+++ b/tools/grit/grit/format/gen_predetermined_ids.py
-@@ -0,0 +1,144 @@
-+#!/usr/bin/env python
-+# Copyright 2017 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+"""
-+A tool to generate a predetermined resource ids file that can be used as an
-+input to grit via the -p option. This is meant to be run manually every once in
-+a while and its output checked in. See tools/gritsettings/README.md for details.
-+"""
-+
-+from __future__ import print_function
-+
-+import os
-+import re
-+import sys
-+
-+# Regular expression for parsing the #define macro format. Matches both the
-+# version of the macro with whitelist support and the one without. For example,
-+# Without generate whitelist flag:
-+# #define IDS_FOO_MESSAGE 1234
-+# With generate whitelist flag:
-+# #define IDS_FOO_MESSAGE (::ui::WhitelistedResource<1234>(), 1234)
-+RESOURCE_EXTRACT_REGEX = re.compile(r'^#define (\S*).* (\d+)\)?$', re.MULTILINE)
-+
-+ORDERED_RESOURCE_IDS_REGEX = re.compile(r'^Resource=(\d*)$', re.MULTILINE)
-+
-+
-+def _GetResourceNameIdPairsIter(string_to_scan):
-+ """Gets an iterator of the resource name and id pairs of the given string.
-+
-+ Scans the input string for lines of the form "#define NAME ID" and returns
-+ an iterator over all matching (NAME, ID) pairs.
-+
-+ Args:
-+ string_to_scan: The input string to scan.
-+
-+ Yields:
-+ A tuple of name and id.
-+ """
-+ for match in RESOURCE_EXTRACT_REGEX.finditer(string_to_scan):
-+ yield match.group(1, 2)
-+
-+
-+def _ReadOrderedResourceIds(path):
-+ """Reads ordered resource ids from the given file.
-+
-+ The resources are expected to be of the format produced by running Chrome
-+ with --print-resource-ids command line.
-+
-+ Args:
-+ path: File path to read resource ids from.
-+
-+ Returns:
-+ An array of ordered resource ids.
-+ """
-+ ordered_resource_ids = []
-+ with open(path, "r") as f:
-+ for match in ORDERED_RESOURCE_IDS_REGEX.finditer(f.read()):
-+ ordered_resource_ids.append(int(match.group(1)))
-+ return ordered_resource_ids
-+
-+
-+def GenerateResourceMapping(original_resources, ordered_resource_ids):
-+ """Generates a resource mapping from the ordered ids and the original mapping.
-+
-+ The returned dict will assign new ids to ordered_resource_ids numerically
-+ increasing from 101.
-+
-+ Args:
-+ original_resources: A dict of original resource ids to resource names.
-+ ordered_resource_ids: An array of ordered resource ids.
-+
-+ Returns:
-+ A dict of resource ids to resource names.
-+ """
-+ output_resource_map = {}
-+ # 101 is used as the starting value since other parts of GRIT require it to be
-+ # the minimum (e.g. rc_header.py) based on Windows resource numbering.
-+ next_id = 101
-+ for original_id in ordered_resource_ids:
-+ resource_name = original_resources[original_id]
-+ output_resource_map[next_id] = resource_name
-+ next_id += 1
-+ return output_resource_map
-+
-+
-+def ReadResourceIdsFromFile(file, original_resources):
-+ """Reads resource ids from a GRIT-produced header file.
-+
-+ Args:
-+ file: File to a GRIT-produced header file to read from.
-+ original_resources: Dict of resource ids to resource names to add to.
-+ """
-+ for resource_name, resource_id in _GetResourceNameIdPairsIter(file.read()):
-+ original_resources[int(resource_id)] = resource_name
-+
-+
-+def _ReadOriginalResourceIds(out_dir):
-+ """Reads resource ids from GRIT header files in the specified directory.
-+
-+ Args:
-+ out_dir: A Chrome build output directory (e.g. out/gn) to scan.
-+
-+ Returns:
-+ A dict of resource ids to resource names.
-+ """
-+ original_resources = {}
-+ for root, dirnames, filenames in os.walk(out_dir + '/gen'):
-+ for filename in filenames:
-+ if filename.endswith(('_resources.h', '_settings.h', '_strings.h')):
-+ with open(os.path.join(root, filename), "r") as f:
-+ ReadResourceIdsFromFile(f, original_resources)
-+ return original_resources
-+
-+
-+def _GeneratePredeterminedIdsFile(ordered_resources_file, out_dir):
-+ """Generates a predetermined ids file.
-+
-+ Args:
-+ ordered_resources_file: File path to read ordered resource ids from.
-+ out_dir: A Chrome build output directory (e.g. out/gn) to scan.
-+
-+ Returns:
-+ A dict of resource ids to resource names.
-+ """
-+ original_resources = _ReadOriginalResourceIds(out_dir)
-+ ordered_resource_ids = _ReadOrderedResourceIds(ordered_resources_file)
-+ output_resource_map = GenerateResourceMapping(original_resources,
-+ ordered_resource_ids)
-+ for res_id in sorted(output_resource_map.keys()):
-+ print(output_resource_map[res_id], res_id)
-+
-+
-+def main(argv):
-+ if len(argv) != 2:
-+ print("usage: gen_predetermined_ids.py <ordered_resources_file> <out_dir>")
-+ sys.exit(1)
-+ ordered_resources_file, out_dir = argv[0], argv[1]
-+ _GeneratePredeterminedIdsFile(ordered_resources_file, out_dir)
-+
-+
-+if '__main__' == __name__:
-+ main(sys.argv[1:])
-diff --git a/tools/grit/grit/format/gen_predetermined_ids_unittest.py b/tools/grit/grit/format/gen_predetermined_ids_unittest.py
-new file mode 100644
-index 0000000000..bd0331adb4
---- /dev/null
-+++ b/tools/grit/grit/format/gen_predetermined_ids_unittest.py
-@@ -0,0 +1,46 @@
-+#!/usr/bin/env python
-+# Copyright 2017 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Unit tests for the gen_predetermined_ids module.'''
-+
-+from __future__ import print_function
-+
-+import os
-+import sys
-+if __name__ == '__main__':
-+ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
-+
-+import unittest
-+
-+from six import StringIO
-+
-+from grit.format import gen_predetermined_ids
-+
-+class GenPredeterminedIdsUnittest(unittest.TestCase):
-+ def testGenerateResourceMapping(self):
-+ original_resources = {200: 'A', 201: 'B', 300: 'C', 350: 'D', 370: 'E'}
-+ ordered_resource_ids = [300, 201, 370]
-+ mapping = gen_predetermined_ids.GenerateResourceMapping(
-+ original_resources, ordered_resource_ids)
-+ self.assertEqual({101: 'C', 102: 'B', 103: 'E'}, mapping)
-+
-+ def testReadResourceIdsFromFile(self):
-+ f = StringIO('''
-+// This file is automatically generated by GRIT. Do not edit.
-+
-+#pragma once
-+
-+#define IDS_BOOKMARKS_NO_ITEMS 12500
-+#define IDS_BOOKMARK_BAR_IMPORT_LINK (::ui::WhitelistedResource<12501>(), 12501)
-+#define IDS_BOOKMARK_X (::ui::WhitelistedResource<12502>(), 12502)
-+''')
-+ resources = {}
-+ gen_predetermined_ids.ReadResourceIdsFromFile(f, resources)
-+ self.assertEqual({12500: 'IDS_BOOKMARKS_OPEN_ALL',
-+ 12501: 'IDS_BOOKMARKS_OPEN_ALL_INCOGNITO',
-+ 12502: 'IDS_BOOKMARK_X'}, resources)
-+
-+if __name__ == '__main__':
-+ unittest.main()
-diff --git a/tools/grit/grit/format/gzip_string.py b/tools/grit/grit/format/gzip_string.py
-new file mode 100644
-index 0000000000..3cd17185c9
---- /dev/null
-+++ b/tools/grit/grit/format/gzip_string.py
-@@ -0,0 +1,46 @@
-+# Copyright (c) 2016 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+"""Provides gzip utilities for strings.
-+"""
-+
-+from __future__ import print_function
-+
-+import gzip
-+import io
-+import subprocess
-+
-+
-+def GzipStringRsyncable(data):
-+ # Make call to host system's gzip to get access to --rsyncable option. This
-+ # option makes updates much smaller - if one line is changed in the resource,
-+ # it won't have to push the entire compressed resource with the update.
-+ # Instead, --rsyncable breaks the file into small chunks, so that one doesn't
-+ # affect the other in compression, and then only that chunk will have to be
-+ # updated.
-+ gzip_proc = subprocess.Popen(['gzip', '--stdout', '--rsyncable',
-+ '--best', '--no-name'],
-+ stdin=subprocess.PIPE,
-+ stdout=subprocess.PIPE,
-+ stderr=subprocess.PIPE)
-+ data, stderr = gzip_proc.communicate(data)
-+ if gzip_proc.returncode != 0:
-+ raise subprocess.CalledProcessError(gzip_proc.returncode, 'gzip',
-+ stderr)
-+ return data
-+
-+
-+def GzipString(data):
-+ # Gzipping using Python's built in gzip: Windows doesn't ship with gzip, and
-+ # OSX's gzip does not have an --rsyncable option built in. Although this is
-+ # not preferable to --rsyncable, it is an option for the systems that do
-+ # not have --rsyncable. If used over GzipStringRsyncable, the primary
-+ # difference of this function's compression will be larger updates every time
-+ # a compressed resource is changed.
-+ gzip_output = io.BytesIO()
-+ with gzip.GzipFile(mode='wb', compresslevel=9, fileobj=gzip_output,
-+ mtime=0) as gzip_file:
-+ gzip_file.write(data)
-+ data = gzip_output.getvalue()
-+ gzip_output.close()
-+ return data
-diff --git a/tools/grit/grit/format/gzip_string_unittest.py b/tools/grit/grit/format/gzip_string_unittest.py
-new file mode 100644
-index 0000000000..c0cfbe1837
---- /dev/null
-+++ b/tools/grit/grit/format/gzip_string_unittest.py
-@@ -0,0 +1,65 @@
-+#!/usr/bin/env python
-+# Copyright (c) 2016 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Unit tests for grit.format.gzip_string'''
-+
-+from __future__ import print_function
-+
-+import gzip
-+import io
-+import os
-+import sys
-+if __name__ == '__main__':
-+ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
-+
-+import unittest
-+
-+from grit.format import gzip_string
-+
-+
-+class FormatGzipStringUnittest(unittest.TestCase):
-+
-+ def testGzipStringRsyncable(self):
-+ # Can only test the rsyncable version on platforms which support rsyncable,
-+ # which at the moment is Linux.
-+ if sys.platform == 'linux2':
-+ header_begin = (b'\x1f\x8b') # gzip first two bytes
-+ input = (b'TEST STRING STARTING NOW'
-+ b'continuing'
-+ b'<even more>'
-+ b'<finished NOW>')
-+
-+ compressed = gzip_string.GzipStringRsyncable(input)
-+ self.failUnless(header_begin == compressed[:2])
-+
-+ compressed_file = io.BytesIO()
-+ compressed_file.write(compressed)
-+ compressed_file.seek(0)
-+
-+ with gzip.GzipFile(mode='rb', fileobj=compressed_file) as f:
-+ output = f.read()
-+ self.failUnless(output == input)
-+
-+ def testGzipString(self):
-+ header_begin = b'\x1f\x8b' # gzip first two bytes
-+ input = (b'TEST STRING STARTING NOW'
-+ b'continuing'
-+ b'<even more>'
-+ b'<finished NOW>')
-+
-+ compressed = gzip_string.GzipString(input)
-+ self.failUnless(header_begin == compressed[:2])
-+
-+ compressed_file = io.BytesIO()
-+ compressed_file.write(compressed)
-+ compressed_file.seek(0)
-+
-+ with gzip.GzipFile(mode='rb', fileobj=compressed_file) as f:
-+ output = f.read()
-+ self.failUnless(output == input)
-+
-+
-+if __name__ == '__main__':
-+ unittest.main()
-diff --git a/tools/grit/grit/format/html_inline.py b/tools/grit/grit/format/html_inline.py
-new file mode 100644
-index 0000000000..da55216ea4
---- /dev/null
-+++ b/tools/grit/grit/format/html_inline.py
-@@ -0,0 +1,602 @@
-+#!/usr/bin/env python
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+"""Flattens a HTML file by inlining its external resources.
-+
-+This is a small script that takes a HTML file, looks for src attributes
-+and inlines the specified file, producing one HTML file with no external
-+dependencies. It recursively inlines the included files.
-+"""
-+
-+from __future__ import print_function
-+
-+import os
-+import re
-+import sys
-+import base64
-+import mimetypes
-+
-+from grit import lazy_re
-+from grit import util
-+from grit.format import minifier
-+
-+# There is a python bug that makes mimetypes crash if the Windows
-+# registry contains non-Latin keys ( http://bugs.python.org/issue9291
-+# ). Initing manually and blocking external mime-type databases will
-+# prevent that bug and if we add svg manually, it will still give us
-+# the data we need.
-+mimetypes.init([])
-+mimetypes.add_type('image/svg+xml', '.svg')
-+
-+# webm video type is not always available if mimetype package is outdated.
-+mimetypes.add_type('video/webm', '.webm')
-+
-+DIST_DEFAULT = 'chromium'
-+DIST_ENV_VAR = 'CHROMIUM_BUILD'
-+DIST_SUBSTR = '%DISTRIBUTION%'
-+
-+# Matches beginning of an "if" block.
-+_BEGIN_IF_BLOCK = lazy_re.compile(
-+ r'<if [^>]*?expr=("(?P<expr1>[^">]*)"|\'(?P<expr2>[^\'>]*)\')[^>]*?>')
-+
-+# Matches ending of an "if" block.
-+_END_IF_BLOCK = lazy_re.compile(r'</if>')
-+
-+# Used by DoInline to replace various links with inline content.
-+_STYLESHEET_RE = lazy_re.compile(
-+ r'<link rel="stylesheet"[^>]+?href="(?P<filename>[^"]*)".*?>(\s*</link>)?',
-+ re.DOTALL)
-+_INCLUDE_RE = lazy_re.compile(
-+ r'(?P<comment>\/\/ )?<include[^>]+?'
-+ r'src=("(?P<file1>[^">]*)"|\'(?P<file2>[^\'>]*)\').*?>(\s*</include>)?',
-+ re.DOTALL)
-+_SRC_RE = lazy_re.compile(
-+ r'<(?!script)(?:[^>]+?\s)src="(?!\[\[|{{)(?P<filename>[^"\']*)"',
-+ re.MULTILINE)
-+# This re matches '<img srcset="..."' or '<source srcset="..."'
-+_SRCSET_RE = lazy_re.compile(
-+ r'<(img|source)\b(?:[^>]*?\s)srcset="(?!\[\[|{{|\$i18n{)'
-+ r'(?P<srcset>[^"\']*)"',
-+ re.MULTILINE)
-+# This re is for splitting srcset value string into "image candidate strings".
-+# Notes:
-+# - HTML 5.2 states that URL cannot start or end with comma.
-+# - the "descriptor" is either "width descriptor" or "pixel density descriptor".
-+# The first one consists of "valid non-negative integer + letter 'x'",
-+# the second one is formed of "positive valid floating-point number +
-+# letter 'w'". As a reasonable compromise, we match a list of characters
-+# that form both of them.
-+# Matches for example "img2.png 2x" or "img9.png 11E-2w".
-+_SRCSET_ENTRY_RE = lazy_re.compile(
-+ r'\s*(?P<url>[^,\s]\S+[^,\s])'
-+ r'(?:\s+(?P<descriptor>[\deE.-]+[wx]))?\s*'
-+ r'(?P<separator>,|$)',
-+ re.MULTILINE)
-+_ICON_RE = lazy_re.compile(
-+ r'<link rel="icon"\s(?:[^>]+?\s)?'
-+ r'href=(?P<quote>")(?P<filename>[^"\']*)\1',
-+ re.MULTILINE)
-+
-+
-+def GetDistribution():
-+ """Helper function that gets the distribution we are building.
-+
-+ Returns:
-+ string
-+ """
-+ distribution = DIST_DEFAULT
-+ if DIST_ENV_VAR in os.environ:
-+ distribution = os.environ[DIST_ENV_VAR]
-+ if len(distribution) > 1 and distribution[0] == '_':
-+ distribution = distribution[1:].lower()
-+ return distribution
-+
-+def ConvertFileToDataURL(filename, base_path, distribution, inlined_files,
-+ names_only):
-+ """Convert filename to inlined data URI.
-+
-+ Takes a filename from ether "src" or "srcset", and attempts to read the file
-+ at 'filename'. Returns data URI as string with given file inlined.
-+ If it finds DIST_SUBSTR string in file name, replaces it with distribution.
-+ If filename contains ':', it is considered URL and not translated.
-+
-+ Args:
-+ filename: filename string from ether src or srcset attributes.
-+ base_path: path that to look for files in
-+ distribution: string that should replace DIST_SUBSTR
-+ inlined_files: The name of the opened file is appended to this list.
-+ names_only: If true, the function will not read the file but just return "".
-+ It will still add the filename to |inlined_files|.
-+
-+ Returns:
-+ string
-+ """
-+ if filename.find(':') != -1:
-+ # filename is probably a URL, which we don't want to bother inlining
-+ return filename
-+
-+ filename = filename.replace(DIST_SUBSTR , distribution)
-+ filepath = os.path.normpath(os.path.join(base_path, filename))
-+ inlined_files.add(filepath)
-+
-+ if names_only:
-+ return ""
-+
-+ mimetype = mimetypes.guess_type(filename)[0]
-+ if mimetype is None:
-+ raise Exception('%s is of an an unknown type and '
-+ 'cannot be stored in a data url.' % filename)
-+ inline_data = base64.standard_b64encode(util.ReadFile(filepath, util.BINARY))
-+ return 'data:%s;base64,%s' % (mimetype, inline_data.decode('utf-8'))
-+
-+
-+def SrcInlineAsDataURL(
-+ src_match, base_path, distribution, inlined_files, names_only=False,
-+ filename_expansion_function=None):
-+ """regex replace function.
-+
-+ Takes a regex match for src="filename", attempts to read the file
-+ at 'filename' and returns the src attribute with the file inlined
-+ as a data URI. If it finds DIST_SUBSTR string in file name, replaces
-+ it with distribution.
-+
-+ Args:
-+ src_match: regex match object with 'filename' named capturing group
-+ base_path: path that to look for files in
-+ distribution: string that should replace DIST_SUBSTR
-+ inlined_files: The name of the opened file is appended to this list.
-+ names_only: If true, the function will not read the file but just return "".
-+ It will still add the filename to |inlined_files|.
-+
-+ Returns:
-+ string
-+ """
-+ filename = src_match.group('filename')
-+ if filename_expansion_function:
-+ filename = filename_expansion_function(filename)
-+
-+ data_url = ConvertFileToDataURL(filename, base_path, distribution,
-+ inlined_files, names_only)
-+
-+ if not data_url:
-+ return data_url
-+
-+ prefix = src_match.string[src_match.start():src_match.start('filename')]
-+ suffix = src_match.string[src_match.end('filename'):src_match.end()]
-+ return prefix + data_url + suffix
-+
-+def SrcsetInlineAsDataURL(
-+ srcset_match, base_path, distribution, inlined_files, names_only=False,
-+ filename_expansion_function=None):
-+ """regex replace function to inline files in srcset="..." attributes
-+
-+ Takes a regex match for srcset="filename 1x, filename 2x, ...", attempts to
-+ read the files referenced by filenames and returns the srcset attribute with
-+ the files inlined as a data URI. If it finds DIST_SUBSTR string in file name,
-+ replaces it with distribution.
-+
-+ Args:
-+ srcset_match: regex match object with 'srcset' named capturing group
-+ base_path: path that to look for files in
-+ distribution: string that should replace DIST_SUBSTR
-+ inlined_files: The name of the opened file is appended to this list.
-+ names_only: If true, the function will not read the file but just return "".
-+ It will still add the filename to |inlined_files|.
-+
-+ Returns:
-+ string
-+ """
-+ srcset = srcset_match.group('srcset')
-+
-+ if not srcset:
-+ return srcset_match.group(0)
-+
-+ # HTML 5.2 defines srcset as a list of "image candidate strings".
-+ # Each of them consists of URL and descriptor.
-+ # _SRCSET_ENTRY_RE splits srcset into a list of URLs, descriptors and
-+ # commas.
-+ # The descriptor part will be None if that optional regex didn't match
-+ parts = _SRCSET_ENTRY_RE.split(srcset)
-+
-+ if not parts:
-+ return srcset_match.group(0)
-+
-+ # List of image candidate strings that will form new srcset="..."
-+ new_candidates = []
-+
-+ # When iterating over split srcset we fill this parts of a single image
-+ # candidate string: [url, descriptor]
-+ candidate = [];
-+
-+ # Each entry should consist of some text before the entry, the url,
-+ # the descriptor or None if the entry has no descriptor, a comma separator or
-+ # the end of the line, and finally some text after the entry (which is the
-+ # same as the text before the next entry).
-+ for i in range(0, len(parts) - 1, 4):
-+ before, url, descriptor, separator, after = parts[i:i+5]
-+
-+ # There must be a comma-separated next entry or this must be the last entry.
-+ assert separator == "," or (separator == "" and i == len(parts) - 5), (
-+ "Bad srcset format in {}".format(srcset_match.group(0)))
-+ # Both before and after the entry must be empty
-+ assert before == after == "", (
-+ "Bad srcset format in {}".format(srcset_match.group(0)))
-+
-+ if filename_expansion_function:
-+ filename = filename_expansion_function(url)
-+ else:
-+ filename = url
-+
-+ data_url = ConvertFileToDataURL(filename, base_path, distribution,
-+ inlined_files, names_only)
-+
-+ # This is not "names_only" mode
-+ if data_url:
-+ candidate = [data_url]
-+ if descriptor:
-+ candidate.append(descriptor)
-+
-+ new_candidates.append(" ".join(candidate))
-+
-+ prefix = srcset_match.string[srcset_match.start():
-+ srcset_match.start('srcset')]
-+ suffix = srcset_match.string[srcset_match.end('srcset'):srcset_match.end()]
-+ return prefix + ','.join(new_candidates) + suffix
-+
-+class InlinedData:
-+ """Helper class holding the results from DoInline().
-+
-+ Holds the inlined data and the set of filenames of all the inlined
-+ files.
-+ """
-+ def __init__(self, inlined_data, inlined_files):
-+ self.inlined_data = inlined_data
-+ self.inlined_files = inlined_files
-+
-+def DoInline(
-+ input_filename, grd_node, allow_external_script=False,
-+ preprocess_only=False, names_only=False, strip_whitespace=False,
-+ rewrite_function=None, filename_expansion_function=None):
-+ """Helper function that inlines the resources in a specified file.
-+
-+ Reads input_filename, finds all the src attributes and attempts to
-+ inline the files they are referring to, then returns the result and
-+ the set of inlined files.
-+
-+ Args:
-+ input_filename: name of file to read in
-+ grd_node: html node from the grd file for this include tag
-+ preprocess_only: Skip all HTML processing, only handle <if> and <include>.
-+ names_only: |nil| will be returned for the inlined contents (faster).
-+ strip_whitespace: remove whitespace and comments in the input files.
-+ rewrite_function: function(filepath, text, distribution) which will be
-+ called to rewrite html content before inlining images.
-+ filename_expansion_function: function(filename) which will be called to
-+ rewrite filenames before attempting to read them.
-+ Returns:
-+ a tuple of the inlined data as a string and the set of filenames
-+ of all the inlined files
-+ """
-+ if filename_expansion_function:
-+ input_filename = filename_expansion_function(input_filename)
-+ input_filepath = os.path.dirname(input_filename)
-+ distribution = GetDistribution()
-+
-+ # Keep track of all the files we inline.
-+ inlined_files = set()
-+
-+ def SrcReplace(src_match, filepath=input_filepath,
-+ inlined_files=inlined_files):
-+ """Helper function to provide SrcInlineAsDataURL with the base file path"""
-+ return SrcInlineAsDataURL(
-+ src_match, filepath, distribution, inlined_files, names_only=names_only,
-+ filename_expansion_function=filename_expansion_function)
-+
-+ def SrcsetReplace(srcset_match, filepath=input_filepath,
-+ inlined_files=inlined_files):
-+ """Helper function to provide SrcsetInlineAsDataURL with the base file
-+ path.
-+ """
-+ return SrcsetInlineAsDataURL(
-+ srcset_match, filepath, distribution, inlined_files,
-+ names_only=names_only,
-+ filename_expansion_function=filename_expansion_function)
-+
-+ def GetFilepath(src_match, base_path = input_filepath):
-+ filename = [v for k, v in src_match.groupdict().items()
-+ if k.startswith('file') and v][0]
-+
-+ if filename.find(':') != -1:
-+ # filename is probably a URL, which we don't want to bother inlining
-+ return None
-+
-+ filename = filename.replace('%DISTRIBUTION%', distribution)
-+ if filename_expansion_function:
-+ filename = filename_expansion_function(filename)
-+ return os.path.normpath(os.path.join(base_path, filename))
-+
-+ def IsConditionSatisfied(src_match):
-+ expr1 = src_match.group('expr1') or ''
-+ expr2 = src_match.group('expr2') or ''
-+ return grd_node is None or grd_node.EvaluateCondition(expr1 + expr2)
-+
-+ def CheckConditionalElements(str):
-+ """Helper function to conditionally inline inner elements"""
-+ while True:
-+ begin_if = _BEGIN_IF_BLOCK.search(str)
-+ if begin_if is None:
-+ if _END_IF_BLOCK.search(str) is not None:
-+ raise Exception('Unmatched </if>')
-+ return str
-+
-+ condition_satisfied = IsConditionSatisfied(begin_if)
-+ leading = str[0:begin_if.start()]
-+ content_start = begin_if.end()
-+
-+ # Find matching "if" block end.
-+ count = 1
-+ pos = begin_if.end()
-+ while True:
-+ end_if = _END_IF_BLOCK.search(str, pos)
-+ if end_if is None:
-+ raise Exception('Unmatched <if>')
-+
-+ next_if = _BEGIN_IF_BLOCK.search(str, pos)
-+ if next_if is None or next_if.start() >= end_if.end():
-+ count = count - 1
-+ if count == 0:
-+ break
-+ pos = end_if.end()
-+ else:
-+ count = count + 1
-+ pos = next_if.end()
-+
-+ content = str[content_start:end_if.start()]
-+ trailing = str[end_if.end():]
-+
-+ if condition_satisfied:
-+ str = leading + CheckConditionalElements(content) + trailing
-+ else:
-+ str = leading + trailing
-+
-+ def InlineFileContents(src_match,
-+ pattern,
-+ inlined_files=inlined_files,
-+ strip_whitespace=False):
-+ """Helper function to inline external files of various types"""
-+ filepath = GetFilepath(src_match)
-+ if filepath is None:
-+ return src_match.group(0)
-+ inlined_files.add(filepath)
-+
-+ if names_only:
-+ inlined_files.update(GetResourceFilenames(
-+ filepath,
-+ grd_node,
-+ allow_external_script,
-+ rewrite_function,
-+ filename_expansion_function=filename_expansion_function))
-+ return ""
-+ # To recursively save inlined files, we need InlinedData instance returned
-+ # by DoInline.
-+ inlined_data_inst=DoInline(filepath, grd_node,
-+ allow_external_script=allow_external_script,
-+ preprocess_only=preprocess_only,
-+ strip_whitespace=strip_whitespace,
-+ filename_expansion_function=filename_expansion_function)
-+
-+ inlined_files.update(inlined_data_inst.inlined_files)
-+
-+ return pattern % inlined_data_inst.inlined_data;
-+
-+
-+ def InlineIncludeFiles(src_match):
-+ """Helper function to directly inline generic external files (without
-+ wrapping them with any kind of tags).
-+ """
-+ return InlineFileContents(src_match, '%s')
-+
-+ def InlineScript(match):
-+ """Helper function to inline external script files"""
-+ attrs = (match.group('attrs1') + match.group('attrs2')).strip()
-+ if attrs:
-+ attrs = ' ' + attrs
-+ return InlineFileContents(match, '<script' + attrs + '>%s</script>',
-+ strip_whitespace=True)
-+
-+ def InlineCSSText(text, css_filepath):
-+ """Helper function that inlines external resources in CSS text"""
-+ filepath = os.path.dirname(css_filepath)
-+ # Allow custom modifications before inlining images.
-+ if rewrite_function:
-+ text = rewrite_function(filepath, text, distribution)
-+ text = InlineCSSImages(text, filepath)
-+ return InlineCSSImports(text, filepath)
-+
-+ def InlineCSSFile(src_match, pattern, base_path=input_filepath):
-+ """Helper function to inline external CSS files.
-+
-+ Args:
-+ src_match: A regular expression match with a named group named "filename".
-+ pattern: The pattern to replace with the contents of the CSS file.
-+ base_path: The base path to use for resolving the CSS file.
-+
-+ Returns:
-+ The text that should replace the reference to the CSS file.
-+ """
-+ filepath = GetFilepath(src_match, base_path)
-+ if filepath is None:
-+ return src_match.group(0)
-+
-+ # Even if names_only is set, the CSS file needs to be opened, because it
-+ # can link to images that need to be added to the file set.
-+ inlined_files.add(filepath)
-+
-+ # Inline stylesheets included in this css file.
-+ text = _INCLUDE_RE.sub(InlineIncludeFiles, util.ReadFile(filepath, 'utf-8'))
-+ # When resolving CSS files we need to pass in the path so that relative URLs
-+ # can be resolved.
-+
-+ return pattern % InlineCSSText(text, filepath)
-+
-+ def GetUrlRegexString(postfix=''):
-+ """Helper function that returns a string for a regex that matches url('')
-+ but not url([[ ]]) or url({{ }}). Appends |postfix| to group names.
-+ """
-+ url_re = (r'url\((?!\[\[|{{)(?P<q%s>"|\'|)(?P<filename%s>[^"\'()]*)'
-+ r'(?P=q%s)\)')
-+ return url_re % (postfix, postfix, postfix)
-+
-+ def InlineCSSImages(text, filepath=input_filepath):
-+ """Helper function that inlines external images in CSS backgrounds."""
-+ # Replace contents of url() for css attributes: content, background,
-+ # or *-image.
-+ property_re = r'(content|background|[\w-]*-image):[^;]*'
-+ # Replace group names to prevent duplicates when forming value_re.
-+ image_set_value_re = (r'image-set\(([ ]*' + GetUrlRegexString('2') +
-+ r'[ ]*[0-9.]*x[ ]*(,[ ]*)?)+\)')
-+ value_re = '(%s|%s)' % (GetUrlRegexString(), image_set_value_re)
-+ css_re = property_re + value_re
-+ return re.sub(css_re, lambda m: InlineCSSUrls(m, filepath), text)
-+
-+ def InlineCSSUrls(src_match, filepath=input_filepath):
-+ """Helper function that inlines each url on a CSS image rule match."""
-+ # Replace contents of url() references in matches.
-+ return re.sub(GetUrlRegexString(),
-+ lambda m: SrcReplace(m, filepath),
-+ src_match.group(0))
-+
-+ def InlineCSSImports(text, filepath=input_filepath):
-+ """Helper function that inlines CSS files included via the @import
-+ directive.
-+ """
-+ return re.sub(r'@import\s+' + GetUrlRegexString() + r';',
-+ lambda m: InlineCSSFile(m, '%s', filepath),
-+ text)
-+
-+
-+ flat_text = util.ReadFile(input_filename, 'utf-8')
-+
-+ # Check conditional elements, remove unsatisfied ones from the file. We do
-+ # this twice. The first pass is so that we don't even bother calling
-+ # InlineScript, InlineCSSFile and InlineIncludeFiles on text we're eventually
-+ # going to throw out anyway.
-+ flat_text = CheckConditionalElements(flat_text)
-+
-+ flat_text = _INCLUDE_RE.sub(InlineIncludeFiles, flat_text)
-+
-+ if not preprocess_only:
-+ if strip_whitespace:
-+ flat_text = minifier.Minify(flat_text.encode('utf-8'),
-+ input_filename).decode('utf-8')
-+
-+ if not allow_external_script:
-+ # We need to inline css and js before we inline images so that image
-+ # references gets inlined in the css and js
-+ flat_text = re.sub(r'<script (?P<attrs1>.*?)src="(?P<filename>[^"\']*)"'
-+ r'(?P<attrs2>.*?)></script>',
-+ InlineScript,
-+ flat_text)
-+
-+ flat_text = _STYLESHEET_RE.sub(
-+ lambda m: InlineCSSFile(m, '<style>%s</style>'),
-+ flat_text)
-+
-+ # Check conditional elements, second pass. This catches conditionals in any
-+ # of the text we just inlined.
-+ flat_text = CheckConditionalElements(flat_text)
-+
-+ # Allow custom modifications before inlining images.
-+ if rewrite_function:
-+ flat_text = rewrite_function(input_filepath, flat_text, distribution)
-+
-+ if not preprocess_only:
-+ flat_text = _SRC_RE.sub(SrcReplace, flat_text)
-+ flat_text = _SRCSET_RE.sub(SrcsetReplace, flat_text)
-+
-+ # TODO(arv): Only do this inside <style> tags.
-+ flat_text = InlineCSSImages(flat_text)
-+
-+ flat_text = _ICON_RE.sub(SrcReplace, flat_text)
-+
-+ if names_only:
-+ flat_text = None # Will contains garbage if the flag is set anyway.
-+ return InlinedData(flat_text, inlined_files)
-+
-+
-+def InlineToString(input_filename, grd_node, preprocess_only = False,
-+ allow_external_script=False, strip_whitespace=False,
-+ rewrite_function=None, filename_expansion_function=None):
-+ """Inlines the resources in a specified file and returns it as a string.
-+
-+ Args:
-+ input_filename: name of file to read in
-+ grd_node: html node from the grd file for this include tag
-+ Returns:
-+ the inlined data as a string
-+ """
-+ try:
-+ return DoInline(
-+ input_filename,
-+ grd_node,
-+ preprocess_only=preprocess_only,
-+ allow_external_script=allow_external_script,
-+ strip_whitespace=strip_whitespace,
-+ rewrite_function=rewrite_function,
-+ filename_expansion_function=filename_expansion_function).inlined_data
-+ except IOError as e:
-+ raise Exception("Failed to open %s while trying to flatten %s. (%s)" %
-+ (e.filename, input_filename, e.strerror))
-+
-+
-+def InlineToFile(input_filename, output_filename, grd_node):
-+ """Inlines the resources in a specified file and writes it.
-+
-+ Reads input_filename, finds all the src attributes and attempts to
-+ inline the files they are referring to, then writes the result
-+ to output_filename.
-+
-+ Args:
-+ input_filename: name of file to read in
-+ output_filename: name of file to be written to
-+ grd_node: html node from the grd file for this include tag
-+ Returns:
-+ a set of filenames of all the inlined files
-+ """
-+ inlined_data = InlineToString(input_filename, grd_node)
-+ with open(output_filename, 'wb') as out_file:
-+ out_file.write(inlined_data)
-+
-+
-+def GetResourceFilenames(filename,
-+ grd_node,
-+ allow_external_script=False,
-+ rewrite_function=None,
-+ filename_expansion_function=None):
-+ """For a grd file, returns a set of all the files that would be inline."""
-+ try:
-+ return DoInline(
-+ filename,
-+ grd_node,
-+ names_only=True,
-+ preprocess_only=False,
-+ allow_external_script=allow_external_script,
-+ strip_whitespace=False,
-+ rewrite_function=rewrite_function,
-+ filename_expansion_function=filename_expansion_function).inlined_files
-+ except IOError as e:
-+ raise Exception("Failed to open %s while trying to flatten %s. (%s)" %
-+ (e.filename, filename, e.strerror))
-+
-+
-+def main():
-+ if len(sys.argv) <= 2:
-+ print("Flattens a HTML file by inlining its external resources.\n")
-+ print("html_inline.py inputfile outputfile")
-+ else:
-+ InlineToFile(sys.argv[1], sys.argv[2], None)
-+
-+if __name__ == '__main__':
-+ main()
-diff --git a/tools/grit/grit/format/html_inline_unittest.py b/tools/grit/grit/format/html_inline_unittest.py
-new file mode 100644
-index 0000000000..1b11e9e476
---- /dev/null
-+++ b/tools/grit/grit/format/html_inline_unittest.py
-@@ -0,0 +1,927 @@
-+#!/usr/bin/env python
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Unit tests for grit.format.html_inline'''
-+
-+from __future__ import print_function
-+
-+import os
-+import re
-+import sys
-+if __name__ == '__main__':
-+ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
-+
-+import unittest
-+
-+from grit import util
-+from grit.format import html_inline
-+
-+
-+class HtmlInlineUnittest(unittest.TestCase):
-+ '''Unit tests for HtmlInline.'''
-+
-+ def testGetResourceFilenames(self):
-+ '''Tests that all included files are returned by GetResourceFilenames.'''
-+
-+ files = {
-+ 'index.html': '''
-+ <!DOCTYPE HTML>
-+ <html>
-+ <head>
-+ <link rel="stylesheet" href="test.css">
-+ <link rel="stylesheet"
-+ href="really-long-long-long-long-long-test.css">
-+ </head>
-+ <body>
-+ <include src='test.html'>
-+ <include
-+ src="really-long-long-long-long-long-test-file-omg-so-long.html">
-+ </body>
-+ </html>
-+ ''',
-+
-+ 'test.html': '''
-+ <include src="test2.html">
-+ ''',
-+
-+ 'really-long-long-long-long-long-test-file-omg-so-long.html': '''
-+ <!-- This really long named resource should be included. -->
-+ ''',
-+
-+ 'test2.html': '''
-+ <!-- This second level resource should also be included. -->
-+ ''',
-+
-+ 'test.css': '''
-+ .image {
-+ background: url('test.png');
-+ }
-+ ''',
-+
-+ 'really-long-long-long-long-long-test.css': '''
-+ a:hover {
-+ font-weight: bold; /* Awesome effect is awesome! */
-+ }
-+ ''',
-+
-+ 'test.png': 'PNG DATA',
-+ }
-+
-+ source_resources = set()
-+ tmp_dir = util.TempDir(files)
-+ for filename in files:
-+ source_resources.add(tmp_dir.GetPath(filename))
-+
-+ resources = html_inline.GetResourceFilenames(tmp_dir.GetPath('index.html'),
-+ None)
-+ resources.add(tmp_dir.GetPath('index.html'))
-+ self.failUnlessEqual(resources, source_resources)
-+ tmp_dir.CleanUp()
-+
-+ def testUnmatchedEndIfBlock(self):
-+ '''Tests that an unmatched </if> raises an exception.'''
-+
-+ files = {
-+ 'index.html': '''
-+ <!DOCTYPE HTML>
-+ <html>
-+ <if expr="lang == 'fr'">
-+ bonjour
-+ </if>
-+ <if expr='lang == "de"'>
-+ hallo
-+ </if>
-+ </if>
-+ </html>
-+ ''',
-+ }
-+
-+ tmp_dir = util.TempDir(files)
-+
-+ with self.assertRaises(Exception) as cm:
-+ html_inline.GetResourceFilenames(tmp_dir.GetPath('index.html'), None)
-+ self.failUnlessEqual(str(cm.exception), 'Unmatched </if>')
-+ tmp_dir.CleanUp()
-+
-+ def testCompressedJavaScript(self):
-+ '''Tests that ".src=" doesn't treat as a tag.'''
-+
-+ files = {
-+ 'index.js': '''
-+ if(i<j)a.src="hoge.png";
-+ ''',
-+ }
-+
-+ source_resources = set()
-+ tmp_dir = util.TempDir(files)
-+ for filename in files:
-+ source_resources.add(tmp_dir.GetPath(filename))
-+
-+ resources = html_inline.GetResourceFilenames(tmp_dir.GetPath('index.js'),
-+ None)
-+ resources.add(tmp_dir.GetPath('index.js'))
-+ self.failUnlessEqual(resources, source_resources)
-+ tmp_dir.CleanUp()
-+
-+ def testInlineCSSImports(self):
-+ '''Tests that @import directives in inlined CSS files are inlined too.
-+ '''
-+
-+ files = {
-+ 'index.html': '''
-+ <html>
-+ <head>
-+ <link rel="stylesheet" href="css/test.css">
-+ </head>
-+ </html>
-+ ''',
-+
-+ 'css/test.css': '''
-+ @import url('test2.css');
-+ blink {
-+ display: none;
-+ }
-+ ''',
-+
-+ 'css/test2.css': '''
-+ .image {
-+ background: url('../images/test.png');
-+ }
-+ '''.strip(),
-+
-+ 'images/test.png': 'PNG DATA'
-+ }
-+
-+ expected_inlined = '''
-+ <html>
-+ <head>
-+ <style>
-+ .image {
-+ background: url('data:image/png;base64,UE5HIERBVEE=');
-+ }
-+ blink {
-+ display: none;
-+ }
-+ </style>
-+ </head>
-+ </html>
-+ '''
-+
-+ source_resources = set()
-+ tmp_dir = util.TempDir(files)
-+ for filename in files:
-+ source_resources.add(tmp_dir.GetPath(util.normpath(filename)))
-+
-+ result = html_inline.DoInline(tmp_dir.GetPath('index.html'), None)
-+ resources = result.inlined_files
-+ resources.add(tmp_dir.GetPath('index.html'))
-+ self.failUnlessEqual(resources, source_resources)
-+ self.failUnlessEqual(expected_inlined,
-+ util.FixLineEnd(result.inlined_data, '\n'))
-+
-+ tmp_dir.CleanUp()
-+
-+ def testInlineIgnoresPolymerBindings(self):
-+ '''Tests that polymer bindings are ignored when inlining.
-+ '''
-+
-+ files = {
-+ 'index.html': '''
-+ <html>
-+ <head>
-+ <link rel="stylesheet" href="test.css">
-+ </head>
-+ <body>
-+ <iron-icon src="[[icon]]"></iron-icon><!-- Should be ignored. -->
-+ <iron-icon src="{{src}}"></iron-icon><!-- Also ignored. -->
-+ <!-- [[image]] should be ignored. -->
-+ <div style="background: url([[image]]),
-+ url('test.png');">
-+ </div>
-+ <div style="background: url('test.png'),
-+ url([[image]]);">
-+ </div>
-+ </body>
-+ </html>
-+ ''',
-+
-+ 'test.css': '''
-+ .image {
-+ background: url('test.png');
-+ background-image: url([[ignoreMe]]);
-+ background-image: image-set(url({{alsoMe}}), 1x);
-+ background-image: image-set(
-+ url({{ignore}}) 1x,
-+ url('test.png') 2x);
-+ }
-+ ''',
-+
-+ 'test.png': 'PNG DATA'
-+ }
-+
-+ expected_inlined = '''
-+ <html>
-+ <head>
-+ <style>
-+ .image {
-+ background: url('data:image/png;base64,UE5HIERBVEE=');
-+ background-image: url([[ignoreMe]]);
-+ background-image: image-set(url({{alsoMe}}), 1x);
-+ background-image: image-set(
-+ url({{ignore}}) 1x,
-+ url('data:image/png;base64,UE5HIERBVEE=') 2x);
-+ }
-+ </style>
-+ </head>
-+ <body>
-+ <iron-icon src="[[icon]]"></iron-icon><!-- Should be ignored. -->
-+ <iron-icon src="{{src}}"></iron-icon><!-- Also ignored. -->
-+ <!-- [[image]] should be ignored. -->
-+ <div style="background: url([[image]]),
-+ url('data:image/png;base64,UE5HIERBVEE=');">
-+ </div>
-+ <div style="background: url('data:image/png;base64,UE5HIERBVEE='),
-+ url([[image]]);">
-+ </div>
-+ </body>
-+ </html>
-+ '''
-+
-+ source_resources = set()
-+ tmp_dir = util.TempDir(files)
-+ for filename in files:
-+ source_resources.add(tmp_dir.GetPath(util.normpath(filename)))
-+
-+ result = html_inline.DoInline(tmp_dir.GetPath('index.html'), None)
-+ resources = result.inlined_files
-+ resources.add(tmp_dir.GetPath('index.html'))
-+ self.failUnlessEqual(resources, source_resources)
-+ self.failUnlessEqual(expected_inlined,
-+ util.FixLineEnd(result.inlined_data, '\n'))
-+
-+ tmp_dir.CleanUp()
-+
-+ def testInlineCSSWithIncludeDirective(self):
-+ '''Tests that include directive in external css files also inlined'''
-+
-+ files = {
-+ 'index.html': '''
-+ <html>
-+ <head>
-+ <link rel="stylesheet" href="foo.css">
-+ </head>
-+ </html>
-+ ''',
-+
-+ 'foo.css': '''<include src="style.css">''',
-+
-+ 'style.css': '''
-+ <include src="style2.css">
-+ blink {
-+ display: none;
-+ }
-+ ''',
-+ 'style2.css': '''h1 {}''',
-+ }
-+
-+ expected_inlined = '''
-+ <html>
-+ <head>
-+ <style>
-+ h1 {}
-+ blink {
-+ display: none;
-+ }
-+ </style>
-+ </head>
-+ </html>
-+ '''
-+
-+ source_resources = set()
-+ tmp_dir = util.TempDir(files)
-+ for filename in files:
-+ source_resources.add(tmp_dir.GetPath(filename))
-+
-+ result = html_inline.DoInline(tmp_dir.GetPath('index.html'), None)
-+ resources = result.inlined_files
-+ resources.add(tmp_dir.GetPath('index.html'))
-+ self.failUnlessEqual(resources, source_resources)
-+ self.failUnlessEqual(expected_inlined,
-+ util.FixLineEnd(result.inlined_data, '\n'))
-+ tmp_dir.CleanUp()
-+
-+ def testCssIncludedFileNames(self):
-+ '''Tests that all included files from css are returned'''
-+
-+ files = {
-+ 'index.html': '''
-+ <!DOCTYPE HTML>
-+ <html>
-+ <head>
-+ <link rel="stylesheet" href="test.css">
-+ </head>
-+ <body>
-+ </body>
-+ </html>
-+ ''',
-+
-+ 'test.css': '''
-+ <include src="test2.css">
-+ ''',
-+
-+ 'test2.css': '''
-+ <include src="test3.css">
-+ .image {
-+ background: url('test.png');
-+ }
-+ ''',
-+
-+ 'test3.css': '''h1 {}''',
-+
-+ 'test.png': 'PNG DATA'
-+ }
-+
-+ source_resources = set()
-+ tmp_dir = util.TempDir(files)
-+ for filename in files:
-+ source_resources.add(tmp_dir.GetPath(filename))
-+
-+ resources = html_inline.GetResourceFilenames(tmp_dir.GetPath('index.html'),
-+ None)
-+ resources.add(tmp_dir.GetPath('index.html'))
-+ self.failUnlessEqual(resources, source_resources)
-+ tmp_dir.CleanUp()
-+
-+ def testInlineCSSLinks(self):
-+ '''Tests that only CSS files referenced via relative URLs are inlined.'''
-+
-+ files = {
-+ 'index.html': '''
-+ <html>
-+ <head>
-+ <link rel="stylesheet" href="foo.css">
-+ <link rel="stylesheet" href="chrome://resources/bar.css">
-+ </head>
-+ </html>
-+ ''',
-+
-+ 'foo.css': '''
-+ @import url(chrome://resources/blurp.css);
-+ blink {
-+ display: none;
-+ }
-+ ''',
-+ }
-+
-+ expected_inlined = '''
-+ <html>
-+ <head>
-+ <style>
-+ @import url(chrome://resources/blurp.css);
-+ blink {
-+ display: none;
-+ }
-+ </style>
-+ <link rel="stylesheet" href="chrome://resources/bar.css">
-+ </head>
-+ </html>
-+ '''
-+
-+ source_resources = set()
-+ tmp_dir = util.TempDir(files)
-+ for filename in files:
-+ source_resources.add(tmp_dir.GetPath(filename))
-+
-+ result = html_inline.DoInline(tmp_dir.GetPath('index.html'), None)
-+ resources = result.inlined_files
-+ resources.add(tmp_dir.GetPath('index.html'))
-+ self.failUnlessEqual(resources, source_resources)
-+ self.failUnlessEqual(expected_inlined,
-+ util.FixLineEnd(result.inlined_data, '\n'))
-+ tmp_dir.CleanUp()
-+
-+ def testFilenameVariableExpansion(self):
-+ '''Tests that variables are expanded in filenames before inlining.'''
-+
-+ files = {
-+ 'index.html': '''
-+ <html>
-+ <head>
-+ <link rel="stylesheet" href="style[WHICH].css">
-+ <script src="script[WHICH].js"></script>
-+ </head>
-+ <include src="tmpl[WHICH].html">
-+ <img src="img[WHICH].png">
-+ </html>
-+ ''',
-+ 'style1.css': '''h1 {}''',
-+ 'tmpl1.html': '''<h1></h1>''',
-+ 'script1.js': '''console.log('hello');''',
-+ 'img1.png': '''abc''',
-+ }
-+
-+ expected_inlined = '''
-+ <html>
-+ <head>
-+ <style>h1 {}</style>
-+ <script>console.log('hello');</script>
-+ </head>
-+ <h1></h1>
-+ <img src="data:image/png;base64,YWJj">
-+ </html>
-+ '''
-+
-+ source_resources = set()
-+ tmp_dir = util.TempDir(files)
-+ for filename in files:
-+ source_resources.add(tmp_dir.GetPath(filename))
-+
-+ def replacer(var, repl):
-+ return lambda filename: filename.replace('[%s]' % var, repl)
-+
-+ # Test normal inlining.
-+ result = html_inline.DoInline(
-+ tmp_dir.GetPath('index.html'),
-+ None,
-+ filename_expansion_function=replacer('WHICH', '1'))
-+ resources = result.inlined_files
-+ resources.add(tmp_dir.GetPath('index.html'))
-+ self.failUnlessEqual(resources, source_resources)
-+ self.failUnlessEqual(expected_inlined,
-+ util.FixLineEnd(result.inlined_data, '\n'))
-+
-+ # Test names-only inlining.
-+ result = html_inline.DoInline(
-+ tmp_dir.GetPath('index.html'),
-+ None,
-+ names_only=True,
-+ filename_expansion_function=replacer('WHICH', '1'))
-+ resources = result.inlined_files
-+ resources.add(tmp_dir.GetPath('index.html'))
-+ self.failUnlessEqual(resources, source_resources)
-+ tmp_dir.CleanUp()
-+
-+ def testWithCloseTags(self):
-+ '''Tests that close tags are removed.'''
-+
-+ files = {
-+ 'index.html': '''
-+ <html>
-+ <head>
-+ <link rel="stylesheet" href="style1.css"></link>
-+ <link rel="stylesheet" href="style2.css">
-+ </link>
-+ <link rel="stylesheet" href="style2.css"
-+ >
-+ </link>
-+ <script src="script1.js"></script>
-+ </head>
-+ <include src="tmpl1.html"></include>
-+ <include src="tmpl2.html">
-+ </include>
-+ <include src="tmpl2.html"
-+ >
-+ </include>
-+ <img src="img1.png">
-+ <include src='single-double-quotes.html"></include>
-+ <include src="double-single-quotes.html'></include>
-+ </html>
-+ ''',
-+ 'style1.css': '''h1 {}''',
-+ 'style2.css': '''h2 {}''',
-+ 'tmpl1.html': '''<h1></h1>''',
-+ 'tmpl2.html': '''<h2></h2>''',
-+ 'script1.js': '''console.log('hello');''',
-+ 'img1.png': '''abc''',
-+ }
-+
-+ expected_inlined = '''
-+ <html>
-+ <head>
-+ <style>h1 {}</style>
-+ <style>h2 {}</style>
-+ <style>h2 {}</style>
-+ <script>console.log('hello');</script>
-+ </head>
-+ <h1></h1>
-+ <h2></h2>
-+ <h2></h2>
-+ <img src="data:image/png;base64,YWJj">
-+ <include src='single-double-quotes.html"></include>
-+ <include src="double-single-quotes.html'></include>
-+ </html>
-+ '''
-+
-+ source_resources = set()
-+ tmp_dir = util.TempDir(files)
-+ for filename in files:
-+ source_resources.add(tmp_dir.GetPath(filename))
-+
-+ # Test normal inlining.
-+ result = html_inline.DoInline(
-+ tmp_dir.GetPath('index.html'),
-+ None)
-+ resources = result.inlined_files
-+ resources.add(tmp_dir.GetPath('index.html'))
-+ self.failUnlessEqual(resources, source_resources)
-+ self.failUnlessEqual(expected_inlined,
-+ util.FixLineEnd(result.inlined_data, '\n'))
-+ tmp_dir.CleanUp()
-+
-+ def testCommentedJsInclude(self):
-+ '''Tests that <include> works inside a comment.'''
-+
-+ files = {
-+ 'include.js': '// <include src="other.js">',
-+ 'other.js': '// Copyright somebody\nalert(1);',
-+ }
-+
-+ expected_inlined = '// Copyright somebody\nalert(1);'
-+
-+ source_resources = set()
-+ tmp_dir = util.TempDir(files)
-+ for filename in files:
-+ source_resources.add(tmp_dir.GetPath(filename))
-+
-+ result = html_inline.DoInline(tmp_dir.GetPath('include.js'), None)
-+ resources = result.inlined_files
-+ resources.add(tmp_dir.GetPath('include.js'))
-+ self.failUnlessEqual(resources, source_resources)
-+ self.failUnlessEqual(expected_inlined,
-+ util.FixLineEnd(result.inlined_data, '\n'))
-+ tmp_dir.CleanUp()
-+
-+ def testCommentedJsIf(self):
-+ '''Tests that <if> works inside a comment.'''
-+
-+ files = {
-+ 'if.js': '''
-+ // <if expr="True">
-+ yep();
-+ // </if>
-+
-+ // <if expr="False">
-+ nope();
-+ // </if>
-+ ''',
-+ }
-+
-+ expected_inlined = '''
-+ //
-+ yep();
-+ //
-+
-+ //
-+ '''
-+
-+ source_resources = set()
-+ tmp_dir = util.TempDir(files)
-+ for filename in files:
-+ source_resources.add(tmp_dir.GetPath(filename))
-+
-+ class FakeGrdNode(object):
-+ def EvaluateCondition(self, cond):
-+ return eval(cond)
-+
-+ result = html_inline.DoInline(tmp_dir.GetPath('if.js'), FakeGrdNode())
-+ resources = result.inlined_files
-+
-+ resources.add(tmp_dir.GetPath('if.js'))
-+ self.failUnlessEqual(resources, source_resources)
-+ self.failUnlessEqual(expected_inlined,
-+ util.FixLineEnd(result.inlined_data, '\n'))
-+ tmp_dir.CleanUp()
-+
-+ def testImgSrcset(self):
-+ '''Tests that img srcset="" attributes are converted.'''
-+
-+ # Note that there is no space before "img10.png" and that
-+ # "img11.png" has no descriptor.
-+ files = {
-+ 'index.html': '''
-+ <html>
-+ <img src="img1.png" srcset="img2.png 1x, img3.png 2x">
-+ <img src="img4.png" srcset=" img5.png 1x , img6.png 2x ">
-+ <img src="chrome://theme/img11.png" srcset="img7.png 1x, '''\
-+ '''chrome://theme/img13.png 2x">
-+ <img srcset="img8.png 300w, img9.png 11E-2w,img10.png -1e2w">
-+ <img srcset="img11.png">
-+ <img srcset="img11.png, img2.png 1x">
-+ <img srcset="img2.png 1x, img11.png">
-+ </html>
-+ ''',
-+ 'img1.png': '''a1''',
-+ 'img2.png': '''a2''',
-+ 'img3.png': '''a3''',
-+ 'img4.png': '''a4''',
-+ 'img5.png': '''a5''',
-+ 'img6.png': '''a6''',
-+ 'img7.png': '''a7''',
-+ 'img8.png': '''a8''',
-+ 'img9.png': '''a9''',
-+ 'img10.png': '''a10''',
-+ 'img11.png': '''a11''',
-+ }
-+
-+ expected_inlined = '''
-+ <html>
-+ <img src="data:image/png;base64,YTE=" srcset="data:image/png;base64,'''\
-+ '''YTI= 1x,data:image/png;base64,YTM= 2x">
-+ <img src="data:image/png;base64,YTQ=" srcset="data:image/png;base64,'''\
-+ '''YTU= 1x,data:image/png;base64,YTY= 2x">
-+ <img src="chrome://theme/img11.png" srcset="data:image/png;base64,'''\
-+ '''YTc= 1x,chrome://theme/img13.png 2x">
-+ <img srcset="data:image/png;base64,YTg= 300w,data:image/png;base64,'''\
-+ '''YTk= 11E-2w,data:image/png;base64,YTEw -1e2w">
-+ <img srcset="data:image/png;base64,YTEx">
-+ <img srcset="data:image/png;base64,YTEx,data:image/png;base64,YTI= 1x">
-+ <img srcset="data:image/png;base64,YTI= 1x,data:image/png;base64,YTEx">
-+ </html>
-+ '''
-+
-+ source_resources = set()
-+ tmp_dir = util.TempDir(files)
-+ for filename in files:
-+ source_resources.add(tmp_dir.GetPath(filename))
-+
-+ # Test normal inlining.
-+ result = html_inline.DoInline(
-+ tmp_dir.GetPath('index.html'),
-+ None)
-+ resources = result.inlined_files
-+ resources.add(tmp_dir.GetPath('index.html'))
-+ self.failUnlessEqual(resources, source_resources)
-+ self.failUnlessEqual(expected_inlined,
-+ util.FixLineEnd(result.inlined_data, '\n'))
-+ tmp_dir.CleanUp()
-+
-+ def testImgSrcsetIgnoresI18n(self):
-+ '''Tests that $i18n{...} strings are ignored when inlining.
-+ '''
-+
-+ src_html = '''
-+ <html>
-+ <head></head>
-+ <body>
-+ <img srcset="$i18n{foo}">
-+ </body>
-+ </html>
-+ '''
-+
-+ files = {
-+ 'index.html': src_html,
-+ }
-+
-+ expected_inlined = src_html
-+
-+ source_resources = set()
-+ tmp_dir = util.TempDir(files)
-+ for filename in files:
-+ source_resources.add(tmp_dir.GetPath(util.normpath(filename)))
-+
-+ result = html_inline.DoInline(tmp_dir.GetPath('index.html'), None)
-+ resources = result.inlined_files
-+ resources.add(tmp_dir.GetPath('index.html'))
-+ self.failUnlessEqual(resources, source_resources)
-+ self.failUnlessEqual(expected_inlined,
-+ util.FixLineEnd(result.inlined_data, '\n'))
-+ tmp_dir.CleanUp()
-+
-+ def testSourceSrcset(self):
-+ '''Tests that source srcset="" attributes are converted.'''
-+
-+ # Note that there is no space before "img10.png" and that
-+ # "img11.png" has no descriptor.
-+ files = {
-+ 'index.html': '''
-+ <html>
-+ <source src="img1.png" srcset="img2.png 1x, img3.png 2x">
-+ <source src="img4.png" srcset=" img5.png 1x , img6.png 2x ">
-+ <source src="chrome://theme/img11.png" srcset="img7.png 1x, '''\
-+ '''chrome://theme/img13.png 2x">
-+ <source srcset="img8.png 300w, img9.png 11E-2w,img10.png -1e2w">
-+ <source srcset="img11.png">
-+ </html>
-+ ''',
-+ 'img1.png': '''a1''',
-+ 'img2.png': '''a2''',
-+ 'img3.png': '''a3''',
-+ 'img4.png': '''a4''',
-+ 'img5.png': '''a5''',
-+ 'img6.png': '''a6''',
-+ 'img7.png': '''a7''',
-+ 'img8.png': '''a8''',
-+ 'img9.png': '''a9''',
-+ 'img10.png': '''a10''',
-+ 'img11.png': '''a11''',
-+ }
-+
-+ expected_inlined = '''
-+ <html>
-+ <source src="data:image/png;base64,YTE=" srcset="data:image/png;'''\
-+ '''base64,YTI= 1x,data:image/png;base64,YTM= 2x">
-+ <source src="data:image/png;base64,YTQ=" srcset="data:image/png;'''\
-+ '''base64,YTU= 1x,data:image/png;base64,YTY= 2x">
-+ <source src="chrome://theme/img11.png" srcset="data:image/png;'''\
-+ '''base64,YTc= 1x,chrome://theme/img13.png 2x">
-+ <source srcset="data:image/png;base64,YTg= 300w,data:image/png;'''\
-+ '''base64,YTk= 11E-2w,data:image/png;base64,YTEw -1e2w">
-+ <source srcset="data:image/png;base64,YTEx">
-+ </html>
-+ '''
-+
-+ source_resources = set()
-+ tmp_dir = util.TempDir(files)
-+ for filename in files:
-+ source_resources.add(tmp_dir.GetPath(filename))
-+
-+ # Test normal inlining.
-+ result = html_inline.DoInline(tmp_dir.GetPath('index.html'), None)
-+ resources = result.inlined_files
-+ resources.add(tmp_dir.GetPath('index.html'))
-+ self.failUnlessEqual(resources, source_resources)
-+ self.failUnlessEqual(expected_inlined,
-+ util.FixLineEnd(result.inlined_data, '\n'))
-+ tmp_dir.CleanUp()
-+
-+ def testConditionalInclude(self):
-+ '''Tests that output and dependency generation includes only files not'''\
-+ ''' blocked by <if> macros.'''
-+
-+ files = {
-+ 'index.html': '''
-+ <html>
-+ <if expr="True">
-+ <img src="img1.png" srcset="img2.png 1x, img3.png 2x">
-+ </if>
-+ <if expr="False">
-+ <img src="img4.png" srcset=" img5.png 1x, img6.png 2x ">
-+ </if>
-+ <if expr="True">
-+ <img src="chrome://theme/img11.png" srcset="img7.png 1x, '''\
-+ '''chrome://theme/img13.png 2x">
-+ </if>
-+ <img srcset="img8.png 300w, img9.png 11E-2w,img10.png -1e2w">
-+ </html>
-+ ''',
-+ 'img1.png': '''a1''',
-+ 'img2.png': '''a2''',
-+ 'img3.png': '''a3''',
-+ 'img4.png': '''a4''',
-+ 'img5.png': '''a5''',
-+ 'img6.png': '''a6''',
-+ 'img7.png': '''a7''',
-+ 'img8.png': '''a8''',
-+ 'img9.png': '''a9''',
-+ 'img10.png': '''a10''',
-+ }
-+
-+ expected_inlined = '''
-+ <html>
-+ <img src="data:image/png;base64,YTE=" srcset="data:image/png;base64,'''\
-+ '''YTI= 1x,data:image/png;base64,YTM= 2x">
-+ <img src="chrome://theme/img11.png" srcset="data:image/png;base64,'''\
-+ '''YTc= 1x,chrome://theme/img13.png 2x">
-+ <img srcset="data:image/png;base64,YTg= 300w,data:image/png;base64,'''\
-+ '''YTk= 11E-2w,data:image/png;base64,YTEw -1e2w">
-+ </html>
-+ '''
-+
-+ expected_files = [
-+ 'index.html',
-+ 'img1.png',
-+ 'img2.png',
-+ 'img3.png',
-+ 'img7.png',
-+ 'img8.png',
-+ 'img9.png',
-+ 'img10.png'
-+ ]
-+
-+ source_resources = set()
-+ tmp_dir = util.TempDir(files)
-+ for filename in expected_files:
-+ source_resources.add(tmp_dir.GetPath(filename))
-+
-+ class FakeGrdNode(object):
-+ def EvaluateCondition(self, cond):
-+ return eval(cond)
-+
-+ # Test normal inlining.
-+ result = html_inline.DoInline(
-+ tmp_dir.GetPath('index.html'),
-+ FakeGrdNode())
-+ resources = result.inlined_files
-+ resources.add(tmp_dir.GetPath('index.html'))
-+ self.failUnlessEqual(resources, source_resources)
-+
-+ # ignore whitespace
-+ expected_inlined = re.sub(r'\s+', ' ', expected_inlined)
-+ actually_inlined = re.sub(r'\s+', ' ',
-+ util.FixLineEnd(result.inlined_data, '\n'))
-+ self.failUnlessEqual(expected_inlined, actually_inlined);
-+ tmp_dir.CleanUp()
-+
-+ def testPreprocessOnlyEvaluatesIncludeAndIf(self):
-+ '''Tests that preprocess_only=true evaluates <include> and <if> only. '''
-+
-+ files = {
-+ 'index.html': '''
-+ <html>
-+ <head>
-+ <link rel="stylesheet" href="not_inlined.css">
-+ <script src="also_not_inlined.js">
-+ </head>
-+ <body>
-+ <include src="inline_this.html">
-+ <if expr="True">
-+ <p>'if' should be evaluated.</p>
-+ </if>
-+ </body>
-+ </html>
-+ ''',
-+ 'not_inlined.css': ''' /* <link> should not be inlined. */ ''',
-+ 'also_not_inlined.js': ''' // <script> should not be inlined. ''',
-+ 'inline_this.html': ''' <p>'include' should be inlined.</p> '''
-+ }
-+
-+ expected_inlined = '''
-+ <html>
-+ <head>
-+ <link rel="stylesheet" href="not_inlined.css">
-+ <script src="also_not_inlined.js">
-+ </head>
-+ <body>
-+ <p>'include' should be inlined.</p>
-+ <p>'if' should be evaluated.</p>
-+ </body>
-+ </html>
-+ '''
-+
-+ source_resources = set()
-+ tmp_dir = util.TempDir(files)
-+ source_resources.add(tmp_dir.GetPath('index.html'))
-+ source_resources.add(tmp_dir.GetPath('inline_this.html'))
-+
-+ result = html_inline.DoInline(tmp_dir.GetPath('index.html'), None,
-+ preprocess_only=True)
-+ resources = result.inlined_files
-+ resources.add(tmp_dir.GetPath('index.html'))
-+ self.failUnlessEqual(resources, source_resources)
-+
-+ # Ignore whitespace
-+ expected_inlined = re.sub(r'\s+', ' ', expected_inlined)
-+ actually_inlined = re.sub(r'\s+', ' ',
-+ util.FixLineEnd(result.inlined_data, '\n'))
-+ self.failUnlessEqual(expected_inlined, actually_inlined)
-+
-+ tmp_dir.CleanUp()
-+
-+ def testPreprocessOnlyAppliesRecursively(self):
-+ '''Tests that preprocess_only=true propagates to included files. '''
-+
-+ files = {
-+ 'index.html': '''
-+ <html>
-+ <include src="outer_include.html">
-+ </html>
-+ ''',
-+ 'outer_include.html': '''
-+ <include src="inner_include.html">
-+ <link rel="stylesheet" href="not_inlined.css">
-+ ''',
-+ 'inner_include.html': ''' <p>This should be inlined in index.html</p> ''',
-+ 'not_inlined.css': ''' /* This should not be inlined. */ '''
-+ }
-+
-+ expected_inlined = '''
-+ <html>
-+ <p>This should be inlined in index.html</p>
-+ <link rel="stylesheet" href="not_inlined.css">
-+ </html>
-+ '''
-+
-+ source_resources = set()
-+ tmp_dir = util.TempDir(files)
-+ source_resources.add(tmp_dir.GetPath('index.html'))
-+ source_resources.add(tmp_dir.GetPath('outer_include.html'))
-+ source_resources.add(tmp_dir.GetPath('inner_include.html'))
-+
-+ result = html_inline.DoInline(tmp_dir.GetPath('index.html'), None,
-+ preprocess_only=True)
-+ resources = result.inlined_files
-+ resources.add(tmp_dir.GetPath('index.html'))
-+ self.failUnlessEqual(resources, source_resources)
-+
-+ # Ignore whitespace
-+ expected_inlined = re.sub(r'\s+', ' ', expected_inlined)
-+ actually_inlined = re.sub(r'\s+', ' ',
-+ util.FixLineEnd(result.inlined_data, '\n'))
-+ self.failUnlessEqual(expected_inlined, actually_inlined)
-+
-+ tmp_dir.CleanUp()
-+
-+if __name__ == '__main__':
-+ unittest.main()
-diff --git a/tools/grit/grit/format/minifier.py b/tools/grit/grit/format/minifier.py
-new file mode 100644
-index 0000000000..1a0ea34e49
---- /dev/null
-+++ b/tools/grit/grit/format/minifier.py
-@@ -0,0 +1,45 @@
-+# Copyright 2016 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+"""Framework for stripping whitespace and comments from resource files"""
-+
-+from __future__ import print_function
-+
-+from os import path
-+import subprocess
-+import sys
-+
-+import six
-+
-+__js_minifier = None
-+__css_minifier = None
-+
-+def SetJsMinifier(minifier):
-+ global __js_minifier
-+ __js_minifier = minifier.split()
-+
-+def SetCssMinifier(minifier):
-+ global __css_minifier
-+ __css_minifier = minifier.split()
-+
-+def Minify(source, filename):
-+ """Minify |source| (bytes) from |filename| and return bytes."""
-+ file_type = path.splitext(filename)[1]
-+ minifier = None
-+ if file_type == '.js':
-+ minifier = __js_minifier
-+ elif file_type == '.css':
-+ minifier = __css_minifier
-+ if not minifier:
-+ return source
-+ p = subprocess.Popen(
-+ minifier,
-+ stdin=subprocess.PIPE,
-+ stdout=subprocess.PIPE,
-+ stderr=subprocess.PIPE)
-+ (stdout, stderr) = p.communicate(source)
-+ if p.returncode != 0:
-+ print('Minification failed for %s' % filename)
-+ print(stderr)
-+ sys.exit(p.returncode)
-+ return stdout
-diff --git a/tools/grit/grit/format/policy_templates_json.py b/tools/grit/grit/format/policy_templates_json.py
-new file mode 100644
-index 0000000000..2f9330bb9a
---- /dev/null
-+++ b/tools/grit/grit/format/policy_templates_json.py
-@@ -0,0 +1,26 @@
-+# Copyright 2017 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+"""Translates policy_templates.json files.
-+"""
-+
-+from __future__ import print_function
-+
-+from grit.node import structure
-+
-+
-+def Format(root, lang='en', output_dir='.'):
-+ policy_json = None
-+ for item in root.ActiveDescendants():
-+ with item:
-+ if (isinstance(item, structure.StructureNode) and
-+ item.attrs['type'] == 'policy_template_metafile'):
-+ json_text = item.gatherer.Translate(
-+ lang,
-+ pseudo_if_not_available=item.PseudoIsAllowed(),
-+ fallback_to_english=item.ShouldFallbackToEnglish())
-+ # We're only expecting one node of this kind.
-+ assert not policy_json
-+ policy_json = json_text
-+ return policy_json
-diff --git a/tools/grit/grit/format/policy_templates_json_unittest.py b/tools/grit/grit/format/policy_templates_json_unittest.py
-new file mode 100644
-index 0000000000..e252c94e2c
---- /dev/null
-+++ b/tools/grit/grit/format/policy_templates_json_unittest.py
-@@ -0,0 +1,207 @@
-+#!/usr/bin/env python
-+# coding: utf-8
-+# Copyright 2017 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+"""Unittest for policy_templates_json.py.
-+"""
-+
-+from __future__ import print_function
-+
-+import os
-+import sys
-+if __name__ == '__main__':
-+ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
-+
-+import grit.extern.tclib
-+import tempfile
-+import unittest
-+
-+from six import StringIO
-+
-+from grit import grd_reader
-+from grit.tool import build
-+
-+
-+class PolicyTemplatesJsonUnittest(unittest.TestCase):
-+
-+ def testPolicyTranslation(self):
-+ # Create test policy_templates.json data.
-+ caption = "The main policy"
-+ caption_translation = "Die Hauptrichtlinie"
-+
-+ message = \
-+ "Red cabbage stays red cabbage and wedding dress stays wedding dress"
-+ message_translation = \
-+ "Blaukraut bleibt Blaukraut und Brautkleid bleibt Brautkleid"
-+
-+ schema_key_description = "Number of users"
-+ schema_key_description_translation = "Anzahl der Nutzer"
-+
-+ policy_json = """
-+ {
-+ "policy_definitions": [
-+ {
-+ 'name': 'MainPolicy',
-+ 'type': 'main',
-+ 'owners': ['foo@bar.com'],
-+ 'schema': {
-+ 'properties': {
-+ 'default_launch_container': {
-+ 'enum': [
-+ 'tab',
-+ 'window',
-+ ],
-+ 'type': 'string',
-+ },
-+ 'users_number': {
-+ 'description': '''%s''',
-+ 'type': 'integer',
-+ },
-+ },
-+ 'type': 'object',
-+ },
-+ 'supported_on': ['chrome_os:29-'],
-+ 'features': {
-+ 'can_be_recommended': True,
-+ 'dynamic_refresh': True,
-+ },
-+ 'example_value': True,
-+ 'caption': '''%s''',
-+ 'tags': [],
-+ 'desc': '''This policy does stuff.'''
-+ },
-+ ],
-+ "policy_atomic_group_definitions": [],
-+ "placeholders": [],
-+ "messages": {
-+ 'message_string_id': {
-+ 'desc': '''The description is removed from the grit output''',
-+ 'text': '''%s'''
-+ }
-+ }
-+ }""" % (schema_key_description, caption, message)
-+
-+ # Create translations. The translation IDs are hashed from the English text.
-+ caption_id = grit.extern.tclib.GenerateMessageId(caption);
-+ message_id = grit.extern.tclib.GenerateMessageId(message);
-+ schema_key_description_id = grit.extern.tclib.GenerateMessageId(
-+ schema_key_description)
-+ policy_xtb = """
-+<?xml version="1.0" ?>
-+<!DOCTYPE translationbundle>
-+<translationbundle lang="de">
-+<translation id="%s">%s</translation>
-+<translation id="%s">%s</translation>
-+<translation id="%s">%s</translation>
-+</translationbundle>""" % (caption_id, caption_translation,
-+ message_id, message_translation,
-+ schema_key_description_id,
-+ schema_key_description_translation)
-+
-+ # Write both to a temp file.
-+ tmp_dir_name = tempfile.gettempdir()
-+
-+ json_file_path = os.path.join(tmp_dir_name, 'test.json')
-+ with open(json_file_path, 'w') as f:
-+ f.write(policy_json.strip())
-+
-+ xtb_file_path = os.path.join(tmp_dir_name, 'test.xtb')
-+ with open(xtb_file_path, 'w') as f:
-+ f.write(policy_xtb.strip())
-+
-+ # Assemble a test grit tree, similar to policy_templates.grd.
-+ grd_text = '''
-+ <grit base_dir="." latest_public_release="0" current_release="1" source_lang_id="en">
-+ <translations>
-+ <file path="%s" lang="de" />
-+ </translations>
-+ <release seq="1">
-+ <structures>
-+ <structure name="IDD_POLICY_SOURCE_FILE" file="%s" type="policy_template_metafile" />
-+ </structures>
-+ </release>
-+ </grit>''' % (xtb_file_path, json_file_path)
-+ grd_string_io = StringIO(grd_text)
-+
-+ # Parse the grit tree and load the policies' JSON with a gatherer.
-+ grd = grd_reader.Parse(grd_string_io, dir=tmp_dir_name, defines={'_google_chrome': True})
-+ grd.SetOutputLanguage('en')
-+ grd.RunGatherers()
-+
-+ # Remove the temp files.
-+ os.unlink(xtb_file_path)
-+ os.unlink(json_file_path)
-+
-+ # Run grit with en->de translation.
-+ env_lang = 'en'
-+ out_lang = 'de'
-+ env_defs = {'_google_chrome': '1'}
-+
-+ grd.SetOutputLanguage(env_lang)
-+ grd.SetDefines(env_defs)
-+ buf = StringIO()
-+ build.RcBuilder.ProcessNode(grd, DummyOutput('policy_templates', out_lang), buf)
-+ output = buf.getvalue()
-+
-+ # Caption and message texts get taken from xtb.
-+ # desc is 'translated' to some pseudo-English
-+ # 'ThïPïs pôPôlïPïcýPý dôéPôés stüPüff'.
-+ expected = u"""{
-+ "policy_definitions": [
-+ {
-+ "caption": "%s",
-+ "desc": "Th\xefP\xefs p\xf4P\xf4l\xefP\xefc\xfdP\xfd d\xf4\xe9P\xf4\xe9s st\xfcP\xfcff.",
-+ "example_value": true,
-+ "features": {"can_be_recommended": true, "dynamic_refresh": true},
-+ "name": "MainPolicy",
-+ "owners": ["foo@bar.com"],
-+ "schema": {
-+ "properties": {
-+ "default_launch_container": {
-+ "enum": [
-+ "tab",
-+ "window"
-+ ],
-+ "type": "string"
-+ },
-+ "users_number": {
-+ "description": "%s",
-+ "type": "integer"
-+ }
-+ },
-+ "type": "object"
-+ },
-+ "supported_on": ["chrome_os:29-"],
-+ "tags": [],
-+ "type": "main"
-+ }
-+ ],
-+ "policy_atomic_group_definitions": [
-+ ],
-+ "messages": {
-+ "message_string_id": {
-+ "text": "%s"
-+ }
-+ }
-+
-+}""" % (caption_translation, schema_key_description_translation,
-+ message_translation)
-+ self.assertEqual(expected, output)
-+
-+
-+class DummyOutput(object):
-+
-+ def __init__(self, type, language):
-+ self.type = type
-+ self.language = language
-+
-+ def GetType(self):
-+ return self.type
-+
-+ def GetLanguage(self):
-+ return self.language
-+
-+ def GetOutputFilename(self):
-+ return 'hello.gif'
-diff --git a/tools/grit/grit/format/rc.py b/tools/grit/grit/format/rc.py
-new file mode 100644
-index 0000000000..ed32bb809e
---- /dev/null
-+++ b/tools/grit/grit/format/rc.py
-@@ -0,0 +1,474 @@
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Support for formatting an RC file for compilation.
-+'''
-+
-+from __future__ import print_function
-+
-+import os
-+import re
-+from functools import partial
-+
-+import six
-+
-+from grit import util
-+from grit.node import misc
-+
-+
-+def Format(root, lang='en', output_dir='.'):
-+ from grit.node import empty, include, message, structure
-+
-+ yield _FormatHeader(root, lang, output_dir)
-+
-+ for item in root.ActiveDescendants():
-+ if isinstance(item, empty.MessagesNode):
-+ # Write one STRINGTABLE per <messages> container.
-+ # This is hacky: it iterates over the children twice.
-+ yield 'STRINGTABLE\nBEGIN\n'
-+ for subitem in item.ActiveDescendants():
-+ if isinstance(subitem, message.MessageNode):
-+ with subitem:
-+ yield FormatMessage(subitem, lang)
-+ yield 'END\n\n'
-+ elif isinstance(item, include.IncludeNode):
-+ with item:
-+ yield FormatInclude(item, lang, output_dir)
-+ elif isinstance(item, structure.StructureNode):
-+ with item:
-+ yield FormatStructure(item, lang, output_dir)
-+
-+
-+'''
-+This dictionary defines the language charset pair lookup table, which is used
-+for replacing the GRIT expand variables for language info in Product Version
-+resource. The key is the language ISO country code, and the value
-+is the language and character-set pair, which is a hexadecimal string
-+consisting of the concatenation of the language and character-set identifiers.
-+The first 4 digit of the value is the hex value of LCID, the remaining
-+4 digits is the hex value of character-set id(code page)of the language.
-+
-+LCID resource: http://msdn.microsoft.com/en-us/library/ms776294.aspx
-+Codepage resource: http://www.science.co.il/language/locale-codes.asp
-+
-+We have defined three GRIT expand_variables to be used in the version resource
-+file to set the language info. Here is an example how they should be used in
-+the VS_VERSION_INFO section of the resource file to allow GRIT to localize
-+the language info correctly according to product locale.
-+
-+VS_VERSION_INFO VERSIONINFO
-+...
-+BEGIN
-+ BLOCK "StringFileInfo"
-+ BEGIN
-+ BLOCK "[GRITVERLANGCHARSETHEX]"
-+ BEGIN
-+ ...
-+ END
-+ END
-+ BLOCK "VarFileInfo"
-+ BEGIN
-+ VALUE "Translation", [GRITVERLANGID], [GRITVERCHARSETID]
-+ END
-+END
-+
-+'''
-+
-+_LANGUAGE_CHARSET_PAIR = {
-+ # Language neutral LCID, unicode(1200) code page.
-+ 'neutral' : '000004b0',
-+ # LANG_USER_DEFAULT LCID, unicode(1200) code page.
-+ 'userdefault' : '040004b0',
-+ 'ar' : '040104e8',
-+ 'fi' : '040b04e4',
-+ 'ko' : '041203b5',
-+ 'es' : '0c0a04e4',
-+ 'bg' : '040204e3',
-+ # No codepage for filipino, use unicode(1200).
-+ 'fil' : '046404e4',
-+ 'fr' : '040c04e4',
-+ 'lv' : '042604e9',
-+ 'sv' : '041d04e4',
-+ 'ca' : '040304e4',
-+ 'de' : '040704e4',
-+ 'lt' : '042704e9',
-+ # Do not use! This is only around for backwards
-+ # compatibility and will be removed - use fil instead
-+ 'tl' : '0c0004b0',
-+ 'zh-CN' : '080403a8',
-+ 'zh-TW' : '040403b6',
-+ 'zh-HK' : '0c0403b6',
-+ 'el' : '040804e5',
-+ 'no' : '001404e4',
-+ 'nb' : '041404e4',
-+ 'nn' : '081404e4',
-+ 'th' : '041e036a',
-+ 'he' : '040d04e7',
-+ 'iw' : '040d04e7',
-+ 'pl' : '041504e2',
-+ 'tr' : '041f04e6',
-+ 'hr' : '041a04e4',
-+ # No codepage for Hindi, use unicode(1200).
-+ 'hi' : '043904b0',
-+ 'pt-PT' : '081604e4',
-+ 'pt-BR' : '041604e4',
-+ 'uk' : '042204e3',
-+ 'cs' : '040504e2',
-+ 'hu' : '040e04e2',
-+ 'ro' : '041804e2',
-+ # No codepage for Urdu, use unicode(1200).
-+ 'ur' : '042004b0',
-+ 'da' : '040604e4',
-+ 'is' : '040f04e4',
-+ 'ru' : '041904e3',
-+ 'vi' : '042a04ea',
-+ 'nl' : '041304e4',
-+ 'id' : '042104e4',
-+ 'sr' : '081a04e2',
-+ 'en-GB' : '0809040e',
-+ 'it' : '041004e4',
-+ 'sk' : '041b04e2',
-+ 'et' : '042504e9',
-+ 'ja' : '041103a4',
-+ 'sl' : '042404e2',
-+ 'en' : '040904b0',
-+ # LCID for Mexico; Windows does not support L.A. LCID.
-+ 'es-419' : '080a04e4',
-+ # No codepage for Bengali, use unicode(1200).
-+ 'bn' : '044504b0',
-+ 'fa' : '042904e8',
-+ # No codepage for Gujarati, use unicode(1200).
-+ 'gu' : '044704b0',
-+ # No codepage for Kannada, use unicode(1200).
-+ 'kn' : '044b04b0',
-+ # Malay (Malaysia) [ms-MY]
-+ 'ms' : '043e04e4',
-+ # No codepage for Malayalam, use unicode(1200).
-+ 'ml' : '044c04b0',
-+ # No codepage for Marathi, use unicode(1200).
-+ 'mr' : '044e04b0',
-+ # No codepage for Oriya , use unicode(1200).
-+ 'or' : '044804b0',
-+ # No codepage for Tamil, use unicode(1200).
-+ 'ta' : '044904b0',
-+ # No codepage for Telugu, use unicode(1200).
-+ 'te' : '044a04b0',
-+ # No codepage for Amharic, use unicode(1200). >= Vista.
-+ 'am' : '045e04b0',
-+ 'sw' : '044104e4',
-+ 'af' : '043604e4',
-+ 'eu' : '042d04e4',
-+ 'fr-CA' : '0c0c04e4',
-+ 'gl' : '045604e4',
-+ # No codepage for Zulu, use unicode(1200).
-+ 'zu' : '043504b0',
-+ 'fake-bidi' : '040d04e7',
-+}
-+
-+# Language ID resource: http://msdn.microsoft.com/en-us/library/ms776294.aspx
-+#
-+# There is no appropriate sublang for Spanish (Latin America) [es-419], so we
-+# use Mexico. SUBLANG_DEFAULT would incorrectly map to Spain. Unlike other
-+# Latin American countries, Mexican Spanish is supported by VERSIONINFO:
-+# http://msdn.microsoft.com/en-us/library/aa381058.aspx
-+
-+_LANGUAGE_DIRECTIVE_PAIR = {
-+ 'neutral' : 'LANG_NEUTRAL, SUBLANG_NEUTRAL',
-+ 'userdefault' : 'LANG_NEUTRAL, SUBLANG_DEFAULT',
-+ 'ar' : 'LANG_ARABIC, SUBLANG_DEFAULT',
-+ 'fi' : 'LANG_FINNISH, SUBLANG_DEFAULT',
-+ 'ko' : 'LANG_KOREAN, SUBLANG_KOREAN',
-+ 'es' : 'LANG_SPANISH, SUBLANG_SPANISH_MODERN',
-+ 'bg' : 'LANG_BULGARIAN, SUBLANG_DEFAULT',
-+ # LANG_FILIPINO (100) not in VC 7 winnt.h.
-+ 'fil' : '100, SUBLANG_DEFAULT',
-+ 'fr' : 'LANG_FRENCH, SUBLANG_FRENCH',
-+ 'lv' : 'LANG_LATVIAN, SUBLANG_DEFAULT',
-+ 'sv' : 'LANG_SWEDISH, SUBLANG_SWEDISH',
-+ 'ca' : 'LANG_CATALAN, SUBLANG_DEFAULT',
-+ 'de' : 'LANG_GERMAN, SUBLANG_GERMAN',
-+ 'lt' : 'LANG_LITHUANIAN, SUBLANG_LITHUANIAN',
-+ # Do not use! See above.
-+ 'tl' : 'LANG_NEUTRAL, SUBLANG_DEFAULT',
-+ 'zh-CN' : 'LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED',
-+ 'zh-TW' : 'LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL',
-+ 'zh-HK' : 'LANG_CHINESE, SUBLANG_CHINESE_HONGKONG',
-+ 'el' : 'LANG_GREEK, SUBLANG_DEFAULT',
-+ 'no' : 'LANG_NORWEGIAN, SUBLANG_DEFAULT',
-+ 'nb' : 'LANG_NORWEGIAN, SUBLANG_NORWEGIAN_BOKMAL',
-+ 'nn' : 'LANG_NORWEGIAN, SUBLANG_NORWEGIAN_NYNORSK',
-+ 'th' : 'LANG_THAI, SUBLANG_DEFAULT',
-+ 'he' : 'LANG_HEBREW, SUBLANG_DEFAULT',
-+ 'iw' : 'LANG_HEBREW, SUBLANG_DEFAULT',
-+ 'pl' : 'LANG_POLISH, SUBLANG_DEFAULT',
-+ 'tr' : 'LANG_TURKISH, SUBLANG_DEFAULT',
-+ 'hr' : 'LANG_CROATIAN, SUBLANG_DEFAULT',
-+ 'hi' : 'LANG_HINDI, SUBLANG_DEFAULT',
-+ 'pt-PT' : 'LANG_PORTUGUESE, SUBLANG_PORTUGUESE',
-+ 'pt-BR' : 'LANG_PORTUGUESE, SUBLANG_DEFAULT',
-+ 'uk' : 'LANG_UKRAINIAN, SUBLANG_DEFAULT',
-+ 'cs' : 'LANG_CZECH, SUBLANG_DEFAULT',
-+ 'hu' : 'LANG_HUNGARIAN, SUBLANG_DEFAULT',
-+ 'ro' : 'LANG_ROMANIAN, SUBLANG_DEFAULT',
-+ 'ur' : 'LANG_URDU, SUBLANG_DEFAULT',
-+ 'da' : 'LANG_DANISH, SUBLANG_DEFAULT',
-+ 'is' : 'LANG_ICELANDIC, SUBLANG_DEFAULT',
-+ 'ru' : 'LANG_RUSSIAN, SUBLANG_DEFAULT',
-+ 'vi' : 'LANG_VIETNAMESE, SUBLANG_DEFAULT',
-+ 'nl' : 'LANG_DUTCH, SUBLANG_DEFAULT',
-+ 'id' : 'LANG_INDONESIAN, SUBLANG_DEFAULT',
-+ 'sr' : 'LANG_SERBIAN, SUBLANG_SERBIAN_LATIN',
-+ 'en-GB' : 'LANG_ENGLISH, SUBLANG_ENGLISH_UK',
-+ 'it' : 'LANG_ITALIAN, SUBLANG_DEFAULT',
-+ 'sk' : 'LANG_SLOVAK, SUBLANG_DEFAULT',
-+ 'et' : 'LANG_ESTONIAN, SUBLANG_DEFAULT',
-+ 'ja' : 'LANG_JAPANESE, SUBLANG_DEFAULT',
-+ 'sl' : 'LANG_SLOVENIAN, SUBLANG_DEFAULT',
-+ 'en' : 'LANG_ENGLISH, SUBLANG_ENGLISH_US',
-+ # No L.A. sublang exists.
-+ 'es-419' : 'LANG_SPANISH, SUBLANG_SPANISH_MEXICAN',
-+ 'bn' : 'LANG_BENGALI, SUBLANG_DEFAULT',
-+ 'fa' : 'LANG_PERSIAN, SUBLANG_DEFAULT',
-+ 'gu' : 'LANG_GUJARATI, SUBLANG_DEFAULT',
-+ 'kn' : 'LANG_KANNADA, SUBLANG_DEFAULT',
-+ 'ms' : 'LANG_MALAY, SUBLANG_DEFAULT',
-+ 'ml' : 'LANG_MALAYALAM, SUBLANG_DEFAULT',
-+ 'mr' : 'LANG_MARATHI, SUBLANG_DEFAULT',
-+ 'or' : 'LANG_ORIYA, SUBLANG_DEFAULT',
-+ 'ta' : 'LANG_TAMIL, SUBLANG_DEFAULT',
-+ 'te' : 'LANG_TELUGU, SUBLANG_DEFAULT',
-+ 'am' : 'LANG_AMHARIC, SUBLANG_DEFAULT',
-+ 'sw' : 'LANG_SWAHILI, SUBLANG_DEFAULT',
-+ 'af' : 'LANG_AFRIKAANS, SUBLANG_DEFAULT',
-+ 'eu' : 'LANG_BASQUE, SUBLANG_DEFAULT',
-+ 'fr-CA' : 'LANG_FRENCH, SUBLANG_FRENCH_CANADIAN',
-+ 'gl' : 'LANG_GALICIAN, SUBLANG_DEFAULT',
-+ 'zu' : 'LANG_ZULU, SUBLANG_DEFAULT',
-+ 'pa' : 'LANG_PUNJABI, SUBLANG_PUNJABI_INDIA',
-+ 'sa' : 'LANG_SANSKRIT, SUBLANG_SANSKRIT_INDIA',
-+ 'si' : 'LANG_SINHALESE, SUBLANG_SINHALESE_SRI_LANKA',
-+ 'ne' : 'LANG_NEPALI, SUBLANG_NEPALI_NEPAL',
-+ 'ti' : 'LANG_TIGRIGNA, SUBLANG_TIGRIGNA_ERITREA',
-+ 'fake-bidi' : 'LANG_HEBREW, SUBLANG_DEFAULT',
-+}
-+
-+# A note on 'no-specific-language' in the following few functions:
-+# Some build systems may wish to call GRIT to scan for dependencies in
-+# a language-agnostic way, and can then specify this fake language as
-+# the output context. It should never be used when output is actually
-+# being generated.
-+
-+def GetLangCharsetPair(language):
-+ if language in _LANGUAGE_CHARSET_PAIR:
-+ return _LANGUAGE_CHARSET_PAIR[language]
-+ if language != 'no-specific-language':
-+ print('Warning:GetLangCharsetPair() found undefined language %s' % language)
-+ return ''
-+
-+def GetLangDirectivePair(language):
-+ if language in _LANGUAGE_DIRECTIVE_PAIR:
-+ return _LANGUAGE_DIRECTIVE_PAIR[language]
-+
-+ # We don't check for 'no-specific-language' here because this
-+ # function should only get called when output is being formatted,
-+ # and at that point we would not want to get
-+ # 'no-specific-language' passed as the language.
-+ print('Warning:GetLangDirectivePair() found undefined language %s' % language)
-+ return 'unknown language: see tools/grit/format/rc.py'
-+
-+def GetLangIdHex(language):
-+ if language in _LANGUAGE_CHARSET_PAIR:
-+ langcharset = _LANGUAGE_CHARSET_PAIR[language]
-+ lang_id = '0x' + langcharset[0:4]
-+ return lang_id
-+ if language != 'no-specific-language':
-+ print('Warning:GetLangIdHex() found undefined language %s' % language)
-+ return ''
-+
-+
-+def GetCharsetIdDecimal(language):
-+ if language in _LANGUAGE_CHARSET_PAIR:
-+ langcharset = _LANGUAGE_CHARSET_PAIR[language]
-+ charset_decimal = int(langcharset[4:], 16)
-+ return str(charset_decimal)
-+ if language != 'no-specific-language':
-+ print('Warning:GetCharsetIdDecimal() found undefined language %s' % language)
-+ return ''
-+
-+
-+def GetUnifiedLangCode(language) :
-+ r = re.compile('([a-z]{1,2})_([a-z]{1,2})')
-+ if r.match(language) :
-+ underscore = language.find('_')
-+ return language[0:underscore] + '-' + language[underscore + 1:].upper()
-+ return language
-+
-+
-+def RcSubstitutions(substituter, lang):
-+ '''Add language-based substitutions for Rc files to the substitutor.'''
-+ unified_lang_code = GetUnifiedLangCode(lang)
-+ substituter.AddSubstitutions({
-+ 'GRITVERLANGCHARSETHEX': GetLangCharsetPair(unified_lang_code),
-+ 'GRITVERLANGID': GetLangIdHex(unified_lang_code),
-+ 'GRITVERCHARSETID': GetCharsetIdDecimal(unified_lang_code)})
-+
-+
-+def _FormatHeader(root, lang, output_dir):
-+ '''Returns the required preamble for RC files.'''
-+ assert isinstance(lang, six.string_types)
-+ assert isinstance(root, misc.GritNode)
-+ # Find the location of the resource header file, so that we can include
-+ # it.
-+ resource_header = 'resource.h' # fall back to this
-+ language_directive = ''
-+ for output in root.GetOutputFiles():
-+ if output.attrs['type'] == 'rc_header':
-+ resource_header = os.path.abspath(output.GetOutputFilename())
-+ resource_header = util.MakeRelativePath(output_dir, resource_header)
-+ if output.attrs['lang'] != lang:
-+ continue
-+ if output.attrs['language_section'] == '':
-+ # If no language_section is requested, no directive is added
-+ # (Used when the generated rc will be included from another rc
-+ # file that will have the appropriate language directive)
-+ language_directive = ''
-+ elif output.attrs['language_section'] == 'neutral':
-+ # If a neutral language section is requested (default), add a
-+ # neutral language directive
-+ language_directive = 'LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL'
-+ elif output.attrs['language_section'] == 'lang':
-+ language_directive = 'LANGUAGE %s' % GetLangDirectivePair(lang)
-+ resource_header = resource_header.replace('\\', '\\\\')
-+ return '''// This file is automatically generated by GRIT. Do not edit.
-+
-+#include "%s"
-+#include <winresrc.h>
-+#ifdef IDC_STATIC
-+#undef IDC_STATIC
-+#endif
-+#define IDC_STATIC (-1)
-+
-+%s
-+
-+
-+''' % (resource_header, language_directive)
-+# end _FormatHeader() function
-+
-+
-+def FormatMessage(item, lang):
-+ '''Returns a single message of a string table.'''
-+ message = item.ws_at_start + item.Translate(lang) + item.ws_at_end
-+ # Escape quotation marks (RC format uses doubling-up
-+ message = message.replace('"', '""')
-+ # Replace linebreaks with a \n escape
-+ message = util.LINEBREAKS.sub(r'\\n', message)
-+ if hasattr(item.GetRoot(), 'GetSubstituter'):
-+ substituter = item.GetRoot().GetSubstituter()
-+ message = substituter.Substitute(message)
-+
-+ name_attr = item.GetTextualIds()[0]
-+
-+ return ' %-15s "%s"\n' % (name_attr, message)
-+
-+
-+def _FormatSection(item, lang, output_dir):
-+ '''Writes out an .rc file section.'''
-+ assert isinstance(lang, six.string_types)
-+ from grit.node import structure
-+ assert isinstance(item, structure.StructureNode)
-+
-+ if item.IsExcludedFromRc():
-+ return ''
-+
-+ text = item.gatherer.Translate(
-+ lang, skeleton_gatherer=item.GetSkeletonGatherer(),
-+ pseudo_if_not_available=item.PseudoIsAllowed(),
-+ fallback_to_english=item.ShouldFallbackToEnglish()) + '\n\n'
-+
-+ # Replace the language expand_variables in version rc info.
-+ if item.ExpandVariables() and hasattr(item.GetRoot(), 'GetSubstituter'):
-+ substituter = item.GetRoot().GetSubstituter()
-+ text = substituter.Substitute(text)
-+ return text
-+
-+
-+def FormatInclude(item, lang, output_dir, type=None, process_html=False):
-+ '''Formats an item that is included in an .rc file (e.g. an ICON).
-+
-+ Args:
-+ item: an IncludeNode or StructureNode
-+ lang, output_dir: standard formatter parameters
-+ type: .rc file resource type, e.g. 'ICON' (ignored unless item is a
-+ StructureNode)
-+ process_html: False/True (ignored unless item is a StructureNode)
-+ '''
-+ assert isinstance(lang, six.string_types)
-+ from grit.node import structure
-+ from grit.node import include
-+ assert isinstance(item, (structure.StructureNode, include.IncludeNode))
-+
-+ if isinstance(item, include.IncludeNode):
-+ type = item.attrs['type'].upper()
-+ process_html = item.attrs['flattenhtml'] == 'true'
-+ filename_only = item.attrs['filenameonly'] == 'true'
-+ relative_path = item.attrs['relativepath'] == 'true'
-+ else:
-+ assert (isinstance(item, structure.StructureNode) and item.attrs['type'] in
-+ ['admin_template', 'chrome_html', 'chrome_scaled_image',
-+ 'tr_html', 'txt'])
-+ filename_only = False
-+ relative_path = False
-+
-+ # By default, we use relative pathnames to included resources so that
-+ # sharing the resulting .rc files is possible.
-+ #
-+ # The FileForLanguage() Function has the side effect of generating the file
-+ # if needed (e.g. if it is an HTML file include).
-+ file_for_lang = item.FileForLanguage(lang, output_dir)
-+ if file_for_lang is None:
-+ return ''
-+
-+ filename = os.path.abspath(file_for_lang)
-+ if process_html:
-+ filename = item.Process(output_dir)
-+ elif filename_only:
-+ filename = os.path.basename(filename)
-+ elif relative_path:
-+ filename = util.MakeRelativePath(output_dir, filename)
-+
-+ filename = filename.replace('\\', '\\\\') # escape for the RC format
-+
-+ if isinstance(item, structure.StructureNode) and item.IsExcludedFromRc():
-+ return ''
-+
-+ name = item.attrs['name']
-+ item_id = item.GetRoot().GetIdMap()[name]
-+ return '// ID: %d\n%-18s %-18s "%s"\n' % (item_id, name, type, filename)
-+
-+
-+def _DoNotFormat(item, lang, output_dir):
-+ return ''
-+
-+
-+# Formatter instance to use for each type attribute
-+# when formatting Structure nodes.
-+_STRUCTURE_FORMATTERS = {
-+ 'accelerators' : _FormatSection,
-+ 'dialog' : _FormatSection,
-+ 'menu' : _FormatSection,
-+ 'rcdata' : _FormatSection,
-+ 'version' : _FormatSection,
-+ 'admin_template' : partial(FormatInclude, type='ADM'),
-+ 'chrome_html' : partial(FormatInclude, type='BINDATA',
-+ process_html=True),
-+ 'chrome_scaled_image' : partial(FormatInclude, type='BINDATA'),
-+ 'tr_html' : partial(FormatInclude, type='HTML'),
-+ 'txt' : partial(FormatInclude, type='TXT'),
-+ 'policy_template_metafile': _DoNotFormat,
-+}
-+
-+
-+def FormatStructure(item, lang, output_dir):
-+ formatter = _STRUCTURE_FORMATTERS[item.attrs['type']]
-+ return formatter(item, lang, output_dir)
-diff --git a/tools/grit/grit/format/rc_header.py b/tools/grit/grit/format/rc_header.py
-new file mode 100644
-index 0000000000..ea2c217f53
---- /dev/null
-+++ b/tools/grit/grit/format/rc_header.py
-@@ -0,0 +1,48 @@
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Item formatters for RC headers.
-+'''
-+
-+from __future__ import print_function
-+
-+
-+def Format(root, lang='en', output_dir='.'):
-+ yield '''\
-+// This file is automatically generated by GRIT. Do not edit.
-+
-+#pragma once
-+'''
-+ # Check for emit nodes under the rc_header. If any emit node
-+ # is present, we assume it means the GRD file wants to override
-+ # the default header, with no includes.
-+ default_includes = ['#include <atlres.h>', '']
-+ emit_lines = []
-+ for output_node in root.GetOutputFiles():
-+ if output_node.GetType() == 'rc_header':
-+ for child in output_node.children:
-+ if child.name == 'emit' and child.attrs['emit_type'] == 'prepend':
-+ emit_lines.append(child.GetCdata())
-+ for line in emit_lines or default_includes:
-+ yield line + '\n'
-+ if root.IsWhitelistSupportEnabled():
-+ yield '#include "ui/base/resource/whitelist.h"\n'
-+ for line in FormatDefines(root):
-+ yield line
-+
-+
-+def FormatDefines(root):
-+ '''Yields #define SYMBOL 1234 lines.
-+
-+ Args:
-+ root: A GritNode.
-+ '''
-+ tids = root.GetIdMap()
-+ rc_header_format = '#define {0} {1}\n'
-+ if root.IsWhitelistSupportEnabled():
-+ rc_header_format = '#define {0} (::ui::WhitelistedResource<{1}>(), {1})\n'
-+ for item in root.ActiveDescendants():
-+ with item:
-+ for tid in item.GetTextualIds():
-+ yield rc_header_format.format(tid, tids[tid])
-diff --git a/tools/grit/grit/format/rc_header_unittest.py b/tools/grit/grit/format/rc_header_unittest.py
-new file mode 100644
-index 0000000000..eed4d70a99
---- /dev/null
-+++ b/tools/grit/grit/format/rc_header_unittest.py
-@@ -0,0 +1,138 @@
-+#!/usr/bin/env python
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Unit tests for the rc_header formatter'''
-+
-+# GRD samples exceed the 80 character limit.
-+# pylint: disable-msg=C6310
-+
-+from __future__ import print_function
-+
-+import os
-+import sys
-+import unittest
-+
-+if __name__ == '__main__':
-+ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
-+
-+from grit import util
-+from grit.format import rc_header
-+
-+
-+class RcHeaderFormatterUnittest(unittest.TestCase):
-+ def FormatAll(self, grd):
-+ output = rc_header.FormatDefines(grd)
-+ return ''.join(output).replace(' ', '')
-+
-+ def testFormatter(self):
-+ grd = util.ParseGrdForUnittest('''
-+ <includes first_id="300" comment="bingo">
-+ <include type="gif" name="ID_LOGO" file="images/logo.gif" />
-+ </includes>
-+ <messages first_id="10000">
-+ <message name="IDS_GREETING" desc="Printed to greet the currently logged in user">
-+ Hello <ph name="USERNAME">%s<ex>Joi</ex></ph>, how are you doing today?
-+ </message>
-+ <message name="IDS_BONGO">
-+ Bongo!
-+ </message>
-+ </messages>
-+ <structures>
-+ <structure type="dialog" name="IDD_NARROW_DIALOG" file="rc_files/dialogs.rc" />
-+ <structure type="version" name="VS_VERSION_INFO" file="rc_files/version.rc" />
-+ </structures>''')
-+ output = self.FormatAll(grd)
-+ self.failUnless(output.count('IDS_GREETING10000'))
-+ self.failUnless(output.count('ID_LOGO300'))
-+
-+ def testOnlyDefineResourcesThatSatisfyOutputCondition(self):
-+ grd = util.ParseGrdForUnittest('''
-+ <includes first_id="300" comment="bingo">
-+ <include type="gif" name="ID_LOGO" file="images/logo.gif" />
-+ </includes>
-+ <messages first_id="10000">
-+ <message name="IDS_FIRSTPRESENTSTRING" desc="Present in .rc file.">
-+ I will appear in the .rc file.
-+ </message>
-+ <if expr="False"> <!--Do not include in the .rc files until used.-->
-+ <message name="IDS_MISSINGSTRING" desc="Not present in .rc file.">
-+ I will not appear in the .rc file.
-+ </message>
-+ </if>
-+ <if expr="lang != 'es'">
-+ <message name="IDS_LANGUAGESPECIFICSTRING" desc="Present in .rc file.">
-+ Hello.
-+ </message>
-+ </if>
-+ <if expr="lang == 'es'">
-+ <message name="IDS_LANGUAGESPECIFICSTRING" desc="Present in .rc file.">
-+ Hola.
-+ </message>
-+ </if>
-+ <message name="IDS_THIRDPRESENTSTRING" desc="Present in .rc file.">
-+ I will also appear in the .rc file.
-+ </message>
-+ </messages>''')
-+ output = self.FormatAll(grd)
-+ self.failUnless(output.count('IDS_FIRSTPRESENTSTRING10000'))
-+ self.failIf(output.count('IDS_MISSINGSTRING'))
-+ self.failUnless(output.count('IDS_LANGUAGESPECIFICSTRING10002'))
-+ self.failUnless(output.count('IDS_THIRDPRESENTSTRING10003'))
-+
-+ def testEmit(self):
-+ grd = util.ParseGrdForUnittest('''
-+ <outputs>
-+ <output type="rc_all" filename="dummy">
-+ <emit emit_type="prepend">Wrong</emit>
-+ </output>
-+ <if expr="False">
-+ <output type="rc_header" filename="dummy">
-+ <emit emit_type="prepend">No</emit>
-+ </output>
-+ </if>
-+ <output type="rc_header" filename="dummy">
-+ <emit emit_type="append">Error</emit>
-+ </output>
-+ <output type="rc_header" filename="dummy">
-+ <emit emit_type="prepend">Bingo</emit>
-+ </output>
-+ </outputs>''')
-+ output = ''.join(rc_header.Format(grd, 'en', '.'))
-+ output = util.StripBlankLinesAndComments(output)
-+ self.assertEqual('#pragma once\nBingo', output)
-+
-+ def testRcHeaderFormat(self):
-+ grd = util.ParseGrdForUnittest('''
-+ <includes first_id="300" comment="bingo">
-+ <include type="gif" name="IDR_LOGO" file="images/logo.gif" />
-+ </includes>
-+ <messages first_id="10000">
-+ <message name="IDS_GREETING" desc="Printed to greet the currently logged in user">
-+ Hello <ph name="USERNAME">%s<ex>Joi</ex></ph>, how are you doing today?
-+ </message>
-+ <message name="IDS_BONGO">
-+ Bongo!
-+ </message>
-+ </messages>''')
-+
-+ # Using the default settings.
-+ output = rc_header.FormatDefines(grd)
-+ self.assertEqual(('#define IDR_LOGO 300\n'
-+ '#define IDS_GREETING 10000\n'
-+ '#define IDS_BONGO 10001\n'), ''.join(output))
-+
-+ # Using resource whitelist support.
-+ grd.SetWhitelistSupportEnabled(True)
-+ output = rc_header.FormatDefines(grd)
-+ self.assertEqual(('#define IDR_LOGO '
-+ '(::ui::WhitelistedResource<300>(), 300)\n'
-+ '#define IDS_GREETING '
-+ '(::ui::WhitelistedResource<10000>(), 10000)\n'
-+ '#define IDS_BONGO '
-+ '(::ui::WhitelistedResource<10001>(), 10001)\n'),
-+ ''.join(output))
-+
-+if __name__ == '__main__':
-+ unittest.main()
-diff --git a/tools/grit/grit/format/rc_unittest.py b/tools/grit/grit/format/rc_unittest.py
-new file mode 100644
-index 0000000000..d23f063596
---- /dev/null
-+++ b/tools/grit/grit/format/rc_unittest.py
-@@ -0,0 +1,415 @@
-+#!/usr/bin/env python
-+# Copyright (c) 2011 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Unit tests for grit.format.rc'''
-+
-+from __future__ import print_function
-+
-+import os
-+import re
-+import sys
-+if __name__ == '__main__':
-+ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
-+
-+import tempfile
-+import unittest
-+
-+from six import StringIO
-+
-+from grit import grd_reader
-+from grit import util
-+from grit.node import structure
-+from grit.tool import build
-+
-+
-+_PREAMBLE = '''\
-+#include "resource.h"
-+#include <winresrc.h>
-+#ifdef IDC_STATIC
-+#undef IDC_STATIC
-+#endif
-+#define IDC_STATIC (-1)
-+'''
-+
-+
-+class DummyOutput(object):
-+ def __init__(self, type, language, file = 'hello.gif'):
-+ self.type = type
-+ self.language = language
-+ self.file = file
-+
-+ def GetType(self):
-+ return self.type
-+
-+ def GetLanguage(self):
-+ return self.language
-+
-+ def GetOutputFilename(self):
-+ return self.file
-+
-+
-+class FormatRcUnittest(unittest.TestCase):
-+ def testMessages(self):
-+ root = util.ParseGrdForUnittest("""
-+ <messages>
-+ <message name="IDS_BTN_GO" desc="Button text" meaning="verb">Go!</message>
-+ <message name="IDS_GREETING" desc="Printed to greet the currently logged in user">
-+ Hello <ph name="USERNAME">%s<ex>Joi</ex></ph>, how are you doing today?
-+ </message>
-+ <message name="BONGO" desc="Flippo nippo">
-+ Howdie "Mr. Elephant", how are you doing? '''
-+ </message>
-+ <message name="IDS_WITH_LINEBREAKS">
-+Good day sir,
-+I am a bee
-+Sting sting
-+ </message>
-+ </messages>
-+ """)
-+
-+ buf = StringIO()
-+ build.RcBuilder.ProcessNode(root, DummyOutput('rc_all', 'en'), buf)
-+ output = util.StripBlankLinesAndComments(buf.getvalue())
-+ self.assertEqual(_PREAMBLE + u'''\
-+STRINGTABLE
-+BEGIN
-+ IDS_BTN_GO "Go!"
-+ IDS_GREETING "Hello %s, how are you doing today?"
-+ BONGO "Howdie ""Mr. Elephant"", how are you doing? "
-+ IDS_WITH_LINEBREAKS "Good day sir,\\nI am a bee\\nSting sting"
-+END''', output)
-+
-+ def testRcSection(self):
-+ root = util.ParseGrdForUnittest(r'''
-+ <structures>
-+ <structure type="menu" name="IDC_KLONKMENU" file="grit\testdata\klonk.rc" encoding="utf-16" />
-+ <structure type="dialog" name="IDD_ABOUTBOX" file="grit\testdata\klonk.rc" encoding="utf-16" />
-+ <structure type="version" name="VS_VERSION_INFO" file="grit\testdata\klonk.rc" encoding="utf-16" />
-+ </structures>''')
-+ root.SetOutputLanguage('en')
-+ root.RunGatherers()
-+
-+ buf = StringIO()
-+ build.RcBuilder.ProcessNode(root, DummyOutput('rc_all', 'en'), buf)
-+ output = util.StripBlankLinesAndComments(buf.getvalue())
-+ expected = _PREAMBLE + u'''\
-+IDC_KLONKMENU MENU
-+BEGIN
-+ POPUP "&File"
-+ BEGIN
-+ MENUITEM "E&xit", IDM_EXIT
-+ MENUITEM "This be ""Klonk"" me like", ID_FILE_THISBE
-+ POPUP "gonk"
-+ BEGIN
-+ MENUITEM "Klonk && is [good]", ID_GONK_KLONKIS
-+ END
-+ END
-+ POPUP "&Help"
-+ BEGIN
-+ MENUITEM "&About ...", IDM_ABOUT
-+ END
-+END
-+
-+IDD_ABOUTBOX DIALOGEX 22, 17, 230, 75
-+STYLE DS_SETFONT | DS_MODALFRAME | WS_CAPTION | WS_SYSMENU
-+CAPTION "About"
-+FONT 8, "System", 0, 0, 0x0
-+BEGIN
-+ ICON IDI_KLONK,IDC_MYICON,14,9,20,20
-+ LTEXT "klonk Version ""yibbee"" 1.0",IDC_STATIC,49,10,119,8,
-+ SS_NOPREFIX
-+ LTEXT "Copyright (C) 2005",IDC_STATIC,49,20,119,8
-+ DEFPUSHBUTTON "OK",IDOK,195,6,30,11,WS_GROUP
-+ CONTROL "Jack ""Black"" Daniels",IDC_RADIO1,"Button",
-+ BS_AUTORADIOBUTTON,46,51,84,10
-+END
-+
-+VS_VERSION_INFO VERSIONINFO
-+ FILEVERSION 1,0,0,1
-+ PRODUCTVERSION 1,0,0,1
-+ FILEFLAGSMASK 0x17L
-+#ifdef _DEBUG
-+ FILEFLAGS 0x1L
-+#else
-+ FILEFLAGS 0x0L
-+#endif
-+ FILEOS 0x4L
-+ FILETYPE 0x1L
-+ FILESUBTYPE 0x0L
-+BEGIN
-+ BLOCK "StringFileInfo"
-+ BEGIN
-+ BLOCK "040904b0"
-+ BEGIN
-+ VALUE "FileDescription", "klonk Application"
-+ VALUE "FileVersion", "1, 0, 0, 1"
-+ VALUE "InternalName", "klonk"
-+ VALUE "LegalCopyright", "Copyright (C) 2005"
-+ VALUE "OriginalFilename", "klonk.exe"
-+ VALUE "ProductName", " klonk Application"
-+ VALUE "ProductVersion", "1, 0, 0, 1"
-+ END
-+ END
-+ BLOCK "VarFileInfo"
-+ BEGIN
-+ VALUE "Translation", 0x409, 1200
-+ END
-+END'''.strip()
-+ for expected_line, output_line in zip(expected.split(), output.split()):
-+ self.assertEqual(expected_line, output_line)
-+
-+ def testRcIncludeStructure(self):
-+ root = util.ParseGrdForUnittest('''
-+ <structures>
-+ <structure type="tr_html" name="IDR_HTML" file="bingo.html"/>
-+ <structure type="tr_html" name="IDR_HTML2" file="bingo2.html"/>
-+ </structures>''', base_dir = '/temp')
-+ # We do not run gatherers as it is not needed and wouldn't find the file
-+
-+ buf = StringIO()
-+ build.RcBuilder.ProcessNode(root, DummyOutput('rc_all', 'en'), buf)
-+ output = util.StripBlankLinesAndComments(buf.getvalue())
-+ expected = (_PREAMBLE +
-+ u'IDR_HTML HTML "%s"\n'
-+ u'IDR_HTML2 HTML "%s"'
-+ % (util.normpath('/temp/bingo.html').replace('\\', '\\\\'),
-+ util.normpath('/temp/bingo2.html').replace('\\', '\\\\')))
-+ # hackety hack to work on win32&lin
-+ output = re.sub(r'"[c-zC-Z]:', '"', output)
-+ self.assertEqual(expected, output)
-+
-+ def testRcIncludeFile(self):
-+ root = util.ParseGrdForUnittest('''
-+ <includes>
-+ <include type="TXT" name="TEXT_ONE" file="bingo.txt"/>
-+ <include type="TXT" name="TEXT_TWO" file="bingo2.txt" filenameonly="true" />
-+ </includes>''', base_dir = '/temp')
-+
-+ buf = StringIO()
-+ build.RcBuilder.ProcessNode(root, DummyOutput('rc_all', 'en'), buf)
-+ output = util.StripBlankLinesAndComments(buf.getvalue())
-+ expected = (_PREAMBLE +
-+ u'TEXT_ONE TXT "%s"\n'
-+ u'TEXT_TWO TXT "%s"'
-+ % (util.normpath('/temp/bingo.txt').replace('\\', '\\\\'),
-+ 'bingo2.txt'))
-+ # hackety hack to work on win32&lin
-+ output = re.sub(r'"[c-zC-Z]:', '"', output)
-+ self.assertEqual(expected, output)
-+
-+ def testRcIncludeFlattenedHtmlFile(self):
-+ input_file = util.PathFromRoot('grit/testdata/include_test.html')
-+ output_file = '%s/HTML_FILE1_include_test.html' % tempfile.gettempdir()
-+ root = util.ParseGrdForUnittest('''
-+ <includes>
-+ <include name="HTML_FILE1" flattenhtml="true" file="%s" type="BINDATA" />
-+ </includes>''' % input_file)
-+
-+ buf = StringIO()
-+ build.RcBuilder.ProcessNode(root, DummyOutput('rc_all', 'en', output_file),
-+ buf)
-+ output = util.StripBlankLinesAndComments(buf.getvalue())
-+
-+ expected = (_PREAMBLE +
-+ u'HTML_FILE1 BINDATA "HTML_FILE1_include_test.html"')
-+ # hackety hack to work on win32&lin
-+ output = re.sub(r'"[c-zC-Z]:', '"', output)
-+ self.assertEqual(expected, output)
-+
-+ file_contents = util.ReadFile(output_file, 'utf-8')
-+
-+ # Check for the content added by the <include> tag.
-+ self.failUnless(file_contents.find('Hello Include!') != -1)
-+ # Check for the content that was removed by if tag.
-+ self.failUnless(file_contents.find('should be removed') == -1)
-+ # Check for the content that was kept in place by if.
-+ self.failUnless(file_contents.find('should be kept') != -1)
-+ self.failUnless(file_contents.find('in the middle...') != -1)
-+ self.failUnless(file_contents.find('at the end...') != -1)
-+ # Check for nested content that was kept
-+ self.failUnless(file_contents.find('nested true should be kept') != -1)
-+ self.failUnless(file_contents.find('silbing true should be kept') != -1)
-+ # Check for removed "<if>" and "</if>" tags.
-+ self.failUnless(file_contents.find('<if expr=') == -1)
-+ self.failUnless(file_contents.find('</if>') == -1)
-+ os.remove(output_file)
-+
-+ def testStructureNodeOutputfile(self):
-+ input_file = util.PathFromRoot('grit/testdata/simple.html')
-+ root = util.ParseGrdForUnittest('''
-+ <structures>
-+ <structure type="tr_html" name="IDR_HTML" file="%s" />
-+ </structures>''' % input_file)
-+ struct, = root.GetChildrenOfType(structure.StructureNode)
-+ # We must run the gatherer since we'll be wanting the translation of the
-+ # file. The file exists in the location pointed to.
-+ root.SetOutputLanguage('en')
-+ root.RunGatherers()
-+
-+ output_dir = tempfile.gettempdir()
-+ en_file = struct.FileForLanguage('en', output_dir)
-+ self.failUnless(en_file == input_file)
-+ fr_file = struct.FileForLanguage('fr', output_dir)
-+ self.failUnless(fr_file == os.path.join(output_dir, 'fr_simple.html'))
-+
-+ contents = util.ReadFile(fr_file, 'utf-8')
-+
-+ self.failUnless(contents.find('<p>') != -1) # should contain the markup
-+ self.failUnless(contents.find('Hello!') == -1) # should be translated
-+ os.remove(fr_file)
-+
-+ def testChromeHtmlNodeOutputfile(self):
-+ input_file = util.PathFromRoot('grit/testdata/chrome_html.html')
-+ output_file = '%s/HTML_FILE1_chrome_html.html' % tempfile.gettempdir()
-+ root = util.ParseGrdForUnittest('''
-+ <structures>
-+ <structure type="chrome_html" name="HTML_FILE1" file="%s" flattenhtml="true" />
-+ </structures>''' % input_file)
-+ struct, = root.GetChildrenOfType(structure.StructureNode)
-+ struct.gatherer.SetDefines({'scale_factors': '2x'})
-+ # We must run the gatherers since we'll be wanting the chrome_html output.
-+ # The file exists in the location pointed to.
-+ root.SetOutputLanguage('en')
-+ root.RunGatherers()
-+
-+ buf = StringIO()
-+ build.RcBuilder.ProcessNode(root, DummyOutput('rc_all', 'en', output_file),
-+ buf)
-+ output = util.StripBlankLinesAndComments(buf.getvalue())
-+ expected = (_PREAMBLE +
-+ u'HTML_FILE1 BINDATA "HTML_FILE1_chrome_html.html"')
-+ # hackety hack to work on win32&lin
-+ output = re.sub(r'"[c-zC-Z]:', '"', output)
-+ self.assertEqual(expected, output)
-+
-+ file_contents = util.ReadFile(output_file, 'utf-8')
-+
-+ # Check for the content added by the <include> tag.
-+ self.failUnless(file_contents.find('Hello Include!') != -1)
-+ # Check for inserted -webkit-image-set.
-+ self.failUnless(file_contents.find('content: -webkit-image-set') != -1)
-+ os.remove(output_file)
-+
-+ def testSubstitutionHtml(self):
-+ input_file = util.PathFromRoot('grit/testdata/toolbar_about.html')
-+ root = grd_reader.Parse(StringIO('''<?xml version="1.0" encoding="UTF-8"?>
-+ <grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
-+ <release seq="1" allow_pseudo="False">
-+ <structures fallback_to_english="True">
-+ <structure type="tr_html" name="IDR_HTML" file="%s" expand_variables="true"/>
-+ </structures>
-+ </release>
-+ </grit>
-+ ''' % input_file), util.PathFromRoot('.'))
-+ root.SetOutputLanguage('ar')
-+ # We must run the gatherers since we'll be wanting the translation of the
-+ # file. The file exists in the location pointed to.
-+ root.RunGatherers()
-+
-+ output_dir = tempfile.gettempdir()
-+ struct, = root.GetChildrenOfType(structure.StructureNode)
-+ ar_file = struct.FileForLanguage('ar', output_dir)
-+ self.failUnless(ar_file == os.path.join(output_dir,
-+ 'ar_toolbar_about.html'))
-+
-+ contents = util.ReadFile(ar_file, 'utf-8')
-+
-+ self.failUnless(contents.find('dir="RTL"') != -1)
-+ os.remove(ar_file)
-+
-+ def testFallbackToEnglish(self):
-+ root = util.ParseGrdForUnittest(r'''
-+ <structures fallback_to_english="True">
-+ <structure type="dialog" name="IDD_ABOUTBOX" file="grit\testdata\klonk.rc" encoding="utf-16" />
-+ </structures>''', base_dir=util.PathFromRoot('.'))
-+ root.SetOutputLanguage('en')
-+ root.RunGatherers()
-+
-+ buf = StringIO()
-+ formatter = build.RcBuilder.ProcessNode(
-+ root, DummyOutput('rc_all', 'bingobongo'), buf)
-+ output = util.StripBlankLinesAndComments(buf.getvalue())
-+ self.assertEqual(_PREAMBLE + '''\
-+IDD_ABOUTBOX DIALOGEX 22, 17, 230, 75
-+STYLE DS_SETFONT | DS_MODALFRAME | WS_CAPTION | WS_SYSMENU
-+CAPTION "About"
-+FONT 8, "System", 0, 0, 0x0
-+BEGIN
-+ ICON IDI_KLONK,IDC_MYICON,14,9,20,20
-+ LTEXT "klonk Version ""yibbee"" 1.0",IDC_STATIC,49,10,119,8,
-+ SS_NOPREFIX
-+ LTEXT "Copyright (C) 2005",IDC_STATIC,49,20,119,8
-+ DEFPUSHBUTTON "OK",IDOK,195,6,30,11,WS_GROUP
-+ CONTROL "Jack ""Black"" Daniels",IDC_RADIO1,"Button",
-+ BS_AUTORADIOBUTTON,46,51,84,10
-+END''', output)
-+
-+
-+ def testSubstitutionRc(self):
-+ root = grd_reader.Parse(StringIO(r'''<?xml version="1.0" encoding="UTF-8"?>
-+ <grit latest_public_release="2" source_lang_id="en-US" current_release="3"
-+ base_dir=".">
-+ <outputs>
-+ <output lang="en" type="rc_all" filename="grit\testdata\klonk_resources.rc"/>
-+ </outputs>
-+ <release seq="1" allow_pseudo="False">
-+ <structures>
-+ <structure type="menu" name="IDC_KLONKMENU"
-+ file="grit\testdata\klonk.rc" encoding="utf-16"
-+ expand_variables="true" />
-+ </structures>
-+ <messages>
-+ <message name="good" sub_variable="true">
-+ excellent
-+ </message>
-+ </messages>
-+ </release>
-+ </grit>
-+ '''), util.PathFromRoot('.'))
-+ root.SetOutputLanguage('en')
-+ root.RunGatherers()
-+
-+ buf = StringIO()
-+ build.RcBuilder.ProcessNode(root, DummyOutput('rc_all', 'en'), buf)
-+ output = buf.getvalue()
-+ self.assertEqual('''
-+// This file is automatically generated by GRIT. Do not edit.
-+
-+#include "resource.h"
-+#include <winresrc.h>
-+#ifdef IDC_STATIC
-+#undef IDC_STATIC
-+#endif
-+#define IDC_STATIC (-1)
-+
-+LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
-+
-+
-+IDC_KLONKMENU MENU
-+BEGIN
-+ POPUP "&File"
-+ BEGIN
-+ MENUITEM "E&xit", IDM_EXIT
-+ MENUITEM "This be ""Klonk"" me like", ID_FILE_THISBE
-+ POPUP "gonk"
-+ BEGIN
-+ MENUITEM "Klonk && is excellent", ID_GONK_KLONKIS
-+ END
-+ END
-+ POPUP "&Help"
-+ BEGIN
-+ MENUITEM "&About ...", IDM_ABOUT
-+ END
-+END
-+
-+STRINGTABLE
-+BEGIN
-+ good "excellent"
-+END
-+'''.strip(), output.strip())
-+
-+
-+if __name__ == '__main__':
-+ unittest.main()
-diff --git a/tools/grit/grit/format/resource_map.py b/tools/grit/grit/format/resource_map.py
-new file mode 100644
-index 0000000000..95a8b83160
---- /dev/null
-+++ b/tools/grit/grit/format/resource_map.py
-@@ -0,0 +1,159 @@
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''This file contains item formatters for resource_map_header and
-+resource_map_source files. A resource map is a mapping between resource names
-+(string) and the internal resource ID.'''
-+
-+from __future__ import print_function
-+
-+import os
-+from functools import partial
-+
-+from grit import util
-+
-+
-+def GetFormatter(type):
-+ if type == 'resource_map_header':
-+ return _FormatHeader
-+ if type == 'resource_file_map_source':
-+ return partial(_FormatSource, _GetItemPath)
-+ if type == 'resource_map_source':
-+ return partial(_FormatSource, _GetItemName)
-+
-+
-+def GetMapName(root):
-+ '''Get the name of the resource map based on the header file name. E.g.,
-+ if our header filename is theme_resources.h, we name our resource map
-+ kThemeResourcesMap.
-+
-+ |root| is the grd file root.'''
-+ outputs = root.GetOutputFiles()
-+ rc_header_file = None
-+ for output in outputs:
-+ if 'rc_header' == output.GetType():
-+ rc_header_file = output.GetFilename()
-+ if not rc_header_file:
-+ raise Exception('unable to find resource header filename')
-+ filename = os.path.splitext(os.path.split(rc_header_file)[1])[0]
-+ filename = filename[0].upper() + filename[1:]
-+ while True:
-+ pos = filename.find('_')
-+ if pos == -1 or pos >= len(filename):
-+ break
-+ filename = filename[:pos] + filename[pos + 1].upper() + filename[pos + 2:]
-+ return 'k' + filename
-+
-+
-+def _FormatHeader(root, lang='en', output_dir='.'):
-+ '''Create the header file for the resource mapping. This file just declares
-+ an array of name/value pairs.'''
-+ return '''\
-+// This file is automatically generated by GRIT. Do not edit.
-+
-+#include <stddef.h>
-+
-+#ifndef GRIT_RESOURCE_MAP_STRUCT_
-+#define GRIT_RESOURCE_MAP_STRUCT_
-+struct GritResourceMap {
-+ const char* const name;
-+ int value;
-+};
-+#endif // GRIT_RESOURCE_MAP_STRUCT_
-+
-+extern const GritResourceMap %(map_name)s[];
-+extern const size_t %(map_name)sSize;
-+''' % { 'map_name': GetMapName(root) }
-+
-+
-+def _FormatSourceHeader(root, output_dir):
-+ '''Create the header of the C++ source file for the resource mapping.'''
-+ rc_header_file = None
-+ map_header_file = None
-+ for output in root.GetOutputFiles():
-+ type = output.GetType()
-+ if 'rc_header' == type:
-+ rc_header_file = util.MakeRelativePath(output_dir,
-+ output.GetOutputFilename())
-+ elif 'resource_map_header' == type:
-+ map_header_file = util.MakeRelativePath(output_dir,
-+ output.GetOutputFilename())
-+ if not rc_header_file or not map_header_file:
-+ raise Exception('resource_map_source output type requires '
-+ 'a resource_map_header and rc_header outputs')
-+ return '''\
-+// This file is automatically generated by GRIT. Do not edit.
-+
-+#include "%(map_header_file)s"
-+
-+#include <stddef.h>
-+
-+#include "base/stl_util.h"
-+
-+#include "%(rc_header_file)s"
-+
-+const GritResourceMap %(map_name)s[] = {
-+''' % { 'map_header_file': map_header_file,
-+ 'rc_header_file': rc_header_file,
-+ 'map_name': GetMapName(root),
-+ }
-+
-+
-+def _FormatSourceFooter(root):
-+ # Return the footer text.
-+ return '''\
-+};
-+
-+const size_t %(map_name)sSize = base::size(%(map_name)s);
-+''' % { 'map_name': GetMapName(root) }
-+
-+
-+def _FormatSource(get_key, root, lang, output_dir):
-+ from grit.node import include, structure, message
-+ id_map = root.GetIdMap()
-+ yield _FormatSourceHeader(root, output_dir)
-+ seen = set()
-+ for item in root.ActiveDescendants():
-+ if not item.IsResourceMapSource():
-+ continue
-+ key = get_key(item)
-+ tid = item.attrs['name']
-+ if tid not in id_map or key in seen:
-+ continue
-+ seen.add(key)
-+ yield ' {"%s", %s},\n' % (key, tid)
-+ yield _FormatSourceFooter(root)
-+
-+
-+def _GetItemName(item):
-+ return item.attrs['name']
-+
-+# Check if |path2| is a subpath of |path1|.
-+def _IsSubpath(path1, path2):
-+ path1_abs = os.path.abspath(path1)
-+ path2_abs = os.path.abspath(path2)
-+ common = os.path.commonprefix([path1_abs, path2_abs])
-+ return path1_abs == common
-+
-+def _GetItemPath(item):
-+ path = item.GetInputPath().replace("\\", "/")
-+
-+ # Handle the case where the file resides within the output folder,
-+ # by expanding any variables as well as replacing the output folder name with
-+ # a fixed string such that the key added to the map does not depend on a given
-+ # developer's setup.
-+ #
-+ # For example this will convert the following path:
-+ # ../../out/gchrome/${root_gen_dir}/ui/webui/resources/js/foo.js
-+ # to:
-+ # @out_folder@/gen/ui/webui/resources/js/foo.js
-+
-+ real_path = item.ToRealPath(item.GetInputPath())
-+ if (item.attrs.get('use_base_dir', 'true') != 'true' and
-+ _IsSubpath(os.path.curdir, real_path)):
-+ path = os.path.join(
-+ '@out_folder@', os.path.relpath(real_path)).replace("\\", "/")
-+
-+ assert '$' not in path, 'all variables should have been expanded'
-+ return path
-diff --git a/tools/grit/grit/format/resource_map_unittest.py b/tools/grit/grit/format/resource_map_unittest.py
-new file mode 100644
-index 0000000000..3499b321ef
---- /dev/null
-+++ b/tools/grit/grit/format/resource_map_unittest.py
-@@ -0,0 +1,345 @@
-+#!/usr/bin/env python
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Unit tests for grit.format.resource_map'''
-+
-+from __future__ import print_function
-+
-+import os
-+import sys
-+if __name__ == '__main__':
-+ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
-+
-+import unittest
-+
-+from grit import util
-+from grit.format import resource_map
-+
-+
-+class FormatResourceMapUnittest(unittest.TestCase):
-+ def testFormatResourceMap(self):
-+ grd = util.ParseGrdForUnittest('''
-+ <outputs>
-+ <output type="rc_header" filename="the_rc_header.h" />
-+ <output type="resource_map_header"
-+ filename="the_resource_map_header.h" />
-+ </outputs>
-+ <release seq="3">
-+ <structures first_id="300">
-+ <structure type="menu" name="IDC_KLONKMENU"
-+ file="grit\\testdata\\klonk.rc" encoding="utf-16" />
-+ </structures>
-+ <includes first_id="10000">
-+ <include type="foo" file="abc" name="IDS_FIRSTPRESENT" />
-+ <if expr="False">
-+ <include type="foo" file="def" name="IDS_MISSING" />
-+ </if>
-+ <if expr="lang != 'es'">
-+ <include type="foo" file="ghi" name="IDS_LANGUAGESPECIFIC" />
-+ </if>
-+ <if expr="lang == 'es'">
-+ <include type="foo" file="jkl" name="IDS_LANGUAGESPECIFIC" />
-+ </if>
-+ <include type="foo" file="mno" name="IDS_THIRDPRESENT" />
-+ <include type="foo" file="opq" name="IDS_FOURTHPRESENT"
-+ skip_in_resource_map="true" />
-+ </includes>
-+ </release>''', run_gatherers=True)
-+ output = util.StripBlankLinesAndComments(''.join(
-+ resource_map.GetFormatter('resource_map_header')(grd, 'en', '.')))
-+ self.assertEqual('''\
-+#include <stddef.h>
-+#ifndef GRIT_RESOURCE_MAP_STRUCT_
-+#define GRIT_RESOURCE_MAP_STRUCT_
-+struct GritResourceMap {
-+ const char* const name;
-+ int value;
-+};
-+#endif // GRIT_RESOURCE_MAP_STRUCT_
-+extern const GritResourceMap kTheRcHeader[];
-+extern const size_t kTheRcHeaderSize;''', output)
-+ output = util.StripBlankLinesAndComments(''.join(
-+ resource_map.GetFormatter('resource_map_source')(grd, 'en', '.')))
-+ self.assertEqual('''\
-+#include "the_resource_map_header.h"
-+#include <stddef.h>
-+#include "base/stl_util.h"
-+#include "the_rc_header.h"
-+const GritResourceMap kTheRcHeader[] = {
-+ {"IDC_KLONKMENU", IDC_KLONKMENU},
-+ {"IDS_FIRSTPRESENT", IDS_FIRSTPRESENT},
-+ {"IDS_LANGUAGESPECIFIC", IDS_LANGUAGESPECIFIC},
-+ {"IDS_THIRDPRESENT", IDS_THIRDPRESENT},
-+};
-+const size_t kTheRcHeaderSize = base::size(kTheRcHeader);''', output)
-+ output = util.StripBlankLinesAndComments(''.join(
-+ resource_map.GetFormatter('resource_file_map_source')(grd, 'en', '.')))
-+ self.assertEqual('''\
-+#include "the_resource_map_header.h"
-+#include <stddef.h>
-+#include "base/stl_util.h"
-+#include "the_rc_header.h"
-+const GritResourceMap kTheRcHeader[] = {
-+ {"grit/testdata/klonk.rc", IDC_KLONKMENU},
-+ {"abc", IDS_FIRSTPRESENT},
-+ {"ghi", IDS_LANGUAGESPECIFIC},
-+ {"mno", IDS_THIRDPRESENT},
-+};
-+const size_t kTheRcHeaderSize = base::size(kTheRcHeader);''', output)
-+
-+ def testFormatResourceMapWithGeneratedFile(self):
-+ os.environ["root_gen_dir"] = "gen"
-+
-+ grd = util.ParseGrdForUnittest('''\
-+ <outputs>
-+ <output type="rc_header" filename="the_rc_header.h" />
-+ <output type="resource_map_header"
-+ filename="resource_map_header.h" />
-+ </outputs>
-+ <release seq="3">
-+ <includes first_id="10000">
-+ <include type="BINDATA"
-+ file="${root_gen_dir}/foo/bar/baz.js"
-+ name="IDR_FOO_BAR_BAZ_JS"
-+ use_base_dir="false"
-+ compress="gzip" />
-+ </includes>
-+ </release>''', run_gatherers=True)
-+
-+ formatter = resource_map.GetFormatter('resource_file_map_source')
-+ output = util.StripBlankLinesAndComments(''.join(formatter(grd, 'en', '.')))
-+ expected = '''\
-+#include "resource_map_header.h"
-+#include <stddef.h>
-+#include "base/stl_util.h"
-+#include "the_rc_header.h"
-+const GritResourceMap kTheRcHeader[] = {
-+ {"@out_folder@/gen/foo/bar/baz.js", IDR_FOO_BAR_BAZ_JS},
-+};
-+const size_t kTheRcHeaderSize = base::size(kTheRcHeader);'''
-+ self.assertEqual(expected, output)
-+
-+ def testFormatResourceMapWithOutputAllEqualsFalseForStructures(self):
-+ grd = util.ParseGrdForUnittest('''
-+ <outputs>
-+ <output type="rc_header" filename="the_rc_header.h" />
-+ <output type="resource_map_header"
-+ filename="the_resource_map_header.h" />
-+ <output type="resource_map_source"
-+ filename="the_resource_map_header.cc" />
-+ </outputs>
-+ <release seq="3">
-+ <structures first_id="300">
-+ <structure type="chrome_scaled_image" name="IDR_KLONKMENU"
-+ file="foo.png" />
-+ <if expr="False">
-+ <structure type="chrome_scaled_image" name="IDR_MISSING"
-+ file="bar.png" />
-+ </if>
-+ <if expr="True">
-+ <structure type="chrome_scaled_image" name="IDR_BLOB"
-+ file="blob.png" />
-+ </if>
-+ <if expr="True">
-+ <then>
-+ <structure type="chrome_scaled_image" name="IDR_METEOR"
-+ file="meteor.png" />
-+ </then>
-+ <else>
-+ <structure type="chrome_scaled_image" name="IDR_METEOR"
-+ file="roetem.png" />
-+ </else>
-+ </if>
-+ <if expr="False">
-+ <structure type="chrome_scaled_image" name="IDR_LAST"
-+ file="zyx.png" />
-+ </if>
-+ <if expr="True">
-+ <structure type="chrome_scaled_image" name="IDR_LAST"
-+ file="xyz.png" />
-+ </if>
-+ </structures>
-+ </release>''', run_gatherers=True)
-+ output = util.StripBlankLinesAndComments(''.join(
-+ resource_map.GetFormatter('resource_map_header')(grd, 'en', '.')))
-+ self.assertEqual('''\
-+#include <stddef.h>
-+#ifndef GRIT_RESOURCE_MAP_STRUCT_
-+#define GRIT_RESOURCE_MAP_STRUCT_
-+struct GritResourceMap {
-+ const char* const name;
-+ int value;
-+};
-+#endif // GRIT_RESOURCE_MAP_STRUCT_
-+extern const GritResourceMap kTheRcHeader[];
-+extern const size_t kTheRcHeaderSize;''', output)
-+ output = util.StripBlankLinesAndComments(''.join(
-+ resource_map.GetFormatter('resource_map_source')(grd, 'en', '.')))
-+ self.assertEqual('''\
-+#include "the_resource_map_header.h"
-+#include <stddef.h>
-+#include "base/stl_util.h"
-+#include "the_rc_header.h"
-+const GritResourceMap kTheRcHeader[] = {
-+ {"IDR_KLONKMENU", IDR_KLONKMENU},
-+ {"IDR_BLOB", IDR_BLOB},
-+ {"IDR_METEOR", IDR_METEOR},
-+ {"IDR_LAST", IDR_LAST},
-+};
-+const size_t kTheRcHeaderSize = base::size(kTheRcHeader);''', output)
-+ output = util.StripBlankLinesAndComments(''.join(
-+ resource_map.GetFormatter('resource_map_source')(grd, 'en', '.')))
-+ self.assertEqual('''\
-+#include "the_resource_map_header.h"
-+#include <stddef.h>
-+#include "base/stl_util.h"
-+#include "the_rc_header.h"
-+const GritResourceMap kTheRcHeader[] = {
-+ {"IDR_KLONKMENU", IDR_KLONKMENU},
-+ {"IDR_BLOB", IDR_BLOB},
-+ {"IDR_METEOR", IDR_METEOR},
-+ {"IDR_LAST", IDR_LAST},
-+};
-+const size_t kTheRcHeaderSize = base::size(kTheRcHeader);''', output)
-+
-+ def testFormatResourceMapWithOutputAllEqualsFalseForIncludes(self):
-+ grd = util.ParseGrdForUnittest('''
-+ <outputs>
-+ <output type="rc_header" filename="the_rc_header.h" />
-+ <output type="resource_map_header"
-+ filename="the_resource_map_header.h" />
-+ </outputs>
-+ <release seq="3">
-+ <structures first_id="300">
-+ <structure type="menu" name="IDC_KLONKMENU"
-+ file="grit\\testdata\\klonk.rc" encoding="utf-16" />
-+ </structures>
-+ <includes first_id="10000">
-+ <include type="foo" file="abc" name="IDS_FIRSTPRESENT" />
-+ <if expr="False">
-+ <include type="foo" file="def" name="IDS_MISSING" />
-+ </if>
-+ <include type="foo" file="mno" name="IDS_THIRDPRESENT" />
-+ <if expr="True">
-+ <include type="foo" file="blob" name="IDS_BLOB" />
-+ </if>
-+ <if expr="True">
-+ <then>
-+ <include type="foo" file="meteor" name="IDS_METEOR" />
-+ </then>
-+ <else>
-+ <include type="foo" file="roetem" name="IDS_METEOR" />
-+ </else>
-+ </if>
-+ <if expr="False">
-+ <include type="foo" file="zyx" name="IDS_LAST" />
-+ </if>
-+ <if expr="True">
-+ <include type="foo" file="xyz" name="IDS_LAST" />
-+ </if>
-+ </includes>
-+ </release>''', run_gatherers=True)
-+ output = util.StripBlankLinesAndComments(''.join(
-+ resource_map.GetFormatter('resource_map_header')(grd, 'en', '.')))
-+ self.assertEqual('''\
-+#include <stddef.h>
-+#ifndef GRIT_RESOURCE_MAP_STRUCT_
-+#define GRIT_RESOURCE_MAP_STRUCT_
-+struct GritResourceMap {
-+ const char* const name;
-+ int value;
-+};
-+#endif // GRIT_RESOURCE_MAP_STRUCT_
-+extern const GritResourceMap kTheRcHeader[];
-+extern const size_t kTheRcHeaderSize;''', output)
-+ output = util.StripBlankLinesAndComments(''.join(
-+ resource_map.GetFormatter('resource_map_source')(grd, 'en', '.')))
-+ self.assertEqual('''\
-+#include "the_resource_map_header.h"
-+#include <stddef.h>
-+#include "base/stl_util.h"
-+#include "the_rc_header.h"
-+const GritResourceMap kTheRcHeader[] = {
-+ {"IDC_KLONKMENU", IDC_KLONKMENU},
-+ {"IDS_FIRSTPRESENT", IDS_FIRSTPRESENT},
-+ {"IDS_THIRDPRESENT", IDS_THIRDPRESENT},
-+ {"IDS_BLOB", IDS_BLOB},
-+ {"IDS_METEOR", IDS_METEOR},
-+ {"IDS_LAST", IDS_LAST},
-+};
-+const size_t kTheRcHeaderSize = base::size(kTheRcHeader);''', output)
-+ output = util.StripBlankLinesAndComments(''.join(
-+ resource_map.GetFormatter('resource_file_map_source')(grd, 'en', '.')))
-+ self.assertEqual('''\
-+#include "the_resource_map_header.h"
-+#include <stddef.h>
-+#include "base/stl_util.h"
-+#include "the_rc_header.h"
-+const GritResourceMap kTheRcHeader[] = {
-+ {"grit/testdata/klonk.rc", IDC_KLONKMENU},
-+ {"abc", IDS_FIRSTPRESENT},
-+ {"mno", IDS_THIRDPRESENT},
-+ {"blob", IDS_BLOB},
-+ {"meteor", IDS_METEOR},
-+ {"xyz", IDS_LAST},
-+};
-+const size_t kTheRcHeaderSize = base::size(kTheRcHeader);''', output)
-+
-+ def testFormatStringResourceMap(self):
-+ grd = util.ParseGrdForUnittest('''
-+ <outputs>
-+ <output type="rc_header" filename="the_rc_header.h" />
-+ <output type="resource_map_header" filename="the_rc_map_header.h" />
-+ <output type="resource_map_source" filename="the_rc_map_source.cc" />
-+ </outputs>
-+ <release seq="1" allow_pseudo="false">
-+ <messages fallback_to_english="true">
-+ <message name="IDS_PRODUCT_NAME" desc="The application name">
-+ Application
-+ </message>
-+ <if expr="True">
-+ <message name="IDS_DEFAULT_TAB_TITLE_TITLE_CASE"
-+ desc="In Title Case: The default title in a tab.">
-+ New Tab
-+ </message>
-+ </if>
-+ <if expr="False">
-+ <message name="IDS_DEFAULT_TAB_TITLE"
-+ desc="The default title in a tab.">
-+ New tab
-+ </message>
-+ </if>
-+ </messages>
-+ </release>''', run_gatherers=True)
-+ grd.InitializeIds()
-+ output = util.StripBlankLinesAndComments(''.join(
-+ resource_map.GetFormatter('resource_map_header')(grd, 'en', '.')))
-+ self.assertEqual('''\
-+#include <stddef.h>
-+#ifndef GRIT_RESOURCE_MAP_STRUCT_
-+#define GRIT_RESOURCE_MAP_STRUCT_
-+struct GritResourceMap {
-+ const char* const name;
-+ int value;
-+};
-+#endif // GRIT_RESOURCE_MAP_STRUCT_
-+extern const GritResourceMap kTheRcHeader[];
-+extern const size_t kTheRcHeaderSize;''', output)
-+ output = util.StripBlankLinesAndComments(''.join(
-+ resource_map.GetFormatter('resource_map_source')(grd, 'en', '.')))
-+ self.assertEqual('''\
-+#include "the_rc_map_header.h"
-+#include <stddef.h>
-+#include "base/stl_util.h"
-+#include "the_rc_header.h"
-+const GritResourceMap kTheRcHeader[] = {
-+ {"IDS_PRODUCT_NAME", IDS_PRODUCT_NAME},
-+ {"IDS_DEFAULT_TAB_TITLE_TITLE_CASE", IDS_DEFAULT_TAB_TITLE_TITLE_CASE},
-+};
-+const size_t kTheRcHeaderSize = base::size(kTheRcHeader);''', output)
-+
-+
-+if __name__ == '__main__':
-+ unittest.main()
-diff --git a/tools/grit/grit/gather/__init__.py b/tools/grit/grit/gather/__init__.py
-new file mode 100644
-index 0000000000..2d578f5643
---- /dev/null
-+++ b/tools/grit/grit/gather/__init__.py
-@@ -0,0 +1,8 @@
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Module grit.gather
-+'''
-+
-+pass
-diff --git a/tools/grit/grit/gather/admin_template.py b/tools/grit/grit/gather/admin_template.py
-new file mode 100644
-index 0000000000..c26b6a88d7
---- /dev/null
-+++ b/tools/grit/grit/gather/admin_template.py
-@@ -0,0 +1,62 @@
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Gatherer for administrative template files.
-+'''
-+
-+from __future__ import print_function
-+
-+import re
-+
-+from grit.gather import regexp
-+from grit import exception
-+from grit import lazy_re
-+
-+
-+class MalformedAdminTemplateException(exception.Base):
-+ '''This file doesn't look like a .adm file to me.'''
-+ pass
-+
-+
-+class AdmGatherer(regexp.RegexpGatherer):
-+ '''Gatherer for the translateable portions of an admin template.
-+
-+ This gatherer currently makes the following assumptions:
-+ - there is only one [strings] section and it is always the last section
-+ of the file
-+ - translateable strings do not need to be escaped.
-+ '''
-+
-+ # Finds the strings section as the group named 'strings'
-+ _STRINGS_SECTION = lazy_re.compile(
-+ r'(?P<first_part>.+^\[strings\])(?P<strings>.+)\Z',
-+ re.MULTILINE | re.DOTALL)
-+
-+ # Finds the translateable sections from within the [strings] section.
-+ _TRANSLATEABLES = lazy_re.compile(
-+ r'^\s*[A-Za-z0-9_]+\s*=\s*"(?P<text>.+)"\s*$',
-+ re.MULTILINE)
-+
-+ def Escape(self, text):
-+ return text.replace('\n', '\\n')
-+
-+ def UnEscape(self, text):
-+ return text.replace('\\n', '\n')
-+
-+ def Parse(self):
-+ if self.have_parsed_:
-+ return
-+ self.have_parsed_ = True
-+
-+ self.text_ = self._LoadInputFile().strip()
-+ m = self._STRINGS_SECTION.match(self.text_)
-+ if not m:
-+ raise MalformedAdminTemplateException()
-+ # Add the first part, which is all nontranslateable, to the skeleton
-+ self._AddNontranslateableChunk(m.group('first_part'))
-+ # Then parse the rest using the _TRANSLATEABLES regexp.
-+ self._RegExpParse(self._TRANSLATEABLES, m.group('strings'))
-+
-+ def GetTextualIds(self):
-+ return [self.extkey]
-diff --git a/tools/grit/grit/gather/admin_template_unittest.py b/tools/grit/grit/gather/admin_template_unittest.py
-new file mode 100644
-index 0000000000..c637af3a75
---- /dev/null
-+++ b/tools/grit/grit/gather/admin_template_unittest.py
-@@ -0,0 +1,115 @@
-+#!/usr/bin/env python
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Unit tests for the admin template gatherer.'''
-+
-+from __future__ import print_function
-+
-+import os
-+import sys
-+if __name__ == '__main__':
-+ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
-+
-+import unittest
-+
-+from six import StringIO
-+
-+from grit.gather import admin_template
-+from grit import util
-+from grit import grd_reader
-+from grit import grit_runner
-+from grit.tool import build
-+
-+
-+class AdmGathererUnittest(unittest.TestCase):
-+ def testParsingAndTranslating(self):
-+ pseudofile = StringIO(
-+ 'bingo bongo\n'
-+ 'ding dong\n'
-+ '[strings] \n'
-+ 'whatcha="bingo bongo"\n'
-+ 'gotcha = "bingolabongola "the wise" fingulafongula" \n')
-+ gatherer = admin_template.AdmGatherer(pseudofile)
-+ gatherer.Parse()
-+ self.failUnless(len(gatherer.GetCliques()) == 2)
-+ self.failUnless(gatherer.GetCliques()[1].GetMessage().GetRealContent() ==
-+ 'bingolabongola "the wise" fingulafongula')
-+
-+ translation = gatherer.Translate('en')
-+ self.failUnless(translation == gatherer.GetText().strip())
-+
-+ def testErrorHandling(self):
-+ pseudofile = StringIO(
-+ 'bingo bongo\n'
-+ 'ding dong\n'
-+ 'whatcha="bingo bongo"\n'
-+ 'gotcha = "bingolabongola "the wise" fingulafongula" \n')
-+ gatherer = admin_template.AdmGatherer(pseudofile)
-+ self.assertRaises(admin_template.MalformedAdminTemplateException,
-+ gatherer.Parse)
-+
-+ _TRANSLATABLES_FROM_FILE = (
-+ 'Google', 'Google Desktop', 'Preferences',
-+ 'Controls Google Desktop preferences',
-+ 'Indexing and Capture Control',
-+ 'Controls what files, web pages, and other content will be indexed by Google Desktop.',
-+ 'Prevent indexing of email',
-+ # there are lots more but we don't check any further
-+ )
-+
-+ def VerifyCliquesFromAdmFile(self, cliques):
-+ self.failUnless(len(cliques) > 20)
-+ for clique, expected in zip(cliques, self._TRANSLATABLES_FROM_FILE):
-+ text = clique.GetMessage().GetRealContent()
-+ self.failUnless(text == expected)
-+
-+ def testFromFile(self):
-+ fname = util.PathFromRoot('grit/testdata/GoogleDesktop.adm')
-+ gatherer = admin_template.AdmGatherer(fname)
-+ gatherer.Parse()
-+ cliques = gatherer.GetCliques()
-+ self.VerifyCliquesFromAdmFile(cliques)
-+
-+ def MakeGrd(self):
-+ grd = grd_reader.Parse(StringIO('''<?xml version="1.0" encoding="UTF-8"?>
-+ <grit latest_public_release="2" source_lang_id="en-US" current_release="3">
-+ <release seq="3">
-+ <structures>
-+ <structure type="admin_template" name="IDAT_GOOGLE_DESKTOP_SEARCH"
-+ file="GoogleDesktop.adm" exclude_from_rc="true" />
-+ <structure type="txt" name="BINGOBONGO"
-+ file="README.txt" exclude_from_rc="true" />
-+ </structures>
-+ </release>
-+ <outputs>
-+ <output filename="de_res.rc" type="rc_all" lang="de" />
-+ </outputs>
-+ </grit>'''), util.PathFromRoot('grit/testdata'))
-+ grd.SetOutputLanguage('en')
-+ grd.RunGatherers()
-+ return grd
-+
-+ def testInGrd(self):
-+ grd = self.MakeGrd()
-+ cliques = grd.children[0].children[0].children[0].GetCliques()
-+ self.VerifyCliquesFromAdmFile(cliques)
-+
-+ def testFileIsOutput(self):
-+ grd = self.MakeGrd()
-+ dirname = util.TempDir({})
-+ try:
-+ tool = build.RcBuilder()
-+ tool.o = grit_runner.Options()
-+ tool.output_directory = dirname.GetPath()
-+ tool.res = grd
-+ tool.Process()
-+
-+ self.failUnless(os.path.isfile(dirname.GetPath('de_GoogleDesktop.adm')))
-+ self.failUnless(os.path.isfile(dirname.GetPath('de_README.txt')))
-+ finally:
-+ dirname.CleanUp()
-+
-+if __name__ == '__main__':
-+ unittest.main()
-diff --git a/tools/grit/grit/gather/chrome_html.py b/tools/grit/grit/gather/chrome_html.py
-new file mode 100644
-index 0000000000..71c1332d66
---- /dev/null
-+++ b/tools/grit/grit/gather/chrome_html.py
-@@ -0,0 +1,377 @@
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+"""Prepares a Chrome HTML file by inlining resources and adding references to
-+high DPI resources and removing references to unsupported scale factors.
-+
-+This is a small gatherer that takes a HTML file, looks for src attributes
-+and inlines the specified file, producing one HTML file with no external
-+dependencies. It recursively inlines the included files. When inlining CSS
-+image files this script also checks for the existence of high DPI versions
-+of the inlined file including those on relevant platforms. Unsupported scale
-+factors are also removed from existing image sets to support explicitly
-+referencing all available images.
-+"""
-+
-+from __future__ import print_function
-+
-+import os
-+import re
-+
-+from grit import lazy_re
-+from grit import util
-+from grit.format import html_inline
-+from grit.gather import interface
-+
-+
-+# Distribution string to replace with distribution.
-+DIST_SUBSTR = '%DISTRIBUTION%'
-+
-+
-+# Matches a chrome theme source URL.
-+_THEME_SOURCE = lazy_re.compile(
-+ r'(?P<baseurl>chrome://theme/IDR_[A-Z0-9_]*)(?P<query>\?.*)?')
-+# Pattern for matching CSS url() function.
-+_CSS_URL_PATTERN = r'url\((?P<quote>"|\'|)(?P<filename>[^"\'()]*)(?P=quote)\)'
-+# Matches CSS url() functions with the capture group 'filename'.
-+_CSS_URL = lazy_re.compile(_CSS_URL_PATTERN)
-+# Matches one or more CSS image urls used in given properties.
-+_CSS_IMAGE_URLS = lazy_re.compile(
-+ r'(?P<attribute>content|background|[\w-]*-image):\s*'
-+ r'(?P<urls>(' + _CSS_URL_PATTERN + r'\s*,?\s*)+)')
-+# Matches CSS image sets.
-+_CSS_IMAGE_SETS = lazy_re.compile(
-+ r'(?P<attribute>content|background|[\w-]*-image):[ ]*'
-+ r'-webkit-image-set\((?P<images>'
-+ r'(\s*,?\s*url\((?P<quote>"|\'|)[^"\'()]*(?P=quote)\)[ ]*[0-9.]*x)*)\)',
-+ re.MULTILINE)
-+# Matches a single image in a CSS image set with the capture group scale.
-+_CSS_IMAGE_SET_IMAGE = lazy_re.compile(r'\s*,?\s*'
-+ r'url\((?P<quote>"|\'|)[^"\'()]*(?P=quote)\)[ ]*(?P<scale>[0-9.]*x)',
-+ re.MULTILINE)
-+_HTML_IMAGE_SRC = lazy_re.compile(
-+ r'<img[^>]+src=\"(?P<filename>[^">]*)\"[^>]*>')
-+
-+def GetImageList(
-+ base_path, filename, scale_factors, distribution,
-+ filename_expansion_function=None):
-+ """Generate the list of images which match the provided scale factors.
-+
-+ Takes an image filename and checks for files of the same name in folders
-+ corresponding to the supported scale factors. If the file is from a
-+ chrome://theme/ source, inserts supported @Nx scale factors as high DPI
-+ versions.
-+
-+ Args:
-+ base_path: path to look for relative file paths in
-+ filename: name of the base image file
-+ scale_factors: a list of the supported scale factors (i.e. ['2x'])
-+ distribution: string that should replace %DISTRIBUTION%
-+
-+ Returns:
-+ array of tuples containing scale factor and image (i.e.
-+ [('1x', 'image.png'), ('2x', '2x/image.png')]).
-+ """
-+ # Any matches for which a chrome URL handler will serve all scale factors
-+ # can simply request all scale factors.
-+ theme_match = _THEME_SOURCE.match(filename)
-+ if theme_match:
-+ images = [('1x', filename)]
-+ for scale_factor in scale_factors:
-+ scale_filename = "%s@%s" % (theme_match.group('baseurl'), scale_factor)
-+ if theme_match.group('query'):
-+ scale_filename += theme_match.group('query')
-+ images.append((scale_factor, scale_filename))
-+ return images
-+
-+ if filename.find(':') != -1:
-+ # filename is probably a URL, only return filename itself.
-+ return [('1x', filename)]
-+
-+ filename = filename.replace(DIST_SUBSTR, distribution)
-+ if filename_expansion_function:
-+ filename = filename_expansion_function(filename)
-+ filepath = os.path.join(base_path, filename)
-+ images = [('1x', filename)]
-+
-+ for scale_factor in scale_factors:
-+ # Check for existence of file and add to image set.
-+ scale_path = os.path.split(os.path.join(base_path, filename))
-+ scale_image_path = os.path.join(scale_path[0], scale_factor, scale_path[1])
-+ if os.path.isfile(scale_image_path):
-+ # HTML/CSS always uses forward slashed paths.
-+ parts = filename.rsplit('/', 1)
-+ if len(parts) == 1:
-+ path = ''
-+ else:
-+ path = parts[0] + '/'
-+ scale_image_name = path + scale_factor + '/' + parts[-1]
-+ images.append((scale_factor, scale_image_name))
-+ return images
-+
-+
-+def GenerateImageSet(images, quote):
-+ """Generates a -webkit-image-set for the provided list of images.
-+
-+ Args:
-+ images: an array of tuples giving scale factor and file path
-+ (i.e. [('1x', 'image.png'), ('2x', '2x/image.png')]).
-+ quote: a string giving the quotation character to use (i.e. "'")
-+
-+ Returns:
-+ string giving a -webkit-image-set rule referencing the provided images.
-+ (i.e. '-webkit-image-set(url('image.png') 1x, url('2x/image.png') 2x)')
-+ """
-+ imageset = []
-+ for (scale_factor, filename) in images:
-+ imageset.append("url(%s%s%s) %s" % (quote, filename, quote, scale_factor))
-+ return "-webkit-image-set(%s)" % (', '.join(imageset))
-+
-+
-+def UrlToImageSet(
-+ src_match, base_path, scale_factors, distribution,
-+ filename_expansion_function=None):
-+ """Regex replace function which replaces url() with -webkit-image-set.
-+
-+ Takes a regex match for url('path'). If the file is local, checks for
-+ files of the same name in folders corresponding to the supported scale
-+ factors. If the file is from a chrome://theme/ source, inserts the
-+ supported @Nx scale factor request. In either case inserts a
-+ -webkit-image-set rule to fetch the appropriate image for the current
-+ scale factor.
-+
-+ Args:
-+ src_match: regex match object from _CSS_URLS
-+ base_path: path to look for relative file paths in
-+ scale_factors: a list of the supported scale factors (i.e. ['2x'])
-+ distribution: string that should replace %DISTRIBUTION%.
-+
-+ Returns:
-+ string
-+ """
-+ quote = src_match.group('quote')
-+ filename = src_match.group('filename')
-+ image_list = GetImageList(
-+ base_path, filename, scale_factors, distribution,
-+ filename_expansion_function=filename_expansion_function)
-+
-+ # Don't modify the source if there is only one image.
-+ if len(image_list) == 1:
-+ return src_match.group(0)
-+
-+ return GenerateImageSet(image_list, quote)
-+
-+
-+def InsertImageSet(
-+ src_match, base_path, scale_factors, distribution,
-+ filename_expansion_function=None):
-+ """Regex replace function which inserts -webkit-image-set rules.
-+
-+ Takes a regex match for `property: url('path')[, url('path')]+`.
-+ Replaces one or more occurances of the match with image set rules.
-+
-+ Args:
-+ src_match: regex match object from _CSS_IMAGE_URLS
-+ base_path: path to look for relative file paths in
-+ scale_factors: a list of the supported scale factors (i.e. ['2x'])
-+ distribution: string that should replace %DISTRIBUTION%.
-+
-+ Returns:
-+ string
-+ """
-+ attr = src_match.group('attribute')
-+ urls = _CSS_URL.sub(
-+ lambda m: UrlToImageSet(m, base_path, scale_factors, distribution,
-+ filename_expansion_function),
-+ src_match.group('urls'))
-+
-+ return "%s: %s" % (attr, urls)
-+
-+
-+def InsertImageStyle(
-+ src_match, base_path, scale_factors, distribution,
-+ filename_expansion_function=None):
-+ """Regex replace function which adds a content style to an <img>.
-+
-+ Takes a regex match from _HTML_IMAGE_SRC and replaces the attribute with a CSS
-+ style which defines the image set.
-+ """
-+ filename = src_match.group('filename')
-+ image_list = GetImageList(
-+ base_path, filename, scale_factors, distribution,
-+ filename_expansion_function=filename_expansion_function)
-+
-+ # Don't modify the source if there is only one image or image already defines
-+ # a style.
-+ if src_match.group(0).find(" style=\"") != -1 or len(image_list) == 1:
-+ return src_match.group(0)
-+
-+ return "%s style=\"content: %s;\">" % (src_match.group(0)[:-1],
-+ GenerateImageSet(image_list, "'"))
-+
-+
-+def InsertImageSets(
-+ filepath, text, scale_factors, distribution,
-+ filename_expansion_function=None):
-+ """Helper function that adds references to external images available in any of
-+ scale_factors in CSS backgrounds.
-+ """
-+ # Add high DPI urls for css attributes: content, background,
-+ # or *-image or <img src="foo">.
-+ return _CSS_IMAGE_URLS.sub(
-+ lambda m: InsertImageSet(
-+ m, filepath, scale_factors, distribution,
-+ filename_expansion_function=filename_expansion_function),
-+ _HTML_IMAGE_SRC.sub(
-+ lambda m: InsertImageStyle(
-+ m, filepath, scale_factors, distribution,
-+ filename_expansion_function=filename_expansion_function),
-+ text))
-+
-+
-+def RemoveImagesNotIn(scale_factors, src_match):
-+ """Regex replace function which removes images for scale factors not in
-+ scale_factors.
-+
-+ Takes a regex match for _CSS_IMAGE_SETS. For each image in the group images,
-+ checks if this scale factor is in scale_factors and if not, removes it.
-+
-+ Args:
-+ scale_factors: a list of the supported scale factors (i.e. ['1x', '2x'])
-+ src_match: regex match object from _CSS_IMAGE_SETS
-+
-+ Returns:
-+ string
-+ """
-+ attr = src_match.group('attribute')
-+ images = _CSS_IMAGE_SET_IMAGE.sub(
-+ lambda m: m.group(0) if m.group('scale') in scale_factors else '',
-+ src_match.group('images'))
-+ return "%s: -webkit-image-set(%s)" % (attr, images)
-+
-+
-+def RemoveImageSetImages(text, scale_factors):
-+ """Helper function which removes images in image sets not in the list of
-+ supported scale_factors.
-+ """
-+ return _CSS_IMAGE_SETS.sub(
-+ lambda m: RemoveImagesNotIn(scale_factors, m), text)
-+
-+
-+def ProcessImageSets(
-+ filepath, text, scale_factors, distribution,
-+ filename_expansion_function=None):
-+ """Helper function that adds references to external images available in other
-+ scale_factors and removes images from image-sets in unsupported scale_factors.
-+ """
-+ # Explicitly add 1x to supported scale factors so that it is not removed.
-+ supported_scale_factors = ['1x']
-+ supported_scale_factors.extend(scale_factors)
-+ return InsertImageSets(
-+ filepath,
-+ RemoveImageSetImages(text, supported_scale_factors),
-+ scale_factors,
-+ distribution,
-+ filename_expansion_function=filename_expansion_function)
-+
-+
-+class ChromeHtml(interface.GathererBase):
-+ """Represents an HTML document processed for Chrome WebUI.
-+
-+ HTML documents used in Chrome WebUI have local resources inlined and
-+ automatically insert references to high DPI assets used in CSS properties
-+ with the use of the -webkit-image-set value. References to unsupported scale
-+ factors in image sets are also removed. This does not generate any
-+ translateable messages and instead generates a single DataPack resource.
-+ """
-+
-+ def __init__(self, *args, **kwargs):
-+ super(ChromeHtml, self).__init__(*args, **kwargs)
-+ self.allow_external_script_ = False
-+ self.flatten_html_ = False
-+ self.preprocess_only_ = False
-+ # 1x resources are implicitly already in the source and do not need to be
-+ # added.
-+ self.scale_factors_ = []
-+ self.filename_expansion_function = None
-+
-+ def SetAttributes(self, attrs):
-+ self.allow_external_script_ = ('allowexternalscript' in attrs and
-+ attrs['allowexternalscript'] == 'true')
-+ self.preprocess_only_ = ('preprocess' in attrs and
-+ attrs['preprocess'] == 'true')
-+ self.flatten_html_ = (self.preprocess_only_ or ('flattenhtml' in attrs and
-+ attrs['flattenhtml'] == 'true'))
-+
-+ def SetDefines(self, defines):
-+ if 'scale_factors' in defines:
-+ self.scale_factors_ = defines['scale_factors'].split(',')
-+
-+ def GetText(self):
-+ """Returns inlined text of the HTML document."""
-+ return self.inlined_text_
-+
-+ def GetTextualIds(self):
-+ return [self.extkey]
-+
-+ def GetData(self, lang, encoding):
-+ """Returns inlined text of the HTML document."""
-+ ret = self.inlined_text_
-+ if encoding == util.BINARY:
-+ ret = ret.encode('utf-8')
-+ return ret
-+
-+ def GetHtmlResourceFilenames(self):
-+ """Returns a set of all filenames inlined by this file."""
-+ if self.flatten_html_:
-+ return html_inline.GetResourceFilenames(
-+ self.grd_node.ToRealPath(self.GetInputPath()),
-+ self.grd_node,
-+ allow_external_script=self.allow_external_script_,
-+ rewrite_function=lambda fp, t, d: ProcessImageSets(
-+ fp, t, self.scale_factors_, d,
-+ filename_expansion_function=self.filename_expansion_function),
-+ filename_expansion_function=self.filename_expansion_function)
-+ return []
-+
-+ def Translate(self, lang, pseudo_if_not_available=True,
-+ skeleton_gatherer=None, fallback_to_english=False):
-+ """Returns this document translated."""
-+ return self.inlined_text_
-+
-+ def SetFilenameExpansionFunction(self, fn):
-+ self.filename_expansion_function = fn
-+
-+ def Parse(self):
-+ """Parses and inlines the represented file."""
-+
-+ filename = self.GetInputPath()
-+ # If there is a grd_node, prefer its GetInputPath(), as that may do more
-+ # processing to make the call to ToRealPath() below work correctly.
-+ if self.grd_node:
-+ filename = self.grd_node.GetInputPath()
-+ if self.filename_expansion_function:
-+ filename = self.filename_expansion_function(filename)
-+ # Hack: some unit tests supply an absolute path and no root node.
-+ if not os.path.isabs(filename):
-+ filename = self.grd_node.ToRealPath(filename)
-+ if self.flatten_html_:
-+ self.inlined_text_ = html_inline.InlineToString(
-+ filename,
-+ self.grd_node,
-+ allow_external_script = self.allow_external_script_,
-+ strip_whitespace=True,
-+ preprocess_only = self.preprocess_only_,
-+ rewrite_function=lambda fp, t, d: ProcessImageSets(
-+ fp, t, self.scale_factors_, d,
-+ filename_expansion_function=self.filename_expansion_function),
-+ filename_expansion_function=self.filename_expansion_function)
-+ else:
-+ distribution = html_inline.GetDistribution()
-+ self.inlined_text_ = ProcessImageSets(
-+ os.path.dirname(filename),
-+ util.ReadFile(filename, 'utf-8'),
-+ self.scale_factors_,
-+ distribution,
-+ filename_expansion_function=self.filename_expansion_function)
-diff --git a/tools/grit/grit/gather/chrome_html_unittest.py b/tools/grit/grit/gather/chrome_html_unittest.py
-new file mode 100644
-index 0000000000..8c75ee5bf4
---- /dev/null
-+++ b/tools/grit/grit/gather/chrome_html_unittest.py
-@@ -0,0 +1,610 @@
-+#!/usr/bin/env python
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Unit tests for grit.gather.chrome_html'''
-+
-+from __future__ import print_function
-+
-+import os
-+import re
-+import sys
-+if __name__ == '__main__':
-+ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
-+
-+import unittest
-+
-+from grit import lazy_re
-+from grit import util
-+from grit.gather import chrome_html
-+
-+
-+_NEW_LINE = lazy_re.compile('(\r\n|\r|\n)', re.MULTILINE)
-+
-+
-+def StandardizeHtml(text):
-+ '''Standardizes the newline format and png mime type in Html text.'''
-+ return _NEW_LINE.sub('\n', text).replace('data:image/x-png;',
-+ 'data:image/png;')
-+
-+
-+class ChromeHtmlUnittest(unittest.TestCase):
-+ '''Unit tests for ChromeHtml.'''
-+
-+ def testFileResources(self):
-+ '''Tests inlined image file resources with available high DPI assets.'''
-+
-+ tmp_dir = util.TempDir({
-+ 'index.html': '''
-+ <!DOCTYPE HTML>
-+ <html>
-+ <head>
-+ <link rel="stylesheet" href="test.css">
-+ </head>
-+ <body>
-+ <!-- Don't need a body. -->
-+ </body>
-+ </html>
-+ ''',
-+
-+ 'test.css': '''
-+ .image {
-+ background: url('test.png');
-+ }
-+ ''',
-+
-+ 'test.png': 'PNG DATA',
-+
-+ '1.4x/test.png': '1.4x PNG DATA',
-+
-+ '1.8x/test.png': '1.8x PNG DATA',
-+ })
-+
-+ html = chrome_html.ChromeHtml(tmp_dir.GetPath('index.html'))
-+ html.SetDefines({'scale_factors': '1.4x,1.8x'})
-+ html.SetAttributes({'flattenhtml': 'true'})
-+ html.Parse()
-+ self.failUnlessEqual(StandardizeHtml(html.GetData('en', 'utf-8')),
-+ StandardizeHtml('''
-+ <!DOCTYPE HTML>
-+ <html>
-+ <head>
-+ <style>
-+ .image {
-+ background: -webkit-image-set(url('data:image/png;base64,UE5HIERBVEE=') 1x, url('data:image/png;base64,MS40eCBQTkcgREFUQQ==') 1.4x, url('data:image/png;base64,MS44eCBQTkcgREFUQQ==') 1.8x);
-+ }
-+ </style>
-+ </head>
-+ <body>
-+ <!-- Don't need a body. -->
-+ </body>
-+ </html>
-+ '''))
-+ tmp_dir.CleanUp()
-+
-+ def testFileResourcesImageTag(self):
-+ '''Tests inlined image file resources with available high DPI assets on
-+ an image tag.'''
-+
-+ tmp_dir = util.TempDir({
-+ 'index.html': '''
-+ <!DOCTYPE HTML>
-+ <html>
-+ <body>
-+ <img id="foo" src="test.png">
-+ </body>
-+ </html>
-+ ''',
-+
-+ 'test.png': 'PNG DATA',
-+
-+ '2x/test.png': '2x PNG DATA',
-+ })
-+
-+ html = chrome_html.ChromeHtml(tmp_dir.GetPath('index.html'))
-+ html.SetDefines({'scale_factors': '2x'})
-+ html.SetAttributes({'flattenhtml': 'true'})
-+ html.Parse()
-+ self.failUnlessEqual(StandardizeHtml(html.GetData('en', 'utf-8')),
-+ StandardizeHtml('''
-+ <!DOCTYPE HTML>
-+ <html>
-+ <body>
-+ <img id="foo" src="data:image/png;base64,UE5HIERBVEE=" style="content: -webkit-image-set(url('data:image/png;base64,UE5HIERBVEE=') 1x, url('data:image/png;base64,MnggUE5HIERBVEE=') 2x);">
-+ </body>
-+ </html>
-+ '''))
-+ tmp_dir.CleanUp()
-+
-+ def testFileResourcesNoFlatten(self):
-+ '''Tests non-inlined image file resources with available high DPI assets.'''
-+
-+ tmp_dir = util.TempDir({
-+ 'test.css': '''
-+ .image {
-+ background: url('test.png');
-+ }
-+ ''',
-+
-+ 'test.png': 'PNG DATA',
-+
-+ '1.4x/test.png': '1.4x PNG DATA',
-+
-+ '1.8x/test.png': '1.8x PNG DATA',
-+ })
-+
-+ html = chrome_html.ChromeHtml(tmp_dir.GetPath('test.css'))
-+ html.SetDefines({'scale_factors': '1.4x,1.8x'})
-+ html.SetAttributes({'flattenhtml': 'false'})
-+ html.Parse()
-+ self.failUnlessEqual(StandardizeHtml(html.GetData('en', 'utf-8')),
-+ StandardizeHtml('''
-+ .image {
-+ background: -webkit-image-set(url('test.png') 1x, url('1.4x/test.png') 1.4x, url('1.8x/test.png') 1.8x);
-+ }
-+ '''))
-+ tmp_dir.CleanUp()
-+
-+ def testFileResourcesNoFlattenSubdir(self):
-+ '''Tests non-inlined image file resources w/high DPI assets in subdirs.'''
-+
-+ tmp_dir = util.TempDir({
-+ 'test.css': '''
-+ .image {
-+ background: url('sub/test.png');
-+ }
-+ ''',
-+
-+ 'sub/test.png': 'PNG DATA',
-+
-+ 'sub/1.4x/test.png': '1.4x PNG DATA',
-+
-+ 'sub/1.8x/test.png': '1.8x PNG DATA',
-+ })
-+
-+ html = chrome_html.ChromeHtml(tmp_dir.GetPath('test.css'))
-+ html.SetDefines({'scale_factors': '1.4x,1.8x'})
-+ html.SetAttributes({'flattenhtml': 'false'})
-+ html.Parse()
-+ self.failUnlessEqual(StandardizeHtml(html.GetData('en', 'utf-8')),
-+ StandardizeHtml('''
-+ .image {
-+ background: -webkit-image-set(url('sub/test.png') 1x, url('sub/1.4x/test.png') 1.4x, url('sub/1.8x/test.png') 1.8x);
-+ }
-+ '''))
-+ tmp_dir.CleanUp()
-+
-+ def testFileResourcesPreprocess(self):
-+ '''Tests preprocessed image file resources with available high DPI
-+ assets.'''
-+
-+ tmp_dir = util.TempDir({
-+ 'test.css': '''
-+ .image {
-+ background: url('test.png');
-+ }
-+ ''',
-+
-+ 'test.png': 'PNG DATA',
-+
-+ '1.4x/test.png': '1.4x PNG DATA',
-+
-+ '1.8x/test.png': '1.8x PNG DATA',
-+ })
-+
-+ html = chrome_html.ChromeHtml(tmp_dir.GetPath('test.css'))
-+ html.SetDefines({'scale_factors': '1.4x,1.8x'})
-+ html.SetAttributes({'flattenhtml': 'false', 'preprocess': 'true'})
-+ html.Parse()
-+ self.failUnlessEqual(StandardizeHtml(html.GetData('en', 'utf-8')),
-+ StandardizeHtml('''
-+ .image {
-+ background: -webkit-image-set(url('test.png') 1x, url('1.4x/test.png') 1.4x, url('1.8x/test.png') 1.8x);
-+ }
-+ '''))
-+ tmp_dir.CleanUp()
-+
-+ def testFileResourcesDoubleQuotes(self):
-+ '''Tests inlined image file resources if url() filename is double quoted.'''
-+
-+ tmp_dir = util.TempDir({
-+ 'test.css': '''
-+ .image {
-+ background: url("test.png");
-+ }
-+ ''',
-+
-+ 'test.png': 'PNG DATA',
-+
-+ '2x/test.png': '2x PNG DATA',
-+ })
-+
-+ html = chrome_html.ChromeHtml(tmp_dir.GetPath('test.css'))
-+ html.SetDefines({'scale_factors': '2x'})
-+ html.SetAttributes({'flattenhtml': 'true'})
-+ html.Parse()
-+ self.failUnlessEqual(StandardizeHtml(html.GetData('en', 'utf-8')),
-+ StandardizeHtml('''
-+ .image {
-+ background: -webkit-image-set(url("data:image/png;base64,UE5HIERBVEE=") 1x, url("data:image/png;base64,MnggUE5HIERBVEE=") 2x);
-+ }
-+ '''))
-+ tmp_dir.CleanUp()
-+
-+ def testFileResourcesNoQuotes(self):
-+ '''Tests inlined image file resources when url() filename is unquoted.'''
-+
-+ tmp_dir = util.TempDir({
-+ 'test.css': '''
-+ .image {
-+ background: url(test.png);
-+ }
-+ ''',
-+
-+ 'test.png': 'PNG DATA',
-+
-+ '2x/test.png': '2x PNG DATA',
-+ })
-+
-+ html = chrome_html.ChromeHtml(tmp_dir.GetPath('test.css'))
-+ html.SetDefines({'scale_factors': '2x'})
-+ html.SetAttributes({'flattenhtml': 'true'})
-+ html.Parse()
-+ self.failUnlessEqual(StandardizeHtml(html.GetData('en', 'utf-8')),
-+ StandardizeHtml('''
-+ .image {
-+ background: -webkit-image-set(url(data:image/png;base64,UE5HIERBVEE=) 1x, url(data:image/png;base64,MnggUE5HIERBVEE=) 2x);
-+ }
-+ '''))
-+ tmp_dir.CleanUp()
-+
-+ def testFileResourcesSubdirs(self):
-+ '''Tests inlined image file resources if url() filename is in a subdir.'''
-+
-+ tmp_dir = util.TempDir({
-+ 'test.css': '''
-+ .image {
-+ background: url('some/sub/path/test.png');
-+ }
-+ ''',
-+
-+ 'some/sub/path/test.png': 'PNG DATA',
-+
-+ 'some/sub/path/2x/test.png': '2x PNG DATA',
-+ })
-+
-+ html = chrome_html.ChromeHtml(tmp_dir.GetPath('test.css'))
-+ html.SetDefines({'scale_factors': '2x'})
-+ html.SetAttributes({'flattenhtml': 'true'})
-+ html.Parse()
-+ self.failUnlessEqual(StandardizeHtml(html.GetData('en', 'utf-8')),
-+ StandardizeHtml('''
-+ .image {
-+ background: -webkit-image-set(url('data:image/png;base64,UE5HIERBVEE=') 1x, url('data:image/png;base64,MnggUE5HIERBVEE=') 2x);
-+ }
-+ '''))
-+ tmp_dir.CleanUp()
-+
-+ def testFileResourcesNoFile(self):
-+ '''Tests inlined image file resources without available high DPI assets.'''
-+
-+ tmp_dir = util.TempDir({
-+ 'index.html': '''
-+ <!DOCTYPE HTML>
-+ <html>
-+ <head>
-+ <link rel="stylesheet" href="test.css">
-+ </head>
-+ <body>
-+ <!-- Don't need a body. -->
-+ </body>
-+ </html>
-+ ''',
-+
-+ 'test.css': '''
-+ .image {
-+ background: url('test.png');
-+ }
-+ ''',
-+
-+ 'test.png': 'PNG DATA',
-+ })
-+
-+ html = chrome_html.ChromeHtml(tmp_dir.GetPath('index.html'))
-+ html.SetDefines({'scale_factors': '2x'})
-+ html.SetAttributes({'flattenhtml': 'true'})
-+ html.Parse()
-+ self.failUnlessEqual(StandardizeHtml(html.GetData('en', 'utf-8')),
-+ StandardizeHtml('''
-+ <!DOCTYPE HTML>
-+ <html>
-+ <head>
-+ <style>
-+ .image {
-+ background: url('data:image/png;base64,UE5HIERBVEE=');
-+ }
-+ </style>
-+ </head>
-+ <body>
-+ <!-- Don't need a body. -->
-+ </body>
-+ </html>
-+ '''))
-+ tmp_dir.CleanUp()
-+
-+ def testFileResourcesMultipleBackgrounds(self):
-+ '''Tests inlined image file resources with two url()s.'''
-+
-+ tmp_dir = util.TempDir({
-+ 'test.css': '''
-+ .image {
-+ background: url(test.png), url(test.png);
-+ }
-+ ''',
-+
-+ 'test.png': 'PNG DATA',
-+
-+ '2x/test.png': '2x PNG DATA',
-+ })
-+
-+ html = chrome_html.ChromeHtml(tmp_dir.GetPath('test.css'))
-+ html.SetDefines({'scale_factors': '2x'})
-+ html.SetAttributes({'flattenhtml': 'true'})
-+ html.Parse()
-+ self.failUnlessEqual(StandardizeHtml(html.GetData('en', 'utf-8')),
-+ StandardizeHtml('''
-+ .image {
-+ background: -webkit-image-set(url(data:image/png;base64,UE5HIERBVEE=) 1x, url(data:image/png;base64,MnggUE5HIERBVEE=) 2x), -webkit-image-set(url(data:image/png;base64,UE5HIERBVEE=) 1x, url(data:image/png;base64,MnggUE5HIERBVEE=) 2x);
-+ }
-+ '''))
-+ tmp_dir.CleanUp()
-+
-+ def testFileResourcesMultipleBackgroundsWithNewline1(self):
-+ '''Tests inlined image file resources with line break after first url().'''
-+
-+ tmp_dir = util.TempDir({
-+ 'test.css': '''
-+ .image {
-+ background: url(test.png),
-+ url(test.png);
-+ }
-+ ''',
-+
-+ 'test.png': 'PNG DATA',
-+
-+ '2x/test.png': '2x PNG DATA',
-+ })
-+
-+ html = chrome_html.ChromeHtml(tmp_dir.GetPath('test.css'))
-+ html.SetDefines({'scale_factors': '2x'})
-+ html.SetAttributes({'flattenhtml': 'true'})
-+ html.Parse()
-+ self.failUnlessEqual(StandardizeHtml(html.GetData('en', 'utf-8')),
-+ StandardizeHtml('''
-+ .image {
-+ background: -webkit-image-set(url(data:image/png;base64,UE5HIERBVEE=) 1x, url(data:image/png;base64,MnggUE5HIERBVEE=) 2x),
-+ -webkit-image-set(url(data:image/png;base64,UE5HIERBVEE=) 1x, url(data:image/png;base64,MnggUE5HIERBVEE=) 2x);
-+ }
-+ '''))
-+ tmp_dir.CleanUp()
-+
-+ def testFileResourcesMultipleBackgroundsWithNewline2(self):
-+ '''Tests inlined image file resources with line break before first url()
-+ and before second url().'''
-+
-+ tmp_dir = util.TempDir({
-+ 'test.css': '''
-+ .image {
-+ background:
-+ url(test.png),
-+ url(test.png);
-+ }
-+ ''',
-+
-+ 'test.png': 'PNG DATA',
-+
-+ '2x/test.png': '2x PNG DATA',
-+ })
-+
-+ html = chrome_html.ChromeHtml(tmp_dir.GetPath('test.css'))
-+ html.SetDefines({'scale_factors': '2x'})
-+ html.SetAttributes({'flattenhtml': 'true'})
-+ html.Parse()
-+ self.failUnlessEqual(StandardizeHtml(html.GetData('en', 'utf-8')),
-+ StandardizeHtml('''
-+ .image {
-+ background: -webkit-image-set(url(data:image/png;base64,UE5HIERBVEE=) 1x, url(data:image/png;base64,MnggUE5HIERBVEE=) 2x),
-+ -webkit-image-set(url(data:image/png;base64,UE5HIERBVEE=) 1x, url(data:image/png;base64,MnggUE5HIERBVEE=) 2x);
-+ }
-+ '''))
-+ tmp_dir.CleanUp()
-+
-+ def testFileResourcesCRLF(self):
-+ '''Tests inlined image file resource when url() is preceded by a Windows
-+ style line break.'''
-+
-+ tmp_dir = util.TempDir({
-+ 'test.css': '''
-+ .image {
-+ background:\r\nurl(test.png);
-+ }
-+ ''',
-+
-+ 'test.png': 'PNG DATA',
-+
-+ '2x/test.png': '2x PNG DATA',
-+ })
-+
-+ html = chrome_html.ChromeHtml(tmp_dir.GetPath('test.css'))
-+ html.SetDefines({'scale_factors': '2x'})
-+ html.SetAttributes({'flattenhtml': 'true'})
-+ html.Parse()
-+ self.failUnlessEqual(StandardizeHtml(html.GetData('en', 'utf-8')),
-+ StandardizeHtml('''
-+ .image {
-+ background: -webkit-image-set(url(data:image/png;base64,UE5HIERBVEE=) 1x, url(data:image/png;base64,MnggUE5HIERBVEE=) 2x);
-+ }
-+ '''))
-+ tmp_dir.CleanUp()
-+
-+ def testThemeResources(self):
-+ '''Tests inserting high DPI chrome://theme references.'''
-+
-+ tmp_dir = util.TempDir({
-+ 'index.html': '''
-+ <!DOCTYPE HTML>
-+ <html>
-+ <head>
-+ <link rel="stylesheet" href="test.css">
-+ </head>
-+ <body>
-+ <!-- Don't need a body. -->
-+ </body>
-+ </html>
-+ ''',
-+
-+ 'test.css': '''
-+ .image {
-+ background: url('chrome://theme/IDR_RESOURCE_NAME');
-+ content: url('chrome://theme/IDR_RESOURCE_NAME_WITH_Q?$1');
-+ }
-+ ''',
-+ })
-+
-+ html = chrome_html.ChromeHtml(tmp_dir.GetPath('index.html'))
-+ html.SetDefines({'scale_factors': '2x'})
-+ html.SetAttributes({'flattenhtml': 'true'})
-+ html.Parse()
-+ self.failUnlessEqual(StandardizeHtml(html.GetData('en', 'utf-8')),
-+ StandardizeHtml('''
-+ <!DOCTYPE HTML>
-+ <html>
-+ <head>
-+ <style>
-+ .image {
-+ background: -webkit-image-set(url('chrome://theme/IDR_RESOURCE_NAME') 1x, url('chrome://theme/IDR_RESOURCE_NAME@2x') 2x);
-+ content: -webkit-image-set(url('chrome://theme/IDR_RESOURCE_NAME_WITH_Q?$1') 1x, url('chrome://theme/IDR_RESOURCE_NAME_WITH_Q@2x?$1') 2x);
-+ }
-+ </style>
-+ </head>
-+ <body>
-+ <!-- Don't need a body. -->
-+ </body>
-+ </html>
-+ '''))
-+ tmp_dir.CleanUp()
-+
-+ def testRemoveUnsupportedScale(self):
-+ '''Tests removing an unsupported scale factor from an explicit image-set.'''
-+
-+ tmp_dir = util.TempDir({
-+ 'index.html': '''
-+ <!DOCTYPE HTML>
-+ <html>
-+ <head>
-+ <link rel="stylesheet" href="test.css">
-+ </head>
-+ <body>
-+ <!-- Don't need a body. -->
-+ </body>
-+ </html>
-+ ''',
-+
-+ 'test.css': '''
-+ .image {
-+ background: -webkit-image-set(url('test.png') 1x,
-+ url('test1.4.png') 1.4x,
-+ url('test1.8.png') 1.8x);
-+ }
-+ ''',
-+
-+ 'test.png': 'PNG DATA',
-+
-+ 'test1.4.png': '1.4x PNG DATA',
-+
-+ 'test1.8.png': '1.8x PNG DATA',
-+ })
-+
-+ html = chrome_html.ChromeHtml(tmp_dir.GetPath('index.html'))
-+ html.SetDefines({'scale_factors': '1.8x'})
-+ html.SetAttributes({'flattenhtml': 'true'})
-+ html.Parse()
-+ self.failUnlessEqual(StandardizeHtml(html.GetData('en', 'utf-8')),
-+ StandardizeHtml('''
-+ <!DOCTYPE HTML>
-+ <html>
-+ <head>
-+ <style>
-+ .image {
-+ background: -webkit-image-set(url('data:image/png;base64,UE5HIERBVEE=') 1x,
-+ url('data:image/png;base64,MS44eCBQTkcgREFUQQ==') 1.8x);
-+ }
-+ </style>
-+ </head>
-+ <body>
-+ <!-- Don't need a body. -->
-+ </body>
-+ </html>
-+ '''))
-+ tmp_dir.CleanUp()
-+
-+ def testExpandVariablesInFilename(self):
-+ '''
-+ Tests variable substitution in filenames while flattening images
-+ with multiple scale factors.
-+ '''
-+
-+ tmp_dir = util.TempDir({
-+ 'index.html': '''
-+ <!DOCTYPE HTML>
-+ <html>
-+ <head>
-+ <link rel="stylesheet" href="test.css">
-+ </head>
-+ <body>
-+ <!-- Don't need a body. -->
-+ </body>
-+ </html>
-+ ''',
-+
-+ 'test.css': '''
-+ .image {
-+ background: url('test[WHICH].png');
-+ }
-+ ''',
-+
-+ 'test1.png': 'PNG DATA',
-+ '1.4x/test1.png': '1.4x PNG DATA',
-+ '1.8x/test1.png': '1.8x PNG DATA',
-+ })
-+
-+ def replacer(var, repl):
-+ return lambda filename: filename.replace('[%s]' % var, repl)
-+
-+ html = chrome_html.ChromeHtml(tmp_dir.GetPath('index.html'))
-+ html.SetDefines({'scale_factors': '1.4x,1.8x'})
-+ html.SetAttributes({'flattenhtml': 'true'})
-+ html.SetFilenameExpansionFunction(replacer('WHICH', '1'));
-+ html.Parse()
-+ self.failUnlessEqual(StandardizeHtml(html.GetData('en', 'utf-8')),
-+ StandardizeHtml('''
-+ <!DOCTYPE HTML>
-+ <html>
-+ <head>
-+ <style>
-+ .image {
-+ background: -webkit-image-set(url('data:image/png;base64,UE5HIERBVEE=') 1x, url('data:image/png;base64,MS40eCBQTkcgREFUQQ==') 1.4x, url('data:image/png;base64,MS44eCBQTkcgREFUQQ==') 1.8x);
-+ }
-+ </style>
-+ </head>
-+ <body>
-+ <!-- Don't need a body. -->
-+ </body>
-+ </html>
-+ '''))
-+ tmp_dir.CleanUp()
-+
-+
-+if __name__ == '__main__':
-+ unittest.main()
-diff --git a/tools/grit/grit/gather/chrome_scaled_image.py b/tools/grit/grit/gather/chrome_scaled_image.py
-new file mode 100644
-index 0000000000..44f98cbcf0
---- /dev/null
-+++ b/tools/grit/grit/gather/chrome_scaled_image.py
-@@ -0,0 +1,157 @@
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Gatherer for <structure type="chrome_scaled_image">.
-+'''
-+
-+from __future__ import print_function
-+
-+import os
-+import struct
-+
-+from grit import exception
-+from grit import lazy_re
-+from grit import util
-+from grit.gather import interface
-+
-+
-+_PNG_SCALE_CHUNK = b'\0\0\0\0csCl\xc1\x30\x60\x4d'
-+
-+
-+def _RescaleImage(data, from_scale, to_scale):
-+ if from_scale != to_scale:
-+ assert from_scale == 100
-+ # Rather than rescaling the image we add a custom chunk directing Chrome to
-+ # rescale it on load. Just append it to the PNG data since
-+ # _MoveSpecialChunksToFront will move it later anyway.
-+ data += _PNG_SCALE_CHUNK
-+ return data
-+
-+
-+_PNG_MAGIC = b'\x89PNG\r\n\x1a\n'
-+
-+'''Mandatory first chunk in order for the png to be valid.'''
-+_FIRST_CHUNK = b'IHDR'
-+
-+'''Special chunks to move immediately after the IHDR chunk. (so that the PNG
-+remains valid.)
-+'''
-+_SPECIAL_CHUNKS = frozenset(b'csCl npTc'.split())
-+
-+'''Any ancillary chunk not in this list is deleted from the PNG.'''
-+_ANCILLARY_CHUNKS_TO_LEAVE = frozenset(
-+ b'bKGD cHRM gAMA iCCP pHYs sBIT sRGB tRNS acTL fcTL fdAT'.split())
-+
-+
-+def _MoveSpecialChunksToFront(data):
-+ '''Move special chunks immediately after the IHDR chunk (so that the PNG
-+ remains valid). Also delete ancillary chunks that are not on our whitelist.
-+ '''
-+ first = [_PNG_MAGIC]
-+ special_chunks = []
-+ rest = []
-+ for chunk in _ChunkifyPNG(data):
-+ type = chunk[4:8]
-+ critical = type < b'a'
-+ if type == _FIRST_CHUNK:
-+ first.append(chunk)
-+ elif type in _SPECIAL_CHUNKS:
-+ special_chunks.append(chunk)
-+ elif critical or type in _ANCILLARY_CHUNKS_TO_LEAVE:
-+ rest.append(chunk)
-+ return b''.join(first + special_chunks + rest)
-+
-+
-+def _ChunkifyPNG(data):
-+ '''Given a PNG image, yield its chunks in order.'''
-+ assert data.startswith(_PNG_MAGIC)
-+ pos = 8
-+ while pos != len(data):
-+ length = 12 + struct.unpack_from('>I', data, pos)[0]
-+ assert 12 <= length <= len(data) - pos
-+ yield data[pos:pos+length]
-+ pos += length
-+
-+
-+def _MakeBraceGlob(strings):
-+ '''Given ['foo', 'bar'], return '{foo,bar}', for error reporting.
-+ '''
-+ if len(strings) == 1:
-+ return strings[0]
-+ else:
-+ return '{' + ','.join(strings) + '}'
-+
-+
-+class ChromeScaledImage(interface.GathererBase):
-+ '''Represents an image that exists in multiple layout variants
-+ (e.g. "default", "touch") and multiple scale variants
-+ (e.g. "100_percent", "200_percent").
-+ '''
-+
-+ split_context_re_ = lazy_re.compile(r'(.+)_(\d+)_percent\Z')
-+
-+ def _FindInputFile(self):
-+ output_context = self.grd_node.GetRoot().output_context
-+ match = self.split_context_re_.match(output_context)
-+ if not match:
-+ raise exception.MissingMandatoryAttribute(
-+ 'All <output> nodes must have an appropriate context attribute'
-+ ' (e.g. context="touch_200_percent")')
-+ req_layout, req_scale = match.group(1), int(match.group(2))
-+
-+ layouts = [req_layout]
-+ try_default_layout = self.grd_node.GetRoot().fallback_to_default_layout
-+ if try_default_layout and 'default' not in layouts:
-+ layouts.append('default')
-+
-+ scales = [req_scale]
-+ try_low_res = self.grd_node.FindBooleanAttribute(
-+ 'fallback_to_low_resolution', default=False, skip_self=False)
-+ if try_low_res and 100 not in scales:
-+ scales.append(100)
-+
-+ for layout in layouts:
-+ for scale in scales:
-+ dir = '%s_%s_percent' % (layout, scale)
-+ path = os.path.join(dir, self.rc_file)
-+ if os.path.exists(self.grd_node.ToRealPath(path)):
-+ return path, scale, req_scale
-+
-+ if not try_default_layout:
-+ # The file was not found in the specified output context and it was
-+ # explicitly indicated that the default context should not be searched
-+ # as a fallback, so return an empty path.
-+ return None, 100, req_scale
-+
-+ # The file was found in neither the specified context nor the default
-+ # context, so raise an exception.
-+ dir = "%s_%s_percent" % (_MakeBraceGlob(layouts),
-+ _MakeBraceGlob([str(x) for x in scales]))
-+ raise exception.FileNotFound(
-+ 'Tried ' + self.grd_node.ToRealPath(os.path.join(dir, self.rc_file)))
-+
-+ def GetInputPath(self):
-+ path, scale, req_scale = self._FindInputFile()
-+ return path
-+
-+ def Parse(self):
-+ pass
-+
-+ def GetTextualIds(self):
-+ return [self.extkey]
-+
-+ def GetData(self, lang, encoding):
-+ assert encoding == util.BINARY
-+
-+ path, scale, req_scale = self._FindInputFile()
-+ if path is None:
-+ return None
-+
-+ data = util.ReadFile(self.grd_node.ToRealPath(path), util.BINARY)
-+ data = _RescaleImage(data, scale, req_scale)
-+ data = _MoveSpecialChunksToFront(data)
-+ return data
-+
-+ def Translate(self, *args, **kwargs):
-+ return self.GetData()
-diff --git a/tools/grit/grit/gather/chrome_scaled_image_unittest.py b/tools/grit/grit/gather/chrome_scaled_image_unittest.py
-new file mode 100644
-index 0000000000..1cebfc6de2
---- /dev/null
-+++ b/tools/grit/grit/gather/chrome_scaled_image_unittest.py
-@@ -0,0 +1,209 @@
-+#!/usr/bin/env python
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Unit tests for ChromeScaledImage.'''
-+
-+from __future__ import print_function
-+
-+import os
-+import sys
-+if __name__ == '__main__':
-+ sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__),
-+ '../..')))
-+
-+import re
-+import struct
-+import unittest
-+import zlib
-+
-+from grit import exception
-+from grit import util
-+from grit.format import data_pack
-+from grit.tool import build
-+
-+
-+_OUTFILETYPES = [
-+ ('.h', 'rc_header'),
-+ ('_map.cc', 'resource_map_source'),
-+ ('_map.h', 'resource_map_header'),
-+ ('.pak', 'data_package'),
-+ ('.rc', 'rc_all'),
-+]
-+
-+
-+_PNG_HEADER = (
-+ b'\x89\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52'
-+ b'\x00\x00\x00\x01\x00\x00\x00\x01\x08\x02\x00\x00\x00\x90\x77\x53'
-+ b'\xde')
-+_PNG_FOOTER = (
-+ b'\x00\x00\x00\x0c\x49\x44\x41\x54\x18\x57\x63\xf8\xff\xff\x3f\x00'
-+ b'\x05\xfe\x02\xfe\xa7\x35\x81\x84\x00\x00\x00\x00\x49\x45\x4e\x44'
-+ b'\xae\x42\x60\x82')
-+
-+
-+def _MakePNG(chunks):
-+ # Python 3 changed the return value of zlib.crc32 to an unsigned int.
-+ format = 'i' if sys.version_info.major < 3 else 'I'
-+ pack_int32 = struct.Struct('>' + format).pack
-+ chunks = [pack_int32(len(payload)) + type + payload +
-+ pack_int32(zlib.crc32(type + payload))
-+ for type, payload in chunks]
-+ return _PNG_HEADER + b''.join(chunks) + _PNG_FOOTER
-+
-+
-+def _GetFilesInPak(pakname):
-+ '''Get a set of the files that were actually included in the .pak output.
-+ '''
-+ return set(data_pack.ReadDataPack(pakname).resources.values())
-+
-+
-+def _GetFilesInRc(rcname, tmp_dir, contents):
-+ '''Get a set of the files that were actually included in the .rc output.
-+ '''
-+ data = util.ReadFile(rcname, util.BINARY).decode('utf-16')
-+ contents = dict((tmp_dir.GetPath(k), v) for k, v in contents.items())
-+ return set(contents[os.path.normpath(m.group(1))]
-+ for m in re.finditer(r'(?m)^\w+\s+BINDATA\s+"([^"]+)"$', data))
-+
-+
-+def _MakeFallbackAttr(fallback):
-+ if fallback is None:
-+ return ''
-+ else:
-+ return ' fallback_to_low_resolution="%s"' % ('false', 'true')[fallback]
-+
-+
-+def _Structures(fallback, *body):
-+ return '<structures%s>\n%s\n</structures>' % (
-+ _MakeFallbackAttr(fallback), '\n'.join(body))
-+
-+
-+def _Structure(name, file, fallback=None):
-+ return '<structure name="%s" file="%s" type="chrome_scaled_image"%s />' % (
-+ name, file, _MakeFallbackAttr(fallback))
-+
-+
-+def _If(expr, *body):
-+ return '<if expr="%s">\n%s\n</if>' % (expr, '\n'.join(body))
-+
-+
-+def _RunBuildTest(self, structures, inputs, expected_outputs, skip_rc=False,
-+ layout_fallback=''):
-+ outputs = '\n'.join('<output filename="out/%s%s" type="%s" context="%s"%s />'
-+ % (context, ext, type, context, layout_fallback)
-+ for ext, type in _OUTFILETYPES
-+ for context in expected_outputs)
-+
-+ infiles = {
-+ 'in/in.grd': ('''<?xml version="1.0" encoding="UTF-8"?>
-+ <grit latest_public_release="0" current_release="1">
-+ <outputs>
-+ %s
-+ </outputs>
-+ <release seq="1">
-+ %s
-+ </release>
-+ </grit>
-+ ''' % (outputs, structures)).encode('utf-8'),
-+ }
-+ for pngpath, pngdata in inputs.items():
-+ normpath = os.path.normpath('in/' + pngpath)
-+ infiles[normpath] = pngdata
-+ class Options(object):
-+ pass
-+
-+ with util.TempDir(infiles, mode='wb') as tmp_dir:
-+ with tmp_dir.AsCurrentDir():
-+ options = Options()
-+ options.input = tmp_dir.GetPath('in/in.grd')
-+ options.verbose = False
-+ options.extra_verbose = False
-+ build.RcBuilder().Run(options, [])
-+ for context, expected_data in expected_outputs.items():
-+ self.assertEquals(expected_data,
-+ _GetFilesInPak(tmp_dir.GetPath('out/%s.pak' % context)))
-+ if not skip_rc:
-+ self.assertEquals(expected_data,
-+ _GetFilesInRc(tmp_dir.GetPath('out/%s.rc' % context),
-+ tmp_dir, infiles))
-+
-+
-+class ChromeScaledImageUnittest(unittest.TestCase):
-+ def testNormalFallback(self):
-+ d123a = _MakePNG([(b'AbCd', b'')])
-+ t123a = _MakePNG([(b'EfGh', b'')])
-+ d123b = _MakePNG([(b'IjKl', b'')])
-+ _RunBuildTest(self,
-+ _Structures(None,
-+ _Structure('IDR_A', 'a.png'),
-+ _Structure('IDR_B', 'b.png'),
-+ ),
-+ {'default_123_percent/a.png': d123a,
-+ 'tactile_123_percent/a.png': t123a,
-+ 'default_123_percent/b.png': d123b,
-+ },
-+ {'default_123_percent': set([d123a, d123b]),
-+ 'tactile_123_percent': set([t123a, d123b]),
-+ })
-+
-+ def testNormalFallbackFailure(self):
-+ self.assertRaises(
-+ exception.FileNotFound, _RunBuildTest, self,
-+ _Structures(
-+ None,
-+ _Structure('IDR_A', 'a.png'),
-+ ), {
-+ 'default_100_percent/a.png': _MakePNG([(b'AbCd', b'')]),
-+ 'tactile_100_percent/a.png': _MakePNG([(b'EfGh', b'')]),
-+ }, {'tactile_123_percent': 'should fail before using this'})
-+
-+ def testLowresFallback(self):
-+ png = _MakePNG([(b'Abcd', b'')])
-+ png_with_csCl = _MakePNG([(b'csCl', b''), (b'Abcd', b'')])
-+ for outer in (None, False, True):
-+ for inner in (None, False, True):
-+ args = (
-+ self,
-+ _Structures(outer,
-+ _Structure('IDR_A', 'a.png', inner),
-+ ),
-+ {'default_100_percent/a.png': png},
-+ {'tactile_200_percent': set([png_with_csCl])})
-+ if inner or (inner is None and outer):
-+ # should fall back to 100%
-+ _RunBuildTest(*args, skip_rc=True)
-+ else:
-+ # shouldn't fall back
-+ self.assertRaises(exception.FileNotFound, _RunBuildTest, *args)
-+
-+ # Test fallback failure with fallback_to_low_resolution=True
-+ self.assertRaises(exception.FileNotFound,
-+ _RunBuildTest, self,
-+ _Structures(True,
-+ _Structure('IDR_A', 'a.png'),
-+ ),
-+ {}, # no files
-+ {'tactile_123_percent': 'should fail before using this'})
-+
-+ def testNoFallbackToDefaultLayout(self):
-+ d123a = _MakePNG([(b'AbCd', b'')])
-+ t123a = _MakePNG([(b'EfGh', b'')])
-+ d123b = _MakePNG([(b'IjKl', b'')])
-+ _RunBuildTest(self,
-+ _Structures(None,
-+ _Structure('IDR_A', 'a.png'),
-+ _Structure('IDR_B', 'b.png'),
-+ ),
-+ {'default_123_percent/a.png': d123a,
-+ 'tactile_123_percent/a.png': t123a,
-+ 'default_123_percent/b.png': d123b,
-+ },
-+ {'default_123_percent': set([d123a, d123b]),
-+ 'tactile_123_percent': set([t123a]),
-+ },
-+ layout_fallback=' fallback_to_default_layout="false"')
-+
-+if __name__ == '__main__':
-+ unittest.main()
-diff --git a/tools/grit/grit/gather/interface.py b/tools/grit/grit/gather/interface.py
-new file mode 100644
-index 0000000000..15d64f9326
---- /dev/null
-+++ b/tools/grit/grit/gather/interface.py
-@@ -0,0 +1,172 @@
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Interface for all gatherers.
-+'''
-+
-+from __future__ import print_function
-+
-+import os.path
-+
-+import six
-+
-+from grit import clique
-+from grit import util
-+
-+
-+class GathererBase(object):
-+ '''Interface for all gatherer implementations. Subclasses must implement
-+ all methods that raise NotImplemented.'''
-+
-+ def __init__(self, rc_file, extkey=None, encoding='cp1252', is_skeleton=False):
-+ '''Initializes the gatherer object's attributes, but does not attempt to
-+ read the input file.
-+
-+ Args:
-+ rc_file: The 'file' attribute of the <structure> node (usually the
-+ relative path to the source file).
-+ extkey: e.g. 'ID_MY_DIALOG'
-+ encoding: e.g. 'utf-8'
-+ is_skeleton: Indicates whether this gatherer is a skeleton gatherer, in
-+ which case we should not do some types of processing on the
-+ translateable bits.
-+ '''
-+ self.rc_file = rc_file
-+ self.extkey = extkey
-+ self.encoding = encoding
-+ # A default uberclique that is local to this object. Users can override
-+ # this with the uberclique they are using.
-+ self.uberclique = clique.UberClique()
-+ # Indicates whether this gatherer is a skeleton gatherer, in which case
-+ # we should not do some types of processing on the translateable bits.
-+ self.is_skeleton = is_skeleton
-+ # Stores the grd node on which this gatherer is running. This allows
-+ # evaluating expressions.
-+ self.grd_node = None
-+
-+ def SetAttributes(self, attrs):
-+ '''Sets node attributes used by the gatherer.
-+
-+ By default, this does nothing. If special handling is desired, it should be
-+ overridden by the child gatherer.
-+
-+ Args:
-+ attrs: The mapping of node attributes.
-+ '''
-+ pass
-+
-+ def SetDefines(self, defines):
-+ '''Sets global defines used by the gatherer.
-+
-+ By default, this does nothing. If special handling is desired, it should be
-+ overridden by the child gatherer.
-+
-+ Args:
-+ defines: The mapping of define values.
-+ '''
-+ pass
-+
-+ def SetGrdNode(self, node):
-+ '''Sets the grd node on which this gatherer is running.
-+ '''
-+ self.grd_node = node
-+
-+ def SetUberClique(self, uberclique):
-+ '''Overrides the default uberclique so that cliques created by this object
-+ become part of the uberclique supplied by the user.
-+ '''
-+ self.uberclique = uberclique
-+
-+ def Parse(self):
-+ '''Reads and parses the contents of what is being gathered.'''
-+ raise NotImplementedError()
-+
-+ def GetData(self, lang, encoding):
-+ '''Returns the data to be added to the DataPack for this node or None if
-+ this node does not add a DataPack entry.
-+ '''
-+ return None
-+
-+ def GetText(self):
-+ '''Returns the text of what is being gathered.'''
-+ raise NotImplementedError()
-+
-+ def GetTextualIds(self):
-+ '''Returns the mnemonic IDs that need to be defined for the resource
-+ being gathered to compile correctly.'''
-+ return []
-+
-+ def GetCliques(self):
-+ '''Returns the MessageClique objects for all translateable portions.'''
-+ return []
-+
-+ def GetInputPath(self):
-+ return self.rc_file
-+
-+ def GetHtmlResourceFilenames(self):
-+ """Returns a set of all filenames inlined by this gatherer."""
-+ return []
-+
-+ def Translate(self, lang, pseudo_if_not_available=True,
-+ skeleton_gatherer=None, fallback_to_english=False):
-+ '''Returns the resource being gathered, with translateable portions filled
-+ with the translation for language 'lang'.
-+
-+ If pseudo_if_not_available is true, a pseudotranslation will be used for any
-+ message that doesn't have a real translation available.
-+
-+ If no translation is available and pseudo_if_not_available is false,
-+ fallback_to_english controls the behavior. If it is false, throw an error.
-+ If it is true, use the English version of the message as its own
-+ "translation".
-+
-+ If skeleton_gatherer is specified, the translation will use the nontranslateable
-+ parts from the gatherer 'skeleton_gatherer', which must be of the same type
-+ as 'self'.
-+
-+ If fallback_to_english
-+
-+ Args:
-+ lang: 'en'
-+ pseudo_if_not_available: True | False
-+ skeleton_gatherer: other_gatherer
-+ fallback_to_english: True | False
-+
-+ Return:
-+ e.g. 'ID_THIS_SECTION TYPE\n...BEGIN\n "Translated message"\n......\nEND'
-+
-+ Raises:
-+ grit.exception.NotReady() if used before Parse() has been successfully
-+ called.
-+ grit.exception.NoSuchTranslation() if 'pseudo_if_not_available' and
-+ fallback_to_english are both false and there is no translation for the
-+ requested language.
-+ '''
-+ raise NotImplementedError()
-+
-+ def SubstituteMessages(self, substituter):
-+ '''Applies substitutions to all messages in the gatherer.
-+
-+ Args:
-+ substituter: a grit.util.Substituter object.
-+ '''
-+ pass
-+
-+ def SetFilenameExpansionFunction(self, fn):
-+ '''Sets a function for rewriting filenames before gathering.'''
-+ pass
-+
-+ # TODO(benrg): Move this elsewhere, since it isn't part of the interface.
-+ def _LoadInputFile(self):
-+ '''A convenience function for subclasses that loads the contents of the
-+ input file.
-+ '''
-+ if isinstance(self.rc_file, six.string_types):
-+ path = self.GetInputPath()
-+ # Hack: some unit tests supply an absolute path and no root node.
-+ if not os.path.isabs(path):
-+ path = self.grd_node.ToRealPath(path)
-+ return util.ReadFile(path, self.encoding)
-+ else:
-+ return self.rc_file.read()
-diff --git a/tools/grit/grit/gather/json_loader.py b/tools/grit/grit/gather/json_loader.py
-new file mode 100644
-index 0000000000..058e5f17ae
---- /dev/null
-+++ b/tools/grit/grit/gather/json_loader.py
-@@ -0,0 +1,27 @@
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+from __future__ import print_function
-+
-+from grit.gather import interface
-+
-+
-+class JsonLoader(interface.GathererBase):
-+ '''A simple gatherer that loads and parses a JSON file.'''
-+
-+ def Parse(self):
-+ '''Reads and parses the text of self._json_text into the data structure in
-+ self._data.
-+ '''
-+ self._json_text = self._LoadInputFile()
-+ self._data = None
-+
-+ globs = {}
-+ exec('data = ' + self._json_text, globs)
-+ self._data = globs['data']
-+
-+ def GetData(self, lang, encoding):
-+ '''Returns the parsed JSON data.'''
-+ assert encoding == 'utf-8'
-+ return self._data
-diff --git a/tools/grit/grit/gather/policy_json.py b/tools/grit/grit/gather/policy_json.py
-new file mode 100644
-index 0000000000..6621c5f3c4
---- /dev/null
-+++ b/tools/grit/grit/gather/policy_json.py
-@@ -0,0 +1,325 @@
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Support for "policy_templates.json" format used by the policy template
-+generator as a source for generating ADM,ADMX,etc files.'''
-+
-+from __future__ import print_function
-+
-+import json
-+import sys
-+
-+import six
-+
-+from grit.gather import skeleton_gatherer
-+from grit import util
-+from grit import tclib
-+from xml.dom import minidom
-+from xml.parsers.expat import ExpatError
-+
-+
-+class PolicyJson(skeleton_gatherer.SkeletonGatherer):
-+ '''Collects and translates the following strings from policy_templates.json:
-+ - captions, descriptions, labels and Android app support details of policies
-+ - captions of enumeration items
-+ - misc strings from the 'messages' section
-+ Translatable strings may have untranslateable placeholders with the same
-+ format that is used in .grd files.
-+ '''
-+
-+ def _AddEndline(self, add_comma):
-+ '''Adds an endline to the skeleton tree. If add_comma is true, adds a
-+ comma before the endline.
-+
-+ Args:
-+ add_comma: A boolean to add a comma or not.
-+ '''
-+ self._AddNontranslateableChunk(',\n' if add_comma else '\n')
-+
-+ def _ParsePlaceholder(self, placeholder, msg):
-+ '''Extracts a placeholder from a DOM node and adds it to a tclib Message.
-+
-+ Args:
-+ placeholder: A DOM node of the form:
-+ <ph name="PLACEHOLDER_NAME">Placeholder text<ex>Example value</ex></ph>
-+ msg: The placeholder is added to this message.
-+ '''
-+ text = []
-+ example_text = []
-+ for node1 in placeholder.childNodes:
-+ if (node1.nodeType == minidom.Node.TEXT_NODE):
-+ text.append(node1.data)
-+ elif (node1.nodeType == minidom.Node.ELEMENT_NODE and
-+ node1.tagName == 'ex'):
-+ for node2 in node1.childNodes:
-+ example_text.append(node2.toxml())
-+ else:
-+ raise Exception('Unexpected element inside a placeholder: ' +
-+ node2.toxml())
-+ if example_text == []:
-+ # In such cases the original text is okay for an example.
-+ example_text = text
-+
-+ replaced_text = self.Escape(''.join(text).strip())
-+ replaced_text = replaced_text.replace('$1', self._config['app_name'])
-+ replaced_text = replaced_text.replace('$2', self._config['os_name'])
-+ replaced_text = replaced_text.replace('$3', self._config['frame_name'])
-+
-+ msg.AppendPlaceholder(tclib.Placeholder(
-+ placeholder.attributes['name'].value,
-+ replaced_text,
-+ ''.join(example_text).strip()))
-+
-+ def _ParseMessage(self, string, desc):
-+ '''Parses a given string and adds it to the output as a translatable chunk
-+ with a given description.
-+
-+ Args:
-+ string: The message string to parse.
-+ desc: The description of the message (for the translators).
-+ '''
-+ msg = tclib.Message(description=desc)
-+ xml = '<msg>' + string + '</msg>'
-+ try:
-+ node = minidom.parseString(xml).childNodes[0]
-+ except ExpatError:
-+ reason = '''Input isn't valid XML (has < & > been escaped?): ''' + string
-+ six.reraise(Exception, reason, sys.exc_info()[2])
-+
-+ for child in node.childNodes:
-+ if child.nodeType == minidom.Node.TEXT_NODE:
-+ msg.AppendText(child.data)
-+ elif child.nodeType == minidom.Node.ELEMENT_NODE:
-+ if child.tagName == 'ph':
-+ self._ParsePlaceholder(child, msg)
-+ else:
-+ raise Exception("Not implemented.")
-+ else:
-+ raise Exception("Not implemented.")
-+ self.skeleton_.append(self.uberclique.MakeClique(msg))
-+
-+ def _ParseNode(self, node):
-+ '''Traverses the subtree of a DOM node, and register a tclib message for
-+ all the <message> nodes.
-+ '''
-+ att_text = []
-+ if node.attributes:
-+ for key, value in sorted(node.attributes.items()):
-+ att_text.append(' %s=\"%s\"' % (key, value))
-+ self._AddNontranslateableChunk("<%s%s>" %
-+ (node.tagName, ''.join(att_text)))
-+ if node.tagName == 'message':
-+ msg = tclib.Message(description=node.attributes['desc'])
-+ for child in node.childNodes:
-+ if child.nodeType == minidom.Node.TEXT_NODE:
-+ if msg == None:
-+ self._AddNontranslateableChunk(child.data)
-+ else:
-+ msg.AppendText(child.data)
-+ elif child.nodeType == minidom.Node.ELEMENT_NODE:
-+ if child.tagName == 'ph':
-+ self._ParsePlaceholder(child, msg)
-+ else:
-+ assert False
-+ self.skeleton_.append(self.uberclique.MakeClique(msg))
-+ else:
-+ for child in node.childNodes:
-+ if child.nodeType == minidom.Node.TEXT_NODE:
-+ self._AddNontranslateableChunk(child.data)
-+ elif node.nodeType == minidom.Node.ELEMENT_NODE:
-+ self._ParseNode(child)
-+
-+ self._AddNontranslateableChunk("</%s>" % node.tagName)
-+
-+ def _AddIndentedNontranslateableChunk(self, depth, string):
-+ '''Adds a nontranslateable chunk of text to the internally stored output.
-+
-+ Args:
-+ depth: The number of double spaces to prepend to the next argument string.
-+ string: The chunk of text to add.
-+ '''
-+ result = []
-+ while depth > 0:
-+ result.append(' ')
-+ depth = depth - 1
-+ result.append(string)
-+ self._AddNontranslateableChunk(''.join(result))
-+
-+ def _GetDescription(self, item, item_type, parent_item, key):
-+ '''Creates a description for a translatable message. The description gives
-+ some context for the person who will translate this message.
-+
-+ Args:
-+ item: A policy or an enumeration item.
-+ item_type: 'enum_item' | 'policy'
-+ parent_item: The owner of item. (A policy of type group or enum.)
-+ key: The name of the key to parse.
-+ depth: The level of indentation.
-+ '''
-+ key_map = {
-+ 'desc': 'Description',
-+ 'caption': 'Caption',
-+ 'label': 'Label',
-+ 'arc_support': 'Information about the effect on Android apps'
-+ }
-+ if item_type == 'policy':
-+ return ('%s of the policy named %s [owner(s): %s]' %
-+ (key_map[key], item['name'],
-+ ','.join(item['owners'] if 'owners' in item else 'unknown')))
-+ if item_type == 'enum_item':
-+ return ('%s of the option named %s in policy %s [owner(s): %s]' %
-+ (key_map[key], item['name'], parent_item['name'],
-+ ','.join(parent_item['owners'] if 'owners' in parent_item else 'unknown')))
-+ raise Exception('Unexpected type %s' % item_type)
-+
-+ def _AddSchemaKeys(self, obj, depth):
-+ obj_type = type(obj)
-+ if obj_type == dict:
-+ self._AddNontranslateableChunk('{\n')
-+ keys = sorted(obj.keys())
-+ for count, (key) in enumerate(keys, 1):
-+ json_key = "%s: " % json.dumps(key)
-+ self._AddIndentedNontranslateableChunk(depth + 1, json_key)
-+ if key == 'description' and type(obj[key]) == str:
-+ self._AddNontranslateableChunk("\"")
-+ self._ParseMessage(obj[key], 'Description of schema property')
-+ self._AddNontranslateableChunk("\"")
-+ elif type(obj[key]) in (bool, int, str):
-+ self._AddSchemaKeys(obj[key], 0)
-+ else:
-+ self._AddSchemaKeys(obj[key], depth + 1)
-+ self._AddEndline(count < len(keys))
-+ self._AddIndentedNontranslateableChunk(depth, '}')
-+ elif obj_type == list:
-+ self._AddNontranslateableChunk('[\n')
-+ for count, (item) in enumerate(obj, 1):
-+ self._AddSchemaKeys(item, depth + 1)
-+ self._AddEndline(count < len(obj))
-+ self._AddIndentedNontranslateableChunk(depth, ']')
-+ elif obj_type in (bool, int, str):
-+ self._AddIndentedNontranslateableChunk(depth, json.dumps(obj))
-+ else:
-+ raise Exception('Invalid schema object: %s' % obj)
-+
-+ def _AddPolicyKey(self, item, item_type, parent_item, key, depth):
-+ '''Given a policy/enumeration item and a key, adds that key and its value
-+ into the output.
-+ E.g.:
-+ 'example_value': 123
-+ If key indicates that the value is a translatable string, then it is parsed
-+ as a translatable string.
-+
-+ Args:
-+ item: A policy or an enumeration item.
-+ item_type: 'enum_item' | 'policy'
-+ parent_item: The owner of item. (A policy of type group or enum.)
-+ key: The name of the key to parse.
-+ depth: The level of indentation.
-+ '''
-+ self._AddIndentedNontranslateableChunk(depth, "%s: " % json.dumps(key))
-+ if key in ('desc', 'caption', 'label', 'arc_support'):
-+ self._AddNontranslateableChunk("\"")
-+ self._ParseMessage(
-+ item[key],
-+ self._GetDescription(item, item_type, parent_item, key))
-+ self._AddNontranslateableChunk("\"")
-+ elif key in ('schema', 'validation_schema', 'description_schema'):
-+ self._AddSchemaKeys(item[key], depth)
-+ else:
-+ self._AddNontranslateableChunk(json.dumps(item[key], ensure_ascii=False))
-+
-+ def _AddItems(self, items, item_type, parent_item, depth):
-+ '''Parses and adds a list of items from the JSON file. Items can be policies
-+ or parts of an enum policy.
-+
-+ Args:
-+ items: Either a list of policies or a list of dictionaries.
-+ item_type: 'enum_item' | 'policy'
-+ parent_item: If items contains a list of policies, then this is the policy
-+ group that owns them. If items contains a list of enumeration items,
-+ then this is the enum policy that holds them.
-+ depth: Indicates the depth of our position in the JSON hierarchy. Used to
-+ add nice line-indent to the output.
-+ '''
-+ for item_count, (item1) in enumerate(items, 1):
-+ self._AddIndentedNontranslateableChunk(depth, "{\n")
-+ keys = sorted(item1.keys())
-+ for keys_count, (key) in enumerate(keys, 1):
-+ if key == 'items':
-+ self._AddIndentedNontranslateableChunk(depth + 1, "\"items\": [\n")
-+ self._AddItems(item1['items'], 'enum_item', item1, depth + 2)
-+ self._AddIndentedNontranslateableChunk(depth + 1, "]")
-+ elif key == 'policies' and all(not isinstance(x, str)
-+ for x in item1['policies']):
-+ self._AddIndentedNontranslateableChunk(depth + 1, "\"policies\": [\n")
-+ self._AddItems(item1['policies'], 'policy', item1, depth + 2)
-+ self._AddIndentedNontranslateableChunk(depth + 1, "]")
-+ else:
-+ self._AddPolicyKey(item1, item_type, parent_item, key, depth + 1)
-+ self._AddEndline(keys_count < len(keys))
-+ self._AddIndentedNontranslateableChunk(depth, "}")
-+ self._AddEndline(item_count < len(items))
-+
-+ def _AddMessages(self):
-+ '''Processed and adds the 'messages' section to the output.'''
-+ self._AddNontranslateableChunk(" \"messages\": {\n")
-+ messages = self.data['messages'].items()
-+ for count, (name, message) in enumerate(messages, 1):
-+ self._AddNontranslateableChunk(" %s: {\n" % json.dumps(name))
-+ self._AddNontranslateableChunk(" \"text\": \"")
-+ self._ParseMessage(message['text'], message['desc'])
-+ self._AddNontranslateableChunk("\"\n")
-+ self._AddNontranslateableChunk(" }")
-+ self._AddEndline(count < len(self.data['messages']))
-+ self._AddNontranslateableChunk(" }\n")
-+
-+ # Although we use the RegexpGatherer base class, we do not use the
-+ # _RegExpParse method of that class to implement Parse(). Instead, we
-+ # parse using a DOM parser.
-+ def Parse(self):
-+ if self.have_parsed_:
-+ return
-+ self.have_parsed_ = True
-+
-+ self.text_ = self._LoadInputFile()
-+ if util.IsExtraVerbose():
-+ print(self.text_)
-+
-+ self.data = eval(self.text_)
-+
-+ self._AddNontranslateableChunk('{\n')
-+ self._AddNontranslateableChunk(" \"policy_definitions\": [\n")
-+ self._AddItems(self.data['policy_definitions'], 'policy', None, 2)
-+ self._AddNontranslateableChunk(" ],\n")
-+ self._AddNontranslateableChunk(" \"policy_atomic_group_definitions\": [\n")
-+ if 'policy_atomic_group_definitions' in self.data:
-+ self._AddItems(self.data['policy_atomic_group_definitions'],
-+ 'policy', None, 2)
-+ self._AddNontranslateableChunk(" ],\n")
-+ self._AddMessages()
-+ self._AddNontranslateableChunk('\n}')
-+
-+ def Escape(self, text):
-+ return json.dumps(text, ensure_ascii=False)[1:-1]
-+
-+ def SetDefines(self, defines):
-+ if not defines:
-+ raise Exception('Must pass valid defines')
-+
-+ if '_chromium' in defines:
-+ self._config = {
-+ 'build': 'chromium',
-+ 'app_name': 'Chromium',
-+ 'frame_name': 'Chromium Frame',
-+ 'os_name': 'Chromium OS',
-+ }
-+ elif '_google_chrome' in defines:
-+ self._config = {
-+ 'build': 'chrome',
-+ 'app_name': 'Google Chrome',
-+ 'frame_name': 'Google Chrome Frame',
-+ 'os_name': 'Google Chrome OS',
-+ }
-+ else:
-+ raise Exception('Unknown build')
-diff --git a/tools/grit/grit/gather/policy_json_unittest.py b/tools/grit/grit/gather/policy_json_unittest.py
-new file mode 100644
-index 0000000000..214cd276aa
---- /dev/null
-+++ b/tools/grit/grit/gather/policy_json_unittest.py
-@@ -0,0 +1,347 @@
-+#!/usr/bin/env python
-+# Copyright (c) 2011 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Unit tests for grit.gather.policy_json'''
-+
-+from __future__ import print_function
-+
-+import json
-+import os
-+import re
-+import sys
-+if __name__ == '__main__':
-+ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
-+
-+import unittest
-+
-+from six import StringIO
-+
-+from grit.gather import policy_json
-+
-+class PolicyJsonUnittest(unittest.TestCase):
-+
-+ def GetExpectedOutput(self, original):
-+ expected = eval(original)
-+ for key, message in expected['messages'].items():
-+ del message['desc']
-+ return expected
-+
-+ def testEmpty(self):
-+ original = """{
-+ 'policy_definitions': [],
-+ 'policy_atomic_group_definitions': [],
-+ 'messages': {}
-+ }"""
-+ gatherer = policy_json.PolicyJson(StringIO(original))
-+ gatherer.Parse()
-+ self.failUnless(len(gatherer.GetCliques()) == 0)
-+ self.failUnless(eval(original) == json.loads(gatherer.Translate('en')))
-+
-+ def testGeneralPolicy(self):
-+ original = (
-+ "{"
-+ " 'policy_definitions': ["
-+ " {"
-+ " 'name': 'HomepageLocation',"
-+ " 'type': 'string',"
-+ " 'owners': ['foo@bar.com'],"
-+ " 'supported_on': ['chrome.*:8-'],"
-+ " 'features': {'dynamic_refresh': 1},"
-+ " 'example_value': 'http://chromium.org',"
-+ " 'caption': 'nothing special 1',"
-+ " 'desc': 'nothing special 2',"
-+ " 'label': 'nothing special 3',"
-+ " },"
-+ " ],"
-+ " 'policy_atomic_group_definitions': [],"
-+ " 'messages': {"
-+ " 'msg_identifier': {"
-+ " 'text': 'nothing special 3',"
-+ " 'desc': 'nothing special descr 3',"
-+ " }"
-+ " }"
-+ "}")
-+ gatherer = policy_json.PolicyJson(StringIO(original))
-+ gatherer.Parse()
-+ self.failUnless(len(gatherer.GetCliques()) == 4)
-+ expected = self.GetExpectedOutput(original)
-+ self.failUnless(expected == json.loads(gatherer.Translate('en')))
-+
-+ def testEnum(self):
-+ original = (
-+ "{"
-+ " 'policy_definitions': ["
-+ " {"
-+ " 'name': 'Policy1',"
-+ " 'owners': ['a@b'],"
-+ " 'items': ["
-+ " {"
-+ " 'name': 'Item1',"
-+ " 'caption': 'nothing special',"
-+ " }"
-+ " ]"
-+ " },"
-+ " ],"
-+ " 'policy_atomic_group_definitions': [],"
-+ " 'messages': {}"
-+ "}")
-+ gatherer = policy_json.PolicyJson(StringIO(original))
-+ gatherer.Parse()
-+ self.failUnless(len(gatherer.GetCliques()) == 1)
-+ expected = self.GetExpectedOutput(original)
-+ self.failUnless(expected == json.loads(gatherer.Translate('en')))
-+
-+ def testSchema(self):
-+ original = ("{"
-+ " 'policy_definitions': ["
-+ " {"
-+ " 'name': 'Policy1',"
-+ " 'schema': {"
-+ " 'type': 'object',"
-+ " 'properties': {"
-+ " 'outer': {"
-+ " 'description': 'outer description',"
-+ " 'type': 'object',"
-+ " 'inner': {"
-+ " 'description': 'inner description',"
-+ " 'type': 'integer', 'minimum': 0, 'maximum': 100"
-+ " },"
-+ " 'inner2': {"
-+ " 'description': 'inner2 description',"
-+ " 'type': 'integer',"
-+ " 'enum': [ 1, 2, 3 ],"
-+ " 'sensitiveValue': True"
-+ " },"
-+ " },"
-+ " },"
-+ " },"
-+ " 'caption': 'nothing special',"
-+ " 'owners': ['a@b']"
-+ " },"
-+ " ],"
-+ " 'policy_atomic_group_definitions': [],"
-+ " 'messages': {}"
-+ "}")
-+ gatherer = policy_json.PolicyJson(StringIO(original))
-+ gatherer.Parse()
-+ self.failUnless(len(gatherer.GetCliques()) == 4)
-+ expected = self.GetExpectedOutput(original)
-+ self.failUnless(expected == json.loads(gatherer.Translate('en')))
-+
-+ def testValidationSchema(self):
-+ original = ("{"
-+ " 'policy_definitions': ["
-+ " {"
-+ " 'name': 'Policy1',"
-+ " 'owners': ['a@b'],"
-+ " 'validation_schema': {"
-+ " 'type': 'object',"
-+ " 'properties': {"
-+ " 'description': 'properties description',"
-+ " 'type': 'object',"
-+ " },"
-+ " },"
-+ " },"
-+ " ],"
-+ " 'policy_atomic_group_definitions': [],"
-+ " 'messages': {}"
-+ "}")
-+ gatherer = policy_json.PolicyJson(StringIO(original))
-+ gatherer.Parse()
-+ self.failUnless(len(gatherer.GetCliques()) == 1)
-+ expected = self.GetExpectedOutput(original)
-+ self.failUnless(expected == json.loads(gatherer.Translate('en')))
-+
-+ def testDescriptionSchema(self):
-+ original = ("{"
-+ " 'policy_definitions': ["
-+ " {"
-+ " 'name': 'Policy1',"
-+ " 'owners': ['a@b'],"
-+ " 'description_schema': {"
-+ " 'type': 'object',"
-+ " 'properties': {"
-+ " 'description': 'properties description',"
-+ " 'type': 'object',"
-+ " },"
-+ " },"
-+ " },"
-+ " ],"
-+ " 'policy_atomic_group_definitions': [],"
-+ " 'messages': {}"
-+ "}")
-+ gatherer = policy_json.PolicyJson(StringIO(original))
-+ gatherer.Parse()
-+ self.failUnless(len(gatherer.GetCliques()) == 1)
-+ expected = self.GetExpectedOutput(original)
-+ self.failUnless(expected == json.loads(gatherer.Translate('en')))
-+
-+ # Keeping for backwards compatibility.
-+ def testSubPolicyOldFormat(self):
-+ original = (
-+ "{"
-+ " 'policy_definitions': ["
-+ " {"
-+ " 'type': 'group',"
-+ " 'policies': ["
-+ " {"
-+ " 'name': 'Policy1',"
-+ " 'caption': 'nothing special',"
-+ " 'owners': ['a@b']"
-+ " }"
-+ " ]"
-+ " }"
-+ " ],"
-+ " 'policy_atomic_group_definitions': [],"
-+ " 'messages': {}"
-+ "}")
-+ gatherer = policy_json.PolicyJson(StringIO(original))
-+ gatherer.Parse()
-+ self.failUnless(len(gatherer.GetCliques()) == 1)
-+ expected = self.GetExpectedOutput(original)
-+ self.failUnless(expected == json.loads(gatherer.Translate('en')))
-+
-+ def testSubPolicyNewFormat(self):
-+ original = (
-+ "{"
-+ " 'policy_definitions': ["
-+ " {"
-+ " 'type': 'group',"
-+ " 'policies': ['Policy1']"
-+ " },"
-+ " {"
-+ " 'name': 'Policy1',"
-+ " 'caption': 'nothing special',"
-+ " 'owners': ['a@b']"
-+ " }"
-+ " ],"
-+ " 'policy_atomic_group_definitions': [],"
-+ " 'messages': {}"
-+ "}")
-+ gatherer = policy_json.PolicyJson(StringIO(original))
-+ gatherer.Parse()
-+ self.failUnless(len(gatherer.GetCliques()) == 1)
-+ expected = self.GetExpectedOutput(original)
-+ self.failUnless(expected == json.loads(gatherer.Translate('en')))
-+
-+ def testEscapingAndLineBreaks(self):
-+ original = """{
-+ 'policy_definitions': [],
-+ 'policy_atomic_group_definitions': [],
-+ 'messages': {
-+ 'msg1': {
-+ # The following line will contain two backslash characters when it
-+ # ends up in eval().
-+ 'text': '''backslashes, Sir? \\\\''',
-+ 'desc': ''
-+ },
-+ 'msg2': {
-+ 'text': '''quotes, Madam? "''',
-+ 'desc': ''
-+ },
-+ 'msg3': {
-+ # The following line will contain two backslash characters when it
-+ # ends up in eval().
-+ 'text': 'backslashes, Sir? \\\\',
-+ 'desc': ''
-+ },
-+ 'msg4': {
-+ 'text': "quotes, Madam? '",
-+ 'desc': ''
-+ },
-+ 'msg5': {
-+ 'text': '''what happens
-+with a newline?''',
-+ 'desc': ''
-+ },
-+ 'msg6': {
-+ # The following line will contain a backslash+n when it ends up in
-+ # eval().
-+ 'text': 'what happens\\nwith a newline? (Episode 1)',
-+ 'desc': ''
-+ }
-+ }
-+}"""
-+ gatherer = policy_json.PolicyJson(StringIO(original))
-+ gatherer.Parse()
-+ self.failUnless(len(gatherer.GetCliques()) == 6)
-+ expected = self.GetExpectedOutput(original)
-+ self.failUnless(expected == json.loads(gatherer.Translate('en')))
-+
-+ def testPlaceholdersChromium(self):
-+ original = """{
-+ "policy_definitions": [
-+ {
-+ "name": "Policy1",
-+ "caption": "Please install\\n<ph name=\\"PRODUCT_NAME\\">$1<ex>Google Chrome</ex></ph>.",
-+ "owners": "a@b"
-+ }
-+ ],
-+ "policy_atomic_group_definitions": [],
-+ "messages": {}
-+}"""
-+ gatherer = policy_json.PolicyJson(StringIO(original))
-+ gatherer.SetDefines({'_chromium': True})
-+ gatherer.Parse()
-+ self.failUnless(len(gatherer.GetCliques()) == 1)
-+ expected = json.loads(re.sub('<ph.*ph>', 'Chromium', original))
-+ self.failUnless(expected == json.loads(gatherer.Translate('en')))
-+ self.failUnless(gatherer.GetCliques()[0].translateable)
-+ msg = gatherer.GetCliques()[0].GetMessage()
-+ self.failUnless(len(msg.GetPlaceholders()) == 1)
-+ ph = msg.GetPlaceholders()[0]
-+ self.failUnless(ph.GetOriginal() == 'Chromium')
-+ self.failUnless(ph.GetPresentation() == 'PRODUCT_NAME')
-+ self.failUnless(ph.GetExample() == 'Google Chrome')
-+
-+ def testPlaceholdersChrome(self):
-+ original = """{
-+ "policy_definitions": [
-+ {
-+ "name": "Policy1",
-+ "caption": "Please install\\n<ph name=\\"PRODUCT_NAME\\">$1<ex>Google Chrome</ex></ph>.",
-+ "owners": "a@b"
-+ }
-+ ],
-+ "policy_atomic_group_definitions": [],
-+ "messages": {}
-+}"""
-+ gatherer = policy_json.PolicyJson(StringIO(original))
-+ gatherer.SetDefines({'_google_chrome': True})
-+ gatherer.Parse()
-+ self.failUnless(len(gatherer.GetCliques()) == 1)
-+ expected = json.loads(re.sub('<ph.*ph>', 'Google Chrome', original))
-+ self.failUnless(expected == json.loads(gatherer.Translate('en')))
-+ self.failUnless(gatherer.GetCliques()[0].translateable)
-+ msg = gatherer.GetCliques()[0].GetMessage()
-+ self.failUnless(len(msg.GetPlaceholders()) == 1)
-+ ph = msg.GetPlaceholders()[0]
-+ self.failUnless(ph.GetOriginal() == 'Google Chrome')
-+ self.failUnless(ph.GetPresentation() == 'PRODUCT_NAME')
-+ self.failUnless(ph.GetExample() == 'Google Chrome')
-+
-+ def testGetDescription(self):
-+ gatherer = policy_json.PolicyJson({})
-+ gatherer.SetDefines({'_google_chrome': True})
-+ self.assertEquals(
-+ gatherer._GetDescription({'name': 'Policy1', 'owners': ['a@b']},
-+ 'policy', None, 'desc'),
-+ 'Description of the policy named Policy1 [owner(s): a@b]')
-+ self.assertEquals(
-+ gatherer._GetDescription({'name': 'Plcy2', 'owners': ['a@b', 'c@d']},
-+ 'policy', None, 'caption'),
-+ 'Caption of the policy named Plcy2 [owner(s): a@b,c@d]')
-+ self.assertEquals(
-+ gatherer._GetDescription({'name': 'Plcy3', 'owners': ['a@b']},
-+ 'policy', None, 'label'),
-+ 'Label of the policy named Plcy3 [owner(s): a@b]')
-+ self.assertEquals(
-+ gatherer._GetDescription({'name': 'Item'}, 'enum_item',
-+ {'name': 'Plcy', 'owners': ['a@b']}, 'caption'),
-+ 'Caption of the option named Item in policy Plcy [owner(s): a@b]')
-+
-+
-+if __name__ == '__main__':
-+ unittest.main()
-diff --git a/tools/grit/grit/gather/rc.py b/tools/grit/grit/gather/rc.py
-new file mode 100644
-index 0000000000..dd091d1e18
---- /dev/null
-+++ b/tools/grit/grit/gather/rc.py
-@@ -0,0 +1,343 @@
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Support for gathering resources from RC files.
-+'''
-+
-+from __future__ import print_function
-+
-+import re
-+
-+from grit import exception
-+from grit import lazy_re
-+from grit import tclib
-+
-+from grit.gather import regexp
-+
-+
-+# Find portions that need unescaping in resource strings. We need to be
-+# careful that a \\n is matched _first_ as a \\ rather than matching as
-+# a \ followed by a \n.
-+# TODO(joi) Handle ampersands if we decide to change them into <ph>
-+# TODO(joi) May need to handle other control characters than \n
-+_NEED_UNESCAPE = lazy_re.compile(r'""|\\\\|\\n|\\t')
-+
-+# Find portions that need escaping to encode string as a resource string.
-+_NEED_ESCAPE = lazy_re.compile(r'"|\n|\t|\\|\&nbsp\;')
-+
-+# How to escape certain characters
-+_ESCAPE_CHARS = {
-+ '"' : '""',
-+ '\n' : '\\n',
-+ '\t' : '\\t',
-+ '\\' : '\\\\',
-+ '&nbsp;' : ' '
-+}
-+
-+# How to unescape certain strings
-+_UNESCAPE_CHARS = dict([[value, key] for key, value in _ESCAPE_CHARS.items()])
-+
-+
-+
-+class Section(regexp.RegexpGatherer):
-+ '''A section from a resource file.'''
-+
-+ @staticmethod
-+ def Escape(text):
-+ '''Returns a version of 'text' with characters escaped that need to be
-+ for inclusion in a resource section.'''
-+ def Replace(match):
-+ return _ESCAPE_CHARS[match.group()]
-+ return _NEED_ESCAPE.sub(Replace, text)
-+
-+ @staticmethod
-+ def UnEscape(text):
-+ '''Returns a version of 'text' with escaped characters unescaped.'''
-+ def Replace(match):
-+ return _UNESCAPE_CHARS[match.group()]
-+ return _NEED_UNESCAPE.sub(Replace, text)
-+
-+ def _RegExpParse(self, rexp, text_to_parse):
-+ '''Overrides _RegExpParse to add shortcut group handling. Otherwise
-+ the same.
-+ '''
-+ super(Section, self)._RegExpParse(rexp, text_to_parse)
-+
-+ if not self.is_skeleton and len(self.GetTextualIds()) > 0:
-+ group_name = self.GetTextualIds()[0]
-+ for c in self.GetCliques():
-+ c.AddToShortcutGroup(group_name)
-+
-+ def ReadSection(self):
-+ rc_text = self._LoadInputFile()
-+
-+ out = ''
-+ begin_count = 0
-+ assert self.extkey
-+ first_line_re = re.compile(r'\s*' + self.extkey + r'\b')
-+ for line in rc_text.splitlines(True):
-+ if out or first_line_re.match(line):
-+ out += line
-+
-+ # we stop once we reach the END for the outermost block.
-+ begin_count_was = begin_count
-+ if len(out) > 0 and line.strip() == 'BEGIN':
-+ begin_count += 1
-+ elif len(out) > 0 and line.strip() == 'END':
-+ begin_count -= 1
-+ if begin_count_was == 1 and begin_count == 0:
-+ break
-+
-+ if len(out) == 0:
-+ raise exception.SectionNotFound('%s in file %s' % (self.extkey, self.rc_file))
-+
-+ self.text_ = out.strip()
-+
-+
-+class Dialog(Section):
-+ '''A resource section that contains a dialog resource.'''
-+
-+ # A typical dialog resource section looks like this:
-+ #
-+ # IDD_ABOUTBOX DIALOGEX 22, 17, 230, 75
-+ # STYLE DS_SETFONT | DS_MODALFRAME | WS_CAPTION | WS_SYSMENU
-+ # CAPTION "About"
-+ # FONT 8, "System", 0, 0, 0x0
-+ # BEGIN
-+ # ICON IDI_KLONK,IDC_MYICON,14,9,20,20
-+ # LTEXT "klonk Version ""yibbee"" 1.0",IDC_STATIC,49,10,119,8,
-+ # SS_NOPREFIX
-+ # LTEXT "Copyright (C) 2005",IDC_STATIC,49,20,119,8
-+ # DEFPUSHBUTTON "OK",IDOK,195,6,30,11,WS_GROUP
-+ # CONTROL "Jack ""Black"" Daniels",IDC_RADIO1,"Button",
-+ # BS_AUTORADIOBUTTON,46,51,84,10
-+ # END
-+
-+ # We are using a sorted set of keys, and we assume that the
-+ # group name used for descriptions (type) will come after the "text"
-+ # group in alphabetical order. We also assume that there cannot be
-+ # more than one description per regular expression match.
-+ # If that's not the case some descriptions will be clobbered.
-+ dialog_re_ = lazy_re.compile(r'''
-+ # The dialog's ID in the first line
-+ (?P<id1>[A-Z0-9_]+)\s+DIALOG(EX)?
-+ |
-+ # The caption of the dialog
-+ (?P<type1>CAPTION)\s+"(?P<text1>.*?([^"]|""))"\s
-+ |
-+ # Lines for controls that have text and an ID
-+ \s+(?P<type2>[A-Z]+)\s+"(?P<text2>.*?([^"]|"")?)"\s*,\s*(?P<id2>[A-Z0-9_]+)\s*,
-+ |
-+ # Lines for controls that have text only
-+ \s+(?P<type3>[A-Z]+)\s+"(?P<text3>.*?([^"]|"")?)"\s*,
-+ |
-+ # Lines for controls that reference other resources
-+ \s+[A-Z]+\s+[A-Z0-9_]+\s*,\s*(?P<id3>[A-Z0-9_]*[A-Z][A-Z0-9_]*)
-+ |
-+ # This matches "NOT SOME_STYLE" so that it gets consumed and doesn't get
-+ # matched by the next option (controls that have only an ID and then just
-+ # numbers)
-+ \s+NOT\s+[A-Z][A-Z0-9_]+
-+ |
-+ # Lines for controls that have only an ID and then just numbers
-+ \s+[A-Z]+\s+(?P<id4>[A-Z0-9_]*[A-Z][A-Z0-9_]*)\s*,
-+ ''', re.MULTILINE | re.VERBOSE)
-+
-+ def Parse(self):
-+ '''Knows how to parse dialog resource sections.'''
-+ self.ReadSection()
-+ self._RegExpParse(self.dialog_re_, self.text_)
-+
-+
-+class Menu(Section):
-+ '''A resource section that contains a menu resource.'''
-+
-+ # A typical menu resource section looks something like this:
-+ #
-+ # IDC_KLONK MENU
-+ # BEGIN
-+ # POPUP "&File"
-+ # BEGIN
-+ # MENUITEM "E&xit", IDM_EXIT
-+ # MENUITEM "This be ""Klonk"" me like", ID_FILE_THISBE
-+ # POPUP "gonk"
-+ # BEGIN
-+ # MENUITEM "Klonk && is ""good""", ID_GONK_KLONKIS
-+ # END
-+ # END
-+ # POPUP "&Help"
-+ # BEGIN
-+ # MENUITEM "&About ...", IDM_ABOUT
-+ # END
-+ # END
-+
-+ # Description used for the messages generated for menus, to explain to
-+ # the translators how to handle them.
-+ MENU_MESSAGE_DESCRIPTION = (
-+ 'This message represents a menu. Each of the items appears in sequence '
-+ '(some possibly within sub-menus) in the menu. The XX01XX placeholders '
-+ 'serve to separate items. Each item contains an & (ampersand) character '
-+ 'in front of the keystroke that should be used as a shortcut for that item '
-+ 'in the menu. Please make sure that no two items in the same menu share '
-+ 'the same shortcut.'
-+ )
-+
-+ # A dandy regexp to suck all the IDs and translateables out of a menu
-+ # resource
-+ menu_re_ = lazy_re.compile(r'''
-+ # Match the MENU ID on the first line
-+ ^(?P<id1>[A-Z0-9_]+)\s+MENU
-+ |
-+ # Match the translateable caption for a popup menu
-+ POPUP\s+"(?P<text1>.*?([^"]|""))"\s
-+ |
-+ # Match the caption & ID of a MENUITEM
-+ MENUITEM\s+"(?P<text2>.*?([^"]|""))"\s*,\s*(?P<id2>[A-Z0-9_]+)
-+ ''', re.MULTILINE | re.VERBOSE)
-+
-+ def Parse(self):
-+ '''Knows how to parse menu resource sections. Because it is important that
-+ menu shortcuts are unique within the menu, we return each menu as a single
-+ message with placeholders to break up the different menu items, rather than
-+ return a single message per menu item. we also add an automatic description
-+ with instructions for the translators.'''
-+ self.ReadSection()
-+ self.single_message_ = tclib.Message(description=self.MENU_MESSAGE_DESCRIPTION)
-+ self._RegExpParse(self.menu_re_, self.text_)
-+
-+
-+class Version(Section):
-+ '''A resource section that contains a VERSIONINFO resource.'''
-+
-+ # A typical version info resource can look like this:
-+ #
-+ # VS_VERSION_INFO VERSIONINFO
-+ # FILEVERSION 1,0,0,1
-+ # PRODUCTVERSION 1,0,0,1
-+ # FILEFLAGSMASK 0x3fL
-+ # #ifdef _DEBUG
-+ # FILEFLAGS 0x1L
-+ # #else
-+ # FILEFLAGS 0x0L
-+ # #endif
-+ # FILEOS 0x4L
-+ # FILETYPE 0x2L
-+ # FILESUBTYPE 0x0L
-+ # BEGIN
-+ # BLOCK "StringFileInfo"
-+ # BEGIN
-+ # BLOCK "040904e4"
-+ # BEGIN
-+ # VALUE "CompanyName", "TODO: <Company name>"
-+ # VALUE "FileDescription", "TODO: <File description>"
-+ # VALUE "FileVersion", "1.0.0.1"
-+ # VALUE "LegalCopyright", "TODO: (c) <Company name>. All rights reserved."
-+ # VALUE "InternalName", "res_format_test.dll"
-+ # VALUE "OriginalFilename", "res_format_test.dll"
-+ # VALUE "ProductName", "TODO: <Product name>"
-+ # VALUE "ProductVersion", "1.0.0.1"
-+ # END
-+ # END
-+ # BLOCK "VarFileInfo"
-+ # BEGIN
-+ # VALUE "Translation", 0x409, 1252
-+ # END
-+ # END
-+ #
-+ #
-+ # In addition to the above fields, VALUE fields named "Comments" and
-+ # "LegalTrademarks" may also be translateable.
-+
-+ version_re_ = lazy_re.compile(r'''
-+ # Match the ID on the first line
-+ ^(?P<id1>[A-Z0-9_]+)\s+VERSIONINFO
-+ |
-+ # Match all potentially translateable VALUE sections
-+ \s+VALUE\s+"
-+ (
-+ CompanyName|FileDescription|LegalCopyright|
-+ ProductName|Comments|LegalTrademarks
-+ )",\s+"(?P<text1>.*?([^"]|""))"\s
-+ ''', re.MULTILINE | re.VERBOSE)
-+
-+ def Parse(self):
-+ '''Knows how to parse VERSIONINFO resource sections.'''
-+ self.ReadSection()
-+ self._RegExpParse(self.version_re_, self.text_)
-+
-+ # TODO(joi) May need to override the Translate() method to change the
-+ # "Translation" VALUE block to indicate the correct language code.
-+
-+
-+class RCData(Section):
-+ '''A resource section that contains some data .'''
-+
-+ # A typical rcdataresource section looks like this:
-+ #
-+ # IDR_BLAH RCDATA { 1, 2, 3, 4 }
-+
-+ dialog_re_ = lazy_re.compile(r'''
-+ ^(?P<id1>[A-Z0-9_]+)\s+RCDATA\s+(DISCARDABLE)?\s+\{.*?\}
-+ ''', re.MULTILINE | re.VERBOSE | re.DOTALL)
-+
-+ def Parse(self):
-+ '''Implementation for resource types w/braces (not BEGIN/END)
-+ '''
-+ rc_text = self._LoadInputFile()
-+
-+ out = ''
-+ begin_count = 0
-+ openbrace_count = 0
-+ assert self.extkey
-+ first_line_re = re.compile(r'\s*' + self.extkey + r'\b')
-+ for line in rc_text.splitlines(True):
-+ if out or first_line_re.match(line):
-+ out += line
-+
-+ # We stop once the braces balance (could happen in one line).
-+ begin_count_was = begin_count
-+ if len(out) > 0:
-+ openbrace_count += line.count('{')
-+ begin_count += line.count('{')
-+ begin_count -= line.count('}')
-+ if ((begin_count_was == 1 and begin_count == 0) or
-+ (openbrace_count > 0 and begin_count == 0)):
-+ break
-+
-+ if len(out) == 0:
-+ raise exception.SectionNotFound('%s in file %s' % (self.extkey, self.rc_file))
-+
-+ self.text_ = out
-+
-+ self._RegExpParse(self.dialog_re_, out)
-+
-+
-+class Accelerators(Section):
-+ '''An ACCELERATORS table.
-+ '''
-+
-+ # A typical ACCELERATORS section looks like this:
-+ #
-+ # IDR_ACCELERATOR1 ACCELERATORS
-+ # BEGIN
-+ # "^C", ID_ACCELERATOR32770, ASCII, NOINVERT
-+ # "^V", ID_ACCELERATOR32771, ASCII, NOINVERT
-+ # VK_INSERT, ID_ACCELERATOR32772, VIRTKEY, CONTROL, NOINVERT
-+ # END
-+
-+ accelerators_re_ = lazy_re.compile(r'''
-+ # Match the ID on the first line
-+ ^(?P<id1>[A-Z0-9_]+)\s+ACCELERATORS\s+
-+ |
-+ # Match accelerators specified as VK_XXX
-+ \s+VK_[A-Z0-9_]+,\s*(?P<id2>[A-Z0-9_]+)\s*,
-+ |
-+ # Match accelerators specified as e.g. "^C"
-+ \s+"[^"]*",\s+(?P<id3>[A-Z0-9_]+)\s*,
-+ ''', re.MULTILINE | re.VERBOSE)
-+
-+ def Parse(self):
-+ '''Knows how to parse ACCELERATORS resource sections.'''
-+ self.ReadSection()
-+ self._RegExpParse(self.accelerators_re_, self.text_)
-diff --git a/tools/grit/grit/gather/rc_unittest.py b/tools/grit/grit/gather/rc_unittest.py
-new file mode 100644
-index 0000000000..3c26a4342a
---- /dev/null
-+++ b/tools/grit/grit/gather/rc_unittest.py
-@@ -0,0 +1,372 @@
-+#!/usr/bin/env python
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Unit tests for grit.gather.rc'''
-+
-+from __future__ import print_function
-+
-+import os
-+import sys
-+if __name__ == '__main__':
-+ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
-+
-+import unittest
-+
-+from six import StringIO
-+
-+from grit.gather import rc
-+from grit import util
-+
-+
-+class RcUnittest(unittest.TestCase):
-+
-+ part_we_want = '''IDC_KLONKACC ACCELERATORS
-+BEGIN
-+ "?", IDM_ABOUT, ASCII, ALT
-+ "/", IDM_ABOUT, ASCII, ALT
-+END'''
-+
-+ def testSectionFromFile(self):
-+ buf = '''IDC_SOMETHINGELSE BINGO
-+BEGIN
-+ BLA BLA
-+ BLA BLA
-+END
-+%s
-+
-+IDC_KLONK BINGOBONGO
-+BEGIN
-+ HONGO KONGO
-+END
-+''' % self.part_we_want
-+
-+ f = StringIO(buf)
-+
-+ out = rc.Section(f, 'IDC_KLONKACC')
-+ out.ReadSection()
-+ self.failUnless(out.GetText() == self.part_we_want)
-+
-+ out = rc.Section(util.PathFromRoot(r'grit/testdata/klonk.rc'),
-+ 'IDC_KLONKACC',
-+ encoding='utf-16')
-+ out.ReadSection()
-+ out_text = out.GetText().replace('\t', '')
-+ out_text = out_text.replace(' ', '')
-+ self.part_we_want = self.part_we_want.replace(' ', '')
-+ self.failUnless(out_text.strip() == self.part_we_want.strip())
-+
-+
-+ def testDialog(self):
-+ dlg = rc.Dialog(StringIO('''IDD_ABOUTBOX DIALOGEX 22, 17, 230, 75
-+STYLE DS_SETFONT | DS_MODALFRAME | WS_CAPTION | WS_SYSMENU
-+CAPTION "About"
-+FONT 8, "System", 0, 0, 0x0
-+BEGIN
-+ ICON IDI_KLONK,IDC_MYICON,14,9,20,20
-+ LTEXT "klonk Version ""yibbee"" 1.0",IDC_STATIC,49,10,119,8,
-+ SS_NOPREFIX
-+ LTEXT "Copyright (C) 2005",IDC_STATIC,49,20,119,8
-+ DEFPUSHBUTTON "OK",IDOK,195,6,30,11,WS_GROUP
-+ CONTROL "Jack ""Black"" Daniels",IDC_RADIO1,"Button",
-+ BS_AUTORADIOBUTTON,46,51,84,10
-+ // try a line where the ID is on the continuation line
-+ LTEXT "blablablabla blablabla blablablablablablablabla blablabla",
-+ ID_SMURF, whatever...
-+END
-+'''), 'IDD_ABOUTBOX')
-+ dlg.Parse()
-+ self.failUnless(len(dlg.GetTextualIds()) == 7)
-+ self.failUnless(len(dlg.GetCliques()) == 6)
-+ self.failUnless(dlg.GetCliques()[1].GetMessage().GetRealContent() ==
-+ 'klonk Version "yibbee" 1.0')
-+
-+ transl = dlg.Translate('en')
-+ self.failUnless(transl.strip() == dlg.GetText().strip())
-+
-+ def testAlternateSkeleton(self):
-+ dlg = rc.Dialog(StringIO('''IDD_ABOUTBOX DIALOGEX 22, 17, 230, 75
-+STYLE DS_SETFONT | DS_MODALFRAME | WS_CAPTION | WS_SYSMENU
-+CAPTION "About"
-+FONT 8, "System", 0, 0, 0x0
-+BEGIN
-+ LTEXT "Yipee skippy",IDC_STATIC,49,10,119,8,
-+ SS_NOPREFIX
-+END
-+'''), 'IDD_ABOUTBOX')
-+ dlg.Parse()
-+
-+ alt_dlg = rc.Dialog(StringIO('''IDD_ABOUTBOX DIALOGEX 040704, 17, 230, 75
-+STYLE DS_SETFONT | DS_MODALFRAME | WS_CAPTION | WS_SYSMENU
-+CAPTION "XXXXXXXXX"
-+FONT 8, "System", 0, 0, 0x0
-+BEGIN
-+ LTEXT "XXXXXXXXXXXXXXXXX",IDC_STATIC,110978,10,119,8,
-+ SS_NOPREFIX
-+END
-+'''), 'IDD_ABOUTBOX')
-+ alt_dlg.Parse()
-+
-+ transl = dlg.Translate('en', skeleton_gatherer=alt_dlg)
-+ self.failUnless(transl.count('040704') and
-+ transl.count('110978'))
-+ self.failUnless(transl.count('Yipee skippy'))
-+
-+ def testMenu(self):
-+ menu = rc.Menu(StringIO('''IDC_KLONK MENU
-+BEGIN
-+ POPUP "&File """
-+ BEGIN
-+ MENUITEM "E&xit", IDM_EXIT
-+ MENUITEM "This be ""Klonk"" me like", ID_FILE_THISBE
-+ POPUP "gonk"
-+ BEGIN
-+ MENUITEM "Klonk && is ""good""", ID_GONK_KLONKIS
-+ END
-+ MENUITEM "This is a very long menu caption to try to see if we can make the ID go to a continuation line, blablabla blablabla bla blabla blablabla blablabla blablabla blablabla...",
-+ ID_FILE_THISISAVERYLONGMENUCAPTIONTOTRYTOSEEIFWECANMAKETHEIDGOTOACONTINUATIONLINE
-+ END
-+ POPUP "&Help"
-+ BEGIN
-+ MENUITEM "&About ...", IDM_ABOUT
-+ END
-+END'''), 'IDC_KLONK')
-+
-+ menu.Parse()
-+ self.failUnless(len(menu.GetTextualIds()) == 6)
-+ self.failUnless(len(menu.GetCliques()) == 1)
-+ self.failUnless(len(menu.GetCliques()[0].GetMessage().GetPlaceholders()) ==
-+ 9)
-+
-+ transl = menu.Translate('en')
-+ self.failUnless(transl.strip() == menu.GetText().strip())
-+
-+ def testVersion(self):
-+ version = rc.Version(StringIO('''
-+VS_VERSION_INFO VERSIONINFO
-+ FILEVERSION 1,0,0,1
-+ PRODUCTVERSION 1,0,0,1
-+ FILEFLAGSMASK 0x3fL
-+#ifdef _DEBUG
-+ FILEFLAGS 0x1L
-+#else
-+ FILEFLAGS 0x0L
-+#endif
-+ FILEOS 0x4L
-+ FILETYPE 0x2L
-+ FILESUBTYPE 0x0L
-+BEGIN
-+ BLOCK "StringFileInfo"
-+ BEGIN
-+ BLOCK "040904e4"
-+ BEGIN
-+ VALUE "CompanyName", "TODO: <Company name>"
-+ VALUE "FileDescription", "TODO: <File description>"
-+ VALUE "FileVersion", "1.0.0.1"
-+ VALUE "LegalCopyright", "TODO: (c) <Company name>. All rights reserved."
-+ VALUE "InternalName", "res_format_test.dll"
-+ VALUE "OriginalFilename", "res_format_test.dll"
-+ VALUE "ProductName", "TODO: <Product name>"
-+ VALUE "ProductVersion", "1.0.0.1"
-+ END
-+ END
-+ BLOCK "VarFileInfo"
-+ BEGIN
-+ VALUE "Translation", 0x409, 1252
-+ END
-+END
-+'''.strip()), 'VS_VERSION_INFO')
-+ version.Parse()
-+ self.failUnless(len(version.GetTextualIds()) == 1)
-+ self.failUnless(len(version.GetCliques()) == 4)
-+
-+ transl = version.Translate('en')
-+ self.failUnless(transl.strip() == version.GetText().strip())
-+
-+
-+ def testRegressionDialogBox(self):
-+ dialog = rc.Dialog(StringIO('''
-+IDD_SIDEBAR_WEATHER_PANEL_PROPPAGE DIALOGEX 0, 0, 205, 157
-+STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD
-+FONT 8, "MS Shell Dlg", 400, 0, 0x1
-+BEGIN
-+ EDITTEXT IDC_SIDEBAR_WEATHER_NEW_CITY,3,27,112,14,ES_AUTOHSCROLL
-+ DEFPUSHBUTTON "Add Location",IDC_SIDEBAR_WEATHER_ADD,119,27,50,14
-+ LISTBOX IDC_SIDEBAR_WEATHER_CURR_CITIES,3,48,127,89,
-+ LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP
-+ PUSHBUTTON "Move Up",IDC_SIDEBAR_WEATHER_MOVE_UP,134,104,50,14
-+ PUSHBUTTON "Move Down",IDC_SIDEBAR_WEATHER_MOVE_DOWN,134,121,50,14
-+ PUSHBUTTON "Remove",IDC_SIDEBAR_WEATHER_DELETE,134,48,50,14
-+ LTEXT "To see current weather conditions and forecasts in the USA, enter the zip code (example: 94043) or city and state (example: Mountain View, CA).",
-+ IDC_STATIC,3,0,199,25
-+ CONTROL "Fahrenheit",IDC_SIDEBAR_WEATHER_FAHRENHEIT,"Button",
-+ BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,3,144,51,10
-+ CONTROL "Celsius",IDC_SIDEBAR_WEATHER_CELSIUS,"Button",
-+ BS_AUTORADIOBUTTON,57,144,38,10
-+END'''.strip()), 'IDD_SIDEBAR_WEATHER_PANEL_PROPPAGE')
-+ dialog.Parse()
-+ self.failUnless(len(dialog.GetTextualIds()) == 10)
-+
-+
-+ def testRegressionDialogBox2(self):
-+ dialog = rc.Dialog(StringIO('''
-+IDD_SIDEBAR_EMAIL_PANEL_PROPPAGE DIALOG DISCARDABLE 0, 0, 264, 220
-+STYLE WS_CHILD
-+FONT 8, "MS Shell Dlg"
-+BEGIN
-+ GROUPBOX "Email Filters",IDC_STATIC,7,3,250,190
-+ LTEXT "Click Add Filter to create the email filter.",IDC_STATIC,16,41,130,9
-+ PUSHBUTTON "Add Filter...",IDC_SIDEBAR_EMAIL_ADD_FILTER,196,38,50,14
-+ PUSHBUTTON "Remove",IDC_SIDEBAR_EMAIL_REMOVE,196,174,50,14
-+ PUSHBUTTON "", IDC_SIDEBAR_EMAIL_HIDDEN, 200, 178, 5, 5, NOT WS_VISIBLE
-+ LISTBOX IDC_SIDEBAR_EMAIL_LIST,16,60,230,108,
-+ LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP
-+ LTEXT "You can prevent certain emails from showing up in the sidebar with a filter.",
-+ IDC_STATIC,16,18,234,18
-+END'''.strip()), 'IDD_SIDEBAR_EMAIL_PANEL_PROPPAGE')
-+ dialog.Parse()
-+ self.failUnless('IDC_SIDEBAR_EMAIL_HIDDEN' in dialog.GetTextualIds())
-+
-+
-+ def testRegressionMenuId(self):
-+ menu = rc.Menu(StringIO('''
-+IDR_HYPERMENU_FOLDER MENU
-+BEGIN
-+ POPUP "HyperFolder"
-+ BEGIN
-+ MENUITEM "Open Containing Folder", IDM_OPENFOLDER
-+ END
-+END'''.strip()), 'IDR_HYPERMENU_FOLDER')
-+ menu.Parse()
-+ self.failUnless(len(menu.GetTextualIds()) == 2)
-+
-+ def testRegressionNewlines(self):
-+ menu = rc.Menu(StringIO('''
-+IDR_HYPERMENU_FOLDER MENU
-+BEGIN
-+ POPUP "Hyper\\nFolder"
-+ BEGIN
-+ MENUITEM "Open Containing Folder", IDM_OPENFOLDER
-+ END
-+END'''.strip()), 'IDR_HYPERMENU_FOLDER')
-+ menu.Parse()
-+ transl = menu.Translate('en')
-+ # Shouldn't find \\n (the \n shouldn't be changed to \\n)
-+ self.failUnless(transl.find('\\\\n') == -1)
-+
-+ def testRegressionTabs(self):
-+ menu = rc.Menu(StringIO('''
-+IDR_HYPERMENU_FOLDER MENU
-+BEGIN
-+ POPUP "Hyper\\tFolder"
-+ BEGIN
-+ MENUITEM "Open Containing Folder", IDM_OPENFOLDER
-+ END
-+END'''.strip()), 'IDR_HYPERMENU_FOLDER')
-+ menu.Parse()
-+ transl = menu.Translate('en')
-+ # Shouldn't find \\t (the \t shouldn't be changed to \\t)
-+ self.failUnless(transl.find('\\\\t') == -1)
-+
-+ def testEscapeUnescape(self):
-+ original = 'Hello "bingo"\n How\\are\\you\\n?'
-+ escaped = rc.Section.Escape(original)
-+ self.failUnless(escaped == 'Hello ""bingo""\\n How\\\\are\\\\you\\\\n?')
-+ unescaped = rc.Section.UnEscape(escaped)
-+ self.failUnless(unescaped == original)
-+
-+ def testRegressionPathsWithSlashN(self):
-+ original = '..\\\\..\\\\trs\\\\res\\\\nav_first.gif'
-+ unescaped = rc.Section.UnEscape(original)
-+ self.failUnless(unescaped == '..\\..\\trs\\res\\nav_first.gif')
-+
-+ def testRegressionDialogItemsTextOnly(self):
-+ dialog = rc.Dialog(StringIO('''IDD_OPTIONS_SEARCH DIALOGEX 0, 0, 280, 292
-+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP |
-+ WS_DISABLED | WS_CAPTION | WS_SYSMENU
-+CAPTION "Search"
-+FONT 8, "MS Shell Dlg", 400, 0, 0x1
-+BEGIN
-+ GROUPBOX "Select search buttons and options",-1,7,5,266,262
-+ CONTROL "",IDC_OPTIONS,"SysTreeView32",TVS_DISABLEDRAGDROP |
-+ WS_BORDER | WS_TABSTOP | 0x800,16,19,248,218
-+ LTEXT "Use Google site:",-1,26,248,52,8
-+ COMBOBOX IDC_GOOGLE_HOME,87,245,177,256,CBS_DROPDOWNLIST |
-+ WS_VSCROLL | WS_TABSTOP
-+ PUSHBUTTON "Restore Defaults...",IDC_RESET,187,272,86,14
-+END'''), 'IDD_OPTIONS_SEARCH')
-+ dialog.Parse()
-+ translateables = [c.GetMessage().GetRealContent()
-+ for c in dialog.GetCliques()]
-+ self.failUnless('Select search buttons and options' in translateables)
-+ self.failUnless('Use Google site:' in translateables)
-+
-+ def testAccelerators(self):
-+ acc = rc.Accelerators(StringIO('''\
-+IDR_ACCELERATOR1 ACCELERATORS
-+BEGIN
-+ "^C", ID_ACCELERATOR32770, ASCII, NOINVERT
-+ "^V", ID_ACCELERATOR32771, ASCII, NOINVERT
-+ VK_INSERT, ID_ACCELERATOR32772, VIRTKEY, CONTROL, NOINVERT
-+END
-+'''), 'IDR_ACCELERATOR1')
-+ acc.Parse()
-+ self.failUnless(len(acc.GetTextualIds()) == 4)
-+ self.failUnless(len(acc.GetCliques()) == 0)
-+
-+ transl = acc.Translate('en')
-+ self.failUnless(transl.strip() == acc.GetText().strip())
-+
-+
-+ def testRegressionEmptyString(self):
-+ dlg = rc.Dialog(StringIO('''\
-+IDD_CONFIRM_QUIT_GD_DLG DIALOGEX 0, 0, 267, 108
-+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP |
-+ WS_CAPTION
-+EXSTYLE WS_EX_TOPMOST
-+CAPTION "Google Desktop"
-+FONT 8, "MS Shell Dlg", 400, 0, 0x1
-+BEGIN
-+ DEFPUSHBUTTON "&Yes",IDYES,82,87,50,14
-+ PUSHBUTTON "&No",IDNO,136,87,50,14
-+ ICON 32514,IDC_STATIC,7,9,21,20
-+ EDITTEXT IDC_TEXTBOX,34,7,231,60,ES_MULTILINE | ES_READONLY | NOT WS_BORDER
-+ CONTROL "",
-+ IDC_ENABLE_GD_AUTOSTART,"Button",BS_AUTOCHECKBOX |
-+ WS_TABSTOP,33,70,231,10
-+END'''), 'IDD_CONFIRM_QUIT_GD_DLG')
-+ dlg.Parse()
-+
-+ def Check():
-+ self.failUnless(transl.count('IDC_ENABLE_GD_AUTOSTART'))
-+ self.failUnless(transl.count('END'))
-+
-+ transl = dlg.Translate('de', pseudo_if_not_available=True,
-+ fallback_to_english=True)
-+ Check()
-+ transl = dlg.Translate('de', pseudo_if_not_available=True,
-+ fallback_to_english=False)
-+ Check()
-+ transl = dlg.Translate('de', pseudo_if_not_available=False,
-+ fallback_to_english=True)
-+ Check()
-+ transl = dlg.Translate('de', pseudo_if_not_available=False,
-+ fallback_to_english=False)
-+ Check()
-+ transl = dlg.Translate('en', pseudo_if_not_available=True,
-+ fallback_to_english=True)
-+ Check()
-+ transl = dlg.Translate('en', pseudo_if_not_available=True,
-+ fallback_to_english=False)
-+ Check()
-+ transl = dlg.Translate('en', pseudo_if_not_available=False,
-+ fallback_to_english=True)
-+ Check()
-+ transl = dlg.Translate('en', pseudo_if_not_available=False,
-+ fallback_to_english=False)
-+ Check()
-+
-+
-+if __name__ == '__main__':
-+ unittest.main()
-diff --git a/tools/grit/grit/gather/regexp.py b/tools/grit/grit/gather/regexp.py
-new file mode 100644
-index 0000000000..97ce2cfbf7
---- /dev/null
-+++ b/tools/grit/grit/gather/regexp.py
-@@ -0,0 +1,82 @@
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''A baseclass for simple gatherers based on regular expressions.
-+'''
-+
-+from __future__ import print_function
-+
-+from grit.gather import skeleton_gatherer
-+
-+
-+class RegexpGatherer(skeleton_gatherer.SkeletonGatherer):
-+ '''Common functionality of gatherers based on parsing using a single
-+ regular expression.
-+ '''
-+
-+ DescriptionMapping_ = {
-+ 'CAPTION' : 'This is a caption for a dialog',
-+ 'CHECKBOX' : 'This is a label for a checkbox',
-+ 'CONTROL': 'This is the text on a control',
-+ 'CTEXT': 'This is a label for a control',
-+ 'DEFPUSHBUTTON': 'This is a button definition',
-+ 'GROUPBOX': 'This is a label for a grouping',
-+ 'ICON': 'This is a label for an icon',
-+ 'LTEXT': 'This is the text for a label',
-+ 'PUSHBUTTON': 'This is the text for a button',
-+ }
-+
-+ # Contextualization elements. Used for adding additional information
-+ # to the message bundle description string from RC files.
-+ def AddDescriptionElement(self, string):
-+ if string in self.DescriptionMapping_:
-+ description = self.DescriptionMapping_[string]
-+ else:
-+ description = string
-+ if self.single_message_:
-+ self.single_message_.SetDescription(description)
-+ else:
-+ if (self.translatable_chunk_):
-+ message = self.skeleton_[len(self.skeleton_) - 1].GetMessage()
-+ message.SetDescription(description)
-+
-+ def _RegExpParse(self, regexp, text_to_parse):
-+ '''An implementation of Parse() that can be used for resource sections that
-+ can be parsed using a single multi-line regular expression.
-+
-+ All translateables must be in named groups that have names starting with
-+ 'text'. All textual IDs must be in named groups that have names starting
-+ with 'id'. All type definitions that can be included in the description
-+ field for contextualization purposes should have a name that starts with
-+ 'type'.
-+
-+ Args:
-+ regexp: re.compile('...', re.MULTILINE)
-+ text_to_parse:
-+ '''
-+ chunk_start = 0
-+ for match in regexp.finditer(text_to_parse):
-+ groups = match.groupdict()
-+ keys = sorted(groups.keys())
-+ self.translatable_chunk_ = False
-+ for group in keys:
-+ if group.startswith('id') and groups[group]:
-+ self._AddTextualId(groups[group])
-+ elif group.startswith('text') and groups[group]:
-+ self._AddNontranslateableChunk(
-+ text_to_parse[chunk_start : match.start(group)])
-+ chunk_start = match.end(group) # Next chunk will start after the match
-+ self._AddTranslateableChunk(groups[group])
-+ elif group.startswith('type') and groups[group]:
-+ # Add the description to the skeleton_ list. This works because
-+ # we are using a sort set of keys, and because we assume that the
-+ # group name used for descriptions (type) will come after the "text"
-+ # group in alphabetical order. We also assume that there cannot be
-+ # more than one description per regular expression match.
-+ self.AddDescriptionElement(groups[group])
-+
-+ self._AddNontranslateableChunk(text_to_parse[chunk_start:])
-+
-+ if self.single_message_:
-+ self.skeleton_.append(self.uberclique.MakeClique(self.single_message_))
-diff --git a/tools/grit/grit/gather/skeleton_gatherer.py b/tools/grit/grit/gather/skeleton_gatherer.py
-new file mode 100644
-index 0000000000..b11862b314
---- /dev/null
-+++ b/tools/grit/grit/gather/skeleton_gatherer.py
-@@ -0,0 +1,149 @@
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''A baseclass for simple gatherers that store their gathered resource in a
-+list.
-+'''
-+
-+from __future__ import print_function
-+
-+import six
-+
-+from grit.gather import interface
-+from grit import clique
-+from grit import exception
-+from grit import tclib
-+
-+
-+class SkeletonGatherer(interface.GathererBase):
-+ '''Common functionality of gatherers that parse their input as a skeleton of
-+ translatable and nontranslatable chunks.
-+ '''
-+
-+ def __init__(self, *args, **kwargs):
-+ super(SkeletonGatherer, self).__init__(*args, **kwargs)
-+ # List of parts of the document. Translateable parts are
-+ # clique.MessageClique objects, nontranslateable parts are plain strings.
-+ # Translated messages are inserted back into the skeleton using the quoting
-+ # rules defined by self.Escape()
-+ self.skeleton_ = []
-+ # A list of the names of IDs that need to be defined for this resource
-+ # section to compile correctly.
-+ self.ids_ = []
-+ # True if Parse() has already been called.
-+ self.have_parsed_ = False
-+ # True if a translatable chunk has been added
-+ self.translatable_chunk_ = False
-+ # If not None, all parts of the document will be put into this single
-+ # message; otherwise the normal skeleton approach is used.
-+ self.single_message_ = None
-+ # Number to use for the next placeholder name. Used only if single_message
-+ # is not None
-+ self.ph_counter_ = 1
-+
-+ def GetText(self):
-+ '''Returns the original text of the section'''
-+ return self.text_
-+
-+ def Escape(self, text):
-+ '''Subclasses can override. Base impl is identity.
-+ '''
-+ return text
-+
-+ def UnEscape(self, text):
-+ '''Subclasses can override. Base impl is identity.
-+ '''
-+ return text
-+
-+ def GetTextualIds(self):
-+ '''Returns the list of textual IDs that need to be defined for this
-+ resource section to compile correctly.'''
-+ return self.ids_
-+
-+ def _AddTextualId(self, id):
-+ self.ids_.append(id)
-+
-+ def GetCliques(self):
-+ '''Returns the message cliques for each translateable message in the
-+ resource section.'''
-+ return [x for x in self.skeleton_ if isinstance(x, clique.MessageClique)]
-+
-+ def Translate(self, lang, pseudo_if_not_available=True,
-+ skeleton_gatherer=None, fallback_to_english=False):
-+ if len(self.skeleton_) == 0:
-+ raise exception.NotReady()
-+ if skeleton_gatherer:
-+ assert len(skeleton_gatherer.skeleton_) == len(self.skeleton_)
-+
-+ out = []
-+ for ix in range(len(self.skeleton_)):
-+ if isinstance(self.skeleton_[ix], six.string_types):
-+ if skeleton_gatherer:
-+ # Make sure the skeleton is like the original
-+ assert(isinstance(skeleton_gatherer.skeleton_[ix], six.string_types))
-+ out.append(skeleton_gatherer.skeleton_[ix])
-+ else:
-+ out.append(self.skeleton_[ix])
-+ else:
-+ if skeleton_gatherer: # Make sure the skeleton is like the original
-+ assert(not isinstance(skeleton_gatherer.skeleton_[ix],
-+ six.string_types))
-+ msg = self.skeleton_[ix].MessageForLanguage(lang,
-+ pseudo_if_not_available,
-+ fallback_to_english)
-+
-+ def MyEscape(text):
-+ return self.Escape(text)
-+ text = msg.GetRealContent(escaping_function=MyEscape)
-+ out.append(text)
-+ return ''.join(out)
-+
-+ def Parse(self):
-+ '''Parses the section. Implemented by subclasses. Idempotent.'''
-+ raise NotImplementedError()
-+
-+ def _AddNontranslateableChunk(self, chunk):
-+ '''Adds a nontranslateable chunk.'''
-+ if self.single_message_:
-+ ph = tclib.Placeholder('XX%02dXX' % self.ph_counter_, chunk, chunk)
-+ self.ph_counter_ += 1
-+ self.single_message_.AppendPlaceholder(ph)
-+ else:
-+ self.skeleton_.append(chunk)
-+
-+ def _AddTranslateableChunk(self, chunk):
-+ '''Adds a translateable chunk. It will be unescaped before being added.'''
-+ # We don't want empty messages since they are redundant and the TC
-+ # doesn't allow them.
-+ if chunk == '':
-+ return
-+
-+ unescaped_text = self.UnEscape(chunk)
-+ if self.single_message_:
-+ self.single_message_.AppendText(unescaped_text)
-+ else:
-+ self.skeleton_.append(self.uberclique.MakeClique(
-+ tclib.Message(text=unescaped_text)))
-+ self.translatable_chunk_ = True
-+
-+ def SubstituteMessages(self, substituter):
-+ '''Applies substitutions to all messages in the tree.
-+
-+ Goes through the skeleton and finds all MessageCliques.
-+
-+ Args:
-+ substituter: a grit.util.Substituter object.
-+ '''
-+ if self.single_message_:
-+ self.single_message_ = substituter.SubstituteMessage(self.single_message_)
-+ new_skel = []
-+ for chunk in self.skeleton_:
-+ if isinstance(chunk, clique.MessageClique):
-+ old_message = chunk.GetMessage()
-+ new_message = substituter.SubstituteMessage(old_message)
-+ if new_message is not old_message:
-+ new_skel.append(self.uberclique.MakeClique(new_message))
-+ continue
-+ new_skel.append(chunk)
-+ self.skeleton_ = new_skel
-diff --git a/tools/grit/grit/gather/tr_html.py b/tools/grit/grit/gather/tr_html.py
-new file mode 100644
-index 0000000000..60a9bfaf4e
---- /dev/null
-+++ b/tools/grit/grit/gather/tr_html.py
-@@ -0,0 +1,743 @@
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''A gatherer for the TotalRecall brand of HTML templates with replaceable
-+portions. We wanted to reuse extern.tclib.api.handlers.html.TCHTMLParser
-+but this proved impossible due to the fact that the TotalRecall HTML templates
-+are in general quite far from parseable HTML and the TCHTMLParser derives
-+
-+from HTMLParser.HTMLParser which requires relatively well-formed HTML. Some
-+examples of "HTML" from the TotalRecall HTML templates that wouldn't be
-+parseable include things like:
-+
-+ <a [PARAMS]>blabla</a> (not parseable because attributes are invalid)
-+
-+ <table><tr><td>[LOTSOFSTUFF]</tr></table> (not parseable because closing
-+ </td> is in the HTML [LOTSOFSTUFF]
-+ is replaced by)
-+
-+The other problem with using general parsers (such as TCHTMLParser) is that
-+we want to make sure we output the TotalRecall template with as little changes
-+as possible in terms of whitespace characters, layout etc. With any parser
-+that generates a parse tree, and generates output by dumping the parse tree,
-+we would always have little inconsistencies which could cause bugs (the
-+TotalRecall template stuff is quite brittle and can break if e.g. a tab
-+character is replaced with spaces).
-+
-+The solution, which may be applicable to some other HTML-like template
-+languages floating around Google, is to create a parser with a simple state
-+machine that keeps track of what kind of tag it's inside, and whether it's in
-+a translateable section or not. Translateable sections are:
-+
-+a) text (including [BINGO] replaceables) inside of tags that
-+ can contain translateable text (which is all tags except
-+ for a few)
-+
-+b) text inside of an 'alt' attribute in an <image> element, or
-+ the 'value' attribute of a <submit>, <button> or <text>
-+ element.
-+
-+The parser does not build up a parse tree but rather a "skeleton" which
-+is a list of nontranslateable strings intermingled with grit.clique.MessageClique
-+objects. This simplifies the parser considerably compared to a regular HTML
-+parser. To output a translated document, each item in the skeleton is
-+printed out, with the relevant Translation from each MessageCliques being used
-+for the requested language.
-+
-+This implementation borrows some code, constants and ideas from
-+extern.tclib.api.handlers.html.TCHTMLParser.
-+'''
-+
-+from __future__ import print_function
-+
-+import re
-+
-+import six
-+
-+from grit import clique
-+from grit import exception
-+from grit import lazy_re
-+from grit import util
-+from grit import tclib
-+
-+from grit.gather import interface
-+
-+
-+# HTML tags which break (separate) chunks.
-+_BLOCK_TAGS = ['script', 'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'br',
-+ 'body', 'style', 'head', 'title', 'table', 'tr', 'td', 'th',
-+ 'ul', 'ol', 'dl', 'nl', 'li', 'div', 'object', 'center',
-+ 'html', 'link', 'form', 'select', 'textarea',
-+ 'button', 'option', 'map', 'area', 'blockquote', 'pre',
-+ 'meta', 'xmp', 'noscript', 'label', 'tbody', 'thead',
-+ 'script', 'style', 'pre', 'iframe', 'img', 'input', 'nowrap',
-+ 'fieldset', 'legend']
-+
-+# HTML tags which may appear within a chunk.
-+_INLINE_TAGS = ['b', 'i', 'u', 'tt', 'code', 'font', 'a', 'span', 'small',
-+ 'key', 'nobr', 'url', 'em', 's', 'sup', 'strike',
-+ 'strong']
-+
-+# HTML tags within which linebreaks are significant.
-+_PREFORMATTED_TAGS = ['textarea', 'xmp', 'pre']
-+
-+# An array mapping some of the inline HTML tags to more meaningful
-+# names for those tags. This will be used when generating placeholders
-+# representing these tags.
-+_HTML_PLACEHOLDER_NAMES = { 'a' : 'link', 'br' : 'break', 'b' : 'bold',
-+ 'i' : 'italic', 'li' : 'item', 'ol' : 'ordered_list', 'p' : 'paragraph',
-+ 'ul' : 'unordered_list', 'img' : 'image', 'em' : 'emphasis' }
-+
-+# We append each of these characters in sequence to distinguish between
-+# different placeholders with basically the same name (e.g. BOLD1, BOLD2).
-+# Keep in mind that a placeholder name must not be a substring of any other
-+# placeholder name in the same message, so we can't simply count (BOLD_1
-+# would be a substring of BOLD_10).
-+_SUFFIXES = '123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
-+
-+# Matches whitespace in an HTML document. Also matches HTML comments, which are
-+# treated as whitespace.
-+_WHITESPACE = lazy_re.compile(r'(\s|&nbsp;|\\n|\\r|<!--\s*desc\s*=.*?-->)+',
-+ re.DOTALL)
-+
-+# Matches whitespace sequences which can be folded into a single whitespace
-+# character. This matches single characters so that non-spaces are replaced
-+# with spaces.
-+_FOLD_WHITESPACE = lazy_re.compile(r'\s+')
-+
-+# Finds a non-whitespace character
-+_NON_WHITESPACE = lazy_re.compile(r'\S')
-+
-+# Matches two or more &nbsp; in a row (a single &nbsp is not changed into
-+# placeholders because different languages require different numbers of spaces
-+# and placeholders must match exactly; more than one is probably a "special"
-+# whitespace sequence and should be turned into a placeholder).
-+_NBSP = lazy_re.compile(r'&nbsp;(&nbsp;)+')
-+
-+# Matches nontranslateable chunks of the document
-+_NONTRANSLATEABLES = lazy_re.compile(r'''
-+ <\s*script.+?<\s*/\s*script\s*>
-+ |
-+ <\s*style.+?<\s*/\s*style\s*>
-+ |
-+ <!--.+?-->
-+ |
-+ <\?IMPORT\s.+?> # import tag
-+ |
-+ <\s*[a-zA-Z_]+:.+?> # custom tag (open)
-+ |
-+ <\s*/\s*[a-zA-Z_]+:.+?> # custom tag (close)
-+ |
-+ <!\s*[A-Z]+\s*([^>]+|"[^"]+"|'[^']+')*?>
-+ ''', re.MULTILINE | re.DOTALL | re.VERBOSE | re.IGNORECASE)
-+
-+# Matches a tag and its attributes
-+_ELEMENT = lazy_re.compile(r'''
-+ # Optional closing /, element name
-+ <\s*(?P<closing>/)?\s*(?P<element>[a-zA-Z0-9]+)\s*
-+ # Attributes and/or replaceables inside the tag, if any
-+ (?P<atts>(
-+ \s*([a-zA-Z_][-:.a-zA-Z_0-9]*) # Attribute name
-+ (\s*=\s*(\'[^\']*\'|"[^"]*"|[-a-zA-Z0-9./,:;+*%?!&$\(\)_#=~\'"@]*))?
-+ |
-+ \s*\[(\$?\~)?([A-Z0-9-_]+?)(\~\$?)?\]
-+ )*)
-+ \s*(?P<empty>/)?\s*> # Optional empty-tag closing /, and tag close
-+ ''',
-+ re.MULTILINE | re.DOTALL | re.VERBOSE)
-+
-+# Matches elements that may have translateable attributes. The value of these
-+# special attributes is given by group 'value1' or 'value2'. Note that this
-+# regexp demands that the attribute value be quoted; this is necessary because
-+# the non-tree-building nature of the parser means we don't know when we're
-+# writing out attributes, so we wouldn't know to escape spaces.
-+_SPECIAL_ELEMENT = lazy_re.compile(r'''
-+ <\s*(
-+ input[^>]+?value\s*=\s*(\'(?P<value3>[^\']*)\'|"(?P<value4>[^"]*)")
-+ [^>]+type\s*=\s*"?'?(button|reset|text|submit)'?"?
-+ |
-+ (
-+ table[^>]+?title\s*=
-+ |
-+ img[^>]+?alt\s*=
-+ |
-+ input[^>]+?type\s*=\s*"?'?(button|reset|text|submit)'?"?[^>]+?value\s*=
-+ )
-+ \s*(\'(?P<value1>[^\']*)\'|"(?P<value2>[^"]*)")
-+ )[^>]*?>
-+ ''', re.MULTILINE | re.DOTALL | re.VERBOSE | re.IGNORECASE)
-+
-+# Matches stuff that is translateable if it occurs in the right context
-+# (between tags). This includes all characters and character entities.
-+# Note that this also matches &nbsp; which needs to be handled as whitespace
-+# before this regexp is applied.
-+_CHARACTERS = lazy_re.compile(r'''
-+ (
-+ \w
-+ |
-+ [\!\@\#\$\%\^\*\(\)\-\=\_\+\[\]\{\}\\\|\;\:\'\"\,\.\/\?\`\~]
-+ |
-+ &(\#[0-9]+|\#x[0-9a-fA-F]+|[A-Za-z0-9]+);
-+ )+
-+ ''', re.MULTILINE | re.DOTALL | re.VERBOSE)
-+
-+# Matches Total Recall's "replaceable" tags, which are just any text
-+# in capitals enclosed by delimiters like [] or [~~] or [$~~$] (e.g. [HELLO],
-+# [~HELLO~] and [$~HELLO~$]).
-+_REPLACEABLE = lazy_re.compile(r'\[(\$?\~)?(?P<name>[A-Z0-9-_]+?)(\~\$?)?\]',
-+ re.MULTILINE)
-+
-+
-+# Matches the silly [!]-prefixed "header" that is used in some TotalRecall
-+# templates.
-+_SILLY_HEADER = lazy_re.compile(r'\[!\]\ntitle\t(?P<title>[^\n]+?)\n.+?\n\n',
-+ re.MULTILINE | re.DOTALL)
-+
-+
-+# Matches a comment that provides a description for the message it occurs in.
-+_DESCRIPTION_COMMENT = lazy_re.compile(
-+ r'<!--\s*desc\s*=\s*(?P<description>.+?)\s*-->', re.DOTALL)
-+
-+# Matches a comment which is used to break apart multiple messages.
-+_MESSAGE_BREAK_COMMENT = lazy_re.compile(r'<!--\s*message-break\s*-->',
-+ re.DOTALL)
-+
-+# Matches a comment which is used to prevent block tags from splitting a message
-+_MESSAGE_NO_BREAK_COMMENT = re.compile(r'<!--\s*message-no-break\s*-->',
-+ re.DOTALL)
-+
-+
-+_DEBUG = 0
-+def _DebugPrint(text):
-+ if _DEBUG:
-+ print(text.encode('utf-8'))
-+
-+
-+class HtmlChunks(object):
-+ '''A parser that knows how to break an HTML-like document into a list of
-+ chunks, where each chunk is either translateable or non-translateable.
-+ The chunks are unmodified sections of the original document, so concatenating
-+ the text of all chunks would result in the original document.'''
-+
-+ def InTranslateable(self):
-+ return self.last_translateable != -1
-+
-+ def Rest(self):
-+ return self.text_[self.current:]
-+
-+ def StartTranslateable(self):
-+ assert not self.InTranslateable()
-+ if self.current != 0:
-+ # Append a nontranslateable chunk
-+ chunk_text = self.text_[self.chunk_start : self.last_nontranslateable + 1]
-+ # Needed in the case where document starts with a translateable.
-+ if len(chunk_text) > 0:
-+ self.AddChunk(False, chunk_text)
-+ self.chunk_start = self.last_nontranslateable + 1
-+ self.last_translateable = self.current
-+ self.last_nontranslateable = -1
-+
-+ def EndTranslateable(self):
-+ assert self.InTranslateable()
-+ # Append a translateable chunk
-+ self.AddChunk(True,
-+ self.text_[self.chunk_start : self.last_translateable + 1])
-+ self.chunk_start = self.last_translateable + 1
-+ self.last_translateable = -1
-+ self.last_nontranslateable = self.current
-+
-+ def AdvancePast(self, match):
-+ self.current += match.end()
-+
-+ def AddChunk(self, translateable, text):
-+ '''Adds a chunk to self, removing linebreaks and duplicate whitespace
-+ if appropriate.
-+ '''
-+ m = _DESCRIPTION_COMMENT.search(text)
-+ if m:
-+ self.last_description = m.group('description')
-+ # Remove the description from the output text
-+ text = _DESCRIPTION_COMMENT.sub('', text)
-+
-+ m = _MESSAGE_BREAK_COMMENT.search(text)
-+ if m:
-+ # Remove the coment from the output text. It should already effectively
-+ # break apart messages.
-+ text = _MESSAGE_BREAK_COMMENT.sub('', text)
-+
-+ if translateable and not self.last_element_ in _PREFORMATTED_TAGS:
-+ if self.fold_whitespace_:
-+ # Fold whitespace sequences if appropriate. This is optional because it
-+ # alters the output strings.
-+ text = _FOLD_WHITESPACE.sub(' ', text)
-+ else:
-+ text = text.replace('\n', ' ')
-+ text = text.replace('\r', ' ')
-+ # This whitespace folding doesn't work in all cases, thus the
-+ # fold_whitespace flag to support backwards compatibility.
-+ text = text.replace(' ', ' ')
-+ text = text.replace(' ', ' ')
-+
-+ if translateable:
-+ description = self.last_description
-+ self.last_description = ''
-+ else:
-+ description = ''
-+
-+ if text != '':
-+ self.chunks_.append((translateable, text, description))
-+
-+ def Parse(self, text, fold_whitespace):
-+ '''Parses self.text_ into an intermediate format stored in self.chunks_
-+ which is translateable and nontranslateable chunks. Also returns
-+ self.chunks_
-+
-+ Args:
-+ text: The HTML for parsing.
-+ fold_whitespace: Whether whitespace sequences should be folded into a
-+ single space.
-+
-+ Return:
-+ [chunk1, chunk2, chunk3, ...] (instances of class Chunk)
-+ '''
-+ #
-+ # Chunker state
-+ #
-+
-+ self.text_ = text
-+ self.fold_whitespace_ = fold_whitespace
-+
-+ # A list of tuples (is_translateable, text) which represents the document
-+ # after chunking.
-+ self.chunks_ = []
-+
-+ # Start index of the last chunk, whether translateable or not
-+ self.chunk_start = 0
-+
-+ # Index of the last for-sure translateable character if we are parsing
-+ # a translateable chunk, -1 to indicate we are not in a translateable chunk.
-+ # This is needed so that we don't include trailing whitespace in the
-+ # translateable chunk (whitespace is neutral).
-+ self.last_translateable = -1
-+
-+ # Index of the last for-sure nontranslateable character if we are parsing
-+ # a nontranslateable chunk, -1 if we are not in a nontranslateable chunk.
-+ # This is needed to make sure we can group e.g. "<b>Hello</b> there"
-+ # together instead of just "Hello</b> there" which would be much worse
-+ # for translation.
-+ self.last_nontranslateable = -1
-+
-+ # Index of the character we're currently looking at.
-+ self.current = 0
-+
-+ # The name of the last block element parsed.
-+ self.last_element_ = ''
-+
-+ # The last explicit description we found.
-+ self.last_description = ''
-+
-+ # Whether no-break was the last chunk seen
-+ self.last_nobreak = False
-+
-+ while self.current < len(self.text_):
-+ _DebugPrint('REST: %s' % self.text_[self.current:self.current+60])
-+
-+ m = _MESSAGE_NO_BREAK_COMMENT.match(self.Rest())
-+ if m:
-+ self.AdvancePast(m)
-+ self.last_nobreak = True
-+ continue
-+
-+ # Try to match whitespace
-+ m = _WHITESPACE.match(self.Rest())
-+ if m:
-+ # Whitespace is neutral, it just advances 'current' and does not switch
-+ # between translateable/nontranslateable. If we are in a
-+ # nontranslateable section that extends to the current point, we extend
-+ # it to include the whitespace. If we are in a translateable section,
-+ # we do not extend it until we find
-+ # more translateable parts, because we never want a translateable chunk
-+ # to end with whitespace.
-+ if (not self.InTranslateable() and
-+ self.last_nontranslateable == self.current - 1):
-+ self.last_nontranslateable = self.current + m.end() - 1
-+ self.AdvancePast(m)
-+ continue
-+
-+ # Then we try to match nontranslateables
-+ m = _NONTRANSLATEABLES.match(self.Rest())
-+ if m:
-+ if self.InTranslateable():
-+ self.EndTranslateable()
-+ self.last_nontranslateable = self.current + m.end() - 1
-+ self.AdvancePast(m)
-+ continue
-+
-+ # Now match all other HTML element tags (opening, closing, or empty, we
-+ # don't care).
-+ m = _ELEMENT.match(self.Rest())
-+ if m:
-+ element_name = m.group('element').lower()
-+ if element_name in _BLOCK_TAGS:
-+ self.last_element_ = element_name
-+ if self.InTranslateable():
-+ if self.last_nobreak:
-+ self.last_nobreak = False
-+ else:
-+ self.EndTranslateable()
-+
-+ # Check for "special" elements, i.e. ones that have a translateable
-+ # attribute, and handle them correctly. Note that all of the
-+ # "special" elements are block tags, so no need to check for this
-+ # if the tag is not a block tag.
-+ sm = _SPECIAL_ELEMENT.match(self.Rest())
-+ if sm:
-+ # Get the appropriate group name
-+ for group in sm.groupdict():
-+ if sm.groupdict()[group]:
-+ break
-+
-+ # First make a nontranslateable chunk up to and including the
-+ # quote before the translateable attribute value
-+ self.AddChunk(False, self.text_[
-+ self.chunk_start : self.current + sm.start(group)])
-+ # Then a translateable for the translateable bit
-+ self.AddChunk(True, self.Rest()[sm.start(group) : sm.end(group)])
-+ # Finally correct the data invariant for the parser
-+ self.chunk_start = self.current + sm.end(group)
-+
-+ self.last_nontranslateable = self.current + m.end() - 1
-+ elif self.InTranslateable():
-+ # We're in a translateable and the tag is an inline tag, so we
-+ # need to include it in the translateable.
-+ self.last_translateable = self.current + m.end() - 1
-+ self.AdvancePast(m)
-+ continue
-+
-+ # Anything else we find must be translateable, so we advance one character
-+ # at a time until one of the above matches.
-+ if not self.InTranslateable():
-+ self.StartTranslateable()
-+ else:
-+ self.last_translateable = self.current
-+ self.current += 1
-+
-+ # Close the final chunk
-+ if self.InTranslateable():
-+ self.AddChunk(True, self.text_[self.chunk_start : ])
-+ else:
-+ self.AddChunk(False, self.text_[self.chunk_start : ])
-+
-+ return self.chunks_
-+
-+
-+def HtmlToMessage(html, include_block_tags=False, description=''):
-+ '''Takes a bit of HTML, which must contain only "inline" HTML elements,
-+ and changes it into a tclib.Message. This involves escaping any entities and
-+ replacing any HTML code with placeholders.
-+
-+ If include_block_tags is true, no error will be given if block tags (e.g.
-+ <p> or <br>) are included in the HTML.
-+
-+ Args:
-+ html: 'Hello <b>[USERNAME]</b>, how&nbsp;<i>are</i> you?'
-+ include_block_tags: False
-+
-+ Return:
-+ tclib.Message('Hello START_BOLD1USERNAMEEND_BOLD, '
-+ 'howNBSPSTART_ITALICareEND_ITALIC you?',
-+ [ Placeholder('START_BOLD', '<b>', ''),
-+ Placeholder('USERNAME', '[USERNAME]', ''),
-+ Placeholder('END_BOLD', '</b>', ''),
-+ Placeholder('START_ITALIC', '<i>', ''),
-+ Placeholder('END_ITALIC', '</i>', ''), ])
-+ '''
-+ # Approach is:
-+ # - first placeholderize, finding <elements>, [REPLACEABLES] and &nbsp;
-+ # - then escape all character entities in text in-between placeholders
-+
-+ parts = [] # List of strings (for text chunks) and tuples (ID, original)
-+ # for placeholders
-+
-+ count_names = {} # Map of base names to number of times used
-+ end_names = {} # Map of base names to stack of end tags (for correct nesting)
-+
-+ def MakeNameClosure(base, type = ''):
-+ '''Returns a closure that can be called once all names have been allocated
-+ to return the final name of the placeholder. This allows us to minimally
-+ number placeholders for non-overlap.
-+
-+ Also ensures that END_XXX_Y placeholders have the same Y as the
-+ corresponding BEGIN_XXX_Y placeholder when we have nested tags of the same
-+ type.
-+
-+ Args:
-+ base: 'phname'
-+ type: '' | 'begin' | 'end'
-+
-+ Return:
-+ Closure()
-+ '''
-+ name = base.upper()
-+ if type != '':
-+ name = ('%s_%s' % (type, base)).upper()
-+
-+ count_names.setdefault(name, 0)
-+ count_names[name] += 1
-+
-+ def MakeFinalName(name_ = name, index = count_names[name] - 1):
-+ if type.lower() == 'end' and end_names.get(base):
-+ return end_names[base].pop(-1) # For correct nesting
-+ if count_names[name_] != 1:
-+ name_ = '%s_%s' % (name_, _SUFFIXES[index])
-+ # We need to use a stack to ensure that the end-tag suffixes match
-+ # the begin-tag suffixes. Only needed when more than one tag of the
-+ # same type.
-+ if type == 'begin':
-+ end_name = ('END_%s_%s' % (base, _SUFFIXES[index])).upper()
-+ if base in end_names:
-+ end_names[base].append(end_name)
-+ else:
-+ end_names[base] = [end_name]
-+
-+ return name_
-+
-+ return MakeFinalName
-+
-+ current = 0
-+ last_nobreak = False
-+
-+ while current < len(html):
-+ m = _MESSAGE_NO_BREAK_COMMENT.match(html[current:])
-+ if m:
-+ last_nobreak = True
-+ current += m.end()
-+ continue
-+
-+ m = _NBSP.match(html[current:])
-+ if m:
-+ parts.append((MakeNameClosure('SPACE'), m.group()))
-+ current += m.end()
-+ continue
-+
-+ m = _REPLACEABLE.match(html[current:])
-+ if m:
-+ # Replaceables allow - but placeholders don't, so replace - with _
-+ ph_name = MakeNameClosure('X_%s_X' % m.group('name').replace('-', '_'))
-+ parts.append((ph_name, m.group()))
-+ current += m.end()
-+ continue
-+
-+ m = _SPECIAL_ELEMENT.match(html[current:])
-+ if m:
-+ if not include_block_tags:
-+ if last_nobreak:
-+ last_nobreak = False
-+ else:
-+ raise exception.BlockTagInTranslateableChunk(html)
-+ element_name = 'block' # for simplification
-+ # Get the appropriate group name
-+ for group in m.groupdict():
-+ if m.groupdict()[group]:
-+ break
-+ parts.append((MakeNameClosure(element_name, 'begin'),
-+ html[current : current + m.start(group)]))
-+ parts.append(m.group(group))
-+ parts.append((MakeNameClosure(element_name, 'end'),
-+ html[current + m.end(group) : current + m.end()]))
-+ current += m.end()
-+ continue
-+
-+ m = _ELEMENT.match(html[current:])
-+ if m:
-+ element_name = m.group('element').lower()
-+ if not include_block_tags and not element_name in _INLINE_TAGS:
-+ if last_nobreak:
-+ last_nobreak = False
-+ else:
-+ raise exception.BlockTagInTranslateableChunk(html[current:])
-+ if element_name in _HTML_PLACEHOLDER_NAMES: # use meaningful names
-+ element_name = _HTML_PLACEHOLDER_NAMES[element_name]
-+
-+ # Make a name for the placeholder
-+ type = ''
-+ if not m.group('empty'):
-+ if m.group('closing'):
-+ type = 'end'
-+ else:
-+ type = 'begin'
-+ parts.append((MakeNameClosure(element_name, type), m.group()))
-+ current += m.end()
-+ continue
-+
-+ if len(parts) and isinstance(parts[-1], six.string_types):
-+ parts[-1] += html[current]
-+ else:
-+ parts.append(html[current])
-+ current += 1
-+
-+ msg_text = ''
-+ placeholders = []
-+ for part in parts:
-+ if isinstance(part, tuple):
-+ final_name = part[0]()
-+ original = part[1]
-+ msg_text += final_name
-+ placeholders.append(tclib.Placeholder(final_name, original, '(HTML code)'))
-+ else:
-+ msg_text += part
-+
-+ msg = tclib.Message(text=msg_text, placeholders=placeholders,
-+ description=description)
-+ content = msg.GetContent()
-+ for ix in range(len(content)):
-+ if isinstance(content[ix], six.string_types):
-+ content[ix] = util.UnescapeHtml(content[ix], replace_nbsp=False)
-+
-+ return msg
-+
-+
-+class TrHtml(interface.GathererBase):
-+ '''Represents a document or message in the template format used by
-+ Total Recall for HTML documents.'''
-+
-+ def __init__(self, *args, **kwargs):
-+ super(TrHtml, self).__init__(*args, **kwargs)
-+ self.have_parsed_ = False
-+ self.skeleton_ = [] # list of strings and MessageClique objects
-+ self.fold_whitespace_ = False
-+
-+ def SetAttributes(self, attrs):
-+ '''Sets node attributes used by the gatherer.
-+
-+ This checks the fold_whitespace attribute.
-+
-+ Args:
-+ attrs: The mapping of node attributes.
-+ '''
-+ self.fold_whitespace_ = ('fold_whitespace' in attrs and
-+ attrs['fold_whitespace'] == 'true')
-+
-+ def GetText(self):
-+ '''Returns the original text of the HTML document'''
-+ return self.text_
-+
-+ def GetTextualIds(self):
-+ return [self.extkey]
-+
-+ def GetCliques(self):
-+ '''Returns the message cliques for each translateable message in the
-+ document.'''
-+ return [x for x in self.skeleton_ if isinstance(x, clique.MessageClique)]
-+
-+ def Translate(self, lang, pseudo_if_not_available=True,
-+ skeleton_gatherer=None, fallback_to_english=False):
-+ '''Returns this document with translateable messages filled with
-+ the translation for language 'lang'.
-+
-+ Args:
-+ lang: 'en'
-+ pseudo_if_not_available: True
-+
-+ Return:
-+ 'ID_THIS_SECTION TYPE\n...BEGIN\n "Translated message"\n......\nEND
-+
-+ Raises:
-+ grit.exception.NotReady() if used before Parse() has been successfully
-+ called.
-+ grit.exception.NoSuchTranslation() if 'pseudo_if_not_available' is false
-+ and there is no translation for the requested language.
-+ '''
-+ if len(self.skeleton_) == 0:
-+ raise exception.NotReady()
-+
-+ # TODO(joi) Implement support for skeleton gatherers here.
-+
-+ out = []
-+ for item in self.skeleton_:
-+ if isinstance(item, six.string_types):
-+ out.append(item)
-+ else:
-+ msg = item.MessageForLanguage(lang,
-+ pseudo_if_not_available,
-+ fallback_to_english)
-+ for content in msg.GetContent():
-+ if isinstance(content, tclib.Placeholder):
-+ out.append(content.GetOriginal())
-+ else:
-+ # We escape " characters to increase the chance that attributes
-+ # will be properly escaped.
-+ out.append(util.EscapeHtml(content, True))
-+
-+ return ''.join(out)
-+
-+ def Parse(self):
-+ if self.have_parsed_:
-+ return
-+ self.have_parsed_ = True
-+
-+ text = self._LoadInputFile()
-+
-+ # Ignore the BOM character if the document starts with one.
-+ if text.startswith(u'\ufeff'):
-+ text = text[1:]
-+
-+ self.text_ = text
-+
-+ # Parsing is done in two phases: First, we break the document into
-+ # translateable and nontranslateable chunks. Second, we run through each
-+ # translateable chunk and insert placeholders for any HTML elements,
-+ # unescape escaped characters, etc.
-+
-+ # First handle the silly little [!]-prefixed header because it's not
-+ # handled by our HTML parsers.
-+ m = _SILLY_HEADER.match(text)
-+ if m:
-+ self.skeleton_.append(text[:m.start('title')])
-+ self.skeleton_.append(self.uberclique.MakeClique(
-+ tclib.Message(text=text[m.start('title'):m.end('title')])))
-+ self.skeleton_.append(text[m.end('title') : m.end()])
-+ text = text[m.end():]
-+
-+ chunks = HtmlChunks().Parse(text, self.fold_whitespace_)
-+
-+ for chunk in chunks:
-+ if chunk[0]: # Chunk is translateable
-+ self.skeleton_.append(self.uberclique.MakeClique(
-+ HtmlToMessage(chunk[1], description=chunk[2])))
-+ else:
-+ self.skeleton_.append(chunk[1])
-+
-+ # Go through the skeleton and change any messages that consist solely of
-+ # placeholders and whitespace into nontranslateable strings.
-+ for ix in range(len(self.skeleton_)):
-+ got_text = False
-+ if isinstance(self.skeleton_[ix], clique.MessageClique):
-+ msg = self.skeleton_[ix].GetMessage()
-+ for item in msg.GetContent():
-+ if (isinstance(item, six.string_types)
-+ and _NON_WHITESPACE.search(item) and item != '&nbsp;'):
-+ got_text = True
-+ break
-+ if not got_text:
-+ self.skeleton_[ix] = msg.GetRealContent()
-+
-+ def SubstituteMessages(self, substituter):
-+ '''Applies substitutions to all messages in the tree.
-+
-+ Goes through the skeleton and finds all MessageCliques.
-+
-+ Args:
-+ substituter: a grit.util.Substituter object.
-+ '''
-+ new_skel = []
-+ for chunk in self.skeleton_:
-+ if isinstance(chunk, clique.MessageClique):
-+ old_message = chunk.GetMessage()
-+ new_message = substituter.SubstituteMessage(old_message)
-+ if new_message is not old_message:
-+ new_skel.append(self.uberclique.MakeClique(new_message))
-+ continue
-+ new_skel.append(chunk)
-+ self.skeleton_ = new_skel
-diff --git a/tools/grit/grit/gather/tr_html_unittest.py b/tools/grit/grit/gather/tr_html_unittest.py
-new file mode 100644
-index 0000000000..1194853d9a
---- /dev/null
-+++ b/tools/grit/grit/gather/tr_html_unittest.py
-@@ -0,0 +1,524 @@
-+#!/usr/bin/env python
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Unit tests for grit.gather.tr_html'''
-+
-+from __future__ import print_function
-+
-+import os
-+import sys
-+if __name__ == '__main__':
-+ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
-+
-+import unittest
-+
-+import six
-+from six import StringIO
-+
-+from grit.gather import tr_html
-+from grit import clique
-+from grit import util
-+
-+
-+class ParserUnittest(unittest.TestCase):
-+ def testChunkingWithoutFoldWhitespace(self):
-+ self.VerifyChunking(False)
-+
-+ def testChunkingWithFoldWhitespace(self):
-+ self.VerifyChunking(True)
-+
-+ def VerifyChunking(self, fold_whitespace):
-+ """Use a single function to run all chunking testing.
-+
-+ This makes it easier to run chunking with fold_whitespace both on and off,
-+ to make sure the outputs are the same.
-+
-+ Args:
-+ fold_whitespace: Whether whitespace sequences should be folded into a
-+ single space.
-+ """
-+ self.VerifyChunkingBasic(fold_whitespace)
-+ self.VerifyChunkingDescriptions(fold_whitespace)
-+ self.VerifyChunkingReplaceables(fold_whitespace)
-+ self.VerifyChunkingLineBreaks(fold_whitespace)
-+ self.VerifyChunkingMessageBreak(fold_whitespace)
-+ self.VerifyChunkingMessageNoBreak(fold_whitespace)
-+
-+ def VerifyChunkingBasic(self, fold_whitespace):
-+ p = tr_html.HtmlChunks()
-+ chunks = p.Parse('<p>Hello <b>dear</b> how <i>are</i>you?<p>Fine!',
-+ fold_whitespace)
-+ self.failUnlessEqual(chunks, [
-+ (False, '<p>', ''), (True, 'Hello <b>dear</b> how <i>are</i>you?', ''),
-+ (False, '<p>', ''), (True, 'Fine!', '')])
-+
-+ chunks = p.Parse('<p> Hello <b>dear</b> how <i>are</i>you? <p>Fine!',
-+ fold_whitespace)
-+ self.failUnlessEqual(chunks, [
-+ (False, '<p> ', ''), (True, 'Hello <b>dear</b> how <i>are</i>you?', ''),
-+ (False, ' <p>', ''), (True, 'Fine!', '')])
-+
-+ chunks = p.Parse('<p> Hello <b>dear how <i>are you? <p> Fine!',
-+ fold_whitespace)
-+ self.failUnlessEqual(chunks, [
-+ (False, '<p> ', ''), (True, 'Hello <b>dear how <i>are you?', ''),
-+ (False, ' <p> ', ''), (True, 'Fine!', '')])
-+
-+ # Ensure translateable sections that start with inline tags contain
-+ # the starting inline tag.
-+ chunks = p.Parse('<b>Hello!</b> how are you?<p><i>I am fine.</i>',
-+ fold_whitespace)
-+ self.failUnlessEqual(chunks, [
-+ (True, '<b>Hello!</b> how are you?', ''), (False, '<p>', ''),
-+ (True, '<i>I am fine.</i>', '')])
-+
-+ # Ensure translateable sections that end with inline tags contain
-+ # the ending inline tag.
-+ chunks = p.Parse("Hello! How are <b>you?</b><p><i>I'm fine!</i>",
-+ fold_whitespace)
-+ self.failUnlessEqual(chunks, [
-+ (True, 'Hello! How are <b>you?</b>', ''), (False, '<p>', ''),
-+ (True, "<i>I'm fine!</i>", '')])
-+
-+ def VerifyChunkingDescriptions(self, fold_whitespace):
-+ p = tr_html.HtmlChunks()
-+ # Check capitals and explicit descriptions
-+ chunks = p.Parse('<!-- desc=bingo! --><B>Hello!</B> how are you?<P>'
-+ '<I>I am fine.</I>', fold_whitespace)
-+ self.failUnlessEqual(chunks, [
-+ (True, '<B>Hello!</B> how are you?', 'bingo!'), (False, '<P>', ''),
-+ (True, '<I>I am fine.</I>', '')])
-+ chunks = p.Parse('<B><!-- desc=bingo! -->Hello!</B> how are you?<P>'
-+ '<I>I am fine.</I>', fold_whitespace)
-+ self.failUnlessEqual(chunks, [
-+ (True, '<B>Hello!</B> how are you?', 'bingo!'), (False, '<P>', ''),
-+ (True, '<I>I am fine.</I>', '')])
-+ # Linebreaks get handled by the tclib message.
-+ chunks = p.Parse('<B>Hello!</B> <!-- desc=bi\nngo\n! -->how are you?<P>'
-+ '<I>I am fine.</I>', fold_whitespace)
-+ self.failUnlessEqual(chunks, [
-+ (True, '<B>Hello!</B> how are you?', 'bi\nngo\n!'), (False, '<P>', ''),
-+ (True, '<I>I am fine.</I>', '')])
-+
-+ # In this case, because the explicit description appears after the first
-+ # translateable, it will actually apply to the second translateable.
-+ chunks = p.Parse('<B>Hello!</B> how are you?<!-- desc=bingo! --><P>'
-+ '<I>I am fine.</I>', fold_whitespace)
-+ self.failUnlessEqual(chunks, [
-+ (True, '<B>Hello!</B> how are you?', ''), (False, '<P>', ''),
-+ (True, '<I>I am fine.</I>', 'bingo!')])
-+
-+ def VerifyChunkingReplaceables(self, fold_whitespace):
-+ # Check that replaceables within block tags (where attributes would go) are
-+ # handled correctly.
-+ p = tr_html.HtmlChunks()
-+ chunks = p.Parse('<b>Hello!</b> how are you?<p [BINGO] [$~BONGO~$]>'
-+ '<i>I am fine.</i>', fold_whitespace)
-+ self.failUnlessEqual(chunks, [
-+ (True, '<b>Hello!</b> how are you?', ''),
-+ (False, '<p [BINGO] [$~BONGO~$]>', ''),
-+ (True, '<i>I am fine.</i>', '')])
-+
-+ def VerifyChunkingLineBreaks(self, fold_whitespace):
-+ # Check that the contents of preformatted tags preserve line breaks.
-+ p = tr_html.HtmlChunks()
-+ chunks = p.Parse('<textarea>Hello\nthere\nhow\nare\nyou?</textarea>',
-+ fold_whitespace)
-+ self.failUnlessEqual(chunks, [(False, '<textarea>', ''),
-+ (True, 'Hello\nthere\nhow\nare\nyou?', ''), (False, '</textarea>', '')])
-+
-+ # ...and that other tags' line breaks are converted to spaces
-+ chunks = p.Parse('<p>Hello\nthere\nhow\nare\nyou?</p>', fold_whitespace)
-+ self.failUnlessEqual(chunks, [(False, '<p>', ''),
-+ (True, 'Hello there how are you?', ''), (False, '</p>', '')])
-+
-+ def VerifyChunkingMessageBreak(self, fold_whitespace):
-+ p = tr_html.HtmlChunks()
-+ # Make sure that message-break comments work properly.
-+ chunks = p.Parse('Break<!-- message-break --> apart '
-+ '<!--message-break-->messages', fold_whitespace)
-+ self.failUnlessEqual(chunks, [(True, 'Break', ''),
-+ (False, ' ', ''),
-+ (True, 'apart', ''),
-+ (False, ' ', ''),
-+ (True, 'messages', '')])
-+
-+ # Make sure message-break comments work in an inline tag.
-+ chunks = p.Parse('<a href=\'google.com\'><!-- message-break -->Google'
-+ '<!--message-break--></a>', fold_whitespace)
-+ self.failUnlessEqual(chunks, [(False, '<a href=\'google.com\'>', ''),
-+ (True, 'Google', ''),
-+ (False, '</a>', '')])
-+
-+ def VerifyChunkingMessageNoBreak(self, fold_whitespace):
-+ p = tr_html.HtmlChunks()
-+ # Make sure that message-no-break comments work properly.
-+ chunks = p.Parse('Please <!-- message-no-break --> <br />don\'t break',
-+ fold_whitespace)
-+ self.failUnlessEqual(chunks, [(True, 'Please <!-- message-no-break --> '
-+ '<br />don\'t break', '')])
-+
-+ chunks = p.Parse('Please <br /> break. <!-- message-no-break --> <br /> '
-+ 'But not this time.', fold_whitespace)
-+ self.failUnlessEqual(chunks, [(True, 'Please', ''),
-+ (False, ' <br /> ', ''),
-+ (True, 'break. <!-- message-no-break --> '
-+ '<br /> But not this time.', '')])
-+
-+ def testTranslateableAttributes(self):
-+ p = tr_html.HtmlChunks()
-+
-+ # Check that the translateable attributes in <img>, <submit>, <button> and
-+ # <text> elements buttons are handled correctly.
-+ chunks = p.Parse('<img src=bingo.jpg alt="hello there">'
-+ '<input type=submit value="hello">'
-+ '<input type="button" value="hello">'
-+ '<input type=\'text\' value=\'Howdie\'>', False)
-+ self.failUnlessEqual(chunks, [
-+ (False, '<img src=bingo.jpg alt="', ''), (True, 'hello there', ''),
-+ (False, '"><input type=submit value="', ''), (True, 'hello', ''),
-+ (False, '"><input type="button" value="', ''), (True, 'hello', ''),
-+ (False, '"><input type=\'text\' value=\'', ''), (True, 'Howdie', ''),
-+ (False, '\'>', '')])
-+
-+
-+ def testTranslateableHtmlToMessage(self):
-+ msg = tr_html.HtmlToMessage(
-+ 'Hello <b>[USERNAME]</b>, &lt;how&gt;&nbsp;<i>are</i> you?')
-+ pres = msg.GetPresentableContent()
-+ self.failUnless(pres ==
-+ 'Hello BEGIN_BOLDX_USERNAME_XEND_BOLD, '
-+ '<how>&nbsp;BEGIN_ITALICareEND_ITALIC you?')
-+
-+ msg = tr_html.HtmlToMessage('<b>Hello</b><I>Hello</I><b>Hello</b>')
-+ pres = msg.GetPresentableContent()
-+ self.failUnless(pres ==
-+ 'BEGIN_BOLD_1HelloEND_BOLD_1BEGIN_ITALICHelloEND_ITALIC'
-+ 'BEGIN_BOLD_2HelloEND_BOLD_2')
-+
-+ # Check that nesting (of the <font> tags) is handled correctly - i.e. that
-+ # the closing placeholder numbers match the opening placeholders.
-+ msg = tr_html.HtmlToMessage(
-+ '''<font size=-1><font color=#FF0000>Update!</font> '''
-+ '''<a href='http://desktop.google.com/whatsnew.html?hl=[$~LANG~$]'>'''
-+ '''New Features</a>: Now search PDFs, MP3s, Firefox web history, and '''
-+ '''more</font>''')
-+ pres = msg.GetPresentableContent()
-+ self.failUnless(pres ==
-+ 'BEGIN_FONT_1BEGIN_FONT_2Update!END_FONT_2 BEGIN_LINK'
-+ 'New FeaturesEND_LINK: Now search PDFs, MP3s, Firefox '
-+ 'web history, and moreEND_FONT_1')
-+
-+ msg = tr_html.HtmlToMessage('''<a href='[$~URL~$]'><b>[NUM][CAT]</b></a>''')
-+ pres = msg.GetPresentableContent()
-+ self.failUnless(pres == 'BEGIN_LINKBEGIN_BOLDX_NUM_XX_CAT_XEND_BOLDEND_LINK')
-+
-+ msg = tr_html.HtmlToMessage(
-+ '''<font size=-1><a class=q onClick='return window.qs?qs(this):1' '''
-+ '''href='http://[WEBSERVER][SEARCH_URI]'>Desktop</a></font>&nbsp;&nbsp;'''
-+ '''&nbsp;&nbsp;''')
-+ pres = msg.GetPresentableContent()
-+ self.failUnless(pres ==
-+ '''BEGIN_FONTBEGIN_LINKDesktopEND_LINKEND_FONTSPACE''')
-+
-+ msg = tr_html.HtmlToMessage(
-+ '''<br><br><center><font size=-2>&copy;2005 Google </font></center>''', 1)
-+ pres = msg.GetPresentableContent()
-+ self.failUnless(pres ==
-+ u'BEGIN_BREAK_1BEGIN_BREAK_2BEGIN_CENTERBEGIN_FONT\xa92005'
-+ u' Google END_FONTEND_CENTER')
-+
-+ msg = tr_html.HtmlToMessage(
-+ '''&nbsp;-&nbsp;<a class=c href=[$~CACHE~$]>Cached</a>''')
-+ pres = msg.GetPresentableContent()
-+ self.failUnless(pres ==
-+ '&nbsp;-&nbsp;BEGIN_LINKCachedEND_LINK')
-+
-+ # Check that upper-case tags are handled correctly.
-+ msg = tr_html.HtmlToMessage(
-+ '''You can read the <A HREF='http://desktop.google.com/privacypolicy.'''
-+ '''html?hl=[LANG_CODE]'>Privacy Policy</A> and <A HREF='http://desktop'''
-+ '''.google.com/privacyfaq.html?hl=[LANG_CODE]'>Privacy FAQ</A> online.''')
-+ pres = msg.GetPresentableContent()
-+ self.failUnless(pres ==
-+ 'You can read the BEGIN_LINK_1Privacy PolicyEND_LINK_1 and '
-+ 'BEGIN_LINK_2Privacy FAQEND_LINK_2 online.')
-+
-+ # Check that tags with linebreaks immediately preceding them are handled
-+ # correctly.
-+ msg = tr_html.HtmlToMessage(
-+ '''You can read the
-+<A HREF='http://desktop.google.com/privacypolicy.html?hl=[LANG_CODE]'>Privacy Policy</A>
-+and <A HREF='http://desktop.google.com/privacyfaq.html?hl=[LANG_CODE]'>Privacy FAQ</A> online.''')
-+ pres = msg.GetPresentableContent()
-+ self.failUnless(pres == '''You can read the
-+BEGIN_LINK_1Privacy PolicyEND_LINK_1
-+and BEGIN_LINK_2Privacy FAQEND_LINK_2 online.''')
-+
-+ # Check that message-no-break comments are handled correctly.
-+ msg = tr_html.HtmlToMessage('''Please <!-- message-no-break --><br /> don't break''')
-+ pres = msg.GetPresentableContent()
-+ self.failUnlessEqual(pres, '''Please BREAK don't break''')
-+
-+class TrHtmlUnittest(unittest.TestCase):
-+ def testSetAttributes(self):
-+ html = tr_html.TrHtml(StringIO(''))
-+ self.failUnlessEqual(html.fold_whitespace_, False)
-+ html.SetAttributes({})
-+ self.failUnlessEqual(html.fold_whitespace_, False)
-+ html.SetAttributes({'fold_whitespace': 'false'})
-+ self.failUnlessEqual(html.fold_whitespace_, False)
-+ html.SetAttributes({'fold_whitespace': 'true'})
-+ self.failUnlessEqual(html.fold_whitespace_, True)
-+
-+ def testFoldWhitespace(self):
-+ text = '<td> Test Message </td>'
-+
-+ html = tr_html.TrHtml(StringIO(text))
-+ html.Parse()
-+ self.failUnlessEqual(html.skeleton_[1].GetMessage().GetPresentableContent(),
-+ 'Test Message')
-+
-+ html = tr_html.TrHtml(StringIO(text))
-+ html.fold_whitespace_ = True
-+ html.Parse()
-+ self.failUnlessEqual(html.skeleton_[1].GetMessage().GetPresentableContent(),
-+ 'Test Message')
-+
-+ def testTable(self):
-+ html = tr_html.TrHtml(StringIO('''<table class="shaded-header"><tr>
-+<td class="header-element b expand">Preferences</td>
-+<td class="header-element s">
-+<a href="http://desktop.google.com/preferences.html">Preferences&nbsp;Help</a>
-+</td>
-+</tr></table>'''))
-+ html.Parse()
-+ self.failUnless(html.skeleton_[3].GetMessage().GetPresentableContent() ==
-+ 'BEGIN_LINKPreferences&nbsp;HelpEND_LINK')
-+
-+ def testSubmitAttribute(self):
-+ html = tr_html.TrHtml(StringIO('''</td>
-+<td class="header-element"><input type=submit value="Save Preferences"
-+name=submit2></td>
-+</tr></table>'''))
-+ html.Parse()
-+ self.failUnless(html.skeleton_[1].GetMessage().GetPresentableContent() ==
-+ 'Save Preferences')
-+
-+ def testWhitespaceAfterInlineTag(self):
-+ '''Test that even if there is whitespace after an inline tag at the start
-+ of a translateable section the inline tag will be included.
-+ '''
-+ html = tr_html.TrHtml(
-+ StringIO('''<label for=DISPLAYNONE><font size=-1> Hello</font>'''))
-+ html.Parse()
-+ self.failUnless(html.skeleton_[1].GetMessage().GetRealContent() ==
-+ '<font size=-1> Hello</font>')
-+
-+ def testSillyHeader(self):
-+ html = tr_html.TrHtml(StringIO('''[!]
-+title\tHello
-+bingo
-+bongo
-+bla
-+
-+<p>Other stuff</p>'''))
-+ html.Parse()
-+ content = html.skeleton_[1].GetMessage().GetRealContent()
-+ self.failUnless(content == 'Hello')
-+ self.failUnless(html.skeleton_[-1] == '</p>')
-+ # Right after the translateable the nontranslateable should start with
-+ # a linebreak (this catches a bug we had).
-+ self.failUnless(html.skeleton_[2][0] == '\n')
-+
-+
-+ def testExplicitDescriptions(self):
-+ html = tr_html.TrHtml(
-+ StringIO('Hello [USER]<br/><!-- desc=explicit -->'
-+ '<input type="button">Go!</input>'))
-+ html.Parse()
-+ msg = html.GetCliques()[1].GetMessage()
-+ self.failUnlessEqual(msg.GetDescription(), 'explicit')
-+ self.failUnlessEqual(msg.GetRealContent(), 'Go!')
-+
-+ html = tr_html.TrHtml(
-+ StringIO('Hello [USER]<br/><!-- desc=explicit\nmultiline -->'
-+ '<input type="button">Go!</input>'))
-+ html.Parse()
-+ msg = html.GetCliques()[1].GetMessage()
-+ self.failUnlessEqual(msg.GetDescription(), 'explicit multiline')
-+ self.failUnlessEqual(msg.GetRealContent(), 'Go!')
-+
-+
-+ def testRegressionInToolbarAbout(self):
-+ html = tr_html.TrHtml(util.PathFromRoot(r'grit/testdata/toolbar_about.html'))
-+ html.Parse()
-+ cliques = html.GetCliques()
-+ for cl in cliques:
-+ content = cl.GetMessage().GetRealContent()
-+ if content.count('De parvis grandis acervus erit'):
-+ self.failIf(content.count('$/translate'))
-+
-+
-+ def HtmlFromFileWithManualCheck(self, f):
-+ html = tr_html.TrHtml(f)
-+ html.Parse()
-+
-+ # For manual results inspection only...
-+ list = []
-+ for item in html.skeleton_:
-+ if isinstance(item, six.string_types):
-+ list.append(item)
-+ else:
-+ list.append(item.GetMessage().GetPresentableContent())
-+
-+ return html
-+
-+
-+ def testPrivacyHtml(self):
-+ html = self.HtmlFromFileWithManualCheck(
-+ util.PathFromRoot(r'grit/testdata/privacy.html'))
-+
-+ self.failUnless(html.skeleton_[1].GetMessage().GetRealContent() ==
-+ 'Privacy and Google Desktop Search')
-+ self.failUnless(html.skeleton_[3].startswith('<'))
-+ self.failUnless(len(html.skeleton_) > 10)
-+
-+
-+ def testPreferencesHtml(self):
-+ html = self.HtmlFromFileWithManualCheck(
-+ util.PathFromRoot(r'grit/testdata/preferences.html'))
-+
-+ # Verify that we don't get '[STATUS-MESSAGE]' as the original content of
-+ # one of the MessageClique objects (it would be a placeholder-only message
-+ # and we're supposed to have stripped those).
-+
-+ for item in [x for x in html.skeleton_
-+ if isinstance(x, clique.MessageClique)]:
-+ if (item.GetMessage().GetRealContent() == '[STATUS-MESSAGE]' or
-+ item.GetMessage().GetRealContent() == '[ADDIN-DO] [ADDIN-OPTIONS]'):
-+ self.fail()
-+
-+ self.failUnless(len(html.skeleton_) > 100)
-+
-+ def AssertNumberOfTranslateables(self, files, num):
-+ '''Fails if any of the files in files don't have exactly
-+ num translateable sections.
-+
-+ Args:
-+ files: ['file1', 'file2']
-+ num: 3
-+ '''
-+ for f in files:
-+ f = util.PathFromRoot(r'grit/testdata/%s' % f)
-+ html = self.HtmlFromFileWithManualCheck(f)
-+ self.failUnless(len(html.GetCliques()) == num)
-+
-+ def testFewTranslateables(self):
-+ self.AssertNumberOfTranslateables(['browser.html', 'email_thread.html',
-+ 'header.html', 'mini.html',
-+ 'oneclick.html', 'script.html',
-+ 'time_related.html', 'versions.html'], 0)
-+ self.AssertNumberOfTranslateables(['footer.html', 'hover.html'], 1)
-+
-+ def testOtherHtmlFilesForManualInspection(self):
-+ files = [
-+ 'about.html', 'bad_browser.html', 'cache_prefix.html',
-+ 'cache_prefix_file.html', 'chat_result.html', 'del_footer.html',
-+ 'del_header.html', 'deleted.html', 'details.html', 'email_result.html',
-+ 'error.html', 'explicit_web.html', 'footer.html',
-+ 'homepage.html', 'indexing_speed.html',
-+ 'install_prefs.html', 'install_prefs2.html',
-+ 'oem_enable.html', 'oem_non_admin.html', 'onebox.html',
-+ 'password.html', 'quit_apps.html', 'recrawl.html',
-+ 'searchbox.html', 'sidebar_h.html', 'sidebar_v.html', 'status.html',
-+ ]
-+ for f in files:
-+ self.HtmlFromFileWithManualCheck(
-+ util.PathFromRoot(r'grit/testdata/%s' % f))
-+
-+ def testTranslate(self):
-+ # Note that the English translation of documents that use character
-+ # literals (e.g. &copy;) will not be the same as the original document
-+ # because the character literal will be transformed into the Unicode
-+ # character itself. So for this test we choose some relatively complex
-+ # HTML without character entities (but with &nbsp; because that's handled
-+ # specially).
-+ html = tr_html.TrHtml(StringIO(''' <script>
-+ <!--
-+ function checkOffice() { var w = document.getElementById("h7");
-+ var e = document.getElementById("h8"); var o = document.getElementById("h10");
-+ if (!(w.checked || e.checked)) { o.checked=0;o.disabled=1;} else {o.disabled=0;} }
-+ // -->
-+ </script>
-+ <input type=checkbox [CHECK-DOC] name=DOC id=h7 onclick='checkOffice()'>
-+ <label for=h7> Word</label><br>
-+ <input type=checkbox [CHECK-XLS] name=XLS id=h8 onclick='checkOffice()'>
-+ <label for=h8> Excel</label><br>
-+ <input type=checkbox [CHECK-PPT] name=PPT id=h9>
-+ <label for=h9> PowerPoint</label><br>
-+ </span></td><td nowrap valign=top><span class="s">
-+ <input type=checkbox [CHECK-PDF] name=PDF id=hpdf>
-+ <label for=hpdf> PDF</label><br>
-+ <input type=checkbox [CHECK-TXT] name=TXT id=h6>
-+ <label for=h6> Text, media, and other files</label><br>
-+ </tr>&nbsp;&nbsp;
-+ <tr><td nowrap valign=top colspan=3><span class="s"><br />
-+ <input type=checkbox [CHECK-SECUREOFFICE] name=SECUREOFFICE id=h10>
-+ <label for=h10> Password-protected Office documents (Word, Excel)</label><br />
-+ <input type=checkbox [DISABLED-HTTPS] [CHECK-HTTPS] name=HTTPS id=h12><label
-+ for=h12> Secure pages (HTTPS) in web history</label></span></td></tr>
-+ </table>'''))
-+ html.Parse()
-+ trans = html.Translate('en')
-+ if (html.GetText() != trans):
-+ self.fail()
-+
-+
-+ def testHtmlToMessageWithBlockTags(self):
-+ msg = tr_html.HtmlToMessage(
-+ 'Hello<p>Howdie<img alt="bingo" src="image.gif">', True)
-+ result = msg.GetPresentableContent()
-+ self.failUnless(
-+ result == 'HelloBEGIN_PARAGRAPHHowdieBEGIN_BLOCKbingoEND_BLOCK')
-+
-+ msg = tr_html.HtmlToMessage(
-+ 'Hello<p>Howdie<input type="button" value="bingo">', True)
-+ result = msg.GetPresentableContent()
-+ self.failUnless(
-+ result == 'HelloBEGIN_PARAGRAPHHowdieBEGIN_BLOCKbingoEND_BLOCK')
-+
-+
-+ def testHtmlToMessageRegressions(self):
-+ msg = tr_html.HtmlToMessage(' - ', True)
-+ result = msg.GetPresentableContent()
-+ self.failUnless(result == ' - ')
-+
-+
-+ def testEscapeUnescaped(self):
-+ text = '&copy;&nbsp; & &quot;&lt;hello&gt;&quot;'
-+ unescaped = util.UnescapeHtml(text)
-+ self.failUnless(unescaped == u'\u00a9\u00a0 & "<hello>"')
-+ escaped_unescaped = util.EscapeHtml(unescaped, True)
-+ self.failUnless(escaped_unescaped ==
-+ u'\u00a9\u00a0 &amp; &quot;&lt;hello&gt;&quot;')
-+
-+ def testRegressionCjkHtmlFile(self):
-+ # TODO(joi) Fix this problem where unquoted attributes that
-+ # have a value that is CJK characters causes the regular expression
-+ # match never to return. (culprit is the _ELEMENT regexp(
-+ if False:
-+ html = self.HtmlFromFileWithManualCheck(util.PathFromRoot(
-+ r'grit/testdata/ko_oem_enable_bug.html'))
-+ self.failUnless(True)
-+
-+ def testRegressionCpuHang(self):
-+ # If this regression occurs, the unit test will never return
-+ html = tr_html.TrHtml(StringIO(
-+ '''<input type=text size=12 id=advFileTypeEntry [~SHOW-FILETYPE-BOX~] value="[EXT]" name=ext>'''))
-+ html.Parse()
-+
-+if __name__ == '__main__':
-+ unittest.main()
-diff --git a/tools/grit/grit/gather/txt.py b/tools/grit/grit/gather/txt.py
-new file mode 100644
-index 0000000000..e5c10abc28
---- /dev/null
-+++ b/tools/grit/grit/gather/txt.py
-@@ -0,0 +1,38 @@
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Supports making amessage from a text file.
-+'''
-+
-+from __future__ import print_function
-+
-+from grit.gather import interface
-+from grit import tclib
-+
-+
-+class TxtFile(interface.GathererBase):
-+ '''A text file gatherer. Very simple, all text from the file becomes a
-+ single clique.
-+ '''
-+
-+ def Parse(self):
-+ self.text_ = self._LoadInputFile()
-+ self.clique_ = self.uberclique.MakeClique(tclib.Message(text=self.text_))
-+
-+ def GetText(self):
-+ '''Returns the text of what is being gathered.'''
-+ return self.text_
-+
-+ def GetTextualIds(self):
-+ return [self.extkey]
-+
-+ def GetCliques(self):
-+ '''Returns the MessageClique objects for all translateable portions.'''
-+ return [self.clique_]
-+
-+ def Translate(self, lang, pseudo_if_not_available=True,
-+ skeleton_gatherer=None, fallback_to_english=False):
-+ return self.clique_.MessageForLanguage(lang,
-+ pseudo_if_not_available,
-+ fallback_to_english).GetRealContent()
-diff --git a/tools/grit/grit/gather/txt_unittest.py b/tools/grit/grit/gather/txt_unittest.py
-new file mode 100644
-index 0000000000..abb9ed98d7
---- /dev/null
-+++ b/tools/grit/grit/gather/txt_unittest.py
-@@ -0,0 +1,35 @@
-+#!/usr/bin/env python
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Unit tests for TxtFile gatherer'''
-+
-+from __future__ import print_function
-+
-+import os
-+import sys
-+if __name__ == '__main__':
-+ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
-+
-+
-+import unittest
-+
-+from six import StringIO
-+
-+from grit.gather import txt
-+
-+
-+class TxtUnittest(unittest.TestCase):
-+ def testGather(self):
-+ input = StringIO('Hello there\nHow are you?')
-+ gatherer = txt.TxtFile(input)
-+ gatherer.Parse()
-+ self.failUnless(gatherer.GetText() == input.getvalue())
-+ self.failUnless(len(gatherer.GetCliques()) == 1)
-+ self.failUnless(gatherer.GetCliques()[0].GetMessage().GetRealContent() ==
-+ input.getvalue())
-+
-+
-+if __name__ == '__main__':
-+ unittest.main()
-diff --git a/tools/grit/grit/grd_reader.py b/tools/grit/grit/grd_reader.py
-new file mode 100644
-index 0000000000..b7bb782977
---- /dev/null
-+++ b/tools/grit/grit/grd_reader.py
-@@ -0,0 +1,238 @@
-+#!/usr/bin/env python
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Class for reading GRD files into memory, without processing them.
-+'''
-+
-+from __future__ import print_function
-+
-+import os.path
-+import sys
-+import xml.sax
-+import xml.sax.handler
-+
-+import six
-+
-+from grit import exception
-+from grit import util
-+from grit.node import mapping
-+from grit.node import misc
-+
-+
-+class StopParsingException(Exception):
-+ '''An exception used to stop parsing.'''
-+ pass
-+
-+
-+class GrdContentHandler(xml.sax.handler.ContentHandler):
-+ def __init__(self, stop_after, debug, dir, defines, tags_to_ignore,
-+ target_platform, source):
-+ # Invariant of data:
-+ # 'root' is the root of the parse tree being created, or None if we haven't
-+ # parsed out any elements.
-+ # 'stack' is the a stack of elements that we push new nodes onto and
-+ # pop from when they finish parsing, or [] if we are not currently parsing.
-+ # 'stack[-1]' is the top of the stack.
-+ self.root = None
-+ self.stack = []
-+ self.stop_after = stop_after
-+ self.debug = debug
-+ self.dir = dir
-+ self.defines = defines
-+ self.tags_to_ignore = tags_to_ignore or set()
-+ self.ignore_depth = 0
-+ self.target_platform = target_platform
-+ self.source = source
-+
-+ def startElement(self, name, attrs):
-+ if self.ignore_depth or name in self.tags_to_ignore:
-+ if self.debug and self.ignore_depth == 0:
-+ print("Ignoring element %s and its children" % name)
-+ self.ignore_depth += 1
-+ return
-+
-+ if self.debug:
-+ attr_list = ' '.join('%s="%s"' % kv for kv in attrs.items())
-+ print("Starting parsing of element %s with attributes %r" %
-+ (name, attr_list or '(none)'))
-+
-+ typeattr = attrs.get('type')
-+ node = mapping.ElementToClass(name, typeattr)()
-+ node.source = self.source
-+
-+ if self.stack:
-+ self.stack[-1].AddChild(node)
-+ node.StartParsing(name, self.stack[-1])
-+ else:
-+ assert self.root is None
-+ self.root = node
-+ if isinstance(self.root, misc.GritNode):
-+ if self.target_platform:
-+ self.root.SetTargetPlatform(self.target_platform)
-+ node.StartParsing(name, None)
-+ if self.defines:
-+ node.SetDefines(self.defines)
-+ self.stack.append(node)
-+
-+ for attr, attrval in attrs.items():
-+ node.HandleAttribute(attr, attrval)
-+
-+ def endElement(self, name):
-+ if self.ignore_depth:
-+ self.ignore_depth -= 1
-+ return
-+
-+ if name == 'part':
-+ partnode = self.stack[-1]
-+ partnode.started_inclusion = True
-+ # Add the contents of the sub-grd file as children of the <part> node.
-+ partname = os.path.join(self.dir, partnode.GetInputPath())
-+ # Check the GRDP file exists.
-+ if not os.path.exists(partname):
-+ raise exception.FileNotFound(partname)
-+ # Exceptions propagate to the handler in grd_reader.Parse().
-+ oldsource = self.source
-+ try:
-+ self.source = partname
-+ xml.sax.parse(partname, GrdPartContentHandler(self))
-+ finally:
-+ self.source = oldsource
-+
-+ if self.debug:
-+ print("End parsing of element %s" % name)
-+ self.stack.pop().EndParsing()
-+
-+ if name == self.stop_after:
-+ raise StopParsingException()
-+
-+ def characters(self, content):
-+ if self.ignore_depth == 0:
-+ if self.stack[-1]:
-+ self.stack[-1].AppendContent(content)
-+
-+ def ignorableWhitespace(self, whitespace):
-+ # TODO(joi): This is not supported by expat. Should use a different XML
-+ # parser?
-+ pass
-+
-+
-+class GrdPartContentHandler(xml.sax.handler.ContentHandler):
-+ def __init__(self, parent):
-+ self.parent = parent
-+ self.depth = 0
-+
-+ def startElement(self, name, attrs):
-+ if self.depth:
-+ self.parent.startElement(name, attrs)
-+ else:
-+ if name != 'grit-part':
-+ raise exception.MissingElement("root tag must be <grit-part>")
-+ if attrs:
-+ raise exception.UnexpectedAttribute(
-+ "<grit-part> tag must not have attributes")
-+ self.depth += 1
-+
-+ def endElement(self, name):
-+ self.depth -= 1
-+ if self.depth:
-+ self.parent.endElement(name)
-+
-+ def characters(self, content):
-+ self.parent.characters(content)
-+
-+ def ignorableWhitespace(self, whitespace):
-+ self.parent.ignorableWhitespace(whitespace)
-+
-+
-+def Parse(filename_or_stream, dir=None, stop_after=None, first_ids_file=None,
-+ debug=False, defines=None, tags_to_ignore=None, target_platform=None,
-+ predetermined_ids_file=None):
-+ '''Parses a GRD file into a tree of nodes (from grit.node).
-+
-+ If filename_or_stream is a stream, 'dir' should point to the directory
-+ notionally containing the stream (this feature is only used in unit tests).
-+
-+ If 'stop_after' is provided, the parsing will stop once the first node
-+ with this name has been fully parsed (including all its contents).
-+
-+ If 'debug' is true, lots of information about the parsing events will be
-+ printed out during parsing of the file.
-+
-+ If 'first_ids_file' is non-empty, it is used to override the setting for the
-+ first_ids_file attribute of the <grit> root node. Note that the first_ids_file
-+ parameter should be relative to the cwd, even though the first_ids_file
-+ attribute of the <grit> node is relative to the grd file.
-+
-+ If 'target_platform' is set, this is used to determine the target
-+ platform of builds, instead of using |sys.platform|.
-+
-+ Args:
-+ filename_or_stream: './bla.xml'
-+ dir: None (if filename_or_stream is a filename) or '.'
-+ stop_after: 'inputs'
-+ first_ids_file: 'GRIT_DIR/../gritsettings/resource_ids'
-+ debug: False
-+ defines: dictionary of defines, like {'chromeos': '1'}
-+ target_platform: None or the value that would be returned by sys.platform
-+ on your target platform.
-+ predetermined_ids_file: File path to a file containing a pre-determined
-+ mapping from resource names to resource ids which will be used to assign
-+ resource ids to those resources.
-+
-+ Return:
-+ Subclass of grit.node.base.Node
-+
-+ Throws:
-+ grit.exception.Parsing
-+ '''
-+
-+ if isinstance(filename_or_stream, six.string_types):
-+ source = filename_or_stream
-+ if dir is None:
-+ dir = util.dirname(filename_or_stream)
-+ else:
-+ source = None
-+
-+ handler = GrdContentHandler(stop_after=stop_after, debug=debug, dir=dir,
-+ defines=defines, tags_to_ignore=tags_to_ignore,
-+ target_platform=target_platform, source=source)
-+ try:
-+ xml.sax.parse(filename_or_stream, handler)
-+ except StopParsingException:
-+ assert stop_after
-+ pass
-+ except:
-+ if not debug:
-+ print("parse exception: run GRIT with the -x flag to debug .grd problems")
-+ raise
-+
-+ if handler.root.name != 'grit':
-+ raise exception.MissingElement("root tag must be <grit>")
-+
-+ if hasattr(handler.root, 'SetOwnDir'):
-+ # Fix up the base_dir so it is relative to the input file.
-+ assert dir is not None
-+ handler.root.SetOwnDir(dir)
-+
-+ if isinstance(handler.root, misc.GritNode):
-+ handler.root.SetPredeterminedIdsFile(predetermined_ids_file)
-+ if first_ids_file:
-+ # Make the path to the first_ids_file relative to the grd file,
-+ # unless it begins with GRIT_DIR.
-+ GRIT_DIR_PREFIX = 'GRIT_DIR'
-+ if not (first_ids_file.startswith(GRIT_DIR_PREFIX)
-+ and first_ids_file[len(GRIT_DIR_PREFIX)] in ['/', '\\']):
-+ rel_dir = os.path.relpath(os.getcwd(), dir)
-+ first_ids_file = util.normpath(os.path.join(rel_dir, first_ids_file))
-+ handler.root.attrs['first_ids_file'] = first_ids_file
-+ # Assign first ids to the nodes that don't have them.
-+ handler.root.AssignFirstIds(filename_or_stream, defines)
-+
-+ return handler.root
-+
-+
-+if __name__ == '__main__':
-+ util.ChangeStdoutEncoding()
-+ print(six.text_type(Parse(sys.argv[1])))
-diff --git a/tools/grit/grit/grd_reader_unittest.py b/tools/grit/grit/grd_reader_unittest.py
-new file mode 100644
-index 0000000000..920a92f9c0
---- /dev/null
-+++ b/tools/grit/grit/grd_reader_unittest.py
-@@ -0,0 +1,346 @@
-+#!/usr/bin/env python
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Unit tests for grd_reader package'''
-+
-+from __future__ import print_function
-+
-+import os
-+import sys
-+if __name__ == '__main__':
-+ sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
-+
-+import unittest
-+
-+import six
-+from six import StringIO
-+
-+from grit import exception
-+from grit import grd_reader
-+from grit import util
-+from grit.node import empty
-+from grit.node import message
-+
-+
-+class GrdReaderUnittest(unittest.TestCase):
-+ def testParsingAndXmlOutput(self):
-+ input = u'''<?xml version="1.0" encoding="UTF-8"?>
-+<grit base_dir="." current_release="3" latest_public_release="2" source_lang_id="en-US">
-+ <release seq="3">
-+ <includes>
-+ <include file="images/logo.gif" name="ID_LOGO" type="gif" />
-+ </includes>
-+ <messages>
-+ <if expr="True">
-+ <message desc="Printed to greet the currently logged in user" name="IDS_GREETING">
-+ Hello <ph name="USERNAME">%s<ex>Joi</ex></ph>, how are you doing today?
-+ </message>
-+ </if>
-+ </messages>
-+ <structures>
-+ <structure file="rc_files/dialogs.rc" name="IDD_NARROW_DIALOG" type="dialog">
-+ <skeleton expr="lang == 'fr-FR'" file="bla.rc" variant_of_revision="3" />
-+ </structure>
-+ <structure file="rc_files/version.rc" name="VS_VERSION_INFO" type="version" />
-+ </structures>
-+ </release>
-+ <translations>
-+ <file lang="nl" path="nl_translations.xtb" />
-+ </translations>
-+ <outputs>
-+ <output filename="resource.h" type="rc_header" />
-+ <output filename="resource.rc" lang="en-US" type="rc_all" />
-+ </outputs>
-+</grit>'''
-+ pseudo_file = StringIO(input)
-+ tree = grd_reader.Parse(pseudo_file, '.')
-+ output = six.text_type(tree)
-+ expected_output = input.replace(u' base_dir="."', u'')
-+ self.assertEqual(expected_output, output)
-+ self.failUnless(tree.GetNodeById('IDS_GREETING'))
-+
-+
-+ def testStopAfter(self):
-+ input = u'''<?xml version="1.0" encoding="UTF-8"?>
-+<grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
-+ <outputs>
-+ <output filename="resource.h" type="rc_header" />
-+ <output filename="resource.rc" lang="en-US" type="rc_all" />
-+ </outputs>
-+ <release seq="3">
-+ <includes>
-+ <include type="gif" name="ID_LOGO" file="images/logo.gif"/>
-+ </includes>
-+ </release>
-+</grit>'''
-+ pseudo_file = StringIO(input)
-+ tree = grd_reader.Parse(pseudo_file, '.', stop_after='outputs')
-+ # only an <outputs> child
-+ self.failUnless(len(tree.children) == 1)
-+ self.failUnless(tree.children[0].name == 'outputs')
-+
-+ def testLongLinesWithComments(self):
-+ input = u'''<?xml version="1.0" encoding="UTF-8"?>
-+<grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
-+ <release seq="3">
-+ <messages>
-+ <message name="IDS_GREETING" desc="Printed to greet the currently logged in user">
-+ This is a very long line with no linebreaks yes yes it stretches on <!--
-+ -->and on <!--
-+ -->and on!
-+ </message>
-+ </messages>
-+ </release>
-+</grit>'''
-+ pseudo_file = StringIO(input)
-+ tree = grd_reader.Parse(pseudo_file, '.')
-+
-+ greeting = tree.GetNodeById('IDS_GREETING')
-+ self.failUnless(greeting.GetCliques()[0].GetMessage().GetRealContent() ==
-+ 'This is a very long line with no linebreaks yes yes it '
-+ 'stretches on and on and on!')
-+
-+ def doTestAssignFirstIds(self, first_ids_path):
-+ input = u'''<?xml version="1.0" encoding="UTF-8"?>
-+<grit latest_public_release="2" source_lang_id="en-US" current_release="3"
-+ base_dir="." first_ids_file="%s">
-+ <release seq="3">
-+ <messages>
-+ <message name="IDS_TEST" desc="test">
-+ test
-+ </message>
-+ </messages>
-+ </release>
-+</grit>''' % first_ids_path
-+ pseudo_file = StringIO(input)
-+ grit_root_dir = os.path.join(os.path.abspath(os.path.dirname(__file__)),
-+ '..')
-+ fake_input_path = os.path.join(
-+ grit_root_dir, "grit/testdata/chrome/app/generated_resources.grd")
-+ root = grd_reader.Parse(pseudo_file, os.path.split(fake_input_path)[0])
-+ root.AssignFirstIds(fake_input_path, {})
-+ messages_node = root.children[0].children[0]
-+ self.failUnless(isinstance(messages_node, empty.MessagesNode))
-+ self.failUnless(messages_node.attrs["first_id"] !=
-+ empty.MessagesNode().DefaultAttributes()["first_id"])
-+
-+ def testAssignFirstIds(self):
-+ self.doTestAssignFirstIds("../../tools/grit/resource_ids")
-+
-+ def testAssignFirstIdsUseGritDir(self):
-+ self.doTestAssignFirstIds("GRIT_DIR/grit/testdata/tools/grit/resource_ids")
-+
-+ def testAssignFirstIdsMultipleMessages(self):
-+ """If there are multiple messages sections, the resource_ids file
-+ needs to list multiple first_id values."""
-+ input = u'''<?xml version="1.0" encoding="UTF-8"?>
-+<grit latest_public_release="2" source_lang_id="en-US" current_release="3"
-+ base_dir="." first_ids_file="resource_ids">
-+ <release seq="3">
-+ <messages>
-+ <message name="IDS_TEST" desc="test">
-+ test
-+ </message>
-+ </messages>
-+ <messages>
-+ <message name="IDS_TEST2" desc="test">
-+ test2
-+ </message>
-+ </messages>
-+ </release>
-+</grit>'''
-+ pseudo_file = StringIO(input)
-+ grit_root_dir = os.path.join(os.path.abspath(os.path.dirname(__file__)),
-+ '..')
-+ fake_input_path = os.path.join(grit_root_dir, "grit/testdata/test.grd")
-+
-+ root = grd_reader.Parse(pseudo_file, os.path.split(fake_input_path)[0])
-+ root.AssignFirstIds(fake_input_path, {})
-+ messages_node = root.children[0].children[0]
-+ self.assertTrue(isinstance(messages_node, empty.MessagesNode))
-+ self.assertEqual('100', messages_node.attrs["first_id"])
-+ messages_node = root.children[0].children[1]
-+ self.assertTrue(isinstance(messages_node, empty.MessagesNode))
-+ self.assertEqual('10000', messages_node.attrs["first_id"])
-+
-+ def testUseNameForIdAndPpIfdef(self):
-+ input = u'''<?xml version="1.0" encoding="UTF-8"?>
-+<grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
-+ <release seq="3">
-+ <messages>
-+ <if expr="pp_ifdef('hello')">
-+ <message name="IDS_HELLO" use_name_for_id="true">
-+ Hello!
-+ </message>
-+ </if>
-+ </messages>
-+ </release>
-+</grit>'''
-+ pseudo_file = StringIO(input)
-+ root = grd_reader.Parse(pseudo_file, '.', defines={'hello': '1'})
-+
-+ # Check if the ID is set to the name. In the past, there was a bug
-+ # that caused the ID to be a generated number.
-+ hello = root.GetNodeById('IDS_HELLO')
-+ self.failUnless(hello.GetCliques()[0].GetId() == 'IDS_HELLO')
-+
-+ def testUseNameForIdWithIfElse(self):
-+ input = u'''<?xml version="1.0" encoding="UTF-8"?>
-+<grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
-+ <release seq="3">
-+ <messages>
-+ <if expr="pp_ifdef('hello')">
-+ <then>
-+ <message name="IDS_HELLO" use_name_for_id="true">
-+ Hello!
-+ </message>
-+ </then>
-+ <else>
-+ <message name="IDS_HELLO" use_name_for_id="true">
-+ Yellow!
-+ </message>
-+ </else>
-+ </if>
-+ </messages>
-+ </release>
-+</grit>'''
-+ pseudo_file = StringIO(input)
-+ root = grd_reader.Parse(pseudo_file, '.', defines={'hello': '1'})
-+
-+ # Check if the ID is set to the name. In the past, there was a bug
-+ # that caused the ID to be a generated number.
-+ hello = root.GetNodeById('IDS_HELLO')
-+ self.failUnless(hello.GetCliques()[0].GetId() == 'IDS_HELLO')
-+
-+ def testPartInclusionAndCorrectSource(self):
-+ arbitrary_path_grd = u'''\
-+ <grit-part>
-+ <message name="IDS_TEST5" desc="test5">test5</message>
-+ </grit-part>'''
-+ tmp_dir = util.TempDir({'arbitrary_path.grp': arbitrary_path_grd})
-+ arbitrary_path_grd_file = tmp_dir.GetPath('arbitrary_path.grp')
-+ top_grd = u'''\
-+ <grit latest_public_release="2" current_release="3">
-+ <release seq="3">
-+ <messages>
-+ <message name="IDS_TEST" desc="test">
-+ test
-+ </message>
-+ <part file="sub.grp" />
-+ <part file="%s" />
-+ </messages>
-+ </release>
-+ </grit>''' % arbitrary_path_grd_file
-+ sub_grd = u'''\
-+ <grit-part>
-+ <message name="IDS_TEST2" desc="test2">test2</message>
-+ <part file="subsub.grp" />
-+ <message name="IDS_TEST3" desc="test3">test3</message>
-+ </grit-part>'''
-+ subsub_grd = u'''\
-+ <grit-part>
-+ <message name="IDS_TEST4" desc="test4">test4</message>
-+ </grit-part>'''
-+ expected_output = u'''\
-+ <grit current_release="3" latest_public_release="2">
-+ <release seq="3">
-+ <messages>
-+ <message desc="test" name="IDS_TEST">
-+ test
-+ </message>
-+ <part file="sub.grp">
-+ <message desc="test2" name="IDS_TEST2">
-+ test2
-+ </message>
-+ <part file="subsub.grp">
-+ <message desc="test4" name="IDS_TEST4">
-+ test4
-+ </message>
-+ </part>
-+ <message desc="test3" name="IDS_TEST3">
-+ test3
-+ </message>
-+ </part>
-+ <part file="%s">
-+ <message desc="test5" name="IDS_TEST5">
-+ test5
-+ </message>
-+ </part>
-+ </messages>
-+ </release>
-+ </grit>''' % arbitrary_path_grd_file
-+
-+ with util.TempDir({'sub.grp': sub_grd,
-+ 'subsub.grp': subsub_grd}) as tmp_sub_dir:
-+ output = grd_reader.Parse(StringIO(top_grd),
-+ tmp_sub_dir.GetPath())
-+ correct_sources = {
-+ 'IDS_TEST': None,
-+ 'IDS_TEST2': tmp_sub_dir.GetPath('sub.grp'),
-+ 'IDS_TEST3': tmp_sub_dir.GetPath('sub.grp'),
-+ 'IDS_TEST4': tmp_sub_dir.GetPath('subsub.grp'),
-+ 'IDS_TEST5': arbitrary_path_grd_file,
-+ }
-+
-+ for node in output.ActiveDescendants():
-+ with node:
-+ if isinstance(node, message.MessageNode):
-+ self.assertEqual(correct_sources[node.attrs.get('name')], node.source)
-+ self.assertEqual(expected_output.split(), output.FormatXml().split())
-+ tmp_dir.CleanUp()
-+
-+ def testPartInclusionFailure(self):
-+ template = u'''
-+ <grit latest_public_release="2" current_release="3">
-+ <outputs>
-+ %s
-+ </outputs>
-+ </grit>'''
-+
-+ part_failures = [
-+ (exception.UnexpectedContent, u'<part file="x">fnord</part>'),
-+ (exception.UnexpectedChild,
-+ u'<part file="x"><output filename="x" type="y" /></part>'),
-+ (exception.FileNotFound, u'<part file="yet_created_x" />'),
-+ ]
-+ for raises, data in part_failures:
-+ data = StringIO(template % data)
-+ self.assertRaises(raises, grd_reader.Parse, data, '.')
-+
-+ gritpart_failures = [
-+ (exception.UnexpectedAttribute, u'<grit-part file="xyz"></grit-part>'),
-+ (exception.MissingElement, u'<output filename="x" type="y" />'),
-+ ]
-+ for raises, data in gritpart_failures:
-+ top_grd = StringIO(template % u'<part file="bad.grp" />')
-+ with util.TempDir({'bad.grp': data}) as temp_dir:
-+ self.assertRaises(raises, grd_reader.Parse, top_grd, temp_dir.GetPath())
-+
-+ def testEarlyEnoughPlatformSpecification(self):
-+ # This is a regression test for issue
-+ # https://code.google.com/p/grit-i18n/issues/detail?id=23
-+ grd_text = u'''<?xml version="1.0" encoding="UTF-8"?>
-+ <grit latest_public_release="1" current_release="1">
-+ <release seq="1">
-+ <messages>
-+ <if expr="not pp_ifdef('use_titlecase')">
-+ <message name="IDS_XYZ">foo</message>
-+ </if>
-+ <!-- The assumption is that use_titlecase is never true for
-+ this platform. When the platform isn't set to 'android'
-+ early enough, we get a duplicate message name. -->
-+ <if expr="os == '%s'">
-+ <message name="IDS_XYZ">boo</message>
-+ </if>
-+ </messages>
-+ </release>
-+ </grit>''' % sys.platform
-+ with util.TempDir({}) as temp_dir:
-+ grd_reader.Parse(StringIO(grd_text), temp_dir.GetPath(),
-+ target_platform='android')
-+
-+
-+if __name__ == '__main__':
-+ unittest.main()
-diff --git a/tools/grit/grit/grit-todo.xml b/tools/grit/grit/grit-todo.xml
-new file mode 100644
-index 0000000000..b8c20fdfad
---- /dev/null
-+++ b/tools/grit/grit/grit-todo.xml
-@@ -0,0 +1,62 @@
-+<?xml version="1.0" encoding="windows-1252"?>
-+<TODOLIST FILEFORMAT="6" PROJECTNAME="GRIT" NEXTUNIQUEID="56" FILEVERSION="69" LASTMODIFIED="2005-08-19">
-+ <TASK STARTDATESTRING="2005-04-08" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38453.49975694" TITLE="check 'name' attribute is unique" TIMEESTUNITS="H" ID="2" PERCENTDONE="100" STARTDATE="38450.00000000" DONEDATESTRING="2005-04-11" POS="22" DONEDATE="38453.00000000"/>
-+ <TASK STARTDATESTRING="2005-04-08" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38488.48189815" TITLE="import id-calculating code" TIMEESTUNITS="H" ID="3" PERCENTDONE="100" STARTDATE="38450.00000000" DONEDATESTRING="2005-05-16" POS="13" DONEDATE="38488.00000000"/>
-+ <TASK STARTDATESTRING="2005-05-16" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38488.48209491" TITLE="Import tool for existing translations" TIMEESTUNITS="H" ID="6" PERCENTDONE="100" STARTDATE="38488.00000000" DONEDATESTRING="2005-06-16" POS="12" DONEDATE="38519.00000000"/>
-+ <TASK STARTDATESTRING="2005-05-16" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38511.00805556" TITLE="Export XMBs" TIMEESTUNITS="H" ID="8" PERCENTDONE="100" STARTDATE="38488.00000000" DONEDATESTRING="2005-06-08" POS="20" DONEDATE="38511.00000000"/>
-+ <TASK STARTDATESTRING="2005-05-16" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38511.00924769" TITLE="Initial Integration" TIMEESTUNITS="H" ID="10" PERCENTDONE="100" STARTDATE="38488.00000000" DONEDATESTRING="2005-06-08" POS="10" DONEDATE="38511.00000000">
-+ <TASK STARTDATESTRING="2005-05-16" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38496.54048611" TITLE="parser for %s strings" TIMEESTUNITS="H" ID="4" PERCENTDONE="100" STARTDATE="38488.00000000" DONEDATESTRING="2005-05-24" POS="2" DONEDATE="38496.00000000"/>
-+ <TASK STARTDATESTRING="2005-05-16" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38497.00261574" TITLE="import tool for existing RC files" TIMEESTUNITS="H" ID="5" PERCENTDONE="100" STARTDATE="38488.00000000" DONEDATESTRING="2005-05-25" POS="4" DONEDATE="38497.00000000">
-+ <TASK STARTDATESTRING="2005-05-24" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38496.92990741" TITLE="handle button value= and img alt= in message HTML text" TIMEESTUNITS="H" ID="22" PERCENTDONE="100" STARTDATE="38496.00000000" DONEDATESTRING="2005-05-24" POS="1" DONEDATE="38496.00000000"/>
-+ <TASK STARTDATESTRING="2005-05-24" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38497.00258102" TITLE="&amp;nbsp; bug" TIMEESTUNITS="H" ID="23" PERCENTDONE="100" STARTDATE="38496.00000000" DONEDATESTRING="2005-05-25" POS="2" DONEDATE="38497.00000000"/>
-+ </TASK>
-+ <TASK STARTDATESTRING="2005-05-16" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38490.61171296" TITLE="grit build" TIMEESTUNITS="H" ID="7" PERCENTDONE="100" STARTDATE="38488.00000000" DONEDATESTRING="2005-05-18" POS="6" DONEDATE="38490.00000000">
-+ <TASK STARTDATESTRING="2005-05-16" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38490.61168981" TITLE="use IDs gathered from gatherers for .h file" TIMEESTUNITS="H" ID="20" PERCENTDONE="100" STARTDATE="38488.00000000" DONEDATESTRING="2005-05-18" POS="1" DONEDATE="38490.00000000"/>
-+ </TASK>
-+ <TASK STARTDATESTRING="2005-05-16" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38504.55199074" TITLE="SCons Integration" TIMEESTUNITS="H" ID="9" PERCENTDONE="100" STARTDATE="38488.00000000" DONEDATESTRING="2005-06-01" POS="1" DONEDATE="38504.00000000"/>
-+ <TASK STARTDATESTRING="2005-05-16" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38490.61181713" TITLE="handle includes" TIMEESTUNITS="H" ID="12" PERCENTDONE="100" STARTDATE="38488.00000000" DONEDATESTRING="2005-05-18" POS="5" DONEDATE="38490.00000000"/>
-+ <TASK STARTDATESTRING="2005-05-24" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38507.98567130" TITLE="output translated HTML templates" TIMEESTUNITS="H" ID="25" PERCENTDONE="100" STARTDATE="38496.00000000" DONEDATESTRING="2005-06-04" POS="3" DONEDATE="38507.00000000"/>
-+ <TASK STARTDATESTRING="2005-05-31" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38507.99394676" TITLE="bug: re-escape too much in RC dialogs etc." TIMEESTUNITS="H" ID="38" PERCENTDONE="100" STARTDATE="38503.00000000" DONEDATESTRING="2005-06-04" POS="7" DONEDATE="38507.00000000"/>
-+ </TASK>
-+ <TASK STARTDATESTRING="2005-05-16" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38489.46444444" TITLE="handle structure variants" TIMEESTUNITS="H" ID="11" PERCENTDONE="100" STARTDATE="38488.00000000" DONEDATESTRING="2005-06-16" POS="15" DONEDATE="38519.00000000"/>
-+ <TASK STARTDATESTRING="2005-05-16" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38489.46456019" TITLE="handle include variants" TIMEESTUNITS="H" ID="13" PERCENTDONE="100" STARTDATE="38488.00000000" DONEDATESTRING="2005-06-16" POS="17" DONEDATE="38519.00000000"/>
-+ <TASK STARTDATESTRING="2005-05-16" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38489.46537037" TITLE="handle translateable text for includes (e.g. image text)" TIMEESTUNITS="H" ID="14" PERCENTDONE="100" STARTDATE="38488.00000000" DONEDATESTRING="2005-06-16" POS="14" DONEDATE="38519.00000000"/>
-+ <TASK STARTDATESTRING="2005-05-16" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38489.46712963" TITLE="ddoc" TIMEESTUNITS="H" ID="15" STARTDATE="38488.00000000" POS="4">
-+ <TASK STARTDATESTRING="2005-05-16" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38489.46718750" TITLE="review comments miket" TIMEESTUNITS="H" ID="16" STARTDATE="38488.00000000" POS="2"/>
-+ <TASK STARTDATESTRING="2005-05-16" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38489.46722222" TITLE="review comments pdoyle" TIMEESTUNITS="H" ID="17" STARTDATE="38488.00000000" POS="1"/>
-+ <TASK STARTDATESTRING="2005-05-16" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38489.46732639" TITLE="remove 'extkey' from structure" TIMEESTUNITS="H" ID="18" STARTDATE="38488.00000000" POS="3"/>
-+ <TASK STARTDATESTRING="2005-05-16" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38489.53537037" TITLE="add 'encoding' to structure" TIMEESTUNITS="H" ID="19" STARTDATE="38488.00000000" POS="6"/>
-+ <TASK STARTDATESTRING="2005-05-31" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38503.55304398" TITLE="document limitation: emitter doesn't emit the translated HTML templates" TIMEESTUNITS="H" ID="30" STARTDATE="38503.00000000" POS="4"/>
-+ <TASK STARTDATESTRING="2005-05-31" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38504.58541667" TITLE="add 'internal_comment' to &lt;message&gt;" TIMEESTUNITS="H" ID="32" STARTDATE="38503.00000000" POS="5"/>
-+ <TASK STARTDATESTRING="2005-05-31" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38505.73391204" TITLE="&lt;outputs&gt; can not have paths (because of SCons integration - goes to build dir)" TIMEESTUNITS="H" ID="36" STARTDATE="38503.00000000" POS="9"/>
-+ <TASK STARTDATESTRING="2005-05-31" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38506.64265046" TITLE="&lt;identifers&gt; and &lt;identifier&gt; nodes" TIMEESTUNITS="H" ID="37" STARTDATE="38503.00000000" POS="10"/>
-+ <TASK STARTDATESTRING="2005-06-23" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38526.62344907" TITLE="&lt;structure&gt; can have 'exclude_from_rc' attribute (default false)" TIMEESTUNITS="H" ID="47" STARTDATE="38526.00000000" POS="8"/>
-+ <TASK STARTDATESTRING="2005-06-23" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38531.94135417" TITLE="add 'enc_check' to &lt;grit&gt;" TIMEESTUNITS="H" ID="48" STARTDATE="38526.00000000" POS="7"/>
-+ </TASK>
-+ <TASK STARTDATESTRING="2005-05-18" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38492.51549769" TITLE="handle nontranslateable messages (in MessageClique?)" TIMEESTUNITS="H" ID="21" PERCENTDONE="100" STARTDATE="38490.00000000" DONEDATESTRING="2005-06-16" POS="16" DONEDATE="38519.00000000"/>
-+ <TASK STARTDATESTRING="2005-05-24" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38505.70454861" TITLE="ask cprince about SCons builder in new mk system" TIMEESTUNITS="H" ID="24" PERCENTDONE="100" STARTDATE="38496.00000000" DONEDATESTRING="2005-06-02" POS="25" DONEDATE="38505.00000000"/>
-+ <TASK STARTDATESTRING="2005-05-24" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38504.57436343" TITLE="fix AOL resource in trunk (&quot;???????&quot;)" TIMEESTUNITS="H" ID="26" PERCENTDONE="100" STARTDATE="38496.00000000" DONEDATESTRING="2005-06-01" POS="19" DONEDATE="38504.00000000"/>
-+ <TASK STARTDATESTRING="2005-05-24" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38498.53893519" TITLE="rc_all vs. rc_translateable vs. rc_nontranslateable" TIMEESTUNITS="H" ID="27" PERCENTDONE="100" STARTDATE="38496.00000000" DONEDATESTRING="2005-06-16" POS="6" DONEDATE="38519.00000000"/>
-+ <TASK STARTDATESTRING="2005-05-31" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38509.45532407" TITLE="make separate .grb &quot;outputs&quot; file (and change SCons integ) (??)" TIMEESTUNITS="H" ID="28" PERCENTDONE="100" STARTDATE="38503.00000000" DONEDATESTRING="2005-06-06" POS="8" DONEDATE="38509.00000000"/>
-+ <TASK STARTDATESTRING="2005-05-31" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38511.00939815" TITLE="fix unit tests so they run from any directory" TIMEESTUNITS="H" ID="33" PERCENTDONE="100" STARTDATE="38503.00000000" DONEDATESTRING="2005-06-08" POS="18" DONEDATE="38511.00000000"/>
-+ <TASK STARTDATESTRING="2005-05-31" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38508.96640046" TITLE="Change R4 tool to CC correct team(s) on GRIT changes" TIMEESTUNITS="H" ID="39" PERCENTDONE="100" STARTDATE="38503.00000000" DONEDATESTRING="2005-06-05" POS="23" DONEDATE="38508.00000000"/>
-+ <TASK STARTDATESTRING="2005-06-07" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38511.00881944" TITLE="Document why wrapper.rc" TIMEESTUNITS="H" ID="40" PERCENTDONE="100" STARTDATE="38510.00000000" DONEDATESTRING="2005-06-08" POS="21" DONEDATE="38511.00000000"/>
-+ <TASK STARTDATESTRING="2005-06-08" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38511.00804398" TITLE="import XTBs" TIMEESTUNITS="H" ID="41" PERCENTDONE="100" STARTDATE="38511.00000000" DONEDATESTRING="2005-06-16" POS="11" DONEDATE="38519.00000000"/>
-+ <TASK STARTDATESTRING="2005-06-08" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38511.00875000" TITLE="Nightly build integration" TIMEESTUNITS="H" ID="42" STARTDATE="38511.00000000" POS="3"/>
-+ <TASK STARTDATESTRING="2005-06-08" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38511.00891204" TITLE="BUGS" TIMEESTUNITS="H" ID="43" STARTDATE="38511.00000000" POS="24">
-+ <TASK STARTDATESTRING="2005-06-08" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38513.03375000" TITLE="Should report error if RC-section structure refers to does not exist" TIMEESTUNITS="H" ID="44" PERCENTDONE="100" STARTDATE="38511.00000000" DONEDATESTRING="2005-06-10" POS="1" DONEDATE="38513.00000000"/>
-+ </TASK>
-+ <TASK STARTDATESTRING="2005-06-08" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38511.00981481" TITLE="NEW FEATURES" TIMEESTUNITS="H" ID="45" PERCENTDONE="100" STARTDATE="38511.00000000" DONEDATESTRING="2005-06-16" POS="7" DONEDATE="38519.00000000">
-+ <TASK STARTDATESTRING="2005-05-31" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38505.70077546" TITLE="Implement line-continuation feature (\ at end of line?)" TIMEESTUNITS="H" ID="34" PERCENTDONE="100" STARTDATE="38503.00000000" DONEDATESTRING="2005-06-16" POS="1" DONEDATE="38519.00000000"/>
-+ <TASK STARTDATESTRING="2005-05-31" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38505.70262731" TITLE="Implement conditional inclusion &amp; reflect the conditionals from R3 RC file" TIMEESTUNITS="H" ID="35" PERCENTDONE="100" STARTDATE="38503.00000000" DONEDATESTRING="2005-06-16" POS="2" DONEDATE="38519.00000000"/>
-+ </TASK>
-+ <TASK STARTDATESTRING="2005-06-08" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38511.01046296" TITLE="TC integration (one-way TO the TC)" TIMEESTUNITS="H" ID="46" PERCENTDONE="100" STARTDATE="38511.00000000" DONEDATESTRING="2005-06-16" POS="5" DONEDATE="38519.00000000"/>
-+ <TASK STARTDATESTRING="2005-06-30" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38533.59072917" TITLE="bazaar20 ad for GRIT help" TIMEESTUNITS="H" ID="49" STARTDATE="38533.00000000" POS="2">
-+ <TASK STARTDATESTRING="2005-08-19" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38583.72346065" TITLE="bazaar20 ideas" TIMEESTUNITS="H" ID="51" STARTDATE="38583.00000000" POS="1">
-+ <TASK STARTDATESTRING="2005-08-19" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38583.72354167" TITLE="GUI for adding/editing messages" TIMEESTUNITS="H" ID="52" STARTDATE="38583.00000000" POS="2"/>
-+ <TASK STARTDATESTRING="2005-08-19" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38583.72365741" TITLE="XLIFF import/export" TIMEESTUNITS="H" ID="54" STARTDATE="38583.00000000" POS="1"/>
-+ </TASK>
-+ </TASK>
-+ <TASK STARTDATESTRING="2005-06-30" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38583.73721065" TITLE="internal_comment for all resource nodes (not just &lt;message&gt;)" TIMEESTUNITS="H" ID="50" PERCENTDONE="100" STARTDATE="38533.00000000" DONEDATESTRING="2005-08-19" POS="9" DONEDATE="38583.73721065"/>
-+ <TASK STARTDATESTRING="2005-08-19" PRIORITY="5" TIMEESPENTUNITS="H" LASTMOD="38583.73743056" TITLE="Preserve XML comments - this gives us line continuation and more" TIMEESTUNITS="H" ID="55" STARTDATE="38583.72326389" POS="1"/>
-+</TODOLIST>
-diff --git a/tools/grit/grit/grit_runner.py b/tools/grit/grit/grit_runner.py
-new file mode 100644
-index 0000000000..26aa0d58c4
---- /dev/null
-+++ b/tools/grit/grit/grit_runner.py
-@@ -0,0 +1,334 @@
-+#!/usr/bin/env python
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+"""Command processor for GRIT. This is the script you invoke to run the various
-+GRIT tools.
-+"""
-+
-+from __future__ import print_function
-+
-+import os
-+import sys
-+if __name__ == '__main__':
-+ sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
-+
-+import getopt
-+
-+from grit import util
-+
-+import grit.extern.FP
-+
-+# Tool info factories; these import only within each factory to avoid
-+# importing most of the GRIT code until required.
-+def ToolFactoryBuild():
-+ import grit.tool.build
-+ return grit.tool.build.RcBuilder()
-+
-+def ToolFactoryBuildInfo():
-+ import grit.tool.buildinfo
-+ return grit.tool.buildinfo.DetermineBuildInfo()
-+
-+def ToolFactoryCount():
-+ import grit.tool.count
-+ return grit.tool.count.CountMessage()
-+
-+def ToolFactoryDiffStructures():
-+ import grit.tool.diff_structures
-+ return grit.tool.diff_structures.DiffStructures()
-+
-+def ToolFactoryMenuTranslationsFromParts():
-+ import grit.tool.menu_from_parts
-+ return grit.tool.menu_from_parts.MenuTranslationsFromParts()
-+
-+def ToolFactoryNewGrd():
-+ import grit.tool.newgrd
-+ return grit.tool.newgrd.NewGrd()
-+
-+def ToolFactoryResizeDialog():
-+ import grit.tool.resize
-+ return grit.tool.resize.ResizeDialog()
-+
-+def ToolFactoryRc2Grd():
-+ import grit.tool.rc2grd
-+ return grit.tool.rc2grd.Rc2Grd()
-+
-+def ToolFactoryTest():
-+ import grit.tool.test
-+ return grit.tool.test.TestTool()
-+
-+def ToolFactoryTranslationToTc():
-+ import grit.tool.transl2tc
-+ return grit.tool.transl2tc.TranslationToTc()
-+
-+def ToolFactoryUnit():
-+ import grit.tool.unit
-+ return grit.tool.unit.UnitTestTool()
-+
-+
-+def ToolFactoryUpdateResourceIds():
-+ import grit.tool.update_resource_ids
-+ return grit.tool.update_resource_ids.UpdateResourceIds()
-+
-+
-+def ToolFactoryXmb():
-+ import grit.tool.xmb
-+ return grit.tool.xmb.OutputXmb()
-+
-+def ToolAndroid2Grd():
-+ import grit.tool.android2grd
-+ return grit.tool.android2grd.Android2Grd()
-+
-+# Keys for the following map
-+_FACTORY = 1
-+_REQUIRES_INPUT = 2
-+_HIDDEN = 3 # optional key - presence indicates tool is hidden
-+
-+# Maps tool names to the tool's module. Done as a list of (key, value) tuples
-+# instead of a map to preserve ordering.
-+_TOOLS = [
-+ ['android2grd', {
-+ _FACTORY: ToolAndroid2Grd,
-+ _REQUIRES_INPUT: False
-+ }],
-+ ['build', {
-+ _FACTORY: ToolFactoryBuild,
-+ _REQUIRES_INPUT: True
-+ }],
-+ ['buildinfo', {
-+ _FACTORY: ToolFactoryBuildInfo,
-+ _REQUIRES_INPUT: True
-+ }],
-+ ['count', {
-+ _FACTORY: ToolFactoryCount,
-+ _REQUIRES_INPUT: True
-+ }],
-+ [
-+ 'menufromparts',
-+ {
-+ _FACTORY: ToolFactoryMenuTranslationsFromParts,
-+ _REQUIRES_INPUT: True,
-+ _HIDDEN: True
-+ }
-+ ],
-+ ['newgrd', {
-+ _FACTORY: ToolFactoryNewGrd,
-+ _REQUIRES_INPUT: False
-+ }],
-+ ['rc2grd', {
-+ _FACTORY: ToolFactoryRc2Grd,
-+ _REQUIRES_INPUT: False
-+ }],
-+ ['resize', {
-+ _FACTORY: ToolFactoryResizeDialog,
-+ _REQUIRES_INPUT: True
-+ }],
-+ ['sdiff', {
-+ _FACTORY: ToolFactoryDiffStructures,
-+ _REQUIRES_INPUT: False
-+ }],
-+ ['test', {
-+ _FACTORY: ToolFactoryTest,
-+ _REQUIRES_INPUT: True,
-+ _HIDDEN: True
-+ }],
-+ [
-+ 'transl2tc',
-+ {
-+ _FACTORY: ToolFactoryTranslationToTc,
-+ _REQUIRES_INPUT: False
-+ }
-+ ],
-+ ['unit', {
-+ _FACTORY: ToolFactoryUnit,
-+ _REQUIRES_INPUT: False
-+ }],
-+ [
-+ 'update_resource_ids',
-+ {
-+ _FACTORY: ToolFactoryUpdateResourceIds,
-+ _REQUIRES_INPUT: False
-+ }
-+ ],
-+ ['xmb', {
-+ _FACTORY: ToolFactoryXmb,
-+ _REQUIRES_INPUT: True
-+ }],
-+]
-+
-+
-+def PrintUsage():
-+ tool_list = ''
-+ for (tool, info) in _TOOLS:
-+ if not _HIDDEN in info:
-+ tool_list += ' %-12s %s\n' % (
-+ tool, info[_FACTORY]().ShortDescription())
-+
-+ print("""GRIT - the Google Resource and Internationalization Tool
-+
-+Usage: grit [GLOBALOPTIONS] TOOL [args to tool]
-+
-+Global options:
-+
-+ -i INPUT Specifies the INPUT file to use (a .grd file). If this is not
-+ specified, GRIT will look for the environment variable GRIT_INPUT.
-+ If it is not present either, GRIT will try to find an input file
-+ named 'resource.grd' in the current working directory.
-+
-+ -h MODULE Causes GRIT to use MODULE.UnsignedFingerPrint instead of
-+ grit.extern.FP.UnsignedFingerprint. MODULE must be
-+ available somewhere in the PYTHONPATH search path.
-+
-+ -v Print more verbose runtime information.
-+
-+ -x Print extremely verbose runtime information. Implies -v
-+
-+ -p FNAME Specifies that GRIT should profile its execution and output the
-+ results to the file FNAME.
-+
-+Tools:
-+
-+ TOOL can be one of the following:
-+%s
-+ For more information on how to use a particular tool, and the specific
-+ arguments you can send to that tool, execute 'grit help TOOL'
-+""" % (tool_list))
-+
-+
-+class Options(object):
-+ """Option storage and parsing."""
-+
-+ def __init__(self):
-+ self.hash = None
-+ self.input = None
-+ self.verbose = False
-+ self.extra_verbose = False
-+ self.output_stream = sys.stdout
-+ self.profile_dest = None
-+
-+ def ReadOptions(self, args):
-+ """Reads options from the start of args and returns the remainder."""
-+ (opts, args) = getopt.getopt(args, 'vxi:p:h:', ('help',))
-+ for (key, val) in opts:
-+ if key == '-h': self.hash = val
-+ elif key == '-i': self.input = val
-+ elif key == '-v':
-+ self.verbose = True
-+ util.verbose = True
-+ elif key == '-x':
-+ self.verbose = True
-+ util.verbose = True
-+ self.extra_verbose = True
-+ util.extra_verbose = True
-+ elif key == '-p': self.profile_dest = val
-+ elif key == '--help':
-+ PrintUsage()
-+ sys.exit(0)
-+
-+ if not self.input:
-+ if 'GRIT_INPUT' in os.environ:
-+ self.input = os.environ['GRIT_INPUT']
-+ else:
-+ self.input = 'resource.grd'
-+
-+ return args
-+
-+ def __repr__(self):
-+ return '(verbose: %d, input: %s)' % (
-+ self.verbose, self.input)
-+
-+
-+def _GetToolInfo(tool):
-+ """Returns the info map for the tool named 'tool' or None if there is no
-+ such tool."""
-+ matches = [t for t in _TOOLS if t[0] == tool]
-+ if not matches:
-+ return None
-+ else:
-+ return matches[0][1]
-+
-+
-+def Main(args=None):
-+ """Parses arguments and does the appropriate thing."""
-+ util.ChangeStdoutEncoding()
-+
-+ # Support for setuptools console wrappers.
-+ if args is None:
-+ args = sys.argv[1:]
-+
-+ options = Options()
-+ try:
-+ args = options.ReadOptions(args) # args may be shorter after this
-+ except getopt.GetoptError as e:
-+ print("grit:", str(e))
-+ print("Try running 'grit help' for valid options.")
-+ return 1
-+ if not args:
-+ print("No tool provided. Try running 'grit help' for a list of tools.")
-+ return 2
-+
-+ tool = args[0]
-+ if tool == 'help':
-+ if len(args) == 1:
-+ PrintUsage()
-+ return 0
-+ else:
-+ tool = args[1]
-+ if not _GetToolInfo(tool):
-+ print("No such tool. Try running 'grit help' for a list of tools.")
-+ return 2
-+
-+ print("Help for 'grit %s' (for general help, run 'grit help'):\n" %
-+ (tool,))
-+ _GetToolInfo(tool)[_FACTORY]().ShowUsage()
-+ return 0
-+ if not _GetToolInfo(tool):
-+ print("No such tool. Try running 'grit help' for a list of tools.")
-+ return 2
-+
-+ try:
-+ if _GetToolInfo(tool)[_REQUIRES_INPUT]:
-+ os.stat(options.input)
-+ except OSError:
-+ print('Input file %s not found.\n'
-+ 'To specify a different input file:\n'
-+ ' 1. Use the GRIT_INPUT environment variable.\n'
-+ ' 2. Use the -i command-line option. This overrides '
-+ 'GRIT_INPUT.\n'
-+ ' 3. Specify neither GRIT_INPUT or -i and GRIT will try to load '
-+ "'resource.grd'\n"
-+ ' from the current directory.' % options.input)
-+ return 2
-+
-+ if options.hash:
-+ grit.extern.FP.UseUnsignedFingerPrintFromModule(options.hash)
-+
-+ try:
-+ toolobject = _GetToolInfo(tool)[_FACTORY]()
-+ if options.profile_dest:
-+ import hotshot
-+ prof = hotshot.Profile(options.profile_dest)
-+ return prof.runcall(toolobject.Run, options, args[1:])
-+ else:
-+ return toolobject.Run(options, args[1:])
-+ except getopt.GetoptError as e:
-+ print("grit: %s: %s" % (tool, str(e)))
-+ print("Try running 'grit help %s' for valid options." % (tool,))
-+ return 1
-+
-+
-+if __name__ == '__main__':
-+ sys.path.append(
-+ os.path.join(
-+ os.path.dirname(
-+ os.path.dirname(os.path.dirname(os.path.abspath(__file__)))),
-+ 'diagnosis'))
-+ try:
-+ import crbug_1001171
-+ with crbug_1001171.DumpStateOnLookupError():
-+ sys.exit(Main(sys.argv[1:]))
-+ except ImportError:
-+ pass
-+
-+ sys.exit(Main(sys.argv[1:]))
-diff --git a/tools/grit/grit/grit_runner_unittest.py b/tools/grit/grit/grit_runner_unittest.py
-new file mode 100644
-index 0000000000..1487001d81
---- /dev/null
-+++ b/tools/grit/grit/grit_runner_unittest.py
-@@ -0,0 +1,42 @@
-+#!/usr/bin/env python
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Unit tests for grit.py'''
-+
-+from __future__ import print_function
-+
-+import os
-+import sys
-+if __name__ == '__main__':
-+ sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
-+
-+import unittest
-+
-+from six import StringIO
-+
-+from grit import util
-+import grit.grit_runner
-+
-+class OptionArgsUnittest(unittest.TestCase):
-+ def setUp(self):
-+ self.buf = StringIO()
-+ self.old_stdout = sys.stdout
-+ sys.stdout = self.buf
-+
-+ def tearDown(self):
-+ sys.stdout = self.old_stdout
-+
-+ def testSimple(self):
-+ grit.grit_runner.Main(['-i',
-+ util.PathFromRoot('grit/testdata/simple-input.xml'),
-+ 'test', 'bla', 'voff', 'ga'])
-+ output = self.buf.getvalue()
-+ self.failUnless(output.count("'test'") == 0) # tool name doesn't occur
-+ self.failUnless(output.count('bla'))
-+ self.failUnless(output.count('simple-input.xml'))
-+
-+
-+if __name__ == '__main__':
-+ unittest.main()
-diff --git a/tools/grit/grit/lazy_re.py b/tools/grit/grit/lazy_re.py
-new file mode 100644
-index 0000000000..5c461e87e7
---- /dev/null
-+++ b/tools/grit/grit/lazy_re.py
-@@ -0,0 +1,46 @@
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''In GRIT, we used to compile a lot of regular expressions at parse
-+time. Since many of them never get used, we use lazy_re to compile
-+them on demand the first time they are used, thus speeding up startup
-+time in some cases.
-+'''
-+
-+from __future__ import print_function
-+
-+import re
-+
-+
-+class LazyRegexObject(object):
-+ '''This object creates a RegexObject with the arguments passed in
-+ its constructor, the first time any attribute except the several on
-+ the class itself is accessed. This accomplishes lazy compilation of
-+ the regular expression while maintaining a nearly-identical
-+ interface.
-+ '''
-+
-+ def __init__(self, *args, **kwargs):
-+ self._stash_args = args
-+ self._stash_kwargs = kwargs
-+ self._lazy_re = None
-+
-+ def _LazyInit(self):
-+ if not self._lazy_re:
-+ self._lazy_re = re.compile(*self._stash_args, **self._stash_kwargs)
-+
-+ def __getattribute__(self, name):
-+ if name in ('_LazyInit', '_lazy_re', '_stash_args', '_stash_kwargs'):
-+ return object.__getattribute__(self, name)
-+ else:
-+ self._LazyInit()
-+ return getattr(self._lazy_re, name)
-+
-+
-+def compile(*args, **kwargs):
-+ '''Creates a LazyRegexObject that, when invoked on, will compile a
-+ re.RegexObject (via re.compile) with the same arguments passed to
-+ this function, and delegate almost all of its methods to it.
-+ '''
-+ return LazyRegexObject(*args, **kwargs)
-diff --git a/tools/grit/grit/lazy_re_unittest.py b/tools/grit/grit/lazy_re_unittest.py
-new file mode 100644
-index 0000000000..8488b454ee
---- /dev/null
-+++ b/tools/grit/grit/lazy_re_unittest.py
-@@ -0,0 +1,40 @@
-+#!/usr/bin/env python
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Unit test for lazy_re.
-+'''
-+
-+from __future__ import print_function
-+
-+import os
-+import sys
-+if __name__ == '__main__':
-+ sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
-+
-+import re
-+import unittest
-+
-+from grit import lazy_re
-+
-+
-+class LazyReUnittest(unittest.TestCase):
-+
-+ def testCreatedOnlyOnDemand(self):
-+ rex = lazy_re.compile('bingo')
-+ self.assertEqual(None, rex._lazy_re)
-+ self.assertTrue(rex.match('bingo'))
-+ self.assertNotEqual(None, rex._lazy_re)
-+
-+ def testJustKwargsWork(self):
-+ rex = lazy_re.compile(flags=re.I, pattern='BiNgO')
-+ self.assertTrue(rex.match('bingo'))
-+
-+ def testPositionalAndKwargsWork(self):
-+ rex = lazy_re.compile('BiNgO', flags=re.I)
-+ self.assertTrue(rex.match('bingo'))
-+
-+
-+if __name__ == '__main__':
-+ unittest.main()
-diff --git a/tools/grit/grit/node/__init__.py b/tools/grit/grit/node/__init__.py
-new file mode 100644
-index 0000000000..2fc0d3360c
---- /dev/null
-+++ b/tools/grit/grit/node/__init__.py
-@@ -0,0 +1,8 @@
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Package 'grit.node'
-+'''
-+
-+pass
-diff --git a/tools/grit/grit/node/base.py b/tools/grit/grit/node/base.py
-new file mode 100644
-index 0000000000..40859d301d
---- /dev/null
-+++ b/tools/grit/grit/node/base.py
-@@ -0,0 +1,670 @@
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Base types for nodes in a GRIT resource tree.
-+'''
-+
-+from __future__ import print_function
-+
-+import ast
-+import os
-+import struct
-+import sys
-+from xml.sax import saxutils
-+
-+import six
-+
-+from grit import constants
-+from grit import clique
-+from grit import exception
-+from grit import util
-+from grit.node import brotli_util
-+import grit.format.gzip_string
-+
-+
-+class Node(object):
-+ '''An item in the tree that has children.'''
-+
-+ # Valid content types that can be returned by _ContentType()
-+ _CONTENT_TYPE_NONE = 0 # No CDATA content but may have children
-+ _CONTENT_TYPE_CDATA = 1 # Only CDATA, no children.
-+ _CONTENT_TYPE_MIXED = 2 # CDATA and children, possibly intermingled
-+
-+ # Types of files to be compressed by default.
-+ _COMPRESS_BY_DEFAULT_EXTENSIONS = ('.js', '.html', '.css', '.svg')
-+
-+ # Default nodes to not whitelist skipped
-+ _whitelist_marked_as_skip = False
-+
-+ # A class-static cache to speed up EvaluateExpression().
-+ # Keys are expressions (e.g. 'is_ios and lang == "fr"'). Values are tuples
-+ # (code, variables_in_expr) where code is the compiled expression and can be
-+ # directly eval'd, and variables_in_expr is the list of variable and method
-+ # names used in the expression (e.g. ['is_ios', 'lang']).
-+ eval_expr_cache = {}
-+
-+ def __init__(self):
-+ self.children = [] # A list of child elements
-+ self.mixed_content = [] # A list of u'' and/or child elements (this
-+ # duplicates 'children' but
-+ # is needed to preserve markup-type content).
-+ self.name = u'' # The name of this element
-+ self.attrs = {} # The set of attributes (keys to values)
-+ self.parent = None # Our parent unless we are the root element.
-+ self.uberclique = None # Allows overriding uberclique for parts of tree
-+ self.source = None # File that this node was parsed from
-+
-+ # This context handler allows you to write "with node:" and get a
-+ # line identifying the offending node if an exception escapes from the body
-+ # of the with statement.
-+ def __enter__(self):
-+ return self
-+
-+ def __exit__(self, exc_type, exc_value, traceback):
-+ if exc_type is not None:
-+ print(u'Error processing node %s: %s' % (six.text_type(self), exc_value))
-+
-+ def __iter__(self):
-+ '''A preorder iteration through the tree that this node is the root of.'''
-+ return self.Preorder()
-+
-+ def Preorder(self):
-+ '''Generator that generates first this node, then the same generator for
-+ any child nodes.'''
-+ yield self
-+ for child in self.children:
-+ for iterchild in child.Preorder():
-+ yield iterchild
-+
-+ def ActiveChildren(self):
-+ '''Returns the children of this node that should be included in the current
-+ configuration. Overridden by <if>.'''
-+ return [node for node in self.children if not node.WhitelistMarkedAsSkip()]
-+
-+ def ActiveDescendants(self):
-+ '''Yields the current node and all descendants that should be included in
-+ the current configuration, in preorder.'''
-+ yield self
-+ for child in self.ActiveChildren():
-+ for descendant in child.ActiveDescendants():
-+ yield descendant
-+
-+ def GetRoot(self):
-+ '''Returns the root Node in the tree this Node belongs to.'''
-+ curr = self
-+ while curr.parent:
-+ curr = curr.parent
-+ return curr
-+
-+ # TODO(joi) Use this (currently untested) optimization?:
-+ #if hasattr(self, '_root'):
-+ # return self._root
-+ #curr = self
-+ #while curr.parent and not hasattr(curr, '_root'):
-+ # curr = curr.parent
-+ #if curr.parent:
-+ # self._root = curr._root
-+ #else:
-+ # self._root = curr
-+ #return self._root
-+
-+ def StartParsing(self, name, parent):
-+ '''Called at the start of parsing.
-+
-+ Args:
-+ name: u'elementname'
-+ parent: grit.node.base.Node or subclass or None
-+ '''
-+ assert isinstance(name, six.string_types)
-+ assert not parent or isinstance(parent, Node)
-+ self.name = name
-+ self.parent = parent
-+
-+ def AddChild(self, child):
-+ '''Adds a child to the list of children of this node, if it is a valid
-+ child for the node.'''
-+ assert isinstance(child, Node)
-+ if (not self._IsValidChild(child) or
-+ self._ContentType() == self._CONTENT_TYPE_CDATA):
-+ explanation = 'invalid child %s for parent %s' % (str(child), self.name)
-+ raise exception.UnexpectedChild(explanation)
-+ self.children.append(child)
-+ self.mixed_content.append(child)
-+
-+ def RemoveChild(self, child_id):
-+ '''Removes the first node that has a "name" attribute which
-+ matches "child_id" in the list of immediate children of
-+ this node.
-+
-+ Args:
-+ child_id: String identifying the child to be removed
-+ '''
-+ index = 0
-+ # Safe not to copy since we only remove the first element found
-+ for child in self.children:
-+ name_attr = child.attrs['name']
-+ if name_attr == child_id:
-+ self.children.pop(index)
-+ self.mixed_content.pop(index)
-+ break
-+ index += 1
-+
-+ def AppendContent(self, content):
-+ '''Appends a chunk of text as content of this node.
-+
-+ Args:
-+ content: u'hello'
-+
-+ Return:
-+ None
-+ '''
-+ assert isinstance(content, six.string_types)
-+ if self._ContentType() != self._CONTENT_TYPE_NONE:
-+ self.mixed_content.append(content)
-+ elif content.strip() != '':
-+ raise exception.UnexpectedContent()
-+
-+ def HandleAttribute(self, attrib, value):
-+ '''Informs the node of an attribute that was parsed out of the GRD file
-+ for it.
-+
-+ Args:
-+ attrib: 'name'
-+ value: 'fooblat'
-+
-+ Return:
-+ None
-+ '''
-+ assert isinstance(attrib, six.string_types)
-+ assert isinstance(value, six.string_types)
-+ if self._IsValidAttribute(attrib, value):
-+ self.attrs[attrib] = value
-+ else:
-+ raise exception.UnexpectedAttribute(attrib)
-+
-+ def EndParsing(self):
-+ '''Called at the end of parsing.'''
-+
-+ # TODO(joi) Rewrite this, it's extremely ugly!
-+ if len(self.mixed_content):
-+ if isinstance(self.mixed_content[0], six.string_types):
-+ # Remove leading and trailing chunks of pure whitespace.
-+ while (len(self.mixed_content) and
-+ isinstance(self.mixed_content[0], six.string_types) and
-+ self.mixed_content[0].strip() == ''):
-+ self.mixed_content = self.mixed_content[1:]
-+ # Strip leading and trailing whitespace from mixed content chunks
-+ # at front and back.
-+ if (len(self.mixed_content) and
-+ isinstance(self.mixed_content[0], six.string_types)):
-+ self.mixed_content[0] = self.mixed_content[0].lstrip()
-+ # Remove leading and trailing ''' (used to demarcate whitespace)
-+ if (len(self.mixed_content) and
-+ isinstance(self.mixed_content[0], six.string_types)):
-+ if self.mixed_content[0].startswith("'''"):
-+ self.mixed_content[0] = self.mixed_content[0][3:]
-+ if len(self.mixed_content):
-+ if isinstance(self.mixed_content[-1], six.string_types):
-+ # Same stuff all over again for the tail end.
-+ while (len(self.mixed_content) and
-+ isinstance(self.mixed_content[-1], six.string_types) and
-+ self.mixed_content[-1].strip() == ''):
-+ self.mixed_content = self.mixed_content[:-1]
-+ if (len(self.mixed_content) and
-+ isinstance(self.mixed_content[-1], six.string_types)):
-+ self.mixed_content[-1] = self.mixed_content[-1].rstrip()
-+ if (len(self.mixed_content) and
-+ isinstance(self.mixed_content[-1], six.string_types)):
-+ if self.mixed_content[-1].endswith("'''"):
-+ self.mixed_content[-1] = self.mixed_content[-1][:-3]
-+
-+ # Check that all mandatory attributes are there.
-+ for node_mandatt in self.MandatoryAttributes():
-+ mandatt_list = []
-+ if node_mandatt.find('|') >= 0:
-+ mandatt_list = node_mandatt.split('|')
-+ else:
-+ mandatt_list.append(node_mandatt)
-+
-+ mandatt_option_found = False
-+ for mandatt in mandatt_list:
-+ assert mandatt not in self.DefaultAttributes()
-+ if mandatt in self.attrs:
-+ if not mandatt_option_found:
-+ mandatt_option_found = True
-+ else:
-+ raise exception.MutuallyExclusiveMandatoryAttribute(mandatt)
-+
-+ if not mandatt_option_found:
-+ raise exception.MissingMandatoryAttribute(mandatt)
-+
-+ # Add default attributes if not specified in input file.
-+ for defattr in self.DefaultAttributes():
-+ if not defattr in self.attrs:
-+ self.attrs[defattr] = self.DefaultAttributes()[defattr]
-+
-+ def GetCdata(self):
-+ '''Returns all CDATA of this element, concatenated into a single
-+ string. Note that this ignores any elements embedded in CDATA.'''
-+ return ''.join([c for c in self.mixed_content
-+ if isinstance(c, six.string_types)])
-+
-+ def __str__(self):
-+ '''Returns this node and all nodes below it as an XML document in a Unicode
-+ string.'''
-+ header = u'<?xml version="1.0" encoding="UTF-8"?>\n'
-+ return header + self.FormatXml()
-+
-+ # Some Python 2 glue.
-+ __unicode__ = __str__
-+
-+ def FormatXml(self, indent = u'', one_line = False):
-+ '''Returns this node and all nodes below it as an XML
-+ element in a Unicode string. This differs from __unicode__ in that it does
-+ not include the <?xml> stuff at the top of the string. If one_line is true,
-+ children and CDATA are layed out in a way that preserves internal
-+ whitespace.
-+ '''
-+ assert isinstance(indent, six.string_types)
-+
-+ content_one_line = (one_line or
-+ self._ContentType() == self._CONTENT_TYPE_MIXED)
-+ inside_content = self.ContentsAsXml(indent, content_one_line)
-+
-+ # Then the attributes for this node.
-+ attribs = u''
-+ default_attribs = self.DefaultAttributes()
-+ for attrib, value in sorted(self.attrs.items()):
-+ # Only print an attribute if it is other than the default value.
-+ if attrib not in default_attribs or value != default_attribs[attrib]:
-+ attribs += u' %s=%s' % (attrib, saxutils.quoteattr(value))
-+
-+ # Finally build the XML for our node and return it
-+ if len(inside_content) > 0:
-+ if one_line:
-+ return u'<%s%s>%s</%s>' % (self.name, attribs, inside_content,
-+ self.name)
-+ elif content_one_line:
-+ return u'%s<%s%s>\n%s %s\n%s</%s>' % (
-+ indent, self.name, attribs,
-+ indent, inside_content,
-+ indent, self.name)
-+ else:
-+ return u'%s<%s%s>\n%s\n%s</%s>' % (
-+ indent, self.name, attribs,
-+ inside_content,
-+ indent, self.name)
-+ else:
-+ return u'%s<%s%s />' % (indent, self.name, attribs)
-+
-+ def ContentsAsXml(self, indent, one_line):
-+ '''Returns the contents of this node (CDATA and child elements) in XML
-+ format. If 'one_line' is true, the content will be laid out on one line.'''
-+ assert isinstance(indent, six.string_types)
-+
-+ # Build the contents of the element.
-+ inside_parts = []
-+ last_item = None
-+ for mixed_item in self.mixed_content:
-+ if isinstance(mixed_item, Node):
-+ inside_parts.append(mixed_item.FormatXml(indent + u' ', one_line))
-+ if not one_line:
-+ inside_parts.append(u'\n')
-+ else:
-+ message = mixed_item
-+ # If this is the first item and it starts with whitespace, we add
-+ # the ''' delimiter.
-+ if not last_item and message.lstrip() != message:
-+ message = u"'''" + message
-+ inside_parts.append(util.EncodeCdata(message))
-+ last_item = mixed_item
-+
-+ # If there are only child nodes and no cdata, there will be a spurious
-+ # trailing \n
-+ if len(inside_parts) and inside_parts[-1] == '\n':
-+ inside_parts = inside_parts[:-1]
-+
-+ # If the last item is a string (not a node) and ends with whitespace,
-+ # we need to add the ''' delimiter.
-+ if (isinstance(last_item, six.string_types) and
-+ last_item.rstrip() != last_item):
-+ inside_parts[-1] = inside_parts[-1] + u"'''"
-+
-+ return u''.join(inside_parts)
-+
-+ def SubstituteMessages(self, substituter):
-+ '''Applies substitutions to all messages in the tree.
-+
-+ Called as a final step of RunGatherers.
-+
-+ Args:
-+ substituter: a grit.util.Substituter object.
-+ '''
-+ for child in self.children:
-+ child.SubstituteMessages(substituter)
-+
-+ def _IsValidChild(self, child):
-+ '''Returns true if 'child' is a valid child of this node.
-+ Overridden by subclasses.'''
-+ return False
-+
-+ def _IsValidAttribute(self, name, value):
-+ '''Returns true if 'name' is the name of a valid attribute of this element
-+ and 'value' is a valid value for that attribute. Overriden by
-+ subclasses unless they have only mandatory attributes.'''
-+ return (name in self.MandatoryAttributes() or
-+ name in self.DefaultAttributes())
-+
-+ def _ContentType(self):
-+ '''Returns the type of content this element can have. Overridden by
-+ subclasses. The content type can be one of the _CONTENT_TYPE_XXX constants
-+ above.'''
-+ return self._CONTENT_TYPE_NONE
-+
-+ def MandatoryAttributes(self):
-+ '''Returns a list of attribute names that are mandatory (non-optional)
-+ on the current element. One can specify a list of
-+ "mutually exclusive mandatory" attributes by specifying them as one
-+ element in the list, separated by a "|" character.
-+ '''
-+ return []
-+
-+ def DefaultAttributes(self):
-+ '''Returns a dictionary of attribute names that have defaults, mapped to
-+ the default value. Overridden by subclasses.'''
-+ return {}
-+
-+ def GetCliques(self):
-+ '''Returns all MessageClique objects belonging to this node. Overridden
-+ by subclasses.
-+
-+ Return:
-+ [clique1, clique2] or []
-+ '''
-+ return []
-+
-+ def ToRealPath(self, path_from_basedir):
-+ '''Returns a real path (which can be absolute or relative to the current
-+ working directory), given a path that is relative to the base directory
-+ set for the GRIT input file.
-+
-+ Args:
-+ path_from_basedir: '..'
-+
-+ Return:
-+ 'resource'
-+ '''
-+ return util.normpath(os.path.join(self.GetRoot().GetBaseDir(),
-+ os.path.expandvars(path_from_basedir)))
-+
-+ def GetInputPath(self):
-+ '''Returns a path, relative to the base directory set for the grd file,
-+ that points to the file the node refers to.
-+ '''
-+ # This implementation works for most nodes that have an input file.
-+ return self.attrs['file']
-+
-+ def UberClique(self):
-+ '''Returns the uberclique that should be used for messages originating in
-+ a given node. If the node itself has its uberclique set, that is what we
-+ use, otherwise we search upwards until we find one. If we do not find one
-+ even at the root node, we set the root node's uberclique to a new
-+ uberclique instance.
-+ '''
-+ node = self
-+ while not node.uberclique and node.parent:
-+ node = node.parent
-+ if not node.uberclique:
-+ node.uberclique = clique.UberClique()
-+ return node.uberclique
-+
-+ def IsTranslateable(self):
-+ '''Returns false if the node has contents that should not be translated,
-+ otherwise returns false (even if the node has no contents).
-+ '''
-+ if not 'translateable' in self.attrs:
-+ return True
-+ else:
-+ return self.attrs['translateable'] == 'true'
-+
-+ def IsAccessibilityWithNoUI(self):
-+ '''Returns true if the node is marked as an accessibility label and the
-+ message isn't shown in the UI. Otherwise returns false. This label is
-+ used to determine if the text requires screenshots.'''
-+ if not 'is_accessibility_with_no_ui' in self.attrs:
-+ return False
-+ else:
-+ return self.attrs['is_accessibility_with_no_ui'] == 'true'
-+
-+ def GetNodeById(self, id):
-+ '''Returns the node in the subtree parented by this node that has a 'name'
-+ attribute matching 'id'. Returns None if no such node is found.
-+ '''
-+ for node in self:
-+ if 'name' in node.attrs and node.attrs['name'] == id:
-+ return node
-+ return None
-+
-+ def GetChildrenOfType(self, type):
-+ '''Returns a list of all subnodes (recursing to all leaves) of this node
-+ that are of the indicated type (or tuple of types).
-+
-+ Args:
-+ type: A type you could use with isinstance().
-+
-+ Return:
-+ A list, possibly empty.
-+ '''
-+ return [child for child in self if isinstance(child, type)]
-+
-+ def GetTextualIds(self):
-+ '''Returns a list of the textual ids of this node.
-+ '''
-+ if 'name' in self.attrs:
-+ return [self.attrs['name']]
-+ return []
-+
-+ @classmethod
-+ def EvaluateExpression(cls, expr, defs, target_platform, extra_variables={}):
-+ '''Worker for EvaluateCondition (below) and conditions in XTB files.'''
-+ if expr in cls.eval_expr_cache:
-+ code, variables_in_expr = cls.eval_expr_cache[expr]
-+ else:
-+ # Get a list of all variable and method names used in the expression.
-+ syntax_tree = ast.parse(expr, mode='eval')
-+ variables_in_expr = [node.id for node in ast.walk(syntax_tree) if
-+ isinstance(node, ast.Name) and node.id not in ('True', 'False')]
-+ code = compile(syntax_tree, filename='<string>', mode='eval')
-+ cls.eval_expr_cache[expr] = code, variables_in_expr
-+
-+ # Set values only for variables that are needed to eval the expression.
-+ variable_map = {}
-+ for name in variables_in_expr:
-+ if name == 'os':
-+ value = target_platform
-+ elif name == 'defs':
-+ value = defs
-+
-+ elif name == 'is_linux':
-+ value = target_platform.startswith('linux')
-+ elif name == 'is_macosx':
-+ value = target_platform == 'darwin'
-+ elif name == 'is_win':
-+ value = target_platform in ('cygwin', 'win32')
-+ elif name == 'is_android':
-+ value = target_platform == 'android'
-+ elif name == 'is_ios':
-+ value = target_platform == 'ios'
-+ elif name == 'is_bsd':
-+ value = 'bsd' in target_platform
-+ elif name == 'is_posix':
-+ value = (target_platform in ('darwin', 'linux2', 'linux3', 'sunos5',
-+ 'android', 'ios')
-+ or 'bsd' in target_platform)
-+
-+ elif name == 'pp_ifdef':
-+ def pp_ifdef(symbol):
-+ return symbol in defs
-+ value = pp_ifdef
-+ elif name == 'pp_if':
-+ def pp_if(symbol):
-+ return defs.get(symbol, False)
-+ value = pp_if
-+
-+ elif name in defs:
-+ value = defs[name]
-+ elif name in extra_variables:
-+ value = extra_variables[name]
-+ else:
-+ # Undefined variables default to False.
-+ value = False
-+
-+ variable_map[name] = value
-+
-+ eval_result = eval(code, {}, variable_map)
-+ assert isinstance(eval_result, bool)
-+ return eval_result
-+
-+ def EvaluateCondition(self, expr):
-+ '''Returns true if and only if the Python expression 'expr' evaluates
-+ to true.
-+
-+ The expression is given a few local variables:
-+ - 'lang' is the language currently being output
-+ (the 'lang' attribute of the <output> element).
-+ - 'context' is the current output context
-+ (the 'context' attribute of the <output> element).
-+ - 'defs' is a map of C preprocessor-style symbol names to their values.
-+ - 'os' is the current platform (likely 'linux2', 'win32' or 'darwin').
-+ - 'pp_ifdef(symbol)' is a shorthand for "symbol in defs".
-+ - 'pp_if(symbol)' is a shorthand for "symbol in defs and defs[symbol]".
-+ - 'is_linux', 'is_macosx', 'is_win', 'is_posix' are true if 'os'
-+ matches the given platform.
-+ '''
-+ root = self.GetRoot()
-+ lang = getattr(root, 'output_language', '')
-+ context = getattr(root, 'output_context', '')
-+ defs = getattr(root, 'defines', {})
-+ target_platform = getattr(root, 'target_platform', '')
-+ extra_variables = {
-+ 'lang': lang,
-+ 'context': context,
-+ }
-+ return Node.EvaluateExpression(
-+ expr, defs, target_platform, extra_variables)
-+
-+ def OnlyTheseTranslations(self, languages):
-+ '''Turns off loading of translations for languages not in the provided list.
-+
-+ Attrs:
-+ languages: ['fr', 'zh_cn']
-+ '''
-+ for node in self:
-+ if (hasattr(node, 'IsTranslation') and
-+ node.IsTranslation() and
-+ node.GetLang() not in languages):
-+ node.DisableLoading()
-+
-+ def FindBooleanAttribute(self, attr, default, skip_self):
-+ '''Searches all ancestors of the current node for the nearest enclosing
-+ definition of the given boolean attribute.
-+
-+ Args:
-+ attr: 'fallback_to_english'
-+ default: What to return if no node defines the attribute.
-+ skip_self: Don't check the current node, only its parents.
-+ '''
-+ p = self.parent if skip_self else self
-+ while p:
-+ value = p.attrs.get(attr, 'default').lower()
-+ if value != 'default':
-+ return (value == 'true')
-+ p = p.parent
-+ return default
-+
-+ def PseudoIsAllowed(self):
-+ '''Returns true if this node is allowed to use pseudo-translations. This
-+ is true by default, unless this node is within a <release> node that has
-+ the allow_pseudo attribute set to false.
-+ '''
-+ return self.FindBooleanAttribute('allow_pseudo',
-+ default=True, skip_self=True)
-+
-+ def ShouldFallbackToEnglish(self):
-+ '''Returns true iff this node should fall back to English when
-+ pseudotranslations are disabled and no translation is available for a
-+ given message.
-+ '''
-+ return self.FindBooleanAttribute('fallback_to_english',
-+ default=False, skip_self=True)
-+
-+ def WhitelistMarkedAsSkip(self):
-+ '''Returns true if the node is marked to be skipped in the output by a
-+ whitelist.
-+ '''
-+ return self._whitelist_marked_as_skip
-+
-+ def SetWhitelistMarkedAsSkip(self, mark_skipped):
-+ '''Sets WhitelistMarkedAsSkip.
-+ '''
-+ self._whitelist_marked_as_skip = mark_skipped
-+
-+ def ExpandVariables(self):
-+ '''Whether we need to expand variables on a given node.'''
-+ return False
-+
-+ def IsResourceMapSource(self):
-+ '''Whether this node is a resource map source.'''
-+ return False
-+
-+ def CompressDataIfNeeded(self, data):
-+ '''Compress data using the format specified in the compress attribute.
-+
-+ Args:
-+ data: The data to compressed.
-+ Returns:
-+ The data in gzipped or brotli compressed format. If the format is
-+ unspecified then this returns the data uncompressed.
-+ '''
-+
-+ compress = self.attrs.get('compress')
-+
-+ # Compress JS, HTML, CSS and SVG files by default (gzip), unless |compress|
-+ # is explicitly specified.
-+ compress_by_default = (compress == 'default'
-+ and self.attrs.get('file').endswith(
-+ self._COMPRESS_BY_DEFAULT_EXTENSIONS))
-+
-+ if compress == 'gzip' or compress_by_default:
-+ # We only use rsyncable compression on Linux.
-+ # We exclude ChromeOS since ChromeOS bots are Linux based but do not have
-+ # the --rsyncable option built in for gzip. See crbug.com/617950.
-+ if sys.platform == 'linux2' and 'chromeos' not in self.GetRoot().defines:
-+ return grit.format.gzip_string.GzipStringRsyncable(data)
-+ return grit.format.gzip_string.GzipString(data)
-+
-+ if compress == 'brotli':
-+ # The length of the uncompressed data as 8 bytes little-endian.
-+ size_bytes = struct.pack("<q", len(data))
-+ data = brotli_util.BrotliCompress(data)
-+ # BROTLI_CONST is prepended to brotli decompressed data in order to
-+ # easily check if a resource has been brotli compressed.
-+ # The length of the uncompressed data is also appended to the start,
-+ # truncated to 6 bytes, little-endian. size_bytes is 8 bytes,
-+ # need to truncate further to 6.
-+ formatter = b'%ds %dx %ds' % (6, 2, len(size_bytes) - 8)
-+ return (constants.BROTLI_CONST +
-+ b''.join(struct.unpack(formatter, size_bytes)) +
-+ data)
-+
-+ if compress == 'false' or compress == 'default':
-+ return data
-+
-+ raise Exception('Invalid value for compression')
-+
-+
-+class ContentNode(Node):
-+ '''Convenience baseclass for nodes that can have content.'''
-+ def _ContentType(self):
-+ return self._CONTENT_TYPE_MIXED
-diff --git a/tools/grit/grit/node/base_unittest.py b/tools/grit/grit/node/base_unittest.py
-new file mode 100644
-index 0000000000..32a5a0ca59
---- /dev/null
-+++ b/tools/grit/grit/node/base_unittest.py
-@@ -0,0 +1,259 @@
-+#!/usr/bin/env python
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Unit tests for base.Node functionality (as used in various subclasses)'''
-+
-+from __future__ import print_function
-+
-+import os
-+import sys
-+if __name__ == '__main__':
-+ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
-+
-+import unittest
-+
-+from six import StringIO
-+
-+from grit import grd_reader
-+from grit import util
-+from grit.node import base
-+from grit.node import message
-+
-+
-+def MakePlaceholder(phname='BINGO'):
-+ ph = message.PhNode()
-+ ph.StartParsing(u'ph', None)
-+ ph.HandleAttribute(u'name', phname)
-+ ph.AppendContent(u'bongo')
-+ ph.EndParsing()
-+ return ph
-+
-+
-+class NodeUnittest(unittest.TestCase):
-+ def testWhitespaceHandling(self):
-+ # We test using the Message node type.
-+ node = message.MessageNode()
-+ node.StartParsing(u'hello', None)
-+ node.HandleAttribute(u'name', u'bla')
-+ node.AppendContent(u" ''' two spaces ")
-+ node.EndParsing()
-+ self.failUnless(node.GetCdata() == u' two spaces')
-+
-+ node = message.MessageNode()
-+ node.StartParsing(u'message', None)
-+ node.HandleAttribute(u'name', u'bla')
-+ node.AppendContent(u" two spaces ''' ")
-+ node.EndParsing()
-+ self.failUnless(node.GetCdata() == u'two spaces ')
-+
-+ def testWhitespaceHandlingWithChildren(self):
-+ # We test using the Message node type.
-+ node = message.MessageNode()
-+ node.StartParsing(u'message', None)
-+ node.HandleAttribute(u'name', u'bla')
-+ node.AppendContent(u" ''' two spaces ")
-+ node.AddChild(MakePlaceholder())
-+ node.AppendContent(u' space before and after ')
-+ node.AddChild(MakePlaceholder('BONGO'))
-+ node.AppendContent(u" space before two after '''")
-+ node.EndParsing()
-+ self.failUnless(node.mixed_content[0] == u' two spaces ')
-+ self.failUnless(node.mixed_content[2] == u' space before and after ')
-+ self.failUnless(node.mixed_content[-1] == u' space before two after ')
-+
-+ def testXmlFormatMixedContent(self):
-+ # Again test using the Message node type, because it is the only mixed
-+ # content node.
-+ node = message.MessageNode()
-+ node.StartParsing(u'message', None)
-+ node.HandleAttribute(u'name', u'name')
-+ node.AppendContent(u'Hello <young> ')
-+
-+ ph = message.PhNode()
-+ ph.StartParsing(u'ph', None)
-+ ph.HandleAttribute(u'name', u'USERNAME')
-+ ph.AppendContent(u'$1')
-+ ex = message.ExNode()
-+ ex.StartParsing(u'ex', None)
-+ ex.AppendContent(u'Joi')
-+ ex.EndParsing()
-+ ph.AddChild(ex)
-+ ph.EndParsing()
-+
-+ node.AddChild(ph)
-+ node.EndParsing()
-+
-+ non_indented_xml = node.FormatXml()
-+ self.failUnless(non_indented_xml == u'<message name="name">\n Hello '
-+ u'&lt;young&gt; <ph name="USERNAME">$1<ex>Joi</ex></ph>'
-+ u'\n</message>')
-+
-+ indented_xml = node.FormatXml(u' ')
-+ self.failUnless(indented_xml == u' <message name="name">\n Hello '
-+ u'&lt;young&gt; <ph name="USERNAME">$1<ex>Joi</ex></ph>'
-+ u'\n </message>')
-+
-+ def testXmlFormatMixedContentWithLeadingWhitespace(self):
-+ # Again test using the Message node type, because it is the only mixed
-+ # content node.
-+ node = message.MessageNode()
-+ node.StartParsing(u'message', None)
-+ node.HandleAttribute(u'name', u'name')
-+ node.AppendContent(u"''' Hello <young> ")
-+
-+ ph = message.PhNode()
-+ ph.StartParsing(u'ph', None)
-+ ph.HandleAttribute(u'name', u'USERNAME')
-+ ph.AppendContent(u'$1')
-+ ex = message.ExNode()
-+ ex.StartParsing(u'ex', None)
-+ ex.AppendContent(u'Joi')
-+ ex.EndParsing()
-+ ph.AddChild(ex)
-+ ph.EndParsing()
-+
-+ node.AddChild(ph)
-+ node.AppendContent(u" yessiree '''")
-+ node.EndParsing()
-+
-+ non_indented_xml = node.FormatXml()
-+ self.failUnless(non_indented_xml ==
-+ u"<message name=\"name\">\n ''' Hello"
-+ u' &lt;young&gt; <ph name="USERNAME">$1<ex>Joi</ex></ph>'
-+ u" yessiree '''\n</message>")
-+
-+ indented_xml = node.FormatXml(u' ')
-+ self.failUnless(indented_xml ==
-+ u" <message name=\"name\">\n ''' Hello"
-+ u' &lt;young&gt; <ph name="USERNAME">$1<ex>Joi</ex></ph>'
-+ u" yessiree '''\n </message>")
-+
-+ self.failUnless(node.GetNodeById('name'))
-+
-+ def testXmlFormatContentWithEntities(self):
-+ '''Tests a bug where &nbsp; would not be escaped correctly.'''
-+ from grit import tclib
-+ msg_node = message.MessageNode.Construct(None, tclib.Message(
-+ text = 'BEGIN_BOLDHelloWHITESPACEthere!END_BOLD Bingo!',
-+ placeholders = [
-+ tclib.Placeholder('BEGIN_BOLD', '<b>', 'bla'),
-+ tclib.Placeholder('WHITESPACE', '&nbsp;', 'bla'),
-+ tclib.Placeholder('END_BOLD', '</b>', 'bla')]),
-+ 'BINGOBONGO')
-+ xml = msg_node.FormatXml()
-+ self.failUnless(xml.find('&nbsp;') == -1, 'should have no entities')
-+
-+ def testIter(self):
-+ # First build a little tree of message and ph nodes.
-+ node = message.MessageNode()
-+ node.StartParsing(u'message', None)
-+ node.HandleAttribute(u'name', u'bla')
-+ node.AppendContent(u" ''' two spaces ")
-+ node.AppendContent(u' space before and after ')
-+ ph = message.PhNode()
-+ ph.StartParsing(u'ph', None)
-+ ph.AddChild(message.ExNode())
-+ ph.HandleAttribute(u'name', u'BINGO')
-+ ph.AppendContent(u'bongo')
-+ node.AddChild(ph)
-+ node.AddChild(message.PhNode())
-+ node.AppendContent(u" space before two after '''")
-+
-+ order = [message.MessageNode, message.PhNode, message.ExNode, message.PhNode]
-+ for n in node:
-+ self.failUnless(type(n) == order[0])
-+ order = order[1:]
-+ self.failUnless(len(order) == 0)
-+
-+ def testGetChildrenOfType(self):
-+ xml = '''<?xml version="1.0" encoding="UTF-8"?>
-+ <grit latest_public_release="2" source_lang_id="en-US"
-+ current_release="3" base_dir=".">
-+ <outputs>
-+ <output filename="resource.h" type="rc_header" />
-+ <output filename="en/generated_resources.rc" type="rc_all"
-+ lang="en" />
-+ <if expr="pp_if('NOT_TRUE')">
-+ <output filename="de/generated_resources.rc" type="rc_all"
-+ lang="de" />
-+ </if>
-+ </outputs>
-+ <release seq="3">
-+ <messages>
-+ <message name="ID_HELLO">Hello!</message>
-+ </messages>
-+ </release>
-+ </grit>'''
-+ grd = grd_reader.Parse(StringIO(xml),
-+ util.PathFromRoot('grit/test/data'))
-+ from grit.node import node_io
-+ output_nodes = grd.GetChildrenOfType(node_io.OutputNode)
-+ self.failUnlessEqual(len(output_nodes), 3)
-+ self.failUnlessEqual(output_nodes[2].attrs['filename'],
-+ 'de/generated_resources.rc')
-+
-+ def testEvaluateExpression(self):
-+ def AssertExpr(expected_value, expr, defs, target_platform,
-+ extra_variables):
-+ self.failUnlessEqual(expected_value, base.Node.EvaluateExpression(
-+ expr, defs, target_platform, extra_variables))
-+
-+ AssertExpr(True, "True", {}, 'linux', {})
-+ AssertExpr(False, "False", {}, 'linux', {})
-+ AssertExpr(True, "True or False", {}, 'linux', {})
-+ AssertExpr(False, "True and False", {}, 'linux', {})
-+ AssertExpr(True, "os == 'linux'", {}, 'linux', {})
-+ AssertExpr(False, "os == 'linux'", {}, 'ios', {})
-+ AssertExpr(True, "'foo' in defs", {'foo': 'bar'}, 'ios', {})
-+ AssertExpr(False, "'foo' in defs", {'baz': 'bar'}, 'ios', {})
-+ AssertExpr(False, "'foo' in defs", {}, 'ios', {})
-+ AssertExpr(True, "is_linux", {}, 'linux2', {})
-+ AssertExpr(False, "is_linux", {}, 'win32', {})
-+ AssertExpr(True, "is_macosx", {}, 'darwin', {})
-+ AssertExpr(False, "is_macosx", {}, 'ios', {})
-+ AssertExpr(True, "is_win", {}, 'win32', {})
-+ AssertExpr(False, "is_win", {}, 'darwin', {})
-+ AssertExpr(True, "is_android", {}, 'android', {})
-+ AssertExpr(False, "is_android", {}, 'linux3', {})
-+ AssertExpr(True, "is_ios", {}, 'ios', {})
-+ AssertExpr(False, "is_ios", {}, 'darwin', {})
-+ AssertExpr(True, "is_posix", {}, 'linux2', {})
-+ AssertExpr(True, "is_posix", {}, 'darwin', {})
-+ AssertExpr(True, "is_posix", {}, 'android', {})
-+ AssertExpr(True, "is_posix", {}, 'ios', {})
-+ AssertExpr(True, "is_posix", {}, 'freebsd7', {})
-+ AssertExpr(False, "is_posix", {}, 'win32', {})
-+ AssertExpr(True, "pp_ifdef('foo')", {'foo': True}, 'win32', {})
-+ AssertExpr(True, "pp_ifdef('foo')", {'foo': False}, 'win32', {})
-+ AssertExpr(False, "pp_ifdef('foo')", {'bar': True}, 'win32', {})
-+ AssertExpr(True, "pp_if('foo')", {'foo': True}, 'win32', {})
-+ AssertExpr(False, "pp_if('foo')", {'foo': False}, 'win32', {})
-+ AssertExpr(False, "pp_if('foo')", {'bar': True}, 'win32', {})
-+ AssertExpr(True, "foo", {'foo': True}, 'win32', {})
-+ AssertExpr(False, "foo", {'foo': False}, 'win32', {})
-+ AssertExpr(False, "foo", {'bar': True}, 'win32', {})
-+ AssertExpr(True, "foo == 'baz'", {'foo': 'baz'}, 'win32', {})
-+ AssertExpr(False, "foo == 'baz'", {'foo': True}, 'win32', {})
-+ AssertExpr(False, "foo == 'baz'", {}, 'win32', {})
-+ AssertExpr(True, "lang == 'de'", {}, 'win32', {'lang': 'de'})
-+ AssertExpr(False, "lang == 'de'", {}, 'win32', {'lang': 'fr'})
-+ AssertExpr(False, "lang == 'de'", {}, 'win32', {})
-+
-+ # Test a couple more complex expressions for good measure.
-+ AssertExpr(True, "is_ios and (lang in ['de', 'fr'] or foo)",
-+ {'foo': 'bar'}, 'ios', {'lang': 'fr', 'context': 'today'})
-+ AssertExpr(False, "is_ios and (lang in ['de', 'fr'] or foo)",
-+ {'foo': False}, 'linux2', {'lang': 'fr', 'context': 'today'})
-+ AssertExpr(False, "is_ios and (lang in ['de', 'fr'] or foo)",
-+ {'baz': 'bar'}, 'ios', {'lang': 'he', 'context': 'today'})
-+ AssertExpr(True, "foo == 'bar' or not baz",
-+ {'foo': 'bar', 'fun': True}, 'ios', {'lang': 'en'})
-+ AssertExpr(True, "foo == 'bar' or not baz",
-+ {}, 'ios', {'lang': 'en', 'context': 'java'})
-+ AssertExpr(False, "foo == 'bar' or not baz",
-+ {'foo': 'ruz', 'baz': True}, 'ios', {'lang': 'en'})
-+
-+if __name__ == '__main__':
-+ unittest.main()
-diff --git a/tools/grit/grit/node/brotli_util.py b/tools/grit/grit/node/brotli_util.py
-new file mode 100644
-index 0000000000..77f70e49d5
---- /dev/null
-+++ b/tools/grit/grit/node/brotli_util.py
-@@ -0,0 +1,29 @@
-+# 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.
-+
-+"""Framework for compressing resources using Brotli."""
-+
-+import subprocess
-+
-+__brotli_executable = None
-+
-+
-+def SetBrotliCommand(brotli):
-+ # brotli is a list. In production it contains the path to the Brotli executable.
-+ # During testing it contains [python, mock_brotli.py] for testing on Windows.
-+ global __brotli_executable
-+ __brotli_executable = brotli
-+
-+
-+def BrotliCompress(data):
-+ if not __brotli_executable:
-+ raise Exception('Add "use_brotli = true" to you GN grit(...) target ' +
-+ 'if you want to use brotli.')
-+ compress = subprocess.Popen(__brotli_executable + ['-', '-f'],
-+ stdin=subprocess.PIPE, stdout=subprocess.PIPE)
-+ return compress.communicate(data)[0]
-+
-+def IsInitialized():
-+ global __brotli_executable
-+ return __brotli_executable is not None
-diff --git a/tools/grit/grit/node/custom/__init__.py b/tools/grit/grit/node/custom/__init__.py
-new file mode 100644
-index 0000000000..e179cf7730
---- /dev/null
-+++ b/tools/grit/grit/node/custom/__init__.py
-@@ -0,0 +1,8 @@
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Package 'grit.node.custom'
-+'''
-+
-+pass
-diff --git a/tools/grit/grit/node/custom/filename.py b/tools/grit/grit/node/custom/filename.py
-new file mode 100644
-index 0000000000..55a27e58c1
---- /dev/null
-+++ b/tools/grit/grit/node/custom/filename.py
-@@ -0,0 +1,29 @@
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''A CustomType for filenames.'''
-+
-+from __future__ import print_function
-+
-+from grit import clique
-+from grit import lazy_re
-+
-+
-+class WindowsFilename(clique.CustomType):
-+ '''Validates that messages can be used as Windows filenames, and strips
-+ illegal characters out of translations.
-+ '''
-+
-+ BANNED = lazy_re.compile(r'\+|:|\/|\\\\|\*|\?|\"|\<|\>|\|')
-+
-+ def Validate(self, message):
-+ return not self.BANNED.search(message.GetPresentableContent())
-+
-+ def ValidateAndModify(self, lang, translation):
-+ is_ok = self.Validate(translation)
-+ self.ModifyEachTextPart(lang, translation)
-+ return is_ok
-+
-+ def ModifyTextPart(self, lang, text):
-+ return self.BANNED.sub(' ', text)
-diff --git a/tools/grit/grit/node/custom/filename_unittest.py b/tools/grit/grit/node/custom/filename_unittest.py
-new file mode 100644
-index 0000000000..8e2a6dd64a
---- /dev/null
-+++ b/tools/grit/grit/node/custom/filename_unittest.py
-@@ -0,0 +1,34 @@
-+#!/usr/bin/env python
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Unit tests for grit.node.custom.filename'''
-+
-+from __future__ import print_function
-+
-+import os
-+import sys
-+if __name__ == '__main__':
-+ sys.path.append(os.path.join(os.path.dirname(__file__), '../../..'))
-+
-+import unittest
-+from grit.node.custom import filename
-+from grit import clique
-+from grit import tclib
-+
-+
-+class WindowsFilenameUnittest(unittest.TestCase):
-+
-+ def testValidate(self):
-+ factory = clique.UberClique()
-+ msg = tclib.Message(text='Bingo bongo')
-+ c = factory.MakeClique(msg)
-+ c.SetCustomType(filename.WindowsFilename())
-+ translation = tclib.Translation(id=msg.GetId(), text='Bilingo bolongo:')
-+ c.AddTranslation(translation, 'fr')
-+ self.failUnless(c.MessageForLanguage('fr').GetRealContent() == 'Bilingo bolongo ')
-+
-+
-+if __name__ == '__main__':
-+ unittest.main()
-diff --git a/tools/grit/grit/node/empty.py b/tools/grit/grit/node/empty.py
-new file mode 100644
-index 0000000000..e19d2c4ddb
---- /dev/null
-+++ b/tools/grit/grit/node/empty.py
-@@ -0,0 +1,64 @@
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Container nodes that don't have any logic.
-+'''
-+
-+from __future__ import print_function
-+
-+from grit.node import base
-+from grit.node import include
-+from grit.node import message
-+from grit.node import misc
-+from grit.node import node_io
-+from grit.node import structure
-+
-+
-+class GroupingNode(base.Node):
-+ '''Base class for all the grouping elements (<structures>, <includes>,
-+ <messages> and <identifiers>).'''
-+ def DefaultAttributes(self):
-+ return {
-+ 'first_id' : '',
-+ 'comment' : '',
-+ 'fallback_to_english' : 'false',
-+ 'fallback_to_low_resolution' : 'false',
-+ }
-+
-+
-+class IncludesNode(GroupingNode):
-+ '''The <includes> element.'''
-+ def _IsValidChild(self, child):
-+ return isinstance(child, (include.IncludeNode, misc.IfNode, misc.PartNode))
-+
-+
-+class MessagesNode(GroupingNode):
-+ '''The <messages> element.'''
-+ def _IsValidChild(self, child):
-+ return isinstance(child, (message.MessageNode, misc.IfNode, misc.PartNode))
-+
-+
-+class StructuresNode(GroupingNode):
-+ '''The <structures> element.'''
-+ def _IsValidChild(self, child):
-+ return isinstance(child, (structure.StructureNode,
-+ misc.IfNode, misc.PartNode))
-+
-+
-+class TranslationsNode(base.Node):
-+ '''The <translations> element.'''
-+ def _IsValidChild(self, child):
-+ return isinstance(child, (node_io.FileNode, misc.IfNode, misc.PartNode))
-+
-+
-+class OutputsNode(base.Node):
-+ '''The <outputs> element.'''
-+ def _IsValidChild(self, child):
-+ return isinstance(child, (node_io.OutputNode, misc.IfNode, misc.PartNode))
-+
-+
-+class IdentifiersNode(GroupingNode):
-+ '''The <identifiers> element.'''
-+ def _IsValidChild(self, child):
-+ return isinstance(child, misc.IdentifierNode)
-diff --git a/tools/grit/grit/node/include.py b/tools/grit/grit/node/include.py
-new file mode 100644
-index 0000000000..b06b9889bb
---- /dev/null
-+++ b/tools/grit/grit/node/include.py
-@@ -0,0 +1,170 @@
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+"""Handling of the <include> element.
-+"""
-+
-+from __future__ import print_function
-+
-+import os
-+
-+from grit import util
-+import grit.format.html_inline
-+import grit.format.rc
-+from grit.format import minifier
-+from grit.node import base
-+
-+class IncludeNode(base.Node):
-+ """An <include> element."""
-+
-+ def __init__(self):
-+ super(IncludeNode, self).__init__()
-+
-+ # Cache flattened data so that we don't flatten the same file
-+ # multiple times.
-+ self._flattened_data = None
-+ # Also keep track of the last filename we flattened to, so we can
-+ # avoid doing it more than once.
-+ self._last_flat_filename = None
-+
-+ def _IsValidChild(self, child):
-+ return False
-+
-+ def _GetFlattenedData(
-+ self, allow_external_script=False, preprocess_only=False):
-+ if not self._flattened_data:
-+ filename = self.ToRealPath(self.GetInputPath())
-+ self._flattened_data = (
-+ grit.format.html_inline.InlineToString(filename, self,
-+ preprocess_only=preprocess_only,
-+ allow_external_script=allow_external_script))
-+ return self._flattened_data.encode('utf-8')
-+
-+ def MandatoryAttributes(self):
-+ return ['name', 'type', 'file']
-+
-+ def DefaultAttributes(self):
-+ """Attributes:
-+ translateable: False if the node has contents that should not be
-+ translated.
-+ preprocess: Takes the same code path as flattenhtml, but it
-+ disables any processing/inlining outside of <if>
-+ and <include>.
-+ compress: The format to compress the data with, e.g. 'gzip'
-+ or 'false' if data should not be compressed.
-+ skip_minify: If true, skips minifying the node's contents.
-+ skip_in_resource_map: If true, do not add to the resource map.
-+ """
-+ return {
-+ 'translateable': 'true',
-+ 'generateid': 'true',
-+ 'filenameonly': 'false',
-+ 'mkoutput': 'false',
-+ 'preprocess': 'false',
-+ 'flattenhtml': 'false',
-+ 'compress': 'default',
-+ 'allowexternalscript': 'false',
-+ 'relativepath': 'false',
-+ 'use_base_dir': 'true',
-+ 'skip_minify': 'false',
-+ 'skip_in_resource_map': 'false',
-+ }
-+
-+ def GetInputPath(self):
-+ # Do not mess with absolute paths, that would make them invalid.
-+ if os.path.isabs(os.path.expandvars(self.attrs['file'])):
-+ return self.attrs['file']
-+
-+ # We have no control over code that calls ToRealPath later, so convert
-+ # the path to be relative against our basedir.
-+ if self.attrs.get('use_base_dir', 'true') != 'true':
-+ # Normalize the directory path to use the appropriate OS separator.
-+ # GetBaseDir() may return paths\like\this or paths/like/this, since it is
-+ # read from the base_dir attribute in the grd file.
-+ norm_base_dir = util.normpath(self.GetRoot().GetBaseDir())
-+ return os.path.relpath(self.attrs['file'], norm_base_dir)
-+
-+ return self.attrs['file']
-+
-+ def FileForLanguage(self, lang, output_dir):
-+ """Returns the file for the specified language. This allows us to return
-+ different files for different language variants of the include file.
-+ """
-+ input_path = self.GetInputPath()
-+ if input_path is None:
-+ return None
-+
-+ return self.ToRealPath(input_path)
-+
-+ def GetDataPackValue(self, lang, encoding):
-+ '''Returns bytes or a str represenation for a data_pack entry.'''
-+ filename = self.ToRealPath(self.GetInputPath())
-+ if self.attrs['flattenhtml'] == 'true':
-+ allow_external_script = self.attrs['allowexternalscript'] == 'true'
-+ data = self._GetFlattenedData(allow_external_script=allow_external_script)
-+ elif self.attrs['preprocess'] == 'true':
-+ data = self._GetFlattenedData(preprocess_only=True)
-+ else:
-+ data = util.ReadFile(filename, util.BINARY)
-+
-+ if self.attrs['skip_minify'] != 'true':
-+ # Note that the minifier will only do anything if a minifier command
-+ # has been set in the command line.
-+ data = minifier.Minify(data, filename)
-+
-+ # Include does not care about the encoding, because it only returns binary
-+ # data.
-+ return self.CompressDataIfNeeded(data)
-+
-+ def Process(self, output_dir):
-+ """Rewrite file references to be base64 encoded data URLs. The new file
-+ will be written to output_dir and the name of the new file is returned."""
-+ filename = self.ToRealPath(self.GetInputPath())
-+ flat_filename = os.path.join(output_dir,
-+ self.attrs['name'] + '_' + os.path.basename(filename))
-+
-+ if self._last_flat_filename == flat_filename:
-+ return
-+
-+ with open(flat_filename, 'wb') as outfile:
-+ outfile.write(self._GetFlattenedData())
-+
-+ self._last_flat_filename = flat_filename
-+ return os.path.basename(flat_filename)
-+
-+ def GetHtmlResourceFilenames(self):
-+ """Returns a set of all filenames inlined by this file."""
-+ allow_external_script = self.attrs['allowexternalscript'] == 'true'
-+ return grit.format.html_inline.GetResourceFilenames(
-+ self.ToRealPath(self.GetInputPath()),
-+ self,
-+ allow_external_script=allow_external_script)
-+
-+ def IsResourceMapSource(self):
-+ skip = self.attrs.get('skip_in_resource_map', 'false') == 'true'
-+ return not skip
-+
-+ @staticmethod
-+ def Construct(parent, name, type, file, translateable=True,
-+ filenameonly=False, mkoutput=False, relativepath=False):
-+ """Creates a new node which is a child of 'parent', with attributes set
-+ by parameters of the same name.
-+ """
-+ # Convert types to appropriate strings
-+ translateable = util.BoolToString(translateable)
-+ filenameonly = util.BoolToString(filenameonly)
-+ mkoutput = util.BoolToString(mkoutput)
-+ relativepath = util.BoolToString(relativepath)
-+
-+ node = IncludeNode()
-+ node.StartParsing('include', parent)
-+ node.HandleAttribute('name', name)
-+ node.HandleAttribute('type', type)
-+ node.HandleAttribute('file', file)
-+ node.HandleAttribute('translateable', translateable)
-+ node.HandleAttribute('filenameonly', filenameonly)
-+ node.HandleAttribute('mkoutput', mkoutput)
-+ node.HandleAttribute('relativepath', relativepath)
-+ node.EndParsing()
-+ return node
-diff --git a/tools/grit/grit/node/include_unittest.py b/tools/grit/grit/node/include_unittest.py
-new file mode 100644
-index 0000000000..4c658f1ffe
---- /dev/null
-+++ b/tools/grit/grit/node/include_unittest.py
-@@ -0,0 +1,134 @@
-+#!/usr/bin/env python
-+# Copyright (c) 2013 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Unit tests for include.IncludeNode'''
-+
-+from __future__ import print_function
-+
-+import os
-+import sys
-+import unittest
-+import zlib
-+
-+if __name__ == '__main__':
-+ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
-+
-+from grit.node import misc
-+from grit.node import include
-+from grit.node import empty
-+from grit import util
-+
-+
-+def checkIsGzipped(filename, compress_attr):
-+ test_data_root = util.PathFromRoot('grit/testdata')
-+ root = util.ParseGrdForUnittest(
-+ '''
-+ <includes>
-+ <include name="TEST_TXT" file="%s" %s type="BINDATA"/>
-+ </includes>''' % (filename, compress_attr),
-+ base_dir=test_data_root)
-+ node, = root.GetChildrenOfType(include.IncludeNode)
-+ compressed = node.GetDataPackValue(lang='en', encoding=util.BINARY)
-+
-+ decompressed_data = zlib.decompress(compressed, 16 + zlib.MAX_WBITS)
-+ expected = util.ReadFile(os.path.join(test_data_root, filename), util.BINARY)
-+ return expected == decompressed_data
-+
-+
-+class IncludeNodeUnittest(unittest.TestCase):
-+ def testGetPath(self):
-+ root = misc.GritNode()
-+ root.StartParsing(u'grit', None)
-+ root.HandleAttribute(u'latest_public_release', u'0')
-+ root.HandleAttribute(u'current_release', u'1')
-+ root.HandleAttribute(u'base_dir', r'..\resource')
-+ release = misc.ReleaseNode()
-+ release.StartParsing(u'release', root)
-+ release.HandleAttribute(u'seq', u'1')
-+ root.AddChild(release)
-+ includes = empty.IncludesNode()
-+ includes.StartParsing(u'includes', release)
-+ release.AddChild(includes)
-+ include_node = include.IncludeNode()
-+ include_node.StartParsing(u'include', includes)
-+ include_node.HandleAttribute(u'file', r'flugel\kugel.pdf')
-+ includes.AddChild(include_node)
-+ root.EndParsing()
-+
-+ self.assertEqual(root.ToRealPath(include_node.GetInputPath()),
-+ util.normpath(
-+ os.path.join(r'../resource', r'flugel/kugel.pdf')))
-+
-+ def testGetPathNoBasedir(self):
-+ root = misc.GritNode()
-+ root.StartParsing(u'grit', None)
-+ root.HandleAttribute(u'latest_public_release', u'0')
-+ root.HandleAttribute(u'current_release', u'1')
-+ root.HandleAttribute(u'base_dir', r'..\resource')
-+ release = misc.ReleaseNode()
-+ release.StartParsing(u'release', root)
-+ release.HandleAttribute(u'seq', u'1')
-+ root.AddChild(release)
-+ includes = empty.IncludesNode()
-+ includes.StartParsing(u'includes', release)
-+ release.AddChild(includes)
-+ include_node = include.IncludeNode()
-+ include_node.StartParsing(u'include', includes)
-+ include_node.HandleAttribute(u'file', r'flugel\kugel.pdf')
-+ include_node.HandleAttribute(u'use_base_dir', u'false')
-+ includes.AddChild(include_node)
-+ root.EndParsing()
-+
-+ last_dir = os.path.basename(os.getcwd())
-+ expected_path = util.normpath(os.path.join(
-+ u'..', last_dir, u'flugel/kugel.pdf'))
-+ self.assertEqual(root.ToRealPath(include_node.GetInputPath()),
-+ expected_path)
-+
-+ def testCompressGzip(self):
-+ self.assertTrue(checkIsGzipped('test_text.txt', 'compress="gzip"'))
-+
-+ def testCompressGzipByDefault(self):
-+ self.assertTrue(checkIsGzipped('test_html.html', ''))
-+ self.assertTrue(checkIsGzipped('test_js.js', ''))
-+ self.assertTrue(checkIsGzipped('test_css.css', ''))
-+ self.assertTrue(checkIsGzipped('test_svg.svg', ''))
-+
-+ self.assertTrue(checkIsGzipped('test_html.html', 'compress="default"'))
-+ self.assertTrue(checkIsGzipped('test_js.js', 'compress="default"'))
-+ self.assertTrue(checkIsGzipped('test_css.css', 'compress="default"'))
-+ self.assertTrue(checkIsGzipped('test_svg.svg', 'compress="default"'))
-+
-+ def testSkipInResourceMap(self):
-+ root = util.ParseGrdForUnittest('''
-+ <includes>
-+ <include name="TEST1_TXT" file="test1_text.txt" type="BINDATA"/>
-+ <include name="TEST2_TXT" file="test1_text.txt" type="BINDATA"
-+ skip_in_resource_map="true"/>
-+ <include name="TEST3_TXT" file="test1_text.txt" type="BINDATA"
-+ skip_in_resource_map="false"/>
-+ </includes>''', base_dir = util.PathFromRoot('grit/testdata'))
-+ inc = root.GetChildrenOfType(include.IncludeNode)
-+ self.assertTrue(inc[0].IsResourceMapSource())
-+ self.assertFalse(inc[1].IsResourceMapSource())
-+ self.assertTrue(inc[2].IsResourceMapSource())
-+
-+ def testAcceptsPreprocess(self):
-+ root = util.ParseGrdForUnittest(
-+ '''
-+ <includes>
-+ <include name="PREPROCESS_TEST" file="preprocess_test.html"
-+ preprocess="true" compress="false" type="chrome_html"/>
-+ </includes>''',
-+ base_dir=util.PathFromRoot('grit/testdata'))
-+ inc, = root.GetChildrenOfType(include.IncludeNode)
-+ result = inc.GetDataPackValue(lang='en', encoding=util.BINARY)
-+ self.assertIn(b'should be kept', result)
-+ self.assertIn(b'in the middle...', result)
-+ self.assertNotIn(b'should be removed', result)
-+
-+
-+if __name__ == '__main__':
-+ unittest.main()
-diff --git a/tools/grit/grit/node/mapping.py b/tools/grit/grit/node/mapping.py
-new file mode 100644
-index 0000000000..6297f0b666
---- /dev/null
-+++ b/tools/grit/grit/node/mapping.py
-@@ -0,0 +1,60 @@
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Maps each node type to an implementation class.
-+When adding a new node type, you add to this mapping.
-+'''
-+
-+from __future__ import print_function
-+
-+from grit import exception
-+
-+from grit.node import empty
-+from grit.node import include
-+from grit.node import message
-+from grit.node import misc
-+from grit.node import node_io
-+from grit.node import structure
-+from grit.node import variant
-+
-+
-+_ELEMENT_TO_CLASS = {
-+ 'identifiers' : empty.IdentifiersNode,
-+ 'includes' : empty.IncludesNode,
-+ 'messages' : empty.MessagesNode,
-+ 'outputs' : empty.OutputsNode,
-+ 'structures' : empty.StructuresNode,
-+ 'translations' : empty.TranslationsNode,
-+ 'include' : include.IncludeNode,
-+ 'emit' : node_io.EmitNode,
-+ 'file' : node_io.FileNode,
-+ 'output' : node_io.OutputNode,
-+ 'ex' : message.ExNode,
-+ 'message' : message.MessageNode,
-+ 'ph' : message.PhNode,
-+ 'else' : misc.ElseNode,
-+ 'grit' : misc.GritNode,
-+ 'identifier' : misc.IdentifierNode,
-+ 'if' : misc.IfNode,
-+ 'part' : misc.PartNode,
-+ 'release' : misc.ReleaseNode,
-+ 'then' : misc.ThenNode,
-+ 'structure' : structure.StructureNode,
-+ 'skeleton' : variant.SkeletonNode,
-+}
-+
-+
-+def ElementToClass(name, typeattr):
-+ '''Maps an element to a class that handles the element.
-+
-+ Args:
-+ name: 'element' (the name of the element)
-+ typeattr: 'type' (the value of the type attribute, if present, else None)
-+
-+ Return:
-+ type
-+ '''
-+ if name not in _ELEMENT_TO_CLASS:
-+ raise exception.UnknownElement()
-+ return _ELEMENT_TO_CLASS[name]
-diff --git a/tools/grit/grit/node/message.py b/tools/grit/grit/node/message.py
-new file mode 100644
-index 0000000000..4fa83cf26b
---- /dev/null
-+++ b/tools/grit/grit/node/message.py
-@@ -0,0 +1,362 @@
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Handling of the <message> element.
-+'''
-+
-+from __future__ import print_function
-+
-+import re
-+
-+import six
-+
-+from grit.node import base
-+
-+from grit import clique
-+from grit import exception
-+from grit import lazy_re
-+from grit import tclib
-+from grit import util
-+
-+
-+# Matches exactly three dots ending a line or followed by whitespace.
-+_ELLIPSIS_PATTERN = lazy_re.compile(r'(?<!\.)\.\.\.(?=$|\s)')
-+_ELLIPSIS_SYMBOL = u'\u2026' # Ellipsis
-+
-+# Finds whitespace at the start and end of a string which can be multiline.
-+_WHITESPACE = lazy_re.compile(r'(?P<start>\s*)(?P<body>.+?)(?P<end>\s*)\Z',
-+ re.DOTALL | re.MULTILINE)
-+
-+# <ph> placeholder elements should contain the special character formatters
-+# used to format <ph> element content.
-+# Android format.
-+_ANDROID_FORMAT = (r'%[1-9]+\$'
-+ r'([-#+ 0,(]*)([0-9]+)?(\.[0-9]+)?'
-+ r'([bBhHsScCdoxXeEfgGaAtT%n])')
-+# Chrome l10n format.
-+_CHROME_FORMAT = r'\$+\d'
-+# Windows EWT numeric and GRIT %s %d formats.
-+_OTHER_FORMAT = r'%[0-9sd]'
-+
-+# Finds formatters that must be in a placeholder (<ph>) element.
-+_FORMATTERS = lazy_re.compile(
-+ '(%s)|(%s)|(%s)' % (_ANDROID_FORMAT, _CHROME_FORMAT, _OTHER_FORMAT))
-+_BAD_PLACEHOLDER_MSG = ('ERROR: Placeholder formatter found outside of <ph> '
-+ 'tag in message "%s" in %s.')
-+_INVALID_PH_CHAR_MSG = ('ERROR: Invalid format characters found in message '
-+ '"%s" <ph> tag in %s.')
-+
-+# Finds HTML tag tokens.
-+_HTMLTOKEN = lazy_re.compile(r'<[/]?[a-z][a-z0-9]*[^>]*>', re.I)
-+
-+# Finds HTML entities.
-+_HTMLENTITY = lazy_re.compile(r'&[^\s]*;')
-+
-+
-+class MessageNode(base.ContentNode):
-+ '''A <message> element.'''
-+
-+ # For splitting a list of things that can be separated by commas or
-+ # whitespace
-+ _SPLIT_RE = lazy_re.compile(r'\s*,\s*|\s+')
-+
-+ def __init__(self):
-+ super(MessageNode, self).__init__()
-+ # Valid after EndParsing, this is the MessageClique that contains the
-+ # source message and any translations of it that have been loaded.
-+ self.clique = None
-+
-+ # We don't send leading and trailing whitespace into the translation
-+ # console, but rather tack it onto the source message and any
-+ # translations when formatting them into RC files or what have you.
-+ self.ws_at_start = '' # Any whitespace characters at the start of the text
-+ self.ws_at_end = '' # --"-- at the end of the text
-+
-+ # A list of "shortcut groups" this message is in. We check to make sure
-+ # that shortcut keys (e.g. &J) within each shortcut group are unique.
-+ self.shortcut_groups_ = []
-+
-+ # Formatter-specific data used to control the output of individual strings.
-+ # formatter_data is a space separated list of C preprocessor-style
-+ # definitions. Names without values are given the empty string value.
-+ # Example: "foo=5 bar baz=100"
-+ self.formatter_data = {}
-+
-+ # Whether or not to convert ... -> U+2026 within Translate().
-+ self._replace_ellipsis = False
-+
-+ def _IsValidChild(self, child):
-+ return isinstance(child, (PhNode))
-+
-+ def _IsValidAttribute(self, name, value):
-+ if name not in [
-+ 'name', 'offset', 'translateable', 'desc', 'meaning',
-+ 'internal_comment', 'shortcut_groups', 'custom_type', 'validation_expr',
-+ 'use_name_for_id', 'sub_variable', 'formatter_data',
-+ 'is_accessibility_with_no_ui'
-+ ]:
-+ return False
-+ if (name in ('translateable', 'sub_variable') and
-+ value not in ['true', 'false']):
-+ return False
-+ return True
-+
-+ def SetReplaceEllipsis(self, value):
-+ r'''Sets whether to replace ... with \u2026.
-+ '''
-+ self._replace_ellipsis = value
-+
-+ def MandatoryAttributes(self):
-+ return ['name|offset']
-+
-+ def DefaultAttributes(self):
-+ return {
-+ 'custom_type': '',
-+ 'desc': '',
-+ 'formatter_data': '',
-+ 'internal_comment': '',
-+ 'is_accessibility_with_no_ui': 'false',
-+ 'meaning': '',
-+ 'shortcut_groups': '',
-+ 'sub_variable': 'false',
-+ 'translateable': 'true',
-+ 'use_name_for_id': 'false',
-+ 'validation_expr': '',
-+ }
-+
-+ def HandleAttribute(self, attrib, value):
-+ base.ContentNode.HandleAttribute(self, attrib, value)
-+ if attrib != 'formatter_data':
-+ return
-+
-+ # Parse value, a space-separated list of defines, into a dict.
-+ # Example: "foo=5 bar" -> {'foo':'5', 'bar':''}
-+ for item in value.split():
-+ name, _, val = item.partition('=')
-+ self.formatter_data[name] = val
-+
-+ def GetTextualIds(self):
-+ '''
-+ Returns the concatenation of the parent's node first_id and
-+ this node's offset if it has one, otherwise just call the
-+ superclass' implementation
-+ '''
-+ if 'offset' not in self.attrs:
-+ return super(MessageNode, self).GetTextualIds()
-+
-+ # we search for the first grouping node in the parents' list
-+ # to take care of the case where the first parent is an <if> node
-+ grouping_parent = self.parent
-+ import grit.node.empty
-+ while grouping_parent and not isinstance(grouping_parent,
-+ grit.node.empty.GroupingNode):
-+ grouping_parent = grouping_parent.parent
-+
-+ assert 'first_id' in grouping_parent.attrs
-+ return [grouping_parent.attrs['first_id'] + '_' + self.attrs['offset']]
-+
-+ def IsTranslateable(self):
-+ return self.attrs['translateable'] == 'true'
-+
-+ def EndParsing(self):
-+ super(MessageNode, self).EndParsing()
-+
-+ # Make the text (including placeholder references) and list of placeholders,
-+ # verify placeholder formats, then strip and store leading and trailing
-+ # whitespace and create the tclib.Message() and a clique to contain it.
-+
-+ text = ''
-+ placeholders = []
-+
-+ for item in self.mixed_content:
-+ if isinstance(item, six.string_types):
-+ # Not a <ph> element: fail if any <ph> formatters are detected.
-+ if _FORMATTERS.search(item):
-+ print(_BAD_PLACEHOLDER_MSG % (item, self.source))
-+ raise exception.PlaceholderNotInsidePhNode
-+ text += item
-+ else:
-+ # Extract the <ph> element components.
-+ presentation = item.attrs['name'].upper()
-+ text += presentation
-+ ex = ' ' # <ex> example element cdata if present.
-+ if len(item.children):
-+ ex = item.children[0].GetCdata()
-+ original = item.GetCdata()
-+
-+ # Sanity check the <ph> element content.
-+ cdata = original
-+ # Replace all HTML tag tokens in cdata.
-+ match = _HTMLTOKEN.search(cdata)
-+ while match:
-+ cdata = cdata.replace(match.group(0), '_')
-+ match = _HTMLTOKEN.search(cdata)
-+ # Replace all HTML entities in cdata.
-+ match = _HTMLENTITY.search(cdata)
-+ while match:
-+ cdata = cdata.replace(match.group(0), '_')
-+ match = _HTMLENTITY.search(cdata)
-+ # Remove first matching formatter from cdata.
-+ match = _FORMATTERS.search(cdata)
-+ if match:
-+ cdata = cdata.replace(match.group(0), '')
-+ # Fail if <ph> special chars remain in cdata.
-+ if re.search(r'[%\$]', cdata):
-+ message_id = self.attrs['name'] + ' ' + original;
-+ print(_INVALID_PH_CHAR_MSG % (message_id, self.source))
-+ raise exception.InvalidCharactersInsidePhNode
-+
-+ # Otherwise, accept this <ph> placeholder.
-+ placeholders.append(tclib.Placeholder(presentation, original, ex))
-+
-+ m = _WHITESPACE.match(text)
-+ if m:
-+ self.ws_at_start = m.group('start')
-+ self.ws_at_end = m.group('end')
-+ text = m.group('body')
-+
-+ self.shortcut_groups_ = self._SPLIT_RE.split(self.attrs['shortcut_groups'])
-+ self.shortcut_groups_ = [i for i in self.shortcut_groups_ if i != '']
-+
-+ description_or_id = self.attrs['desc']
-+ if description_or_id == '' and 'name' in self.attrs:
-+ description_or_id = 'ID: %s' % self.attrs['name']
-+
-+ assigned_id = None
-+ if self.attrs['use_name_for_id'] == 'true':
-+ assigned_id = self.attrs['name']
-+ message = tclib.Message(text=text, placeholders=placeholders,
-+ description=description_or_id,
-+ meaning=self.attrs['meaning'],
-+ assigned_id=assigned_id)
-+ self.InstallMessage(message)
-+
-+ def InstallMessage(self, message):
-+ '''Sets this node's clique from a tclib.Message instance.
-+
-+ Args:
-+ message: A tclib.Message.
-+ '''
-+ self.clique = self.UberClique().MakeClique(message, self.IsTranslateable())
-+ for group in self.shortcut_groups_:
-+ self.clique.AddToShortcutGroup(group)
-+ if self.attrs['custom_type'] != '':
-+ self.clique.SetCustomType(util.NewClassInstance(self.attrs['custom_type'],
-+ clique.CustomType))
-+ elif self.attrs['validation_expr'] != '':
-+ self.clique.SetCustomType(
-+ clique.OneOffCustomType(self.attrs['validation_expr']))
-+
-+ def SubstituteMessages(self, substituter):
-+ '''Applies substitution to this message.
-+
-+ Args:
-+ substituter: a grit.util.Substituter object.
-+ '''
-+ message = substituter.SubstituteMessage(self.clique.GetMessage())
-+ if message is not self.clique.GetMessage():
-+ self.InstallMessage(message)
-+
-+ def GetCliques(self):
-+ return [self.clique] if self.clique else []
-+
-+ def Translate(self, lang):
-+ '''Returns a translated version of this message.
-+ '''
-+ assert self.clique
-+ msg = self.clique.MessageForLanguage(lang,
-+ self.PseudoIsAllowed(),
-+ self.ShouldFallbackToEnglish()
-+ ).GetRealContent()
-+ if self._replace_ellipsis:
-+ msg = _ELLIPSIS_PATTERN.sub(_ELLIPSIS_SYMBOL, msg)
-+ # Always remove all byte order marks (\uFEFF) https://crbug.com/1033305
-+ msg = msg.replace(u'\uFEFF','')
-+ return msg.replace('[GRITLANGCODE]', lang)
-+
-+ def NameOrOffset(self):
-+ key = 'name' if 'name' in self.attrs else 'offset'
-+ return self.attrs[key]
-+
-+ def ExpandVariables(self):
-+ '''We always expand variables on Messages.'''
-+ return True
-+
-+ def GetDataPackValue(self, lang, encoding):
-+ '''Returns a str represenation for a data_pack entry.'''
-+ message = self.ws_at_start + self.Translate(lang) + self.ws_at_end
-+ return util.Encode(message, encoding)
-+
-+ def IsResourceMapSource(self):
-+ return True
-+
-+ @staticmethod
-+ def Construct(parent, message, name, desc='', meaning='', translateable=True):
-+ '''Constructs a new message node that is a child of 'parent', with the
-+ name, desc, meaning and translateable attributes set using the same-named
-+ parameters and the text of the message and any placeholders taken from
-+ 'message', which must be a tclib.Message() object.'''
-+ # Convert type to appropriate string
-+ translateable = 'true' if translateable else 'false'
-+
-+ node = MessageNode()
-+ node.StartParsing('message', parent)
-+ node.HandleAttribute('name', name)
-+ node.HandleAttribute('desc', desc)
-+ node.HandleAttribute('meaning', meaning)
-+ node.HandleAttribute('translateable', translateable)
-+
-+ items = message.GetContent()
-+ for ix, item in enumerate(items):
-+ if isinstance(item, six.string_types):
-+ # Ensure whitespace at front and back of message is correctly handled.
-+ if ix == 0:
-+ item = "'''" + item
-+ if ix == len(items) - 1:
-+ item = item + "'''"
-+
-+ node.AppendContent(item)
-+ else:
-+ phnode = PhNode()
-+ phnode.StartParsing('ph', node)
-+ phnode.HandleAttribute('name', item.GetPresentation())
-+ phnode.AppendContent(item.GetOriginal())
-+
-+ if len(item.GetExample()) and item.GetExample() != ' ':
-+ exnode = ExNode()
-+ exnode.StartParsing('ex', phnode)
-+ exnode.AppendContent(item.GetExample())
-+ exnode.EndParsing()
-+ phnode.AddChild(exnode)
-+
-+ phnode.EndParsing()
-+ node.AddChild(phnode)
-+
-+ node.EndParsing()
-+ return node
-+
-+
-+class PhNode(base.ContentNode):
-+ '''A <ph> element.'''
-+
-+ def _IsValidChild(self, child):
-+ return isinstance(child, ExNode)
-+
-+ def MandatoryAttributes(self):
-+ return ['name']
-+
-+ def EndParsing(self):
-+ super(PhNode, self).EndParsing()
-+ # We only allow a single example for each placeholder
-+ if len(self.children) > 1:
-+ raise exception.TooManyExamples()
-+
-+ def GetTextualIds(self):
-+ # The 'name' attribute is not an ID.
-+ return []
-+
-+
-+class ExNode(base.ContentNode):
-+ '''An <ex> element.'''
-+ pass
-diff --git a/tools/grit/grit/node/message_unittest.py b/tools/grit/grit/node/message_unittest.py
-new file mode 100644
-index 0000000000..7a4cbbedc2
---- /dev/null
-+++ b/tools/grit/grit/node/message_unittest.py
-@@ -0,0 +1,380 @@
-+#!/usr/bin/env python
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Unit tests for grit.node.message'''
-+
-+from __future__ import print_function
-+
-+import os
-+import sys
-+import unittest
-+
-+if __name__ == '__main__':
-+ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
-+
-+from grit import exception
-+from grit import tclib
-+from grit import util
-+from grit.node import message
-+
-+class MessageUnittest(unittest.TestCase):
-+ def testMessage(self):
-+ root = util.ParseGrdForUnittest('''
-+ <messages>
-+ <message name="IDS_GREETING"
-+ desc="Printed to greet the currently logged in user">
-+ Hello <ph name="USERNAME">%s<ex>Joi</ex></ph>, how are you doing today?
-+ </message>
-+ </messages>''')
-+ msg, = root.GetChildrenOfType(message.MessageNode)
-+ cliques = msg.GetCliques()
-+ content = cliques[0].GetMessage().GetPresentableContent()
-+ self.failUnless(content == 'Hello USERNAME, how are you doing today?')
-+
-+ def testMessageWithWhitespace(self):
-+ root = util.ParseGrdForUnittest("""\
-+ <messages>
-+ <message name="IDS_BLA" desc="">
-+ ''' Hello there <ph name="USERNAME">%s</ph> '''
-+ </message>
-+ </messages>""")
-+ msg, = root.GetChildrenOfType(message.MessageNode)
-+ content = msg.GetCliques()[0].GetMessage().GetPresentableContent()
-+ self.failUnless(content == 'Hello there USERNAME')
-+ self.failUnless(msg.ws_at_start == ' ')
-+ self.failUnless(msg.ws_at_end == ' ')
-+
-+ def testConstruct(self):
-+ msg = tclib.Message(text=" Hello USERNAME, how are you? BINGO\t\t",
-+ placeholders=[tclib.Placeholder('USERNAME', '%s', 'Joi'),
-+ tclib.Placeholder('BINGO', '%d', '11')])
-+ msg_node = message.MessageNode.Construct(None, msg, 'BINGOBONGO')
-+ self.failUnless(msg_node.children[0].name == 'ph')
-+ self.failUnless(msg_node.children[0].children[0].name == 'ex')
-+ self.failUnless(msg_node.children[0].children[0].GetCdata() == 'Joi')
-+ self.failUnless(msg_node.children[1].children[0].GetCdata() == '11')
-+ self.failUnless(msg_node.ws_at_start == ' ')
-+ self.failUnless(msg_node.ws_at_end == '\t\t')
-+
-+ def testUnicodeConstruct(self):
-+ text = u'Howdie \u00fe'
-+ msg = tclib.Message(text=text)
-+ msg_node = message.MessageNode.Construct(None, msg, 'BINGOBONGO')
-+ msg_from_node = msg_node.GetCdata()
-+ self.failUnless(msg_from_node == text)
-+
-+ def testFormatterData(self):
-+ root = util.ParseGrdForUnittest("""\
-+ <messages>
-+ <message name="IDS_BLA" desc="" formatter_data=" foo=123 bar qux=low">
-+ Text
-+ </message>
-+ </messages>""")
-+ msg, = root.GetChildrenOfType(message.MessageNode)
-+ expected_formatter_data = {
-+ 'foo': '123',
-+ 'bar': '',
-+ 'qux': 'low'}
-+
-+ # Can't use assertDictEqual, not available in Python 2.6, so do it
-+ # by hand.
-+ self.failUnlessEqual(len(expected_formatter_data),
-+ len(msg.formatter_data))
-+ for key in expected_formatter_data:
-+ self.failUnlessEqual(expected_formatter_data[key],
-+ msg.formatter_data[key])
-+
-+ def testReplaceEllipsis(self):
-+ root = util.ParseGrdForUnittest('''
-+ <messages>
-+ <message name="IDS_GREETING" desc="">
-+ A...B.... <ph name="PH">%s<ex>A</ex></ph>... B... C...
-+ </message>
-+ </messages>''')
-+ msg, = root.GetChildrenOfType(message.MessageNode)
-+ msg.SetReplaceEllipsis(True)
-+ content = msg.Translate('en')
-+ self.failUnlessEqual(u'A...B.... %s\u2026 B\u2026 C\u2026', content)
-+
-+ def testRemoveByteOrderMark(self):
-+ root = util.ParseGrdForUnittest(u'''
-+ <messages>
-+ <message name="IDS_HAS_BOM" desc="">
-+ \uFEFFThis\uFEFF i\uFEFFs OK\uFEFF
-+ </message>
-+ </messages>''')
-+ msg, = root.GetChildrenOfType(message.MessageNode)
-+ content = msg.Translate('en')
-+ self.failUnlessEqual(u'This is OK', content)
-+
-+ def testPlaceholderHasTooManyExamples(self):
-+ try:
-+ util.ParseGrdForUnittest("""\
-+ <messages>
-+ <message name="IDS_FOO" desc="foo">
-+ Hi <ph name="NAME">$1<ex>Joi</ex><ex>Joy</ex></ph>
-+ </message>
-+ </messages>""")
-+ except exception.TooManyExamples:
-+ return
-+ self.fail('Should have gotten exception')
-+
-+ def testPlaceholderHasInvalidName(self):
-+ try:
-+ util.ParseGrdForUnittest("""\
-+ <messages>
-+ <message name="IDS_FOO" desc="foo">
-+ Hi <ph name="ABC!">$1</ph>
-+ </message>
-+ </messages>""")
-+ except exception.InvalidPlaceholderName:
-+ return
-+ self.fail('Should have gotten exception')
-+
-+ def testChromeLocalizedFormatIsInsidePhNode(self):
-+ try:
-+ util.ParseGrdForUnittest("""\
-+ <messages>
-+ <message name="IDS_CHROME_L10N" desc="l10n format">
-+ This message is missing the ph node: $1
-+ </message>
-+ </messages>""")
-+ except exception.PlaceholderNotInsidePhNode:
-+ return
-+ self.fail('Should have gotten exception')
-+
-+ def testAndroidStringFormatIsInsidePhNode(self):
-+ try:
-+ util.ParseGrdForUnittest("""\
-+ <messages>
-+ <message name="IDS_ANDROID" desc="string format">
-+ This message is missing a ph node: %1$s
-+ </message>
-+ </messages>""")
-+ except exception.PlaceholderNotInsidePhNode:
-+ return
-+ self.fail('Should have gotten exception')
-+
-+ def testAndroidIntegerFormatIsInsidePhNode(self):
-+ try:
-+ util.ParseGrdForUnittest("""\
-+ <messages>
-+ <message name="IDS_ANDROID" desc="integer format">
-+ This message is missing a ph node: %2$d
-+ </message>
-+ </messages>""")
-+ except exception.PlaceholderNotInsidePhNode:
-+ return
-+ self.fail('Should have gotten exception')
-+
-+ def testAndroidIntegerWidthFormatIsInsidePhNode(self):
-+ try:
-+ util.ParseGrdForUnittest("""\
-+ <messages>
-+ <message name="IDS_ANDROID" desc="integer width format">
-+ This message is missing a ph node: %2$3d
-+ </message>
-+ </messages>""")
-+ except exception.PlaceholderNotInsidePhNode:
-+ return
-+ self.fail('Should have gotten exception')
-+
-+ def testValidAndroidIntegerWidthFormatInPhNode(self):
-+ try:
-+ util.ParseGrdForUnittest("""\
-+ <messages>
-+ <message name="IDS_ANDROID_WIDTH">
-+ <ph name="VALID">%2$3d<ex>042</ex></ph>
-+ </message>
-+ </messages>""")
-+ except:
-+ self.fail('Should not have gotten exception')
-+
-+ def testAndroidFloatFormatIsInsidePhNode(self):
-+ try:
-+ util.ParseGrdForUnittest("""\
-+ <messages>
-+ <message name="IDS_ANDROID" desc="float number format">
-+ This message is missing a ph node: %3$4.5f
-+ </message>
-+ </messages>""")
-+ except exception.PlaceholderNotInsidePhNode:
-+ return
-+ self.fail('Should have gotten exception')
-+
-+ def testGritStringFormatIsInsidePhNode(self):
-+ try:
-+ util.ParseGrdForUnittest("""\
-+ <messages>
-+ <message name="IDS_GRIT_STRING" desc="grit string format">
-+ This message is missing the ph node: %s
-+ </message>
-+ </messages>""")
-+ except exception.PlaceholderNotInsidePhNode:
-+ return
-+ self.fail('Should have gotten exception')
-+
-+ def testGritIntegerFormatIsInsidePhNode(self):
-+ try:
-+ util.ParseGrdForUnittest("""\
-+ <messages>
-+ <message name="IDS_GRIT_INTEGER" desc="grit integer format">
-+ This message is missing the ph node: %d
-+ </message>
-+ </messages>""")
-+ except exception.PlaceholderNotInsidePhNode:
-+ return
-+ self.fail('Should have gotten exception')
-+
-+ def testWindowsETWIntegerFormatIsInsidePhNode(self):
-+ try:
-+ util.ParseGrdForUnittest("""\
-+ <messages>
-+ <message name="IDS_WINDOWS_ETW" desc="ETW tracing integer">
-+ This message is missing the ph node: %1
-+ </message>
-+ </messages>""")
-+ except exception.PlaceholderNotInsidePhNode:
-+ return
-+ self.fail('Should have gotten exception')
-+
-+ def testValidMultipleFormattersInsidePhNodes(self):
-+ root = util.ParseGrdForUnittest("""\
-+ <messages>
-+ <message name="IDS_MULTIPLE_FORMATTERS">
-+ <ph name="ERROR_COUNT">%1$d<ex>1</ex></ph> error, <ph name="WARNING_COUNT">%2$d<ex>1</ex></ph> warning
-+ </message>
-+ </messages>""")
-+ msg, = root.GetChildrenOfType(message.MessageNode)
-+ cliques = msg.GetCliques()
-+ content = cliques[0].GetMessage().GetPresentableContent()
-+ self.failUnless(content == 'ERROR_COUNT error, WARNING_COUNT warning')
-+
-+ def testMultipleFormattersAreInsidePhNodes(self):
-+ failed = True
-+ try:
-+ util.ParseGrdForUnittest("""\
-+ <messages>
-+ <message name="IDS_MULTIPLE_FORMATTERS">
-+ %1$d error, %2$d warning
-+ </message>
-+ </messages>""")
-+ except exception.PlaceholderNotInsidePhNode:
-+ failed = False
-+ if failed:
-+ self.fail('Should have gotten exception')
-+ return
-+
-+ failed = True
-+ try:
-+ util.ParseGrdForUnittest("""\
-+ <messages>
-+ <message name="IDS_MULTIPLE_FORMATTERS">
-+ <ph name="ERROR_COUNT">%1$d<ex>1</ex></ph> error, %2$d warning
-+ </message>
-+ </messages>""")
-+ except exception.PlaceholderNotInsidePhNode:
-+ failed = False
-+ if failed:
-+ self.fail('Should have gotten exception')
-+ return
-+
-+ failed = True
-+ try:
-+ util.ParseGrdForUnittest("""\
-+ <messages>
-+ <message name="IDS_MULTIPLE_FORMATTERS">
-+ <ph name="INVALID">%1$d %2$d</ph>
-+ </message>
-+ </messages>""")
-+ except exception.InvalidCharactersInsidePhNode:
-+ failed = False
-+ if failed:
-+ self.fail('Should have gotten exception')
-+ return
-+
-+ def testValidHTMLFormatInsidePhNode(self):
-+ try:
-+ util.ParseGrdForUnittest("""\
-+ <messages>
-+ <message name="IDS_HTML">
-+ <ph name="VALID">&lt;span&gt;$1&lt;/span&gt;<ex>1</ex></ph>
-+ </message>
-+ </messages>""")
-+ except:
-+ self.fail('Should not have gotten exception')
-+
-+ def testValidHTMLWithAttributesFormatInsidePhNode(self):
-+ try:
-+ util.ParseGrdForUnittest("""\
-+ <messages>
-+ <message name="IDS_HTML_ATTRIBUTE">
-+ <ph name="VALID">&lt;span attribute="js:$this %"&gt;$2&lt;/span&gt;<ex>2</ex></ph>
-+ </message>
-+ </messages>""")
-+ except:
-+ self.fail('Should not have gotten exception')
-+
-+ def testValidHTMLEntityFormatInsidePhNode(self):
-+ try:
-+ util.ParseGrdForUnittest("""\
-+ <messages>
-+ <message name="IDS_ENTITY">
-+ <ph name="VALID">&gt;%1$d&lt;<ex>1</ex></ph>
-+ </message>
-+ </messages>""")
-+ except:
-+ self.fail('Should not have gotten exception')
-+
-+ def testValidMultipleDollarFormatInsidePhNode(self):
-+ try:
-+ util.ParseGrdForUnittest("""\
-+ <messages>
-+ <message name="IDS_DOLLARS" desc="l10n dollars format">
-+ <ph name="VALID">$$1</ph>
-+ </message>
-+ </messages>""")
-+ except:
-+ self.fail('Should not have gotten exception')
-+
-+ def testInvalidDollarCharacterInsidePhNode(self):
-+ try:
-+ util.ParseGrdForUnittest("""\
-+ <messages>
-+ <message name="IDS_BAD_DOLLAR">
-+ <ph name="INVALID">%1$d $</ph>
-+ </message>
-+ </messages>""")
-+ except exception.InvalidCharactersInsidePhNode:
-+ return
-+ self.fail('Should have gotten exception')
-+
-+ def testInvalidPercentCharacterInsidePhNode(self):
-+ try:
-+ util.ParseGrdForUnittest("""\
-+ <messages>
-+ <message name="IDS_BAD_PERCENT">
-+ <ph name="INVALID">%1$d %</ph>
-+ </message>
-+ </messages>""")
-+ except exception.InvalidCharactersInsidePhNode:
-+ return
-+ self.fail('Should have gotten exception')
-+
-+ def testInvalidMixedFormatCharactersInsidePhNode(self):
-+ try:
-+ util.ParseGrdForUnittest("""\
-+ <messages>
-+ <message name="IDS_MIXED_FORMATS">
-+ <ph name="INVALID">%1$2</ph>
-+ </message>
-+ </messages>""")
-+ except exception.InvalidCharactersInsidePhNode:
-+ return
-+ self.fail('Should have gotten exception')
-+
-+
-+if __name__ == '__main__':
-+ unittest.main()
-diff --git a/tools/grit/grit/node/misc.py b/tools/grit/grit/node/misc.py
-new file mode 100644
-index 0000000000..2d8b06d6a5
---- /dev/null
-+++ b/tools/grit/grit/node/misc.py
-@@ -0,0 +1,707 @@
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+"""Miscellaneous node types.
-+"""
-+
-+from __future__ import print_function
-+
-+import os.path
-+import re
-+import sys
-+
-+import six
-+
-+from grit import constants
-+from grit import exception
-+from grit import util
-+from grit.extern import FP
-+from grit.node import base
-+from grit.node import message
-+from grit.node import node_io
-+
-+
-+# Python 3 doesn't have long() as int() works everywhere. But we really do need
-+# the long() behavior on Python 2 as our ids are much too large for int().
-+try:
-+ long
-+except NameError:
-+ long = int
-+
-+
-+# RTL languages
-+# TODO(jennyz): remove this fixed set of RTL language array
-+# now that generic expand_variable code exists.
-+_RTL_LANGS = (
-+ 'ar', # Arabic
-+ 'fa', # Farsi
-+ 'iw', # Hebrew
-+ 'ks', # Kashmiri
-+ 'ku', # Kurdish
-+ 'ps', # Pashto
-+ 'ur', # Urdu
-+ 'yi', # Yiddish
-+)
-+
-+
-+def _ReadFirstIdsFromFile(filename, defines):
-+ """Read the starting resource id values from |filename|. We also
-+ expand variables of the form <(FOO) based on defines passed in on
-+ the command line.
-+
-+ Returns a tuple, the absolute path of SRCDIR followed by the
-+ first_ids dictionary.
-+ """
-+ first_ids_dict = eval(util.ReadFile(filename, 'utf-8'))
-+ src_root_dir = os.path.abspath(os.path.join(os.path.dirname(filename),
-+ first_ids_dict['SRCDIR']))
-+
-+ def ReplaceVariable(matchobj):
-+ for key, value in defines.items():
-+ if matchobj.group(1) == key:
-+ return value
-+ return ''
-+
-+ renames = []
-+ for grd_filename in first_ids_dict:
-+ new_grd_filename = re.sub(r'<\(([A-Za-z_]+)\)', ReplaceVariable,
-+ grd_filename)
-+ if new_grd_filename != grd_filename:
-+ abs_grd_filename = os.path.abspath(new_grd_filename)
-+ if abs_grd_filename[:len(src_root_dir)] != src_root_dir:
-+ new_grd_filename = os.path.basename(abs_grd_filename)
-+ else:
-+ new_grd_filename = abs_grd_filename[len(src_root_dir) + 1:]
-+ new_grd_filename = new_grd_filename.replace('\\', '/')
-+ renames.append((grd_filename, new_grd_filename))
-+
-+ for grd_filename, new_grd_filename in renames:
-+ first_ids_dict[new_grd_filename] = first_ids_dict[grd_filename]
-+ del(first_ids_dict[grd_filename])
-+
-+ return (src_root_dir, first_ids_dict)
-+
-+
-+def _ComputeIds(root, predetermined_tids):
-+ """Returns a dict of textual id -> numeric id for all nodes in root.
-+
-+ IDs are mostly assigned sequentially, but will vary based on:
-+ * first_id node attribute (from first_ids_file)
-+ * hash of textual id (if not first_id is defined)
-+ * offset node attribute
-+ * whether the textual id matches a system id
-+ * whether the node generates its own ID via GetId()
-+
-+ Args:
-+ predetermined_tids: Dict of textual id -> numeric id to use in return dict.
-+ """
-+ from grit.node import empty, include, misc, structure
-+
-+ ids = {} # Maps numeric id to textual id
-+ tids = {} # Maps textual id to numeric id
-+ id_reasons = {} # Maps numeric id to text id and a human-readable explanation
-+ group = None
-+ last_id = None
-+ predetermined_ids = {value: key
-+ for key, value in predetermined_tids.items()}
-+
-+ for item in root:
-+ if isinstance(item, empty.GroupingNode):
-+ # Note: this won't work if any GroupingNode can be contained inside
-+ # another.
-+ group = item
-+ last_id = None
-+ continue
-+
-+ assert not item.GetTextualIds() or isinstance(item,
-+ (include.IncludeNode, message.MessageNode,
-+ misc.IdentifierNode, structure.StructureNode))
-+
-+ # Resources that use the RES protocol don't need
-+ # any numerical ids generated, so we skip them altogether.
-+ # This is accomplished by setting the flag 'generateid' to false
-+ # in the GRD file.
-+ if item.attrs.get('generateid', 'true') == 'false':
-+ continue
-+
-+ for tid in item.GetTextualIds():
-+ if util.SYSTEM_IDENTIFIERS.match(tid):
-+ # Don't emit a new ID for predefined IDs
-+ continue
-+
-+ if tid in tids:
-+ continue
-+
-+ if predetermined_tids and tid in predetermined_tids:
-+ id = predetermined_tids[tid]
-+ reason = "from predetermined_tids map"
-+
-+ # Some identifier nodes can provide their own id,
-+ # and we use that id in the generated header in that case.
-+ elif hasattr(item, 'GetId') and item.GetId():
-+ id = long(item.GetId())
-+ reason = 'returned by GetId() method'
-+
-+ elif ('offset' in item.attrs and group and
-+ group.attrs.get('first_id', '') != ''):
-+ offset_text = item.attrs['offset']
-+ parent_text = group.attrs['first_id']
-+
-+ try:
-+ offset_id = long(offset_text)
-+ except ValueError:
-+ offset_id = tids[offset_text]
-+
-+ try:
-+ parent_id = long(parent_text)
-+ except ValueError:
-+ parent_id = tids[parent_text]
-+
-+ id = parent_id + offset_id
-+ reason = 'first_id %d + offset %d' % (parent_id, offset_id)
-+
-+ # We try to allocate IDs sequentially for blocks of items that might
-+ # be related, for instance strings in a stringtable (as their IDs might be
-+ # used e.g. as IDs for some radio buttons, in which case the IDs must
-+ # be sequential).
-+ #
-+ # We do this by having the first item in a section store its computed ID
-+ # (computed from a fingerprint) in its parent object. Subsequent children
-+ # of the same parent will then try to get IDs that sequentially follow
-+ # the currently stored ID (on the parent) and increment it.
-+ elif last_id is None:
-+ # First check if the starting ID is explicitly specified by the parent.
-+ if group and group.attrs.get('first_id', '') != '':
-+ id = long(group.attrs['first_id'])
-+ reason = "from parent's first_id attribute"
-+ else:
-+ # Automatically generate the ID based on the first clique from the
-+ # first child of the first child node of our parent (i.e. when we
-+ # first get to this location in the code).
-+
-+ # According to
-+ # http://msdn.microsoft.com/en-us/library/t2zechd4(VS.71).aspx
-+ # the safe usable range for resource IDs in Windows is from decimal
-+ # 101 to 0x7FFF.
-+
-+ id = FP.UnsignedFingerPrint(tid)
-+ id = id % (0x7FFF - 101) + 101
-+ reason = 'chosen by random fingerprint -- use first_id to override'
-+
-+ last_id = id
-+ else:
-+ id = last_id = last_id + 1
-+ reason = 'sequentially assigned'
-+
-+ reason = "%s (%s)" % (tid, reason)
-+ # Don't fail when 'offset' is specified, as the base and the 0th
-+ # offset will have the same ID.
-+ if id in id_reasons and not 'offset' in item.attrs:
-+ raise exception.IdRangeOverlap('ID %d was assigned to both %s and %s.'
-+ % (id, id_reasons[id], reason))
-+
-+ if id < 101:
-+ print('WARNING: Numeric resource IDs should be greater than 100 to\n'
-+ 'avoid conflicts with system-defined resource IDs.')
-+
-+ if tid not in predetermined_tids and id in predetermined_ids:
-+ raise exception.IdRangeOverlap('ID %d overlaps between %s and %s'
-+ % (id, tid, predetermined_ids[tid]))
-+
-+ ids[id] = tid
-+ tids[tid] = id
-+ id_reasons[id] = reason
-+
-+ return tids
-+
-+class SplicingNode(base.Node):
-+ """A node whose children should be considered to be at the same level as
-+ its siblings for most purposes. This includes <if> and <part> nodes.
-+ """
-+
-+ def _IsValidChild(self, child):
-+ assert self.parent, '<%s> node should never be root.' % self.name
-+ if isinstance(child, SplicingNode):
-+ return True # avoid O(n^2) behavior
-+ return self.parent._IsValidChild(child)
-+
-+
-+class IfNode(SplicingNode):
-+ """A node for conditional inclusion of resources.
-+ """
-+
-+ def MandatoryAttributes(self):
-+ return ['expr']
-+
-+ def _IsValidChild(self, child):
-+ return (isinstance(child, (ThenNode, ElseNode)) or
-+ super(IfNode, self)._IsValidChild(child))
-+
-+ def EndParsing(self):
-+ children = self.children
-+ self.if_then_else = False
-+ if any(isinstance(node, (ThenNode, ElseNode)) for node in children):
-+ if (len(children) != 2 or not isinstance(children[0], ThenNode) or
-+ not isinstance(children[1], ElseNode)):
-+ raise exception.UnexpectedChild(
-+ '<if> element must be <if><then>...</then><else>...</else></if>')
-+ self.if_then_else = True
-+
-+ def ActiveChildren(self):
-+ cond = self.EvaluateCondition(self.attrs['expr'])
-+ if self.if_then_else:
-+ return self.children[0 if cond else 1].ActiveChildren()
-+ else:
-+ # Equivalent to having all children inside <then> with an empty <else>
-+ return super(IfNode, self).ActiveChildren() if cond else []
-+
-+
-+class ThenNode(SplicingNode):
-+ """A <then> node. Can only appear directly inside an <if> node."""
-+ pass
-+
-+
-+class ElseNode(SplicingNode):
-+ """An <else> node. Can only appear directly inside an <if> node."""
-+ pass
-+
-+
-+class PartNode(SplicingNode):
-+ """A node for inclusion of sub-grd (*.grp) files.
-+ """
-+
-+ def __init__(self):
-+ super(PartNode, self).__init__()
-+ self.started_inclusion = False
-+
-+ def MandatoryAttributes(self):
-+ return ['file']
-+
-+ def _IsValidChild(self, child):
-+ return self.started_inclusion and super(PartNode, self)._IsValidChild(child)
-+
-+
-+class ReleaseNode(base.Node):
-+ """The <release> element."""
-+
-+ def _IsValidChild(self, child):
-+ from grit.node import empty
-+ return isinstance(child, (empty.IncludesNode, empty.MessagesNode,
-+ empty.StructuresNode, empty.IdentifiersNode))
-+
-+ def _IsValidAttribute(self, name, value):
-+ return (
-+ (name == 'seq' and int(value) <= self.GetRoot().GetCurrentRelease()) or
-+ name == 'allow_pseudo'
-+ )
-+
-+ def MandatoryAttributes(self):
-+ return ['seq']
-+
-+ def DefaultAttributes(self):
-+ return { 'allow_pseudo' : 'true' }
-+
-+
-+class GritNode(base.Node):
-+ """The <grit> root element."""
-+
-+ def __init__(self):
-+ super(GritNode, self).__init__()
-+ self.output_language = ''
-+ self.defines = {}
-+ self.substituter = None
-+ self.target_platform = sys.platform
-+ self.whitelist_support = False
-+ self._predetermined_ids_file = None
-+ self._id_map = None # Dict of textual_id -> numeric_id.
-+
-+ def _IsValidChild(self, child):
-+ from grit.node import empty
-+ return isinstance(child, (ReleaseNode, empty.TranslationsNode,
-+ empty.OutputsNode))
-+
-+ def _IsValidAttribute(self, name, value):
-+ if name not in ['base_dir', 'first_ids_file', 'source_lang_id',
-+ 'latest_public_release', 'current_release',
-+ 'enc_check', 'tc_project', 'grit_version',
-+ 'output_all_resource_defines']:
-+ return False
-+ if name in ['latest_public_release', 'current_release'] and value.strip(
-+ '0123456789') != '':
-+ return False
-+ return True
-+
-+ def MandatoryAttributes(self):
-+ return ['latest_public_release', 'current_release']
-+
-+ def DefaultAttributes(self):
-+ return {
-+ 'base_dir' : '.',
-+ 'first_ids_file': '',
-+ 'grit_version': 1,
-+ 'source_lang_id' : 'en',
-+ 'enc_check' : constants.ENCODING_CHECK,
-+ 'tc_project' : 'NEED_TO_SET_tc_project_ATTRIBUTE',
-+ }
-+
-+ def EndParsing(self):
-+ super(GritNode, self).EndParsing()
-+ if (int(self.attrs['latest_public_release'])
-+ > int(self.attrs['current_release'])):
-+ raise exception.Parsing('latest_public_release cannot have a greater '
-+ 'value than current_release')
-+
-+ self.ValidateUniqueIds()
-+
-+ # Add the encoding check if it's not present (should ensure that it's always
-+ # present in all .grd files generated by GRIT). If it's present, assert if
-+ # it's not correct.
-+ if 'enc_check' not in self.attrs or self.attrs['enc_check'] == '':
-+ self.attrs['enc_check'] = constants.ENCODING_CHECK
-+ else:
-+ assert self.attrs['enc_check'] == constants.ENCODING_CHECK, (
-+ 'Are you sure your .grd file is in the correct encoding (UTF-8)?')
-+
-+ def ValidateUniqueIds(self):
-+ """Validate that 'name' attribute is unique in all nodes in this tree
-+ except for nodes that are children of <if> nodes.
-+ """
-+ unique_names = {}
-+ duplicate_names = []
-+ # To avoid false positives from mutually exclusive <if> clauses, check
-+ # against whatever the output condition happens to be right now.
-+ # TODO(benrg): do something better.
-+ for node in self.ActiveDescendants():
-+ if node.attrs.get('generateid', 'true') == 'false':
-+ continue # Duplication not relevant in that case
-+
-+ for node_id in node.GetTextualIds():
-+ if util.SYSTEM_IDENTIFIERS.match(node_id):
-+ continue # predefined IDs are sometimes used more than once
-+
-+ if node_id in unique_names and node_id not in duplicate_names:
-+ duplicate_names.append(node_id)
-+ unique_names[node_id] = 1
-+
-+ if len(duplicate_names):
-+ raise exception.DuplicateKey(', '.join(duplicate_names))
-+
-+
-+ def GetCurrentRelease(self):
-+ """Returns the current release number."""
-+ return int(self.attrs['current_release'])
-+
-+ def GetLatestPublicRelease(self):
-+ """Returns the latest public release number."""
-+ return int(self.attrs['latest_public_release'])
-+
-+ def GetSourceLanguage(self):
-+ """Returns the language code of the source language."""
-+ return self.attrs['source_lang_id']
-+
-+ def GetTcProject(self):
-+ """Returns the name of this project in the TranslationConsole, or
-+ 'NEED_TO_SET_tc_project_ATTRIBUTE' if it is not defined."""
-+ return self.attrs['tc_project']
-+
-+ def SetOwnDir(self, dir):
-+ """Informs the 'grit' element of the directory the file it is in resides.
-+ This allows it to calculate relative paths from the input file, which is
-+ what we desire (rather than from the current path).
-+
-+ Args:
-+ dir: r'c:\bla'
-+
-+ Return:
-+ None
-+ """
-+ assert dir
-+ self.base_dir = os.path.normpath(os.path.join(dir, self.attrs['base_dir']))
-+
-+ def GetBaseDir(self):
-+ """Returns the base directory, relative to the working directory. To get
-+ the base directory as set in the .grd file, use GetOriginalBaseDir()
-+ """
-+ if hasattr(self, 'base_dir'):
-+ return self.base_dir
-+ else:
-+ return self.GetOriginalBaseDir()
-+
-+ def GetOriginalBaseDir(self):
-+ """Returns the base directory, as set in the .grd file.
-+ """
-+ return self.attrs['base_dir']
-+
-+ def IsWhitelistSupportEnabled(self):
-+ return self.whitelist_support
-+
-+ def SetWhitelistSupportEnabled(self, whitelist_support):
-+ self.whitelist_support = whitelist_support
-+
-+ def GetInputFiles(self):
-+ """Returns the list of files that are read to produce the output."""
-+
-+ # Importing this here avoids a circular dependency in the imports.
-+ # pylint: disable-msg=C6204
-+ from grit.node import include
-+ from grit.node import misc
-+ from grit.node import structure
-+ from grit.node import variant
-+
-+ # Check if the input is required for any output configuration.
-+ input_files = set()
-+ # Collect even inactive PartNodes since they affect ID assignments.
-+ for node in self:
-+ if isinstance(node, misc.PartNode):
-+ input_files.add(self.ToRealPath(node.GetInputPath()))
-+
-+ old_output_language = self.output_language
-+ for lang, ctx, fallback in self.GetConfigurations():
-+ self.SetOutputLanguage(lang or self.GetSourceLanguage())
-+ self.SetOutputContext(ctx)
-+ self.SetFallbackToDefaultLayout(fallback)
-+
-+ for node in self.ActiveDescendants():
-+ if isinstance(node, (node_io.FileNode, include.IncludeNode,
-+ structure.StructureNode, variant.SkeletonNode)):
-+ input_path = node.GetInputPath()
-+ if input_path is not None:
-+ input_files.add(self.ToRealPath(input_path))
-+
-+ # If it's a flattened node, grab inlined resources too.
-+ if ((node.name == 'structure' or node.name == 'include')
-+ and node.attrs['flattenhtml'] == 'true'):
-+ if node.name == 'structure':
-+ node.RunPreSubstitutionGatherer()
-+ input_files.update(node.GetHtmlResourceFilenames())
-+
-+ self.SetOutputLanguage(old_output_language)
-+ return sorted(input_files)
-+
-+ def GetFirstIdsFile(self):
-+ """Returns a usable path to the first_ids file, if set, otherwise
-+ returns None.
-+
-+ The first_ids_file attribute is by default relative to the
-+ base_dir of the .grd file, but may be prefixed by GRIT_DIR/,
-+ which makes it relative to the directory of grit.py
-+ (e.g. GRIT_DIR/../gritsettings/resource_ids).
-+ """
-+ if not self.attrs['first_ids_file']:
-+ return None
-+
-+ path = self.attrs['first_ids_file']
-+ GRIT_DIR_PREFIX = 'GRIT_DIR'
-+ if (path.startswith(GRIT_DIR_PREFIX)
-+ and path[len(GRIT_DIR_PREFIX)] in ['/', '\\']):
-+ return util.PathFromRoot(path[len(GRIT_DIR_PREFIX) + 1:])
-+ else:
-+ return self.ToRealPath(path)
-+
-+ def GetOutputFiles(self):
-+ """Returns the list of <output> nodes that are descendants of this node's
-+ <outputs> child and are not enclosed by unsatisfied <if> conditionals.
-+ """
-+ for child in self.children:
-+ if child.name == 'outputs':
-+ return [node for node in child.ActiveDescendants()
-+ if node.name == 'output']
-+ raise exception.MissingElement()
-+
-+ def GetConfigurations(self):
-+ """Returns the distinct (language, context, fallback_to_default_layout)
-+ triples from the output nodes.
-+ """
-+ return set((n.GetLanguage(), n.GetContext(), n.GetFallbackToDefaultLayout())
-+ for n in self.GetOutputFiles())
-+
-+ def GetSubstitutionMessages(self):
-+ """Returns the list of <message sub_variable="true"> nodes."""
-+ return [n for n in self.ActiveDescendants()
-+ if isinstance(n, message.MessageNode)
-+ and n.attrs['sub_variable'] == 'true']
-+
-+ def SetOutputLanguage(self, output_language):
-+ """Set the output language. Prepares substitutions.
-+
-+ The substitutions are reset every time the language is changed.
-+ They include messages designated as variables, and language codes for html
-+ and rc files.
-+
-+ Args:
-+ output_language: a two-letter language code (eg: 'en', 'ar'...) or ''
-+ """
-+ if not output_language:
-+ # We do not specify the output language for .grh files,
-+ # so we get an empty string as the default.
-+ # The value should match grit.clique.MessageClique.source_language.
-+ output_language = self.GetSourceLanguage()
-+ if output_language != self.output_language:
-+ self.output_language = output_language
-+ self.substituter = None # force recalculate
-+
-+ def SetOutputContext(self, output_context):
-+ self.output_context = output_context
-+ self.substituter = None # force recalculate
-+
-+ def SetFallbackToDefaultLayout(self, fallback_to_default_layout):
-+ self.fallback_to_default_layout = fallback_to_default_layout
-+ self.substituter = None # force recalculate
-+
-+ def SetDefines(self, defines):
-+ self.defines = defines
-+ self.substituter = None # force recalculate
-+
-+ def SetTargetPlatform(self, target_platform):
-+ self.target_platform = target_platform
-+
-+ def GetSubstituter(self):
-+ if self.substituter is None:
-+ self.substituter = util.Substituter()
-+ self.substituter.AddMessages(self.GetSubstitutionMessages(),
-+ self.output_language)
-+ if self.output_language in _RTL_LANGS:
-+ direction = 'dir="RTL"'
-+ else:
-+ direction = 'dir="LTR"'
-+ self.substituter.AddSubstitutions({
-+ 'GRITLANGCODE': self.output_language,
-+ 'GRITDIR': direction,
-+ })
-+ from grit.format import rc # avoid circular dep
-+ rc.RcSubstitutions(self.substituter, self.output_language)
-+ return self.substituter
-+
-+ def AssignFirstIds(self, filename_or_stream, defines):
-+ """Assign first ids to each grouping node based on values from the
-+ first_ids file (if specified on the <grit> node).
-+ """
-+ assert self._id_map is None, 'AssignFirstIds() after InitializeIds()'
-+ # If the input is a stream, then we're probably in a unit test and
-+ # should skip this step.
-+ if not isinstance(filename_or_stream, six.string_types):
-+ return
-+
-+ # Nothing to do if the first_ids_filename attribute isn't set.
-+ first_ids_filename = self.GetFirstIdsFile()
-+ if not first_ids_filename:
-+ return
-+
-+ src_root_dir, first_ids = _ReadFirstIdsFromFile(first_ids_filename,
-+ defines)
-+ from grit.node import empty
-+ for node in self.Preorder():
-+ if isinstance(node, empty.GroupingNode):
-+ abs_filename = os.path.abspath(filename_or_stream)
-+ if abs_filename[:len(src_root_dir)] != src_root_dir:
-+ filename = os.path.basename(filename_or_stream)
-+ else:
-+ filename = abs_filename[len(src_root_dir) + 1:]
-+ filename = filename.replace('\\', '/')
-+
-+ if node.attrs['first_id'] != '':
-+ raise Exception(
-+ "Don't set the first_id attribute when using the first_ids_file "
-+ "attribute on the <grit> node, update %s instead." %
-+ first_ids_filename)
-+
-+ try:
-+ id_list = first_ids[filename][node.name]
-+ except KeyError as e:
-+ print('-' * 78)
-+ print('Resource id not set for %s (%s)!' % (filename, node.name))
-+ print('Please update %s to include an entry for %s. See the '
-+ 'comments in resource_ids for information on why you need to '
-+ 'update that file.' % (first_ids_filename, filename))
-+ print('-' * 78)
-+ raise e
-+
-+ try:
-+ node.attrs['first_id'] = str(id_list.pop(0))
-+ except IndexError as e:
-+ raise Exception('Please update %s and add a first id for %s (%s).'
-+ % (first_ids_filename, filename, node.name))
-+
-+ def GetIdMap(self):
-+ '''Return a dictionary mapping textual ids to numeric ids.'''
-+ return self._id_map
-+
-+ def SetPredeterminedIdsFile(self, predetermined_ids_file):
-+ assert self._id_map is None, (
-+ 'SetPredeterminedIdsFile() after InitializeIds()')
-+ self._predetermined_ids_file = predetermined_ids_file
-+
-+ def InitializeIds(self):
-+ '''Initializes the text ID -> numeric ID mapping.'''
-+ predetermined_id_map = {}
-+ if self._predetermined_ids_file:
-+ with open(self._predetermined_ids_file) as f:
-+ for line in f:
-+ tid, nid = line.split()
-+ predetermined_id_map[tid] = int(nid)
-+ self._id_map = _ComputeIds(self, predetermined_id_map)
-+
-+ def RunGatherers(self, debug=False):
-+ '''Call RunPreSubstitutionGatherer() on every node of the tree, then apply
-+ substitutions, then call RunPostSubstitutionGatherer() on every node.
-+
-+ The substitutions step requires that the output language has been set.
-+ Locally, get the Substitution messages and add them to the substituter.
-+ Also add substitutions for language codes in the Rc.
-+
-+ Args:
-+ debug: will print information while running gatherers.
-+ '''
-+ for node in self.ActiveDescendants():
-+ if hasattr(node, 'RunPreSubstitutionGatherer'):
-+ with node:
-+ node.RunPreSubstitutionGatherer(debug=debug)
-+
-+ assert self.output_language
-+ self.SubstituteMessages(self.GetSubstituter())
-+
-+ for node in self.ActiveDescendants():
-+ if hasattr(node, 'RunPostSubstitutionGatherer'):
-+ with node:
-+ node.RunPostSubstitutionGatherer(debug=debug)
-+
-+
-+class IdentifierNode(base.Node):
-+ """A node for specifying identifiers that should appear in the resource
-+ header file, and be unique amongst all other resource identifiers, but don't
-+ have any other attributes or reference any resources.
-+ """
-+
-+ def MandatoryAttributes(self):
-+ return ['name']
-+
-+ def DefaultAttributes(self):
-+ return { 'comment' : '', 'id' : '', 'systemid': 'false' }
-+
-+ def GetId(self):
-+ """Returns the id of this identifier if it has one, None otherwise
-+ """
-+ if 'id' in self.attrs:
-+ return self.attrs['id']
-+ return None
-+
-+ def EndParsing(self):
-+ """Handles system identifiers."""
-+ super(IdentifierNode, self).EndParsing()
-+ if self.attrs['systemid'] == 'true':
-+ util.SetupSystemIdentifiers((self.attrs['name'],))
-+
-+ @staticmethod
-+ def Construct(parent, name, id, comment, systemid='false'):
-+ """Creates a new node which is a child of 'parent', with attributes set
-+ by parameters of the same name.
-+ """
-+ node = IdentifierNode()
-+ node.StartParsing('identifier', parent)
-+ node.HandleAttribute('name', name)
-+ node.HandleAttribute('id', id)
-+ node.HandleAttribute('comment', comment)
-+ node.HandleAttribute('systemid', systemid)
-+ node.EndParsing()
-+ return node
-diff --git a/tools/grit/grit/node/misc_unittest.py b/tools/grit/grit/node/misc_unittest.py
-new file mode 100644
-index 0000000000..c192b096f4
---- /dev/null
-+++ b/tools/grit/grit/node/misc_unittest.py
-@@ -0,0 +1,590 @@
-+#!/usr/bin/env python
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Unit tests for misc.GritNode'''
-+
-+from __future__ import print_function
-+
-+import contextlib
-+import os
-+import sys
-+import tempfile
-+import unittest
-+
-+if __name__ == '__main__':
-+ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
-+
-+from six import StringIO
-+
-+from grit import grd_reader
-+import grit.exception
-+from grit import util
-+from grit.format import rc
-+from grit.format import rc_header
-+from grit.node import misc
-+
-+
-+@contextlib.contextmanager
-+def _MakeTempPredeterminedIdsFile(content):
-+ """Write the |content| string to a temporary file.
-+
-+ The temporary file must be deleted by the caller.
-+
-+ Example:
-+ with _MakeTempPredeterminedIdsFile('foo') as path:
-+ ...
-+ os.remove(path)
-+
-+ Args:
-+ content: The string to write.
-+
-+ Yields:
-+ The name of the temporary file.
-+ """
-+ with tempfile.NamedTemporaryFile(mode='w', delete=False) as f:
-+ f.write(content)
-+ f.flush()
-+ f.close()
-+ yield f.name
-+
-+
-+class GritNodeUnittest(unittest.TestCase):
-+ def testUniqueNameAttribute(self):
-+ try:
-+ restree = grd_reader.Parse(
-+ util.PathFromRoot('grit/testdata/duplicate-name-input.xml'))
-+ self.fail('Expected parsing exception because of duplicate names.')
-+ except grit.exception.Parsing:
-+ pass # Expected case
-+
-+ def testReadFirstIdsFromFile(self):
-+ test_resource_ids = os.path.join(os.path.dirname(__file__), '..',
-+ 'testdata', 'resource_ids')
-+ base_dir = os.path.dirname(test_resource_ids)
-+
-+ src_dir, id_dict = misc._ReadFirstIdsFromFile(
-+ test_resource_ids,
-+ {
-+ 'FOO': os.path.join(base_dir, 'bar'),
-+ 'SHARED_INTERMEDIATE_DIR': os.path.join(base_dir,
-+ 'out/Release/obj/gen'),
-+ })
-+ self.assertEqual({}, id_dict.get('bar/file.grd', None))
-+ self.assertEqual({},
-+ id_dict.get('out/Release/obj/gen/devtools/devtools.grd', None))
-+
-+ src_dir, id_dict = misc._ReadFirstIdsFromFile(
-+ test_resource_ids,
-+ {
-+ 'SHARED_INTERMEDIATE_DIR': '/outside/src_dir',
-+ })
-+ self.assertEqual({}, id_dict.get('devtools.grd', None))
-+
-+ # Verifies that GetInputFiles() returns the correct list of files
-+ # corresponding to ChromeScaledImage nodes when assets are missing.
-+ def testGetInputFilesChromeScaledImage(self):
-+ chrome_html_path = util.PathFromRoot('grit/testdata/chrome_html.html')
-+ xml = '''<?xml version="1.0" encoding="utf-8"?>
-+ <grit latest_public_release="0" current_release="1">
-+ <outputs>
-+ <output filename="default.pak" type="data_package" context="default_100_percent" />
-+ <output filename="special.pak" type="data_package" context="special_100_percent" fallback_to_default_layout="false" />
-+ </outputs>
-+ <release seq="1">
-+ <structures fallback_to_low_resolution="true">
-+ <structure type="chrome_scaled_image" name="IDR_A" file="a.png" />
-+ <structure type="chrome_scaled_image" name="IDR_B" file="b.png" />
-+ <structure type="chrome_html" name="HTML_FILE1" file="%s" flattenhtml="true" />
-+ </structures>
-+ </release>
-+ </grit>''' % chrome_html_path
-+
-+ grd = grd_reader.Parse(StringIO(xml),
-+ util.PathFromRoot('grit/testdata'))
-+ expected = ['chrome_html.html', 'default_100_percent/a.png',
-+ 'default_100_percent/b.png', 'included_sample.html',
-+ 'special_100_percent/a.png']
-+ actual = [os.path.relpath(path, util.PathFromRoot('grit/testdata')) for
-+ path in grd.GetInputFiles()]
-+ # Convert path separator for Windows paths.
-+ actual = [path.replace('\\', '/') for path in actual]
-+ self.assertEquals(expected, actual)
-+
-+ # Verifies that GetInputFiles() returns the correct list of files
-+ # when files include other files.
-+ def testGetInputFilesFromIncludes(self):
-+ chrome_html_path = util.PathFromRoot('grit/testdata/chrome_html.html')
-+ xml = '''<?xml version="1.0" encoding="utf-8"?>
-+ <grit latest_public_release="0" current_release="1">
-+ <outputs>
-+ <output filename="default.pak" type="data_package" context="default_100_percent" />
-+ <output filename="special.pak" type="data_package" context="special_100_percent" fallback_to_default_layout="false" />
-+ </outputs>
-+ <release seq="1">
-+ <includes>
-+ <include name="IDR_TESTDATA_CHROME_HTML" file="%s" flattenhtml="true"
-+ allowexternalscript="true" type="BINDATA" />
-+ </includes>
-+ </release>
-+ </grit>''' % chrome_html_path
-+
-+ grd = grd_reader.Parse(StringIO(xml), util.PathFromRoot('grit/testdata'))
-+ expected = ['chrome_html.html', 'included_sample.html']
-+ actual = [os.path.relpath(path, util.PathFromRoot('grit/testdata')) for
-+ path in grd.GetInputFiles()]
-+ # Convert path separator for Windows paths.
-+ actual = [path.replace('\\', '/') for path in actual]
-+ self.assertEquals(expected, actual)
-+
-+ def testNonDefaultEntry(self):
-+ grd = util.ParseGrdForUnittest('''
-+ <messages>
-+ <message name="IDS_A" desc="foo">bar</message>
-+ <if expr="lang == 'fr'">
-+ <message name="IDS_B" desc="foo">bar</message>
-+ </if>
-+ </messages>''')
-+ grd.SetOutputLanguage('fr')
-+ output = ''.join(rc_header.Format(grd, 'fr', '.'))
-+ self.assertIn('#define IDS_A 2378\n#define IDS_B 2379', output)
-+
-+ def testExplicitFirstIdOverlaps(self):
-+ # second first_id will overlap preexisting range
-+ self.assertRaises(grit.exception.IdRangeOverlap,
-+ util.ParseGrdForUnittest, '''
-+ <includes first_id="300" comment="bingo">
-+ <include type="gif" name="ID_LOGO" file="images/logo.gif" />
-+ <include type="gif" name="ID_LOGO2" file="images/logo2.gif" />
-+ </includes>
-+ <messages first_id="301">
-+ <message name="IDS_GREETING" desc="Printed to greet the currently logged in user">
-+ Hello <ph name="USERNAME">%s<ex>Joi</ex></ph>, how are you doing today?
-+ </message>
-+ <message name="IDS_SMURFGEBURF">Frubegfrums</message>
-+ </messages>''')
-+
-+ def testImplicitOverlapsPreexisting(self):
-+ # second message in <messages> will overlap preexisting range
-+ self.assertRaises(grit.exception.IdRangeOverlap,
-+ util.ParseGrdForUnittest, '''
-+ <includes first_id="301" comment="bingo">
-+ <include type="gif" name="ID_LOGO" file="images/logo.gif" />
-+ <include type="gif" name="ID_LOGO2" file="images/logo2.gif" />
-+ </includes>
-+ <messages first_id="300">
-+ <message name="IDS_GREETING" desc="Printed to greet the currently logged in user">
-+ Hello <ph name="USERNAME">%s<ex>Joi</ex></ph>, how are you doing today?
-+ </message>
-+ <message name="IDS_SMURFGEBURF">Frubegfrums</message>
-+ </messages>''')
-+
-+ def testPredeterminedIds(self):
-+ with _MakeTempPredeterminedIdsFile('IDS_A 101\nIDS_B 102') as ids_file:
-+ grd = util.ParseGrdForUnittest('''
-+ <includes first_id="300" comment="bingo">
-+ <include type="gif" name="IDS_B" file="images/logo.gif" />
-+ </includes>
-+ <messages first_id="10000">
-+ <message name="IDS_GREETING" desc="Printed to greet the currently logged in user">
-+ Hello <ph name="USERNAME">%s<ex>Joi</ex></ph>, how are you doing today?
-+ </message>
-+ <message name="IDS_A">
-+ Bongo!
-+ </message>
-+ </messages>''', predetermined_ids_file=ids_file)
-+ output = rc_header.FormatDefines(grd)
-+ self.assertEqual(('#define IDS_B 102\n'
-+ '#define IDS_GREETING 10000\n'
-+ '#define IDS_A 101\n'), ''.join(output))
-+ os.remove(ids_file)
-+
-+ def testPredeterminedIdsOverlap(self):
-+ with _MakeTempPredeterminedIdsFile('ID_LOGO 10000') as ids_file:
-+ self.assertRaises(grit.exception.IdRangeOverlap,
-+ util.ParseGrdForUnittest, '''
-+ <includes first_id="300" comment="bingo">
-+ <include type="gif" name="ID_LOGO" file="images/logo.gif" />
-+ </includes>
-+ <messages first_id="10000">
-+ <message name="IDS_GREETING" desc="Printed to greet the currently logged in user">
-+ Hello <ph name="USERNAME">%s<ex>Joi</ex></ph>, how are you doing today?
-+ </message>
-+ <message name="IDS_BONGO">
-+ Bongo!
-+ </message>
-+ </messages>''', predetermined_ids_file=ids_file)
-+ os.remove(ids_file)
-+
-+
-+class IfNodeUnittest(unittest.TestCase):
-+ def testIffyness(self):
-+ grd = grd_reader.Parse(StringIO('''
-+ <grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
-+ <release seq="3">
-+ <messages>
-+ <if expr="'bingo' in defs">
-+ <message name="IDS_BINGO">
-+ Bingo!
-+ </message>
-+ </if>
-+ <if expr="'hello' in defs">
-+ <message name="IDS_HELLO">
-+ Hello!
-+ </message>
-+ </if>
-+ <if expr="lang == 'fr' or 'FORCE_FRENCH' in defs">
-+ <message name="IDS_HELLO" internal_comment="French version">
-+ Good morning
-+ </message>
-+ </if>
-+ <if expr="is_win">
-+ <message name="IDS_ISWIN">is_win</message>
-+ </if>
-+ </messages>
-+ </release>
-+ </grit>'''), dir='.')
-+
-+ messages_node = grd.children[0].children[0]
-+ bingo_message = messages_node.children[0].children[0]
-+ hello_message = messages_node.children[1].children[0]
-+ french_message = messages_node.children[2].children[0]
-+ is_win_message = messages_node.children[3].children[0]
-+
-+ self.assertTrue(bingo_message.name == 'message')
-+ self.assertTrue(hello_message.name == 'message')
-+ self.assertTrue(french_message.name == 'message')
-+
-+ grd.SetOutputLanguage('fr')
-+ grd.SetDefines({'hello': '1'})
-+ active = set(grd.ActiveDescendants())
-+ self.failUnless(bingo_message not in active)
-+ self.failUnless(hello_message in active)
-+ self.failUnless(french_message in active)
-+
-+ grd.SetOutputLanguage('en')
-+ grd.SetDefines({'bingo': 1})
-+ active = set(grd.ActiveDescendants())
-+ self.failUnless(bingo_message in active)
-+ self.failUnless(hello_message not in active)
-+ self.failUnless(french_message not in active)
-+
-+ grd.SetOutputLanguage('en')
-+ grd.SetDefines({'FORCE_FRENCH': '1', 'bingo': '1'})
-+ active = set(grd.ActiveDescendants())
-+ self.failUnless(bingo_message in active)
-+ self.failUnless(hello_message not in active)
-+ self.failUnless(french_message in active)
-+
-+ grd.SetOutputLanguage('en')
-+ grd.SetDefines({})
-+ self.failUnless(grd.target_platform == sys.platform)
-+ grd.SetTargetPlatform('darwin')
-+ active = set(grd.ActiveDescendants())
-+ self.failUnless(is_win_message not in active)
-+ grd.SetTargetPlatform('win32')
-+ active = set(grd.ActiveDescendants())
-+ self.failUnless(is_win_message in active)
-+
-+ def testElsiness(self):
-+ grd = util.ParseGrdForUnittest('''
-+ <messages>
-+ <if expr="True">
-+ <then> <message name="IDS_YES1"></message> </then>
-+ <else> <message name="IDS_NO1"></message> </else>
-+ </if>
-+ <if expr="True">
-+ <then> <message name="IDS_YES2"></message> </then>
-+ <else> </else>
-+ </if>
-+ <if expr="True">
-+ <then> </then>
-+ <else> <message name="IDS_NO2"></message> </else>
-+ </if>
-+ <if expr="True">
-+ <then> </then>
-+ <else> </else>
-+ </if>
-+ <if expr="False">
-+ <then> <message name="IDS_NO3"></message> </then>
-+ <else> <message name="IDS_YES3"></message> </else>
-+ </if>
-+ <if expr="False">
-+ <then> <message name="IDS_NO4"></message> </then>
-+ <else> </else>
-+ </if>
-+ <if expr="False">
-+ <then> </then>
-+ <else> <message name="IDS_YES4"></message> </else>
-+ </if>
-+ <if expr="False">
-+ <then> </then>
-+ <else> </else>
-+ </if>
-+ </messages>''')
-+ included = [msg.attrs['name'] for msg in grd.ActiveDescendants()
-+ if msg.name == 'message']
-+ self.assertEqual(['IDS_YES1', 'IDS_YES2', 'IDS_YES3', 'IDS_YES4'], included)
-+
-+ def testIffynessWithOutputNodes(self):
-+ grd = grd_reader.Parse(StringIO('''
-+ <grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
-+ <outputs>
-+ <output filename="uncond1.rc" type="rc_data" />
-+ <if expr="lang == 'fr' or 'hello' in defs">
-+ <output filename="only_fr.adm" type="adm" />
-+ <output filename="only_fr.plist" type="plist" />
-+ </if>
-+ <if expr="lang == 'ru'">
-+ <output filename="doc.html" type="document" />
-+ </if>
-+ <output filename="uncond2.adm" type="adm" />
-+ <output filename="iftest.h" type="rc_header">
-+ <emit emit_type='prepend'></emit>
-+ </output>
-+ </outputs>
-+ </grit>'''), dir='.')
-+
-+ outputs_node = grd.children[0]
-+ uncond1_output = outputs_node.children[0]
-+ only_fr_adm_output = outputs_node.children[1].children[0]
-+ only_fr_plist_output = outputs_node.children[1].children[1]
-+ doc_output = outputs_node.children[2].children[0]
-+ uncond2_output = outputs_node.children[0]
-+ self.assertTrue(uncond1_output.name == 'output')
-+ self.assertTrue(only_fr_adm_output.name == 'output')
-+ self.assertTrue(only_fr_plist_output.name == 'output')
-+ self.assertTrue(doc_output.name == 'output')
-+ self.assertTrue(uncond2_output.name == 'output')
-+
-+ grd.SetOutputLanguage('ru')
-+ grd.SetDefines({'hello': '1'})
-+ outputs = [output.GetFilename() for output in grd.GetOutputFiles()]
-+ self.assertEquals(
-+ outputs,
-+ ['uncond1.rc', 'only_fr.adm', 'only_fr.plist', 'doc.html',
-+ 'uncond2.adm', 'iftest.h'])
-+
-+ grd.SetOutputLanguage('ru')
-+ grd.SetDefines({'bingo': '2'})
-+ outputs = [output.GetFilename() for output in grd.GetOutputFiles()]
-+ self.assertEquals(
-+ outputs,
-+ ['uncond1.rc', 'doc.html', 'uncond2.adm', 'iftest.h'])
-+
-+ grd.SetOutputLanguage('fr')
-+ grd.SetDefines({'hello': '1'})
-+ outputs = [output.GetFilename() for output in grd.GetOutputFiles()]
-+ self.assertEquals(
-+ outputs,
-+ ['uncond1.rc', 'only_fr.adm', 'only_fr.plist', 'uncond2.adm',
-+ 'iftest.h'])
-+
-+ grd.SetOutputLanguage('en')
-+ grd.SetDefines({'bingo': '1'})
-+ outputs = [output.GetFilename() for output in grd.GetOutputFiles()]
-+ self.assertEquals(outputs, ['uncond1.rc', 'uncond2.adm', 'iftest.h'])
-+
-+ grd.SetOutputLanguage('fr')
-+ grd.SetDefines({'bingo': '1'})
-+ outputs = [output.GetFilename() for output in grd.GetOutputFiles()]
-+ self.assertNotEquals(outputs, ['uncond1.rc', 'uncond2.adm', 'iftest.h'])
-+
-+ def testChildrenAccepted(self):
-+ grd_reader.Parse(StringIO(r'''<?xml version="1.0"?>
-+ <grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
-+ <release seq="3">
-+ <includes>
-+ <if expr="'bingo' in defs">
-+ <include type="gif" name="ID_LOGO2" file="images/logo2.gif" />
-+ </if>
-+ <if expr="'bingo' in defs">
-+ <if expr="'hello' in defs">
-+ <include type="gif" name="ID_LOGO2" file="images/logo2.gif" />
-+ </if>
-+ </if>
-+ </includes>
-+ <structures>
-+ <if expr="'bingo' in defs">
-+ <structure type="dialog" name="IDD_ABOUTBOX" file="grit\test\data\klonk.rc" encoding="utf-16" />
-+ </if>
-+ <if expr="'bingo' in defs">
-+ <if expr="'hello' in defs">
-+ <structure type="dialog" name="IDD_ABOUTBOX" file="grit\test\data\klonk.rc" encoding="utf-16" />
-+ </if>
-+ </if>
-+ </structures>
-+ <messages>
-+ <if expr="'bingo' in defs">
-+ <message name="IDS_BINGO">Bingo!</message>
-+ </if>
-+ <if expr="'bingo' in defs">
-+ <if expr="'hello' in defs">
-+ <message name="IDS_BINGO">Bingo!</message>
-+ </if>
-+ </if>
-+ </messages>
-+ </release>
-+ <translations>
-+ <if expr="'bingo' in defs">
-+ <file lang="nl" path="nl_translations.xtb" />
-+ </if>
-+ <if expr="'bingo' in defs">
-+ <if expr="'hello' in defs">
-+ <file lang="nl" path="nl_translations.xtb" />
-+ </if>
-+ </if>
-+ </translations>
-+ </grit>'''), dir='.')
-+
-+ def testIfBadChildrenNesting(self):
-+ # includes
-+ xml = StringIO(r'''<?xml version="1.0"?>
-+ <grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
-+ <release seq="3">
-+ <includes>
-+ <if expr="'bingo' in defs">
-+ <structure type="dialog" name="IDD_ABOUTBOX" file="grit\test\data\klonk.rc" encoding="utf-16" />
-+ </if>
-+ </includes>
-+ </release>
-+ </grit>''')
-+ self.assertRaises(grit.exception.UnexpectedChild, grd_reader.Parse, xml)
-+ # messages
-+ xml = StringIO(r'''<?xml version="1.0"?>
-+ <grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
-+ <release seq="3">
-+ <messages>
-+ <if expr="'bingo' in defs">
-+ <structure type="dialog" name="IDD_ABOUTBOX" file="grit\test\data\klonk.rc" encoding="utf-16" />
-+ </if>
-+ </messages>
-+ </release>
-+ </grit>''')
-+ self.assertRaises(grit.exception.UnexpectedChild, grd_reader.Parse, xml)
-+ # structures
-+ xml = StringIO('''<?xml version="1.0"?>
-+ <grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
-+ <release seq="3">
-+ <structures>
-+ <if expr="'bingo' in defs">
-+ <message name="IDS_BINGO">Bingo!</message>
-+ </if>
-+ </structures>
-+ </release>
-+ </grit>''')
-+ # translations
-+ self.assertRaises(grit.exception.UnexpectedChild, grd_reader.Parse, xml)
-+ xml = StringIO('''<?xml version="1.0"?>
-+ <grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
-+ <translations>
-+ <if expr="'bingo' in defs">
-+ <message name="IDS_BINGO">Bingo!</message>
-+ </if>
-+ </translations>
-+ </grit>''')
-+ self.assertRaises(grit.exception.UnexpectedChild, grd_reader.Parse, xml)
-+ # same with nesting
-+ xml = StringIO(r'''<?xml version="1.0"?>
-+ <grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
-+ <release seq="3">
-+ <includes>
-+ <if expr="'bingo' in defs">
-+ <if expr="'hello' in defs">
-+ <structure type="dialog" name="IDD_ABOUTBOX" file="grit\test\data\klonk.rc" encoding="utf-16" />
-+ </if>
-+ </if>
-+ </includes>
-+ </release>
-+ </grit>''')
-+ self.assertRaises(grit.exception.UnexpectedChild, grd_reader.Parse, xml)
-+ xml = StringIO(r'''<?xml version="1.0"?>
-+ <grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
-+ <release seq="3">
-+ <messages>
-+ <if expr="'bingo' in defs">
-+ <if expr="'hello' in defs">
-+ <structure type="dialog" name="IDD_ABOUTBOX" file="grit\test\data\klonk.rc" encoding="utf-16" />
-+ </if>
-+ </if>
-+ </messages>
-+ </release>
-+ </grit>''')
-+ self.assertRaises(grit.exception.UnexpectedChild, grd_reader.Parse, xml)
-+ xml = StringIO('''<?xml version="1.0"?>
-+ <grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
-+ <release seq="3">
-+ <structures>
-+ <if expr="'bingo' in defs">
-+ <if expr="'hello' in defs">
-+ <message name="IDS_BINGO">Bingo!</message>
-+ </if>
-+ </if>
-+ </structures>
-+ </release>
-+ </grit>''')
-+ self.assertRaises(grit.exception.UnexpectedChild, grd_reader.Parse, xml)
-+ xml = StringIO('''<?xml version="1.0"?>
-+ <grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
-+ <translations>
-+ <if expr="'bingo' in defs">
-+ <if expr="'hello' in defs">
-+ <message name="IDS_BINGO">Bingo!</message>
-+ </if>
-+ </if>
-+ </translations>
-+ </grit>''')
-+ self.assertRaises(grit.exception.UnexpectedChild, grd_reader.Parse, xml)
-+
-+
-+class ReleaseNodeUnittest(unittest.TestCase):
-+ def testPseudoControl(self):
-+ grd = grd_reader.Parse(StringIO('''<?xml version="1.0" encoding="UTF-8"?>
-+ <grit latest_public_release="1" source_lang_id="en-US" current_release="2" base_dir=".">
-+ <release seq="1" allow_pseudo="false">
-+ <messages>
-+ <message name="IDS_HELLO">
-+ Hello
-+ </message>
-+ </messages>
-+ <structures>
-+ <structure type="dialog" name="IDD_ABOUTBOX" encoding="utf-16" file="klonk.rc" />
-+ </structures>
-+ </release>
-+ <release seq="2">
-+ <messages>
-+ <message name="IDS_BINGO">
-+ Bingo
-+ </message>
-+ </messages>
-+ <structures>
-+ <structure type="menu" name="IDC_KLONKMENU" encoding="utf-16" file="klonk.rc" />
-+ </structures>
-+ </release>
-+ </grit>'''), util.PathFromRoot('grit/testdata'))
-+ grd.SetOutputLanguage('en')
-+ grd.RunGatherers()
-+
-+ hello = grd.GetNodeById('IDS_HELLO')
-+ aboutbox = grd.GetNodeById('IDD_ABOUTBOX')
-+ bingo = grd.GetNodeById('IDS_BINGO')
-+ menu = grd.GetNodeById('IDC_KLONKMENU')
-+
-+ for node in [hello, aboutbox]:
-+ self.failUnless(not node.PseudoIsAllowed())
-+
-+ for node in [bingo, menu]:
-+ self.failUnless(node.PseudoIsAllowed())
-+
-+ # TODO(benrg): There was a test here that formatting hello and aboutbox with
-+ # a pseudo language should fail, but they do not fail and the test was
-+ # broken and failed to catch it. Fix this.
-+
-+ # Should not raise an exception since pseudo is allowed
-+ rc.FormatMessage(bingo, 'xyz-pseudo')
-+ rc.FormatStructure(menu, 'xyz-pseudo', '.')
-+
-+
-+if __name__ == '__main__':
-+ unittest.main()
-diff --git a/tools/grit/grit/node/mock_brotli.py b/tools/grit/grit/node/mock_brotli.py
-new file mode 100644
-index 0000000000..14237aab20
---- /dev/null
-+++ b/tools/grit/grit/node/mock_brotli.py
-@@ -0,0 +1,10 @@
-+#!/usr/bin/env python
-+# 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.
-+
-+"""Mock Brotli Executable for testing purposes."""
-+
-+import sys
-+
-+sys.stdout.write('This has been mock compressed!')
-diff --git a/tools/grit/grit/node/node_io.py b/tools/grit/grit/node/node_io.py
-new file mode 100644
-index 0000000000..ccbc2c0647
---- /dev/null
-+++ b/tools/grit/grit/node/node_io.py
-@@ -0,0 +1,117 @@
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''The <output> and <file> elements.
-+'''
-+
-+from __future__ import print_function
-+
-+import os
-+
-+from grit import xtb_reader
-+from grit.node import base
-+
-+
-+class FileNode(base.Node):
-+ '''A <file> element.'''
-+
-+ def __init__(self):
-+ super(FileNode, self).__init__()
-+ self.re = None
-+ self.should_load_ = True
-+
-+ def IsTranslation(self):
-+ return True
-+
-+ def GetLang(self):
-+ return self.attrs['lang']
-+
-+ def DisableLoading(self):
-+ self.should_load_ = False
-+
-+ def MandatoryAttributes(self):
-+ return ['path', 'lang']
-+
-+ def RunPostSubstitutionGatherer(self, debug=False):
-+ if not self.should_load_:
-+ return
-+
-+ root = self.GetRoot()
-+ defs = getattr(root, 'defines', {})
-+ target_platform = getattr(root, 'target_platform', '')
-+
-+ xtb_file = open(self.ToRealPath(self.GetInputPath()), 'rb')
-+ try:
-+ lang = xtb_reader.Parse(xtb_file,
-+ self.UberClique().GenerateXtbParserCallback(
-+ self.attrs['lang'], debug=debug),
-+ defs=defs,
-+ target_platform=target_platform)
-+ except:
-+ print("Exception during parsing of %s" % self.GetInputPath())
-+ raise
-+ # Translation console uses non-standard language codes 'iw' and 'no' for
-+ # Hebrew and Norwegian Bokmal instead of 'he' and 'nb' used in Chrome.
-+ # Note that some Chrome's .grd still use 'no' instead of 'nb', but 'nb' is
-+ # always used for generated .pak files.
-+ ALTERNATIVE_LANG_CODE_MAP = { 'he': 'iw', 'nb': 'no' }
-+ assert (lang == self.attrs['lang'] or
-+ lang == ALTERNATIVE_LANG_CODE_MAP[self.attrs['lang']]), (
-+ 'The XTB file you reference must contain messages in the language '
-+ 'specified\nby the \'lang\' attribute.')
-+
-+ def GetInputPath(self):
-+ return os.path.expandvars(self.attrs['path'])
-+
-+
-+class OutputNode(base.Node):
-+ '''An <output> element.'''
-+
-+ def MandatoryAttributes(self):
-+ return ['filename', 'type']
-+
-+ def DefaultAttributes(self):
-+ return {
-+ 'lang' : '', # empty lang indicates all languages
-+ 'language_section' : 'neutral', # defines a language neutral section
-+ 'context' : '',
-+ 'fallback_to_default_layout' : 'true',
-+ }
-+
-+ def GetType(self):
-+ return self.attrs['type']
-+
-+ def GetLanguage(self):
-+ '''Returns the language ID, default 'en'.'''
-+ return self.attrs['lang']
-+
-+ def GetContext(self):
-+ return self.attrs['context']
-+
-+ def GetFilename(self):
-+ return self.attrs['filename']
-+
-+ def GetOutputFilename(self):
-+ path = None
-+ if hasattr(self, 'output_filename'):
-+ path = self.output_filename
-+ else:
-+ path = self.attrs['filename']
-+ return os.path.expandvars(path)
-+
-+ def GetFallbackToDefaultLayout(self):
-+ return self.attrs['fallback_to_default_layout'].lower() == 'true'
-+
-+ def _IsValidChild(self, child):
-+ return isinstance(child, EmitNode)
-+
-+class EmitNode(base.ContentNode):
-+ ''' An <emit> element.'''
-+
-+ def DefaultAttributes(self):
-+ return { 'emit_type' : 'prepend'}
-+
-+ def GetEmitType(self):
-+ '''Returns the emit_type for this node. Default is 'append'.'''
-+ return self.attrs['emit_type']
-diff --git a/tools/grit/grit/node/node_io_unittest.py b/tools/grit/grit/node/node_io_unittest.py
-new file mode 100644
-index 0000000000..1f45e51af8
---- /dev/null
-+++ b/tools/grit/grit/node/node_io_unittest.py
-@@ -0,0 +1,182 @@
-+#!/usr/bin/env python
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Unit tests for node_io.FileNode'''
-+
-+from __future__ import print_function
-+
-+import os
-+import sys
-+import unittest
-+
-+if __name__ == '__main__':
-+ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
-+
-+from six import StringIO
-+
-+from grit.node import misc
-+from grit.node import node_io
-+from grit.node import empty
-+from grit import grd_reader
-+from grit import util
-+
-+
-+def _GetAllCliques(root_node):
-+ """Return all cliques in the |root_node| tree."""
-+ ret = []
-+ for node in root_node:
-+ ret.extend(node.GetCliques())
-+ return ret
-+
-+
-+class FileNodeUnittest(unittest.TestCase):
-+ def testGetPath(self):
-+ root = misc.GritNode()
-+ root.StartParsing(u'grit', None)
-+ root.HandleAttribute(u'latest_public_release', u'0')
-+ root.HandleAttribute(u'current_release', u'1')
-+ root.HandleAttribute(u'base_dir', r'..\resource')
-+ translations = empty.TranslationsNode()
-+ translations.StartParsing(u'translations', root)
-+ root.AddChild(translations)
-+ file_node = node_io.FileNode()
-+ file_node.StartParsing(u'file', translations)
-+ file_node.HandleAttribute(u'path', r'flugel\kugel.pdf')
-+ translations.AddChild(file_node)
-+ root.EndParsing()
-+
-+ self.failUnless(root.ToRealPath(file_node.GetInputPath()) ==
-+ util.normpath(
-+ os.path.join(r'../resource', r'flugel/kugel.pdf')))
-+
-+ def VerifyCliquesContainEnglishAndFrenchAndNothingElse(self, cliques):
-+ self.assertEqual(2, len(cliques))
-+ for clique in cliques:
-+ self.assertEqual({'en', 'fr'}, set(clique.clique.keys()))
-+
-+ def testLoadTranslations(self):
-+ xml = '''<?xml version="1.0" encoding="UTF-8"?>
-+ <grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
-+ <translations>
-+ <file path="generated_resources_fr.xtb" lang="fr" />
-+ </translations>
-+ <release seq="3">
-+ <messages>
-+ <message name="ID_HELLO">Hello!</message>
-+ <message name="ID_HELLO_USER">Hello <ph name="USERNAME">%s<ex>Joi</ex></ph></message>
-+ </messages>
-+ </release>
-+ </grit>'''
-+ grd = grd_reader.Parse(StringIO(xml),
-+ util.PathFromRoot('grit/testdata'))
-+ grd.SetOutputLanguage('en')
-+ grd.RunGatherers()
-+ self.VerifyCliquesContainEnglishAndFrenchAndNothingElse(_GetAllCliques(grd))
-+
-+ def testIffyness(self):
-+ grd = grd_reader.Parse(StringIO('''<?xml version="1.0" encoding="UTF-8"?>
-+ <grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
-+ <translations>
-+ <if expr="lang == 'fr'">
-+ <file path="generated_resources_fr.xtb" lang="fr" />
-+ </if>
-+ </translations>
-+ <release seq="3">
-+ <messages>
-+ <message name="ID_HELLO">Hello!</message>
-+ <message name="ID_HELLO_USER">Hello <ph name="USERNAME">%s<ex>Joi</ex></ph></message>
-+ </messages>
-+ </release>
-+ </grit>'''), util.PathFromRoot('grit/testdata'))
-+ grd.SetOutputLanguage('en')
-+ grd.RunGatherers()
-+ cliques = _GetAllCliques(grd)
-+ self.assertEqual(2, len(cliques))
-+ for clique in cliques:
-+ self.assertEqual({'en'}, set(clique.clique.keys()))
-+
-+ grd.SetOutputLanguage('fr')
-+ grd.RunGatherers()
-+ self.VerifyCliquesContainEnglishAndFrenchAndNothingElse(_GetAllCliques(grd))
-+
-+ def testConditionalLoadTranslations(self):
-+ xml = '''<?xml version="1.0" encoding="UTF-8"?>
-+ <grit latest_public_release="2" source_lang_id="en-US" current_release="3"
-+ base_dir=".">
-+ <translations>
-+ <if expr="True">
-+ <file path="generated_resources_fr.xtb" lang="fr" />
-+ </if>
-+ <if expr="False">
-+ <file path="no_such_file.xtb" lang="de" />
-+ </if>
-+ </translations>
-+ <release seq="3">
-+ <messages>
-+ <message name="ID_HELLO">Hello!</message>
-+ <message name="ID_HELLO_USER">Hello <ph name="USERNAME">%s<ex>
-+ Joi</ex></ph></message>
-+ </messages>
-+ </release>
-+ </grit>'''
-+ grd = grd_reader.Parse(StringIO(xml),
-+ util.PathFromRoot('grit/testdata'))
-+ grd.SetOutputLanguage('en')
-+ grd.RunGatherers()
-+ self.VerifyCliquesContainEnglishAndFrenchAndNothingElse(_GetAllCliques(grd))
-+
-+ def testConditionalOutput(self):
-+ xml = '''<?xml version="1.0" encoding="UTF-8"?>
-+ <grit latest_public_release="2" source_lang_id="en-US" current_release="3"
-+ base_dir=".">
-+ <outputs>
-+ <output filename="resource.h" type="rc_header" />
-+ <output filename="en/generated_resources.rc" type="rc_all"
-+ lang="en" />
-+ <if expr="pp_if('NOT_TRUE')">
-+ <output filename="de/generated_resources.rc" type="rc_all"
-+ lang="de" />
-+ </if>
-+ </outputs>
-+ <release seq="3">
-+ <messages>
-+ <message name="ID_HELLO">Hello!</message>
-+ </messages>
-+ </release>
-+ </grit>'''
-+ grd = grd_reader.Parse(StringIO(xml),
-+ util.PathFromRoot('grit/test/data'),
-+ defines={})
-+ grd.SetOutputLanguage('en')
-+ grd.RunGatherers()
-+ outputs = grd.GetChildrenOfType(node_io.OutputNode)
-+ active = set(grd.ActiveDescendants())
-+ self.failUnless(outputs[0] in active)
-+ self.failUnless(outputs[0].GetType() == 'rc_header')
-+ self.failUnless(outputs[1] in active)
-+ self.failUnless(outputs[1].GetType() == 'rc_all')
-+ self.failUnless(outputs[2] not in active)
-+ self.failUnless(outputs[2].GetType() == 'rc_all')
-+
-+ # Verify that 'iw' and 'no' language codes in xtb files are mapped to 'he' and
-+ # 'nb'.
-+ def testLangCodeMapping(self):
-+ grd = grd_reader.Parse(StringIO('''<?xml version="1.0" encoding="UTF-8"?>
-+ <grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
-+ <translations>
-+ <file path="generated_resources_no.xtb" lang="nb" />
-+ <file path="generated_resources_iw.xtb" lang="he" />
-+ </translations>
-+ <release seq="3">
-+ <messages></messages>
-+ </release>
-+ </grit>'''), util.PathFromRoot('grit/testdata'))
-+ grd.SetOutputLanguage('en')
-+ grd.RunGatherers()
-+ self.assertEqual([], _GetAllCliques(grd))
-+
-+
-+if __name__ == '__main__':
-+ unittest.main()
-diff --git a/tools/grit/grit/node/structure.py b/tools/grit/grit/node/structure.py
-new file mode 100644
-index 0000000000..ec170faebb
---- /dev/null
-+++ b/tools/grit/grit/node/structure.py
-@@ -0,0 +1,375 @@
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''The <structure> element.
-+'''
-+
-+from __future__ import print_function
-+
-+import os
-+import platform
-+import re
-+
-+from grit import exception
-+from grit import util
-+from grit.node import base
-+from grit.node import variant
-+
-+import grit.gather.admin_template
-+import grit.gather.chrome_html
-+import grit.gather.chrome_scaled_image
-+import grit.gather.policy_json
-+import grit.gather.rc
-+import grit.gather.tr_html
-+import grit.gather.txt
-+
-+import grit.format.rc
-+
-+# Type of the gatherer to use for each type attribute
-+_GATHERERS = {
-+ 'accelerators' : grit.gather.rc.Accelerators,
-+ 'admin_template' : grit.gather.admin_template.AdmGatherer,
-+ 'chrome_html' : grit.gather.chrome_html.ChromeHtml,
-+ 'chrome_scaled_image' : grit.gather.chrome_scaled_image.ChromeScaledImage,
-+ 'dialog' : grit.gather.rc.Dialog,
-+ 'menu' : grit.gather.rc.Menu,
-+ 'rcdata' : grit.gather.rc.RCData,
-+ 'tr_html' : grit.gather.tr_html.TrHtml,
-+ 'txt' : grit.gather.txt.TxtFile,
-+ 'version' : grit.gather.rc.Version,
-+ 'policy_template_metafile' : grit.gather.policy_json.PolicyJson,
-+}
-+
-+
-+# TODO(joi) Print a warning if the 'variant_of_revision' attribute indicates
-+# that a skeleton variant is older than the original file.
-+
-+
-+class StructureNode(base.Node):
-+ '''A <structure> element.'''
-+
-+ # Regular expression for a local variable definition. Each definition
-+ # is of the form NAME=VALUE, where NAME cannot contain '=' or ',' and
-+ # VALUE must escape all commas: ',' -> ',,'. Each variable definition
-+ # should be separated by a comma with no extra whitespace.
-+ # Example: THING1=foo,THING2=bar
-+ variable_pattern = re.compile(r'([^,=\s]+)=((?:,,|[^,])*)')
-+
-+ def __init__(self):
-+ super(StructureNode, self).__init__()
-+
-+ # Keep track of the last filename we flattened to, so we can
-+ # avoid doing it more than once.
-+ self._last_flat_filename = None
-+
-+ # See _Substitute; this substituter is used for local variables and
-+ # the root substituter is used for global variables.
-+ self.substituter = None
-+
-+ def _IsValidChild(self, child):
-+ return isinstance(child, variant.SkeletonNode)
-+
-+ def _ParseVariables(self, variables):
-+ '''Parse a variable string into a dictionary.'''
-+ matches = StructureNode.variable_pattern.findall(variables)
-+ return dict((name, value.replace(',,', ',')) for name, value in matches)
-+
-+ def EndParsing(self):
-+ super(StructureNode, self).EndParsing()
-+
-+ # Now that we have attributes and children, instantiate the gatherers.
-+ gathertype = _GATHERERS[self.attrs['type']]
-+
-+ self.gatherer = gathertype(self.attrs['file'],
-+ self.attrs['name'],
-+ self.attrs['encoding'])
-+ self.gatherer.SetGrdNode(self)
-+ self.gatherer.SetUberClique(self.UberClique())
-+ if hasattr(self.GetRoot(), 'defines'):
-+ self.gatherer.SetDefines(self.GetRoot().defines)
-+ self.gatherer.SetAttributes(self.attrs)
-+ if self.ExpandVariables():
-+ self.gatherer.SetFilenameExpansionFunction(self._Substitute)
-+
-+ # Parse local variables and instantiate the substituter.
-+ if self.attrs['variables']:
-+ variables = self.attrs['variables']
-+ self.substituter = util.Substituter()
-+ self.substituter.AddSubstitutions(self._ParseVariables(variables))
-+
-+ self.skeletons = {} # Maps expressions to skeleton gatherers
-+ for child in self.children:
-+ assert isinstance(child, variant.SkeletonNode)
-+ skel = gathertype(child.attrs['file'],
-+ self.attrs['name'],
-+ child.GetEncodingToUse(),
-+ is_skeleton=True)
-+ skel.SetGrdNode(self) # TODO(benrg): Or child? Only used for ToRealPath
-+ skel.SetUberClique(self.UberClique())
-+ if hasattr(self.GetRoot(), 'defines'):
-+ skel.SetDefines(self.GetRoot().defines)
-+ if self.ExpandVariables():
-+ skel.SetFilenameExpansionFunction(self._Substitute)
-+ self.skeletons[child.attrs['expr']] = skel
-+
-+ def MandatoryAttributes(self):
-+ return ['type', 'name', 'file']
-+
-+ def DefaultAttributes(self):
-+ return {
-+ 'encoding': 'cp1252',
-+ 'exclude_from_rc': 'false',
-+ 'line_end': 'unix',
-+ 'output_encoding': 'utf-8',
-+ 'generateid': 'true',
-+ 'expand_variables': 'false',
-+ 'output_filename': '',
-+ 'fold_whitespace': 'false',
-+ # Run an arbitrary command after translation is complete
-+ # so that it doesn't interfere with what's in translation
-+ # console.
-+ 'run_command': '',
-+ # Leave empty to run on all platforms, comma-separated
-+ # for one or more specific platforms. Values must match
-+ # output of platform.system().
-+ 'run_command_on_platforms': '',
-+ 'allowexternalscript': 'false',
-+ # preprocess takes the same code path as flattenhtml, but it
-+ # disables any processing/inlining outside of <if> and <include>.
-+ 'preprocess': 'false',
-+ 'flattenhtml': 'false',
-+ 'fallback_to_low_resolution': 'default',
-+ 'variables': '',
-+ 'compress': 'default',
-+ 'use_base_dir': 'true',
-+ }
-+
-+ def IsExcludedFromRc(self):
-+ return self.attrs['exclude_from_rc'] == 'true'
-+
-+ def Process(self, output_dir):
-+ """Writes the processed data to output_dir. In the case of a chrome_html
-+ structure this will add references to other scale factors. If flattening
-+ this will also write file references to be base64 encoded data URLs. The
-+ name of the new file is returned."""
-+ filename = self.ToRealPath(self.GetInputPath())
-+ flat_filename = os.path.join(output_dir,
-+ self.attrs['name'] + '_' + os.path.basename(filename))
-+
-+ if self._last_flat_filename == flat_filename:
-+ return
-+
-+ with open(flat_filename, 'wb') as outfile:
-+ if self.ExpandVariables():
-+ text = self.gatherer.GetText()
-+ file_contents = self._Substitute(text)
-+ else:
-+ file_contents = self.gatherer.GetData('', 'utf-8')
-+ outfile.write(file_contents.encode('utf-8'))
-+
-+ self._last_flat_filename = flat_filename
-+ return os.path.basename(flat_filename)
-+
-+ def GetLineEnd(self):
-+ '''Returns the end-of-line character or characters for files output because
-+ of this node ('\r\n', '\n', or '\r' depending on the 'line_end' attribute).
-+ '''
-+ if self.attrs['line_end'] == 'unix':
-+ return '\n'
-+ elif self.attrs['line_end'] == 'windows':
-+ return '\r\n'
-+ elif self.attrs['line_end'] == 'mac':
-+ return '\r'
-+ else:
-+ raise exception.UnexpectedAttribute(
-+ "Attribute 'line_end' must be one of 'unix' (default), 'windows' or "
-+ "'mac'")
-+
-+ def GetCliques(self):
-+ return self.gatherer.GetCliques()
-+
-+ def GetDataPackValue(self, lang, encoding):
-+ """Returns a bytes representation for a data_pack entry."""
-+ if self.ExpandVariables():
-+ text = self.gatherer.GetText()
-+ data = util.Encode(self._Substitute(text), encoding)
-+ else:
-+ data = self.gatherer.GetData(lang, encoding)
-+ if encoding != util.BINARY:
-+ data = data.encode(encoding)
-+ return self.CompressDataIfNeeded(data)
-+
-+ def GetHtmlResourceFilenames(self):
-+ """Returns a set of all filenames inlined by this node."""
-+ return self.gatherer.GetHtmlResourceFilenames()
-+
-+ def GetInputPath(self):
-+ path = self.gatherer.GetInputPath()
-+ if path is None:
-+ return path
-+
-+ # Do not mess with absolute paths, that would make them invalid.
-+ if os.path.isabs(os.path.expandvars(path)):
-+ return path
-+
-+ # We have no control over code that calls ToRealPath later, so convert
-+ # the path to be relative against our basedir.
-+ if self.attrs.get('use_base_dir', 'true') != 'true':
-+ # Normalize the directory path to use the appropriate OS separator.
-+ # GetBaseDir() may return paths\like\this or paths/like/this, since it is
-+ # read from the base_dir attribute in the grd file.
-+ norm_base_dir = util.normpath(self.GetRoot().GetBaseDir())
-+ return os.path.relpath(path, norm_base_dir)
-+
-+ return path
-+
-+ def GetTextualIds(self):
-+ if not hasattr(self, 'gatherer'):
-+ # This case is needed because this method is called by
-+ # GritNode.ValidateUniqueIds before RunGatherers has been called.
-+ # TODO(benrg): Fix this?
-+ return [self.attrs['name']]
-+ return self.gatherer.GetTextualIds()
-+
-+ def RunPreSubstitutionGatherer(self, debug=False):
-+ if debug:
-+ print('Running gatherer %s for file %s' %
-+ (type(self.gatherer), self.GetInputPath()))
-+
-+ # Note: Parse() is idempotent, therefore this method is also.
-+ self.gatherer.Parse()
-+ for skel in self.skeletons.values():
-+ skel.Parse()
-+
-+ def GetSkeletonGatherer(self):
-+ '''Returns the gatherer for the alternate skeleton that should be used,
-+ based on the expressions for selecting skeletons, or None if the skeleton
-+ from the English version of the structure should be used.
-+ '''
-+ for expr in self.skeletons:
-+ if self.EvaluateCondition(expr):
-+ return self.skeletons[expr]
-+ return None
-+
-+ def HasFileForLanguage(self):
-+ return self.attrs['type'] in ['tr_html', 'admin_template', 'txt',
-+ 'chrome_scaled_image',
-+ 'chrome_html']
-+
-+ def ExpandVariables(self):
-+ '''Variable expansion on structures is controlled by an XML attribute.
-+
-+ However, old files assume that expansion is always on for Rc files.
-+
-+ Returns:
-+ A boolean.
-+ '''
-+ attrs = self.GetRoot().attrs
-+ if 'grit_version' in attrs and attrs['grit_version'] > 1:
-+ return self.attrs['expand_variables'] == 'true'
-+ else:
-+ return (self.attrs['expand_variables'] == 'true' or
-+ self.attrs['file'].lower().endswith('.rc'))
-+
-+ def _Substitute(self, text):
-+ '''Perform local and global variable substitution.'''
-+ if self.substituter:
-+ text = self.substituter.Substitute(text)
-+ return self.GetRoot().GetSubstituter().Substitute(text)
-+
-+ def RunCommandOnCurrentPlatform(self):
-+ if self.attrs['run_command_on_platforms'] == '':
-+ return True
-+ else:
-+ target_platforms = self.attrs['run_command_on_platforms'].split(',')
-+ return platform.system() in target_platforms
-+
-+ def FileForLanguage(self, lang, output_dir, create_file=True,
-+ return_if_not_generated=True):
-+ '''Returns the filename of the file associated with this structure,
-+ for the specified language.
-+
-+ Args:
-+ lang: 'fr'
-+ output_dir: 'c:\temp'
-+ create_file: True
-+ '''
-+ assert self.HasFileForLanguage()
-+ # If the source language is requested, and no extra changes are requested,
-+ # use the existing file.
-+ if ((not lang or lang == self.GetRoot().GetSourceLanguage()) and
-+ self.attrs['expand_variables'] != 'true' and
-+ (not self.attrs['run_command'] or
-+ not self.RunCommandOnCurrentPlatform())):
-+ if return_if_not_generated:
-+ input_path = self.GetInputPath()
-+ if input_path is None:
-+ return None
-+ return self.ToRealPath(input_path)
-+ else:
-+ return None
-+
-+ if self.attrs['output_filename'] != '':
-+ filename = self.attrs['output_filename']
-+ else:
-+ filename = os.path.basename(self.attrs['file'])
-+ assert len(filename)
-+ filename = '%s_%s' % (lang, filename)
-+ filename = os.path.join(output_dir, filename)
-+
-+ # Only create the output if it was requested by the call.
-+ if create_file:
-+ text = self.gatherer.Translate(
-+ lang,
-+ pseudo_if_not_available=self.PseudoIsAllowed(),
-+ fallback_to_english=self.ShouldFallbackToEnglish(),
-+ skeleton_gatherer=self.GetSkeletonGatherer())
-+
-+ file_contents = util.FixLineEnd(text, self.GetLineEnd())
-+ if self.ExpandVariables():
-+ # Note that we reapply substitution a second time here.
-+ # This is because a) we need to look inside placeholders
-+ # b) the substitution values are language-dependent
-+ file_contents = self._Substitute(file_contents)
-+
-+ with open(filename, 'wb') as file_object:
-+ output_stream = util.WrapOutputStream(file_object,
-+ self.attrs['output_encoding'])
-+ output_stream.write(file_contents)
-+
-+ if self.attrs['run_command'] and self.RunCommandOnCurrentPlatform():
-+ # Run arbitrary commands after translation is complete so that it
-+ # doesn't interfere with what's in translation console.
-+ command = self.attrs['run_command'] % {'filename': filename}
-+ result = os.system(command)
-+ assert result == 0, '"%s" failed.' % command
-+
-+ return filename
-+
-+ def IsResourceMapSource(self):
-+ return True
-+
-+ @staticmethod
-+ def Construct(parent, name, type, file, encoding='cp1252'):
-+ '''Creates a new node which is a child of 'parent', with attributes set
-+ by parameters of the same name.
-+ '''
-+ node = StructureNode()
-+ node.StartParsing('structure', parent)
-+ node.HandleAttribute('name', name)
-+ node.HandleAttribute('type', type)
-+ node.HandleAttribute('file', file)
-+ node.HandleAttribute('encoding', encoding)
-+ node.EndParsing()
-+ return node
-+
-+ def SubstituteMessages(self, substituter):
-+ '''Propagates substitution to gatherer.
-+
-+ Args:
-+ substituter: a grit.util.Substituter object.
-+ '''
-+ assert hasattr(self, 'gatherer')
-+ if self.ExpandVariables():
-+ self.gatherer.SubstituteMessages(substituter)
-diff --git a/tools/grit/grit/node/structure_unittest.py b/tools/grit/grit/node/structure_unittest.py
-new file mode 100644
-index 0000000000..0e66dce37a
---- /dev/null
-+++ b/tools/grit/grit/node/structure_unittest.py
-@@ -0,0 +1,178 @@
-+#!/usr/bin/env python
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Unit tests for <structure> nodes.
-+'''
-+
-+from __future__ import print_function
-+
-+import os
-+import os.path
-+import sys
-+import zlib
-+if __name__ == '__main__':
-+ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
-+
-+import platform
-+import tempfile
-+import unittest
-+import struct
-+
-+from grit import constants
-+from grit import util
-+from grit.node import brotli_util
-+from grit.node import structure
-+from grit.format import rc
-+
-+
-+def checkIsGzipped(filename, compress_attr):
-+ test_data_root = util.PathFromRoot('grit/testdata')
-+ root = util.ParseGrdForUnittest(
-+ '''
-+ <structures>
-+ <structure name="TEST_TXT" file="%s" %s type="chrome_html"/>
-+ </structures>''' % (filename, compress_attr),
-+ base_dir=test_data_root)
-+ node, = root.GetChildrenOfType(structure.StructureNode)
-+ node.RunPreSubstitutionGatherer()
-+ compressed = node.GetDataPackValue(lang='en', encoding=util.BINARY)
-+
-+ decompressed_data = zlib.decompress(compressed, 16 + zlib.MAX_WBITS)
-+ expected = util.ReadFile(os.path.join(test_data_root, filename), util.BINARY)
-+ return expected == decompressed_data
-+
-+
-+class StructureUnittest(unittest.TestCase):
-+ def testSkeleton(self):
-+ grd = util.ParseGrdForUnittest('''
-+ <structures>
-+ <structure type="dialog" name="IDD_ABOUTBOX" file="klonk.rc" encoding="utf-16-le">
-+ <skeleton expr="lang == 'fr'" variant_of_revision="1" file="klonk-alternate-skeleton.rc" />
-+ </structure>
-+ </structures>''', base_dir=util.PathFromRoot('grit/testdata'))
-+ grd.SetOutputLanguage('fr')
-+ grd.RunGatherers()
-+ transl = ''.join(rc.Format(grd, 'fr', '.'))
-+ self.failUnless(transl.count('040704') and transl.count('110978'))
-+ self.failUnless(transl.count('2005",IDC_STATIC'))
-+
-+ def testRunCommandOnCurrentPlatform(self):
-+ node = structure.StructureNode()
-+ node.attrs = node.DefaultAttributes()
-+ self.failUnless(node.RunCommandOnCurrentPlatform())
-+ node.attrs['run_command_on_platforms'] = 'Nosuch'
-+ self.failIf(node.RunCommandOnCurrentPlatform())
-+ node.attrs['run_command_on_platforms'] = (
-+ 'Nosuch,%s,Othernot' % platform.system())
-+ self.failUnless(node.RunCommandOnCurrentPlatform())
-+
-+ def testVariables(self):
-+ grd = util.ParseGrdForUnittest('''
-+ <structures>
-+ <structure type="chrome_html" name="hello_tmpl" file="structure_variables.html" expand_variables="true" variables="GREETING=Hello,THINGS=foo,, bar,, baz,EQUATION=2+2==4,filename=simple" flattenhtml="true"></structure>
-+ </structures>''', base_dir=util.PathFromRoot('grit/testdata'))
-+ grd.SetOutputLanguage('en')
-+ grd.RunGatherers()
-+ node, = grd.GetChildrenOfType(structure.StructureNode)
-+ filename = node.Process(tempfile.gettempdir())
-+ filepath = os.path.join(tempfile.gettempdir(), filename)
-+ with open(filepath) as f:
-+ result = f.read()
-+ self.failUnlessEqual(('<h1>Hello!</h1>\n'
-+ 'Some cool things are foo, bar, baz.\n'
-+ 'Did you know that 2+2==4?\n'
-+ '<p>\n'
-+ ' Hello!\n'
-+ '</p>\n'), result)
-+ os.remove(filepath)
-+
-+ def testGetPath(self):
-+ base_dir = util.PathFromRoot('grit/testdata')
-+ grd = util.ParseGrdForUnittest('''
-+ <structures>
-+ <structure type="chrome_html" name="hello_tmpl" file="structure_variables.html" expand_variables="true" variables="GREETING=Hello,THINGS=foo,, bar,, baz,EQUATION=2+2==4,filename=simple" flattenhtml="true" use_base_dir="true"></structure>
-+ </structures>''', base_dir)
-+ grd.SetOutputLanguage('en')
-+ grd.RunGatherers()
-+ node, = grd.GetChildrenOfType(structure.StructureNode)
-+ self.assertEqual(grd.ToRealPath(node.GetInputPath()),
-+ os.path.abspath(os.path.join(
-+ base_dir, r'structure_variables.html')))
-+
-+ def testGetPathNoBasedir(self):
-+ base_dir = util.PathFromRoot('grit/testdata')
-+ abs_path = os.path.join(base_dir, r'structure_variables.html')
-+ rel_path = os.path.relpath(abs_path, os.getcwd())
-+ grd = util.ParseGrdForUnittest('''
-+ <structures>
-+ <structure type="chrome_html" name="hello_tmpl" file="''' + rel_path + '''" expand_variables="true" variables="GREETING=Hello,THINGS=foo,, bar,, baz,EQUATION=2+2==4,filename=simple" flattenhtml="true" use_base_dir="false"></structure>
-+ </structures>''', util.PathFromRoot('grit/testdata'))
-+ grd.SetOutputLanguage('en')
-+ grd.RunGatherers()
-+ node, = grd.GetChildrenOfType(structure.StructureNode)
-+ self.assertEqual(grd.ToRealPath(node.GetInputPath()),
-+ os.path.abspath(os.path.join(
-+ base_dir, r'structure_variables.html')))
-+
-+ def testCompressGzip(self):
-+ self.assertTrue(checkIsGzipped('test_text.txt', 'compress="gzip"'))
-+
-+ def testCompressGzipByDefault(self):
-+ self.assertTrue(checkIsGzipped('test_html.html', ''))
-+ self.assertTrue(checkIsGzipped('test_js.js', ''))
-+ self.assertTrue(checkIsGzipped('test_css.css', ''))
-+ self.assertTrue(checkIsGzipped('test_svg.svg', ''))
-+
-+ self.assertTrue(checkIsGzipped('test_html.html', 'compress="default"'))
-+ self.assertTrue(checkIsGzipped('test_js.js', 'compress="default"'))
-+ self.assertTrue(checkIsGzipped('test_css.css', 'compress="default"'))
-+ self.assertTrue(checkIsGzipped('test_svg.svg', 'compress="default"'))
-+
-+ def testCompressBrotli(self):
-+ test_data_root = util.PathFromRoot('grit/testdata')
-+ root = util.ParseGrdForUnittest(
-+ '''
-+ <structures>
-+ <structure name="TEST_TXT" file="test_text.txt"
-+ compress="brotli" type="chrome_html" />
-+ </structures>''',
-+ base_dir=test_data_root)
-+ node, = root.GetChildrenOfType(structure.StructureNode)
-+ node.RunPreSubstitutionGatherer()
-+
-+ # Using the mock brotli decompression executable.
-+ brotli_util.SetBrotliCommand([sys.executable,
-+ os.path.join(os.path.dirname(__file__),
-+ 'mock_brotli.py')])
-+ compressed = node.GetDataPackValue(lang='en', encoding=util.BINARY)
-+ # Assert that the first two bytes in compressed format is BROTLI_CONST.
-+ self.assertEqual(constants.BROTLI_CONST, compressed[0:2])
-+
-+ # Compare the actual size of the uncompressed test data with
-+ # the size appended during compression.
-+ actual_size = len(util.ReadFile(
-+ os.path.join(test_data_root, 'test_text.txt'), util.BINARY))
-+ uncompress_size = struct.unpack('<i', compressed[2:6])[0]
-+ uncompress_size += struct.unpack('<h', compressed[6:8])[0] << 4*8
-+ self.assertEqual(actual_size, uncompress_size)
-+
-+ self.assertEqual(b'This has been mock compressed!', compressed[8:])
-+
-+ def testNotCompressed(self):
-+ test_data_root = util.PathFromRoot('grit/testdata')
-+ root = util.ParseGrdForUnittest('''
-+ <structures>
-+ <structure name="TEST_TXT" file="test_text.txt" type="chrome_html" />
-+ </structures>''', base_dir=test_data_root)
-+ node, = root.GetChildrenOfType(structure.StructureNode)
-+ node.RunPreSubstitutionGatherer()
-+ data = node.GetDataPackValue(lang='en', encoding=util.BINARY)
-+
-+ self.assertEqual(util.ReadFile(
-+ os.path.join(test_data_root, 'test_text.txt'), util.BINARY), data)
-+
-+
-+if __name__ == '__main__':
-+ unittest.main()
-diff --git a/tools/grit/grit/node/variant.py b/tools/grit/grit/node/variant.py
-new file mode 100644
-index 0000000000..9f5845f954
---- /dev/null
-+++ b/tools/grit/grit/node/variant.py
-@@ -0,0 +1,41 @@
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''The <skeleton> element.
-+'''
-+
-+from __future__ import print_function
-+
-+from grit.node import base
-+
-+
-+class SkeletonNode(base.Node):
-+ '''A <skeleton> element.'''
-+
-+ # TODO(joi) Support inline skeleton variants as CDATA instead of requiring
-+ # a 'file' attribute.
-+
-+ def MandatoryAttributes(self):
-+ return ['expr', 'variant_of_revision', 'file']
-+
-+ def DefaultAttributes(self):
-+ '''If not specified, 'encoding' will actually default to the parent node's
-+ encoding.
-+ '''
-+ return {'encoding' : ''}
-+
-+ def _ContentType(self):
-+ if 'file' in self.attrs:
-+ return self._CONTENT_TYPE_NONE
-+ else:
-+ return self._CONTENT_TYPE_CDATA
-+
-+ def GetEncodingToUse(self):
-+ if self.attrs['encoding'] == '':
-+ return self.parent.attrs['encoding']
-+ else:
-+ return self.attrs['encoding']
-+
-+ def GetInputPath(self):
-+ return self.attrs['file']
-diff --git a/tools/grit/grit/pseudo.py b/tools/grit/grit/pseudo.py
-new file mode 100644
-index 0000000000..b607bfc6bb
---- /dev/null
-+++ b/tools/grit/grit/pseudo.py
-@@ -0,0 +1,129 @@
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Pseudotranslation support. Our pseudotranslations are based on the
-+P-language, which is a simple vowel-extending language. Examples of P:
-+ - "hello" becomes "hepellopo"
-+ - "howdie" becomes "hopowdiepie"
-+ - "because" becomes "bepecaupause" (but in our implementation we don't
-+ handle the silent e at the end so it actually would return "bepecaupausepe"
-+
-+The P-language has the excellent quality of increasing the length of text
-+by around 30-50% which is great for pseudotranslations, to stress test any
-+GUI layouts etc.
-+
-+To make the pseudotranslations more obviously "not a translation" and to make
-+them exercise any code that deals with encodings, we also transform all English
-+vowels into equivalent vowels with diacriticals on them (rings, acutes,
-+diaresis, and circumflex), and we write the "p" in the P-language as a Hebrew
-+character Qof. It looks sort of like a latin character "p" but it is outside
-+the latin-1 character set which will stress character encoding bugs.
-+'''
-+
-+from __future__ import print_function
-+
-+from grit import lazy_re
-+from grit import tclib
-+
-+
-+# An RFC language code for the P pseudolanguage.
-+PSEUDO_LANG = 'x-P-pseudo'
-+
-+# Hebrew character Qof. It looks kind of like a 'p' but is outside
-+# the latin-1 character set which is good for our purposes.
-+# TODO(joi) For now using P instead of Qof, because of some bugs it used. Find
-+# a better solution, i.e. one that introduces a non-latin1 character into the
-+# pseudotranslation.
-+#_QOF = u'\u05e7'
-+_QOF = u'P'
-+
-+# How we map each vowel.
-+_VOWELS = {
-+ u'a' : u'\u00e5', # a with ring
-+ u'e' : u'\u00e9', # e acute
-+ u'i' : u'\u00ef', # i diaresis
-+ u'o' : u'\u00f4', # o circumflex
-+ u'u' : u'\u00fc', # u diaresis
-+ u'y' : u'\u00fd', # y acute
-+ u'A' : u'\u00c5', # A with ring
-+ u'E' : u'\u00c9', # E acute
-+ u'I' : u'\u00cf', # I diaresis
-+ u'O' : u'\u00d4', # O circumflex
-+ u'U' : u'\u00dc', # U diaresis
-+ u'Y' : u'\u00dd', # Y acute
-+}
-+_VOWELS_KEYS = set(_VOWELS.keys())
-+
-+# Matches vowels and P
-+_PSUB_RE = lazy_re.compile("(%s)" % '|'.join(_VOWELS_KEYS | {'P'}))
-+
-+
-+# Pseudotranslations previously created. This is important for performance
-+# reasons, especially since we routinely pseudotranslate the whole project
-+# several or many different times for each build.
-+_existing_translations = {}
-+
-+
-+def MapVowels(str, also_p = False):
-+ '''Returns a copy of 'str' where characters that exist as keys in _VOWELS
-+ have been replaced with the corresponding value. If also_p is true, this
-+ function will also change capital P characters into a Hebrew character Qof.
-+ '''
-+ def Repl(match):
-+ if match.group() == 'p':
-+ if also_p:
-+ return _QOF
-+ else:
-+ return 'p'
-+ else:
-+ return _VOWELS[match.group()]
-+ return _PSUB_RE.sub(Repl, str)
-+
-+
-+def PseudoString(str):
-+ '''Returns a pseudotranslation of the provided string, in our enhanced
-+ P-language.'''
-+ if str in _existing_translations:
-+ return _existing_translations[str]
-+
-+ outstr = u''
-+ ix = 0
-+ while ix < len(str):
-+ if str[ix] not in _VOWELS_KEYS:
-+ outstr += str[ix]
-+ ix += 1
-+ else:
-+ # We want to treat consecutive vowels as one composite vowel. This is not
-+ # always accurate e.g. in composite words but good enough.
-+ consecutive_vowels = u''
-+ while ix < len(str) and str[ix] in _VOWELS_KEYS:
-+ consecutive_vowels += str[ix]
-+ ix += 1
-+ changed_vowels = MapVowels(consecutive_vowels)
-+ outstr += changed_vowels
-+ outstr += _QOF
-+ outstr += changed_vowels
-+
-+ _existing_translations[str] = outstr
-+ return outstr
-+
-+
-+def PseudoMessage(message):
-+ '''Returns a pseudotranslation of the provided message.
-+
-+ Args:
-+ message: tclib.Message()
-+
-+ Return:
-+ tclib.Translation()
-+ '''
-+ transl = tclib.Translation()
-+
-+ for part in message.GetContent():
-+ if isinstance(part, tclib.Placeholder):
-+ transl.AppendPlaceholder(part)
-+ else:
-+ transl.AppendText(PseudoString(part))
-+
-+ return transl
-diff --git a/tools/grit/grit/pseudo_rtl.py b/tools/grit/grit/pseudo_rtl.py
-new file mode 100644
-index 0000000000..2240b571de
---- /dev/null
-+++ b/tools/grit/grit/pseudo_rtl.py
-@@ -0,0 +1,104 @@
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Pseudo RTL, (aka Fake Bidi) support. It simply wraps each word with
-+Unicode RTL overrides.
-+More info at https://sites.google.com/a/chromium.org/dev/Home/fake-bidi
-+'''
-+
-+from __future__ import print_function
-+
-+import re
-+
-+from grit import lazy_re
-+from grit import tclib
-+
-+ACCENTED_STRINGS = {
-+ 'a': u"\u00e5", 'e': u"\u00e9", 'i': u"\u00ee", 'o': u"\u00f6",
-+ 'u': u"\u00fb", 'A': u"\u00c5", 'E': u"\u00c9", 'I': u"\u00ce",
-+ 'O': u"\u00d6", 'U': u"\u00db", 'c': u"\u00e7", 'd': u"\u00f0",
-+ 'n': u"\u00f1", 'p': u"\u00fe", 'y': u"\u00fd", 'C': u"\u00c7",
-+ 'D': u"\u00d0", 'N': u"\u00d1", 'P': u"\u00de", 'Y': u"\u00dd",
-+ 'f': u"\u0192", 's': u"\u0161", 'S': u"\u0160", 'z': u"\u017e",
-+ 'Z': u"\u017d", 'g': u"\u011d", 'G': u"\u011c", 'h': u"\u0125",
-+ 'H': u"\u0124", 'j': u"\u0135", 'J': u"\u0134", 'k': u"\u0137",
-+ 'K': u"\u0136", 'l': u"\u013c", 'L': u"\u013b", 't': u"\u0163",
-+ 'T': u"\u0162", 'w': u"\u0175", 'W': u"\u0174",
-+ '$': u"\u20ac", '?': u"\u00bf", 'R': u"\u00ae", r'!': u"\u00a1",
-+}
-+
-+# a character set containing the keys in ACCENTED_STRINGS
-+# We should not accent characters in an escape sequence such as "\n".
-+# To be safe, we assume every character following a backslash is an escaped
-+# character. We also need to consider the case like "\\n", which means
-+# a blackslash and a character "n", we will accent the character "n".
-+TO_ACCENT = lazy_re.compile(
-+ r'[%s]|\\[a-z\\]' % ''.join(ACCENTED_STRINGS.keys()))
-+
-+# Lex text so that we don't interfere with html tokens and entities.
-+# This lexing scheme will handle all well formed tags and entities, html or
-+# xhtml. It will not handle comments, CDATA sections, or the unescaping tags:
-+# script, style, xmp or listing. If any of those appear in messages,
-+# something is wrong.
-+TOKENS = [ lazy_re.compile(
-+ '^%s' % pattern, # match at the beginning of input
-+ re.I | re.S # html tokens are case-insensitive
-+ )
-+ for pattern in
-+ (
-+ # a run of non html special characters
-+ r'[^<&]+',
-+ # a tag
-+ (r'</?[a-z]\w*' # beginning of tag
-+ r'(?:\s+\w+(?:\s*=\s*' # attribute start
-+ r'(?:[^\s"\'>]+|"[^\"]*"|\'[^\']*\'))?' # attribute value
-+ r')*\s*/?>'),
-+ # an entity
-+ r'&(?:[a-z]\w+|#\d+|#x[\da-f]+);',
-+ # an html special character not part of a special sequence
-+ r'.'
-+ ) ]
-+
-+ALPHABETIC_RUN = lazy_re.compile(r'([^\W0-9_]+)')
-+
-+RLO = u'\u202e'
-+PDF = u'\u202c'
-+
-+def PseudoRTLString(text):
-+ '''Returns a fake bidirectional version of the source string. This code is
-+ based on accentString above, in turn copied from Frank Tang.
-+ '''
-+ parts = []
-+ while text:
-+ m = None
-+ for token in TOKENS:
-+ m = token.search(text)
-+ if m:
-+ part = m.group(0)
-+ text = text[len(part):]
-+ if part[0] not in ('<', '&'):
-+ # not a tag or entity, so accent
-+ part = ALPHABETIC_RUN.sub(lambda run: RLO + run.group() + PDF, part)
-+ parts.append(part)
-+ break
-+ return ''.join(parts)
-+
-+
-+def PseudoRTLMessage(message):
-+ '''Returns a pseudo-RTL (aka Fake-Bidi) translation of the provided message.
-+
-+ Args:
-+ message: tclib.Message()
-+
-+ Return:
-+ tclib.Translation()
-+ '''
-+ transl = tclib.Translation()
-+ for part in message.GetContent():
-+ if isinstance(part, tclib.Placeholder):
-+ transl.AppendPlaceholder(part)
-+ else:
-+ transl.AppendText(PseudoRTLString(part))
-+
-+ return transl
-diff --git a/tools/grit/grit/pseudo_unittest.py b/tools/grit/grit/pseudo_unittest.py
-new file mode 100644
-index 0000000000..b1d53ff401
---- /dev/null
-+++ b/tools/grit/grit/pseudo_unittest.py
-@@ -0,0 +1,55 @@
-+#!/usr/bin/env python
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Unit tests for grit.pseudo'''
-+
-+from __future__ import print_function
-+
-+import os
-+import sys
-+if __name__ == '__main__':
-+ sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
-+
-+import unittest
-+
-+from grit import pseudo
-+from grit import tclib
-+
-+
-+class PseudoUnittest(unittest.TestCase):
-+ def testVowelMapping(self):
-+ self.failUnless(pseudo.MapVowels('abebibobuby') ==
-+ u'\u00e5b\u00e9b\u00efb\u00f4b\u00fcb\u00fd')
-+ self.failUnless(pseudo.MapVowels('ABEBIBOBUBY') ==
-+ u'\u00c5B\u00c9B\u00cfB\u00d4B\u00dcB\u00dd')
-+
-+ def testPseudoString(self):
-+ out = pseudo.PseudoString('hello')
-+ self.failUnless(out == pseudo.MapVowels(u'hePelloPo', True))
-+
-+ def testConsecutiveVowels(self):
-+ out = pseudo.PseudoString("beautiful weather, ain't it?")
-+ self.failUnless(out == pseudo.MapVowels(
-+ u"beauPeautiPifuPul weaPeathePer, aiPain't iPit?", 1))
-+
-+ def testCapitals(self):
-+ out = pseudo.PseudoString("HOWDIE DOODIE, DR. JONES")
-+ self.failUnless(out == pseudo.MapVowels(
-+ u"HOPOWDIEPIE DOOPOODIEPIE, DR. JOPONEPES", 1))
-+
-+ def testPseudoMessage(self):
-+ msg = tclib.Message(text='Hello USERNAME, how are you?',
-+ placeholders=[
-+ tclib.Placeholder('USERNAME', '%s', 'Joi')])
-+ trans = pseudo.PseudoMessage(msg)
-+ # TODO(joi) It would be nicer if 'you' -> 'youPou' instead of
-+ # 'you' -> 'youPyou' and if we handled the silent e in 'are'
-+ self.failUnless(trans.GetPresentableContent() ==
-+ pseudo.MapVowels(
-+ u'HePelloPo USERNAME, hoPow aParePe youPyou?', 1))
-+
-+
-+if __name__ == '__main__':
-+ unittest.main()
-diff --git a/tools/grit/grit/shortcuts.py b/tools/grit/grit/shortcuts.py
-new file mode 100644
-index 0000000000..0db2ce436c
---- /dev/null
-+++ b/tools/grit/grit/shortcuts.py
-@@ -0,0 +1,93 @@
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Stuff to prevent conflicting shortcuts.
-+'''
-+
-+from __future__ import print_function
-+
-+from grit import lazy_re
-+
-+
-+class ShortcutGroup(object):
-+ '''Manages a list of cliques that belong together in a single shortcut
-+ group. Knows how to detect conflicting shortcut keys.
-+ '''
-+
-+ # Matches shortcut keys, e.g. &J
-+ SHORTCUT_RE = lazy_re.compile('([^&]|^)(&[A-Za-z])')
-+
-+ def __init__(self, name):
-+ self.name = name
-+ # Map of language codes to shortcut keys used (which is a map of
-+ # shortcut keys to counts).
-+ self.keys_by_lang = {}
-+ # List of cliques in this group
-+ self.cliques = []
-+
-+ def AddClique(self, c):
-+ for existing_clique in self.cliques:
-+ if existing_clique.GetId() == c.GetId():
-+ # This happens e.g. when we have e.g.
-+ # <if expr1><structure 1></if> <if expr2><structure 2></if>
-+ # where only one will really be included in the output.
-+ return
-+
-+ self.cliques.append(c)
-+ for (lang, msg) in c.clique.items():
-+ if lang not in self.keys_by_lang:
-+ self.keys_by_lang[lang] = {}
-+ keymap = self.keys_by_lang[lang]
-+
-+ content = msg.GetRealContent()
-+ keys = [groups[1] for groups in self.SHORTCUT_RE.findall(content)]
-+ for key in keys:
-+ key = key.upper()
-+ if key in keymap:
-+ keymap[key] += 1
-+ else:
-+ keymap[key] = 1
-+
-+ def GenerateWarnings(self, tc_project):
-+ # For any language that has more than one occurrence of any shortcut,
-+ # make a list of the conflicting shortcuts.
-+ problem_langs = {}
-+ for (lang, keys) in self.keys_by_lang.items():
-+ for (key, count) in keys.items():
-+ if count > 1:
-+ if lang not in problem_langs:
-+ problem_langs[lang] = []
-+ problem_langs[lang].append(key)
-+
-+ warnings = []
-+ if len(problem_langs):
-+ warnings.append("WARNING - duplicate keys exist in shortcut group %s" %
-+ self.name)
-+ for (lang,keys) in problem_langs.items():
-+ warnings.append(" %6s duplicates: %s" % (lang, ', '.join(keys)))
-+ return warnings
-+
-+
-+def GenerateDuplicateShortcutsWarnings(uberclique, tc_project):
-+ '''Given an UberClique and a project name, will print out helpful warnings
-+ if there are conflicting shortcuts within shortcut groups in the provided
-+ UberClique.
-+
-+ Args:
-+ uberclique: clique.UberClique()
-+ tc_project: 'MyProjectNameInTheTranslationConsole'
-+
-+ Returns:
-+ ['warning line 1', 'warning line 2', ...]
-+ '''
-+ warnings = []
-+ groups = {}
-+ for c in uberclique.AllCliques():
-+ for group in c.shortcut_groups:
-+ if group not in groups:
-+ groups[group] = ShortcutGroup(group)
-+ groups[group].AddClique(c)
-+ for group in groups.values():
-+ warnings += group.GenerateWarnings(tc_project)
-+ return warnings
-diff --git a/tools/grit/grit/shortcuts_unittest.py b/tools/grit/grit/shortcuts_unittest.py
-new file mode 100644
-index 0000000000..30e7c4f758
---- /dev/null
-+++ b/tools/grit/grit/shortcuts_unittest.py
-@@ -0,0 +1,79 @@
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Unit tests for grit.shortcuts
-+'''
-+
-+from __future__ import print_function
-+
-+import os
-+import sys
-+if __name__ == '__main__':
-+ sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
-+
-+import unittest
-+
-+from six import StringIO
-+
-+from grit import shortcuts
-+from grit import clique
-+from grit import tclib
-+from grit.gather import rc
-+
-+class ShortcutsUnittest(unittest.TestCase):
-+
-+ def setUp(self):
-+ self.uq = clique.UberClique()
-+
-+ def testFunctionality(self):
-+ c = self.uq.MakeClique(tclib.Message(text="Hello &there"))
-+ c.AddToShortcutGroup('group_name')
-+ c = self.uq.MakeClique(tclib.Message(text="Howdie &there partner"))
-+ c.AddToShortcutGroup('group_name')
-+
-+ warnings = shortcuts.GenerateDuplicateShortcutsWarnings(self.uq, 'PROJECT')
-+ self.failUnless(warnings)
-+
-+ def testAmpersandEscaping(self):
-+ c = self.uq.MakeClique(tclib.Message(text="Hello &there"))
-+ c.AddToShortcutGroup('group_name')
-+ c = self.uq.MakeClique(tclib.Message(text="S&&T are the &letters S and T"))
-+ c.AddToShortcutGroup('group_name')
-+
-+ warnings = shortcuts.GenerateDuplicateShortcutsWarnings(self.uq, 'PROJECT')
-+ self.failUnless(len(warnings) == 0)
-+
-+ def testDialog(self):
-+ dlg = rc.Dialog(StringIO('''\
-+IDD_SIDEBAR_RSS_PANEL_PROPPAGE DIALOGEX 0, 0, 239, 221
-+STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD
-+FONT 8, "MS Shell Dlg", 400, 0, 0x1
-+BEGIN
-+ PUSHBUTTON "Add &URL",IDC_SIDEBAR_RSS_ADD_URL,182,53,57,14
-+ EDITTEXT IDC_SIDEBAR_RSS_NEW_URL,0,53,178,15,ES_AUTOHSCROLL
-+ PUSHBUTTON "&Remove",IDC_SIDEBAR_RSS_REMOVE,183,200,56,14
-+ PUSHBUTTON "&Edit",IDC_SIDEBAR_RSS_EDIT,123,200,56,14
-+ CONTROL "&Automatically add commonly viewed clips",
-+ IDC_SIDEBAR_RSS_AUTO_ADD,"Button",BS_AUTOCHECKBOX |
-+ BS_MULTILINE | WS_TABSTOP,0,200,120,17
-+ PUSHBUTTON "",IDC_SIDEBAR_RSS_HIDDEN,179,208,6,6,NOT WS_VISIBLE
-+ LTEXT "You can display clips from blogs, news sites, and other online sources.",
-+ IDC_STATIC,0,0,239,10
-+ LISTBOX IDC_SIDEBAR_DISPLAYED_FEED_LIST,0,69,239,127,LBS_SORT |
-+ LBS_OWNERDRAWFIXED | LBS_HASSTRINGS |
-+ LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_HSCROLL |
-+ WS_TABSTOP
-+ LTEXT "Add a clip from a recently viewed website by clicking Add Recent Clips.",
-+ IDC_STATIC,0,13,141,19
-+ LTEXT "Or, if you know a site supports RSS or Atom, you can enter the RSS or Atom URL below and add it to your list of Web Clips.",
-+ IDC_STATIC,0,33,239,18
-+ PUSHBUTTON "Add Recent &Clips (10)...",
-+ IDC_SIDEBAR_RSS_ADD_RECENT_CLIPS,146,14,93,14
-+END'''), 'IDD_SIDEBAR_RSS_PANEL_PROPPAGE')
-+ dlg.SetUberClique(self.uq)
-+ dlg.Parse()
-+
-+ warnings = shortcuts.GenerateDuplicateShortcutsWarnings(self.uq, 'PROJECT')
-+ self.failUnless(len(warnings) == 0)
-+
-diff --git a/tools/grit/grit/tclib.py b/tools/grit/grit/tclib.py
-new file mode 100644
-index 0000000000..27ba366924
---- /dev/null
-+++ b/tools/grit/grit/tclib.py
-@@ -0,0 +1,246 @@
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Adaptation of the extern.tclib classes for our needs.
-+'''
-+
-+from __future__ import print_function
-+
-+import functools
-+import re
-+
-+import six
-+
-+from grit import exception
-+from grit import lazy_re
-+import grit.extern.tclib
-+
-+
-+# Matches whitespace sequences which can be folded into a single whitespace
-+# character. This matches single characters so that non-spaces are replaced
-+# with spaces.
-+_FOLD_WHITESPACE = re.compile(r'\s+')
-+
-+# Caches compiled regexp used to split tags in BaseMessage.__init__()
-+_RE_CACHE = {}
-+
-+def Identity(i):
-+ return i
-+
-+
-+class BaseMessage(object):
-+ '''Base class with methods shared by Message and Translation.
-+ '''
-+
-+ def __init__(self, text='', placeholders=[], description='', meaning=''):
-+ self.parts = []
-+ self.placeholders = []
-+ self.meaning = meaning
-+ self.dirty = True # True if self.id is (or might be) wrong
-+ self.id = 0
-+ self.SetDescription(description)
-+
-+ if text != '':
-+ if not placeholders or placeholders == []:
-+ self.AppendText(text)
-+ else:
-+ tag_map = {}
-+ for placeholder in placeholders:
-+ tag_map[placeholder.GetPresentation()] = [placeholder, 0]
-+ # This creates a regexp like '(TAG1|TAG2|TAG3)'.
-+ # The tags have to be sorted in order of decreasing length, so that
-+ # longer tags are substituted before shorter tags that happen to be
-+ # substrings of the longer tag.
-+ # E.g. "EXAMPLE_FOO_NAME" must be matched before "EXAMPLE_FOO",
-+ # otherwise "EXAMPLE_FOO" splits "EXAMPLE_FOO_NAME" too.
-+ tags = sorted(tag_map.keys(),
-+ key=functools.cmp_to_key(
-+ lambda x, y: len(x) - len(y) or ((x > y) - (x < y))),
-+ reverse=True)
-+ tag_re = '(' + '|'.join(tags) + ')'
-+
-+ # This caching improves the time to build
-+ # chrome/app:generated_resources from 21.562s to 17.672s on Linux.
-+ compiled_re = _RE_CACHE.get(tag_re, None)
-+ if compiled_re is None:
-+ compiled_re = re.compile(tag_re)
-+ _RE_CACHE[tag_re] = compiled_re
-+
-+ chunked_text = compiled_re.split(text)
-+
-+ for chunk in chunked_text:
-+ if chunk: # ignore empty chunk
-+ if chunk in tag_map:
-+ self.AppendPlaceholder(tag_map[chunk][0])
-+ tag_map[chunk][1] += 1 # increase placeholder use count
-+ else:
-+ self.AppendText(chunk)
-+ for key in tag_map:
-+ assert tag_map[key][1] != 0
-+
-+ def GetRealContent(self, escaping_function=Identity):
-+ '''Returns the original content, i.e. what your application and users
-+ will see.
-+
-+ Specify a function to escape each translateable bit, if you like.
-+ '''
-+ bits = []
-+ for item in self.parts:
-+ if isinstance(item, six.string_types):
-+ bits.append(escaping_function(item))
-+ else:
-+ bits.append(item.GetOriginal())
-+ return ''.join(bits)
-+
-+ def GetPresentableContent(self):
-+ presentable_content = []
-+ for part in self.parts:
-+ if isinstance(part, Placeholder):
-+ presentable_content.append(part.GetPresentation())
-+ else:
-+ presentable_content.append(part)
-+ return ''.join(presentable_content)
-+
-+ def AppendPlaceholder(self, placeholder):
-+ assert isinstance(placeholder, Placeholder)
-+ dup = False
-+ for other in self.GetPlaceholders():
-+ if other.presentation == placeholder.presentation:
-+ assert other.original == placeholder.original
-+ dup = True
-+
-+ if not dup:
-+ self.placeholders.append(placeholder)
-+ self.parts.append(placeholder)
-+ self.dirty = True
-+
-+ def AppendText(self, text):
-+ assert isinstance(text, six.string_types)
-+ assert text != ''
-+
-+ self.parts.append(text)
-+ self.dirty = True
-+
-+ def GetContent(self):
-+ '''Returns the parts of the message. You may modify parts if you wish.
-+ Note that you must not call GetId() on this object until you have finished
-+ modifying the contents.
-+ '''
-+ self.dirty = True # user might modify content
-+ return self.parts
-+
-+ def GetDescription(self):
-+ return self.description
-+
-+ def SetDescription(self, description):
-+ self.description = _FOLD_WHITESPACE.sub(' ', description)
-+
-+ def GetMeaning(self):
-+ return self.meaning
-+
-+ def GetId(self):
-+ if self.dirty:
-+ self.id = self.GenerateId()
-+ self.dirty = False
-+ return self.id
-+
-+ def GenerateId(self):
-+ return grit.extern.tclib.GenerateMessageId(self.GetPresentableContent(),
-+ self.meaning)
-+
-+ def GetPlaceholders(self):
-+ return self.placeholders
-+
-+ def FillTclibBaseMessage(self, msg):
-+ msg.SetDescription(self.description.encode('utf-8'))
-+
-+ for part in self.parts:
-+ if isinstance(part, Placeholder):
-+ ph = grit.extern.tclib.Placeholder(
-+ part.presentation.encode('utf-8'),
-+ part.original.encode('utf-8'),
-+ part.example.encode('utf-8'))
-+ msg.AppendPlaceholder(ph)
-+ else:
-+ msg.AppendText(part.encode('utf-8'))
-+
-+
-+class Message(BaseMessage):
-+ '''A message.'''
-+
-+ def __init__(self, text='', placeholders=[], description='', meaning='',
-+ assigned_id=None):
-+ super(Message, self).__init__(text, placeholders, description, meaning)
-+ self.assigned_id = assigned_id
-+
-+ def ToTclibMessage(self):
-+ msg = grit.extern.tclib.Message('utf-8', meaning=self.meaning)
-+ self.FillTclibBaseMessage(msg)
-+ return msg
-+
-+ def GetId(self):
-+ '''Use the assigned id if we have one.'''
-+ if self.assigned_id:
-+ return self.assigned_id
-+
-+ return super(Message, self).GetId()
-+
-+ def HasAssignedId(self):
-+ '''Returns True if this message has an assigned id.'''
-+ return bool(self.assigned_id)
-+
-+
-+class Translation(BaseMessage):
-+ '''A translation.'''
-+
-+ def __init__(self, text='', id='', placeholders=[], description='', meaning=''):
-+ super(Translation, self).__init__(text, placeholders, description, meaning)
-+ self.id = id
-+
-+ def GetId(self):
-+ assert id != '', "ID has not been set."
-+ return self.id
-+
-+ def SetId(self, id):
-+ self.id = id
-+
-+ def ToTclibMessage(self):
-+ msg = grit.extern.tclib.Message(
-+ 'utf-8', id=self.id, meaning=self.meaning)
-+ self.FillTclibBaseMessage(msg)
-+ return msg
-+
-+
-+class Placeholder(grit.extern.tclib.Placeholder):
-+ '''Modifies constructor to accept a Unicode string
-+ '''
-+
-+ # Must match placeholder presentation names
-+ _NAME_RE = lazy_re.compile('^[A-Za-z0-9_]+$')
-+
-+ def __init__(self, presentation, original, example):
-+ '''Creates a new placeholder.
-+
-+ Args:
-+ presentation: 'USERNAME'
-+ original: '%s'
-+ example: 'Joi'
-+ '''
-+ assert presentation != ''
-+ assert original != ''
-+ assert example != ''
-+ if not self._NAME_RE.match(presentation):
-+ raise exception.InvalidPlaceholderName(presentation)
-+ self.presentation = presentation
-+ self.original = original
-+ self.example = example
-+
-+ def GetPresentation(self):
-+ return self.presentation
-+
-+ def GetOriginal(self):
-+ return self.original
-+
-+ def GetExample(self):
-+ return self.example
-diff --git a/tools/grit/grit/tclib_unittest.py b/tools/grit/grit/tclib_unittest.py
-new file mode 100644
-index 0000000000..7a08654e1b
---- /dev/null
-+++ b/tools/grit/grit/tclib_unittest.py
-@@ -0,0 +1,180 @@
-+#!/usr/bin/env python
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Unit tests for grit.tclib'''
-+
-+from __future__ import print_function
-+
-+import sys
-+import os.path
-+if __name__ == '__main__':
-+ sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
-+
-+import unittest
-+
-+import six
-+
-+from grit import tclib
-+
-+from grit import exception
-+import grit.extern.tclib
-+
-+
-+class TclibUnittest(unittest.TestCase):
-+ def testInit(self):
-+ msg = tclib.Message(text=u'Hello Earthlings',
-+ description='Greetings\n\t message')
-+ self.failUnlessEqual(msg.GetPresentableContent(), 'Hello Earthlings')
-+ self.failUnless(isinstance(msg.GetPresentableContent(), six.string_types))
-+ self.failUnlessEqual(msg.GetDescription(), 'Greetings message')
-+
-+ def testGetAttr(self):
-+ msg = tclib.Message()
-+ msg.AppendText(u'Hello') # Tests __getattr__
-+ self.failUnless(msg.GetPresentableContent() == 'Hello')
-+ self.failUnless(isinstance(msg.GetPresentableContent(), six.string_types))
-+
-+ def testAll(self):
-+ text = u'Howdie USERNAME'
-+ phs = [tclib.Placeholder(u'USERNAME', u'%s', 'Joi')]
-+ msg = tclib.Message(text=text, placeholders=phs)
-+ self.failUnless(msg.GetPresentableContent() == 'Howdie USERNAME')
-+
-+ trans = tclib.Translation(text=text, placeholders=phs)
-+ self.failUnless(trans.GetPresentableContent() == 'Howdie USERNAME')
-+ self.failUnless(isinstance(trans.GetPresentableContent(), six.string_types))
-+
-+ def testUnicodeReturn(self):
-+ text = u'\u00fe'
-+ msg = tclib.Message(text=text)
-+ self.failUnless(msg.GetPresentableContent() == text)
-+ from_list = msg.GetContent()[0]
-+ self.failUnless(from_list == text)
-+
-+ def testRegressionTranslationInherited(self):
-+ '''Regression tests a bug that was caused by grit.tclib.Translation
-+ inheriting from the translation console's Translation object
-+ instead of only owning an instance of it.
-+ '''
-+ msg = tclib.Message(text=u"BLA1\r\nFrom: BLA2 \u00fe BLA3",
-+ placeholders=[
-+ tclib.Placeholder('BLA1', '%s', '%s'),
-+ tclib.Placeholder('BLA2', '%s', '%s'),
-+ tclib.Placeholder('BLA3', '%s', '%s')])
-+ transl = tclib.Translation(text=msg.GetPresentableContent(),
-+ placeholders=msg.GetPlaceholders())
-+ content = transl.GetContent()
-+ self.failUnless(isinstance(content[3], six.string_types))
-+
-+ def testFingerprint(self):
-+ # This has Windows line endings. That is on purpose.
-+ id = grit.extern.tclib.GenerateMessageId(
-+ 'Google Desktop for Enterprise\r\n'
-+ 'All Rights Reserved\r\n'
-+ '\r\n'
-+ '---------\r\n'
-+ 'Contents\r\n'
-+ '---------\r\n'
-+ 'This distribution contains the following files:\r\n'
-+ '\r\n'
-+ 'GoogleDesktopSetup.msi - Installation and setup program\r\n'
-+ 'GoogleDesktop.adm - Group Policy administrative template file\r\n'
-+ 'AdminGuide.pdf - Google Desktop for Enterprise administrative guide\r\n'
-+ '\r\n'
-+ '\r\n'
-+ '--------------\r\n'
-+ 'Documentation\r\n'
-+ '--------------\r\n'
-+ 'Full documentation and installation instructions are in the \r\n'
-+ 'administrative guide, and also online at \r\n'
-+ 'http://desktop.google.com/enterprise/adminguide.html.\r\n'
-+ '\r\n'
-+ '\r\n'
-+ '------------------------\r\n'
-+ 'IBM Lotus Notes Plug-In\r\n'
-+ '------------------------\r\n'
-+ 'The Lotus Notes plug-in is included in the release of Google \r\n'
-+ 'Desktop for Enterprise. The IBM Lotus Notes Plug-in for Google \r\n'
-+ 'Desktop indexes mail, calendar, task, contact and journal \r\n'
-+ 'documents from Notes. Discussion documents including those from \r\n'
-+ 'the discussion and team room templates can also be indexed by \r\n'
-+ 'selecting an option from the preferences. Once indexed, this data\r\n'
-+ 'will be returned in Google Desktop searches. The corresponding\r\n'
-+ 'document can be opened in Lotus Notes from the Google Desktop \r\n'
-+ 'results page.\r\n'
-+ '\r\n'
-+ 'Install: The plug-in will install automatically during the Google \r\n'
-+ 'Desktop setup process if Lotus Notes is already installed. Lotus \r\n'
-+ 'Notes must not be running in order for the install to occur. \r\n'
-+ '\r\n'
-+ 'Preferences: Preferences and selection of databases to index are\r\n'
-+ 'set in the \'Google Desktop for Notes\' dialog reached through the \r\n'
-+ '\'Actions\' menu.\r\n'
-+ '\r\n'
-+ 'Reindexing: Selecting \'Reindex all databases\' will index all the \r\n'
-+ 'documents in each database again.\r\n'
-+ '\r\n'
-+ '\r\n'
-+ 'Notes Plug-in Known Issues\r\n'
-+ '---------------------------\r\n'
-+ '\r\n'
-+ 'If the \'Google Desktop for Notes\' item is not available from the \r\n'
-+ 'Lotus Notes Actions menu, then installation was not successful. \r\n'
-+ 'Installation consists of writing one file, notesgdsplugin.dll, to \r\n'
-+ 'the Notes application directory and a setting to the notes.ini \r\n'
-+ 'configuration file. The most likely cause of an unsuccessful \r\n'
-+ 'installation is that the installer was not able to locate the \r\n'
-+ 'notes.ini file. Installation will complete if the user closes Notes\r\n'
-+ 'and manually adds the following setting to this file on a new line:\r\n'
-+ 'AddinMenus=notegdsplugin.dll\r\n'
-+ '\r\n'
-+ 'If the notesgdsplugin.dll file is not in the application directory\r\n'
-+ r'(e.g., C:\Program Files\Lotus\Notes) after Google Desktop \r\n'
-+ 'installation, it is likely that Notes was not installed correctly. \r\n'
-+ '\r\n'
-+ 'Only local databases can be indexed. If they can be determined, \r\n'
-+ 'the user\'s local mail file and address book will be included in the\r\n'
-+ 'list automatically. Mail archives and other databases must be \r\n'
-+ 'added with the \'Add\' button.\r\n'
-+ '\r\n'
-+ 'Some users may experience performance issues during the initial \r\n'
-+ 'indexing of a database. The \'Perform the initial index of a \r\n'
-+ 'database only when I\'m idle\' option will limit the indexing process\r\n'
-+ 'to times when the user is not using the machine. If this does not \r\n'
-+ 'alleviate the problem or the user would like to continually index \r\n'
-+ 'but just do so more slowly or quickly, the GoogleWaitTime notes.ini\r\n'
-+ 'value can be set. Increasing the GoogleWaitTime value will slow \r\n'
-+ 'down the indexing process, and lowering the value will speed it up.\r\n'
-+ 'A value of zero causes the fastest possible indexing. Removing the\r\n'
-+ 'ini parameter altogether returns it to the default (20).\r\n'
-+ '\r\n'
-+ 'Crashes have been known to occur with certain types of history \r\n'
-+ 'bookmarks. If the Notes client seems to crash randomly, try \r\n'
-+ 'disabling the \'Index note history\' option. If it crashes before,\r\n'
-+ 'you can get to the preferences, add the following line to your \r\n'
-+ 'notes.ini file:\r\n'
-+ 'GDSNoIndexHistory=1\r\n')
-+ self.assertEqual(id, '7660964495923572726')
-+
-+ def testPlaceholderNameChecking(self):
-+ try:
-+ ph = tclib.Placeholder('BINGO BONGO', 'bla', 'bla')
-+ raise Exception("We shouldn't get here")
-+ except exception.InvalidPlaceholderName:
-+ pass # Expect exception to be thrown because presentation contained space
-+
-+ def testTagsWithCommonSubstring(self):
-+ word = 'ABCDEFGHIJ'
-+ text = ' '.join([word[:i] for i in range(1, 11)])
-+ phs = [tclib.Placeholder(word[:i], str(i), str(i)) for i in range(1, 11)]
-+ try:
-+ msg = tclib.Message(text=text, placeholders=phs)
-+ self.failUnless(msg.GetRealContent() == '1 2 3 4 5 6 7 8 9 10')
-+ except:
-+ self.fail('tclib.Message() should handle placeholders that are '
-+ 'substrings of each other')
-+
-+if __name__ == '__main__':
-+ unittest.main()
-diff --git a/tools/grit/grit/test_suite_all.py b/tools/grit/grit/test_suite_all.py
-new file mode 100644
-index 0000000000..3bfe2a79d5
---- /dev/null
-+++ b/tools/grit/grit/test_suite_all.py
-@@ -0,0 +1,34 @@
-+#!/usr/bin/env python3
-+# Copyright (c) 2011 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Unit test suite that collects all test cases for GRIT.'''
-+
-+from __future__ import print_function
-+
-+import os
-+import sys
-+
-+
-+CUR_DIR = os.path.dirname(os.path.realpath(__file__))
-+SRC_DIR = os.path.dirname(os.path.dirname(os.path.dirname(CUR_DIR)))
-+TYP_DIR = os.path.join(
-+ SRC_DIR, 'third_party', 'catapult', 'third_party', 'typ')
-+
-+if TYP_DIR not in sys.path:
-+ sys.path.insert(0, TYP_DIR)
-+
-+
-+import typ # pylint: disable=import-error,unused-import
-+
-+
-+def main(args):
-+ return typ.main(
-+ top_level_dirs=[os.path.join(CUR_DIR, '..')],
-+ skip=['grit.format.gen_predetermined_ids_unittest.*',
-+ 'grit.pseudo_unittest.*']
-+ )
-+
-+if __name__ == '__main__':
-+ sys.exit(main(sys.argv[1:]))
-diff --git a/tools/grit/grit/testdata/GoogleDesktop.adm b/tools/grit/grit/testdata/GoogleDesktop.adm
-new file mode 100644
-index 0000000000..082f56bb1a
---- /dev/null
-+++ b/tools/grit/grit/testdata/GoogleDesktop.adm
-@@ -0,0 +1,945 @@
-+CLASS MACHINE
-+ CATEGORY !!Cat_Google
-+ CATEGORY !!Cat_GoogleDesktopSearch
-+ KEYNAME "Software\Policies\Google\Google Desktop"
-+
-+ CATEGORY !!Cat_Preferences
-+ KEYNAME "Software\Policies\Google\Google Desktop\Preferences"
-+
-+ CATEGORY !!Cat_IndexAndCaptureControl
-+ POLICY !!Blacklist_Email
-+ EXPLAIN !!Explain_Blacklist_Email
-+ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-1"
-+ VALUENAME "1"
-+ END POLICY
-+
-+ POLICY !!Blacklist_Gmail
-+ EXPLAIN !!Explain_Blacklist_Gmail
-+ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-pop"
-+ VALUENAME "gmail"
-+ END POLICY
-+
-+ POLICY !!Blacklist_WebHistory
-+ EXPLAIN !!Explain_Blacklist_WebHistory
-+ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-1"
-+ VALUENAME "2"
-+ END POLICY
-+
-+ POLICY !!Blacklist_Chat
-+ EXPLAIN !!Explain_Blacklist_Chat
-+ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-1"
-+ ACTIONLISTON
-+ VALUENAME "3" VALUE NUMERIC 1
-+ END ACTIONLISTON
-+ END POLICY
-+
-+ POLICY !!Blacklist_Text
-+ EXPLAIN !!Explain_Blacklist_Text
-+ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-1"
-+ ACTIONLISTON
-+ VALUENAME "4" VALUE NUMERIC 1
-+ END ACTIONLISTON
-+ END POLICY
-+
-+ POLICY !!Blacklist_Media
-+ EXPLAIN !!Explain_Blacklist_Media
-+ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-1"
-+ ACTIONLISTON
-+ VALUENAME "5" VALUE NUMERIC 1
-+ END ACTIONLISTON
-+ END POLICY
-+
-+ POLICY !!Blacklist_Contact
-+ EXPLAIN !!Explain_Blacklist_Contact
-+ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-1"
-+ ACTIONLISTON
-+ VALUENAME "9" VALUE NUMERIC 1
-+ END ACTIONLISTON
-+ END POLICY
-+
-+ POLICY !!Blacklist_Calendar
-+ EXPLAIN !!Explain_Blacklist_Calendar
-+ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-1"
-+ ACTIONLISTON
-+ VALUENAME "10" VALUE NUMERIC 1
-+ END ACTIONLISTON
-+ END POLICY
-+
-+ POLICY !!Blacklist_Task
-+ EXPLAIN !!Explain_Blacklist_Task
-+ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-1"
-+ ACTIONLISTON
-+ VALUENAME "11" VALUE NUMERIC 1
-+ END ACTIONLISTON
-+ END POLICY
-+
-+ POLICY !!Blacklist_Note
-+ EXPLAIN !!Explain_Blacklist_Note
-+ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-1"
-+ ACTIONLISTON
-+ VALUENAME "12" VALUE NUMERIC 1
-+ END ACTIONLISTON
-+ END POLICY
-+
-+ POLICY !!Blacklist_Journal
-+ EXPLAIN !!Explain_Blacklist_Journal
-+ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-1"
-+ ACTIONLISTON
-+ VALUENAME "13" VALUE NUMERIC 1
-+ END ACTIONLISTON
-+ END POLICY
-+
-+ POLICY !!Blacklist_Word
-+ EXPLAIN !!Explain_Blacklist_Word
-+ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-2"
-+ VALUENAME "DOC"
-+ END POLICY
-+
-+ POLICY !!Blacklist_Excel
-+ EXPLAIN !!Explain_Blacklist_Excel
-+ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-2"
-+ VALUENAME "XLS"
-+ END POLICY
-+
-+ POLICY !!Blacklist_Powerpoint
-+ EXPLAIN !!Explain_Blacklist_Powerpoint
-+ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-2"
-+ VALUENAME "PPT"
-+ END POLICY
-+
-+ POLICY !!Blacklist_PDF
-+ EXPLAIN !!Explain_Blacklist_PDF
-+ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-2"
-+ VALUENAME "PDF"
-+ END POLICY
-+
-+ POLICY !!Blacklist_ZIP
-+ EXPLAIN !!Explain_Blacklist_ZIP
-+ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-2"
-+ VALUENAME "ZIP"
-+ END POLICY
-+
-+ POLICY !!Blacklist_HTTPS
-+ EXPLAIN !!Explain_Blacklist_HTTPS
-+ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-3"
-+ VALUENAME "HTTPS"
-+ END POLICY
-+
-+ POLICY !!Blacklist_PasswordProtectedOffice
-+ EXPLAIN !!Explain_Blacklist_PasswordProtectedOffice
-+ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-13"
-+ VALUENAME "SECUREOFFICE"
-+ END POLICY
-+
-+ POLICY !!Blacklist_URI_Contains
-+ EXPLAIN !!Explain_Blacklist_URI_Contains
-+ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-6"
-+ PART !!Blacklist_URI_Contains LISTBOX
-+ END PART
-+ END POLICY
-+
-+ POLICY !!Blacklist_Extensions
-+ EXPLAIN !!Explain_Blacklist_Extensions
-+ PART !!Blacklist_Extensions EDITTEXT
-+ VALUENAME "file_extensions_to_skip"
-+ END PART
-+ END POLICY
-+
-+ POLICY !!Pol_Disallow_UserSearchLocations
-+ EXPLAIN !!Explain_Disallow_UserSearchLocations
-+ VALUENAME user_search_locations
-+ VALUEON NUMERIC 1
-+ END POLICY
-+
-+ POLICY !!Pol_Search_Location_Whitelist
-+ EXPLAIN !!Explain_Search_Location_Whitelist
-+ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\policy_search_location_whitelist"
-+ PART !!Search_Locations_Whitelist LISTBOX
-+ END PART
-+ END POLICY
-+
-+ POLICY !!Email_Retention
-+ EXPLAIN !!Explain_Email_Retention
-+ PART !!Email_Retention_Edit NUMERIC
-+ VALUENAME "email_days_to_retain"
-+ MIN 1 MAX 65535 DEFAULT 30 SPIN 1
-+ END PART
-+ END POLICY
-+
-+ POLICY !!Webpage_Retention
-+ EXPLAIN !!Explain_Webpage_Retention
-+ PART !!Webpage_Retention_Edit NUMERIC
-+ VALUENAME "webpage_days_to_retain"
-+ MIN 1 MAX 65535 DEFAULT 30 SPIN 1
-+ END PART
-+ END POLICY
-+
-+ POLICY !!File_Retention
-+ EXPLAIN !!Explain_File_Retention
-+ PART !!File_Retention_Edit NUMERIC
-+ VALUENAME "file_days_to_retain"
-+ MIN 1 MAX 65535 DEFAULT 30 SPIN 1
-+ END PART
-+ END POLICY
-+
-+ POLICY !!IM_Retention
-+ EXPLAIN !!Explain_IM_Retention
-+ PART !!IM_Retention_Edit NUMERIC
-+ VALUENAME "im_days_to_retain"
-+ MIN 1 MAX 65535 DEFAULT 30 SPIN 1
-+ END PART
-+ END POLICY
-+
-+ POLICY !!Pol_Remove_Deleted_Items
-+ EXPLAIN !!Explain_Remove_Deleted_Items
-+ VALUENAME remove_deleted_items
-+ VALUEON NUMERIC 1
-+ END POLICY
-+
-+ POLICY !!Pol_Allow_Simultaneous_Indexing
-+ EXPLAIN !!Explain_Allow_Simultaneous_Indexing
-+ VALUENAME simultaneous_indexing
-+ VALUEON NUMERIC 1
-+ END POLICY
-+
-+ END CATEGORY
-+
-+ POLICY !!Pol_TurnOffAdvancedFeatures
-+ EXPLAIN !!Explain_TurnOffAdvancedFeatures
-+ VALUENAME error_report_on
-+ VALUEON NUMERIC 0
-+ END POLICY
-+
-+ POLICY !!Pol_TurnOffImproveGd
-+ EXPLAIN !!Explain_TurnOffImproveGd
-+ VALUENAME improve_gd
-+ VALUEON NUMERIC 0
-+ VALUEOFF NUMERIC 1
-+ END POLICY
-+
-+ POLICY !!Pol_NoPersonalizationInfo
-+ EXPLAIN !!Explain_NoPersonalizationInfo
-+ VALUENAME send_personalization_info
-+ VALUEON NUMERIC 0
-+ VALUEOFF NUMERIC 1
-+ END POLICY
-+
-+ POLICY !!Pol_OneBoxMode
-+ EXPLAIN !!Explain_OneBoxMode
-+ VALUENAME onebox_mode
-+ VALUEON NUMERIC 0
-+ END POLICY
-+
-+ POLICY !!Pol_EncryptIndex
-+ EXPLAIN !!Explain_EncryptIndex
-+ VALUENAME encrypt_index
-+ VALUEON NUMERIC 1
-+ END POLICY
-+
-+ POLICY !!Pol_Hyper
-+ EXPLAIN !!Explain_Hyper
-+ VALUENAME hyper_off
-+ VALUEON NUMERIC 1
-+ END POLICY
-+
-+ POLICY !!Pol_Display_Mode
-+ EXPLAIN !!Explain_Display_Mode
-+ PART !!Pol_Display_Mode DROPDOWNLIST
-+ VALUENAME display_mode
-+ ITEMLIST
-+ NAME !!Sidebar VALUE NUMERIC 1
-+ NAME !!Deskbar VALUE NUMERIC 8
-+ NAME !!FloatingDeskbar VALUE NUMERIC 4
-+ NAME !!None VALUE NUMERIC 0
-+ END ITEMLIST
-+ END PART
-+ END POLICY
-+
-+ END CATEGORY ; Preferences
-+
-+ CATEGORY !!Cat_Enterprise
-+ KEYNAME "Software\Policies\Google\Google Desktop\Enterprise"
-+
-+ POLICY !!Pol_Autoupdate
-+ EXPLAIN !!Explain_Autoupdate
-+ VALUENAME autoupdate_host
-+ VALUEON ""
-+ END POLICY
-+
-+ POLICY !!Pol_AutoupdateAsSystem
-+ EXPLAIN !!Explain_AutoupdateAsSystem
-+ VALUENAME autoupdate_impersonate_user
-+ VALUEON NUMERIC 0
-+ VALUEOFF NUMERIC 1
-+ END POLICY
-+
-+ POLICY !!Pol_EnterpriseTab
-+ EXPLAIN !!Explain_EnterpriseTab
-+ PART !!EnterpriseTabText EDITTEXT
-+ VALUENAME enterprise_tab_text
-+ END PART
-+ PART !!EnterpriseTabHomepage EDITTEXT
-+ VALUENAME enterprise_tab_homepage
-+ END PART
-+ PART !!EnterpriseTabHomepageQuery CHECKBOX
-+ VALUENAME enterprise_tab_homepage_query
-+ END PART
-+ PART !!EnterpriseTabResults EDITTEXT
-+ VALUENAME enterprise_tab_results
-+ END PART
-+ PART !!EnterpriseTabResultsQuery CHECKBOX
-+ VALUENAME enterprise_tab_results_query
-+ END PART
-+ END POLICY
-+
-+ POLICY !!Pol_GSAHosts
-+ EXPLAIN !!Explain_GSAHosts
-+ KEYNAME "Software\Policies\Google\Google Desktop\Enterprise\GSAHosts"
-+ PART !!Pol_GSAHosts LISTBOX
-+ END PART
-+ END POLICY
-+
-+ POLICY !!Pol_PolicyUnawareClientProhibitedFlag
-+ EXPLAIN !!Explain_PolicyUnawareClientProhibitedFlag
-+ KEYNAME "Software\Policies\Google\Google Desktop"
-+ VALUENAME PolicyUnawareClientProhibitedFlag
-+ END POLICY
-+
-+ POLICY !!Pol_MinimumAllowedVersion
-+ EXPLAIN !!Explain_MinimumAllowedVersion
-+ PART !!Pol_MinimumAllowedVersion EDITTEXT
-+ VALUENAME minimum_allowed_version
-+ END PART
-+ END POLICY
-+
-+ POLICY !!Pol_MaximumAllowedVersion
-+ EXPLAIN !!Explain_MaximumAllowedVersion
-+ PART !!Pol_MaximumAllowedVersion EDITTEXT
-+ VALUENAME maximum_allowed_version
-+ END PART
-+ END POLICY
-+
-+ POLICY !!Pol_Disallow_Gadgets
-+ EXPLAIN !!Explain_Disallow_Gadgets
-+ VALUENAME disallow_gadgets
-+ VALUEON NUMERIC 1
-+ PART !!Disallow_Only_Non_Builtin_Gadgets CHECKBOX DEFCHECKED
-+ VALUENAME disallow_only_non_builtin_gadgets
-+ VALUEON NUMERIC 1
-+ VALUEOFF NUMERIC 0
-+ END PART
-+ END POLICY
-+
-+ POLICY !!Pol_Gadget_Whitelist
-+ EXPLAIN !!Explain_Gadget_Whitelist
-+ KEYNAME "Software\Policies\Google\Google Desktop\Enterprise\gadget_whitelist"
-+ PART !!Pol_Gadget_Whitelist LISTBOX
-+ END PART
-+ END POLICY
-+
-+ POLICY !!Pol_Gadget_Install_Confirmation_Whitelist
-+ EXPLAIN !!Explain_Gadget_Install_Confirmation_Whitelist
-+ KEYNAME "Software\Policies\Google\Google Desktop\Enterprise\install_confirmation_whitelist"
-+ PART !!Pol_Gadget_Install_Confirmation_Whitelist LISTBOX
-+ END PART
-+ END POLICY
-+
-+ POLICY !!Pol_Alternate_User_Data_Dir
-+ EXPLAIN !!Explain_Alternate_User_Data_Dir
-+ PART !!Pol_Alternate_User_Data_Dir EDITTEXT
-+ VALUENAME alternate_user_data_dir
-+ END PART
-+ END POLICY
-+
-+ POLICY !!Pol_MaxAllowedOutlookConnections
-+ EXPLAIN !!Explain_MaxAllowedOutlookConnections
-+ PART !!Pol_MaxAllowedOutlookConnections NUMERIC
-+ VALUENAME max_allowed_outlook_connections
-+ MIN 1 MAX 65535 DEFAULT 400 SPIN 1
-+ END PART
-+ END POLICY
-+
-+ POLICY !!Pol_DisallowSsdService
-+ EXPLAIN !!Explain_DisallowSsdService
-+ VALUENAME disallow_ssd_service
-+ VALUEON NUMERIC 1
-+ END POLICY
-+
-+ POLICY !!Pol_DisallowSsdOutbound
-+ EXPLAIN !!Explain_DisallowSsdOutbound
-+ VALUENAME disallow_ssd_outbound
-+ VALUEON NUMERIC 1
-+ END POLICY
-+
-+ POLICY !!Pol_Disallow_Store_Gadget_Service
-+ EXPLAIN !!Explain_Disallow_Store_Gadget_Service
-+ VALUENAME disallow_store_gadget_service
-+ VALUEON NUMERIC 1
-+ END POLICY
-+
-+ POLICY !!Pol_MaxExchangeIndexingRate
-+ EXPLAIN !!Explain_MaxExchangeIndexingRate
-+ PART !!Pol_MaxExchangeIndexingRate NUMERIC
-+ VALUENAME max_exchange_indexing_rate
-+ MIN 1 MAX 1000 DEFAULT 60 SPIN 1
-+ END PART
-+ END POLICY
-+
-+ POLICY !!Pol_EnableSafeweb
-+ EXPLAIN !!Explain_Safeweb
-+ VALUENAME safe_browsing
-+ VALUEON NUMERIC 1
-+ VALUEOFF NUMERIC 0
-+ END POLICY
-+
-+ END CATEGORY ; Enterprise
-+
-+ END CATEGORY ; GoogleDesktopSearch
-+ END CATEGORY ; Google
-+
-+
-+CLASS USER
-+ CATEGORY !!Cat_Google
-+ CATEGORY !!Cat_GoogleDesktopSearch
-+ KEYNAME "Software\Policies\Google\Google Desktop"
-+
-+ CATEGORY !!Cat_Preferences
-+ KEYNAME "Software\Policies\Google\Google Desktop\Preferences"
-+
-+ CATEGORY !!Cat_IndexAndCaptureControl
-+ POLICY !!Blacklist_Email
-+ EXPLAIN !!Explain_Blacklist_Email
-+ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-1"
-+ VALUENAME "1"
-+ END POLICY
-+
-+ POLICY !!Blacklist_Gmail
-+ EXPLAIN !!Explain_Blacklist_Gmail
-+ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-pop"
-+ VALUENAME "gmail"
-+ END POLICY
-+
-+ POLICY !!Blacklist_WebHistory
-+ EXPLAIN !!Explain_Blacklist_WebHistory
-+ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-1"
-+ VALUENAME "2"
-+ END POLICY
-+
-+ POLICY !!Blacklist_Chat
-+ EXPLAIN !!Explain_Blacklist_Chat
-+ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-1"
-+ ACTIONLISTON
-+ VALUENAME "3" VALUE NUMERIC 1
-+ END ACTIONLISTON
-+ END POLICY
-+
-+ POLICY !!Blacklist_Text
-+ EXPLAIN !!Explain_Blacklist_Text
-+ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-1"
-+ ACTIONLISTON
-+ VALUENAME "4" VALUE NUMERIC 1
-+ END ACTIONLISTON
-+ END POLICY
-+
-+ POLICY !!Blacklist_Media
-+ EXPLAIN !!Explain_Blacklist_Media
-+ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-1"
-+ ACTIONLISTON
-+ VALUENAME "5" VALUE NUMERIC 1
-+ END ACTIONLISTON
-+ END POLICY
-+
-+ POLICY !!Blacklist_Contact
-+ EXPLAIN !!Explain_Blacklist_Contact
-+ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-1"
-+ ACTIONLISTON
-+ VALUENAME "9" VALUE NUMERIC 1
-+ END ACTIONLISTON
-+ END POLICY
-+
-+ POLICY !!Blacklist_Calendar
-+ EXPLAIN !!Explain_Blacklist_Calendar
-+ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-1"
-+ ACTIONLISTON
-+ VALUENAME "10" VALUE NUMERIC 1
-+ END ACTIONLISTON
-+ END POLICY
-+
-+ POLICY !!Blacklist_Task
-+ EXPLAIN !!Explain_Blacklist_Task
-+ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-1"
-+ ACTIONLISTON
-+ VALUENAME "11" VALUE NUMERIC 1
-+ END ACTIONLISTON
-+ END POLICY
-+
-+ POLICY !!Blacklist_Note
-+ EXPLAIN !!Explain_Blacklist_Note
-+ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-1"
-+ ACTIONLISTON
-+ VALUENAME "12" VALUE NUMERIC 1
-+ END ACTIONLISTON
-+ END POLICY
-+
-+ POLICY !!Blacklist_Journal
-+ EXPLAIN !!Explain_Blacklist_Journal
-+ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-1"
-+ ACTIONLISTON
-+ VALUENAME "13" VALUE NUMERIC 1
-+ END ACTIONLISTON
-+ END POLICY
-+
-+ POLICY !!Blacklist_Word
-+ EXPLAIN !!Explain_Blacklist_Word
-+ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-2"
-+ VALUENAME "DOC"
-+ END POLICY
-+
-+ POLICY !!Blacklist_Excel
-+ EXPLAIN !!Explain_Blacklist_Excel
-+ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-2"
-+ VALUENAME "XLS"
-+ END POLICY
-+
-+ POLICY !!Blacklist_Powerpoint
-+ EXPLAIN !!Explain_Blacklist_Powerpoint
-+ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-2"
-+ VALUENAME "PPT"
-+ END POLICY
-+
-+ POLICY !!Blacklist_PDF
-+ EXPLAIN !!Explain_Blacklist_PDF
-+ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-2"
-+ VALUENAME "PDF"
-+ END POLICY
-+
-+ POLICY !!Blacklist_ZIP
-+ EXPLAIN !!Explain_Blacklist_ZIP
-+ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-2"
-+ VALUENAME "ZIP"
-+ END POLICY
-+
-+ POLICY !!Blacklist_HTTPS
-+ EXPLAIN !!Explain_Blacklist_HTTPS
-+ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-3"
-+ VALUENAME "HTTPS"
-+ END POLICY
-+
-+ POLICY !!Blacklist_PasswordProtectedOffice
-+ EXPLAIN !!Explain_Blacklist_PasswordProtectedOffice
-+ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-13"
-+ VALUENAME "SECUREOFFICE"
-+ END POLICY
-+
-+ POLICY !!Blacklist_URI_Contains
-+ EXPLAIN !!Explain_Blacklist_URI_Contains
-+ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\blacklist-6"
-+ PART !!Blacklist_URI_Contains LISTBOX
-+ END PART
-+ END POLICY
-+
-+ POLICY !!Blacklist_Extensions
-+ EXPLAIN !!Explain_Blacklist_Extensions
-+ PART !!Blacklist_Extensions EDITTEXT
-+ VALUENAME "file_extensions_to_skip"
-+ END PART
-+ END POLICY
-+
-+ POLICY !!Pol_Disallow_UserSearchLocations
-+ EXPLAIN !!Explain_Disallow_UserSearchLocations
-+ VALUENAME user_search_locations
-+ VALUEON NUMERIC 1
-+ END POLICY
-+
-+ POLICY !!Pol_Search_Location_Whitelist
-+ EXPLAIN !!Explain_Search_Location_Whitelist
-+ KEYNAME "Software\Policies\Google\Google Desktop\Preferences\policy_search_location_whitelist"
-+ PART !!Search_Locations_Whitelist LISTBOX
-+ END PART
-+ END POLICY
-+
-+ POLICY !!Email_Retention
-+ EXPLAIN !!Explain_Email_Retention
-+ PART !!Email_Retention_Edit NUMERIC
-+ VALUENAME "email_days_to_retain"
-+ MIN 1 MAX 65535 DEFAULT 30 SPIN 1
-+ END PART
-+ END POLICY
-+
-+ POLICY !!Webpage_Retention
-+ EXPLAIN !!Explain_Webpage_Retention
-+ PART !!Webpage_Retention_Edit NUMERIC
-+ VALUENAME "webpage_days_to_retain"
-+ MIN 1 MAX 65535 DEFAULT 30 SPIN 1
-+ END PART
-+ END POLICY
-+
-+ POLICY !!File_Retention
-+ EXPLAIN !!Explain_File_Retention
-+ PART !!File_Retention_Edit NUMERIC
-+ VALUENAME "file_days_to_retain"
-+ MIN 1 MAX 65535 DEFAULT 30 SPIN 1
-+ END PART
-+ END POLICY
-+
-+ POLICY !!IM_Retention
-+ EXPLAIN !!Explain_IM_Retention
-+ PART !!IM_Retention_Edit NUMERIC
-+ VALUENAME "im_days_to_retain"
-+ MIN 1 MAX 65535 DEFAULT 30 SPIN 1
-+ END PART
-+ END POLICY
-+
-+ POLICY !!Pol_Remove_Deleted_Items
-+ EXPLAIN !!Explain_Remove_Deleted_Items
-+ VALUENAME remove_deleted_items
-+ VALUEON NUMERIC 1
-+ END POLICY
-+
-+ POLICY !!Pol_Allow_Simultaneous_Indexing
-+ EXPLAIN !!Explain_Allow_Simultaneous_Indexing
-+ VALUENAME simultaneous_indexing
-+ VALUEON NUMERIC 1
-+ END POLICY
-+
-+ END CATEGORY
-+
-+ POLICY !!Pol_TurnOffAdvancedFeatures
-+ EXPLAIN !!Explain_TurnOffAdvancedFeatures
-+ VALUENAME error_report_on
-+ VALUEON NUMERIC 0
-+ END POLICY
-+
-+ POLICY !!Pol_TurnOffImproveGd
-+ EXPLAIN !!Explain_TurnOffImproveGd
-+ VALUENAME improve_gd
-+ VALUEON NUMERIC 0
-+ VALUEOFF NUMERIC 1
-+ END POLICY
-+
-+ POLICY !!Pol_NoPersonalizationInfo
-+ EXPLAIN !!Explain_NoPersonalizationInfo
-+ VALUENAME send_personalization_info
-+ VALUEON NUMERIC 0
-+ VALUEOFF NUMERIC 1
-+ END POLICY
-+
-+ POLICY !!Pol_OneBoxMode
-+ EXPLAIN !!Explain_OneBoxMode
-+ VALUENAME onebox_mode
-+ VALUEON NUMERIC 0
-+ END POLICY
-+
-+ POLICY !!Pol_EncryptIndex
-+ EXPLAIN !!Explain_EncryptIndex
-+ VALUENAME encrypt_index
-+ VALUEON NUMERIC 1
-+ END POLICY
-+
-+ POLICY !!Pol_Hyper
-+ EXPLAIN !!Explain_Hyper
-+ VALUENAME hyper_off
-+ VALUEON NUMERIC 1
-+ END POLICY
-+
-+ POLICY !!Pol_Display_Mode
-+ EXPLAIN !!Explain_Display_Mode
-+ PART !!Pol_Display_Mode DROPDOWNLIST
-+ VALUENAME display_mode
-+ ITEMLIST
-+ NAME !!Sidebar VALUE NUMERIC 1
-+ NAME !!Deskbar VALUE NUMERIC 8
-+ NAME !!FloatingDeskbar VALUE NUMERIC 4
-+ NAME !!None VALUE NUMERIC 0
-+ END ITEMLIST
-+ END PART
-+ END POLICY
-+
-+ END CATEGORY ; Preferences
-+
-+ CATEGORY !!Cat_Enterprise
-+ KEYNAME "Software\Policies\Google\Google Desktop\Enterprise"
-+
-+ POLICY !!Pol_Autoupdate
-+ EXPLAIN !!Explain_Autoupdate
-+ VALUENAME autoupdate_host
-+ VALUEON ""
-+ END POLICY
-+
-+ POLICY !!Pol_AutoupdateAsSystem
-+ EXPLAIN !!Explain_AutoupdateAsSystem
-+ VALUENAME autoupdate_impersonate_user
-+ VALUEON NUMERIC 0
-+ VALUEOFF NUMERIC 1
-+ END POLICY
-+
-+ POLICY !!Pol_EnterpriseTab
-+ EXPLAIN !!Explain_EnterpriseTab
-+ PART !!EnterpriseTabText EDITTEXT
-+ VALUENAME enterprise_tab_text
-+ END PART
-+ PART !!EnterpriseTabHomepage EDITTEXT
-+ VALUENAME enterprise_tab_homepage
-+ END PART
-+ PART !!EnterpriseTabHomepageQuery CHECKBOX
-+ VALUENAME enterprise_tab_homepage_query
-+ END PART
-+ PART !!EnterpriseTabResults EDITTEXT
-+ VALUENAME enterprise_tab_results
-+ END PART
-+ PART !!EnterpriseTabResultsQuery CHECKBOX
-+ VALUENAME enterprise_tab_results_query
-+ END PART
-+ END POLICY
-+
-+ POLICY !!Pol_GSAHosts
-+ EXPLAIN !!Explain_GSAHosts
-+ KEYNAME "Software\Policies\Google\Google Desktop\Enterprise\GSAHosts"
-+ PART !!Pol_GSAHosts LISTBOX
-+ END PART
-+ END POLICY
-+
-+ POLICY !!Pol_Disallow_Gadgets
-+ EXPLAIN !!Explain_Disallow_Gadgets
-+ VALUENAME disallow_gadgets
-+ VALUEON NUMERIC 1
-+ PART !!Disallow_Only_Non_Builtin_Gadgets CHECKBOX DEFCHECKED
-+ VALUENAME disallow_only_non_builtin_gadgets
-+ VALUEON NUMERIC 1
-+ VALUEOFF NUMERIC 0
-+ END PART
-+ END POLICY
-+
-+ POLICY !!Pol_Gadget_Whitelist
-+ EXPLAIN !!Explain_Gadget_Whitelist
-+ KEYNAME "Software\Policies\Google\Google Desktop\Enterprise\gadget_whitelist"
-+ PART !!Pol_Gadget_Whitelist LISTBOX
-+ END PART
-+ END POLICY
-+
-+ POLICY !!Pol_Gadget_Install_Confirmation_Whitelist
-+ EXPLAIN !!Explain_Gadget_Install_Confirmation_Whitelist
-+ KEYNAME "Software\Policies\Google\Google Desktop\Enterprise\install_confirmation_whitelist"
-+ PART !!Pol_Gadget_Install_Confirmation_Whitelist LISTBOX
-+ END PART
-+ END POLICY
-+
-+ POLICY !!Pol_Alternate_User_Data_Dir
-+ EXPLAIN !!Explain_Alternate_User_Data_Dir
-+ PART !!Pol_Alternate_User_Data_Dir EDITTEXT
-+ VALUENAME alternate_user_data_dir
-+ END PART
-+ END POLICY
-+
-+ POLICY !!Pol_MaxAllowedOutlookConnections
-+ EXPLAIN !!Explain_MaxAllowedOutlookConnections
-+ PART !!Pol_MaxAllowedOutlookConnections NUMERIC
-+ VALUENAME max_allowed_outlook_connections
-+ MIN 1 MAX 65535 DEFAULT 400 SPIN 1
-+ END PART
-+ END POLICY
-+
-+ POLICY !!Pol_DisallowSsdService
-+ EXPLAIN !!Explain_DisallowSsdService
-+ VALUENAME disallow_ssd_service
-+ VALUEON NUMERIC 1
-+ END POLICY
-+
-+ POLICY !!Pol_DisallowSsdOutbound
-+ EXPLAIN !!Explain_DisallowSsdOutbound
-+ VALUENAME disallow_ssd_outbound
-+ VALUEON NUMERIC 1
-+ END POLICY
-+
-+ POLICY !!Pol_Disallow_Store_Gadget_Service
-+ EXPLAIN !!Explain_Disallow_Store_Gadget_Service
-+ VALUENAME disallow_store_gadget_service
-+ VALUEON NUMERIC 1
-+ END POLICY
-+
-+ POLICY !!Pol_MaxExchangeIndexingRate
-+ EXPLAIN !!Explain_MaxExchangeIndexingRate
-+ PART !!Pol_MaxExchangeIndexingRate NUMERIC
-+ VALUENAME max_exchange_indexing_rate
-+ MIN 1 MAX 1000 DEFAULT 60 SPIN 1
-+ END PART
-+ END POLICY
-+
-+ POLICY !!Pol_EnableSafeweb
-+ EXPLAIN !!Explain_Safeweb
-+ VALUENAME safe_browsing
-+ VALUEON NUMERIC 1
-+ VALUEOFF NUMERIC 0
-+ END POLICY
-+
-+ END CATEGORY ; Enterprise
-+
-+ END CATEGORY ; GoogleDesktopSearch
-+ END CATEGORY ; Google
-+
-+;------------------------------------------------------------------------------
-+
-+[strings]
-+Cat_Google="Google"
-+Cat_GoogleDesktopSearch="Google Desktop"
-+
-+;------------------------------------------------------------------------------
-+; Preferences
-+;------------------------------------------------------------------------------
-+Cat_Preferences="Preferences"
-+Explain_Preferences="Controls Google Desktop preferences"
-+
-+Cat_IndexAndCaptureControl="Indexing and Capture Control"
-+Explain_IndexAndCaptureControl="Controls what files, web pages, and other content will be indexed by Google Desktop."
-+
-+Blacklist_Email="Prevent indexing of email"
-+Explain_Blacklist_Email="Enabling this policy will prevent Google Desktop from indexing emails.\n\nIf this policy is not configured, the user can choose whether or not to index emails."
-+Blacklist_Gmail="Prevent indexing of Gmail"
-+Explain_Blacklist_Gmail="Enabling this policy prevents Google Desktop from indexing Gmail messages.\n\nThis policy is in effect only when the policy "Prevent indexing of email" is disabled. When that policy is enabled, all email indexing is disabled, including Gmail indexing.\n\nIf both this policy and "Prevent indexing of email" are disabled or not configured, a user can choose whether or not to index Gmail messages."
-+Blacklist_WebHistory="Prevent indexing of web pages"
-+Explain_Blacklist_WebHistory="Enabling this policy will prevent Google Desktop from indexing web pages.\n\nIf this policy is disabled or not configured, the user can choose whether or not to index web pages."
-+Blacklist_Text="Prevent indexing of text files"
-+Explain_Blacklist_Text="Enabling this policy will prevent Google Desktop from indexing text files.\n\nIf this policy is disabled or not configured, the user can choose whether or not to index text files."
-+Blacklist_Media="Prevent indexing of media files"
-+Explain_Blacklist_Media="Enabling this policy will prevent Google Desktop from indexing media files.\n\nIf this policy is disabled or not configured, the user can choose whether or not to index media files."
-+Blacklist_Contact="Prevent indexing of contacts"
-+Explain_Blacklist_Contact="Enabling this policy will prevent Google Desktop from indexing contacts.\n\nIf this policy is disabled or not configured, the user can choose whether or not to index contacts."
-+Blacklist_Calendar="Prevent indexing of calendar entries"
-+Explain_Blacklist_Calendar="Enabling this policy will prevent Google Desktop from indexing calendar entries.\n\nIf this policy is disabled or not configured, the user can choose whether or not to index calendar entries."
-+Blacklist_Task="Prevent indexing of tasks"
-+Explain_Blacklist_Task="Enabling this policy will prevent Google Desktop from indexing tasks.\n\nIf this policy is disabled or not configured, the user can choose whether or not to index tasks."
-+Blacklist_Note="Prevent indexing of notes"
-+Explain_Blacklist_Note="Enabling this policy will prevent Google Desktop from indexing notes.\n\nIf this policy is disabled or not configured, the user can choose whether or not to index notes."
-+Blacklist_Journal="Prevent indexing of journal entries"
-+Explain_Blacklist_Journal="Enabling this policy will prevent Google Desktop from indexing journal entries.\n\nIf this policy is disabled or not configured, the user can choose whether or not to index journal entries."
-+Blacklist_Word="Prevent indexing of Word documents"
-+Explain_Blacklist_Word="Enabling this policy will prevent Google Desktop from indexing Word documents.\n\nIf this policy is disabled or not configured, the user can choose whether or not to index Word documents."
-+Blacklist_Excel="Prevent indexing of Excel documents"
-+Explain_Blacklist_Excel="Enabling this policy will prevent Google Desktop from indexing Excel documents.\n\nIf this policy is disabled or not configured, the user can choose whether or not to index Excel documents."
-+Blacklist_Powerpoint="Prevent indexing of PowerPoint documents"
-+Explain_Blacklist_Powerpoint="Enabling this policy will prevent Google Desktop from indexing PowerPoint documents.\n\nIf this policy is disabled or not configured, the user can choose whether or not to index PowerPoint documents."
-+Blacklist_PDF="Prevent indexing of PDF documents"
-+Explain_Blacklist_PDF="Enabling this policy will prevent Google Desktop from indexing PDF documents.\n\nIf this policy is disabled or not configured, the user can choose whether or not to index PDF documents."
-+Blacklist_ZIP="Prevent indexing of ZIP files"
-+Explain_Blacklist_ZIP="Enabling this policy will prevent Google Desktop from indexing ZIP files.\n\nIf this policy is disabled or not configured, the user can choose whether or not to index ZIP files."
-+Blacklist_HTTPS="Prevent indexing of secure web pages"
-+Explain_Blacklist_HTTPS="Enabling this policy will prevent Google Desktop from indexing secure web pages (pages with HTTPS in the URL).\n\nIf this policy is disabled or not configured, the user can choose whether or not to index secure web pages."
-+Blacklist_URI_Contains="Prevent indexing of specific web sites and folders"
-+Explain_Blacklist_URI_Contains="This policy allows you to prevent Google Desktop from indexing specific websites or folders. If an item's URL or path name contains any of these specified strings, it will not be indexed. These restrictions will be applied in addition to any websites or folders that the user has specified.\n\nThis policy has no effect when disabled or not configured."
-+Blacklist_Chat="Prevent indexing of IM chats"
-+Explain_Blacklist_Chat="Enabling this policy will prevent Google Desktop from indexing IM chat conversations.\n\nIf this policy is disabled or not configured, the user can choose whether or not to index IM chat conversations."
-+Blacklist_PasswordProtectedOffice="Prevent indexing of password-protected Office documents (Word, Excel)"
-+Explain_Blacklist_PasswordProtectedOffice="Enabling this policy will prevent Google Desktop from indexing password-protected office documents.\n\nIf this policy is disabled or not configured, the user can choose whether or not to index password-protected office documents."
-+Blacklist_Extensions="Prevent indexing of specific file extensions"
-+Explain_Blacklist_Extensions="This policy allows you to prevent Google Desktop from indexing files with specific extensions. Enter a list of file extensions, separated by commas, that you wish to exclude from indexing.\n\nThis policy has no effect when disabled or not configured."
-+Pol_Disallow_UserSearchLocations="Disallow adding search locations for indexing"
-+Explain_Disallow_UserSearchLocations="Enabling this policy will prevent the user from specifying additional drives or networked folders to be indexed by Google Desktop.\n\nIf this policy is disabled or not configured, users may specify additional drives and networked folders to be indexed."
-+Pol_Search_Location_Whitelist="Allow indexing of specific folders"
-+Explain_Search_Location_Whitelist="This policy allows you to add additional drives and networked folders to index."
-+Search_Locations_Whitelist="Search these locations"
-+Email_Retention="Only retain emails that are less than x days old"
-+Explain_Email_Retention="This policy allows you to configure Google Desktop to only retain emails that are less than the specified number of days old in the index. Enter the number of days to retain emails for\n\nThis policy has no effect when disabled or not configured."
-+Email_Retention_Edit="Number of days to retain emails"
-+Webpage_Retention="Only retain webpages that are less than x days old"
-+Explain_Webpage_Retention="This policy allows you to configure Google Desktop to only retain webpages that are less than the specified number of days old in the index. Enter the number of days to retain webpages for\n\nThis policy has no effect when disabled or not configured."
-+Webpage_Retention_Edit="Number of days to retain webpages"
-+File_Retention="Only retain files that are less than x days old"
-+Explain_File_Retention="This policy allows you to configure Google Desktop to only retain files that are less than the specified number of days old in the index. Enter the number of days to retain files for\n\nThis policy has no effect when disabled or not configured."
-+File_Retention_Edit="Number of days to retain files"
-+IM_Retention="Only retain IM that are less than x days old"
-+Explain_IM_Retention="This policy allows you to configure Google Desktop to only retain IM that are less than the specified number of days old in the index. Enter the number of days to retain IM for\n\nThis policy has no effect when disabled or not configured."
-+IM_Retention_Edit="Number of days to retain IM"
-+
-+Pol_Remove_Deleted_Items="Remove deleted items from the index."
-+Explain_Remove_Deleted_Items="Enabling this policy will remove all deleted items from the index and cache. Any items that are deleted will no longer be searchable."
-+
-+Pol_Allow_Simultaneous_Indexing="Allow historical indexing for multiple users simultaneously."
-+Explain_Allow_Simultaneous_Indexing="Enabling this policy will allow a computer to generate first-time indexes for multiple users simultaneously. \n\nIf this policy is disabled or not configured, historical indexing will happen only for the logged-in user that was connected last; historical indexing for any other logged-in user will happen the next time that other user connects."
-+
-+Pol_TurnOffAdvancedFeatures="Turn off Advanced Features options"
-+Explain_TurnOffAdvancedFeatures="Enabling this policy will prevent Google Desktop from sending Advanced Features data to Google (for either improvements or personalization), and users won't be able to change these options. Enabling this policy also prevents older versions of Google Desktop from sending data.\n\nIf this policy is disabled or not configured and the user has a pre-5.5 version of Google Desktop, the user can choose whether or not to enable sending data to Google. If the user has version 5.5 or later, the 'Turn off Improve Google Desktop option' and 'Do not send personalization info' policies will be used instead."
-+
-+Pol_TurnOffImproveGd="Turn off Improve Google Desktop option"
-+Explain_TurnOffImproveGd="Enabling this policy will prevent Google Desktop from sending improvement data, including crash reports and anonymous usage data, to Google.\n\nIf this policy is disabled, improvement data will be sent to Google and the user won't be able to change the option.\n\nIf this policy is not configured, the user can choose whether or not to enable the Improve Google Desktop option.\n\nNote that this policy applies only to version 5.5 or later and doesn't affect previous versions of Google Desktop.\n\nAlso note that this policy can be overridden by the 'Turn off Advanced Features options' policy."
-+
-+Pol_NoPersonalizationInfo="Do not send personalization info"
-+Explain_NoPersonalizationInfo="Enabling this policy will prevent Google Desktop from displaying personalized content, such as news that reflects the user's past interest in articles. Personalized content is derived from anonymous usage data sent to Google.\n\nIf this policy is disabled, personalized content will be displayed for all users, and users won't be able to disable this feature.\n\nIf this policy is not configured, users can choose whether or not to enable personalization in each gadget that supports this feature.\n\nNote that this policy applies only to version 5.5 or later and doesn't affect previous versions of Google Desktop.\n\nAlso note that this policy can be overridden by the 'Turn off Advanced Features options' policy."
-+
-+Pol_OneBoxMode="Turn off Google Web Search Integration"
-+Explain_OneBoxMode="Enabling this policy will prevent Google Desktop from displaying Desktop Search results in queries to google.com.\n\nIf this policy is disabled or not configured, the user can choose whether or not to include Desktop Search results in queries to google.com."
-+
-+Pol_EncryptIndex="Encrypt index data"
-+Explain_EncryptIndex="Enabling this policy will cause Google Desktop to turn on Windows file encryption for the folder containing the Google Desktop index and related user data the next time it is run.\n\nNote that Windows EFS is only available on NTFS volumes. If the user's data is stored on a FAT volume, this policy will have no effect.\n\nThis policy has no effect when disabled or not configured."
-+
-+Pol_Hyper="Turn off Quick Find"
-+Explain_Hyper="Enabling this policy will cause Google Desktop to turn off Quick Find feature. Quick Find allows you to see results as you type.\n\nIf this policy is disabled or not configured, the user can choose whether or not to enable it."
-+
-+Pol_Display_Mode="Choose display option"
-+Explain_Display_Mode="This policy sets the Google Desktop display option: Sidebar, Deskbar, Floating Deskbar or none.\n\nNote that on 64-bit systems, a setting of Deskbar will be interpreted as Floating Deskbar.\n\nIf this policy is disabled or not configured, the user can choose a display option."
-+Sidebar="Sidebar"
-+Deskbar="Deskbar"
-+FloatingDeskbar="Floating Deskbar"
-+None="None"
-+
-+;------------------------------------------------------------------------------
-+; Enterprise
-+;------------------------------------------------------------------------------
-+Cat_Enterprise="Enterprise Integration"
-+Explain_Enterprise="Controls features specific to Enterprise installations of Google Desktop"
-+
-+Pol_Autoupdate="Block Auto-update"
-+Explain_Autoupdate="Enabling this policy prevents Google Desktop from automatically checking for and installing updates from google.com.\n\nIf you enable this policy, you must distribute updates to Google Desktop using Group Policy, SMS, or a similar enterprise software distribution mechanism. You should check http://desktop.google.com/enterprise/ for updates.\n\nIf this policy is disabled or not configured, Google Desktop will periodically check for updates from desktop.google.com."
-+
-+Pol_AutoupdateAsSystem="Use system proxy settings when auto-updating"
-+Explain_AutoupdateAsSystem="Enabling this policy makes Google Desktop use the machine-wide proxy settings (as specified using e.g. proxycfg.exe) when performing autoupdates (if enabled).\n\nIf this policy is disabled or not configured, Google Desktop will use the logged-on user's Internet Explorer proxy settings when checking for auto-updates (if enabled)."
-+
-+Pol_EnterpriseTab="Enterprise search tab"
-+Explain_EnterpriseTab="This policy allows you to add a search tab for your Google Search Appliance to Google Desktop and google.com web pages.\n\nYou must provide the name of the tab, such as "Intranet", as well as URLs for the search homepage and for retrieving search results. Use [DISP_QUERY] in place of the query term for the search results URL.\n\nSee the administrator's guide for more details."
-+EnterpriseTabText="Tab name"
-+EnterpriseTabHomepage="Search homepage URL"
-+EnterpriseTabHomepageQuery="Check if search homepage supports '&&q=<query>'"
-+EnterpriseTabResults="Search results URL"
-+EnterpriseTabResultsQuery="Check if search results page supports '&&q=<query>'"
-+
-+Pol_GSAHosts="Google Search Appliances"
-+Explain_GSAHosts="This policy allows you to list any Google Search Appliances in your intranet. When properly configured, Google Desktop will insert Google Desktop results into the results of queries on the Google Search Appliance"
-+
-+Pol_PolicyUnawareClientProhibitedFlag="Prohibit Policy-Unaware versions"
-+Explain_PolicyUnawareClientProhibitedFlag="Prohibits installation and execution of versions of Google Desktop that are unaware of group policy.\n\nEnabling this policy will prevent users from installing or running version 1.0 of Google Desktop.\n\nThis policy has no effect when disabled or not configured."
-+
-+Pol_MinimumAllowedVersion="Minimum allowed version"
-+Explain_MinimumAllowedVersion="This policy allows you to prevent installation and/or execution of older versions of Google Desktop by specifying the minimum version you wish to allow. When enabling this policy, you should also enable the "Prohibit Policy-Unaware versions" policy to block versions of Google Desktop that did not support group policy.\n\nThis policy has no effect when disabled or not configured."
-+
-+Pol_MaximumAllowedVersion="Maximum allowed version"
-+Explain_MaximumAllowedVersion="This policy allows you to prevent installation and/or execution of newer versions of Google Desktop by specifying the maximum version you wish to allow.\n\nThis policy has no effect when disabled or not configured."
-+
-+Pol_Disallow_Gadgets="Disallow gadgets and indexing plug-ins"
-+Explain_Disallow_Gadgets="This policy prevents the use of all Google Desktop gadgets and indexing plug-ins. The policy applies to gadgets that are included in the Google Desktop installation package (built-in gadgets), built-in indexing plug-ins (currently only the Lotus Notes plug-in), and to gadgets or indexing plug-ins that a user might want to add later (non-built-in gadgets and indexing plug-ins).\n\nYou can prohibit use of all non-built-in gadgets and indexing plug-ins, but allow use of built-in gadgets and indexing plug-ins. To do so, enable this policy and then select the option "Disallow only non-built-in gadgets and indexing plug-ins.\n\nYou can supersede this policy to allow specified built-in and non-built-in gadgets and indexing plug-ins. To do so, enable this policy and then specify the gadgets and/or indexing plug-ins you want to allow under "Gadget and Plug-in Whitelist.""
-+Disallow_Only_Non_Builtin_Gadgets="Disallow only non-built-in gadgets and indexing plug-ins"
-+
-+Pol_Gadget_Whitelist="Gadget and plug-in whitelist"
-+Explain_Gadget_Whitelist="This policy specifies a list of Google Desktop gadgets and indexing plug-ins that you want to allow, as exceptions to the "Disallow gadgets and indexing plug-ins" policy. This policy is valid only when the "Disallow gadgets and indexing plug-ins" policy is enabled.\n\nFor each gadget or indexing plug-in you wish to allow, add the CLSID or PROGID of the gadget or indexing plug-in (see the administrator's guide for more details).\n\nThis policy has no effect when disabled or not configured."
-+
-+Pol_Gadget_Install_Confirmation_Whitelist="Allow silent installation of gadgets"
-+Explain_Gadget_Install_Confirmation_Whitelist="Enabling this policy lets you specify a list of Google Desktop gadgets or indexing plug-ins that can be installed without confirmation from the user.\n\nAdd a gadget or indexing plug-in by placing its class ID (CLSID) or program identifier (PROGID) in the list, surrounded with curly braces ({ }).\n\nThis policy has no effect when disabled or not configured."
-+
-+Pol_Alternate_User_Data_Dir="Alternate user data directory"
-+Explain_Alternate_User_Data_Dir="This policy allows you to specify a directory to be used to store user data for Google Desktop (such as index data and cached documents).\n\nYou may use [USER_NAME] or [DOMAIN_NAME] in the path to specify the current user's name or domain. If [USER_NAME] is not specified, the user name will be appended at the end of the path.\n\nThis policy has no effect when disabled or not configured."
-+
-+Pol_MaxAllowedOutlookConnections="Maximum allowed Outlook connections"
-+Explain_MaxAllowedOutlookConnections="This policy specifies the maximum number of open connections that Google Desktop maintains with the Exchange server. Google Desktop opens a connection for each email folder that it indexes. If insufficient connections are allowed, Google Desktop cannot index all the user email folders.\n\nThe default value is 400. Because users rarely have as many as 400 email folders, Google Desktop rarely reaches the limit.\n\nIf you set this policy's value above 400, you must also configure the number of open connections between Outlook and the Exchange server. By default, approximately 400 connections are allowed. If Google Desktop uses too many of these connections, Outlook might be unable to access email.\n\nThis policy has no effect when disabled or not configured."
-+
-+Pol_DisallowSsdService="Disallow sharing and receiving of web history and documents across computers"
-+Explain_DisallowSsdService="Enabling this policy will prevent Google Desktop from sharing the user's web history and document contents across the user's different Google Desktop installations, and will also prevent it from receiving such shared items from the user's other machines. To allow reception but disallow sharing, use DisallowSsdOutbound.\nThis policy has no effect when disabled or not configured."
-+
-+Pol_DisallowSsdOutbound="Disallow sharing of web history and documents to user's other computers."
-+Explain_DisallowSsdOutbound="Enabling this policy will prevent Google Desktop from sending the user's web history and document contents from this machine to the user's other machines. It does not prevent reception of items from the user's other machines; to disallow both, use DisallowSsdService.\nThis policy has no effect when disabled or not configured."
-+
-+Pol_Disallow_Store_Gadget_Service="Disallow storage of gadget content and settings."
-+Explain_Disallow_Store_Gadget_Service="Enabling this policy will prevent users from storing their gadget content and settings with Google. Users will be unable to access their gadget content and settings from other computers and all content and settings will be lost if Google Desktop is uninstalled."
-+
-+Pol_MaxExchangeIndexingRate="Maximum allowed Exchange indexing rate"
-+Explain_MaxExchangeIndexingRate="This policy allows you to specify the maximum number of emails that are indexed per minute. \n\nThis policy has no effect when disabled or not configured."
-+
-+Pol_EnableSafeweb="Enable or disable safe browsing"
-+Explain_Safeweb="Google Desktop safe browsing informs the user whenever they visit any site which is a suspected forgery site or may harm their computer. Enabling this policy turns on safe browsing; disabling the policy turns it off. \n\nIf this policy is not configured, the user can select whether to turn on safe browsing."
-\ No newline at end of file
-diff --git a/tools/grit/grit/testdata/README.txt b/tools/grit/grit/testdata/README.txt
-new file mode 100644
-index 0000000000..a683b3b9e3
---- /dev/null
-+++ b/tools/grit/grit/testdata/README.txt
-@@ -0,0 +1,87 @@
-+Google Desktop for Enterprise
-+Copyright (C) 2007 Google Inc.
-+All Rights Reserved
-+
-+---------
-+Contents
-+---------
-+This distribution contains the following files:
-+
-+GoogleDesktopSetup.msi - Installation and setup program
-+GoogleDesktop.adm - Group Policy administrative template file
-+AdminGuide.pdf - Google Desktop for Enterprise administrative guide
-+
-+
-+--------------
-+Documentation
-+--------------
-+Full documentation and installation instructions are in the
-+administrative guide, and also online at
-+http://desktop.google.com/enterprise/adminguide.html.
-+
-+
-+------------------------
-+IBM Lotus Notes Plug-In
-+------------------------
-+The Lotus Notes plug-in is included in the release of Google
-+Desktop for Enterprise. The IBM Lotus Notes Plug-in for Google
-+Desktop indexes mail, calendar, task, contact and journal
-+documents from Notes. Discussion documents including those from
-+the discussion and team room templates can also be indexed by
-+selecting an option from the preferences. Once indexed, this data
-+will be returned in Google Desktop searches. The corresponding
-+document can be opened in Lotus Notes from the Google Desktop
-+results page.
-+
-+Install: The plug-in will install automatically during the Google
-+Desktop setup process if Lotus Notes is already installed. Lotus
-+Notes must not be running in order for the install to occur. The
-+Class ID for this plug-in is {8F42BDFB-33E8-427B-AFDC-A04E046D3F07}.
-+
-+Preferences: Preferences and selection of databases to index are
-+set in the 'Google Desktop for Notes' dialog reached through the
-+'Actions' menu.
-+
-+Reindexing: Selecting 'Reindex all databases' will index all the
-+documents in each database again.
-+
-+
-+Notes Plug-in Known Issues
-+---------------------------
-+
-+If the 'Google Desktop for Notes' item is not available from the
-+Lotus Notes Actions menu, then installation was not successful.
-+Installation consists of writing one file, notesgdsplugin.dll, to
-+the Notes application directory and a setting to the notes.ini
-+configuration file. The most likely cause of an unsuccessful
-+installation is that the installer was not able to locate the
-+notes.ini file. Installation will complete if the user closes Notes
-+and manually adds the following setting to this file on a new line:
-+AddinMenus=notesgdsplugin.dll
-+
-+If the notesgdsplugin.dll file is not in the application directory
-+(e.g., C:\Program Files\Lotus\Notes) after Google Desktop
-+installation, it is likely that Notes was not installed correctly.
-+
-+Only local databases can be indexed. If they can be determined,
-+the user's local mail file and address book will be included in the
-+list automatically. Mail archives and other databases must be
-+added with the 'Add' button.
-+
-+Some users may experience performance issues during the initial
-+indexing of a database. The 'Perform the initial index of a
-+database only when I'm idle' option will limit the indexing process
-+to times when the user is not using the machine. If this does not
-+alleviate the problem or the user would like to continually index
-+but just do so more slowly or quickly, the GoogleWaitTime notes.ini
-+value can be set. Increasing the GoogleWaitTime value will slow
-+down the indexing process, and lowering the value will speed it up.
-+A value of zero causes the fastest possible indexing. Removing the
-+ini parameter altogether returns it to the default (20).
-+
-+Crashes have been known to occur with certain types of history
-+bookmarks. If the Notes client seems to crash randomly, try
-+disabling the 'Index note history' option. If it crashes before,
-+you can get to the preferences, add the following line to your
-+notes.ini file:
-+GDSNoIndexHistory=1
-diff --git a/tools/grit/grit/testdata/about.html b/tools/grit/grit/testdata/about.html
-new file mode 100644
-index 0000000000..8e5fad7b2b
---- /dev/null
-+++ b/tools/grit/grit/testdata/about.html
-@@ -0,0 +1,45 @@
-+[HEADER]
-+<table cellspacing=0 cellPadding=0 width="100%" border=0><tr bgcolor=#3399cc><td align=middle height=1><img height=1 width=1></td></tr></table>
-+<table cellspacing=0 cellPadding=1 width="100%" bgcolor=#e8f4f7 border=0><tr><td height=20><font size=+1 color=#000000>&nbsp;<b>[TITLE]</b></font></td></tr></table>
-+<br><center><span style="line-height:16pt"><font color=#335cec><B>Google Desktop Search: Search your own computer.</B></font></span></center><br>
-+
-+<table cellspacing=1 cellpadding=0 width=300 align=center border=0>
-+<tr><td nowrap><font size=-1><img style="vertical-align:middle" height=16 src="outlook.gif" width=16>&nbsp;&nbsp;Outlook Email</font></td>
-+<td nowrap>&nbsp;</td>
-+<td nowrap><font size=-1><img style="vertical-align:middle" height=16 src="netscape.gif" width=16>&nbsp;&nbsp;Netscape Mail / Thunderbird</font></td></tr>
-+
-+<tr><td nowrap><font size=-1><img style="vertical-align:middle" height=16 src="oe.gif" width=16>&nbsp;&nbsp;Outlook Express</font></td>
-+<td nowrap>&nbsp;</td>
-+<td nowrap><font size=-1><img style="vertical-align:middle" height=16 src="ff.gif" width=16>&nbsp;&nbsp;Netscape / Firefox / Mozilla</font></td></tr>
-+
-+<tr><td nowrap><font size=-1><img style="vertical-align:middle" height=16 src="doc.gif" width=16>&nbsp;&nbsp;Word</font></td>
-+<td nowrap>&nbsp;</td>
-+<td nowrap><font size=-1><img style="vertical-align:middle" height=16 src="pdf.gif" width=16>&nbsp;&nbsp;PDF</font></td></tr>
-+
-+<tr><td nowrap><font size=-1><img style="vertical-align:middle" height=16 src="xls.gif" width=16>&nbsp;&nbsp;Excel</font></td>
-+<td nowrap>&nbsp;</td>
-+<td nowrap><font size=-1><img style="vertical-align:middle" height=16 src="mus.gif" width=16>&nbsp;&nbsp;Music</font></td></tr>
-+
-+<tr><td nowrap><font size=-1><img style="vertical-align:middle" height=16 src="ppt.gif" width=16>&nbsp;&nbsp;PowerPoint</font></td>
-+<td nowrap>&nbsp;</td>
-+<td nowrap><font size=-1><img style="vertical-align:middle" height=16 src="jpg.gif" width=16>&nbsp;&nbsp;Images</font></td></tr>
-+
-+<tr><td nowrap><font size=-1><img style="vertical-align:middle" height=16 src="ie.gif" width=16>&nbsp;&nbsp;Internet Explorer</font></td>
-+<td nowrap>&nbsp;</td>
-+<td nowrap><font size=-1><img style="vertical-align:middle" height=16 src="mov.gif" width=16>&nbsp;&nbsp;Video</font></td></tr>
-+
-+<tr><td nowrap><font size=-1><img style="vertical-align:middle" height=16 src="aim.gif" width=16>&nbsp;&nbsp;AOL Instant Messenger</font></td>
-+<td nowrap>&nbsp;</td>
-+<td nowrap><font size=-1><img style="vertical-align:middle" height=16 src="other.gif" width=16>&nbsp;&nbsp;Even more with <a href="http://desktop.google.com/plugins.html">these plug-ins</A></font></td></tr>
-+
-+<tr><td nowrap><font size=-1><img style="vertical-align:middle" height=16 src="txt.gif" width=16>&nbsp;&nbsp;Text and others</font></td></tr>
-+</table>
-+<center>
-+<p><table cellpadding=1>
-+<tr><td><a href="http://desktop.google.com/gettingstarted.html?hl=[LANG_CODE]"><B>Getting Started</B></A> - Learn more about using Google Desktop Search</td></tr>
-+<tr><td><a href="http://desktop.google.com/help.html?hl=[LANG_CODE]"><B>Online Help</B></A> - Up-to-date answers to your questions</td></tr>
-+<tr><td><a href="[$~PRIVACY~$]"><B>Privacy</B></A> - A few words about privacy and Google Desktop Search</td></tr>
-+<tr><td><a href="http://desktop.google.com/uninstall.html?hl=[LANG_CODE]"><B>Uninstall</B></A> - How to uninstall Google Desktop Search</td></tr>
-+<tr><td><a href="http://desktop.google.com/feedback.html?hl=[LANG_CODE]"><B>Submit Feedback</B></A> - Send us your comments and ideas</td></tr>
-+</table><br><font size=-2>Google Desktop Search [$~BUILDNUMBER~$]</font><br><br>
-+[FOOTER]
-\ No newline at end of file
-diff --git a/tools/grit/grit/testdata/android.xml b/tools/grit/grit/testdata/android.xml
-new file mode 100644
-index 0000000000..cc3b141f70
---- /dev/null
-+++ b/tools/grit/grit/testdata/android.xml
-@@ -0,0 +1,24 @@
-+<!--
-+ Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+ Use of this source code is governed by a BSD-style license that can be
-+ found in the LICENSE file.
-+-->
-+
-+<resources>
-+ <!-- A string with placeholder. -->
-+ <string xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2" name="placeholders">
-+ Open <xliff:g id="FILENAME" example="internet.html">%s</xliff:g>?
-+ </string>
-+
-+ <!-- A simple string. -->
-+ <string name="simple">A simple string.</string>
-+
-+ <!-- A string with a comment. -->
-+ <string name="comment">Contains a <!-- ignore this --> comment. </string>
-+
-+ <!-- A second simple string. -->
-+ <string name="simple2"> Another simple string. </string>
-+
-+ <!-- A non-translatable string. -->
-+ <string name="constant" translatable="false">Do not translate me.</string>
-+</resources>
-diff --git a/tools/grit/grit/testdata/bad_browser.html b/tools/grit/grit/testdata/bad_browser.html
-new file mode 100644
-index 0000000000..e8cf34664d
---- /dev/null
-+++ b/tools/grit/grit/testdata/bad_browser.html
-@@ -0,0 +1,16 @@
-+<p><b>We're sorry, but we don't seem to be compatible.</b></p>
-+<p><font size="-1">Our software suggests that you're using a browser incompatible with Google Desktop Search.
-+ Google Desktop Search currently supports the following:</font></p>
-+<ul><font size="-1">
-+ <li>Microsoft IE 5 and newer (<a href="http://www.microsoft.com/windows/ie/downloads/default.asp">Download</a>)</li>
-+ <li>Mozilla (<a href="http://www.mozilla.org/products/mozilla1.x/">Download</a>)</li>
-+ <li>Mozilla Firefox (<a href="http://www.mozilla.org/products/firefox/">Download</a>)</li>
-+ <li>Netscape 7 and newer (<a href="http://channels.netscape.com/ns/browsers/download.jsp">Download</a>)</li>
-+</font></ul>
-+
-+<p><font size="-1">You may <a href="[REDIR]">click here</a> to use your
-+ unsupported browser, though you likely will encounter some areas that don't
-+ work as expected. You need to have Javascript enabled, regardless of the
-+ browser you use.</font>
-+<p><font size="-1">We hope to expand this list in the near future and announce new
-+ browsers as they become available.
-diff --git a/tools/grit/grit/testdata/browser.html b/tools/grit/grit/testdata/browser.html
-new file mode 100644
-index 0000000000..45d364d56f
---- /dev/null
-+++ b/tools/grit/grit/testdata/browser.html
-@@ -0,0 +1,42 @@
-+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
-+<html><head><title>[$~TITLE~$]</title>
-+<style>
-+BODY { MARGIN-LEFT: 1em; MARGIN-RIGHT: 1em }
-+BODY, TD, DIV, A { FONT-FAMILY: arial,sans-serif}
-+DIV, TD { COLOR: #000}
-+A:link { COLOR: #00c}
-+A:visited { COLOR: #551a8b}
-+A:active { COLOR: #f00 }
-+</style>
-+</head>
-+
-+<body bgcolor="#ffffff" text="#000000" link="#0000cc" vlink="#800080" alink="#ff0000" topmargin=2>
-+
-+<table cellspacing=2 cellpadding=0 width="99%" border=0>
-+<tr>
-+ <td width="1%" rowspan=2>[$~IMAGE~$]
-+ <td>&nbsp;</td>
-+ <td rowspan=2>
-+ <table cellspacing=0 cellpadding=0 width="100%" border=0>
-+ <tr>
-+ <td bgcolor=#3399cc><img height=1 width=1></td>
-+ </tr>
-+ </table>
-+ <table cellspacing=0 cellpadding=0 width="100%" border=0 bgcolor=#efefef>
-+ <tr>
-+ <td nowrap bgcolor=#E8F4F7><font face=arial,sans-serif color=#000000 size=+1><b>&nbsp;[$~CHROME_TITLE~$]</b></font></td>
-+ </tr>
-+ </table>
-+ </td>
-+</tr>
-+</table>
-+
-+<table cellpadding=3 width="94%" align="center" cellspacing=0 border=0>
-+<tr valign="middle">
-+ <td valign="top">
-+ [$~BODY~$]
-+ </td>
-+ </tr>
-+</table>
-+[$~FOOTER~$]
-+</body></html>
-\ No newline at end of file
-diff --git a/tools/grit/grit/testdata/buildinfo.grd b/tools/grit/grit/testdata/buildinfo.grd
-new file mode 100644
-index 0000000000..80458a8265
---- /dev/null
-+++ b/tools/grit/grit/testdata/buildinfo.grd
-@@ -0,0 +1,46 @@
-+<?xml version="1.0" encoding="UTF-8"?> <!-- -*- XML -*- -->
-+<grit
-+ base_dir="."
-+ source_lang_id="en"
-+ tc_project="GoogleDesktopWindowsClient"
-+ latest_public_release="0"
-+ current_release="1"
-+ enc_check="möl">
-+ <outputs>
-+ <output filename="resource.h" type="rc_header" />
-+ <output filename="en_generated_resources.rc" type="rc_all" lang="en" />
-+ <output filename="sv_generated_resources.rc" type="rc_all" lang="sv" />
-+ </outputs>
-+ <translations>
-+ <file path="substitute.xmb" lang="sv" />
-+ </translations>
-+ <release seq="1" allow_pseudo="false">
-+ <includes>
-+ <include type="BITMAP" name="IDB_PR" file="pr.bmp" />
-+ <if expr="lang == 'sv'">
-+ <include type="BITMAP" name="IDB_PR2" file="pr2.bmp" />
-+ </if>
-+ </includes>
-+ <structures>
-+ <structure name="SIDEBAR_LOADING.HTML" encoding="utf-8" file="sidebar_loading.html" type="tr_html" generateid="false" expand_variables="false"/>
-+ <structure name="IDS_PLACEHOLDER" file="transl.rc" type="dialog" >
-+ <skeleton expr="lang == 'sv'" file="transl1.rc" variant_of_revision="1"/>
-+ </structure>
-+ <if expr="lang != 'sv'">
-+ <structure name="WELCOME_TOAST.HTML" encoding="utf-8" file="welcome_toast.html" type="tr_html" generateid="false" expand_variables="true"/>
-+ </if>
-+ </structures>
-+ <messages first_id="8192">
-+ <message name="IDS_COPYRIGHT_GOOGLE_LONG" sub_variable="true" desc="Gadget copyright notice. Needs to be updated every year.">
-+ Copyright 2008 Google Inc. All Rights Reserved.
-+ </message>
-+ <message name="IDS_NEWS_PANEL_COPYRIGHT">
-+ Google Desktop News gadget
-+[IDS_COPYRIGHT_GOOGLE_LONG]
-+View news that is personalized based on the articles you read.
-+
-+For example, if you read lots of sports news, you'll see more sports articles. If you read technology news less often, you'll see fewer of those articles.
-+ </message>
-+ </messages>
-+ </release>
-+</grit>
-diff --git a/tools/grit/grit/testdata/cache_prefix.html b/tools/grit/grit/testdata/cache_prefix.html
-new file mode 100644
-index 0000000000..b1f91dd82b
---- /dev/null
-+++ b/tools/grit/grit/testdata/cache_prefix.html
-@@ -0,0 +1,24 @@
-+<head>
-+<meta http-equiv="cache-control" content="no-cache">
-+<meta http-equiv="pragma" content="no-cache">
-+<meta http-equiv="expires" content="-1">
-+</head>
-+<body onload="[ONLOAD]">
-+<table width="100%" border=1><tr><td>
-+<table cellspacing=0 cellpadding=10 width="100%" bgcolor=#ffffff border=1 color="#ffffff">
-+<tr><td><font face="arial,sans-serif" color=black size=-1>This is one version of <a href="[$~URL~$]">
-+<font color="blue">[URL-DISP]</font></a> from your personal <a href="http://desktop.google.com/webcache.html"><font color=blue>cache</font></a>.<br>
-+The page may have changed since that time. Click here for the <a href="[$~URL~$]"><font color="blue">current page</font></a>.<br>
-+Since this page is stored on your computer, publicly linking to this page will not work.[$~EXTRA~$]<br><br>
-+<font size="-2"><i>Google may not be affiliated with the authors of this page nor responsible for its content. This page may be protected by copyright.</i></font>
-+</td>
-+</tr></table></td></tr></table>
-+<style>
-+.hl { color:black; background-color:#ffff88}
-+</style>
-+<script>
-+[$~HIGHLIGHT_SCRIPT~$]
-+window.onerror=new Function(';');
-+</script>
-+<hr id=gg_1>
-+</body>
-\ No newline at end of file
-diff --git a/tools/grit/grit/testdata/cache_prefix_file.html b/tools/grit/grit/testdata/cache_prefix_file.html
-new file mode 100644
-index 0000000000..f3eb8e0f11
---- /dev/null
-+++ b/tools/grit/grit/testdata/cache_prefix_file.html
-@@ -0,0 +1,25 @@
-+<head>
-+<meta http-equiv="cache-control" content="no-cache">
-+<meta http-equiv="pragma" content="no-cache">
-+<meta http-equiv="expires" content="-1"></head>
-+<body onload="[ONLOAD]">
-+<table width="100%" border=1>
-+<tr><td>
-+<table cellspacing=0 cellpadding=10 width="100%" bgcolor=#ffffff border=1 color="#ffffff">
-+<tr><td><font face=arial,sans-serif color=black size=-1>This is one version of <a href="[$~URL~$]"><font color=blue>[URL-DISP]</font></a>
-+from your personal <a href="http://desktop.google.com/filecache.html"><font color=blue>cache</font></a>.<br>
-+The file may have changed since that time. Click here for the <a href="[$~URL~$]"><font color=blue>current file</font></a>.<br>
-+Since this file is stored on your computer, publicly linking to it will not work.[$~EXTRA~$]<br><br>
-+<font size="-2"><i>Google may not be affiliated with the authors of this page nor responsible for its content. This page may be protected by copyright.</i></font>
-+</td></tr>
-+</table>
-+</td></tr></table>
-+<style>
-+.hl { color:black; background-color:#ffff88}
-+</style>
-+<script>
-+[$~HIGHLIGHT_SCRIPT~$]
-+window.onerror=new Function(';');
-+</script>
-+<hr id=gg_1>
-+</body>
-\ No newline at end of file
-diff --git a/tools/grit/grit/testdata/chat_result.html b/tools/grit/grit/testdata/chat_result.html
-new file mode 100644
-index 0000000000..318078bc3d
---- /dev/null
-+++ b/tools/grit/grit/testdata/chat_result.html
-@@ -0,0 +1,24 @@
-+[HEADER]
-+[CHROME]
-+<table border=0 cellpadding=2 cellspacing=2>
-+<tr><td>[$~STARTCHAT~$]</td></tr>
-+</table>
-+<blockquote id=gg_1>
-+<table bgcolor=#f0f8ff width=80% cellpadding=5><tr><td>
-+<img style="vertical-align:middle;" height=16 src="16x16_chat.gif" width=16> &nbsp; <b>[$~TITLE~$]</b>
-+<font size=-1><br><br>Participants: [USERNAME], [BUDDYNAME]<br>
-+Date: [TIME]</font></td></tr></table>
-+<br id=contents>
-+<label>[CONTENTS]</label>
-+</blockquote>
-+<table border=0 cellpadding=2 cellspacing=2>
-+<tr><td>[$~STARTCHAT~$]</td></tr>
-+</table>
-+<style>
-+.hl { color:black; background-color:#ffff88}
-+</style>
-+<script>
-+[$~HIGHLIGHT_SCRIPT~$]
-+[ONLOAD]
-+</script>
-+[FOOTER]
-diff --git a/tools/grit/grit/testdata/chrome/app/generated_resources.grd b/tools/grit/grit/testdata/chrome/app/generated_resources.grd
-new file mode 100644
-index 0000000000..c2efb77fd8
---- /dev/null
-+++ b/tools/grit/grit/testdata/chrome/app/generated_resources.grd
-@@ -0,0 +1,199 @@
-+<?xml version="1.0" encoding="UTF-8"?>
-+
-+<!--
-+This file contains definitions of resources that will be translated for each
-+locale. The variables is_win, is_macosx, is_linux, and is_posix are available
-+for making strings OS specific. Other platform defines such as use_titlecase
-+are declared in build/common.gypi.
-+-->
-+
-+<grit base_dir="." latest_public_release="0" current_release="1"
-+ source_lang_id="en" enc_check="möl">
-+ <outputs>
-+ <output filename="grit/generated_resources.h" type="rc_header">
-+ <emit emit_type='prepend'></emit>
-+ </output>
-+ <output filename="generated_resources_am.pak" type="data_package" lang="am" />
-+ <output filename="generated_resources_ar.pak" type="data_package" lang="ar" />
-+ <if expr="pp_ifdef('use_third_party_translations')">
-+ <output filename="generated_resources_ast.pak" type="data_package" lang="ast" />
-+ </if>
-+ <output filename="generated_resources_bg.pak" type="data_package" lang="bg" />
-+ <output filename="generated_resources_bn.pak" type="data_package" lang="bn" />
-+ <if expr="pp_ifdef('use_third_party_translations')">
-+ <output filename="generated_resources_bs.pak" type="data_package" lang="bs" />
-+ </if>
-+ <output filename="generated_resources_ca.pak" type="data_package" lang="ca" />
-+ <if expr="pp_ifdef('use_third_party_translations')">
-+ <output filename="generated_resources_ca@valencia.pak" type="data_package" lang="ca@valencia" />
-+ </if>
-+ <output filename="generated_resources_cs.pak" type="data_package" lang="cs" />
-+ <output filename="generated_resources_da.pak" type="data_package" lang="da" />
-+ <output filename="generated_resources_de.pak" type="data_package" lang="de" />
-+ <output filename="generated_resources_el.pak" type="data_package" lang="el" />
-+ <if expr="pp_ifdef('use_third_party_translations')">
-+ <output filename="generated_resources_en-AU.pak" type="data_package" lang="en-AU" />
-+ </if>
-+ <output filename="generated_resources_en-GB.pak" type="data_package" lang="en-GB" />
-+ <output filename="generated_resources_en-US.pak" type="data_package" lang="en" />
-+ <if expr="pp_ifdef('use_third_party_translations')">
-+ <output filename="generated_resources_eo.pak" type="data_package" lang="eo" />
-+ </if>
-+ <output filename="generated_resources_es.pak" type="data_package" lang="es" />
-+ <output filename="generated_resources_es-419.pak" type="data_package" lang="es-419" />
-+ <output filename="generated_resources_et.pak" type="data_package" lang="et" />
-+ <if expr="pp_ifdef('use_third_party_translations')">
-+ <output filename="generated_resources_eu.pak" type="data_package" lang="eu" />
-+ </if>
-+ <output filename="generated_resources_fa.pak" type="data_package" lang="fa" />
-+ <output filename="generated_resources_fake-bidi.pak" type="data_package" lang="fake-bidi" />
-+ <output filename="generated_resources_fi.pak" type="data_package" lang="fi" />
-+ <output filename="generated_resources_fil.pak" type="data_package" lang="fil" />
-+ <output filename="generated_resources_fr.pak" type="data_package" lang="fr" />
-+ <if expr="pp_ifdef('use_third_party_translations')">
-+ <output filename="generated_resources_gl.pak" type="data_package" lang="gl" />
-+ </if>
-+ <output filename="generated_resources_gu.pak" type="data_package" lang="gu" />
-+ <output filename="generated_resources_he.pak" type="data_package" lang="he" />
-+ <output filename="generated_resources_hi.pak" type="data_package" lang="hi" />
-+ <output filename="generated_resources_hr.pak" type="data_package" lang="hr" />
-+ <output filename="generated_resources_hu.pak" type="data_package" lang="hu" />
-+ <if expr="pp_ifdef('use_third_party_translations')">
-+ <output filename="generated_resources_hy.pak" type="data_package" lang="hy" />
-+ <output filename="generated_resources_ia.pak" type="data_package" lang="ia" />
-+ </if>
-+ <output filename="generated_resources_id.pak" type="data_package" lang="id" />
-+ <output filename="generated_resources_it.pak" type="data_package" lang="it" />
-+ <output filename="generated_resources_ja.pak" type="data_package" lang="ja" />
-+ <if expr="pp_ifdef('use_third_party_translations')">
-+ <output filename="generated_resources_ka.pak" type="data_package" lang="ka" />
-+ </if>
-+ <output filename="generated_resources_kn.pak" type="data_package" lang="kn" />
-+ <output filename="generated_resources_ko.pak" type="data_package" lang="ko" />
-+ <if expr="pp_ifdef('use_third_party_translations')">
-+ <output filename="generated_resources_ku.pak" type="data_package" lang="ku" />
-+ <output filename="generated_resources_kw.pak" type="data_package" lang="kw" />
-+ </if>
-+ <output filename="generated_resources_lt.pak" type="data_package" lang="lt" />
-+ <output filename="generated_resources_lv.pak" type="data_package" lang="lv" />
-+ <output filename="generated_resources_ml.pak" type="data_package" lang="ml" />
-+ <output filename="generated_resources_mr.pak" type="data_package" lang="mr" />
-+ <if expr="pp_ifdef('use_third_party_translations')">
-+ <output filename="generated_resources_ms.pak" type="data_package" lang="ms" />
-+ </if>
-+ <output filename="generated_resources_nl.pak" type="data_package" lang="nl" />
-+ <!-- The translation console uses 'no' for Norwegian Bokmål. It should
-+ be 'nb'. -->
-+ <output filename="generated_resources_nb.pak" type="data_package" lang="no" />
-+ <output filename="generated_resources_pl.pak" type="data_package" lang="pl" />
-+ <output filename="generated_resources_pt-BR.pak" type="data_package" lang="pt-BR" />
-+ <output filename="generated_resources_pt-PT.pak" type="data_package" lang="pt-PT" />
-+ <output filename="generated_resources_ro.pak" type="data_package" lang="ro" />
-+ <output filename="generated_resources_ru.pak" type="data_package" lang="ru" />
-+ <output filename="generated_resources_sk.pak" type="data_package" lang="sk" />
-+ <output filename="generated_resources_sl.pak" type="data_package" lang="sl" />
-+ <output filename="generated_resources_sr.pak" type="data_package" lang="sr" />
-+ <output filename="generated_resources_sv.pak" type="data_package" lang="sv" />
-+ <output filename="generated_resources_sw.pak" type="data_package" lang="sw" />
-+ <output filename="generated_resources_ta.pak" type="data_package" lang="ta" />
-+ <output filename="generated_resources_te.pak" type="data_package" lang="te" />
-+ <output filename="generated_resources_th.pak" type="data_package" lang="th" />
-+ <output filename="generated_resources_tr.pak" type="data_package" lang="tr" />
-+ <if expr="pp_ifdef('use_third_party_translations')">
-+ <output filename="generated_resources_ug.pak" type="data_package" lang="ug" />
-+ </if>
-+ <output filename="generated_resources_uk.pak" type="data_package" lang="uk" />
-+ <output filename="generated_resources_vi.pak" type="data_package" lang="vi" />
-+ <output filename="generated_resources_zh-CN.pak" type="data_package" lang="zh-CN" />
-+ <output filename="generated_resources_zh-TW.pak" type="data_package" lang="zh-TW" />
-+ </outputs>
-+ <translations>
-+ <file path="resources/generated_resources_am.xtb" lang="am" />
-+ <file path="resources/generated_resources_ar.xtb" lang="ar" />
-+ <file path="../../third_party/launchpad_translations/generated_resources_ast.xtb" lang="ast" />
-+ <file path="resources/generated_resources_bg.xtb" lang="bg" />
-+ <file path="resources/generated_resources_bn.xtb" lang="bn" />
-+ <file path="../../third_party/launchpad_translations/generated_resources_bs.xtb" lang="bs" />
-+ <file path="resources/generated_resources_ca.xtb" lang="ca" />
-+ <file path="../../third_party/launchpad_translations/generated_resources_ca-valencia.xtb" lang="ca@valencia" />
-+ <file path="resources/generated_resources_cs.xtb" lang="cs" />
-+ <file path="resources/generated_resources_da.xtb" lang="da" />
-+ <file path="resources/generated_resources_de.xtb" lang="de" />
-+ <file path="resources/generated_resources_el.xtb" lang="el" />
-+ <file path="../../third_party/launchpad_translations/generated_resources_en-AU.xtb" lang="en-AU" />
-+ <file path="resources/generated_resources_en-GB.xtb" lang="en-GB" />
-+ <file path="../../third_party/launchpad_translations/generated_resources_eo.xtb" lang="eo" />
-+ <file path="resources/generated_resources_es.xtb" lang="es" />
-+ <file path="resources/generated_resources_es-419.xtb" lang="es-419" />
-+ <file path="resources/generated_resources_et.xtb" lang="et" />
-+ <file path="../../third_party/launchpad_translations/generated_resources_eu.xtb" lang="eu" />
-+ <file path="resources/generated_resources_fa.xtb" lang="fa" />
-+ <file path="resources/generated_resources_fi.xtb" lang="fi" />
-+ <file path="resources/generated_resources_fil.xtb" lang="fil" />
-+ <file path="resources/generated_resources_fr.xtb" lang="fr" />
-+ <file path="../../third_party/launchpad_translations/generated_resources_gl.xtb" lang="gl" />
-+ <file path="resources/generated_resources_gu.xtb" lang="gu" />
-+ <file path="resources/generated_resources_hi.xtb" lang="hi" />
-+ <file path="resources/generated_resources_hr.xtb" lang="hr" />
-+ <file path="resources/generated_resources_hu.xtb" lang="hu" />
-+ <file path="../../third_party/launchpad_translations/generated_resources_hy.xtb" lang="hy" />
-+ <file path="../../third_party/launchpad_translations/generated_resources_ia.xtb" lang="ia" />
-+ <file path="resources/generated_resources_id.xtb" lang="id" />
-+ <file path="resources/generated_resources_it.xtb" lang="it" />
-+ <!-- The translation console uses 'iw' for Hebrew, but we use 'he'. -->
-+ <file path="resources/generated_resources_iw.xtb" lang="he" />
-+ <file path="resources/generated_resources_ja.xtb" lang="ja" />
-+ <file path="../../third_party/launchpad_translations/generated_resources_ka.xtb" lang="ka" />
-+ <file path="resources/generated_resources_kn.xtb" lang="kn" />
-+ <file path="resources/generated_resources_ko.xtb" lang="ko" />
-+ <file path="../../third_party/launchpad_translations/generated_resources_ku.xtb" lang="ku" />
-+ <file path="../../third_party/launchpad_translations/generated_resources_kw.xtb" lang="kw" />
-+ <file path="resources/generated_resources_lt.xtb" lang="lt" />
-+ <file path="resources/generated_resources_lv.xtb" lang="lv" />
-+ <file path="resources/generated_resources_ml.xtb" lang="ml" />
-+ <file path="resources/generated_resources_mr.xtb" lang="mr" />
-+ <file path="../../third_party/launchpad_translations/generated_resources_ms.xtb" lang="ms" />
-+ <file path="resources/generated_resources_nl.xtb" lang="nl" />
-+ <file path="resources/generated_resources_no.xtb" lang="no" />
-+ <file path="resources/generated_resources_pl.xtb" lang="pl" />
-+ <file path="resources/generated_resources_pt-BR.xtb" lang="pt-BR" />
-+ <file path="resources/generated_resources_pt-PT.xtb" lang="pt-PT" />
-+ <file path="resources/generated_resources_ro.xtb" lang="ro" />
-+ <file path="resources/generated_resources_ru.xtb" lang="ru" />
-+ <file path="resources/generated_resources_sk.xtb" lang="sk" />
-+ <file path="resources/generated_resources_sl.xtb" lang="sl" />
-+ <file path="resources/generated_resources_sr.xtb" lang="sr" />
-+ <file path="resources/generated_resources_sv.xtb" lang="sv" />
-+ <file path="resources/generated_resources_sw.xtb" lang="sw" />
-+ <file path="resources/generated_resources_ta.xtb" lang="ta" />
-+ <file path="resources/generated_resources_te.xtb" lang="te" />
-+ <file path="resources/generated_resources_th.xtb" lang="th" />
-+ <file path="resources/generated_resources_tr.xtb" lang="tr" />
-+ <file path="../../third_party/launchpad_translations/generated_resources_ug.xtb" lang="ug" />
-+ <file path="resources/generated_resources_uk.xtb" lang="uk" />
-+ <file path="resources/generated_resources_vi.xtb" lang="vi" />
-+ <file path="resources/generated_resources_zh-CN.xtb" lang="zh-CN" />
-+ <file path="resources/generated_resources_zh-TW.xtb" lang="zh-TW" />
-+ </translations>
-+ <release seq="1" allow_pseudo="false">
-+ <messages fallback_to_english="true">
-+ <!-- TODO add all of your "string table" messages here. Remember to
-+ change nontranslateable parts of the messages into placeholders (using the
-+ <ph> element). You can also use the 'grit add' tool to help you identify
-+ nontranslateable parts and create placeholders for them. -->
-+ <message name="IDS_BACKGROUND_APP_INSTALLED_BALLOON_TITLE" desc="The title of the balloon that is displayed when a background app is installed">
-+ New background app installed
-+ </message>
-+ <message name="IDS_BACKGROUND_APP_INSTALLED_BALLOON_BODY" desc="The contents of the balloon that is displayed when a background app is installed">
-+ <ph name="APP_NAME">$1<ex>Background App</ex></ph> will launch at system startup and continue to run in the background even once you've closed all other <ph name="PRODUCT_NAME">$2<ex>Google Chrome</ex></ph> windows.
-+ </message>
-+ </messages>
-+ <structures fallback_to_english="true">
-+ <!-- Make sure these stay in sync with the structures in generated_resources.grd. -->
-+ <structure name="IDD_CHROME_FRAME_FIND_DIALOG" file="cf_resources.rc" type="dialog" >
-+ </structure>
-+ <structure name="IDD_CHROME_FRAME_READY_PROMPT" file="cf_resources.rc" type="dialog" >
-+ </structure>
-+ </structures>
-+ </release>
-+</grit>
-diff --git a/tools/grit/grit/testdata/chrome_html.html b/tools/grit/grit/testdata/chrome_html.html
-new file mode 100644
-index 0000000000..7f7633c5cf
---- /dev/null
-+++ b/tools/grit/grit/testdata/chrome_html.html
-@@ -0,0 +1,6 @@
-+<include src="included_sample.html">
-+<style type="text/css">
-+#image {
-+ content: url('chrome://theme/IDR_SOME_FILE');
-+}
-+</style>
-diff --git a/tools/grit/grit/testdata/default_100_percent/a.png b/tools/grit/grit/testdata/default_100_percent/a.png
-new file mode 100644
-index 0000000000000000000000000000000000000000..5d5089038ca71172e95db9e7aae1e1fa5cebd505
-GIT binary patch
-literal 159
-zcmeAS@N?(olHy`uVBq!ia0vp^oFL4>0wld=oSO}#(mY)pLnNjq|2Y3)zGGzYPN&L+
-zMSC}CcCfp=Dtxv4%6W%G#Q=|R|L;6pCCLUWO)Z<5eoL%TkDTw=s4X!^d(Qa<2khAN
-zZPy!XToBAic1Ss}vcWiD27B3&`Zj^H6CO>7R1{ToQ;=ggdEYbV=IISvfHpFCy85}S
-Ib4q9e0O9jEh5!Hn
-
-literal 0
-HcmV?d00001
-
-diff --git a/tools/grit/grit/testdata/default_100_percent/b.png b/tools/grit/grit/testdata/default_100_percent/b.png
-new file mode 100644
-index 0000000000..6178079822
---- /dev/null
-+++ b/tools/grit/grit/testdata/default_100_percent/b.png
-@@ -0,0 +1 @@
-+b
-diff --git a/tools/grit/grit/testdata/del_footer.html b/tools/grit/grit/testdata/del_footer.html
-new file mode 100644
-index 0000000000..4e19950bfc
---- /dev/null
-+++ b/tools/grit/grit/testdata/del_footer.html
-@@ -0,0 +1,8 @@
-+<table cellspacing=0 cellpadding=2 width="100%" border=0>
-+<tr bgcolor=#EFEFEF><td><font size=-1>&nbsp;<b>Remove</b> checked results and <b>return to search</b>.</font></td>
-+<td align=right><font size=-1><a onClick='checkall(1)' href="#">Check all</a> - <a onClick='checkall(0)' href="#">Uncheck all</a>&nbsp;&nbsp;</font></td>
-+<td align=right width=1% nowrap><font size=-1>
-+<input onclick=deleting() type=submit value="Remove checked results" name=submit2>
-+</font></td></tr></table>
-+<center><br><font size=-1>[$~BOTTOMLINE~$] - &copy;2005 Google </font></center>
-+</body></html>
-diff --git a/tools/grit/grit/testdata/del_header.html b/tools/grit/grit/testdata/del_header.html
-new file mode 100644
-index 0000000000..72bc6756eb
---- /dev/null
-+++ b/tools/grit/grit/testdata/del_header.html
-@@ -0,0 +1,60 @@
-+<body bgcolor="#ffffff" topmargin="2" marginheight="2">
-+<table cellSpacing="2" cellPadding="0" width="100%" border="0">
-+<form action='[$~DELETE~$]' method="post" name="delform">
-+<input name="redir" type="hidden" value="[REDIR]">
-+<script>
-+<!--
-+function deleting() {
-+f=document.getElementsByName("del");
-+var num = 0;
-+if (f.length)
-+ for(i=0;i<f.length; i++)
-+ if(f[i].checked) num++;
-+ if (num == 1) alert("One checked result has been removed");
-+ else if (num > 1) alert(num + " checked results have been removed");
-+ else alert("No results were checked, so no results have been removed");
-+}
-+function checkall(v) {
-+ f=document.getElementsByName("del");
-+ if (f.length)
-+ for(i=0;i<f.length; i++)
-+ f[i].checked=v;
-+}
-+//-->
-+</script>
-+<tr>
-+<td vAlign="top" width="1%"><A href='[$~HOMEPAGE~$]'> <img alt="Go to Google Desktop Search" width="150" height="55" src="/logo3.gif" border="0" vspace="12"></A></td>
-+<td>&nbsp;</td>
-+<td noWrap>
-+ <table cellSpacing="0" cellPadding="0" width="100%" border="0">
-+ <tr>
-+ <td bgColor="#DD0000"><img height="1" alt="" width="1"></td>
-+ </tr>
-+ </table>
-+ <table cellSpacing="0" cellPadding="0" width="100%" border="0">
-+ <tr>
-+ <td noWrap bgColor="#efefef"><font size="+1"><b>&nbsp;Remove Specific Items</b></font></td>
-+ <td noWrap align="right" bgColor="#efefef"><font size="-1"><a href="http://desktop.google.com/remove.html">Help</a>&nbsp;&nbsp;</font></td>
-+ </tr>
-+ </table>
-+</td>
-+</tr>
-+</table>
-+<table cellSpacing="0" cellPadding="2" width="100%" border="0">
-+<tr bgColor="#EFEFEF">
-+<td><font size="-1">&nbsp;<B>Remove</B> checked results and <B>return to search</B>.</font></td>
-+<td align="right"><font size="-1"><a onClick='checkall(1)' href="#">Check all</a> - <a onClick='checkall(0)' href="#">
-+Uncheck all</a>&nbsp;&nbsp;</font></td>
-+<td align="right" width="1%" nowrap><font size="-1"><input onclick="deleting()" type="submit" value="Remove checked results" name="submit2"></font></td>
-+</tr>
-+</table>
-+<br>
-+<table cellspacing="0" cellpadding="2" width="100%" border="0">
-+<tr>
-+<td colSpan="3" bgcolor="#FFFFFF" style="border:solid; border-width:1px; border-color:#DD0000"><font size="-1">&nbsp;<b>Remove
-+checked items from Google Desktop Search. Other copies of the same items will not be
-+affected.<br>
-+&nbsp;If you view the item again, it will be added back to Google Desktop Search.</b></font></td>
-+</tr>
-+</table>
-+<br>
-\ No newline at end of file
-diff --git a/tools/grit/grit/testdata/deleted.html b/tools/grit/grit/testdata/deleted.html
-new file mode 100644
-index 0000000000..5ae5f355fa
---- /dev/null
-+++ b/tools/grit/grit/testdata/deleted.html
-@@ -0,0 +1,21 @@
-+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
-+<html><head><title>Database Deleted</title>
-+<meta http-equiv="content-type" content="text/html; charset=utf-8">
-+<meta http-equiv="cache-control" content="no-cache">
-+<meta http-equiv="pragma" content="no-cache">
-+<meta http-equiv="expires" content="-1">
-+<style>
-+BODY,TD,A,P {FONT-FAMILY: arial,sans-serif}
-+.q {COLOR: #0000cc}
-+</style>
-+</head>
-+<BODY text=#000000 vLink=#551a8b aLink=#ff0000 link=#0000cc bgColor=#ffffff onload=sf()>
-+<center>
-+<TABLE cellSpacing=0 cellPadding=0 border=0>
-+<tr><td><a href="[$~HOMEPAGE~$]"><IMG border=0 height=110 alt="Google Desktop Search" src="hp_logo.gif" width=276></a>
-+</td></tr></table><BR>
-+<center>The database has been deleted. Click <a href="[$~HOMEPAGE~$]">here</a> to continue.</center>
-+</td></tr>
-+</table>
-+<br><FONT size=-1>[$~BOTTOMLINE~$]</font></p>
-+<p><FONT size=-2>&copy;2005 Google</font></p></center></body></html>
-\ No newline at end of file
-diff --git a/tools/grit/grit/testdata/depfile.grd b/tools/grit/grit/testdata/depfile.grd
-new file mode 100644
-index 0000000000..e2f7191218
---- /dev/null
-+++ b/tools/grit/grit/testdata/depfile.grd
-@@ -0,0 +1,18 @@
-+<?xml version="1.0" encoding="UTF-8"?> <!-- -*- XML -*- -->
-+<grit
-+ base_dir="."
-+ latest_public_release="0"
-+ current_release="1">
-+ <outputs>
-+ <output filename="default_100_percent.pak" lang="en" type="data_package" context="default_100_percent" />
-+ <output filename="special_100_percent.pak" lang="en" type="data_package" context="special_100_percent" />
-+ </outputs>
-+ <release seq="1">
-+ <structures fallback_to_low_resolution="true">
-+ <if expr="False">
-+ <part file="grit_part.grdp" />
-+ </if>
-+ <structure type="chrome_scaled_image" name="IDR_A" file="a.png" />
-+ </structures>
-+ </release>
-+</grit>
-diff --git a/tools/grit/grit/testdata/details.html b/tools/grit/grit/testdata/details.html
-new file mode 100644
-index 0000000000..0ab0e2a90c
---- /dev/null
-+++ b/tools/grit/grit/testdata/details.html
-@@ -0,0 +1,10 @@
-+[!]
-+title Improve Google Desktop Search by Sending Non-Personal Information
-+template
-+bottomline
-+hp_image
-+
-+<p><strong>This documentation is not yet available</strong></p>
-+<center><br>
-+<font size=-1>[$~BOTTOMLINE~$] - &copy;2005 Google </font>
-+</center>
-diff --git a/tools/grit/grit/testdata/duplicate-name-input.xml b/tools/grit/grit/testdata/duplicate-name-input.xml
-new file mode 100644
-index 0000000000..cc4d1d65c5
---- /dev/null
-+++ b/tools/grit/grit/testdata/duplicate-name-input.xml
-@@ -0,0 +1,26 @@
-+<?xml version="1.0" encoding="UTF-8"?>
-+<grit base_dir="." latest_public_release="2" current_release="3" source_lang_id="en-US">
-+ <release seq="3">
-+ <messages>
-+ <message name="IDS_GREETING" desc="Printed to greet the currently logged in user">
-+ Hello <ph name="USERNAME">%s<ex>Joi</ex></ph>, how are you doing today?
-+ </message>
-+ </messages>
-+ <structures>
-+ <!-- Duplicate name here -->
-+ <structure type="version" name="IDS_GREETING" file="rc_files/bla.rc" />
-+ </structures>
-+ </release>
-+ <translations>
-+ <file path="figs_nl_translations.xml" />
-+ <file path="cjk_translations.xml" />
-+ </translations>
-+ <outputs>
-+ <output filename="resource.h" type="rc_header" />
-+ <output filename="resource_en.rc" type="rc_all" lang="en-US" />
-+ <output filename="resource_fr.rc" type="rc_all" lang="fr-FR" />
-+ <output filename="resource_it.rc" type="rc_translateable" lang="it-IT" />
-+ <output filename="resource_zh_cn.rc" type="rc_translateable" lang="zh-CN" />
-+ <output filename="nontranslateable.rc" type="rc_nontranslateable" />
-+ </outputs>
-+</grit>
-diff --git a/tools/grit/grit/testdata/email_result.html b/tools/grit/grit/testdata/email_result.html
-new file mode 100644
-index 0000000000..8bb04b988c
---- /dev/null
-+++ b/tools/grit/grit/testdata/email_result.html
-@@ -0,0 +1,34 @@
-+[HEADER]
-+[CHROME]
-+<table border=0 cellpadding=2 cellspacing=2 width='100%'>
-+<tr><td><font size=-1>[CONV]
-+<a href='[$~REPLY_URL~$]'>Reply</a> | <a href='[$~REPLYALL_URL~$]'>Reply&nbsp;to&nbsp;All</a>[$~FORWARD_URL~$] | <a href='mailto:'>Compose</a>[$~OUTLOOKVIEW~$]
-+</font></td></tr>
-+</table>
-+<blockquote id=gg_1>
-+<table bgcolor=#f0f8ff width=80% cellpadding=5><tr><td>
-+<img style="vertical-align:middle;" height=16 src='/email.gif' width=16> &nbsp; <b>[SUBJECT]</b>
-+<p><font size=-1>[FROM-DISP]
-+[TO-DISP]
-+[CC-DISP]
-+[BCC-DISP]
-+[REPLYTO-DISP]
-+[DATE-DISP]
-+[VIEW-DISP]
-+[$~ATTACH~$]
-+</font></td></tr></table>
-+<p class=g><span style="width:500;"><font size=-1><label>[MESSAGE]</label></span></p>
-+</font>
-+</blockquote>
-+<table border=0 cellpadding=2 cellspacing=2 width='100%'>
-+<tr><td><font size=-1>[CONV]
-+<a href='[$~REPLY_URL~$]'>Reply</a> | <a href='[$~REPLYALL_URL~$]'>Reply&nbsp;to&nbsp;All</a>[$~FORWARD_URL~$] | <a href='mailto:'>Compose</a>[$~OUTLOOKVIEW~$]
-+</font></td></tr></table>
-+<style>
-+.hl { color:black; background-color:#ffff88}
-+</style>
-+<script>
-+[$~HIGHLIGHT_SCRIPT~$]
-+[ONLOAD]
-+</script>
-+[FOOTER]
-\ No newline at end of file
-diff --git a/tools/grit/grit/testdata/email_thread.html b/tools/grit/grit/testdata/email_thread.html
-new file mode 100644
-index 0000000000..3c7279b841
---- /dev/null
-+++ b/tools/grit/grit/testdata/email_thread.html
-@@ -0,0 +1,10 @@
-+[HEADER]
-+[CHROME]
-+<blockquote [MAXWIDTH]>
-+<b><img src=email.gif style="vertical-align:middle;" width=16 height=16> &nbsp; [SUBJECT]</b><br><br>
-+<TABLE cellSpacing=0 cellPadding=3 border=0>
-+[CONTENTS]
-+</table>
-+</blockquote>
-+[NEXT_PREV]
-+[FOOTER]
-\ No newline at end of file
-diff --git a/tools/grit/grit/testdata/error.html b/tools/grit/grit/testdata/error.html
-new file mode 100644
-index 0000000000..66875a234c
---- /dev/null
-+++ b/tools/grit/grit/testdata/error.html
-@@ -0,0 +1,8 @@
-+[HEADER]
-+[CHROME]
-+<br>
-+<blockquote>
-+[ERROR]<br><br>
-+If you think this is an error, please <a href="http://desktop.google.com/feedback.html?version=[VERSION]">contact us</a>.
-+</blockquote>
-+[FOOTER]
-\ No newline at end of file
-diff --git a/tools/grit/grit/testdata/explicit_web.html b/tools/grit/grit/testdata/explicit_web.html
-new file mode 100644
-index 0000000000..1424adc617
---- /dev/null
-+++ b/tools/grit/grit/testdata/explicit_web.html
-@@ -0,0 +1,11 @@
-+[HEADER]
-+<style>
-+.image {BORDER: #0000cc 1px solid;}
-+.imageh {BORDER: #0000cc 1px solid;}
-+</style>
-+[WEB_TOP_CHROME]
-+[$~STATUS~$]
-+[$~MESSAGE~$]
-+[WEB_FILES]
-+<br>[NEXT_PREV]
-+[FOOTER]
-\ No newline at end of file
-diff --git a/tools/grit/grit/testdata/footer.html b/tools/grit/grit/testdata/footer.html
-new file mode 100644
-index 0000000000..3372d6afac
---- /dev/null
-+++ b/tools/grit/grit/testdata/footer.html
-@@ -0,0 +1,14 @@
-+<center><br clear=all><br>
-+<table cellspacing=0 cellpadding=0 width="100%" border=0><tr bgcolor=#3399CC><td align=middle height=1><img height=1 width=1></td></tr></table>
-+<table cellspacing=0 cellpadding=0 width="100%" bgcolor=#e8f4f7 border=0>
-+<tr bgcolor=#e8f4f7>
-+<td><br>
-+<table cellpadding=1 align=center border=0 cellspacing=0 bgcolor=#e8f4f7>
-+<form action='[$~SEARCHURL~$]' method=get>
-+<tr><td noWrap>[$~BOTTOM~$]</td></tr></form>
-+</table><br>
-+</td></tr></table>
-+<table cellspacing=0 cellpadding=0 width="100%" border=0><tr bgcolor=#3399CC><td align=middle height=1><img height=1 width=1></td></tr></table><br>
-+<font size=-1>[$~BOTTOMLINE~$] - &copy;2005 Google </font></center>
-+[SCRIPT]
-+</body></html>
-diff --git a/tools/grit/grit/testdata/generated_resources_fr.xtb b/tools/grit/grit/testdata/generated_resources_fr.xtb
-new file mode 100644
-index 0000000000..373c40feea
---- /dev/null
-+++ b/tools/grit/grit/testdata/generated_resources_fr.xtb
-@@ -0,0 +1,3079 @@
-+<?xml version="1.0" ?>
-+<!DOCTYPE translationbundle>
-+<translationbundle lang="fr">
-+<translation id="1525924600121678168">Salut!</translation>
-+<translation id="5335090254790956485">Salut <ph name="USERNAME"/></translation>
-+<translation id="6779164083355903755">Supprime&amp;r</translation>
-+<translation id="6879617193011158416">Activer la barre de favoris</translation>
-+<translation id="8130276680150879341">Déconnexion du réseau privé</translation>
-+<translation id="1058418043520174283"><ph name="INDEX"/> sur <ph name="COUNT"/></translation>
-+<translation id="4480627574828695486">Déconnecter ce compte...</translation>
-+<translation id="7040807039050164757">&amp;Vérifier l'orthographe dans ce champ</translation>
-+<translation id="778579833039460630">Aucune donnée reçue.</translation>
-+<translation id="1852799913675865625">Une erreur s'est produite lors de la tentative de lecture du fichier : <ph name="ERROR_TEXT"/>.</translation>
-+<translation id="3828924085048779000">Le mot de passe multiterme est obligatoire.</translation>
-+<translation id="8265562484034134517">Importer les données d'un autre navigateur...</translation>
-+<translation id="2709516037105925701">Saisie automatique</translation>
-+<translation id="4857138207355690859">API P2P</translation>
-+<translation id="250599269244456932">Exécuter automatiquement (recommandé)</translation>
-+<translation id="3581034179710640788">Le certificat de sécurité du site a expiré !</translation>
-+<translation id="2825758591930162672">Clé publique de l'objet</translation>
-+<translation id="8275038454117074363">Importer</translation>
-+<translation id="8418445294933751433">Afficher dan&amp;s un onglet</translation>
-+<translation id="6985276906761169321">ID :</translation>
-+<translation id="859285277496340001">Le certificat n'indique aucun mécanisme permettant de vérifier s'il a été révoqué.</translation>
-+<translation id="2010799328026760191">Touches de modification...</translation>
-+<translation id="3300394989536077382">Signé par :</translation>
-+<translation id="654233263479157500">Utiliser un service Web pour résoudre les erreurs de navigation</translation>
-+<translation id="4940047036413029306">Guillemet</translation>
-+<translation id="1526811905352917883">Une nouvelle tentative de connexion avec SSL 3.0 a dû être effectuée. Cette opération indique généralement que le serveur utilise un logiciel très ancien et qu'il est susceptible de présenter d'autres problèmes de sécurité.</translation>
-+<translation id="1497897566809397301">Autoriser le stockage des données locales (recommandé)</translation>
-+<translation id="3275778913554317645">Ouvrir dans une fenêtre</translation>
-+<translation id="4553117311324416101">Google pense qu'un logiciel malveillant pourrait être installé sur votre ordinateur si vous continuez. Nous vous conseillons de ne pas continuer, même si vous avez déjà consulté ce site auparavant ou si vous avez confiance en celui-ci. Il se peut qu'il ait été piraté récemment. Réessayez demain ou utilisez un autre site.</translation>
-+<translation id="509988127256758334">&amp;Rechercher :</translation>
-+<translation id="1420684932347524586">Échec de génération de clé privée RSA aléatoire</translation>
-+<translation id="2501173422421700905">Certificat en attente</translation>
-+<translation id="2313634973119803790">Technologie réseau :</translation>
-+<translation id="2382901536325590843">Le certificat du serveur ne figure pas dans le DNS.</translation>
-+<translation id="2833791489321462313">Demander le mot de passe au retour de veille</translation>
-+<translation id="3850258314292525915">Désactiver la synchronisation</translation>
-+<translation id="2721561274224027017">Base de données indexée</translation>
-+<translation id="8208216423136871611">Ne pas enregistrer</translation>
-+<translation id="684587995079587263"><ph name="PRODUCT_NAME"/> synchronise vos données avec votre compte Google en toute sécurité. Synchronisez toutes vos données ou personnalisez les types de données synchronisées et les options de chiffrement.</translation>
-+<translation id="4405141258442788789">Le délai imparti à l'opération est dépassé.</translation>
-+<translation id="5048179823246820836">Nordique</translation>
-+<translation id="1763046204212875858">Créer des raccourcis vers des applications</translation>
-+<translation id="2105006017282194539">Pas encore chargé</translation>
-+<translation id="524759338601046922">Confirmer le nouveau code PIN :</translation>
-+<translation id="688547603556380205">L2TP/IPSec + Certificat utilisateur</translation>
-+<translation id="777702478322588152">Préfecture</translation>
-+<translation id="6562437808764959486">Extraction de l'image de récupération...</translation>
-+<translation id="561349411957324076">Terminé</translation>
-+<translation id="4764776831041365478">Il se peut que la page Web à l'adresse <ph name="URL"/> soit temporairement inaccessible ou qu'elle ait été déplacée de façon permanente à une autre adresse Web.</translation>
-+<translation id="6156863943908443225">Cache des scripts</translation>
-+<translation id="4610656722473172270">Barre d'outils Google</translation>
-+<translation id="151501797353681931">Importés depuis Safari</translation>
-+<translation id="6706684875496318067">Le plug-in <ph name="PLUGIN_NAME"/> n'est pas autorisé.</translation>
-+<translation id="586567932979200359">Vous exécutez <ph name="PRODUCT_NAME"/> à partir de son image disque. Si vous l'installez sur votre ordinateur, vous pourrez l'utiliser sans image disque et bénéficierez de mises à jour automatiques.</translation>
-+<translation id="3775432569830822555">Certificat du serveur SSL</translation>
-+<translation id="1829192082282182671">Z&amp;oom arrière</translation>
-+<translation id="6102827823267795198">Indique si la suggestion du moteur de recherche doit être entrée immédiatement via la saisie semi-automatique lorsque la fonctionnalité Recherche instantanée est activée.</translation>
-+<translation id="1467071896935429871">Mise à jour du système : <ph name="PERCENT"/> % téléchargés</translation>
-+<translation id="7881267037441701396">Les informations d'identification associées au partage de vos imprimantes via <ph name="CLOUD_PRINT_NAME"/> sont arrivées à expiration. Cliquez ici pour saisir à nouveau votre nom d'utilisateur et votre mot de passe.</translation>
-+<translation id="816055135686411707">Erreur de définition du paramètre de confiance du certificat</translation>
-+<translation id="4714531393479055912"><ph name="PRODUCT_NAME"/> peut maintenant synchroniser vos mots de passe.</translation>
-+<translation id="5704565838965461712">Sélectionnez le certificat à présenter pour l'identification :</translation>
-+<translation id="2025632980034333559"><ph name="APP_NAME"/> a planté. Cliquez sur cette info-bulle pour actualiser l'extension.</translation>
-+<translation id="4059593000330943833">Compatibilité expérimentale avec des méthodes Wi-Fi Extensible Authentication Protocol supplémentaires, telles que EAP-TLS et LEAP.</translation>
-+<translation id="6322279351188361895">Échec de lecture de la clé privée</translation>
-+<translation id="3781072658385678636">Les plug-ins suivants ont été bloqués sur cette page :</translation>
-+<translation id="4428782877951507641">Configuration de la synchronisation</translation>
-+<translation id="3648460724479383440">Case d'option cochée</translation>
-+<translation id="4654488276758583406">Très petite</translation>
-+<translation id="6647228709620733774">URL de révocation de l'autorité de certification Netscape</translation>
-+<translation id="546411240573627095">Style de pavé numérique</translation>
-+<translation id="7663002797281767775">Active les feuilles de style CSS 3D et la composition graphique haute performance des pages Web via le processeur graphique.</translation>
-+<translation id="2972581237482394796">&amp;Rétablir</translation>
-+<translation id="5895138241574237353">Redémarrer</translation>
-+<translation id="1858072074757584559">La connexion n'est pas compressée.</translation>
-+<translation id="528468243742722775">Fin</translation>
-+<translation id="1723824996674794290">&amp;Nouvelle fenêtre</translation>
-+<translation id="1313405956111467313">Configuration automatique du proxy</translation>
-+<translation id="1589055389569595240">Afficher l'orthographe et la grammaire</translation>
-+<translation id="4364779374839574930">Aucune imprimante n'a été trouvée. Veuillez en installer une.</translation>
-+<translation id="7017587484910029005">Saisissez les caractères visibles dans l'image ci-dessous.</translation>
-+<translation id="9013589315497579992">Certificat d'authentification de client SSL incorrect</translation>
-+<translation id="8595062045771121608">Le certificat du serveur ou un certificat AC intermédiaire présenté au navigateur a été signé avec un algorithme de signature faible tel que RSA-MD2. D'après des études récentes menées par des informaticiens, les algorithmes de signature seraient plus faibles qu'on ne le pensait jusqu'alors. Aujourd'hui, ils sont très rarement utilisés par les sites Web jugés dignes de confiance. Ce certificat a peut-être été contrefait. Nous vous déconseillons vivement de continuer.</translation>
-+<translation id="8666632926482119393">Rechercher le précédent</translation>
-+<translation id="7567293639574541773">I&amp;nspecter l'élément</translation>
-+<translation id="8392896330146417149">État d'itinérance :</translation>
-+<translation id="6813971406343552491">&amp;Non</translation>
-+<translation id="36224234498066874">Effacer les données de navigation...</translation>
-+<translation id="3384773155383850738">Nombre maximal de suggestions</translation>
-+<translation id="8331498498435985864">L'accessibilité est désactivée.</translation>
-+<translation id="8530339740589765688">Sélectionner par domaine</translation>
-+<translation id="8677212948402625567">Tout réduire...</translation>
-+<translation id="7600965453749440009">Ne jamais traduire les pages rédigées en <ph name="LANGUAGE"/> </translation>
-+<translation id="3208703785962634733">Non confirmé</translation>
-+<translation id="6523841952727744497">Avant de vous connecter, démarrez une session en tant qu'invité afin d'activer le réseau <ph name="NETWORK_ID"/>.</translation>
-+<translation id="7450044767321666434">La gravure de l'image est terminée.</translation>
-+<translation id="2653266418988778031">Si vous supprimez le certificat d'une autorité de certification, votre navigateur ne fera plus confiance aux certificats émis par cette autorité de certification.</translation>
-+<translation id="298068999958468740">Synchronisez toutes les données de cet ordinateur ou sélectionnez celles que vous souhaitez synchroniser.</translation>
-+<translation id="5341849548509163798"><ph name="NUMBER_MANY"/> hours ago</translation>
-+<translation id="4422428420715047158">Domaine :</translation>
-+<translation id="3602290021589620013">Aperçu</translation>
-+<translation id="7516602544578411747">Associe chaque fenêtre du navigateur à un profil et ajoute une option de sélection des profils en haut à droite. Chaque profil possède ses propres favoris, extensions, applications, etc.</translation>
-+<translation id="7082055294850503883">Ignorer le verrouillage des majuscules et saisir des minuscules par défaut</translation>
-+<translation id="1800124151523561876">Aucune parole détectée</translation>
-+<translation id="7814266509351532385">Changer de moteur de recherche par défaut</translation>
-+<translation id="5376169624176189338">Cliquer pour revenir en arrière, maintenir pour voir l'historique</translation>
-+<translation id="6310545596129886942"><ph name="NUMBER_FEW"/> secondes restantes</translation>
-+<translation id="9181716872983600413">Unicode</translation>
-+<translation id="1383861834909034572">Ouverture à la fin du téléchargement</translation>
-+<translation id="5727728807527375859">Les extensions, les applications et les thèmes peuvent endommager votre ordinateur. Voulez-vous vraiment continuer ?</translation>
-+<translation id="3857272004253733895">Schéma du pinyin double</translation>
-+<translation id="1636842079139032947">Déconnecter ce compte...</translation>
-+<translation id="6721972322305477112">&amp;Fichier</translation>
-+<translation id="1076818208934827215">Microsoft Internet Explorer</translation>
-+<translation id="9056810968620647706">Aucune correspondance trouvée</translation>
-+<translation id="1901494098092085382">État de votre commentaire</translation>
-+<translation id="2861301611394761800">Mise à jour terminée. Veuillez redémarrer le système.</translation>
-+<translation id="2231238007119540260">Lorsque vous supprimez un certificat de serveur, vous rétablissez les contrôles de sécurité habituels du serveur et un certificat valide lui est demandé.</translation>
-+<translation id="5463582782056205887">Essayez d'ajouter
-+ <ph name="PRODUCT_NAME"/>
-+ aux programmes autorisés dans les paramètres de votre pare-feu ou de votre antivirus. S'il
-+ est déjà autorisé, tentez de le supprimer de la liste et de l'ajouter à nouveau à
-+ la liste des programmes autorisés.</translation>
-+<translation id="7624154074265342755">Réseaux sans fil</translation>
-+<translation id="3315158641124845231">Masquer <ph name="PRODUCT_NAME"/></translation>
-+<translation id="3496213124478423963">Zoom arrière</translation>
-+<translation id="2296019197782308739">Méthode EAP :</translation>
-+<translation id="42981349822642051">Développer</translation>
-+<translation id="4013794286379809233">Veuillez vous connecter</translation>
-+<translation id="7693221960936265065">de n'importe quand</translation>
-+<translation id="1763138995382273070">Désactiver la validation des formulaires interactifs HTML5</translation>
-+<translation id="4920887663447894854">Le suivi de votre position géographique sur cette page a été bloqué pour les sites suivants :</translation>
-+<translation id="7690346658388844119">La gravure de l'image a été interrompue.</translation>
-+<translation id="8133676275609324831">&amp;Afficher dans le dossier</translation>
-+<translation id="645705751491738698">Continuer à bloquer JavaScript</translation>
-+<translation id="4780321648949301421">Enregistrer la page sous...</translation>
-+<translation id="9154072353677278078">Le serveur <ph name="DOMAIN"/> à l'adresse <ph name="REALM"/> requiert un nom d'utilisateur et un mot de passe.</translation>
-+<translation id="2551191967044410069">Exceptions de géolocalisation</translation>
-+<translation id="4092066334306401966">13px</translation>
-+<translation id="8178665534778830238">Contenu :</translation>
-+<translation id="153384433402665971">Le plug-in <ph name="PLUGIN_NAME"/> a été bloqué, car il n'est plus à jour.</translation>
-+<translation id="2610260699262139870">Taille ré&amp;elle</translation>
-+<translation id="4535734014498033861">Échec de la connexion au serveur proxy.</translation>
-+<translation id="558170650521898289">Vérification de pilote matériel Microsoft Windows</translation>
-+<translation id="98515147261107953">Paysage</translation>
-+<translation id="8974161578568356045">Détecter automatiquement</translation>
-+<translation id="1818606096021558659">Page</translation>
-+<translation id="5388588172257446328">Nom d'utilisateur :</translation>
-+<translation id="1657406563541664238">Nous aider à améliorer <ph name="PRODUCT_NAME"/> en envoyant automatiquement les statistiques d'utilisation et les rapports d'erreur à Google</translation>
-+<translation id="7982789257301363584">Réseau</translation>
-+<translation id="8528962588711550376">Connexion en cours</translation>
-+<translation id="2336228925368920074">Ajouter tous les onglets aux favoris...</translation>
-+<translation id="4985312428111449076">Onglets ou fenêtres</translation>
-+<translation id="7481475534986701730">Sites récemment consultés</translation>
-+<translation id="4260722247480053581">Ouvrir dans une fenêtre de navigation privée</translation>
-+<translation id="8503758797520866434">Préférences de saisie automatique...</translation>
-+<translation id="2757031529886297178">Compteur d'images par seconde</translation>
-+<translation id="6657585470893396449">Mot de passe</translation>
-+<translation id="7881483672146086348">Afficher le compte</translation>
-+<translation id="1776883657531386793"><ph name="OID"/> : <ph name="INFO"/></translation>
-+<translation id="1510030919967934016">Le suivi de votre position géographique a été bloqué pour cette page.</translation>
-+<translation id="4640525840053037973">Connexion à l'aide de votre compte Google</translation>
-+<translation id="5255315797444241226">Le mot de passe multiterme entré est incorrect.</translation>
-+<translation id="6242054993434749861">télécopie : #<ph name="FAX"/></translation>
-+<translation id="762917759028004464">Le navigateur par défaut est actuellement <ph name="BROWSER_NAME"/>.</translation>
-+<translation id="9213479837033539041"><ph name="NUMBER_MANY"/> secondes restantes</translation>
-+<translation id="300544934591011246">Mot de passe précédent</translation>
-+<translation id="5078796286268621944">Code PIN incorrect</translation>
-+<translation id="989988560359834682">Modifier l'adresse</translation>
-+<translation id="8487678622945914333">Zoom avant</translation>
-+<translation id="2972557485845626008">Micrologiciel</translation>
-+<translation id="735327918767574393">Une erreur s'est produite lors de l'affichage de cette page Web. Pour continuer, actualisez cette page ou ouvrez-en une autre.</translation>
-+<translation id="8028060951694135607">Récupération de clé Microsoft</translation>
-+<translation id="9187657844611842955">recto verso</translation>
-+<translation id="6391832066170725637">Fichier ou répertoire introuvable</translation>
-+<translation id="4694445829210540512">Aucun forfait de données <ph name="NETWORK"/> actif</translation>
-+<translation id="5494920125229734069">Tout sélectionner</translation>
-+<translation id="2857834222104759979">Le fichier manifeste est incorrect.</translation>
-+<translation id="7931071620596053769">Les pages suivantes ne répondent plus. Vous pouvez attendre qu'elles soient de nouveau accessibles ou les supprimer.</translation>
-+<translation id="1209866192426315618"><ph name="NUMBER_DEFAULT"/> minutes restantes</translation>
-+<translation id="7938958445268990899">Le certificat du serveur n'est pas encore valide.</translation>
-+<translation id="4569998400745857585">Menu contenant des extensions masquées</translation>
-+<translation id="4081383687659939437">Enregistrer les infos</translation>
-+<translation id="5786805320574273267">Configuration de l'accès à distance à cet ordinateur.</translation>
-+<translation id="1801827354178857021">Point</translation>
-+<translation id="2179052183774520942">Ajouter un moteur de recherche</translation>
-+<translation id="5498951625591520696">Impossible d'atteindre le serveur.</translation>
-+<translation id="2956948609882871496">Importer mes favoris...</translation>
-+<translation id="1621207256975573490">Enregistrer le &amp;cadre sous...</translation>
-+<translation id="4681260323810445443">Vous n'êtes pas autorisé à accéder à la page Web <ph name="URL"/>. Votre connexion peut être requise.</translation>
-+<translation id="2176444992480806665">Envoyer la capture d'écran du dernier onglet actif</translation>
-+<translation id="1165039591588034296">Erreur</translation>
-+<translation id="2064942105849061141">Utiliser le thème GTK+</translation>
-+<translation id="2278562042389100163">Ouvrir une fenêtre du navigateur</translation>
-+<translation id="5246282308050205996"><ph name="APP_NAME"/> a planté. Cliquez sur cette info-bulle pour redémarrer l'application.</translation>
-+<translation id="9218430445555521422">Définir comme navigateur par défaut</translation>
-+<translation id="5027550639139316293">Certificat de courrier électronique</translation>
-+<translation id="938582441709398163">Clavier en superposition</translation>
-+<translation id="427208986916971462">La connexion est compressée avec <ph name="COMPRESSION"/>.</translation>
-+<translation id="4589279373639964403">Exporter mes favoris...</translation>
-+<translation id="8876215549894133151">Format :</translation>
-+<translation id="5234764350956374838">Ignorer</translation>
-+<translation id="40027638859996362">Déplacer un mot</translation>
-+<translation id="5463275305984126951">Index de <ph name="LOCATION"/></translation>
-+<translation id="5154917547274118687">Mémoire</translation>
-+<translation id="1493492096534259649">Impossible d'utiliser cette langue pour corriger l'orthographe.</translation>
-+<translation id="6628463337424475685">Recherche <ph name="ENGINE"/></translation>
-+<translation id="2502105862509471425">Ajouter une autre carte de paiement...</translation>
-+<translation id="4037618776454394829">Envoyer la dernière capture d'écran enregistrée</translation>
-+<translation id="8987670145726065238">Ce fichier contient du code malveillant. Voulez-vous vraiment continuer ?</translation>
-+<translation id="182729337634291014">Erreur de synchronisation...</translation>
-+<translation id="4465830120256509958">Clavier brésilien</translation>
-+<translation id="2459861677908225199">Utiliser TLS 1.0</translation>
-+<translation id="4792711294155034829">&amp;Signaler un problème...</translation>
-+<translation id="5819484510464120153">Créer des raccourci&amp;s vers des applications...</translation>
-+<translation id="6845180713465955339">Le certificat &quot;<ph name="CERTIFICATE_NAME"/>&quot; a été émis par :</translation>
-+<translation id="7531238562312180404"><ph name="PRODUCT_NAME"/> ne contrôlant pas la façon dont les extensions gèrent vos données personnelles, toutes les extensions sont désactivées dans les fenêtres de navigation privée. Vous pouvez les réactiver individuellement dans le <ph name="BEGIN_LINK"/>gestionnaire des extensions<ph name="END_LINK"/>.</translation>
-+<translation id="5667293444945855280">Logiciels malveillants</translation>
-+<translation id="3435845180011337502">Mise en page ou mise en forme de la page</translation>
-+<translation id="3838186299160040975">Acheter davantage...</translation>
-+<translation id="6831043979455480757">Traduire</translation>
-+<translation id="3587482841069643663">Tout</translation>
-+<translation id="6698381487523150993">Créé :</translation>
-+<translation id="4684748086689879921">Annuler l'importation</translation>
-+<translation id="9130015405878219958">Le mode indiqué est incorrect.</translation>
-+<translation id="6615807189585243369"><ph name="BURNT_AMOUNT"/> copié(s) sur <ph name="TOTAL_SIZE"/></translation>
-+<translation id="8518425453349204360">L'accès à distance à cet ordinateur est activé pour <ph name="USER_EMAIL_ADDRESS"/>.</translation>
-+<translation id="4950138595962845479">Options...</translation>
-+<translation id="4653235815000740718">Un problème est survenu lors de la création du support de récupération du système d'exploitation. Le périphérique de stockage utilisé est introuvable.</translation>
-+<translation id="5516565854418269276">Toujours &amp;afficher la barre de favoris</translation>
-+<translation id="6426222199977479699">Erreur SSL</translation>
-+<translation id="7104784605502674932">Confirmer les préférences de synchronisation</translation>
-+<translation id="1788636309517085411">Utiliser les valeurs par défaut</translation>
-+<translation id="1661867754829461514">Code secret manquant</translation>
-+<translation id="7406714851119047430">L'accès à distance à cet ordinateur est désactivé.</translation>
-+<translation id="8589311641140863898">API des extensions expérimentales</translation>
-+<translation id="2804922931795102237">Inclure les informations système</translation>
-+<translation id="869891660844655955">Date d'expiration</translation>
-+<translation id="2178614541317717477">Autorité de certification compromise</translation>
-+<translation id="4449935293120761385">À propos de la saisie automatique</translation>
-+<translation id="4194570336751258953">Activer la fonction &quot;Taper pour cliquer&quot;</translation>
-+<translation id="6066742401428748382">Accès à la page Web refusé</translation>
-+<translation id="5111692334209731439">&amp;Gestionnaire de favoris</translation>
-+<translation id="8295070100601117548">Erreur serveur</translation>
-+<translation id="5661272705528507004">Cette carte SIM est désactivée et ne peut être utilisée. Veuillez demander à votre fournisseur de services de la remplacer.</translation>
-+<translation id="443008484043213881">Outils</translation>
-+<translation id="2529657954821696995">Clavier néerlandais</translation>
-+<translation id="1128128132059598906">EAP-TTLS</translation>
-+<translation id="6585234750898046415">Choisissez une image à associer à votre compte. Celle-ci s'affichera sur l'écran de connexion.</translation>
-+<translation id="7957054228628133943">Configurer le blocage des fenêtres pop-up...</translation>
-+<translation id="179767530217573436">des 4 dernières semaines</translation>
-+<translation id="2279770628980885996">Une situation inattendue s'est produite tandis que le serveur tentait de traiter la demande.</translation>
-+<translation id="8079135502601738761">Impossible d'afficher certaines parties de ce document PDF. Souhaitez-vous l'ouvrir dans Adobe Reader ?</translation>
-+<translation id="9123413579398459698">Proxy FTP</translation>
-+<translation id="3887875461425980041">Si vous utilisez la version PPAPI de Flash, exécutez-la dans chaque processus de moteur du rendu plutôt que dans un processus de plug-in dédié.</translation>
-+<translation id="8534801226027872331">Cela signifie que le certificat présenté à votre navigateur contient des erreurs et qu'il ne peut pas être compris. Il est possible que les informations sur l'identité du certificat ou que d'autres informations du certificat relatives à la sécurité de la connexion soient incompréhensibles. Ne poursuivez pas.</translation>
-+<translation id="3608527593787258723">Activer l'onglet 1</translation>
-+<translation id="4497369307931735818">Communication à distance</translation>
-+<translation id="3855676282923585394">Importer les favoris et les paramètres...</translation>
-+<translation id="1116694919640316211">À propos</translation>
-+<translation id="4422347585044846479">Modifier le favori de cette page</translation>
-+<translation id="1684638090259711957">Ajouter un format d'exception</translation>
-+<translation id="4925481733100738363">Configurer l'accès à distance...</translation>
-+<translation id="1880905663253319515">Supprimer le certificat &quot;<ph name="CERTIFICATE_NAME"/>&quot; ?</translation>
-+<translation id="8546306075665861288">Cache des images</translation>
-+<translation id="5904093760909470684">Configuration du proxy</translation>
-+<translation id="2874027208508018603">En l'absence de connexion Wi-Fi, Google Chrome utilise les données 3G.</translation>
-+<translation id="4558734465070698159">Appuyez sur <ph name="HOTKEY_NAME"/> pour sélectionner le mode de saisie précédent.</translation>
-+<translation id="3348643303702027858">La création du support de récupération du système d'exploitation a été annulée.</translation>
-+<translation id="3391060940042023865">Le plug-in suivant est bloqué : <ph name="PLUGIN_NAME"/></translation>
-+<translation id="4237016987259239829">Erreur de connexion réseau</translation>
-+<translation id="9050666287014529139">Mot de passe multiterme</translation>
-+<translation id="5197255632782567636">Internet</translation>
-+<translation id="4755860829306298968">Configurer les paramètres de blocage des plug-ins...</translation>
-+<translation id="8879284080359814990">Afficher dan&amp;s un onglet</translation>
-+<translation id="2786847742169026523">Synchroniser vos mots de passe</translation>
-+<translation id="41293960377217290">Le serveur proxy agit comme un intermédiaire entre votre ordinateur et les autres serveurs. Votre configuration système utilise actuellement un proxy, mais
-+ <ph name="PRODUCT_NAME"/>
-+ ne parvient pas à s'y connecter.</translation>
-+<translation id="4520722934040288962">Sélectionner par type d'application</translation>
-+<translation id="3873139305050062481">Procéder à l'i&amp;nspection de l'élément</translation>
-+<translation id="7445762425076701745">Impossible de valider entièrement l'identité du serveur auquel vous êtes connecté. Le nom utilisé pour cette connexion n'est valide que sur votre réseau et aucune autorité de certification externe ne peut en vérifier la propriété. Certaines autorités de certification délivrent tout de même des certificats pour ces types de nom, par conséquent nous ne sommes pas en mesure de vérifier que vous êtes connecté au site voulu et non à un site malveillant.</translation>
-+<translation id="1556537182262721003">Impossible de déplacer le répertoire d'extensions dans le profil.</translation>
-+<translation id="5866557323934807206">Supprimer ces paramètres pour les prochaines visites</translation>
-+<translation id="6506104645588011859">L'accessibilité est activée.</translation>
-+<translation id="5355351445385646029">Appuyer sur la touche Espace pour sélectionner la suggestion</translation>
-+<translation id="6978622699095559061">Vos favoris</translation>
-+<translation id="6370820475163108109"><ph name="ORGANIZATION_NAME"/> (<ph name="DOMAIN_NAME"/>)</translation>
-+<translation id="5453029940327926427">Fermer les onglets</translation>
-+<translation id="406070391919917862">Applications en arrière-plan</translation>
-+<translation id="8820817407110198400">Favoris</translation>
-+<translation id="3214837514330816581">Supprimer les données synchronisées de Google Dashboard</translation>
-+<translation id="2580170710466019930">Veuillez patienter pendant que <ph name="PRODUCT_NAME"/> installe les dernières mises à jour système.</translation>
-+<translation id="7428061718435085649">Utilisez les touches Maj gauche et droite pour sélectionner les 2e et 3e propositions</translation>
-+<translation id="1070066693520972135">WEP</translation>
-+<translation id="206683469794463668">Mode Zhuyin complet. La sélection automatique de la suggestion et les options associées sont désactivées ou ignorées.</translation>
-+<translation id="5191625995327478163">&amp;Paramètres linguistiques...</translation>
-+<translation id="8833054222610756741">CRX-less Web Apps</translation>
-+<translation id="4031729365043810780">Connexion au réseau</translation>
-+<translation id="3332115613788466465">Reliure bord long</translation>
-+<translation id="1985136186573666099"><ph name="PRODUCT_NAME"/> utilise les paramètres proxy du système pour se connecter au réseau.</translation>
-+<translation id="6508261954199872201">Application : <ph name="APP_NAME"/></translation>
-+<translation id="5585645215698205895">&amp;Descendre</translation>
-+<translation id="8366757838691703947">? Toutes les données présentes sur le périphérique seront supprimées.</translation>
-+<translation id="6596816719288285829">Adresse IP</translation>
-+<translation id="7747704580171477003">Active le nouveau design de la page &quot;Nouvel onglet&quot; (en cours de développement).</translation>
-+<translation id="4508265954913339219">Échec de l'activation</translation>
-+<translation id="8656768832129462377">Ne pas vérifier</translation>
-+<translation id="715487527529576698">Le chinois simplifié est le mode de saisie initial</translation>
-+<translation id="1674989413181946727">Paramètres SSL sur tout l'ordinateur :</translation>
-+<translation id="8703575177326907206">Votre connexion à <ph name="DOMAIN"/> n'est pas chiffrée.</translation>
-+<translation id="8472623782143987204">matériel requis</translation>
-+<translation id="4865571580044923428">Gérer les exceptions...</translation>
-+<translation id="2526619973349913024">Rechercher des mises à jour</translation>
-+<translation id="3874070094967379652">Utiliser un mot de passe multiterme pour chiffrer mes données de synchronisation</translation>
-+<translation id="4864369630010738180">Connexion en cours...</translation>
-+<translation id="6500116422101723010">Le serveur ne parvient pas à traiter la demande pour le moment. Ce code indique une situation temporaire. Le serveur sera de nouveau opérationnel ultérieurement.</translation>
-+<translation id="1644574205037202324">Historique</translation>
-+<translation id="1297175357211070620">Destination</translation>
-+<translation id="6219983382864672018">Web audio</translation>
-+<translation id="479280082949089240">Cookies placés par cette page</translation>
-+<translation id="4198861010405014042">Accès partagé</translation>
-+<translation id="6204930791202015665">Afficher...</translation>
-+<translation id="5941343993301164315">Veuillez vous connecter à <ph name="TOKEN_NAME"/>.</translation>
-+<translation id="4417229845571722044">Ajouter un nouvel e-mail</translation>
-+<translation id="8049151370369915255">Personnaliser les polices...</translation>
-+<translation id="2886862922374605295">Matériel :</translation>
-+<translation id="4497097279402334319">Erreur de connexion au réseau.</translation>
-+<translation id="5303618139271450299">Cette page Web est introuvable.</translation>
-+<translation id="4256316378292851214">En&amp;registrer la vidéo sous...</translation>
-+<translation id="3528171143076753409">Le certificat du serveur n'est pas approuvé.</translation>
-+<translation id="6518014396551869914">Cop&amp;ier l'image</translation>
-+<translation id="3236997602556743698">Sebeol-sik 390</translation>
-+<translation id="542155483965056918"><ph name="NUMBER_ZERO"/> mins ago</translation>
-+<translation id="289426338439836048">Autre réseau mobile...</translation>
-+<translation id="3986287159189541211">Erreur HTTP <ph name="ERROR_NUMBER"/> (<ph name="ERROR_NAME"/>) : <ph name="ERROR_TEXT"/></translation>
-+<translation id="3225319735946384299">Signature du code</translation>
-+<translation id="3118319026408854581">Aide <ph name="PRODUCT_NAME"/></translation>
-+<translation id="2422426094670600218">&lt;sans nom&gt;</translation>
-+<translation id="2012766523151663935">Version du micrologiciel :</translation>
-+<translation id="4120898696391891645">La page ne se charge pas</translation>
-+<translation id="6060685159320643512">Attention, ces fonctionnalités expérimentales peuvent mordre.</translation>
-+<translation id="5829990587040054282">Verrouiller l'écran ou éteindre</translation>
-+<translation id="7800304661137206267">La connexion est chiffrée au moyen de <ph name="CIPHER"/>, avec <ph name="MAC"/> pour l'authentification des messages et <ph name="KX"/> pour la méthode d'échange de clés.</translation>
-+<translation id="7706319470528945664">Clavier portugais</translation>
-+<translation id="5584537427775243893">Importation</translation>
-+<translation id="9128870381267983090">Connexion au réseau</translation>
-+<translation id="4181841719683918333">Langues</translation>
-+<translation id="6535131196824081346">Cette erreur peut se produire lors de la connexion à un serveur sécurisé (HTTPS).
-+ Elle indique que le serveur tente d'établir une connexion sécurisée, mais
-+ que celle-ci ne sera pas du tout sécurisée en raison d'une grave erreur de configuration.
-+ <ph name="LINE_BREAK"/> Dans ce cas, une intervention
-+ est requise sur le serveur.
-+ <ph name="PRODUCT_NAME"/>
-+ n'utilise pas de connexion non sécurisée pour protéger la confidentialité
-+ de vos données.</translation>
-+<translation id="5235889404533735074">La synchronisation de <ph name="PRODUCT_NAME"/> vous permet de partager vos données (favoris, préférences) sur vos ordinateurs en toute simplicité. Pour ce faire, <ph name="PRODUCT_NAME"/> enregistre vos données en ligne via Google lorsque vous vous connectez à votre compte.</translation>
-+<translation id="6533668113756472185">Format ou mise en forme de la page</translation>
-+<translation id="5640179856859982418">Clavier suisse</translation>
-+<translation id="5910363049092958439">En&amp;registrer l'image sous...</translation>
-+<translation id="1363055550067308502">Basculer en mode pleine chasse ou demi-chasse</translation>
-+<translation id="3108967419958202225">Sélectionner...</translation>
-+<translation id="6451650035642342749">Effacer les paramètres d'ouverture automatique</translation>
-+<translation id="5948544841277865110">Ajouter un réseau privé</translation>
-+<translation id="7121570032414343252"><ph name="NUMBER_TWO"/> secondes</translation>
-+<translation id="1378451347523657898">Ne pas envoyer de capture d'écran</translation>
-+<translation id="5098629044894065541">Hébreu</translation>
-+<translation id="7751559664766943798">Toujours afficher la barre de favoris</translation>
-+<translation id="5098647635849512368">Impossible de trouver le chemin d'accès absolu du répertoire à empaqueter.</translation>
-+<translation id="780617032715125782">Créer un profil</translation>
-+<translation id="933712198907837967">Diners Club</translation>
-+<translation id="6380224340023442078">Paramètres de contenu...</translation>
-+<translation id="950108145290971791">Activer la recherche instantanée pour accélérer la recherche et la navigation ?</translation>
-+<translation id="144136026008224475">Plus d'extensions &gt;&gt;</translation>
-+<translation id="5486326529110362464">La valeur d'entrée de la clé privée est obligatoire.</translation>
-+<translation id="9039663905644212491">PEAP</translation>
-+<translation id="62780591024586043">Fonctionnalités de localisation expérimentales</translation>
-+<translation id="8584280235376696778">Ou&amp;vrir la vidéo dans un nouvel onglet</translation>
-+<translation id="2845382757467349449">Toujours afficher la barre de favoris</translation>
-+<translation id="2516384155283419848">Reliure</translation>
-+<translation id="3053013834507634016">Utilisation de la clé du certificat</translation>
-+<translation id="4487088045714738411">Clavier belge</translation>
-+<translation id="7511635910912978956"><ph name="NUMBER_FEW"/> heures restantes</translation>
-+<translation id="2152580633399033274">Afficher toutes les images (recommandé)</translation>
-+<translation id="7894567402659809897">Cliquez sur
-+ <ph name="BEGIN_BOLD"/>Démarrer<ph name="END_BOLD"/>,
-+ puis sur
-+ <ph name="BEGIN_BOLD"/>Exécuter<ph name="END_BOLD"/>.
-+ Saisissez
-+ et cliquez sur
-+ <ph name="BEGIN_BOLD"/>OK<ph name="END_BOLD"/>.</translation>
-+<translation id="2934952234745269935">Nom de volume</translation>
-+<translation id="7960533875494434480">Reliure bord court</translation>
-+<translation id="6431347207794742960"><ph name="PRODUCT_NAME"/> va configurer les mises à jour automatiques pour tous les utilisateurs de cet ordinateur.</translation>
-+<translation id="4973698491777102067">Effacer les éléments datant :</translation>
-+<translation id="6074963268421707432">Interdire à tous les sites d'afficher des notifications sur le Bureau</translation>
-+<translation id="8508050303181238566">Appuyez sur <ph name="HOTKEY_NAME"/> pour passer d'un mode de saisie à l'autre.</translation>
-+<translation id="6273404661268779365">Ajouter un nouveau fax</translation>
-+<translation id="1995173078718234136">Recherche de contenu en cours...</translation>
-+<translation id="4735819417216076266">Style d'entrée avec Espace</translation>
-+<translation id="2977095037388048586">Vous avez tenté d'accéder à <ph name="DOMAIN"/>, mais, au lieu de cela, vous communiquez actuellement avec un serveur identifié comme <ph name="DOMAIN2"/>. Cela est peut-être dû à un défaut de configuration du serveur ou à quelque chose de plus grave. Un pirate informatique sur votre réseau cherche peut-être à vous faire visiter une version falsifiée de <ph name="DOMAIN3"/>, donc potentiellement préjudiciable. Nous vous déconseillons vivement de continuer.</translation>
-+<translation id="220138918934036434">Masquer le bouton</translation>
-+<translation id="5374359983950678924">Modifier l'image</translation>
-+<translation id="5158548125608505876">Ne pas synchroniser mes mots de passe</translation>
-+<translation id="2167276631610992935">JavaScript</translation>
-+<translation id="6974306300279582256">Activer les notifications de <ph name="SITE"/></translation>
-+<translation id="492914099844938733">Afficher les incompatibilités</translation>
-+<translation id="5233638681132016545">Nouvel onglet</translation>
-+<translation id="6567688344210276845">Impossible de charger l'icône &quot;<ph name="ICON"/>&quot; d'action de page.</translation>
-+<translation id="5210365745912300556">Fermer l'onglet</translation>
-+<translation id="8628085465172583869">Nom d'hôte du serveur :</translation>
-+<translation id="498765271601821113">Ajouter une carte de paiement</translation>
-+<translation id="7694379099184430148"><ph name="FILENAME"/> : type de fichier inconnu</translation>
-+<translation id="1992397118740194946">Non défini</translation>
-+<translation id="7966826846893205925">Gérer les paramètres de saisie automatique...</translation>
-+<translation id="8556732995053816225">Respecter la &amp;casse</translation>
-+<translation id="3314070176311241517">Autoriser tous les sites à exécuter JavaScript (recommandé)</translation>
-+<translation id="2406911946387278693">Gérer vos périphériques depuis le cloud</translation>
-+<translation id="7419631653042041064">Clavier catalan</translation>
-+<translation id="5710740561465385694">Me demander lorsqu'un site essaie de stocker des données</translation>
-+<translation id="3897092660631435901">Menu</translation>
-+<translation id="7024867552176634416">Sélectionnez le périphérique de stockage amovible à utiliser.</translation>
-+<translation id="8553075262323480129">La traduction a échoué, car nous n'avons pas pu déterminer la langue de la page.</translation>
-+<translation id="5910680277043747137">Vous pouvez créer des profils supplémentaires pour autoriser plusieurs personnes à utiliser et personnaliser Google Chrome.</translation>
-+<translation id="4381849418013903196">Deux-points</translation>
-+<translation id="1103523840287552314">Toujours traduire en <ph name="LANGUAGE"/></translation>
-+<translation id="2263497240924215535">(désactivée)</translation>
-+<translation id="2159087636560291862">Cela signifie que le certificat n'a pas été vérifié par un tiers reconnu par votre ordinateur. N'importe qui peut émettre un certificat en se faisant passer pour un autre site Web. Ce certificat doit donc être vérifié par un tiers approuvé. Sans cette vérification, les informations sur l'identité du certificat sont sans intérêt. Par conséquent, il nous est impossible de vérifier que vous communiquez bien avec <ph name="DOMAIN"/> et non avec un pirate informatique ayant émis son propre certificat en prétendant être <ph name="DOMAIN2"/>. Nous vous déconseillons vivement de continuer.</translation>
-+<translation id="58625595078799656"><ph name="PRODUCT_NAME"/> requiert que vous cryptiez vos données à l'aide de votre mot de passe Google ou de votre propre mot de passe multiterme.</translation>
-+<translation id="8017335670460187064"><ph name="LABEL"/></translation>
-+<translation id="6840184929775541289">N'est pas une autorité de certification</translation>
-+<translation id="6099520380851856040">Date et heure : <ph name="CRASH_TIME"/></translation>
-+<translation id="144518587530125858">Impossible de charger &quot;<ph name="IMAGE_PATH"/>&quot; pour le thème.</translation>
-+<translation id="5355097969896547230">Rechercher à nouveau</translation>
-+<translation id="7925285046818567682">En attente de <ph name="HOST_NAME"/>...</translation>
-+<translation id="2553440850688409052">Masquer ce plug-in</translation>
-+<translation id="3280237271814976245">Enregistrer &amp;sous...</translation>
-+<translation id="8301162128839682420">Ajouter une langue :</translation>
-+<translation id="7658239707568436148">Annuler</translation>
-+<translation id="8695825812785969222">Ouvrir une &amp;adresse...</translation>
-+<translation id="4538417792467843292">Supprimer le mot</translation>
-+<translation id="8412392972487953978">Vous devez saisir deux fois le même mot de passe multiterme.</translation>
-+<translation id="9121814364785106365">Ouvrir dans un onglet épinglé</translation>
-+<translation id="6996264303975215450">Page Web, tout type de contenu</translation>
-+<translation id="3435896845095436175">Activer</translation>
-+<translation id="1891668193654680795">Considérer ce certificat comme fiable pour identifier les développeurs de logiciels.</translation>
-+<translation id="5078638979202084724">Ajouter tous les onglets aux favoris</translation>
-+<translation id="5585118885427931890">Impossible de créer le dossier de favoris.</translation>
-+<translation id="2154710561487035718">Copier l'URL</translation>
-+<translation id="8163672774605900272">Si vous pensez ne pas avoir à utiliser de serveur proxy, procédez comme suit :
-+ <ph name="PLATFORM_TEXT"/></translation>
-+<translation id="5510687173983454382">Définir les utilisateurs autorisés à se connecter à un périphérique et autoriser les sessions de navigation en tant qu'invité</translation>
-+<translation id="3241680850019875542">Sélectionnez le répertoire racine de l'extension à empaqueter. Pour mettre à jour une extension, sélectionnez également le fichier de clé privée à réutiliser.</translation>
-+<translation id="7500424997253660722">Pool restreint :</translation>
-+<translation id="657402800789773160">&amp;Rafraîchir cette page</translation>
-+<translation id="6163363155248589649">&amp;Normal</translation>
-+<translation id="7972714317346275248">PKCS #1 SHA-384 avec chiffrement RSA</translation>
-+<translation id="3020990233660977256">Numéro de série : <ph name="SERIAL_NUMBER"/></translation>
-+<translation id="8426519927982004547">HTTPS/SSL</translation>
-+<translation id="8216781342946147825">Toutes les données de votre ordinateur et des sites Web que vous visitez</translation>
-+<translation id="5548207786079516019">Ceci est une installation secondaire de <ph name="PRODUCT_NAME"/> et ce dernier ne peut pas être défini comme navigateur par défaut.</translation>
-+<translation id="3984413272403535372">Erreur lors de la signature de l'extension</translation>
-+<translation id="8807083958935897582"><ph name="PRODUCT_NAME"/> permet d'effectuer des recherches sur Internet à l'aide du champ polyvalent. Sélectionnez le moteur de recherche à utiliser :</translation>
-+<translation id="6629104427484407292">Sécurité : <ph name="SECURITY"/></translation>
-+<translation id="9208886416788010685">Adobe Reader n'est pas à jour</translation>
-+<translation id="3373604799988099680">Extensions ou applications</translation>
-+<translation id="318408932946428277">Effacer les cookies et autres données de site et de plug-in lorsque je ferme le navigateur</translation>
-+<translation id="314141447227043789">Téléchargement de l'image terminé.</translation>
-+<translation id="8725178340343806893">Favoris</translation>
-+<translation id="5177526793333269655">Afficher les vignettes</translation>
-+<translation id="8926389886865778422">Ne plus afficher ce message</translation>
-+<translation id="6985235333261347343">Agent de récupération de clé Microsoft</translation>
-+<translation id="3605499851022050619">Page de diagnostic de navigation sécurisée</translation>
-+<translation id="4417271111203525803">Adresse ligne 2</translation>
-+<translation id="7617095560120859490">Décrivez-nous le problème recontré. (Champ obligatoire)</translation>
-+<translation id="5618333180342767515">Cela peut prendre quelques minutes.</translation>
-+<translation id="4307992518367153382">Options de base</translation>
-+<translation id="8480417584335382321">Niveau de zoom par défaut :</translation>
-+<translation id="3872166400289564527">Stockage externe</translation>
-+<translation id="5912378097832178659">Modifi&amp;er les moteurs de recherche...</translation>
-+<translation id="8272426682713568063">Cartes de paiement</translation>
-+<translation id="3749289110408117711">Nom du fichier</translation>
-+<translation id="3173397526570909331">Arrêter la synchronisation</translation>
-+<translation id="5538092967727216836">Actualiser le cadre</translation>
-+<translation id="4813345808229079766">Connexion</translation>
-+<translation id="411666854932687641">Mémoire privée</translation>
-+<translation id="119944043368869598">Tout effacer</translation>
-+<translation id="3467848195100883852">Activer la correction orthographique automatique</translation>
-+<translation id="1336254985736398701">Afficher les &amp;infos sur la page</translation>
-+<translation id="7550830279652415241">favoris_<ph name="DATESTAMP"/>.html</translation>
-+<translation id="6828153365543658583">Autoriser uniquement les utilisateurs suivants à se connecter :</translation>
-+<translation id="1652965563555864525">&amp;Muet</translation>
-+<translation id="4200983522494130825">Nouvel ongle&amp;t</translation>
-+<translation id="7979036127916589816">Erreur de synchronisation</translation>
-+<translation id="1029317248976101138">Zoom</translation>
-+<translation id="5455790498993699893"><ph name="ACTIVE_MATCH"/> sur <ph name="TOTAL_MATCHCOUNT"/></translation>
-+<translation id="8890069497175260255">Type de clavier</translation>
-+<translation id="1202290638211552064">Délai d'expiration atteint au niveau de la passerelle ou du serveur proxy en attente d'une réponse d'un serveur en amont.</translation>
-+<translation id="7765158879357617694">Déplacer</translation>
-+<translation id="5731751937436428514">Mode de saisie du vietnamien (VIQR)</translation>
-+<translation id="8412144371993786373">Ajouter la page actuelle aux favoris</translation>
-+<translation id="7615851733760445951">&lt;aucun cookie sélectionné&gt;</translation>
-+<translation id="469553822757430352">Le mot de passe de l'application est incorrect.</translation>
-+<translation id="2493021387995458222">Sélectionner &quot;un mot à la fois&quot;</translation>
-+<translation id="5279600392753459966">Tout bloquer</translation>
-+<translation id="6846298663435243399">Chargement en cours…</translation>
-+<translation id="7392915005464253525">&amp;Rouvrir la fenêtre fermée</translation>
-+<translation id="1144684570366564048">Gérer les exceptions...</translation>
-+<translation id="7400418766976504921">URL</translation>
-+<translation id="1541725072327856736">Katakana demi-chasse</translation>
-+<translation id="7456847797759667638">Ouvrir une adresse</translation>
-+<translation id="1388866984373351434">Données de navigation</translation>
-+<translation id="3754634516926225076">Code PIN incorrect. Veuillez réessayer.</translation>
-+<translation id="7378627244592794276">Non</translation>
-+<translation id="2800537048826676660">Utiliser cette langue pour corriger l'orthographe</translation>
-+<translation id="68541483639528434">Fermer les autres onglets</translation>
-+<translation id="941543339607623937">Clé privée non valide.</translation>
-+<translation id="6499058468232888609">Une erreur réseau s'est produite pendant la communication avec le service de gestion des périphériques.</translation>
-+<translation id="4433862206975946675">Importer les données d'un autre navigateur...</translation>
-+<translation id="4022426551683927403">&amp;Ajouter au dictionnaire</translation>
-+<translation id="2897878306272793870">Voulez-vous vraiment ouvrir <ph name="TAB_COUNT"/> onglets ?</translation>
-+<translation id="312759608736432009">Fabricant du périphérique :</translation>
-+<translation id="362276910939193118">Afficher l'historique complet</translation>
-+<translation id="6079696972035130497">Illimité</translation>
-+<translation id="4365411729367255048">Clavier Neo2 allemand</translation>
-+<translation id="6348657800373377022">Liste déroulante</translation>
-+<translation id="8064671687106936412">Clé :</translation>
-+<translation id="2218515861914035131">Coller en texte brut</translation>
-+<translation id="1725149567830788547">Afficher les &amp;commandes</translation>
-+<translation id="3528033729920178817">Cette page suit votre position géographique.</translation>
-+<translation id="5518584115117143805">Certificat de chiffrement de courrier électronique</translation>
-+<translation id="9203398526606335860">&amp;Profilage activé</translation>
-+<translation id="4307281933914537745">En savoir plus sur la récupération du système</translation>
-+<translation id="2849936225196189499">Essentielle</translation>
-+<translation id="9001035236599590379">Type MIME</translation>
-+<translation id="5612754943696799373">Autoriser le téléchargement ?</translation>
-+<translation id="6353618411602605519">Clavier croate</translation>
-+<translation id="5515810278159179124">Interdire à tous les sites de suivre ma position géographique</translation>
-+<translation id="5999606216064768721">Utiliser la barre de titre du système et les bordures de la fenêtre</translation>
-+<translation id="904752364881701675">En bas à gauche</translation>
-+<translation id="3398951731874728419">Informations sur l'erreur :</translation>
-+<translation id="1464570622807304272">Essayez : saisissez &quot;orchidées&quot;, puis appuyez sur Entrée.</translation>
-+<translation id="8026684114486203427">Pour utiliser Chrome Web Store, vous devez être connecté à un compte Google.</translation>
-+<translation id="8417276187983054885">Configurer <ph name="CLOUD_PRINT_NAME"/></translation>
-+<translation id="3056462238804545033">Petit problème... Nous n'avons pas réussi à vous authentifier. Veuillez vérifier vos identifiants de connexion puis réessayer.</translation>
-+<translation id="5298420986276701358">Pour gérer à distance la configuration de ce périphérique <ph name="PRODUCT_NAME"/> depuis le cloud, connectez-vous avec votre compte Google Apps.</translation>
-+<translation id="2678063897982469759">Réactiver</translation>
-+<translation id="1779766957982586368">Fermer la fenêtre</translation>
-+<translation id="4850886885716139402">Présentation</translation>
-+<translation id="89217462949994770">Vous avez saisi un trop grand nombre de codes PIN incorrects. Veuillez contacter <ph name="CARRIER_ID"/> pour obtenir une nouvelle clé de déverrouillage du code PIN à 8 chiffres.</translation>
-+<translation id="5920618722884262402">Bloquer le contenu inapproprié</translation>
-+<translation id="5120247199412907247">Configuration avancée</translation>
-+<translation id="5922220455727404691">Utiliser SSL 3.0</translation>
-+<translation id="1368352873613152012">Règles de confidentialité liées à la navigation sécurisée</translation>
-+<translation id="5105859138906591953">Vous devez être connecté à votre compte Google pour importer les favoris de la barre d'outils Google dans Google Chrome. Connectez-vous et relancez l'importation.</translation>
-+<translation id="8899851313684471736">Ouvrir le lien dans une nouvelle &amp;fenêtre</translation>
-+<translation id="4110342520124362335">Les cookies de <ph name="DOMAIN"/> ont été bloqués.</translation>
-+<translation id="3303818374450886607">Copies</translation>
-+<translation id="2019718679933488176">&amp;Ouvrir le fichier audio dans un nouvel onglet</translation>
-+<translation id="4138267921960073861">Afficher les noms d'utilisateurs et leur photo sur la page de connexion</translation>
-+<translation id="7465778193084373987">URL de révocation de certificat Netscape</translation>
-+<translation id="5976690834266782200">Ajoute des options de regroupement des onglets dans le menu contextuel des onglets.</translation>
-+<translation id="4755240240651974342">Clavier finnois</translation>
-+<translation id="7049893973755373474">Vérifiez votre connexion Internet. Redémarrez votre routeur, votre modem
-+ ou tout autre périphérique réseau que vous utilisez.</translation>
-+<translation id="7421925624202799674">&amp;Afficher le code source de la page</translation>
-+<translation id="3940082421246752453">Le serveur ne prend pas en charge la version HTTP utilisée dans la demande.</translation>
-+<translation id="661719348160586794">Vos mots de passe enregistrés s'afficheront ici.</translation>
-+<translation id="6686490380836145850">Fermer les onglets sur la droite</translation>
-+<translation id="5608669887400696928"><ph name="NUMBER_DEFAULT"/> heures</translation>
-+<translation id="8844709414456935411"><ph name="PRODUCT_NAME"/>
-+ indique qu'un produit ESET intercepte les connexions sécurisées.
-+ En général, cela ne constitue pas un problème de sécurité car le
-+ logiciel ESET s'exécute souvent sur le même ordinateur. Toutefois, en raison
-+ de certaines incompatibilités avec les connexions sécurisées
-+ <ph name="PRODUCT_NAME"/>,
-+ vous devez configurer les produits ESET de manière à éviter ces
-+ interceptions. Cliquez sur le lien En savoir plus pour obtenir des instructions.</translation>
-+<translation id="3936877246852975078">Les requêtes adressées au serveur ont été temporairement limitées.</translation>
-+<translation id="2600306978737826651">Impossible de télécharger l'image. Gravure annulée.</translation>
-+<translation id="609978099044725181">Activer/désactiver le mode Hanja</translation>
-+<translation id="1829483195200467833">Effacer les paramètres d'ouverture automatique</translation>
-+<translation id="2738771556149464852">Pas après le</translation>
-+<translation id="5774515636230743468">Manifeste :</translation>
-+<translation id="719464814642662924">Visa</translation>
-+<translation id="7474889694310679759">Clavier anglais canadien</translation>
-+<translation id="1817871734039893258">Récupération de fichier Microsoft</translation>
-+<translation id="2423578206845792524">En&amp;registrer l'image sous...</translation>
-+<translation id="6839929833149231406">Zone</translation>
-+<translation id="9068931793451030927">Chemin :</translation>
-+<translation id="283278805979278081">Prendre la photo</translation>
-+<translation id="7320906967354320621">Inactif</translation>
-+<translation id="1407050882688520094">Certains de vos certificats enregistrés identifient ces autorités de certification :</translation>
-+<translation id="4287689875748136217">Impossible d'afficher la page Web, car le serveur n'a envoyé aucune donnée.</translation>
-+<translation id="1634788685286903402">Considérer ce certificat comme fiable pour identifier les utilisateurs de messageries.</translation>
-+<translation id="7052402604161570346">Ce type de fichier peut endommager votre ordinateur. Voulez-vous vraiment télécharger <ph name="FILE_NAME"/> ?</translation>
-+<translation id="8642489171979176277">Importés depuis la barre d'outils Google</translation>
-+<translation id="4142744419835627535">Recherche instantanée et saisie semi-automatique</translation>
-+<translation id="4684427112815847243">Tout synchroniser</translation>
-+<translation id="1125520545229165057">Dvorak (Hsu)</translation>
-+<translation id="8940229512486821554">Exécuter la commande <ph name="EXTENSION_NAME"/> : <ph name="SEARCH_TERMS"/></translation>
-+<translation id="2232876851878324699">Le fichier contenait un certificat, qui n'a pas été importé :</translation>
-+<translation id="7787129790495067395">Vous utilisez actuellement un mot de passe multiterme. Si vous l'oubliez, vous pouvez réinitialiser la synchronisation afin de supprimer vos données des serveurs Google à l'aide de Google Dashboard.</translation>
-+<translation id="2686759344028411998">Impossible de détecter les modules chargés.</translation>
-+<translation id="572525680133754531">Cette fonctionnalité affiche une bordure autour des couches de rendu afin de déboguer et d'étudier leur composition.</translation>
-+<translation id="2011110593081822050">Processus de traitement Web : <ph name="WORKER_NAME"/></translation>
-+<translation id="3294437725009624529">Invité</translation>
-+<translation id="350069200438440499">Nom du fichier :</translation>
-+<translation id="9058204152876341570">Un élément est manquant.</translation>
-+<translation id="8494979374722910010">Échec de la tentative de connexion au serveur.</translation>
-+<translation id="7810202088502699111">Des fenêtres pop-up ont été bloquées sur cette page.</translation>
-+<translation id="8190698733819146287">Personnaliser les langues et la saisie...</translation>
-+<translation id="646727171725540434">Proxy HTTP</translation>
-+<translation id="1006316751839332762">Mot de passe multiterme de chiffrement</translation>
-+<translation id="8795916974678578410">Nouvelle fenêtre</translation>
-+<translation id="2733275712367076659">Certains certificats provenant de ces organisations vous identifient :</translation>
-+<translation id="4801512016965057443">Autoriser l'itinérance des données mobiles</translation>
-+<translation id="2515586267016047495">Alt</translation>
-+<translation id="2046040965693081040">Utiliser les pages actuelles</translation>
-+<translation id="3798449238516105146">Version</translation>
-+<translation id="5764483294734785780">En&amp;registrer le fichier audio sous...</translation>
-+<translation id="5252456968953390977">Itinérance</translation>
-+<translation id="8744641000906923997">Romaji</translation>
-+<translation id="8507996248087185956"><ph name="NUMBER_DEFAULT"/> minutes</translation>
-+<translation id="4845656988780854088">Synchroniser uniquement les paramètres et\ndonnées qui ont changé depuis la dernière connexion\n(requiert votre mot de passe précédent)</translation>
-+<translation id="348620396154188443">Autoriser tous les sites à afficher des notifications sur le Bureau</translation>
-+<translation id="8214489666383623925">Ouvrir le fichier...</translation>
-+<translation id="5376120287135475614">Changer de fenêtre</translation>
-+<translation id="5230160809118287008">Chèvres téléportées</translation>
-+<translation id="1701567960725324452">Si vous arrêtez la synchronisation, les données stockées sur cet ordinateur et dans votre compte Google demeureront à ces deux emplacements. Toutefois, les nouvelles données ou les modifications apportées au contenu existant ne seront pas synchronisées.</translation>
-+<translation id="7761701407923456692">Le certificat du serveur ne correspond pas à l'URL.</translation>
-+<translation id="3885155851504623709">Commune</translation>
-+<translation id="4910171858422458941">Impossible d'activer les plug-ins désactivés par une stratégie d'entreprise.</translation>
-+<translation id="4495419450179050807">Ne pas afficher sur cette page</translation>
-+<translation id="4745800796303246012">Méthodes EAP en Wi-Fi expérimentales</translation>
-+<translation id="1225544122210684390">Disque dur</translation>
-+<translation id="939736085109172342">Nouveau dossier</translation>
-+<translation id="4933484234309072027">intégration sur <ph name="URL"/></translation>
-+<translation id="5554720593229208774">Autorité de certification de messagerie</translation>
-+<translation id="862750493060684461">Cache CSS</translation>
-+<translation id="2832519330402637498">En haut à gauche</translation>
-+<translation id="6749695674681934117">Saisissez le nom du nouveau dossier.</translation>
-+<translation id="6204994989617056362">L'extension de renégociation SSL était introuvable lors de la négociation sécurisée. Avec certains sites, connus pour leur prise en charge de l'extension de renégociation, Google Chrome exige une négociation mieux sécurisée afin de prévenir certaines attaques. L'absence de cette extension suggère que votre connexion a été interceptée et manipulée au cours du transfert.</translation>
-+<translation id="6679492495854441399">Petit problème... Une erreur de communication avec le réseau est survenue lors de la tentative d'inscription de ce périphérique. Veuillez vérifier votre connexion réseau et réessayer.</translation>
-+<translation id="7789962463072032349">pause</translation>
-+<translation id="121827551500866099">Afficher tous les téléchargements...</translation>
-+<translation id="1562633988311880769">Connexion à <ph name="CLOUD_PRINT_NAME"/></translation>
-+<translation id="5949910269212525572">Impossible de résoudre l'adresse DNS du serveur.</translation>
-+<translation id="3115147772012638511">En attente de l'affichage du cache</translation>
-+<translation id="257088987046510401">Thèmes</translation>
-+<translation id="6771079623344431310">Impossible de se connecter au serveur proxy.</translation>
-+<translation id="2200129049109201305">Ignorer la synchronisation des données chiffrées ?</translation>
-+<translation id="1426410128494586442">Oui</translation>
-+<translation id="6725970970008349185">Nombre de suggestions par page</translation>
-+<translation id="6198252989419008588">Modifier le code PIN</translation>
-+<translation id="5749483996735055937">Un problème est survenu lors de la copie de l'image de récupération sur le périphérique.</translation>
-+<translation id="3520476450377425184"><ph name="NUMBER_MANY"/> jours restants</translation>
-+<translation id="7643817847124207232">La connexion Internet a été interrompue.</translation>
-+<translation id="932327136139879170">Début</translation>
-+<translation id="4764675709794295630">« Précédent</translation>
-+<translation id="2560794850818211873">C&amp;opier l'URL de la vidéo</translation>
-+<translation id="6042708169578999844">Vos données sur <ph name="WEBSITE_1"/> et <ph name="WEBSITE_2"/></translation>
-+<translation id="5302048478445481009">Langue</translation>
-+<translation id="5553089923092577885">Mappages des stratégies de certificat</translation>
-+<translation id="5600907569873192868"><ph name="NUMBER_MANY"/> minutes restantes</translation>
-+<translation id="1519704592140256923">Sélectionner la position</translation>
-+<translation id="1275018677838892971">Le site Web à l'adresse <ph name="HOST_NAME"/> contient des éléments provenant de sites signalés comme étant des sites de phishing. Ces derniers incitent les internautes à divulguer leurs informations personnelles en se faisant passer pour des institutions de confiance, telles que des banques.</translation>
-+<translation id="702455272205692181"><ph name="EXTENSION_NAME"/></translation>
-+<translation id="7170041865419449892">Hors de portée</translation>
-+<translation id="908263542783690259">Effacer l'historique de navigation</translation>
-+<translation id="7518003948725431193">Aucune page Web trouvée à l'adresse :<ph name="URL"/></translation>
-+<translation id="745602119385594863">Nouveau moteur de recherche :</translation>
-+<translation id="7484645889979462775">Jamais pour ce site</translation>
-+<translation id="8666066831007952346"><ph name="NUMBER_TWO"/> jours restants</translation>
-+<translation id="9086455579313502267">Impossible d'accéder au réseau.</translation>
-+<translation id="5595485650161345191">Modifier l'adresse</translation>
-+<translation id="2374144379568843525">&amp;Masquer le panneau de la vérification orthographique</translation>
-+<translation id="2694026874607847549">1 cookie</translation>
-+<translation id="4393664266930911253">Activer ces fonctionnalités...</translation>
-+<translation id="6390842777729054533"><ph name="NUMBER_ZERO"/> secondes restantes</translation>
-+<translation id="3909791450649380159">Cou&amp;per</translation>
-+<translation id="2955913368246107853">Fermer la barre de recherche</translation>
-+<translation id="5642508497713047">Signataire de la liste de révocation de certificats</translation>
-+<translation id="813082847718468539">Afficher des informations à propos du site</translation>
-+<translation id="3122464029669770682">UC</translation>
-+<translation id="1684861821302948641">Fermer les pages</translation>
-+<translation id="6092270396854197260">MSPY</translation>
-+<translation id="6802031077390104172"><ph name="USAGE"/> (<ph name="OID"/>)</translation>
-+<translation id="4052120076834320548">Très petite</translation>
-+<translation id="6623138136890659562">Afficher les réseaux privés dans le menu Réseau pour activer la connexion à un VPN</translation>
-+<translation id="8969837897925075737">Vérification de la mise à jour du système...</translation>
-+<translation id="3393716657345709557">L'entrée demandée est introuvable dans le cache.</translation>
-+<translation id="7241389281993241388">Connectez-vous à <ph name="TOKEN_NAME"/> pour importer le certificat client.</translation>
-+<translation id="40334469106837974">Modifier la mise en page</translation>
-+<translation id="4804818685124855865">Se déconnecter</translation>
-+<translation id="2617919205928008385">Espace insuffisant.</translation>
-+<translation id="210445503571712769">Préférences synchronisées</translation>
-+<translation id="1608306110678187802">Imp&amp;rimer le cadre...</translation>
-+<translation id="7427315641433634153">MSCHAP</translation>
-+<translation id="6622980291894852883">Continuer à bloquer les images</translation>
-+<translation id="5937837224523037661">Lorsqu'un site utilise des plug-ins :</translation>
-+<translation id="4988792151665380515">Échec d'exportation de la clé publique</translation>
-+<translation id="6333049849394141510">Choisir les éléments à synchroniser</translation>
-+<translation id="446322110108864323">Paramètres de saisie du Pinyin</translation>
-+<translation id="4948468046837535074">Ouvrir les pages suivantes :</translation>
-+<translation id="5222676887888702881">Déconnexion</translation>
-+<translation id="6978121630131642226">Moteurs de recherche</translation>
-+<translation id="6839225236531462745">Erreur de suppression de certificat</translation>
-+<translation id="6745994589677103306">Ne rien faire</translation>
-+<translation id="855081842937141170">Épingler l'onglet</translation>
-+<translation id="6263541650532042179">réinitialiser la synchronisation</translation>
-+<translation id="6055392876709372977">PKCS #1 SHA-256 avec chiffrement RSA</translation>
-+<translation id="7903984238293908205">Katakana</translation>
-+<translation id="3781488789734864345">Choisir un réseau mobile</translation>
-+<translation id="268053382412112343">&amp;Historique</translation>
-+<translation id="2723893843198727027">Mode développeur :</translation>
-+<translation id="1722567105086139392">Lien</translation>
-+<translation id="2620436844016719705">Système</translation>
-+<translation id="5362741141255528695">Sélectionnez le fichier de clé privée.</translation>
-+<translation id="5292890015345653304">Insérez une carte SD ou une carte mémoire USB.</translation>
-+<translation id="5583370583559395927">Temps restant : <ph name="TIME_REMAINING"/></translation>
-+<translation id="8065982201906486420">Cliquez ici pour exécuter le plug-in <ph name="PLUGIN_NAME"/>.</translation>
-+<translation id="6219717821796422795">Hanyu</translation>
-+<translation id="3725367690636977613">pages</translation>
-+<translation id="2688477613306174402">Configuration en cours</translation>
-+<translation id="1195447618553298278">Erreur inconnue</translation>
-+<translation id="3353284378027041011"><ph name="NUMBER_FEW"/> days ago</translation>
-+<translation id="8811462119186190367">La langue utilisée pour Google Chrome est passée de &quot;<ph name="FROM_LOCALE"/>&quot; à &quot;<ph name="TO_LOCALE"/>&quot; après la synchronisation de vos paramètres.</translation>
-+<translation id="1087119889335281750">&amp;Aucune suggestion orthographique</translation>
-+<translation id="5228309736894624122">Erreur de protocole SSL</translation>
-+<translation id="8216170236829567922">Mode de saisie du thaï (clavier Pattachote)</translation>
-+<translation id="8464132254133862871">Ce compte utilisateur n'est pas compatible avec ce service.</translation>
-+<translation id="6812349420832218321"><ph name="PRODUCT_NAME"/> ne peut pas être exécuté en tant que root.</translation>
-+<translation id="5076340679995252485">C&amp;oller</translation>
-+<translation id="2904348843321044456">Paramètres de contenu...</translation>
-+<translation id="1055216403268280980">Dimensions de l'image</translation>
-+<translation id="1784284518684746740">Sélectionner le fichier à enregistrer sous</translation>
-+<translation id="7032947513385578725">Disque Flash</translation>
-+<translation id="5518442882456325299">Moteur de recherche actuel :</translation>
-+<translation id="14171126816530869">L'identité de <ph name="ORGANIZATION"/> situé à <ph name="LOCALITY"/> a été vérifiée par <ph name="ISSUER"/>.</translation>
-+<translation id="6263082573641595914">Version de l'autorité de certification Microsoft</translation>
-+<translation id="3105917916468784889">Enregistrer une capture d'écran</translation>
-+<translation id="1741763547273950878">Page sur <ph name="SITE"/></translation>
-+<translation id="1587275751631642843">Console &amp;JavaScript</translation>
-+<translation id="8460696843433742627">Réponse reçue incorrecte lors de la tentative de chargement de <ph name="URL"/>.
-+ Cela peut être dû à une opération de maintenance ou à une configuration incorrecte sur le serveur.</translation>
-+<translation id="297870353673992530">Serveur DNS :</translation>
-+<translation id="3222066309010235055">Pré-rendu : <ph name="PRERENDER_CONTENTS_NAME"/></translation>
-+<translation id="6410063390789552572">Impossible d'accéder à la bibliothèque réseau.</translation>
-+<translation id="6880587130513028875">Des images ont été bloquées sur cette page.</translation>
-+<translation id="851263357009351303">Toujours autoriser <ph name="HOST"/> à afficher les images</translation>
-+<translation id="3511307672085573050">Copier l'adr&amp;esse du lien</translation>
-+<translation id="1134009406053225289">Ouvrir dans une fenêtre de navigation privée</translation>
-+<translation id="6655190889273724601">Mode développeur</translation>
-+<translation id="1071917609930274619">Chiffrement des données</translation>
-+<translation id="3473105180351527598">Activer la protection contre le phishing et les logiciels malveillants</translation>
-+<translation id="6151323131516309312">Appuyez sur <ph name="SEARCH_KEY"/> pour rechercher sur <ph name="SITE_NAME"/></translation>
-+<translation id="3753317529742723206">Voulez-vous utiliser <ph name="HANDLER_TITLE"/> (<ph name="HANDLER_HOSTNAME"/>) au lieu de <ph name="REPLACED_HANDLER_TITLE"/> pour gérer les liens <ph name="PROTOCOL"/>:// à partir de maintenant ?</translation>
-+<translation id="6216679966696797604">Démarrer une session en tant qu'invité</translation>
-+<translation id="5456397824015721611">Nombre maximal de caractères chinois dans la mémoire tampon de pré-édition, notamment les entrées de symboles Zhuyin</translation>
-+<translation id="2055443983279698110">Barre de menus GNOME expérimentale disponible</translation>
-+<translation id="2342959293776168129">Effacer l'historique des téléchargements</translation>
-+<translation id="2503522102815150840">Navigateur bloqué...</translation>
-+<translation id="7201354769043018523">Parenthèse drte</translation>
-+<translation id="425878420164891689">Calcul du temps de chargement</translation>
-+<translation id="508794495705880051">Ajouter une carte de paiement...</translation>
-+<translation id="1425975335069981043">Itinérance :</translation>
-+<translation id="1272079795634619415">Arrêter</translation>
-+<translation id="5442787703230926158">Erreur de synchronisation...</translation>
-+<translation id="2462724976360937186">ID de clé de l'autorité de certification</translation>
-+<translation id="6786747875388722282">Extensions</translation>
-+<translation id="3944384147860595744">Imprimez où que vous soyez.</translation>
-+<translation id="2570648609346224037">Un problème est survenu lors du téléchargement de l'image de récupération.</translation>
-+<translation id="4306718255138772973">Cloud Print Proxy</translation>
-+<translation id="9053965862400494292">Une erreur s'est produite lors de la configuration de la synchronisation.</translation>
-+<translation id="8596540852772265699">Fichiers personnalisés</translation>
-+<translation id="7017354871202642555">Impossible de définir le mode une fois la fenêtre créée.</translation>
-+<translation id="3101709781009526431">Date et heure</translation>
-+<translation id="69375245706918574">Personnaliser les préférences de synchronisation</translation>
-+<translation id="833853299050699606">Aucune information disponible sur le forfait</translation>
-+<translation id="1737968601308870607">Signaler un problème</translation>
-+<translation id="4571852245489094179">Importer mes favoris et paramètres</translation>
-+<translation id="99648783926443049">Sélectionnez le <ph name="BEGIN_BOLD"/>menu clé à molette &gt; Paramètres &gt; Options avancées &gt; Modifier les paramètres du proxy<ph name="END_BOLD"/> et vérifiez que vos paramètres sont définis sur &quot;sans proxy&quot; ou &quot;direct&quot;.</translation>
-+<translation id="4421917670248123270">Fermer et annuler les téléchargements</translation>
-+<translation id="5605623530403479164">Autres moteurs de recherche</translation>
-+<translation id="8887243200615092733"><ph name="PRODUCT_NAME"/> peut maintenant synchroniser vos mots de passe. Pour protéger vos données, vous devez confirmer les informations relatives à votre compte.</translation>
-+<translation id="4740663705480958372">Cette fonctionnalité active les API P2P Pepper et P2P JavaScript. L'API est en cours de développement et n'est pas encore opérationnelle.</translation>
-+<translation id="5710435578057952990">L'identité de ce site Web n'a pas été vérifiée.</translation>
-+<translation id="1421046588786494306">Sessions à l'étranger</translation>
-+<translation id="1661245713600520330">Cette page répertorie tous les modules chargés dans le processus principal et les modules enregistrés de manière à être chargés ultérieurement.</translation>
-+<translation id="5451646087589576080">Afficher les &amp;infos sur le cadre</translation>
-+<translation id="3368922792935385530">Connecté</translation>
-+<translation id="3498309188699715599">Paramètres d'entrée en Chewing</translation>
-+<translation id="8486154204771389705">Conserver sur cette page</translation>
-+<translation id="3866443872548686097">Votre support de récupération est prêt. Vous pouvez le retirer du système.</translation>
-+<translation id="6824564591481349393">Copi&amp;er l'adresse e-mail</translation>
-+<translation id="907148966137935206">Interdire à tous les sites d'afficher des fenêtres pop-up (recommandé)</translation>
-+<translation id="5184063094292164363">Console &amp;JavaScript</translation>
-+<translation id="333371639341676808">Empêcher cette page de générer des boîtes de dialogue supplémentaires</translation>
-+<translation id="7632380866023782514">En haut à droite</translation>
-+<translation id="4925520021222027859">Entrez le mot de passe associé à votre application :</translation>
-+<translation id="3494768541638400973">Mode de saisie Google du japonais (pour clavier japonais)</translation>
-+<translation id="5844183150118566785"><ph name="PRODUCT_NAME"/> est à jour (<ph name="VERSION"/>)</translation>
-+<translation id="3118046075435288765">Le serveur a mis fin à la connexion de manière inattendue.</translation>
-+<translation id="8041140688818013446">Il est possible que le serveur hébergeant la page Web soit surchargé ou ait rencontré une erreur. Pour éviter de générer
-+ trop de trafic et d'aggraver la situation,
-+ <ph name="PRODUCT_NAME"/> a temporairement
-+ bloqué l'acceptation des requêtes adressées au serveur.
-+ <ph name="LINE_BREAK"/>
-+ Si vous pensez que ce comportement n'est pas souhaitable, (par exemple, dans le cas où vous déboguez votre propre site Web), vous pouvez
-+ consulter la page <ph name="NET_INTERNALS_PAGE"/>,
-+ sur laquelle vous pourrez trouver plus d'informations ou désactiver cette fonctionnalité.</translation>
-+<translation id="1725068750138367834">Gestionnaire de &amp;fichiers</translation>
-+<translation id="4254921211241441775">Arrêter la synchronisation du compte</translation>
-+<translation id="7791543448312431591">Ajouter</translation>
-+<translation id="8569764466147087991">Sélectionnez le fichier à ouvrir</translation>
-+<translation id="5449451542704866098">Aucun forfait de données</translation>
-+<translation id="307505906468538196">Créer un compte Google</translation>
-+<translation id="2053553514270667976">Code postal</translation>
-+<translation id="48838266408104654">&amp;Gestionnaire de tâches</translation>
-+<translation id="4378154925671717803">Téléphone</translation>
-+<translation id="3694027410380121301">Sélectionner l'onglet précédent</translation>
-+<translation id="6178664161104547336">Sélectionner un certificat</translation>
-+<translation id="1375321115329958930">Mots de passe enregistrés</translation>
-+<translation id="3341703758641437857">Autoriser l'accès aux URL de fichier</translation>
-+<translation id="5702898740348134351">Modifi&amp;er les moteurs de recherche...</translation>
-+<translation id="734303607351427494">Gérer les moteurs de recherche...</translation>
-+<translation id="8326478304147373412">PKCS #7, chaîne de certificats</translation>
-+<translation id="3242765319725186192">Clé pré-partagée :</translation>
-+<translation id="8089798106823170468">Contrôlez et partagez l'accès à vos imprimantes depuis n'importe quel compte Google.</translation>
-+<translation id="5984992849064510607">Ajoute l'option &quot;Utiliser les onglets latéraux&quot; au menu contextuel de la barre d'onglets. Utilisez cette option pour déplacer les onglets du haut de l'écran (affichage par défaut) vers le côté. Particulièrement utile sur les grands écrans.</translation>
-+<translation id="839736845446313156">S'inscrire</translation>
-+<translation id="4668929960204016307">,</translation>
-+<translation id="2409527877874991071">Saisissez un nouveau nom.</translation>
-+<translation id="4240069395079660403"><ph name="PRODUCT_NAME"/> ne peut pas être affiché dans cette langue.</translation>
-+<translation id="747114903913869239">Erreur : impossible de décoder l'extension.</translation>
-+<translation id="5412637665001827670">Clavier bulgare</translation>
-+<translation id="2113921862428609753">Accès aux informations de l'autorité</translation>
-+<translation id="5227536357203429560">Ajouter un réseau privé...</translation>
-+<translation id="732677191631732447">C&amp;opier l'URL du fichier audio</translation>
-+<translation id="7224023051066864079">Masque de sous-réseau :</translation>
-+<translation id="2401813394437822086">Impossible d'accéder à votre compte ?</translation>
-+<translation id="2344262275956902282">Utiliser les touches - et = pour paginer une liste d'entrées</translation>
-+<translation id="3609138628363401169">Le serveur ne prend pas en charge l'extension de renégociation TLS.</translation>
-+<translation id="3369624026883419694">Résolution de l'hôte...</translation>
-+<translation id="8870413625673593573">Récemment fermés</translation>
-+<translation id="9145357542626308749">Le certificat de sécurité du site a été signé avec un algorithme de signature faible.</translation>
-+<translation id="8502803898357295528">Votre mot de passe a été modifié</translation>
-+<translation id="4064488613268730704">Gérer les paramètres de saisie automatique...</translation>
-+<translation id="6830600606572693159">La page Web <ph name="URL"/> n'est pas disponible pour le moment. Cela peut être dû à une surcharge ou à une opération de maintenance.</translation>
-+<translation id="4145797339181155891">Éjecter</translation>
-+<translation id="7886793013438592140">Impossible de lancer le processus de service.</translation>
-+<translation id="8417944620073548444"><ph name="MEGABYTES"/> Mo restants</translation>
-+<translation id="7339898014177206373">Nouvelle fenêtre</translation>
-+<translation id="3026202950002788510">Sélectionnez
-+ <ph name="BEGIN_BOLD"/>
-+ Applications &gt; Préférences système &gt; Réseau &gt; Avancé &gt; Proxys
-+ <ph name="END_BOLD"/>
-+ et désélectionnez les serveurs proxy sélectionnés.</translation>
-+<translation id="7033648024564583278">Gravure en cours d'initialisation...</translation>
-+<translation id="2246340272688122454">Téléchargement de l'image de récupération...</translation>
-+<translation id="7770995925463083016">il y a <ph name="NUMBER_TWO"/> minutes</translation>
-+<translation id="2816269189405906839">Mode de saisie du chinois (cangjie)</translation>
-+<translation id="7087282848513945231">Comté</translation>
-+<translation id="2149951639139208969">Ouvrir l'adresse dans un nouvel onglet</translation>
-+<translation id="175196451752279553">&amp;Rouvrir l'onglet fermé</translation>
-+<translation id="5992618901488170220">Impossible d'afficher la page Web, car votre ordinateur est passé en mode
-+ veille ou hibernation. Dans ce cas, les connexions réseau sont
-+ coupées et les requêtes réseau échouent. L'actualisation de la page
-+ devrait permettre de résoudre ce problème.</translation>
-+<translation id="2655386581175833247">Certificat utilisateur :</translation>
-+<translation id="5039804452771397117">Autoriser</translation>
-+<translation id="5435964418642993308">Appuyer sur Entrée pour revenir en arrière et sur la touche de menu contextuel pour afficher l'historique</translation>
-+<translation id="81686154743329117">ZRM</translation>
-+<translation id="7564146504836211400">Cookies et autres données</translation>
-+<translation id="2266011376676382776">Page(s) ne répondant pas</translation>
-+<translation id="2714313179822741882">Paramètres d'entrée hangûl</translation>
-+<translation id="8658163650946386262">Configurer la synchronisation...</translation>
-+<translation id="3100609564180505575">Modules (<ph name="TOTAL_COUNT"/>). Conflits connus : <ph name="BAD_COUNT"/>, conflits probables : <ph name="SUSPICIOUS_COUNT"/></translation>
-+<translation id="3627671146180677314">Date de renouvellement du certificat Netscape</translation>
-+<translation id="1319824869167805246">Ouvrir tous les favoris dans une nouvelle fenêtre</translation>
-+<translation id="8652487083013326477">bouton radio concernant l'étendue de pages</translation>
-+<translation id="5204967432542742771">Saisissez votre mot de passe</translation>
-+<translation id="4388712255200933062"><ph name="CLOUD_PRINT_NAME"/> est conçu pour rendre l'impression plus intuitive, accessible et utile. <ph name="CLOUD_PRINT_NAME"/> vous permet de rendre vos imprimantes accessibles depuis n'importe quelle application Web ou mobile associée à <ph name="CLOUD_PRINT_NAME"/>.</translation>
-+<translation id="2932611376188126394">Dictionnaire de kanji unique</translation>
-+<translation id="5485754497697573575">Rétablir tous les onglets</translation>
-+<translation id="3371861036502301517">Échec de l'installation de l'extension</translation>
-+<translation id="644038709730536388">En savoir plus sur la manière de se protéger des logiciels malveillants en ligne</translation>
-+<translation id="2155931291251286316">Toujours afficher les fenêtres pop-up de <ph name="HOST"/></translation>
-+<translation id="3445830502289589282">Authentification phase 2 :</translation>
-+<translation id="5650551054760837876">Aucun résultat de recherche trouvé</translation>
-+<translation id="5494362494988149300">Ouvrir une fois le téléchargement &amp;terminé</translation>
-+<translation id="2956763290572484660"><ph name="COOKIES"/> cookies</translation>
-+<translation id="6989836856146457314">Mode de saisie du japonais (pour clavier américain)</translation>
-+<translation id="9187787570099877815">Continuer à bloquer les plug-ins</translation>
-+<translation id="8425492902634685834">Épingler sur la barre des tâches</translation>
-+<translation id="825608351287166772">Les certificats ont une période de validité, comme tous les documents relatifs à votre identité (tel qu'un passeport). Le certificat présenté à votre navigateur n'est pas encore valide ! Lorsqu'un certificat est en dehors de sa période de validité, il n'est pas nécessaire d'assurer la maintenance de certaines informations relatives à son état (s'il a été révoqué ou s'il n'est plus approuvé). Par conséquent, il est impossible de vérifier que le certificat est fiable. Ne poursuivez pas.</translation>
-+<translation id="741630086309232721">Fermer la session d'invité</translation>
-+<translation id="7309459761865060639">Contrôlez vos tâches d'impression et l'état de connexion de vos imprimantes en ligne.</translation>
-+<translation id="4803909571878637176">Désinstallation</translation>
-+<translation id="5209518306177824490">Empreinte SHA-1</translation>
-+<translation id="3300768886937313568">Modifier le code PIN de la carte SIM</translation>
-+<translation id="7447657194129453603">État du réseau :</translation>
-+<translation id="1553538517812678578">sans limite</translation>
-+<translation id="7947315300197525319">(Choisir une autre capture d'écran)</translation>
-+<translation id="3612070600336666959">Désactivation</translation>
-+<translation id="3759461132968374835">Aucune erreur n'a été signalée récemment. Les erreurs n'apparaissent ici que lorsque l'envoi de rapports d'erreur est activé.</translation>
-+<translation id="1516602185768225813">Rouvrir les dernières pages ouvertes</translation>
-+<translation id="189210018541388520">Ouvrir en mode plein écran</translation>
-+<translation id="8795668016723474529">Ajouter une carte de paiement</translation>
-+<translation id="5860033963881614850">Désactivé</translation>
-+<translation id="3956882961292411849">Chargement des informations sur votre forfait Internet mobile, veuillez patienter...</translation>
-+<translation id="689050928053557380">Acheter un forfait de données...</translation>
-+<translation id="4235618124995926194">Inclure cet e-mail :</translation>
-+<translation id="4874539263382920044">Le titre doit comporter au moins un caractère.</translation>
-+<translation id="798525203920325731">Espaces de noms réseau</translation>
-+<translation id="263325223718984101"><ph name="PRODUCT_NAME"/> n'a pas pu terminer l'installation, mais va poursuivre son exécution à partir de son image disque.</translation>
-+<translation id="7025190659207909717">Gestion des services Internet mobiles</translation>
-+<translation id="8265096285667890932">Utiliser les onglets latéraux</translation>
-+<translation id="4250680216510889253">Non</translation>
-+<translation id="3949593566929137881">Saisir le code PIN de la carte SIM</translation>
-+<translation id="6291953229176937411">&amp;Afficher dans le Finder</translation>
-+<translation id="2476990193835943955">Maintenez la touche Ctrl, Alt ou Maj enfoncée&lt;br&gt;pour afficher le raccourci clavier qui lui est associé.</translation>
-+<translation id="9187827965378254003">Vraiment désolé, aucun prototype n'est disponible pour le moment.</translation>
-+<translation id="8933960630081805351">&amp;Afficher dans le Finder</translation>
-+<translation id="3041612393474885105">Informations relatives au certificat</translation>
-+<translation id="7378810950367401542">/</translation>
-+<translation id="4611079913162790275">La synchronisation des mots de passe requiert votre attention.</translation>
-+<translation id="6562758426028728553">Veuillez saisir l'ancien et le nouveau code PIN.</translation>
-+<translation id="614161640521680948">Langue :</translation>
-+<translation id="3665650519256633768">Résultats de recherche</translation>
-+<translation id="3733127536501031542">Serveur SSL avec fonction d'optimisation</translation>
-+<translation id="3614837889828516995">Enregistrer en PDF</translation>
-+<translation id="5745056705311424885">Mémoire USB détectée</translation>
-+<translation id="5895875028328858187">M'avertir lorsque le flux de données est faible ou presque inexistant</translation>
-+<translation id="939598580284253335">Saisir le mot de passe multiterme</translation>
-+<translation id="7917972308273378936">Clavier lituanien</translation>
-+<translation id="8371806639176876412">Les éléments saisis dans le champ polyvalent peuvent être enregistrés.</translation>
-+<translation id="4216499942524365685">Les informations de connexion à votre compte sont obsolètes. Cliquez ici pour saisir à nouveau votre mot de passe.</translation>
-+<translation id="8899388739470541164">Vietnamien</translation>
-+<translation id="4091434297613116013">feuilles de papier</translation>
-+<translation id="7475671414023905704">URL de mot de passe perdu Netscape</translation>
-+<translation id="3335947283844343239">Rouvrir l'onglet fermé</translation>
-+<translation id="4089663545127310568">Effacer les mots de passe enregistrés</translation>
-+<translation id="6500444002471948304">Créer un nouveau dossier...</translation>
-+<translation id="2480626392695177423">Basculer en mode ponctuation pleine chasse ou demi-chasse</translation>
-+<translation id="5830410401012830739">Gérer les paramètres de localisation...</translation>
-+<translation id="8977410484919641907">Synchronisé...</translation>
-+<translation id="2794293857160098038">Options de recherche par défaut</translation>
-+<translation id="3947376313153737208">Aucune sélection</translation>
-+<translation id="1346104802985271895">Mode de saisie du vietnamien (TELEX)</translation>
-+<translation id="5935630983280450497"><ph name="NUMBER_ONE"/> minute restante</translation>
-+<translation id="5889282057229379085">Le nombre maximal d'autorités de certification intermédiaires a été dépassé : <ph name="NUM_INTERMEDIATE_CA"/></translation>
-+<translation id="3180365125572747493">Saisissez un mot de passe pour chiffrer ce fichier de certificat.</translation>
-+<translation id="5496587651328244253">Organiser</translation>
-+<translation id="4821086771593057290">Votre mot de passe a changé. Veuillez réessayer avec votre nouveau mot de passe.</translation>
-+<translation id="7075513071073410194">PKCS #1 MD5 avec chiffrement RSA</translation>
-+<translation id="4378727699507047138">Utiliser le thème classique</translation>
-+<translation id="7124398136655728606">Échap efface toute la mémoire tampon de pré-édition</translation>
-+<translation id="8293206222192510085">Ajouter aux favoris</translation>
-+<translation id="2592884116796016067">Un incident est survenu sur une partie de cette page (HTML WebWorker). Elle risque de ne pas fonctionner correctement.</translation>
-+<translation id="2529133382850673012">Clavier américain</translation>
-+<translation id="4411578466613447185">Signataire de code</translation>
-+<translation id="1354868058853714482">Adobe Reader n'est pas à jour et risque de ne plus être sécurisé.</translation>
-+<translation id="6252594924928912846">Personnaliser les paramètres de synchronisation...</translation>
-+<translation id="8425755597197517046">Co&amp;ller et rechercher</translation>
-+<translation id="1093148655619282731">Détails du certificat sélectionné :</translation>
-+<translation id="5568069709869097550">Impossible de se connecter</translation>
-+<translation id="2743322561779022895">Activation :</translation>
-+<translation id="4181898366589410653">Système de révocation introuvable dans le certificat du serveur</translation>
-+<translation id="8705331520020532516">Numéro de série</translation>
-+<translation id="1665770420914915777">Afficher la page &quot;Nouvel onglet&quot;</translation>
-+<translation id="2629089419211541119">il y a <ph name="NUMBER_ONE"/> heure</translation>
-+<translation id="1691063574428301566">Votre ordinateur redémarrera une fois la mise à jour effectuée.</translation>
-+<translation id="131364520783682672">Verr. maj.</translation>
-+<translation id="6259308910735500867">L'accès au répertoire de l'hôte de communication à distance a été refusé. Essayez avec un autre compte.</translation>
-+<translation id="3415261598051655619">Accessible aux scripts :</translation>
-+<translation id="2335122562899522968">Cette page place des cookies.</translation>
-+<translation id="8461914792118322307">Proxy</translation>
-+<translation id="4089521618207933045">Avec sous-menu</translation>
-+<translation id="1936157145127842922">Afficher dans le dossier</translation>
-+<translation id="6982279413068714821">il y a <ph name="NUMBER_DEFAULT"/> minutes</translation>
-+<translation id="7977590112176369853">&lt;saisir une requête&gt;</translation>
-+<translation id="3449839693241009168">Appuyez sur <ph name="SEARCH_KEY"/> pour envoyer des commandes à <ph name="EXTENSION_NAME"/>.</translation>
-+<translation id="7443484992065838938">Prévisualiser le rapport</translation>
-+<translation id="5714678912774000384">Activer le dernier onglet</translation>
-+<translation id="3799598397265899467">Lorsque je quitte le navigateur</translation>
-+<translation id="2125314715136825419">Continuer sans mettre à jour Adobe Reader (non recommandé)</translation>
-+<translation id="1120026268649657149">Le champ de mot clé doit être vide ou comporter un mot unique</translation>
-+<translation id="542318722822983047">Déplacer le curseur automatiquement au caractère suivant</translation>
-+<translation id="5317780077021120954">Enregistrer</translation>
-+<translation id="9027459031423301635">Ouvrir le lien dans un nouvel ongle&amp;t</translation>
-+<translation id="2251809247798634662">Nouvelle fenêtre de navigation privée</translation>
-+<translation id="358344266898797651">Celtique</translation>
-+<translation id="3625870480639975468">Réinitialiser le zoom</translation>
-+<translation id="5199729219167945352">Prototypes</translation>
-+<translation id="5055518462594137986">Mémoriser mes choix pour tous les liens de ce type</translation>
-+<translation id="246059062092993255">Les plug-ins de cette page ont été bloqués.</translation>
-+<translation id="2870560284913253234">Site</translation>
-+<translation id="6945221475159498467">Sélectionner</translation>
-+<translation id="7724603315864178912">Couper</translation>
-+<translation id="4164507027399414915">Restaurer toutes les miniatures supprimées</translation>
-+<translation id="917051065831856788">Utiliser les onglets latéraux</translation>
-+<translation id="1976150099241323601">Se connecter au dispositif de sécurité</translation>
-+<translation id="6620110761915583480">Enregistrer le fichier</translation>
-+<translation id="4988526792673242964">Pages</translation>
-+<translation id="7543025879977230179">Options de <ph name="PRODUCT_NAME"/></translation>
-+<translation id="2175607476662778685">Barre de lancement rapide</translation>
-+<translation id="6434309073475700221">Annuler</translation>
-+<translation id="1367951781824006909">Choisir un fichier</translation>
-+<translation id="1425127764082410430">&amp;Rechercher <ph name="SEARCH_TERMS"/> avec <ph name="SEARCH_ENGINE"/></translation>
-+<translation id="684265517037058883">(pas encore valide)</translation>
-+<translation id="2027538664690697700">Mettre à jour le plug-in...</translation>
-+<translation id="8205333955675906842">Police Sans-Serif</translation>
-+<translation id="39964277676607559">Impossible de charger le JavaScript &quot;<ph name="RELATIVE_PATH"/>&quot; du script de contenu.</translation>
-+<translation id="4378551569595875038">Connexion...</translation>
-+<translation id="7029809446516969842">Mots de passe</translation>
-+<translation id="8053278772142718589">Fichiers PKCS #12</translation>
-+<translation id="3129020372442395066">Options de saisie automatique de <ph name="PRODUCT_NAME_SHORT"/></translation>
-+<translation id="4114360727879906392">Fenêtre précédente</translation>
-+<translation id="8238649969398088015">Astuce</translation>
-+<translation id="5958418293370246440"><ph name="SAVED_FILES"/> / <ph name="TOTAL_FILES"/> fichiers</translation>
-+<translation id="2350172092385603347">Localisation utilisée, mais les paramètres régionaux par défaut (default_locale) n'ont pas été indiqués dans le manifeste. </translation>
-+<translation id="8221729492052686226">Si vous n'êtes pas à l'origine de cette requête, il s'agit probablement d'une attaque contre votre système. Si vous n'avez pas lancé cette requête de manière intentionnelle, cliquez sur Ne rien faire.</translation>
-+<translation id="5894314466642127212">Votre commentaire a bien été envoyé.</translation>
-+<translation id="894360074127026135">Fonction d'optimisation internationale Netscape </translation>
-+<translation id="6025294537656405544">Taille de police minimale</translation>
-+<translation id="1201402288615127009">Suivant</translation>
-+<translation id="1335588927966684346">Utilitaire :</translation>
-+<translation id="7857823885309308051">Cette opération peut prendre une minute...</translation>
-+<translation id="662870454757950142">Le format du mot de passe est incorrect.</translation>
-+<translation id="370665806235115550">Chargement...</translation>
-+<translation id="1808792122276977615">Ajouter la page...</translation>
-+<translation id="2076269580855484719">Masquer ce plug-in</translation>
-+<translation id="254416073296957292">&amp;Paramètres linguistiques...</translation>
-+<translation id="6652975592920847366">Créer un support de récupération du système d'exploitation</translation>
-+<translation id="52912272896845572">Le fichier de clé privée est incorrect.</translation>
-+<translation id="3232318083971127729">Valeur :</translation>
-+<translation id="8807632654848257479">Stable</translation>
-+<translation id="4209092469652827314">Grande</translation>
-+<translation id="4222982218026733335">Certificat serveur invalide</translation>
-+<translation id="152234381334907219">Jamais enregistrés</translation>
-+<translation id="5600599436595580114">Cette page a été préchargée.</translation>
-+<translation id="8926468725336609312">Google Chrome ne peut pas afficher l'aperçu avant impression lorsque la visionneuse de documents PDF intégrée est désactivée. Pour l'afficher, veuillez accéder à <ph name="BEGIN_LINK"/>chrome://plugins<ph name="END_LINK"/>, activer &quot;Chrome PDF Viewer&quot; et réessayer.</translation>
-+<translation id="8494214181322051417">Nouveau !</translation>
-+<translation id="7762841930144642410"><ph name="BEGIN_BOLD"/>Vous êtes passé en navigation privée<ph name="END_BOLD"/>. Les pages que vous consultez dans cette fenêtre n'apparaîtront ni dans l'historique de votre navigateur, ni dans l'historique des recherches, et ne laisseront aucune trace (comme les cookies) sur votre ordinateur une fois que vous aurez fermé la fenêtre de navigation privée. Tous les fichiers téléchargés et les favoris créés seront toutefois conservés. <ph name="LINE_BREAK"/> <ph name="BEGIN_BOLD"/>Passer en navigation privée n'a aucun effet sur les autres utilisateurs, serveurs ou logiciels. Méfiez-vous :<ph name="END_BOLD"/> <ph name="BEGIN_LIST"/> <ph name="BEGIN_LIST_ITEM"/>Des sites Web qui collectent ou partagent des informations vous concernant<ph name="END_LIST_ITEM"/> <ph name="BEGIN_LIST_ITEM"/>Des fournisseurs d'accès Internet ou des employeurs qui conservent une trace des pages que vous visitez<ph name="END_LIST_ITEM"/> <ph name="BEGIN_LIST_ITEM"/>Des programmes indésirables qui enregistrent vos frappes en échange d'émoticônes gratuites<ph name="END_LIST_ITEM"/> <ph name="BEGIN_LIST_ITEM"/>Des personnes qui pourraient surveiller vos activités<ph name="END_LIST_ITEM"/> <ph name="BEGIN_LIST_ITEM"/>Des personnes qui se tiennent derrière vous<ph name="END_LIST_ITEM"/> <ph name="END_LIST"/> <ph name="BEGIN_LINK"/>En savoir plus sur la navigation privée<ph name="END_LINK"/></translation>
-+<translation id="2386255080630008482">Le certificat du serveur a été révoqué.</translation>
-+<translation id="2135787500304447609">&amp;Reprendre</translation>
-+<translation id="8309505303672555187">Sélectionnez un réseau :</translation>
-+<translation id="6143635259298204954">Impossible d'extraire les fichiers de l'extension. Pour effectuer cette opération en toute sécurité, vous devez disposer d'un chemin d'accès à votre répertoire de profils ne contenant pas de lien symbolique. Aucun chemin de ce type n'existe pour votre profil.</translation>
-+<translation id="1813414402673211292">Effacer les données de navigation</translation>
-+<translation id="4062903950301992112">Si vous êtes conscient que la visite de ce site peut être préjudiciable à votre ordinateur, vous pouvez <ph name="PROCEED_LINK"/>.</translation>
-+<translation id="32330993344203779">Votre périphérique est inscrit pour bénéficier de la gestion d'entreprise.</translation>
-+<translation id="2356762928523809690">Serveur de mise à jour non disponible (erreur : <ph name="ERROR_NUMBER"/>)</translation>
-+<translation id="219008588003277019">Module client natif : <ph name="NEXE_NAME"/></translation>
-+<translation id="5436510242972373446">Rechercher sur <ph name="SITE_NAME"/> :</translation>
-+<translation id="3800764353337460026">Style de symboles</translation>
-+<translation id="6719684875142564568"><ph name="NUMBER_ZERO"/> hours</translation>
-+<translation id="2096368010154057602">Département</translation>
-+<translation id="1036561994998035917">Continuer à utiliser <ph name="ENGINE_NAME"/></translation>
-+<translation id="8730621377337864115">OK</translation>
-+<translation id="665757950158579497">Essayez de désactiver les prédictions d'actions du réseau en procédant comme suit :
-+ Sélectionnez le
-+ <ph name="BEGIN_BOLD"/>
-+ menu clé à molette &gt;
-+ <ph name="SETTINGS_TITLE"/>
-+ &gt;
-+ <ph name="ADVANCED_TITLE"/>
-+ <ph name="END_BOLD"/>
-+ et désélectionnez &quot;<ph name="NO_PREFETCH_DESCRIPTION"/>&quot;.
-+ Si le problème n'est pas résolu, nous vous conseillons de sélectionner de nouveau
-+ cette option pour améliorer les performances.</translation>
-+<translation id="4932733599132424254">Date</translation>
-+<translation id="6267166720438879315">Sélectionnez un certificat pour vous authentifier sur <ph name="HOST_NAME"/>.</translation>
-+<translation id="2422927186524098759">Barre latérale</translation>
-+<translation id="7839809549045544450">La clé publique éphémère Diffie-Hellman associée au serveur est peu sûre.</translation>
-+<translation id="5515806255487262353">Rechercher dans Dictionnaire</translation>
-+<translation id="350048665517711141">Sélectionnez un moteur de recherche</translation>
-+<translation id="2790805296069989825">Clavier russe</translation>
-+<translation id="5708171344853220004">Nom Microsoft principal</translation>
-+<translation id="5464696796438641524">Clavier polonais</translation>
-+<translation id="2080010875307505892">Clavier serbe</translation>
-+<translation id="2953767478223974804"><ph name="NUMBER_ONE"/> minute</translation>
-+<translation id="201192063813189384">Erreur lors de la lecture des données du cache.</translation>
-+<translation id="7851768487828137624">Canary</translation>
-+<translation id="6129938384427316298">Commentaire du certificat Netscape</translation>
-+<translation id="8210608804940886430">Page suivante</translation>
-+<translation id="9065596142905430007"><ph name="PRODUCT_NAME"/> est à jour.</translation>
-+<translation id="1035650339541835006">Paramètres de saisie automatique...</translation>
-+<translation id="6315493146179903667">Tout ramener au premier plan</translation>
-+<translation id="1000498691615767391">Sélectionner le dossier à ouvrir</translation>
-+<translation id="3593152357631900254">Activer le mode Pinyin fuzzy</translation>
-+<translation id="5015344424288992913">Résolution du proxy...</translation>
-+<translation id="8506299468868975633">Le téléchargement de l'image a été interrompu.</translation>
-+<translation id="4724168406730866204">Eten 26</translation>
-+<translation id="308268297242056490">URI</translation>
-+<translation id="4479812471636796472">Clavier Dvorak américain</translation>
-+<translation id="8673026256276578048">Rechercher sur le Web...</translation>
-+<translation id="1437307674059038925">Si vous utilisez un serveur proxy, vérifiez les paramètres associés ou demandez à votre administrateur réseau
-+ si ce serveur fonctionne.</translation>
-+<translation id="149347756975725155">Impossible de charger l'icône de l'extension &quot;<ph name="ICON"/>&quot;.</translation>
-+<translation id="3675321783533846350">Définir un proxy pour se connecter au réseau</translation>
-+<translation id="5451285724299252438">zone de texte concernant l'étendue de pages</translation>
-+<translation id="5669267381087807207">Activation</translation>
-+<translation id="7434823369735508263">Clavier Dvorak britannique</translation>
-+<translation id="1572103024875503863"><ph name="NUMBER_MANY"/> jours</translation>
-+<translation id="2084978867795361905">MS-IME</translation>
-+<translation id="7227669995306390694">Aucun forfait de données <ph name="NETWORK"/></translation>
-+<translation id="3481915276125965083">Les fenêtres pop-up suivantes ont été bloquées sur cette page :</translation>
-+<translation id="7163503212501929773"><ph name="NUMBER_MANY"/> heures restantes</translation>
-+<translation id="7705276765467986571">Impossible de charger le modèle du favori.</translation>
-+<translation id="1196338895211115272">Échec d'exportation de la clé privée</translation>
-+<translation id="5586329397967040209">Utiliser comme page d'accueil</translation>
-+<translation id="629730747756840877">Compte</translation>
-+<translation id="8525306231823319788">Plein écran</translation>
-+<translation id="9054208318010838">Autoriser tous les sites à suivre ma position géographique</translation>
-+<translation id="3058212636943679650">Si la restauration du système d'exploitation de votre ordinateur s'avère nécessaire, une carte SD ou une clé USB de récupération vous sera demandée.</translation>
-+<translation id="2815382244540487333">Les cookies suivants ont été bloqués :</translation>
-+<translation id="8882395288517865445">Inclure les adresses de ma fiche de Carnet d’adresses</translation>
-+<translation id="374530189620960299">Le certificat de sécurité du site n'est pas approuvé !</translation>
-+<translation id="8852407435047342287">Votre liste d'applications, d'extensions et de thèmes installés</translation>
-+<translation id="5647283451836752568">Exécuter tous les plug-ins de cette page</translation>
-+<translation id="8642947597466641025">Augmente la taille du texte</translation>
-+<translation id="5188181431048702787">Accepter et continuer »</translation>
-+<translation id="1293556467332435079">Fichiers
-+</translation>
-+<translation id="2490270303663597841">Appliquer uniquement à cette session de navigation privée</translation>
-+<translation id="1757915090001272240">Latin large</translation>
-+<translation id="8496717697661868878">Exécuter ce plug-in</translation>
-+<translation id="3450660100078934250">MasterCard</translation>
-+<translation id="2916073183900451334">Sur le Web, Tab permet de sélectionner les liens, ainsi que les champs de formulaire.</translation>
-+<translation id="7772127298218883077">À propos de <ph name="PRODUCT_NAME"/></translation>
-+<translation id="2090876986345970080">Paramètres de sécurité du système</translation>
-+<translation id="9219103736887031265">Images</translation>
-+<translation id="5453632173748266363">Cyrillique</translation>
-+<translation id="1008557486741366299">Pas maintenant</translation>
-+<translation id="8415351664471761088">Attendre la fin du téléchargement</translation>
-+<translation id="1545775234664667895">Thème &quot;<ph name="THEME_NAME"/>&quot; installé</translation>
-+<translation id="5329858601952122676">&amp;Supprimer</translation>
-+<translation id="6100736666660498114">Menu Démarrer</translation>
-+<translation id="3994878504415702912">&amp;Zoom</translation>
-+<translation id="9009369504041480176">Transfert en cours (<ph name="PROGRESS_PERCENT"/> %)...</translation>
-+<translation id="8995603266996330174">Géré par <ph name="DOMAIN"/></translation>
-+<translation id="5602600725402519729">&amp;Rafraîchir</translation>
-+<translation id="172612876728038702">Configuration du module de plate-forme sécurisée (TPM) en cours. Veuillez patienter, cela peut prendre quelques minutes.</translation>
-+<translation id="1362165759943288856">Vous avez acheté une quantité illimitée de données le <ph name="DATE"/>.</translation>
-+<translation id="2078019350989722914">Confirmer avant de quitter (<ph name="KEY_EQUIVALENT"/>)</translation>
-+<translation id="7965010376480416255">Mémoire partagée</translation>
-+<translation id="6248988683584659830">Rech. dans les paramètres</translation>
-+<translation id="8323232699731382745">mot de passe d'accès au réseau</translation>
-+<translation id="6588399906604251380">Activer la vérification orthographique</translation>
-+<translation id="7167621057293532233">Types de données</translation>
-+<translation id="7053983685419859001">Bloquer</translation>
-+<translation id="2485056306054380289">Certificat de l'autorité de certification du serveur :</translation>
-+<translation id="6462109140674788769">Clavier grec</translation>
-+<translation id="2727712005121231835">Taille réelle</translation>
-+<translation id="8887733174653581061">Toujours en haut</translation>
-+<translation id="5581211282705227543">Aucun plug-in installé.</translation>
-+<translation id="610886263749567451">Alerte JavaScript</translation>
-+<translation id="5488468185303821006">Autoriser en mode navigation privée</translation>
-+<translation id="6556866813142980365">Rétablir</translation>
-+<translation id="2107287771748948380"><ph name="OBFUSCATED_CC_NUMBER"/>, expire le : <ph name="CC_EXPIRATION_DATE"/></translation>
-+<translation id="6584811624537923135">Confirmer la désinstallation</translation>
-+<translation id="7429235532957570505">Impossible de désactiver les plug-ins ayant été activés par une stratégie d'entreprise.</translation>
-+<translation id="7866522434127619318">Cette fonctionnalité active l'option &quot;Lire en un clic&quot; dans les paramètres de contenu du plug-in.</translation>
-+<translation id="8860923508273563464">Attendre la fin des téléchargements</translation>
-+<translation id="6406506848690869874">Synchronisation</translation>
-+<translation id="5288678174502918605">&amp;Rouvrir l'onglet fermé</translation>
-+<translation id="7238461040709361198">Votre mot de passe de compte Google a changé depuis votre dernière connexion à partir de cet ordinateur.</translation>
-+<translation id="1956050014111002555">Le fichier contenait plusieurs certificats, aucun d'eux n'a été importé :</translation>
-+<translation id="302620147503052030">Afficher le bouton</translation>
-+<translation id="5512074755152723588">La saisie dans le champ polyvalent d'une URL déjà ouverte dans un autre onglet entraîne l'affichage de l'onglet en question, et non l'affichage de l'URL dans l'onglet actuel.</translation>
-+<translation id="9157595877708044936">Configuration en cours...</translation>
-+<translation id="4475552974751346499">Rechercher dans les téléchargements</translation>
-+<translation id="3021256392995617989">Me demander lorsqu'un site tente de suivre ma position géographique (recommandé)</translation>
-+<translation id="5185386675596372454">La nouvelle version de &quot;<ph name="EXTENSION_NAME"/>&quot; a été désactivée, car elle nécessite davantage d'autorisations.</translation>
-+<translation id="4285669636069255873">Clavier phonétique russe</translation>
-+<translation id="4148925816941278100">American Express</translation>
-+<translation id="2320435940785160168">Ce serveur exige un certificat d'authentification et n'a pas accepté celui envoyé par le navigateur.
-+Votre certificat a peut-être expiré ou le serveur n'a pas approuvé l'émetteur.
-+Réessayez avec un autre certificat si vous en avez un.
-+Sinon, vous devrez en obtenir un nouveau d'un autre émetteur.</translation>
-+<translation id="6295228342562451544">Lorsque vous vous connectez à un site Web sécurisé, le serveur hébergeant ce site présente à votre navigateur un &quot;certificat&quot; afin de vérifier l'identité du site. Ce certificat contient des informations d'identité, telles que l'adresse du site Web, laquelle est vérifiée par un tiers approuvé par votre ordinateur. En vérifiant que l'adresse du certificat correspond à l'adresse du site Web, il est possible de s'assurer que vous êtes connecté de façon sécurisée avec le site Web souhaité et non pas avec un tiers (tel qu'un pirate informatique sur votre réseau).</translation>
-+<translation id="6342069812937806050">À l'instant</translation>
-+<translation id="5605716740717446121">Votre carte SIM sera définitivement désactivée si vous ne saisissez pas correctement la clé de déverrouillage du code PIN. Nombre de tentatives restantes : <ph name="TRIES_COUNT"/></translation>
-+<translation id="8836712291807476944"><ph name="SAVED_BYTES"/> / <ph name="TOTAL_BYTES"/> octets, Interrompu</translation>
-+<translation id="5502500733115278303">Importés depuis Firefox</translation>
-+<translation id="569109051430110155">Détection automatique</translation>
-+<translation id="4408599188496843485">&amp;Aide</translation>
-+<translation id="5399158067281117682">Les codes PIN sont différents !</translation>
-+<translation id="8494234776635784157">Contenu Web</translation>
-+<translation id="2681441671465314329">Vider le cache</translation>
-+<translation id="3646789916214779970">Rétablir le thème par défaut</translation>
-+<translation id="1592960452683145077">Le service de communication à distance a démarré correctement. Vous devriez maintenant pouvoir vous connecter à distance à cet ordinateur.</translation>
-+<translation id="1679068421605151609">Outils de développement</translation>
-+<translation id="6648524591329069940">Police Serif</translation>
-+<translation id="6896758677409633944">Copier</translation>
-+<translation id="5260508466980570042">Adresse e-mail ou mot de passe incorrect. Veuillez réessayer.</translation>
-+<translation id="7887998671651498201">Le plug-in suivant ne répond pas : souhaitez-vous interrompre <ph name="PLUGIN_NAME"/> ?</translation>
-+<translation id="173188813625889224">Sens</translation>
-+<translation id="8088823334188264070"><ph name="NUMBER_MANY"/> secondes</translation>
-+<translation id="1337036551624197047">Clavier tchèque</translation>
-+<translation id="4212108296677106246">Voulez-vous que &quot;<ph name="CERTIFICATE_NAME"/>&quot; soit considérée comme une autorité de certification fiable ?</translation>
-+<translation id="2861941300086904918">Gestionnaire de sécurité natif du client</translation>
-+<translation id="6991443949605114807">&lt;p&gt;Lorsque vous exécutez <ph name="PRODUCT_NAME"/> dans un environnement de bureau pris en charge, les paramètres proxy du système sont utilisés. Toutefois, soit votre système n'est pas pris en charge, soit un problème est survenu lors du lancement de votre configuration système.&lt;/p&gt;
-+
-+ &lt;p&gt;Vous avez toujours la possibilité d'effectuer la configuration via la ligne de commande. Pour plus d'informations sur les indicateurs et les variables d'environnement, veuillez vous reporter à &lt;code&gt;man <ph name="PRODUCT_BINARY_NAME"/>&lt;/code&gt;.&lt;/p&gt;</translation>
-+<translation id="9071590393348537582">La page Web à l'adresse <ph name="URL"/> a déclenché trop de redirections. Pour résoudre le problème, effacez les cookies de ce site ou autorisez les cookies tiers. Si le problème persiste, il peut être dû à une mauvaise configuration du serveur et n'être aucunement lié à votre ordinateur.</translation>
-+<translation id="7205869271332034173">SSID :</translation>
-+<translation id="7084579131203911145">Nom du forfait :</translation>
-+<translation id="5815645614496570556">Adresse X.400</translation>
-+<translation id="3551320343578183772">Fermer l'onglet</translation>
-+<translation id="3345886924813989455">Impossible de trouver un navigateur pris en charge.</translation>
-+<translation id="74354239584446316">Le compte associé à la boutique en ligne est le suivant : <ph name="EMAIL_ADDRESS"/>. L'utilisation d'un autre compte pour la synchronisation provoque des erreurs.</translation>
-+<translation id="3712897371525859903">Enregistrer la p&amp;age sous...</translation>
-+<translation id="7926251226597967072"><ph name="PRODUCT_NAME"/> importe actuellement les éléments suivants à partir de <ph name="IMPORT_BROWSER_NAME"/> :</translation>
-+<translation id="2767649238005085901">Appuyez sur Entrée pour avancer et sur la touche de menu contextuel pour afficher l'historique</translation>
-+<translation id="8580634710208701824">Actualiser le cadre</translation>
-+<translation id="1018656279737460067">Annulé</translation>
-+<translation id="7606992457248886637">Autorités</translation>
-+<translation id="707392107419594760">Sélectionnez votre clavier :</translation>
-+<translation id="2007404777272201486">Signaler un problème...</translation>
-+<translation id="2390045462562521613">Ignorer ce réseau</translation>
-+<translation id="3348038390189153836">Nouveau matériel détecté</translation>
-+<translation id="1666788816626221136">Vous disposez de certificats qui n'appartiennent à aucune autre catégorie :</translation>
-+<translation id="4821935166599369261">&amp;Profilage activé</translation>
-+<translation id="1603914832182249871">(Navigation privée)</translation>
-+<translation id="7910768399700579500">&amp;Nouveau dossier</translation>
-+<translation id="7472639616520044048">Types MIME :</translation>
-+<translation id="2307164895203900614">Afficher les pages en arrière-plan (<ph name="NUM_BACKGROUND_APPS"/>)</translation>
-+<translation id="3192947282887913208">Fichiers audio</translation>
-+<translation id="6295535972717341389">Plug-ins</translation>
-+<translation id="8116190140324504026">Plus d'informations...</translation>
-+<translation id="7469894403370665791">Se connecter automatiquement à ce réseau</translation>
-+<translation id="4807098396393229769">Titulaire de la carte</translation>
-+<translation id="4094130554533891764">Elle peut désormais accéder à :</translation>
-+<translation id="4131410914670010031">Noir et blanc</translation>
-+<translation id="3800503346337426623">Ignorer la connexion et naviguer en tant qu'invité</translation>
-+<translation id="2615413226240911668">Toutefois, cette page inclut d'autres ressources qui ne sont pas sécurisées. Ces ressources peuvent être consultées par des tiers pendant leur transfert, et modifiées par un pirate informatique dans le but de changer l'aspect et le comportement de cette page.</translation>
-+<translation id="5880867612172997051">Accès réseau interrompu</translation>
-+<translation id="7842346819602959665">La dernière version de l'extension &quot;<ph name="EXTENSION_NAME"/>&quot; requiert d'autres permissions. Elle a donc été désactivée.</translation>
-+<translation id="3776667127601582921">Dans ce cas, le certificat du serveur ou un certificat d'autorité intermédiaire présenté à votre navigateur n'est pas valide. Cela peut signifier que le certificat est incorrect, qu'il contient des champs non valides ou qu'il n'est pas compatible.</translation>
-+<translation id="2412835451908901523">Veuillez saisir la clé de déverrouillage du code PIN à 8 chiffres fournie par <ph name="CARRIER_ID"/>.</translation>
-+<translation id="6979448128170032817">Exceptions...</translation>
-+<translation id="7584802760054545466">Connexion à <ph name="NETWORK_ID"/></translation>
-+<translation id="208047771235602537">Voulez-vous vraiment quitter <ph name="PRODUCT_NAME"/> alors qu'un téléchargement est en cours ?</translation>
-+<translation id="4060383410180771901">Le site Web ne parvient pas à gérer la demande associée à <ph name="URL"/>.</translation>
-+<translation id="6710213216561001401">Précédent</translation>
-+<translation id="1108600514891325577">&amp;Arrêter</translation>
-+<translation id="6035087343161522833">Lorsque l'option permettant de bloquer l'enregistrement des cookies tiers est activée, la lecture de ces cookies est également bloquée.</translation>
-+<translation id="8619892228487928601"><ph name="CERTIFICATE_NAME"/> : <ph name="ERROR"/></translation>
-+<translation id="1567993339577891801">Console JavaScript</translation>
-+<translation id="1548132948283577726">Les sites pour lesquels vos mots de passe ne seront jamais enregistrés s'afficheront ici.</translation>
-+<translation id="583281660410589416">Inconnu</translation>
-+<translation id="3774278775728862009">Mode de saisie du thaï (clavier TIS-820.2538)</translation>
-+<translation id="9115675100829699941">&amp;Favoris</translation>
-+<translation id="2485422356828889247">Désinstaller</translation>
-+<translation id="2621889926470140926">Voulez-vous vraiment quitter <ph name="PRODUCT_NAME"/> alors que <ph name="DOWNLOAD_COUNT"/> téléchargements sont en cours ?</translation>
-+<translation id="7279701417129455881">Configurer le blocage des cookies...</translation>
-+<translation id="1166359541137214543">ABC</translation>
-+<translation id="5412713837047574330">L'application hébergée par <ph name="HOST_NAME"/> est inaccessible, car vous êtes déconnecté du réseau. Cette page s'affichera dès que la connexion réseau sera rétablie. &lt;br&gt;</translation>
-+<translation id="5528368756083817449">Gestionnaire de favoris</translation>
-+<translation id="7275974018215686543"><ph name="NUMBER_MANY"/> secs ago</translation>
-+<translation id="215753907730220065">Quitter le mode plein écran</translation>
-+<translation id="7849264908733290972">Ouvrir l'&amp;image dans un nouvel onglet</translation>
-+<translation id="1560991001553749272">Favori ajouté !</translation>
-+<translation id="3966072572894326936">Choisir un autre dossier...</translation>
-+<translation id="8766796754185931010">Kotoeri</translation>
-+<translation id="7781829728241885113">Hier</translation>
-+<translation id="2762402405578816341">Synchroniser automatiquement les éléments suivants :</translation>
-+<translation id="1623661092385839831">Votre ordinateur intègre un périphérique de sécurité TPM (module de plate-forme sécurisée) qui permet de mettre en œuvre plusieurs fonctionnalités de sécurité critiques dans Google Chrome OS.</translation>
-+<translation id="3359256513598016054">Contraintes des stratégies de certificat</translation>
-+<translation id="4433914671537236274">Créer un support de récupération</translation>
-+<translation id="4509345063551561634">Emplacement :</translation>
-+<translation id="7596288230018319236">Toutes les pages que vous consultez apparaîtront ici à moins que vous ne les ouvriez dans une fenêtre en navigation privée. Vous pouvez utiliser le bouton Rechercher de cette page pour rechercher dans toutes les pages de votre historique.</translation>
-+<translation id="7434509671034404296">Options pour les développeurs</translation>
-+<translation id="6447842834002726250">Cookies</translation>
-+<translation id="2609371827041010694">Toujours exécuter pour ce site</translation>
-+<translation id="5170568018924773124">Afficher le dossier</translation>
-+<translation id="883848425547221593">Autres favoris</translation>
-+<translation id="6054173164583630569">Clavier français</translation>
-+<translation id="4870177177395420201"><ph name="PRODUCT_NAME"/> ne parvient pas à déterminer ou à définir le navigateur par défaut.</translation>
-+<translation id="8898786835233784856">Sélectionner l'onglet suivant</translation>
-+<translation id="2674170444375937751">Voulez-vous vraiment supprimer ces pages de votre historique ?</translation>
-+<translation id="9111102763498581341">Déverrouiller</translation>
-+<translation id="289695669188700754">ID de clé : <ph name="KEY_ID"/></translation>
-+<translation id="3067198360141518313">Exécuter ce plug-in</translation>
-+<translation id="8767072502252310690">Utilisateurs</translation>
-+<translation id="683526731807555621">Ajouter un moteur</translation>
-+<translation id="6871644448911473373">Répondeur OCSP : <ph name="LOCATION"/></translation>
-+<translation id="8281886186245836920">Ignorer</translation>
-+<translation id="3867944738977021751">Champs de certificat</translation>
-+<translation id="2114224913786726438">Modules (<ph name="TOTAL_COUNT"/>) : aucun conflit détecté.</translation>
-+<translation id="7629827748548208700">Onglet : <ph name="TAB_NAME"/></translation>
-+<translation id="388442998277590542">Impossible de charger la page d'options &quot;<ph name="OPTIONS_PAGE"/>&quot;.</translation>
-+<translation id="8449008133205184768">Coller en adaptant le style</translation>
-+<translation id="9114223350847410618">Veuillez ajouter une autre langue avant de supprimer celle-ci.</translation>
-+<translation id="4408427661507229495">nom du réseau</translation>
-+<translation id="8886960478266132308"><ph name="PRODUCT_NAME"/> synchronise de manière sécurisée vos données avec votre compte Google.</translation>
-+<translation id="8028993641010258682">Taille</translation>
-+<translation id="5031603669928715570">Activer...</translation>
-+<translation id="1383876407941801731">Recherche</translation>
-+<translation id="8398877366907290961">Poursuivre quand même</translation>
-+<translation id="5063180925553000800">Nouveau code PIN :</translation>
-+<translation id="2496540304887968742">La capacité du périphérique doit être d'au moins 4 Go.</translation>
-+<translation id="6974053822202609517">De droite à gauche</translation>
-+<translation id="2370882663124746154">Activer le mode Pinyin double</translation>
-+<translation id="5463856536939868464">Menu contenant des favoris masqués</translation>
-+<translation id="8286227656784970313">Utiliser le dictionnaire système</translation>
-+<translation id="5431084084184068621">Vous avez choisi de chiffrer les données à l'aide de votre mot de passe Google. Vous pouvez modifier vos paramètres de synchronisation à tout moment, si vous changez d'avis.</translation>
-+<translation id="1493263392339817010">Personnaliser les polices...</translation>
-+<translation id="5352033265844765294">Enregistrement des informations de date</translation>
-+<translation id="6449085810994685586">&amp;Vérifier l'orthographe du texte de ce champ</translation>
-+<translation id="3621320549246006887">Ceci est un modèle expérimental qui permet aux enregistrements DNS (utilisant le protocole de sécurité DNSSEC) d'autoriser ou de refuser des certificats HTTPS. Ce message s'affiche lorsque vous activez des fonctionnalités expérimentales via des options de ligne de commande. Vous pouvez supprimer ces options de ligne de commande pour ignorer cette erreur.</translation>
-+<translation id="50960180632766478"><ph name="NUMBER_FEW"/> minutes restantes</translation>
-+<translation id="3174168572213147020">Île</translation>
-+<translation id="748138892655239008">Contraintes de base du certificat</translation>
-+<translation id="457386861538956877">Autres...</translation>
-+<translation id="8063491445163840780">Activer l'onglet 4</translation>
-+<translation id="5966654788342289517">Données personnelles</translation>
-+<translation id="9137013805542155359">Afficher l'original</translation>
-+<translation id="4792385443586519711">Nom de la société</translation>
-+<translation id="6423731501149634044">Définir Adobe Reader comme visionneuse de documents PDF par défaut ?</translation>
-+<translation id="8839907368860424444">Pour gérer les extensions installées, cliquez sur Extensions dans le menu Fenêtre.</translation>
-+<translation id="2461687051570989462">Accédez à vos imprimantes depuis n'importe quel ordinateur ou smartphone. <ph name="BEGIN_LINK"/>En savoir plus<ph name="END_LINK"/></translation>
-+<translation id="7194430665029924274">Me &amp;le rappeler plus tard</translation>
-+<translation id="5790085346892983794">Opération réussie !</translation>
-+<translation id="1901769927849168791">Carte SD détectée.</translation>
-+<translation id="818454486170715660"><ph name="NAME"/> - Propriétaire</translation>
-+<translation id="1358032944105037487">Clavier japonais</translation>
-+<translation id="8201956630388867069">WPA</translation>
-+<translation id="603890000178803545">janv.^févr.^mars^avr.^mai^juin^juil.^août^sept.^oct.^nov.^déc.</translation>
-+<translation id="8302838426652833913">Sélectionnez
-+ <ph name="BEGIN_BOLD"/>
-+ Applications &gt; Préférences système &gt; Réseau &gt; Assistant
-+ <ph name="END_BOLD"/>
-+ pour tester votre connexion.</translation>
-+<translation id="8664389313780386848">&amp;Afficher le code source de la page</translation>
-+<translation id="8970407809569722516">Micrologiciel :</translation>
-+<translation id="1180549724812639004">Créer un profil</translation>
-+<translation id="57646104491463491">Date de modification</translation>
-+<translation id="5992752872167177798">Sandbox seccomp</translation>
-+<translation id="6362853299801475928">Signale&amp;r un problème...</translation>
-+<translation id="3289566588497100676">Entrée de symboles simplifiée</translation>
-+<translation id="6507969014813375884">Chinois simplifié</translation>
-+<translation id="7314244761674113881">Hôte SOCKS</translation>
-+<translation id="5285794783728826432">Considérer ce certificat comme fiable pour identifier les sites Web.</translation>
-+<translation id="4224803122026931301">Exceptions de localisation</translation>
-+<translation id="749452993132003881">Hiragana</translation>
-+<translation id="8226742006292257240">Le mot de passe TPM ci-dessous, généré de façon aléatoire, a été attribué à votre ordinateur :</translation>
-+<translation id="8487693399751278191">Importer mes favoris maintenant...</translation>
-+<translation id="7985242821674907985"><ph name="PRODUCT_NAME"/></translation>
-+<translation id="7484580869648358686">Avertissement : Un problème a été détecté sur cette page.</translation>
-+<translation id="2074739700630368799">Avec Google Chrome OS for business, vous pouvez connecter votre périphérique à Google Apps, ce qui vous permet de le rechercher et de le contrôler depuis le panneau de configuration de Google Apps.</translation>
-+<translation id="4474155171896946103">Ajouter tous les onglets aux favoris...</translation>
-+<translation id="5895187275912066135">Émis le</translation>
-+<translation id="1190844492833803334">Lorsque je ferme le navigateur</translation>
-+<translation id="5646376287012673985">Localisation</translation>
-+<translation id="1110155001042129815">Attendre</translation>
-+<translation id="2607101320794533334">Infos sur la clé publique de l'objet</translation>
-+<translation id="7071586181848220801">Plug-in inconnu</translation>
-+<translation id="3354601307791487577">Connexion en mode invité</translation>
-+<translation id="4419409365248380979">Toujours autoriser <ph name="HOST"/> à paramétrer les cookies</translation>
-+<translation id="2956070106555335453">Résumé</translation>
-+<translation id="917450738466192189">Le certificat du serveur n'est pas valide.</translation>
-+<translation id="2649045351178520408">Chaîne de certificats codés Base 64 ASCII</translation>
-+<translation id="7424526482660971538">Choisir mon propre mot de passe multiterme</translation>
-+<translation id="380271916710942399">Certificat de serveur non répertorié</translation>
-+<translation id="6459488832681039634">Rechercher la sélection</translation>
-+<translation id="2392369802118427583">Activer</translation>
-+<translation id="9040421302519041149">L'accès à ce réseau est protégé.</translation>
-+<translation id="5659593005791499971">E-mail</translation>
-+<translation id="8235325155053717782">Erreur <ph name="ERROR_NUMBER"/> (<ph name="ERROR_NAME"/>) : <ph name="ERROR_TEXT"/></translation>
-+<translation id="6584878029876017575">Signature permanente Microsoft</translation>
-+<translation id="562901740552630300">Sélectionnez
-+ <ph name="BEGIN_BOLD"/>
-+ Démarrer &gt; Panneau de configuration &gt; Réseau et Internet &gt; Centre Réseau et partage &gt; Résolution des problèmes (en bas) &gt; Connexions Internet.
-+ <ph name="END_BOLD"/></translation>
-+<translation id="8816996941061600321">Gestionnaire de &amp;fichiers</translation>
-+<translation id="2773223079752808209">Service client</translation>
-+<translation id="4585473702689066695">Impossible de se connecter au réseau &quot;<ph name="NAME"/>&quot;.</translation>
-+<translation id="4647175434312795566">J'accepte ces termes</translation>
-+<translation id="1084824384139382525">Copier l'adr&amp;esse du lien</translation>
-+<translation id="1221462285898798023">Veuillez démarrer <ph name="PRODUCT_NAME"/> en tant qu'utilisateur normal. Pour l'exécuter en tant que root, vous devez indiquer un autre répertoire de données utilisateur pour stocker les informations du profil.</translation>
-+<translation id="3220586366024592812">Le processus du connecteur <ph name="CLOUD_PRINT_NAME"/> est bloqué. Voulez-vous le redémarrer ?</translation>
-+<translation id="5042992464904238023">Contenu Web</translation>
-+<translation id="6254503684448816922">Clé compromise</translation>
-+<translation id="1181037720776840403">Supprimer</translation>
-+<translation id="4006726980536015530">Si vous fermez <ph name="PRODUCT_NAME"/> maintenant, ces téléchargements seront annulés.</translation>
-+<translation id="4194415033234465088">Dachen 26</translation>
-+<translation id="1664712100580477121">Voulez-vous vraiment graver l'image sur le périphérique suivant :</translation>
-+<translation id="6639554308659482635">Mémoire SQLite</translation>
-+<translation id="8141503649579618569"><ph name="DOWNLOAD_RECEIVED"/>/<ph name="DOWNLOAD_TOTAL"/>, <ph name="TIME_LEFT"/></translation>
-+<translation id="7650701856438921772"><ph name="PRODUCT_NAME"/> est affiché dans cette langue.</translation>
-+<translation id="740624631517654988">Fenêtre pop-up bloquée</translation>
-+<translation id="3738924763801731196"><ph name="OID"/> :</translation>
-+<translation id="6550769511678490130">Ouvrir tous les favoris</translation>
-+<translation id="1847961471583915783">Effacer les cookies et autres données de site et de plug-in lorsque je ferme le navigateur</translation>
-+<translation id="8870318296973696995">Page d'accueil</translation>
-+<translation id="6659594942844771486">Onglet</translation>
-+<translation id="6575134580692778371">Non configuré</translation>
-+<translation id="4624768044135598934">Opération réussie !</translation>
-+<translation id="6014776969142880350">Relancez <ph name="PRODUCT_NAME"/> pour terminer la mise à jour.</translation>
-+<translation id="5582768900447355629">Chiffrer toutes mes données</translation>
-+<translation id="6122365914076864562">Veuillez patienter pendant que nous configurons votre réseau pour mobile.</translation>
-+<translation id="1974043046396539880">Points de distribution de listes de révocation des certificats</translation>
-+<translation id="7049357003967926684">Association</translation>
-+<translation id="8641392906089904981">Appuyez sur Maj+Alt pour changer la disposition du clavier.</translation>
-+<translation id="3024374909719388945">Utiliser l'horloge au format 24 heures</translation>
-+<translation id="1867780286110144690"><ph name="PRODUCT_NAME"/> est prêt à terminer l'installation.</translation>
-+<translation id="5316814419223884568">Lancez votre recherche à partir d'ici</translation>
-+<translation id="8142732521333266922">OK, synchroniser tout</translation>
-+<translation id="965674096648379287">Afin d'être correctement affichée, cette page requiert des données que vous avez précédemment entrées. Vous pouvez de nouveau transmettre ces données, mais, en procédant ainsi, vous devrez répéter chaque action que cette page a effectuée auparavant. Cliquez sur Rafraîchir pour transmettre de nouveau ces données et pour afficher cette page.</translation>
-+<translation id="43742617823094120">Cela signifie que le certificat présenté à votre navigateur a été révoqué par son émetteur. L'intégrité de ce certificat a certainement été compromise, et il ne doit donc pas être approuvé. Ne poursuivez pas.</translation>
-+<translation id="9019654278847959325">Clavier slovaque</translation>
-+<translation id="18139523105317219">Nom de partie EDI</translation>
-+<translation id="6657193944556309583">Vous avez déjà chiffré des données avec un mot de passe multiterme. Saisissez-le ci-dessous.</translation>
-+<translation id="3328801116991980348">Informations sur le site</translation>
-+<translation id="1205605488412590044">Créer un raccourci vers l'application...</translation>
-+<translation id="2065985942032347596">Authentification requise</translation>
-+<translation id="2553340429761841190"><ph name="PRODUCT_NAME"/> n'est pas parvenu à se connecter à <ph name="NETWORK_ID"/>. Sélectionnez un autre réseau ou réessayez.</translation>
-+<translation id="2086712242472027775">Votre compte n'est pas compatible avec <ph name="PRODUCT_NAME"/>. Contactez l'administrateur de votre domaine ou utilisez un compte Google standard pour vous connecter.</translation>
-+<translation id="7222232353993864120">Adresse e-mail</translation>
-+<translation id="2128531968068887769">Client natif</translation>
-+<translation id="7175353351958621980">Chargé depuis :</translation>
-+<translation id="4590074117005971373">Active les balises canvas hautes performances dans un contexte 2D, pour effectuer le rendu via le processeur graphique.</translation>
-+<translation id="7186367841673660872">Cette page en<ph name="ORIGINAL_LANGUAGE"/>a été traduite en<ph name="LANGUAGE_LANGUAGE"/></translation>
-+<translation id="8448695406146523553">Seule une personne en possession de votre mot de passe multiterme peut lire vos données chiffrées. Google ne reçoit ni n'enregistre votre mot de passe multiterme. Si vous oubliez votre mot de passe multiterme, vous devrez</translation>
-+<translation id="6052976518993719690">Autorité de certification SSL</translation>
-+<translation id="1636959874332483835"><ph name="HOST_NAME"/> contient un logiciel malveillant. Votre ordinateur pourrait être infecté par un virus si vous consultez ce site.</translation>
-+<translation id="8050783156231782848">Aucune donnée disponible.</translation>
-+<translation id="1175364870820465910">Im&amp;primer...</translation>
-+<translation id="3866249974567520381">Description</translation>
-+<translation id="2900139581179749587">Voix non reconnue.</translation>
-+<translation id="953692523250483872">Aucun fichier sélectionné</translation>
-+<translation id="2294358108254308676">Souhaitez-vous installer <ph name="PRODUCT_NAME"/> ?</translation>
-+<translation id="6549689063733911810">Activité récente</translation>
-+<translation id="1529968269513889022">de la dernière semaine</translation>
-+<translation id="5542132724887566711">Profil</translation>
-+<translation id="5196117515621749903">Actualiser sans utiliser le cache</translation>
-+<translation id="5552632479093547648">Logiciels malveillants et sites de phishing détectés !</translation>
-+<translation id="4310537301481716192">Onglet fermé !</translation>
-+<translation id="4988273303304146523">il y a <ph name="NUMBER_DEFAULT"/> jours</translation>
-+<translation id="8428213095426709021">Paramètres</translation>
-+<translation id="1588343679702972132">Ce site exige que vous vous identifiiez avec un certificat :</translation>
-+<translation id="7211994749225247711">Supprimer...</translation>
-+<translation id="2819994928625218237">&amp;Aucune suggestion orthographique</translation>
-+<translation id="1065449928621190041">Clavier franco-canadien</translation>
-+<translation id="8327626790128680264">Clavier étendu américain</translation>
-+<translation id="2950186680359523359">Le serveur a mis fin à la connexion sans envoyer de données.</translation>
-+<translation id="9142623379911037913">Autoriser <ph name="SITE"/> à afficher des notifications sur le Bureau ?</translation>
-+<translation id="4196320913210960460">Pour gérer les extensions installées, cliquez sur Extensions dans le menu Outils.</translation>
-+<translation id="3449494395612243720">Erreur de synchronisation, veuillez vous connecter à nouveau.</translation>
-+<translation id="9118804773997839291">La liste suivante fait état des éléments dangereux détectés sur la page. Cliquez sur le lien &quot;Diagnostic&quot; pour obtenir plus d'informations sur un élément particulier.</translation>
-+<translation id="7139724024395191329">Émirat</translation>
-+<translation id="1761265592227862828">Synchroniser tous les paramètres et toutes les données\n(peut prendre un certain temps)</translation>
-+<translation id="7754704193130578113">Toujours demander où enregistrer les fichiers</translation>
-+<translation id="204914487372604757">Créer un raccourci</translation>
-+<translation id="2497284189126895209">Tous les fichiers</translation>
-+<translation id="696036063053180184">Sebeol-sik No-shift</translation>
-+<translation id="452785312504541111">Anglais (pleine chasse)</translation>
-+<translation id="945332329539165145">2D avec canvas et accélération matérielle</translation>
-+<translation id="5220797120063118010">Cette fonctionnalité autorise l'installation d'applications Google Chrome déployées à partir d'un manifeste situé sur une page Web, plutôt qu'avec un fichier crx contenant le manifeste et les icônes.</translation>
-+<translation id="9148126808321036104">Nouvelle connexion</translation>
-+<translation id="2282146716419988068">GPU</translation>
-+<translation id="428771275901304970">Moins de 1 Mo disponible</translation>
-+<translation id="1682548588986054654">Nouvelle fenêtre de navigation privée</translation>
-+<translation id="6833901631330113163">Europe du Sud</translation>
-+<translation id="8691262314411702087">Sélectionner les éléments à synchroniser</translation>
-+<translation id="6065289257230303064">Attributs du répertoire de l'objet du certificat</translation>
-+<translation id="2423017480076849397">Accédez à vos imprimantes et partagez-les en ligne via <ph name="CLOUD_PRINT_NAME"/>.</translation>
-+<translation id="569520194956422927">&amp;Ajouter...</translation>
-+<translation id="4018133169783460046">Afficher <ph name="PRODUCT_NAME"/> dans cette langue</translation>
-+<translation id="5110450810124758964">il y a <ph name="NUMBER_ONE"/> jour</translation>
-+<translation id="3264544094376351444">Police Sans-Serif</translation>
-+<translation id="5586942249556966598">Ne rien faire</translation>
-+<translation id="2820806154655529776"><ph name="NUMBER_ONE"/> seconde</translation>
-+<translation id="1077946062898560804">Configurer les mises à jour automatiques pour tous les utilisateurs</translation>
-+<translation id="3122496702278727796">Échec de la création du répertoire des données</translation>
-+<translation id="4517036173149081027">Fermer et annuler le chargement</translation>
-+<translation id="7150146631451105528"><ph name="DATE"/></translation>
-+<translation id="3166547286524371413">Adresse :</translation>
-+<translation id="4522570452068850558">Détails</translation>
-+<translation id="59659456909144943">Notification : <ph name="NOTIFICATION_NAME"/></translation>
-+<translation id="6731320427842222405">Cette opération peut prendre quelques minutes.</translation>
-+<translation id="4806525999832945986">Géré par <ph name="DOMAIN"/> (<ph name="STATUS"/>)</translation>
-+<translation id="7503191893372251637">Type de certificat Netscape</translation>
-+<translation id="1502960562739459116">Impossible d'afficher certaines parties de ce document PDF. Souhaitez-vous installer Adobe Reader ?</translation>
-+<translation id="4135450933899346655">Vos certificats</translation>
-+<translation id="4731578803613910821">Vos données personnelles sur <ph name="WEBSITE_1"/>, <ph name="WEBSITE_2"/> et <ph name="WEBSITE_3"/></translation>
-+<translation id="7716781361494605745">URL de stratégie de l'autorité de certification Netscape</translation>
-+<translation id="2881966438216424900">Dernier accès :</translation>
-+<translation id="7552203043556919163">Synchroniser les mots de passe</translation>
-+<translation id="630065524203833229">&amp;Quitter</translation>
-+<translation id="4647090755847581616">&amp;Fermer l'onglet</translation>
-+<translation id="2649204054376361687"><ph name="CITY"/>, <ph name="COUNTRY"/></translation>
-+<translation id="7886758531743562066">Le site Web à l'adresse <ph name="HOST_NAME"/> contient des éléments provenant de sites qui semblent héberger des logiciels malveillants. Ces derniers peuvent nuire à votre ordinateur ou agir à votre insu. Le simple fait de visiter un site hébergeant ce type de logiciels peut infecter votre ordinateur.</translation>
-+<translation id="2064746092913005102">Total : <ph name="NUMBER_OF_PAGES"/> <ph name="PAGE_OR_PAGES_LABEL"/> <ph name="TWO_SIDED"/> <ph name="TIMES"/> <ph name="NUMBER_OF_COPIES"/> <ph name="COPIES_LABEL"/> <ph name="EQUAL_SIGN"/> <ph name="NUMBER_OF_SHEETS"/> <ph name="SHEETS_LABEL"/></translation>
-+<translation id="7538227655922918841">Les cookies de plusieurs sites ont été autorisés pour la session uniquement.</translation>
-+<translation id="2385700042425247848">Nom du service :</translation>
-+<translation id="7751005832163144684">Imprimer une page de test</translation>
-+<translation id="3638865692466101147">Aperçu avant impression - <ph name="PREVIEW_TAB_TITLE"/></translation>
-+<translation id="1471300011765310414"><ph name="PRODUCT_NAME"/>
-+ ne peut pas à afficher la page Web, car votre ordinateur n'est pas connecté à Internet.</translation>
-+<translation id="5464632865477611176">Exécuter cette fois</translation>
-+<translation id="4268025649754414643">Chiffrement de la clé</translation>
-+<translation id="7925247922861151263">Échec de la vérification AAA</translation>
-+<translation id="1168020859489941584">Ouverture dans <ph name="TIME_REMAINING"/>...</translation>
-+<translation id="7814458197256864873">&amp;Copier</translation>
-+<translation id="8186706823560132848">Logiciel</translation>
-+<translation id="4692623383562244444">Moteurs de recherche</translation>
-+<translation id="567760371929988174">&amp;Méthodes d'entrée</translation>
-+<translation id="10614374240317010">Jamais enregistrés</translation>
-+<translation id="5116300307302421503">Impossible d'analyser le fichier.</translation>
-+<translation id="2745080116229976798">Subordination qualifiée Microsoft</translation>
-+<translation id="2526590354069164005">Bureau</translation>
-+<translation id="7983301409776629893">Toujours traduire en <ph name="TARGET_LANGUAGE"/> les pages en <ph name="ORIGINAL_LANGUAGE"/></translation>
-+<translation id="4890284164788142455">Thaï</translation>
-+<translation id="4312207540304900419">Activer l'onglet suivant</translation>
-+<translation id="8456362689280298700"><ph name="HOUR"/>:<ph name="MINUTE"/> de chargement</translation>
-+<translation id="7648048654005891115">Style de mappage du clavier</translation>
-+<translation id="539295039523818097">Un problème lié à votre microphone s'est produit.</translation>
-+<translation id="4033319557821527966"><ph name="CLOUD_PRINT_NAME"/> vous permet d'accéder aux imprimantes de cet ordinateur, où que vous soyez. Connectez-vous pour l'activer.</translation>
-+<translation id="6970216967273061347">District</translation>
-+<translation id="4479639480957787382">Ethernet</translation>
-+<translation id="6312403991423642364">Erreur de réseau inconnue.</translation>
-+<translation id="751377616343077236">Nom du certificat</translation>
-+<translation id="7154108546743862496">Plus d'informations</translation>
-+<translation id="8637688295594795546">Mise à jour du système disponible. Préparation du téléchargement…</translation>
-+<translation id="5167270755190684957">Galerie des thèmes Google Chrome</translation>
-+<translation id="8382913212082956454">Copi&amp;er l'adresse e-mail</translation>
-+<translation id="7447930227192971403">Activer l'onglet 3</translation>
-+<translation id="2903493209154104877">Adresses</translation>
-+<translation id="2056143100006548702">Plug-in : <ph name="PLUGIN_NAME"/> (<ph name="PLUGIN_VERSION"/>)</translation>
-+<translation id="3479552764303398839">Pas maintenant</translation>
-+<translation id="6445051938772793705">Pays</translation>
-+<translation id="3251759466064201842">&lt;Ne fait pas partie du certificat&gt;</translation>
-+<translation id="4229495110203539533">il y a <ph name="NUMBER_ONE"/> seconde</translation>
-+<translation id="6410257289063177456">Fichiers image</translation>
-+<translation id="6419902127459849040">Europe centrale</translation>
-+<translation id="6707389671160270963">Certificat client SSL</translation>
-+<translation id="6083557600037991373">Pour accélérer l'affichage des pages Web,
-+ <ph name="PRODUCT_NAME"/>
-+ enregistre temporairement les fichiers téléchargés sur le disque. Si
-+ <ph name="PRODUCT_NAME"/>
-+ ne s'arrête pas correctement, ces fichiers peuvent être endommagés, ce qui
-+ génère cette erreur. L'actualisation de la page devrait permettre de résoudre
-+ ce problème ; celui-ci ne se reproduira vraisemblablement plus si l'arrêt s'effectue
-+ correctement.
-+ <ph name="LINE_BREAK"/>
-+ Si le problème persiste, essayez de supprimer le contenu du cache. Cette
-+ erreur peut aussi indiquer que le matériel est sur le point de tomber
-+ en panne.</translation>
-+<translation id="5298219193514155779">Thème créé par</translation>
-+<translation id="7366909168761621528">Données de navigation</translation>
-+<translation id="1047726139967079566">Ajouter cette page aux favoris</translation>
-+<translation id="9020142588544155172">Le serveur a refusé la connexion.</translation>
-+<translation id="6113225828180044308">Module (<ph name="MODULUS_NUM_BITS"/> bits) :\n<ph name="MODULUS_HEX_DUMP"/>\n\nExposant public (<ph name="PUBLIC_EXPONENT_NUM_BITS"/> bits) :\n<ph name="EXPONENT_HEX_DUMP"/></translation>
-+<translation id="2544782972264605588"><ph name="NUMBER_DEFAULT"/> secondes restantes</translation>
-+<translation id="8871696467337989339">Vous utilisez un indicateur de ligne de commande non pris en charge : <ph name="BAD_FLAG"/>. La stabilité et la sécurité en seront affectées.</translation>
-+<translation id="4767443964295394154">Emplacement de téléchargement</translation>
-+<translation id="5031870354684148875">À propos de Google Traduction</translation>
-+<translation id="720658115504386855">Les lettres ne sont pas sensibles à la casse.</translation>
-+<translation id="2454247629720664989">Mot clé</translation>
-+<translation id="3950820424414687140">Connexion</translation>
-+<translation id="4626106357471783850">Redémarrez <ph name="PRODUCT_NAME"/> pour appliquer la mise à jour.</translation>
-+<translation id="1697068104427956555">Sélectionner un carré dans l'image</translation>
-+<translation id="2840798130349147766">Bases de données Web</translation>
-+<translation id="1628736721748648976">Codage</translation>
-+<translation id="1198271701881992799">Mise en route</translation>
-+<translation id="782590969421016895">Utiliser les pages actuelles</translation>
-+<translation id="6521850982405273806">Signaler une erreur</translation>
-+<translation id="736515969993332243">Recherche de réseaux en cours</translation>
-+<translation id="8026334261755873520">Effacer les données de navigation</translation>
-+<translation id="1769104665586091481">Ouvrir le lien dans une nouvelle &amp;fenêtre</translation>
-+<translation id="8503813439785031346">Nom d'utilisateur</translation>
-+<translation id="5319782540886810524">Clavier letton</translation>
-+<translation id="8651585100578802546">Forcer l'actualisation de cette page</translation>
-+<translation id="685714579710025096">Disposition du clavier :</translation>
-+<translation id="1361655923249334273">Non utilisé</translation>
-+<translation id="290555789621781773"><ph name="NUMBER_TWO"/> minutes</translation>
-+<translation id="5434065355175441495">Chiffrement RSA PKCS #1</translation>
-+<translation id="7073704676847768330">Ce n'est probablement pas le site que vous recherchez !</translation>
-+<translation id="8477384620836102176">&amp;Général</translation>
-+<translation id="1074663319790387896">Configurer la synchronisation</translation>
-+<translation id="4302315780171881488">État de connexion :</translation>
-+<translation id="3391392691301057522">Ancien code PIN :</translation>
-+<translation id="1344519653668879001">Désactiver le contrôle des liens hypertexte</translation>
-+<translation id="6463795194797719782">&amp;Modifier</translation>
-+<translation id="4262113024799883061">Chinois</translation>
-+<translation id="4775879719735953715">Navigateur par défaut</translation>
-+<translation id="5575473780076478375">Extension en mode navigation privée :<ph name="EXTENSION_NAME"/></translation>
-+<translation id="4188026131102273494">Mot clé :</translation>
-+<translation id="2930644991850369934">Un problème est survenu lors du téléchargement de l'image de récupération. La connexion réseau a été perdue.</translation>
-+<translation id="3461610253915486539">Votre administrateur a désactivé certaines préférences.</translation>
-+<translation id="5750053751252005701">Forfait de données <ph name="NETWORK"/> épuisé</translation>
-+<translation id="8858939932848080433">Veuillez indiquer à quel niveau vous rencontrez des problèmes avant d'envoyer vos commentaires.</translation>
-+<translation id="1720318856472900922">Authentification du serveur WWW TLS</translation>
-+<translation id="8550022383519221471">Le service de synchronisation n'est pas disponible pour votre domaine.</translation>
-+<translation id="3355823806454867987">Modifier les paramètres du proxy...</translation>
-+<translation id="4780374166989101364">Cette fonctionnalité active les API des extensions expérimentales. Notez que vous ne pouvez pas mettre en ligne des extensions qui font appel aux API expérimentales dans la galerie d'extensions.</translation>
-+<translation id="7227780179130368205">Un logiciel malveillant a été détecté !</translation>
-+<translation id="435243347905038008">Forfait de données <ph name="NETWORK"/> presque épuisé</translation>
-+<translation id="2489428929217601177">des dernières 24 heures</translation>
-+<translation id="7418490403869327287">Une fois activée, la recherche instantanée charge la plupart des pages Web dès que vous saisissez l'URL dans le champ polyvalent, avant même que vous n'appuyiez sur Entrée. Si votre moteur de recherche par défaut est compatible, toute lettre saisie dans ce champ offre de nouveaux résultats et les prédictions intégrées vous guident dans vos recherches.\n\nChaque touche utilisée fait l'objet d'une requête, par conséquent il se peut que les éléments saisies dans le champ polyvalent soient enregistrés par votre moteur de recherche par défaut.\n</translation>
-+<translation id="5149131957118398098"><ph name="NUMBER_ZERO"/> hours left</translation>
-+<translation id="2541913031883863396">poursuivre quand même</translation>
-+<translation id="4278390842282768270">Autorisé</translation>
-+<translation id="2074527029802029717">Retirer l'onglet</translation>
-+<translation id="1533897085022183721">Moins de <ph name="MINUTES"/></translation>
-+<translation id="7503821294401948377">Impossible de charger l'icône &quot;<ph name="ICON"/>&quot; d'action du navigateur.</translation>
-+<translation id="5539694491979265537">Consulter Google Dashboard</translation>
-+<translation id="3942946088478181888">Plus d'informations</translation>
-+<translation id="3722396466546931176">Ajoutez des langues puis faites-les glisser pour les classer dans l'ordre souhaité.</translation>
-+<translation id="7396845648024431313"><ph name="APP_NAME"/> sera lancé au démarrage du système et continuera de s'exécuter en arrière-plan, même toutes les fenêtres de <ph name="PRODUCT_NAME"/> sont fermées.</translation>
-+<translation id="8539727552378197395">Non (HttpOnly)</translation>
-+<translation id="4519351128520996510">Saisir votre mot de passe multiterme pour la synchronisation</translation>
-+<translation id="2391419135980381625">Police standard</translation>
-+<translation id="7893393459573308604"><ph name="ENGINE_NAME"/> (par défaut)</translation>
-+<translation id="5392544185395226057">Cette fonctionnalité active la prise en charge du client natif.</translation>
-+<translation id="5400640815024374115">La puce du module de plate-forme sécurisée (TPM) est désactivée ou inexistante.</translation>
-+<translation id="2151576029659734873">L'index de l'onglet indiqué est incorrect.</translation>
-+<translation id="5150254825601720210">Nom du serveur SSL du certificat Netscape</translation>
-+<translation id="6771503742377376720">Est une autorité de certification</translation>
-+<translation id="8814190375133053267">Wi-Fi</translation>
-+<translation id="2040078585890208937">Connexion à <ph name="NAME"/></translation>
-+<translation id="8410619858754994443">Confirmer le mot de passe :</translation>
-+<translation id="2210840298541351314">Aperçu avant impression</translation>
-+<translation id="3858678421048828670">Clavier italien</translation>
-+<translation id="4938277090904056629">Impossible d'établir une connexion sécurisée à cause de l'antivirus ESET.</translation>
-+<translation id="4521805507184738876">(expiré)</translation>
-+<translation id="111844081046043029">Voulez-vous vraiment quitter cette page ?</translation>
-+<translation id="1951615167417147110">Faire défiler d'une page vers le haut</translation>
-+<translation id="4154664944169082762">Empreintes</translation>
-+<translation id="3202578601642193415">Le plus récent</translation>
-+<translation id="8112886015144590373"><ph name="NUMBER_FEW"/> heures</translation>
-+<translation id="1398853756734560583">Agrandir</translation>
-+<translation id="8988255471271407508">La page Web est introuvable dans le cache. Certaines ressources ne sont restituées fidèlement que si elles sont extraites du cache, notamment les pages générées à partir de données que vous avez envoyées. <ph name="LINE_BREAK"/> Cette erreur peut également être due à un cache endommagé lors d'une fermeture incorrecte. <ph name="LINE_BREAK"/> Si le problème persiste, essayez d'effacer le cache.</translation>
-+<translation id="1195977189444203128">Le plug-in <ph name="PLUGIN_NAME"/> n'est plus à jour.</translation>
-+<translation id="3878562341724547165">Vous avez changé de position. Souhaitez-vous utiliser <ph name="NEW_GOOGLE_URL"/> ?</translation>
-+<translation id="1758018619400202187">EAP-TLS</translation>
-+<translation id="6690744523875189208"><ph name="NUMBER_TWO"/> heures</translation>
-+<translation id="8053390638574070785">Rafraîchir cette page</translation>
-+<translation id="5507756662695126555">Non-répudiation</translation>
-+<translation id="3678156199662914018">Extension : <ph name="EXTENSION_NAME"/></translation>
-+<translation id="9194519262242876737">Active l'API Web audio.</translation>
-+<translation id="3531250013160506608">Zone de saisie de mot de passe</translation>
-+<translation id="8314066201485587418">Effacer les cookies et autres données de site lorsque je quitte le navigateur</translation>
-+<translation id="4094105377635924481">Ajouter l'option de regroupement au menu contextuel des onglets</translation>
-+<translation id="8655295600908251630">Version</translation>
-+<translation id="8250690786522693009">Latin</translation>
-+<translation id="2119721408814495896">Le connecteur <ph name="CLOUD_PRINT_NAME"/> requiert l'installation du pack Microsoft XML Paper Specification Essentials.</translation>
-+<translation id="7624267205732106503">Effacer les cookies et autres données de site lorsque je ferme le navigateur</translation>
-+<translation id="8401363965527883709">Case décochée</translation>
-+<translation id="7771452384635174008">Mise en page</translation>
-+<translation id="6188939051578398125">Saisir un nom ou une adresse</translation>
-+<translation id="8443621894987748190">Choix de l'image du compte</translation>
-+<translation id="10122177803156699">Me montrer</translation>
-+<translation id="5260878308685146029"><ph name="NUMBER_TWO"/> minutes restantes</translation>
-+<translation id="2192505247865591433">De :</translation>
-+<translation id="238391805422906964">Ouvrir un rapport de phishing</translation>
-+<translation id="5921544176073914576">Page de phishing</translation>
-+<translation id="3727187387656390258">Inspecter le pop-up</translation>
-+<translation id="569068482611873351">Importer...</translation>
-+<translation id="6571070086367343653">Modifier la carte de paiement</translation>
-+<translation id="1204242529756846967">Cette langue est utilisée pour corriger l'orthographe.</translation>
-+<translation id="3981760180856053153">Le type d'enregistrement indiqué est incorrect.</translation>
-+<translation id="8464591670878858520">Forfait de données <ph name="NETWORK"/> arrivé à expiration</translation>
-+<translation id="4568660204877256194">Exporter mes favoris...</translation>
-+<translation id="3116361045094675131">Clavier britannique</translation>
-+<translation id="4577070033074325641">Importer des favoris...</translation>
-+<translation id="1641504961675316934"><ph name="CLOUD_PRINT_NAME"/></translation>
-+<translation id="1715941336038158809">Nom d'utilisateur ou mot de passe incorrect</translation>
-+<translation id="1901303067676059328">&amp;Tout sélectionner</translation>
-+<translation id="674375294223700098">Erreur inconnue liée au certificat du serveur.</translation>
-+<translation id="7780428956635859355">Envoyer une capture d'écran enregistrée</translation>
-+<translation id="2850961597638370327">Émis pour : <ph name="NAME"/></translation>
-+<translation id="2168039046890040389">Page précédente</translation>
-+<translation id="1767519210550978135">Hsu</translation>
-+<translation id="2498539833203011245">Réduire</translation>
-+<translation id="2893168226686371498">Navigateur par défaut</translation>
-+<translation id="2435457462613246316">Afficher le mot de passe</translation>
-+<translation id="7988355189918024273">Activer les fonctionnalités d'accessibilité</translation>
-+<translation id="5438653034651341183">Inclure la capture d'écran actuelle :</translation>
-+<translation id="1899708097738826574"><ph name="OPTIONS_TITLE"/> - <ph name="SUBPAGE_TITLE"/></translation>
-+<translation id="1765313842989969521">(cette extension est gérée et ne peut être désinstallée ni désactivée)</translation>
-+<translation id="6983783921975806247">OID enregistré</translation>
-+<translation id="394984172568887996">Importés depuis IE</translation>
-+<translation id="5311260548612583999">Fichier de clé privée (facultatif) :</translation>
-+<translation id="2430043402233747791">Autoriser pour la session uniquement</translation>
-+<translation id="7363290921156020669"><ph name="NUMBER_ZERO"/> mins</translation>
-+<translation id="7568790562536448087">Mise à jour en cours</translation>
-+<translation id="4856408283021169561">Aucun microphone trouvé.</translation>
-+<translation id="8190193592390505034">Connexion à <ph name="PROVIDER_NAME"/></translation>
-+<translation id="6144890426075165477"><ph name="PRODUCT_NAME"/> n'est pas votre navigateur par défaut.</translation>
-+<translation id="823241703361685511">Forfait</translation>
-+<translation id="4068506536726151626">Cette page contient des éléments des sites ci-dessous qui suivent votre position géographique :</translation>
-+<translation id="4721475475128190282">Plusieurs profils</translation>
-+<translation id="4220128509585149162">Plantages</translation>
-+<translation id="8798099450830957504">Par défaut</translation>
-+<translation id="9107059250669762581"><ph name="NUMBER_DEFAULT"/> jours</translation>
-+<translation id="1640283014264083726">PKCS #1 MD4 avec chiffrement RSA</translation>
-+<translation id="872451400847464257">Modifier le moteur de recherche</translation>
-+<translation id="6463061331681402734"><ph name="NUMBER_MANY"/> minutes</translation>
-+<translation id="2466804342846034717">Indiquez le mot de passe approprié ci-dessus, puis saisissez les caractères figurant dans l'image ci-dessous.</translation>
-+<translation id="3881435075661337013">Expiration de <ph name="NETWORK"/> imminente</translation>
-+<translation id="5681833099441553262">Activer l'onglet précédent</translation>
-+<translation id="4792057643643237295">Désactiver l'accès à distance</translation>
-+<translation id="1681614449735360921">Afficher les incompatibilités</translation>
-+<translation id="19094784437781028">Carte de débit Solo</translation>
-+<translation id="2657327428424666237"><ph name="BEGIN_LINK"/>Actualisez<ph name="END_LINK"/> cette page Web ultérieurement.</translation>
-+<translation id="7347751611463936647">Pour utiliser cette extension, saisissez &quot;<ph name="EXTENSION_KEYWORD"/>&quot;, TAB, puis votre commande ou votre recherche.</translation>
-+<translation id="659432221160402784"><ph name="PRODUCT_NAME"/> synchronisera les applications installées, afin que vous puissiez y accéder en vous connectant depuis tout navigateur <ph name="PRODUCT_NAME"/>.</translation>
-+<translation id="892464165639979917">Langues et paramètres du correcteur orthographique...</translation>
-+<translation id="5645845270586517071">Erreur de sécurité</translation>
-+<translation id="2805756323405976993">Applications</translation>
-+<translation id="3651020361689274926">La ressource demandée n'existe plus et aucune adresse de transfert n'est disponible. Il semble que cet état de fait soit permanent.</translation>
-+<translation id="2989786307324390836">Certificat unique binaire codé DER</translation>
-+<translation id="3827774300009121996">&amp;Plein écran</translation>
-+<translation id="3771294271822695279">Fichiers vidéo</translation>
-+<translation id="6704875430222476107"><ph name="PRODUCT_NAME"/> indique que
-+ NetNanny intercepte les connexions sécurisées. En général, cela
-+ ne constitue pas un problème de sécurité, car le logiciel NetNanny s'exécute souvent
-+ sur le même ordinateur. Toutefois, en raison de certaines incompatibilités avec
-+ les connexions sécurisées Google Chrome, vous devez configurer NetNanny
-+ de manière à éviter ces interceptions. Cliquez sur le lien En savoir plus pour obtenir des instructions.</translation>
-+<translation id="3388026114049080752">Vos onglets et activités de navigation</translation>
-+<translation id="7525067979554623046">Créer</translation>
-+<translation id="4711094779914110278">Turc</translation>
-+<translation id="1031460590482534116">Une erreur s'est produite lors de la tentative d'enregistrement du certificat client. Erreur <ph name="ERROR_NUMBER"/> (<ph name="ERROR_NAME"/>)</translation>
-+<translation id="7136984461011502314">Bienvenue dans <ph name="PRODUCT_NAME"/></translation>
-+<translation id="1594030484168838125">Sélectionner</translation>
-+<translation id="204497730941176055">Nom du modèle de certificat Microsoft</translation>
-+<translation id="6705264787989366486">Configuration de l'adresse IP pour <ph name="NAME"/></translation>
-+<translation id="8970721300630048025">Immortalisez votre plus beau sourire et utilisez la photo comme image de compte.</translation>
-+<translation id="4087089424473531098">Extension créée :
-+
-+<ph name="EXTENSION_FILE"/></translation>
-+<translation id="16620462294541761">Mot de passe incorrect. Veuillez réessayer.</translation>
-+<translation id="5017508259293544172">LEAP</translation>
-+<translation id="1394630846966197578">Échec de la connexion aux serveurs de reconnaissance vocale.</translation>
-+<translation id="2498765460639677199">Très grande</translation>
-+<translation id="2378982052244864789">Sélectionner le répertoire de l'extension</translation>
-+<translation id="7861215335140947162">&amp;Téléchargements</translation>
-+<translation id="4778630024246633221">Gestionnaire des certificats</translation>
-+<translation id="6705050455568279082"><ph name="URL"/> souhaite suivre votre position géographique</translation>
-+<translation id="4708849949179781599">Quitter <ph name="PRODUCT_NAME"/></translation>
-+<translation id="2505402373176859469"><ph name="RECEIVED_AMOUNT"/> sur <ph name="TOTAL_SIZE"/></translation>
-+<translation id="6644512095122093795">Proposer d'enregistrer les mots de passe</translation>
-+<translation id="4724450788351008910">Modification de l'affiliation</translation>
-+<translation id="2249605167705922988">par exemple : 1-5, 8, 11-13</translation>
-+<translation id="8691686986795184760">(Activé par une stratégie d'entreprise)</translation>
-+<translation id="1911483096198679472">Qu'est-ce que c'est ?</translation>
-+<translation id="1976323404609382849">Les cookies de plusieurs sites ont été bloqués.</translation>
-+<translation id="2662952950313424742">Serveur DNS spécifié par l'utilisateur et utilisé par Google Chrome, à la place du paramètre système par défaut, pour les résolutions DNS.</translation>
-+<translation id="4176463684765177261">Désactivé</translation>
-+<translation id="2079545284768500474">Annuler</translation>
-+<translation id="114140604515785785">Répertoire racine de l'extension :</translation>
-+<translation id="4788968718241181184">Mode de saisie du vietnamien (TCVN6064)</translation>
-+<translation id="1512064327686280138">Échec de l'activation</translation>
-+<translation id="3254409185687681395">Ajouter cette page aux favoris</translation>
-+<translation id="1384616079544830839">L'identité de ce site Web a été vérifiée par <ph name="ISSUER"/>.</translation>
-+<translation id="8710160868773349942">Adresse e-mail : <ph name="EMAIL_ADDRESSES"/></translation>
-+<translation id="4057991113334098539">Activation...</translation>
-+<translation id="9073281213608662541">PAP</translation>
-+<translation id="1800035677272595847">Sites de phishing</translation>
-+<translation id="8448317557906454022"><ph name="NUMBER_ZERO"/> secs ago</translation>
-+<translation id="402759845255257575">Interdire à tous les sites d'exécuter JavaScript</translation>
-+<translation id="4610637590575890427">Vouliez-vous accéder à <ph name="SITE"/> ?</translation>
-+<translation id="7723779034587221017">La connexion avec le service de configuration a été perdue. Veuillez réinitialiser votre périphérique ou contacter votre représentant de l'assistance technique.</translation>
-+<translation id="3046388203776734202">Paramètres des fenêtres pop-up :</translation>
-+<translation id="3437994698969764647">Tout exporter...</translation>
-+<translation id="8349305172487531364">Barre de favoris</translation>
-+<translation id="1898064240243672867">Stocké dans : <ph name="CERT_LOCATION"/></translation>
-+<translation id="444134486829715816">Développer...</translation>
-+<translation id="1401874662068168819">Gin Yieh</translation>
-+<translation id="7208899522964477531">Rechercher <ph name="SEARCH_TERMS"/> sur <ph name="SITE_NAME"/></translation>
-+<translation id="6255097610484507482">Modifier la carte de paiement</translation>
-+<translation id="5584091888252706332">Au démarrage</translation>
-+<translation id="8960795431111723921">Nous examinons actuellement le problème.</translation>
-+<translation id="2482878487686419369">Notifications</translation>
-+<translation id="8004582292198964060">Navigateur</translation>
-+<translation id="695755122858488207">Case d'option décochée</translation>
-+<translation id="6357135709975569075"><ph name="NUMBER_ZERO"/> days</translation>
-+<translation id="8666678546361132282">Anglais</translation>
-+<translation id="2224551243087462610">Modifier le nom du dossier</translation>
-+<translation id="1358741672408003399">Grammaire et orthographe</translation>
-+<translation id="4910673011243110136">Réseaux privés</translation>
-+<translation id="2527167509808613699">Toutes sortes de connexions</translation>
-+<translation id="9095710730982563314">Exceptions liées aux notifications</translation>
-+<translation id="8072988827236813198">Épingler les onglets</translation>
-+<translation id="1234466194727942574">Barre d'onglets</translation>
-+<translation id="7974087985088771286">Activer l'onglet 6</translation>
-+<translation id="4035758313003622889">Gestionnaire de &amp;tâches</translation>
-+<translation id="6356936121715252359">Paramètres de stockage d'Adobe Flash Player...</translation>
-+<translation id="5885996401168273077">Connexion au réseau</translation>
-+<translation id="7313804056609272439">Mode de saisie du vietnamien (VNI)</translation>
-+<translation id="1768211415369530011">L'application suivante va être lancée si vous acceptez cette requête :\n\n<ph name="APPLICATION"/></translation>
-+<translation id="8793043992023823866">Importation...</translation>
-+<translation id="8106211421800660735">N° de carte</translation>
-+<translation id="2550839177807794974">Gérer les moteurs de recherche...</translation>
-+<translation id="7031711645186424727">Utiliser un moniteur externe</translation>
-+<translation id="6316768948917110108">Gravure de l'image en cours...</translation>
-+<translation id="5089810972385038852">État</translation>
-+<translation id="2872961005593481000">Éteindre</translation>
-+<translation id="8986267729801483565">Enregistrer les fichiers dans le dossier :</translation>
-+<translation id="4322394346347055525">Fermer les autres onglets</translation>
-+<translation id="4411770745820968260">Répertoire de fichiers</translation>
-+<translation id="881799181680267069">Masquer les autres</translation>
-+<translation id="1812631533912615985">Annuler l'épinglage des onglets</translation>
-+<translation id="6042308850641462728">Plus</translation>
-+<translation id="8318945219881683434">Échec de la vérification de la révocation</translation>
-+<translation id="1650709179466243265">Ajouter www. et .com, puis ouvrir la page</translation>
-+<translation id="3524079319150349823">Pour inspecter un pop-up, cliquez avec le bouton droit sur la page ou sur l'icône d'action du navigateur, puis sélectionnez Inspecter le pop-up.</translation>
-+<translation id="994289308992179865">&amp;Répéter</translation>
-+<translation id="7793343764764530903"><ph name="CLOUD_PRINT_NAME"/> est à présent activé. <ph name="PRODUCT_NAME"/> a enregistré les imprimantes installées sur cette machine en les associant à &lt;b&gt;<ph name="EMAIL_ADDRESSES"/>&lt;/b&gt;. Vous pouvez désormais utiliser vos imprimantes depuis n'importe quelle application Web ou mobile associée à <ph name="CLOUD_PRINT_NAME"/>.</translation>
-+<translation id="1703490097606704369">Le serveur de <ph name="HOST_NAME"/>
-+ est introuvable, car la résolution DNS a échoué. DNS est le service Web qui
-+ traduit les noms de site Web en adresses Internet. Cette erreur est
-+ généralement due à l'absence de connexion Internet ou à une configuration
-+ incorrecte du réseau. Cela peut également venir d'un serveur DNS qui ne
-+ répond pas ou d'un pare-feu interdisant l'accès de
-+ <ph name="PRODUCT_NAME"/>
-+ au réseau.</translation>
-+<translation id="8887090188469175989">ZGPY</translation>
-+<translation id="3302709122321372472">Impossible de charger le fichier css &quot;<ph name="RELATIVE_PATH"/>&quot; du script de contenu.</translation>
-+<translation id="305803244554250778">Créer des raccourcis vers des applications aux emplacements suivants :</translation>
-+<translation id="574392208103952083">Moyenne</translation>
-+<translation id="3745810751851099214">Envoyé pour :</translation>
-+<translation id="3937609171782005782">Aider Google à détecter les logiciels malveillants en envoyant des données supplémentaires concernant les sites pour lesquels cet avertissement s'affiche. Ces données seront gérées conformément aux règles définies sur la page <ph name="PRIVACY_PAGE_LINK"/>.</translation>
-+<translation id="8877448029301136595">[répertoire parent]</translation>
-+<translation id="7301360164412453905">Touches de sélection du clavier Hsu</translation>
-+<translation id="8631271110654520730">Copie de l'image de récupération...</translation>
-+<translation id="1963227389609234879">Tout supprimer</translation>
-+<translation id="7779140087128114262">Seule une personne en possession de votre mot de passe multiterme peut lire vos données chiffrées. Google ne reçoit ni n'enregistre votre mot de passe multiterme. Si vous oubliez votre mot de passe multiterme, vous devrez réinitialiser la synchronisation.</translation>
-+<translation id="8027581147000338959">Ouvrir dans une nouvelle fenêtre</translation>
-+<translation id="8019305344918958688">Dommage... Aucune extension n'est installée. :-(</translation>
-+<translation id="7466861475611330213">Style de ponctuation</translation>
-+<translation id="2496180316473517155">Historique de navigation</translation>
-+<translation id="602251597322198729">Ce site tente de télécharger plusieurs fichiers. Voulez-vous autoriser le chargement ?</translation>
-+<translation id="5843685321177053287">Établissement de la liaison avec le service de gestion des périphériques en attente...</translation>
-+<translation id="2052389551707911401"><ph name="NUMBER_MANY"/> heures</translation>
-+<translation id="5411472733320185105">Ne pas utiliser les paramètres du proxy pour les hôtes et domaines suivants :</translation>
-+<translation id="6691936601825168937">&amp;Avancer</translation>
-+<translation id="6566142449942033617">Impossible de charger &quot;<ph name="PLUGIN_PATH"/>&quot; pour le plug-in.</translation>
-+<translation id="7065534935986314333">À propos du système</translation>
-+<translation id="45025857977132537">Utilisation de la clé du certificat : <ph name="USAGES"/></translation>
-+<translation id="6454421252317455908">Mode de saisie du chinois (quick)</translation>
-+<translation id="368789413795732264">Une erreur s'est produite lors de la tentative d'écriture du fichier : <ph name="ERROR_TEXT"/>.</translation>
-+<translation id="1173894706177603556">Renommer</translation>
-+<translation id="5670032673361607750">La synchronisation requiert votre attention.</translation>
-+<translation id="2148716181193084225">Aujourd'hui</translation>
-+<translation id="1002064594444093641">Imp&amp;rimer le cadre...</translation>
-+<translation id="7234674978021619913">Le site <ph name="HOST_NAME"/> a déjà été informé qu'un logiciel malveillant a été détecté sur ses pages. Pour plus d'informations concernant les problèmes rencontrés sur <ph name="HOST_NAME2"/>, consultez notre <ph name="DIAGNOSTIC_PAGE"/> Google.</translation>
-+<translation id="8202390211066742724">Adresse de serveur DNS spécifiée par l'utilisateur.</translation>
-+<translation id="4608500690299898628">&amp;Rechercher...</translation>
-+<translation id="3574305903863751447"><ph name="CITY"/>, <ph name="STATE"/> <ph name="COUNTRY"/></translation>
-+<translation id="8724859055372736596">&amp;Afficher dans le dossier</translation>
-+<translation id="4605399136610325267">Non connecté à Internet.</translation>
-+<translation id="978407797571588532">Sélectionnez
-+ <ph name="BEGIN_BOLD"/>
-+ Démarrer &gt; Panneau de configuration &gt; Connexions réseau &gt; Assistant Nouvelle connexion
-+ <ph name="END_BOLD"/>
-+ pour tester votre connexion.</translation>
-+<translation id="5554489410841842733">Cette icône s'affiche lorsque l'extension peut agir sur la page active.</translation>
-+<translation id="579702532610384533">Reconnexion</translation>
-+<translation id="4862642413395066333">Réponses OCSP de signature</translation>
-+<translation id="5266113311903163739">Erreur d'importation de l'autorité de certification</translation>
-+<translation id="9563164493805065">Gravure de l'image terminée.</translation>
-+<translation id="4756388243121344051">&amp;Historique</translation>
-+<translation id="3789841737615482174">Installer</translation>
-+<translation id="4320697033624943677">Ajouter des utilisateurs</translation>
-+<translation id="9153934054460603056">Enregistrer l'authentification et le mot de passe</translation>
-+<translation id="1455548678241328678">Clavier norvégien</translation>
-+<translation id="2520481907516975884">Basculer en mode chinois/anglais</translation>
-+<translation id="8571890674111243710">Traduction de la page en <ph name="LANGUAGE"/>...</translation>
-+<translation id="4789872672210757069">À propos de &amp;<ph name="PRODUCT_NAME"/></translation>
-+<translation id="4056561919922437609"><ph name="TAB_COUNT"/> onglets</translation>
-+<translation id="4373894838514502496">il y a <ph name="NUMBER_FEW"/> minutes</translation>
-+<translation id="6358450015545214790">Qu'est-ce que c'est ?</translation>
-+<translation id="6264365405983206840">Tout &amp;sélectionner</translation>
-+<translation id="1017280919048282932">&amp;Ajouter au dictionnaire</translation>
-+<translation id="8319414634934645341">Utilisation étendue de la clé</translation>
-+<translation id="4563210852471260509">Le chinois est la langue de saisie initiale</translation>
-+<translation id="6897140037006041989">Agent utilisateur</translation>
-+<translation id="3413122095806433232">Émetteurs de l'autorité de certification : <ph name="LOCATION"/></translation>
-+<translation id="4115153316875436289"><ph name="NUMBER_TWO"/> jours</translation>
-+<translation id="701080569351381435">Code source</translation>
-+<translation id="3286538390144397061">Redémarrer maintenant</translation>
-+<translation id="163309982320328737">La largeur de caractères initiale est Complète</translation>
-+<translation id="5107325588313356747">Pour masquer l'accès à ce programme, vous devez le désinstaller au moyen de \n<ph name="CONTROL_PANEL_APPLET_NAME"/> du Panneau de configuration.\n\nSouhaitez-vous exécuter <ph name="CONTROL_PANEL_APPLET_NAME"/> ?</translation>
-+<translation id="4841055638263130507">Paramètres du microphone</translation>
-+<translation id="6965648386495488594">Port</translation>
-+<translation id="7631887513477658702">&amp;Toujours ouvrir les fichiers de ce type</translation>
-+<translation id="8627795981664801467">Uniquement les connexions sécurisées</translation>
-+<translation id="8680787084697685621">Les informations de connexion au compte sont obsolètes.</translation>
-+<translation id="3228969707346345236">La traduction a échoué, car la page est déjà en <ph name="LANGUAGE"/>.</translation>
-+<translation id="1873879463550486830">Sandbox SUID</translation>
-+<translation id="2190355936436201913">(vide)</translation>
-+<translation id="8515737884867295000">Échec de l'authentification basée sur le certificat</translation>
-+<translation id="5868426874618963178">Envoyer le code source de la page actuelle</translation>
-+<translation id="1269138312169077280">Votre administrateur a désactivé certains paramètres.</translation>
-+<translation id="5818003990515275822">Coréen</translation>
-+<translation id="4182252350869425879">Avertissement : Il s'agit peut-être d'un site de phishing !</translation>
-+<translation id="5458214261780477893">Dvorak</translation>
-+<translation id="5353719617589986386">Étendue de pages incorrecte</translation>
-+<translation id="1164369517022005061"><ph name="NUMBER_DEFAULT"/> heures restantes</translation>
-+<translation id="5943260032016910017">Exceptions liées aux cookies et aux données de site</translation>
-+<translation id="2214283295778284209"><ph name="SITE"/> n'est pas accessible</translation>
-+<translation id="8755376271068075440">P&amp;lus grand</translation>
-+<translation id="8132793192354020517">Connecté à <ph name="NAME"/></translation>
-+<translation id="8187473050234053012">Le certificat de sécurité du site a été révoqué !</translation>
-+<translation id="7444983668544353857">Désactiver <ph name="NETWORKDEVICE"/></translation>
-+<translation id="6003177993629630467"><ph name="PRODUCT_NAME"/> risque de ne pas rester à jour.</translation>
-+<translation id="421577943854572179">intégré sur tout autre site</translation>
-+<translation id="580886651983547002"><ph name="PRODUCT_NAME"/>
-+ ne parvient pas à atteindre le site Web. Cela vient probablement d'un problème de réseau,
-+ mais peut également être dû à un pare-feu ou à un serveur proxy mal configuré.</translation>
-+<translation id="5445557969380904478">À propos de la reconnaissance vocale</translation>
-+<translation id="3093473105505681231">Langues et paramètres du correcteur orthographique...</translation>
-+<translation id="152482086482215392"><ph name="NUMBER_ONE"/> seconde restante</translation>
-+<translation id="529172024324796256">Nom d'utilisateur :</translation>
-+<translation id="3308116878371095290">Le stockage des cookies n'est pas autorisé pour cette page.</translation>
-+<translation id="7521387064766892559">JavaScript</translation>
-+<translation id="7219179957768738017">La connexion utilise <ph name="SSL_VERSION"/>.</translation>
-+<translation id="7014174261166285193">Échec de l'installation</translation>
-+<translation id="1970746430676306437">Afficher les &amp;infos sur la page</translation>
-+<translation id="3199127022143353223">Serveurs</translation>
-+<translation id="2805646850212350655">Système de fichiers de chiffrement Microsoft </translation>
-+<translation id="8053959338015477773">Un plug-in supplémentaire est requis pour afficher certains éléments sur cette page.</translation>
-+<translation id="3541661933757219855">Appuyez sur Ctrl+Alt+/ ou sur Échap pour masquer</translation>
-+<translation id="8813873272012220470">Cette fonctionnalité effectue des vérifications en arrière-plan et vous avertit en cas d'incompatibilité logicielle (modules tiers bloquant le navigateur, par exemple).</translation>
-+<translation id="5020734739305654865">Connexion avec</translation>
-+<translation id="2679385451463308372">Imprimer depuis la boîte de dialogue système…</translation>
-+<translation id="7414887922320653780"><ph name="NUMBER_ONE"/> heure restante</translation>
-+<translation id="121632099317611328">Échec de l'initialisation de l'appareil photo</translation>
-+<translation id="399179161741278232">Importés</translation>
-+<translation id="3829932584934971895">Type de fournisseur :</translation>
-+<translation id="462288279674432182">IP restreinte :</translation>
-+<translation id="3927932062596804919">Refuser</translation>
-+<translation id="3524915994314972210">Démarrage du téléchargement en cours...</translation>
-+<translation id="6484929352454160200">Une nouvelle version de <ph name="PRODUCT_NAME"/> est disponible.</translation>
-+<translation id="3187212781151025377">Clavier hébreu</translation>
-+<translation id="351152300840026870">Police à largeur fixe</translation>
-+<translation id="5827266244928330802">Safari</translation>
-+<translation id="778881183694837592">Les champs obligatoires ne doivent pas rester vides.</translation>
-+<translation id="2371076942591664043">Ouvrir une fois le téléchargement &amp;terminé</translation>
-+<translation id="3920504717067627103">Stratégies de certificat</translation>
-+<translation id="155865706765934889">Pavé tactile</translation>
-+<translation id="7701040980221191251">Aucun</translation>
-+<translation id="5917011688104426363">Activer la barre d'adresse en mode recherche</translation>
-+<translation id="6910239454641394402">Exceptions pour JavaScript</translation>
-+<translation id="2979639724566107830">Ouvrir dans une nouvelle fenêtre</translation>
-+<translation id="3269101346657272573">Veuillez saisir le code PIN.</translation>
-+<translation id="9204065299849069896">Options de saisie automatique...</translation>
-+<translation id="2822854841007275488">Arabe</translation>
-+<translation id="5857090052475505287">Nouveau dossier</translation>
-+<translation id="7450732239874446337">E/S réseau interrompue</translation>
-+<translation id="5178667623289523808">Rechercher le précédent</translation>
-+<translation id="2815448242176260024">Ne jamais enregistrer les mots de passe</translation>
-+<translation id="2989805286512600854">Ouvrir dans un nouvel onglet</translation>
-+<translation id="8687485617085920635">Fenêtre suivante</translation>
-+<translation id="4122118036811378575">&amp;Rechercher le suivant</translation>
-+<translation id="6008256403891681546">JCB</translation>
-+<translation id="2610780100389066815">Signature de liste d'approbation Microsoft</translation>
-+<translation id="8289811203643526145">Gérer les certificats...</translation>
-+<translation id="2788575669734834343">Sélectionnez le fichier de certificat.</translation>
-+<translation id="8404409224170843728">Fabricant :</translation>
-+<translation id="8267453826113867474">Bloquer le contenu inapproprié</translation>
-+<translation id="7959074893852789871">Le fichier contenait plusieurs certificats, dont certains n'ont pas été importés :</translation>
-+<translation id="1213999834285861200">Exceptions pour les images</translation>
-+<translation id="2805707493867224476">Autoriser tous les sites à afficher des fenêtres pop-up</translation>
-+<translation id="3561217442734750519">Vous devez indiquer un chemin valide comme valeur de clé privée.</translation>
-+<translation id="2444609190341826949">Sans mot de passe multiterme, vos mots de passe et autres données chiffrées ne seront pas synchronisés sur cet ordinateur.</translation>
-+<translation id="77221669950527621">Extensions ou applications</translation>
-+<translation id="6650142020817594541">Ce site recommande Google Chrome Frame (déjà installé).</translation>
-+<translation id="6503077044568424649">Les plus visités</translation>
-+<translation id="4625904365165566833">Vous n'êtes pas autorisé à vous connecter. Consultez le propriétaire de cet ordinateur portable.</translation>
-+<translation id="7450633916678972976">Remarque : Lorsque vous cliquez sur &quot;Envoyer&quot;, Google Chrome joint à votre
-+ envoi un journal indiquant votre version de Google Chrome et celle du système
-+ d'exploitation utilisé, ainsi que l'URL associée à votre rapport. Vous pouvez
-+ également joindre une capture d'écran. Ces informations nous
-+ permettent de diagnostiquer les problèmes et d'améliorer les performances de
-+ Google Chrome. Les informations personnelles fournies sciemment dans vos
-+ commentaires ou involontairement dans le journal, l'URL ou la capture
-+ d'écran sont protégées conformément à nos règles de
-+ confidentialité. Si vous ne souhaitez pas indiquer d'URL et/ou de capture
-+ d'écran, décochez les cases &quot;Inclure cette URL&quot; et/ou &quot;Inclure cette capture d'écran&quot;. Vous acceptez que Google utilise vos commentaires pour améliorer ses produits ou services.</translation>
-+<translation id="465365366590259328">Vos modifications seront prises en compte au prochain démarrage de <ph name="PRODUCT_NAME"/>.</translation>
-+<translation id="7168109975831002660">Taille de police minimale</translation>
-+<translation id="7070804685954057874">Entrée directe</translation>
-+<translation id="3265459715026181080">Fermer la fenêtre</translation>
-+<translation id="6074871234879228294">Mode de saisie du japonais (pour clavier japonais)</translation>
-+<translation id="7855296476260297092">Inscription réussie</translation>
-+<translation id="907841381057066561">Échec de création du fichier zip temporaire lors de la création du pack</translation>
-+<translation id="1294298200424241932">Modifier les paramètres de confiance :</translation>
-+<translation id="1384617406392001144">Votre historique de navigation</translation>
-+<translation id="3831099738707437457">&amp;Masquer le panneau de la vérification orthographique</translation>
-+<translation id="1040471547130882189">Plug-in ne répondant pas</translation>
-+<translation id="5473075389972733037">IBM</translation>
-+<translation id="8307664665247532435">Les paramètres seront effacés lors de la prochaine actualisation.</translation>
-+<translation id="790025292736025802"><ph name="URL"/> introuvable</translation>
-+<translation id="895347679606913382">Démarrage...</translation>
-+<translation id="3319048459796106952">Nouvelle fenêtre de nav&amp;igation privée</translation>
-+<translation id="5832669303303483065">Ajouter une adresse postale...</translation>
-+<translation id="3127919023693423797">Authentification en cours...</translation>
-+<translation id="4195643157523330669">Ouvrir dans un nouvel onglet</translation>
-+<translation id="8030169304546394654">Déconnecté</translation>
-+<translation id="4010065515774514159">Action du navigateur</translation>
-+<translation id="4286563808063000730">Le mot de passe multiterme saisi ne peut pas être utilisé, car vous avez déjà chiffré des données avec un mot de passe multiterme. Entrez ci-dessous le mot de passe multiterme actuellement défini pour la synchronisation.</translation>
-+<translation id="1154228249304313899">Ouvrir cette page :</translation>
-+<translation id="9074348188580488499">Voulez-vous vraiment supprimer tous les mots de passe ?</translation>
-+<translation id="6635491740861629599">Sélectionner par domaine</translation>
-+<translation id="3627588569887975815">Ouvrir le lien dans une fenêtre en navi&amp;gation privée</translation>
-+<translation id="5851868085455377790">Émetteur</translation>
-+<translation id="8223496248037436966">Options de saisie automatique</translation>
-+<translation id="1470719357688513792">Les nouveaux paramètres des cookies seront appliqués quand vous aurez actualisé la page.</translation>
-+<translation id="5578327870501192725">Votre connexion à <ph name="DOMAIN"/> est sécurisée par un chiffrement <ph name="BIT_COUNT"/> bits.</translation>
-+<translation id="869884720829132584">Menu Applications</translation>
-+<translation id="7764209408768029281">Outi&amp;ls</translation>
-+<translation id="1139892513581762545">Onglets latéraux</translation>
-+<translation id="7634357567062076565">Reprendre</translation>
-+<translation id="4779083564647765204">Zoom</translation>
-+<translation id="3282430104564575032">Inspecteur de DOM</translation>
-+<translation id="1526560967942511387">Document sans titre</translation>
-+<translation id="1291144580684226670">Police standard</translation>
-+<translation id="3979748722126423326">Activer <ph name="NETWORKDEVICE"/></translation>
-+<translation id="5538307496474303926">Opération en cours...</translation>
-+<translation id="4367133129601245178">C&amp;opier l'URL de l'image</translation>
-+<translation id="7542995811387359312">La saisie automatique des numéros de carte de paiement est désactivée, car la connexion utilisée par ce formulaire n'est pas sécurisée.</translation>
-+<translation id="3494444535872870968">Enregistrer le &amp;cadre sous...</translation>
-+<translation id="987264212798334818">Général</translation>
-+<translation id="7005812687360380971">Défaillance</translation>
-+<translation id="2356070529366658676">Demander</translation>
-+<translation id="5731247495086897348">Coller l'URL et y a&amp;ccéder</translation>
-+<translation id="8467548439852845758">Pour plus de sécurité, <ph name="PRODUCT_NAME"/> va chiffrer vos mots de passe.</translation>
-+<translation id="2524947000814989347">Si vous avez oublié votre mot de passe multiterme, vous devrez arrêter la synchronisation via Google Dashboard.</translation>
-+<translation id="8018154597338652331"><ph name="BURNT_AMOUNT"/> sur <ph name="TOTAL_SIZE"/></translation>
-+<translation id="7635741716790924709">Adresse ligne 1</translation>
-+<translation id="5135533361271311778">Impossible de créer le favori.</translation>
-+<translation id="5271247532544265821">Basculer en mode chinois simplifié/traditionnel</translation>
-+<translation id="2052610617971448509">Votre système Sandbox n'est pas correctement configuré.</translation>
-+<translation id="7384913436093989340">Sélectionnez le <ph name="BEGIN_BOLD"/>menu clé à molette &gt; Préférences &gt; Options avancées &gt; Modifier les paramètres du proxy<ph name="END_BOLD"/> et vérifiez que vos paramètres sont définis sur &quot;sans proxy&quot; ou &quot;direct&quot;.</translation>
-+<translation id="6417515091412812850">Impossible de vérifier si le certificat a été révoqué.</translation>
-+<translation id="7282743297697561153">Stockage des données</translation>
-+<translation id="3363332416643747536"><ph name="DOWNLOAD_RECEIVED"/>/<ph name="DOWNLOAD_TOTAL"/>, Interrompu</translation>
-+<translation id="7347702518873971555">Acheter un forfait</translation>
-+<translation id="5285267187067365830">Installer le plug-in...</translation>
-+<translation id="5334844597069022743">Afficher le code source</translation>
-+<translation id="1166212789817575481">Fermer les onglets sur la droite</translation>
-+<translation id="6472893788822429178">Afficher le bouton &quot;Accueil&quot;</translation>
-+<translation id="4270393598798225102">Version <ph name="NUMBER"/></translation>
-+<translation id="534916491091036097">Parenthèse gche</translation>
-+<translation id="4157869833395312646">Microsoft Server Gated Cryptography</translation>
-+<translation id="8903921497873541725">Zoom avant</translation>
-+<translation id="2195729137168608510">Protection du courrier électronique</translation>
-+<translation id="1425734930786274278">Les cookies suivants ont été bloqués (tous les cookies tiers sont bloqués, sans exception) :</translation>
-+<translation id="6805647936811177813">Connectez-vous à <ph name="TOKEN_NAME"/> pour importer le certificat client de <ph name="HOST_NAME"/></translation>
-+<translation id="3437016096396740659">La batterie est chargée.</translation>
-+<translation id="6916146760805488559">Créer un nouveau profil...</translation>
-+<translation id="1199232041627643649">Maintenez la touche <ph name="KEY_EQUIVALENT"/> enfoncée pour quitter.</translation>
-+<translation id="5428562714029661924">Masquer ce plug-in</translation>
-+<translation id="7907591526440419938">Ouvrir le fichier</translation>
-+<translation id="2568774940984945469">Conteneur de barres d'infos</translation>
-+<translation id="8971063699422889582">Le certificat du serveur a expiré.</translation>
-+<translation id="8281596639154340028">Utiliser <ph name="HANDLER_TITLE"/></translation>
-+<translation id="7134098520442464001">Réduit la taille du texte</translation>
-+<translation id="21133533946938348">Épingler l'onglet</translation>
-+<translation id="1325040735987616223">Mise à jour du système</translation>
-+<translation id="2864069933652346933"><ph name="NUMBER_ZERO"/> days left</translation>
-+<translation id="9090669887503413452">Inclure les informations système</translation>
-+<translation id="3084771660770137092">Google Chrome n'avait pas suffisamment de mémoire ou le processus de la page Web a été arrêté pour une autre raison. Pour continuer, actualisez la page ou ouvrez-en une autre.</translation>
-+<translation id="1114901192629963971">Impossible de valider votre mot de passe sur le réseau actuel. Sélectionnez un autre réseau.</translation>
-+<translation id="5179510805599951267">Cette page n'est pas rédigée en <ph name="ORIGINAL_LANGUAGE"/> ? Signaler l'erreur</translation>
-+<translation id="6430814529589430811">Certificat unique codé Base 64 ASCII</translation>
-+<translation id="5143712164865402236">Activer le mode plein écran</translation>
-+<translation id="8434177709403049435">Codag&amp;e</translation>
-+<translation id="4051923669149193910"><ph name="HANDLER_TITLE"/> est déjà utilisé pour gérer les liens <ph name="PROTOCOL"/>://.</translation>
-+<translation id="2722201176532936492">Touches de sélection</translation>
-+<translation id="385120052649200804">Clavier international américain</translation>
-+<translation id="9012607008263791152">Je comprends que la visite de ce site peut être préjudiciable à mon ordinateur.</translation>
-+<translation id="6640442327198413730">Ressource cache manquante.</translation>
-+<translation id="1441458099223378239">Impossible d'accéder à mon compte</translation>
-+<translation id="5793220536715630615">C&amp;opier l'URL de la vidéo</translation>
-+<translation id="523397668577733901">Vous préférez <ph name="BEGIN_LINK"/>parcourir la galerie<ph name="END_LINK"/> ?</translation>
-+<translation id="2922350208395188000">Impossible de vérifier le certificat du serveur.</translation>
-+<translation id="3778740492972734840">Outils de &amp;développement</translation>
-+<translation id="8335971947739877923">Exporter...</translation>
-+<translation id="5680966941935662618">Paramètres de saisie automatique</translation>
-+<translation id="38385141699319881">Téléchargement de l'image en cours...</translation>
-+<translation id="6004539838376062211">&amp;Options du vérificateur d'orthographe</translation>
-+<translation id="5350198318881239970">Impossible d'ouvrir votre profil correctement.\n\nIl est possible que certaines fonctionnalités ne soient pas disponibles. Vérifiez que ce profil existe et que vous disposez d'une autorisation d'accès à son contenu en lecture et en écriture.</translation>
-+<translation id="4058793769387728514">Vérifier le document maintenant</translation>
-+<translation id="1810107444790159527">Zone de liste</translation>
-+<translation id="3338239663705455570">Clavier slovène</translation>
-+<translation id="1859234291848436338">Sens de l'écriture</translation>
-+<translation id="4567836003335927027">Vos données sur <ph name="WEBSITE_1"/></translation>
-+<translation id="756445078718366910">Ouvrir une fenêtre du navigateur</translation>
-+<translation id="4126154898592630571">Conversion de la date et de l'heure</translation>
-+<translation id="5088534251099454936">PKCS #1 SHA-512 avec chiffrement RSA</translation>
-+<translation id="6392373519963504642">Clavier coréen</translation>
-+<translation id="7887334752153342268">Dupliquer</translation>
-+<translation id="4980691186726139495">Ne pas conserver sur cette page</translation>
-+<translation id="3081523290047420375">Désactiver <ph name="CLOUD_PRINT_NAME"/></translation>
-+<translation id="9207194316435230304">ATOK</translation>
-+<translation id="9026731007018893674">téléchargement</translation>
-+<translation id="7646591409235458998">E-mail :</translation>
-+<translation id="703748601351783580">Ouvrir tous les favoris dans une nouvelle &amp;fenêtre</translation>
-+<translation id="6199775032047436064">Rafraîchir la page actuelle</translation>
-+<translation id="6981982820502123353">Accessibilité</translation>
-+<translation id="112343676265501403">Exceptions pour les plug-ins</translation>
-+<translation id="770273299705142744">Remplissage automatique des formulaires</translation>
-+<translation id="7210998213739223319">Nom d'utilisateur</translation>
-+<translation id="4478664379124702289">Enregistrer le lie&amp;n sous...</translation>
-+<translation id="8725066075913043281">Réessayer</translation>
-+<translation id="8502249598105294518">Personnaliser et configurer <ph name="PRODUCT_NAME"/></translation>
-+<translation id="7392089327262158658">Préférences de saisie automatique <ph name="PRODUCT_NAME_SHORT"/></translation>
-+<translation id="4163521619127344201">Votre position géographique</translation>
-+<translation id="3797008485206955964">Afficher les pages en arrière-plan (<ph name="NUM_BACKGROUND_APPS"/>)</translation>
-+<translation id="8590375307970699841">Configurer les mises à jour automatiques</translation>
-+<translation id="2797524280730715045">il y a <ph name="NUMBER_DEFAULT"/> heures</translation>
-+<translation id="265390580714150011">Valeur du champ</translation>
-+<translation id="9073247318500677671">Les dernières versions d'Unity et GNOME (ainsi que la prochaine version d'Ubuntu, Natty Narwhal) affichent une barre de menus de type OSX sur toute la largeur supérieure de l'écran.</translation>
-+<translation id="3869917919960562512">Index erroné.</translation>
-+<translation id="7031962166228839643">Préparation du module de plate-forme sécurisée (TPM) en cours. Veuillez patienter, l'opération peut prendre quelques minutes.</translation>
-+<translation id="4250377793615429299">Nombre de copies incorrect</translation>
-+<translation id="7180865173735832675">Personnaliser</translation>
-+<translation id="5737306429639033676">Prédire les actions du réseau pour améliorer les performances de chargement des pages</translation>
-+<translation id="8123426182923614874">Données restantes :</translation>
-+<translation id="3707020109030358290">N'est pas une autorité de certification.</translation>
-+<translation id="2115926821277323019">L'URL doit être valide.</translation>
-+<translation id="8986494364107987395">Envoyer automatiquement les statistiques d'utilisation et les rapports d'erreur à Google</translation>
-+<translation id="7070714457904110559">Cette fonctionnalité active la géolocalisation dans les extensions expérimentales. Cela implique l'utilisation des API de localisation du système d'exploitation (si disponibles) et l'envoi de données sur la configuration réseau locale au service de localisation de Google afin de déterminer une position précise.</translation>
-+<translation id="6701535245008341853">Impossible de charger le profil.</translation>
-+<translation id="527605982717517565">Toujours exécuter JavaScript sur <ph name="HOST"/></translation>
-+<translation id="702373420751953740">Version PRL :</translation>
-+<translation id="1307041843857566458">Confirmer la réactivation</translation>
-+<translation id="8314308967132194952">Ajouter une adresse postale...</translation>
-+<translation id="1221024147024329929">PKCS #1 MD2 avec chiffrement RSA</translation>
-+<translation id="853265131227167869">Dim.^Lun.^Mar.^Mer.^Jeu.^Ven.^Sam.</translation>
-+<translation id="3323447499041942178">Zone de saisie</translation>
-+<translation id="580571955903695899">Trier par nom</translation>
-+<translation id="5230516054153933099">Fenêtre</translation>
-+<translation id="7554791636758816595">Nouvel onglet</translation>
-+<translation id="5503844897713343920">Vous tentez d'accéder au site <ph name="DOMAIN"/>, mais le certificat présenté par le serveur a été révoqué par son émetteur. Cela signifie que les informations d'identification présentées par le serveur ne sont pas approuvées. Vous communiquez peut-être avec un pirate informatique. Nous vous déconseillons vivement de continuer.</translation>
-+<translation id="6928853950228839340">Composition hors écran</translation>
-+<translation id="1308727876662951186"><ph name="NUMBER_ZERO"/> mins left</translation>
-+<translation id="7671576867600624">Technologie :</translation>
-+<translation id="1103966635949043187">Accédez à la page d'accueil du site :</translation>
-+<translation id="1951332921786364801">Configurer la communication à distance</translation>
-+<translation id="1086613338090581534">L'émetteur d'un certificat n'ayant pas expiré est tenu d'assurer la maintenance de ce qui s'appelle &quot;une liste de révocation&quot;. Si un certificat est compromis, l'émetteur peut le révoquer en l'ajoutant à la liste de révocation. Ce certificat n'est alors plus approuvé par votre navigateur. Il n'est pas nécessaire d'assurer la maintenance de l'état &quot;révoqué&quot; des certificats expirés. Donc, bien qu'un certificat ait été qualifié de valide pour le site Web que vous visitez actuellement, il est impossible de déterminer s'il a été, depuis, compromis puis révoqué ou s'il est toujours valide. Par conséquent, il n'est pas possible de s'assurer si vous communiquez avec un site Web légitime ou si le certificat a été compromis et se trouve maintenant en la possession d'un pirate informatique avec lequel vous communiquez. Ne poursuivez pas.</translation>
-+<translation id="2645575947416143543">Néanmoins, si vous travaillez dans une entreprise qui génère ses propres certificats, et que vous essayez de vous connecter au site Web interne de l'entreprise avec un certificat de ce type, vous pouvez résoudre ce problème en toute sécurité. Pour ce faire, importez le certificat racine de l'entreprise en tant que &quot;certificat racine&quot;. Par la suite, les certificats émis ou vérifiés par votre entreprise seront approuvés et vous ne verrez plus cette erreur lorsque vous tenterez de vous connecter à nouveau au site Web interne. Contactez le support informatique de votre entreprise pour savoir comment ajouter un nouveau certificat racine sur votre ordinateur.</translation>
-+<translation id="376466258076168640">Définir <ph name="PRODUCT_NAME"/> en tant que navigateur par défaut</translation>
-+<translation id="1056898198331236512">Avertissement</translation>
-+<translation id="8630826211403662855">Préférences de recherche</translation>
-+<translation id="8432745813735585631">Clavier Colemak américain</translation>
-+<translation id="8151639108075998630">Activer la navigation en tant qu'invité</translation>
-+<translation id="2608770217409477136">Utiliser les paramètres par défaut</translation>
-+<translation id="3157931365184549694">Rétablir</translation>
-+<translation id="7426243339717063209">Désinstaller &quot;<ph name="EXTENSION_NAME"/>&quot; ?</translation>
-+<translation id="996250603853062861">Établissement de la connexion sécurisée...</translation>
-+<translation id="6059232451013891645">Dossier :</translation>
-+<translation id="4274292172790327596">Erreur non reconnue</translation>
-+<translation id="760537465793895946">Consultez les conflits connus avec des modules tiers.</translation>
-+<translation id="7042418530779813870">Co&amp;ller et rechercher</translation>
-+<translation id="9110447413660189038">&amp;Remonter</translation>
-+<translation id="375403751935624634">Échec de la traduction en raison d'une erreur de serveur</translation>
-+<translation id="2101225219012730419">Version :</translation>
-+<translation id="1570242578492689919">Polices et codage</translation>
-+<translation id="3082374807674020857"><ph name="PAGE_TITLE"/> - <ph name="PAGE_URL"/></translation>
-+<translation id="8050038245906040378">Signature du code commercial Microsoft</translation>
-+<translation id="3031557471081358569">Sélectionnez les éléments à importer :</translation>
-+<translation id="1368832886055348810">De gauche à droite</translation>
-+<translation id="3031433885594348982">Votre connexion à <ph name="DOMAIN"/> est sécurisée par le biais d'un faible chiffrement.</translation>
-+<translation id="4047345532928475040">sans objet</translation>
-+<translation id="5604324414379907186">Toujours afficher la barre de favoris</translation>
-+<translation id="3220630151624181591">Activer l'onglet 2</translation>
-+<translation id="8898139864468905752">Aperçu des onglets</translation>
-+<translation id="2799223571221894425">Redémarrer</translation>
-+<translation id="5771816112378578655">Configuration en cours...</translation>
-+<translation id="1197979282329025000">Une erreur s'est produite lors de la récupération des fonctions de l'imprimante <ph name="PRINTER_NAME"/>. Cette imprimante n'a pas pu être enregistrée dans <ph name="CLOUD_PRINT_NAME"/>.</translation>
-+<translation id="8820901253980281117">Exceptions pour les fenêtres pop-up</translation>
-+<translation id="1143142264369994168">Signataire du certificat </translation>
-+<translation id="904949795138183864">La page Web <ph name="URL"/> n'existe plus.</translation>
-+<translation id="3228279582454007836">Vous n'avez jamais visité ce site auparavant.</translation>
-+<translation id="2159017110205600596">Personnaliser...</translation>
-+<translation id="5449716055534515760">Fe&amp;rmer la fenêtre</translation>
-+<translation id="2814489978934728345">Arrêter le chargement de cette page</translation>
-+<translation id="2354001756790975382">Autres favoris</translation>
-+<translation id="8561574028787046517"><ph name="PRODUCT_NAME"/> a été mis à jour.</translation>
-+<translation id="5234325087306733083">Mode hors connexion</translation>
-+<translation id="1779392088388639487">Erreur d'importation de fichier PKCS #12</translation>
-+<translation id="166278006618318542">Algorithme de clé publique de l'objet</translation>
-+<translation id="5759272020525228995">Le site Web a rencontré une erreur lors de l'extraction de <ph name="URL"/>.
-+ Cela peut être dû à une opération de maintenance ou à une configuration incorrecte.</translation>
-+<translation id="641480858134062906">Échec du chargement de la page <ph name="URL"/></translation>
-+<translation id="3693415264595406141">Mot de passe :</translation>
-+<translation id="74568296546932365">Conserver <ph name="PAGE_TITLE"/> en tant que moteur de recherche par défaut</translation>
-+<translation id="8602184400052594090">Fichier manifeste absent ou illisible</translation>
-+<translation id="2784949926578158345">La connexion a été réinitialisée.</translation>
-+<translation id="6663792236418322902">Le mot de passe choisi vous sera demandé pour restaurer le fichier. Veillez à le conserver en lieu sûr.</translation>
-+<translation id="4532822216683966758">La vérification de la provenance du certificat DNS est activée, ce qui peut entraîner l'envoi d'informations privées à Google.</translation>
-+<translation id="6321196148033717308">À propos de la reconnaissance vocale</translation>
-+<translation id="3412265149091626468">Aller à la sélection</translation>
-+<translation id="8167737133281862792">Ajouter un certificat</translation>
-+<translation id="2911372483530471524">Espaces de noms PID</translation>
-+<translation id="6093374025603915876">Préférences de saisie automatique</translation>
-+<translation id="8584134039559266300">Activer l'onglet 8</translation>
-+<translation id="5189060859917252173">Le certificat &quot;<ph name="CERTIFICATE_NAME"/>&quot; représente une autorité de certification.</translation>
-+<translation id="3785852283863272759">Envoyer par e-mail l'emplacement de la page</translation>
-+<translation id="2255317897038918278">Enregistrement des informations de date Microsoft</translation>
-+<translation id="3493881266323043047">Validité</translation>
-+<translation id="5979421442488174909">&amp;Traduire en <ph name="LANGUAGE"/></translation>
-+<translation id="7326526699920221209">Batterie : <ph name="PRECENTAGE"/> %</translation>
-+<translation id="952992212772159698">Désactivé</translation>
-+<translation id="8299269255470343364">Japonais</translation>
-+<translation id="5187826826541650604"><ph name="KEY_NAME"/> (<ph name="DEVICE"/>)</translation>
-+<translation id="6429639049555216915">L'application est actuellement inaccessible.</translation>
-+<translation id="2144536955299248197">Lecteur du certificat : <ph name="CERTIFICATE_NAME"/></translation>
-+<translation id="50030952220075532"><ph name="NUMBER_ONE"/> jour restant</translation>
-+<translation id="2885378588091291677">Gestionnaire de tâches</translation>
-+<translation id="5792852254658380406">Gérer les extensions...</translation>
-+<translation id="2359808026110333948">Continuer</translation>
-+<translation id="176759384517330673">Synchronisation avec <ph name="USER_EMAIL_ADDRESS"/> effectuée. Dernière synchronisation : <ph name="LAST_SYNC_TIME"/></translation>
-+<translation id="1618661679583408047">Le certificat de sécurité du site n'est pas encore valide !</translation>
-+<translation id="7039912931802252762">Ouverture de session par carte à puce Microsoft</translation>
-+<translation id="6285074077487067719">Format</translation>
-+<translation id="3065140616557457172">Tapez votre requête ou saisissez une URL pour commencer la navigation : c'est à vous de choisir.</translation>
-+<translation id="5509693895992845810">Enregistrer &amp;sous...</translation>
-+<translation id="5986279928654338866">Le serveur <ph name="DOMAIN"/> requiert un nom d'utilisateur et un mot de passe.</translation>
-+<translation id="521467793286158632">Supprimer tous les mots de passe</translation>
-+<translation id="2491120439723279231">Le certificat du serveur contient des erreurs.</translation>
-+<translation id="4448844063988177157">Recherche de réseaux Wi-Fi...</translation>
-+<translation id="5765780083710877561">Description :</translation>
-+<translation id="338583716107319301">Séparateur</translation>
-+<translation id="2079053412993822885">Si vous supprimez l'un de vos propres certificats, vous ne pouvez plus l'utiliser pour vous identifier.</translation>
-+<translation id="7221869452894271364">Rafraîchir cette page</translation>
-+<translation id="6791443592650989371">État d'activation :</translation>
-+<translation id="4801257000660565496">Créer des raccourcis vers des applications</translation>
-+<translation id="6503256918647795660">Clavier franco-suisse</translation>
-+<translation id="6175314957787328458">GUID de domaine Microsoft</translation>
-+<translation id="8179976553408161302">Entrer</translation>
-+<translation id="8261506727792406068">Supprimer</translation>
-+<translation id="4404805853119650018">Échec de l'enregistrement de cet ordinateur pour l'accès à distance.</translation>
-+<translation id="345693547134384690">Ouvrir l'&amp;image dans un nouvel onglet</translation>
-+<translation id="7422192691352527311">Préférences...</translation>
-+<translation id="354211537509721945">L'administrateur a désactivé les mises à jour.</translation>
-+<translation id="1375198122581997741">À propos de la version</translation>
-+<translation id="7915471803647590281">Veuillez nous indiquer ce qu'il se passe avant d'envoyer votre rapport.</translation>
-+<translation id="5725124651280963564">Connectez-vous à <ph name="TOKEN_NAME"/> afin de générer une clé pour <ph name="HOST_NAME"/>.</translation>
-+<translation id="8418113698656761985">Clavier roumain</translation>
-+<translation id="5976160379964388480">Autres</translation>
-+<translation id="3665842570601375360">Sécurité :</translation>
-+<translation id="1430915738399379752">Imprimer</translation>
-+<translation id="7999087758969799248">Mode de saisie standard</translation>
-+<translation id="2635276683026132559">Signature</translation>
-+<translation id="4835836146030131423">Erreur lors de la connexion</translation>
-+<translation id="7715454002193035316">Pour cette session uniquement</translation>
-+<translation id="2475982808118771221">Une erreur s'est produite.</translation>
-+<translation id="3324684065575061611">(Désactivé par une stratégie d'entreprise)</translation>
-+<translation id="7385854874724088939">Erreur lors de la tentative d'impression. Vérifiez votre imprimante et réessayez.</translation>
-+<translation id="770015031906360009">Grec</translation>
-+<translation id="3834901049798243128">Ignorer les exceptions et bloquer l'enregistrement des cookies tiers</translation>
-+<translation id="8116152017593700047">Cet outil vous permet de sélectionner une capture d'écran enregistrée. Aucune capture d'écran n'est disponible pour le moment. Appuyez simultanément sur Ctrl et sur la touche &quot;Mode Présentation&quot; pour enregistrer une capture d'écran. Vos trois dernières captures apparaissent ici.</translation>
-+<translation id="3454157711543303649">Activation effectuée</translation>
-+<translation id="884923133447025588">Aucun système de révocation trouvé</translation>
-+<translation id="556042886152191864">Bouton</translation>
-+<translation id="1352060938076340443">Interrompu</translation>
-+<translation id="8571226144504132898">Dictionnaire de symboles</translation>
-+<translation id="7229570126336867161">Technologie EvDo requise</translation>
-+<translation id="7582844466922312471">Internet mobile</translation>
-+<translation id="945522503751344254">Envoyer le commentaire</translation>
-+<translation id="4539401194496451708">Associé au profil Chrome <ph name="USER_EMAIL_ADDRESS"/>. Dernière synchronisation : <ph name="LAST_SYNC_TIME"/></translation>
-+<translation id="7369847606959702983">Carte de crédit (autre)</translation>
-+<translation id="6867459744367338172">Langues et saisie</translation>
-+<translation id="7671130400130574146">Utiliser la barre de titre et les bordures de fenêtre du système</translation>
-+<translation id="9170848237812810038">Ann&amp;uler</translation>
-+<translation id="284970761985428403"><ph name="ASCII_NAME"/> (<ph name="UNICODE_NAME"/>)</translation>
-+<translation id="3903912596042358459">Le serveur a refusé d'exécuter la demande.</translation>
-+<translation id="8135557862853121765"><ph name="NUM_KILOBYTES"/> Ko</translation>
-+<translation id="4444364671565852729"><ph name="PRODUCT_NAME"/> a été mis à jour vers la version <ph name="VERSION"/>.</translation>
-+<translation id="5819890516935349394">Navigateur de contenu</translation>
-+<translation id="2731392572903530958">&amp;Rouvrir la fenêtre fermée</translation>
-+<translation id="1254593899333212300">Se connecter directement à Internet</translation>
-+<translation id="6107012941649240045">Émis pour</translation>
-+<translation id="6483805311199035658">Ouverture de <ph name="FILE"/> en cours</translation>
-+<translation id="3576278878016363465">Cibles disponibles pour l'image</translation>
-+<translation id="895541991026785598">Signaler un problème</translation>
-+<translation id="940425055435005472">Taille de police :</translation>
-+<translation id="494286511941020793">Aide pour la configuration de proxy</translation>
-+<translation id="2765217105034171413">Petite</translation>
-+<translation id="1285266685456062655"><ph name="NUMBER_FEW"/> hours ago</translation>
-+<translation id="9154176715500758432">Rester sur cette page</translation>
-+<translation id="5875565123733157100">Type de bug :</translation>
-+<translation id="6988771638657196063">Inclure cette URL :</translation>
-+<translation id="5717920936024713315">Cookies et données de site...</translation>
-+<translation id="3842552989725514455">Police Serif</translation>
-+<translation id="1949795154112250744"><ph name="BEGIN_BOLD"/>Avertissement :<ph name="END_BOLD"/> <ph name="PRODUCT_NAME"/> ne peut pas empêcher les extensions d'enregistrer votre historique de navigation. Pour désactiver cette extension en mode navigation privée, désélectionnez-la.</translation>
-+<translation id="4440967101351338638">ChromiumOs Image Burn</translation>
-+<translation id="1813278315230285598">Services</translation>
-+<translation id="6860097299815761905">Paramètres du proxy...</translation>
-+<translation id="373572798843615002">1 onglet</translation>
-+<translation id="4162393307849942816"><ph name="BEGIN_BOLD"/>Vous naviguez en tant qu'invité<ph name="END_BOLD"/>. Les pages que vous consultez dans cette fenêtre n'apparaîtront pas dans l'historique de votre navigateur ni dans votre historique des recherches. Les autres traces telles que les cookies seront supprimées de l'ordinateur à la fin de votre session. En revanche, les fichiers téléchargés et les favoris créés seront conservés.
-+ <ph name="LINE_BREAK"/>
-+ <ph name="BEGIN_LINK"/>En savoir plus<ph name="END_LINK"/> sur le mode invité</translation>
-+<translation id="827924395145979961">Chargement des pages impossible</translation>
-+<translation id="3092544800441494315">Inclure cette capture d'écran :</translation>
-+<translation id="7714464543167945231">Certificat</translation>
-+<translation id="3616741288025931835">&amp;Effacer les données de navigation...</translation>
-+<translation id="3313622045786997898">Valeur de signature du certificat</translation>
-+<translation id="8535005006684281994">URL de renouvellement du certificat Netscape</translation>
-+<translation id="2440604414813129000">Afficher la s&amp;ource</translation>
-+<translation id="816095449251911490"><ph name="SPEED"/> - <ph name="RECEIVED_AMOUNT"/>, <ph name="TIME_REMAINING"/></translation>
-+<translation id="8200772114523450471">Reprendre</translation>
-+<translation id="6358975074282722691"><ph name="NUMBER_TWO"/> secs ago</translation>
-+<translation id="5423849171846380976">Activé</translation>
-+<translation id="6748105842970712833">Carte SIM désactivée</translation>
-+<translation id="7323391064335160098">Compatibilité avec VPN</translation>
-+<translation id="3929673387302322681">Développement - Instable</translation>
-+<translation id="4251486191409116828">Échec de création du raccourci vers l'application</translation>
-+<translation id="5190835502935405962">Barre de favoris</translation>
-+<translation id="7828272290962178636">Le serveur est en mesure de répondre à la demande.</translation>
-+<translation id="7823073559911777904">Modifier les paramètres du proxy...</translation>
-+<translation id="5438430601586617544">(non empaquetée)</translation>
-+<translation id="6460601847208524483">Rechercher le suivant</translation>
-+<translation id="8433186206711564395">Paramètres réseau</translation>
-+<translation id="4856478137399998590">Votre service Internet mobile est activé et prêt à l'emploi.</translation>
-+<translation id="1676388805288306495">Modifier la police et la langue par défaut des pages Web</translation>
-+<translation id="8969761905474557563">Composition graphique avec accélération matérielle</translation>
-+<translation id="3937640725563832867">Autre nom de l'émetteur du certificat</translation>
-+<translation id="4701488924964507374"><ph name="SENTENCE1"/> <ph name="SENTENCE2"/></translation>
-+<translation id="1163931534039071049">&amp;Afficher le code source du cadre</translation>
-+<translation id="8770196827482281187">Mode de saisie du persan (clavier ISIRI 2901)</translation>
-+<translation id="6423239382391657905">OpenVPN</translation>
-+<translation id="7564847347806291057">Arrêter le processus</translation>
-+<translation id="1607220950420093847">Votre compte a peut-être été supprimé ou désactivé. Veuillez vous déconnecter.</translation>
-+<translation id="5613695965848159202">Authentification anonyme :</translation>
-+<translation id="2233320200890047564">Bases de données indexées</translation>
-+<translation id="7063412606254013905">En savoir plus sur les escroqueries par phishing</translation>
-+<translation id="307767688111441685">Page à l'apparence anormale</translation>
-+<translation id="9076523132036239772">Adresse e-mail ou mot de passe incorrect. Essayez tout d'abord de vous connecter à un réseau.</translation>
-+<translation id="6965978654500191972">Périphérique</translation>
-+<translation id="1242521815104806351">Informations sur la connexion</translation>
-+<translation id="5295309862264981122">Confirmer la navigation</translation>
-+<translation id="1492817554256909552">Nom du point d'accès :</translation>
-+<translation id="5546865291508181392">Rechercher</translation>
-+<translation id="1999115740519098545">Au démarrage</translation>
-+<translation id="2983818520079887040">Paramètres...</translation>
-+<translation id="1465619815762735808">Lire en un clic</translation>
-+<translation id="6941937518557314510">Connectez-vous à <ph name="TOKEN_NAME"/> pour vous authentifier auprès de <ph name="HOST_NAME"/> avec votre certificat.</translation>
-+<translation id="2783600004153937501">Votre administrateur informatique a désactivé certaines options.</translation>
-+<translation id="2099686503067610784">Supprimer le certificat de serveur &quot;<ph name="CERTIFICATE_NAME"/>&quot;?</translation>
-+<translation id="9027603907212475920">Configurer la synchronisation...</translation>
-+<translation id="6873213799448839504">Valider automatiquement une chaîne</translation>
-+<translation id="7238585580608191973">Empreinte SHA-256</translation>
-+<translation id="2501278716633472235">Retour</translation>
-+<translation id="131461803491198646">Réseau domestique, sans itinérance</translation>
-+<translation id="7377249249140280793"><ph name="RELATIVE_DATE"/> - <ph name="FULL_DATE"/></translation>
-+<translation id="5679279978772703611">Gérer les mots de passe enregistrés...</translation>
-+<translation id="4551440281920791563">Sélectionnez
-+ <ph name="BEGIN_BOLD"/>
-+ Menu clé à molette &gt; Options &gt; Options avancées &gt; Modifier les paramètres du proxy &gt; Paramètres réseau
-+ <ph name="END_BOLD"/>
-+ et désélectionnez l'option &quot;Utiliser un serveur proxy pour votre réseau local&quot;.</translation>
-+<translation id="1285320974508926690">Ne jamais traduire ce site</translation>
-+<translation id="8954894007019320973">(suite)</translation>
-+<translation id="3748412725338508953">Trop de redirections</translation>
-+<translation id="5833726373896279253">Ces paramètres ne peuvent être modifiés que par le propriétaire :</translation>
-+<translation id="6858960932090176617">Active la protection XSS Auditor de WebKit (protection contre le Cross-site Scripting), une fonctionnalité qui vous protège de certaines attaques de sites malveillants et offre une sécurité accrue, mais qui n'est pas compatible avec tous les sites Web.</translation>
-+<translation id="6005282720244019462">Clavier latino-américain</translation>
-+<translation id="8831104962952173133">Phishing détecté !</translation>
-+<translation id="5141720258550370428">Voulez-vous utiliser <ph name="HANDLER_TITLE"/> (<ph name="HANDLER_HOSTNAME"/>) pour gérer les liens <ph name="PROTOCOL"/>:// à partir de maintenant ?</translation>
-+<translation id="6751344591405861699"><ph name="WINDOW_TITLE"/> (Navigation privée)</translation>
-+<translation id="6681668084120808868">Prendre une photo</translation>
-+<translation id="780301667611848630">Non merci</translation>
-+<translation id="2812989263793994277">Ne pas afficher les images</translation>
-+<translation id="7190251665563814471">Toujours autoriser ces plug-ins sur <ph name="HOST"/></translation>
-+<translation id="6845383723252244143">Sélectionner un dossier</translation>
-+<translation id="8925458182817574960">&amp;Paramètres</translation>
-+<translation id="6361850914223837199">Informations sur l'erreur :</translation>
-+<translation id="8948393169621400698">Toujours autoriser les plug-ins sur <ph name="HOST"/></translation>
-+<translation id="3865082058368813534">Effacer les données de saisie automatique enregistrées</translation>
-+<translation id="8288345061925649502">Changer de moteur de recherche</translation>
-+<translation id="5436492226391861498">En attente du tunnel proxy...</translation>
-+<translation id="3803991353670408298">Veuillez ajouter un autre mode de saisie avant de supprimer celui-ci.</translation>
-+<translation id="1095623615273566396"><ph name="NUMBER_FEW"/> secondes</translation>
-+<translation id="7006788746334555276">Paramètres de contenu</translation>
-+<translation id="3369521687965833290">Impossible d'extraire les fichiers de l'extension. Pour effectuer cette opération en toute sécurité, vous devez disposer d'un chemin d'accès à votre répertoire de profils commençant par une lettre de lecteur et ne contenant ni jonction, ni point de montage, ni lien symbolique. Aucun chemin de ce type n'existe pour votre profil.</translation>
-+<translation id="337920581046691015"><ph name="PRODUCT_NAME"/> va être installé.</translation>
-+<translation id="6282194474023008486">Code postal</translation>
-+<translation id="7733107687644253241">En bas à droite</translation>
-+<translation id="5139955368427980650">&amp;Ouvrir</translation>
-+<translation id="8136149669168180907"><ph name="DOWNLOADED_AMOUNT"/> téléchargé(s) sur <ph name="TOTAL_SIZE"/></translation>
-+<translation id="7375268158414503514">Commentaires d'ordre général/Autres</translation>
-+<translation id="4643612240819915418">Ou&amp;vrir la vidéo dans un nouvel onglet</translation>
-+<translation id="7997479212858899587">Identité :</translation>
-+<translation id="8300849813060516376">Échec de l'opération OTASP</translation>
-+<translation id="2213819743710253654">Action sur la page</translation>
-+<translation id="1317130519471511503">Modifier des éléments...</translation>
-+<translation id="6391538222494443604">Le répertoire d'extensions est obligatoire.</translation>
-+<translation id="8051695050440594747"><ph name="MEGABYTES"/> Mo disponibles</translation>
-+<translation id="7088615885725309056">Ancien</translation>
-+<translation id="461656879692943278"><ph name="HOST_NAME"/> fournit du contenu provenant de <ph name="ELEMENTS_HOST_NAME"/>, un site connu pour distribuer des logiciels malveillants. Votre ordinateur pourrait être infecté par un virus si vous consultez ce site.</translation>
-+<translation id="1387022316521171484">Ces fonctionnalités expérimentales sont susceptibles d'être modifiées, interrompues ou supprimées à tout moment. Nous ne fournissons aucune garantie quant aux effets de leur activation. Votre navigateur pourrait bien prendre feu. Trêve de plaisanterie, il est possible que votre navigateur supprime toutes vos données ou que votre sécurité et votre vie privée soient compromises de manière inattendue. Nous vous prions d'agir avec précaution.</translation>
-+<translation id="2143778271340628265">Configuration manuelle du proxy</translation>
-+<translation id="5294529402252479912">Mettre à jour Adobe Reader maintenant</translation>
-+<translation id="5263972071113911534"><ph name="NUMBER_MANY"/> days ago</translation>
-+<translation id="7461850476009326849">Désactiver les plug-ins individuels...</translation>
-+<translation id="4097411759948332224">Envoyer une capture d'écran de la page en cours</translation>
-+<translation id="2231990265377706070">Point d'exclamation</translation>
-+<translation id="7199540622786492483"><ph name="PRODUCT_NAME"/> n'est plus à jour, car il n'a pas été relancé depuis quelque temps. La mise à jour disponible sera installée dès que vous le relancerez.</translation>
-+<translation id="8652722422052983852">Petit problème... Tentons de le résoudre.</translation>
-+<translation id="5970231080121144965">Cette fonctionnalité permet d'établir des correspondances entre les sous-chaînes et plusieurs fragments d'URL figurant dans l'historique.</translation>
-+<translation id="4665674675433053715">Page &quot;Nouvel onglet&quot; expérimentale</translation>
-+<translation id="3726527440140411893">Les cookies suivants étaient autorisés lorsque vous avez consulté cette page :</translation>
-+<translation id="3320859581025497771">votre opérateur</translation>
-+<translation id="8828781037212165374">Activer ces fonctionnalités...</translation>
-+<translation id="8562413501751825163">Quitter Firefox avant l'importation</translation>
-+<translation id="3435541101098866721">Ajouter un nouveau téléphone</translation>
-+<translation id="2448046586580826824">Proxy HTTP sécurisé</translation>
-+<translation id="4032534284272647190">Accès à <ph name="URL"/> refusé.</translation>
-+<translation id="4928569512886388887">Finalisation de la mise à jour du système...</translation>
-+<translation id="8258002508340330928">Voulez-vous continuer ?</translation>
-+<translation id="4309420042698375243"><ph name="NUM_KILOBYTES"/> Ko (<ph name="NUM_KILOBYTES_LIVE"/> Ko effectifs)</translation>
-+<translation id="5554573843028719904">Autre réseau Wi-Fi...</translation>
-+<translation id="5034259512732355072">Choisir un autre répertoire...</translation>
-+<translation id="8885905466771744233">L'extension indiquée est déjà associée à une clé privée. Utilisez cette clé ou supprimez-la.</translation>
-+<translation id="7831504847856284956">Ajouter une adresse</translation>
-+<translation id="7505152414826719222">Stockage local</translation>
-+<translation id="2541423446708352368">Afficher tous les téléchargements</translation>
-+<translation id="4381021079159453506">Navigateur de contenu</translation>
-+<translation id="8109246889182548008">Magasin de certificats</translation>
-+<translation id="5030338702439866405">Émis par</translation>
-+<translation id="5280833172404792470">Quitter le mode plein écran (<ph name="ACCELERATOR"/>)</translation>
-+<translation id="2728127805433021124">Le certificat du serveur a été signé avec un algorithme de signature faible.</translation>
-+<translation id="2137808486242513288">Ajouter un utilisateur</translation>
-+<translation id="6193618946302416945">Me proposer de traduire les pages qui sont écrites dans une langue que je ne sais pas lire</translation>
-+<translation id="129553762522093515">Récemment fermés</translation>
-+<translation id="4287167099933143704">Saisir la clé de déverrouillage du code PIN</translation>
-+<translation id="8355915647418390920"><ph name="NUMBER_FEW"/> jours</translation>
-+<translation id="3129140854689651517">Rechercher du texte</translation>
-+<translation id="7221585318879598658">Sans-Serif</translation>
-+<translation id="5558129378926964177">Zoom &amp;avant</translation>
-+<translation id="6451458296329894277">Confirmer le nouvel envoi du formulaire</translation>
-+<translation id="5116333507878097773"><ph name="NUMBER_ONE"/> heure</translation>
-+<translation id="8028152732786498049">Cet élément doit être installé depuis <ph name="CHROME_WEB_STORE"/>.</translation>
-+<translation id="9199258761842902152">Mise en veille ou reprise</translation>
-+<translation id="1851266746056575977">Mettre à jour maintenant</translation>
-+<translation id="7017219178341817193">Ajouter une page</translation>
-+<translation id="1038168778161626396">Chiffrer seulement</translation>
-+<translation id="2756651186786928409">Ne jamais intervertir les touches de modification</translation>
-+<translation id="1217515703261622005">Conversion des numéros spéciaux</translation>
-+<translation id="7179921470347911571">Relancer maintenant</translation>
-+<translation id="3715099868207290855">Synchronisation avec <ph name="USER_EMAIL_ADDRESS"/> effectuée</translation>
-+<translation id="2679312662830811292">il y a <ph name="NUMBER_ONE"/> minute</translation>
-+<translation id="9065203028668620118">Édition</translation>
-+<translation id="4718464510840275738">Préférences synchronisées</translation>
-+<translation id="8788572795284305350"><ph name="NUMBER_ZERO"/> hours ago</translation>
-+<translation id="1177863135347784049">Personnalisé</translation>
-+<translation id="8236028464988198644">Rechercher à partir de la barre d'adresse</translation>
-+<translation id="4881695831933465202">Ouvrir</translation>
-+<translation id="5988520580879236902">Inspecter les vues actives :</translation>
-+<translation id="3593965109698325041">Contraintes de nom du certificat</translation>
-+<translation id="4358697938732213860">Ajouter une adresse</translation>
-+<translation id="8396532978067103567">Mot de passe incorrect.</translation>
-+<translation id="5981759340456370804">Statistiques avancées</translation>
-+<translation id="8160015581537295331">Clavier espagnol</translation>
-+<translation id="3505920073976671674">Sélectionnez votre réseau</translation>
-+<translation id="6644971472240498405"><ph name="NUMBER_ONE"/> jour</translation>
-+<translation id="1782924894173027610">Le serveur de synchronisation est occupé. Veuillez réessayer ultérieurement.</translation>
-+<translation id="6512448926095770873">Quitter cette page</translation>
-+<translation id="5457599981699367932">Naviguer en tant qu'invité</translation>
-+<translation id="3169472444629675720">Discover</translation>
-+<translation id="6294193300318171613">&amp;Toujours afficher la barre de favoris</translation>
-+<translation id="4088820693488683766">Options de recherche</translation>
-+<translation id="3414952576877147120">Taille :</translation>
-+<translation id="9098468523912235228">il y a <ph name="NUMBER_DEFAULT"/> secondes</translation>
-+<translation id="7009102566764819240">La liste suivante fait état des éléments dangereux détectés sur la page. Cliquez sur le lien &quot;Diagnostic&quot; pour obtenir plus d'informations sur une ressource particulière. Si une ressource a été signalée comme site de phishing alors que vous êtes certain de sa fiabilité, cliquez sur le lien &quot;Signaler une erreur&quot;.</translation>
-+<translation id="4923417429809017348">Cette page rédigée dans une langue non identifiée a été traduite en <ph name="LANGUAGE_LANGUAGE"/>.</translation>
-+<translation id="3631337165634322335">Les exceptions ci-dessous s'appliquent uniquement à la session de navigation privée actuelle.</translation>
-+<translation id="676327646545845024">Ne plus afficher la boîte de dialogue pour les liens de ce type</translation>
-+<translation id="494645311413743213"><ph name="NUMBER_TWO"/> secondes restantes</translation>
-+<translation id="1485146213770915382">Insérez <ph name="SEARCH_TERMS_LITERAL"/> dans l'URL où les termes de recherche devraient apparaître.</translation>
-+<translation id="4839303808932127586">En&amp;registrer la vidéo sous...</translation>
-+<translation id="5626134646977739690">Nom :</translation>
-+<translation id="5854409662653665676">Si vous rencontrez des problèmes fréquents avec ce module, vous pouvez tenter d'y remédier en suivant la procédure ci-après :</translation>
-+<translation id="3681007416295224113">Informations relatives au certificat</translation>
-+<translation id="3046084099139788433">Activer l'onglet 7</translation>
-+<translation id="721197778055552897"><ph name="BEGIN_LINK"/>En savoir plus<ph name="END_LINK"/> sur ce problème.</translation>
-+<translation id="1699395855685456105">Version du matériel :</translation>
-+<translation id="212464871579942993">Le site Web à l'adresse <ph name="HOST_NAME"/> contient des éléments provenant de sites qui semblent héberger des logiciels malveillants. Ces derniers peuvent nuire à votre ordinateur ou agir à votre insu. Le simple fait de visiter un site hébergeant ce type de logiciels peut infecter votre ordinateur. Ce site héberge également des informations provenant de sites signalés comme étant des sites de phishing. Ces derniers incitent les internautes à divulguer des informations personnelles en se faisant passer pour des institutions de confiance, telles que des banques.</translation>
-+<translation id="8156020606310233796">Afficher la liste</translation>
-+<translation id="957120528631539888">Désactivez l'affichage des messages de confirmation et le blocage de l'envoi des formulaires.</translation>
-+<translation id="146000042969587795">Ce cadre a été bloqué, car il contient des éléments non sécurisés.</translation>
-+<translation id="8112223930265703044">Tout</translation>
-+<translation id="3968739731834770921">Kana</translation>
-+<translation id="3729920814805072072">Gérer les mots de passe enregistrés...</translation>
-+<translation id="7387829944233909572">Boîte de dialogue &quot;Effacer les données de navigation&quot;</translation>
-+<translation id="8023801379949507775">Mettre à jour les extensions maintenant</translation>
-+<translation id="6549677549082720666">Nouvelle application en arrière-plan installée</translation>
-+<translation id="1983108933174595844">Envoyer une capture d'écran de la page actuelle</translation>
-+<translation id="3298789223962368867">L'URL indiquée est incorrecte.</translation>
-+<translation id="2202898655984161076">Un problème est survenu lors de l'affichage de la liste des imprimantes. Certaines de vos imprimantes ne sont peut-être pas correctement enregistrées dans <ph name="CLOUD_PRINT_NAME"/>.</translation>
-+<translation id="6154697846084421647">Actuellement connecté</translation>
-+<translation id="8241707690549784388">La page que vous recherchez a utilisé des informations que vous avez envoyées. Si vous revenez sur cette page, chaque action précédemment effectuée sera répétée. Souhaitez-vous continuer ?</translation>
-+<translation id="5359419173856026110">Cette fonctionnalité indique la vitesse d'affichage réelle d'une page, en images par seconde, lorsque l'accélération matérielle est active.</translation>
-+<translation id="4104163789986725820">E&amp;xporter...</translation>
-+<translation id="2113479184312716848">&amp;Ouvrir un fichier...</translation>
-+<translation id="8412709057120877195">Configurer le contrôle d'accès pour vos périphériques</translation>
-+<translation id="486595306984036763">Ouvrir un rapport de phishing</translation>
-+<translation id="3140353188828248647">Activer la barre d'adresse</translation>
-+<translation id="4860787810836767172"><ph name="NUMBER_FEW"/> secs ago</translation>
-+<translation id="5565871407246142825">Cartes de paiement</translation>
-+<translation id="2587203970400270934">Code opérateur :</translation>
-+<translation id="3355936511340229503">Erreur de connexion</translation>
-+<translation id="1824910108648426227">Vous avez la possibilité de désactiver ces services.</translation>
-+<translation id="3092040396860056776">Essayer d'afficher la page malgré tout</translation>
-+<translation id="4350711002179453268">Impossible d'établir une connexion sécurisée avec le serveur. Le serveur a peut-être rencontré un problème ou exige un certificat d'authentification du client dont vous ne disposez pas.</translation>
-+<translation id="91731790394942114">Ajouter un nouveau nom</translation>
-+<translation id="5963026469094486319">Obtenir d'autres thèmes</translation>
-+<translation id="2441719842399509963">Rétablir les valeurs par défaut</translation>
-+<translation id="1893137424981664888">Aucun Plug-in installé.</translation>
-+<translation id="3718288130002896473">Action</translation>
-+<translation id="2168725742002792683">Extensions de fichier</translation>
-+<translation id="1753905327828125965">Les plus visités</translation>
-+<translation id="8116972784401310538">&amp;Gestionnaire de favoris</translation>
-+<translation id="1849632043866553433">Caches des applications</translation>
-+<translation id="3591607774768458617">Cette langue est actuellement utilisée par <ph name="PRODUCT_NAME"/>.</translation>
-+<translation id="621638399744152264"><ph name="VALUE"/> %</translation>
-+<translation id="4927301649992043040">Empaqueter l'extension</translation>
-+<translation id="8679658258416378906">Activer l'onglet 5</translation>
-+<translation id="4763816722366148126">Sélectionner le mode de saisie précédent</translation>
-+<translation id="6458308652667395253">Configurer le blocage de JavaScript...</translation>
-+<translation id="8435334418765210033">Réseaux mémorisés</translation>
-+<translation id="6516193643535292276">Impossible de se connecter à Internet.</translation>
-+<translation id="5125751979347152379">URL incorrecte</translation>
-+<translation id="2791364193466153585">Informations sur la sécurité</translation>
-+<translation id="4673916386520338632">Impossible d'installer l'application, car elle est en conflit avec &quot;<ph name="APP_NAME"/>&quot;, qui est déjà installé.</translation>
-+<translation id="2024918351532495204">Votre périphérique n'est pas connecté.</translation>
-+<translation id="6040143037577758943">Fermer</translation>
-+<translation id="5787146423283493983">Accord de la clé</translation>
-+<translation id="1101671447232096497"><ph name="NUMBER_MANY"/> mins ago</translation>
-+<translation id="4265682251887479829">Vous ne trouvez pas ce que vous recherchez ?</translation>
-+<translation id="1804251416207250805">Désactivez l'envoi des pings de contrôle des liens hypertexte.</translation>
-+<translation id="5116628073786783676">En&amp;registrer le fichier audio sous...</translation>
-+<translation id="2557899542277210112">Accédez rapidement à vos favoris en les ajoutant à la barre de favoris.</translation>
-+<translation id="2749881179542288782">Vérifier la grammaire et l'orthographe</translation>
-+<translation id="5105855035535475848">Épingler les onglets</translation>
-+<translation id="6892450194319317066">Sélectionner par type d'application</translation>
-+<translation id="3549436232897695316">assembler</translation>
-+<translation id="5414121716219514204"><ph name="ENGINE_HOST_NAME"/> souhaite devenir votre moteur de recherche.</translation>
-+<translation id="2752805177271551234">Utiliser l'historique d'entrée</translation>
-+<translation id="7268365133021434339">Fermer les onglets</translation>
-+<translation id="4910619056351738551">Voici quelques suggestions :</translation>
-+<translation id="9131598836763251128">Sélectionnez un ou plusieurs fichiers</translation>
-+<translation id="5489059749897101717">Afficher le panneau de la &amp;vérification orthographique</translation>
-+<translation id="3423858849633684918">Veuillez relancer <ph name="PRODUCT_NAME"/>.</translation>
-+<translation id="1232569758102978740">Sans titre</translation>
-+<translation id="1903219944620007795">Pour saisir du texte, sélectionnez une langue et consultez la liste des modes de saisie disponibles.</translation>
-+<translation id="4362187533051781987">Ville</translation>
-+<translation id="9149866541089851383">Modifier...</translation>
-+<translation id="7608619752233383356">Réinitialiser la synchronisation</translation>
-+<translation id="1065245965611933814">Inclure une capture d'écran enregistrée :</translation>
-+<translation id="7876243839304621966">Tout supprimer</translation>
-+<translation id="5663459693447872156">Passer automatiquement en demi-chasse</translation>
-+<translation id="4593021220803146968">&amp;Accéder à <ph name="URL"/></translation>
-+<translation id="1128987120443782698">La capacité de ce périphérique de stockage est de <ph name="DEVICE_CAPACITY"/>. Veuillez insérer une carte SD ou une clé USB d'au moins 4 Go.</translation>
-+<translation id="869257642790614972">Rouvrir le dernier onglet fermé</translation>
-+<translation id="3978267865113951599">(blocage)</translation>
-+<translation id="8412145213513410671">Erreurs (<ph name="CRASH_COUNT"/>)</translation>
-+<translation id="560602183358579978">Traitement de la sélection...</translation>
-+<translation id="7649070708921625228">Aide</translation>
-+<translation id="5994107996638824097">Désolé ! La visionneuse de documents PDF intégrée à Google Chrome, nécessaire à l'affichage de l'aperçu avant impression, n'est pas incluse dans Chromium.</translation>
-+<translation id="976526967778596630">Impossible d'ouvrir <ph name="HOST_NAME"/>, car vous êtes déconnecté du réseau. Cette page s'affichera dès que la connexion réseau sera rétablie. &lt;br&gt;</translation>
-+<translation id="1734072960870006811">Télécopie</translation>
-+<translation id="3095995014811312755">version</translation>
-+<translation id="7052500709156631672">La passerelle ou le serveur proxy a reçu une réponse incorrecte d'un serveur en amont.</translation>
-+<translation id="281133045296806353">Nouvelle fenêtre ouverte dans la session du navigateur</translation>
-+<translation id="7144878232160441200">Réessayer</translation>
-+<translation id="2860002559146138960"><ph name="PRODUCT_NAME"/> peut maintenant synchroniser vos mots de passe. Vos données seront chiffrées avec le mot de passe de votre compte Google ou le mot de passe multiterme de votre choix.</translation>
-+<translation id="3951872452847539732">Les paramètres réseau de votre proxy sont gérés par une extension.</translation>
-+<translation id="6442697326824312960">Retirer l'onglet</translation>
-+<translation id="6382612843547381371">Valable du <ph name="START_DATE_TIME"/> au <ph name="END_DATE_TIME"/></translation>
-+<translation id="6869402422344886127">Case cochée</translation>
-+<translation id="5637380810526272785">Mode de saisie</translation>
-+<translation id="404928562651467259">AVERTISSEMENT</translation>
-+<translation id="7172053773111046550">Clavier estonien</translation>
-+<translation id="497490572025913070">Ajout de bordures aux couches de rendu composées</translation>
-+<translation id="9002707937526687073">Imp&amp;rimer...</translation>
-+<translation id="5953934840931207585">Paramètres de saisie automatique <ph name="PRODUCT_NAME_SHORT"/></translation>
-+<translation id="5556459405103347317">Rafraîchir</translation>
-+<translation id="8000020256436988724">Barre d'outils</translation>
-+<translation id="8326395326942127023">Nom de la base de données :</translation>
-+<translation id="7507930499305566459">Certificat du répondeur d'état</translation>
-+<translation id="2689915906323125315">Utiliser le mot de passe de mon compte Google</translation>
-+<translation id="6440205424473899061">Vos favoris sont maintenant synchronisés avec Google Documents !
-+Pour fusionner et synchroniser vos favoris dans <ph name="PRODUCT_NAME"/> sur un autre ordinateur, procédez de la même manière que précédemment sur l'ordinateur voulu.</translation>
-+<translation id="7727721885715384408">Renommer...</translation>
-+<translation id="2604243255129603442"><ph name="NAME_OF_EXTENSION"/> a été désactivé. Si vous arrêtez la synchronisation des favoris, vous pouvez la réactiver sur la page des extensions, via le menu Outils.</translation>
-+<translation id="2024621544377454980">Affichage des pages impossible</translation>
-+<translation id="7136694880210472378">Utiliser par défaut</translation>
-+<translation id="7681202901521675750">La carte SIM est verrouillée. Veuillez saisir votre code PIN. Nombre de tentatives restantes : <ph name="TRIES_COUNT"/></translation>
-+<translation id="1731346223650886555">Point-virgule</translation>
-+<translation id="158849752021629804">Réseau domestique requis</translation>
-+<translation id="7339763383339757376">PKCS #7, certificat unique</translation>
-+<translation id="7587108133605326224">Langues baltes</translation>
-+<translation id="3991936620356087075">Vous avez saisi un trop grand nombre de clés de verrouillage du code PIN incorrectes. Votre carte SIM est définitivement désactivée.</translation>
-+<translation id="936801553271523408">Données de diagnostic système</translation>
-+<translation id="6389701355360299052">Page Web, contenu HTML uniquement</translation>
-+<translation id="8067791725177197206">Continuer »</translation>
-+<translation id="9009144784540995197">Gérez vos imprimantes.</translation>
-+<translation id="1055006259534905434">(Choisir un problème dans la liste ci-dessous)</translation>
-+<translation id="3021678814754966447">&amp;Afficher le code source du cadre</translation>
-+<translation id="8601206103050338563">Authentification du client WWW TLS</translation>
-+<translation id="1692799361700686467">Les cookies de plusieurs sites sont autorisés.</translation>
-+<translation id="7074488040076962230">Impossible d'afficher la page de la barre latérale &quot;<ph name="SIDEBAR_PAGE"/>&quot;.</translation>
-+<translation id="529232389703829405">Vous avez acheté <ph name="DATA_AMOUNT"/> de données le <ph name="DATE"/>.</translation>
-+<translation id="5271549068863921519">Enregistrer le mot de passe</translation>
-+<translation id="4345587454538109430">Configurer...</translation>
-+<translation id="8148264977957212129">Mode de saisie du pinyin</translation>
-+<translation id="5787378733537687553">Intervertir les touches Ctrl et Alt de gauche</translation>
-+<translation id="7772032839648071052">Confirmer le mot de passe multiterme</translation>
-+<translation id="6857811139397017780">Activer <ph name="NETWORKSERVICE"/></translation>
-+<translation id="3251855518428926750">Ajouter...</translation>
-+<translation id="4120075327926916474">Voulez-vous que Google Chrome enregistre ces informations de carte de paiement pour le remplissage de formulaires Web ?</translation>
-+<translation id="6929555043669117778">Continuer à bloquer les fenêtres pop-up</translation>
-+<translation id="5864471791310927901">Échec de la vérification DHCP</translation>
-+<translation id="3508920295779105875">Choisir un autre dossier...</translation>
-+<translation id="2503458975635466059">Le profil semble être utilisé par le processus <ph name="PROCESS_ID"/> sur l'hôte <ph name="HOST_NAME"/>. Si vous êtes certain qu'aucun autre processus n'utilise ce profil, supprimez le fichier <ph name="LOCK_FILE"/> et relancez <ph name="PRODUCT_NAME"/>.</translation>
-+<translation id="2987775926667433828">Chinois traditionnel</translation>
-+<translation id="6684737638449364721">Effacer les données de navigation...</translation>
-+<translation id="3954582159466790312">Ré&amp;activer le son</translation>
-+<translation id="1110772031432362678">Aucun réseau trouvé.</translation>
-+<translation id="3936390757709632190">&amp;Ouvrir le fichier audio dans un nouvel onglet</translation>
-+<translation id="7297622089831776169">&amp;Méthodes d'entrée</translation>
-+<translation id="5731698828607291678">Onglets ou fenêtres</translation>
-+<translation id="1152775729948968688">Toutefois, cette page inclut d'autres ressources qui ne sont pas sécurisées. Ces ressources peuvent être consultées par des tiers pendant leur transfert, et modifiées par un pirate informatique dans le but de changer le comportement de cette page.</translation>
-+<translation id="604124094241169006">Automatique</translation>
-+<translation id="862542460444371744">&amp;Extensions</translation>
-+<translation id="8045462269890919536">Roumain</translation>
-+<translation id="6320286250305104236">Paramètres du réseau...</translation>
-+<translation id="2927657246008729253">Changer...</translation>
-+<translation id="7978412674231730200">Clé privée</translation>
-+<translation id="464745974361668466">Format :</translation>
-+<translation id="5308380583665731573">Se connecter</translation>
-+<translation id="9111395131601239814"><ph name="NETWORKDEVICE"/> : <ph name="STATUS"/></translation>
-+<translation id="9049981332609050619">Vous avez tenté de contacter <ph name="DOMAIN"/>, mais le certificat présenté par le serveur est incorrect.</translation>
-+<translation id="4414232939543644979">Nouvelle fenêtre de nav&amp;igation privée</translation>
-+<translation id="1693754753824026215">La page à l'adresse <ph name="SITE"/> indique :</translation>
-+<translation id="7148804936871729015">Le serveur associé à <ph name="URL"/> n'a pas répondu à temps. Cela peut être dû à une surcharge.</translation>
-+<translation id="5950967683057767490">L2TP/IPSec + Clé pré-partagée</translation>
-+<translation id="8108473539339615591">XSS Auditor</translation>
-+<translation id="1902576642799138955">Durée de validité</translation>
-+<translation id="4910021444507283344">WebGL</translation>
-+<translation id="6692173217867674490">Mot de passe multiterme erroné</translation>
-+<translation id="5550431144454300634">Corriger automatiquement la saisie</translation>
-+<translation id="3308006649705061278">Unité d'organisation</translation>
-+<translation id="8912362522468806198">Compte Google</translation>
-+<translation id="4443536555189480885">&amp;Aide</translation>
-+<translation id="340485819826776184">Utiliser un service de prédiction afin de compléter les recherches et les URL saisies dans la barre d'adresse</translation>
-+<translation id="4074900173531346617">Certificat du signataire de courrier électronique</translation>
-+<translation id="6165508094623778733">En savoir plus</translation>
-+<translation id="9052208328806230490">Vous avez enregistré vos imprimantes sur <ph name="CLOUD_PRINT_NAME"/> via le compte <ph name="EMAIL"/>.</translation>
-+<translation id="822618367988303761">il y a <ph name="NUMBER_TWO"/> jours</translation>
-+<translation id="7928333295097642153"><ph name="HOUR"/>:<ph name="MINUTE"/> restantes</translation>
-+<translation id="7568593326407688803">Cette page est en<ph name="ORIGINAL_LANGUAGE"/>Voulez-vous la traduire ?</translation>
-+<translation id="563969276220951735">Saisie automatique des formulaires</translation>
-+<translation id="6870130893560916279">Clavier ukrainien</translation>
-+<translation id="8629974950076222828">Ouvrir tous les favoris dans une fenêtre de navigation privée</translation>
-+<translation id="3126026824346185272">Ctrl</translation>
-+<translation id="4745438305783437565"><ph name="NUMBER_FEW"/> minutes</translation>
-+<translation id="2649911884196340328">Le certificat de sécurité du serveur contient des erreurs !</translation>
-+<translation id="6666647326143344290">avec votre compte Google</translation>
-+<translation id="3828029223314399057">Rechercher dans les favoris</translation>
-+<translation id="4885705234041587624">MSCHAPv2</translation>
-+<translation id="5614190747811328134">Avertissement utilisateur</translation>
-+<translation id="8906421963862390172">&amp;Options du vérificateur d'orthographe</translation>
-+<translation id="9046895021617826162">Échec de la connexion</translation>
-+<translation id="1492188167929010410">Identifiant de l'erreur <ph name="CRASH_ID"/></translation>
-+<translation id="1963692530539281474"><ph name="NUMBER_DEFAULT"/> jours restants</translation>
-+<translation id="4470270245053809099">Émis par : <ph name="NAME"/></translation>
-+<translation id="5365539031341696497">Mode de saisie du thaï (clavier Kesmanee)</translation>
-+<translation id="2403091441537561402">Passerelle :</translation>
-+<translation id="6337234675334993532">Chiffrement</translation>
-+<translation id="668171684555832681">Autre...</translation>
-+<translation id="1932098463447129402">Pas avant le</translation>
-+<translation id="7845920762538502375"><ph name="PRODUCT_NAME"/> n'a pas pu synchroniser vos données, car la connexion avec le serveur de synchronisation n'a pas pu être établie. Nouvel essai...</translation>
-+<translation id="2192664328428693215">Me demander lorsqu'un site souhaite afficher des notifications sur le Bureau (recommandé)</translation>
-+<translation id="6708242697268981054">Source :</translation>
-+<translation id="4786993863723020412">Erreur de lecture du cache</translation>
-+<translation id="6630452975878488444">Raccourci de sélection</translation>
-+<translation id="8709969075297564489">Vérifier la révocation du certificat serveur</translation>
-+<translation id="8698171900303917290">Vous rencontrez des problèmes lors de l'installation ?</translation>
-+<translation id="830868413617744215">Bêta</translation>
-+<translation id="5925147183566400388">Pointeur de la déclaration CPS (Certification Practice Statement)</translation>
-+<translation id="1497270430858433901">Le <ph name="DATE"/>, vous avez reçu <ph name="DATA_AMOUNT"/> à utiliser librement.</translation>
-+<translation id="8150167929304790980">Nom complet</translation>
-+<translation id="636850387210749493">Inscription d'entreprise</translation>
-+<translation id="1947424002851288782">Clavier allemand</translation>
-+<translation id="932508678520956232">Impossible de lancer l'impression.</translation>
-+<translation id="4861833787540810454">&amp;Lire</translation>
-+<translation id="2552545117464357659">Récent</translation>
-+<translation id="7269802741830436641">Cette page Web présente une boucle de redirection.</translation>
-+<translation id="4180788401304023883">Supprimer le certificat &quot;<ph name="CERTIFICATE_NAME"/>&quot; émis par l'autorité de certification ?</translation>
-+<translation id="5869522115854928033">Mots de passe enregistrés</translation>
-+<translation id="2089090684895656482">Moins</translation>
-+<translation id="1709220265083931213">Options avancées</translation>
-+<translation id="5748266869826978907">Vérifiez vos paramètres DNS. Contactez votre administrateur réseau si vous n'êtes pas sûr de vous.</translation>
-+<translation id="4771973620359291008">Une erreur inconnue s'est produite.</translation>
-+<translation id="5509914365760201064">Émetteur : <ph name="CERTIFICATE_AUTHORITY"/></translation>
-+<translation id="7073385929680664879">Passer d'un mode de saisie à l'autre</translation>
-+<translation id="6898699227549475383">Organisation (O)</translation>
-+<translation id="4333854382783149454">PKCS #1 SHA-1 avec chiffrement RSA</translation>
-+<translation id="762904068808419792">Entrez la requête de recherche ici.</translation>
-+<translation id="8615618338313291042">Application en mode navigation privée : <ph name="APP_NAME"/></translation>
-+<translation id="978146274692397928">La largeur de ponctuation initiale est Complète</translation>
-+<translation id="8959027566438633317">Installer <ph name="EXTENSION_NAME"/> ?</translation>
-+<translation id="8155798677707647270">Installation d'une nouvelle version...</translation>
-+<translation id="6886871292305414135">Ouvrir le lien dans un nouvel ongle&amp;t</translation>
-+<translation id="1639192739400715787">Pour accéder aux paramètres de sécurité, saisissez le code PIN de la carte SIM.</translation>
-+<translation id="7961015016161918242">Jamais</translation>
-+<translation id="3950924596163729246">Impossible d'accéder au réseau.</translation>
-+<translation id="2835170189407361413">Effacer le formulaire</translation>
-+<translation id="4631110328717267096">Échec de la mise à jour du système</translation>
-+<translation id="3695919544155087829">Saisissez le mot de passe utilisé pour chiffrer ce fichier de certificat.</translation>
-+<translation id="2230051135190148440">CHAP</translation>
-+<translation id="6308937455967653460">Enregistrer le lie&amp;n sous...</translation>
-+<translation id="5421136146218899937">Effacer les données de navigation...</translation>
-+<translation id="5783059781478674569">Options de reconnaissance vocale</translation>
-+<translation id="5441100684135434593">Réseau câblé</translation>
-+<translation id="3285322247471302225">Nouvel ongle&amp;t</translation>
-+<translation id="3943582379552582368">R&amp;etour</translation>
-+<translation id="7607002721634913082">Téléchargement suspendu</translation>
-+<translation id="480990236307250886">Ouvrir la page d'accueil</translation>
-+<translation id="8286036467436129157">Connexion</translation>
-+<translation id="5999940714422617743">L'installation de <ph name="EXTENSION_NAME"/> est terminée.</translation>
-+<translation id="1122198203221319518">&amp;Outils</translation>
-+<translation id="5757539081890243754">Page d'accueil</translation>
-+<translation id="2760009672169282879">Clavier phonétique bulgare</translation>
-+<translation id="6608140561353073361">Cookies et données de site...</translation>
-+<translation id="8007030362289124303">Batterie faible</translation>
-+<translation id="4513946894732546136">Commentaires</translation>
-+<translation id="1135328998467923690">Package incorrect : &quot;<ph name="ERROR_CODE"/>&quot;.</translation>
-+<translation id="5906719743126878045"><ph name="NUMBER_TWO"/> heures restantes</translation>
-+<translation id="1753682364559456262">Configurer les paramètres de blocage des images...</translation>
-+<translation id="6550675742724504774">Options</translation>
-+<translation id="8959208747503200525"><ph name="NUMBER_TWO"/> hours ago</translation>
-+<translation id="431076611119798497">&amp;Détails</translation>
-+<translation id="737801893573836157">Masquer la barre de titre du système et utiliser les bordures</translation>
-+<translation id="5352235189388345738">Elle peut accéder aux éléments suivants :</translation>
-+<translation id="5040262127954254034">Confidentialité</translation>
-+<translation id="7666868073052500132">Objets : <ph name="USAGES"/></translation>
-+<translation id="6985345720668445131">Paramètres d'entrée du japonais</translation>
-+<translation id="3258281577757096226">Sebeol-sik Final</translation>
-+<translation id="6906268095242253962">Veuillez vous connecter à Internet pour continuer.</translation>
-+<translation id="1908748899139377733">Afficher les &amp;infos sur le cadre</translation>
-+<translation id="803771048473350947">Fichier</translation>
-+<translation id="6206311232642889873">Cop&amp;ier l'image</translation>
-+<translation id="5158983316805876233">Utiliser le même proxy pour tous les protocoles</translation>
-+<translation id="7108338896283013870">Masquer</translation>
-+<translation id="3366404380928138336">Requête de protocole externe</translation>
-+<translation id="5300589172476337783">Afficher</translation>
-+<translation id="3160041952246459240">Certains de vos certificats enregistrés identifient ces serveurs :</translation>
-+<translation id="566920818739465183">Vous avez visité ce site pour la première fois le <ph name="VISIT_DATE"/>.</translation>
-+<translation id="2961695502793809356">Cliquer pour avancer, maintenir pour voir l'historique</translation>
-+<translation id="4092878864607680421">La dernière version de l'application &quot;<ph name="APP_NAME"/>&quot; requiert d'autres autorisations. Elle a donc été désactivée.</translation>
-+<translation id="8421864404045570940"><ph name="NUMBER_DEFAULT"/> secondes</translation>
-+<translation id="5828228029189342317">Vous avez choisi d'ouvrir automatiquement certains types de fichiers après leur téléchargement.</translation>
-+<translation id="1416836038590872660">EAP-MD5</translation>
-+<translation id="176587472219019965">&amp;Nouvelle fenêtre</translation>
-+<translation id="2788135150614412178">+</translation>
-+<translation id="4055738107007928968">Vous avez essayé d'accéder au site <ph name="DOMAIN"/>, mais le serveur a présenté un certificat signé avec un algorithme de signature faible. Il se peut que les informations d'identification fournies par le serveur aient été falsifiées. Le serveur n'est peut-être pas celui auquel vous souhaitez accéder (il peut s'agir d'une tentative de piratage). Nous nous déconseillons vivement de continuer.</translation>
-+<translation id="5308689395849655368">L'envoi de rapports d'erreur est désactivé.</translation>
-+<translation id="8372369524088641025">Clé WEP incorrecte</translation>
-+<translation id="8689341121182997459">Date d'expiration :</translation>
-+<translation id="899403249577094719">URL de base du certificat Netscape</translation>
-+<translation id="2737363922397526254">Réduire...</translation>
-+<translation id="4880827082731008257">Rechercher dans l'historique</translation>
-+<translation id="8661290697478713397">Ouvrir le lien dans la fenêtre de navi&amp;gation privée</translation>
-+<translation id="4197700912384709145"><ph name="NUMBER_ZERO"/> secondes</translation>
-+<translation id="7454780465968211330">Historique avancé pour le champ polyvalent</translation>
-+<translation id="2158448795143567596">Active l'utilisation de graphismes 3D dans les éléments canvas via l'API WebGL.</translation>
-+<translation id="1702534956030472451">Occident</translation>
-+<translation id="6636709850131805001">État non reconnu</translation>
-+<translation id="6095984072944024315">−</translation>
-+<translation id="9141716082071217089">Impossible de vérifier si le certificat du serveur a été révoqué.</translation>
-+<translation id="4304224509867189079">Se connecter</translation>
-+<translation id="5332624210073556029">Fuseau horaire :</translation>
-+<translation id="4799797264838369263">Cette option est soumise à une stratégie d'entreprise. Contactez votre administrateur pour plus d'informations.</translation>
-+<translation id="4492190037599258964">Résultats de recherche pour &quot;<ph name="SEARCH_STRING"/>&quot;</translation>
-+<translation id="3573179567135747900">Revenir à &quot;<ph name="FROM_LOCALE"/>&quot; (redémarrage requis)</translation>
-+<translation id="2238123906478057869"><ph name="PRODUCT_NAME"/> va exécuter les tâches suivantes :</translation>
-+<translation id="4042471398575101546">Ajouter la page</translation>
-+<translation id="8848709220963126773">Changement de mode via la touche Maj</translation>
-+<translation id="4871865824885782245">Options de date et d'heure...</translation>
-+<translation id="8828933418460119530">Nom DNS</translation>
-+<translation id="988159990683914416">Build de développement</translation>
-+<translation id="8026354464835030469"><ph name="BURNT_AMOUNT"/> sur ...</translation>
-+<translation id="4114470632216071239">Verrouiller la carte SIM (code PIN obligatoire pour utiliser les données mobiles)</translation>
-+<translation id="2183426022964444701">Sélectionnez le répertoire racine de l'extension.</translation>
-+<translation id="2517143724531502372">Les cookies de <ph name="DOMAIN"/> sont autorisés uniquement pour cette session.</translation>
-+<translation id="9018524897810991865">Confirmer les préférences de synchronisation</translation>
-+<translation id="4719905780348837473">RSN</translation>
-+<translation id="5212108862377457573">Ajuster la conversion en fonction de l'entrée précédente</translation>
-+<translation id="5398353896536222911">Afficher le panneau de la &amp;vérification orthographique</translation>
-+<translation id="5811533512835101223">(Revenir à la capture d'écran d'origine)</translation>
-+<translation id="5131817835990480221">Mettre à jour &amp;<ph name="PRODUCT_NAME"/></translation>
-+<translation id="939519157834106403">SSID</translation>
-+<translation id="3705722231355495246">-</translation>
-+<translation id="2635102990349508383">Les informations de connexion au compte n'ont pas encore été saisies.</translation>
-+<translation id="6902055721023340732">URL de configuration automatique</translation>
-+<translation id="4268574628540273656">URL :</translation>
-+<translation id="7481312909269577407">Avancer</translation>
-+<translation id="3759876923365568382"><ph name="NUMBER_FEW"/> jours restants</translation>
-+<translation id="295228163843771014">Vous avez choisi de ne pas synchroniser les mots de passe. Vous pouvez à tout moment modifier vos paramètres de synchronisation, si vous changez d'avis.</translation>
-+<translation id="5972826969634861500">Lancer <ph name="PRODUCT_NAME"/></translation>
-+<translation id="7828702903116529889"><ph name="PRODUCT_NAME"/>
-+ ne parvient pas à accéder au réseau.
-+ <ph name="LINE_BREAK"/>
-+ Il est possible que votre pare-feu ou votre antivirus considère
-+ <ph name="PRODUCT_NAME"/>
-+ comme un intrus dans votre ordinateur et qu'il bloque ses tentatives de connexion à Internet.</translation>
-+<translation id="878069093594050299">Ce certificat a été vérifié pour les utilisations suivantes :</translation>
-+<translation id="5852112051279473187">Petit problème ! Une erreur est survenue lors de l'inscription de ce périphérique. Veuillez réessayer ou contacter votre représentant de l'assistance technique.</translation>
-+<translation id="1664314758578115406">Ajouter aux favoris</translation>
-+<translation id="7088418943933034707">Gérer les certificats...</translation>
-+<translation id="8482183012530311851">Analyse du périphérique...</translation>
-+<translation id="3127589841327267804">PYJJ</translation>
-+<translation id="8808478386290700967">Web Store</translation>
-+<translation id="1732215134274276513">Annuler l'épinglage des onglets</translation>
-+<translation id="4084682180776658562">Favori</translation>
-+<translation id="8859057652521303089">Sélectionnez votre langue :</translation>
-+<translation id="3030138564564344289">Réessayer le téléchargement</translation>
-+<translation id="8525552230188318924">Configurer la synchronisation des mots de passe</translation>
-+<translation id="4381091992796011497">Nom d'utilisateur :</translation>
-+<translation id="5830720307094128296">Enregistrer la p&amp;age sous...</translation>
-+<translation id="8114439576766120195">Vos données sur tous les sites Web</translation>
-+<translation id="4668954208278016290">Un problème est survenu lors de l'extraction de l'image sur l'ordinateur.</translation>
-+<translation id="5822838715583768518">Lancer l'application</translation>
-+<translation id="3942974664341190312">Dubeol-sik</translation>
-+<translation id="8477241577829954800">Remplacé</translation>
-+<translation id="6735304988756581115">Afficher les cookies et autres données de site...</translation>
-+<translation id="3048564749795856202">Si vous pensez avoir cerné les risques, vous pouvez <ph name="PROCEED_LINK"/>.</translation>
-+<translation id="2433507940547922241">Apparence</translation>
-+<translation id="839072384475670817">Créer des raccourci&amp;s vers des applications...</translation>
-+<translation id="1478632774608054702">Exécuter le flash PPAPI dans le processus du moteur de rendu</translation>
-+<translation id="6756161853376828318">Définir <ph name="PRODUCT_NAME"/> en tant que navigateur par défaut</translation>
-+<translation id="9112614144067920641">Veuillez choisir un nouveau code PIN.</translation>
-+<translation id="2061855250933714566"><ph name="ENCODING_CATEGORY"/> (<ph name="ENCODING_NAME"/>)</translation>
-+<translation id="7138678301420049075">Autre</translation>
-+<translation id="9147392381910171771">&amp;Options</translation>
-+<translation id="1803557475693955505">Impossible de charger la page d'arrière-plan &quot;<ph name="BACKGROUND_PAGE"/>&quot;.</translation>
-+<translation id="5818334088068591797">À quel niveau rencontrez-vous des problèmes ? (Champ obligatoire)</translation>
-+<translation id="6264485186158353794">Retour à la sécurité</translation>
-+<translation id="5130080518784460891">Eten</translation>
-+<translation id="5847724078457510387">Ce site répertorie tous ses certificats valides dans le système DNS. Un certificat non répertorié a cependant été utilisé par le serveur.</translation>
-+<translation id="1394853081832053657">Options de reconnaissance vocale</translation>
-+<translation id="5037676449506322593">Tout sélectionner</translation>
-+<translation id="2785530881066938471">Impossible de charger le fichier &quot;<ph name="RELATIVE_PATH"/>&quot; pour le script de contenu, car ce fichier n'est pas codé en UTF-8.</translation>
-+<translation id="3807747707162121253">&amp;Annuler</translation>
-+<translation id="3306897190788753224">Désactiver temporairement la personnalisation des conversions, les suggestions basées sur l'historique et le dictionnaire utilisateur</translation>
-+<translation id="2574102660421949343">Les cookies de <ph name="DOMAIN"/> sont autorisés.</translation>
-+<translation id="77999321721642562">Au fil du temps, la zone ci-dessous affichera les huit sites que vous avez le plus visités.</translation>
-+<translation id="1503894213707460512">Le plug-in <ph name="PLUGIN_NAME"/> a besoin de votre autorisation pour s'exécuter.</translation>
-+<translation id="471800408830181311">Échec de création de clé privée</translation>
-+<translation id="1273291576878293349">Ouvrir tous les favoris dans une fenêtre de navigation privée</translation>
-+<translation id="1639058970766796751">Placer dans la file d'attente</translation>
-+<translation id="1177437665183591855">Erreur de certificat serveur inconnue</translation>
-+<translation id="8467473010914675605">Mode de saisie du coréen</translation>
-+<translation id="3819800052061700452">&amp;Plein écran</translation>
-+<translation id="5419540894229653647"><ph name="ERROR_DESCRIPTION_TEXT"/>
-+ <ph name="LINE_BREAK"/>
-+ Vous pouvez essayer de diagnostiquer le problème en procédant comme suit :
-+ <ph name="LINE_BREAK"/>
-+ <ph name="PLATFORM_TEXT"/></translation>
-+<translation id="3533943170037501541">Bienvenue sur votre page d'accueil !</translation>
-+<translation id="2333340435262918287">Vos modifications seront prises en compte au prochain démarrage de <ph name="PRODUCT_NAME"/>.</translation>
-+<translation id="5906065664303289925">Adresse du matériel :</translation>
-+<translation id="3178000186192127858">Lecture seule</translation>
-+<translation id="2187895286714876935">Erreur d'importation du certificat serveur</translation>
-+<translation id="5460896875189097758">Données stockées localement</translation>
-+<translation id="4618990963915449444">Tous les fichiers de <ph name="DEVICE_NAME"/> vont être effacés.</translation>
-+<translation id="614998064310228828">Modèle du périphérique :</translation>
-+<translation id="1581962803218266616">Afficher dans le Finder</translation>
-+<translation id="6096326118418049043">Nom X.500</translation>
-+<translation id="6086259540486894113">Vous devez sélectionner au moins un type de données à synchroniser.</translation>
-+<translation id="923467487918828349">Tout afficher</translation>
-+<translation id="5101042277149003567">Ouvrir tous les favoris</translation>
-+<translation id="4298972503445160211">Clavier danois</translation>
-+<translation id="6621440228032089700">Cette fonctionnalité permet de réaliser un rendu hors écran de la texture, au lieu d'un affichage direct.</translation>
-+<translation id="3488065109653206955">Partiellement activé</translation>
-+<translation id="1481244281142949601">Votre système Sandbox est correctement configuré.</translation>
-+<translation id="4849517651082200438">Ne pas installer</translation>
-+<translation id="8602882075393902833">Activer la recherche instantanée pour accélérer la recherche et la navigation</translation>
-+<translation id="6349678711452810642">Utiliser par défaut</translation>
-+<translation id="6263284346895336537">Non essentielle</translation>
-+<translation id="6409731863280057959">Fenêtres pop-up</translation>
-+<translation id="3459774175445953971">Dernière modification :</translation>
-+<translation id="73289266812733869">Désélectionné</translation>
-+<translation id="3435738964857648380">Sécurité</translation>
-+<translation id="9112987648460918699">Rechercher...</translation>
-+<translation id="2231233239095101917">Le script de la page utilisait trop de mémoire. Rafraîchissez la page pour réactiver le script.</translation>
-+<translation id="870805141700401153">Signature du code individuel Microsoft</translation>
-+<translation id="5119173345047096771">Mozilla Firefox</translation>
-+<translation id="9020278534503090146">Page Web inaccessible</translation>
-+<translation id="4768698601728450387">Recadrer l'image</translation>
-+<translation id="6245028464673554252">Si vous fermez <ph name="PRODUCT_NAME"/> maintenant, le téléchargement sera annulé.</translation>
-+<translation id="3943857333388298514">Coller</translation>
-+<translation id="385051799172605136">Retour</translation>
-+<translation id="1742300158964248589">Impossible de graver l'image.</translation>
-+<translation id="2670965183549957348">Mode de saisie du Chewing</translation>
-+<translation id="5095208057601539847">Province</translation>
-+<translation id="4085298594534903246">JavaScript a été bloqué sur cette page.</translation>
-+<translation id="5630492933376732170">Remarque : Lorsque vous cliquez sur &quot;Envoyer&quot;, Google Chrome OS
-+ joint à votre envoi un journal des événements système de
-+ votre périphérique. Ces informations nous permettent de diagnostiquer les
-+ problèmes, de comprendre comment vous interagissez avec votre
-+ périphérique et d'améliorer les performances de ce dernier. Les
-+ informations personnelles fournies sciemment dans vos commentaires ou
-+ involontairement dans les journaux système et la capture d'écran sont
-+ protégées conformément à nos <ph name="BEGIN_LINK"/>Règles de confidentialité<ph name="END_LINK"/>.
-+ Si vous ne souhaitez pas envoyer de journaux système, décochez la case
-+ &quot;Inclure les informations système&quot;.</translation>
-+<translation id="4341977339441987045">Interdire à tous les sites de stocker des données</translation>
-+<translation id="806812017500012252">Trier par nom</translation>
-+<translation id="3781751432212184938">Afficher un aperçu des onglets...</translation>
-+<translation id="2960316970329790041">Annuler l'importation</translation>
-+<translation id="3835522725882634757">Ce serveur envoie des données que <ph name="PRODUCT_NAME"/> ne comprend pas. Veuillez <ph name="BEGIN_LINK"/>signaler un bug<ph name="END_LINK"/> et inclure la <ph name="BEGIN2_LINK"/>liste des raw<ph name="END2_LINK"/>.</translation>
-+<translation id="5361734574074701223">Calcul de la durée restante</translation>
-+<translation id="6937152069980083337">Mode de saisie Google du japonais (pour clavier américain)</translation>
-+<translation id="1731911755844941020">Envoi de la requête...</translation>
-+<translation id="8371695176452482769">Parlez maintenant</translation>
-+<translation id="2988488679308982380">Impossible d'installer le package : &quot;<ph name="ERROR_CODE"/>&quot;</translation>
-+<translation id="2904079386864173492">Modèle :</translation>
-+<translation id="3447644283769633681">Bloquer tous les cookies tiers</translation>
-+<translation id="8917047707340793412">Remplacer par <ph name="ENGINE_NAME"/></translation>
-+<translation id="6129953537138746214">Espace</translation>
-+<translation id="3704331259350077894">Arrêt du fonctionnement</translation>
-+<translation id="5801568494490449797">Préférences</translation>
-+<translation id="1038842779957582377">Nom inconnu</translation>
-+<translation id="5327248766486351172">Nom</translation>
-+<translation id="5553784454066145694">Choisir un nouveau code PIN</translation>
-+<translation id="8989148748219918422"><ph name="ORGANIZATION"/> [<ph name="COUNTRY"/>]</translation>
-+<translation id="4664482161435122549">Erreur d'exportation de fichier PKCS #12</translation>
-+<translation id="2445081178310039857">Le répertoire racine de l'extension doit être indiqué.</translation>
-+<translation id="8251578425305135684">Miniature supprimée</translation>
-+<translation id="6163522313638838258">Tout développer...</translation>
-+<translation id="3037605927509011580">Aie aie aie</translation>
-+<translation id="5803531701633845775">Choisir les expressions en arrière-plan, sans déplacer le pointeur</translation>
-+<translation id="1918141783557917887">Plu&amp;s petit</translation>
-+<translation id="6996550240668667907">Afficher le clavier en superposition</translation>
-+<translation id="4065006016613364460">C&amp;opier l'URL de l'image</translation>
-+<translation id="6965382102122355670">OK</translation>
-+<translation id="8000066093800657092">Aucun réseau détecté</translation>
-+<translation id="4481249487722541506">Charger l'extension non empaquetée...</translation>
-+<translation id="8180239481735238521">page</translation>
-+<translation id="8321738493186308836">Active l'interface utilisateur et le code de support pour le processus du service de communication à distance, de même que le plug-in client. Avertissement : ce service n'est actuellement disponible que pour les tests de développeurs. Si vous ne faites pas partie de l'équipe de développement et ne figurez pas sur la liste blanche, aucun élément de l'interface utilisateur activée ne fonctionnera.</translation>
-+<translation id="2963783323012015985">Clavier turc</translation>
-+<translation id="2149973817440762519">Modifier le favori</translation>
-+<translation id="5431318178759467895">Couleur</translation>
-+<translation id="7064842770504520784">Personnaliser les paramètres de synchronisation...</translation>
-+<translation id="2784407158394623927">Activation de votre service Internet mobile</translation>
-+<translation id="3679848754951088761"><ph name="SOURCE_ORIGIN"/></translation>
-+<translation id="6920989436227028121">Ouvrir dans un onglet standard</translation>
-+<translation id="4057041477816018958"><ph name="SPEED"/> - <ph name="RECEIVED_AMOUNT"/></translation>
-+<translation id="2050339315714019657">Portrait</translation>
-+<translation id="6978839998405419496"><ph name="NUMBER_ZERO"/> days ago</translation>
-+<translation id="6139139147415955203">Active un service en arrière-plan qui connecte le service <ph name="CLOUD_PRINT_NAME"/> aux éventuelles imprimantes installées sur cet ordinateur. Une fois ce labo activé, vous pouvez lancer <ph name="CLOUD_PRINT_NAME"/> en vous connectant à votre compte Google via Options/Préférences dans la section Options avancées.</translation>
-+<translation id="5112577000029535889">Outils de &amp;développement</translation>
-+<translation id="2301382460326681002">Le répertoire racine de l'extension est incorrect.</translation>
-+<translation id="7839192898639727867">ID de clé de l'objet du certificat</translation>
-+<translation id="4759238208242260848">Téléchargements</translation>
-+<translation id="2879560882721503072">Le stockage du certificat client généré par <ph name="ISSUER"/> a réussi.</translation>
-+<translation id="1275718070701477396">Sélectionnée</translation>
-+<translation id="1178581264944972037">Suspendre</translation>
-+<translation id="6492313032770352219">Taille sur le disque :</translation>
-+<translation id="5233231016133573565">ID du processus</translation>
-+<translation id="5941711191222866238">Réduire</translation>
-+<translation id="4121428309786185360">Expire le</translation>
-+<translation id="2049137146490122801">Votre administrateur a désactivé l'accès aux fichiers locaux sur votre ordinateur.</translation>
-+<translation id="1146498888431277930">Erreur de connexion SSL</translation>
-+<translation id="8041089156583427627">Envoyer</translation>
-+<translation id="6394627529324717982">Virgule</translation>
-+<translation id="253434972992662860">&amp;Pause</translation>
-+<translation id="335985608243443814">Parcourir...</translation>
-+<translation id="7802488492289385605">Mode de saisie Google du japonais (pour clavier Dvorak américain)</translation>
-+<translation id="7452120598248906474">Police à largeur fixe</translation>
-+<translation id="3129687551880844787">Stockage de session</translation>
-+<translation id="7427348830195639090">Page en arrière-plan : <ph name="BACKGROUND_PAGE_URL"/></translation>
-+<translation id="5898154795085152510">Le serveur a renvoyé un certificat client incorrect. Erreur <ph name="ERROR_NUMBER"/> (<ph name="ERROR_NAME"/>)</translation>
-+<translation id="2704184184447774363">Signature de document Microsoft</translation>
-+<translation id="5677928146339483299">Bloqué</translation>
-+<translation id="1474842329983231719">Gérer les paramètres d'impression...</translation>
-+<translation id="2455981314101692989">Cette page Web a désactivé la saisie automatique dans ce formulaire.</translation>
-+<translation id="1646136617204068573">Clavier hongrois</translation>
-+<translation id="5988840637546770870">Les versions en développement permettent de tester de nouvelles idées, mais elles peuvent s'avérer très instables. Nous vous prions d'agir avec précaution.</translation>
-+<translation id="3569713929051927529">Ajouter un dossier...</translation>
-+<translation id="4032664149172368180">Mode de saisie du japonais (pour clavier Dvorak américain)</translation>
-+<translation id="3748706263662799310">Signaler un bug</translation>
-+<translation id="7167486101654761064">&amp;Toujours ouvrir les fichiers de ce type</translation>
-+<translation id="4283623729247862189">Disque optique</translation>
-+<translation id="5826507051599432481">Nom commun</translation>
-+<translation id="8914326144705007149">Très grande</translation>
-+<translation id="4215444178533108414">Modification terminée</translation>
-+<translation id="5154702632169343078">Objet</translation>
-+<translation id="2273562597641264981">Opérateur :</translation>
-+<translation id="122082903575839559">Algorithme de signature du certificat</translation>
-+<translation id="2181257377760181418">Cette fonctionnalité permet d'afficher un onglet d'aperçu avant de lancer une impression.</translation>
-+<translation id="7240120331469437312">Autre nom de l'objet du certificat</translation>
-+<translation id="6900113680982781280">Activer la saisie automatique pour remplir les formulaires Web d'un simple clic</translation>
-+<translation id="1131850611586448366">Le site Web à l'adresse <ph name="HOST_NAME"/> a été signalé comme étant un site de phishing. Ces sites tentent d'amener les internautes à divulguer leurs informations personnelles en se faisant passer pour des institutions de confiance, telles que des banques.</translation>
-+<translation id="5413218268059792983">Rechercher directement sur <ph name="SEARCH_ENGINE"/></translation>
-+<translation id="1161575384898972166">Connectez-vous à <ph name="TOKEN_NAME"/> pour exporter le certificat client.</translation>
-+<translation id="1718559768876751602">Créer un compte Google maintenant</translation>
-+<translation id="1884319566525838835">État Sandbox</translation>
-+<translation id="2770465223704140727">Retirer de la liste</translation>
-+<translation id="3590587280253938212">rapide</translation>
-+<translation id="6053401458108962351">&amp;Effacer les données de navigation…</translation>
-+<translation id="2339641773402824483">Vérification des mises à jour...</translation>
-+<translation id="9111742992492686570">Télécharger les mises à jour de sécurité essentielles</translation>
-+<translation id="8636666366616799973">Package incorrect. Détails : &quot;<ph name="ERROR_MESSAGE"/>&quot;.</translation>
-+<translation id="2045969484888636535">Continuer à bloquer les cookies</translation>
-+<translation id="7353601530677266744">Ligne de commande</translation>
-+<translation id="2766006623206032690">Coller l'URL et y a&amp;ccéder</translation>
-+<translation id="4394049700291259645">Désactiver</translation>
-+<translation id="969892804517981540">Build officiel</translation>
-+<translation id="445923051607553918">Se connecter à un réseau Wi-Fi</translation>
-+<translation id="100242374795662595">Périphérique inconnu</translation>
-+<translation id="9087725134750123268">Supprimer les cookies et autres données de site</translation>
-+<translation id="5050255233730056751">URL saisies</translation>
-+<translation id="3349155901412833452">Utiliser les touches , et . pour paginer une liste d'entrées</translation>
-+<translation id="6872947427305732831">Vider la mémoire</translation>
-+<translation id="2742870351467570537">Supprimer les éléments sélectionnés</translation>
-+<translation id="7561196759112975576">Toujours</translation>
-+<translation id="2116673936380190819">de moins d'une heure</translation>
-+<translation id="5765491088802881382">Aucun réseau n'est disponible.</translation>
-+<translation id="1971538228422220140">Supprimer les cookies et autres données de site et de plug-in</translation>
-+<translation id="883487340845134897">Intervertir les touches Rechercher et Ctrl gauche</translation>
-+<translation id="5692957461404855190">Faites glisser trois doigts sur la surface de votre trackpad pour afficher un aperçu de tous vos onglets. Cliquez sur une vignette pour la sélectionner. Idéal en mode plein écran.</translation>
-+<translation id="1375215959205954975">Nouveau ! Configurer la synchronisation des mots de passe</translation>
-+<translation id="5183088099396036950">Échec de la tentative de connexion au serveur</translation>
-+<translation id="4469842253116033348">Désactiver les notifications de <ph name="SITE"/></translation>
-+<translation id="7999229196265990314">Les fichiers suivants ont été créés :
-+
-+Extension : <ph name="EXTENSION_FILE"/>
-+Fichier de clé : <ph name="KEY_FILE"/>
-+
-+Conservez votre fichier de clé en lieu sûr. Vous en aurez besoin lors de la création de nouvelles versions de l'extension.</translation>
-+<translation id="1846078536247420691">&amp;Oui</translation>
-+<translation id="3036649622769666520">Ouvrir les fichiers</translation>
-+<translation id="2966459079597787514">Clavier suédois</translation>
-+<translation id="7685049629764448582">Mémoire JavaScript </translation>
-+<translation id="6398765197997659313">Quitter le mode plein écran</translation>
-+<translation id="6059652578941944813">Hiérarchie des certificats</translation>
-+<translation id="4886690096315032939">Afficher l'onglet existant si l'URL associée est demandée dans un autre</translation>
-+<translation id="5729712731028706266">&amp;Afficher</translation>
-+<translation id="774576312655125744">Vos données personnelles sur <ph name="WEBSITE_1"/>, <ph name="WEBSITE_2"/> et sur <ph name="NUMBER_OF_OTHER_WEBSITES"/> autres sites Web</translation>
-+<translation id="6359806961507272919">SMS de <ph name="PHONE_NUMBER"/></translation>
-+<translation id="4508765956121923607">Afficher la s&amp;ource</translation>
-+<translation id="5975083100439434680">Zoom arrière</translation>
-+<translation id="8080048886850452639">C&amp;opier l'URL du fichier audio</translation>
-+<translation id="2817109084437064140">Importer et associer au périphérique...</translation>
-+<translation id="3331321258768829690">(<ph name="UTCOFFSET"/>) <ph name="LONGTZNAME"/> (<ph name="EXEMPLARCITY"/>)</translation>
-+<translation id="619398760000422129">Plug-ins (par ex. Adobe Flash Player, QuickTime, etc.)</translation>
-+<translation id="5849869942539715694">Empaqueter l'extension...</translation>
-+<translation id="7339785458027436441">Vérifier l'orthographe lors de la frappe</translation>
-+<translation id="8308427013383895095">Échec de la traduction en raison d'un problème de connexion réseau</translation>
-+<translation id="1801298019027379214">Code PIN incorrect. Veuillez réessayer. Nombre de tentatives restantes : <ph name="TRIES_COUNT"/></translation>
-+<translation id="1384721974622518101">Vous pouvez effectuer une recherche directement à partir du champ ci-dessus.</translation>
-+<translation id="992543612453727859">Ajouter les expressions au premier plan</translation>
-+<translation id="3857773447683694438">Lorem ipsum dolor sit amet, consectetur adipiscing elit.</translation>
-+<translation id="1244147615850840081">Opérateur</translation>
-+<translation id="8203365863660628138">Confirmer l'installation</translation>
-+<translation id="406259880812417922">(Mot clé : <ph name="KEYWORD"/>)</translation>
-+<translation id="309628958563171656">Sensibilité :</translation>
-+</translationbundle>
-diff --git a/tools/grit/grit/testdata/generated_resources_iw.xtb b/tools/grit/grit/testdata/generated_resources_iw.xtb
-new file mode 100644
-index 0000000000..86b55334c0
---- /dev/null
-+++ b/tools/grit/grit/testdata/generated_resources_iw.xtb
-@@ -0,0 +1,4 @@
-+<?xml version="1.0" ?>
-+<!DOCTYPE translationbundle>
-+<translationbundle lang="iw">
-+</translationbundle>
-diff --git a/tools/grit/grit/testdata/generated_resources_no.xtb b/tools/grit/grit/testdata/generated_resources_no.xtb
-new file mode 100644
-index 0000000000..913638ba4e
---- /dev/null
-+++ b/tools/grit/grit/testdata/generated_resources_no.xtb
-@@ -0,0 +1,4 @@
-+<?xml version="1.0" ?>
-+<!DOCTYPE translationbundle>
-+<translationbundle lang="no">
-+</translationbundle>
-diff --git a/tools/grit/grit/testdata/grit_part.grdp b/tools/grit/grit/testdata/grit_part.grdp
-new file mode 100644
-index 0000000000..c8e9d92692
---- /dev/null
-+++ b/tools/grit/grit/testdata/grit_part.grdp
-@@ -0,0 +1,5 @@
-+<?xml version="1.0" encoding="UTF-8"?>
-+<grit-part>
-+ <!-- Important for test purposes that this file not exist. -->
-+ <structure type="chrome_scaled_image" name="IDR_DOES_NOT_EXIST" file="does-not-exist.png" />
-+</grit-part>
-diff --git a/tools/grit/grit/testdata/header.html b/tools/grit/grit/testdata/header.html
-new file mode 100644
-index 0000000000..8e9d10ec50
---- /dev/null
-+++ b/tools/grit/grit/testdata/header.html
-@@ -0,0 +1,39 @@
-+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
-+<html><head><title>[$~TITLE~$]</title>
-+<meta http-equiv="cache-control" content="no-cache">
-+<meta http-equiv="content-type" content="text/html; charset=utf-8">
-+<meta http-equiv="pragma" content="no-cache">
-+<meta http-equiv="expires" content="-1">
-+[EXTRA_META]
-+<style>
-+BODY,TD,DIV,.P,A { FONT-FAMILY: arial,sans-serif}
-+DIV,TD { COLOR: #000}
-+.f { COLOR: #6f6f6f}
-+.fl:link { COLOR: #6f6f6f}
-+A:link, .w, A.w:link, .w A:link { COLOR: #00c}
-+A:visited { COLOR: #551a8b}
-+.fl:visited { COLOR: #551a8b}
-+A:active, .fl:active { COLOR: #f00}
-+.h { COLOR: #3399CC}
-+.i { COLOR: #a90a08}
-+.i:link { COLOR: #a90a08}
-+.a, .a:link, .a:visited { COLOR: #008000}
-+DIV.n { MARGIN-TOP: 1ex}
-+.n A { FONT-SIZE: 10pt; COLOR: #000}
-+.n .i { FONT-WEIGHT: bold; FONT-SIZE: 10pt}
-+.q A:visited { COLOR: #00c}
-+.q A:link { COLOR: #00c}
-+.q A:active { COLOR: #00c}
-+.q { COLOR: #00c}
-+.b { FONT-WEIGHT: bold; FONT-SIZE: 12pt; COLOR: #00c}
-+.ch { CURSOR: hand}
-+.e { MARGIN-TOP: 0.75em; MARGIN-BOTTOM: 0.75em}
-+.g { MARGIN-TOP: 1em; MARGIN-BOTTOM: 1em}
-+.f { MARGIN-TOP: 0.5em; MARGIN-BOTTOM: 0.25em}
-+.s { HEIGHT: 10px }
-+.c:active { COLOR: #ff0000}
-+.c:visited { COLOR: #551a8b}
-+.c:link { COLOR: #7777cc}
-+.c { COLOR: #7777cc }
-+</style>
-+</head>
-\ No newline at end of file
-diff --git a/tools/grit/grit/testdata/homepage.html b/tools/grit/grit/testdata/homepage.html
-new file mode 100644
-index 0000000000..cce4f2cf35
---- /dev/null
-+++ b/tools/grit/grit/testdata/homepage.html
-@@ -0,0 +1,37 @@
-+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
-+<html><head><title>Google Desktop Search</title>
-+<meta http-equiv="content-type" content="text/html; charset=utf-8">
-+<meta http-equiv="cache-control" content="no-cache">
-+<meta http-equiv="pragma" content="no-cache">
-+<meta http-equiv="expires" content="-1">
-+<style>
-+BODY,TD,A,P {FONT-FAMILY: arial,sans-serif}
-+.q {COLOR: #0000cc}
-+</style>
-+<script>
-+<!--
-+function sf(){document.f.q.focus();}
-+// -->
-+</script>
-+</head>
-+<BODY text=#000000 vLink=#551a8b aLink=#ff0000 link=#0000cc bgColor=#ffffff onload=sf()>
-+<center>
-+<TABLE cellSpacing=0 cellPadding=0 border=0>
-+<tr><td><a href="[$~HOMEPAGE~$]"><IMG border=0 height=110 alt="Google Desktop Search" src="hp_logo.gif" width=276></a></td></tr></table><BR>
-+<FORM name=f method=GET action='[$~SEARCHURL~$]'>
-+<TABLE cellSpacing=0 cellPadding=4 border=0>
-+<tr>
-+<TD class=q noWrap><FONT size=-1>
-+[$~LINKS~$]
-+</font></td>
-+</tr></table>
-+<table cellspacing=0 cellpadding=0>
-+<tr vAlign=top>
-+<td width=25%>&nbsp;</td>
-+<td align=center><input maxlength=256 size=62 name=q value="[DISP_QUERY]"><br><input type=submit value="Search Desktop" name=btnG><INPUT type=submit value="Search the Web" name="redir" accesskey=w></td>
-+<td align=left valign=top nowrap width=25%><font size=-2>&nbsp;&nbsp;<A href="[$~PREFERENCES~$]">Desktop&nbsp;Preferences</a></font></td>
-+</tr></table></FORM>
-+<p><FONT color=#224499><B>Search your own computer.</B></font></p>
-+<span style='width:29em'>[$~MESSAGE~$]</span><br>
-+<br><FONT size=-1>[$~SETHOMEPAGE~$][$~BOTTOMLINE~$]</font></p>
-+<p><FONT size=-2>&copy;2005 Google - Searching [NUM_ITEMS] items</font></p></center></body></html>
-\ No newline at end of file
-diff --git a/tools/grit/grit/testdata/hover.html b/tools/grit/grit/testdata/hover.html
-new file mode 100644
-index 0000000000..b8f9ce0200
---- /dev/null
-+++ b/tools/grit/grit/testdata/hover.html
-@@ -0,0 +1,177 @@
-+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
-+<html><head>
-+<meta http-equiv="content-type" content="text/html; charset=utf-8">
-+<meta http-equiv="cache-control" content="no-cache">
-+<meta http-equiv="pragma" content="no-cache">
-+<meta http-equiv="expires" content="-1">
-+<style>
-+BODY { FONT-SIZE: 8pt; FONT-FAMILY: verdana,arial,san-serif }
-+P { FONT-SIZE: 8pt; FONT-FAMILY: verdana,arial,san-serif }
-+TD { FONT-SIZE: 8pt; FONT-FAMILY: verdana,arial,san-serif }
-+A { FONT-SIZE: 8pt; FONT-FAMILY: verdana,arial,san-serif }
-+DIV { FONT-SIZE: 8pt; TEXT-DECORATION: none }
-+A:hover { COLOR: #ffffff }
-+.border { BORDER-RIGHT: 0px; BORDER-TOP: 0px; BORDER-LEFT: 0px; BORDER-BOTTOM: 0px }
-+</style>
-+
-+<!-- menu experiment start -->
-+
-+<style>
-+<!--
-+.menu1 {
-+cursor:default;
-+position:absolute;
-+left: 10;
-+top: 0;
-+text-align: left;
-+font-family: Arial, Helvetica, sans-serif;
-+font-size: 8pt;
-+background-color: menu;
-+visibility: hidden;
-+padding-top: 2px;
-+padding-bottom: 2px;
-+border: 1 solid;
-+border-color: #888888;
-+z-index: 100;
-+}
-+.menuitems {
-+padding-left: 5px;
-+padding-right: 5px;
-+}
-+-->
-+</style>
-+<SCRIPT LANGUAGE="JavaScript1.2">
-+<!--
-+var menustyle = "menu1";
-+
-+function showmenu() {
-+ // var rightedge = document.body.clientWidth-event.clientX;
-+ // var bottomedge = document.body.clientHeight-event.clientY;
-+ // if (rightedge < rcmenu.offsetWidth)
-+ // rcmenu.style.left = document.body.scrollLeft + event.clientX - rcmenu.offsetWidth;
-+ // else
-+ // rcmenu.style.left = document.body.scrollLeft + event.clientX;
-+ // if (bottomedge < rcmenu.offsetHeight)
-+ // rcmenu.style.top = document.body.scrollTop + event.clientY - rcmenu.offsetHeight;
-+ // else
-+ // rcmenu.style.top = document.body.scrollTop + event.clientY;
-+
-+ rcmenu.style.visibility = "visible";
-+ // rcmenu.style.zindex = 0;
-+ // document.all('rcmenu').style.zindex = 20;
-+ document.onkeydown=ck;
-+ return false;
-+}
-+
-+function hidemenu() {
-+ rcmenu.style.visibility = "hidden";
-+}
-+
-+function ck(e){
-+ evt=document.all?window.event:e;
-+ k=document.all?window.event.keyCode:e.keyCode;
-+
-+ if(k==27 /*<Esc>*/) {
-+ hidemenu();
-+ }
-+}
-+
-+function menumouseover() {
-+ if (event.srcElement.className == "menuitems") {
-+ event.srcElement.style.backgroundColor = "highlight";
-+ event.srcElement.style.color = "white";
-+ }
-+}
-+
-+function menumouseout() {
-+ if (event.srcElement.className == "menuitems") {
-+ event.srcElement.style.backgroundColor = "";
-+ event.srcElement.style.color = "black";
-+ window.status = "";
-+ }
-+}
-+
-+function menuselect() {
-+ if (event.srcElement.className == "menuitems") {
-+ if (event.srcElement.getAttribute("target") != null)
-+ window.open(event.srcElement.url, event.srcElement.getAttribute("target"));
-+ else if (event.srcElement.url.length)
-+ window.location = event.srcElement.url;
-+ }
-+}
-+// -->
-+</script>
-+
-+<!-- menu experiment end -->
-+
-+</head>
-+<BODY bottomMargin=0 bgColor=#3300cc leftMargin=0 topMargin=0 rightMargin=0 marginwidth="0" marginheight="0" border=0 style="border-width:0;" scroll=no>
-+
-+<!-- <br> -->
-+
-+<!-- menu experiment start -->
-+
-+<div id="rcmenu" class="skin0" onMouseover="menumouseover()" onMouseout="menumouseout()" onClick="menuselect();">
-+<span class="menuitems" url="[$~SETDISP1~$]">Sidebar</span>
-+<span class="menuitems" url="[$~SETDISP4~$]">Minibar</span>
-+<span class="menuitems" url="[$~HIDE2~$]">Close</span>
-+</div>
-+
-+<script language="JavaScript1.2">
-+if (document.all && window.print) {
-+ rcmenu.className = menustyle;
-+ document.oncontextmenu = showmenu;
-+ document.body.onclick = hidemenu;
-+}
-+</script>
-+
-+<!-- menu experiment end -->
-+
-+<script>
-+function hide() {
-+ return 1;
-+ // return confirm("Are you sure you want to hide the minibar?\nYou can show it again in Google Desktop Search Preferences. ");
-+}
-+function clear() {
-+ document.getElementById('q').value='';
-+}
-+</script>
-+
-+<TABLE cellSpacing=0 cellPadding=0 bgColor=#3300cc border=0><TBODY>
-+<tr><TD vAlign=top>
-+
-+<form method=get action="[$~SEARCHURL~$]" [$~SEARCH_TARGET~$] name=f1 ID="f1" onsubmit="window.setTimeout('clear()', 500)">
-+<input type=hidden name=src value=3>
-+<input type=hidden name=redir value='' ID="redir">
-+
-+<TABLE cellSpacing=0 cellPadding=0 bgColor=#3300cc border=0><TBODY>
-+
-+<tr>
-+<!-- border-top: #414a4f 0px solid; -->
-+<!-- z-index:2; z-order:2; -->
-+<TD vAlign=top>&nbsp;<INPUT name=q style="position:relative; height=19px;" class=border size=12>&nbsp;</td>
-+
-+<TD TABINDEX="2" onkeydown='if(event.keyCode!=16&&event.keyCode!=9)onclick()' onclick="f1.submit();q.value=''" class=ch onmouseover="this.bgColor='6666FF'" style="BORDER-RIGHT: #000000 1px solid; BORDER-TOP: #6666cc 1px solid; BORDER-LEFT: #6666cc 1px solid; BORDER-BOTTOM: #000000 1px solid" onmouseout="this.bgColor='#000099'" vAlign=center align=middle bgColor=#000099><IMG src="logo.gif" align=middle></td>
-+
-+<TD width=2><IMG height=1 width=1></td>
-+
-+<TD TABINDEX="3" onkeydown='if(event.keyCode!=16&&event.keyCode!=9)onclick()' onclick="redir.value='google'; f1.submit(); redir.value='';q.value=''" class=ch onmouseover="this.bgColor='6666FF'" style="BORDER-RIGHT: #000000 1px solid; BORDER-TOP: #6666cc 1px solid; BORDER-LEFT: #6666cc 1px solid; BORDER-BOTTOM: #000000 1px solid" onmouseout="this.bgColor='#000099'" vAlign=center align=middle bgColor=#000099><IMG src="gfavicon.ico" align=middle></td></tr>
-+</TBODY></table>
-+</td>
-+
-+<TD width=5><IMG height=1 width=1></td>
-+
-+<TD vAlign=top>
-+
-+<TABLE cellSpacing=0 cellPadding=1 bgColor=#000099><TBODY>
-+<tr><TD TABINDEX="4" onkeydown='if(event.keyCode!=16&&event.keyCode!=9)onclick()' valign=top onclick="location.href='[$~SETDISP1~$]';" class=ch onmouseover="this.bgColor='6666FF'" style="BORDER-RIGHT: #000000 1px solid; BORDER-TOP: #6666cc 1px solid; BORDER-LEFT: #6666cc 1px solid; BORDER-BOTTOM: #000000 1px solid" onmouseout="this.bgColor='#000099'" vAlign=top noWrap bgColor=#000099><IMG src="down.gif"></td></tr></TBODY></table>
-+
-+</td>
-+
-+<TD width=1><IMG height=1 width=1></td>
-+
-+<TD vAlign=top><TABLE cellSpacing=0 cellPadding=1><TBODY>
-+<tr><TD TABINDEX="5" onkeydown='if(event.keyCode!=16&&event.keyCode!=9)onclick()' valign=top onclick="if (hide())location.href='[$~HIDE2~$]';" class=ch onmouseover="this.bgColor='6666FF'" style="BORDER-RIGHT: #000000 1px solid; BORDER-TOP: #6666cc 1px solid; BORDER-LEFT: #6666cc 1px solid; BORDER-BOTTOM: #000000 1px solid" onmouseout="this.bgColor='#000099'" vAlign=top noWrap bgColor=#000099><IMG src="close.gif"></td></tr></TBODY></table></td></tr></TBODY></table>
-+
-+</form>
-+</body></html>
-diff --git a/tools/grit/grit/testdata/include_test.html b/tools/grit/grit/testdata/include_test.html
-new file mode 100644
-index 0000000000..e08f2e2e8a
---- /dev/null
-+++ b/tools/grit/grit/testdata/include_test.html
-@@ -0,0 +1,31 @@
-+<include src="included_sample.html">
-+<if expr="True">
-+should be kept
-+</if>
-+in the middle...
-+<if expr="False">
-+should be removed
-+</if>
-+
-+<if expr="False">
-+should be removed
-+ <if expr="True">
-+ should be removed because outer expr is False
-+ </if>
-+should be removed
-+</if>
-+
-+<if expr="True">
-+ <if expr="True">
-+ <if expr="True">
-+ nested true should be kept
-+ </if>
-+ <if expr="False">
-+ should be removed
-+ </if>
-+ </if>
-+ <if expr="True">
-+ silbing true should be kept
-+ </if>
-+</if>
-+at the end...
-diff --git a/tools/grit/grit/testdata/included_sample.html b/tools/grit/grit/testdata/included_sample.html
-new file mode 100644
-index 0000000000..7150ffcbea
---- /dev/null
-+++ b/tools/grit/grit/testdata/included_sample.html
-@@ -0,0 +1 @@
-+Hello Include!
-diff --git a/tools/grit/grit/testdata/indexing_speed.html b/tools/grit/grit/testdata/indexing_speed.html
-new file mode 100644
-index 0000000000..db1787b0e2
---- /dev/null
-+++ b/tools/grit/grit/testdata/indexing_speed.html
-@@ -0,0 +1,58 @@
-+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
-+<html><head><title>Google Desktop Search Index Speed</title>
-+<meta http-equiv=content-type content="text/html; charset=utf-8">
-+<style>
-+BODY {
-+ MARGIN-LEFT: 3em; MARGIN-RIGHT: 3em; FONT-FAMILY: arial,sans-serif
-+}
-+</style>
-+</head>
-+<BODY text=#000000 vLink=#551a8b aLink=#ff0000 link=#0000cc bgColor=#ffffff>
-+<TABLE cellSpacing=2 cellPadding=0 width="100%" border=0>
-+ <tr>
-+ <TD vAlign=top width="1%"><A href="[$~HOMEPAGE~$]">
-+ <IMG alt="Go to Google Desktop Search" src="/logo3.gif" border=0></A></td>
-+ <td>&nbsp;</td>
-+ <TD noWrap>
-+ <TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
-+ <tr>
-+ <TD bgColor=#3399CC><IMG height=1 alt="" width=1></td>
-+ </tr>
-+ </table>
-+ <TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
-+ <tr>
-+ <TD noWrap bgColor=#efefef><B>&nbsp;Index Speed</B></td>
-+ <TD noWrap align=right bgColor=#efefef><FONT size=-1><A href="/customize.html">Index Speed
-+ Help</A> | <A href="[$~ABOUT~$]"> About Google Desktop Search</A></font></td>
-+ </tr></table></td></tr></table>
-+<FONT size=-1>
-+<p>
-+To make your emails, files, and previously viewed web pages searchable, Google Desktop Search
-+needs to index them. This indexing process is currently occuring in the background
-+and your computer performance is minimally impacted.
-+<p>
-+You have the option of speeding up this process.
-+<p><B><FONT color=#FF0000>Warning:</font></B> Speeding up indexing will cause your computer
-+to become unusable for many minutes, depending on how many items need to be indexed. FAST INDEXING IS NOT
-+RECOMMENDED.
-+<BR>&nbsp;<BR>
-+<FORM action="[$~SETINDEXSPEED~$]" method=GET>
-+<input name=url value="[PREVPAGE]" type=hidden>
-+<input type=radio name=FAST value="0" [FAST0-CHECKED] id=f0><label for=f0>Use background indexing (recommended)</label><br>
-+<input type=radio name=FAST value="1" [FAST1-CHECKED] id=f1><label for=f1>Use fast indexing</label><br><br>
-+<input type=submit value="Set Indexing Speed">
-+</FORM>
-+<BR>
-+
-+<p>&nbsp;<BR>
-+<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
-+<TR bgColor=#3399CC>
-+ <TD align=middle height=1><IMG height=1 alt="" width=1></td></tr>
-+</table>
-+
-+<TABLE cellSpacing=0 cellPadding=0 width="100%" align=center bgColor=#efefef border=0>
-+<tr>
-+ <TD align=middle height=20><FONT size=-1><A href="[$~HOMEPAGE~$]">Google Desktop Search&nbsp;Home</A> - <a href="[$~STATUS~$]">Status</a> - <A href="[$~ABOUT~$]">About Google Desktop Search</A> - [$~BUILDNUMBER~$] - &copy;2005 Google </font> </td></tr>
-+</table><BR>
-+</body>
-+</html>
-\ No newline at end of file
-diff --git a/tools/grit/grit/testdata/install_prefs.html b/tools/grit/grit/testdata/install_prefs.html
-new file mode 100644
-index 0000000000..eca0b56de5
---- /dev/null
-+++ b/tools/grit/grit/testdata/install_prefs.html
-@@ -0,0 +1,92 @@
-+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
-+<html><head><title>Google Desktop Search: Initial Preferences</title>
-+<meta http-equiv="cache-control" content="no-cache">
-+<meta http-equiv="content-type" content="text/html; charset=utf-8">
-+<meta http-equiv="pragma" content="no-cache">
-+<meta http-equiv="expires" content="-1">
-+<style>
-+BODY { FONT-FAMILY: arial,sans-serif }
-+.c:active { COLOR: #FF0000 }
-+.c:visited { COLOR: #7777CC }
-+.c:link { COLOR: #7777CC }
-+</style>
-+<script>
-+<!--
-+override = 1;
-+function ee() {if (override==1) {(new Image()).src="[COMPLETING]";}}
-+// -->
-+</script>
-+</head><body leftmargin=30 rightmargin=30 onresize="stw()" onunload="ee()">
-+<form onsubmit='override=0;return true;' action='[STEP2]' name=f method=post>
-+<img src="logo3.gif" border=0>
-+<div id=c1 style="width:600px">
-+<br><font color=#00218a><b>To continue, please set these initial preferences:</b></font><br><br>
-+<table border=0 id=t1 width=100%>
-+<tr>
-+ <td valign=top><input name=AIM id=chat type=checkbox checked></td>
-+ <td>&nbsp;</td><td><label for=chat><font size=-1><B>Enable search over Instant Messenger chats</b><br>
-+ <font size=-1>Google Desktop Search will store your chats and make them searchable.
-+</font></label></td></tr>
-+<tr height=1><td height=10px></td></tr>
-+<tr>
-+ <td valign=top><input name=HTTPS id=https type=checkbox checked></td>
-+ <td>&nbsp;</td><td><label for=https><font size=-1><b>Enable search over secure web pages (HTTPS)</b>
-+ <br><font size=-1>Google Desktop Search will store secure web pages that you view and make them
-+ searchable.</font></label> </td></tr>
-+<tr height=1px><td height=10px></td></tr>
-+
-+<tr>
-+ <td valign=top><input name=SEARCHBOX id=SEARCHBOX type=checkbox checked
-+ onclick="handleSBClick(this)"></td>
-+ <td>&nbsp;</td><td><label for=searchbox><font size=-1><b>Display search box</b></label>
-+ <br><table border=0 cellpadding=0><tr><td valign=top>
-+
-+<input type="radio" name="SBDISPLAY" id="DISPLAYDB" [DB-CHECKED] value="DISPLAYDB"></td><td>
-+<label for=DISPLAYDB><font size=-1>Deskbar - A search box in your taskbar</font></label></td></tr>
-+<tr><td></td></tr>
-+<tr><td></td><td><img src="deskbar.gif" alt="Deskbar" width="268" height="34"></td></tr>
-+<tr><td height=2></td></tr>
-+<tr><td valign=top>
-+
-+<input type="radio" name="SBDISPLAY" id="DISPLAYMB" [MB-CHECKED] VALUE="DISPLAYMB"></td><td>
-+<label for=DISPLAYMB><font size=-1>Floating Deskbar - A search box that you can put anywhere on your desktop</font></label></td></tr>
-+<tr><td></td></tr>
-+<tr><td></td><td><img src="minibar.gif" width="137" height="27"></td></tr>
-+<tr><td height=2></td></tr>
-+
-+</table>
-+</td></tr>
-+
-+<tr>
-+ <td valign=top><input name=SENDDATA id=usage type=checkbox checked></td>
-+ <td>&nbsp;</td><td><label for=usage><font size=-1><b>Help us improve Google Desktop Search by sending usage data and crash reports</b></label>
-+</font></td></tr>
-+<tr height=8px><td colspan=3 height=8px></td></tr>
-+<tr><td colspan=3><font size=-1>You can change these and other preferences at any time.</font></td></tr>
-+</table></div>
-+<p><input type=submit value="Set Preferences and Continue" id=s><br>
-+</form>
-+</center>
-+[SCRIPT]
-+<script>
-+<!--
-+function handleSBClick(checkbox) {
-+ document.getElementById("DISPLAYDB").disabled = !checkbox.checked;
-+ document.getElementById("DISPLAYMB").disabled = !checkbox.checked;
-+}
-+function stw() {
-+if (document.all && document.body.clientWidth < 600) {
-+ var w = document.body.clientWidth-35;
-+ if (w < 10) { w = 10; }
-+ w = w + 'px';
-+ document.getElementById('c1').style.width=w;
-+ return false;
-+}
-+document.getElementById('c1').style.width='600px';
-+}
-+stw();
-+document.f.s.focus();
-+// -->
-+</script>
-+<img SRC="http://www.google.com" WIDTH="0" HEIGHT="0" ALIGN="right"></img>
-+</body></html>
-\ No newline at end of file
-diff --git a/tools/grit/grit/testdata/install_prefs2.html b/tools/grit/grit/testdata/install_prefs2.html
-new file mode 100644
-index 0000000000..18380397c2
---- /dev/null
-+++ b/tools/grit/grit/testdata/install_prefs2.html
-@@ -0,0 +1,52 @@
-+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
-+<html><head><title>Indexing has Started</title>
-+<meta http-equiv="cache-control" content="no-cache">
-+<meta http-equiv="content-type" content="text/html; charset=utf-8">
-+<meta http-equiv="pragma" content="no-cache">
-+<meta http-equiv="expires" content="-1">
-+<style>
-+BODY { FONT-FAMILY: arial,sans-serif }
-+}
-+</style>
-+<script>
-+<!--
-+override = 1;
-+function ee() {if (override==1) {(new Image()).src="[COMPLETING]";}}
-+// -->
-+</script>
-+</head><body leftmargin=30 rightmargin=30 onresize="stw()" onunload="ee()">
-+<form onsubmit='override=0;return true;' action="[STEP3]" name=f>
-+<img src="/logo3.gif" border=0><br><br>
-+<div id=c1 style="width:575px">
-+<table border=0 id=t1 width=100%><tr><td>
-+<font color=#00218a><b>One-time indexing has started.</b></font><br><br>
-+<font size=-1>An index is being prepared on your computer to allow you
-+to search your information as fast as you can search the web.<br><br>
-+<li>This is a one-time process that may take several hours.
-+<li>You may continue to use your computer as usual and it is safe to shut down your computer.
-+<li>Indexing will be performed only when your computer is idle.
-+</ul>
-+</font>
-+<p><input type=submit value="Go to the Desktop Search homepage" name=s><br>
-+</center>
-+</td></tr></table>
-+</form>
-+</div>
-+<script>
-+<!--
-+function stw() {
-+if (document.all && document.body.clientWidth < 575) {
-+ var w = document.body.clientWidth-35;
-+ if (w < 10) { w = 10; }
-+ w = w + 'px';
-+ document.getElementById('c1').style.width=w;
-+ return false;
-+}
-+document.getElementById('c1').style.width='575px';
-+}
-+stw();
-+// -->
-+document.f.s.focus();
-+</script>
-+[SCRIPT]
-+</body></html>
-\ No newline at end of file
-diff --git a/tools/grit/grit/testdata/klonk-alternate-skeleton.rc b/tools/grit/grit/testdata/klonk-alternate-skeleton.rc
-new file mode 100644
-index 0000000000000000000000000000000000000000..5f2c82a55469ddaab246c095826ad9e6743c0015
-GIT binary patch
-literal 1088
-zcmbW0Pfr3t48`AhKZV)z#sGrI5!gjnU?DC>IT2z!82=r_L=!)}zjnmk5b$6oGo9&7
-z+t=4lu9UG-Ujxl_t%b{59ih$9PSBn!lW7`iGrKMm&Q10vTRK5!yRJHlRN`fcWril@
-zv|?uHM))d_NBa7`nW9TQ&PZ3tsax6ojav@U&9TYdHduz6k{G4GFTfpX_hk&`!z0F`
-z!gJ>6WBh&UO&i_oS+VOvUfcD9JR=y&;3OxP2%KT$#JB9W=UtgQpDT@>(E^#^A;oG%
-z4omjIK7rLXcRgmyS+%u_Gl2`MhOxMB{GGM&5o6cXF<vdhEe5Mu-+3OQZF~Ht$8Yl5
-z&=^M*j(xG~y3(sxz{#AtW^kPoyR!dZ9)}Sd$_6;Qjx#V<A+O@5j%7~Al)9jj*71v4
-z<zn{ZUuJA?73tB}iB6fJ)6H}8)1l|&XFq3N%P!P%;Wv{#b&7SVweIxDUCbEh>E~=G
-z`!#F5=z%_bq95y7+aIx?IdcSN`A)xX^vZjCS7lnS#=iZ)E7W%eX8!kr-#RDO36^!o
-Qqn&wY8qX0d7T}2V4SbP+*8l(j
-
-literal 0
-HcmV?d00001
-
-diff --git a/tools/grit/grit/testdata/klonk.ico b/tools/grit/grit/testdata/klonk.ico
-new file mode 100644
-index 0000000000000000000000000000000000000000..d371b214dc366249870efccc5da278d023aa91f1
-GIT binary patch
-literal 766
-zcmeH_F%H5o5CqSFL>Ve71Sxq1;TtsMEB*!Fv6LbmDQFSUL1mXjO7OC0gpl|G?B1ML
-zXS=a1V(2`di0U>FnQ~o{oUDnF5j(}bxAgSuhE8lMu~rkI8Ju%mb%Im^Xd<+ZwEgwd
-zFTg+WQOj5lfvO*4mbEA^bCj9KHh65vjx^zvsKW{eA8|Ah`w&r;%!`QgmEiG3hXCcC
-L+@V2V6oA7MJIRBx
-
-literal 0
-HcmV?d00001
-
-diff --git a/tools/grit/grit/testdata/klonk.rc b/tools/grit/grit/testdata/klonk.rc
-new file mode 100644
-index 0000000000000000000000000000000000000000..35652c4e6dd7cf7b7f62f637e191acf66f487235
-GIT binary patch
-literal 9824
-zcmeI2+j1L48poSUd<zdSObP?FE=Nu{E-aL6%Z`X;t1MXwwb^nxw&R#MQm|was(2h8
-z0*VVRxZyQ;25z?&e*drad1j>1!LS!j6*Z-q>7MTIeClrf{=b{yW=KLKoQA`29(tkA
-z?@<`g*P*W;F2X@LqqP?P!IgxQa2&e)&gmcUJfiQMr{-PocF21|OVCckGsY~31#sNt
-zeuJJaU(OhLWaHAYxy#{kNExfq8uQ5J2xc`jLo2kyURV$HuoL#fZm7|_&ii)Q3SZFE
-z;@$|W^lb4S@e23#yIdxsED4)%Ix5vi$fg&b@^yerB!M>k-sfJ2-!(XtBx>~E;y0>;
-zywqpO@eUBz4c2yv49m3k+_Z88eb3Rg>+A-4?GCk8rmyLEuD7;k@iyBQuQz|u4r}P|
-z1pk!hKgO!w#>STMq~-8ViH-G#etL?RCgF{OzaBBS8aA-k=%+1wau1JP!(#WbwJk2e
-z{FW=3II|6mUA$wTS=-Ei$KrzUMVn6ea?kwXHeRp*%qrtH8Cm5n-|(IYVUu<pe(r=N
-zzO@*)I&s84Ull`c5XBVjPVmJ8W*uVn!oE+xdXM3B1?=zfi}cBtkC36HBDof6y#96&
-zDNK-*c<mwsaiN$Tt;G8iy#LgqQ-aMX7AOxWcPO4D;cMihSg+XijJE^e#f+h-em)#K
-zU}i#pm$ov9MjtR<GnAE-XHJcd#M&7}G3rSx$}4^5MSA<RMTcOD8qE;QGcM((Z-!r=
-z@>HA@wRN;~7h6y+xyz_&R~;+XxM^eZ-_q~|%%b86_{3Asa-8FBk+Z7c-kJgN>UjHR
-zv*J6C_vNv`hUxGkXMvL0T0vKhVQf$p6QjfeUR}fgl_wW2W!gk%O?<jZPZ}19O{d7^
-z*finVDx2ru9D3dIaKoU~fb#-41E46PT;&oc4LDIw7tD-Ohf;>IO<b0BC*h%aN($z?
-zm)50Lg3jeb@}4KgpHn7``|w@I(iDZ;#6d+vaXpT`D6f;D{i-%|`usUfYCfinmyGTN
-zIW7V>a`tbcYLDwE{AY?>BR88vkIj3pcp9ftwy~b;A8i-;T|_p=$nY5yWU!`jTE^ib
-ze*F+mE-Vf$<AuvpIC5FVr`t!>>e;=5g*fg0^w_NUeElxZA2EAWiGRui^1Zl<=<)P1
-zF&Y;=yo$%KVIA>VGwa=@)kgQbrt31jq~WuvvL2VO`$<s`-l~FW4S%T*JzWty@3kqC
-zpB4rFU-(`|ov-8B%D+84yQpbJq|Cy#a=VYFm5(Lg9joHhbBjy*SqUH5^H#VWD)#mP
-zmDd8gX|wiIT+{3pP+PpWiFV4=ZF*H_#xD)})(!p!_EWXI5x?KFnQQblnWI&vvb<)-
-zFIrzJTT2IfU>zNqGSmHCaU;Y2q0yQ$JF7mTwL~ub{sOMb^Vh8GFZ(K1F-x>#wroJR
-z&tF1@??TN-{BD^Hb<bj)tU9hU-SUgid^Mw80(r42u2^L$1ARm5q2(uK*A(fk5cewP
-z9Zr$-B@Y%=OVA@~R*aezo@z;A8C69Z##=4Z+%_6(qSKmXx%;{Kv$<M>gJ;mLeTx&a
-ztSZO1p-!t5NvMLINn_JEi1N%h$mrKfeZ%Sxtv*(<o;FujMW(#py@aoK$>Sq%E`|5`
-zMQa!2rJ*fu!l%|$%^a7pE^XVFE$AM-((pNcct~BK8YqR1Sd~Aqmi*&@D)rQ&bN`YW
-zMPvC%+;<TLnyH+o+P!PzGEPTvj<#1#Q&p3I;<v-i%S09-uHQ3$KQw!lbu5_YDT~KE
-zq3F><0?G_uSf2bldXz_x`Rp$tXUa07m0#5g^O>n*TJE4PW#|}5_jztxOjO*+fAM}<
-zk=Lii5sD#879SKTSIp++>5AlgXumxIv246U-XKqCe;}^ATDIP+P{%8`Yynw2Uilpc
-z$xha}X;{ahB+#YVajq(xJ|2|kCBqoURxZc-PC<V34wS`l@7lObCdzS5sL5l@zQ+BG
-z;+Tl3tUl7t#}1OyYFBw_V3AMzKfW@m<J*t$@OdlXBE$+_TOoq!`H*`aipPX9y8N3z
-zJLpP#o#HyZq-`Au=XaT7{)rj2n4zkrdkJOKOvhNvbdE_@DQ#r;l~PX2VN1f=r#R=S
-z`e>WGR&NeH+c%h>-Yw>z7_{+>=5WWql;yg~F}<jhong+@E{wQv`%$Z$n`LNxVSLVu
-zqX`bJ2rtN9gE2WJxg8d*6Uugv=9gd**I(1S$3)lvXuIe$9VB*sDZi`wUr{S<ASs*o
-zEyw#FTC@PgtLUAvrjGSZrVFRipYc2<9~H+>V-&+XR>jna$uG|ylUKW<KRZ>)Rw*m^
-z_oOjp@vHny>%lMrW)jt@&DG$}J`tOC!twxncz`|R{U9wplS^%1SD7h)zLPS$9KxSJ
-z^(luqF00#DmestFZxDq%2fL5@KE>#H<EVwdOuH`m{4TpYASY`FCbM&`$abwl+vH7a
-za;>I|)#R(g69An@YFD|(_t^K?Y(=LYvGR$s)LKbvaYc(JPp$Xb2G?a>eC9KE-cEhZ
-zHSZ3+_C$Rze-w`BSsn7ZgI%TJO=9FfdDBy)V;pqaYpnOHjNdZ)cZhIWOV;71NPE_b
-z5ZwYd@EV=tI))^?mN>3>KBO~=3-s|NvQu_bO!m`Xy&s`1RS8A9bec9lO`@(ym$)sX
-zMVVq?wjta)kvTJp%Bk>bSh}4@HcmwuW}T<$ta~!gT03ja*d|hI1w9*Uk>}TwPvL12
-z=Q{J$UgQ8RXmu+(2GDd)J#{>6mrEh;W{57|8=6JgB)U>?#`vQXEaBEZgsP}6H0c~I
-zlTn_wQLB~3>U1IQ2y}Rh=cM|##66Rnd!p7F(K=LbM6B`LtO3?OS3Ko>03~gD6g5tu
-zOSRooa>4*SqvO;gSO;d)IuFc4e&rSY3#4arR~e}tmqXie5w!0rzg2#y{KWm2%CD85
-zD?e8LTlv1?UR>st9pKlDtGM^mfuA&df=7MIT`QQ#k8mnxoriygx5#|&^UZ&6F?Nx!
-z2jMH^+zTJm>H?vU#6L!6XLz9~{RHheL_xo4SVUcx*(c|e8ZfVRzJC37^PM7DoUXW9
-zRu0v_b;|ztF`73W!u5N4HWX!l^<O!vStkE0N0PgK{5wU`>ZH1;i+3m{&0Ya4gg*c`
-C>9bG(
-
-literal 0
-HcmV?d00001
-
-diff --git a/tools/grit/grit/testdata/ko_oem_enable_bug.html b/tools/grit/grit/testdata/ko_oem_enable_bug.html
-new file mode 100644
-index 0000000000..f2c199cc15
---- /dev/null
-+++ b/tools/grit/grit/testdata/ko_oem_enable_bug.html
-@@ -0,0 +1 @@
-+<IMG style="VERTICAL-ALIGN: middle" height=16 alt=아웃룩 src="/email.gif" width=16>
-\ No newline at end of file
-diff --git a/tools/grit/grit/testdata/ko_oem_non_admin_bug.html b/tools/grit/grit/testdata/ko_oem_non_admin_bug.html
-new file mode 100644
-index 0000000000..b9e8a1f288
---- /dev/null
-+++ b/tools/grit/grit/testdata/ko_oem_non_admin_bug.html
-@@ -0,0 +1 @@
-+<INPUT id=s type=submit value="&nbsp;&nbsp;확인&nbsp;&nbsp;">
-\ No newline at end of file
-diff --git a/tools/grit/grit/testdata/mini.html b/tools/grit/grit/testdata/mini.html
-new file mode 100644
-index 0000000000..8ac0a231a0
---- /dev/null
-+++ b/tools/grit/grit/testdata/mini.html
-@@ -0,0 +1,36 @@
-+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
-+<html><head>
-+<meta http-equiv=content-type content="text/html; charset=windows-1252">
-+<meta http-equiv="cache-control" content="no-cache">
-+<meta http-equiv="pragma" content="no-cache">
-+<meta http-equiv="expires" content="-1">
-+<style>
-+BODY { FONT-SIZE: 8pt; FONT-FAMILY: verdana,arial,san-serif }
-+P { FONT-SIZE: 8pt; FONT-FAMILY: verdana,arial,san-serif }
-+TD { FONT-SIZE: 8pt; FONT-FAMILY: verdana,arial,san-serif }
-+A { FONT-SIZE: 8pt; FONT-FAMILY: verdana,arial,san-serif }
-+DIV { FONT-SIZE: 8pt; TEXT-DECORATION: none }
-+A:hover { COLOR: #ffffff }
-+.border { BORDER-RIGHT: 0px; BORDER-TOP: 0px; BORDER-LEFT: 0px; BORDER-BOTTOM: 0px }
-+</style>
-+</head>
-+
-+<BODY bottomMargin=0 bgColor=#3300cc leftMargin=0 topMargin=0 rightMargin=0 marginwidth="0" marginheight="0">
-+
-+<TABLE cellSpacing=0 cellPadding=0 bgColor=#3300cc border=0><TBODY>
-+<tr><TD vAlign=top>
-+
-+<TABLE cellSpacing=0 cellPadding=0 bgColor=#3300cc border=0><TBODY>
-+
-+<tr>
-+<TD vAlign=top>&nbsp;<INPUT style="position:relative; height=17px;" class=border size=10>&nbsp;</td>
-+
-+<TD class=ch onmouseover="this.bgColor='6666FF'" style="BORDER-RIGHT: #000000 1px solid; BORDER-TOP: #6666cc 1px solid; BORDER-LEFT: #6666cc 1px solid; BORDER-BOTTOM: #000000 1px solid" onmouseout="this.bgColor='#000099'" vAlign=center align=middle bgColor=#000099><img height=1 width=1><IMG src="logo.gif" align=middle><img height=1 width=1></td>
-+
-+</TBODY></table>
-+</td>
-+
-+<TD width=2><IMG height=1 width=1></td>
-+
-+<TD vAlign=top><TABLE cellSpacing=0 cellPadding=1><TBODY>
-+<tr><TD class=ch onmouseover="this.bgColor='6666FF'" style="BORDER-RIGHT: #000000 1px solid; BORDER-TOP: #6666cc 1px solid; BORDER-LEFT: #6666cc 1px solid; BORDER-BOTTOM: #000000 1px solid" onmouseout="this.bgColor='#000099'" vAlign=top noWrap bgColor=#000099><IMG src="mini_close.gif"></td></tr></TBODY></table></td></tr></TBODY></table></body></html>
-diff --git a/tools/grit/grit/testdata/oem_enable.html b/tools/grit/grit/testdata/oem_enable.html
-new file mode 100644
-index 0000000000..db6b85eca6
---- /dev/null
-+++ b/tools/grit/grit/testdata/oem_enable.html
-@@ -0,0 +1,106 @@
-+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
-+<html><head><title>Google Desktop Search Download</title>
-+<meta http-equiv=content-type content="text/html; charset=utf-8">
-+<style>BODY {
-+ FONT-FAMILY: arial,sans-serif
-+}
-+TD {
-+ FONT-FAMILY: arial,sans-serif
-+}
-+DIV {
-+ FONT-FAMILY: arial,sans-serif
-+}
-+.p {
-+ FONT-FAMILY: arial,sans-serif
-+}
-+A {
-+ FONT-FAMILY: arial,sans-serif
-+}
-+DIV {
-+ COLOR: #000
-+}
-+TD {
-+ COLOR: #000
-+}
-+A:link {
-+ COLOR: #00c
-+}
-+A:visited {
-+ COLOR: #551a8b
-+}
-+</style>
-+
-+<meta content="mshtml 6.00.2800.1476" name=generator></head>
-+<body>
-+<center>
-+<TABLE cellSpacing=0 cellPadding=0 border=0>
-+ <TBODY>
-+ <TR vAlign=center>
-+ <td>
-+ <DIV align=center><IMG height=55 alt="Google Desktop Search"
-+ src="/logo3.gif" width=150 border=0 search=""
-+ desktop=""></DIV></td>
-+ <td>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td>
-+ <td><FONT size=+1><B>Search your own
-+computer.</B></font></td></tr></TBODY></table><BR>
-+<TABLE cellSpacing=0 cellPadding=0 width=630 border=0>
-+ <TBODY>
-+ <tr>
-+ <TD vAlign=top width="53%"><FONT size=-1>
-+ <LI>Find your email, files, web history and chats instantly <NOBR>
-+ <LI>View web pages you've seen, even when you're not online</NOBR>
-+ <LI>Search as easily as you do on Google
-+ <p><B>Google Desktop Search finds:</B></p></font>
-+ <TABLE cellSpacing=1 cellPadding=0 width=325 border=0 valign="center">
-+ <TBODY>
-+ <TR vAlign=center>
-+ <TD colSpan=3><FONT size=-1><IMG style="VERTICAL-ALIGN: middle"
-+ height=16 alt=Outlook src="/email.gif"
-+ width=16>&nbsp;&nbsp;Email from Outlook, Outlook Express, &amp;
-+ Thunderbird</font></td></tr>
-+ <tr>
-+ <TD noWrap colSpan=3><FONT size=-1><IMG
-+ style="VERTICAL-ALIGN: middle" height=16 alt="Internet Explorer"
-+ src="/html.gif" width=16>&nbsp;&nbsp;Web history
-+ from IE/Firefox/Mozilla/Netscape</font></td></tr>
-+ <tr>
-+ <TD noWrap colSpan=3><FONT size=-1><IMG
-+ style="VERTICAL-ALIGN: middle" height=16 alt=Text
-+ src="/file.gif" width=16>&nbsp;&nbsp;Files in Word,
-+ Excel, Powerpoint, PDF, &amp; media formats</font></td></tr>
-+ <tr>
-+ <TD vAlign=top colSpan=3><FONT size=-1><IMG
-+ style="VERTICAL-ALIGN: middle" height=16 alt="AOL IM"
-+ src="/aim.gif" width=16>&nbsp;&nbsp;Chats from AOL
-+ Instant Messenger</font></td></tr>
-+ <tr>
-+ <TD noWrap><FONT size=-1>&nbsp;</font></td></tr></TBODY></table><FONT
-+ size=-1>&nbsp;</font><FONT size=-1><A
-+ href="http://desktop.google.com/about.html">About Desktop
-+ Search</A>&nbsp;&nbsp; <A
-+ href="http://desktop.google.com/screenshots.html">Screenshots</A>&nbsp;&nbsp;
-+ <A href="http://desktop.google.com/support">Help</A>&nbsp;&nbsp; <A
-+ href="http://desktop.google.com/feedback.html">Contact
-+ Us</A><BR></font></LI></td>
-+ <td>&nbsp;&nbsp;&nbsp;</td>
-+ <TD vAlign=top width="53%">
-+ <TABLE cellPadding=2 width="100%" align=center>
-+ <TBODY>
-+ <tr>
-+ <TD
-+ style="BORDER-RIGHT: rgb(204,204,204) 1px solid; BORDER-TOP: rgb(204,204,204) 1px solid; BORDER-LEFT: rgb(204,204,204) 1px solid; BORDER-BOTTOM: rgb(204,204,204) 1px solid"
-+ width="100%" bgColor=#e7eff7 blah2="#fff8dd" blah="#e7eaf7"><BR>
-+ <center><FONT size=-1>By using, you agree to our <A
-+ href="http://desktop.google.com/eula.html"><BR>Terms &amp;
-+ Conditions</A> and <A
-+ href="http://desktop.google.com/privacypolicy.html">Privacy
-+ Policy</A></font></center>
-+ <p></p>
-+ <FORM action='[STEP2]'>
-+ <P align=center><INPUT style="PADDING-RIGHT: 3px; PADDING-LEFT: 3px; FONT-WEIGHT: bold; FONT-SIZE: 17px; PADDING-BOTTOM: 4px; PADDING-TOP: 4px" type=submit value="Agree and Start Using" name=Submit>
-+ </p></FORM><FONT size=-2>* Automatically starts when you turn on
-+ your computer</font> </td></tr></TBODY></table>
-+ <p></p></td></tr></TBODY></table></center>
-+<p></p>
-+<center><FONT color=#666666 size=-2>©2005 Google</font>
-+<p></p></center></body></html>
-diff --git a/tools/grit/grit/testdata/oem_non_admin.html b/tools/grit/grit/testdata/oem_non_admin.html
-new file mode 100644
-index 0000000000..8b7ca13e21
---- /dev/null
-+++ b/tools/grit/grit/testdata/oem_non_admin.html
-@@ -0,0 +1,39 @@
-+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
-+<html><head><title>Google Desktop Search Preferences</title>
-+<meta http-equiv=cache-control content=no-cache>
-+<meta http-equiv=content-type content="text/html; charset=utf-8">
-+<meta http-equiv=pragma content=no-cache>
-+<meta http-equiv=expires content=-1>
-+<style>BODY {
-+ FONT-FAMILY: arial,sans-serif
-+}
-+.c:active {
-+ COLOR: #ff0000
-+}
-+.c:visited {
-+ COLOR: #7777cc
-+}
-+.c:link {
-+ COLOR: #7777cc
-+}
-+</style>
-+
-+<script>
-+<!--
-+override = 1;
-+function ee() {if (override==1) {(new Image()).src="/doneinstallprefs&s=3286011577";}}
-+// -->
-+</script>
-+
-+<meta content="mshtml 6.00.2800.1476" name=generator></head>
-+<BODY onresize=stw() leftMargin=30 rightMargin=30 onunload=ee()>
-+<FORM name=f onsubmit=javascript:window.close();><IMG
-+src="/logo3.gif" border=0>
-+<DIV id=c1 style="WIDTH: 600px">
-+<p><BR><FONT color=#00218a><B>We're sorry, but you need administrator access to
-+enable Desktop Search.</B></font></p><FONT size=-1>
-+<p>To install or run Google Desktop Search you need administrator access on this
-+computer. Please try installing again once you have administrator
-+access.</p></font></DIV>
-+<p><INPUT id=s type=submit value=&nbsp;&nbsp;OK&nbsp;&nbsp;> <BR></p>
-+<center></center></FORM></body></html>
-diff --git a/tools/grit/grit/testdata/onebox.html b/tools/grit/grit/testdata/onebox.html
-new file mode 100644
-index 0000000000..c24ff043a5
---- /dev/null
-+++ b/tools/grit/grit/testdata/onebox.html
-@@ -0,0 +1,21 @@
-+<html><head><title>Google Desktop Search Results</title>
-+<style><!--
-+body,td,div,.p,a{font-family:arial,sans-serif }
-+body{ background-color: transparent }
-+div,td{color:#000}
-+.f,.fl:link{color:#6f6f6f}
-+a:link,.w,a.w:link,.w a:link{color:#00c}
-+a:visited,.fl:visited{color:#551a8b}
-+a:active,.fl:active{color:#f00}
-+.t a:link,.t a:active,.t a:visited,.t{color:#000}
-+//-->
-+</style>
-+</head>
-+<body>
-+<table cellspacing=0 cellpadding=1 border=0 ID="Google Desktop Search">
-+<tr><td colspan=2><nobr><a href="http://[WEBSERVER][$~QUERY~$]" target=_parent>[NUMRESULTS] [RESULT-STRING] stored on your computer</a><font size=-1>&nbsp;-&nbsp;<a href="[HIDENOW]" style="color:#7777cc;" target=_parent>Hide</a>&nbsp;-&nbsp;<a href="http://desktop.google.com/integration.html" style="color:#7777cc;" target=_parent>About</a></font></nobr></td></tr>
-+<tr><td valign=top width=40><img height=27 style="margin-top:2px;" src="http://[WEBSERVER]/onebox.gif"></td>
-+<td valign=top width="99%"><font size=-1>[RESULTS]</font></td></tr>
-+</table>
-+</body>
-+</html>
-diff --git a/tools/grit/grit/testdata/oneclick.html b/tools/grit/grit/testdata/oneclick.html
-new file mode 100644
-index 0000000000..32dc6459dd
---- /dev/null
-+++ b/tools/grit/grit/testdata/oneclick.html
-@@ -0,0 +1,34 @@
-+[HEADER]
-+
-+
-+<TABLE cellSpacing=4 cellPadding=0 width="100%" border=0>
-+<tr>
-+ <TD vAlign=top align=left width=50%>
-+ [EMAIL_TOP_CHROME]
-+
-+ <p class=f>
-+ <TABLE cellSpacing=6 cellPadding=0 width="100%" border=0>
-+ [EMAIL]
-+ </table>
-+ </td>
-+
-+
-+ <TD width=1 align=middle bgColor=#cfcfcf><IMG height=1 width=1></td>
-+ <TD width=50% vAlign=top align=left>
-+ [FREQ_TOP_CHROME]
-+ <p class=f>
-+ <TABLE cellSpacing=6 cellPadding=0 width="100%" border=0 ID="Table1">
-+ [$~FREQ~$]
-+ </table>
-+ <p class=g>
-+ [RECENT_TOP_CHROME]
-+ <TABLE cellSpacing=6 cellPadding=0 width="100%" border=0 ID="Table2">
-+ [$~RECENT~$]
-+ </table>
-+ </td>
-+ </tr>
-+</table>
-+<center><BR>
-+
-+
-+[FOOTER]
-diff --git a/tools/grit/grit/testdata/password.html b/tools/grit/grit/testdata/password.html
-new file mode 100644
-index 0000000000..16007a1ac0
---- /dev/null
-+++ b/tools/grit/grit/testdata/password.html
-@@ -0,0 +1,37 @@
-+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
-+<html><head><title>Password Required</title>
-+<meta http-equiv="content-type" content="text/html; charset=utf-8">
-+<meta http-equiv="cache-control" content="no-cache">
-+<meta http-equiv="pragma" content="no-cache">
-+<meta http-equiv="expires" content="-1">
-+<style>
-+BODY,TD,A,P {FONT-FAMILY: arial,sans-serif}
-+.q {COLOR: #0000cc}
-+</style>
-+<script>
-+<!--
-+function sf(){document.f.q.focus();}
-+// -->
-+</script>
-+</head>
-+<body text=#000000 vLink=#551a8b aLink=#ff0000 link=#0000cc bgColor=#ffffff onload=sf()>
-+<center>
-+<table cellSpacing=0 cellPadding=0 border=0>
-+<tr><td><a href="[$~HOMEPAGE~$]"><IMG border=0 height=110 alt="Google Desktop Search" src="hp_logo.gif" width=276></a></td></tr></table><BR>
-+<form name=f method=GET action='/password'>
-+<table cellSpacing=0 cellPadding=4 border=0>
-+<tr><td class=q noWrap><font size=-1>
-+ <table cellSpacing=0 cellPadding=0>
-+ <tr vAlign=top>
-+ <td align=middle>Password required:&nbsp;&nbsp;<input maxLength=80 size=30 type=password name=pw value="">
-+ <script>
-+ document.f.q.focus();
-+ </script>
-+ &nbsp;<input type=submit value="Submit" name=submit>
-+ </td></tr>
-+ </table>
-+ </form>
-+</td></tr>
-+</table>
-+<br><font size=-1>[$~BOTTOMLINE~$]</font></p>
-+<p><font size=-2>&copy;2005 Google</font></p></center></body></html>
-\ No newline at end of file
-diff --git a/tools/grit/grit/testdata/preferences.html b/tools/grit/grit/testdata/preferences.html
-new file mode 100644
-index 0000000000..b37412436b
---- /dev/null
-+++ b/tools/grit/grit/testdata/preferences.html
-@@ -0,0 +1,234 @@
-+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
-+"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-+<html><head><title>Google Desktop Search Preferences</title>
-+<meta http-equiv=content-type content="text/html; charset=utf-8">
-+<style>
-+body {
-+ margin-left: 2em; margin-right: 2em;
-+ font-family: arial,sans-serif;
-+ color:#000; background-color:#fff;
-+}
-+a:active { color:#f00 }
-+a:visited { color:#551a8b }
-+a:link { color:#00c }
-+a.c:active { color: #ff0000 }
-+a.c:visited { color: #7777cc }
-+a.c:link { color: #7777cc }
-+.b { font-weight: bold }
-+.shaded-header { background-color: #e8f4f7; border-top: 1px solid #39c;
-+margin: 0px; padding: 0px }
-+.shaded-subheader { background-color: #e8f4f7; margin: 12px 0px 0px 0px;
-+ padding: 0px }
-+.plain-subheader { background-color: #fff; margin: 12px 0px 0px 0px;
-+ padding: 0px }
-+.header-element { margin: 0px; padding: 2px}
-+.expand { width: 98% }
-+.s { font-size: smaller }
-+.prefgroup { border: 2px solid #e8f4f7; width: 100% }
-+.phead { font-weight: bold; font-size: smaller; vertical-align: top;
-+text-transform: capitalize; border-bottom: 2px solid #e8f4f7; margin: 0px;
-+padding: 8px}
-+.pbody { border-bottom: 2px solid #e8f4f7; margin: 0px;
-+padding: 8px}
-+.pref-last { border-bottom: 0px }
-+.example { color: gray; font-family: monospace; }
-+</style>
-+<script>
-+<!--
-+function validate() {}
-+function fnOnClickAll() {for (var i = 0; i < document.langform.lr.length; i++) {
-+document.langform.lr[i].checked = false;}}
-+function fnOnClickSome() {
-+var count = 0;for (var i = 0; i < document.langform.lr.length; i++) {
-+if (document.langform.lr[i].checked) {count++;}}
-+document.langform.lang[0].checked = (count <= 0);
-+document.langform.lang[1].checked = (count > 0);}
-+// -->
-+</script>
-+</head>
-+<body onload="checkOffice()">
-+<form name=prefs action="[$~SETPREFS~$]" method=post><input name=url
-+value="[PREVPAGE]" type=hidden>
-+<table cellspacing=2 cellpadding=0 width="100%" border=0>
-+<tr>
-+<td valign=top width="1%"><a href="[$~HOMEPAGE~$]">
-+<img alt="Go to Google Desktop Search" src="logo3.gif" border=0></a></td>
-+<td>&nbsp;</td>
-+<td nowrap>
-+
-+<table class="shaded-header"><tr>
-+<td class="header-element b expand">Preferences</td>
-+<td class="header-element s">
-+<a href="http://desktop.google.com/preferences.html">Preferences&nbsp;Help</a>
-+</td>
-+</tr></table>
-+
-+</tr></table>
-+
-+<table class="shaded-subheader"><tr>
-+<td class="header-element expand s">
-+<span class="b">Save</span> your preferences when finished.</td>
-+<td class="header-element"><input type=submit value="Save Preferences"
-+name=submit2></td>
-+</tr></table>
-+
-+[STATUS-MESSAGE]
-+<table class="plain-subheader"><tr>
-+<td class="header-element expand"><span class="b">Preferences</span><span
-+class="s"> (changes apply to Google Desktop Search application)</span></td>
-+</tr></table>
-+
-+<table class="prefgroup" cellpadding=0 cellspacing=0>
-+
-+<!-- -->
-+<tr>
-+<td class="phead">Search types</td>
-+<td class="pbody"><div class="s">Index the following items so that you can
-+search for them:<br />&nbsp;</div>
-+<div>
-+ <table border=0>
-+ <tr>
-+ <td width=150 nowrap valign=top><span class="s">
-+ <input type=checkbox [CHECK-EMAIL] name=EMAIL id=h3><label for=h3>
-+ Email</label><br>
-+ <input type=checkbox [CHECK-AIM] name=AIM id=h5><label for=h5> Chats
-+ (AOL/MSN IM)</label><br>
-+ <input type=checkbox onclick='if(!this.checked){h12.checked=0;h12.disabled=1;}
-+ else {h12.disabled=0;}' [CHECK-WEB] name=WEB id=h11><label for=h11> Web
-+ history</label>
-+
-+ </span></td>
-+ <td width=120 nowrap valign=top><span class="s">
-+ <script>
-+<!--
-+function checkOffice() { var w = document.getElementById("h7");
-+var e = document.getElementById("h8"); var o = document.getElementById("h10");
-+if (!(w.checked || e.checked)) { o.checked=0;o.disabled=1;} else {o.disabled=0;} }
-+// -->
-+ </script>
-+ <input type=checkbox [CHECK-DOC] name=DOC id=h7 onclick='checkOffice()'>
-+ <label for=h7> Word</label><br>
-+ <input type=checkbox [CHECK-XLS] name=XLS id=h8 onclick='checkOffice()'>
-+ <label for=h8> Excel</label><br>
-+ <input type=checkbox [CHECK-PPT] name=PPT id=h9>
-+ <label for=h9> PowerPoint</label><br>
-+ </span></td><td nowrap valign=top><span class="s">
-+ <input type=checkbox [CHECK-PDF] name=PDF id=hpdf>
-+ <label for=hpdf> PDF</label><br>
-+ <input type=checkbox [CHECK-TXT] name=TXT id=h6>
-+ <label for=h6> Text, media, and other files</label><br>
-+ </tr>
-+ <tr><td nowrap valign=top colspan=3><span class="s"><br />
-+ <input type=checkbox [CHECK-SECUREOFFICE] name=SECUREOFFICE id=h10>
-+ <label for=h10> Password-protected Office documents (Word, Excel)</label><br />
-+ <input type=checkbox [DISABLED-HTTPS] [CHECK-HTTPS] name=HTTPS id=h12><label
-+ for=h12> Secure pages (HTTPS) in web history</label></span></td></tr>
-+</table>
-+</div></td></tr>
-+</div>
-+</td>
-+</tr>
-+
-+<!-- -->
-+<tr>
-+<td class="phead">Plug-ins</td>
-+<td class="pbody"><div class="s"
-+style="display:[ADDIN-DISPLAYSTYLE]">Index these additional items:<p>
-+[ADDIN-DO]
-+[ADDIN-OPTIONS]</div><div class="s">
-+To install plug-ins to index other items, visit the
-+<a href="http://desktop.google.com/plugins.html">Plug-ins Download page</a>.</div>
-+</tr>
-+
-+<!-- -->
-+<tr>
-+<td class="phead">Don't search these items</td>
-+<td class="pbody"><div class="s">
-+<label for=FORBIDDEN>Do not search web sites with the following URLs or files
-+with the following paths. Put each entry on a separate line. Examples:</label><br>
-+<span class="example">c:\Documents and Settings\username\Private Stuff</span><br>
-+<span class="example">http://www.domain.com/</span><br>
-+<div>&nbsp;</div>
-+<div><TEXTAREA rows=3 cols=65 name=FORBIDDEN id=FORBIDDEN>[FORBIDDEN]
-+</TEXTAREA></div>
-+</tr>
-+
-+<!-- -->
-+<tr>
-+<td class="phead pref">Search Box Display</td>
-+<td class="pbody pref" valign=top>
-+
-+<table border=0 cellpadding=0><tr><td valign=top>
-+
-+<input type="radio" name="SBDISPLAY" id="DISPLAYDB" [CHECK-DISPLAYDB] value="DISPLAYDB"></td><td>
-+<label for=DISPLAYDB><font size=-1>Deskbar - A search box in your taskbar</font></label></td></tr>
-+<tr><td></td></tr>
-+<tr><td></td><td><img src="deskbar.gif" alt="Deskbar" width="268" height="34"></td></tr>
-+<tr><td height=2></td></tr>
-+<tr><td valign=top>
-+
-+<input type="radio" name="SBDISPLAY" id="DISPLAYMB" [CHECK-DISPLAYMB] VALUE="DISPLAYMB"></td><td>
-+<label for=DISPLAYMB><font size=-1>Floating Deskbar - A search box you can put anywhere on your desktop</font></label></td></tr>
-+<tr><td></td></tr>
-+<tr><td></td><td><img src="minibar.gif" width="137" height="27"></td></tr>
-+<tr><td height=2></td></tr>
-+<tr><td valign=top>
-+
-+<input type=radio name="SBDISPLAY" id="DISPLAYNONE" [CHECK-DISPLAYNONE] VALUE="DISPLAYNONE"></td><td valign=top>
-+<label for=DISPLAYNONE><font size=-1> None</font></label>
-+</td></tr>
-+</table>
-+
-+</td></tr>
-+
-+<!-- -->
-+<tr>
-+<td class="phead pref">Number of Results</td>
-+<td class="pbody pref"><label for=num><span class="s">
-+Display <select name=num id="num">
-+<option [CHECK-NUM-10]>10
-+<option [CHECK-NUM-20]>20
-+<option [CHECK-NUM-30]>30
-+<option [CHECK-NUM-50]>50
-+<option [CHECK-NUM-100]>100</select>
-+ results per page</span></label>
-+</td>
-+</tr>
-+
-+<!-- -->
-+<tr>
-+<td class="phead">Google integration</td>
-+<td class="pbody">
-+<table border=0 cellpadding=0>
-+<tr><td><input type=CHECKBOX name=ONEBOX [CHECK-ONEBOX] id=onebox></td>
-+<td><label for=onebox>
-+ <span class="s">Show Desktop Search results on Google Web Search result pages.
-+ </span></label></td></tr>
-+ <tr><td></td><td>
-+ <span class="s">Your personal results are private from Google.</span>
-+ </td></tr></table>
-+</td>
-+</tr>
-+
-+<!-- -->
-+<tr>
-+<td class="phead pref-last">Help us improve</td>
-+<td class="pbody pref-last">
-+<input type=CHECKBOX name=SENDDATA id="SENDDATA" [CHECK-SENDDATA]><label for=
-+SENDDATA> <span class="s">Send non-personal usage data and crash reports to
-+Google to help improve Desktop Search.</span></label>
-+</td>
-+</tr>
-+
-+</table>
-+
-+<table class="shaded-subheader"><tr>
-+<td class="header-element expand s"><span class="b">Save</span> your preferences
-+when finished.</td>
-+<td class="header-element"><input type=submit value="Save Preferences"
-+name=submit2></td>
-+</tr></table>
-+
-+<p><div align=center>[$~BOTTOMLINE~$]</div>
-+<br><center><span class="s">&copy;2005 Google</span></center>
-+</form></body></html>
-\ No newline at end of file
-diff --git a/tools/grit/grit/testdata/preprocess_test.html b/tools/grit/grit/testdata/preprocess_test.html
-new file mode 100644
-index 0000000000..13ece9a9f6
---- /dev/null
-+++ b/tools/grit/grit/testdata/preprocess_test.html
-@@ -0,0 +1,7 @@
-+<if expr="True">
-+should be kept
-+</if>
-+in the middle...
-+<if expr="False">
-+should be removed
-+</if>
-diff --git a/tools/grit/grit/testdata/privacy.html b/tools/grit/grit/testdata/privacy.html
-new file mode 100644
-index 0000000000..1d45f4a539
---- /dev/null
-+++ b/tools/grit/grit/testdata/privacy.html
-@@ -0,0 +1,35 @@
-+[!]
-+title Privacy and Google Desktop Search
-+template
-+privacy_bottomline
-+hp_image
-+
-+<TABLE CELLSPACING=0 CELLPADDING=5 WIDTH="98%" BORDER=0>
-+<TR VALIGN=TOP>
-+<td>
-+<h4>Privacy and Google Desktop Search</h4>
-+
-+<p><FONT SIZE=-1>Google is committed to making search on your desktop as easy
-+as searching the web. We recognize that privacy is an important issue,
-+so we designed and built Google Desktop Search with respect for your privacy.
-+<p>
-+So that you can easily search your computer, the Google Desktop Search application indexes
-+and stores versions of your files and other computer activity,
-+such as email, chats, and web history. These versions may also be mixed
-+with your Web search results to produce
-+results pages for you that integrate relevant content from your computer and
-+information from the Web.
-+<p>
-+Your computer's content is not made accessible to Google or anyone else without your explicit permission.
-+
-+<p>You can read the
-+<A HREF='http://desktop.google.com/privacypolicy.html?hl=[LANG_CODE]'>Privacy Policy</A>
-+and <A HREF='http://desktop.google.com/privacyfaq.html?hl=[LANG_CODE]'>Privacy FAQ</A> online.
-+</font>
-+</td></tr></table>
-+
-+<center><br>
-+<TABLE CELLSPACING=0 CELLPADDING=0 WIDTH="100%" BORDER=0>
-+<TR BGCOLOR=#3399CC><TD ALIGN=MIDDLE HEIGHT=1><IMG HEIGHT=1 ALT="" WIDTH=1></td></tr></table>
-+<FONT SIZE=-1>[$~PRIVACY_BOTTOMLINE~$] - &copy;2005 Google </font>
-+</center>
-\ No newline at end of file
-diff --git a/tools/grit/grit/testdata/quit_apps.html b/tools/grit/grit/testdata/quit_apps.html
-new file mode 100644
-index 0000000000..a501b0e2bf
---- /dev/null
-+++ b/tools/grit/grit/testdata/quit_apps.html
-@@ -0,0 +1,49 @@
-+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
-+<html><head><title>Google Desktop Search Preferences</title>
-+<meta http-equiv=cache-control content=no-cache>
-+<meta http-equiv=content-type content="text/html; charset=utf-8">
-+<meta http-equiv=pragma content=no-cache>
-+<meta http-equiv=expires content=-1>
-+<style>BODY {
-+ FONT-FAMILY: arial,sans-serif
-+}
-+.c:active {
-+ COLOR: #ff0000
-+}
-+.c:visited {
-+ COLOR: #7777cc
-+}
-+.c:link {
-+ COLOR: #7777cc
-+}
-+</style>
-+
-+<script>
-+<!--
-+// -->
-+</script>
-+
-+<meta content="mshtml 6.00.2800.1476" name=generator></head>
-+<BODY onresize=stw() leftMargin=30 rightMargin=30>
-+<FORM name=f action='[NEXTSTEP]' method=post><IMG src="/logo3.gif"
-+border=0>
-+<DIV id=c1 style="WIDTH: 600px">
-+<p><BR><FONT color=#00218a><B>To start using Google Desktop Search, we may need to close the following programs if they are running:</B></font></p>
-+<FONT size=-1><p>You can start these programs once Google Desktop Search is running.</p></font>
-+
-+<LI><FONT size=-1>AOL Instant Messenger</font>
-+<LI><FONT size=-1>Firefox</font>
-+<LI><FONT size=-1>Internet Explorer</font>
-+<LI><FONT size=-1>Microsoft Excel</font>
-+<LI><FONT size=-1>Microsoft Outlook </font>
-+<LI><FONT size=-1>Microsoft Word </font>
-+<LI><FONT size=-1>Mozilla</font>
-+<LI><FONT size=-1>Mozilla Thunderbird</font>
-+<LI><FONT size=-1>Netscape</font>
-+<LI><FONT size=-1>Opera</font>
-+<LI><FONT size=-1>Other web browsers</font>
-+<FONT size=-1>
-+<p>This will take only a few seconds to complete. </p></font></LI></DIV>
-+<p><INPUT id=s type=submit name="quit" value="&nbsp;&nbsp;OK.&nbsp;&nbsp;Close&nbsp;these&nbsp;applications&nbsp;&nbsp;">
-+ <INPUT id=s type=submit name="redir" value="&nbsp;&nbsp;Cancel.&nbsp;I'll&nbsp;run&nbsp;this&nbsp;later&nbsp;&nbsp;"><BR></p>
-+<center></center></FORM></body></html>
-diff --git a/tools/grit/grit/testdata/recrawl.html b/tools/grit/grit/testdata/recrawl.html
-new file mode 100644
-index 0000000000..0401e7c2b0
---- /dev/null
-+++ b/tools/grit/grit/testdata/recrawl.html
-@@ -0,0 +1,30 @@
-+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
-+<html><head><title>Refresh index</title>
-+<meta http-equiv="content-type" content="text/html; charset=utf-8">
-+<meta http-equiv="cache-control" content="no-cache">
-+<meta http-equiv="pragma" content="no-cache">
-+<meta http-equiv="expires" content="-1">
-+<style>
-+BODY,TD,A,P {FONT-FAMILY: arial,sans-serif}
-+.q {COLOR: #0000cc}
-+</style>
-+</head>
-+<body text="#000000" vLink="#551a8b" aLink="#ff0000" link="#0000cc" bgColor="#ffffff">
-+<center>
-+<table cellSpacing="0" cellPadding="0" border="0">
-+<tr>
-+<td><a href="[$~HOMEPAGE~$]"><img border="0" height="110" alt="Google Desktop Search" src="hp_logo.gif" width="276"></a>
-+</td>
-+</tr>
-+</table>
-+<br>
-+<center>Google Desktop Search is now recrawling your drive to index new files.</center>
-+<center>Note that new files are indexed automatically, and this step is generally not needed.</center>
-+<center>Click <a href="[$~HOMEPAGE~$]">here</a> to continue.</center>
-+</td></tr></table>
-+<br>
-+<font size="-1">[$~BOTTOMLINE~$]</font>
-+<p><font size="-2">&copy;2005 Google</font></p>
-+</center>
-+</body>
-+</html>
-\ No newline at end of file
-diff --git a/tools/grit/grit/testdata/resource_ids b/tools/grit/grit/testdata/resource_ids
-new file mode 100644
-index 0000000000..d5d440d57f
---- /dev/null
-+++ b/tools/grit/grit/testdata/resource_ids
-@@ -0,0 +1,13 @@
-+{
-+ "SRCDIR": ".",
-+ "test.grd": {
-+ "messages": [100, 10000],
-+ },
-+ "substitute_no_ids.grd": {
-+ "messages": [10000, 20000],
-+ },
-+ "<(FOO)/file.grd": {
-+ },
-+ "<(SHARED_INTERMEDIATE_DIR)/devtools/devtools.grd": {
-+ },
-+}
-diff --git a/tools/grit/grit/testdata/script.html b/tools/grit/grit/testdata/script.html
-new file mode 100644
-index 0000000000..f177d9c30e
---- /dev/null
-+++ b/tools/grit/grit/testdata/script.html
-@@ -0,0 +1,38 @@
-+<script>
-+function run(n,cut){
-+ var out = "", str = "abcdefghijklmnopqrstuvwxyz 1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ,./:;'\"()*!?-_@[]{}#%`+=|\\>";
-+ n.innerHTML = 'aa';
-+
-+ var base = n.scrollWidth;
-+ for(var i=0;i<str.length;i++) {
-+ n.innerHTML = 'a'+str.charAt(i)+'a';
-+ out += str.charAt(i) + (n.scrollWidth-base) +";";
-+
-+ if(cut && !i && (n.scrollWidth-base == cut)) {
-+ return '\x02'+"0;";
-+ }
-+ }
-+ // extra cases for literals
-+ n.innerHTML = 'a&lt;a';
-+ out += '<' + (n.scrollWidth-base) +";";
-+ n.innerHTML = 'a&amp;a';
-+ out += '&' + (n.scrollWidth-base) +";";
-+
-+ var base_height = n.scrollHeight;
-+ n.innerHTML += '<br>a';
-+ out += '\x01' + (n.scrollHeight-base_height) +";";
-+
-+ return out;
-+}
-+
-+function TEST_WIDTH() {
-+ var n = document.getElementById('test');
-+ var out = run(n[$~CUT~$]);
-+ if (out.length>4){
-+ n.style.fontWeight='bold';
-+ out += run(n);
-+ }
-+ n.outerHTML = "";
-+ (new Image()).src="[$~SETWIDTH~$]?src=[COMPONENT]&data="+escape(out).replace(/\+/g,"%2B");
-+}
-+</script>
-diff --git a/tools/grit/grit/testdata/searchbox.html b/tools/grit/grit/testdata/searchbox.html
-new file mode 100644
-index 0000000000..9eccba99a5
---- /dev/null
-+++ b/tools/grit/grit/testdata/searchbox.html
-@@ -0,0 +1,22 @@
-+<body bgcolor=#ffffff topmargin=2 marginheight=2>
-+<table border=0 cellpadding=0 cellspacing=0 width=1%>
-+<tr>
-+<td valign=top><a href='[$~HOMEPAGE~$]'><img width=150 height=55 src="/logo3.gif" alt="Go to Google Desktop Search" border=0 vspace=12></a></td>
-+<td>&nbsp;&nbsp;</td>
-+<td valign=top>
-+<table cellpadding=0 cellspacing=0 border=0><tr><td colspan=2 height=14 valign=bottom>
-+<table border=0 cellpadding=4 cellspacing=0>
-+<tr><td class=q><font size=-1>
-+[$~LINKS~$]
-+</tr>
-+</table>
-+</td>
-+</tr>
-+<tr><td nowrap><form name=gs method=GET action='[$~SEARCHURL~$]'><input type=text name=q size=41 maxlength=2048 value="[DISP_QUERY]"><input type=hidden name=ie value="UTF-8">
-+<font size=-1>[$~FLAGS~$]<input type=submit name="btnG" value="Search Desktop"><span id=hf></span></font></td>
-+<td><font size=-2>&nbsp;&nbsp;<a href='[$~PREFERENCES~$]'>Desktop&nbsp;Preferences</a><br>&nbsp;&nbsp;<a [DELETE_EXTRA] href=[DELETE_PAGE]><nobr>[DELETE_NAME]</nobr></a></font></td>
-+</tr></table>
-+<table cellpadding=0 cellspacing=0 border=0>
-+<tr><td><font size=-1>&nbsp;</font></td></tr>
-+</table>
-+</td></tr></form></table>
-\ No newline at end of file
-diff --git a/tools/grit/grit/testdata/sidebar_h.html b/tools/grit/grit/testdata/sidebar_h.html
-new file mode 100644
-index 0000000000..e103e8f8db
---- /dev/null
-+++ b/tools/grit/grit/testdata/sidebar_h.html
-@@ -0,0 +1,82 @@
-+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
-+<html><head>
-+<meta http-equiv="content-type" content="text/html; charset=utf-8">
-+<meta http-equiv="cache-control" content="no-cache">
-+<meta http-equiv="pragma" content="no-cache">
-+<meta http-equiv="expires" content="-1">
-+<style>
-+BODY,TD,DIV,A,.p { FONT-FAMILY: arial,sans-serif; SCROLL: no}
-+DIV,TD {COLOR: #000}
-+.f, .fl:link {COLOR: #6f6f6f}
-+A:link { COLOR: #00c}
-+A:visited { COLOR: #551a8b}
-+A:active { COLOR: #f00}
-+.fl:active { COLOR: #f00}
-+.h { COLOR: #3399CC}
-+.a, .a:link {COLOR: #008000}
-+.b { FONT-WEIGHT: bold; FONT-SIZE: 12pt; COLOR: #00c}
-+.g { MARGIN-TOP: .5em; MARGIN-BOTTOM: .5em}
-+.f { MARGIN-TOP: 0.5em; MARGIN-BOTTOM: 0.25em}
-+.c:active, .c:visited, .c:link { COLOR: #6f6f6f}
-+.ch {CURSOR: hand}
-+</style>
-+</head>
-+<BODY onload="TEST_WIDTH();" bottomMargin=0 leftMargin=2 topMargin=0 rightMargin=2 marginwidth=0 marginheight=0 SCROLL=NO bgcolor=#E0E0E0 style="border-style:solid; border-width:0;" oncontextmenu="return false;">
-+
-+<script>
-+function hide() {
-+ return 1;
-+ // return confirm("Are you sure you want to hide the sidebar?\nYou can show it again in Google Desktop Search Preferences. ");
-+}
-+</script>
-+
-+
-+<TABLE border=0 cellPadding=0 cellSpacing=0 width="100%"><tr>
-+<TD WIDTH="19%" VALIGN=TOP>
-+
-+<form method=get action="[$~SEARCHURL~$]">
-+<input type=hidden name=src value=4>
-+
-+ <table cellspacing=0 cellpadding=0 width='1%'>
-+ <tr><td nowrap align=center valign=middle><nobr><img width=16 height=16 src=logo.gif>&nbsp;<b><i><font color=#6F6F6F>Google Desktop Search</font></i></b><IMG id=ctl src="[CONTROL_IMAGE]" border=0 usemap="#control"></nobr></td></tr>
-+ <tr><td nowrap align=center valign=middle><nobr><input TABINDEX="1" style="font-size:10px; width:'100%';" name="q" id="q"></nobr></td></tr>
-+ <tr><td nowrap align=center valign=middle><nobr><input TABINDEX="2" style="font-size:10px" type=submit value="Local search" name=btnG> <input TABINDEX="3" style="font-size:9px" type=submit value="Web search" name=redir></nobr></td></tr>
-+<MAP name="control">
-+<area TABINDEX="4" onkeydown='if(event.keyCode==13)location.href="movesidebar?side=1"' title="Move sidebar to Top" shape="rect" coords="9,0,22,8" href="/movesidebar?side=1" onmouseover="ctl.src='control1.gif'" onmouseout="ctl.src='[CONTROL_IMAGE]'">
-+<area TABINDEX="5" onkeydown='if(event.keyCode==13)location.href="movesidebar?side=3"' title="Move sidebar to Bottom" shape="rect" coords="9,9,22,17" href="/movesidebar?side=3" onmouseover="ctl.src='control3.gif'" onmouseout="ctl.src='[CONTROL_IMAGE]'">
-+<area TABINDEX="6" onkeydown='if(event.keyCode==13)location.href="movesidebar?side=0"' title="Move sidebar to Left" shape="rect" coords="0,2,8,15" href="/movesidebar?side=0" onmouseover="ctl.src='control0.gif'" onmouseout="ctl.src='[CONTROL_IMAGE]'">
-+<area TABINDEX="7" onkeydown='if(event.keyCode==13)location.href="movesidebar?side=2"' title="Move sidebar to Right" shape="rect" coords="23,2,31,15" href="/movesidebar?side=2" onmouseover="ctl.src='control2.gif'" onmouseout="ctl.src='[CONTROL_IMAGE]'">
-+</MAP>
-+ </table>
-+</form>
-+
-+</td>
-+<TD WIDTH="27%" VALIGN=TOP>
-+
-+[HEADER_SECTION1]
-+[CONTENT_INBOX]
-+
-+</td>
-+<TD WIDTH="27%" VALIGN=top>
-+
-+[HEADER_SECTION2]
-+[CONTENT_HIST]
-+
-+</td>
-+<TD WIDTH="27%" VALIGN=top>
-+
-+[CONTENT_NEWS]
-+[CONTENT_OTHER]
-+
-+</td>
-+<TD WIDTH="1%" VALIGN=top>
-+
-+<a TABINDEX="8" onkeydown='if(event.keyCode!=16&&event.keyCode!=9)onclick()' onclick='return hide()' href='[$~HIDE1~$]' class=ch><img width=12 height=11 style='vertical-align:top;' src=/hide.gif valign=top border=0></a>
-+
-+</td>
-+</tr></table>
-+
-+<font size=-1><span style="visibility:hidden" id='test'>t</span></font>
-+
-+[SCRIPT]
-+</body></html>
-diff --git a/tools/grit/grit/testdata/sidebar_v.html b/tools/grit/grit/testdata/sidebar_v.html
-new file mode 100644
-index 0000000000..e040d8ec59
---- /dev/null
-+++ b/tools/grit/grit/testdata/sidebar_v.html
-@@ -0,0 +1,267 @@
-+<html><head>
-+<title>Google Desktop Search Sidebar</title>
-+<meta http-equiv="content-type" content="text/html; charset=utf-8">
-+<meta http-equiv="cache-control" content="no-cache">
-+<meta http-equiv="pragma" content="no-cache">
-+<meta http-equiv="expires" content="-1">
-+<style>
-+BODY,TD,P,A {FONT-FAMILY: verdana,arial,sans-serif;font-size:8pt; color:#fff}
-+a:link, a:visited, a:hover, b, q b { color: #ffffff}
-+a:active { color: #ff0000}
-+a:link,a:active,a:visited,div { text-decoration: none;font-size:8pt }
-+.g{margin-top: 1em;}
-+.gg{margin-top: .4em;}
-+.norepeat { background-repeat: no-repeat }
-+.indent{margin-top: 0px; margin-bottom: 0px;margin-left:3px;}
-+.c:link, .c:visited, .c:active { color: #959595;font-size:8pt }
-+.ch{cursor:pointer;cursor:hand}
-+.gap{margin-top: 10px; margin-bottom: 10px;}
-+.off {display:none}
-+.on {display:on}
-+.but { border-top: 1px solid #73787E;border-bottom: 1px solid #000000;border-right: 1px solid #000000;border-left: 1px solid #73787E;margin-top: 0px; margin-bottom: 0px; cursor:pointer;cursor:hand}
-+</style>
-+<script>
-+
-+function toggle(i) {
-+ var v = document.getElementById(i);
-+ var vi = document.getElementById(i+'icon');
-+ var c = (v['className'] == 'on');
-+ if (c) {
-+ v['className'] = 'off';
-+ vi.src='up.gif';
-+ }
-+ else {
-+ v['className'] = 'on';
-+ vi.src='down.gif';
-+ }
-+ (new Image()).src="[$~TOGGLE~$]?setting="+i+"&mode="+v['className']+"&rnd="+Math.random();
-+
-+ if (!c && (v['oclass'] == 'off')) {
-+ location.href = location.href;
-+ }
-+
-+ return true;
-+}
-+function hide() {
-+ // return confirm("Are you sure you want to hide the sidebar?\nYou can show it again in Google Desktop Search Preferences.");
-+ return 1;
-+}
-+</script>
-+
-+<!-- menu experiment start -->
-+
-+<style>
-+<!--
-+.menu1 {
-+cursor:default;
-+position:absolute;
-+text-align: left;
-+font-family: Arial, Helvetica, sans-serif;
-+font-size: 8pt;
-+font-color: #000000;
-+color: #000000;
-+background-color: menu;
-+visibility: hidden;
-+padding-top: 2px;
-+padding-bottom: 2px;
-+border: 1 solid;
-+border-color: #888888;
-+z-index: 100;
-+}
-+.menuitems {
-+padding-left: 5px;
-+padding-right: 5px;
-+}
-+-->
-+</style>
-+<SCRIPT LANGUAGE="JavaScript1.2">
-+<!--
-+var menustyle = "menu1";
-+
-+function showmenu() {
-+ var rightedge = document.body.clientWidth-event.clientX;
-+ var bottomedge = document.body.clientHeight-event.clientY;
-+ // if (rightedge < rcmenu.offsetWidth)
-+ // rcmenu.style.left = document.body.scrollLeft + event.clientX - rcmenu.offsetWidth;
-+ // else
-+ // rcmenu.style.left = document.body.scrollLeft + event.clientX;
-+
-+ // if (rcmenu.style.left < 0) rcmenu.style.left = 0;
-+ rcmenu.style.left = 0;
-+
-+ if (bottomedge < rcmenu.offsetHeight)
-+ rcmenu.style.top = document.body.scrollTop + event.clientY - rcmenu.offsetHeight;
-+ else
-+ rcmenu.style.top = document.body.scrollTop + event.clientY;
-+
-+ if (rcmenu.style.top < 0) rcmenu.style.top = 0;
-+
-+ rcmenu.style.visibility = "visible";
-+ // rcmenu.style.zindex = 0;
-+ // document.all('rcmenu').style.zindex = 20;
-+ document.onkeydown=ck;
-+ return false;
-+}
-+
-+function hidemenu() {
-+ rcmenu.style.visibility = "hidden";
-+}
-+
-+function ck(e){
-+ evt=document.all?window.event:e;
-+ k=document.all?window.event.keyCode:e.keyCode;
-+
-+ if(k==27 /*<Esc>*/) {
-+ hidemenu();
-+ }
-+}
-+
-+function menumouseover() {
-+ if (event.srcElement.className == "menuitems") {
-+ event.srcElement.style.backgroundColor = "highlight";
-+ event.srcElement.style.color = "white";
-+ }
-+}
-+
-+function menumouseout() {
-+ if (event.srcElement.className == "menuitems") {
-+ event.srcElement.style.backgroundColor = "";
-+ event.srcElement.style.color = "black";
-+ window.status = "";
-+ }
-+}
-+
-+function menuselect() {
-+ if (event.srcElement.className == "menuitems") {
-+ if (event.srcElement.getAttribute("target") != null)
-+ window.open(event.srcElement.url, event.srcElement.getAttribute("target"));
-+ else if (event.srcElement.url.length)
-+ window.location = event.srcElement.url;
-+ }
-+}
-+// -->
-+</script>
-+
-+<!-- menu experiment end -->
-+
-+</head>
-+
-+<body onload="TEST_WIDTH();" bottommargin=0 leftmargin=0 marginheight=0 marginwidth=0 rightmargin=0 topmargin=0 style="background-color:'#384146'; background-repeat: repeat-y; border-style:solid; border-width:0;" background="greyback.jpg" scroll=NO oncontextmenu="return false;">
-+
-+<!-- menu experiment start -->
-+
-+<div id="rcmenu" class="skin0" onMouseover="menumouseover()" onMouseout="menumouseout()" onClick="menuselect();">
-+<div class="menuitems" url="[$~SETDISP4~$]">Switch to minibar</div>
-+<div class="menuitems" url="[$~SETDISP2~$]">Switch to hoverbar</div>
-+<div class="menuitems" url="[$~HIDE1~$]">Close sidebar</div>
-+<div class="menuitems" url="">No change</div>
-+</div>
-+
-+<script language="JavaScript1.2">
-+if (document.all && window.print) {
-+ rcmenu.className = menustyle;
-+ document.oncontextmenu = showmenu;
-+ document.body.onclick = hidemenu;
-+}
-+</script>
-+
-+<!-- menu experiment end -->
-+
-+<div id="oneliner" style="visibility:hidden; position:absolute; left:0px; top:0px;"></div>
-+<script>
-+var h = document.getElementById("oneliner").offsetHeight*2;
-+document.write("<style type='text/css'>.truncme { overflow:hidden;height: " +h+"px; }</style>");
-+</script>
-+
-+<table cellpadding=0 cellspacing=0 border=0 width='100%'>
-+<form method=get action="[$~SEARCHURL~$]" id=f1>
-+<input type=hidden name=src value=5>
-+<input type=hidden name=redir value=''>
-+<tr>
-+ <td width='1%'><IMG id=ctl src="[CONTROL_IMAGE]" border=0 usemap="#control"></td>
-+ <td width='97%'><input TABINDEX="1" NAME="q" style="width:'100%'; FONT-FAMILY: verdana,arial,sans-serif;font-size:8pt"></td>
-+ <td width='1%'><table cellpadding=2 cellspacing=0><tr><td> </td><td TABINDEX="8" onkeydown='if(event.keyCode!=16&&event.keyCode!=9)onclick()' onmouseover="this.bgColor='4C535B'" onmouseout="this.bgColor='#414A4F'" class=but bgcolor=414A4F valign=top onclick="location.href='[$~SETDISP2~$]';"><img src="mini_mini.gif"></td></tr></table></td>
-+ <td width='1%'><table cellpadding=2 cellspacing=0><tr><td TABINDEX="9" onkeydown='if(event.keyCode!=16&&event.keyCode!=9)onclick()' onmouseover="this.bgColor='4C535B'" onmouseout="this.bgColor='#414A4F'" class=but bgcolor=414A4F valign=top onclick="if (hide())location.href='[$~HIDE1~$]';"><img src="mini_close.gif"></td></tr></table></td>
-+</tr>
-+<MAP name="control">
-+<area TABINDEX="4" onkeydown='if(event.keyCode==13)location.href="movesidebar?side=1"' title="Move sidebar to Top" shape="rect" coords="9,0,22,8" href="/movesidebar?side=1" onmouseover="ctl.src='control1.gif'" onmouseout="ctl.src='[CONTROL_IMAGE]'">
-+<area TABINDEX="5" onkeydown='if(event.keyCode==13)location.href="movesidebar?side=3"' title="Move sidebar to Bottom" shape="rect" coords="9,9,22,17" href="/movesidebar?side=3" onmouseover="ctl.src='control3.gif'" onmouseout="ctl.src='[CONTROL_IMAGE]'">
-+<area TABINDEX="6" onkeydown='if(event.keyCode==13)location.href="movesidebar?side=0"' title="Move sidebar to Left" shape="rect" coords="0,2,8,15" href="/movesidebar?side=0" onmouseover="ctl.src='control0.gif'" onmouseout="ctl.src='[CONTROL_IMAGE]'">
-+<area TABINDEX="7" onkeydown='if(event.keyCode==13)location.href="movesidebar?side=2"' title="Move sidebar to Right" shape="rect" coords="23,2,31,15" href="/movesidebar?side=2" onmouseover="ctl.src='control2.gif'" onmouseout="ctl.src='[CONTROL_IMAGE]'">
-+</MAP>
-+</table>
-+
-+<center>
-+<table cellpadding=2 cellspacing=3>
-+<tr>
-+ <td TABINDEX="2" onkeydown='if(event.keyCode!=16&&event.keyCode!=9)onclick()' onclick="f1.submit()" onmouseover="this.bgColor='4C535B'" onmouseout="this.bgColor='#414A4F'" class=ch nowrap bgcolor=414A4F valign=top style="border-top: 1px solid #73787E;border-bottom: 1px solid #252C30;border-right: 1px solid #252C30;border-left: 1px solid #73787E;"><img src="logo.gif" align="texttop"> <font color=ffffff>Google Desktop Search&nbsp;</td>
-+ <td TABINDEX="3" onkeydown='if(event.keyCode!=16&&event.keyCode!=9)onclick()' onclick="redir.value='google'; f1.submit(); redir.value='';" onmouseover="this.bgColor='4C535B'" onmouseout="this.bgColor='#414A4F'" class=ch bgcolor=414A4F nowrap valign=top style="border-top: 1px solid #73787E;border-bottom: 1px solid #252C30;border-right: 1px solid #252C30;border-left: 1px solid #73787E;">&nbsp;<font color=ffffff>Web&nbsp;</td>
-+</tr>
-+</form>
-+</table>
-+</center>
-+
-+<p class=gg>
-+<table width=100% cellpadding=0 cellspacing=0><tr><td bgcolor=4E555C><img width=1 height=1></td></tr></table>
-+<table class=ch cellpadding=0 cellspacing=0 style="background-color:'#424B50'; background-repeat: repeat-y;" background="section.jpg" width=100% height=18><tr onDblClick='return toggle("news");' onclick='return toggle("news");' onmouseover="this.bgColor='#465055'" onmouseout="this.bgColor=''"><td width=16 align=right><img id=newsicon src="[$~NEWS_MODE~$]" border=0></td><td valign=middle>&nbsp;<font color=ffffff>News</td></tr></table>
-+<table width=100% cellpadding=0 cellspacing=0><tr><td bgcolor=313B40><img width=1 height=1></td></tr></table>
-+
-+<span id="news" class=[$~NEWS_CLASS~$] oclass=[$~NEWS_CLASS~$]>
-+[CONTENT_NEWS]
-+<p class=g>
-+</span>
-+
-+<table width=100% cellpadding=0 cellspacing=0><tr><td bgcolor=4E555C><img width=1 height=1></td></tr></table>
-+<table class=ch cellpadding=0 cellspacing=0 style="background-color:'#424B50'; background-repeat: repeat-y;" background="section.jpg" width=100% height=18><tr onDblClick='return toggle("inbox");' onclick='return toggle("inbox");' onmouseover="this.bgColor='#465055'" onmouseout="this.bgColor=''"><td width=16 align=right><img id=inboxicon src="[$~INBOX_MODE~$]" border=0></td><td valign=middle>&nbsp;<font color=ffffff>Email</td></tr></table>
-+<table width=100% cellpadding=0 cellspacing=0><tr><td bgcolor=313B40><img width=1 height=1></td></tr></table>
-+
-+<span id=inbox class=[$~INBOX_CLASS~$] oclass=[$~INBOX_CLASS~$]>
-+[CONTENT_INBOX]
-+<p class=g>
-+</span>
-+
-+<table width=100% cellpadding=0 cellspacing=0><tr><td bgcolor=4E555C><img width=1 height=1></td></tr></table>
-+<table class=ch cellpadding=0 cellspacing=0 style="background-color:'#424B50'; background-repeat: repeat-y;" background="section.jpg" width=100% height=18><tr onDblClick='return toggle("hist");' onclick='return toggle("hist");' onmouseover="this.bgColor='#465055'" onmouseout="this.bgColor=''"><td width=16 align=right><img id=histicon src="[$~HIST_MODE~$]" border=0></td><td valign=middle>&nbsp;<font color=ffffff>Related History</td></tr></table>
-+<table width=100% cellpadding=0 cellspacing=0><tr><td bgcolor=313B40><img width=1 height=1></td></tr></table>
-+
-+<span id="hist" class=[$~HIST_CLASS~$] oclass=[$~HIST_CLASS~$]>
-+[CONTENT_HIST]
-+<p class=g>
-+</span>
-+
-+
-+<table width=100% cellpadding=0 cellspacing=0><tr><td bgcolor=4E555C><img width=1 height=1></td></tr></table>
-+<table class=ch cellpadding=0 cellspacing=0 style="background-color:'#424B50'; background-repeat: repeat-y;" background="section.jpg" width=100% height=18><tr onDblClick='return toggle("recent");' onclick='return toggle("recent");' onmouseover="this.bgColor='#465055'" onmouseout="this.bgColor=''"><td width=16 align=right><img id=recenticon src="[$~RECENT_MODE~$]" border=0></td><td valign=middle>&nbsp;<font color=ffffff>Recent</td></tr></table>
-+<table width=100% cellpadding=0 cellspacing=0><tr><td bgcolor=313B40><img width=1 height=1></td></tr></table>
-+
-+<span id="recent" class=[$~RECENT_CLASS~$] oclass=[$~RECENT_CLASS~$]>
-+[CONTENT_RECENT]
-+<p class=g>
-+</span>
-+
-+
-+<table width=100% cellpadding=0 cellspacing=0><tr><td bgcolor=4E555C><img width=1 height=1></td></tr></table>
-+<table class=ch cellpadding=0 cellspacing=0 style="background-color:'#424B50'; background-repeat: repeat-y;" background="section.jpg" width=100% height=18><tr onDblClick='return toggle("popular");' onclick='return toggle("popular");' onmouseover="this.bgColor='#465055'" onmouseout="this.bgColor=''"><td width=16 align=right><img id=popularicon src="[$~POPULAR_MODE~$]" border=0></td><td valign=middle>&nbsp;<font color=ffffff>Frequently Visited</td></tr></table>
-+<table width=100% cellpadding=0 cellspacing=0><tr><td bgcolor=313B40><img width=1 height=1></td></tr></table>
-+
-+<span id="popular" class=[$~POPULAR_CLASS~$] oclass=[$~POPULAR_CLASS~$]>
-+[CONTENT_POPULAR]
-+<p class=g>
-+</span>
-+
-+
-+<table width=100% cellpadding=0 cellspacing=0><tr><td bgcolor=4E555C><img width=1 height=1></td></tr></table>
-+<table class=ch cellpadding=0 cellspacing=0 style="background-color:'#424B50'; background-repeat: repeat-y;" background="section.jpg" width=100% height=18><tr onDblClick='return toggle("quib_debug");' onclick='return toggle("quib_debug");' onmouseover="this.bgColor='#465055'" onmouseout="this.bgColor=''"><td width=16 align=right><img id=quib_debugicon src="[$~QUIB_DEBUG_MODE~$]" border=0></td><td valign=middle>&nbsp;<font color=ffffff>Implicit Query Debug</td></tr></table>
-+<table width=100% cellpadding=0 cellspacing=0><tr><td bgcolor=313B40><img width=1 height=1></td></tr></table>
-+
-+<span id="quib_debug" class=[$~QUIB_DEBUG_CLASS~$] oclass=[$~QUIB_DEBUG_CLASS~$]>
-+[CONTENT_QUIB_DEBUG]
-+</span>
-+
-+<span style="visibility:hidden" id='test'>t</span>
-+
-+[CONTENT_OTHER]
-+
-+[SCRIPT]
-+</body>
-+</html>
-diff --git a/tools/grit/grit/testdata/simple-input.xml b/tools/grit/grit/testdata/simple-input.xml
-new file mode 100644
-index 0000000000..92827fa4b5
---- /dev/null
-+++ b/tools/grit/grit/testdata/simple-input.xml
-@@ -0,0 +1,52 @@
-+<?xml version="1.0" encoding="UTF-8"?>
-+<grit base_dir="." latest_public_release="2" current_release="3" source_lang_id="en-US">
-+ <release seq="2">
-+ <messages>
-+ <message name="IDS_OLD_MESSAGE" translateable="true">Hello earthlings!</message>
-+ </messages>
-+ </release>
-+ <release seq="3">
-+ <includes>
-+ <include name="ID_EDIT_BOX_ICON" type="icon" translateable="false" file="images/edit_box.ico" />
-+ <include name="ID_LOGO" type="gif" translateable="true" file="images/logo.gif"/>
-+ </includes>
-+ <messages>
-+ <message name="IDS_BTN_GO" desc="Button text" meaning="verb">Go!</message>
-+ <message name="IDS_GREETING" desc="Printed to greet the currently logged in user">
-+ Hello <ph name="USERNAME">%s<ex>Joi</ex></ph>, how are you doing today?
-+ </message>
-+ </messages>
-+ <structures>
-+ <structure type="menu" name="IDM_FOO" file="rc_files/menus.rc" />
-+ <structure type="dialog" name="IDD_BLAT" file="rc_files/dialogs.rc" />
-+ <structure type="tr_html" name="IDR_HTML_TEMPLATE" file="templates/homepage.html" />
-+ <structure type="dialog" name="IDD_NARROW_DIALOG" file="rc_files/dialogs.rc">
-+ <skeleton expr="lang == 'fr-FR'" variant_of_revision="3">
-+ <![CDATA[IDD_DIALOG1 DIALOGEX 0, 0, 186, 90
-+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION |
-+ WS_SYSMENU
-+CAPTION "TRANSLATEABLEPLACEHOLDER1"
-+FONT 8, "MS Shell Dlg", 400, 0, 0x1
-+BEGIN
-+ DEFPUSHBUTTON "TRANSLATEABLEPLACEHOLDER2",IDOK,129,7,50,14
-+ PUSHBUTTON "TRANSLATEABLEPLACEHOLDER3",IDCANCEL,129,24,50,14
-+ LTEXT "TRANSLATEABLEPLACEHOLDER4",IDC_STATIC,23,31,40,8
-+END]]>
-+ </skeleton>
-+ </structure>
-+ <structure type="version" name="VS_VERSION_INFO" file="rc_files/version.rc"/>
-+ </structures>
-+ </release>
-+ <translations>
-+ <file path="figs_nl_translations.xml" />
-+ <file path="cjk_translations.xml" />
-+ </translations>
-+ <outputs>
-+ <output filename="resource.h" type="rc_header" />
-+ <output filename="resource_en.rc" type="rc_all" lang="en-US" />
-+ <output filename="resource_fr.rc" type="rc_all" lang="fr-FR" />
-+ <output filename="resource_it.rc" type="rc_translateable" lang="it-IT" />
-+ <output filename="resource_zh_cn.rc" type="rc_translateable" lang="zh-CN" />
-+ <output filename="nontranslateable.rc" type="rc_nontranslateable" />
-+ </outputs>
-+</grit>
-diff --git a/tools/grit/grit/testdata/simple.html b/tools/grit/grit/testdata/simple.html
-new file mode 100644
-index 0000000000..4392d23e98
---- /dev/null
-+++ b/tools/grit/grit/testdata/simple.html
-@@ -0,0 +1,3 @@
-+<p>
-+ Hello!
-+</p>
-\ No newline at end of file
-diff --git a/tools/grit/grit/testdata/source.rc b/tools/grit/grit/testdata/source.rc
-new file mode 100644
-index 0000000000..fbc72284e9
---- /dev/null
-+++ b/tools/grit/grit/testdata/source.rc
-@@ -0,0 +1,57 @@
-+IDC_KLONKMENU MENU
-+BEGIN
-+ POPUP "&File"
-+ BEGIN
-+ MENUITEM "E&xit", IDM_EXIT
-+ MENUITEM "This be ""Klonk"" me like", ID_FILE_THISBE
-+ POPUP "gonk"
-+ BEGIN
-+ MENUITEM "Klonk && is [good]", ID_GONK_KLONKIS
-+ END
-+ END
-+ POPUP "&Help"
-+ BEGIN
-+ MENUITEM "&About ...", IDM_ABOUT
-+ END
-+END
-+
-+IDD_ABOUTBOX DIALOGEX 22, 17, 230, 75
-+STYLE DS_SETFONT | DS_MODALFRAME | WS_CAPTION | WS_SYSMENU
-+CAPTION "About"
-+FONT 8, "System", 0, 0, 0x0
-+BEGIN
-+ ICON IDI_KLONK,IDC_MYICON,14,9,20,20
-+ LTEXT "klonk Version ""yibbee"" 1.0",IDC_STATIC,49,10,119,8,
-+ SS_NOPREFIX
-+ LTEXT "Copyright (C) 2005",IDC_STATIC,49,20,119,8
-+ DEFPUSHBUTTON "OK",IDOK,195,6,30,11,WS_GROUP
-+ CONTROL "Jack ""Black"" Daniels",IDC_RADIO1,"Button",
-+ BS_AUTORADIOBUTTON,46,51,84,10
-+END
-+
-+IDD_DIFFERENT_LENGTH_IN_TRANSL DIALOGEX 22, 17, 230, 75
-+STYLE DS_SETFONT | DS_MODALFRAME | WS_CAPTION | WS_SYSMENU
-+CAPTION "Bingobobbi"
-+FONT 8, "System", 0, 0, 0x0
-+BEGIN
-+ LTEXT "Howdie dodie!",IDC_STATIC,49,10,119,8,SS_NOPREFIX
-+ LTEXT "Yo froodie!",IDC_STATIC,49,20,119,8
-+END
-+
-+STRINGTABLE
-+BEGIN
-+ IDS_SIMPLE "One"
-+ IDS_PLACEHOLDER "%s birds"
-+ IDS_PLACEHOLDERS "%d of %d"
-+ IDS_REORDERED_PLACEHOLDERS "$1 of $2"
-+ // Won't be in translations list because it has changed
-+ IDS_CHANGED "This was the old version"
-+ IDS_TWIN_1 "Hello"
-+ IDS_TWIN_2 "Hello"
-+ IDS_NOT_TRANSLATEABLE ":"
-+ IDS_LONGER_TRANSLATED "Removed document $1"
-+ // Won't appear in the list of translations because it's not in the .grd file
-+ IDS_NO_LONGER_USED "Not used"
-+ IDS_DIFFERENT_TWIN_1 "Howdie"
-+ IDS_DIFFERENT_TWIN_2 "Howdie"
-+END
-diff --git a/tools/grit/grit/testdata/special_100_percent/a.png b/tools/grit/grit/testdata/special_100_percent/a.png
-new file mode 100644
-index 0000000000000000000000000000000000000000..5d5089038ca71172e95db9e7aae1e1fa5cebd505
-GIT binary patch
-literal 159
-zcmeAS@N?(olHy`uVBq!ia0vp^oFL4>0wld=oSO}#(mY)pLnNjq|2Y3)zGGzYPN&L+
-zMSC}CcCfp=Dtxv4%6W%G#Q=|R|L;6pCCLUWO)Z<5eoL%TkDTw=s4X!^d(Qa<2khAN
-zZPy!XToBAic1Ss}vcWiD27B3&`Zj^H6CO>7R1{ToQ;=ggdEYbV=IISvfHpFCy85}S
-Ib4q9e0O9jEh5!Hn
-
-literal 0
-HcmV?d00001
-
-diff --git a/tools/grit/grit/testdata/status.html b/tools/grit/grit/testdata/status.html
-new file mode 100644
-index 0000000000..6b997b9369
---- /dev/null
-+++ b/tools/grit/grit/testdata/status.html
-@@ -0,0 +1,44 @@
-+[HEADER]
-+<table cellspacing=0 cellPadding=0 width="100%" border=0>
-+<tr bgcolor=#3399cc><td align=middle height=1><img height=1 width=1></td></tr>
-+</table>
-+<table cellspacing=0 cellPadding=1 width="100%" bgcolor=#e8f4f7 border=0>
-+<tr><td height=20><font size=+1 color=#000000>&nbsp;<b>Desktop Search Status</b></font></td></tr>
-+</table>
-+<br>
-+<center>
-+[$~MESSAGE~$]
-+<table cellspacing=0 cellPadding=6 width=500 border=0>
-+<tr>
-+ <td>&nbsp;</td>
-+ <td align=right nowrap><i><font size=-1>Number of items</font></i></td>
-+ <td align=right nowrap><i><font size=-1>Time of newest item</font></i></td>
-+</tr>
-+<tr>
-+ <td width=1% nowrap><img style="vertical-align:middle" width=16 height=16 src=favicon.ico>&nbsp; Total searchable items</td>
-+ <td align=right><b>[TOTAL_COUNT]</b></td>
-+ <td align=right><b>[TOTAL_TIME]</b></td>
-+</tr>
-+<tr>
-+ <td><font size=-1>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img style="vertical-align:middle" src=email.gif width=16 height=16>&nbsp; Emails</font></td>
-+ <td align=right><font size=-1>[EMAIL_COUNT]</font></td>
-+ <td align=right><font size=-1>[EMAIL_TIME]</font></td>
-+</tr>
-+<tr>
-+ <td><font size=-1>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img style="vertical-align:middle" src="16x16_chat.gif" width=16 height=16>&nbsp; Chats</font></td>
-+ <td align=right><font size=-1>[IM_COUNT]</font></td>
-+ <td align=right><font size=-1>[IM_TIME]</font></td>
-+</tr>
-+<tr>
-+ <td><font size=-1>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img style="vertical-align:middle" src=html.gif width=16 height=16>&nbsp; Web history</font></td>
-+ <td align=right><font size=-1>[WEB_COUNT]</font></td>
-+ <td align=right><font size=-1>[WEB_TIME]</font></td>
-+</tr>
-+<tr>
-+ <td><font size=-1>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img style="vertical-align:middle" src=file.gif width=16 height=16>&nbsp; Files</font></td>
-+ <td align=right><font size=-1>[FILE_COUNT]</font></td>
-+ <td align=right><font size=-1>[FILE_TIME]</font></td>
-+</tr>
-+</table>
-+</center>
-+[FOOTER]
-\ No newline at end of file
-diff --git a/tools/grit/grit/testdata/structure_variables.html b/tools/grit/grit/testdata/structure_variables.html
-new file mode 100644
-index 0000000000..2a15de8072
---- /dev/null
-+++ b/tools/grit/grit/testdata/structure_variables.html
-@@ -0,0 +1,4 @@
-+<h1>[GREETING]!</h1>
-+Some cool things are [THINGS].
-+Did you know that [EQUATION]?
-+<include src="[filename].html">
-diff --git a/tools/grit/grit/testdata/substitute.grd b/tools/grit/grit/testdata/substitute.grd
-new file mode 100644
-index 0000000000..95dcc56e1d
---- /dev/null
-+++ b/tools/grit/grit/testdata/substitute.grd
-@@ -0,0 +1,31 @@
-+<?xml version="1.0" encoding="UTF-8"?> <!-- -*- XML -*- -->
-+<grit
-+ base_dir="."
-+ source_lang_id="en"
-+ tc_project="GoogleDesktopWindowsClient"
-+ latest_public_release="0"
-+ current_release="1"
-+ enc_check="möl">
-+ <outputs>
-+ <output filename="resource.h" type="rc_header" />
-+ <output filename="en_generated_resources.rc" type="rc_all" lang="en" />
-+ <output filename="sv_generated_resources.rc" type="rc_all" lang="sv" />
-+ </outputs>
-+ <translations>
-+ <file path="substitute.xmb" lang="sv" />
-+ </translations>
-+ <release seq="1" allow_pseudo="false">
-+ <messages first_id="8192">
-+ <message name="IDS_COPYRIGHT_GOOGLE_LONG" sub_variable="true" desc="Gadget copyright notice. Needs to be updated every year.">
-+ Copyright 2008 Google Inc. All Rights Reserved.
-+ </message>
-+ <message name="IDS_NEWS_PANEL_COPYRIGHT">
-+ Google Desktop News gadget
-+[IDS_COPYRIGHT_GOOGLE_LONG]
-+View news that is personalized based on the articles you read.
-+
-+For example, if you read lots of sports news, you'll see more sports articles. If you read technology news less often, you'll see fewer of those articles.
-+ </message>
-+ </messages>
-+ </release>
-+</grit>
-diff --git a/tools/grit/grit/testdata/substitute.xmb b/tools/grit/grit/testdata/substitute.xmb
-new file mode 100644
-index 0000000000..e592069c8b
---- /dev/null
-+++ b/tools/grit/grit/testdata/substitute.xmb
-@@ -0,0 +1,10 @@
-+<?xml version="1.0" encoding="UTF-8"?>
-+<!DOCTYPE translationbundle SYSTEM "/home/build/nonconf/google3/i18n/translationbundle.dtd">
-+<translationbundle lang="sv">
-+<translation id="7239109800378180620">© 2008 Google Inc. Med ensamrätt.</translation>
-+<translation id="6212022020330010625">Google Desktop News gadget
-+<ph name="IDS_COPYRIGHT_GOOGLE_LONG_1"/>
-+Se nyheter som är anpassade till dig, baserat på de artiklar du läser.
-+
-+Om du t.ex. läser massor av sportnyheter kommer du att se fler sportartiklar. Om du inte läser tekniknyheter lika ofta ser du färre av dessa artiklar.</translation>
-+</translationbundle>
-diff --git a/tools/grit/grit/testdata/substitute_no_ids.grd b/tools/grit/grit/testdata/substitute_no_ids.grd
-new file mode 100644
-index 0000000000..d569d1cacd
---- /dev/null
-+++ b/tools/grit/grit/testdata/substitute_no_ids.grd
-@@ -0,0 +1,31 @@
-+<?xml version="1.0" encoding="UTF-8"?> <!-- -*- XML -*- -->
-+<grit
-+ base_dir="."
-+ source_lang_id="en"
-+ tc_project="GoogleDesktopWindowsClient"
-+ latest_public_release="0"
-+ current_release="1"
-+ enc_check="möl">
-+ <outputs>
-+ <output filename="resource.h" type="rc_header" />
-+ <output filename="en_generated_resources.rc" type="rc_all" lang="en" />
-+ <output filename="sv_generated_resources.rc" type="rc_all" lang="sv" />
-+ </outputs>
-+ <translations>
-+ <file path="substitute.xmb" lang="sv" />
-+ </translations>
-+ <release seq="1" allow_pseudo="false">
-+ <messages>
-+ <message name="IDS_COPYRIGHT_GOOGLE_LONG" sub_variable="true" desc="Gadget copyright notice. Needs to be updated every year.">
-+ Copyright 2008 Google Inc. All Rights Reserved.
-+ </message>
-+ <message name="IDS_NEWS_PANEL_COPYRIGHT">
-+ Google Desktop News gadget
-+[IDS_COPYRIGHT_GOOGLE_LONG]
-+View news that is personalized based on the articles you read.
-+
-+For example, if you read lots of sports news, you'll see more sports articles. If you read technology news less often, you'll see fewer of those articles.
-+ </message>
-+ </messages>
-+ </release>
-+</grit>
-diff --git a/tools/grit/grit/testdata/substitute_tmpl.grd b/tools/grit/grit/testdata/substitute_tmpl.grd
-new file mode 100644
-index 0000000000..be7b601707
---- /dev/null
-+++ b/tools/grit/grit/testdata/substitute_tmpl.grd
-@@ -0,0 +1,31 @@
-+<?xml version="1.0" encoding="UTF-8"?> <!-- -*- XML -*- -->
-+<grit
-+ base_dir="."
-+ source_lang_id="en"
-+ tc_project="GoogleDesktopWindowsClient"
-+ latest_public_release="0"
-+ current_release="1"
-+ enc_check="möl">
-+ <outputs>
-+ <output filename="resource.h" type="rc_header" />
-+ <output filename="en_${name}_resources.rc" type="rc_all" lang="en" />
-+ <output filename="sv_${name}_resources.rc" type="rc_all" lang="sv" />
-+ </outputs>
-+ <translations>
-+ <file path="substitute.xmb" lang="sv" />
-+ </translations>
-+ <release seq="1" allow_pseudo="false">
-+ <messages first_id="8192">
-+ <message name="IDS_COPYRIGHT_GOOGLE_LONG" sub_variable="true" desc="Gadget copyright notice. Needs to be updated every year.">
-+ Copyright 2008 Google Inc. All Rights Reserved.
-+ </message>
-+ <message name="IDS_NEWS_PANEL_COPYRIGHT">
-+ Google Desktop News gadget
-+[IDS_COPYRIGHT_GOOGLE_LONG]
-+View news that is personalized based on the articles you read.
-+
-+For example, if you read lots of sports news, you'll see more sports articles. If you read technology news less often, you'll see fewer of those articles.
-+ </message>
-+ </messages>
-+ </release>
-+</grit>
-diff --git a/tools/grit/grit/testdata/test_css.css b/tools/grit/grit/testdata/test_css.css
-new file mode 100644
-index 0000000000..55d5dd1770
---- /dev/null
-+++ b/tools/grit/grit/testdata/test_css.css
-@@ -0,0 +1 @@
-+This is a test!
-diff --git a/tools/grit/grit/testdata/test_html.html b/tools/grit/grit/testdata/test_html.html
-new file mode 100644
-index 0000000000..55d5dd1770
---- /dev/null
-+++ b/tools/grit/grit/testdata/test_html.html
-@@ -0,0 +1 @@
-+This is a test!
-diff --git a/tools/grit/grit/testdata/test_js.js b/tools/grit/grit/testdata/test_js.js
-new file mode 100644
-index 0000000000..55d5dd1770
---- /dev/null
-+++ b/tools/grit/grit/testdata/test_js.js
-@@ -0,0 +1 @@
-+This is a test!
-diff --git a/tools/grit/grit/testdata/test_svg.svg b/tools/grit/grit/testdata/test_svg.svg
-new file mode 100644
-index 0000000000..55d5dd1770
---- /dev/null
-+++ b/tools/grit/grit/testdata/test_svg.svg
-@@ -0,0 +1 @@
-+This is a test!
-diff --git a/tools/grit/grit/testdata/test_text.txt b/tools/grit/grit/testdata/test_text.txt
-new file mode 100644
-index 0000000000..55d5dd1770
---- /dev/null
-+++ b/tools/grit/grit/testdata/test_text.txt
-@@ -0,0 +1 @@
-+This is a test!
-diff --git a/tools/grit/grit/testdata/time_related.html b/tools/grit/grit/testdata/time_related.html
-new file mode 100644
-index 0000000000..ee64b1665e
---- /dev/null
-+++ b/tools/grit/grit/testdata/time_related.html
-@@ -0,0 +1,11 @@
-+[HEADER]
-+[CHROME]
-+[NAV_PRE_POST]
-+[$~MESSAGE~$]<br>
-+<table border=0 cellpadding=2 cellspacing=0 width='100%'>
-+[CONTENTS]
-+</table><br>
-+
-+[NAV_PRE_POST]
-+[FOOTER]
-+
-diff --git a/tools/grit/grit/testdata/toolbar_about.html b/tools/grit/grit/testdata/toolbar_about.html
-new file mode 100644
-index 0000000000..bb4b0eb355
---- /dev/null
-+++ b/tools/grit/grit/testdata/toolbar_about.html
-@@ -0,0 +1,138 @@
-+<html id=dlgAbout STYLE="width: 25.8em; height: 17em" [GRITDIR]>
-+<head>
-+<meta http-equiv="content-type" content="text/html; charset=utf-8">
-+<title>About Google Toolbar</title>
-+<style>
-+.button {
-+ width: 7em;
-+ height: 2.2em;
-+ color: buttontext;
-+ font-family: MS Sans Serif;
-+ font-size:8pt;
-+ cursor: hand;
-+}
-+</style>
-+
-+<script> <!--
-+ function HandleError(message, url, line) {
-+ var L_Dialog_ErrorMessage = "An error has occured in this dialog.";
-+ var L_ErrorNumber_Text = "Error: ";
-+ var str = L_Dialog_ErrorMessage + "\n\n"
-+ + L_ErrorNumber_Text + line + "\n"
-+ + message;
-+ alert (str);
-+ window.close();
-+ return true;
-+ }
-+
-+ function OnKeyPress(nCode) {
-+ if (nCode == 27) {
-+ window.close();
-+ return;
-+ }
-+ }
-+
-+ function OnLoad() {
-+ if ((null != window.dialogArguments) && (window.dialogArguments.indexOf("&") == -1) && (window.dialogArguments.indexOf("<") == -1)) {
-+ version.innerHTML = window.dialogArguments;
-+ } else {
-+ version.innerText = "Version: Unknown";
-+ }
-+ }
-+
-+ window.onerror = HandleError;
-+ // -->
-+</script>
-+
-+</head>
-+
-+
-+<body bgcolor="#FFFFFF" onload="OnLoad()" onkeydown="OnKeyPress(event.keyCode)" onkeypress="OnKeyPress(event.keyCode)" scroll=no>
-+
-+<table border=0>
-+
-+ <tr height=5>
-+ <td width=5></td>
-+ <td></td>
-+ <td></td>
-+ <td></td>
-+ <td width=5></td>
-+ </tr>
-+
-+ <tr>
-+ <td></td>
-+ <td colspan=3>
-+
-+
-+<table border="0" cellpadding="0" cellspacing="0" valign="top">
-+ <tr>
-+ <td valign="top" height="47" width="155">
-+ <div align="center"><img src="title_toolbar.gif" width="275" height="59" alt="Google Toolbar"></div>
-+ </td>
-+ <td valign="middle" height="47" width="713">
-+ <hr size=1 color=25479D></td></tr>
-+</table>
-+
-+
-+ </td>
-+ <!--
-+ <TD colspan=2>
-+ <span style="COLOR: black; FONT: 18pt Tahoma, MS Shell Dlg"><b>
-+ Google Toolbar&trade;</b>
-+ </span>
-+ </TD>
-+ -->
-+ <td valign="middle">
-+ </td>
-+ </tr>
-+
-+ <tr>
-+ <td></td>
-+ <td align=center><img src="googly.gif"></td>
-+ <td colspan=2 align=left>
-+ <span style="WIDTH: 25em; height:6em COLOR: black; FONT: 8pt Tahoma, MS Shell Dlg">
-+ <span id=version></span><br>
-+ </span>
-+ </td>
-+ <td></td>
-+ </tr>
-+
-+ <tr height=50>
-+ <td></td>
-+ <td></td>
-+ <td colspan=2 align=left>
-+ <span style="WIDTH: 25em; height:6em COLOR: black; FONT: 8pt Tahoma, MS Shell Dlg">
-+ <!--$/translate-->
-+ <i>De parvis grandis acervus erit</i>
-+ <!--$translate-->
-+ </span>
-+ </td>
-+ <td></td>
-+ </tr>
-+
-+ <tr height=40>
-+ <td></td>
-+ <td></td>
-+ <td></td>
-+ <td></td>
-+ <td></td>
-+ </tr>
-+
-+ <tr>
-+ <td></td>
-+ <td width=80></td>
-+ <td>
-+ <!--$/translate-->
-+ <span style="WIDTH: 20em; COLOR: black; FONT: 8pt Tahoma, MS Shell Dlg" id="copyright">&copy; 2006 Google</span>
-+ <!--$translate-->
-+ </td>
-+ <td id=ok-button align=right><button tabindex=1 type=submit align=right id="okButton" class=button onClick="window.close();" >OK</button>
-+ </td>
-+ <td></td>
-+ </tr>
-+
-+</table>
-+</span>
-+
-+</body>
-+</html>
-diff --git a/tools/grit/grit/testdata/tools/grit/resource_ids b/tools/grit/grit/testdata/tools/grit/resource_ids
-new file mode 100644
-index 0000000000..8a2b608df1
---- /dev/null
-+++ b/tools/grit/grit/testdata/tools/grit/resource_ids
-@@ -0,0 +1,176 @@
-+# Copyright (c) 2011 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+#
-+# This file is used to assign starting resource ids for resources and strings
-+# used by Chromium. This is done to ensure that resource ids are unique
-+# across all the grd files. If you are adding a new grd file, please add
-+# a new entry to this file.
-+#
-+# The first entry in the file, SRCDIR, is special: It is a relative path from
-+# this file to the base of your checkout.
-+#
-+# http://msdn.microsoft.com/en-us/library/t2zechd4(VS.71).aspx says that the
-+# range for IDR_ is 1 to 28,671 and the range for IDS_ is 1 to 32,767 and
-+# common convention starts practical use of IDs at 100 or 101.
-+{
-+ "SRCDIR": "../..",
-+
-+ "chrome/browser/browser_resources.grd": {
-+ "includes": [500],
-+ },
-+ "chrome/browser/resources/component_extension_resources.grd": {
-+ "includes": [1000],
-+ },
-+ "chrome/browser/resources/net_internals_resources.grd": {
-+ "includes": [1500],
-+ },
-+ "chrome/browser/resources/shared_resources.grd": {
-+ "includes": [2000],
-+ },
-+ "chrome/common/common_resources.grd": {
-+ "includes": [2500],
-+ },
-+ "chrome/default_plugin/default_plugin_resources.grd": {
-+ "includes": [3000],
-+ },
-+ "chrome/renderer/renderer_resources.grd": {
-+ "includes": [3500],
-+ },
-+ "net/base/net_resources.grd": {
-+ "includes": [4000],
-+ },
-+ "webkit/glue/webkit_resources.grd": {
-+ "includes": [4500],
-+ },
-+ "webkit/tools/test_shell/test_shell_resources.grd": {
-+ "includes": [5000],
-+ },
-+ "ui/resources/ui_resources.grd": {
-+ "includes": [5500],
-+ },
-+ "chrome/app/theme/theme_resources.grd": {
-+ "includes": [6000],
-+ },
-+ "chrome_frame/resources/chrome_frame_resources.grd": {
-+ "includes": [6500],
-+ },
-+ # WebKit.grd can be in two different places depending on whether we are
-+ # in a chromium checkout or a webkit-only checkout.
-+ "third_party/WebKit/Source/WebKit/chromium/WebKit.grd": {
-+ "includes": [7000],
-+ },
-+ "WebKit.grd": {
-+ "includes": [7000],
-+ },
-+
-+ "ui/base/strings/app_locale_settings.grd": {
-+ "META": {"join": 2},
-+ "messages": [7500],
-+ },
-+ "chrome/app/resources/locale_settings.grd": {
-+ "includes": [8000],
-+ "messages": [8500],
-+ },
-+ # These each start with the same resource id because we only use one
-+ # file for each build (cros, linux, mac, or win).
-+ "chrome/app/resources/locale_settings_cros.grd": {
-+ "messages": [9000],
-+ },
-+ "chrome/app/resources/locale_settings_linux.grd": {
-+ "messages": [9000],
-+ },
-+ "chrome/app/resources/locale_settings_mac.grd": {
-+ "messages": [9000],
-+ },
-+ "chrome/app/resources/locale_settings_win.grd": {
-+ "messages": [9000],
-+ },
-+
-+ "ui/base/strings/ui_strings.grd": {
-+ "META": {"join": 4},
-+ "messages": [9500],
-+ },
-+ # Chromium strings and Google Chrome strings must start at the same id.
-+ # We only use one file depending on whether we're building Chromium or
-+ # Google Chrome.
-+ "chrome/app/chromium_strings.grd": {
-+ "messages": [10000],
-+ },
-+ "chrome/app/google_chrome_strings.grd": {
-+ "messages": [10000],
-+ },
-+ # Leave lots of space for generated_resources since it has most of our
-+ # strings.
-+ "chrome/app/generated_resources.grd": {
-+ "META": {"join": 2},
-+ "structures": [10500],
-+ "messages": [11000],
-+ },
-+ # The chrome frame dialogs are also in generated_resources.grd so they
-+ # get included by the translation console. We make sure that the ids
-+ # for structures here are the same as for generated_resources.grd.
-+ "chrome_frame/resources/chrome_frame_dialogs.grd": {
-+ "structures": [10500],
-+ "includes": [10750],
-+ },
-+ "webkit/glue/inspector_strings.grd": {
-+ "messages": [16000],
-+ },
-+ "webkit/glue/webkit_strings.grd": {
-+ "messages": [16500],
-+ },
-+
-+ "chrome_frame/resources/chrome_frame_resources.grd": {
-+ "includes": [17500],
-+ "structures": [18000],
-+ },
-+
-+ "ui/gfx/gfx_resources.grd": {
-+ "includes": [18500],
-+ },
-+
-+ "chrome/app/policy/policy_templates.grd": {
-+ "structures": [19000],
-+ "messages": [19010],
-+ },
-+
-+ "chrome/browser/autofill/autofill_resources.grd": {
-+ "messages": [19500],
-+ },
-+ "chrome/browser/resources/sync_internals_resources.grd": {
-+ "includes": [20000],
-+ },
-+ # This file is generated during the build.
-+ "<(SHARED_INTERMEDIATE_DIR)/devtools/devtools_resources.grd": {
-+ "includes": [20500],
-+ },
-+ # All standard and large theme resources should have the same IDs.
-+ "chrome/app/theme/theme_resources_standard.grd": {
-+ "includes": [21000],
-+ },
-+ "chrome/app/theme/theme_resources_large.grd": {
-+ "includes": [21000],
-+ },
-+ # This file is generated during the build.
-+ "chrome/browser/debugger/frontend/devtools_frontend_resources.grd": {
-+ "META": {"join": 2},
-+ "includes": [21500],
-+ },
-+ "cloud_print/virtual_driver/win/install/virtual_driver_setup_resources.grd": {
-+ "messages": [22500],
-+ },
-+ "chrome/browser/resources/quota_internals_resources.grd": {
-+ "includes": [23000],
-+ },
-+ "chrome/browser/resources/workers_resources.grd": {
-+ "includes": [23500],
-+ },
-+ # All standard and large theme resources should have the same IDs.
-+ "ui/resources/ui_resources_standard.grd": {
-+ "includes": [24000],
-+ },
-+ "ui/resources/ui_resources_large.grd": {
-+ "includes": [24000],
-+ },
-+}
-diff --git a/tools/grit/grit/testdata/transl.rc b/tools/grit/grit/testdata/transl.rc
-new file mode 100644
-index 0000000000..2f2595db3f
---- /dev/null
-+++ b/tools/grit/grit/testdata/transl.rc
-@@ -0,0 +1,56 @@
-+IDC_KLONKMENU MENU
-+BEGIN
-+ POPUP "&Skra"
-+ BEGIN
-+ MENUITEM "&Haetta", IDM_EXIT
-+ MENUITEM "Thetta er ""Klonk"" sem eg fyla", ID_FILE_THISBE
-+ POPUP "gonkurinn"
-+ BEGIN
-+ MENUITEM "Klonk && er [good]", ID_GONK_KLONKIS
-+ END
-+ END
-+ POPUP "&Hjalp"
-+ BEGIN
-+ MENUITEM "&Um...", IDM_ABOUT
-+ END
-+END
-+
-+IDD_ABOUTBOX DIALOGEX 22, 17, 230, 75
-+STYLE DS_SETFONT | DS_MODALFRAME | WS_CAPTION | WS_SYSMENU
-+CAPTION "Um Klonk"
-+FONT 8, "System", 0, 0, 0x0
-+BEGIN
-+ ICON IDI_KLONK,IDC_MYICON,14,9,20,20
-+ LTEXT "klonk utgafa ""jibbi"" 1.0",IDC_STATIC,49,10,119,8,
-+ SS_NOPREFIX
-+ LTEXT "Hofundarrettur (C) 2005",IDC_STATIC,49,20,119,8
-+ DEFPUSHBUTTON "I lagi",IDOK,195,6,30,11,WS_GROUP
-+ CONTROL "Jack ""Black"" Daniels",IDC_RADIO1,"Button",
-+ BS_AUTORADIOBUTTON,46,51,84,10
-+END
-+
-+IDD_DIFFERENT_LENGTH_IN_TRANSL DIALOGEX 22, 17, 230, 75
-+STYLE DS_SETFONT | DS_MODALFRAME | WS_CAPTION | WS_SYSMENU
-+CAPTION "Bingobobbi"
-+FONT 8, "System", 0, 0, 0x0
-+BEGIN
-+ LTEXT "Howdie dodie!",IDC_STATIC,49,10,119,8,SS_NOPREFIX
-+END
-+
-+STRINGTABLE
-+BEGIN
-+ IDS_SIMPLE "Ein"
-+ IDS_PLACEHOLDER "%s Vogeln"
-+ IDS_PLACEHOLDERS "%d von %d"
-+ // Shouldn't be part of translations list because the translation is
-+ // reordered so placeholder fixup fails
-+ IDS_REORDERED_PLACEHOLDERS "$2 auf $1"
-+ IDS_CHANGED "Dass war die alte Version"
-+ IDS_TWIN_1 "Hallo"
-+ IDS_TWIN_2 "Hallo"
-+ IDS_NOT_TRANSLATEABLE ":"
-+ IDS_LONGER_TRANSLATED "Dokument $1 ist entfernt worden"
-+ IDS_NO_LONGER_USED "Nicht verwendet"
-+ IDS_DIFFERENT_TWIN_1 "Howdie"
-+ IDS_DIFFERENT_TWIN_2 "Hallo sagt man"
-+END
-diff --git a/tools/grit/grit/testdata/versions.html b/tools/grit/grit/testdata/versions.html
-new file mode 100644
-index 0000000000..d1f40d8d72
---- /dev/null
-+++ b/tools/grit/grit/testdata/versions.html
-@@ -0,0 +1,7 @@
-+[HEADER]
-+
-+[TOP_CHROME]
-+[CONTENTS]
-+
-+[NEXT_PREV]
-+[FOOTER]
-diff --git a/tools/grit/grit/testdata/whitelist.txt b/tools/grit/grit/testdata/whitelist.txt
-new file mode 100644
-index 0000000000..5b3aca40b5
---- /dev/null
-+++ b/tools/grit/grit/testdata/whitelist.txt
-@@ -0,0 +1,4 @@
-+IDS_MESSAGE_WHITELISTED
-+IDR_STRUCTURE_WHITELISTED
-+IDR_STRUCTURE_IN_TRUE_IF_WHITELISTED
-+IDR_INCLUDE_WHITELISTED
-diff --git a/tools/grit/grit/testdata/whitelist_resources.grd b/tools/grit/grit/testdata/whitelist_resources.grd
-new file mode 100644
-index 0000000000..9925688ff5
---- /dev/null
-+++ b/tools/grit/grit/testdata/whitelist_resources.grd
-@@ -0,0 +1,54 @@
-+<?xml version="1.0" encoding="UTF-8"?>
-+<grit latest_public_release="0"
-+ current_release="1"
-+ output_all_resource_defines="false">
-+ <outputs>
-+ <output filename="whitelist_test_resources.h" type="rc_header">
-+ <emit emit_type='prepend'></emit>
-+ </output>
-+ <output filename="whitelist_test_resources_map.cc"
-+ type="resource_file_map_source" />
-+ <output filename="whitelist_test_resources_map.h"
-+ type="resource_map_header" />
-+ <output filename="whitelist_test_resources.pak" type="data_package" />
-+ </outputs>
-+ <translations>
-+ <file path="substitute.xmb" lang="sv" />
-+ </translations>
-+ <release seq="1">
-+ <structures>
-+ <structure name="IDR_STRUCTURE_WHITELISTED" file="browser.html"
-+ type="chrome_html" >
-+ </structure>
-+ <structure name="IDR_STRUCTURE_NOT_WHITELISTED" file="deleted.html"
-+ type="chrome_html" >
-+ </structure>
-+ <if expr="True">
-+ <structure name="IDR_STRUCTURE_IN_TRUE_IF_WHITELISTED"
-+ file="details.html"
-+ type="chrome_html" >
-+ </structure>
-+ <structure name="IDR_STRUCTURE_IN_TRUE_IF_NOT_WHITELISTED"
-+ file="error.html"
-+ type="chrome_html" >
-+ </structure>
-+ </if>
-+ <if expr="False">
-+ <structure name="IDR_STRUCTURE_IN_FALSE_IF_WHITELISTED"
-+ file="status.html"
-+ type="chrome_html" >
-+ </structure>
-+ <structure name="IDR_STRUCTURE_IN_FALSE_IF_NOT_WHITELISTED"
-+ file="simple.html"
-+ type="chrome_html" >
-+ </structure>
-+ </if>
-+ </structures>
-+ <includes>
-+ <include name="IDR_INCLUDE_WHITELISTED" file="klonk.ico"
-+ type="BINDATA" />
-+ <include name="IDR_INCLUDE_NOT_WHITELISTED" file="klonk.rc"
-+ type="BINDATA" />
-+ </includes>
-+ </release>
-+</grit>
-diff --git a/tools/grit/grit/testdata/whitelist_strings.grd b/tools/grit/grit/testdata/whitelist_strings.grd
-new file mode 100644
-index 0000000000..df80f5fd32
---- /dev/null
-+++ b/tools/grit/grit/testdata/whitelist_strings.grd
-@@ -0,0 +1,23 @@
-+<?xml version="1.0" encoding="UTF-8"?>
-+<grit latest_public_release="0"
-+ current_release="1"
-+ output_all_resource_defines="false">
-+ <outputs >
-+ <output filename="whitelist_test_resources.h" type="rc_header">
-+ <emit emit_type='prepend'></emit>
-+ </output>
-+ <output filename="en_whitelist_test_strings.rc" type="rc_all" lang="en" />
-+ </outputs>
-+ <release seq="1">
-+ <messages>
-+ <message name="IDS_MESSAGE_WHITELISTED"
-+ desc="A message in the whiltelist file.">
-+ Whitelisted.
-+ </message>
-+ <message name="IDS_MESSAGE_NOT_WHITELISTED"
-+ desc="A message that isn't in the whiltelist file.">
-+ Not whitelisted.
-+ </message>
-+ </messages>
-+ </release>
-+</grit>
-diff --git a/tools/grit/grit/tool/__init__.py b/tools/grit/grit/tool/__init__.py
-new file mode 100644
-index 0000000000..cc455b36e7
---- /dev/null
-+++ b/tools/grit/grit/tool/__init__.py
-@@ -0,0 +1,8 @@
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Package grit.tool
-+'''
-+
-+pass
-diff --git a/tools/grit/grit/tool/android2grd.py b/tools/grit/grit/tool/android2grd.py
-new file mode 100644
-index 0000000000..005297bafe
---- /dev/null
-+++ b/tools/grit/grit/tool/android2grd.py
-@@ -0,0 +1,484 @@
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+"""The 'grit android2grd' tool."""
-+
-+from __future__ import print_function
-+
-+import getopt
-+import os.path
-+import sys
-+from xml.dom import Node
-+import xml.dom.minidom
-+
-+import six
-+from six import StringIO
-+
-+import grit.node.empty
-+from grit.node import node_io
-+from grit.node import message
-+
-+from grit.tool import interface
-+
-+from grit import grd_reader
-+from grit import lazy_re
-+from grit import tclib
-+
-+
-+# The name of a string in strings.xml
-+_STRING_NAME = lazy_re.compile(r'[a-z0-9_]+\Z')
-+
-+# A string's character limit in strings.xml
-+_CHAR_LIMIT = lazy_re.compile(r'\[CHAR-LIMIT=(\d+)\]')
-+
-+# Finds String.Format() style format specifiers such as "%-5.2f".
-+_FORMAT_SPECIFIER = lazy_re.compile(
-+ r'%'
-+ r'([1-9][0-9]*\$|<)?' # argument_index
-+ r'([-#+ 0,(]*)' # flags
-+ r'([0-9]+)?' # width
-+ r'(\.[0-9]+)?' # precision
-+ r'([bBhHsScCdoxXeEfgGaAtT%n])') # conversion
-+
-+
-+class Android2Grd(interface.Tool):
-+ """Tool for converting Android string.xml files into chrome Grd files.
-+
-+Usage: grit [global options] android2grd [OPTIONS] STRINGS_XML
-+
-+The Android2Grd tool will convert an Android strings.xml file (whose path is
-+specified by STRINGS_XML) and create a chrome style grd file containing the
-+relevant information.
-+
-+Because grd documents are much richer than strings.xml documents we supplement
-+the information required by grds using OPTIONS with sensible defaults.
-+
-+OPTIONS may be any of the following:
-+
-+ --name FILENAME Specify the base FILENAME. This should be without
-+ any file type suffix. By default
-+ "chrome_android_strings" will be used.
-+
-+ --languages LANGUAGES Comma separated list of ISO language codes (e.g.
-+ en-US, en-GB, ru, zh-CN). These codes will be used
-+ to determine the names of resource and translations
-+ files that will be declared by the output grd file.
-+
-+ --grd-dir GRD_DIR Specify where the resultant grd file
-+ (FILENAME.grd) should be output. By default this
-+ will be the present working directory.
-+
-+ --header-dir HEADER_DIR Specify the location of the directory where grit
-+ generated C++ headers (whose name will be
-+ FILENAME.h) will be placed. Use an empty string to
-+ disable rc generation. Default: empty.
-+
-+ --rc-dir RC_DIR Specify the directory where resource files will
-+ be located relative to grit build's output
-+ directory. Use an empty string to disable rc
-+ generation. Default: empty.
-+
-+ --xml-dir XML_DIR Specify where to place localized strings.xml files
-+ relative to grit build's output directory. For each
-+ language xx a values-xx/strings.xml file will be
-+ generated. Use an empty string to disable
-+ strings.xml generation. Default: '.'.
-+
-+ --xtb-dir XTB_DIR Specify where the xtb files containing translations
-+ will be located relative to the grd file. Default:
-+ '.'.
-+"""
-+
-+ _NAME_FLAG = 'name'
-+ _LANGUAGES_FLAG = 'languages'
-+ _GRD_DIR_FLAG = 'grd-dir'
-+ _RC_DIR_FLAG = 'rc-dir'
-+ _HEADER_DIR_FLAG = 'header-dir'
-+ _XTB_DIR_FLAG = 'xtb-dir'
-+ _XML_DIR_FLAG = 'xml-dir'
-+
-+ def __init__(self):
-+ self.name = 'chrome_android_strings'
-+ self.languages = []
-+ self.grd_dir = '.'
-+ self.rc_dir = None
-+ self.xtb_dir = '.'
-+ self.xml_res_dir = '.'
-+ self.header_dir = None
-+
-+ def ShortDescription(self):
-+ """Returns a short description of the Android2Grd tool.
-+
-+ Overridden from grit.interface.Tool
-+
-+ Returns:
-+ A string containing a short description of the android2grd tool.
-+ """
-+ return 'Converts Android string.xml files into Chrome grd files.'
-+
-+ def ParseOptions(self, args):
-+ """Set this objects and return all non-option arguments."""
-+ flags = [
-+ Android2Grd._NAME_FLAG,
-+ Android2Grd._LANGUAGES_FLAG,
-+ Android2Grd._GRD_DIR_FLAG,
-+ Android2Grd._RC_DIR_FLAG,
-+ Android2Grd._HEADER_DIR_FLAG,
-+ Android2Grd._XTB_DIR_FLAG,
-+ Android2Grd._XML_DIR_FLAG, ]
-+ (opts, args) = getopt.getopt(
-+ args, None, ['%s=' % o for o in flags] + ['help'])
-+
-+ for key, val in opts:
-+ # Get rid of the preceding hypens.
-+ k = key[2:]
-+ if k == Android2Grd._NAME_FLAG:
-+ self.name = val
-+ elif k == Android2Grd._LANGUAGES_FLAG:
-+ self.languages = val.split(',')
-+ elif k == Android2Grd._GRD_DIR_FLAG:
-+ self.grd_dir = val
-+ elif k == Android2Grd._RC_DIR_FLAG:
-+ self.rc_dir = val
-+ elif k == Android2Grd._HEADER_DIR_FLAG:
-+ self.header_dir = val
-+ elif k == Android2Grd._XTB_DIR_FLAG:
-+ self.xtb_dir = val
-+ elif k == Android2Grd._XML_DIR_FLAG:
-+ self.xml_res_dir = val
-+ elif k == 'help':
-+ self.ShowUsage()
-+ sys.exit(0)
-+ return args
-+
-+ def Run(self, opts, args):
-+ """Runs the Android2Grd tool.
-+
-+ Inherited from grit.interface.Tool.
-+
-+ Args:
-+ opts: List of string arguments that should be parsed.
-+ args: String containing the path of the strings.xml file to be converted.
-+ """
-+ args = self.ParseOptions(args)
-+ if len(args) != 1:
-+ print('Tool requires one argument, the path to the Android '
-+ 'strings.xml resource file to be converted.')
-+ return 2
-+ self.SetOptions(opts)
-+
-+ android_path = args[0]
-+
-+ # Read and parse the Android strings.xml file.
-+ with open(android_path) as android_file:
-+ android_dom = xml.dom.minidom.parse(android_file)
-+
-+ # Do the hard work -- convert the Android dom to grd file contents.
-+ grd_dom = self.AndroidDomToGrdDom(android_dom)
-+ grd_string = six.text_type(grd_dom)
-+
-+ # Write the grd string to a file in grd_dir.
-+ grd_filename = self.name + '.grd'
-+ grd_path = os.path.join(self.grd_dir, grd_filename)
-+ with open(grd_path, 'w') as grd_file:
-+ grd_file.write(grd_string)
-+
-+ def AndroidDomToGrdDom(self, android_dom):
-+ """Converts a strings.xml DOM into a DOM representing the contents of
-+ a grd file.
-+
-+ Args:
-+ android_dom: A xml.dom.Document containing the contents of the Android
-+ string.xml document.
-+ Returns:
-+ The DOM for the grd xml document produced by converting the Android DOM.
-+ """
-+
-+ # Start with a basic skeleton for the .grd file.
-+ root = grd_reader.Parse(StringIO(
-+ '''<?xml version="1.0" encoding="UTF-8"?>
-+ <grit base_dir="." latest_public_release="0"
-+ current_release="1" source_lang_id="en">
-+ <outputs />
-+ <translations />
-+ <release allow_pseudo="false" seq="1">
-+ <messages fallback_to_english="true" />
-+ </release>
-+ </grit>'''), dir='.')
-+ outputs = root.children[0]
-+ translations = root.children[1]
-+ messages = root.children[2].children[0]
-+ assert (isinstance(messages, grit.node.empty.MessagesNode) and
-+ isinstance(translations, grit.node.empty.TranslationsNode) and
-+ isinstance(outputs, grit.node.empty.OutputsNode))
-+
-+ if self.header_dir:
-+ cpp_header = self.__CreateCppHeaderOutputNode(outputs, self.header_dir)
-+ for lang in self.languages:
-+ # Create an output element for each language.
-+ if self.rc_dir:
-+ self.__CreateRcOutputNode(outputs, lang, self.rc_dir)
-+ if self.xml_res_dir:
-+ self.__CreateAndroidXmlOutputNode(outputs, lang, self.xml_res_dir)
-+ if lang != 'en':
-+ self.__CreateFileNode(translations, lang)
-+ # Convert all the strings.xml strings into grd messages.
-+ self.__CreateMessageNodes(messages, android_dom.documentElement)
-+
-+ return root
-+
-+ def __CreateMessageNodes(self, messages, resources):
-+ """Creates the <message> elements and adds them as children of <messages>.
-+
-+ Args:
-+ messages: the <messages> element in the strings.xml dom.
-+ resources: the <resources> element in the grd dom.
-+ """
-+ # <string> elements contain the definition of the resource.
-+ # The description of a <string> element is contained within the comment
-+ # node element immediately preceeding the string element in question.
-+ description = ''
-+ for child in resources.childNodes:
-+ if child.nodeType == Node.COMMENT_NODE:
-+ # Remove leading/trailing whitespace; collapse consecutive whitespaces.
-+ description = ' '.join(child.data.split())
-+ elif child.nodeType == Node.ELEMENT_NODE:
-+ if child.tagName != 'string':
-+ print('Warning: ignoring unknown tag <%s>' % child.tagName)
-+ else:
-+ translatable = self.IsTranslatable(child)
-+ raw_name = child.getAttribute('name')
-+ if not _STRING_NAME.match(raw_name):
-+ print('Error: illegal string name: %s' % raw_name)
-+ grd_name = 'IDS_' + raw_name.upper()
-+ # Transform the <string> node contents into a tclib.Message, taking
-+ # care to handle whitespace transformations and escaped characters,
-+ # and coverting <xliff:g> placeholders into <ph> placeholders.
-+ msg = self.CreateTclibMessage(child)
-+ msg_node = self.__CreateMessageNode(messages, grd_name, description,
-+ msg, translatable)
-+ messages.AddChild(msg_node)
-+ # Reset the description once a message has been parsed.
-+ description = ''
-+
-+ def CreateTclibMessage(self, android_string):
-+ """Transforms a <string/> element from strings.xml into a tclib.Message.
-+
-+ Interprets whitespace, quotes, and escaped characters in the android_string
-+ according to Android's formatting and styling rules for strings. Also
-+ converts <xliff:g> placeholders into <ph> placeholders, e.g.:
-+
-+ <xliff:g id="website" example="google.com">%s</xliff:g>
-+ becomes
-+ <ph name="website"><ex>google.com</ex>%s</ph>
-+
-+ Returns:
-+ The tclib.Message.
-+ """
-+ msg = tclib.Message()
-+ current_text = '' # Accumulated text that hasn't yet been added to msg.
-+ nodes = android_string.childNodes
-+
-+ for i, node in enumerate(nodes):
-+ # Handle text nodes.
-+ if node.nodeType in (Node.TEXT_NODE, Node.CDATA_SECTION_NODE):
-+ current_text += node.data
-+
-+ # Handle <xliff:g> and other tags.
-+ elif node.nodeType == Node.ELEMENT_NODE:
-+ if node.tagName == 'xliff:g':
-+ assert node.hasAttribute('id'), 'missing id: ' + node.data()
-+ placeholder_id = node.getAttribute('id')
-+ placeholder_text = self.__FormatPlaceholderText(node)
-+ placeholder_example = node.getAttribute('example')
-+ if not placeholder_example:
-+ print('Info: placeholder does not contain an example: %s' %
-+ node.toxml())
-+ placeholder_example = placeholder_id.upper()
-+ msg.AppendPlaceholder(tclib.Placeholder(placeholder_id,
-+ placeholder_text, placeholder_example))
-+ else:
-+ print('Warning: removing tag <%s> which must be inside a '
-+ 'placeholder: %s' % (node.tagName, node.toxml()))
-+ msg.AppendText(self.__FormatPlaceholderText(node))
-+
-+ # Handle other nodes.
-+ elif node.nodeType != Node.COMMENT_NODE:
-+ assert False, 'Unknown node type: %s' % node.nodeType
-+
-+ is_last_node = (i == len(nodes) - 1)
-+ if (current_text and
-+ (is_last_node or nodes[i + 1].nodeType == Node.ELEMENT_NODE)):
-+ # For messages containing just text and comments (no xml tags) Android
-+ # strips leading and trailing whitespace. We mimic that behavior.
-+ if not msg.GetContent() and is_last_node:
-+ current_text = current_text.strip()
-+ msg.AppendText(self.__FormatAndroidString(current_text))
-+ current_text = ''
-+
-+ return msg
-+
-+ def __FormatAndroidString(self, android_string, inside_placeholder=False):
-+ r"""Returns android_string formatted for a .grd file.
-+
-+ * Collapses consecutive whitespaces, except when inside double-quotes.
-+ * Replaces \\, \n, \t, \", \' with \, newline, tab, ", '.
-+ """
-+ backslash_map = {'\\' : '\\', 'n' : '\n', 't' : '\t', '"' : '"', "'" : "'"}
-+ is_quoted_section = False # True when we're inside double quotes.
-+ is_backslash_sequence = False # True after seeing an unescaped backslash.
-+ prev_char = ''
-+ output = []
-+ for c in android_string:
-+ if is_backslash_sequence:
-+ # Unescape \\, \n, \t, \", and \'.
-+ assert c in backslash_map, 'Illegal escape sequence: \\%s' % c
-+ output.append(backslash_map[c])
-+ is_backslash_sequence = False
-+ elif c == '\\':
-+ is_backslash_sequence = True
-+ elif c.isspace() and not is_quoted_section:
-+ # Turn whitespace into ' ' and collapse consecutive whitespaces.
-+ if not prev_char.isspace():
-+ output.append(' ')
-+ elif c == '"':
-+ is_quoted_section = not is_quoted_section
-+ else:
-+ output.append(c)
-+ prev_char = c
-+ output = ''.join(output)
-+
-+ if is_quoted_section:
-+ print('Warning: unbalanced quotes in string: %s' % android_string)
-+
-+ if is_backslash_sequence:
-+ print('Warning: trailing backslash in string: %s' % android_string)
-+
-+ # Check for format specifiers outside of placeholder tags.
-+ if not inside_placeholder:
-+ format_specifier = _FORMAT_SPECIFIER.search(output)
-+ if format_specifier:
-+ print('Warning: format specifiers are not inside a placeholder '
-+ '<xliff:g/> tag: %s' % output)
-+
-+ return output
-+
-+ def __FormatPlaceholderText(self, placeholder_node):
-+ """Returns the text inside of an <xliff:g> placeholder node."""
-+ text = []
-+ for childNode in placeholder_node.childNodes:
-+ if childNode.nodeType in (Node.TEXT_NODE, Node.CDATA_SECTION_NODE):
-+ text.append(childNode.data)
-+ elif childNode.nodeType != Node.COMMENT_NODE:
-+ assert False, 'Unknown node type in ' + placeholder_node.toxml()
-+ return self.__FormatAndroidString(''.join(text), inside_placeholder=True)
-+
-+ def __CreateMessageNode(self, messages_node, grd_name, description, msg,
-+ translatable):
-+ """Creates and initializes a <message> element.
-+
-+ Message elements correspond to Android <string> elements in that they
-+ declare a string resource along with a programmatic id.
-+ """
-+ if not description:
-+ print('Warning: no description for %s' % grd_name)
-+ # Check that we actually fit within the character limit we've specified.
-+ match = _CHAR_LIMIT.search(description)
-+ if match:
-+ char_limit = int(match.group(1))
-+ msg_content = msg.GetRealContent()
-+ if len(msg_content) > char_limit:
-+ print('Warning: char-limit for %s is %d, but length is %d: %s' %
-+ (grd_name, char_limit, len(msg_content), msg_content))
-+ return message.MessageNode.Construct(parent=messages_node,
-+ name=grd_name,
-+ message=msg,
-+ desc=description,
-+ translateable=translatable)
-+
-+ def __CreateFileNode(self, translations_node, lang):
-+ """Creates and initializes the <file> elements.
-+
-+ File elements provide information on the location of translation files
-+ (xtbs)
-+ """
-+ xtb_file = os.path.normpath(os.path.join(
-+ self.xtb_dir, '%s_%s.xtb' % (self.name, lang)))
-+ fnode = node_io.FileNode()
-+ fnode.StartParsing(u'file', translations_node)
-+ fnode.HandleAttribute('path', xtb_file)
-+ fnode.HandleAttribute('lang', lang)
-+ fnode.EndParsing()
-+ translations_node.AddChild(fnode)
-+ return fnode
-+
-+ def __CreateCppHeaderOutputNode(self, outputs_node, header_dir):
-+ """Creates the <output> element corresponding to the generated c header."""
-+ header_file_name = os.path.join(header_dir, self.name + '.h')
-+ header_node = node_io.OutputNode()
-+ header_node.StartParsing(u'output', outputs_node)
-+ header_node.HandleAttribute('filename', header_file_name)
-+ header_node.HandleAttribute('type', 'rc_header')
-+ emit_node = node_io.EmitNode()
-+ emit_node.StartParsing(u'emit', header_node)
-+ emit_node.HandleAttribute('emit_type', 'prepend')
-+ emit_node.EndParsing()
-+ header_node.AddChild(emit_node)
-+ header_node.EndParsing()
-+ outputs_node.AddChild(header_node)
-+ return header_node
-+
-+ def __CreateRcOutputNode(self, outputs_node, lang, rc_dir):
-+ """Creates the <output> element corresponding to various rc file output."""
-+ rc_file_name = self.name + '_' + lang + ".rc"
-+ rc_path = os.path.join(rc_dir, rc_file_name)
-+ node = node_io.OutputNode()
-+ node.StartParsing(u'output', outputs_node)
-+ node.HandleAttribute('filename', rc_path)
-+ node.HandleAttribute('lang', lang)
-+ node.HandleAttribute('type', 'rc_all')
-+ node.EndParsing()
-+ outputs_node.AddChild(node)
-+ return node
-+
-+ def __CreateAndroidXmlOutputNode(self, outputs_node, locale, xml_res_dir):
-+ """Creates the <output> element corresponding to various rc file output."""
-+ # Need to check to see if the locale has a region, e.g. the GB in en-GB.
-+ # When a locale has a region Android expects the region to be prefixed
-+ # with an 'r'. For example for en-GB Android expects a values-en-rGB
-+ # directory. Also, Android expects nb, tl, in, iw, ji as the language
-+ # codes for Norwegian, Tagalog/Filipino, Indonesian, Hebrew, and Yiddish:
-+ # http://developer.android.com/reference/java/util/Locale.html
-+ if locale == 'es-419':
-+ android_locale = 'es-rUS'
-+ else:
-+ android_lang, dash, region = locale.partition('-')
-+ lang_map = {'no': 'nb', 'fil': 'tl', 'id': 'in', 'he': 'iw', 'yi': 'ji'}
-+ android_lang = lang_map.get(android_lang, android_lang)
-+ android_locale = android_lang + ('-r' + region if region else '')
-+ values = 'values-' + android_locale if android_locale != 'en' else 'values'
-+ xml_path = os.path.normpath(os.path.join(
-+ xml_res_dir, values, 'strings.xml'))
-+
-+ node = node_io.OutputNode()
-+ node.StartParsing(u'output', outputs_node)
-+ node.HandleAttribute('filename', xml_path)
-+ node.HandleAttribute('lang', locale)
-+ node.HandleAttribute('type', 'android')
-+ node.EndParsing()
-+ outputs_node.AddChild(node)
-+ return node
-+
-+ def IsTranslatable(self, android_string):
-+ """Determines if a <string> element is a candidate for translation.
-+
-+ A <string> element is by default translatable unless otherwise marked.
-+ """
-+ if android_string.hasAttribute('translatable'):
-+ value = android_string.getAttribute('translatable').lower()
-+ if value not in ('true', 'false'):
-+ print('Warning: translatable attribute has invalid value: %s' % value)
-+ return value == 'true'
-+ else:
-+ return True
-diff --git a/tools/grit/grit/tool/android2grd_unittest.py b/tools/grit/grit/tool/android2grd_unittest.py
-new file mode 100644
-index 0000000000..a6934a707c
---- /dev/null
-+++ b/tools/grit/grit/tool/android2grd_unittest.py
-@@ -0,0 +1,181 @@
-+#!/usr/bin/env python
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Unit tests for grit.tool.android2grd'''
-+
-+from __future__ import print_function
-+
-+import os
-+import sys
-+if __name__ == '__main__':
-+ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
-+
-+import unittest
-+import xml.dom.minidom
-+
-+from grit import util
-+from grit.node import empty
-+from grit.node import message
-+from grit.node import misc
-+from grit.node import node_io
-+from grit.tool import android2grd
-+
-+
-+class Android2GrdUnittest(unittest.TestCase):
-+
-+ def __Parse(self, xml_string):
-+ return xml.dom.minidom.parseString(xml_string).childNodes[0]
-+
-+ def testCreateTclibMessage(self):
-+ tool = android2grd.Android2Grd()
-+ msg = tool.CreateTclibMessage(self.__Parse(r'''
-+ <string name="simple">A simple string</string>'''))
-+ self.assertEqual(msg.GetRealContent(), 'A simple string')
-+ msg = tool.CreateTclibMessage(self.__Parse(r'''
-+ <string name="outer_whitespace">
-+ Strip leading/trailing whitespace
-+ </string>'''))
-+ self.assertEqual(msg.GetRealContent(), 'Strip leading/trailing whitespace')
-+ msg = tool.CreateTclibMessage(self.__Parse(r'''
-+ <string name="inner_whitespace">Fold multiple spaces</string>'''))
-+ self.assertEqual(msg.GetRealContent(), 'Fold multiple spaces')
-+ msg = tool.CreateTclibMessage(self.__Parse(r'''
-+ <string name="escaped_spaces">Retain \n escaped\t spaces</string>'''))
-+ self.assertEqual(msg.GetRealContent(), 'Retain \n escaped\t spaces')
-+ msg = tool.CreateTclibMessage(self.__Parse(r'''
-+ <string name="quotes"> " Quotes preserve
-+ whitespace" but only for "enclosed elements "
-+ </string>'''))
-+ self.assertEqual(msg.GetRealContent(), ''' Quotes preserve
-+ whitespace but only for enclosed elements ''')
-+ msg = tool.CreateTclibMessage(self.__Parse(
-+ r'''<string name="escaped_characters">Escaped characters: \"\'\\\t\n'''
-+ '</string>'))
-+ self.assertEqual(msg.GetRealContent(), '''Escaped characters: "'\\\t\n''')
-+ msg = tool.CreateTclibMessage(self.__Parse(
-+ '<string xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2" '
-+ 'name="placeholders">'
-+ 'Open <xliff:g id="FILENAME" example="internet.html">%s</xliff:g>?'
-+ '</string>'))
-+ self.assertEqual(msg.GetRealContent(), 'Open %s?')
-+ self.assertEqual(len(msg.GetPlaceholders()), 1)
-+ self.assertEqual(msg.GetPlaceholders()[0].presentation, 'FILENAME')
-+ self.assertEqual(msg.GetPlaceholders()[0].original, '%s')
-+ self.assertEqual(msg.GetPlaceholders()[0].example, 'internet.html')
-+ msg = tool.CreateTclibMessage(self.__Parse(r'''
-+ <string name="comment">Contains a <!-- ignore this --> comment
-+ </string>'''))
-+ self.assertEqual(msg.GetRealContent(), 'Contains a comment')
-+
-+ def testIsTranslatable(self):
-+ tool = android2grd.Android2Grd()
-+ string_el = self.__Parse('<string>Hi</string>')
-+ self.assertTrue(tool.IsTranslatable(string_el))
-+ string_el = self.__Parse(
-+ '<string translatable="true">Hi</string>')
-+ self.assertTrue(tool.IsTranslatable(string_el))
-+ string_el = self.__Parse(
-+ '<string translatable="false">Hi</string>')
-+ self.assertFalse(tool.IsTranslatable(string_el))
-+
-+ def __ParseAndroidXml(self, options = []):
-+ tool = android2grd.Android2Grd()
-+
-+ tool.ParseOptions(options)
-+
-+ android_path = util.PathFromRoot('grit/testdata/android.xml')
-+ with open(android_path) as android_file:
-+ android_dom = xml.dom.minidom.parse(android_file)
-+
-+ grd = tool.AndroidDomToGrdDom(android_dom)
-+ self.assertTrue(isinstance(grd, misc.GritNode))
-+
-+ return grd
-+
-+ def testAndroidDomToGrdDom(self):
-+ grd = self.__ParseAndroidXml(['--languages', 'en-US,en-GB,ru'])
-+
-+ # Check that the structure of the GritNode is as expected.
-+ messages = grd.GetChildrenOfType(message.MessageNode)
-+ translations = grd.GetChildrenOfType(empty.TranslationsNode)
-+ files = grd.GetChildrenOfType(node_io.FileNode)
-+
-+ self.assertEqual(len(translations), 1)
-+ self.assertEqual(len(files), 3)
-+ self.assertEqual(len(messages), 5)
-+
-+ # Check that a message node is constructed correctly.
-+ msg = [x for x in messages if x.GetTextualIds()[0] == 'IDS_PLACEHOLDERS']
-+ self.assertTrue(msg)
-+ msg = msg[0]
-+
-+ self.assertTrue(msg.IsTranslateable())
-+ self.assertEqual(msg.attrs["desc"], "A string with placeholder.")
-+
-+ def testTranslatableAttribute(self):
-+ grd = self.__ParseAndroidXml([])
-+ messages = grd.GetChildrenOfType(message.MessageNode)
-+ msgs = [x for x in messages if x.GetTextualIds()[0] == 'IDS_CONSTANT']
-+ self.assertTrue(msgs)
-+ self.assertFalse(msgs[0].IsTranslateable())
-+
-+ def testTranslations(self):
-+ grd = self.__ParseAndroidXml(['--languages', 'en-US,en-GB,ru,id'])
-+
-+ files = grd.GetChildrenOfType(node_io.FileNode)
-+ us_file = [x for x in files if x.attrs['lang'] == 'en-US']
-+ self.assertTrue(us_file)
-+ self.assertEqual(us_file[0].GetInputPath(),
-+ 'chrome_android_strings_en-US.xtb')
-+
-+ id_file = [x for x in files if x.attrs['lang'] == 'id']
-+ self.assertTrue(id_file)
-+ self.assertEqual(id_file[0].GetInputPath(),
-+ 'chrome_android_strings_id.xtb')
-+
-+ def testOutputs(self):
-+ grd = self.__ParseAndroidXml(['--languages', 'en-US,ru,id',
-+ '--rc-dir', 'rc/dir',
-+ '--header-dir', 'header/dir',
-+ '--xtb-dir', 'xtb/dir',
-+ '--xml-dir', 'xml/dir'])
-+
-+ outputs = grd.GetChildrenOfType(node_io.OutputNode)
-+ self.assertEqual(len(outputs), 7)
-+
-+ header_outputs = [x for x in outputs if x.GetType() == 'rc_header']
-+ rc_outputs = [x for x in outputs if x.GetType() == 'rc_all']
-+ xml_outputs = [x for x in outputs if x.GetType() == 'android']
-+
-+ self.assertEqual(len(header_outputs), 1)
-+ self.assertEqual(len(rc_outputs), 3)
-+ self.assertEqual(len(xml_outputs), 3)
-+
-+ # The header node should have an "<emit>" child and the proper filename.
-+ self.assertTrue(header_outputs[0].GetChildrenOfType(node_io.EmitNode))
-+ self.assertEqual(util.normpath(header_outputs[0].GetFilename()),
-+ util.normpath('header/dir/chrome_android_strings.h'))
-+
-+ id_rc = [x for x in rc_outputs if x.GetLanguage() == 'id']
-+ id_xml = [x for x in xml_outputs if x.GetLanguage() == 'id']
-+ self.assertTrue(id_rc)
-+ self.assertTrue(id_xml)
-+ self.assertEqual(util.normpath(id_rc[0].GetFilename()),
-+ util.normpath('rc/dir/chrome_android_strings_id.rc'))
-+ self.assertEqual(util.normpath(id_xml[0].GetFilename()),
-+ util.normpath('xml/dir/values-in/strings.xml'))
-+
-+ us_rc = [x for x in rc_outputs if x.GetLanguage() == 'en-US']
-+ us_xml = [x for x in xml_outputs if x.GetLanguage() == 'en-US']
-+ self.assertTrue(us_rc)
-+ self.assertTrue(us_xml)
-+ self.assertEqual(util.normpath(us_rc[0].GetFilename()),
-+ util.normpath('rc/dir/chrome_android_strings_en-US.rc'))
-+ self.assertEqual(util.normpath(us_xml[0].GetFilename()),
-+ util.normpath('xml/dir/values-en-rUS/strings.xml'))
-+
-+
-+if __name__ == '__main__':
-+ unittest.main()
-diff --git a/tools/grit/grit/tool/build.py b/tools/grit/grit/tool/build.py
-new file mode 100644
-index 0000000000..204592bf0d
---- /dev/null
-+++ b/tools/grit/grit/tool/build.py
-@@ -0,0 +1,556 @@
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''The 'grit build' tool.
-+'''
-+
-+from __future__ import print_function
-+
-+import codecs
-+import filecmp
-+import getopt
-+import gzip
-+import os
-+import shutil
-+import sys
-+
-+import six
-+
-+from grit import grd_reader
-+from grit import shortcuts
-+from grit import util
-+from grit.format import minifier
-+from grit.node import brotli_util
-+from grit.node import include
-+from grit.node import message
-+from grit.node import structure
-+from grit.tool import interface
-+
-+
-+# It would be cleaner to have each module register itself, but that would
-+# require importing all of them on every run of GRIT.
-+'''Map from <output> node types to modules under grit.format.'''
-+_format_modules = {
-+ 'android': 'android_xml',
-+ 'c_format': 'c_format',
-+ 'chrome_messages_json': 'chrome_messages_json',
-+ 'chrome_messages_json_gzip': 'chrome_messages_json',
-+ 'data_package': 'data_pack',
-+ 'policy_templates': 'policy_templates_json',
-+ 'rc_all': 'rc',
-+ 'rc_header': 'rc_header',
-+ 'rc_nontranslateable': 'rc',
-+ 'rc_translateable': 'rc',
-+ 'resource_file_map_source': 'resource_map',
-+ 'resource_map_header': 'resource_map',
-+ 'resource_map_source': 'resource_map',
-+}
-+
-+def GetFormatter(type):
-+ modulename = 'grit.format.' + _format_modules[type]
-+ __import__(modulename)
-+ module = sys.modules[modulename]
-+ try:
-+ return module.Format
-+ except AttributeError:
-+ return module.GetFormatter(type)
-+
-+
-+class RcBuilder(interface.Tool):
-+ '''A tool that builds RC files and resource header files for compilation.
-+
-+Usage: grit build [-o OUTPUTDIR] [-D NAME[=VAL]]*
-+
-+All output options for this tool are specified in the input file (see
-+'grit help' for details on how to specify the input file - it is a global
-+option).
-+
-+Options:
-+
-+ -a FILE Assert that the given file is an output. There can be
-+ multiple "-a" flags listed for multiple outputs. If a "-a"
-+ or "--assert-file-list" argument is present, then the list
-+ of asserted files must match the output files or the tool
-+ will fail. The use-case is for the build system to maintain
-+ separate lists of output files and to catch errors if the
-+ build system's list and the grit list are out-of-sync.
-+
-+ --assert-file-list Provide a file listing multiple asserted output files.
-+ There is one file name per line. This acts like specifying
-+ each file with "-a" on the command line, but without the
-+ possibility of running into OS line-length limits for very
-+ long lists.
-+
-+ -o OUTPUTDIR Specify what directory output paths are relative to.
-+ Defaults to the current directory.
-+
-+ -p FILE Specify a file containing a pre-determined mapping from
-+ resource names to resource ids which will be used to assign
-+ resource ids to those resources. Resources not found in this
-+ file will be assigned ids normally. The motivation is to run
-+ your app's startup and have it dump the resources it loads,
-+ and then pass these via this flag. This will pack startup
-+ resources together, thus reducing paging while all other
-+ resources are unperturbed. The file should have the format:
-+ RESOURCE_ONE_NAME 123
-+ RESOURCE_TWO_NAME 124
-+
-+ -D NAME[=VAL] Specify a C-preprocessor-like define NAME with optional
-+ value VAL (defaults to 1) which will be used to control
-+ conditional inclusion of resources.
-+
-+ -E NAME=VALUE Set environment variable NAME to VALUE (within grit).
-+
-+ -f FIRSTIDSFILE Path to a python file that specifies the first id of
-+ value to use for resources. A non-empty value here will
-+ override the value specified in the <grit> node's
-+ first_ids_file.
-+
-+ -w WHITELISTFILE Path to a file containing the string names of the
-+ resources to include. Anything not listed is dropped.
-+
-+ -t PLATFORM Specifies the platform the build is targeting; defaults
-+ to the value of sys.platform. The value provided via this
-+ flag should match what sys.platform would report for your
-+ target platform; see grit.node.base.EvaluateCondition.
-+
-+ --whitelist-support
-+ Generate code to support extracting a resource whitelist
-+ from executables.
-+
-+ --write-only-new flag
-+ If flag is non-0, write output files to a temporary file
-+ first, and copy it to the real output only if the new file
-+ is different from the old file. This allows some build
-+ systems to realize that dependent build steps might be
-+ unnecessary, at the cost of comparing the output data at
-+ grit time.
-+
-+ --depend-on-stamp
-+ If specified along with --depfile and --depdir, the depfile
-+ generated will depend on a stampfile instead of the first
-+ output in the input .grd file.
-+
-+ --js-minifier A command to run the Javascript minifier. If not set then
-+ Javascript won't be minified. The command should read the
-+ original Javascript from standard input, and output the
-+ minified Javascript to standard output. A non-zero exit
-+ status will be taken as indicating failure.
-+
-+ --css-minifier A command to run the CSS minifier. If not set then CSS won't
-+ be minified. The command should read the original CSS from
-+ standard input, and output the minified CSS to standard
-+ output. A non-zero exit status will be taken as indicating
-+ failure.
-+
-+ --brotli The full path to the brotli executable generated by
-+ third_party/brotli/BUILD.gn, required if any entries use
-+ compress="brotli".
-+
-+Conditional inclusion of resources only affects the output of files which
-+control which resources get linked into a binary, e.g. it affects .rc files
-+meant for compilation but it does not affect resource header files (that define
-+IDs). This helps ensure that values of IDs stay the same, that all messages
-+are exported to translation interchange files (e.g. XMB files), etc.
-+'''
-+
-+ def ShortDescription(self):
-+ return 'A tool that builds RC files for compilation.'
-+
-+ def Run(self, opts, args):
-+ brotli_util.SetBrotliCommand(None)
-+ os.environ['cwd'] = os.getcwd()
-+ self.output_directory = '.'
-+ first_ids_file = None
-+ predetermined_ids_file = None
-+ whitelist_filenames = []
-+ assert_output_files = []
-+ target_platform = None
-+ depfile = None
-+ depdir = None
-+ whitelist_support = False
-+ write_only_new = False
-+ depend_on_stamp = False
-+ js_minifier = None
-+ css_minifier = None
-+ replace_ellipsis = True
-+ (own_opts, args) = getopt.getopt(
-+ args, 'a:p:o:D:E:f:w:t:',
-+ ('depdir=', 'depfile=', 'assert-file-list=', 'help',
-+ 'output-all-resource-defines', 'no-output-all-resource-defines',
-+ 'no-replace-ellipsis', 'depend-on-stamp', 'js-minifier=',
-+ 'css-minifier=', 'write-only-new=', 'whitelist-support', 'brotli='))
-+ for (key, val) in own_opts:
-+ if key == '-a':
-+ assert_output_files.append(val)
-+ elif key == '--assert-file-list':
-+ with open(val) as f:
-+ assert_output_files += f.read().splitlines()
-+ elif key == '-o':
-+ self.output_directory = val
-+ elif key == '-D':
-+ name, val = util.ParseDefine(val)
-+ self.defines[name] = val
-+ elif key == '-E':
-+ (env_name, env_value) = val.split('=', 1)
-+ os.environ[env_name] = env_value
-+ elif key == '-f':
-+ # TODO(joi@chromium.org): Remove this override once change
-+ # lands in WebKit.grd to specify the first_ids_file in the
-+ # .grd itself.
-+ first_ids_file = val
-+ elif key == '-w':
-+ whitelist_filenames.append(val)
-+ elif key == '--no-replace-ellipsis':
-+ replace_ellipsis = False
-+ elif key == '-p':
-+ predetermined_ids_file = val
-+ elif key == '-t':
-+ target_platform = val
-+ elif key == '--depdir':
-+ depdir = val
-+ elif key == '--depfile':
-+ depfile = val
-+ elif key == '--write-only-new':
-+ write_only_new = val != '0'
-+ elif key == '--depend-on-stamp':
-+ depend_on_stamp = True
-+ elif key == '--js-minifier':
-+ js_minifier = val
-+ elif key == '--css-minifier':
-+ css_minifier = val
-+ elif key == '--whitelist-support':
-+ whitelist_support = True
-+ elif key == '--brotli':
-+ brotli_util.SetBrotliCommand([os.path.abspath(val)])
-+ elif key == '--help':
-+ self.ShowUsage()
-+ sys.exit(0)
-+
-+ if len(args):
-+ print('This tool takes no tool-specific arguments.')
-+ return 2
-+ self.SetOptions(opts)
-+ self.VerboseOut('Output directory: %s (absolute path: %s)\n' %
-+ (self.output_directory,
-+ os.path.abspath(self.output_directory)))
-+
-+ if whitelist_filenames:
-+ self.whitelist_names = set()
-+ for whitelist_filename in whitelist_filenames:
-+ self.VerboseOut('Using whitelist: %s\n' % whitelist_filename);
-+ whitelist_contents = util.ReadFile(whitelist_filename, 'utf-8')
-+ self.whitelist_names.update(whitelist_contents.strip().split('\n'))
-+
-+ if js_minifier:
-+ minifier.SetJsMinifier(js_minifier)
-+
-+ if css_minifier:
-+ minifier.SetCssMinifier(css_minifier)
-+
-+ self.write_only_new = write_only_new
-+
-+ self.res = grd_reader.Parse(opts.input,
-+ debug=opts.extra_verbose,
-+ first_ids_file=first_ids_file,
-+ predetermined_ids_file=predetermined_ids_file,
-+ defines=self.defines,
-+ target_platform=target_platform)
-+
-+ # Set an output context so that conditionals can use defines during the
-+ # gathering stage; we use a dummy language here since we are not outputting
-+ # a specific language.
-+ self.res.SetOutputLanguage('en')
-+ self.res.SetWhitelistSupportEnabled(whitelist_support)
-+ self.res.RunGatherers()
-+
-+ # Replace ... with the single-character version. http://crbug.com/621772
-+ if replace_ellipsis:
-+ for node in self.res:
-+ if isinstance(node, message.MessageNode):
-+ node.SetReplaceEllipsis(True)
-+
-+ self.Process()
-+
-+ if assert_output_files:
-+ if not self.CheckAssertedOutputFiles(assert_output_files):
-+ return 2
-+
-+ if depfile and depdir:
-+ self.GenerateDepfile(depfile, depdir, first_ids_file, depend_on_stamp)
-+
-+ return 0
-+
-+ def __init__(self, defines=None):
-+ # Default file-creation function is codecs.open(). Only done to allow
-+ # overriding by unit test.
-+ self.fo_create = codecs.open
-+
-+ # key/value pairs of C-preprocessor like defines that are used for
-+ # conditional output of resources
-+ self.defines = defines or {}
-+
-+ # self.res is a fully-populated resource tree if Run()
-+ # has been called, otherwise None.
-+ self.res = None
-+
-+ # The set of names that are whitelisted to actually be included in the
-+ # output.
-+ self.whitelist_names = None
-+
-+ # Whether to compare outputs to their old contents before writing.
-+ self.write_only_new = False
-+
-+ @staticmethod
-+ def AddWhitelistTags(start_node, whitelist_names):
-+ # Walk the tree of nodes added attributes for the nodes that shouldn't
-+ # be written into the target files (skip markers).
-+ for node in start_node:
-+ # Same trick data_pack.py uses to see what nodes actually result in
-+ # real items.
-+ if (isinstance(node, include.IncludeNode) or
-+ isinstance(node, message.MessageNode) or
-+ isinstance(node, structure.StructureNode)):
-+ text_ids = node.GetTextualIds()
-+ # Mark the item to be skipped if it wasn't in the whitelist.
-+ if text_ids and text_ids[0] not in whitelist_names:
-+ node.SetWhitelistMarkedAsSkip(True)
-+
-+ @staticmethod
-+ def ProcessNode(node, output_node, outfile):
-+ '''Processes a node in-order, calling its formatter before and after
-+ recursing to its children.
-+
-+ Args:
-+ node: grit.node.base.Node subclass
-+ output_node: grit.node.io.OutputNode
-+ outfile: open filehandle
-+ '''
-+ base_dir = util.dirname(output_node.GetOutputFilename())
-+
-+ formatter = GetFormatter(output_node.GetType())
-+ formatted = formatter(node, output_node.GetLanguage(), output_dir=base_dir)
-+ # NB: Formatters may be generators or return lists. The writelines API
-+ # accepts iterables as a shortcut to calling write directly. That means
-+ # you can pass strings (iteration yields characters), but not bytes (as
-+ # iteration yields integers). Python 2 worked due to its quirks with
-+ # bytes/string implementation, but Python 3 fails. It's also a bit more
-+ # inefficient to call write once per character/byte. Handle all of this
-+ # ourselves by calling write directly on strings/bytes before falling back
-+ # to writelines.
-+ if isinstance(formatted, (six.string_types, six.binary_type)):
-+ outfile.write(formatted)
-+ else:
-+ outfile.writelines(formatted)
-+ if output_node.GetType() == 'data_package':
-+ with open(output_node.GetOutputFilename() + '.info', 'w') as infofile:
-+ if node.info:
-+ # We terminate with a newline so that when these files are
-+ # concatenated later we consistently terminate with a newline so
-+ # consumers can account for terminating newlines.
-+ infofile.writelines(['\n'.join(node.info), '\n'])
-+
-+ @staticmethod
-+ def _EncodingForOutputType(output_type):
-+ # Microsoft's RC compiler can only deal with single-byte or double-byte
-+ # files (no UTF-8), so we make all RC files UTF-16 to support all
-+ # character sets.
-+ if output_type in ('rc_header', 'resource_file_map_source',
-+ 'resource_map_header', 'resource_map_source'):
-+ return 'cp1252'
-+ if output_type in ('android', 'c_format', 'plist', 'plist_strings', 'doc',
-+ 'json', 'android_policy', 'chrome_messages_json',
-+ 'chrome_messages_json_gzip', 'policy_templates'):
-+ return 'utf_8'
-+ # TODO(gfeher) modify here to set utf-8 encoding for admx/adml
-+ return 'utf_16'
-+
-+ def Process(self):
-+ for output in self.res.GetOutputFiles():
-+ output.output_filename = os.path.abspath(os.path.join(
-+ self.output_directory, output.GetOutputFilename()))
-+
-+ # If there are whitelisted names, tag the tree once up front, this way
-+ # while looping through the actual output, it is just an attribute check.
-+ if self.whitelist_names:
-+ self.AddWhitelistTags(self.res, self.whitelist_names)
-+
-+ for output in self.res.GetOutputFiles():
-+ self.VerboseOut('Creating %s...' % output.GetOutputFilename())
-+
-+ # Set the context, for conditional inclusion of resources
-+ self.res.SetOutputLanguage(output.GetLanguage())
-+ self.res.SetOutputContext(output.GetContext())
-+ self.res.SetFallbackToDefaultLayout(output.GetFallbackToDefaultLayout())
-+ self.res.SetDefines(self.defines)
-+
-+ # Assign IDs only once to ensure that all outputs use the same IDs.
-+ if self.res.GetIdMap() is None:
-+ self.res.InitializeIds()
-+
-+ # Make the output directory if it doesn't exist.
-+ self.MakeDirectoriesTo(output.GetOutputFilename())
-+
-+ # Write the results to a temporary file and only overwrite the original
-+ # if the file changed. This avoids unnecessary rebuilds.
-+ out_filename = output.GetOutputFilename()
-+ tmp_filename = out_filename + '.tmp'
-+ tmpfile = self.fo_create(tmp_filename, 'wb')
-+
-+ output_type = output.GetType()
-+ if output_type != 'data_package':
-+ encoding = self._EncodingForOutputType(output_type)
-+ tmpfile = util.WrapOutputStream(tmpfile, encoding)
-+
-+ # Iterate in-order through entire resource tree, calling formatters on
-+ # the entry into a node and on exit out of it.
-+ with tmpfile:
-+ self.ProcessNode(self.res, output, tmpfile)
-+
-+ if output_type == 'chrome_messages_json_gzip':
-+ gz_filename = tmp_filename + '.gz'
-+ with open(tmp_filename, 'rb') as tmpfile, open(gz_filename, 'wb') as f:
-+ with gzip.GzipFile(filename='', mode='wb', fileobj=f, mtime=0) as fgz:
-+ shutil.copyfileobj(tmpfile, fgz)
-+ os.remove(tmp_filename)
-+ tmp_filename = gz_filename
-+
-+ # Now copy from the temp file back to the real output, but on Windows,
-+ # only if the real output doesn't exist or the contents of the file
-+ # changed. This prevents identical headers from being written and .cc
-+ # files from recompiling (which is painful on Windows).
-+ if not os.path.exists(out_filename):
-+ os.rename(tmp_filename, out_filename)
-+ else:
-+ # CHROMIUM SPECIFIC CHANGE.
-+ # This clashes with gyp + vstudio, which expect the output timestamp
-+ # to change on a rebuild, even if nothing has changed, so only do
-+ # it when opted in.
-+ if not self.write_only_new:
-+ write_file = True
-+ else:
-+ files_match = filecmp.cmp(out_filename, tmp_filename)
-+ write_file = not files_match
-+ if write_file:
-+ shutil.copy2(tmp_filename, out_filename)
-+ os.remove(tmp_filename)
-+
-+ self.VerboseOut(' done.\n')
-+
-+ # Print warnings if there are any duplicate shortcuts.
-+ warnings = shortcuts.GenerateDuplicateShortcutsWarnings(
-+ self.res.UberClique(), self.res.GetTcProject())
-+ if warnings:
-+ print('\n'.join(warnings))
-+
-+ # Print out any fallback warnings, and missing translation errors, and
-+ # exit with an error code if there are missing translations in a non-pseudo
-+ # and non-official build.
-+ warnings = (self.res.UberClique().MissingTranslationsReport().
-+ encode('ascii', 'replace'))
-+ if warnings:
-+ self.VerboseOut(warnings)
-+ if self.res.UberClique().HasMissingTranslations():
-+ print(self.res.UberClique().missing_translations_)
-+ sys.exit(-1)
-+
-+
-+ def CheckAssertedOutputFiles(self, assert_output_files):
-+ '''Checks that the asserted output files are specified in the given list.
-+
-+ Returns true if the asserted files are present. If they are not, returns
-+ False and prints the failure.
-+ '''
-+ # Compare the absolute path names, sorted.
-+ asserted = sorted([os.path.abspath(i) for i in assert_output_files])
-+ actual = sorted([
-+ os.path.abspath(os.path.join(self.output_directory,
-+ i.GetOutputFilename()))
-+ for i in self.res.GetOutputFiles()])
-+
-+ if asserted != actual:
-+ missing = list(set(asserted) - set(actual))
-+ extra = list(set(actual) - set(asserted))
-+ error = '''Asserted file list does not match.
-+
-+Expected output files:
-+%s
-+Actual output files:
-+%s
-+Missing output files:
-+%s
-+Extra output files:
-+%s
-+'''
-+ print(error % ('\n'.join(asserted), '\n'.join(actual), '\n'.join(missing),
-+ ' \n'.join(extra)))
-+ return False
-+ return True
-+
-+
-+ def GenerateDepfile(self, depfile, depdir, first_ids_file, depend_on_stamp):
-+ '''Generate a depfile that contains the imlicit dependencies of the input
-+ grd. The depfile will be in the same format as a makefile, and will contain
-+ references to files relative to |depdir|. It will be put in |depfile|.
-+
-+ For example, supposing we have three files in a directory src/
-+
-+ src/
-+ blah.grd <- depends on input{1,2}.xtb
-+ input1.xtb
-+ input2.xtb
-+
-+ and we run
-+
-+ grit -i blah.grd -o ../out/gen \
-+ --depdir ../out \
-+ --depfile ../out/gen/blah.rd.d
-+
-+ from the directory src/ we will generate a depfile ../out/gen/blah.grd.d
-+ that has the contents
-+
-+ gen/blah.h: ../src/input1.xtb ../src/input2.xtb
-+
-+ Where "gen/blah.h" is the first output (Ninja expects the .d file to list
-+ the first output in cases where there is more than one). If the flag
-+ --depend-on-stamp is specified, "gen/blah.rd.d.stamp" will be used that is
-+ 'touched' whenever a new depfile is generated.
-+
-+ Note that all paths in the depfile are relative to ../out, the depdir.
-+ '''
-+ depfile = os.path.abspath(depfile)
-+ depdir = os.path.abspath(depdir)
-+ infiles = self.res.GetInputFiles()
-+
-+ # We want to trigger a rebuild if the first ids change.
-+ if first_ids_file is not None:
-+ infiles.append(first_ids_file)
-+
-+ if (depend_on_stamp):
-+ output_file = depfile + ".stamp"
-+ # Touch the stamp file before generating the depfile.
-+ with open(output_file, 'a'):
-+ os.utime(output_file, None)
-+ else:
-+ # Get the first output file relative to the depdir.
-+ outputs = self.res.GetOutputFiles()
-+ output_file = os.path.join(self.output_directory,
-+ outputs[0].GetOutputFilename())
-+
-+ output_file = os.path.relpath(output_file, depdir)
-+ # The path prefix to prepend to dependencies in the depfile.
-+ prefix = os.path.relpath(os.getcwd(), depdir)
-+ deps_text = ' '.join([os.path.join(prefix, i) for i in infiles])
-+
-+ depfile_contents = output_file + ': ' + deps_text
-+ self.MakeDirectoriesTo(depfile)
-+ outfile = self.fo_create(depfile, 'w', encoding='utf-8')
-+ outfile.write(depfile_contents)
-+
-+ @staticmethod
-+ def MakeDirectoriesTo(file):
-+ '''Creates directories necessary to contain |file|.'''
-+ dir = os.path.split(file)[0]
-+ if not os.path.exists(dir):
-+ os.makedirs(dir)
-diff --git a/tools/grit/grit/tool/build_unittest.py b/tools/grit/grit/tool/build_unittest.py
-new file mode 100644
-index 0000000000..c4a2f2752b
---- /dev/null
-+++ b/tools/grit/grit/tool/build_unittest.py
-@@ -0,0 +1,341 @@
-+#!/usr/bin/env python
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Unit tests for the 'grit build' tool.
-+'''
-+
-+from __future__ import print_function
-+
-+import codecs
-+import os
-+import sys
-+if __name__ == '__main__':
-+ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
-+
-+import unittest
-+
-+from grit import util
-+from grit.tool import build
-+
-+
-+class BuildUnittest(unittest.TestCase):
-+
-+ # IDs should not change based on whitelisting.
-+ # Android WebView currently relies on this.
-+ EXPECTED_ID_MAP = {
-+ 'IDS_MESSAGE_WHITELISTED': 6889,
-+ 'IDR_STRUCTURE_WHITELISTED': 11546,
-+ 'IDR_STRUCTURE_IN_TRUE_IF_WHITELISTED': 11548,
-+ 'IDR_INCLUDE_WHITELISTED': 15601,
-+ }
-+
-+ def testFindTranslationsWithSubstitutions(self):
-+ # This is a regression test; we had a bug where GRIT would fail to find
-+ # messages with substitutions e.g. "Hello [IDS_USER]" where IDS_USER is
-+ # another <message>.
-+ output_dir = util.TempDir({})
-+ builder = build.RcBuilder()
-+ class DummyOpts(object):
-+ def __init__(self):
-+ self.input = util.PathFromRoot('grit/testdata/substitute.grd')
-+ self.verbose = False
-+ self.extra_verbose = False
-+ builder.Run(DummyOpts(), ['-o', output_dir.GetPath()])
-+ output_dir.CleanUp()
-+
-+ def testGenerateDepFile(self):
-+ output_dir = util.TempDir({})
-+ builder = build.RcBuilder()
-+ class DummyOpts(object):
-+ def __init__(self):
-+ self.input = util.PathFromRoot('grit/testdata/depfile.grd')
-+ self.verbose = False
-+ self.extra_verbose = False
-+ expected_dep_file = output_dir.GetPath('substitute.grd.d')
-+ builder.Run(DummyOpts(), ['-o', output_dir.GetPath(),
-+ '--depdir', output_dir.GetPath(),
-+ '--depfile', expected_dep_file])
-+
-+ self.failUnless(os.path.isfile(expected_dep_file))
-+ with open(expected_dep_file) as f:
-+ line = f.readline()
-+ (dep_output_file, deps_string) = line.split(': ')
-+ deps = deps_string.split(' ')
-+
-+ self.failUnlessEqual("default_100_percent.pak", dep_output_file)
-+ self.failUnlessEqual(deps, [
-+ util.PathFromRoot('grit/testdata/default_100_percent/a.png'),
-+ util.PathFromRoot('grit/testdata/grit_part.grdp'),
-+ util.PathFromRoot('grit/testdata/special_100_percent/a.png'),
-+ ])
-+ output_dir.CleanUp()
-+
-+ def testGenerateDepFileWithResourceIds(self):
-+ output_dir = util.TempDir({})
-+ builder = build.RcBuilder()
-+ class DummyOpts(object):
-+ def __init__(self):
-+ self.input = util.PathFromRoot('grit/testdata/substitute_no_ids.grd')
-+ self.verbose = False
-+ self.extra_verbose = False
-+ expected_dep_file = output_dir.GetPath('substitute_no_ids.grd.d')
-+ builder.Run(DummyOpts(),
-+ ['-f', util.PathFromRoot('grit/testdata/resource_ids'),
-+ '-o', output_dir.GetPath(),
-+ '--depdir', output_dir.GetPath(),
-+ '--depfile', expected_dep_file])
-+
-+ self.failUnless(os.path.isfile(expected_dep_file))
-+ with open(expected_dep_file) as f:
-+ line = f.readline()
-+ (dep_output_file, deps_string) = line.split(': ')
-+ deps = deps_string.split(' ')
-+
-+ self.failUnlessEqual("resource.h", dep_output_file)
-+ self.failUnlessEqual(2, len(deps))
-+ self.failUnlessEqual(deps[0],
-+ util.PathFromRoot('grit/testdata/substitute.xmb'))
-+ self.failUnlessEqual(deps[1],
-+ util.PathFromRoot('grit/testdata/resource_ids'))
-+ output_dir.CleanUp()
-+
-+ def testAssertOutputs(self):
-+ output_dir = util.TempDir({})
-+ class DummyOpts(object):
-+ def __init__(self):
-+ self.input = util.PathFromRoot('grit/testdata/substitute.grd')
-+ self.verbose = False
-+ self.extra_verbose = False
-+
-+ # Incomplete output file list should fail.
-+ builder_fail = build.RcBuilder()
-+ self.failUnlessEqual(2,
-+ builder_fail.Run(DummyOpts(), [
-+ '-o', output_dir.GetPath(),
-+ '-a', os.path.abspath(
-+ output_dir.GetPath('en_generated_resources.rc'))]))
-+
-+ # Complete output file list should succeed.
-+ builder_ok = build.RcBuilder()
-+ self.failUnlessEqual(0,
-+ builder_ok.Run(DummyOpts(), [
-+ '-o', output_dir.GetPath(),
-+ '-a', os.path.abspath(
-+ output_dir.GetPath('en_generated_resources.rc')),
-+ '-a', os.path.abspath(
-+ output_dir.GetPath('sv_generated_resources.rc')),
-+ '-a', os.path.abspath(output_dir.GetPath('resource.h'))]))
-+ output_dir.CleanUp()
-+
-+ def testAssertTemplateOutputs(self):
-+ output_dir = util.TempDir({})
-+ class DummyOpts(object):
-+ def __init__(self):
-+ self.input = util.PathFromRoot('grit/testdata/substitute_tmpl.grd')
-+ self.verbose = False
-+ self.extra_verbose = False
-+
-+ # Incomplete output file list should fail.
-+ builder_fail = build.RcBuilder()
-+ self.failUnlessEqual(2,
-+ builder_fail.Run(DummyOpts(), [
-+ '-o', output_dir.GetPath(),
-+ '-E', 'name=foo',
-+ '-a', os.path.abspath(output_dir.GetPath('en_foo_resources.rc'))]))
-+
-+ # Complete output file list should succeed.
-+ builder_ok = build.RcBuilder()
-+ self.failUnlessEqual(0,
-+ builder_ok.Run(DummyOpts(), [
-+ '-o', output_dir.GetPath(),
-+ '-E', 'name=foo',
-+ '-a', os.path.abspath(output_dir.GetPath('en_foo_resources.rc')),
-+ '-a', os.path.abspath(output_dir.GetPath('sv_foo_resources.rc')),
-+ '-a', os.path.abspath(output_dir.GetPath('resource.h'))]))
-+ output_dir.CleanUp()
-+
-+ def _verifyWhitelistedOutput(self,
-+ filename,
-+ whitelisted_ids,
-+ non_whitelisted_ids,
-+ encoding='utf8'):
-+ self.failUnless(os.path.exists(filename))
-+ whitelisted_ids_found = []
-+ non_whitelisted_ids_found = []
-+ with codecs.open(filename, encoding=encoding) as f:
-+ for line in f.readlines():
-+ for whitelisted_id in whitelisted_ids:
-+ if whitelisted_id in line:
-+ whitelisted_ids_found.append(whitelisted_id)
-+ if filename.endswith('.h'):
-+ numeric_id = int(line.split()[2])
-+ expected_numeric_id = self.EXPECTED_ID_MAP.get(whitelisted_id)
-+ self.assertEqual(
-+ expected_numeric_id, numeric_id,
-+ 'Numeric ID for {} was {} should be {}'.format(
-+ whitelisted_id, numeric_id, expected_numeric_id))
-+ for non_whitelisted_id in non_whitelisted_ids:
-+ if non_whitelisted_id in line:
-+ non_whitelisted_ids_found.append(non_whitelisted_id)
-+ self.longMessage = True
-+ self.assertEqual(whitelisted_ids,
-+ whitelisted_ids_found,
-+ '\nin file {}'.format(os.path.basename(filename)))
-+ non_whitelisted_msg = ('Non-Whitelisted IDs {} found in {}'
-+ .format(non_whitelisted_ids_found, os.path.basename(filename)))
-+ self.assertFalse(non_whitelisted_ids_found, non_whitelisted_msg)
-+
-+ def testWhitelistStrings(self):
-+ output_dir = util.TempDir({})
-+ builder = build.RcBuilder()
-+ class DummyOpts(object):
-+ def __init__(self):
-+ self.input = util.PathFromRoot('grit/testdata/whitelist_strings.grd')
-+ self.verbose = False
-+ self.extra_verbose = False
-+ whitelist_file = util.PathFromRoot('grit/testdata/whitelist.txt')
-+ builder.Run(DummyOpts(), ['-o', output_dir.GetPath(),
-+ '-w', whitelist_file])
-+ header = output_dir.GetPath('whitelist_test_resources.h')
-+ rc = output_dir.GetPath('en_whitelist_test_strings.rc')
-+
-+ whitelisted_ids = ['IDS_MESSAGE_WHITELISTED']
-+ non_whitelisted_ids = ['IDS_MESSAGE_NOT_WHITELISTED']
-+ self._verifyWhitelistedOutput(
-+ header,
-+ whitelisted_ids,
-+ non_whitelisted_ids,
-+ )
-+ self._verifyWhitelistedOutput(
-+ rc,
-+ whitelisted_ids,
-+ non_whitelisted_ids,
-+ encoding='utf16'
-+ )
-+ output_dir.CleanUp()
-+
-+ def testWhitelistResources(self):
-+ output_dir = util.TempDir({})
-+ builder = build.RcBuilder()
-+ class DummyOpts(object):
-+ def __init__(self):
-+ self.input = util.PathFromRoot('grit/testdata/whitelist_resources.grd')
-+ self.verbose = False
-+ self.extra_verbose = False
-+ whitelist_file = util.PathFromRoot('grit/testdata/whitelist.txt')
-+ builder.Run(DummyOpts(), ['-o', output_dir.GetPath(),
-+ '-w', whitelist_file])
-+ header = output_dir.GetPath('whitelist_test_resources.h')
-+ map_cc = output_dir.GetPath('whitelist_test_resources_map.cc')
-+ map_h = output_dir.GetPath('whitelist_test_resources_map.h')
-+ pak = output_dir.GetPath('whitelist_test_resources.pak')
-+
-+ # Ensure the resource map header and .pak files exist, but don't verify
-+ # their content.
-+ self.failUnless(os.path.exists(map_h))
-+ self.failUnless(os.path.exists(pak))
-+
-+ whitelisted_ids = [
-+ 'IDR_STRUCTURE_WHITELISTED',
-+ 'IDR_STRUCTURE_IN_TRUE_IF_WHITELISTED',
-+ 'IDR_INCLUDE_WHITELISTED',
-+ ]
-+ non_whitelisted_ids = [
-+ 'IDR_STRUCTURE_NOT_WHITELISTED',
-+ 'IDR_STRUCTURE_IN_TRUE_IF_NOT_WHITELISTED',
-+ 'IDR_STRUCTURE_IN_FALSE_IF_WHITELISTED',
-+ 'IDR_STRUCTURE_IN_FALSE_IF_NOT_WHITELISTED',
-+ 'IDR_INCLUDE_NOT_WHITELISTED',
-+ ]
-+ for output_file in (header, map_cc):
-+ self._verifyWhitelistedOutput(
-+ output_file,
-+ whitelisted_ids,
-+ non_whitelisted_ids,
-+ )
-+ output_dir.CleanUp()
-+
-+ def testWriteOnlyNew(self):
-+ output_dir = util.TempDir({})
-+ builder = build.RcBuilder()
-+ class DummyOpts(object):
-+ def __init__(self):
-+ self.input = util.PathFromRoot('grit/testdata/substitute.grd')
-+ self.verbose = False
-+ self.extra_verbose = False
-+ UNCHANGED = 10
-+ header = output_dir.GetPath('resource.h')
-+
-+ builder.Run(DummyOpts(), ['-o', output_dir.GetPath()])
-+ self.failUnless(os.path.exists(header))
-+ first_mtime = os.stat(header).st_mtime
-+
-+ os.utime(header, (UNCHANGED, UNCHANGED))
-+ builder.Run(DummyOpts(),
-+ ['-o', output_dir.GetPath(), '--write-only-new', '0'])
-+ self.failUnless(os.path.exists(header))
-+ second_mtime = os.stat(header).st_mtime
-+
-+ os.utime(header, (UNCHANGED, UNCHANGED))
-+ builder.Run(DummyOpts(),
-+ ['-o', output_dir.GetPath(), '--write-only-new', '1'])
-+ self.failUnless(os.path.exists(header))
-+ third_mtime = os.stat(header).st_mtime
-+
-+ self.assertTrue(abs(second_mtime - UNCHANGED) > 5)
-+ self.assertTrue(abs(third_mtime - UNCHANGED) < 5)
-+ output_dir.CleanUp()
-+
-+ def testGenerateDepFileWithDependOnStamp(self):
-+ output_dir = util.TempDir({})
-+ builder = build.RcBuilder()
-+ class DummyOpts(object):
-+ def __init__(self):
-+ self.input = util.PathFromRoot('grit/testdata/substitute.grd')
-+ self.verbose = False
-+ self.extra_verbose = False
-+ expected_dep_file_name = 'substitute.grd.d'
-+ expected_stamp_file_name = expected_dep_file_name + '.stamp'
-+ expected_dep_file = output_dir.GetPath(expected_dep_file_name)
-+ expected_stamp_file = output_dir.GetPath(expected_stamp_file_name)
-+ if os.path.isfile(expected_stamp_file):
-+ os.remove(expected_stamp_file)
-+ builder.Run(DummyOpts(), ['-o', output_dir.GetPath(),
-+ '--depdir', output_dir.GetPath(),
-+ '--depfile', expected_dep_file,
-+ '--depend-on-stamp'])
-+ self.failUnless(os.path.isfile(expected_stamp_file))
-+ first_mtime = os.stat(expected_stamp_file).st_mtime
-+
-+ # Reset mtime to very old.
-+ OLDTIME = 10
-+ os.utime(expected_stamp_file, (OLDTIME, OLDTIME))
-+
-+ builder.Run(DummyOpts(), ['-o', output_dir.GetPath(),
-+ '--depdir', output_dir.GetPath(),
-+ '--depfile', expected_dep_file,
-+ '--depend-on-stamp'])
-+ self.failUnless(os.path.isfile(expected_stamp_file))
-+ second_mtime = os.stat(expected_stamp_file).st_mtime
-+
-+ # Some OS have a 2s stat resolution window, so can't do a direct comparison.
-+ self.assertTrue((second_mtime - OLDTIME) > 5)
-+ self.assertTrue(abs(second_mtime - first_mtime) < 5)
-+
-+ self.failUnless(os.path.isfile(expected_dep_file))
-+ with open(expected_dep_file) as f:
-+ line = f.readline()
-+ (dep_output_file, deps_string) = line.split(': ')
-+ deps = deps_string.split(' ')
-+
-+ self.failUnlessEqual(expected_stamp_file_name, dep_output_file)
-+ self.failUnlessEqual(deps, [
-+ util.PathFromRoot('grit/testdata/substitute.xmb'),
-+ ])
-+ output_dir.CleanUp()
-+
-+
-+if __name__ == '__main__':
-+ unittest.main()
-diff --git a/tools/grit/grit/tool/buildinfo.py b/tools/grit/grit/tool/buildinfo.py
-new file mode 100644
-index 0000000000..7f8d1a3b04
---- /dev/null
-+++ b/tools/grit/grit/tool/buildinfo.py
-@@ -0,0 +1,78 @@
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+"""Output the list of files to be generated by GRIT from an input.
-+"""
-+
-+from __future__ import print_function
-+
-+import getopt
-+import os
-+import sys
-+
-+from grit import grd_reader
-+from grit.node import structure
-+from grit.tool import interface
-+
-+class DetermineBuildInfo(interface.Tool):
-+ """Determine what files will be read and output by GRIT.
-+Outputs the list of generated files and inputs used to stdout.
-+
-+Usage: grit buildinfo [-o DIR]
-+
-+The output directory is used for display only.
-+"""
-+
-+ def __init__(self):
-+ pass
-+
-+ def ShortDescription(self):
-+ """Describes this tool for the usage message."""
-+ return ('Determine what files will be needed and\n'
-+ 'output by GRIT with a given input.')
-+
-+ def Run(self, opts, args):
-+ """Main method for the buildinfo tool."""
-+ self.output_directory = '.'
-+ (own_opts, args) = getopt.getopt(args, 'o:', ('help',))
-+ for (key, val) in own_opts:
-+ if key == '-o':
-+ self.output_directory = val
-+ elif key == '--help':
-+ self.ShowUsage()
-+ sys.exit(0)
-+ if len(args) > 0:
-+ print('This tool takes exactly one argument: the output directory via -o')
-+ return 2
-+ self.SetOptions(opts)
-+
-+ res_tree = grd_reader.Parse(opts.input, debug=opts.extra_verbose)
-+
-+ langs = {}
-+ for output in res_tree.GetOutputFiles():
-+ if output.attrs['lang']:
-+ langs[output.attrs['lang']] = os.path.dirname(output.GetFilename())
-+
-+ for lang, dirname in langs.items():
-+ old_output_language = res_tree.output_language
-+ res_tree.SetOutputLanguage(lang)
-+ for node in res_tree.ActiveDescendants():
-+ with node:
-+ if (isinstance(node, structure.StructureNode) and
-+ node.HasFileForLanguage()):
-+ path = node.FileForLanguage(lang, dirname, create_file=False,
-+ return_if_not_generated=False)
-+ if path:
-+ path = os.path.join(self.output_directory, path)
-+ path = os.path.normpath(path)
-+ print('%s|%s' % ('rc_all', path))
-+ res_tree.SetOutputLanguage(old_output_language)
-+
-+ for output in res_tree.GetOutputFiles():
-+ path = os.path.join(self.output_directory, output.GetFilename())
-+ path = os.path.normpath(path)
-+ print('%s|%s' % (output.GetType(), path))
-+
-+ for infile in res_tree.GetInputFiles():
-+ print('input|%s' % os.path.normpath(infile))
-diff --git a/tools/grit/grit/tool/buildinfo_unittest.py b/tools/grit/grit/tool/buildinfo_unittest.py
-new file mode 100644
-index 0000000000..24e9ddf8d8
---- /dev/null
-+++ b/tools/grit/grit/tool/buildinfo_unittest.py
-@@ -0,0 +1,90 @@
-+#!/usr/bin/env python
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+"""Unit tests for the 'grit buildinfo' tool.
-+"""
-+
-+from __future__ import print_function
-+
-+import os
-+import sys
-+import unittest
-+
-+# This is needed to find some of the imports below.
-+if __name__ == '__main__':
-+ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
-+
-+from six import StringIO
-+
-+# pylint: disable-msg=C6204
-+from grit.tool import buildinfo
-+
-+
-+class BuildInfoUnittest(unittest.TestCase):
-+ def setUp(self):
-+ self.old_cwd = os.getcwd()
-+ # Change CWD to make tests work independently of callers CWD.
-+ os.chdir(os.path.dirname(__file__))
-+ os.chdir('..')
-+ self.buf = StringIO()
-+ self.old_stdout = sys.stdout
-+ sys.stdout = self.buf
-+
-+ def tearDown(self):
-+ sys.stdout = self.old_stdout
-+ os.chdir(self.old_cwd)
-+
-+ def testBuildOutput(self):
-+ """Find all of the inputs and outputs for a GRD file."""
-+ info_object = buildinfo.DetermineBuildInfo()
-+
-+ class DummyOpts(object):
-+ def __init__(self):
-+ self.input = '../grit/testdata/buildinfo.grd'
-+ self.print_header = False
-+ self.verbose = False
-+ self.extra_verbose = False
-+ info_object.Run(DummyOpts(), [])
-+ output = self.buf.getvalue().replace('\\', '/')
-+ self.failUnless(output.count(r'rc_all|sv_sidebar_loading.html'))
-+ self.failUnless(output.count(r'rc_header|resource.h'))
-+ self.failUnless(output.count(r'rc_all|en_generated_resources.rc'))
-+ self.failUnless(output.count(r'rc_all|sv_generated_resources.rc'))
-+ self.failUnless(output.count(r'input|../grit/testdata/substitute.xmb'))
-+ self.failUnless(output.count(r'input|../grit/testdata/pr.bmp'))
-+ self.failUnless(output.count(r'input|../grit/testdata/pr2.bmp'))
-+ self.failUnless(
-+ output.count(r'input|../grit/testdata/sidebar_loading.html'))
-+ self.failUnless(output.count(r'input|../grit/testdata/transl.rc'))
-+ self.failUnless(output.count(r'input|../grit/testdata/transl1.rc'))
-+
-+ def testBuildOutputWithDir(self):
-+ """Find all the inputs and outputs for a GRD file with an output dir."""
-+ info_object = buildinfo.DetermineBuildInfo()
-+
-+ class DummyOpts(object):
-+ def __init__(self):
-+ self.input = '../grit/testdata/buildinfo.grd'
-+ self.print_header = False
-+ self.verbose = False
-+ self.extra_verbose = False
-+ info_object.Run(DummyOpts(), ['-o', '../grit/testdata'])
-+ output = self.buf.getvalue().replace('\\', '/')
-+ self.failUnless(
-+ output.count(r'rc_all|../grit/testdata/sv_sidebar_loading.html'))
-+ self.failUnless(output.count(r'rc_header|../grit/testdata/resource.h'))
-+ self.failUnless(
-+ output.count(r'rc_all|../grit/testdata/en_generated_resources.rc'))
-+ self.failUnless(
-+ output.count(r'rc_all|../grit/testdata/sv_generated_resources.rc'))
-+ self.failUnless(output.count(r'input|../grit/testdata/substitute.xmb'))
-+ self.failUnlessEqual(0,
-+ output.count(r'rc_all|../grit/testdata/sv_welcome_toast.html'))
-+ self.failUnless(
-+ output.count(r'rc_all|../grit/testdata/en_welcome_toast.html'))
-+
-+
-+if __name__ == '__main__':
-+ unittest.main()
-diff --git a/tools/grit/grit/tool/count.py b/tools/grit/grit/tool/count.py
-new file mode 100644
-index 0000000000..ab37f2ddb3
---- /dev/null
-+++ b/tools/grit/grit/tool/count.py
-@@ -0,0 +1,52 @@
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Count number of occurrences of a given message ID.'''
-+
-+from __future__ import print_function
-+
-+import getopt
-+import sys
-+
-+from grit import grd_reader
-+from grit.tool import interface
-+
-+
-+class CountMessage(interface.Tool):
-+ '''Count the number of times a given message ID is used.'''
-+
-+ def __init__(self):
-+ pass
-+
-+ def ShortDescription(self):
-+ return 'Count the number of times a given message ID is used.'
-+
-+ def ParseOptions(self, args):
-+ """Set this objects and return all non-option arguments."""
-+ own_opts, args = getopt.getopt(args, '', ('help',))
-+ for key, val in own_opts:
-+ if key == '--help':
-+ self.ShowUsage()
-+ sys.exit(0)
-+ return args
-+
-+ def Run(self, opts, args):
-+ args = self.ParseOptions(args)
-+ if len(args) != 1:
-+ print('This tool takes a single tool-specific argument, the message '
-+ 'ID to count.')
-+ return 2
-+ self.SetOptions(opts)
-+
-+ id = args[0]
-+ res_tree = grd_reader.Parse(opts.input, debug=opts.extra_verbose)
-+ res_tree.OnlyTheseTranslations([])
-+ res_tree.RunGatherers()
-+
-+ count = 0
-+ for c in res_tree.UberClique().AllCliques():
-+ if c.GetId() == id:
-+ count += 1
-+
-+ print("There are %d occurrences of message %s." % (count, id))
-diff --git a/tools/grit/grit/tool/diff_structures.py b/tools/grit/grit/tool/diff_structures.py
-new file mode 100644
-index 0000000000..d69e009b58
---- /dev/null
-+++ b/tools/grit/grit/tool/diff_structures.py
-@@ -0,0 +1,119 @@
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''The 'grit sdiff' tool.
-+'''
-+
-+from __future__ import print_function
-+
-+import os
-+import getopt
-+import sys
-+import tempfile
-+
-+from grit.node import structure
-+from grit.tool import interface
-+
-+from grit import constants
-+from grit import util
-+
-+# Builds the description for the tool (used as the __doc__
-+# for the DiffStructures class).
-+_class_doc = """\
-+Allows you to view the differences in the structure of two files,
-+disregarding their translateable content. Translateable portions of
-+each file are changed to the string "TTTTTT" before invoking the diff program
-+specified by the P4DIFF environment variable.
-+
-+Usage: grit sdiff [-t TYPE] [-s SECTION] [-e ENCODING] LEFT RIGHT
-+
-+LEFT and RIGHT are the files you want to diff. SECTION is required
-+for structure types like 'dialog' to identify the part of the file to look at.
-+ENCODING indicates the encoding of the left and right files (default 'cp1252').
-+TYPE can be one of the following, defaults to 'tr_html':
-+"""
-+for gatherer in structure._GATHERERS:
-+ _class_doc += " - %s\n" % gatherer
-+
-+
-+class DiffStructures(interface.Tool):
-+ __doc__ = _class_doc
-+
-+ def __init__(self):
-+ self.section = None
-+ self.left_encoding = 'cp1252'
-+ self.right_encoding = 'cp1252'
-+ self.structure_type = 'tr_html'
-+
-+ def ShortDescription(self):
-+ return 'View differences without regard for translateable portions.'
-+
-+ def Run(self, global_opts, args):
-+ (opts, args) = getopt.getopt(args, 's:e:t:',
-+ ('help', 'left_encoding=', 'right_encoding='))
-+ for key, val in opts:
-+ if key == '-s':
-+ self.section = val
-+ elif key == '-e':
-+ self.left_encoding = val
-+ self.right_encoding = val
-+ elif key == '-t':
-+ self.structure_type = val
-+ elif key == '--left_encoding':
-+ self.left_encoding = val
-+ elif key == '--right_encoding':
-+ self.right_encoding == val
-+ elif key == '--help':
-+ self.ShowUsage()
-+ sys.exit(0)
-+
-+ if len(args) != 2:
-+ print("Incorrect usage - 'grit help sdiff' for usage details.")
-+ return 2
-+
-+ if 'P4DIFF' not in os.environ:
-+ print("Environment variable P4DIFF not set; defaulting to 'windiff'.")
-+ diff_program = 'windiff'
-+ else:
-+ diff_program = os.environ['P4DIFF']
-+
-+ left_trans = self.MakeStaticTranslation(args[0], self.left_encoding)
-+ try:
-+ try:
-+ right_trans = self.MakeStaticTranslation(args[1], self.right_encoding)
-+
-+ os.system('%s %s %s' % (diff_program, left_trans, right_trans))
-+ finally:
-+ os.unlink(right_trans)
-+ finally:
-+ os.unlink(left_trans)
-+
-+ def MakeStaticTranslation(self, original_filename, encoding):
-+ """Given the name of the structure type (self.structure_type), the filename
-+ of the file holding the original structure, and optionally the "section" key
-+ identifying the part of the file to look at (self.section), creates a
-+ temporary file holding a "static" translation of the original structure
-+ (i.e. one where all translateable parts have been replaced with "TTTTTT")
-+ and returns the temporary file name. It is the caller's responsibility to
-+ delete the file when finished.
-+
-+ Args:
-+ original_filename: 'c:\\bingo\\bla.rc'
-+
-+ Return:
-+ 'c:\\temp\\werlkjsdf334.tmp'
-+ """
-+ original = structure._GATHERERS[self.structure_type](original_filename,
-+ extkey=self.section,
-+ encoding=encoding)
-+ original.Parse()
-+ translated = original.Translate(constants.CONSTANT_LANGUAGE, False)
-+
-+ fname = tempfile.mktemp()
-+ with util.WrapOutputStream(open(fname, 'wb')) as writer:
-+ writer.write("Original filename: %s\n=============\n\n"
-+ % original_filename)
-+ writer.write(translated) # write in UTF-8
-+
-+ return fname
-diff --git a/tools/grit/grit/tool/diff_structures_unittest.py b/tools/grit/grit/tool/diff_structures_unittest.py
-new file mode 100644
-index 0000000000..a6d7585761
---- /dev/null
-+++ b/tools/grit/grit/tool/diff_structures_unittest.py
-@@ -0,0 +1,46 @@
-+#!/usr/bin/env python
-+# 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.
-+
-+'''Unit tests for the 'grit newgrd' tool.'''
-+
-+from __future__ import print_function
-+
-+import os
-+import sys
-+if __name__ == '__main__':
-+ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
-+
-+import unittest
-+
-+from grit.tool import diff_structures
-+
-+
-+class DummyOpts(object):
-+ """Options needed by NewGrd."""
-+
-+
-+class DiffStructuresUnittest(unittest.TestCase):
-+
-+ def testMissingFiles(self):
-+ """Verify failure w/out file inputs."""
-+ tool = diff_structures.DiffStructures()
-+ ret = tool.Run(DummyOpts(), [])
-+ self.assertIsNotNone(ret)
-+ self.assertGreater(ret, 0)
-+
-+ ret = tool.Run(DummyOpts(), ['left'])
-+ self.assertIsNotNone(ret)
-+ self.assertGreater(ret, 0)
-+
-+ def testTooManyArgs(self):
-+ """Verify failure w/too many inputs."""
-+ tool = diff_structures.DiffStructures()
-+ ret = tool.Run(DummyOpts(), ['a', 'b', 'c'])
-+ self.assertIsNotNone(ret)
-+ self.assertGreater(ret, 0)
-+
-+
-+if __name__ == '__main__':
-+ unittest.main()
-diff --git a/tools/grit/grit/tool/interface.py b/tools/grit/grit/tool/interface.py
-new file mode 100644
-index 0000000000..e923205223
---- /dev/null
-+++ b/tools/grit/grit/tool/interface.py
-@@ -0,0 +1,62 @@
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Base class and interface for tools.
-+'''
-+
-+from __future__ import print_function
-+
-+class Tool(object):
-+ '''Base class for all tools. Tools should use their docstring (i.e. the
-+ class-level docstring) for the help they want to have printed when they
-+ are invoked.'''
-+
-+ #
-+ # Interface (abstract methods)
-+ #
-+
-+ def ShortDescription(self):
-+ '''Returns a short description of the functionality of the tool.'''
-+ raise NotImplementedError()
-+
-+ def Run(self, global_options, my_arguments):
-+ '''Runs the tool.
-+
-+ Args:
-+ global_options: object grit_runner.Options
-+ my_arguments: [arg1 arg2 ...]
-+
-+ Return:
-+ 0 for success, non-0 for error
-+ '''
-+ raise NotImplementedError()
-+
-+ #
-+ # Base class implementation
-+ #
-+
-+ def __init__(self):
-+ self.o = None
-+
-+ def ShowUsage(self):
-+ '''Show usage text for this tool.'''
-+ print(self.__doc__)
-+
-+ def SetOptions(self, opts):
-+ self.o = opts
-+
-+ def Out(self, text):
-+ '''Always writes out 'text'.'''
-+ self.o.output_stream.write(text)
-+
-+ def VerboseOut(self, text):
-+ '''Writes out 'text' if the verbose option is on.'''
-+ if self.o.verbose:
-+ self.o.output_stream.write(text)
-+
-+ def ExtraVerboseOut(self, text):
-+ '''Writes out 'text' if the extra-verbose option is on.
-+ '''
-+ if self.o.extra_verbose:
-+ self.o.output_stream.write(text)
-diff --git a/tools/grit/grit/tool/menu_from_parts.py b/tools/grit/grit/tool/menu_from_parts.py
-new file mode 100644
-index 0000000000..fcec26c5b1
---- /dev/null
-+++ b/tools/grit/grit/tool/menu_from_parts.py
-@@ -0,0 +1,79 @@
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''The 'grit menufromparts' tool.'''
-+
-+from __future__ import print_function
-+
-+import six
-+
-+from grit import grd_reader
-+from grit import util
-+from grit import xtb_reader
-+from grit.tool import interface
-+from grit.tool import transl2tc
-+
-+import grit.extern.tclib
-+
-+
-+class MenuTranslationsFromParts(interface.Tool):
-+ '''One-off tool to generate translated menu messages (where each menu is kept
-+in a single message) based on existing translations of the individual menu
-+items. Was needed when changing menus from being one message per menu item
-+to being one message for the whole menu.'''
-+
-+ def ShortDescription(self):
-+ return ('Create translations of whole menus from existing translations of '
-+ 'menu items.')
-+
-+ def Run(self, globopt, args):
-+ self.SetOptions(globopt)
-+ assert len(args) == 2, "Need exactly two arguments, the XTB file and the output file"
-+
-+ xtb_file = args[0]
-+ output_file = args[1]
-+
-+ grd = grd_reader.Parse(self.o.input, debug=self.o.extra_verbose)
-+ grd.OnlyTheseTranslations([]) # don't load translations
-+ grd.RunGatherers()
-+
-+ xtb = {}
-+ def Callback(msg_id, parts):
-+ msg = []
-+ for part in parts:
-+ if part[0]:
-+ msg = []
-+ break # it had a placeholder so ignore it
-+ else:
-+ msg.append(part[1])
-+ if len(msg):
-+ xtb[msg_id] = ''.join(msg)
-+ with open(xtb_file, 'rb') as f:
-+ xtb_reader.Parse(f, Callback)
-+
-+ translations = [] # list of translations as per transl2tc.WriteTranslations
-+ for node in grd:
-+ if node.name == 'structure' and node.attrs['type'] == 'menu':
-+ assert len(node.GetCliques()) == 1
-+ message = node.GetCliques()[0].GetMessage()
-+ translation = []
-+
-+ contents = message.GetContent()
-+ for part in contents:
-+ if isinstance(part, six.string_types):
-+ id = grit.extern.tclib.GenerateMessageId(part)
-+ if id not in xtb:
-+ print("WARNING didn't find all translations for menu %s" %
-+ (node.attrs['name'],))
-+ translation = []
-+ break
-+ translation.append(xtb[id])
-+ else:
-+ translation.append(part.GetPresentation())
-+
-+ if len(translation):
-+ translations.append([message.GetId(), ''.join(translation)])
-+
-+ with util.WrapOutputStream(open(output_file, 'wb')) as f:
-+ transl2tc.TranslationToTc.WriteTranslations(f, translations)
-diff --git a/tools/grit/grit/tool/newgrd.py b/tools/grit/grit/tool/newgrd.py
-new file mode 100644
-index 0000000000..66a18e9c04
---- /dev/null
-+++ b/tools/grit/grit/tool/newgrd.py
-@@ -0,0 +1,85 @@
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Tool to create a new, empty .grd file with all the basic sections.
-+'''
-+
-+from __future__ import print_function
-+
-+import getopt
-+import sys
-+
-+from grit.tool import interface
-+from grit import constants
-+from grit import util
-+
-+# The contents of the new .grd file
-+_FILE_CONTENTS = '''\
-+<?xml version="1.0" encoding="UTF-8"?>
-+<grit base_dir="." latest_public_release="0" current_release="1"
-+ source_lang_id="en" enc_check="%s">
-+ <outputs>
-+ <!-- TODO add each of your output files. Modify the three below, and add
-+ your own for your various languages. See the user's guide for more
-+ details.
-+ Note that all output references are relative to the output directory
-+ which is specified at build time. -->
-+ <output filename="resource.h" type="rc_header" />
-+ <output filename="en_resource.rc" type="rc_all" />
-+ <output filename="fr_resource.rc" type="rc_all" />
-+ </outputs>
-+ <translations>
-+ <!-- TODO add references to each of the XTB files (from the Translation
-+ Console) that contain translations of messages in your project. Each
-+ takes a form like <file path="english.xtb" />. Remember that all file
-+ references are relative to this .grd file. -->
-+ </translations>
-+ <release seq="1">
-+ <includes>
-+ <!-- TODO add a list of your included resources here, e.g. BMP and GIF
-+ resources. -->
-+ </includes>
-+ <structures>
-+ <!-- TODO add a list of all your structured resources here, e.g. HTML
-+ templates, menus, dialogs etc. Note that for menus, dialogs and version
-+ information resources you reference an .rc file containing them.-->
-+ </structures>
-+ <messages>
-+ <!-- TODO add all of your "string table" messages here. Remember to
-+ change nontranslateable parts of the messages into placeholders (using the
-+ <ph> element). You can also use the 'grit add' tool to help you identify
-+ nontranslateable parts and create placeholders for them. -->
-+ </messages>
-+ </release>
-+</grit>''' % constants.ENCODING_CHECK
-+
-+
-+class NewGrd(interface.Tool):
-+ '''Usage: grit newgrd OUTPUT_FILE
-+
-+Creates a new, empty .grd file OUTPUT_FILE with comments about what to put
-+where in the file.'''
-+
-+ def ShortDescription(self):
-+ return 'Create a new empty .grd file.'
-+
-+ def ParseOptions(self, args):
-+ """Set this objects and return all non-option arguments."""
-+ own_opts, args = getopt.getopt(args, '', ('help',))
-+ for key, val in own_opts:
-+ if key == '--help':
-+ self.ShowUsage()
-+ sys.exit(0)
-+ return args
-+
-+ def Run(self, opts, args):
-+ args = self.ParseOptions(args)
-+ if len(args) != 1:
-+ print('This tool requires exactly one argument, the name of the output '
-+ 'file.')
-+ return 2
-+ filename = args[0]
-+ with util.WrapOutputStream(open(filename, 'wb'), 'utf-8') as out:
-+ out.write(_FILE_CONTENTS)
-+ print("Wrote file %s" % filename)
-diff --git a/tools/grit/grit/tool/newgrd_unittest.py b/tools/grit/grit/tool/newgrd_unittest.py
-new file mode 100644
-index 0000000000..f7c8831df5
---- /dev/null
-+++ b/tools/grit/grit/tool/newgrd_unittest.py
-@@ -0,0 +1,51 @@
-+#!/usr/bin/env python
-+# 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.
-+
-+'''Unit tests for the 'grit newgrd' tool.'''
-+
-+from __future__ import print_function
-+
-+import os
-+import sys
-+if __name__ == '__main__':
-+ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
-+
-+import unittest
-+
-+from grit import util
-+from grit.tool import newgrd
-+
-+
-+class DummyOpts(object):
-+ """Options needed by NewGrd."""
-+
-+
-+class NewgrdUnittest(unittest.TestCase):
-+
-+ def testNewFile(self):
-+ """Create a new file."""
-+ tool = newgrd.NewGrd()
-+ with util.TempDir({}) as output_dir:
-+ output_file = os.path.join(output_dir.GetPath(), 'new.grd')
-+ self.assertIsNone(tool.Run(DummyOpts(), [output_file]))
-+ self.assertTrue(os.path.exists(output_file))
-+
-+ def testMissingFile(self):
-+ """Verify failure w/out file output."""
-+ tool = newgrd.NewGrd()
-+ ret = tool.Run(DummyOpts(), [])
-+ self.assertIsNotNone(ret)
-+ self.assertGreater(ret, 0)
-+
-+ def testTooManyArgs(self):
-+ """Verify failure w/too many outputs."""
-+ tool = newgrd.NewGrd()
-+ ret = tool.Run(DummyOpts(), ['a', 'b'])
-+ self.assertIsNotNone(ret)
-+ self.assertGreater(ret, 0)
-+
-+
-+if __name__ == '__main__':
-+ unittest.main()
-diff --git a/tools/grit/grit/tool/postprocess_interface.py b/tools/grit/grit/tool/postprocess_interface.py
-new file mode 100644
-index 0000000000..4bb8c5871f
---- /dev/null
-+++ b/tools/grit/grit/tool/postprocess_interface.py
-@@ -0,0 +1,29 @@
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+''' Base class for postprocessing of RC files.
-+'''
-+
-+from __future__ import print_function
-+
-+class PostProcessor(object):
-+ ''' Base class for postprocessing of the RC file data before being
-+ output through the RC2GRD tool. You should implement this class if
-+ you want GRIT to do specific things to the RC files after it has
-+ converted the data into GRD format, i.e. change the content of the
-+ RC file, and put it into a P4 changelist, etc.'''
-+
-+
-+ def Process(self, rctext, rcpath, grdnode):
-+ ''' Processes the data in rctext and grdnode.
-+ Args:
-+ rctext: string containing the contents of the RC file being processed.
-+ rcpath: the path used to access the file.
-+ grdtext: the root node of the grd xml data generated by
-+ the rc2grd tool.
-+
-+ Return:
-+ The root node of the processed GRD tree.
-+ '''
-+ raise NotImplementedError()
-diff --git a/tools/grit/grit/tool/postprocess_unittest.py b/tools/grit/grit/tool/postprocess_unittest.py
-new file mode 100644
-index 0000000000..77fe228bbe
---- /dev/null
-+++ b/tools/grit/grit/tool/postprocess_unittest.py
-@@ -0,0 +1,64 @@
-+#!/usr/bin/env python
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Unit test that checks postprocessing of files.
-+ Tests postprocessing by having the postprocessor
-+ modify the grd data tree, changing the message name attributes.
-+'''
-+
-+from __future__ import print_function
-+
-+import os
-+import re
-+import sys
-+if __name__ == '__main__':
-+ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
-+
-+import unittest
-+
-+import grit.tool.postprocess_interface
-+from grit.tool import rc2grd
-+
-+
-+class PostProcessingUnittest(unittest.TestCase):
-+
-+ def testPostProcessing(self):
-+ rctext = '''STRINGTABLE
-+BEGIN
-+ DUMMY_STRING_1 "String 1"
-+ // Some random description
-+ DUMMY_STRING_2 "This text was added during preprocessing"
-+END
-+ '''
-+ tool = rc2grd.Rc2Grd()
-+ class DummyOpts(object):
-+ verbose = False
-+ extra_verbose = False
-+ tool.o = DummyOpts()
-+ tool.post_process = 'grit.tool.postprocess_unittest.DummyPostProcessor'
-+ result = tool.Process(rctext, '.\resource.rc')
-+
-+ self.failUnless(
-+ result.children[2].children[2].children[0].attrs['name'] == 'SMART_STRING_1')
-+ self.failUnless(
-+ result.children[2].children[2].children[1].attrs['name'] == 'SMART_STRING_2')
-+
-+class DummyPostProcessor(grit.tool.postprocess_interface.PostProcessor):
-+ '''
-+ Post processing replaces all message name attributes containing "DUMMY" to
-+ "SMART".
-+ '''
-+ def Process(self, rctext, rcpath, grdnode):
-+ smarter = re.compile(r'(DUMMY)(.*)')
-+ messages = grdnode.children[2].children[2]
-+ for node in messages.children:
-+ name_attr = node.attrs['name']
-+ m = smarter.search(name_attr)
-+ if m:
-+ node.attrs['name'] = 'SMART' + m.group(2)
-+ return grdnode
-+
-+if __name__ == '__main__':
-+ unittest.main()
-diff --git a/tools/grit/grit/tool/preprocess_interface.py b/tools/grit/grit/tool/preprocess_interface.py
-new file mode 100644
-index 0000000000..67974e704e
---- /dev/null
-+++ b/tools/grit/grit/tool/preprocess_interface.py
-@@ -0,0 +1,25 @@
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+''' Base class for preprocessing of RC files.
-+'''
-+
-+from __future__ import print_function
-+
-+class PreProcessor(object):
-+ ''' Base class for preprocessing of the RC file data before being
-+ output through the RC2GRD tool. You should implement this class if
-+ you have specific constructs in your RC files that GRIT cannot handle.'''
-+
-+
-+ def Process(self, rctext, rcpath):
-+ ''' Processes the data in rctext.
-+ Args:
-+ rctext: string containing the contents of the RC file being processed
-+ rcpath: the path used to access the file.
-+
-+ Return:
-+ The processed text.
-+ '''
-+ raise NotImplementedError()
-diff --git a/tools/grit/grit/tool/preprocess_unittest.py b/tools/grit/grit/tool/preprocess_unittest.py
-new file mode 100644
-index 0000000000..40b95cd6f8
---- /dev/null
-+++ b/tools/grit/grit/tool/preprocess_unittest.py
-@@ -0,0 +1,50 @@
-+#!/usr/bin/env python
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Unit test that checks preprocessing of files.
-+ Tests preprocessing by adding having the preprocessor
-+ provide the actual rctext data.
-+'''
-+
-+from __future__ import print_function
-+
-+import os
-+import sys
-+if __name__ == '__main__':
-+ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
-+
-+import unittest
-+
-+import grit.tool.preprocess_interface
-+from grit.tool import rc2grd
-+
-+
-+class PreProcessingUnittest(unittest.TestCase):
-+
-+ def testPreProcessing(self):
-+ tool = rc2grd.Rc2Grd()
-+ class DummyOpts(object):
-+ verbose = False
-+ extra_verbose = False
-+ tool.o = DummyOpts()
-+ tool.pre_process = 'grit.tool.preprocess_unittest.DummyPreProcessor'
-+ result = tool.Process('', '.\resource.rc')
-+
-+ self.failUnless(
-+ result.children[2].children[2].children[0].attrs['name'] == 'DUMMY_STRING_1')
-+
-+class DummyPreProcessor(grit.tool.preprocess_interface.PreProcessor):
-+ def Process(self, rctext, rcpath):
-+ rctext = '''STRINGTABLE
-+BEGIN
-+ DUMMY_STRING_1 "String 1"
-+ // Some random description
-+ DUMMY_STRING_2 "This text was added during preprocessing"
-+END
-+ '''
-+ return rctext
-+
-+if __name__ == '__main__':
-+ unittest.main()
-diff --git a/tools/grit/grit/tool/rc2grd.py b/tools/grit/grit/tool/rc2grd.py
-new file mode 100644
-index 0000000000..3195b39000
---- /dev/null
-+++ b/tools/grit/grit/tool/rc2grd.py
-@@ -0,0 +1,418 @@
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''The 'grit rc2grd' tool.'''
-+
-+from __future__ import print_function
-+
-+import os.path
-+import getopt
-+import re
-+import sys
-+
-+import six
-+from six import StringIO
-+
-+import grit.node.empty
-+from grit.node import include
-+from grit.node import structure
-+from grit.node import message
-+
-+from grit.gather import rc
-+from grit.gather import tr_html
-+
-+from grit.tool import interface
-+from grit.tool import postprocess_interface
-+from grit.tool import preprocess_interface
-+
-+from grit import grd_reader
-+from grit import lazy_re
-+from grit import tclib
-+from grit import util
-+
-+
-+# Matches files referenced from an .rc file
-+_FILE_REF = lazy_re.compile(r'''
-+ ^(?P<id>[A-Z_0-9.]+)[ \t]+
-+ (?P<type>[A-Z_0-9]+)[ \t]+
-+ "(?P<file>.*?([^"]|""))"[ \t]*$''', re.VERBOSE | re.MULTILINE)
-+
-+
-+# Matches a dialog section
-+_DIALOG = lazy_re.compile(
-+ r'^(?P<id>[A-Z0-9_]+)\s+DIALOG(EX)?\s.+?^BEGIN\s*$.+?^END\s*$',
-+ re.MULTILINE | re.DOTALL)
-+
-+
-+# Matches a menu section
-+_MENU = lazy_re.compile(r'^(?P<id>[A-Z0-9_]+)\s+MENU.+?^BEGIN\s*$.+?^END\s*$',
-+ re.MULTILINE | re.DOTALL)
-+
-+
-+# Matches a versioninfo section
-+_VERSIONINFO = lazy_re.compile(
-+ r'^(?P<id>[A-Z0-9_]+)\s+VERSIONINFO\s.+?^BEGIN\s*$.+?^END\s*$',
-+ re.MULTILINE | re.DOTALL)
-+
-+
-+# Matches a stringtable
-+_STRING_TABLE = lazy_re.compile(
-+ (r'^STRINGTABLE(\s+(PRELOAD|DISCARDABLE|CHARACTERISTICS.+|LANGUAGE.+|'
-+ r'VERSION.+))*\s*\nBEGIN\s*$(?P<body>.+?)^END\s*$'),
-+ re.MULTILINE | re.DOTALL)
-+
-+
-+# Matches each message inside a stringtable, breaking it up into comments,
-+# the ID of the message, and the (RC-escaped) message text.
-+_MESSAGE = lazy_re.compile(r'''
-+ (?P<comment>(^\s+//.+?)*) # 0 or more lines of comments preceding the message
-+ ^\s*
-+ (?P<id>[A-Za-z0-9_]+) # id
-+ \s+
-+ "(?P<text>.*?([^"]|""))"([^"]|$) # The message itself
-+ ''', re.MULTILINE | re.DOTALL | re.VERBOSE)
-+
-+
-+# Matches each line of comment text in a multi-line comment.
-+_COMMENT_TEXT = lazy_re.compile(r'^\s*//\s*(?P<text>.+?)$', re.MULTILINE)
-+
-+
-+# Matches a string that is empty or all whitespace
-+_WHITESPACE_ONLY = lazy_re.compile(r'\A\s*\Z', re.MULTILINE)
-+
-+
-+# Finds printf and FormatMessage style format specifiers
-+# Uses non-capturing groups except for the outermost group, so the output of
-+# re.split() should include both the normal text and what we intend to
-+# replace with placeholders.
-+# TODO(joi) Check documentation for printf (and Windows variants) and FormatMessage
-+_FORMAT_SPECIFIER = lazy_re.compile(
-+ r'(%[-# +]?(?:[0-9]*|\*)(?:\.(?:[0-9]+|\*))?(?:h|l|L)?' # printf up to last char
-+ r'(?:d|i|o|u|x|X|e|E|f|F|g|G|c|r|s|ls|ws)' # printf last char
-+ r'|\$[1-9][0-9]*)') # FormatMessage
-+
-+
-+class Rc2Grd(interface.Tool):
-+ '''A tool for converting .rc files to .grd files. This tool is only for
-+converting the source (nontranslated) .rc file to a .grd file. For importing
-+existing translations, use the rc2xtb tool.
-+
-+Usage: grit [global options] rc2grd [OPTIONS] RCFILE
-+
-+The tool takes a single argument, which is the path to the .rc file to convert.
-+It outputs a .grd file with the same name in the same directory as the .rc file.
-+The .grd file may have one or more TODO comments for things that have to be
-+cleaned up manually.
-+
-+OPTIONS may be any of the following:
-+
-+ -e ENCODING Specify the ENCODING of the .rc file. Default is 'cp1252'.
-+
-+ -h TYPE Specify the TYPE attribute for HTML structures.
-+ Default is 'tr_html'.
-+
-+ -u ENCODING Specify the ENCODING of HTML files. Default is 'utf-8'.
-+
-+ -n MATCH Specify the regular expression to match in comments that will
-+ indicate that the resource the comment belongs to is not
-+ translateable. Default is 'Not locali(s|z)able'.
-+
-+ -r GRDFILE Specify that GRDFILE should be used as a "role model" for
-+ any placeholders that otherwise would have had TODO names.
-+ This attempts to find an identical message in the GRDFILE
-+ and uses that instead of the automatically placeholderized
-+ message.
-+
-+ --pre CLASS Specify an optional, fully qualified classname, which
-+ has to be a subclass of grit.tool.PreProcessor, to
-+ run on the text of the RC file before conversion occurs.
-+ This can be used to support constructs in the RC files
-+ that GRIT cannot handle on its own.
-+
-+ --post CLASS Specify an optional, fully qualified classname, which
-+ has to be a subclass of grit.tool.PostProcessor, to
-+ run on the text of the converted RC file.
-+ This can be used to alter the content of the RC file
-+ based on the conversion that occured.
-+
-+For menus, dialogs and version info, the .grd file will refer to the original
-+.rc file. Once conversion is complete, you can strip the original .rc file
-+of its string table and all comments as these will be available in the .grd
-+file.
-+
-+Note that this tool WILL NOT obey C preprocessor rules, so even if something
-+is #if 0-ed out it will still be included in the output of this tool
-+Therefore, if your .rc file contains sections like this, you should run the
-+C preprocessor on the .rc file or manually edit it before using this tool.
-+'''
-+
-+ def ShortDescription(self):
-+ return 'A tool for converting .rc source files to .grd files.'
-+
-+ def __init__(self):
-+ self.input_encoding = 'cp1252'
-+ self.html_type = 'tr_html'
-+ self.html_encoding = 'utf-8'
-+ self.not_localizable_re = re.compile('Not locali(s|z)able')
-+ self.role_model = None
-+ self.pre_process = None
-+ self.post_process = None
-+
-+ def ParseOptions(self, args, help_func=None):
-+ '''Given a list of arguments, set this object's options and return
-+ all non-option arguments.
-+ '''
-+ (own_opts, args) = getopt.getopt(args, 'e:h:u:n:r',
-+ ('help', 'pre=', 'post='))
-+ for (key, val) in own_opts:
-+ if key == '-e':
-+ self.input_encoding = val
-+ elif key == '-h':
-+ self.html_type = val
-+ elif key == '-u':
-+ self.html_encoding = val
-+ elif key == '-n':
-+ self.not_localizable_re = re.compile(val)
-+ elif key == '-r':
-+ self.role_model = grd_reader.Parse(val)
-+ elif key == '--pre':
-+ self.pre_process = val
-+ elif key == '--post':
-+ self.post_process = val
-+ elif key == '--help':
-+ if help_func is None:
-+ self.ShowUsage()
-+ else:
-+ help_func()
-+ sys.exit(0)
-+ return args
-+
-+ def Run(self, opts, args):
-+ args = self.ParseOptions(args)
-+ if len(args) != 1:
-+ print('This tool takes a single tool-specific argument, the path to the\n'
-+ '.rc file to process.')
-+ return 2
-+ self.SetOptions(opts)
-+
-+ path = args[0]
-+ out_path = os.path.join(util.dirname(path),
-+ os.path.splitext(os.path.basename(path))[0] + '.grd')
-+
-+ rctext = util.ReadFile(path, self.input_encoding)
-+ grd_text = six.text_type(self.Process(rctext, path))
-+ with util.WrapOutputStream(open(out_path, 'wb'), 'utf-8') as outfile:
-+ outfile.write(grd_text)
-+
-+ print('Wrote output file %s.\nPlease check for TODO items in the file.' %
-+ (out_path,))
-+
-+
-+ def Process(self, rctext, rc_path):
-+ '''Processes 'rctext' and returns a resource tree corresponding to it.
-+
-+ Args:
-+ rctext: complete text of the rc file
-+ rc_path: 'resource\resource.rc'
-+
-+ Return:
-+ grit.node.base.Node subclass
-+ '''
-+
-+ if self.pre_process:
-+ preprocess_class = util.NewClassInstance(self.pre_process,
-+ preprocess_interface.PreProcessor)
-+ if preprocess_class:
-+ rctext = preprocess_class.Process(rctext, rc_path)
-+ else:
-+ self.Out(
-+ 'PreProcessing class could not be found. Skipping preprocessing.\n')
-+
-+ # Start with a basic skeleton for the .grd file
-+ root = grd_reader.Parse(StringIO(
-+ '''<?xml version="1.0" encoding="UTF-8"?>
-+ <grit base_dir="." latest_public_release="0"
-+ current_release="1" source_lang_id="en">
-+ <outputs />
-+ <translations />
-+ <release seq="1">
-+ <includes />
-+ <structures />
-+ <messages />
-+ </release>
-+ </grit>'''), util.dirname(rc_path))
-+ includes = root.children[2].children[0]
-+ structures = root.children[2].children[1]
-+ messages = root.children[2].children[2]
-+ assert (isinstance(includes, grit.node.empty.IncludesNode) and
-+ isinstance(structures, grit.node.empty.StructuresNode) and
-+ isinstance(messages, grit.node.empty.MessagesNode))
-+
-+ self.AddIncludes(rctext, includes)
-+ self.AddStructures(rctext, structures, os.path.basename(rc_path))
-+ self.AddMessages(rctext, messages)
-+
-+ self.VerboseOut('Validating that all IDs are unique...\n')
-+ root.ValidateUniqueIds()
-+ self.ExtraVerboseOut('Done validating that all IDs are unique.\n')
-+
-+ if self.post_process:
-+ postprocess_class = util.NewClassInstance(self.post_process,
-+ postprocess_interface.PostProcessor)
-+ if postprocess_class:
-+ root = postprocess_class.Process(rctext, rc_path, root)
-+ else:
-+ self.Out(
-+ 'PostProcessing class could not be found. Skipping postprocessing.\n')
-+
-+ return root
-+
-+
-+ def IsHtml(self, res_type, fname):
-+ '''Check whether both the type and file extension indicate HTML'''
-+ fext = fname.split('.')[-1].lower()
-+ return res_type == 'HTML' and fext in ('htm', 'html')
-+
-+
-+ def AddIncludes(self, rctext, node):
-+ '''Scans 'rctext' for included resources (e.g. BITMAP, ICON) and
-+ adds each included resource as an <include> child node of 'node'.'''
-+ for m in _FILE_REF.finditer(rctext):
-+ id = m.group('id')
-+ res_type = m.group('type').upper()
-+ fname = rc.Section.UnEscape(m.group('file'))
-+ assert fname.find('\n') == -1
-+ if not self.IsHtml(res_type, fname):
-+ self.VerboseOut('Processing %s with ID %s (filename: %s)\n' %
-+ (res_type, id, fname))
-+ node.AddChild(include.IncludeNode.Construct(node, id, res_type, fname))
-+
-+
-+ def AddStructures(self, rctext, node, rc_filename):
-+ '''Scans 'rctext' for structured resources (e.g. menus, dialogs, version
-+ information resources and HTML templates) and adds each as a <structure>
-+ child of 'node'.'''
-+ # First add HTML includes
-+ for m in _FILE_REF.finditer(rctext):
-+ id = m.group('id')
-+ res_type = m.group('type').upper()
-+ fname = rc.Section.UnEscape(m.group('file'))
-+ if self.IsHtml(type, fname):
-+ node.AddChild(structure.StructureNode.Construct(
-+ node, id, self.html_type, fname, self.html_encoding))
-+
-+ # Then add all RC includes
-+ def AddStructure(res_type, id):
-+ self.VerboseOut('Processing %s with ID %s\n' % (res_type, id))
-+ node.AddChild(structure.StructureNode.Construct(node, id, res_type,
-+ rc_filename,
-+ encoding=self.input_encoding))
-+ for m in _MENU.finditer(rctext):
-+ AddStructure('menu', m.group('id'))
-+ for m in _DIALOG.finditer(rctext):
-+ AddStructure('dialog', m.group('id'))
-+ for m in _VERSIONINFO.finditer(rctext):
-+ AddStructure('version', m.group('id'))
-+
-+
-+ def AddMessages(self, rctext, node):
-+ '''Scans 'rctext' for all messages in string tables, preprocesses them as
-+ much as possible for placeholders (e.g. messages containing $1, $2 or %s, %d
-+ type format specifiers get those specifiers replaced with placeholders, and
-+ HTML-formatted messages get run through the HTML-placeholderizer). Adds
-+ each message as a <message> node child of 'node'.'''
-+ for tm in _STRING_TABLE.finditer(rctext):
-+ table = tm.group('body')
-+ for mm in _MESSAGE.finditer(table):
-+ comment_block = mm.group('comment')
-+ comment_text = []
-+ for cm in _COMMENT_TEXT.finditer(comment_block):
-+ comment_text.append(cm.group('text'))
-+ comment_text = ' '.join(comment_text)
-+
-+ id = mm.group('id')
-+ text = rc.Section.UnEscape(mm.group('text'))
-+
-+ self.VerboseOut('Processing message %s (text: "%s")\n' % (id, text))
-+
-+ msg_obj = self.Placeholderize(text)
-+
-+ # Messages that contain only placeholders do not need translation.
-+ is_translateable = False
-+ for item in msg_obj.GetContent():
-+ if isinstance(item, six.string_types):
-+ if not _WHITESPACE_ONLY.match(item):
-+ is_translateable = True
-+
-+ if self.not_localizable_re.search(comment_text):
-+ is_translateable = False
-+
-+ message_meaning = ''
-+ internal_comment = ''
-+
-+ # If we have a "role model" (existing GRD file) and this node exists
-+ # in the role model, use the description, meaning and translateable
-+ # attributes from the role model.
-+ if self.role_model:
-+ role_node = self.role_model.GetNodeById(id)
-+ if role_node:
-+ is_translateable = role_node.IsTranslateable()
-+ message_meaning = role_node.attrs['meaning']
-+ comment_text = role_node.attrs['desc']
-+ internal_comment = role_node.attrs['internal_comment']
-+
-+ # For nontranslateable messages, we don't want the complexity of
-+ # placeholderizing everything.
-+ if not is_translateable:
-+ msg_obj = tclib.Message(text=text)
-+
-+ msg_node = message.MessageNode.Construct(node, msg_obj, id,
-+ desc=comment_text,
-+ translateable=is_translateable,
-+ meaning=message_meaning)
-+ msg_node.attrs['internal_comment'] = internal_comment
-+
-+ node.AddChild(msg_node)
-+ self.ExtraVerboseOut('Done processing message %s\n' % id)
-+
-+
-+ def Placeholderize(self, text):
-+ '''Creates a tclib.Message object from 'text', attempting to recognize
-+ a few different formats of text that can be automatically placeholderized
-+ (HTML code, printf-style format strings, and FormatMessage-style format
-+ strings).
-+ '''
-+
-+ try:
-+ # First try HTML placeholderizing.
-+ # TODO(joi) Allow use of non-TotalRecall flavors of HTML placeholderizing
-+ msg = tr_html.HtmlToMessage(text, True)
-+ for item in msg.GetContent():
-+ if not isinstance(item, six.string_types):
-+ return msg # Contained at least one placeholder, so we're done
-+
-+ # HTML placeholderization didn't do anything, so try to find printf or
-+ # FormatMessage format specifiers and change them into placeholders.
-+ msg = tclib.Message()
-+ parts = _FORMAT_SPECIFIER.split(text)
-+ todo_counter = 1 # We make placeholder IDs 'TODO_0001' etc.
-+ for part in parts:
-+ if _FORMAT_SPECIFIER.match(part):
-+ msg.AppendPlaceholder(tclib.Placeholder(
-+ 'TODO_%04d' % todo_counter, part, 'TODO'))
-+ todo_counter += 1
-+ elif part != '':
-+ msg.AppendText(part)
-+
-+ if self.role_model and len(parts) > 1: # there are TODO placeholders
-+ role_model_msg = self.role_model.UberClique().BestCliqueByOriginalText(
-+ msg.GetRealContent(), '')
-+ if role_model_msg:
-+ # replace wholesale to get placeholder names and examples
-+ msg = role_model_msg
-+
-+ return msg
-+ except:
-+ print('Exception processing message with text "%s"' % text)
-+ raise
-diff --git a/tools/grit/grit/tool/rc2grd_unittest.py b/tools/grit/grit/tool/rc2grd_unittest.py
-new file mode 100644
-index 0000000000..6d53794c27
---- /dev/null
-+++ b/tools/grit/grit/tool/rc2grd_unittest.py
-@@ -0,0 +1,163 @@
-+#!/usr/bin/env python
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Unit tests for grit.tool.rc2grd'''
-+
-+from __future__ import print_function
-+
-+import os
-+import sys
-+if __name__ == '__main__':
-+ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
-+
-+import re
-+import unittest
-+
-+from six import StringIO
-+
-+from grit import grd_reader
-+from grit import util
-+from grit.node import base
-+from grit.tool import rc2grd
-+
-+
-+class Rc2GrdUnittest(unittest.TestCase):
-+ def testPlaceholderize(self):
-+ tool = rc2grd.Rc2Grd()
-+ original = "Hello %s, how are you? I'm $1 years old!"
-+ msg = tool.Placeholderize(original)
-+ self.failUnless(msg.GetPresentableContent() == "Hello TODO_0001, how are you? I'm TODO_0002 years old!")
-+ self.failUnless(msg.GetRealContent() == original)
-+
-+ def testHtmlPlaceholderize(self):
-+ tool = rc2grd.Rc2Grd()
-+ original = "Hello <b>[USERNAME]</b>, how are you? I'm [AGE] years old!"
-+ msg = tool.Placeholderize(original)
-+ self.failUnless(msg.GetPresentableContent() ==
-+ "Hello BEGIN_BOLDX_USERNAME_XEND_BOLD, how are you? I'm X_AGE_X years old!")
-+ self.failUnless(msg.GetRealContent() == original)
-+
-+ def testMenuWithoutWhitespaceRegression(self):
-+ # There was a problem in the original regular expression for parsing out
-+ # menu sections, that would parse the following block of text as a single
-+ # menu instead of two.
-+ two_menus = '''
-+// Hyper context menus
-+IDR_HYPERMENU_FOLDER MENU
-+BEGIN
-+ POPUP "HyperFolder"
-+ BEGIN
-+ MENUITEM "Open Containing Folder", IDM_OPENFOLDER
-+ END
-+END
-+
-+IDR_HYPERMENU_FILE MENU
-+BEGIN
-+ POPUP "HyperFile"
-+ BEGIN
-+ MENUITEM "Open Folder", IDM_OPENFOLDER
-+ END
-+END
-+
-+'''
-+ self.failUnless(len(rc2grd._MENU.findall(two_menus)) == 2)
-+
-+ def testRegressionScriptWithTranslateable(self):
-+ tool = rc2grd.Rc2Grd()
-+
-+ # test rig
-+ class DummyNode(base.Node):
-+ def AddChild(self, item):
-+ self.node = item
-+ verbose = False
-+ extra_verbose = False
-+ tool.not_localizable_re = re.compile('')
-+ tool.o = DummyNode()
-+
-+ rc_text = '''STRINGTABLE\nBEGIN\nID_BINGO "<SPAN id=hp style='BEHAVIOR: url(#default#homepage)'></SPAN><script>if (!hp.isHomePage('[$~HOMEPAGE~$]')) {document.write(""<a href=\\""[$~SETHOMEPAGEURL~$]\\"" >Set As Homepage</a> - "");}</script>"\nEND\n'''
-+ tool.AddMessages(rc_text, tool.o)
-+ self.failUnless(tool.o.node.GetCdata().find('Set As Homepage') != -1)
-+
-+ # TODO(joi) Improve the HTML parser to support translateables inside
-+ # <script> blocks?
-+ self.failUnless(tool.o.node.attrs['translateable'] == 'false')
-+
-+ def testRoleModel(self):
-+ rc_text = ('STRINGTABLE\n'
-+ 'BEGIN\n'
-+ ' // This should not show up\n'
-+ ' IDS_BINGO "Hello %s, how are you?"\n'
-+ ' // The first description\n'
-+ ' IDS_BONGO "Hello %s, my name is %s, and yours?"\n'
-+ ' IDS_PROGRAMS_SHUTDOWN_TEXT "Google Desktop Search needs to close the following programs:\\n\\n$1\\nThe installation will not proceed if you choose to cancel."\n'
-+ 'END\n')
-+ tool = rc2grd.Rc2Grd()
-+ tool.role_model = grd_reader.Parse(StringIO(
-+ '''<?xml version="1.0" encoding="UTF-8"?>
-+ <grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
-+ <release seq="3">
-+ <messages>
-+ <message name="IDS_BINGO">
-+ Hello <ph name="USERNAME">%s<ex>Joi</ex></ph>, how are you?
-+ </message>
-+ <message name="IDS_BONGO" desc="The other description">
-+ Hello <ph name="USERNAME">%s<ex>Jakob</ex></ph>, my name is <ph name="ADMINNAME">%s<ex>Joi</ex></ph>, and yours?
-+ </message>
-+ <message name="IDS_PROGRAMS_SHUTDOWN_TEXT" desc="LIST_OF_PROGRAMS is replaced by a bulleted list of program names.">
-+ Google Desktop Search needs to close the following programs:
-+
-+<ph name="LIST_OF_PROGRAMS">$1<ex>Program 1, Program 2</ex></ph>
-+The installation will not proceed if you choose to cancel.
-+ </message>
-+ </messages>
-+ </release>
-+ </grit>'''), dir='.')
-+
-+ # test rig
-+ class DummyOpts(object):
-+ verbose = False
-+ extra_verbose = False
-+ tool.o = DummyOpts()
-+ result = tool.Process(rc_text, '.\resource.rc')
-+ self.failUnless(
-+ result.children[2].children[2].children[0].attrs['desc'] == '')
-+ self.failUnless(
-+ result.children[2].children[2].children[0].children[0].attrs['name'] == 'USERNAME')
-+ self.failUnless(
-+ result.children[2].children[2].children[1].attrs['desc'] == 'The other description')
-+ self.failUnless(
-+ result.children[2].children[2].children[1].attrs['meaning'] == '')
-+ self.failUnless(
-+ result.children[2].children[2].children[1].children[0].attrs['name'] == 'USERNAME')
-+ self.failUnless(
-+ result.children[2].children[2].children[1].children[1].attrs['name'] == 'ADMINNAME')
-+ self.failUnless(
-+ result.children[2].children[2].children[2].children[0].attrs['name'] == 'LIST_OF_PROGRAMS')
-+
-+ def testRunOutput(self):
-+ """Verify basic correct Run behavior."""
-+ tool = rc2grd.Rc2Grd()
-+ class DummyOpts(object):
-+ verbose = False
-+ extra_verbose = False
-+ with util.TempDir({}) as output_dir:
-+ rcfile = os.path.join(output_dir.GetPath(), 'foo.rc')
-+ open(rcfile, 'w').close()
-+ self.assertIsNone(tool.Run(DummyOpts(), [rcfile]))
-+ self.assertTrue(os.path.exists(os.path.join(output_dir.GetPath(), 'foo.grd')))
-+
-+ def testMissingOutput(self):
-+ """Verify failure with no args."""
-+ tool = rc2grd.Rc2Grd()
-+ class DummyOpts(object):
-+ verbose = False
-+ extra_verbose = False
-+ ret = tool.Run(DummyOpts(), [])
-+ self.assertIsNotNone(ret)
-+ self.assertGreater(ret, 0)
-+
-+
-+if __name__ == '__main__':
-+ unittest.main()
-diff --git a/tools/grit/grit/tool/resize.py b/tools/grit/grit/tool/resize.py
-new file mode 100644
-index 0000000000..6a897c077e
---- /dev/null
-+++ b/tools/grit/grit/tool/resize.py
-@@ -0,0 +1,295 @@
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''The 'grit resize' tool.
-+'''
-+
-+from __future__ import print_function
-+
-+import getopt
-+import os
-+import sys
-+
-+from grit import grd_reader
-+from grit import pseudo
-+from grit import util
-+from grit.format import rc
-+from grit.format import rc_header
-+from grit.node import include
-+from grit.tool import interface
-+
-+
-+# Template for the .vcproj file, with a couple of [[REPLACEABLE]] parts.
-+PROJECT_TEMPLATE = '''\
-+<?xml version="1.0" encoding="Windows-1252"?>
-+<VisualStudioProject
-+ ProjectType="Visual C++"
-+ Version="7.10"
-+ Name="[[DIALOG_NAME]]"
-+ ProjectGUID="[[PROJECT_GUID]]"
-+ Keyword="Win32Proj">
-+ <Platforms>
-+ <Platform
-+ Name="Win32"/>
-+ </Platforms>
-+ <Configurations>
-+ <Configuration
-+ Name="Debug|Win32"
-+ OutputDirectory="Debug"
-+ IntermediateDirectory="Debug"
-+ ConfigurationType="1"
-+ CharacterSet="2">
-+ </Configuration>
-+ </Configurations>
-+ <References>
-+ </References>
-+ <Files>
-+ <Filter
-+ Name="Resource Files"
-+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"
-+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}">
-+ <File
-+ RelativePath=".\\[[DIALOG_NAME]].rc">
-+ </File>
-+ </Filter>
-+ </Files>
-+ <Globals>
-+ </Globals>
-+</VisualStudioProject>'''
-+
-+
-+# Template for the .rc file with a couple of [[REPLACEABLE]] parts.
-+# TODO(joi) Improve this (and the resource.h template) to allow saving and then
-+# reopening of the RC file in Visual Studio. Currently you can only open it
-+# once and change it, then after you close it you won't be able to reopen it.
-+RC_TEMPLATE = '''\
-+// This file is automatically generated by GRIT and intended for editing
-+// the layout of the dialogs contained in it. Do not edit anything but the
-+// dialogs. Any changes made to translateable portions of the dialogs will
-+// be ignored by GRIT.
-+
-+#include "resource.h"
-+#include <winresrc.h>
-+#ifdef IDC_STATIC
-+#undef IDC_STATIC
-+#endif
-+#define IDC_STATIC (-1)
-+
-+LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
-+
-+#pragma code_page([[CODEPAGE_NUM]])
-+
-+[[INCLUDES]]
-+
-+[[DIALOGS]]
-+'''
-+
-+
-+# Template for the resource.h file with a couple of [[REPLACEABLE]] parts.
-+HEADER_TEMPLATE = '''\
-+// This file is automatically generated by GRIT. Do not edit.
-+
-+#pragma once
-+
-+// Edit commands
-+#define ID_EDIT_CLEAR 0xE120
-+#define ID_EDIT_CLEAR_ALL 0xE121
-+#define ID_EDIT_COPY 0xE122
-+#define ID_EDIT_CUT 0xE123
-+#define ID_EDIT_FIND 0xE124
-+#define ID_EDIT_PASTE 0xE125
-+#define ID_EDIT_PASTE_LINK 0xE126
-+#define ID_EDIT_PASTE_SPECIAL 0xE127
-+#define ID_EDIT_REPEAT 0xE128
-+#define ID_EDIT_REPLACE 0xE129
-+#define ID_EDIT_SELECT_ALL 0xE12A
-+#define ID_EDIT_UNDO 0xE12B
-+#define ID_EDIT_REDO 0xE12C
-+
-+
-+[[DEFINES]]
-+'''
-+
-+
-+class ResizeDialog(interface.Tool):
-+ '''Generates an RC file, header and Visual Studio project that you can use
-+with Visual Studio's GUI resource editor to modify the layout of dialogs for
-+the language of your choice. You then use the RC file, after you resize the
-+dialog, for the language or languages of your choice, using the <skeleton> child
-+of the <structure> node for the dialog. The translateable bits of the dialog
-+will be ignored when you use the <skeleton> node (GRIT will instead use the
-+translateable bits from the original dialog) but the layout changes you make
-+will be used. Note that your layout changes must preserve the order of the
-+translateable elements in the RC file.
-+
-+Usage: grit resize [-f BASEFOLDER] [-l LANG] [-e RCENCODING] DIALOGID*
-+
-+Arguments:
-+ DIALOGID The 'name' attribute of a dialog to output for resizing. Zero
-+ or more of these parameters can be used. If none are
-+ specified, all dialogs from the input .grd file are output.
-+
-+Options:
-+
-+ -f BASEFOLDER The project will be created in a subfolder of BASEFOLDER.
-+ The name of the subfolder will be the first DIALOGID you
-+ specify. Defaults to '.'
-+
-+ -l LANG Specifies that the RC file should contain a dialog translated
-+ into the language LANG. The default is a cp1252-representable
-+ pseudotranslation, because Visual Studio's GUI RC editor only
-+ supports single-byte encodings.
-+
-+ -c CODEPAGE Code page number to indicate to the RC compiler the encoding
-+ of the RC file, default is something reasonable for the
-+ language you selected (but this does not work for every single
-+ language). See details on codepages below. NOTE that you do
-+ not need to specify the codepage unless the tool complains
-+ that it's not sure which codepage to use. See the following
-+ page for codepage numbers supported by Windows:
-+ http://www.microsoft.com/globaldev/reference/wincp.mspx
-+
-+ -D NAME[=VAL] Specify a C-preprocessor-like define NAME with optional
-+ value VAL (defaults to 1) which will be used to control
-+ conditional inclusion of resources.
-+
-+
-+IMPORTANT NOTE: For now, the tool outputs a UTF-8 encoded file for any language
-+that can not be represented in cp1252 (i.e. anything other than Western
-+European languages). You will need to open this file in a text editor and
-+save it using the codepage indicated in the #pragma code_page(XXXX) command
-+near the top of the file, before you open it in Visual Studio.
-+
-+'''
-+
-+ # TODO(joi) It would be cool to have this tool note the Perforce revision
-+ # of the original RC file somewhere, such that the <skeleton> node could warn
-+ # if the original RC file gets updated without the skeleton file being updated.
-+
-+ # TODO(joi) Would be cool to have option to add the files to Perforce
-+
-+ def __init__(self):
-+ self.lang = pseudo.PSEUDO_LANG
-+ self.defines = {}
-+ self.base_folder = '.'
-+ self.codepage_number = 1252
-+ self.codepage_number_specified_explicitly = False
-+
-+ def SetLanguage(self, lang):
-+ '''Sets the language code to output things in.
-+ '''
-+ self.lang = lang
-+ if not self.codepage_number_specified_explicitly:
-+ self.codepage_number = util.LanguageToCodepage(lang)
-+
-+ def GetEncoding(self):
-+ if self.codepage_number == 1200:
-+ return 'utf_16'
-+ if self.codepage_number == 65001:
-+ return 'utf_8'
-+ return 'cp%d' % self.codepage_number
-+
-+ def ShortDescription(self):
-+ return 'Generate a file where you can resize a given dialog.'
-+
-+ def Run(self, opts, args):
-+ self.SetOptions(opts)
-+
-+ own_opts, args = getopt.getopt(args, 'l:f:c:D:', ('help',))
-+ for key, val in own_opts:
-+ if key == '-l':
-+ self.SetLanguage(val)
-+ if key == '-f':
-+ self.base_folder = val
-+ if key == '-c':
-+ self.codepage_number = int(val)
-+ self.codepage_number_specified_explicitly = True
-+ if key == '-D':
-+ name, val = util.ParseDefine(val)
-+ self.defines[name] = val
-+ elif key == '--help':
-+ self.ShowUsage()
-+ sys.exit(0)
-+
-+ res_tree = grd_reader.Parse(opts.input, debug=opts.extra_verbose)
-+ res_tree.OnlyTheseTranslations([self.lang])
-+ res_tree.RunGatherers()
-+
-+ # Dialog IDs are either explicitly listed, or we output all dialogs from the
-+ # .grd file
-+ dialog_ids = args
-+ if not len(dialog_ids):
-+ for node in res_tree:
-+ if node.name == 'structure' and node.attrs['type'] == 'dialog':
-+ dialog_ids.append(node.attrs['name'])
-+
-+ self.Process(res_tree, dialog_ids)
-+
-+ def Process(self, grd, dialog_ids):
-+ '''Outputs an RC file and header file for the dialog 'dialog_id' stored in
-+ resource tree 'grd', to self.base_folder, as discussed in this class's
-+ documentation.
-+
-+ Arguments:
-+ grd: grd = grd_reader.Parse(...); grd.RunGatherers()
-+ dialog_ids: ['IDD_MYDIALOG', 'IDD_OTHERDIALOG']
-+ '''
-+ grd.SetOutputLanguage(self.lang)
-+ grd.SetDefines(self.defines)
-+
-+ project_name = dialog_ids[0]
-+
-+ dir_path = os.path.join(self.base_folder, project_name)
-+ if not os.path.isdir(dir_path):
-+ os.mkdir(dir_path)
-+
-+ # If this fails then we're not on Windows (or you don't have the required
-+ # win32all Python libraries installed), so what are you doing mucking
-+ # about with RC files anyway? :)
-+ # pylint: disable=import-error
-+ import pythoncom
-+
-+ # Create the .vcproj file
-+ project_text = PROJECT_TEMPLATE.replace(
-+ '[[PROJECT_GUID]]', str(pythoncom.CreateGuid())
-+ ).replace('[[DIALOG_NAME]]', project_name)
-+ fname = os.path.join(dir_path, '%s.vcproj' % project_name)
-+ self.WriteFile(fname, project_text)
-+ print("Wrote %s" % fname)
-+
-+ # Create the .rc file
-+ # Output all <include> nodes since the dialogs might depend on them (e.g.
-+ # for icons and bitmaps).
-+ include_items = []
-+ for node in grd.ActiveDescendants():
-+ if isinstance(node, include.IncludeNode):
-+ include_items.append(rc.FormatInclude(node, self.lang, '.'))
-+ rc_text = RC_TEMPLATE.replace('[[CODEPAGE_NUM]]',
-+ str(self.codepage_number))
-+ rc_text = rc_text.replace('[[INCLUDES]]', ''.join(include_items))
-+
-+ # Then output the dialogs we have been asked to output.
-+ dialogs = []
-+ for dialog_id in dialog_ids:
-+ node = grd.GetNodeById(dialog_id)
-+ assert node.name == 'structure' and node.attrs['type'] == 'dialog'
-+ # TODO(joi) Add exception handling for better error reporting
-+ dialogs.append(rc.FormatStructure(node, self.lang, '.'))
-+ rc_text = rc_text.replace('[[DIALOGS]]', ''.join(dialogs))
-+
-+ fname = os.path.join(dir_path, '%s.rc' % project_name)
-+ self.WriteFile(fname, rc_text, self.GetEncoding())
-+ print("Wrote %s" % fname)
-+
-+ # Create the resource.h file
-+ header_defines = ''.join(rc_header.FormatDefines(grd))
-+ header_text = HEADER_TEMPLATE.replace('[[DEFINES]]', header_defines)
-+ fname = os.path.join(dir_path, 'resource.h')
-+ self.WriteFile(fname, header_text)
-+ print("Wrote %s" % fname)
-+
-+ def WriteFile(self, filename, contents, encoding='cp1252'):
-+ with open(filename, 'wb') as f:
-+ writer = util.WrapOutputStream(f, encoding)
-+ writer.write(contents)
-diff --git a/tools/grit/grit/tool/test.py b/tools/grit/grit/tool/test.py
-new file mode 100644
-index 0000000000..241a976d74
---- /dev/null
-+++ b/tools/grit/grit/tool/test.py
-@@ -0,0 +1,24 @@
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+from __future__ import print_function
-+
-+from grit.tool import interface
-+
-+class TestTool(interface.Tool):
-+ '''This tool does nothing except print out the global options and
-+tool-specific arguments that it receives. It is intended only for testing,
-+hence the name :)
-+'''
-+
-+ def ShortDescription(self):
-+ return 'A do-nothing tool for testing command-line parsing.'
-+
-+ def Run(self, global_options, my_arguments):
-+ print('NOTE This tool is only for testing the parsing of global options and')
-+ print('tool-specific arguments that it receives. You may have intended to')
-+ print('run "grit unit" which is the unit-test suite for GRIT.')
-+ print('Options: %s' % repr(global_options))
-+ print('Arguments: %s' % repr(my_arguments))
-+ return 0
-diff --git a/tools/grit/grit/tool/transl2tc.py b/tools/grit/grit/tool/transl2tc.py
-new file mode 100644
-index 0000000000..45301bbf58
---- /dev/null
-+++ b/tools/grit/grit/tool/transl2tc.py
-@@ -0,0 +1,251 @@
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''The 'grit transl2tc' tool.
-+'''
-+
-+from __future__ import print_function
-+
-+from grit import grd_reader
-+from grit import util
-+from grit.tool import interface
-+from grit.tool import rc2grd
-+
-+from grit.extern import tclib
-+
-+
-+class TranslationToTc(interface.Tool):
-+ '''A tool for importing existing translations in RC format into the
-+Translation Console.
-+
-+Usage:
-+
-+grit -i GRD transl2tc [-l LIMITS] [RCOPTS] SOURCE_RC TRANSLATED_RC OUT_FILE
-+
-+The tool needs a "source" RC file, i.e. in English, and an RC file that is a
-+translation of precisely the source RC file (not of an older or newer version).
-+
-+The tool also requires you to provide a .grd file (input file) e.g. using the
-+-i global option or the GRIT_INPUT environment variable. The tool uses
-+information from your .grd file to correct placeholder names in the
-+translations and ensure that only translatable items and translations still
-+being used are output.
-+
-+This tool will accept all the same RCOPTS as the 'grit rc2grd' tool. To get
-+a list of these options, run 'grit help rc2grd'.
-+
-+Additionally, you can use the -l option (which must be the first option to the
-+tool) to specify a file containing a list of message IDs to which output should
-+be limited. This is only useful if you are limiting the output to your XMB
-+files using the 'grit xmb' tool's -l option. See 'grit help xmb' for how to
-+generate a file containing a list of the message IDs in an XMB file.
-+
-+The tool will scan through both of the RC files as well as any HTML files they
-+refer to, and match together the source messages and translated messages. It
-+will output a file (OUTPUT_FILE) you can import directly into the TC using the
-+Bulk Translation Upload tool.
-+'''
-+
-+ def ShortDescription(self):
-+ return 'Import existing translations in RC format into the TC'
-+
-+ def Setup(self, globopt, args):
-+ '''Sets the instance up for use.
-+ '''
-+ self.SetOptions(globopt)
-+ self.rc2grd = rc2grd.Rc2Grd()
-+ self.rc2grd.SetOptions(globopt)
-+ self.limits = None
-+ if len(args) and args[0] == '-l':
-+ self.limits = util.ReadFile(args[1], 'utf-8').splitlines()
-+ args = args[2:]
-+ return self.rc2grd.ParseOptions(args, help_func=self.ShowUsage)
-+
-+ def Run(self, globopt, args):
-+ args = self.Setup(globopt, args)
-+
-+ if len(args) != 3:
-+ self.Out('This tool takes exactly three arguments:\n'
-+ ' 1. The path to the original RC file\n'
-+ ' 2. The path to the translated RC file\n'
-+ ' 3. The output file path.\n')
-+ return 2
-+
-+ grd = grd_reader.Parse(self.o.input, debug=self.o.extra_verbose)
-+ grd.RunGatherers()
-+
-+ source_rc = util.ReadFile(args[0], self.rc2grd.input_encoding)
-+ transl_rc = util.ReadFile(args[1], self.rc2grd.input_encoding)
-+ translations = self.ExtractTranslations(grd,
-+ source_rc, args[0],
-+ transl_rc, args[1])
-+
-+ with util.WrapOutputStream(open(args[2], 'wb')) as output_file:
-+ self.WriteTranslations(output_file, translations.items())
-+
-+ self.Out('Wrote output file %s' % args[2])
-+
-+ def ExtractTranslations(self, current_grd, source_rc, source_path,
-+ transl_rc, transl_path):
-+ '''Extracts translations from the translated RC file, matching them with
-+ translations in the source RC file to calculate their ID, and correcting
-+ placeholders, limiting output to translateables, etc. using the supplied
-+ .grd file which is the current .grd file for your project.
-+
-+ If this object's 'limits' attribute is not None but a list, the output of
-+ this function will be further limited to include only messages that have
-+ message IDs in the 'limits' list.
-+
-+ Args:
-+ current_grd: grit.node.base.Node child, that has had RunGatherers() run
-+ on it
-+ source_rc: Complete text of source RC file
-+ source_path: Path to the source RC file
-+ transl_rc: Complete text of translated RC file
-+ transl_path: Path to the translated RC file
-+
-+ Return:
-+ { id1 : text1, '12345678' : 'Hello USERNAME, howzit?' }
-+ '''
-+ source_grd = self.rc2grd.Process(source_rc, source_path)
-+ self.VerboseOut('Read %s into GRIT format, running gatherers.\n' % source_path)
-+ source_grd.SetOutputLanguage(current_grd.output_language)
-+ source_grd.SetDefines(current_grd.defines)
-+ source_grd.RunGatherers(debug=self.o.extra_verbose)
-+ transl_grd = self.rc2grd.Process(transl_rc, transl_path)
-+ transl_grd.SetOutputLanguage(current_grd.output_language)
-+ transl_grd.SetDefines(current_grd.defines)
-+ self.VerboseOut('Read %s into GRIT format, running gatherers.\n' % transl_path)
-+ transl_grd.RunGatherers(debug=self.o.extra_verbose)
-+ self.VerboseOut('Done running gatherers for %s.\n' % transl_path)
-+
-+ # Proceed to create a map from ID to translation, getting the ID from the
-+ # source GRD and the translation from the translated GRD.
-+ id2transl = {}
-+ for source_node in source_grd:
-+ source_cliques = source_node.GetCliques()
-+ if not len(source_cliques):
-+ continue
-+
-+ assert 'name' in source_node.attrs, 'All nodes with cliques should have an ID'
-+ node_id = source_node.attrs['name']
-+ self.ExtraVerboseOut('Processing node %s\n' % node_id)
-+ transl_node = transl_grd.GetNodeById(node_id)
-+
-+ if transl_node:
-+ transl_cliques = transl_node.GetCliques()
-+ if not len(transl_cliques) == len(source_cliques):
-+ self.Out(
-+ 'Warning: Translation for %s has wrong # of cliques, skipping.\n' %
-+ node_id)
-+ continue
-+ else:
-+ self.Out('Warning: No translation for %s, skipping.\n' % node_id)
-+ continue
-+
-+ if source_node.name == 'message':
-+ # Fixup placeholders as well as possible based on information from
-+ # the current .grd file if they are 'TODO_XXXX' placeholders. We need
-+ # to fixup placeholders in the translated message so that it looks right
-+ # and we also need to fixup placeholders in the source message so that
-+ # its calculated ID will match the current message.
-+ current_node = current_grd.GetNodeById(node_id)
-+ if current_node:
-+ assert len(source_cliques) == len(current_node.GetCliques()) == 1
-+
-+ source_msg = source_cliques[0].GetMessage()
-+ current_msg = current_node.GetCliques()[0].GetMessage()
-+
-+ # Only do this for messages whose source version has not changed.
-+ if (source_msg.GetRealContent() != current_msg.GetRealContent()):
-+ self.VerboseOut('Info: Message %s has changed; skipping\n' % node_id)
-+ else:
-+ transl_msg = transl_cliques[0].GetMessage()
-+ transl_content = transl_msg.GetContent()
-+ current_content = current_msg.GetContent()
-+ source_content = source_msg.GetContent()
-+
-+ ok_to_fixup = True
-+ if (len(transl_content) != len(current_content)):
-+ # message structure of translation is different, don't try fixup
-+ ok_to_fixup = False
-+ if ok_to_fixup:
-+ for ix in range(len(transl_content)):
-+ if isinstance(transl_content[ix], tclib.Placeholder):
-+ if not isinstance(current_content[ix], tclib.Placeholder):
-+ ok_to_fixup = False # structure has changed
-+ break
-+ if (transl_content[ix].GetOriginal() !=
-+ current_content[ix].GetOriginal()):
-+ ok_to_fixup = False # placeholders have likely been reordered
-+ break
-+ else: # translated part is not a placeholder but a string
-+ if isinstance(current_content[ix], tclib.Placeholder):
-+ ok_to_fixup = False # placeholders have likely been reordered
-+ break
-+
-+ if not ok_to_fixup:
-+ self.VerboseOut(
-+ 'Info: Structure of message %s has changed; skipping.\n' % node_id)
-+ else:
-+ def Fixup(content, ix):
-+ if (isinstance(content[ix], tclib.Placeholder) and
-+ content[ix].GetPresentation().startswith('TODO_')):
-+ assert isinstance(current_content[ix], tclib.Placeholder)
-+ # Get the placeholder ID and example from the current message
-+ content[ix] = current_content[ix]
-+ for ix in range(len(transl_content)):
-+ Fixup(transl_content, ix)
-+ Fixup(source_content, ix)
-+
-+ # Only put each translation once into the map. Warn if translations
-+ # for the same message are different.
-+ for ix in range(len(transl_cliques)):
-+ source_msg = source_cliques[ix].GetMessage()
-+ source_msg.GenerateId() # needed to refresh ID based on new placeholders
-+ message_id = source_msg.GetId()
-+ translated_content = transl_cliques[ix].GetMessage().GetPresentableContent()
-+
-+ if message_id in id2transl:
-+ existing_translation = id2transl[message_id]
-+ if existing_translation != translated_content:
-+ original_text = source_cliques[ix].GetMessage().GetPresentableContent()
-+ self.Out('Warning: Two different translations for "%s":\n'
-+ ' Translation 1: "%s"\n'
-+ ' Translation 2: "%s"\n' %
-+ (original_text, existing_translation, translated_content))
-+ else:
-+ id2transl[message_id] = translated_content
-+
-+ # Remove translations for messages that do not occur in the current .grd
-+ # or have been marked as not translateable, or do not occur in the 'limits'
-+ # list (if it has been set).
-+ current_message_ids = current_grd.UberClique().AllMessageIds()
-+ for message_id in list(id2transl.keys()):
-+ if (message_id not in current_message_ids or
-+ not current_grd.UberClique().BestClique(message_id).IsTranslateable() or
-+ (self.limits and message_id not in self.limits)):
-+ del id2transl[message_id]
-+
-+ return id2transl
-+
-+ @staticmethod
-+ def WriteTranslations(output_file, translations):
-+ '''Writes the provided list of translations to the provided output file
-+ in the format used by the TC's Bulk Translation Upload tool. The file
-+ must be UTF-8 encoded.
-+
-+ Args:
-+ output_file: util.WrapOutputStream(open('bingo.out', 'wb'))
-+ translations: [ [id1, text1], ['12345678', 'Hello USERNAME, howzit?'] ]
-+
-+ Return:
-+ None
-+ '''
-+ for id, text in translations:
-+ text = text.replace('<', '&lt;').replace('>', '&gt;')
-+ output_file.write(id)
-+ output_file.write(' ')
-+ output_file.write(text)
-+ output_file.write('\n')
-diff --git a/tools/grit/grit/tool/transl2tc_unittest.py b/tools/grit/grit/tool/transl2tc_unittest.py
-new file mode 100644
-index 0000000000..22e937f9f2
---- /dev/null
-+++ b/tools/grit/grit/tool/transl2tc_unittest.py
-@@ -0,0 +1,133 @@
-+#!/usr/bin/env python
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Unit tests for the 'grit transl2tc' tool.'''
-+
-+from __future__ import print_function
-+
-+import os
-+import sys
-+if __name__ == '__main__':
-+ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
-+
-+import unittest
-+
-+from six import StringIO
-+
-+from grit.tool import transl2tc
-+from grit import grd_reader
-+from grit import util
-+
-+
-+def MakeOptions():
-+ from grit import grit_runner
-+ return grit_runner.Options()
-+
-+
-+class TranslationToTcUnittest(unittest.TestCase):
-+
-+ def testOutput(self):
-+ buf = StringIO()
-+ tool = transl2tc.TranslationToTc()
-+ translations = [
-+ ['1', 'Hello USERNAME, how are you?'],
-+ ['12', 'Howdie doodie!'],
-+ ['123', 'Hello\n\nthere\n\nhow are you?'],
-+ ['1234', 'Hello is > goodbye but < howdie pardner'],
-+ ]
-+ tool.WriteTranslations(buf, translations)
-+ output = buf.getvalue()
-+ self.failUnless(output.strip() == '''
-+1 Hello USERNAME, how are you?
-+12 Howdie doodie!
-+123 Hello
-+
-+there
-+
-+how are you?
-+1234 Hello is &gt; goodbye but &lt; howdie pardner
-+'''.strip())
-+
-+ def testExtractTranslations(self):
-+ path = util.PathFromRoot('grit/testdata')
-+ current_grd = grd_reader.Parse(StringIO('''<?xml version="1.0" encoding="UTF-8"?>
-+ <grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
-+ <release seq="3">
-+ <messages>
-+ <message name="IDS_SIMPLE">
-+ One
-+ </message>
-+ <message name="IDS_PLACEHOLDER">
-+ <ph name="NUMBIRDS">%s<ex>3</ex></ph> birds
-+ </message>
-+ <message name="IDS_PLACEHOLDERS">
-+ <ph name="ITEM">%d<ex>1</ex></ph> of <ph name="COUNT">%d<ex>3</ex></ph>
-+ </message>
-+ <message name="IDS_REORDERED_PLACEHOLDERS">
-+ <ph name="ITEM">$1<ex>1</ex></ph> of <ph name="COUNT">$2<ex>3</ex></ph>
-+ </message>
-+ <message name="IDS_CHANGED">
-+ This is the new version
-+ </message>
-+ <message name="IDS_TWIN_1">Hello</message>
-+ <message name="IDS_TWIN_2">Hello</message>
-+ <message name="IDS_NOT_TRANSLATEABLE" translateable="false">:</message>
-+ <message name="IDS_LONGER_TRANSLATED">
-+ Removed document <ph name="FILENAME">$1<ex>c:\temp</ex></ph>
-+ </message>
-+ <message name="IDS_DIFFERENT_TWIN_1">Howdie</message>
-+ <message name="IDS_DIFFERENT_TWIN_2">Howdie</message>
-+ </messages>
-+ <structures>
-+ <structure type="dialog" name="IDD_ABOUTBOX" encoding="utf-16" file="klonk.rc" />
-+ <structure type="menu" name="IDC_KLONKMENU" encoding="utf-16" file="klonk.rc" />
-+ </structures>
-+ </release>
-+ </grit>'''), path)
-+ current_grd.SetOutputLanguage('en')
-+ current_grd.RunGatherers()
-+
-+ source_rc_path = util.PathFromRoot('grit/testdata/source.rc')
-+ source_rc = util.ReadFile(source_rc_path, 'utf-8')
-+ transl_rc_path = util.PathFromRoot('grit/testdata/transl.rc')
-+ transl_rc = util.ReadFile(transl_rc_path, 'utf-8')
-+
-+ tool = transl2tc.TranslationToTc()
-+ output_buf = StringIO()
-+ globopts = MakeOptions()
-+ globopts.verbose = True
-+ globopts.output_stream = output_buf
-+ tool.Setup(globopts, [])
-+ translations = tool.ExtractTranslations(current_grd,
-+ source_rc, source_rc_path,
-+ transl_rc, transl_rc_path)
-+
-+ values = list(translations.values())
-+ output = output_buf.getvalue()
-+
-+ self.failUnless('Ein' in values)
-+ self.failUnless('NUMBIRDS Vogeln' in values)
-+ self.failUnless('ITEM von COUNT' in values)
-+ self.failUnless(values.count('Hallo') == 1)
-+ self.failIf('Dass war die alte Version' in values)
-+ self.failIf(':' in values)
-+ self.failIf('Dokument FILENAME ist entfernt worden' in values)
-+ self.failIf('Nicht verwendet' in values)
-+ self.failUnless(('Howdie' in values or 'Hallo sagt man' in values) and not
-+ ('Howdie' in values and 'Hallo sagt man' in values))
-+
-+ self.failUnless('XX01XX&SkraXX02XX&HaettaXX03XXThetta er "Klonk" sem eg fylaXX04XXgonkurinnXX05XXKlonk && er [good]XX06XX&HjalpXX07XX&Um...XX08XX' in values)
-+
-+ self.failUnless('I lagi' in values)
-+
-+ self.failUnless(output.count('Structure of message IDS_REORDERED_PLACEHOLDERS has changed'))
-+ self.failUnless(output.count('Message IDS_CHANGED has changed'))
-+ self.failUnless(output.count('Structure of message IDS_LONGER_TRANSLATED has changed'))
-+ self.failUnless(output.count('Two different translations for "Howdie"'))
-+ self.failUnless(output.count('IDD_DIFFERENT_LENGTH_IN_TRANSL has wrong # of cliques'))
-+
-+
-+if __name__ == '__main__':
-+ unittest.main()
-diff --git a/tools/grit/grit/tool/unit.py b/tools/grit/grit/tool/unit.py
-new file mode 100644
-index 0000000000..7e96b699c3
---- /dev/null
-+++ b/tools/grit/grit/tool/unit.py
-@@ -0,0 +1,43 @@
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''GRIT tool that runs the unit test suite for GRIT.'''
-+
-+from __future__ import print_function
-+
-+import getopt
-+import sys
-+import unittest
-+
-+try:
-+ import grit.test_suite_all
-+except ImportError:
-+ pass
-+from grit.tool import interface
-+
-+
-+class UnitTestTool(interface.Tool):
-+ '''By using this tool (e.g. 'grit unit') you run all the unit tests for GRIT.
-+This happens in the environment that is set up by the basic GRIT runner.'''
-+
-+ def ShortDescription(self):
-+ return 'Use this tool to run all the unit tests for GRIT.'
-+
-+ def ParseOptions(self, args):
-+ """Set this objects and return all non-option arguments."""
-+ own_opts, args = getopt.getopt(args, '', ('help',))
-+ for key, val in own_opts:
-+ if key == '--help':
-+ self.ShowUsage()
-+ sys.exit(0)
-+ return args
-+
-+ def Run(self, opts, args):
-+ args = self.ParseOptions(args)
-+ if args:
-+ print('This tool takes no arguments.')
-+ return 2
-+
-+ return unittest.TextTestRunner(verbosity=2).run(
-+ grit.test_suite_all.TestSuiteAll())
-diff --git a/tools/grit/grit/tool/update_resource_ids/__init__.py b/tools/grit/grit/tool/update_resource_ids/__init__.py
-new file mode 100644
-index 0000000000..3006fbffab
---- /dev/null
-+++ b/tools/grit/grit/tool/update_resource_ids/__init__.py
-@@ -0,0 +1,305 @@
-+# 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.
-+"""Package grit.tool.update_resource_ids
-+
-+Updates GRID resource_ids from linked GRD files, while preserving structure.
-+
-+A resource_ids file is a JSON dict (with Python comments) that maps GRD paths
-+to *items*. Item order is ignored by GRIT, but is important since it establishes
-+a narrative of item dependency (needs non-overlapping IDs) and mutual exclusion
-+(allows ID overlap). Example:
-+
-+{
-+ # The first entry in the file, SRCDIR, is special: It is a relative path from
-+ # this file to the base of your checkout.
-+ "SRCDIR": "../..",
-+
-+ # First GRD file. This entry is an "Item".
-+ "1.grd": {
-+ "messages": [400], # "Tag".
-+ },
-+ # Depends on 1.grd, i.e., 500 >= 400 + (# of IDs used in 1.grd).
-+ "2a.grd": {
-+ "includes": [500], # "Tag".
-+ "structures": [510], # "Tag" (etc.).
-+ },
-+ # Depends on 2a.grd.
-+ "3a.grd": {
-+ "includes": [1000],
-+ },
-+ # Depends on 2a.grd, but overlaps with 3b.grd due to mutually exclusivity.
-+ "3b.grd": {
-+ "includes": [1000],
-+ },
-+ # Depends on {3a.grd, 3b.grd}.
-+ "4.grd": {
-+ "META": {"join": 2}, # Hint for update_resource_ids.
-+ "structures": [1500],
-+ },
-+ # Depends on 1.grd but overlaps with 2a.grd.
-+ "2b.grd": {
-+ "includes": [500],
-+ "structures": [540],
-+ },
-+ # Depends on {4.grd, 2b.grd}.
-+ "5.grd": {
-+ "META": {"join": 2}, # Hint for update_resource_ids.
-+ "includes": [600],
-+ },
-+ # Depends on 5.grd. File is generated, so hint is needed for sizes.
-+ "<(SHARED_INTERMEDIATE_DIR)/6.grd": {
-+ "META": {"sizes": {"includes": [10]}},
-+ "includes": [700],
-+ },
-+}
-+
-+The "structure" within a resouces_ids file are as follows:
-+1. Comments and spacing.
-+2. Item ordering, to establish dependency and grouping.
-+3. Special provision to allow ID overlaps from mutual exclusion.
-+
-+This module parses a resource_ids file, reads ID usages from GRD files it refers
-+to, and generates an updated version of the resource_ids file while preserving
-+structure elements 1-3 stated above.
-+"""
-+
-+from __future__ import print_function
-+
-+import collections
-+import getopt
-+import os
-+import shutil
-+import sys
-+import tempfile
-+
-+from grit.tool import interface
-+from grit.tool.update_resource_ids import assigner, common, parser, reader
-+
-+
-+def _ReadData(input_file):
-+ if input_file == '-':
-+ data = sys.stdin.read()
-+ file_dir = os.getcwd()
-+ else:
-+ with open(input_file, 'rt') as f:
-+ data = f.read()
-+ file_dir = os.path.dirname(input_file)
-+ return data, file_dir
-+
-+
-+def _MultiReplace(data, repl):
-+ """Multi-replacement of text |data| by ranges and replacement text.
-+
-+ Args:
-+ data: Original text.
-+ repl: List of (lo, hi, s) tuples, specifying that |data[lo:hi]| should be
-+ replaced with |s|. The ranges must be inside |data|, and not overlap.
-+ Returns: New text.
-+ """
-+ res = []
-+ prev = 0
-+ for (lo, hi, s) in sorted(repl):
-+ if prev < lo:
-+ res.append(data[prev:lo])
-+ res.append(s)
-+ prev = hi
-+ res.append(data[prev:])
-+ return ''.join(res)
-+
-+
-+def _WriteFileIfChanged(output, new_data):
-+ if not output:
-+ sys.stdout.write(new_data)
-+ return
-+
-+ # Avoid touching outputs if file contents has not changed so that ninja
-+ # does not rebuild dependent when not necessary.
-+ if os.path.exists(output) and _ReadData(output)[0] == new_data:
-+ return
-+
-+ # Write to a temporary file to ensure atomic changes.
-+ with tempfile.NamedTemporaryFile('wt', delete=False) as f:
-+ f.write(new_data)
-+ shutil.move(f.name, output)
-+
-+
-+class _Args:
-+ """Encapsulated arguments for this module."""
-+ def __init__(self):
-+ self.add_header = False
-+ self.analyze_inputs = False
-+ self.count = False
-+ self.depfile = None
-+ self.fake = False
-+ self.input = None
-+ self.naive = False
-+ self.output = None
-+ self.parse = False
-+ self.tokenize = False
-+
-+ @staticmethod
-+ def Parse(raw_args):
-+ own_opts, raw_args = getopt.getopt(raw_args, 'o:cpt', [
-+ 'add-header',
-+ 'analyze-inputs',
-+ 'count',
-+ 'depfile=',
-+ 'fake',
-+ 'naive',
-+ 'parse',
-+ 'tokenize',
-+ ])
-+ args = _Args();
-+ if not len(raw_args) == 1:
-+ print('grit update_resource_ids takes exactly one argument, the path to '
-+ 'the resource ids file.')
-+ return 2
-+ args.input = raw_args[0]
-+ for (key, val) in own_opts:
-+ if key == '-o':
-+ args.output = val
-+ elif key == '--add-header':
-+ args.add_header = True
-+ elif key == '--analyze-inputs':
-+ args.analyze_inputs = True
-+ elif key in ('--count', '-c'):
-+ args.count = True
-+ elif key == '--depfile':
-+ args.depfile = val
-+ elif key == '--fake':
-+ args.fake = True
-+ elif key == '--naive':
-+ args.naive = True
-+ elif key in ('--parse', '-p'):
-+ args.parse = True
-+ elif key in ('--tokenize', '-t'):
-+ args.tokenize = True
-+ return args
-+
-+
-+class UpdateResourceIds(interface.Tool):
-+ """Updates all start IDs in an resource_ids file by reading all GRD files it
-+refers to, estimating the number of required IDs of each type, then rewrites
-+start IDs while preserving structure.
-+
-+Usage: grit update_resource_ids [--parse|-p] [--read-grd|-r] [--tokenize|-t]
-+ [--naive] [--fake] [-o OUTPUT_FILE]
-+ [--analyze-inputs] [--depfile DEPFILE]
-+ [--add-header] RESOURCE_IDS_FILE
-+
-+RESOURCE_IDS_FILE is the path of the input resource_ids file.
-+
-+The -o option redirects output (default stdout) to OUPTUT_FILE, which can also
-+be RESOURCE_IDS_FILE.
-+
-+Other options:
-+
-+ -E NAME=VALUE Sets environment variable NAME to VALUE (within grit).
-+
-+ --count|-c Parses RESOURCE_IDS_FILE, reads the GRD files, and prints
-+ required sizes.
-+
-+ --fake For testing: Skips reading GRD files, and assigns 10 as the
-+ usage of every tag.
-+
-+ --naive Use naive coarse assigner.
-+
-+ --parse|-p Parses RESOURCE_IDS_FILE and dumps its nodes to console.
-+
-+ --tokenize|-t Tokenizes RESOURCE_IDS_FILE and reprints it as syntax-
-+ highlighted output.
-+
-+ --depfile=DEPFILE Write out a depfile for ninja to know about dependencies.
-+ --analyze-inputs Writes dependencies to stdout.
-+ --add-header Adds a "THIS FILE IS GENERATED" header to the output.
-+"""
-+
-+ def __init(self):
-+ super(UpdateResourceIds, self).__init__()
-+
-+ def ShortDescription(self):
-+ return 'Updates a resource_ids file based on usage, preserving structure'
-+
-+ def _DumpTokens(self, data, tok_gen):
-+ # Reprint |data| with syntax highlight.
-+ color_map = {
-+ '#': common.Color.GRAY,
-+ 'S': common.Color.CYAN,
-+ '0': common.Color.RED,
-+ '{': common.Color.YELLOW,
-+ '}': common.Color.YELLOW,
-+ '[': common.Color.GREEN,
-+ ']': common.Color.GREEN,
-+ ':': common.Color.MAGENTA,
-+ ',': common.Color.MAGENTA,
-+ }
-+ for t, lo, hi in tok_gen:
-+ c = color_map.get(t, common.Color.NONE)
-+ sys.stdout.write(c(data[lo:hi]))
-+
-+ def _DumpRootObj(self, root_obj):
-+ print(root_obj)
-+
-+ def _DumpResourceCounts(self, usage_gen):
-+ tot = collections.Counter()
-+ for item, tag_name_to_usage in usage_gen:
-+ c = common.Color.YELLOW if item.grd.startswith('<') else common.Color.CYAN
-+ print('%s: %r' % (c(item.grd), dict(tag_name_to_usage)))
-+ tot += collections.Counter(tag_name_to_usage)
-+ print(common.Color.GRAY('-' * 80))
-+ print('%s: %r' % (common.Color.GREEN('Total'), dict(tot)))
-+ print('%s: %d' % (common.Color.GREEN('Grand Total'), sum(tot.values())))
-+
-+ def Run(self, opts, raw_args):
-+ self.SetOptions(opts)
-+
-+ args = _Args.Parse(raw_args)
-+ data, file_dir = _ReadData(args.input)
-+
-+ tok_gen = parser.Tokenize(data)
-+ if args.tokenize:
-+ return self._DumpTokens(data, tok_gen)
-+
-+ root_obj = parser.ResourceIdParser(data, tok_gen).Parse()
-+ if args.parse:
-+ return self._DumpRootObj(root_obj)
-+ item_list = common.BuildItemList(root_obj)
-+
-+ src_dir = os.path.normpath(os.path.join(file_dir, root_obj['SRCDIR'].val))
-+ seen_files = set()
-+ usage_gen = reader.GenerateResourceUsages(item_list, src_dir, args.fake,
-+ seen_files)
-+ if args.count:
-+ return self._DumpResourceCounts(usage_gen)
-+ for item, tag_name_to_usage in usage_gen:
-+ item.SetUsages(tag_name_to_usage)
-+
-+ if args.analyze_inputs:
-+ print('\n'.join(sorted(seen_files)))
-+ return 0
-+
-+ new_ids_gen = assigner.GenerateNewIds(item_list, args.naive)
-+ # Create replacement specs usable by _MultiReplace().
-+ repl = [(tag.lo, tag.hi, str(new_id)) for tag, new_id in new_ids_gen]
-+ rel_input_dir = args.input
-+ # Update "SRCDIR" entry if output is specified.
-+ if args.output:
-+ new_srcdir = os.path.relpath(src_dir, os.path.dirname(args.output))
-+ repl.append((root_obj['SRCDIR'].lo, root_obj['SRCDIR'].hi,
-+ repr(new_srcdir)))
-+ rel_input_dir = os.path.join('$SRCDIR',
-+ os.path.relpath(rel_input_dir, new_srcdir))
-+
-+ new_data = _MultiReplace(data, repl)
-+ if args.add_header:
-+ header = []
-+ header.append('# GENERATED FILE.')
-+ header.append('# Edit %s instead.' % rel_input_dir)
-+ header.append('#' * 80)
-+ new_data = '\n'.join(header + ['']) + new_data
-+ _WriteFileIfChanged(args.output, new_data)
-+
-+ if args.depfile:
-+ deps_data = '{}: {}'.format(args.output, ' '.join(sorted(seen_files)))
-+ _WriteFileIfChanged(args.depfile, deps_data)
-diff --git a/tools/grit/grit/tool/update_resource_ids/assigner.py b/tools/grit/grit/tool/update_resource_ids/assigner.py
-new file mode 100644
-index 0000000000..6cd46031a6
---- /dev/null
-+++ b/tools/grit/grit/tool/update_resource_ids/assigner.py
-@@ -0,0 +1,286 @@
-+# 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.
-+"""Assign IDs to resource_ids file based on usage, while preserving structure.
-+
-+resource_ids assignment is divided into two parts:
-+(A) Coarse assignment: Assigns start IDs of items.
-+(B) Quota assignment: Assigns per-tag ID allotments for a given item, allowing
-+ padding for IDs.
-+
-+These parts are interdependent: Start IDs (A) of an item depends on ID
-+allotments (B) of *other* items; and ID allotment (B) of an item depends on its
-+start IDs (A) to compute alignment.
-+
-+(B) hides padding and alignment details of tags so that (A) can be abstracted
-+into the graph construction and traversal problem in DagCoarseIdAssigner.
-+"""
-+
-+import math
-+
-+from grit.tool.update_resource_ids import common
-+
-+
-+class Aligner:
-+ """Helper to allot IDs, given start ID and ID usage.
-+
-+ Args:
-+ expand: Scale factor relative to ID usage. Must be >= 1.0.
-+ slack: Minimum number of reserved ID at end. Must be >= 0.
-+ align: ID alignment of results. Must be >= 1.
-+ """
-+
-+ def __init__(self, expand=1.0, slack=0, align=1):
-+ assert expand >= 1.0 and slack >= 0 and align >= 1
-+ self._expand = expand
-+ self._slack = slack
-+ self._align = align
-+
-+ def Calc(self, cur_id, usage):
-+ quota = max(int(math.ceil(usage * self._expand)), usage + self._slack)
-+ return common.AlignUp(cur_id + quota, self._align)
-+
-+
-+class QuotaAssigner:
-+ """Main class for (B), for ID allotment of tags for an item."""
-+
-+ def __init__(self, aligner):
-+ self._aligner = aligner
-+
-+ def Gen(self, item, start_id):
-+ """Generates per-tag *end* ID in |item|, succeeding |start_id|."""
-+ cur_id = start_id
-+ for tag in item.tags: # Sorted by |tag.lo|.
-+ cur_id = self._aligner.Calc(cur_id, tag.usage)
-+ yield tag, cur_id
-+
-+
-+class BaseCoarseIdAssigner(object):
-+ """Base class for coarse assignment."""
-+
-+ def __init__(self, item_list, align):
-+ self._item_list = item_list
-+ self._align = align
-+
-+ def GenStartIds(self):
-+ """Visits |_item_list| and yields (|item|, new |start_id|).
-+
-+ Visit follows dependency order: If item B succeeds item A, then A is visited
-+ before B. Caller must call FeedWeight() to assign ID allotment.
-+ """
-+ raise NotImplementedError()
-+
-+ def FeedWeight(self, item, weight):
-+ """Callback to assign number of IDs allotted to |item|."""
-+ raise NotImplementedError()
-+
-+
-+class NaiveCoarseIdAssigner(BaseCoarseIdAssigner):
-+ """CoarseIdAssigner that assigns item with non-overlapping start IDs."""
-+
-+ def __init__(self, item_list, align):
-+ super(NaiveCoarseIdAssigner, self).__init__(item_list, align)
-+ first_id = self._item_list[0].tags[0].id
-+ self._cur_id = common.AlignUp(first_id, self._align)
-+
-+ def GenStartIds(self):
-+ """Visits items in array order."""
-+ for item in self._item_list:
-+ yield item, self._cur_id
-+
-+ def FeedWeight(self, item, weight):
-+ self._cur_id = common.AlignUp(self._cur_id + weight, self._align)
-+
-+
-+class DagCoarseIdAssigner(BaseCoarseIdAssigner):
-+ """CoarseIdAssigner that preserves existing structure.
-+
-+Start ID assignment in resource_ids is structured a Series-Parallel Graph, which
-+is a directed, acyclic multi-graph generated by the following:
-+* Start: Single directed edge. S <-- T.
-+* Operation 1: A <-- B becomes A <-- C <-- B.
-+* Operation 2: A <-- B becomes A <== B (add parallel edge).
-+
-+Each vertex (A, B, ...) is a start ID. S = globally minimal ID. T = \infty is an
-+implicit sentinel. Each edge maps to an item, and edge weight is ID allotment.
-+The edge A <-- B means "A's ID assignment needs to be determined before B's"
-+(i.e., "B depends on A"), and requires A < B.
-+
-+resource_ids stores a "flattened" representation of the graph, as a list of
-+items (with meta data). Thus coarse ID assignment consists of the following:
-+(1) Process list of items (with old start ID and meta data) to rebuild graph.
-+(2) Traverse graph in an order that satisfies dependencies.
-+(3) When vertex A has its ID assigned, the weight of each edge "A <--" (i.e., an
-+ item) can have its weight (ID allotment) computed via quota assignment of A.
-+(4) New start IDs satisfy A + w <= B for each edge A <-- B with weight w > 0.
-+
-+The key algorithm challenge is (1). Note that it does not need weight details,
-+so we only assume A < B whenever A <-- B. Now we're faced with 2 subproblems:
-+(1a) How to represent the graph as a list of integers (with meta data)?
-+(1b) Given the list representation, how to recover the graph?
-+
-+For (1a), we start with DFS traversal of the (transposed, i.e., reversed) graph
-+starting from S, and apply the following:
-+* For each edge A <-- B traversed, emit A into sequence,
-+* Traverse a B <-- Y only when all X <-- B have been traversed.
-+
-+The resulting sequence has the length as the number of edges, and has the useful
-+property that a vertex's dependencies always appear before the vertex itself!
-+Note this the sentinel T is omitted.
-+
-+Example 1:
-+ S <-- A <-- B <-- C <-- T => "SABC".
-+
-+Example 2:
-+ S <-- A <-- B <-- C <-- T => "SA|AB|SDEC|SF",
-+ | | | | | or "SF|SA|AB|SDEC",
-+ | + <-- + | | or "SDE|SA|ABC|SF",
-+ | | | or "SF|SDE|SA|ABC".
-+ + <---D <-- E <---+ |
-+ | |
-+ + <-- F <---------------+
-+
-+Here, "X|Y" denotes backtracking between visiting X and visiting Y. This appears
-+if and only if X >= Y, so "|" an optional (but illustrative) character that's
-+not in the actual output. We will use it consistently in comments, and so the
-+absence of "|" denotes the converse. For example, "XY" implies X < Y.
-+
-+In terms of the basic operations:
-+* Start: S <-- T => "S".
-+* Operation 1: "...AB..." => "...ACB..." (or "...A" => "...AB").
-+* Operation 2: "...AB..." => "...A|AB..." (or "...A" => "...A|A").
-+
-+For Example 2, a viable "evolution path" is:
-+"S" => "S|S" => "SC|S" => "S|SC|S" => "SA|SC|S" => "SAB|SC|S" => "SA|AB|SC|S"
-+ => "SA|AB|SDC|S" => "SA|AB|SDEC|S" => "SA|AB|SDEC|SF".
-+(Alternative: "S|S" => "S|SC" => etc.).
-+
-+Note: {A, ...} are *unlabelled* integers, and "spurious equalities" such as
-+A = D or A = F can occur!
-+
-+For (1b), we wish to build the graph from the sequence. This requires (1a) to be
-+injective (up to isomorphism). Unfortunately, this does not always hold.
-+Example:
-+ S <-- A <-- C <-- D <-- T => "SA|SBCD".
-+ | |
-+ + <-- B <---+
-+vs.
-+ S <----- A <----- D <-- T => "SA|SBCD".
-+ | |
-+ + <-- B <-- C <---+
-+
-+To fix this, we prepend a "join" label (*) to each vertex that has multiple
-+dependencies. With this, the example above produce different results:
-+ "SA|SB*CD" != "SA|SBC*D".
-+
-+Unfortunately, this is also inadequate. Example:
-+ S <-------- B <-- T => "S|S|S|S*A*B",
-+ | |
-+ + <---------+
-+ | |
-+ + <-- A <---+
-+ | |
-+ + <---+
-+vs.
-+ S <-------- B <-- T => "S|S|S|S*A*B".
-+ | |
-+ + <---A <---+
-+ | |
-+ + <---+
-+ | |
-+ + <---+
-+
-+To fix this, we also label the number of dependencies. In text representation,
-+we just show multiple (#dependencies - 1) copies of '*'. Now we have:
-+ "S|S|S|S*A**B" != "S|S|S|S**A*B".
-+
-+The "join" label with count adequately addresses the issue (proof omitted). In
-+the resource_ids files, these are stored as the "join" field of an item's meta
-+data.
-+
-+Additional comments for (1b) and other steps are detailed below.
-+"""
-+
-+ class DagNode:
-+ """An element of the implicit graph, corresponding to an item.
-+
-+ This actually represents an edge-vertex pair, corresponding to an item.
-+ A vertex is represented by a collection of DagNode that uses |sib| to link
-+ to a "representative node". The representative node, in turn, holds the list
-+ of all |deps| (dependencies) of the vertex.
-+ """
-+
-+ def __init__(self, item, old_id):
-+ self.item = item
-+ self.old_id = old_id
-+ self.new_id = None
-+ self.weight = None
-+
-+ def __init__(self, item_list, align):
-+ super(DagCoarseIdAssigner, self).__init__(item_list, align)
-+ self._node_dict = {} # Maps from |lo| to item.
-+
-+ def GenStartIds(self):
-+ """Traverses implicit graph and yields new IDs.
-+
-+ Algorithm: Process |old_id| of items sequentially. Successive items A and B
-+ can be "AB" (A < B), "A*...B" (A < B), or "A|B" (A >= B). "AB" and "A*...B"
-+ imply A <-- B, and are accumulated in |trace|. "A|B" are jumps that rewinds
-+ |trace| to the latest B (must exist), and A is pushed into |jumps|. A join
-+ "A*...B" also pops |num_join - 1| items {X_i} in |jump|, and X_i <-- B. In
-+ the end, unprocessed elements in |jumps| all link to sentinel T, and can be
-+ ignored.
-+ """
-+ # DagNode stack of "A" when "AB" is found (increasing |old_id|).
-+ trace = []
-+ # DagNode stack of "A" when "A|B" jumps is found.
-+ jumps = []
-+ for item in self._item_list: # Sorted by |lo|.
-+ meta = item.meta
-+ # |num_join| indicates "*" in "A*...B", and specify B's dependencies: +1
-+ # from A, and +count("*") from |jumps|.
-+ num_join = meta['join'].val if meta and 'join' in meta else None
-+ node = DagCoarseIdAssigner.DagNode(item, item.tags[0].id)
-+ self._node_dict[item.lo] = node
-+ if trace:
-+ if trace[-1].old_id >= node.old_id: # "A|B".
-+ if num_join:
-+ raise ValueError('Cannot join on jump: %d' % node.old_id)
-+ jumps.append(trace[-1]) # Add A to |jumps|, for later join.
-+ while trace and trace[-1].old_id > node.old_id: # Rewind to find B.
-+ trace.pop()
-+ if not trace or trace[-1].old_id != node.old_id: #
-+ raise ValueError('Cannot jump to unvisited: %d' % node.old_id)
-+ node.new_id = trace.pop().new_id # Copy B & remove. Will re-add B.
-+ else: # "AB" or "A*...B".
-+ node.new_id = trace[-1].new_id + trace[-1].weight # A --> B
-+ if num_join: # "A*...B".
-+ for _ in range(1, num_join):
-+ t = jumps.pop()
-+ node.new_id = max(node.new_id, t.new_id + t.weight) # X_i --> B.
-+ else:
-+ node.new_id = node.old_id # Initial S.
-+ trace.append(node) # Add B.
-+ align = meta['align'].val if meta and 'align' in meta else self._align
-+ node.new_id = common.AlignUp(node.new_id, align)
-+ yield node.item, node.new_id
-+ # Expect caller to calling FreedWeight() and update |node.weight|.
-+
-+ def FeedWeight(self, item, weight):
-+ self._node_dict[item.lo].weight = weight
-+
-+
-+def GenerateNewIds(item_list, use_naive):
-+ """Visits all tags in |item_list| and generates new ids.
-+
-+ New ids are generated based on old ids and usages.
-+ """
-+ Assigner = NaiveCoarseIdAssigner if use_naive else DagCoarseIdAssigner
-+ coarse_id_assigner = Assigner(item_list, 10)
-+ quota_assigner = QuotaAssigner(Aligner(expand=1.15, slack=3, align=10))
-+ for item, start_id in coarse_id_assigner.GenStartIds(): # Topo-sorted.
-+ cur_id = start_id
-+ for tag, next_id in quota_assigner.Gen(item, start_id): # Sorted by |lo|.
-+ yield tag, cur_id
-+ cur_id = next_id
-+ coarse_id_assigner.FeedWeight(item, next_id - start_id)
-diff --git a/tools/grit/grit/tool/update_resource_ids/assigner_unittest.py b/tools/grit/grit/tool/update_resource_ids/assigner_unittest.py
-new file mode 100644
-index 0000000000..164d820762
---- /dev/null
-+++ b/tools/grit/grit/tool/update_resource_ids/assigner_unittest.py
-@@ -0,0 +1,154 @@
-+#!/usr/bin/env python
-+# 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.
-+
-+from __future__ import print_function
-+
-+import os
-+import sys
-+import traceback
-+import unittest
-+if __name__ == '__main__':
-+ sys.path.append(os.path.join(os.path.dirname(__file__), '../../..'))
-+
-+from grit.tool.update_resource_ids import assigner, common, parser
-+
-+# |spec| format: A comma-separated list of (old) start IDs. Modifiers:
-+# * Prefix with n '*' to assign the item's META "join" field to n + 1.
-+# * Suffix with "+[usage]" to assign |usage| for the item (else default=10)
-+
-+
-+def _RenderTestResourceId(spec):
-+ """Renders barebone resource_ids data based on |spec|."""
-+ data = '{"SRCDIR": ".",'
-+ for i, tok in enumerate(spec.split(',')):
-+ num_star = len(tok) - len(tok.lstrip('*'))
-+ tok = tok[num_star:]
-+ meta = '"META":{"join": %d},' % (num_star + 1) if num_star else ''
-+ start_id = tok.split('+')[0] # Strip '+usage'.
-+ data += '"foo%d.grd": {%s"includes": [%s]},' % (i, meta, start_id)
-+ data += '}'
-+ return data
-+
-+
-+def _CreateTestItemList(spec):
-+ """Creates list of ItemInfo based on |spec|."""
-+ data = _RenderTestResourceId(spec)
-+ item_list = common.BuildItemList(
-+ parser.ResourceIdParser(data, parser.Tokenize(data)).Parse())
-+ # Assign usages from "id+usage", default to 10.
-+ for i, tok in enumerate(spec.split(',')):
-+ item_list[i].tags[0].usage = int((tok.split('+') + ['10'])[1])
-+ return item_list
-+
-+
-+def _RunCoarseIdAssigner(spec):
-+ item_list = _CreateTestItemList(spec)
-+ coarse_id_assigner = assigner.DagCoarseIdAssigner(item_list, 1)
-+ new_id_list = [] # List of new IDs, to check ID assignment.
-+ new_spec_list = [] # List of new tokens to construct new |spec|.
-+ for item, start_id in coarse_id_assigner.GenStartIds(): # Topo-sorted..
-+ new_id_list.append(str(start_id))
-+ meta = item.meta
-+ num_join = meta['join'].val if meta and 'join' in meta else 0
-+ t = '*' * max(0, num_join - 1)
-+ t += str(start_id)
-+ t += '' if item.tags[0].usage == 10 else '+' + str(item.tags[0].usage)
-+ new_spec_list.append((item.lo, t))
-+ coarse_id_assigner.FeedWeight(item, item.tags[0].usage)
-+ new_spec = ','.join(s for _, s in sorted(new_spec_list))
-+ return ','.join(new_id_list), new_spec
-+
-+
-+class AssignerUnittest(unittest.TestCase):
-+
-+ def testDagAssigner(self):
-+ test_cases = [
-+ # Trivial.
-+ ('0', '0'),
-+ ('137', '137'),
-+ ('5,15', '5,6'),
-+ ('11,18', '11+7,12'),
-+ ('5,5', '5,5'),
-+ # Series only.
-+ ('0,10,20,30,40', '0,1,2,3,4'),
-+ ('5,15,25,35,45,55', '5,6,7,8,9,10'),
-+ ('5,15,25,35,45,55', '5,7,100,101,256,1001'),
-+ ('0,10,20,45,85', '0,1,2+25,3+40,4'),
-+ # Branching with and without join.
-+ ('0,0,10,20,20,30,40', '0,0,1,2,2,3,4'),
-+ ('0,0,10,20,20,30,40', '0,0,*1,2,2,*3,4'),
-+ ('0,0,2,12,12,16,26', '0+4,0+2,1,2+8,2+4,3,4'),
-+ ('0,0,4,14,14,22,32', '0+4,0+2,*1,2+8,2+4,*3,4'),
-+ # Wide branching with and without join.
-+ ('0,10,10,10,10,10,10,20,30', '0,1,1,1,1,1,1,2,3'),
-+ ('0,10,10,10,10,10,10,20,30', '0,1,1,1,1,1,1,*****2,3'),
-+ ('0,2,2,2,2,2,2,7,17', '0+2,1+4,1+19,1,1+4,1+2,1+5,2,3'),
-+ ('0,2,2,2,2,2,2,21,31', '0+2,1+4,1+19,1,1+4,1+2,1+5,*****2,3'),
-+ # Expanding different branch, without join.
-+ ('0,10,10,10,60,70,80', '0,1+15,1+15,1+50,2,3,4'),
-+ ('0,10,10,10,25,35,45', '0,1+15,1+50,1+15,2,3,4'),
-+ ('0,10,10,10,25,35,45', '0,1+50,1+15,1+15,2,3,4'),
-+ # ... with join.
-+ ('0,10,10,10,60,70,80', '0,1+15,1+15,1+50,**2,3,4'),
-+ ('0,10,10,10,60,70,80', '0,1+15,1+50,1+15,**2,3,4'),
-+ ('0,10,10,10,60,70,80', '0,1+50,1+15,1+15,**2,3,4'),
-+ # ... with alternative join.
-+ ('0,10,10,10,60,70,80', '0,1+15,1+15,1+50,2,**3,4'),
-+ ('0,10,10,10,25,60,70', '0,1+15,1+50,1+15,2,**3,4'),
-+ ('0,10,10,10,25,60,70', '0,1+50,1+15,1+15,2,**3,4'),
-+ # Examples from assigner.py.
-+ ('0,10,10,20,0,10,20,30,0,10',
-+ '0,1,1,*2,0,4,5,*6,0,7'), # SA|AB|SDEC|SF
-+ ('0,10,0,10,20,30', '0,1,0,2,*3,4'), # SA|SB*CD
-+ ('0,10,0,10,20,30', '0,1,0,2,3,*4'), # SA|SBC*D
-+ ('0,7,0,5,11,21', '0+7,1+4,0+5,2+3,*3,4'), # SA|SB*CD
-+ ('0,7,0,5,8,18', '0+7,1+4,0+5,2+3,3,*4'), # SA|SBC*D
-+ ('0,0,0,0,10,20', '0,0,0,0,*1,**2'), # S|S|S|S*A**B
-+ ('0,0,0,0,10,20', '0,0,0,0,**1,*2'), # S|S|S|S**A*B
-+ ('0,0,0,0,6,16', '0+8,0+7,0+6,0+5,*1,**2'), # S|S|S|S*A**B
-+ ('0,0,0,0,7,17', '0+8,0+7,0+6,0+5,**1,*2'), # S|S|S|S**A*B
-+ # Long branches without join.
-+ ('0,10,0,0,10,20,0,10,20,30', '0,1,0,0,1,2,0,1,2,3'),
-+ ('0,30,0,0,20,30,0,10,13,28', '0+30,1,0+50,0+20,1,2+17,0,1+3,2+15,3'),
-+ # Long branches with join.
-+ ('0,10,0,0,10,20,0,10,20,30', '0,1,0,0,1,2,0,1,2,***3'),
-+ ('0,30,0,0,20,30,0,10,13,50',
-+ '0+30,1,0+50,0+20,1,2+17,0,1+3,2+15,***3'),
-+ # 2-level hierarchy.
-+ ('0,10,10,20,0,10,10,20,30', '0,1,1,*2,0,1,1,*2,*3'),
-+ ('0,2,2,10,0,3,3,6,34', '0+2,1+5,1+8,*2+24,0+3,1+2,1+3,*2+27,*3'),
-+ ('0,2,2,10,0,3,3,6,34', '0+2,1+5,1+8,*2+24,0+3,1+2,1+3,*2+28,*3'),
-+ ('0,2,2,10,0,3,3,6,35', '0+2,1+5,1+8,*2+24,0+3,1+2,1+3,*2+29,*3'),
-+ # Binary hierarchy.
-+ ('0,0,10,0,0,10,20,0,0,10,0,0,10,20,30',
-+ '0,0,*1,0,0,*1,*2,0,0,*1,0,0,*1,*2,*3'),
-+ ('0,0,2,0,0,5,11,0,0,8,0,0,5,14,18',
-+ '0+1,0+2,*1+3,0+4,0+5,*1+6,*2+7,0+8,0+7,*1+6,0+5,0+4,*1+3,*2+2,*3+1'),
-+ # Joining from different heads.
-+ ('0,10,20,30,40,30,20,10,0,50', '0,1,2,3,4,3,2,1,0,****5'),
-+ # Miscellaneous.
-+ ('0,1,0,11', '0+1,1,0,*1'),
-+ ]
-+ for exp, spec in test_cases:
-+ try:
-+ actual, new_spec = _RunCoarseIdAssigner(spec)
-+ self.failUnlessEqual(exp, actual)
-+ # Test that assignment is idempotent.
-+ actual2, new_spec2 = _RunCoarseIdAssigner(new_spec)
-+ self.failUnlessEqual(actual, actual2)
-+ self.failUnlessEqual(new_spec, new_spec2)
-+ except Exception as e:
-+ print(common.Color.RED(traceback.format_exc().rstrip()))
-+ print('Failed spec: %s' % common.Color.CYAN(spec))
-+ print(' Expected: %s' % common.Color.YELLOW(exp))
-+ print(' Actual: %s' % common.Color.YELLOW(actual))
-+ if new_spec != new_spec2:
-+ print('Not idempotent')
-+ if isinstance(e, AssertionError):
-+ raise e
-+
-+
-+if __name__ == '__main__':
-+ unittest.main()
-diff --git a/tools/grit/grit/tool/update_resource_ids/common.py b/tools/grit/grit/tool/update_resource_ids/common.py
-new file mode 100644
-index 0000000000..004d8aa0e3
---- /dev/null
-+++ b/tools/grit/grit/tool/update_resource_ids/common.py
-@@ -0,0 +1,101 @@
-+# 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.
-+
-+
-+def AlignUp(v, align):
-+ return (v + align - 1) // align * align
-+
-+
-+def StripPlural(s):
-+ assert s.endswith('s'), 'Expect %s to be plural' % s
-+ return s[:-1]
-+
-+
-+class Color:
-+
-+ def _MakeColor(code):
-+ t = '\033[' + code + 'm%s\033[0m'
-+ return lambda s: t % s
-+
-+ NONE = staticmethod(lambda s: s)
-+ RED = staticmethod(_MakeColor('31'))
-+ GREEN = staticmethod(_MakeColor('32'))
-+ YELLOW = staticmethod(_MakeColor('33'))
-+ BLUE = staticmethod(_MakeColor('34'))
-+ MAGENTA = staticmethod(_MakeColor('35'))
-+ CYAN = staticmethod(_MakeColor('36'))
-+ WHITE = staticmethod(_MakeColor('37'))
-+ GRAY = staticmethod(_MakeColor('30;1'))
-+
-+
-+class TagInfo:
-+ """Stores resource_ids tag entry (e.g., {"includes": 100} pair)."""
-+
-+ def __init__(self, raw_key, raw_value):
-+ """TagInfo Constructor.
-+
-+ Args:
-+ raw_key: parser.AnnotatedValue for the parsed key, e.g., "includes".
-+ raw_value: parser.AnnotatedValue for the parsed value, e.g., 100.
-+ """
-+ # Tag name, e.g., 'include' (no "s" at end).
-+ self.name = StripPlural(raw_key.val)
-+ # |len(raw_value) > 1| is possible, e.g., see grd_reader_unittest.py's
-+ # testAssignFirstIdsMultipleMessages. This feature seems unused though.
-+ # TODO(huangs): Reconcile this (may end up removing multi-value feature).
-+ assert len(raw_value) == 1
-+ # Inclusive start *position* of the tag's start ID in resource_ids.
-+ self.lo = raw_value[0].lo
-+ # Exclusive end *position* of the tag's start ID in resource_ids.
-+ self.hi = raw_value[0].hi
-+ # The tag's start ID. Initially the old value, but may be reassigned to new.
-+ self.id = raw_value[0].val
-+ # The number of IDs the tag uses, to be assigned by ItemInfo.SetUsages().
-+ self.usage = None
-+
-+
-+class ItemInfo:
-+ """resource_ids item, containing multiple TagInfo."""
-+
-+ def __init__(self, lo, grd, raw_item):
-+ # Inclusive start position of the item's key. Serve as unique identifier.
-+ self.lo = lo
-+ # The GRD filename for the item.
-+ self.grd = grd
-+ # Optional META information for the item.
-+ self.meta = None
-+ # List of TagInfo associated witih the item.
-+ self.tags = []
-+ for k, v in raw_item.items():
-+ if k.val == 'META':
-+ assert self.meta is None
-+ self.meta = v # Not flattened.
-+ else:
-+ self.tags.append(TagInfo(k, v))
-+ self.tags.sort(key=lambda tag: tag.lo)
-+
-+ def SetUsages(self, tag_name_to_usage):
-+ for tag in self.tags:
-+ tag.usage = tag_name_to_usage.get(tag.name, 0)
-+
-+
-+def BuildItemList(root_obj):
-+ """Extracts ID assignments and structure from parsed resource_ids.
-+
-+ Returns: A list of ItemInfo, ordered by |lo|.
-+ """
-+ item_list = []
-+ grd_seen = set()
-+ for raw_key, raw_item in root_obj.items(): # Unordered.
-+ grd = raw_key.val
-+ if grd == 'SRCDIR':
-+ continue
-+ if not grd.endswith('.grd'):
-+ raise ValueError('Invalid GRD file: %s' % grd)
-+ if grd in grd_seen:
-+ raise ValueError('Duplicate GRD: %s' % grd)
-+ grd_seen.add(grd)
-+ item_list.append(ItemInfo(raw_key.lo, grd, raw_item))
-+ item_list.sort(key=lambda item: item.lo)
-+ return item_list
-diff --git a/tools/grit/grit/tool/update_resource_ids/parser.py b/tools/grit/grit/tool/update_resource_ids/parser.py
-new file mode 100644
-index 0000000000..da956bbd1c
---- /dev/null
-+++ b/tools/grit/grit/tool/update_resource_ids/parser.py
-@@ -0,0 +1,231 @@
-+# 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.
-+"""Structure-preserving parser for resource_ids files.
-+
-+Naive usage of eval() destroys resource_ids structure. This module provides a
-+custom parser that annotates source byte ranges of "leaf values" (strings and
-+integers).
-+"""
-+
-+from __future__ import print_function
-+
-+_isWhitespace = lambda ch: ch in ' \t\n'
-+_isNotNewline = lambda ch: ch != '\n'
-+_isDigit = lambda ch: ch.isdigit()
-+
-+
-+def _RenderLineCol(data, pos):
-+ """Renders |pos| within text |data| in as text showing line and column."""
-+ # This is used to pinpoint fatal parse errors, so okay to be inefficient.
-+ new_lines = [i for i in range(pos) if data[i] == '\n']
-+ row = 1 + len(new_lines)
-+ col = (pos - new_lines[-1]) if new_lines else 1 + pos
-+ return 'line %d, column %d' % (row, col)
-+
-+
-+def Tokenize(data):
-+ """Generator to split |data| into tokens.
-+
-+ Each token is specified as |(t, lo, hi)|:
-+ * |t|: Type, with '#' = space / comments, '0' = int, 'S' = string, 'E' = end,
-+ and other characters denoting themselves.
-+ * |lo, hi|: Token's range within |data| (as |data[lo:hi]|).
-+ """
-+
-+ class ctx: # Local context for mutable data shared across inner functions.
-+ pos = 0
-+
-+ def _HasData():
-+ return ctx.pos < len(data)
-+
-+ # Returns True if ended by |not pred()|, or False if ended by EOF.
-+ def _EatWhile(pred):
-+ while _HasData():
-+ if pred(data[ctx.pos]):
-+ ctx.pos += 1
-+ else:
-+ return True
-+ return False
-+
-+ def _NextBlank():
-+ lo = ctx.pos
-+ while True:
-+ if not _EatWhile(_isWhitespace) or data[ctx.pos] != '#':
-+ break
-+ ctx.pos += 1
-+ if not _EatWhile(_isNotNewline):
-+ break
-+ ctx.pos += 1
-+ return None if ctx.pos == lo else (lo, ctx.pos)
-+
-+ def _EatString():
-+ lo = ctx.pos
-+ delim = data[ctx.pos]
-+ is_escaped = False
-+ ctx.pos += 1
-+ while _HasData():
-+ ch = data[ctx.pos]
-+ ctx.pos += 1
-+ if is_escaped:
-+ is_escaped = False
-+ elif ch == '\\':
-+ is_escaped = True
-+ elif ch == delim:
-+ return
-+ raise ValueError('Unterminated string at %s' % _RenderLineCol(data, lo))
-+
-+ while _HasData():
-+ blank = _NextBlank()
-+ if blank is not None:
-+ yield ('#', blank[0], blank[1])
-+ if not _HasData():
-+ break
-+ lo = ctx.pos
-+ ch = data[ctx.pos]
-+ if ch in '{}[],:':
-+ ctx.pos += 1
-+ t = ch
-+ elif ch.isdigit():
-+ _EatWhile(_isDigit)
-+ t = '0'
-+ elif ch in '+-':
-+ ctx.pos += 1
-+ if not _HasData() or not data[ctx.pos].isdigit():
-+ raise ValueError('Invalid int at %s' % _RenderLineCol(data, lo))
-+ _EatWhile(_isDigit)
-+ t = '0'
-+ elif ch in '"\'':
-+ _EatString()
-+ t = 'S'
-+ else:
-+ raise ValueError('Unknown char %s at %s' %
-+ (repr(ch), _RenderLineCol(data, lo)))
-+ yield (t, lo, ctx.pos)
-+ yield ('E', ctx.pos, ctx.pos) # End sentinel.
-+
-+
-+def _SkipBlanks(toks):
-+ """Generator to remove whitespace and comments from Tokenize()."""
-+ for t, lo, hi in toks:
-+ if t != '#':
-+ yield t, lo, hi
-+
-+
-+class AnnotatedValue:
-+ """Container for leaf values (ints or strings) with an annotated range."""
-+
-+ def __init__(self, val, lo, hi):
-+ self.val = val
-+ self.lo = lo
-+ self.hi = hi
-+
-+ def __str__(self):
-+ return '<%s@%d:%d>' % (str(self.val), self.lo, self.hi)
-+
-+ def __repr__(self):
-+ return '<%r@%d:%d>' % (self.val, self.lo, self.hi)
-+
-+ def __hash__(self):
-+ return hash(self.val)
-+
-+ def __eq__(self, other):
-+ return self.val == other
-+
-+
-+class ResourceIdParser:
-+ """resource_ids parser that stores leaf values as AnnotatedValue.
-+
-+ Algorithm: Use Tokenize() to split |data| into tokens and _SkipBlanks() to
-+ ignore comments and spacing, then apply a recursive parsing, using a one-token
-+ look-ahead for decision making.
-+ """
-+
-+ def __init__(self, data, tok_gen):
-+ self.data = data
-+ self.state = []
-+ self.toks = _SkipBlanks(tok_gen)
-+ self.tok_look_ahead = None
-+
-+ def _MakeErr(self, msg, pos):
-+ return ValueError(msg + ' at ' + _RenderLineCol(self.data, pos))
-+
-+ def _PeekTok(self):
-+ if self.tok_look_ahead is None:
-+ self.tok_look_ahead = next(self.toks)
-+ return self.tok_look_ahead
-+
-+ def _NextTok(self):
-+ if self.tok_look_ahead is None:
-+ return next(self.toks)
-+ ret = self.tok_look_ahead
-+ self.tok_look_ahead = None
-+ return ret
-+
-+ def _EatTok(self, exp_t, tok_name=None):
-+ t, lo, _ = self._NextTok()
-+ if t != exp_t:
-+ raise self._MakeErr('Bad token: Expect \'%s\'' % (tok_name or exp_t), lo)
-+
-+ def _NextIntOrString(self):
-+ t, lo, hi = self._NextTok()
-+ if t != '0' and t != 'S':
-+ raise self._MakeErr('Expected number or string', lo)
-+ value = eval(self.data[lo:hi])
-+ return AnnotatedValue(value, lo, hi)
-+
-+ # Consumes separator ',' and returns whether |end_ch| is encountered.
-+ def _EatSep(self, end_ch):
-+ t, lo, _ = self._PeekTok()
-+ if t == ',':
-+ self._EatTok(',')
-+ # Allow trailing ','.
-+ t, _, _ = self._PeekTok()
-+ return t == end_ch
-+ elif t == end_ch:
-+ return True
-+ else:
-+ raise self._MakeErr('Expect \',\' or \'%s\'' % end_ch, lo)
-+
-+ def _NextList(self):
-+ self._EatTok('[')
-+ ret = []
-+ t, _, _ = self._PeekTok()
-+ if t != ']':
-+ while True:
-+ ret.append(self._NextObject())
-+ if self._EatSep(']'):
-+ break
-+ self._EatTok(']')
-+ return ret
-+
-+ def _NextDict(self):
-+ self._EatTok('{')
-+ ret = {}
-+ t, _, _ = self._PeekTok()
-+ if t != '}':
-+ while True:
-+ k = self._NextIntOrString()
-+ self._EatTok(':')
-+ v = self._NextObject()
-+ ret[k] = v
-+ if self._EatSep('}'):
-+ break
-+ self._EatTok('}')
-+ return ret
-+
-+ def _NextObject(self):
-+ t, lo, _ = self._PeekTok()
-+ if t == '[':
-+ return self._NextList()
-+ elif t == '{':
-+ return self._NextDict()
-+ elif t == '0' or t == 'S':
-+ return self._NextIntOrString()
-+ else:
-+ raise self._MakeErr('Bad token: Type = %s' % t, lo)
-+
-+ def Parse(self):
-+ root_obj = self._NextObject()
-+ self._EatTok('E', 'EOF')
-+ return root_obj
-diff --git a/tools/grit/grit/tool/update_resource_ids/reader.py b/tools/grit/grit/tool/update_resource_ids/reader.py
-new file mode 100644
-index 0000000000..0a156d2deb
---- /dev/null
-+++ b/tools/grit/grit/tool/update_resource_ids/reader.py
-@@ -0,0 +1,83 @@
-+# 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.
-+"""Helpers to read GRD files and estimate resource ID usages.
-+
-+This module uses grit.grd_reader to estimate resource ID usages in GRD
-+(and GRDP) files by counting the occurrences of {include, message, structure}
-+tags. This approach avoids the complexties of conditional inclusions, but
-+produces a conservative estimate of ID usages.
-+"""
-+
-+from __future__ import print_function
-+
-+import collections
-+import os
-+
-+from grit import grd_reader
-+from grit import util
-+from grit.tool.update_resource_ids import common
-+
-+TAGS_OF_INTEREST = set(['include', 'message', 'structure'])
-+
-+def _CountResourceUsage(grd, seen_files):
-+ tag_name_to_count = {tag: set() for tag in TAGS_OF_INTEREST}
-+ # Pass '_chromium', but '_google_chrome' would produce the same result.
-+ root = grd_reader.Parse(grd, defines={'_chromium': True})
-+ seen_files.add(grd)
-+ # Count all descendant tags, regardless of whether they're active.
-+ for node in root.Preorder():
-+ if node.name in TAGS_OF_INTEREST:
-+ tag_name_to_count[node.name].add(node.attrs['name'])
-+ elif node.name == 'part':
-+ part_path = os.path.join(os.path.dirname(grd), node.GetInputPath())
-+ seen_files.add(util.normpath(part_path))
-+ return {k: len(v) for k, v in tag_name_to_count.items() if v}
-+
-+
-+def GenerateResourceUsages(item_list, src_dir, fake, seen_files):
-+ """Visits a list of ItemInfo to generate maps from tag name to usage.
-+
-+ Args:
-+ root_obj: Root dict of a resource_ids file.
-+ src_dir: Absolute directory of Chrome's src/ directory.
-+ fake: For testing: Sets 10 as usages for all tags, to avoid reading GRD.
-+ seen_files: A set to collect paths of files read.
-+ Yields:
-+ Tuple (item, tag_name_to_usage), where |item| is from |item_list| and
-+ |tag_name_to_usage| is a dict() mapping tag name to (int) usage.
-+ """
-+ if fake:
-+ for item in item_list:
-+ tag_name_to_usage = collections.Counter({t.name: 10 for t in item.tags})
-+ yield item, tag_name_to_usage
-+ return
-+ for item in item_list:
-+ supported_tag_names = set(tag.name for tag in item.tags)
-+ if item.meta and 'sizes' in item.meta:
-+ # If META has "sizes" field, use it instead of reading GRD.
-+ tag_name_to_usage = collections.Counter()
-+ for k, vlist in item.meta['sizes'].items():
-+ tag_name_to_usage[common.StripPlural(k.val)] = sum(v.val for v in vlist)
-+ tag_names = set(tag_name_to_usage.keys())
-+ if tag_names != supported_tag_names:
-+ raise ValueError('META "sizes" field have identical fields as actual '
-+ '"sizes" field.')
-+ else:
-+ # Generated GRD start with '<(SHARED_INTERMEDIATE_DIR)'. Just check '<'.
-+ if item.grd.startswith('<'):
-+ raise ValueError('%s: Generated GRD must use META with "sizes" field '
-+ 'to specify size bounds.' % item.grd)
-+ grd_file = os.path.join(src_dir, item.grd)
-+ if not os.path.exists(grd_file):
-+ # Silently skip missing files so that src-internal files do not break
-+ # public checkouts.
-+ yield item, {}
-+ continue
-+ tag_name_to_usage = _CountResourceUsage(grd_file, seen_files)
-+ tag_names = set(tag_name_to_usage.keys())
-+ if not tag_names.issubset(supported_tag_names):
-+ missing = [t + 's' for t in tag_names - supported_tag_names]
-+ raise ValueError(
-+ 'Resource ids for %s needs entry for %s' % (item.grd, missing))
-+ yield item, tag_name_to_usage
-diff --git a/tools/grit/grit/tool/xmb.py b/tools/grit/grit/tool/xmb.py
-new file mode 100644
-index 0000000000..b821308369
---- /dev/null
-+++ b/tools/grit/grit/tool/xmb.py
-@@ -0,0 +1,295 @@
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+"""The 'grit xmb' tool.
-+"""
-+
-+from __future__ import print_function
-+
-+import getopt
-+import os
-+import sys
-+
-+from xml.sax import saxutils
-+
-+import six
-+
-+from grit import grd_reader
-+from grit import lazy_re
-+from grit import tclib
-+from grit import util
-+from grit.tool import interface
-+
-+
-+# Used to collapse presentable content to determine if
-+# xml:space="preserve" is needed.
-+_WHITESPACES_REGEX = lazy_re.compile(r'\s\s*')
-+
-+
-+# See XmlEscape below.
-+_XML_QUOTE_ESCAPES = {
-+ u"'": u'&apos;',
-+ u'"': u'&quot;',
-+}
-+
-+def _XmlEscape(s):
-+ """Returns text escaped for XML in a way compatible with Google's
-+ internal Translation Console tool. May be used for attributes as
-+ well as for contents.
-+ """
-+ return saxutils.escape(six.text_type(s), _XML_QUOTE_ESCAPES).encode('utf-8')
-+
-+
-+def _WriteAttribute(file, name, value):
-+ """Writes an XML attribute to the specified file.
-+
-+ Args:
-+ file: file to write to
-+ name: name of the attribute
-+ value: (unescaped) value of the attribute
-+ """
-+ name = name.encode('utf-8')
-+ if value:
-+ file.write(b' %s="%s"' % (name, _XmlEscape(value)))
-+
-+
-+def _WriteMessage(file, message):
-+ presentable_content = message.GetPresentableContent()
-+ assert (isinstance(presentable_content, six.string_types) or
-+ (len(message.parts) == 1 and
-+ type(message.parts[0] == tclib.Placeholder)))
-+ preserve_space = presentable_content != _WHITESPACES_REGEX.sub(
-+ u' ', presentable_content.strip())
-+
-+ file.write(b'<msg')
-+ _WriteAttribute(file, 'desc', message.GetDescription())
-+ _WriteAttribute(file, 'id', message.GetId())
-+ _WriteAttribute(file, 'meaning', message.GetMeaning())
-+ if preserve_space:
-+ _WriteAttribute(file, 'xml:space', 'preserve')
-+ file.write(b'>')
-+ if not preserve_space:
-+ file.write(b'\n ')
-+
-+ parts = message.GetContent()
-+ for part in parts:
-+ if isinstance(part, tclib.Placeholder):
-+ file.write(b'<ph')
-+ _WriteAttribute(file, 'name', part.GetPresentation())
-+ file.write(b'><ex>')
-+ file.write(_XmlEscape(part.GetExample()))
-+ file.write(b'</ex>')
-+ file.write(_XmlEscape(part.GetOriginal()))
-+ file.write(b'</ph>')
-+ else:
-+ file.write(_XmlEscape(part))
-+ if not preserve_space:
-+ file.write(b'\n')
-+ file.write(b'</msg>\n')
-+
-+
-+def WriteXmbFile(file, messages):
-+ """Writes the given grit.tclib.Message items to the specified open
-+ file-like object in the XMB format.
-+ """
-+ file.write(b"""<?xml version="1.0" encoding="UTF-8"?>
-+<!DOCTYPE messagebundle [
-+<!ELEMENT messagebundle (msg)*>
-+<!ATTLIST messagebundle class CDATA #IMPLIED>
-+
-+<!ELEMENT msg (#PCDATA|ph|source)*>
-+<!ATTLIST msg id CDATA #IMPLIED>
-+<!ATTLIST msg seq CDATA #IMPLIED>
-+<!ATTLIST msg name CDATA #IMPLIED>
-+<!ATTLIST msg desc CDATA #IMPLIED>
-+<!ATTLIST msg meaning CDATA #IMPLIED>
-+<!ATTLIST msg obsolete (obsolete) #IMPLIED>
-+<!ATTLIST msg xml:space (default|preserve) "default">
-+<!ATTLIST msg is_hidden CDATA #IMPLIED>
-+
-+<!ELEMENT source (#PCDATA)>
-+
-+<!ELEMENT ph (#PCDATA|ex)*>
-+<!ATTLIST ph name CDATA #REQUIRED>
-+
-+<!ELEMENT ex (#PCDATA)>
-+]>
-+<messagebundle>
-+""")
-+ for message in messages:
-+ _WriteMessage(file, message)
-+ file.write(b'</messagebundle>')
-+
-+
-+class OutputXmb(interface.Tool):
-+ """Outputs all translateable messages in the .grd input file to an
-+.xmb file, which is the format used to give source messages to
-+Google's internal Translation Console tool. The format could easily
-+be used for other systems.
-+
-+Usage: grit xmb [-i|-h] [-l LIMITFILE] OUTPUTPATH
-+
-+OUTPUTPATH is the path you want to output the .xmb file to.
-+
-+The -l option can be used to output only some of the resources to the .xmb file.
-+LIMITFILE is the path to a file that is used to limit the items output to the
-+xmb file. If the filename extension is .grd, the file must be a .grd file
-+and the tool only output the contents of nodes from the input file that also
-+exist in the limit file (as compared on the 'name' attribute). Otherwise it must
-+contain a list of the IDs that output should be limited to, one ID per line, and
-+the tool will only output nodes with 'name' attributes that match one of the
-+IDs.
-+
-+The -i option causes 'grit xmb' to output an "IDs only" file instead of an XMB
-+file. The "IDs only" file contains the message ID of each message that would
-+normally be output to the XMB file, one message ID per line. It is designed for
-+use with the 'grit transl2tc' tool's -l option.
-+
-+Other options:
-+
-+ -D NAME[=VAL] Specify a C-preprocessor-like define NAME with optional
-+ value VAL (defaults to 1) which will be used to control
-+ conditional inclusion of resources.
-+
-+ -E NAME=VALUE Set environment variable NAME to VALUE (within grit).
-+
-+"""
-+ # The different output formats supported by this tool
-+ FORMAT_XMB = 0
-+ FORMAT_IDS_ONLY = 1
-+
-+ def __init__(self, defines=None):
-+ super(OutputXmb, self).__init__()
-+ self.format = self.FORMAT_XMB
-+ self.defines = defines or {}
-+
-+ def ShortDescription(self):
-+ return 'Exports all translateable messages into an XMB file.'
-+
-+ def Run(self, opts, args):
-+ os.environ['cwd'] = os.getcwd()
-+
-+ self.SetOptions(opts)
-+
-+ limit_file = None
-+ limit_is_grd = False
-+ limit_file_dir = None
-+ own_opts, args = getopt.getopt(args, 'l:D:ih', ('help',))
-+ for key, val in own_opts:
-+ if key == '-l':
-+ limit_file = open(val, 'r')
-+ limit_file_dir = util.dirname(val)
-+ if not len(limit_file_dir):
-+ limit_file_dir = '.'
-+ limit_is_grd = os.path.splitext(val)[1] == '.grd'
-+ elif key == '-i':
-+ self.format = self.FORMAT_IDS_ONLY
-+ elif key == '-D':
-+ name, val = util.ParseDefine(val)
-+ self.defines[name] = val
-+ elif key == '-E':
-+ (env_name, env_value) = val.split('=', 1)
-+ os.environ[env_name] = env_value
-+ elif key == '--help':
-+ self.ShowUsage()
-+ sys.exit(0)
-+ if not len(args) == 1:
-+ print('grit xmb takes exactly one argument, the path to the XMB file '
-+ 'to output.')
-+ return 2
-+
-+ xmb_path = args[0]
-+ res_tree = grd_reader.Parse(opts.input, debug=opts.extra_verbose, defines=self.defines)
-+ res_tree.SetOutputLanguage('en')
-+ res_tree.SetDefines(self.defines)
-+ res_tree.OnlyTheseTranslations([])
-+ res_tree.RunGatherers()
-+
-+ with open(xmb_path, 'wb') as output_file:
-+ self.Process(
-+ res_tree, output_file, limit_file, limit_is_grd, limit_file_dir)
-+ if limit_file:
-+ limit_file.close()
-+ print("Wrote %s" % xmb_path)
-+
-+ def Process(self, res_tree, output_file, limit_file=None, limit_is_grd=False,
-+ dir=None):
-+ """Writes a document with the contents of res_tree into output_file,
-+ limiting output to the IDs specified in limit_file, which is a GRD file if
-+ limit_is_grd is true, otherwise a file with one ID per line.
-+
-+ The format of the output document depends on this object's format attribute.
-+ It can be FORMAT_XMB or FORMAT_IDS_ONLY.
-+
-+ The FORMAT_IDS_ONLY format causes this function to write just a list
-+ of the IDs of all messages that would have been added to the XMB file, one
-+ ID per line.
-+
-+ The FORMAT_XMB format causes this function to output the (default) XMB
-+ format.
-+
-+ Args:
-+ res_tree: base.Node()
-+ output_file: file open for writing
-+ limit_file: None or file open for reading
-+ limit_is_grd: True | False
-+ dir: Directory of the limit file
-+ """
-+ if limit_file:
-+ if limit_is_grd:
-+ limit_list = []
-+ limit_tree = grd_reader.Parse(limit_file,
-+ dir=dir,
-+ debug=self.o.extra_verbose)
-+ for node in limit_tree:
-+ if 'name' in node.attrs:
-+ limit_list.append(node.attrs['name'])
-+ else:
-+ # Not a GRD file, so it's just a file with one ID per line
-+ limit_list = [item.strip() for item in limit_file.read().split('\n')]
-+
-+ ids_already_done = {}
-+ messages = []
-+ for node in res_tree:
-+ if (limit_file and
-+ not ('name' in node.attrs and node.attrs['name'] in limit_list)):
-+ continue
-+ if not node.IsTranslateable():
-+ continue
-+
-+ for clique in node.GetCliques():
-+ if not clique.IsTranslateable():
-+ continue
-+ if not clique.GetMessage().GetRealContent():
-+ continue
-+
-+ # Some explanation is in order here. Note that we can have
-+ # many messages with the same ID.
-+ #
-+ # The way we work around this is to maintain a list of cliques
-+ # per message ID (in the UberClique) and select the "best" one
-+ # (the first one that has a description, or an arbitrary one
-+ # if there is no description) for inclusion in the XMB file.
-+ # The translations are all going to be the same for messages
-+ # with the same ID, although the way we replace placeholders
-+ # might be slightly different.
-+ id = clique.GetMessage().GetId()
-+ if id in ids_already_done:
-+ continue
-+ ids_already_done[id] = 1
-+
-+ message = node.UberClique().BestClique(id).GetMessage()
-+ messages += [message]
-+
-+ # Ensure a stable order of messages, to help regression testing.
-+ messages.sort(key=lambda x:x.GetId())
-+
-+ if self.format == self.FORMAT_IDS_ONLY:
-+ # We just print the list of IDs to the output file.
-+ for msg in messages:
-+ output_file.write(msg.GetId())
-+ output_file.write('\n')
-+ else:
-+ assert self.format == self.FORMAT_XMB
-+ WriteXmbFile(output_file, messages)
-diff --git a/tools/grit/grit/tool/xmb_unittest.py b/tools/grit/grit/tool/xmb_unittest.py
-new file mode 100644
-index 0000000000..3c7e92cee7
---- /dev/null
-+++ b/tools/grit/grit/tool/xmb_unittest.py
-@@ -0,0 +1,132 @@
-+#!/usr/bin/env python
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Unit tests for 'grit xmb' tool.'''
-+
-+from __future__ import print_function
-+
-+import io
-+import os
-+import sys
-+if __name__ == '__main__':
-+ sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
-+
-+import unittest
-+import xml.sax
-+
-+from six import StringIO
-+
-+from grit import grd_reader
-+from grit import util
-+from grit.tool import xmb
-+
-+
-+class XmbUnittest(unittest.TestCase):
-+ def setUp(self):
-+ self.res_tree = grd_reader.Parse(
-+ io.BytesIO(u'''<?xml version="1.0" encoding="UTF-8"?>
-+ <grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
-+ <release seq="3">
-+ <includes>
-+ <include type="gif" name="ID_LOGO" file="images/logo.gif" />
-+ </includes>
-+ <messages>
-+ <message name="GOOD" desc="sub" sub_variable="true">
-+ excellent
-+ </message>
-+ <message name="IDS_GREETING" desc="Printed to greet the currently logged in user">
-+ Hello <ph name="USERNAME">%s<ex>Joi</ex></ph>, are you doing [GOOD] today?
-+ </message>
-+ <message name="IDS_BONGOBINGO">
-+ Yibbee
-+ </message>
-+ <message name="IDS_UNICODE">
-+ Ol\xe1, \u4eca\u65e5\u306f! \U0001F60A
-+ </message>
-+ </messages>
-+ <structures>
-+ <structure type="dialog" name="IDD_SPACYBOX" encoding="utf-16" file="grit/testdata/klonk.rc" />
-+ </structures>
-+ </release>
-+ </grit>'''.encode('utf-8')), '.')
-+ self.xmb_file = io.BytesIO()
-+
-+ def testNormalOutput(self):
-+ xmb.OutputXmb().Process(self.res_tree, self.xmb_file)
-+ output = self.xmb_file.getvalue().decode('utf-8')
-+ self.failUnless(output.count('Joi'))
-+ self.failUnless(output.count('Yibbee'))
-+ self.failUnless(output.count(u'Ol\xe1, \u4eca\u65e5\u306f! \U0001F60A'))
-+
-+ def testLimitList(self):
-+ limit_file = StringIO(
-+ 'IDS_BONGOBINGO\nIDS_DOES_NOT_EXIST\nIDS_ALSO_DOES_NOT_EXIST')
-+ xmb.OutputXmb().Process(self.res_tree, self.xmb_file, limit_file, False)
-+ output = self.xmb_file.getvalue().decode('utf-8')
-+ self.failUnless(output.count('Yibbee'))
-+ self.failUnless(not output.count('Joi'))
-+
-+ def testLimitGrd(self):
-+ limit_file = StringIO('''<?xml version="1.0" encoding="UTF-8"?>
-+ <grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
-+ <release seq="3">
-+ <messages>
-+ <message name="IDS_GREETING" desc="Printed to greet the currently logged in user">
-+ Hello <ph name="USERNAME">%s<ex>Joi</ex></ph>, how are you doing today?
-+ </message>
-+ </messages>
-+ </release>
-+ </grit>''')
-+ tool = xmb.OutputXmb()
-+ class DummyOpts(object):
-+ extra_verbose = False
-+ tool.o = DummyOpts()
-+ tool.Process(self.res_tree, self.xmb_file, limit_file, True, dir='.')
-+ output = self.xmb_file.getvalue().decode('utf-8')
-+ self.failUnless(output.count('Joi'))
-+ self.failUnless(not output.count('Yibbee'))
-+
-+ def testSubstitution(self):
-+ self.res_tree.SetOutputLanguage('en')
-+ os.chdir(util.PathFromRoot('.')) # so it can find klonk.rc
-+ self.res_tree.RunGatherers()
-+ xmb.OutputXmb().Process(self.res_tree, self.xmb_file)
-+ output = self.xmb_file.getvalue().decode('utf-8')
-+ self.failUnless(output.count(
-+ '<ph name="GOOD_1"><ex>excellent</ex>[GOOD]</ph>'))
-+
-+ def testLeadingTrailingWhitespace(self):
-+ # Regression test for problems outputting messages with leading or
-+ # trailing whitespace (these come in via structures only, as
-+ # message nodes already strip and store whitespace).
-+ self.res_tree.SetOutputLanguage('en')
-+ os.chdir(util.PathFromRoot('.')) # so it can find klonk.rc
-+ self.res_tree.RunGatherers()
-+ xmb.OutputXmb().Process(self.res_tree, self.xmb_file)
-+ output = self.xmb_file.getvalue().decode('utf-8')
-+ self.failUnless(output.count('OK ? </msg>'))
-+
-+ def testDisallowedChars(self):
-+ # Validate that the invalid unicode is not accepted. Since it's not valid,
-+ # we can't specify it in a string literal, so write as a byte sequence.
-+ bad_xml = io.BytesIO()
-+ bad_xml.write(b'''<?xml version="1.0" encoding="UTF-8"?>
-+ <grit latest_public_release="2" source_lang_id="en-US"
-+ current_release="3" base_dir=".">
-+ <release seq="3">
-+ <messages>
-+ <message name="ID_FOO">''')
-+ # UTF-8 corresponding to to \U00110000
-+ # http://apps.timwhitlock.info/unicode/inspect/hex/110000
-+ bad_xml.write(b'\xF4\x90\x80\x80')
-+ bad_xml.write(b'''</message>
-+ </messages>
-+ </release>
-+ </grit>''')
-+ bad_xml.seek(0)
-+ self.assertRaises(xml.sax.SAXParseException, grd_reader.Parse, bad_xml, '.')
-+
-+if __name__ == '__main__':
-+ unittest.main()
-diff --git a/tools/grit/grit/util.py b/tools/grit/grit/util.py
-new file mode 100644
-index 0000000000..98433d154c
---- /dev/null
-+++ b/tools/grit/grit/util.py
-@@ -0,0 +1,691 @@
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Utilities used by GRIT.
-+'''
-+
-+from __future__ import print_function
-+
-+import codecs
-+import io
-+import os
-+import re
-+import shutil
-+import sys
-+import tempfile
-+from xml.sax import saxutils
-+
-+import six
-+from six import StringIO
-+from six.moves import html_entities as entities
-+
-+from grit import lazy_re
-+
-+_root_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
-+
-+
-+# Unique constants for use by ReadFile().
-+BINARY = 0
-+
-+
-+# Unique constants representing data pack encodings.
-+_, UTF8, UTF16 = range(3)
-+
-+
-+def Encode(message, encoding):
-+ '''Returns a byte stream that represents |message| in the given |encoding|.'''
-+ # |message| is a python unicode string, so convert to a byte stream that
-+ # has the correct encoding requested for the datapacks. We skip the first
-+ # 2 bytes of text resources because it is the BOM.
-+ if encoding == UTF8:
-+ return message.encode('utf8')
-+ if encoding == UTF16:
-+ return message.encode('utf16')[2:]
-+ # Default is BINARY
-+ return message
-+
-+
-+# Matches all different types of linebreaks.
-+LINEBREAKS = re.compile('\r\n|\n|\r')
-+
-+def MakeRelativePath(base_path, path_to_make_relative):
-+ """Returns a relative path such from the base_path to
-+ the path_to_make_relative.
-+
-+ In other words, os.join(base_path,
-+ MakeRelativePath(base_path, path_to_make_relative))
-+ is the same location as path_to_make_relative.
-+
-+ Args:
-+ base_path: the root path
-+ path_to_make_relative: an absolute path that is on the same drive
-+ as base_path
-+ """
-+
-+ def _GetPathAfterPrefix(prefix_path, path_with_prefix):
-+ """Gets the subpath within in prefix_path for the path_with_prefix
-+ with no beginning or trailing path separators.
-+
-+ Args:
-+ prefix_path: the base path
-+ path_with_prefix: a path that starts with prefix_path
-+ """
-+ assert path_with_prefix.startswith(prefix_path)
-+ path_without_prefix = path_with_prefix[len(prefix_path):]
-+ normalized_path = os.path.normpath(path_without_prefix.strip(os.path.sep))
-+ if normalized_path == '.':
-+ normalized_path = ''
-+ return normalized_path
-+
-+ def _GetCommonBaseDirectory(*args):
-+ """Returns the common prefix directory for the given paths
-+
-+ Args:
-+ The list of paths (at least one of which should be a directory)
-+ """
-+ prefix = os.path.commonprefix(args)
-+ # prefix is a character-by-character prefix (i.e. it does not end
-+ # on a directory bound, so this code fixes that)
-+
-+ # if the prefix ends with the separator, then it is prefect.
-+ if len(prefix) > 0 and prefix[-1] == os.path.sep:
-+ return prefix
-+
-+ # We need to loop through all paths or else we can get
-+ # tripped up by "c:\a" and "c:\abc". The common prefix
-+ # is "c:\a" which is a directory and looks good with
-+ # respect to the first directory but it is clear that
-+ # isn't a common directory when the second path is
-+ # examined.
-+ for path in args:
-+ assert len(path) >= len(prefix)
-+ # If the prefix the same length as the path,
-+ # then the prefix must be a directory (since one
-+ # of the arguements should be a directory).
-+ if path == prefix:
-+ continue
-+ # if the character after the prefix in the path
-+ # is the separator, then the prefix appears to be a
-+ # valid a directory as well for the given path
-+ if path[len(prefix)] == os.path.sep:
-+ continue
-+ # Otherwise, the prefix is not a directory, so it needs
-+ # to be shortened to be one
-+ index_sep = prefix.rfind(os.path.sep)
-+ # The use "index_sep + 1" because it includes the final sep
-+ # and it handles the case when the index_sep is -1 as well
-+ prefix = prefix[:index_sep + 1]
-+ # At this point we backed up to a directory bound which is
-+ # common to all paths, so we can quit going through all of
-+ # the paths.
-+ break
-+ return prefix
-+
-+ prefix = _GetCommonBaseDirectory(base_path, path_to_make_relative)
-+ # If the paths had no commonality at all, then return the absolute path
-+ # because it is the best that can be done. If the path had to be relative
-+ # then eventually this absolute path will be discovered (when a build breaks)
-+ # and an appropriate fix can be made, but having this allows for the best
-+ # backward compatibility with the absolute path behavior in the past.
-+ if len(prefix) <= 0:
-+ return path_to_make_relative
-+ # Build a path from the base dir to the common prefix
-+ remaining_base_path = _GetPathAfterPrefix(prefix, base_path)
-+
-+ # The follow handles two case: "" and "foo\\bar"
-+ path_pieces = remaining_base_path.split(os.path.sep)
-+ base_depth_from_prefix = len([d for d in path_pieces if len(d)])
-+ base_to_prefix = (".." + os.path.sep) * base_depth_from_prefix
-+
-+ # Put add in the path from the prefix to the path_to_make_relative
-+ remaining_other_path = _GetPathAfterPrefix(prefix, path_to_make_relative)
-+ return base_to_prefix + remaining_other_path
-+
-+
-+KNOWN_SYSTEM_IDENTIFIERS = set()
-+
-+SYSTEM_IDENTIFIERS = None
-+
-+def SetupSystemIdentifiers(ids):
-+ '''Adds ids to a regexp of known system identifiers.
-+
-+ Can be called many times, ids will be accumulated.
-+
-+ Args:
-+ ids: an iterable of strings
-+ '''
-+ KNOWN_SYSTEM_IDENTIFIERS.update(ids)
-+ global SYSTEM_IDENTIFIERS
-+ SYSTEM_IDENTIFIERS = lazy_re.compile(
-+ ' | '.join([r'\b%s\b' % i for i in KNOWN_SYSTEM_IDENTIFIERS]),
-+ re.VERBOSE)
-+
-+
-+# Matches all of the resource IDs predefined by Windows.
-+SetupSystemIdentifiers((
-+ 'IDOK', 'IDCANCEL', 'IDC_STATIC', 'IDYES', 'IDNO',
-+ 'ID_FILE_NEW', 'ID_FILE_OPEN', 'ID_FILE_CLOSE', 'ID_FILE_SAVE',
-+ 'ID_FILE_SAVE_AS', 'ID_FILE_PAGE_SETUP', 'ID_FILE_PRINT_SETUP',
-+ 'ID_FILE_PRINT', 'ID_FILE_PRINT_DIRECT', 'ID_FILE_PRINT_PREVIEW',
-+ 'ID_FILE_UPDATE', 'ID_FILE_SAVE_COPY_AS', 'ID_FILE_SEND_MAIL',
-+ 'ID_FILE_MRU_FIRST', 'ID_FILE_MRU_LAST',
-+ 'ID_EDIT_CLEAR', 'ID_EDIT_CLEAR_ALL', 'ID_EDIT_COPY',
-+ 'ID_EDIT_CUT', 'ID_EDIT_FIND', 'ID_EDIT_PASTE', 'ID_EDIT_PASTE_LINK',
-+ 'ID_EDIT_PASTE_SPECIAL', 'ID_EDIT_REPEAT', 'ID_EDIT_REPLACE',
-+ 'ID_EDIT_SELECT_ALL', 'ID_EDIT_UNDO', 'ID_EDIT_REDO',
-+ 'VS_VERSION_INFO', 'IDRETRY',
-+ 'ID_APP_ABOUT', 'ID_APP_EXIT',
-+ 'ID_NEXT_PANE', 'ID_PREV_PANE',
-+ 'ID_WINDOW_NEW', 'ID_WINDOW_ARRANGE', 'ID_WINDOW_CASCADE',
-+ 'ID_WINDOW_TILE_HORZ', 'ID_WINDOW_TILE_VERT', 'ID_WINDOW_SPLIT',
-+ 'ATL_IDS_SCSIZE', 'ATL_IDS_SCMOVE', 'ATL_IDS_SCMINIMIZE',
-+ 'ATL_IDS_SCMAXIMIZE', 'ATL_IDS_SCNEXTWINDOW', 'ATL_IDS_SCPREVWINDOW',
-+ 'ATL_IDS_SCCLOSE', 'ATL_IDS_SCRESTORE', 'ATL_IDS_SCTASKLIST',
-+ 'ATL_IDS_MDICHILD', 'ATL_IDS_IDLEMESSAGE', 'ATL_IDS_MRU_FILE' ))
-+
-+
-+# Matches character entities, whether specified by name, decimal or hex.
-+_HTML_ENTITY = lazy_re.compile(
-+ '&(#(?P<decimal>[0-9]+)|#x(?P<hex>[a-fA-F0-9]+)|(?P<named>[a-z0-9]+));',
-+ re.IGNORECASE)
-+
-+# Matches characters that should be HTML-escaped. This is <, > and &, but only
-+# if the & is not the start of an HTML character entity.
-+_HTML_CHARS_TO_ESCAPE = lazy_re.compile(
-+ '"|<|>|&(?!#[0-9]+|#x[0-9a-z]+|[a-z]+;)',
-+ re.IGNORECASE | re.MULTILINE)
-+
-+
-+def ReadFile(filename, encoding):
-+ '''Reads and returns the entire contents of the given file.
-+
-+ Args:
-+ filename: The path to the file.
-+ encoding: A Python codec name or the special value: BINARY to read
-+ the file in binary mode.
-+ '''
-+ if encoding == BINARY:
-+ mode = 'rb'
-+ encoding = None
-+ else:
-+ mode = 'rU'
-+
-+ with io.open(filename, mode, encoding=encoding) as f:
-+ return f.read()
-+
-+
-+def WrapOutputStream(stream, encoding = 'utf-8'):
-+ '''Returns a stream that wraps the provided stream, making it write
-+ characters using the specified encoding.'''
-+ return codecs.getwriter(encoding)(stream)
-+
-+
-+def ChangeStdoutEncoding(encoding = 'utf-8'):
-+ '''Changes STDOUT to print characters using the specified encoding.'''
-+ # If we're unittesting, don't reconfigure.
-+ if isinstance(sys.stdout, StringIO):
-+ return
-+
-+ if sys.version_info.major < 3:
-+ # Python 2 has binary streams by default, so reconfigure directly.
-+ sys.stdout = WrapOutputStream(sys.stdout, encoding)
-+ sys.stderr = WrapOutputStream(sys.stderr, encoding)
-+ elif sys.version_info < (3, 7):
-+ # Python 3 has text streams by default, so we have to detach them first.
-+ sys.stdout = WrapOutputStream(sys.stdout.detach(), encoding)
-+ sys.stderr = WrapOutputStream(sys.stderr.detach(), encoding)
-+ else:
-+ # Python 3.7+ provides an API for this specifically.
-+ sys.stdout.reconfigure(encoding=encoding)
-+ sys.stderr.reconfigure(encoding=encoding)
-+
-+
-+def EscapeHtml(text, escape_quotes = False):
-+ '''Returns 'text' with <, > and & (and optionally ") escaped to named HTML
-+ entities. Any existing named entity or HTML entity defined by decimal or
-+ hex code will be left untouched. This is appropriate for escaping text for
-+ inclusion in HTML, but not for XML.
-+ '''
-+ def Replace(match):
-+ if match.group() == '&': return '&amp;'
-+ elif match.group() == '<': return '&lt;'
-+ elif match.group() == '>': return '&gt;'
-+ elif match.group() == '"':
-+ if escape_quotes: return '&quot;'
-+ else: return match.group()
-+ else: assert False
-+ out = _HTML_CHARS_TO_ESCAPE.sub(Replace, text)
-+ return out
-+
-+
-+def UnescapeHtml(text, replace_nbsp=True):
-+ '''Returns 'text' with all HTML character entities (both named character
-+ entities and those specified by decimal or hexadecimal Unicode ordinal)
-+ replaced by their Unicode characters (or latin1 characters if possible).
-+
-+ The only exception is that &nbsp; will not be escaped if 'replace_nbsp' is
-+ False.
-+ '''
-+ def Replace(match):
-+ groups = match.groupdict()
-+ if groups['hex']:
-+ return six.unichr(int(groups['hex'], 16))
-+ elif groups['decimal']:
-+ return six.unichr(int(groups['decimal'], 10))
-+ else:
-+ name = groups['named']
-+ if name == 'nbsp' and not replace_nbsp:
-+ return match.group() # Don't replace &nbsp;
-+ assert name != None
-+ if name in entities.name2codepoint:
-+ return six.unichr(entities.name2codepoint[name])
-+ else:
-+ return match.group() # Unknown HTML character entity - don't replace
-+
-+ out = _HTML_ENTITY.sub(Replace, text)
-+ return out
-+
-+
-+def EncodeCdata(cdata):
-+ '''Returns the provided cdata in either escaped format or <![CDATA[xxx]]>
-+ format, depending on which is more appropriate for easy editing. The data
-+ is escaped for inclusion in an XML element's body.
-+
-+ Args:
-+ cdata: 'If x < y and y < z then x < z'
-+
-+ Return:
-+ '<![CDATA[If x < y and y < z then x < z]]>'
-+ '''
-+ if cdata.count('<') > 1 or cdata.count('>') > 1 and cdata.count(']]>') == 0:
-+ return '<![CDATA[%s]]>' % cdata
-+ else:
-+ return saxutils.escape(cdata)
-+
-+
-+def FixupNamedParam(function, param_name, param_value):
-+ '''Returns a closure that is identical to 'function' but ensures that the
-+ named parameter 'param_name' is always set to 'param_value' unless explicitly
-+ set by the caller.
-+
-+ Args:
-+ function: callable
-+ param_name: 'bingo'
-+ param_value: 'bongo' (any type)
-+
-+ Return:
-+ callable
-+ '''
-+ def FixupClosure(*args, **kw):
-+ if not param_name in kw:
-+ kw[param_name] = param_value
-+ return function(*args, **kw)
-+ return FixupClosure
-+
-+
-+def PathFromRoot(path):
-+ r'''Takes a path relative to the root directory for GRIT (the one that grit.py
-+ resides in) and returns a path that is either absolute or relative to the
-+ current working directory (i.e .a path you can use to open the file).
-+
-+ Args:
-+ path: 'rel_dir\file.ext'
-+
-+ Return:
-+ 'c:\src\tools\rel_dir\file.ext
-+ '''
-+ return os.path.normpath(os.path.join(_root_dir, path))
-+
-+
-+def ParseGrdForUnittest(body, base_dir=None, predetermined_ids_file=None,
-+ run_gatherers=False):
-+ '''Parse a skeleton .grd file and return it, for use in unit tests.
-+
-+ Args:
-+ body: XML that goes inside the <release> element.
-+ base_dir: The base_dir attribute of the <grit> tag.
-+ '''
-+ from grit import grd_reader
-+ if isinstance(body, six.text_type):
-+ body = body.encode('utf-8')
-+ if base_dir is None:
-+ base_dir = PathFromRoot('.')
-+ lines = [b'<?xml version="1.0" encoding="UTF-8"?>']
-+ lines.append(b'<grit latest_public_release="2" current_release="3" '
-+ b'source_lang_id="en" base_dir="%s">' % base_dir.encode('utf-8'))
-+ if b'<outputs>' in body:
-+ lines.append(body)
-+ else:
-+ lines.append(b' <outputs></outputs>')
-+ lines.append(b' <release seq="3">')
-+ lines.append(body)
-+ lines.append(b' </release>')
-+ lines.append(b'</grit>')
-+ ret = grd_reader.Parse(io.BytesIO(b'\n'.join(lines)), dir='.')
-+ ret.SetOutputLanguage('en')
-+ if run_gatherers:
-+ ret.RunGatherers()
-+ ret.SetPredeterminedIdsFile(predetermined_ids_file)
-+ ret.InitializeIds()
-+ return ret
-+
-+
-+def StripBlankLinesAndComments(text):
-+ '''Strips blank lines and comments from C source code, for unit tests.'''
-+ return '\n'.join(line for line in text.splitlines()
-+ if line and not line.startswith('//'))
-+
-+
-+def dirname(filename):
-+ '''Version of os.path.dirname() that never returns empty paths (returns
-+ '.' if the result of os.path.dirname() is empty).
-+ '''
-+ ret = os.path.dirname(filename)
-+ if ret == '':
-+ ret = '.'
-+ return ret
-+
-+
-+def normpath(path):
-+ '''Version of os.path.normpath that also changes backward slashes to
-+ forward slashes when not running on Windows.
-+ '''
-+ # This is safe to always do because the Windows version of os.path.normpath
-+ # will replace forward slashes with backward slashes.
-+ path = path.replace('\\', '/')
-+ return os.path.normpath(path)
-+
-+
-+_LANGUAGE_SPLIT_RE = lazy_re.compile('-|_|/')
-+
-+
-+def CanonicalLanguage(code):
-+ '''Canonicalizes two-part language codes by using a dash and making the
-+ second part upper case. Returns one-part language codes unchanged.
-+
-+ Args:
-+ code: 'zh_cn'
-+
-+ Return:
-+ code: 'zh-CN'
-+ '''
-+ parts = _LANGUAGE_SPLIT_RE.split(code)
-+ code = [ parts[0] ]
-+ for part in parts[1:]:
-+ code.append(part.upper())
-+ return '-'.join(code)
-+
-+
-+_LANG_TO_CODEPAGE = {
-+ 'en' : 1252,
-+ 'fr' : 1252,
-+ 'it' : 1252,
-+ 'de' : 1252,
-+ 'es' : 1252,
-+ 'nl' : 1252,
-+ 'sv' : 1252,
-+ 'no' : 1252,
-+ 'da' : 1252,
-+ 'fi' : 1252,
-+ 'pt-BR' : 1252,
-+ 'ru' : 1251,
-+ 'ja' : 932,
-+ 'zh-TW' : 950,
-+ 'zh-CN' : 936,
-+ 'ko' : 949,
-+}
-+
-+
-+def LanguageToCodepage(lang):
-+ '''Returns the codepage _number_ that can be used to represent 'lang', which
-+ may be either in formats such as 'en', 'pt_br', 'pt-BR', etc.
-+
-+ The codepage returned will be one of the 'cpXXXX' codepage numbers.
-+
-+ Args:
-+ lang: 'de'
-+
-+ Return:
-+ 1252
-+ '''
-+ lang = CanonicalLanguage(lang)
-+ if lang in _LANG_TO_CODEPAGE:
-+ return _LANG_TO_CODEPAGE[lang]
-+ else:
-+ print("Not sure which codepage to use for %s, assuming cp1252" % lang)
-+ return 1252
-+
-+def NewClassInstance(class_name, class_type):
-+ '''Returns an instance of the class specified in classname
-+
-+ Args:
-+ class_name: the fully qualified, dot separated package + classname,
-+ i.e. "my.package.name.MyClass". Short class names are not supported.
-+ class_type: the class or superclass this object must implement
-+
-+ Return:
-+ An instance of the class, or None if none was found
-+ '''
-+ lastdot = class_name.rfind('.')
-+ module_name = ''
-+ if lastdot >= 0:
-+ module_name = class_name[0:lastdot]
-+ if module_name:
-+ class_name = class_name[lastdot+1:]
-+ module = __import__(module_name, globals(), locals(), [''])
-+ if hasattr(module, class_name):
-+ class_ = getattr(module, class_name)
-+ class_instance = class_()
-+ if isinstance(class_instance, class_type):
-+ return class_instance
-+ return None
-+
-+
-+def FixLineEnd(text, line_end):
-+ # First normalize
-+ text = text.replace('\r\n', '\n')
-+ text = text.replace('\r', '\n')
-+ # Then fix
-+ text = text.replace('\n', line_end)
-+ return text
-+
-+
-+def BoolToString(bool):
-+ if bool:
-+ return 'true'
-+ else:
-+ return 'false'
-+
-+
-+verbose = False
-+extra_verbose = False
-+
-+def IsVerbose():
-+ return verbose
-+
-+def IsExtraVerbose():
-+ return extra_verbose
-+
-+def ParseDefine(define):
-+ '''Parses a define argument and returns the name and value.
-+
-+ The format is either "NAME=VAL" or "NAME", using True as the default value.
-+ Values of "1"/"true" and "0"/"false" are transformed to True and False
-+ respectively.
-+
-+ Args:
-+ define: a string of the form "NAME=VAL" or "NAME".
-+
-+ Returns:
-+ A (name, value) pair. name is a string, value a string or boolean.
-+ '''
-+ parts = [part.strip() for part in define.split('=', 1)]
-+ assert len(parts) >= 1
-+ name = parts[0]
-+ val = True
-+ if len(parts) > 1:
-+ val = parts[1]
-+ if val == "1" or val == "true": val = True
-+ elif val == "0" or val == "false": val = False
-+ return (name, val)
-+
-+
-+class Substituter(object):
-+ '''Finds and substitutes variable names in text strings.
-+
-+ Given a dictionary of variable names and values, prepares to
-+ search for patterns of the form [VAR_NAME] in a text.
-+ The value will be substituted back efficiently.
-+ Also applies to tclib.Message objects.
-+ '''
-+
-+ def __init__(self):
-+ '''Create an empty substituter.'''
-+ self.substitutions_ = {}
-+ self.dirty_ = True
-+
-+ def AddSubstitutions(self, subs):
-+ '''Add new values to the substitutor.
-+
-+ Args:
-+ subs: A dictionary of new substitutions.
-+ '''
-+ self.substitutions_.update(subs)
-+ self.dirty_ = True
-+
-+ def AddMessages(self, messages, lang):
-+ '''Adds substitutions extracted from node.Message objects.
-+
-+ Args:
-+ messages: a list of node.Message objects.
-+ lang: The translation language to use in substitutions.
-+ '''
-+ subs = [(str(msg.attrs['name']), msg.Translate(lang)) for msg in messages]
-+ self.AddSubstitutions(dict(subs))
-+ self.dirty_ = True
-+
-+ def GetExp(self):
-+ '''Obtain a regular expression that will find substitution keys in text.
-+
-+ Create and cache if the substituter has been updated. Use the cached value
-+ otherwise. Keys will be enclosed in [square brackets] in text.
-+
-+ Returns:
-+ A regular expression object.
-+ '''
-+ if self.dirty_:
-+ components = [r'\[%s\]' % (k,) for k in self.substitutions_]
-+ self.exp = re.compile(r'(%s)' % ('|'.join(components),))
-+ self.dirty_ = False
-+ return self.exp
-+
-+ def Substitute(self, text):
-+ '''Substitute the variable values in the given text.
-+
-+ Text of the form [message_name] will be replaced by the message's value.
-+
-+ Args:
-+ text: A string of text.
-+
-+ Returns:
-+ A string of text with substitutions done.
-+ '''
-+ return ''.join([self._SubFragment(f) for f in self.GetExp().split(text)])
-+
-+ def _SubFragment(self, fragment):
-+ '''Utility function for Substitute.
-+
-+ Performs a simple substitution if the fragment is exactly of the form
-+ [message_name].
-+
-+ Args:
-+ fragment: A simple string.
-+
-+ Returns:
-+ A string with the substitution done.
-+ '''
-+ if len(fragment) > 2 and fragment[0] == '[' and fragment[-1] == ']':
-+ sub = self.substitutions_.get(fragment[1:-1], None)
-+ if sub is not None:
-+ return sub
-+ return fragment
-+
-+ def SubstituteMessage(self, msg):
-+ '''Apply substitutions to a tclib.Message object.
-+
-+ Text of the form [message_name] will be replaced by a new placeholder,
-+ whose presentation will take the form the message_name_{UsageCount}, and
-+ whose example will be the message's value. Existing placeholders are
-+ not affected.
-+
-+ Args:
-+ msg: A tclib.Message object.
-+
-+ Returns:
-+ A tclib.Message object, with substitutions done.
-+ '''
-+ from grit import tclib # avoid circular import
-+ counts = {}
-+ text = msg.GetPresentableContent()
-+ placeholders = []
-+ newtext = ''
-+ for f in self.GetExp().split(text):
-+ sub = self._SubFragment(f)
-+ if f != sub:
-+ f = str(f)
-+ count = counts.get(f, 0) + 1
-+ counts[f] = count
-+ name = "%s_%d" % (f[1:-1], count)
-+ placeholders.append(tclib.Placeholder(name, f, sub))
-+ newtext += name
-+ else:
-+ newtext += f
-+ if placeholders:
-+ return tclib.Message(newtext, msg.GetPlaceholders() + placeholders,
-+ msg.GetDescription(), msg.GetMeaning())
-+ else:
-+ return msg
-+
-+
-+class TempDir(object):
-+ '''Creates files with the specified contents in a temporary directory,
-+ for unit testing.
-+ '''
-+
-+ def __init__(self, file_data, mode='w'):
-+ self._tmp_dir_name = tempfile.mkdtemp()
-+ assert not os.listdir(self.GetPath())
-+ for name, contents in file_data.items():
-+ file_path = self.GetPath(name)
-+ dir_path = os.path.split(file_path)[0]
-+ if not os.path.exists(dir_path):
-+ os.makedirs(dir_path)
-+ with open(file_path, mode) as f:
-+ f.write(file_data[name])
-+
-+ def __enter__(self):
-+ return self
-+
-+ def __exit__(self, *exc_info):
-+ self.CleanUp()
-+
-+ def CleanUp(self):
-+ shutil.rmtree(self.GetPath())
-+
-+ def GetPath(self, name=''):
-+ name = os.path.join(self._tmp_dir_name, name)
-+ assert name.startswith(self._tmp_dir_name)
-+ return name
-+
-+ def AsCurrentDir(self):
-+ return self._AsCurrentDirClass(self.GetPath())
-+
-+ class _AsCurrentDirClass(object):
-+ def __init__(self, path):
-+ self.path = path
-+ def __enter__(self):
-+ self.oldpath = os.getcwd()
-+ os.chdir(self.path)
-+ def __exit__(self, *exc_info):
-+ os.chdir(self.oldpath)
-diff --git a/tools/grit/grit/util_unittest.py b/tools/grit/grit/util_unittest.py
-new file mode 100644
-index 0000000000..7d6efaf858
---- /dev/null
-+++ b/tools/grit/grit/util_unittest.py
-@@ -0,0 +1,118 @@
-+#!/usr/bin/env python
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Unit test that checks some of util functions.
-+'''
-+
-+from __future__ import print_function
-+
-+import os
-+import sys
-+if __name__ == '__main__':
-+ sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
-+
-+import unittest
-+
-+import six
-+
-+from grit import util
-+
-+
-+class UtilUnittest(unittest.TestCase):
-+ ''' Tests functions from util
-+ '''
-+
-+ def testNewClassInstance(self):
-+ # Test short class name with no fully qualified package name
-+ # Should fail, it is not supported by the function now (as documented)
-+ cls = util.NewClassInstance('grit.util.TestClassToLoad',
-+ TestBaseClassToLoad)
-+ self.failUnless(cls == None)
-+
-+ # Test non existent class name
-+ cls = util.NewClassInstance('grit.util_unittest.NotExistingClass',
-+ TestBaseClassToLoad)
-+ self.failUnless(cls == None)
-+
-+ # Test valid class name and valid base class
-+ cls = util.NewClassInstance('grit.util_unittest.TestClassToLoad',
-+ TestBaseClassToLoad)
-+ self.failUnless(isinstance(cls, TestBaseClassToLoad))
-+
-+ # Test valid class name with wrong hierarchy
-+ cls = util.NewClassInstance('grit.util_unittest.TestClassNoBase',
-+ TestBaseClassToLoad)
-+ self.failUnless(cls == None)
-+
-+ def testCanonicalLanguage(self):
-+ self.failUnless(util.CanonicalLanguage('en') == 'en')
-+ self.failUnless(util.CanonicalLanguage('pt_br') == 'pt-BR')
-+ self.failUnless(util.CanonicalLanguage('pt-br') == 'pt-BR')
-+ self.failUnless(util.CanonicalLanguage('pt-BR') == 'pt-BR')
-+ self.failUnless(util.CanonicalLanguage('pt/br') == 'pt-BR')
-+ self.failUnless(util.CanonicalLanguage('pt/BR') == 'pt-BR')
-+ self.failUnless(util.CanonicalLanguage('no_no_bokmal') == 'no-NO-BOKMAL')
-+
-+ def testUnescapeHtml(self):
-+ self.failUnless(util.UnescapeHtml('&#1010;') == six.unichr(1010))
-+ self.failUnless(util.UnescapeHtml('&#xABcd;') == six.unichr(43981))
-+
-+ def testRelativePath(self):
-+ """ Verify that MakeRelativePath works in some tricky cases."""
-+
-+ def TestRelativePathCombinations(base_path, other_path, expected_result):
-+ """ Verify that the relative path function works for
-+ the given paths regardless of whether or not they end with
-+ a trailing slash."""
-+ for path1 in [base_path, base_path + os.path.sep]:
-+ for path2 in [other_path, other_path + os.path.sep]:
-+ result = util.MakeRelativePath(path1, path2)
-+ self.failUnless(result == expected_result)
-+
-+ # set-up variables
-+ root_dir = 'c:%sa' % os.path.sep
-+ result1 = '..%sabc' % os.path.sep
-+ path1 = root_dir + 'bc'
-+ result2 = 'bc'
-+ path2 = '%s%s%s' % (root_dir, os.path.sep, result2)
-+ # run the tests
-+ TestRelativePathCombinations(root_dir, path1, result1)
-+ TestRelativePathCombinations(root_dir, path2, result2)
-+
-+ def testReadFile(self):
-+ def Test(data, encoding, expected_result):
-+ with open('testfile', 'wb') as f:
-+ f.write(data)
-+ self.assertEqual(util.ReadFile('testfile', encoding), expected_result)
-+
-+ test_std_newline = b'\xEF\xBB\xBFabc\ndef' # EF BB BF is UTF-8 BOM
-+ newlines = [b'\n', b'\r\n', b'\r']
-+
-+ with util.TempDir({}) as tmp_dir:
-+ with tmp_dir.AsCurrentDir():
-+ for newline in newlines:
-+ test = test_std_newline.replace(b'\n', newline)
-+ Test(test, util.BINARY, test)
-+ # utf-8 doesn't strip BOM
-+ Test(test, 'utf-8', test_std_newline.decode('utf-8'))
-+ # utf-8-sig strips BOM
-+ Test(test, 'utf-8-sig', test_std_newline.decode('utf-8')[1:])
-+ # test another encoding
-+ Test(test, 'cp1252', test_std_newline.decode('cp1252'))
-+ self.assertRaises(UnicodeDecodeError, Test, b'\x80', 'utf-8', None)
-+
-+
-+class TestBaseClassToLoad(object):
-+ pass
-+
-+class TestClassToLoad(TestBaseClassToLoad):
-+ pass
-+
-+class TestClassNoBase(object):
-+ pass
-+
-+
-+if __name__ == '__main__':
-+ unittest.main()
-diff --git a/tools/grit/grit/xtb_reader.py b/tools/grit/grit/xtb_reader.py
-new file mode 100644
-index 0000000000..e0f842588a
---- /dev/null
-+++ b/tools/grit/grit/xtb_reader.py
-@@ -0,0 +1,140 @@
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Fast and efficient parser for XTB files.
-+'''
-+
-+from __future__ import print_function
-+
-+import sys
-+import xml.sax
-+import xml.sax.handler
-+
-+import grit.node.base
-+
-+
-+class XtbContentHandler(xml.sax.handler.ContentHandler):
-+ '''A content handler that calls a given callback function for each
-+ translation in the XTB file.
-+ '''
-+
-+ def __init__(self, callback, defs=None, debug=False, target_platform=None):
-+ self.callback = callback
-+ self.debug = debug
-+ # 0 if we are not currently parsing a translation, otherwise the message
-+ # ID of that translation.
-+ self.current_id = 0
-+ # Empty if we are not currently parsing a translation, otherwise the
-+ # parts we have for that translation - a list of tuples
-+ # (is_placeholder, text)
-+ self.current_structure = []
-+ # Set to the language ID when we see the <translationbundle> node.
-+ self.language = ''
-+ # Keep track of the if block we're inside. We can't nest ifs.
-+ self.if_expr = None
-+ # Root defines to be used with if expr.
-+ if defs:
-+ self.defines = defs
-+ else:
-+ self.defines = {}
-+ # Target platform for build.
-+ if target_platform:
-+ self.target_platform = target_platform
-+ else:
-+ self.target_platform = sys.platform
-+
-+ def startElement(self, name, attrs):
-+ if name == 'translation':
-+ assert self.current_id == 0 and len(self.current_structure) == 0, (
-+ "Didn't expect a <translation> element here.")
-+ self.current_id = attrs.getValue('id')
-+ elif name == 'ph':
-+ assert self.current_id != 0, "Didn't expect a <ph> element here."
-+ self.current_structure.append((True, attrs.getValue('name')))
-+ elif name == 'translationbundle':
-+ self.language = attrs.getValue('lang')
-+ elif name in ('if', 'then', 'else'):
-+ assert self.if_expr is None, "Can't nest <if> or use <else> in xtb files"
-+ self.if_expr = attrs.getValue('expr')
-+
-+ def endElement(self, name):
-+ if name == 'translation':
-+ assert self.current_id != 0
-+
-+ defs = self.defines
-+ def pp_ifdef(define):
-+ return define in defs
-+ def pp_if(define):
-+ return define in defs and defs[define]
-+
-+ # If we're in an if block, only call the callback (add the translation)
-+ # if the expression is True.
-+ should_run_callback = True
-+ if self.if_expr:
-+ should_run_callback = grit.node.base.Node.EvaluateExpression(
-+ self.if_expr, self.defines, self.target_platform)
-+ if should_run_callback:
-+ self.callback(self.current_id, self.current_structure)
-+
-+ self.current_id = 0
-+ self.current_structure = []
-+ elif name == 'if':
-+ assert self.if_expr is not None
-+ self.if_expr = None
-+
-+ def characters(self, content):
-+ if self.current_id != 0:
-+ # We are inside a <translation> node so just add the characters to our
-+ # structure.
-+ #
-+ # This naive way of handling characters is OK because in the XTB format,
-+ # <ph> nodes are always empty (always <ph name="XXX"/>) and whitespace
-+ # inside the <translation> node should be preserved.
-+ self.current_structure.append((False, content))
-+
-+
-+class XtbErrorHandler(xml.sax.handler.ErrorHandler):
-+ def error(self, exception):
-+ pass
-+
-+ def fatalError(self, exception):
-+ raise exception
-+
-+ def warning(self, exception):
-+ pass
-+
-+
-+def Parse(xtb_file, callback_function, defs=None, debug=False,
-+ target_platform=None):
-+ '''Parse xtb_file, making a call to callback_function for every translation
-+ in the XTB file.
-+
-+ The callback function must have the signature as described below. The 'parts'
-+ parameter is a list of tuples (is_placeholder, text). The 'text' part is
-+ either the raw text (if is_placeholder is False) or the name of the placeholder
-+ (if is_placeholder is True).
-+
-+ Args:
-+ xtb_file: open('fr.xtb', 'rb')
-+ callback_function: def Callback(msg_id, parts): pass
-+ defs: None, or a dictionary of preprocessor definitions.
-+ debug: Default False. Set True for verbose debug output.
-+ target_platform: None, or a sys.platform-like identifier of the build
-+ target platform.
-+
-+ Return:
-+ The language of the XTB, e.g. 'fr'
-+ '''
-+ # Start by advancing the file pointer past the DOCTYPE thing, as the TC
-+ # uses a path to the DTD that only works in Unix.
-+ # TODO(joi) Remove this ugly hack by getting the TC gang to change the
-+ # XTB files somehow?
-+ front_of_file = xtb_file.read(1024)
-+ xtb_file.seek(front_of_file.find(b'<translationbundle'))
-+
-+ handler = XtbContentHandler(callback=callback_function, defs=defs,
-+ debug=debug, target_platform=target_platform)
-+ xml.sax.parse(xtb_file, handler)
-+ assert handler.language != ''
-+ return handler.language
-diff --git a/tools/grit/grit/xtb_reader_unittest.py b/tools/grit/grit/xtb_reader_unittest.py
-new file mode 100644
-index 0000000000..79c0ac9ef1
---- /dev/null
-+++ b/tools/grit/grit/xtb_reader_unittest.py
-@@ -0,0 +1,110 @@
-+#!/usr/bin/env python
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Unit tests for grit.xtb_reader'''
-+
-+from __future__ import print_function
-+
-+import io
-+import os
-+import sys
-+if __name__ == '__main__':
-+ sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
-+
-+import unittest
-+
-+from grit import util
-+from grit import xtb_reader
-+from grit.node import empty
-+
-+
-+class XtbReaderUnittest(unittest.TestCase):
-+ def testParsing(self):
-+ xtb_file = io.BytesIO(b'''<?xml version="1.0" encoding="UTF-8"?>
-+ <!DOCTYPE translationbundle>
-+ <translationbundle lang="fr">
-+ <translation id="5282608565720904145">Bingo.</translation>
-+ <translation id="2955977306445326147">Bongo longo.</translation>
-+ <translation id="238824332917605038">Hullo</translation>
-+ <translation id="6629135689895381486"><ph name="PROBLEM_REPORT"/> peut <ph name="START_LINK"/>utilisation excessive de majuscules<ph name="END_LINK"/>.</translation>
-+ <translation id="7729135689895381486">Hello
-+this is another line
-+and another
-+
-+and another after a blank line.</translation>
-+ </translationbundle>''')
-+
-+ messages = []
-+ def Callback(id, structure):
-+ messages.append((id, structure))
-+ xtb_reader.Parse(xtb_file, Callback)
-+ self.failUnless(len(messages[0][1]) == 1)
-+ self.failUnless(messages[3][1][0]) # PROBLEM_REPORT placeholder
-+ self.failUnless(messages[4][0] == '7729135689895381486')
-+ self.failUnless(messages[4][1][7][1] == 'and another after a blank line.')
-+
-+ def testParsingIntoMessages(self):
-+ root = util.ParseGrdForUnittest('''
-+ <messages>
-+ <message name="ID_MEGA">Fantastic!</message>
-+ <message name="ID_HELLO_USER">Hello <ph name="USERNAME">%s<ex>Joi</ex></ph></message>
-+ </messages>''')
-+
-+ msgs, = root.GetChildrenOfType(empty.MessagesNode)
-+ clique_mega = msgs.children[0].GetCliques()[0]
-+ msg_mega = clique_mega.GetMessage()
-+ clique_hello_user = msgs.children[1].GetCliques()[0]
-+ msg_hello_user = clique_hello_user.GetMessage()
-+
-+ xtb_file = io.BytesIO(b'''<?xml version="1.0" encoding="UTF-8"?>
-+ <!DOCTYPE translationbundle>
-+ <translationbundle lang="is">
-+ <translation id="%s">Meirihattar!</translation>
-+ <translation id="%s">Saelir <ph name="USERNAME"/></translation>
-+ </translationbundle>''' % (
-+ msg_mega.GetId().encode('utf-8'),
-+ msg_hello_user.GetId().encode('utf-8')))
-+
-+ xtb_reader.Parse(xtb_file,
-+ msgs.UberClique().GenerateXtbParserCallback('is'))
-+ self.assertEqual('Meirihattar!',
-+ clique_mega.MessageForLanguage('is').GetRealContent())
-+ self.failUnless('Saelir %s',
-+ clique_hello_user.MessageForLanguage('is').GetRealContent())
-+
-+ def testIfNodesWithUseNameForId(self):
-+ root = util.ParseGrdForUnittest('''
-+ <messages>
-+ <message name="ID_BINGO" use_name_for_id="true">Bingo!</message>
-+ </messages>''')
-+ msgs, = root.GetChildrenOfType(empty.MessagesNode)
-+ clique = msgs.children[0].GetCliques()[0]
-+ msg = clique.GetMessage()
-+
-+ xtb_file = io.BytesIO(b'''<?xml version="1.0" encoding="UTF-8"?>
-+ <!DOCTYPE translationbundle>
-+ <translationbundle lang="is">
-+ <if expr="is_linux">
-+ <translation id="ID_BINGO">Bongo!</translation>
-+ </if>
-+ <if expr="not is_linux">
-+ <translation id="ID_BINGO">Congo!</translation>
-+ </if>
-+ </translationbundle>''')
-+ xtb_reader.Parse(xtb_file,
-+ msgs.UberClique().GenerateXtbParserCallback('is'),
-+ target_platform='darwin')
-+ self.assertEqual('Congo!', clique.MessageForLanguage('is').GetRealContent())
-+
-+ def testParseLargeFile(self):
-+ def Callback(id, structure):
-+ pass
-+ path = util.PathFromRoot('grit/testdata/generated_resources_fr.xtb')
-+ with open(path, 'rb') as xtb:
-+ xtb_reader.Parse(xtb, Callback)
-+
-+
-+if __name__ == '__main__':
-+ unittest.main()
-diff --git a/tools/grit/grit_info.py b/tools/grit/grit_info.py
-new file mode 100644
-index 0000000000..55738f25f6
---- /dev/null
-+++ b/tools/grit/grit_info.py
-@@ -0,0 +1,173 @@
-+#!/usr/bin/env python
-+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+'''Tool to determine inputs and outputs of a grit file.
-+'''
-+
-+from __future__ import print_function
-+
-+import optparse
-+import os
-+import posixpath
-+import sys
-+
-+from grit import grd_reader
-+from grit import util
-+
-+class WrongNumberOfArguments(Exception):
-+ pass
-+
-+
-+def Outputs(filename, defines, ids_file, target_platform=None):
-+ grd = grd_reader.Parse(
-+ filename, defines=defines, tags_to_ignore=set(['messages']),
-+ first_ids_file=ids_file, target_platform=target_platform)
-+
-+ target = []
-+ lang_folders = {}
-+ # Add all explicitly-specified output files
-+ for output in grd.GetOutputFiles():
-+ path = output.GetFilename()
-+ target.append(path)
-+
-+ if path.endswith('.h'):
-+ path, filename = os.path.split(path)
-+ if output.attrs['lang']:
-+ lang_folders[output.attrs['lang']] = os.path.dirname(path)
-+
-+ return [t.replace('\\', '/') for t in target]
-+
-+
-+def GritSourceFiles():
-+ files = []
-+ grit_root_dir = os.path.relpath(os.path.dirname(__file__), os.getcwd())
-+ for root, dirs, filenames in os.walk(grit_root_dir):
-+ grit_src = [os.path.join(root, f) for f in filenames
-+ if f.endswith('.py') and not f.endswith('_unittest.py')]
-+ files.extend(grit_src)
-+ return sorted(files)
-+
-+
-+def Inputs(filename, defines, ids_file, target_platform=None):
-+ grd = grd_reader.Parse(
-+ filename, debug=False, defines=defines, tags_to_ignore=set(['message']),
-+ first_ids_file=ids_file, target_platform=target_platform)
-+ files = set()
-+ for lang, ctx, fallback in grd.GetConfigurations():
-+ # TODO(tdanderson): Refactor all places which perform the action of setting
-+ # output attributes on the root. See crbug.com/503637.
-+ grd.SetOutputLanguage(lang or grd.GetSourceLanguage())
-+ grd.SetOutputContext(ctx)
-+ grd.SetFallbackToDefaultLayout(fallback)
-+ for node in grd.ActiveDescendants():
-+ with node:
-+ if (node.name == 'structure' or node.name == 'skeleton' or
-+ (node.name == 'file' and node.parent and
-+ node.parent.name == 'translations')):
-+ path = node.GetInputPath()
-+ if path is not None:
-+ files.add(grd.ToRealPath(path))
-+
-+ # If it's a flattened node, grab inlined resources too.
-+ if node.name == 'structure' and node.attrs['flattenhtml'] == 'true':
-+ node.RunPreSubstitutionGatherer()
-+ files.update(node.GetHtmlResourceFilenames())
-+ elif node.name == 'grit':
-+ first_ids_file = node.GetFirstIdsFile()
-+ if first_ids_file:
-+ files.add(first_ids_file)
-+ elif node.name == 'include':
-+ files.add(grd.ToRealPath(node.GetInputPath()))
-+ # If it's a flattened node, grab inlined resources too.
-+ if node.attrs['flattenhtml'] == 'true':
-+ files.update(node.GetHtmlResourceFilenames())
-+ elif node.name == 'part':
-+ files.add(util.normpath(os.path.join(os.path.dirname(filename),
-+ node.GetInputPath())))
-+
-+ cwd = os.getcwd()
-+ return [os.path.relpath(f, cwd) for f in sorted(files)]
-+
-+
-+def PrintUsage():
-+ print('USAGE: ./grit_info.py --inputs [-D foo] [-f resource_ids] <grd-file>')
-+ print(' ./grit_info.py --outputs [-D foo] [-f resource_ids] ' +
-+ '<out-prefix> <grd-file>')
-+
-+
-+def DoMain(argv):
-+ os.environ['cwd'] = os.getcwd()
-+
-+ parser = optparse.OptionParser()
-+ parser.add_option("--inputs", action="store_true", dest="inputs")
-+ parser.add_option("--outputs", action="store_true", dest="outputs")
-+ parser.add_option("-D", action="append", dest="defines", default=[])
-+ # grit build also supports '-E KEY=VALUE', support that to share command
-+ # line flags.
-+ parser.add_option("-E", action="append", dest="build_env", default=[])
-+ parser.add_option("-p", action="store", dest="predetermined_ids_file")
-+ parser.add_option("-w", action="append", dest="whitelist_files", default=[])
-+ parser.add_option("-f", dest="ids_file", default="")
-+ parser.add_option("-t", dest="target_platform", default=None)
-+
-+ options, args = parser.parse_args(argv)
-+
-+ defines = {}
-+ for define in options.defines:
-+ name, val = util.ParseDefine(define)
-+ defines[name] = val
-+
-+ for env_pair in options.build_env:
-+ (env_name, env_value) = env_pair.split('=', 1)
-+ os.environ[env_name] = env_value
-+
-+ if options.inputs:
-+ if len(args) > 1:
-+ raise WrongNumberOfArguments("Expected 0 or 1 arguments for --inputs.")
-+
-+ inputs = []
-+ if len(args) == 1:
-+ filename = args[0]
-+ inputs = Inputs(filename, defines, options.ids_file,
-+ options.target_platform)
-+
-+ # Add in the grit source files. If one of these change, we want to re-run
-+ # grit.
-+ inputs.extend(GritSourceFiles())
-+ inputs = [f.replace('\\', '/') for f in inputs]
-+
-+ if len(args) == 1:
-+ # Include grd file as second input (works around gyp expecting it).
-+ inputs.insert(1, args[0])
-+ if options.whitelist_files:
-+ inputs.extend(options.whitelist_files)
-+ return '\n'.join(inputs)
-+ elif options.outputs:
-+ if len(args) != 2:
-+ raise WrongNumberOfArguments(
-+ "Expected exactly 2 arguments for --outputs.")
-+
-+ prefix, filename = args
-+ outputs = [posixpath.join(prefix, f)
-+ for f in Outputs(filename, defines,
-+ options.ids_file, options.target_platform)]
-+ return '\n'.join(outputs)
-+ else:
-+ raise WrongNumberOfArguments("Expected --inputs or --outputs.")
-+
-+
-+def main(argv):
-+ try:
-+ result = DoMain(argv[1:])
-+ except WrongNumberOfArguments as e:
-+ PrintUsage()
-+ print(e)
-+ return 1
-+ print(result)
-+ return 0
-+
-+
-+if __name__ == '__main__':
-+ sys.exit(main(sys.argv))
-diff --git a/tools/grit/grit_rule.gni b/tools/grit/grit_rule.gni
-new file mode 100644
-index 0000000000..fb107ef1a3
---- /dev/null
-+++ b/tools/grit/grit_rule.gni
-@@ -0,0 +1,485 @@
-+# Copyright 2014 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+# Instantiate grit. This will produce a script target to run grit (named
-+# ${target_name}_grit), and a static library that compiles the .cc files.
-+#
-+# In general, code should depend on the static library. However, if the
-+# generated files are only processed by other actions to generate other
-+# files, it is possible to depend on the script target directly.
-+#
-+# Parameters
-+#
-+# source (required)
-+# Path to .grd file.
-+#
-+# enable_input_discovery_for_gn_analyze (default=true)
-+# Runs grit_info.py via exec_script() when compute_inputs_for_analyze=true
-+# in order to discover all files that affect this target.
-+# Turn this off when the .grd file is generated, or an <include> with
-+# flattenhtml=true points to a generated file.
-+# For "gn analyze" to be correct with this arg disabled, all inputs
-+# must be listed via |inputs|.
-+#
-+# inputs (optional)
-+# List of additional files, required for grit to process source file.
-+#
-+# outputs (required)
-+# List of outputs from grit, relative to the target_gen_dir. Grit will
-+# verify at build time that this list is correct and will fail if there
-+# is a mismatch between the outputs specified by the .grd file and the
-+# outputs list here.
-+#
-+# To get this list, you can look in the .grd file for
-+# <output filename="..." and put those filename here. The base directory
-+# of the list in Grit and the output list specified in the GN grit target
-+# are the same (the target_gen_dir) so you can generally copy the names
-+# exactly.
-+#
-+# To get the list of outputs programatically, run:
-+# python tools/grit/grit_info.py --outputs . path/to/your.grd
-+# And strip the leading "./" from the output files.
-+#
-+# defines (optional)
-+# Extra defines to pass to grit (on top of the global grit_defines list).
-+#
-+# grit_flags (optional)
-+# List of strings containing extra command-line flags to pass to Grit.
-+#
-+# resource_ids (optional)
-+# Path to a grit "firstidsfile". Default is
-+# //tools/gritsettings/resource_ids. Set to "" to use the value specified
-+# in the <grit> nodes of the processed files.
-+#
-+# output_dir (optional)
-+# Directory for generated files. If you specify this, you will often
-+# want to specify output_name if the target name is not particularly
-+# unique, since this can cause files from multiple grit targets to
-+# overwrite each other.
-+#
-+# output_name (optional)
-+# Provide an alternate base name for the generated files, like the .d
-+# files. Normally these are based on the target name and go in the
-+# output_dir, but if multiple targets with the same name end up in
-+# the same output_dir, they can collide.
-+#
-+# configs (optional)
-+# List of additional configs to be applied to the generated target.
-+#
-+# deps (optional)
-+# testonly (optional)
-+# visibility (optional)
-+# Normal meaning.
-+#
-+# Example
-+#
-+# grit("my_resources") {
-+# # Source and outputs are required.
-+# source = "myfile.grd"
-+# outputs = [
-+# "foo_strings.h",
-+# "foo_strings.pak",
-+# ]
-+#
-+# grit_flags = [ "-E", "foo=bar" ] # Optional extra flags.
-+# # You can also put deps here if the grit source depends on generated
-+# # files.
-+# }
-+import("//build/config/chrome_build.gni")
-+import("//build/config/chromeos/ui_mode.gni")
-+import("//build/config/compiler/compiler.gni")
-+import("//build/config/compute_inputs_for_analyze.gni")
-+import("//build/config/crypto.gni")
-+import("//build/config/features.gni")
-+import("//build/config/sanitizers/sanitizers.gni")
-+import("//build/config/ui.gni")
-+import("//build/toolchain/gcc_toolchain.gni")
-+
-+declare_args() {
-+ enable_resource_whitelist_generation = is_android && is_official_build
-+}
-+
-+if (enable_resource_whitelist_generation) {
-+ assert(target_os == "android" || target_os == "win",
-+ "unsupported platform for resource whitelist generation")
-+ assert(
-+ symbol_level > 0 && !strip_debug_info && !is_component_build,
-+ "resource whitelist generation only works on non-component builds with debug info enabled.")
-+}
-+
-+grit_defines = []
-+
-+if (is_mac || is_win || is_linux || is_chromeos || is_ios) {
-+ grit_defines += [
-+ "-D",
-+ "scale_factors=2x",
-+ ]
-+}
-+
-+# Mac and iOS want Title Case strings.
-+use_titlecase_in_grd_files = is_apple
-+if (use_titlecase_in_grd_files) {
-+ grit_defines += [
-+ "-D",
-+ "use_titlecase",
-+ ]
-+}
-+
-+if (is_chrome_branded) {
-+ grit_defines += [
-+ "-D",
-+ "_google_chrome",
-+ "-E",
-+ "CHROMIUM_BUILD=google_chrome",
-+ ]
-+} else {
-+ grit_defines += [
-+ "-D",
-+ "_chromium",
-+ "-E",
-+ "CHROMIUM_BUILD=chromium",
-+ ]
-+}
-+
-+if (is_chromeos) {
-+ grit_defines += [
-+ "-D",
-+ "chromeos",
-+ ]
-+}
-+
-+if (chromeos_is_browser_only) {
-+ grit_defines += [
-+ "-D",
-+ "lacros",
-+ ]
-+}
-+
-+if (is_desktop_linux) {
-+ grit_defines += [
-+ "-D",
-+ "desktop_linux",
-+ ]
-+}
-+
-+if (toolkit_views) {
-+ grit_defines += [
-+ "-D",
-+ "toolkit_views",
-+ ]
-+}
-+
-+if (use_aura) {
-+ grit_defines += [
-+ "-D",
-+ "use_aura",
-+ ]
-+}
-+
-+if (use_nss_certs) {
-+ grit_defines += [
-+ "-D",
-+ "use_nss_certs",
-+ ]
-+}
-+
-+if (use_ozone) {
-+ grit_defines += [
-+ "-D",
-+ "use_ozone",
-+ ]
-+}
-+
-+if (is_android) {
-+ grit_defines += [
-+ "-E",
-+ "ANDROID_JAVA_TAGGED_ONLY=true",
-+ ]
-+}
-+
-+# When cross-compiling, explicitly pass the target system to grit.
-+if (current_toolchain != host_toolchain) {
-+ if (is_android) {
-+ grit_defines += [
-+ "-t",
-+ "android",
-+ ]
-+ }
-+ if (is_ios) {
-+ grit_defines += [
-+ "-t",
-+ "ios",
-+ ]
-+ }
-+ if (is_linux || is_chromeos) {
-+ grit_defines += [
-+ "-t",
-+ "linux2",
-+ ]
-+ }
-+ if (is_mac) {
-+ grit_defines += [
-+ "-t",
-+ "darwin",
-+ ]
-+ }
-+ if (is_win) {
-+ grit_defines += [
-+ "-t",
-+ "win32",
-+ ]
-+ }
-+}
-+
-+_strip_resource_files = is_android && is_official_build
-+_js_minifier = "//tools/grit/minify_with_uglify.py"
-+_css_minifier = "//tools/grit/minimize_css.py"
-+
-+grit_resource_id_target = "//tools/gritsettings:default_resource_ids"
-+grit_resource_id_file =
-+ get_label_info(grit_resource_id_target, "target_gen_dir") +
-+ "/default_resource_ids"
-+grit_info_script = "//tools/grit/grit_info.py"
-+
-+# TODO(asvitkine): Add predetermined ids files for other platforms.
-+grit_predetermined_resource_ids_file = ""
-+if (is_mac) {
-+ grit_predetermined_resource_ids_file =
-+ "//tools/gritsettings/startup_resources_mac.txt"
-+}
-+if (is_win) {
-+ grit_predetermined_resource_ids_file =
-+ "//tools/gritsettings/startup_resources_win.txt"
-+}
-+
-+template("grit") {
-+ if (defined(invoker.output_dir)) {
-+ _output_dir = invoker.output_dir
-+ } else {
-+ _output_dir = target_gen_dir
-+ }
-+
-+ _grit_outputs =
-+ get_path_info(rebase_path(invoker.outputs, ".", _output_dir), "abspath")
-+
-+ # Add .info output for all pak files
-+ _pak_info_outputs = []
-+ foreach(output, _grit_outputs) {
-+ if (get_path_info(output, "extension") == "pak") {
-+ _pak_info_outputs += [ output + ".info" ]
-+ }
-+ }
-+
-+ if (defined(invoker.output_name)) {
-+ _grit_output_name = invoker.output_name
-+ } else {
-+ _grit_output_name = target_name
-+ }
-+
-+ _grit_custom_target = target_name + "_grit"
-+ action(_grit_custom_target) {
-+ testonly = defined(invoker.testonly) && invoker.testonly
-+
-+ script = "//tools/grit/grit.py"
-+ depfile = "$target_gen_dir/$target_name.d"
-+
-+ inputs = [ invoker.source ]
-+ deps = [ "//tools/grit:grit_sources" ]
-+ outputs = [ "${depfile}.stamp" ] + _grit_outputs + _pak_info_outputs
-+
-+ _grit_flags = grit_defines
-+
-+ # Add extra defines with -D flags.
-+ if (defined(invoker.defines)) {
-+ foreach(i, invoker.defines) {
-+ _grit_flags += [
-+ "-D",
-+ i,
-+ ]
-+ }
-+ }
-+
-+ if (defined(invoker.grit_flags)) {
-+ _grit_flags += invoker.grit_flags
-+ }
-+
-+ _rebased_source_path = rebase_path(invoker.source, root_build_dir)
-+ _enable_grit_info =
-+ !defined(invoker.enable_input_discovery_for_gn_analyze) ||
-+ invoker.enable_input_discovery_for_gn_analyze
-+ if (_enable_grit_info && compute_inputs_for_analyze) {
-+ # Only call exec_script when the user has explicitly opted into greater
-+ # precision at the expense of performance.
-+ _rel_inputs = exec_script("//tools/grit/grit_info.py",
-+ [
-+ "--inputs",
-+ _rebased_source_path,
-+ ] + _grit_flags,
-+ "list lines")
-+ inputs += rebase_path(_rel_inputs, ".", root_build_dir)
-+ }
-+
-+ args = [
-+ "-i",
-+ _rebased_source_path,
-+ "build",
-+ "-o",
-+ rebase_path(_output_dir, root_build_dir),
-+ "--depdir",
-+ ".",
-+ "--depfile",
-+ rebase_path(depfile, root_build_dir),
-+ "--write-only-new=1",
-+ "--depend-on-stamp",
-+ ] + _grit_flags
-+
-+ # Add brotli executable if using brotli.
-+ if (defined(invoker.use_brotli) && invoker.use_brotli) {
-+ _brotli_target = "//third_party/brotli:brotli($host_toolchain)"
-+ _brotli_executable = get_label_info(_brotli_target, "root_out_dir") +
-+ "/" + get_label_info(_brotli_target, "name")
-+ if (host_os == "win") {
-+ _brotli_executable += ".exe"
-+ }
-+
-+ inputs += [ _brotli_executable ]
-+ args += [
-+ "--brotli",
-+ rebase_path(_brotli_executable, root_build_dir),
-+ ]
-+ }
-+
-+ _resource_ids = grit_resource_id_file
-+ if (defined(invoker.resource_ids)) {
-+ _resource_ids = invoker.resource_ids
-+ }
-+
-+ if (_resource_ids != "") {
-+ inputs += [ _resource_ids ]
-+ args += [
-+ "-f",
-+ rebase_path(_resource_ids, root_build_dir),
-+ ]
-+ if (_resource_ids == grit_resource_id_file) {
-+ deps += [ grit_resource_id_target ]
-+ }
-+ }
-+ if (grit_predetermined_resource_ids_file != "") {
-+ inputs += [ grit_predetermined_resource_ids_file ]
-+ args += [
-+ "-p",
-+ rebase_path(grit_predetermined_resource_ids_file, root_build_dir),
-+ ]
-+ }
-+
-+ # We want to make sure the declared outputs actually match what Grit is
-+ # writing. We write the list to a file (some of the output lists are long
-+ # enough to not fit on a Windows command line) and ask Grit to verify those
-+ # are the actual outputs at runtime.
-+ _asserted_list_file =
-+ "$target_out_dir/${_grit_output_name}_expected_outputs.txt"
-+ write_file(_asserted_list_file,
-+ rebase_path(invoker.outputs, root_build_dir, _output_dir))
-+ inputs += [ _asserted_list_file ]
-+ args += [
-+ "--assert-file-list",
-+ rebase_path(_asserted_list_file, root_build_dir),
-+ ]
-+
-+ if (enable_resource_whitelist_generation) {
-+ _rc_grit_outputs = []
-+ foreach(output, _grit_outputs) {
-+ if (get_path_info(output, "extension") == "rc") {
-+ _rc_grit_outputs += [ output ]
-+ }
-+ }
-+
-+ if (_rc_grit_outputs != []) {
-+ # Resource whitelisting cannot be used with .rc files.
-+ # Make sure that there aren't any .pak outputs which would require
-+ # whitelist annotations.
-+ assert(_pak_info_outputs == [], "can't combine .pak and .rc outputs")
-+ } else {
-+ args += [ "--whitelist-support" ]
-+ }
-+ }
-+ if (_strip_resource_files) {
-+ _js_minifier_command = rebase_path(_js_minifier, root_build_dir)
-+ _css_minifier_command = rebase_path(_css_minifier, root_build_dir)
-+ args += [
-+ "--js-minifier",
-+ _js_minifier_command,
-+ "--css-minifier",
-+ _css_minifier_command,
-+ ]
-+ inputs += [
-+ _js_minifier,
-+ _css_minifier,
-+ ]
-+ }
-+
-+ if (defined(invoker.visibility)) {
-+ # This needs to include both what the invoker specified (since they
-+ # probably include generated headers from this target), as well as the
-+ # generated source set (since there's no guarantee that the visibility
-+ # specified by the invoker includes our target).
-+ #
-+ # Only define visibility at all if the invoker specified it. Otherwise,
-+ # we want to keep the public "no visibility specified" default.
-+ visibility = [ ":${invoker.target_name}" ] + invoker.visibility
-+ }
-+
-+ if (defined(invoker.use_brotli) && invoker.use_brotli) {
-+ if (is_mac && is_asan) {
-+ deps += [ "//tools/grit:brotli_mac_asan_workaround" ]
-+ } else {
-+ deps += [ "//third_party/brotli:brotli($host_toolchain)" ]
-+ }
-+ }
-+ if (defined(invoker.deps)) {
-+ deps += invoker.deps
-+ }
-+ if (defined(invoker.inputs)) {
-+ inputs += invoker.inputs
-+ }
-+ }
-+
-+ # This is the thing that people actually link with, it must be named the
-+ # same as the argument the template was invoked with.
-+ source_set(target_name) {
-+ testonly = defined(invoker.testonly) && invoker.testonly
-+
-+ # Since we generate a file, we need to be run before the targets that
-+ # depend on us.
-+ sources = []
-+ foreach(_output, _grit_outputs) {
-+ _extension = get_path_info(_output, "extension")
-+ if (_extension != "json" && _extension != "gz" && _extension != "pak" &&
-+ _extension != "xml") {
-+ sources += [ _output ]
-+ }
-+ }
-+
-+ # Deps set on the template invocation will go on the action that runs
-+ # grit above rather than this library. This target needs to depend on the
-+ # action publicly so other scripts can take the outputs from the grit
-+ # script as inputs.
-+ public_deps = [ ":$_grit_custom_target" ]
-+
-+ deps = [ "//base" ]
-+
-+ if (defined(invoker.public_configs)) {
-+ public_configs += invoker.public_configs
-+ }
-+
-+ if (defined(invoker.configs)) {
-+ configs += invoker.configs
-+ }
-+
-+ if (defined(invoker.visibility)) {
-+ visibility = invoker.visibility
-+ }
-+ output_name = _grit_output_name
-+ }
-+}
-diff --git a/tools/grit/minify_with_uglify.py b/tools/grit/minify_with_uglify.py
-new file mode 100644
-index 0000000000..788ffa6a75
---- /dev/null
-+++ b/tools/grit/minify_with_uglify.py
-@@ -0,0 +1,44 @@
-+#!/usr/bin/env python
-+# 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.
-+
-+from __future__ import print_function
-+
-+import os
-+import sys
-+import tempfile
-+
-+_HERE_PATH = os.path.dirname(__file__)
-+_SRC_PATH = os.path.normpath(os.path.join(_HERE_PATH, '..', '..'))
-+sys.path.append(os.path.join(_SRC_PATH, 'third_party', 'node'))
-+
-+import node
-+import node_modules
-+
-+def Minify(source):
-+ # Open two temporary files, so that uglify can read the input from one and
-+ # write its output to the other.
-+ with tempfile.NamedTemporaryFile(suffix='.js') as infile, \
-+ tempfile.NamedTemporaryFile(suffix='.js') as outfile:
-+ infile.write(source)
-+ infile.flush();
-+ node.RunNode([
-+ node_modules.PathToUglify(), infile.name, '--output', outfile.name])
-+ result = outfile.read()
-+ return result
-+
-+
-+def main():
-+ orig_stdout = sys.stdout
-+ result = ''
-+ try:
-+ sys.stdout = sys.stderr
-+ result = Minify(sys.stdin.read())
-+ finally:
-+ sys.stdout = orig_stdout
-+ print(result)
-+
-+
-+if __name__ == '__main__':
-+ main()
-diff --git a/tools/grit/minimize_css.py b/tools/grit/minimize_css.py
-new file mode 100644
-index 0000000000..2c3b8aeb1e
---- /dev/null
-+++ b/tools/grit/minimize_css.py
-@@ -0,0 +1,105 @@
-+#!/usr/bin/env python
-+# Copyright 2016 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+import re
-+import sys
-+
-+class CSSMinimizer(object):
-+
-+ INITIAL = 0
-+ MAYBE_COMMENT_START = 1
-+ INSIDE_COMMENT = 2
-+ MAYBE_COMMENT_END = 3
-+ INSIDE_SINGLE_QUOTE = 4
-+ INSIDE_SINGLE_QUOTE_ESCAPE = 5
-+ INSIDE_DOUBLE_QUOTE = 6
-+ INSIDE_DOUBLE_QUOTE_ESCAPE = 7
-+
-+ def __init__(self):
-+ self._output = ''
-+ self._codeblock = ''
-+
-+ def flush_codeblock(self):
-+ stripped = re.sub(r"\s+", ' ', self._codeblock)
-+ stripped = re.sub(r";?\s*(?P<op>[{};])\s*", r'\g<op>', stripped)
-+ self._output += stripped
-+ self._codeblock = ''
-+
-+ def parse(self, content):
-+ state = self.INITIAL
-+ for char in content:
-+ if state == self.INITIAL:
-+ if char == '/':
-+ state = self.MAYBE_COMMENT_START
-+ elif char == "'":
-+ self.flush_codeblock()
-+ self._output += char
-+ state = self.INSIDE_SINGLE_QUOTE
-+ elif char == '"':
-+ self.flush_codeblock()
-+ self._output += char
-+ state = self.INSIDE_DOUBLE_QUOTE
-+ else:
-+ self._codeblock += char
-+ elif state == self.MAYBE_COMMENT_START:
-+ if char == '*':
-+ self.flush_codeblock()
-+ state = self.INSIDE_COMMENT
-+ else:
-+ self._codeblock += '/' + char
-+ state = self.INITIAL
-+ elif state == self.INSIDE_COMMENT:
-+ if char == '*':
-+ state = self.MAYBE_COMMENT_END
-+ else:
-+ pass
-+ elif state == self.MAYBE_COMMENT_END:
-+ if char == '/':
-+ state = self.INITIAL
-+ else:
-+ state = self.INSIDE_COMMENT
-+ elif state == self.INSIDE_SINGLE_QUOTE:
-+ if char == '\\':
-+ self._output += char
-+ state = self.INSIDE_SINGLE_QUOTE_ESCAPE
-+ elif char == "'":
-+ self._output += char
-+ state = self.INITIAL
-+ else:
-+ self._output += char
-+ elif state == self.INSIDE_SINGLE_QUOTE_ESCAPE:
-+ self._output += char
-+ state = self.INSIDE_SINGLE_QUOTE
-+ elif state == self.INSIDE_DOUBLE_QUOTE:
-+ if char == '\\':
-+ self._output += char
-+ state = self.INSIDE_DOUBLE_QUOTE_ESCAPE
-+ elif char == '"':
-+ self._output += char
-+ state = self.INITIAL
-+ else:
-+ self._output += char
-+ elif state == self.INSIDE_DOUBLE_QUOTE_ESCAPE:
-+ self._output += char
-+ state = self.INSIDE_DOUBLE_QUOTE
-+
-+ self.flush_codeblock()
-+ self._output = self._output.strip()
-+ return self._output
-+
-+ @classmethod
-+ def minimize_css(cls, content):
-+ minimizer = CSSMinimizer()
-+ return minimizer.parse(content)
-+
-+def main():
-+ result = ''
-+ try:
-+ result = CSSMinimizer.minimize_css(sys.stdin.read())
-+ finally:
-+ print(result)
-+
-+if __name__ == '__main__':
-+ main()
-diff --git a/tools/grit/minimize_css_unittest.py b/tools/grit/minimize_css_unittest.py
-new file mode 100644
-index 0000000000..cddc313078
---- /dev/null
-+++ b/tools/grit/minimize_css_unittest.py
-@@ -0,0 +1,58 @@
-+#!/usr/bin/env python
-+# Copyright 2016 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+import unittest
-+
-+import minimize_css
-+
-+
-+class CSSMinimizerTest(unittest.TestCase):
-+
-+ def test_simple(self):
-+ source = """
-+ div {
-+ color: blue;
-+ }
-+ """
-+ minimized = minimize_css.CSSMinimizer.minimize_css(source)
-+ self.assertEquals(minimized, "div{color: blue}")
-+
-+ def test_attribute_selectors(self):
-+ source = """
-+ input[type="search" i]::-webkit-textfield-decoration-container {
-+ direction: ltr;
-+ }
-+ """
-+ minimized = minimize_css.CSSMinimizer.minimize_css(source)
-+ self.assertEquals(
-+ minimized,
-+ # pylint: disable=line-too-long
-+ """input[type="search" i]::-webkit-textfield-decoration-container{direction: ltr}""")
-+
-+ def test_strip_comment(self):
-+ source = """
-+ /* header */
-+ html {
-+ /* inside block */
-+ display: block;
-+ }
-+ /* footer */
-+ """
-+ minimized = minimize_css.CSSMinimizer.minimize_css(source)
-+ self.assertEquals(minimized, "html{ display: block}")
-+
-+ def test_no_strip_inside_quotes(self):
-+ source = """div[foo=' bar ']"""
-+ minimized = minimize_css.CSSMinimizer.minimize_css(source)
-+ self.assertEquals(minimized, source)
-+
-+ source = """div[foo=" bar "]"""
-+ minimized = minimize_css.CSSMinimizer.minimize_css(source)
-+ self.assertEquals(minimized, source)
-+
-+ def test_escape_string(self):
-+ source = """content: " <a onclick=\\\"javascript: alert ( 'foobar' ); \\\">";"""
-+ minimized = minimize_css.CSSMinimizer.minimize_css(source)
-+ self.assertEquals(minimized, source)
-diff --git a/tools/grit/pak_util.py b/tools/grit/pak_util.py
-new file mode 100644
-index 0000000000..ede638bbe1
---- /dev/null
-+++ b/tools/grit/pak_util.py
-@@ -0,0 +1,223 @@
-+#!/usr/bin/env python
-+# Copyright 2017 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+"""A tool for interacting with .pak files.
-+
-+For details on the pak file format, see:
-+https://dev.chromium.org/developers/design-documents/linuxresourcesandlocalizedstrings
-+"""
-+
-+from __future__ import print_function
-+
-+import argparse
-+import gzip
-+import hashlib
-+import os
-+import shutil
-+import sys
-+import tempfile
-+
-+# Import grit first to get local third_party modules.
-+import grit # pylint: disable=ungrouped-imports,unused-import
-+
-+import six
-+
-+from grit.format import data_pack
-+
-+
-+def _RepackMain(args):
-+ output_info_filepath = args.output_pak_file + '.info'
-+ if args.compress:
-+ # If the file needs to be compressed, call RePack with a tempfile path,
-+ # then compress the tempfile to args.output_pak_file.
-+ temp_outfile = tempfile.NamedTemporaryFile()
-+ out_path = temp_outfile.name
-+ # Strip any non .pak extension from the .info output file path.
-+ splitext = os.path.splitext(args.output_pak_file)
-+ if splitext[1] != '.pak':
-+ output_info_filepath = splitext[0] + '.info'
-+ else:
-+ out_path = args.output_pak_file
-+ data_pack.RePack(out_path, args.input_pak_files, args.whitelist,
-+ args.suppress_removed_key_output,
-+ output_info_filepath=output_info_filepath)
-+ if args.compress:
-+ with open(args.output_pak_file, 'wb') as out:
-+ with gzip.GzipFile(filename='', mode='wb', fileobj=out, mtime=0) as outgz:
-+ shutil.copyfileobj(temp_outfile, outgz)
-+
-+
-+def _ExtractMain(args):
-+ pak = data_pack.ReadDataPack(args.pak_file)
-+ if args.textual_id:
-+ info_dict = data_pack.ReadGrdInfo(args.pak_file)
-+ for resource_id, payload in pak.resources.items():
-+ filename = (
-+ info_dict[resource_id].textual_id
-+ if args.textual_id else str(resource_id))
-+ path = os.path.join(args.output_dir, filename)
-+ with open(path, 'w') as f:
-+ f.write(payload)
-+
-+
-+def _CreateMain(args):
-+ pak = {}
-+ for name in os.listdir(args.input_dir):
-+ try:
-+ resource_id = int(name)
-+ except:
-+ continue
-+ filename = os.path.join(args.input_dir, name)
-+ if os.path.isfile(filename):
-+ with open(filename, 'rb') as f:
-+ pak[resource_id] = f.read()
-+ data_pack.WriteDataPack(pak, args.output_pak_file, data_pack.UTF8)
-+
-+
-+def _PrintMain(args):
-+ pak = data_pack.ReadDataPack(args.pak_file)
-+ if args.textual_id:
-+ info_dict = data_pack.ReadGrdInfo(args.pak_file)
-+ output = args.output
-+ encoding = 'binary'
-+ if pak.encoding == 1:
-+ encoding = 'utf-8'
-+ elif pak.encoding == 2:
-+ encoding = 'utf-16'
-+ else:
-+ encoding = '?' + str(pak.encoding)
-+
-+ output.write('version: {}\n'.format(pak.version))
-+ output.write('encoding: {}\n'.format(encoding))
-+ output.write('num_resources: {}\n'.format(len(pak.resources)))
-+ output.write('num_aliases: {}\n'.format(len(pak.aliases)))
-+ breakdown = ', '.join('{}: {}'.format(*x) for x in pak.sizes)
-+ output.write('total_size: {} ({})\n'.format(pak.sizes.total, breakdown))
-+
-+ try_decode = args.decode and encoding.startswith('utf')
-+ # Print IDs in ascending order, since that's the order in which they appear in
-+ # the file (order is lost by Python dict).
-+ for resource_id in sorted(pak.resources):
-+ data = pak.resources[resource_id]
-+ canonical_id = pak.aliases.get(resource_id, resource_id)
-+ desc = '<data>'
-+ if try_decode:
-+ try:
-+ desc = six.text_type(data, encoding)
-+ if len(desc) > 60:
-+ desc = desc[:60] + u'...'
-+ desc = desc.replace('\n', '\\n')
-+ except UnicodeDecodeError:
-+ pass
-+ sha1 = hashlib.sha1(data).hexdigest()[:10]
-+ if args.textual_id:
-+ textual_id = info_dict[resource_id].textual_id
-+ canonical_textual_id = info_dict[canonical_id].textual_id
-+ output.write(
-+ u'Entry(id={}, canonical_id={}, size={}, sha1={}): {}\n'.format(
-+ textual_id, canonical_textual_id, len(data), sha1,
-+ desc).encode('utf-8'))
-+ else:
-+ output.write(
-+ u'Entry(id={}, canonical_id={}, size={}, sha1={}): {}\n'.format(
-+ resource_id, canonical_id, len(data), sha1, desc).encode('utf-8'))
-+
-+
-+def _ListMain(args):
-+ pak = data_pack.ReadDataPack(args.pak_file)
-+ if args.textual_id or args.path:
-+ info_dict = data_pack.ReadGrdInfo(args.pak_file)
-+ fmt = ''.join([
-+ '{id}', ' = {textual_id}' if args.textual_id else '',
-+ ' @ {path}' if args.path else '', '\n'
-+ ])
-+ for resource_id in sorted(pak.resources):
-+ item = info_dict[resource_id]
-+ args.output.write(
-+ fmt.format(textual_id=item.textual_id, id=item.id, path=item.path))
-+ else:
-+ for resource_id in sorted(pak.resources):
-+ args.output.write('%d\n' % resource_id)
-+
-+
-+def main():
-+ parser = argparse.ArgumentParser(
-+ description=__doc__, formatter_class=argparse.RawTextHelpFormatter)
-+ # Subparsers are required by default under Python 2. Python 3 changed to
-+ # not required, but didn't include a required option until 3.7. Setting
-+ # the required member works in all versions (and setting dest name).
-+ sub_parsers = parser.add_subparsers(dest='action')
-+ sub_parsers.required = True
-+
-+ sub_parser = sub_parsers.add_parser('repack',
-+ help='Combines several .pak files into one.')
-+ sub_parser.add_argument('output_pak_file', help='File to create.')
-+ sub_parser.add_argument('input_pak_files', nargs='+',
-+ help='Input .pak files.')
-+ sub_parser.add_argument('--whitelist',
-+ help='Path to a whitelist used to filter output pak file resource IDs.')
-+ sub_parser.add_argument('--suppress-removed-key-output', action='store_true',
-+ help='Do not log which keys were removed by the whitelist.')
-+ sub_parser.add_argument('--compress', dest='compress', action='store_true',
-+ default=False, help='Compress output_pak_file using gzip.')
-+ sub_parser.set_defaults(func=_RepackMain)
-+
-+ sub_parser = sub_parsers.add_parser('extract', help='Extracts pak file')
-+ sub_parser.add_argument('pak_file')
-+ sub_parser.add_argument('-o', '--output-dir', default='.',
-+ help='Directory to extract to.')
-+ sub_parser.add_argument(
-+ '-t',
-+ '--textual-id',
-+ action='store_true',
-+ help='Use textual resource ID (name) (from .info file) as filenames.')
-+ sub_parser.set_defaults(func=_ExtractMain)
-+
-+ sub_parser = sub_parsers.add_parser('create',
-+ help='Creates pak file from extracted directory.')
-+ sub_parser.add_argument('output_pak_file', help='File to create.')
-+ sub_parser.add_argument('-i', '--input-dir', default='.',
-+ help='Directory to create from.')
-+ sub_parser.set_defaults(func=_CreateMain)
-+
-+ sub_parser = sub_parsers.add_parser('print',
-+ help='Prints all pak IDs and contents. Useful for diffing.')
-+ sub_parser.add_argument('pak_file')
-+ sub_parser.add_argument('--output', type=argparse.FileType('w'),
-+ default=sys.stdout,
-+ help='The resource list path to write (default stdout)')
-+ sub_parser.add_argument('--no-decode', dest='decode', action='store_false',
-+ default=True, help='Do not print entry data.')
-+ sub_parser.add_argument(
-+ '-t',
-+ '--textual-id',
-+ action='store_true',
-+ help='Print textual ID (name) (from .info file) instead of the ID.')
-+ sub_parser.set_defaults(func=_PrintMain)
-+
-+ sub_parser = sub_parsers.add_parser('list-id',
-+ help='Outputs all resource IDs to a file.')
-+ sub_parser.add_argument('pak_file')
-+ sub_parser.add_argument('--output', type=argparse.FileType('w'),
-+ default=sys.stdout,
-+ help='The resource list path to write (default stdout)')
-+ sub_parser.add_argument(
-+ '-t',
-+ '--textual-id',
-+ action='store_true',
-+ help='Print the textual resource ID (from .info file).')
-+ sub_parser.add_argument(
-+ '-p',
-+ '--path',
-+ action='store_true',
-+ help='Print the resource path (from .info file).')
-+ sub_parser.set_defaults(func=_ListMain)
-+
-+ args = parser.parse_args()
-+ args.func(args)
-+
-+
-+if __name__ == '__main__':
-+ main()
-diff --git a/tools/grit/repack.gni b/tools/grit/repack.gni
-new file mode 100644
-index 0000000000..193f2dc43f
---- /dev/null
-+++ b/tools/grit/repack.gni
-@@ -0,0 +1,189 @@
-+# Copyright 2014 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+import("//tools/grit/grit_rule.gni")
-+
-+# This file defines a template to invoke grit repack in a consistent manner.
-+#
-+# Parameters:
-+# sources [required]
-+# List of pak files that need to be combined.
-+#
-+# output [required]
-+# File name (single string) of the output file.
-+#
-+# copy_data_to_bundle [optional]
-+# Whether to define a bundle_data() for the resulting pak.
-+#
-+# bundle_output [optional]
-+# Path of the file in the application bundle, defaults to
-+# {{bundle_resources_dir}}/{{source_file_part}}.
-+#
-+# compress [optional]
-+# Gzip the resulting bundle (and append .gz to the output name).
-+#
-+# deps [optional]
-+# public_deps [optional]
-+# visibility [optional]
-+# Normal meaning.
-+template("repack") {
-+ _copy_data_to_bundle =
-+ defined(invoker.copy_data_to_bundle) && invoker.copy_data_to_bundle
-+ _repack_target_name = target_name
-+ if (_copy_data_to_bundle) {
-+ _repack_target_name = "${target_name}__repack"
-+ }
-+
-+ _compress = defined(invoker.compress) && invoker.compress
-+
-+ action(_repack_target_name) {
-+ forward_variables_from(invoker,
-+ [
-+ "deps",
-+ "public_deps",
-+ "testonly",
-+ "visibility",
-+ ])
-+ if (defined(visibility) && _copy_data_to_bundle) {
-+ visibility += [ ":${invoker.target_name}" ]
-+ }
-+ assert(defined(invoker.sources), "Need sources for $target_name")
-+ assert(defined(invoker.output), "Need output for $target_name")
-+
-+ script = "//tools/grit/pak_util.py"
-+
-+ inputs = invoker.sources
-+ outputs = [
-+ invoker.output,
-+ "${invoker.output}.info",
-+ ]
-+
-+ args = [ "repack" ]
-+ if (defined(invoker.repack_whitelist)) {
-+ inputs += [ invoker.repack_whitelist ]
-+ _rebased_whitelist = rebase_path(invoker.repack_whitelist)
-+ args += [ "--whitelist=$_rebased_whitelist" ]
-+ args += [ "--suppress-removed-key-output" ]
-+ }
-+ args += [ rebase_path(invoker.output, root_build_dir) ]
-+ args += rebase_path(invoker.sources, root_build_dir)
-+ if (_compress) {
-+ args += [ "--compress" ]
-+ }
-+ }
-+
-+ if (_copy_data_to_bundle) {
-+ bundle_data(target_name) {
-+ forward_variables_from(invoker,
-+ [
-+ "testonly",
-+ "visibility",
-+ ])
-+
-+ public_deps = [ ":$_repack_target_name" ]
-+ sources = [ invoker.output ]
-+ if (defined(invoker.bundle_output)) {
-+ outputs = [ invoker.bundle_output ]
-+ } else {
-+ outputs = [ "{{bundle_resources_dir}}/{{source_file_part}}" ]
-+ }
-+ }
-+ }
-+}
-+
-+# Repacks a set of .pak files for each locale.
-+#
-+# Parameters:
-+#
-+# input_locales [required]
-+# List of locale names to use as inputs.
-+#
-+# output_locales [required]
-+# A list containing the corresponding output names for each of the
-+# input names. Mac and iOS use different names in some cases.
-+#
-+# source_patterns [required]
-+# The pattern for pak files which need repacked. The filenames always end
-+# with "${locale}.pak".
-+# E.g.:
-+# ${root_gen_dir}/foo_ expands to ${root_gen_dir}/foo_zh-CN.pak
-+# when locale is zh-CN.
-+#
-+# output_dir [optional]
-+# Directory in which to put all pak files.
-+#
-+# deps [optional]
-+# visibility [optional]
-+# testonly [optional]
-+# copy_data_to_bundle [optional]
-+# repack_whitelist [optional]
-+# Normal meaning.
-+template("repack_locales") {
-+ if (defined(invoker.output_dir)) {
-+ _output_dir = invoker.output_dir
-+ } else if (is_ios) {
-+ _output_dir = "$target_gen_dir"
-+ } else {
-+ _output_dir = "$target_gen_dir/$target_name"
-+ }
-+
-+ # GN can't handle invoker.output_locales[foo] (http://crbug.com/614747).
-+ _output_locales = invoker.output_locales
-+
-+ # Collects all targets the loop generates.
-+ _locale_targets = []
-+
-+ # This loop iterates over the input locales and also keeps a counter so it
-+ # can simultaneously iterate over the output locales (using GN's very
-+ # limited looping capabilities).
-+ _current_index = 0
-+ foreach(_input_locale, invoker.input_locales) {
-+ _output_locale = _output_locales[_current_index]
-+
-+ # Compute the name of the target for the current file. Save it for the deps.
-+ _current_name = "${target_name}_${_input_locale}"
-+ _locale_targets += [ ":$_current_name" ]
-+
-+ repack(_current_name) {
-+ forward_variables_from(invoker,
-+ [
-+ "copy_data_to_bundle",
-+ "bundle_output",
-+ "compress",
-+ "deps",
-+ "repack_whitelist",
-+ "testonly",
-+ ])
-+ visibility = [ ":${invoker.target_name}" ]
-+ if (is_ios) {
-+ output = "$_output_dir/${_output_locale}.lproj/locale.pak"
-+ } else {
-+ output = "$_output_dir/${_output_locale}.pak"
-+ }
-+ if (defined(copy_data_to_bundle) && copy_data_to_bundle) {
-+ bundle_output =
-+ "{{bundle_resources_dir}}/${_output_locale}.lproj/locale.pak"
-+ }
-+ sources = []
-+ foreach(_pattern, invoker.source_patterns) {
-+ sources += [ "${_pattern}${_input_locale}.pak" ]
-+ }
-+ }
-+
-+ _current_index = _current_index + 1
-+ }
-+
-+ # The group that external targets depend on which collects all deps.
-+ group(target_name) {
-+ forward_variables_from(invoker,
-+ [
-+ "visibility",
-+ "testonly",
-+ ])
-+ public_deps = _locale_targets
-+ if (!defined(invoker.copy_data_to_bundle) || !invoker.copy_data_to_bundle) {
-+ data_deps = public_deps
-+ }
-+ }
-+}
-diff --git a/tools/grit/setup.py b/tools/grit/setup.py
-new file mode 100644
-index 0000000000..5d86dfc2fc
---- /dev/null
-+++ b/tools/grit/setup.py
-@@ -0,0 +1,46 @@
-+#!/usr/bin/env python3
-+# 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.
-+
-+"""Install the package!"""
-+
-+from __future__ import absolute_import
-+
-+import setuptools
-+
-+
-+setuptools.setup(
-+ name='grit',
-+ version='0',
-+ entry_points={
-+ 'console_scripts': ['grit = grit.grit_runner:Main'],
-+ },
-+ packages=setuptools.find_packages(),
-+ install_requires=[
-+ 'six >= 1.10',
-+ ],
-+ author='The Chromium Authors',
-+ author_email='chromium-dev@chromium.org',
-+ description='Google Resource and Internationalization Tool for managing '
-+ 'translations & resource files',
-+ license='BSD-3',
-+ url='https://chromium.googlesource.com/chromium/src/tools/grit/',
-+ classifiers=[
-+ 'Development Status :: 6 - Mature',
-+ 'Environment :: Console',
-+ 'Intended Audience :: Developers',
-+ 'License :: OSI Approved :: BSD License',
-+ 'Operating System :: MacOS',
-+ 'Operating System :: Microsoft :: Windows',
-+ 'Operating System :: POSIX :: Linux',
-+ 'Programming Language :: Python',
-+ 'Programming Language :: Python :: 2.7',
-+ 'Programming Language :: Python :: 3',
-+ 'Programming Language :: Python :: 3.6',
-+ 'Programming Language :: Python :: 3.7',
-+ 'Programming Language :: Python :: 3.8',
-+ 'Programming Language :: Python :: 3.9',
-+ 'Topic :: Utilities',
-+ ],
-+)
-diff --git a/tools/grit/stamp_grit_sources.py b/tools/grit/stamp_grit_sources.py
-new file mode 100644
-index 0000000000..bc7265c6cb
---- /dev/null
-+++ b/tools/grit/stamp_grit_sources.py
-@@ -0,0 +1,57 @@
-+# Copyright 2014 The Chromium Authors. All rights reserved.
-+# Use of this source code is governed by a BSD-style license that can be
-+# found in the LICENSE file.
-+
-+# This script enumerates the files in the given directory, writing an empty
-+# stamp file and a .d file listing the inputs required to make the stamp. This
-+# allows us to dynamically depend on the grit sources without enumerating the
-+# grit directory for every invocation of grit (which is what adding the source
-+# files to every .grd file's .d file would entail) or shelling out to grit
-+# synchronously during GN execution to get the list (which would be slow).
-+#
-+# Usage:
-+# stamp_grit_sources.py <directory> <stamp-file> <.d-file>
-+
-+from __future__ import print_function
-+
-+import os
-+import sys
-+
-+def GritSourceFiles(grit_root_dir):
-+ files = []
-+ for root, _, filenames in os.walk(grit_root_dir):
-+ grit_src = [os.path.join(root, f) for f in filenames
-+ if f.endswith('.py') and not f.endswith('_unittest.py')]
-+ files.extend(grit_src)
-+ files = [f.replace('\\', '/') for f in files]
-+ return sorted(files)
-+
-+
-+def WriteDepFile(dep_file, stamp_file, source_files):
-+ with open(dep_file, "w") as f:
-+ f.write(stamp_file)
-+ f.write(": ")
-+ f.write(' '.join(source_files))
-+
-+
-+def WriteStampFile(stamp_file):
-+ with open(stamp_file, "w"):
-+ pass
-+
-+
-+def main(argv):
-+ if len(argv) != 4:
-+ print("Error: expecting 3 args.")
-+ return 1
-+
-+ grit_root_dir = sys.argv[1]
-+ stamp_file = sys.argv[2]
-+ dep_file = sys.argv[3]
-+
-+ WriteStampFile(stamp_file)
-+ WriteDepFile(dep_file, stamp_file, GritSourceFiles(grit_root_dir))
-+ return 0
-+
-+
-+if __name__ == '__main__':
-+ sys.exit(main(sys.argv))
-diff --git a/tools/grit/third_party/six/LICENSE b/tools/grit/third_party/six/LICENSE
-new file mode 100644
-index 0000000000..e558f9d494
---- /dev/null
-+++ b/tools/grit/third_party/six/LICENSE
-@@ -0,0 +1,18 @@
-+Copyright (c) 2010-2015 Benjamin Peterson
-+
-+Permission is hereby granted, free of charge, to any person obtaining a copy of
-+this software and associated documentation files (the "Software"), to deal in
-+the Software without restriction, including without limitation the rights to
-+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
-+the Software, and to permit persons to whom the Software is furnished to do so,
-+subject to the following conditions:
-+
-+The above copyright notice and this permission notice shall be included in all
-+copies or substantial portions of the Software.
-+
-+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
-+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
-+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
-+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-diff --git a/tools/grit/third_party/six/README b/tools/grit/third_party/six/README
-new file mode 100644
-index 0000000000..ee628a9db6
---- /dev/null
-+++ b/tools/grit/third_party/six/README
-@@ -0,0 +1,16 @@
-+Six is a Python 2 and 3 compatibility library. It provides utility functions
-+for smoothing over the differences between the Python versions with the goal of
-+writing Python code that is compatible on both Python versions. See the
-+documentation for more information on what is provided.
-+
-+Six supports every Python version since 2.6. It is contained in only one Python
-+file, so it can be easily copied into your project. (The copyright and license
-+notice must be retained.)
-+
-+Online documentation is at https://pythonhosted.org/six/.
-+
-+Bugs can be reported to https://bitbucket.org/gutworth/six. The code can also
-+be found there.
-+
-+For questions about six or porting in general, email the python-porting mailing
-+list: https://mail.python.org/mailman/listinfo/python-porting
-diff --git a/tools/grit/third_party/six/README.chromium b/tools/grit/third_party/six/README.chromium
-new file mode 100644
-index 0000000000..100b24d046
---- /dev/null
-+++ b/tools/grit/third_party/six/README.chromium
-@@ -0,0 +1,13 @@
-+Name: six
-+Short Name: six
-+URL: https://bitbucket.org/gutworth/six/commits/tag/1.10.0
-+Version: 1.10.0
-+Revision: 403:e5218c3f66a2
-+License: Apache License, Version 2.0
-+
-+Description:
-+Six is a Python 2 and 3 compatibility library.
-+
-+Local Modifications:
-+- Copied six.py as __init__.py.
-+- Kept LICENSE and README.
-diff --git a/tools/grit/third_party/six/__init__.py b/tools/grit/third_party/six/__init__.py
-new file mode 100644
-index 0000000000..56e4272cb3
---- /dev/null
-+++ b/tools/grit/third_party/six/__init__.py
-@@ -0,0 +1,868 @@
-+# Copyright (c) 2010-2015 Benjamin Peterson
-+#
-+# Permission is hereby granted, free of charge, to any person obtaining a copy
-+# of this software and associated documentation files (the "Software"), to deal
-+# in the Software without restriction, including without limitation the rights
-+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-+# copies of the Software, and to permit persons to whom the Software is
-+# furnished to do so, subject to the following conditions:
-+#
-+# The above copyright notice and this permission notice shall be included in all
-+# copies or substantial portions of the Software.
-+#
-+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-+# SOFTWARE.
-+
-+"""Utilities for writing code that runs on Python 2 and 3"""
-+
-+from __future__ import absolute_import
-+
-+import functools
-+import itertools
-+import operator
-+import sys
-+import types
-+
-+__author__ = "Benjamin Peterson <benjamin@python.org>"
-+__version__ = "1.10.0"
-+
-+
-+# Useful for very coarse version differentiation.
-+PY2 = sys.version_info[0] == 2
-+PY3 = sys.version_info[0] == 3
-+PY34 = sys.version_info[0:2] >= (3, 4)
-+
-+if PY3:
-+ string_types = str,
-+ integer_types = int,
-+ class_types = type,
-+ text_type = str
-+ binary_type = bytes
-+
-+ MAXSIZE = sys.maxsize
-+else:
-+ string_types = basestring,
-+ integer_types = (int, long)
-+ class_types = (type, types.ClassType)
-+ text_type = unicode
-+ binary_type = str
-+
-+ if sys.platform.startswith("java"):
-+ # Jython always uses 32 bits.
-+ MAXSIZE = int((1 << 31) - 1)
-+ else:
-+ # It's possible to have sizeof(long) != sizeof(Py_ssize_t).
-+ class X(object):
-+
-+ def __len__(self):
-+ return 1 << 31
-+ try:
-+ len(X())
-+ except OverflowError:
-+ # 32-bit
-+ MAXSIZE = int((1 << 31) - 1)
-+ else:
-+ # 64-bit
-+ MAXSIZE = int((1 << 63) - 1)
-+ del X
-+
-+
-+def _add_doc(func, doc):
-+ """Add documentation to a function."""
-+ func.__doc__ = doc
-+
-+
-+def _import_module(name):
-+ """Import module, returning the module after the last dot."""
-+ __import__(name)
-+ return sys.modules[name]
-+
-+
-+class _LazyDescr(object):
-+
-+ def __init__(self, name):
-+ self.name = name
-+
-+ def __get__(self, obj, tp):
-+ result = self._resolve()
-+ setattr(obj, self.name, result) # Invokes __set__.
-+ try:
-+ # This is a bit ugly, but it avoids running this again by
-+ # removing this descriptor.
-+ delattr(obj.__class__, self.name)
-+ except AttributeError:
-+ pass
-+ return result
-+
-+
-+class MovedModule(_LazyDescr):
-+
-+ def __init__(self, name, old, new=None):
-+ super(MovedModule, self).__init__(name)
-+ if PY3:
-+ if new is None:
-+ new = name
-+ self.mod = new
-+ else:
-+ self.mod = old
-+
-+ def _resolve(self):
-+ return _import_module(self.mod)
-+
-+ def __getattr__(self, attr):
-+ _module = self._resolve()
-+ value = getattr(_module, attr)
-+ setattr(self, attr, value)
-+ return value
-+
-+
-+class _LazyModule(types.ModuleType):
-+
-+ def __init__(self, name):
-+ super(_LazyModule, self).__init__(name)
-+ self.__doc__ = self.__class__.__doc__
-+
-+ def __dir__(self):
-+ attrs = ["__doc__", "__name__"]
-+ attrs += [attr.name for attr in self._moved_attributes]
-+ return attrs
-+
-+ # Subclasses should override this
-+ _moved_attributes = []
-+
-+
-+class MovedAttribute(_LazyDescr):
-+
-+ def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None):
-+ super(MovedAttribute, self).__init__(name)
-+ if PY3:
-+ if new_mod is None:
-+ new_mod = name
-+ self.mod = new_mod
-+ if new_attr is None:
-+ if old_attr is None:
-+ new_attr = name
-+ else:
-+ new_attr = old_attr
-+ self.attr = new_attr
-+ else:
-+ self.mod = old_mod
-+ if old_attr is None:
-+ old_attr = name
-+ self.attr = old_attr
-+
-+ def _resolve(self):
-+ module = _import_module(self.mod)
-+ return getattr(module, self.attr)
-+
-+
-+class _SixMetaPathImporter(object):
-+
-+ """
-+ A meta path importer to import six.moves and its submodules.
-+
-+ This class implements a PEP302 finder and loader. It should be compatible
-+ with Python 2.5 and all existing versions of Python3
-+ """
-+
-+ def __init__(self, six_module_name):
-+ self.name = six_module_name
-+ self.known_modules = {}
-+
-+ def _add_module(self, mod, *fullnames):
-+ for fullname in fullnames:
-+ self.known_modules[self.name + "." + fullname] = mod
-+
-+ def _get_module(self, fullname):
-+ return self.known_modules[self.name + "." + fullname]
-+
-+ def find_module(self, fullname, path=None):
-+ if fullname in self.known_modules:
-+ return self
-+ return None
-+
-+ def __get_module(self, fullname):
-+ try:
-+ return self.known_modules[fullname]
-+ except KeyError:
-+ raise ImportError("This loader does not know module " + fullname)
-+
-+ def load_module(self, fullname):
-+ try:
-+ # in case of a reload
-+ return sys.modules[fullname]
-+ except KeyError:
-+ pass
-+ mod = self.__get_module(fullname)
-+ if isinstance(mod, MovedModule):
-+ mod = mod._resolve()
-+ else:
-+ mod.__loader__ = self
-+ sys.modules[fullname] = mod
-+ return mod
-+
-+ def is_package(self, fullname):
-+ """
-+ Return true, if the named module is a package.
-+
-+ We need this method to get correct spec objects with
-+ Python 3.4 (see PEP451)
-+ """
-+ return hasattr(self.__get_module(fullname), "__path__")
-+
-+ def get_code(self, fullname):
-+ """Return None
-+
-+ Required, if is_package is implemented"""
-+ self.__get_module(fullname) # eventually raises ImportError
-+ return None
-+ get_source = get_code # same as get_code
-+
-+_importer = _SixMetaPathImporter(__name__)
-+
-+
-+class _MovedItems(_LazyModule):
-+
-+ """Lazy loading of moved objects"""
-+ __path__ = [] # mark as package
-+
-+
-+_moved_attributes = [
-+ MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"),
-+ MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"),
-+ MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"),
-+ MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"),
-+ MovedAttribute("intern", "__builtin__", "sys"),
-+ MovedAttribute("map", "itertools", "builtins", "imap", "map"),
-+ MovedAttribute("getcwd", "os", "os", "getcwdu", "getcwd"),
-+ MovedAttribute("getcwdb", "os", "os", "getcwd", "getcwdb"),
-+ MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"),
-+ MovedAttribute("reload_module", "__builtin__", "importlib" if PY34 else "imp", "reload"),
-+ MovedAttribute("reduce", "__builtin__", "functools"),
-+ MovedAttribute("shlex_quote", "pipes", "shlex", "quote"),
-+ MovedAttribute("StringIO", "StringIO", "io"),
-+ MovedAttribute("UserDict", "UserDict", "collections"),
-+ MovedAttribute("UserList", "UserList", "collections"),
-+ MovedAttribute("UserString", "UserString", "collections"),
-+ MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"),
-+ MovedAttribute("zip", "itertools", "builtins", "izip", "zip"),
-+ MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"),
-+ MovedModule("builtins", "__builtin__"),
-+ MovedModule("configparser", "ConfigParser"),
-+ MovedModule("copyreg", "copy_reg"),
-+ MovedModule("dbm_gnu", "gdbm", "dbm.gnu"),
-+ MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread"),
-+ MovedModule("http_cookiejar", "cookielib", "http.cookiejar"),
-+ MovedModule("http_cookies", "Cookie", "http.cookies"),
-+ MovedModule("html_entities", "htmlentitydefs", "html.entities"),
-+ MovedModule("html_parser", "HTMLParser", "html.parser"),
-+ MovedModule("http_client", "httplib", "http.client"),
-+ MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"),
-+ MovedModule("email_mime_nonmultipart", "email.MIMENonMultipart", "email.mime.nonmultipart"),
-+ MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"),
-+ MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"),
-+ MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"),
-+ MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"),
-+ MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"),
-+ MovedModule("cPickle", "cPickle", "pickle"),
-+ MovedModule("queue", "Queue"),
-+ MovedModule("reprlib", "repr"),
-+ MovedModule("socketserver", "SocketServer"),
-+ MovedModule("_thread", "thread", "_thread"),
-+ MovedModule("tkinter", "Tkinter"),
-+ MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"),
-+ MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"),
-+ MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"),
-+ MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"),
-+ MovedModule("tkinter_tix", "Tix", "tkinter.tix"),
-+ MovedModule("tkinter_ttk", "ttk", "tkinter.ttk"),
-+ MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"),
-+ MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"),
-+ MovedModule("tkinter_colorchooser", "tkColorChooser",
-+ "tkinter.colorchooser"),
-+ MovedModule("tkinter_commondialog", "tkCommonDialog",
-+ "tkinter.commondialog"),
-+ MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"),
-+ MovedModule("tkinter_font", "tkFont", "tkinter.font"),
-+ MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"),
-+ MovedModule("tkinter_tksimpledialog", "tkSimpleDialog",
-+ "tkinter.simpledialog"),
-+ MovedModule("urllib_parse", __name__ + ".moves.urllib_parse", "urllib.parse"),
-+ MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"),
-+ MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"),
-+ MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"),
-+ MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"),
-+ MovedModule("xmlrpc_server", "SimpleXMLRPCServer", "xmlrpc.server"),
-+]
-+# Add windows specific modules.
-+if sys.platform == "win32":
-+ _moved_attributes += [
-+ MovedModule("winreg", "_winreg"),
-+ ]
-+
-+for attr in _moved_attributes:
-+ setattr(_MovedItems, attr.name, attr)
-+ if isinstance(attr, MovedModule):
-+ _importer._add_module(attr, "moves." + attr.name)
-+del attr
-+
-+_MovedItems._moved_attributes = _moved_attributes
-+
-+moves = _MovedItems(__name__ + ".moves")
-+_importer._add_module(moves, "moves")
-+
-+
-+class Module_six_moves_urllib_parse(_LazyModule):
-+
-+ """Lazy loading of moved objects in six.moves.urllib_parse"""
-+
-+
-+_urllib_parse_moved_attributes = [
-+ MovedAttribute("ParseResult", "urlparse", "urllib.parse"),
-+ MovedAttribute("SplitResult", "urlparse", "urllib.parse"),
-+ MovedAttribute("parse_qs", "urlparse", "urllib.parse"),
-+ MovedAttribute("parse_qsl", "urlparse", "urllib.parse"),
-+ MovedAttribute("urldefrag", "urlparse", "urllib.parse"),
-+ MovedAttribute("urljoin", "urlparse", "urllib.parse"),
-+ MovedAttribute("urlparse", "urlparse", "urllib.parse"),
-+ MovedAttribute("urlsplit", "urlparse", "urllib.parse"),
-+ MovedAttribute("urlunparse", "urlparse", "urllib.parse"),
-+ MovedAttribute("urlunsplit", "urlparse", "urllib.parse"),
-+ MovedAttribute("quote", "urllib", "urllib.parse"),
-+ MovedAttribute("quote_plus", "urllib", "urllib.parse"),
-+ MovedAttribute("unquote", "urllib", "urllib.parse"),
-+ MovedAttribute("unquote_plus", "urllib", "urllib.parse"),
-+ MovedAttribute("urlencode", "urllib", "urllib.parse"),
-+ MovedAttribute("splitquery", "urllib", "urllib.parse"),
-+ MovedAttribute("splittag", "urllib", "urllib.parse"),
-+ MovedAttribute("splituser", "urllib", "urllib.parse"),
-+ MovedAttribute("uses_fragment", "urlparse", "urllib.parse"),
-+ MovedAttribute("uses_netloc", "urlparse", "urllib.parse"),
-+ MovedAttribute("uses_params", "urlparse", "urllib.parse"),
-+ MovedAttribute("uses_query", "urlparse", "urllib.parse"),
-+ MovedAttribute("uses_relative", "urlparse", "urllib.parse"),
-+]
-+for attr in _urllib_parse_moved_attributes:
-+ setattr(Module_six_moves_urllib_parse, attr.name, attr)
-+del attr
-+
-+Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes
-+
-+_importer._add_module(Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse"),
-+ "moves.urllib_parse", "moves.urllib.parse")
-+
-+
-+class Module_six_moves_urllib_error(_LazyModule):
-+
-+ """Lazy loading of moved objects in six.moves.urllib_error"""
-+
-+
-+_urllib_error_moved_attributes = [
-+ MovedAttribute("URLError", "urllib2", "urllib.error"),
-+ MovedAttribute("HTTPError", "urllib2", "urllib.error"),
-+ MovedAttribute("ContentTooShortError", "urllib", "urllib.error"),
-+]
-+for attr in _urllib_error_moved_attributes:
-+ setattr(Module_six_moves_urllib_error, attr.name, attr)
-+del attr
-+
-+Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes
-+
-+_importer._add_module(Module_six_moves_urllib_error(__name__ + ".moves.urllib.error"),
-+ "moves.urllib_error", "moves.urllib.error")
-+
-+
-+class Module_six_moves_urllib_request(_LazyModule):
-+
-+ """Lazy loading of moved objects in six.moves.urllib_request"""
-+
-+
-+_urllib_request_moved_attributes = [
-+ MovedAttribute("urlopen", "urllib2", "urllib.request"),
-+ MovedAttribute("install_opener", "urllib2", "urllib.request"),
-+ MovedAttribute("build_opener", "urllib2", "urllib.request"),
-+ MovedAttribute("pathname2url", "urllib", "urllib.request"),
-+ MovedAttribute("url2pathname", "urllib", "urllib.request"),
-+ MovedAttribute("getproxies", "urllib", "urllib.request"),
-+ MovedAttribute("Request", "urllib2", "urllib.request"),
-+ MovedAttribute("OpenerDirector", "urllib2", "urllib.request"),
-+ MovedAttribute("HTTPDefaultErrorHandler", "urllib2", "urllib.request"),
-+ MovedAttribute("HTTPRedirectHandler", "urllib2", "urllib.request"),
-+ MovedAttribute("HTTPCookieProcessor", "urllib2", "urllib.request"),
-+ MovedAttribute("ProxyHandler", "urllib2", "urllib.request"),
-+ MovedAttribute("BaseHandler", "urllib2", "urllib.request"),
-+ MovedAttribute("HTTPPasswordMgr", "urllib2", "urllib.request"),
-+ MovedAttribute("HTTPPasswordMgrWithDefaultRealm", "urllib2", "urllib.request"),
-+ MovedAttribute("AbstractBasicAuthHandler", "urllib2", "urllib.request"),
-+ MovedAttribute("HTTPBasicAuthHandler", "urllib2", "urllib.request"),
-+ MovedAttribute("ProxyBasicAuthHandler", "urllib2", "urllib.request"),
-+ MovedAttribute("AbstractDigestAuthHandler", "urllib2", "urllib.request"),
-+ MovedAttribute("HTTPDigestAuthHandler", "urllib2", "urllib.request"),
-+ MovedAttribute("ProxyDigestAuthHandler", "urllib2", "urllib.request"),
-+ MovedAttribute("HTTPHandler", "urllib2", "urllib.request"),
-+ MovedAttribute("HTTPSHandler", "urllib2", "urllib.request"),
-+ MovedAttribute("FileHandler", "urllib2", "urllib.request"),
-+ MovedAttribute("FTPHandler", "urllib2", "urllib.request"),
-+ MovedAttribute("CacheFTPHandler", "urllib2", "urllib.request"),
-+ MovedAttribute("UnknownHandler", "urllib2", "urllib.request"),
-+ MovedAttribute("HTTPErrorProcessor", "urllib2", "urllib.request"),
-+ MovedAttribute("urlretrieve", "urllib", "urllib.request"),
-+ MovedAttribute("urlcleanup", "urllib", "urllib.request"),
-+ MovedAttribute("URLopener", "urllib", "urllib.request"),
-+ MovedAttribute("FancyURLopener", "urllib", "urllib.request"),
-+ MovedAttribute("proxy_bypass", "urllib", "urllib.request"),
-+]
-+for attr in _urllib_request_moved_attributes:
-+ setattr(Module_six_moves_urllib_request, attr.name, attr)
-+del attr
-+
-+Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes
-+
-+_importer._add_module(Module_six_moves_urllib_request(__name__ + ".moves.urllib.request"),
-+ "moves.urllib_request", "moves.urllib.request")
-+
-+
-+class Module_six_moves_urllib_response(_LazyModule):
-+
-+ """Lazy loading of moved objects in six.moves.urllib_response"""
-+
-+
-+_urllib_response_moved_attributes = [
-+ MovedAttribute("addbase", "urllib", "urllib.response"),
-+ MovedAttribute("addclosehook", "urllib", "urllib.response"),
-+ MovedAttribute("addinfo", "urllib", "urllib.response"),
-+ MovedAttribute("addinfourl", "urllib", "urllib.response"),
-+]
-+for attr in _urllib_response_moved_attributes:
-+ setattr(Module_six_moves_urllib_response, attr.name, attr)
-+del attr
-+
-+Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes
-+
-+_importer._add_module(Module_six_moves_urllib_response(__name__ + ".moves.urllib.response"),
-+ "moves.urllib_response", "moves.urllib.response")
-+
-+
-+class Module_six_moves_urllib_robotparser(_LazyModule):
-+
-+ """Lazy loading of moved objects in six.moves.urllib_robotparser"""
-+
-+
-+_urllib_robotparser_moved_attributes = [
-+ MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser"),
-+]
-+for attr in _urllib_robotparser_moved_attributes:
-+ setattr(Module_six_moves_urllib_robotparser, attr.name, attr)
-+del attr
-+
-+Module_six_moves_urllib_robotparser._moved_attributes = _urllib_robotparser_moved_attributes
-+
-+_importer._add_module(Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser"),
-+ "moves.urllib_robotparser", "moves.urllib.robotparser")
-+
-+
-+class Module_six_moves_urllib(types.ModuleType):
-+
-+ """Create a six.moves.urllib namespace that resembles the Python 3 namespace"""
-+ __path__ = [] # mark as package
-+ parse = _importer._get_module("moves.urllib_parse")
-+ error = _importer._get_module("moves.urllib_error")
-+ request = _importer._get_module("moves.urllib_request")
-+ response = _importer._get_module("moves.urllib_response")
-+ robotparser = _importer._get_module("moves.urllib_robotparser")
-+
-+ def __dir__(self):
-+ return ['parse', 'error', 'request', 'response', 'robotparser']
-+
-+_importer._add_module(Module_six_moves_urllib(__name__ + ".moves.urllib"),
-+ "moves.urllib")
-+
-+
-+def add_move(move):
-+ """Add an item to six.moves."""
-+ setattr(_MovedItems, move.name, move)
-+
-+
-+def remove_move(name):
-+ """Remove item from six.moves."""
-+ try:
-+ delattr(_MovedItems, name)
-+ except AttributeError:
-+ try:
-+ del moves.__dict__[name]
-+ except KeyError:
-+ raise AttributeError("no such move, %r" % (name,))
-+
-+
-+if PY3:
-+ _meth_func = "__func__"
-+ _meth_self = "__self__"
-+
-+ _func_closure = "__closure__"
-+ _func_code = "__code__"
-+ _func_defaults = "__defaults__"
-+ _func_globals = "__globals__"
-+else:
-+ _meth_func = "im_func"
-+ _meth_self = "im_self"
-+
-+ _func_closure = "func_closure"
-+ _func_code = "func_code"
-+ _func_defaults = "func_defaults"
-+ _func_globals = "func_globals"
-+
-+
-+try:
-+ advance_iterator = next
-+except NameError:
-+ def advance_iterator(it):
-+ return it.next()
-+next = advance_iterator
-+
-+
-+try:
-+ callable = callable
-+except NameError:
-+ def callable(obj):
-+ return any("__call__" in klass.__dict__ for klass in type(obj).__mro__)
-+
-+
-+if PY3:
-+ def get_unbound_function(unbound):
-+ return unbound
-+
-+ create_bound_method = types.MethodType
-+
-+ def create_unbound_method(func, cls):
-+ return func
-+
-+ Iterator = object
-+else:
-+ def get_unbound_function(unbound):
-+ return unbound.im_func
-+
-+ def create_bound_method(func, obj):
-+ return types.MethodType(func, obj, obj.__class__)
-+
-+ def create_unbound_method(func, cls):
-+ return types.MethodType(func, None, cls)
-+
-+ class Iterator(object):
-+
-+ def next(self):
-+ return type(self).__next__(self)
-+
-+ callable = callable
-+_add_doc(get_unbound_function,
-+ """Get the function out of a possibly unbound function""")
-+
-+
-+get_method_function = operator.attrgetter(_meth_func)
-+get_method_self = operator.attrgetter(_meth_self)
-+get_function_closure = operator.attrgetter(_func_closure)
-+get_function_code = operator.attrgetter(_func_code)
-+get_function_defaults = operator.attrgetter(_func_defaults)
-+get_function_globals = operator.attrgetter(_func_globals)
-+
-+
-+if PY3:
-+ def iterkeys(d, **kw):
-+ return iter(d.keys(**kw))
-+
-+ def itervalues(d, **kw):
-+ return iter(d.values(**kw))
-+
-+ def iteritems(d, **kw):
-+ return iter(d.items(**kw))
-+
-+ def iterlists(d, **kw):
-+ return iter(d.lists(**kw))
-+
-+ viewkeys = operator.methodcaller("keys")
-+
-+ viewvalues = operator.methodcaller("values")
-+
-+ viewitems = operator.methodcaller("items")
-+else:
-+ def iterkeys(d, **kw):
-+ return d.iterkeys(**kw)
-+
-+ def itervalues(d, **kw):
-+ return d.itervalues(**kw)
-+
-+ def iteritems(d, **kw):
-+ return d.iteritems(**kw)
-+
-+ def iterlists(d, **kw):
-+ return d.iterlists(**kw)
-+
-+ viewkeys = operator.methodcaller("viewkeys")
-+
-+ viewvalues = operator.methodcaller("viewvalues")
-+
-+ viewitems = operator.methodcaller("viewitems")
-+
-+_add_doc(iterkeys, "Return an iterator over the keys of a dictionary.")
-+_add_doc(itervalues, "Return an iterator over the values of a dictionary.")
-+_add_doc(iteritems,
-+ "Return an iterator over the (key, value) pairs of a dictionary.")
-+_add_doc(iterlists,
-+ "Return an iterator over the (key, [values]) pairs of a dictionary.")
-+
-+
-+if PY3:
-+ def b(s):
-+ return s.encode("latin-1")
-+
-+ def u(s):
-+ return s
-+ unichr = chr
-+ import struct
-+ int2byte = struct.Struct(">B").pack
-+ del struct
-+ byte2int = operator.itemgetter(0)
-+ indexbytes = operator.getitem
-+ iterbytes = iter
-+ import io
-+ StringIO = io.StringIO
-+ BytesIO = io.BytesIO
-+ _assertCountEqual = "assertCountEqual"
-+ if sys.version_info[1] <= 1:
-+ _assertRaisesRegex = "assertRaisesRegexp"
-+ _assertRegex = "assertRegexpMatches"
-+ else:
-+ _assertRaisesRegex = "assertRaisesRegex"
-+ _assertRegex = "assertRegex"
-+else:
-+ def b(s):
-+ return s
-+ # Workaround for standalone backslash
-+
-+ def u(s):
-+ return unicode(s.replace(r'\\', r'\\\\'), "unicode_escape")
-+ unichr = unichr
-+ int2byte = chr
-+
-+ def byte2int(bs):
-+ return ord(bs[0])
-+
-+ def indexbytes(buf, i):
-+ return ord(buf[i])
-+ iterbytes = functools.partial(itertools.imap, ord)
-+ import StringIO
-+ StringIO = BytesIO = StringIO.StringIO
-+ _assertCountEqual = "assertItemsEqual"
-+ _assertRaisesRegex = "assertRaisesRegexp"
-+ _assertRegex = "assertRegexpMatches"
-+_add_doc(b, """Byte literal""")
-+_add_doc(u, """Text literal""")
-+
-+
-+def assertCountEqual(self, *args, **kwargs):
-+ return getattr(self, _assertCountEqual)(*args, **kwargs)
-+
-+
-+def assertRaisesRegex(self, *args, **kwargs):
-+ return getattr(self, _assertRaisesRegex)(*args, **kwargs)
-+
-+
-+def assertRegex(self, *args, **kwargs):
-+ return getattr(self, _assertRegex)(*args, **kwargs)
-+
-+
-+if PY3:
-+ exec_ = getattr(moves.builtins, "exec")
-+
-+ def reraise(tp, value, tb=None):
-+ if value is None:
-+ value = tp()
-+ if value.__traceback__ is not tb:
-+ raise value.with_traceback(tb)
-+ raise value
-+
-+else:
-+ def exec_(_code_, _globs_=None, _locs_=None):
-+ """Execute code in a namespace."""
-+ if _globs_ is None:
-+ frame = sys._getframe(1)
-+ _globs_ = frame.f_globals
-+ if _locs_ is None:
-+ _locs_ = frame.f_locals
-+ del frame
-+ elif _locs_ is None:
-+ _locs_ = _globs_
-+ exec("""exec _code_ in _globs_, _locs_""")
-+
-+ exec_("""def reraise(tp, value, tb=None):
-+ raise tp, value, tb
-+""")
-+
-+
-+if sys.version_info[:2] == (3, 2):
-+ exec_("""def raise_from(value, from_value):
-+ if from_value is None:
-+ raise value
-+ raise value from from_value
-+""")
-+elif sys.version_info[:2] > (3, 2):
-+ exec_("""def raise_from(value, from_value):
-+ raise value from from_value
-+""")
-+else:
-+ def raise_from(value, from_value):
-+ raise value
-+
-+
-+print_ = getattr(moves.builtins, "print", None)
-+if print_ is None:
-+ def print_(*args, **kwargs):
-+ """The new-style print function for Python 2.4 and 2.5."""
-+ fp = kwargs.pop("file", sys.stdout)
-+ if fp is None:
-+ return
-+
-+ def write(data):
-+ if not isinstance(data, basestring):
-+ data = str(data)
-+ # If the file has an encoding, encode unicode with it.
-+ if (isinstance(fp, file) and
-+ isinstance(data, unicode) and
-+ fp.encoding is not None):
-+ errors = getattr(fp, "errors", None)
-+ if errors is None:
-+ errors = "strict"
-+ data = data.encode(fp.encoding, errors)
-+ fp.write(data)
-+ want_unicode = False
-+ sep = kwargs.pop("sep", None)
-+ if sep is not None:
-+ if isinstance(sep, unicode):
-+ want_unicode = True
-+ elif not isinstance(sep, str):
-+ raise TypeError("sep must be None or a string")
-+ end = kwargs.pop("end", None)
-+ if end is not None:
-+ if isinstance(end, unicode):
-+ want_unicode = True
-+ elif not isinstance(end, str):
-+ raise TypeError("end must be None or a string")
-+ if kwargs:
-+ raise TypeError("invalid keyword arguments to print()")
-+ if not want_unicode:
-+ for arg in args:
-+ if isinstance(arg, unicode):
-+ want_unicode = True
-+ break
-+ if want_unicode:
-+ newline = unicode("\n")
-+ space = unicode(" ")
-+ else:
-+ newline = "\n"
-+ space = " "
-+ if sep is None:
-+ sep = space
-+ if end is None:
-+ end = newline
-+ for i, arg in enumerate(args):
-+ if i:
-+ write(sep)
-+ write(arg)
-+ write(end)
-+if sys.version_info[:2] < (3, 3):
-+ _print = print_
-+
-+ def print_(*args, **kwargs):
-+ fp = kwargs.get("file", sys.stdout)
-+ flush = kwargs.pop("flush", False)
-+ _print(*args, **kwargs)
-+ if flush and fp is not None:
-+ fp.flush()
-+
-+_add_doc(reraise, """Reraise an exception.""")
-+
-+if sys.version_info[0:2] < (3, 4):
-+ def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS,
-+ updated=functools.WRAPPER_UPDATES):
-+ def wrapper(f):
-+ f = functools.wraps(wrapped, assigned, updated)(f)
-+ f.__wrapped__ = wrapped
-+ return f
-+ return wrapper
-+else:
-+ wraps = functools.wraps
-+
-+
-+def with_metaclass(meta, *bases):
-+ """Create a base class with a metaclass."""
-+ # This requires a bit of explanation: the basic idea is to make a dummy
-+ # metaclass for one level of class instantiation that replaces itself with
-+ # the actual metaclass.
-+ class metaclass(meta):
-+
-+ def __new__(cls, name, this_bases, d):
-+ return meta(name, bases, d)
-+ return type.__new__(metaclass, 'temporary_class', (), {})
-+
-+
-+def add_metaclass(metaclass):
-+ """Class decorator for creating a class with a metaclass."""
-+ def wrapper(cls):
-+ orig_vars = cls.__dict__.copy()
-+ slots = orig_vars.get('__slots__')
-+ if slots is not None:
-+ if isinstance(slots, str):
-+ slots = [slots]
-+ for slots_var in slots:
-+ orig_vars.pop(slots_var)
-+ orig_vars.pop('__dict__', None)
-+ orig_vars.pop('__weakref__', None)
-+ return metaclass(cls.__name__, cls.__bases__, orig_vars)
-+ return wrapper
-+
-+
-+def python_2_unicode_compatible(klass):
-+ """
-+ A decorator that defines __unicode__ and __str__ methods under Python 2.
-+ Under Python 3 it does nothing.
-+
-+ To support Python 2 and 3 with a single code base, define a __str__ method
-+ returning text and apply this decorator to the class.
-+ """
-+ if PY2:
-+ if '__str__' not in klass.__dict__:
-+ raise ValueError("@python_2_unicode_compatible cannot be applied "
-+ "to %s because it doesn't define __str__()." %
-+ klass.__name__)
-+ klass.__unicode__ = klass.__str__
-+ klass.__str__ = lambda self: self.__unicode__().encode('utf-8')
-+ return klass
-+
-+
-+# Complete the moves implementation.
-+# This code is at the end of this module to speed up module loading.
-+# Turn this module into a package.
-+__path__ = [] # required for PEP 302 and PEP 451
-+__package__ = __name__ # see PEP 366 @ReservedAssignment
-+if globals().get("__spec__") is not None:
-+ __spec__.submodule_search_locations = [] # PEP 451 @UndefinedVariable
-+# Remove other six meta path importers, since they cause problems. This can
-+# happen if six is removed from sys.modules and then reloaded. (Setuptools does
-+# this for some reason.)
-+if sys.meta_path:
-+ for i, importer in enumerate(sys.meta_path):
-+ # Here's some real nastiness: Another "instance" of the six module might
-+ # be floating around. Therefore, we can't use isinstance() to check for
-+ # the six meta path importer, since the other six instance will have
-+ # inserted an importer with different class.
-+ if (type(importer).__name__ == "_SixMetaPathImporter" and
-+ importer.name == __name__):
-+ del sys.meta_path[i]
-+ break
-+ del i, importer
-+# Finally, add the importer to the meta path import hook.
-+sys.meta_path.append(_importer)
++++ b/tools/generate_stubs/rules.gni
+@@ -0,0 +1,2 @@
++# "empty" file in place of importing new tools/generate_stubs
++# to allow BUILD.gn imports to succeed.
diff --git a/third_party/libwebrtc/moz-patch-stack/0100.patch b/third_party/libwebrtc/moz-patch-stack/0100.patch
index dc3cc7ca1a..6ad5dd698c 100644
--- a/third_party/libwebrtc/moz-patch-stack/0100.patch
+++ b/third_party/libwebrtc/moz-patch-stack/0100.patch
@@ -1,20 +1,64 @@
-From: Michael Froman <mfroman@mozilla.com>
-Date: Wed, 7 Dec 2022 17:09:00 +0000
-Subject: Bug 1744645 - pt1 - add a couple empty gni files to help with
- BUILD.gn corrections. r=ng
+From: Nico Grunbaum <na-g@nostrum.com>
+Date: Wed, 15 Nov 2023 22:33:00 +0000
+Subject: Bug 1863041 - P0 - add device filter control to
+ RTCCameraVideoCapturer;r=pehrsons,webrtc-reviewers
-Differential Revision: https://phabricator.services.mozilla.com/D163991
-Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/81d86382ee468f3b36deed00d0c9d59eb85524be
+I have filed this bug upstream: https://bugs.chromium.org/p/webrtc/issues/detail?id=15639
+
+Differential Revision: https://phabricator.services.mozilla.com/D193172
+Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/91a763d768b74acc9cf4828f91a86df4a7b092ce
---
- tools/generate_stubs/rules.gni | 2 ++
- 1 file changed, 2 insertions(+)
- create mode 100644 tools/generate_stubs/rules.gni
+ .../capturer/RTCCameraVideoCapturer.h | 5 ++++-
+ .../capturer/RTCCameraVideoCapturer.m | 17 +++++++++++++++--
+ 2 files changed, 19 insertions(+), 3 deletions(-)
-diff --git a/tools/generate_stubs/rules.gni b/tools/generate_stubs/rules.gni
-new file mode 100644
-index 0000000000..1d9f36eb72
---- /dev/null
-+++ b/tools/generate_stubs/rules.gni
-@@ -0,0 +1,2 @@
-+# "empty" file in place of importing new tools/generate_stubs
-+# to allow BUILD.gn imports to succeed.
+diff --git a/sdk/objc/components/capturer/RTCCameraVideoCapturer.h b/sdk/objc/components/capturer/RTCCameraVideoCapturer.h
+index 370bfa70f0..b1f3f64f74 100644
+--- a/sdk/objc/components/capturer/RTCCameraVideoCapturer.h
++++ b/sdk/objc/components/capturer/RTCCameraVideoCapturer.h
+@@ -26,7 +26,10 @@ NS_EXTENSION_UNAVAILABLE_IOS("Camera not available in app extensions.")
+ @property(readonly, nonatomic) AVCaptureSession *captureSession;
+
+ // Returns list of available capture devices that support video capture.
+-+ (NSArray<AVCaptureDevice *> *)captureDevices;
+++ (NSArray<AVCaptureDevice *> *)captureDevicesWithDeviceTypes:
++ (NSArray<AVCaptureDeviceType> *)deviceTypes;
++// Returns list of default capture devices types
+++ (NSArray<AVCaptureDeviceType> *)defaultCaptureDeviceTypes;
+ // Returns list of formats that are supported by this class for this device.
+ + (NSArray<AVCaptureDeviceFormat *> *)supportedFormatsForDevice:(AVCaptureDevice *)device;
+
+diff --git a/sdk/objc/components/capturer/RTCCameraVideoCapturer.m b/sdk/objc/components/capturer/RTCCameraVideoCapturer.m
+index e7c47b4e99..1361207faf 100644
+--- a/sdk/objc/components/capturer/RTCCameraVideoCapturer.m
++++ b/sdk/objc/components/capturer/RTCCameraVideoCapturer.m
+@@ -117,14 +117,27 @@ const int64_t kNanosecondsPerSecond = 1000000000;
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+ }
+
+-+ (NSArray<AVCaptureDevice *> *)captureDevices {
+++ (NSArray<AVCaptureDevice *> *)captureDevicesWithDeviceTypes:
++ (NSArray<AVCaptureDeviceType> *)deviceTypes {
+ AVCaptureDeviceDiscoverySession *session = [AVCaptureDeviceDiscoverySession
+- discoverySessionWithDeviceTypes:@[ AVCaptureDeviceTypeBuiltInWideAngleCamera ]
++ discoverySessionWithDeviceTypes:deviceTypes
+ mediaType:AVMediaTypeVideo
+ position:AVCaptureDevicePositionUnspecified];
+ return session.devices;
+ }
+
+++ (NSArray<AVCaptureDeviceType> *)defaultCaptureDeviceTypes {
++ NSArray *types = @[ AVCaptureDeviceTypeBuiltInWideAngleCamera ];
++#if !defined(WEBRTC_IOS)
++ if (@available(macOS 14.0, *)) {
++ types = [types arrayByAddingObject:AVCaptureDeviceTypeExternal];
++ } else {
++ types = [types arrayByAddingObject:AVCaptureDeviceTypeExternalUnknown];
++ }
++#endif
++ return types;
++}
++
+ + (NSArray<AVCaptureDeviceFormat *> *)supportedFormatsForDevice:(AVCaptureDevice *)device {
+ // Support opening the device in any format. We make sure it's converted to a format we
+ // can handle, if needed, in the method `-setupVideoDataOutput`.
diff --git a/third_party/libwebrtc/moz-patch-stack/0101.patch b/third_party/libwebrtc/moz-patch-stack/0101.patch
index 6ad5dd698c..7cf1017a12 100644
--- a/third_party/libwebrtc/moz-patch-stack/0101.patch
+++ b/third_party/libwebrtc/moz-patch-stack/0101.patch
@@ -1,64 +1,36 @@
-From: Nico Grunbaum <na-g@nostrum.com>
-Date: Wed, 15 Nov 2023 22:33:00 +0000
-Subject: Bug 1863041 - P0 - add device filter control to
- RTCCameraVideoCapturer;r=pehrsons,webrtc-reviewers
+From: Michael Froman <mfroman@mozilla.com>
+Date: Mon, 4 Dec 2023 12:57:00 -0600
+Subject: Bug 1867099 - (fix-66b7275561) disable wgc capture yellow-line
+ removal. r?ng!
-I have filed this bug upstream: https://bugs.chromium.org/p/webrtc/issues/detail?id=15639
-
-Differential Revision: https://phabricator.services.mozilla.com/D193172
-Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/91a763d768b74acc9cf4828f91a86df4a7b092ce
+This code won't build until we support building with
+Win 10 SDK v10.0.20348.0 or newer.
+Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/0aac94794aad2ddb637f5076bc08706a11866737
---
- .../capturer/RTCCameraVideoCapturer.h | 5 ++++-
- .../capturer/RTCCameraVideoCapturer.m | 17 +++++++++++++++--
- 2 files changed, 19 insertions(+), 3 deletions(-)
+ modules/desktop_capture/win/wgc_capture_session.cc | 6 ++++++
+ 1 file changed, 6 insertions(+)
-diff --git a/sdk/objc/components/capturer/RTCCameraVideoCapturer.h b/sdk/objc/components/capturer/RTCCameraVideoCapturer.h
-index 370bfa70f0..b1f3f64f74 100644
---- a/sdk/objc/components/capturer/RTCCameraVideoCapturer.h
-+++ b/sdk/objc/components/capturer/RTCCameraVideoCapturer.h
-@@ -26,7 +26,10 @@ NS_EXTENSION_UNAVAILABLE_IOS("Camera not available in app extensions.")
- @property(readonly, nonatomic) AVCaptureSession *captureSession;
-
- // Returns list of available capture devices that support video capture.
--+ (NSArray<AVCaptureDevice *> *)captureDevices;
-++ (NSArray<AVCaptureDevice *> *)captureDevicesWithDeviceTypes:
-+ (NSArray<AVCaptureDeviceType> *)deviceTypes;
-+// Returns list of default capture devices types
-++ (NSArray<AVCaptureDeviceType> *)defaultCaptureDeviceTypes;
- // Returns list of formats that are supported by this class for this device.
- + (NSArray<AVCaptureDeviceFormat *> *)supportedFormatsForDevice:(AVCaptureDevice *)device;
+diff --git a/modules/desktop_capture/win/wgc_capture_session.cc b/modules/desktop_capture/win/wgc_capture_session.cc
+index 8c74c2bf24..86afc52411 100644
+--- a/modules/desktop_capture/win/wgc_capture_session.cc
++++ b/modules/desktop_capture/win/wgc_capture_session.cc
+@@ -188,6 +188,11 @@ HRESULT WgcCaptureSession::StartCapture(const DesktopCaptureOptions& options) {
+ }
+ }
-diff --git a/sdk/objc/components/capturer/RTCCameraVideoCapturer.m b/sdk/objc/components/capturer/RTCCameraVideoCapturer.m
-index e7c47b4e99..1361207faf 100644
---- a/sdk/objc/components/capturer/RTCCameraVideoCapturer.m
-+++ b/sdk/objc/components/capturer/RTCCameraVideoCapturer.m
-@@ -117,14 +117,27 @@ const int64_t kNanosecondsPerSecond = 1000000000;
- [[NSNotificationCenter defaultCenter] removeObserver:self];
- }
++// Until Mozilla builds with Win 10 SDK v10.0.20348.0 or newer, this
++// code will not build. Once we support the newer SDK, Bug 1868198
++// exists to decide if we ever want to use this code since it is
++// removing an indicator that capture is happening.
++#if !defined(WEBRTC_MOZILLA_BUILD)
+ // By default, the WGC capture API adds a yellow border around the captured
+ // window or display to indicate that a capture is in progress. The section
+ // below is an attempt to remove this yellow border to make the capture
+@@ -199,6 +204,7 @@ HRESULT WgcCaptureSession::StartCapture(const DesktopCaptureOptions& options) {
+ &session3))) {
+ session3->put_IsBorderRequired(false);
+ }
++#endif
--+ (NSArray<AVCaptureDevice *> *)captureDevices {
-++ (NSArray<AVCaptureDevice *> *)captureDevicesWithDeviceTypes:
-+ (NSArray<AVCaptureDeviceType> *)deviceTypes {
- AVCaptureDeviceDiscoverySession *session = [AVCaptureDeviceDiscoverySession
-- discoverySessionWithDeviceTypes:@[ AVCaptureDeviceTypeBuiltInWideAngleCamera ]
-+ discoverySessionWithDeviceTypes:deviceTypes
- mediaType:AVMediaTypeVideo
- position:AVCaptureDevicePositionUnspecified];
- return session.devices;
- }
+ allow_zero_hertz_ = options.allow_wgc_zero_hertz();
-++ (NSArray<AVCaptureDeviceType> *)defaultCaptureDeviceTypes {
-+ NSArray *types = @[ AVCaptureDeviceTypeBuiltInWideAngleCamera ];
-+#if !defined(WEBRTC_IOS)
-+ if (@available(macOS 14.0, *)) {
-+ types = [types arrayByAddingObject:AVCaptureDeviceTypeExternal];
-+ } else {
-+ types = [types arrayByAddingObject:AVCaptureDeviceTypeExternalUnknown];
-+ }
-+#endif
-+ return types;
-+}
-+
- + (NSArray<AVCaptureDeviceFormat *> *)supportedFormatsForDevice:(AVCaptureDevice *)device {
- // Support opening the device in any format. We make sure it's converted to a format we
- // can handle, if needed, in the method `-setupVideoDataOutput`.
diff --git a/third_party/libwebrtc/moz-patch-stack/0102.patch b/third_party/libwebrtc/moz-patch-stack/0102.patch
index 9b0e544701..07be6fc934 100644
--- a/third_party/libwebrtc/moz-patch-stack/0102.patch
+++ b/third_party/libwebrtc/moz-patch-stack/0102.patch
@@ -1,110 +1,121 @@
From: Jan Grulich <jgrulich@redhat.com>
-Date: Mon, 27 Nov 2023 10:33:00 +0000
-Subject: Bug 1865872 - Fix thread and lock annotations in PipeWire video
- capture r=pehrsons,webrtc-reviewers
+Date: Thu, 30 Nov 2023 11:49:00 +0000
+Subject: Bug 1844020 - Add option to DeviceInfo::GetDeviceName() identifying a
+ placeholder device r=pehrsons,webrtc-reviewers
-This is a simple backport of an WebRTC upstream change.
+Adds a new parameter "deviceIsPlaceholder" that will be set to true in
+case the returned device is not a real device but a placeholder that is
+just used to inform about camera device existence.
-Upstream commit: a9d497b52dc21497fdfd0e8c03ab2f8559e02d15
-
-Differential Revision: https://phabricator.services.mozilla.com/D194247
-Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/f8332fd7347051f18a029f5c9314698cf3cd68ae
+Differential Revision: https://phabricator.services.mozilla.com/D189929
+Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/ed31b2acb5fbca3e2d0691a64bc52e65952070c0
---
- modules/video_capture/linux/video_capture_pipewire.cc | 8 +++++---
- modules/video_capture/linux/video_capture_pipewire.h | 8 +++++---
- rtc_base/race_checker.h | 11 ++++++++---
- 3 files changed, 18 insertions(+), 9 deletions(-)
+ modules/video_capture/linux/device_info_pipewire.cc | 4 +++-
+ modules/video_capture/linux/device_info_pipewire.h | 3 ++-
+ modules/video_capture/linux/device_info_v4l2.cc | 3 ++-
+ modules/video_capture/linux/device_info_v4l2.h | 3 ++-
+ modules/video_capture/video_capture.h | 3 ++-
+ modules/video_capture/windows/device_info_ds.cc | 3 ++-
+ modules/video_capture/windows/device_info_ds.h | 3 ++-
+ 7 files changed, 15 insertions(+), 7 deletions(-)
-diff --git a/modules/video_capture/linux/video_capture_pipewire.cc b/modules/video_capture/linux/video_capture_pipewire.cc
-index 7b79f15a6c..9d47e3ddbf 100644
---- a/modules/video_capture/linux/video_capture_pipewire.cc
-+++ b/modules/video_capture/linux/video_capture_pipewire.cc
-@@ -126,6 +126,7 @@ int32_t VideoCaptureModulePipeWire::StartCapture(
- RTC_LOG(LS_VERBOSE) << "Creating new PipeWire stream for node " << node_id_;
-
- PipeWireThreadLoopLock thread_loop_lock(session_->pw_main_loop_);
-+ RTC_CHECK_RUNS_SERIALIZED(&pipewire_checker_);
- pw_properties* reuse_props =
- pw_properties_new_string("pipewire.client.reuse=1");
- stream_ = pw_stream_new(session_->pw_core_, "camera-stream", reuse_props);
-@@ -178,6 +179,7 @@ int32_t VideoCaptureModulePipeWire::StopCapture() {
- RTC_DCHECK_RUN_ON(&api_checker_);
-
- PipeWireThreadLoopLock thread_loop_lock(session_->pw_main_loop_);
-+ RTC_CHECK_RUNS_SERIALIZED(&pipewire_checker_);
- if (stream_) {
- pw_stream_destroy(stream_);
- stream_ = nullptr;
-@@ -210,14 +212,14 @@ void VideoCaptureModulePipeWire::OnStreamParamChanged(
- VideoCaptureModulePipeWire* that =
- static_cast<VideoCaptureModulePipeWire*>(data);
- RTC_DCHECK(that);
-- RTC_CHECK_RUNS_SERIALIZED(&that->capture_checker_);
-+ RTC_CHECK_RUNS_SERIALIZED(&that->pipewire_checker_);
-
- if (format && id == SPA_PARAM_Format)
- that->OnFormatChanged(format);
- }
-
- void VideoCaptureModulePipeWire::OnFormatChanged(const struct spa_pod* format) {
-- RTC_CHECK_RUNS_SERIALIZED(&capture_checker_);
-+ RTC_CHECK_RUNS_SERIALIZED(&pipewire_checker_);
-
- uint32_t media_type, media_subtype;
-
-@@ -359,7 +361,7 @@ static VideoRotation VideorotationFromPipeWireTransform(uint32_t transform) {
- }
-
- void VideoCaptureModulePipeWire::ProcessBuffers() {
-- RTC_CHECK_RUNS_SERIALIZED(&capture_checker_);
-+ RTC_CHECK_RUNS_SERIALIZED(&pipewire_checker_);
-
- while (pw_buffer* buffer = pw_stream_dequeue_buffer(stream_)) {
- struct spa_meta_header* h;
-diff --git a/modules/video_capture/linux/video_capture_pipewire.h b/modules/video_capture/linux/video_capture_pipewire.h
-index 316fb2449d..620ee520ca 100644
---- a/modules/video_capture/linux/video_capture_pipewire.h
-+++ b/modules/video_capture/linux/video_capture_pipewire.h
-@@ -43,15 +43,17 @@ class VideoCaptureModulePipeWire : public VideoCaptureImpl {
- void OnFormatChanged(const struct spa_pod* format);
- void ProcessBuffers();
-
-+ rtc::RaceChecker pipewire_checker_;
+diff --git a/modules/video_capture/linux/device_info_pipewire.cc b/modules/video_capture/linux/device_info_pipewire.cc
+index fc0554f384..f9f08a9c27 100644
+--- a/modules/video_capture/linux/device_info_pipewire.cc
++++ b/modules/video_capture/linux/device_info_pipewire.cc
+@@ -50,8 +50,10 @@ int32_t DeviceInfoPipeWire::GetDeviceName(uint32_t deviceNumber,
+ uint32_t deviceUniqueIdUTF8Length,
+ char* productUniqueIdUTF8,
+ uint32_t productUniqueIdUTF8Length,
+- pid_t* pid) {
++ pid_t* pid,
++ bool* deviceIsPlaceholder) {
+ RTC_CHECK(pipewire_session_);
+
- const rtc::scoped_refptr<PipeWireSession> session_
- RTC_GUARDED_BY(capture_checker_);
- int node_id_ RTC_GUARDED_BY(capture_checker_);
- VideoCaptureCapability configured_capability_
-- RTC_GUARDED_BY(capture_checker_);
-+ RTC_GUARDED_BY(pipewire_checker_);
- bool started_ RTC_GUARDED_BY(api_lock_);
+ if (deviceNumber >= NumberOfDevices())
+ return -1;
-- struct pw_stream* stream_ RTC_GUARDED_BY(capture_checker_) = nullptr;
-- struct spa_hook stream_listener_ RTC_GUARDED_BY(capture_checker_);
-+ struct pw_stream* stream_ RTC_GUARDED_BY(pipewire_checker_) = nullptr;
-+ struct spa_hook stream_listener_ RTC_GUARDED_BY(pipewire_checker_);
- };
- } // namespace videocapturemodule
- } // namespace webrtc
-diff --git a/rtc_base/race_checker.h b/rtc_base/race_checker.h
-index 4d574601eb..00bab52f33 100644
---- a/rtc_base/race_checker.h
-+++ b/rtc_base/race_checker.h
-@@ -62,9 +62,14 @@ class RTC_SCOPED_LOCKABLE RaceCheckerScopeDoNothing {
- } // namespace internal
- } // namespace rtc
+diff --git a/modules/video_capture/linux/device_info_pipewire.h b/modules/video_capture/linux/device_info_pipewire.h
+index 8a33d75892..00715c94bc 100644
+--- a/modules/video_capture/linux/device_info_pipewire.h
++++ b/modules/video_capture/linux/device_info_pipewire.h
+@@ -30,7 +30,8 @@ class DeviceInfoPipeWire : public DeviceInfoImpl {
+ uint32_t deviceUniqueIdUTF8Length,
+ char* productUniqueIdUTF8 = nullptr,
+ uint32_t productUniqueIdUTF8Length = 0,
+- pid_t* pid = 0) override;
++ pid_t* pid = 0,
++ bool* deviceIsPlaceholder = 0) override;
+ /*
+ * Fills the membervariable _captureCapabilities with capabilites for the
+ * given device name.
+diff --git a/modules/video_capture/linux/device_info_v4l2.cc b/modules/video_capture/linux/device_info_v4l2.cc
+index 04caaea592..401c38f9c5 100644
+--- a/modules/video_capture/linux/device_info_v4l2.cc
++++ b/modules/video_capture/linux/device_info_v4l2.cc
+@@ -232,7 +232,8 @@ int32_t DeviceInfoV4l2::GetDeviceName(uint32_t deviceNumber,
+ uint32_t deviceUniqueIdUTF8Length,
+ char* /*productUniqueIdUTF8*/,
+ uint32_t /*productUniqueIdUTF8Length*/,
+- pid_t* /*pid*/) {
++ pid_t* /*pid*/,
++ bool* /*deviceIsPlaceholder*/) {
+ // Travel through /dev/video [0-63]
+ uint32_t count = 0;
+ char device[20];
+diff --git a/modules/video_capture/linux/device_info_v4l2.h b/modules/video_capture/linux/device_info_v4l2.h
+index 0bec3eb765..55415845ad 100644
+--- a/modules/video_capture/linux/device_info_v4l2.h
++++ b/modules/video_capture/linux/device_info_v4l2.h
+@@ -36,7 +36,8 @@ class DeviceInfoV4l2 : public DeviceInfoImpl {
+ uint32_t deviceUniqueIdUTF8Length,
+ char* productUniqueIdUTF8 = 0,
+ uint32_t productUniqueIdUTF8Length = 0,
+- pid_t* pid=0) override;
++ pid_t* pid = 0,
++ bool* deviceIsPlaceholder = 0) override;
+ /*
+ * Fills the membervariable _captureCapabilities with capabilites for the
+ * given device name.
+diff --git a/modules/video_capture/video_capture.h b/modules/video_capture/video_capture.h
+index 43a6a7f832..f59c34f8b2 100644
+--- a/modules/video_capture/video_capture.h
++++ b/modules/video_capture/video_capture.h
+@@ -74,7 +74,8 @@ class VideoCaptureModule : public RefCountInterface {
+ uint32_t deviceUniqueIdUTF8Length,
+ char* productUniqueIdUTF8 = 0,
+ uint32_t productUniqueIdUTF8Length = 0,
+- pid_t* pid = 0) = 0;
++ pid_t* pid = 0,
++ bool* deviceIsPlaceholder = 0) = 0;
--#define RTC_CHECK_RUNS_SERIALIZED(x) \
-- rtc::internal::RaceCheckerScope race_checker(x); \
-- RTC_CHECK(!race_checker.RaceDetected())
-+#define RTC_CHECK_RUNS_SERIALIZED(x) RTC_CHECK_RUNS_SERIALIZED_NEXT(x, __LINE__)
-+
-+#define RTC_CHECK_RUNS_SERIALIZED_NEXT(x, suffix) \
-+ RTC_CHECK_RUNS_SERIALIZED_IMPL(x, suffix)
-+
-+#define RTC_CHECK_RUNS_SERIALIZED_IMPL(x, suffix) \
-+ rtc::internal::RaceCheckerScope race_checker##suffix(x); \
-+ RTC_CHECK(!race_checker##suffix.RaceDetected())
+ // Returns the number of capabilities this device.
+ virtual int32_t NumberOfCapabilities(const char* deviceUniqueIdUTF8) = 0;
+diff --git a/modules/video_capture/windows/device_info_ds.cc b/modules/video_capture/windows/device_info_ds.cc
+index f6927281f3..8ca741239c 100644
+--- a/modules/video_capture/windows/device_info_ds.cc
++++ b/modules/video_capture/windows/device_info_ds.cc
+@@ -173,7 +173,8 @@ int32_t DeviceInfoDS::GetDeviceName(uint32_t deviceNumber,
+ uint32_t deviceUniqueIdUTF8Length,
+ char* productUniqueIdUTF8,
+ uint32_t productUniqueIdUTF8Length,
+- pid_t* pid) {
++ pid_t* pid,
++ bool* deviceIsPlaceholder) {
+ MutexLock lock(&_apiLock);
+ const int32_t result = GetDeviceInfo(
+ deviceNumber, deviceNameUTF8, deviceNameLength, deviceUniqueIdUTF8,
+diff --git a/modules/video_capture/windows/device_info_ds.h b/modules/video_capture/windows/device_info_ds.h
+index e6dfaed366..a9a1449b99 100644
+--- a/modules/video_capture/windows/device_info_ds.h
++++ b/modules/video_capture/windows/device_info_ds.h
+@@ -51,7 +51,8 @@ class DeviceInfoDS : public DeviceInfoImpl {
+ uint32_t deviceUniqueIdUTF8Length,
+ char* productUniqueIdUTF8,
+ uint32_t productUniqueIdUTF8Length,
+- pid_t* pid) override;
++ pid_t* pid,
++ bool* deviceIsPlaceholder) override;
- #if RTC_DCHECK_IS_ON
- #define RTC_DCHECK_RUNS_SERIALIZED(x) \
+ /*
+ * Display OS /capture device specific settings dialog
diff --git a/third_party/libwebrtc/moz-patch-stack/0103.patch b/third_party/libwebrtc/moz-patch-stack/0103.patch
index 7cf1017a12..d232dcb897 100644
--- a/third_party/libwebrtc/moz-patch-stack/0103.patch
+++ b/third_party/libwebrtc/moz-patch-stack/0103.patch
@@ -1,36 +1,107 @@
From: Michael Froman <mfroman@mozilla.com>
-Date: Mon, 4 Dec 2023 12:57:00 -0600
-Subject: Bug 1867099 - (fix-66b7275561) disable wgc capture yellow-line
- removal. r?ng!
+Date: Mon, 18 Dec 2023 15:00:00 +0000
+Subject: Bug 1867099 - revert libwebrtc 8602f604e0. r=bwc
-This code won't build until we support building with
-Win 10 SDK v10.0.20348.0 or newer.
-Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/0aac94794aad2ddb637f5076bc08706a11866737
+Upstream 8602f604e0 removed code sending BYEs which breaks some of
+our wpt. They've opened a bug for a real fix here:
+https://bugs.chromium.org/p/webrtc/issues/detail?id=15664
+
+I've opened Bug 1870643 to track the real fix and upstream bug.
+
+Differential Revision: https://phabricator.services.mozilla.com/D196729
+Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/d92a578327f524ec3e1c144c82492a4c76b8266f
---
- modules/desktop_capture/win/wgc_capture_session.cc | 6 ++++++
- 1 file changed, 6 insertions(+)
+ call/rtp_video_sender.cc | 1 +
+ modules/rtp_rtcp/source/rtcp_sender.cc | 19 +++++++++++++++++--
+ .../rtp_rtcp/source/rtcp_sender_unittest.cc | 5 +++--
+ modules/rtp_rtcp/source/rtp_rtcp_impl.cc | 1 +
+ modules/rtp_rtcp/source/rtp_rtcp_interface.h | 2 +-
+ 5 files changed, 23 insertions(+), 5 deletions(-)
-diff --git a/modules/desktop_capture/win/wgc_capture_session.cc b/modules/desktop_capture/win/wgc_capture_session.cc
-index 8c74c2bf24..86afc52411 100644
---- a/modules/desktop_capture/win/wgc_capture_session.cc
-+++ b/modules/desktop_capture/win/wgc_capture_session.cc
-@@ -188,6 +188,11 @@ HRESULT WgcCaptureSession::StartCapture(const DesktopCaptureOptions& options) {
- }
- }
+diff --git a/call/rtp_video_sender.cc b/call/rtp_video_sender.cc
+index 1ace08fa32..4d99c61bb4 100644
+--- a/call/rtp_video_sender.cc
++++ b/call/rtp_video_sender.cc
+@@ -510,6 +510,7 @@ void RtpVideoSender::SetActiveModulesLocked(
+ const bool was_active = rtp_module.Sending();
+ const bool should_be_active = active_modules[i];
+
++ // Sends a kRtcpByeCode when going from true to false.
+ rtp_module.SetSendingStatus(active_modules[i]);
+
+ if (was_active && !should_be_active) {
+diff --git a/modules/rtp_rtcp/source/rtcp_sender.cc b/modules/rtp_rtcp/source/rtcp_sender.cc
+index 099b0be1a3..971f49b949 100644
+--- a/modules/rtp_rtcp/source/rtcp_sender.cc
++++ b/modules/rtp_rtcp/source/rtcp_sender.cc
+@@ -212,8 +212,23 @@ bool RTCPSender::Sending() const {
+
+ void RTCPSender::SetSendingStatus(const FeedbackState& feedback_state,
+ bool sending) {
+- MutexLock lock(&mutex_rtcp_sender_);
+- sending_ = sending;
++ bool sendRTCPBye = false;
++ {
++ MutexLock lock(&mutex_rtcp_sender_);
++
++ if (method_ != RtcpMode::kOff) {
++ if (sending == false && sending_ == true) {
++ // Trigger RTCP bye
++ sendRTCPBye = true;
++ }
++ }
++ sending_ = sending;
++ }
++ if (sendRTCPBye) {
++ if (SendRTCP(feedback_state, kRtcpBye) != 0) {
++ RTC_LOG(LS_WARNING) << "Failed to send RTCP BYE";
++ }
++ }
+ }
+
+ void RTCPSender::SetNonSenderRttMeasurement(bool enabled) {
+diff --git a/modules/rtp_rtcp/source/rtcp_sender_unittest.cc b/modules/rtp_rtcp/source/rtcp_sender_unittest.cc
+index 002a5f86f1..1dcb628722 100644
+--- a/modules/rtp_rtcp/source/rtcp_sender_unittest.cc
++++ b/modules/rtp_rtcp/source/rtcp_sender_unittest.cc
+@@ -328,12 +328,13 @@ TEST_F(RtcpSenderTest, SendBye) {
+ EXPECT_EQ(kSenderSsrc, parser()->bye()->sender_ssrc());
+ }
+
+-TEST_F(RtcpSenderTest, StopSendingDoesNotTriggersBye) {
++TEST_F(RtcpSenderTest, StopSendingTriggersBye) {
+ auto rtcp_sender = CreateRtcpSender(GetDefaultConfig());
+ rtcp_sender->SetRTCPStatus(RtcpMode::kReducedSize);
+ rtcp_sender->SetSendingStatus(feedback_state(), true);
+ rtcp_sender->SetSendingStatus(feedback_state(), false);
+- EXPECT_EQ(0, parser()->bye()->num_packets());
++ EXPECT_EQ(1, parser()->bye()->num_packets());
++ EXPECT_EQ(kSenderSsrc, parser()->bye()->sender_ssrc());
+ }
+
+ TEST_F(RtcpSenderTest, SendFir) {
+diff --git a/modules/rtp_rtcp/source/rtp_rtcp_impl.cc b/modules/rtp_rtcp/source/rtp_rtcp_impl.cc
+index cca9a40250..a63067141d 100644
+--- a/modules/rtp_rtcp/source/rtp_rtcp_impl.cc
++++ b/modules/rtp_rtcp/source/rtp_rtcp_impl.cc
+@@ -296,6 +296,7 @@ RTCPSender::FeedbackState ModuleRtpRtcpImpl::GetFeedbackState() {
-+// Until Mozilla builds with Win 10 SDK v10.0.20348.0 or newer, this
-+// code will not build. Once we support the newer SDK, Bug 1868198
-+// exists to decide if we ever want to use this code since it is
-+// removing an indicator that capture is happening.
-+#if !defined(WEBRTC_MOZILLA_BUILD)
- // By default, the WGC capture API adds a yellow border around the captured
- // window or display to indicate that a capture is in progress. The section
- // below is an attempt to remove this yellow border to make the capture
-@@ -199,6 +204,7 @@ HRESULT WgcCaptureSession::StartCapture(const DesktopCaptureOptions& options) {
- &session3))) {
- session3->put_IsBorderRequired(false);
+ int32_t ModuleRtpRtcpImpl::SetSendingStatus(const bool sending) {
+ if (rtcp_sender_.Sending() != sending) {
++ // Sends RTCP BYE when going from true to false
+ rtcp_sender_.SetSendingStatus(GetFeedbackState(), sending);
}
-+#endif
+ return 0;
+diff --git a/modules/rtp_rtcp/source/rtp_rtcp_interface.h b/modules/rtp_rtcp/source/rtp_rtcp_interface.h
+index f196d11b58..bc8da63ab6 100644
+--- a/modules/rtp_rtcp/source/rtp_rtcp_interface.h
++++ b/modules/rtp_rtcp/source/rtp_rtcp_interface.h
+@@ -277,7 +277,7 @@ class RtpRtcpInterface : public RtcpFeedbackSenderInterface {
+ // Returns the FlexFEC SSRC, if there is one.
+ virtual absl::optional<uint32_t> FlexfecSsrc() const = 0;
- allow_zero_hertz_ = options.allow_wgc_zero_hertz();
+- // Sets sending status.
++ // Sets sending status. Sends kRtcpByeCode when going from true to false.
+ // Returns -1 on failure else 0.
+ virtual int32_t SetSendingStatus(bool sending) = 0;
diff --git a/third_party/libwebrtc/moz-patch-stack/0104.patch b/third_party/libwebrtc/moz-patch-stack/0104.patch
index 988f029577..a6da56ee72 100644
--- a/third_party/libwebrtc/moz-patch-stack/0104.patch
+++ b/third_party/libwebrtc/moz-patch-stack/0104.patch
@@ -1,119 +1,33 @@
-From: Jan Grulich <jgrulich@redhat.com>
-Date: Thu, 30 Nov 2023 11:49:00 +0000
-Subject: Bug 1844020 - Add option to DeviceInfo::GetDeviceName() identifying a
- placeholder device r=pehrsons,webrtc-reviewers
+From: Andreas Pehrson <apehrson@mozilla.com>
+Date: Fri, 2 Feb 2024 18:43:00 +0000
+Subject: Bug 1878010 - Fix webrtc::VideoCaptureFactory for BSD.
+ r=grulja,gaston,webrtc-reviewers,mjf
-Adds a new parameter "deviceIsPlaceholder" that will be set to true in
-case the returned device is not a real device but a placeholder that is
-just used to inform about camera device existence.
-
-Differential Revision: https://phabricator.services.mozilla.com/D189929
-Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/ed31b2acb5fbca3e2d0691a64bc52e65952070c0
+Differential Revision: https://phabricator.services.mozilla.com/D200427
+Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/f890637efe5abc0020fab83ff2224313cd0c8460
---
- modules/video_capture/linux/device_info_pipewire.cc | 3 ++-
- modules/video_capture/linux/device_info_pipewire.h | 3 ++-
- modules/video_capture/linux/device_info_v4l2.cc | 3 ++-
- modules/video_capture/linux/device_info_v4l2.h | 3 ++-
- modules/video_capture/video_capture.h | 3 ++-
- modules/video_capture/windows/device_info_ds.cc | 3 ++-
- modules/video_capture/windows/device_info_ds.h | 3 ++-
- 7 files changed, 14 insertions(+), 7 deletions(-)
+ modules/video_capture/video_capture_factory.cc | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
-diff --git a/modules/video_capture/linux/device_info_pipewire.cc b/modules/video_capture/linux/device_info_pipewire.cc
-index 2cb6161514..71dcec4da9 100644
---- a/modules/video_capture/linux/device_info_pipewire.cc
-+++ b/modules/video_capture/linux/device_info_pipewire.cc
-@@ -48,7 +48,8 @@ int32_t DeviceInfoPipeWire::GetDeviceName(uint32_t deviceNumber,
- uint32_t deviceUniqueIdUTF8Length,
- char* productUniqueIdUTF8,
- uint32_t productUniqueIdUTF8Length,
-- pid_t* pid) {
-+ pid_t* pid,
-+ bool* deviceIsPlaceholder) {
- if (deviceNumber >= NumberOfDevices())
- return -1;
-
-diff --git a/modules/video_capture/linux/device_info_pipewire.h b/modules/video_capture/linux/device_info_pipewire.h
-index 724717be5e..1a1324e92b 100644
---- a/modules/video_capture/linux/device_info_pipewire.h
-+++ b/modules/video_capture/linux/device_info_pipewire.h
-@@ -30,7 +30,8 @@ class DeviceInfoPipeWire : public DeviceInfoImpl {
- uint32_t deviceUniqueIdUTF8Length,
- char* productUniqueIdUTF8 = nullptr,
- uint32_t productUniqueIdUTF8Length = 0,
-- pid_t* pid = 0) override;
-+ pid_t* pid = 0,
-+ bool* deviceIsPlaceholder = 0) override;
- /*
- * Fills the membervariable _captureCapabilities with capabilites for the
- * given device name.
-diff --git a/modules/video_capture/linux/device_info_v4l2.cc b/modules/video_capture/linux/device_info_v4l2.cc
-index abd2886f85..eaeed26b7c 100644
---- a/modules/video_capture/linux/device_info_v4l2.cc
-+++ b/modules/video_capture/linux/device_info_v4l2.cc
-@@ -250,7 +250,8 @@ int32_t DeviceInfoV4l2::GetDeviceName(uint32_t deviceNumber,
- uint32_t deviceUniqueIdUTF8Length,
- char* /*productUniqueIdUTF8*/,
- uint32_t /*productUniqueIdUTF8Length*/,
-- pid_t* /*pid*/) {
-+ pid_t* /*pid*/,
-+ bool* /*deviceIsPlaceholder*/) {
- // Travel through /dev/video [0-63]
- uint32_t count = 0;
- char device[20];
-diff --git a/modules/video_capture/linux/device_info_v4l2.h b/modules/video_capture/linux/device_info_v4l2.h
-index 0bec3eb765..55415845ad 100644
---- a/modules/video_capture/linux/device_info_v4l2.h
-+++ b/modules/video_capture/linux/device_info_v4l2.h
-@@ -36,7 +36,8 @@ class DeviceInfoV4l2 : public DeviceInfoImpl {
- uint32_t deviceUniqueIdUTF8Length,
- char* productUniqueIdUTF8 = 0,
- uint32_t productUniqueIdUTF8Length = 0,
-- pid_t* pid=0) override;
-+ pid_t* pid = 0,
-+ bool* deviceIsPlaceholder = 0) override;
- /*
- * Fills the membervariable _captureCapabilities with capabilites for the
- * given device name.
-diff --git a/modules/video_capture/video_capture.h b/modules/video_capture/video_capture.h
-index 6614032299..378a53b4d2 100644
---- a/modules/video_capture/video_capture.h
-+++ b/modules/video_capture/video_capture.h
-@@ -74,7 +74,8 @@ class VideoCaptureModule : public rtc::RefCountInterface {
- uint32_t deviceUniqueIdUTF8Length,
- char* productUniqueIdUTF8 = 0,
- uint32_t productUniqueIdUTF8Length = 0,
-- pid_t* pid = 0) = 0;
-+ pid_t* pid = 0,
-+ bool* deviceIsPlaceholder = 0) = 0;
-
- // Returns the number of capabilities this device.
- virtual int32_t NumberOfCapabilities(const char* deviceUniqueIdUTF8) = 0;
-diff --git a/modules/video_capture/windows/device_info_ds.cc b/modules/video_capture/windows/device_info_ds.cc
-index f6927281f3..8ca741239c 100644
---- a/modules/video_capture/windows/device_info_ds.cc
-+++ b/modules/video_capture/windows/device_info_ds.cc
-@@ -173,7 +173,8 @@ int32_t DeviceInfoDS::GetDeviceName(uint32_t deviceNumber,
- uint32_t deviceUniqueIdUTF8Length,
- char* productUniqueIdUTF8,
- uint32_t productUniqueIdUTF8Length,
-- pid_t* pid) {
-+ pid_t* pid,
-+ bool* deviceIsPlaceholder) {
- MutexLock lock(&_apiLock);
- const int32_t result = GetDeviceInfo(
- deviceNumber, deviceNameUTF8, deviceNameLength, deviceUniqueIdUTF8,
-diff --git a/modules/video_capture/windows/device_info_ds.h b/modules/video_capture/windows/device_info_ds.h
-index e6dfaed366..a9a1449b99 100644
---- a/modules/video_capture/windows/device_info_ds.h
-+++ b/modules/video_capture/windows/device_info_ds.h
-@@ -51,7 +51,8 @@ class DeviceInfoDS : public DeviceInfoImpl {
- uint32_t deviceUniqueIdUTF8Length,
- char* productUniqueIdUTF8,
- uint32_t productUniqueIdUTF8Length,
-- pid_t* pid) override;
-+ pid_t* pid,
-+ bool* deviceIsPlaceholder) override;
-
- /*
- * Display OS /capture device specific settings dialog
+diff --git a/modules/video_capture/video_capture_factory.cc b/modules/video_capture/video_capture_factory.cc
+index e085ac2df8..2790fbbe1c 100644
+--- a/modules/video_capture/video_capture_factory.cc
++++ b/modules/video_capture/video_capture_factory.cc
+@@ -24,7 +24,7 @@ rtc::scoped_refptr<VideoCaptureModule> VideoCaptureFactory::Create(
+ const char* deviceUniqueIdUTF8) {
+ // This is only implemented on pure Linux and WEBRTC_LINUX is defined for
+ // Android as well
+-#if !defined(WEBRTC_LINUX) || defined(WEBRTC_ANDROID)
++#if (!defined(WEBRTC_LINUX) && !defined(WEBRTC_BSD)) || defined(WEBRTC_ANDROID)
+ return nullptr;
+ #else
+ return videocapturemodule::VideoCaptureImpl::Create(options,
+@@ -40,7 +40,7 @@ VideoCaptureModule::DeviceInfo* VideoCaptureFactory::CreateDeviceInfo(
+ VideoCaptureOptions* options) {
+ // This is only implemented on pure Linux and WEBRTC_LINUX is defined for
+ // Android as well
+-#if !defined(WEBRTC_LINUX) || defined(WEBRTC_ANDROID)
++#if (!defined(WEBRTC_LINUX) && !defined(WEBRTC_BSD)) || defined(WEBRTC_ANDROID)
+ return nullptr;
+ #else
+ return videocapturemodule::VideoCaptureImpl::CreateDeviceInfo(options);
diff --git a/third_party/libwebrtc/moz-patch-stack/0105.patch b/third_party/libwebrtc/moz-patch-stack/0105.patch
index f2aacdcc7f..fd05008507 100644
--- a/third_party/libwebrtc/moz-patch-stack/0105.patch
+++ b/third_party/libwebrtc/moz-patch-stack/0105.patch
@@ -1,35 +1,68 @@
-From: Chun-Min Chang <chun.m.chang@gmail.com>
-Date: Tue, 5 Dec 2023 20:08:00 +0000
-Subject: Bug 1864008 - Move libvpx to libgkcodecs, part 2
- r=webrtc-reviewers,padenot,mjf
+From: Jan Grulich <jgrulich@redhat.com>
+Date: Fri, 2 Feb 2024 11:47:00 +0000
+Subject: Bug 1876896 - WebRTC backport: Allow VideoCaptureModulePipeWire to be
+ shared with more consumers r=pehrsons,webrtc-reviewers
-This patch addes trampoline headers for libvpx.
+This is a simple backport of an WebRTC upstream change.
-To follow the libwebrtc merge procedure, the vpx headers are silently
-replaced with "trampoline" headers, which do nothing but include real
-VPX headers. This makes the libwebrtc-merge process easier without
-worrying headers' paths.
+Upstream commit: 958c9ac546f33716d097b5092515dcac705151d3
-On the other hand, the `rtc_build_libvpx` is set to `true` in
-webrtc.gni, so moz.build file for third_party/libvpx can be generated by
-the gn_processor in the next patch.
-
-Differential Revision: https://phabricator.services.mozilla.com/D195495
-Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/d43978d3d8356e176fac2ad18f328871f36698ce
+Differential Revision: https://phabricator.services.mozilla.com/D200142
+Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/2ba2ef65280b2e6f246fed24d6986718981744f5
---
- webrtc.gni | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
+ .../video_capture/linux/video_capture_pipewire.cc | 15 ++++++++++++++-
+ .../video_capture/linux/video_capture_pipewire.h | 1 +
+ 2 files changed, 15 insertions(+), 1 deletion(-)
-diff --git a/webrtc.gni b/webrtc.gni
-index 931a0a24e5..d763691759 100644
---- a/webrtc.gni
-+++ b/webrtc.gni
-@@ -286,7 +286,7 @@ declare_args() {
- # Disable these to not build components which can be externally provided.
- rtc_build_json = !build_with_mozilla
- rtc_build_libsrtp = !build_with_mozilla
-- rtc_build_libvpx = !build_with_mozilla
-+ rtc_build_libvpx = true
- rtc_libvpx_build_vp9 = true
- rtc_build_opus = !build_with_mozilla
- rtc_build_ssl = !build_with_mozilla
+diff --git a/modules/video_capture/linux/video_capture_pipewire.cc b/modules/video_capture/linux/video_capture_pipewire.cc
+index 9d47e3ddbf..fb813e331f 100644
+--- a/modules/video_capture/linux/video_capture_pipewire.cc
++++ b/modules/video_capture/linux/video_capture_pipewire.cc
+@@ -48,7 +48,10 @@ VideoType VideoCaptureModulePipeWire::PipeWireRawFormatToVideoType(
+
+ VideoCaptureModulePipeWire::VideoCaptureModulePipeWire(
+ VideoCaptureOptions* options)
+- : VideoCaptureImpl(), session_(options->pipewire_session()) {}
++ : VideoCaptureImpl(),
++ session_(options->pipewire_session()),
++ initialized_(false),
++ started_(false) {}
+
+ VideoCaptureModulePipeWire::~VideoCaptureModulePipeWire() {
+ RTC_DCHECK_RUN_ON(&api_checker_);
+@@ -121,6 +124,14 @@ int32_t VideoCaptureModulePipeWire::StartCapture(
+ RTC_CHECK_RUNS_SERIALIZED(&capture_checker_);
+ RTC_DCHECK_RUN_ON(&api_checker_);
+
++ if (initialized_) {
++ if (capability == _requestedCapability) {
++ return 0;
++ } else {
++ StopCapture();
++ }
++ }
++
+ uint8_t buffer[1024] = {};
+
+ RTC_LOG(LS_VERBOSE) << "Creating new PipeWire stream for node " << node_id_;
+@@ -171,6 +182,8 @@ int32_t VideoCaptureModulePipeWire::StartCapture(
+ }
+
+ _requestedCapability = capability;
++ initialized_ = true;
++
+ return 0;
+ }
+
+diff --git a/modules/video_capture/linux/video_capture_pipewire.h b/modules/video_capture/linux/video_capture_pipewire.h
+index 620ee520ca..5d6794ed65 100644
+--- a/modules/video_capture/linux/video_capture_pipewire.h
++++ b/modules/video_capture/linux/video_capture_pipewire.h
+@@ -50,6 +50,7 @@ class VideoCaptureModulePipeWire : public VideoCaptureImpl {
+ int node_id_ RTC_GUARDED_BY(capture_checker_);
+ VideoCaptureCapability configured_capability_
+ RTC_GUARDED_BY(pipewire_checker_);
++ bool initialized_ RTC_GUARDED_BY(capture_checker_);
+ bool started_ RTC_GUARDED_BY(api_lock_);
+
+ struct pw_stream* stream_ RTC_GUARDED_BY(pipewire_checker_) = nullptr;
diff --git a/third_party/libwebrtc/moz-patch-stack/0106.patch b/third_party/libwebrtc/moz-patch-stack/0106.patch
index d232dcb897..1db996311f 100644
--- a/third_party/libwebrtc/moz-patch-stack/0106.patch
+++ b/third_party/libwebrtc/moz-patch-stack/0106.patch
@@ -1,107 +1,127 @@
-From: Michael Froman <mfroman@mozilla.com>
-Date: Mon, 18 Dec 2023 15:00:00 +0000
-Subject: Bug 1867099 - revert libwebrtc 8602f604e0. r=bwc
+From: Jan Grulich <jgrulich@redhat.com>
+Date: Tue, 13 Feb 2024 13:12:00 +0000
+Subject: Bug 1879752 - WebRTC backport: Video capture PipeWire - simplify
+ thread and lock annotations r=pehrsons,webrtc-reviewers
-Upstream 8602f604e0 removed code sending BYEs which breaks some of
-our wpt. They've opened a bug for a real fix here:
-https://bugs.chromium.org/p/webrtc/issues/detail?id=15664
+This is a simple backport of an WebRTC upstream change.
-I've opened Bug 1870643 to track the real fix and upstream bug.
+Upstream commit: 541f202354e2b4906935c8db6e54386aa8bc8d1f
-Differential Revision: https://phabricator.services.mozilla.com/D196729
-Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/d92a578327f524ec3e1c144c82492a4c76b8266f
+Differential Revision: https://phabricator.services.mozilla.com/D201328
+Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/dc12e6eecaa8dc91ee0a517cfd27570691c441b9
---
- call/rtp_video_sender.cc | 1 +
- modules/rtp_rtcp/source/rtcp_sender.cc | 19 +++++++++++++++++--
- .../rtp_rtcp/source/rtcp_sender_unittest.cc | 5 +++--
- modules/rtp_rtcp/source/rtp_rtcp_impl.cc | 1 +
- modules/rtp_rtcp/source/rtp_rtcp_interface.h | 2 +-
- 5 files changed, 23 insertions(+), 5 deletions(-)
+ .../linux/video_capture_pipewire.cc | 25 ++++++++++++-------
+ .../linux/video_capture_pipewire.h | 14 +++++------
+ 2 files changed, 22 insertions(+), 17 deletions(-)
-diff --git a/call/rtp_video_sender.cc b/call/rtp_video_sender.cc
-index 1ace08fa32..4d99c61bb4 100644
---- a/call/rtp_video_sender.cc
-+++ b/call/rtp_video_sender.cc
-@@ -510,6 +510,7 @@ void RtpVideoSender::SetActiveModulesLocked(
- const bool was_active = rtp_module.Sending();
- const bool should_be_active = active_modules[i];
-
-+ // Sends a kRtcpByeCode when going from true to false.
- rtp_module.SetSendingStatus(active_modules[i]);
-
- if (was_active && !should_be_active) {
-diff --git a/modules/rtp_rtcp/source/rtcp_sender.cc b/modules/rtp_rtcp/source/rtcp_sender.cc
-index 099b0be1a3..971f49b949 100644
---- a/modules/rtp_rtcp/source/rtcp_sender.cc
-+++ b/modules/rtp_rtcp/source/rtcp_sender.cc
-@@ -212,8 +212,23 @@ bool RTCPSender::Sending() const {
-
- void RTCPSender::SetSendingStatus(const FeedbackState& feedback_state,
- bool sending) {
-- MutexLock lock(&mutex_rtcp_sender_);
-- sending_ = sending;
-+ bool sendRTCPBye = false;
-+ {
-+ MutexLock lock(&mutex_rtcp_sender_);
+diff --git a/modules/video_capture/linux/video_capture_pipewire.cc b/modules/video_capture/linux/video_capture_pipewire.cc
+index fb813e331f..8af483636a 100644
+--- a/modules/video_capture/linux/video_capture_pipewire.cc
++++ b/modules/video_capture/linux/video_capture_pipewire.cc
+@@ -121,7 +121,6 @@ static spa_pod* BuildFormat(spa_pod_builder* builder,
+
+ int32_t VideoCaptureModulePipeWire::StartCapture(
+ const VideoCaptureCapability& capability) {
+- RTC_CHECK_RUNS_SERIALIZED(&capture_checker_);
+ RTC_DCHECK_RUN_ON(&api_checker_);
+
+ if (initialized_) {
+@@ -134,10 +133,17 @@ int32_t VideoCaptureModulePipeWire::StartCapture(
+
+ uint8_t buffer[1024] = {};
+
++ // We don't want members above to be guarded by capture_checker_ as
++ // it's meant to be for members that are accessed on the API thread
++ // only when we are not capturing. The code above can be called many
++ // times while sharing instance of VideoCapturePipeWire between
++ // websites and therefore it would not follow the requirements of this
++ // checker.
++ RTC_CHECK_RUNS_SERIALIZED(&capture_checker_);
++ PipeWireThreadLoopLock thread_loop_lock(session_->pw_main_loop_);
+
-+ if (method_ != RtcpMode::kOff) {
-+ if (sending == false && sending_ == true) {
-+ // Trigger RTCP bye
-+ sendRTCPBye = true;
-+ }
-+ }
-+ sending_ = sending;
-+ }
-+ if (sendRTCPBye) {
-+ if (SendRTCP(feedback_state, kRtcpBye) != 0) {
-+ RTC_LOG(LS_WARNING) << "Failed to send RTCP BYE";
-+ }
-+ }
+ RTC_LOG(LS_VERBOSE) << "Creating new PipeWire stream for node " << node_id_;
+
+- PipeWireThreadLoopLock thread_loop_lock(session_->pw_main_loop_);
+- RTC_CHECK_RUNS_SERIALIZED(&pipewire_checker_);
+ pw_properties* reuse_props =
+ pw_properties_new_string("pipewire.client.reuse=1");
+ stream_ = pw_stream_new(session_->pw_core_, "camera-stream", reuse_props);
+@@ -188,11 +194,13 @@ int32_t VideoCaptureModulePipeWire::StartCapture(
}
- void RTCPSender::SetNonSenderRttMeasurement(bool enabled) {
-diff --git a/modules/rtp_rtcp/source/rtcp_sender_unittest.cc b/modules/rtp_rtcp/source/rtcp_sender_unittest.cc
-index 002a5f86f1..1dcb628722 100644
---- a/modules/rtp_rtcp/source/rtcp_sender_unittest.cc
-+++ b/modules/rtp_rtcp/source/rtcp_sender_unittest.cc
-@@ -328,12 +328,13 @@ TEST_F(RtcpSenderTest, SendBye) {
- EXPECT_EQ(kSenderSsrc, parser()->bye()->sender_ssrc());
+ int32_t VideoCaptureModulePipeWire::StopCapture() {
+- RTC_CHECK_RUNS_SERIALIZED(&capture_checker_);
+ RTC_DCHECK_RUN_ON(&api_checker_);
+
+ PipeWireThreadLoopLock thread_loop_lock(session_->pw_main_loop_);
+- RTC_CHECK_RUNS_SERIALIZED(&pipewire_checker_);
++ // PipeWireSession is guarded by API checker so just make sure we do
++ // race detection when the PipeWire loop is locked/stopped to not run
++ // any callback at this point.
++ RTC_CHECK_RUNS_SERIALIZED(&capture_checker_);
+ if (stream_) {
+ pw_stream_destroy(stream_);
+ stream_ = nullptr;
+@@ -225,14 +233,14 @@ void VideoCaptureModulePipeWire::OnStreamParamChanged(
+ VideoCaptureModulePipeWire* that =
+ static_cast<VideoCaptureModulePipeWire*>(data);
+ RTC_DCHECK(that);
+- RTC_CHECK_RUNS_SERIALIZED(&that->pipewire_checker_);
++ RTC_CHECK_RUNS_SERIALIZED(&that->capture_checker_);
+
+ if (format && id == SPA_PARAM_Format)
+ that->OnFormatChanged(format);
}
--TEST_F(RtcpSenderTest, StopSendingDoesNotTriggersBye) {
-+TEST_F(RtcpSenderTest, StopSendingTriggersBye) {
- auto rtcp_sender = CreateRtcpSender(GetDefaultConfig());
- rtcp_sender->SetRTCPStatus(RtcpMode::kReducedSize);
- rtcp_sender->SetSendingStatus(feedback_state(), true);
- rtcp_sender->SetSendingStatus(feedback_state(), false);
-- EXPECT_EQ(0, parser()->bye()->num_packets());
-+ EXPECT_EQ(1, parser()->bye()->num_packets());
-+ EXPECT_EQ(kSenderSsrc, parser()->bye()->sender_ssrc());
+ void VideoCaptureModulePipeWire::OnFormatChanged(const struct spa_pod* format) {
+- RTC_CHECK_RUNS_SERIALIZED(&pipewire_checker_);
++ RTC_CHECK_RUNS_SERIALIZED(&capture_checker_);
+
+ uint32_t media_type, media_subtype;
+
+@@ -331,7 +339,6 @@ void VideoCaptureModulePipeWire::OnStreamStateChanged(
+ VideoCaptureModulePipeWire* that =
+ static_cast<VideoCaptureModulePipeWire*>(data);
+ RTC_DCHECK(that);
+- RTC_CHECK_RUNS_SERIALIZED(&that->capture_checker_);
+
+ MutexLock lock(&that->api_lock_);
+ switch (state) {
+@@ -374,7 +381,7 @@ static VideoRotation VideorotationFromPipeWireTransform(uint32_t transform) {
}
- TEST_F(RtcpSenderTest, SendFir) {
-diff --git a/modules/rtp_rtcp/source/rtp_rtcp_impl.cc b/modules/rtp_rtcp/source/rtp_rtcp_impl.cc
-index cca9a40250..a63067141d 100644
---- a/modules/rtp_rtcp/source/rtp_rtcp_impl.cc
-+++ b/modules/rtp_rtcp/source/rtp_rtcp_impl.cc
-@@ -296,6 +296,7 @@ RTCPSender::FeedbackState ModuleRtpRtcpImpl::GetFeedbackState() {
-
- int32_t ModuleRtpRtcpImpl::SetSendingStatus(const bool sending) {
- if (rtcp_sender_.Sending() != sending) {
-+ // Sends RTCP BYE when going from true to false
- rtcp_sender_.SetSendingStatus(GetFeedbackState(), sending);
- }
- return 0;
-diff --git a/modules/rtp_rtcp/source/rtp_rtcp_interface.h b/modules/rtp_rtcp/source/rtp_rtcp_interface.h
-index f196d11b58..bc8da63ab6 100644
---- a/modules/rtp_rtcp/source/rtp_rtcp_interface.h
-+++ b/modules/rtp_rtcp/source/rtp_rtcp_interface.h
-@@ -277,7 +277,7 @@ class RtpRtcpInterface : public RtcpFeedbackSenderInterface {
- // Returns the FlexFEC SSRC, if there is one.
- virtual absl::optional<uint32_t> FlexfecSsrc() const = 0;
-
-- // Sets sending status.
-+ // Sets sending status. Sends kRtcpByeCode when going from true to false.
- // Returns -1 on failure else 0.
- virtual int32_t SetSendingStatus(bool sending) = 0;
+ void VideoCaptureModulePipeWire::ProcessBuffers() {
+- RTC_CHECK_RUNS_SERIALIZED(&pipewire_checker_);
++ RTC_CHECK_RUNS_SERIALIZED(&capture_checker_);
+
+ while (pw_buffer* buffer = pw_stream_dequeue_buffer(stream_)) {
+ struct spa_meta_header* h;
+diff --git a/modules/video_capture/linux/video_capture_pipewire.h b/modules/video_capture/linux/video_capture_pipewire.h
+index 5d6794ed65..eeb3b9497c 100644
+--- a/modules/video_capture/linux/video_capture_pipewire.h
++++ b/modules/video_capture/linux/video_capture_pipewire.h
+@@ -43,18 +43,16 @@ class VideoCaptureModulePipeWire : public VideoCaptureImpl {
+ void OnFormatChanged(const struct spa_pod* format);
+ void ProcessBuffers();
+
+- rtc::RaceChecker pipewire_checker_;
+-
+ const rtc::scoped_refptr<PipeWireSession> session_
+- RTC_GUARDED_BY(capture_checker_);
++ RTC_GUARDED_BY(api_checker_);
++ bool initialized_ RTC_GUARDED_BY(api_checker_);
++ bool started_ RTC_GUARDED_BY(api_lock_);
+ int node_id_ RTC_GUARDED_BY(capture_checker_);
+ VideoCaptureCapability configured_capability_
+- RTC_GUARDED_BY(pipewire_checker_);
+- bool initialized_ RTC_GUARDED_BY(capture_checker_);
+- bool started_ RTC_GUARDED_BY(api_lock_);
++ RTC_GUARDED_BY(capture_checker_);
+- struct pw_stream* stream_ RTC_GUARDED_BY(pipewire_checker_) = nullptr;
+- struct spa_hook stream_listener_ RTC_GUARDED_BY(pipewire_checker_);
++ struct pw_stream* stream_ RTC_GUARDED_BY(capture_checker_) = nullptr;
++ struct spa_hook stream_listener_ RTC_GUARDED_BY(capture_checker_);
+ };
+ } // namespace videocapturemodule
+ } // namespace webrtc
diff --git a/third_party/libwebrtc/moz-patch-stack/058bfe3ae3.no-op-cherry-pick-msg b/third_party/libwebrtc/moz-patch-stack/058bfe3ae3.no-op-cherry-pick-msg
new file mode 100644
index 0000000000..40b3e5620d
--- /dev/null
+++ b/third_party/libwebrtc/moz-patch-stack/058bfe3ae3.no-op-cherry-pick-msg
@@ -0,0 +1 @@
+We cherry-picked this in bug 1615282.
diff --git a/third_party/libwebrtc/moz-patch-stack/334e9133dc.no-op-cherry-pick-msg b/third_party/libwebrtc/moz-patch-stack/334e9133dc.no-op-cherry-pick-msg
new file mode 100644
index 0000000000..28f8bcdc9d
--- /dev/null
+++ b/third_party/libwebrtc/moz-patch-stack/334e9133dc.no-op-cherry-pick-msg
@@ -0,0 +1 @@
+We cherry-picked this in bug 1876895.
diff --git a/third_party/libwebrtc/moz-patch-stack/a9d497b52d.no-op-cherry-pick-msg b/third_party/libwebrtc/moz-patch-stack/a9d497b52d.no-op-cherry-pick-msg
deleted file mode 100644
index ab6be0cf31..0000000000
--- a/third_party/libwebrtc/moz-patch-stack/a9d497b52d.no-op-cherry-pick-msg
+++ /dev/null
@@ -1 +0,0 @@
-We cherry-picked this in bug 1865872
diff --git a/third_party/libwebrtc/moz.build b/third_party/libwebrtc/moz.build
index ae994ffc67..ad1adce757 100644
--- a/third_party/libwebrtc/moz.build
+++ b/third_party/libwebrtc/moz.build
@@ -42,6 +42,7 @@ DIRS += [
"/third_party/libwebrtc/api/crypto/frame_decryptor_interface_gn",
"/third_party/libwebrtc/api/crypto/frame_encryptor_interface_gn",
"/third_party/libwebrtc/api/crypto/options_gn",
+ "/third_party/libwebrtc/api/environment/environment_gn",
"/third_party/libwebrtc/api/fec_controller_api_gn",
"/third_party/libwebrtc/api/field_trials_registry_gn",
"/third_party/libwebrtc/api/field_trials_view_gn",
@@ -59,6 +60,7 @@ DIRS += [
"/third_party/libwebrtc/api/neteq/tick_timer_gn",
"/third_party/libwebrtc/api/network_state_predictor_api_gn",
"/third_party/libwebrtc/api/priority_gn",
+ "/third_party/libwebrtc/api/ref_count_gn",
"/third_party/libwebrtc/api/refcountedbase_gn",
"/third_party/libwebrtc/api/rtc_error_gn",
"/third_party/libwebrtc/api/rtc_event_log/rtc_event_log_gn",
@@ -309,7 +311,6 @@ DIRS += [
"/third_party/libwebrtc/modules/video_coding/webrtc_vp9_helpers_gn",
"/third_party/libwebrtc/rtc_base/async_dns_resolver_gn",
"/third_party/libwebrtc/rtc_base/async_packet_socket_gn",
- "/third_party/libwebrtc/rtc_base/async_resolver_interface_gn",
"/third_party/libwebrtc/rtc_base/audio_format_to_string_gn",
"/third_party/libwebrtc/rtc_base/bit_buffer_gn",
"/third_party/libwebrtc/rtc_base/bitrate_tracker_gn",
diff --git a/third_party/libwebrtc/net/dcsctp/common/BUILD.gn b/third_party/libwebrtc/net/dcsctp/common/BUILD.gn
index 78fa0d307e..d496c64a56 100644
--- a/third_party/libwebrtc/net/dcsctp/common/BUILD.gn
+++ b/third_party/libwebrtc/net/dcsctp/common/BUILD.gn
@@ -29,12 +29,6 @@ rtc_source_set("sequence_numbers") {
sources = [ "sequence_numbers.h" ]
}
-rtc_source_set("str_join") {
- deps = [ "../../../rtc_base:stringutils" ]
- sources = [ "str_join.h" ]
- absl_deps = [ "//third_party/abseil-cpp/absl/strings" ]
-}
-
if (rtc_include_tests) {
rtc_library("dcsctp_common_unittests") {
testonly = true
@@ -43,7 +37,6 @@ if (rtc_include_tests) {
deps = [
":math",
":sequence_numbers",
- ":str_join",
"../../../api:array_view",
"../../../rtc_base:checks",
"../../../rtc_base:gunit_helpers",
@@ -52,7 +45,6 @@ if (rtc_include_tests) {
sources = [
"math_test.cc",
"sequence_numbers_test.cc",
- "str_join_test.cc",
]
}
}
diff --git a/third_party/libwebrtc/net/dcsctp/fuzzers/dcsctp_fuzzers.h b/third_party/libwebrtc/net/dcsctp/fuzzers/dcsctp_fuzzers.h
index 90cfa35099..49aa7f0430 100644
--- a/third_party/libwebrtc/net/dcsctp/fuzzers/dcsctp_fuzzers.h
+++ b/third_party/libwebrtc/net/dcsctp/fuzzers/dcsctp_fuzzers.h
@@ -64,7 +64,7 @@ class FuzzerCallbacks : public DcSctpSocketCallbacks {
// The fuzzer timeouts don't implement |precision|.
return std::make_unique<FuzzerTimeout>(active_timeouts_);
}
- TimeMs TimeMillis() override { return TimeMs(42); }
+ webrtc::Timestamp Now() override { return webrtc::Timestamp::Millis(42); }
uint32_t GetRandomInt(uint32_t low, uint32_t high) override {
return kRandomValue;
}
diff --git a/third_party/libwebrtc/net/dcsctp/packet/BUILD.gn b/third_party/libwebrtc/net/dcsctp/packet/BUILD.gn
index 7abccc004b..a0c9d8d4df 100644
--- a/third_party/libwebrtc/net/dcsctp/packet/BUILD.gn
+++ b/third_party/libwebrtc/net/dcsctp/packet/BUILD.gn
@@ -72,7 +72,6 @@ rtc_library("parameter") {
"../../../rtc_base:stringutils",
"../common:internal_types",
"../common:math",
- "../common:str_join",
"../public:types",
]
sources = [
@@ -120,7 +119,6 @@ rtc_library("error_cause") {
"../../../rtc_base:stringutils",
"../common:internal_types",
"../common:math",
- "../common:str_join",
"../packet:bounded_io",
"../public:types",
]
@@ -172,7 +170,6 @@ rtc_library("chunk") {
"../../../rtc_base:logging",
"../../../rtc_base:stringutils",
"../common:math",
- "../common:str_join",
"../packet:bounded_io",
]
sources = [
diff --git a/third_party/libwebrtc/net/dcsctp/packet/chunk/sack_chunk.cc b/third_party/libwebrtc/net/dcsctp/packet/chunk/sack_chunk.cc
index d80e430082..179f7ea379 100644
--- a/third_party/libwebrtc/net/dcsctp/packet/chunk/sack_chunk.cc
+++ b/third_party/libwebrtc/net/dcsctp/packet/chunk/sack_chunk.cc
@@ -18,11 +18,11 @@
#include "absl/types/optional.h"
#include "api/array_view.h"
-#include "net/dcsctp/common/str_join.h"
#include "net/dcsctp/packet/bounded_byte_reader.h"
#include "net/dcsctp/packet/bounded_byte_writer.h"
#include "net/dcsctp/packet/tlv_trait.h"
#include "rtc_base/logging.h"
+#include "rtc_base/strings/str_join.h"
#include "rtc_base/strings/string_builder.h"
namespace dcsctp {
diff --git a/third_party/libwebrtc/net/dcsctp/packet/error_cause/missing_mandatory_parameter_cause.cc b/third_party/libwebrtc/net/dcsctp/packet/error_cause/missing_mandatory_parameter_cause.cc
index b89f86e43e..679439d4c2 100644
--- a/third_party/libwebrtc/net/dcsctp/packet/error_cause/missing_mandatory_parameter_cause.cc
+++ b/third_party/libwebrtc/net/dcsctp/packet/error_cause/missing_mandatory_parameter_cause.cc
@@ -18,11 +18,11 @@
#include "absl/types/optional.h"
#include "api/array_view.h"
-#include "net/dcsctp/common/str_join.h"
#include "net/dcsctp/packet/bounded_byte_reader.h"
#include "net/dcsctp/packet/bounded_byte_writer.h"
#include "net/dcsctp/packet/tlv_trait.h"
#include "rtc_base/logging.h"
+#include "rtc_base/strings/str_join.h"
#include "rtc_base/strings/string_builder.h"
namespace dcsctp {
@@ -83,7 +83,7 @@ void MissingMandatoryParameterCause::SerializeTo(
std::string MissingMandatoryParameterCause::ToString() const {
rtc::StringBuilder sb;
sb << "Missing Mandatory Parameter, missing_parameter_types="
- << StrJoin(missing_parameter_types_, ",");
+ << webrtc::StrJoin(missing_parameter_types_, ",");
return sb.Release();
}
diff --git a/third_party/libwebrtc/net/dcsctp/packet/parameter/supported_extensions_parameter.cc b/third_party/libwebrtc/net/dcsctp/packet/parameter/supported_extensions_parameter.cc
index 6a8fb214de..87a5bd9b52 100644
--- a/third_party/libwebrtc/net/dcsctp/packet/parameter/supported_extensions_parameter.cc
+++ b/third_party/libwebrtc/net/dcsctp/packet/parameter/supported_extensions_parameter.cc
@@ -16,10 +16,10 @@
#include "absl/types/optional.h"
#include "api/array_view.h"
-#include "net/dcsctp/common/str_join.h"
#include "net/dcsctp/packet/bounded_byte_reader.h"
#include "net/dcsctp/packet/bounded_byte_writer.h"
#include "net/dcsctp/packet/tlv_trait.h"
+#include "rtc_base/strings/str_join.h"
#include "rtc_base/strings/string_builder.h"
namespace dcsctp {
@@ -59,7 +59,7 @@ void SupportedExtensionsParameter::SerializeTo(
std::string SupportedExtensionsParameter::ToString() const {
rtc::StringBuilder sb;
- sb << "Supported Extensions (" << StrJoin(chunk_types_, ", ") << ")";
+ sb << "Supported Extensions (" << webrtc::StrJoin(chunk_types_, ", ") << ")";
return sb.Release();
}
} // namespace dcsctp
diff --git a/third_party/libwebrtc/net/dcsctp/public/BUILD.gn b/third_party/libwebrtc/net/dcsctp/public/BUILD.gn
index 6cb289bf5b..8af0fd88c4 100644
--- a/third_party/libwebrtc/net/dcsctp/public/BUILD.gn
+++ b/third_party/libwebrtc/net/dcsctp/public/BUILD.gn
@@ -11,6 +11,7 @@ import("../../../webrtc.gni")
rtc_source_set("types") {
deps = [
"../../../api:array_view",
+ "../../../api/units:time_delta",
"../../../rtc_base:strong_alias",
]
sources = [
@@ -26,6 +27,7 @@ rtc_source_set("socket") {
":types",
"../../../api:array_view",
"../../../api/task_queue:task_queue",
+ "../../../api/units:timestamp",
"../../../rtc_base:checks",
"../../../rtc_base:strong_alias",
]
diff --git a/third_party/libwebrtc/net/dcsctp/public/dcsctp_socket.h b/third_party/libwebrtc/net/dcsctp/public/dcsctp_socket.h
index 3cfb8052f8..d0a81eaeb2 100644
--- a/third_party/libwebrtc/net/dcsctp/public/dcsctp_socket.h
+++ b/third_party/libwebrtc/net/dcsctp/public/dcsctp_socket.h
@@ -18,6 +18,7 @@
#include "absl/types/optional.h"
#include "api/array_view.h"
#include "api/task_queue/task_queue_base.h"
+#include "api/units/timestamp.h"
#include "net/dcsctp/public/dcsctp_handover_state.h"
#include "net/dcsctp/public/dcsctp_message.h"
#include "net/dcsctp/public/dcsctp_options.h"
@@ -323,9 +324,21 @@ class DcSctpSocketCallbacks {
// Returns the current time in milliseconds (from any epoch).
//
+ // TODO(bugs.webrtc.org/15593): This method is deprecated, see `Now`.
+ //
// Note that it's NOT ALLOWED to call into this library from within this
// callback.
- virtual TimeMs TimeMillis() = 0;
+ virtual TimeMs TimeMillis() { return TimeMs(0); }
+
+ // Returns the current time (from any epoch).
+ //
+ // This callback will eventually replace `TimeMillis()`.
+ //
+ // Note that it's NOT ALLOWED to call into this library from within this
+ // callback.
+ virtual webrtc::Timestamp Now() {
+ return webrtc::Timestamp::Millis(*TimeMillis());
+ }
// Called when the library needs a random number uniformly distributed between
// `low` (inclusive) and `high` (exclusive). The random numbers used by the
diff --git a/third_party/libwebrtc/net/dcsctp/public/types.h b/third_party/libwebrtc/net/dcsctp/public/types.h
index 7d69875d1a..02e2ce1e5e 100644
--- a/third_party/libwebrtc/net/dcsctp/public/types.h
+++ b/third_party/libwebrtc/net/dcsctp/public/types.h
@@ -14,6 +14,7 @@
#include <cstdint>
#include <limits>
+#include "api/units/time_delta.h"
#include "rtc_base/strong_alias.h"
namespace dcsctp {
@@ -41,6 +42,10 @@ class DurationMs : public webrtc::StrongAlias<class DurationMsTag, int32_t> {
constexpr explicit DurationMs(const UnderlyingType& v)
: webrtc::StrongAlias<class DurationMsTag, int32_t>(v) {}
+ constexpr explicit DurationMs(webrtc::TimeDelta v)
+ : webrtc::StrongAlias<class DurationMsTag, int32_t>(
+ v.IsInfinite() ? InfiniteDuration() : DurationMs(v.ms())) {}
+
static constexpr DurationMs InfiniteDuration() {
return DurationMs(std::numeric_limits<int32_t>::max());
}
@@ -58,6 +63,11 @@ class DurationMs : public webrtc::StrongAlias<class DurationMsTag, int32_t> {
value_ *= factor;
return *this;
}
+ constexpr webrtc::TimeDelta ToTimeDelta() const {
+ return *this == DurationMs::InfiniteDuration()
+ ? webrtc::TimeDelta::PlusInfinity()
+ : webrtc::TimeDelta::Millis(value_);
+ }
};
constexpr inline DurationMs operator+(DurationMs lhs, DurationMs rhs) {
diff --git a/third_party/libwebrtc/net/dcsctp/rx/BUILD.gn b/third_party/libwebrtc/net/dcsctp/rx/BUILD.gn
index f5f5b7ed81..15b9f60f3d 100644
--- a/third_party/libwebrtc/net/dcsctp/rx/BUILD.gn
+++ b/third_party/libwebrtc/net/dcsctp/rx/BUILD.gn
@@ -95,10 +95,10 @@ rtc_library("reassembly_queue") {
"../../../api:array_view",
"../../../rtc_base:checks",
"../../../rtc_base:logging",
+ "../../../rtc_base:stringutils",
"../../../rtc_base/containers:flat_set",
"../common:internal_types",
"../common:sequence_numbers",
- "../common:str_join",
"../packet:chunk",
"../packet:data",
"../packet:parameter",
diff --git a/third_party/libwebrtc/net/dcsctp/rx/data_tracker_test.cc b/third_party/libwebrtc/net/dcsctp/rx/data_tracker_test.cc
index 07192fda54..0e9e4fcb60 100644
--- a/third_party/libwebrtc/net/dcsctp/rx/data_tracker_test.cc
+++ b/third_party/libwebrtc/net/dcsctp/rx/data_tracker_test.cc
@@ -29,6 +29,8 @@ using ::testing::ElementsAre;
using ::testing::IsEmpty;
using ::testing::SizeIs;
using ::testing::UnorderedElementsAre;
+using ::webrtc::TimeDelta;
+using ::webrtc::Timestamp;
constexpr size_t kArwnd = 10000;
constexpr TSN kInitialTSN(11);
@@ -42,8 +44,8 @@ class DataTrackerTest : public testing::Test {
}),
timer_(timer_manager_.CreateTimer(
"test/delayed_ack",
- []() { return absl::nullopt; },
- TimerOptions(DurationMs(0)))),
+ []() { return TimeDelta::Zero(); },
+ TimerOptions(TimeDelta::Zero()))),
tracker_(
std::make_unique<DataTracker>("log: ", timer_.get(), kInitialTSN)) {
}
@@ -71,7 +73,7 @@ class DataTrackerTest : public testing::Test {
tracker_->RestoreFromState(state);
}
- TimeMs now_ = TimeMs(0);
+ Timestamp now_ = Timestamp::Zero();
FakeTimeoutManager timeout_manager_;
TimerManager timer_manager_;
std::unique_ptr<Timer> timer_;
@@ -784,5 +786,16 @@ TEST_F(DataTrackerTest, DoesNotAcceptGapsWithDuplicateData) {
EXPECT_FALSE(tracker_->Observe(TSN(12)));
}
+TEST_F(DataTrackerTest, NotReadyForHandoverWhenHavingTsnGaps) {
+ tracker_->Observe(TSN(10));
+ tracker_->Observe(TSN(12));
+ EXPECT_EQ(tracker_->GetHandoverReadiness(),
+ HandoverReadinessStatus().Add(
+ HandoverUnreadinessReason::kDataTrackerTsnBlocksPending));
+
+ tracker_->Observe(TSN(11));
+ EXPECT_EQ(tracker_->GetHandoverReadiness(), HandoverReadinessStatus());
+}
+
} // namespace
} // namespace dcsctp
diff --git a/third_party/libwebrtc/net/dcsctp/rx/reassembly_queue.cc b/third_party/libwebrtc/net/dcsctp/rx/reassembly_queue.cc
index 573443635c..4c223d0532 100644
--- a/third_party/libwebrtc/net/dcsctp/rx/reassembly_queue.cc
+++ b/third_party/libwebrtc/net/dcsctp/rx/reassembly_queue.cc
@@ -23,7 +23,6 @@
#include "absl/types/optional.h"
#include "api/array_view.h"
#include "net/dcsctp/common/sequence_numbers.h"
-#include "net/dcsctp/common/str_join.h"
#include "net/dcsctp/packet/chunk/forward_tsn_common.h"
#include "net/dcsctp/packet/data.h"
#include "net/dcsctp/packet/parameter/outgoing_ssn_reset_request_parameter.h"
@@ -34,6 +33,7 @@
#include "net/dcsctp/rx/reassembly_streams.h"
#include "net/dcsctp/rx/traditional_reassembly_streams.h"
#include "rtc_base/logging.h"
+#include "rtc_base/strings/str_join.h"
namespace dcsctp {
namespace {
@@ -57,8 +57,6 @@ ReassemblyQueue::ReassemblyQueue(absl::string_view log_prefix,
: log_prefix_(log_prefix),
max_size_bytes_(max_size_bytes),
watermark_bytes_(max_size_bytes * kHighWatermarkLimit),
- last_assembled_tsn_watermark_(
- tsn_unwrapper_.Unwrap(TSN(*peer_initial_tsn - 1))),
last_completed_reset_req_seq_nbr_(ReconfigRequestSN(0)),
streams_(CreateStreams(
log_prefix_,
@@ -180,33 +178,9 @@ void ReassemblyQueue::AddReassembledMessage(
<< ", ppid=" << *message.ppid()
<< ", payload=" << message.payload().size() << " bytes";
- for (const UnwrappedTSN tsn : tsns) {
- if (tsn == last_assembled_tsn_watermark_.next_value()) {
- // Update watermark, or insert into delivered_tsns_
- last_assembled_tsn_watermark_.Increment();
- } else {
- delivered_tsns_.insert(tsn);
- }
- }
-
- // With new TSNs in delivered_tsns, gaps might be filled.
- MaybeMoveLastAssembledWatermarkFurther();
-
reassembled_messages_.emplace_back(std::move(message));
}
-void ReassemblyQueue::MaybeMoveLastAssembledWatermarkFurther() {
- // `delivered_tsns_` contain TSNS when there is a gap between ranges of
- // assembled TSNs. `last_assembled_tsn_watermark_` should not be adjacent to
- // that list, because if so, it can be moved.
- while (!delivered_tsns_.empty() &&
- *delivered_tsns_.begin() ==
- last_assembled_tsn_watermark_.next_value()) {
- last_assembled_tsn_watermark_.Increment();
- delivered_tsns_.erase(delivered_tsns_.begin());
- }
-}
-
void ReassemblyQueue::HandleForwardTsn(
TSN new_cumulative_tsn,
rtc::ArrayView<const AnyForwardTsnChunk::SkippedStream> skipped_streams) {
@@ -228,33 +202,19 @@ void ReassemblyQueue::HandleForwardTsn(
RTC_DLOG(LS_VERBOSE) << log_prefix_ << "ForwardTSN to " << *tsn.Wrap()
<< " - performing.";
- last_assembled_tsn_watermark_ = std::max(last_assembled_tsn_watermark_, tsn);
- delivered_tsns_.erase(delivered_tsns_.begin(),
- delivered_tsns_.upper_bound(tsn));
- MaybeMoveLastAssembledWatermarkFurther();
queued_bytes_ -= streams_->HandleForwardTsn(tsn, skipped_streams);
RTC_DCHECK(IsConsistent());
}
bool ReassemblyQueue::IsConsistent() const {
- // `delivered_tsns_` and `last_assembled_tsn_watermark_` mustn't overlap or be
- // adjacent.
- if (!delivered_tsns_.empty() &&
- last_assembled_tsn_watermark_.next_value() >= *delivered_tsns_.begin()) {
- return false;
- }
-
// Allow queued_bytes_ to be larger than max_size_bytes, as it's not actively
- // enforced in this class. This comparison will still trigger if queued_bytes_
- // became "negative".
- return (queued_bytes_ >= 0 && queued_bytes_ <= 2 * max_size_bytes_);
+ // enforced in this class. But in case it wraps around (becomes negative, but
+ // as it's unsigned, that would wrap to very big), this would trigger.
+ return (queued_bytes_ <= 2 * max_size_bytes_);
}
HandoverReadinessStatus ReassemblyQueue::GetHandoverReadiness() const {
HandoverReadinessStatus status = streams_->GetHandoverReadiness();
- if (!delivered_tsns_.empty()) {
- status.Add(HandoverUnreadinessReason::kReassemblyQueueDeliveredTSNsGap);
- }
if (deferred_reset_streams_.has_value()) {
status.Add(HandoverUnreadinessReason::kStreamResetDeferred);
}
@@ -262,7 +222,6 @@ HandoverReadinessStatus ReassemblyQueue::GetHandoverReadiness() const {
}
void ReassemblyQueue::AddHandoverState(DcSctpSocketHandoverState& state) {
- state.rx.last_assembled_tsn = last_assembled_tsn_watermark_.Wrap().value();
state.rx.last_completed_deferred_reset_req_sn =
last_completed_reset_req_seq_nbr_.value();
streams_->AddHandoverState(state);
@@ -272,8 +231,6 @@ void ReassemblyQueue::RestoreFromState(const DcSctpSocketHandoverState& state) {
// Validate that the component is in pristine state.
RTC_DCHECK(last_completed_reset_req_seq_nbr_ == ReconfigRequestSN(0));
- last_assembled_tsn_watermark_ =
- tsn_unwrapper_.Unwrap(TSN(state.rx.last_assembled_tsn));
last_completed_reset_req_seq_nbr_ =
ReconfigRequestSN(state.rx.last_completed_deferred_reset_req_sn);
streams_->RestoreFromState(state);
diff --git a/third_party/libwebrtc/net/dcsctp/rx/reassembly_queue.h b/third_party/libwebrtc/net/dcsctp/rx/reassembly_queue.h
index 761ec3556c..82a66d089c 100644
--- a/third_party/libwebrtc/net/dcsctp/rx/reassembly_queue.h
+++ b/third_party/libwebrtc/net/dcsctp/rx/reassembly_queue.h
@@ -142,19 +142,12 @@ class ReassemblyQueue {
bool IsConsistent() const;
void AddReassembledMessage(rtc::ArrayView<const UnwrappedTSN> tsns,
DcSctpMessage message);
- void MaybeMoveLastAssembledWatermarkFurther();
const absl::string_view log_prefix_;
const size_t max_size_bytes_;
const size_t watermark_bytes_;
UnwrappedTSN::Unwrapper tsn_unwrapper_;
- // Whenever a message has been assembled, either increase
- // `last_assembled_tsn_watermark_` or - if there are gaps - add the message's
- // TSNs into delivered_tsns_ so that messages are not re-delivered on
- // duplicate chunks.
- UnwrappedTSN last_assembled_tsn_watermark_;
- std::set<UnwrappedTSN> delivered_tsns_;
// Messages that have been reassembled, and will be returned by
// `FlushMessages`.
std::vector<DcSctpMessage> reassembled_messages_;
diff --git a/third_party/libwebrtc/net/dcsctp/rx/reassembly_queue_test.cc b/third_party/libwebrtc/net/dcsctp/rx/reassembly_queue_test.cc
index fd8c423a5f..81bb7af963 100644
--- a/third_party/libwebrtc/net/dcsctp/rx/reassembly_queue_test.cc
+++ b/third_party/libwebrtc/net/dcsctp/rx/reassembly_queue_test.cc
@@ -252,33 +252,6 @@ TEST_F(ReassemblyQueueTest, ForwardTSNRemoveALotOrdered) {
ElementsAre(SctpMessageIs(kStreamID, kPPID, kMessage2Payload)));
}
-TEST_F(ReassemblyQueueTest, NotReadyForHandoverWhenDeliveredTsnsHaveGap) {
- ReassemblyQueue reasm("log: ", TSN(10), kBufferSize);
- reasm.Add(TSN(10), gen_.Unordered({1, 2, 3, 4}, "B"));
- EXPECT_FALSE(reasm.HasMessages());
-
- reasm.Add(TSN(12), gen_.Unordered({1, 2, 3, 4}, "BE"));
- EXPECT_TRUE(reasm.HasMessages());
- EXPECT_EQ(
- reasm.GetHandoverReadiness(),
- HandoverReadinessStatus()
- .Add(HandoverUnreadinessReason::kReassemblyQueueDeliveredTSNsGap)
- .Add(
- HandoverUnreadinessReason::kUnorderedStreamHasUnassembledChunks));
-
- EXPECT_THAT(reasm.FlushMessages(),
- ElementsAre(SctpMessageIs(kStreamID, kPPID, kShortPayload)));
- EXPECT_EQ(
- reasm.GetHandoverReadiness(),
- HandoverReadinessStatus()
- .Add(HandoverUnreadinessReason::kReassemblyQueueDeliveredTSNsGap)
- .Add(
- HandoverUnreadinessReason::kUnorderedStreamHasUnassembledChunks));
-
- reasm.HandleForwardTsn(TSN(13), std::vector<SkippedStream>());
- EXPECT_EQ(reasm.GetHandoverReadiness(), HandoverReadinessStatus());
-}
-
TEST_F(ReassemblyQueueTest, NotReadyForHandoverWhenResetStreamIsDeferred) {
ReassemblyQueue reasm("log: ", TSN(10), kBufferSize);
reasm.Add(TSN(10), gen_.Ordered({1, 2, 3, 4}, "BE", {.mid = MID(0)}));
diff --git a/third_party/libwebrtc/net/dcsctp/socket/BUILD.gn b/third_party/libwebrtc/net/dcsctp/socket/BUILD.gn
index 681ddd47e9..04f61e5b72 100644
--- a/third_party/libwebrtc/net/dcsctp/socket/BUILD.gn
+++ b/third_party/libwebrtc/net/dcsctp/socket/BUILD.gn
@@ -11,6 +11,7 @@ import("../../../webrtc.gni")
rtc_source_set("context") {
sources = [ "context.h" ]
deps = [
+ "../../../api/units:time_delta",
"../common:internal_types",
"../packet:sctp_packet",
"../public:socket",
@@ -23,6 +24,7 @@ rtc_library("heartbeat_handler") {
deps = [
":context",
"../../../api:array_view",
+ "../../../api/units:time_delta",
"../../../rtc_base:checks",
"../../../rtc_base:logging",
"../packet:bounded_io",
@@ -48,11 +50,12 @@ rtc_library("stream_reset_handler") {
deps = [
":context",
"../../../api:array_view",
+ "../../../api/units:time_delta",
"../../../rtc_base:checks",
"../../../rtc_base:logging",
+ "../../../rtc_base:stringutils",
"../../../rtc_base/containers:flat_set",
"../common:internal_types",
- "../common:str_join",
"../packet:chunk",
"../packet:parameter",
"../packet:sctp_packet",
@@ -97,6 +100,7 @@ rtc_library("transmission_control_block") {
":stream_reset_handler",
"../../../api:array_view",
"../../../api/task_queue:task_queue",
+ "../../../api/units:time_delta",
"../../../rtc_base:checks",
"../../../rtc_base:logging",
"../../../rtc_base:stringutils",
diff --git a/third_party/libwebrtc/net/dcsctp/socket/callback_deferrer.cc b/third_party/libwebrtc/net/dcsctp/socket/callback_deferrer.cc
index 123526e782..0a24020167 100644
--- a/third_party/libwebrtc/net/dcsctp/socket/callback_deferrer.cc
+++ b/third_party/libwebrtc/net/dcsctp/socket/callback_deferrer.cc
@@ -28,7 +28,7 @@ class MessageDeliverer {
}
private:
- struct State : public rtc::RefCountInterface {
+ struct State : public webrtc::RefCountInterface {
explicit State(DcSctpMessage&& m)
: has_delivered(false), message(std::move(m)) {}
bool has_delivered;
@@ -70,6 +70,8 @@ std::unique_ptr<Timeout> CallbackDeferrer::CreateTimeout(
}
TimeMs CallbackDeferrer::TimeMillis() {
+ // This should not be called by the library - it's migrated to `Now()`.
+ RTC_DCHECK(false);
// Will not be deferred - call directly.
return underlying_.TimeMillis();
}
diff --git a/third_party/libwebrtc/net/dcsctp/socket/callback_deferrer.h b/third_party/libwebrtc/net/dcsctp/socket/callback_deferrer.h
index 1c35dda6cf..6659e87155 100644
--- a/third_party/libwebrtc/net/dcsctp/socket/callback_deferrer.h
+++ b/third_party/libwebrtc/net/dcsctp/socket/callback_deferrer.h
@@ -65,6 +65,7 @@ class CallbackDeferrer : public DcSctpSocketCallbacks {
std::unique_ptr<Timeout> CreateTimeout(
webrtc::TaskQueueBase::DelayPrecision precision) override;
TimeMs TimeMillis() override;
+ webrtc::Timestamp Now() override { return underlying_.Now(); }
uint32_t GetRandomInt(uint32_t low, uint32_t high) override;
void OnMessageReceived(DcSctpMessage message) override;
void OnError(ErrorKind error, absl::string_view message) override;
diff --git a/third_party/libwebrtc/net/dcsctp/socket/context.h b/third_party/libwebrtc/net/dcsctp/socket/context.h
index eca5b9e4fb..8e970e8c8e 100644
--- a/third_party/libwebrtc/net/dcsctp/socket/context.h
+++ b/third_party/libwebrtc/net/dcsctp/socket/context.h
@@ -13,6 +13,7 @@
#include <cstdint>
#include "absl/strings/string_view.h"
+#include "api/units/time_delta.h"
#include "net/dcsctp/common/internal_types.h"
#include "net/dcsctp/packet/sctp_packet.h"
#include "net/dcsctp/public/dcsctp_socket.h"
@@ -39,11 +40,11 @@ class Context {
// Returns the socket callbacks.
virtual DcSctpSocketCallbacks& callbacks() const = 0;
- // Observes a measured RTT value, in milliseconds.
- virtual void ObserveRTT(DurationMs rtt_ms) = 0;
+ // Observes a measured RTT value.
+ virtual void ObserveRTT(webrtc::TimeDelta rtt_ms) = 0;
// Returns the current Retransmission Timeout (rto) value, in milliseconds.
- virtual DurationMs current_rto() const = 0;
+ virtual webrtc::TimeDelta current_rto() const = 0;
// Increments the transmission error counter, given a human readable reason.
virtual bool IncrementTxErrorCounter(absl::string_view reason) = 0;
diff --git a/third_party/libwebrtc/net/dcsctp/socket/dcsctp_socket.cc b/third_party/libwebrtc/net/dcsctp/socket/dcsctp_socket.cc
index 32bcdaaacf..f0f9590943 100644
--- a/third_party/libwebrtc/net/dcsctp/socket/dcsctp_socket.cc
+++ b/third_party/libwebrtc/net/dcsctp/socket/dcsctp_socket.cc
@@ -82,6 +82,8 @@
namespace dcsctp {
namespace {
+using ::webrtc::TimeDelta;
+using ::webrtc::Timestamp;
// https://tools.ietf.org/html/rfc4960#section-5.1
constexpr uint32_t kMinVerificationTag = 1;
@@ -187,19 +189,19 @@ DcSctpSocket::DcSctpSocket(absl::string_view log_prefix,
t1_init_(timer_manager_.CreateTimer(
"t1-init",
absl::bind_front(&DcSctpSocket::OnInitTimerExpiry, this),
- TimerOptions(options.t1_init_timeout,
+ TimerOptions(options.t1_init_timeout.ToTimeDelta(),
TimerBackoffAlgorithm::kExponential,
options.max_init_retransmits))),
t1_cookie_(timer_manager_.CreateTimer(
"t1-cookie",
absl::bind_front(&DcSctpSocket::OnCookieTimerExpiry, this),
- TimerOptions(options.t1_cookie_timeout,
+ TimerOptions(options.t1_cookie_timeout.ToTimeDelta(),
TimerBackoffAlgorithm::kExponential,
options.max_init_retransmits))),
t2_shutdown_(timer_manager_.CreateTimer(
"t2-shutdown",
absl::bind_front(&DcSctpSocket::OnShutdownTimerExpiry, this),
- TimerOptions(options.t2_shutdown_timeout,
+ TimerOptions(options.t2_shutdown_timeout.ToTimeDelta(),
TimerBackoffAlgorithm::kExponential,
options.max_retransmissions))),
packet_sender_(callbacks_,
@@ -518,7 +520,7 @@ SendStatus DcSctpSocket::Send(DcSctpMessage message,
return SendStatus::kErrorResourceExhaustion;
}
- TimeMs now = callbacks_.TimeMillis();
+ Timestamp now = callbacks_.Now();
++metrics_.tx_messages_count;
send_queue_.Add(now, std::move(message), send_options);
if (tcb_ != nullptr) {
@@ -600,11 +602,11 @@ absl::optional<Metrics> DcSctpSocket::GetMetrics() const {
Metrics metrics = metrics_;
metrics.cwnd_bytes = tcb_->cwnd();
- metrics.srtt_ms = tcb_->current_srtt().value();
+ metrics.srtt_ms = tcb_->current_srtt().ms();
size_t packet_payload_size =
options_.mtu - SctpPacket::kHeaderSize - DataChunk::kHeaderSize;
metrics.unack_data_count =
- tcb_->retransmission_queue().outstanding_items() +
+ tcb_->retransmission_queue().unacked_items() +
(send_queue_.total_buffered_amount() + packet_payload_size - 1) /
packet_payload_size;
metrics.peer_rwnd_bytes = tcb_->retransmission_queue().rwnd();
@@ -768,7 +770,7 @@ void DcSctpSocket::ReceivePacket(rtc::ArrayView<const uint8_t> data) {
++metrics_.rx_packets_count;
if (packet_observer_ != nullptr) {
- packet_observer_->OnReceivedPacket(callbacks_.TimeMillis(), data);
+ packet_observer_->OnReceivedPacket(TimeMs(callbacks_.Now().ms()), data);
}
absl::optional<SctpPacket> packet = SctpPacket::Parse(data, options_);
@@ -921,7 +923,7 @@ bool DcSctpSocket::HandleUnrecognizedChunk(
return continue_processing;
}
-absl::optional<DurationMs> DcSctpSocket::OnInitTimerExpiry() {
+TimeDelta DcSctpSocket::OnInitTimerExpiry() {
RTC_DLOG(LS_VERBOSE) << log_prefix() << "Timer " << t1_init_->name()
<< " has expired: " << t1_init_->expiration_count()
<< "/" << t1_init_->options().max_restarts.value_or(-1);
@@ -933,10 +935,10 @@ absl::optional<DurationMs> DcSctpSocket::OnInitTimerExpiry() {
InternalClose(ErrorKind::kTooManyRetries, "No INIT_ACK received");
}
RTC_DCHECK(IsConsistent());
- return absl::nullopt;
+ return TimeDelta::Zero();
}
-absl::optional<DurationMs> DcSctpSocket::OnCookieTimerExpiry() {
+TimeDelta DcSctpSocket::OnCookieTimerExpiry() {
// https://tools.ietf.org/html/rfc4960#section-4
// "If the T1-cookie timer expires, the endpoint MUST retransmit COOKIE
// ECHO and restart the T1-cookie timer without changing state. This MUST
@@ -951,16 +953,16 @@ absl::optional<DurationMs> DcSctpSocket::OnCookieTimerExpiry() {
RTC_DCHECK(state_ == State::kCookieEchoed);
if (t1_cookie_->is_running()) {
- tcb_->SendBufferedPackets(callbacks_.TimeMillis());
+ tcb_->SendBufferedPackets(callbacks_.Now());
} else {
InternalClose(ErrorKind::kTooManyRetries, "No COOKIE_ACK received");
}
RTC_DCHECK(IsConsistent());
- return absl::nullopt;
+ return TimeDelta::Zero();
}
-absl::optional<DurationMs> DcSctpSocket::OnShutdownTimerExpiry() {
+TimeDelta DcSctpSocket::OnShutdownTimerExpiry() {
RTC_DLOG(LS_VERBOSE) << log_prefix() << "Timer " << t2_shutdown_->name()
<< " has expired: " << t2_shutdown_->expiration_count()
<< "/"
@@ -980,7 +982,7 @@ absl::optional<DurationMs> DcSctpSocket::OnShutdownTimerExpiry() {
InternalClose(ErrorKind::kTooManyRetries, "No SHUTDOWN_ACK received");
RTC_DCHECK(IsConsistent());
- return absl::nullopt;
+ return TimeDelta::Zero();
}
// https://tools.ietf.org/html/rfc4960#section-9.2
@@ -996,7 +998,7 @@ void DcSctpSocket::OnSentPacket(rtc::ArrayView<const uint8_t> packet,
// The packet observer is invoked even if the packet was failed to be sent, to
// indicate an attempt was made.
if (packet_observer_ != nullptr) {
- packet_observer_->OnSentPacket(callbacks_.TimeMillis(), packet);
+ packet_observer_->OnSentPacket(TimeMs(callbacks_.Now().ms()), packet);
}
if (status == SendPacketStatus::kSuccess) {
@@ -1282,7 +1284,7 @@ void DcSctpSocket::HandleInitAck(
// The connection isn't fully established just yet.
tcb_->SetCookieEchoChunk(CookieEchoChunk(cookie->data()));
- tcb_->SendBufferedPackets(callbacks_.TimeMillis());
+ tcb_->SendBufferedPackets(callbacks_.Now());
t1_cookie_->Start();
}
@@ -1351,7 +1353,7 @@ void DcSctpSocket::HandleCookieEcho(
// "A COOKIE ACK chunk may be bundled with any pending DATA chunks (and/or
// SACK chunks), but the COOKIE ACK chunk MUST be the first chunk in the
// packet."
- tcb_->SendBufferedPackets(b, callbacks_.TimeMillis());
+ tcb_->SendBufferedPackets(b, callbacks_.Now());
}
bool DcSctpSocket::HandleCookieEchoWithTCB(const CommonHeader& header,
@@ -1449,7 +1451,7 @@ void DcSctpSocket::HandleCookieAck(
t1_cookie_->Stop();
tcb_->ClearCookieEchoChunk();
SetState(State::kEstablished, "COOKIE_ACK received");
- tcb_->SendBufferedPackets(callbacks_.TimeMillis());
+ tcb_->SendBufferedPackets(callbacks_.Now());
callbacks_.OnConnected();
}
@@ -1465,7 +1467,7 @@ void DcSctpSocket::HandleSack(const CommonHeader& header,
absl::optional<SackChunk> chunk = SackChunk::Parse(descriptor.data);
if (ValidateParseSuccess(chunk) && ValidateHasTCB()) {
- TimeMs now = callbacks_.TimeMillis();
+ Timestamp now = callbacks_.Now();
SackChunk sack = ChunkValidators::Clean(*std::move(chunk));
if (tcb_->retransmission_queue().HandleSack(now, sack)) {
@@ -1554,7 +1556,7 @@ void DcSctpSocket::HandleError(const CommonHeader& header,
void DcSctpSocket::HandleReconfig(
const CommonHeader& header,
const SctpPacket::ChunkDescriptor& descriptor) {
- TimeMs now = callbacks_.TimeMillis();
+ Timestamp now = callbacks_.Now();
absl::optional<ReConfigChunk> chunk = ReConfigChunk::Parse(descriptor.data);
if (ValidateParseSuccess(chunk) && ValidateHasTCB()) {
tcb_->stream_reset_handler().HandleReConfig(*std::move(chunk));
@@ -1718,7 +1720,7 @@ void DcSctpSocket::HandleForwardTsnCommon(const AnyForwardTsnChunk& chunk) {
}
void DcSctpSocket::MaybeSendShutdownOrAck() {
- if (tcb_->retransmission_queue().outstanding_bytes() != 0) {
+ if (tcb_->retransmission_queue().unacked_bytes() != 0) {
return;
}
diff --git a/third_party/libwebrtc/net/dcsctp/socket/dcsctp_socket.h b/third_party/libwebrtc/net/dcsctp/socket/dcsctp_socket.h
index f91eb3ead4..deb6ee23e7 100644
--- a/third_party/libwebrtc/net/dcsctp/socket/dcsctp_socket.h
+++ b/third_party/libwebrtc/net/dcsctp/socket/dcsctp_socket.h
@@ -155,9 +155,9 @@ class DcSctpSocket : public DcSctpSocketInterface {
// Closes the association, because of too many retransmission errors.
void CloseConnectionBecauseOfTooManyTransmissionErrors();
// Timer expiration handlers
- absl::optional<DurationMs> OnInitTimerExpiry();
- absl::optional<DurationMs> OnCookieTimerExpiry();
- absl::optional<DurationMs> OnShutdownTimerExpiry();
+ webrtc::TimeDelta OnInitTimerExpiry();
+ webrtc::TimeDelta OnCookieTimerExpiry();
+ webrtc::TimeDelta OnShutdownTimerExpiry();
void OnSentPacket(rtc::ArrayView<const uint8_t> packet,
SendPacketStatus status);
// Sends SHUTDOWN or SHUTDOWN-ACK if the socket is shutting down and if all
diff --git a/third_party/libwebrtc/net/dcsctp/socket/dcsctp_socket_network_test.cc b/third_party/libwebrtc/net/dcsctp/socket/dcsctp_socket_network_test.cc
index f097bfa095..f73ecce445 100644
--- a/third_party/libwebrtc/net/dcsctp/socket/dcsctp_socket_network_test.cc
+++ b/third_party/libwebrtc/net/dcsctp/socket/dcsctp_socket_network_test.cc
@@ -55,6 +55,8 @@ using ::testing::AllOf;
using ::testing::Ge;
using ::testing::Le;
using ::testing::SizeIs;
+using ::webrtc::TimeDelta;
+using ::webrtc::Timestamp;
constexpr StreamID kStreamId(1);
constexpr PPID kPpid(53);
@@ -142,13 +144,13 @@ class SctpActor : public DcSctpSocketCallbacks {
emulated_socket_(emulated_socket),
timeout_factory_(
*thread_,
- [this]() { return TimeMillis(); },
+ [this]() { return TimeMs(Now().ms()); },
[this](dcsctp::TimeoutID timeout_id) {
sctp_socket_.HandleTimeout(timeout_id);
}),
random_(GetUniqueSeed()),
sctp_socket_(name, *this, nullptr, sctp_options),
- last_bandwidth_printout_(TimeMs(TimeMillis())) {
+ last_bandwidth_printout_(Now()) {
emulated_socket.SetReceiver([this](rtc::CopyOnWriteBuffer buf) {
// The receiver will be executed on the NetworkEmulation task queue, but
// the dcSCTP socket is owned by `thread_` and is not thread-safe.
@@ -157,11 +159,11 @@ class SctpActor : public DcSctpSocketCallbacks {
}
void PrintBandwidth() {
- TimeMs now = TimeMillis();
- DurationMs duration = now - last_bandwidth_printout_;
+ Timestamp now = Now();
+ TimeDelta duration = now - last_bandwidth_printout_;
double bitrate_mbps =
- static_cast<double>(received_bytes_ * 8) / *duration / 1000;
+ static_cast<double>(received_bytes_ * 8) / duration.ms() / 1000;
RTC_LOG(LS_INFO) << log_prefix()
<< rtc::StringFormat("Received %0.2f Mbps", bitrate_mbps);
@@ -185,7 +187,7 @@ class SctpActor : public DcSctpSocketCallbacks {
return timeout_factory_.CreateTimeout(precision);
}
- TimeMs TimeMillis() override { return TimeMs(rtc::TimeMillis()); }
+ Timestamp Now() override { return Timestamp::Millis(rtc::TimeMillis()); }
uint32_t GetRandomInt(uint32_t low, uint32_t high) override {
return random_.Rand(low, high);
@@ -314,7 +316,7 @@ class SctpActor : public DcSctpSocketCallbacks {
DcSctpSocket sctp_socket_;
size_t received_bytes_ = 0;
absl::optional<DcSctpMessage> last_received_message_;
- TimeMs last_bandwidth_printout_;
+ Timestamp last_bandwidth_printout_;
// Per-second received bitrates, in Mbps
std::vector<double> received_bitrate_mbps_;
webrtc::ScopedTaskSafety safety_;
diff --git a/third_party/libwebrtc/net/dcsctp/socket/dcsctp_socket_test.cc b/third_party/libwebrtc/net/dcsctp/socket/dcsctp_socket_test.cc
index 13202846ac..dc76b80a37 100644
--- a/third_party/libwebrtc/net/dcsctp/socket/dcsctp_socket_test.cc
+++ b/third_party/libwebrtc/net/dcsctp/socket/dcsctp_socket_test.cc
@@ -73,6 +73,8 @@ using ::testing::Not;
using ::testing::Property;
using ::testing::SizeIs;
using ::testing::UnorderedElementsAre;
+using ::webrtc::TimeDelta;
+using ::webrtc::Timestamp;
constexpr SendOptions kSendOptions;
constexpr size_t kLargeMessageSize = DcSctpOptions::kMaxSafeMTUSize * 20;
@@ -269,7 +271,7 @@ void RunTimers(SocketUnderTest& s) {
}
}
-void AdvanceTime(SocketUnderTest& a, SocketUnderTest& z, DurationMs duration) {
+void AdvanceTime(SocketUnderTest& a, SocketUnderTest& z, TimeDelta duration) {
a.cb.AdvanceTime(duration);
z.cb.AdvanceTime(duration);
@@ -282,14 +284,14 @@ void AdvanceTime(SocketUnderTest& a, SocketUnderTest& z, DurationMs duration) {
void ExchangeMessagesAndAdvanceTime(
SocketUnderTest& a,
SocketUnderTest& z,
- DurationMs max_timeout = DurationMs(10000)) {
- TimeMs time_started = a.cb.TimeMillis();
- while (a.cb.TimeMillis() - time_started < max_timeout) {
+ TimeDelta max_timeout = TimeDelta::Seconds(10)) {
+ Timestamp time_started = a.cb.Now();
+ while (a.cb.Now() - time_started < max_timeout) {
ExchangeMessages(a, z);
- DurationMs time_to_next_timeout =
+ TimeDelta time_to_next_timeout =
std::min(a.cb.GetTimeToNextTimeout(), z.cb.GetTimeToNextTimeout());
- if (time_to_next_timeout == DurationMs::InfiniteDuration()) {
+ if (time_to_next_timeout.IsPlusInfinity()) {
// No more pending timer.
return;
}
@@ -535,7 +537,7 @@ TEST(DcSctpSocketTest, EstablishConnectionLostCookieAck) {
EXPECT_EQ(z.socket.state(), SocketState::kConnected);
// This will make A re-send the COOKIE_ECHO
- AdvanceTime(a, z, DurationMs(a.options.t1_cookie_timeout));
+ AdvanceTime(a, z, a.options.t1_cookie_timeout.ToTimeDelta());
// Z reads COOKIE_ECHO, produces COOKIE_ACK
z.socket.ReceivePacket(a.cb.ConsumeSentPacket());
@@ -555,7 +557,7 @@ TEST(DcSctpSocketTest, ResendInitAndEstablishConnection) {
EXPECT_THAT(a.cb.ConsumeSentPacket(),
HasChunks(ElementsAre(IsChunkType(InitChunk::kType))));
- AdvanceTime(a, z, a.options.t1_init_timeout);
+ AdvanceTime(a, z, a.options.t1_init_timeout.ToTimeDelta());
// Z reads INIT, produces INIT_ACK
z.socket.ReceivePacket(a.cb.ConsumeSentPacket());
@@ -581,7 +583,7 @@ TEST(DcSctpSocketTest, ResendingInitTooManyTimesAborts) {
HasChunks(ElementsAre(IsChunkType(InitChunk::kType))));
for (int i = 0; i < *a.options.max_init_retransmits; ++i) {
- AdvanceTime(a, z, a.options.t1_init_timeout * (1 << i));
+ AdvanceTime(a, z, a.options.t1_init_timeout.ToTimeDelta() * (1 << i));
// INIT is resent
EXPECT_THAT(a.cb.ConsumeSentPacket(),
@@ -590,8 +592,9 @@ TEST(DcSctpSocketTest, ResendingInitTooManyTimesAborts) {
// Another timeout, after the max init retransmits.
EXPECT_CALL(a.cb, OnAborted).Times(1);
- AdvanceTime(
- a, z, a.options.t1_init_timeout * (1 << *a.options.max_init_retransmits));
+ AdvanceTime(a, z,
+ a.options.t1_init_timeout.ToTimeDelta() *
+ (1 << *a.options.max_init_retransmits));
EXPECT_EQ(a.socket.state(), SocketState::kClosed);
}
@@ -611,7 +614,7 @@ TEST(DcSctpSocketTest, ResendCookieEchoAndEstablishConnection) {
EXPECT_THAT(a.cb.ConsumeSentPacket(),
HasChunks(ElementsAre(IsChunkType(CookieEchoChunk::kType))));
- AdvanceTime(a, z, a.options.t1_init_timeout);
+ AdvanceTime(a, z, a.options.t1_init_timeout.ToTimeDelta());
// Z reads COOKIE_ECHO, produces COOKIE_ACK
z.socket.ReceivePacket(a.cb.ConsumeSentPacket());
@@ -638,7 +641,7 @@ TEST(DcSctpSocketTest, ResendingCookieEchoTooManyTimesAborts) {
HasChunks(ElementsAre(IsChunkType(CookieEchoChunk::kType))));
for (int i = 0; i < *a.options.max_init_retransmits; ++i) {
- AdvanceTime(a, z, a.options.t1_cookie_timeout * (1 << i));
+ AdvanceTime(a, z, a.options.t1_cookie_timeout.ToTimeDelta() * (1 << i));
// COOKIE_ECHO is resent
EXPECT_THAT(a.cb.ConsumeSentPacket(),
@@ -647,9 +650,9 @@ TEST(DcSctpSocketTest, ResendingCookieEchoTooManyTimesAborts) {
// Another timeout, after the max init retransmits.
EXPECT_CALL(a.cb, OnAborted).Times(1);
- AdvanceTime(
- a, z,
- a.options.t1_cookie_timeout * (1 << *a.options.max_init_retransmits));
+ AdvanceTime(a, z,
+ a.options.t1_cookie_timeout.ToTimeDelta() *
+ (1 << *a.options.max_init_retransmits));
EXPECT_EQ(a.socket.state(), SocketState::kClosed);
}
@@ -680,11 +683,13 @@ TEST(DcSctpSocketTest, DoesntSendMorePacketsUntilCookieAckHasBeenReceived) {
// will be T1-COOKIE that drives retransmissions, so when the T3-RTX expires,
// nothing should be retransmitted.
ASSERT_TRUE(a.options.rto_initial < a.options.t1_cookie_timeout);
- AdvanceTime(a, z, a.options.rto_initial);
+ AdvanceTime(a, z, a.options.rto_initial.ToTimeDelta());
EXPECT_THAT(a.cb.ConsumeSentPacket(), IsEmpty());
// When T1-COOKIE expires, both the COOKIE-ECHO and DATA should be present.
- AdvanceTime(a, z, a.options.t1_cookie_timeout - a.options.rto_initial);
+ AdvanceTime(a, z,
+ a.options.t1_cookie_timeout.ToTimeDelta() -
+ a.options.rto_initial.ToTimeDelta());
// And this COOKIE-ECHO and DATA is also lost - never received by Z.
EXPECT_THAT(a.cb.ConsumeSentPacket(),
@@ -694,7 +699,7 @@ TEST(DcSctpSocketTest, DoesntSendMorePacketsUntilCookieAckHasBeenReceived) {
EXPECT_THAT(a.cb.ConsumeSentPacket(), IsEmpty());
// COOKIE_ECHO has exponential backoff.
- AdvanceTime(a, z, a.options.t1_cookie_timeout * 2);
+ AdvanceTime(a, z, a.options.t1_cookie_timeout.ToTimeDelta() * 2);
// Z reads COOKIE_ECHO, produces COOKIE_ACK
z.socket.ReceivePacket(a.cb.ConsumeSentPacket());
@@ -747,7 +752,7 @@ TEST(DcSctpSocketTest, ShutdownTimerExpiresTooManyTimeClosesConnection) {
EXPECT_EQ(a.socket.state(), SocketState::kShuttingDown);
for (int i = 0; i < *a.options.max_retransmissions; ++i) {
- AdvanceTime(a, z, DurationMs(a.options.rto_initial * (1 << i)));
+ AdvanceTime(a, z, a.options.rto_initial.ToTimeDelta() * (1 << i));
// Dropping every shutdown chunk.
EXPECT_THAT(a.cb.ConsumeSentPacket(),
@@ -757,7 +762,8 @@ TEST(DcSctpSocketTest, ShutdownTimerExpiresTooManyTimeClosesConnection) {
// The last expiry, makes it abort the connection.
EXPECT_CALL(a.cb, OnAborted).Times(1);
AdvanceTime(a, z,
- a.options.rto_initial * (1 << *a.options.max_retransmissions));
+ a.options.rto_initial.ToTimeDelta() *
+ (1 << *a.options.max_retransmissions));
EXPECT_EQ(a.socket.state(), SocketState::kClosed);
EXPECT_THAT(a.cb.ConsumeSentPacket(),
@@ -815,7 +821,7 @@ TEST_P(DcSctpSocketParametrizedTest, TimeoutResendsPacket) {
a.cb.ConsumeSentPacket();
RTC_LOG(LS_INFO) << "Advancing time";
- AdvanceTime(a, *z, a.options.rto_initial);
+ AdvanceTime(a, *z, a.options.rto_initial.ToTimeDelta());
z->socket.ReceivePacket(a.cb.ConsumeSentPacket());
@@ -886,7 +892,7 @@ TEST_P(DcSctpSocketParametrizedTest, ExpectHeartbeatToBeSent) {
EXPECT_THAT(a.cb.ConsumeSentPacket(), IsEmpty());
- AdvanceTime(a, *z, a.options.heartbeat_interval);
+ AdvanceTime(a, *z, a.options.heartbeat_interval.ToTimeDelta());
std::vector<uint8_t> packet = a.cb.ConsumeSentPacket();
// The info is a single 64-bit number.
@@ -920,7 +926,7 @@ TEST_P(DcSctpSocketParametrizedTest,
for (int i = 0; i < *a.options.max_retransmissions; ++i) {
RTC_LOG(LS_INFO) << "Letting HEARTBEAT interval timer expire - sending...";
- AdvanceTime(a, *z, time_to_next_hearbeat);
+ AdvanceTime(a, *z, time_to_next_hearbeat.ToTimeDelta());
// Dropping every heartbeat.
ASSERT_HAS_VALUE_AND_ASSIGN(
@@ -929,20 +935,20 @@ TEST_P(DcSctpSocketParametrizedTest,
EXPECT_EQ(hb_packet.descriptors()[0].type, HeartbeatRequestChunk::kType);
RTC_LOG(LS_INFO) << "Letting the heartbeat expire.";
- AdvanceTime(a, *z, DurationMs(1000));
+ AdvanceTime(a, *z, TimeDelta::Millis(1000));
time_to_next_hearbeat = a.options.heartbeat_interval - DurationMs(1000);
}
RTC_LOG(LS_INFO) << "Letting HEARTBEAT interval timer expire - sending...";
- AdvanceTime(a, *z, time_to_next_hearbeat);
+ AdvanceTime(a, *z, time_to_next_hearbeat.ToTimeDelta());
// Last heartbeat
EXPECT_THAT(a.cb.ConsumeSentPacket(), Not(IsEmpty()));
EXPECT_CALL(a.cb, OnAborted).Times(1);
// Should suffice as exceeding RTO
- AdvanceTime(a, *z, DurationMs(1000));
+ AdvanceTime(a, *z, TimeDelta::Millis(1000));
z = MaybeHandoverSocket(std::move(z));
}
@@ -959,7 +965,7 @@ TEST_P(DcSctpSocketParametrizedTest, RecoversAfterASuccessfulAck) {
// Force-close socket Z so that it doesn't interfere from now on.
z->socket.Close();
- DurationMs time_to_next_hearbeat = a.options.heartbeat_interval;
+ TimeDelta time_to_next_hearbeat = a.options.heartbeat_interval.ToTimeDelta();
for (int i = 0; i < *a.options.max_retransmissions; ++i) {
AdvanceTime(a, *z, time_to_next_hearbeat);
@@ -968,9 +974,10 @@ TEST_P(DcSctpSocketParametrizedTest, RecoversAfterASuccessfulAck) {
a.cb.ConsumeSentPacket();
RTC_LOG(LS_INFO) << "Letting the heartbeat expire.";
- AdvanceTime(a, *z, DurationMs(1000));
+ AdvanceTime(a, *z, TimeDelta::Seconds(1));
- time_to_next_hearbeat = a.options.heartbeat_interval - DurationMs(1000);
+ time_to_next_hearbeat =
+ a.options.heartbeat_interval.ToTimeDelta() - TimeDelta::Seconds(1);
}
RTC_LOG(LS_INFO) << "Getting the last heartbeat - and acking it";
@@ -990,7 +997,7 @@ TEST_P(DcSctpSocketParametrizedTest, RecoversAfterASuccessfulAck) {
// Should suffice as exceeding RTO - which will not fire.
EXPECT_CALL(a.cb, OnAborted).Times(0);
- AdvanceTime(a, *z, DurationMs(1000));
+ AdvanceTime(a, *z, TimeDelta::Seconds(1));
EXPECT_THAT(a.cb.ConsumeSentPacket(), IsEmpty());
@@ -1245,7 +1252,7 @@ TEST_P(DcSctpSocketParametrizedTest, SendMessageWithLimitedRtx) {
a.socket.ReceivePacket(z->cb.ConsumeSentPacket());
// Handle delayed SACK for third DATA
- AdvanceTime(a, *z, a.options.delayed_ack_max_timeout);
+ AdvanceTime(a, *z, a.options.delayed_ack_max_timeout.ToTimeDelta());
// Handle SACK for second DATA
a.socket.ReceivePacket(z->cb.ConsumeSentPacket());
@@ -1254,7 +1261,7 @@ TEST_P(DcSctpSocketParametrizedTest, SendMessageWithLimitedRtx) {
// in-flight and the reported gap could be due to out-of-order delivery. So
// the RetransmissionQueue will not mark it as "to be retransmitted" until
// after the t3-rtx timer has expired.
- AdvanceTime(a, *z, a.options.rto_initial);
+ AdvanceTime(a, *z, a.options.rto_initial.ToTimeDelta());
// The chunk will be marked as retransmitted, and then as abandoned, which
// will trigger a FORWARD-TSN to be sent.
@@ -1352,7 +1359,7 @@ TEST_P(DcSctpSocketParametrizedTest, SendManyFragmentedMessagesWithLimitedRtx) {
ExchangeMessages(a, *z);
// Let the RTX timer expire, and exchange FORWARD-TSN/SACKs
- AdvanceTime(a, *z, a.options.rto_initial);
+ AdvanceTime(a, *z, a.options.rto_initial.ToTimeDelta());
ExchangeMessages(a, *z);
@@ -1484,7 +1491,7 @@ TEST(DcSctpSocketTest, PassingHighWatermarkWillOnlyAcceptCumAckTsn) {
.Build());
// The receiver might have moved into delayed ack mode.
- AdvanceTime(a, z, z.options.rto_initial);
+ AdvanceTime(a, z, z.options.rto_initial.ToTimeDelta());
EXPECT_THAT(z.cb.ConsumeSentPacket(),
HasChunks(ElementsAre(IsSack(
@@ -1528,7 +1535,7 @@ TEST(DcSctpSocketTest, PassingHighWatermarkWillOnlyAcceptCumAckTsn) {
.Build());
// The receiver might have moved into delayed ack mode.
- AdvanceTime(a, z, z.options.rto_initial);
+ AdvanceTime(a, z, z.options.rto_initial.ToTimeDelta());
EXPECT_THAT(z.cb.ConsumeSentPacket(),
HasChunks(ElementsAre(IsSack(
@@ -1562,13 +1569,13 @@ TEST_P(DcSctpSocketParametrizedTest, SendsMessagesWithLowLifetime) {
z = MaybeHandoverSocket(std::move(z));
// Mock that the time always goes forward.
- TimeMs now(0);
- EXPECT_CALL(a.cb, TimeMillis).WillRepeatedly([&]() {
- now += DurationMs(3);
+ Timestamp now = Timestamp::Zero();
+ EXPECT_CALL(a.cb, Now).WillRepeatedly([&]() {
+ now += TimeDelta::Millis(3);
return now;
});
- EXPECT_CALL(z->cb, TimeMillis).WillRepeatedly([&]() {
- now += DurationMs(3);
+ EXPECT_CALL(z->cb, Now).WillRepeatedly([&]() {
+ now += TimeDelta::Millis(3);
return now;
});
@@ -1592,7 +1599,7 @@ TEST_P(DcSctpSocketParametrizedTest, SendsMessagesWithLowLifetime) {
EXPECT_FALSE(z->cb.ConsumeReceivedMessage().has_value());
// Validate that the sockets really make the time move forward.
- EXPECT_GE(*now, kIterations * 2);
+ EXPECT_GE(now.ms(), kIterations * 2);
MaybeHandoverSocketAndSendMessage(a, std::move(z));
}
@@ -1614,13 +1621,13 @@ TEST_P(DcSctpSocketParametrizedTest,
lifetime_1.lifetime = DurationMs(1);
// Mock that the time always goes forward.
- TimeMs now(0);
- EXPECT_CALL(a.cb, TimeMillis).WillRepeatedly([&]() {
- now += DurationMs(3);
+ Timestamp now = Timestamp::Zero();
+ EXPECT_CALL(a.cb, Now).WillRepeatedly([&]() {
+ now += TimeDelta::Millis(3);
return now;
});
- EXPECT_CALL(z->cb, TimeMillis).WillRepeatedly([&]() {
- now += DurationMs(3);
+ EXPECT_CALL(z->cb, Now).WillRepeatedly([&]() {
+ now += TimeDelta::Millis(3);
return now;
});
@@ -1944,7 +1951,7 @@ TEST(DcSctpSocketTest, RxAndTxPacketMetricsIncrease) {
EXPECT_EQ(z.socket.GetMetrics()->rx_messages_count, 2u);
// Delayed sack
- AdvanceTime(a, z, a.options.delayed_ack_max_timeout);
+ AdvanceTime(a, z, a.options.delayed_ack_max_timeout.ToTimeDelta());
a.socket.ReceivePacket(z.cb.ConsumeSentPacket()); // SACK
EXPECT_EQ(a.socket.GetMetrics()->unack_data_count, 0u);
@@ -1981,7 +1988,7 @@ TEST(DcSctpSocketTest, RetransmissionMetricsAreSetForNormalRetransmit) {
a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), payload), kSendOptions);
a.cb.ConsumeSentPacket();
- AdvanceTime(a, z, a.options.rto_initial);
+ AdvanceTime(a, z, a.options.rto_initial.ToTimeDelta());
ExchangeMessages(a, z);
EXPECT_EQ(a.socket.GetMetrics()->rtx_packets_count, 1u);
@@ -2185,7 +2192,7 @@ TEST_P(DcSctpSocketParametrizedTest, CanLoseFirstOrderedMessage) {
// First DATA is lost, and retransmission timer will delete it.
a.cb.ConsumeSentPacket();
- AdvanceTime(a, *z, a.options.rto_initial);
+ AdvanceTime(a, *z, a.options.rto_initial.ToTimeDelta());
ExchangeMessages(a, *z);
// Send a second message (SID=0, SSN=1).
@@ -2574,7 +2581,7 @@ TEST(DcSctpSocketTest, LifecycleEventsAreGeneratedForAckedMessages) {
EXPECT_CALL(a.cb, OnLifecycleEnd(LifecycleId(42)));
ExchangeMessages(a, z);
// In case of delayed ack.
- AdvanceTime(a, z, a.options.delayed_ack_max_timeout);
+ AdvanceTime(a, z, a.options.delayed_ack_max_timeout.ToTimeDelta());
ExchangeMessages(a, z);
EXPECT_THAT(GetReceivedMessagePpids(z), ElementsAre(101, 102, 103));
@@ -2617,15 +2624,15 @@ TEST(DcSctpSocketTest, LifecycleEventsForFailMaxRetransmissions) {
ExchangeMessages(a, z);
// Handle delayed SACK.
- AdvanceTime(a, z, a.options.delayed_ack_max_timeout);
+ AdvanceTime(a, z, a.options.delayed_ack_max_timeout.ToTimeDelta());
ExchangeMessages(a, z);
// The chunk is now NACKed. Let the RTO expire, to discard the message.
- AdvanceTime(a, z, a.options.rto_initial);
+ AdvanceTime(a, z, a.options.rto_initial.ToTimeDelta());
ExchangeMessages(a, z);
// Handle delayed SACK.
- AdvanceTime(a, z, a.options.delayed_ack_max_timeout);
+ AdvanceTime(a, z, a.options.delayed_ack_max_timeout.ToTimeDelta());
ExchangeMessages(a, z);
EXPECT_THAT(GetReceivedMessagePpids(z), ElementsAre(51, 53));
@@ -2672,7 +2679,7 @@ TEST(DcSctpSocketTest, LifecycleEventsForExpiredMessageWithLifetimeLimit) {
.lifecycle_id = LifecycleId(1),
});
- AdvanceTime(a, z, DurationMs(200));
+ AdvanceTime(a, z, TimeDelta::Millis(200));
EXPECT_CALL(a.cb, OnLifecycleMessageExpired(LifecycleId(1),
/*maybe_delivered=*/false));
@@ -2769,7 +2776,7 @@ TEST(DcSctpSocketTest, ResetStreamsDeferred) {
// Z sent "in progress", which will make A buffer packets until it's sure
// that the reconfiguration has been applied. A will retry - wait for that.
- AdvanceTime(a, z, a.options.rto_initial);
+ AdvanceTime(a, z, a.options.rto_initial.ToTimeDelta());
auto reconfig2 = a.cb.ConsumeSentPacket();
EXPECT_THAT(reconfig2, HasChunks(ElementsAre(IsReConfig(HasParameters(
@@ -3018,7 +3025,7 @@ TEST(DcSctpSocketTest, HandlesForwardTsnOutOfOrderWithStreamResetting) {
HasChunks(ElementsAre(
IsDataChunk(AllOf(Property(&DataChunk::ssn, SSN(0)),
Property(&DataChunk::ppid, PPID(51)))))));
- AdvanceTime(a, z, a.options.rto_initial);
+ AdvanceTime(a, z, a.options.rto_initial.ToTimeDelta());
auto fwd_tsn_packet = a.cb.ConsumeSentPacket();
EXPECT_THAT(fwd_tsn_packet,
diff --git a/third_party/libwebrtc/net/dcsctp/socket/heartbeat_handler.cc b/third_party/libwebrtc/net/dcsctp/socket/heartbeat_handler.cc
index 902dff962f..31211e0d13 100644
--- a/third_party/libwebrtc/net/dcsctp/socket/heartbeat_handler.cc
+++ b/third_party/libwebrtc/net/dcsctp/socket/heartbeat_handler.cc
@@ -21,6 +21,7 @@
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
#include "api/array_view.h"
+#include "api/units/time_delta.h"
#include "net/dcsctp/packet/bounded_byte_reader.h"
#include "net/dcsctp/packet/bounded_byte_writer.h"
#include "net/dcsctp/packet/chunk/heartbeat_ack_chunk.h"
@@ -35,6 +36,8 @@
#include "rtc_base/logging.h"
namespace dcsctp {
+using ::webrtc::TimeDelta;
+using ::webrtc::Timestamp;
// This is stored (in serialized form) as HeartbeatInfoParameter sent in
// HeartbeatRequestChunk and received back in HeartbeatAckChunk. It should be
@@ -49,11 +52,11 @@ class HeartbeatInfo {
static constexpr size_t kBufferSize = sizeof(uint64_t);
static_assert(kBufferSize == 8, "Unexpected buffer size");
- explicit HeartbeatInfo(TimeMs created_at) : created_at_(created_at) {}
+ explicit HeartbeatInfo(Timestamp created_at) : created_at_(created_at) {}
std::vector<uint8_t> Serialize() {
- uint32_t high_bits = static_cast<uint32_t>(*created_at_ >> 32);
- uint32_t low_bits = static_cast<uint32_t>(*created_at_);
+ uint32_t high_bits = static_cast<uint32_t>(created_at_.ms() >> 32);
+ uint32_t low_bits = static_cast<uint32_t>(created_at_.ms());
std::vector<uint8_t> data(kBufferSize);
BoundedByteWriter<kBufferSize> writer(data);
@@ -75,13 +78,13 @@ class HeartbeatInfo {
uint32_t low_bits = reader.Load32<4>();
uint64_t created_at = static_cast<uint64_t>(high_bits) << 32 | low_bits;
- return HeartbeatInfo(TimeMs(created_at));
+ return HeartbeatInfo(Timestamp::Millis(created_at));
}
- TimeMs created_at() const { return created_at_; }
+ Timestamp created_at() const { return created_at_; }
private:
- const TimeMs created_at_;
+ const Timestamp created_at_;
};
HeartbeatHandler::HeartbeatHandler(absl::string_view log_prefix,
@@ -91,17 +94,18 @@ HeartbeatHandler::HeartbeatHandler(absl::string_view log_prefix,
: log_prefix_(log_prefix),
ctx_(context),
timer_manager_(timer_manager),
- interval_duration_(options.heartbeat_interval),
+ interval_duration_(options.heartbeat_interval.ToTimeDelta()),
interval_duration_should_include_rtt_(
options.heartbeat_interval_include_rtt),
interval_timer_(timer_manager_->CreateTimer(
"heartbeat-interval",
absl::bind_front(&HeartbeatHandler::OnIntervalTimerExpiry, this),
- TimerOptions(interval_duration_, TimerBackoffAlgorithm::kFixed))),
+ TimerOptions(interval_duration_,
+ TimerBackoffAlgorithm::kFixed))),
timeout_timer_(timer_manager_->CreateTimer(
"heartbeat-timeout",
absl::bind_front(&HeartbeatHandler::OnTimeoutTimerExpiry, this),
- TimerOptions(options.rto_initial,
+ TimerOptions(options.rto_initial.ToTimeDelta(),
TimerBackoffAlgorithm::kExponential,
/*max_restarts=*/0))) {
// The interval timer must always be running as long as the association is up.
@@ -109,7 +113,7 @@ HeartbeatHandler::HeartbeatHandler(absl::string_view log_prefix,
}
void HeartbeatHandler::RestartTimer() {
- if (interval_duration_ == DurationMs(0)) {
+ if (interval_duration_.IsZero()) {
// Heartbeating has been disabled.
return;
}
@@ -117,7 +121,8 @@ void HeartbeatHandler::RestartTimer() {
if (interval_duration_should_include_rtt_) {
// The RTT should be used, but it's not easy accessible. The RTO will
// suffice.
- interval_timer_->set_duration(interval_duration_ + ctx_->current_rto());
+ interval_timer_->set_duration(
+ interval_duration_ + ctx_->current_rto());
} else {
interval_timer_->set_duration(interval_duration_);
}
@@ -153,8 +158,8 @@ void HeartbeatHandler::HandleHeartbeatAck(HeartbeatAckChunk chunk) {
return;
}
- TimeMs now = ctx_->callbacks().TimeMillis();
- if (info->created_at() > TimeMs(0) && info->created_at() <= now) {
+ Timestamp now = ctx_->callbacks().Now();
+ if (info->created_at() > Timestamp::Zero() && info->created_at() <= now) {
ctx_->ObserveRTT(now - info->created_at());
}
@@ -164,13 +169,13 @@ void HeartbeatHandler::HandleHeartbeatAck(HeartbeatAckChunk chunk) {
ctx_->ClearTxErrorCounter();
}
-absl::optional<DurationMs> HeartbeatHandler::OnIntervalTimerExpiry() {
+TimeDelta HeartbeatHandler::OnIntervalTimerExpiry() {
if (ctx_->is_connection_established()) {
- HeartbeatInfo info(ctx_->callbacks().TimeMillis());
+ HeartbeatInfo info(ctx_->callbacks().Now());
timeout_timer_->set_duration(ctx_->current_rto());
timeout_timer_->Start();
RTC_DLOG(LS_INFO) << log_prefix_ << "Sending HEARTBEAT with timeout "
- << *timeout_timer_->duration();
+ << webrtc::ToString(timeout_timer_->duration());
Parameters parameters = Parameters::Builder()
.Add(HeartbeatInfoParameter(info.Serialize()))
@@ -183,14 +188,14 @@ absl::optional<DurationMs> HeartbeatHandler::OnIntervalTimerExpiry() {
<< log_prefix_
<< "Will not send HEARTBEAT when connection not established";
}
- return absl::nullopt;
+ return TimeDelta::Zero();
}
-absl::optional<DurationMs> HeartbeatHandler::OnTimeoutTimerExpiry() {
+TimeDelta HeartbeatHandler::OnTimeoutTimerExpiry() {
// Note that the timeout timer is not restarted. It will be started again when
// the interval timer expires.
RTC_DCHECK(!timeout_timer_->is_running());
ctx_->IncrementTxErrorCounter("HEARTBEAT timeout");
- return absl::nullopt;
+ return TimeDelta::Zero();
}
} // namespace dcsctp
diff --git a/third_party/libwebrtc/net/dcsctp/socket/heartbeat_handler.h b/third_party/libwebrtc/net/dcsctp/socket/heartbeat_handler.h
index 318b02955b..ac58b97a64 100644
--- a/third_party/libwebrtc/net/dcsctp/socket/heartbeat_handler.h
+++ b/third_party/libwebrtc/net/dcsctp/socket/heartbeat_handler.h
@@ -50,14 +50,14 @@ class HeartbeatHandler {
void HandleHeartbeatAck(HeartbeatAckChunk chunk);
private:
- absl::optional<DurationMs> OnIntervalTimerExpiry();
- absl::optional<DurationMs> OnTimeoutTimerExpiry();
+ webrtc::TimeDelta OnIntervalTimerExpiry();
+ webrtc::TimeDelta OnTimeoutTimerExpiry();
const absl::string_view log_prefix_;
Context* ctx_;
TimerManager* timer_manager_;
// The time for a connection to be idle before a heartbeat is sent.
- const DurationMs interval_duration_;
+ const webrtc::TimeDelta interval_duration_;
// Adding RTT to the duration will add some jitter, which is good in
// production, but less good in unit tests, which is why it can be disabled.
const bool interval_duration_should_include_rtt_;
diff --git a/third_party/libwebrtc/net/dcsctp/socket/heartbeat_handler_test.cc b/third_party/libwebrtc/net/dcsctp/socket/heartbeat_handler_test.cc
index d573192440..4475527322 100644
--- a/third_party/libwebrtc/net/dcsctp/socket/heartbeat_handler_test.cc
+++ b/third_party/libwebrtc/net/dcsctp/socket/heartbeat_handler_test.cc
@@ -30,6 +30,7 @@ using ::testing::IsEmpty;
using ::testing::NiceMock;
using ::testing::Return;
using ::testing::SizeIs;
+using ::webrtc::TimeDelta;
constexpr DurationMs kHeartbeatInterval = DurationMs(30'000);
@@ -51,7 +52,7 @@ class HeartbeatHandlerTestBase : public testing::Test {
}),
handler_("log: ", options_, &context_, &timer_manager_) {}
- void AdvanceTime(DurationMs duration) {
+ void AdvanceTime(webrtc::TimeDelta duration) {
callbacks_.AdvanceTime(duration);
for (;;) {
absl::optional<TimeoutID> timeout_id = callbacks_.GetNextExpiredTimeout();
@@ -80,7 +81,7 @@ class DisabledHeartbeatHandlerTest : public HeartbeatHandlerTestBase {
};
TEST_F(HeartbeatHandlerTest, HasRunningHeartbeatIntervalTimer) {
- AdvanceTime(options_.heartbeat_interval);
+ AdvanceTime(options_.heartbeat_interval.ToTimeDelta());
// Validate that a heartbeat request was sent.
std::vector<uint8_t> payload = callbacks_.ConsumeSentPacket();
@@ -119,7 +120,7 @@ TEST_F(HeartbeatHandlerTest, RepliesToHeartbeatRequests) {
}
TEST_F(HeartbeatHandlerTest, SendsHeartbeatRequestsOnIdleChannel) {
- AdvanceTime(options_.heartbeat_interval);
+ AdvanceTime(options_.heartbeat_interval.ToTimeDelta());
// Grab the request, and make a response.
std::vector<uint8_t> payload = callbacks_.ConsumeSentPacket();
@@ -134,7 +135,7 @@ TEST_F(HeartbeatHandlerTest, SendsHeartbeatRequestsOnIdleChannel) {
HeartbeatAckChunk ack(std::move(req).extract_parameters());
// Respond a while later. This RTT will be measured by the handler
- constexpr DurationMs rtt(313);
+ constexpr TimeDelta rtt = TimeDelta::Millis(313);
EXPECT_CALL(context_, ObserveRTT(rtt)).Times(1);
@@ -143,7 +144,7 @@ TEST_F(HeartbeatHandlerTest, SendsHeartbeatRequestsOnIdleChannel) {
}
TEST_F(HeartbeatHandlerTest, DoesntObserveInvalidHeartbeats) {
- AdvanceTime(options_.heartbeat_interval);
+ AdvanceTime(options_.heartbeat_interval.ToTimeDelta());
// Grab the request, and make a response.
std::vector<uint8_t> payload = callbacks_.ConsumeSentPacket();
@@ -161,15 +162,15 @@ TEST_F(HeartbeatHandlerTest, DoesntObserveInvalidHeartbeats) {
// Go backwards in time - which make the HEARTBEAT-ACK have an invalid
// timestamp in it, as it will be in the future.
- callbacks_.AdvanceTime(DurationMs(-100));
+ callbacks_.AdvanceTime(TimeDelta::Millis(-100));
handler_.HandleHeartbeatAck(std::move(ack));
}
TEST_F(HeartbeatHandlerTest, IncreasesErrorIfNotAckedInTime) {
- DurationMs rto(105);
+ TimeDelta rto = TimeDelta::Millis(105);
EXPECT_CALL(context_, current_rto).WillOnce(Return(rto));
- AdvanceTime(options_.heartbeat_interval);
+ AdvanceTime(options_.heartbeat_interval.ToTimeDelta());
// Validate that a request was sent.
EXPECT_THAT(callbacks_.ConsumeSentPacket(), Not(IsEmpty()));
@@ -179,7 +180,7 @@ TEST_F(HeartbeatHandlerTest, IncreasesErrorIfNotAckedInTime) {
}
TEST_F(DisabledHeartbeatHandlerTest, IsReallyDisabled) {
- AdvanceTime(options_.heartbeat_interval);
+ AdvanceTime(options_.heartbeat_interval.ToTimeDelta());
// Validate that a request was NOT sent.
EXPECT_THAT(callbacks_.ConsumeSentPacket(), IsEmpty());
diff --git a/third_party/libwebrtc/net/dcsctp/socket/mock_context.h b/third_party/libwebrtc/net/dcsctp/socket/mock_context.h
index 88e71d1b35..bbd9cd17d9 100644
--- a/third_party/libwebrtc/net/dcsctp/socket/mock_context.h
+++ b/third_party/libwebrtc/net/dcsctp/socket/mock_context.h
@@ -40,7 +40,8 @@ class MockContext : public Context {
ON_CALL(*this, peer_initial_tsn)
.WillByDefault(testing::Return(PeerInitialTsn()));
ON_CALL(*this, callbacks).WillByDefault(testing::ReturnRef(callbacks_));
- ON_CALL(*this, current_rto).WillByDefault(testing::Return(DurationMs(123)));
+ ON_CALL(*this, current_rto)
+ .WillByDefault(testing::Return(webrtc::TimeDelta::Millis(123)));
ON_CALL(*this, Send).WillByDefault([this](SctpPacket::Builder& builder) {
callbacks_.SendPacketWithStatus(builder.Build());
});
@@ -51,8 +52,8 @@ class MockContext : public Context {
MOCK_METHOD(TSN, peer_initial_tsn, (), (const, override));
MOCK_METHOD(DcSctpSocketCallbacks&, callbacks, (), (const, override));
- MOCK_METHOD(void, ObserveRTT, (DurationMs rtt_ms), (override));
- MOCK_METHOD(DurationMs, current_rto, (), (const, override));
+ MOCK_METHOD(void, ObserveRTT, (webrtc::TimeDelta rtt), (override));
+ MOCK_METHOD(webrtc::TimeDelta, current_rto, (), (const, override));
MOCK_METHOD(bool,
IncrementTxErrorCounter,
(absl::string_view reason),
diff --git a/third_party/libwebrtc/net/dcsctp/socket/mock_dcsctp_socket_callbacks.h b/third_party/libwebrtc/net/dcsctp/socket/mock_dcsctp_socket_callbacks.h
index 150c1b9fa5..972e547b12 100644
--- a/third_party/libwebrtc/net/dcsctp/socket/mock_dcsctp_socket_callbacks.h
+++ b/third_party/libwebrtc/net/dcsctp/socket/mock_dcsctp_socket_callbacks.h
@@ -80,7 +80,7 @@ class MockDcSctpSocketCallbacks : public DcSctpSocketCallbacks {
<< log_prefix_ << "Socket abort: " << ToString(error) << "; "
<< message;
});
- ON_CALL(*this, TimeMillis).WillByDefault([this]() { return now_; });
+ ON_CALL(*this, Now).WillByDefault([this]() { return now_; });
}
MOCK_METHOD(SendPacketStatus,
@@ -94,7 +94,7 @@ class MockDcSctpSocketCallbacks : public DcSctpSocketCallbacks {
return timeout_manager_.CreateTimeout();
}
- MOCK_METHOD(TimeMs, TimeMillis, (), (override));
+ MOCK_METHOD(webrtc::Timestamp, Now, (), (override));
uint32_t GetRandomInt(uint32_t low, uint32_t high) override {
return random_.Rand(low, high);
}
@@ -159,20 +159,20 @@ class MockDcSctpSocketCallbacks : public DcSctpSocketCallbacks {
return ret;
}
- void AdvanceTime(DurationMs duration_ms) { now_ = now_ + duration_ms; }
- void SetTime(TimeMs now) { now_ = now; }
+ void AdvanceTime(webrtc::TimeDelta duration) { now_ = now_ + duration; }
+ void SetTime(webrtc::Timestamp now) { now_ = now; }
absl::optional<TimeoutID> GetNextExpiredTimeout() {
return timeout_manager_.GetNextExpiredTimeout();
}
- DurationMs GetTimeToNextTimeout() const {
+ webrtc::TimeDelta GetTimeToNextTimeout() const {
return timeout_manager_.GetTimeToNextTimeout();
}
private:
const std::string log_prefix_;
- TimeMs now_ = TimeMs(0);
+ webrtc::Timestamp now_ = webrtc::Timestamp::Zero();
webrtc::Random random_;
FakeTimeoutManager timeout_manager_;
std::deque<std::vector<uint8_t>> sent_packets_;
diff --git a/third_party/libwebrtc/net/dcsctp/socket/stream_reset_handler.cc b/third_party/libwebrtc/net/dcsctp/socket/stream_reset_handler.cc
index 2094309afe..fafb9933e5 100644
--- a/third_party/libwebrtc/net/dcsctp/socket/stream_reset_handler.cc
+++ b/third_party/libwebrtc/net/dcsctp/socket/stream_reset_handler.cc
@@ -16,8 +16,8 @@
#include "absl/types/optional.h"
#include "api/array_view.h"
+#include "api/units/time_delta.h"
#include "net/dcsctp/common/internal_types.h"
-#include "net/dcsctp/common/str_join.h"
#include "net/dcsctp/packet/chunk/reconfig_chunk.h"
#include "net/dcsctp/packet/parameter/add_incoming_streams_request_parameter.h"
#include "net/dcsctp/packet/parameter/add_outgoing_streams_request_parameter.h"
@@ -35,9 +35,11 @@
#include "net/dcsctp/timer/timer.h"
#include "net/dcsctp/tx/retransmission_queue.h"
#include "rtc_base/logging.h"
+#include "rtc_base/strings/str_join.h"
namespace dcsctp {
namespace {
+using ::webrtc::TimeDelta;
using ResponseResult = ReconfigurationResponseParameter::Result;
bool DescriptorsAre(const std::vector<ParameterDescriptor>& c,
@@ -347,13 +349,13 @@ void StreamResetHandler::ResetStreams(
}
}
-absl::optional<DurationMs> StreamResetHandler::OnReconfigTimerExpiry() {
+TimeDelta StreamResetHandler::OnReconfigTimerExpiry() {
if (current_request_->has_been_sent()) {
// There is an outstanding request, which timed out while waiting for a
// response.
if (!ctx_->IncrementTxErrorCounter("RECONFIG timeout")) {
// Timed out. The connection will close after processing the timers.
- return absl::nullopt;
+ return TimeDelta::Zero();
}
} else {
// There is no outstanding request, but there is a prepared one. This means
diff --git a/third_party/libwebrtc/net/dcsctp/socket/stream_reset_handler.h b/third_party/libwebrtc/net/dcsctp/socket/stream_reset_handler.h
index c335130175..77e8f3bd97 100644
--- a/third_party/libwebrtc/net/dcsctp/socket/stream_reset_handler.h
+++ b/third_party/libwebrtc/net/dcsctp/socket/stream_reset_handler.h
@@ -20,6 +20,7 @@
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
#include "api/array_view.h"
+#include "api/units/time_delta.h"
#include "net/dcsctp/common/internal_types.h"
#include "net/dcsctp/packet/chunk/reconfig_chunk.h"
#include "net/dcsctp/packet/parameter/incoming_ssn_reset_request_parameter.h"
@@ -80,7 +81,7 @@ class StreamResetHandler {
reconfig_timer_(timer_manager->CreateTimer(
"re-config",
absl::bind_front(&StreamResetHandler::OnReconfigTimerExpiry, this),
- TimerOptions(DurationMs(0)))),
+ TimerOptions(webrtc::TimeDelta::Zero()))),
next_outgoing_req_seq_nbr_(
handover_state
? ReconfigRequestSN(handover_state->tx.next_reset_req_sn)
@@ -211,7 +212,7 @@ class StreamResetHandler {
void HandleResponse(const ParameterDescriptor& descriptor);
// Expiration handler for the Reconfig timer.
- absl::optional<DurationMs> OnReconfigTimerExpiry();
+ webrtc::TimeDelta OnReconfigTimerExpiry();
const absl::string_view log_prefix_;
Context* ctx_;
diff --git a/third_party/libwebrtc/net/dcsctp/socket/stream_reset_handler_test.cc b/third_party/libwebrtc/net/dcsctp/socket/stream_reset_handler_test.cc
index 091d717f8a..e675c9bcef 100644
--- a/third_party/libwebrtc/net/dcsctp/socket/stream_reset_handler_test.cc
+++ b/third_party/libwebrtc/net/dcsctp/socket/stream_reset_handler_test.cc
@@ -48,6 +48,7 @@ using ::testing::Property;
using ::testing::Return;
using ::testing::SizeIs;
using ::testing::UnorderedElementsAre;
+using ::webrtc::TimeDelta;
using ResponseResult = ReconfigurationResponseParameter::Result;
using SkippedStream = AnyForwardTsnChunk::SkippedStream;
@@ -57,7 +58,7 @@ constexpr TSN kPeerInitialTsn = MockContext::PeerInitialTsn();
constexpr ReconfigRequestSN kPeerInitialReqSn =
ReconfigRequestSN(*kPeerInitialTsn);
constexpr uint32_t kArwnd = 131072;
-constexpr DurationMs kRto = DurationMs(250);
+constexpr TimeDelta kRto = TimeDelta::Millis(250);
constexpr std::array<uint8_t, 4> kShortPayload = {1, 2, 3, 4};
@@ -97,12 +98,12 @@ class StreamResetHandlerTest : public testing::Test {
}),
delayed_ack_timer_(timer_manager_.CreateTimer(
"test/delayed_ack",
- []() { return absl::nullopt; },
- TimerOptions(DurationMs(0)))),
+ []() { return TimeDelta::Zero(); },
+ TimerOptions(TimeDelta::Zero()))),
t3_rtx_timer_(timer_manager_.CreateTimer(
"test/t3_rtx",
- []() { return absl::nullopt; },
- TimerOptions(DurationMs(0)))),
+ []() { return TimeDelta::Zero(); },
+ TimerOptions(TimeDelta::Zero()))),
data_tracker_(std::make_unique<DataTracker>("log: ",
delayed_ack_timer_.get(),
kPeerInitialTsn)),
@@ -115,7 +116,7 @@ class StreamResetHandlerTest : public testing::Test {
kMyInitialTsn,
kArwnd,
producer_,
- [](DurationMs rtt_ms) {},
+ [](TimeDelta rtt) {},
[]() {},
*t3_rtx_timer_,
DcSctpOptions())),
@@ -129,8 +130,8 @@ class StreamResetHandlerTest : public testing::Test {
EXPECT_CALL(ctx_, current_rto).WillRepeatedly(Return(kRto));
}
- void AdvanceTime(DurationMs duration) {
- callbacks_.AdvanceTime(kRto);
+ void AdvanceTime(TimeDelta duration) {
+ callbacks_.AdvanceTime(duration);
for (;;) {
absl::optional<TimeoutID> timeout_id = callbacks_.GetNextExpiredTimeout();
if (!timeout_id.has_value()) {
@@ -204,8 +205,8 @@ class StreamResetHandlerTest : public testing::Test {
std::make_unique<ReassemblyQueue>("log: ", kPeerInitialTsn, kArwnd);
reasm_->RestoreFromState(state);
retransmission_queue_ = std::make_unique<RetransmissionQueue>(
- "", &callbacks_, kMyInitialTsn, kArwnd, producer_,
- [](DurationMs rtt_ms) {}, []() {}, *t3_rtx_timer_, DcSctpOptions(),
+ "", &callbacks_, kMyInitialTsn, kArwnd, producer_, [](TimeDelta rtt) {},
+ []() {}, *t3_rtx_timer_, DcSctpOptions(),
/*supports_partial_reliability=*/true,
/*use_message_interleaving=*/false);
retransmission_queue_->RestoreFromState(state);
diff --git a/third_party/libwebrtc/net/dcsctp/socket/transmission_control_block.cc b/third_party/libwebrtc/net/dcsctp/socket/transmission_control_block.cc
index 0621b48e80..c6c8861e1f 100644
--- a/third_party/libwebrtc/net/dcsctp/socket/transmission_control_block.cc
+++ b/third_party/libwebrtc/net/dcsctp/socket/transmission_control_block.cc
@@ -17,6 +17,7 @@
#include <vector>
#include "absl/types/optional.h"
+#include "api/units/time_delta.h"
#include "net/dcsctp/packet/chunk/data_chunk.h"
#include "net/dcsctp/packet/chunk/forward_tsn_chunk.h"
#include "net/dcsctp/packet/chunk/idata_chunk.h"
@@ -37,6 +38,8 @@
#include "rtc_base/strings/string_builder.h"
namespace dcsctp {
+using ::webrtc::TimeDelta;
+using ::webrtc::Timestamp;
TransmissionControlBlock::TransmissionControlBlock(
TimerManager& timer_manager,
@@ -61,20 +64,20 @@ TransmissionControlBlock::TransmissionControlBlock(
t3_rtx_(timer_manager_.CreateTimer(
"t3-rtx",
absl::bind_front(&TransmissionControlBlock::OnRtxTimerExpiry, this),
- TimerOptions(options.rto_initial,
+ TimerOptions(options.rto_initial.ToTimeDelta(),
TimerBackoffAlgorithm::kExponential,
/*max_restarts=*/absl::nullopt,
options.max_timer_backoff_duration.has_value()
- ? *options.max_timer_backoff_duration
- : DurationMs::InfiniteDuration()))),
+ ? options.max_timer_backoff_duration->ToTimeDelta()
+ : TimeDelta::PlusInfinity()))),
delayed_ack_timer_(timer_manager_.CreateTimer(
"delayed-ack",
absl::bind_front(&TransmissionControlBlock::OnDelayedAckTimerExpiry,
this),
- TimerOptions(options.delayed_ack_max_timeout,
+ TimerOptions(options.delayed_ack_max_timeout.ToTimeDelta(),
TimerBackoffAlgorithm::kExponential,
/*max_restarts=*/0,
- /*max_backoff_duration=*/DurationMs::InfiniteDuration(),
+ /*max_backoff_duration=*/TimeDelta::PlusInfinity(),
webrtc::TaskQueueBase::DelayPrecision::kHigh))),
my_verification_tag_(my_verification_tag),
my_initial_tsn_(my_initial_tsn),
@@ -112,21 +115,22 @@ TransmissionControlBlock::TransmissionControlBlock(
send_queue.EnableMessageInterleaving(capabilities.message_interleaving);
}
-void TransmissionControlBlock::ObserveRTT(DurationMs rtt) {
- DurationMs prev_rto = rto_.rto();
+void TransmissionControlBlock::ObserveRTT(TimeDelta rtt) {
+ TimeDelta prev_rto = rto_.rto();
rto_.ObserveRTT(rtt);
- RTC_DLOG(LS_VERBOSE) << log_prefix_ << "new rtt=" << *rtt
- << ", srtt=" << *rto_.srtt() << ", rto=" << *rto_.rto()
- << " (" << *prev_rto << ")";
+ RTC_DLOG(LS_VERBOSE) << log_prefix_ << "new rtt=" << webrtc::ToString(rtt)
+ << ", srtt=" << webrtc::ToString(rto_.srtt())
+ << ", rto=" << webrtc::ToString(rto_.rto()) << " ("
+ << webrtc::ToString(prev_rto) << ")";
t3_rtx_->set_duration(rto_.rto());
- DurationMs delayed_ack_tmo =
- std::min(rto_.rto() * 0.5, options_.delayed_ack_max_timeout);
+ TimeDelta delayed_ack_tmo = std::min(
+ rto_.rto() * 0.5, options_.delayed_ack_max_timeout.ToTimeDelta());
delayed_ack_timer_->set_duration(delayed_ack_tmo);
}
-absl::optional<DurationMs> TransmissionControlBlock::OnRtxTimerExpiry() {
- TimeMs now = callbacks_.TimeMillis();
+TimeDelta TransmissionControlBlock::OnRtxTimerExpiry() {
+ Timestamp now = callbacks_.Now();
RTC_DLOG(LS_INFO) << log_prefix_ << "Timer " << t3_rtx_->name()
<< " has expired";
if (cookie_echo_chunk_.has_value()) {
@@ -139,13 +143,13 @@ absl::optional<DurationMs> TransmissionControlBlock::OnRtxTimerExpiry() {
SendBufferedPackets(now);
}
}
- return absl::nullopt;
+ return TimeDelta::Zero();
}
-absl::optional<DurationMs> TransmissionControlBlock::OnDelayedAckTimerExpiry() {
+TimeDelta TransmissionControlBlock::OnDelayedAckTimerExpiry() {
data_tracker_.HandleDelayedAckTimerExpiry();
MaybeSendSack();
- return absl::nullopt;
+ return TimeDelta::Zero();
}
void TransmissionControlBlock::MaybeSendSack() {
@@ -158,7 +162,7 @@ void TransmissionControlBlock::MaybeSendSack() {
}
void TransmissionControlBlock::MaybeSendForwardTsn(SctpPacket::Builder& builder,
- TimeMs now) {
+ Timestamp now) {
if (now >= limit_forward_tsn_until_ &&
retransmission_queue_.ShouldSendForwardTsn(now)) {
if (capabilities_.message_interleaving) {
@@ -173,7 +177,8 @@ void TransmissionControlBlock::MaybeSendForwardTsn(SctpPacket::Builder& builder,
// sending a duplicate FORWARD TSN."
// "Any delay applied to the sending of FORWARD TSN chunk SHOULD NOT exceed
// 200ms and MUST NOT exceed 500ms".
- limit_forward_tsn_until_ = now + std::min(DurationMs(200), rto_.srtt());
+ limit_forward_tsn_until_ =
+ now + std::min(TimeDelta::Millis(200), rto_.srtt());
}
}
@@ -205,7 +210,7 @@ void TransmissionControlBlock::MaybeSendFastRetransmit() {
}
void TransmissionControlBlock::SendBufferedPackets(SctpPacket::Builder& builder,
- TimeMs now) {
+ Timestamp now) {
for (int packet_idx = 0;
packet_idx < options_.max_burst && retransmission_queue_.can_send_data();
++packet_idx) {
diff --git a/third_party/libwebrtc/net/dcsctp/socket/transmission_control_block.h b/third_party/libwebrtc/net/dcsctp/socket/transmission_control_block.h
index 46a39d5a7b..f8b2445525 100644
--- a/third_party/libwebrtc/net/dcsctp/socket/transmission_control_block.h
+++ b/third_party/libwebrtc/net/dcsctp/socket/transmission_control_block.h
@@ -67,8 +67,8 @@ class TransmissionControlBlock : public Context {
TSN my_initial_tsn() const override { return my_initial_tsn_; }
TSN peer_initial_tsn() const override { return peer_initial_tsn_; }
DcSctpSocketCallbacks& callbacks() const override { return callbacks_; }
- void ObserveRTT(DurationMs rtt) override;
- DurationMs current_rto() const override { return rto_.rto(); }
+ void ObserveRTT(webrtc::TimeDelta rtt) override;
+ webrtc::TimeDelta current_rto() const override { return rto_.rto(); }
bool IncrementTxErrorCounter(absl::string_view reason) override {
return tx_error_counter_.Increment(reason);
}
@@ -91,7 +91,7 @@ class TransmissionControlBlock : public Context {
StreamResetHandler& stream_reset_handler() { return stream_reset_handler_; }
HeartbeatHandler& heartbeat_handler() { return heartbeat_handler_; }
size_t cwnd() const { return retransmission_queue_.cwnd(); }
- DurationMs current_srtt() const { return rto_.srtt(); }
+ webrtc::TimeDelta current_srtt() const { return rto_.srtt(); }
// Returns this socket's verification tag, set in all packet headers.
VerificationTag my_verification_tag() const { return my_verification_tag_; }
@@ -108,7 +108,7 @@ class TransmissionControlBlock : public Context {
void MaybeSendSack();
// Sends a FORWARD-TSN, if it is needed and allowed (rate-limited).
- void MaybeSendForwardTsn(SctpPacket::Builder& builder, TimeMs now);
+ void MaybeSendForwardTsn(SctpPacket::Builder& builder, webrtc::Timestamp now);
// Will be set while the socket is in kCookieEcho state. In this state, there
// can only be a single packet outstanding, and it must contain the COOKIE
@@ -129,12 +129,12 @@ class TransmissionControlBlock : public Context {
// Fills `builder` (which may already be filled with control chunks) with
// other control and data chunks, and sends packets as much as can be
// allowed by the congestion control algorithm.
- void SendBufferedPackets(SctpPacket::Builder& builder, TimeMs now);
+ void SendBufferedPackets(SctpPacket::Builder& builder, webrtc::Timestamp now);
// As above, but without passing in a builder. If `cookie_echo_chunk_` is
// present, then only one packet will be sent, with this chunk as the first
// chunk.
- void SendBufferedPackets(TimeMs now) {
+ void SendBufferedPackets(webrtc::Timestamp now) {
SctpPacket::Builder builder(peer_verification_tag_, options_);
SendBufferedPackets(builder, now);
}
@@ -149,9 +149,9 @@ class TransmissionControlBlock : public Context {
private:
// Will be called when the retransmission timer (t3-rtx) expires.
- absl::optional<DurationMs> OnRtxTimerExpiry();
+ webrtc::TimeDelta OnRtxTimerExpiry();
// Will be called when the delayed ack timer expires.
- absl::optional<DurationMs> OnDelayedAckTimerExpiry();
+ webrtc::TimeDelta OnDelayedAckTimerExpiry();
const absl::string_view log_prefix_;
const DcSctpOptions options_;
@@ -172,7 +172,7 @@ class TransmissionControlBlock : public Context {
const std::function<bool()> is_connection_established_;
PacketSender& packet_sender_;
// Rate limiting of FORWARD-TSN. Next can be sent at or after this timestamp.
- TimeMs limit_forward_tsn_until_ = TimeMs(0);
+ webrtc::Timestamp limit_forward_tsn_until_ = webrtc::Timestamp::Zero();
RetransmissionTimeout rto_;
RetransmissionErrorCounter tx_error_counter_;
diff --git a/third_party/libwebrtc/net/dcsctp/timer/BUILD.gn b/third_party/libwebrtc/net/dcsctp/timer/BUILD.gn
index d3be1ec872..9dbe11b3ba 100644
--- a/third_party/libwebrtc/net/dcsctp/timer/BUILD.gn
+++ b/third_party/libwebrtc/net/dcsctp/timer/BUILD.gn
@@ -12,6 +12,8 @@ rtc_library("timer") {
deps = [
"../../../api:array_view",
"../../../api/task_queue:task_queue",
+ "../../../api/units:time_delta",
+ "../../../api/units:timestamp",
"../../../rtc_base:checks",
"../../../rtc_base:strong_alias",
"../../../rtc_base/containers:flat_map",
@@ -37,6 +39,7 @@ rtc_library("task_queue_timeout") {
"../../../api/task_queue:pending_task_safety_flag",
"../../../api/task_queue:task_queue",
"../../../api/units:time_delta",
+ "../../../api/units:timestamp",
"../../../rtc_base:checks",
"../../../rtc_base:logging",
"../public:socket",
@@ -59,6 +62,7 @@ if (rtc_include_tests) {
"../../../api:array_view",
"../../../api/task_queue:task_queue",
"../../../api/task_queue/test:mock_task_queue_base",
+ "../../../api/units:time_delta",
"../../../rtc_base:checks",
"../../../rtc_base:gunit_helpers",
"../../../test:test_support",
diff --git a/third_party/libwebrtc/net/dcsctp/timer/fake_timeout.h b/third_party/libwebrtc/net/dcsctp/timer/fake_timeout.h
index 4621b2ce83..cac49287d4 100644
--- a/third_party/libwebrtc/net/dcsctp/timer/fake_timeout.h
+++ b/third_party/libwebrtc/net/dcsctp/timer/fake_timeout.h
@@ -19,6 +19,7 @@
#include "absl/types/optional.h"
#include "api/task_queue/task_queue_base.h"
+#include "api/units/timestamp.h"
#include "net/dcsctp/public/timeout.h"
#include "net/dcsctp/public/types.h"
#include "rtc_base/checks.h"
@@ -29,46 +30,46 @@ namespace dcsctp {
// A timeout used in tests.
class FakeTimeout : public Timeout {
public:
- FakeTimeout(std::function<TimeMs()> get_time,
+ FakeTimeout(std::function<webrtc::Timestamp()> get_time,
std::function<void(FakeTimeout*)> on_delete)
: get_time_(std::move(get_time)), on_delete_(std::move(on_delete)) {}
~FakeTimeout() override { on_delete_(this); }
void Start(DurationMs duration_ms, TimeoutID timeout_id) override {
- RTC_DCHECK(expiry_ == TimeMs::InfiniteFuture());
+ RTC_DCHECK(expiry_.IsPlusInfinity());
timeout_id_ = timeout_id;
- expiry_ = get_time_() + duration_ms;
+ expiry_ = get_time_() + duration_ms.ToTimeDelta();
}
void Stop() override {
- RTC_DCHECK(expiry_ != TimeMs::InfiniteFuture());
- expiry_ = TimeMs::InfiniteFuture();
+ RTC_DCHECK(!expiry_.IsPlusInfinity());
+ expiry_ = webrtc::Timestamp::PlusInfinity();
}
- bool EvaluateHasExpired(TimeMs now) {
+ bool EvaluateHasExpired(webrtc::Timestamp now) {
if (now >= expiry_) {
- expiry_ = TimeMs::InfiniteFuture();
+ expiry_ = webrtc::Timestamp::PlusInfinity();
return true;
}
return false;
}
TimeoutID timeout_id() const { return timeout_id_; }
- TimeMs expiry() const { return expiry_; }
+ webrtc::Timestamp expiry() const { return expiry_; }
private:
- const std::function<TimeMs()> get_time_;
+ const std::function<webrtc::Timestamp()> get_time_;
const std::function<void(FakeTimeout*)> on_delete_;
TimeoutID timeout_id_ = TimeoutID(0);
- TimeMs expiry_ = TimeMs::InfiniteFuture();
+ webrtc::Timestamp expiry_ = webrtc::Timestamp::PlusInfinity();
};
class FakeTimeoutManager {
public:
// The `get_time` function must return the current time, relative to any
// epoch.
- explicit FakeTimeoutManager(std::function<TimeMs()> get_time)
+ explicit FakeTimeoutManager(std::function<webrtc::Timestamp()> get_time)
: get_time_(std::move(get_time)) {}
std::unique_ptr<FakeTimeout> CreateTimeout() {
@@ -89,7 +90,7 @@ class FakeTimeoutManager {
// Timer::is_running_ to false before you operate on the Timer or Timeout
// again.
absl::optional<TimeoutID> GetNextExpiredTimeout() {
- TimeMs now = get_time_();
+ webrtc::Timestamp now = get_time_();
std::vector<TimeoutID> expired_timers;
for (auto& timer : timers_) {
if (timer->EvaluateHasExpired(now)) {
@@ -99,21 +100,21 @@ class FakeTimeoutManager {
return absl::nullopt;
}
- DurationMs GetTimeToNextTimeout() const {
- TimeMs next_expiry = TimeMs::InfiniteFuture();
+ webrtc::TimeDelta GetTimeToNextTimeout() const {
+ webrtc::Timestamp next_expiry = webrtc::Timestamp::PlusInfinity();
for (const FakeTimeout* timer : timers_) {
if (timer->expiry() < next_expiry) {
next_expiry = timer->expiry();
}
}
- TimeMs now = get_time_();
- return next_expiry != TimeMs::InfiniteFuture() && next_expiry >= now
+ webrtc::Timestamp now = get_time_();
+ return !next_expiry.IsPlusInfinity() && next_expiry >= now
? next_expiry - now
- : DurationMs::InfiniteDuration();
+ : webrtc::TimeDelta::PlusInfinity();
}
private:
- const std::function<TimeMs()> get_time_;
+ const std::function<webrtc::Timestamp()> get_time_;
webrtc::flat_set<FakeTimeout*> timers_;
};
diff --git a/third_party/libwebrtc/net/dcsctp/timer/task_queue_timeout.cc b/third_party/libwebrtc/net/dcsctp/timer/task_queue_timeout.cc
index 6c43640d39..7612f98f3a 100644
--- a/third_party/libwebrtc/net/dcsctp/timer/task_queue_timeout.cc
+++ b/third_party/libwebrtc/net/dcsctp/timer/task_queue_timeout.cc
@@ -14,6 +14,8 @@
#include "rtc_base/logging.h"
namespace dcsctp {
+using ::webrtc::TimeDelta;
+using ::webrtc::Timestamp;
TaskQueueTimeoutFactory::TaskQueueTimeout::TaskQueueTimeout(
TaskQueueTimeoutFactory& parent,
@@ -30,8 +32,8 @@ TaskQueueTimeoutFactory::TaskQueueTimeout::~TaskQueueTimeout() {
void TaskQueueTimeoutFactory::TaskQueueTimeout::Start(DurationMs duration_ms,
TimeoutID timeout_id) {
RTC_DCHECK_RUN_ON(&parent_.thread_checker_);
- RTC_DCHECK(timeout_expiration_ == TimeMs::InfiniteFuture());
- timeout_expiration_ = parent_.get_time_() + duration_ms;
+ RTC_DCHECK(timeout_expiration_.IsPlusInfinity());
+ timeout_expiration_ = parent_.Now() + duration_ms.ToTimeDelta();
timeout_id_ = timeout_id;
if (timeout_expiration_ >= posted_task_expiration_) {
@@ -43,7 +45,7 @@ void TaskQueueTimeoutFactory::TaskQueueTimeout::Start(DurationMs duration_ms,
return;
}
- if (posted_task_expiration_ != TimeMs::InfiniteFuture()) {
+ if (!posted_task_expiration_.IsPlusInfinity()) {
RTC_DLOG(LS_VERBOSE) << "New timeout duration is less than scheduled - "
"ghosting old delayed task.";
// There is already a scheduled delayed task, but its expiration time is
@@ -63,10 +65,10 @@ void TaskQueueTimeoutFactory::TaskQueueTimeout::Start(DurationMs duration_ms,
[timeout_id, this]() {
RTC_DLOG(LS_VERBOSE) << "Timout expired: " << timeout_id.value();
RTC_DCHECK_RUN_ON(&parent_.thread_checker_);
- RTC_DCHECK(posted_task_expiration_ != TimeMs::InfiniteFuture());
- posted_task_expiration_ = TimeMs::InfiniteFuture();
+ RTC_DCHECK(!posted_task_expiration_.IsPlusInfinity());
+ posted_task_expiration_ = Timestamp::PlusInfinity();
- if (timeout_expiration_ == TimeMs::InfiniteFuture()) {
+ if (timeout_expiration_.IsPlusInfinity()) {
// The timeout was stopped before it expired. Very common.
} else {
// Note that the timeout might have been restarted, which updated
@@ -74,10 +76,10 @@ void TaskQueueTimeoutFactory::TaskQueueTimeout::Start(DurationMs duration_ms,
// if it's not quite time to trigger the timeout yet, schedule a
// new delayed task with what's remaining and retry at that point
// in time.
- DurationMs remaining = timeout_expiration_ - parent_.get_time_();
- timeout_expiration_ = TimeMs::InfiniteFuture();
- if (*remaining > 0) {
- Start(remaining, timeout_id_);
+ TimeDelta remaining = timeout_expiration_ - parent_.Now();
+ timeout_expiration_ = Timestamp::PlusInfinity();
+ if (remaining > TimeDelta::Zero()) {
+ Start(DurationMs(remaining.ms()), timeout_id_);
} else {
// It has actually triggered.
RTC_DLOG(LS_VERBOSE)
@@ -93,7 +95,7 @@ void TaskQueueTimeoutFactory::TaskQueueTimeout::Stop() {
// As the TaskQueue doesn't support deleting a posted task, just mark the
// timeout as not running.
RTC_DCHECK_RUN_ON(&parent_.thread_checker_);
- timeout_expiration_ = TimeMs::InfiniteFuture();
+ timeout_expiration_ = Timestamp::PlusInfinity();
}
} // namespace dcsctp
diff --git a/third_party/libwebrtc/net/dcsctp/timer/task_queue_timeout.h b/third_party/libwebrtc/net/dcsctp/timer/task_queue_timeout.h
index faae14464f..4b40309f83 100644
--- a/third_party/libwebrtc/net/dcsctp/timer/task_queue_timeout.h
+++ b/third_party/libwebrtc/net/dcsctp/timer/task_queue_timeout.h
@@ -15,6 +15,7 @@
#include "api/task_queue/pending_task_safety_flag.h"
#include "api/task_queue/task_queue_base.h"
+#include "api/units/timestamp.h"
#include "net/dcsctp/public/timeout.h"
namespace dcsctp {
@@ -74,14 +75,17 @@ class TaskQueueTimeoutFactory {
rtc::scoped_refptr<webrtc::PendingTaskSafetyFlag> pending_task_safety_flag_;
// The time when the posted delayed task is set to expire. Will be set to
// the infinite future if there is no such task running.
- TimeMs posted_task_expiration_ = TimeMs::InfiniteFuture();
+ webrtc::Timestamp posted_task_expiration_ =
+ webrtc::Timestamp::PlusInfinity();
// The time when the timeout expires. It will be set to the infinite future
// if the timeout is not running/not started.
- TimeMs timeout_expiration_ = TimeMs::InfiniteFuture();
+ webrtc::Timestamp timeout_expiration_ = webrtc::Timestamp::PlusInfinity();
// The current timeout ID that will be reported when expired.
TimeoutID timeout_id_ = TimeoutID(0);
};
+ webrtc::Timestamp Now() { return webrtc::Timestamp::Millis(*get_time_()); }
+
RTC_NO_UNIQUE_ADDRESS webrtc::SequenceChecker thread_checker_;
webrtc::TaskQueueBase& task_queue_;
const std::function<TimeMs()> get_time_;
diff --git a/third_party/libwebrtc/net/dcsctp/timer/timer.cc b/third_party/libwebrtc/net/dcsctp/timer/timer.cc
index 208f26fdf9..07c9f3d786 100644
--- a/third_party/libwebrtc/net/dcsctp/timer/timer.cc
+++ b/third_party/libwebrtc/net/dcsctp/timer/timer.cc
@@ -22,21 +22,23 @@
namespace dcsctp {
namespace {
+using ::webrtc::TimeDelta;
+
TimeoutID MakeTimeoutId(TimerID timer_id, TimerGeneration generation) {
return TimeoutID(static_cast<uint64_t>(*timer_id) << 32 | *generation);
}
-DurationMs GetBackoffDuration(const TimerOptions& options,
- DurationMs base_duration,
- int expiration_count) {
+TimeDelta GetBackoffDuration(const TimerOptions& options,
+ TimeDelta base_duration,
+ int expiration_count) {
switch (options.backoff_algorithm) {
case TimerBackoffAlgorithm::kFixed:
return base_duration;
case TimerBackoffAlgorithm::kExponential: {
- DurationMs duration = base_duration;
+ TimeDelta duration = base_duration;
while (expiration_count > 0 && duration < Timer::kMaxTimerDuration) {
- duration *= 2;
+ duration = duration * 2;
--expiration_count;
if (duration > options.max_backoff_duration) {
@@ -44,13 +46,13 @@ DurationMs GetBackoffDuration(const TimerOptions& options,
}
}
- return DurationMs(std::min(duration, Timer::kMaxTimerDuration));
+ return TimeDelta(std::min(duration, Timer::kMaxTimerDuration));
}
}
}
} // namespace
-constexpr DurationMs Timer::kMaxTimerDuration;
+constexpr TimeDelta Timer::kMaxTimerDuration;
Timer::Timer(TimerID id,
absl::string_view name,
@@ -76,12 +78,12 @@ void Timer::Start() {
if (!is_running()) {
is_running_ = true;
generation_ = TimerGeneration(*generation_ + 1);
- timeout_->Start(duration_, MakeTimeoutId(id_, generation_));
+ timeout_->Start(DurationMs(duration_), MakeTimeoutId(id_, generation_));
} else {
// Timer was running - stop and restart it, to make it expire in `duration_`
// from now.
generation_ = TimerGeneration(*generation_ + 1);
- timeout_->Restart(duration_, MakeTimeoutId(id_, generation_));
+ timeout_->Restart(DurationMs(duration_), MakeTimeoutId(id_, generation_));
}
}
@@ -103,23 +105,24 @@ void Timer::Trigger(TimerGeneration generation) {
// timer. Note that it might be very quickly restarted again, if the
// `on_expired_` callback returns a new duration.
is_running_ = true;
- DurationMs duration =
+ TimeDelta duration =
GetBackoffDuration(options_, duration_, expiration_count_);
generation_ = TimerGeneration(*generation_ + 1);
- timeout_->Start(duration, MakeTimeoutId(id_, generation_));
+ timeout_->Start(DurationMs(duration), MakeTimeoutId(id_, generation_));
}
- absl::optional<DurationMs> new_duration = on_expired_();
- if (new_duration.has_value() && new_duration != duration_) {
- duration_ = new_duration.value();
+ TimeDelta new_duration = on_expired_();
+ RTC_DCHECK(new_duration != TimeDelta::PlusInfinity());
+ if (new_duration > TimeDelta::Zero() && new_duration != duration_) {
+ duration_ = new_duration;
if (is_running_) {
// Restart it with new duration.
timeout_->Stop();
- DurationMs duration =
+ TimeDelta duration =
GetBackoffDuration(options_, duration_, expiration_count_);
generation_ = TimerGeneration(*generation_ + 1);
- timeout_->Start(duration, MakeTimeoutId(id_, generation_));
+ timeout_->Start(DurationMs(duration), MakeTimeoutId(id_, generation_));
}
}
}
diff --git a/third_party/libwebrtc/net/dcsctp/timer/timer.h b/third_party/libwebrtc/net/dcsctp/timer/timer.h
index 95aae570c8..30b07f9bfa 100644
--- a/third_party/libwebrtc/net/dcsctp/timer/timer.h
+++ b/third_party/libwebrtc/net/dcsctp/timer/timer.h
@@ -22,6 +22,7 @@
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
#include "api/task_queue/task_queue_base.h"
+#include "api/units/time_delta.h"
#include "net/dcsctp/public/timeout.h"
#include "rtc_base/strong_alias.h"
@@ -40,30 +41,31 @@ enum class TimerBackoffAlgorithm {
};
struct TimerOptions {
- explicit TimerOptions(DurationMs duration)
+ explicit TimerOptions(webrtc::TimeDelta duration)
: TimerOptions(duration, TimerBackoffAlgorithm::kExponential) {}
- TimerOptions(DurationMs duration, TimerBackoffAlgorithm backoff_algorithm)
+ TimerOptions(webrtc::TimeDelta duration,
+ TimerBackoffAlgorithm backoff_algorithm)
: TimerOptions(duration, backoff_algorithm, absl::nullopt) {}
- TimerOptions(DurationMs duration,
+ TimerOptions(webrtc::TimeDelta duration,
TimerBackoffAlgorithm backoff_algorithm,
absl::optional<int> max_restarts)
: TimerOptions(duration,
backoff_algorithm,
max_restarts,
- DurationMs::InfiniteDuration()) {}
- TimerOptions(DurationMs duration,
+ webrtc::TimeDelta::PlusInfinity()) {}
+ TimerOptions(webrtc::TimeDelta duration,
TimerBackoffAlgorithm backoff_algorithm,
absl::optional<int> max_restarts,
- DurationMs max_backoff_duration)
+ webrtc::TimeDelta max_backoff_duration)
: TimerOptions(duration,
backoff_algorithm,
max_restarts,
max_backoff_duration,
webrtc::TaskQueueBase::DelayPrecision::kLow) {}
- TimerOptions(DurationMs duration,
+ TimerOptions(webrtc::TimeDelta duration,
TimerBackoffAlgorithm backoff_algorithm,
absl::optional<int> max_restarts,
- DurationMs max_backoff_duration,
+ webrtc::TimeDelta max_backoff_duration,
webrtc::TaskQueueBase::DelayPrecision precision)
: duration(duration),
backoff_algorithm(backoff_algorithm),
@@ -72,7 +74,7 @@ struct TimerOptions {
precision(precision) {}
// The initial timer duration. Can be overridden with `set_duration`.
- const DurationMs duration;
+ const webrtc::TimeDelta duration;
// If the duration should be increased (using exponential backoff) when it is
// restarted. If not set, the same duration will be used.
const TimerBackoffAlgorithm backoff_algorithm;
@@ -80,7 +82,7 @@ struct TimerOptions {
// or absl::nullopt if there is no limit.
const absl::optional<int> max_restarts;
// The maximum timeout value for exponential backoff.
- const DurationMs max_backoff_duration;
+ const webrtc::TimeDelta max_backoff_duration;
// The precision of the webrtc::TaskQueueBase used for scheduling.
const webrtc::TaskQueueBase::DelayPrecision precision;
};
@@ -100,12 +102,14 @@ struct TimerOptions {
class Timer {
public:
// The maximum timer duration - one day.
- static constexpr DurationMs kMaxTimerDuration = DurationMs(24 * 3600 * 1000);
+ static constexpr webrtc::TimeDelta kMaxTimerDuration =
+ webrtc::TimeDelta::Seconds(24 * 3600);
- // When expired, the timer handler can optionally return a new duration which
- // will be set as `duration` and used as base duration when the timer is
- // restarted and as input to the backoff algorithm.
- using OnExpired = std::function<absl::optional<DurationMs>()>;
+ // When expired, the timer handler can optionally return a new non-zero
+ // duration which will be set as `duration` and used as base duration when the
+ // timer is restarted and as input to the backoff algorithm. If zero is
+ // returned, the current duration is used.
+ using OnExpired = std::function<webrtc::TimeDelta()>;
// TimerManager will have pointers to these instances, so they must not move.
Timer(const Timer&) = delete;
@@ -123,13 +127,13 @@ class Timer {
// Sets the base duration. The actual timer duration may be larger depending
// on the backoff algorithm.
- void set_duration(DurationMs duration) {
+ void set_duration(webrtc::TimeDelta duration) {
duration_ = std::min(duration, kMaxTimerDuration);
}
// Retrieves the base duration. The actual timer duration may be larger
// depending on the backoff algorithm.
- DurationMs duration() const { return duration_; }
+ webrtc::TimeDelta duration() const { return duration_; }
// Returns the number of times the timer has expired.
int expiration_count() const { return expiration_count_; }
@@ -167,7 +171,7 @@ class Timer {
const UnregisterHandler unregister_handler_;
const std::unique_ptr<Timeout> timeout_;
- DurationMs duration_;
+ webrtc::TimeDelta duration_;
// Increased on each start, and is matched on Trigger, to avoid races. And by
// race, meaning that a timeout - which may be evaluated/expired on a
diff --git a/third_party/libwebrtc/net/dcsctp/timer/timer_test.cc b/third_party/libwebrtc/net/dcsctp/timer/timer_test.cc
index 93876160bb..9a7c029ec5 100644
--- a/third_party/libwebrtc/net/dcsctp/timer/timer_test.cc
+++ b/third_party/libwebrtc/net/dcsctp/timer/timer_test.cc
@@ -13,6 +13,7 @@
#include "absl/types/optional.h"
#include "api/task_queue/task_queue_base.h"
+#include "api/units/time_delta.h"
#include "net/dcsctp/public/timeout.h"
#include "net/dcsctp/timer/fake_timeout.h"
#include "rtc_base/gunit.h"
@@ -21,6 +22,8 @@
namespace dcsctp {
namespace {
using ::testing::Return;
+using ::webrtc::TimeDelta;
+using ::webrtc::Timestamp;
class TimerTest : public testing::Test {
protected:
@@ -29,10 +32,10 @@ class TimerTest : public testing::Test {
manager_([this](webrtc::TaskQueueBase::DelayPrecision precision) {
return timeout_manager_.CreateTimeout(precision);
}) {
- ON_CALL(on_expired_, Call).WillByDefault(Return(absl::nullopt));
+ ON_CALL(on_expired_, Call).WillByDefault(Return(TimeDelta::Zero()));
}
- void AdvanceTimeAndRunTimers(DurationMs duration) {
+ void AdvanceTimeAndRunTimers(TimeDelta duration) {
now_ = now_ + duration;
for (;;) {
@@ -45,16 +48,16 @@ class TimerTest : public testing::Test {
}
}
- TimeMs now_ = TimeMs(0);
+ Timestamp now_ = Timestamp::Zero();
FakeTimeoutManager timeout_manager_;
TimerManager manager_;
- testing::MockFunction<absl::optional<DurationMs>()> on_expired_;
+ testing::MockFunction<TimeDelta()> on_expired_;
};
TEST_F(TimerTest, TimerIsInitiallyStopped) {
std::unique_ptr<Timer> t1 = manager_.CreateTimer(
"t1", on_expired_.AsStdFunction(),
- TimerOptions(DurationMs(5000), TimerBackoffAlgorithm::kFixed));
+ TimerOptions(TimeDelta::Seconds(5), TimerBackoffAlgorithm::kFixed));
EXPECT_FALSE(t1->is_running());
}
@@ -62,50 +65,50 @@ TEST_F(TimerTest, TimerIsInitiallyStopped) {
TEST_F(TimerTest, TimerExpiresAtGivenTime) {
std::unique_ptr<Timer> t1 = manager_.CreateTimer(
"t1", on_expired_.AsStdFunction(),
- TimerOptions(DurationMs(5000), TimerBackoffAlgorithm::kFixed));
+ TimerOptions(TimeDelta::Seconds(5), TimerBackoffAlgorithm::kFixed));
EXPECT_CALL(on_expired_, Call).Times(0);
t1->Start();
EXPECT_TRUE(t1->is_running());
- AdvanceTimeAndRunTimers(DurationMs(4000));
+ AdvanceTimeAndRunTimers(TimeDelta::Seconds(4));
EXPECT_CALL(on_expired_, Call).Times(1);
- AdvanceTimeAndRunTimers(DurationMs(1000));
+ AdvanceTimeAndRunTimers(TimeDelta::Seconds(1));
}
TEST_F(TimerTest, TimerReschedulesAfterExpiredWithFixedBackoff) {
std::unique_ptr<Timer> t1 = manager_.CreateTimer(
"t1", on_expired_.AsStdFunction(),
- TimerOptions(DurationMs(5000), TimerBackoffAlgorithm::kFixed));
+ TimerOptions(TimeDelta::Seconds(5), TimerBackoffAlgorithm::kFixed));
EXPECT_CALL(on_expired_, Call).Times(0);
t1->Start();
EXPECT_EQ(t1->expiration_count(), 0);
- AdvanceTimeAndRunTimers(DurationMs(4000));
+ AdvanceTimeAndRunTimers(TimeDelta::Seconds(4));
// Fire first time
EXPECT_CALL(on_expired_, Call).Times(1);
- AdvanceTimeAndRunTimers(DurationMs(1000));
+ AdvanceTimeAndRunTimers(TimeDelta::Seconds(1));
EXPECT_TRUE(t1->is_running());
EXPECT_EQ(t1->expiration_count(), 1);
EXPECT_CALL(on_expired_, Call).Times(0);
- AdvanceTimeAndRunTimers(DurationMs(4000));
+ AdvanceTimeAndRunTimers(TimeDelta::Seconds(4));
// Second time
EXPECT_CALL(on_expired_, Call).Times(1);
- AdvanceTimeAndRunTimers(DurationMs(1000));
+ AdvanceTimeAndRunTimers(TimeDelta::Seconds(1));
EXPECT_TRUE(t1->is_running());
EXPECT_EQ(t1->expiration_count(), 2);
EXPECT_CALL(on_expired_, Call).Times(0);
- AdvanceTimeAndRunTimers(DurationMs(4000));
+ AdvanceTimeAndRunTimers(TimeDelta::Seconds(4));
// Third time
EXPECT_CALL(on_expired_, Call).Times(1);
- AdvanceTimeAndRunTimers(DurationMs(1000));
+ AdvanceTimeAndRunTimers(TimeDelta::Seconds(1));
EXPECT_TRUE(t1->is_running());
EXPECT_EQ(t1->expiration_count(), 3);
}
@@ -113,151 +116,151 @@ TEST_F(TimerTest, TimerReschedulesAfterExpiredWithFixedBackoff) {
TEST_F(TimerTest, TimerWithNoRestarts) {
std::unique_ptr<Timer> t1 = manager_.CreateTimer(
"t1", on_expired_.AsStdFunction(),
- TimerOptions(DurationMs(5000), TimerBackoffAlgorithm::kFixed,
+ TimerOptions(TimeDelta::Seconds(5), TimerBackoffAlgorithm::kFixed,
/*max_restart=*/0));
EXPECT_CALL(on_expired_, Call).Times(0);
t1->Start();
- AdvanceTimeAndRunTimers(DurationMs(4000));
+ AdvanceTimeAndRunTimers(TimeDelta::Seconds(4));
// Fire first time
EXPECT_CALL(on_expired_, Call).Times(1);
- AdvanceTimeAndRunTimers(DurationMs(1000));
+ AdvanceTimeAndRunTimers(TimeDelta::Seconds(1));
EXPECT_FALSE(t1->is_running());
// Second time - shouldn't fire
EXPECT_CALL(on_expired_, Call).Times(0);
- AdvanceTimeAndRunTimers(DurationMs(5000));
+ AdvanceTimeAndRunTimers(TimeDelta::Seconds(5));
EXPECT_FALSE(t1->is_running());
}
TEST_F(TimerTest, TimerWithOneRestart) {
std::unique_ptr<Timer> t1 = manager_.CreateTimer(
"t1", on_expired_.AsStdFunction(),
- TimerOptions(DurationMs(5000), TimerBackoffAlgorithm::kFixed,
+ TimerOptions(TimeDelta::Seconds(5), TimerBackoffAlgorithm::kFixed,
/*max_restart=*/1));
EXPECT_CALL(on_expired_, Call).Times(0);
t1->Start();
- AdvanceTimeAndRunTimers(DurationMs(4000));
+ AdvanceTimeAndRunTimers(TimeDelta::Seconds(4));
// Fire first time
EXPECT_CALL(on_expired_, Call).Times(1);
- AdvanceTimeAndRunTimers(DurationMs(1000));
+ AdvanceTimeAndRunTimers(TimeDelta::Seconds(1));
EXPECT_TRUE(t1->is_running());
EXPECT_CALL(on_expired_, Call).Times(0);
- AdvanceTimeAndRunTimers(DurationMs(4000));
+ AdvanceTimeAndRunTimers(TimeDelta::Seconds(4));
// Second time - max restart limit reached.
EXPECT_CALL(on_expired_, Call).Times(1);
- AdvanceTimeAndRunTimers(DurationMs(1000));
+ AdvanceTimeAndRunTimers(TimeDelta::Seconds(1));
EXPECT_FALSE(t1->is_running());
// Third time - should not fire.
EXPECT_CALL(on_expired_, Call).Times(0);
- AdvanceTimeAndRunTimers(DurationMs(5000));
+ AdvanceTimeAndRunTimers(TimeDelta::Seconds(5));
EXPECT_FALSE(t1->is_running());
}
TEST_F(TimerTest, TimerWithTwoRestart) {
std::unique_ptr<Timer> t1 = manager_.CreateTimer(
"t1", on_expired_.AsStdFunction(),
- TimerOptions(DurationMs(5000), TimerBackoffAlgorithm::kFixed,
+ TimerOptions(TimeDelta::Seconds(5), TimerBackoffAlgorithm::kFixed,
/*max_restart=*/2));
EXPECT_CALL(on_expired_, Call).Times(0);
t1->Start();
- AdvanceTimeAndRunTimers(DurationMs(4000));
+ AdvanceTimeAndRunTimers(TimeDelta::Seconds(4));
// Fire first time
EXPECT_CALL(on_expired_, Call).Times(1);
- AdvanceTimeAndRunTimers(DurationMs(1000));
+ AdvanceTimeAndRunTimers(TimeDelta::Seconds(1));
EXPECT_TRUE(t1->is_running());
EXPECT_CALL(on_expired_, Call).Times(0);
- AdvanceTimeAndRunTimers(DurationMs(4000));
+ AdvanceTimeAndRunTimers(TimeDelta::Seconds(4));
// Second time
EXPECT_CALL(on_expired_, Call).Times(1);
- AdvanceTimeAndRunTimers(DurationMs(1000));
+ AdvanceTimeAndRunTimers(TimeDelta::Seconds(1));
EXPECT_TRUE(t1->is_running());
EXPECT_CALL(on_expired_, Call).Times(0);
- AdvanceTimeAndRunTimers(DurationMs(4000));
+ AdvanceTimeAndRunTimers(TimeDelta::Seconds(4));
// Third time
EXPECT_CALL(on_expired_, Call).Times(1);
- AdvanceTimeAndRunTimers(DurationMs(1000));
+ AdvanceTimeAndRunTimers(TimeDelta::Seconds(1));
EXPECT_FALSE(t1->is_running());
}
TEST_F(TimerTest, TimerWithExponentialBackoff) {
std::unique_ptr<Timer> t1 = manager_.CreateTimer(
"t1", on_expired_.AsStdFunction(),
- TimerOptions(DurationMs(5000), TimerBackoffAlgorithm::kExponential));
+ TimerOptions(TimeDelta::Seconds(5), TimerBackoffAlgorithm::kExponential));
t1->Start();
// Fire first time at 5 seconds
EXPECT_CALL(on_expired_, Call).Times(1);
- AdvanceTimeAndRunTimers(DurationMs(5000));
+ AdvanceTimeAndRunTimers(TimeDelta::Seconds(5));
// Second time at 5*2^1 = 10 seconds later.
EXPECT_CALL(on_expired_, Call).Times(0);
- AdvanceTimeAndRunTimers(DurationMs(9000));
+ AdvanceTimeAndRunTimers(TimeDelta::Seconds(9));
EXPECT_CALL(on_expired_, Call).Times(1);
- AdvanceTimeAndRunTimers(DurationMs(1000));
+ AdvanceTimeAndRunTimers(TimeDelta::Seconds(1));
// Third time at 5*2^2 = 20 seconds later.
EXPECT_CALL(on_expired_, Call).Times(0);
- AdvanceTimeAndRunTimers(DurationMs(19000));
+ AdvanceTimeAndRunTimers(TimeDelta::Seconds(19));
EXPECT_CALL(on_expired_, Call).Times(1);
- AdvanceTimeAndRunTimers(DurationMs(1000));
+ AdvanceTimeAndRunTimers(TimeDelta::Seconds(1));
// Fourth time at 5*2^3 = 40 seconds later.
EXPECT_CALL(on_expired_, Call).Times(0);
- AdvanceTimeAndRunTimers(DurationMs(39000));
+ AdvanceTimeAndRunTimers(TimeDelta::Seconds(39));
EXPECT_CALL(on_expired_, Call).Times(1);
- AdvanceTimeAndRunTimers(DurationMs(1000));
+ AdvanceTimeAndRunTimers(TimeDelta::Seconds(1));
}
TEST_F(TimerTest, StartTimerWillStopAndStart) {
std::unique_ptr<Timer> t1 = manager_.CreateTimer(
"t1", on_expired_.AsStdFunction(),
- TimerOptions(DurationMs(5000), TimerBackoffAlgorithm::kExponential));
+ TimerOptions(TimeDelta::Seconds(5), TimerBackoffAlgorithm::kExponential));
t1->Start();
- AdvanceTimeAndRunTimers(DurationMs(3000));
+ AdvanceTimeAndRunTimers(TimeDelta::Seconds(3));
t1->Start();
EXPECT_CALL(on_expired_, Call).Times(0);
- AdvanceTimeAndRunTimers(DurationMs(2000));
+ AdvanceTimeAndRunTimers(TimeDelta::Seconds(2));
EXPECT_CALL(on_expired_, Call).Times(1);
- AdvanceTimeAndRunTimers(DurationMs(3000));
+ AdvanceTimeAndRunTimers(TimeDelta::Seconds(3));
}
TEST_F(TimerTest, ExpirationCounterWillResetIfStopped) {
std::unique_ptr<Timer> t1 = manager_.CreateTimer(
"t1", on_expired_.AsStdFunction(),
- TimerOptions(DurationMs(5000), TimerBackoffAlgorithm::kExponential));
+ TimerOptions(TimeDelta::Seconds(5), TimerBackoffAlgorithm::kExponential));
t1->Start();
// Fire first time at 5 seconds
EXPECT_CALL(on_expired_, Call).Times(1);
- AdvanceTimeAndRunTimers(DurationMs(5000));
+ AdvanceTimeAndRunTimers(TimeDelta::Seconds(5));
EXPECT_EQ(t1->expiration_count(), 1);
// Second time at 5*2^1 = 10 seconds later.
EXPECT_CALL(on_expired_, Call).Times(0);
- AdvanceTimeAndRunTimers(DurationMs(9000));
+ AdvanceTimeAndRunTimers(TimeDelta::Seconds(9));
EXPECT_CALL(on_expired_, Call).Times(1);
- AdvanceTimeAndRunTimers(DurationMs(1000));
+ AdvanceTimeAndRunTimers(TimeDelta::Seconds(1));
EXPECT_EQ(t1->expiration_count(), 2);
t1->Start();
@@ -265,79 +268,79 @@ TEST_F(TimerTest, ExpirationCounterWillResetIfStopped) {
// Third time at 5*2^0 = 5 seconds later.
EXPECT_CALL(on_expired_, Call).Times(0);
- AdvanceTimeAndRunTimers(DurationMs(4000));
+ AdvanceTimeAndRunTimers(TimeDelta::Seconds(4));
EXPECT_CALL(on_expired_, Call).Times(1);
- AdvanceTimeAndRunTimers(DurationMs(1000));
+ AdvanceTimeAndRunTimers(TimeDelta::Seconds(1));
EXPECT_EQ(t1->expiration_count(), 1);
}
TEST_F(TimerTest, StopTimerWillMakeItNotExpire) {
std::unique_ptr<Timer> t1 = manager_.CreateTimer(
"t1", on_expired_.AsStdFunction(),
- TimerOptions(DurationMs(5000), TimerBackoffAlgorithm::kExponential));
+ TimerOptions(TimeDelta::Seconds(5), TimerBackoffAlgorithm::kExponential));
t1->Start();
EXPECT_TRUE(t1->is_running());
EXPECT_CALL(on_expired_, Call).Times(0);
- AdvanceTimeAndRunTimers(DurationMs(4000));
+ AdvanceTimeAndRunTimers(TimeDelta::Seconds(4));
t1->Stop();
EXPECT_FALSE(t1->is_running());
EXPECT_CALL(on_expired_, Call).Times(0);
- AdvanceTimeAndRunTimers(DurationMs(1000));
+ AdvanceTimeAndRunTimers(TimeDelta::Seconds(1));
}
TEST_F(TimerTest, ReturningNewDurationWhenExpired) {
std::unique_ptr<Timer> t1 = manager_.CreateTimer(
"t1", on_expired_.AsStdFunction(),
- TimerOptions(DurationMs(5000), TimerBackoffAlgorithm::kFixed));
+ TimerOptions(TimeDelta::Seconds(5), TimerBackoffAlgorithm::kFixed));
EXPECT_CALL(on_expired_, Call).Times(0);
t1->Start();
- EXPECT_EQ(t1->duration(), DurationMs(5000));
+ EXPECT_EQ(t1->duration(), TimeDelta::Seconds(5));
- AdvanceTimeAndRunTimers(DurationMs(4000));
+ AdvanceTimeAndRunTimers(TimeDelta::Seconds(4));
// Fire first time
- EXPECT_CALL(on_expired_, Call).WillOnce(Return(DurationMs(2000)));
- AdvanceTimeAndRunTimers(DurationMs(1000));
- EXPECT_EQ(t1->duration(), DurationMs(2000));
+ EXPECT_CALL(on_expired_, Call).WillOnce(Return(TimeDelta::Seconds(2)));
+ AdvanceTimeAndRunTimers(TimeDelta::Seconds(1));
+ EXPECT_EQ(t1->duration(), TimeDelta::Seconds(2));
EXPECT_CALL(on_expired_, Call).Times(0);
- AdvanceTimeAndRunTimers(DurationMs(1000));
+ AdvanceTimeAndRunTimers(TimeDelta::Seconds(1));
// Second time
- EXPECT_CALL(on_expired_, Call).WillOnce(Return(DurationMs(10000)));
- AdvanceTimeAndRunTimers(DurationMs(1000));
- EXPECT_EQ(t1->duration(), DurationMs(10000));
+ EXPECT_CALL(on_expired_, Call).WillOnce(Return(TimeDelta::Seconds(10)));
+ AdvanceTimeAndRunTimers(TimeDelta::Seconds(1));
+ EXPECT_EQ(t1->duration(), TimeDelta::Seconds(10));
EXPECT_CALL(on_expired_, Call).Times(0);
- AdvanceTimeAndRunTimers(DurationMs(9000));
+ AdvanceTimeAndRunTimers(TimeDelta::Seconds(9));
EXPECT_CALL(on_expired_, Call).Times(1);
- AdvanceTimeAndRunTimers(DurationMs(1000));
+ AdvanceTimeAndRunTimers(TimeDelta::Seconds(1));
}
TEST_F(TimerTest, TimersHaveMaximumDuration) {
std::unique_ptr<Timer> t1 = manager_.CreateTimer(
"t1", on_expired_.AsStdFunction(),
- TimerOptions(DurationMs(1000), TimerBackoffAlgorithm::kExponential));
+ TimerOptions(TimeDelta::Seconds(1), TimerBackoffAlgorithm::kExponential));
- t1->set_duration(DurationMs(2 * *Timer::kMaxTimerDuration));
+ t1->set_duration(2 * Timer::kMaxTimerDuration);
EXPECT_EQ(t1->duration(), Timer::kMaxTimerDuration);
}
TEST_F(TimerTest, TimersHaveMaximumBackoffDuration) {
std::unique_ptr<Timer> t1 = manager_.CreateTimer(
"t1", on_expired_.AsStdFunction(),
- TimerOptions(DurationMs(1000), TimerBackoffAlgorithm::kExponential));
+ TimerOptions(TimeDelta::Seconds(1), TimerBackoffAlgorithm::kExponential));
t1->Start();
- int max_exponent = static_cast<int>(log2(*Timer::kMaxTimerDuration / 1000));
+ int max_exponent = static_cast<int>(log2(Timer::kMaxTimerDuration.seconds()));
for (int i = 0; i < max_exponent; ++i) {
EXPECT_CALL(on_expired_, Call).Times(1);
- AdvanceTimeAndRunTimers(DurationMs(1000 * (1 << i)));
+ AdvanceTimeAndRunTimers(TimeDelta::Seconds(1 * (1 << i)));
}
// Reached the maximum duration.
@@ -357,77 +360,77 @@ TEST_F(TimerTest, TimersHaveMaximumBackoffDuration) {
TEST_F(TimerTest, TimerCanBeStartedFromWithinExpirationHandler) {
std::unique_ptr<Timer> t1 = manager_.CreateTimer(
"t1", on_expired_.AsStdFunction(),
- TimerOptions(DurationMs(1000), TimerBackoffAlgorithm::kFixed));
+ TimerOptions(TimeDelta::Seconds(1), TimerBackoffAlgorithm::kFixed));
t1->Start();
// Start a timer, but don't return any new duration in callback.
EXPECT_CALL(on_expired_, Call).WillOnce([&]() {
EXPECT_TRUE(t1->is_running());
- t1->set_duration(DurationMs(5000));
+ t1->set_duration(TimeDelta::Seconds(5));
t1->Start();
- return absl::nullopt;
+ return TimeDelta::Zero();
});
- AdvanceTimeAndRunTimers(DurationMs(1000));
+ AdvanceTimeAndRunTimers(TimeDelta::Seconds(1));
EXPECT_CALL(on_expired_, Call).Times(0);
- AdvanceTimeAndRunTimers(DurationMs(4999));
+ AdvanceTimeAndRunTimers(TimeDelta::Millis(4999));
// Start a timer, and return any new duration in callback.
EXPECT_CALL(on_expired_, Call).WillOnce([&]() {
EXPECT_TRUE(t1->is_running());
- t1->set_duration(DurationMs(5000));
+ t1->set_duration(TimeDelta::Seconds(5));
t1->Start();
- return absl::make_optional(DurationMs(8000));
+ return TimeDelta::Seconds(8);
});
- AdvanceTimeAndRunTimers(DurationMs(1));
+ AdvanceTimeAndRunTimers(TimeDelta::Millis(1));
EXPECT_CALL(on_expired_, Call).Times(0);
- AdvanceTimeAndRunTimers(DurationMs(7999));
+ AdvanceTimeAndRunTimers(TimeDelta::Millis(7999));
EXPECT_CALL(on_expired_, Call).Times(1);
- AdvanceTimeAndRunTimers(DurationMs(1));
+ AdvanceTimeAndRunTimers(TimeDelta::Millis(1));
}
TEST_F(TimerTest, DurationStaysWithinMaxTimerBackOffDuration) {
std::unique_ptr<Timer> t1 = manager_.CreateTimer(
"t1", on_expired_.AsStdFunction(),
- TimerOptions(DurationMs(1000), TimerBackoffAlgorithm::kExponential,
- /*max_restarts=*/absl::nullopt, DurationMs(5000)));
+ TimerOptions(TimeDelta::Seconds(1), TimerBackoffAlgorithm::kExponential,
+ /*max_restarts=*/absl::nullopt, TimeDelta::Seconds(5)));
t1->Start();
// Initial timeout, 1000 ms
EXPECT_CALL(on_expired_, Call).Times(1);
- AdvanceTimeAndRunTimers(DurationMs(1000));
+ AdvanceTimeAndRunTimers(TimeDelta::Seconds(1));
// Exponential backoff -> 2000 ms
EXPECT_CALL(on_expired_, Call).Times(0);
- AdvanceTimeAndRunTimers(DurationMs(1999));
+ AdvanceTimeAndRunTimers(TimeDelta::Millis(1999));
EXPECT_CALL(on_expired_, Call).Times(1);
- AdvanceTimeAndRunTimers(DurationMs(1));
+ AdvanceTimeAndRunTimers(TimeDelta::Millis(1));
// Exponential backoff -> 4000 ms
EXPECT_CALL(on_expired_, Call).Times(0);
- AdvanceTimeAndRunTimers(DurationMs(3999));
+ AdvanceTimeAndRunTimers(TimeDelta::Millis(3999));
EXPECT_CALL(on_expired_, Call).Times(1);
- AdvanceTimeAndRunTimers(DurationMs(1));
+ AdvanceTimeAndRunTimers(TimeDelta::Millis(1));
// Limited backoff -> 5000ms
EXPECT_CALL(on_expired_, Call).Times(0);
- AdvanceTimeAndRunTimers(DurationMs(4999));
+ AdvanceTimeAndRunTimers(TimeDelta::Millis(4999));
EXPECT_CALL(on_expired_, Call).Times(1);
- AdvanceTimeAndRunTimers(DurationMs(1));
+ AdvanceTimeAndRunTimers(TimeDelta::Millis(1));
// ... where it plateaus
EXPECT_CALL(on_expired_, Call).Times(0);
- AdvanceTimeAndRunTimers(DurationMs(4999));
+ AdvanceTimeAndRunTimers(TimeDelta::Millis(4999));
EXPECT_CALL(on_expired_, Call).Times(1);
- AdvanceTimeAndRunTimers(DurationMs(1));
+ AdvanceTimeAndRunTimers(TimeDelta::Millis(1));
}
TEST(TimerManagerTest, TimerManagerPassesPrecisionToCreateTimeoutMethod) {
- FakeTimeoutManager timeout_manager([&]() { return TimeMs(0); });
+ FakeTimeoutManager timeout_manager([&]() { return Timestamp::Zero(); });
absl::optional<webrtc::TaskQueueBase::DelayPrecision> create_timer_precison;
TimerManager manager([&](webrtc::TaskQueueBase::DelayPrecision precision) {
create_timer_precison = precision;
@@ -435,22 +438,22 @@ TEST(TimerManagerTest, TimerManagerPassesPrecisionToCreateTimeoutMethod) {
});
// Default TimerOptions.
manager.CreateTimer(
- "test_timer", []() { return absl::optional<DurationMs>(); },
- TimerOptions(DurationMs(123)));
+ "test_timer", []() { return TimeDelta::Zero(); },
+ TimerOptions(TimeDelta::Millis(123)));
EXPECT_EQ(create_timer_precison, webrtc::TaskQueueBase::DelayPrecision::kLow);
// High precision TimerOptions.
manager.CreateTimer(
- "test_timer", []() { return absl::optional<DurationMs>(); },
- TimerOptions(DurationMs(123), TimerBackoffAlgorithm::kExponential,
- absl::nullopt, DurationMs::InfiniteDuration(),
+ "test_timer", []() { return TimeDelta::Zero(); },
+ TimerOptions(TimeDelta::Millis(123), TimerBackoffAlgorithm::kExponential,
+ absl::nullopt, TimeDelta::PlusInfinity(),
webrtc::TaskQueueBase::DelayPrecision::kHigh));
EXPECT_EQ(create_timer_precison,
webrtc::TaskQueueBase::DelayPrecision::kHigh);
// Low precision TimerOptions.
manager.CreateTimer(
- "test_timer", []() { return absl::optional<DurationMs>(); },
- TimerOptions(DurationMs(123), TimerBackoffAlgorithm::kExponential,
- absl::nullopt, DurationMs::InfiniteDuration(),
+ "test_timer", []() { return TimeDelta::Zero(); },
+ TimerOptions(TimeDelta::Millis(123), TimerBackoffAlgorithm::kExponential,
+ absl::nullopt, TimeDelta::PlusInfinity(),
webrtc::TaskQueueBase::DelayPrecision::kLow));
EXPECT_EQ(create_timer_precison, webrtc::TaskQueueBase::DelayPrecision::kLow);
}
diff --git a/third_party/libwebrtc/net/dcsctp/tx/BUILD.gn b/third_party/libwebrtc/net/dcsctp/tx/BUILD.gn
index 5547ffa870..d1fd8ab3d5 100644
--- a/third_party/libwebrtc/net/dcsctp/tx/BUILD.gn
+++ b/third_party/libwebrtc/net/dcsctp/tx/BUILD.gn
@@ -11,6 +11,7 @@ import("../../../webrtc.gni")
rtc_source_set("send_queue") {
deps = [
"../../../api:array_view",
+ "../../../api/units:timestamp",
"../common:internal_types",
"../packet:chunk",
"../packet:data",
@@ -28,9 +29,9 @@ rtc_library("rr_send_queue") {
"../../../api:array_view",
"../../../rtc_base:checks",
"../../../rtc_base:logging",
+ "../../../rtc_base:stringutils",
"../../../rtc_base/containers:flat_map",
"../common:internal_types",
- "../common:str_join",
"../packet:data",
"../public:socket",
"../public:types",
@@ -53,9 +54,9 @@ rtc_library("stream_scheduler") {
"../../../api:array_view",
"../../../rtc_base:checks",
"../../../rtc_base:logging",
+ "../../../rtc_base:stringutils",
"../../../rtc_base:strong_alias",
"../../../rtc_base/containers:flat_set",
- "../common:str_join",
"../packet:chunk",
"../packet:data",
"../packet:sctp_packet",
@@ -89,6 +90,7 @@ rtc_library("retransmission_error_counter") {
rtc_library("retransmission_timeout") {
deps = [
+ "../../../api/units:time_delta",
"../../../rtc_base:checks",
"../public:types",
]
@@ -103,13 +105,15 @@ rtc_library("outstanding_data") {
":retransmission_timeout",
":send_queue",
"../../../api:array_view",
+ "../../../api/units:time_delta",
+ "../../../api/units:timestamp",
"../../../rtc_base:checks",
"../../../rtc_base:logging",
+ "../../../rtc_base:stringutils",
"../../../rtc_base/containers:flat_set",
"../common:internal_types",
"../common:math",
"../common:sequence_numbers",
- "../common:str_join",
"../packet:chunk",
"../packet:data",
"../public:socket",
@@ -138,7 +142,6 @@ rtc_library("retransmission_queue") {
"../../../rtc_base:stringutils",
"../common:math",
"../common:sequence_numbers",
- "../common:str_join",
"../packet:chunk",
"../packet:data",
"../public:socket",
@@ -162,6 +165,7 @@ if (rtc_include_tests) {
deps = [
":send_queue",
"../../../api:array_view",
+ "../../../api/units:timestamp",
"../../../test:test_support",
]
absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ]
diff --git a/third_party/libwebrtc/net/dcsctp/tx/mock_send_queue.h b/third_party/libwebrtc/net/dcsctp/tx/mock_send_queue.h
index 04921866ae..3511403eab 100644
--- a/third_party/libwebrtc/net/dcsctp/tx/mock_send_queue.h
+++ b/third_party/libwebrtc/net/dcsctp/tx/mock_send_queue.h
@@ -15,6 +15,7 @@
#include "absl/types/optional.h"
#include "api/array_view.h"
+#include "api/units/timestamp.h"
#include "net/dcsctp/tx/send_queue.h"
#include "test/gmock.h"
@@ -23,14 +24,15 @@ namespace dcsctp {
class MockSendQueue : public SendQueue {
public:
MockSendQueue() {
- ON_CALL(*this, Produce).WillByDefault([](TimeMs now, size_t max_size) {
- return absl::nullopt;
- });
+ ON_CALL(*this, Produce)
+ .WillByDefault([](webrtc::Timestamp now, size_t max_size) {
+ return absl::nullopt;
+ });
}
MOCK_METHOD(absl::optional<SendQueue::DataToSend>,
Produce,
- (TimeMs now, size_t max_size),
+ (webrtc::Timestamp now, size_t max_size),
(override));
MOCK_METHOD(bool,
Discard,
diff --git a/third_party/libwebrtc/net/dcsctp/tx/outstanding_data.cc b/third_party/libwebrtc/net/dcsctp/tx/outstanding_data.cc
index c2706bd0d2..ca639abc54 100644
--- a/third_party/libwebrtc/net/dcsctp/tx/outstanding_data.cc
+++ b/third_party/libwebrtc/net/dcsctp/tx/outstanding_data.cc
@@ -14,12 +14,16 @@
#include <utility>
#include <vector>
+#include "api/units/time_delta.h"
+#include "api/units/timestamp.h"
#include "net/dcsctp/common/math.h"
#include "net/dcsctp/common/sequence_numbers.h"
#include "net/dcsctp/public/types.h"
+#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
namespace dcsctp {
+using ::webrtc::Timestamp;
// The number of times a packet must be NACKed before it's retransmitted.
// See https://tools.ietf.org/html/rfc4960#section-7.2.4
@@ -63,18 +67,18 @@ void OutstandingData::Item::MarkAsRetransmitted() {
}
void OutstandingData::Item::Abandon() {
- RTC_DCHECK(expires_at_ != TimeMs::InfiniteFuture() ||
+ RTC_DCHECK(!expires_at_.IsPlusInfinity() ||
max_retransmissions_ != MaxRetransmits::NoLimit());
lifecycle_ = Lifecycle::kAbandoned;
}
-bool OutstandingData::Item::has_expired(TimeMs now) const {
+bool OutstandingData::Item::has_expired(Timestamp now) const {
return expires_at_ <= now;
}
bool OutstandingData::IsConsistent() const {
- size_t actual_outstanding_bytes = 0;
- size_t actual_outstanding_items = 0;
+ size_t actual_unacked_bytes = 0;
+ size_t actual_unacked_items = 0;
std::set<UnwrappedTSN> combined_to_be_retransmitted;
combined_to_be_retransmitted.insert(to_be_retransmitted_.begin(),
@@ -83,10 +87,12 @@ bool OutstandingData::IsConsistent() const {
to_be_fast_retransmitted_.end());
std::set<UnwrappedTSN> actual_combined_to_be_retransmitted;
- for (const auto& [tsn, item] : outstanding_data_) {
+ UnwrappedTSN tsn = last_cumulative_tsn_ack_;
+ for (const Item& item : outstanding_data_) {
+ tsn.Increment();
if (item.is_outstanding()) {
- actual_outstanding_bytes += GetSerializedChunkSize(item.data());
- ++actual_outstanding_items;
+ actual_unacked_bytes += GetSerializedChunkSize(item.data());
+ ++actual_unacked_items;
}
if (item.should_be_retransmitted()) {
@@ -94,33 +100,28 @@ bool OutstandingData::IsConsistent() const {
}
}
- if (outstanding_data_.empty() &&
- next_tsn_ != last_cumulative_tsn_ack_.next_value()) {
- return false;
- }
-
- return actual_outstanding_bytes == outstanding_bytes_ &&
- actual_outstanding_items == outstanding_items_ &&
+ return actual_unacked_bytes == unacked_bytes_ &&
+ actual_unacked_items == unacked_items_ &&
actual_combined_to_be_retransmitted == combined_to_be_retransmitted;
}
void OutstandingData::AckChunk(AckInfo& ack_info,
- std::map<UnwrappedTSN, Item>::iterator iter) {
- if (!iter->second.is_acked()) {
- size_t serialized_size = GetSerializedChunkSize(iter->second.data());
+ UnwrappedTSN tsn,
+ Item& item) {
+ if (!item.is_acked()) {
+ size_t serialized_size = GetSerializedChunkSize(item.data());
ack_info.bytes_acked += serialized_size;
- if (iter->second.is_outstanding()) {
- outstanding_bytes_ -= serialized_size;
- --outstanding_items_;
+ if (item.is_outstanding()) {
+ unacked_bytes_ -= serialized_size;
+ --unacked_items_;
}
- if (iter->second.should_be_retransmitted()) {
- RTC_DCHECK(to_be_fast_retransmitted_.find(iter->first) ==
+ if (item.should_be_retransmitted()) {
+ RTC_DCHECK(to_be_fast_retransmitted_.find(tsn) ==
to_be_fast_retransmitted_.end());
- to_be_retransmitted_.erase(iter->first);
+ to_be_retransmitted_.erase(tsn);
}
- iter->second.Ack();
- ack_info.highest_tsn_acked =
- std::max(ack_info.highest_tsn_acked, iter->first);
+ item.Ack();
+ ack_info.highest_tsn_acked = std::max(ack_info.highest_tsn_acked, tsn);
}
}
@@ -143,24 +144,43 @@ OutstandingData::AckInfo OutstandingData::HandleSack(
return ack_info;
}
+OutstandingData::Item& OutstandingData::GetItem(UnwrappedTSN tsn) {
+ RTC_DCHECK(tsn > last_cumulative_tsn_ack_);
+ RTC_DCHECK(tsn < next_tsn());
+ int index = UnwrappedTSN::Difference(tsn, last_cumulative_tsn_ack_) - 1;
+ RTC_DCHECK(index >= 0);
+ RTC_DCHECK(index < static_cast<int>(outstanding_data_.size()));
+ return outstanding_data_[index];
+}
+
+const OutstandingData::Item& OutstandingData::GetItem(UnwrappedTSN tsn) const {
+ RTC_DCHECK(tsn > last_cumulative_tsn_ack_);
+ RTC_DCHECK(tsn < next_tsn());
+ int index = UnwrappedTSN::Difference(tsn, last_cumulative_tsn_ack_) - 1;
+ RTC_DCHECK(index >= 0);
+ RTC_DCHECK(index < static_cast<int>(outstanding_data_.size()));
+ return outstanding_data_[index];
+}
+
void OutstandingData::RemoveAcked(UnwrappedTSN cumulative_tsn_ack,
AckInfo& ack_info) {
- auto first_unacked = outstanding_data_.upper_bound(cumulative_tsn_ack);
-
- for (auto iter = outstanding_data_.begin(); iter != first_unacked; ++iter) {
- AckChunk(ack_info, iter);
- if (iter->second.lifecycle_id().IsSet()) {
- RTC_DCHECK(iter->second.data().is_end);
- if (iter->second.is_abandoned()) {
- ack_info.abandoned_lifecycle_ids.push_back(iter->second.lifecycle_id());
+ while (!outstanding_data_.empty() &&
+ last_cumulative_tsn_ack_ < cumulative_tsn_ack) {
+ UnwrappedTSN tsn = last_cumulative_tsn_ack_.next_value();
+ Item& item = outstanding_data_.front();
+ AckChunk(ack_info, tsn, item);
+ if (item.lifecycle_id().IsSet()) {
+ RTC_DCHECK(item.data().is_end);
+ if (item.is_abandoned()) {
+ ack_info.abandoned_lifecycle_ids.push_back(item.lifecycle_id());
} else {
- ack_info.acked_lifecycle_ids.push_back(iter->second.lifecycle_id());
+ ack_info.acked_lifecycle_ids.push_back(item.lifecycle_id());
}
}
+ outstanding_data_.pop_front();
+ last_cumulative_tsn_ack_.Increment();
}
- outstanding_data_.erase(outstanding_data_.begin(), first_unacked);
- last_cumulative_tsn_ack_ = cumulative_tsn_ack;
stream_reset_breakpoint_tsns_.erase(stream_reset_breakpoint_tsns_.begin(),
stream_reset_breakpoint_tsns_.upper_bound(
cumulative_tsn_ack.next_value()));
@@ -176,12 +196,13 @@ void OutstandingData::AckGapBlocks(
// handled differently.
for (auto& block : gap_ack_blocks) {
- auto start = outstanding_data_.lower_bound(
- UnwrappedTSN::AddTo(cumulative_tsn_ack, block.start));
- auto end = outstanding_data_.upper_bound(
- UnwrappedTSN::AddTo(cumulative_tsn_ack, block.end));
- for (auto iter = start; iter != end; ++iter) {
- AckChunk(ack_info, iter);
+ UnwrappedTSN start = UnwrappedTSN::AddTo(cumulative_tsn_ack, block.start);
+ UnwrappedTSN end = UnwrappedTSN::AddTo(cumulative_tsn_ack, block.end);
+ for (UnwrappedTSN tsn = start; tsn <= end; tsn = tsn.next_value()) {
+ if (tsn > last_cumulative_tsn_ack_ && tsn < next_tsn()) {
+ Item& item = GetItem(tsn);
+ AckChunk(ack_info, tsn, item);
+ }
}
}
}
@@ -216,13 +237,12 @@ void OutstandingData::NackBetweenAckBlocks(
for (auto& block : gap_ack_blocks) {
UnwrappedTSN cur_block_first_acked =
UnwrappedTSN::AddTo(cumulative_tsn_ack, block.start);
- for (auto iter = outstanding_data_.upper_bound(prev_block_last_acked);
- iter != outstanding_data_.lower_bound(cur_block_first_acked); ++iter) {
- if (iter->first <= max_tsn_to_nack) {
- ack_info.has_packet_loss |=
- NackItem(iter->first, iter->second, /*retransmit_now=*/false,
- /*do_fast_retransmit=*/!is_in_fast_recovery);
- }
+ for (UnwrappedTSN tsn = prev_block_last_acked.next_value();
+ tsn < cur_block_first_acked && tsn <= max_tsn_to_nack;
+ tsn = tsn.next_value()) {
+ ack_info.has_packet_loss |=
+ NackItem(tsn, /*retransmit_now=*/false,
+ /*do_fast_retransmit=*/!is_in_fast_recovery);
}
prev_block_last_acked = UnwrappedTSN::AddTo(cumulative_tsn_ack, block.end);
}
@@ -234,12 +254,12 @@ void OutstandingData::NackBetweenAckBlocks(
}
bool OutstandingData::NackItem(UnwrappedTSN tsn,
- Item& item,
bool retransmit_now,
bool do_fast_retransmit) {
+ Item& item = GetItem(tsn);
if (item.is_outstanding()) {
- outstanding_bytes_ -= GetSerializedChunkSize(item.data());
- --outstanding_items_;
+ unacked_bytes_ -= GetSerializedChunkSize(item.data());
+ --unacked_items_;
}
switch (item.Nack(retransmit_now)) {
@@ -272,28 +292,25 @@ void OutstandingData::AbandonAllFor(const Item& item) {
// skipped over). So create a new fragment, representing the end, that the
// received will never see as it is abandoned immediately and used as cum
// TSN in the sent FORWARD-TSN.
- UnwrappedTSN tsn = next_tsn_;
- next_tsn_.Increment();
Data message_end(item.data().stream_id, item.data().ssn, item.data().mid,
item.data().fsn, item.data().ppid, std::vector<uint8_t>(),
Data::IsBeginning(false), Data::IsEnd(true),
item.data().is_unordered);
- Item& added_item =
- outstanding_data_
- .emplace(std::piecewise_construct, std::forward_as_tuple(tsn),
- std::forward_as_tuple(
- item.message_id(), std::move(message_end), TimeMs(0),
- MaxRetransmits(0), TimeMs::InfiniteFuture(),
- LifecycleId::NotSet()))
- .first->second;
- // The added chunk shouldn't be included in `outstanding_bytes`, so set it
+ UnwrappedTSN tsn = next_tsn();
+ Item& added_item = outstanding_data_.emplace_back(
+ item.message_id(), std::move(message_end), Timestamp::Zero(),
+ MaxRetransmits(0), Timestamp::PlusInfinity(), LifecycleId::NotSet());
+
+ // The added chunk shouldn't be included in `unacked_bytes`, so set it
// as acked.
added_item.Ack();
RTC_DLOG(LS_VERBOSE) << "Adding unsent end placeholder for message at tsn="
<< *tsn.Wrap();
}
- for (auto& [tsn, other] : outstanding_data_) {
+ UnwrappedTSN tsn = last_cumulative_tsn_ack_;
+ for (Item& other : outstanding_data_) {
+ tsn.Increment();
if (!other.is_abandoned() &&
other.data().stream_id == item.data().stream_id &&
other.message_id() == item.message_id()) {
@@ -315,9 +332,7 @@ std::vector<std::pair<TSN, Data>> OutstandingData::ExtractChunksThatCanFit(
for (auto it = chunks.begin(); it != chunks.end();) {
UnwrappedTSN tsn = *it;
- auto elem = outstanding_data_.find(tsn);
- RTC_DCHECK(elem != outstanding_data_.end());
- Item& item = elem->second;
+ Item& item = GetItem(tsn);
RTC_DCHECK(item.should_be_retransmitted());
RTC_DCHECK(!item.is_outstanding());
RTC_DCHECK(!item.is_abandoned());
@@ -328,8 +343,8 @@ std::vector<std::pair<TSN, Data>> OutstandingData::ExtractChunksThatCanFit(
item.MarkAsRetransmitted();
result.emplace_back(tsn.Wrap(), item.data().Clone());
max_size -= serialized_size;
- outstanding_bytes_ += serialized_size;
- ++outstanding_items_;
+ unacked_bytes_ += serialized_size;
+ ++unacked_items_;
it = chunks.erase(it);
} else {
++it;
@@ -370,8 +385,10 @@ std::vector<std::pair<TSN, Data>> OutstandingData::GetChunksToBeRetransmitted(
return ExtractChunksThatCanFit(to_be_retransmitted_, max_size);
}
-void OutstandingData::ExpireOutstandingChunks(TimeMs now) {
- for (const auto& [tsn, item] : outstanding_data_) {
+void OutstandingData::ExpireOutstandingChunks(Timestamp now) {
+ UnwrappedTSN tsn = last_cumulative_tsn_ack_;
+ for (const Item& item : outstanding_data_) {
+ tsn.Increment();
// Chunks that are nacked can be expired. Care should be taken not to expire
// unacked (in-flight) chunks as they might have been received, but the SACK
// is either delayed or in-flight and may be received later.
@@ -391,38 +408,33 @@ void OutstandingData::ExpireOutstandingChunks(TimeMs now) {
}
UnwrappedTSN OutstandingData::highest_outstanding_tsn() const {
- return outstanding_data_.empty() ? last_cumulative_tsn_ack_
- : outstanding_data_.rbegin()->first;
+ return UnwrappedTSN::AddTo(last_cumulative_tsn_ack_,
+ outstanding_data_.size());
}
absl::optional<UnwrappedTSN> OutstandingData::Insert(
OutgoingMessageId message_id,
const Data& data,
- TimeMs time_sent,
+ Timestamp time_sent,
MaxRetransmits max_retransmissions,
- TimeMs expires_at,
+ Timestamp expires_at,
LifecycleId lifecycle_id) {
- UnwrappedTSN tsn = next_tsn_;
- next_tsn_.Increment();
-
// All chunks are always padded to be even divisible by 4.
size_t chunk_size = GetSerializedChunkSize(data);
- outstanding_bytes_ += chunk_size;
- ++outstanding_items_;
- auto it = outstanding_data_
- .emplace(std::piecewise_construct, std::forward_as_tuple(tsn),
- std::forward_as_tuple(message_id, data.Clone(),
- time_sent, max_retransmissions,
- expires_at, lifecycle_id))
- .first;
-
- if (it->second.has_expired(time_sent)) {
+ unacked_bytes_ += chunk_size;
+ ++unacked_items_;
+ UnwrappedTSN tsn = next_tsn();
+ Item& item = outstanding_data_.emplace_back(message_id, data.Clone(),
+ time_sent, max_retransmissions,
+ expires_at, lifecycle_id);
+
+ if (item.has_expired(time_sent)) {
// No need to send it - it was expired when it was in the send
// queue.
- RTC_DLOG(LS_VERBOSE) << "Marking freshly produced chunk "
- << *it->first.Wrap() << " and message "
- << *it->second.data().mid << " as expired";
- AbandonAllFor(it->second);
+ RTC_DLOG(LS_VERBOSE) << "Marking freshly produced chunk " << *tsn.Wrap()
+ << " and message " << *item.data().mid
+ << " as expired";
+ AbandonAllFor(item);
RTC_DCHECK(IsConsistent());
return absl::nullopt;
}
@@ -432,34 +444,47 @@ absl::optional<UnwrappedTSN> OutstandingData::Insert(
}
void OutstandingData::NackAll() {
- for (auto& [tsn, item] : outstanding_data_) {
+ UnwrappedTSN tsn = last_cumulative_tsn_ack_;
+ // A two-pass algorithm is needed, as NackItem will invalidate iterators.
+ std::vector<UnwrappedTSN> tsns_to_nack;
+ for (Item& item : outstanding_data_) {
+ tsn.Increment();
if (!item.is_acked()) {
- NackItem(tsn, item, /*retransmit_now=*/true,
- /*do_fast_retransmit=*/false);
+ tsns_to_nack.push_back(tsn);
}
}
+
+ for (UnwrappedTSN tsn : tsns_to_nack) {
+ NackItem(tsn, /*retransmit_now=*/true,
+ /*do_fast_retransmit=*/false);
+ }
+
RTC_DCHECK(IsConsistent());
}
-absl::optional<DurationMs> OutstandingData::MeasureRTT(TimeMs now,
- UnwrappedTSN tsn) const {
- auto it = outstanding_data_.find(tsn);
- if (it != outstanding_data_.end() && !it->second.has_been_retransmitted()) {
- // https://tools.ietf.org/html/rfc4960#section-6.3.1
- // "Karn's algorithm: RTT measurements MUST NOT be made using
- // packets that were retransmitted (and thus for which it is ambiguous
- // whether the reply was for the first instance of the chunk or for a
- // later instance)"
- return now - it->second.time_sent();
+webrtc::TimeDelta OutstandingData::MeasureRTT(Timestamp now,
+ UnwrappedTSN tsn) const {
+ if (tsn > last_cumulative_tsn_ack_ && tsn < next_tsn()) {
+ const Item& item = GetItem(tsn);
+ if (!item.has_been_retransmitted()) {
+ // https://tools.ietf.org/html/rfc4960#section-6.3.1
+ // "Karn's algorithm: RTT measurements MUST NOT be made using
+ // packets that were retransmitted (and thus for which it is ambiguous
+ // whether the reply was for the first instance of the chunk or for a
+ // later instance)"
+ return now - item.time_sent();
+ }
}
- return absl::nullopt;
+ return webrtc::TimeDelta::PlusInfinity();
}
std::vector<std::pair<TSN, OutstandingData::State>>
OutstandingData::GetChunkStatesForTesting() const {
std::vector<std::pair<TSN, State>> states;
states.emplace_back(last_cumulative_tsn_ack_.Wrap(), State::kAcked);
- for (const auto& [tsn, item] : outstanding_data_) {
+ UnwrappedTSN tsn = last_cumulative_tsn_ack_;
+ for (const Item& item : outstanding_data_) {
+ tsn.Increment();
State state;
if (item.is_abandoned()) {
state = State::kAbandoned;
@@ -480,9 +505,7 @@ OutstandingData::GetChunkStatesForTesting() const {
bool OutstandingData::ShouldSendForwardTsn() const {
if (!outstanding_data_.empty()) {
- auto it = outstanding_data_.begin();
- return it->first == last_cumulative_tsn_ack_.next_value() &&
- it->second.is_abandoned();
+ return outstanding_data_.front().is_abandoned();
}
return false;
}
@@ -491,7 +514,9 @@ ForwardTsnChunk OutstandingData::CreateForwardTsn() const {
std::map<StreamID, SSN> skipped_per_ordered_stream;
UnwrappedTSN new_cumulative_ack = last_cumulative_tsn_ack_;
- for (const auto& [tsn, item] : outstanding_data_) {
+ UnwrappedTSN tsn = last_cumulative_tsn_ack_;
+ for (const Item& item : outstanding_data_) {
+ tsn.Increment();
if (stream_reset_breakpoint_tsns_.contains(tsn) ||
(tsn != new_cumulative_ack.next_value()) || !item.is_abandoned()) {
break;
@@ -515,7 +540,9 @@ IForwardTsnChunk OutstandingData::CreateIForwardTsn() const {
std::map<std::pair<IsUnordered, StreamID>, MID> skipped_per_stream;
UnwrappedTSN new_cumulative_ack = last_cumulative_tsn_ack_;
- for (const auto& [tsn, item] : outstanding_data_) {
+ UnwrappedTSN tsn = last_cumulative_tsn_ack_;
+ for (const Item& item : outstanding_data_) {
+ tsn.Increment();
if (stream_reset_breakpoint_tsns_.contains(tsn) ||
(tsn != new_cumulative_ack.next_value()) || !item.is_abandoned()) {
break;
@@ -539,16 +566,12 @@ IForwardTsnChunk OutstandingData::CreateIForwardTsn() const {
std::move(skipped_streams));
}
-void OutstandingData::ResetSequenceNumbers(UnwrappedTSN next_tsn,
- UnwrappedTSN last_cumulative_tsn) {
+void OutstandingData::ResetSequenceNumbers(UnwrappedTSN last_cumulative_tsn) {
RTC_DCHECK(outstanding_data_.empty());
- RTC_DCHECK(next_tsn_ == last_cumulative_tsn_ack_.next_value());
- RTC_DCHECK(next_tsn == last_cumulative_tsn.next_value());
- next_tsn_ = next_tsn;
last_cumulative_tsn_ack_ = last_cumulative_tsn;
}
void OutstandingData::BeginResetStreams() {
- stream_reset_breakpoint_tsns_.insert(next_tsn_);
+ stream_reset_breakpoint_tsns_.insert(next_tsn());
}
} // namespace dcsctp
diff --git a/third_party/libwebrtc/net/dcsctp/tx/outstanding_data.h b/third_party/libwebrtc/net/dcsctp/tx/outstanding_data.h
index f8e939661d..2a214975e6 100644
--- a/third_party/libwebrtc/net/dcsctp/tx/outstanding_data.h
+++ b/third_party/libwebrtc/net/dcsctp/tx/outstanding_data.h
@@ -10,12 +10,14 @@
#ifndef NET_DCSCTP_TX_OUTSTANDING_DATA_H_
#define NET_DCSCTP_TX_OUTSTANDING_DATA_H_
+#include <deque>
#include <map>
#include <set>
#include <utility>
#include <vector>
#include "absl/types/optional.h"
+#include "api/units/timestamp.h"
#include "net/dcsctp/common/internal_types.h"
#include "net/dcsctp/common/sequence_numbers.h"
#include "net/dcsctp/packet/chunk/forward_tsn_chunk.h"
@@ -29,6 +31,9 @@ namespace dcsctp {
// This class keeps track of outstanding data chunks (sent, not yet acked) and
// handles acking, nacking, rescheduling and abandoning.
+//
+// Items are added to this queue as they are sent and will be removed when the
+// peer acks them using the cumulative TSN ack.
class OutstandingData {
public:
// State for DATA chunks (message fragments) in the queue - used in tests.
@@ -74,11 +79,9 @@ class OutstandingData {
OutstandingData(
size_t data_chunk_header_size,
- UnwrappedTSN next_tsn,
UnwrappedTSN last_cumulative_tsn_ack,
std::function<bool(StreamID, OutgoingMessageId)> discard_from_send_queue)
: data_chunk_header_size_(data_chunk_header_size),
- next_tsn_(next_tsn),
last_cumulative_tsn_ack_(last_cumulative_tsn_ack),
discard_from_send_queue_(std::move(discard_from_send_queue)) {}
@@ -98,14 +101,14 @@ class OutstandingData {
// it?
std::vector<std::pair<TSN, Data>> GetChunksToBeRetransmitted(size_t max_size);
- size_t outstanding_bytes() const { return outstanding_bytes_; }
+ size_t unacked_bytes() const { return unacked_bytes_; }
- // Returns the number of DATA chunks that are in-flight.
- size_t outstanding_items() const { return outstanding_items_; }
+ // Returns the number of DATA chunks that are in-flight (not acked or nacked).
+ size_t unacked_items() const { return unacked_items_; }
// Given the current time `now_ms`, expire and abandon outstanding (sent at
// least once) chunks that have a limited lifetime.
- void ExpireOutstandingChunks(TimeMs now);
+ void ExpireOutstandingChunks(webrtc::Timestamp now);
bool empty() const { return outstanding_data_.empty(); }
@@ -121,7 +124,9 @@ class OutstandingData {
return last_cumulative_tsn_ack_;
}
- UnwrappedTSN next_tsn() const { return next_tsn_; }
+ UnwrappedTSN next_tsn() const {
+ return highest_outstanding_tsn().next_value();
+ }
UnwrappedTSN highest_outstanding_tsn() const;
@@ -131,9 +136,9 @@ class OutstandingData {
absl::optional<UnwrappedTSN> Insert(
OutgoingMessageId message_id,
const Data& data,
- TimeMs time_sent,
+ webrtc::Timestamp time_sent,
MaxRetransmits max_retransmissions = MaxRetransmits::NoLimit(),
- TimeMs expires_at = TimeMs::InfiniteFuture(),
+ webrtc::Timestamp expires_at = webrtc::Timestamp::PlusInfinity(),
LifecycleId lifecycle_id = LifecycleId::NotSet());
// Nacks all outstanding data.
@@ -147,8 +152,8 @@ class OutstandingData {
// Given the current time and a TSN, it returns the measured RTT between when
// the chunk was sent and now. It takes into acccount Karn's algorithm, so if
- // the chunk has ever been retransmitted, it will return absl::nullopt.
- absl::optional<DurationMs> MeasureRTT(TimeMs now, UnwrappedTSN tsn) const;
+ // the chunk has ever been retransmitted, it will return `PlusInfinity()`.
+ webrtc::TimeDelta MeasureRTT(webrtc::Timestamp now, UnwrappedTSN tsn) const;
// Returns the internal state of all queued chunks. This is only used in
// unit-tests.
@@ -159,8 +164,7 @@ class OutstandingData {
bool ShouldSendForwardTsn() const;
// Sets the next TSN to be used. This is used in handover.
- void ResetSequenceNumbers(UnwrappedTSN next_tsn,
- UnwrappedTSN last_cumulative_tsn);
+ void ResetSequenceNumbers(UnwrappedTSN last_cumulative_tsn);
// Called when an outgoing stream reset is sent, marking the last assigned TSN
// as a breakpoint that a FORWARD-TSN shouldn't cross.
@@ -179,9 +183,9 @@ class OutstandingData {
Item(OutgoingMessageId message_id,
Data data,
- TimeMs time_sent,
+ webrtc::Timestamp time_sent,
MaxRetransmits max_retransmissions,
- TimeMs expires_at,
+ webrtc::Timestamp expires_at,
LifecycleId lifecycle_id)
: message_id_(message_id),
time_sent_(time_sent),
@@ -195,7 +199,7 @@ class OutstandingData {
OutgoingMessageId message_id() const { return message_id_; }
- TimeMs time_sent() const { return time_sent_; }
+ webrtc::Timestamp time_sent() const { return time_sent_; }
const Data& data() const { return data_; }
@@ -229,7 +233,7 @@ class OutstandingData {
// Given the current time, and the current state of this DATA chunk, it will
// indicate if it has expired (SCTP Partial Reliability Extension).
- bool has_expired(TimeMs now) const;
+ bool has_expired(webrtc::Timestamp now) const;
LifecycleId lifecycle_id() const { return lifecycle_id_; }
@@ -258,7 +262,7 @@ class OutstandingData {
const OutgoingMessageId message_id_;
// When the packet was sent, and placed in this queue.
- const TimeMs time_sent_;
+ const webrtc::Timestamp time_sent_;
// If the message was sent with a maximum number of retransmissions, this is
// set to that number. The value zero (0) means that it will never be
// retransmitted.
@@ -278,7 +282,7 @@ class OutstandingData {
// At this exact millisecond, the item is considered expired. If the message
// is not to be expired, this is set to the infinite future.
- const TimeMs expires_at_;
+ const webrtc::Timestamp expires_at_;
// An optional lifecycle id, which may only be set for the last fragment.
const LifecycleId lifecycle_id_;
@@ -290,6 +294,9 @@ class OutstandingData {
// Returns how large a chunk will be, serialized, carrying the data
size_t GetSerializedChunkSize(const Data& data) const;
+ Item& GetItem(UnwrappedTSN tsn);
+ const Item& GetItem(UnwrappedTSN tsn) const;
+
// Given a `cumulative_tsn_ack` from an incoming SACK, will remove those items
// in the retransmission queue up until this value and will update `ack_info`
// by setting `bytes_acked_by_cumulative_tsn_ack`.
@@ -313,7 +320,7 @@ class OutstandingData {
// Process the acknowledgement of the chunk referenced by `iter` and updates
// state in `ack_info` and the object's state.
- void AckChunk(AckInfo& ack_info, std::map<UnwrappedTSN, Item>::iterator iter);
+ void AckChunk(AckInfo& ack_info, UnwrappedTSN tsn, Item& item);
// Helper method to process an incoming nack of an item and perform the
// correct operations given the action indicated when nacking an item (e.g.
@@ -323,10 +330,11 @@ class OutstandingData {
// many times so that it should be retransmitted, this will schedule it to be
// "fast retransmitted". This is only done just before going into fast
// recovery.
- bool NackItem(UnwrappedTSN tsn,
- Item& item,
- bool retransmit_now,
- bool do_fast_retransmit);
+ //
+ // Note that since nacking an item may result in it becoming abandoned, which
+ // in turn could alter `outstanding_data_`, any iterators are invalidated
+ // after having called this method.
+ bool NackItem(UnwrappedTSN tsn, bool retransmit_now, bool do_fast_retransmit);
// Given that a message fragment, `item` has been abandoned, abandon all other
// fragments that share the same message - both never-before-sent fragments
@@ -341,19 +349,20 @@ class OutstandingData {
// The size of the data chunk (DATA/I-DATA) header that is used.
const size_t data_chunk_header_size_;
- // Next TSN to used.
- UnwrappedTSN next_tsn_;
// The last cumulative TSN ack number.
UnwrappedTSN last_cumulative_tsn_ack_;
// Callback when to discard items from the send queue.
std::function<bool(StreamID, OutgoingMessageId)> discard_from_send_queue_;
- std::map<UnwrappedTSN, Item> outstanding_data_;
+ // Outstanding items. If non-empty, the first element has
+ // `TSN=last_cumulative_tsn_ack_ + 1` and the following items are in strict
+ // increasing TSN order. The last item has `TSN=highest_outstanding_tsn()`.
+ std::deque<Item> outstanding_data_;
// The number of bytes that are in-flight (sent but not yet acked or nacked).
- size_t outstanding_bytes_ = 0;
+ size_t unacked_bytes_ = 0;
// The number of DATA chunks that are in-flight (sent but not yet acked or
// nacked).
- size_t outstanding_items_ = 0;
+ size_t unacked_items_ = 0;
// Data chunks that are eligible for fast retransmission.
std::set<UnwrappedTSN> to_be_fast_retransmitted_;
// Data chunks that are to be retransmitted.
diff --git a/third_party/libwebrtc/net/dcsctp/tx/outstanding_data_test.cc b/third_party/libwebrtc/net/dcsctp/tx/outstanding_data_test.cc
index b8c2e593a1..e4bdb7ce7e 100644
--- a/third_party/libwebrtc/net/dcsctp/tx/outstanding_data_test.cc
+++ b/third_party/libwebrtc/net/dcsctp/tx/outstanding_data_test.cc
@@ -37,8 +37,10 @@ using ::testing::Property;
using ::testing::Return;
using ::testing::StrictMock;
using ::testing::UnorderedElementsAre;
+using ::webrtc::TimeDelta;
+using ::webrtc::Timestamp;
-constexpr TimeMs kNow(42);
+constexpr Timestamp kNow = Timestamp::Millis(42);
constexpr OutgoingMessageId kMessageId = OutgoingMessageId(17);
class OutstandingDataTest : public testing::Test {
@@ -46,7 +48,6 @@ class OutstandingDataTest : public testing::Test {
OutstandingDataTest()
: gen_(MID(42)),
buf_(DataChunk::kHeaderSize,
- unwrapper_.Unwrap(TSN(10)),
unwrapper_.Unwrap(TSN(9)),
on_discard_.AsStdFunction()) {}
@@ -58,8 +59,8 @@ class OutstandingDataTest : public testing::Test {
TEST_F(OutstandingDataTest, HasInitialState) {
EXPECT_TRUE(buf_.empty());
- EXPECT_EQ(buf_.outstanding_bytes(), 0u);
- EXPECT_EQ(buf_.outstanding_items(), 0u);
+ EXPECT_EQ(buf_.unacked_bytes(), 0u);
+ EXPECT_EQ(buf_.unacked_items(), 0u);
EXPECT_FALSE(buf_.has_data_to_be_retransmitted());
EXPECT_EQ(buf_.last_cumulative_tsn_ack().Wrap(), TSN(9));
EXPECT_EQ(buf_.next_tsn().Wrap(), TSN(10));
@@ -75,8 +76,8 @@ TEST_F(OutstandingDataTest, InsertChunk) {
EXPECT_EQ(tsn.Wrap(), TSN(10));
- EXPECT_EQ(buf_.outstanding_bytes(), DataChunk::kHeaderSize + RoundUpTo4(1));
- EXPECT_EQ(buf_.outstanding_items(), 1u);
+ EXPECT_EQ(buf_.unacked_bytes(), DataChunk::kHeaderSize + RoundUpTo4(1));
+ EXPECT_EQ(buf_.unacked_items(), 1u);
EXPECT_FALSE(buf_.has_data_to_be_retransmitted());
EXPECT_EQ(buf_.last_cumulative_tsn_ack().Wrap(), TSN(9));
EXPECT_EQ(buf_.next_tsn().Wrap(), TSN(11));
@@ -95,8 +96,8 @@ TEST_F(OutstandingDataTest, AcksSingleChunk) {
EXPECT_EQ(ack.highest_tsn_acked.Wrap(), TSN(10));
EXPECT_FALSE(ack.has_packet_loss);
- EXPECT_EQ(buf_.outstanding_bytes(), 0u);
- EXPECT_EQ(buf_.outstanding_items(), 0u);
+ EXPECT_EQ(buf_.unacked_bytes(), 0u);
+ EXPECT_EQ(buf_.unacked_items(), 0u);
EXPECT_FALSE(buf_.has_data_to_be_retransmitted());
EXPECT_EQ(buf_.last_cumulative_tsn_ack().Wrap(), TSN(10));
EXPECT_EQ(buf_.next_tsn().Wrap(), TSN(11));
@@ -109,8 +110,8 @@ TEST_F(OutstandingDataTest, AcksPreviousChunkDoesntUpdate) {
buf_.Insert(kMessageId, gen_.Ordered({1}, "BE"), kNow);
buf_.HandleSack(unwrapper_.Unwrap(TSN(9)), {}, false);
- EXPECT_EQ(buf_.outstanding_bytes(), DataChunk::kHeaderSize + RoundUpTo4(1));
- EXPECT_EQ(buf_.outstanding_items(), 1u);
+ EXPECT_EQ(buf_.unacked_bytes(), DataChunk::kHeaderSize + RoundUpTo4(1));
+ EXPECT_EQ(buf_.unacked_items(), 1u);
EXPECT_FALSE(buf_.has_data_to_be_retransmitted());
EXPECT_EQ(buf_.last_cumulative_tsn_ack().Wrap(), TSN(9));
EXPECT_EQ(buf_.next_tsn().Wrap(), TSN(11));
@@ -131,8 +132,8 @@ TEST_F(OutstandingDataTest, AcksAndNacksWithGapAckBlocks) {
EXPECT_EQ(ack.highest_tsn_acked.Wrap(), TSN(11));
EXPECT_FALSE(ack.has_packet_loss);
- EXPECT_EQ(buf_.outstanding_bytes(), 0u);
- EXPECT_EQ(buf_.outstanding_items(), 0u);
+ EXPECT_EQ(buf_.unacked_bytes(), 0u);
+ EXPECT_EQ(buf_.unacked_items(), 0u);
EXPECT_FALSE(buf_.has_data_to_be_retransmitted());
EXPECT_EQ(buf_.last_cumulative_tsn_ack().Wrap(), TSN(9));
EXPECT_EQ(buf_.next_tsn().Wrap(), TSN(12));
@@ -277,20 +278,20 @@ TEST_F(OutstandingDataTest, NacksThreeTimesResultsInAbandoningWithPlaceholder) {
}
TEST_F(OutstandingDataTest, ExpiresChunkBeforeItIsInserted) {
- static constexpr TimeMs kExpiresAt = kNow + DurationMs(1);
+ static constexpr Timestamp kExpiresAt = kNow + TimeDelta::Millis(1);
EXPECT_TRUE(buf_.Insert(kMessageId, gen_.Ordered({1}, "B"), kNow,
MaxRetransmits::NoLimit(), kExpiresAt)
.has_value());
EXPECT_TRUE(buf_.Insert(kMessageId, gen_.Ordered({1}, ""),
- kNow + DurationMs(0), MaxRetransmits::NoLimit(),
- kExpiresAt)
+ kNow + TimeDelta::Millis(0),
+ MaxRetransmits::NoLimit(), kExpiresAt)
.has_value());
EXPECT_CALL(on_discard_, Call(StreamID(1), kMessageId))
.WillOnce(Return(false));
EXPECT_FALSE(buf_.Insert(kMessageId, gen_.Ordered({1}, "E"),
- kNow + DurationMs(1), MaxRetransmits::NoLimit(),
- kExpiresAt)
+ kNow + TimeDelta::Millis(1),
+ MaxRetransmits::NoLimit(), kExpiresAt)
.has_value());
EXPECT_FALSE(buf_.has_data_to_be_retransmitted());
@@ -362,15 +363,14 @@ TEST_F(OutstandingDataTest, AckWithGapBlocksFromRFC4960Section334) {
TEST_F(OutstandingDataTest, MeasureRTT) {
buf_.Insert(kMessageId, gen_.Ordered({1}, "BE"), kNow);
- buf_.Insert(kMessageId, gen_.Ordered({1}, "BE"), kNow + DurationMs(1));
- buf_.Insert(kMessageId, gen_.Ordered({1}, "BE"), kNow + DurationMs(2));
+ buf_.Insert(kMessageId, gen_.Ordered({1}, "BE"), kNow + TimeDelta::Millis(1));
+ buf_.Insert(kMessageId, gen_.Ordered({1}, "BE"), kNow + TimeDelta::Millis(2));
- static constexpr DurationMs kDuration(123);
- ASSERT_HAS_VALUE_AND_ASSIGN(
- DurationMs duration,
- buf_.MeasureRTT(kNow + kDuration, unwrapper_.Unwrap(TSN(11))));
+ static constexpr TimeDelta kDuration = TimeDelta::Millis(123);
+ TimeDelta duration =
+ buf_.MeasureRTT(kNow + kDuration, unwrapper_.Unwrap(TSN(11)));
- EXPECT_EQ(duration, kDuration - DurationMs(1));
+ EXPECT_EQ(duration, kDuration - TimeDelta::Millis(1));
}
TEST_F(OutstandingDataTest, MustRetransmitBeforeGettingNackedAgain) {
@@ -453,13 +453,13 @@ TEST_F(OutstandingDataTest, MustRetransmitBeforeGettingNackedAgain) {
TEST_F(OutstandingDataTest, LifecyleReturnsAckedItemsInAckInfo) {
buf_.Insert(OutgoingMessageId(1), gen_.Ordered({1}, "BE"), kNow,
- MaxRetransmits::NoLimit(), TimeMs::InfiniteFuture(),
+ MaxRetransmits::NoLimit(), Timestamp::PlusInfinity(),
LifecycleId(42));
buf_.Insert(OutgoingMessageId(2), gen_.Ordered({1}, "BE"), kNow,
- MaxRetransmits::NoLimit(), TimeMs::InfiniteFuture(),
+ MaxRetransmits::NoLimit(), Timestamp::PlusInfinity(),
LifecycleId(43));
buf_.Insert(OutgoingMessageId(3), gen_.Ordered({1}, "BE"), kNow,
- MaxRetransmits::NoLimit(), TimeMs::InfiniteFuture(),
+ MaxRetransmits::NoLimit(), Timestamp::PlusInfinity(),
LifecycleId(44));
OutstandingData::AckInfo ack1 =
@@ -479,7 +479,7 @@ TEST_F(OutstandingDataTest, LifecycleReturnsAbandonedNackedThreeTimes) {
buf_.Insert(kMessageId, gen_.Ordered({1}, ""), kNow, MaxRetransmits(0));
buf_.Insert(kMessageId, gen_.Ordered({1}, ""), kNow, MaxRetransmits(0));
buf_.Insert(kMessageId, gen_.Ordered({1}, "E"), kNow, MaxRetransmits(0),
- TimeMs::InfiniteFuture(), LifecycleId(42));
+ Timestamp::PlusInfinity(), LifecycleId(42));
std::vector<SackChunk::GapAckBlock> gab1 = {SackChunk::GapAckBlock(2, 2)};
EXPECT_FALSE(
@@ -515,7 +515,7 @@ TEST_F(OutstandingDataTest, LifecycleReturnsAbandonedAfterT3rtxExpired) {
buf_.Insert(kMessageId, gen_.Ordered({1}, ""), kNow, MaxRetransmits(0));
buf_.Insert(kMessageId, gen_.Ordered({1}, ""), kNow, MaxRetransmits(0));
buf_.Insert(kMessageId, gen_.Ordered({1}, "E"), kNow, MaxRetransmits(0),
- TimeMs::InfiniteFuture(), LifecycleId(42));
+ Timestamp::PlusInfinity(), LifecycleId(42));
EXPECT_THAT(buf_.GetChunkStatesForTesting(),
testing::ElementsAre(Pair(TSN(9), State::kAcked), //
diff --git a/third_party/libwebrtc/net/dcsctp/tx/retransmission_queue.cc b/third_party/libwebrtc/net/dcsctp/tx/retransmission_queue.cc
index 2b9843f4a7..8c0d227a36 100644
--- a/third_party/libwebrtc/net/dcsctp/tx/retransmission_queue.cc
+++ b/third_party/libwebrtc/net/dcsctp/tx/retransmission_queue.cc
@@ -25,7 +25,6 @@
#include "api/array_view.h"
#include "net/dcsctp/common/math.h"
#include "net/dcsctp/common/sequence_numbers.h"
-#include "net/dcsctp/common/str_join.h"
#include "net/dcsctp/packet/chunk/data_chunk.h"
#include "net/dcsctp/packet/chunk/forward_tsn_chunk.h"
#include "net/dcsctp/packet/chunk/forward_tsn_common.h"
@@ -40,10 +39,13 @@
#include "net/dcsctp/tx/send_queue.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
+#include "rtc_base/strings/str_join.h"
#include "rtc_base/strings/string_builder.h"
namespace dcsctp {
namespace {
+using ::webrtc::TimeDelta;
+using ::webrtc::Timestamp;
// Allow sending only slightly less than an MTU, to account for headers.
constexpr float kMinBytesRequiredToSendFactor = 0.9;
@@ -55,7 +57,7 @@ RetransmissionQueue::RetransmissionQueue(
TSN my_initial_tsn,
size_t a_rwnd,
SendQueue& send_queue,
- std::function<void(DurationMs rtt)> on_new_rtt,
+ std::function<void(TimeDelta rtt)> on_new_rtt,
std::function<void()> on_clear_retransmission_counter,
Timer& t3_rtx,
const DcSctpOptions& options,
@@ -84,7 +86,6 @@ RetransmissionQueue::RetransmissionQueue(
send_queue_(send_queue),
outstanding_data_(
data_chunk_header_size_,
- tsn_unwrapper_.Unwrap(my_initial_tsn),
tsn_unwrapper_.Unwrap(TSN(*my_initial_tsn - 1)),
[this](StreamID stream_id, OutgoingMessageId message_id) {
return send_queue_.Discard(stream_id, message_id);
@@ -114,12 +115,12 @@ void RetransmissionQueue::MaybeExitFastRecovery(
}
void RetransmissionQueue::HandleIncreasedCumulativeTsnAck(
- size_t outstanding_bytes,
+ size_t unacked_bytes,
size_t total_bytes_acked) {
// Allow some margin for classifying as fully utilized, due to e.g. that too
// small packets (less than kMinimumFragmentedPayload) are not sent +
// overhead.
- bool is_fully_utilized = outstanding_bytes + options_.mtu >= cwnd_;
+ bool is_fully_utilized = unacked_bytes + options_.mtu >= cwnd_;
size_t old_cwnd = cwnd_;
if (phase() == CongestionAlgorithmPhase::kSlowStart) {
if (is_fully_utilized && !is_in_fast_recovery()) {
@@ -204,13 +205,13 @@ void RetransmissionQueue::HandlePacketLoss(UnwrappedTSN highest_tsn_acked) {
}
void RetransmissionQueue::UpdateReceiverWindow(uint32_t a_rwnd) {
- rwnd_ = outstanding_data_.outstanding_bytes() >= a_rwnd
+ rwnd_ = outstanding_data_.unacked_bytes() >= a_rwnd
? 0
- : a_rwnd - outstanding_data_.outstanding_bytes();
+ : a_rwnd - outstanding_data_.unacked_bytes();
}
void RetransmissionQueue::StartT3RtxTimerIfOutstandingData() {
- // Note: Can't use `outstanding_bytes()` as that one doesn't count chunks to
+ // Note: Can't use `unacked_bytes()` as that one doesn't count chunks to
// be retransmitted.
if (outstanding_data_.empty()) {
// https://tools.ietf.org/html/rfc4960#section-6.3.2
@@ -257,14 +258,14 @@ bool RetransmissionQueue::IsSackValid(const SackChunk& sack) const {
return true;
}
-bool RetransmissionQueue::HandleSack(TimeMs now, const SackChunk& sack) {
+bool RetransmissionQueue::HandleSack(Timestamp now, const SackChunk& sack) {
if (!IsSackValid(sack)) {
return false;
}
UnwrappedTSN old_last_cumulative_tsn_ack =
outstanding_data_.last_cumulative_tsn_ack();
- size_t old_outstanding_bytes = outstanding_data_.outstanding_bytes();
+ size_t old_unacked_bytes = outstanding_data_.unacked_bytes();
size_t old_rwnd = rwnd_;
UnwrappedTSN cumulative_tsn_ack =
tsn_unwrapper_.Unwrap(sack.cumulative_tsn_ack());
@@ -301,9 +302,9 @@ bool RetransmissionQueue::HandleSack(TimeMs now, const SackChunk& sack) {
RTC_DLOG(LS_VERBOSE) << log_prefix_ << "Received SACK, cum_tsn_ack="
<< *cumulative_tsn_ack.Wrap() << " ("
<< *old_last_cumulative_tsn_ack.Wrap()
- << "), outstanding_bytes="
- << outstanding_data_.outstanding_bytes() << " ("
- << old_outstanding_bytes << "), rwnd=" << rwnd_ << " ("
+ << "), unacked_bytes="
+ << outstanding_data_.unacked_bytes() << " ("
+ << old_unacked_bytes << "), rwnd=" << rwnd_ << " ("
<< old_rwnd << ")";
if (cumulative_tsn_ack > old_last_cumulative_tsn_ack) {
@@ -315,8 +316,7 @@ bool RetransmissionQueue::HandleSack(TimeMs now, const SackChunk& sack) {
// Note: It may be started again in a bit further down.
t3_rtx_.Stop();
- HandleIncreasedCumulativeTsnAck(old_outstanding_bytes,
- ack_info.bytes_acked);
+ HandleIncreasedCumulativeTsnAck(old_unacked_bytes, ack_info.bytes_acked);
}
if (ack_info.has_packet_loss) {
@@ -335,7 +335,7 @@ bool RetransmissionQueue::HandleSack(TimeMs now, const SackChunk& sack) {
return true;
}
-void RetransmissionQueue::UpdateRTT(TimeMs now,
+void RetransmissionQueue::UpdateRTT(Timestamp now,
UnwrappedTSN cumulative_tsn_ack) {
// RTT updating is flawed in SCTP, as explained in e.g. Pedersen J, Griwodz C,
// Halvorsen P (2006) Considerations of SCTP retransmission delays for thin
@@ -345,17 +345,16 @@ void RetransmissionQueue::UpdateRTT(TimeMs now,
// TODO(boivie): Consider occasionally sending DATA chunks with I-bit set and
// use only those packets for measurement.
- absl::optional<DurationMs> rtt =
- outstanding_data_.MeasureRTT(now, cumulative_tsn_ack);
+ TimeDelta rtt = outstanding_data_.MeasureRTT(now, cumulative_tsn_ack);
- if (rtt.has_value()) {
- on_new_rtt_(*rtt);
+ if (rtt.IsFinite()) {
+ on_new_rtt_(rtt);
}
}
void RetransmissionQueue::HandleT3RtxTimerExpiry() {
size_t old_cwnd = cwnd_;
- size_t old_outstanding_bytes = outstanding_bytes();
+ size_t old_unacked_bytes = unacked_bytes();
// https://tools.ietf.org/html/rfc4960#section-6.3.3
// "For the destination address for which the timer expires, adjust
// its ssthresh with rules defined in Section 7.2.3 and set the cwnd <- MTU."
@@ -392,8 +391,8 @@ void RetransmissionQueue::HandleT3RtxTimerExpiry() {
RTC_DLOG(LS_INFO) << log_prefix_ << "t3-rtx expired. new cwnd=" << cwnd_
<< " (" << old_cwnd << "), ssthresh=" << ssthresh_
- << ", outstanding_bytes " << outstanding_bytes() << " ("
- << old_outstanding_bytes << ")";
+ << ", unacked_bytes " << unacked_bytes() << " ("
+ << old_unacked_bytes << ")";
RTC_DCHECK(IsConsistent());
}
@@ -402,7 +401,7 @@ RetransmissionQueue::GetChunksForFastRetransmit(size_t bytes_in_packet) {
RTC_DCHECK(outstanding_data_.has_data_to_be_fast_retransmitted());
RTC_DCHECK(IsDivisibleBy4(bytes_in_packet));
std::vector<std::pair<TSN, Data>> to_be_sent;
- size_t old_outstanding_bytes = outstanding_bytes();
+ size_t old_unacked_bytes = unacked_bytes();
to_be_sent =
outstanding_data_.GetChunksToBeFastRetransmitted(bytes_in_packet);
@@ -441,21 +440,21 @@ RetransmissionQueue::GetChunksForFastRetransmit(size_t bytes_in_packet) {
sb << *c.first;
})
<< " - " << bytes_retransmitted
- << " bytes. outstanding_bytes=" << outstanding_bytes()
- << " (" << old_outstanding_bytes << ")";
+ << " bytes. unacked_bytes=" << unacked_bytes() << " ("
+ << old_unacked_bytes << ")";
RTC_DCHECK(IsConsistent());
return to_be_sent;
}
std::vector<std::pair<TSN, Data>> RetransmissionQueue::GetChunksToSend(
- TimeMs now,
+ Timestamp now,
size_t bytes_remaining_in_packet) {
// Chunks are always padded to even divisible by four.
RTC_DCHECK(IsDivisibleBy4(bytes_remaining_in_packet));
std::vector<std::pair<TSN, Data>> to_be_sent;
- size_t old_outstanding_bytes = outstanding_bytes();
+ size_t old_unacked_bytes = unacked_bytes();
size_t old_rwnd = rwnd_;
// Calculate the bandwidth budget (how many bytes that is
@@ -494,7 +493,8 @@ std::vector<std::pair<TSN, Data>> RetransmissionQueue::GetChunksToSend(
chunk_opt->message_id, chunk_opt->data, now,
partial_reliability_ ? chunk_opt->max_retransmissions
: MaxRetransmits::NoLimit(),
- partial_reliability_ ? chunk_opt->expires_at : TimeMs::InfiniteFuture(),
+ partial_reliability_ ? chunk_opt->expires_at
+ : Timestamp::PlusInfinity(),
chunk_opt->lifecycle_id);
if (tsn.has_value()) {
@@ -526,8 +526,8 @@ std::vector<std::pair<TSN, Data>> RetransmissionQueue::GetChunksToSend(
[&](size_t r, const std::pair<TSN, Data>& d) {
return r + GetSerializedChunkSize(d.second);
})
- << " bytes. outstanding_bytes=" << outstanding_bytes()
- << " (" << old_outstanding_bytes << "), cwnd=" << cwnd_
+ << " bytes. unacked_bytes=" << unacked_bytes() << " ("
+ << old_unacked_bytes << "), cwnd=" << cwnd_
<< ", rwnd=" << rwnd_ << " (" << old_rwnd << ")";
}
RTC_DCHECK(IsConsistent());
@@ -539,7 +539,7 @@ bool RetransmissionQueue::can_send_data() const {
max_bytes_to_send() >= min_bytes_required_to_send_;
}
-bool RetransmissionQueue::ShouldSendForwardTsn(TimeMs now) {
+bool RetransmissionQueue::ShouldSendForwardTsn(Timestamp now) {
if (!partial_reliability_) {
return false;
}
@@ -550,9 +550,9 @@ bool RetransmissionQueue::ShouldSendForwardTsn(TimeMs now) {
}
size_t RetransmissionQueue::max_bytes_to_send() const {
- size_t left = outstanding_bytes() >= cwnd_ ? 0 : cwnd_ - outstanding_bytes();
+ size_t left = unacked_bytes() >= cwnd_ ? 0 : cwnd_ - unacked_bytes();
- if (outstanding_bytes() == 0) {
+ if (unacked_bytes() == 0) {
// https://datatracker.ietf.org/doc/html/rfc4960#section-6.1
// ... However, regardless of the value of rwnd (including if it is 0), the
// data sender can always have one DATA chunk in flight to the receiver if
@@ -619,7 +619,6 @@ void RetransmissionQueue::RestoreFromState(
partial_bytes_acked_ = state.tx.partial_bytes_acked;
outstanding_data_.ResetSequenceNumbers(
- tsn_unwrapper_.Unwrap(TSN(state.tx.next_tsn)),
tsn_unwrapper_.Unwrap(TSN(state.tx.next_tsn - 1)));
}
} // namespace dcsctp
diff --git a/third_party/libwebrtc/net/dcsctp/tx/retransmission_queue.h b/third_party/libwebrtc/net/dcsctp/tx/retransmission_queue.h
index b44db2a9a0..a0fbb33c47 100644
--- a/third_party/libwebrtc/net/dcsctp/tx/retransmission_queue.h
+++ b/third_party/libwebrtc/net/dcsctp/tx/retransmission_queue.h
@@ -60,7 +60,7 @@ class RetransmissionQueue {
TSN my_initial_tsn,
size_t a_rwnd,
SendQueue& send_queue,
- std::function<void(DurationMs rtt)> on_new_rtt,
+ std::function<void(webrtc::TimeDelta rtt)> on_new_rtt,
std::function<void()> on_clear_retransmission_counter,
Timer& t3_rtx,
const DcSctpOptions& options,
@@ -69,7 +69,7 @@ class RetransmissionQueue {
// Handles a received SACK. Returns true if the `sack` was processed and
// false if it was discarded due to received out-of-order and not relevant.
- bool HandleSack(TimeMs now, const SackChunk& sack);
+ bool HandleSack(webrtc::Timestamp now, const SackChunk& sack);
// Handles an expired retransmission timer.
void HandleT3RtxTimerExpiry();
@@ -90,7 +90,7 @@ class RetransmissionQueue {
// called prior to this method, to abandon expired chunks, as this method will
// not expire any chunks.
std::vector<std::pair<TSN, Data>> GetChunksToSend(
- TimeMs now,
+ webrtc::Timestamp now,
size_t bytes_remaining_in_packet);
// Returns the internal state of all queued chunks. This is only used in
@@ -121,14 +121,10 @@ class RetransmissionQueue {
uint64_t rtx_bytes_count() const { return rtx_bytes_count_; }
// Returns the number of bytes of packets that are in-flight.
- size_t outstanding_bytes() const {
- return outstanding_data_.outstanding_bytes();
- }
+ size_t unacked_bytes() const { return outstanding_data_.unacked_bytes(); }
// Returns the number of DATA chunks that are in-flight.
- size_t outstanding_items() const {
- return outstanding_data_.outstanding_items();
- }
+ size_t unacked_items() const { return outstanding_data_.unacked_items(); }
// Indicates if the congestion control algorithm allows data to be sent.
bool can_send_data() const;
@@ -136,7 +132,7 @@ class RetransmissionQueue {
// Given the current time `now`, it will evaluate if there are chunks that
// have expired and that need to be discarded. It returns true if a
// FORWARD-TSN should be sent.
- bool ShouldSendForwardTsn(TimeMs now);
+ bool ShouldSendForwardTsn(webrtc::Timestamp now);
// Creates a FORWARD-TSN chunk.
ForwardTsnChunk CreateForwardTsn() const {
@@ -185,7 +181,7 @@ class RetransmissionQueue {
// When a SACK chunk is received, this method will be called which _may_ call
// into the `RetransmissionTimeout` to update the RTO.
- void UpdateRTT(TimeMs now, UnwrappedTSN cumulative_tsn_ack);
+ void UpdateRTT(webrtc::Timestamp now, UnwrappedTSN cumulative_tsn_ack);
// If the congestion control is in "fast recovery mode", this may be exited
// now.
@@ -197,7 +193,7 @@ class RetransmissionQueue {
// Update the congestion control algorithm given as the cumulative ack TSN
// value has increased, as reported in an incoming SACK chunk.
- void HandleIncreasedCumulativeTsnAck(size_t outstanding_bytes,
+ void HandleIncreasedCumulativeTsnAck(size_t unacked_bytes,
size_t total_bytes_acked);
// Update the congestion control algorithm, given as packet loss has been
// detected, as reported in an incoming SACK chunk.
@@ -230,7 +226,7 @@ class RetransmissionQueue {
// The size of the data chunk (DATA/I-DATA) header that is used.
const size_t data_chunk_header_size_;
// Called when a new RTT measurement has been done
- const std::function<void(DurationMs rtt)> on_new_rtt_;
+ const std::function<void(webrtc::TimeDelta rtt)> on_new_rtt_;
// Called when a SACK has been seen that cleared the retransmission counter.
const std::function<void()> on_clear_retransmission_counter_;
// The retransmission counter.
diff --git a/third_party/libwebrtc/net/dcsctp/tx/retransmission_queue_test.cc b/third_party/libwebrtc/net/dcsctp/tx/retransmission_queue_test.cc
index d50494f084..eb1e04a5bb 100644
--- a/third_party/libwebrtc/net/dcsctp/tx/retransmission_queue_test.cc
+++ b/third_party/libwebrtc/net/dcsctp/tx/retransmission_queue_test.cc
@@ -52,6 +52,8 @@ using ::testing::Pair;
using ::testing::Return;
using ::testing::SizeIs;
using ::testing::UnorderedElementsAre;
+using ::webrtc::TimeDelta;
+using ::webrtc::Timestamp;
constexpr uint32_t kArwnd = 100000;
constexpr uint32_t kMaxMtu = 1191;
@@ -74,12 +76,12 @@ class RetransmissionQueueTest : public testing::Test {
}),
timer_(timer_manager_.CreateTimer(
"test/t3_rtx",
- []() { return absl::nullopt; },
- TimerOptions(options_.rto_initial))) {}
+ []() { return TimeDelta::Zero(); },
+ TimerOptions(options_.rto_initial.ToTimeDelta()))) {}
- std::function<SendQueue::DataToSend(TimeMs, size_t)> CreateChunk(
+ std::function<SendQueue::DataToSend(Timestamp, size_t)> CreateChunk(
OutgoingMessageId message_id) {
- return [this, message_id](TimeMs now, size_t max_size) {
+ return [this, message_id](Timestamp now, size_t max_size) {
return SendQueue::DataToSend(message_id,
gen_.Ordered({1, 2, 3, 4}, "BE"));
};
@@ -127,10 +129,10 @@ class RetransmissionQueueTest : public testing::Test {
MockDcSctpSocketCallbacks callbacks_;
DcSctpOptions options_;
DataGenerator gen_;
- TimeMs now_ = TimeMs(0);
+ Timestamp now_ = Timestamp::Zero();
FakeTimeoutManager timeout_manager_;
TimerManager timer_manager_;
- NiceMock<MockFunction<void(DurationMs rtt_ms)>> on_rtt_;
+ NiceMock<MockFunction<void(TimeDelta rtt_ms)>> on_rtt_;
NiceMock<MockFunction<void()>> on_clear_retransmission_counter_;
NiceMock<MockSendQueue> producer_;
std::unique_ptr<Timer> timer_;
@@ -146,7 +148,7 @@ TEST_F(RetransmissionQueueTest, SendOneChunk) {
RetransmissionQueue queue = CreateQueue();
EXPECT_CALL(producer_, Produce)
.WillOnce(CreateChunk(OutgoingMessageId(0)))
- .WillRepeatedly([](TimeMs, size_t) { return absl::nullopt; });
+ .WillRepeatedly([](Timestamp, size_t) { return absl::nullopt; });
EXPECT_THAT(GetSentPacketTSNs(queue), testing::ElementsAre(TSN(10)));
@@ -159,7 +161,7 @@ TEST_F(RetransmissionQueueTest, SendOneChunkAndAck) {
RetransmissionQueue queue = CreateQueue();
EXPECT_CALL(producer_, Produce)
.WillOnce(CreateChunk(OutgoingMessageId(0)))
- .WillRepeatedly([](TimeMs, size_t) { return absl::nullopt; });
+ .WillRepeatedly([](Timestamp, size_t) { return absl::nullopt; });
EXPECT_THAT(GetSentPacketTSNs(queue), testing::ElementsAre(TSN(10)));
@@ -175,7 +177,7 @@ TEST_F(RetransmissionQueueTest, SendThreeChunksAndAckTwo) {
.WillOnce(CreateChunk(OutgoingMessageId(0)))
.WillOnce(CreateChunk(OutgoingMessageId(1)))
.WillOnce(CreateChunk(OutgoingMessageId(2)))
- .WillRepeatedly([](TimeMs, size_t) { return absl::nullopt; });
+ .WillRepeatedly([](Timestamp, size_t) { return absl::nullopt; });
EXPECT_THAT(GetSentPacketTSNs(queue),
testing::ElementsAre(TSN(10), TSN(11), TSN(12)));
@@ -198,7 +200,7 @@ TEST_F(RetransmissionQueueTest, AckWithGapBlocksFromRFC4960Section334) {
.WillOnce(CreateChunk(OutgoingMessageId(5)))
.WillOnce(CreateChunk(OutgoingMessageId(6)))
.WillOnce(CreateChunk(OutgoingMessageId(7)))
- .WillRepeatedly([](TimeMs, size_t) { return absl::nullopt; });
+ .WillRepeatedly([](Timestamp, size_t) { return absl::nullopt; });
EXPECT_THAT(GetSentPacketTSNs(queue),
testing::ElementsAre(TSN(10), TSN(11), TSN(12), TSN(13), TSN(14),
@@ -229,7 +231,7 @@ TEST_F(RetransmissionQueueTest, ResendPacketsWhenNackedThreeTimes) {
.WillOnce(CreateChunk(OutgoingMessageId(5)))
.WillOnce(CreateChunk(OutgoingMessageId(6)))
.WillOnce(CreateChunk(OutgoingMessageId(7)))
- .WillRepeatedly([](TimeMs, size_t) { return absl::nullopt; });
+ .WillRepeatedly([](Timestamp, size_t) { return absl::nullopt; });
EXPECT_THAT(GetSentPacketTSNs(queue),
testing::ElementsAre(TSN(10), TSN(11), TSN(12), TSN(13), TSN(14),
@@ -241,7 +243,7 @@ TEST_F(RetransmissionQueueTest, ResendPacketsWhenNackedThreeTimes) {
// Send 18
EXPECT_CALL(producer_, Produce)
.WillOnce(CreateChunk(OutgoingMessageId(8)))
- .WillRepeatedly([](TimeMs, size_t) { return absl::nullopt; });
+ .WillRepeatedly([](Timestamp, size_t) { return absl::nullopt; });
EXPECT_THAT(GetSentPacketTSNs(queue), testing::ElementsAre(TSN(18)));
// Ack 12, 14-15, 17-18
@@ -262,7 +264,7 @@ TEST_F(RetransmissionQueueTest, ResendPacketsWhenNackedThreeTimes) {
// Send 19
EXPECT_CALL(producer_, Produce)
.WillOnce(CreateChunk(OutgoingMessageId(9)))
- .WillRepeatedly([](TimeMs, size_t) { return absl::nullopt; });
+ .WillRepeatedly([](Timestamp, size_t) { return absl::nullopt; });
EXPECT_THAT(GetSentPacketTSNs(queue), testing::ElementsAre(TSN(19)));
// Ack 12, 14-15, 17-19
@@ -274,7 +276,7 @@ TEST_F(RetransmissionQueueTest, ResendPacketsWhenNackedThreeTimes) {
// Send 20
EXPECT_CALL(producer_, Produce)
.WillOnce(CreateChunk(OutgoingMessageId(10)))
- .WillRepeatedly([](TimeMs, size_t) { return absl::nullopt; });
+ .WillRepeatedly([](Timestamp, size_t) { return absl::nullopt; });
EXPECT_THAT(GetSentPacketTSNs(queue), testing::ElementsAre(TSN(20)));
// Ack 12, 14-15, 17-20
@@ -321,16 +323,16 @@ TEST_F(RetransmissionQueueTest, RestartsT3RtxOnRetransmitFirstOutstandingTSN) {
.WillOnce(CreateChunk(OutgoingMessageId(0)))
.WillOnce(CreateChunk(OutgoingMessageId(1)))
.WillOnce(CreateChunk(OutgoingMessageId(2)))
- .WillRepeatedly([](TimeMs, size_t) { return absl::nullopt; });
+ .WillRepeatedly([](Timestamp, size_t) { return absl::nullopt; });
- static constexpr TimeMs kStartTime(100000);
+ static constexpr Timestamp kStartTime = Timestamp::Seconds(100);
now_ = kStartTime;
EXPECT_THAT(GetSentPacketTSNs(queue),
testing::ElementsAre(TSN(10), TSN(11), TSN(12)));
// Ack 10, 12, after 100ms.
- now_ += DurationMs(100);
+ now_ += TimeDelta::Millis(100);
queue.HandleSack(
now_, SackChunk(TSN(10), kArwnd, {SackChunk::GapAckBlock(2, 2)}, {}));
@@ -342,22 +344,22 @@ TEST_F(RetransmissionQueueTest, RestartsT3RtxOnRetransmitFirstOutstandingTSN) {
// Send 13
EXPECT_CALL(producer_, Produce)
.WillOnce(CreateChunk(OutgoingMessageId(3)))
- .WillRepeatedly([](TimeMs, size_t) { return absl::nullopt; });
+ .WillRepeatedly([](Timestamp, size_t) { return absl::nullopt; });
EXPECT_THAT(GetSentPacketTSNs(queue), testing::ElementsAre(TSN(13)));
// Ack 10, 12-13, after 100ms.
- now_ += DurationMs(100);
+ now_ += TimeDelta::Millis(100);
queue.HandleSack(
now_, SackChunk(TSN(10), kArwnd, {SackChunk::GapAckBlock(2, 3)}, {}));
// Send 14
EXPECT_CALL(producer_, Produce)
.WillOnce(CreateChunk(OutgoingMessageId(4)))
- .WillRepeatedly([](TimeMs, size_t) { return absl::nullopt; });
+ .WillRepeatedly([](Timestamp, size_t) { return absl::nullopt; });
EXPECT_THAT(GetSentPacketTSNs(queue), testing::ElementsAre(TSN(14)));
// Ack 10, 12-14, after 100 ms.
- now_ += DurationMs(100);
+ now_ += TimeDelta::Millis(100);
queue.HandleSack(
now_, SackChunk(TSN(10), kArwnd, {SackChunk::GapAckBlock(2, 4)}, {}));
@@ -383,11 +385,11 @@ TEST_F(RetransmissionQueueTest, RestartsT3RtxOnRetransmitFirstOutstandingTSN) {
// Verify that the timer was really restarted when fast-retransmitting. The
// timeout is `options_.rto_initial`, so advance the time just before that.
- now_ += options_.rto_initial - DurationMs(1);
+ now_ += options_.rto_initial.ToTimeDelta() - TimeDelta::Millis(1);
EXPECT_FALSE(timeout_manager_.GetNextExpiredTimeout().has_value());
// And ensure it really is running.
- now_ += DurationMs(1);
+ now_ += TimeDelta::Millis(1);
ASSERT_HAS_VALUE_AND_ASSIGN(TimeoutID timeout,
timeout_manager_.GetNextExpiredTimeout());
// An expired timeout has to be handled (asserts validate this).
@@ -397,15 +399,15 @@ TEST_F(RetransmissionQueueTest, RestartsT3RtxOnRetransmitFirstOutstandingTSN) {
TEST_F(RetransmissionQueueTest, CanOnlyProduceTwoPacketsButWantsToSendThree) {
RetransmissionQueue queue = CreateQueue();
EXPECT_CALL(producer_, Produce)
- .WillOnce([this](TimeMs, size_t) {
+ .WillOnce([this](Timestamp, size_t) {
return SendQueue::DataToSend(OutgoingMessageId(0),
gen_.Ordered({1, 2, 3, 4}, "BE"));
})
- .WillOnce([this](TimeMs, size_t) {
+ .WillOnce([this](Timestamp, size_t) {
return SendQueue::DataToSend(OutgoingMessageId(1),
gen_.Ordered({1, 2, 3, 4}, "BE"));
})
- .WillRepeatedly([](TimeMs, size_t) { return absl::nullopt; });
+ .WillRepeatedly([](Timestamp, size_t) { return absl::nullopt; });
std::vector<std::pair<TSN, Data>> chunks_to_send =
queue.GetChunksToSend(now_, 1000);
@@ -420,11 +422,11 @@ TEST_F(RetransmissionQueueTest, CanOnlyProduceTwoPacketsButWantsToSendThree) {
TEST_F(RetransmissionQueueTest, RetransmitsOnT3Expiry) {
RetransmissionQueue queue = CreateQueue();
EXPECT_CALL(producer_, Produce)
- .WillOnce([this](TimeMs, size_t) {
+ .WillOnce([this](Timestamp, size_t) {
return SendQueue::DataToSend(OutgoingMessageId(0),
gen_.Ordered({1, 2, 3, 4}, "BE"));
})
- .WillRepeatedly([](TimeMs, size_t) { return absl::nullopt; });
+ .WillRepeatedly([](Timestamp, size_t) { return absl::nullopt; });
EXPECT_FALSE(queue.ShouldSendForwardTsn(now_));
std::vector<std::pair<TSN, Data>> chunks_to_send =
@@ -459,12 +461,12 @@ TEST_F(RetransmissionQueueTest, LimitedRetransmissionOnlyWithRfc3758Support) {
RetransmissionQueue queue =
CreateQueue(/*supports_partial_reliability=*/false);
EXPECT_CALL(producer_, Produce)
- .WillOnce([this](TimeMs, size_t) {
+ .WillOnce([this](Timestamp, size_t) {
SendQueue::DataToSend dts(kMessageId, gen_.Ordered({1, 2, 3, 4}, "BE"));
dts.max_retransmissions = MaxRetransmits(0);
return dts;
})
- .WillRepeatedly([](TimeMs, size_t) { return absl::nullopt; });
+ .WillRepeatedly([](Timestamp, size_t) { return absl::nullopt; });
EXPECT_FALSE(queue.ShouldSendForwardTsn(now_));
std::vector<std::pair<TSN, Data>> chunks_to_send =
@@ -488,12 +490,12 @@ TEST_F(RetransmissionQueueTest, LimitedRetransmissionOnlyWithRfc3758Support) {
TEST_F(RetransmissionQueueTest, LimitsRetransmissionsAsUdp) {
RetransmissionQueue queue = CreateQueue();
EXPECT_CALL(producer_, Produce)
- .WillOnce([this](TimeMs, size_t) {
+ .WillOnce([this](Timestamp, size_t) {
SendQueue::DataToSend dts(kMessageId, gen_.Ordered({1, 2, 3, 4}, "BE"));
dts.max_retransmissions = MaxRetransmits(0);
return dts;
})
- .WillRepeatedly([](TimeMs, size_t) { return absl::nullopt; });
+ .WillRepeatedly([](Timestamp, size_t) { return absl::nullopt; });
EXPECT_FALSE(queue.ShouldSendForwardTsn(now_));
std::vector<std::pair<TSN, Data>> chunks_to_send =
@@ -529,12 +531,12 @@ TEST_F(RetransmissionQueueTest, LimitsRetransmissionsAsUdp) {
TEST_F(RetransmissionQueueTest, LimitsRetransmissionsToThreeSends) {
RetransmissionQueue queue = CreateQueue();
EXPECT_CALL(producer_, Produce)
- .WillOnce([this](TimeMs, size_t) {
+ .WillOnce([this](Timestamp, size_t) {
SendQueue::DataToSend dts(kMessageId, gen_.Ordered({1, 2, 3, 4}, "BE"));
dts.max_retransmissions = MaxRetransmits(3);
return dts;
})
- .WillRepeatedly([](TimeMs, size_t) { return absl::nullopt; });
+ .WillRepeatedly([](Timestamp, size_t) { return absl::nullopt; });
EXPECT_FALSE(queue.ShouldSendForwardTsn(now_));
std::vector<std::pair<TSN, Data>> chunks_to_send =
@@ -577,16 +579,16 @@ TEST_F(RetransmissionQueueTest, RetransmitsWhenSendBufferIsFullT3Expiry) {
static constexpr size_t kCwnd = 1200;
queue.set_cwnd(kCwnd);
EXPECT_EQ(queue.cwnd(), kCwnd);
- EXPECT_EQ(queue.outstanding_bytes(), 0u);
- EXPECT_EQ(queue.outstanding_items(), 0u);
+ EXPECT_EQ(queue.unacked_bytes(), 0u);
+ EXPECT_EQ(queue.unacked_items(), 0u);
std::vector<uint8_t> payload(1000);
EXPECT_CALL(producer_, Produce)
- .WillOnce([this, payload](TimeMs, size_t) {
+ .WillOnce([this, payload](Timestamp, size_t) {
return SendQueue::DataToSend(OutgoingMessageId(0),
gen_.Ordered(payload, "BE"));
})
- .WillRepeatedly([](TimeMs, size_t) { return absl::nullopt; });
+ .WillRepeatedly([](Timestamp, size_t) { return absl::nullopt; });
std::vector<std::pair<TSN, Data>> chunks_to_send =
queue.GetChunksToSend(now_, 1500);
@@ -594,8 +596,8 @@ TEST_F(RetransmissionQueueTest, RetransmitsWhenSendBufferIsFullT3Expiry) {
EXPECT_THAT(queue.GetChunkStatesForTesting(),
ElementsAre(Pair(TSN(9), State::kAcked), //
Pair(TSN(10), State::kInFlight)));
- EXPECT_EQ(queue.outstanding_bytes(), payload.size() + DataChunk::kHeaderSize);
- EXPECT_EQ(queue.outstanding_items(), 1u);
+ EXPECT_EQ(queue.unacked_bytes(), payload.size() + DataChunk::kHeaderSize);
+ EXPECT_EQ(queue.unacked_items(), 1u);
// Will force chunks to be retransmitted
queue.HandleT3RtxTimerExpiry();
@@ -603,8 +605,8 @@ TEST_F(RetransmissionQueueTest, RetransmitsWhenSendBufferIsFullT3Expiry) {
EXPECT_THAT(queue.GetChunkStatesForTesting(),
ElementsAre(Pair(TSN(9), State::kAcked), //
Pair(TSN(10), State::kToBeRetransmitted)));
- EXPECT_EQ(queue.outstanding_bytes(), 0u);
- EXPECT_EQ(queue.outstanding_items(), 0u);
+ EXPECT_EQ(queue.unacked_bytes(), 0u);
+ EXPECT_EQ(queue.unacked_items(), 0u);
std::vector<std::pair<TSN, Data>> chunks_to_rtx =
queue.GetChunksToSend(now_, 1500);
@@ -612,30 +614,30 @@ TEST_F(RetransmissionQueueTest, RetransmitsWhenSendBufferIsFullT3Expiry) {
EXPECT_THAT(queue.GetChunkStatesForTesting(),
ElementsAre(Pair(TSN(9), State::kAcked), //
Pair(TSN(10), State::kInFlight)));
- EXPECT_EQ(queue.outstanding_bytes(), payload.size() + DataChunk::kHeaderSize);
- EXPECT_EQ(queue.outstanding_items(), 1u);
+ EXPECT_EQ(queue.unacked_bytes(), payload.size() + DataChunk::kHeaderSize);
+ EXPECT_EQ(queue.unacked_items(), 1u);
}
TEST_F(RetransmissionQueueTest, ProducesValidForwardTsn) {
RetransmissionQueue queue = CreateQueue();
EXPECT_CALL(producer_, Produce)
- .WillOnce([this](TimeMs, size_t) {
+ .WillOnce([this](Timestamp, size_t) {
SendQueue::DataToSend dts(kMessageId, gen_.Ordered({1, 2, 3, 4}, "B"));
dts.max_retransmissions = MaxRetransmits(0);
return dts;
})
- .WillOnce([this](TimeMs, size_t) {
+ .WillOnce([this](Timestamp, size_t) {
SendQueue::DataToSend dts(kMessageId, gen_.Ordered({5, 6, 7, 8}, ""));
dts.max_retransmissions = MaxRetransmits(0);
return dts;
})
- .WillOnce([this](TimeMs, size_t) {
+ .WillOnce([this](Timestamp, size_t) {
SendQueue::DataToSend dts(kMessageId,
gen_.Ordered({9, 10, 11, 12}, ""));
dts.max_retransmissions = MaxRetransmits(0);
return dts;
})
- .WillRepeatedly([](TimeMs, size_t) { return absl::nullopt; });
+ .WillRepeatedly([](Timestamp, size_t) { return absl::nullopt; });
// Send and ack first chunk (TSN 10)
std::vector<std::pair<TSN, Data>> chunks_to_send =
@@ -675,23 +677,23 @@ TEST_F(RetransmissionQueueTest, ProducesValidForwardTsn) {
TEST_F(RetransmissionQueueTest, ProducesValidForwardTsnWhenFullySent) {
RetransmissionQueue queue = CreateQueue();
EXPECT_CALL(producer_, Produce)
- .WillOnce([this](TimeMs, size_t) {
+ .WillOnce([this](Timestamp, size_t) {
SendQueue::DataToSend dts(kMessageId, gen_.Ordered({1, 2, 3, 4}, "B"));
dts.max_retransmissions = MaxRetransmits(0);
return dts;
})
- .WillOnce([this](TimeMs, size_t) {
+ .WillOnce([this](Timestamp, size_t) {
SendQueue::DataToSend dts(kMessageId, gen_.Ordered({5, 6, 7, 8}, ""));
dts.max_retransmissions = MaxRetransmits(0);
return dts;
})
- .WillOnce([this](TimeMs, size_t) {
+ .WillOnce([this](Timestamp, size_t) {
SendQueue::DataToSend dts(kMessageId,
gen_.Ordered({9, 10, 11, 12}, "E"));
dts.max_retransmissions = MaxRetransmits(0);
return dts;
})
- .WillRepeatedly([](TimeMs, size_t) { return absl::nullopt; });
+ .WillRepeatedly([](Timestamp, size_t) { return absl::nullopt; });
// Send and ack first chunk (TSN 10)
std::vector<std::pair<TSN, Data>> chunks_to_send =
@@ -729,7 +731,7 @@ TEST_F(RetransmissionQueueTest, ProducesValidForwardTsnWhenFullySent) {
TEST_F(RetransmissionQueueTest, ProducesValidIForwardTsn) {
RetransmissionQueue queue = CreateQueue(/*use_message_interleaving=*/true);
EXPECT_CALL(producer_, Produce)
- .WillOnce([this](TimeMs, size_t) {
+ .WillOnce([this](Timestamp, size_t) {
DataGeneratorOptions opts;
opts.stream_id = StreamID(1);
SendQueue::DataToSend dts(OutgoingMessageId(42),
@@ -737,7 +739,7 @@ TEST_F(RetransmissionQueueTest, ProducesValidIForwardTsn) {
dts.max_retransmissions = MaxRetransmits(0);
return dts;
})
- .WillOnce([this](TimeMs, size_t) {
+ .WillOnce([this](Timestamp, size_t) {
DataGeneratorOptions opts;
opts.stream_id = StreamID(2);
SendQueue::DataToSend dts(OutgoingMessageId(43),
@@ -745,7 +747,7 @@ TEST_F(RetransmissionQueueTest, ProducesValidIForwardTsn) {
dts.max_retransmissions = MaxRetransmits(0);
return dts;
})
- .WillOnce([this](TimeMs, size_t) {
+ .WillOnce([this](Timestamp, size_t) {
DataGeneratorOptions opts;
opts.stream_id = StreamID(3);
SendQueue::DataToSend dts(OutgoingMessageId(44),
@@ -753,7 +755,7 @@ TEST_F(RetransmissionQueueTest, ProducesValidIForwardTsn) {
dts.max_retransmissions = MaxRetransmits(0);
return dts;
})
- .WillOnce([this](TimeMs, size_t) {
+ .WillOnce([this](Timestamp, size_t) {
DataGeneratorOptions opts;
opts.stream_id = StreamID(4);
SendQueue::DataToSend dts(OutgoingMessageId(45),
@@ -761,7 +763,7 @@ TEST_F(RetransmissionQueueTest, ProducesValidIForwardTsn) {
dts.max_retransmissions = MaxRetransmits(0);
return dts;
})
- .WillRepeatedly([](TimeMs, size_t) { return absl::nullopt; });
+ .WillRepeatedly([](Timestamp, size_t) { return absl::nullopt; });
std::vector<std::pair<TSN, Data>> chunks_to_send =
queue.GetChunksToSend(now_, 1000);
@@ -850,21 +852,21 @@ TEST_F(RetransmissionQueueTest, ProducesValidIForwardTsn) {
TEST_F(RetransmissionQueueTest, MeasureRTT) {
RetransmissionQueue queue = CreateQueue(/*use_message_interleaving=*/true);
EXPECT_CALL(producer_, Produce)
- .WillOnce([this](TimeMs, size_t) {
+ .WillOnce([this](Timestamp, size_t) {
SendQueue::DataToSend dts(OutgoingMessageId(0),
gen_.Ordered({1, 2, 3, 4}, "B"));
dts.max_retransmissions = MaxRetransmits(0);
return dts;
})
- .WillRepeatedly([](TimeMs, size_t) { return absl::nullopt; });
+ .WillRepeatedly([](Timestamp, size_t) { return absl::nullopt; });
std::vector<std::pair<TSN, Data>> chunks_to_send =
queue.GetChunksToSend(now_, 1000);
EXPECT_THAT(chunks_to_send, ElementsAre(Pair(TSN(10), _)));
- now_ = now_ + DurationMs(123);
+ now_ = now_ + TimeDelta::Millis(123);
- EXPECT_CALL(on_rtt_, Call(DurationMs(123))).Times(1);
+ EXPECT_CALL(on_rtt_, Call(TimeDelta::Millis(123))).Times(1);
queue.HandleSack(now_, SackChunk(TSN(10), kArwnd, {}, {}));
}
@@ -888,7 +890,7 @@ TEST_F(RetransmissionQueueTest, ValidateCumTsnAckOnInflightData) {
.WillOnce(CreateChunk(OutgoingMessageId(5)))
.WillOnce(CreateChunk(OutgoingMessageId(6)))
.WillOnce(CreateChunk(OutgoingMessageId(7)))
- .WillRepeatedly([](TimeMs, size_t) { return absl::nullopt; });
+ .WillRepeatedly([](Timestamp, size_t) { return absl::nullopt; });
EXPECT_THAT(GetSentPacketTSNs(queue),
testing::ElementsAre(TSN(10), TSN(11), TSN(12), TSN(13), TSN(14),
@@ -918,7 +920,7 @@ TEST_F(RetransmissionQueueTest, HandleGapAckBlocksMatchingNoInflightData) {
.WillOnce(CreateChunk(OutgoingMessageId(5)))
.WillOnce(CreateChunk(OutgoingMessageId(6)))
.WillOnce(CreateChunk(OutgoingMessageId(7)))
- .WillRepeatedly([](TimeMs, size_t) { return absl::nullopt; });
+ .WillRepeatedly([](Timestamp, size_t) { return absl::nullopt; });
EXPECT_THAT(GetSentPacketTSNs(queue),
testing::ElementsAre(TSN(10), TSN(11), TSN(12), TSN(13), TSN(14),
@@ -965,7 +967,7 @@ TEST_F(RetransmissionQueueTest, GapAckBlocksDoNotMoveCumTsnAck) {
.WillOnce(CreateChunk(OutgoingMessageId(5)))
.WillOnce(CreateChunk(OutgoingMessageId(6)))
.WillOnce(CreateChunk(OutgoingMessageId(7)))
- .WillRepeatedly([](TimeMs, size_t) { return absl::nullopt; });
+ .WillRepeatedly([](Timestamp, size_t) { return absl::nullopt; });
EXPECT_THAT(GetSentPacketTSNs(queue),
testing::ElementsAre(TSN(10), TSN(11), TSN(12), TSN(13), TSN(14),
@@ -995,14 +997,14 @@ TEST_F(RetransmissionQueueTest, StaysWithinAvailableSize) {
// See SctpPacketTest::ReturnsCorrectSpaceAvailableToStayWithinMTU for the
// magic numbers in this test.
EXPECT_CALL(producer_, Produce)
- .WillOnce([this](TimeMs, size_t size) {
+ .WillOnce([this](Timestamp, size_t size) {
EXPECT_EQ(size, 1176 - DataChunk::kHeaderSize);
std::vector<uint8_t> payload(183);
return SendQueue::DataToSend(OutgoingMessageId(0),
gen_.Ordered(payload, "BE"));
})
- .WillOnce([this](TimeMs, size_t size) {
+ .WillOnce([this](Timestamp, size_t size) {
EXPECT_EQ(size, 976 - DataChunk::kHeaderSize);
std::vector<uint8_t> payload(957);
@@ -1018,23 +1020,23 @@ TEST_F(RetransmissionQueueTest, StaysWithinAvailableSize) {
TEST_F(RetransmissionQueueTest, AccountsNackedAbandonedChunksAsNotOutstanding) {
RetransmissionQueue queue = CreateQueue();
EXPECT_CALL(producer_, Produce)
- .WillOnce([this](TimeMs, size_t) {
+ .WillOnce([this](Timestamp, size_t) {
SendQueue::DataToSend dts(kMessageId, gen_.Ordered({1, 2, 3, 4}, "B"));
dts.max_retransmissions = MaxRetransmits(0);
return dts;
})
- .WillOnce([this](TimeMs, size_t) {
+ .WillOnce([this](Timestamp, size_t) {
SendQueue::DataToSend dts(kMessageId, gen_.Ordered({5, 6, 7, 8}, ""));
dts.max_retransmissions = MaxRetransmits(0);
return dts;
})
- .WillOnce([this](TimeMs, size_t) {
+ .WillOnce([this](Timestamp, size_t) {
SendQueue::DataToSend dts(kMessageId,
gen_.Ordered({9, 10, 11, 12}, ""));
dts.max_retransmissions = MaxRetransmits(0);
return dts;
})
- .WillRepeatedly([](TimeMs, size_t) { return absl::nullopt; });
+ .WillRepeatedly([](Timestamp, size_t) { return absl::nullopt; });
// Send and ack first chunk (TSN 10)
std::vector<std::pair<TSN, Data>> chunks_to_send =
@@ -1046,8 +1048,8 @@ TEST_F(RetransmissionQueueTest, AccountsNackedAbandonedChunksAsNotOutstanding) {
Pair(TSN(10), State::kInFlight), //
Pair(TSN(11), State::kInFlight), //
Pair(TSN(12), State::kInFlight)));
- EXPECT_EQ(queue.outstanding_bytes(), (16 + 4) * 3u);
- EXPECT_EQ(queue.outstanding_items(), 3u);
+ EXPECT_EQ(queue.unacked_bytes(), (16 + 4) * 3u);
+ EXPECT_EQ(queue.unacked_items(), 3u);
// Mark the message as lost.
EXPECT_CALL(producer_, Discard(StreamID(1), kMessageId)).Times(1);
@@ -1060,21 +1062,21 @@ TEST_F(RetransmissionQueueTest, AccountsNackedAbandonedChunksAsNotOutstanding) {
Pair(TSN(10), State::kAbandoned), //
Pair(TSN(11), State::kAbandoned), //
Pair(TSN(12), State::kAbandoned)));
- EXPECT_EQ(queue.outstanding_bytes(), 0u);
- EXPECT_EQ(queue.outstanding_items(), 0u);
+ EXPECT_EQ(queue.unacked_bytes(), 0u);
+ EXPECT_EQ(queue.unacked_items(), 0u);
// Now ACK those, one at a time.
queue.HandleSack(now_, SackChunk(TSN(10), kArwnd, {}, {}));
- EXPECT_EQ(queue.outstanding_bytes(), 0u);
- EXPECT_EQ(queue.outstanding_items(), 0u);
+ EXPECT_EQ(queue.unacked_bytes(), 0u);
+ EXPECT_EQ(queue.unacked_items(), 0u);
queue.HandleSack(now_, SackChunk(TSN(11), kArwnd, {}, {}));
- EXPECT_EQ(queue.outstanding_bytes(), 0u);
- EXPECT_EQ(queue.outstanding_items(), 0u);
+ EXPECT_EQ(queue.unacked_bytes(), 0u);
+ EXPECT_EQ(queue.unacked_items(), 0u);
queue.HandleSack(now_, SackChunk(TSN(12), kArwnd, {}, {}));
- EXPECT_EQ(queue.outstanding_bytes(), 0u);
- EXPECT_EQ(queue.outstanding_items(), 0u);
+ EXPECT_EQ(queue.unacked_bytes(), 0u);
+ EXPECT_EQ(queue.unacked_items(), 0u);
}
TEST_F(RetransmissionQueueTest, ExpireFromSendQueueWhenPartiallySent) {
@@ -1082,21 +1084,21 @@ TEST_F(RetransmissionQueueTest, ExpireFromSendQueueWhenPartiallySent) {
DataGeneratorOptions options;
options.stream_id = StreamID(17);
options.mid = MID(42);
- TimeMs test_start = now_;
+ Timestamp test_start = now_;
EXPECT_CALL(producer_, Produce)
- .WillOnce([&](TimeMs, size_t) {
+ .WillOnce([&](Timestamp, size_t) {
SendQueue::DataToSend dts(kMessageId,
gen_.Ordered({1, 2, 3, 4}, "B", options));
- dts.expires_at = TimeMs(test_start + DurationMs(10));
+ dts.expires_at = Timestamp(test_start + TimeDelta::Millis(10));
return dts;
})
- .WillOnce([&](TimeMs, size_t) {
+ .WillOnce([&](Timestamp, size_t) {
SendQueue::DataToSend dts(kMessageId,
gen_.Ordered({5, 6, 7, 8}, "", options));
- dts.expires_at = TimeMs(test_start + DurationMs(10));
+ dts.expires_at = Timestamp(test_start + TimeDelta::Millis(10));
return dts;
})
- .WillRepeatedly([](TimeMs, size_t) { return absl::nullopt; });
+ .WillRepeatedly([](Timestamp, size_t) { return absl::nullopt; });
std::vector<std::pair<TSN, Data>> chunks_to_send =
queue.GetChunksToSend(now_, 24);
@@ -1104,7 +1106,7 @@ TEST_F(RetransmissionQueueTest, ExpireFromSendQueueWhenPartiallySent) {
EXPECT_CALL(producer_, Discard(StreamID(17), kMessageId))
.WillOnce(Return(true));
- now_ += DurationMs(100);
+ now_ += TimeDelta::Millis(100);
EXPECT_THAT(queue.GetChunksToSend(now_, 24), IsEmpty());
@@ -1118,38 +1120,38 @@ TEST_F(RetransmissionQueueTest, ExpireFromSendQueueWhenPartiallySent) {
TEST_F(RetransmissionQueueTest, ExpireCorrectMessageFromSendQueue) {
RetransmissionQueue queue = CreateQueue();
- TimeMs test_start = now_;
+ Timestamp test_start = now_;
EXPECT_CALL(producer_, Produce)
- .WillOnce([&](TimeMs, size_t) {
+ .WillOnce([&](Timestamp, size_t) {
SendQueue::DataToSend dts(
OutgoingMessageId(42),
gen_.Ordered({1, 2, 3, 4}, "BE", {.mid = MID(0)}));
- dts.expires_at = TimeMs(test_start + DurationMs(10));
+ dts.expires_at = Timestamp(test_start + TimeDelta::Millis(10));
return dts;
})
- .WillOnce([&](TimeMs, size_t) {
+ .WillOnce([&](Timestamp, size_t) {
SendQueue::DataToSend dts(
OutgoingMessageId(43),
gen_.Ordered({1, 2, 3, 4}, "BE", {.mid = MID(1)}));
- dts.expires_at = TimeMs(test_start + DurationMs(10));
+ dts.expires_at = Timestamp(test_start + TimeDelta::Millis(10));
return dts;
})
// Stream reset - MID reset to zero again.
- .WillOnce([&](TimeMs, size_t) {
+ .WillOnce([&](Timestamp, size_t) {
SendQueue::DataToSend dts(
OutgoingMessageId(44),
gen_.Ordered({1, 2, 3, 4}, "B", {.mid = MID(0)}));
- dts.expires_at = TimeMs(test_start + DurationMs(10));
+ dts.expires_at = Timestamp(test_start + TimeDelta::Millis(10));
return dts;
})
- .WillOnce([&](TimeMs, size_t) {
+ .WillOnce([&](Timestamp, size_t) {
SendQueue::DataToSend dts(
OutgoingMessageId(44),
gen_.Ordered({5, 6, 7, 8}, "", {.mid = MID(0)}));
- dts.expires_at = TimeMs(test_start + DurationMs(10));
+ dts.expires_at = Timestamp(test_start + TimeDelta::Millis(10));
return dts;
})
- .WillRepeatedly([](TimeMs, size_t) { return absl::nullopt; });
+ .WillRepeatedly([](Timestamp, size_t) { return absl::nullopt; });
EXPECT_CALL(producer_, Discard(StreamID(1), OutgoingMessageId(44)))
.WillOnce(Return(true));
@@ -1160,7 +1162,7 @@ TEST_F(RetransmissionQueueTest, ExpireCorrectMessageFromSendQueue) {
EXPECT_THAT(queue.GetChunksToSend(now_, 24),
ElementsAre(Pair(TSN(12), Field(&Data::mid, MID(0)))));
- now_ += DurationMs(100);
+ now_ += TimeDelta::Millis(100);
EXPECT_THAT(queue.GetChunksToSend(now_, 24), IsEmpty());
EXPECT_THAT(
@@ -1176,7 +1178,7 @@ TEST_F(RetransmissionQueueTest, ExpireCorrectMessageFromSendQueue) {
TEST_F(RetransmissionQueueTest, LimitsRetransmissionsOnlyWhenNackedThreeTimes) {
RetransmissionQueue queue = CreateQueue();
EXPECT_CALL(producer_, Produce)
- .WillOnce([this](TimeMs, size_t) {
+ .WillOnce([this](Timestamp, size_t) {
SendQueue::DataToSend dts(kMessageId, gen_.Ordered({1, 2, 3, 4}, "BE"));
dts.max_retransmissions = MaxRetransmits(0);
return dts;
@@ -1184,7 +1186,7 @@ TEST_F(RetransmissionQueueTest, LimitsRetransmissionsOnlyWhenNackedThreeTimes) {
.WillOnce(CreateChunk(OutgoingMessageId(0)))
.WillOnce(CreateChunk(OutgoingMessageId(1)))
.WillOnce(CreateChunk(OutgoingMessageId(2)))
- .WillRepeatedly([](TimeMs, size_t) { return absl::nullopt; });
+ .WillRepeatedly([](Timestamp, size_t) { return absl::nullopt; });
EXPECT_FALSE(queue.ShouldSendForwardTsn(now_));
@@ -1246,7 +1248,7 @@ TEST_F(RetransmissionQueueTest, AbandonsRtxLimit2WhenNackedNineTimes) {
// This is a fairly long test.
RetransmissionQueue queue = CreateQueue();
EXPECT_CALL(producer_, Produce)
- .WillOnce([this](TimeMs, size_t) {
+ .WillOnce([this](Timestamp, size_t) {
SendQueue::DataToSend dts(kMessageId, gen_.Ordered({1, 2, 3, 4}, "BE"));
dts.max_retransmissions = MaxRetransmits(2);
return dts;
@@ -1260,7 +1262,7 @@ TEST_F(RetransmissionQueueTest, AbandonsRtxLimit2WhenNackedNineTimes) {
.WillOnce(CreateChunk(OutgoingMessageId(6)))
.WillOnce(CreateChunk(OutgoingMessageId(7)))
.WillOnce(CreateChunk(OutgoingMessageId(8)))
- .WillRepeatedly([](TimeMs, size_t) { return absl::nullopt; });
+ .WillRepeatedly([](Timestamp, size_t) { return absl::nullopt; });
EXPECT_FALSE(queue.ShouldSendForwardTsn(now_));
@@ -1386,17 +1388,17 @@ TEST_F(RetransmissionQueueTest, CwndRecoversWhenAcking) {
std::vector<uint8_t> payload(1000);
EXPECT_CALL(producer_, Produce)
- .WillOnce([this, payload](TimeMs, size_t) {
+ .WillOnce([this, payload](Timestamp, size_t) {
return SendQueue::DataToSend(OutgoingMessageId(0),
gen_.Ordered(payload, "BE"));
})
- .WillRepeatedly([](TimeMs, size_t) { return absl::nullopt; });
+ .WillRepeatedly([](Timestamp, size_t) { return absl::nullopt; });
std::vector<std::pair<TSN, Data>> chunks_to_send =
queue.GetChunksToSend(now_, 1500);
EXPECT_THAT(chunks_to_send, ElementsAre(Pair(TSN(10), _)));
size_t serialized_size = payload.size() + DataChunk::kHeaderSize;
- EXPECT_EQ(queue.outstanding_bytes(), serialized_size);
+ EXPECT_EQ(queue.unacked_bytes(), serialized_size);
queue.HandleSack(now_, SackChunk(TSN(10), kArwnd, {}, {}));
@@ -1414,12 +1416,12 @@ TEST_F(RetransmissionQueueTest, OnlySendsLargePacketsOnLargeCongestionWindow) {
// Fill the congestion window almost - leaving 500 bytes.
size_t chunk_size = intial_cwnd - 500;
EXPECT_CALL(producer_, Produce)
- .WillOnce([chunk_size, this](TimeMs, size_t) {
+ .WillOnce([chunk_size, this](Timestamp, size_t) {
return SendQueue::DataToSend(
OutgoingMessageId(0),
gen_.Ordered(std::vector<uint8_t>(chunk_size), "BE"));
})
- .WillRepeatedly([](TimeMs, size_t) { return absl::nullopt; });
+ .WillRepeatedly([](Timestamp, size_t) { return absl::nullopt; });
EXPECT_TRUE(queue.can_send_data());
std::vector<std::pair<TSN, Data>> chunks_to_send =
@@ -1433,7 +1435,7 @@ TEST_F(RetransmissionQueueTest, OnlySendsLargePacketsOnLargeCongestionWindow) {
queue.HandleSack(now_, SackChunk(TSN(10), kArwnd, {}, {}));
EXPECT_TRUE(queue.can_send_data());
- EXPECT_EQ(queue.outstanding_bytes(), 0u);
+ EXPECT_EQ(queue.unacked_bytes(), 0u);
EXPECT_EQ(queue.cwnd(), intial_cwnd + kMaxMtu);
}
@@ -1447,12 +1449,12 @@ TEST_F(RetransmissionQueueTest, AllowsSmallFragmentsOnSmallCongestionWindow) {
// Fill the congestion window almost - leaving 500 bytes.
size_t chunk_size = intial_cwnd - 500;
EXPECT_CALL(producer_, Produce)
- .WillOnce([chunk_size, this](TimeMs, size_t) {
+ .WillOnce([chunk_size, this](Timestamp, size_t) {
return SendQueue::DataToSend(
OutgoingMessageId(0),
gen_.Ordered(std::vector<uint8_t>(chunk_size), "BE"));
})
- .WillRepeatedly([](TimeMs, size_t) { return absl::nullopt; });
+ .WillRepeatedly([](Timestamp, size_t) { return absl::nullopt; });
EXPECT_TRUE(queue.can_send_data());
std::vector<std::pair<TSN, Data>> chunks_to_send =
@@ -1467,7 +1469,7 @@ TEST_F(RetransmissionQueueTest, ReadyForHandoverWhenHasNoOutstandingData) {
RetransmissionQueue queue = CreateQueue();
EXPECT_CALL(producer_, Produce)
.WillOnce(CreateChunk(OutgoingMessageId(0)))
- .WillRepeatedly([](TimeMs, size_t) { return absl::nullopt; });
+ .WillRepeatedly([](Timestamp, size_t) { return absl::nullopt; });
EXPECT_THAT(GetSentPacketTSNs(queue), SizeIs(1));
EXPECT_EQ(
@@ -1490,7 +1492,7 @@ TEST_F(RetransmissionQueueTest, ReadyForHandoverWhenNothingToRetransmit) {
.WillOnce(CreateChunk(OutgoingMessageId(5)))
.WillOnce(CreateChunk(OutgoingMessageId(6)))
.WillOnce(CreateChunk(OutgoingMessageId(7)))
- .WillRepeatedly([](TimeMs, size_t) { return absl::nullopt; });
+ .WillRepeatedly([](Timestamp, size_t) { return absl::nullopt; });
EXPECT_THAT(GetSentPacketTSNs(queue), SizeIs(8));
EXPECT_EQ(
queue.GetHandoverReadiness(),
@@ -1503,7 +1505,7 @@ TEST_F(RetransmissionQueueTest, ReadyForHandoverWhenNothingToRetransmit) {
// Send 18
EXPECT_CALL(producer_, Produce)
.WillOnce(CreateChunk(OutgoingMessageId(8)))
- .WillRepeatedly([](TimeMs, size_t) { return absl::nullopt; });
+ .WillRepeatedly([](Timestamp, size_t) { return absl::nullopt; });
EXPECT_THAT(GetSentPacketTSNs(queue), SizeIs(1));
// Ack 12, 14-15, 17-18
@@ -1515,7 +1517,7 @@ TEST_F(RetransmissionQueueTest, ReadyForHandoverWhenNothingToRetransmit) {
// Send 19
EXPECT_CALL(producer_, Produce)
.WillOnce(CreateChunk(OutgoingMessageId(9)))
- .WillRepeatedly([](TimeMs, size_t) { return absl::nullopt; });
+ .WillRepeatedly([](Timestamp, size_t) { return absl::nullopt; });
EXPECT_THAT(GetSentPacketTSNs(queue), SizeIs(1));
// Ack 12, 14-15, 17-19
@@ -1527,7 +1529,7 @@ TEST_F(RetransmissionQueueTest, ReadyForHandoverWhenNothingToRetransmit) {
// Send 20
EXPECT_CALL(producer_, Produce)
.WillOnce(CreateChunk(OutgoingMessageId(10)))
- .WillRepeatedly([](TimeMs, size_t) { return absl::nullopt; });
+ .WillRepeatedly([](Timestamp, size_t) { return absl::nullopt; });
EXPECT_THAT(GetSentPacketTSNs(queue), SizeIs(1));
// Ack 12, 14-15, 17-20
@@ -1563,7 +1565,7 @@ TEST_F(RetransmissionQueueTest, HandoverTest) {
EXPECT_CALL(producer_, Produce)
.WillOnce(CreateChunk(OutgoingMessageId(0)))
.WillOnce(CreateChunk(OutgoingMessageId(1)))
- .WillRepeatedly([](TimeMs, size_t) { return absl::nullopt; });
+ .WillRepeatedly([](Timestamp, size_t) { return absl::nullopt; });
EXPECT_THAT(GetSentPacketTSNs(queue), SizeIs(2));
queue.HandleSack(now_, SackChunk(TSN(11), kArwnd, {}, {}));
@@ -1574,7 +1576,7 @@ TEST_F(RetransmissionQueueTest, HandoverTest) {
.WillOnce(CreateChunk(OutgoingMessageId(2)))
.WillOnce(CreateChunk(OutgoingMessageId(3)))
.WillOnce(CreateChunk(OutgoingMessageId(4)))
- .WillRepeatedly([](TimeMs, size_t) { return absl::nullopt; });
+ .WillRepeatedly([](Timestamp, size_t) { return absl::nullopt; });
EXPECT_THAT(GetSentPacketTSNs(*handedover_queue),
testing::ElementsAre(TSN(12), TSN(13), TSN(14)));
@@ -1592,27 +1594,27 @@ TEST_F(RetransmissionQueueTest, CanAlwaysSendOnePacket) {
std::vector<uint8_t> payload(mtu - 100);
EXPECT_CALL(producer_, Produce)
- .WillOnce([this, payload](TimeMs, size_t) {
+ .WillOnce([this, payload](Timestamp, size_t) {
return SendQueue::DataToSend(OutgoingMessageId(0),
gen_.Ordered(payload, "B"));
})
- .WillOnce([this, payload](TimeMs, size_t) {
+ .WillOnce([this, payload](Timestamp, size_t) {
return SendQueue::DataToSend(OutgoingMessageId(0),
gen_.Ordered(payload, ""));
})
- .WillOnce([this, payload](TimeMs, size_t) {
+ .WillOnce([this, payload](Timestamp, size_t) {
return SendQueue::DataToSend(OutgoingMessageId(0),
gen_.Ordered(payload, ""));
})
- .WillOnce([this, payload](TimeMs, size_t) {
+ .WillOnce([this, payload](Timestamp, size_t) {
return SendQueue::DataToSend(OutgoingMessageId(0),
gen_.Ordered(payload, ""));
})
- .WillOnce([this, payload](TimeMs, size_t) {
+ .WillOnce([this, payload](Timestamp, size_t) {
return SendQueue::DataToSend(OutgoingMessageId(0),
gen_.Ordered(payload, "E"));
})
- .WillRepeatedly([](TimeMs, size_t) { return absl::nullopt; });
+ .WillRepeatedly([](Timestamp, size_t) { return absl::nullopt; });
// Produce all chunks and put them in the retransmission queue.
std::vector<std::pair<TSN, Data>> chunks_to_send =
diff --git a/third_party/libwebrtc/net/dcsctp/tx/retransmission_timeout.cc b/third_party/libwebrtc/net/dcsctp/tx/retransmission_timeout.cc
index 7d8fb9761c..8af77041a5 100644
--- a/third_party/libwebrtc/net/dcsctp/tx/retransmission_timeout.cc
+++ b/third_party/libwebrtc/net/dcsctp/tx/retransmission_timeout.cc
@@ -12,28 +12,29 @@
#include <algorithm>
#include <cstdint>
+#include "api/units/time_delta.h"
#include "net/dcsctp/public/dcsctp_options.h"
namespace dcsctp {
RetransmissionTimeout::RetransmissionTimeout(const DcSctpOptions& options)
- : min_rto_(*options.rto_min),
- max_rto_(*options.rto_max),
- max_rtt_(*options.rtt_max),
+ : min_rto_(options.rto_min.ToTimeDelta()),
+ max_rto_(options.rto_max.ToTimeDelta()),
+ max_rtt_(options.rtt_max.ToTimeDelta()),
min_rtt_variance_(*options.min_rtt_variance),
scaled_srtt_(*options.rto_initial << kRttShift),
rto_(*options.rto_initial) {}
-void RetransmissionTimeout::ObserveRTT(DurationMs measured_rtt) {
- const int32_t rtt = *measured_rtt;
-
+void RetransmissionTimeout::ObserveRTT(webrtc::TimeDelta measured_rtt) {
// Unrealistic values will be skipped. If a wrongly measured (or otherwise
// corrupt) value was processed, it could change the state in a way that would
// take a very long time to recover.
- if (rtt < 0 || rtt > max_rtt_) {
+ if (measured_rtt < webrtc::TimeDelta::Zero() || measured_rtt > max_rtt_) {
return;
}
+ const int64_t rtt = measured_rtt.ms();
+
// From https://tools.ietf.org/html/rfc4960#section-6.3.1, but avoiding
// floating point math by implementing algorithm from "V. Jacobson: Congestion
// avoidance and control", but adapted for SCTP.
@@ -42,7 +43,7 @@ void RetransmissionTimeout::ObserveRTT(DurationMs measured_rtt) {
scaled_rtt_var_ = (rtt / 2) << kRttVarShift;
first_measurement_ = false;
} else {
- int32_t rtt_diff = rtt - (scaled_srtt_ >> kRttShift);
+ int64_t rtt_diff = rtt - (scaled_srtt_ >> kRttShift);
scaled_srtt_ += rtt_diff;
if (rtt_diff < 0) {
rtt_diff = -rtt_diff;
@@ -58,6 +59,6 @@ void RetransmissionTimeout::ObserveRTT(DurationMs measured_rtt) {
rto_ = (scaled_srtt_ >> kRttShift) + scaled_rtt_var_;
// Clamp RTO between min and max.
- rto_ = std::min(std::max(rto_, min_rto_), max_rto_);
+ rto_ = std::min(std::max(rto_, min_rto_.ms()), max_rto_.ms());
}
} // namespace dcsctp
diff --git a/third_party/libwebrtc/net/dcsctp/tx/retransmission_timeout.h b/third_party/libwebrtc/net/dcsctp/tx/retransmission_timeout.h
index 01530cb3b5..b4b0fd7fef 100644
--- a/third_party/libwebrtc/net/dcsctp/tx/retransmission_timeout.h
+++ b/third_party/libwebrtc/net/dcsctp/tx/retransmission_timeout.h
@@ -32,27 +32,29 @@ class RetransmissionTimeout {
explicit RetransmissionTimeout(const DcSctpOptions& options);
// To be called when a RTT has been measured, to update the RTO value.
- void ObserveRTT(DurationMs measured_rtt);
+ void ObserveRTT(webrtc::TimeDelta measured_rtt);
// Returns the Retransmission Timeout (RTO) value, in milliseconds.
- DurationMs rto() const { return DurationMs(rto_); }
+ webrtc::TimeDelta rto() const { return webrtc::TimeDelta::Millis(rto_); }
// Returns the smoothed RTT value, in milliseconds.
- DurationMs srtt() const { return DurationMs(scaled_srtt_ >> kRttShift); }
+ webrtc::TimeDelta srtt() const {
+ return webrtc::TimeDelta::Millis(scaled_srtt_ >> kRttShift);
+ }
private:
- const int32_t min_rto_;
- const int32_t max_rto_;
- const int32_t max_rtt_;
- const int32_t min_rtt_variance_;
+ const webrtc::TimeDelta min_rto_;
+ const webrtc::TimeDelta max_rto_;
+ const webrtc::TimeDelta max_rtt_;
+ const int64_t min_rtt_variance_;
// If this is the first measurement
bool first_measurement_ = true;
// Smoothed Round-Trip Time, shifted by kRttShift
- int32_t scaled_srtt_;
+ int64_t scaled_srtt_;
// Round-Trip Time Variation, shifted by kRttVarShift
- int32_t scaled_rtt_var_ = 0;
+ int64_t scaled_rtt_var_ = 0;
// Retransmission Timeout
- int32_t rto_;
+ int64_t rto_;
};
} // namespace dcsctp
diff --git a/third_party/libwebrtc/net/dcsctp/tx/retransmission_timeout_test.cc b/third_party/libwebrtc/net/dcsctp/tx/retransmission_timeout_test.cc
index b901995e97..7754578f32 100644
--- a/third_party/libwebrtc/net/dcsctp/tx/retransmission_timeout_test.cc
+++ b/third_party/libwebrtc/net/dcsctp/tx/retransmission_timeout_test.cc
@@ -15,20 +15,21 @@
namespace dcsctp {
namespace {
+using ::webrtc::TimeDelta;
-constexpr DurationMs kMaxRtt = DurationMs(8'000);
-constexpr DurationMs kInitialRto = DurationMs(200);
-constexpr DurationMs kMaxRto = DurationMs(800);
-constexpr DurationMs kMinRto = DurationMs(120);
-constexpr DurationMs kMinRttVariance = DurationMs(220);
+constexpr TimeDelta kMaxRtt = TimeDelta::Millis(8'000);
+constexpr TimeDelta kInitialRto = TimeDelta::Millis(200);
+constexpr TimeDelta kMaxRto = TimeDelta::Millis(800);
+constexpr TimeDelta kMinRto = TimeDelta::Millis(120);
+constexpr TimeDelta kMinRttVariance = TimeDelta::Millis(220);
DcSctpOptions MakeOptions() {
DcSctpOptions options;
- options.rtt_max = kMaxRtt;
- options.rto_initial = kInitialRto;
- options.rto_max = kMaxRto;
- options.rto_min = kMinRto;
- options.min_rtt_variance = kMinRttVariance;
+ options.rtt_max = DurationMs(kMaxRtt);
+ options.rto_initial = DurationMs(kInitialRto);
+ options.rto_max = DurationMs(kMaxRto);
+ options.rto_min = DurationMs(kMinRto);
+ options.min_rtt_variance = DurationMs(kMinRttVariance);
return options;
}
@@ -45,31 +46,31 @@ TEST(RetransmissionTimeoutTest, HasValidInitialSrtt) {
TEST(RetransmissionTimeoutTest, NegativeValuesDoNotAffectRTO) {
RetransmissionTimeout rto_(MakeOptions());
// Initial negative value
- rto_.ObserveRTT(DurationMs(-10));
+ rto_.ObserveRTT(TimeDelta::Millis(-10));
EXPECT_EQ(rto_.rto(), kInitialRto);
- rto_.ObserveRTT(DurationMs(124));
- EXPECT_EQ(*rto_.rto(), 372);
+ rto_.ObserveRTT(TimeDelta::Millis(124));
+ EXPECT_EQ(rto_.rto(), TimeDelta::Millis(372));
// Subsequent negative value
- rto_.ObserveRTT(DurationMs(-10));
- EXPECT_EQ(*rto_.rto(), 372);
+ rto_.ObserveRTT(TimeDelta::Millis(-10));
+ EXPECT_EQ(rto_.rto(), TimeDelta::Millis(372));
}
TEST(RetransmissionTimeoutTest, TooLargeValuesDoNotAffectRTO) {
RetransmissionTimeout rto_(MakeOptions());
// Initial too large value
- rto_.ObserveRTT(kMaxRtt + DurationMs(100));
+ rto_.ObserveRTT(kMaxRtt + TimeDelta::Millis(100));
EXPECT_EQ(rto_.rto(), kInitialRto);
- rto_.ObserveRTT(DurationMs(124));
- EXPECT_EQ(*rto_.rto(), 372);
+ rto_.ObserveRTT(TimeDelta::Millis(124));
+ EXPECT_EQ(rto_.rto(), TimeDelta::Millis(372));
// Subsequent too large value
- rto_.ObserveRTT(kMaxRtt + DurationMs(100));
- EXPECT_EQ(*rto_.rto(), 372);
+ rto_.ObserveRTT(kMaxRtt + TimeDelta::Millis(100));
+ EXPECT_EQ(rto_.rto(), TimeDelta::Millis(372));
}
TEST(RetransmissionTimeoutTest, WillNeverGoBelowMinimumRto) {
RetransmissionTimeout rto_(MakeOptions());
for (int i = 0; i < 1000; ++i) {
- rto_.ObserveRTT(DurationMs(1));
+ rto_.ObserveRTT(TimeDelta::Millis(1));
}
EXPECT_GE(rto_.rto(), kMinRto);
}
@@ -77,67 +78,67 @@ TEST(RetransmissionTimeoutTest, WillNeverGoBelowMinimumRto) {
TEST(RetransmissionTimeoutTest, WillNeverGoAboveMaximumRto) {
RetransmissionTimeout rto_(MakeOptions());
for (int i = 0; i < 1000; ++i) {
- rto_.ObserveRTT(kMaxRtt - DurationMs(1));
+ rto_.ObserveRTT(kMaxRtt - TimeDelta::Millis(1));
// Adding jitter, which would make it RTO be well above RTT.
- rto_.ObserveRTT(kMaxRtt - DurationMs(100));
+ rto_.ObserveRTT(kMaxRtt - TimeDelta::Millis(100));
}
EXPECT_LE(rto_.rto(), kMaxRto);
}
TEST(RetransmissionTimeoutTest, CalculatesRtoForStableRtt) {
RetransmissionTimeout rto_(MakeOptions());
- rto_.ObserveRTT(DurationMs(124));
- EXPECT_EQ(*rto_.rto(), 372);
- rto_.ObserveRTT(DurationMs(128));
- EXPECT_EQ(*rto_.rto(), 344);
- rto_.ObserveRTT(DurationMs(123));
- EXPECT_EQ(*rto_.rto(), 344);
- rto_.ObserveRTT(DurationMs(125));
- EXPECT_EQ(*rto_.rto(), 344);
- rto_.ObserveRTT(DurationMs(127));
- EXPECT_EQ(*rto_.rto(), 344);
+ rto_.ObserveRTT(TimeDelta::Millis(124));
+ EXPECT_EQ(rto_.rto(), TimeDelta::Millis(372));
+ rto_.ObserveRTT(TimeDelta::Millis(128));
+ EXPECT_EQ(rto_.rto(), TimeDelta::Millis(344));
+ rto_.ObserveRTT(TimeDelta::Millis(123));
+ EXPECT_EQ(rto_.rto(), TimeDelta::Millis(344));
+ rto_.ObserveRTT(TimeDelta::Millis(125));
+ EXPECT_EQ(rto_.rto(), TimeDelta::Millis(344));
+ rto_.ObserveRTT(TimeDelta::Millis(127));
+ EXPECT_EQ(rto_.rto(), TimeDelta::Millis(344));
}
TEST(RetransmissionTimeoutTest, CalculatesRtoForUnstableRtt) {
RetransmissionTimeout rto_(MakeOptions());
- rto_.ObserveRTT(DurationMs(124));
- EXPECT_EQ(*rto_.rto(), 372);
- rto_.ObserveRTT(DurationMs(402));
- EXPECT_EQ(*rto_.rto(), 622);
- rto_.ObserveRTT(DurationMs(728));
- EXPECT_EQ(*rto_.rto(), 800);
- rto_.ObserveRTT(DurationMs(89));
- EXPECT_EQ(*rto_.rto(), 800);
- rto_.ObserveRTT(DurationMs(126));
- EXPECT_EQ(*rto_.rto(), 800);
+ rto_.ObserveRTT(TimeDelta::Millis(124));
+ EXPECT_EQ(rto_.rto(), TimeDelta::Millis(372));
+ rto_.ObserveRTT(TimeDelta::Millis(402));
+ EXPECT_EQ(rto_.rto(), TimeDelta::Millis(622));
+ rto_.ObserveRTT(TimeDelta::Millis(728));
+ EXPECT_EQ(rto_.rto(), TimeDelta::Millis(800));
+ rto_.ObserveRTT(TimeDelta::Millis(89));
+ EXPECT_EQ(rto_.rto(), TimeDelta::Millis(800));
+ rto_.ObserveRTT(TimeDelta::Millis(126));
+ EXPECT_EQ(rto_.rto(), TimeDelta::Millis(800));
}
TEST(RetransmissionTimeoutTest, WillStabilizeAfterAWhile) {
RetransmissionTimeout rto_(MakeOptions());
- rto_.ObserveRTT(DurationMs(124));
- rto_.ObserveRTT(DurationMs(402));
- rto_.ObserveRTT(DurationMs(728));
- rto_.ObserveRTT(DurationMs(89));
- rto_.ObserveRTT(DurationMs(126));
- EXPECT_EQ(*rto_.rto(), 800);
- rto_.ObserveRTT(DurationMs(124));
- EXPECT_EQ(*rto_.rto(), 800);
- rto_.ObserveRTT(DurationMs(122));
- EXPECT_EQ(*rto_.rto(), 710);
- rto_.ObserveRTT(DurationMs(123));
- EXPECT_EQ(*rto_.rto(), 631);
- rto_.ObserveRTT(DurationMs(124));
- EXPECT_EQ(*rto_.rto(), 562);
- rto_.ObserveRTT(DurationMs(122));
- EXPECT_EQ(*rto_.rto(), 505);
- rto_.ObserveRTT(DurationMs(124));
- EXPECT_EQ(*rto_.rto(), 454);
- rto_.ObserveRTT(DurationMs(124));
- EXPECT_EQ(*rto_.rto(), 410);
- rto_.ObserveRTT(DurationMs(124));
- EXPECT_EQ(*rto_.rto(), 372);
- rto_.ObserveRTT(DurationMs(124));
- EXPECT_EQ(*rto_.rto(), 367);
+ rto_.ObserveRTT(TimeDelta::Millis(124));
+ rto_.ObserveRTT(TimeDelta::Millis(402));
+ rto_.ObserveRTT(TimeDelta::Millis(728));
+ rto_.ObserveRTT(TimeDelta::Millis(89));
+ rto_.ObserveRTT(TimeDelta::Millis(126));
+ EXPECT_EQ(rto_.rto(), TimeDelta::Millis(800));
+ rto_.ObserveRTT(TimeDelta::Millis(124));
+ EXPECT_EQ(rto_.rto(), TimeDelta::Millis(800));
+ rto_.ObserveRTT(TimeDelta::Millis(122));
+ EXPECT_EQ(rto_.rto(), TimeDelta::Millis(710));
+ rto_.ObserveRTT(TimeDelta::Millis(123));
+ EXPECT_EQ(rto_.rto(), TimeDelta::Millis(631));
+ rto_.ObserveRTT(TimeDelta::Millis(124));
+ EXPECT_EQ(rto_.rto(), TimeDelta::Millis(562));
+ rto_.ObserveRTT(TimeDelta::Millis(122));
+ EXPECT_EQ(rto_.rto(), TimeDelta::Millis(505));
+ rto_.ObserveRTT(TimeDelta::Millis(124));
+ EXPECT_EQ(rto_.rto(), TimeDelta::Millis(454));
+ rto_.ObserveRTT(TimeDelta::Millis(124));
+ EXPECT_EQ(rto_.rto(), TimeDelta::Millis(410));
+ rto_.ObserveRTT(TimeDelta::Millis(124));
+ EXPECT_EQ(rto_.rto(), TimeDelta::Millis(372));
+ rto_.ObserveRTT(TimeDelta::Millis(124));
+ EXPECT_EQ(rto_.rto(), TimeDelta::Millis(367));
}
TEST(RetransmissionTimeoutTest, WillAlwaysStayAboveRTT) {
@@ -149,31 +150,33 @@ TEST(RetransmissionTimeoutTest, WillAlwaysStayAboveRTT) {
RetransmissionTimeout rto_(MakeOptions());
for (int i = 0; i < 1000; ++i) {
- rto_.ObserveRTT(DurationMs(124));
+ rto_.ObserveRTT(TimeDelta::Millis(124));
}
- EXPECT_EQ(*rto_.rto(), 344);
+ EXPECT_EQ(rto_.rto(), TimeDelta::Millis(344));
}
TEST(RetransmissionTimeoutTest, CanSpecifySmallerMinimumRttVariance) {
DcSctpOptions options = MakeOptions();
- options.min_rtt_variance = kMinRttVariance - DurationMs(100);
+ options.min_rtt_variance =
+ DurationMs(kMinRttVariance - TimeDelta::Millis(100));
RetransmissionTimeout rto_(options);
for (int i = 0; i < 1000; ++i) {
- rto_.ObserveRTT(DurationMs(124));
+ rto_.ObserveRTT(TimeDelta::Millis(124));
}
- EXPECT_EQ(*rto_.rto(), 244);
+ EXPECT_EQ(rto_.rto(), TimeDelta::Millis(244));
}
TEST(RetransmissionTimeoutTest, CanSpecifyLargerMinimumRttVariance) {
DcSctpOptions options = MakeOptions();
- options.min_rtt_variance = kMinRttVariance + DurationMs(100);
+ options.min_rtt_variance =
+ DurationMs(kMinRttVariance + TimeDelta::Millis(100));
RetransmissionTimeout rto_(options);
for (int i = 0; i < 1000; ++i) {
- rto_.ObserveRTT(DurationMs(124));
+ rto_.ObserveRTT(TimeDelta::Millis(124));
}
- EXPECT_EQ(*rto_.rto(), 444);
+ EXPECT_EQ(rto_.rto(), TimeDelta::Millis(444));
}
} // namespace
diff --git a/third_party/libwebrtc/net/dcsctp/tx/rr_send_queue.cc b/third_party/libwebrtc/net/dcsctp/tx/rr_send_queue.cc
index facb432c59..7cbead296c 100644
--- a/third_party/libwebrtc/net/dcsctp/tx/rr_send_queue.cc
+++ b/third_party/libwebrtc/net/dcsctp/tx/rr_send_queue.cc
@@ -21,15 +21,17 @@
#include "absl/types/optional.h"
#include "api/array_view.h"
#include "net/dcsctp/common/internal_types.h"
-#include "net/dcsctp/common/str_join.h"
#include "net/dcsctp/packet/data.h"
#include "net/dcsctp/public/dcsctp_message.h"
#include "net/dcsctp/public/dcsctp_socket.h"
#include "net/dcsctp/public/types.h"
#include "net/dcsctp/tx/send_queue.h"
#include "rtc_base/logging.h"
+#include "rtc_base/strings/str_join.h"
namespace dcsctp {
+using ::webrtc::TimeDelta;
+using ::webrtc::Timestamp;
RRSendQueue::RRSendQueue(absl::string_view log_prefix,
DcSctpSocketCallbacks* callbacks,
@@ -137,7 +139,7 @@ void RRSendQueue::OutgoingStream::Add(DcSctpMessage message,
}
absl::optional<SendQueue::DataToSend> RRSendQueue::OutgoingStream::Produce(
- TimeMs now,
+ Timestamp now,
size_t max_size) {
RTC_DCHECK(pause_state_ != PauseState::kPaused &&
pause_state_ != PauseState::kResetting);
@@ -349,7 +351,7 @@ bool RRSendQueue::OutgoingStream::has_partially_sent_message() const {
return items_.front().mid.has_value();
}
-void RRSendQueue::Add(TimeMs now,
+void RRSendQueue::Add(Timestamp now,
DcSctpMessage message,
const SendOptions& send_options) {
RTC_DCHECK(!message.payload().empty());
@@ -366,8 +368,9 @@ void RRSendQueue::Add(TimeMs now,
? MaxRetransmits(send_options.max_retransmissions.value())
: MaxRetransmits::NoLimit(),
.expires_at = send_options.lifetime.has_value()
- ? now + *send_options.lifetime + DurationMs(1)
- : TimeMs::InfiniteFuture(),
+ ? now + send_options.lifetime->ToTimeDelta() +
+ TimeDelta::Millis(1)
+ : Timestamp::PlusInfinity(),
.lifecycle_id = send_options.lifecycle_id,
};
GetOrCreateStreamInfo(message.stream_id())
@@ -383,7 +386,7 @@ bool RRSendQueue::IsEmpty() const {
return total_buffered_amount() == 0;
}
-absl::optional<SendQueue::DataToSend> RRSendQueue::Produce(TimeMs now,
+absl::optional<SendQueue::DataToSend> RRSendQueue::Produce(Timestamp now,
size_t max_size) {
return scheduler_.Produce(now, max_size);
}
diff --git a/third_party/libwebrtc/net/dcsctp/tx/rr_send_queue.h b/third_party/libwebrtc/net/dcsctp/tx/rr_send_queue.h
index bef5fe437d..b6c359dc1e 100644
--- a/third_party/libwebrtc/net/dcsctp/tx/rr_send_queue.h
+++ b/third_party/libwebrtc/net/dcsctp/tx/rr_send_queue.h
@@ -71,12 +71,13 @@ class RRSendQueue : public SendQueue {
// time should be in `now`. Note that it's the responsibility of the caller to
// ensure that the buffer is not full (by calling `IsFull`) before adding
// messages to it.
- void Add(TimeMs now,
+ void Add(webrtc::Timestamp now,
DcSctpMessage message,
const SendOptions& send_options = {});
// Implementation of `SendQueue`.
- absl::optional<DataToSend> Produce(TimeMs now, size_t max_size) override;
+ absl::optional<DataToSend> Produce(webrtc::Timestamp now,
+ size_t max_size) override;
bool Discard(StreamID stream_id, OutgoingMessageId message_id) override;
void PrepareResetStream(StreamID streams) override;
bool HasStreamsReadyToBeReset() const override;
@@ -104,7 +105,7 @@ class RRSendQueue : public SendQueue {
struct MessageAttributes {
IsUnordered unordered;
MaxRetransmits max_retransmissions;
- TimeMs expires_at;
+ webrtc::Timestamp expires_at;
LifecycleId lifecycle_id;
};
@@ -154,7 +155,7 @@ class RRSendQueue : public SendQueue {
void Add(DcSctpMessage message, MessageAttributes attributes);
// Implementing `StreamScheduler::StreamProducer`.
- absl::optional<SendQueue::DataToSend> Produce(TimeMs now,
+ absl::optional<SendQueue::DataToSend> Produce(webrtc::Timestamp now,
size_t max_size) override;
size_t bytes_to_send_in_next_message() const override;
@@ -265,7 +266,7 @@ class RRSendQueue : public SendQueue {
OutgoingStream& GetOrCreateStreamInfo(StreamID stream_id);
absl::optional<DataToSend> Produce(
std::map<StreamID, OutgoingStream>::iterator it,
- TimeMs now,
+ webrtc::Timestamp now,
size_t max_size);
const absl::string_view log_prefix_;
diff --git a/third_party/libwebrtc/net/dcsctp/tx/rr_send_queue_test.cc b/third_party/libwebrtc/net/dcsctp/tx/rr_send_queue_test.cc
index 9d6da7bdff..632cd8fc19 100644
--- a/third_party/libwebrtc/net/dcsctp/tx/rr_send_queue_test.cc
+++ b/third_party/libwebrtc/net/dcsctp/tx/rr_send_queue_test.cc
@@ -29,8 +29,10 @@ namespace dcsctp {
namespace {
using ::testing::SizeIs;
using ::testing::UnorderedElementsAre;
+using ::webrtc::TimeDelta;
+using ::webrtc::Timestamp;
-constexpr TimeMs kNow = TimeMs(0);
+constexpr Timestamp kNow = Timestamp::Zero();
constexpr StreamID kStreamID(1);
constexpr PPID kPPID(53);
constexpr size_t kMaxQueueSize = 1000;
@@ -181,9 +183,9 @@ TEST_F(RRSendQueueTest, ProduceWithLifetimeExpiry) {
std::vector<uint8_t> payload(20);
// Default is no expiry
- TimeMs now = kNow;
+ Timestamp now = kNow;
buf_.Add(now, DcSctpMessage(kStreamID, kPPID, payload));
- now += DurationMs(1000000);
+ now += TimeDelta::Seconds(1000);
ASSERT_TRUE(buf_.Produce(now, kOneFragmentPacketSize));
SendOptions expires_2_seconds;
@@ -191,17 +193,17 @@ TEST_F(RRSendQueueTest, ProduceWithLifetimeExpiry) {
// Add and consume within lifetime
buf_.Add(now, DcSctpMessage(kStreamID, kPPID, payload), expires_2_seconds);
- now += DurationMs(2000);
+ now += TimeDelta::Millis(2000);
ASSERT_TRUE(buf_.Produce(now, kOneFragmentPacketSize));
// Add and consume just outside lifetime
buf_.Add(now, DcSctpMessage(kStreamID, kPPID, payload), expires_2_seconds);
- now += DurationMs(2001);
+ now += TimeDelta::Millis(2001);
ASSERT_FALSE(buf_.Produce(now, kOneFragmentPacketSize));
// A long time after expiry
buf_.Add(now, DcSctpMessage(kStreamID, kPPID, payload), expires_2_seconds);
- now += DurationMs(1000000);
+ now += TimeDelta::Seconds(1000);
ASSERT_FALSE(buf_.Produce(now, kOneFragmentPacketSize));
// Expire one message, but produce the second that is not expired.
@@ -211,7 +213,7 @@ TEST_F(RRSendQueueTest, ProduceWithLifetimeExpiry) {
expires_4_seconds.lifetime = DurationMs(4000);
buf_.Add(now, DcSctpMessage(kStreamID, kPPID, payload), expires_4_seconds);
- now += DurationMs(2001);
+ now += TimeDelta::Millis(2001);
ASSERT_TRUE(buf_.Produce(now, kOneFragmentPacketSize));
ASSERT_FALSE(buf_.Produce(now, kOneFragmentPacketSize));
@@ -846,8 +848,9 @@ TEST_F(RRSendQueueTest, WillSendLifecycleExpireWhenExpiredInSendQueue) {
EXPECT_CALL(callbacks_, OnLifecycleMessageExpired(LifecycleId(1),
/*maybe_delivered=*/false));
EXPECT_CALL(callbacks_, OnLifecycleEnd(LifecycleId(1)));
- EXPECT_FALSE(buf_.Produce(kNow + DurationMs(1001), kOneFragmentPacketSize)
- .has_value());
+ EXPECT_FALSE(
+ buf_.Produce(kNow + TimeDelta::Millis(1001), kOneFragmentPacketSize)
+ .has_value());
}
TEST_F(RRSendQueueTest, WillSendLifecycleExpireWhenDiscardingDuringPause) {
diff --git a/third_party/libwebrtc/net/dcsctp/tx/send_queue.h b/third_party/libwebrtc/net/dcsctp/tx/send_queue.h
index 48eaefaf6a..d0d834c901 100644
--- a/third_party/libwebrtc/net/dcsctp/tx/send_queue.h
+++ b/third_party/libwebrtc/net/dcsctp/tx/send_queue.h
@@ -17,6 +17,7 @@
#include "absl/types/optional.h"
#include "api/array_view.h"
+#include "api/units/timestamp.h"
#include "net/dcsctp/common/internal_types.h"
#include "net/dcsctp/packet/data.h"
#include "net/dcsctp/public/types.h"
@@ -37,7 +38,7 @@ class SendQueue {
// Partial reliability - RFC3758
MaxRetransmits max_retransmissions = MaxRetransmits::NoLimit();
- TimeMs expires_at = TimeMs::InfiniteFuture();
+ webrtc::Timestamp expires_at = webrtc::Timestamp::PlusInfinity();
// Lifecycle - set for the last fragment, and `LifecycleId::NotSet()` for
// all other fragments.
@@ -55,7 +56,8 @@ class SendQueue {
//
// `max_size` refers to how many payload bytes that may be produced, not
// including any headers.
- virtual absl::optional<DataToSend> Produce(TimeMs now, size_t max_size) = 0;
+ virtual absl::optional<DataToSend> Produce(webrtc::Timestamp now,
+ size_t max_size) = 0;
// Discards a partially sent message identified by the parameters
// `stream_id` and `message_id`. The `message_id` comes from the returned
diff --git a/third_party/libwebrtc/net/dcsctp/tx/stream_scheduler.cc b/third_party/libwebrtc/net/dcsctp/tx/stream_scheduler.cc
index c1d220aaa2..66c4457481 100644
--- a/third_party/libwebrtc/net/dcsctp/tx/stream_scheduler.cc
+++ b/third_party/libwebrtc/net/dcsctp/tx/stream_scheduler.cc
@@ -14,7 +14,6 @@
#include "absl/algorithm/container.h"
#include "absl/types/optional.h"
#include "api/array_view.h"
-#include "net/dcsctp/common/str_join.h"
#include "net/dcsctp/packet/data.h"
#include "net/dcsctp/public/dcsctp_message.h"
#include "net/dcsctp/public/dcsctp_socket.h"
@@ -22,6 +21,7 @@
#include "net/dcsctp/tx/send_queue.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
+#include "rtc_base/strings/str_join.h"
namespace dcsctp {
@@ -31,7 +31,7 @@ void StreamScheduler::Stream::SetPriority(StreamPriority priority) {
}
absl::optional<SendQueue::DataToSend> StreamScheduler::Produce(
- TimeMs now,
+ webrtc::Timestamp now,
size_t max_size) {
// For non-interleaved streams, avoid rescheduling while still sending a
// message as it needs to be sent in full. For interleaved messaging,
@@ -127,7 +127,7 @@ StreamScheduler::VirtualTime StreamScheduler::Stream::CalculateFinishTime(
}
absl::optional<SendQueue::DataToSend> StreamScheduler::Stream::Produce(
- TimeMs now,
+ webrtc::Timestamp now,
size_t max_size) {
absl::optional<SendQueue::DataToSend> data = producer_.Produce(now, max_size);
diff --git a/third_party/libwebrtc/net/dcsctp/tx/stream_scheduler.h b/third_party/libwebrtc/net/dcsctp/tx/stream_scheduler.h
index ce836a5826..9d76fc6f56 100644
--- a/third_party/libwebrtc/net/dcsctp/tx/stream_scheduler.h
+++ b/third_party/libwebrtc/net/dcsctp/tx/stream_scheduler.h
@@ -87,7 +87,7 @@ class StreamScheduler {
// The parameter `max_size` specifies the maximum amount of actual payload
// that may be returned. If these constraints prevents the stream from
// sending some data, `absl::nullopt` should be returned.
- virtual absl::optional<SendQueue::DataToSend> Produce(TimeMs now,
+ virtual absl::optional<SendQueue::DataToSend> Produce(webrtc::Timestamp now,
size_t max_size) = 0;
// Returns the number of payload bytes that is scheduled to be sent in the
@@ -132,7 +132,8 @@ class StreamScheduler {
// Produces a message from this stream. This will only be called on streams
// that have data.
- absl::optional<SendQueue::DataToSend> Produce(TimeMs now, size_t max_size);
+ absl::optional<SendQueue::DataToSend> Produce(webrtc::Timestamp now,
+ size_t max_size);
void MakeActive(size_t bytes_to_send_next);
void ForceMarkInactive();
@@ -180,7 +181,8 @@ class StreamScheduler {
// `now` and will be used to skip chunks with expired limited lifetime. The
// parameter `max_size` specifies the maximum amount of actual payload that
// may be returned. If no data can be produced, `absl::nullopt` is returned.
- absl::optional<SendQueue::DataToSend> Produce(TimeMs now, size_t max_size);
+ absl::optional<SendQueue::DataToSend> Produce(webrtc::Timestamp now,
+ size_t max_size);
std::set<StreamID> ActiveStreamsForTesting() const;
diff --git a/third_party/libwebrtc/net/dcsctp/tx/stream_scheduler_test.cc b/third_party/libwebrtc/net/dcsctp/tx/stream_scheduler_test.cc
index 4f5fb0fb84..42d0b3cd35 100644
--- a/third_party/libwebrtc/net/dcsctp/tx/stream_scheduler_test.cc
+++ b/third_party/libwebrtc/net/dcsctp/tx/stream_scheduler_test.cc
@@ -19,9 +19,11 @@ namespace dcsctp {
namespace {
using ::testing::Return;
using ::testing::StrictMock;
+using ::webrtc::Timestamp;
constexpr size_t kMtu = 1000;
constexpr size_t kPayloadSize = 4;
+constexpr Timestamp kNow = Timestamp::Zero();
MATCHER_P(HasDataWithMid, mid, "") {
if (!arg.has_value()) {
@@ -38,12 +40,12 @@ MATCHER_P(HasDataWithMid, mid, "") {
return true;
}
-std::function<absl::optional<SendQueue::DataToSend>(TimeMs, size_t)>
+std::function<absl::optional<SendQueue::DataToSend>(Timestamp, size_t)>
CreateChunk(OutgoingMessageId message_id,
StreamID sid,
MID mid,
size_t payload_size = kPayloadSize) {
- return [sid, mid, payload_size, message_id](TimeMs now, size_t max_size) {
+ return [sid, mid, payload_size, message_id](Timestamp now, size_t max_size) {
return SendQueue::DataToSend(
message_id,
Data(sid, SSN(0), mid, FSN(0), PPID(42),
@@ -56,8 +58,7 @@ std::map<StreamID, size_t> GetPacketCounts(StreamScheduler& scheduler,
size_t packets_to_generate) {
std::map<StreamID, size_t> packet_counts;
for (size_t i = 0; i < packets_to_generate; ++i) {
- absl::optional<SendQueue::DataToSend> data =
- scheduler.Produce(TimeMs(0), kMtu);
+ absl::optional<SendQueue::DataToSend> data = scheduler.Produce(kNow, kMtu);
if (data.has_value()) {
++packet_counts[data->data.stream_id];
}
@@ -69,7 +70,7 @@ class MockStreamProducer : public StreamScheduler::StreamProducer {
public:
MOCK_METHOD(absl::optional<SendQueue::DataToSend>,
Produce,
- (TimeMs, size_t),
+ (Timestamp, size_t),
(override));
MOCK_METHOD(size_t, bytes_to_send_in_next_message, (), (const, override));
};
@@ -100,7 +101,7 @@ class TestStream {
TEST(StreamSchedulerTest, HasNoActiveStreams) {
StreamScheduler scheduler("", kMtu);
- EXPECT_EQ(scheduler.Produce(TimeMs(0), kMtu), absl::nullopt);
+ EXPECT_EQ(scheduler.Produce(kNow, kMtu), absl::nullopt);
}
// Stream properties can be set and retrieved
@@ -132,8 +133,8 @@ TEST(StreamSchedulerTest, CanProduceFromSingleStream) {
scheduler.CreateStream(&producer, StreamID(1), StreamPriority(2));
stream->MaybeMakeActive();
- EXPECT_THAT(scheduler.Produce(TimeMs(0), kMtu), HasDataWithMid(MID(0)));
- EXPECT_EQ(scheduler.Produce(TimeMs(0), kMtu), absl::nullopt);
+ EXPECT_THAT(scheduler.Produce(kNow, kMtu), HasDataWithMid(MID(0)));
+ EXPECT_EQ(scheduler.Produce(kNow, kMtu), absl::nullopt);
}
// Switches between two streams after every packet.
@@ -168,13 +169,13 @@ TEST(StreamSchedulerTest, WillRoundRobinBetweenStreams) {
scheduler.CreateStream(&producer2, StreamID(2), StreamPriority(2));
stream2->MaybeMakeActive();
- EXPECT_THAT(scheduler.Produce(TimeMs(0), kMtu), HasDataWithMid(MID(100)));
- EXPECT_THAT(scheduler.Produce(TimeMs(0), kMtu), HasDataWithMid(MID(200)));
- EXPECT_THAT(scheduler.Produce(TimeMs(0), kMtu), HasDataWithMid(MID(101)));
- EXPECT_THAT(scheduler.Produce(TimeMs(0), kMtu), HasDataWithMid(MID(201)));
- EXPECT_THAT(scheduler.Produce(TimeMs(0), kMtu), HasDataWithMid(MID(102)));
- EXPECT_THAT(scheduler.Produce(TimeMs(0), kMtu), HasDataWithMid(MID(202)));
- EXPECT_EQ(scheduler.Produce(TimeMs(0), kMtu), absl::nullopt);
+ EXPECT_THAT(scheduler.Produce(kNow, kMtu), HasDataWithMid(MID(100)));
+ EXPECT_THAT(scheduler.Produce(kNow, kMtu), HasDataWithMid(MID(200)));
+ EXPECT_THAT(scheduler.Produce(kNow, kMtu), HasDataWithMid(MID(101)));
+ EXPECT_THAT(scheduler.Produce(kNow, kMtu), HasDataWithMid(MID(201)));
+ EXPECT_THAT(scheduler.Produce(kNow, kMtu), HasDataWithMid(MID(102)));
+ EXPECT_THAT(scheduler.Produce(kNow, kMtu), HasDataWithMid(MID(202)));
+ EXPECT_EQ(scheduler.Produce(kNow, kMtu), absl::nullopt);
}
// Switches between two streams after every packet, but keeps producing from the
@@ -232,15 +233,15 @@ TEST(StreamSchedulerTest, WillRoundRobinOnlyWhenFinishedProducingChunk) {
scheduler.CreateStream(&producer2, StreamID(2), StreamPriority(2));
stream2->MaybeMakeActive();
- EXPECT_THAT(scheduler.Produce(TimeMs(0), kMtu), HasDataWithMid(MID(100)));
- EXPECT_THAT(scheduler.Produce(TimeMs(0), kMtu), HasDataWithMid(MID(200)));
- EXPECT_THAT(scheduler.Produce(TimeMs(0), kMtu), HasDataWithMid(MID(101)));
- EXPECT_THAT(scheduler.Produce(TimeMs(0), kMtu), HasDataWithMid(MID(101)));
- EXPECT_THAT(scheduler.Produce(TimeMs(0), kMtu), HasDataWithMid(MID(101)));
- EXPECT_THAT(scheduler.Produce(TimeMs(0), kMtu), HasDataWithMid(MID(201)));
- EXPECT_THAT(scheduler.Produce(TimeMs(0), kMtu), HasDataWithMid(MID(102)));
- EXPECT_THAT(scheduler.Produce(TimeMs(0), kMtu), HasDataWithMid(MID(202)));
- EXPECT_EQ(scheduler.Produce(TimeMs(0), kMtu), absl::nullopt);
+ EXPECT_THAT(scheduler.Produce(kNow, kMtu), HasDataWithMid(MID(100)));
+ EXPECT_THAT(scheduler.Produce(kNow, kMtu), HasDataWithMid(MID(200)));
+ EXPECT_THAT(scheduler.Produce(kNow, kMtu), HasDataWithMid(MID(101)));
+ EXPECT_THAT(scheduler.Produce(kNow, kMtu), HasDataWithMid(MID(101)));
+ EXPECT_THAT(scheduler.Produce(kNow, kMtu), HasDataWithMid(MID(101)));
+ EXPECT_THAT(scheduler.Produce(kNow, kMtu), HasDataWithMid(MID(201)));
+ EXPECT_THAT(scheduler.Produce(kNow, kMtu), HasDataWithMid(MID(102)));
+ EXPECT_THAT(scheduler.Produce(kNow, kMtu), HasDataWithMid(MID(202)));
+ EXPECT_EQ(scheduler.Produce(kNow, kMtu), absl::nullopt);
}
// Deactivates a stream before it has finished producing all packets.
@@ -259,12 +260,12 @@ TEST(StreamSchedulerTest, StreamsCanBeMadeInactive) {
scheduler.CreateStream(&producer1, StreamID(1), StreamPriority(2));
stream1->MaybeMakeActive();
- EXPECT_THAT(scheduler.Produce(TimeMs(0), kMtu), HasDataWithMid(MID(100)));
- EXPECT_THAT(scheduler.Produce(TimeMs(0), kMtu), HasDataWithMid(MID(101)));
+ EXPECT_THAT(scheduler.Produce(kNow, kMtu), HasDataWithMid(MID(100)));
+ EXPECT_THAT(scheduler.Produce(kNow, kMtu), HasDataWithMid(MID(101)));
// ... but the stream is made inactive before it can be produced.
stream1->MakeInactive();
- EXPECT_EQ(scheduler.Produce(TimeMs(0), kMtu), absl::nullopt);
+ EXPECT_EQ(scheduler.Produce(kNow, kMtu), absl::nullopt);
}
// Resumes a paused stream - makes a stream active after inactivating it.
@@ -287,14 +288,14 @@ TEST(StreamSchedulerTest, SingleStreamCanBeResumed) {
scheduler.CreateStream(&producer1, StreamID(1), StreamPriority(2));
stream1->MaybeMakeActive();
- EXPECT_THAT(scheduler.Produce(TimeMs(0), kMtu), HasDataWithMid(MID(100)));
- EXPECT_THAT(scheduler.Produce(TimeMs(0), kMtu), HasDataWithMid(MID(101)));
+ EXPECT_THAT(scheduler.Produce(kNow, kMtu), HasDataWithMid(MID(100)));
+ EXPECT_THAT(scheduler.Produce(kNow, kMtu), HasDataWithMid(MID(101)));
stream1->MakeInactive();
- EXPECT_EQ(scheduler.Produce(TimeMs(0), kMtu), absl::nullopt);
+ EXPECT_EQ(scheduler.Produce(kNow, kMtu), absl::nullopt);
stream1->MaybeMakeActive();
- EXPECT_THAT(scheduler.Produce(TimeMs(0), kMtu), HasDataWithMid(MID(102)));
- EXPECT_EQ(scheduler.Produce(TimeMs(0), kMtu), absl::nullopt);
+ EXPECT_THAT(scheduler.Produce(kNow, kMtu), HasDataWithMid(MID(102)));
+ EXPECT_EQ(scheduler.Produce(kNow, kMtu), absl::nullopt);
}
// Iterates between streams, where one is suddenly paused and later resumed.
@@ -330,15 +331,15 @@ TEST(StreamSchedulerTest, WillRoundRobinWithPausedStream) {
scheduler.CreateStream(&producer2, StreamID(2), StreamPriority(2));
stream2->MaybeMakeActive();
- EXPECT_THAT(scheduler.Produce(TimeMs(0), kMtu), HasDataWithMid(MID(100)));
- EXPECT_THAT(scheduler.Produce(TimeMs(0), kMtu), HasDataWithMid(MID(200)));
+ EXPECT_THAT(scheduler.Produce(kNow, kMtu), HasDataWithMid(MID(100)));
+ EXPECT_THAT(scheduler.Produce(kNow, kMtu), HasDataWithMid(MID(200)));
stream1->MakeInactive();
- EXPECT_THAT(scheduler.Produce(TimeMs(0), kMtu), HasDataWithMid(MID(201)));
- EXPECT_THAT(scheduler.Produce(TimeMs(0), kMtu), HasDataWithMid(MID(202)));
+ EXPECT_THAT(scheduler.Produce(kNow, kMtu), HasDataWithMid(MID(201)));
+ EXPECT_THAT(scheduler.Produce(kNow, kMtu), HasDataWithMid(MID(202)));
stream1->MaybeMakeActive();
- EXPECT_THAT(scheduler.Produce(TimeMs(0), kMtu), HasDataWithMid(MID(101)));
- EXPECT_THAT(scheduler.Produce(TimeMs(0), kMtu), HasDataWithMid(MID(102)));
- EXPECT_EQ(scheduler.Produce(TimeMs(0), kMtu), absl::nullopt);
+ EXPECT_THAT(scheduler.Produce(kNow, kMtu), HasDataWithMid(MID(101)));
+ EXPECT_THAT(scheduler.Produce(kNow, kMtu), HasDataWithMid(MID(102)));
+ EXPECT_EQ(scheduler.Produce(kNow, kMtu), absl::nullopt);
}
// Verifies that packet counts are evenly distributed in round robin scheduling.
@@ -427,18 +428,18 @@ TEST(StreamSchedulerTest, WillDoFairQueuingWithSamePriority) {
stream2->MaybeMakeActive();
// t = 30
- EXPECT_THAT(scheduler.Produce(TimeMs(0), kMtu), HasDataWithMid(MID(100)));
+ EXPECT_THAT(scheduler.Produce(kNow, kMtu), HasDataWithMid(MID(100)));
// t = 60
- EXPECT_THAT(scheduler.Produce(TimeMs(0), kMtu), HasDataWithMid(MID(101)));
+ EXPECT_THAT(scheduler.Produce(kNow, kMtu), HasDataWithMid(MID(101)));
// t = 70
- EXPECT_THAT(scheduler.Produce(TimeMs(0), kMtu), HasDataWithMid(MID(200)));
+ EXPECT_THAT(scheduler.Produce(kNow, kMtu), HasDataWithMid(MID(200)));
// t = 90
- EXPECT_THAT(scheduler.Produce(TimeMs(0), kMtu), HasDataWithMid(MID(102)));
+ EXPECT_THAT(scheduler.Produce(kNow, kMtu), HasDataWithMid(MID(102)));
// t = 140
- EXPECT_THAT(scheduler.Produce(TimeMs(0), kMtu), HasDataWithMid(MID(201)));
+ EXPECT_THAT(scheduler.Produce(kNow, kMtu), HasDataWithMid(MID(201)));
// t = 210
- EXPECT_THAT(scheduler.Produce(TimeMs(0), kMtu), HasDataWithMid(MID(202)));
- EXPECT_EQ(scheduler.Produce(TimeMs(0), kMtu), absl::nullopt);
+ EXPECT_THAT(scheduler.Produce(kNow, kMtu), HasDataWithMid(MID(202)));
+ EXPECT_EQ(scheduler.Produce(kNow, kMtu), absl::nullopt);
}
// Will do weighted fair queuing with three streams having different priority.
@@ -492,24 +493,24 @@ TEST(StreamSchedulerTest, WillDoWeightedFairQueuingSameSizeDifferentPriority) {
stream3->MaybeMakeActive();
// t ~= 20
- EXPECT_THAT(scheduler.Produce(TimeMs(0), kMtu), HasDataWithMid(MID(300)));
+ EXPECT_THAT(scheduler.Produce(kNow, kMtu), HasDataWithMid(MID(300)));
// t ~= 40
- EXPECT_THAT(scheduler.Produce(TimeMs(0), kMtu), HasDataWithMid(MID(301)));
+ EXPECT_THAT(scheduler.Produce(kNow, kMtu), HasDataWithMid(MID(301)));
// t ~= 50
- EXPECT_THAT(scheduler.Produce(TimeMs(0), kMtu), HasDataWithMid(MID(200)));
+ EXPECT_THAT(scheduler.Produce(kNow, kMtu), HasDataWithMid(MID(200)));
// t ~= 60
- EXPECT_THAT(scheduler.Produce(TimeMs(0), kMtu), HasDataWithMid(MID(302)));
+ EXPECT_THAT(scheduler.Produce(kNow, kMtu), HasDataWithMid(MID(302)));
// t ~= 80
- EXPECT_THAT(scheduler.Produce(TimeMs(0), kMtu), HasDataWithMid(MID(100)));
+ EXPECT_THAT(scheduler.Produce(kNow, kMtu), HasDataWithMid(MID(100)));
// t ~= 100
- EXPECT_THAT(scheduler.Produce(TimeMs(0), kMtu), HasDataWithMid(MID(201)));
+ EXPECT_THAT(scheduler.Produce(kNow, kMtu), HasDataWithMid(MID(201)));
// t ~= 150
- EXPECT_THAT(scheduler.Produce(TimeMs(0), kMtu), HasDataWithMid(MID(202)));
+ EXPECT_THAT(scheduler.Produce(kNow, kMtu), HasDataWithMid(MID(202)));
// t ~= 160
- EXPECT_THAT(scheduler.Produce(TimeMs(0), kMtu), HasDataWithMid(MID(101)));
+ EXPECT_THAT(scheduler.Produce(kNow, kMtu), HasDataWithMid(MID(101)));
// t ~= 240
- EXPECT_THAT(scheduler.Produce(TimeMs(0), kMtu), HasDataWithMid(MID(102)));
- EXPECT_EQ(scheduler.Produce(TimeMs(0), kMtu), absl::nullopt);
+ EXPECT_THAT(scheduler.Produce(kNow, kMtu), HasDataWithMid(MID(102)));
+ EXPECT_EQ(scheduler.Produce(kNow, kMtu), absl::nullopt);
}
// Will do weighted fair queuing with three streams having different priority
@@ -586,24 +587,24 @@ TEST(StreamSchedulerTest, WillDoWeightedFairQueuingDifferentSizeAndPriority) {
stream3->MaybeMakeActive();
// t ~= 400
- EXPECT_THAT(scheduler.Produce(TimeMs(0), kMtu), HasDataWithMid(MID(300)));
+ EXPECT_THAT(scheduler.Produce(kNow, kMtu), HasDataWithMid(MID(300)));
// t ~= 1400
- EXPECT_THAT(scheduler.Produce(TimeMs(0), kMtu), HasDataWithMid(MID(301)));
+ EXPECT_THAT(scheduler.Produce(kNow, kMtu), HasDataWithMid(MID(301)));
// t ~= 2500
- EXPECT_THAT(scheduler.Produce(TimeMs(0), kMtu), HasDataWithMid(MID(200)));
+ EXPECT_THAT(scheduler.Produce(kNow, kMtu), HasDataWithMid(MID(200)));
// t ~= 2800
- EXPECT_THAT(scheduler.Produce(TimeMs(0), kMtu), HasDataWithMid(MID(302)));
+ EXPECT_THAT(scheduler.Produce(kNow, kMtu), HasDataWithMid(MID(302)));
// t ~= 4000
- EXPECT_THAT(scheduler.Produce(TimeMs(0), kMtu), HasDataWithMid(MID(100)));
+ EXPECT_THAT(scheduler.Produce(kNow, kMtu), HasDataWithMid(MID(100)));
// t ~= 5600
- EXPECT_THAT(scheduler.Produce(TimeMs(0), kMtu), HasDataWithMid(MID(101)));
+ EXPECT_THAT(scheduler.Produce(kNow, kMtu), HasDataWithMid(MID(101)));
// t ~= 6000
- EXPECT_THAT(scheduler.Produce(TimeMs(0), kMtu), HasDataWithMid(MID(201)));
+ EXPECT_THAT(scheduler.Produce(kNow, kMtu), HasDataWithMid(MID(201)));
// t ~= 7000
- EXPECT_THAT(scheduler.Produce(TimeMs(0), kMtu), HasDataWithMid(MID(202)));
+ EXPECT_THAT(scheduler.Produce(kNow, kMtu), HasDataWithMid(MID(202)));
// t ~= 11200
- EXPECT_THAT(scheduler.Produce(TimeMs(0), kMtu), HasDataWithMid(MID(102)));
- EXPECT_EQ(scheduler.Produce(TimeMs(0), kMtu), absl::nullopt);
+ EXPECT_THAT(scheduler.Produce(kNow, kMtu), HasDataWithMid(MID(102)));
+ EXPECT_EQ(scheduler.Produce(kNow, kMtu), absl::nullopt);
}
TEST(StreamSchedulerTest, WillDistributeWFQPacketsInTwoStreamsByPriority) {
// A simple test with two streams of different priority, but sending packets
@@ -723,11 +724,11 @@ TEST(StreamSchedulerTest, SendLargeMessageWithSmallMtu) {
auto stream2 =
scheduler.CreateStream(&producer2, StreamID(2), StreamPriority(1));
stream2->MaybeMakeActive();
- EXPECT_THAT(scheduler.Produce(TimeMs(0), kMtu), HasDataWithMid(MID(0)));
- EXPECT_THAT(scheduler.Produce(TimeMs(0), kMtu), HasDataWithMid(MID(1)));
- EXPECT_THAT(scheduler.Produce(TimeMs(0), kMtu), HasDataWithMid(MID(1)));
- EXPECT_THAT(scheduler.Produce(TimeMs(0), kMtu), HasDataWithMid(MID(0)));
- EXPECT_EQ(scheduler.Produce(TimeMs(0), kMtu), absl::nullopt);
+ EXPECT_THAT(scheduler.Produce(kNow, kMtu), HasDataWithMid(MID(0)));
+ EXPECT_THAT(scheduler.Produce(kNow, kMtu), HasDataWithMid(MID(1)));
+ EXPECT_THAT(scheduler.Produce(kNow, kMtu), HasDataWithMid(MID(1)));
+ EXPECT_THAT(scheduler.Produce(kNow, kMtu), HasDataWithMid(MID(0)));
+ EXPECT_EQ(scheduler.Produce(kNow, kMtu), absl::nullopt);
}
// Sending large messages with large MTU will not fragment messages and will
@@ -756,9 +757,9 @@ TEST(StreamSchedulerTest, SendLargeMessageWithLargeMtu) {
auto stream2 =
scheduler.CreateStream(&producer2, StreamID(2), StreamPriority(1));
stream2->MaybeMakeActive();
- EXPECT_THAT(scheduler.Produce(TimeMs(0), kMtu), HasDataWithMid(MID(1)));
- EXPECT_THAT(scheduler.Produce(TimeMs(0), kMtu), HasDataWithMid(MID(0)));
- EXPECT_EQ(scheduler.Produce(TimeMs(0), kMtu), absl::nullopt);
+ EXPECT_THAT(scheduler.Produce(kNow, kMtu), HasDataWithMid(MID(1)));
+ EXPECT_THAT(scheduler.Produce(kNow, kMtu), HasDataWithMid(MID(0)));
+ EXPECT_EQ(scheduler.Produce(kNow, kMtu), absl::nullopt);
}
} // namespace
diff --git a/third_party/libwebrtc/p2p/BUILD.gn b/third_party/libwebrtc/p2p/BUILD.gn
index 596a9fe5b5..a663e85e22 100644
--- a/third_party/libwebrtc/p2p/BUILD.gn
+++ b/third_party/libwebrtc/p2p/BUILD.gn
@@ -105,7 +105,6 @@ rtc_library("rtc_p2p") {
"../api:scoped_refptr",
"../api:sequence_checker",
"../api:turn_customizer",
- "../api:wrapping_async_dns_resolver",
"../api/crypto:options",
"../api/rtc_event_log",
"../api/task_queue",
@@ -117,7 +116,6 @@ rtc_library("rtc_p2p") {
"../logging:ice_log",
"../rtc_base:async_dns_resolver",
"../rtc_base:async_packet_socket",
- "../rtc_base:async_resolver_interface",
"../rtc_base:async_tcp_socket",
"../rtc_base:async_udp_socket",
"../rtc_base:buffer",
@@ -220,7 +218,6 @@ if (rtc_include_tests) {
"base/fake_dtls_transport.h",
"base/fake_packet_transport.h",
"base/mock_active_ice_controller.h",
- "base/mock_async_resolver.h",
"base/mock_dns_resolving_packet_socket_factory.h",
"base/mock_ice_agent.h",
"base/mock_ice_controller.h",
@@ -243,7 +240,6 @@ if (rtc_include_tests) {
"../api:turn_customizer",
"../api/crypto:options",
"../api/transport:stun_types",
- "../rtc_base:async_resolver_interface",
"../rtc_base:async_udp_socket",
"../rtc_base:copy_on_write_buffer",
"../rtc_base:gunit_helpers",
@@ -268,7 +264,6 @@ if (rtc_include_tests) {
sources = [
"base/async_stun_tcp_socket_unittest.cc",
- "base/basic_async_resolver_factory_unittest.cc",
"base/dtls_transport_unittest.cc",
"base/ice_credentials_iterator_unittest.cc",
"base/p2p_transport_channel_unittest.cc",
@@ -294,6 +289,7 @@ if (rtc_include_tests) {
":p2p_server_utils",
":p2p_test_utils",
":rtc_p2p",
+ "../api:array_view",
"../api:candidate",
"../api:dtls_transport_interface",
"../api:field_trials_view",
@@ -332,6 +328,7 @@ if (rtc_include_tests) {
"../rtc_base:testclient",
"../rtc_base:threading",
"../rtc_base:timeutils",
+ "../rtc_base/network:received_packet",
"../rtc_base/network:sent_packet",
"../rtc_base/third_party/sigslot",
"../system_wrappers:metrics",
@@ -376,6 +373,7 @@ rtc_library("p2p_server_utils") {
"../rtc_base:socket_address",
"../rtc_base:ssl",
"../rtc_base:stringutils",
+ "../rtc_base/network:received_packet",
"../rtc_base/third_party/sigslot",
]
absl_deps = [
@@ -394,6 +392,7 @@ rtc_library("libstunprober") {
deps = [
":rtc_p2p",
+ "../api:array_view",
"../api:async_dns_resolver",
"../api:packet_socket_factory",
"../api:sequence_checker",
@@ -401,7 +400,6 @@ rtc_library("libstunprober") {
"../api/transport:stun_types",
"../api/units:time_delta",
"../rtc_base:async_packet_socket",
- "../rtc_base:async_resolver_interface",
"../rtc_base:byte_buffer",
"../rtc_base:checks",
"../rtc_base:ip_address",
@@ -411,6 +409,7 @@ rtc_library("libstunprober") {
"../rtc_base:ssl",
"../rtc_base:threading",
"../rtc_base:timeutils",
+ "../rtc_base/network:received_packet",
"../rtc_base/system:rtc_export",
]
}
diff --git a/third_party/libwebrtc/p2p/base/async_stun_tcp_socket.cc b/third_party/libwebrtc/p2p/base/async_stun_tcp_socket.cc
index 4a35903dfe..dcaf7d285d 100644
--- a/third_party/libwebrtc/p2p/base/async_stun_tcp_socket.cc
+++ b/third_party/libwebrtc/p2p/base/async_stun_tcp_socket.cc
@@ -14,9 +14,15 @@
#include <stdint.h>
#include <string.h>
+#include <cstddef>
+#include <cstdint>
+
+#include "api/array_view.h"
#include "api/transport/stun.h"
+#include "api/units/timestamp.h"
#include "rtc_base/byte_order.h"
#include "rtc_base/checks.h"
+#include "rtc_base/network/received_packet.h"
#include "rtc_base/network/sent_packet.h"
#include "rtc_base/time_utils.h"
@@ -89,7 +95,7 @@ int AsyncStunTCPSocket::Send(const void* pv,
return static_cast<int>(cb);
}
-void AsyncStunTCPSocket::ProcessInput(char* data, size_t* len) {
+size_t AsyncStunTCPSocket::ProcessInput(rtc::ArrayView<const uint8_t> data) {
rtc::SocketAddress remote_addr(GetRemoteAddress());
// STUN packet - First 4 bytes. Total header size is 20 bytes.
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
@@ -101,26 +107,27 @@ void AsyncStunTCPSocket::ProcessInput(char* data, size_t* len) {
// | Channel Number | Length |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ size_t processed_bytes = 0;
while (true) {
+ size_t bytes_left = data.size() - processed_bytes;
// We need at least 4 bytes to read the STUN or ChannelData packet length.
- if (*len < kPacketLenOffset + kPacketLenSize)
- return;
+ if (bytes_left < kPacketLenOffset + kPacketLenSize)
+ return processed_bytes;
int pad_bytes;
- size_t expected_pkt_len = GetExpectedLength(data, *len, &pad_bytes);
+ size_t expected_pkt_len = GetExpectedLength(data.data() + processed_bytes,
+ bytes_left, &pad_bytes);
size_t actual_length = expected_pkt_len + pad_bytes;
- if (*len < actual_length) {
- return;
+ if (bytes_left < actual_length) {
+ return processed_bytes;
}
- SignalReadPacket(this, data, expected_pkt_len, remote_addr,
- rtc::TimeMicros());
-
- *len -= actual_length;
- if (*len > 0) {
- memmove(data, data + actual_length, *len);
- }
+ rtc::ReceivedPacket received_packet(
+ data.subview(processed_bytes, expected_pkt_len), remote_addr,
+ webrtc::Timestamp::Micros(rtc::TimeMicros()));
+ NotifyPacketReceived(received_packet);
+ processed_bytes += actual_length;
}
}
diff --git a/third_party/libwebrtc/p2p/base/async_stun_tcp_socket.h b/third_party/libwebrtc/p2p/base/async_stun_tcp_socket.h
index f0df42b52a..2c43d554fe 100644
--- a/third_party/libwebrtc/p2p/base/async_stun_tcp_socket.h
+++ b/third_party/libwebrtc/p2p/base/async_stun_tcp_socket.h
@@ -37,7 +37,7 @@ class AsyncStunTCPSocket : public rtc::AsyncTCPSocketBase {
int Send(const void* pv,
size_t cb,
const rtc::PacketOptions& options) override;
- void ProcessInput(char* data, size_t* len) override;
+ size_t ProcessInput(rtc::ArrayView<const uint8_t> data) override;
private:
// This method returns the message hdr + length written in the header.
diff --git a/third_party/libwebrtc/p2p/base/async_stun_tcp_socket_unittest.cc b/third_party/libwebrtc/p2p/base/async_stun_tcp_socket_unittest.cc
index 72d6a7fde0..853fbb471f 100644
--- a/third_party/libwebrtc/p2p/base/async_stun_tcp_socket_unittest.cc
+++ b/third_party/libwebrtc/p2p/base/async_stun_tcp_socket_unittest.cc
@@ -13,12 +13,17 @@
#include <stdint.h>
#include <string.h>
+#include <cstdint>
#include <list>
#include <memory>
#include <string>
#include <utility>
#include "absl/memory/memory.h"
+#include "api/array_view.h"
+#include "rtc_base/buffer.h"
+#include "rtc_base/byte_buffer.h"
+#include "rtc_base/network/received_packet.h"
#include "rtc_base/network/sent_packet.h"
#include "rtc_base/socket.h"
#include "rtc_base/third_party/sigslot/sigslot.h"
@@ -96,11 +101,10 @@ class AsyncStunTCPSocketTest : public ::testing::Test,
}
void OnReadPacket(rtc::AsyncPacketSocket* socket,
- const char* data,
- size_t len,
- const rtc::SocketAddress& remote_addr,
- const int64_t& /* packet_time_us */) {
- recv_packets_.push_back(std::string(data, len));
+ const rtc::ReceivedPacket& packet) {
+ recv_packets_.push_back(
+ std::string(reinterpret_cast<const char*>(packet.payload().data()),
+ packet.payload().size()));
}
void OnSentPacket(rtc::AsyncPacketSocket* socket,
@@ -111,8 +115,10 @@ class AsyncStunTCPSocketTest : public ::testing::Test,
void OnNewConnection(rtc::AsyncListenSocket* /*server*/,
rtc::AsyncPacketSocket* new_socket) {
recv_socket_ = absl::WrapUnique(new_socket);
- new_socket->SignalReadPacket.connect(this,
- &AsyncStunTCPSocketTest::OnReadPacket);
+ new_socket->RegisterReceivedPacketCallback(
+ [&](rtc::AsyncPacketSocket* socket, const rtc::ReceivedPacket& packet) {
+ OnReadPacket(socket, packet);
+ });
}
bool Send(const void* data, size_t len) {
@@ -164,6 +170,30 @@ TEST_F(AsyncStunTCPSocketTest, TestMultipleStunPackets) {
EXPECT_EQ(4u, recv_packets_.size());
}
+TEST_F(AsyncStunTCPSocketTest, ProcessInputHandlesMultiplePackets) {
+ send_socket_->RegisterReceivedPacketCallback(
+ [&](rtc::AsyncPacketSocket* socket, const rtc::ReceivedPacket& packet) {
+ recv_packets_.push_back(
+ std::string(reinterpret_cast<const char*>(packet.payload().data()),
+ packet.payload().size()));
+ });
+ rtc::Buffer buffer;
+ buffer.AppendData(kStunMessageWithZeroLength,
+ sizeof(kStunMessageWithZeroLength));
+ // ChannelData message MUST be padded to
+ // a multiple of four bytes.
+ const unsigned char kTurnChannelData[] = {
+ 0x40, 0x00, 0x00, 0x04, 0x21, 0x12, 0xA4, 0x42,
+ };
+ buffer.AppendData(kTurnChannelData, sizeof(kTurnChannelData));
+
+ send_socket_->ProcessInput(buffer);
+ EXPECT_EQ(2u, recv_packets_.size());
+ EXPECT_TRUE(CheckData(kStunMessageWithZeroLength,
+ sizeof(kStunMessageWithZeroLength)));
+ EXPECT_TRUE(CheckData(kTurnChannelData, sizeof(kTurnChannelData)));
+}
+
// Verifying TURN channel data message with zero length.
TEST_F(AsyncStunTCPSocketTest, TestTurnChannelDataWithZeroLength) {
EXPECT_TRUE(Send(kTurnChannelDataMessageWithZeroLength,
diff --git a/third_party/libwebrtc/p2p/base/basic_async_resolver_factory.cc b/third_party/libwebrtc/p2p/base/basic_async_resolver_factory.cc
index 5a8c7a27a7..b69e066039 100644
--- a/third_party/libwebrtc/p2p/base/basic_async_resolver_factory.cc
+++ b/third_party/libwebrtc/p2p/base/basic_async_resolver_factory.cc
@@ -15,20 +15,11 @@
#include "absl/memory/memory.h"
#include "api/async_dns_resolver.h"
-#include "api/wrapping_async_dns_resolver.h"
#include "rtc_base/async_dns_resolver.h"
-#include "rtc_base/async_resolver.h"
#include "rtc_base/logging.h"
namespace webrtc {
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
-rtc::AsyncResolverInterface* BasicAsyncResolverFactory::Create() {
- return new rtc::AsyncResolver();
-}
-#pragma clang diagnostic pop
-
std::unique_ptr<webrtc::AsyncDnsResolverInterface>
BasicAsyncDnsResolverFactory::Create() {
return std::make_unique<AsyncDnsResolver>();
@@ -53,28 +44,4 @@ BasicAsyncDnsResolverFactory::CreateAndResolve(
return resolver;
}
-std::unique_ptr<webrtc::AsyncDnsResolverInterface>
-WrappingAsyncDnsResolverFactory::Create() {
- return std::make_unique<WrappingAsyncDnsResolver>(wrapped_factory_->Create());
-}
-
-std::unique_ptr<webrtc::AsyncDnsResolverInterface>
-WrappingAsyncDnsResolverFactory::CreateAndResolve(
- const rtc::SocketAddress& addr,
- absl::AnyInvocable<void()> callback) {
- std::unique_ptr<webrtc::AsyncDnsResolverInterface> resolver = Create();
- resolver->Start(addr, std::move(callback));
- return resolver;
-}
-
-std::unique_ptr<webrtc::AsyncDnsResolverInterface>
-WrappingAsyncDnsResolverFactory::CreateAndResolve(
- const rtc::SocketAddress& addr,
- int family,
- absl::AnyInvocable<void()> callback) {
- std::unique_ptr<webrtc::AsyncDnsResolverInterface> resolver = Create();
- resolver->Start(addr, family, std::move(callback));
- return resolver;
-}
-
} // namespace webrtc
diff --git a/third_party/libwebrtc/p2p/base/basic_async_resolver_factory.h b/third_party/libwebrtc/p2p/base/basic_async_resolver_factory.h
index 1a94fb9679..f99fb3e6b5 100644
--- a/third_party/libwebrtc/p2p/base/basic_async_resolver_factory.h
+++ b/third_party/libwebrtc/p2p/base/basic_async_resolver_factory.h
@@ -16,21 +16,9 @@
#include <utility>
#include "api/async_dns_resolver.h"
-#include "api/async_resolver_factory.h"
-#include "rtc_base/async_resolver_interface.h"
namespace webrtc {
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
-class [[deprecated(
- "Use BasicAsyncDnsResolverFactory")]] BasicAsyncResolverFactory final
- : public AsyncResolverFactory {
- public:
- rtc::AsyncResolverInterface* Create() override;
-};
-#pragma clang diagnostic pop
-
// A factory that vends AsyncDnsResolver instances.
class BasicAsyncDnsResolverFactory final
: public AsyncDnsResolverFactoryInterface {
@@ -49,40 +37,6 @@ class BasicAsyncDnsResolverFactory final
std::unique_ptr<webrtc::AsyncDnsResolverInterface> Create() override;
};
-// This class wraps a factory using the older webrtc::AsyncResolverFactory API,
-// and produces webrtc::AsyncDnsResolver objects that contain an
-// rtc::AsyncResolver object.
-class [[deprecated]] WrappingAsyncDnsResolverFactory final
- : public AsyncDnsResolverFactoryInterface {
- public:
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
- explicit WrappingAsyncDnsResolverFactory(
- std::unique_ptr<AsyncResolverFactory> wrapped_factory)
- : owned_factory_(std::move(wrapped_factory)),
- wrapped_factory_(owned_factory_.get()) {}
-
- explicit WrappingAsyncDnsResolverFactory(
- AsyncResolverFactory* non_owned_factory)
- : wrapped_factory_(non_owned_factory) {}
-#pragma clang diagnostic pop
-
- std::unique_ptr<webrtc::AsyncDnsResolverInterface> CreateAndResolve(
- const rtc::SocketAddress& addr,
- absl::AnyInvocable<void()> callback) override;
-
- std::unique_ptr<webrtc::AsyncDnsResolverInterface> CreateAndResolve(
- const rtc::SocketAddress& addr,
- int family,
- absl::AnyInvocable<void()> callback) override;
-
- std::unique_ptr<webrtc::AsyncDnsResolverInterface> Create() override;
-
- private:
- const std::unique_ptr<AsyncResolverFactory> owned_factory_;
- AsyncResolverFactory* const wrapped_factory_;
-};
-
} // namespace webrtc
#endif // P2P_BASE_BASIC_ASYNC_RESOLVER_FACTORY_H_
diff --git a/third_party/libwebrtc/p2p/base/basic_async_resolver_factory_unittest.cc b/third_party/libwebrtc/p2p/base/basic_async_resolver_factory_unittest.cc
index 313907abb9..39b7c2d1d0 100644
--- a/third_party/libwebrtc/p2p/base/basic_async_resolver_factory_unittest.cc
+++ b/third_party/libwebrtc/p2p/base/basic_async_resolver_factory_unittest.cc
@@ -11,8 +11,6 @@
#include "p2p/base/basic_async_resolver_factory.h"
#include "api/test/mock_async_dns_resolver.h"
-#include "p2p/base/mock_async_resolver.h"
-#include "rtc_base/async_resolver.h"
#include "rtc_base/gunit.h"
#include "rtc_base/socket_address.h"
#include "rtc_base/third_party/sigslot/sigslot.h"
@@ -22,95 +20,6 @@
namespace webrtc {
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
-
-class BasicAsyncResolverFactoryTest : public ::testing::Test,
- public sigslot::has_slots<> {
- public:
- void TestCreate() {
- BasicAsyncResolverFactory factory;
- rtc::AsyncResolverInterface* resolver = factory.Create();
- ASSERT_TRUE(resolver);
- resolver->SignalDone.connect(
- this, &BasicAsyncResolverFactoryTest::SetAddressResolved);
-
- rtc::SocketAddress address("", 0);
- resolver->Start(address);
- ASSERT_TRUE_WAIT(address_resolved_, 10000 /*ms*/);
- resolver->Destroy(false);
- }
-
- void SetAddressResolved(rtc::AsyncResolverInterface* resolver) {
- address_resolved_ = true;
- }
-
- private:
- bool address_resolved_ = false;
-};
-
-// This test is primarily intended to let tools check that the created resolver
-// doesn't leak.
-TEST_F(BasicAsyncResolverFactoryTest, TestCreate) {
- rtc::AutoThread main_thread;
- TestCreate();
-}
-
-TEST(WrappingAsyncDnsResolverFactoryTest, TestCreateAndResolve) {
- rtc::AutoThread main_thread;
- WrappingAsyncDnsResolverFactory factory(
- std::make_unique<BasicAsyncResolverFactory>());
-
- std::unique_ptr<AsyncDnsResolverInterface> resolver(factory.Create());
- ASSERT_TRUE(resolver);
-
- bool address_resolved = false;
- rtc::SocketAddress address("", 0);
- resolver->Start(address, [&address_resolved]() { address_resolved = true; });
- ASSERT_TRUE_WAIT(address_resolved, 10000 /*ms*/);
- resolver.reset();
-}
-
-TEST(WrappingAsyncDnsResolverFactoryTest, WrapOtherResolver) {
- rtc::AutoThread main_thread;
- BasicAsyncResolverFactory non_owned_factory;
- WrappingAsyncDnsResolverFactory factory(&non_owned_factory);
- std::unique_ptr<AsyncDnsResolverInterface> resolver(factory.Create());
- ASSERT_TRUE(resolver);
-
- bool address_resolved = false;
- rtc::SocketAddress address("", 0);
- resolver->Start(address, [&address_resolved]() { address_resolved = true; });
- ASSERT_TRUE_WAIT(address_resolved, 10000 /*ms*/);
- resolver.reset();
-}
-
-#if GTEST_HAS_DEATH_TEST && defined(WEBRTC_LINUX)
-// Tests that the prohibition against deleting the resolver from the callback
-// is enforced. This is required by the use of sigslot in the wrapped resolver.
-// Checking the error message fails on a number of platforms, so run this
-// test only on the platforms where it works.
-void CallResolver(WrappingAsyncDnsResolverFactory& factory) {
- rtc::SocketAddress address("", 0);
- std::unique_ptr<AsyncDnsResolverInterface> resolver(factory.Create());
- resolver->Start(address, [&resolver]() { resolver.reset(); });
- WAIT(!resolver.get(), 10000 /*ms*/);
-}
-
-TEST(WrappingAsyncDnsResolverFactoryDeathTest, DestroyResolverInCallback) {
- rtc::AutoThread main_thread;
- // TODO(bugs.webrtc.org/12652): Rewrite as death test in loop style when it
- // works.
- WrappingAsyncDnsResolverFactory factory(
- std::make_unique<BasicAsyncResolverFactory>());
-
- // Since EXPECT_DEATH is thread sensitive, and the resolver creates a thread,
- // we wrap the whole creation section in EXPECT_DEATH.
- RTC_EXPECT_DEATH(CallResolver(factory),
- "Check failed: !within_resolve_result_");
-}
-#endif
-
-#pragma clang diagnostic pop
+// all tests were on deleted APIs
} // namespace webrtc
diff --git a/third_party/libwebrtc/p2p/base/basic_packet_socket_factory.cc b/third_party/libwebrtc/p2p/base/basic_packet_socket_factory.cc
index 7d87e12859..6a811af71a 100644
--- a/third_party/libwebrtc/p2p/base/basic_packet_socket_factory.cc
+++ b/third_party/libwebrtc/p2p/base/basic_packet_socket_factory.cc
@@ -16,7 +16,6 @@
#include "absl/memory/memory.h"
#include "api/async_dns_resolver.h"
-#include "api/wrapping_async_dns_resolver.h"
#include "p2p/base/async_stun_tcp_socket.h"
#include "rtc_base/async_dns_resolver.h"
#include "rtc_base/async_tcp_socket.h"
@@ -181,10 +180,6 @@ AsyncPacketSocket* BasicPacketSocketFactory::CreateClientTcpSocket(
return tcp_socket;
}
-AsyncResolverInterface* BasicPacketSocketFactory::CreateAsyncResolver() {
- return new AsyncResolver();
-}
-
std::unique_ptr<webrtc::AsyncDnsResolverInterface>
BasicPacketSocketFactory::CreateAsyncDnsResolver() {
return std::make_unique<webrtc::AsyncDnsResolver>();
diff --git a/third_party/libwebrtc/p2p/base/basic_packet_socket_factory.h b/third_party/libwebrtc/p2p/base/basic_packet_socket_factory.h
index 396a8ba4eb..f9bdf7b2c7 100644
--- a/third_party/libwebrtc/p2p/base/basic_packet_socket_factory.h
+++ b/third_party/libwebrtc/p2p/base/basic_packet_socket_factory.h
@@ -48,10 +48,6 @@ class RTC_EXPORT BasicPacketSocketFactory : public PacketSocketFactory {
const std::string& user_agent,
const PacketSocketTcpOptions& tcp_options) override;
- // TODO(bugs.webrtc.org/12598) Remove when downstream stops using it.
- ABSL_DEPRECATED("Use CreateAsyncDnsResolver")
- AsyncResolverInterface* CreateAsyncResolver() override;
-
std::unique_ptr<webrtc::AsyncDnsResolverInterface> CreateAsyncDnsResolver()
override;
diff --git a/third_party/libwebrtc/p2p/base/connection.cc b/third_party/libwebrtc/p2p/base/connection.cc
index 3bdba0e445..d0e6f1bff8 100644
--- a/third_party/libwebrtc/p2p/base/connection.cc
+++ b/third_party/libwebrtc/p2p/base/connection.cc
@@ -466,32 +466,25 @@ void Connection::DeregisterReceivedPacketCallback() {
void Connection::OnReadPacket(const char* data,
size_t size,
int64_t packet_time_us) {
+ OnReadPacket(
+ rtc::ReceivedPacket::CreateFromLegacy(data, size, packet_time_us));
+}
+void Connection::OnReadPacket(const rtc::ReceivedPacket& packet) {
RTC_DCHECK_RUN_ON(network_thread_);
std::unique_ptr<IceMessage> msg;
std::string remote_ufrag;
const rtc::SocketAddress& addr(remote_candidate_.address());
- if (!port_->GetStunMessage(data, size, addr, &msg, &remote_ufrag)) {
+ if (!port_->GetStunMessage(
+ reinterpret_cast<const char*>(packet.payload().data()),
+ packet.payload().size(), addr, &msg, &remote_ufrag)) {
// The packet did not parse as a valid STUN message
// This is a data packet, pass it along.
last_data_received_ = rtc::TimeMillis();
UpdateReceiving(last_data_received_);
- recv_rate_tracker_.AddSamples(size);
+ recv_rate_tracker_.AddSamples(packet.payload().size());
stats_.packets_received++;
if (received_packet_callback_) {
- RTC_DCHECK(packet_time_us == -1 || packet_time_us >= 0);
- RTC_DCHECK(SignalReadPacket.is_empty());
- received_packet_callback_(
- this, rtc::ReceivedPacket(
- rtc::reinterpret_array_view<const uint8_t>(
- rtc::MakeArrayView(data, size)),
- (packet_time_us >= 0)
- ? absl::optional<webrtc::Timestamp>(
- webrtc::Timestamp::Micros(packet_time_us))
- : absl::nullopt));
- } else {
- // TODO(webrtc:11943): Remove SignalReadPacket once upstream projects have
- // switched to use RegisterReceivedPacket.
- SignalReadPacket(this, data, size, packet_time_us);
+ received_packet_callback_(this, packet);
}
// If timed out sending writability checks, start up again
if (!pruned_ && (write_state_ == STATE_WRITE_TIMEOUT)) {
diff --git a/third_party/libwebrtc/p2p/base/connection.h b/third_party/libwebrtc/p2p/base/connection.h
index 3481c69cd9..cf54dc800f 100644
--- a/third_party/libwebrtc/p2p/base/connection.h
+++ b/third_party/libwebrtc/p2p/base/connection.h
@@ -147,10 +147,6 @@ class RTC_EXPORT Connection : public CandidatePairInterface {
// Error if Send() returns < 0
virtual int GetError() = 0;
- // TODO(webrtc:11943): Remove SignalReadPacket once upstream projects have
- // switched to use RegisterReceivedPacket.
- sigslot::signal4<Connection*, const char*, size_t, int64_t> SignalReadPacket;
-
// Register as a recipient of received packets. There can only be one.
void RegisterReceivedPacketCallback(
absl::AnyInvocable<void(Connection*, const rtc::ReceivedPacket&)>
@@ -160,7 +156,9 @@ class RTC_EXPORT Connection : public CandidatePairInterface {
sigslot::signal1<Connection*> SignalReadyToSend;
// Called when a packet is received on this connection.
- void OnReadPacket(const char* data, size_t size, int64_t packet_time_us);
+ void OnReadPacket(const rtc::ReceivedPacket& packet);
+ [[deprecated("Pass a rtc::ReceivedPacket")]] void
+ OnReadPacket(const char* data, size_t size, int64_t packet_time_us);
// Called when the socket is currently able to send.
void OnReadyToSend();
diff --git a/third_party/libwebrtc/p2p/base/fake_ice_transport.h b/third_party/libwebrtc/p2p/base/fake_ice_transport.h
index ae7bf8947e..6172ebb15b 100644
--- a/third_party/libwebrtc/p2p/base/fake_ice_transport.h
+++ b/third_party/libwebrtc/p2p/base/fake_ice_transport.h
@@ -124,7 +124,7 @@ class FakeIceTransport : public IceTransportInternal {
RTC_DCHECK_RUN_ON(network_thread_);
if (gathering_state_ != kIceGatheringComplete) {
gathering_state_ = kIceGatheringComplete;
- SignalGatheringState(this);
+ SendGatheringStateEvent();
}
}
@@ -232,7 +232,7 @@ class FakeIceTransport : public IceTransportInternal {
RTC_DCHECK_RUN_ON(network_thread_);
if (gathering_state_ == kIceGatheringNew) {
gathering_state_ = kIceGatheringGathering;
- SignalGatheringState(this);
+ SendGatheringStateEvent();
}
}
diff --git a/third_party/libwebrtc/p2p/base/ice_transport_internal.cc b/third_party/libwebrtc/p2p/base/ice_transport_internal.cc
index fab6f2037a..0dc7e50af2 100644
--- a/third_party/libwebrtc/p2p/base/ice_transport_internal.cc
+++ b/third_party/libwebrtc/p2p/base/ice_transport_internal.cc
@@ -123,7 +123,13 @@ int IceConfig::stun_keepalive_interval_or_default() const {
return stun_keepalive_interval.value_or(STUN_KEEPALIVE_INTERVAL);
}
-IceTransportInternal::IceTransportInternal() = default;
+IceTransportInternal::IceTransportInternal() {
+ // Set up detector for use of SignalGatheringState rather than
+ // SetGatheringStateCallback, and behave accordingly
+ // TODO(bugs.webrtc.org/11943): remove when Signal removed
+ SignalGatheringState.connect(
+ this, &IceTransportInternal::SignalGatheringStateFired);
+}
IceTransportInternal::~IceTransportInternal() = default;
diff --git a/third_party/libwebrtc/p2p/base/ice_transport_internal.h b/third_party/libwebrtc/p2p/base/ice_transport_internal.h
index eb21596612..38b6bfe303 100644
--- a/third_party/libwebrtc/p2p/base/ice_transport_internal.h
+++ b/third_party/libwebrtc/p2p/base/ice_transport_internal.h
@@ -304,17 +304,31 @@ class RTC_EXPORT IceTransportInternal : public rtc::PacketTransportInternal {
return absl::nullopt;
}
+ // Signal Exposed for backwards compatibility.
sigslot::signal1<IceTransportInternal*> SignalGatheringState;
+ void SetGatheringStateCallback(
+ absl::AnyInvocable<void(IceTransportInternal*)> callback) {
+ RTC_DCHECK(!gathering_state_callback_);
+ gathering_state_callback_ = std::move(callback);
+ }
// Handles sending and receiving of candidates.
sigslot::signal2<IceTransportInternal*, const Candidate&>
SignalCandidateGathered;
- sigslot::signal2<IceTransportInternal*, const IceCandidateErrorEvent&>
- SignalCandidateError;
+ void SetCandidateErrorCallback(
+ absl::AnyInvocable<void(IceTransportInternal*,
+ const IceCandidateErrorEvent&)> callback) {
+ RTC_DCHECK(!candidate_error_callback_);
+ candidate_error_callback_ = std::move(callback);
+ }
- sigslot::signal2<IceTransportInternal*, const Candidates&>
- SignalCandidatesRemoved;
+ void SetCandidatesRemovedCallback(
+ absl::AnyInvocable<void(IceTransportInternal*, const Candidates&)>
+ callback) {
+ RTC_DCHECK(!candidates_removed_callback_);
+ candidates_removed_callback_ = std::move(callback);
+ }
// Deprecated by PacketTransportInternal::SignalNetworkRouteChanged.
// This signal occurs when there is a change in the way that packets are
@@ -324,8 +338,12 @@ class RTC_EXPORT IceTransportInternal : public rtc::PacketTransportInternal {
// SignalNetworkRouteChanged.
sigslot::signal2<IceTransportInternal*, const Candidate&> SignalRouteChange;
- sigslot::signal1<const cricket::CandidatePairChangeEvent&>
- SignalCandidatePairChanged;
+ void SetCandidatePairChangeCallback(
+ absl::AnyInvocable<void(const cricket::CandidatePairChangeEvent&)>
+ callback) {
+ RTC_DCHECK(!candidate_pair_change_callback_);
+ candidate_pair_change_callback_ = std::move(callback);
+ }
// Invoked when there is conflict in the ICE role between local and remote
// agents.
@@ -366,12 +384,33 @@ class RTC_EXPORT IceTransportInternal : public rtc::PacketTransportInternal {
}
protected:
+ void SendGatheringStateEvent() { SignalGatheringState(this); }
+
webrtc::CallbackList<IceTransportInternal*,
const StunDictionaryView&,
rtc::ArrayView<uint16_t>>
dictionary_view_updated_callback_list_;
webrtc::CallbackList<IceTransportInternal*, const StunDictionaryWriter&>
dictionary_writer_synced_callback_list_;
+
+ absl::AnyInvocable<void(IceTransportInternal*)> gathering_state_callback_;
+
+ absl::AnyInvocable<void(IceTransportInternal*, const IceCandidateErrorEvent&)>
+ candidate_error_callback_;
+
+ absl::AnyInvocable<void(IceTransportInternal*, const Candidates&)>
+ candidates_removed_callback_;
+
+ absl::AnyInvocable<void(const cricket::CandidatePairChangeEvent&)>
+ candidate_pair_change_callback_;
+
+ private:
+ // TODO(bugs.webrtc.org/11943): remove when removing Signal
+ void SignalGatheringStateFired(IceTransportInternal* transport) {
+ if (gathering_state_callback_) {
+ gathering_state_callback_(transport);
+ }
+ }
};
} // namespace cricket
diff --git a/third_party/libwebrtc/p2p/base/mock_async_resolver.h b/third_party/libwebrtc/p2p/base/mock_async_resolver.h
deleted file mode 100644
index 73ad1a4c34..0000000000
--- a/third_party/libwebrtc/p2p/base/mock_async_resolver.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright 2018 The WebRTC Project Authors. All rights reserved.
- *
- * Use of this source code is governed by a BSD-style license
- * that can be found in the LICENSE file in the root of the source
- * tree. An additional intellectual property rights grant can be found
- * in the file PATENTS. All contributing project authors may
- * be found in the AUTHORS file in the root of the source tree.
- */
-
-#ifndef P2P_BASE_MOCK_ASYNC_RESOLVER_H_
-#define P2P_BASE_MOCK_ASYNC_RESOLVER_H_
-
-#include "api/async_resolver_factory.h"
-#include "rtc_base/async_resolver_interface.h"
-#include "test/gmock.h"
-
-namespace rtc {
-
-using ::testing::_;
-using ::testing::InvokeWithoutArgs;
-
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
-class [[deprecated]] MockAsyncResolver : public AsyncResolverInterface {
-#pragma clang diagnostic pop
- public:
- MockAsyncResolver() {
- ON_CALL(*this, Start(_)).WillByDefault(InvokeWithoutArgs([this] {
- SignalDone(this);
- }));
- }
- ~MockAsyncResolver() = default;
-
- MOCK_METHOD(void, Start, (const rtc::SocketAddress&), (override));
- MOCK_METHOD(void, Start, (const rtc::SocketAddress&, int family), (override));
- MOCK_METHOD(bool,
- GetResolvedAddress,
- (int family, SocketAddress* addr),
- (const, override));
- MOCK_METHOD(int, GetError, (), (const, override));
-
- // Note that this won't delete the object like AsyncResolverInterface says in
- // order to avoid sanitizer failures caused by this being a synchronous
- // implementation. The test code should delete the object instead.
- MOCK_METHOD(void, Destroy, (bool), (override));
-};
-
-} // namespace rtc
-
-namespace webrtc {
-
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
-class [[deprecated]] MockAsyncResolverFactory : public AsyncResolverFactory {
- public:
- MOCK_METHOD(rtc::AsyncResolverInterface*, Create, (), (override));
-};
-#pragma clang diagnostic pop
-} // namespace webrtc
-
-#endif // P2P_BASE_MOCK_ASYNC_RESOLVER_H_
diff --git a/third_party/libwebrtc/p2p/base/p2p_transport_channel.cc b/third_party/libwebrtc/p2p/base/p2p_transport_channel.cc
index 5ddab77de3..0c869ff622 100644
--- a/third_party/libwebrtc/p2p/base/p2p_transport_channel.cc
+++ b/third_party/libwebrtc/p2p/base/p2p_transport_channel.cc
@@ -28,7 +28,6 @@
#include "api/field_trials_view.h"
#include "api/units/time_delta.h"
#include "logging/rtc_event_log/ice_logger.h"
-#include "p2p/base/basic_async_resolver_factory.h"
#include "p2p/base/basic_ice_controller.h"
#include "p2p/base/connection.h"
#include "p2p/base/connection_info.h"
@@ -117,25 +116,11 @@ std::unique_ptr<P2PTransportChannel> P2PTransportChannel::Create(
absl::string_view transport_name,
int component,
webrtc::IceTransportInit init) {
- // TODO(bugs.webrtc.org/12598): Remove pragma and fallback once
- // async_resolver_factory is gone
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
- if (init.async_resolver_factory()) {
- return absl::WrapUnique(new P2PTransportChannel(
- transport_name, component, init.port_allocator(), nullptr,
- std::make_unique<webrtc::WrappingAsyncDnsResolverFactory>(
- init.async_resolver_factory()),
- init.event_log(), init.ice_controller_factory(),
- init.active_ice_controller_factory(), init.field_trials()));
-#pragma clang diagnostic pop
- } else {
- return absl::WrapUnique(new P2PTransportChannel(
- transport_name, component, init.port_allocator(),
- init.async_dns_resolver_factory(), nullptr, init.event_log(),
- init.ice_controller_factory(), init.active_ice_controller_factory(),
- init.field_trials()));
- }
+ return absl::WrapUnique(new P2PTransportChannel(
+ transport_name, component, init.port_allocator(),
+ init.async_dns_resolver_factory(), nullptr, init.event_log(),
+ init.ice_controller_factory(), init.active_ice_controller_factory(),
+ init.field_trials()));
}
P2PTransportChannel::P2PTransportChannel(
@@ -884,7 +869,7 @@ void P2PTransportChannel::MaybeStartGathering() {
ice_parameters_.ufrag, ice_parameters_.pwd)) {
if (gathering_state_ != kIceGatheringGathering) {
gathering_state_ = kIceGatheringGathering;
- SignalGatheringState(this);
+ SendGatheringStateEvent();
}
if (!allocator_sessions_.empty()) {
@@ -996,7 +981,9 @@ void P2PTransportChannel::OnCandidateError(
PortAllocatorSession* session,
const IceCandidateErrorEvent& event) {
RTC_DCHECK(network_thread_ == rtc::Thread::Current());
- SignalCandidateError(this, event);
+ if (candidate_error_callback_) {
+ candidate_error_callback_(this, event);
+ }
}
void P2PTransportChannel::OnCandidatesAllocationDone(
@@ -1012,7 +999,7 @@ void P2PTransportChannel::OnCandidatesAllocationDone(
gathering_state_ = kIceGatheringComplete;
RTC_LOG(LS_INFO) << "P2PTransportChannel: " << transport_name()
<< ", component " << component() << " gathering complete";
- SignalGatheringState(this);
+ SendGatheringStateEvent();
}
// Handle stun packets
@@ -1869,8 +1856,9 @@ void P2PTransportChannel::SwitchSelectedConnectionInternal(
} else {
pair_change.estimated_disconnected_time_ms = 0;
}
-
- SignalCandidatePairChanged(pair_change);
+ if (candidate_pair_change_callback_) {
+ candidate_pair_change_callback_(pair_change);
+ }
}
++selected_candidate_pair_changes_;
@@ -2200,7 +2188,9 @@ void P2PTransportChannel::OnCandidatesRemoved(
candidate.set_transport_name(transport_name());
candidates_to_remove.push_back(candidate);
}
- SignalCandidatesRemoved(this, candidates_to_remove);
+ if (candidates_removed_callback_) {
+ candidates_removed_callback_(this, candidates_to_remove);
+ }
}
void P2PTransportChannel::PruneAllPorts() {
diff --git a/third_party/libwebrtc/p2p/base/p2p_transport_channel.h b/third_party/libwebrtc/p2p/base/p2p_transport_channel.h
index 7f85018cd8..84325b8bef 100644
--- a/third_party/libwebrtc/p2p/base/p2p_transport_channel.h
+++ b/third_party/libwebrtc/p2p/base/p2p_transport_channel.h
@@ -36,7 +36,6 @@
#include "absl/types/optional.h"
#include "api/array_view.h"
#include "api/async_dns_resolver.h"
-#include "api/async_resolver_factory.h"
#include "api/candidate.h"
#include "api/ice_transport_interface.h"
#include "api/rtc_error.h"
@@ -47,7 +46,6 @@
#include "logging/rtc_event_log/events/rtc_event_ice_candidate_pair_config.h"
#include "logging/rtc_event_log/ice_logger.h"
#include "p2p/base/active_ice_controller_factory_interface.h"
-#include "p2p/base/basic_async_resolver_factory.h"
#include "p2p/base/candidate_pair_interface.h"
#include "p2p/base/connection.h"
#include "p2p/base/ice_agent_interface.h"
diff --git a/third_party/libwebrtc/p2p/base/p2p_transport_channel_unittest.cc b/third_party/libwebrtc/p2p/base/p2p_transport_channel_unittest.cc
index e414e3f558..44b1bfc5e3 100644
--- a/third_party/libwebrtc/p2p/base/p2p_transport_channel_unittest.cc
+++ b/third_party/libwebrtc/p2p/base/p2p_transport_channel_unittest.cc
@@ -26,7 +26,6 @@
#include "p2p/base/fake_port_allocator.h"
#include "p2p/base/ice_transport_internal.h"
#include "p2p/base/mock_active_ice_controller.h"
-#include "p2p/base/mock_async_resolver.h"
#include "p2p/base/mock_ice_controller.h"
#include "p2p/base/packet_transport_internal.h"
#include "p2p/base/test_stun_server.h"
@@ -45,11 +44,13 @@
#include "rtc_base/mdns_responder_interface.h"
#include "rtc_base/nat_server.h"
#include "rtc_base/nat_socket_factory.h"
+#include "rtc_base/network/received_packet.h"
#include "rtc_base/proxy_server.h"
#include "rtc_base/socket_address.h"
#include "rtc_base/ssl_adapter.h"
#include "rtc_base/strings/string_builder.h"
#include "rtc_base/thread.h"
+#include "rtc_base/time_utils.h"
#include "rtc_base/virtual_socket_server.h"
#include "system_wrappers/include/metrics.h"
#include "test/scoped_key_value_config.h"
@@ -283,7 +284,7 @@ class P2PTransportChannelTestBase : public ::testing::Test,
ss_(new rtc::FirewallSocketServer(nss_.get())),
socket_factory_(new rtc::BasicPacketSocketFactory(ss_.get())),
main_(ss_.get()),
- stun_server_(TestStunServer::Create(ss_.get(), kStunAddr)),
+ stun_server_(TestStunServer::Create(ss_.get(), kStunAddr, main_)),
turn_server_(&main_, ss_.get(), kTurnUdpIntAddr, kTurnUdpExtAddr),
socks_server1_(ss_.get(),
kSocksProxyAddrs[0],
@@ -486,8 +487,10 @@ class P2PTransportChannelTestBase : public ::testing::Test,
this, &P2PTransportChannelTestBase::OnReadyToSend);
channel->SignalCandidateGathered.connect(
this, &P2PTransportChannelTestBase::OnCandidateGathered);
- channel->SignalCandidatesRemoved.connect(
- this, &P2PTransportChannelTestBase::OnCandidatesRemoved);
+ channel->SetCandidatesRemovedCallback(
+ [this](IceTransportInternal* transport, const Candidates& candidates) {
+ OnCandidatesRemoved(transport, candidates);
+ });
channel->SignalReadPacket.connect(
this, &P2PTransportChannelTestBase::OnReadPacket);
channel->SignalRoleConflict.connect(
@@ -1023,7 +1026,7 @@ class P2PTransportChannelTestBase : public ::testing::Test,
rtc::AutoSocketServerThread main_;
rtc::scoped_refptr<PendingTaskSafetyFlag> safety_ =
PendingTaskSafetyFlag::Create();
- std::unique_ptr<TestStunServer> stun_server_;
+ TestStunServer::StunServerPtr stun_server_;
TestTurnServer turn_server_;
rtc::SocksProxyServer socks_server1_;
rtc::SocksProxyServer socks_server2_;
@@ -3479,8 +3482,10 @@ class P2PTransportChannelPingTest : public ::testing::Test,
&P2PTransportChannelPingTest::OnReadyToSend);
ch->SignalStateChanged.connect(
this, &P2PTransportChannelPingTest::OnChannelStateChanged);
- ch->SignalCandidatePairChanged.connect(
- this, &P2PTransportChannelPingTest::OnCandidatePairChanged);
+ ch->SetCandidatePairChangeCallback(
+ [this](const cricket::CandidatePairChangeEvent& event) {
+ OnCandidatePairChanged(event);
+ });
}
Connection* WaitForConnectionTo(
@@ -3595,7 +3600,8 @@ class P2PTransportChannelPingTest : public ::testing::Test,
msg.AddFingerprint();
rtc::ByteBufferWriter buf;
msg.Write(&buf);
- conn->OnReadPacket(buf.Data(), buf.Length(), rtc::TimeMicros());
+ conn->OnReadPacket(rtc::ReceivedPacket::CreateFromLegacy(
+ buf.Data(), buf.Length(), rtc::TimeMicros()));
}
void ReceivePingOnConnection(Connection* conn,
@@ -4060,7 +4066,9 @@ TEST_F(P2PTransportChannelPingTest, TestReceivingStateChange) {
clock.AdvanceTime(webrtc::TimeDelta::Seconds(1));
conn1->ReceivedPing();
- conn1->OnReadPacket("ABC", 3, rtc::TimeMicros());
+ conn1->OnReadPacket(
+ rtc::ReceivedPacket::CreateFromLegacy("ABC", 3, rtc::TimeMicros()));
+
EXPECT_TRUE_SIMULATED_WAIT(ch.receiving(), kShortTimeout, clock);
EXPECT_TRUE_SIMULATED_WAIT(!ch.receiving(), kShortTimeout, clock);
}
@@ -4370,7 +4378,8 @@ TEST_F(P2PTransportChannelPingTest, TestSelectConnectionBasedOnMediaReceived) {
Connection* conn2 = WaitForConnectionTo(&ch, "2.2.2.2", 2);
ASSERT_TRUE(conn2 != nullptr);
conn2->ReceivedPingResponse(LOW_RTT, "id"); // Become writable and receiving.
- conn2->OnReadPacket("ABC", 3, rtc::TimeMicros());
+ conn2->OnReadPacket(
+ rtc::ReceivedPacket::CreateFromLegacy("ABC", 3, rtc::TimeMicros()));
EXPECT_EQ(conn2, ch.selected_connection());
conn2->ReceivedPingResponse(LOW_RTT, "id"); // Become writable.
@@ -4397,7 +4406,8 @@ TEST_F(P2PTransportChannelPingTest, TestSelectConnectionBasedOnMediaReceived) {
// selected connection was nominated by the controlling side.
conn2->ReceivedPing();
conn2->ReceivedPingResponse(LOW_RTT, "id");
- conn2->OnReadPacket("XYZ", 3, rtc::TimeMicros());
+ conn2->OnReadPacket(
+ rtc::ReceivedPacket::CreateFromLegacy("XYZ", 3, rtc::TimeMicros()));
EXPECT_EQ_WAIT(conn3, ch.selected_connection(), kDefaultTimeout);
}
@@ -4427,12 +4437,15 @@ TEST_F(P2PTransportChannelPingTest,
// Advance the clock by 1ms so that the last data receiving timestamp of
// conn2 is larger.
SIMULATED_WAIT(false, 1, clock);
- conn2->OnReadPacket("XYZ", 3, rtc::TimeMicros());
+
+ conn2->OnReadPacket(
+ rtc::ReceivedPacket::CreateFromLegacy("XYZ", 3, rtc::TimeMicros()));
EXPECT_EQ(1, reset_selected_candidate_pair_switches());
EXPECT_TRUE(CandidatePairMatchesNetworkRoute(conn2));
// conn1 also receives data; it becomes selected due to priority again.
- conn1->OnReadPacket("XYZ", 3, rtc::TimeMicros());
+ conn1->OnReadPacket(
+ rtc::ReceivedPacket::CreateFromLegacy("ABC", 3, rtc::TimeMicros()));
EXPECT_EQ(1, reset_selected_candidate_pair_switches());
EXPECT_TRUE(CandidatePairMatchesNetworkRoute(conn2));
@@ -4441,7 +4454,8 @@ TEST_F(P2PTransportChannelPingTest,
SIMULATED_WAIT(false, 1, clock);
// Need to become writable again because it was pruned.
conn2->ReceivedPingResponse(LOW_RTT, "id");
- conn2->OnReadPacket("XYZ", 3, rtc::TimeMicros());
+ conn2->OnReadPacket(
+ rtc::ReceivedPacket::CreateFromLegacy("ABC", 3, rtc::TimeMicros()));
EXPECT_EQ(1, reset_selected_candidate_pair_switches());
EXPECT_TRUE(CandidatePairMatchesNetworkRoute(conn2));
@@ -4472,7 +4486,9 @@ TEST_F(P2PTransportChannelPingTest,
// conn1 received data; it is the selected connection.
// Advance the clock to have a non-zero last-data-receiving time.
SIMULATED_WAIT(false, 1, clock);
- conn1->OnReadPacket("XYZ", 3, rtc::TimeMicros());
+
+ conn1->OnReadPacket(
+ rtc::ReceivedPacket::CreateFromLegacy("XYZ", 3, rtc::TimeMicros()));
EXPECT_EQ(1, reset_selected_candidate_pair_switches());
EXPECT_TRUE(CandidatePairMatchesNetworkRoute(conn1));
@@ -4572,7 +4588,8 @@ TEST_F(P2PTransportChannelPingTest, TestEstimatedDisconnectedTime) {
{
clock.AdvanceTime(webrtc::TimeDelta::Seconds(1));
// This will not parse as STUN, and is considered data
- conn1->OnReadPacket("XYZ", 3, rtc::TimeMicros());
+ conn1->OnReadPacket(
+ rtc::ReceivedPacket::CreateFromLegacy("XYZ", 3, rtc::TimeMicros()));
clock.AdvanceTime(webrtc::TimeDelta::Seconds(2));
// conn2 is nominated; it becomes selected.
@@ -4584,8 +4601,8 @@ TEST_F(P2PTransportChannelPingTest, TestEstimatedDisconnectedTime) {
{
clock.AdvanceTime(webrtc::TimeDelta::Seconds(1));
- conn2->OnReadPacket("XYZ", 3, rtc::TimeMicros());
-
+ conn2->OnReadPacket(
+ rtc::ReceivedPacket::CreateFromLegacy("XYZ", 3, rtc::TimeMicros()));
clock.AdvanceTime(webrtc::TimeDelta::Seconds(2));
ReceivePingOnConnection(conn2, kIceUfrag[1], 1, nomination++);
@@ -4758,7 +4775,8 @@ TEST_F(P2PTransportChannelPingTest, TestDontPruneHighPriorityConnections) {
// conn2.
NominateConnection(conn1);
SIMULATED_WAIT(false, 1, clock);
- conn1->OnReadPacket("XYZ", 3, rtc::TimeMicros());
+ conn1->OnReadPacket(
+ rtc::ReceivedPacket::CreateFromLegacy("XYZ", 3, rtc::TimeMicros()));
SIMULATED_WAIT(conn2->pruned(), 100, clock);
EXPECT_FALSE(conn2->pruned());
}
diff --git a/third_party/libwebrtc/p2p/base/port.cc b/third_party/libwebrtc/p2p/base/port.cc
index afd998c3ab..3069799f7b 100644
--- a/third_party/libwebrtc/p2p/base/port.cc
+++ b/third_party/libwebrtc/p2p/base/port.cc
@@ -13,6 +13,7 @@
#include <math.h>
#include <algorithm>
+#include <cstddef>
#include <memory>
#include <utility>
#include <vector>
@@ -31,6 +32,7 @@
#include "rtc_base/message_digest.h"
#include "rtc_base/network.h"
#include "rtc_base/numerics/safe_minmax.h"
+#include "rtc_base/socket_address.h"
#include "rtc_base/string_encode.h"
#include "rtc_base/string_utils.h"
#include "rtc_base/strings/string_builder.h"
@@ -359,10 +361,10 @@ void Port::AddOrReplaceConnection(Connection* conn) {
}
}
-void Port::OnReadPacket(const char* data,
- size_t size,
- const rtc::SocketAddress& addr,
- ProtocolType proto) {
+void Port::OnReadPacket(const rtc::ReceivedPacket& packet, ProtocolType proto) {
+ const char* data = reinterpret_cast<const char*>(packet.payload().data());
+ size_t size = packet.payload().size();
+ const rtc::SocketAddress& addr = packet.source_address();
// If the user has enabled port packets, just hand this over.
if (enable_port_packets_) {
SignalReadPacket(this, data, size, addr);
@@ -452,7 +454,8 @@ bool Port::GetStunMessage(const char* data,
// Parse the request message. If the packet is not a complete and correct
// STUN message, then ignore it.
std::unique_ptr<IceMessage> stun_msg(new IceMessage());
- rtc::ByteBufferReader buf(data, size);
+ rtc::ByteBufferReader buf(
+ rtc::MakeArrayView(reinterpret_cast<const uint8_t*>(data), size));
if (!stun_msg->Read(&buf) || (buf.Length() > 0)) {
return false;
}
@@ -725,10 +728,7 @@ std::string Port::CreateStunUsername(absl::string_view remote_username) const {
}
bool Port::HandleIncomingPacket(rtc::AsyncPacketSocket* socket,
- const char* data,
- size_t size,
- const rtc::SocketAddress& remote_addr,
- int64_t packet_time_us) {
+ const rtc::ReceivedPacket& packet) {
RTC_DCHECK_NOTREACHED();
return false;
}
diff --git a/third_party/libwebrtc/p2p/base/port.h b/third_party/libwebrtc/p2p/base/port.h
index 78a4f91a93..796e1e1d5b 100644
--- a/third_party/libwebrtc/p2p/base/port.h
+++ b/third_party/libwebrtc/p2p/base/port.h
@@ -43,6 +43,7 @@
#include "rtc_base/memory/always_valid_pointer.h"
#include "rtc_base/net_helper.h"
#include "rtc_base/network.h"
+#include "rtc_base/network/received_packet.h"
#include "rtc_base/proxy_info.h"
#include "rtc_base/rate_tracker.h"
#include "rtc_base/socket_address.h"
@@ -313,10 +314,7 @@ class RTC_EXPORT Port : public PortInterface, public sigslot::has_slots<> {
// port implemented this method.
// TODO(mallinath) - Make it pure virtual.
virtual bool HandleIncomingPacket(rtc::AsyncPacketSocket* socket,
- const char* data,
- size_t size,
- const rtc::SocketAddress& remote_addr,
- int64_t packet_time_us);
+ const rtc::ReceivedPacket& packet);
// Shall the port handle packet from this `remote_addr`.
// This method is overridden by TurnPort.
@@ -422,10 +420,19 @@ class RTC_EXPORT Port : public PortInterface, public sigslot::has_slots<> {
// Called when a packet is received from an unknown address that is not
// currently a connection. If this is an authenticated STUN binding request,
// then we will signal the client.
- void OnReadPacket(const char* data,
- size_t size,
- const rtc::SocketAddress& addr,
- ProtocolType proto);
+ void OnReadPacket(const rtc::ReceivedPacket& packet, ProtocolType proto);
+
+ [[deprecated(
+ "Use OnReadPacket(const rtc::ReceivedPacket& packet, ProtocolType "
+ "proto)")]] void
+ OnReadPacket(const char* data,
+ size_t size,
+ const rtc::SocketAddress& addr,
+ ProtocolType proto) {
+ OnReadPacket(rtc::ReceivedPacket::CreateFromLegacy(
+ data, size, /*packet_time_us = */ -1, addr),
+ proto);
+ }
// If the given data comprises a complete and correct STUN message then the
// return value is true, otherwise false. If the message username corresponds
diff --git a/third_party/libwebrtc/p2p/base/port_unittest.cc b/third_party/libwebrtc/p2p/base/port_unittest.cc
index b27afe2f39..96c1bd5ee1 100644
--- a/third_party/libwebrtc/p2p/base/port_unittest.cc
+++ b/third_party/libwebrtc/p2p/base/port_unittest.cc
@@ -23,6 +23,7 @@
#include "absl/memory/memory.h"
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
+#include "api/array_view.h"
#include "api/candidate.h"
#include "api/packet_socket_factory.h"
#include "api/transport/stun.h"
@@ -55,6 +56,7 @@
#include "rtc_base/nat_types.h"
#include "rtc_base/net_helper.h"
#include "rtc_base/network.h"
+#include "rtc_base/network/received_packet.h"
#include "rtc_base/network/sent_packet.h"
#include "rtc_base/network_constants.h"
#include "rtc_base/proxy_info.h"
@@ -162,8 +164,11 @@ class TestPort : public Port {
using cricket::Port::GetStunMessage;
// The last StunMessage that was sent on this Port.
- // TODO(?): Make these const; requires changes to SendXXXXResponse.
- rtc::BufferT<uint8_t>* last_stun_buf() { return last_stun_buf_.get(); }
+ rtc::ArrayView<const uint8_t> last_stun_buf() {
+ if (!last_stun_buf_)
+ return rtc::ArrayView<const uint8_t>();
+ return *last_stun_buf_;
+ }
IceMessage* last_stun_msg() { return last_stun_msg_.get(); }
int last_stun_error_code() {
int code = 0;
@@ -260,14 +265,15 @@ static void SendPingAndReceiveResponse(Connection* lconn,
int64_t ms) {
lconn->Ping(rtc::TimeMillis());
ASSERT_TRUE_WAIT(lport->last_stun_msg(), kDefaultTimeout);
- ASSERT_TRUE(lport->last_stun_buf());
- rconn->OnReadPacket(lport->last_stun_buf()->data<char>(),
- lport->last_stun_buf()->size(), /* packet_time_us */ -1);
+ ASSERT_GT(lport->last_stun_buf().size(), 0u);
+ rconn->OnReadPacket(rtc::ReceivedPacket(lport->last_stun_buf(),
+ rtc::SocketAddress(), absl::nullopt));
+
clock->AdvanceTime(webrtc::TimeDelta::Millis(ms));
ASSERT_TRUE_WAIT(rport->last_stun_msg(), kDefaultTimeout);
- ASSERT_TRUE(rport->last_stun_buf());
- lconn->OnReadPacket(rport->last_stun_buf()->data<char>(),
- rport->last_stun_buf()->size(), /* packet_time_us */ -1);
+ ASSERT_GT(rport->last_stun_buf().size(), 0u);
+ lconn->OnReadPacket(rtc::ReceivedPacket(rport->last_stun_buf(),
+ rtc::SocketAddress(), absl::nullopt));
}
class TestChannel : public sigslot::has_slots<> {
@@ -415,7 +421,7 @@ class PortTest : public ::testing::Test, public sigslot::has_slots<> {
nat_factory2_(ss_.get(), kNatAddr2, SocketAddress()),
nat_socket_factory1_(&nat_factory1_),
nat_socket_factory2_(&nat_factory2_),
- stun_server_(TestStunServer::Create(ss_.get(), kStunAddr)),
+ stun_server_(TestStunServer::Create(ss_.get(), kStunAddr, main_)),
turn_server_(&main_, ss_.get(), kTurnUdpIntAddr, kTurnUdpExtAddr),
username_(rtc::CreateRandomString(ICE_UFRAG_LENGTH)),
password_(rtc::CreateRandomString(ICE_PWD_LENGTH)),
@@ -867,7 +873,7 @@ class PortTest : public ::testing::Test, public sigslot::has_slots<> {
rtc::NATSocketFactory nat_factory2_;
rtc::BasicPacketSocketFactory nat_socket_factory1_;
rtc::BasicPacketSocketFactory nat_socket_factory2_;
- std::unique_ptr<TestStunServer> stun_server_;
+ TestStunServer::StunServerPtr stun_server_;
TestTurnServer turn_server_;
std::string username_;
std::string password_;
@@ -1488,8 +1494,8 @@ TEST_F(PortTest, TestLoopbackCall) {
ASSERT_TRUE_WAIT(lport->last_stun_msg() != NULL, kDefaultTimeout);
IceMessage* msg = lport->last_stun_msg();
EXPECT_EQ(STUN_BINDING_REQUEST, msg->type());
- conn->OnReadPacket(lport->last_stun_buf()->data<char>(),
- lport->last_stun_buf()->size(), /* packet_time_us */ -1);
+ conn->OnReadPacket(rtc::ReceivedPacket(lport->last_stun_buf(),
+ rtc::SocketAddress(), absl::nullopt));
ASSERT_TRUE_WAIT(lport->last_stun_msg() != NULL, kDefaultTimeout);
msg = lport->last_stun_msg();
EXPECT_EQ(STUN_BINDING_RESPONSE, msg->type());
@@ -1522,7 +1528,8 @@ TEST_F(PortTest, TestLoopbackCall) {
lport->Reset();
auto buf = std::make_unique<ByteBufferWriter>();
WriteStunMessage(*modified_req, buf.get());
- conn1->OnReadPacket(buf->Data(), buf->Length(), /* packet_time_us */ -1);
+ conn1->OnReadPacket(rtc::ReceivedPacket::CreateFromLegacy(
+ buf->Data(), buf->Length(), /*packet_time_us=*/-1));
ASSERT_TRUE_WAIT(lport->last_stun_msg() != NULL, kDefaultTimeout);
msg = lport->last_stun_msg();
EXPECT_EQ(STUN_BINDING_ERROR_RESPONSE, msg->type());
@@ -1555,8 +1562,8 @@ TEST_F(PortTest, TestIceRoleConflict) {
IceMessage* msg = rport->last_stun_msg();
EXPECT_EQ(STUN_BINDING_REQUEST, msg->type());
// Send rport binding request to lport.
- lconn->OnReadPacket(rport->last_stun_buf()->data<char>(),
- rport->last_stun_buf()->size(), /* packet_time_us */ -1);
+ lconn->OnReadPacket(rtc::ReceivedPacket(rport->last_stun_buf(),
+ rtc::SocketAddress(), absl::nullopt));
ASSERT_TRUE_WAIT(lport->last_stun_msg() != NULL, kDefaultTimeout);
EXPECT_EQ(STUN_BINDING_RESPONSE, lport->last_stun_msg()->type());
@@ -1878,7 +1885,8 @@ TEST_F(PortTest, TestSendStunMessage) {
EXPECT_TRUE(msg->GetByteString(STUN_ATTR_USE_CANDIDATE) != NULL);
EXPECT_TRUE(msg->GetUInt32(STUN_ATTR_FINGERPRINT) != NULL);
EXPECT_TRUE(StunMessage::ValidateFingerprint(
- lport->last_stun_buf()->data<char>(), lport->last_stun_buf()->size()));
+ reinterpret_cast<const char*>(lport->last_stun_buf().data()),
+ lport->last_stun_buf().size()));
// Request should not include ping count.
ASSERT_TRUE(msg->GetUInt32(STUN_ATTR_RETRANSMIT_COUNT) == NULL);
@@ -1887,14 +1895,15 @@ TEST_F(PortTest, TestSendStunMessage) {
std::unique_ptr<IceMessage> request = CopyStunMessage(*msg);
// Receive the BINDING-REQUEST and respond with BINDING-RESPONSE.
- rconn->OnReadPacket(lport->last_stun_buf()->data<char>(),
- lport->last_stun_buf()->size(), /* packet_time_us */ -1);
+ rconn->OnReadPacket(rtc::ReceivedPacket(lport->last_stun_buf(),
+ rtc::SocketAddress(), absl::nullopt));
msg = rport->last_stun_msg();
ASSERT_TRUE(msg != NULL);
EXPECT_EQ(STUN_BINDING_RESPONSE, msg->type());
// Received a BINDING-RESPONSE.
- lconn->OnReadPacket(rport->last_stun_buf()->data<char>(),
- rport->last_stun_buf()->size(), /* packet_time_us */ -1);
+ lconn->OnReadPacket(rtc::ReceivedPacket(rport->last_stun_buf(),
+ rtc::SocketAddress(), absl::nullopt));
+
// Verify the STUN Stats.
EXPECT_EQ(1U, lconn->stats().sent_ping_requests_total);
EXPECT_EQ(1U, lconn->stats().sent_ping_requests_before_first_response);
@@ -1912,7 +1921,8 @@ TEST_F(PortTest, TestSendStunMessage) {
msg->ValidateMessageIntegrity("rpass"));
EXPECT_TRUE(msg->GetUInt32(STUN_ATTR_FINGERPRINT) != NULL);
EXPECT_TRUE(StunMessage::ValidateFingerprint(
- lport->last_stun_buf()->data<char>(), lport->last_stun_buf()->size()));
+ reinterpret_cast<const char*>(lport->last_stun_buf().data()),
+ lport->last_stun_buf().size()));
// No USERNAME or PRIORITY in ICE responses.
EXPECT_TRUE(msg->GetByteString(STUN_ATTR_USERNAME) == NULL);
EXPECT_TRUE(msg->GetByteString(STUN_ATTR_PRIORITY) == NULL);
@@ -1942,7 +1952,8 @@ TEST_F(PortTest, TestSendStunMessage) {
msg->ValidateMessageIntegrity("rpass"));
EXPECT_TRUE(msg->GetUInt32(STUN_ATTR_FINGERPRINT) != NULL);
EXPECT_TRUE(StunMessage::ValidateFingerprint(
- lport->last_stun_buf()->data<char>(), lport->last_stun_buf()->size()));
+ reinterpret_cast<const char*>(lport->last_stun_buf().data()),
+ lport->last_stun_buf().size()));
// No USERNAME with ICE.
EXPECT_TRUE(msg->GetByteString(STUN_ATTR_USERNAME) == NULL);
EXPECT_TRUE(msg->GetByteString(STUN_ATTR_PRIORITY) == NULL);
@@ -1971,12 +1982,12 @@ TEST_F(PortTest, TestSendStunMessage) {
// Respond with a BINDING-RESPONSE.
request = CopyStunMessage(*msg);
- lconn->OnReadPacket(rport->last_stun_buf()->data<char>(),
- rport->last_stun_buf()->size(), /* packet_time_us */ -1);
+ lconn->OnReadPacket(rtc::ReceivedPacket(rport->last_stun_buf(),
+ rtc::SocketAddress(), absl::nullopt));
msg = lport->last_stun_msg();
// Receive the BINDING-RESPONSE.
- rconn->OnReadPacket(lport->last_stun_buf()->data<char>(),
- lport->last_stun_buf()->size(), /* packet_time_us */ -1);
+ rconn->OnReadPacket(rtc::ReceivedPacket(lport->last_stun_buf(),
+ rtc::SocketAddress(), absl::nullopt));
// Verify the Stun ping stats.
EXPECT_EQ(3U, rconn->stats().sent_ping_requests_total);
@@ -2026,9 +2037,10 @@ TEST_F(PortTest, TestNomination) {
// should set the remote nomination of `rconn`.
lconn->Ping(0);
ASSERT_TRUE_WAIT(lport->last_stun_msg(), kDefaultTimeout);
- ASSERT_TRUE(lport->last_stun_buf());
- rconn->OnReadPacket(lport->last_stun_buf()->data<char>(),
- lport->last_stun_buf()->size(), /* packet_time_us */ -1);
+ ASSERT_GT(lport->last_stun_buf().size(), 0u);
+ rconn->OnReadPacket(rtc::ReceivedPacket(lport->last_stun_buf(),
+ rtc::SocketAddress(), absl::nullopt));
+
EXPECT_EQ(nomination, rconn->remote_nomination());
EXPECT_FALSE(lconn->nominated());
EXPECT_TRUE(rconn->nominated());
@@ -2038,9 +2050,10 @@ TEST_F(PortTest, TestNomination) {
// This should result in an acknowledgment sent back from `rconn` to `lconn`,
// updating the acknowledged nomination of `lconn`.
ASSERT_TRUE_WAIT(rport->last_stun_msg(), kDefaultTimeout);
- ASSERT_TRUE(rport->last_stun_buf());
- lconn->OnReadPacket(rport->last_stun_buf()->data<char>(),
- rport->last_stun_buf()->size(), /* packet_time_us */ -1);
+ ASSERT_GT(rport->last_stun_buf().size(), 0u);
+ lconn->OnReadPacket(rtc::ReceivedPacket(rport->last_stun_buf(),
+ rtc::SocketAddress(), absl::nullopt));
+
EXPECT_EQ(nomination, lconn->acked_nomination());
EXPECT_TRUE(lconn->nominated());
EXPECT_TRUE(rconn->nominated());
@@ -2166,8 +2179,9 @@ TEST_F(PortTest, TestNetworkCostChange) {
IceMessage* msg = lport->last_stun_msg();
EXPECT_EQ(STUN_BINDING_REQUEST, msg->type());
// Pass the binding request to rport.
- rconn->OnReadPacket(lport->last_stun_buf()->data<char>(),
- lport->last_stun_buf()->size(), /* packet_time_us */ -1);
+ rconn->OnReadPacket(rtc::ReceivedPacket(lport->last_stun_buf(),
+ rtc::SocketAddress(), absl::nullopt));
+
// Wait until rport sends the response and then check the remote network cost.
ASSERT_TRUE_WAIT(rport->last_stun_msg() != NULL, kDefaultTimeout);
EXPECT_EQ(rtc::kNetworkCostHigh, rconn->remote_candidate().network_cost());
@@ -2496,8 +2510,8 @@ TEST_F(PortTest,
// Send request.
lconn->Ping(0);
ASSERT_TRUE_WAIT(lport->last_stun_msg() != NULL, kDefaultTimeout);
- rconn->OnReadPacket(lport->last_stun_buf()->data<char>(),
- lport->last_stun_buf()->size(), /* packet_time_us */ -1);
+ rconn->OnReadPacket(rtc::ReceivedPacket(lport->last_stun_buf(),
+ rtc::SocketAddress(), absl::nullopt));
// Intercept request and add comprehension required attribute.
ASSERT_TRUE_WAIT(rport->last_stun_msg() != NULL, kDefaultTimeout);
@@ -2507,7 +2521,8 @@ TEST_F(PortTest,
modified_response->AddFingerprint();
ByteBufferWriter buf;
WriteStunMessage(*modified_response, &buf);
- lconn->OnReadPacket(buf.Data(), buf.Length(), /* packet_time_us */ -1);
+ lconn->OnReadPacket(rtc::ReceivedPacket::CreateFromLegacy(
+ buf.Data(), buf.Length(), /*packet_time_us=*/-1));
// Response should have been ignored, leaving us unwritable still.
EXPECT_FALSE(lconn->writable());
}
@@ -2535,7 +2550,8 @@ TEST_F(PortTest,
in_msg->AddFingerprint();
ByteBufferWriter buf;
WriteStunMessage(*in_msg, &buf);
- lconn->OnReadPacket(buf.Data(), buf.Length(), /* packet_time_us */ -1);
+ lconn->OnReadPacket(rtc::ReceivedPacket::CreateFromLegacy(
+ buf.Data(), buf.Length(), /*packet_time_us=*/-1));
EXPECT_EQ(0u, lconn->last_ping_received());
}
@@ -2581,8 +2597,9 @@ TEST_F(PortTest, TestHandleStunBindingIndication) {
IceMessage* msg = rport->last_stun_msg();
EXPECT_EQ(STUN_BINDING_REQUEST, msg->type());
// Send rport binding request to lport.
- lconn->OnReadPacket(rport->last_stun_buf()->data<char>(),
- rport->last_stun_buf()->size(), /* packet_time_us */ -1);
+ lconn->OnReadPacket(rtc::ReceivedPacket(rport->last_stun_buf(),
+ rtc::SocketAddress(), absl::nullopt));
+
ASSERT_TRUE_WAIT(lport->last_stun_msg() != NULL, kDefaultTimeout);
EXPECT_EQ(STUN_BINDING_RESPONSE, lport->last_stun_msg()->type());
int64_t last_ping_received1 = lconn->last_ping_received();
@@ -2590,7 +2607,8 @@ TEST_F(PortTest, TestHandleStunBindingIndication) {
// Adding a delay of 100ms.
rtc::Thread::Current()->ProcessMessages(100);
// Pinging lconn using stun indication message.
- lconn->OnReadPacket(buf->Data(), buf->Length(), /* packet_time_us */ -1);
+ lconn->OnReadPacket(rtc::ReceivedPacket::CreateFromLegacy(
+ buf->Data(), buf->Length(), /*packet_time_us=*/-1));
int64_t last_ping_received2 = lconn->last_ping_received();
EXPECT_GT(last_ping_received2, last_ping_received1);
}
@@ -3087,9 +3105,9 @@ TEST_F(PortTest, TestIceLiteConnectivity) {
con->SendStunBindingResponse(request.get());
// Feeding the respone message from litemode to the full mode connection.
- ch1.conn()->OnReadPacket(ice_lite_port->last_stun_buf()->data<char>(),
- ice_lite_port->last_stun_buf()->size(),
- /* packet_time_us */ -1);
+ ch1.conn()->OnReadPacket(rtc::ReceivedPacket(
+ ice_lite_port->last_stun_buf(), rtc::SocketAddress(), absl::nullopt));
+
// Verifying full mode connection becomes writable from the response.
EXPECT_EQ_WAIT(Connection::STATE_WRITABLE, ch1.conn()->write_state(),
kDefaultTimeout);
@@ -3205,9 +3223,8 @@ TEST_P(GoogPingTest, TestGoogPingAnnounceEnable) {
GetSupportedGoogPingVersion(response) >= kGoogPingVersion);
// Feeding the respone message back.
- ch1.conn()->OnReadPacket(port2->last_stun_buf()->data<char>(),
- port2->last_stun_buf()->size(),
- /* packet_time_us */ -1);
+ ch1.conn()->OnReadPacket(rtc::ReceivedPacket(
+ port2->last_stun_buf(), rtc::SocketAddress(), absl::nullopt));
port1->Reset();
port2->Reset();
@@ -3390,7 +3407,8 @@ TEST_F(PortTest, TestGoogPingUnsupportedVersionInStunBindingResponse) {
modified_response->Write(&buf);
// Feeding the modified respone message back.
- ch1.conn()->OnReadPacket(buf.Data(), buf.Length(), /* packet_time_us */ -1);
+ ch1.conn()->OnReadPacket(rtc::ReceivedPacket::CreateFromLegacy(
+ buf.Data(), buf.Length(), /*packet_time_us=*/-1));
port1->Reset();
port2->Reset();
@@ -3462,9 +3480,8 @@ TEST_F(PortTest, TestChangeInAttributeMakesGoogPingFallsbackToStunBinding) {
ASSERT_TRUE(GetSupportedGoogPingVersion(response) >= kGoogPingVersion);
// Feeding the respone message back.
- ch1.conn()->OnReadPacket(port2->last_stun_buf()->data<char>(),
- port2->last_stun_buf()->size(),
- /* packet_time_us */ -1);
+ ch1.conn()->OnReadPacket(rtc::ReceivedPacket(
+ port2->last_stun_buf(), rtc::SocketAddress(), absl::nullopt));
port1->Reset();
port2->Reset();
@@ -3548,9 +3565,8 @@ TEST_F(PortTest, TestErrorResponseMakesGoogPingFallBackToStunBinding) {
ASSERT_TRUE(GetSupportedGoogPingVersion(response) >= kGoogPingVersion);
// Feeding the respone message back.
- ch1.conn()->OnReadPacket(port2->last_stun_buf()->data<char>(),
- port2->last_stun_buf()->size(),
- /* packet_time_us */ -1);
+ ch1.conn()->OnReadPacket(rtc::ReceivedPacket(
+ port2->last_stun_buf(), rtc::SocketAddress(), absl::nullopt));
port1->Reset();
port2->Reset();
@@ -3576,8 +3592,8 @@ TEST_F(PortTest, TestErrorResponseMakesGoogPingFallBackToStunBinding) {
rtc::ByteBufferWriter buf;
error_response.Write(&buf);
- ch1.conn()->OnReadPacket(buf.Data(), buf.Length(),
- /* packet_time_us */ -1);
+ ch1.conn()->OnReadPacket(rtc::ReceivedPacket::CreateFromLegacy(
+ buf.Data(), buf.Length(), /*packet_time_us=*/-1));
// And now the third ping...this should be a binding.
port1->Reset();
@@ -3813,14 +3829,14 @@ class ConnectionTest : public PortTest {
rconn->PortForTest() == rport_.get() ? rport_.get() : lport_.get();
lconn->Ping(rtc::TimeMillis());
ASSERT_TRUE_WAIT(lport->last_stun_msg(), kDefaultTimeout);
- ASSERT_TRUE(lport->last_stun_buf());
- rconn->OnReadPacket(lport->last_stun_buf()->data<char>(),
- lport->last_stun_buf()->size(),
- /* packet_time_us */ -1);
+ ASSERT_GT(lport->last_stun_buf().size(), 0u);
+ rconn->OnReadPacket(rtc::ReceivedPacket(
+ lport->last_stun_buf(), rtc::SocketAddress(), absl::nullopt));
+
clock_.AdvanceTime(webrtc::TimeDelta::Millis(ms));
ASSERT_TRUE_WAIT(rport->last_stun_msg(), kDefaultTimeout);
- ASSERT_TRUE(rport->last_stun_buf());
- *reply = std::move(*rport->last_stun_buf());
+ ASSERT_GT(rport->last_stun_buf().size(), 0u);
+ reply->SetData(rport->last_stun_buf());
}
void SendPingAndReceiveResponse(Connection* lconn,
@@ -3828,8 +3844,9 @@ class ConnectionTest : public PortTest {
int64_t ms) {
rtc::BufferT<uint8_t> reply;
SendPingAndCaptureReply(lconn, rconn, ms, &reply);
- lconn->OnReadPacket(reply.data<char>(), reply.size(),
- /* packet_time_us */ -1);
+
+ lconn->OnReadPacket(
+ rtc::ReceivedPacket(reply, rtc::SocketAddress(), absl::nullopt));
}
void OnConnectionStateChange(Connection* connection) { num_state_changes_++; }
@@ -3890,8 +3907,8 @@ TEST_F(ConnectionTest, ConnectionForgetLearnedStateDiscardsPendingPings) {
EXPECT_FALSE(lconn->writable());
EXPECT_FALSE(lconn->receiving());
- lconn->OnReadPacket(reply.data<char>(), reply.size(),
- /* packet_time_us */ -1);
+ lconn->OnReadPacket(
+ rtc::ReceivedPacket(reply, rtc::SocketAddress(), absl::nullopt));
// That reply was discarded due to the ForgetLearnedState() while it was
// outstanding.
@@ -3962,16 +3979,17 @@ TEST_F(ConnectionTest, SendReceiveGoogDelta) {
lconn->Ping(rtc::TimeMillis(), std::move(delta));
ASSERT_TRUE_WAIT(lport_->last_stun_msg(), kDefaultTimeout);
- ASSERT_TRUE(lport_->last_stun_buf());
- rconn->OnReadPacket(lport_->last_stun_buf()->data<char>(),
- lport_->last_stun_buf()->size(), /* packet_time_us */ -1);
+ ASSERT_GT(lport_->last_stun_buf().size(), 0u);
+ rconn->OnReadPacket(rtc::ReceivedPacket(lport_->last_stun_buf(),
+ rtc::SocketAddress(), absl::nullopt));
EXPECT_TRUE(received_goog_delta);
clock_.AdvanceTime(webrtc::TimeDelta::Millis(ms));
ASSERT_TRUE_WAIT(rport_->last_stun_msg(), kDefaultTimeout);
- ASSERT_TRUE(rport_->last_stun_buf());
- lconn->OnReadPacket(rport_->last_stun_buf()->data<char>(),
- rport_->last_stun_buf()->size(), /* packet_time_us */ -1);
+ ASSERT_GT(rport_->last_stun_buf().size(), 0u);
+ lconn->OnReadPacket(rtc::ReceivedPacket(rport_->last_stun_buf(),
+ rtc::SocketAddress(), absl::nullopt));
+
EXPECT_TRUE(received_goog_delta_ack);
}
@@ -3999,15 +4017,15 @@ TEST_F(ConnectionTest, SendGoogDeltaNoReply) {
lconn->Ping(rtc::TimeMillis(), std::move(delta));
ASSERT_TRUE_WAIT(lport_->last_stun_msg(), kDefaultTimeout);
- ASSERT_TRUE(lport_->last_stun_buf());
- rconn->OnReadPacket(lport_->last_stun_buf()->data<char>(),
- lport_->last_stun_buf()->size(), /* packet_time_us */ -1);
+ ASSERT_GT(lport_->last_stun_buf().size(), 0u);
+ rconn->OnReadPacket(rtc::ReceivedPacket(lport_->last_stun_buf(),
+ rtc::SocketAddress(), absl::nullopt));
clock_.AdvanceTime(webrtc::TimeDelta::Millis(ms));
ASSERT_TRUE_WAIT(rport_->last_stun_msg(), kDefaultTimeout);
- ASSERT_TRUE(rport_->last_stun_buf());
- lconn->OnReadPacket(rport_->last_stun_buf()->data<char>(),
- rport_->last_stun_buf()->size(), /* packet_time_us */ -1);
+ ASSERT_GT(rport_->last_stun_buf().size(), 0u);
+ lconn->OnReadPacket(rtc::ReceivedPacket(rport_->last_stun_buf(),
+ rtc::SocketAddress(), absl::nullopt));
EXPECT_TRUE(received_goog_delta_ack_error);
}
diff --git a/third_party/libwebrtc/p2p/base/pseudo_tcp.cc b/third_party/libwebrtc/p2p/base/pseudo_tcp.cc
index eff86e849e..5a5ce0392b 100644
--- a/third_party/libwebrtc/p2p/base/pseudo_tcp.cc
+++ b/third_party/libwebrtc/p2p/base/pseudo_tcp.cc
@@ -1191,7 +1191,8 @@ void PseudoTcp::parseOptions(const char* data, uint32_t len) {
// See http://www.freesoft.org/CIE/Course/Section4/8.htm for
// parsing the options list.
- rtc::ByteBufferReader buf(data, len);
+ rtc::ByteBufferReader buf(
+ rtc::MakeArrayView(reinterpret_cast<const uint8_t*>(data), len));
while (buf.Length()) {
uint8_t kind = TCP_OPT_EOL;
buf.ReadUInt8(&kind);
diff --git a/third_party/libwebrtc/p2p/base/stun_dictionary.cc b/third_party/libwebrtc/p2p/base/stun_dictionary.cc
index bf6a1e49c2..318bed0ad1 100644
--- a/third_party/libwebrtc/p2p/base/stun_dictionary.cc
+++ b/third_party/libwebrtc/p2p/base/stun_dictionary.cc
@@ -80,7 +80,7 @@ const StunAttribute* StunDictionaryView::GetOrNull(
webrtc::RTCErrorOr<
std::pair<uint64_t, std::deque<std::unique_ptr<StunAttribute>>>>
StunDictionaryView::ParseDelta(const StunByteStringAttribute& delta) {
- rtc::ByteBufferReader buf(delta.bytes(), delta.length());
+ rtc::ByteBufferReader buf(delta.array_view());
uint16_t magic;
if (!buf.ReadUInt16(&magic)) {
return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER,
diff --git a/third_party/libwebrtc/p2p/base/stun_port.cc b/third_party/libwebrtc/p2p/base/stun_port.cc
index 3b024bdfea..7acaac6dbe 100644
--- a/third_party/libwebrtc/p2p/base/stun_port.cc
+++ b/third_party/libwebrtc/p2p/base/stun_port.cc
@@ -19,12 +19,12 @@
#include "p2p/base/connection.h"
#include "p2p/base/p2p_constants.h"
#include "p2p/base/port_allocator.h"
-#include "rtc_base/async_resolver_interface.h"
#include "rtc_base/checks.h"
#include "rtc_base/experiments/field_trial_parser.h"
#include "rtc_base/helpers.h"
#include "rtc_base/ip_address.h"
#include "rtc_base/logging.h"
+#include "rtc_base/network/received_packet.h"
#include "rtc_base/strings/string_builder.h"
namespace cricket {
@@ -46,7 +46,9 @@ class StunBindingRequest : public StunRequest {
std::make_unique<StunMessage>(STUN_BINDING_REQUEST)),
port_(port),
server_addr_(addr),
- start_time_(start_time) {}
+ start_time_(start_time) {
+ SetAuthenticationRequired(false);
+ }
const rtc::SocketAddress& server_addr() const { return server_addr_; }
@@ -220,7 +222,10 @@ bool UDPPort::Init() {
RTC_LOG(LS_WARNING) << ToString() << ": UDP socket creation failed";
return false;
}
- socket_->SignalReadPacket.connect(this, &UDPPort::OnReadPacket);
+ socket_->RegisterReceivedPacketCallback(
+ [&](rtc::AsyncPacketSocket* socket, const rtc::ReceivedPacket& packet) {
+ OnReadPacket(socket, packet);
+ });
}
socket_->SignalSentPacket.connect(this, &UDPPort::OnSentPacket);
socket_->SignalReadyToSend.connect(this, &UDPPort::OnReadyToSend);
@@ -340,12 +345,9 @@ int UDPPort::GetError() {
}
bool UDPPort::HandleIncomingPacket(rtc::AsyncPacketSocket* socket,
- const char* data,
- size_t size,
- const rtc::SocketAddress& remote_addr,
- int64_t packet_time_us) {
+ const rtc::ReceivedPacket& packet) {
// All packets given to UDP port will be consumed.
- OnReadPacket(socket, data, size, remote_addr, packet_time_us);
+ OnReadPacket(socket, packet);
return true;
}
@@ -387,26 +389,26 @@ void UDPPort::PostAddAddress(bool is_final) {
}
void UDPPort::OnReadPacket(rtc::AsyncPacketSocket* socket,
- const char* data,
- size_t size,
- const rtc::SocketAddress& remote_addr,
- const int64_t& packet_time_us) {
+ const rtc::ReceivedPacket& packet) {
RTC_DCHECK(socket == socket_);
- RTC_DCHECK(!remote_addr.IsUnresolvedIP());
+ RTC_DCHECK(!packet.source_address().IsUnresolvedIP());
// Look for a response from the STUN server.
// Even if the response doesn't match one of our outstanding requests, we
// will eat it because it might be a response to a retransmitted packet, and
// we already cleared the request when we got the first response.
- if (server_addresses_.find(remote_addr) != server_addresses_.end()) {
- request_manager_.CheckResponse(data, size);
+ if (server_addresses_.find(packet.source_address()) !=
+ server_addresses_.end()) {
+ request_manager_.CheckResponse(
+ reinterpret_cast<const char*>(packet.payload().data()),
+ packet.payload().size());
return;
}
- if (Connection* conn = GetConnection(remote_addr)) {
- conn->OnReadPacket(data, size, packet_time_us);
+ if (Connection* conn = GetConnection(packet.source_address())) {
+ conn->OnReadPacket(packet);
} else {
- Port::OnReadPacket(data, size, remote_addr, PROTO_UDP);
+ Port::OnReadPacket(packet, PROTO_UDP);
}
}
diff --git a/third_party/libwebrtc/p2p/base/stun_port.h b/third_party/libwebrtc/p2p/base/stun_port.h
index 3df725eaf9..a28983b040 100644
--- a/third_party/libwebrtc/p2p/base/stun_port.h
+++ b/third_party/libwebrtc/p2p/base/stun_port.h
@@ -22,6 +22,7 @@
#include "p2p/base/port.h"
#include "p2p/base/stun_request.h"
#include "rtc_base/async_packet_socket.h"
+#include "rtc_base/network/received_packet.h"
#include "rtc_base/system/rtc_export.h"
namespace cricket {
@@ -97,10 +98,7 @@ class RTC_EXPORT UDPPort : public Port {
int GetError() override;
bool HandleIncomingPacket(rtc::AsyncPacketSocket* socket,
- const char* data,
- size_t size,
- const rtc::SocketAddress& remote_addr,
- int64_t packet_time_us) override;
+ const rtc::ReceivedPacket& packet) override;
bool SupportsProtocol(absl::string_view protocol) const override;
ProtocolType GetProtocol() const override;
@@ -158,10 +156,7 @@ class RTC_EXPORT UDPPort : public Port {
void PostAddAddress(bool is_final) override;
void OnReadPacket(rtc::AsyncPacketSocket* socket,
- const char* data,
- size_t size,
- const rtc::SocketAddress& remote_addr,
- const int64_t& packet_time_us);
+ const rtc::ReceivedPacket& packet);
void OnSentPacket(rtc::AsyncPacketSocket* socket,
const rtc::SentPacket& sent_packet) override;
diff --git a/third_party/libwebrtc/p2p/base/stun_port_unittest.cc b/third_party/libwebrtc/p2p/base/stun_port_unittest.cc
index bf51151536..04505d26ff 100644
--- a/third_party/libwebrtc/p2p/base/stun_port_unittest.cc
+++ b/third_party/libwebrtc/p2p/base/stun_port_unittest.cc
@@ -16,8 +16,10 @@
#include "p2p/base/basic_packet_socket_factory.h"
#include "p2p/base/mock_dns_resolving_packet_socket_factory.h"
#include "p2p/base/test_stun_server.h"
+#include "rtc_base/async_packet_socket.h"
#include "rtc_base/gunit.h"
#include "rtc_base/helpers.h"
+#include "rtc_base/network/received_packet.h"
#include "rtc_base/socket_address.h"
#include "rtc_base/ssl_adapter.h"
#include "rtc_base/virtual_socket_server.h"
@@ -94,8 +96,10 @@ class StunPortTestBase : public ::testing::Test, public sigslot::has_slots<> {
thread_(ss_.get()),
network_(network),
socket_factory_(ss_.get()),
- stun_server_1_(cricket::TestStunServer::Create(ss_.get(), kStunAddr1)),
- stun_server_2_(cricket::TestStunServer::Create(ss_.get(), kStunAddr2)),
+ stun_server_1_(
+ cricket::TestStunServer::Create(ss_.get(), kStunAddr1, thread_)),
+ stun_server_2_(
+ cricket::TestStunServer::Create(ss_.get(), kStunAddr2, thread_)),
mdns_responder_provider_(new FakeMdnsResponderProvider()),
done_(false),
error_(false),
@@ -160,7 +164,10 @@ class StunPortTestBase : public ::testing::Test, public sigslot::has_slots<> {
rtc::SocketAddress(kLocalAddr.ipaddr(), 0), 0, 0));
}
ASSERT_TRUE(socket_ != NULL);
- socket_->SignalReadPacket.connect(this, &StunPortTestBase::OnReadPacket);
+ socket_->RegisterReceivedPacketCallback(
+ [&](rtc::AsyncPacketSocket* socket, const rtc::ReceivedPacket& packet) {
+ OnReadPacket(socket, packet);
+ });
stun_port_ = cricket::UDPPort::Create(
rtc::Thread::Current(), socket_factory(), &network_, socket_.get(),
rtc::CreateRandomString(16), rtc::CreateRandomString(22), false,
@@ -178,18 +185,15 @@ class StunPortTestBase : public ::testing::Test, public sigslot::has_slots<> {
void PrepareAddress() { stun_port_->PrepareAddress(); }
void OnReadPacket(rtc::AsyncPacketSocket* socket,
- const char* data,
- size_t size,
- const rtc::SocketAddress& remote_addr,
- const int64_t& /* packet_time_us */) {
- stun_port_->HandleIncomingPacket(socket, data, size, remote_addr,
- /* packet_time_us */ -1);
+ const rtc::ReceivedPacket& packet) {
+ stun_port_->HandleIncomingPacket(socket, packet);
}
void SendData(const char* data, size_t len) {
- stun_port_->HandleIncomingPacket(socket_.get(), data, len,
- rtc::SocketAddress("22.22.22.22", 0),
- /* packet_time_us */ -1);
+ stun_port_->HandleIncomingPacket(socket_.get(),
+ rtc::ReceivedPacket::CreateFromLegacy(
+ data, len, /* packet_time_us */ -1,
+ rtc::SocketAddress("22.22.22.22", 0)));
}
void EnableMdnsObfuscation() {
@@ -224,14 +228,16 @@ class StunPortTestBase : public ::testing::Test, public sigslot::has_slots<> {
cricket::TestStunServer* stun_server_1() { return stun_server_1_.get(); }
cricket::TestStunServer* stun_server_2() { return stun_server_2_.get(); }
+ rtc::AutoSocketServerThread& thread() { return thread_; }
+
private:
std::unique_ptr<rtc::VirtualSocketServer> ss_;
rtc::AutoSocketServerThread thread_;
rtc::Network network_;
rtc::BasicPacketSocketFactory socket_factory_;
std::unique_ptr<cricket::UDPPort> stun_port_;
- std::unique_ptr<cricket::TestStunServer> stun_server_1_;
- std::unique_ptr<cricket::TestStunServer> stun_server_2_;
+ cricket::TestStunServer::StunServerPtr stun_server_1_;
+ cricket::TestStunServer::StunServerPtr stun_server_2_;
std::unique_ptr<rtc::AsyncPacketSocket> socket_;
std::unique_ptr<rtc::MdnsResponderProvider> mdns_responder_provider_;
bool done_;
@@ -618,12 +624,12 @@ class StunIPv6PortTestBase : public StunPortTestBase {
kIPv6LocalAddr.ipaddr(),
128),
kIPv6LocalAddr.ipaddr()) {
- stun_server_ipv6_1_.reset(
- cricket::TestStunServer::Create(ss(), kIPv6StunAddr1));
+ stun_server_ipv6_1_ =
+ cricket::TestStunServer::Create(ss(), kIPv6StunAddr1, thread());
}
protected:
- std::unique_ptr<cricket::TestStunServer> stun_server_ipv6_1_;
+ cricket::TestStunServer::StunServerPtr stun_server_ipv6_1_;
};
class StunIPv6PortTestWithRealClock : public StunIPv6PortTestBase {};
diff --git a/third_party/libwebrtc/p2p/base/stun_request.cc b/third_party/libwebrtc/p2p/base/stun_request.cc
index 15c1e6a269..fc5d43dc81 100644
--- a/third_party/libwebrtc/p2p/base/stun_request.cc
+++ b/third_party/libwebrtc/p2p/base/stun_request.cc
@@ -57,6 +57,10 @@ void StunRequestManager::Send(StunRequest* request) {
void StunRequestManager::SendDelayed(StunRequest* request, int delay) {
RTC_DCHECK_RUN_ON(thread_);
RTC_DCHECK_EQ(this, request->manager());
+ RTC_DCHECK(!request->AuthenticationRequired() ||
+ request->msg()->integrity() !=
+ StunMessage::IntegrityStatus::kNotSet)
+ << "Sending request w/o integrity!";
auto [iter, was_inserted] =
requests_.emplace(request->id(), absl::WrapUnique(request));
RTC_DCHECK(was_inserted);
@@ -104,15 +108,23 @@ bool StunRequestManager::CheckResponse(StunMessage* msg) {
StunRequest* request = iter->second.get();
// Now that we know the request, we can see if the response is
- // integrity-protected or not.
- // For some tests, the message integrity is not set in the request.
- // Complain, and then don't check.
+ // integrity-protected or not. Some requests explicitly disables
+ // integrity checks using SetAuthenticationRequired.
+ // TODO(chromium:1177125): Remove below!
+ // And we suspect that for some tests, the message integrity is not set in the
+ // request. Complain, and then don't check.
bool skip_integrity_checking =
(request->msg()->integrity() == StunMessage::IntegrityStatus::kNotSet);
- if (skip_integrity_checking) {
+ if (!request->AuthenticationRequired()) {
+ // This is a STUN_BINDING to from stun_port.cc or
+ // the initial (unauthenticated) TURN_ALLOCATE_REQUEST.
+ } else if (skip_integrity_checking) {
+ // TODO(chromium:1177125): Remove below!
// This indicates lazy test writing (not adding integrity attribute).
// Complain, but only in debug mode (while developing).
- RTC_DLOG(LS_ERROR)
+ RTC_LOG(LS_ERROR)
+ << "CheckResponse called on a passwordless request. Fix test!";
+ RTC_DCHECK(false)
<< "CheckResponse called on a passwordless request. Fix test!";
} else {
if (msg->integrity() == StunMessage::IntegrityStatus::kNotSet) {
@@ -190,7 +202,8 @@ bool StunRequestManager::CheckResponse(const char* data, size_t size) {
// Parse the STUN message and continue processing as usual.
- rtc::ByteBufferReader buf(data, size);
+ rtc::ByteBufferReader buf(
+ rtc::MakeArrayView(reinterpret_cast<const uint8_t*>(data), size));
std::unique_ptr<StunMessage> response(iter->second->msg_->CreateNew());
if (!response->Read(&buf)) {
RTC_LOG(LS_WARNING) << "Failed to read STUN response "
diff --git a/third_party/libwebrtc/p2p/base/stun_request.h b/third_party/libwebrtc/p2p/base/stun_request.h
index 6e83be3830..f2fd7e5211 100644
--- a/third_party/libwebrtc/p2p/base/stun_request.h
+++ b/third_party/libwebrtc/p2p/base/stun_request.h
@@ -115,6 +115,12 @@ class StunRequest {
// Time elapsed since last send (in ms)
int Elapsed() const;
+ // Add method to explitly allow requests w/o password.
+ // - STUN_BINDINGs from StunPort to a stun server
+ // - The initial TURN_ALLOCATE_REQUEST
+ void SetAuthenticationRequired(bool val) { authentication_required_ = val; }
+ bool AuthenticationRequired() const { return authentication_required_; }
+
protected:
friend class StunRequestManager;
@@ -155,6 +161,7 @@ class StunRequest {
bool timeout_ RTC_GUARDED_BY(network_thread());
webrtc::ScopedTaskSafety task_safety_{
webrtc::PendingTaskSafetyFlag::CreateDetachedInactive()};
+ bool authentication_required_ = true;
};
} // namespace cricket
diff --git a/third_party/libwebrtc/p2p/base/stun_request_unittest.cc b/third_party/libwebrtc/p2p/base/stun_request_unittest.cc
index 2f88ab1fd3..8ce89f8a00 100644
--- a/third_party/libwebrtc/p2p/base/stun_request_unittest.cc
+++ b/third_party/libwebrtc/p2p/base/stun_request_unittest.cc
@@ -79,7 +79,9 @@ class StunRequestThunker : public StunRequest {
public:
StunRequestThunker(StunRequestManager& manager, StunRequestTest* test)
: StunRequest(manager, CreateStunMessage(STUN_BINDING_REQUEST)),
- test_(test) {}
+ test_(test) {
+ SetAuthenticationRequired(false);
+ }
std::unique_ptr<StunMessage> CreateResponseMessage(StunMessageType type) {
return CreateStunMessage(type, msg());
diff --git a/third_party/libwebrtc/p2p/base/stun_server.cc b/third_party/libwebrtc/p2p/base/stun_server.cc
index 7827a0bb81..e37c5baf26 100644
--- a/third_party/libwebrtc/p2p/base/stun_server.cc
+++ b/third_party/libwebrtc/p2p/base/stun_server.cc
@@ -14,42 +14,49 @@
#include <utility>
#include "absl/strings/string_view.h"
+#include "api/sequence_checker.h"
+#include "rtc_base/async_packet_socket.h"
#include "rtc_base/byte_buffer.h"
#include "rtc_base/logging.h"
+#include "rtc_base/network/received_packet.h"
namespace cricket {
StunServer::StunServer(rtc::AsyncUDPSocket* socket) : socket_(socket) {
- socket_->SignalReadPacket.connect(this, &StunServer::OnPacket);
+ socket_->RegisterReceivedPacketCallback(
+ [&](rtc::AsyncPacketSocket* socket, const rtc::ReceivedPacket& packet) {
+ OnPacket(socket, packet);
+ });
}
StunServer::~StunServer() {
- socket_->SignalReadPacket.disconnect(this);
+ RTC_DCHECK_RUN_ON(&sequence_checker_);
+ socket_->DeregisterReceivedPacketCallback();
}
void StunServer::OnPacket(rtc::AsyncPacketSocket* socket,
- const char* buf,
- size_t size,
- const rtc::SocketAddress& remote_addr,
- const int64_t& /* packet_time_us */) {
+ const rtc::ReceivedPacket& packet) {
+ RTC_DCHECK_RUN_ON(&sequence_checker_);
// Parse the STUN message; eat any messages that fail to parse.
- rtc::ByteBufferReader bbuf(buf, size);
+ rtc::ByteBufferReader bbuf(packet.payload());
StunMessage msg;
if (!msg.Read(&bbuf)) {
return;
}
- // TODO(?): If unknown non-optional (<= 0x7fff) attributes are found, send a
+ // TODO(?): If unknown non-optional (<= 0x7fff) attributes are found,
+ // send a
// 420 "Unknown Attribute" response.
// Send the message to the appropriate handler function.
switch (msg.type()) {
case STUN_BINDING_REQUEST:
- OnBindingRequest(&msg, remote_addr);
+ OnBindingRequest(&msg, packet.source_address());
break;
default:
- SendErrorResponse(msg, remote_addr, 600, "Operation Not Supported");
+ SendErrorResponse(msg, packet.source_address(), 600,
+ "Operation Not Supported");
}
}
diff --git a/third_party/libwebrtc/p2p/base/stun_server.h b/third_party/libwebrtc/p2p/base/stun_server.h
index 505773b052..f6a776c5f7 100644
--- a/third_party/libwebrtc/p2p/base/stun_server.h
+++ b/third_party/libwebrtc/p2p/base/stun_server.h
@@ -12,7 +12,6 @@
#define P2P_BASE_STUN_SERVER_H_
#include <stddef.h>
-#include <stdint.h>
#include <memory>
@@ -21,26 +20,22 @@
#include "rtc_base/async_packet_socket.h"
#include "rtc_base/async_udp_socket.h"
#include "rtc_base/socket_address.h"
-#include "rtc_base/third_party/sigslot/sigslot.h"
namespace cricket {
const int STUN_SERVER_PORT = 3478;
-class StunServer : public sigslot::has_slots<> {
+class StunServer {
public:
// Creates a STUN server, which will listen on the given socket.
explicit StunServer(rtc::AsyncUDPSocket* socket);
// Removes the STUN server from the socket and deletes the socket.
- ~StunServer() override;
+ virtual ~StunServer();
protected:
- // Slot for Socket.PacketRead:
+ // Callback for packets from socket.
void OnPacket(rtc::AsyncPacketSocket* socket,
- const char* buf,
- size_t size,
- const rtc::SocketAddress& remote_addr,
- const int64_t& packet_time_us);
+ const rtc::ReceivedPacket& packet);
// Handlers for the different types of STUN/TURN requests:
virtual void OnBindingRequest(StunMessage* msg,
@@ -64,6 +59,7 @@ class StunServer : public sigslot::has_slots<> {
StunMessage* response) const;
private:
+ webrtc::SequenceChecker sequence_checker_;
std::unique_ptr<rtc::AsyncUDPSocket> socket_;
};
diff --git a/third_party/libwebrtc/p2p/base/stun_server_unittest.cc b/third_party/libwebrtc/p2p/base/stun_server_unittest.cc
index 5d3f31fb98..e4ea30cba4 100644
--- a/third_party/libwebrtc/p2p/base/stun_server_unittest.cc
+++ b/third_party/libwebrtc/p2p/base/stun_server_unittest.cc
@@ -33,15 +33,13 @@ const rtc::SocketAddress client_addr("1.2.3.4", 1234);
class StunServerTest : public ::testing::Test {
public:
- StunServerTest() : ss_(new rtc::VirtualSocketServer()), network_(ss_.get()) {
+ StunServerTest() : ss_(new rtc::VirtualSocketServer()) {
+ ss_->SetMessageQueue(&main_thread);
server_.reset(
new StunServer(rtc::AsyncUDPSocket::Create(ss_.get(), server_addr)));
client_.reset(new rtc::TestClient(
absl::WrapUnique(rtc::AsyncUDPSocket::Create(ss_.get(), client_addr))));
-
- network_.Start();
}
- ~StunServerTest() override { network_.Stop(); }
void Send(const StunMessage& msg) {
rtc::ByteBufferWriter buf;
@@ -57,7 +55,7 @@ class StunServerTest : public ::testing::Test {
std::unique_ptr<rtc::TestClient::Packet> packet =
client_->NextPacket(rtc::TestClient::kTimeoutMs);
if (packet) {
- rtc::ByteBufferReader buf(packet->buf, packet->size);
+ rtc::ByteBufferReader buf(packet->buf);
msg = new StunMessage();
msg->Read(&buf);
}
@@ -67,7 +65,6 @@ class StunServerTest : public ::testing::Test {
private:
rtc::AutoThread main_thread;
std::unique_ptr<rtc::VirtualSocketServer> ss_;
- rtc::Thread network_;
std::unique_ptr<StunServer> server_;
std::unique_ptr<rtc::TestClient> client_;
};
diff --git a/third_party/libwebrtc/p2p/base/tcp_port.cc b/third_party/libwebrtc/p2p/base/tcp_port.cc
index 5f25624d00..ce61416979 100644
--- a/third_party/libwebrtc/p2p/base/tcp_port.cc
+++ b/third_party/libwebrtc/p2p/base/tcp_port.cc
@@ -81,7 +81,9 @@
#include "rtc_base/ip_address.h"
#include "rtc_base/logging.h"
#include "rtc_base/net_helper.h"
+#include "rtc_base/network/received_packet.h"
#include "rtc_base/rate_tracker.h"
+#include "rtc_base/thread.h"
namespace cricket {
using ::webrtc::SafeTask;
@@ -159,7 +161,7 @@ Connection* TCPPort::CreateConnection(const Candidate& address,
// Incoming connection; we already created a socket and connected signals,
// so we need to hand off the "read packet" responsibility to
// TCPConnection.
- socket->SignalReadPacket.disconnect(this);
+ socket->DeregisterReceivedPacketCallback();
conn = new TCPConnection(NewWeakPtr(), address, socket);
} else {
// Outgoing connection, which will create a new socket for which we still
@@ -288,7 +290,10 @@ void TCPPort::OnNewConnection(rtc::AsyncListenSocket* socket,
Incoming incoming;
incoming.addr = new_socket->GetRemoteAddress();
incoming.socket = new_socket;
- incoming.socket->SignalReadPacket.connect(this, &TCPPort::OnReadPacket);
+ incoming.socket->RegisterReceivedPacketCallback(
+ [&](rtc::AsyncPacketSocket* socket, const rtc::ReceivedPacket& packet) {
+ OnReadPacket(socket, packet);
+ });
incoming.socket->SignalReadyToSend.connect(this, &TCPPort::OnReadyToSend);
incoming.socket->SignalSentPacket.connect(this, &TCPPort::OnSentPacket);
@@ -326,11 +331,8 @@ rtc::AsyncPacketSocket* TCPPort::GetIncoming(const rtc::SocketAddress& addr,
}
void TCPPort::OnReadPacket(rtc::AsyncPacketSocket* socket,
- const char* data,
- size_t size,
- const rtc::SocketAddress& remote_addr,
- const int64_t& packet_time_us) {
- Port::OnReadPacket(data, size, remote_addr, PROTO_TCP);
+ const rtc::ReceivedPacket& packet) {
+ Port::OnReadPacket(packet, PROTO_TCP);
}
void TCPPort::OnSentPacket(rtc::AsyncPacketSocket* socket,
@@ -559,13 +561,10 @@ void TCPConnection::MaybeReconnect() {
}
void TCPConnection::OnReadPacket(rtc::AsyncPacketSocket* socket,
- const char* data,
- size_t size,
- const rtc::SocketAddress& remote_addr,
- const int64_t& packet_time_us) {
+ const rtc::ReceivedPacket& packet) {
RTC_DCHECK_RUN_ON(network_thread());
RTC_DCHECK_EQ(socket, socket_.get());
- Connection::OnReadPacket(data, size, packet_time_us);
+ Connection::OnReadPacket(packet);
}
void TCPConnection::OnReadyToSend(rtc::AsyncPacketSocket* socket) {
@@ -623,7 +622,10 @@ void TCPConnection::ConnectSocketSignals(rtc::AsyncPacketSocket* socket) {
if (outgoing_) {
socket->SignalConnect.connect(this, &TCPConnection::OnConnect);
}
- socket->SignalReadPacket.connect(this, &TCPConnection::OnReadPacket);
+ socket->RegisterReceivedPacketCallback(
+ [&](rtc::AsyncPacketSocket* socket, const rtc::ReceivedPacket& packet) {
+ OnReadPacket(socket, packet);
+ });
socket->SignalReadyToSend.connect(this, &TCPConnection::OnReadyToSend);
socket->SubscribeCloseEvent(this, [this, safety = network_safety_.flag()](
rtc::AsyncPacketSocket* s, int err) {
@@ -636,7 +638,7 @@ void TCPConnection::DisconnectSocketSignals(rtc::AsyncPacketSocket* socket) {
if (outgoing_) {
socket->SignalConnect.disconnect(this);
}
- socket->SignalReadPacket.disconnect(this);
+ socket->DeregisterReceivedPacketCallback();
socket->SignalReadyToSend.disconnect(this);
socket->UnsubscribeCloseEvent(this);
}
diff --git a/third_party/libwebrtc/p2p/base/tcp_port.h b/third_party/libwebrtc/p2p/base/tcp_port.h
index a1bbaa9f35..bd7ed4c110 100644
--- a/third_party/libwebrtc/p2p/base/tcp_port.h
+++ b/third_party/libwebrtc/p2p/base/tcp_port.h
@@ -22,6 +22,7 @@
#include "p2p/base/port.h"
#include "rtc_base/async_packet_socket.h"
#include "rtc_base/containers/flat_map.h"
+#include "rtc_base/network/received_packet.h"
namespace cricket {
@@ -101,10 +102,7 @@ class TCPPort : public Port {
// Receives packet signal from the local TCP Socket.
void OnReadPacket(rtc::AsyncPacketSocket* socket,
- const char* data,
- size_t size,
- const rtc::SocketAddress& remote_addr,
- const int64_t& packet_time_us);
+ const rtc::ReceivedPacket& packet);
void OnSentPacket(rtc::AsyncPacketSocket* socket,
const rtc::SentPacket& sent_packet) override;
@@ -170,10 +168,7 @@ class TCPConnection : public Connection, public sigslot::has_slots<> {
void OnConnect(rtc::AsyncPacketSocket* socket);
void OnClose(rtc::AsyncPacketSocket* socket, int error);
void OnReadPacket(rtc::AsyncPacketSocket* socket,
- const char* data,
- size_t size,
- const rtc::SocketAddress& remote_addr,
- const int64_t& packet_time_us);
+ const rtc::ReceivedPacket& packet);
void OnReadyToSend(rtc::AsyncPacketSocket* socket);
void OnDestroyed(Connection* c);
diff --git a/third_party/libwebrtc/p2p/base/test_stun_server.cc b/third_party/libwebrtc/p2p/base/test_stun_server.cc
index d4c3b2d851..a8a5c46f8c 100644
--- a/third_party/libwebrtc/p2p/base/test_stun_server.cc
+++ b/third_party/libwebrtc/p2p/base/test_stun_server.cc
@@ -10,21 +10,32 @@
#include "p2p/base/test_stun_server.h"
+#include <memory>
+
#include "rtc_base/socket.h"
#include "rtc_base/socket_server.h"
namespace cricket {
-TestStunServer* TestStunServer::Create(rtc::SocketServer* ss,
- const rtc::SocketAddress& addr) {
+std::unique_ptr<TestStunServer, std::function<void(TestStunServer*)>>
+TestStunServer::Create(rtc::SocketServer* ss,
+ const rtc::SocketAddress& addr,
+ rtc::Thread& network_thread) {
rtc::Socket* socket = ss->CreateSocket(addr.family(), SOCK_DGRAM);
rtc::AsyncUDPSocket* udp_socket = rtc::AsyncUDPSocket::Create(socket, addr);
-
- return new TestStunServer(udp_socket);
+ TestStunServer* server = nullptr;
+ network_thread.BlockingCall(
+ [&]() { server = new TestStunServer(udp_socket, network_thread); });
+ std::unique_ptr<TestStunServer, std::function<void(TestStunServer*)>> result(
+ server, [&](TestStunServer* server) {
+ network_thread.BlockingCall([server]() { delete server; });
+ });
+ return result;
}
void TestStunServer::OnBindingRequest(StunMessage* msg,
const rtc::SocketAddress& remote_addr) {
+ RTC_DCHECK_RUN_ON(&network_thread_);
if (fake_stun_addr_.IsNil()) {
StunServer::OnBindingRequest(msg, remote_addr);
} else {
diff --git a/third_party/libwebrtc/p2p/base/test_stun_server.h b/third_party/libwebrtc/p2p/base/test_stun_server.h
index 11ac620bb8..7bf7dc1dba 100644
--- a/third_party/libwebrtc/p2p/base/test_stun_server.h
+++ b/third_party/libwebrtc/p2p/base/test_stun_server.h
@@ -11,19 +11,25 @@
#ifndef P2P_BASE_TEST_STUN_SERVER_H_
#define P2P_BASE_TEST_STUN_SERVER_H_
+#include <memory>
+
#include "api/transport/stun.h"
#include "p2p/base/stun_server.h"
#include "rtc_base/async_udp_socket.h"
#include "rtc_base/socket_address.h"
#include "rtc_base/socket_server.h"
+#include "rtc_base/thread.h"
namespace cricket {
// A test STUN server. Useful for unit tests.
class TestStunServer : StunServer {
public:
- static TestStunServer* Create(rtc::SocketServer* ss,
- const rtc::SocketAddress& addr);
+ using StunServerPtr =
+ std::unique_ptr<TestStunServer, std::function<void(TestStunServer*)>>;
+ static StunServerPtr Create(rtc::SocketServer* ss,
+ const rtc::SocketAddress& addr,
+ rtc::Thread& network_thread);
// Set a fake STUN address to return to the client.
void set_fake_stun_addr(const rtc::SocketAddress& addr) {
@@ -31,13 +37,17 @@ class TestStunServer : StunServer {
}
private:
- explicit TestStunServer(rtc::AsyncUDPSocket* socket) : StunServer(socket) {}
+ static void DeleteOnNetworkThread(TestStunServer* server);
+
+ TestStunServer(rtc::AsyncUDPSocket* socket, rtc::Thread& network_thread)
+ : StunServer(socket), network_thread_(network_thread) {}
void OnBindingRequest(StunMessage* msg,
const rtc::SocketAddress& remote_addr) override;
private:
rtc::SocketAddress fake_stun_addr_;
+ rtc::Thread& network_thread_;
};
} // namespace cricket
diff --git a/third_party/libwebrtc/p2p/base/turn_port.cc b/third_party/libwebrtc/p2p/base/turn_port.cc
index 1b05d82b89..e6f5e77114 100644
--- a/third_party/libwebrtc/p2p/base/turn_port.cc
+++ b/third_party/libwebrtc/p2p/base/turn_port.cc
@@ -10,6 +10,7 @@
#include "p2p/base/turn_port.h"
+#include <cstdint>
#include <functional>
#include <memory>
#include <utility>
@@ -29,6 +30,7 @@
#include "rtc_base/experiments/field_trial_parser.h"
#include "rtc_base/logging.h"
#include "rtc_base/net_helpers.h"
+#include "rtc_base/network/received_packet.h"
#include "rtc_base/socket_address.h"
#include "rtc_base/strings/string_builder.h"
@@ -435,7 +437,10 @@ bool TurnPort::CreateTurnClientSocket() {
if (!SharedSocket()) {
// If socket is shared, AllocationSequence will receive the packet.
- socket_->SignalReadPacket.connect(this, &TurnPort::OnReadPacket);
+ socket_->RegisterReceivedPacketCallback(
+ [&](rtc::AsyncPacketSocket* socket, const rtc::ReceivedPacket& packet) {
+ OnReadPacket(socket, packet);
+ });
}
socket_->SignalReadyToSend.connect(this, &TurnPort::OnReadyToSend);
@@ -679,10 +684,7 @@ void TurnPort::SendBindingErrorResponse(StunMessage* message,
}
bool TurnPort::HandleIncomingPacket(rtc::AsyncPacketSocket* socket,
- const char* data,
- size_t size,
- const rtc::SocketAddress& remote_addr,
- int64_t packet_time_us) {
+ const rtc::ReceivedPacket& packet) {
if (socket != socket_) {
// The packet was received on a shared socket after we've allocated a new
// socket for this TURN port.
@@ -692,16 +694,17 @@ bool TurnPort::HandleIncomingPacket(rtc::AsyncPacketSocket* socket,
// This is to guard against a STUN response from previous server after
// alternative server redirection. TODO(guoweis): add a unit test for this
// race condition.
- if (remote_addr != server_address_.address) {
+ if (packet.source_address() != server_address_.address) {
RTC_LOG(LS_WARNING)
<< ToString() << ": Discarding TURN message from unknown address: "
- << remote_addr.ToSensitiveNameAndAddressString() << " server_address_: "
+ << packet.source_address().ToSensitiveNameAndAddressString()
+ << " server_address_: "
<< server_address_.address.ToSensitiveNameAndAddressString();
return false;
}
// The message must be at least the size of a channel header.
- if (size < TURN_CHANNEL_HEADER_SIZE) {
+ if (packet.payload().size() < TURN_CHANNEL_HEADER_SIZE) {
RTC_LOG(LS_WARNING) << ToString()
<< ": Received TURN message that was too short";
return false;
@@ -714,10 +717,15 @@ bool TurnPort::HandleIncomingPacket(rtc::AsyncPacketSocket* socket,
return false;
}
+ const char* data = reinterpret_cast<const char*>(packet.payload().data());
+ int size = packet.payload().size();
+ int64_t packet_time_us =
+ packet.arrival_time() ? packet.arrival_time()->us() : -1;
+
// Check the message type, to see if is a Channel Data message.
// The message will either be channel data, a TURN data indication, or
// a response to a previous request.
- uint16_t msg_type = rtc::GetBE16(data);
+ uint16_t msg_type = rtc::GetBE16(packet.payload().data());
if (IsTurnChannelData(msg_type)) {
HandleChannelData(msg_type, data, size, packet_time_us);
return true;
@@ -742,11 +750,8 @@ bool TurnPort::HandleIncomingPacket(rtc::AsyncPacketSocket* socket,
}
void TurnPort::OnReadPacket(rtc::AsyncPacketSocket* socket,
- const char* data,
- size_t size,
- const rtc::SocketAddress& remote_addr,
- const int64_t& packet_time_us) {
- HandleIncomingPacket(socket, data, size, remote_addr, packet_time_us);
+ const rtc::ReceivedPacket& packet) {
+ HandleIncomingPacket(socket, packet);
}
void TurnPort::OnSentPacket(rtc::AsyncPacketSocket* socket,
@@ -986,7 +991,8 @@ void TurnPort::HandleDataIndication(const char* data,
size_t size,
int64_t packet_time_us) {
// Read in the message, and process according to RFC5766, Section 10.4.
- rtc::ByteBufferReader buf(data, size);
+ rtc::ByteBufferReader buf(
+ rtc::MakeArrayView(reinterpret_cast<const uint8_t*>(data), size));
TurnMessage msg;
if (!msg.Read(&buf)) {
RTC_LOG(LS_WARNING) << ToString()
@@ -1021,9 +1027,10 @@ void TurnPort::HandleDataIndication(const char* data,
"peer address, addr: "
<< ext_addr.ToSensitiveString();
}
-
- DispatchPacket(data_attr->bytes(), data_attr->length(), ext_addr, PROTO_UDP,
- packet_time_us);
+ // TODO(bugs.webrtc.org/14870): rebuild DispatchPacket to take an
+ // ArrayView<uint8_t>
+ DispatchPacket(reinterpret_cast<const char*>(data_attr->array_view().data()),
+ data_attr->length(), ext_addr, PROTO_UDP, packet_time_us);
}
void TurnPort::HandleChannelData(int channel_id,
@@ -1073,10 +1080,12 @@ void TurnPort::DispatchPacket(const char* data,
const rtc::SocketAddress& remote_addr,
ProtocolType proto,
int64_t packet_time_us) {
+ rtc::ReceivedPacket packet = rtc::ReceivedPacket::CreateFromLegacy(
+ data, size, packet_time_us, remote_addr);
if (Connection* conn = GetConnection(remote_addr)) {
- conn->OnReadPacket(data, size, packet_time_us);
+ conn->OnReadPacket(packet);
} else {
- Port::OnReadPacket(data, size, remote_addr, proto);
+ Port::OnReadPacket(packet, proto);
}
}
@@ -1313,6 +1322,8 @@ TurnAllocateRequest::TurnAllocateRequest(TurnPort* port)
message->AddAttribute(std::move(transport_attr));
if (!port_->hash().empty()) {
port_->AddRequestAuthInfo(message);
+ } else {
+ SetAuthenticationRequired(false);
}
port_->MaybeAddTurnLoggingId(message);
port_->TurnCustomizerMaybeModifyOutgoingStunMessage(message);
diff --git a/third_party/libwebrtc/p2p/base/turn_port.h b/third_party/libwebrtc/p2p/base/turn_port.h
index 8fa4607e51..686edaf595 100644
--- a/third_party/libwebrtc/p2p/base/turn_port.h
+++ b/third_party/libwebrtc/p2p/base/turn_port.h
@@ -27,6 +27,7 @@
#include "p2p/base/port.h"
#include "p2p/client/basic_port_allocator.h"
#include "rtc_base/async_packet_socket.h"
+#include "rtc_base/network/received_packet.h"
#include "rtc_base/ssl_certificate.h"
namespace webrtc {
@@ -144,10 +145,7 @@ class TurnPort : public Port {
int GetError() override;
bool HandleIncomingPacket(rtc::AsyncPacketSocket* socket,
- const char* data,
- size_t size,
- const rtc::SocketAddress& remote_addr,
- int64_t packet_time_us) override;
+ const rtc::ReceivedPacket& packet) override;
bool CanHandleIncomingPacketsFrom(
const rtc::SocketAddress& addr) const override;
@@ -159,10 +157,7 @@ class TurnPort : public Port {
absl::string_view reason) override;
virtual void OnReadPacket(rtc::AsyncPacketSocket* socket,
- const char* data,
- size_t size,
- const rtc::SocketAddress& remote_addr,
- const int64_t& packet_time_us);
+ const rtc::ReceivedPacket& packet);
void OnSentPacket(rtc::AsyncPacketSocket* socket,
const rtc::SentPacket& sent_packet) override;
diff --git a/third_party/libwebrtc/p2p/base/turn_port_unittest.cc b/third_party/libwebrtc/p2p/base/turn_port_unittest.cc
index 55706e142b..e7efb5e594 100644
--- a/third_party/libwebrtc/p2p/base/turn_port_unittest.cc
+++ b/third_party/libwebrtc/p2p/base/turn_port_unittest.cc
@@ -7,6 +7,10 @@
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
+#include <cstdint>
+
+#include "api/array_view.h"
+#include "rtc_base/network/received_packet.h"
#if defined(WEBRTC_POSIX)
#include <dirent.h>
@@ -218,12 +222,8 @@ class TurnPortTest : public ::testing::Test,
}
void OnUdpPortComplete(Port* port) { udp_ready_ = true; }
void OnSocketReadPacket(rtc::AsyncPacketSocket* socket,
- const char* data,
- size_t size,
- const rtc::SocketAddress& remote_addr,
- const int64_t& packet_time_us) {
- turn_port_->HandleIncomingPacket(socket, data, size, remote_addr,
- packet_time_us);
+ const rtc::ReceivedPacket& packet) {
+ turn_port_->HandleIncomingPacket(socket, packet);
}
void OnTurnPortDestroyed(PortInterface* port) { turn_port_destroyed_ = true; }
@@ -323,8 +323,11 @@ class TurnPortTest : public ::testing::Test,
socket_.reset(socket_factory()->CreateUdpSocket(
rtc::SocketAddress(kLocalAddr1.ipaddr(), 0), 0, 0));
ASSERT_TRUE(socket_ != NULL);
- socket_->SignalReadPacket.connect(this,
- &TurnPortTest::OnSocketReadPacket);
+ socket_->RegisterReceivedPacketCallback(
+ [&](rtc::AsyncPacketSocket* socket,
+ const rtc::ReceivedPacket& packet) {
+ OnSocketReadPacket(socket, packet);
+ });
}
RelayServerConfig config;
@@ -929,7 +932,7 @@ class TurnLoggingIdValidator : public StunMessageObserver {
}
}
}
- void ReceivedChannelData(const char* data, size_t size) override {}
+ void ReceivedChannelData(rtc::ArrayView<const uint8_t> packet) override {}
private:
const char* expect_val_;
@@ -1193,8 +1196,10 @@ TEST_F(TurnPortTest, TestTurnAllocateMismatch) {
// Verify that all packets received from the shared socket are ignored.
std::string test_packet = "Test packet";
EXPECT_FALSE(turn_port_->HandleIncomingPacket(
- socket_.get(), test_packet.data(), test_packet.size(),
- rtc::SocketAddress(kTurnUdpExtAddr.ipaddr(), 0), rtc::TimeMicros()));
+ socket_.get(),
+ rtc::ReceivedPacket::CreateFromLegacy(
+ test_packet.data(), test_packet.size(), rtc::TimeMicros(),
+ rtc::SocketAddress(kTurnUdpExtAddr.ipaddr(), 0))));
}
// Tests that a shared-socket-TurnPort creates its own socket after
@@ -1722,14 +1727,14 @@ class MessageObserver : public StunMessageObserver {
const StunByteStringAttribute* attr =
msg->GetByteString(TestTurnCustomizer::STUN_ATTR_COUNTER);
if (attr != nullptr && attr_counter_ != nullptr) {
- rtc::ByteBufferReader buf(attr->bytes(), attr->length());
+ rtc::ByteBufferReader buf(attr->array_view());
unsigned int val = ~0u;
buf.ReadUInt32(&val);
(*attr_counter_)++;
}
}
- void ReceivedChannelData(const char* data, size_t size) override {
+ void ReceivedChannelData(rtc::ArrayView<const uint8_t> payload) override {
if (channel_data_counter_ != nullptr) {
(*channel_data_counter_)++;
}
diff --git a/third_party/libwebrtc/p2p/base/turn_server.cc b/third_party/libwebrtc/p2p/base/turn_server.cc
index b362bfa5cd..3d633110a7 100644
--- a/third_party/libwebrtc/p2p/base/turn_server.cc
+++ b/third_party/libwebrtc/p2p/base/turn_server.cc
@@ -102,7 +102,11 @@ void TurnServer::AddInternalSocket(rtc::AsyncPacketSocket* socket,
RTC_DCHECK_RUN_ON(thread_);
RTC_DCHECK(server_sockets_.end() == server_sockets_.find(socket));
server_sockets_[socket] = proto;
- socket->SignalReadPacket.connect(this, &TurnServer::OnInternalPacket);
+ socket->RegisterReceivedPacketCallback(
+ [&](rtc::AsyncPacketSocket* socket, const rtc::ReceivedPacket& packet) {
+ RTC_DCHECK_RUN_ON(thread_);
+ OnInternalPacket(socket, packet);
+ });
}
void TurnServer::AddInternalServerSocket(
@@ -163,39 +167,35 @@ void TurnServer::OnInternalSocketClose(rtc::AsyncPacketSocket* socket,
}
void TurnServer::OnInternalPacket(rtc::AsyncPacketSocket* socket,
- const char* data,
- size_t size,
- const rtc::SocketAddress& addr,
- const int64_t& /* packet_time_us */) {
+ const rtc::ReceivedPacket& packet) {
RTC_DCHECK_RUN_ON(thread_);
// Fail if the packet is too small to even contain a channel header.
- if (size < TURN_CHANNEL_HEADER_SIZE) {
+ if (packet.payload().size() < TURN_CHANNEL_HEADER_SIZE) {
return;
}
InternalSocketMap::iterator iter = server_sockets_.find(socket);
RTC_DCHECK(iter != server_sockets_.end());
- TurnServerConnection conn(addr, iter->second, socket);
- uint16_t msg_type = rtc::GetBE16(data);
+ TurnServerConnection conn(packet.source_address(), iter->second, socket);
+ uint16_t msg_type = rtc::GetBE16(packet.payload().data());
if (!IsTurnChannelData(msg_type)) {
// This is a STUN message.
- HandleStunMessage(&conn, data, size);
+ HandleStunMessage(&conn, packet.payload());
} else {
// This is a channel message; let the allocation handle it.
TurnServerAllocation* allocation = FindAllocation(&conn);
if (allocation) {
- allocation->HandleChannelData(data, size);
+ allocation->HandleChannelData(packet.payload());
}
if (stun_message_observer_ != nullptr) {
- stun_message_observer_->ReceivedChannelData(data, size);
+ stun_message_observer_->ReceivedChannelData(packet.payload());
}
}
}
void TurnServer::HandleStunMessage(TurnServerConnection* conn,
- const char* data,
- size_t size) {
+ rtc::ArrayView<const uint8_t> payload) {
TurnMessage msg;
- rtc::ByteBufferReader buf(data, size);
+ rtc::ByteBufferReader buf(payload);
if (!msg.Read(&buf) || (buf.Length() > 0)) {
RTC_LOG(LS_WARNING) << "Received invalid STUN message";
return;
@@ -231,7 +231,7 @@ void TurnServer::HandleStunMessage(TurnServerConnection* conn,
// Ensure the message is authorized; only needed for requests.
if (IsStunRequestType(msg.type())) {
- if (!CheckAuthorization(conn, &msg, data, size, key)) {
+ if (!CheckAuthorization(conn, &msg, key)) {
return;
}
}
@@ -272,8 +272,6 @@ bool TurnServer::GetKey(const StunMessage* msg, std::string* key) {
bool TurnServer::CheckAuthorization(TurnServerConnection* conn,
StunMessage* msg,
- const char* data,
- size_t size,
absl::string_view key) {
// RFC 5389, 10.2.2.
RTC_DCHECK(IsStunRequestType(msg->type()));
@@ -516,7 +514,7 @@ void TurnServer::DestroyInternalSocket(rtc::AsyncPacketSocket* socket) {
if (iter != server_sockets_.end()) {
rtc::AsyncPacketSocket* socket = iter->first;
socket->UnsubscribeCloseEvent(this);
- socket->SignalReadPacket.disconnect(this);
+ socket->DeregisterReceivedPacketCallback();
server_sockets_.erase(iter);
std::unique_ptr<rtc::AsyncPacketSocket> socket_to_delete =
absl::WrapUnique(socket);
@@ -561,8 +559,11 @@ TurnServerAllocation::TurnServerAllocation(TurnServer* server,
conn_(conn),
external_socket_(socket),
key_(key) {
- external_socket_->SignalReadPacket.connect(
- this, &TurnServerAllocation::OnExternalPacket);
+ external_socket_->RegisterReceivedPacketCallback(
+ [&](rtc::AsyncPacketSocket* socket, const rtc::ReceivedPacket& packet) {
+ RTC_DCHECK_RUN_ON(thread_);
+ OnExternalPacket(socket, packet);
+ });
}
TurnServerAllocation::~TurnServerAllocation() {
@@ -669,8 +670,8 @@ void TurnServerAllocation::HandleSendIndication(const TurnMessage* msg) {
// If a permission exists, send the data on to the peer.
if (HasPermission(peer_attr->GetAddress().ipaddr())) {
- SendExternal(data_attr->bytes(), data_attr->length(),
- peer_attr->GetAddress());
+ SendExternal(reinterpret_cast<char*>(data_attr->array_view().data()),
+ data_attr->length(), peer_attr->GetAddress());
} else {
RTC_LOG(LS_WARNING) << ToString()
<< ": Received send indication without permission"
@@ -758,14 +759,15 @@ void TurnServerAllocation::HandleChannelBindRequest(const TurnMessage* msg) {
SendResponse(&response);
}
-void TurnServerAllocation::HandleChannelData(const char* data, size_t size) {
+void TurnServerAllocation::HandleChannelData(
+ rtc::ArrayView<const uint8_t> payload) {
// Extract the channel number from the data.
- uint16_t channel_id = rtc::GetBE16(data);
+ uint16_t channel_id = rtc::GetBE16(payload.data());
auto channel = FindChannel(channel_id);
if (channel != channels_.end()) {
// Send the data to the peer address.
- SendExternal(data + TURN_CHANNEL_HEADER_SIZE,
- size - TURN_CHANNEL_HEADER_SIZE, channel->peer);
+ SendExternal(payload.data() + TURN_CHANNEL_HEADER_SIZE,
+ payload.size() - TURN_CHANNEL_HEADER_SIZE, channel->peer);
} else {
RTC_LOG(LS_WARNING) << ToString()
<< ": Received channel data for invalid channel, id="
@@ -773,34 +775,31 @@ void TurnServerAllocation::HandleChannelData(const char* data, size_t size) {
}
}
-void TurnServerAllocation::OnExternalPacket(
- rtc::AsyncPacketSocket* socket,
- const char* data,
- size_t size,
- const rtc::SocketAddress& addr,
- const int64_t& /* packet_time_us */) {
+void TurnServerAllocation::OnExternalPacket(rtc::AsyncPacketSocket* socket,
+ const rtc::ReceivedPacket& packet) {
RTC_DCHECK(external_socket_.get() == socket);
- auto channel = FindChannel(addr);
+ auto channel = FindChannel(packet.source_address());
if (channel != channels_.end()) {
// There is a channel bound to this address. Send as a channel message.
rtc::ByteBufferWriter buf;
buf.WriteUInt16(channel->id);
- buf.WriteUInt16(static_cast<uint16_t>(size));
- buf.WriteBytes(data, size);
+ buf.WriteUInt16(static_cast<uint16_t>(packet.payload().size()));
+ buf.WriteBytes(reinterpret_cast<const char*>(packet.payload().data()),
+ packet.payload().size());
server_->Send(&conn_, buf);
} else if (!server_->enable_permission_checks_ ||
- HasPermission(addr.ipaddr())) {
+ HasPermission(packet.source_address().ipaddr())) {
// No channel, but a permission exists. Send as a data indication.
TurnMessage msg(TURN_DATA_INDICATION);
msg.AddAttribute(std::make_unique<StunXorAddressAttribute>(
- STUN_ATTR_XOR_PEER_ADDRESS, addr));
- msg.AddAttribute(
- std::make_unique<StunByteStringAttribute>(STUN_ATTR_DATA, data, size));
+ STUN_ATTR_XOR_PEER_ADDRESS, packet.source_address()));
+ msg.AddAttribute(std::make_unique<StunByteStringAttribute>(
+ STUN_ATTR_DATA, packet.payload().data(), packet.payload().size()));
server_->SendStun(&conn_, &msg);
} else {
RTC_LOG(LS_WARNING)
<< ToString() << ": Received external packet without permission, peer="
- << addr.ToSensitiveString();
+ << packet.source_address().ToSensitiveString();
}
}
diff --git a/third_party/libwebrtc/p2p/base/turn_server.h b/third_party/libwebrtc/p2p/base/turn_server.h
index e951d089af..be42338a3b 100644
--- a/third_party/libwebrtc/p2p/base/turn_server.h
+++ b/third_party/libwebrtc/p2p/base/turn_server.h
@@ -26,6 +26,7 @@
#include "api/units/time_delta.h"
#include "p2p/base/port_interface.h"
#include "rtc_base/async_packet_socket.h"
+#include "rtc_base/network/received_packet.h"
#include "rtc_base/socket_address.h"
#include "rtc_base/ssl_adapter.h"
#include "rtc_base/third_party/sigslot/sigslot.h"
@@ -69,14 +70,14 @@ class TurnServerConnection {
// handles TURN messages (via HandleTurnMessage) and channel data messages
// (via HandleChannelData) for this allocation when received by the server.
// The object informs the server when its lifetime timer expires.
-class TurnServerAllocation : public sigslot::has_slots<> {
+class TurnServerAllocation {
public:
TurnServerAllocation(TurnServer* server_,
webrtc::TaskQueueBase* thread,
const TurnServerConnection& conn,
rtc::AsyncPacketSocket* server_socket,
absl::string_view key);
- ~TurnServerAllocation() override;
+ virtual ~TurnServerAllocation();
TurnServerConnection* conn() { return &conn_; }
const std::string& key() const { return key_; }
@@ -90,7 +91,7 @@ class TurnServerAllocation : public sigslot::has_slots<> {
std::string ToString() const;
void HandleTurnMessage(const TurnMessage* msg);
- void HandleChannelData(const char* data, size_t size);
+ void HandleChannelData(rtc::ArrayView<const uint8_t> payload);
private:
struct Channel {
@@ -114,10 +115,7 @@ class TurnServerAllocation : public sigslot::has_slots<> {
void HandleChannelBindRequest(const TurnMessage* msg);
void OnExternalPacket(rtc::AsyncPacketSocket* socket,
- const char* data,
- size_t size,
- const rtc::SocketAddress& addr,
- const int64_t& packet_time_us);
+ const rtc::ReceivedPacket& packet);
static webrtc::TimeDelta ComputeLifetime(const TurnMessage& msg);
bool HasPermission(const rtc::IPAddress& addr);
@@ -171,7 +169,7 @@ class TurnRedirectInterface {
class StunMessageObserver {
public:
virtual void ReceivedMessage(const TurnMessage* msg) = 0;
- virtual void ReceivedChannelData(const char* data, size_t size) = 0;
+ virtual void ReceivedChannelData(rtc::ArrayView<const uint8_t> payload) = 0;
virtual ~StunMessageObserver() {}
};
@@ -266,14 +264,11 @@ class TurnServer : public sigslot::has_slots<> {
private:
// All private member functions and variables should have access restricted to
// thread_. But compile-time annotations are missing for members access from
- // TurnServerAllocation (via friend declaration), and the On* methods, which
- // are called via sigslot.
+ // TurnServerAllocation (via friend declaration).
+
std::string GenerateNonce(int64_t now) const RTC_RUN_ON(thread_);
void OnInternalPacket(rtc::AsyncPacketSocket* socket,
- const char* data,
- size_t size,
- const rtc::SocketAddress& address,
- const int64_t& packet_time_us);
+ const rtc::ReceivedPacket& packet) RTC_RUN_ON(thread_);
void OnNewInternalConnection(rtc::Socket* socket);
@@ -282,8 +277,8 @@ class TurnServer : public sigslot::has_slots<> {
void OnInternalSocketClose(rtc::AsyncPacketSocket* socket, int err);
void HandleStunMessage(TurnServerConnection* conn,
- const char* data,
- size_t size) RTC_RUN_ON(thread_);
+ rtc::ArrayView<const uint8_t> payload)
+ RTC_RUN_ON(thread_);
void HandleBindingRequest(TurnServerConnection* conn, const StunMessage* msg)
RTC_RUN_ON(thread_);
void HandleAllocateRequest(TurnServerConnection* conn,
@@ -293,8 +288,6 @@ class TurnServer : public sigslot::has_slots<> {
bool GetKey(const StunMessage* msg, std::string* key) RTC_RUN_ON(thread_);
bool CheckAuthorization(TurnServerConnection* conn,
StunMessage* msg,
- const char* data,
- size_t size,
absl::string_view key) RTC_RUN_ON(thread_);
bool ValidateNonce(absl::string_view nonce) const RTC_RUN_ON(thread_);
diff --git a/third_party/libwebrtc/p2p/client/basic_port_allocator.cc b/third_party/libwebrtc/p2p/client/basic_port_allocator.cc
index b6cbf1fff9..e8255f1fd5 100644
--- a/third_party/libwebrtc/p2p/client/basic_port_allocator.cc
+++ b/third_party/libwebrtc/p2p/client/basic_port_allocator.cc
@@ -1308,8 +1308,11 @@ void AllocationSequence::Init() {
rtc::SocketAddress(network_->GetBestIP(), 0),
session_->allocator()->min_port(), session_->allocator()->max_port()));
if (udp_socket_) {
- udp_socket_->SignalReadPacket.connect(this,
- &AllocationSequence::OnReadPacket);
+ udp_socket_->RegisterReceivedPacketCallback(
+ [&](rtc::AsyncPacketSocket* socket,
+ const rtc::ReceivedPacket& packet) {
+ OnReadPacket(socket, packet);
+ });
}
// Continuing if `udp_socket_` is NULL, as local TCP and RelayPort using TCP
// are next available options to setup a communication channel.
@@ -1668,10 +1671,7 @@ void AllocationSequence::CreateTurnPort(const RelayServerConfig& config,
}
void AllocationSequence::OnReadPacket(rtc::AsyncPacketSocket* socket,
- const char* data,
- size_t size,
- const rtc::SocketAddress& remote_addr,
- const int64_t& packet_time_us) {
+ const rtc::ReceivedPacket& packet) {
RTC_DCHECK(socket == udp_socket_.get());
bool turn_port_found = false;
@@ -1683,9 +1683,8 @@ void AllocationSequence::OnReadPacket(rtc::AsyncPacketSocket* socket,
// the message type. The TurnPort will just ignore the message since it will
// not find any request by transaction ID.
for (auto* port : relay_ports_) {
- if (port->CanHandleIncomingPacketsFrom(remote_addr)) {
- if (port->HandleIncomingPacket(socket, data, size, remote_addr,
- packet_time_us)) {
+ if (port->CanHandleIncomingPacketsFrom(packet.source_address())) {
+ if (port->HandleIncomingPacket(socket, packet)) {
return;
}
turn_port_found = true;
@@ -1698,10 +1697,9 @@ void AllocationSequence::OnReadPacket(rtc::AsyncPacketSocket* socket,
// Pass the packet to the UdpPort if there is no matching TurnPort, or if
// the TURN server is also a STUN server.
if (!turn_port_found ||
- stun_servers.find(remote_addr) != stun_servers.end()) {
+ stun_servers.find(packet.source_address()) != stun_servers.end()) {
RTC_DCHECK(udp_port_->SharedSocket());
- udp_port_->HandleIncomingPacket(socket, data, size, remote_addr,
- packet_time_us);
+ udp_port_->HandleIncomingPacket(socket, packet);
}
}
}
diff --git a/third_party/libwebrtc/p2p/client/basic_port_allocator.h b/third_party/libwebrtc/p2p/client/basic_port_allocator.h
index 95bbdb183e..643904ab27 100644
--- a/third_party/libwebrtc/p2p/client/basic_port_allocator.h
+++ b/third_party/libwebrtc/p2p/client/basic_port_allocator.h
@@ -25,6 +25,7 @@
#include "rtc_base/checks.h"
#include "rtc_base/memory/always_valid_pointer.h"
#include "rtc_base/network.h"
+#include "rtc_base/network/received_packet.h"
#include "rtc_base/system/rtc_export.h"
#include "rtc_base/thread.h"
#include "rtc_base/thread_annotations.h"
@@ -330,7 +331,7 @@ class TurnPort;
// Performs the allocation of ports, in a sequenced (timed) manner, for a given
// network and IP address.
// This class is thread-compatible.
-class AllocationSequence : public sigslot::has_slots<> {
+class AllocationSequence {
public:
enum State {
kInit, // Initial state.
@@ -386,10 +387,7 @@ class AllocationSequence : public sigslot::has_slots<> {
void CreateRelayPorts();
void OnReadPacket(rtc::AsyncPacketSocket* socket,
- const char* data,
- size_t size,
- const rtc::SocketAddress& remote_addr,
- const int64_t& packet_time_us);
+ const rtc::ReceivedPacket& packet);
void OnPortDestroyed(PortInterface* port);
diff --git a/third_party/libwebrtc/p2p/client/basic_port_allocator_unittest.cc b/third_party/libwebrtc/p2p/client/basic_port_allocator_unittest.cc
index 55222a1be2..defcab01c9 100644
--- a/third_party/libwebrtc/p2p/client/basic_port_allocator_unittest.cc
+++ b/third_party/libwebrtc/p2p/client/basic_port_allocator_unittest.cc
@@ -163,7 +163,7 @@ class BasicPortAllocatorTestBase : public ::testing::Test,
// must be called.
nat_factory_(vss_.get(), kNatUdpAddr, kNatTcpAddr),
nat_socket_factory_(new rtc::BasicPacketSocketFactory(&nat_factory_)),
- stun_server_(TestStunServer::Create(fss_.get(), kStunAddr)),
+ stun_server_(TestStunServer::Create(fss_.get(), kStunAddr, thread_)),
turn_server_(rtc::Thread::Current(),
fss_.get(),
kTurnUdpIntAddr,
@@ -521,7 +521,7 @@ class BasicPortAllocatorTestBase : public ::testing::Test,
std::unique_ptr<rtc::NATServer> nat_server_;
rtc::NATSocketFactory nat_factory_;
std::unique_ptr<rtc::BasicPacketSocketFactory> nat_socket_factory_;
- std::unique_ptr<TestStunServer> stun_server_;
+ TestStunServer::StunServerPtr stun_server_;
TestTurnServer turn_server_;
rtc::FakeNetworkManager network_manager_;
std::unique_ptr<BasicPortAllocator> allocator_;
diff --git a/third_party/libwebrtc/p2p/stunprober/stun_prober.cc b/third_party/libwebrtc/p2p/stunprober/stun_prober.cc
index f5abf43beb..c60e7ede89 100644
--- a/third_party/libwebrtc/p2p/stunprober/stun_prober.cc
+++ b/third_party/libwebrtc/p2p/stunprober/stun_prober.cc
@@ -10,21 +10,21 @@
#include "p2p/stunprober/stun_prober.h"
+#include <cstdint>
#include <map>
#include <memory>
#include <set>
#include <string>
#include <utility>
+#include "api/array_view.h"
#include "api/packet_socket_factory.h"
#include "api/task_queue/pending_task_safety_flag.h"
#include "api/transport/stun.h"
#include "api/units/time_delta.h"
#include "rtc_base/async_packet_socket.h"
-#include "rtc_base/async_resolver_interface.h"
#include "rtc_base/checks.h"
-#include "rtc_base/helpers.h"
-#include "rtc_base/logging.h"
+#include "rtc_base/network/received_packet.h"
#include "rtc_base/thread.h"
#include "rtc_base/time_utils.h"
@@ -60,7 +60,7 @@ class StunProber::Requester : public sigslot::has_slots<> {
rtc::IPAddress server_addr;
int64_t rtt() { return received_time_ms - sent_time_ms; }
- void ProcessResponse(const char* buf, size_t buf_len);
+ void ProcessResponse(rtc::ArrayView<const uint8_t> payload);
};
// StunProber provides `server_ips` for Requester to probe. For shared
@@ -80,10 +80,7 @@ class StunProber::Requester : public sigslot::has_slots<> {
void SendStunRequest();
void OnStunResponseReceived(rtc::AsyncPacketSocket* socket,
- const char* buf,
- size_t size,
- const rtc::SocketAddress& addr,
- const int64_t& packet_time_us);
+ const rtc::ReceivedPacket& packet);
const std::vector<Request*>& requests() { return requests_; }
@@ -121,8 +118,10 @@ StunProber::Requester::Requester(
response_packet_(new rtc::ByteBufferWriter(nullptr, kMaxUdpBufferSize)),
server_ips_(server_ips),
thread_checker_(prober->thread_checker_) {
- socket_->SignalReadPacket.connect(
- this, &StunProber::Requester::OnStunResponseReceived);
+ socket_->RegisterReceivedPacketCallback(
+ [&](rtc::AsyncPacketSocket* socket, const rtc::ReceivedPacket& packet) {
+ OnStunResponseReceived(socket, packet);
+ });
}
StunProber::Requester::~Requester() {
@@ -170,10 +169,10 @@ void StunProber::Requester::SendStunRequest() {
RTC_DCHECK(static_cast<size_t>(num_request_sent_) <= server_ips_.size());
}
-void StunProber::Requester::Request::ProcessResponse(const char* buf,
- size_t buf_len) {
+void StunProber::Requester::Request::ProcessResponse(
+ rtc::ArrayView<const uint8_t> payload) {
int64_t now = rtc::TimeMillis();
- rtc::ByteBufferReader message(buf, buf_len);
+ rtc::ByteBufferReader message(payload);
cricket::StunMessage stun_response;
if (!stun_response.Read(&message)) {
// Invalid or incomplete STUN packet.
@@ -201,13 +200,10 @@ void StunProber::Requester::Request::ProcessResponse(const char* buf,
void StunProber::Requester::OnStunResponseReceived(
rtc::AsyncPacketSocket* socket,
- const char* buf,
- size_t size,
- const rtc::SocketAddress& addr,
- const int64_t& /* packet_time_us */) {
+ const rtc::ReceivedPacket& packet) {
RTC_DCHECK(thread_checker_.IsCurrent());
RTC_DCHECK(socket_);
- Request* request = GetRequestByAddress(addr.ipaddr());
+ Request* request = GetRequestByAddress(packet.source_address().ipaddr());
if (!request) {
// Something is wrong, finish the test.
prober_->ReportOnFinished(GENERIC_FAILURE);
@@ -215,7 +211,7 @@ void StunProber::Requester::OnStunResponseReceived(
}
num_response_received_++;
- request->ProcessResponse(buf, size);
+ request->ProcessResponse(packet.payload());
}
StunProber::Requester::Request* StunProber::Requester::GetRequestByAddress(
diff --git a/third_party/libwebrtc/p2p/stunprober/stun_prober.h b/third_party/libwebrtc/p2p/stunprober/stun_prober.h
index 3f0f4a2476..07f3a17233 100644
--- a/third_party/libwebrtc/p2p/stunprober/stun_prober.h
+++ b/third_party/libwebrtc/p2p/stunprober/stun_prober.h
@@ -19,8 +19,6 @@
#include "api/async_dns_resolver.h"
#include "api/sequence_checker.h"
#include "api/task_queue/pending_task_safety_flag.h"
-#include "rtc_base/byte_buffer.h"
-#include "rtc_base/ip_address.h"
#include "rtc_base/network.h"
#include "rtc_base/socket_address.h"
#include "rtc_base/system/rtc_export.h"
diff --git a/third_party/libwebrtc/p2p/stunprober/stun_prober_unittest.cc b/third_party/libwebrtc/p2p/stunprober/stun_prober_unittest.cc
index ca20fccb6b..1aa2be2844 100644
--- a/third_party/libwebrtc/p2p/stunprober/stun_prober_unittest.cc
+++ b/third_party/libwebrtc/p2p/stunprober/stun_prober_unittest.cc
@@ -44,8 +44,10 @@ class StunProberTest : public ::testing::Test {
: ss_(std::make_unique<rtc::VirtualSocketServer>()),
main_(ss_.get()),
result_(StunProber::SUCCESS),
- stun_server_1_(cricket::TestStunServer::Create(ss_.get(), kStunAddr1)),
- stun_server_2_(cricket::TestStunServer::Create(ss_.get(), kStunAddr2)) {
+ stun_server_1_(
+ cricket::TestStunServer::Create(ss_.get(), kStunAddr1, main_)),
+ stun_server_2_(
+ cricket::TestStunServer::Create(ss_.get(), kStunAddr2, main_)) {
stun_server_1_->set_fake_stun_addr(kStunMappedAddr);
stun_server_2_->set_fake_stun_addr(kStunMappedAddr);
rtc::InitializeSSL();
@@ -57,8 +59,8 @@ class StunProberTest : public ::testing::Test {
void CreateProber(rtc::PacketSocketFactory* socket_factory,
std::vector<const rtc::Network*> networks) {
- prober_ = std::make_unique<StunProber>(
- socket_factory, rtc::Thread::Current(), std::move(networks));
+ prober_ = std::make_unique<StunProber>(socket_factory, &main_,
+ std::move(networks));
}
void StartProbing(rtc::PacketSocketFactory* socket_factory,
@@ -137,8 +139,8 @@ class StunProberTest : public ::testing::Test {
std::unique_ptr<StunProber> prober_;
int result_ = 0;
bool stopped_ = false;
- std::unique_ptr<cricket::TestStunServer> stun_server_1_;
- std::unique_ptr<cricket::TestStunServer> stun_server_2_;
+ cricket::TestStunServer::StunServerPtr stun_server_1_;
+ cricket::TestStunServer::StunServerPtr stun_server_2_;
StunProber::Stats stats_;
};
diff --git a/third_party/libwebrtc/pc/BUILD.gn b/third_party/libwebrtc/pc/BUILD.gn
index 7c22a26d12..e9549cdfd8 100644
--- a/third_party/libwebrtc/pc/BUILD.gn
+++ b/third_party/libwebrtc/pc/BUILD.gn
@@ -314,6 +314,15 @@ rtc_source_set("jsep_transport_controller") {
]
}
+rtc_source_set("media_factory") {
+ sources = [ "media_factory.h" ]
+ deps = [
+ "../api/environment",
+ "../call:call_interfaces",
+ "../media:rtc_media_base",
+ ]
+}
+
rtc_source_set("media_session") {
visibility = [ "*" ] # Used by Chrome
sources = [
@@ -730,6 +739,7 @@ rtc_library("media_protocol_names") {
rtc_source_set("peerconnection") {
# TODO(bugs.webrtc.org/13661): Reduce visibility if possible
visibility = [ "*" ] # Used by Chromium and others
+ allow_poison = [ "environment_construction" ]
cflags = []
sources = []
@@ -919,6 +929,7 @@ rtc_library("connection_context") {
"connection_context.h",
]
deps = [
+ ":media_factory",
"../api:callfactory_api",
"../api:field_trials_view",
"../api:libjingle_peerconnection_api",
@@ -926,6 +937,7 @@ rtc_library("connection_context") {
"../api:refcountedbase",
"../api:scoped_refptr",
"../api:sequence_checker",
+ "../api/environment",
"../api/neteq:neteq_api",
"../api/transport:field_trial_based_config",
"../api/transport:sctp_transport_factory_interface",
@@ -1223,6 +1235,7 @@ rtc_source_set("peer_connection") {
"../api:turn_customizer",
"../api/adaptation:resource_adaptation_api",
"../api/crypto:options",
+ "../api/environment",
"../api/rtc_event_log",
"../api/task_queue:pending_task_safety_flag",
"../api/transport:bitrate_settings",
@@ -1486,6 +1499,7 @@ rtc_library("media_stream_observer") {
rtc_source_set("peer_connection_factory") {
# TODO(bugs.webrtc.org/13661): Reduce visibility if possible
visibility = [ "*" ] # Known to be used externally
+ allow_poison = [ "environment_construction" ]
sources = [
"peer_connection_factory.cc",
"peer_connection_factory.h",
@@ -1510,10 +1524,11 @@ rtc_source_set("peer_connection_factory") {
"../api:rtp_parameters",
"../api:scoped_refptr",
"../api:sequence_checker",
+ "../api/environment",
+ "../api/environment:environment_factory",
"../api/metronome",
"../api/neteq:neteq_api",
"../api/rtc_event_log:rtc_event_log",
- "../api/task_queue:task_queue",
"../api/transport:bitrate_settings",
"../api/transport:network_control",
"../api/transport:sctp_transport_factory_interface",
@@ -1525,6 +1540,7 @@ rtc_source_set("peer_connection_factory") {
"../p2p:rtc_p2p",
"../pc:audio_track",
"../pc:connection_context",
+ "../pc:media_factory",
"../pc:media_stream",
"../pc:rtp_parameters_conversion",
"../pc:session_description",
@@ -2051,6 +2067,7 @@ rtc_source_set("legacy_stats_collector_interface") {
rtc_source_set("libjingle_peerconnection") {
# TODO(bugs.webrtc.org/13661): Reduce visibility if possible
visibility = [ "*" ] # Used by Chrome and others
+ allow_poison = [ "environment_construction" ]
deps = [
":peerconnection",
@@ -2362,6 +2379,7 @@ if (rtc_include_tests && !build_with_chromium) {
":dtls_srtp_transport",
":dtls_transport",
":dtmf_sender",
+ ":enable_fake_media",
":ice_server_parsing",
":integration_test_helpers",
":jitter_buffer_delay",
@@ -2401,10 +2419,13 @@ if (rtc_include_tests && !build_with_chromium) {
":webrtc_sdp",
"../api:array_view",
"../api:audio_options_api",
+ "../api:callfactory_api",
"../api:candidate",
"../api:create_peerconnection_factory",
"../api:dtls_transport_interface",
"../api:dtmf_sender_interface",
+ "../api:enable_media",
+ "../api:enable_media_with_defaults",
"../api:fake_frame_decryptor",
"../api:fake_frame_encryptor",
"../api:field_trials_view",
@@ -2429,6 +2450,7 @@ if (rtc_include_tests && !build_with_chromium) {
"../api/crypto:frame_decryptor_interface",
"../api/crypto:frame_encryptor_interface",
"../api/crypto:options",
+ "../api/environment:environment_factory",
"../api/rtc_event_log",
"../api/rtc_event_log:rtc_event_log_factory",
"../api/task_queue",
@@ -2457,7 +2479,6 @@ if (rtc_include_tests && !build_with_chromium) {
"../media:rid_description",
"../media:rtc_data_sctp_transport_internal",
"../media:rtc_media_config",
- "../media:rtc_media_engine_defaults",
"../media:stream_params",
"../modules/audio_device:audio_device_api",
"../modules/audio_processing:audio_processing_statistics",
@@ -2520,7 +2541,6 @@ if (rtc_include_tests && !build_with_chromium) {
":libjingle_peerconnection",
":pc_test_utils",
":rtc_pc",
- "../api:callfactory_api",
"../api:rtc_event_log_output_file",
"../api:rtc_stats_api",
"../api:rtp_parameters",
@@ -2627,9 +2647,9 @@ if (rtc_include_tests && !build_with_chromium) {
":video_track_source",
"../api:array_view",
"../api:audio_options_api",
- "../api:callfactory_api",
"../api:candidate",
"../api:create_peerconnection_factory",
+ "../api:enable_media_with_defaults",
"../api:fake_frame_decryptor",
"../api:fake_frame_encryptor",
"../api:field_trials_view",
@@ -2665,10 +2685,8 @@ if (rtc_include_tests && !build_with_chromium) {
"../call:call_interfaces",
"../call/adaptation:resource_adaptation_test_utilities",
"../logging:fake_rtc_event_log",
- "../media:rtc_audio_video",
"../media:rtc_media_base",
"../media:rtc_media_config",
- "../media:rtc_media_engine_defaults",
"../media:rtc_media_tests_utils",
"../media:stream_params",
"../modules/audio_device:audio_device_api",
@@ -2719,6 +2737,24 @@ if (rtc_include_tests && !build_with_chromium) {
]
}
+ rtc_library("enable_fake_media") {
+ testonly = true
+ visibility = [ ":*" ]
+ sources = [
+ "test/enable_fake_media.cc",
+ "test/enable_fake_media.h",
+ ]
+ deps = [
+ ":media_factory",
+ "../api:libjingle_peerconnection_api",
+ "../api/environment",
+ "../call:call_interfaces",
+ "../media:rtc_media_tests_utils",
+ "../rtc_base:checks",
+ ]
+ absl_deps = [ "//third_party/abseil-cpp/absl/base:nullability" ]
+ }
+
rtc_library("pc_test_utils") {
testonly = true
sources = [
@@ -2751,6 +2787,7 @@ if (rtc_include_tests && !build_with_chromium) {
deps = [
":channel",
":channel_interface",
+ ":enable_fake_media",
":jitter_buffer_delay",
":libjingle_peerconnection",
":peer_connection_internal",
@@ -2778,6 +2815,7 @@ if (rtc_include_tests && !build_with_chromium) {
"../api:sequence_checker",
"../api/audio:audio_mixer_api",
"../api/audio_codecs:audio_codecs_api",
+ "../api/environment:environment_factory",
"../api/task_queue",
"../api/task_queue:default_task_queue_factory",
"../api/units:time_delta",
diff --git a/third_party/libwebrtc/pc/audio_rtp_receiver.cc b/third_party/libwebrtc/pc/audio_rtp_receiver.cc
index a8659de5f9..6e7ca6d0b5 100644
--- a/third_party/libwebrtc/pc/audio_rtp_receiver.cc
+++ b/third_party/libwebrtc/pc/audio_rtp_receiver.cc
@@ -278,7 +278,7 @@ std::vector<RtpSource> AudioRtpReceiver::GetSources() const {
}
void AudioRtpReceiver::SetDepacketizerToDecoderFrameTransformer(
- rtc::scoped_refptr<webrtc::FrameTransformerInterface> frame_transformer) {
+ rtc::scoped_refptr<FrameTransformerInterface> frame_transformer) {
RTC_DCHECK_RUN_ON(worker_thread_);
if (media_channel_) {
media_channel_->SetDepacketizerToDecoderFrameTransformer(
diff --git a/third_party/libwebrtc/pc/audio_rtp_receiver.h b/third_party/libwebrtc/pc/audio_rtp_receiver.h
index 86c42d532a..36cbdffc35 100644
--- a/third_party/libwebrtc/pc/audio_rtp_receiver.h
+++ b/third_party/libwebrtc/pc/audio_rtp_receiver.h
@@ -118,8 +118,7 @@ class AudioRtpReceiver : public ObserverInterface,
std::vector<RtpSource> GetSources() const override;
int AttachmentId() const override { return attachment_id_; }
void SetDepacketizerToDecoderFrameTransformer(
- rtc::scoped_refptr<webrtc::FrameTransformerInterface> frame_transformer)
- override;
+ rtc::scoped_refptr<FrameTransformerInterface> frame_transformer) override;
private:
void RestartMediaChannel(absl::optional<uint32_t> ssrc)
diff --git a/third_party/libwebrtc/pc/audio_rtp_receiver_unittest.cc b/third_party/libwebrtc/pc/audio_rtp_receiver_unittest.cc
index 9eb20c982f..e031f90359 100644
--- a/third_party/libwebrtc/pc/audio_rtp_receiver_unittest.cc
+++ b/third_party/libwebrtc/pc/audio_rtp_receiver_unittest.cc
@@ -98,7 +98,7 @@ TEST_F(AudioRtpReceiverTest, VolumesSetBeforeStartingAreRespected) {
// thread when a media channel pointer is passed to the receiver via the
// constructor.
TEST(AudioRtpReceiver, OnChangedNotificationsAfterConstruction) {
- webrtc::test::RunLoop loop;
+ test::RunLoop loop;
auto* thread = rtc::Thread::Current(); // Points to loop's thread.
cricket::MockVoiceMediaReceiveChannelInterface receive_channel;
auto receiver = rtc::make_ref_counted<AudioRtpReceiver>(
diff --git a/third_party/libwebrtc/pc/audio_track.h b/third_party/libwebrtc/pc/audio_track.h
index ae326b304b..92c3141d8a 100644
--- a/third_party/libwebrtc/pc/audio_track.h
+++ b/third_party/libwebrtc/pc/audio_track.h
@@ -58,7 +58,7 @@ class AudioTrack : public MediaStreamTrack<AudioTrackInterface>,
private:
const rtc::scoped_refptr<AudioSourceInterface> audio_source_;
- RTC_NO_UNIQUE_ADDRESS webrtc::SequenceChecker signaling_thread_checker_;
+ RTC_NO_UNIQUE_ADDRESS SequenceChecker signaling_thread_checker_;
};
} // namespace webrtc
diff --git a/third_party/libwebrtc/pc/channel.cc b/third_party/libwebrtc/pc/channel.cc
index 0024ba0e35..7a1f55d9e9 100644
--- a/third_party/libwebrtc/pc/channel.cc
+++ b/third_party/libwebrtc/pc/channel.cc
@@ -79,7 +79,7 @@ struct StreamFinder {
} // namespace
void MediaChannelParametersFromMediaDescription(
- const RtpMediaContentDescription* desc,
+ const MediaContentDescription* desc,
const RtpHeaderExtensions& extensions,
bool is_stream_active,
MediaChannelParameters* params) {
@@ -97,7 +97,7 @@ void MediaChannelParametersFromMediaDescription(
}
void RtpSendParametersFromMediaDescription(
- const RtpMediaContentDescription* desc,
+ const MediaContentDescription* desc,
webrtc::RtpExtension::Filter extensions_filter,
SenderParameters* send_params) {
RtpHeaderExtensions extensions =
@@ -875,7 +875,7 @@ bool VoiceChannel::SetLocalContent_w(const MediaContentDescription* content,
AudioReceiverParameters recv_params = last_recv_params_;
MediaChannelParametersFromMediaDescription(
- content->as_audio(), header_extensions,
+ content, header_extensions,
webrtc::RtpTransceiverDirectionHasRecv(content->direction()),
&recv_params);
@@ -927,8 +927,8 @@ bool VoiceChannel::SetRemoteContent_w(const MediaContentDescription* content,
RTC_LOG(LS_INFO) << "Setting remote voice description for " << ToString();
AudioSenderParameter send_params = last_send_params_;
- RtpSendParametersFromMediaDescription(content->as_audio(),
- extensions_filter(), &send_params);
+ RtpSendParametersFromMediaDescription(content, extensions_filter(),
+ &send_params);
send_params.mid = mid();
bool parameters_applied =
@@ -1016,7 +1016,7 @@ bool VideoChannel::SetLocalContent_w(const MediaContentDescription* content,
VideoReceiverParameters recv_params = last_recv_params_;
MediaChannelParametersFromMediaDescription(
- content->as_video(), header_extensions,
+ content, header_extensions,
webrtc::RtpTransceiverDirectionHasRecv(content->direction()),
&recv_params);
@@ -1100,7 +1100,7 @@ bool VideoChannel::SetLocalContent_w(const MediaContentDescription* content,
last_send_params_ = send_params;
}
- if (!UpdateLocalStreams_w(content->as_video()->streams(), type, error_desc)) {
+ if (!UpdateLocalStreams_w(content->streams(), type, error_desc)) {
RTC_DCHECK(!error_desc.empty());
return false;
}
@@ -1128,13 +1128,11 @@ bool VideoChannel::SetRemoteContent_w(const MediaContentDescription* content,
TRACE_EVENT0("webrtc", "VideoChannel::SetRemoteContent_w");
RTC_LOG(LS_INFO) << "Setting remote video description for " << ToString();
- const VideoContentDescription* video = content->as_video();
-
VideoSenderParameters send_params = last_send_params_;
- RtpSendParametersFromMediaDescription(video, extensions_filter(),
+ RtpSendParametersFromMediaDescription(content, extensions_filter(),
&send_params);
send_params.mid = mid();
- send_params.conference_mode = video->conference_mode();
+ send_params.conference_mode = content->conference_mode();
VideoReceiverParameters recv_params = last_recv_params_;
@@ -1145,15 +1143,15 @@ bool VideoChannel::SetRemoteContent_w(const MediaContentDescription* content,
// instead.
bool needs_recv_params_update = false;
if (type == SdpType::kAnswer || type == SdpType::kPrAnswer) {
- webrtc::flat_set<const VideoCodec*> matched_codecs;
- for (VideoCodec& recv_codec : recv_params.codecs) {
- if (absl::c_any_of(matched_codecs, [&](const VideoCodec* c) {
+ webrtc::flat_set<const Codec*> matched_codecs;
+ for (Codec& recv_codec : recv_params.codecs) {
+ if (absl::c_any_of(matched_codecs, [&](const Codec* c) {
return recv_codec.Matches(*c);
})) {
continue;
}
- std::vector<const VideoCodec*> send_codecs =
+ std::vector<const Codec*> send_codecs =
FindAllMatchingCodecs(send_params.codecs, recv_codec);
if (send_codecs.empty()) {
continue;
@@ -1161,7 +1159,7 @@ bool VideoChannel::SetRemoteContent_w(const MediaContentDescription* content,
bool may_ignore_packetization = false;
bool has_matching_packetization = false;
- for (const VideoCodec* send_codec : send_codecs) {
+ for (const Codec* send_codec : send_codecs) {
if (!send_codec->packetization.has_value() &&
recv_codec.packetization.has_value()) {
may_ignore_packetization = true;
diff --git a/third_party/libwebrtc/pc/channel_unittest.cc b/third_party/libwebrtc/pc/channel_unittest.cc
index c675cd0446..98a61ea673 100644
--- a/third_party/libwebrtc/pc/channel_unittest.cc
+++ b/third_party/libwebrtc/pc/channel_unittest.cc
@@ -2094,12 +2094,12 @@ TEST_F(VideoChannelSingleThreadTest, TestSetLocalOfferWithPacketization) {
EXPECT_TRUE(channel1_->SetLocalContent(&video, SdpType::kOffer, err));
EXPECT_THAT(media_send_channel1_impl()->send_codecs(), testing::IsEmpty());
ASSERT_THAT(media_receive_channel1_impl()->recv_codecs(), testing::SizeIs(2));
- EXPECT_TRUE(media_receive_channel1_impl()->recv_codecs()[0].Matches(
- kVp8Codec, &field_trials_));
+ EXPECT_TRUE(
+ media_receive_channel1_impl()->recv_codecs()[0].Matches(kVp8Codec));
EXPECT_EQ(media_receive_channel1_impl()->recv_codecs()[0].packetization,
absl::nullopt);
- EXPECT_TRUE(media_receive_channel1_impl()->recv_codecs()[1].Matches(
- vp9_codec, &field_trials_));
+ EXPECT_TRUE(
+ media_receive_channel1_impl()->recv_codecs()[1].Matches(vp9_codec));
EXPECT_EQ(media_receive_channel1_impl()->recv_codecs()[1].packetization,
cricket::kPacketizationParamRaw);
}
@@ -2118,12 +2118,10 @@ TEST_F(VideoChannelSingleThreadTest, TestSetRemoteOfferWithPacketization) {
EXPECT_TRUE(err.empty());
EXPECT_THAT(media_receive_channel1_impl()->recv_codecs(), testing::IsEmpty());
ASSERT_THAT(media_send_channel1_impl()->send_codecs(), testing::SizeIs(2));
- EXPECT_TRUE(media_send_channel1_impl()->send_codecs()[0].Matches(
- kVp8Codec, &field_trials_));
+ EXPECT_TRUE(media_send_channel1_impl()->send_codecs()[0].Matches(kVp8Codec));
EXPECT_EQ(media_send_channel1_impl()->send_codecs()[0].packetization,
absl::nullopt);
- EXPECT_TRUE(media_send_channel1_impl()->send_codecs()[1].Matches(
- vp9_codec, &field_trials_));
+ EXPECT_TRUE(media_send_channel1_impl()->send_codecs()[1].Matches(vp9_codec));
EXPECT_EQ(media_send_channel1_impl()->send_codecs()[1].packetization,
cricket::kPacketizationParamRaw);
}
@@ -2143,21 +2141,19 @@ TEST_F(VideoChannelSingleThreadTest, TestSetAnswerWithPacketization) {
EXPECT_TRUE(channel1_->SetRemoteContent(&video, SdpType::kAnswer, err));
EXPECT_TRUE(err.empty());
ASSERT_THAT(media_receive_channel1_impl()->recv_codecs(), testing::SizeIs(2));
- EXPECT_TRUE(media_receive_channel1_impl()->recv_codecs()[0].Matches(
- kVp8Codec, &field_trials_));
+ EXPECT_TRUE(
+ media_receive_channel1_impl()->recv_codecs()[0].Matches(kVp8Codec));
EXPECT_EQ(media_receive_channel1_impl()->recv_codecs()[0].packetization,
absl::nullopt);
- EXPECT_TRUE(media_receive_channel1_impl()->recv_codecs()[1].Matches(
- vp9_codec, &field_trials_));
+ EXPECT_TRUE(
+ media_receive_channel1_impl()->recv_codecs()[1].Matches(vp9_codec));
EXPECT_EQ(media_receive_channel1_impl()->recv_codecs()[1].packetization,
cricket::kPacketizationParamRaw);
EXPECT_THAT(media_send_channel1_impl()->send_codecs(), testing::SizeIs(2));
- EXPECT_TRUE(media_send_channel1_impl()->send_codecs()[0].Matches(
- kVp8Codec, &field_trials_));
+ EXPECT_TRUE(media_send_channel1_impl()->send_codecs()[0].Matches(kVp8Codec));
EXPECT_EQ(media_send_channel1_impl()->send_codecs()[0].packetization,
absl::nullopt);
- EXPECT_TRUE(media_send_channel1_impl()->send_codecs()[1].Matches(
- vp9_codec, &field_trials_));
+ EXPECT_TRUE(media_send_channel1_impl()->send_codecs()[1].Matches(vp9_codec));
EXPECT_EQ(media_send_channel1_impl()->send_codecs()[1].packetization,
cricket::kPacketizationParamRaw);
}
diff --git a/third_party/libwebrtc/pc/connection_context.cc b/third_party/libwebrtc/pc/connection_context.cc
index f436e27c0a..df4522bf13 100644
--- a/third_party/libwebrtc/pc/connection_context.cc
+++ b/third_party/libwebrtc/pc/connection_context.cc
@@ -14,9 +14,11 @@
#include <utility>
#include <vector>
+#include "api/environment/environment.h"
#include "api/transport/field_trial_based_config.h"
#include "media/base/media_engine.h"
#include "media/sctp/sctp_transport_factory.h"
+#include "pc/media_factory.h"
#include "rtc_base/helpers.h"
#include "rtc_base/internal/default_socket_server.h"
#include "rtc_base/socket_server.h"
@@ -77,12 +79,14 @@ std::unique_ptr<SctpTransportFactoryInterface> MaybeCreateSctpFactory(
// Static
rtc::scoped_refptr<ConnectionContext> ConnectionContext::Create(
+ const Environment& env,
PeerConnectionFactoryDependencies* dependencies) {
return rtc::scoped_refptr<ConnectionContext>(
- new ConnectionContext(dependencies));
+ new ConnectionContext(env, dependencies));
}
ConnectionContext::ConnectionContext(
+ const Environment& env,
PeerConnectionFactoryDependencies* dependencies)
: network_thread_(MaybeStartNetworkThread(dependencies->network_thread,
owned_socket_factory_,
@@ -96,18 +100,21 @@ ConnectionContext::ConnectionContext(
}),
signaling_thread_(MaybeWrapThread(dependencies->signaling_thread,
wraps_current_thread_)),
- trials_(dependencies->trials ? std::move(dependencies->trials)
- : std::make_unique<FieldTrialBasedConfig>()),
- media_engine_(std::move(dependencies->media_engine)),
+ env_(env),
+ media_engine_(
+ dependencies->media_factory != nullptr
+ ? dependencies->media_factory->CreateMediaEngine(env_,
+ *dependencies)
+ : nullptr),
network_monitor_factory_(
std::move(dependencies->network_monitor_factory)),
default_network_manager_(std::move(dependencies->network_manager)),
- call_factory_(std::move(dependencies->call_factory)),
+ call_factory_(std::move(dependencies->media_factory)),
default_socket_factory_(std::move(dependencies->packet_socket_factory)),
sctp_factory_(
MaybeCreateSctpFactory(std::move(dependencies->sctp_factory),
network_thread(),
- *trials_.get())),
+ env_.field_trials())),
use_rtx_(true) {
RTC_DCHECK_RUN_ON(signaling_thread_);
RTC_DCHECK(!(default_network_manager_ && network_monitor_factory_))
@@ -150,7 +157,7 @@ ConnectionContext::ConnectionContext(
// If network_monitor_factory_ is non-null, it will be used to create a
// network monitor while on the network thread.
default_network_manager_ = std::make_unique<rtc::BasicNetworkManager>(
- network_monitor_factory_.get(), socket_factory, &field_trials());
+ network_monitor_factory_.get(), socket_factory, &env_.field_trials());
}
if (!default_socket_factory_) {
default_socket_factory_ =
diff --git a/third_party/libwebrtc/pc/connection_context.h b/third_party/libwebrtc/pc/connection_context.h
index 399e7c2b45..893a3b0e52 100644
--- a/third_party/libwebrtc/pc/connection_context.h
+++ b/third_party/libwebrtc/pc/connection_context.h
@@ -15,6 +15,7 @@
#include <string>
#include "api/call/call_factory_interface.h"
+#include "api/environment/environment.h"
#include "api/field_trials_view.h"
#include "api/media_stream_interface.h"
#include "api/peer_connection_interface.h"
@@ -39,8 +40,6 @@ class UniqueRandomIdGenerator;
namespace webrtc {
-class RtcEventLog;
-
// This class contains resources needed by PeerConnection and associated
// objects. A reference to this object is passed to each PeerConnection. The
// methods on this object are assumed not to change the state in any way that
@@ -54,6 +53,7 @@ class ConnectionContext final
// The Dependencies class allows simple management of all new dependencies
// being added to the ConnectionContext.
static rtc::scoped_refptr<ConnectionContext> Create(
+ const Environment& env,
PeerConnectionFactoryDependencies* dependencies);
// This class is not copyable or movable.
@@ -76,11 +76,16 @@ class ConnectionContext final
rtc::Thread* network_thread() { return network_thread_; }
const rtc::Thread* network_thread() const { return network_thread_; }
+ // Environment associated with the PeerConnectionFactory.
+ // Note: environments are different for different PeerConnections,
+ // but they are not supposed to change after creating the PeerConnection.
+ const Environment& env() const { return env_; }
+
// Field trials associated with the PeerConnectionFactory.
// Note: that there can be different field trials for different
// PeerConnections (but they are not supposed change after creating the
// PeerConnection).
- const FieldTrialsView& field_trials() const { return *trials_.get(); }
+ const FieldTrialsView& field_trials() const { return env_.field_trials(); }
// Accessors only used from the PeerConnectionFactory class
rtc::NetworkManager* default_network_manager() {
@@ -91,7 +96,7 @@ class ConnectionContext final
RTC_DCHECK_RUN_ON(signaling_thread_);
return default_socket_factory_.get();
}
- CallFactoryInterface* call_factory() {
+ MediaFactory* call_factory() {
RTC_DCHECK_RUN_ON(worker_thread());
return call_factory_.get();
}
@@ -106,7 +111,8 @@ class ConnectionContext final
void set_use_rtx(bool use_rtx) { use_rtx_ = use_rtx; }
protected:
- explicit ConnectionContext(PeerConnectionFactoryDependencies* dependencies);
+ ConnectionContext(const Environment& env,
+ PeerConnectionFactoryDependencies* dependencies);
friend class rtc::RefCountedNonVirtual<ConnectionContext>;
~ConnectionContext();
@@ -122,8 +128,7 @@ class ConnectionContext final
AlwaysValidPointer<rtc::Thread> const worker_thread_;
rtc::Thread* const signaling_thread_;
- // Accessed both on signaling thread and worker thread.
- std::unique_ptr<FieldTrialsView> const trials_;
+ const Environment env_;
// This object is const over the lifetime of the ConnectionContext, and is
// only altered in the destructor.
@@ -138,7 +143,7 @@ class ConnectionContext final
RTC_GUARDED_BY(signaling_thread_);
std::unique_ptr<rtc::NetworkManager> default_network_manager_
RTC_GUARDED_BY(signaling_thread_);
- std::unique_ptr<webrtc::CallFactoryInterface> const call_factory_
+ std::unique_ptr<MediaFactory> const call_factory_
RTC_GUARDED_BY(worker_thread());
std::unique_ptr<rtc::PacketSocketFactory> default_socket_factory_
diff --git a/third_party/libwebrtc/pc/data_channel_controller_unittest.cc b/third_party/libwebrtc/pc/data_channel_controller_unittest.cc
index 3b8adb6819..7d4e60467e 100644
--- a/third_party/libwebrtc/pc/data_channel_controller_unittest.cc
+++ b/third_party/libwebrtc/pc/data_channel_controller_unittest.cc
@@ -27,7 +27,7 @@ namespace {
using ::testing::NiceMock;
using ::testing::Return;
-class MockDataChannelTransport : public webrtc::DataChannelTransportInterface {
+class MockDataChannelTransport : public DataChannelTransportInterface {
public:
~MockDataChannelTransport() override {}
diff --git a/third_party/libwebrtc/pc/data_channel_integrationtest.cc b/third_party/libwebrtc/pc/data_channel_integrationtest.cc
index faec76d03e..5a8004c72a 100644
--- a/third_party/libwebrtc/pc/data_channel_integrationtest.cc
+++ b/third_party/libwebrtc/pc/data_channel_integrationtest.cc
@@ -90,7 +90,7 @@ class FakeClockForTest : public rtc::ScopedFakeClock {
// Some things use a time of "0" as a special value, so we need to start out
// the fake clock at a nonzero time.
// TODO(deadbeef): Fix this.
- AdvanceTime(webrtc::TimeDelta::Seconds(1));
+ AdvanceTime(TimeDelta::Seconds(1));
}
// Explicit handle.
@@ -422,7 +422,7 @@ TEST_P(DataChannelIntegrationTest, CalleeClosesSctpDataChannel) {
TEST_P(DataChannelIntegrationTest, SctpDataChannelConfigSentToOtherSide) {
ASSERT_TRUE(CreatePeerConnectionWrappers());
ConnectFakeSignaling();
- webrtc::DataChannelInit init;
+ DataChannelInit init;
init.id = 53;
init.maxRetransmits = 52;
caller()->CreateDataChannel("data-channel", &init);
@@ -453,7 +453,7 @@ TEST_P(DataChannelIntegrationTest, StressTestUnorderedSctpDataChannel) {
// Normal procedure, but with unordered data channel config.
ASSERT_TRUE(CreatePeerConnectionWrappers());
ConnectFakeSignaling();
- webrtc::DataChannelInit init;
+ DataChannelInit init;
init.ordered = false;
caller()->CreateDataChannel(&init);
caller()->CreateAndSetAndSignalOffer();
@@ -515,7 +515,7 @@ TEST_P(DataChannelIntegrationTest, StressTestOpenCloseChannelNoDelay) {
const size_t kIterations = 10;
bool has_negotiated = false;
- webrtc::DataChannelInit init;
+ DataChannelInit init;
for (size_t repeats = 0; repeats < kIterations; ++repeats) {
RTC_LOG(LS_INFO) << "Iteration " << (repeats + 1) << "/" << kIterations;
@@ -592,7 +592,7 @@ TEST_P(DataChannelIntegrationTest, StressTestOpenCloseChannelWithDelay) {
const size_t kIterations = 10;
bool has_negotiated = false;
- webrtc::DataChannelInit init;
+ DataChannelInit init;
for (size_t repeats = 0; repeats < kIterations; ++repeats) {
RTC_LOG(LS_INFO) << "Iteration " << (repeats + 1) << "/" << kIterations;
diff --git a/third_party/libwebrtc/pc/data_channel_unittest.cc b/third_party/libwebrtc/pc/data_channel_unittest.cc
index 9b84a1be61..a27a66c3de 100644
--- a/third_party/libwebrtc/pc/data_channel_unittest.cc
+++ b/third_party/libwebrtc/pc/data_channel_unittest.cc
@@ -81,8 +81,7 @@ class SctpDataChannelTest : public ::testing::Test {
controller_(new FakeDataChannelController(&network_thread_)) {
network_thread_.Start();
inner_channel_ = controller_->CreateDataChannel("test", init_);
- channel_ =
- webrtc::SctpDataChannel::CreateProxy(inner_channel_, signaling_safety_);
+ channel_ = SctpDataChannel::CreateProxy(inner_channel_, signaling_safety_);
}
~SctpDataChannelTest() override {
run_loop_.Flush();
@@ -510,7 +509,7 @@ TEST_F(SctpDataChannelTest, LateCreatedChannelTransitionToOpen) {
SetChannelReady();
InternalDataChannelInit init;
init.id = 1;
- auto dc = webrtc::SctpDataChannel::CreateProxy(
+ auto dc = SctpDataChannel::CreateProxy(
controller_->CreateDataChannel("test1", init), signaling_safety_);
EXPECT_EQ(DataChannelInterface::kOpen, dc->state());
}
@@ -524,7 +523,7 @@ TEST_F(SctpDataChannelTest, SendUnorderedAfterReceivesOpenAck) {
init.ordered = false;
rtc::scoped_refptr<SctpDataChannel> dc =
controller_->CreateDataChannel("test1", init);
- auto proxy = webrtc::SctpDataChannel::CreateProxy(dc, signaling_safety_);
+ auto proxy = SctpDataChannel::CreateProxy(dc, signaling_safety_);
EXPECT_EQ_WAIT(DataChannelInterface::kOpen, proxy->state(), 1000);
@@ -553,7 +552,7 @@ TEST_F(SctpDataChannelTest, DeprecatedSendUnorderedAfterReceivesOpenAck) {
init.ordered = false;
rtc::scoped_refptr<SctpDataChannel> dc =
controller_->CreateDataChannel("test1", init);
- auto proxy = webrtc::SctpDataChannel::CreateProxy(dc, signaling_safety_);
+ auto proxy = SctpDataChannel::CreateProxy(dc, signaling_safety_);
EXPECT_EQ_WAIT(DataChannelInterface::kOpen, proxy->state(), 1000);
@@ -582,7 +581,7 @@ TEST_F(SctpDataChannelTest, SendUnorderedAfterReceiveData) {
init.ordered = false;
rtc::scoped_refptr<SctpDataChannel> dc =
controller_->CreateDataChannel("test1", init);
- auto proxy = webrtc::SctpDataChannel::CreateProxy(dc, signaling_safety_);
+ auto proxy = SctpDataChannel::CreateProxy(dc, signaling_safety_);
EXPECT_EQ_WAIT(DataChannelInterface::kOpen, proxy->state(), 1000);
@@ -605,7 +604,7 @@ TEST_F(SctpDataChannelTest, DeprecatedSendUnorderedAfterReceiveData) {
init.ordered = false;
rtc::scoped_refptr<SctpDataChannel> dc =
controller_->CreateDataChannel("test1", init);
- auto proxy = webrtc::SctpDataChannel::CreateProxy(dc, signaling_safety_);
+ auto proxy = SctpDataChannel::CreateProxy(dc, signaling_safety_);
EXPECT_EQ_WAIT(DataChannelInterface::kOpen, proxy->state(), 1000);
@@ -714,7 +713,7 @@ TEST_F(SctpDataChannelTest, NoMsgSentIfNegotiatedAndNotFromOpenMsg) {
SetChannelReady();
rtc::scoped_refptr<SctpDataChannel> dc =
controller_->CreateDataChannel("test1", config);
- auto proxy = webrtc::SctpDataChannel::CreateProxy(dc, signaling_safety_);
+ auto proxy = SctpDataChannel::CreateProxy(dc, signaling_safety_);
EXPECT_EQ_WAIT(DataChannelInterface::kOpen, proxy->state(), 1000);
EXPECT_EQ(0, controller_->last_sid());
@@ -779,7 +778,7 @@ TEST_F(SctpDataChannelTest, OpenAckSentIfCreatedFromOpenMessage) {
SetChannelReady();
rtc::scoped_refptr<SctpDataChannel> dc =
controller_->CreateDataChannel("test1", config);
- auto proxy = webrtc::SctpDataChannel::CreateProxy(dc, signaling_safety_);
+ auto proxy = SctpDataChannel::CreateProxy(dc, signaling_safety_);
EXPECT_EQ_WAIT(DataChannelInterface::kOpen, proxy->state(), 1000);
diff --git a/third_party/libwebrtc/pc/dtls_srtp_transport.h b/third_party/libwebrtc/pc/dtls_srtp_transport.h
index 0f8338ca0d..995809ed4b 100644
--- a/third_party/libwebrtc/pc/dtls_srtp_transport.h
+++ b/third_party/libwebrtc/pc/dtls_srtp_transport.h
@@ -49,15 +49,6 @@ class DtlsSrtpTransport : public SrtpTransport {
void SetOnDtlsStateChange(std::function<void(void)> callback);
- RTCError SetSrtpSendKey(const cricket::CryptoParams& params) override {
- return RTCError(RTCErrorType::UNSUPPORTED_OPERATION,
- "Set SRTP keys for DTLS-SRTP is not supported.");
- }
- RTCError SetSrtpReceiveKey(const cricket::CryptoParams& params) override {
- return RTCError(RTCErrorType::UNSUPPORTED_OPERATION,
- "Set SRTP keys for DTLS-SRTP is not supported.");
- }
-
// If `active_reset_srtp_params_` is set to be true, the SRTP parameters will
// be reset whenever the DtlsTransports are reset.
void SetActiveResetSrtpParams(bool active_reset_srtp_params) {
diff --git a/third_party/libwebrtc/pc/ice_server_parsing_unittest.cc b/third_party/libwebrtc/pc/ice_server_parsing_unittest.cc
index 4356b1efb0..a38638e507 100644
--- a/third_party/libwebrtc/pc/ice_server_parsing_unittest.cc
+++ b/third_party/libwebrtc/pc/ice_server_parsing_unittest.cc
@@ -62,9 +62,7 @@ class IceServerParsingTest : public ::testing::Test {
server.tls_cert_policy = tls_certificate_policy;
server.hostname = hostname;
servers.push_back(server);
- return webrtc::ParseIceServersOrError(servers, &stun_servers_,
- &turn_servers_)
- .ok();
+ return ParseIceServersOrError(servers, &stun_servers_, &turn_servers_).ok();
}
protected:
@@ -233,8 +231,7 @@ TEST_F(IceServerParsingTest, ParseMultipleUrls) {
server.password = "bar";
servers.push_back(server);
EXPECT_TRUE(
- webrtc::ParseIceServersOrError(servers, &stun_servers_, &turn_servers_)
- .ok());
+ ParseIceServersOrError(servers, &stun_servers_, &turn_servers_).ok());
EXPECT_EQ(1U, stun_servers_.size());
EXPECT_EQ(1U, turn_servers_.size());
}
diff --git a/third_party/libwebrtc/pc/ice_transport_unittest.cc b/third_party/libwebrtc/pc/ice_transport_unittest.cc
index aaf9f2e57a..a42c107072 100644
--- a/third_party/libwebrtc/pc/ice_transport_unittest.cc
+++ b/third_party/libwebrtc/pc/ice_transport_unittest.cc
@@ -32,7 +32,7 @@ class IceTransportTest : public ::testing::Test {
rtc::SocketServer* socket_server() const { return socket_server_.get(); }
- webrtc::test::ScopedKeyValueConfig field_trials_;
+ test::ScopedKeyValueConfig field_trials_;
private:
std::unique_ptr<rtc::SocketServer> socket_server_;
diff --git a/third_party/libwebrtc/pc/jsep_transport_controller.cc b/third_party/libwebrtc/pc/jsep_transport_controller.cc
index 7c669a5ae3..d5d1cd24a9 100644
--- a/third_party/libwebrtc/pc/jsep_transport_controller.cc
+++ b/third_party/libwebrtc/pc/jsep_transport_controller.cc
@@ -76,14 +76,18 @@ JsepTransportController::~JsepTransportController() {
RTCError JsepTransportController::SetLocalDescription(
SdpType type,
- const cricket::SessionDescription* description) {
+ const cricket::SessionDescription* local_desc,
+ const cricket::SessionDescription* remote_desc) {
+ RTC_DCHECK(local_desc);
TRACE_EVENT0("webrtc", "JsepTransportController::SetLocalDescription");
+
if (!network_thread_->IsCurrent()) {
return network_thread_->BlockingCall(
- [=] { return SetLocalDescription(type, description); });
+ [=] { return SetLocalDescription(type, local_desc, remote_desc); });
}
RTC_DCHECK_RUN_ON(network_thread_);
+
if (!initial_offerer_.has_value()) {
initial_offerer_.emplace(type == SdpType::kOffer);
if (*initial_offerer_) {
@@ -92,20 +96,22 @@ RTCError JsepTransportController::SetLocalDescription(
SetIceRole_n(cricket::ICEROLE_CONTROLLED);
}
}
- return ApplyDescription_n(/*local=*/true, type, description);
+ return ApplyDescription_n(/*local=*/true, type, local_desc, remote_desc);
}
RTCError JsepTransportController::SetRemoteDescription(
SdpType type,
- const cricket::SessionDescription* description) {
+ const cricket::SessionDescription* local_desc,
+ const cricket::SessionDescription* remote_desc) {
+ RTC_DCHECK(remote_desc);
TRACE_EVENT0("webrtc", "JsepTransportController::SetRemoteDescription");
if (!network_thread_->IsCurrent()) {
return network_thread_->BlockingCall(
- [=] { return SetRemoteDescription(type, description); });
+ [=] { return SetRemoteDescription(type, local_desc, remote_desc); });
}
RTC_DCHECK_RUN_ON(network_thread_);
- return ApplyDescription_n(/*local=*/false, type, description);
+ return ApplyDescription_n(/*local=*/false, type, local_desc, remote_desc);
}
RtpTransportInternal* JsepTransportController::GetRtpTransport(
@@ -148,7 +154,7 @@ JsepTransportController::GetRtcpDtlsTransport(const std::string& mid) const {
return jsep_transport->rtcp_dtls_transport();
}
-rtc::scoped_refptr<webrtc::DtlsTransport>
+rtc::scoped_refptr<DtlsTransport>
JsepTransportController::LookupDtlsTransportByMid(const std::string& mid) {
RTC_DCHECK_RUN_ON(network_thread_);
auto jsep_transport = GetJsepTransportForMid(mid);
@@ -383,7 +389,7 @@ RTCError JsepTransportController::RollbackTransports() {
return RTCError::OK();
}
-rtc::scoped_refptr<webrtc::IceTransportInterface>
+rtc::scoped_refptr<IceTransportInterface>
JsepTransportController::CreateIceTransport(const std::string& transport_name,
bool rtcp) {
int component = rtcp ? cricket::ICE_CANDIDATE_COMPONENT_RTCP
@@ -433,29 +439,43 @@ JsepTransportController::CreateDtlsTransport(
this, &JsepTransportController::OnTransportWritableState_n);
dtls->SignalReceivingState.connect(
this, &JsepTransportController::OnTransportReceivingState_n);
- dtls->ice_transport()->SignalGatheringState.connect(
- this, &JsepTransportController::OnTransportGatheringState_n);
+ dtls->ice_transport()->SetGatheringStateCallback(
+ [this](cricket::IceTransportInternal* transport) {
+ RTC_DCHECK_RUN_ON(network_thread_);
+ OnTransportGatheringState_n(transport);
+ });
dtls->ice_transport()->SignalCandidateGathered.connect(
this, &JsepTransportController::OnTransportCandidateGathered_n);
- dtls->ice_transport()->SignalCandidateError.connect(
- this, &JsepTransportController::OnTransportCandidateError_n);
- dtls->ice_transport()->SignalCandidatesRemoved.connect(
- this, &JsepTransportController::OnTransportCandidatesRemoved_n);
+ dtls->ice_transport()->SetCandidateErrorCallback(
+ [this](cricket::IceTransportInternal* transport,
+ const cricket::IceCandidateErrorEvent& error) {
+ RTC_DCHECK_RUN_ON(network_thread_);
+ OnTransportCandidateError_n(transport, error);
+ });
+ dtls->ice_transport()->SetCandidatesRemovedCallback(
+ [this](cricket::IceTransportInternal* transport,
+ const cricket::Candidates& candidates) {
+ RTC_DCHECK_RUN_ON(network_thread_);
+ OnTransportCandidatesRemoved_n(transport, candidates);
+ });
dtls->ice_transport()->SignalRoleConflict.connect(
this, &JsepTransportController::OnTransportRoleConflict_n);
dtls->ice_transport()->SignalStateChanged.connect(
this, &JsepTransportController::OnTransportStateChanged_n);
dtls->ice_transport()->SignalIceTransportStateChanged.connect(
this, &JsepTransportController::OnTransportStateChanged_n);
- dtls->ice_transport()->SignalCandidatePairChanged.connect(
- this, &JsepTransportController::OnTransportCandidatePairChanged_n);
+ dtls->ice_transport()->SetCandidatePairChangeCallback(
+ [this](const cricket::CandidatePairChangeEvent& event) {
+ RTC_DCHECK_RUN_ON(network_thread_);
+ OnTransportCandidatePairChanged_n(event);
+ });
dtls->SubscribeDtlsHandshakeError(
[this](rtc::SSLHandshakeError error) { OnDtlsHandshakeError(error); });
return dtls;
}
-std::unique_ptr<webrtc::RtpTransport>
+std::unique_ptr<RtpTransport>
JsepTransportController::CreateUnencryptedRtpTransport(
const std::string& transport_name,
rtc::PacketTransportInternal* rtp_packet_transport,
@@ -470,13 +490,12 @@ JsepTransportController::CreateUnencryptedRtpTransport(
return unencrypted_rtp_transport;
}
-std::unique_ptr<webrtc::SrtpTransport>
-JsepTransportController::CreateSdesTransport(
+std::unique_ptr<SrtpTransport> JsepTransportController::CreateSdesTransport(
const std::string& transport_name,
cricket::DtlsTransportInternal* rtp_dtls_transport,
cricket::DtlsTransportInternal* rtcp_dtls_transport) {
RTC_DCHECK_RUN_ON(network_thread_);
- auto srtp_transport = std::make_unique<webrtc::SrtpTransport>(
+ auto srtp_transport = std::make_unique<SrtpTransport>(
rtcp_dtls_transport == nullptr, *config_.field_trials);
RTC_DCHECK(rtp_dtls_transport);
srtp_transport->SetRtpPacketTransport(rtp_dtls_transport);
@@ -489,13 +508,13 @@ JsepTransportController::CreateSdesTransport(
return srtp_transport;
}
-std::unique_ptr<webrtc::DtlsSrtpTransport>
+std::unique_ptr<DtlsSrtpTransport>
JsepTransportController::CreateDtlsSrtpTransport(
const std::string& transport_name,
cricket::DtlsTransportInternal* rtp_dtls_transport,
cricket::DtlsTransportInternal* rtcp_dtls_transport) {
RTC_DCHECK_RUN_ON(network_thread_);
- auto dtls_srtp_transport = std::make_unique<webrtc::DtlsSrtpTransport>(
+ auto dtls_srtp_transport = std::make_unique<DtlsSrtpTransport>(
rtcp_dtls_transport == nullptr, *config_.field_trials);
if (config_.enable_external_auth) {
dtls_srtp_transport->EnableExternalAuth();
@@ -550,18 +569,20 @@ JsepTransportController::GetActiveDtlsTransports() {
RTCError JsepTransportController::ApplyDescription_n(
bool local,
SdpType type,
- const cricket::SessionDescription* description) {
+ const cricket::SessionDescription* local_desc,
+ const cricket::SessionDescription* remote_desc) {
TRACE_EVENT0("webrtc", "JsepTransportController::ApplyDescription_n");
- RTC_DCHECK(description);
- if (local) {
- local_desc_ = description;
- } else {
- remote_desc_ = description;
- }
+ // Stash away the description object that we'll be applying (since this
+ // function is used for both local and remote).
+ const cricket::SessionDescription* description =
+ local ? local_desc : remote_desc;
+
+ RTC_DCHECK(description);
RTCError error;
- error = ValidateAndMaybeUpdateBundleGroups(local, type, description);
+ error =
+ ValidateAndMaybeUpdateBundleGroups(local, type, local_desc, remote_desc);
if (!error.ok()) {
return error;
}
@@ -673,7 +694,11 @@ RTCError JsepTransportController::ApplyDescription_n(
RTCError JsepTransportController::ValidateAndMaybeUpdateBundleGroups(
bool local,
SdpType type,
- const cricket::SessionDescription* description) {
+ const cricket::SessionDescription* local_desc,
+ const cricket::SessionDescription* remote_desc) {
+ const cricket::SessionDescription* description =
+ local ? local_desc : remote_desc;
+
RTC_DCHECK(description);
std::vector<const cricket::ContentGroup*> new_bundle_groups =
@@ -739,72 +764,74 @@ RTCError JsepTransportController::ValidateAndMaybeUpdateBundleGroups(
}
}
} else if (type == SdpType::kAnswer) {
- std::vector<const cricket::ContentGroup*> offered_bundle_groups =
- local ? remote_desc_->GetGroupsByName(cricket::GROUP_TYPE_BUNDLE)
- : local_desc_->GetGroupsByName(cricket::GROUP_TYPE_BUNDLE);
-
- std::map<std::string, const cricket::ContentGroup*>
- offered_bundle_groups_by_mid;
- for (const cricket::ContentGroup* offered_bundle_group :
- offered_bundle_groups) {
- for (const std::string& content_name :
- offered_bundle_group->content_names()) {
- offered_bundle_groups_by_mid[content_name] = offered_bundle_group;
+ if ((local && remote_desc) || (!local && local_desc)) {
+ std::vector<const cricket::ContentGroup*> offered_bundle_groups =
+ local ? remote_desc->GetGroupsByName(cricket::GROUP_TYPE_BUNDLE)
+ : local_desc->GetGroupsByName(cricket::GROUP_TYPE_BUNDLE);
+
+ std::map<std::string, const cricket::ContentGroup*>
+ offered_bundle_groups_by_mid;
+ for (const cricket::ContentGroup* offered_bundle_group :
+ offered_bundle_groups) {
+ for (const std::string& content_name :
+ offered_bundle_group->content_names()) {
+ offered_bundle_groups_by_mid[content_name] = offered_bundle_group;
+ }
}
- }
- std::map<const cricket::ContentGroup*, const cricket::ContentGroup*>
- new_bundle_groups_by_offered_bundle_groups;
- for (const cricket::ContentGroup* new_bundle_group : new_bundle_groups) {
- if (!new_bundle_group->FirstContentName()) {
- // Empty groups could be a subset of any group.
- continue;
- }
- // The group in the answer (new_bundle_group) must have a corresponding
- // group in the offer (original_group), because the answer groups may only
- // be subsets of the offer groups.
- auto it = offered_bundle_groups_by_mid.find(
- *new_bundle_group->FirstContentName());
- if (it == offered_bundle_groups_by_mid.end()) {
- return RTCError(RTCErrorType::INVALID_PARAMETER,
- "A BUNDLE group was added in the answer that did not "
- "exist in the offer.");
- }
- const cricket::ContentGroup* offered_bundle_group = it->second;
- if (new_bundle_groups_by_offered_bundle_groups.find(
- offered_bundle_group) !=
- new_bundle_groups_by_offered_bundle_groups.end()) {
- return RTCError(RTCErrorType::INVALID_PARAMETER,
- "A MID in the answer has changed group.");
- }
- new_bundle_groups_by_offered_bundle_groups.insert(
- std::make_pair(offered_bundle_group, new_bundle_group));
- for (const std::string& content_name :
- new_bundle_group->content_names()) {
- it = offered_bundle_groups_by_mid.find(content_name);
- // The BUNDLE group in answer should be a subset of offered group.
- if (it == offered_bundle_groups_by_mid.end() ||
- it->second != offered_bundle_group) {
+ std::map<const cricket::ContentGroup*, const cricket::ContentGroup*>
+ new_bundle_groups_by_offered_bundle_groups;
+ for (const cricket::ContentGroup* new_bundle_group : new_bundle_groups) {
+ if (!new_bundle_group->FirstContentName()) {
+ // Empty groups could be a subset of any group.
+ continue;
+ }
+ // The group in the answer (new_bundle_group) must have a corresponding
+ // group in the offer (original_group), because the answer groups may
+ // only be subsets of the offer groups.
+ auto it = offered_bundle_groups_by_mid.find(
+ *new_bundle_group->FirstContentName());
+ if (it == offered_bundle_groups_by_mid.end()) {
return RTCError(RTCErrorType::INVALID_PARAMETER,
- "A BUNDLE group in answer contains a MID='" +
- content_name +
- "' that was not in the offered group.");
+ "A BUNDLE group was added in the answer that did not "
+ "exist in the offer.");
}
- }
- }
-
- for (const auto& bundle_group : bundles_.bundle_groups()) {
- for (const std::string& content_name : bundle_group->content_names()) {
- // An answer that removes m= sections from pre-negotiated BUNDLE group
- // without rejecting it, is invalid.
- auto it = new_bundle_groups_by_mid.find(content_name);
- if (it == new_bundle_groups_by_mid.end()) {
- auto* content_info = description->GetContentByName(content_name);
- if (!content_info || !content_info->rejected) {
+ const cricket::ContentGroup* offered_bundle_group = it->second;
+ if (new_bundle_groups_by_offered_bundle_groups.find(
+ offered_bundle_group) !=
+ new_bundle_groups_by_offered_bundle_groups.end()) {
+ return RTCError(RTCErrorType::INVALID_PARAMETER,
+ "A MID in the answer has changed group.");
+ }
+ new_bundle_groups_by_offered_bundle_groups.insert(
+ std::make_pair(offered_bundle_group, new_bundle_group));
+ for (const std::string& content_name :
+ new_bundle_group->content_names()) {
+ it = offered_bundle_groups_by_mid.find(content_name);
+ // The BUNDLE group in answer should be a subset of offered group.
+ if (it == offered_bundle_groups_by_mid.end() ||
+ it->second != offered_bundle_group) {
return RTCError(RTCErrorType::INVALID_PARAMETER,
- "Answer cannot remove m= section with mid='" +
+ "A BUNDLE group in answer contains a MID='" +
content_name +
- "' from already-established BUNDLE group.");
+ "' that was not in the offered group.");
+ }
+ }
+ }
+
+ for (const auto& bundle_group : bundles_.bundle_groups()) {
+ for (const std::string& content_name : bundle_group->content_names()) {
+ // An answer that removes m= sections from pre-negotiated BUNDLE group
+ // without rejecting it, is invalid.
+ auto it = new_bundle_groups_by_mid.find(content_name);
+ if (it == new_bundle_groups_by_mid.end()) {
+ auto* content_info = description->GetContentByName(content_name);
+ if (!content_info || !content_info->rejected) {
+ return RTCError(RTCErrorType::INVALID_PARAMETER,
+ "Answer cannot remove m= section with mid='" +
+ content_name +
+ "' from already-established BUNDLE group.");
+ }
}
}
}
@@ -985,13 +1012,12 @@ int JsepTransportController::GetRtpAbsSendTimeHeaderExtensionId(
const cricket::MediaContentDescription* content_desc =
content_info.media_description();
- const webrtc::RtpExtension* send_time_extension =
- webrtc::RtpExtension::FindHeaderExtensionByUri(
- content_desc->rtp_header_extensions(),
- webrtc::RtpExtension::kAbsSendTimeUri,
+ const RtpExtension* send_time_extension =
+ RtpExtension::FindHeaderExtensionByUri(
+ content_desc->rtp_header_extensions(), RtpExtension::kAbsSendTimeUri,
config_.crypto_options.srtp.enable_encrypted_rtp_header_extensions
- ? webrtc::RtpExtension::kPreferEncryptedExtension
- : webrtc::RtpExtension::kDiscardEncryptedExtension);
+ ? RtpExtension::kPreferEncryptedExtension
+ : RtpExtension::kDiscardEncryptedExtension);
return send_time_extension ? send_time_extension->id : -1;
}
@@ -1039,7 +1065,7 @@ RTCError JsepTransportController::MaybeCreateJsepTransport(
"SDES and DTLS-SRTP cannot be enabled at the same time.");
}
- rtc::scoped_refptr<webrtc::IceTransportInterface> ice =
+ rtc::scoped_refptr<IceTransportInterface> ice =
CreateIceTransport(content_info.name, /*rtcp=*/false);
std::unique_ptr<cricket::DtlsTransportInternal> rtp_dtls_transport =
@@ -1050,7 +1076,7 @@ RTCError JsepTransportController::MaybeCreateJsepTransport(
std::unique_ptr<SrtpTransport> sdes_transport;
std::unique_ptr<DtlsSrtpTransport> dtls_srtp_transport;
- rtc::scoped_refptr<webrtc::IceTransportInterface> rtcp_ice;
+ rtc::scoped_refptr<IceTransportInterface> rtcp_ice;
if (config_.rtcp_mux_policy !=
PeerConnectionInterface::kRtcpMuxPolicyRequire &&
content_info.type == cricket::MediaProtocolType::kRtp) {
@@ -1096,7 +1122,7 @@ RTCError JsepTransportController::MaybeCreateJsepTransport(
OnRtcpPacketReceived_n(buffer, packet_time_ms);
});
jsep_transport->rtp_transport()->SetUnDemuxableRtpPacketReceivedHandler(
- [this](webrtc::RtpPacketReceived& packet) {
+ [this](RtpPacketReceived& packet) {
RTC_DCHECK_RUN_ON(network_thread_);
OnUnDemuxableRtpPacketReceived_n(packet);
});
@@ -1421,7 +1447,7 @@ void JsepTransportController::OnRtcpPacketReceived_n(
}
void JsepTransportController::OnUnDemuxableRtpPacketReceived_n(
- const webrtc::RtpPacketReceived& packet) {
+ const RtpPacketReceived& packet) {
RTC_DCHECK(config_.un_demuxable_packet_handler);
config_.un_demuxable_packet_handler(packet);
}
diff --git a/third_party/libwebrtc/pc/jsep_transport_controller.h b/third_party/libwebrtc/pc/jsep_transport_controller.h
index 5880e346cd..448844ac79 100644
--- a/third_party/libwebrtc/pc/jsep_transport_controller.h
+++ b/third_party/libwebrtc/pc/jsep_transport_controller.h
@@ -112,7 +112,7 @@ class JsepTransportController : public sigslot::has_slots<> {
rtc::SSLProtocolVersion ssl_max_version = rtc::SSL_PROTOCOL_DTLS_12;
// `crypto_options` is used to determine if created DTLS transports
// negotiate GCM crypto suites or not.
- webrtc::CryptoOptions crypto_options;
+ CryptoOptions crypto_options;
PeerConnectionInterface::BundlePolicy bundle_policy =
PeerConnectionInterface::kBundlePolicyBalanced;
PeerConnectionInterface::RtcpMuxPolicy rtcp_mux_policy =
@@ -120,7 +120,7 @@ class JsepTransportController : public sigslot::has_slots<> {
bool disable_encryption = false;
bool enable_external_auth = false;
// Used to inject the ICE/DTLS transports created externally.
- webrtc::IceTransportFactory* ice_transport_factory = nullptr;
+ IceTransportFactory* ice_transport_factory = nullptr;
cricket::DtlsTransportFactory* dtls_transport_factory = nullptr;
Observer* transport_observer = nullptr;
// Must be provided and valid for the lifetime of the
@@ -140,7 +140,7 @@ class JsepTransportController : public sigslot::has_slots<> {
std::function<void(rtc::SSLHandshakeError)> on_dtls_handshake_error_;
// Field trials.
- const webrtc::FieldTrialsView* field_trials;
+ const FieldTrialsView* field_trials;
};
// The ICE related events are fired on the `network_thread`.
@@ -161,11 +161,24 @@ class JsepTransportController : public sigslot::has_slots<> {
// level, creating/destroying transport objects as needed and updating their
// properties. This includes RTP, DTLS, and ICE (but not SCTP). At least not
// yet? May make sense to in the future.
+ //
+ // `local_desc` must always be valid. If a remote description has previously
+ // been set via a call to `SetRemoteDescription()` then `remote_desc` should
+ // point to that description object in order to keep the current local and
+ // remote session descriptions in sync.
RTCError SetLocalDescription(SdpType type,
- const cricket::SessionDescription* description);
-
+ const cricket::SessionDescription* local_desc,
+ const cricket::SessionDescription* remote_desc);
+
+ // Call to apply a remote description (See `SetLocalDescription()` for local).
+ //
+ // `remote_desc` must always be valid. If a local description has previously
+ // been set via a call to `SetLocalDescription()` then `local_desc` should
+ // point to that description object in order to keep the current local and
+ // remote session descriptions in sync.
RTCError SetRemoteDescription(SdpType type,
- const cricket::SessionDescription* description);
+ const cricket::SessionDescription* local_desc,
+ const cricket::SessionDescription* remote_desc);
// Get transports to be used for the provided `mid`. If bundling is enabled,
// calling GetRtpTransport for multiple MIDs may yield the same object.
@@ -174,7 +187,7 @@ class JsepTransportController : public sigslot::has_slots<> {
const cricket::DtlsTransportInternal* GetRtcpDtlsTransport(
const std::string& mid) const;
// Gets the externally sharable version of the DtlsTransport.
- rtc::scoped_refptr<webrtc::DtlsTransport> LookupDtlsTransportByMid(
+ rtc::scoped_refptr<DtlsTransport> LookupDtlsTransportByMid(
const std::string& mid);
rtc::scoped_refptr<SctpTransport> GetSctpTransport(
const std::string& mid) const;
@@ -325,14 +338,23 @@ class JsepTransportController : public sigslot::has_slots<> {
CallbackList<const cricket::CandidatePairChangeEvent&>
signal_ice_candidate_pair_changed_ RTC_GUARDED_BY(network_thread_);
+ // Called from SetLocalDescription and SetRemoteDescription.
+ // When `local` is true, local_desc must be valid. Similarly when
+ // `local` is false, remote_desc must be valid. The description counterpart
+ // to the one that's being applied, may be nullptr but when it's supplied
+ // the counterpart description's content groups will be kept up to date for
+ // `type == SdpType::kAnswer`.
RTCError ApplyDescription_n(bool local,
SdpType type,
- const cricket::SessionDescription* description)
+ const cricket::SessionDescription* local_desc,
+ const cricket::SessionDescription* remote_desc)
RTC_RUN_ON(network_thread_);
RTCError ValidateAndMaybeUpdateBundleGroups(
bool local,
SdpType type,
- const cricket::SessionDescription* description);
+ const cricket::SessionDescription* local_desc,
+ const cricket::SessionDescription* remote_desc)
+ RTC_RUN_ON(network_thread_);
RTCError ValidateContent(const cricket::ContentInfo& content_info);
void HandleRejectedContent(const cricket::ContentInfo& content_info)
@@ -399,19 +421,19 @@ class JsepTransportController : public sigslot::has_slots<> {
std::unique_ptr<cricket::DtlsTransportInternal> CreateDtlsTransport(
const cricket::ContentInfo& content_info,
cricket::IceTransportInternal* ice);
- rtc::scoped_refptr<webrtc::IceTransportInterface> CreateIceTransport(
+ rtc::scoped_refptr<IceTransportInterface> CreateIceTransport(
const std::string& transport_name,
bool rtcp);
- std::unique_ptr<webrtc::RtpTransport> CreateUnencryptedRtpTransport(
+ std::unique_ptr<RtpTransport> CreateUnencryptedRtpTransport(
const std::string& transport_name,
rtc::PacketTransportInternal* rtp_packet_transport,
rtc::PacketTransportInternal* rtcp_packet_transport);
- std::unique_ptr<webrtc::SrtpTransport> CreateSdesTransport(
+ std::unique_ptr<SrtpTransport> CreateSdesTransport(
const std::string& transport_name,
cricket::DtlsTransportInternal* rtp_dtls_transport,
cricket::DtlsTransportInternal* rtcp_dtls_transport);
- std::unique_ptr<webrtc::DtlsSrtpTransport> CreateDtlsSrtpTransport(
+ std::unique_ptr<DtlsSrtpTransport> CreateDtlsSrtpTransport(
const std::string& transport_name,
cricket::DtlsTransportInternal* rtp_dtls_transport,
cricket::DtlsTransportInternal* rtcp_dtls_transport);
@@ -453,7 +475,7 @@ class JsepTransportController : public sigslot::has_slots<> {
void OnRtcpPacketReceived_n(rtc::CopyOnWriteBuffer* packet,
int64_t packet_time_us)
RTC_RUN_ON(network_thread_);
- void OnUnDemuxableRtpPacketReceived_n(const webrtc::RtpPacketReceived& packet)
+ void OnUnDemuxableRtpPacketReceived_n(const RtpPacketReceived& packet)
RTC_RUN_ON(network_thread_);
void OnDtlsHandshakeError(rtc::SSLHandshakeError error);
@@ -481,8 +503,6 @@ class JsepTransportController : public sigslot::has_slots<> {
const Config config_;
bool active_reset_srtp_params_ RTC_GUARDED_BY(network_thread_);
- const cricket::SessionDescription* local_desc_ = nullptr;
- const cricket::SessionDescription* remote_desc_ = nullptr;
absl::optional<bool> initial_offerer_;
cricket::IceConfig ice_config_;
diff --git a/third_party/libwebrtc/pc/jsep_transport_controller_unittest.cc b/third_party/libwebrtc/pc/jsep_transport_controller_unittest.cc
index faa8842e35..7696d82be8 100644
--- a/third_party/libwebrtc/pc/jsep_transport_controller_unittest.cc
+++ b/third_party/libwebrtc/pc/jsep_transport_controller_unittest.cc
@@ -56,7 +56,7 @@ static const char kDataMid1[] = "data1";
namespace webrtc {
-class FakeIceTransportFactory : public webrtc::IceTransportFactory {
+class FakeIceTransportFactory : public IceTransportFactory {
public:
~FakeIceTransportFactory() override = default;
rtc::scoped_refptr<IceTransportInterface> CreateIceTransport(
@@ -72,7 +72,7 @@ class FakeDtlsTransportFactory : public cricket::DtlsTransportFactory {
public:
std::unique_ptr<cricket::DtlsTransportInternal> CreateDtlsTransport(
cricket::IceTransportInternal* ice,
- const webrtc::CryptoOptions& crypto_options,
+ const CryptoOptions& crypto_options,
rtc::SSLProtocolVersion max_version) override {
return std::make_unique<FakeDtlsTransport>(
static_cast<cricket::FakeIceTransport*>(ice));
@@ -265,9 +265,10 @@ class JsepTransportControllerTest : public JsepTransportController::Observer,
}
auto description = CreateSessionDescriptionWithBundleGroup();
- EXPECT_TRUE(transport_controller_
- ->SetLocalDescription(SdpType::kOffer, description.get())
- .ok());
+ EXPECT_TRUE(
+ transport_controller_
+ ->SetLocalDescription(SdpType::kOffer, description.get(), nullptr)
+ .ok());
transport_controller_->MaybeStartGathering();
auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
@@ -379,15 +380,16 @@ class JsepTransportControllerTest : public JsepTransportController::Observer,
// Transport controller needs to be destroyed first, because it may issue
// callbacks that modify the changed_*_by_mid in the destructor.
std::unique_ptr<JsepTransportController> transport_controller_;
- webrtc::test::ScopedKeyValueConfig field_trials_;
+ test::ScopedKeyValueConfig field_trials_;
};
TEST_F(JsepTransportControllerTest, GetRtpTransport) {
CreateJsepTransportController(JsepTransportController::Config());
auto description = CreateSessionDescriptionWithoutBundle();
- EXPECT_TRUE(transport_controller_
- ->SetLocalDescription(SdpType::kOffer, description.get())
- .ok());
+ EXPECT_TRUE(
+ transport_controller_
+ ->SetLocalDescription(SdpType::kOffer, description.get(), nullptr)
+ .ok());
auto audio_rtp_transport = transport_controller_->GetRtpTransport(kAudioMid1);
auto video_rtp_transport = transport_controller_->GetRtpTransport(kVideoMid1);
EXPECT_NE(nullptr, audio_rtp_transport);
@@ -402,9 +404,10 @@ TEST_F(JsepTransportControllerTest, GetDtlsTransport) {
config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyNegotiate;
CreateJsepTransportController(std::move(config));
auto description = CreateSessionDescriptionWithoutBundle();
- EXPECT_TRUE(transport_controller_
- ->SetLocalDescription(SdpType::kOffer, description.get())
- .ok());
+ EXPECT_TRUE(
+ transport_controller_
+ ->SetLocalDescription(SdpType::kOffer, description.get(), nullptr)
+ .ok());
EXPECT_NE(nullptr, transport_controller_->GetDtlsTransport(kAudioMid1));
EXPECT_NE(nullptr, transport_controller_->GetRtcpDtlsTransport(kAudioMid1));
EXPECT_NE(nullptr,
@@ -425,7 +428,7 @@ TEST_F(JsepTransportControllerTest, GetDtlsTransport) {
// and verify that the resulting container is empty.
auto dtls_transport =
transport_controller_->LookupDtlsTransportByMid(kVideoMid1);
- webrtc::DtlsTransport* my_transport =
+ DtlsTransport* my_transport =
static_cast<DtlsTransport*>(dtls_transport.get());
EXPECT_NE(nullptr, my_transport->internal());
transport_controller_.reset();
@@ -437,9 +440,10 @@ TEST_F(JsepTransportControllerTest, GetDtlsTransportWithRtcpMux) {
config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyRequire;
CreateJsepTransportController(std::move(config));
auto description = CreateSessionDescriptionWithoutBundle();
- EXPECT_TRUE(transport_controller_
- ->SetLocalDescription(SdpType::kOffer, description.get())
- .ok());
+ EXPECT_TRUE(
+ transport_controller_
+ ->SetLocalDescription(SdpType::kOffer, description.get(), nullptr)
+ .ok());
EXPECT_NE(nullptr, transport_controller_->GetDtlsTransport(kAudioMid1));
EXPECT_EQ(nullptr, transport_controller_->GetRtcpDtlsTransport(kAudioMid1));
EXPECT_NE(nullptr, transport_controller_->GetDtlsTransport(kVideoMid1));
@@ -449,9 +453,10 @@ TEST_F(JsepTransportControllerTest, GetDtlsTransportWithRtcpMux) {
TEST_F(JsepTransportControllerTest, SetIceConfig) {
CreateJsepTransportController(JsepTransportController::Config());
auto description = CreateSessionDescriptionWithoutBundle();
- EXPECT_TRUE(transport_controller_
- ->SetLocalDescription(SdpType::kOffer, description.get())
- .ok());
+ EXPECT_TRUE(
+ transport_controller_
+ ->SetLocalDescription(SdpType::kOffer, description.get(), nullptr)
+ .ok());
transport_controller_->SetIceConfig(
CreateIceConfig(kTimeout, cricket::GATHER_CONTINUALLY));
@@ -467,9 +472,10 @@ TEST_F(JsepTransportControllerTest, SetIceConfig) {
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
nullptr);
- EXPECT_TRUE(transport_controller_
- ->SetLocalDescription(SdpType::kOffer, description.get())
- .ok());
+ EXPECT_TRUE(
+ transport_controller_
+ ->SetLocalDescription(SdpType::kOffer, description.get(), nullptr)
+ .ok());
fake_audio_dtls = static_cast<FakeDtlsTransport*>(
transport_controller_->GetDtlsTransport(kAudioMid2));
ASSERT_NE(nullptr, fake_audio_dtls);
@@ -482,11 +488,14 @@ TEST_F(JsepTransportControllerTest, SetIceConfig) {
TEST_F(JsepTransportControllerTest, NeedIceRestart) {
CreateJsepTransportController(JsepTransportController::Config());
auto description = CreateSessionDescriptionWithoutBundle();
+ EXPECT_TRUE(
+ transport_controller_
+ ->SetLocalDescription(SdpType::kOffer, description.get(), nullptr)
+ .ok());
+ // TODO(tommi): Note that _now_ we set `remote`. (was not set before).
EXPECT_TRUE(transport_controller_
- ->SetLocalDescription(SdpType::kOffer, description.get())
- .ok());
- EXPECT_TRUE(transport_controller_
- ->SetRemoteDescription(SdpType::kAnswer, description.get())
+ ->SetRemoteDescription(SdpType::kAnswer, description.get(),
+ description.get())
.ok());
// Initially NeedsIceRestart should return false.
@@ -505,7 +514,8 @@ TEST_F(JsepTransportControllerTest, NeedIceRestart) {
audio_transport_info->description.ice_ufrag = kIceUfrag2;
audio_transport_info->description.ice_pwd = kIcePwd2;
EXPECT_TRUE(transport_controller_
- ->SetLocalDescription(SdpType::kOffer, description.get())
+ ->SetLocalDescription(SdpType::kOffer, description.get(),
+ description.get())
.ok());
// Because the ICE is only restarted for audio, NeedsIceRestart is expected to
// return false for audio and true for video.
@@ -516,9 +526,10 @@ TEST_F(JsepTransportControllerTest, NeedIceRestart) {
TEST_F(JsepTransportControllerTest, MaybeStartGathering) {
CreateJsepTransportController(JsepTransportController::Config());
auto description = CreateSessionDescriptionWithBundleGroup();
- EXPECT_TRUE(transport_controller_
- ->SetLocalDescription(SdpType::kOffer, description.get())
- .ok());
+ EXPECT_TRUE(
+ transport_controller_
+ ->SetLocalDescription(SdpType::kOffer, description.get(), nullptr)
+ .ok());
// After setting the local description, we should be able to start gathering
// candidates.
transport_controller_->MaybeStartGathering();
@@ -529,10 +540,10 @@ TEST_F(JsepTransportControllerTest, MaybeStartGathering) {
TEST_F(JsepTransportControllerTest, AddRemoveRemoteCandidates) {
CreateJsepTransportController(JsepTransportController::Config());
auto description = CreateSessionDescriptionWithoutBundle();
- transport_controller_->SetLocalDescription(SdpType::kOffer,
- description.get());
- transport_controller_->SetRemoteDescription(SdpType::kAnswer,
- description.get());
+ transport_controller_->SetLocalDescription(SdpType::kOffer, description.get(),
+ nullptr);
+ transport_controller_->SetRemoteDescription(
+ SdpType::kAnswer, description.get(), description.get());
auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
transport_controller_->GetDtlsTransport(kAudioMid1));
ASSERT_NE(nullptr, fake_audio_dtls);
@@ -565,9 +576,10 @@ TEST_F(JsepTransportControllerTest, SetAndGetLocalCertificate) {
// Apply the local certificate.
EXPECT_TRUE(transport_controller_->SetLocalCertificate(certificate1));
// Apply the local description.
- EXPECT_TRUE(transport_controller_
- ->SetLocalDescription(SdpType::kOffer, description.get())
- .ok());
+ EXPECT_TRUE(
+ transport_controller_
+ ->SetLocalDescription(SdpType::kOffer, description.get(), nullptr)
+ .ok());
returned_certificate = transport_controller_->GetLocalCertificate(kAudioMid1);
EXPECT_TRUE(returned_certificate);
EXPECT_EQ(certificate1->identity()->certificate().ToPEMString(),
@@ -586,9 +598,10 @@ TEST_F(JsepTransportControllerTest, SetAndGetLocalCertificate) {
TEST_F(JsepTransportControllerTest, GetRemoteSSLCertChain) {
CreateJsepTransportController(JsepTransportController::Config());
auto description = CreateSessionDescriptionWithBundleGroup();
- EXPECT_TRUE(transport_controller_
- ->SetLocalDescription(SdpType::kOffer, description.get())
- .ok());
+ EXPECT_TRUE(
+ transport_controller_
+ ->SetLocalDescription(SdpType::kOffer, description.get(), nullptr)
+ .ok());
rtc::FakeSSLCertificate fake_certificate("fake_data");
auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
@@ -622,16 +635,18 @@ TEST_F(JsepTransportControllerTest, GetDtlsRole) {
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
answer_certificate);
- EXPECT_TRUE(transport_controller_
- ->SetLocalDescription(SdpType::kOffer, offer_desc.get())
- .ok());
+ EXPECT_TRUE(
+ transport_controller_
+ ->SetLocalDescription(SdpType::kOffer, offer_desc.get(), nullptr)
+ .ok());
absl::optional<rtc::SSLRole> role =
transport_controller_->GetDtlsRole(kAudioMid1);
// The DTLS role is not decided yet.
EXPECT_FALSE(role);
EXPECT_TRUE(transport_controller_
- ->SetRemoteDescription(SdpType::kAnswer, answer_desc.get())
+ ->SetRemoteDescription(SdpType::kAnswer, offer_desc.get(),
+ answer_desc.get())
.ok());
role = transport_controller_->GetDtlsRole(kAudioMid1);
@@ -642,9 +657,10 @@ TEST_F(JsepTransportControllerTest, GetDtlsRole) {
TEST_F(JsepTransportControllerTest, GetStats) {
CreateJsepTransportController(JsepTransportController::Config());
auto description = CreateSessionDescriptionWithBundleGroup();
- EXPECT_TRUE(transport_controller_
- ->SetLocalDescription(SdpType::kOffer, description.get())
- .ok());
+ EXPECT_TRUE(
+ transport_controller_
+ ->SetLocalDescription(SdpType::kOffer, description.get(), nullptr)
+ .ok());
cricket::TransportStats stats;
EXPECT_TRUE(transport_controller_->GetStats(kAudioMid1, &stats));
@@ -657,9 +673,10 @@ TEST_F(JsepTransportControllerTest, GetStats) {
TEST_F(JsepTransportControllerTest, SignalConnectionStateFailed) {
CreateJsepTransportController(JsepTransportController::Config());
auto description = CreateSessionDescriptionWithoutBundle();
- EXPECT_TRUE(transport_controller_
- ->SetLocalDescription(SdpType::kOffer, description.get())
- .ok());
+ EXPECT_TRUE(
+ transport_controller_
+ ->SetLocalDescription(SdpType::kOffer, description.get(), nullptr)
+ .ok());
auto fake_ice = static_cast<cricket::FakeIceTransport*>(
transport_controller_->GetDtlsTransport(kAudioMid1)->ice_transport());
@@ -681,9 +698,10 @@ TEST_F(JsepTransportControllerTest,
SignalConnectionStateConnectedNoMediaTransport) {
CreateJsepTransportController(JsepTransportController::Config());
auto description = CreateSessionDescriptionWithoutBundle();
- EXPECT_TRUE(transport_controller_
- ->SetLocalDescription(SdpType::kOffer, description.get())
- .ok());
+ EXPECT_TRUE(
+ transport_controller_
+ ->SetLocalDescription(SdpType::kOffer, description.get(), nullptr)
+ .ok());
auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
transport_controller_->GetDtlsTransport(kAudioMid1));
@@ -729,9 +747,10 @@ TEST_F(JsepTransportControllerTest,
TEST_F(JsepTransportControllerTest, SignalConnectionStateComplete) {
CreateJsepTransportController(JsepTransportController::Config());
auto description = CreateSessionDescriptionWithoutBundle();
- EXPECT_TRUE(transport_controller_
- ->SetLocalDescription(SdpType::kOffer, description.get())
- .ok());
+ EXPECT_TRUE(
+ transport_controller_
+ ->SetLocalDescription(SdpType::kOffer, description.get(), nullptr)
+ .ok());
auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
transport_controller_->GetDtlsTransport(kAudioMid1));
@@ -788,9 +807,10 @@ TEST_F(JsepTransportControllerTest, SignalConnectionStateComplete) {
TEST_F(JsepTransportControllerTest, SignalIceGatheringStateGathering) {
CreateJsepTransportController(JsepTransportController::Config());
auto description = CreateSessionDescriptionWithoutBundle();
- EXPECT_TRUE(transport_controller_
- ->SetLocalDescription(SdpType::kOffer, description.get())
- .ok());
+ EXPECT_TRUE(
+ transport_controller_
+ ->SetLocalDescription(SdpType::kOffer, description.get(), nullptr)
+ .ok());
auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
transport_controller_->GetDtlsTransport(kAudioMid1));
@@ -803,9 +823,10 @@ TEST_F(JsepTransportControllerTest, SignalIceGatheringStateGathering) {
TEST_F(JsepTransportControllerTest, SignalIceGatheringStateComplete) {
CreateJsepTransportController(JsepTransportController::Config());
auto description = CreateSessionDescriptionWithoutBundle();
- EXPECT_TRUE(transport_controller_
- ->SetLocalDescription(SdpType::kOffer, description.get())
- .ok());
+ EXPECT_TRUE(
+ transport_controller_
+ ->SetLocalDescription(SdpType::kOffer, description.get(), nullptr)
+ .ok());
auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
transport_controller_->GetDtlsTransport(kAudioMid1));
@@ -838,9 +859,10 @@ TEST_F(JsepTransportControllerTest,
SignalingWhenLastIncompleteTransportDestroyed) {
CreateJsepTransportController(JsepTransportController::Config());
auto description = CreateSessionDescriptionWithBundleGroup();
- EXPECT_TRUE(transport_controller_
- ->SetLocalDescription(SdpType::kOffer, description.get())
- .ok());
+ EXPECT_TRUE(
+ transport_controller_
+ ->SetLocalDescription(SdpType::kOffer, description.get(), nullptr)
+ .ok());
auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
transport_controller_->GetDtlsTransport(kAudioMid1));
@@ -861,7 +883,8 @@ TEST_F(JsepTransportControllerTest,
// Set the remote description and enable the bundle.
EXPECT_TRUE(transport_controller_
- ->SetRemoteDescription(SdpType::kAnswer, description.get())
+ ->SetRemoteDescription(SdpType::kAnswer, description.get(),
+ description.get())
.ok());
// The BUNDLE should be enabled, the incomplete video transport should be
// deleted and the states should be updated.
@@ -887,11 +910,13 @@ TEST_F(JsepTransportControllerTest,
AddAudioSection(description.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
nullptr);
+ EXPECT_TRUE(
+ transport_controller_
+ ->SetLocalDescription(SdpType::kOffer, description.get(), nullptr)
+ .ok());
EXPECT_TRUE(transport_controller_
- ->SetLocalDescription(SdpType::kOffer, description.get())
- .ok());
- EXPECT_TRUE(transport_controller_
- ->SetRemoteDescription(SdpType::kAnswer, description.get())
+ ->SetRemoteDescription(SdpType::kAnswer, description.get(),
+ description.get())
.ok());
// Trigger and verify initial non-new states.
@@ -899,7 +924,7 @@ TEST_F(JsepTransportControllerTest,
transport_controller_->GetDtlsTransport(kAudioMid1));
fake_audio_dtls->fake_ice_transport()->MaybeStartGathering();
fake_audio_dtls->fake_ice_transport()->SetTransportState(
- webrtc::IceTransportState::kChecking,
+ IceTransportState::kChecking,
cricket::IceTransportState::STATE_CONNECTING);
EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionChecking,
ice_connection_state_, kTimeout);
@@ -914,7 +939,8 @@ TEST_F(JsepTransportControllerTest,
// to "new".
description->contents()[0].rejected = true;
EXPECT_TRUE(transport_controller_
- ->SetRemoteDescription(SdpType::kOffer, description.get())
+ ->SetRemoteDescription(SdpType::kOffer, description.get(),
+ description.get())
.ok());
EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionNew,
ice_connection_state_, kTimeout);
@@ -941,9 +967,10 @@ TEST_F(JsepTransportControllerTest,
TEST_F(JsepTransportControllerTest, SignalCandidatesGathered) {
CreateJsepTransportController(JsepTransportController::Config());
auto description = CreateSessionDescriptionWithBundleGroup();
- EXPECT_TRUE(transport_controller_
- ->SetLocalDescription(SdpType::kOffer, description.get())
- .ok());
+ EXPECT_TRUE(
+ transport_controller_
+ ->SetLocalDescription(SdpType::kOffer, description.get(), nullptr)
+ .ok());
transport_controller_->MaybeStartGathering();
auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
@@ -998,11 +1025,13 @@ TEST_F(JsepTransportControllerTest, IceRoleNotRedetermined) {
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
nullptr);
+ EXPECT_TRUE(
+ transport_controller_
+ ->SetRemoteDescription(SdpType::kOffer, nullptr, remote_offer.get())
+ .ok());
EXPECT_TRUE(transport_controller_
- ->SetRemoteDescription(SdpType::kOffer, remote_offer.get())
- .ok());
- EXPECT_TRUE(transport_controller_
- ->SetLocalDescription(SdpType::kAnswer, local_answer.get())
+ ->SetLocalDescription(SdpType::kAnswer, local_answer.get(),
+ remote_offer.get())
.ok());
auto fake_dtls = static_cast<FakeDtlsTransport*>(
@@ -1015,10 +1044,11 @@ TEST_F(JsepTransportControllerTest, IceRoleNotRedetermined) {
AddAudioSection(restart_local_offer.get(), kAudioMid1, kIceUfrag3, kIcePwd3,
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
nullptr);
- EXPECT_TRUE(
- transport_controller_
- ->SetLocalDescription(SdpType::kOffer, restart_local_offer.get())
- .ok());
+ EXPECT_TRUE(transport_controller_
+ ->SetLocalDescription(SdpType::kOffer,
+ restart_local_offer.get(),
+ remote_offer.get())
+ .ok());
EXPECT_EQ(cricket::ICEROLE_CONTROLLED,
fake_dtls->fake_ice_transport()->GetIceRole());
}
@@ -1030,9 +1060,10 @@ TEST_F(JsepTransportControllerTest, SetIceRoleWhenIceLiteInRemoteAnswer) {
AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
nullptr);
- EXPECT_TRUE(transport_controller_
- ->SetLocalDescription(SdpType::kOffer, local_offer.get())
- .ok());
+ EXPECT_TRUE(
+ transport_controller_
+ ->SetLocalDescription(SdpType::kOffer, local_offer.get(), nullptr)
+ .ok());
auto fake_dtls = static_cast<FakeDtlsTransport*>(
transport_controller_->GetDtlsTransport(kAudioMid1));
EXPECT_EQ(cricket::ICEROLE_CONTROLLING,
@@ -1045,7 +1076,8 @@ TEST_F(JsepTransportControllerTest, SetIceRoleWhenIceLiteInRemoteAnswer) {
cricket::ICEMODE_LITE, cricket::CONNECTIONROLE_PASSIVE,
nullptr);
EXPECT_TRUE(transport_controller_
- ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
+ ->SetRemoteDescription(SdpType::kAnswer, local_offer.get(),
+ remote_answer.get())
.ok());
EXPECT_EQ(cricket::ICEROLE_CONTROLLING,
fake_dtls->fake_ice_transport()->GetIceRole());
@@ -1069,11 +1101,13 @@ TEST_F(JsepTransportControllerTest,
nullptr);
// Initial Offer/Answer exchange. If the remote offerer is ICE-Lite, then the
// local side is the controlling.
+ EXPECT_TRUE(
+ transport_controller_
+ ->SetRemoteDescription(SdpType::kOffer, nullptr, remote_offer.get())
+ .ok());
EXPECT_TRUE(transport_controller_
- ->SetRemoteDescription(SdpType::kOffer, remote_offer.get())
- .ok());
- EXPECT_TRUE(transport_controller_
- ->SetLocalDescription(SdpType::kAnswer, local_answer.get())
+ ->SetLocalDescription(SdpType::kAnswer, local_answer.get(),
+ remote_offer.get())
.ok());
auto fake_dtls = static_cast<FakeDtlsTransport*>(
transport_controller_->GetDtlsTransport(kAudioMid1));
@@ -1085,15 +1119,17 @@ TEST_F(JsepTransportControllerTest,
AddAudioSection(remote_offer2.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
cricket::ICEMODE_LITE, cricket::CONNECTIONROLE_ACTPASS,
nullptr);
+ EXPECT_TRUE(transport_controller_
+ ->SetRemoteDescription(SdpType::kOffer, local_answer.get(),
+ remote_offer2.get())
+ .ok());
auto local_answer2 = std::make_unique<cricket::SessionDescription>();
AddAudioSection(local_answer2.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
nullptr);
EXPECT_TRUE(transport_controller_
- ->SetRemoteDescription(SdpType::kOffer, remote_offer2.get())
- .ok());
- EXPECT_TRUE(transport_controller_
- ->SetLocalDescription(SdpType::kAnswer, local_answer2.get())
+ ->SetLocalDescription(SdpType::kAnswer, local_answer2.get(),
+ remote_offer2.get())
.ok());
fake_dtls = static_cast<FakeDtlsTransport*>(
transport_controller_->GetDtlsTransport(kAudioMid1));
@@ -1145,11 +1181,13 @@ TEST_F(JsepTransportControllerTest, MultipleMediaSectionsOfSameTypeWithBundle) {
local_offer->AddGroup(bundle_group);
remote_answer->AddGroup(bundle_group);
+ EXPECT_TRUE(
+ transport_controller_
+ ->SetLocalDescription(SdpType::kOffer, local_offer.get(), nullptr)
+ .ok());
EXPECT_TRUE(transport_controller_
- ->SetLocalDescription(SdpType::kOffer, local_offer.get())
- .ok());
- EXPECT_TRUE(transport_controller_
- ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
+ ->SetRemoteDescription(SdpType::kAnswer, local_offer.get(),
+ remote_answer.get())
.ok());
// Verify that all the sections are bundled on kAudio1.
auto transport1 = transport_controller_->GetRtpTransport(kAudioMid1);
@@ -1224,11 +1262,13 @@ TEST_F(JsepTransportControllerTest, MultipleBundleGroups) {
remote_answer->AddGroup(bundle_group1);
remote_answer->AddGroup(bundle_group2);
+ EXPECT_TRUE(
+ transport_controller_
+ ->SetLocalDescription(SdpType::kOffer, local_offer.get(), nullptr)
+ .ok());
EXPECT_TRUE(transport_controller_
- ->SetLocalDescription(SdpType::kOffer, local_offer.get())
- .ok());
- EXPECT_TRUE(transport_controller_
- ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
+ ->SetRemoteDescription(SdpType::kAnswer, local_offer.get(),
+ remote_answer.get())
.ok());
// Verify that (kMid1Audio,kMid2Video) and (kMid3Audio,kMid4Video) form two
@@ -1307,11 +1347,13 @@ TEST_F(JsepTransportControllerTest,
// endpoint that does not have support for multiple BUNDLE groups.
remote_answer->AddGroup(bundle_group1);
+ EXPECT_TRUE(
+ transport_controller_
+ ->SetLocalDescription(SdpType::kOffer, local_offer.get(), nullptr)
+ .ok());
EXPECT_TRUE(transport_controller_
- ->SetLocalDescription(SdpType::kOffer, local_offer.get())
- .ok());
- EXPECT_TRUE(transport_controller_
- ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
+ ->SetRemoteDescription(SdpType::kAnswer, local_offer.get(),
+ remote_answer.get())
.ok());
// Verify that (kMid1Audio,kMid2Video) form a bundle group, but that
@@ -1382,12 +1424,14 @@ TEST_F(JsepTransportControllerTest, MultipleBundleGroupsIllegallyChangeGroup) {
remote_answer->AddGroup(answer_bundle_group2);
// Accept offer.
- EXPECT_TRUE(transport_controller_
- ->SetLocalDescription(SdpType::kOffer, local_offer.get())
- .ok());
+ EXPECT_TRUE(
+ transport_controller_
+ ->SetLocalDescription(SdpType::kOffer, local_offer.get(), nullptr)
+ .ok());
// Reject answer!
EXPECT_FALSE(transport_controller_
- ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
+ ->SetRemoteDescription(SdpType::kAnswer, local_offer.get(),
+ remote_answer.get())
.ok());
}
@@ -1445,12 +1489,14 @@ TEST_F(JsepTransportControllerTest, MultipleBundleGroupsInvalidSubsets) {
remote_answer->AddGroup(answer_bundle_group2);
// Accept offer.
- EXPECT_TRUE(transport_controller_
- ->SetLocalDescription(SdpType::kOffer, local_offer.get())
- .ok());
+ EXPECT_TRUE(
+ transport_controller_
+ ->SetLocalDescription(SdpType::kOffer, local_offer.get(), nullptr)
+ .ok());
// Reject answer!
EXPECT_FALSE(transport_controller_
- ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
+ ->SetRemoteDescription(SdpType::kAnswer, local_offer.get(),
+ remote_answer.get())
.ok());
}
@@ -1483,11 +1529,12 @@ TEST_F(JsepTransportControllerTest, MultipleBundleGroupsInvalidOverlap) {
offer->AddGroup(offer_bundle_group2);
// Reject offer, both if set as local or remote.
+ EXPECT_FALSE(transport_controller_
+ ->SetLocalDescription(SdpType::kOffer, offer.get(), nullptr)
+ .ok());
EXPECT_FALSE(
- transport_controller_->SetLocalDescription(SdpType::kOffer, offer.get())
- .ok());
- EXPECT_FALSE(
- transport_controller_->SetRemoteDescription(SdpType::kOffer, offer.get())
+ transport_controller_
+ ->SetRemoteDescription(SdpType::kOffer, offer.get(), offer.get())
.ok());
}
@@ -1563,11 +1610,13 @@ TEST_F(JsepTransportControllerTest, MultipleBundleGroupsUnbundleFirstMid) {
remote_answer->AddGroup(answer_bundle_group1);
remote_answer->AddGroup(answer_bundle_group2);
+ EXPECT_TRUE(
+ transport_controller_
+ ->SetLocalDescription(SdpType::kOffer, local_offer.get(), nullptr)
+ .ok());
EXPECT_TRUE(transport_controller_
- ->SetLocalDescription(SdpType::kOffer, local_offer.get())
- .ok());
- EXPECT_TRUE(transport_controller_
- ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
+ ->SetRemoteDescription(SdpType::kAnswer, local_offer.get(),
+ remote_answer.get())
.ok());
auto mid1_transport = transport_controller_->GetRtpTransport(kMid1Audio);
@@ -1659,9 +1708,10 @@ TEST_F(JsepTransportControllerTest, MultipleBundleGroupsChangeFirstMid) {
remote_answer->AddGroup(answer_bundle_group1);
remote_answer->AddGroup(answer_bundle_group2);
- EXPECT_TRUE(transport_controller_
- ->SetLocalDescription(SdpType::kOffer, local_offer.get())
- .ok());
+ EXPECT_TRUE(
+ transport_controller_
+ ->SetLocalDescription(SdpType::kOffer, local_offer.get(), nullptr)
+ .ok());
// The fact that we accept this answer is actually a bug. If we accept the
// first MID to be in the group, we should also accept that it is the tagged
@@ -1669,7 +1719,8 @@ TEST_F(JsepTransportControllerTest, MultipleBundleGroupsChangeFirstMid) {
// TODO(https://crbug.com/webrtc/12699): When this issue is fixed, change this
// to EXPECT_FALSE and remove the below expectations about transports.
EXPECT_TRUE(transport_controller_
- ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
+ ->SetRemoteDescription(SdpType::kAnswer, local_offer.get(),
+ remote_answer.get())
.ok());
auto mid1_transport = transport_controller_->GetRtpTransport(kMid1Audio);
auto mid2_transport = transport_controller_->GetRtpTransport(kMid2Audio);
@@ -1734,11 +1785,13 @@ TEST_F(JsepTransportControllerTest,
remote_answer->AddGroup(bundle_group1);
remote_answer->AddGroup(bundle_group2);
+ EXPECT_TRUE(
+ transport_controller_
+ ->SetLocalDescription(SdpType::kOffer, local_offer.get(), nullptr)
+ .ok());
EXPECT_TRUE(transport_controller_
- ->SetLocalDescription(SdpType::kOffer, local_offer.get())
- .ok());
- EXPECT_TRUE(transport_controller_
- ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
+ ->SetRemoteDescription(SdpType::kAnswer, local_offer.get(),
+ remote_answer.get())
.ok());
// Add kMid3Audio and kMid6Video to the respective audio/video bundle groups.
@@ -1769,7 +1822,8 @@ TEST_F(JsepTransportControllerTest,
subsequent_offer->AddGroup(bundle_group1);
subsequent_offer->AddGroup(bundle_group2);
EXPECT_TRUE(transport_controller_
- ->SetLocalDescription(SdpType::kOffer, subsequent_offer.get())
+ ->SetLocalDescription(SdpType::kOffer, subsequent_offer.get(),
+ remote_answer.get())
.ok());
auto mid1_transport = transport_controller_->GetRtpTransport(kMid1Audio);
auto mid2_transport = transport_controller_->GetRtpTransport(kMid2Audio);
@@ -1832,11 +1886,13 @@ TEST_F(JsepTransportControllerTest,
remote_answer->AddGroup(bundle_group1);
remote_answer->AddGroup(bundle_group2);
+ EXPECT_TRUE(
+ transport_controller_
+ ->SetLocalDescription(SdpType::kOffer, local_offer.get(), nullptr)
+ .ok());
EXPECT_TRUE(transport_controller_
- ->SetLocalDescription(SdpType::kOffer, local_offer.get())
- .ok());
- EXPECT_TRUE(transport_controller_
- ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
+ ->SetRemoteDescription(SdpType::kAnswer, local_offer.get(),
+ remote_answer.get())
.ok());
// Switch to grouping (kMid1Audio,kMid2Audio,kMid3Video,kMid4Video).
@@ -1861,10 +1917,11 @@ TEST_F(JsepTransportControllerTest,
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
nullptr);
subsequent_offer->AddGroup(new_bundle_group);
- EXPECT_FALSE(
- transport_controller_
- ->SetLocalDescription(SdpType::kOffer, subsequent_offer.get())
- .ok());
+ EXPECT_FALSE(transport_controller_
+ ->SetLocalDescription(SdpType::kOffer,
+ subsequent_offer.get(),
+ remote_answer.get())
+ .ok());
}
TEST_F(JsepTransportControllerTest,
@@ -1912,11 +1969,13 @@ TEST_F(JsepTransportControllerTest,
nullptr);
remote_answer->AddGroup(bundle_group);
+ EXPECT_TRUE(
+ transport_controller_
+ ->SetLocalDescription(SdpType::kOffer, local_offer.get(), nullptr)
+ .ok());
EXPECT_TRUE(transport_controller_
- ->SetLocalDescription(SdpType::kOffer, local_offer.get())
- .ok());
- EXPECT_TRUE(transport_controller_
- ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
+ ->SetRemoteDescription(SdpType::kAnswer, local_offer.get(),
+ remote_answer.get())
.ok());
// Switch to grouping (kMid1Audio,kMid2Audio) and (kMid3Video,kMid4Video).
@@ -1943,10 +2002,11 @@ TEST_F(JsepTransportControllerTest,
nullptr);
subsequent_offer->AddGroup(new_bundle_group1);
subsequent_offer->AddGroup(new_bundle_group2);
- EXPECT_FALSE(
- transport_controller_
- ->SetLocalDescription(SdpType::kOffer, subsequent_offer.get())
- .ok());
+ EXPECT_FALSE(transport_controller_
+ ->SetLocalDescription(SdpType::kOffer,
+ subsequent_offer.get(),
+ remote_answer.get())
+ .ok());
}
TEST_F(JsepTransportControllerTest,
@@ -1997,11 +2057,13 @@ TEST_F(JsepTransportControllerTest,
remote_answer->AddGroup(bundle_group1);
remote_answer->AddGroup(bundle_group2);
+ EXPECT_TRUE(
+ transport_controller_
+ ->SetLocalDescription(SdpType::kOffer, local_offer.get(), nullptr)
+ .ok());
EXPECT_TRUE(transport_controller_
- ->SetLocalDescription(SdpType::kOffer, local_offer.get())
- .ok());
- EXPECT_TRUE(transport_controller_
- ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
+ ->SetRemoteDescription(SdpType::kAnswer, local_offer.get(),
+ remote_answer.get())
.ok());
// Switch to grouping (kMid1Audio,kMid3Video) and (kMid2Audio,kMid3Video).
@@ -2028,10 +2090,11 @@ TEST_F(JsepTransportControllerTest,
nullptr);
subsequent_offer->AddGroup(new_bundle_group1);
subsequent_offer->AddGroup(new_bundle_group2);
- EXPECT_FALSE(
- transport_controller_
- ->SetLocalDescription(SdpType::kOffer, subsequent_offer.get())
- .ok());
+ EXPECT_FALSE(transport_controller_
+ ->SetLocalDescription(SdpType::kOffer,
+ subsequent_offer.get(),
+ remote_answer.get())
+ .ok());
}
// Tests that only a subset of all the m= sections are bundled.
@@ -2065,11 +2128,13 @@ TEST_F(JsepTransportControllerTest, BundleSubsetOfMediaSections) {
local_offer->AddGroup(bundle_group);
remote_answer->AddGroup(bundle_group);
+ EXPECT_TRUE(
+ transport_controller_
+ ->SetLocalDescription(SdpType::kOffer, local_offer.get(), nullptr)
+ .ok());
EXPECT_TRUE(transport_controller_
- ->SetLocalDescription(SdpType::kOffer, local_offer.get())
- .ok());
- EXPECT_TRUE(transport_controller_
- ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
+ ->SetRemoteDescription(SdpType::kAnswer, local_offer.get(),
+ remote_answer.get())
.ok());
// Verifiy that only `kAudio1` and `kVideo1` are bundled.
@@ -2106,11 +2171,13 @@ TEST_F(JsepTransportControllerTest, BundleOnDataSectionInSubsequentOffer) {
local_offer->AddGroup(bundle_group);
remote_answer->AddGroup(bundle_group);
+ EXPECT_TRUE(
+ transport_controller_
+ ->SetLocalDescription(SdpType::kOffer, local_offer.get(), nullptr)
+ .ok());
EXPECT_TRUE(transport_controller_
- ->SetLocalDescription(SdpType::kOffer, local_offer.get())
- .ok());
- EXPECT_TRUE(transport_controller_
- ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
+ ->SetRemoteDescription(SdpType::kAnswer, local_offer.get(),
+ remote_answer.get())
.ok());
auto data_transport = transport_controller_->GetRtpTransport(kDataMid1);
@@ -2132,15 +2199,17 @@ TEST_F(JsepTransportControllerTest, BundleOnDataSectionInSubsequentOffer) {
bundle_group.AddContentName(kAudioMid1);
bundle_group.AddContentName(kVideoMid1);
local_offer->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
- remote_answer->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
local_offer->AddGroup(bundle_group);
- remote_answer->AddGroup(bundle_group);
EXPECT_TRUE(transport_controller_
- ->SetLocalDescription(SdpType::kOffer, local_offer.get())
+ ->SetLocalDescription(SdpType::kOffer, local_offer.get(),
+ remote_answer.get())
.ok());
+ remote_answer->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
+ remote_answer->AddGroup(bundle_group);
EXPECT_TRUE(transport_controller_
- ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
+ ->SetRemoteDescription(SdpType::kAnswer, local_offer.get(),
+ remote_answer.get())
.ok());
auto audio_transport = transport_controller_->GetRtpTransport(kAudioMid1);
@@ -2186,11 +2255,13 @@ TEST_F(JsepTransportControllerTest, VideoDataRejectedInAnswer) {
local_offer->AddGroup(bundle_group);
remote_answer->AddGroup(bundle_group);
+ EXPECT_TRUE(
+ transport_controller_
+ ->SetLocalDescription(SdpType::kOffer, local_offer.get(), nullptr)
+ .ok());
EXPECT_TRUE(transport_controller_
- ->SetLocalDescription(SdpType::kOffer, local_offer.get())
- .ok());
- EXPECT_TRUE(transport_controller_
- ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
+ ->SetRemoteDescription(SdpType::kAnswer, local_offer.get(),
+ remote_answer.get())
.ok());
// Verify the RtpTransport/DtlsTransport is destroyed correctly.
@@ -2233,11 +2304,13 @@ TEST_F(JsepTransportControllerTest, ChangeBundledMidNotSupported) {
local_offer->AddGroup(bundle_group);
remote_answer->AddGroup(bundle_group);
+ EXPECT_TRUE(
+ transport_controller_
+ ->SetLocalDescription(SdpType::kOffer, local_offer.get(), nullptr)
+ .ok());
EXPECT_TRUE(transport_controller_
- ->SetLocalDescription(SdpType::kOffer, local_offer.get())
- .ok());
- EXPECT_TRUE(transport_controller_
- ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
+ ->SetRemoteDescription(SdpType::kAnswer, local_offer.get(),
+ remote_answer.get())
.ok());
EXPECT_EQ(transport_controller_->GetRtpTransport(kAudioMid1),
transport_controller_->GetRtpTransport(kVideoMid1));
@@ -2245,15 +2318,17 @@ TEST_F(JsepTransportControllerTest, ChangeBundledMidNotSupported) {
// Reorder the bundle group.
EXPECT_TRUE(bundle_group.RemoveContentName(kAudioMid1));
bundle_group.AddContentName(kAudioMid1);
+ EXPECT_TRUE(transport_controller_
+ ->SetLocalDescription(SdpType::kOffer, local_offer.get(),
+ remote_answer.get())
+ .ok());
// The answerer uses the new bundle group and now the bundle mid is changed to
// `kVideo1`.
remote_answer->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
remote_answer->AddGroup(bundle_group);
- EXPECT_TRUE(transport_controller_
- ->SetLocalDescription(SdpType::kOffer, local_offer.get())
- .ok());
EXPECT_FALSE(transport_controller_
- ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
+ ->SetRemoteDescription(SdpType::kAnswer, local_offer.get(),
+ remote_answer.get())
.ok());
}
// Test that rejecting only the first m= section of a BUNDLE group is treated as
@@ -2294,18 +2369,21 @@ TEST_F(JsepTransportControllerTest, RejectFirstContentInBundleGroup) {
local_offer->AddGroup(bundle_group);
remote_answer->AddGroup(bundle_group);
- EXPECT_TRUE(transport_controller_
- ->SetLocalDescription(SdpType::kOffer, local_offer.get())
- .ok());
+ EXPECT_TRUE(
+ transport_controller_
+ ->SetLocalDescription(SdpType::kOffer, local_offer.get(), nullptr)
+ .ok());
EXPECT_FALSE(transport_controller_
- ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
+ ->SetRemoteDescription(SdpType::kAnswer, local_offer.get(),
+ remote_answer.get())
.ok());
// Reject all the contents.
remote_answer->contents()[1].rejected = true;
remote_answer->contents()[2].rejected = true;
EXPECT_TRUE(transport_controller_
- ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
+ ->SetRemoteDescription(SdpType::kAnswer, local_offer.get(),
+ remote_answer.get())
.ok());
EXPECT_EQ(nullptr, transport_controller_->GetRtpTransport(kAudioMid1));
EXPECT_EQ(nullptr, transport_controller_->GetRtpTransport(kVideoMid1));
@@ -2325,9 +2403,10 @@ TEST_F(JsepTransportControllerTest, ApplyNonRtcpMuxOfferWhenMuxingRequired) {
local_offer->contents()[0].media_description()->set_rtcp_mux(false);
// Applying a non-RTCP-mux offer is expected to fail.
- EXPECT_FALSE(transport_controller_
- ->SetLocalDescription(SdpType::kOffer, local_offer.get())
- .ok());
+ EXPECT_FALSE(
+ transport_controller_
+ ->SetLocalDescription(SdpType::kOffer, local_offer.get(), nullptr)
+ .ok());
}
// Tests that applying non-RTCP-mux answer would fail when kRtcpMuxPolicyRequire
@@ -2340,9 +2419,10 @@ TEST_F(JsepTransportControllerTest, ApplyNonRtcpMuxAnswerWhenMuxingRequired) {
AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
nullptr);
- EXPECT_TRUE(transport_controller_
- ->SetLocalDescription(SdpType::kOffer, local_offer.get())
- .ok());
+ EXPECT_TRUE(
+ transport_controller_
+ ->SetLocalDescription(SdpType::kOffer, local_offer.get(), nullptr)
+ .ok());
auto remote_answer = std::make_unique<cricket::SessionDescription>();
AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
@@ -2351,7 +2431,8 @@ TEST_F(JsepTransportControllerTest, ApplyNonRtcpMuxAnswerWhenMuxingRequired) {
// Applying a non-RTCP-mux answer is expected to fail.
remote_answer->contents()[0].media_description()->set_rtcp_mux(false);
EXPECT_FALSE(transport_controller_
- ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
+ ->SetRemoteDescription(SdpType::kAnswer, local_offer.get(),
+ remote_answer.get())
.ok());
}
@@ -2371,11 +2452,13 @@ TEST_F(JsepTransportControllerTest,
answer_bundle_group.AddContentName(kAudioMid1);
answer_bundle_group.AddContentName(kVideoMid1);
remote_answer->AddGroup(answer_bundle_group);
- EXPECT_TRUE(transport_controller_
- ->SetLocalDescription(SdpType::kOffer, local_offer.get())
- .ok());
+ EXPECT_TRUE(
+ transport_controller_
+ ->SetLocalDescription(SdpType::kOffer, local_offer.get(), nullptr)
+ .ok());
EXPECT_FALSE(transport_controller_
- ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
+ ->SetRemoteDescription(SdpType::kAnswer, local_offer.get(),
+ remote_answer.get())
.ok());
}
@@ -2392,11 +2475,13 @@ TEST_F(JsepTransportControllerTest, RejectBundleGroupWithNonExistingMid) {
local_offer->AddGroup(invalid_bundle_group);
remote_answer->AddGroup(invalid_bundle_group);
+ EXPECT_FALSE(
+ transport_controller_
+ ->SetLocalDescription(SdpType::kOffer, local_offer.get(), nullptr)
+ .ok());
EXPECT_FALSE(transport_controller_
- ->SetLocalDescription(SdpType::kOffer, local_offer.get())
- .ok());
- EXPECT_FALSE(transport_controller_
- ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
+ ->SetRemoteDescription(SdpType::kAnswer, local_offer.get(),
+ remote_answer.get())
.ok());
}
@@ -2407,16 +2492,19 @@ TEST_F(JsepTransportControllerTest, RemoveContentFromBundleGroup) {
auto local_offer = CreateSessionDescriptionWithBundleGroup();
auto remote_answer = CreateSessionDescriptionWithBundleGroup();
+ EXPECT_TRUE(
+ transport_controller_
+ ->SetLocalDescription(SdpType::kOffer, local_offer.get(), nullptr)
+ .ok());
EXPECT_TRUE(transport_controller_
- ->SetLocalDescription(SdpType::kOffer, local_offer.get())
- .ok());
- EXPECT_TRUE(transport_controller_
- ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
+ ->SetRemoteDescription(SdpType::kAnswer, local_offer.get(),
+ remote_answer.get())
.ok());
// Do an re-offer/answer.
EXPECT_TRUE(transport_controller_
- ->SetLocalDescription(SdpType::kOffer, local_offer.get())
+ ->SetLocalDescription(SdpType::kOffer, local_offer.get(),
+ remote_answer.get())
.ok());
auto new_answer = CreateSessionDescriptionWithoutBundle();
cricket::ContentGroup new_bundle_group(cricket::GROUP_TYPE_BUNDLE);
@@ -2427,7 +2515,8 @@ TEST_F(JsepTransportControllerTest, RemoveContentFromBundleGroup) {
// Applying invalid answer is expected to fail.
EXPECT_FALSE(transport_controller_
- ->SetRemoteDescription(SdpType::kAnswer, new_answer.get())
+ ->SetRemoteDescription(SdpType::kAnswer, local_offer.get(),
+ new_answer.get())
.ok());
// Rejected the video content.
@@ -2435,7 +2524,8 @@ TEST_F(JsepTransportControllerTest, RemoveContentFromBundleGroup) {
ASSERT_TRUE(video_content);
video_content->rejected = true;
EXPECT_TRUE(transport_controller_
- ->SetRemoteDescription(SdpType::kAnswer, new_answer.get())
+ ->SetRemoteDescription(SdpType::kAnswer, local_offer.get(),
+ new_answer.get())
.ok());
}
@@ -2453,14 +2543,16 @@ TEST_F(JsepTransportControllerTest, ChangeTaggedMediaSectionMaxBundle) {
cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
bundle_group.AddContentName(kAudioMid1);
local_offer->AddGroup(bundle_group);
- EXPECT_TRUE(transport_controller_
- ->SetLocalDescription(SdpType::kOffer, local_offer.get())
- .ok());
+ EXPECT_TRUE(
+ transport_controller_
+ ->SetLocalDescription(SdpType::kOffer, local_offer.get(), nullptr)
+ .ok());
std::unique_ptr<cricket::SessionDescription> remote_answer(
local_offer->Clone());
EXPECT_TRUE(transport_controller_
- ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
+ ->SetRemoteDescription(SdpType::kAnswer, local_offer.get(),
+ remote_answer.get())
.ok());
std::unique_ptr<cricket::SessionDescription> local_reoffer(
@@ -2475,14 +2567,15 @@ TEST_F(JsepTransportControllerTest, ChangeTaggedMediaSectionMaxBundle) {
local_reoffer->AddGroup(new_bundle_group);
EXPECT_TRUE(transport_controller_
- ->SetLocalDescription(SdpType::kOffer, local_reoffer.get())
+ ->SetLocalDescription(SdpType::kOffer, local_reoffer.get(),
+ remote_answer.get())
.ok());
std::unique_ptr<cricket::SessionDescription> remote_reanswer(
local_reoffer->Clone());
- EXPECT_TRUE(
- transport_controller_
- ->SetRemoteDescription(SdpType::kAnswer, remote_reanswer.get())
- .ok());
+ EXPECT_TRUE(transport_controller_
+ ->SetRemoteDescription(SdpType::kAnswer, local_reoffer.get(),
+ remote_reanswer.get())
+ .ok());
}
TEST_F(JsepTransportControllerTest, RollbackRestoresRejectedTransport) {
@@ -2496,11 +2589,13 @@ TEST_F(JsepTransportControllerTest, RollbackRestoresRejectedTransport) {
nullptr);
std::unique_ptr<cricket::SessionDescription> remote_answer(
local_offer->Clone());
+ EXPECT_TRUE(
+ transport_controller_
+ ->SetLocalDescription(SdpType::kOffer, local_offer.get(), nullptr)
+ .ok());
EXPECT_TRUE(transport_controller_
- ->SetLocalDescription(SdpType::kOffer, local_offer.get())
- .ok());
- EXPECT_TRUE(transport_controller_
- ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
+ ->SetRemoteDescription(SdpType::kAnswer, local_offer.get(),
+ remote_answer.get())
.ok());
auto mid1_transport = transport_controller_->GetRtpTransport(kMid1Audio);
@@ -2514,7 +2609,8 @@ TEST_F(JsepTransportControllerTest, RollbackRestoresRejectedTransport) {
local_reoffer->contents()[0].rejected = true;
EXPECT_TRUE(transport_controller_
- ->SetLocalDescription(SdpType::kOffer, local_reoffer.get())
+ ->SetLocalDescription(SdpType::kOffer, local_reoffer.get(),
+ remote_answer.get())
.ok());
auto old_mid1_transport = mid1_transport;
mid1_transport = transport_controller_->GetRtpTransport(kMid1Audio);
@@ -2556,11 +2652,13 @@ TEST_F(JsepTransportControllerTest, RollbackRestoresPreviousTransportMapping) {
std::unique_ptr<cricket::SessionDescription> remote_answer(
local_offer->Clone());
+ EXPECT_TRUE(
+ transport_controller_
+ ->SetLocalDescription(SdpType::kOffer, local_offer.get(), nullptr)
+ .ok());
EXPECT_TRUE(transport_controller_
- ->SetLocalDescription(SdpType::kOffer, local_offer.get())
- .ok());
- EXPECT_TRUE(transport_controller_
- ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
+ ->SetRemoteDescription(SdpType::kAnswer, local_offer.get(),
+ remote_answer.get())
.ok());
auto mid1_transport = transport_controller_->GetRtpTransport(kMid1Audio);
@@ -2585,7 +2683,8 @@ TEST_F(JsepTransportControllerTest, RollbackRestoresPreviousTransportMapping) {
local_reoffer->AddGroup(bundle_group);
EXPECT_TRUE(transport_controller_
- ->SetLocalDescription(SdpType::kOffer, local_reoffer.get())
+ ->SetLocalDescription(SdpType::kOffer, local_reoffer.get(),
+ remote_answer.get())
.ok());
// Store the old transport pointer and verify that the offer actually changed
@@ -2633,11 +2732,13 @@ TEST_F(JsepTransportControllerTest, RollbackAndAddToDifferentBundleGroup) {
std::unique_ptr<cricket::SessionDescription> remote_answer(
local_offer->Clone());
+ EXPECT_TRUE(
+ transport_controller_
+ ->SetLocalDescription(SdpType::kOffer, local_offer.get(), nullptr)
+ .ok());
EXPECT_TRUE(transport_controller_
- ->SetLocalDescription(SdpType::kOffer, local_offer.get())
- .ok());
- EXPECT_TRUE(transport_controller_
- ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
+ ->SetRemoteDescription(SdpType::kAnswer, local_offer.get(),
+ remote_answer.get())
.ok());
// Apply an offer that adds kMid3Audio to the first BUNDLE group.,
@@ -2657,10 +2758,11 @@ TEST_F(JsepTransportControllerTest, RollbackAndAddToDifferentBundleGroup) {
subsequent_offer_1->AddGroup(modified_bundle_group1);
subsequent_offer_1->AddGroup(bundle_group2);
- EXPECT_TRUE(
- transport_controller_
- ->SetLocalDescription(SdpType::kOffer, subsequent_offer_1.get())
- .ok());
+ EXPECT_TRUE(transport_controller_
+ ->SetLocalDescription(SdpType::kOffer,
+ subsequent_offer_1.get(),
+ remote_answer.get())
+ .ok());
auto mid1_transport = transport_controller_->GetRtpTransport(kMid1Audio);
auto mid2_transport = transport_controller_->GetRtpTransport(kMid2Audio);
@@ -2689,10 +2791,11 @@ TEST_F(JsepTransportControllerTest, RollbackAndAddToDifferentBundleGroup) {
subsequent_offer_2->AddGroup(bundle_group1);
subsequent_offer_2->AddGroup(modified_bundle_group2);
- EXPECT_TRUE(
- transport_controller_
- ->SetLocalDescription(SdpType::kOffer, subsequent_offer_2.get())
- .ok());
+ EXPECT_TRUE(transport_controller_
+ ->SetLocalDescription(SdpType::kOffer,
+ subsequent_offer_2.get(),
+ remote_answer.get())
+ .ok());
mid1_transport = transport_controller_->GetRtpTransport(kMid1Audio);
mid2_transport = transport_controller_->GetRtpTransport(kMid2Audio);
@@ -2722,9 +2825,9 @@ TEST_F(JsepTransportControllerTest, BundleOnlySectionDoesNotNeedRtcpMux) {
offer->contents()[1].media_description()->set_rtcp_mux(false);
offer->contents()[1].bundle_only = true;
- EXPECT_TRUE(
- transport_controller_->SetRemoteDescription(SdpType::kOffer, offer.get())
- .ok());
+ EXPECT_TRUE(transport_controller_
+ ->SetRemoteDescription(SdpType::kOffer, nullptr, offer.get())
+ .ok());
}
// Test that with max-bundle a single unbundled m-line is accepted.
@@ -2738,9 +2841,9 @@ TEST_F(JsepTransportControllerTest,
AddAudioSection(offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
nullptr);
- EXPECT_TRUE(
- transport_controller_->SetRemoteDescription(SdpType::kOffer, offer.get())
- .ok());
+ EXPECT_TRUE(transport_controller_
+ ->SetRemoteDescription(SdpType::kOffer, nullptr, offer.get())
+ .ok());
}
} // namespace webrtc
diff --git a/third_party/libwebrtc/pc/legacy_stats_collector.cc b/third_party/libwebrtc/pc/legacy_stats_collector.cc
index 3bc65ee3ee..98b7cb9677 100644
--- a/third_party/libwebrtc/pc/legacy_stats_collector.cc
+++ b/third_party/libwebrtc/pc/legacy_stats_collector.cc
@@ -355,9 +355,8 @@ void ExtractStats(const cricket::VideoReceiverInfo& info,
report->AddInt64(StatsReport::kStatsValueNameInterframeDelayMaxMs,
info.interframe_delay_max_ms);
- report->AddString(
- StatsReport::kStatsValueNameContentType,
- webrtc::videocontenttypehelpers::ToString(info.content_type));
+ report->AddString(StatsReport::kStatsValueNameContentType,
+ videocontenttypehelpers::ToString(info.content_type));
}
void ExtractStats(const cricket::VideoSenderInfo& info,
@@ -398,9 +397,8 @@ void ExtractStats(const cricket::VideoSenderInfo& info,
for (const auto& i : ints)
report->AddInt(i.name, i.value);
report->AddString(StatsReport::kStatsValueNameMediaType, "video");
- report->AddString(
- StatsReport::kStatsValueNameContentType,
- webrtc::videocontenttypehelpers::ToString(info.content_type));
+ report->AddString(StatsReport::kStatsValueNameContentType,
+ videocontenttypehelpers::ToString(info.content_type));
}
void ExtractStats(const cricket::BandwidthEstimationInfo& info,
@@ -1033,7 +1031,7 @@ void LegacyStatsCollector::ExtractBweInfo() {
if (pc_->signaling_state() == PeerConnectionInterface::kClosed)
return;
- webrtc::Call::Stats call_stats = pc_->GetCallStats();
+ Call::Stats call_stats = pc_->GetCallStats();
cricket::BandwidthEstimationInfo bwe_info;
bwe_info.available_send_bandwidth = call_stats.send_bandwidth_bps;
bwe_info.available_recv_bandwidth = call_stats.recv_bandwidth_bps;
diff --git a/third_party/libwebrtc/pc/legacy_stats_collector.h b/third_party/libwebrtc/pc/legacy_stats_collector.h
index e905b39d48..1c7aad0636 100644
--- a/third_party/libwebrtc/pc/legacy_stats_collector.h
+++ b/third_party/libwebrtc/pc/legacy_stats_collector.h
@@ -177,9 +177,9 @@ class LegacyStatsCollector : public LegacyStatsCollectorInterface {
void ExtractMediaInfo(
const std::map<std::string, std::string>& transport_names_by_mid);
void ExtractSenderInfo();
- webrtc::StatsReport* GetReport(const StatsReport::StatsType& type,
- const std::string& id,
- StatsReport::Direction direction);
+ StatsReport* GetReport(const StatsReport::StatsType& type,
+ const std::string& id,
+ StatsReport::Direction direction);
// Helper method to get stats from the local audio tracks.
void UpdateStatsFromExistingLocalAudioTracks(bool has_remote_tracks);
diff --git a/third_party/libwebrtc/pc/media_factory.h b/third_party/libwebrtc/pc/media_factory.h
new file mode 100644
index 0000000000..c86784632e
--- /dev/null
+++ b/third_party/libwebrtc/pc/media_factory.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2023 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef PC_MEDIA_FACTORY_H_
+#define PC_MEDIA_FACTORY_H_
+
+#include <memory>
+
+#include "api/environment/environment.h"
+#include "call/call.h"
+#include "call/call_config.h"
+#include "media/base/media_engine.h"
+
+namespace webrtc {
+
+// PeerConnectionFactoryDependencies is forward declared because of circular
+// dependency between MediaFactory and PeerConnectionFactoryDependencies:
+// PeerConnectionFactoryDependencies keeps an instance of MediaFactory and thus
+// needs to know how to destroy it.
+// MediaFactory mentions PeerConnectionFactoryDependencies in api, but does not
+// need its full definition.
+struct PeerConnectionFactoryDependencies;
+
+// Interface repsponsible for constructing media specific classes for
+// PeerConnectionFactory and PeerConnection.
+class MediaFactory {
+ public:
+ virtual ~MediaFactory() = default;
+
+ virtual std::unique_ptr<Call> CreateCall(const CallConfig& config) = 0;
+ virtual std::unique_ptr<cricket::MediaEngineInterface> CreateMediaEngine(
+ const Environment& env,
+ PeerConnectionFactoryDependencies& dependencies) = 0;
+};
+
+} // namespace webrtc
+
+#endif // PC_MEDIA_FACTORY_H_
diff --git a/third_party/libwebrtc/pc/media_session.cc b/third_party/libwebrtc/pc/media_session.cc
index a763919c16..573e35225e 100644
--- a/third_party/libwebrtc/pc/media_session.cc
+++ b/third_party/libwebrtc/pc/media_session.cc
@@ -117,52 +117,38 @@ namespace cricket {
namespace {
-bool IsRtxCodec(const Codec& codec) {
- return absl::EqualsIgnoreCase(codec.name, kRtxCodecName);
-}
-
bool IsRtxCodec(const webrtc::RtpCodecCapability& capability) {
return absl::EqualsIgnoreCase(capability.name, kRtxCodecName);
}
bool ContainsRtxCodec(const std::vector<Codec>& codecs) {
- for (const auto& codec : codecs) {
- if (IsRtxCodec(codec)) {
- return true;
- }
- }
- return false;
-}
-
-bool IsRedCodec(const Codec& codec) {
- return absl::EqualsIgnoreCase(codec.name, kRedCodecName);
+ return absl::c_find_if(codecs, [](const Codec& c) {
+ return c.GetResiliencyType() == Codec::ResiliencyType::kRtx;
+ }) != codecs.end();
}
bool IsRedCodec(const webrtc::RtpCodecCapability& capability) {
return absl::EqualsIgnoreCase(capability.name, kRedCodecName);
}
-bool IsFlexfecCodec(const Codec& codec) {
- return absl::EqualsIgnoreCase(codec.name, kFlexfecCodecName);
-}
-
bool ContainsFlexfecCodec(const std::vector<Codec>& codecs) {
- for (const auto& codec : codecs) {
- if (IsFlexfecCodec(codec)) {
- return true;
- }
- }
- return false;
-}
-
-bool IsUlpfecCodec(const Codec& codec) {
- return absl::EqualsIgnoreCase(codec.name, kUlpfecCodecName);
+ return absl::c_find_if(codecs, [](const Codec& c) {
+ return c.GetResiliencyType() == Codec::ResiliencyType::kFlexfec;
+ }) != codecs.end();
}
bool IsComfortNoiseCodec(const Codec& codec) {
return absl::EqualsIgnoreCase(codec.name, kComfortNoiseCodecName);
}
+void StripCNCodecs(AudioCodecs* audio_codecs) {
+ audio_codecs->erase(std::remove_if(audio_codecs->begin(), audio_codecs->end(),
+ [](const AudioCodec& codec) {
+ return IsComfortNoiseCodec(codec);
+ }),
+ audio_codecs->end());
+}
+
RtpTransceiverDirection NegotiateRtpTransceiverDirection(
RtpTransceiverDirection offer,
RtpTransceiverDirection wants) {
@@ -727,12 +713,10 @@ RTCError CreateMediaContentOffer(
bool ReferencedCodecsMatch(const std::vector<Codec>& codecs1,
const int codec1_id,
const std::vector<Codec>& codecs2,
- const int codec2_id,
- const webrtc::FieldTrialsView* field_trials) {
+ const int codec2_id) {
const Codec* codec1 = FindCodecById(codecs1, codec1_id);
const Codec* codec2 = FindCodecById(codecs2, codec2_id);
- return codec1 != nullptr && codec2 != nullptr &&
- codec1->Matches(*codec2, field_trials);
+ return codec1 != nullptr && codec2 != nullptr && codec1->Matches(*codec2);
}
void NegotiatePacketization(const Codec& local_codec,
@@ -747,11 +731,9 @@ void NegotiatePacketization(const Codec& local_codec,
// Finds a codec in `codecs2` that matches `codec_to_match`, which is
// a member of `codecs1`. If `codec_to_match` is an RED or RTX codec, both
// the codecs themselves and their associated codecs must match.
-absl::optional<Codec> FindMatchingCodec(
- const std::vector<Codec>& codecs1,
- const std::vector<Codec>& codecs2,
- const Codec& codec_to_match,
- const webrtc::FieldTrialsView* field_trials) {
+absl::optional<Codec> FindMatchingCodec(const std::vector<Codec>& codecs1,
+ const std::vector<Codec>& codecs2,
+ const Codec& codec_to_match) {
// `codec_to_match` should be a member of `codecs1`, in order to look up
// RED/RTX codecs' associated codecs correctly. If not, that's a programming
// error.
@@ -759,8 +741,8 @@ absl::optional<Codec> FindMatchingCodec(
return &codec == &codec_to_match;
}));
for (const Codec& potential_match : codecs2) {
- if (potential_match.Matches(codec_to_match, field_trials)) {
- if (IsRtxCodec(codec_to_match)) {
+ if (potential_match.Matches(codec_to_match)) {
+ if (codec_to_match.GetResiliencyType() == Codec::ResiliencyType::kRtx) {
int apt_value_1 = 0;
int apt_value_2 = 0;
if (!codec_to_match.GetParam(kCodecParamAssociatedPayloadType,
@@ -770,11 +752,12 @@ absl::optional<Codec> FindMatchingCodec(
RTC_LOG(LS_WARNING) << "RTX missing associated payload type.";
continue;
}
- if (!ReferencedCodecsMatch(codecs1, apt_value_1, codecs2, apt_value_2,
- field_trials)) {
+ if (!ReferencedCodecsMatch(codecs1, apt_value_1, codecs2,
+ apt_value_2)) {
continue;
}
- } else if (IsRedCodec(codec_to_match)) {
+ } else if (codec_to_match.GetResiliencyType() ==
+ Codec::ResiliencyType::kRed) {
auto red_parameters_1 =
codec_to_match.params.find(kCodecParamNotInNameValueFormat);
auto red_parameters_2 =
@@ -814,7 +797,7 @@ absl::optional<Codec> FindMatchingCodec(
if (rtc::FromString(redundant_payloads_1[0], &red_value_1) &&
rtc::FromString(redundant_payloads_2[0], &red_value_2)) {
if (!ReferencedCodecsMatch(codecs1, red_value_1, codecs2,
- red_value_2, field_trials)) {
+ red_value_2)) {
continue;
}
}
@@ -832,18 +815,17 @@ absl::optional<Codec> FindMatchingCodec(
void NegotiateCodecs(const std::vector<Codec>& local_codecs,
const std::vector<Codec>& offered_codecs,
std::vector<Codec>* negotiated_codecs,
- bool keep_offer_order,
- const webrtc::FieldTrialsView* field_trials) {
+ bool keep_offer_order) {
for (const Codec& ours : local_codecs) {
absl::optional<Codec> theirs =
- FindMatchingCodec(local_codecs, offered_codecs, ours, field_trials);
+ FindMatchingCodec(local_codecs, offered_codecs, ours);
// Note that we intentionally only find one matching codec for each of our
// local codecs, in case the remote offer contains duplicate codecs.
if (theirs) {
Codec negotiated = ours;
NegotiatePacketization(ours, *theirs, &negotiated);
negotiated.IntersectFeedbackParams(*theirs);
- if (IsRtxCodec(negotiated)) {
+ if (negotiated.GetResiliencyType() == Codec::ResiliencyType::kRtx) {
const auto apt_it =
theirs->params.find(kCodecParamAssociatedPayloadType);
// FindMatchingCodec shouldn't return something with no apt value.
@@ -855,7 +837,8 @@ void NegotiateCodecs(const std::vector<Codec>& local_codecs,
if (rtx_time_it != theirs->params.end()) {
negotiated.SetParam(kCodecParamRtxTime, rtx_time_it->second);
}
- } else if (IsRedCodec(negotiated)) {
+ } else if (negotiated.GetResiliencyType() ==
+ Codec::ResiliencyType::kRed) {
const auto red_it =
theirs->params.find(kCodecParamNotInNameValueFormat);
if (red_it != theirs->params.end()) {
@@ -960,15 +943,15 @@ const Codec* GetAssociatedCodecForRed(const std::vector<Codec>& codec_list,
// collide.
void MergeCodecs(const std::vector<Codec>& reference_codecs,
std::vector<Codec>* offered_codecs,
- UsedPayloadTypes* used_pltypes,
- const webrtc::FieldTrialsView* field_trials) {
+ UsedPayloadTypes* used_pltypes) {
// Add all new codecs that are not RTX/RED codecs.
// The two-pass splitting of the loops means preferring payload types
// of actual codecs with respect to collisions.
for (const Codec& reference_codec : reference_codecs) {
- if (!IsRtxCodec(reference_codec) && !IsRedCodec(reference_codec) &&
- !FindMatchingCodec(reference_codecs, *offered_codecs, reference_codec,
- field_trials)) {
+ if (reference_codec.GetResiliencyType() != Codec::ResiliencyType::kRtx &&
+ reference_codec.GetResiliencyType() != Codec::ResiliencyType::kRed &&
+ !FindMatchingCodec(reference_codecs, *offered_codecs,
+ reference_codec)) {
Codec codec = reference_codec;
used_pltypes->FindAndSetIdUsed(&codec);
offered_codecs->push_back(codec);
@@ -977,9 +960,9 @@ void MergeCodecs(const std::vector<Codec>& reference_codecs,
// Add all new RTX or RED codecs.
for (const Codec& reference_codec : reference_codecs) {
- if (IsRtxCodec(reference_codec) &&
- !FindMatchingCodec(reference_codecs, *offered_codecs, reference_codec,
- field_trials)) {
+ if (reference_codec.GetResiliencyType() == Codec::ResiliencyType::kRtx &&
+ !FindMatchingCodec(reference_codecs, *offered_codecs,
+ reference_codec)) {
Codec rtx_codec = reference_codec;
const Codec* associated_codec =
GetAssociatedCodecForRtx(reference_codecs, rtx_codec);
@@ -989,7 +972,7 @@ void MergeCodecs(const std::vector<Codec>& reference_codecs,
// Find a codec in the offered list that matches the reference codec.
// Its payload type may be different than the reference codec.
absl::optional<Codec> matching_codec = FindMatchingCodec(
- reference_codecs, *offered_codecs, *associated_codec, field_trials);
+ reference_codecs, *offered_codecs, *associated_codec);
if (!matching_codec) {
RTC_LOG(LS_WARNING)
<< "Couldn't find matching " << associated_codec->name << " codec.";
@@ -1000,15 +983,16 @@ void MergeCodecs(const std::vector<Codec>& reference_codecs,
rtc::ToString(matching_codec->id);
used_pltypes->FindAndSetIdUsed(&rtx_codec);
offered_codecs->push_back(rtx_codec);
- } else if (IsRedCodec(reference_codec) &&
+ } else if (reference_codec.GetResiliencyType() ==
+ Codec::ResiliencyType::kRed &&
!FindMatchingCodec(reference_codecs, *offered_codecs,
- reference_codec, field_trials)) {
+ reference_codec)) {
Codec red_codec = reference_codec;
const Codec* associated_codec =
GetAssociatedCodecForRed(reference_codecs, red_codec);
if (associated_codec) {
absl::optional<Codec> matching_codec = FindMatchingCodec(
- reference_codecs, *offered_codecs, *associated_codec, field_trials);
+ reference_codecs, *offered_codecs, *associated_codec);
if (!matching_codec) {
RTC_LOG(LS_WARNING) << "Couldn't find matching "
<< associated_codec->name << " codec.";
@@ -1032,8 +1016,7 @@ void MergeCodecs(const std::vector<Codec>& reference_codecs,
std::vector<Codec> MatchCodecPreference(
const std::vector<webrtc::RtpCodecCapability>& codec_preferences,
const std::vector<Codec>& codecs,
- const std::vector<Codec>& supported_codecs,
- const webrtc::FieldTrialsView* field_trials) {
+ const std::vector<Codec>& supported_codecs) {
std::vector<Codec> filtered_codecs;
bool want_rtx = false;
bool want_red = false;
@@ -1060,12 +1043,13 @@ std::vector<Codec> MatchCodecPreference(
});
if (found_codec != supported_codecs.end()) {
- absl::optional<Codec> found_codec_with_correct_pt = FindMatchingCodec(
- supported_codecs, codecs, *found_codec, field_trials);
+ absl::optional<Codec> found_codec_with_correct_pt =
+ FindMatchingCodec(supported_codecs, codecs, *found_codec);
if (found_codec_with_correct_pt) {
// RED may already have been added if its primary codec is before RED
// in the codec list.
- bool is_red_codec = IsRedCodec(*found_codec_with_correct_pt);
+ bool is_red_codec = found_codec_with_correct_pt->GetResiliencyType() ==
+ Codec::ResiliencyType::kRed;
if (!is_red_codec || !red_was_added) {
filtered_codecs.push_back(*found_codec_with_correct_pt);
red_was_added = is_red_codec ? true : red_was_added;
@@ -1074,14 +1058,15 @@ std::vector<Codec> MatchCodecPreference(
// Search for the matching rtx or red codec.
if (want_red || want_rtx) {
for (const auto& codec : codecs) {
- if (IsRtxCodec(codec)) {
+ if (codec.GetResiliencyType() == Codec::ResiliencyType::kRtx) {
const auto apt =
codec.params.find(cricket::kCodecParamAssociatedPayloadType);
if (apt != codec.params.end() && apt->second == id) {
filtered_codecs.push_back(codec);
break;
}
- } else if (IsRedCodec(codec)) {
+ } else if (codec.GetResiliencyType() ==
+ Codec::ResiliencyType::kRed) {
// For RED, do not insert the codec again if it was already
// inserted. audio/red for opus gets enabled by having RED before
// the primary codec.
@@ -1110,10 +1095,8 @@ std::vector<Codec> MatchCodecPreference(
}
// Compute the union of `codecs1` and `codecs2`.
-std::vector<Codec> ComputeCodecsUnion(
- const std::vector<Codec>& codecs1,
- const std::vector<Codec>& codecs2,
- const webrtc::FieldTrialsView* field_trials) {
+std::vector<Codec> ComputeCodecsUnion(const std::vector<Codec>& codecs1,
+ const std::vector<Codec>& codecs2) {
std::vector<Codec> all_codecs;
UsedPayloadTypes used_payload_types;
for (const Codec& codec : codecs1) {
@@ -1124,7 +1107,7 @@ std::vector<Codec> ComputeCodecsUnion(
// Use MergeCodecs to merge the second half of our list as it already checks
// and fixes problems with duplicate payload types.
- MergeCodecs(codecs2, &all_codecs, &used_payload_types, field_trials);
+ MergeCodecs(codecs2, &all_codecs, &used_payload_types);
return all_codecs;
}
@@ -1333,14 +1316,6 @@ void NegotiateRtpHeaderExtensions(const RtpHeaderExtensions& local_extensions,
}
}
-void StripCNCodecs(AudioCodecs* audio_codecs) {
- audio_codecs->erase(std::remove_if(audio_codecs->begin(), audio_codecs->end(),
- [](const AudioCodec& codec) {
- return IsComfortNoiseCodec(codec);
- }),
- audio_codecs->end());
-}
-
bool SetCodecsInAnswer(const MediaContentDescription* offer,
const std::vector<Codec>& local_codecs,
const MediaDescriptionOptions& media_description_options,
@@ -1353,8 +1328,7 @@ bool SetCodecsInAnswer(const MediaContentDescription* offer,
offer->type() == MEDIA_TYPE_VIDEO);
std::vector<Codec> negotiated_codecs;
NegotiateCodecs(local_codecs, offer->codecs(), &negotiated_codecs,
- media_description_options.codec_preferences.empty(),
- &field_trials);
+ media_description_options.codec_preferences.empty());
answer->AddCodecs(negotiated_codecs);
answer->set_protocol(offer->protocol());
if (!AddStreamParams(media_description_options.sender_options,
@@ -1512,6 +1486,145 @@ bool IsDtlsActive(const ContentInfo* content,
.description.secure();
}
+webrtc::RTCErrorOr<AudioCodecs> GetNegotiatedCodecsForOffer(
+ const MediaDescriptionOptions& media_description_options,
+ const MediaSessionOptions& session_options,
+ const ContentInfo* current_content,
+ const std::vector<Codec>& codecs,
+ const std::vector<Codec>& supported_codecs) {
+ std::vector<Codec> filtered_codecs;
+ if (!media_description_options.codec_preferences.empty()) {
+ // Add the codecs from the current transceiver's codec preferences.
+ // They override any existing codecs from previous negotiations.
+ filtered_codecs = MatchCodecPreference(
+ media_description_options.codec_preferences, codecs, supported_codecs);
+ } else {
+ // Add the codecs from current content if it exists and is not rejected nor
+ // recycled.
+ if (current_content && !current_content->rejected &&
+ current_content->name == media_description_options.mid) {
+ if (!IsMediaContentOfType(current_content,
+ media_description_options.type)) {
+ // Can happen if the remote side re-uses a MID while recycling.
+ LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR,
+ "Media type for content with mid='" +
+ current_content->name +
+ "' does not match previous type.");
+ }
+ const MediaContentDescription* mcd = current_content->media_description();
+ for (const Codec& codec : mcd->codecs()) {
+ if (FindMatchingCodec(mcd->codecs(), codecs, codec)) {
+ filtered_codecs.push_back(codec);
+ }
+ }
+ }
+ // Add other supported codecs.
+ for (const Codec& codec : supported_codecs) {
+ absl::optional<Codec> found_codec =
+ FindMatchingCodec(supported_codecs, codecs, codec);
+ if (found_codec &&
+ !FindMatchingCodec(supported_codecs, filtered_codecs, codec)) {
+ // Use the `found_codec` from `codecs` because it has the
+ // correctly mapped payload type.
+ // This is only done for video since we do not yet have rtx for audio.
+ if (media_description_options.type == MEDIA_TYPE_VIDEO &&
+ found_codec->GetResiliencyType() == Codec::ResiliencyType::kRtx) {
+ // For RTX we might need to adjust the apt parameter if we got a
+ // remote offer without RTX for a codec for which we support RTX.
+ auto referenced_codec =
+ GetAssociatedCodecForRtx(supported_codecs, codec);
+ RTC_DCHECK(referenced_codec);
+
+ // Find the codec we should be referencing and point to it.
+ absl::optional<Codec> changed_referenced_codec = FindMatchingCodec(
+ supported_codecs, filtered_codecs, *referenced_codec);
+ if (changed_referenced_codec) {
+ found_codec->SetParam(kCodecParamAssociatedPayloadType,
+ changed_referenced_codec->id);
+ }
+ }
+ filtered_codecs.push_back(*found_codec);
+ }
+ }
+ }
+
+ if (media_description_options.type == MEDIA_TYPE_AUDIO &&
+ !session_options.vad_enabled) {
+ // If application doesn't want CN codecs in offer.
+ StripCNCodecs(&filtered_codecs);
+ } else if (media_description_options.type == MEDIA_TYPE_VIDEO &&
+ session_options.raw_packetization_for_video) {
+ for (Codec& codec : filtered_codecs) {
+ if (codec.IsMediaCodec()) {
+ codec.packetization = kPacketizationParamRaw;
+ }
+ }
+ }
+ return filtered_codecs;
+}
+
+webrtc::RTCErrorOr<AudioCodecs> GetNegotiatedCodecsForAnswer(
+ const MediaDescriptionOptions& media_description_options,
+ const MediaSessionOptions& session_options,
+ const ContentInfo* current_content,
+ const std::vector<Codec>& codecs,
+ const std::vector<Codec>& supported_codecs) {
+ std::vector<Codec> filtered_codecs;
+
+ if (!media_description_options.codec_preferences.empty()) {
+ filtered_codecs = MatchCodecPreference(
+ media_description_options.codec_preferences, codecs, supported_codecs);
+ } else {
+ // Add the codecs from current content if it exists and is not rejected nor
+ // recycled.
+ if (current_content && !current_content->rejected &&
+ current_content->name == media_description_options.mid) {
+ if (!IsMediaContentOfType(current_content,
+ media_description_options.type)) {
+ // Can happen if the remote side re-uses a MID while recycling.
+ LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR,
+ "Media type for content with mid='" +
+ current_content->name +
+ "' does not match previous type.");
+ }
+ const MediaContentDescription* mcd = current_content->media_description();
+ for (const Codec& codec : mcd->codecs()) {
+ if (FindMatchingCodec(mcd->codecs(), codecs, codec)) {
+ filtered_codecs.push_back(codec);
+ }
+ }
+ }
+ // Add other supported video codecs.
+ std::vector<Codec> other_codecs;
+ for (const Codec& codec : supported_codecs) {
+ if (FindMatchingCodec(supported_codecs, codecs, codec) &&
+ !FindMatchingCodec(supported_codecs, filtered_codecs, codec)) {
+ // We should use the local codec with local parameters and the codec id
+ // would be correctly mapped in `NegotiateCodecs`.
+ other_codecs.push_back(codec);
+ }
+ }
+
+ // Use ComputeCodecsUnion to avoid having duplicate payload IDs.
+ // This is a no-op for audio until RTX is added.
+ filtered_codecs = ComputeCodecsUnion(filtered_codecs, other_codecs);
+ }
+
+ if (media_description_options.type == MEDIA_TYPE_AUDIO &&
+ !session_options.vad_enabled) {
+ // If application doesn't want CN codecs in offer.
+ StripCNCodecs(&filtered_codecs);
+ } else if (media_description_options.type == MEDIA_TYPE_VIDEO &&
+ session_options.raw_packetization_for_video) {
+ for (Codec& codec : filtered_codecs) {
+ if (codec.IsMediaCodec()) {
+ codec.packetization = kPacketizationParamRaw;
+ }
+ }
+ }
+ return filtered_codecs;
+}
+
} // namespace
void MediaDescriptionOptions::AddAudioSender(
@@ -1683,15 +1796,16 @@ MediaSessionDescriptionFactory::CreateOfferOrError(
RTCError error;
switch (media_description_options.type) {
case MEDIA_TYPE_AUDIO:
- error = AddAudioContentForOffer(
- media_description_options, session_options, current_content,
- current_description, extensions_with_ids.audio, offer_audio_codecs,
- &current_streams, offer.get(), &ice_credentials);
- break;
case MEDIA_TYPE_VIDEO:
- error = AddVideoContentForOffer(
+ error = AddRtpContentForOffer(
media_description_options, session_options, current_content,
- current_description, extensions_with_ids.video, offer_video_codecs,
+ current_description,
+ media_description_options.type == MEDIA_TYPE_AUDIO
+ ? extensions_with_ids.audio
+ : extensions_with_ids.video,
+ media_description_options.type == MEDIA_TYPE_AUDIO
+ ? offer_audio_codecs
+ : offer_video_codecs,
&current_streams, offer.get(), &ice_credentials);
break;
case MEDIA_TYPE_DATA:
@@ -1852,18 +1966,15 @@ MediaSessionDescriptionFactory::CreateAnswerOrError(
RTCError error;
switch (media_description_options.type) {
case MEDIA_TYPE_AUDIO:
- error = AddAudioContentForAnswer(
- media_description_options, session_options, offer_content, offer,
- current_content, current_description, bundle_transport,
- answer_audio_codecs, header_extensions, &current_streams,
- answer.get(), &ice_credentials);
- break;
case MEDIA_TYPE_VIDEO:
- error = AddVideoContentForAnswer(
+ error = AddRtpContentForAnswer(
media_description_options, session_options, offer_content, offer,
current_content, current_description, bundle_transport,
- answer_video_codecs, header_extensions, &current_streams,
- answer.get(), &ice_credentials);
+ media_description_options.type == MEDIA_TYPE_AUDIO
+ ? answer_audio_codecs
+ : answer_video_codecs,
+ header_extensions, &current_streams, answer.get(),
+ &ice_credentials);
break;
case MEDIA_TYPE_DATA:
error = AddDataContentForAnswer(
@@ -2035,15 +2146,14 @@ void MergeCodecsFromDescription(
const std::vector<const ContentInfo*>& current_active_contents,
AudioCodecs* audio_codecs,
VideoCodecs* video_codecs,
- UsedPayloadTypes* used_pltypes,
- const webrtc::FieldTrialsView* field_trials) {
+ UsedPayloadTypes* used_pltypes) {
for (const ContentInfo* content : current_active_contents) {
if (IsMediaContentOfType(content, MEDIA_TYPE_AUDIO)) {
MergeCodecs(content->media_description()->codecs(), audio_codecs,
- used_pltypes, field_trials);
+ used_pltypes);
} else if (IsMediaContentOfType(content, MEDIA_TYPE_VIDEO)) {
MergeCodecs(content->media_description()->codecs(), video_codecs,
- used_pltypes, field_trials);
+ used_pltypes);
}
}
}
@@ -2058,18 +2168,16 @@ void MediaSessionDescriptionFactory::GetCodecsForOffer(
const std::vector<const ContentInfo*>& current_active_contents,
AudioCodecs* audio_codecs,
VideoCodecs* video_codecs) const {
- const webrtc::FieldTrialsView* field_trials =
- &transport_desc_factory_->trials();
// First - get all codecs from the current description if the media type
// is used. Add them to `used_pltypes` so the payload type is not reused if a
// new media type is added.
UsedPayloadTypes used_pltypes;
MergeCodecsFromDescription(current_active_contents, audio_codecs,
- video_codecs, &used_pltypes, field_trials);
+ video_codecs, &used_pltypes);
// Add our codecs that are not in the current description.
- MergeCodecs(all_audio_codecs_, audio_codecs, &used_pltypes, field_trials);
- MergeCodecs(all_video_codecs_, video_codecs, &used_pltypes, field_trials);
+ MergeCodecs(all_audio_codecs_, audio_codecs, &used_pltypes);
+ MergeCodecs(all_video_codecs_, video_codecs, &used_pltypes);
}
// Getting codecs for an answer involves these steps:
@@ -2084,14 +2192,12 @@ void MediaSessionDescriptionFactory::GetCodecsForAnswer(
const SessionDescription& remote_offer,
AudioCodecs* audio_codecs,
VideoCodecs* video_codecs) const {
- const webrtc::FieldTrialsView* field_trials =
- &transport_desc_factory_->trials();
// First - get all codecs from the current description if the media type
// is used. Add them to `used_pltypes` so the payload type is not reused if a
// new media type is added.
UsedPayloadTypes used_pltypes;
MergeCodecsFromDescription(current_active_contents, audio_codecs,
- video_codecs, &used_pltypes, field_trials);
+ video_codecs, &used_pltypes);
// Second - filter out codecs that we don't support at all and should ignore.
AudioCodecs filtered_offered_audio_codecs;
@@ -2101,9 +2207,9 @@ void MediaSessionDescriptionFactory::GetCodecsForAnswer(
std::vector<Codec> offered_codecs = content.media_description()->codecs();
for (const Codec& offered_audio_codec : offered_codecs) {
if (!FindMatchingCodec(offered_codecs, filtered_offered_audio_codecs,
- offered_audio_codec, field_trials) &&
+ offered_audio_codec) &&
FindMatchingCodec(offered_codecs, all_audio_codecs_,
- offered_audio_codec, field_trials)) {
+ offered_audio_codec)) {
filtered_offered_audio_codecs.push_back(offered_audio_codec);
}
}
@@ -2111,9 +2217,9 @@ void MediaSessionDescriptionFactory::GetCodecsForAnswer(
std::vector<Codec> offered_codecs = content.media_description()->codecs();
for (const Codec& offered_video_codec : offered_codecs) {
if (!FindMatchingCodec(offered_codecs, filtered_offered_video_codecs,
- offered_video_codec, field_trials) &&
+ offered_video_codec) &&
FindMatchingCodec(offered_codecs, all_video_codecs_,
- offered_video_codec, field_trials)) {
+ offered_video_codec)) {
filtered_offered_video_codecs.push_back(offered_video_codec);
}
}
@@ -2122,10 +2228,8 @@ void MediaSessionDescriptionFactory::GetCodecsForAnswer(
// Add codecs that are not in the current description but were in
// `remote_offer`.
- MergeCodecs(filtered_offered_audio_codecs, audio_codecs, &used_pltypes,
- field_trials);
- MergeCodecs(filtered_offered_video_codecs, video_codecs, &used_pltypes,
- field_trials);
+ MergeCodecs(filtered_offered_audio_codecs, audio_codecs, &used_pltypes);
+ MergeCodecs(filtered_offered_video_codecs, video_codecs, &used_pltypes);
}
MediaSessionDescriptionFactory::AudioVideoRtpHeaderExtensions
@@ -2237,228 +2341,85 @@ RTCError MediaSessionDescriptionFactory::AddTransportAnswer(
return RTCError::OK();
}
-// `audio_codecs` = set of all possible codecs that can be used, with correct
+// `codecs` = set of all possible codecs that can be used, with correct
// payload type mappings
//
-// `supported_audio_codecs` = set of codecs that are supported for the direction
+// `supported_codecs` = set of codecs that are supported for the direction
// of this m= section
+// `current_content` = current description, may be null.
+// current_content->codecs() = set of previously negotiated codecs for this m=
+// section
//
-// mcd->codecs() = set of previously negotiated codecs for this m= section
-//
-// The payload types should come from audio_codecs, but the order should come
-// from mcd->codecs() and then supported_codecs, to ensure that re-offers don't
-// change existing codec priority, and that new codecs are added with the right
-// priority.
-RTCError MediaSessionDescriptionFactory::AddAudioContentForOffer(
+// The payload types should come from codecs, but the order should come
+// from current_content->codecs() and then supported_codecs, to ensure that
+// re-offers don't change existing codec priority, and that new codecs are added
+// with the right priority.
+RTCError MediaSessionDescriptionFactory::AddRtpContentForOffer(
const MediaDescriptionOptions& media_description_options,
const MediaSessionOptions& session_options,
const ContentInfo* current_content,
const SessionDescription* current_description,
- const RtpHeaderExtensions& audio_rtp_extensions,
- const AudioCodecs& audio_codecs,
+ const RtpHeaderExtensions& header_extensions,
+ const std::vector<Codec>& codecs,
StreamParamsVec* current_streams,
- SessionDescription* desc,
+ SessionDescription* session_description,
IceCredentialsIterator* ice_credentials) const {
- const webrtc::FieldTrialsView* field_trials =
- &transport_desc_factory_->trials();
- // Filter audio_codecs (which includes all codecs, with correctly remapped
- // payload types) based on transceiver direction.
- const AudioCodecs& supported_audio_codecs =
- GetAudioCodecsForOffer(media_description_options.direction);
-
- AudioCodecs filtered_codecs;
+ RTC_DCHECK(media_description_options.type == MEDIA_TYPE_AUDIO ||
+ media_description_options.type == MEDIA_TYPE_VIDEO);
- if (!media_description_options.codec_preferences.empty()) {
- // Add the codecs from the current transceiver's codec preferences.
- // They override any existing codecs from previous negotiations.
- filtered_codecs = MatchCodecPreference(
- media_description_options.codec_preferences, audio_codecs,
- supported_audio_codecs, field_trials);
- } else {
- // Add the codecs from current content if it exists and is not rejected nor
- // recycled.
- if (current_content && !current_content->rejected &&
- current_content->name == media_description_options.mid) {
- if (!IsMediaContentOfType(current_content, MEDIA_TYPE_AUDIO)) {
- // Can happen if the remote side re-uses a MID while recycling.
- LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR,
- "Media type for content with mid='" +
- current_content->name +
- "' does not match previous type.");
- }
- const MediaContentDescription* mcd = current_content->media_description();
- for (const Codec& codec : mcd->codecs()) {
- if (FindMatchingCodec(mcd->codecs(), audio_codecs, codec,
- field_trials)) {
- filtered_codecs.push_back(codec);
- }
- }
- }
- // Add other supported audio codecs.
- for (const Codec& codec : supported_audio_codecs) {
- absl::optional<Codec> found_codec = FindMatchingCodec(
- supported_audio_codecs, audio_codecs, codec, field_trials);
- if (found_codec &&
- !FindMatchingCodec(supported_audio_codecs, filtered_codecs, codec,
- field_trials)) {
- // Use the `found_codec` from `audio_codecs` because it has the
- // correctly mapped payload type.
- filtered_codecs.push_back(*found_codec);
- }
- }
- }
- if (!session_options.vad_enabled) {
- // If application doesn't want CN codecs in offer.
- StripCNCodecs(&filtered_codecs);
+ const std::vector<Codec>& supported_codecs =
+ media_description_options.type == MEDIA_TYPE_AUDIO
+ ? GetAudioCodecsForOffer(media_description_options.direction)
+ : GetVideoCodecsForOffer(media_description_options.direction);
+ webrtc::RTCErrorOr<std::vector<Codec>> error_or_filtered_codecs =
+ GetNegotiatedCodecsForOffer(media_description_options, session_options,
+ current_content, codecs, supported_codecs);
+ if (!error_or_filtered_codecs.ok()) {
+ return error_or_filtered_codecs.MoveError();
}
cricket::SecurePolicy sdes_policy =
IsDtlsActive(current_content, current_description) ? cricket::SEC_DISABLED
: secure();
- auto audio = std::make_unique<AudioContentDescription>();
std::vector<std::string> crypto_suites;
- GetSupportedAudioSdesCryptoSuiteNames(session_options.crypto_options,
- &crypto_suites);
- auto error = CreateMediaContentOffer(
- media_description_options, session_options, filtered_codecs, sdes_policy,
- GetCryptos(current_content), crypto_suites, audio_rtp_extensions,
- ssrc_generator(), current_streams, audio.get(),
- transport_desc_factory_->trials());
- if (!error.ok()) {
- return error;
- }
-
- bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
- SetMediaProtocol(secure_transport, audio.get());
-
- audio->set_direction(media_description_options.direction);
-
- desc->AddContent(media_description_options.mid, MediaProtocolType::kRtp,
- media_description_options.stopped, std::move(audio));
- error = AddTransportOffer(media_description_options.mid,
- media_description_options.transport_options,
- current_description, desc, ice_credentials);
- if (!error.ok()) {
- return error;
- }
-
- return RTCError::OK();
-}
-
-// TODO(kron): This function is very similar to AddAudioContentForOffer.
-// Refactor to reuse shared code.
-RTCError MediaSessionDescriptionFactory::AddVideoContentForOffer(
- const MediaDescriptionOptions& media_description_options,
- const MediaSessionOptions& session_options,
- const ContentInfo* current_content,
- const SessionDescription* current_description,
- const RtpHeaderExtensions& video_rtp_extensions,
- const VideoCodecs& video_codecs,
- StreamParamsVec* current_streams,
- SessionDescription* desc,
- IceCredentialsIterator* ice_credentials) const {
- const webrtc::FieldTrialsView* field_trials =
- &transport_desc_factory_->trials();
- // Filter video_codecs (which includes all codecs, with correctly remapped
- // payload types) based on transceiver direction.
- const VideoCodecs& supported_video_codecs =
- GetVideoCodecsForOffer(media_description_options.direction);
-
- VideoCodecs filtered_codecs;
-
- if (!media_description_options.codec_preferences.empty()) {
- // Add the codecs from the current transceiver's codec preferences.
- // They override any existing codecs from previous negotiations.
- filtered_codecs = MatchCodecPreference(
- media_description_options.codec_preferences, video_codecs,
- supported_video_codecs, field_trials);
+ if (media_description_options.type == MEDIA_TYPE_AUDIO) {
+ GetSupportedAudioSdesCryptoSuiteNames(session_options.crypto_options,
+ &crypto_suites);
} else {
- // Add the codecs from current content if it exists and is not rejected nor
- // recycled.
- if (current_content && !current_content->rejected &&
- current_content->name == media_description_options.mid) {
- if (!IsMediaContentOfType(current_content, MEDIA_TYPE_VIDEO)) {
- // Can happen if the remote side re-uses a MID while recycling.
- LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR,
- "Media type for content with mid='" +
- current_content->name +
- "' does not match previous type.");
- }
- const MediaContentDescription* mcd = current_content->media_description();
- for (const Codec& codec : mcd->codecs()) {
- if (FindMatchingCodec(mcd->codecs(), video_codecs, codec,
- field_trials)) {
- filtered_codecs.push_back(codec);
- }
- }
- }
- // Add other supported video codecs.
- for (const Codec& codec : supported_video_codecs) {
- absl::optional<Codec> found_codec = FindMatchingCodec(
- supported_video_codecs, video_codecs, codec, field_trials);
- if (found_codec &&
- !FindMatchingCodec(supported_video_codecs, filtered_codecs, codec,
- field_trials)) {
- // Use the `found_codec` from `video_codecs` because it has the
- // correctly mapped payload type.
- if (IsRtxCodec(codec)) {
- // For RTX we might need to adjust the apt parameter if we got a
- // remote offer without RTX for a codec for which we support RTX.
- auto referenced_codec =
- GetAssociatedCodecForRtx(supported_video_codecs, codec);
- RTC_DCHECK(referenced_codec);
-
- // Find the codec we should be referencing and point to it.
- absl::optional<Codec> changed_referenced_codec =
- FindMatchingCodec(supported_video_codecs, filtered_codecs,
- *referenced_codec, field_trials);
- if (changed_referenced_codec) {
- found_codec->SetParam(kCodecParamAssociatedPayloadType,
- changed_referenced_codec->id);
- }
- }
- filtered_codecs.push_back(*found_codec);
- }
- }
+ GetSupportedVideoSdesCryptoSuiteNames(session_options.crypto_options,
+ &crypto_suites);
}
- if (session_options.raw_packetization_for_video) {
- for (Codec& codec : filtered_codecs) {
- if (codec.IsMediaCodec()) {
- codec.packetization = kPacketizationParamRaw;
- }
- }
+ std::unique_ptr<MediaContentDescription> content_description;
+ if (media_description_options.type == MEDIA_TYPE_AUDIO) {
+ content_description = std::make_unique<AudioContentDescription>();
+ } else {
+ content_description = std::make_unique<VideoContentDescription>();
}
- cricket::SecurePolicy sdes_policy =
- IsDtlsActive(current_content, current_description) ? cricket::SEC_DISABLED
- : secure();
- auto video = std::make_unique<VideoContentDescription>();
- std::vector<std::string> crypto_suites;
- GetSupportedVideoSdesCryptoSuiteNames(session_options.crypto_options,
- &crypto_suites);
auto error = CreateMediaContentOffer(
- media_description_options, session_options, filtered_codecs, sdes_policy,
- GetCryptos(current_content), crypto_suites, video_rtp_extensions,
- ssrc_generator(), current_streams, video.get(),
+ media_description_options, session_options,
+ error_or_filtered_codecs.MoveValue(), sdes_policy,
+ GetCryptos(current_content), crypto_suites, header_extensions,
+ ssrc_generator(), current_streams, content_description.get(),
transport_desc_factory_->trials());
if (!error.ok()) {
return error;
}
- video->set_bandwidth(kAutoBandwidth);
-
- bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
- SetMediaProtocol(secure_transport, video.get());
+ bool secure_transport = transport_desc_factory_->secure() != SEC_DISABLED;
+ SetMediaProtocol(secure_transport, content_description.get());
- video->set_direction(media_description_options.direction);
+ content_description->set_direction(media_description_options.direction);
- desc->AddContent(media_description_options.mid, MediaProtocolType::kRtp,
- media_description_options.stopped, std::move(video));
+ session_description->AddContent(
+ media_description_options.mid, MediaProtocolType::kRtp,
+ media_description_options.stopped, std::move(content_description));
return AddTransportOffer(media_description_options.mid,
media_description_options.transport_options,
- current_description, desc, ice_credentials);
+ current_description, session_description,
+ ice_credentials);
}
RTCError MediaSessionDescriptionFactory::AddDataContentForOffer(
@@ -2526,19 +2487,19 @@ RTCError MediaSessionDescriptionFactory::AddUnsupportedContentForOffer(
current_description, desc, ice_credentials);
}
-// `audio_codecs` = set of all possible codecs that can be used, with correct
+// `codecs` = set of all possible codecs that can be used, with correct
// payload type mappings
//
-// `supported_audio_codecs` = set of codecs that are supported for the direction
+// `supported_codecs` = set of codecs that are supported for the direction
// of this m= section
//
// mcd->codecs() = set of previously negotiated codecs for this m= section
//
-// The payload types should come from audio_codecs, but the order should come
+// The payload types should come from codecs, but the order should come
// from mcd->codecs() and then supported_codecs, to ensure that re-offers don't
// change existing codec priority, and that new codecs are added with the right
// priority.
-RTCError MediaSessionDescriptionFactory::AddAudioContentForAnswer(
+RTCError MediaSessionDescriptionFactory::AddRtpContentForAnswer(
const MediaDescriptionOptions& media_description_options,
const MediaSessionOptions& session_options,
const ContentInfo* offer_content,
@@ -2546,272 +2507,104 @@ RTCError MediaSessionDescriptionFactory::AddAudioContentForAnswer(
const ContentInfo* current_content,
const SessionDescription* current_description,
const TransportInfo* bundle_transport,
- const AudioCodecs& audio_codecs,
- const RtpHeaderExtensions& rtp_header_extensions,
+ const std::vector<Codec>& codecs,
+ const RtpHeaderExtensions& header_extensions,
StreamParamsVec* current_streams,
SessionDescription* answer,
IceCredentialsIterator* ice_credentials) const {
- const webrtc::FieldTrialsView* field_trials =
- &transport_desc_factory_->trials();
- RTC_CHECK(IsMediaContentOfType(offer_content, MEDIA_TYPE_AUDIO));
- const AudioContentDescription* offer_audio_description =
- offer_content->media_description()->as_audio();
-
- std::unique_ptr<TransportDescription> audio_transport = CreateTransportAnswer(
+ RTC_DCHECK(media_description_options.type == MEDIA_TYPE_AUDIO ||
+ media_description_options.type == MEDIA_TYPE_VIDEO);
+ RTC_CHECK(
+ IsMediaContentOfType(offer_content, media_description_options.type));
+ const RtpMediaContentDescription* offer_content_description;
+ if (media_description_options.type == MEDIA_TYPE_AUDIO) {
+ offer_content_description = offer_content->media_description()->as_audio();
+ } else {
+ offer_content_description = offer_content->media_description()->as_video();
+ }
+ std::unique_ptr<TransportDescription> transport = CreateTransportAnswer(
media_description_options.mid, offer_description,
media_description_options.transport_options, current_description,
bundle_transport != nullptr, ice_credentials);
- if (!audio_transport) {
+ if (!transport) {
LOG_AND_RETURN_ERROR(
RTCErrorType::INTERNAL_ERROR,
- "Failed to create transport answer, audio transport is missing");
+ "Failed to create transport answer, transport is missing");
}
// Pick codecs based on the requested communications direction in the offer
// and the selected direction in the answer.
// Note these will be filtered one final time in CreateMediaContentAnswer.
auto wants_rtd = media_description_options.direction;
- auto offer_rtd = offer_audio_description->direction();
+ auto offer_rtd = offer_content_description->direction();
auto answer_rtd = NegotiateRtpTransceiverDirection(offer_rtd, wants_rtd);
- AudioCodecs supported_audio_codecs =
- GetAudioCodecsForAnswer(offer_rtd, answer_rtd);
- AudioCodecs filtered_codecs;
-
- if (!media_description_options.codec_preferences.empty()) {
- filtered_codecs = MatchCodecPreference(
- media_description_options.codec_preferences, audio_codecs,
- supported_audio_codecs, field_trials);
- } else {
- // Add the codecs from current content if it exists and is not rejected nor
- // recycled.
- if (current_content && !current_content->rejected &&
- current_content->name == media_description_options.mid) {
- if (!IsMediaContentOfType(current_content, MEDIA_TYPE_AUDIO)) {
- // Can happen if the remote side re-uses a MID while recycling.
- LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR,
- "Media type for content with mid='" +
- current_content->name +
- "' does not match previous type.");
- }
- const MediaContentDescription* mcd = current_content->media_description();
- for (const Codec& codec : mcd->codecs()) {
- if (FindMatchingCodec(mcd->codecs(), audio_codecs, codec,
- field_trials)) {
- filtered_codecs.push_back(codec);
- }
- }
- }
- // Add other supported audio codecs.
- for (const Codec& codec : supported_audio_codecs) {
- if (FindMatchingCodec(supported_audio_codecs, audio_codecs, codec,
- field_trials) &&
- !FindMatchingCodec(supported_audio_codecs, filtered_codecs, codec,
- field_trials)) {
- // We should use the local codec with local parameters and the codec id
- // would be correctly mapped in `NegotiateCodecs`.
- filtered_codecs.push_back(codec);
- }
- }
- }
- if (!session_options.vad_enabled) {
- // If application doesn't want CN codecs in answer.
- StripCNCodecs(&filtered_codecs);
+ const std::vector<Codec>& supported_codecs =
+ media_description_options.type == MEDIA_TYPE_AUDIO
+ ? GetAudioCodecsForAnswer(offer_rtd, answer_rtd)
+ : GetVideoCodecsForAnswer(offer_rtd, answer_rtd);
+ webrtc::RTCErrorOr<std::vector<Codec>> error_or_filtered_codecs =
+ GetNegotiatedCodecsForAnswer(media_description_options, session_options,
+ current_content, codecs, supported_codecs);
+ if (!error_or_filtered_codecs.ok()) {
+ return error_or_filtered_codecs.MoveError();
}
+ auto filtered_codecs = error_or_filtered_codecs.MoveValue();
// Determine if we have media codecs in common.
bool has_common_media_codecs =
std::find_if(filtered_codecs.begin(), filtered_codecs.end(),
- [](const AudioCodec& c) {
- return !(IsRedCodec(c) || IsComfortNoiseCodec(c));
+ [](const Codec& c) {
+ return c.IsMediaCodec() && !IsComfortNoiseCodec(c);
}) != filtered_codecs.end();
bool bundle_enabled = offer_description->HasGroup(GROUP_TYPE_BUNDLE) &&
session_options.bundle_enabled;
- auto audio_answer = std::make_unique<AudioContentDescription>();
+ std::unique_ptr<MediaContentDescription> answer_content;
+ if (media_description_options.type == MEDIA_TYPE_AUDIO) {
+ answer_content = std::make_unique<AudioContentDescription>();
+ } else {
+ answer_content = std::make_unique<VideoContentDescription>();
+ }
// Do not require or create SDES cryptos if DTLS is used.
cricket::SecurePolicy sdes_policy =
- audio_transport->secure() ? cricket::SEC_DISABLED : secure();
- if (!SetCodecsInAnswer(offer_audio_description, filtered_codecs,
- media_description_options, session_options,
- ssrc_generator(), current_streams, audio_answer.get(),
- transport_desc_factory_->trials())) {
+ transport->secure() ? cricket::SEC_DISABLED : secure();
+ if (!SetCodecsInAnswer(
+ offer_content_description, filtered_codecs, media_description_options,
+ session_options, ssrc_generator(), current_streams,
+ answer_content.get(), transport_desc_factory_->trials())) {
LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR,
"Failed to set codecs in answer");
}
if (!CreateMediaContentAnswer(
- offer_audio_description, media_description_options, session_options,
+ offer_content_description, media_description_options, session_options,
sdes_policy, GetCryptos(current_content),
- filtered_rtp_header_extensions(rtp_header_extensions),
- ssrc_generator(), enable_encrypted_rtp_header_extensions_,
- current_streams, bundle_enabled, audio_answer.get())) {
+ filtered_rtp_header_extensions(header_extensions), ssrc_generator(),
+ enable_encrypted_rtp_header_extensions_, current_streams,
+ bundle_enabled, answer_content.get())) {
LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR,
"Failed to create answer");
}
bool secure = bundle_transport ? bundle_transport->description.secure()
- : audio_transport->secure();
+ : transport->secure();
bool rejected = media_description_options.stopped ||
offer_content->rejected || !has_common_media_codecs ||
!IsMediaProtocolSupported(MEDIA_TYPE_AUDIO,
- audio_answer->protocol(), secure);
- auto error = AddTransportAnswer(media_description_options.mid,
- *(audio_transport.get()), answer);
- if (!error.ok()) {
- return error;
- }
-
+ answer_content->protocol(), secure);
if (rejected) {
- RTC_LOG(LS_INFO) << "Audio m= section '" << media_description_options.mid
+ RTC_LOG(LS_INFO) << "m= section '" << media_description_options.mid
<< "' being rejected in answer.";
}
- answer->AddContent(media_description_options.mid, offer_content->type,
- rejected, std::move(audio_answer));
- return RTCError::OK();
-}
-
-// TODO(kron): This function is very similar to AddAudioContentForAnswer.
-// Refactor to reuse shared code.
-RTCError MediaSessionDescriptionFactory::AddVideoContentForAnswer(
- const MediaDescriptionOptions& media_description_options,
- const MediaSessionOptions& session_options,
- const ContentInfo* offer_content,
- const SessionDescription* offer_description,
- const ContentInfo* current_content,
- const SessionDescription* current_description,
- const TransportInfo* bundle_transport,
- const VideoCodecs& video_codecs,
- const RtpHeaderExtensions& default_video_rtp_header_extensions,
- StreamParamsVec* current_streams,
- SessionDescription* answer,
- IceCredentialsIterator* ice_credentials) const {
- const webrtc::FieldTrialsView* field_trials =
- &transport_desc_factory_->trials();
- RTC_CHECK(IsMediaContentOfType(offer_content, MEDIA_TYPE_VIDEO));
- const VideoContentDescription* offer_video_description =
- offer_content->media_description()->as_video();
-
- std::unique_ptr<TransportDescription> video_transport = CreateTransportAnswer(
- media_description_options.mid, offer_description,
- media_description_options.transport_options, current_description,
- bundle_transport != nullptr, ice_credentials);
- if (!video_transport) {
- LOG_AND_RETURN_ERROR(
- RTCErrorType::INTERNAL_ERROR,
- "Failed to create transport answer, video transport is missing");
- }
-
- // Pick codecs based on the requested communications direction in the offer
- // and the selected direction in the answer.
- // Note these will be filtered one final time in CreateMediaContentAnswer.
- auto wants_rtd = media_description_options.direction;
- auto offer_rtd = offer_video_description->direction();
- auto answer_rtd = NegotiateRtpTransceiverDirection(offer_rtd, wants_rtd);
- VideoCodecs supported_video_codecs =
- GetVideoCodecsForAnswer(offer_rtd, answer_rtd);
-
- VideoCodecs filtered_codecs;
-
- if (!media_description_options.codec_preferences.empty()) {
- filtered_codecs = MatchCodecPreference(
- media_description_options.codec_preferences, video_codecs,
- supported_video_codecs, field_trials);
- } else {
- // Add the codecs from current content if it exists and is not rejected nor
- // recycled.
- if (current_content && !current_content->rejected &&
- current_content->name == media_description_options.mid) {
- if (!IsMediaContentOfType(current_content, MEDIA_TYPE_VIDEO)) {
- // Can happen if the remote side re-uses a MID while recycling.
- LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR,
- "Media type for content with mid='" +
- current_content->name +
- "' does not match previous type.");
- }
- const MediaContentDescription* mcd = current_content->media_description();
- for (const Codec& codec : mcd->codecs()) {
- if (FindMatchingCodec(mcd->codecs(), video_codecs, codec,
- field_trials)) {
- filtered_codecs.push_back(codec);
- }
- }
- }
-
- // Add other supported video codecs.
- VideoCodecs other_video_codecs;
- for (const Codec& codec : supported_video_codecs) {
- if (FindMatchingCodec(supported_video_codecs, video_codecs, codec,
- field_trials) &&
- !FindMatchingCodec(supported_video_codecs, filtered_codecs, codec,
- field_trials)) {
- // We should use the local codec with local parameters and the codec id
- // would be correctly mapped in `NegotiateCodecs`.
- other_video_codecs.push_back(codec);
- }
- }
-
- // Use ComputeCodecsUnion to avoid having duplicate payload IDs
- filtered_codecs =
- ComputeCodecsUnion(filtered_codecs, other_video_codecs, field_trials);
- }
- // Determine if we have media codecs in common.
- bool has_common_media_codecs =
- std::find_if(
- filtered_codecs.begin(), filtered_codecs.end(), [](const Codec& c) {
- return !(IsRedCodec(c) || IsUlpfecCodec(c) || IsFlexfecCodec(c));
- }) != filtered_codecs.end();
-
- if (session_options.raw_packetization_for_video) {
- for (Codec& codec : filtered_codecs) {
- if (codec.IsMediaCodec()) {
- codec.packetization = kPacketizationParamRaw;
- }
- }
- }
-
- bool bundle_enabled = offer_description->HasGroup(GROUP_TYPE_BUNDLE) &&
- session_options.bundle_enabled;
- auto video_answer = std::make_unique<VideoContentDescription>();
- // Do not require or create SDES cryptos if DTLS is used.
- cricket::SecurePolicy sdes_policy =
- video_transport->secure() ? cricket::SEC_DISABLED : secure();
- if (!SetCodecsInAnswer(offer_video_description, filtered_codecs,
- media_description_options, session_options,
- ssrc_generator(), current_streams, video_answer.get(),
- transport_desc_factory_->trials())) {
- LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR,
- "Failed to set codecs in answer");
- }
- if (!CreateMediaContentAnswer(
- offer_video_description, media_description_options, session_options,
- sdes_policy, GetCryptos(current_content),
- filtered_rtp_header_extensions(default_video_rtp_header_extensions),
- ssrc_generator(), enable_encrypted_rtp_header_extensions_,
- current_streams, bundle_enabled, video_answer.get())) {
- LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR,
- "Failed to create answer");
- }
- bool secure = bundle_transport ? bundle_transport->description.secure()
- : video_transport->secure();
- bool rejected = media_description_options.stopped ||
- offer_content->rejected || !has_common_media_codecs ||
- !IsMediaProtocolSupported(MEDIA_TYPE_VIDEO,
- video_answer->protocol(), secure);
auto error = AddTransportAnswer(media_description_options.mid,
- *(video_transport.get()), answer);
+ *(transport.get()), answer);
if (!error.ok()) {
return error;
}
- if (!rejected) {
- video_answer->set_bandwidth(kAutoBandwidth);
- } else {
- RTC_LOG(LS_INFO) << "Video m= section '" << media_description_options.mid
- << "' being rejected in answer.";
- }
answer->AddContent(media_description_options.mid, offer_content->type,
- rejected, std::move(video_answer));
+ rejected, std::move(answer_content));
return RTCError::OK();
}
@@ -2934,23 +2727,19 @@ RTCError MediaSessionDescriptionFactory::AddUnsupportedContentForAnswer(
}
void MediaSessionDescriptionFactory::ComputeAudioCodecsIntersectionAndUnion() {
- const webrtc::FieldTrialsView* field_trials =
- &transport_desc_factory_->trials();
audio_sendrecv_codecs_.clear();
all_audio_codecs_.clear();
// Compute the audio codecs union.
for (const Codec& send : audio_send_codecs_) {
all_audio_codecs_.push_back(send);
- if (!FindMatchingCodec(audio_send_codecs_, audio_recv_codecs_, send,
- field_trials)) {
+ if (!FindMatchingCodec(audio_send_codecs_, audio_recv_codecs_, send)) {
// It doesn't make sense to have an RTX codec we support sending but not
// receiving.
- RTC_DCHECK(!IsRtxCodec(send));
+ RTC_DCHECK(send.GetResiliencyType() != Codec::ResiliencyType::kRtx);
}
}
for (const Codec& recv : audio_recv_codecs_) {
- if (!FindMatchingCodec(audio_recv_codecs_, audio_send_codecs_, recv,
- field_trials)) {
+ if (!FindMatchingCodec(audio_recv_codecs_, audio_send_codecs_, recv)) {
all_audio_codecs_.push_back(recv);
}
}
@@ -2960,17 +2749,15 @@ void MediaSessionDescriptionFactory::ComputeAudioCodecsIntersectionAndUnion() {
// expensive than decoding, and prioritizing a codec in the send list probably
// means it's a codec we can handle efficiently.
NegotiateCodecs(audio_recv_codecs_, audio_send_codecs_,
- &audio_sendrecv_codecs_, true, field_trials);
+ &audio_sendrecv_codecs_, true);
}
void MediaSessionDescriptionFactory::ComputeVideoCodecsIntersectionAndUnion() {
- const webrtc::FieldTrialsView* field_trials =
- &transport_desc_factory_->trials();
video_sendrecv_codecs_.clear();
// Use ComputeCodecsUnion to avoid having duplicate payload IDs
all_video_codecs_ =
- ComputeCodecsUnion(video_recv_codecs_, video_send_codecs_, field_trials);
+ ComputeCodecsUnion(video_recv_codecs_, video_send_codecs_);
// Use NegotiateCodecs to merge our codec lists, since the operation is
// essentially the same. Put send_codecs as the offered_codecs, which is the
@@ -2978,7 +2765,7 @@ void MediaSessionDescriptionFactory::ComputeVideoCodecsIntersectionAndUnion() {
// expensive than decoding, and prioritizing a codec in the send list probably
// means it's a codec we can handle efficiently.
NegotiateCodecs(video_recv_codecs_, video_send_codecs_,
- &video_sendrecv_codecs_, true, field_trials);
+ &video_sendrecv_codecs_, true);
}
bool IsMediaContent(const ContentInfo* content) {
diff --git a/third_party/libwebrtc/pc/media_session.h b/third_party/libwebrtc/pc/media_session.h
index 3100fb6fdb..0b3cb67c35 100644
--- a/third_party/libwebrtc/pc/media_session.h
+++ b/third_party/libwebrtc/pc/media_session.h
@@ -19,7 +19,6 @@
#include <vector>
#include "api/crypto/crypto_options.h"
-#include "api/field_trials_view.h"
#include "api/media_types.h"
#include "api/rtp_parameters.h"
#include "api/rtp_transceiver_direction.h"
@@ -230,28 +229,14 @@ class MediaSessionDescriptionFactory {
const TransportDescription& transport_desc,
SessionDescription* answer_desc) const;
- // Helpers for adding media contents to the SessionDescription. Returns true
- // it succeeds or the media content is not needed, or false if there is any
- // error.
-
- webrtc::RTCError AddAudioContentForOffer(
- const MediaDescriptionOptions& media_description_options,
- const MediaSessionOptions& session_options,
- const ContentInfo* current_content,
- const SessionDescription* current_description,
- const RtpHeaderExtensions& audio_rtp_extensions,
- const AudioCodecs& audio_codecs,
- StreamParamsVec* current_streams,
- SessionDescription* desc,
- IceCredentialsIterator* ice_credentials) const;
-
- webrtc::RTCError AddVideoContentForOffer(
+ // Helpers for adding media contents to the SessionDescription.
+ webrtc::RTCError AddRtpContentForOffer(
const MediaDescriptionOptions& media_description_options,
const MediaSessionOptions& session_options,
const ContentInfo* current_content,
const SessionDescription* current_description,
- const RtpHeaderExtensions& video_rtp_extensions,
- const VideoCodecs& video_codecs,
+ const RtpHeaderExtensions& header_extensions,
+ const std::vector<Codec>& codecs,
StreamParamsVec* current_streams,
SessionDescription* desc,
IceCredentialsIterator* ice_credentials) const;
@@ -273,21 +258,7 @@ class MediaSessionDescriptionFactory {
SessionDescription* desc,
IceCredentialsIterator* ice_credentials) const;
- webrtc::RTCError AddAudioContentForAnswer(
- const MediaDescriptionOptions& media_description_options,
- const MediaSessionOptions& session_options,
- const ContentInfo* offer_content,
- const SessionDescription* offer_description,
- const ContentInfo* current_content,
- const SessionDescription* current_description,
- const TransportInfo* bundle_transport,
- const AudioCodecs& audio_codecs,
- const RtpHeaderExtensions& rtp_header_extensions,
- StreamParamsVec* current_streams,
- SessionDescription* answer,
- IceCredentialsIterator* ice_credentials) const;
-
- webrtc::RTCError AddVideoContentForAnswer(
+ webrtc::RTCError AddRtpContentForAnswer(
const MediaDescriptionOptions& media_description_options,
const MediaSessionOptions& session_options,
const ContentInfo* offer_content,
@@ -295,8 +266,8 @@ class MediaSessionDescriptionFactory {
const ContentInfo* current_content,
const SessionDescription* current_description,
const TransportInfo* bundle_transport,
- const VideoCodecs& video_codecs,
- const RtpHeaderExtensions& rtp_header_extensions,
+ const std::vector<Codec>& codecs,
+ const RtpHeaderExtensions& header_extensions,
StreamParamsVec* current_streams,
SessionDescription* answer,
IceCredentialsIterator* ice_credentials) const;
diff --git a/third_party/libwebrtc/pc/media_session_unittest.cc b/third_party/libwebrtc/pc/media_session_unittest.cc
index a1770c18c5..641f638e72 100644
--- a/third_party/libwebrtc/pc/media_session_unittest.cc
+++ b/third_party/libwebrtc/pc/media_session_unittest.cc
@@ -54,50 +54,16 @@
ASSERT_EQ(s, cd->cryptos().size()); \
ASSERT_EQ(cs, cd->cryptos()[0].crypto_suite)
-typedef std::vector<cricket::Candidate> Candidates;
-
-using cricket::AudioCodec;
-using cricket::AudioContentDescription;
-using cricket::ContentInfo;
-using cricket::CryptoParamsVec;
-using cricket::GetFirstAudioContent;
-using cricket::GetFirstAudioContentDescription;
-using cricket::GetFirstDataContent;
-using cricket::GetFirstVideoContent;
-using cricket::GetFirstVideoContentDescription;
-using cricket::kAutoBandwidth;
-using cricket::MEDIA_TYPE_AUDIO;
-using cricket::MEDIA_TYPE_DATA;
-using cricket::MEDIA_TYPE_VIDEO;
-using cricket::MediaContentDescription;
-using cricket::MediaDescriptionOptions;
-using cricket::MediaProtocolType;
-using cricket::MediaSessionDescriptionFactory;
-using cricket::MediaSessionOptions;
-using cricket::MediaType;
-using cricket::RidDescription;
-using cricket::RidDirection;
-using cricket::SctpDataContentDescription;
-using cricket::SEC_DISABLED;
-using cricket::SEC_ENABLED;
-using cricket::SEC_REQUIRED;
-using cricket::SessionDescription;
-using cricket::SimulcastDescription;
-using cricket::SimulcastLayer;
-using cricket::SimulcastLayerList;
-using cricket::SsrcGroup;
-using cricket::StreamParams;
-using cricket::StreamParamsVec;
-using cricket::TransportDescription;
-using cricket::TransportDescriptionFactory;
-using cricket::TransportInfo;
-using cricket::VideoCodec;
-using cricket::VideoContentDescription;
-using rtc::kCsAeadAes128Gcm;
-using rtc::kCsAeadAes256Gcm;
-using rtc::kCsAesCm128HmacSha1_32;
-using rtc::kCsAesCm128HmacSha1_80;
-using rtc::UniqueRandomIdGenerator;
+namespace cricket {
+namespace {
+
+using ::rtc::kCsAeadAes128Gcm;
+using ::rtc::kCsAeadAes256Gcm;
+using ::rtc::kCsAesCm128HmacSha1_32;
+using ::rtc::kCsAesCm128HmacSha1_80;
+using ::rtc::UniqueRandomIdGenerator;
+using ::testing::Bool;
+using ::testing::Combine;
using ::testing::Contains;
using ::testing::Each;
using ::testing::ElementsAre;
@@ -110,146 +76,145 @@ using ::testing::Ne;
using ::testing::Not;
using ::testing::Pointwise;
using ::testing::SizeIs;
-using webrtc::RtpExtension;
-using webrtc::RtpTransceiverDirection;
+using ::testing::Values;
+using ::testing::ValuesIn;
+using ::webrtc::RtpExtension;
+using ::webrtc::RtpTransceiverDirection;
+
+using Candidates = std::vector<Candidate>;
-static AudioCodec createRedAudioCodec(absl::string_view encoding_id) {
- AudioCodec red = cricket::CreateAudioCodec(63, "red", 48000, 2);
- red.SetParam(cricket::kCodecParamNotInNameValueFormat,
+AudioCodec CreateRedAudioCodec(absl::string_view encoding_id) {
+ AudioCodec red = CreateAudioCodec(63, "red", 48000, 2);
+ red.SetParam(kCodecParamNotInNameValueFormat,
std::string(encoding_id) + '/' + std::string(encoding_id));
return red;
}
-static const AudioCodec kAudioCodecs1[] = {
- cricket::CreateAudioCodec(111, "opus", 48000, 2),
- createRedAudioCodec("111"),
- cricket::CreateAudioCodec(102, "iLBC", 8000, 1),
- cricket::CreateAudioCodec(0, "PCMU", 8000, 1),
- cricket::CreateAudioCodec(8, "PCMA", 8000, 1),
- cricket::CreateAudioCodec(117, "red", 8000, 1),
- cricket::CreateAudioCodec(107, "CN", 48000, 1)};
+const AudioCodec kAudioCodecs1[] = {CreateAudioCodec(111, "opus", 48000, 2),
+ CreateRedAudioCodec("111"),
+ CreateAudioCodec(102, "iLBC", 8000, 1),
+ CreateAudioCodec(0, "PCMU", 8000, 1),
+ CreateAudioCodec(8, "PCMA", 8000, 1),
+ CreateAudioCodec(117, "red", 8000, 1),
+ CreateAudioCodec(107, "CN", 48000, 1)};
-static const AudioCodec kAudioCodecs2[] = {
- cricket::CreateAudioCodec(126, "foo", 16000, 1),
- cricket::CreateAudioCodec(0, "PCMU", 8000, 1),
- cricket::CreateAudioCodec(127, "iLBC", 8000, 1),
+const AudioCodec kAudioCodecs2[] = {
+ CreateAudioCodec(126, "foo", 16000, 1),
+ CreateAudioCodec(0, "PCMU", 8000, 1),
+ CreateAudioCodec(127, "iLBC", 8000, 1),
};
-static const AudioCodec kAudioCodecsAnswer[] = {
- cricket::CreateAudioCodec(102, "iLBC", 8000, 1),
- cricket::CreateAudioCodec(0, "PCMU", 8000, 1),
+const AudioCodec kAudioCodecsAnswer[] = {
+ CreateAudioCodec(102, "iLBC", 8000, 1),
+ CreateAudioCodec(0, "PCMU", 8000, 1),
};
-static const VideoCodec kVideoCodecs1[] = {
- cricket::CreateVideoCodec(96, "H264-SVC"),
- cricket::CreateVideoCodec(97, "H264")};
+const VideoCodec kVideoCodecs1[] = {CreateVideoCodec(96, "H264-SVC"),
+ CreateVideoCodec(97, "H264")};
-static const VideoCodec kVideoCodecs1Reverse[] = {
- cricket::CreateVideoCodec(97, "H264"),
- cricket::CreateVideoCodec(96, "H264-SVC")};
+const VideoCodec kVideoCodecs1Reverse[] = {CreateVideoCodec(97, "H264"),
+ CreateVideoCodec(96, "H264-SVC")};
-static const VideoCodec kVideoCodecs2[] = {
- cricket::CreateVideoCodec(126, "H264"),
- cricket::CreateVideoCodec(127, "H263")};
+const VideoCodec kVideoCodecs2[] = {CreateVideoCodec(126, "H264"),
+ CreateVideoCodec(127, "H263")};
-static const VideoCodec kVideoCodecsAnswer[] = {
- cricket::CreateVideoCodec(97, "H264")};
+const VideoCodec kVideoCodecsAnswer[] = {CreateVideoCodec(97, "H264")};
-static const RtpExtension kAudioRtpExtension1[] = {
+const RtpExtension kAudioRtpExtension1[] = {
RtpExtension("urn:ietf:params:rtp-hdrext:ssrc-audio-level", 8),
RtpExtension("http://google.com/testing/audio_something", 10),
};
-static const RtpExtension kAudioRtpExtensionEncrypted1[] = {
+const RtpExtension kAudioRtpExtensionEncrypted1[] = {
RtpExtension("urn:ietf:params:rtp-hdrext:ssrc-audio-level", 8),
RtpExtension("http://google.com/testing/audio_something", 10),
RtpExtension("urn:ietf:params:rtp-hdrext:ssrc-audio-level", 12, true),
RtpExtension("http://google.com/testing/audio_something", 11, true),
};
-static const RtpExtension kAudioRtpExtension2[] = {
+const RtpExtension kAudioRtpExtension2[] = {
RtpExtension("urn:ietf:params:rtp-hdrext:ssrc-audio-level", 2),
RtpExtension("http://google.com/testing/audio_something_else", 8),
RtpExtension("http://google.com/testing/both_audio_and_video", 7),
};
-static const RtpExtension kAudioRtpExtension3[] = {
+const RtpExtension kAudioRtpExtension3[] = {
RtpExtension("http://google.com/testing/audio_something", 2),
RtpExtension("http://google.com/testing/both_audio_and_video", 3),
};
-static const RtpExtension kAudioRtpExtension3ForEncryption[] = {
+const RtpExtension kAudioRtpExtension3ForEncryption[] = {
RtpExtension("http://google.com/testing/audio_something", 2),
// Use RTP extension that supports encryption.
RtpExtension("urn:ietf:params:rtp-hdrext:toffset", 3),
};
-static const RtpExtension kAudioRtpExtension3ForEncryptionOffer[] = {
+const RtpExtension kAudioRtpExtension3ForEncryptionOffer[] = {
RtpExtension("http://google.com/testing/audio_something", 2),
RtpExtension("urn:ietf:params:rtp-hdrext:toffset", 3),
RtpExtension("http://google.com/testing/audio_something", 14, true),
RtpExtension("urn:ietf:params:rtp-hdrext:toffset", 13, true),
};
-static const RtpExtension kVideoRtpExtension3ForEncryptionOffer[] = {
+const RtpExtension kVideoRtpExtension3ForEncryptionOffer[] = {
RtpExtension("http://google.com/testing/video_something", 4),
RtpExtension("urn:ietf:params:rtp-hdrext:toffset", 3),
RtpExtension("http://google.com/testing/video_something", 12, true),
RtpExtension("urn:ietf:params:rtp-hdrext:toffset", 13, true),
};
-static const RtpExtension kAudioRtpExtensionAnswer[] = {
+const RtpExtension kAudioRtpExtensionAnswer[] = {
RtpExtension("urn:ietf:params:rtp-hdrext:ssrc-audio-level", 8),
};
-static const RtpExtension kAudioRtpExtensionEncryptedAnswer[] = {
+const RtpExtension kAudioRtpExtensionEncryptedAnswer[] = {
RtpExtension("urn:ietf:params:rtp-hdrext:ssrc-audio-level", 12, true),
};
-static const RtpExtension kVideoRtpExtension1[] = {
+const RtpExtension kVideoRtpExtension1[] = {
RtpExtension("urn:ietf:params:rtp-hdrext:toffset", 14),
RtpExtension("http://google.com/testing/video_something", 13),
};
-static const RtpExtension kVideoRtpExtensionEncrypted1[] = {
+const RtpExtension kVideoRtpExtensionEncrypted1[] = {
RtpExtension("urn:ietf:params:rtp-hdrext:toffset", 14),
RtpExtension("http://google.com/testing/video_something", 13),
RtpExtension("urn:ietf:params:rtp-hdrext:toffset", 9, true),
RtpExtension("http://google.com/testing/video_something", 7, true),
};
-static const RtpExtension kVideoRtpExtension2[] = {
+const RtpExtension kVideoRtpExtension2[] = {
RtpExtension("urn:ietf:params:rtp-hdrext:toffset", 2),
RtpExtension("http://google.com/testing/video_something_else", 14),
RtpExtension("http://google.com/testing/both_audio_and_video", 7),
};
-static const RtpExtension kVideoRtpExtension3[] = {
+const RtpExtension kVideoRtpExtension3[] = {
RtpExtension("http://google.com/testing/video_something", 4),
RtpExtension("http://google.com/testing/both_audio_and_video", 5),
};
-static const RtpExtension kVideoRtpExtension3ForEncryption[] = {
+const RtpExtension kVideoRtpExtension3ForEncryption[] = {
RtpExtension("http://google.com/testing/video_something", 4),
// Use RTP extension that supports encryption.
RtpExtension("urn:ietf:params:rtp-hdrext:toffset", 5),
};
-static const RtpExtension kVideoRtpExtensionAnswer[] = {
+const RtpExtension kVideoRtpExtensionAnswer[] = {
RtpExtension("urn:ietf:params:rtp-hdrext:toffset", 14),
};
-static const RtpExtension kVideoRtpExtensionEncryptedAnswer[] = {
+const RtpExtension kVideoRtpExtensionEncryptedAnswer[] = {
RtpExtension("urn:ietf:params:rtp-hdrext:toffset", 9, true),
};
-static const RtpExtension kRtpExtensionTransportSequenceNumber01[] = {
+const RtpExtension kRtpExtensionTransportSequenceNumber01[] = {
RtpExtension("http://www.ietf.org/id/"
"draft-holmer-rmcat-transport-wide-cc-extensions-01",
1),
};
-static const RtpExtension kRtpExtensionTransportSequenceNumber01And02[] = {
+const RtpExtension kRtpExtensionTransportSequenceNumber01And02[] = {
RtpExtension("http://www.ietf.org/id/"
"draft-holmer-rmcat-transport-wide-cc-extensions-01",
1),
@@ -258,68 +223,64 @@ static const RtpExtension kRtpExtensionTransportSequenceNumber01And02[] = {
2),
};
-static const RtpExtension kRtpExtensionTransportSequenceNumber02[] = {
+const RtpExtension kRtpExtensionTransportSequenceNumber02[] = {
RtpExtension(
"http://www.webrtc.org/experiments/rtp-hdrext/transport-wide-cc-02",
2),
};
-static const RtpExtension kRtpExtensionGenericFrameDescriptorUri00[] = {
+const RtpExtension kRtpExtensionGenericFrameDescriptorUri00[] = {
RtpExtension("http://www.webrtc.org/experiments/rtp-hdrext/"
"generic-frame-descriptor-00",
3),
};
-static const uint32_t kSimulcastParamsSsrc[] = {10, 11, 20, 21, 30, 31};
-static const uint32_t kSimSsrc[] = {10, 20, 30};
-static const uint32_t kFec1Ssrc[] = {10, 11};
-static const uint32_t kFec2Ssrc[] = {20, 21};
-static const uint32_t kFec3Ssrc[] = {30, 31};
-
-static const char kMediaStream1[] = "stream_1";
-static const char kMediaStream2[] = "stream_2";
-static const char kVideoTrack1[] = "video_1";
-static const char kVideoTrack2[] = "video_2";
-static const char kAudioTrack1[] = "audio_1";
-static const char kAudioTrack2[] = "audio_2";
-static const char kAudioTrack3[] = "audio_3";
-
-static const char* kMediaProtocols[] = {"RTP/AVP", "RTP/SAVP", "RTP/AVPF",
- "RTP/SAVPF"};
-static const char* kMediaProtocolsDtls[] = {
- "TCP/TLS/RTP/SAVPF", "TCP/TLS/RTP/SAVP", "UDP/TLS/RTP/SAVPF",
- "UDP/TLS/RTP/SAVP"};
+const uint32_t kSimulcastParamsSsrc[] = {10, 11, 20, 21, 30, 31};
+const uint32_t kSimSsrc[] = {10, 20, 30};
+const uint32_t kFec1Ssrc[] = {10, 11};
+const uint32_t kFec2Ssrc[] = {20, 21};
+const uint32_t kFec3Ssrc[] = {30, 31};
+
+const char kMediaStream1[] = "stream_1";
+const char kMediaStream2[] = "stream_2";
+const char kVideoTrack1[] = "video_1";
+const char kVideoTrack2[] = "video_2";
+const char kAudioTrack1[] = "audio_1";
+const char kAudioTrack2[] = "audio_2";
+const char kAudioTrack3[] = "audio_3";
+
+const char* kMediaProtocols[] = {"RTP/AVP", "RTP/SAVP", "RTP/AVPF",
+ "RTP/SAVPF"};
+const char* kMediaProtocolsDtls[] = {"TCP/TLS/RTP/SAVPF", "TCP/TLS/RTP/SAVP",
+ "UDP/TLS/RTP/SAVPF", "UDP/TLS/RTP/SAVP"};
// SRTP cipher name negotiated by the tests. This must be updated if the
// default changes.
-static const char* kDefaultSrtpCryptoSuite = kCsAesCm128HmacSha1_80;
-static const char* kDefaultSrtpCryptoSuiteGcm = kCsAeadAes256Gcm;
-static const uint8_t kDefaultCryptoSuiteSize = 3U;
+const char* kDefaultSrtpCryptoSuite = kCsAesCm128HmacSha1_80;
+const char* kDefaultSrtpCryptoSuiteGcm = kCsAeadAes256Gcm;
+const uint8_t kDefaultCryptoSuiteSize = 3U;
// These constants are used to make the code using "AddMediaDescriptionOptions"
// more readable.
-static constexpr bool kStopped = true;
-static constexpr bool kActive = false;
+constexpr bool kStopped = true;
+constexpr bool kActive = false;
-static bool IsMediaContentOfType(const ContentInfo* content,
- MediaType media_type) {
+bool IsMediaContentOfType(const ContentInfo* content, MediaType media_type) {
RTC_DCHECK(content);
return content->media_description()->type() == media_type;
}
-static RtpTransceiverDirection GetMediaDirection(const ContentInfo* content) {
+RtpTransceiverDirection GetMediaDirection(const ContentInfo* content) {
RTC_DCHECK(content);
return content->media_description()->direction();
}
-static void AddRtxCodec(const VideoCodec& rtx_codec,
- std::vector<VideoCodec>* codecs) {
- ASSERT_FALSE(cricket::FindCodecById(*codecs, rtx_codec.id));
+void AddRtxCodec(const VideoCodec& rtx_codec, std::vector<VideoCodec>* codecs) {
+ ASSERT_FALSE(FindCodecById(*codecs, rtx_codec.id));
codecs->push_back(rtx_codec);
}
-static std::vector<std::string> GetCodecNames(
- const std::vector<cricket::Codec>& codecs) {
+std::vector<std::string> GetCodecNames(const std::vector<Codec>& codecs) {
std::vector<std::string> codec_names;
codec_names.reserve(codecs.size());
for (const auto& codec : codecs) {
@@ -348,29 +309,29 @@ FindFirstMediaDescriptionByMid(const std::string& mid,
}
// Add a media section to the `session_options`.
-static void AddMediaDescriptionOptions(MediaType type,
- const std::string& mid,
- RtpTransceiverDirection direction,
- bool stopped,
- MediaSessionOptions* opts) {
+void AddMediaDescriptionOptions(MediaType type,
+ const std::string& mid,
+ RtpTransceiverDirection direction,
+ bool stopped,
+ MediaSessionOptions* opts) {
opts->media_description_options.push_back(
MediaDescriptionOptions(type, mid, direction, stopped));
}
-static void AddAudioVideoSections(RtpTransceiverDirection direction,
- MediaSessionOptions* opts) {
+void AddAudioVideoSections(RtpTransceiverDirection direction,
+ MediaSessionOptions* opts) {
AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio", direction, kActive,
opts);
AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video", direction, kActive,
opts);
}
-static void AddDataSection(RtpTransceiverDirection direction,
- MediaSessionOptions* opts) {
+void AddDataSection(RtpTransceiverDirection direction,
+ MediaSessionOptions* opts) {
AddMediaDescriptionOptions(MEDIA_TYPE_DATA, "data", direction, kActive, opts);
}
-static void AttachSenderToMediaDescriptionOptions(
+void AttachSenderToMediaDescriptionOptions(
const std::string& mid,
MediaType type,
const std::string& track_id,
@@ -393,7 +354,7 @@ static void AttachSenderToMediaDescriptionOptions(
}
}
-static void AttachSenderToMediaDescriptionOptions(
+void AttachSenderToMediaDescriptionOptions(
const std::string& mid,
MediaType type,
const std::string& track_id,
@@ -405,23 +366,21 @@ static void AttachSenderToMediaDescriptionOptions(
session_options);
}
-static void DetachSenderFromMediaSection(const std::string& mid,
- const std::string& track_id,
- MediaSessionOptions* session_options) {
- std::vector<cricket::SenderOptions>& sender_options_list =
+void DetachSenderFromMediaSection(const std::string& mid,
+ const std::string& track_id,
+ MediaSessionOptions* session_options) {
+ std::vector<SenderOptions>& sender_options_list =
FindFirstMediaDescriptionByMid(mid, session_options)->sender_options;
- auto sender_it =
- absl::c_find_if(sender_options_list,
- [track_id](const cricket::SenderOptions& sender_options) {
- return sender_options.track_id == track_id;
- });
+ auto sender_it = absl::c_find_if(
+ sender_options_list, [track_id](const SenderOptions& sender_options) {
+ return sender_options.track_id == track_id;
+ });
RTC_DCHECK(sender_it != sender_options_list.end());
sender_options_list.erase(sender_it);
}
-// Helper function used to create a default MediaSessionOptions for Plan B SDP.
-// (https://tools.ietf.org/html/draft-uberti-rtcweb-plan-00).
-static MediaSessionOptions CreatePlanBMediaSessionOptions() {
+// Helper function used to create recv-only audio MediaSessionOptions.
+MediaSessionOptions CreateAudioMediaSession() {
MediaSessionOptions session_options;
AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio",
RtpTransceiverDirection::kRecvOnly, kActive,
@@ -433,7 +392,7 @@ static MediaSessionOptions CreatePlanBMediaSessionOptions() {
void PreferGcmCryptoParameters(CryptoParamsVec* cryptos) {
cryptos->erase(
std::remove_if(cryptos->begin(), cryptos->end(),
- [](const cricket::CryptoParams& crypto) {
+ [](const CryptoParams& crypto) {
return crypto.crypto_suite != kCsAeadAes256Gcm &&
crypto.crypto_suite != kCsAeadAes128Gcm;
}),
@@ -444,7 +403,7 @@ void PreferGcmCryptoParameters(CryptoParamsVec* cryptos) {
// was designed for Plan B SDP, where only one audio "m=" section and one video
// "m=" section could be generated, and ordering couldn't be controlled. Many of
// these tests may be obsolete as a result, and should be refactored or removed.
-class MediaSessionDescriptionFactoryTest : public ::testing::Test {
+class MediaSessionDescriptionFactoryTest : public testing::Test {
public:
MediaSessionDescriptionFactoryTest()
: tdf1_(field_trials),
@@ -548,9 +507,9 @@ class MediaSessionDescriptionFactoryTest : public ::testing::Test {
EXPECT_EQ(current_audio_ufrag, ti_audio->description.ice_ufrag);
EXPECT_EQ(current_audio_pwd, ti_audio->description.ice_pwd);
} else {
- EXPECT_EQ(static_cast<size_t>(cricket::ICE_UFRAG_LENGTH),
+ EXPECT_EQ(static_cast<size_t>(ICE_UFRAG_LENGTH),
ti_audio->description.ice_ufrag.size());
- EXPECT_EQ(static_cast<size_t>(cricket::ICE_PWD_LENGTH),
+ EXPECT_EQ(static_cast<size_t>(ICE_PWD_LENGTH),
ti_audio->description.ice_pwd.size());
}
auto media_desc_options_it =
@@ -572,9 +531,9 @@ class MediaSessionDescriptionFactoryTest : public ::testing::Test {
EXPECT_EQ(current_video_ufrag, ti_video->description.ice_ufrag);
EXPECT_EQ(current_video_pwd, ti_video->description.ice_pwd);
} else {
- EXPECT_EQ(static_cast<size_t>(cricket::ICE_UFRAG_LENGTH),
+ EXPECT_EQ(static_cast<size_t>(ICE_UFRAG_LENGTH),
ti_video->description.ice_ufrag.size());
- EXPECT_EQ(static_cast<size_t>(cricket::ICE_PWD_LENGTH),
+ EXPECT_EQ(static_cast<size_t>(ICE_PWD_LENGTH),
ti_video->description.ice_pwd.size());
}
}
@@ -593,9 +552,9 @@ class MediaSessionDescriptionFactoryTest : public ::testing::Test {
EXPECT_EQ(current_data_ufrag, ti_data->description.ice_ufrag);
EXPECT_EQ(current_data_pwd, ti_data->description.ice_pwd);
} else {
- EXPECT_EQ(static_cast<size_t>(cricket::ICE_UFRAG_LENGTH),
+ EXPECT_EQ(static_cast<size_t>(ICE_UFRAG_LENGTH),
ti_data->description.ice_ufrag.size());
- EXPECT_EQ(static_cast<size_t>(cricket::ICE_PWD_LENGTH),
+ EXPECT_EQ(static_cast<size_t>(ICE_PWD_LENGTH),
ti_data->description.ice_pwd.size());
}
}
@@ -625,10 +584,10 @@ class MediaSessionDescriptionFactoryTest : public ::testing::Test {
f1_.CreateAnswerOrError(ref_desc.get(), options, nullptr).MoveValue();
}
ASSERT_TRUE(desc);
- const cricket::MediaContentDescription* audio_media_desc =
+ const MediaContentDescription* audio_media_desc =
desc->GetContentDescriptionByName("audio");
ASSERT_TRUE(audio_media_desc);
- const cricket::MediaContentDescription* video_media_desc =
+ const MediaContentDescription* video_media_desc =
desc->GetContentDescriptionByName("video");
ASSERT_TRUE(video_media_desc);
EXPECT_TRUE(CompareCryptoParams(audio_media_desc->cryptos(),
@@ -638,7 +597,7 @@ class MediaSessionDescriptionFactoryTest : public ::testing::Test {
// Verify the selected crypto is one from the reference audio
// media content.
- const cricket::MediaContentDescription* ref_audio_media_desc =
+ const MediaContentDescription* ref_audio_media_desc =
ref_desc->GetContentDescriptionByName("audio");
bool found = false;
for (size_t i = 0; i < ref_audio_media_desc->cryptos().size(); ++i) {
@@ -680,10 +639,10 @@ class MediaSessionDescriptionFactoryTest : public ::testing::Test {
EXPECT_EQ(expected_direction_in_answer, vcd_answer->direction());
}
- bool VerifyNoCNCodecs(const cricket::ContentInfo* content) {
+ bool VerifyNoCNCodecs(const ContentInfo* content) {
RTC_DCHECK(content);
RTC_CHECK(content->media_description());
- for (const cricket::Codec& codec : content->media_description()->codecs()) {
+ for (const Codec& codec : content->media_description()->codecs()) {
if (codec.name == "CN") {
return false;
}
@@ -706,7 +665,7 @@ class MediaSessionDescriptionFactoryTest : public ::testing::Test {
f1_.CreateOfferOrError(offer_opts, nullptr).MoveValue();
ASSERT_TRUE(offer.get());
if (gcm_offer && gcm_answer) {
- for (cricket::ContentInfo& content : offer->contents()) {
+ for (ContentInfo& content : offer->contents()) {
auto cryptos = content.media_description()->cryptos();
PreferGcmCryptoParameters(&cryptos);
content.media_description()->set_cryptos(cryptos);
@@ -720,8 +679,8 @@ class MediaSessionDescriptionFactoryTest : public ::testing::Test {
ASSERT_TRUE(vc);
EXPECT_EQ(MediaProtocolType::kRtp, ac->type);
EXPECT_EQ(MediaProtocolType::kRtp, vc->type);
- const AudioContentDescription* acd = ac->media_description()->as_audio();
- const VideoContentDescription* vcd = vc->media_description()->as_video();
+ const MediaContentDescription* acd = ac->media_description();
+ const MediaContentDescription* vcd = vc->media_description();
EXPECT_EQ(MEDIA_TYPE_AUDIO, acd->type());
EXPECT_THAT(acd->codecs(), ElementsAreArray(kAudioCodecsAnswer));
EXPECT_EQ(kAutoBandwidth, acd->bandwidth()); // negotiated auto bw
@@ -741,13 +700,13 @@ class MediaSessionDescriptionFactoryTest : public ::testing::Test {
} else {
ASSERT_CRYPTO(vcd, 1U, kDefaultSrtpCryptoSuite);
}
- EXPECT_EQ(cricket::kMediaProtocolSavpf, vcd->protocol());
+ EXPECT_EQ(kMediaProtocolSavpf, vcd->protocol());
}
void TestTransportSequenceNumberNegotiation(
- const cricket::RtpHeaderExtensions& local,
- const cricket::RtpHeaderExtensions& offered,
- const cricket::RtpHeaderExtensions& expectedAnswer) {
+ const RtpHeaderExtensions& local,
+ const RtpHeaderExtensions& offered,
+ const RtpHeaderExtensions& expectedAnswer) {
MediaSessionOptions opts;
AddAudioVideoSections(RtpTransceiverDirection::kRecvOnly, &opts);
SetAudioVideoRtpHeaderExtensions(offered, offered, &opts);
@@ -767,8 +726,7 @@ class MediaSessionDescriptionFactoryTest : public ::testing::Test {
}
std::vector<webrtc::RtpHeaderExtensionCapability>
- HeaderExtensionCapabilitiesFromRtpExtensions(
- cricket::RtpHeaderExtensions extensions) {
+ HeaderExtensionCapabilitiesFromRtpExtensions(RtpHeaderExtensions extensions) {
std::vector<webrtc::RtpHeaderExtensionCapability> capabilities;
for (const auto& extension : extensions) {
webrtc::RtpHeaderExtensionCapability capability(
@@ -779,8 +737,8 @@ class MediaSessionDescriptionFactoryTest : public ::testing::Test {
return capabilities;
}
- void SetAudioVideoRtpHeaderExtensions(cricket::RtpHeaderExtensions audio_exts,
- cricket::RtpHeaderExtensions video_exts,
+ void SetAudioVideoRtpHeaderExtensions(RtpHeaderExtensions audio_exts,
+ RtpHeaderExtensions video_exts,
MediaSessionOptions* opts) {
auto audio_caps = HeaderExtensionCapabilitiesFromRtpExtensions(audio_exts);
auto video_caps = HeaderExtensionCapabilitiesFromRtpExtensions(video_exts);
@@ -812,22 +770,21 @@ class MediaSessionDescriptionFactoryTest : public ::testing::Test {
TEST_F(MediaSessionDescriptionFactoryTest, TestCreateAudioOffer) {
f1_.set_secure(SEC_ENABLED);
std::unique_ptr<SessionDescription> offer =
- f1_.CreateOfferOrError(CreatePlanBMediaSessionOptions(), nullptr)
- .MoveValue();
+ f1_.CreateOfferOrError(CreateAudioMediaSession(), nullptr).MoveValue();
ASSERT_TRUE(offer.get());
const ContentInfo* ac = offer->GetContentByName("audio");
const ContentInfo* vc = offer->GetContentByName("video");
ASSERT_TRUE(ac);
EXPECT_FALSE(vc);
EXPECT_EQ(MediaProtocolType::kRtp, ac->type);
- const AudioContentDescription* acd = ac->media_description()->as_audio();
+ const MediaContentDescription* acd = ac->media_description();
EXPECT_EQ(MEDIA_TYPE_AUDIO, acd->type());
EXPECT_EQ(f1_.audio_sendrecv_codecs(), acd->codecs());
EXPECT_EQ(0U, acd->first_ssrc()); // no sender is attached.
EXPECT_EQ(kAutoBandwidth, acd->bandwidth()); // default bandwidth (auto)
EXPECT_TRUE(acd->rtcp_mux()); // rtcp-mux defaults on
ASSERT_CRYPTO(acd, kDefaultCryptoSuiteSize, kDefaultSrtpCryptoSuite);
- EXPECT_EQ(cricket::kMediaProtocolSavpf, acd->protocol());
+ EXPECT_EQ(kMediaProtocolSavpf, acd->protocol());
}
// Create an offer with just Opus and RED.
@@ -843,7 +800,7 @@ TEST_F(MediaSessionDescriptionFactoryTest,
EXPECT_EQ("opus", preferences[0].name);
EXPECT_EQ("red", preferences[1].name);
- auto opts = CreatePlanBMediaSessionOptions();
+ auto opts = CreateAudioMediaSession();
opts.media_description_options.at(0).codec_preferences = preferences;
std::unique_ptr<SessionDescription> offer =
f1_.CreateOfferOrError(opts, nullptr).MoveValue();
@@ -853,7 +810,7 @@ TEST_F(MediaSessionDescriptionFactoryTest,
ASSERT_TRUE(ac != NULL);
ASSERT_TRUE(vc == NULL);
EXPECT_EQ(MediaProtocolType::kRtp, ac->type);
- const AudioContentDescription* acd = ac->media_description()->as_audio();
+ const MediaContentDescription* acd = ac->media_description();
EXPECT_EQ(MEDIA_TYPE_AUDIO, acd->type());
EXPECT_EQ(2U, acd->codecs().size());
EXPECT_EQ("opus", acd->codecs()[0].name);
@@ -872,7 +829,7 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCreateAudioOfferWithRedForOpus) {
EXPECT_EQ("red", preferences[0].name);
EXPECT_EQ("opus", preferences[1].name);
- auto opts = CreatePlanBMediaSessionOptions();
+ auto opts = CreateAudioMediaSession();
opts.media_description_options.at(0).codec_preferences = preferences;
std::unique_ptr<SessionDescription> offer =
f1_.CreateOfferOrError(opts, nullptr).MoveValue();
@@ -882,7 +839,7 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCreateAudioOfferWithRedForOpus) {
ASSERT_TRUE(ac != NULL);
ASSERT_TRUE(vc == NULL);
EXPECT_EQ(MediaProtocolType::kRtp, ac->type);
- const AudioContentDescription* acd = ac->media_description()->as_audio();
+ const MediaContentDescription* acd = ac->media_description();
EXPECT_EQ(MEDIA_TYPE_AUDIO, acd->type());
EXPECT_EQ(2U, acd->codecs().size());
EXPECT_EQ("red", acd->codecs()[0].name);
@@ -903,22 +860,22 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCreateVideoOffer) {
ASSERT_TRUE(vc);
EXPECT_EQ(MediaProtocolType::kRtp, ac->type);
EXPECT_EQ(MediaProtocolType::kRtp, vc->type);
- const AudioContentDescription* acd = ac->media_description()->as_audio();
- const VideoContentDescription* vcd = vc->media_description()->as_video();
+ const MediaContentDescription* acd = ac->media_description();
+ const MediaContentDescription* vcd = vc->media_description();
EXPECT_EQ(MEDIA_TYPE_AUDIO, acd->type());
EXPECT_EQ(f1_.audio_sendrecv_codecs(), acd->codecs());
EXPECT_EQ(0U, acd->first_ssrc()); // no sender is attached
EXPECT_EQ(kAutoBandwidth, acd->bandwidth()); // default bandwidth (auto)
EXPECT_TRUE(acd->rtcp_mux()); // rtcp-mux defaults on
ASSERT_CRYPTO(acd, kDefaultCryptoSuiteSize, kDefaultSrtpCryptoSuite);
- EXPECT_EQ(cricket::kMediaProtocolSavpf, acd->protocol());
+ EXPECT_EQ(kMediaProtocolSavpf, acd->protocol());
EXPECT_EQ(MEDIA_TYPE_VIDEO, vcd->type());
EXPECT_EQ(f1_.video_sendrecv_codecs(), vcd->codecs());
EXPECT_EQ(0U, vcd->first_ssrc()); // no sender is attached
EXPECT_EQ(kAutoBandwidth, vcd->bandwidth()); // default bandwidth (auto)
EXPECT_TRUE(vcd->rtcp_mux()); // rtcp-mux defaults on
ASSERT_CRYPTO(vcd, kDefaultCryptoSuiteSize, kDefaultSrtpCryptoSuite);
- EXPECT_EQ(cricket::kMediaProtocolSavpf, vcd->protocol());
+ EXPECT_EQ(kMediaProtocolSavpf, vcd->protocol());
}
// Test creating an offer with bundle where the Codecs have the same dynamic
@@ -978,9 +935,9 @@ TEST_F(MediaSessionDescriptionFactoryTest,
EXPECT_TRUE(acd);
ASSERT_CRYPTO(acd, 1U, kDefaultSrtpCryptoSuite);
- EXPECT_EQ(cricket::kMediaProtocolSavpf, acd->protocol());
+ EXPECT_EQ(kMediaProtocolSavpf, acd->protocol());
ASSERT_CRYPTO(vcd, 1U, kDefaultSrtpCryptoSuite);
- EXPECT_EQ(cricket::kMediaProtocolSavpf, vcd->protocol());
+ EXPECT_EQ(kMediaProtocolSavpf, vcd->protocol());
}
// Create an SCTP data offer with bundle without error.
@@ -996,7 +953,7 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCreateSctpDataOffer) {
auto dcd = GetFirstSctpDataContentDescription(offer.get());
ASSERT_TRUE(dcd);
// Since this transport is insecure, the protocol should be "SCTP".
- EXPECT_EQ(cricket::kMediaProtocolSctp, dcd->protocol());
+ EXPECT_EQ(kMediaProtocolSctp, dcd->protocol());
}
// Create an SCTP data offer with bundle without error.
@@ -1013,7 +970,7 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCreateSecureSctpDataOffer) {
auto dcd = GetFirstSctpDataContentDescription(offer.get());
ASSERT_TRUE(dcd);
// The protocol should now be "UDP/DTLS/SCTP"
- EXPECT_EQ(cricket::kMediaProtocolUdpDtlsSctp, dcd->protocol());
+ EXPECT_EQ(kMediaProtocolUdpDtlsSctp, dcd->protocol());
}
// Test creating an sctp data channel from an already generated offer.
@@ -1027,13 +984,13 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCreateImplicitSctpDataOffer) {
ASSERT_TRUE(offer1.get());
const ContentInfo* data = offer1->GetContentByName("data");
ASSERT_TRUE(data);
- ASSERT_EQ(cricket::kMediaProtocolSctp, data->media_description()->protocol());
+ ASSERT_EQ(kMediaProtocolSctp, data->media_description()->protocol());
std::unique_ptr<SessionDescription> offer2(
f1_.CreateOfferOrError(opts, offer1.get()).MoveValue());
data = offer2->GetContentByName("data");
ASSERT_TRUE(data);
- EXPECT_EQ(cricket::kMediaProtocolSctp, data->media_description()->protocol());
+ EXPECT_EQ(kMediaProtocolSctp, data->media_description()->protocol());
}
// Test that if BUNDLE is enabled and all media sections are rejected then the
@@ -1051,7 +1008,7 @@ TEST_F(MediaSessionDescriptionFactoryTest, ReOfferNoBundleGroupIfAllRejected) {
std::unique_ptr<SessionDescription> reoffer =
f1_.CreateOfferOrError(opts, offer.get()).MoveValue();
- EXPECT_FALSE(reoffer->GetGroupByName(cricket::GROUP_TYPE_BUNDLE));
+ EXPECT_FALSE(reoffer->GetGroupByName(GROUP_TYPE_BUNDLE));
}
// Test that if BUNDLE is enabled and the remote re-offer does not include a
@@ -1074,7 +1031,7 @@ TEST_F(MediaSessionDescriptionFactoryTest, ReAnswerNoBundleGroupIfAllRejected) {
std::unique_ptr<SessionDescription> reanswer =
f2_.CreateAnswerOrError(reoffer.get(), opts, answer.get()).MoveValue();
- EXPECT_FALSE(reanswer->GetGroupByName(cricket::GROUP_TYPE_BUNDLE));
+ EXPECT_FALSE(reanswer->GetGroupByName(GROUP_TYPE_BUNDLE));
}
// Test that if BUNDLE is enabled and the previous offerer-tagged media section
@@ -1097,8 +1054,7 @@ TEST_F(MediaSessionDescriptionFactoryTest, ReOfferChangeBundleOffererTagged) {
std::unique_ptr<SessionDescription> reoffer =
f1_.CreateOfferOrError(opts, offer.get()).MoveValue();
- const cricket::ContentGroup* bundle_group =
- reoffer->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
+ const ContentGroup* bundle_group = reoffer->GetGroupByName(GROUP_TYPE_BUNDLE);
ASSERT_TRUE(bundle_group);
EXPECT_FALSE(bundle_group->HasContentName("audio"));
EXPECT_TRUE(bundle_group->HasContentName("video"));
@@ -1128,8 +1084,8 @@ TEST_F(MediaSessionDescriptionFactoryTest, ReAnswerChangedBundleOffererTagged) {
std::unique_ptr<SessionDescription> reanswer =
f2_.CreateAnswerOrError(reoffer.get(), opts, answer.get()).MoveValue();
- const cricket::ContentGroup* bundle_group =
- reanswer->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
+ const ContentGroup* bundle_group =
+ reanswer->GetGroupByName(GROUP_TYPE_BUNDLE);
ASSERT_TRUE(bundle_group);
EXPECT_FALSE(bundle_group->HasContentName("audio"));
EXPECT_TRUE(bundle_group->HasContentName("video"));
@@ -1159,10 +1115,10 @@ TEST_F(MediaSessionDescriptionFactoryTest,
// Munge the offer to have two groups. Offers like these cannot be generated
// without munging, but it is valid to receive such offers from remote
// endpoints.
- cricket::ContentGroup bundle_group1(cricket::GROUP_TYPE_BUNDLE);
+ ContentGroup bundle_group1(GROUP_TYPE_BUNDLE);
bundle_group1.AddContentName("1");
bundle_group1.AddContentName("2");
- cricket::ContentGroup bundle_group2(cricket::GROUP_TYPE_BUNDLE);
+ ContentGroup bundle_group2(GROUP_TYPE_BUNDLE);
bundle_group2.AddContentName("3");
bundle_group2.AddContentName("4");
offer->AddGroup(bundle_group1);
@@ -1174,8 +1130,8 @@ TEST_F(MediaSessionDescriptionFactoryTest,
std::unique_ptr<SessionDescription> answer =
f2_.CreateAnswerOrError(offer.get(), opts, nullptr).MoveValue();
- std::vector<const cricket::ContentGroup*> answer_groups =
- answer->GetGroupsByName(cricket::GROUP_TYPE_BUNDLE);
+ std::vector<const ContentGroup*> answer_groups =
+ answer->GetGroupsByName(GROUP_TYPE_BUNDLE);
ASSERT_EQ(answer_groups.size(), 2u);
EXPECT_EQ(answer_groups[0]->content_names().size(), 2u);
EXPECT_TRUE(answer_groups[0]->HasContentName("1"));
@@ -1189,7 +1145,7 @@ TEST_F(MediaSessionDescriptionFactoryTest,
opts.bundle_enabled = false;
answer = f2_.CreateAnswerOrError(offer.get(), opts, nullptr).MoveValue();
- answer_groups = answer->GetGroupsByName(cricket::GROUP_TYPE_BUNDLE);
+ answer_groups = answer->GetGroupsByName(GROUP_TYPE_BUNDLE);
// Rejected groups are still listed, but they are empty.
ASSERT_EQ(answer_groups.size(), 2u);
EXPECT_TRUE(answer_groups[0]->content_names().empty());
@@ -1268,8 +1224,8 @@ TEST_F(MediaSessionDescriptionFactoryTest,
const ContentInfo* vc = offer->GetContentByName("video");
ASSERT_TRUE(ac);
ASSERT_TRUE(vc);
- const AudioContentDescription* acd = ac->media_description()->as_audio();
- const VideoContentDescription* vcd = vc->media_description()->as_video();
+ const MediaContentDescription* acd = ac->media_description();
+ const MediaContentDescription* vcd = vc->media_description();
EXPECT_FALSE(vcd->has_ssrcs()); // No StreamParams.
EXPECT_FALSE(acd->has_ssrcs()); // No StreamParams.
@@ -1336,26 +1292,24 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCreateAudioAnswer) {
f1_.set_secure(SEC_ENABLED);
f2_.set_secure(SEC_ENABLED);
std::unique_ptr<SessionDescription> offer =
- f1_.CreateOfferOrError(CreatePlanBMediaSessionOptions(), nullptr)
- .MoveValue();
+ f1_.CreateOfferOrError(CreateAudioMediaSession(), nullptr).MoveValue();
ASSERT_TRUE(offer.get());
std::unique_ptr<SessionDescription> answer =
- f2_.CreateAnswerOrError(offer.get(), CreatePlanBMediaSessionOptions(),
- nullptr)
+ f2_.CreateAnswerOrError(offer.get(), CreateAudioMediaSession(), nullptr)
.MoveValue();
const ContentInfo* ac = answer->GetContentByName("audio");
const ContentInfo* vc = answer->GetContentByName("video");
ASSERT_TRUE(ac);
EXPECT_FALSE(vc);
EXPECT_EQ(MediaProtocolType::kRtp, ac->type);
- const AudioContentDescription* acd = ac->media_description()->as_audio();
+ const MediaContentDescription* acd = ac->media_description();
EXPECT_EQ(MEDIA_TYPE_AUDIO, acd->type());
EXPECT_THAT(acd->codecs(), ElementsAreArray(kAudioCodecsAnswer));
EXPECT_EQ(0U, acd->first_ssrc()); // no sender is attached
EXPECT_EQ(kAutoBandwidth, acd->bandwidth()); // negotiated auto bw
EXPECT_TRUE(acd->rtcp_mux()); // negotiated rtcp-mux
ASSERT_CRYPTO(acd, 1U, kDefaultSrtpCryptoSuite);
- EXPECT_EQ(cricket::kMediaProtocolSavpf, acd->protocol());
+ EXPECT_EQ(kMediaProtocolSavpf, acd->protocol());
}
// Create a typical audio answer with GCM ciphers enabled, and ensure it
@@ -1363,11 +1317,11 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCreateAudioAnswer) {
TEST_F(MediaSessionDescriptionFactoryTest, TestCreateAudioAnswerGcm) {
f1_.set_secure(SEC_ENABLED);
f2_.set_secure(SEC_ENABLED);
- MediaSessionOptions opts = CreatePlanBMediaSessionOptions();
+ MediaSessionOptions opts = CreateAudioMediaSession();
std::unique_ptr<SessionDescription> offer =
f1_.CreateOfferOrError(opts, nullptr).MoveValue();
ASSERT_TRUE(offer.get());
- for (cricket::ContentInfo& content : offer->contents()) {
+ for (ContentInfo& content : offer->contents()) {
auto cryptos = content.media_description()->cryptos();
PreferGcmCryptoParameters(&cryptos);
content.media_description()->set_cryptos(cryptos);
@@ -1379,14 +1333,14 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCreateAudioAnswerGcm) {
ASSERT_TRUE(ac);
EXPECT_FALSE(vc);
EXPECT_EQ(MediaProtocolType::kRtp, ac->type);
- const AudioContentDescription* acd = ac->media_description()->as_audio();
+ const MediaContentDescription* acd = ac->media_description();
EXPECT_EQ(MEDIA_TYPE_AUDIO, acd->type());
EXPECT_THAT(acd->codecs(), ElementsAreArray(kAudioCodecsAnswer));
EXPECT_EQ(0U, acd->first_ssrc()); // no sender is attached
EXPECT_EQ(kAutoBandwidth, acd->bandwidth()); // negotiated auto bw
EXPECT_TRUE(acd->rtcp_mux()); // negotiated rtcp-mux
ASSERT_CRYPTO(acd, 1U, kDefaultSrtpCryptoSuiteGcm);
- EXPECT_EQ(cricket::kMediaProtocolSavpf, acd->protocol());
+ EXPECT_EQ(kMediaProtocolSavpf, acd->protocol());
}
// Create an audio answer with no common codecs, and ensure it is rejected.
@@ -1396,10 +1350,10 @@ TEST_F(MediaSessionDescriptionFactoryTest,
AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio",
RtpTransceiverDirection::kSendRecv, kActive,
&opts);
- std::vector f1_codecs = {cricket::CreateAudioCodec(96, "opus", 48000, 1)};
+ std::vector f1_codecs = {CreateAudioCodec(96, "opus", 48000, 1)};
f1_.set_audio_codecs(f1_codecs, f1_codecs);
- std::vector f2_codecs = {cricket::CreateAudioCodec(0, "PCMU", 8000, 1)};
+ std::vector f2_codecs = {CreateAudioCodec(0, "PCMU", 8000, 1)};
f2_.set_audio_codecs(f2_codecs, f2_codecs);
std::unique_ptr<SessionDescription> offer =
@@ -1428,8 +1382,8 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCreateVideoAnswer) {
ASSERT_TRUE(vc);
EXPECT_EQ(MediaProtocolType::kRtp, ac->type);
EXPECT_EQ(MediaProtocolType::kRtp, vc->type);
- const AudioContentDescription* acd = ac->media_description()->as_audio();
- const VideoContentDescription* vcd = vc->media_description()->as_video();
+ const MediaContentDescription* acd = ac->media_description();
+ const MediaContentDescription* vcd = vc->media_description();
EXPECT_EQ(MEDIA_TYPE_AUDIO, acd->type());
EXPECT_THAT(acd->codecs(), ElementsAreArray(kAudioCodecsAnswer));
EXPECT_EQ(kAutoBandwidth, acd->bandwidth()); // negotiated auto bw
@@ -1441,7 +1395,7 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCreateVideoAnswer) {
EXPECT_EQ(0U, vcd->first_ssrc()); // no sender is attached
EXPECT_TRUE(vcd->rtcp_mux()); // negotiated rtcp-mux
ASSERT_CRYPTO(vcd, 1U, kDefaultSrtpCryptoSuite);
- EXPECT_EQ(cricket::kMediaProtocolSavpf, vcd->protocol());
+ EXPECT_EQ(kMediaProtocolSavpf, vcd->protocol());
}
// Create a typical video answer with GCM ciphers enabled, and ensure it
@@ -1469,10 +1423,10 @@ TEST_F(MediaSessionDescriptionFactoryTest,
AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video",
RtpTransceiverDirection::kSendRecv, kActive,
&opts);
- std::vector f1_codecs = {cricket::CreateVideoCodec(96, "H264")};
+ std::vector f1_codecs = {CreateVideoCodec(96, "H264")};
f1_.set_video_codecs(f1_codecs, f1_codecs);
- std::vector f2_codecs = {cricket::CreateVideoCodec(97, "VP8")};
+ std::vector f2_codecs = {CreateVideoCodec(97, "VP8")};
f2_.set_video_codecs(f2_codecs, f2_codecs);
std::unique_ptr<SessionDescription> offer =
@@ -1492,12 +1446,12 @@ TEST_F(MediaSessionDescriptionFactoryTest,
AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video",
RtpTransceiverDirection::kSendRecv, kActive,
&opts);
- std::vector f1_codecs = {cricket::CreateVideoCodec(96, "H264"),
- cricket::CreateVideoCodec(118, "flexfec-03")};
+ std::vector f1_codecs = {CreateVideoCodec(96, "H264"),
+ CreateVideoCodec(118, "flexfec-03")};
f1_.set_video_codecs(f1_codecs, f1_codecs);
- std::vector f2_codecs = {cricket::CreateVideoCodec(97, "VP8"),
- cricket::CreateVideoCodec(118, "flexfec-03")};
+ std::vector f2_codecs = {CreateVideoCodec(97, "VP8"),
+ CreateVideoCodec(118, "flexfec-03")};
f2_.set_video_codecs(f2_codecs, f2_codecs);
std::unique_ptr<SessionDescription> offer =
@@ -1648,7 +1602,7 @@ TEST_F(MediaSessionDescriptionFactoryTest,
const SctpDataContentDescription* dcd_answer =
dc_answer->media_description()->as_sctp();
EXPECT_FALSE(dc_answer->rejected);
- EXPECT_EQ(cricket::kSctpSendBufferSize, dcd_answer->max_message_size());
+ EXPECT_EQ(kSctpSendBufferSize, dcd_answer->max_message_size());
}
// Verifies that the order of the media contents in the offer is preserved in
@@ -1720,7 +1674,7 @@ TEST_F(MediaSessionDescriptionFactoryTest, CreateAnswerToInactiveOffer) {
// Test that the media protocol is RTP/AVPF if DTLS and SDES are disabled.
TEST_F(MediaSessionDescriptionFactoryTest, AudioOfferAnswerWithCryptoDisabled) {
- MediaSessionOptions opts = CreatePlanBMediaSessionOptions();
+ MediaSessionOptions opts = CreateAudioMediaSession();
f1_.set_secure(SEC_DISABLED);
f2_.set_secure(SEC_DISABLED);
tdf1_.set_secure(SEC_DISABLED);
@@ -1731,7 +1685,7 @@ TEST_F(MediaSessionDescriptionFactoryTest, AudioOfferAnswerWithCryptoDisabled) {
const AudioContentDescription* offer_acd =
GetFirstAudioContentDescription(offer.get());
ASSERT_TRUE(offer_acd);
- EXPECT_EQ(cricket::kMediaProtocolAvpf, offer_acd->protocol());
+ EXPECT_EQ(kMediaProtocolAvpf, offer_acd->protocol());
std::unique_ptr<SessionDescription> answer =
f2_.CreateAnswerOrError(offer.get(), opts, nullptr).MoveValue();
@@ -1743,7 +1697,7 @@ TEST_F(MediaSessionDescriptionFactoryTest, AudioOfferAnswerWithCryptoDisabled) {
const AudioContentDescription* answer_acd =
GetFirstAudioContentDescription(answer.get());
ASSERT_TRUE(answer_acd);
- EXPECT_EQ(cricket::kMediaProtocolAvpf, answer_acd->protocol());
+ EXPECT_EQ(kMediaProtocolAvpf, answer_acd->protocol());
}
// Create a video offer and answer and ensure the RTP header extensions
@@ -1886,9 +1840,9 @@ TEST_F(MediaSessionDescriptionFactoryTest,
MediaSessionOptions opts;
AddAudioVideoSections(RtpTransceiverDirection::kRecvOnly, &opts);
- const cricket::RtpHeaderExtensions offered_extensions = {
+ const RtpHeaderExtensions offered_extensions = {
RtpExtension(RtpExtension::kAbsoluteCaptureTimeUri, 7)};
- const cricket::RtpHeaderExtensions local_extensions = {
+ const RtpHeaderExtensions local_extensions = {
RtpExtension(RtpExtension::kTransportSequenceNumberUri, 5)};
SetAudioVideoRtpHeaderExtensions(offered_extensions, offered_extensions,
&opts);
@@ -1910,9 +1864,9 @@ TEST_F(MediaSessionDescriptionFactoryTest,
MediaSessionOptions opts;
AddAudioVideoSections(RtpTransceiverDirection::kRecvOnly, &opts);
- const cricket::RtpHeaderExtensions offered_extensions = {
+ const RtpHeaderExtensions offered_extensions = {
RtpExtension(RtpExtension::kAbsoluteCaptureTimeUri, 7)};
- const cricket::RtpHeaderExtensions local_extensions = {
+ const RtpHeaderExtensions local_extensions = {
RtpExtension(RtpExtension::kAbsoluteCaptureTimeUri, 5)};
SetAudioVideoRtpHeaderExtensions(offered_extensions, offered_extensions,
&opts);
@@ -1934,9 +1888,9 @@ TEST_F(MediaSessionDescriptionFactoryTest,
MediaSessionOptions opts;
AddAudioVideoSections(RtpTransceiverDirection::kRecvOnly, &opts);
- const cricket::RtpHeaderExtensions offered_extensions = {
+ const RtpHeaderExtensions offered_extensions = {
RtpExtension(RtpExtension::kTransportSequenceNumberUri, 7)};
- const cricket::RtpHeaderExtensions local_extensions = {
+ const RtpHeaderExtensions local_extensions = {
RtpExtension(RtpExtension::kAbsoluteCaptureTimeUri, 5)};
SetAudioVideoRtpHeaderExtensions(offered_extensions, offered_extensions,
&opts);
@@ -2257,8 +2211,8 @@ TEST_F(MediaSessionDescriptionFactoryTest,
const ContentInfo* vc = answer->GetContentByName("video");
ASSERT_TRUE(ac);
ASSERT_TRUE(vc);
- const AudioContentDescription* acd = ac->media_description()->as_audio();
- const VideoContentDescription* vcd = vc->media_description()->as_video();
+ const MediaContentDescription* acd = ac->media_description();
+ const MediaContentDescription* vcd = vc->media_description();
EXPECT_FALSE(acd->has_ssrcs()); // No StreamParams.
EXPECT_FALSE(vcd->has_ssrcs()); // No StreamParams.
@@ -2523,8 +2477,8 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCreateMultiStreamVideoOffer) {
const ContentInfo* vc = offer->GetContentByName("video");
ASSERT_TRUE(ac);
ASSERT_TRUE(vc);
- const AudioContentDescription* acd = ac->media_description()->as_audio();
- const VideoContentDescription* vcd = vc->media_description()->as_video();
+ const MediaContentDescription* acd = ac->media_description();
+ const MediaContentDescription* vcd = vc->media_description();
EXPECT_EQ(MEDIA_TYPE_AUDIO, acd->type());
EXPECT_EQ(f1_.audio_sendrecv_codecs(), acd->codecs());
@@ -2568,10 +2522,8 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCreateMultiStreamVideoOffer) {
vc = updated_offer->GetContentByName("video");
ASSERT_TRUE(ac);
ASSERT_TRUE(vc);
- const AudioContentDescription* updated_acd =
- ac->media_description()->as_audio();
- const VideoContentDescription* updated_vcd =
- vc->media_description()->as_video();
+ const MediaContentDescription* updated_acd = ac->media_description();
+ const MediaContentDescription* updated_vcd = vc->media_description();
EXPECT_EQ(acd->type(), updated_acd->type());
EXPECT_EQ(acd->codecs(), updated_acd->codecs());
@@ -2616,24 +2568,24 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCreateSimulcastVideoOffer) {
ASSERT_TRUE(offer.get());
const ContentInfo* vc = offer->GetContentByName("video");
ASSERT_TRUE(vc);
- const VideoContentDescription* vcd = vc->media_description()->as_video();
+ const MediaContentDescription* vcd = vc->media_description();
const StreamParamsVec& video_streams = vcd->streams();
ASSERT_EQ(1U, video_streams.size());
EXPECT_EQ(kVideoTrack1, video_streams[0].id);
const SsrcGroup* sim_ssrc_group =
- video_streams[0].get_ssrc_group(cricket::kSimSsrcGroupSemantics);
+ video_streams[0].get_ssrc_group(kSimSsrcGroupSemantics);
ASSERT_TRUE(sim_ssrc_group);
EXPECT_EQ(static_cast<size_t>(num_sim_layers), sim_ssrc_group->ssrcs.size());
}
MATCHER(RidDescriptionEquals, "Verifies that two RidDescriptions are equal.") {
- const RidDescription& rid1 = ::testing::get<0>(arg);
- const RidDescription& rid2 = ::testing::get<1>(arg);
+ const RidDescription& rid1 = std::get<0>(arg);
+ const RidDescription& rid2 = std::get<1>(arg);
return rid1.rid == rid2.rid && rid1.direction == rid2.direction;
}
-static void CheckSimulcastInSessionDescription(
+void CheckSimulcastInSessionDescription(
const SessionDescription* description,
const std::string& content_name,
const std::vector<RidDescription>& send_rids,
@@ -2828,8 +2780,8 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCreateMultiStreamVideoAnswer) {
const ContentInfo* vc = answer->GetContentByName("video");
ASSERT_TRUE(ac);
ASSERT_TRUE(vc);
- const AudioContentDescription* acd = ac->media_description()->as_audio();
- const VideoContentDescription* vcd = vc->media_description()->as_video();
+ const MediaContentDescription* acd = ac->media_description();
+ const MediaContentDescription* vcd = vc->media_description();
ASSERT_CRYPTO(acd, 1U, kDefaultSrtpCryptoSuite);
ASSERT_CRYPTO(vcd, 1U, kDefaultSrtpCryptoSuite);
@@ -2873,10 +2825,8 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCreateMultiStreamVideoAnswer) {
vc = updated_answer->GetContentByName("video");
ASSERT_TRUE(ac);
ASSERT_TRUE(vc);
- const AudioContentDescription* updated_acd =
- ac->media_description()->as_audio();
- const VideoContentDescription* updated_vcd =
- vc->media_description()->as_video();
+ const MediaContentDescription* updated_acd = ac->media_description();
+ const MediaContentDescription* updated_vcd = vc->media_description();
ASSERT_CRYPTO(updated_acd, 1U, kDefaultSrtpCryptoSuite);
EXPECT_TRUE(CompareCryptoParams(acd->cryptos(), updated_acd->cryptos()));
@@ -3082,14 +3032,12 @@ TEST_F(MediaSessionDescriptionFactoryTest,
&opts);
std::vector<VideoCodec> f1_codecs = MAKE_VECTOR(kVideoCodecs1);
// This creates rtx for H264 with the payload type `f1_` uses.
- AddRtxCodec(cricket::CreateVideoRtxCodec(126, kVideoCodecs1[1].id),
- &f1_codecs);
+ AddRtxCodec(CreateVideoRtxCodec(126, kVideoCodecs1[1].id), &f1_codecs);
f1_.set_video_codecs(f1_codecs, f1_codecs);
std::vector<VideoCodec> f2_codecs = MAKE_VECTOR(kVideoCodecs2);
// This creates rtx for H264 with the payload type `f2_` uses.
- AddRtxCodec(cricket::CreateVideoRtxCodec(125, kVideoCodecs2[0].id),
- &f2_codecs);
+ AddRtxCodec(CreateVideoRtxCodec(125, kVideoCodecs2[0].id), &f2_codecs);
f2_.set_video_codecs(f2_codecs, f2_codecs);
std::unique_ptr<SessionDescription> offer =
@@ -3102,8 +3050,7 @@ TEST_F(MediaSessionDescriptionFactoryTest,
GetFirstVideoContentDescription(answer.get());
std::vector<VideoCodec> expected_codecs = MAKE_VECTOR(kVideoCodecsAnswer);
- AddRtxCodec(cricket::CreateVideoRtxCodec(126, kVideoCodecs1[1].id),
- &expected_codecs);
+ AddRtxCodec(CreateVideoRtxCodec(126, kVideoCodecs1[1].id), &expected_codecs);
EXPECT_EQ(expected_codecs, vcd->codecs());
@@ -3136,14 +3083,12 @@ TEST_F(MediaSessionDescriptionFactoryTest,
&opts);
// We specifically choose different preferred payload types for VP8 to
// trigger the issue.
- cricket::VideoCodec vp8_offerer = cricket::CreateVideoCodec(100, "VP8");
- cricket::VideoCodec vp8_offerer_rtx =
- cricket::CreateVideoRtxCodec(101, vp8_offerer.id);
- cricket::VideoCodec vp8_answerer = cricket::CreateVideoCodec(110, "VP8");
- cricket::VideoCodec vp8_answerer_rtx =
- cricket::CreateVideoRtxCodec(111, vp8_answerer.id);
- cricket::VideoCodec vp9 = cricket::CreateVideoCodec(120, "VP9");
- cricket::VideoCodec vp9_rtx = cricket::CreateVideoRtxCodec(121, vp9.id);
+ VideoCodec vp8_offerer = CreateVideoCodec(100, "VP8");
+ VideoCodec vp8_offerer_rtx = CreateVideoRtxCodec(101, vp8_offerer.id);
+ VideoCodec vp8_answerer = CreateVideoCodec(110, "VP8");
+ VideoCodec vp8_answerer_rtx = CreateVideoRtxCodec(111, vp8_answerer.id);
+ VideoCodec vp9 = CreateVideoCodec(120, "VP9");
+ VideoCodec vp9_rtx = CreateVideoRtxCodec(121, vp9.id);
std::vector<VideoCodec> f1_codecs = {vp8_offerer, vp8_offerer_rtx};
// We also specifically cause the answerer to prefer VP9, such that if it
@@ -3171,7 +3116,7 @@ TEST_F(MediaSessionDescriptionFactoryTest,
const VideoContentDescription* vcd =
GetFirstVideoContentDescription(updated_offer.get());
- std::vector<cricket::VideoCodec> codecs = vcd->codecs();
+ std::vector<VideoCodec> codecs = vcd->codecs();
ASSERT_EQ(4u, codecs.size());
EXPECT_EQ(vp8_offerer, codecs[0]);
EXPECT_EQ(vp8_offerer_rtx, codecs[1]);
@@ -3187,8 +3132,7 @@ TEST_F(MediaSessionDescriptionFactoryTest,
RespondentCreatesOfferWithVideoAndRtxAfterCreatingAudioAnswer) {
std::vector<VideoCodec> f1_codecs = MAKE_VECTOR(kVideoCodecs1);
// This creates rtx for H264 with the payload type `f1_` uses.
- AddRtxCodec(cricket::CreateVideoRtxCodec(126, kVideoCodecs1[1].id),
- &f1_codecs);
+ AddRtxCodec(CreateVideoRtxCodec(126, kVideoCodecs1[1].id), &f1_codecs);
f1_.set_video_codecs(f1_codecs, f1_codecs);
MediaSessionOptions opts;
@@ -3214,7 +3158,7 @@ TEST_F(MediaSessionDescriptionFactoryTest,
std::vector<VideoCodec> f2_codecs = MAKE_VECTOR(kVideoCodecs2);
int used_pl_type = acd->codecs()[0].id;
f2_codecs[0].id = used_pl_type; // Set the payload type for H264.
- AddRtxCodec(cricket::CreateVideoRtxCodec(125, used_pl_type), &f2_codecs);
+ AddRtxCodec(CreateVideoRtxCodec(125, used_pl_type), &f2_codecs);
f2_.set_video_codecs(f2_codecs, f2_codecs);
std::unique_ptr<SessionDescription> updated_offer(
@@ -3232,12 +3176,12 @@ TEST_F(MediaSessionDescriptionFactoryTest,
GetFirstVideoContentDescription(updated_answer.get());
ASSERT_EQ("H264", updated_vcd->codecs()[0].name);
- ASSERT_EQ(cricket::kRtxCodecName, updated_vcd->codecs()[1].name);
+ ASSERT_EQ(kRtxCodecName, updated_vcd->codecs()[1].name);
int new_h264_pl_type = updated_vcd->codecs()[0].id;
EXPECT_NE(used_pl_type, new_h264_pl_type);
VideoCodec rtx = updated_vcd->codecs()[1];
- int pt_referenced_by_rtx = rtc::FromString<int>(
- rtx.params[cricket::kCodecParamAssociatedPayloadType]);
+ int pt_referenced_by_rtx =
+ rtc::FromString<int>(rtx.params[kCodecParamAssociatedPayloadType]);
EXPECT_EQ(new_h264_pl_type, pt_referenced_by_rtx);
}
@@ -3251,8 +3195,7 @@ TEST_F(MediaSessionDescriptionFactoryTest,
std::vector<VideoCodec> f2_codecs = MAKE_VECTOR(kVideoCodecs2);
// This creates rtx for H264 with the payload type `f2_` uses.
- AddRtxCodec(cricket::CreateVideoRtxCodec(125, kVideoCodecs2[0].id),
- &f2_codecs);
+ AddRtxCodec(CreateVideoRtxCodec(125, kVideoCodecs2[0].id), &f2_codecs);
f2_.set_video_codecs(f2_codecs, f2_codecs);
std::unique_ptr<SessionDescription> offer =
@@ -3279,8 +3222,7 @@ TEST_F(MediaSessionDescriptionFactoryTest,
// New offer should attempt to add H263, and RTX for H264.
expected_codecs.push_back(kVideoCodecs2[1]);
- AddRtxCodec(cricket::CreateVideoRtxCodec(125, kVideoCodecs1[1].id),
- &expected_codecs);
+ AddRtxCodec(CreateVideoRtxCodec(125, kVideoCodecs1[1].id), &expected_codecs);
EXPECT_EQ(expected_codecs, updated_vcd->codecs());
}
@@ -3292,14 +3234,12 @@ TEST_F(MediaSessionDescriptionFactoryTest, RtxWithoutApt) {
&opts);
std::vector<VideoCodec> f1_codecs = MAKE_VECTOR(kVideoCodecs1);
// This creates RTX without associated payload type parameter.
- AddRtxCodec(cricket::CreateVideoCodec(126, cricket::kRtxCodecName),
- &f1_codecs);
+ AddRtxCodec(CreateVideoCodec(126, kRtxCodecName), &f1_codecs);
f1_.set_video_codecs(f1_codecs, f1_codecs);
std::vector<VideoCodec> f2_codecs = MAKE_VECTOR(kVideoCodecs2);
// This creates RTX for H264 with the payload type `f2_` uses.
- AddRtxCodec(cricket::CreateVideoRtxCodec(125, kVideoCodecs2[0].id),
- &f2_codecs);
+ AddRtxCodec(CreateVideoRtxCodec(125, kVideoCodecs2[0].id), &f2_codecs);
f2_.set_video_codecs(f2_codecs, f2_codecs);
std::unique_ptr<SessionDescription> offer =
@@ -3310,23 +3250,22 @@ TEST_F(MediaSessionDescriptionFactoryTest, RtxWithoutApt) {
// is possible to test that that RTX is dropped when
// kCodecParamAssociatedPayloadType is missing in the offer.
MediaContentDescription* media_desc =
- offer->GetContentDescriptionByName(cricket::CN_VIDEO);
+ offer->GetContentDescriptionByName(CN_VIDEO);
ASSERT_TRUE(media_desc);
- VideoContentDescription* desc = media_desc->as_video();
- std::vector<VideoCodec> codecs = desc->codecs();
- for (VideoCodec& codec : codecs) {
- if (absl::StartsWith(codec.name, cricket::kRtxCodecName)) {
+ std::vector<Codec> codecs = media_desc->codecs();
+ for (Codec& codec : codecs) {
+ if (absl::StartsWith(codec.name, kRtxCodecName)) {
codec.params.clear();
}
}
- desc->set_codecs(codecs);
+ media_desc->set_codecs(codecs);
std::unique_ptr<SessionDescription> answer =
f2_.CreateAnswerOrError(offer.get(), opts, nullptr).MoveValue();
EXPECT_THAT(
GetCodecNames(GetFirstVideoContentDescription(answer.get())->codecs()),
- Not(Contains(cricket::kRtxCodecName)));
+ Not(Contains(kRtxCodecName)));
}
// Test that RTX will be filtered out in the answer if its associated payload
@@ -3338,14 +3277,12 @@ TEST_F(MediaSessionDescriptionFactoryTest, FilterOutRtxIfAptDoesntMatch) {
&opts);
std::vector<VideoCodec> f1_codecs = MAKE_VECTOR(kVideoCodecs1);
// This creates RTX for H264 in sender.
- AddRtxCodec(cricket::CreateVideoRtxCodec(126, kVideoCodecs1[1].id),
- &f1_codecs);
+ AddRtxCodec(CreateVideoRtxCodec(126, kVideoCodecs1[1].id), &f1_codecs);
f1_.set_video_codecs(f1_codecs, f1_codecs);
std::vector<VideoCodec> f2_codecs = MAKE_VECTOR(kVideoCodecs2);
// This creates RTX for H263 in receiver.
- AddRtxCodec(cricket::CreateVideoRtxCodec(125, kVideoCodecs2[1].id),
- &f2_codecs);
+ AddRtxCodec(CreateVideoRtxCodec(125, kVideoCodecs2[1].id), &f2_codecs);
f2_.set_video_codecs(f2_codecs, f2_codecs);
std::unique_ptr<SessionDescription> offer =
@@ -3358,7 +3295,7 @@ TEST_F(MediaSessionDescriptionFactoryTest, FilterOutRtxIfAptDoesntMatch) {
EXPECT_THAT(
GetCodecNames(GetFirstVideoContentDescription(answer.get())->codecs()),
- Not(Contains(cricket::kRtxCodecName)));
+ Not(Contains(kRtxCodecName)));
}
// Test that when multiple RTX codecs are offered, only the matched RTX codec
@@ -3371,19 +3308,16 @@ TEST_F(MediaSessionDescriptionFactoryTest,
&opts);
std::vector<VideoCodec> f1_codecs = MAKE_VECTOR(kVideoCodecs1);
// This creates RTX for H264-SVC in sender.
- AddRtxCodec(cricket::CreateVideoRtxCodec(125, kVideoCodecs1[0].id),
- &f1_codecs);
+ AddRtxCodec(CreateVideoRtxCodec(125, kVideoCodecs1[0].id), &f1_codecs);
f1_.set_video_codecs(f1_codecs, f1_codecs);
// This creates RTX for H264 in sender.
- AddRtxCodec(cricket::CreateVideoRtxCodec(126, kVideoCodecs1[1].id),
- &f1_codecs);
+ AddRtxCodec(CreateVideoRtxCodec(126, kVideoCodecs1[1].id), &f1_codecs);
f1_.set_video_codecs(f1_codecs, f1_codecs);
std::vector<VideoCodec> f2_codecs = MAKE_VECTOR(kVideoCodecs2);
// This creates RTX for H264 in receiver.
- AddRtxCodec(cricket::CreateVideoRtxCodec(124, kVideoCodecs2[0].id),
- &f2_codecs);
+ AddRtxCodec(CreateVideoRtxCodec(124, kVideoCodecs2[0].id), &f2_codecs);
f2_.set_video_codecs(f2_codecs, f1_codecs);
// H264-SVC codec is removed in the answer, therefore, associated RTX codec
@@ -3396,8 +3330,7 @@ TEST_F(MediaSessionDescriptionFactoryTest,
const VideoContentDescription* vcd =
GetFirstVideoContentDescription(answer.get());
std::vector<VideoCodec> expected_codecs = MAKE_VECTOR(kVideoCodecsAnswer);
- AddRtxCodec(cricket::CreateVideoRtxCodec(126, kVideoCodecs1[1].id),
- &expected_codecs);
+ AddRtxCodec(CreateVideoRtxCodec(126, kVideoCodecs1[1].id), &expected_codecs);
EXPECT_EQ(expected_codecs, vcd->codecs());
}
@@ -3411,8 +3344,7 @@ TEST_F(MediaSessionDescriptionFactoryTest, AddSecondRtxInNewOffer) {
&opts);
std::vector<VideoCodec> f1_codecs = MAKE_VECTOR(kVideoCodecs1);
// This creates RTX for H264 for the offerer.
- AddRtxCodec(cricket::CreateVideoRtxCodec(126, kVideoCodecs1[1].id),
- &f1_codecs);
+ AddRtxCodec(CreateVideoRtxCodec(126, kVideoCodecs1[1].id), &f1_codecs);
f1_.set_video_codecs(f1_codecs, f1_codecs);
std::unique_ptr<SessionDescription> offer =
@@ -3422,13 +3354,11 @@ TEST_F(MediaSessionDescriptionFactoryTest, AddSecondRtxInNewOffer) {
GetFirstVideoContentDescription(offer.get());
std::vector<VideoCodec> expected_codecs = MAKE_VECTOR(kVideoCodecs1);
- AddRtxCodec(cricket::CreateVideoRtxCodec(126, kVideoCodecs1[1].id),
- &expected_codecs);
+ AddRtxCodec(CreateVideoRtxCodec(126, kVideoCodecs1[1].id), &expected_codecs);
EXPECT_EQ(expected_codecs, vcd->codecs());
// Now, attempt to add RTX for H264-SVC.
- AddRtxCodec(cricket::CreateVideoRtxCodec(125, kVideoCodecs1[0].id),
- &f1_codecs);
+ AddRtxCodec(CreateVideoRtxCodec(125, kVideoCodecs1[0].id), &f1_codecs);
f1_.set_video_codecs(f1_codecs, f1_codecs);
std::unique_ptr<SessionDescription> updated_offer(
@@ -3436,8 +3366,7 @@ TEST_F(MediaSessionDescriptionFactoryTest, AddSecondRtxInNewOffer) {
ASSERT_TRUE(updated_offer);
vcd = GetFirstVideoContentDescription(updated_offer.get());
- AddRtxCodec(cricket::CreateVideoRtxCodec(125, kVideoCodecs1[0].id),
- &expected_codecs);
+ AddRtxCodec(CreateVideoRtxCodec(125, kVideoCodecs1[0].id), &expected_codecs);
EXPECT_EQ(expected_codecs, vcd->codecs());
}
@@ -3454,8 +3383,8 @@ TEST_F(MediaSessionDescriptionFactoryTest, SimSsrcsGenerateMultipleRtxSsrcs) {
// Use a single real codec, and then add RTX for it.
std::vector<VideoCodec> f1_codecs;
- f1_codecs.push_back(cricket::CreateVideoCodec(97, "H264"));
- AddRtxCodec(cricket::CreateVideoRtxCodec(125, 97), &f1_codecs);
+ f1_codecs.push_back(CreateVideoCodec(97, "H264"));
+ AddRtxCodec(CreateVideoRtxCodec(125, 97), &f1_codecs);
f1_.set_video_codecs(f1_codecs, f1_codecs);
// Ensure that the offer has an RTX ssrc for each regular ssrc, and that there
@@ -3464,10 +3393,9 @@ TEST_F(MediaSessionDescriptionFactoryTest, SimSsrcsGenerateMultipleRtxSsrcs) {
f1_.CreateOfferOrError(opts, nullptr).MoveValue();
ASSERT_TRUE(offer.get());
MediaContentDescription* media_desc =
- offer->GetContentDescriptionByName(cricket::CN_VIDEO);
+ offer->GetContentDescriptionByName(CN_VIDEO);
ASSERT_TRUE(media_desc);
- VideoContentDescription* desc = media_desc->as_video();
- const StreamParamsVec& streams = desc->streams();
+ const StreamParamsVec& streams = media_desc->streams();
// Single stream.
ASSERT_EQ(1u, streams.size());
// Stream should have 6 ssrcs: 3 for video, 3 for RTX.
@@ -3499,8 +3427,8 @@ TEST_F(MediaSessionDescriptionFactoryTest, GenerateFlexfecSsrc) {
// Use a single real codec, and then add FlexFEC for it.
std::vector<VideoCodec> f1_codecs;
- f1_codecs.push_back(cricket::CreateVideoCodec(97, "H264"));
- f1_codecs.push_back(cricket::CreateVideoCodec(118, "flexfec-03"));
+ f1_codecs.push_back(CreateVideoCodec(97, "H264"));
+ f1_codecs.push_back(CreateVideoCodec(118, "flexfec-03"));
f1_.set_video_codecs(f1_codecs, f1_codecs);
// Ensure that the offer has a single FlexFEC ssrc and that
@@ -3509,10 +3437,9 @@ TEST_F(MediaSessionDescriptionFactoryTest, GenerateFlexfecSsrc) {
f1_.CreateOfferOrError(opts, nullptr).MoveValue();
ASSERT_TRUE(offer.get());
MediaContentDescription* media_desc =
- offer->GetContentDescriptionByName(cricket::CN_VIDEO);
+ offer->GetContentDescriptionByName(CN_VIDEO);
ASSERT_TRUE(media_desc);
- VideoContentDescription* desc = media_desc->as_video();
- const StreamParamsVec& streams = desc->streams();
+ const StreamParamsVec& streams = media_desc->streams();
// Single stream.
ASSERT_EQ(1u, streams.size());
// Stream should have 2 ssrcs: 1 for video, 1 for FlexFEC.
@@ -3543,8 +3470,8 @@ TEST_F(MediaSessionDescriptionFactoryTest, SimSsrcsGenerateNoFlexfecSsrcs) {
// Use a single real codec, and then add FlexFEC for it.
std::vector<VideoCodec> f1_codecs;
- f1_codecs.push_back(cricket::CreateVideoCodec(97, "H264"));
- f1_codecs.push_back(cricket::CreateVideoCodec(118, "flexfec-03"));
+ f1_codecs.push_back(CreateVideoCodec(97, "H264"));
+ f1_codecs.push_back(CreateVideoCodec(118, "flexfec-03"));
f1_.set_video_codecs(f1_codecs, f1_codecs);
// Ensure that the offer has no FlexFEC ssrcs for each regular ssrc, and that
@@ -3553,10 +3480,9 @@ TEST_F(MediaSessionDescriptionFactoryTest, SimSsrcsGenerateNoFlexfecSsrcs) {
f1_.CreateOfferOrError(opts, nullptr).MoveValue();
ASSERT_TRUE(offer.get());
MediaContentDescription* media_desc =
- offer->GetContentDescriptionByName(cricket::CN_VIDEO);
+ offer->GetContentDescriptionByName(CN_VIDEO);
ASSERT_TRUE(media_desc);
- VideoContentDescription* desc = media_desc->as_video();
- const StreamParamsVec& streams = desc->streams();
+ const StreamParamsVec& streams = media_desc->streams();
// Single stream.
ASSERT_EQ(1u, streams.size());
// Stream should have 3 ssrcs: 3 for video, 0 for FlexFEC.
@@ -3704,33 +3630,33 @@ TEST_F(MediaSessionDescriptionFactoryTest, RtpExtensionIdReusedEncrypted) {
TEST(MediaSessionDescription, CopySessionDescription) {
SessionDescription source;
- cricket::ContentGroup group(cricket::CN_AUDIO);
+ ContentGroup group(CN_AUDIO);
source.AddGroup(group);
std::unique_ptr<AudioContentDescription> acd =
std::make_unique<AudioContentDescription>();
acd->set_codecs(MAKE_VECTOR(kAudioCodecs1));
acd->AddLegacyStream(1);
- source.AddContent(cricket::CN_AUDIO, MediaProtocolType::kRtp, acd->Clone());
+ source.AddContent(CN_AUDIO, MediaProtocolType::kRtp, acd->Clone());
std::unique_ptr<VideoContentDescription> vcd =
std::make_unique<VideoContentDescription>();
vcd->set_codecs(MAKE_VECTOR(kVideoCodecs1));
vcd->AddLegacyStream(2);
- source.AddContent(cricket::CN_VIDEO, MediaProtocolType::kRtp, vcd->Clone());
+ source.AddContent(CN_VIDEO, MediaProtocolType::kRtp, vcd->Clone());
std::unique_ptr<SessionDescription> copy = source.Clone();
ASSERT_TRUE(copy.get());
- EXPECT_TRUE(copy->HasGroup(cricket::CN_AUDIO));
+ EXPECT_TRUE(copy->HasGroup(CN_AUDIO));
const ContentInfo* ac = copy->GetContentByName("audio");
const ContentInfo* vc = copy->GetContentByName("video");
ASSERT_TRUE(ac);
ASSERT_TRUE(vc);
EXPECT_EQ(MediaProtocolType::kRtp, ac->type);
- const AudioContentDescription* acd_copy = ac->media_description()->as_audio();
+ const MediaContentDescription* acd_copy = ac->media_description();
EXPECT_EQ(acd->codecs(), acd_copy->codecs());
EXPECT_EQ(1u, acd->first_ssrc());
EXPECT_EQ(MediaProtocolType::kRtp, vc->type);
- const VideoContentDescription* vcd_copy = vc->media_description()->as_video();
+ const MediaContentDescription* vcd_copy = vc->media_description();
EXPECT_EQ(vcd->codecs(), vcd_copy->codecs());
EXPECT_EQ(2u, vcd->first_ssrc());
}
@@ -3870,18 +3796,16 @@ TEST_F(MediaSessionDescriptionFactoryTest,
tdf2_.set_secure(SEC_DISABLED);
std::unique_ptr<SessionDescription> offer =
- f1_.CreateOfferOrError(CreatePlanBMediaSessionOptions(), nullptr)
- .MoveValue();
+ f1_.CreateOfferOrError(CreateAudioMediaSession(), nullptr).MoveValue();
ASSERT_TRUE(offer.get());
ContentInfo* offer_content = offer->GetContentByName("audio");
ASSERT_TRUE(offer_content);
- AudioContentDescription* offer_audio_desc =
- offer_content->media_description()->as_audio();
- offer_audio_desc->set_protocol(cricket::kMediaProtocolDtlsSavpf);
+ MediaContentDescription* offer_audio_desc =
+ offer_content->media_description();
+ offer_audio_desc->set_protocol(kMediaProtocolDtlsSavpf);
std::unique_ptr<SessionDescription> answer =
- f2_.CreateAnswerOrError(offer.get(), CreatePlanBMediaSessionOptions(),
- nullptr)
+ f2_.CreateAnswerOrError(offer.get(), CreateAudioMediaSession(), nullptr)
.MoveValue();
ASSERT_TRUE(answer);
ContentInfo* answer_content = answer->GetContentByName("audio");
@@ -3899,18 +3823,16 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestOfferDtlsSavpfCreateAnswer) {
tdf2_.set_secure(SEC_ENABLED);
std::unique_ptr<SessionDescription> offer =
- f1_.CreateOfferOrError(CreatePlanBMediaSessionOptions(), nullptr)
- .MoveValue();
+ f1_.CreateOfferOrError(CreateAudioMediaSession(), nullptr).MoveValue();
ASSERT_TRUE(offer.get());
ContentInfo* offer_content = offer->GetContentByName("audio");
ASSERT_TRUE(offer_content);
- AudioContentDescription* offer_audio_desc =
- offer_content->media_description()->as_audio();
- offer_audio_desc->set_protocol(cricket::kMediaProtocolDtlsSavpf);
+ MediaContentDescription* offer_audio_desc =
+ offer_content->media_description();
+ offer_audio_desc->set_protocol(kMediaProtocolDtlsSavpf);
std::unique_ptr<SessionDescription> answer =
- f2_.CreateAnswerOrError(offer.get(), CreatePlanBMediaSessionOptions(),
- nullptr)
+ f2_.CreateAnswerOrError(offer.get(), CreateAudioMediaSession(), nullptr)
.MoveValue();
ASSERT_TRUE(answer);
@@ -3918,9 +3840,9 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestOfferDtlsSavpfCreateAnswer) {
ASSERT_TRUE(answer_content);
ASSERT_FALSE(answer_content->rejected);
- const AudioContentDescription* answer_audio_desc =
- answer_content->media_description()->as_audio();
- EXPECT_EQ(cricket::kMediaProtocolDtlsSavpf, answer_audio_desc->protocol());
+ const MediaContentDescription* answer_audio_desc =
+ answer_content->media_description();
+ EXPECT_EQ(kMediaProtocolDtlsSavpf, answer_audio_desc->protocol());
}
// Test that we include both SDES and DTLS in the offer, but only include SDES
@@ -3933,10 +3855,10 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCryptoDtls) {
MediaSessionOptions options;
AddAudioVideoSections(RtpTransceiverDirection::kRecvOnly, &options);
std::unique_ptr<SessionDescription> offer, answer;
- const cricket::MediaContentDescription* audio_media_desc;
- const cricket::MediaContentDescription* video_media_desc;
- const cricket::TransportDescription* audio_trans_desc;
- const cricket::TransportDescription* video_trans_desc;
+ const MediaContentDescription* audio_media_desc;
+ const MediaContentDescription* video_media_desc;
+ const TransportDescription* audio_trans_desc;
+ const TransportDescription* video_trans_desc;
// Generate an offer with SDES and DTLS support.
offer = f1_.CreateOfferOrError(options, nullptr).MoveValue();
@@ -3985,8 +3907,8 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCryptoDtls) {
ASSERT_TRUE(video_media_desc);
EXPECT_TRUE(audio_media_desc->cryptos().empty());
EXPECT_TRUE(video_media_desc->cryptos().empty());
- EXPECT_EQ(cricket::kMediaProtocolSavpf, audio_media_desc->protocol());
- EXPECT_EQ(cricket::kMediaProtocolSavpf, video_media_desc->protocol());
+ EXPECT_EQ(kMediaProtocolSavpf, audio_media_desc->protocol());
+ EXPECT_EQ(kMediaProtocolSavpf, video_media_desc->protocol());
audio_trans_desc = answer->GetTransportDescriptionByName("audio");
ASSERT_TRUE(audio_trans_desc);
@@ -4017,7 +3939,7 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCryptoDtls) {
// Test that an answer can't be created if cryptos are required but the offer is
// unsecure.
TEST_F(MediaSessionDescriptionFactoryTest, TestSecureAnswerToUnsecureOffer) {
- MediaSessionOptions options = CreatePlanBMediaSessionOptions();
+ MediaSessionOptions options = CreateAudioMediaSession();
f1_.set_secure(SEC_DISABLED);
tdf1_.set_secure(SEC_DISABLED);
f2_.set_secure(SEC_REQUIRED);
@@ -4053,10 +3975,10 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCryptoOfferDtlsButNotSdes) {
GetFirstVideoContentDescription(offer.get());
ASSERT_TRUE(video_offer->cryptos().empty());
- const cricket::TransportDescription* audio_offer_trans_desc =
+ const TransportDescription* audio_offer_trans_desc =
offer->GetTransportDescriptionByName("audio");
ASSERT_TRUE(audio_offer_trans_desc->identity_fingerprint.get());
- const cricket::TransportDescription* video_offer_trans_desc =
+ const TransportDescription* video_offer_trans_desc =
offer->GetTransportDescriptionByName("video");
ASSERT_TRUE(video_offer_trans_desc->identity_fingerprint.get());
@@ -4065,10 +3987,10 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCryptoOfferDtlsButNotSdes) {
f2_.CreateAnswerOrError(offer.get(), options, nullptr).MoveValue();
ASSERT_TRUE(answer.get());
- const cricket::TransportDescription* audio_answer_trans_desc =
+ const TransportDescription* audio_answer_trans_desc =
answer->GetTransportDescriptionByName("audio");
EXPECT_TRUE(audio_answer_trans_desc->identity_fingerprint.get());
- const cricket::TransportDescription* video_answer_trans_desc =
+ const TransportDescription* video_answer_trans_desc =
answer->GetTransportDescriptionByName("video");
EXPECT_TRUE(video_answer_trans_desc->identity_fingerprint.get());
}
@@ -4160,27 +4082,25 @@ TEST_F(MediaSessionDescriptionFactoryTest,
ASSERT_EQ(4u, offer->contents().size());
EXPECT_FALSE(offer->contents()[0].rejected);
- const AudioContentDescription* acd =
- offer->contents()[0].media_description()->as_audio();
+ const MediaContentDescription* acd = offer->contents()[0].media_description();
ASSERT_EQ(1u, acd->streams().size());
EXPECT_EQ(kAudioTrack1, acd->streams()[0].id);
EXPECT_EQ(RtpTransceiverDirection::kSendRecv, acd->direction());
EXPECT_FALSE(offer->contents()[1].rejected);
- const VideoContentDescription* vcd =
- offer->contents()[1].media_description()->as_video();
+ const MediaContentDescription* vcd = offer->contents()[1].media_description();
ASSERT_EQ(1u, vcd->streams().size());
EXPECT_EQ(kVideoTrack1, vcd->streams()[0].id);
EXPECT_EQ(RtpTransceiverDirection::kSendRecv, vcd->direction());
EXPECT_FALSE(offer->contents()[2].rejected);
- acd = offer->contents()[2].media_description()->as_audio();
+ acd = offer->contents()[2].media_description();
ASSERT_EQ(1u, acd->streams().size());
EXPECT_EQ(kAudioTrack2, acd->streams()[0].id);
EXPECT_EQ(RtpTransceiverDirection::kSendRecv, acd->direction());
EXPECT_FALSE(offer->contents()[3].rejected);
- vcd = offer->contents()[3].media_description()->as_video();
+ vcd = offer->contents()[3].media_description();
ASSERT_EQ(1u, vcd->streams().size());
EXPECT_EQ(kVideoTrack2, vcd->streams()[0].id);
EXPECT_EQ(RtpTransceiverDirection::kSendRecv, vcd->direction());
@@ -4223,27 +4143,27 @@ TEST_F(MediaSessionDescriptionFactoryTest,
ASSERT_EQ(4u, answer->contents().size());
EXPECT_FALSE(answer->contents()[0].rejected);
- const AudioContentDescription* acd =
- answer->contents()[0].media_description()->as_audio();
+ const MediaContentDescription* acd =
+ answer->contents()[0].media_description();
ASSERT_EQ(1u, acd->streams().size());
EXPECT_EQ(kAudioTrack1, acd->streams()[0].id);
EXPECT_EQ(RtpTransceiverDirection::kSendRecv, acd->direction());
EXPECT_FALSE(answer->contents()[1].rejected);
- const VideoContentDescription* vcd =
- answer->contents()[1].media_description()->as_video();
+ const MediaContentDescription* vcd =
+ answer->contents()[1].media_description();
ASSERT_EQ(1u, vcd->streams().size());
EXPECT_EQ(kVideoTrack1, vcd->streams()[0].id);
EXPECT_EQ(RtpTransceiverDirection::kSendRecv, vcd->direction());
EXPECT_FALSE(answer->contents()[2].rejected);
- acd = answer->contents()[2].media_description()->as_audio();
+ acd = answer->contents()[2].media_description();
ASSERT_EQ(1u, acd->streams().size());
EXPECT_EQ(kAudioTrack2, acd->streams()[0].id);
EXPECT_EQ(RtpTransceiverDirection::kSendRecv, acd->direction());
EXPECT_FALSE(answer->contents()[3].rejected);
- vcd = answer->contents()[3].media_description()->as_video();
+ vcd = answer->contents()[3].media_description();
ASSERT_EQ(1u, vcd->streams().size());
EXPECT_EQ(kVideoTrack2, vcd->streams()[0].id);
EXPECT_EQ(RtpTransceiverDirection::kSendRecv, vcd->direction());
@@ -4379,10 +4299,10 @@ TEST_F(MediaSessionDescriptionFactoryTest,
f1_.CreateOfferOrError(opts, nullptr).MoveValue();
ASSERT_TRUE(offer);
ASSERT_EQ(2u, offer->contents().size());
- const VideoContentDescription* vcd1 =
- offer->contents()[0].media_description()->as_video();
- const VideoContentDescription* vcd2 =
- offer->contents()[1].media_description()->as_video();
+ const MediaContentDescription* vcd1 =
+ offer->contents()[0].media_description();
+ const MediaContentDescription* vcd2 =
+ offer->contents()[1].media_description();
EXPECT_EQ(vcd1->codecs().size(), vcd2->codecs().size());
ASSERT_EQ(2u, vcd1->codecs().size());
EXPECT_EQ(vcd1->codecs()[0].name, vcd2->codecs()[0].name);
@@ -4395,8 +4315,8 @@ TEST_F(MediaSessionDescriptionFactoryTest,
f2_.CreateAnswerOrError(offer.get(), opts, nullptr).MoveValue();
ASSERT_TRUE(answer);
ASSERT_EQ(2u, answer->contents().size());
- vcd1 = answer->contents()[0].media_description()->as_video();
- vcd2 = answer->contents()[1].media_description()->as_video();
+ vcd1 = answer->contents()[0].media_description();
+ vcd2 = answer->contents()[1].media_description();
EXPECT_EQ(vcd1->codecs().size(), vcd2->codecs().size());
ASSERT_EQ(1u, vcd1->codecs().size());
EXPECT_EQ(vcd1->codecs()[0].name, vcd2->codecs()[0].name);
@@ -4406,11 +4326,11 @@ TEST_F(MediaSessionDescriptionFactoryTest,
// Test verifying that negotiating codecs with the same packetization retains
// the packetization value.
TEST_F(MediaSessionDescriptionFactoryTest, PacketizationIsEqual) {
- std::vector f1_codecs = {cricket::CreateVideoCodec(96, "H264")};
+ std::vector f1_codecs = {CreateVideoCodec(96, "H264")};
f1_codecs.back().packetization = "raw";
f1_.set_video_codecs(f1_codecs, f1_codecs);
- std::vector f2_codecs = {cricket::CreateVideoCodec(96, "H264")};
+ std::vector f2_codecs = {CreateVideoCodec(96, "H264")};
f2_codecs.back().packetization = "raw";
f2_.set_video_codecs(f2_codecs, f2_codecs);
@@ -4424,8 +4344,8 @@ TEST_F(MediaSessionDescriptionFactoryTest, PacketizationIsEqual) {
f1_.CreateOfferOrError(opts, nullptr).MoveValue();
ASSERT_TRUE(offer);
ASSERT_EQ(1u, offer->contents().size());
- const VideoContentDescription* vcd1 =
- offer->contents()[0].media_description()->as_video();
+ const MediaContentDescription* vcd1 =
+ offer->contents()[0].media_description();
ASSERT_EQ(1u, vcd1->codecs().size());
EXPECT_EQ(vcd1->codecs()[0].packetization, "raw");
@@ -4434,7 +4354,7 @@ TEST_F(MediaSessionDescriptionFactoryTest, PacketizationIsEqual) {
f2_.CreateAnswerOrError(offer.get(), opts, nullptr).MoveValue();
ASSERT_TRUE(answer);
ASSERT_EQ(1u, answer->contents().size());
- vcd1 = answer->contents()[0].media_description()->as_video();
+ vcd1 = answer->contents()[0].media_description();
ASSERT_EQ(1u, vcd1->codecs().size());
EXPECT_EQ(vcd1->codecs()[0].packetization, "raw");
}
@@ -4442,11 +4362,11 @@ TEST_F(MediaSessionDescriptionFactoryTest, PacketizationIsEqual) {
// Test verifying that negotiating codecs with different packetization removes
// the packetization value.
TEST_F(MediaSessionDescriptionFactoryTest, PacketizationIsDifferent) {
- std::vector f1_codecs = {cricket::CreateVideoCodec(96, "H264")};
+ std::vector f1_codecs = {CreateVideoCodec(96, "H264")};
f1_codecs.back().packetization = "raw";
f1_.set_video_codecs(f1_codecs, f1_codecs);
- std::vector f2_codecs = {cricket::CreateVideoCodec(96, "H264")};
+ std::vector f2_codecs = {CreateVideoCodec(96, "H264")};
f2_codecs.back().packetization = "notraw";
f2_.set_video_codecs(f2_codecs, f2_codecs);
@@ -4491,10 +4411,9 @@ TEST_F(MediaSessionDescriptionFactoryTest,
f1_.CreateOfferOrError(opts, nullptr).MoveValue();
ASSERT_TRUE(offer);
ASSERT_EQ(2u, offer->contents().size());
- VideoContentDescription* vcd1 =
- offer->contents()[0].media_description()->as_video();
- const VideoContentDescription* vcd2 =
- offer->contents()[1].media_description()->as_video();
+ MediaContentDescription* vcd1 = offer->contents()[0].media_description();
+ const MediaContentDescription* vcd2 =
+ offer->contents()[1].media_description();
auto video_codecs = MAKE_VECTOR(kVideoCodecs1);
EXPECT_EQ(video_codecs, vcd1->codecs());
EXPECT_EQ(video_codecs, vcd2->codecs());
@@ -4505,8 +4424,8 @@ TEST_F(MediaSessionDescriptionFactoryTest,
vcd1->set_codecs(video_codecs_reverse);
std::unique_ptr<SessionDescription> updated_offer(
f1_.CreateOfferOrError(opts, offer.get()).MoveValue());
- vcd1 = updated_offer->contents()[0].media_description()->as_video();
- vcd2 = updated_offer->contents()[1].media_description()->as_video();
+ vcd1 = updated_offer->contents()[0].media_description();
+ vcd2 = updated_offer->contents()[1].media_description();
// The video codec preference order should be respected.
EXPECT_EQ(video_codecs_reverse, vcd1->codecs());
EXPECT_EQ(video_codecs, vcd2->codecs());
@@ -4528,10 +4447,9 @@ TEST_F(MediaSessionDescriptionFactoryTest,
f1_.CreateOfferOrError(opts, nullptr).MoveValue();
ASSERT_TRUE(offer);
ASSERT_EQ(2u, offer->contents().size());
- VideoContentDescription* vcd1 =
- offer->contents()[0].media_description()->as_video();
- const VideoContentDescription* vcd2 =
- offer->contents()[1].media_description()->as_video();
+ MediaContentDescription* vcd1 = offer->contents()[0].media_description();
+ const MediaContentDescription* vcd2 =
+ offer->contents()[1].media_description();
auto video_codecs = MAKE_VECTOR(kVideoCodecs1);
EXPECT_EQ(video_codecs, vcd1->codecs());
EXPECT_EQ(video_codecs, vcd2->codecs());
@@ -4542,8 +4460,8 @@ TEST_F(MediaSessionDescriptionFactoryTest,
vcd1->set_codecs(video_codecs_reverse);
std::unique_ptr<SessionDescription> answer =
f1_.CreateAnswerOrError(offer.get(), opts, nullptr).MoveValue();
- vcd1 = answer->contents()[0].media_description()->as_video();
- vcd2 = answer->contents()[1].media_description()->as_video();
+ vcd1 = answer->contents()[0].media_description();
+ vcd2 = answer->contents()[1].media_description();
// The video codec preference order should be respected.
EXPECT_EQ(video_codecs_reverse, vcd1->codecs());
EXPECT_EQ(video_codecs, vcd2->codecs());
@@ -4586,8 +4504,8 @@ TEST_F(MediaSessionDescriptionFactoryTest, CreateAnswerWithLocalCodecParams) {
std::unique_ptr<SessionDescription> offer =
f1_.CreateOfferOrError(opts, nullptr).MoveValue();
ASSERT_TRUE(offer);
- auto offer_acd = offer->contents()[0].media_description()->as_audio();
- auto offer_vcd = offer->contents()[1].media_description()->as_video();
+ auto offer_acd = offer->contents()[0].media_description();
+ auto offer_vcd = offer->contents()[1].media_description();
std::string value;
EXPECT_TRUE(offer_acd->codecs()[0].GetParam(audio_param_name, &value));
EXPECT_EQ(audio_value1, value);
@@ -4597,8 +4515,8 @@ TEST_F(MediaSessionDescriptionFactoryTest, CreateAnswerWithLocalCodecParams) {
std::unique_ptr<SessionDescription> answer =
f2_.CreateAnswerOrError(offer.get(), opts, nullptr).MoveValue();
ASSERT_TRUE(answer);
- auto answer_acd = answer->contents()[0].media_description()->as_audio();
- auto answer_vcd = answer->contents()[1].media_description()->as_video();
+ auto answer_acd = answer->contents()[0].media_description();
+ auto answer_vcd = answer->contents()[1].media_description();
// Use the parameters from the local codecs.
EXPECT_TRUE(answer_acd->codecs()[0].GetParam(audio_param_name, &value));
EXPECT_EQ(audio_value2, value);
@@ -4615,12 +4533,12 @@ TEST_F(MediaSessionDescriptionFactoryTest,
H264MatchCriteriaIncludesPacketizationMode) {
// Create two H264 codecs with the same profile level ID and different
// packetization modes.
- VideoCodec h264_pm0 = cricket::CreateVideoCodec(96, "H264");
- h264_pm0.params[cricket::kH264FmtpProfileLevelId] = "42c01f";
- h264_pm0.params[cricket::kH264FmtpPacketizationMode] = "0";
- VideoCodec h264_pm1 = cricket::CreateVideoCodec(97, "H264");
- h264_pm1.params[cricket::kH264FmtpProfileLevelId] = "42c01f";
- h264_pm1.params[cricket::kH264FmtpPacketizationMode] = "1";
+ VideoCodec h264_pm0 = CreateVideoCodec(96, "H264");
+ h264_pm0.params[kH264FmtpProfileLevelId] = "42c01f";
+ h264_pm0.params[kH264FmtpPacketizationMode] = "0";
+ VideoCodec h264_pm1 = CreateVideoCodec(97, "H264");
+ h264_pm1.params[kH264FmtpProfileLevelId] = "42c01f";
+ h264_pm1.params[kH264FmtpPacketizationMode] = "1";
// Offerer will send both codecs, answerer should choose the one with matching
// packetization mode (and not the first one it sees).
@@ -4643,13 +4561,13 @@ TEST_F(MediaSessionDescriptionFactoryTest,
// Answer should have one negotiated codec with packetization-mode=1 using the
// offered payload type.
ASSERT_EQ(1u, answer->contents().size());
- auto answer_vcd = answer->contents()[0].media_description()->as_video();
+ auto answer_vcd = answer->contents()[0].media_description();
ASSERT_EQ(1u, answer_vcd->codecs().size());
auto answer_codec = answer_vcd->codecs()[0];
EXPECT_EQ(h264_pm1.id, answer_codec.id);
}
-class MediaProtocolTest : public ::testing::TestWithParam<const char*> {
+class MediaProtocolTest : public testing::TestWithParam<const char*> {
public:
MediaProtocolTest()
: tdf1_(field_trials_),
@@ -4702,18 +4620,18 @@ TEST_P(MediaProtocolTest, TestAudioVideoAcceptance) {
ASSERT_TRUE(vc);
EXPECT_FALSE(ac->rejected); // the offer is accepted
EXPECT_FALSE(vc->rejected);
- const AudioContentDescription* acd = ac->media_description()->as_audio();
- const VideoContentDescription* vcd = vc->media_description()->as_video();
+ const MediaContentDescription* acd = ac->media_description();
+ const MediaContentDescription* vcd = vc->media_description();
EXPECT_EQ(GetParam(), acd->protocol());
EXPECT_EQ(GetParam(), vcd->protocol());
}
INSTANTIATE_TEST_SUITE_P(MediaProtocolPatternTest,
MediaProtocolTest,
- ::testing::ValuesIn(kMediaProtocols));
+ ValuesIn(kMediaProtocols));
INSTANTIATE_TEST_SUITE_P(MediaProtocolDtlsPatternTest,
MediaProtocolTest,
- ::testing::ValuesIn(kMediaProtocolsDtls));
+ ValuesIn(kMediaProtocolsDtls));
TEST_F(MediaSessionDescriptionFactoryTest, TestSetAudioCodecs) {
webrtc::test::ScopedKeyValueConfig field_trials;
@@ -4769,17 +4687,15 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestSetAudioCodecs) {
EXPECT_EQ(no_codecs, sf.audio_sendrecv_codecs());
}
-namespace {
// Compare the two vectors of codecs ignoring the payload type.
-bool CodecsMatch(const std::vector<cricket::Codec>& codecs1,
- const std::vector<cricket::Codec>& codecs2,
- const webrtc::FieldTrialsView* field_trials) {
+bool CodecsMatch(const std::vector<Codec>& codecs1,
+ const std::vector<Codec>& codecs2) {
if (codecs1.size() != codecs2.size()) {
return false;
}
for (size_t i = 0; i < codecs1.size(); ++i) {
- if (!codecs1[i].Matches(codecs2[i], field_trials)) {
+ if (!codecs1[i].Matches(codecs2[i])) {
return false;
}
}
@@ -4817,29 +4733,29 @@ void TestAudioCodecsOffer(RtpTransceiverDirection direction) {
// send nor receive audio. The checks are still in place if at some point
// we'd instead create an inactive stream.
if (ac) {
- AudioContentDescription* acd = ac->media_description()->as_audio();
+ MediaContentDescription* acd = ac->media_description();
// sendrecv and inactive should both present lists as if the channel was
// to be used for sending and receiving. Inactive essentially means it
// might eventually be used anything, but we don't know more at this
// moment.
if (acd->direction() == RtpTransceiverDirection::kSendOnly) {
- EXPECT_TRUE(CodecsMatch(send_codecs, acd->codecs(), &field_trials));
+ EXPECT_TRUE(CodecsMatch(send_codecs, acd->codecs()));
} else if (acd->direction() == RtpTransceiverDirection::kRecvOnly) {
- EXPECT_TRUE(CodecsMatch(recv_codecs, acd->codecs(), &field_trials));
+ EXPECT_TRUE(CodecsMatch(recv_codecs, acd->codecs()));
} else {
- EXPECT_TRUE(CodecsMatch(sendrecv_codecs, acd->codecs(), &field_trials));
+ EXPECT_TRUE(CodecsMatch(sendrecv_codecs, acd->codecs()));
}
}
}
-static const AudioCodec kOfferAnswerCodecs[] = {
- cricket::CreateAudioCodec(0, "codec0", 16000, 1),
- cricket::CreateAudioCodec(1, "codec1", 8000, 1),
- cricket::CreateAudioCodec(2, "codec2", 8000, 1),
- cricket::CreateAudioCodec(3, "codec3", 8000, 1),
- cricket::CreateAudioCodec(4, "codec4", 8000, 2),
- cricket::CreateAudioCodec(5, "codec5", 32000, 1),
- cricket::CreateAudioCodec(6, "codec6", 48000, 1)};
+const AudioCodec kOfferAnswerCodecs[] = {
+ CreateAudioCodec(0, "codec0", 16000, 1),
+ CreateAudioCodec(1, "codec1", 8000, 1),
+ CreateAudioCodec(2, "codec2", 8000, 1),
+ CreateAudioCodec(3, "codec3", 8000, 1),
+ CreateAudioCodec(4, "codec4", 8000, 2),
+ CreateAudioCodec(5, "codec5", 32000, 1),
+ CreateAudioCodec(6, "codec6", 48000, 1)};
/* The codecs groups below are chosen as per the matrix below. The objective
* is to have different sets of codecs in the inputs, to get unique sets of
@@ -4860,18 +4776,18 @@ static const AudioCodec kOfferAnswerCodecs[] = {
* 6 | x x x | x x x | x x x x x
*/
// Codecs used by offerer in the AudioCodecsAnswerTest
-static const int kOfferSendCodecs[] = {0, 1, 3, 5, 6};
-static const int kOfferRecvCodecs[] = {1, 2, 3, 4, 6};
+const int kOfferSendCodecs[] = {0, 1, 3, 5, 6};
+const int kOfferRecvCodecs[] = {1, 2, 3, 4, 6};
// Codecs used in the answerer in the AudioCodecsAnswerTest. The order is
// jumbled to catch the answer not following the order in the offer.
-static const int kAnswerSendCodecs[] = {6, 5, 2, 3, 4};
-static const int kAnswerRecvCodecs[] = {6, 5, 4, 1, 0};
+const int kAnswerSendCodecs[] = {6, 5, 2, 3, 4};
+const int kAnswerRecvCodecs[] = {6, 5, 4, 1, 0};
// The resulting sets of codecs in the answer in the AudioCodecsAnswerTest
-static const int kResultSend_RecvCodecs[] = {0, 1, 5, 6};
-static const int kResultRecv_SendCodecs[] = {2, 3, 4, 6};
-static const int kResultSendrecv_SendCodecs[] = {3, 6};
-static const int kResultSendrecv_RecvCodecs[] = {1, 6};
-static const int kResultSendrecv_SendrecvCodecs[] = {6};
+const int kResultSend_RecvCodecs[] = {0, 1, 5, 6};
+const int kResultRecv_SendCodecs[] = {2, 3, 4, 6};
+const int kResultSendrecv_SendCodecs[] = {3, 6};
+const int kResultSendrecv_RecvCodecs[] = {1, 6};
+const int kResultSendrecv_SendrecvCodecs[] = {6};
template <typename T, int IDXS>
std::vector<T> VectorFromIndices(const T* array, const int (&indices)[IDXS]) {
@@ -4936,7 +4852,7 @@ void TestAudioCodecsAnswer(RtpTransceiverDirection offer_direction,
// we'd instead create an inactive stream.
if (ac) {
ASSERT_EQ(MEDIA_TYPE_AUDIO, ac->media_description()->type());
- const AudioContentDescription* acd = ac->media_description()->as_audio();
+ const MediaContentDescription* acd = ac->media_description();
std::vector<AudioCodec> target_codecs;
// For offers with sendrecv or inactive, we should never reply with more
@@ -4998,10 +4914,7 @@ void TestAudioCodecsAnswer(RtpTransceiverDirection offer_direction,
}
}
-} // namespace
-
-class AudioCodecsOfferTest
- : public ::testing::TestWithParam<RtpTransceiverDirection> {};
+using AudioCodecsOfferTest = testing::TestWithParam<RtpTransceiverDirection>;
TEST_P(AudioCodecsOfferTest, TestCodecsInOffer) {
TestAudioCodecsOffer(GetParam());
@@ -5009,31 +4922,30 @@ TEST_P(AudioCodecsOfferTest, TestCodecsInOffer) {
INSTANTIATE_TEST_SUITE_P(MediaSessionDescriptionFactoryTest,
AudioCodecsOfferTest,
- ::testing::Values(RtpTransceiverDirection::kSendOnly,
- RtpTransceiverDirection::kRecvOnly,
- RtpTransceiverDirection::kSendRecv,
- RtpTransceiverDirection::kInactive));
+ Values(RtpTransceiverDirection::kSendOnly,
+ RtpTransceiverDirection::kRecvOnly,
+ RtpTransceiverDirection::kSendRecv,
+ RtpTransceiverDirection::kInactive));
-class AudioCodecsAnswerTest
- : public ::testing::TestWithParam<::testing::tuple<RtpTransceiverDirection,
- RtpTransceiverDirection,
- bool>> {};
+using AudioCodecsAnswerTest = testing::TestWithParam<
+ std::tuple<RtpTransceiverDirection, RtpTransceiverDirection, bool>>;
TEST_P(AudioCodecsAnswerTest, TestCodecsInAnswer) {
- TestAudioCodecsAnswer(::testing::get<0>(GetParam()),
- ::testing::get<1>(GetParam()),
- ::testing::get<2>(GetParam()));
-}
-
-INSTANTIATE_TEST_SUITE_P(
- MediaSessionDescriptionFactoryTest,
- AudioCodecsAnswerTest,
- ::testing::Combine(::testing::Values(RtpTransceiverDirection::kSendOnly,
- RtpTransceiverDirection::kRecvOnly,
- RtpTransceiverDirection::kSendRecv,
- RtpTransceiverDirection::kInactive),
- ::testing::Values(RtpTransceiverDirection::kSendOnly,
- RtpTransceiverDirection::kRecvOnly,
- RtpTransceiverDirection::kSendRecv,
- RtpTransceiverDirection::kInactive),
- ::testing::Bool()));
+ TestAudioCodecsAnswer(std::get<0>(GetParam()), std::get<1>(GetParam()),
+ std::get<2>(GetParam()));
+}
+
+INSTANTIATE_TEST_SUITE_P(MediaSessionDescriptionFactoryTest,
+ AudioCodecsAnswerTest,
+ Combine(Values(RtpTransceiverDirection::kSendOnly,
+ RtpTransceiverDirection::kRecvOnly,
+ RtpTransceiverDirection::kSendRecv,
+ RtpTransceiverDirection::kInactive),
+ Values(RtpTransceiverDirection::kSendOnly,
+ RtpTransceiverDirection::kRecvOnly,
+ RtpTransceiverDirection::kSendRecv,
+ RtpTransceiverDirection::kInactive),
+ Bool()));
+
+} // namespace
+} // namespace cricket
diff --git a/third_party/libwebrtc/pc/media_stream_unittest.cc b/third_party/libwebrtc/pc/media_stream_unittest.cc
index f55ea203fb..d6c79efae9 100644
--- a/third_party/libwebrtc/pc/media_stream_unittest.cc
+++ b/third_party/libwebrtc/pc/media_stream_unittest.cc
@@ -91,7 +91,7 @@ TEST_F(MediaStreamTest, GetTrackInfo) {
ASSERT_EQ(1u, stream_->GetAudioTracks().size());
// Verify the video track.
- scoped_refptr<webrtc::MediaStreamTrackInterface> video_track(
+ scoped_refptr<MediaStreamTrackInterface> video_track(
stream_->GetVideoTracks()[0]);
EXPECT_EQ(0, video_track->id().compare(kVideoTrackId));
EXPECT_TRUE(video_track->enabled());
@@ -105,7 +105,7 @@ TEST_F(MediaStreamTest, GetTrackInfo) {
EXPECT_TRUE(video_track->enabled());
// Verify the audio track.
- scoped_refptr<webrtc::MediaStreamTrackInterface> audio_track(
+ scoped_refptr<MediaStreamTrackInterface> audio_track(
stream_->GetAudioTracks()[0]);
EXPECT_EQ(0, audio_track->id().compare(kAudioTrackId));
EXPECT_TRUE(audio_track->enabled());
@@ -139,14 +139,12 @@ TEST_F(MediaStreamTest, RemoveTrack) {
}
TEST_F(MediaStreamTest, ChangeVideoTrack) {
- scoped_refptr<webrtc::VideoTrackInterface> video_track(
- stream_->GetVideoTracks()[0]);
+ scoped_refptr<VideoTrackInterface> video_track(stream_->GetVideoTracks()[0]);
ChangeTrack(video_track.get());
}
TEST_F(MediaStreamTest, ChangeAudioTrack) {
- scoped_refptr<webrtc::AudioTrackInterface> audio_track(
- stream_->GetAudioTracks()[0]);
+ scoped_refptr<AudioTrackInterface> audio_track(stream_->GetAudioTracks()[0]);
ChangeTrack(audio_track.get());
}
diff --git a/third_party/libwebrtc/pc/peer_connection.cc b/third_party/libwebrtc/pc/peer_connection.cc
index 183cbeb7cd..26b70c63db 100644
--- a/third_party/libwebrtc/pc/peer_connection.cc
+++ b/third_party/libwebrtc/pc/peer_connection.cc
@@ -23,6 +23,7 @@
#include "absl/strings/match.h"
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
+#include "api/environment/environment.h"
#include "api/jsep_ice_candidate.h"
#include "api/media_types.h"
#include "api/rtp_parameters.h"
@@ -185,7 +186,7 @@ IceCandidatePairType GetIceCandidatePairCounter(
absl::optional<int> RTCConfigurationToIceConfigOptionalInt(
int rtc_configuration_parameter) {
if (rtc_configuration_parameter ==
- webrtc::PeerConnectionInterface::RTCConfiguration::kUndefined) {
+ PeerConnectionInterface::RTCConfiguration::kUndefined) {
return absl::nullopt;
}
return rtc_configuration_parameter;
@@ -449,7 +450,7 @@ bool PeerConnectionInterface::RTCConfiguration::operator==(
absl::optional<int> ice_unwritable_min_checks;
absl::optional<int> ice_inactive_timeout;
absl::optional<int> stun_candidate_keepalive_interval;
- webrtc::TurnCustomizer* turn_customizer;
+ TurnCustomizer* turn_customizer;
SdpSemantics sdp_semantics;
absl::optional<rtc::AdapterType> network_preference;
bool active_reset_srtp_params;
@@ -459,7 +460,7 @@ bool PeerConnectionInterface::RTCConfiguration::operator==(
bool enable_implicit_rollback;
absl::optional<int> report_usage_pattern_delay_ms;
absl::optional<int> stable_writable_connection_ping_interval_ms;
- webrtc::VpnPreference vpn_preference;
+ VpnPreference vpn_preference;
std::vector<rtc::NetworkMask> vpn_list;
PortAllocatorConfig port_allocator_config;
absl::optional<TimeDelta> pacer_burst_interval;
@@ -536,9 +537,9 @@ bool PeerConnectionInterface::RTCConfiguration::operator!=(
}
RTCErrorOr<rtc::scoped_refptr<PeerConnection>> PeerConnection::Create(
+ const Environment& env,
rtc::scoped_refptr<ConnectionContext> context,
const PeerConnectionFactoryInterface::Options& options,
- std::unique_ptr<RtcEventLog> event_log,
std::unique_ptr<Call> call,
const PeerConnectionInterface::RTCConfiguration& configuration,
PeerConnectionDependencies dependencies) {
@@ -577,36 +578,15 @@ RTCErrorOr<rtc::scoped_refptr<PeerConnection>> PeerConnection::Create(
configuration.sdp_semantics == SdpSemantics::kUnifiedPlan;
bool dtls_enabled = DtlsEnabled(configuration, options, dependencies);
- // Interim code: If an AsyncResolverFactory is given, but not an
- // AsyncDnsResolverFactory, wrap it in a WrappingAsyncDnsResolverFactory
- // If neither is given, create a BasicAsyncDnsResolverFactory.
- // TODO(bugs.webrtc.org/12598): Remove code once all callers pass a
- // AsyncDnsResolverFactory.
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
- if (dependencies.async_dns_resolver_factory &&
- dependencies.async_resolver_factory) {
- RTC_LOG(LS_ERROR)
- << "Attempt to set both old and new type of DNS resolver factory";
- return RTCError(RTCErrorType::INVALID_PARAMETER,
- "Both old and new type of DNS resolver given");
- }
if (!dependencies.async_dns_resolver_factory) {
- if (dependencies.async_resolver_factory) {
- dependencies.async_dns_resolver_factory =
- std::make_unique<WrappingAsyncDnsResolverFactory>(
- std::move(dependencies.async_resolver_factory));
- } else {
dependencies.async_dns_resolver_factory =
std::make_unique<BasicAsyncDnsResolverFactory>();
- }
}
-#pragma clang diagnostic pop
// The PeerConnection constructor consumes some, but not all, dependencies.
auto pc = rtc::make_ref_counted<PeerConnection>(
- context, options, is_unified_plan, std::move(event_log), std::move(call),
- dependencies, dtls_enabled);
+ env, context, options, is_unified_plan, std::move(call), dependencies,
+ dtls_enabled);
RTCError init_error = pc->Initialize(configuration, std::move(dependencies));
if (!init_error.ok()) {
RTC_LOG(LS_ERROR) << "PeerConnection initialization failed";
@@ -616,20 +596,18 @@ RTCErrorOr<rtc::scoped_refptr<PeerConnection>> PeerConnection::Create(
}
PeerConnection::PeerConnection(
+ const Environment& env,
rtc::scoped_refptr<ConnectionContext> context,
const PeerConnectionFactoryInterface::Options& options,
bool is_unified_plan,
- std::unique_ptr<RtcEventLog> event_log,
std::unique_ptr<Call> call,
PeerConnectionDependencies& dependencies,
bool dtls_enabled)
- : context_(context),
- trials_(std::move(dependencies.trials), &context->field_trials()),
+ : env_(env),
+ context_(context),
options_(options),
observer_(dependencies.observer),
is_unified_plan_(is_unified_plan),
- event_log_(std::move(event_log)),
- event_log_ptr_(event_log_.get()),
async_dns_resolver_factory_(
std::move(dependencies.async_dns_resolver_factory)),
port_allocator_(std::move(dependencies.allocator)),
@@ -648,7 +626,10 @@ PeerConnection::PeerConnection(
dtls_enabled_(dtls_enabled),
data_channel_controller_(this),
message_handler_(signaling_thread()),
- weak_factory_(this) {}
+ weak_factory_(this) {
+ // Field trials specific to the peerconnection should be owned by the `env`,
+ RTC_DCHECK(dependencies.trials == nullptr);
+}
PeerConnection::~PeerConnection() {
TRACE_EVENT0("webrtc", "PeerConnection::~PeerConnection");
@@ -699,13 +680,11 @@ PeerConnection::~PeerConnection() {
sctp_mid_s_.reset();
SetSctpTransportName("");
- // call_ and event_log_ must be destroyed on the worker thread.
+ // call_ must be destroyed on the worker thread.
worker_thread()->BlockingCall([this] {
RTC_DCHECK_RUN_ON(worker_thread());
worker_thread_safety_->SetNotAlive();
call_.reset();
- // The event log must outlive call (and any other object that uses it).
- event_log_.reset();
});
data_channel_controller_.PrepareForShutdown();
@@ -797,7 +776,7 @@ JsepTransportController* PeerConnection::InitializeTransportController_n(
config.transport_observer = this;
config.rtcp_handler = InitializeRtcpCallback();
config.un_demuxable_packet_handler = InitializeUnDemuxablePacketHandler();
- config.event_log = event_log_ptr_;
+ config.event_log = &env_.event_log();
#if defined(ENABLE_EXTERNAL_AUTH)
config.enable_external_auth = true;
#endif
@@ -816,7 +795,7 @@ JsepTransportController* PeerConnection::InitializeTransportController_n(
}
};
- config.field_trials = trials_.get();
+ config.field_trials = &env_.field_trials();
transport_controller_.reset(new JsepTransportController(
network_thread(), port_allocator_.get(),
@@ -1685,7 +1664,7 @@ void PeerConnection::AddIceCandidate(
std::function<void(RTCError)> callback) {
RTC_DCHECK_RUN_ON(signaling_thread());
sdp_handler_->AddIceCandidate(std::move(candidate),
- [this, callback](webrtc::RTCError result) {
+ [this, callback](RTCError result) {
ClearStatsCache();
callback(result);
});
@@ -1789,7 +1768,7 @@ bool PeerConnection::StartRtcEventLog(
std::unique_ptr<RtcEventLogOutput> output) {
int64_t output_period_ms = 5000;
if (trials().IsDisabled("WebRTC-RtcEventLogNewFormat")) {
- output_period_ms = webrtc::RtcEventLog::kImmediateOutput;
+ output_period_ms = RtcEventLog::kImmediateOutput;
}
return StartRtcEventLog(std::move(output), output_period_ms);
}
@@ -1931,8 +1910,7 @@ void PeerConnection::Close() {
RTC_DCHECK_RUN_ON(worker_thread());
worker_thread_safety_->SetNotAlive();
call_.reset();
- // The event log must outlive call (and any other object that uses it).
- event_log_.reset();
+ StopRtcEventLog_w();
});
ReportUsagePattern();
@@ -2029,13 +2007,13 @@ void PeerConnection::ReportFirstConnectUsageMetrics() {
RTC_HISTOGRAM_ENUMERATION("WebRTC.PeerConnection.ProvisionalAnswer", pranswer,
kProvisionalAnswerMax);
- // Record the number of valid / invalid ice-ufrag. We do allow certain
- // non-spec ice-char for backward-compat reasons. At this point we know
- // that the ufrag/pwd consists of a valid ice-char or one of the four
- // not allowed characters since we have passed the IsIceChar check done
- // by the p2p transport description on setRemoteDescription calls.
auto transport_infos = remote_description()->description()->transport_infos();
- if (transport_infos.size() > 0) {
+ if (!transport_infos.empty()) {
+ // Record the number of valid / invalid ice-ufrag. We do allow certain
+ // non-spec ice-char for backward-compat reasons. At this point we know
+ // that the ufrag/pwd consists of a valid ice-char or one of the four
+ // not allowed characters since we have passed the IsIceChar check done
+ // by the p2p transport description on setRemoteDescription calls.
auto ice_parameters = transport_infos[0].description.GetIceParameters();
auto is_invalid_char = [](char c) {
return c == '-' || c == '=' || c == '#' || c == '_';
@@ -2047,6 +2025,16 @@ void PeerConnection::ReportFirstConnectUsageMetrics() {
RTC_HISTOGRAM_BOOLEAN(
"WebRTC.PeerConnection.ValidIceChars",
!(isUsingInvalidIceCharInUfrag || isUsingInvalidIceCharInPwd));
+
+ // Record whether the hash algorithm of the first transport's
+ // DTLS fingerprint is still using SHA-1.
+ if (transport_infos[0].description.identity_fingerprint) {
+ RTC_HISTOGRAM_BOOLEAN(
+ "WebRTC.PeerConnection.DtlsFingerprintLegacySha1",
+ absl::EqualsIgnoreCase(
+ transport_infos[0].description.identity_fingerprint->algorithm,
+ "sha-1"));
+ }
}
// Record RtcpMuxPolicy setting.
@@ -2222,7 +2210,7 @@ bool PeerConnection::ReconfigurePortAllocator_n(
IceTransportsType type,
int candidate_pool_size,
PortPrunePolicy turn_port_prune_policy,
- webrtc::TurnCustomizer* turn_customizer,
+ TurnCustomizer* turn_customizer,
absl::optional<int> stun_candidate_keepalive_interval,
bool have_local_description) {
RTC_DCHECK_RUN_ON(network_thread());
@@ -2245,17 +2233,15 @@ bool PeerConnection::StartRtcEventLog_w(
std::unique_ptr<RtcEventLogOutput> output,
int64_t output_period_ms) {
RTC_DCHECK_RUN_ON(worker_thread());
- if (!event_log_) {
+ if (!worker_thread_safety_->alive()) {
return false;
}
- return event_log_->StartLogging(std::move(output), output_period_ms);
+ return env_.event_log().StartLogging(std::move(output), output_period_ms);
}
void PeerConnection::StopRtcEventLog_w() {
RTC_DCHECK_RUN_ON(worker_thread());
- if (event_log_) {
- event_log_->StopLogging();
- }
+ env_.event_log().StopLogging();
}
absl::optional<rtc::SSLRole> PeerConnection::GetSctpSslRole_n() {
diff --git a/third_party/libwebrtc/pc/peer_connection.h b/third_party/libwebrtc/pc/peer_connection.h
index ea1a9d9d90..e6037a2698 100644
--- a/third_party/libwebrtc/pc/peer_connection.h
+++ b/third_party/libwebrtc/pc/peer_connection.h
@@ -27,6 +27,7 @@
#include "api/crypto/crypto_options.h"
#include "api/data_channel_interface.h"
#include "api/dtls_transport_interface.h"
+#include "api/environment/environment.h"
#include "api/field_trials_view.h"
#include "api/ice_transport_interface.h"
#include "api/jsep.h"
@@ -110,9 +111,9 @@ class PeerConnection : public PeerConnectionInternal,
// Note that the function takes ownership of dependencies, and will
// either use them or release them, whether it succeeds or fails.
static RTCErrorOr<rtc::scoped_refptr<PeerConnection>> Create(
+ const Environment& env,
rtc::scoped_refptr<ConnectionContext> context,
const PeerConnectionFactoryInterface::Options& options,
- std::unique_ptr<RtcEventLog> event_log,
std::unique_ptr<Call> call,
const PeerConnectionInterface::RTCConfiguration& configuration,
PeerConnectionDependencies dependencies);
@@ -163,7 +164,7 @@ class PeerConnection : public PeerConnectionInternal,
const DataChannelInit* config) override;
// WARNING: LEGACY. See peerconnectioninterface.h
bool GetStats(StatsObserver* observer,
- webrtc::MediaStreamTrackInterface* track,
+ MediaStreamTrackInterface* track,
StatsOutputLevel level) override;
// Spec-complaint GetStats(). See peerconnectioninterface.h
void GetStats(RTCStatsCollectorCallback* callback) override;
@@ -428,7 +429,7 @@ class PeerConnection : public PeerConnectionInternal,
void TeardownDataChannelTransport_n(RTCError error)
RTC_RUN_ON(network_thread());
- const FieldTrialsView& trials() const override { return *trials_; }
+ const FieldTrialsView& trials() const override { return env_.field_trials(); }
bool ConfiguredForMedia() const;
@@ -441,10 +442,10 @@ class PeerConnection : public PeerConnectionInternal,
protected:
// Available for rtc::scoped_refptr creation
- PeerConnection(rtc::scoped_refptr<ConnectionContext> context,
+ PeerConnection(const Environment& env,
+ rtc::scoped_refptr<ConnectionContext> context,
const PeerConnectionFactoryInterface::Options& options,
bool is_unified_plan,
- std::unique_ptr<RtcEventLog> event_log,
std::unique_ptr<Call> call,
PeerConnectionDependencies& dependencies,
bool dtls_enabled);
@@ -510,7 +511,7 @@ class PeerConnection : public PeerConnectionInternal,
IceTransportsType type,
int candidate_pool_size,
PortPrunePolicy turn_port_prune_policy,
- webrtc::TurnCustomizer* turn_customizer,
+ TurnCustomizer* turn_customizer,
absl::optional<int> stun_candidate_keepalive_interval,
bool have_local_description);
@@ -597,26 +598,14 @@ class PeerConnection : public PeerConnectionInternal,
std::function<void(const RtpPacketReceived& parsed_packet)>
InitializeUnDemuxablePacketHandler();
+ const Environment env_;
const rtc::scoped_refptr<ConnectionContext> context_;
- // Field trials active for this PeerConnection is the first of:
- // a) Specified in PeerConnectionDependencies (owned).
- // b) Accessed via ConnectionContext (e.g PeerConnectionFactoryDependencies>
- // c) Created as Default (FieldTrialBasedConfig).
- const webrtc::AlwaysValidPointer<const FieldTrialsView, FieldTrialBasedConfig>
- trials_;
const PeerConnectionFactoryInterface::Options options_;
PeerConnectionObserver* observer_ RTC_GUARDED_BY(signaling_thread()) =
nullptr;
const bool is_unified_plan_;
- // The EventLog needs to outlive `call_` (and any other object that uses it).
- std::unique_ptr<RtcEventLog> event_log_ RTC_GUARDED_BY(worker_thread());
-
- // Points to the same thing as `event_log_`. Since it's const, we may read the
- // pointer (but not touch the object) from any thread.
- RtcEventLog* const event_log_ptr_ RTC_PT_GUARDED_BY(worker_thread());
-
IceConnectionState ice_connection_state_ RTC_GUARDED_BY(signaling_thread()) =
kIceConnectionNew;
PeerConnectionInterface::IceConnectionState standardized_ice_connection_state_
@@ -634,7 +623,7 @@ class PeerConnection : public PeerConnectionInternal,
std::unique_ptr<cricket::PortAllocator>
port_allocator_; // TODO(bugs.webrtc.org/9987): Accessed on both
// signaling and network thread.
- const std::unique_ptr<webrtc::IceTransportFactory>
+ const std::unique_ptr<IceTransportFactory>
ice_transport_factory_; // TODO(bugs.webrtc.org/9987): Accessed on the
// signaling thread but the underlying raw
// pointer is given to
diff --git a/third_party/libwebrtc/pc/peer_connection_crypto_unittest.cc b/third_party/libwebrtc/pc/peer_connection_crypto_unittest.cc
index dc350b2be0..a65988ab05 100644
--- a/third_party/libwebrtc/pc/peer_connection_crypto_unittest.cc
+++ b/third_party/libwebrtc/pc/peer_connection_crypto_unittest.cc
@@ -162,7 +162,7 @@ class PeerConnectionCryptoBaseTest : public ::testing::Test {
return transport_info->description.connection_role;
}
- webrtc::test::ScopedKeyValueConfig field_trials_;
+ test::ScopedKeyValueConfig field_trials_;
std::unique_ptr<rtc::VirtualSocketServer> vss_;
rtc::AutoSocketServerThread main_;
rtc::scoped_refptr<PeerConnectionFactoryInterface> pc_factory_;
diff --git a/third_party/libwebrtc/pc/peer_connection_data_channel_unittest.cc b/third_party/libwebrtc/pc/peer_connection_data_channel_unittest.cc
index 3bb2088866..8682cfa9fb 100644
--- a/third_party/libwebrtc/pc/peer_connection_data_channel_unittest.cc
+++ b/third_party/libwebrtc/pc/peer_connection_data_channel_unittest.cc
@@ -15,7 +15,6 @@
#include <vector>
#include "absl/types/optional.h"
-#include "api/call/call_factory_interface.h"
#include "api/jsep.h"
#include "api/media_types.h"
#include "api/peer_connection_interface.h"
@@ -24,8 +23,6 @@
#include "api/task_queue/default_task_queue_factory.h"
#include "api/task_queue/task_queue_factory.h"
#include "api/transport/sctp_transport_factory_interface.h"
-#include "media/base/fake_media_engine.h"
-#include "media/base/media_engine.h"
#include "p2p/base/p2p_constants.h"
#include "p2p/base/port_allocator.h"
#include "pc/media_session.h"
@@ -35,6 +32,7 @@
#include "pc/sctp_transport.h"
#include "pc/sdp_utils.h"
#include "pc/session_description.h"
+#include "pc/test/enable_fake_media.h"
#include "pc/test/mock_peer_connection_observers.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
@@ -64,8 +62,7 @@ PeerConnectionFactoryDependencies CreatePeerConnectionFactoryDependencies() {
deps.worker_thread = rtc::Thread::Current();
deps.signaling_thread = rtc::Thread::Current();
deps.task_queue_factory = CreateDefaultTaskQueueFactory();
- deps.media_engine = std::make_unique<cricket::FakeMediaEngine>();
- deps.call_factory = CreateCallFactory();
+ EnableFakeMedia(deps);
deps.sctp_factory = std::make_unique<FakeSctpTransportFactory>();
return deps;
}
diff --git a/third_party/libwebrtc/pc/peer_connection_encodings_integrationtest.cc b/third_party/libwebrtc/pc/peer_connection_encodings_integrationtest.cc
index c7181c53ae..ae238671c2 100644
--- a/third_party/libwebrtc/pc/peer_connection_encodings_integrationtest.cc
+++ b/third_party/libwebrtc/pc/peer_connection_encodings_integrationtest.cc
@@ -77,18 +77,17 @@ struct StringParamToString {
// RTX, RED and FEC are reliability mechanisms used in combinations with other
// codecs, but are not themselves a specific codec. Typically you don't want to
// filter these out of the list of codec preferences.
-bool IsReliabilityMechanism(const webrtc::RtpCodecCapability& codec) {
+bool IsReliabilityMechanism(const RtpCodecCapability& codec) {
return absl::EqualsIgnoreCase(codec.name, cricket::kRtxCodecName) ||
absl::EqualsIgnoreCase(codec.name, cricket::kRedCodecName) ||
absl::EqualsIgnoreCase(codec.name, cricket::kUlpfecCodecName);
}
std::string GetCurrentCodecMimeType(
- rtc::scoped_refptr<const webrtc::RTCStatsReport> report,
- const webrtc::RTCOutboundRtpStreamStats& outbound_rtp) {
+ rtc::scoped_refptr<const RTCStatsReport> report,
+ const RTCOutboundRtpStreamStats& outbound_rtp) {
return outbound_rtp.codec_id.is_defined()
- ? *report->GetAs<webrtc::RTCCodecStats>(*outbound_rtp.codec_id)
- ->mime_type
+ ? *report->GetAs<RTCCodecStats>(*outbound_rtp.codec_id)->mime_type
: "";
}
@@ -98,8 +97,8 @@ struct RidAndResolution {
uint32_t height;
};
-const webrtc::RTCOutboundRtpStreamStats* FindOutboundRtpByRid(
- const std::vector<const webrtc::RTCOutboundRtpStreamStats*>& outbound_rtps,
+const RTCOutboundRtpStreamStats* FindOutboundRtpByRid(
+ const std::vector<const RTCOutboundRtpStreamStats*>& outbound_rtps,
const absl::string_view& rid) {
for (const auto* outbound_rtp : outbound_rtps) {
if (outbound_rtp->rid.is_defined() && *outbound_rtp->rid == rid) {
@@ -121,8 +120,8 @@ class PeerConnectionEncodingsIntegrationTest : public ::testing::Test {
rtc::scoped_refptr<PeerConnectionTestWrapper> CreatePc() {
auto pc_wrapper = rtc::make_ref_counted<PeerConnectionTestWrapper>(
"pc", &pss_, background_thread_.get(), background_thread_.get());
- pc_wrapper->CreatePc({}, webrtc::CreateBuiltinAudioEncoderFactory(),
- webrtc::CreateBuiltinAudioDecoderFactory());
+ pc_wrapper->CreatePc({}, CreateBuiltinAudioEncoderFactory(),
+ CreateBuiltinAudioDecoderFactory());
return pc_wrapper;
}
@@ -130,10 +129,9 @@ class PeerConnectionEncodingsIntegrationTest : public ::testing::Test {
rtc::scoped_refptr<PeerConnectionTestWrapper> local,
rtc::scoped_refptr<PeerConnectionTestWrapper> remote,
std::vector<cricket::SimulcastLayer> init_layers) {
- rtc::scoped_refptr<webrtc::MediaStreamInterface> stream =
- local->GetUserMedia(
- /*audio=*/false, cricket::AudioOptions(), /*video=*/true,
- {.width = 1280, .height = 720});
+ rtc::scoped_refptr<MediaStreamInterface> stream = local->GetUserMedia(
+ /*audio=*/false, cricket::AudioOptions(), /*video=*/true,
+ {.width = 1280, .height = 720});
rtc::scoped_refptr<VideoTrackInterface> track = stream->GetVideoTracks()[0];
RTCErrorOr<rtc::scoped_refptr<RtpTransceiverInterface>>
@@ -973,8 +971,7 @@ TEST_F(PeerConnectionEncodingsIntegrationTest,
local_pc_wrapper->pc()->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
rtc::scoped_refptr<RtpTransceiverInterface> audio_transceiver =
transceiver_or_error.MoveValue();
- webrtc::RtpParameters parameters =
- audio_transceiver->sender()->GetParameters();
+ RtpParameters parameters = audio_transceiver->sender()->GetParameters();
EXPECT_FALSE(parameters.encodings[0].codec.has_value());
}
@@ -986,8 +983,7 @@ TEST_F(PeerConnectionEncodingsIntegrationTest,
local_pc_wrapper->pc()->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
rtc::scoped_refptr<RtpTransceiverInterface> video_transceiver =
transceiver_or_error.MoveValue();
- webrtc::RtpParameters parameters =
- video_transceiver->sender()->GetParameters();
+ RtpParameters parameters = video_transceiver->sender()->GetParameters();
EXPECT_FALSE(parameters.encodings[0].codec.has_value());
}
@@ -997,19 +993,19 @@ TEST_F(PeerConnectionEncodingsIntegrationTest,
rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
- rtc::scoped_refptr<webrtc::MediaStreamInterface> stream =
+ rtc::scoped_refptr<MediaStreamInterface> stream =
local_pc_wrapper->GetUserMedia(
/*audio=*/true, {}, /*video=*/false, {});
rtc::scoped_refptr<AudioTrackInterface> track = stream->GetAudioTracks()[0];
- absl::optional<webrtc::RtpCodecCapability> pcmu =
+ absl::optional<RtpCodecCapability> pcmu =
local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_AUDIO,
"pcmu");
ASSERT_TRUE(pcmu);
- webrtc::RtpTransceiverInit init;
- init.direction = webrtc::RtpTransceiverDirection::kSendOnly;
- webrtc::RtpEncodingParameters encoding_parameters;
+ RtpTransceiverInit init;
+ init.direction = RtpTransceiverDirection::kSendOnly;
+ RtpEncodingParameters encoding_parameters;
encoding_parameters.codec = pcmu;
init.send_encodings.push_back(encoding_parameters);
@@ -1017,8 +1013,7 @@ TEST_F(PeerConnectionEncodingsIntegrationTest,
local_pc_wrapper->pc()->AddTransceiver(track, init);
rtc::scoped_refptr<RtpTransceiverInterface> audio_transceiver =
transceiver_or_error.MoveValue();
- webrtc::RtpParameters parameters =
- audio_transceiver->sender()->GetParameters();
+ RtpParameters parameters = audio_transceiver->sender()->GetParameters();
EXPECT_EQ(*parameters.encodings[0].codec, *pcmu);
NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
@@ -1039,19 +1034,19 @@ TEST_F(PeerConnectionEncodingsIntegrationTest,
rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
- rtc::scoped_refptr<webrtc::MediaStreamInterface> stream =
+ rtc::scoped_refptr<MediaStreamInterface> stream =
local_pc_wrapper->GetUserMedia(
/*audio=*/false, {}, /*video=*/true, {.width = 1280, .height = 720});
rtc::scoped_refptr<VideoTrackInterface> track = stream->GetVideoTracks()[0];
- absl::optional<webrtc::RtpCodecCapability> vp9 =
+ absl::optional<RtpCodecCapability> vp9 =
local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_VIDEO,
"vp9");
ASSERT_TRUE(vp9);
- webrtc::RtpTransceiverInit init;
- init.direction = webrtc::RtpTransceiverDirection::kSendOnly;
- webrtc::RtpEncodingParameters encoding_parameters;
+ RtpTransceiverInit init;
+ init.direction = RtpTransceiverDirection::kSendOnly;
+ RtpEncodingParameters encoding_parameters;
encoding_parameters.codec = vp9;
encoding_parameters.scalability_mode = "L3T3";
init.send_encodings.push_back(encoding_parameters);
@@ -1060,8 +1055,7 @@ TEST_F(PeerConnectionEncodingsIntegrationTest,
local_pc_wrapper->pc()->AddTransceiver(track, init);
rtc::scoped_refptr<RtpTransceiverInterface> audio_transceiver =
transceiver_or_error.MoveValue();
- webrtc::RtpParameters parameters =
- audio_transceiver->sender()->GetParameters();
+ RtpParameters parameters = audio_transceiver->sender()->GetParameters();
EXPECT_EQ(*parameters.encodings[0].codec, *vp9);
NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
@@ -1087,20 +1081,19 @@ TEST_F(PeerConnectionEncodingsIntegrationTest,
rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
- rtc::scoped_refptr<webrtc::MediaStreamInterface> stream =
+ rtc::scoped_refptr<MediaStreamInterface> stream =
local_pc_wrapper->GetUserMedia(
/*audio=*/true, {}, /*video=*/false, {});
rtc::scoped_refptr<AudioTrackInterface> track = stream->GetAudioTracks()[0];
- absl::optional<webrtc::RtpCodecCapability> pcmu =
+ absl::optional<RtpCodecCapability> pcmu =
local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_AUDIO,
"pcmu");
auto transceiver_or_error = local_pc_wrapper->pc()->AddTransceiver(track);
rtc::scoped_refptr<RtpTransceiverInterface> audio_transceiver =
transceiver_or_error.MoveValue();
- webrtc::RtpParameters parameters =
- audio_transceiver->sender()->GetParameters();
+ RtpParameters parameters = audio_transceiver->sender()->GetParameters();
parameters.encodings[0].codec = pcmu;
EXPECT_TRUE(audio_transceiver->sender()->SetParameters(parameters).ok());
@@ -1125,12 +1118,12 @@ TEST_F(PeerConnectionEncodingsIntegrationTest,
rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
- rtc::scoped_refptr<webrtc::MediaStreamInterface> stream =
+ rtc::scoped_refptr<MediaStreamInterface> stream =
local_pc_wrapper->GetUserMedia(
/*audio=*/true, {}, /*video=*/false, {});
rtc::scoped_refptr<AudioTrackInterface> track = stream->GetAudioTracks()[0];
- absl::optional<webrtc::RtpCodecCapability> pcmu =
+ absl::optional<RtpCodecCapability> pcmu =
local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_AUDIO,
"pcmu");
@@ -1150,8 +1143,7 @@ TEST_F(PeerConnectionEncodingsIntegrationTest,
EXPECT_STRCASENE(("audio/" + pcmu->name).c_str(), codec_name.c_str());
std::string last_codec_id = outbound_rtps[0]->codec_id.value();
- webrtc::RtpParameters parameters =
- audio_transceiver->sender()->GetParameters();
+ RtpParameters parameters = audio_transceiver->sender()->GetParameters();
parameters.encodings[0].codec = pcmu;
EXPECT_TRUE(audio_transceiver->sender()->SetParameters(parameters).ok());
@@ -1174,20 +1166,19 @@ TEST_F(PeerConnectionEncodingsIntegrationTest,
rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
- rtc::scoped_refptr<webrtc::MediaStreamInterface> stream =
+ rtc::scoped_refptr<MediaStreamInterface> stream =
local_pc_wrapper->GetUserMedia(
/*audio=*/false, {}, /*video=*/true, {.width = 1280, .height = 720});
rtc::scoped_refptr<VideoTrackInterface> track = stream->GetVideoTracks()[0];
- absl::optional<webrtc::RtpCodecCapability> vp9 =
+ absl::optional<RtpCodecCapability> vp9 =
local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_VIDEO,
"vp9");
auto transceiver_or_error = local_pc_wrapper->pc()->AddTransceiver(track);
rtc::scoped_refptr<RtpTransceiverInterface> video_transceiver =
transceiver_or_error.MoveValue();
- webrtc::RtpParameters parameters =
- video_transceiver->sender()->GetParameters();
+ RtpParameters parameters = video_transceiver->sender()->GetParameters();
parameters.encodings[0].codec = vp9;
parameters.encodings[0].scalability_mode = "L3T3";
EXPECT_TRUE(video_transceiver->sender()->SetParameters(parameters).ok());
@@ -1218,12 +1209,12 @@ TEST_F(PeerConnectionEncodingsIntegrationTest,
rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
- rtc::scoped_refptr<webrtc::MediaStreamInterface> stream =
+ rtc::scoped_refptr<MediaStreamInterface> stream =
local_pc_wrapper->GetUserMedia(
/*audio=*/false, {}, /*video=*/true, {.width = 1280, .height = 720});
rtc::scoped_refptr<VideoTrackInterface> track = stream->GetVideoTracks()[0];
- absl::optional<webrtc::RtpCodecCapability> vp9 =
+ absl::optional<RtpCodecCapability> vp9 =
local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_VIDEO,
"vp9");
@@ -1243,8 +1234,7 @@ TEST_F(PeerConnectionEncodingsIntegrationTest,
EXPECT_STRCASENE(("audio/" + vp9->name).c_str(), codec_name.c_str());
std::string last_codec_id = outbound_rtps[0]->codec_id.value();
- webrtc::RtpParameters parameters =
- video_transceiver->sender()->GetParameters();
+ RtpParameters parameters = video_transceiver->sender()->GetParameters();
parameters.encodings[0].codec = vp9;
parameters.encodings[0].scalability_mode = "L3T3";
EXPECT_TRUE(video_transceiver->sender()->SetParameters(parameters).ok());
@@ -1269,15 +1259,15 @@ TEST_F(PeerConnectionEncodingsIntegrationTest,
AddTransceiverRejectsUnknownCodecParameterAudio) {
rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
- webrtc::RtpCodec dummy_codec;
+ RtpCodec dummy_codec;
dummy_codec.kind = cricket::MEDIA_TYPE_AUDIO;
dummy_codec.name = "FOOBAR";
dummy_codec.clock_rate = 90000;
dummy_codec.num_channels = 2;
- webrtc::RtpTransceiverInit init;
- init.direction = webrtc::RtpTransceiverDirection::kSendOnly;
- webrtc::RtpEncodingParameters encoding_parameters;
+ RtpTransceiverInit init;
+ init.direction = RtpTransceiverDirection::kSendOnly;
+ RtpEncodingParameters encoding_parameters;
encoding_parameters.codec = dummy_codec;
init.send_encodings.push_back(encoding_parameters);
@@ -1292,14 +1282,14 @@ TEST_F(PeerConnectionEncodingsIntegrationTest,
AddTransceiverRejectsUnknownCodecParameterVideo) {
rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
- webrtc::RtpCodec dummy_codec;
+ RtpCodec dummy_codec;
dummy_codec.kind = cricket::MEDIA_TYPE_VIDEO;
dummy_codec.name = "FOOBAR";
dummy_codec.clock_rate = 90000;
- webrtc::RtpTransceiverInit init;
- init.direction = webrtc::RtpTransceiverDirection::kSendOnly;
- webrtc::RtpEncodingParameters encoding_parameters;
+ RtpTransceiverInit init;
+ init.direction = RtpTransceiverDirection::kSendOnly;
+ RtpEncodingParameters encoding_parameters;
encoding_parameters.codec = dummy_codec;
init.send_encodings.push_back(encoding_parameters);
@@ -1314,7 +1304,7 @@ TEST_F(PeerConnectionEncodingsIntegrationTest,
SetParametersRejectsUnknownCodecParameterAudio) {
rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
- webrtc::RtpCodec dummy_codec;
+ RtpCodec dummy_codec;
dummy_codec.kind = cricket::MEDIA_TYPE_AUDIO;
dummy_codec.name = "FOOBAR";
dummy_codec.clock_rate = 90000;
@@ -1326,8 +1316,7 @@ TEST_F(PeerConnectionEncodingsIntegrationTest,
rtc::scoped_refptr<RtpTransceiverInterface> audio_transceiver =
transceiver_or_error.MoveValue();
- webrtc::RtpParameters parameters =
- audio_transceiver->sender()->GetParameters();
+ RtpParameters parameters = audio_transceiver->sender()->GetParameters();
parameters.encodings[0].codec = dummy_codec;
RTCError error = audio_transceiver->sender()->SetParameters(parameters);
EXPECT_EQ(error.type(), RTCErrorType::INVALID_MODIFICATION);
@@ -1337,7 +1326,7 @@ TEST_F(PeerConnectionEncodingsIntegrationTest,
SetParametersRejectsUnknownCodecParameterVideo) {
rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
- webrtc::RtpCodec dummy_codec;
+ RtpCodec dummy_codec;
dummy_codec.kind = cricket::MEDIA_TYPE_VIDEO;
dummy_codec.name = "FOOBAR";
dummy_codec.clock_rate = 90000;
@@ -1348,8 +1337,7 @@ TEST_F(PeerConnectionEncodingsIntegrationTest,
rtc::scoped_refptr<RtpTransceiverInterface> video_transceiver =
transceiver_or_error.MoveValue();
- webrtc::RtpParameters parameters =
- video_transceiver->sender()->GetParameters();
+ RtpParameters parameters = video_transceiver->sender()->GetParameters();
parameters.encodings[0].codec = dummy_codec;
RTCError error = video_transceiver->sender()->SetParameters(parameters);
EXPECT_EQ(error.type(), RTCErrorType::INVALID_MODIFICATION);
@@ -1359,12 +1347,12 @@ TEST_F(PeerConnectionEncodingsIntegrationTest,
SetParametersRejectsNonPreferredCodecParameterAudio) {
rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
- absl::optional<webrtc::RtpCodecCapability> opus =
+ absl::optional<RtpCodecCapability> opus =
local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_AUDIO,
"opus");
ASSERT_TRUE(opus);
- std::vector<webrtc::RtpCodecCapability> not_opus_codecs =
+ std::vector<RtpCodecCapability> not_opus_codecs =
local_pc_wrapper->pc_factory()
->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_AUDIO)
.codecs;
@@ -1382,8 +1370,7 @@ TEST_F(PeerConnectionEncodingsIntegrationTest,
transceiver_or_error.MoveValue();
ASSERT_TRUE(audio_transceiver->SetCodecPreferences(not_opus_codecs).ok());
- webrtc::RtpParameters parameters =
- audio_transceiver->sender()->GetParameters();
+ RtpParameters parameters = audio_transceiver->sender()->GetParameters();
parameters.encodings[0].codec = opus;
RTCError error = audio_transceiver->sender()->SetParameters(parameters);
EXPECT_EQ(error.type(), RTCErrorType::INVALID_MODIFICATION);
@@ -1393,12 +1380,12 @@ TEST_F(PeerConnectionEncodingsIntegrationTest,
SetParametersRejectsNonPreferredCodecParameterVideo) {
rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
- absl::optional<webrtc::RtpCodecCapability> vp8 =
+ absl::optional<RtpCodecCapability> vp8 =
local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_VIDEO,
"vp8");
ASSERT_TRUE(vp8);
- std::vector<webrtc::RtpCodecCapability> not_vp8_codecs =
+ std::vector<RtpCodecCapability> not_vp8_codecs =
local_pc_wrapper->pc_factory()
->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_VIDEO)
.codecs;
@@ -1416,8 +1403,7 @@ TEST_F(PeerConnectionEncodingsIntegrationTest,
transceiver_or_error.MoveValue();
ASSERT_TRUE(video_transceiver->SetCodecPreferences(not_vp8_codecs).ok());
- webrtc::RtpParameters parameters =
- video_transceiver->sender()->GetParameters();
+ RtpParameters parameters = video_transceiver->sender()->GetParameters();
parameters.encodings[0].codec = vp8;
RTCError error = video_transceiver->sender()->SetParameters(parameters);
EXPECT_EQ(error.type(), RTCErrorType::INVALID_MODIFICATION);
@@ -1429,12 +1415,12 @@ TEST_F(PeerConnectionEncodingsIntegrationTest,
rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
- absl::optional<webrtc::RtpCodecCapability> opus =
+ absl::optional<RtpCodecCapability> opus =
local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_AUDIO,
"opus");
ASSERT_TRUE(opus);
- std::vector<webrtc::RtpCodecCapability> not_opus_codecs =
+ std::vector<RtpCodecCapability> not_opus_codecs =
local_pc_wrapper->pc_factory()
->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_AUDIO)
.codecs;
@@ -1456,8 +1442,7 @@ TEST_F(PeerConnectionEncodingsIntegrationTest,
local_pc_wrapper->WaitForConnection();
remote_pc_wrapper->WaitForConnection();
- webrtc::RtpParameters parameters =
- audio_transceiver->sender()->GetParameters();
+ RtpParameters parameters = audio_transceiver->sender()->GetParameters();
parameters.encodings[0].codec = opus;
RTCError error = audio_transceiver->sender()->SetParameters(parameters);
EXPECT_EQ(error.type(), RTCErrorType::INVALID_MODIFICATION);
@@ -1469,12 +1454,12 @@ TEST_F(PeerConnectionEncodingsIntegrationTest,
rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
- absl::optional<webrtc::RtpCodecCapability> opus =
+ absl::optional<RtpCodecCapability> opus =
local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_AUDIO,
"opus");
ASSERT_TRUE(opus);
- std::vector<webrtc::RtpCodecCapability> not_opus_codecs =
+ std::vector<RtpCodecCapability> not_opus_codecs =
local_pc_wrapper->pc_factory()
->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_AUDIO)
.codecs;
@@ -1519,8 +1504,7 @@ TEST_F(PeerConnectionEncodingsIntegrationTest,
local_pc_wrapper->WaitForConnection();
remote_pc_wrapper->WaitForConnection();
- webrtc::RtpParameters parameters =
- audio_transceiver->sender()->GetParameters();
+ RtpParameters parameters = audio_transceiver->sender()->GetParameters();
parameters.encodings[0].codec = opus;
RTCError error = audio_transceiver->sender()->SetParameters(parameters);
EXPECT_EQ(error.type(), RTCErrorType::INVALID_MODIFICATION);
@@ -1532,12 +1516,12 @@ TEST_F(PeerConnectionEncodingsIntegrationTest,
rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
- absl::optional<webrtc::RtpCodecCapability> vp8 =
+ absl::optional<RtpCodecCapability> vp8 =
local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_VIDEO,
"vp8");
ASSERT_TRUE(vp8);
- std::vector<webrtc::RtpCodecCapability> not_vp8_codecs =
+ std::vector<RtpCodecCapability> not_vp8_codecs =
local_pc_wrapper->pc_factory()
->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_VIDEO)
.codecs;
@@ -1559,8 +1543,7 @@ TEST_F(PeerConnectionEncodingsIntegrationTest,
local_pc_wrapper->WaitForConnection();
remote_pc_wrapper->WaitForConnection();
- webrtc::RtpParameters parameters =
- video_transceiver->sender()->GetParameters();
+ RtpParameters parameters = video_transceiver->sender()->GetParameters();
parameters.encodings[0].codec = vp8;
RTCError error = video_transceiver->sender()->SetParameters(parameters);
EXPECT_EQ(error.type(), RTCErrorType::INVALID_MODIFICATION);
@@ -1572,12 +1555,12 @@ TEST_F(PeerConnectionEncodingsIntegrationTest,
rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
- absl::optional<webrtc::RtpCodecCapability> vp8 =
+ absl::optional<RtpCodecCapability> vp8 =
local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_VIDEO,
"vp8");
ASSERT_TRUE(vp8);
- std::vector<webrtc::RtpCodecCapability> not_vp8_codecs =
+ std::vector<RtpCodecCapability> not_vp8_codecs =
local_pc_wrapper->pc_factory()
->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_VIDEO)
.codecs;
@@ -1622,8 +1605,7 @@ TEST_F(PeerConnectionEncodingsIntegrationTest,
local_pc_wrapper->WaitForConnection();
remote_pc_wrapper->WaitForConnection();
- webrtc::RtpParameters parameters =
- video_transceiver->sender()->GetParameters();
+ RtpParameters parameters = video_transceiver->sender()->GetParameters();
parameters.encodings[0].codec = vp8;
RTCError error = video_transceiver->sender()->SetParameters(parameters);
EXPECT_EQ(error.type(), RTCErrorType::INVALID_MODIFICATION);
@@ -1635,12 +1617,12 @@ TEST_F(PeerConnectionEncodingsIntegrationTest,
rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
- absl::optional<webrtc::RtpCodecCapability> opus =
+ absl::optional<RtpCodecCapability> opus =
local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_AUDIO,
"opus");
ASSERT_TRUE(opus);
- std::vector<webrtc::RtpCodecCapability> not_opus_codecs =
+ std::vector<RtpCodecCapability> not_opus_codecs =
local_pc_wrapper->pc_factory()
->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_AUDIO)
.codecs;
@@ -1651,9 +1633,9 @@ TEST_F(PeerConnectionEncodingsIntegrationTest,
}),
not_opus_codecs.end());
- webrtc::RtpTransceiverInit init;
- init.direction = webrtc::RtpTransceiverDirection::kSendOnly;
- webrtc::RtpEncodingParameters encoding_parameters;
+ RtpTransceiverInit init;
+ init.direction = RtpTransceiverDirection::kSendOnly;
+ RtpEncodingParameters encoding_parameters;
encoding_parameters.codec = opus;
init.send_encodings.push_back(encoding_parameters);
@@ -1667,8 +1649,7 @@ TEST_F(PeerConnectionEncodingsIntegrationTest,
local_pc_wrapper->WaitForConnection();
remote_pc_wrapper->WaitForConnection();
- webrtc::RtpParameters parameters =
- audio_transceiver->sender()->GetParameters();
+ RtpParameters parameters = audio_transceiver->sender()->GetParameters();
EXPECT_EQ(parameters.encodings[0].codec, opus);
ASSERT_TRUE(audio_transceiver->SetCodecPreferences(not_opus_codecs).ok());
@@ -1684,24 +1665,24 @@ TEST_F(PeerConnectionEncodingsIntegrationTest,
rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
- std::vector<webrtc::RtpCodecCapability> send_codecs =
+ std::vector<RtpCodecCapability> send_codecs =
local_pc_wrapper->pc_factory()
->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_AUDIO)
.codecs;
- absl::optional<webrtc::RtpCodecCapability> opus =
+ absl::optional<RtpCodecCapability> opus =
local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_AUDIO,
"opus");
ASSERT_TRUE(opus);
- absl::optional<webrtc::RtpCodecCapability> red =
+ absl::optional<RtpCodecCapability> red =
local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_AUDIO,
"red");
ASSERT_TRUE(red);
- webrtc::RtpTransceiverInit init;
- init.direction = webrtc::RtpTransceiverDirection::kSendOnly;
- webrtc::RtpEncodingParameters encoding_parameters;
+ RtpTransceiverInit init;
+ init.direction = RtpTransceiverDirection::kSendOnly;
+ RtpEncodingParameters encoding_parameters;
encoding_parameters.codec = opus;
init.send_encodings.push_back(encoding_parameters);
@@ -1720,8 +1701,7 @@ TEST_F(PeerConnectionEncodingsIntegrationTest,
local_pc_wrapper->WaitForConnection();
remote_pc_wrapper->WaitForConnection();
- webrtc::RtpParameters parameters =
- audio_transceiver->sender()->GetParameters();
+ RtpParameters parameters = audio_transceiver->sender()->GetParameters();
EXPECT_EQ(parameters.encodings[0].codec, opus);
EXPECT_EQ(parameters.codecs[0].payload_type, red->preferred_payload_type);
EXPECT_EQ(parameters.codecs[0].name, red->name);
@@ -1743,14 +1723,14 @@ TEST_F(PeerConnectionEncodingsIntegrationTest,
SetParametersRejectsScalabilityModeForSelectedCodec) {
rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
- absl::optional<webrtc::RtpCodecCapability> vp8 =
+ absl::optional<RtpCodecCapability> vp8 =
local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_VIDEO,
"vp8");
ASSERT_TRUE(vp8);
- webrtc::RtpTransceiverInit init;
- init.direction = webrtc::RtpTransceiverDirection::kSendOnly;
- webrtc::RtpEncodingParameters encoding_parameters;
+ RtpTransceiverInit init;
+ init.direction = RtpTransceiverDirection::kSendOnly;
+ RtpEncodingParameters encoding_parameters;
encoding_parameters.codec = vp8;
encoding_parameters.scalability_mode = "L1T3";
init.send_encodings.push_back(encoding_parameters);
@@ -1761,8 +1741,7 @@ TEST_F(PeerConnectionEncodingsIntegrationTest,
rtc::scoped_refptr<RtpTransceiverInterface> video_transceiver =
transceiver_or_error.MoveValue();
- webrtc::RtpParameters parameters =
- video_transceiver->sender()->GetParameters();
+ RtpParameters parameters = video_transceiver->sender()->GetParameters();
parameters.encodings[0].scalability_mode = "L3T3";
RTCError error = video_transceiver->sender()->SetParameters(parameters);
EXPECT_EQ(error.type(), RTCErrorType::INVALID_MODIFICATION);
@@ -1774,12 +1753,12 @@ TEST_F(PeerConnectionEncodingsIntegrationTest,
rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
- absl::optional<webrtc::RtpCodecCapability> vp8 =
+ absl::optional<RtpCodecCapability> vp8 =
local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_VIDEO,
"vp8");
ASSERT_TRUE(vp8);
- std::vector<webrtc::RtpCodecCapability> not_vp8_codecs =
+ std::vector<RtpCodecCapability> not_vp8_codecs =
local_pc_wrapper->pc_factory()
->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_VIDEO)
.codecs;
@@ -1790,9 +1769,9 @@ TEST_F(PeerConnectionEncodingsIntegrationTest,
}),
not_vp8_codecs.end());
- webrtc::RtpTransceiverInit init;
- init.direction = webrtc::RtpTransceiverDirection::kSendOnly;
- webrtc::RtpEncodingParameters encoding_parameters;
+ RtpTransceiverInit init;
+ init.direction = RtpTransceiverDirection::kSendOnly;
+ RtpEncodingParameters encoding_parameters;
encoding_parameters.rid = "h";
encoding_parameters.codec = vp8;
encoding_parameters.scale_resolution_down_by = 2;
@@ -1811,8 +1790,7 @@ TEST_F(PeerConnectionEncodingsIntegrationTest,
local_pc_wrapper->WaitForConnection();
remote_pc_wrapper->WaitForConnection();
- webrtc::RtpParameters parameters =
- video_transceiver->sender()->GetParameters();
+ RtpParameters parameters = video_transceiver->sender()->GetParameters();
ASSERT_EQ(parameters.encodings.size(), 2u);
EXPECT_EQ(parameters.encodings[0].codec, vp8);
EXPECT_EQ(parameters.encodings[1].codec, vp8);
@@ -1833,17 +1811,17 @@ TEST_F(PeerConnectionEncodingsIntegrationTest,
rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
- absl::optional<webrtc::RtpCodecCapability> vp8 =
+ absl::optional<RtpCodecCapability> vp8 =
local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_VIDEO,
"vp8");
ASSERT_TRUE(vp8);
- absl::optional<webrtc::RtpCodecCapability> vp9 =
+ absl::optional<RtpCodecCapability> vp9 =
local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_VIDEO,
"vp9");
- webrtc::RtpTransceiverInit init;
- init.direction = webrtc::RtpTransceiverDirection::kSendOnly;
- webrtc::RtpEncodingParameters encoding_parameters;
+ RtpTransceiverInit init;
+ init.direction = RtpTransceiverDirection::kSendOnly;
+ RtpEncodingParameters encoding_parameters;
encoding_parameters.rid = "h";
encoding_parameters.codec = vp8;
encoding_parameters.scale_resolution_down_by = 2;
diff --git a/third_party/libwebrtc/pc/peer_connection_factory.cc b/third_party/libwebrtc/pc/peer_connection_factory.cc
index 81780cf51e..8ce44d374f 100644
--- a/third_party/libwebrtc/pc/peer_connection_factory.cc
+++ b/third_party/libwebrtc/pc/peer_connection_factory.cc
@@ -14,8 +14,8 @@
#include <utility>
#include "absl/strings/match.h"
-#include "api/async_resolver_factory.h"
-#include "api/call/call_factory_interface.h"
+#include "api/environment/environment.h"
+#include "api/environment/environment_factory.h"
#include "api/fec_controller.h"
#include "api/ice_transport_interface.h"
#include "api/network_state_predictor.h"
@@ -27,13 +27,13 @@
#include "call/audio_state.h"
#include "call/rtp_transport_controller_send_factory.h"
#include "media/base/media_engine.h"
-#include "p2p/base/basic_async_resolver_factory.h"
#include "p2p/base/basic_packet_socket_factory.h"
#include "p2p/base/default_ice_transport_factory.h"
#include "p2p/base/port_allocator.h"
#include "p2p/client/basic_port_allocator.h"
#include "pc/audio_track.h"
#include "pc/local_audio_source.h"
+#include "pc/media_factory.h"
#include "pc/media_stream.h"
#include "pc/media_stream_proxy.h"
#include "pc/media_stream_track_proxy.h"
@@ -78,7 +78,10 @@ CreateModularPeerConnectionFactory(
// Static
rtc::scoped_refptr<PeerConnectionFactory> PeerConnectionFactory::Create(
PeerConnectionFactoryDependencies dependencies) {
- auto context = ConnectionContext::Create(&dependencies);
+ auto context = ConnectionContext::Create(
+ CreateEnvironment(std::move(dependencies.trials),
+ std::move(dependencies.task_queue_factory)),
+ &dependencies);
if (!context) {
return nullptr;
}
@@ -89,7 +92,6 @@ PeerConnectionFactory::PeerConnectionFactory(
rtc::scoped_refptr<ConnectionContext> context,
PeerConnectionFactoryDependencies* dependencies)
: context_(context),
- task_queue_factory_(std::move(dependencies->task_queue_factory)),
event_log_factory_(std::move(dependencies->event_log_factory)),
fec_controller_factory_(std::move(dependencies->fec_controller_factory)),
network_state_predictor_factory_(
@@ -105,8 +107,12 @@ PeerConnectionFactory::PeerConnectionFactory(
PeerConnectionFactory::PeerConnectionFactory(
PeerConnectionFactoryDependencies dependencies)
- : PeerConnectionFactory(ConnectionContext::Create(&dependencies),
- &dependencies) {}
+ : PeerConnectionFactory(
+ ConnectionContext::Create(
+ CreateEnvironment(std::move(dependencies.trials),
+ std::move(dependencies.task_queue_factory)),
+ &dependencies),
+ &dependencies) {}
PeerConnectionFactory::~PeerConnectionFactory() {
RTC_DCHECK_RUN_ON(signaling_thread());
@@ -205,6 +211,23 @@ PeerConnectionFactory::CreatePeerConnectionOrError(
PeerConnectionDependencies dependencies) {
RTC_DCHECK_RUN_ON(signaling_thread());
+ EnvironmentFactory env_factory(context_->env());
+
+ // Field trials active for this PeerConnection is the first of:
+ // a) Specified in the PeerConnectionDependencies
+ // b) Specified in the PeerConnectionFactoryDependencies
+ // c) Created as default by the EnvironmentFactory.
+ env_factory.Set(std::move(dependencies.trials));
+
+ if (event_log_factory_ != nullptr) {
+ worker_thread()->BlockingCall([&] {
+ Environment env_for_rtc_event_log = env_factory.Create();
+ env_factory.Set(event_log_factory_->Create(env_for_rtc_event_log));
+ });
+ }
+
+ const Environment env = env_factory.Create();
+
// Set internal defaults if optional dependencies are not set.
if (!dependencies.cert_generator) {
dependencies.cert_generator =
@@ -212,11 +235,10 @@ PeerConnectionFactory::CreatePeerConnectionOrError(
network_thread());
}
if (!dependencies.allocator) {
- const FieldTrialsView* trials =
- dependencies.trials ? dependencies.trials.get() : &field_trials();
dependencies.allocator = std::make_unique<cricket::BasicPortAllocator>(
context_->default_network_manager(), context_->default_socket_factory(),
- configuration.turn_customizer, /*relay_port_factory=*/nullptr, trials);
+ configuration.turn_customizer, /*relay_port_factory=*/nullptr,
+ &env.field_trials());
dependencies.allocator->SetPortRange(
configuration.port_allocator_config.min_port,
configuration.port_allocator_config.max_port);
@@ -232,19 +254,13 @@ PeerConnectionFactory::CreatePeerConnectionOrError(
dependencies.allocator->SetNetworkIgnoreMask(options().network_ignore_mask);
dependencies.allocator->SetVpnList(configuration.vpn_list);
- std::unique_ptr<RtcEventLog> event_log =
- worker_thread()->BlockingCall([this] { return CreateRtcEventLog_w(); });
-
- const FieldTrialsView* trials =
- dependencies.trials ? dependencies.trials.get() : &field_trials();
std::unique_ptr<Call> call =
- worker_thread()->BlockingCall([this, &event_log, trials, &configuration] {
- return CreateCall_w(event_log.get(), *trials, configuration);
+ worker_thread()->BlockingCall([this, &env, &configuration] {
+ return CreateCall_w(env, configuration);
});
- auto result = PeerConnection::Create(context_, options_, std::move(event_log),
- std::move(call), configuration,
- std::move(dependencies));
+ auto result = PeerConnection::Create(env, context_, options_, std::move(call),
+ configuration, std::move(dependencies));
if (!result.ok()) {
return result.MoveError();
}
@@ -285,23 +301,12 @@ rtc::scoped_refptr<AudioTrackInterface> PeerConnectionFactory::CreateAudioTrack(
return AudioTrackProxy::Create(signaling_thread(), track);
}
-std::unique_ptr<RtcEventLog> PeerConnectionFactory::CreateRtcEventLog_w() {
- RTC_DCHECK_RUN_ON(worker_thread());
-
- auto encoding_type = RtcEventLog::EncodingType::NewFormat;
- if (field_trials().IsDisabled("WebRTC-RtcEventLogNewFormat"))
- encoding_type = RtcEventLog::EncodingType::Legacy;
- return event_log_factory_ ? event_log_factory_->Create(encoding_type)
- : std::make_unique<RtcEventLogNull>();
-}
-
std::unique_ptr<Call> PeerConnectionFactory::CreateCall_w(
- RtcEventLog* event_log,
- const FieldTrialsView& field_trials,
+ const Environment& env,
const PeerConnectionInterface::RTCConfiguration& configuration) {
RTC_DCHECK_RUN_ON(worker_thread());
- CallConfig call_config(event_log, network_thread());
+ CallConfig call_config(env, network_thread());
if (!media_engine() || !context_->call_factory()) {
return nullptr;
}
@@ -314,7 +319,7 @@ std::unique_ptr<Call> PeerConnectionFactory::CreateCall_w(
FieldTrialParameter<DataRate> max_bandwidth("max",
DataRate::KilobitsPerSec(2000));
ParseFieldTrial({&min_bandwidth, &start_bandwidth, &max_bandwidth},
- field_trials.Lookup("WebRTC-PcFactoryDefaultBitrates"));
+ env.field_trials().Lookup("WebRTC-PcFactoryDefaultBitrates"));
call_config.bitrate_config.min_bitrate_bps =
rtc::saturated_cast<int>(min_bandwidth->bps());
@@ -324,7 +329,6 @@ std::unique_ptr<Call> PeerConnectionFactory::CreateCall_w(
rtc::saturated_cast<int>(max_bandwidth->bps());
call_config.fec_controller_factory = fec_controller_factory_.get();
- call_config.task_queue_factory = task_queue_factory_.get();
call_config.network_state_predictor_factory =
network_state_predictor_factory_.get();
call_config.neteq_factory = neteq_factory_.get();
@@ -337,11 +341,9 @@ std::unique_ptr<Call> PeerConnectionFactory::CreateCall_w(
RTC_LOG(LS_INFO) << "Using default network controller factory";
}
- call_config.trials = &field_trials;
call_config.rtp_transport_controller_send_factory =
transport_controller_send_factory_.get();
call_config.metronome = metronome_.get();
- call_config.pacer_burst_interval = configuration.pacer_burst_interval;
return context_->call_factory()->CreateCall(call_config);
}
diff --git a/third_party/libwebrtc/pc/peer_connection_factory.h b/third_party/libwebrtc/pc/peer_connection_factory.h
index f55d09f6d8..c3760c02c9 100644
--- a/third_party/libwebrtc/pc/peer_connection_factory.h
+++ b/third_party/libwebrtc/pc/peer_connection_factory.h
@@ -29,12 +29,10 @@
#include "api/network_state_predictor.h"
#include "api/peer_connection_interface.h"
#include "api/rtc_error.h"
-#include "api/rtc_event_log/rtc_event_log.h"
#include "api/rtc_event_log/rtc_event_log_factory_interface.h"
#include "api/rtp_parameters.h"
#include "api/scoped_refptr.h"
#include "api/sequence_checker.h"
-#include "api/task_queue/task_queue_factory.h"
#include "api/transport/network_control.h"
#include "api/transport/sctp_transport_factory_interface.h"
#include "call/call.h"
@@ -53,8 +51,6 @@ class BasicPacketSocketFactory;
namespace webrtc {
-class RtcEventLog;
-
class PeerConnectionFactory : public PeerConnectionFactoryInterface {
public:
// Creates a PeerConnectionFactory. It returns nullptr on initialization
@@ -135,16 +131,13 @@ class PeerConnectionFactory : public PeerConnectionFactoryInterface {
bool IsTrialEnabled(absl::string_view key) const;
- std::unique_ptr<RtcEventLog> CreateRtcEventLog_w();
std::unique_ptr<Call> CreateCall_w(
- RtcEventLog* event_log,
- const FieldTrialsView& field_trials,
+ const Environment& env,
const PeerConnectionInterface::RTCConfiguration& configuration);
rtc::scoped_refptr<ConnectionContext> context_;
PeerConnectionFactoryInterface::Options options_
RTC_GUARDED_BY(signaling_thread());
- std::unique_ptr<TaskQueueFactory> task_queue_factory_;
std::unique_ptr<RtcEventLogFactoryInterface> event_log_factory_;
std::unique_ptr<FecControllerFactoryInterface> fec_controller_factory_;
std::unique_ptr<NetworkStatePredictorFactoryInterface>
diff --git a/third_party/libwebrtc/pc/peer_connection_factory_proxy.h b/third_party/libwebrtc/pc/peer_connection_factory_proxy.h
index 4781497642..b9bd1cbf0e 100644
--- a/third_party/libwebrtc/pc/peer_connection_factory_proxy.h
+++ b/third_party/libwebrtc/pc/peer_connection_factory_proxy.h
@@ -29,10 +29,10 @@ PROXY_METHOD2(RTCErrorOr<rtc::scoped_refptr<PeerConnectionInterface>>,
CreatePeerConnectionOrError,
const PeerConnectionInterface::RTCConfiguration&,
PeerConnectionDependencies)
-PROXY_CONSTMETHOD1(webrtc::RtpCapabilities,
+PROXY_CONSTMETHOD1(RtpCapabilities,
GetRtpSenderCapabilities,
cricket::MediaType)
-PROXY_CONSTMETHOD1(webrtc::RtpCapabilities,
+PROXY_CONSTMETHOD1(RtpCapabilities,
GetRtpReceiverCapabilities,
cricket::MediaType)
PROXY_METHOD1(rtc::scoped_refptr<MediaStreamInterface>,
diff --git a/third_party/libwebrtc/pc/peer_connection_factory_unittest.cc b/third_party/libwebrtc/pc/peer_connection_factory_unittest.cc
index 11e232c01f..618ac68388 100644
--- a/third_party/libwebrtc/pc/peer_connection_factory_unittest.cc
+++ b/third_party/libwebrtc/pc/peer_connection_factory_unittest.cc
@@ -20,6 +20,8 @@
#include "api/audio_codecs/builtin_audio_encoder_factory.h"
#include "api/create_peerconnection_factory.h"
#include "api/data_channel_interface.h"
+#include "api/enable_media.h"
+#include "api/environment/environment_factory.h"
#include "api/jsep.h"
#include "api/media_stream_interface.h"
#include "api/task_queue/default_task_queue_factory.h"
@@ -35,7 +37,6 @@
#include "api/video_codecs/video_encoder_factory_template_libvpx_vp9_adapter.h"
#include "api/video_codecs/video_encoder_factory_template_open_h264_adapter.h"
#include "media/base/fake_frame_source.h"
-#include "media/engine/webrtc_media_engine.h"
#include "modules/audio_device/include/audio_device.h"
#include "modules/audio_processing/include/audio_processing.h"
#include "p2p/base/fake_port_allocator.h"
@@ -106,8 +107,7 @@ class NullPeerConnectionObserver : public PeerConnectionObserver {
PeerConnectionInterface::IceConnectionState new_state) override {}
void OnIceGatheringChange(
PeerConnectionInterface::IceGatheringState new_state) override {}
- void OnIceCandidate(const webrtc::IceCandidateInterface* candidate) override {
- }
+ void OnIceCandidate(const IceCandidateInterface* candidate) override {}
};
class MockNetworkManager : public rtc::NetworkManager {
@@ -133,17 +133,15 @@ class PeerConnectionFactoryTest : public ::testing::Test {
private:
void SetUp() {
#ifdef WEBRTC_ANDROID
- webrtc::InitializeAndroidObjects();
+ InitializeAndroidObjects();
#endif
// Use fake audio device module since we're only testing the interface
// level, and using a real one could make tests flaky e.g. when run in
// parallel.
- factory_ = webrtc::CreatePeerConnectionFactory(
+ factory_ = CreatePeerConnectionFactory(
rtc::Thread::Current(), rtc::Thread::Current(), rtc::Thread::Current(),
- rtc::scoped_refptr<webrtc::AudioDeviceModule>(
- FakeAudioCaptureModule::Create()),
- webrtc::CreateBuiltinAudioEncoderFactory(),
- webrtc::CreateBuiltinAudioDecoderFactory(),
+ rtc::scoped_refptr<AudioDeviceModule>(FakeAudioCaptureModule::Create()),
+ CreateBuiltinAudioEncoderFactory(), CreateBuiltinAudioDecoderFactory(),
std::make_unique<VideoEncoderFactoryTemplate<
LibvpxVp8EncoderTemplateAdapter, LibvpxVp9EncoderTemplateAdapter,
OpenH264EncoderTemplateAdapter, LibaomAv1EncoderTemplateAdapter>>(),
@@ -182,64 +180,64 @@ class PeerConnectionFactoryTest : public ::testing::Test {
}
}
- void VerifyAudioCodecCapability(const webrtc::RtpCodecCapability& codec) {
+ void VerifyAudioCodecCapability(const RtpCodecCapability& codec) {
EXPECT_EQ(codec.kind, cricket::MEDIA_TYPE_AUDIO);
EXPECT_FALSE(codec.name.empty());
EXPECT_GT(codec.clock_rate, 0);
EXPECT_GT(codec.num_channels, 0);
}
- void VerifyVideoCodecCapability(const webrtc::RtpCodecCapability& codec,
+ void VerifyVideoCodecCapability(const RtpCodecCapability& codec,
bool sender) {
EXPECT_EQ(codec.kind, cricket::MEDIA_TYPE_VIDEO);
EXPECT_FALSE(codec.name.empty());
EXPECT_GT(codec.clock_rate, 0);
if (sender) {
if (codec.name == "VP8" || codec.name == "H264") {
- EXPECT_THAT(codec.scalability_modes,
- UnorderedElementsAre(webrtc::ScalabilityMode::kL1T1,
- webrtc::ScalabilityMode::kL1T2,
- webrtc::ScalabilityMode::kL1T3))
+ EXPECT_THAT(
+ codec.scalability_modes,
+ UnorderedElementsAre(ScalabilityMode::kL1T1, ScalabilityMode::kL1T2,
+ ScalabilityMode::kL1T3))
<< "Codec: " << codec.name;
} else if (codec.name == "VP9" || codec.name == "AV1") {
EXPECT_THAT(
codec.scalability_modes,
UnorderedElementsAre(
// clang-format off
- webrtc::ScalabilityMode::kL1T1,
- webrtc::ScalabilityMode::kL1T2,
- webrtc::ScalabilityMode::kL1T3,
- webrtc::ScalabilityMode::kL2T1,
- webrtc::ScalabilityMode::kL2T1h,
- webrtc::ScalabilityMode::kL2T1_KEY,
- webrtc::ScalabilityMode::kL2T2,
- webrtc::ScalabilityMode::kL2T2h,
- webrtc::ScalabilityMode::kL2T2_KEY,
- webrtc::ScalabilityMode::kL2T2_KEY_SHIFT,
- webrtc::ScalabilityMode::kL2T3,
- webrtc::ScalabilityMode::kL2T3h,
- webrtc::ScalabilityMode::kL2T3_KEY,
- webrtc::ScalabilityMode::kL3T1,
- webrtc::ScalabilityMode::kL3T1h,
- webrtc::ScalabilityMode::kL3T1_KEY,
- webrtc::ScalabilityMode::kL3T2,
- webrtc::ScalabilityMode::kL3T2h,
- webrtc::ScalabilityMode::kL3T2_KEY,
- webrtc::ScalabilityMode::kL3T3,
- webrtc::ScalabilityMode::kL3T3h,
- webrtc::ScalabilityMode::kL3T3_KEY,
- webrtc::ScalabilityMode::kS2T1,
- webrtc::ScalabilityMode::kS2T1h,
- webrtc::ScalabilityMode::kS2T2,
- webrtc::ScalabilityMode::kS2T2h,
- webrtc::ScalabilityMode::kS2T3,
- webrtc::ScalabilityMode::kS2T3h,
- webrtc::ScalabilityMode::kS3T1,
- webrtc::ScalabilityMode::kS3T1h,
- webrtc::ScalabilityMode::kS3T2,
- webrtc::ScalabilityMode::kS3T2h,
- webrtc::ScalabilityMode::kS3T3,
- webrtc::ScalabilityMode::kS3T3h)
+ ScalabilityMode::kL1T1,
+ ScalabilityMode::kL1T2,
+ ScalabilityMode::kL1T3,
+ ScalabilityMode::kL2T1,
+ ScalabilityMode::kL2T1h,
+ ScalabilityMode::kL2T1_KEY,
+ ScalabilityMode::kL2T2,
+ ScalabilityMode::kL2T2h,
+ ScalabilityMode::kL2T2_KEY,
+ ScalabilityMode::kL2T2_KEY_SHIFT,
+ ScalabilityMode::kL2T3,
+ ScalabilityMode::kL2T3h,
+ ScalabilityMode::kL2T3_KEY,
+ ScalabilityMode::kL3T1,
+ ScalabilityMode::kL3T1h,
+ ScalabilityMode::kL3T1_KEY,
+ ScalabilityMode::kL3T2,
+ ScalabilityMode::kL3T2h,
+ ScalabilityMode::kL3T2_KEY,
+ ScalabilityMode::kL3T3,
+ ScalabilityMode::kL3T3h,
+ ScalabilityMode::kL3T3_KEY,
+ ScalabilityMode::kS2T1,
+ ScalabilityMode::kS2T1h,
+ ScalabilityMode::kS2T2,
+ ScalabilityMode::kS2T2h,
+ ScalabilityMode::kS2T3,
+ ScalabilityMode::kS2T3h,
+ ScalabilityMode::kS3T1,
+ ScalabilityMode::kS3T1h,
+ ScalabilityMode::kS3T2,
+ ScalabilityMode::kS3T2h,
+ ScalabilityMode::kS3T3,
+ ScalabilityMode::kS3T3h)
// clang-format on
)
<< "Codec: " << codec.name;
@@ -251,7 +249,7 @@ class PeerConnectionFactoryTest : public ::testing::Test {
}
}
- webrtc::test::ScopedKeyValueConfig field_trials_;
+ test::ScopedKeyValueConfig field_trials_;
std::unique_ptr<rtc::SocketServer> socket_server_;
rtc::AutoSocketServerThread main_thread_;
rtc::scoped_refptr<PeerConnectionFactoryInterface> factory_;
@@ -267,37 +265,27 @@ class PeerConnectionFactoryTest : public ::testing::Test {
// to reconstruct factory with our own ConnectionContext.
rtc::scoped_refptr<PeerConnectionFactoryInterface>
CreatePeerConnectionFactoryWithRtxDisabled() {
- webrtc::PeerConnectionFactoryDependencies pcf_dependencies;
+ PeerConnectionFactoryDependencies pcf_dependencies;
pcf_dependencies.signaling_thread = rtc::Thread::Current();
pcf_dependencies.worker_thread = rtc::Thread::Current();
pcf_dependencies.network_thread = rtc::Thread::Current();
pcf_dependencies.task_queue_factory = CreateDefaultTaskQueueFactory();
- pcf_dependencies.call_factory = CreateCallFactory();
- pcf_dependencies.trials = std::make_unique<webrtc::FieldTrialBasedConfig>();
-
- cricket::MediaEngineDependencies media_dependencies;
- media_dependencies.task_queue_factory =
- pcf_dependencies.task_queue_factory.get();
- media_dependencies.adm = rtc::scoped_refptr<webrtc::AudioDeviceModule>(
- FakeAudioCaptureModule::Create());
- media_dependencies.audio_encoder_factory =
- webrtc::CreateBuiltinAudioEncoderFactory();
- media_dependencies.audio_decoder_factory =
- webrtc::CreateBuiltinAudioDecoderFactory();
- media_dependencies.video_encoder_factory =
+
+ pcf_dependencies.adm = FakeAudioCaptureModule::Create();
+ pcf_dependencies.audio_encoder_factory = CreateBuiltinAudioEncoderFactory();
+ pcf_dependencies.audio_decoder_factory = CreateBuiltinAudioDecoderFactory();
+ pcf_dependencies.video_encoder_factory =
std::make_unique<VideoEncoderFactoryTemplate<
LibvpxVp8EncoderTemplateAdapter, LibvpxVp9EncoderTemplateAdapter,
OpenH264EncoderTemplateAdapter, LibaomAv1EncoderTemplateAdapter>>();
- media_dependencies.video_decoder_factory =
+ pcf_dependencies.video_decoder_factory =
std::make_unique<VideoDecoderFactoryTemplate<
LibvpxVp8DecoderTemplateAdapter, LibvpxVp9DecoderTemplateAdapter,
OpenH264DecoderTemplateAdapter, Dav1dDecoderTemplateAdapter>>(),
- media_dependencies.trials = pcf_dependencies.trials.get();
- pcf_dependencies.media_engine =
- cricket::CreateMediaEngine(std::move(media_dependencies));
+ EnableMedia(pcf_dependencies);
- rtc::scoped_refptr<webrtc::ConnectionContext> context =
- ConnectionContext::Create(&pcf_dependencies);
+ rtc::scoped_refptr<ConnectionContext> context =
+ ConnectionContext::Create(CreateEnvironment(), &pcf_dependencies);
context->set_use_rtx(false);
return rtc::make_ref_counted<PeerConnectionFactory>(context,
&pcf_dependencies);
@@ -311,26 +299,26 @@ CreatePeerConnectionFactoryWithRtxDisabled() {
// See https://bugs.chromium.org/p/webrtc/issues/detail?id=7806 for details.
TEST(PeerConnectionFactoryTestInternal, DISABLED_CreatePCUsingInternalModules) {
#ifdef WEBRTC_ANDROID
- webrtc::InitializeAndroidObjects();
+ InitializeAndroidObjects();
#endif
rtc::scoped_refptr<PeerConnectionFactoryInterface> factory(
- webrtc::CreatePeerConnectionFactory(
+ CreatePeerConnectionFactory(
nullptr /* network_thread */, nullptr /* worker_thread */,
nullptr /* signaling_thread */, nullptr /* default_adm */,
- webrtc::CreateBuiltinAudioEncoderFactory(),
- webrtc::CreateBuiltinAudioDecoderFactory(),
+ CreateBuiltinAudioEncoderFactory(),
+ CreateBuiltinAudioDecoderFactory(),
nullptr /* video_encoder_factory */,
nullptr /* video_decoder_factory */, nullptr /* audio_mixer */,
nullptr /* audio_processing */));
NullPeerConnectionObserver observer;
- webrtc::PeerConnectionInterface::RTCConfiguration config;
- config.sdp_semantics = webrtc::SdpSemantics::kUnifiedPlan;
+ PeerConnectionInterface::RTCConfiguration config;
+ config.sdp_semantics = SdpSemantics::kUnifiedPlan;
std::unique_ptr<FakeRTCCertificateGenerator> cert_generator(
new FakeRTCCertificateGenerator());
- webrtc::PeerConnectionDependencies pc_dependencies(&observer);
+ PeerConnectionDependencies pc_dependencies(&observer);
pc_dependencies.cert_generator = std::move(cert_generator);
auto result =
factory->CreatePeerConnectionOrError(config, std::move(pc_dependencies));
@@ -339,7 +327,7 @@ TEST(PeerConnectionFactoryTestInternal, DISABLED_CreatePCUsingInternalModules) {
}
TEST_F(PeerConnectionFactoryTest, CheckRtpSenderAudioCapabilities) {
- webrtc::RtpCapabilities audio_capabilities =
+ RtpCapabilities audio_capabilities =
factory_->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_AUDIO);
EXPECT_FALSE(audio_capabilities.codecs.empty());
for (const auto& codec : audio_capabilities.codecs) {
@@ -352,7 +340,7 @@ TEST_F(PeerConnectionFactoryTest, CheckRtpSenderAudioCapabilities) {
}
TEST_F(PeerConnectionFactoryTest, CheckRtpSenderVideoCapabilities) {
- webrtc::RtpCapabilities video_capabilities =
+ RtpCapabilities video_capabilities =
factory_->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_VIDEO);
EXPECT_FALSE(video_capabilities.codecs.empty());
for (const auto& codec : video_capabilities.codecs) {
@@ -365,7 +353,7 @@ TEST_F(PeerConnectionFactoryTest, CheckRtpSenderVideoCapabilities) {
}
TEST_F(PeerConnectionFactoryTest, CheckRtpSenderRtxEnabledCapabilities) {
- webrtc::RtpCapabilities video_capabilities =
+ RtpCapabilities video_capabilities =
factory_->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_VIDEO);
const auto it = std::find_if(
video_capabilities.codecs.begin(), video_capabilities.codecs.end(),
@@ -375,7 +363,7 @@ TEST_F(PeerConnectionFactoryTest, CheckRtpSenderRtxEnabledCapabilities) {
TEST(PeerConnectionFactoryTestInternal, CheckRtpSenderRtxDisabledCapabilities) {
auto factory = CreatePeerConnectionFactoryWithRtxDisabled();
- webrtc::RtpCapabilities video_capabilities =
+ RtpCapabilities video_capabilities =
factory->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_VIDEO);
const auto it = std::find_if(
video_capabilities.codecs.begin(), video_capabilities.codecs.end(),
@@ -384,14 +372,14 @@ TEST(PeerConnectionFactoryTestInternal, CheckRtpSenderRtxDisabledCapabilities) {
}
TEST_F(PeerConnectionFactoryTest, CheckRtpSenderDataCapabilities) {
- webrtc::RtpCapabilities data_capabilities =
+ RtpCapabilities data_capabilities =
factory_->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_DATA);
EXPECT_TRUE(data_capabilities.codecs.empty());
EXPECT_TRUE(data_capabilities.header_extensions.empty());
}
TEST_F(PeerConnectionFactoryTest, CheckRtpReceiverAudioCapabilities) {
- webrtc::RtpCapabilities audio_capabilities =
+ RtpCapabilities audio_capabilities =
factory_->GetRtpReceiverCapabilities(cricket::MEDIA_TYPE_AUDIO);
EXPECT_FALSE(audio_capabilities.codecs.empty());
for (const auto& codec : audio_capabilities.codecs) {
@@ -404,7 +392,7 @@ TEST_F(PeerConnectionFactoryTest, CheckRtpReceiverAudioCapabilities) {
}
TEST_F(PeerConnectionFactoryTest, CheckRtpReceiverVideoCapabilities) {
- webrtc::RtpCapabilities video_capabilities =
+ RtpCapabilities video_capabilities =
factory_->GetRtpReceiverCapabilities(cricket::MEDIA_TYPE_VIDEO);
EXPECT_FALSE(video_capabilities.codecs.empty());
for (const auto& codec : video_capabilities.codecs) {
@@ -417,7 +405,7 @@ TEST_F(PeerConnectionFactoryTest, CheckRtpReceiverVideoCapabilities) {
}
TEST_F(PeerConnectionFactoryTest, CheckRtpReceiverRtxEnabledCapabilities) {
- webrtc::RtpCapabilities video_capabilities =
+ RtpCapabilities video_capabilities =
factory_->GetRtpReceiverCapabilities(cricket::MEDIA_TYPE_VIDEO);
const auto it = std::find_if(
video_capabilities.codecs.begin(), video_capabilities.codecs.end(),
@@ -428,7 +416,7 @@ TEST_F(PeerConnectionFactoryTest, CheckRtpReceiverRtxEnabledCapabilities) {
TEST(PeerConnectionFactoryTestInternal,
CheckRtpReceiverRtxDisabledCapabilities) {
auto factory = CreatePeerConnectionFactoryWithRtxDisabled();
- webrtc::RtpCapabilities video_capabilities =
+ RtpCapabilities video_capabilities =
factory->GetRtpReceiverCapabilities(cricket::MEDIA_TYPE_VIDEO);
const auto it = std::find_if(
video_capabilities.codecs.begin(), video_capabilities.codecs.end(),
@@ -437,7 +425,7 @@ TEST(PeerConnectionFactoryTestInternal,
}
TEST_F(PeerConnectionFactoryTest, CheckRtpReceiverDataCapabilities) {
- webrtc::RtpCapabilities data_capabilities =
+ RtpCapabilities data_capabilities =
factory_->GetRtpReceiverCapabilities(cricket::MEDIA_TYPE_DATA);
EXPECT_TRUE(data_capabilities.codecs.empty());
EXPECT_TRUE(data_capabilities.header_extensions.empty());
@@ -447,8 +435,8 @@ TEST_F(PeerConnectionFactoryTest, CheckRtpReceiverDataCapabilities) {
// configuration. Also verifies the URL's parsed correctly as expected.
TEST_F(PeerConnectionFactoryTest, CreatePCUsingIceServers) {
PeerConnectionInterface::RTCConfiguration config;
- config.sdp_semantics = webrtc::SdpSemantics::kUnifiedPlan;
- webrtc::PeerConnectionInterface::IceServer ice_server;
+ config.sdp_semantics = SdpSemantics::kUnifiedPlan;
+ PeerConnectionInterface::IceServer ice_server;
ice_server.uri = kStunIceServer;
config.servers.push_back(ice_server);
ice_server.uri = kTurnIceServer;
@@ -459,7 +447,7 @@ TEST_F(PeerConnectionFactoryTest, CreatePCUsingIceServers) {
ice_server.username = kTurnUsername;
ice_server.password = kTurnPassword;
config.servers.push_back(ice_server);
- webrtc::PeerConnectionDependencies pc_dependencies(&observer_);
+ PeerConnectionDependencies pc_dependencies(&observer_);
pc_dependencies.cert_generator =
std::make_unique<FakeRTCCertificateGenerator>();
pc_dependencies.allocator = std::move(port_allocator_);
@@ -484,15 +472,15 @@ TEST_F(PeerConnectionFactoryTest, CreatePCUsingIceServers) {
// configuration. Also verifies the list of URL's parsed correctly as expected.
TEST_F(PeerConnectionFactoryTest, CreatePCUsingIceServersUrls) {
PeerConnectionInterface::RTCConfiguration config;
- config.sdp_semantics = webrtc::SdpSemantics::kUnifiedPlan;
- webrtc::PeerConnectionInterface::IceServer ice_server;
+ config.sdp_semantics = SdpSemantics::kUnifiedPlan;
+ PeerConnectionInterface::IceServer ice_server;
ice_server.urls.push_back(kStunIceServer);
ice_server.urls.push_back(kTurnIceServer);
ice_server.urls.push_back(kTurnIceServerWithTransport);
ice_server.username = kTurnUsername;
ice_server.password = kTurnPassword;
config.servers.push_back(ice_server);
- webrtc::PeerConnectionDependencies pc_dependencies(&observer_);
+ PeerConnectionDependencies pc_dependencies(&observer_);
pc_dependencies.cert_generator =
std::make_unique<FakeRTCCertificateGenerator>();
pc_dependencies.allocator = std::move(port_allocator_);
@@ -515,15 +503,15 @@ TEST_F(PeerConnectionFactoryTest, CreatePCUsingIceServersUrls) {
TEST_F(PeerConnectionFactoryTest, CreatePCUsingNoUsernameInUri) {
PeerConnectionInterface::RTCConfiguration config;
- config.sdp_semantics = webrtc::SdpSemantics::kUnifiedPlan;
- webrtc::PeerConnectionInterface::IceServer ice_server;
+ config.sdp_semantics = SdpSemantics::kUnifiedPlan;
+ PeerConnectionInterface::IceServer ice_server;
ice_server.uri = kStunIceServer;
config.servers.push_back(ice_server);
ice_server.uri = kTurnIceServerWithNoUsernameInUri;
ice_server.username = kTurnUsername;
ice_server.password = kTurnPassword;
config.servers.push_back(ice_server);
- webrtc::PeerConnectionDependencies pc_dependencies(&observer_);
+ PeerConnectionDependencies pc_dependencies(&observer_);
pc_dependencies.cert_generator =
std::make_unique<FakeRTCCertificateGenerator>();
pc_dependencies.allocator = std::move(port_allocator_);
@@ -541,13 +529,13 @@ TEST_F(PeerConnectionFactoryTest, CreatePCUsingNoUsernameInUri) {
// has transport parameter in it.
TEST_F(PeerConnectionFactoryTest, CreatePCUsingTurnUrlWithTransportParam) {
PeerConnectionInterface::RTCConfiguration config;
- config.sdp_semantics = webrtc::SdpSemantics::kUnifiedPlan;
- webrtc::PeerConnectionInterface::IceServer ice_server;
+ config.sdp_semantics = SdpSemantics::kUnifiedPlan;
+ PeerConnectionInterface::IceServer ice_server;
ice_server.uri = kTurnIceServerWithTransport;
ice_server.username = kTurnUsername;
ice_server.password = kTurnPassword;
config.servers.push_back(ice_server);
- webrtc::PeerConnectionDependencies pc_dependencies(&observer_);
+ PeerConnectionDependencies pc_dependencies(&observer_);
pc_dependencies.cert_generator =
std::make_unique<FakeRTCCertificateGenerator>();
pc_dependencies.allocator = std::move(port_allocator_);
@@ -563,8 +551,8 @@ TEST_F(PeerConnectionFactoryTest, CreatePCUsingTurnUrlWithTransportParam) {
TEST_F(PeerConnectionFactoryTest, CreatePCUsingSecureTurnUrl) {
PeerConnectionInterface::RTCConfiguration config;
- config.sdp_semantics = webrtc::SdpSemantics::kUnifiedPlan;
- webrtc::PeerConnectionInterface::IceServer ice_server;
+ config.sdp_semantics = SdpSemantics::kUnifiedPlan;
+ PeerConnectionInterface::IceServer ice_server;
ice_server.uri = kSecureTurnIceServer;
ice_server.username = kTurnUsername;
ice_server.password = kTurnPassword;
@@ -577,7 +565,7 @@ TEST_F(PeerConnectionFactoryTest, CreatePCUsingSecureTurnUrl) {
ice_server.username = kTurnUsername;
ice_server.password = kTurnPassword;
config.servers.push_back(ice_server);
- webrtc::PeerConnectionDependencies pc_dependencies(&observer_);
+ PeerConnectionDependencies pc_dependencies(&observer_);
pc_dependencies.cert_generator =
std::make_unique<FakeRTCCertificateGenerator>();
pc_dependencies.allocator = std::move(port_allocator_);
@@ -602,8 +590,8 @@ TEST_F(PeerConnectionFactoryTest, CreatePCUsingSecureTurnUrl) {
TEST_F(PeerConnectionFactoryTest, CreatePCUsingIPLiteralAddress) {
PeerConnectionInterface::RTCConfiguration config;
- config.sdp_semantics = webrtc::SdpSemantics::kUnifiedPlan;
- webrtc::PeerConnectionInterface::IceServer ice_server;
+ config.sdp_semantics = SdpSemantics::kUnifiedPlan;
+ PeerConnectionInterface::IceServer ice_server;
ice_server.uri = kStunIceServerWithIPv4Address;
config.servers.push_back(ice_server);
ice_server.uri = kStunIceServerWithIPv4AddressWithoutPort;
@@ -616,7 +604,7 @@ TEST_F(PeerConnectionFactoryTest, CreatePCUsingIPLiteralAddress) {
ice_server.username = kTurnUsername;
ice_server.password = kTurnPassword;
config.servers.push_back(ice_server);
- webrtc::PeerConnectionDependencies pc_dependencies(&observer_);
+ PeerConnectionDependencies pc_dependencies(&observer_);
pc_dependencies.cert_generator =
std::make_unique<FakeRTCCertificateGenerator>();
pc_dependencies.allocator = std::move(port_allocator_);
@@ -644,8 +632,8 @@ TEST_F(PeerConnectionFactoryTest, CreatePCUsingIPLiteralAddress) {
// This test verifies the captured stream is rendered locally using a
// local video track.
TEST_F(PeerConnectionFactoryTest, LocalRendering) {
- rtc::scoped_refptr<webrtc::FakeVideoTrackSource> source =
- webrtc::FakeVideoTrackSource::Create(/*is_screencast=*/false);
+ rtc::scoped_refptr<FakeVideoTrackSource> source =
+ FakeVideoTrackSource::Create(/*is_screencast=*/false);
cricket::FakeFrameSource frame_source(1280, 720,
rtc::kNumMicrosecsPerSec / 30);
@@ -673,7 +661,7 @@ TEST_F(PeerConnectionFactoryTest, LocalRendering) {
}
TEST(PeerConnectionFactoryDependenciesTest, UsesNetworkManager) {
- constexpr webrtc::TimeDelta kWaitTimeout = webrtc::TimeDelta::Seconds(10);
+ constexpr TimeDelta kWaitTimeout = TimeDelta::Seconds(10);
auto mock_network_manager = std::make_unique<NiceMock<MockNetworkManager>>();
rtc::Event called;
@@ -681,24 +669,24 @@ TEST(PeerConnectionFactoryDependenciesTest, UsesNetworkManager) {
.Times(AtLeast(1))
.WillRepeatedly(InvokeWithoutArgs([&] { called.Set(); }));
- webrtc::PeerConnectionFactoryDependencies pcf_dependencies;
+ PeerConnectionFactoryDependencies pcf_dependencies;
pcf_dependencies.network_manager = std::move(mock_network_manager);
- rtc::scoped_refptr<webrtc::PeerConnectionFactoryInterface> pcf =
+ rtc::scoped_refptr<PeerConnectionFactoryInterface> pcf =
CreateModularPeerConnectionFactory(std::move(pcf_dependencies));
PeerConnectionInterface::RTCConfiguration config;
config.ice_candidate_pool_size = 2;
NullPeerConnectionObserver observer;
auto pc = pcf->CreatePeerConnectionOrError(
- config, webrtc::PeerConnectionDependencies(&observer));
+ config, PeerConnectionDependencies(&observer));
ASSERT_TRUE(pc.ok());
called.Wait(kWaitTimeout);
}
TEST(PeerConnectionFactoryDependenciesTest, UsesPacketSocketFactory) {
- constexpr webrtc::TimeDelta kWaitTimeout = webrtc::TimeDelta::Seconds(10);
+ constexpr TimeDelta kWaitTimeout = TimeDelta::Seconds(10);
auto mock_socket_factory =
std::make_unique<NiceMock<rtc::MockPacketSocketFactory>>();
@@ -710,10 +698,10 @@ TEST(PeerConnectionFactoryDependenciesTest, UsesPacketSocketFactory) {
}))
.WillRepeatedly(Return(nullptr));
- webrtc::PeerConnectionFactoryDependencies pcf_dependencies;
+ PeerConnectionFactoryDependencies pcf_dependencies;
pcf_dependencies.packet_socket_factory = std::move(mock_socket_factory);
- rtc::scoped_refptr<webrtc::PeerConnectionFactoryInterface> pcf =
+ rtc::scoped_refptr<PeerConnectionFactoryInterface> pcf =
CreateModularPeerConnectionFactory(std::move(pcf_dependencies));
// By default, localhost addresses are ignored, which makes tests fail if test
@@ -726,7 +714,7 @@ TEST(PeerConnectionFactoryDependenciesTest, UsesPacketSocketFactory) {
config.ice_candidate_pool_size = 2;
NullPeerConnectionObserver observer;
auto pc = pcf->CreatePeerConnectionOrError(
- config, webrtc::PeerConnectionDependencies(&observer));
+ config, PeerConnectionDependencies(&observer));
ASSERT_TRUE(pc.ok());
called.Wait(kWaitTimeout);
diff --git a/third_party/libwebrtc/pc/peer_connection_field_trial_tests.cc b/third_party/libwebrtc/pc/peer_connection_field_trial_tests.cc
index 7799c9d6e3..4cbe24986c 100644
--- a/third_party/libwebrtc/pc/peer_connection_field_trial_tests.cc
+++ b/third_party/libwebrtc/pc/peer_connection_field_trial_tests.cc
@@ -16,13 +16,13 @@
#include "api/audio_codecs/builtin_audio_decoder_factory.h"
#include "api/audio_codecs/builtin_audio_encoder_factory.h"
#include "api/create_peerconnection_factory.h"
+#include "api/enable_media_with_defaults.h"
#include "api/peer_connection_interface.h"
#include "api/stats/rtcstats_objects.h"
#include "api/task_queue/default_task_queue_factory.h"
#include "api/video_codecs/builtin_video_decoder_factory.h"
#include "api/video_codecs/builtin_video_encoder_factory.h"
#include "media/engine/webrtc_media_engine.h"
-#include "media/engine/webrtc_media_engine_defaults.h"
#include "pc/peer_connection_wrapper.h"
#include "pc/session_description.h"
#include "pc/test/fake_audio_capture_module.h"
@@ -68,7 +68,7 @@ class PeerConnectionFieldTrialTest : public ::testing::Test {
#ifdef WEBRTC_ANDROID
InitializeAndroidObjects();
#endif
- webrtc::PeerConnectionInterface::IceServer ice_server;
+ PeerConnectionInterface::IceServer ice_server;
ice_server.uri = "stun:stun.l.google.com:19302";
config_.servers.push_back(ice_server);
config_.sdp_semantics = SdpSemantics::kUnifiedPlan;
@@ -81,13 +81,8 @@ class PeerConnectionFieldTrialTest : public ::testing::Test {
pcf_deps.signaling_thread = rtc::Thread::Current();
pcf_deps.trials = std::move(field_trials);
pcf_deps.task_queue_factory = CreateDefaultTaskQueueFactory();
- pcf_deps.call_factory = webrtc::CreateCallFactory();
- cricket::MediaEngineDependencies media_deps;
- media_deps.task_queue_factory = pcf_deps.task_queue_factory.get();
- media_deps.adm = FakeAudioCaptureModule::Create();
- media_deps.trials = pcf_deps.trials.get();
- webrtc::SetMediaEngineDefaults(&media_deps);
- pcf_deps.media_engine = cricket::CreateMediaEngine(std::move(media_deps));
+ pcf_deps.adm = FakeAudioCaptureModule::Create();
+ EnableMediaWithDefaults(pcf_deps);
pc_factory_ = CreateModularPeerConnectionFactory(std::move(pcf_deps));
// Allow ADAPTER_TYPE_LOOPBACK to create PeerConnections with loopback in
@@ -113,7 +108,7 @@ class PeerConnectionFieldTrialTest : public ::testing::Test {
std::unique_ptr<rtc::SocketServer> socket_server_;
rtc::AutoSocketServerThread main_thread_;
rtc::scoped_refptr<PeerConnectionFactoryInterface> pc_factory_ = nullptr;
- webrtc::PeerConnectionInterface::RTCConfiguration config_;
+ PeerConnectionInterface::RTCConfiguration config_;
};
// Tests for the dependency descriptor field trial. The dependency descriptor
@@ -138,7 +133,7 @@ TEST_F(PeerConnectionFieldTrialTest, EnableDependencyDescriptorAdvertised) {
media_description1->rtp_header_extensions();
bool found = absl::c_find_if(rtp_header_extensions1,
- [](const webrtc::RtpExtension& rtp_extension) {
+ [](const RtpExtension& rtp_extension) {
return rtp_extension.uri ==
RtpExtension::kDependencyDescriptorUri;
}) != rtp_header_extensions1.end();
@@ -168,14 +163,14 @@ TEST_F(PeerConnectionFieldTrialTest, InjectDependencyDescriptor) {
media_description1->rtp_header_extensions();
bool found1 = absl::c_find_if(rtp_header_extensions1,
- [](const webrtc::RtpExtension& rtp_extension) {
+ [](const RtpExtension& rtp_extension) {
return rtp_extension.uri ==
RtpExtension::kDependencyDescriptorUri;
}) != rtp_header_extensions1.end();
EXPECT_FALSE(found1);
std::set<int> existing_ids;
- for (const webrtc::RtpExtension& rtp_extension : rtp_header_extensions1) {
+ for (const RtpExtension& rtp_extension : rtp_header_extensions1) {
existing_ids.insert(rtp_extension.id);
}
@@ -212,7 +207,7 @@ TEST_F(PeerConnectionFieldTrialTest, InjectDependencyDescriptor) {
media_description2->rtp_header_extensions();
bool found2 = absl::c_find_if(rtp_header_extensions2,
- [](const webrtc::RtpExtension& rtp_extension) {
+ [](const RtpExtension& rtp_extension) {
return rtp_extension.uri ==
RtpExtension::kDependencyDescriptorUri;
}) != rtp_header_extensions2.end();
diff --git a/third_party/libwebrtc/pc/peer_connection_header_extension_unittest.cc b/third_party/libwebrtc/pc/peer_connection_header_extension_unittest.cc
index b1c6c3cfb5..277979b330 100644
--- a/third_party/libwebrtc/pc/peer_connection_header_extension_unittest.cc
+++ b/third_party/libwebrtc/pc/peer_connection_header_extension_unittest.cc
@@ -16,7 +16,6 @@
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
-#include "api/call/call_factory_interface.h"
#include "api/jsep.h"
#include "api/media_types.h"
#include "api/peer_connection_interface.h"
@@ -35,6 +34,7 @@
#include "p2p/base/port_allocator.h"
#include "pc/peer_connection_wrapper.h"
#include "pc/session_description.h"
+#include "pc/test/enable_fake_media.h"
#include "pc/test/mock_peer_connection_observers.h"
#include "rtc_base/internal/default_socket_server.h"
#include "rtc_base/rtc_certificate_generator.h"
@@ -77,21 +77,18 @@ class PeerConnectionHeaderExtensionTest
std::unique_ptr<PeerConnectionWrapper> CreatePeerConnection(
cricket::MediaType media_type,
absl::optional<SdpSemantics> semantics) {
- auto voice = std::make_unique<cricket::FakeVoiceEngine>();
- auto video = std::make_unique<cricket::FakeVideoEngine>();
+ auto media_engine = std::make_unique<cricket::FakeMediaEngine>();
if (media_type == cricket::MediaType::MEDIA_TYPE_AUDIO)
- voice->SetRtpHeaderExtensions(extensions_);
+ media_engine->fake_voice_engine()->SetRtpHeaderExtensions(extensions_);
else
- video->SetRtpHeaderExtensions(extensions_);
- auto media_engine = std::make_unique<cricket::CompositeMediaEngine>(
- std::move(voice), std::move(video));
+ media_engine->fake_video_engine()->SetRtpHeaderExtensions(extensions_);
PeerConnectionFactoryDependencies factory_dependencies;
factory_dependencies.network_thread = rtc::Thread::Current();
factory_dependencies.worker_thread = rtc::Thread::Current();
factory_dependencies.signaling_thread = rtc::Thread::Current();
factory_dependencies.task_queue_factory = CreateDefaultTaskQueueFactory();
- factory_dependencies.media_engine = std::move(media_engine);
- factory_dependencies.call_factory = CreateCallFactory();
+ EnableFakeMedia(factory_dependencies, std::move(media_engine));
+
factory_dependencies.event_log_factory =
std::make_unique<RtcEventLogFactory>(
factory_dependencies.task_queue_factory.get());
@@ -117,7 +114,7 @@ class PeerConnectionHeaderExtensionTest
pc_factory, result.MoveValue(), std::move(observer));
}
- webrtc::test::ScopedKeyValueConfig field_trials_;
+ test::ScopedKeyValueConfig field_trials_;
std::unique_ptr<rtc::SocketServer> socket_server_;
rtc::AutoSocketServerThread main_thread_;
std::vector<RtpHeaderExtensionCapability> extensions_;
diff --git a/third_party/libwebrtc/pc/peer_connection_histogram_unittest.cc b/third_party/libwebrtc/pc/peer_connection_histogram_unittest.cc
index 68a4dbc361..58bd6ebb48 100644
--- a/third_party/libwebrtc/pc/peer_connection_histogram_unittest.cc
+++ b/third_party/libwebrtc/pc/peer_connection_histogram_unittest.cc
@@ -15,7 +15,6 @@
#include <vector>
#include "absl/types/optional.h"
-#include "api/async_resolver_factory.h"
#include "api/call/call_factory_interface.h"
#include "api/jsep.h"
#include "api/jsep_session_description.h"
@@ -25,9 +24,7 @@
#include "api/task_queue/default_task_queue_factory.h"
#include "api/task_queue/task_queue_factory.h"
#include "api/test/mock_async_dns_resolver.h"
-#include "media/base/fake_media_engine.h"
#include "media/base/media_engine.h"
-#include "p2p/base/mock_async_resolver.h"
#include "p2p/base/port_allocator.h"
#include "p2p/client/basic_port_allocator.h"
#include "pc/peer_connection.h"
@@ -35,6 +32,7 @@
#include "pc/peer_connection_proxy.h"
#include "pc/peer_connection_wrapper.h"
#include "pc/sdp_utils.h"
+#include "pc/test/enable_fake_media.h"
#include "pc/test/mock_peer_connection_observers.h"
#include "pc/usage_pattern.h"
#include "pc/webrtc_sdp.h"
@@ -84,9 +82,7 @@ class PeerConnectionFactoryForUsageHistogramTest
dependencies.worker_thread = rtc::Thread::Current();
dependencies.signaling_thread = rtc::Thread::Current();
dependencies.task_queue_factory = CreateDefaultTaskQueueFactory();
- dependencies.media_engine =
- std::make_unique<cricket::FakeMediaEngine>();
- dependencies.call_factory = CreateCallFactory();
+ EnableFakeMedia(dependencies);
return dependencies;
}()) {}
};
@@ -97,7 +93,7 @@ typedef PeerConnectionWrapperForUsageHistogramTest* RawWrapperPtr;
class ObserverForUsageHistogramTest : public MockPeerConnectionObserver {
public:
- void OnIceCandidate(const webrtc::IceCandidateInterface* candidate) override;
+ void OnIceCandidate(const IceCandidateInterface* candidate) override;
void OnInterestingUsage(int usage_pattern) override {
interesting_usage_detected_ = usage_pattern;
@@ -160,12 +156,11 @@ class PeerConnectionWrapperForUsageHistogramTest
return static_cast<ObserverForUsageHistogramTest*>(observer())
->HaveDataChannel();
}
- void BufferIceCandidate(const webrtc::IceCandidateInterface* candidate) {
+ void BufferIceCandidate(const IceCandidateInterface* candidate) {
std::string sdp;
EXPECT_TRUE(candidate->ToString(&sdp));
- std::unique_ptr<webrtc::IceCandidateInterface> candidate_copy(
- CreateIceCandidate(candidate->sdp_mid(), candidate->sdp_mline_index(),
- sdp, nullptr));
+ std::unique_ptr<IceCandidateInterface> candidate_copy(CreateIceCandidate(
+ candidate->sdp_mid(), candidate->sdp_mline_index(), sdp, nullptr));
buffered_candidates_.push_back(std::move(candidate_copy));
}
@@ -216,19 +211,18 @@ class PeerConnectionWrapperForUsageHistogramTest
return true;
}
- webrtc::PeerConnectionInterface::IceGatheringState ice_gathering_state() {
+ PeerConnectionInterface::IceGatheringState ice_gathering_state() {
return pc()->ice_gathering_state();
}
private:
// Candidates that have been sent but not yet configured
- std::vector<std::unique_ptr<webrtc::IceCandidateInterface>>
- buffered_candidates_;
+ std::vector<std::unique_ptr<IceCandidateInterface>> buffered_candidates_;
};
// Buffers candidates until we add them via AddBufferedIceCandidates.
void ObserverForUsageHistogramTest::OnIceCandidate(
- const webrtc::IceCandidateInterface* candidate) {
+ const IceCandidateInterface* candidate) {
// If target is not set, ignore. This happens in one-ended unit tests.
if (candidate_target_) {
this->candidate_target_->BufferIceCandidate(candidate);
@@ -245,12 +239,12 @@ class PeerConnectionUsageHistogramTest : public ::testing::Test {
: vss_(new rtc::VirtualSocketServer()),
socket_factory_(new rtc::BasicPacketSocketFactory(vss_.get())),
main_(vss_.get()) {
- webrtc::metrics::Reset();
+ metrics::Reset();
}
WrapperPtr CreatePeerConnection() {
RTCConfiguration config;
- config.sdp_semantics = webrtc::SdpSemantics::kUnifiedPlan;
+ config.sdp_semantics = SdpSemantics::kUnifiedPlan;
return CreatePeerConnection(
config, PeerConnectionFactoryInterface::Options(), nullptr);
}
@@ -262,13 +256,13 @@ class PeerConnectionUsageHistogramTest : public ::testing::Test {
WrapperPtr CreatePeerConnectionWithMdns(const RTCConfiguration& config) {
auto resolver_factory =
- std::make_unique<NiceMock<webrtc::MockAsyncDnsResolverFactory>>();
+ std::make_unique<NiceMock<MockAsyncDnsResolverFactory>>();
- webrtc::PeerConnectionDependencies deps(nullptr /* observer_in */);
+ PeerConnectionDependencies deps(nullptr /* observer_in */);
auto fake_network = NewFakeNetwork();
fake_network->set_mdns_responder(
- std::make_unique<webrtc::FakeMdnsResponder>(rtc::Thread::Current()));
+ std::make_unique<FakeMdnsResponder>(rtc::Thread::Current()));
fake_network->AddInterface(NextLocalAddress());
std::unique_ptr<cricket::BasicPortAllocator> port_allocator(
@@ -283,7 +277,7 @@ class PeerConnectionUsageHistogramTest : public ::testing::Test {
WrapperPtr CreatePeerConnectionWithImmediateReport() {
RTCConfiguration configuration;
- configuration.sdp_semantics = webrtc::SdpSemantics::kUnifiedPlan;
+ configuration.sdp_semantics = SdpSemantics::kUnifiedPlan;
configuration.report_usage_pattern_delay_ms = 0;
return CreatePeerConnection(
configuration, PeerConnectionFactoryInterface::Options(), nullptr);
@@ -364,7 +358,7 @@ class PeerConnectionUsageHistogramTest : public ::testing::Test {
// This works correctly only if there is only one sample value
// that has been counted.
// Returns -1 for "not found".
- return webrtc::metrics::MinSample(kUsagePatternMetric);
+ return metrics::MinSample(kUsagePatternMetric);
}
// The PeerConnection's port allocator is tied to the PeerConnection's
@@ -393,10 +387,10 @@ TEST_F(PeerConnectionUsageHistogramTest, UsageFingerprintHistogramFromTimeout) {
auto pc = CreatePeerConnectionWithImmediateReport();
int expected_fingerprint = MakeUsageFingerprint({});
- EXPECT_METRIC_EQ_WAIT(1, webrtc::metrics::NumSamples(kUsagePatternMetric),
+ EXPECT_METRIC_EQ_WAIT(1, metrics::NumSamples(kUsagePatternMetric),
kDefaultTimeout);
EXPECT_METRIC_EQ(
- 1, webrtc::metrics::NumEvents(kUsagePatternMetric, expected_fingerprint));
+ 1, metrics::NumEvents(kUsagePatternMetric, expected_fingerprint));
}
#ifndef WEBRTC_ANDROID
@@ -421,11 +415,10 @@ TEST_F(PeerConnectionUsageHistogramTest, FingerprintAudioVideo) {
UsageEvent::DIRECT_CONNECTION_SELECTED, UsageEvent::CLOSE_CALLED});
// In this case, we may or may not have PRIVATE_CANDIDATE_COLLECTED,
// depending on the machine configuration.
- EXPECT_METRIC_EQ(2, webrtc::metrics::NumSamples(kUsagePatternMetric));
+ EXPECT_METRIC_EQ(2, metrics::NumSamples(kUsagePatternMetric));
EXPECT_METRIC_TRUE(
- webrtc::metrics::NumEvents(kUsagePatternMetric, expected_fingerprint) ==
- 2 ||
- webrtc::metrics::NumEvents(
+ metrics::NumEvents(kUsagePatternMetric, expected_fingerprint) == 2 ||
+ metrics::NumEvents(
kUsagePatternMetric,
expected_fingerprint |
static_cast<int>(UsageEvent::PRIVATE_CANDIDATE_COLLECTED)) == 2);
@@ -466,11 +459,11 @@ TEST_F(PeerConnectionUsageHistogramTest, FingerprintWithMdnsCaller) {
UsageEvent::CANDIDATE_COLLECTED, UsageEvent::ADD_ICE_CANDIDATE_SUCCEEDED,
UsageEvent::REMOTE_MDNS_CANDIDATE_ADDED, UsageEvent::ICE_STATE_CONNECTED,
UsageEvent::REMOTE_CANDIDATE_ADDED, UsageEvent::CLOSE_CALLED});
- EXPECT_METRIC_EQ(2, webrtc::metrics::NumSamples(kUsagePatternMetric));
- EXPECT_METRIC_EQ(1, webrtc::metrics::NumEvents(kUsagePatternMetric,
- expected_fingerprint_caller));
- EXPECT_METRIC_EQ(1, webrtc::metrics::NumEvents(kUsagePatternMetric,
- expected_fingerprint_callee));
+ EXPECT_METRIC_EQ(2, metrics::NumSamples(kUsagePatternMetric));
+ EXPECT_METRIC_EQ(
+ 1, metrics::NumEvents(kUsagePatternMetric, expected_fingerprint_caller));
+ EXPECT_METRIC_EQ(
+ 1, metrics::NumEvents(kUsagePatternMetric, expected_fingerprint_callee));
}
// Test getting the usage fingerprint when the callee collects an mDNS
@@ -507,11 +500,11 @@ TEST_F(PeerConnectionUsageHistogramTest, FingerprintWithMdnsCallee) {
UsageEvent::ADD_ICE_CANDIDATE_SUCCEEDED, UsageEvent::ICE_STATE_CONNECTED,
UsageEvent::REMOTE_CANDIDATE_ADDED,
UsageEvent::DIRECT_CONNECTION_SELECTED, UsageEvent::CLOSE_CALLED});
- EXPECT_METRIC_EQ(2, webrtc::metrics::NumSamples(kUsagePatternMetric));
- EXPECT_METRIC_EQ(1, webrtc::metrics::NumEvents(kUsagePatternMetric,
- expected_fingerprint_caller));
- EXPECT_METRIC_EQ(1, webrtc::metrics::NumEvents(kUsagePatternMetric,
- expected_fingerprint_callee));
+ EXPECT_METRIC_EQ(2, metrics::NumSamples(kUsagePatternMetric));
+ EXPECT_METRIC_EQ(
+ 1, metrics::NumEvents(kUsagePatternMetric, expected_fingerprint_caller));
+ EXPECT_METRIC_EQ(
+ 1, metrics::NumEvents(kUsagePatternMetric, expected_fingerprint_callee));
}
#ifdef WEBRTC_HAVE_SCTP
@@ -529,11 +522,10 @@ TEST_F(PeerConnectionUsageHistogramTest, FingerprintDataOnly) {
UsageEvent::CANDIDATE_COLLECTED, UsageEvent::ADD_ICE_CANDIDATE_SUCCEEDED,
UsageEvent::ICE_STATE_CONNECTED, UsageEvent::REMOTE_CANDIDATE_ADDED,
UsageEvent::DIRECT_CONNECTION_SELECTED, UsageEvent::CLOSE_CALLED});
- EXPECT_METRIC_EQ(2, webrtc::metrics::NumSamples(kUsagePatternMetric));
+ EXPECT_METRIC_EQ(2, metrics::NumSamples(kUsagePatternMetric));
EXPECT_METRIC_TRUE(
- webrtc::metrics::NumEvents(kUsagePatternMetric, expected_fingerprint) ==
- 2 ||
- webrtc::metrics::NumEvents(
+ metrics::NumEvents(kUsagePatternMetric, expected_fingerprint) == 2 ||
+ metrics::NumEvents(
kUsagePatternMetric,
expected_fingerprint |
static_cast<int>(UsageEvent::PRIVATE_CANDIDATE_COLLECTED)) == 2);
@@ -557,9 +549,9 @@ TEST_F(PeerConnectionUsageHistogramTest, FingerprintStunTurn) {
int expected_fingerprint = MakeUsageFingerprint(
{UsageEvent::STUN_SERVER_ADDED, UsageEvent::TURN_SERVER_ADDED,
UsageEvent::CLOSE_CALLED});
- EXPECT_METRIC_EQ(1, webrtc::metrics::NumSamples(kUsagePatternMetric));
+ EXPECT_METRIC_EQ(1, metrics::NumSamples(kUsagePatternMetric));
EXPECT_METRIC_EQ(
- 1, webrtc::metrics::NumEvents(kUsagePatternMetric, expected_fingerprint));
+ 1, metrics::NumEvents(kUsagePatternMetric, expected_fingerprint));
}
TEST_F(PeerConnectionUsageHistogramTest, FingerprintStunTurnInReconfiguration) {
@@ -579,9 +571,9 @@ TEST_F(PeerConnectionUsageHistogramTest, FingerprintStunTurnInReconfiguration) {
int expected_fingerprint = MakeUsageFingerprint(
{UsageEvent::STUN_SERVER_ADDED, UsageEvent::TURN_SERVER_ADDED,
UsageEvent::CLOSE_CALLED});
- EXPECT_METRIC_EQ(1, webrtc::metrics::NumSamples(kUsagePatternMetric));
+ EXPECT_METRIC_EQ(1, metrics::NumSamples(kUsagePatternMetric));
EXPECT_METRIC_EQ(
- 1, webrtc::metrics::NumEvents(kUsagePatternMetric, expected_fingerprint));
+ 1, metrics::NumEvents(kUsagePatternMetric, expected_fingerprint));
}
TEST_F(PeerConnectionUsageHistogramTest, FingerprintWithPrivateIPCaller) {
@@ -607,11 +599,11 @@ TEST_F(PeerConnectionUsageHistogramTest, FingerprintWithPrivateIPCaller) {
UsageEvent::REMOTE_PRIVATE_CANDIDATE_ADDED,
UsageEvent::ICE_STATE_CONNECTED, UsageEvent::REMOTE_CANDIDATE_ADDED,
UsageEvent::DIRECT_CONNECTION_SELECTED, UsageEvent::CLOSE_CALLED});
- EXPECT_METRIC_EQ(2, webrtc::metrics::NumSamples(kUsagePatternMetric));
- EXPECT_METRIC_EQ(1, webrtc::metrics::NumEvents(kUsagePatternMetric,
- expected_fingerprint_caller));
- EXPECT_METRIC_EQ(1, webrtc::metrics::NumEvents(kUsagePatternMetric,
- expected_fingerprint_callee));
+ EXPECT_METRIC_EQ(2, metrics::NumSamples(kUsagePatternMetric));
+ EXPECT_METRIC_EQ(
+ 1, metrics::NumEvents(kUsagePatternMetric, expected_fingerprint_caller));
+ EXPECT_METRIC_EQ(
+ 1, metrics::NumEvents(kUsagePatternMetric, expected_fingerprint_callee));
}
TEST_F(PeerConnectionUsageHistogramTest, FingerprintWithPrivateIpv6Callee) {
@@ -639,11 +631,11 @@ TEST_F(PeerConnectionUsageHistogramTest, FingerprintWithPrivateIpv6Callee) {
UsageEvent::ADD_ICE_CANDIDATE_SUCCEEDED,
UsageEvent::REMOTE_CANDIDATE_ADDED, UsageEvent::ICE_STATE_CONNECTED,
UsageEvent::DIRECT_CONNECTION_SELECTED, UsageEvent::CLOSE_CALLED});
- EXPECT_METRIC_EQ(2, webrtc::metrics::NumSamples(kUsagePatternMetric));
- EXPECT_METRIC_EQ(1, webrtc::metrics::NumEvents(kUsagePatternMetric,
- expected_fingerprint_caller));
- EXPECT_METRIC_EQ(1, webrtc::metrics::NumEvents(kUsagePatternMetric,
- expected_fingerprint_callee));
+ EXPECT_METRIC_EQ(2, metrics::NumSamples(kUsagePatternMetric));
+ EXPECT_METRIC_EQ(
+ 1, metrics::NumEvents(kUsagePatternMetric, expected_fingerprint_caller));
+ EXPECT_METRIC_EQ(
+ 1, metrics::NumEvents(kUsagePatternMetric, expected_fingerprint_callee));
}
#ifndef WEBRTC_ANDROID
@@ -667,7 +659,7 @@ TEST_F(PeerConnectionUsageHistogramTest,
ASSERT_TRUE(caller->SetLocalDescription(caller->CreateOffer()));
// Wait until the gathering completes so that the session description would
// have contained ICE candidates.
- EXPECT_EQ_WAIT(webrtc::PeerConnectionInterface::kIceGatheringComplete,
+ EXPECT_EQ_WAIT(PeerConnectionInterface::kIceGatheringComplete,
caller->ice_gathering_state(), kDefaultTimeout);
EXPECT_TRUE(caller->observer()->candidate_gathered());
// Get the current offer that contains candidates and pass it to the callee.
@@ -716,11 +708,11 @@ TEST_F(PeerConnectionUsageHistogramTest,
UsageEvent::REMOTE_PRIVATE_CANDIDATE_ADDED,
UsageEvent::REMOTE_IPV6_CANDIDATE_ADDED, UsageEvent::ICE_STATE_CONNECTED,
UsageEvent::DIRECT_CONNECTION_SELECTED, UsageEvent::CLOSE_CALLED});
- EXPECT_METRIC_EQ(2, webrtc::metrics::NumSamples(kUsagePatternMetric));
- EXPECT_METRIC_EQ(1, webrtc::metrics::NumEvents(kUsagePatternMetric,
- expected_fingerprint_caller));
- EXPECT_METRIC_EQ(1, webrtc::metrics::NumEvents(kUsagePatternMetric,
- expected_fingerprint_callee));
+ EXPECT_METRIC_EQ(2, metrics::NumSamples(kUsagePatternMetric));
+ EXPECT_METRIC_EQ(
+ 1, metrics::NumEvents(kUsagePatternMetric, expected_fingerprint_caller));
+ EXPECT_METRIC_EQ(
+ 1, metrics::NumEvents(kUsagePatternMetric, expected_fingerprint_callee));
}
TEST_F(PeerConnectionUsageHistogramTest, NotableUsageNoted) {
@@ -731,7 +723,7 @@ TEST_F(PeerConnectionUsageHistogramTest, NotableUsageNoted) {
int expected_fingerprint = MakeUsageFingerprint(
{UsageEvent::DATA_ADDED, UsageEvent::SET_LOCAL_DESCRIPTION_SUCCEEDED,
UsageEvent::CANDIDATE_COLLECTED, UsageEvent::CLOSE_CALLED});
- EXPECT_METRIC_EQ(1, webrtc::metrics::NumSamples(kUsagePatternMetric));
+ EXPECT_METRIC_EQ(1, metrics::NumSamples(kUsagePatternMetric));
EXPECT_METRIC_TRUE(
expected_fingerprint == ObservedFingerprint() ||
(expected_fingerprint |
@@ -748,9 +740,9 @@ TEST_F(PeerConnectionUsageHistogramTest, NotableUsageOnEventFiring) {
int expected_fingerprint = MakeUsageFingerprint(
{UsageEvent::DATA_ADDED, UsageEvent::SET_LOCAL_DESCRIPTION_SUCCEEDED,
UsageEvent::CANDIDATE_COLLECTED});
- EXPECT_METRIC_EQ(0, webrtc::metrics::NumSamples(kUsagePatternMetric));
+ EXPECT_METRIC_EQ(0, metrics::NumSamples(kUsagePatternMetric));
caller->GetInternalPeerConnection()->RequestUsagePatternReportForTesting();
- EXPECT_METRIC_EQ_WAIT(1, webrtc::metrics::NumSamples(kUsagePatternMetric),
+ EXPECT_METRIC_EQ_WAIT(1, metrics::NumSamples(kUsagePatternMetric),
kDefaultTimeout);
EXPECT_METRIC_TRUE(
expected_fingerprint == ObservedFingerprint() ||
@@ -769,12 +761,12 @@ TEST_F(PeerConnectionUsageHistogramTest,
int expected_fingerprint = MakeUsageFingerprint(
{UsageEvent::DATA_ADDED, UsageEvent::SET_LOCAL_DESCRIPTION_SUCCEEDED,
UsageEvent::CANDIDATE_COLLECTED, UsageEvent::CLOSE_CALLED});
- EXPECT_METRIC_EQ(0, webrtc::metrics::NumSamples(kUsagePatternMetric));
+ EXPECT_METRIC_EQ(0, metrics::NumSamples(kUsagePatternMetric));
caller->pc()->Close();
- EXPECT_METRIC_EQ(1, webrtc::metrics::NumSamples(kUsagePatternMetric));
+ EXPECT_METRIC_EQ(1, metrics::NumSamples(kUsagePatternMetric));
caller->GetInternalPeerConnection()->RequestUsagePatternReportForTesting();
caller->observer()->ClearInterestingUsageDetector();
- EXPECT_METRIC_EQ_WAIT(2, webrtc::metrics::NumSamples(kUsagePatternMetric),
+ EXPECT_METRIC_EQ_WAIT(2, metrics::NumSamples(kUsagePatternMetric),
kDefaultTimeout);
EXPECT_METRIC_TRUE(
expected_fingerprint == ObservedFingerprint() ||
diff --git a/third_party/libwebrtc/pc/peer_connection_ice_unittest.cc b/third_party/libwebrtc/pc/peer_connection_ice_unittest.cc
index 532583f307..492e108cbc 100644
--- a/third_party/libwebrtc/pc/peer_connection_ice_unittest.cc
+++ b/third_party/libwebrtc/pc/peer_connection_ice_unittest.cc
@@ -342,7 +342,7 @@ class PeerConnectionIceTest
public ::testing::WithParamInterface<SdpSemantics> {
protected:
PeerConnectionIceTest() : PeerConnectionIceBaseTest(GetParam()) {
- webrtc::metrics::Reset();
+ metrics::Reset();
}
};
@@ -514,7 +514,7 @@ TEST_P(PeerConnectionIceTest, CannotAddCandidateWhenRemoteDescriptionNotSet) {
EXPECT_FALSE(caller->pc()->AddIceCandidate(jsep_candidate.get()));
EXPECT_METRIC_THAT(
- webrtc::metrics::Samples("WebRTC.PeerConnection.AddIceCandidate"),
+ metrics::Samples("WebRTC.PeerConnection.AddIceCandidate"),
ElementsAre(Pair(kAddIceCandidateFailNoRemoteDescription, 2)));
}
@@ -1457,7 +1457,7 @@ class PeerConnectionIceConfigTest : public ::testing::Test {
pc_ = result.MoveValue();
}
- webrtc::test::ScopedKeyValueConfig field_trials_;
+ test::ScopedKeyValueConfig field_trials_;
std::unique_ptr<rtc::SocketServer> socket_server_;
rtc::AutoSocketServerThread main_thread_;
rtc::scoped_refptr<PeerConnectionFactoryInterface> pc_factory_ = nullptr;
diff --git a/third_party/libwebrtc/pc/peer_connection_integrationtest.cc b/third_party/libwebrtc/pc/peer_connection_integrationtest.cc
index d76e5e27d5..bfff86ee93 100644
--- a/third_party/libwebrtc/pc/peer_connection_integrationtest.cc
+++ b/third_party/libwebrtc/pc/peer_connection_integrationtest.cc
@@ -28,7 +28,6 @@
#include "absl/memory/memory.h"
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
-#include "api/async_resolver_factory.h"
#include "api/candidate.h"
#include "api/crypto/crypto_options.h"
#include "api/dtmf_sender_interface.h"
@@ -61,7 +60,6 @@
#include "media/base/codec.h"
#include "media/base/media_constants.h"
#include "media/base/stream_params.h"
-#include "p2p/base/mock_async_resolver.h"
#include "p2p/base/port.h"
#include "p2p/base/port_allocator.h"
#include "p2p/base/port_interface.h"
@@ -104,6 +102,12 @@ namespace webrtc {
namespace {
+using ::testing::AtLeast;
+using ::testing::InSequence;
+using ::testing::MockFunction;
+using ::testing::NiceMock;
+using ::testing::Return;
+
class PeerConnectionIntegrationTest
: public PeerConnectionIntegrationBaseTest,
public ::testing::WithParamInterface<SdpSemantics> {
@@ -124,7 +128,7 @@ class FakeClockForTest : public rtc::ScopedFakeClock {
// Some things use a time of "0" as a special value, so we need to start out
// the fake clock at a nonzero time.
// TODO(deadbeef): Fix this.
- AdvanceTime(webrtc::TimeDelta::Seconds(1));
+ AdvanceTime(TimeDelta::Seconds(1));
}
// Explicit handle.
@@ -324,7 +328,7 @@ TEST_P(PeerConnectionIntegrationTest,
ConnectFakeSignaling();
// Add video tracks with 16:9 aspect ratio, size 1280 x 720.
- webrtc::FakePeriodicVideoSource::Config config;
+ FakePeriodicVideoSource::Config config;
config.width = 1280;
config.height = 720;
config.timestamp_offset_ms = rtc::TimeMillis();
@@ -366,7 +370,7 @@ TEST_P(PeerConnectionIntegrationTest, EndToEndCallWithSendOnlyVideo) {
CreateOneDirectionalPeerConnectionWrappers(/*caller_to_callee=*/true));
ConnectFakeSignaling();
// Add one-directional video, from caller to callee.
- rtc::scoped_refptr<webrtc::VideoTrackInterface> caller_track =
+ rtc::scoped_refptr<VideoTrackInterface> caller_track =
caller()->CreateLocalVideoTrack();
caller()->AddTrack(caller_track);
PeerConnectionInterface::RTCOfferAnswerOptions options;
@@ -391,7 +395,7 @@ TEST_P(PeerConnectionIntegrationTest, EndToEndCallWithReceiveOnlyVideo) {
CreateOneDirectionalPeerConnectionWrappers(/*caller_to_callee=*/false));
ConnectFakeSignaling();
// Add one-directional video, from callee to caller.
- rtc::scoped_refptr<webrtc::VideoTrackInterface> callee_track =
+ rtc::scoped_refptr<VideoTrackInterface> callee_track =
callee()->CreateLocalVideoTrack();
callee()->AddTrack(callee_track);
PeerConnectionInterface::RTCOfferAnswerOptions options;
@@ -414,14 +418,14 @@ TEST_P(PeerConnectionIntegrationTest,
ASSERT_TRUE(CreatePeerConnectionWrappers());
ConnectFakeSignaling();
// Add one-directional video, from caller to callee.
- rtc::scoped_refptr<webrtc::VideoTrackInterface> caller_track =
+ rtc::scoped_refptr<VideoTrackInterface> caller_track =
caller()->CreateLocalVideoTrack();
caller()->AddTrack(caller_track);
caller()->CreateAndSetAndSignalOffer();
ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
// Add receive video.
- rtc::scoped_refptr<webrtc::VideoTrackInterface> callee_track =
+ rtc::scoped_refptr<VideoTrackInterface> callee_track =
callee()->CreateLocalVideoTrack();
callee()->AddTrack(callee_track);
caller()->CreateAndSetAndSignalOffer();
@@ -438,14 +442,14 @@ TEST_P(PeerConnectionIntegrationTest,
ASSERT_TRUE(CreatePeerConnectionWrappers());
ConnectFakeSignaling();
// Add one-directional video, from callee to caller.
- rtc::scoped_refptr<webrtc::VideoTrackInterface> callee_track =
+ rtc::scoped_refptr<VideoTrackInterface> callee_track =
callee()->CreateLocalVideoTrack();
callee()->AddTrack(callee_track);
caller()->CreateAndSetAndSignalOffer();
ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
// Add send video.
- rtc::scoped_refptr<webrtc::VideoTrackInterface> caller_track =
+ rtc::scoped_refptr<VideoTrackInterface> caller_track =
caller()->CreateLocalVideoTrack();
caller()->AddTrack(caller_track);
caller()->CreateAndSetAndSignalOffer();
@@ -462,15 +466,15 @@ TEST_P(PeerConnectionIntegrationTest,
ASSERT_TRUE(CreatePeerConnectionWrappers());
ConnectFakeSignaling();
// Add send video, from caller to callee.
- rtc::scoped_refptr<webrtc::VideoTrackInterface> caller_track =
+ rtc::scoped_refptr<VideoTrackInterface> caller_track =
caller()->CreateLocalVideoTrack();
- rtc::scoped_refptr<webrtc::RtpSenderInterface> caller_sender =
+ rtc::scoped_refptr<RtpSenderInterface> caller_sender =
caller()->AddTrack(caller_track);
// Add receive video, from callee to caller.
- rtc::scoped_refptr<webrtc::VideoTrackInterface> callee_track =
+ rtc::scoped_refptr<VideoTrackInterface> callee_track =
callee()->CreateLocalVideoTrack();
- rtc::scoped_refptr<webrtc::RtpSenderInterface> callee_sender =
+ rtc::scoped_refptr<RtpSenderInterface> callee_sender =
callee()->AddTrack(callee_track);
caller()->CreateAndSetAndSignalOffer();
ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
@@ -494,15 +498,15 @@ TEST_P(PeerConnectionIntegrationTest,
ASSERT_TRUE(CreatePeerConnectionWrappers());
ConnectFakeSignaling();
// Add send video, from caller to callee.
- rtc::scoped_refptr<webrtc::VideoTrackInterface> caller_track =
+ rtc::scoped_refptr<VideoTrackInterface> caller_track =
caller()->CreateLocalVideoTrack();
- rtc::scoped_refptr<webrtc::RtpSenderInterface> caller_sender =
+ rtc::scoped_refptr<RtpSenderInterface> caller_sender =
caller()->AddTrack(caller_track);
// Add receive video, from callee to caller.
- rtc::scoped_refptr<webrtc::VideoTrackInterface> callee_track =
+ rtc::scoped_refptr<VideoTrackInterface> callee_track =
callee()->CreateLocalVideoTrack();
- rtc::scoped_refptr<webrtc::RtpSenderInterface> callee_sender =
+ rtc::scoped_refptr<RtpSenderInterface> callee_sender =
callee()->AddTrack(callee_track);
caller()->CreateAndSetAndSignalOffer();
ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
@@ -654,9 +658,9 @@ TEST_P(PeerConnectionIntegrationTest, RotatedVideoWithCVOExtension) {
ConnectFakeSignaling();
// Add rotated video tracks.
caller()->AddTrack(
- caller()->CreateLocalVideoTrackWithRotation(webrtc::kVideoRotation_90));
+ caller()->CreateLocalVideoTrackWithRotation(kVideoRotation_90));
callee()->AddTrack(
- callee()->CreateLocalVideoTrackWithRotation(webrtc::kVideoRotation_270));
+ callee()->CreateLocalVideoTrackWithRotation(kVideoRotation_270));
// Wait for video frames to be received by both sides.
caller()->CreateAndSetAndSignalOffer();
@@ -673,8 +677,8 @@ TEST_P(PeerConnectionIntegrationTest, RotatedVideoWithCVOExtension) {
EXPECT_EQ(4.0 / 3, callee()->local_rendered_aspect_ratio());
EXPECT_EQ(4.0 / 3, callee()->rendered_aspect_ratio());
// Ensure that the CVO bits were surfaced to the renderer.
- EXPECT_EQ(webrtc::kVideoRotation_270, caller()->rendered_rotation());
- EXPECT_EQ(webrtc::kVideoRotation_90, callee()->rendered_rotation());
+ EXPECT_EQ(kVideoRotation_270, caller()->rendered_rotation());
+ EXPECT_EQ(kVideoRotation_90, callee()->rendered_rotation());
}
// Test that when the CVO extension isn't supported, video is rotated the
@@ -684,9 +688,9 @@ TEST_P(PeerConnectionIntegrationTest, RotatedVideoWithoutCVOExtension) {
ConnectFakeSignaling();
// Add rotated video tracks.
caller()->AddTrack(
- caller()->CreateLocalVideoTrackWithRotation(webrtc::kVideoRotation_90));
+ caller()->CreateLocalVideoTrackWithRotation(kVideoRotation_90));
callee()->AddTrack(
- callee()->CreateLocalVideoTrackWithRotation(webrtc::kVideoRotation_270));
+ callee()->CreateLocalVideoTrackWithRotation(kVideoRotation_270));
// Remove the CVO extension from the offered SDP.
callee()->SetReceivedSdpMunger([](cricket::SessionDescription* desc) {
@@ -710,8 +714,8 @@ TEST_P(PeerConnectionIntegrationTest, RotatedVideoWithoutCVOExtension) {
EXPECT_EQ(3.0 / 4, callee()->local_rendered_aspect_ratio());
EXPECT_EQ(3.0 / 4, callee()->rendered_aspect_ratio());
// Expect that each endpoint is unaware of the rotation of the other endpoint.
- EXPECT_EQ(webrtc::kVideoRotation_0, caller()->rendered_rotation());
- EXPECT_EQ(webrtc::kVideoRotation_0, callee()->rendered_rotation());
+ EXPECT_EQ(kVideoRotation_0, caller()->rendered_rotation());
+ EXPECT_EQ(kVideoRotation_0, callee()->rendered_rotation());
}
// Test that if the answerer rejects the audio m= section, no audio is sent or
@@ -899,9 +903,9 @@ TEST_F(PeerConnectionIntegrationTestPlanB, EnableAudioAfterRejecting) {
ConnectFakeSignaling();
// Add audio track, do normal offer/answer.
- rtc::scoped_refptr<webrtc::AudioTrackInterface> track =
+ rtc::scoped_refptr<AudioTrackInterface> track =
caller()->CreateLocalAudioTrack();
- rtc::scoped_refptr<webrtc::RtpSenderInterface> sender =
+ rtc::scoped_refptr<RtpSenderInterface> sender =
caller()->pc()->AddTrack(track, {"stream"}).MoveValue();
caller()->CreateAndSetAndSignalOffer();
ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
@@ -974,7 +978,7 @@ TEST_F(PeerConnectionIntegrationTestUnifiedPlan,
ASSERT_TRUE(CreatePeerConnectionWrappers());
ConnectFakeSignaling();
// Add one-directional video, from caller to callee.
- rtc::scoped_refptr<webrtc::VideoTrackInterface> track =
+ rtc::scoped_refptr<VideoTrackInterface> track =
caller()->CreateLocalVideoTrack();
RtpTransceiverInit video_transceiver_init;
@@ -988,7 +992,7 @@ TEST_F(PeerConnectionIntegrationTestUnifiedPlan,
// Add receive direction.
video_sender->SetDirectionWithError(RtpTransceiverDirection::kSendRecv);
- rtc::scoped_refptr<webrtc::VideoTrackInterface> callee_track =
+ rtc::scoped_refptr<VideoTrackInterface> callee_track =
callee()->CreateLocalVideoTrack();
callee()->AddTrack(callee_track);
@@ -1086,11 +1090,7 @@ void ModifyPayloadTypesAndRemoveMidExtension(
}),
extensions.end());
media->set_rtp_header_extensions(extensions);
- cricket::VideoContentDescription* video = media->as_video();
- ASSERT_TRUE(video != nullptr);
- std::vector<cricket::VideoCodec> codecs = {
- cricket::CreateVideoCodec(pt++, "VP8")};
- video->set_codecs(codecs);
+ media->set_codecs({cricket::CreateVideoCodec(pt++, "VP8")});
}
}
@@ -1348,11 +1348,11 @@ TEST_P(PeerConnectionIntegrationTest, NewGetStatsManyAudioAndManyVideoStreams) {
audio_sender_1->track()->id(), video_sender_1->track()->id(),
audio_sender_2->track()->id(), video_sender_2->track()->id()};
- rtc::scoped_refptr<const webrtc::RTCStatsReport> caller_report =
+ rtc::scoped_refptr<const RTCStatsReport> caller_report =
caller()->NewGetStats();
ASSERT_TRUE(caller_report);
auto outbound_stream_stats =
- caller_report->GetStatsOfType<webrtc::RTCOutboundRtpStreamStats>();
+ caller_report->GetStatsOfType<RTCOutboundRtpStreamStats>();
ASSERT_EQ(outbound_stream_stats.size(), 4u);
std::vector<std::string> outbound_track_ids;
for (const auto& stat : outbound_stream_stats) {
@@ -1373,11 +1373,11 @@ TEST_P(PeerConnectionIntegrationTest, NewGetStatsManyAudioAndManyVideoStreams) {
}
EXPECT_THAT(outbound_track_ids, UnorderedElementsAreArray(track_ids));
- rtc::scoped_refptr<const webrtc::RTCStatsReport> callee_report =
+ rtc::scoped_refptr<const RTCStatsReport> callee_report =
callee()->NewGetStats();
ASSERT_TRUE(callee_report);
auto inbound_stream_stats =
- callee_report->GetStatsOfType<webrtc::RTCInboundRtpStreamStats>();
+ callee_report->GetStatsOfType<RTCInboundRtpStreamStats>();
ASSERT_EQ(4u, inbound_stream_stats.size());
std::vector<std::string> inbound_track_ids;
for (const auto& stat : inbound_stream_stats) {
@@ -1412,11 +1412,10 @@ TEST_P(PeerConnectionIntegrationTest,
// We received a frame, so we should have nonzero "bytes received" stats for
// the unsignaled stream, if stats are working for it.
- rtc::scoped_refptr<const webrtc::RTCStatsReport> report =
- callee()->NewGetStats();
+ rtc::scoped_refptr<const RTCStatsReport> report = callee()->NewGetStats();
ASSERT_NE(nullptr, report);
auto inbound_stream_stats =
- report->GetStatsOfType<webrtc::RTCInboundRtpStreamStats>();
+ report->GetStatsOfType<RTCInboundRtpStreamStats>();
ASSERT_EQ(1U, inbound_stream_stats.size());
ASSERT_TRUE(inbound_stream_stats[0]->bytes_received.is_defined());
ASSERT_GT(*inbound_stream_stats[0]->bytes_received, 0U);
@@ -1459,12 +1458,10 @@ TEST_P(PeerConnectionIntegrationTest,
media_expectations.CalleeExpectsSomeVideo(1);
ASSERT_TRUE(ExpectNewFrames(media_expectations));
- rtc::scoped_refptr<const webrtc::RTCStatsReport> report =
- callee()->NewGetStats();
+ rtc::scoped_refptr<const RTCStatsReport> report = callee()->NewGetStats();
ASSERT_NE(nullptr, report);
- auto inbound_rtps =
- report->GetStatsOfType<webrtc::RTCInboundRtpStreamStats>();
+ auto inbound_rtps = report->GetStatsOfType<RTCInboundRtpStreamStats>();
auto index = FindFirstMediaStatsIndexByKind("audio", inbound_rtps);
ASSERT_GE(index, 0);
EXPECT_TRUE(inbound_rtps[index]->audio_level.is_defined());
@@ -1655,18 +1652,18 @@ TEST_P(PeerConnectionIntegrationTest, IceStatesReachCompletion) {
callee()->AddAudioVideoTracks();
caller()->CreateAndSetAndSignalOffer();
ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
- EXPECT_EQ_WAIT(webrtc::PeerConnectionInterface::kIceGatheringComplete,
+ EXPECT_EQ_WAIT(PeerConnectionInterface::kIceGatheringComplete,
caller()->ice_gathering_state(), kMaxWaitForFramesMs);
- EXPECT_EQ_WAIT(webrtc::PeerConnectionInterface::kIceGatheringComplete,
+ EXPECT_EQ_WAIT(PeerConnectionInterface::kIceGatheringComplete,
callee()->ice_gathering_state(), kMaxWaitForFramesMs);
// After the best candidate pair is selected and all candidates are signaled,
// the ICE connection state should reach "complete".
// TODO(deadbeef): Currently, the ICE "controlled" agent (the
// answerer/"callee" by default) only reaches "connected". When this is
// fixed, this test should be updated.
- EXPECT_EQ_WAIT(webrtc::PeerConnectionInterface::kIceConnectionCompleted,
+ EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionCompleted,
caller()->ice_connection_state(), kDefaultTimeout);
- EXPECT_EQ_WAIT(webrtc::PeerConnectionInterface::kIceConnectionConnected,
+ EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionConnected,
callee()->ice_connection_state(), kDefaultTimeout);
}
@@ -1679,9 +1676,9 @@ constexpr int kOnlyLocalPorts = cricket::PORTALLOCATOR_DISABLE_STUN |
TEST_P(PeerConnectionIntegrationTest,
IceStatesReachCompletionWithRemoteHostname) {
auto caller_resolver_factory =
- std::make_unique<NiceMock<webrtc::MockAsyncDnsResolverFactory>>();
+ std::make_unique<NiceMock<MockAsyncDnsResolverFactory>>();
auto callee_resolver_factory =
- std::make_unique<NiceMock<webrtc::MockAsyncDnsResolverFactory>>();
+ std::make_unique<NiceMock<MockAsyncDnsResolverFactory>>();
auto callee_async_resolver =
std::make_unique<NiceMock<MockAsyncDnsResolver>>();
auto caller_async_resolver =
@@ -1695,12 +1692,12 @@ TEST_P(PeerConnectionIntegrationTest,
// P2PTransportChannel.
EXPECT_CALL(*caller_resolver_factory, Create())
.WillOnce(Return(ByMove(std::move(caller_async_resolver))));
- webrtc::PeerConnectionDependencies caller_deps(nullptr);
+ PeerConnectionDependencies caller_deps(nullptr);
caller_deps.async_dns_resolver_factory = std::move(caller_resolver_factory);
EXPECT_CALL(*callee_resolver_factory, Create())
.WillOnce(Return(ByMove(std::move(callee_async_resolver))));
- webrtc::PeerConnectionDependencies callee_deps(nullptr);
+ PeerConnectionDependencies callee_deps(nullptr);
callee_deps.async_dns_resolver_factory = std::move(callee_resolver_factory);
PeerConnectionInterface::RTCConfiguration config;
@@ -1719,9 +1716,9 @@ TEST_P(PeerConnectionIntegrationTest,
// Enable hostname candidates with mDNS names.
caller()->SetMdnsResponder(
- std::make_unique<webrtc::FakeMdnsResponder>(network_thread()));
+ std::make_unique<FakeMdnsResponder>(network_thread()));
callee()->SetMdnsResponder(
- std::make_unique<webrtc::FakeMdnsResponder>(network_thread()));
+ std::make_unique<FakeMdnsResponder>(network_thread()));
SetPortAllocatorFlags(kOnlyLocalPorts, kOnlyLocalPorts);
@@ -1730,18 +1727,18 @@ TEST_P(PeerConnectionIntegrationTest,
callee()->AddAudioVideoTracks();
caller()->CreateAndSetAndSignalOffer();
ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
- EXPECT_EQ_WAIT(webrtc::PeerConnectionInterface::kIceConnectionCompleted,
+ EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionCompleted,
caller()->ice_connection_state(), kDefaultTimeout);
- EXPECT_EQ_WAIT(webrtc::PeerConnectionInterface::kIceConnectionConnected,
+ EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionConnected,
callee()->ice_connection_state(), kDefaultTimeout);
// Part of reporting the stats will occur on the network thread, so flush it
// before checking NumEvents.
SendTask(network_thread(), [] {});
- EXPECT_METRIC_EQ(1, webrtc::metrics::NumEvents(
- "WebRTC.PeerConnection.CandidatePairType_UDP",
- webrtc::kIceCandidatePairHostNameHostName));
+ EXPECT_METRIC_EQ(
+ 1, metrics::NumEvents("WebRTC.PeerConnection.CandidatePairType_UDP",
+ kIceCandidatePairHostNameHostName));
DestroyPeerConnections();
}
@@ -1758,8 +1755,8 @@ class PeerConnectionIntegrationIceStatesTest
}
void StartStunServer(const SocketAddress& server_address) {
- stun_server_.reset(
- cricket::TestStunServer::Create(firewall(), server_address));
+ stun_server_ = cricket::TestStunServer::Create(firewall(), server_address,
+ *network_thread());
}
bool TestIPv6() {
@@ -1805,7 +1802,7 @@ class PeerConnectionIntegrationIceStatesTest
private:
uint32_t port_allocator_flags_;
- std::unique_ptr<cricket::TestStunServer> stun_server_;
+ cricket::TestStunServer::StunServerPtr stun_server_;
};
// Ensure FakeClockForTest is constructed first (see class for rationale).
@@ -1862,9 +1859,9 @@ TEST_P(PeerConnectionIntegrationIceStatesTest, MAYBE_VerifyBestConnection) {
caller()->CreateAndSetAndSignalOffer();
ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
- EXPECT_EQ_WAIT(webrtc::PeerConnectionInterface::kIceConnectionCompleted,
+ EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionCompleted,
caller()->ice_connection_state(), kDefaultTimeout);
- EXPECT_EQ_WAIT(webrtc::PeerConnectionInterface::kIceConnectionConnected,
+ EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionConnected,
callee()->ice_connection_state(), kDefaultTimeout);
// Part of reporting the stats will occur on the network thread, so flush it
@@ -1872,10 +1869,10 @@ TEST_P(PeerConnectionIntegrationIceStatesTest, MAYBE_VerifyBestConnection) {
SendTask(network_thread(), [] {});
// TODO(bugs.webrtc.org/9456): Fix it.
- const int num_best_ipv4 = webrtc::metrics::NumEvents(
- "WebRTC.PeerConnection.IPMetrics", webrtc::kBestConnections_IPv4);
- const int num_best_ipv6 = webrtc::metrics::NumEvents(
- "WebRTC.PeerConnection.IPMetrics", webrtc::kBestConnections_IPv6);
+ const int num_best_ipv4 = metrics::NumEvents(
+ "WebRTC.PeerConnection.IPMetrics", kBestConnections_IPv4);
+ const int num_best_ipv6 = metrics::NumEvents(
+ "WebRTC.PeerConnection.IPMetrics", kBestConnections_IPv6);
if (TestIPv6()) {
// When IPv6 is enabled, we should prefer an IPv6 connection over an IPv4
// connection.
@@ -1886,12 +1883,12 @@ TEST_P(PeerConnectionIntegrationIceStatesTest, MAYBE_VerifyBestConnection) {
EXPECT_METRIC_EQ(0, num_best_ipv6);
}
- EXPECT_METRIC_EQ(0, webrtc::metrics::NumEvents(
- "WebRTC.PeerConnection.CandidatePairType_UDP",
- webrtc::kIceCandidatePairHostHost));
- EXPECT_METRIC_EQ(1, webrtc::metrics::NumEvents(
- "WebRTC.PeerConnection.CandidatePairType_UDP",
- webrtc::kIceCandidatePairHostPublicHostPublic));
+ EXPECT_METRIC_EQ(
+ 0, metrics::NumEvents("WebRTC.PeerConnection.CandidatePairType_UDP",
+ kIceCandidatePairHostHost));
+ EXPECT_METRIC_EQ(
+ 1, metrics::NumEvents("WebRTC.PeerConnection.CandidatePairType_UDP",
+ kIceCandidatePairHostPublicHostPublic));
}
constexpr uint32_t kFlagsIPv4NoStun = cricket::PORTALLOCATOR_DISABLE_TCP |
@@ -1931,17 +1928,17 @@ TEST_P(PeerConnectionIntegrationTest, MediaContinuesFlowingAfterIceRestart) {
callee()->AddAudioVideoTracks();
caller()->CreateAndSetAndSignalOffer();
ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
- EXPECT_EQ_WAIT(webrtc::PeerConnectionInterface::kIceConnectionCompleted,
+ EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionCompleted,
caller()->ice_connection_state(), kMaxWaitForFramesMs);
- EXPECT_EQ_WAIT(webrtc::PeerConnectionInterface::kIceConnectionConnected,
+ EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionConnected,
callee()->ice_connection_state(), kMaxWaitForFramesMs);
// To verify that the ICE restart actually occurs, get
// ufrag/password/candidates before and after restart.
// Create an SDP string of the first audio candidate for both clients.
- const webrtc::IceCandidateCollection* audio_candidates_caller =
+ const IceCandidateCollection* audio_candidates_caller =
caller()->pc()->local_description()->candidates(0);
- const webrtc::IceCandidateCollection* audio_candidates_callee =
+ const IceCandidateCollection* audio_candidates_callee =
callee()->pc()->local_description()->candidates(0);
ASSERT_GT(audio_candidates_caller->count(), 0u);
ASSERT_GT(audio_candidates_callee->count(), 0u);
@@ -1964,9 +1961,9 @@ TEST_P(PeerConnectionIntegrationTest, MediaContinuesFlowingAfterIceRestart) {
caller()->SetOfferAnswerOptions(IceRestartOfferAnswerOptions());
caller()->CreateAndSetAndSignalOffer();
ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
- EXPECT_EQ_WAIT(webrtc::PeerConnectionInterface::kIceConnectionCompleted,
+ EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionCompleted,
caller()->ice_connection_state(), kMaxWaitForFramesMs);
- EXPECT_EQ_WAIT(webrtc::PeerConnectionInterface::kIceConnectionConnected,
+ EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionConnected,
callee()->ice_connection_state(), kMaxWaitForFramesMs);
// Grab the ufrags/candidates again.
@@ -2141,9 +2138,9 @@ TEST_F(PeerConnectionIntegrationTestPlanB,
caller()->CreateAndSetAndSignalOffer();
ASSERT_TRUE_WAIT(SignalingStateStable(), kMaxWaitForActivationMs);
// Wait for ICE to complete, without any tracks being set.
- EXPECT_EQ_WAIT(webrtc::PeerConnectionInterface::kIceConnectionCompleted,
+ EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionCompleted,
caller()->ice_connection_state(), kMaxWaitForFramesMs);
- EXPECT_EQ_WAIT(webrtc::PeerConnectionInterface::kIceConnectionConnected,
+ EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionConnected,
callee()->ice_connection_state(), kMaxWaitForFramesMs);
// Now set the tracks, and expect frames to immediately start flowing.
EXPECT_TRUE(
@@ -2182,9 +2179,9 @@ TEST_F(PeerConnectionIntegrationTestUnifiedPlan,
caller()->CreateAndSetAndSignalOffer();
ASSERT_TRUE_WAIT(SignalingStateStable(), kMaxWaitForActivationMs);
// Wait for ICE to complete, without any tracks being set.
- EXPECT_EQ_WAIT(webrtc::PeerConnectionInterface::kIceConnectionCompleted,
+ EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionCompleted,
caller()->ice_connection_state(), kMaxWaitForFramesMs);
- EXPECT_EQ_WAIT(webrtc::PeerConnectionInterface::kIceConnectionConnected,
+ EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionConnected,
callee()->ice_connection_state(), kMaxWaitForFramesMs);
// Now set the tracks, and expect frames to immediately start flowing.
auto callee_audio_sender = callee()->pc()->GetSenders()[0];
@@ -2279,21 +2276,21 @@ TEST_P(PeerConnectionIntegrationTestWithFakeClock,
});
PeerConnectionInterface::RTCConfiguration client_1_config;
- webrtc::PeerConnectionInterface::IceServer ice_server_1;
+ PeerConnectionInterface::IceServer ice_server_1;
ice_server_1.urls.push_back("turn:88.88.88.0:3478");
ice_server_1.username = "test";
ice_server_1.password = "test";
client_1_config.servers.push_back(ice_server_1);
- client_1_config.type = webrtc::PeerConnectionInterface::kRelay;
+ client_1_config.type = PeerConnectionInterface::kRelay;
client_1_config.presume_writable_when_fully_relayed = true;
PeerConnectionInterface::RTCConfiguration client_2_config;
- webrtc::PeerConnectionInterface::IceServer ice_server_2;
+ PeerConnectionInterface::IceServer ice_server_2;
ice_server_2.urls.push_back("turn:99.99.99.0:3478");
ice_server_2.username = "test";
ice_server_2.password = "test";
client_2_config.servers.push_back(ice_server_2);
- client_2_config.type = webrtc::PeerConnectionInterface::kRelay;
+ client_2_config.type = PeerConnectionInterface::kRelay;
client_2_config.presume_writable_when_fully_relayed = true;
ASSERT_TRUE(
@@ -2326,22 +2323,22 @@ TEST_P(PeerConnectionIntegrationTestWithFakeClock,
caller()->AddAudioTrack();
// Call getStats, assert there are no candidates.
- rtc::scoped_refptr<const webrtc::RTCStatsReport> first_report =
+ rtc::scoped_refptr<const RTCStatsReport> first_report =
caller()->NewGetStats();
ASSERT_TRUE(first_report);
auto first_candidate_stats =
- first_report->GetStatsOfType<webrtc::RTCLocalIceCandidateStats>();
+ first_report->GetStatsOfType<RTCLocalIceCandidateStats>();
ASSERT_EQ(first_candidate_stats.size(), 0u);
// Create an offer at the caller and set it as remote description on the
// callee.
caller()->CreateAndSetAndSignalOffer();
// Call getStats again, assert there are candidates now.
- rtc::scoped_refptr<const webrtc::RTCStatsReport> second_report =
+ rtc::scoped_refptr<const RTCStatsReport> second_report =
caller()->NewGetStats();
ASSERT_TRUE(second_report);
auto second_candidate_stats =
- second_report->GetStatsOfType<webrtc::RTCLocalIceCandidateStats>();
+ second_report->GetStatsOfType<RTCLocalIceCandidateStats>();
ASSERT_NE(second_candidate_stats.size(), 0u);
// The fake clock ensures that no time has passed so the cache must have been
@@ -2362,17 +2359,17 @@ TEST_P(PeerConnectionIntegrationTestWithFakeClock,
kDefaultTimeout, FakeClock());
// Call getStats, assert there are no candidates.
- rtc::scoped_refptr<const webrtc::RTCStatsReport> first_report =
+ rtc::scoped_refptr<const RTCStatsReport> first_report =
caller()->NewGetStats();
ASSERT_TRUE(first_report);
auto first_candidate_stats =
- first_report->GetStatsOfType<webrtc::RTCRemoteIceCandidateStats>();
+ first_report->GetStatsOfType<RTCRemoteIceCandidateStats>();
ASSERT_EQ(first_candidate_stats.size(), 0u);
// Add a "fake" candidate.
absl::optional<RTCError> result;
caller()->pc()->AddIceCandidate(
- absl::WrapUnique(webrtc::CreateIceCandidate(
+ absl::WrapUnique(CreateIceCandidate(
"", 0,
"candidate:2214029314 1 udp 2122260223 127.0.0.1 49152 typ host",
nullptr)),
@@ -2381,11 +2378,11 @@ TEST_P(PeerConnectionIntegrationTestWithFakeClock,
ASSERT_TRUE(result.value().ok());
// Call getStats again, assert there is a remote candidate now.
- rtc::scoped_refptr<const webrtc::RTCStatsReport> second_report =
+ rtc::scoped_refptr<const RTCStatsReport> second_report =
caller()->NewGetStats();
ASSERT_TRUE(second_report);
auto second_candidate_stats =
- second_report->GetStatsOfType<webrtc::RTCRemoteIceCandidateStats>();
+ second_report->GetStatsOfType<RTCRemoteIceCandidateStats>();
ASSERT_EQ(second_candidate_stats.size(), 1u);
// The fake clock ensures that no time has passed so the cache must have been
@@ -2413,22 +2410,22 @@ TEST_P(PeerConnectionIntegrationTest, TurnCustomizerUsedForTurnConnections) {
turn_server_2_external_address);
PeerConnectionInterface::RTCConfiguration client_1_config;
- webrtc::PeerConnectionInterface::IceServer ice_server_1;
+ PeerConnectionInterface::IceServer ice_server_1;
ice_server_1.urls.push_back("turn:88.88.88.0:3478");
ice_server_1.username = "test";
ice_server_1.password = "test";
client_1_config.servers.push_back(ice_server_1);
- client_1_config.type = webrtc::PeerConnectionInterface::kRelay;
+ client_1_config.type = PeerConnectionInterface::kRelay;
auto* customizer1 = CreateTurnCustomizer();
client_1_config.turn_customizer = customizer1;
PeerConnectionInterface::RTCConfiguration client_2_config;
- webrtc::PeerConnectionInterface::IceServer ice_server_2;
+ PeerConnectionInterface::IceServer ice_server_2;
ice_server_2.urls.push_back("turn:99.99.99.0:3478");
ice_server_2.username = "test";
ice_server_2.password = "test";
client_2_config.servers.push_back(ice_server_2);
- client_2_config.type = webrtc::PeerConnectionInterface::kRelay;
+ client_2_config.type = PeerConnectionInterface::kRelay;
auto* customizer2 = CreateTurnCustomizer();
client_2_config.turn_customizer = customizer2;
@@ -2460,18 +2457,18 @@ TEST_P(PeerConnectionIntegrationTest, TCPUsedForTurnConnections) {
CreateTurnServer(turn_server_internal_address, turn_server_external_address,
cricket::PROTO_TCP);
- webrtc::PeerConnectionInterface::IceServer ice_server;
+ PeerConnectionInterface::IceServer ice_server;
ice_server.urls.push_back("turn:88.88.88.0:3478?transport=tcp");
ice_server.username = "test";
ice_server.password = "test";
PeerConnectionInterface::RTCConfiguration client_1_config;
client_1_config.servers.push_back(ice_server);
- client_1_config.type = webrtc::PeerConnectionInterface::kRelay;
+ client_1_config.type = PeerConnectionInterface::kRelay;
PeerConnectionInterface::RTCConfiguration client_2_config;
client_2_config.servers.push_back(ice_server);
- client_2_config.type = webrtc::PeerConnectionInterface::kRelay;
+ client_2_config.type = PeerConnectionInterface::kRelay;
ASSERT_TRUE(
CreatePeerConnectionWrappersWithConfig(client_1_config, client_2_config));
@@ -2482,7 +2479,7 @@ TEST_P(PeerConnectionIntegrationTest, TCPUsedForTurnConnections) {
callee()->AddAudioVideoTracks();
caller()->CreateAndSetAndSignalOffer();
ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
- EXPECT_EQ_WAIT(webrtc::PeerConnectionInterface::kIceConnectionConnected,
+ EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionConnected,
callee()->ice_connection_state(), kMaxWaitForFramesMs);
MediaExpectations media_expectations;
@@ -2506,20 +2503,20 @@ TEST_P(PeerConnectionIntegrationTest,
CreateTurnServer(turn_server_internal_address, turn_server_external_address,
cricket::PROTO_TLS, "88.88.88.0");
- webrtc::PeerConnectionInterface::IceServer ice_server;
+ PeerConnectionInterface::IceServer ice_server;
ice_server.urls.push_back("turns:88.88.88.0:3478?transport=tcp");
ice_server.username = "test";
ice_server.password = "test";
PeerConnectionInterface::RTCConfiguration client_1_config;
client_1_config.servers.push_back(ice_server);
- client_1_config.type = webrtc::PeerConnectionInterface::kRelay;
+ client_1_config.type = PeerConnectionInterface::kRelay;
PeerConnectionInterface::RTCConfiguration client_2_config;
client_2_config.servers.push_back(ice_server);
// Setting the type to kRelay forces the connection to go through a TURN
// server.
- client_2_config.type = webrtc::PeerConnectionInterface::kRelay;
+ client_2_config.type = PeerConnectionInterface::kRelay;
// Get a copy to the pointer so we can verify calls later.
rtc::TestCertificateVerifier* client_1_cert_verifier =
@@ -2530,10 +2527,10 @@ TEST_P(PeerConnectionIntegrationTest,
client_2_cert_verifier->verify_certificate_ = true;
// Create the dependencies with the test certificate verifier.
- webrtc::PeerConnectionDependencies client_1_deps(nullptr);
+ PeerConnectionDependencies client_1_deps(nullptr);
client_1_deps.tls_cert_verifier =
std::unique_ptr<rtc::TestCertificateVerifier>(client_1_cert_verifier);
- webrtc::PeerConnectionDependencies client_2_deps(nullptr);
+ PeerConnectionDependencies client_2_deps(nullptr);
client_2_deps.tls_cert_verifier =
std::unique_ptr<rtc::TestCertificateVerifier>(client_2_cert_verifier);
@@ -2644,7 +2641,7 @@ TEST_P(PeerConnectionIntegrationTest, GetSourcesAudio) {
ASSERT_GT(receiver->GetParameters().encodings.size(), 0u);
EXPECT_EQ(receiver->GetParameters().encodings[0].ssrc,
sources[0].source_id());
- EXPECT_EQ(webrtc::RtpSourceType::SSRC, sources[0].source_type());
+ EXPECT_EQ(RtpSourceType::SSRC, sources[0].source_type());
}
TEST_P(PeerConnectionIntegrationTest, GetSourcesVideo) {
@@ -2665,7 +2662,7 @@ TEST_P(PeerConnectionIntegrationTest, GetSourcesVideo) {
ASSERT_GT(sources.size(), 0u);
EXPECT_EQ(receiver->GetParameters().encodings[0].ssrc,
sources[0].source_id());
- EXPECT_EQ(webrtc::RtpSourceType::SSRC, sources[0].source_type());
+ EXPECT_EQ(RtpSourceType::SSRC, sources[0].source_type());
}
TEST_P(PeerConnectionIntegrationTest, UnsignaledSsrcGetSourcesAudio) {
@@ -2684,7 +2681,7 @@ TEST_P(PeerConnectionIntegrationTest, UnsignaledSsrcGetSourcesAudio) {
})(),
kDefaultTimeout);
ASSERT_GT(sources.size(), 0u);
- EXPECT_EQ(webrtc::RtpSourceType::SSRC, sources[0].source_type());
+ EXPECT_EQ(RtpSourceType::SSRC, sources[0].source_type());
}
TEST_P(PeerConnectionIntegrationTest, UnsignaledSsrcGetSourcesVideo) {
@@ -2703,7 +2700,7 @@ TEST_P(PeerConnectionIntegrationTest, UnsignaledSsrcGetSourcesVideo) {
})(),
kDefaultTimeout);
ASSERT_GT(sources.size(), 0u);
- EXPECT_EQ(webrtc::RtpSourceType::SSRC, sources[0].source_type());
+ EXPECT_EQ(RtpSourceType::SSRC, sources[0].source_type());
}
// Similar to the above test, except instead of waiting until GetSources() is
@@ -2728,7 +2725,7 @@ TEST_P(PeerConnectionIntegrationTest,
std::vector<RtpSource> sources = receiver->GetSources();
// SSRC history must not be cleared since the reception of the first frame.
ASSERT_GT(sources.size(), 0u);
- EXPECT_EQ(webrtc::RtpSourceType::SSRC, sources[0].source_type());
+ EXPECT_EQ(RtpSourceType::SSRC, sources[0].source_type());
}
TEST_P(PeerConnectionIntegrationTest, UnsignaledSsrcGetParametersAudio) {
@@ -2791,9 +2788,9 @@ TEST_F(PeerConnectionIntegrationTestPlanB, RemoveAndAddTrackWithNewStreamId) {
ConnectFakeSignaling();
// Add track using stream 1, do offer/answer.
- rtc::scoped_refptr<webrtc::AudioTrackInterface> track =
+ rtc::scoped_refptr<AudioTrackInterface> track =
caller()->CreateLocalAudioTrack();
- rtc::scoped_refptr<webrtc::RtpSenderInterface> sender =
+ rtc::scoped_refptr<RtpSenderInterface> sender =
caller()->AddTrack(track, {"stream_1"});
caller()->CreateAndSetAndSignalOffer();
ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
@@ -2819,18 +2816,70 @@ TEST_P(PeerConnectionIntegrationTest, RtcEventLogOutputWriteCalled) {
ASSERT_TRUE(CreatePeerConnectionWrappers());
ConnectFakeSignaling();
- auto output = std::make_unique<testing::NiceMock<MockRtcEventLogOutput>>();
- ON_CALL(*output, IsActive()).WillByDefault(::testing::Return(true));
- ON_CALL(*output, Write(::testing::A<absl::string_view>()))
- .WillByDefault(::testing::Return(true));
- EXPECT_CALL(*output, Write(::testing::A<absl::string_view>()))
- .Times(::testing::AtLeast(1));
- EXPECT_TRUE(caller()->pc()->StartRtcEventLog(
- std::move(output), webrtc::RtcEventLog::kImmediateOutput));
+ auto output = std::make_unique<NiceMock<MockRtcEventLogOutput>>();
+ ON_CALL(*output, IsActive).WillByDefault(Return(true));
+ ON_CALL(*output, Write).WillByDefault(Return(true));
+ EXPECT_CALL(*output, Write).Times(AtLeast(1));
+ EXPECT_TRUE(caller()->pc()->StartRtcEventLog(std::move(output),
+ RtcEventLog::kImmediateOutput));
+
+ caller()->AddAudioVideoTracks();
+ caller()->CreateAndSetAndSignalOffer();
+ ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
+}
+
+TEST_P(PeerConnectionIntegrationTest, RtcEventLogOutputWriteCalledOnStop) {
+ // This test uses check point to ensure log is written before peer connection
+ // is destroyed.
+ // https://google.github.io/googletest/gmock_cook_book.html#UsingCheckPoints
+ MockFunction<void()> test_is_complete;
+ ASSERT_TRUE(CreatePeerConnectionWrappers());
+ ConnectFakeSignaling();
+
+ auto output = std::make_unique<NiceMock<MockRtcEventLogOutput>>();
+ ON_CALL(*output, IsActive).WillByDefault(Return(true));
+ ON_CALL(*output, Write).WillByDefault(Return(true));
+ InSequence s;
+ EXPECT_CALL(*output, Write).Times(AtLeast(1));
+ EXPECT_CALL(test_is_complete, Call);
+
+ // Use large output period to prevent this test pass for the wrong reason.
+ EXPECT_TRUE(caller()->pc()->StartRtcEventLog(std::move(output),
+ /*output_period_ms=*/100'000));
caller()->AddAudioVideoTracks();
caller()->CreateAndSetAndSignalOffer();
ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
+
+ caller()->pc()->StopRtcEventLog();
+ test_is_complete.Call();
+}
+
+TEST_P(PeerConnectionIntegrationTest, RtcEventLogOutputWriteCalledOnClose) {
+ // This test uses check point to ensure log is written before peer connection
+ // is destroyed.
+ // https://google.github.io/googletest/gmock_cook_book.html#UsingCheckPoints
+ MockFunction<void()> test_is_complete;
+ ASSERT_TRUE(CreatePeerConnectionWrappers());
+ ConnectFakeSignaling();
+
+ auto output = std::make_unique<NiceMock<MockRtcEventLogOutput>>();
+ ON_CALL(*output, IsActive).WillByDefault(Return(true));
+ ON_CALL(*output, Write).WillByDefault(Return(true));
+ InSequence s;
+ EXPECT_CALL(*output, Write).Times(AtLeast(1));
+ EXPECT_CALL(test_is_complete, Call);
+
+ // Use large output period to prevent this test pass for the wrong reason.
+ EXPECT_TRUE(caller()->pc()->StartRtcEventLog(std::move(output),
+ /*output_period_ms=*/100'000));
+
+ caller()->AddAudioVideoTracks();
+ caller()->CreateAndSetAndSignalOffer();
+ ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
+
+ caller()->pc()->Close();
+ test_is_complete.Call();
}
// Test that if candidates are only signaled by applying full session
@@ -2900,8 +2949,7 @@ TEST_P(PeerConnectionIntegrationTest, DisableAndEnableAudioPlayout) {
double GetAudioEnergyStat(PeerConnectionIntegrationWrapper* pc) {
auto report = pc->NewGetStats();
- auto inbound_rtps =
- report->GetStatsOfType<webrtc::RTCInboundRtpStreamStats>();
+ auto inbound_rtps = report->GetStatsOfType<RTCInboundRtpStreamStats>();
RTC_CHECK(!inbound_rtps.empty());
auto* inbound_rtp = inbound_rtps[0];
if (!inbound_rtp->total_audio_energy.is_defined()) {
@@ -2974,20 +3022,20 @@ TEST_P(PeerConnectionIntegrationTest,
ASSERT_TRUE_WAIT(DtlsConnected(), kDefaultTimeout);
ASSERT_NE(nullptr, caller()->event_log_factory());
ASSERT_NE(nullptr, callee()->event_log_factory());
- webrtc::FakeRtcEventLog* caller_event_log =
+ FakeRtcEventLog* caller_event_log =
caller()->event_log_factory()->last_log_created();
- webrtc::FakeRtcEventLog* callee_event_log =
+ FakeRtcEventLog* callee_event_log =
callee()->event_log_factory()->last_log_created();
ASSERT_NE(nullptr, caller_event_log);
ASSERT_NE(nullptr, callee_event_log);
- int caller_ice_config_count = caller_event_log->GetEventCount(
- webrtc::RtcEvent::Type::IceCandidatePairConfig);
- int caller_ice_event_count = caller_event_log->GetEventCount(
- webrtc::RtcEvent::Type::IceCandidatePairEvent);
- int callee_ice_config_count = callee_event_log->GetEventCount(
- webrtc::RtcEvent::Type::IceCandidatePairConfig);
- int callee_ice_event_count = callee_event_log->GetEventCount(
- webrtc::RtcEvent::Type::IceCandidatePairEvent);
+ int caller_ice_config_count =
+ caller_event_log->GetEventCount(RtcEvent::Type::IceCandidatePairConfig);
+ int caller_ice_event_count =
+ caller_event_log->GetEventCount(RtcEvent::Type::IceCandidatePairEvent);
+ int callee_ice_config_count =
+ callee_event_log->GetEventCount(RtcEvent::Type::IceCandidatePairConfig);
+ int callee_ice_event_count =
+ callee_event_log->GetEventCount(RtcEvent::Type::IceCandidatePairEvent);
EXPECT_LT(0, caller_ice_config_count);
EXPECT_LT(0, caller_ice_event_count);
EXPECT_LT(0, callee_ice_config_count);
@@ -3001,20 +3049,20 @@ TEST_P(PeerConnectionIntegrationTest, RegatherAfterChangingIceTransportType) {
CreateTurnServer(turn_server_internal_address, turn_server_external_address);
- webrtc::PeerConnectionInterface::IceServer ice_server;
+ PeerConnectionInterface::IceServer ice_server;
ice_server.urls.push_back("turn:88.88.88.0:3478");
ice_server.username = "test";
ice_server.password = "test";
PeerConnectionInterface::RTCConfiguration caller_config;
caller_config.servers.push_back(ice_server);
- caller_config.type = webrtc::PeerConnectionInterface::kRelay;
+ caller_config.type = PeerConnectionInterface::kRelay;
caller_config.continual_gathering_policy = PeerConnection::GATHER_CONTINUALLY;
caller_config.surface_ice_candidates_on_ice_transport_type_changed = true;
PeerConnectionInterface::RTCConfiguration callee_config;
callee_config.servers.push_back(ice_server);
- callee_config.type = webrtc::PeerConnectionInterface::kRelay;
+ callee_config.type = PeerConnectionInterface::kRelay;
callee_config.continual_gathering_policy = PeerConnection::GATHER_CONTINUALLY;
callee_config.surface_ice_candidates_on_ice_transport_type_changed = true;
@@ -3031,9 +3079,9 @@ TEST_P(PeerConnectionIntegrationTest, RegatherAfterChangingIceTransportType) {
// kIceGatheringComplete (see
// P2PTransportChannel::OnCandidatesAllocationDone), and consequently not
// kIceConnectionComplete.
- EXPECT_EQ_WAIT(webrtc::PeerConnectionInterface::kIceConnectionConnected,
+ EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionConnected,
caller()->ice_connection_state(), kDefaultTimeout);
- EXPECT_EQ_WAIT(webrtc::PeerConnectionInterface::kIceConnectionConnected,
+ EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionConnected,
callee()->ice_connection_state(), kDefaultTimeout);
// Note that we cannot use the metric
// `WebRTC.PeerConnection.CandidatePairType_UDP` in this test since this
@@ -3046,7 +3094,7 @@ TEST_P(PeerConnectionIntegrationTest, RegatherAfterChangingIceTransportType) {
// Loosen the caller's candidate filter.
caller_config = caller()->pc()->GetConfiguration();
- caller_config.type = webrtc::PeerConnectionInterface::kAll;
+ caller_config.type = PeerConnectionInterface::kAll;
caller()->pc()->SetConfiguration(caller_config);
// We should have gathered a new host candidate.
EXPECT_EQ_WAIT(cricket::LOCAL_PORT_TYPE,
@@ -3054,7 +3102,7 @@ TEST_P(PeerConnectionIntegrationTest, RegatherAfterChangingIceTransportType) {
// Loosen the callee's candidate filter.
callee_config = callee()->pc()->GetConfiguration();
- callee_config.type = webrtc::PeerConnectionInterface::kAll;
+ callee_config.type = PeerConnectionInterface::kAll;
callee()->pc()->SetConfiguration(callee_config);
EXPECT_EQ_WAIT(cricket::LOCAL_PORT_TYPE,
callee()->last_candidate_gathered().type(), kDefaultTimeout);
@@ -3084,19 +3132,19 @@ TEST_P(PeerConnectionIntegrationTest, OnIceCandidateError) {
CreateTurnServer(turn_server_internal_address, turn_server_external_address);
- webrtc::PeerConnectionInterface::IceServer ice_server;
+ PeerConnectionInterface::IceServer ice_server;
ice_server.urls.push_back("turn:88.88.88.0:3478");
ice_server.username = "test";
ice_server.password = "123";
PeerConnectionInterface::RTCConfiguration caller_config;
caller_config.servers.push_back(ice_server);
- caller_config.type = webrtc::PeerConnectionInterface::kRelay;
+ caller_config.type = PeerConnectionInterface::kRelay;
caller_config.continual_gathering_policy = PeerConnection::GATHER_CONTINUALLY;
PeerConnectionInterface::RTCConfiguration callee_config;
callee_config.servers.push_back(ice_server);
- callee_config.type = webrtc::PeerConnectionInterface::kRelay;
+ callee_config.type = PeerConnectionInterface::kRelay;
callee_config.continual_gathering_policy = PeerConnection::GATHER_CONTINUALLY;
ASSERT_TRUE(
@@ -3115,19 +3163,19 @@ TEST_P(PeerConnectionIntegrationTest, OnIceCandidateError) {
}
TEST_P(PeerConnectionIntegrationTest, OnIceCandidateErrorWithEmptyAddress) {
- webrtc::PeerConnectionInterface::IceServer ice_server;
+ PeerConnectionInterface::IceServer ice_server;
ice_server.urls.push_back("turn:127.0.0.1:3478?transport=tcp");
ice_server.username = "test";
ice_server.password = "test";
PeerConnectionInterface::RTCConfiguration caller_config;
caller_config.servers.push_back(ice_server);
- caller_config.type = webrtc::PeerConnectionInterface::kRelay;
+ caller_config.type = PeerConnectionInterface::kRelay;
caller_config.continual_gathering_policy = PeerConnection::GATHER_CONTINUALLY;
PeerConnectionInterface::RTCConfiguration callee_config;
callee_config.servers.push_back(ice_server);
- callee_config.type = webrtc::PeerConnectionInterface::kRelay;
+ callee_config.type = PeerConnectionInterface::kRelay;
callee_config.continual_gathering_policy = PeerConnection::GATHER_CONTINUALLY;
ASSERT_TRUE(
@@ -3697,7 +3745,7 @@ TEST_P(PeerConnectionIntegrationTest, EndToEndRtpSenderVideoEncoderSelector) {
CreateOneDirectionalPeerConnectionWrappers(/*caller_to_callee=*/true));
ConnectFakeSignaling();
// Add one-directional video, from caller to callee.
- rtc::scoped_refptr<webrtc::VideoTrackInterface> caller_track =
+ rtc::scoped_refptr<VideoTrackInterface> caller_track =
caller()->CreateLocalVideoTrack();
auto sender = caller()->AddTrack(caller_track);
PeerConnectionInterface::RTCOfferAnswerOptions options;
@@ -3722,7 +3770,7 @@ TEST_P(PeerConnectionIntegrationTest, EndToEndRtpSenderVideoEncoderSelector) {
}
int NacksReceivedCount(PeerConnectionIntegrationWrapper& pc) {
- rtc::scoped_refptr<const webrtc::RTCStatsReport> report = pc.NewGetStats();
+ rtc::scoped_refptr<const RTCStatsReport> report = pc.NewGetStats();
auto sender_stats = report->GetStatsOfType<RTCOutboundRtpStreamStats>();
if (sender_stats.size() != 1) {
ADD_FAILURE();
@@ -3735,7 +3783,7 @@ int NacksReceivedCount(PeerConnectionIntegrationWrapper& pc) {
}
int NacksSentCount(PeerConnectionIntegrationWrapper& pc) {
- rtc::scoped_refptr<const webrtc::RTCStatsReport> report = pc.NewGetStats();
+ rtc::scoped_refptr<const RTCStatsReport> report = pc.NewGetStats();
auto receiver_stats = report->GetStatsOfType<RTCInboundRtpStreamStats>();
if (receiver_stats.size() != 1) {
ADD_FAILURE();
@@ -3761,11 +3809,10 @@ TEST_F(PeerConnectionIntegrationTestUnifiedPlan,
// codecs.
caller()->SetGeneratedSdpMunger([](cricket::SessionDescription* desc) {
for (ContentInfo& content : desc->contents()) {
- cricket::AudioContentDescription* media =
- content.media_description()->as_audio();
- std::vector<cricket::AudioCodec> codecs = media->codecs();
- std::vector<cricket::AudioCodec> codecs_out;
- for (cricket::AudioCodec codec : codecs) {
+ cricket::MediaContentDescription* media = content.media_description();
+ std::vector<cricket::Codec> codecs = media->codecs();
+ std::vector<cricket::Codec> codecs_out;
+ for (cricket::Codec codec : codecs) {
if (codec.name == "opus") {
codec.AddFeedbackParam(cricket::FeedbackParam(
cricket::kRtcpFbParamNack, cricket::kParamValueEmpty));
@@ -3812,11 +3859,10 @@ TEST_F(PeerConnectionIntegrationTestUnifiedPlan, VideoPacketLossCausesNack) {
// codecs.
caller()->SetGeneratedSdpMunger([](cricket::SessionDescription* desc) {
for (ContentInfo& content : desc->contents()) {
- cricket::VideoContentDescription* media =
- content.media_description()->as_video();
- std::vector<cricket::VideoCodec> codecs = media->codecs();
- std::vector<cricket::VideoCodec> codecs_out;
- for (cricket::VideoCodec codec : codecs) {
+ cricket::MediaContentDescription* media = content.media_description();
+ std::vector<cricket::Codec> codecs = media->codecs();
+ std::vector<cricket::Codec> codecs_out;
+ for (cricket::Codec codec : codecs) {
if (codec.name == "VP8") {
ASSERT_TRUE(codec.HasFeedbackParam(cricket::FeedbackParam(
cricket::kRtcpFbParamNack, cricket::kParamValueEmpty)));
diff --git a/third_party/libwebrtc/pc/peer_connection_interface_unittest.cc b/third_party/libwebrtc/pc/peer_connection_interface_unittest.cc
index 1f5ab2f449..5ee9881b84 100644
--- a/third_party/libwebrtc/pc/peer_connection_interface_unittest.cc
+++ b/third_party/libwebrtc/pc/peer_connection_interface_unittest.cc
@@ -22,9 +22,9 @@
#include "api/audio/audio_mixer.h"
#include "api/audio_codecs/builtin_audio_decoder_factory.h"
#include "api/audio_codecs/builtin_audio_encoder_factory.h"
-#include "api/call/call_factory_interface.h"
#include "api/create_peerconnection_factory.h"
#include "api/data_channel_interface.h"
+#include "api/enable_media_with_defaults.h"
#include "api/jsep.h"
#include "api/media_stream_interface.h"
#include "api/media_types.h"
@@ -53,7 +53,6 @@
#include "media/base/media_engine.h"
#include "media/base/stream_params.h"
#include "media/engine/webrtc_media_engine.h"
-#include "media/engine/webrtc_media_engine_defaults.h"
#include "media/sctp/sctp_transport_internal.h"
#include "modules/audio_device/include/audio_device.h"
#include "modules/audio_processing/include/audio_processing.h"
@@ -475,8 +474,7 @@ bool GetFirstSsrc(const cricket::ContentInfo* content_info, int* ssrc) {
// Get the ufrags out of an SDP blob. Useful for testing ICE restart
// behavior.
-std::vector<std::string> GetUfrags(
- const webrtc::SessionDescriptionInterface* desc) {
+std::vector<std::string> GetUfrags(const SessionDescriptionInterface* desc) {
std::vector<std::string> ufrags;
for (const cricket::TransportInfo& info :
desc->description()->transport_infos()) {
@@ -545,21 +543,19 @@ rtc::scoped_refptr<StreamCollection> CreateStreamCollection(
StreamCollection::Create());
for (int i = 0; i < number_of_streams; ++i) {
- rtc::scoped_refptr<webrtc::MediaStreamInterface> stream(
- webrtc::MediaStream::Create(kStreams[i]));
+ rtc::scoped_refptr<MediaStreamInterface> stream(
+ MediaStream::Create(kStreams[i]));
for (int j = 0; j < tracks_per_stream; ++j) {
// Add a local audio track.
- rtc::scoped_refptr<webrtc::AudioTrackInterface> audio_track(
- webrtc::AudioTrack::Create(kAudioTracks[i * tracks_per_stream + j],
- nullptr));
+ rtc::scoped_refptr<AudioTrackInterface> audio_track(
+ AudioTrack::Create(kAudioTracks[i * tracks_per_stream + j], nullptr));
stream->AddTrack(audio_track);
// Add a local video track.
- rtc::scoped_refptr<webrtc::VideoTrackInterface> video_track(
- webrtc::VideoTrack::Create(kVideoTracks[i * tracks_per_stream + j],
- webrtc::FakeVideoTrackSource::Create(),
- rtc::Thread::Current()));
+ rtc::scoped_refptr<VideoTrackInterface> video_track(VideoTrack::Create(
+ kVideoTracks[i * tracks_per_stream + j],
+ FakeVideoTrackSource::Create(), rtc::Thread::Current()));
stream->AddTrack(video_track);
}
@@ -579,10 +575,10 @@ bool CompareStreamCollections(StreamCollectionInterface* s1,
if (s1->at(i)->id() != s2->at(i)->id()) {
return false;
}
- webrtc::AudioTrackVector audio_tracks1 = s1->at(i)->GetAudioTracks();
- webrtc::AudioTrackVector audio_tracks2 = s2->at(i)->GetAudioTracks();
- webrtc::VideoTrackVector video_tracks1 = s1->at(i)->GetVideoTracks();
- webrtc::VideoTrackVector video_tracks2 = s2->at(i)->GetVideoTracks();
+ AudioTrackVector audio_tracks1 = s1->at(i)->GetAudioTracks();
+ AudioTrackVector audio_tracks2 = s2->at(i)->GetAudioTracks();
+ VideoTrackVector video_tracks1 = s1->at(i)->GetVideoTracks();
+ VideoTrackVector video_tracks2 = s2->at(i)->GetVideoTracks();
if (audio_tracks1.size() != audio_tracks2.size()) {
return false;
@@ -631,7 +627,7 @@ class MockTrackObserver : public ObserverInterface {
// constraints are propagated into the PeerConnection's MediaConfig. These
// settings are intended for MediaChannel constructors, but that is not
// exercised by these unittest.
-class PeerConnectionFactoryForTest : public webrtc::PeerConnectionFactory {
+class PeerConnectionFactoryForTest : public PeerConnectionFactory {
public:
static rtc::scoped_refptr<PeerConnectionFactoryForTest>
CreatePeerConnectionFactoryForTest() {
@@ -641,16 +637,10 @@ class PeerConnectionFactoryForTest : public webrtc::PeerConnectionFactory {
dependencies.signaling_thread = rtc::Thread::Current();
dependencies.task_queue_factory = CreateDefaultTaskQueueFactory();
dependencies.trials = std::make_unique<FieldTrialBasedConfig>();
- cricket::MediaEngineDependencies media_deps;
- media_deps.task_queue_factory = dependencies.task_queue_factory.get();
// Use fake audio device module since we're only testing the interface
// level, and using a real one could make tests flaky when run in parallel.
- media_deps.adm = FakeAudioCaptureModule::Create();
- SetMediaEngineDefaults(&media_deps);
- media_deps.trials = dependencies.trials.get();
- dependencies.media_engine =
- cricket::CreateMediaEngine(std::move(media_deps));
- dependencies.call_factory = webrtc::CreateCallFactory();
+ dependencies.adm = FakeAudioCaptureModule::Create();
+ EnableMediaWithDefaults(dependencies);
dependencies.event_log_factory = std::make_unique<RtcEventLogFactory>(
dependencies.task_queue_factory.get());
@@ -672,7 +662,7 @@ class PeerConnectionInterfaceBaseTest : public ::testing::Test {
main_(vss_.get()),
sdp_semantics_(sdp_semantics) {
#ifdef WEBRTC_ANDROID
- webrtc::InitializeAndroidObjects();
+ InitializeAndroidObjects();
#endif
}
@@ -680,22 +670,16 @@ class PeerConnectionInterfaceBaseTest : public ::testing::Test {
// Use fake audio capture module since we're only testing the interface
// level, and using a real one could make tests flaky when run in parallel.
fake_audio_capture_module_ = FakeAudioCaptureModule::Create();
- pc_factory_ = webrtc::CreatePeerConnectionFactory(
+ pc_factory_ = CreatePeerConnectionFactory(
rtc::Thread::Current(), rtc::Thread::Current(), rtc::Thread::Current(),
- rtc::scoped_refptr<webrtc::AudioDeviceModule>(
- fake_audio_capture_module_),
- webrtc::CreateBuiltinAudioEncoderFactory(),
- webrtc::CreateBuiltinAudioDecoderFactory(),
- std::make_unique<webrtc::VideoEncoderFactoryTemplate<
- webrtc::LibvpxVp8EncoderTemplateAdapter,
- webrtc::LibvpxVp9EncoderTemplateAdapter,
- webrtc::OpenH264EncoderTemplateAdapter,
- webrtc::LibaomAv1EncoderTemplateAdapter>>(),
- std::make_unique<webrtc::VideoDecoderFactoryTemplate<
- webrtc::LibvpxVp8DecoderTemplateAdapter,
- webrtc::LibvpxVp9DecoderTemplateAdapter,
- webrtc::OpenH264DecoderTemplateAdapter,
- webrtc::Dav1dDecoderTemplateAdapter>>(),
+ rtc::scoped_refptr<AudioDeviceModule>(fake_audio_capture_module_),
+ CreateBuiltinAudioEncoderFactory(), CreateBuiltinAudioDecoderFactory(),
+ std::make_unique<VideoEncoderFactoryTemplate<
+ LibvpxVp8EncoderTemplateAdapter, LibvpxVp9EncoderTemplateAdapter,
+ OpenH264EncoderTemplateAdapter, LibaomAv1EncoderTemplateAdapter>>(),
+ std::make_unique<VideoDecoderFactoryTemplate<
+ LibvpxVp8DecoderTemplateAdapter, LibvpxVp9DecoderTemplateAdapter,
+ OpenH264DecoderTemplateAdapter, Dav1dDecoderTemplateAdapter>>(),
nullptr /* audio_mixer */, nullptr /* audio_processing */);
ASSERT_TRUE(pc_factory_);
}
@@ -953,8 +937,7 @@ class PeerConnectionInterfaceBaseTest : public ::testing::Test {
// Call the standards-compliant GetStats function.
bool DoGetRTCStats() {
- auto callback =
- rtc::make_ref_counted<webrtc::MockRTCStatsCollectorCallback>();
+ auto callback = rtc::make_ref_counted<MockRTCStatsCollectorCallback>();
pc_->GetStats(callback.get());
EXPECT_TRUE_WAIT(callback->called(), kTimeout);
return callback->called();
@@ -994,14 +977,14 @@ class PeerConnectionInterfaceBaseTest : public ::testing::Test {
std::string sdp;
EXPECT_TRUE(offer->ToString(&sdp));
std::unique_ptr<SessionDescriptionInterface> remote_offer(
- webrtc::CreateSessionDescription(SdpType::kOffer, sdp));
+ CreateSessionDescription(SdpType::kOffer, sdp));
EXPECT_TRUE(DoSetRemoteDescription(std::move(remote_offer)));
EXPECT_EQ(PeerConnectionInterface::kHaveRemoteOffer, observer_.state_);
}
void CreateAndSetRemoteOffer(const std::string& sdp) {
std::unique_ptr<SessionDescriptionInterface> remote_offer(
- webrtc::CreateSessionDescription(SdpType::kOffer, sdp));
+ CreateSessionDescription(SdpType::kOffer, sdp));
EXPECT_TRUE(DoSetRemoteDescription(std::move(remote_offer)));
EXPECT_EQ(PeerConnectionInterface::kHaveRemoteOffer, observer_.state_);
}
@@ -1020,7 +1003,7 @@ class PeerConnectionInterfaceBaseTest : public ::testing::Test {
std::string sdp;
EXPECT_TRUE(answer->ToString(&sdp));
std::unique_ptr<SessionDescriptionInterface> new_answer(
- webrtc::CreateSessionDescription(SdpType::kAnswer, sdp));
+ CreateSessionDescription(SdpType::kAnswer, sdp));
EXPECT_TRUE(DoSetLocalDescription(std::move(new_answer)));
EXPECT_EQ(PeerConnectionInterface::kStable, observer_.state_);
}
@@ -1032,7 +1015,7 @@ class PeerConnectionInterfaceBaseTest : public ::testing::Test {
std::string sdp;
EXPECT_TRUE(answer->ToString(&sdp));
std::unique_ptr<SessionDescriptionInterface> pr_answer(
- webrtc::CreateSessionDescription(SdpType::kPrAnswer, sdp));
+ CreateSessionDescription(SdpType::kPrAnswer, sdp));
EXPECT_TRUE(DoSetLocalDescription(std::move(pr_answer)));
EXPECT_EQ(PeerConnectionInterface::kHaveLocalPrAnswer, observer_.state_);
}
@@ -1057,7 +1040,7 @@ class PeerConnectionInterfaceBaseTest : public ::testing::Test {
std::string sdp;
EXPECT_TRUE(offer->ToString(&sdp));
std::unique_ptr<SessionDescriptionInterface> new_offer(
- webrtc::CreateSessionDescription(SdpType::kOffer, sdp));
+ CreateSessionDescription(SdpType::kOffer, sdp));
EXPECT_TRUE(DoSetLocalDescription(std::move(new_offer)));
EXPECT_EQ(PeerConnectionInterface::kHaveLocalOffer, observer_.state_);
@@ -1067,7 +1050,7 @@ class PeerConnectionInterfaceBaseTest : public ::testing::Test {
void CreateAnswerAsRemoteDescription(const std::string& sdp) {
std::unique_ptr<SessionDescriptionInterface> answer(
- webrtc::CreateSessionDescription(SdpType::kAnswer, sdp));
+ CreateSessionDescription(SdpType::kAnswer, sdp));
ASSERT_TRUE(answer);
EXPECT_TRUE(DoSetRemoteDescription(std::move(answer)));
EXPECT_EQ(PeerConnectionInterface::kStable, observer_.state_);
@@ -1075,12 +1058,12 @@ class PeerConnectionInterfaceBaseTest : public ::testing::Test {
void CreatePrAnswerAndAnswerAsRemoteDescription(const std::string& sdp) {
std::unique_ptr<SessionDescriptionInterface> pr_answer(
- webrtc::CreateSessionDescription(SdpType::kPrAnswer, sdp));
+ CreateSessionDescription(SdpType::kPrAnswer, sdp));
ASSERT_TRUE(pr_answer);
EXPECT_TRUE(DoSetRemoteDescription(std::move(pr_answer)));
EXPECT_EQ(PeerConnectionInterface::kHaveRemotePrAnswer, observer_.state_);
std::unique_ptr<SessionDescriptionInterface> answer(
- webrtc::CreateSessionDescription(SdpType::kAnswer, sdp));
+ CreateSessionDescription(SdpType::kAnswer, sdp));
ASSERT_TRUE(answer);
EXPECT_TRUE(DoSetRemoteDescription(std::move(answer)));
EXPECT_EQ(PeerConnectionInterface::kStable, observer_.state_);
@@ -1124,8 +1107,8 @@ class PeerConnectionInterfaceBaseTest : public ::testing::Test {
std::string mediastream_id = kStreams[0];
- rtc::scoped_refptr<webrtc::MediaStreamInterface> stream(
- webrtc::MediaStream::Create(mediastream_id));
+ rtc::scoped_refptr<MediaStreamInterface> stream(
+ MediaStream::Create(mediastream_id));
reference_collection_->AddStream(stream);
if (number_of_audio_tracks > 0) {
@@ -1149,22 +1132,20 @@ class PeerConnectionInterfaceBaseTest : public ::testing::Test {
}
return std::unique_ptr<SessionDescriptionInterface>(
- webrtc::CreateSessionDescription(SdpType::kOffer, sdp_ms1));
+ CreateSessionDescription(SdpType::kOffer, sdp_ms1));
}
void AddAudioTrack(const std::string& track_id,
MediaStreamInterface* stream) {
- rtc::scoped_refptr<webrtc::AudioTrackInterface> audio_track(
- webrtc::AudioTrack::Create(track_id, nullptr));
+ rtc::scoped_refptr<AudioTrackInterface> audio_track(
+ AudioTrack::Create(track_id, nullptr));
ASSERT_TRUE(stream->AddTrack(audio_track));
}
void AddVideoTrack(const std::string& track_id,
MediaStreamInterface* stream) {
- rtc::scoped_refptr<webrtc::VideoTrackInterface> video_track(
- webrtc::VideoTrack::Create(track_id,
- webrtc::FakeVideoTrackSource::Create(),
- rtc::Thread::Current()));
+ rtc::scoped_refptr<VideoTrackInterface> video_track(VideoTrack::Create(
+ track_id, FakeVideoTrackSource::Create(), rtc::Thread::Current()));
ASSERT_TRUE(stream->AddTrack(video_track));
}
@@ -1224,7 +1205,7 @@ class PeerConnectionInterfaceBaseTest : public ::testing::Test {
std::string sdp;
EXPECT_TRUE((*desc)->ToString(&sdp));
std::unique_ptr<SessionDescriptionInterface> remote_offer(
- webrtc::CreateSessionDescription(SdpType::kOffer, sdp));
+ CreateSessionDescription(SdpType::kOffer, sdp));
EXPECT_TRUE(DoSetRemoteDescription(std::move(remote_offer)));
EXPECT_EQ(PeerConnectionInterface::kHaveRemoteOffer, observer_.state_);
}
@@ -1237,7 +1218,7 @@ class PeerConnectionInterfaceBaseTest : public ::testing::Test {
std::string sdp;
EXPECT_TRUE((*desc)->ToString(&sdp));
std::unique_ptr<SessionDescriptionInterface> new_offer(
- webrtc::CreateSessionDescription(SdpType::kOffer, sdp));
+ CreateSessionDescription(SdpType::kOffer, sdp));
EXPECT_TRUE(DoSetLocalDescription(std::move(new_offer)));
EXPECT_EQ(PeerConnectionInterface::kHaveLocalOffer, observer_.state_);
@@ -1246,8 +1227,7 @@ class PeerConnectionInterfaceBaseTest : public ::testing::Test {
bool HasCNCodecs(const cricket::ContentInfo* content) {
RTC_DCHECK(content);
RTC_DCHECK(content->media_description());
- for (const cricket::AudioCodec& codec :
- content->media_description()->as_audio()->codecs()) {
+ for (const cricket::Codec& codec : content->media_description()->codecs()) {
if (codec.name == "CN") {
return true;
}
@@ -1273,13 +1253,13 @@ class PeerConnectionInterfaceBaseTest : public ::testing::Test {
rtc::SocketServer* socket_server() const { return vss_.get(); }
- webrtc::test::ScopedKeyValueConfig field_trials_;
+ test::ScopedKeyValueConfig field_trials_;
std::unique_ptr<rtc::VirtualSocketServer> vss_;
rtc::AutoSocketServerThread main_;
rtc::scoped_refptr<FakeAudioCaptureModule> fake_audio_capture_module_;
cricket::FakePortAllocator* port_allocator_ = nullptr;
FakeRTCCertificateGenerator* fake_certificate_generator_ = nullptr;
- rtc::scoped_refptr<webrtc::PeerConnectionFactoryInterface> pc_factory_;
+ rtc::scoped_refptr<PeerConnectionFactoryInterface> pc_factory_;
rtc::scoped_refptr<PeerConnectionInterface> pc_;
MockPeerConnectionObserver observer_;
rtc::scoped_refptr<StreamCollection> reference_collection_;
@@ -1399,22 +1379,19 @@ TEST_P(PeerConnectionInterfaceTest,
config.prune_turn_ports = true;
// Create the PC factory and PC with the above config.
- rtc::scoped_refptr<webrtc::PeerConnectionFactoryInterface> pc_factory(
- webrtc::CreatePeerConnectionFactory(
+ rtc::scoped_refptr<PeerConnectionFactoryInterface> pc_factory(
+ CreatePeerConnectionFactory(
rtc::Thread::Current(), rtc::Thread::Current(),
rtc::Thread::Current(), fake_audio_capture_module_,
- webrtc::CreateBuiltinAudioEncoderFactory(),
- webrtc::CreateBuiltinAudioDecoderFactory(),
- std::make_unique<webrtc::VideoEncoderFactoryTemplate<
- webrtc::LibvpxVp8EncoderTemplateAdapter,
- webrtc::LibvpxVp9EncoderTemplateAdapter,
- webrtc::OpenH264EncoderTemplateAdapter,
- webrtc::LibaomAv1EncoderTemplateAdapter>>(),
- std::make_unique<webrtc::VideoDecoderFactoryTemplate<
- webrtc::LibvpxVp8DecoderTemplateAdapter,
- webrtc::LibvpxVp9DecoderTemplateAdapter,
- webrtc::OpenH264DecoderTemplateAdapter,
- webrtc::Dav1dDecoderTemplateAdapter>>(),
+ CreateBuiltinAudioEncoderFactory(),
+ CreateBuiltinAudioDecoderFactory(),
+ std::make_unique<VideoEncoderFactoryTemplate<
+ LibvpxVp8EncoderTemplateAdapter, LibvpxVp9EncoderTemplateAdapter,
+ OpenH264EncoderTemplateAdapter,
+ LibaomAv1EncoderTemplateAdapter>>(),
+ std::make_unique<VideoDecoderFactoryTemplate<
+ LibvpxVp8DecoderTemplateAdapter, LibvpxVp9DecoderTemplateAdapter,
+ OpenH264DecoderTemplateAdapter, Dav1dDecoderTemplateAdapter>>(),
nullptr /* audio_mixer */, nullptr /* audio_processing */));
PeerConnectionDependencies pc_dependencies(&observer_);
pc_dependencies.allocator = std::move(port_allocator);
@@ -1431,7 +1408,7 @@ TEST_P(PeerConnectionInterfaceTest,
EXPECT_TRUE(raw_port_allocator->flags() & cricket::PORTALLOCATOR_DISABLE_TCP);
EXPECT_TRUE(raw_port_allocator->flags() &
cricket::PORTALLOCATOR_DISABLE_COSTLY_NETWORKS);
- EXPECT_EQ(webrtc::PRUNE_BASED_ON_PRIORITY,
+ EXPECT_EQ(PRUNE_BASED_ON_PRIORITY,
raw_port_allocator->turn_port_prune_policy());
}
@@ -1453,8 +1430,7 @@ TEST_P(PeerConnectionInterfaceTest, GetConfigurationAfterCreatePeerConnection) {
TEST_P(PeerConnectionInterfaceTest, GetConfigurationAfterSetConfiguration) {
PeerConnectionInterface::RTCConfiguration starting_config;
starting_config.sdp_semantics = sdp_semantics_;
- starting_config.bundle_policy =
- webrtc::PeerConnection::kBundlePolicyMaxBundle;
+ starting_config.bundle_policy = PeerConnection::kBundlePolicyMaxBundle;
CreatePeerConnection(starting_config);
PeerConnectionInterface::RTCConfiguration config = pc_->GetConfiguration();
@@ -1985,7 +1961,7 @@ TEST_P(PeerConnectionInterfaceTest, CreateSctpDataChannel) {
RTCConfiguration rtc_config;
CreatePeerConnection(rtc_config);
- webrtc::DataChannelInit config;
+ DataChannelInit config;
auto channel = pc_->CreateDataChannelOrError("1", &config);
EXPECT_TRUE(channel.ok());
EXPECT_TRUE(channel.value()->reliable());
@@ -2017,7 +1993,7 @@ TEST_P(PeerConnectionInterfaceTest, CreateSctpDataChannelWhenClosed) {
RTCConfiguration rtc_config;
CreatePeerConnection(rtc_config);
pc_->Close();
- webrtc::DataChannelInit config;
+ DataChannelInit config;
auto ret = pc_->CreateDataChannelOrError("1", &config);
ASSERT_FALSE(ret.ok());
EXPECT_EQ(ret.error().type(), RTCErrorType::INVALID_STATE);
@@ -2029,7 +2005,7 @@ TEST_P(PeerConnectionInterfaceTest, CreateSctpDataChannelWithMinusOne) {
RTCConfiguration rtc_config;
CreatePeerConnection(rtc_config);
- webrtc::DataChannelInit config;
+ DataChannelInit config;
config.maxRetransmitTime = -1;
config.maxRetransmits = -1;
auto channel = pc_->CreateDataChannelOrError("1", &config);
@@ -2044,7 +2020,7 @@ TEST_P(PeerConnectionInterfaceTest,
CreatePeerConnection(rtc_config);
std::string label = "test";
- webrtc::DataChannelInit config;
+ DataChannelInit config;
config.maxRetransmits = 0;
config.maxRetransmitTime = 0;
@@ -2059,7 +2035,7 @@ TEST_P(PeerConnectionInterfaceTest,
RTCConfiguration rtc_config;
CreatePeerConnection(rtc_config);
- webrtc::DataChannelInit config;
+ DataChannelInit config;
config.id = 1;
config.negotiated = true;
@@ -2113,7 +2089,7 @@ TEST_P(PeerConnectionInterfaceTest, DISABLED_TestRejectSctpDataChannelInAnswer)
std::string sdp;
EXPECT_TRUE(pc_->local_description()->ToString(&sdp));
std::unique_ptr<SessionDescriptionInterface> answer(
- webrtc::CreateSessionDescription(SdpType::kAnswer, sdp));
+ CreateSessionDescription(SdpType::kAnswer, sdp));
ASSERT_TRUE(answer);
cricket::ContentInfo* data_info =
cricket::GetFirstDataContent(answer->description());
@@ -2132,8 +2108,7 @@ TEST_P(PeerConnectionInterfaceTest, ReceiveFireFoxOffer) {
AddAudioTrack("audio_label");
AddVideoTrack("video_label");
std::unique_ptr<SessionDescriptionInterface> desc(
- webrtc::CreateSessionDescription(SdpType::kOffer,
- webrtc::kFireFoxSdpOffer, nullptr));
+ CreateSessionDescription(SdpType::kOffer, kFireFoxSdpOffer, nullptr));
EXPECT_TRUE(DoSetSessionDescription(std::move(desc), false));
CreateAnswerAsLocalDescription();
ASSERT_TRUE(pc_->local_description() != nullptr);
@@ -2170,8 +2145,7 @@ TEST_P(PeerConnectionInterfaceTest, DtlsSdesFallbackNotSupported) {
EXPECT_EQ_WAIT(1, fake_certificate_generator_->generated_certificates(),
kTimeout);
std::unique_ptr<SessionDescriptionInterface> desc(
- webrtc::CreateSessionDescription(SdpType::kOffer, kDtlsSdesFallbackSdp,
- nullptr));
+ CreateSessionDescription(SdpType::kOffer, kDtlsSdesFallbackSdp, nullptr));
EXPECT_FALSE(DoSetSessionDescription(std::move(desc), /*local=*/false));
}
@@ -2184,18 +2158,17 @@ TEST_P(PeerConnectionInterfaceTest, ReceiveUpdatedAudioOfferWithBadCodecs) {
CreateOfferAsLocalDescription();
const char* answer_sdp = (sdp_semantics_ == SdpSemantics::kPlanB_DEPRECATED
- ? webrtc::kAudioSdpPlanB
- : webrtc::kAudioSdpUnifiedPlan);
+ ? kAudioSdpPlanB
+ : kAudioSdpUnifiedPlan);
std::unique_ptr<SessionDescriptionInterface> answer(
- webrtc::CreateSessionDescription(SdpType::kAnswer, answer_sdp, nullptr));
+ CreateSessionDescription(SdpType::kAnswer, answer_sdp, nullptr));
EXPECT_TRUE(DoSetSessionDescription(std::move(answer), false));
- const char* reoffer_sdp =
- (sdp_semantics_ == SdpSemantics::kPlanB_DEPRECATED
- ? webrtc::kAudioSdpWithUnsupportedCodecsPlanB
- : webrtc::kAudioSdpWithUnsupportedCodecsUnifiedPlan);
+ const char* reoffer_sdp = (sdp_semantics_ == SdpSemantics::kPlanB_DEPRECATED
+ ? kAudioSdpWithUnsupportedCodecsPlanB
+ : kAudioSdpWithUnsupportedCodecsUnifiedPlan);
std::unique_ptr<SessionDescriptionInterface> updated_offer(
- webrtc::CreateSessionDescription(SdpType::kOffer, reoffer_sdp, nullptr));
+ CreateSessionDescription(SdpType::kOffer, reoffer_sdp, nullptr));
EXPECT_TRUE(DoSetSessionDescription(std::move(updated_offer), false));
CreateAnswerAsLocalDescription();
}
@@ -2282,12 +2255,11 @@ TEST_P(PeerConnectionInterfaceTest, SetConfigurationChangesPruneTurnPortsFlag) {
config.prune_turn_ports = false;
CreatePeerConnection(config);
config = pc_->GetConfiguration();
- EXPECT_EQ(webrtc::NO_PRUNE, port_allocator_->turn_port_prune_policy());
+ EXPECT_EQ(NO_PRUNE, port_allocator_->turn_port_prune_policy());
config.prune_turn_ports = true;
EXPECT_TRUE(pc_->SetConfiguration(config).ok());
- EXPECT_EQ(webrtc::PRUNE_BASED_ON_PRIORITY,
- port_allocator_->turn_port_prune_policy());
+ EXPECT_EQ(PRUNE_BASED_ON_PRIORITY, port_allocator_->turn_port_prune_policy());
}
// Test that the ice check interval can be changed. This does not verify that
@@ -2556,12 +2528,12 @@ TEST_F(PeerConnectionInterfaceTestPlanB, CloseAndTestMethods) {
std::string sdp;
ASSERT_TRUE(pc_->remote_description()->ToString(&sdp));
std::unique_ptr<SessionDescriptionInterface> remote_offer(
- webrtc::CreateSessionDescription(SdpType::kOffer, sdp));
+ CreateSessionDescription(SdpType::kOffer, sdp));
EXPECT_FALSE(DoSetRemoteDescription(std::move(remote_offer)));
ASSERT_TRUE(pc_->local_description()->ToString(&sdp));
std::unique_ptr<SessionDescriptionInterface> local_offer(
- webrtc::CreateSessionDescription(SdpType::kOffer, sdp));
+ CreateSessionDescription(SdpType::kOffer, sdp));
EXPECT_FALSE(DoSetLocalDescription(std::move(local_offer)));
}
@@ -2621,10 +2593,10 @@ TEST_F(PeerConnectionInterfaceTestPlanB,
reference_collection_.get()));
rtc::scoped_refptr<AudioTrackInterface> audio_track2 =
observer_.remote_streams()->at(0)->GetAudioTracks()[1];
- EXPECT_EQ(webrtc::MediaStreamTrackInterface::kLive, audio_track2->state());
+ EXPECT_EQ(MediaStreamTrackInterface::kLive, audio_track2->state());
rtc::scoped_refptr<VideoTrackInterface> video_track2 =
observer_.remote_streams()->at(0)->GetVideoTracks()[1];
- EXPECT_EQ(webrtc::MediaStreamTrackInterface::kLive, video_track2->state());
+ EXPECT_EQ(MediaStreamTrackInterface::kLive, video_track2->state());
// Remove the extra audio and video tracks.
std::unique_ptr<SessionDescriptionInterface> desc_ms2 =
@@ -2638,10 +2610,10 @@ TEST_F(PeerConnectionInterfaceTestPlanB,
EXPECT_TRUE(CompareStreamCollections(observer_.remote_streams(),
reference_collection_.get()));
// Track state may be updated asynchronously.
- EXPECT_EQ_WAIT(webrtc::MediaStreamTrackInterface::kEnded,
- audio_track2->state(), kTimeout);
- EXPECT_EQ_WAIT(webrtc::MediaStreamTrackInterface::kEnded,
- video_track2->state(), kTimeout);
+ EXPECT_EQ_WAIT(MediaStreamTrackInterface::kEnded, audio_track2->state(),
+ kTimeout);
+ EXPECT_EQ_WAIT(MediaStreamTrackInterface::kEnded, video_track2->state(),
+ kTimeout);
}
// This tests that remote tracks are ended if a local session description is set
@@ -2659,7 +2631,7 @@ TEST_P(PeerConnectionInterfaceTest, RejectMediaContent) {
rtc::scoped_refptr<MediaStreamTrackInterface> remote_audio =
audio_receiver->track();
- EXPECT_EQ(webrtc::MediaStreamTrackInterface::kLive, remote_audio->state());
+ EXPECT_EQ(MediaStreamTrackInterface::kLive, remote_audio->state());
rtc::scoped_refptr<MediaStreamTrackInterface> remote_video =
video_receiver->track();
EXPECT_EQ(MediaStreamTrackInterface::kLive, remote_video->state());
@@ -2703,8 +2675,8 @@ TEST_F(PeerConnectionInterfaceTestPlanB, RemoveTrackThenRejectMediaContent) {
remote_stream->RemoveTrack(remote_stream->GetAudioTracks()[0]);
std::unique_ptr<SessionDescriptionInterface> local_answer(
- webrtc::CreateSessionDescription(SdpType::kAnswer,
- GetSdpStringWithStream1(), nullptr));
+ CreateSessionDescription(SdpType::kAnswer, GetSdpStringWithStream1(),
+ nullptr));
cricket::ContentInfo* video_info =
local_answer->description()->GetContentByName("video");
video_info->rejected = true;
@@ -2993,9 +2965,9 @@ TEST_P(PeerConnectionInterfaceTest,
ASSERT_TRUE(DoCreateOffer(&offer, nullptr));
// Grab a copy of the offer before it gets passed into the PC.
std::unique_ptr<SessionDescriptionInterface> modified_offer =
- webrtc::CreateSessionDescription(
- webrtc::SdpType::kOffer, offer->session_id(),
- offer->session_version(), offer->description()->Clone());
+ CreateSessionDescription(SdpType::kOffer, offer->session_id(),
+ offer->session_version(),
+ offer->description()->Clone());
EXPECT_TRUE(DoSetLocalDescription(std::move(offer)));
auto senders = pc_->GetSenders();
@@ -3051,8 +3023,8 @@ TEST_F(PeerConnectionInterfaceTestPlanB,
EXPECT_TRUE(ContainsSender(senders, kVideoTracks[0], kStreams[0]));
// Add a new MediaStream but with the same tracks as in the first stream.
- rtc::scoped_refptr<webrtc::MediaStreamInterface> stream_1(
- webrtc::MediaStream::Create(kStreams[1]));
+ rtc::scoped_refptr<MediaStreamInterface> stream_1(
+ MediaStream::Create(kStreams[1]));
stream_1->AddTrack(stream_collection->at(0)->GetVideoTracks()[0]);
stream_1->AddTrack(stream_collection->at(0)->GetAudioTracks()[0]);
pc_->AddStream(stream_1.get());
@@ -3173,9 +3145,9 @@ TEST_P(PeerConnectionInterfaceTest, SetConfigurationCausingPartialIceRestart) {
EXPECT_TRUE(pc_->SetConfiguration(config).ok());
// Do ICE restart for the first m= section, initiated by remote peer.
- std::unique_ptr<webrtc::SessionDescriptionInterface> remote_offer(
- webrtc::CreateSessionDescription(SdpType::kOffer,
- GetSdpStringWithStream1(), nullptr));
+ std::unique_ptr<SessionDescriptionInterface> remote_offer(
+ CreateSessionDescription(SdpType::kOffer, GetSdpStringWithStream1(),
+ nullptr));
ASSERT_TRUE(remote_offer);
remote_offer->description()->transport_infos()[0].description.ice_ufrag =
"modified";
@@ -3221,7 +3193,7 @@ TEST_P(PeerConnectionInterfaceTest, CurrentAndPendingDescriptions) {
// Set remote pranswer.
std::unique_ptr<SessionDescriptionInterface> remote_pranswer(
- webrtc::CreateSessionDescription(SdpType::kPrAnswer, sdp));
+ CreateSessionDescription(SdpType::kPrAnswer, sdp));
SessionDescriptionInterface* remote_pranswer_ptr = remote_pranswer.get();
EXPECT_TRUE(DoSetRemoteDescription(std::move(remote_pranswer)));
EXPECT_EQ(local_offer_ptr, pc_->pending_local_description());
@@ -3231,7 +3203,7 @@ TEST_P(PeerConnectionInterfaceTest, CurrentAndPendingDescriptions) {
// Set remote answer.
std::unique_ptr<SessionDescriptionInterface> remote_answer(
- webrtc::CreateSessionDescription(SdpType::kAnswer, sdp));
+ CreateSessionDescription(SdpType::kAnswer, sdp));
SessionDescriptionInterface* remote_answer_ptr = remote_answer.get();
EXPECT_TRUE(DoSetRemoteDescription(std::move(remote_answer)));
EXPECT_EQ(nullptr, pc_->pending_local_description());
@@ -3241,7 +3213,7 @@ TEST_P(PeerConnectionInterfaceTest, CurrentAndPendingDescriptions) {
// Set remote offer.
std::unique_ptr<SessionDescriptionInterface> remote_offer(
- webrtc::CreateSessionDescription(SdpType::kOffer, sdp));
+ CreateSessionDescription(SdpType::kOffer, sdp));
SessionDescriptionInterface* remote_offer_ptr = remote_offer.get();
EXPECT_TRUE(DoSetRemoteDescription(std::move(remote_offer)));
EXPECT_EQ(remote_offer_ptr, pc_->pending_remote_description());
@@ -3251,7 +3223,7 @@ TEST_P(PeerConnectionInterfaceTest, CurrentAndPendingDescriptions) {
// Set local pranswer.
std::unique_ptr<SessionDescriptionInterface> local_pranswer(
- webrtc::CreateSessionDescription(SdpType::kPrAnswer, sdp));
+ CreateSessionDescription(SdpType::kPrAnswer, sdp));
SessionDescriptionInterface* local_pranswer_ptr = local_pranswer.get();
EXPECT_TRUE(DoSetLocalDescription(std::move(local_pranswer)));
EXPECT_EQ(remote_offer_ptr, pc_->pending_remote_description());
@@ -3261,7 +3233,7 @@ TEST_P(PeerConnectionInterfaceTest, CurrentAndPendingDescriptions) {
// Set local answer.
std::unique_ptr<SessionDescriptionInterface> local_answer(
- webrtc::CreateSessionDescription(SdpType::kAnswer, sdp));
+ CreateSessionDescription(SdpType::kAnswer, sdp));
SessionDescriptionInterface* local_answer_ptr = local_answer.get();
EXPECT_TRUE(DoSetLocalDescription(std::move(local_answer)));
EXPECT_EQ(nullptr, pc_->pending_remote_description());
@@ -3280,9 +3252,8 @@ TEST_P(PeerConnectionInterfaceTest,
// The RtcEventLog will be reset when the PeerConnection is closed.
pc_->Close();
- EXPECT_FALSE(
- pc_->StartRtcEventLog(std::make_unique<webrtc::RtcEventLogOutputNull>(),
- webrtc::RtcEventLog::kImmediateOutput));
+ EXPECT_FALSE(pc_->StartRtcEventLog(std::make_unique<RtcEventLogOutputNull>(),
+ RtcEventLog::kImmediateOutput));
pc_->StopRtcEventLog();
}
diff --git a/third_party/libwebrtc/pc/peer_connection_jsep_unittest.cc b/third_party/libwebrtc/pc/peer_connection_jsep_unittest.cc
index 1369253ad6..786b519a93 100644
--- a/third_party/libwebrtc/pc/peer_connection_jsep_unittest.cc
+++ b/third_party/libwebrtc/pc/peer_connection_jsep_unittest.cc
@@ -20,7 +20,7 @@
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
-#include "api/call/call_factory_interface.h"
+#include "api/enable_media_with_defaults.h"
#include "api/field_trials_view.h"
#include "api/jsep.h"
#include "api/media_stream_interface.h"
@@ -40,7 +40,6 @@
#include "media/base/media_engine.h"
#include "media/base/stream_params.h"
#include "media/engine/webrtc_media_engine.h"
-#include "media/engine/webrtc_media_engine_defaults.h"
#include "modules/audio_device/include/audio_device.h"
#include "p2p/base/p2p_constants.h"
#include "p2p/base/port_allocator.h"
@@ -84,13 +83,8 @@ PeerConnectionFactoryDependencies CreatePeerConnectionFactoryDependencies() {
dependencies.signaling_thread = rtc::Thread::Current();
dependencies.task_queue_factory = CreateDefaultTaskQueueFactory();
dependencies.trials = std::make_unique<FieldTrialBasedConfig>();
- cricket::MediaEngineDependencies media_deps;
- media_deps.task_queue_factory = dependencies.task_queue_factory.get();
- media_deps.adm = FakeAudioCaptureModule::Create();
- media_deps.trials = dependencies.trials.get();
- SetMediaEngineDefaults(&media_deps);
- dependencies.media_engine = cricket::CreateMediaEngine(std::move(media_deps));
- dependencies.call_factory = CreateCallFactory();
+ dependencies.adm = FakeAudioCaptureModule::Create();
+ EnableMediaWithDefaults(dependencies);
dependencies.sctp_factory = std::make_unique<FakeSctpTransportFactory>();
return dependencies;
}
diff --git a/third_party/libwebrtc/pc/peer_connection_media_unittest.cc b/third_party/libwebrtc/pc/peer_connection_media_unittest.cc
index 485541981e..387094cc4f 100644
--- a/third_party/libwebrtc/pc/peer_connection_media_unittest.cc
+++ b/third_party/libwebrtc/pc/peer_connection_media_unittest.cc
@@ -27,7 +27,6 @@
#include "absl/algorithm/container.h"
#include "absl/types/optional.h"
#include "api/audio_options.h"
-#include "api/call/call_factory_interface.h"
#include "api/jsep.h"
#include "api/media_types.h"
#include "api/peer_connection_interface.h"
@@ -57,6 +56,7 @@
#include "pc/rtp_media_utils.h"
#include "pc/rtp_transceiver.h"
#include "pc/session_description.h"
+#include "pc/test/enable_fake_media.h"
#include "pc/test/mock_peer_connection_observers.h"
#include "rtc_base/checks.h"
#include "rtc_base/rtc_certificate_generator.h"
@@ -82,9 +82,9 @@ using ::testing::NotNull;
using ::testing::Values;
cricket::MediaSendChannelInterface* SendChannelInternal(
- rtc::scoped_refptr<webrtc::RtpTransceiverInterface> transceiver) {
- auto transceiver_with_internal = static_cast<rtc::RefCountedObject<
- webrtc::RtpTransceiverProxyWithInternal<webrtc::RtpTransceiver>>*>(
+ rtc::scoped_refptr<RtpTransceiverInterface> transceiver) {
+ auto transceiver_with_internal = static_cast<
+ rtc::RefCountedObject<RtpTransceiverProxyWithInternal<RtpTransceiver>>*>(
transceiver.get());
auto transceiver_internal =
static_cast<RtpTransceiver*>(transceiver_with_internal->internal());
@@ -92,9 +92,9 @@ cricket::MediaSendChannelInterface* SendChannelInternal(
}
cricket::MediaReceiveChannelInterface* ReceiveChannelInternal(
- rtc::scoped_refptr<webrtc::RtpTransceiverInterface> transceiver) {
- auto transceiver_with_internal = static_cast<rtc::RefCountedObject<
- webrtc::RtpTransceiverProxyWithInternal<webrtc::RtpTransceiver>>*>(
+ rtc::scoped_refptr<RtpTransceiverInterface> transceiver) {
+ auto transceiver_with_internal = static_cast<
+ rtc::RefCountedObject<RtpTransceiverProxyWithInternal<RtpTransceiver>>*>(
transceiver.get());
auto transceiver_internal =
static_cast<RtpTransceiver*>(transceiver_with_internal->internal());
@@ -102,22 +102,22 @@ cricket::MediaReceiveChannelInterface* ReceiveChannelInternal(
}
cricket::FakeVideoMediaSendChannel* VideoMediaSendChannel(
- rtc::scoped_refptr<webrtc::RtpTransceiverInterface> transceiver) {
+ rtc::scoped_refptr<RtpTransceiverInterface> transceiver) {
return static_cast<cricket::FakeVideoMediaSendChannel*>(
SendChannelInternal(transceiver));
}
cricket::FakeVideoMediaReceiveChannel* VideoMediaReceiveChannel(
- rtc::scoped_refptr<webrtc::RtpTransceiverInterface> transceiver) {
+ rtc::scoped_refptr<RtpTransceiverInterface> transceiver) {
return static_cast<cricket::FakeVideoMediaReceiveChannel*>(
ReceiveChannelInternal(transceiver));
}
cricket::FakeVoiceMediaSendChannel* VoiceMediaSendChannel(
- rtc::scoped_refptr<webrtc::RtpTransceiverInterface> transceiver) {
+ rtc::scoped_refptr<RtpTransceiverInterface> transceiver) {
return static_cast<cricket::FakeVoiceMediaSendChannel*>(
SendChannelInternal(transceiver));
}
cricket::FakeVoiceMediaReceiveChannel* VoiceMediaReceiveChannel(
- rtc::scoped_refptr<webrtc::RtpTransceiverInterface> transceiver) {
+ rtc::scoped_refptr<RtpTransceiverInterface> transceiver) {
return static_cast<cricket::FakeVoiceMediaReceiveChannel*>(
ReceiveChannelInternal(transceiver));
}
@@ -173,8 +173,7 @@ class PeerConnectionMediaBaseTest : public ::testing::Test {
factory_dependencies.worker_thread = rtc::Thread::Current();
factory_dependencies.signaling_thread = rtc::Thread::Current();
factory_dependencies.task_queue_factory = CreateDefaultTaskQueueFactory();
- factory_dependencies.media_engine = std::move(media_engine);
- factory_dependencies.call_factory = CreateCallFactory();
+ EnableFakeMedia(factory_dependencies, std::move(media_engine));
factory_dependencies.event_log_factory =
std::make_unique<RtcEventLogFactory>(
factory_dependencies.task_queue_factory.get());
@@ -255,7 +254,7 @@ class PeerConnectionMediaBaseTest : public ::testing::Test {
return sdp_semantics_ == SdpSemantics::kUnifiedPlan;
}
- webrtc::test::ScopedKeyValueConfig field_trials_;
+ test::ScopedKeyValueConfig field_trials_;
std::unique_ptr<rtc::VirtualSocketServer> vss_;
rtc::AutoSocketServerThread main_;
const SdpSemantics sdp_semantics_;
@@ -1496,10 +1495,10 @@ TEST_P(PeerConnectionMediaTest, RedFmtpPayloadDifferentRedundancy) {
}
template <typename C>
-bool CompareCodecs(const std::vector<webrtc::RtpCodecCapability>& capabilities,
+bool CompareCodecs(const std::vector<RtpCodecCapability>& capabilities,
const std::vector<C>& codecs) {
bool capability_has_rtx =
- absl::c_any_of(capabilities, [](const webrtc::RtpCodecCapability& codec) {
+ absl::c_any_of(capabilities, [](const RtpCodecCapability& codec) {
return codec.name == cricket::kRtxCodecName;
});
bool codecs_has_rtx = absl::c_any_of(codecs, [](const C& codec) {
@@ -1511,16 +1510,16 @@ bool CompareCodecs(const std::vector<webrtc::RtpCodecCapability>& capabilities,
codecs, std::back_inserter(codecs_no_rtx),
[](const C& codec) { return codec.name != cricket::kRtxCodecName; });
- std::vector<webrtc::RtpCodecCapability> capabilities_no_rtx;
+ std::vector<RtpCodecCapability> capabilities_no_rtx;
absl::c_copy_if(capabilities, std::back_inserter(capabilities_no_rtx),
- [](const webrtc::RtpCodecCapability& codec) {
+ [](const RtpCodecCapability& codec) {
return codec.name != cricket::kRtxCodecName;
});
return capability_has_rtx == codecs_has_rtx &&
absl::c_equal(
capabilities_no_rtx, codecs_no_rtx,
- [](const webrtc::RtpCodecCapability& capability, const C& codec) {
+ [](const RtpCodecCapability& capability, const C& codec) {
return codec.MatchesRtpCodec(capability);
});
}
@@ -1539,9 +1538,9 @@ TEST_F(PeerConnectionMediaTestUnifiedPlan,
auto capabilities = caller->pc_factory()->GetRtpSenderCapabilities(
cricket::MediaType::MEDIA_TYPE_AUDIO);
- std::vector<webrtc::RtpCodecCapability> codecs;
+ std::vector<RtpCodecCapability> codecs;
absl::c_copy_if(capabilities.codecs, std::back_inserter(codecs),
- [](const webrtc::RtpCodecCapability& codec) {
+ [](const RtpCodecCapability& codec) {
return codec.name.find("_only_") != std::string::npos;
});
@@ -1562,9 +1561,9 @@ TEST_F(PeerConnectionMediaTestUnifiedPlan,
auto capabilities = caller->pc_factory()->GetRtpReceiverCapabilities(
cricket::MediaType::MEDIA_TYPE_AUDIO);
- std::vector<webrtc::RtpCodecCapability> codecs;
+ std::vector<RtpCodecCapability> codecs;
absl::c_copy_if(capabilities.codecs, std::back_inserter(codecs),
- [](const webrtc::RtpCodecCapability& codec) {
+ [](const RtpCodecCapability& codec) {
return codec.name.find("_only_") != std::string::npos;
});
@@ -1612,7 +1611,7 @@ TEST_F(PeerConnectionMediaTestUnifiedPlan,
auto codecs_only_rtx_red_fec = codecs;
auto it = std::remove_if(codecs_only_rtx_red_fec.begin(),
codecs_only_rtx_red_fec.end(),
- [](const webrtc::RtpCodecCapability& codec) {
+ [](const RtpCodecCapability& codec) {
return !(codec.name == cricket::kRtxCodecName ||
codec.name == cricket::kRedCodecName ||
codec.name == cricket::kUlpfecCodecName);
@@ -1639,7 +1638,6 @@ TEST_F(PeerConnectionMediaTestUnifiedPlan, SetCodecPreferencesAllAudioCodecs) {
auto codecs = offer->description()
->contents()[0]
.media_description()
- ->as_audio()
->codecs();
EXPECT_TRUE(CompareCodecs(sender_audio_codecs, codecs));
}
@@ -1652,7 +1650,7 @@ TEST_F(PeerConnectionMediaTestUnifiedPlan,
caller->pc_factory()
->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_AUDIO)
.codecs;
- std::vector<webrtc::RtpCodecCapability> empty_codecs = {};
+ std::vector<RtpCodecCapability> empty_codecs = {};
auto audio_transceiver = caller->pc()->GetTransceivers().front();
@@ -1662,7 +1660,6 @@ TEST_F(PeerConnectionMediaTestUnifiedPlan,
auto codecs = offer->description()
->contents()[0]
.media_description()
- ->as_audio()
->codecs();
EXPECT_TRUE(CompareCodecs(sender_audio_codecs, codecs));
}
@@ -1707,7 +1704,7 @@ TEST_F(PeerConnectionMediaTestUnifiedPlan,
auto codecs_only_rtx_red_fec = codecs;
auto it = std::remove_if(codecs_only_rtx_red_fec.begin(),
codecs_only_rtx_red_fec.end(),
- [](const webrtc::RtpCodecCapability& codec) {
+ [](const RtpCodecCapability& codec) {
return !(codec.name == cricket::kRtxCodecName ||
codec.name == cricket::kRedCodecName ||
codec.name == cricket::kUlpfecCodecName);
@@ -1734,7 +1731,6 @@ TEST_F(PeerConnectionMediaTestUnifiedPlan, SetCodecPreferencesAllVideoCodecs) {
auto codecs = offer->description()
->contents()[0]
.media_description()
- ->as_video()
->codecs();
EXPECT_TRUE(CompareCodecs(sender_video_codecs, codecs));
}
@@ -1748,7 +1744,7 @@ TEST_F(PeerConnectionMediaTestUnifiedPlan,
->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_VIDEO)
.codecs;
- std::vector<webrtc::RtpCodecCapability> empty_codecs = {};
+ std::vector<RtpCodecCapability> empty_codecs = {};
auto video_transceiver = caller->pc()->GetTransceivers().front();
@@ -1758,7 +1754,6 @@ TEST_F(PeerConnectionMediaTestUnifiedPlan,
auto codecs = offer->description()
->contents()[0]
.media_description()
- ->as_video()
->codecs();
EXPECT_TRUE(CompareCodecs(sender_video_codecs, codecs));
}
@@ -1787,7 +1782,6 @@ TEST_F(PeerConnectionMediaTestUnifiedPlan,
auto codecs = offer->description()
->contents()[0]
.media_description()
- ->as_video()
->codecs();
EXPECT_TRUE(CompareCodecs(single_codec, codecs));
}
@@ -1818,7 +1812,7 @@ TEST_F(PeerConnectionMediaTestUnifiedPlan, SetCodecPreferencesVideoWithRtx) {
auto video_codecs_vpx_rtx = sender_video_codecs;
auto it =
std::remove_if(video_codecs_vpx_rtx.begin(), video_codecs_vpx_rtx.end(),
- [](const webrtc::RtpCodecCapability& codec) {
+ [](const RtpCodecCapability& codec) {
return codec.name != cricket::kRtxCodecName &&
codec.name != cricket::kVp8CodecName &&
codec.name != cricket::kVp9CodecName;
@@ -1832,7 +1826,6 @@ TEST_F(PeerConnectionMediaTestUnifiedPlan, SetCodecPreferencesVideoWithRtx) {
auto codecs = offer->description()
->contents()[0]
.media_description()
- ->as_video()
->codecs();
EXPECT_TRUE(CompareCodecs(video_codecs_vpx_rtx, codecs));
@@ -1867,7 +1860,7 @@ TEST_F(PeerConnectionMediaTestUnifiedPlan,
auto video_codecs_vpx = video_codecs;
auto it = std::remove_if(video_codecs_vpx.begin(), video_codecs_vpx.end(),
- [](const webrtc::RtpCodecCapability& codec) {
+ [](const RtpCodecCapability& codec) {
return codec.name != cricket::kVp8CodecName &&
codec.name != cricket::kVp9CodecName;
});
@@ -1879,7 +1872,6 @@ TEST_F(PeerConnectionMediaTestUnifiedPlan,
auto codecs = offer->description()
->contents()[0]
.media_description()
- ->as_video()
->codecs();
EXPECT_EQ(codecs.size(), 2u); // VP8, VP9
@@ -1890,7 +1882,7 @@ TEST_F(PeerConnectionMediaTestUnifiedPlan,
auto recv_transceiver = callee->pc()->GetTransceivers().front();
auto video_codecs_vp8_rtx = video_codecs;
it = std::remove_if(video_codecs_vp8_rtx.begin(), video_codecs_vp8_rtx.end(),
- [](const webrtc::RtpCodecCapability& codec) {
+ [](const RtpCodecCapability& codec) {
bool r = codec.name != cricket::kVp8CodecName &&
codec.name != cricket::kRtxCodecName;
return r;
@@ -1904,7 +1896,6 @@ TEST_F(PeerConnectionMediaTestUnifiedPlan,
auto recv_codecs = answer->description()
->contents()[0]
.media_description()
- ->as_video()
->codecs();
EXPECT_EQ(recv_codecs.size(), 1u); // VP8
}
@@ -1937,7 +1928,7 @@ TEST_F(PeerConnectionMediaTestUnifiedPlan,
auto video_codecs_vpx = video_codecs;
auto it = std::remove_if(video_codecs_vpx.begin(), video_codecs_vpx.end(),
- [](const webrtc::RtpCodecCapability& codec) {
+ [](const RtpCodecCapability& codec) {
return codec.name != cricket::kVp8CodecName &&
codec.name != cricket::kVp9CodecName;
});
@@ -1952,7 +1943,6 @@ TEST_F(PeerConnectionMediaTestUnifiedPlan,
auto codecs = offer->description()
->contents()[0]
.media_description()
- ->as_video()
->codecs();
EXPECT_EQ(codecs.size(), 2u); // VP9, VP8
EXPECT_TRUE(CompareCodecs(video_codecs_vpx, codecs));
@@ -1967,7 +1957,6 @@ TEST_F(PeerConnectionMediaTestUnifiedPlan,
auto recv_codecs = answer->description()
->contents()[0]
.media_description()
- ->as_video()
->codecs();
EXPECT_TRUE(CompareCodecs(video_codecs_vpx_reverse, recv_codecs));
diff --git a/third_party/libwebrtc/pc/peer_connection_rampup_tests.cc b/third_party/libwebrtc/pc/peer_connection_rampup_tests.cc
index 545a1d53d0..0fd3c27f7d 100644
--- a/third_party/libwebrtc/pc/peer_connection_rampup_tests.cc
+++ b/third_party/libwebrtc/pc/peer_connection_rampup_tests.cc
@@ -201,7 +201,7 @@ class PeerConnectionRampUpTest : public ::testing::Test {
fake_network_managers_.emplace_back(fake_network_manager);
auto observer = std::make_unique<MockPeerConnectionObserver>();
- webrtc::PeerConnectionDependencies dependencies(observer.get());
+ PeerConnectionDependencies dependencies(observer.get());
cricket::BasicPortAllocator* port_allocator =
new cricket::BasicPortAllocator(fake_network_manager,
firewall_socket_factory_.get());
diff --git a/third_party/libwebrtc/pc/peer_connection_rtp_unittest.cc b/third_party/libwebrtc/pc/peer_connection_rtp_unittest.cc
index b93e5923bb..1a97a4bb44 100644
--- a/third_party/libwebrtc/pc/peer_connection_rtp_unittest.cc
+++ b/third_party/libwebrtc/pc/peer_connection_rtp_unittest.cc
@@ -75,13 +75,13 @@ using ::testing::UnorderedElementsAre;
using ::testing::Values;
template <typename MethodFunctor>
-class OnSuccessObserver : public webrtc::SetRemoteDescriptionObserverInterface {
+class OnSuccessObserver : public SetRemoteDescriptionObserverInterface {
public:
explicit OnSuccessObserver(MethodFunctor on_success)
: on_success_(std::move(on_success)) {}
- // webrtc::SetRemoteDescriptionObserverInterface implementation.
- void OnSetRemoteDescriptionComplete(webrtc::RTCError error) override {
+ // SetRemoteDescriptionObserverInterface implementation.
+ void OnSetRemoteDescriptionComplete(RTCError error) override {
RTC_CHECK(error.ok());
on_success_();
}
@@ -113,7 +113,7 @@ class PeerConnectionRtpBaseTest : public ::testing::Test {
Dav1dDecoderTemplateAdapter>>(),
nullptr /* audio_mixer */,
nullptr /* audio_processing */)) {
- webrtc::metrics::Reset();
+ metrics::Reset();
}
std::unique_ptr<PeerConnectionWrapper> CreatePeerConnection() {
@@ -201,7 +201,7 @@ class PeerConnectionRtpTestUnifiedPlan : public PeerConnectionRtpBaseTest {
}
};
-// These tests cover `webrtc::PeerConnectionObserver` callbacks firing upon
+// These tests cover `PeerConnectionObserver` callbacks firing upon
// setting the remote description.
TEST_P(PeerConnectionRtpTest, AddTrackWithoutStreamFiresOnAddTrack) {
@@ -934,8 +934,8 @@ TEST_P(PeerConnectionRtpTest,
auto caller = CreatePeerConnection();
auto callee = CreatePeerConnection();
- rtc::scoped_refptr<webrtc::MockSetSessionDescriptionObserver> observer =
- rtc::make_ref_counted<webrtc::MockSetSessionDescriptionObserver>();
+ rtc::scoped_refptr<MockSetSessionDescriptionObserver> observer =
+ rtc::make_ref_counted<MockSetSessionDescriptionObserver>();
auto offer = caller->CreateOfferAndSetAsLocal();
callee->pc()->SetRemoteDescription(observer.get(), offer.release());
diff --git a/third_party/libwebrtc/pc/peer_connection_signaling_unittest.cc b/third_party/libwebrtc/pc/peer_connection_signaling_unittest.cc
index 8ca59fc20c..190fb38b43 100644
--- a/third_party/libwebrtc/pc/peer_connection_signaling_unittest.cc
+++ b/third_party/libwebrtc/pc/peer_connection_signaling_unittest.cc
@@ -896,8 +896,8 @@ TEST_P(PeerConnectionSignalingTest, UnsupportedContentType) {
"m=bogus 9 FOO 0 8\r\n"
"c=IN IP4 0.0.0.0\r\n"
"a=mid:bogusmid\r\n";
- std::unique_ptr<webrtc::SessionDescriptionInterface> remote_description =
- webrtc::CreateSessionDescription(SdpType::kOffer, sdp, nullptr);
+ std::unique_ptr<SessionDescriptionInterface> remote_description =
+ CreateSessionDescription(SdpType::kOffer, sdp, nullptr);
EXPECT_TRUE(caller->SetRemoteDescription(std::move(remote_description)));
@@ -977,20 +977,17 @@ TEST_P(PeerConnectionSignalingTest, ReceiveFlexFec) {
"a=ssrc-group:FEC-FR 1224551896 1953032773\r\n"
"a=ssrc:1224551896 cname:/exJcmhSLpyu9FgV\r\n"
"a=ssrc:1953032773 cname:/exJcmhSLpyu9FgV\r\n";
- std::unique_ptr<webrtc::SessionDescriptionInterface> remote_description =
- webrtc::CreateSessionDescription(SdpType::kOffer, sdp, nullptr);
+ std::unique_ptr<SessionDescriptionInterface> remote_description =
+ CreateSessionDescription(SdpType::kOffer, sdp, nullptr);
EXPECT_TRUE(caller->SetRemoteDescription(std::move(remote_description)));
auto answer = caller->CreateAnswer();
ASSERT_EQ(answer->description()->contents().size(), 1u);
- ASSERT_NE(
- answer->description()->contents()[0].media_description()->as_video(),
- nullptr);
+ ASSERT_NE(answer->description()->contents()[0].media_description(), nullptr);
auto codecs = answer->description()
->contents()[0]
.media_description()
- ->as_video()
->codecs();
ASSERT_EQ(codecs.size(), 2u);
EXPECT_EQ(codecs[1].name, "flexfec-03");
@@ -1033,20 +1030,17 @@ TEST_P(PeerConnectionSignalingTest, ReceiveFlexFecReoffer) {
"a=ssrc-group:FEC-FR 1224551896 1953032773\r\n"
"a=ssrc:1224551896 cname:/exJcmhSLpyu9FgV\r\n"
"a=ssrc:1953032773 cname:/exJcmhSLpyu9FgV\r\n";
- std::unique_ptr<webrtc::SessionDescriptionInterface> remote_description =
- webrtc::CreateSessionDescription(SdpType::kOffer, sdp, nullptr);
+ std::unique_ptr<SessionDescriptionInterface> remote_description =
+ CreateSessionDescription(SdpType::kOffer, sdp, nullptr);
EXPECT_TRUE(caller->SetRemoteDescription(std::move(remote_description)));
auto answer = caller->CreateAnswer();
ASSERT_EQ(answer->description()->contents().size(), 1u);
- ASSERT_NE(
- answer->description()->contents()[0].media_description()->as_video(),
- nullptr);
+ ASSERT_NE(answer->description()->contents()[0].media_description(), nullptr);
auto codecs = answer->description()
->contents()[0]
.media_description()
- ->as_video()
->codecs();
ASSERT_EQ(codecs.size(), 2u);
EXPECT_EQ(codecs[1].name, "flexfec-03");
@@ -1059,7 +1053,6 @@ TEST_P(PeerConnectionSignalingTest, ReceiveFlexFecReoffer) {
auto offer_codecs = offer->description()
->contents()[0]
.media_description()
- ->as_video()
->codecs();
auto flexfec_it = std::find_if(
offer_codecs.begin(), offer_codecs.end(),
@@ -1104,8 +1097,8 @@ TEST_P(PeerConnectionSignalingTest, MidAttributeMaxLength) {
"a=rtcp-fb:102 nack\r\n"
"a=rtcp-fb:102 nack pli\r\n"
"a=ssrc:1224551896 cname:/exJcmhSLpyu9FgV\r\n";
- std::unique_ptr<webrtc::SessionDescriptionInterface> remote_description =
- webrtc::CreateSessionDescription(SdpType::kOffer, sdp, nullptr);
+ std::unique_ptr<SessionDescriptionInterface> remote_description =
+ CreateSessionDescription(SdpType::kOffer, sdp, nullptr);
EXPECT_FALSE(caller->SetRemoteDescription(std::move(remote_description)));
}
@@ -1339,8 +1332,8 @@ TEST_F(PeerConnectionSignalingUnifiedPlanTest, RtxReofferApt) {
"a=rtcp-fb:102 nack\r\n"
"a=rtcp-fb:102 nack pli\r\n"
"a=ssrc:1224551896 cname:/exJcmhSLpyu9FgV\r\n";
- std::unique_ptr<webrtc::SessionDescriptionInterface> remote_description =
- webrtc::CreateSessionDescription(SdpType::kOffer, sdp, nullptr);
+ std::unique_ptr<SessionDescriptionInterface> remote_description =
+ CreateSessionDescription(SdpType::kOffer, sdp, nullptr);
EXPECT_TRUE(callee->SetRemoteDescription(std::move(remote_description)));
@@ -1353,7 +1346,6 @@ TEST_F(PeerConnectionSignalingUnifiedPlanTest, RtxReofferApt) {
auto codecs = reoffer->description()
->contents()[0]
.media_description()
- ->as_video()
->codecs();
ASSERT_GT(codecs.size(), 2u);
EXPECT_EQ(codecs[0].name, "VP8");
diff --git a/third_party/libwebrtc/pc/peer_connection_simulcast_unittest.cc b/third_party/libwebrtc/pc/peer_connection_simulcast_unittest.cc
index 6b6a96c473..bffb5d9e9f 100644
--- a/third_party/libwebrtc/pc/peer_connection_simulcast_unittest.cc
+++ b/third_party/libwebrtc/pc/peer_connection_simulcast_unittest.cc
@@ -220,7 +220,7 @@ class PeerConnectionSimulcastMetricsTests
: public PeerConnectionSimulcastTests,
public ::testing::WithParamInterface<int> {
protected:
- PeerConnectionSimulcastMetricsTests() { webrtc::metrics::Reset(); }
+ PeerConnectionSimulcastMetricsTests() { metrics::Reset(); }
};
#endif
diff --git a/third_party/libwebrtc/pc/peer_connection_svc_integrationtest.cc b/third_party/libwebrtc/pc/peer_connection_svc_integrationtest.cc
index 672f3eef99..32ca451866 100644
--- a/third_party/libwebrtc/pc/peer_connection_svc_integrationtest.cc
+++ b/third_party/libwebrtc/pc/peer_connection_svc_integrationtest.cc
@@ -37,14 +37,13 @@ class PeerConnectionSVCIntegrationTest
: PeerConnectionIntegrationBaseTest(SdpSemantics::kUnifiedPlan) {}
RTCError SetCodecPreferences(
- rtc::scoped_refptr<webrtc::RtpTransceiverInterface> transceiver,
+ rtc::scoped_refptr<RtpTransceiverInterface> transceiver,
absl::string_view codec_name) {
- webrtc::RtpCapabilities capabilities =
+ RtpCapabilities capabilities =
caller()->pc_factory()->GetRtpSenderCapabilities(
cricket::MEDIA_TYPE_VIDEO);
std::vector<RtpCodecCapability> codecs;
- for (const webrtc::RtpCodecCapability& codec_capability :
- capabilities.codecs) {
+ for (const RtpCodecCapability& codec_capability : capabilities.codecs) {
if (codec_capability.name == codec_name)
codecs.push_back(codec_capability);
}
@@ -55,8 +54,8 @@ class PeerConnectionSVCIntegrationTest
TEST_F(PeerConnectionSVCIntegrationTest, AddTransceiverAcceptsL1T1) {
ASSERT_TRUE(CreatePeerConnectionWrappers());
ConnectFakeSignaling();
- webrtc::RtpTransceiverInit init;
- webrtc::RtpEncodingParameters encoding_parameters;
+ RtpTransceiverInit init;
+ RtpEncodingParameters encoding_parameters;
encoding_parameters.scalability_mode = "L1T1";
init.send_encodings.push_back(encoding_parameters);
auto transceiver_or_error =
@@ -67,8 +66,8 @@ TEST_F(PeerConnectionSVCIntegrationTest, AddTransceiverAcceptsL1T1) {
TEST_F(PeerConnectionSVCIntegrationTest, AddTransceiverAcceptsL3T3) {
ASSERT_TRUE(CreatePeerConnectionWrappers());
ConnectFakeSignaling();
- webrtc::RtpTransceiverInit init;
- webrtc::RtpEncodingParameters encoding_parameters;
+ RtpTransceiverInit init;
+ RtpEncodingParameters encoding_parameters;
encoding_parameters.scalability_mode = "L3T3";
init.send_encodings.push_back(encoding_parameters);
auto transceiver_or_error =
@@ -80,33 +79,32 @@ TEST_F(PeerConnectionSVCIntegrationTest,
AddTransceiverRejectsUnknownScalabilityMode) {
ASSERT_TRUE(CreatePeerConnectionWrappers());
ConnectFakeSignaling();
- webrtc::RtpTransceiverInit init;
- webrtc::RtpEncodingParameters encoding_parameters;
+ RtpTransceiverInit init;
+ RtpEncodingParameters encoding_parameters;
encoding_parameters.scalability_mode = "FOOBAR";
init.send_encodings.push_back(encoding_parameters);
auto transceiver_or_error =
caller()->pc()->AddTransceiver(caller()->CreateLocalVideoTrack(), init);
EXPECT_FALSE(transceiver_or_error.ok());
EXPECT_EQ(transceiver_or_error.error().type(),
- webrtc::RTCErrorType::UNSUPPORTED_OPERATION);
+ RTCErrorType::UNSUPPORTED_OPERATION);
}
TEST_F(PeerConnectionSVCIntegrationTest, SetParametersAcceptsL1T3WithVP8) {
ASSERT_TRUE(CreatePeerConnectionWrappers());
ConnectFakeSignaling();
- webrtc::RtpCapabilities capabilities =
+ RtpCapabilities capabilities =
caller()->pc_factory()->GetRtpSenderCapabilities(
cricket::MEDIA_TYPE_VIDEO);
std::vector<RtpCodecCapability> vp8_codec;
- for (const webrtc::RtpCodecCapability& codec_capability :
- capabilities.codecs) {
+ for (const RtpCodecCapability& codec_capability : capabilities.codecs) {
if (codec_capability.name == cricket::kVp8CodecName)
vp8_codec.push_back(codec_capability);
}
- webrtc::RtpTransceiverInit init;
- webrtc::RtpEncodingParameters encoding_parameters;
+ RtpTransceiverInit init;
+ RtpEncodingParameters encoding_parameters;
init.send_encodings.push_back(encoding_parameters);
auto transceiver_or_error =
caller()->pc()->AddTransceiver(caller()->CreateLocalVideoTrack(), init);
@@ -114,7 +112,7 @@ TEST_F(PeerConnectionSVCIntegrationTest, SetParametersAcceptsL1T3WithVP8) {
auto transceiver = transceiver_or_error.MoveValue();
EXPECT_TRUE(transceiver->SetCodecPreferences(vp8_codec).ok());
- webrtc::RtpParameters parameters = transceiver->sender()->GetParameters();
+ RtpParameters parameters = transceiver->sender()->GetParameters();
ASSERT_EQ(parameters.encodings.size(), 1u);
parameters.encodings[0].scalability_mode = "L1T3";
auto result = transceiver->sender()->SetParameters(parameters);
@@ -125,8 +123,8 @@ TEST_F(PeerConnectionSVCIntegrationTest, SetParametersRejectsL3T3WithVP8) {
ASSERT_TRUE(CreatePeerConnectionWrappers());
ConnectFakeSignaling();
- webrtc::RtpTransceiverInit init;
- webrtc::RtpEncodingParameters encoding_parameters;
+ RtpTransceiverInit init;
+ RtpEncodingParameters encoding_parameters;
init.send_encodings.push_back(encoding_parameters);
auto transceiver_or_error =
caller()->pc()->AddTransceiver(caller()->CreateLocalVideoTrack(), init);
@@ -134,12 +132,12 @@ TEST_F(PeerConnectionSVCIntegrationTest, SetParametersRejectsL3T3WithVP8) {
auto transceiver = transceiver_or_error.MoveValue();
EXPECT_TRUE(SetCodecPreferences(transceiver, cricket::kVp8CodecName).ok());
- webrtc::RtpParameters parameters = transceiver->sender()->GetParameters();
+ RtpParameters parameters = transceiver->sender()->GetParameters();
ASSERT_EQ(parameters.encodings.size(), 1u);
parameters.encodings[0].scalability_mode = "L3T3";
auto result = transceiver->sender()->SetParameters(parameters);
EXPECT_FALSE(result.ok());
- EXPECT_EQ(result.type(), webrtc::RTCErrorType::INVALID_MODIFICATION);
+ EXPECT_EQ(result.type(), RTCErrorType::INVALID_MODIFICATION);
}
TEST_F(PeerConnectionSVCIntegrationTest,
@@ -147,8 +145,8 @@ TEST_F(PeerConnectionSVCIntegrationTest,
ASSERT_TRUE(CreatePeerConnectionWrappers());
ConnectFakeSignaling();
- webrtc::RtpTransceiverInit init;
- webrtc::RtpEncodingParameters encoding_parameters;
+ RtpTransceiverInit init;
+ RtpEncodingParameters encoding_parameters;
init.send_encodings.push_back(encoding_parameters);
auto transceiver_or_error =
caller()->pc()->AddTransceiver(caller()->CreateLocalVideoTrack(), init);
@@ -159,7 +157,7 @@ TEST_F(PeerConnectionSVCIntegrationTest,
caller()->CreateAndSetAndSignalOffer();
ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
- webrtc::RtpParameters parameters = transceiver->sender()->GetParameters();
+ RtpParameters parameters = transceiver->sender()->GetParameters();
ASSERT_EQ(parameters.encodings.size(), 1u);
parameters.encodings[0].scalability_mode = "L1T3";
auto result = transceiver->sender()->SetParameters(parameters);
@@ -171,8 +169,8 @@ TEST_F(PeerConnectionSVCIntegrationTest,
ASSERT_TRUE(CreatePeerConnectionWrappers());
ConnectFakeSignaling();
- webrtc::RtpTransceiverInit init;
- webrtc::RtpEncodingParameters encoding_parameters;
+ RtpTransceiverInit init;
+ RtpEncodingParameters encoding_parameters;
init.send_encodings.push_back(encoding_parameters);
auto transceiver_or_error =
caller()->pc()->AddTransceiver(caller()->CreateLocalVideoTrack(), init);
@@ -183,7 +181,7 @@ TEST_F(PeerConnectionSVCIntegrationTest,
caller()->CreateAndSetAndSignalOffer();
ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
- webrtc::RtpParameters parameters = transceiver->sender()->GetParameters();
+ RtpParameters parameters = transceiver->sender()->GetParameters();
ASSERT_EQ(parameters.encodings.size(), 1u);
parameters.encodings[0].scalability_mode = "L3T3";
auto result = transceiver->sender()->SetParameters(parameters);
@@ -195,8 +193,8 @@ TEST_F(PeerConnectionSVCIntegrationTest,
ASSERT_TRUE(CreatePeerConnectionWrappers());
ConnectFakeSignaling();
- webrtc::RtpTransceiverInit init;
- webrtc::RtpEncodingParameters encoding_parameters;
+ RtpTransceiverInit init;
+ RtpEncodingParameters encoding_parameters;
init.send_encodings.push_back(encoding_parameters);
auto transceiver_or_error =
caller()->pc()->AddTransceiver(caller()->CreateLocalVideoTrack(), init);
@@ -207,12 +205,12 @@ TEST_F(PeerConnectionSVCIntegrationTest,
caller()->CreateAndSetAndSignalOffer();
ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
- webrtc::RtpParameters parameters = transceiver->sender()->GetParameters();
+ RtpParameters parameters = transceiver->sender()->GetParameters();
ASSERT_EQ(parameters.encodings.size(), 1u);
parameters.encodings[0].scalability_mode = "L3T3";
auto result = transceiver->sender()->SetParameters(parameters);
EXPECT_FALSE(result.ok());
- EXPECT_EQ(result.type(), webrtc::RTCErrorType::INVALID_MODIFICATION);
+ EXPECT_EQ(result.type(), RTCErrorType::INVALID_MODIFICATION);
}
TEST_F(PeerConnectionSVCIntegrationTest,
@@ -220,8 +218,8 @@ TEST_F(PeerConnectionSVCIntegrationTest,
ASSERT_TRUE(CreatePeerConnectionWrappers());
ConnectFakeSignaling();
- webrtc::RtpTransceiverInit init;
- webrtc::RtpEncodingParameters encoding_parameters;
+ RtpTransceiverInit init;
+ RtpEncodingParameters encoding_parameters;
init.send_encodings.push_back(encoding_parameters);
auto transceiver_or_error =
caller()->pc()->AddTransceiver(caller()->CreateLocalVideoTrack(), init);
@@ -232,27 +230,27 @@ TEST_F(PeerConnectionSVCIntegrationTest,
caller()->CreateAndSetAndSignalOffer();
ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
- webrtc::RtpParameters parameters = transceiver->sender()->GetParameters();
+ RtpParameters parameters = transceiver->sender()->GetParameters();
ASSERT_EQ(parameters.encodings.size(), 1u);
parameters.encodings[0].scalability_mode = "FOOBAR";
auto result = transceiver->sender()->SetParameters(parameters);
EXPECT_FALSE(result.ok());
- EXPECT_EQ(result.type(), webrtc::RTCErrorType::INVALID_MODIFICATION);
+ EXPECT_EQ(result.type(), RTCErrorType::INVALID_MODIFICATION);
}
TEST_F(PeerConnectionSVCIntegrationTest, FallbackToL1Tx) {
ASSERT_TRUE(CreatePeerConnectionWrappers());
ConnectFakeSignaling();
- webrtc::RtpTransceiverInit init;
- webrtc::RtpEncodingParameters encoding_parameters;
+ RtpTransceiverInit init;
+ RtpEncodingParameters encoding_parameters;
init.send_encodings.push_back(encoding_parameters);
auto transceiver_or_error =
caller()->pc()->AddTransceiver(caller()->CreateLocalVideoTrack(), init);
ASSERT_TRUE(transceiver_or_error.ok());
auto caller_transceiver = transceiver_or_error.MoveValue();
- webrtc::RtpCapabilities capabilities =
+ RtpCapabilities capabilities =
caller()->pc_factory()->GetRtpSenderCapabilities(
cricket::MEDIA_TYPE_VIDEO);
std::vector<RtpCodecCapability> send_codecs = capabilities.codecs;
@@ -267,8 +265,7 @@ TEST_F(PeerConnectionSVCIntegrationTest, FallbackToL1Tx) {
caller_transceiver->SetCodecPreferences(send_codecs);
// L3T3 should be supported by VP9
- webrtc::RtpParameters parameters =
- caller_transceiver->sender()->GetParameters();
+ RtpParameters parameters = caller_transceiver->sender()->GetParameters();
ASSERT_EQ(parameters.encodings.size(), 1u);
parameters.encodings[0].scalability_mode = "L3T3";
auto result = caller_transceiver->sender()->SetParameters(parameters);
diff --git a/third_party/libwebrtc/pc/peer_connection_wrapper.cc b/third_party/libwebrtc/pc/peer_connection_wrapper.cc
index 44f4256b10..557d0c8422 100644
--- a/third_party/libwebrtc/pc/peer_connection_wrapper.cc
+++ b/third_party/libwebrtc/pc/peer_connection_wrapper.cc
@@ -339,8 +339,7 @@ bool PeerConnectionWrapper::IsIceConnected() {
return observer()->ice_connected_;
}
-rtc::scoped_refptr<const webrtc::RTCStatsReport>
-PeerConnectionWrapper::GetStats() {
+rtc::scoped_refptr<const RTCStatsReport> PeerConnectionWrapper::GetStats() {
auto callback = rtc::make_ref_counted<MockRTCStatsCollectorCallback>();
pc()->GetStats(callback.get());
EXPECT_TRUE_WAIT(callback->called(), kDefaultTimeout);
diff --git a/third_party/libwebrtc/pc/rtc_stats_collector.cc b/third_party/libwebrtc/pc/rtc_stats_collector.cc
index 0797ba2a76..2bac176aac 100644
--- a/third_party/libwebrtc/pc/rtc_stats_collector.cc
+++ b/third_party/libwebrtc/pc/rtc_stats_collector.cc
@@ -336,7 +336,7 @@ const char* QualityLimitationReasonToRTCQualityLimitationReason(
std::map<std::string, double>
QualityLimitationDurationToRTCQualityLimitationDuration(
- std::map<webrtc::QualityLimitationReason, int64_t> durations_ms) {
+ std::map<QualityLimitationReason, int64_t> durations_ms) {
std::map<std::string, double> result;
// The internal duration is defined in milliseconds while the spec defines
// the value in seconds:
@@ -513,7 +513,7 @@ std::unique_ptr<RTCInboundRtpStreamStats> CreateInboundAudioStreamStats(
std::unique_ptr<RTCAudioPlayoutStats> CreateAudioPlayoutStats(
const AudioDeviceModule::Stats& audio_device_stats,
- webrtc::Timestamp timestamp) {
+ Timestamp timestamp) {
auto stats = std::make_unique<RTCAudioPlayoutStats>(
/*id=*/kAudioPlayoutSingletonId, timestamp);
stats->synthesized_samples_duration =
diff --git a/third_party/libwebrtc/pc/rtc_stats_collector.h b/third_party/libwebrtc/pc/rtc_stats_collector.h
index e94d23944c..4c68e77086 100644
--- a/third_party/libwebrtc/pc/rtc_stats_collector.h
+++ b/third_party/libwebrtc/pc/rtc_stats_collector.h
@@ -317,7 +317,7 @@ class RTCStatsCollector : public rtc::RefCountInterface {
uint32_t data_channels_closed;
// Identifies channels that have been opened, whose internal id is stored in
// the set until they have been fully closed.
- webrtc::flat_set<int> opened_data_channels;
+ flat_set<int> opened_data_channels;
};
InternalRecord internal_record_;
};
diff --git a/third_party/libwebrtc/pc/rtc_stats_collector_unittest.cc b/third_party/libwebrtc/pc/rtc_stats_collector_unittest.cc
index 37821ac829..055be6fe99 100644
--- a/third_party/libwebrtc/pc/rtc_stats_collector_unittest.cc
+++ b/third_party/libwebrtc/pc/rtc_stats_collector_unittest.cc
@@ -263,9 +263,9 @@ class FakeAudioTrackForStats : public MediaStreamTrack<AudioTrackInterface> {
std::string kind() const override {
return MediaStreamTrackInterface::kAudioKind;
}
- webrtc::AudioSourceInterface* GetSource() const override { return nullptr; }
- void AddSink(webrtc::AudioTrackSinkInterface* sink) override {}
- void RemoveSink(webrtc::AudioTrackSinkInterface* sink) override {}
+ AudioSourceInterface* GetSource() const override { return nullptr; }
+ void AddSink(AudioTrackSinkInterface* sink) override {}
+ void RemoveSink(AudioTrackSinkInterface* sink) override {}
bool GetSignalLevel(int* level) override { return false; }
rtc::scoped_refptr<AudioProcessorInterface> GetAudioProcessor() override {
return processor_;
@@ -2030,7 +2030,7 @@ TEST_F(RTCStatsCollectorTest, CollectRTCIceCandidatePairStats) {
EXPECT_TRUE(report->Get(*expected_pair.transport_id));
// Set bandwidth and "GetStats" again.
- webrtc::Call::Stats call_stats;
+ Call::Stats call_stats;
const int kSendBandwidth = 888;
call_stats.send_bandwidth_bps = kSendBandwidth;
const int kRecvBandwidth = 999;
@@ -2339,12 +2339,9 @@ TEST_F(RTCStatsCollectorTest, CollectRTCInboundRtpStreamStats_Video) {
video_media_info.receivers[0].key_frames_decoded = 3;
video_media_info.receivers[0].frames_dropped = 13;
video_media_info.receivers[0].qp_sum = absl::nullopt;
- video_media_info.receivers[0].total_decode_time =
- webrtc::TimeDelta::Seconds(9);
- video_media_info.receivers[0].total_processing_delay =
- webrtc::TimeDelta::Millis(600);
- video_media_info.receivers[0].total_assembly_time =
- webrtc::TimeDelta::Millis(500);
+ video_media_info.receivers[0].total_decode_time = TimeDelta::Seconds(9);
+ video_media_info.receivers[0].total_processing_delay = TimeDelta::Millis(600);
+ video_media_info.receivers[0].total_assembly_time = TimeDelta::Millis(500);
video_media_info.receivers[0].frames_assembled_from_multiple_packets = 23;
video_media_info.receivers[0].total_inter_frame_delay = 0.123;
video_media_info.receivers[0].total_squared_inter_frame_delay = 0.00456;
@@ -2617,12 +2614,12 @@ TEST_F(RTCStatsCollectorTest, CollectRTCOutboundRtpStreamStats_Video) {
video_media_info.senders[0].key_frames_encoded = 3;
video_media_info.senders[0].total_encode_time_ms = 9000;
video_media_info.senders[0].total_encoded_bytes_target = 1234;
- video_media_info.senders[0].total_packet_send_delay =
- webrtc::TimeDelta::Seconds(10);
+ video_media_info.senders[0].total_packet_send_delay = TimeDelta::Seconds(10);
video_media_info.senders[0].quality_limitation_reason =
QualityLimitationReason::kBandwidth;
- video_media_info.senders[0].quality_limitation_durations_ms
- [webrtc::QualityLimitationReason::kBandwidth] = 300;
+ video_media_info.senders[0]
+ .quality_limitation_durations_ms[QualityLimitationReason::kBandwidth] =
+ 300;
video_media_info.senders[0].quality_limitation_resolution_changes = 56u;
video_media_info.senders[0].qp_sum = absl::nullopt;
video_media_info.senders[0].content_type = VideoContentType::UNSPECIFIED;
diff --git a/third_party/libwebrtc/pc/rtc_stats_traversal_unittest.cc b/third_party/libwebrtc/pc/rtc_stats_traversal_unittest.cc
index 72ad255564..8205ebedc0 100644
--- a/third_party/libwebrtc/pc/rtc_stats_traversal_unittest.cc
+++ b/third_party/libwebrtc/pc/rtc_stats_traversal_unittest.cc
@@ -47,7 +47,7 @@ class RTCStatsTraversalTest : public ::testing::Test {
for (const RTCStats* start_node : start_nodes) {
start_ids.push_back(start_node->id());
}
- result_ = webrtc::TakeReferencedStats(initial_report_, start_ids);
+ result_ = ::webrtc::TakeReferencedStats(initial_report_, start_ids);
}
void EXPECT_VISITED(const RTCStats* stats) {
diff --git a/third_party/libwebrtc/pc/rtp_sender.cc b/third_party/libwebrtc/pc/rtp_sender.cc
index cdae1595b3..b0c32eff85 100644
--- a/third_party/libwebrtc/pc/rtp_sender.cc
+++ b/third_party/libwebrtc/pc/rtp_sender.cc
@@ -115,13 +115,13 @@ class SignalingThreadCallback {
if (!signaling_thread_->IsCurrent()) {
signaling_thread_->PostTask(
[callback = std::move(callback_), error]() mutable {
- webrtc::InvokeSetParametersCallback(callback, error);
+ InvokeSetParametersCallback(callback, error);
});
callback_ = nullptr;
return;
}
- webrtc::InvokeSetParametersCallback(callback_, error);
+ InvokeSetParametersCallback(callback_, error);
callback_ = nullptr;
}
@@ -243,7 +243,7 @@ void RtpSenderBase::SetParametersInternal(const RtpParameters& parameters,
"Attempted to set an unimplemented parameter of RtpParameters.");
RTC_LOG(LS_ERROR) << error.message() << " ("
<< ::webrtc::ToString(error.type()) << ")";
- webrtc::InvokeSetParametersCallback(callback, error);
+ InvokeSetParametersCallback(callback, error);
return;
}
if (!media_channel_ || !ssrc_) {
@@ -252,7 +252,7 @@ void RtpSenderBase::SetParametersInternal(const RtpParameters& parameters,
if (result.ok()) {
init_parameters_ = parameters;
}
- webrtc::InvokeSetParametersCallback(callback, result);
+ InvokeSetParametersCallback(callback, result);
return;
}
auto task = [&, callback = std::move(callback),
@@ -268,13 +268,13 @@ void RtpSenderBase::SetParametersInternal(const RtpParameters& parameters,
RTCError result = cricket::CheckRtpParametersInvalidModificationAndValues(
old_parameters, rtp_parameters);
if (!result.ok()) {
- webrtc::InvokeSetParametersCallback(callback, result);
+ InvokeSetParametersCallback(callback, result);
return;
}
result = CheckCodecParameters(rtp_parameters);
if (!result.ok()) {
- webrtc::InvokeSetParametersCallback(callback, result);
+ InvokeSetParametersCallback(callback, result);
return;
}
@@ -389,7 +389,7 @@ void RtpSenderBase::SetParametersAsync(const RtpParameters& parameters,
TRACE_EVENT0("webrtc", "RtpSenderBase::SetParametersAsync");
RTCError result = CheckSetParameters(parameters);
if (!result.ok()) {
- webrtc::InvokeSetParametersCallback(callback, result);
+ InvokeSetParametersCallback(callback, result);
return;
}
@@ -399,7 +399,7 @@ void RtpSenderBase::SetParametersAsync(const RtpParameters& parameters,
signaling_thread_,
[this, callback = std::move(callback)](RTCError error) mutable {
last_transaction_id_.reset();
- webrtc::InvokeSetParametersCallback(callback, error);
+ InvokeSetParametersCallback(callback, error);
}),
false);
}
diff --git a/third_party/libwebrtc/pc/rtp_sender.h b/third_party/libwebrtc/pc/rtp_sender.h
index d29c3760e6..8925230636 100644
--- a/third_party/libwebrtc/pc/rtp_sender.h
+++ b/third_party/libwebrtc/pc/rtp_sender.h
@@ -86,9 +86,7 @@ class RtpSenderInternal : public RtpSenderInterface {
const RtpParameters& parameters) = 0;
// Additional checks that are specific to the current codec settings
- virtual RTCError CheckCodecParameters(const RtpParameters& parameters) {
- return webrtc::RTCError::OK();
- }
+ virtual RTCError CheckCodecParameters(const RtpParameters& parameters) = 0;
// Returns an ID that changes every time SetTrack() is called, but
// otherwise remains constant. Used to generate IDs for stats.
diff --git a/third_party/libwebrtc/pc/rtp_sender_receiver_unittest.cc b/third_party/libwebrtc/pc/rtp_sender_receiver_unittest.cc
index 3092e53c2d..4387aedf53 100644
--- a/third_party/libwebrtc/pc/rtp_sender_receiver_unittest.cc
+++ b/third_party/libwebrtc/pc/rtp_sender_receiver_unittest.cc
@@ -105,7 +105,7 @@ class RtpSenderReceiverTest
: network_thread_(rtc::Thread::Current()),
worker_thread_(rtc::Thread::Current()),
video_bitrate_allocator_factory_(
- webrtc::CreateBuiltinVideoBitrateAllocatorFactory()),
+ CreateBuiltinVideoBitrateAllocatorFactory()),
// Create fake media engine/etc. so we can create channels to use to
// test RtpSenders/RtpReceivers.
media_engine_(std::make_unique<cricket::FakeMediaEngine>()),
@@ -119,16 +119,16 @@ class RtpSenderReceiverTest
// Fake media channels are owned by the media engine.
voice_media_send_channel_ = media_engine_->voice().CreateSendChannel(
&fake_call_, cricket::MediaConfig(), cricket::AudioOptions(),
- webrtc::CryptoOptions(), webrtc::AudioCodecPairId::Create());
+ CryptoOptions(), AudioCodecPairId::Create());
video_media_send_channel_ = media_engine_->video().CreateSendChannel(
&fake_call_, cricket::MediaConfig(), cricket::VideoOptions(),
- webrtc::CryptoOptions(), video_bitrate_allocator_factory_.get());
+ CryptoOptions(), video_bitrate_allocator_factory_.get());
voice_media_receive_channel_ = media_engine_->voice().CreateReceiveChannel(
&fake_call_, cricket::MediaConfig(), cricket::AudioOptions(),
- webrtc::CryptoOptions(), webrtc::AudioCodecPairId::Create());
+ CryptoOptions(), AudioCodecPairId::Create());
video_media_receive_channel_ = media_engine_->video().CreateReceiveChannel(
&fake_call_, cricket::MediaConfig(), cricket::VideoOptions(),
- webrtc::CryptoOptions());
+ CryptoOptions());
// Create streams for predefined SSRCs. Streams need to exist in order
// for the senders and receievers to apply parameters to them.
@@ -162,8 +162,8 @@ class RtpSenderReceiverTest
audio_track_ = nullptr;
}
- std::unique_ptr<webrtc::RtpTransportInternal> CreateDtlsSrtpTransport() {
- auto dtls_srtp_transport = std::make_unique<webrtc::DtlsSrtpTransport>(
+ std::unique_ptr<RtpTransportInternal> CreateDtlsSrtpTransport() {
+ auto dtls_srtp_transport = std::make_unique<DtlsSrtpTransport>(
/*rtcp_mux_required=*/true, field_trials_);
dtls_srtp_transport->SetDtlsTransports(rtp_dtls_transport_.get(),
/*rtcp_dtls_transport=*/nullptr);
@@ -515,12 +515,12 @@ class RtpSenderReceiverTest
test::RunLoop run_loop_;
rtc::Thread* const network_thread_;
rtc::Thread* const worker_thread_;
- webrtc::RtcEventLogNull event_log_;
+ RtcEventLogNull event_log_;
// The `rtp_dtls_transport_` and `rtp_transport_` should be destroyed after
// the `channel_manager`.
std::unique_ptr<cricket::DtlsTransportInternal> rtp_dtls_transport_;
- std::unique_ptr<webrtc::RtpTransportInternal> rtp_transport_;
- std::unique_ptr<webrtc::VideoBitrateAllocatorFactory>
+ std::unique_ptr<RtpTransportInternal> rtp_transport_;
+ std::unique_ptr<VideoBitrateAllocatorFactory>
video_bitrate_allocator_factory_;
std::unique_ptr<cricket::FakeMediaEngine> media_engine_;
rtc::UniqueRandomIdGenerator ssrc_generator_;
@@ -540,7 +540,7 @@ class RtpSenderReceiverTest
rtc::scoped_refptr<MediaStreamInterface> local_stream_;
rtc::scoped_refptr<VideoTrackInterface> video_track_;
rtc::scoped_refptr<AudioTrackInterface> audio_track_;
- webrtc::test::ScopedKeyValueConfig field_trials_;
+ test::ScopedKeyValueConfig field_trials_;
};
// Test that `voice_channel_` is updated when an audio track is associated
@@ -651,15 +651,13 @@ TEST_F(RtpSenderReceiverTest, LocalVideoTrackDisable) {
TEST_F(RtpSenderReceiverTest, RemoteVideoTrackState) {
CreateVideoRtpReceiver();
- EXPECT_EQ(webrtc::MediaStreamTrackInterface::kLive, video_track_->state());
- EXPECT_EQ(webrtc::MediaSourceInterface::kLive,
- video_track_->GetSource()->state());
+ EXPECT_EQ(MediaStreamTrackInterface::kLive, video_track_->state());
+ EXPECT_EQ(MediaSourceInterface::kLive, video_track_->GetSource()->state());
DestroyVideoRtpReceiver();
- EXPECT_EQ(webrtc::MediaStreamTrackInterface::kEnded, video_track_->state());
- EXPECT_EQ(webrtc::MediaSourceInterface::kEnded,
- video_track_->GetSource()->state());
+ EXPECT_EQ(MediaStreamTrackInterface::kEnded, video_track_->state());
+ EXPECT_EQ(MediaSourceInterface::kEnded, video_track_->GetSource()->state());
DestroyVideoRtpReceiver();
}
@@ -888,9 +886,9 @@ TEST_F(RtpSenderReceiverTest, AudioSenderCanSetParametersAsync) {
RtpParameters params = audio_rtp_sender_->GetParameters();
EXPECT_EQ(1u, params.encodings.size());
- absl::optional<webrtc::RTCError> result;
+ absl::optional<RTCError> result;
audio_rtp_sender_->SetParametersAsync(
- params, [&result](webrtc::RTCError error) { result = error; });
+ params, [&result](RTCError error) { result = error; });
run_loop_.Flush();
EXPECT_TRUE(result->ok());
@@ -918,13 +916,13 @@ TEST_F(RtpSenderReceiverTest,
audio_rtp_sender_ =
AudioRtpSender::Create(worker_thread_, /*id=*/"", nullptr, nullptr);
- absl::optional<webrtc::RTCError> result;
+ absl::optional<RTCError> result;
RtpParameters params = audio_rtp_sender_->GetParameters();
ASSERT_EQ(1u, params.encodings.size());
params.encodings[0].max_bitrate_bps = 90000;
audio_rtp_sender_->SetParametersAsync(
- params, [&result](webrtc::RTCError error) { result = error; });
+ params, [&result](RTCError error) { result = error; });
run_loop_.Flush();
EXPECT_TRUE(result->ok());
@@ -932,7 +930,7 @@ TEST_F(RtpSenderReceiverTest,
EXPECT_EQ(params.encodings[0].max_bitrate_bps, 90000);
audio_rtp_sender_->SetParametersAsync(
- params, [&result](webrtc::RTCError error) { result = error; });
+ params, [&result](RTCError error) { result = error; });
run_loop_.Flush();
EXPECT_TRUE(result->ok());
@@ -1016,13 +1014,13 @@ TEST_F(RtpSenderReceiverTest,
RtpParameters params = audio_rtp_sender_->GetParameters();
EXPECT_EQ(1u, params.encodings.size());
- absl::optional<webrtc::RTCError> result;
+ absl::optional<RTCError> result;
audio_rtp_sender_->SetParametersAsync(
- params, [&result](webrtc::RTCError error) { result = error; });
+ params, [&result](RTCError error) { result = error; });
run_loop_.Flush();
EXPECT_TRUE(result->ok());
audio_rtp_sender_->SetParametersAsync(
- params, [&result](webrtc::RTCError error) { result = error; });
+ params, [&result](RTCError error) { result = error; });
run_loop_.Flush();
EXPECT_EQ(RTCErrorType::INVALID_STATE, result->type());
@@ -1081,7 +1079,7 @@ TEST_F(RtpSenderReceiverTest, SetAudioMaxSendBitrate) {
CreateAudioRtpSender();
EXPECT_EQ(-1, voice_media_send_channel()->max_bps());
- webrtc::RtpParameters params = audio_rtp_sender_->GetParameters();
+ RtpParameters params = audio_rtp_sender_->GetParameters();
EXPECT_EQ(1U, params.encodings.size());
EXPECT_FALSE(params.encodings[0].max_bitrate_bps);
params.encodings[0].max_bitrate_bps = 1000;
@@ -1106,10 +1104,9 @@ TEST_F(RtpSenderReceiverTest, SetAudioMaxSendBitrate) {
TEST_F(RtpSenderReceiverTest, SetAudioBitratePriority) {
CreateAudioRtpSender();
- webrtc::RtpParameters params = audio_rtp_sender_->GetParameters();
+ RtpParameters params = audio_rtp_sender_->GetParameters();
EXPECT_EQ(1U, params.encodings.size());
- EXPECT_EQ(webrtc::kDefaultBitratePriority,
- params.encodings[0].bitrate_priority);
+ EXPECT_EQ(kDefaultBitratePriority, params.encodings[0].bitrate_priority);
double new_bitrate_priority = 2.0;
params.encodings[0].bitrate_priority = new_bitrate_priority;
EXPECT_TRUE(audio_rtp_sender_->SetParameters(params).ok());
@@ -1140,9 +1137,9 @@ TEST_F(RtpSenderReceiverTest, VideoSenderCanSetParametersAsync) {
RtpParameters params = video_rtp_sender_->GetParameters();
EXPECT_EQ(1u, params.encodings.size());
- absl::optional<webrtc::RTCError> result;
+ absl::optional<RTCError> result;
video_rtp_sender_->SetParametersAsync(
- params, [&result](webrtc::RTCError error) { result = error; });
+ params, [&result](RTCError error) { result = error; });
run_loop_.Flush();
EXPECT_TRUE(result->ok());
@@ -1170,19 +1167,19 @@ TEST_F(RtpSenderReceiverTest,
video_rtp_sender_ =
VideoRtpSender::Create(worker_thread_, /*id=*/"", nullptr);
- absl::optional<webrtc::RTCError> result;
+ absl::optional<RTCError> result;
RtpParameters params = video_rtp_sender_->GetParameters();
ASSERT_EQ(1u, params.encodings.size());
params.encodings[0].max_bitrate_bps = 90000;
video_rtp_sender_->SetParametersAsync(
- params, [&result](webrtc::RTCError error) { result = error; });
+ params, [&result](RTCError error) { result = error; });
run_loop_.Flush();
EXPECT_TRUE(result->ok());
params = video_rtp_sender_->GetParameters();
EXPECT_EQ(params.encodings[0].max_bitrate_bps, 90000);
video_rtp_sender_->SetParametersAsync(
- params, [&result](webrtc::RTCError error) { result = error; });
+ params, [&result](RTCError error) { result = error; });
run_loop_.Flush();
EXPECT_TRUE(result->ok());
@@ -1350,13 +1347,13 @@ TEST_F(RtpSenderReceiverTest,
RtpParameters params = video_rtp_sender_->GetParameters();
EXPECT_EQ(1u, params.encodings.size());
- absl::optional<webrtc::RTCError> result;
+ absl::optional<RTCError> result;
video_rtp_sender_->SetParametersAsync(
- params, [&result](webrtc::RTCError error) { result = error; });
+ params, [&result](RTCError error) { result = error; });
run_loop_.Flush();
EXPECT_TRUE(result->ok());
video_rtp_sender_->SetParametersAsync(
- params, [&result](webrtc::RTCError error) { result = error; });
+ params, [&result](RTCError error) { result = error; });
run_loop_.Flush();
EXPECT_EQ(RTCErrorType::INVALID_STATE, result->type());
@@ -1453,7 +1450,7 @@ TEST_F(RtpSenderReceiverTest, VideoSenderDetectInvalidNumTemporalLayers) {
CreateVideoRtpSender();
RtpParameters params = video_rtp_sender_->GetParameters();
- params.encodings[0].num_temporal_layers = webrtc::kMaxTemporalStreams + 1;
+ params.encodings[0].num_temporal_layers = kMaxTemporalStreams + 1;
RTCError result = video_rtp_sender_->SetParameters(params);
EXPECT_EQ(RTCErrorType::INVALID_RANGE, result.type());
@@ -1536,7 +1533,7 @@ TEST_F(RtpSenderReceiverTest, SetVideoMinMaxSendBitrate) {
CreateVideoRtpSender();
EXPECT_EQ(-1, video_media_send_channel()->max_bps());
- webrtc::RtpParameters params = video_rtp_sender_->GetParameters();
+ RtpParameters params = video_rtp_sender_->GetParameters();
EXPECT_EQ(1U, params.encodings.size());
EXPECT_FALSE(params.encodings[0].min_bitrate_bps);
EXPECT_FALSE(params.encodings[0].max_bitrate_bps);
@@ -1589,10 +1586,9 @@ TEST_F(RtpSenderReceiverTest, SetVideoMinMaxSendBitrateSimulcast) {
TEST_F(RtpSenderReceiverTest, SetVideoBitratePriority) {
CreateVideoRtpSender();
- webrtc::RtpParameters params = video_rtp_sender_->GetParameters();
+ RtpParameters params = video_rtp_sender_->GetParameters();
EXPECT_EQ(1U, params.encodings.size());
- EXPECT_EQ(webrtc::kDefaultBitratePriority,
- params.encodings[0].bitrate_priority);
+ EXPECT_EQ(kDefaultBitratePriority, params.encodings[0].bitrate_priority);
double new_bitrate_priority = 2.0;
params.encodings[0].bitrate_priority = new_bitrate_priority;
EXPECT_TRUE(video_rtp_sender_->SetParameters(params).ok());
diff --git a/third_party/libwebrtc/pc/rtp_transceiver.cc b/third_party/libwebrtc/pc/rtp_transceiver.cc
index 815ec9dece..ca626cc94b 100644
--- a/third_party/libwebrtc/pc/rtp_transceiver.cc
+++ b/third_party/libwebrtc/pc/rtp_transceiver.cc
@@ -542,7 +542,7 @@ bool RtpTransceiver::stopping() const {
RtpTransceiverDirection RtpTransceiver::direction() const {
if (unified_plan_ && stopping())
- return webrtc::RtpTransceiverDirection::kStopped;
+ return RtpTransceiverDirection::kStopped;
return direction_;
}
@@ -570,7 +570,7 @@ RTCError RtpTransceiver::SetDirectionWithError(
absl::optional<RtpTransceiverDirection> RtpTransceiver::current_direction()
const {
if (unified_plan_ && stopped())
- return webrtc::RtpTransceiverDirection::kStopped;
+ return RtpTransceiverDirection::kStopped;
return current_direction_;
}
@@ -604,7 +604,7 @@ void RtpTransceiver::StopSendingAndReceiving() {
});
stopping_ = true;
- direction_ = webrtc::RtpTransceiverDirection::kInactive;
+ direction_ = RtpTransceiverDirection::kInactive;
}
RTCError RtpTransceiver::StopStandard() {
diff --git a/third_party/libwebrtc/pc/rtp_transceiver.h b/third_party/libwebrtc/pc/rtp_transceiver.h
index deda5d7d61..88febb9429 100644
--- a/third_party/libwebrtc/pc/rtp_transceiver.h
+++ b/third_party/libwebrtc/pc/rtp_transceiver.h
@@ -358,20 +358,18 @@ PROXY_CONSTMETHOD0(rtc::scoped_refptr<RtpReceiverInterface>, receiver)
PROXY_CONSTMETHOD0(bool, stopped)
PROXY_CONSTMETHOD0(bool, stopping)
PROXY_CONSTMETHOD0(RtpTransceiverDirection, direction)
-PROXY_METHOD1(webrtc::RTCError, SetDirectionWithError, RtpTransceiverDirection)
+PROXY_METHOD1(RTCError, SetDirectionWithError, RtpTransceiverDirection)
PROXY_CONSTMETHOD0(absl::optional<RtpTransceiverDirection>, current_direction)
PROXY_CONSTMETHOD0(absl::optional<RtpTransceiverDirection>, fired_direction)
-PROXY_METHOD0(webrtc::RTCError, StopStandard)
+PROXY_METHOD0(RTCError, StopStandard)
PROXY_METHOD0(void, StopInternal)
-PROXY_METHOD1(webrtc::RTCError,
- SetCodecPreferences,
- rtc::ArrayView<RtpCodecCapability>)
+PROXY_METHOD1(RTCError, SetCodecPreferences, rtc::ArrayView<RtpCodecCapability>)
PROXY_CONSTMETHOD0(std::vector<RtpCodecCapability>, codec_preferences)
PROXY_CONSTMETHOD0(std::vector<RtpHeaderExtensionCapability>,
GetHeaderExtensionsToNegotiate)
PROXY_CONSTMETHOD0(std::vector<RtpHeaderExtensionCapability>,
GetNegotiatedHeaderExtensions)
-PROXY_METHOD1(webrtc::RTCError,
+PROXY_METHOD1(RTCError,
SetHeaderExtensionsToNegotiate,
rtc::ArrayView<const RtpHeaderExtensionCapability>)
END_PROXY_MAP(RtpTransceiver)
diff --git a/third_party/libwebrtc/pc/rtp_transceiver_unittest.cc b/third_party/libwebrtc/pc/rtp_transceiver_unittest.cc
index 8b4a2389ce..d75e964509 100644
--- a/third_party/libwebrtc/pc/rtp_transceiver_unittest.cc
+++ b/third_party/libwebrtc/pc/rtp_transceiver_unittest.cc
@@ -17,10 +17,11 @@
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
+#include "api/environment/environment_factory.h"
#include "api/peer_connection_interface.h"
#include "api/rtp_parameters.h"
-#include "media/base/fake_media_engine.h"
#include "media/base/media_engine.h"
+#include "pc/test/enable_fake_media.h"
#include "pc/test/mock_channel_interface.h"
#include "pc/test/mock_rtp_receiver_internal.h"
#include "pc/test/mock_rtp_sender_internal.h"
@@ -44,7 +45,8 @@ class RtpTransceiverTest : public testing::Test {
public:
RtpTransceiverTest()
: dependencies_(MakeDependencies()),
- context_(ConnectionContext::Create(&dependencies_)) {}
+ context_(
+ ConnectionContext::Create(CreateEnvironment(), &dependencies_)) {}
protected:
cricket::MediaEngineInterface* media_engine() {
@@ -60,7 +62,7 @@ class RtpTransceiverTest : public testing::Test {
d.network_thread = rtc::Thread::Current();
d.worker_thread = rtc::Thread::Current();
d.signaling_thread = rtc::Thread::Current();
- d.media_engine = std::make_unique<cricket::FakeMediaEngine>();
+ EnableFakeMedia(d);
return d;
}
@@ -420,8 +422,8 @@ TEST_F(RtpTransceiverTestForHeaderExtensions, ReturnsNegotiatedHdrExts) {
EXPECT_CALL(*mock_channel, mid()).WillRepeatedly(ReturnRef(content_name));
EXPECT_CALL(*mock_channel, SetRtpTransport(_)).WillRepeatedly(Return(true));
- cricket::RtpHeaderExtensions extensions = {webrtc::RtpExtension("uri1", 1),
- webrtc::RtpExtension("uri2", 2)};
+ cricket::RtpHeaderExtensions extensions = {RtpExtension("uri1", 1),
+ RtpExtension("uri2", 2)};
cricket::AudioContentDescription description;
description.set_rtp_header_extensions(extensions);
transceiver_->OnNegotiationUpdate(SdpType::kAnswer, &description);
@@ -449,8 +451,8 @@ TEST_F(RtpTransceiverTestForHeaderExtensions,
EXPECT_CALL(*sender_.get(), SetTransceiverAsStopped());
EXPECT_CALL(*sender_.get(), Stop());
- cricket::RtpHeaderExtensions extensions = {webrtc::RtpExtension("uri1", 1),
- webrtc::RtpExtension("uri2", 2)};
+ cricket::RtpHeaderExtensions extensions = {RtpExtension("uri1", 1),
+ RtpExtension("uri2", 2)};
cricket::AudioContentDescription description;
description.set_rtp_header_extensions(extensions);
transceiver_->OnNegotiationUpdate(SdpType::kAnswer, &description);
@@ -464,8 +466,7 @@ TEST_F(RtpTransceiverTestForHeaderExtensions,
RtpTransceiverDirection::kStopped),
Field(&RtpHeaderExtensionCapability::direction,
RtpTransceiverDirection::kStopped)));
- extensions = {webrtc::RtpExtension("uri3", 4),
- webrtc::RtpExtension("uri5", 6)};
+ extensions = {RtpExtension("uri3", 4), RtpExtension("uri5", 6)};
description.set_rtp_header_extensions(extensions);
transceiver_->OnNegotiationUpdate(SdpType::kAnswer, &description);
diff --git a/third_party/libwebrtc/pc/rtp_transport.cc b/third_party/libwebrtc/pc/rtp_transport.cc
index 653b51fd9e..7cf9fe0ace 100644
--- a/third_party/libwebrtc/pc/rtp_transport.cc
+++ b/third_party/libwebrtc/pc/rtp_transport.cc
@@ -180,12 +180,16 @@ bool RtpTransport::UnregisterRtpDemuxerSink(RtpPacketSinkInterface* sink) {
return true;
}
+flat_set<uint32_t> RtpTransport::GetSsrcsForSink(RtpPacketSinkInterface* sink) {
+ return rtp_demuxer_.GetSsrcsForSink(sink);
+}
+
void RtpTransport::DemuxPacket(rtc::CopyOnWriteBuffer packet,
int64_t packet_time_us) {
- webrtc::RtpPacketReceived parsed_packet(
- &header_extension_map_, packet_time_us == -1
- ? Timestamp::MinusInfinity()
- : Timestamp::Micros(packet_time_us));
+ RtpPacketReceived parsed_packet(&header_extension_map_,
+ packet_time_us == -1
+ ? Timestamp::MinusInfinity()
+ : Timestamp::Micros(packet_time_us));
if (!parsed_packet.Parse(std::move(packet))) {
RTC_LOG(LS_ERROR)
<< "Failed to parse the incoming RTP packet before demuxing. Drop it.";
@@ -226,7 +230,14 @@ void RtpTransport::OnSentPacket(rtc::PacketTransportInternal* packet_transport,
const rtc::SentPacket& sent_packet) {
RTC_DCHECK(packet_transport == rtp_packet_transport_ ||
packet_transport == rtcp_packet_transport_);
+ if (processing_sent_packet_) {
+ TaskQueueBase::Current()->PostTask(SafeTask(
+ safety_.flag(), [this, sent_packet] { SendSentPacket(sent_packet); }));
+ return;
+ }
+ processing_sent_packet_ = true;
SendSentPacket(sent_packet);
+ processing_sent_packet_ = false;
}
void RtpTransport::OnRtpPacketReceived(rtc::CopyOnWriteBuffer packet,
diff --git a/third_party/libwebrtc/pc/rtp_transport.h b/third_party/libwebrtc/pc/rtp_transport.h
index 456c91c370..6d5d4bff57 100644
--- a/third_party/libwebrtc/pc/rtp_transport.h
+++ b/third_party/libwebrtc/pc/rtp_transport.h
@@ -96,6 +96,7 @@ class RtpTransport : public RtpTransportInternal {
rtc::CopyOnWriteBuffer* packet,
const rtc::PacketOptions& options,
int flags);
+ flat_set<uint32_t> GetSsrcsForSink(RtpPacketSinkInterface* sink);
// Overridden by SrtpTransport.
virtual void OnNetworkRouteChanged(
@@ -140,6 +141,7 @@ class RtpTransport : public RtpTransportInternal {
RtpHeaderExtensionMap header_extension_map_;
// Guard against recursive "ready to send" signals
bool processing_ready_to_send_ = false;
+ bool processing_sent_packet_ = false;
ScopedTaskSafety safety_;
};
diff --git a/third_party/libwebrtc/pc/rtp_transport_internal.h b/third_party/libwebrtc/pc/rtp_transport_internal.h
index 4114fa9340..483a1cee38 100644
--- a/third_party/libwebrtc/pc/rtp_transport_internal.h
+++ b/third_party/libwebrtc/pc/rtp_transport_internal.h
@@ -72,7 +72,7 @@ class RtpTransportInternal : public sigslot::has_slots<> {
// Called whenever a RTP packet that can not be demuxed by the transport is
// received.
void SetUnDemuxableRtpPacketReceivedHandler(
- absl::AnyInvocable<void(webrtc::RtpPacketReceived&)> callback) {
+ absl::AnyInvocable<void(RtpPacketReceived&)> callback) {
callback_undemuxable_rtp_packet_received_ = std::move(callback);
}
@@ -160,7 +160,7 @@ class RtpTransportInternal : public sigslot::has_slots<> {
CallbackList<bool> callback_list_ready_to_send_;
CallbackList<rtc::CopyOnWriteBuffer*, int64_t>
callback_list_rtcp_packet_received_;
- absl::AnyInvocable<void(webrtc::RtpPacketReceived&)>
+ absl::AnyInvocable<void(RtpPacketReceived&)>
callback_undemuxable_rtp_packet_received_ =
[](RtpPacketReceived& packet) {};
CallbackList<absl::optional<rtc::NetworkRoute>>
diff --git a/third_party/libwebrtc/pc/rtp_transport_unittest.cc b/third_party/libwebrtc/pc/rtp_transport_unittest.cc
index 5b6a8309e0..6b8e616799 100644
--- a/third_party/libwebrtc/pc/rtp_transport_unittest.cc
+++ b/third_party/libwebrtc/pc/rtp_transport_unittest.cc
@@ -349,4 +349,28 @@ TEST(RtpTransportTest, RecursiveSetSendDoesNotCrash) {
EXPECT_FALSE(observer.ready_to_send());
}
+TEST(RtpTransportTest, RecursiveOnSentPacketDoesNotCrash) {
+ const int kShortTimeout = 100;
+ test::RunLoop loop;
+ RtpTransport transport(kMuxEnabled);
+ rtc::FakePacketTransport fake_rtp("fake_rtp");
+ transport.SetRtpPacketTransport(&fake_rtp);
+ fake_rtp.SetDestination(&fake_rtp, true);
+ TransportObserver observer(&transport);
+ const rtc::PacketOptions options;
+ const int flags = 0;
+
+ fake_rtp.SetWritable(true);
+ observer.SetActionOnSentPacket([&]() {
+ rtc::CopyOnWriteBuffer rtp_data(kRtpData, kRtpLen);
+ if (observer.sent_packet_count() < 2) {
+ transport.SendRtpPacket(&rtp_data, options, flags);
+ }
+ });
+ rtc::CopyOnWriteBuffer rtp_data(kRtpData, kRtpLen);
+ transport.SendRtpPacket(&rtp_data, options, flags);
+ EXPECT_EQ(observer.sent_packet_count(), 1);
+ EXPECT_EQ_WAIT(observer.sent_packet_count(), 2, kShortTimeout);
+}
+
} // namespace webrtc
diff --git a/third_party/libwebrtc/pc/sctp_transport.h b/third_party/libwebrtc/pc/sctp_transport.h
index 35e7656100..076dee5318 100644
--- a/third_party/libwebrtc/pc/sctp_transport.h
+++ b/third_party/libwebrtc/pc/sctp_transport.h
@@ -61,7 +61,7 @@ class SctpTransport : public SctpTransportInterface,
void Start(int local_port, int remote_port, int max_message_size);
// TODO(https://bugs.webrtc.org/10629): Move functions that need
- // internal() to be functions on the webrtc::SctpTransport interface,
+ // internal() to be functions on the SctpTransport interface,
// and make the internal() function private.
cricket::SctpTransportInternal* internal() {
RTC_DCHECK_RUN_ON(owner_thread_);
diff --git a/third_party/libwebrtc/pc/sctp_utils.cc b/third_party/libwebrtc/pc/sctp_utils.cc
index 54742c27a7..60ffdc7919 100644
--- a/third_party/libwebrtc/pc/sctp_utils.cc
+++ b/third_party/libwebrtc/pc/sctp_utils.cc
@@ -64,7 +64,7 @@ bool ParseDataChannelOpenMessage(const rtc::CopyOnWriteBuffer& payload,
// Format defined at
// http://tools.ietf.org/html/draft-jesup-rtcweb-data-protocol-04
- rtc::ByteBufferReader buffer(payload.data<char>(), payload.size());
+ rtc::ByteBufferReader buffer(payload);
uint8_t message_type;
if (!buffer.ReadUInt8(&message_type)) {
RTC_LOG(LS_WARNING) << "Could not read OPEN message type.";
diff --git a/third_party/libwebrtc/pc/sctp_utils_unittest.cc b/third_party/libwebrtc/pc/sctp_utils_unittest.cc
index 3e49824b45..9ef2068a05 100644
--- a/third_party/libwebrtc/pc/sctp_utils_unittest.cc
+++ b/third_party/libwebrtc/pc/sctp_utils_unittest.cc
@@ -35,7 +35,7 @@ class SctpUtilsTest : public ::testing::Test {
uint16_t label_length;
uint16_t protocol_length;
- rtc::ByteBufferReader buffer(packet.data<char>(), packet.size());
+ rtc::ByteBufferReader buffer(packet);
ASSERT_TRUE(buffer.ReadUInt8(&message_type));
EXPECT_EQ(0x03, message_type);
@@ -176,7 +176,7 @@ TEST_F(SctpUtilsTest, WriteParseAckMessage) {
webrtc::WriteDataChannelOpenAckMessage(&packet);
uint8_t message_type;
- rtc::ByteBufferReader buffer(packet.data<char>(), packet.size());
+ rtc::ByteBufferReader buffer(packet);
ASSERT_TRUE(buffer.ReadUInt8(&message_type));
EXPECT_EQ(0x02, message_type);
diff --git a/third_party/libwebrtc/pc/sdp_offer_answer.cc b/third_party/libwebrtc/pc/sdp_offer_answer.cc
index 0261195bb0..1e43833a0b 100644
--- a/third_party/libwebrtc/pc/sdp_offer_answer.cc
+++ b/third_party/libwebrtc/pc/sdp_offer_answer.cc
@@ -86,8 +86,7 @@ namespace webrtc {
namespace {
-typedef webrtc::PeerConnectionInterface::RTCOfferAnswerOptions
- RTCOfferAnswerOptions;
+typedef PeerConnectionInterface::RTCOfferAnswerOptions RTCOfferAnswerOptions;
// Error messages
const char kInvalidSdp[] = "Invalid session description.";
@@ -566,22 +565,9 @@ RTCError ValidatePayloadTypes(const cricket::SessionDescription& description) {
continue;
}
const auto type = media_description->type();
- if (type == cricket::MEDIA_TYPE_AUDIO) {
- RTC_DCHECK(media_description->as_audio());
- for (const auto& codec : media_description->as_audio()->codecs()) {
- if (!cricket::UsedPayloadTypes::IsIdValid(
- codec, media_description->rtcp_mux())) {
- LOG_AND_RETURN_ERROR(
- RTCErrorType::INVALID_PARAMETER,
- "The media section with MID='" + content.mid() +
- "' used an invalid payload type " + rtc::ToString(codec.id) +
- " for codec '" + codec.name + ", rtcp-mux:" +
- (media_description->rtcp_mux() ? "enabled" : "disabled"));
- }
- }
- } else if (type == cricket::MEDIA_TYPE_VIDEO) {
- RTC_DCHECK(media_description->as_video());
- for (const auto& codec : media_description->as_video()->codecs()) {
+ if (type == cricket::MEDIA_TYPE_AUDIO ||
+ type == cricket::MEDIA_TYPE_VIDEO) {
+ for (const auto& codec : media_description->codecs()) {
if (!cricket::UsedPayloadTypes::IsIdValid(
codec, media_description->rtcp_mux())) {
LOG_AND_RETURN_ERROR(
@@ -834,8 +820,8 @@ std::string GenerateRtcpCname() {
}
// Check if we can send `new_stream` on a PeerConnection.
-bool CanAddLocalMediaStream(webrtc::StreamCollectionInterface* current_streams,
- webrtc::MediaStreamInterface* new_stream) {
+bool CanAddLocalMediaStream(StreamCollectionInterface* current_streams,
+ MediaStreamInterface* new_stream) {
if (!new_stream || !current_streams) {
return false;
}
@@ -847,7 +833,7 @@ bool CanAddLocalMediaStream(webrtc::StreamCollectionInterface* current_streams,
return true;
}
-rtc::scoped_refptr<webrtc::DtlsTransport> LookupDtlsTransportByMid(
+rtc::scoped_refptr<DtlsTransport> LookupDtlsTransportByMid(
rtc::Thread* network_thread,
JsepTransportController* controller,
const std::string& mid) {
@@ -1888,8 +1874,8 @@ RTCError SdpOfferAnswerHandler::ApplyLocalDescription(
if (audio_content->rejected) {
RemoveSenders(cricket::MEDIA_TYPE_AUDIO);
} else {
- const cricket::AudioContentDescription* audio_desc =
- audio_content->media_description()->as_audio();
+ const cricket::MediaContentDescription* audio_desc =
+ audio_content->media_description();
UpdateLocalSenders(audio_desc->streams(), audio_desc->type());
}
}
@@ -1900,8 +1886,8 @@ RTCError SdpOfferAnswerHandler::ApplyLocalDescription(
if (video_content->rejected) {
RemoveSenders(cricket::MEDIA_TYPE_VIDEO);
} else {
- const cricket::VideoContentDescription* video_desc =
- video_content->media_description()->as_video();
+ const cricket::MediaContentDescription* video_desc =
+ video_content->media_description();
UpdateLocalSenders(video_desc->streams(), video_desc->type());
}
}
@@ -2010,8 +1996,11 @@ RTCError SdpOfferAnswerHandler::ReplaceRemoteDescription(
const cricket::SessionDescription* session_desc =
remote_description()->description();
+ const auto* local = local_description();
+
// NOTE: This will perform a BlockingCall() to the network thread.
- return transport_controller_s()->SetRemoteDescription(sdp_type, session_desc);
+ return transport_controller_s()->SetRemoteDescription(
+ sdp_type, local ? local->description() : nullptr, session_desc);
}
void SdpOfferAnswerHandler::ApplyRemoteDescription(
@@ -4932,13 +4921,15 @@ RTCError SdpOfferAnswerHandler::PushdownTransportDescription(
if (source == cricket::CS_LOCAL) {
const SessionDescriptionInterface* sdesc = local_description();
RTC_DCHECK(sdesc);
- return transport_controller_s()->SetLocalDescription(type,
- sdesc->description());
+ const auto* remote = remote_description();
+ return transport_controller_s()->SetLocalDescription(
+ type, sdesc->description(), remote ? remote->description() : nullptr);
} else {
const SessionDescriptionInterface* sdesc = remote_description();
RTC_DCHECK(sdesc);
- return transport_controller_s()->SetRemoteDescription(type,
- sdesc->description());
+ const auto* local = local_description();
+ return transport_controller_s()->SetRemoteDescription(
+ type, local ? local->description() : nullptr, sdesc->description());
}
}
@@ -5366,44 +5357,40 @@ bool SdpOfferAnswerHandler::UpdatePayloadTypeDemuxingState(
// Ignore transceivers that are not receiving.
continue;
}
- switch (content_info.media_description()->type()) {
- case cricket::MediaType::MEDIA_TYPE_AUDIO: {
- if (!mid_header_extension_missing_audio) {
- mid_header_extension_missing_audio =
- !ContentHasHeaderExtension(content_info, RtpExtension::kMidUri);
- }
- const cricket::AudioContentDescription* audio_desc =
- content_info.media_description()->as_audio();
- for (const cricket::AudioCodec& audio : audio_desc->codecs()) {
- if (payload_types->audio_payload_types.count(audio.id)) {
+ const cricket::MediaType media_type =
+ content_info.media_description()->type();
+ if (media_type == cricket::MediaType::MEDIA_TYPE_AUDIO ||
+ media_type == cricket::MediaType::MEDIA_TYPE_VIDEO) {
+ if (media_type == cricket::MediaType::MEDIA_TYPE_AUDIO &&
+ !mid_header_extension_missing_audio) {
+ mid_header_extension_missing_audio =
+ !ContentHasHeaderExtension(content_info, RtpExtension::kMidUri);
+ } else if (media_type == cricket::MEDIA_TYPE_VIDEO &&
+ !mid_header_extension_missing_video) {
+ mid_header_extension_missing_video =
+ !ContentHasHeaderExtension(content_info, RtpExtension::kMidUri);
+ }
+ const cricket::MediaContentDescription* media_desc =
+ content_info.media_description();
+ for (const cricket::Codec& codec : media_desc->codecs()) {
+ if (media_type == cricket::MediaType::MEDIA_TYPE_AUDIO) {
+ if (payload_types->audio_payload_types.count(codec.id)) {
// Two m= sections are using the same payload type, thus demuxing
// by payload type is not possible.
- payload_types->pt_demuxing_possible_audio = false;
+ if (media_type == cricket::MediaType::MEDIA_TYPE_AUDIO) {
+ payload_types->pt_demuxing_possible_audio = false;
+ }
}
- payload_types->audio_payload_types.insert(audio.id);
- }
- break;
- }
- case cricket::MediaType::MEDIA_TYPE_VIDEO: {
- if (!mid_header_extension_missing_video) {
- mid_header_extension_missing_video =
- !ContentHasHeaderExtension(content_info, RtpExtension::kMidUri);
- }
- const cricket::VideoContentDescription* video_desc =
- content_info.media_description()->as_video();
- for (const cricket::VideoCodec& video : video_desc->codecs()) {
- if (payload_types->video_payload_types.count(video.id)) {
+ payload_types->audio_payload_types.insert(codec.id);
+ } else if (media_type == cricket::MEDIA_TYPE_VIDEO) {
+ if (payload_types->video_payload_types.count(codec.id)) {
// Two m= sections are using the same payload type, thus demuxing
// by payload type is not possible.
payload_types->pt_demuxing_possible_video = false;
}
- payload_types->video_payload_types.insert(video.id);
+ payload_types->video_payload_types.insert(codec.id);
}
- break;
}
- default:
- // Ignore data channels.
- continue;
}
}
diff --git a/third_party/libwebrtc/pc/sdp_offer_answer.h b/third_party/libwebrtc/pc/sdp_offer_answer.h
index 8aa7040b16..88ddfe0332 100644
--- a/third_party/libwebrtc/pc/sdp_offer_answer.h
+++ b/third_party/libwebrtc/pc/sdp_offer_answer.h
@@ -674,8 +674,8 @@ class SdpOfferAnswerHandler : public SdpStateProvider {
// or else the CreateBuiltinVideoBitrateAllocatorFactory() will be called.
// Note that one can still choose to override this in a MediaEngine
// if one wants too.
- std::unique_ptr<webrtc::VideoBitrateAllocatorFactory>
- video_bitrate_allocator_factory_ RTC_GUARDED_BY(signaling_thread());
+ std::unique_ptr<VideoBitrateAllocatorFactory> video_bitrate_allocator_factory_
+ RTC_GUARDED_BY(signaling_thread());
// Whether we are the initial offerer on the association. This
// determines the SSL role.
diff --git a/third_party/libwebrtc/pc/sdp_offer_answer_unittest.cc b/third_party/libwebrtc/pc/sdp_offer_answer_unittest.cc
index 94ceff10ac..f158febac7 100644
--- a/third_party/libwebrtc/pc/sdp_offer_answer_unittest.cc
+++ b/third_party/libwebrtc/pc/sdp_offer_answer_unittest.cc
@@ -88,7 +88,7 @@ class SdpOfferAnswerTest : public ::testing::Test {
Dav1dDecoderTemplateAdapter>>(),
nullptr /* audio_mixer */,
nullptr /* audio_processing */)) {
- webrtc::metrics::Reset();
+ metrics::Reset();
}
std::unique_ptr<PeerConnectionWrapper> CreatePeerConnection() {
@@ -168,8 +168,8 @@ TEST_F(SdpOfferAnswerTest, BundleRejectsCodecCollisionsAudioVideo) {
// There is no error yet but the metrics counter will increase.
EXPECT_TRUE(error.ok());
EXPECT_METRIC_EQ(
- 1, webrtc::metrics::NumEvents(
- "WebRTC.PeerConnection.ValidBundledPayloadTypes", false));
+ 1, metrics::NumEvents("WebRTC.PeerConnection.ValidBundledPayloadTypes",
+ false));
// Tolerate codec collisions in rejected m-lines.
pc = CreatePeerConnection();
@@ -178,9 +178,9 @@ TEST_F(SdpOfferAnswerTest, BundleRejectsCodecCollisionsAudioVideo) {
absl::StrReplaceAll(sdp, {{"m=video 9 ", "m=video 0 "}}));
pc->SetRemoteDescription(std::move(rejected_offer), &error);
EXPECT_TRUE(error.ok());
- EXPECT_METRIC_EQ(1,
- webrtc::metrics::NumEvents(
- "WebRTC.PeerConnection.ValidBundledPayloadTypes", true));
+ EXPECT_METRIC_EQ(
+ 1, metrics::NumEvents("WebRTC.PeerConnection.ValidBundledPayloadTypes",
+ true));
}
TEST_F(SdpOfferAnswerTest, BundleRejectsCodecCollisionsVideoFmtp) {
@@ -221,8 +221,8 @@ TEST_F(SdpOfferAnswerTest, BundleRejectsCodecCollisionsVideoFmtp) {
pc->SetRemoteDescription(std::move(desc), &error);
EXPECT_TRUE(error.ok());
EXPECT_METRIC_EQ(
- 1, webrtc::metrics::NumEvents(
- "WebRTC.PeerConnection.ValidBundledPayloadTypes", false));
+ 1, metrics::NumEvents("WebRTC.PeerConnection.ValidBundledPayloadTypes",
+ false));
}
TEST_F(SdpOfferAnswerTest, BundleCodecCollisionInDifferentBundlesAllowed) {
@@ -264,8 +264,8 @@ TEST_F(SdpOfferAnswerTest, BundleCodecCollisionInDifferentBundlesAllowed) {
pc->SetRemoteDescription(std::move(desc), &error);
EXPECT_TRUE(error.ok());
EXPECT_METRIC_EQ(
- 0, webrtc::metrics::NumEvents(
- "WebRTC.PeerConnection.ValidBundledPayloadTypes", false));
+ 0, metrics::NumEvents("WebRTC.PeerConnection.ValidBundledPayloadTypes",
+ false));
}
TEST_F(SdpOfferAnswerTest, BundleMeasuresHeaderExtensionIdCollision) {
@@ -990,8 +990,7 @@ TEST_F(SdpOfferAnswerTest, SdpMungingWithInvalidPayloadTypeIsRejected) {
auto offer = pc->CreateOffer();
ASSERT_EQ(offer->description()->contents().size(), 1u);
- auto* audio =
- offer->description()->contents()[0].media_description()->as_audio();
+ auto* audio = offer->description()->contents()[0].media_description();
ASSERT_GT(audio->codecs().size(), 0u);
EXPECT_TRUE(audio->rtcp_mux());
auto codecs = audio->codecs();
diff --git a/third_party/libwebrtc/pc/slow_peer_connection_integration_test.cc b/third_party/libwebrtc/pc/slow_peer_connection_integration_test.cc
index fd9d3417df..9e49291d94 100644
--- a/third_party/libwebrtc/pc/slow_peer_connection_integration_test.cc
+++ b/third_party/libwebrtc/pc/slow_peer_connection_integration_test.cc
@@ -67,7 +67,7 @@ class FakeClockForTest : public rtc::ScopedFakeClock {
// Some things use a time of "0" as a special value, so we need to start out
// the fake clock at a nonzero time.
// TODO(deadbeef): Fix this.
- AdvanceTime(webrtc::TimeDelta::Seconds(1000));
+ AdvanceTime(TimeDelta::Seconds(1000));
}
// Explicit handle.
@@ -170,20 +170,20 @@ TEST_P(PeerConnectionIntegrationTest,
CreateTurnServer(turn_server_internal_address, turn_server_external_address,
cricket::PROTO_TLS, "88.88.88.0");
- webrtc::PeerConnectionInterface::IceServer ice_server;
+ PeerConnectionInterface::IceServer ice_server;
ice_server.urls.push_back("turns:88.88.88.0:3478?transport=tcp");
ice_server.username = "test";
ice_server.password = "test";
PeerConnectionInterface::RTCConfiguration client_1_config;
client_1_config.servers.push_back(ice_server);
- client_1_config.type = webrtc::PeerConnectionInterface::kRelay;
+ client_1_config.type = PeerConnectionInterface::kRelay;
PeerConnectionInterface::RTCConfiguration client_2_config;
client_2_config.servers.push_back(ice_server);
// Setting the type to kRelay forces the connection to go through a TURN
// server.
- client_2_config.type = webrtc::PeerConnectionInterface::kRelay;
+ client_2_config.type = PeerConnectionInterface::kRelay;
// Get a copy to the pointer so we can verify calls later.
rtc::TestCertificateVerifier* client_1_cert_verifier =
@@ -194,10 +194,10 @@ TEST_P(PeerConnectionIntegrationTest,
client_2_cert_verifier->verify_certificate_ = false;
// Create the dependencies with the test certificate verifier.
- webrtc::PeerConnectionDependencies client_1_deps(nullptr);
+ PeerConnectionDependencies client_1_deps(nullptr);
client_1_deps.tls_cert_verifier =
std::unique_ptr<rtc::TestCertificateVerifier>(client_1_cert_verifier);
- webrtc::PeerConnectionDependencies client_2_deps(nullptr);
+ PeerConnectionDependencies client_2_deps(nullptr);
client_2_deps.tls_cert_verifier =
std::unique_ptr<rtc::TestCertificateVerifier>(client_2_cert_verifier);
@@ -262,8 +262,8 @@ class PeerConnectionIntegrationIceStatesTest
}
void StartStunServer(const SocketAddress& server_address) {
- stun_server_.reset(
- cricket::TestStunServer::Create(firewall(), server_address));
+ stun_server_ = cricket::TestStunServer::Create(firewall(), server_address,
+ *network_thread());
}
bool TestIPv6() {
@@ -309,7 +309,7 @@ class PeerConnectionIntegrationIceStatesTest
private:
uint32_t port_allocator_flags_;
- std::unique_ptr<cricket::TestStunServer> stun_server_;
+ cricket::TestStunServer::StunServerPtr stun_server_;
};
// Ensure FakeClockForTest is constructed first (see class for rationale).
diff --git a/third_party/libwebrtc/pc/srtp_session.cc b/third_party/libwebrtc/pc/srtp_session.cc
index 5408d3e0da..f16679cef1 100644
--- a/third_party/libwebrtc/pc/srtp_session.cc
+++ b/third_party/libwebrtc/pc/srtp_session.cc
@@ -332,6 +332,12 @@ bool SrtpSession::IsExternalAuthActive() const {
return external_auth_active_;
}
+bool SrtpSession::RemoveSsrcFromSession(uint32_t ssrc) {
+ RTC_DCHECK(session_);
+ // libSRTP expects the SSRC to be in network byte order.
+ return srtp_remove_stream(session_, htonl(ssrc)) == srtp_err_status_ok;
+}
+
bool SrtpSession::GetSendStreamPacketIndex(void* p,
int in_len,
int64_t* index) {
diff --git a/third_party/libwebrtc/pc/srtp_session.h b/third_party/libwebrtc/pc/srtp_session.h
index 60f1860ada..f8fd3e3123 100644
--- a/third_party/libwebrtc/pc/srtp_session.h
+++ b/third_party/libwebrtc/pc/srtp_session.h
@@ -97,6 +97,14 @@ class SrtpSession {
// been set.
bool IsExternalAuthActive() const;
+ // Removes a SSRC from the underlying libSRTP session.
+ // Note: this should only be done for SSRCs that are received.
+ // Removing SSRCs that were sent and then reusing them leads to
+ // cryptographic weaknesses described in
+ // https://www.rfc-editor.org/rfc/rfc3711#section-8
+ // https://www.rfc-editor.org/rfc/rfc7714#section-8.4
+ bool RemoveSsrcFromSession(uint32_t ssrc);
+
private:
bool DoSetKey(int type,
int crypto_suite,
diff --git a/third_party/libwebrtc/pc/srtp_session_unittest.cc b/third_party/libwebrtc/pc/srtp_session_unittest.cc
index 16a840a307..7adfee86fd 100644
--- a/third_party/libwebrtc/pc/srtp_session_unittest.cc
+++ b/third_party/libwebrtc/pc/srtp_session_unittest.cc
@@ -251,4 +251,36 @@ TEST_F(SrtpSessionTest, TestReplay) {
s1_.ProtectRtp(rtp_packet_, rtp_len_, sizeof(rtp_packet_), &out_len));
}
+TEST_F(SrtpSessionTest, RemoveSsrc) {
+ EXPECT_TRUE(s1_.SetSend(kSrtpAes128CmSha1_80, kTestKey1, kTestKeyLen,
+ kEncryptedHeaderExtensionIds));
+ EXPECT_TRUE(s2_.SetRecv(kSrtpAes128CmSha1_80, kTestKey1, kTestKeyLen,
+ kEncryptedHeaderExtensionIds));
+ int out_len;
+ // Encrypt and decrypt the packet once.
+ EXPECT_TRUE(
+ s1_.ProtectRtp(rtp_packet_, rtp_len_, sizeof(rtp_packet_), &out_len));
+ EXPECT_TRUE(s2_.UnprotectRtp(rtp_packet_, out_len, &out_len));
+ EXPECT_EQ(rtp_len_, out_len);
+ EXPECT_EQ(0, memcmp(rtp_packet_, kPcmuFrame, out_len));
+
+ // Recreate the original packet and encrypt again.
+ memcpy(rtp_packet_, kPcmuFrame, rtp_len_);
+ EXPECT_TRUE(
+ s1_.ProtectRtp(rtp_packet_, rtp_len_, sizeof(rtp_packet_), &out_len));
+ // Attempting to decrypt will fail as a replay attack.
+ // (srtp_err_status_replay_fail) since the sequence number was already seen.
+ EXPECT_FALSE(s2_.UnprotectRtp(rtp_packet_, out_len, &out_len));
+
+ // Remove the fake packet SSRC 1 from the session.
+ EXPECT_TRUE(s2_.RemoveSsrcFromSession(1));
+ EXPECT_FALSE(s2_.RemoveSsrcFromSession(1));
+
+ // Since the SRTP state was discarded, this is no longer a replay attack.
+ EXPECT_TRUE(s2_.UnprotectRtp(rtp_packet_, out_len, &out_len));
+ EXPECT_EQ(rtp_len_, out_len);
+ EXPECT_EQ(0, memcmp(rtp_packet_, kPcmuFrame, out_len));
+ EXPECT_TRUE(s2_.RemoveSsrcFromSession(1));
+}
+
} // namespace rtc
diff --git a/third_party/libwebrtc/pc/srtp_transport.cc b/third_party/libwebrtc/pc/srtp_transport.cc
index cc20216672..c82839ca82 100644
--- a/third_party/libwebrtc/pc/srtp_transport.cc
+++ b/third_party/libwebrtc/pc/srtp_transport.cc
@@ -37,86 +37,6 @@ SrtpTransport::SrtpTransport(bool rtcp_mux_enabled,
const FieldTrialsView& field_trials)
: RtpTransport(rtcp_mux_enabled), field_trials_(field_trials) {}
-RTCError SrtpTransport::SetSrtpSendKey(const cricket::CryptoParams& params) {
- if (send_params_) {
- LOG_AND_RETURN_ERROR(
- webrtc::RTCErrorType::UNSUPPORTED_OPERATION,
- "Setting the SRTP send key twice is currently unsupported.");
- }
- if (recv_params_ && recv_params_->crypto_suite != params.crypto_suite) {
- LOG_AND_RETURN_ERROR(
- webrtc::RTCErrorType::UNSUPPORTED_OPERATION,
- "The send key and receive key must have the same cipher suite.");
- }
-
- send_crypto_suite_ = rtc::SrtpCryptoSuiteFromName(params.crypto_suite);
- if (*send_crypto_suite_ == rtc::kSrtpInvalidCryptoSuite) {
- return RTCError(RTCErrorType::INVALID_PARAMETER,
- "Invalid SRTP crypto suite");
- }
-
- int send_key_len, send_salt_len;
- if (!rtc::GetSrtpKeyAndSaltLengths(*send_crypto_suite_, &send_key_len,
- &send_salt_len)) {
- return RTCError(RTCErrorType::INVALID_PARAMETER,
- "Could not get lengths for crypto suite(s):"
- " send crypto_suite ");
- }
-
- send_key_ = rtc::ZeroOnFreeBuffer<uint8_t>(send_key_len + send_salt_len);
- if (!ParseKeyParams(params.key_params, send_key_.data(), send_key_.size())) {
- return RTCError(RTCErrorType::INVALID_PARAMETER,
- "Failed to parse the crypto key params");
- }
-
- if (!MaybeSetKeyParams()) {
- return RTCError(RTCErrorType::INVALID_PARAMETER,
- "Failed to set the crypto key params");
- }
- send_params_ = params;
- return RTCError::OK();
-}
-
-RTCError SrtpTransport::SetSrtpReceiveKey(const cricket::CryptoParams& params) {
- if (recv_params_) {
- LOG_AND_RETURN_ERROR(
- webrtc::RTCErrorType::UNSUPPORTED_OPERATION,
- "Setting the SRTP send key twice is currently unsupported.");
- }
- if (send_params_ && send_params_->crypto_suite != params.crypto_suite) {
- LOG_AND_RETURN_ERROR(
- webrtc::RTCErrorType::UNSUPPORTED_OPERATION,
- "The send key and receive key must have the same cipher suite.");
- }
-
- recv_crypto_suite_ = rtc::SrtpCryptoSuiteFromName(params.crypto_suite);
- if (*recv_crypto_suite_ == rtc::kSrtpInvalidCryptoSuite) {
- return RTCError(RTCErrorType::INVALID_PARAMETER,
- "Invalid SRTP crypto suite");
- }
-
- int recv_key_len, recv_salt_len;
- if (!rtc::GetSrtpKeyAndSaltLengths(*recv_crypto_suite_, &recv_key_len,
- &recv_salt_len)) {
- return RTCError(RTCErrorType::INVALID_PARAMETER,
- "Could not get lengths for crypto suite(s):"
- " recv crypto_suite ");
- }
-
- recv_key_ = rtc::ZeroOnFreeBuffer<uint8_t>(recv_key_len + recv_salt_len);
- if (!ParseKeyParams(params.key_params, recv_key_.data(), recv_key_.size())) {
- return RTCError(RTCErrorType::INVALID_PARAMETER,
- "Failed to parse the crypto key params");
- }
-
- if (!MaybeSetKeyParams()) {
- return RTCError(RTCErrorType::INVALID_PARAMETER,
- "Failed to set the crypto key params");
- }
- recv_params_ = params;
- return RTCError::OK();
-}
-
bool SrtpTransport::SendRtpPacket(rtc::CopyOnWriteBuffer* packet,
const rtc::PacketOptions& options,
int flags) {
@@ -519,4 +439,19 @@ void SrtpTransport::MaybeUpdateWritableState() {
}
}
+bool SrtpTransport::UnregisterRtpDemuxerSink(RtpPacketSinkInterface* sink) {
+ if (recv_session_ &&
+ field_trials_.IsEnabled("WebRTC-SrtpRemoveReceiveStream")) {
+ // Remove the SSRCs explicitly registered with the demuxer
+ // (via SDP negotiation) from the SRTP session.
+ for (const auto ssrc : GetSsrcsForSink(sink)) {
+ if (!recv_session_->RemoveSsrcFromSession(ssrc)) {
+ RTC_LOG(LS_WARNING)
+ << "Could not remove SSRC " << ssrc << " from SRTP session.";
+ }
+ }
+ }
+ return RtpTransport::UnregisterRtpDemuxerSink(sink);
+}
+
} // namespace webrtc
diff --git a/third_party/libwebrtc/pc/srtp_transport.h b/third_party/libwebrtc/pc/srtp_transport.h
index 46c11ed56d..29721f3b68 100644
--- a/third_party/libwebrtc/pc/srtp_transport.h
+++ b/third_party/libwebrtc/pc/srtp_transport.h
@@ -41,9 +41,6 @@ class SrtpTransport : public RtpTransport {
virtual ~SrtpTransport() = default;
- virtual RTCError SetSrtpSendKey(const cricket::CryptoParams& params);
- virtual RTCError SetSrtpReceiveKey(const cricket::CryptoParams& params);
-
bool SendRtpPacket(rtc::CopyOnWriteBuffer* packet,
const rtc::PacketOptions& options,
int flags) override;
@@ -109,6 +106,10 @@ class SrtpTransport : public RtpTransport {
rtp_abs_sendtime_extn_id_ = rtp_abs_sendtime_extn_id;
}
+ // In addition to unregistering the sink, the SRTP transport
+ // disassociates all SSRCs of the sink from libSRTP.
+ bool UnregisterRtpDemuxerSink(RtpPacketSinkInterface* sink) override;
+
protected:
// If the writable state changed, fire the SignalWritableState.
void MaybeUpdateWritableState();
diff --git a/third_party/libwebrtc/pc/srtp_transport_unittest.cc b/third_party/libwebrtc/pc/srtp_transport_unittest.cc
index ac8be8762b..de4ff03179 100644
--- a/third_party/libwebrtc/pc/srtp_transport_unittest.cc
+++ b/third_party/libwebrtc/pc/srtp_transport_unittest.cc
@@ -342,7 +342,7 @@ class SrtpTransportTest : public ::testing::Test, public sigslot::has_slots<> {
TransportObserver rtp_sink2_;
int sequence_number_ = 0;
- webrtc::test::ScopedKeyValueConfig field_trials_;
+ test::ScopedKeyValueConfig field_trials_;
};
class SrtpTransportTestWithExternalAuth
@@ -425,4 +425,66 @@ TEST_F(SrtpTransportTest, TestSetParamsKeyTooShort) {
rtc::kSrtpAes128CmSha1_80, kTestKey1, kTestKeyLen - 1, extension_ids));
}
+TEST_F(SrtpTransportTest, RemoveSrtpReceiveStream) {
+ test::ScopedKeyValueConfig field_trials(
+ "WebRTC-SrtpRemoveReceiveStream/Enabled/");
+ auto srtp_transport =
+ std::make_unique<SrtpTransport>(/*rtcp_mux_enabled=*/true, field_trials);
+ auto rtp_packet_transport = std::make_unique<rtc::FakePacketTransport>(
+ "fake_packet_transport_loopback");
+
+ bool asymmetric = false;
+ rtp_packet_transport->SetDestination(rtp_packet_transport.get(), asymmetric);
+ srtp_transport->SetRtpPacketTransport(rtp_packet_transport.get());
+
+ TransportObserver rtp_sink;
+
+ std::vector<int> extension_ids;
+ EXPECT_TRUE(srtp_transport->SetRtpParams(
+ rtc::kSrtpAeadAes128Gcm, kTestKeyGcm128_1, kTestKeyGcm128Len,
+ extension_ids, rtc::kSrtpAeadAes128Gcm, kTestKeyGcm128_1,
+ kTestKeyGcm128Len, extension_ids));
+
+ RtpDemuxerCriteria demuxer_criteria;
+ uint32_t ssrc = 0x1; // SSRC of kPcmuFrame
+ demuxer_criteria.ssrcs().insert(ssrc);
+ EXPECT_TRUE(
+ srtp_transport->RegisterRtpDemuxerSink(demuxer_criteria, &rtp_sink));
+
+ // Create a packet and try to send it three times.
+ size_t rtp_len = sizeof(kPcmuFrame);
+ size_t packet_size = rtp_len + rtc::rtp_auth_tag_len(rtc::kCsAeadAes128Gcm);
+ rtc::Buffer rtp_packet_buffer(packet_size);
+ char* rtp_packet_data = rtp_packet_buffer.data<char>();
+ memcpy(rtp_packet_data, kPcmuFrame, rtp_len);
+
+ // First attempt will succeed.
+ rtc::CopyOnWriteBuffer first_try(rtp_packet_data, rtp_len, packet_size);
+ EXPECT_TRUE(srtp_transport->SendRtpPacket(&first_try, rtc::PacketOptions(),
+ cricket::PF_SRTP_BYPASS));
+ EXPECT_EQ(rtp_sink.rtp_count(), 1);
+
+ // Second attempt will be rejected by libSRTP as a replay attack
+ // (srtp_err_status_replay_fail) since the sequence number was already seen.
+ // Hence the packet never reaches the sink.
+ rtc::CopyOnWriteBuffer second_try(rtp_packet_data, rtp_len, packet_size);
+ EXPECT_TRUE(srtp_transport->SendRtpPacket(&second_try, rtc::PacketOptions(),
+ cricket::PF_SRTP_BYPASS));
+ EXPECT_EQ(rtp_sink.rtp_count(), 1);
+
+ // Reset the sink.
+ EXPECT_TRUE(srtp_transport->UnregisterRtpDemuxerSink(&rtp_sink));
+ EXPECT_TRUE(
+ srtp_transport->RegisterRtpDemuxerSink(demuxer_criteria, &rtp_sink));
+
+ // Third attempt will succeed again since libSRTP does not remember seeing
+ // the sequence number after the reset.
+ rtc::CopyOnWriteBuffer third_try(rtp_packet_data, rtp_len, packet_size);
+ EXPECT_TRUE(srtp_transport->SendRtpPacket(&third_try, rtc::PacketOptions(),
+ cricket::PF_SRTP_BYPASS));
+ EXPECT_EQ(rtp_sink.rtp_count(), 2);
+ // Clear the sink to clean up.
+ srtp_transport->UnregisterRtpDemuxerSink(&rtp_sink);
+}
+
} // namespace webrtc
diff --git a/third_party/libwebrtc/pc/test/android_test_initializer.cc b/third_party/libwebrtc/pc/test/android_test_initializer.cc
index 963544cb4b..88b4587789 100644
--- a/third_party/libwebrtc/pc/test/android_test_initializer.cc
+++ b/third_party/libwebrtc/pc/test/android_test_initializer.cc
@@ -39,7 +39,7 @@ void EnsureInitializedOnce() {
RTC_CHECK(rtc::InitializeSSL()) << "Failed to InitializeSSL()";
- webrtc::JVM::Initialize(jvm);
+ JVM::Initialize(jvm);
}
} // anonymous namespace
diff --git a/third_party/libwebrtc/pc/test/enable_fake_media.cc b/third_party/libwebrtc/pc/test/enable_fake_media.cc
new file mode 100644
index 0000000000..5497c60724
--- /dev/null
+++ b/third_party/libwebrtc/pc/test/enable_fake_media.cc
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2023 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "pc/test/enable_fake_media.h"
+
+#include <memory>
+#include <utility>
+
+#include "absl/base/nullability.h"
+#include "api/environment/environment.h"
+#include "api/peer_connection_interface.h"
+#include "call/call.h"
+#include "call/call_config.h"
+#include "media/base/fake_media_engine.h"
+#include "pc/media_factory.h"
+#include "rtc_base/checks.h"
+
+namespace webrtc {
+
+using ::cricket::FakeMediaEngine;
+using ::cricket::MediaEngineInterface;
+
+void EnableFakeMedia(
+ PeerConnectionFactoryDependencies& deps,
+ absl::Nonnull<std::unique_ptr<FakeMediaEngine>> fake_media_engine) {
+ class FakeMediaFactory : public MediaFactory {
+ public:
+ explicit FakeMediaFactory(
+ absl::Nonnull<std::unique_ptr<FakeMediaEngine>> fake)
+ : fake_(std::move(fake)) {}
+
+ std::unique_ptr<Call> CreateCall(const CallConfig& config) override {
+ return Call::Create(config);
+ }
+
+ std::unique_ptr<MediaEngineInterface> CreateMediaEngine(
+ const Environment& /*env*/,
+ PeerConnectionFactoryDependencies& /*dependencies*/) {
+ RTC_CHECK(fake_ != nullptr)
+ << "CreateMediaEngine can be called at most once.";
+ return std::move(fake_);
+ }
+
+ private:
+ absl::Nullable<std::unique_ptr<FakeMediaEngine>> fake_;
+ };
+
+ deps.media_factory =
+ std::make_unique<FakeMediaFactory>(std::move(fake_media_engine));
+}
+
+void EnableFakeMedia(PeerConnectionFactoryDependencies& deps) {
+ EnableFakeMedia(deps, std::make_unique<cricket::FakeMediaEngine>());
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/pc/test/enable_fake_media.h b/third_party/libwebrtc/pc/test/enable_fake_media.h
new file mode 100644
index 0000000000..82c55ad08b
--- /dev/null
+++ b/third_party/libwebrtc/pc/test/enable_fake_media.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2023 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+// Enables fake media support for PeerConnnectionFactory created from `deps` for
+// testing purposes. Such fake media support ignores media dependencies in the
+// `PeerConnectionFactoryDependencies`. Allows to test PeerConnection and
+// PeerConnectionFactory in the presence of the media, but doesn't test media
+// support itself.
+
+#ifndef PC_TEST_ENABLE_FAKE_MEDIA_H_
+#define PC_TEST_ENABLE_FAKE_MEDIA_H_
+
+#include <memory>
+
+#include "absl/base/nullability.h"
+#include "api/peer_connection_interface.h"
+#include "media/base/fake_media_engine.h"
+
+namespace webrtc {
+
+// Enables media support backed by the 'fake_media_engine'.
+void EnableFakeMedia(
+ PeerConnectionFactoryDependencies& deps,
+ absl::Nonnull<std::unique_ptr<cricket::FakeMediaEngine>> fake_media_engine);
+
+// Enables media support backed by unspecified lightweight fake implementation.
+void EnableFakeMedia(PeerConnectionFactoryDependencies& deps);
+
+} // namespace webrtc
+
+#endif // PC_TEST_ENABLE_FAKE_MEDIA_H_
diff --git a/third_party/libwebrtc/pc/test/fake_peer_connection_base.h b/third_party/libwebrtc/pc/test/fake_peer_connection_base.h
index a1c8dca12e..1615088e99 100644
--- a/third_party/libwebrtc/pc/test/fake_peer_connection_base.h
+++ b/third_party/libwebrtc/pc/test/fake_peer_connection_base.h
@@ -363,7 +363,7 @@ class FakePeerConnectionBase : public PeerConnectionInternal {
const FieldTrialsView& trials() const override { return field_trials_; }
protected:
- webrtc::test::ScopedKeyValueConfig field_trials_;
+ test::ScopedKeyValueConfig field_trials_;
};
} // namespace webrtc
diff --git a/third_party/libwebrtc/pc/test/fake_peer_connection_for_stats.h b/third_party/libwebrtc/pc/test/fake_peer_connection_for_stats.h
index 7302182912..2883c86b58 100644
--- a/third_party/libwebrtc/pc/test/fake_peer_connection_for_stats.h
+++ b/third_party/libwebrtc/pc/test/fake_peer_connection_for_stats.h
@@ -18,10 +18,11 @@
#include <utility>
#include <vector>
-#include "media/base/fake_media_engine.h"
+#include "api/environment/environment_factory.h"
#include "media/base/media_channel.h"
#include "pc/channel.h"
#include "pc/stream_collection.h"
+#include "pc/test/enable_fake_media.h"
#include "pc/test/fake_data_channel_controller.h"
#include "pc/test/fake_peer_connection_base.h"
@@ -150,7 +151,7 @@ class VoiceChannelForTesting : public cricket::VoiceChannel {
receive_channel,
const std::string& content_name,
bool srtp_required,
- webrtc::CryptoOptions crypto_options,
+ CryptoOptions crypto_options,
rtc::UniqueRandomIdGenerator* ssrc_generator,
std::string transport_name)
: VoiceChannel(worker_thread,
@@ -183,7 +184,7 @@ class VideoChannelForTesting : public cricket::VideoChannel {
receive_channel,
const std::string& content_name,
bool srtp_required,
- webrtc::CryptoOptions crypto_options,
+ CryptoOptions crypto_options,
rtc::UniqueRandomIdGenerator* ssrc_generator,
std::string transport_name)
: VideoChannel(worker_thread,
@@ -219,7 +220,8 @@ class FakePeerConnectionForStats : public FakePeerConnectionBase {
signaling_thread_(rtc::Thread::Current()),
// TODO(hta): remove separate thread variables and use context.
dependencies_(MakeDependencies()),
- context_(ConnectionContext::Create(&dependencies_)),
+ context_(
+ ConnectionContext::Create(CreateEnvironment(), &dependencies_)),
local_streams_(StreamCollection::Create()),
remote_streams_(StreamCollection::Create()),
data_channel_controller_(network_thread_) {}
@@ -235,7 +237,7 @@ class FakePeerConnectionForStats : public FakePeerConnectionBase {
dependencies.network_thread = rtc::Thread::Current();
dependencies.worker_thread = rtc::Thread::Current();
dependencies.signaling_thread = rtc::Thread::Current();
- dependencies.media_engine = std::make_unique<cricket::FakeMediaEngine>();
+ EnableFakeMedia(dependencies);
return dependencies;
}
@@ -298,7 +300,7 @@ class FakePeerConnectionForStats : public FakePeerConnectionBase {
worker_thread_, network_thread_, signaling_thread_,
std::move(voice_media_send_channel),
std::move(voice_media_receive_channel), mid, kDefaultSrtpRequired,
- webrtc::CryptoOptions(), context_->ssrc_generator(), transport_name);
+ CryptoOptions(), context_->ssrc_generator(), transport_name);
auto transceiver =
GetOrCreateFirstTransceiverOfType(cricket::MEDIA_TYPE_AUDIO)
->internal();
@@ -332,7 +334,7 @@ class FakePeerConnectionForStats : public FakePeerConnectionBase {
worker_thread_, network_thread_, signaling_thread_,
std::move(video_media_send_channel),
std::move(video_media_receive_channel), mid, kDefaultSrtpRequired,
- webrtc::CryptoOptions(), context_->ssrc_generator(), transport_name);
+ CryptoOptions(), context_->ssrc_generator(), transport_name);
auto transceiver =
GetOrCreateFirstTransceiverOfType(cricket::MEDIA_TYPE_VIDEO)
->internal();
diff --git a/third_party/libwebrtc/pc/test/fake_periodic_video_source.h b/third_party/libwebrtc/pc/test/fake_periodic_video_source.h
index 452a8f6c30..65652bdf0d 100644
--- a/third_party/libwebrtc/pc/test/fake_periodic_video_source.h
+++ b/third_party/libwebrtc/pc/test/fake_periodic_video_source.h
@@ -65,12 +65,12 @@ class FakePeriodicVideoSource final
return wants_;
}
- void RemoveSink(rtc::VideoSinkInterface<webrtc::VideoFrame>* sink) override {
+ void RemoveSink(rtc::VideoSinkInterface<VideoFrame>* sink) override {
RTC_DCHECK(thread_checker_.IsCurrent());
broadcaster_.RemoveSink(sink);
}
- void AddOrUpdateSink(rtc::VideoSinkInterface<webrtc::VideoFrame>* sink,
+ void AddOrUpdateSink(rtc::VideoSinkInterface<VideoFrame>* sink,
const rtc::VideoSinkWants& wants) override {
RTC_DCHECK(thread_checker_.IsCurrent());
{
diff --git a/third_party/libwebrtc/pc/test/integration_test_helpers.cc b/third_party/libwebrtc/pc/test/integration_test_helpers.cc
index ede159d744..64d8debc09 100644
--- a/third_party/libwebrtc/pc/test/integration_test_helpers.cc
+++ b/third_party/libwebrtc/pc/test/integration_test_helpers.cc
@@ -46,7 +46,7 @@ void RemoveSsrcsAndKeepMsids(cricket::SessionDescription* desc) {
int FindFirstMediaStatsIndexByKind(
const std::string& kind,
- const std::vector<const webrtc::RTCInboundRtpStreamStats*>& inbound_rtps) {
+ const std::vector<const RTCInboundRtpStreamStats*>& inbound_rtps) {
for (size_t i = 0; i < inbound_rtps.size(); i++) {
if (*inbound_rtps[i]->kind == kind) {
return i;
diff --git a/third_party/libwebrtc/pc/test/integration_test_helpers.h b/third_party/libwebrtc/pc/test/integration_test_helpers.h
index 36b2111324..fb719e7ddd 100644
--- a/third_party/libwebrtc/pc/test/integration_test_helpers.h
+++ b/third_party/libwebrtc/pc/test/integration_test_helpers.h
@@ -31,10 +31,10 @@
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
#include "api/audio_options.h"
-#include "api/call/call_factory_interface.h"
#include "api/candidate.h"
#include "api/crypto/crypto_options.h"
#include "api/data_channel_interface.h"
+#include "api/enable_media_with_defaults.h"
#include "api/field_trials_view.h"
#include "api/ice_transport_interface.h"
#include "api/jsep.h"
@@ -68,14 +68,11 @@
#include "media/base/media_engine.h"
#include "media/base/stream_params.h"
#include "media/engine/fake_webrtc_video_engine.h"
-#include "media/engine/webrtc_media_engine.h"
-#include "media/engine/webrtc_media_engine_defaults.h"
#include "modules/audio_device/include/audio_device.h"
#include "modules/audio_processing/include/audio_processing.h"
#include "modules/audio_processing/test/audio_processing_builder_for_testing.h"
#include "p2p/base/fake_ice_transport.h"
#include "p2p/base/ice_transport_internal.h"
-#include "p2p/base/mock_async_resolver.h"
#include "p2p/base/p2p_constants.h"
#include "p2p/base/port.h"
#include "p2p/base/port_allocator.h"
@@ -179,14 +176,14 @@ void ReplaceFirstSsrc(StreamParams& stream, uint32_t ssrc);
int FindFirstMediaStatsIndexByKind(
const std::string& kind,
- const std::vector<const webrtc::RTCInboundRtpStreamStats*>& inbound_rtps);
+ const std::vector<const RTCInboundRtpStreamStats*>& inbound_rtps);
-class TaskQueueMetronome : public webrtc::Metronome {
+class TaskQueueMetronome : public Metronome {
public:
explicit TaskQueueMetronome(TimeDelta tick_period);
~TaskQueueMetronome() override;
- // webrtc::Metronome implementation.
+ // Metronome implementation.
void RequestCallOnNextTick(absl::AnyInvocable<void() &&> callback) override;
TimeDelta TickPeriod() const override;
@@ -209,7 +206,7 @@ class SignalingMessageReceiver {
virtual ~SignalingMessageReceiver() {}
};
-class MockRtpReceiverObserver : public webrtc::RtpReceiverObserverInterface {
+class MockRtpReceiverObserver : public RtpReceiverObserverInterface {
public:
explicit MockRtpReceiverObserver(cricket::MediaType media_type)
: expected_media_type_(media_type) {}
@@ -236,14 +233,14 @@ class MockRtpReceiverObserver : public webrtc::RtpReceiverObserverInterface {
// advertise support of any codecs.
// TODO(steveanton): See how this could become a subclass of
// PeerConnectionWrapper defined in peerconnectionwrapper.h.
-class PeerConnectionIntegrationWrapper : public webrtc::PeerConnectionObserver,
+class PeerConnectionIntegrationWrapper : public PeerConnectionObserver,
public SignalingMessageReceiver {
public:
- webrtc::PeerConnectionFactoryInterface* pc_factory() const {
+ PeerConnectionFactoryInterface* pc_factory() const {
return peer_connection_factory_.get();
}
- webrtc::PeerConnectionInterface* pc() const { return peer_connection_.get(); }
+ PeerConnectionInterface* pc() const { return peer_connection_.get(); }
// If a signaling message receiver is set (via ConnectFakeSignaling), this
// will set the whole offer/answer exchange in motion. Just need to wait for
@@ -341,11 +338,11 @@ class PeerConnectionIntegrationWrapper : public webrtc::PeerConnectionObserver,
return AddTrack(CreateLocalVideoTrack());
}
- rtc::scoped_refptr<webrtc::AudioTrackInterface> CreateLocalAudioTrack() {
+ rtc::scoped_refptr<AudioTrackInterface> CreateLocalAudioTrack() {
cricket::AudioOptions options;
// Disable highpass filter so that we can get all the test audio frames.
options.highpass_filter = false;
- rtc::scoped_refptr<webrtc::AudioSourceInterface> source =
+ rtc::scoped_refptr<AudioSourceInterface> source =
peer_connection_factory_->CreateAudioSource(options);
// TODO(perkj): Test audio source when it is implemented. Currently audio
// always use the default input.
@@ -353,21 +350,20 @@ class PeerConnectionIntegrationWrapper : public webrtc::PeerConnectionObserver,
source.get());
}
- rtc::scoped_refptr<webrtc::VideoTrackInterface> CreateLocalVideoTrack() {
- webrtc::FakePeriodicVideoSource::Config config;
+ rtc::scoped_refptr<VideoTrackInterface> CreateLocalVideoTrack() {
+ FakePeriodicVideoSource::Config config;
config.timestamp_offset_ms = rtc::TimeMillis();
return CreateLocalVideoTrackInternal(config);
}
- rtc::scoped_refptr<webrtc::VideoTrackInterface>
- CreateLocalVideoTrackWithConfig(
- webrtc::FakePeriodicVideoSource::Config config) {
+ rtc::scoped_refptr<VideoTrackInterface> CreateLocalVideoTrackWithConfig(
+ FakePeriodicVideoSource::Config config) {
return CreateLocalVideoTrackInternal(config);
}
- rtc::scoped_refptr<webrtc::VideoTrackInterface>
- CreateLocalVideoTrackWithRotation(webrtc::VideoRotation rotation) {
- webrtc::FakePeriodicVideoSource::Config config;
+ rtc::scoped_refptr<VideoTrackInterface> CreateLocalVideoTrackWithRotation(
+ VideoRotation rotation) {
+ FakePeriodicVideoSource::Config config;
config.rotation = rotation;
config.timestamp_offset_ms = rtc::TimeMillis();
return CreateLocalVideoTrackInternal(config);
@@ -411,22 +407,22 @@ class PeerConnectionIntegrationWrapper : public webrtc::PeerConnectionObserver,
}
bool SignalingStateStable() {
- return pc()->signaling_state() == webrtc::PeerConnectionInterface::kStable;
+ return pc()->signaling_state() == PeerConnectionInterface::kStable;
}
bool IceGatheringStateComplete() {
return pc()->ice_gathering_state() ==
- webrtc::PeerConnectionInterface::kIceGatheringComplete;
+ PeerConnectionInterface::kIceGatheringComplete;
}
void CreateDataChannel() { CreateDataChannel(nullptr); }
- void CreateDataChannel(const webrtc::DataChannelInit* init) {
+ void CreateDataChannel(const DataChannelInit* init) {
CreateDataChannel(kDataChannelLabel, init);
}
void CreateDataChannel(const std::string& label,
- const webrtc::DataChannelInit* init) {
+ const DataChannelInit* init) {
auto data_channel_or_error = pc()->CreateDataChannelOrError(label, init);
ASSERT_TRUE(data_channel_or_error.ok());
data_channels_.push_back(data_channel_or_error.MoveValue());
@@ -484,7 +480,7 @@ class PeerConnectionIntegrationWrapper : public webrtc::PeerConnectionObserver,
// Returns a MockStatsObserver in a state after stats gathering finished,
// which can be used to access the gathered stats.
rtc::scoped_refptr<MockStatsObserver> OldGetStatsForTrack(
- webrtc::MediaStreamTrackInterface* track) {
+ MediaStreamTrackInterface* track) {
auto observer = rtc::make_ref_counted<MockStatsObserver>();
EXPECT_TRUE(peer_connection_->GetStats(
observer.get(), nullptr,
@@ -500,9 +496,8 @@ class PeerConnectionIntegrationWrapper : public webrtc::PeerConnectionObserver,
// Synchronously gets stats and returns them. If it times out, fails the test
// and returns null.
- rtc::scoped_refptr<const webrtc::RTCStatsReport> NewGetStats() {
- auto callback =
- rtc::make_ref_counted<webrtc::MockRTCStatsCollectorCallback>();
+ rtc::scoped_refptr<const RTCStatsReport> NewGetStats() {
+ auto callback = rtc::make_ref_counted<MockRTCStatsCollectorCallback>();
peer_connection_->GetStats(callback.get());
EXPECT_TRUE_WAIT(callback->called(), kDefaultTimeout);
return callback->report();
@@ -529,10 +524,10 @@ class PeerConnectionIntegrationWrapper : public webrtc::PeerConnectionObserver,
return static_cast<double>(rendered_width()) / rendered_height();
}
- webrtc::VideoRotation rendered_rotation() {
+ VideoRotation rendered_rotation() {
EXPECT_FALSE(fake_video_renderers_.empty());
return fake_video_renderers_.empty()
- ? webrtc::kVideoRotation_0
+ ? kVideoRotation_0
: fake_video_renderers_.begin()->second->rotation();
}
@@ -575,20 +570,20 @@ class PeerConnectionIntegrationWrapper : public webrtc::PeerConnectionObserver,
return pc()->local_streams().get();
}
- webrtc::PeerConnectionInterface::SignalingState signaling_state() {
+ PeerConnectionInterface::SignalingState signaling_state() {
return pc()->signaling_state();
}
- webrtc::PeerConnectionInterface::IceConnectionState ice_connection_state() {
+ PeerConnectionInterface::IceConnectionState ice_connection_state() {
return pc()->ice_connection_state();
}
- webrtc::PeerConnectionInterface::IceConnectionState
+ PeerConnectionInterface::IceConnectionState
standardized_ice_connection_state() {
return pc()->standardized_ice_connection_state();
}
- webrtc::PeerConnectionInterface::IceGatheringState ice_gathering_state() {
+ PeerConnectionInterface::IceGatheringState ice_gathering_state() {
return pc()->ice_gathering_state();
}
@@ -617,7 +612,7 @@ class PeerConnectionIntegrationWrapper : public webrtc::PeerConnectionObserver,
}
cricket::PortAllocator* port_allocator() const { return port_allocator_; }
- webrtc::FakeRtcEventLogFactory* event_log_factory() const {
+ FakeRtcEventLogFactory* event_log_factory() const {
return event_log_factory_;
}
@@ -630,8 +625,7 @@ class PeerConnectionIntegrationWrapper : public webrtc::PeerConnectionObserver,
// Sets the mDNS responder for the owned fake network manager and keeps a
// reference to the responder.
- void SetMdnsResponder(
- std::unique_ptr<webrtc::FakeMdnsResponder> mdns_responder) {
+ void SetMdnsResponder(std::unique_ptr<FakeMdnsResponder> mdns_responder) {
RTC_DCHECK(mdns_responder != nullptr);
mdns_responder_ = mdns_responder.get();
network_manager()->set_mdns_responder(std::move(mdns_responder));
@@ -646,7 +640,7 @@ class PeerConnectionIntegrationWrapper : public webrtc::PeerConnectionObserver,
}
bool Rollback() {
return SetRemoteDescription(
- webrtc::CreateSessionDescription(SdpType::kRollback, ""));
+ CreateSessionDescription(SdpType::kRollback, ""));
}
// Functions for querying stats.
@@ -654,7 +648,7 @@ class PeerConnectionIntegrationWrapper : public webrtc::PeerConnectionObserver,
// Get the baseline numbers for audio_packets and audio_delay.
auto received_stats = NewGetStats();
auto rtp_stats =
- received_stats->GetStatsOfType<webrtc::RTCInboundRtpStreamStats>()[0];
+ received_stats->GetStatsOfType<RTCInboundRtpStreamStats>()[0];
ASSERT_TRUE(rtp_stats->relative_packet_arrival_delay.is_defined());
ASSERT_TRUE(rtp_stats->packets_received.is_defined());
rtp_stats_id_ = rtp_stats->id();
@@ -666,8 +660,7 @@ class PeerConnectionIntegrationWrapper : public webrtc::PeerConnectionObserver,
void UpdateDelayStats(std::string tag, int desc_size) {
auto report = NewGetStats();
- auto rtp_stats =
- report->GetAs<webrtc::RTCInboundRtpStreamStats>(rtp_stats_id_);
+ auto rtp_stats = report->GetAs<RTCInboundRtpStreamStats>(rtp_stats_id_);
ASSERT_TRUE(rtp_stats);
auto delta_packets = *rtp_stats->packets_received - audio_packets_stat_;
auto delta_rpad =
@@ -746,11 +739,11 @@ class PeerConnectionIntegrationWrapper : public webrtc::PeerConnectionObserver,
bool Init(const PeerConnectionFactory::Options* options,
const PeerConnectionInterface::RTCConfiguration* config,
- webrtc::PeerConnectionDependencies dependencies,
+ PeerConnectionDependencies dependencies,
rtc::SocketServer* socket_server,
rtc::Thread* network_thread,
rtc::Thread* worker_thread,
- std::unique_ptr<webrtc::FakeRtcEventLogFactory> event_log_factory,
+ std::unique_ptr<FakeRtcEventLogFactory> event_log_factory,
bool reset_encoder_factory,
bool reset_decoder_factory,
bool create_media_engine) {
@@ -773,51 +766,45 @@ class PeerConnectionIntegrationWrapper : public webrtc::PeerConnectionObserver,
}
rtc::Thread* const signaling_thread = rtc::Thread::Current();
- webrtc::PeerConnectionFactoryDependencies pc_factory_dependencies;
+ PeerConnectionFactoryDependencies pc_factory_dependencies;
pc_factory_dependencies.network_thread = network_thread;
pc_factory_dependencies.worker_thread = worker_thread;
pc_factory_dependencies.signaling_thread = signaling_thread;
pc_factory_dependencies.task_queue_factory =
- webrtc::CreateDefaultTaskQueueFactory();
+ CreateDefaultTaskQueueFactory();
pc_factory_dependencies.trials = std::make_unique<FieldTrialBasedConfig>();
pc_factory_dependencies.metronome =
std::make_unique<TaskQueueMetronome>(TimeDelta::Millis(8));
- cricket::MediaEngineDependencies media_deps;
- media_deps.task_queue_factory =
- pc_factory_dependencies.task_queue_factory.get();
- media_deps.adm = fake_audio_capture_module_;
- webrtc::SetMediaEngineDefaults(&media_deps);
+
+ pc_factory_dependencies.adm = fake_audio_capture_module_;
+ if (create_media_engine) {
+ EnableMediaWithDefaults(pc_factory_dependencies);
+ }
if (reset_encoder_factory) {
- media_deps.video_encoder_factory.reset();
+ pc_factory_dependencies.video_encoder_factory.reset();
}
if (reset_decoder_factory) {
- media_deps.video_decoder_factory.reset();
+ pc_factory_dependencies.video_decoder_factory.reset();
}
- if (!media_deps.audio_processing) {
+ if (!pc_factory_dependencies.audio_processing) {
// If the standard Creation method for APM returns a null pointer, instead
// use the builder for testing to create an APM object.
- media_deps.audio_processing = AudioProcessingBuilderForTesting().Create();
+ pc_factory_dependencies.audio_processing =
+ AudioProcessingBuilderForTesting().Create();
}
- media_deps.trials = pc_factory_dependencies.trials.get();
-
- if (create_media_engine) {
- pc_factory_dependencies.media_engine =
- cricket::CreateMediaEngine(std::move(media_deps));
- }
- pc_factory_dependencies.call_factory = webrtc::CreateCallFactory();
if (event_log_factory) {
event_log_factory_ = event_log_factory.get();
pc_factory_dependencies.event_log_factory = std::move(event_log_factory);
} else {
pc_factory_dependencies.event_log_factory =
- std::make_unique<webrtc::RtcEventLogFactory>(
+ std::make_unique<RtcEventLogFactory>(
pc_factory_dependencies.task_queue_factory.get());
}
- peer_connection_factory_ = webrtc::CreateModularPeerConnectionFactory(
- std::move(pc_factory_dependencies));
+ peer_connection_factory_ =
+ CreateModularPeerConnectionFactory(std::move(pc_factory_dependencies));
if (!peer_connection_factory_) {
return false;
@@ -834,9 +821,9 @@ class PeerConnectionIntegrationWrapper : public webrtc::PeerConnectionObserver,
return peer_connection_.get() != nullptr;
}
- rtc::scoped_refptr<webrtc::PeerConnectionInterface> CreatePeerConnection(
+ rtc::scoped_refptr<PeerConnectionInterface> CreatePeerConnection(
const PeerConnectionInterface::RTCConfiguration* config,
- webrtc::PeerConnectionDependencies dependencies) {
+ PeerConnectionDependencies dependencies) {
PeerConnectionInterface::RTCConfiguration modified_config;
modified_config.sdp_semantics = sdp_semantics_;
// If `config` is null, this will result in a default configuration being
@@ -869,21 +856,20 @@ class PeerConnectionIntegrationWrapper : public webrtc::PeerConnectionObserver,
signal_ice_candidates_ = signal;
}
- rtc::scoped_refptr<webrtc::VideoTrackInterface> CreateLocalVideoTrackInternal(
- webrtc::FakePeriodicVideoSource::Config config) {
+ rtc::scoped_refptr<VideoTrackInterface> CreateLocalVideoTrackInternal(
+ FakePeriodicVideoSource::Config config) {
// Set max frame rate to 10fps to reduce the risk of test flakiness.
// TODO(deadbeef): Do something more robust.
config.frame_interval_ms = 100;
video_track_sources_.emplace_back(
- rtc::make_ref_counted<webrtc::FakePeriodicVideoTrackSource>(
+ rtc::make_ref_counted<FakePeriodicVideoTrackSource>(
config, false /* remote */));
- rtc::scoped_refptr<webrtc::VideoTrackInterface> track =
+ rtc::scoped_refptr<VideoTrackInterface> track =
peer_connection_factory_->CreateVideoTrack(video_track_sources_.back(),
rtc::CreateRandomUuid());
if (!local_video_renderer_) {
- local_video_renderer_.reset(
- new webrtc::FakeVideoTrackRenderer(track.get()));
+ local_video_renderer_.reset(new FakeVideoTrackRenderer(track.get()));
}
return track;
}
@@ -891,7 +877,7 @@ class PeerConnectionIntegrationWrapper : public webrtc::PeerConnectionObserver,
void HandleIncomingOffer(const std::string& msg) {
RTC_LOG(LS_INFO) << debug_name_ << ": HandleIncomingOffer";
std::unique_ptr<SessionDescriptionInterface> desc =
- webrtc::CreateSessionDescription(SdpType::kOffer, msg);
+ CreateSessionDescription(SdpType::kOffer, msg);
if (received_sdp_munger_) {
received_sdp_munger_(desc->description());
}
@@ -911,7 +897,7 @@ class PeerConnectionIntegrationWrapper : public webrtc::PeerConnectionObserver,
void HandleIncomingAnswer(const std::string& msg) {
RTC_LOG(LS_INFO) << debug_name_ << ": HandleIncomingAnswer";
std::unique_ptr<SessionDescriptionInterface> desc =
- webrtc::CreateSessionDescription(SdpType::kAnswer, msg);
+ CreateSessionDescription(SdpType::kAnswer, msg);
if (received_sdp_munger_) {
received_sdp_munger_(desc->description());
}
@@ -1062,7 +1048,7 @@ class PeerConnectionIntegrationWrapper : public webrtc::PeerConnectionObserver,
const std::string& msg) override {
RTC_LOG(LS_INFO) << debug_name_ << ": ReceiveIceMessage";
absl::optional<RTCError> result;
- pc()->AddIceCandidate(absl::WrapUnique(webrtc::CreateIceCandidate(
+ pc()->AddIceCandidate(absl::WrapUnique(CreateIceCandidate(
sdp_mid, sdp_mline_index, msg, nullptr)),
[&result](RTCError r) { result = r; });
EXPECT_TRUE_WAIT(result.has_value(), kDefaultTimeout);
@@ -1071,7 +1057,7 @@ class PeerConnectionIntegrationWrapper : public webrtc::PeerConnectionObserver,
// PeerConnectionObserver callbacks.
void OnSignalingChange(
- webrtc::PeerConnectionInterface::SignalingState new_state) override {
+ PeerConnectionInterface::SignalingState new_state) override {
EXPECT_EQ(pc()->signaling_state(), new_state);
peer_connection_signaling_state_history_.push_back(new_state);
}
@@ -1100,21 +1086,21 @@ class PeerConnectionIntegrationWrapper : public webrtc::PeerConnectionObserver,
}
void OnRenegotiationNeeded() override {}
void OnIceConnectionChange(
- webrtc::PeerConnectionInterface::IceConnectionState new_state) override {
+ PeerConnectionInterface::IceConnectionState new_state) override {
EXPECT_EQ(pc()->ice_connection_state(), new_state);
ice_connection_state_history_.push_back(new_state);
}
void OnStandardizedIceConnectionChange(
- webrtc::PeerConnectionInterface::IceConnectionState new_state) override {
+ PeerConnectionInterface::IceConnectionState new_state) override {
standardized_ice_connection_state_history_.push_back(new_state);
}
void OnConnectionChange(
- webrtc::PeerConnectionInterface::PeerConnectionState new_state) override {
+ PeerConnectionInterface::PeerConnectionState new_state) override {
peer_connection_state_history_.push_back(new_state);
}
void OnIceGatheringChange(
- webrtc::PeerConnectionInterface::IceGatheringState new_state) override {
+ PeerConnectionInterface::IceGatheringState new_state) override {
EXPECT_EQ(pc()->ice_gathering_state(), new_state);
ice_gathering_state_history_.push_back(new_state);
}
@@ -1124,7 +1110,7 @@ class PeerConnectionIntegrationWrapper : public webrtc::PeerConnectionObserver,
ice_candidate_pair_change_history_.push_back(event);
}
- void OnIceCandidate(const webrtc::IceCandidateInterface* candidate) override {
+ void OnIceCandidate(const IceCandidateInterface* candidate) override {
RTC_LOG(LS_INFO) << debug_name_ << ": OnIceCandidate";
if (remote_async_dns_resolver_) {
@@ -1180,20 +1166,19 @@ class PeerConnectionIntegrationWrapper : public webrtc::PeerConnectionObserver,
std::unique_ptr<rtc::FakeNetworkManager> fake_network_manager_;
std::unique_ptr<rtc::BasicPacketSocketFactory> socket_factory_;
// Reference to the mDNS responder owned by `fake_network_manager_` after set.
- webrtc::FakeMdnsResponder* mdns_responder_ = nullptr;
+ FakeMdnsResponder* mdns_responder_ = nullptr;
- rtc::scoped_refptr<webrtc::PeerConnectionInterface> peer_connection_;
- rtc::scoped_refptr<webrtc::PeerConnectionFactoryInterface>
- peer_connection_factory_;
+ rtc::scoped_refptr<PeerConnectionInterface> peer_connection_;
+ rtc::scoped_refptr<PeerConnectionFactoryInterface> peer_connection_factory_;
cricket::PortAllocator* port_allocator_;
// Needed to keep track of number of frames sent.
rtc::scoped_refptr<FakeAudioCaptureModule> fake_audio_capture_module_;
// Needed to keep track of number of frames received.
- std::map<std::string, std::unique_ptr<webrtc::FakeVideoTrackRenderer>>
+ std::map<std::string, std::unique_ptr<FakeVideoTrackRenderer>>
fake_video_renderers_;
// Needed to ensure frames aren't received for removed tracks.
- std::vector<std::unique_ptr<webrtc::FakeVideoTrackRenderer>>
+ std::vector<std::unique_ptr<FakeVideoTrackRenderer>>
removed_fake_video_renderers_;
// For remote peer communication.
@@ -1205,10 +1190,9 @@ class PeerConnectionIntegrationWrapper : public webrtc::PeerConnectionObserver,
// Store references to the video sources we've created, so that we can stop
// them, if required.
- std::vector<rtc::scoped_refptr<webrtc::VideoTrackSource>>
- video_track_sources_;
+ std::vector<rtc::scoped_refptr<VideoTrackSource>> video_track_sources_;
// `local_video_renderer_` attached to the first created local video track.
- std::unique_ptr<webrtc::FakeVideoTrackRenderer> local_video_renderer_;
+ std::unique_ptr<FakeVideoTrackRenderer> local_video_renderer_;
SdpSemantics sdp_semantics_;
PeerConnectionInterface::RTCOfferAnswerOptions offer_answer_options_;
@@ -1238,7 +1222,7 @@ class PeerConnectionIntegrationWrapper : public webrtc::PeerConnectionObserver,
ice_candidate_pair_change_history_;
std::vector<PeerConnectionInterface::SignalingState>
peer_connection_signaling_state_history_;
- webrtc::FakeRtcEventLogFactory* event_log_factory_;
+ FakeRtcEventLogFactory* event_log_factory_;
// Number of ICE candidates expected. The default is no limit.
int candidates_expected_ = std::numeric_limits<int>::max();
@@ -1255,7 +1239,7 @@ class PeerConnectionIntegrationWrapper : public webrtc::PeerConnectionObserver,
friend class PeerConnectionIntegrationBaseTest;
};
-class MockRtcEventLogOutput : public webrtc::RtcEventLogOutput {
+class MockRtcEventLogOutput : public RtcEventLogOutput {
public:
virtual ~MockRtcEventLogOutput() = default;
MOCK_METHOD(bool, IsActive, (), (const, override));
@@ -1367,7 +1351,7 @@ class MediaExpectations {
int callee_video_frames_expected_ = 0;
};
-class MockIceTransport : public webrtc::IceTransportInterface {
+class MockIceTransport : public IceTransportInterface {
public:
MockIceTransport(const std::string& name, int component)
: internal_(std::make_unique<cricket::FakeIceTransport>(
@@ -1415,7 +1399,7 @@ class PeerConnectionIntegrationBaseTest : public ::testing::Test {
worker_thread_->SetName("PCWorkerThread", this);
RTC_CHECK(network_thread_->Start());
RTC_CHECK(worker_thread_->Start());
- webrtc::metrics::Reset();
+ metrics::Reset();
}
~PeerConnectionIntegrationBaseTest() {
@@ -1452,13 +1436,13 @@ class PeerConnectionIntegrationBaseTest : public ::testing::Test {
// are connected. This is an important distinction. Once we have separate
// ICE and DTLS state, this check needs to use the DTLS state.
return (callee()->ice_connection_state() ==
- webrtc::PeerConnectionInterface::kIceConnectionConnected ||
+ PeerConnectionInterface::kIceConnectionConnected ||
callee()->ice_connection_state() ==
- webrtc::PeerConnectionInterface::kIceConnectionCompleted) &&
+ PeerConnectionInterface::kIceConnectionCompleted) &&
(caller()->ice_connection_state() ==
- webrtc::PeerConnectionInterface::kIceConnectionConnected ||
+ PeerConnectionInterface::kIceConnectionConnected ||
caller()->ice_connection_state() ==
- webrtc::PeerConnectionInterface::kIceConnectionCompleted);
+ PeerConnectionInterface::kIceConnectionCompleted);
}
// When `event_log_factory` is null, the default implementation of the event
@@ -1467,8 +1451,8 @@ class PeerConnectionIntegrationBaseTest : public ::testing::Test {
const std::string& debug_name,
const PeerConnectionFactory::Options* options,
const RTCConfiguration* config,
- webrtc::PeerConnectionDependencies dependencies,
- std::unique_ptr<webrtc::FakeRtcEventLogFactory> event_log_factory,
+ PeerConnectionDependencies dependencies,
+ std::unique_ptr<FakeRtcEventLogFactory> event_log_factory,
bool reset_encoder_factory,
bool reset_decoder_factory,
bool create_media_engine = true) {
@@ -1498,10 +1482,10 @@ class PeerConnectionIntegrationBaseTest : public ::testing::Test {
const std::string& debug_name,
const PeerConnectionFactory::Options* options,
const RTCConfiguration* config,
- webrtc::PeerConnectionDependencies dependencies) {
+ PeerConnectionDependencies dependencies) {
return CreatePeerConnectionWrapper(
debug_name, options, config, std::move(dependencies),
- std::make_unique<webrtc::FakeRtcEventLogFactory>(),
+ std::make_unique<FakeRtcEventLogFactory>(),
/*reset_encoder_factory=*/false,
/*reset_decoder_factory=*/false);
}
@@ -1522,17 +1506,17 @@ class PeerConnectionIntegrationBaseTest : public ::testing::Test {
// callee PeerConnections.
SdpSemantics original_semantics = sdp_semantics_;
sdp_semantics_ = caller_semantics;
- caller_ = CreatePeerConnectionWrapper(
- "Caller", nullptr, nullptr, webrtc::PeerConnectionDependencies(nullptr),
- nullptr,
- /*reset_encoder_factory=*/false,
- /*reset_decoder_factory=*/false);
+ caller_ = CreatePeerConnectionWrapper("Caller", nullptr, nullptr,
+ PeerConnectionDependencies(nullptr),
+ nullptr,
+ /*reset_encoder_factory=*/false,
+ /*reset_decoder_factory=*/false);
sdp_semantics_ = callee_semantics;
- callee_ = CreatePeerConnectionWrapper(
- "Callee", nullptr, nullptr, webrtc::PeerConnectionDependencies(nullptr),
- nullptr,
- /*reset_encoder_factory=*/false,
- /*reset_decoder_factory=*/false);
+ callee_ = CreatePeerConnectionWrapper("Callee", nullptr, nullptr,
+ PeerConnectionDependencies(nullptr),
+ nullptr,
+ /*reset_encoder_factory=*/false,
+ /*reset_decoder_factory=*/false);
sdp_semantics_ = original_semantics;
return caller_ && callee_;
}
@@ -1540,24 +1524,24 @@ class PeerConnectionIntegrationBaseTest : public ::testing::Test {
bool CreatePeerConnectionWrappersWithConfig(
const PeerConnectionInterface::RTCConfiguration& caller_config,
const PeerConnectionInterface::RTCConfiguration& callee_config) {
- caller_ = CreatePeerConnectionWrapper(
- "Caller", nullptr, &caller_config,
- webrtc::PeerConnectionDependencies(nullptr), nullptr,
- /*reset_encoder_factory=*/false,
- /*reset_decoder_factory=*/false);
- callee_ = CreatePeerConnectionWrapper(
- "Callee", nullptr, &callee_config,
- webrtc::PeerConnectionDependencies(nullptr), nullptr,
- /*reset_encoder_factory=*/false,
- /*reset_decoder_factory=*/false);
+ caller_ = CreatePeerConnectionWrapper("Caller", nullptr, &caller_config,
+ PeerConnectionDependencies(nullptr),
+ nullptr,
+ /*reset_encoder_factory=*/false,
+ /*reset_decoder_factory=*/false);
+ callee_ = CreatePeerConnectionWrapper("Callee", nullptr, &callee_config,
+ PeerConnectionDependencies(nullptr),
+ nullptr,
+ /*reset_encoder_factory=*/false,
+ /*reset_decoder_factory=*/false);
return caller_ && callee_;
}
bool CreatePeerConnectionWrappersWithConfigAndDeps(
const PeerConnectionInterface::RTCConfiguration& caller_config,
- webrtc::PeerConnectionDependencies caller_dependencies,
+ PeerConnectionDependencies caller_dependencies,
const PeerConnectionInterface::RTCConfiguration& callee_config,
- webrtc::PeerConnectionDependencies callee_dependencies) {
+ PeerConnectionDependencies callee_dependencies) {
caller_ =
CreatePeerConnectionWrapper("Caller", nullptr, &caller_config,
std::move(caller_dependencies), nullptr,
@@ -1574,16 +1558,16 @@ class PeerConnectionIntegrationBaseTest : public ::testing::Test {
bool CreatePeerConnectionWrappersWithOptions(
const PeerConnectionFactory::Options& caller_options,
const PeerConnectionFactory::Options& callee_options) {
- caller_ = CreatePeerConnectionWrapper(
- "Caller", &caller_options, nullptr,
- webrtc::PeerConnectionDependencies(nullptr), nullptr,
- /*reset_encoder_factory=*/false,
- /*reset_decoder_factory=*/false);
- callee_ = CreatePeerConnectionWrapper(
- "Callee", &callee_options, nullptr,
- webrtc::PeerConnectionDependencies(nullptr), nullptr,
- /*reset_encoder_factory=*/false,
- /*reset_decoder_factory=*/false);
+ caller_ = CreatePeerConnectionWrapper("Caller", &caller_options, nullptr,
+ PeerConnectionDependencies(nullptr),
+ nullptr,
+ /*reset_encoder_factory=*/false,
+ /*reset_decoder_factory=*/false);
+ callee_ = CreatePeerConnectionWrapper("Callee", &callee_options, nullptr,
+ PeerConnectionDependencies(nullptr),
+ nullptr,
+ /*reset_encoder_factory=*/false,
+ /*reset_decoder_factory=*/false);
return caller_ && callee_;
}
@@ -1591,10 +1575,10 @@ class PeerConnectionIntegrationBaseTest : public ::testing::Test {
PeerConnectionInterface::RTCConfiguration default_config;
caller_ = CreatePeerConnectionWrapperWithFakeRtcEventLog(
"Caller", nullptr, &default_config,
- webrtc::PeerConnectionDependencies(nullptr));
+ PeerConnectionDependencies(nullptr));
callee_ = CreatePeerConnectionWrapperWithFakeRtcEventLog(
"Callee", nullptr, &default_config,
- webrtc::PeerConnectionDependencies(nullptr));
+ PeerConnectionDependencies(nullptr));
return caller_ && callee_;
}
@@ -1604,7 +1588,7 @@ class PeerConnectionIntegrationBaseTest : public ::testing::Test {
new FakeRTCCertificateGenerator());
cert_generator->use_alternate_key();
- webrtc::PeerConnectionDependencies dependencies(nullptr);
+ PeerConnectionDependencies dependencies(nullptr);
dependencies.cert_generator = std::move(cert_generator);
return CreatePeerConnectionWrapper("New Peer", nullptr, nullptr,
std::move(dependencies), nullptr,
@@ -1614,12 +1598,12 @@ class PeerConnectionIntegrationBaseTest : public ::testing::Test {
bool CreateOneDirectionalPeerConnectionWrappers(bool caller_to_callee) {
caller_ = CreatePeerConnectionWrapper(
- "Caller", nullptr, nullptr, webrtc::PeerConnectionDependencies(nullptr),
+ "Caller", nullptr, nullptr, PeerConnectionDependencies(nullptr),
nullptr,
/*reset_encoder_factory=*/!caller_to_callee,
/*reset_decoder_factory=*/caller_to_callee);
callee_ = CreatePeerConnectionWrapper(
- "Callee", nullptr, nullptr, webrtc::PeerConnectionDependencies(nullptr),
+ "Callee", nullptr, nullptr, PeerConnectionDependencies(nullptr),
nullptr,
/*reset_encoder_factory=*/caller_to_callee,
/*reset_decoder_factory=*/!caller_to_callee);
@@ -1627,18 +1611,18 @@ class PeerConnectionIntegrationBaseTest : public ::testing::Test {
}
bool CreatePeerConnectionWrappersWithoutMediaEngine() {
- caller_ = CreatePeerConnectionWrapper(
- "Caller", nullptr, nullptr, webrtc::PeerConnectionDependencies(nullptr),
- nullptr,
- /*reset_encoder_factory=*/false,
- /*reset_decoder_factory=*/false,
- /*create_media_engine=*/false);
- callee_ = CreatePeerConnectionWrapper(
- "Callee", nullptr, nullptr, webrtc::PeerConnectionDependencies(nullptr),
- nullptr,
- /*reset_encoder_factory=*/false,
- /*reset_decoder_factory=*/false,
- /*create_media_engine=*/false);
+ caller_ = CreatePeerConnectionWrapper("Caller", nullptr, nullptr,
+ PeerConnectionDependencies(nullptr),
+ nullptr,
+ /*reset_encoder_factory=*/false,
+ /*reset_decoder_factory=*/false,
+ /*create_media_engine=*/false);
+ callee_ = CreatePeerConnectionWrapper("Callee", nullptr, nullptr,
+ PeerConnectionDependencies(nullptr),
+ nullptr,
+ /*reset_encoder_factory=*/false,
+ /*reset_decoder_factory=*/false,
+ /*create_media_engine=*/false);
return caller_ && callee_;
}
@@ -1708,7 +1692,7 @@ class PeerConnectionIntegrationBaseTest : public ::testing::Test {
// Messages may get lost on the unreliable DataChannel, so we send multiple
// times to avoid test flakiness.
- void SendRtpDataWithRetries(webrtc::DataChannelInterface* dc,
+ void SendRtpDataWithRetries(DataChannelInterface* dc,
const std::string& data,
int retries) {
for (int i = 0; i < retries; ++i) {
diff --git a/third_party/libwebrtc/pc/test/mock_peer_connection_observers.h b/third_party/libwebrtc/pc/test/mock_peer_connection_observers.h
index e9d97a97f6..6222ef7719 100644
--- a/third_party/libwebrtc/pc/test/mock_peer_connection_observers.h
+++ b/third_party/libwebrtc/pc/test/mock_peer_connection_observers.h
@@ -254,7 +254,7 @@ class MockPeerConnectionObserver : public PeerConnectionObserver {
};
class MockCreateSessionDescriptionObserver
- : public webrtc::CreateSessionDescriptionObserver {
+ : public CreateSessionDescriptionObserver {
public:
MockCreateSessionDescriptionObserver()
: called_(false),
@@ -266,7 +266,7 @@ class MockCreateSessionDescriptionObserver
error_ = "";
desc_.reset(desc);
}
- void OnFailure(webrtc::RTCError error) override {
+ void OnFailure(RTCError error) override {
MutexLock lock(&mutex_);
called_ = true;
error_ = error.message();
@@ -295,8 +295,7 @@ class MockCreateSessionDescriptionObserver
std::unique_ptr<SessionDescriptionInterface> desc_ RTC_GUARDED_BY(mutex_);
};
-class MockSetSessionDescriptionObserver
- : public webrtc::SetSessionDescriptionObserver {
+class MockSetSessionDescriptionObserver : public SetSessionDescriptionObserver {
public:
static rtc::scoped_refptr<MockSetSessionDescriptionObserver> Create() {
return rtc::make_ref_counted<MockSetSessionDescriptionObserver>();
@@ -312,7 +311,7 @@ class MockSetSessionDescriptionObserver
called_ = true;
error_ = "";
}
- void OnFailure(webrtc::RTCError error) override {
+ void OnFailure(RTCError error) override {
MutexLock lock(&mutex_);
called_ = true;
error_ = error.message();
@@ -375,14 +374,14 @@ class FakeSetRemoteDescriptionObserver
absl::optional<RTCError> error_;
};
-class MockDataChannelObserver : public webrtc::DataChannelObserver {
+class MockDataChannelObserver : public DataChannelObserver {
public:
struct Message {
std::string data;
bool binary;
};
- explicit MockDataChannelObserver(webrtc::DataChannelInterface* channel)
+ explicit MockDataChannelObserver(DataChannelInterface* channel)
: channel_(channel) {
channel_->RegisterObserver(this);
states_.push_back(channel_->state());
@@ -419,12 +418,12 @@ class MockDataChannelObserver : public webrtc::DataChannelObserver {
}
private:
- rtc::scoped_refptr<webrtc::DataChannelInterface> channel_;
+ rtc::scoped_refptr<DataChannelInterface> channel_;
std::vector<DataChannelInterface::DataState> states_;
std::vector<Message> messages_;
};
-class MockStatsObserver : public webrtc::StatsObserver {
+class MockStatsObserver : public StatsObserver {
public:
MockStatsObserver() : called_(false), stats_() {}
virtual ~MockStatsObserver() {}
@@ -576,7 +575,7 @@ class MockStatsObserver : public webrtc::StatsObserver {
};
// Helper class that just stores the report from the callback.
-class MockRTCStatsCollectorCallback : public webrtc::RTCStatsCollectorCallback {
+class MockRTCStatsCollectorCallback : public RTCStatsCollectorCallback {
public:
rtc::scoped_refptr<const RTCStatsReport> report() { return report_; }
diff --git a/third_party/libwebrtc/pc/test/rtp_transport_test_util.h b/third_party/libwebrtc/pc/test/rtp_transport_test_util.h
index 593ee002c9..563014f94a 100644
--- a/third_party/libwebrtc/pc/test/rtp_transport_test_util.h
+++ b/third_party/libwebrtc/pc/test/rtp_transport_test_util.h
@@ -33,9 +33,14 @@ class TransportObserver : public RtpPacketSinkInterface {
rtp_transport->SubscribeReadyToSend(
this, [this](bool arg) { OnReadyToSend(arg); });
rtp_transport->SetUnDemuxableRtpPacketReceivedHandler(
- [this](webrtc::RtpPacketReceived& packet) {
- OnUndemuxableRtpPacket(packet);
- });
+ [this](RtpPacketReceived& packet) { OnUndemuxableRtpPacket(packet); });
+ rtp_transport->SubscribeSentPacket(this,
+ [this](const rtc::SentPacket& packet) {
+ sent_packet_count_++;
+ if (action_on_sent_packet_) {
+ action_on_sent_packet_();
+ }
+ });
}
// RtpPacketInterface override.
@@ -57,6 +62,7 @@ class TransportObserver : public RtpPacketSinkInterface {
int rtp_count() const { return rtp_count_; }
int un_demuxable_rtp_count() const { return un_demuxable_rtp_count_; }
int rtcp_count() const { return rtcp_count_; }
+ int sent_packet_count() const { return sent_packet_count_; }
rtc::CopyOnWriteBuffer last_recv_rtp_packet() {
return last_recv_rtp_packet_;
@@ -81,16 +87,21 @@ class TransportObserver : public RtpPacketSinkInterface {
void SetActionOnReadyToSend(absl::AnyInvocable<void(bool)> action) {
action_on_ready_to_send_ = std::move(action);
}
+ void SetActionOnSentPacket(absl::AnyInvocable<void()> action) {
+ action_on_sent_packet_ = std::move(action);
+ }
private:
bool ready_to_send_ = false;
int rtp_count_ = 0;
int un_demuxable_rtp_count_ = 0;
int rtcp_count_ = 0;
+ int sent_packet_count_ = 0;
int ready_to_send_signal_count_ = 0;
rtc::CopyOnWriteBuffer last_recv_rtp_packet_;
rtc::CopyOnWriteBuffer last_recv_rtcp_packet_;
absl::AnyInvocable<void(bool)> action_on_ready_to_send_;
+ absl::AnyInvocable<void()> action_on_sent_packet_;
};
} // namespace webrtc
diff --git a/third_party/libwebrtc/pc/test/svc_e2e_tests.cc b/third_party/libwebrtc/pc/test/svc_e2e_tests.cc
index ae35c7f676..3fde5a44e0 100644
--- a/third_party/libwebrtc/pc/test/svc_e2e_tests.cc
+++ b/third_party/libwebrtc/pc/test/svc_e2e_tests.cc
@@ -160,10 +160,9 @@ std::string SvcTestNameGenerator(
// encoder and decoder level.
class SvcVideoQualityAnalyzer : public DefaultVideoQualityAnalyzer {
public:
- using SpatialTemporalLayerCounts =
- webrtc::flat_map<int, webrtc::flat_map<int, int>>;
+ using SpatialTemporalLayerCounts = flat_map<int, flat_map<int, int>>;
- explicit SvcVideoQualityAnalyzer(webrtc::Clock* clock)
+ explicit SvcVideoQualityAnalyzer(Clock* clock)
: DefaultVideoQualityAnalyzer(clock,
test::GetGlobalMetricsLogger(),
DefaultVideoQualityAnalyzerOptions{
@@ -315,9 +314,9 @@ TEST_P(SvcTest, ScalabilityModeSupported) {
if (UseDependencyDescriptor()) {
trials += "WebRTC-DependencyDescriptorAdvertised/Enabled/";
}
- webrtc::test::ScopedFieldTrials override_trials(AppendFieldTrials(trials));
+ test::ScopedFieldTrials override_trials(AppendFieldTrials(trials));
std::unique_ptr<NetworkEmulationManager> network_emulation_manager =
- CreateNetworkEmulationManager(webrtc::TimeMode::kSimulated);
+ CreateNetworkEmulationManager(TimeMode::kSimulated);
auto analyzer = std::make_unique<SvcVideoQualityAnalyzer>(
network_emulation_manager->time_controller()->GetClock());
SvcVideoQualityAnalyzer* analyzer_ptr = analyzer.get();
@@ -475,16 +474,17 @@ INSTANTIATE_TEST_SUITE_P(
SvcTestParameters::Create(kAv1CodecName, "L2T3h"),
SvcTestParameters::Create(kAv1CodecName, "L2T3_KEY"),
// SvcTestParameters::Create(kAv1CodecName, "L2T3_KEY_SHIFT"),
- SvcTestParameters::Create(kAv1CodecName, "L3T1"),
- SvcTestParameters::Create(kAv1CodecName, "L3T1h"),
- SvcTestParameters::Create(kAv1CodecName, "L3T1_KEY"),
- SvcTestParameters::Create(kAv1CodecName, "L3T2"),
- SvcTestParameters::Create(kAv1CodecName, "L3T2h"),
- SvcTestParameters::Create(kAv1CodecName, "L3T2_KEY"),
+ // TODO(bugs.webrtc.org/15666): Investigate and reenable AV1
+ // L3 tests. SvcTestParameters::Create(kAv1CodecName, "L3T1"),
+ // SvcTestParameters::Create(kAv1CodecName, "L3T1h"),
+ // SvcTestParameters::Create(kAv1CodecName, "L3T1_KEY"),
+ // SvcTestParameters::Create(kAv1CodecName, "L3T2"),
+ // SvcTestParameters::Create(kAv1CodecName, "L3T2h"),
+ // SvcTestParameters::Create(kAv1CodecName, "L3T2_KEY"),
// SvcTestParameters::Create(kAv1CodecName, "L3T2_KEY_SHIFT"),
- SvcTestParameters::Create(kAv1CodecName, "L3T3"),
- SvcTestParameters::Create(kAv1CodecName, "L3T3h"),
- SvcTestParameters::Create(kAv1CodecName, "L3T3_KEY"),
+ // SvcTestParameters::Create(kAv1CodecName, "L3T3"),
+ // SvcTestParameters::Create(kAv1CodecName, "L3T3h"),
+ // SvcTestParameters::Create(kAv1CodecName, "L3T3_KEY"),
// SvcTestParameters::Create(kAv1CodecName, "L3T3_KEY_SHIFT"),
SvcTestParameters::Create(kAv1CodecName, "S2T1"),
SvcTestParameters::Create(kAv1CodecName, "S2T1h"),
@@ -492,12 +492,14 @@ INSTANTIATE_TEST_SUITE_P(
SvcTestParameters::Create(kAv1CodecName, "S2T2h"),
SvcTestParameters::Create(kAv1CodecName, "S2T3"),
SvcTestParameters::Create(kAv1CodecName, "S2T3h"),
- SvcTestParameters::Create(kAv1CodecName, "S3T1"),
- SvcTestParameters::Create(kAv1CodecName, "S3T1h"),
- SvcTestParameters::Create(kAv1CodecName, "S3T2"),
- SvcTestParameters::Create(kAv1CodecName, "S3T2h"),
- SvcTestParameters::Create(kAv1CodecName, "S3T3"),
- SvcTestParameters::Create(kAv1CodecName, "S3T3h"),
+ // TODO(bugs.webrtc.org/15666): Investigate and reenable AV1
+ // S3 tests.
+ // SvcTestParameters::Create(kAv1CodecName, "S3T1"),
+ // SvcTestParameters::Create(kAv1CodecName, "S3T1h"),
+ // SvcTestParameters::Create(kAv1CodecName, "S3T2"),
+ // SvcTestParameters::Create(kAv1CodecName, "S3T2h"),
+ // SvcTestParameters::Create(kAv1CodecName, "S3T3"),
+ // SvcTestParameters::Create(kAv1CodecName, "S3T3h"),
}),
Values(UseDependencyDescriptor::Enabled)),
SvcTestNameGenerator);
diff --git a/third_party/libwebrtc/pc/video_rtp_receiver_unittest.cc b/third_party/libwebrtc/pc/video_rtp_receiver_unittest.cc
index 5ff736084f..e9729170b7 100644
--- a/third_party/libwebrtc/pc/video_rtp_receiver_unittest.cc
+++ b/third_party/libwebrtc/pc/video_rtp_receiver_unittest.cc
@@ -94,7 +94,7 @@ class VideoRtpReceiverTest : public testing::Test {
[&]() { receiver_->SetMediaChannel(media_channel); });
}
- webrtc::VideoTrackSourceInterface* Source() {
+ VideoTrackSourceInterface* Source() {
return receiver_->streams()[0]->FindVideoTrack("receiver")->GetSource();
}
diff --git a/third_party/libwebrtc/pc/video_rtp_track_source_unittest.cc b/third_party/libwebrtc/pc/video_rtp_track_source_unittest.cc
index 13728c7eff..55632cea42 100644
--- a/third_party/libwebrtc/pc/video_rtp_track_source_unittest.cc
+++ b/third_party/libwebrtc/pc/video_rtp_track_source_unittest.cc
@@ -109,11 +109,11 @@ TEST(VideoRtpTrackSourceTest, NoCallbacksAfterClearedCallback) {
class TestFrame : public RecordableEncodedFrame {
public:
- rtc::scoped_refptr<const webrtc::EncodedImageBufferInterface> encoded_buffer()
+ rtc::scoped_refptr<const EncodedImageBufferInterface> encoded_buffer()
const override {
return nullptr;
}
- absl::optional<webrtc::ColorSpace> color_space() const override {
+ absl::optional<ColorSpace> color_space() const override {
return absl::nullopt;
}
VideoCodecType codec() const override { return kVideoCodecGeneric; }
diff --git a/third_party/libwebrtc/pc/video_track.h b/third_party/libwebrtc/pc/video_track.h
index 13a51c454b..e504182c82 100644
--- a/third_party/libwebrtc/pc/video_track.h
+++ b/third_party/libwebrtc/pc/video_track.h
@@ -70,7 +70,7 @@ class VideoTrack : public MediaStreamTrack<VideoTrackInterface>,
// Implements ObserverInterface. Observes `video_source_` state.
void OnChanged() override;
- RTC_NO_UNIQUE_ADDRESS webrtc::SequenceChecker signaling_thread_;
+ RTC_NO_UNIQUE_ADDRESS SequenceChecker signaling_thread_;
rtc::Thread* const worker_thread_;
const rtc::scoped_refptr<
VideoTrackSourceProxyWithInternal<VideoTrackSourceInterface>>
diff --git a/third_party/libwebrtc/pc/video_track_source_proxy.h b/third_party/libwebrtc/pc/video_track_source_proxy.h
index 8500a98766..40d24234ad 100644
--- a/third_party/libwebrtc/pc/video_track_source_proxy.h
+++ b/third_party/libwebrtc/pc/video_track_source_proxy.h
@@ -52,7 +52,7 @@ PROXY_SECONDARY_METHOD1(void,
rtc::VideoSinkInterface<RecordableEncodedFrame>*)
PROXY_SECONDARY_METHOD1(void,
ProcessConstraints,
- const webrtc::VideoTrackSourceConstraints&)
+ const VideoTrackSourceConstraints&)
END_PROXY_MAP(VideoTrackSource)
} // namespace webrtc
diff --git a/third_party/libwebrtc/pc/webrtc_sdp.cc b/third_party/libwebrtc/pc/webrtc_sdp.cc
index 2a3173cb02..da024eab81 100644
--- a/third_party/libwebrtc/pc/webrtc_sdp.cc
+++ b/third_party/libwebrtc/pc/webrtc_sdp.cc
@@ -267,11 +267,9 @@ struct SsrcInfo {
std::string stream_id;
std::string track_id;
};
-typedef std::vector<SsrcInfo> SsrcInfoVec;
-typedef std::vector<SsrcGroup> SsrcGroupVec;
+using SsrcInfoVec = std::vector<SsrcInfo>;
+using SsrcGroupVec = std::vector<SsrcGroup>;
-template <class T>
-static void AddFmtpLine(const T& codec, std::string* message);
static void BuildMediaDescription(const ContentInfo* content_info,
const TransportInfo* transport_info,
const cricket::MediaType media_type,
@@ -2628,7 +2626,7 @@ static std::unique_ptr<MediaContentDescription> ParseContentDescription(
int* msid_signaling,
TransportDescription* transport,
std::vector<std::unique_ptr<JsepIceCandidate>>* candidates,
- webrtc::SdpParseError* error) {
+ SdpParseError* error) {
std::unique_ptr<MediaContentDescription> media_desc;
if (media_type == cricket::MediaType::MEDIA_TYPE_AUDIO) {
media_desc = std::make_unique<AudioContentDescription>();
diff --git a/third_party/libwebrtc/pc/webrtc_sdp_unittest.cc b/third_party/libwebrtc/pc/webrtc_sdp_unittest.cc
index 2c43c35d15..ae26ba0ce2 100644
--- a/third_party/libwebrtc/pc/webrtc_sdp_unittest.cc
+++ b/third_party/libwebrtc/pc/webrtc_sdp_unittest.cc
@@ -1316,8 +1316,9 @@ class WebRtcSdpTest : public ::testing::Test {
return video;
}
- template <class MCD>
- void CompareMediaContentDescription(const MCD* cd1, const MCD* cd2) {
+ void CompareMediaContentDescription(
+ const cricket::MediaContentDescription* cd1,
+ const cricket::MediaContentDescription* cd2) {
// type
EXPECT_EQ(cd1->type(), cd2->type());
@@ -1427,20 +1428,14 @@ class WebRtcSdpTest : public ::testing::Test {
ASSERT_EQ(IsAudioContent(&c1), IsAudioContent(&c2));
if (IsAudioContent(&c1)) {
- const AudioContentDescription* acd1 =
- c1.media_description()->as_audio();
- const AudioContentDescription* acd2 =
- c2.media_description()->as_audio();
- CompareMediaContentDescription<AudioContentDescription>(acd1, acd2);
+ CompareMediaContentDescription(c1.media_description(),
+ c2.media_description());
}
ASSERT_EQ(IsVideoContent(&c1), IsVideoContent(&c2));
if (IsVideoContent(&c1)) {
- const VideoContentDescription* vcd1 =
- c1.media_description()->as_video();
- const VideoContentDescription* vcd2 =
- c2.media_description()->as_video();
- CompareMediaContentDescription<VideoContentDescription>(vcd1, vcd2);
+ CompareMediaContentDescription(c1.media_description(),
+ c2.media_description());
}
ASSERT_EQ(IsDataContent(&c1), IsDataContent(&c2));
@@ -5015,7 +5010,7 @@ TEST_F(WebRtcSdpTest, ParseSessionLevelExtmapAttributes) {
EXPECT_TRUE(SdpDeserialize(sdp, &jdesc));
ASSERT_EQ(1u, jdesc.description()->contents().size());
const auto content = jdesc.description()->contents()[0];
- const auto* audio_description = content.media_description()->as_audio();
+ const auto* audio_description = content.media_description();
ASSERT_NE(audio_description, nullptr);
const auto& extensions = audio_description->rtp_header_extensions();
ASSERT_EQ(1u, extensions.size());
diff --git a/third_party/libwebrtc/rtc_base/BUILD.gn b/third_party/libwebrtc/rtc_base/BUILD.gn
index 3d57e3bfb4..5392e5f472 100644
--- a/third_party/libwebrtc/rtc_base/BUILD.gn
+++ b/third_party/libwebrtc/rtc_base/BUILD.gn
@@ -156,8 +156,12 @@ rtc_library("byte_buffer") {
deps = [
":buffer",
":byte_order",
+ "../api:array_view",
+ ]
+ absl_deps = [
+ "//third_party/abseil-cpp/absl/base:core_headers",
+ "//third_party/abseil-cpp/absl/strings",
]
- absl_deps = [ "//third_party/abseil-cpp/absl/strings" ]
}
rtc_library("buffer_queue") {
@@ -371,6 +375,7 @@ rtc_source_set("refcount") {
]
deps = [
":macromagic",
+ "../api:ref_count",
"../api:scoped_refptr",
]
}
@@ -613,6 +618,7 @@ rtc_library("stringutils") {
"string_to_number.h",
"string_utils.cc",
"string_utils.h",
+ "strings/str_join.h",
"strings/string_builder.cc",
"strings/string_builder.h",
"strings/string_format.cc",
@@ -925,20 +931,6 @@ rtc_library("net_test_helpers") {
}
}
-rtc_library("async_resolver_interface") {
- visibility = [ "*" ]
- sources = [
- "async_resolver_interface.cc",
- "async_resolver_interface.h",
- ]
- deps = [
- ":checks",
- ":socket_address",
- "system:rtc_export",
- "third_party/sigslot",
- ]
-}
-
rtc_library("async_dns_resolver") {
sources = [
"async_dns_resolver.cc",
@@ -1037,8 +1029,6 @@ rtc_library("threading") {
visibility = [ "*" ]
sources = [
- "async_resolver.cc",
- "async_resolver.h",
"internal/default_socket_server.cc",
"internal/default_socket_server.h",
"network_monitor.cc",
@@ -1060,7 +1050,6 @@ rtc_library("threading") {
]
deps = [
":async_dns_resolver",
- ":async_resolver_interface",
":byte_order",
":checks",
":criticalsection",
@@ -1395,10 +1384,12 @@ if (!build_with_mozilla) {
]
deps = [
":callback_list",
+ ":checks",
":dscp",
":socket",
":timeutils",
"../api:sequence_checker",
+ "network:received_packet",
"network:sent_packet",
"system:no_unique_address",
"system:rtc_export",
@@ -1407,6 +1398,21 @@ if (!build_with_mozilla) {
}
}
+if (rtc_include_tests) {
+ rtc_library("async_packet_socket_unittest") {
+ testonly = true
+ visibility = [ "*" ]
+ sources = [ "async_packet_socket_unittest.cc" ]
+ deps = [
+ ":async_packet_socket",
+ ":gunit_helpers",
+ "../test:test_support",
+ "network:received_packet",
+ "third_party/sigslot",
+ ]
+ }
+}
+
rtc_library("mdns_responder_interface") {
sources = [ "mdns_responder_interface.h" ]
deps = [ ":ip_address" ]
@@ -1680,10 +1686,13 @@ rtc_library("testclient") {
]
deps = [
":async_udp_socket",
+ ":buffer",
":gunit_helpers",
":rtc_base_tests_utils",
":threading",
":timeutils",
+ "../api/units:timestamp",
+ "network:received_packet",
"synchronization:mutex",
]
}
@@ -1766,12 +1775,14 @@ rtc_library("rtc_base_tests_utils") {
"../api:make_ref_counted",
"../api:refcountedbase",
"../api:scoped_refptr",
+ "../api:sequence_checker",
"../api/task_queue",
"../api/units:time_delta",
"../api/units:timestamp",
"../test:scoped_key_value_config",
"memory:always_valid_pointer",
"memory:fifo_buffer",
+ "network:received_packet",
"synchronization:mutex",
"third_party/sigslot",
]
@@ -1929,6 +1940,7 @@ if (rtc_include_tests) {
"string_encode_unittest.cc",
"string_to_number_unittest.cc",
"string_utils_unittest.cc",
+ "strings/str_join_unittest.cc",
"strings/string_builder_unittest.cc",
"strings/string_format_unittest.cc",
"strong_alias_unittest.cc",
@@ -1993,6 +2005,7 @@ if (rtc_include_tests) {
":zero_memory",
"../api:array_view",
"../api:make_ref_counted",
+ "../api:ref_count",
"../api:scoped_refptr",
"../api/numerics",
"../api/units:data_rate",
diff --git a/third_party/libwebrtc/rtc_base/async_dns_resolver_gn/moz.build b/third_party/libwebrtc/rtc_base/async_dns_resolver_gn/moz.build
index 866c7aec5a..c278a28f8f 100644
--- a/third_party/libwebrtc/rtc_base/async_dns_resolver_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/async_dns_resolver_gn/moz.build
@@ -196,7 +196,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -206,10 +205,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/async_packet_socket.cc b/third_party/libwebrtc/rtc_base/async_packet_socket.cc
index f50138cb62..3721366099 100644
--- a/third_party/libwebrtc/rtc_base/async_packet_socket.cc
+++ b/third_party/libwebrtc/rtc_base/async_packet_socket.cc
@@ -10,6 +10,8 @@
#include "rtc_base/async_packet_socket.h"
+#include "rtc_base/checks.h"
+
namespace rtc {
PacketTimeUpdateParams::PacketTimeUpdateParams() = default;
@@ -38,6 +40,41 @@ void AsyncPacketSocket::UnsubscribeCloseEvent(const void* removal_tag) {
on_close_.RemoveReceivers(removal_tag);
}
+void AsyncPacketSocket::RegisterReceivedPacketCallback(
+ absl::AnyInvocable<void(AsyncPacketSocket*, const rtc::ReceivedPacket&)>
+ received_packet_callback) {
+ RTC_DCHECK_RUN_ON(&network_checker_);
+ RTC_CHECK(!received_packet_callback_);
+ SignalReadPacket.connect(this, &AsyncPacketSocket::NotifyPacketReceived);
+ received_packet_callback_ = std::move(received_packet_callback);
+}
+
+void AsyncPacketSocket::DeregisterReceivedPacketCallback() {
+ RTC_DCHECK_RUN_ON(&network_checker_);
+ SignalReadPacket.disconnect(this);
+ received_packet_callback_ = nullptr;
+}
+
+void AsyncPacketSocket::NotifyPacketReceived(
+ const rtc::ReceivedPacket& packet) {
+ RTC_DCHECK_RUN_ON(&network_checker_);
+ if (received_packet_callback_) {
+ received_packet_callback_(this, packet);
+ return;
+ }
+ if (SignalReadPacket.is_empty()) {
+ RTC_DCHECK_NOTREACHED() << " No listener registered";
+ return;
+ }
+ // TODO(bugs.webrtc.org:15368): Remove. This code path is only used if
+ // SignalReadyPacket is used by clients to get notification of received
+ // packets but actual socket implementation use NotifyPacketReceived to
+ // trigger the notification.
+ SignalReadPacket(this, reinterpret_cast<const char*>(packet.payload().data()),
+ packet.payload().size(), packet.source_address(),
+ packet.arrival_time() ? packet.arrival_time()->us() : -1);
+}
+
void CopySocketInformationToPacketInfo(size_t packet_size_bytes,
const AsyncPacketSocket& socket_from,
bool is_connectionless,
diff --git a/third_party/libwebrtc/rtc_base/async_packet_socket.h b/third_party/libwebrtc/rtc_base/async_packet_socket.h
index 0d3ceb94e7..768fcd446b 100644
--- a/third_party/libwebrtc/rtc_base/async_packet_socket.h
+++ b/third_party/libwebrtc/rtc_base/async_packet_socket.h
@@ -11,11 +11,13 @@
#ifndef RTC_BASE_ASYNC_PACKET_SOCKET_H_
#define RTC_BASE_ASYNC_PACKET_SOCKET_H_
+#include <cstdint>
#include <vector>
#include "api/sequence_checker.h"
#include "rtc_base/callback_list.h"
#include "rtc_base/dscp.h"
+#include "rtc_base/network/received_packet.h"
#include "rtc_base/network/sent_packet.h"
#include "rtc_base/socket.h"
#include "rtc_base/system/no_unique_address.h"
@@ -115,8 +117,14 @@ class RTC_EXPORT AsyncPacketSocket : public sigslot::has_slots<> {
std::function<void(AsyncPacketSocket*, int)> callback);
void UnsubscribeCloseEvent(const void* removal_tag);
+ void RegisterReceivedPacketCallback(
+ absl::AnyInvocable<void(AsyncPacketSocket*, const rtc::ReceivedPacket&)>
+ received_packet_callback);
+ void DeregisterReceivedPacketCallback();
+
// Emitted each time a packet is read. Used only for UDP and
// connected TCP sockets.
+ // TODO(bugs.webrtc.org:15368): Deprecate and remove.
sigslot::signal5<AsyncPacketSocket*,
const char*,
size_t,
@@ -155,12 +163,26 @@ class RTC_EXPORT AsyncPacketSocket : public sigslot::has_slots<> {
on_close_.Send(this, err);
}
+ // TODO(bugs.webrtc.org:15368): Deprecate and remove.
+ void NotifyPacketReceived(AsyncPacketSocket*,
+ const char* data,
+ size_t size,
+ const SocketAddress& address,
+ const int64_t& packet_time_us) {
+ NotifyPacketReceived(
+ ReceivedPacket::CreateFromLegacy(data, size, packet_time_us, address));
+ }
+
+ void NotifyPacketReceived(const rtc::ReceivedPacket& packet);
+
RTC_NO_UNIQUE_ADDRESS webrtc::SequenceChecker network_checker_{
webrtc::SequenceChecker::kDetached};
private:
webrtc::CallbackList<AsyncPacketSocket*, int> on_close_
RTC_GUARDED_BY(&network_checker_);
+ absl::AnyInvocable<void(AsyncPacketSocket*, const rtc::ReceivedPacket&)>
+ received_packet_callback_ RTC_GUARDED_BY(&network_checker_);
};
// Listen socket, producing an AsyncPacketSocket when a peer connects.
diff --git a/third_party/libwebrtc/rtc_base/async_packet_socket_gn/moz.build b/third_party/libwebrtc/rtc_base/async_packet_socket_gn/moz.build
index 0038aa7ef8..40c8366f97 100644
--- a/third_party/libwebrtc/rtc_base/async_packet_socket_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/async_packet_socket_gn/moz.build
@@ -176,16 +176,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/async_packet_socket_unittest.cc b/third_party/libwebrtc/rtc_base/async_packet_socket_unittest.cc
new file mode 100644
index 0000000000..6cd4f09459
--- /dev/null
+++ b/third_party/libwebrtc/rtc_base/async_packet_socket_unittest.cc
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2023 The WebRTC Project Authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "rtc_base/async_packet_socket.h"
+
+#include "rtc_base/third_party/sigslot/sigslot.h"
+#include "test/gmock.h"
+#include "test/gtest.h"
+
+namespace rtc {
+namespace {
+
+using ::testing::MockFunction;
+
+class MockAsyncPacketSocket : public rtc::AsyncPacketSocket {
+ public:
+ ~MockAsyncPacketSocket() = default;
+
+ MOCK_METHOD(SocketAddress, GetLocalAddress, (), (const, override));
+ MOCK_METHOD(SocketAddress, GetRemoteAddress, (), (const, override));
+ MOCK_METHOD(int,
+ Send,
+ (const void* pv, size_t cb, const rtc::PacketOptions& options),
+ (override));
+
+ MOCK_METHOD(int,
+ SendTo,
+ (const void* pv,
+ size_t cb,
+ const SocketAddress& addr,
+ const rtc::PacketOptions& options),
+ (override));
+ MOCK_METHOD(int, Close, (), (override));
+ MOCK_METHOD(State, GetState, (), (const, override));
+ MOCK_METHOD(int,
+ GetOption,
+ (rtc::Socket::Option opt, int* value),
+ (override));
+ MOCK_METHOD(int, SetOption, (rtc::Socket::Option opt, int value), (override));
+ MOCK_METHOD(int, GetError, (), (const, override));
+ MOCK_METHOD(void, SetError, (int error), (override));
+
+ void NotifyPacketReceived() {
+ char data[1] = {'a'};
+ AsyncPacketSocket::NotifyPacketReceived(this, data, 1, SocketAddress(), -1);
+ }
+};
+
+TEST(AsyncPacketSocket, RegisteredCallbackReceivePacketsFromNotify) {
+ MockAsyncPacketSocket mock_socket;
+ MockFunction<void(AsyncPacketSocket*, const rtc::ReceivedPacket&)>
+ received_packet;
+
+ EXPECT_CALL(received_packet, Call);
+ mock_socket.RegisterReceivedPacketCallback(received_packet.AsStdFunction());
+ mock_socket.NotifyPacketReceived();
+}
+
+TEST(AsyncPacketSocket, RegisteredCallbackReceivePacketsFromSignalReadPacket) {
+ MockAsyncPacketSocket mock_socket;
+ MockFunction<void(AsyncPacketSocket*, const rtc::ReceivedPacket&)>
+ received_packet;
+
+ EXPECT_CALL(received_packet, Call);
+ mock_socket.RegisterReceivedPacketCallback(received_packet.AsStdFunction());
+ char data[1] = {'a'};
+ mock_socket.SignalReadPacket(&mock_socket, data, 1, SocketAddress(), -1);
+}
+
+TEST(AsyncPacketSocket, SignalReadPacketTriggeredByNotifyPacketReceived) {
+ class SigslotPacketReceiver : public sigslot::has_slots<> {
+ public:
+ explicit SigslotPacketReceiver(rtc::AsyncPacketSocket& socket) {
+ socket.SignalReadPacket.connect(this,
+ &SigslotPacketReceiver::OnPacketReceived);
+ }
+
+ bool packet_received() const { return packet_received_; }
+
+ private:
+ void OnPacketReceived(AsyncPacketSocket*,
+ const char*,
+ size_t,
+ const SocketAddress&,
+ // TODO(bugs.webrtc.org/9584): Change to passing the
+ // int64_t timestamp by value.
+ const int64_t&) {
+ packet_received_ = true;
+ }
+
+ bool packet_received_ = false;
+ };
+
+ MockAsyncPacketSocket mock_socket;
+ SigslotPacketReceiver receiver(mock_socket);
+ ASSERT_FALSE(receiver.packet_received());
+
+ mock_socket.NotifyPacketReceived();
+ EXPECT_TRUE(receiver.packet_received());
+}
+
+} // namespace
+} // namespace rtc
diff --git a/third_party/libwebrtc/rtc_base/async_resolver.cc b/third_party/libwebrtc/rtc_base/async_resolver.cc
deleted file mode 100644
index f79200bc05..0000000000
--- a/third_party/libwebrtc/rtc_base/async_resolver.cc
+++ /dev/null
@@ -1,239 +0,0 @@
-/*
- * Copyright 2008 The WebRTC Project Authors. All rights reserved.
- *
- * Use of this source code is governed by a BSD-style license
- * that can be found in the LICENSE file in the root of the source
- * tree. An additional intellectual property rights grant can be found
- * in the file PATENTS. All contributing project authors may
- * be found in the AUTHORS file in the root of the source tree.
- */
-
-#include "rtc_base/async_resolver.h"
-
-#include <memory>
-#include <string>
-#include <utility>
-
-#include "absl/strings/string_view.h"
-#include "api/ref_counted_base.h"
-#include "rtc_base/synchronization/mutex.h"
-#include "rtc_base/thread_annotations.h"
-
-#if defined(WEBRTC_WIN)
-#include <ws2spi.h>
-#include <ws2tcpip.h>
-
-#include "rtc_base/win32.h"
-#endif
-#if defined(WEBRTC_POSIX) && !defined(__native_client__)
-#if defined(WEBRTC_ANDROID)
-#include "rtc_base/ifaddrs_android.h"
-#else
-#include <ifaddrs.h>
-#endif
-#endif // defined(WEBRTC_POSIX) && !defined(__native_client__)
-
-#include "api/task_queue/task_queue_base.h"
-#include "rtc_base/ip_address.h"
-#include "rtc_base/logging.h"
-#include "rtc_base/platform_thread.h"
-#include "rtc_base/task_queue.h"
-#include "rtc_base/third_party/sigslot/sigslot.h" // for signal_with_thread...
-
-#if defined(WEBRTC_MAC) || defined(WEBRTC_IOS)
-#include <dispatch/dispatch.h>
-#endif
-
-namespace rtc {
-
-#if defined(WEBRTC_MAC) || defined(WEBRTC_IOS)
-namespace {
-
-void GlobalGcdRunTask(void* context) {
- std::unique_ptr<absl::AnyInvocable<void() &&>> task(
- static_cast<absl::AnyInvocable<void() &&>*>(context));
- std::move (*task)();
-}
-
-// Post a task into the system-defined global concurrent queue.
-void PostTaskToGlobalQueue(
- std::unique_ptr<absl::AnyInvocable<void() &&>> task) {
- dispatch_async_f(
- dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
- task.release(), &GlobalGcdRunTask);
-}
-
-} // namespace
-#endif
-
-int ResolveHostname(absl::string_view hostname,
- int family,
- std::vector<IPAddress>* addresses) {
-#ifdef __native_client__
- RTC_DCHECK_NOTREACHED();
- RTC_LOG(LS_WARNING) << "ResolveHostname() is not implemented for NaCl";
- return -1;
-#else // __native_client__
- if (!addresses) {
- return -1;
- }
- addresses->clear();
- struct addrinfo* result = nullptr;
- struct addrinfo hints = {0};
- hints.ai_family = family;
- // `family` here will almost always be AF_UNSPEC, because `family` comes from
- // AsyncResolver::addr_.family(), which comes from a SocketAddress constructed
- // with a hostname. When a SocketAddress is constructed with a hostname, its
- // family is AF_UNSPEC. However, if someday in the future we construct
- // a SocketAddress with both a hostname and a family other than AF_UNSPEC,
- // then it would be possible to get a specific family value here.
-
- // The behavior of AF_UNSPEC is roughly "get both ipv4 and ipv6", as
- // documented by the various operating systems:
- // Linux: http://man7.org/linux/man-pages/man3/getaddrinfo.3.html
- // Windows: https://msdn.microsoft.com/en-us/library/windows/desktop/
- // ms738520(v=vs.85).aspx
- // Mac: https://developer.apple.com/legacy/library/documentation/Darwin/
- // Reference/ManPages/man3/getaddrinfo.3.html
- // Android (source code, not documentation):
- // https://android.googlesource.com/platform/bionic/+/
- // 7e0bfb511e85834d7c6cb9631206b62f82701d60/libc/netbsd/net/getaddrinfo.c#1657
- hints.ai_flags = AI_ADDRCONFIG;
- int ret =
- getaddrinfo(std::string(hostname).c_str(), nullptr, &hints, &result);
- if (ret != 0) {
- return ret;
- }
- struct addrinfo* cursor = result;
- for (; cursor; cursor = cursor->ai_next) {
- if (family == AF_UNSPEC || cursor->ai_family == family) {
- IPAddress ip;
- if (IPFromAddrInfo(cursor, &ip)) {
- addresses->push_back(ip);
- }
- }
- }
- freeaddrinfo(result);
- return 0;
-#endif // !__native_client__
-}
-
-struct AsyncResolver::State : public RefCountedBase {
- webrtc::Mutex mutex;
- enum class Status {
- kLive,
- kDead
- } status RTC_GUARDED_BY(mutex) = Status::kLive;
-};
-
-AsyncResolver::AsyncResolver() : error_(-1), state_(new State) {}
-
-AsyncResolver::~AsyncResolver() {
- RTC_DCHECK_RUN_ON(&sequence_checker_);
-
- // Ensure the thread isn't using a stale reference to the current task queue,
- // or calling into ResolveDone post destruction.
- webrtc::MutexLock lock(&state_->mutex);
- state_->status = State::Status::kDead;
-}
-
-void RunResolution(void* obj) {
- std::function<void()>* function_ptr =
- static_cast<std::function<void()>*>(obj);
- (*function_ptr)();
- delete function_ptr;
-}
-
-void AsyncResolver::Start(const SocketAddress& addr) {
- Start(addr, addr.family());
-}
-
-void AsyncResolver::Start(const SocketAddress& addr, int family) {
- RTC_DCHECK_RUN_ON(&sequence_checker_);
- RTC_DCHECK(!destroy_called_);
- addr_ = addr;
- auto thread_function = [this, addr, family,
- caller_task_queue = webrtc::TaskQueueBase::Current(),
- state = state_] {
- std::vector<IPAddress> addresses;
- int error = ResolveHostname(addr.hostname(), family, &addresses);
- webrtc::MutexLock lock(&state->mutex);
- if (state->status == State::Status::kLive) {
- caller_task_queue->PostTask(
- [this, error, addresses = std::move(addresses), state] {
- bool live;
- {
- // ResolveDone can lead to instance destruction, so make sure
- // we don't deadlock.
- webrtc::MutexLock lock(&state->mutex);
- live = state->status == State::Status::kLive;
- }
- if (live) {
- RTC_DCHECK_RUN_ON(&sequence_checker_);
- ResolveDone(std::move(addresses), error);
- }
- });
- }
- };
-#if defined(WEBRTC_MAC) || defined(WEBRTC_IOS)
- PostTaskToGlobalQueue(
- std::make_unique<absl::AnyInvocable<void() &&>>(thread_function));
-#else
- PlatformThread::SpawnDetached(std::move(thread_function), "AsyncResolver");
-#endif
-}
-
-bool AsyncResolver::GetResolvedAddress(int family, SocketAddress* addr) const {
- RTC_DCHECK_RUN_ON(&sequence_checker_);
- RTC_DCHECK(!destroy_called_);
- if (error_ != 0 || addresses_.empty())
- return false;
-
- *addr = addr_;
- for (size_t i = 0; i < addresses_.size(); ++i) {
- if (family == addresses_[i].family()) {
- addr->SetResolvedIP(addresses_[i]);
- return true;
- }
- }
- return false;
-}
-
-int AsyncResolver::GetError() const {
- RTC_DCHECK_RUN_ON(&sequence_checker_);
- RTC_DCHECK(!destroy_called_);
- return error_;
-}
-
-void AsyncResolver::Destroy(bool wait) {
- // Some callers have trouble guaranteeing that Destroy is called on the
- // sequence guarded by `sequence_checker_`.
- // RTC_DCHECK_RUN_ON(&sequence_checker_);
- RTC_DCHECK(!destroy_called_);
- destroy_called_ = true;
- MaybeSelfDestruct();
-}
-
-const std::vector<IPAddress>& AsyncResolver::addresses() const {
- RTC_DCHECK_RUN_ON(&sequence_checker_);
- RTC_DCHECK(!destroy_called_);
- return addresses_;
-}
-
-void AsyncResolver::ResolveDone(std::vector<IPAddress> addresses, int error) {
- addresses_ = addresses;
- error_ = error;
- recursion_check_ = true;
- SignalDone(this);
- MaybeSelfDestruct();
-}
-
-void AsyncResolver::MaybeSelfDestruct() {
- if (!recursion_check_) {
- delete this;
- } else {
- recursion_check_ = false;
- }
-}
-
-} // namespace rtc
diff --git a/third_party/libwebrtc/rtc_base/async_resolver.h b/third_party/libwebrtc/rtc_base/async_resolver.h
deleted file mode 100644
index 9de4d12fed..0000000000
--- a/third_party/libwebrtc/rtc_base/async_resolver.h
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright 2008 The WebRTC Project Authors. All rights reserved.
- *
- * Use of this source code is governed by a BSD-style license
- * that can be found in the LICENSE file in the root of the source
- * tree. An additional intellectual property rights grant can be found
- * in the file PATENTS. All contributing project authors may
- * be found in the AUTHORS file in the root of the source tree.
- */
-
-#ifndef RTC_BASE_ASYNC_RESOLVER_H_
-#define RTC_BASE_ASYNC_RESOLVER_H_
-
-#if defined(WEBRTC_POSIX)
-#include <sys/socket.h>
-#elif WEBRTC_WIN
-#include <winsock2.h> // NOLINT
-#endif
-
-#include <vector>
-
-#include "api/sequence_checker.h"
-#include "api/task_queue/pending_task_safety_flag.h"
-#include "rtc_base/async_resolver_interface.h"
-#include "rtc_base/event.h"
-#include "rtc_base/ip_address.h"
-#include "rtc_base/socket_address.h"
-#include "rtc_base/system/no_unique_address.h"
-#include "rtc_base/system/rtc_export.h"
-#include "rtc_base/thread.h"
-#include "rtc_base/thread_annotations.h"
-
-namespace rtc {
-
-// AsyncResolver will perform async DNS resolution, signaling the result on
-// the SignalDone from AsyncResolverInterface when the operation completes.
-//
-// This class is thread-compatible, and all methods and destruction needs to
-// happen from the same rtc::Thread, except for Destroy which is allowed to
-// happen on another context provided it's not happening concurrently to another
-// public API call, and is the last access to the object.
-// TODO(bugs.webrtc.org/12598): Deprecate and remove
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wstrict-aliasing"
-class [[deprecated("Use AsyncDnsResolver")]] RTC_EXPORT AsyncResolver
- : public AsyncResolverInterface {
-#pragma clang diagnostic pop
- public:
- AsyncResolver();
- ~AsyncResolver() override;
-
- void Start(const SocketAddress& addr) override;
- void Start(const SocketAddress& addr, int family) override;
- bool GetResolvedAddress(int family, SocketAddress* addr) const override;
- int GetError() const override;
- void Destroy(bool wait) override;
-
- const std::vector<IPAddress>& addresses() const;
-
- private:
- // Fwd decl.
- struct State;
-
- void ResolveDone(std::vector<IPAddress> addresses, int error)
- RTC_EXCLUSIVE_LOCKS_REQUIRED(sequence_checker_);
- void MaybeSelfDestruct();
-
- SocketAddress addr_ RTC_GUARDED_BY(sequence_checker_);
- std::vector<IPAddress> addresses_ RTC_GUARDED_BY(sequence_checker_);
- int error_ RTC_GUARDED_BY(sequence_checker_);
- bool recursion_check_ =
- false; // Protects against SignalDone calling into Destroy.
- bool destroy_called_ = false;
- scoped_refptr<State> state_;
- RTC_NO_UNIQUE_ADDRESS webrtc::SequenceChecker sequence_checker_;
-};
-
-} // namespace rtc
-
-#endif // RTC_BASE_ASYNC_RESOLVER_H_
diff --git a/third_party/libwebrtc/rtc_base/async_resolver_interface.cc b/third_party/libwebrtc/rtc_base/async_resolver_interface.cc
deleted file mode 100644
index ff8c87e346..0000000000
--- a/third_party/libwebrtc/rtc_base/async_resolver_interface.cc
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright 2015 The WebRTC Project Authors. All rights reserved.
- *
- * Use of this source code is governed by a BSD-style license
- * that can be found in the LICENSE file in the root of the source
- * tree. An additional intellectual property rights grant can be found
- * in the file PATENTS. All contributing project authors may
- * be found in the AUTHORS file in the root of the source tree.
- */
-
-#include "rtc_base/async_resolver_interface.h"
-
-namespace rtc {
-
-AsyncResolverInterface::AsyncResolverInterface() {}
-
-AsyncResolverInterface::~AsyncResolverInterface() = default;
-
-} // namespace rtc
diff --git a/third_party/libwebrtc/rtc_base/async_resolver_interface.h b/third_party/libwebrtc/rtc_base/async_resolver_interface.h
deleted file mode 100644
index a0bda2774a..0000000000
--- a/third_party/libwebrtc/rtc_base/async_resolver_interface.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright 2013 The WebRTC Project Authors. All rights reserved.
- *
- * Use of this source code is governed by a BSD-style license
- * that can be found in the LICENSE file in the root of the source
- * tree. An additional intellectual property rights grant can be found
- * in the file PATENTS. All contributing project authors may
- * be found in the AUTHORS file in the root of the source tree.
- */
-
-#ifndef RTC_BASE_ASYNC_RESOLVER_INTERFACE_H_
-#define RTC_BASE_ASYNC_RESOLVER_INTERFACE_H_
-
-#include "rtc_base/checks.h"
-#include "rtc_base/socket_address.h"
-#include "rtc_base/system/rtc_export.h"
-#include "rtc_base/third_party/sigslot/sigslot.h"
-
-namespace rtc {
-
-// This interface defines the methods to resolve the address asynchronously.
-// TODO(bugs.webrtc.org/12598): Deprecate and remove.
-class [[deprecated("Use AsyncDnsResolver")]] RTC_EXPORT AsyncResolverInterface {
- public:
- AsyncResolverInterface();
- virtual ~AsyncResolverInterface();
-
- // Start address resolution of the hostname in `addr`.
- virtual void Start(const SocketAddress& addr) = 0;
- // Start address resolution of the hostname in `addr` matching `family`.
- virtual void Start(const SocketAddress& addr, int family) = 0;
- // Returns true iff the address from `Start` was successfully resolved.
- // If the address was successfully resolved, sets `addr` to a copy of the
- // address from `Start` with the IP address set to the top most resolved
- // address of `family` (`addr` will have both hostname and the resolved ip).
- virtual bool GetResolvedAddress(int family, SocketAddress* addr) const = 0;
- // Returns error from resolver.
- virtual int GetError() const = 0;
- // Delete the resolver.
- virtual void Destroy(bool wait) = 0;
- // Returns top most resolved IPv4 address if address is resolved successfully.
- // Otherwise returns address set in SetAddress.
- SocketAddress address() const {
- SocketAddress addr;
- GetResolvedAddress(AF_INET, &addr);
- return addr;
- }
-
- // This signal is fired when address resolve process is completed.
- sigslot::signal1<AsyncResolverInterface*> SignalDone;
-};
-
-} // namespace rtc
-
-#endif
diff --git a/third_party/libwebrtc/rtc_base/async_tcp_socket.cc b/third_party/libwebrtc/rtc_base/async_tcp_socket.cc
index 367c5b04e7..2924340c16 100644
--- a/third_party/libwebrtc/rtc_base/async_tcp_socket.cc
+++ b/third_party/libwebrtc/rtc_base/async_tcp_socket.cc
@@ -14,6 +14,8 @@
#include <string.h>
#include <algorithm>
+#include <cstddef>
+#include <cstdint>
#include <memory>
#include "api/array_view.h"
@@ -209,15 +211,17 @@ void AsyncTCPSocketBase::OnReadEvent(Socket* socket) {
return;
}
- size_t size = inbuf_.size();
- ProcessInput(inbuf_.data<char>(), &size);
-
- if (size > inbuf_.size()) {
+ size_t processed = ProcessInput(inbuf_);
+ size_t bytes_remaining = inbuf_.size() - processed;
+ if (processed > inbuf_.size()) {
RTC_LOG(LS_ERROR) << "input buffer overflow";
RTC_DCHECK_NOTREACHED();
inbuf_.Clear();
} else {
- inbuf_.SetSize(size);
+ if (bytes_remaining > 0) {
+ memmove(inbuf_.data(), inbuf_.data() + processed, bytes_remaining);
+ }
+ inbuf_.SetSize(bytes_remaining);
}
}
@@ -283,24 +287,24 @@ int AsyncTCPSocket::Send(const void* pv,
return static_cast<int>(cb);
}
-void AsyncTCPSocket::ProcessInput(char* data, size_t* len) {
+size_t AsyncTCPSocket::ProcessInput(rtc::ArrayView<const uint8_t> data) {
SocketAddress remote_addr(GetRemoteAddress());
+ size_t processed_bytes = 0;
while (true) {
- if (*len < kPacketLenSize)
- return;
-
- PacketLength pkt_len = rtc::GetBE16(data);
- if (*len < kPacketLenSize + pkt_len)
- return;
-
- SignalReadPacket(this, data + kPacketLenSize, pkt_len, remote_addr,
- TimeMicros());
-
- *len -= kPacketLenSize + pkt_len;
- if (*len > 0) {
- memmove(data, data + kPacketLenSize + pkt_len, *len);
- }
+ size_t bytes_left = data.size() - processed_bytes;
+ if (bytes_left < kPacketLenSize)
+ return processed_bytes;
+
+ PacketLength pkt_len = rtc::GetBE16(data.data() + processed_bytes);
+ if (bytes_left < kPacketLenSize + pkt_len)
+ return processed_bytes;
+
+ rtc::ReceivedPacket received_packet(
+ data.subview(processed_bytes + kPacketLenSize, pkt_len), remote_addr,
+ webrtc::Timestamp::Micros(rtc::TimeMicros()));
+ NotifyPacketReceived(received_packet);
+ processed_bytes += kPacketLenSize + pkt_len;
}
}
diff --git a/third_party/libwebrtc/rtc_base/async_tcp_socket.h b/third_party/libwebrtc/rtc_base/async_tcp_socket.h
index 541080fba7..d3aff60520 100644
--- a/third_party/libwebrtc/rtc_base/async_tcp_socket.h
+++ b/third_party/libwebrtc/rtc_base/async_tcp_socket.h
@@ -13,8 +13,10 @@
#include <stddef.h>
+#include <cstdint>
#include <memory>
+#include "api/array_view.h"
#include "rtc_base/async_packet_socket.h"
#include "rtc_base/buffer.h"
#include "rtc_base/socket.h"
@@ -37,7 +39,8 @@ class AsyncTCPSocketBase : public AsyncPacketSocket {
int Send(const void* pv,
size_t cb,
const rtc::PacketOptions& options) override = 0;
- virtual void ProcessInput(char* data, size_t* len) = 0;
+ // Must return the number of bytes processed.
+ virtual size_t ProcessInput(rtc::ArrayView<const uint8_t> data) = 0;
SocketAddress GetLocalAddress() const override;
SocketAddress GetRemoteAddress() const override;
@@ -99,7 +102,7 @@ class AsyncTCPSocket : public AsyncTCPSocketBase {
int Send(const void* pv,
size_t cb,
const rtc::PacketOptions& options) override;
- void ProcessInput(char* data, size_t* len) override;
+ size_t ProcessInput(rtc::ArrayView<const uint8_t>) override;
};
class AsyncTcpListenSocket : public AsyncListenSocket {
diff --git a/third_party/libwebrtc/rtc_base/async_udp_socket.cc b/third_party/libwebrtc/rtc_base/async_udp_socket.cc
index af7ae56fb6..358420a5de 100644
--- a/third_party/libwebrtc/rtc_base/async_udp_socket.cc
+++ b/third_party/libwebrtc/rtc_base/async_udp_socket.cc
@@ -136,8 +136,8 @@ void AsyncUDPSocket::OnReadEvent(Socket* socket) {
// TODO: Make sure that we got all of the packet.
// If we did not, then we should resize our buffer to be large enough.
- SignalReadPacket(this, buf_, static_cast<size_t>(len), remote_addr,
- timestamp);
+ NotifyPacketReceived(
+ rtc::ReceivedPacket::CreateFromLegacy(buf_, len, timestamp, remote_addr));
}
void AsyncUDPSocket::OnWriteEvent(Socket* socket) {
diff --git a/third_party/libwebrtc/rtc_base/audio_format_to_string_gn/moz.build b/third_party/libwebrtc/rtc_base/audio_format_to_string_gn/moz.build
index 62b75c06ce..c756a0eb11 100644
--- a/third_party/libwebrtc/rtc_base/audio_format_to_string_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/audio_format_to_string_gn/moz.build
@@ -195,7 +195,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -205,10 +204,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/bit_buffer_gn/moz.build b/third_party/libwebrtc/rtc_base/bit_buffer_gn/moz.build
index a4a4e8c6e4..ca5959866c 100644
--- a/third_party/libwebrtc/rtc_base/bit_buffer_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/bit_buffer_gn/moz.build
@@ -188,7 +188,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -198,10 +197,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/bitrate_tracker_gn/moz.build b/third_party/libwebrtc/rtc_base/bitrate_tracker_gn/moz.build
index 8836b1e089..8c00ef1ac5 100644
--- a/third_party/libwebrtc/rtc_base/bitrate_tracker_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/bitrate_tracker_gn/moz.build
@@ -195,7 +195,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -205,10 +204,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/bitstream_reader_gn/moz.build b/third_party/libwebrtc/rtc_base/bitstream_reader_gn/moz.build
index b6a5de1971..2047e015e4 100644
--- a/third_party/libwebrtc/rtc_base/bitstream_reader_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/bitstream_reader_gn/moz.build
@@ -188,7 +188,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -198,10 +197,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/buffer_gn/moz.build b/third_party/libwebrtc/rtc_base/buffer_gn/moz.build
index cb5a45ce58..acbe827a94 100644
--- a/third_party/libwebrtc/rtc_base/buffer_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/buffer_gn/moz.build
@@ -180,16 +180,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/byte_buffer.cc b/third_party/libwebrtc/rtc_base/byte_buffer.cc
index e1278337a4..a076f46ecb 100644
--- a/third_party/libwebrtc/rtc_base/byte_buffer.cc
+++ b/third_party/libwebrtc/rtc_base/byte_buffer.cc
@@ -19,23 +19,23 @@ ByteBufferWriter::ByteBufferWriter() : ByteBufferWriterT() {}
ByteBufferWriter::ByteBufferWriter(const char* bytes, size_t len)
: ByteBufferWriterT(bytes, len) {}
-ByteBufferReader::ByteBufferReader(const char* bytes, size_t len) {
- Construct(bytes, len);
+ByteBufferReader::ByteBufferReader(rtc::ArrayView<const uint8_t> bytes) {
+ Construct(bytes.data(), bytes.size());
}
-ByteBufferReader::ByteBufferReader(const char* bytes) {
- Construct(bytes, strlen(bytes));
+ByteBufferReader::ByteBufferReader(const char* bytes, size_t len) {
+ Construct(reinterpret_cast<const uint8_t*>(bytes), len);
}
-ByteBufferReader::ByteBufferReader(const Buffer& buf) {
- Construct(buf.data<char>(), buf.size());
+ByteBufferReader::ByteBufferReader(const char* bytes) {
+ Construct(reinterpret_cast<const uint8_t*>(bytes), strlen(bytes));
}
ByteBufferReader::ByteBufferReader(const ByteBufferWriter& buf) {
- Construct(buf.Data(), buf.Length());
+ Construct(reinterpret_cast<const uint8_t*>(buf.Data()), buf.Length());
}
-void ByteBufferReader::Construct(const char* bytes, size_t len) {
+void ByteBufferReader::Construct(const uint8_t* bytes, size_t len) {
bytes_ = bytes;
size_ = len;
start_ = 0;
@@ -46,7 +46,7 @@ bool ByteBufferReader::ReadUInt8(uint8_t* val) {
if (!val)
return false;
- return ReadBytes(reinterpret_cast<char*>(val), 1);
+ return ReadBytes(val, 1);
}
bool ByteBufferReader::ReadUInt16(uint16_t* val) {
@@ -54,7 +54,7 @@ bool ByteBufferReader::ReadUInt16(uint16_t* val) {
return false;
uint16_t v;
- if (!ReadBytes(reinterpret_cast<char*>(&v), 2)) {
+ if (!ReadBytes(reinterpret_cast<uint8_t*>(&v), 2)) {
return false;
} else {
*val = NetworkToHost16(v);
@@ -67,7 +67,7 @@ bool ByteBufferReader::ReadUInt24(uint32_t* val) {
return false;
uint32_t v = 0;
- char* read_into = reinterpret_cast<char*>(&v);
+ uint8_t* read_into = reinterpret_cast<uint8_t*>(&v);
++read_into;
if (!ReadBytes(read_into, 3)) {
@@ -83,7 +83,7 @@ bool ByteBufferReader::ReadUInt32(uint32_t* val) {
return false;
uint32_t v;
- if (!ReadBytes(reinterpret_cast<char*>(&v), 4)) {
+ if (!ReadBytes(reinterpret_cast<uint8_t*>(&v), 4)) {
return false;
} else {
*val = NetworkToHost32(v);
@@ -96,7 +96,7 @@ bool ByteBufferReader::ReadUInt64(uint64_t* val) {
return false;
uint64_t v;
- if (!ReadBytes(reinterpret_cast<char*>(&v), 8)) {
+ if (!ReadBytes(reinterpret_cast<uint8_t*>(&v), 8)) {
return false;
} else {
*val = NetworkToHost64(v);
@@ -112,14 +112,14 @@ bool ByteBufferReader::ReadUVarint(uint64_t* val) {
// continuation byte (msb=1) if there are more bytes to be read.
uint64_t v = 0;
for (int i = 0; i < 64; i += 7) {
- char byte;
+ uint8_t byte;
if (!ReadBytes(&byte, 1)) {
return false;
}
// Read the first 7 bits of the byte, then offset by bits read so far.
v |= (static_cast<uint64_t>(byte) & 0x7F) << i;
- // True if the msb is not a continuation byte.
- if (static_cast<uint64_t>(byte) < 0x80) {
+ // Return if the msb is not a continuation byte.
+ if (byte < 0x80) {
*val = v;
return true;
}
@@ -134,20 +134,34 @@ bool ByteBufferReader::ReadString(std::string* val, size_t len) {
if (len > Length()) {
return false;
} else {
- val->append(bytes_ + start_, len);
+ val->append(reinterpret_cast<const char*>(bytes_ + start_), len);
start_ += len;
return true;
}
}
+bool ByteBufferReader::ReadBytes(rtc::ArrayView<uint8_t> val) {
+ if (val.size() == 0) {
+ return true;
+ }
+ return ReadBytes(val.data(), val.size());
+}
+
bool ByteBufferReader::ReadBytes(char* val, size_t len) {
+ return ReadBytes(reinterpret_cast<uint8_t*>(val), len);
+}
+
+// Private function supporting the other Read* functions.
+bool ByteBufferReader::ReadBytes(uint8_t* val, size_t len) {
if (len > Length()) {
return false;
- } else {
- memcpy(val, bytes_ + start_, len);
- start_ += len;
+ }
+ if (len == 0) {
return true;
}
+ memcpy(val, bytes_ + start_, len);
+ start_ += len;
+ return true;
}
bool ByteBufferReader::Consume(size_t size) {
diff --git a/third_party/libwebrtc/rtc_base/byte_buffer.h b/third_party/libwebrtc/rtc_base/byte_buffer.h
index 9bcbb838aa..c15773779e 100644
--- a/third_party/libwebrtc/rtc_base/byte_buffer.h
+++ b/third_party/libwebrtc/rtc_base/byte_buffer.h
@@ -16,7 +16,9 @@
#include <string>
+#include "absl/base/attributes.h"
#include "absl/strings/string_view.h"
+#include "api/array_view.h"
#include "rtc_base/buffer.h"
#include "rtc_base/byte_order.h"
@@ -25,39 +27,54 @@ namespace rtc {
template <class BufferClassT>
class ByteBufferWriterT {
+ using value_type = typename BufferClassT::value_type;
+
public:
ByteBufferWriterT() { Construct(nullptr, kDefaultCapacity); }
- ByteBufferWriterT(const char* bytes, size_t len) { Construct(bytes, len); }
+ ByteBufferWriterT(const value_type* bytes, size_t len) {
+ Construct(bytes, len);
+ }
ByteBufferWriterT(const ByteBufferWriterT&) = delete;
ByteBufferWriterT& operator=(const ByteBufferWriterT&) = delete;
- const char* Data() const { return buffer_.data(); }
+ const value_type* Data() const { return buffer_.data(); }
size_t Length() const { return buffer_.size(); }
size_t Capacity() const { return buffer_.capacity(); }
+ rtc::ArrayView<const value_type> DataView() const {
+ return rtc::MakeArrayView(Data(), Length());
+ }
+ // Accessor that returns a string_view, independent of underlying type.
+ // Intended to provide access for existing users that expect char*
+ // when the underlying type changes to uint8_t.
+ // TODO(bugs.webrtc.org/15665): Delete when users are converted.
+ absl::string_view DataAsStringView() const {
+ return absl::string_view(reinterpret_cast<const char*>(Data()), Length());
+ }
+ char* DataAsCharPointer() const { return reinterpret_cast<char*>(Data()); }
// Write value to the buffer. Resizes the buffer when it is
// neccessary.
void WriteUInt8(uint8_t val) {
- WriteBytes(reinterpret_cast<const char*>(&val), 1);
+ WriteBytes(reinterpret_cast<const value_type*>(&val), 1);
}
void WriteUInt16(uint16_t val) {
uint16_t v = HostToNetwork16(val);
- WriteBytes(reinterpret_cast<const char*>(&v), 2);
+ WriteBytes(reinterpret_cast<const value_type*>(&v), 2);
}
void WriteUInt24(uint32_t val) {
uint32_t v = HostToNetwork32(val);
- char* start = reinterpret_cast<char*>(&v);
+ value_type* start = reinterpret_cast<value_type*>(&v);
++start;
WriteBytes(start, 3);
}
void WriteUInt32(uint32_t val) {
uint32_t v = HostToNetwork32(val);
- WriteBytes(reinterpret_cast<const char*>(&v), 4);
+ WriteBytes(reinterpret_cast<const value_type*>(&v), 4);
}
void WriteUInt64(uint64_t val) {
uint64_t v = HostToNetwork64(val);
- WriteBytes(reinterpret_cast<const char*>(&v), 8);
+ WriteBytes(reinterpret_cast<const value_type*>(&v), 8);
}
// Serializes an unsigned varint in the format described by
// https://developers.google.com/protocol-buffers/docs/encoding#varints
@@ -66,22 +83,24 @@ class ByteBufferWriterT {
while (val >= 0x80) {
// Write 7 bits at a time, then set the msb to a continuation byte
// (msb=1).
- char byte = static_cast<char>(val) | 0x80;
+ value_type byte = static_cast<value_type>(val) | 0x80;
WriteBytes(&byte, 1);
val >>= 7;
}
- char last_byte = static_cast<char>(val);
+ value_type last_byte = static_cast<value_type>(val);
WriteBytes(&last_byte, 1);
}
void WriteString(absl::string_view val) {
WriteBytes(val.data(), val.size());
}
- void WriteBytes(const char* val, size_t len) { buffer_.AppendData(val, len); }
+ void WriteBytes(const value_type* val, size_t len) {
+ buffer_.AppendData(val, len);
+ }
- // Reserves the given number of bytes and returns a char* that can be written
- // into. Useful for functions that require a char* buffer and not a
- // ByteBufferWriter.
- char* ReserveWriteBuffer(size_t len) {
+ // Reserves the given number of bytes and returns a value_type* that can be
+ // written into. Useful for functions that require a value_type* buffer and
+ // not a ByteBufferWriter.
+ value_type* ReserveWriteBuffer(size_t len) {
buffer_.SetSize(buffer_.size() + len);
return buffer_.data();
}
@@ -95,7 +114,7 @@ class ByteBufferWriterT {
private:
static constexpr size_t kDefaultCapacity = 4096;
- void Construct(const char* bytes, size_t size) {
+ void Construct(const value_type* bytes, size_t size) {
if (bytes) {
buffer_.AppendData(bytes, size);
} else {
@@ -122,22 +141,31 @@ class ByteBufferWriter : public ByteBufferWriterT<BufferT<char>> {
// valid during the lifetime of the reader.
class ByteBufferReader {
public:
- ByteBufferReader(const char* bytes, size_t len);
+ [[deprecated("Use ArrayView<uint8_t>")]] ByteBufferReader(const char* bytes,
+ size_t len);
+
+ explicit ByteBufferReader(
+ rtc::ArrayView<const uint8_t> bytes ABSL_ATTRIBUTE_LIFETIME_BOUND);
// Initializes buffer from a zero-terminated string.
explicit ByteBufferReader(const char* bytes);
- explicit ByteBufferReader(const Buffer& buf);
-
explicit ByteBufferReader(const ByteBufferWriter& buf);
ByteBufferReader(const ByteBufferReader&) = delete;
ByteBufferReader& operator=(const ByteBufferReader&) = delete;
// Returns start of unprocessed data.
- const char* Data() const { return bytes_ + start_; }
+ // TODO(bugs.webrtc.org/15661): Deprecate and remove.
+ const char* Data() const {
+ return reinterpret_cast<const char*>(bytes_ + start_);
+ }
// Returns number of unprocessed bytes.
size_t Length() const { return end_ - start_; }
+ // Returns a view of the unprocessed data.
+ rtc::ArrayView<const uint8_t> DataView() const {
+ return rtc::ArrayView<const uint8_t>(bytes_ + start_, end_ - start_);
+ }
// Read a next value from the buffer. Return false if there isn't
// enough data left for the specified type.
@@ -147,7 +175,10 @@ class ByteBufferReader {
bool ReadUInt32(uint32_t* val);
bool ReadUInt64(uint64_t* val);
bool ReadUVarint(uint64_t* val);
- bool ReadBytes(char* val, size_t len);
+ bool ReadBytes(rtc::ArrayView<uint8_t> val);
+ // For backwards compatibility.
+ // TODO(bugs.webrtc.org/15661): Deprecate and remove.
+ [[deprecated("Read using ArrayView")]] bool ReadBytes(char* val, size_t len);
// Appends next `len` bytes from the buffer to `val`. Returns false
// if there is less than `len` bytes left.
@@ -159,10 +190,11 @@ class ByteBufferReader {
// after this call.
bool Consume(size_t size);
- protected:
- void Construct(const char* bytes, size_t size);
+ private:
+ void Construct(const uint8_t* bytes, size_t size);
+ bool ReadBytes(uint8_t* val, size_t len);
- const char* bytes_;
+ const uint8_t* bytes_;
size_t size_;
size_t start_;
size_t end_;
diff --git a/third_party/libwebrtc/rtc_base/byte_buffer_gn/moz.build b/third_party/libwebrtc/rtc_base/byte_buffer_gn/moz.build
index b093b72f1f..4d9cc19bfb 100644
--- a/third_party/libwebrtc/rtc_base/byte_buffer_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/byte_buffer_gn/moz.build
@@ -188,7 +188,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -198,10 +197,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/byte_buffer_unittest.cc b/third_party/libwebrtc/rtc_base/byte_buffer_unittest.cc
index 4f8043c98f..f65299e639 100644
--- a/third_party/libwebrtc/rtc_base/byte_buffer_unittest.cc
+++ b/third_party/libwebrtc/rtc_base/byte_buffer_unittest.cc
@@ -12,6 +12,8 @@
#include <string.h>
+#include <cstdint>
+
#include "rtc_base/arraysize.h"
#include "rtc_base/byte_order.h"
#include "test/gtest.h"
@@ -83,14 +85,14 @@ TEST(ByteBufferTest, TestBufferLength) {
TEST(ByteBufferTest, TestReadWriteBuffer) {
ByteBufferWriter buffer;
- ByteBufferReader read_buf(nullptr, 0);
+ ByteBufferReader read_buf(rtc::ArrayView<const uint8_t>(nullptr, 0));
uint8_t ru8;
EXPECT_FALSE(read_buf.ReadUInt8(&ru8));
// Write and read uint8_t.
uint8_t wu8 = 1;
buffer.WriteUInt8(wu8);
- ByteBufferReader read_buf1(buffer.Data(), buffer.Length());
+ ByteBufferReader read_buf1(buffer);
EXPECT_TRUE(read_buf1.ReadUInt8(&ru8));
EXPECT_EQ(wu8, ru8);
EXPECT_EQ(0U, read_buf1.Length());
@@ -99,7 +101,7 @@ TEST(ByteBufferTest, TestReadWriteBuffer) {
// Write and read uint16_t.
uint16_t wu16 = (1 << 8) + 1;
buffer.WriteUInt16(wu16);
- ByteBufferReader read_buf2(buffer.Data(), buffer.Length());
+ ByteBufferReader read_buf2(buffer);
uint16_t ru16;
EXPECT_TRUE(read_buf2.ReadUInt16(&ru16));
EXPECT_EQ(wu16, ru16);
@@ -109,7 +111,7 @@ TEST(ByteBufferTest, TestReadWriteBuffer) {
// Write and read uint24.
uint32_t wu24 = (3 << 16) + (2 << 8) + 1;
buffer.WriteUInt24(wu24);
- ByteBufferReader read_buf3(buffer.Data(), buffer.Length());
+ ByteBufferReader read_buf3(buffer);
uint32_t ru24;
EXPECT_TRUE(read_buf3.ReadUInt24(&ru24));
EXPECT_EQ(wu24, ru24);
@@ -119,7 +121,7 @@ TEST(ByteBufferTest, TestReadWriteBuffer) {
// Write and read uint32_t.
uint32_t wu32 = (4 << 24) + (3 << 16) + (2 << 8) + 1;
buffer.WriteUInt32(wu32);
- ByteBufferReader read_buf4(buffer.Data(), buffer.Length());
+ ByteBufferReader read_buf4(buffer);
uint32_t ru32;
EXPECT_TRUE(read_buf4.ReadUInt32(&ru32));
EXPECT_EQ(wu32, ru32);
@@ -130,7 +132,7 @@ TEST(ByteBufferTest, TestReadWriteBuffer) {
uint32_t another32 = (8 << 24) + (7 << 16) + (6 << 8) + 5;
uint64_t wu64 = (static_cast<uint64_t>(another32) << 32) + wu32;
buffer.WriteUInt64(wu64);
- ByteBufferReader read_buf5(buffer.Data(), buffer.Length());
+ ByteBufferReader read_buf5(buffer);
uint64_t ru64;
EXPECT_TRUE(read_buf5.ReadUInt64(&ru64));
EXPECT_EQ(wu64, ru64);
@@ -140,7 +142,7 @@ TEST(ByteBufferTest, TestReadWriteBuffer) {
// Write and read string.
std::string write_string("hello");
buffer.WriteString(write_string);
- ByteBufferReader read_buf6(buffer.Data(), buffer.Length());
+ ByteBufferReader read_buf6(buffer);
std::string read_string;
EXPECT_TRUE(read_buf6.ReadString(&read_string, write_string.size()));
EXPECT_EQ(write_string, read_string);
@@ -150,9 +152,9 @@ TEST(ByteBufferTest, TestReadWriteBuffer) {
// Write and read bytes
char write_bytes[] = "foo";
buffer.WriteBytes(write_bytes, 3);
- ByteBufferReader read_buf7(buffer.Data(), buffer.Length());
- char read_bytes[3];
- EXPECT_TRUE(read_buf7.ReadBytes(read_bytes, 3));
+ ByteBufferReader read_buf7(buffer);
+ uint8_t read_bytes[3];
+ EXPECT_TRUE(read_buf7.ReadBytes(read_bytes));
for (int i = 0; i < 3; ++i) {
EXPECT_EQ(write_bytes[i], read_bytes[i]);
}
@@ -162,9 +164,9 @@ TEST(ByteBufferTest, TestReadWriteBuffer) {
// Write and read reserved buffer space
char* write_dst = buffer.ReserveWriteBuffer(3);
memcpy(write_dst, write_bytes, 3);
- ByteBufferReader read_buf8(buffer.Data(), buffer.Length());
+ ByteBufferReader read_buf8(buffer);
memset(read_bytes, 0, 3);
- EXPECT_TRUE(read_buf8.ReadBytes(read_bytes, 3));
+ EXPECT_TRUE(read_buf8.ReadBytes(read_bytes));
for (int i = 0; i < 3; ++i) {
EXPECT_EQ(write_bytes[i], read_bytes[i]);
}
@@ -177,7 +179,7 @@ TEST(ByteBufferTest, TestReadWriteBuffer) {
buffer.WriteUInt24(wu24);
buffer.WriteUInt32(wu32);
buffer.WriteUInt64(wu64);
- ByteBufferReader read_buf9(buffer.Data(), buffer.Length());
+ ByteBufferReader read_buf9(buffer);
EXPECT_TRUE(read_buf9.ReadUInt8(&ru8));
EXPECT_EQ(wu8, ru8);
EXPECT_TRUE(read_buf9.ReadUInt16(&ru16));
@@ -217,7 +219,7 @@ TEST(ByteBufferTest, TestReadWriteUVarint) {
size += 6;
EXPECT_EQ(size, write_buffer.Length());
- ByteBufferReader read_buffer(write_buffer.Data(), write_buffer.Length());
+ ByteBufferReader read_buffer(write_buffer);
EXPECT_EQ(size, read_buffer.Length());
uint64_t val1, val2, val3, val4, val5;
@@ -247,4 +249,33 @@ TEST(ByteBufferTest, TestReadWriteUVarint) {
EXPECT_EQ(size, read_buffer.Length());
}
+TEST(ByteBufferTest, ReadFromArrayView) {
+ const uint8_t buf[] = {'a', 'b', 'c'};
+ ArrayView<const uint8_t> view(buf, 3);
+
+ ByteBufferReader read_buffer(view);
+ uint8_t val;
+ EXPECT_TRUE(read_buffer.ReadUInt8(&val));
+ EXPECT_EQ(val, 'a');
+ EXPECT_TRUE(read_buffer.ReadUInt8(&val));
+ EXPECT_EQ(val, 'b');
+ EXPECT_TRUE(read_buffer.ReadUInt8(&val));
+ EXPECT_EQ(val, 'c');
+ EXPECT_FALSE(read_buffer.ReadUInt8(&val));
+}
+
+TEST(ByteBufferTest, ReadToArrayView) {
+ const uint8_t buf[] = {'a', 'b', 'c'};
+ ArrayView<const uint8_t> stored_view(buf, 3);
+ ByteBufferReader read_buffer(stored_view);
+ uint8_t result[] = {'1', '2', '3'};
+ EXPECT_TRUE(read_buffer.ReadBytes(rtc::MakeArrayView(result, 2)));
+ EXPECT_EQ(result[0], 'a');
+ EXPECT_EQ(result[1], 'b');
+ EXPECT_EQ(result[2], '3');
+ EXPECT_TRUE(read_buffer.ReadBytes(rtc::MakeArrayView(&result[2], 1)));
+ EXPECT_EQ(result[2], 'c');
+ EXPECT_FALSE(read_buffer.ReadBytes(rtc::MakeArrayView(result, 1)));
+}
+
} // namespace rtc
diff --git a/third_party/libwebrtc/rtc_base/byte_order_gn/moz.build b/third_party/libwebrtc/rtc_base/byte_order_gn/moz.build
index 21ea639dd4..621abaa96d 100644
--- a/third_party/libwebrtc/rtc_base/byte_order_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/byte_order_gn/moz.build
@@ -176,16 +176,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/checks_gn/moz.build b/third_party/libwebrtc/rtc_base/checks_gn/moz.build
index f2ef2e2a78..ee982603d3 100644
--- a/third_party/libwebrtc/rtc_base/checks_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/checks_gn/moz.build
@@ -188,7 +188,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -198,10 +197,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/compile_assert_c_gn/moz.build b/third_party/libwebrtc/rtc_base/compile_assert_c_gn/moz.build
index 9f6999032d..62b89c7deb 100644
--- a/third_party/libwebrtc/rtc_base/compile_assert_c_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/compile_assert_c_gn/moz.build
@@ -176,16 +176,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/containers/flat_containers_internal_gn/moz.build b/third_party/libwebrtc/rtc_base/containers/flat_containers_internal_gn/moz.build
index 8fd59a6ce2..ff33c5381a 100644
--- a/third_party/libwebrtc/rtc_base/containers/flat_containers_internal_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/containers/flat_containers_internal_gn/moz.build
@@ -188,7 +188,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -198,10 +197,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/containers/flat_map_gn/moz.build b/third_party/libwebrtc/rtc_base/containers/flat_map_gn/moz.build
index 164aae2969..9b6add6b86 100644
--- a/third_party/libwebrtc/rtc_base/containers/flat_map_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/containers/flat_map_gn/moz.build
@@ -180,16 +180,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/containers/flat_set_gn/moz.build b/third_party/libwebrtc/rtc_base/containers/flat_set_gn/moz.build
index 5283c7e3d3..4b398de383 100644
--- a/third_party/libwebrtc/rtc_base/containers/flat_set_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/containers/flat_set_gn/moz.build
@@ -180,16 +180,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/copy_on_write_buffer.h b/third_party/libwebrtc/rtc_base/copy_on_write_buffer.h
index 8332ee6f62..bc5e82f89f 100644
--- a/third_party/libwebrtc/rtc_base/copy_on_write_buffer.h
+++ b/third_party/libwebrtc/rtc_base/copy_on_write_buffer.h
@@ -132,6 +132,8 @@ class RTC_EXPORT CopyOnWriteBuffer {
return buffer_->data<T>() + offset_;
}
+ bool empty() const { return size_ == 0; }
+
size_t size() const {
RTC_DCHECK(IsConsistent());
return size_;
diff --git a/third_party/libwebrtc/rtc_base/copy_on_write_buffer_gn/moz.build b/third_party/libwebrtc/rtc_base/copy_on_write_buffer_gn/moz.build
index f026e25abd..cb8e9c4a5f 100644
--- a/third_party/libwebrtc/rtc_base/copy_on_write_buffer_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/copy_on_write_buffer_gn/moz.build
@@ -188,7 +188,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -198,10 +197,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/copy_on_write_buffer_unittest.cc b/third_party/libwebrtc/rtc_base/copy_on_write_buffer_unittest.cc
index 8a9fc4e2f4..8bf53c4d4c 100644
--- a/third_party/libwebrtc/rtc_base/copy_on_write_buffer_unittest.cc
+++ b/third_party/libwebrtc/rtc_base/copy_on_write_buffer_unittest.cc
@@ -46,11 +46,20 @@ void EnsureBuffersDontShareData(const CopyOnWriteBuffer& buf1,
TEST(CopyOnWriteBufferTest, TestCreateEmptyData) {
CopyOnWriteBuffer buf(static_cast<const uint8_t*>(nullptr), 0);
+ EXPECT_TRUE(buf.empty());
EXPECT_EQ(buf.size(), 0u);
EXPECT_EQ(buf.capacity(), 0u);
EXPECT_EQ(buf.data(), nullptr);
}
+TEST(CopyOnWriteBufferTest, CreateEmptyDataWithCapacity) {
+ CopyOnWriteBuffer buf(0, 16);
+ EXPECT_TRUE(buf.empty());
+ EXPECT_EQ(buf.size(), 0u);
+ EXPECT_EQ(buf.capacity(), 16u);
+ EXPECT_NE(buf.MutableData(), nullptr);
+}
+
TEST(CopyOnWriteBufferTest, TestMoveConstruct) {
EXPECT_TRUE(std::is_nothrow_move_constructible_v<CopyOnWriteBuffer>);
@@ -60,9 +69,11 @@ TEST(CopyOnWriteBufferTest, TestMoveConstruct) {
const uint8_t* buf1_data = buf1.cdata();
CopyOnWriteBuffer buf2(std::move(buf1));
+ EXPECT_TRUE(buf1.empty());
EXPECT_EQ(buf1.size(), 0u);
EXPECT_EQ(buf1.capacity(), 0u);
EXPECT_EQ(buf1.data(), nullptr);
+ EXPECT_FALSE(buf2.empty());
EXPECT_EQ(buf2.size(), buf1_size);
EXPECT_EQ(buf2.capacity(), buf1_capacity);
EXPECT_EQ(buf2.data(), buf1_data);
@@ -127,6 +138,7 @@ TEST(CopyOnWriteBufferTest, SetEmptyData) {
buf.SetData<uint8_t>(nullptr, 0);
EXPECT_EQ(0u, buf.size());
+ EXPECT_TRUE(buf.empty());
}
TEST(CopyOnWriteBufferTest, SetDataNoMoreThanCapacityDoesntCauseReallocation) {
diff --git a/third_party/libwebrtc/rtc_base/criticalsection_gn/moz.build b/third_party/libwebrtc/rtc_base/criticalsection_gn/moz.build
index 4aa1aa4353..a681a12746 100644
--- a/third_party/libwebrtc/rtc_base/criticalsection_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/criticalsection_gn/moz.build
@@ -188,7 +188,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -198,10 +197,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/divide_round_gn/moz.build b/third_party/libwebrtc/rtc_base/divide_round_gn/moz.build
index 79146161c2..f30da236c4 100644
--- a/third_party/libwebrtc/rtc_base/divide_round_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/divide_round_gn/moz.build
@@ -180,16 +180,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/dscp_gn/moz.build b/third_party/libwebrtc/rtc_base/dscp_gn/moz.build
index 7bfe2d0c75..5f1d97d2e8 100644
--- a/third_party/libwebrtc/rtc_base/dscp_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/dscp_gn/moz.build
@@ -176,16 +176,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/event_tracer_gn/moz.build b/third_party/libwebrtc/rtc_base/event_tracer_gn/moz.build
index 06daf6b03a..9ff988a251 100644
--- a/third_party/libwebrtc/rtc_base/event_tracer_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/event_tracer_gn/moz.build
@@ -195,7 +195,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -205,10 +204,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/experiments/alr_experiment_gn/moz.build b/third_party/libwebrtc/rtc_base/experiments/alr_experiment_gn/moz.build
index 4435f010b6..1857d67850 100644
--- a/third_party/libwebrtc/rtc_base/experiments/alr_experiment_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/experiments/alr_experiment_gn/moz.build
@@ -195,7 +195,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -205,10 +204,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/experiments/balanced_degradation_settings_gn/moz.build b/third_party/libwebrtc/rtc_base/experiments/balanced_degradation_settings_gn/moz.build
index cbbb32ab70..3a258e4696 100644
--- a/third_party/libwebrtc/rtc_base/experiments/balanced_degradation_settings_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/experiments/balanced_degradation_settings_gn/moz.build
@@ -195,7 +195,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -205,10 +204,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/experiments/bandwidth_quality_scaler_settings_gn/moz.build b/third_party/libwebrtc/rtc_base/experiments/bandwidth_quality_scaler_settings_gn/moz.build
index 95a92d20e1..b8a6ccdd29 100644
--- a/third_party/libwebrtc/rtc_base/experiments/bandwidth_quality_scaler_settings_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/experiments/bandwidth_quality_scaler_settings_gn/moz.build
@@ -195,7 +195,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -205,10 +204,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/experiments/cpu_speed_experiment_gn/moz.build b/third_party/libwebrtc/rtc_base/experiments/cpu_speed_experiment_gn/moz.build
index 5151c986c4..b436f5962d 100644
--- a/third_party/libwebrtc/rtc_base/experiments/cpu_speed_experiment_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/experiments/cpu_speed_experiment_gn/moz.build
@@ -195,7 +195,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -205,10 +204,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/experiments/encoder_info_settings_gn/moz.build b/third_party/libwebrtc/rtc_base/experiments/encoder_info_settings_gn/moz.build
index b7a699fe03..ad4be851e0 100644
--- a/third_party/libwebrtc/rtc_base/experiments/encoder_info_settings_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/experiments/encoder_info_settings_gn/moz.build
@@ -195,7 +195,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -205,10 +204,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/experiments/field_trial_parser_gn/moz.build b/third_party/libwebrtc/rtc_base/experiments/field_trial_parser_gn/moz.build
index ea63aafc99..31a945e00f 100644
--- a/third_party/libwebrtc/rtc_base/experiments/field_trial_parser_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/experiments/field_trial_parser_gn/moz.build
@@ -198,7 +198,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -208,10 +207,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/experiments/keyframe_interval_settings_experiment_gn/moz.build b/third_party/libwebrtc/rtc_base/experiments/keyframe_interval_settings_experiment_gn/moz.build
index 3857bc9451..3b5cbf462f 100644
--- a/third_party/libwebrtc/rtc_base/experiments/keyframe_interval_settings_experiment_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/experiments/keyframe_interval_settings_experiment_gn/moz.build
@@ -195,7 +195,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -205,10 +204,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/experiments/min_video_bitrate_experiment_gn/moz.build b/third_party/libwebrtc/rtc_base/experiments/min_video_bitrate_experiment_gn/moz.build
index 5f811a6c6d..e2c4f2537e 100644
--- a/third_party/libwebrtc/rtc_base/experiments/min_video_bitrate_experiment_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/experiments/min_video_bitrate_experiment_gn/moz.build
@@ -195,7 +195,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -205,10 +204,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/experiments/normalize_simulcast_size_experiment_gn/moz.build b/third_party/libwebrtc/rtc_base/experiments/normalize_simulcast_size_experiment_gn/moz.build
index 68ad8a0f14..2cdcea5897 100644
--- a/third_party/libwebrtc/rtc_base/experiments/normalize_simulcast_size_experiment_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/experiments/normalize_simulcast_size_experiment_gn/moz.build
@@ -195,7 +195,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -205,10 +204,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/experiments/quality_rampup_experiment_gn/moz.build b/third_party/libwebrtc/rtc_base/experiments/quality_rampup_experiment_gn/moz.build
index c4b1f5bc22..234db8e906 100644
--- a/third_party/libwebrtc/rtc_base/experiments/quality_rampup_experiment_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/experiments/quality_rampup_experiment_gn/moz.build
@@ -195,7 +195,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -205,10 +204,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/experiments/quality_scaler_settings_gn/moz.build b/third_party/libwebrtc/rtc_base/experiments/quality_scaler_settings_gn/moz.build
index 298ca9297c..8ea1b50754 100644
--- a/third_party/libwebrtc/rtc_base/experiments/quality_scaler_settings_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/experiments/quality_scaler_settings_gn/moz.build
@@ -195,7 +195,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -205,10 +204,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/experiments/quality_scaling_experiment_gn/moz.build b/third_party/libwebrtc/rtc_base/experiments/quality_scaling_experiment_gn/moz.build
index d606a38148..36a79a7347 100644
--- a/third_party/libwebrtc/rtc_base/experiments/quality_scaling_experiment_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/experiments/quality_scaling_experiment_gn/moz.build
@@ -195,7 +195,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -205,10 +204,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/experiments/rate_control_settings_gn/moz.build b/third_party/libwebrtc/rtc_base/experiments/rate_control_settings_gn/moz.build
index be85251358..416e7a35c7 100644
--- a/third_party/libwebrtc/rtc_base/experiments/rate_control_settings_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/experiments/rate_control_settings_gn/moz.build
@@ -195,7 +195,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -205,10 +204,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/experiments/rtt_mult_experiment_gn/moz.build b/third_party/libwebrtc/rtc_base/experiments/rtt_mult_experiment_gn/moz.build
index 7229d639e8..32fd27e4ef 100644
--- a/third_party/libwebrtc/rtc_base/experiments/rtt_mult_experiment_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/experiments/rtt_mult_experiment_gn/moz.build
@@ -195,7 +195,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -205,10 +204,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/experiments/stable_target_rate_experiment_gn/moz.build b/third_party/libwebrtc/rtc_base/experiments/stable_target_rate_experiment_gn/moz.build
index d031f8ace4..bdf9e49985 100644
--- a/third_party/libwebrtc/rtc_base/experiments/stable_target_rate_experiment_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/experiments/stable_target_rate_experiment_gn/moz.build
@@ -195,7 +195,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -205,10 +204,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/frequency_tracker_gn/moz.build b/third_party/libwebrtc/rtc_base/frequency_tracker_gn/moz.build
index 30c52de724..e7a94c5776 100644
--- a/third_party/libwebrtc/rtc_base/frequency_tracker_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/frequency_tracker_gn/moz.build
@@ -195,7 +195,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -205,10 +204,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/gtest_prod_gn/moz.build b/third_party/libwebrtc/rtc_base/gtest_prod_gn/moz.build
index eb85347810..54596d501f 100644
--- a/third_party/libwebrtc/rtc_base/gtest_prod_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/gtest_prod_gn/moz.build
@@ -176,16 +176,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/histogram_percentile_counter_gn/moz.build b/third_party/libwebrtc/rtc_base/histogram_percentile_counter_gn/moz.build
index ee30758d89..48d98b418b 100644
--- a/third_party/libwebrtc/rtc_base/histogram_percentile_counter_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/histogram_percentile_counter_gn/moz.build
@@ -188,7 +188,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -198,10 +197,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/ifaddrs_android_gn/moz.build b/third_party/libwebrtc/rtc_base/ifaddrs_android_gn/moz.build
index 7985a5f7fe..6697a8f93c 100644
--- a/third_party/libwebrtc/rtc_base/ifaddrs_android_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/ifaddrs_android_gn/moz.build
@@ -77,7 +77,6 @@ if CONFIG["TARGET_CPU"] == "arm":
DEFINES["WEBRTC_HAS_NEON"] = True
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -89,10 +88,6 @@ if CONFIG["TARGET_CPU"] == "x86":
DEFINES["WEBRTC_ENABLE_AVX2"] = True
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["TARGET_CPU"] == "x86_64":
DEFINES["WEBRTC_ENABLE_AVX2"] = True
diff --git a/third_party/libwebrtc/rtc_base/ignore_wundef_gn/moz.build b/third_party/libwebrtc/rtc_base/ignore_wundef_gn/moz.build
index 15c6052f7b..502a867a4b 100644
--- a/third_party/libwebrtc/rtc_base/ignore_wundef_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/ignore_wundef_gn/moz.build
@@ -176,16 +176,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/ip_address_gn/moz.build b/third_party/libwebrtc/rtc_base/ip_address_gn/moz.build
index a893dab3c2..108fd06e54 100644
--- a/third_party/libwebrtc/rtc_base/ip_address_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/ip_address_gn/moz.build
@@ -195,7 +195,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -205,10 +204,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/logging_gn/moz.build b/third_party/libwebrtc/rtc_base/logging_gn/moz.build
index 02f74cddfa..85c9773f24 100644
--- a/third_party/libwebrtc/rtc_base/logging_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/logging_gn/moz.build
@@ -196,7 +196,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -206,10 +205,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/macromagic_gn/moz.build b/third_party/libwebrtc/rtc_base/macromagic_gn/moz.build
index b44dfa38f5..949d31b870 100644
--- a/third_party/libwebrtc/rtc_base/macromagic_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/macromagic_gn/moz.build
@@ -176,16 +176,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/memory/aligned_malloc_gn/moz.build b/third_party/libwebrtc/rtc_base/memory/aligned_malloc_gn/moz.build
index f5720480dd..8709bdd95b 100644
--- a/third_party/libwebrtc/rtc_base/memory/aligned_malloc_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/memory/aligned_malloc_gn/moz.build
@@ -188,7 +188,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -198,10 +197,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/mod_ops_gn/moz.build b/third_party/libwebrtc/rtc_base/mod_ops_gn/moz.build
index 84e775fe74..43f0844548 100644
--- a/third_party/libwebrtc/rtc_base/mod_ops_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/mod_ops_gn/moz.build
@@ -180,16 +180,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/moving_max_counter_gn/moz.build b/third_party/libwebrtc/rtc_base/moving_max_counter_gn/moz.build
index 5cd827786e..15ef767b07 100644
--- a/third_party/libwebrtc/rtc_base/moving_max_counter_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/moving_max_counter_gn/moz.build
@@ -180,16 +180,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/nat_unittest.cc b/third_party/libwebrtc/rtc_base/nat_unittest.cc
index 19e53543ba..432985d283 100644
--- a/third_party/libwebrtc/rtc_base/nat_unittest.cc
+++ b/third_party/libwebrtc/rtc_base/nat_unittest.cc
@@ -11,14 +11,17 @@
#include <string.h>
#include <algorithm>
+#include <cstddef>
#include <memory>
#include <string>
#include <vector>
#include "absl/memory/memory.h"
+#include "api/units/time_delta.h"
#include "rtc_base/async_packet_socket.h"
#include "rtc_base/async_tcp_socket.h"
#include "rtc_base/async_udp_socket.h"
+#include "rtc_base/event.h"
#include "rtc_base/gunit.h"
#include "rtc_base/ip_address.h"
#include "rtc_base/logging.h"
@@ -80,29 +83,36 @@ void TestSend(SocketServer* internal,
NATSocketFactory* natsf = new NATSocketFactory(
internal, nat->internal_udp_address(), nat->internal_tcp_address());
- TestClient* in = CreateTestClient(natsf, internal_addr);
- TestClient* out[4];
- for (int i = 0; i < 4; i++)
- out[i] = CreateTestClient(external, external_addrs[i]);
-
th_int.Start();
th_ext.Start();
+ TestClient* in;
+ th_int.BlockingCall([&] { in = CreateTestClient(natsf, internal_addr); });
+
+ TestClient* out[4];
+ th_ext.BlockingCall([&] {
+ for (int i = 0; i < 4; i++)
+ out[i] = CreateTestClient(external, external_addrs[i]);
+ });
+
const char* buf = "filter_test";
size_t len = strlen(buf);
- in->SendTo(buf, len, out[0]->address());
+ th_int.BlockingCall([&] { in->SendTo(buf, len, out[0]->address()); });
SocketAddress trans_addr;
- EXPECT_TRUE(out[0]->CheckNextPacket(buf, len, &trans_addr));
+ th_ext.BlockingCall(
+ [&] { EXPECT_TRUE(out[0]->CheckNextPacket(buf, len, &trans_addr)); });
for (int i = 1; i < 4; i++) {
- in->SendTo(buf, len, out[i]->address());
+ th_int.BlockingCall([&] { in->SendTo(buf, len, out[i]->address()); });
SocketAddress trans_addr2;
- EXPECT_TRUE(out[i]->CheckNextPacket(buf, len, &trans_addr2));
- bool are_same = (trans_addr == trans_addr2);
- ASSERT_EQ(are_same, exp_same) << "same translated address";
- ASSERT_NE(AF_UNSPEC, trans_addr.family());
- ASSERT_NE(AF_UNSPEC, trans_addr2.family());
+ th_ext.BlockingCall([&] {
+ EXPECT_TRUE(out[i]->CheckNextPacket(buf, len, &trans_addr2));
+ bool are_same = (trans_addr == trans_addr2);
+ ASSERT_EQ(are_same, exp_same) << "same translated address";
+ ASSERT_NE(AF_UNSPEC, trans_addr.family());
+ ASSERT_NE(AF_UNSPEC, trans_addr2.family());
+ });
}
th_int.Stop();
@@ -134,29 +144,39 @@ void TestRecv(SocketServer* internal,
NATSocketFactory* natsf = new NATSocketFactory(
internal, nat->internal_udp_address(), nat->internal_tcp_address());
- TestClient* in = CreateTestClient(natsf, internal_addr);
- TestClient* out[4];
- for (int i = 0; i < 4; i++)
- out[i] = CreateTestClient(external, external_addrs[i]);
-
th_int.Start();
th_ext.Start();
+ TestClient* in = nullptr;
+ th_int.BlockingCall([&] { in = CreateTestClient(natsf, internal_addr); });
+
+ TestClient* out[4];
+ th_ext.BlockingCall([&] {
+ for (int i = 0; i < 4; i++)
+ out[i] = CreateTestClient(external, external_addrs[i]);
+ });
+
const char* buf = "filter_test";
size_t len = strlen(buf);
- in->SendTo(buf, len, out[0]->address());
+ th_int.BlockingCall([&] { in->SendTo(buf, len, out[0]->address()); });
SocketAddress trans_addr;
- EXPECT_TRUE(out[0]->CheckNextPacket(buf, len, &trans_addr));
+ th_ext.BlockingCall(
+ [&] { EXPECT_TRUE(out[0]->CheckNextPacket(buf, len, &trans_addr)); });
+
+ th_ext.BlockingCall([&] { out[1]->SendTo(buf, len, trans_addr); });
+ th_int.BlockingCall(
+ [&] { EXPECT_TRUE(CheckReceive(in, !filter_ip, buf, len)); });
+ th_ext.BlockingCall([&] { out[2]->SendTo(buf, len, trans_addr); });
- out[1]->SendTo(buf, len, trans_addr);
- EXPECT_TRUE(CheckReceive(in, !filter_ip, buf, len));
+ th_int.BlockingCall(
+ [&] { EXPECT_TRUE(CheckReceive(in, !filter_port, buf, len)); });
- out[2]->SendTo(buf, len, trans_addr);
- EXPECT_TRUE(CheckReceive(in, !filter_port, buf, len));
+ th_ext.BlockingCall([&] { out[3]->SendTo(buf, len, trans_addr); });
- out[3]->SendTo(buf, len, trans_addr);
- EXPECT_TRUE(CheckReceive(in, !filter_ip && !filter_port, buf, len));
+ th_int.BlockingCall([&] {
+ EXPECT_TRUE(CheckReceive(in, !filter_ip && !filter_port, buf, len));
+ });
th_int.Stop();
th_ext.Stop();
diff --git a/third_party/libwebrtc/rtc_base/net_helpers_gn/moz.build b/third_party/libwebrtc/rtc_base/net_helpers_gn/moz.build
index c036110d1a..ceb2150c06 100644
--- a/third_party/libwebrtc/rtc_base/net_helpers_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/net_helpers_gn/moz.build
@@ -195,7 +195,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -205,10 +204,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/network/BUILD.gn b/third_party/libwebrtc/rtc_base/network/BUILD.gn
index a42745a4c0..7e9cf7ab68 100644
--- a/third_party/libwebrtc/rtc_base/network/BUILD.gn
+++ b/third_party/libwebrtc/rtc_base/network/BUILD.gn
@@ -23,8 +23,10 @@ rtc_library("received_packet") {
"received_packet.h",
]
deps = [
+ "..:socket_address",
"../../api:array_view",
"../../api/units:timestamp",
+ "../system:rtc_export",
]
absl_deps = [
"//third_party/abseil-cpp/absl/functional:any_invocable",
diff --git a/third_party/libwebrtc/rtc_base/network/received_packet.cc b/third_party/libwebrtc/rtc_base/network/received_packet.cc
index 9612c3dab4..40d6e1142c 100644
--- a/third_party/libwebrtc/rtc_base/network/received_packet.cc
+++ b/third_party/libwebrtc/rtc_base/network/received_packet.cc
@@ -17,7 +17,26 @@
namespace rtc {
ReceivedPacket::ReceivedPacket(rtc::ArrayView<const uint8_t> payload,
+ const SocketAddress& source_address,
absl::optional<webrtc::Timestamp> arrival_time)
- : payload_(payload), arrival_time_(std::move(arrival_time)) {}
+ : payload_(payload),
+ arrival_time_(std::move(arrival_time)),
+ source_address_(source_address) {}
+
+// static
+ReceivedPacket ReceivedPacket::CreateFromLegacy(
+ const char* data,
+ size_t size,
+ int64_t packet_time_us,
+ const rtc::SocketAddress& source_address) {
+ RTC_DCHECK(packet_time_us == -1 || packet_time_us >= 0);
+ return ReceivedPacket(rtc::reinterpret_array_view<const uint8_t>(
+ rtc::MakeArrayView(data, size)),
+ source_address,
+ (packet_time_us >= 0)
+ ? absl::optional<webrtc::Timestamp>(
+ webrtc::Timestamp::Micros(packet_time_us))
+ : absl::nullopt);
+}
} // namespace rtc
diff --git a/third_party/libwebrtc/rtc_base/network/received_packet.h b/third_party/libwebrtc/rtc_base/network/received_packet.h
index 7f8b2f934c..e33361ca29 100644
--- a/third_party/libwebrtc/rtc_base/network/received_packet.h
+++ b/third_party/libwebrtc/rtc_base/network/received_packet.h
@@ -15,6 +15,8 @@
#include "absl/types/optional.h"
#include "api/array_view.h"
#include "api/units/timestamp.h"
+#include "rtc_base/socket_address.h"
+#include "rtc_base/system/rtc_export.h"
namespace rtc {
@@ -22,14 +24,17 @@ namespace rtc {
// It contains a payload and metadata.
// ReceivedPacket itself does not put constraints on what payload contains. For
// example it may contains STUN, SCTP, SRTP, RTP, RTCP.... etc.
-class ReceivedPacket {
+class RTC_EXPORT ReceivedPacket {
public:
- // Caller must keep memory pointed to by payload valid for the lifetime of
- // this ReceivedPacket.
+ // Caller must keep memory pointed to by payload and address valid for the
+ // lifetime of this ReceivedPacket.
ReceivedPacket(
rtc::ArrayView<const uint8_t> payload,
+ const SocketAddress& source_address,
absl::optional<webrtc::Timestamp> arrival_time = absl::nullopt);
+ // Address/port of the packet sender.
+ const SocketAddress& source_address() const { return source_address_; }
rtc::ArrayView<const uint8_t> payload() const { return payload_; }
// Timestamp when this packet was received. Not available on all socket
@@ -38,9 +43,16 @@ class ReceivedPacket {
return arrival_time_;
}
+ static ReceivedPacket CreateFromLegacy(
+ const char* data,
+ size_t size,
+ int64_t packet_time_us,
+ const rtc::SocketAddress& = rtc::SocketAddress());
+
private:
rtc::ArrayView<const uint8_t> payload_;
absl::optional<webrtc::Timestamp> arrival_time_;
+ const SocketAddress& source_address_;
};
} // namespace rtc
diff --git a/third_party/libwebrtc/rtc_base/network/sent_packet_gn/moz.build b/third_party/libwebrtc/rtc_base/network/sent_packet_gn/moz.build
index a1276d9a7a..fb580667ce 100644
--- a/third_party/libwebrtc/rtc_base/network/sent_packet_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/network/sent_packet_gn/moz.build
@@ -184,7 +184,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -194,10 +193,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/network_constants_gn/moz.build b/third_party/libwebrtc/rtc_base/network_constants_gn/moz.build
index 083e18ac8e..40a117b476 100644
--- a/third_party/libwebrtc/rtc_base/network_constants_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/network_constants_gn/moz.build
@@ -188,7 +188,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -198,10 +197,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/network_route_gn/moz.build b/third_party/libwebrtc/rtc_base/network_route_gn/moz.build
index c35c498d44..b14b2b5633 100644
--- a/third_party/libwebrtc/rtc_base/network_route_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/network_route_gn/moz.build
@@ -188,7 +188,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -198,10 +197,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/null_socket_server_gn/moz.build b/third_party/libwebrtc/rtc_base/null_socket_server_gn/moz.build
index 7385764373..e6b3d3c59a 100644
--- a/third_party/libwebrtc/rtc_base/null_socket_server_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/null_socket_server_gn/moz.build
@@ -196,7 +196,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -206,10 +205,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/one_time_event_gn/moz.build b/third_party/libwebrtc/rtc_base/one_time_event_gn/moz.build
index d5afd56fb6..10ad1f454a 100644
--- a/third_party/libwebrtc/rtc_base/one_time_event_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/one_time_event_gn/moz.build
@@ -180,16 +180,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/openssl_adapter.cc b/third_party/libwebrtc/rtc_base/openssl_adapter.cc
index c68eb22f5c..e48cdf43bd 100644
--- a/third_party/libwebrtc/rtc_base/openssl_adapter.cc
+++ b/third_party/libwebrtc/rtc_base/openssl_adapter.cc
@@ -44,6 +44,7 @@
#include "rtc_base/openssl_identity.h"
#endif
#include "rtc_base/openssl_utility.h"
+#include "rtc_base/strings/str_join.h"
#include "rtc_base/strings/string_builder.h"
#include "rtc_base/thread.h"
@@ -168,23 +169,6 @@ namespace rtc {
using ::webrtc::TimeDelta;
-namespace webrtc_openssl_adapter_internal {
-
-// Simple O(n^2) implementation is sufficient for current use case.
-std::string StrJoin(const std::vector<std::string>& list, char delimiter) {
- RTC_CHECK(!list.empty());
- StringBuilder sb;
- sb << list[0];
- for (size_t i = 1; i < list.size(); i++) {
- sb.AppendFormat("%c", delimiter);
- sb << list[i];
- }
- return sb.Release();
-}
-} // namespace webrtc_openssl_adapter_internal
-
-using webrtc_openssl_adapter_internal::StrJoin;
-
bool OpenSSLAdapter::InitializeSSL() {
if (!SSL_library_init())
return false;
@@ -373,7 +357,7 @@ int OpenSSLAdapter::BeginSSL() {
}
if (!elliptic_curves_.empty()) {
- SSL_set1_curves_list(ssl_, StrJoin(elliptic_curves_, ':').c_str());
+ SSL_set1_curves_list(ssl_, webrtc::StrJoin(elliptic_curves_, ":").c_str());
}
// Now that the initial config is done, transfer ownership of `bio` to the
diff --git a/third_party/libwebrtc/rtc_base/openssl_adapter.h b/third_party/libwebrtc/rtc_base/openssl_adapter.h
index 558a04077a..4c05471b2b 100644
--- a/third_party/libwebrtc/rtc_base/openssl_adapter.h
+++ b/third_party/libwebrtc/rtc_base/openssl_adapter.h
@@ -37,14 +37,6 @@
namespace rtc {
-namespace webrtc_openssl_adapter_internal {
-
-// Local definition, since absl::StrJoin is not allow-listed. Declared in header
-// file only for unittests.
-std::string StrJoin(const std::vector<std::string>& list, char delimiter);
-
-} // namespace webrtc_openssl_adapter_internal
-
class OpenSSLAdapter final : public SSLAdapter {
public:
static bool InitializeSSL();
diff --git a/third_party/libwebrtc/rtc_base/openssl_adapter_unittest.cc b/third_party/libwebrtc/rtc_base/openssl_adapter_unittest.cc
index ce351dc98e..5b59a8019e 100644
--- a/third_party/libwebrtc/rtc_base/openssl_adapter_unittest.cc
+++ b/third_party/libwebrtc/rtc_base/openssl_adapter_unittest.cc
@@ -116,19 +116,4 @@ TEST(OpenSSLAdapterFactoryTest, CreateWorksWithCustomVerifier) {
EXPECT_NE(simple_adapter, nullptr);
}
-TEST(StrJoinTest, SingleElement) {
- EXPECT_EQ(webrtc_openssl_adapter_internal::StrJoin({"a"}, ','), "a");
-}
-
-TEST(StrJoinTest, TwoElements) {
- EXPECT_EQ(webrtc_openssl_adapter_internal::StrJoin({"first", "second"}, ':'),
- "first:second");
-}
-
-TEST(StrJoinTest, WithEmptyElement) {
- EXPECT_EQ(
- webrtc_openssl_adapter_internal::StrJoin({"first", "", "second"}, ':'),
- "first::second");
-}
-
} // namespace rtc
diff --git a/third_party/libwebrtc/rtc_base/openssl_stream_adapter.cc b/third_party/libwebrtc/rtc_base/openssl_stream_adapter.cc
index 353cbbe681..9642674d45 100644
--- a/third_party/libwebrtc/rtc_base/openssl_stream_adapter.cc
+++ b/third_party/libwebrtc/rtc_base/openssl_stream_adapter.cc
@@ -278,21 +278,6 @@ static long stream_ctrl(BIO* b, int cmd, long num, void* ptr) {
// OpenSSLStreamAdapter
/////////////////////////////////////////////////////////////////////////////
-static std::atomic<bool> g_use_legacy_tls_protocols_override(false);
-static std::atomic<bool> g_allow_legacy_tls_protocols(false);
-
-void SetAllowLegacyTLSProtocols(const absl::optional<bool>& allow) {
- g_use_legacy_tls_protocols_override.store(allow.has_value());
- if (allow.has_value())
- g_allow_legacy_tls_protocols.store(allow.value());
-}
-
-bool ShouldAllowLegacyTLSProtocols() {
- return g_use_legacy_tls_protocols_override.load()
- ? g_allow_legacy_tls_protocols.load()
- : webrtc::field_trial::IsEnabled("WebRTC-LegacyTlsProtocols");
-}
-
OpenSSLStreamAdapter::OpenSSLStreamAdapter(
std::unique_ptr<StreamInterface> stream,
absl::AnyInvocable<void(SSLHandshakeError)> handshake_error)
@@ -306,10 +291,7 @@ OpenSSLStreamAdapter::OpenSSLStreamAdapter(
ssl_(nullptr),
ssl_ctx_(nullptr),
ssl_mode_(SSL_MODE_TLS),
- ssl_max_version_(SSL_PROTOCOL_TLS_12),
- // Default is to support legacy TLS protocols.
- // This will be changed to default non-support in M82 or M83.
- support_legacy_tls_protocols_flag_(ShouldAllowLegacyTLSProtocols()) {
+ ssl_max_version_(SSL_PROTOCOL_TLS_12) {
stream_->SignalEvent.connect(this, &OpenSSLStreamAdapter::OnEvent);
}
@@ -1031,33 +1013,10 @@ SSL_CTX* OpenSSLStreamAdapter::SetupSSLContext() {
return nullptr;
}
- if (support_legacy_tls_protocols_flag_) {
- // TODO(https://bugs.webrtc.org/10261): Completely remove this branch in
- // M84.
- SSL_CTX_set_min_proto_version(
- ctx, ssl_mode_ == SSL_MODE_DTLS ? DTLS1_VERSION : TLS1_VERSION);
- switch (ssl_max_version_) {
- case SSL_PROTOCOL_TLS_10:
- SSL_CTX_set_max_proto_version(
- ctx, ssl_mode_ == SSL_MODE_DTLS ? DTLS1_VERSION : TLS1_VERSION);
- break;
- case SSL_PROTOCOL_TLS_11:
- SSL_CTX_set_max_proto_version(
- ctx, ssl_mode_ == SSL_MODE_DTLS ? DTLS1_VERSION : TLS1_1_VERSION);
- break;
- case SSL_PROTOCOL_TLS_12:
- default:
- SSL_CTX_set_max_proto_version(
- ctx, ssl_mode_ == SSL_MODE_DTLS ? DTLS1_2_VERSION : TLS1_2_VERSION);
- break;
- }
- } else {
- // TODO(https://bugs.webrtc.org/10261): Make this the default in M84.
- SSL_CTX_set_min_proto_version(
- ctx, ssl_mode_ == SSL_MODE_DTLS ? DTLS1_2_VERSION : TLS1_2_VERSION);
- SSL_CTX_set_max_proto_version(
- ctx, ssl_mode_ == SSL_MODE_DTLS ? DTLS1_2_VERSION : TLS1_2_VERSION);
- }
+ SSL_CTX_set_min_proto_version(
+ ctx, ssl_mode_ == SSL_MODE_DTLS ? DTLS1_2_VERSION : TLS1_2_VERSION);
+ SSL_CTX_set_max_proto_version(
+ ctx, ssl_mode_ == SSL_MODE_DTLS ? DTLS1_2_VERSION : TLS1_2_VERSION);
#ifdef OPENSSL_IS_BORINGSSL
// SSL_CTX_set_current_time_cb is only supported in BoringSSL.
diff --git a/third_party/libwebrtc/rtc_base/openssl_stream_adapter.h b/third_party/libwebrtc/rtc_base/openssl_stream_adapter.h
index 42fdb08aae..34cadeb06c 100644
--- a/third_party/libwebrtc/rtc_base/openssl_stream_adapter.h
+++ b/third_party/libwebrtc/rtc_base/openssl_stream_adapter.h
@@ -66,12 +66,6 @@ class SSLCertChain;
///////////////////////////////////////////////////////////////////////////////
-// If `allow` has a value, its value determines if legacy TLS protocols are
-// allowed, overriding the default configuration.
-// If `allow` has no value, any previous override is removed and the default
-// configuration is restored.
-RTC_EXPORT void SetAllowLegacyTLSProtocols(const absl::optional<bool>& allow);
-
class OpenSSLStreamAdapter final : public SSLStreamAdapter,
public sigslot::has_slots<> {
public:
@@ -252,9 +246,6 @@ class OpenSSLStreamAdapter final : public SSLStreamAdapter,
// A 50-ms initial timeout ensures rapid setup on fast connections, but may
// be too aggressive for low bandwidth links.
int dtls_handshake_timeout_ms_ = 50;
-
- // TODO(https://bugs.webrtc.org/10261): Completely remove this option in M84.
- const bool support_legacy_tls_protocols_flag_;
};
/////////////////////////////////////////////////////////////////////////////
diff --git a/third_party/libwebrtc/rtc_base/physical_socket_server.h b/third_party/libwebrtc/rtc_base/physical_socket_server.h
index ea449ff121..584f42a188 100644
--- a/third_party/libwebrtc/rtc_base/physical_socket_server.h
+++ b/third_party/libwebrtc/rtc_base/physical_socket_server.h
@@ -41,8 +41,6 @@
#include <unordered_map>
#include <vector>
-#include "rtc_base/async_resolver.h"
-#include "rtc_base/async_resolver_interface.h"
#include "rtc_base/deprecated/recursive_critical_section.h"
#include "rtc_base/socket_server.h"
#include "rtc_base/synchronization/mutex.h"
diff --git a/third_party/libwebrtc/rtc_base/platform_thread_gn/moz.build b/third_party/libwebrtc/rtc_base/platform_thread_gn/moz.build
index 6e962e3280..8045b2de62 100644
--- a/third_party/libwebrtc/rtc_base/platform_thread_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/platform_thread_gn/moz.build
@@ -195,7 +195,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -205,10 +204,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/platform_thread_types_gn/moz.build b/third_party/libwebrtc/rtc_base/platform_thread_types_gn/moz.build
index 0e817e6eb9..3738ea61fc 100644
--- a/third_party/libwebrtc/rtc_base/platform_thread_types_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/platform_thread_types_gn/moz.build
@@ -188,7 +188,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -198,10 +197,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/protobuf_utils_gn/moz.build b/third_party/libwebrtc/rtc_base/protobuf_utils_gn/moz.build
index 7f1e533b39..4ec9f79060 100644
--- a/third_party/libwebrtc/rtc_base/protobuf_utils_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/protobuf_utils_gn/moz.build
@@ -176,16 +176,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/race_checker_gn/moz.build b/third_party/libwebrtc/rtc_base/race_checker_gn/moz.build
index 2fab623156..ed66936a90 100644
--- a/third_party/libwebrtc/rtc_base/race_checker_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/race_checker_gn/moz.build
@@ -188,7 +188,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -198,10 +197,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/random_gn/moz.build b/third_party/libwebrtc/rtc_base/random_gn/moz.build
index 394eefde59..93b07582aa 100644
--- a/third_party/libwebrtc/rtc_base/random_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/random_gn/moz.build
@@ -188,7 +188,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -198,10 +197,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/rate_limiter_gn/moz.build b/third_party/libwebrtc/rtc_base/rate_limiter_gn/moz.build
index cf10767d72..60acfb0176 100644
--- a/third_party/libwebrtc/rtc_base/rate_limiter_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/rate_limiter_gn/moz.build
@@ -199,7 +199,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -209,10 +208,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/rate_statistics_gn/moz.build b/third_party/libwebrtc/rtc_base/rate_statistics_gn/moz.build
index 657f0a6597..84b47cf914 100644
--- a/third_party/libwebrtc/rtc_base/rate_statistics_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/rate_statistics_gn/moz.build
@@ -195,7 +195,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -205,10 +204,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/rate_tracker_gn/moz.build b/third_party/libwebrtc/rtc_base/rate_tracker_gn/moz.build
index f8ebf228f1..880d7d6236 100644
--- a/third_party/libwebrtc/rtc_base/rate_tracker_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/rate_tracker_gn/moz.build
@@ -195,7 +195,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -205,10 +204,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/ref_count.h b/third_party/libwebrtc/rtc_base/ref_count.h
index d8d652abd8..60a11fa5c7 100644
--- a/third_party/libwebrtc/rtc_base/ref_count.h
+++ b/third_party/libwebrtc/rtc_base/ref_count.h
@@ -10,57 +10,16 @@
#ifndef RTC_BASE_REF_COUNT_H_
#define RTC_BASE_REF_COUNT_H_
-namespace rtc {
-
-// Refcounted objects should implement the following informal interface:
-//
-// void AddRef() const ;
-// RefCountReleaseStatus Release() const;
-//
-// You may access members of a reference-counted object, including the AddRef()
-// and Release() methods, only if you already own a reference to it, or if
-// you're borrowing someone else's reference. (A newly created object is a
-// special case: the reference count is zero on construction, and the code that
-// creates the object should immediately call AddRef(), bringing the reference
-// count from zero to one, e.g., by constructing an rtc::scoped_refptr).
-//
-// AddRef() creates a new reference to the object.
-//
-// Release() releases a reference to the object; the caller now has one less
-// reference than before the call. Returns kDroppedLastRef if the number of
-// references dropped to zero because of this (in which case the object destroys
-// itself). Otherwise, returns kOtherRefsRemained, to signal that at the precise
-// time the caller's reference was dropped, other references still remained (but
-// if other threads own references, this may of course have changed by the time
-// Release() returns).
-//
-// The caller of Release() must treat it in the same way as a delete operation:
-// Regardless of the return value from Release(), the caller mustn't access the
-// object. The object might still be alive, due to references held by other
-// users of the object, but the object can go away at any time, e.g., as the
-// result of another thread calling Release().
-//
-// Calling AddRef() and Release() manually is discouraged. It's recommended to
-// use rtc::scoped_refptr to manage all pointers to reference counted objects.
-// Note that rtc::scoped_refptr depends on compile-time duck-typing; formally
-// implementing the below RefCountInterface is not required.
+// Transition file for backwards compatibility with source code
+// that includes the non-API file.
-enum class RefCountReleaseStatus { kDroppedLastRef, kOtherRefsRemained };
+#include "api/ref_count.h"
-// Interfaces where refcounting is part of the public api should
-// inherit this abstract interface. The implementation of these
-// methods is usually provided by the RefCountedObject template class,
-// applied as a leaf in the inheritance tree.
-class RefCountInterface {
- public:
- virtual void AddRef() const = 0;
- virtual RefCountReleaseStatus Release() const = 0;
+namespace rtc {
- // Non-public destructor, because Release() has exclusive responsibility for
- // destroying the object.
- protected:
- virtual ~RefCountInterface() {}
-};
+// TODO(bugs.webrtc.org/15622): Deprecate and remove these aliases.
+using webrtc::RefCountInterface;
+using webrtc::RefCountReleaseStatus;
} // namespace rtc
diff --git a/third_party/libwebrtc/rtc_base/refcount_gn/moz.build b/third_party/libwebrtc/rtc_base/refcount_gn/moz.build
index a1b2bcfecc..383d69e458 100644
--- a/third_party/libwebrtc/rtc_base/refcount_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/refcount_gn/moz.build
@@ -176,16 +176,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/rolling_accumulator_gn/moz.build b/third_party/libwebrtc/rtc_base/rolling_accumulator_gn/moz.build
index c5cbc0cb54..97fafd6dfb 100644
--- a/third_party/libwebrtc/rtc_base/rolling_accumulator_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/rolling_accumulator_gn/moz.build
@@ -180,16 +180,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/rtc_event_gn/moz.build b/third_party/libwebrtc/rtc_base/rtc_event_gn/moz.build
index a3bb657070..d120effc69 100644
--- a/third_party/libwebrtc/rtc_base/rtc_event_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/rtc_event_gn/moz.build
@@ -195,7 +195,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -205,10 +204,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/rtc_numerics_gn/moz.build b/third_party/libwebrtc/rtc_base/rtc_numerics_gn/moz.build
index 1c0d84013b..ac5a402772 100644
--- a/third_party/libwebrtc/rtc_base/rtc_numerics_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/rtc_numerics_gn/moz.build
@@ -190,7 +190,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -200,10 +199,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/rtc_task_queue_gn/moz.build b/third_party/libwebrtc/rtc_base/rtc_task_queue_gn/moz.build
index 21635e570b..a8de45e8df 100644
--- a/third_party/libwebrtc/rtc_base/rtc_task_queue_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/rtc_task_queue_gn/moz.build
@@ -188,7 +188,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -198,10 +197,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/safe_compare_gn/moz.build b/third_party/libwebrtc/rtc_base/safe_compare_gn/moz.build
index fdcf248573..77dbd737c8 100644
--- a/third_party/libwebrtc/rtc_base/safe_compare_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/safe_compare_gn/moz.build
@@ -176,16 +176,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/safe_conversions_gn/moz.build b/third_party/libwebrtc/rtc_base/safe_conversions_gn/moz.build
index 5901f174a6..f0d60f3922 100644
--- a/third_party/libwebrtc/rtc_base/safe_conversions_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/safe_conversions_gn/moz.build
@@ -180,16 +180,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/safe_minmax_gn/moz.build b/third_party/libwebrtc/rtc_base/safe_minmax_gn/moz.build
index c01e7c799b..a975f0911f 100644
--- a/third_party/libwebrtc/rtc_base/safe_minmax_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/safe_minmax_gn/moz.build
@@ -180,16 +180,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/sample_counter_gn/moz.build b/third_party/libwebrtc/rtc_base/sample_counter_gn/moz.build
index 99c6eb8f53..6cfed54db6 100644
--- a/third_party/libwebrtc/rtc_base/sample_counter_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/sample_counter_gn/moz.build
@@ -188,7 +188,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -198,10 +197,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/sanitizer_gn/moz.build b/third_party/libwebrtc/rtc_base/sanitizer_gn/moz.build
index 43e96ee810..8bf1b02b4f 100644
--- a/third_party/libwebrtc/rtc_base/sanitizer_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/sanitizer_gn/moz.build
@@ -176,16 +176,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/server_socket_adapters.cc b/third_party/libwebrtc/rtc_base/server_socket_adapters.cc
index 673083c7cd..47c19cbed9 100644
--- a/third_party/libwebrtc/rtc_base/server_socket_adapters.cc
+++ b/third_party/libwebrtc/rtc_base/server_socket_adapters.cc
@@ -63,7 +63,8 @@ AsyncSocksProxyServerSocket::AsyncSocksProxyServerSocket(Socket* socket)
void AsyncSocksProxyServerSocket::ProcessInput(char* data, size_t* len) {
RTC_DCHECK(state_ < SS_CONNECT_PENDING);
- ByteBufferReader response(data, *len);
+ ByteBufferReader response(
+ rtc::MakeArrayView(reinterpret_cast<const uint8_t*>(data), *len));
if (state_ == SS_HELLO) {
HandleHello(&response);
} else if (state_ == SS_AUTH) {
diff --git a/third_party/libwebrtc/rtc_base/socket_adapters.cc b/third_party/libwebrtc/rtc_base/socket_adapters.cc
index 4ec93ae3e9..f628929a46 100644
--- a/third_party/libwebrtc/rtc_base/socket_adapters.cc
+++ b/third_party/libwebrtc/rtc_base/socket_adapters.cc
@@ -520,7 +520,8 @@ void AsyncSocksProxySocket::OnConnectEvent(Socket* socket) {
void AsyncSocksProxySocket::ProcessInput(char* data, size_t* len) {
RTC_DCHECK(state_ < SS_TUNNEL);
- ByteBufferReader response(data, *len);
+ ByteBufferReader response(
+ rtc::MakeArrayView(reinterpret_cast<uint8_t*>(data), *len));
if (state_ == SS_HELLO) {
uint8_t ver, method;
diff --git a/third_party/libwebrtc/rtc_base/socket_address_gn/moz.build b/third_party/libwebrtc/rtc_base/socket_address_gn/moz.build
index 86ef2dc1a7..7d4c5ea206 100644
--- a/third_party/libwebrtc/rtc_base/socket_address_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/socket_address_gn/moz.build
@@ -196,7 +196,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -206,10 +205,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/socket_factory_gn/moz.build b/third_party/libwebrtc/rtc_base/socket_factory_gn/moz.build
index 3a2f72d1e6..c0df17e881 100644
--- a/third_party/libwebrtc/rtc_base/socket_factory_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/socket_factory_gn/moz.build
@@ -188,16 +188,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/socket_gn/moz.build b/third_party/libwebrtc/rtc_base/socket_gn/moz.build
index 0d1760a15c..0001376066 100644
--- a/third_party/libwebrtc/rtc_base/socket_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/socket_gn/moz.build
@@ -196,7 +196,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -206,10 +205,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/socket_server_gn/moz.build b/third_party/libwebrtc/rtc_base/socket_server_gn/moz.build
index 48972cbb4c..e671f719bc 100644
--- a/third_party/libwebrtc/rtc_base/socket_server_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/socket_server_gn/moz.build
@@ -188,16 +188,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/socket_unittest.cc b/third_party/libwebrtc/rtc_base/socket_unittest.cc
index 0a41a776ac..f5ef2a33fc 100644
--- a/third_party/libwebrtc/rtc_base/socket_unittest.cc
+++ b/third_party/libwebrtc/rtc_base/socket_unittest.cc
@@ -1132,13 +1132,13 @@ void SocketTest::UdpSocketRecvTimestampUseRtcEpoch(const IPAddress& loopback) {
client2->SendTo("foo", 3, address);
std::unique_ptr<TestClient::Packet> packet_1 = client1->NextPacket(10000);
ASSERT_TRUE(packet_1 != nullptr);
- EXPECT_NEAR(packet_1->packet_time_us, rtc::TimeMicros(), 1000'000);
+ EXPECT_NEAR(packet_1->packet_time->us(), rtc::TimeMicros(), 1000'000);
Thread::SleepMs(100);
client2->SendTo("bar", 3, address);
std::unique_ptr<TestClient::Packet> packet_2 = client1->NextPacket(10000);
ASSERT_TRUE(packet_2 != nullptr);
- EXPECT_GT(packet_2->packet_time_us, packet_1->packet_time_us);
- EXPECT_NEAR(packet_2->packet_time_us, rtc::TimeMicros(), 1000'000);
+ EXPECT_GT(packet_2->packet_time->us(), packet_1->packet_time->us());
+ EXPECT_NEAR(packet_2->packet_time->us(), rtc::TimeMicros(), 1000'000);
}
} // namespace rtc
diff --git a/third_party/libwebrtc/rtc_base/ssl_gn/moz.build b/third_party/libwebrtc/rtc_base/ssl_gn/moz.build
index 4be1ae9766..66200fa70c 100644
--- a/third_party/libwebrtc/rtc_base/ssl_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/ssl_gn/moz.build
@@ -176,16 +176,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/ssl_stream_adapter_unittest.cc b/third_party/libwebrtc/rtc_base/ssl_stream_adapter_unittest.cc
index 0a99d9b1f0..def4c47c0d 100644
--- a/third_party/libwebrtc/rtc_base/ssl_stream_adapter_unittest.cc
+++ b/third_party/libwebrtc/rtc_base/ssl_stream_adapter_unittest.cc
@@ -31,8 +31,11 @@
#include "rtc_base/ssl_identity.h"
#include "rtc_base/stream.h"
#include "test/field_trial.h"
+#include "test/gmock.h"
+#include "test/gtest.h"
using ::testing::Combine;
+using ::testing::NotNull;
using ::testing::tuple;
using ::testing::Values;
using ::testing::WithParamInterface;
@@ -593,9 +596,12 @@ class SSLStreamAdapterTestBase : public ::testing::Test,
size_t client_digest_len;
bool rv;
+ ASSERT_THAT(server_identity(), NotNull());
rv = server_identity()->certificate().ComputeDigest(
rtc::DIGEST_SHA_1, server_digest, 20, &server_digest_len);
ASSERT_TRUE(rv);
+
+ ASSERT_THAT(client_identity(), NotNull());
rv = client_identity()->certificate().ComputeDigest(
rtc::DIGEST_SHA_1, client_digest, 20, &client_digest_len);
ASSERT_TRUE(rv);
@@ -1607,11 +1613,14 @@ INSTANTIATE_TEST_SUITE_P(
rtc::KeyParams::RSA(1152, 65537),
rtc::KeyParams::ECDSA(rtc::EC_NIST_P256))));
-// Tests for enabling / disabling legacy TLS protocols in DTLS.
-class SSLStreamAdapterTestDTLSLegacyProtocols
+// Tests for enabling the (D)TLS extension permutation which randomizes the
+// order of extensions in the client hello.
+// These tests are a no-op under OpenSSL.
+#ifdef OPENSSL_IS_BORINGSSL
+class SSLStreamAdapterTestDTLSExtensionPermutation
: public SSLStreamAdapterTestDTLSBase {
public:
- SSLStreamAdapterTestDTLSLegacyProtocols()
+ SSLStreamAdapterTestDTLSExtensionPermutation()
: SSLStreamAdapterTestDTLSBase(rtc::KeyParams::ECDSA(rtc::EC_NIST_P256),
rtc::KeyParams::ECDSA(rtc::EC_NIST_P256)) {
}
@@ -1650,179 +1659,6 @@ class SSLStreamAdapterTestDTLSLegacyProtocols
}
};
-// Test getting the used DTLS ciphers.
-// DTLS 1.2 enabled for neither client nor server -> DTLS 1.0 will be used.
-TEST_F(SSLStreamAdapterTestDTLSLegacyProtocols, TestGetSslCipherSuite) {
- ConfigureClient("WebRTC-LegacyTlsProtocols/Enabled/");
- ConfigureServer("WebRTC-LegacyTlsProtocols/Enabled/");
- SetupProtocolVersions(rtc::SSL_PROTOCOL_DTLS_10, rtc::SSL_PROTOCOL_DTLS_10);
- TestHandshake();
-
- int client_cipher;
- ASSERT_TRUE(GetSslCipherSuite(true, &client_cipher));
- int server_cipher;
- ASSERT_TRUE(GetSslCipherSuite(false, &server_cipher));
-
- ASSERT_EQ(rtc::SSL_PROTOCOL_DTLS_10, GetSslVersion(true));
- ASSERT_EQ(rtc::SSL_PROTOCOL_DTLS_10, GetSslVersion(false));
-
- ASSERT_EQ(client_cipher, server_cipher);
-}
-
-// Test getting the used DTLS 1.2 ciphers.
-// DTLS 1.2 enabled for client and server -> DTLS 1.2 will be used.
-TEST_F(SSLStreamAdapterTestDTLSLegacyProtocols,
- TestGetSslCipherSuiteDtls12Both) {
- ConfigureClient("");
- ConfigureServer("");
- SetupProtocolVersions(rtc::SSL_PROTOCOL_DTLS_12, rtc::SSL_PROTOCOL_DTLS_12);
- TestHandshake();
-
- int client_cipher;
- ASSERT_TRUE(GetSslCipherSuite(true, &client_cipher));
- int server_cipher;
- ASSERT_TRUE(GetSslCipherSuite(false, &server_cipher));
-
- ASSERT_EQ(rtc::SSL_PROTOCOL_DTLS_12, GetSslVersion(true));
- ASSERT_EQ(rtc::SSL_PROTOCOL_DTLS_12, GetSslVersion(false));
-
- ASSERT_EQ(client_cipher, server_cipher);
-}
-
-// DTLS 1.2 enabled for client only -> DTLS 1.0 will be used.
-TEST_F(SSLStreamAdapterTestDTLSLegacyProtocols,
- TestGetSslCipherSuiteDtls12Client) {
- ConfigureClient("WebRTC-LegacyTlsProtocols/Enabled/");
- ConfigureServer("WebRTC-LegacyTlsProtocols/Enabled/");
- SetupProtocolVersions(rtc::SSL_PROTOCOL_DTLS_10, rtc::SSL_PROTOCOL_DTLS_12);
- TestHandshake();
-
- int client_cipher;
- ASSERT_TRUE(GetSslCipherSuite(true, &client_cipher));
- int server_cipher;
- ASSERT_TRUE(GetSslCipherSuite(false, &server_cipher));
-
- ASSERT_EQ(rtc::SSL_PROTOCOL_DTLS_10, GetSslVersion(true));
- ASSERT_EQ(rtc::SSL_PROTOCOL_DTLS_10, GetSslVersion(false));
-
- ASSERT_EQ(client_cipher, server_cipher);
-}
-
-// DTLS 1.2 enabled for server only -> DTLS 1.0 will be used.
-TEST_F(SSLStreamAdapterTestDTLSLegacyProtocols,
- TestGetSslCipherSuiteDtls12Server) {
- ConfigureClient("WebRTC-LegacyTlsProtocols/Enabled/");
- ConfigureServer("WebRTC-LegacyTlsProtocols/Enabled/");
- SetupProtocolVersions(rtc::SSL_PROTOCOL_DTLS_12, rtc::SSL_PROTOCOL_DTLS_10);
- TestHandshake();
-
- int client_cipher;
- ASSERT_TRUE(GetSslCipherSuite(true, &client_cipher));
- int server_cipher;
- ASSERT_TRUE(GetSslCipherSuite(false, &server_cipher));
-
- ASSERT_EQ(rtc::SSL_PROTOCOL_DTLS_10, GetSslVersion(true));
- ASSERT_EQ(rtc::SSL_PROTOCOL_DTLS_10, GetSslVersion(false));
-
- ASSERT_EQ(client_cipher, server_cipher);
-}
-
-// Client has legacy TLS versions disabled, server has DTLS 1.0 only.
-// This is meant to cause a failure.
-TEST_F(SSLStreamAdapterTestDTLSLegacyProtocols,
- TestGetSslVersionLegacyDisabledServer10) {
- ConfigureClient("");
- ConfigureServer("WebRTC-LegacyTlsProtocols/Enabled/");
- SetupProtocolVersions(rtc::SSL_PROTOCOL_DTLS_10, rtc::SSL_PROTOCOL_DTLS_12);
- // Handshake should fail.
- TestHandshake(false);
-}
-
-// Both client and server have legacy TLS versions disabled and support
-// DTLS 1.2. This should work.
-TEST_F(SSLStreamAdapterTestDTLSLegacyProtocols,
- TestGetSslVersionLegacyDisabledServer12) {
- ConfigureClient("");
- ConfigureServer("");
- SetupProtocolVersions(rtc::SSL_PROTOCOL_DTLS_12, rtc::SSL_PROTOCOL_DTLS_12);
- TestHandshake();
-}
-
-// Both client and server have legacy TLS versions enabled and support DTLS 1.0.
-// This should work.
-TEST_F(SSLStreamAdapterTestDTLSLegacyProtocols,
- TestGetSslVersionLegacyEnabledClient10Server10) {
- ConfigureClient("WebRTC-LegacyTlsProtocols/Enabled/");
- ConfigureServer("WebRTC-LegacyTlsProtocols/Enabled/");
- SetupProtocolVersions(rtc::SSL_PROTOCOL_DTLS_10, rtc::SSL_PROTOCOL_DTLS_10);
- TestHandshake();
-}
-
-// Legacy protocols are disabled in the client, max TLS version is 1.0
-// This should be a configuration error, and handshake should fail.
-TEST_F(SSLStreamAdapterTestDTLSLegacyProtocols,
- TestGetSslVersionLegacyDisabledClient10Server10) {
- ConfigureClient("");
- ConfigureServer("WebRTC-LegacyTlsProtocols/Enabled/");
- SetupProtocolVersions(rtc::SSL_PROTOCOL_DTLS_10, rtc::SSL_PROTOCOL_DTLS_10);
- TestHandshake(false);
-}
-
-// Both client and server have legacy TLS versions enabled and support DTLS 1.0.
-// This should work.
-TEST_F(SSLStreamAdapterTestDTLSLegacyProtocols,
- TestGetSslVersionLegacyOverrideEnabledClient10Server10) {
- rtc::SetAllowLegacyTLSProtocols(true);
- ConfigureClient("");
- ConfigureServer("");
- // Remove override.
- rtc::SetAllowLegacyTLSProtocols(absl::nullopt);
- SetupProtocolVersions(rtc::SSL_PROTOCOL_DTLS_10, rtc::SSL_PROTOCOL_DTLS_10);
- TestHandshake();
-}
-
-// Client has legacy TLS disabled and server has legacy TLS enabled via
-// override. Handshake for DTLS 1.0 should fail.
-TEST_F(SSLStreamAdapterTestDTLSLegacyProtocols,
- TestGetSslVersionLegacyOverrideDisabledClient10EnabledServer10) {
- rtc::SetAllowLegacyTLSProtocols(false);
- ConfigureClient("");
- rtc::SetAllowLegacyTLSProtocols(true);
- ConfigureServer("");
- // Remove override.
- rtc::SetAllowLegacyTLSProtocols(absl::nullopt);
- SetupProtocolVersions(rtc::SSL_PROTOCOL_DTLS_10, rtc::SSL_PROTOCOL_DTLS_10);
- TestHandshake(false);
-}
-
-// Client has legacy TLS enabled and server has legacy TLS disabled via
-// override. Handshake for DTLS 1.0 should fail.
-TEST_F(SSLStreamAdapterTestDTLSLegacyProtocols,
- TestGetSslVersionLegacyOverrideEnabledClient10DisabledServer10) {
- rtc::SetAllowLegacyTLSProtocols(true);
- ConfigureClient("");
- rtc::SetAllowLegacyTLSProtocols(false);
- ConfigureServer("");
- // Remove override.
- rtc::SetAllowLegacyTLSProtocols(absl::nullopt);
- SetupProtocolVersions(rtc::SSL_PROTOCOL_DTLS_10, rtc::SSL_PROTOCOL_DTLS_10);
- TestHandshake(false);
-}
-
-// These tests are a no-op under OpenSSL.
-#ifdef OPENSSL_IS_BORINGSSL
-// TODO(https://bugs.webrtc.org/10261): when removing
-// SSLStreamAdapterTestDTLSLegacyProtocols that this class
-// inherits from move the code to this class.
-class SSLStreamAdapterTestDTLSExtensionPermutation
- : public SSLStreamAdapterTestDTLSLegacyProtocols {
- public:
- SSLStreamAdapterTestDTLSExtensionPermutation()
- : SSLStreamAdapterTestDTLSLegacyProtocols() {}
-};
-
-// Tests for enabling the (D)TLS extension permutation which randomizes the
-// order of extensions in the client hello.
TEST_F(SSLStreamAdapterTestDTLSExtensionPermutation,
ClientDefaultServerDefault) {
ConfigureClient("");
diff --git a/third_party/libwebrtc/net/dcsctp/common/str_join.h b/third_party/libwebrtc/rtc_base/strings/str_join.h
index 04517827b7..762e63ae2a 100644
--- a/third_party/libwebrtc/net/dcsctp/common/str_join.h
+++ b/third_party/libwebrtc/rtc_base/strings/str_join.h
@@ -7,15 +7,15 @@
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
-#ifndef NET_DCSCTP_COMMON_STR_JOIN_H_
-#define NET_DCSCTP_COMMON_STR_JOIN_H_
+#ifndef RTC_BASE_STRINGS_STR_JOIN_H_
+#define RTC_BASE_STRINGS_STR_JOIN_H_
#include <string>
#include "absl/strings/string_view.h"
#include "rtc_base/strings/string_builder.h"
-namespace dcsctp {
+namespace webrtc {
template <typename Range>
std::string StrJoin(const Range& seq, absl::string_view delimiter) {
@@ -51,6 +51,6 @@ std::string StrJoin(const Range& seq,
return sb.Release();
}
-} // namespace dcsctp
+} // namespace webrtc
-#endif // NET_DCSCTP_COMMON_STR_JOIN_H_
+#endif // RTC_BASE_STRINGS_STR_JOIN_H_
diff --git a/third_party/libwebrtc/net/dcsctp/common/str_join_test.cc b/third_party/libwebrtc/rtc_base/strings/str_join_unittest.cc
index dbfd92c1cf..a4ac02125f 100644
--- a/third_party/libwebrtc/net/dcsctp/common/str_join_test.cc
+++ b/third_party/libwebrtc/rtc_base/strings/str_join_unittest.cc
@@ -7,15 +7,15 @@
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
-#include "net/dcsctp/common/str_join.h"
+#include "rtc_base/strings/str_join.h"
#include <string>
#include <utility>
#include <vector>
-#include "test/gmock.h"
+#include "test/gtest.h"
-namespace dcsctp {
+namespace webrtc {
namespace {
TEST(StrJoinTest, CanJoinStringsFromVector) {
@@ -42,4 +42,4 @@ TEST(StrJoinTest, CanFormatElementsWhileJoining) {
}
} // namespace
-} // namespace dcsctp
+} // namespace webrtc
diff --git a/third_party/libwebrtc/rtc_base/stringutils_gn/moz.build b/third_party/libwebrtc/rtc_base/stringutils_gn/moz.build
index 58008d114a..3b7954e86e 100644
--- a/third_party/libwebrtc/rtc_base/stringutils_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/stringutils_gn/moz.build
@@ -192,7 +192,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -202,10 +201,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/swap_queue_gn/moz.build b/third_party/libwebrtc/rtc_base/swap_queue_gn/moz.build
index 3bffe145d6..b03cf37955 100644
--- a/third_party/libwebrtc/rtc_base/swap_queue_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/swap_queue_gn/moz.build
@@ -180,16 +180,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/synchronization/mutex_gn/moz.build b/third_party/libwebrtc/rtc_base/synchronization/mutex_gn/moz.build
index 9799330606..197f9213a8 100644
--- a/third_party/libwebrtc/rtc_base/synchronization/mutex_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/synchronization/mutex_gn/moz.build
@@ -180,16 +180,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/synchronization/sequence_checker_internal_gn/moz.build b/third_party/libwebrtc/rtc_base/synchronization/sequence_checker_internal_gn/moz.build
index a43e97a8c2..28788b4b65 100644
--- a/third_party/libwebrtc/rtc_base/synchronization/sequence_checker_internal_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/synchronization/sequence_checker_internal_gn/moz.build
@@ -188,7 +188,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -198,10 +197,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/synchronization/yield_gn/moz.build b/third_party/libwebrtc/rtc_base/synchronization/yield_gn/moz.build
index ac962ac0a2..6e5fe912f1 100644
--- a/third_party/libwebrtc/rtc_base/synchronization/yield_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/synchronization/yield_gn/moz.build
@@ -184,7 +184,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -194,10 +193,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/synchronization/yield_policy_gn/moz.build b/third_party/libwebrtc/rtc_base/synchronization/yield_policy_gn/moz.build
index 7adceec9b4..df7b6a62b9 100644
--- a/third_party/libwebrtc/rtc_base/synchronization/yield_policy_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/synchronization/yield_policy_gn/moz.build
@@ -188,7 +188,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -198,10 +197,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/system/arch_gn/moz.build b/third_party/libwebrtc/rtc_base/system/arch_gn/moz.build
index 136948d0f2..c72b2b9951 100644
--- a/third_party/libwebrtc/rtc_base/system/arch_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/system/arch_gn/moz.build
@@ -176,16 +176,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/system/asm_defines_gn/moz.build b/third_party/libwebrtc/rtc_base/system/asm_defines_gn/moz.build
index b99d782d88..4a2dbbea32 100644
--- a/third_party/libwebrtc/rtc_base/system/asm_defines_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/system/asm_defines_gn/moz.build
@@ -59,7 +59,6 @@ if CONFIG["OS_TARGET"] == "Android":
DEFINES["WEBRTC_ANDROID_OPENSLES"] = True
OS_LIBS += [
- "android_support",
"unwind"
]
diff --git a/third_party/libwebrtc/rtc_base/system/file_wrapper_gn/moz.build b/third_party/libwebrtc/rtc_base/system/file_wrapper_gn/moz.build
index 6d0a523103..4fdec8d732 100644
--- a/third_party/libwebrtc/rtc_base/system/file_wrapper_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/system/file_wrapper_gn/moz.build
@@ -188,7 +188,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -198,10 +197,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/system/ignore_warnings_gn/moz.build b/third_party/libwebrtc/rtc_base/system/ignore_warnings_gn/moz.build
index 61bbeefabc..3406348246 100644
--- a/third_party/libwebrtc/rtc_base/system/ignore_warnings_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/system/ignore_warnings_gn/moz.build
@@ -176,16 +176,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/system/inline_gn/moz.build b/third_party/libwebrtc/rtc_base/system/inline_gn/moz.build
index 574d7958f1..4236cd7c5d 100644
--- a/third_party/libwebrtc/rtc_base/system/inline_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/system/inline_gn/moz.build
@@ -176,16 +176,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/system/no_unique_address_gn/moz.build b/third_party/libwebrtc/rtc_base/system/no_unique_address_gn/moz.build
index 4d51581c1b..bcb6b00848 100644
--- a/third_party/libwebrtc/rtc_base/system/no_unique_address_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/system/no_unique_address_gn/moz.build
@@ -176,16 +176,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/system/rtc_export_gn/moz.build b/third_party/libwebrtc/rtc_base/system/rtc_export_gn/moz.build
index e4d340d3c6..4eb3c21325 100644
--- a/third_party/libwebrtc/rtc_base/system/rtc_export_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/system/rtc_export_gn/moz.build
@@ -176,16 +176,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/system/unused_gn/moz.build b/third_party/libwebrtc/rtc_base/system/unused_gn/moz.build
index c6dd8a6bab..25a3e4e088 100644
--- a/third_party/libwebrtc/rtc_base/system/unused_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/system/unused_gn/moz.build
@@ -176,16 +176,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/system/warn_current_thread_is_deadlocked_gn/moz.build b/third_party/libwebrtc/rtc_base/system/warn_current_thread_is_deadlocked_gn/moz.build
index 3aa57ac48e..cc34279237 100644
--- a/third_party/libwebrtc/rtc_base/system/warn_current_thread_is_deadlocked_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/system/warn_current_thread_is_deadlocked_gn/moz.build
@@ -176,16 +176,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/task_utils/repeating_task_gn/moz.build b/third_party/libwebrtc/rtc_base/task_utils/repeating_task_gn/moz.build
index 224f776828..ffe3228fdc 100644
--- a/third_party/libwebrtc/rtc_base/task_utils/repeating_task_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/task_utils/repeating_task_gn/moz.build
@@ -199,7 +199,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -209,10 +208,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/test_client.cc b/third_party/libwebrtc/rtc_base/test_client.cc
index f23ac2aec0..87c946529e 100644
--- a/third_party/libwebrtc/rtc_base/test_client.cc
+++ b/third_party/libwebrtc/rtc_base/test_client.cc
@@ -15,7 +15,9 @@
#include <memory>
#include <utility>
+#include "api/units/timestamp.h"
#include "rtc_base/gunit.h"
+#include "rtc_base/network/received_packet.h"
#include "rtc_base/thread.h"
#include "rtc_base/time_utils.h"
@@ -30,10 +32,11 @@ TestClient::TestClient(std::unique_ptr<AsyncPacketSocket> socket)
TestClient::TestClient(std::unique_ptr<AsyncPacketSocket> socket,
ThreadProcessingFakeClock* fake_clock)
- : fake_clock_(fake_clock),
- socket_(std::move(socket)),
- prev_packet_timestamp_(-1) {
- socket_->SignalReadPacket.connect(this, &TestClient::OnPacket);
+ : fake_clock_(fake_clock), socket_(std::move(socket)) {
+ socket_->RegisterReceivedPacketCallback(
+ [&](rtc::AsyncPacketSocket* socket, const rtc::ReceivedPacket& packet) {
+ OnPacket(socket, packet);
+ });
socket_->SignalReadyToSend.connect(this, &TestClient::OnReadyToSend);
}
@@ -100,20 +103,22 @@ bool TestClient::CheckNextPacket(const char* buf,
bool res = false;
std::unique_ptr<Packet> packet = NextPacket(kTimeoutMs);
if (packet) {
- res = (packet->size == size && memcmp(packet->buf, buf, size) == 0 &&
- CheckTimestamp(packet->packet_time_us));
+ res = (packet->buf.size() == size &&
+ memcmp(packet->buf.data(), buf, size) == 0 &&
+ CheckTimestamp(packet->packet_time));
if (addr)
*addr = packet->addr;
}
return res;
}
-bool TestClient::CheckTimestamp(int64_t packet_timestamp) {
+bool TestClient::CheckTimestamp(
+ absl::optional<webrtc::Timestamp> packet_timestamp) {
bool res = true;
- if (packet_timestamp == -1) {
+ if (!packet_timestamp) {
res = false;
}
- if (prev_packet_timestamp_ != -1) {
+ if (prev_packet_timestamp_) {
if (packet_timestamp < prev_packet_timestamp_) {
res = false;
}
@@ -145,36 +150,24 @@ int TestClient::SetOption(Socket::Option opt, int value) {
}
void TestClient::OnPacket(AsyncPacketSocket* socket,
- const char* buf,
- size_t size,
- const SocketAddress& remote_addr,
- const int64_t& packet_time_us) {
+ const rtc::ReceivedPacket& received_packet) {
webrtc::MutexLock lock(&mutex_);
- packets_.push_back(
- std::make_unique<Packet>(remote_addr, buf, size, packet_time_us));
+ packets_.push_back(std::make_unique<Packet>(received_packet));
}
void TestClient::OnReadyToSend(AsyncPacketSocket* socket) {
++ready_to_send_count_;
}
-TestClient::Packet::Packet(const SocketAddress& a,
- const char* b,
- size_t s,
- int64_t packet_time_us)
- : addr(a), buf(0), size(s), packet_time_us(packet_time_us) {
- buf = new char[size];
- memcpy(buf, b, size);
-}
+TestClient::Packet::Packet(const rtc::ReceivedPacket& received_packet)
+ : addr(received_packet.source_address()),
+ // Copy received_packet payload to a buffer owned by Packet.
+ buf(received_packet.payload().data(), received_packet.payload().size()),
+ packet_time(received_packet.arrival_time()) {}
TestClient::Packet::Packet(const Packet& p)
- : addr(p.addr), buf(0), size(p.size), packet_time_us(p.packet_time_us) {
- buf = new char[size];
- memcpy(buf, p.buf, size);
-}
-
-TestClient::Packet::~Packet() {
- delete[] buf;
-}
+ : addr(p.addr),
+ buf(p.buf.data(), p.buf.size()),
+ packet_time(p.packet_time) {}
} // namespace rtc
diff --git a/third_party/libwebrtc/rtc_base/test_client.h b/third_party/libwebrtc/rtc_base/test_client.h
index dd91d37ab9..6fe6fd5b83 100644
--- a/third_party/libwebrtc/rtc_base/test_client.h
+++ b/third_party/libwebrtc/rtc_base/test_client.h
@@ -14,8 +14,11 @@
#include <memory>
#include <vector>
+#include "api/units/timestamp.h"
#include "rtc_base/async_udp_socket.h"
+#include "rtc_base/buffer.h"
#include "rtc_base/fake_clock.h"
+#include "rtc_base/network/received_packet.h"
#include "rtc_base/synchronization/mutex.h"
namespace rtc {
@@ -26,17 +29,12 @@ class TestClient : public sigslot::has_slots<> {
public:
// Records the contents of a packet that was received.
struct Packet {
- Packet(const SocketAddress& a,
- const char* b,
- size_t s,
- int64_t packet_time_us);
+ Packet(const rtc::ReceivedPacket& received_packet);
Packet(const Packet& p);
- virtual ~Packet();
SocketAddress addr;
- char* buf;
- size_t size;
- int64_t packet_time_us;
+ Buffer buf;
+ absl::optional<webrtc::Timestamp> packet_time;
};
// Default timeout for NextPacket reads.
@@ -96,14 +94,11 @@ class TestClient : public sigslot::has_slots<> {
static const int kNoPacketTimeoutMs = 1000;
// Workaround for the fact that AsyncPacketSocket::GetConnState doesn't exist.
Socket::ConnState GetState();
- // Slot for packets read on the socket.
+
void OnPacket(AsyncPacketSocket* socket,
- const char* buf,
- size_t len,
- const SocketAddress& remote_addr,
- const int64_t& packet_time_us);
+ const rtc::ReceivedPacket& received_packet);
void OnReadyToSend(AsyncPacketSocket* socket);
- bool CheckTimestamp(int64_t packet_timestamp);
+ bool CheckTimestamp(absl::optional<webrtc::Timestamp> packet_timestamp);
void AdvanceTime(int ms);
ThreadProcessingFakeClock* fake_clock_ = nullptr;
@@ -111,7 +106,7 @@ class TestClient : public sigslot::has_slots<> {
std::unique_ptr<AsyncPacketSocket> socket_;
std::vector<std::unique_ptr<Packet>> packets_;
int ready_to_send_count_ = 0;
- int64_t prev_packet_timestamp_;
+ absl::optional<webrtc::Timestamp> prev_packet_timestamp_;
};
} // namespace rtc
diff --git a/third_party/libwebrtc/rtc_base/test_echo_server.h b/third_party/libwebrtc/rtc_base/test_echo_server.h
index 82817624a5..d99ed72f00 100644
--- a/third_party/libwebrtc/rtc_base/test_echo_server.h
+++ b/third_party/libwebrtc/rtc_base/test_echo_server.h
@@ -21,6 +21,7 @@
#include "absl/memory/memory.h"
#include "rtc_base/async_packet_socket.h"
#include "rtc_base/async_tcp_socket.h"
+#include "rtc_base/network/received_packet.h"
#include "rtc_base/socket.h"
#include "rtc_base/socket_address.h"
#include "rtc_base/third_party/sigslot/sigslot.h"
@@ -45,19 +46,17 @@ class TestEchoServer : public sigslot::has_slots<> {
Socket* raw_socket = socket->Accept(nullptr);
if (raw_socket) {
AsyncTCPSocket* packet_socket = new AsyncTCPSocket(raw_socket);
- packet_socket->SignalReadPacket.connect(this, &TestEchoServer::OnPacket);
+ packet_socket->RegisterReceivedPacketCallback(
+ [&](rtc::AsyncPacketSocket* socket,
+ const rtc::ReceivedPacket& packet) { OnPacket(socket, packet); });
packet_socket->SubscribeCloseEvent(
this, [this](AsyncPacketSocket* s, int err) { OnClose(s, err); });
client_sockets_.push_back(packet_socket);
}
}
- void OnPacket(AsyncPacketSocket* socket,
- const char* buf,
- size_t size,
- const SocketAddress& remote_addr,
- const int64_t& /* packet_time_us */) {
+ void OnPacket(AsyncPacketSocket* socket, const rtc::ReceivedPacket& packet) {
rtc::PacketOptions options;
- socket->Send(buf, size, options);
+ socket->Send(packet.payload().data(), packet.payload().size(), options);
}
void OnClose(AsyncPacketSocket* socket, int err) {
ClientList::iterator it = absl::c_find(client_sockets_, socket);
diff --git a/third_party/libwebrtc/rtc_base/third_party/base64/base64_gn/moz.build b/third_party/libwebrtc/rtc_base/third_party/base64/base64_gn/moz.build
index 0d35da2f19..18384b6a38 100644
--- a/third_party/libwebrtc/rtc_base/third_party/base64/base64_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/third_party/base64/base64_gn/moz.build
@@ -188,7 +188,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -198,10 +197,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/third_party/sigslot/sigslot_gn/moz.build b/third_party/libwebrtc/rtc_base/third_party/sigslot/sigslot_gn/moz.build
index 906a4a9e81..b5b20ed538 100644
--- a/third_party/libwebrtc/rtc_base/third_party/sigslot/sigslot_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/third_party/sigslot/sigslot_gn/moz.build
@@ -184,7 +184,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -194,10 +193,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/threading_gn/moz.build b/third_party/libwebrtc/rtc_base/threading_gn/moz.build
index 199af4b1d3..1ed74bfaad 100644
--- a/third_party/libwebrtc/rtc_base/threading_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/threading_gn/moz.build
@@ -31,7 +31,6 @@ LOCAL_INCLUDES += [
]
UNIFIED_SOURCES += [
- "/third_party/libwebrtc/rtc_base/async_resolver.cc",
"/third_party/libwebrtc/rtc_base/internal/default_socket_server.cc",
"/third_party/libwebrtc/rtc_base/network_monitor.cc",
"/third_party/libwebrtc/rtc_base/network_monitor_factory.cc",
@@ -201,7 +200,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -211,10 +209,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/timeutils_gn/moz.build b/third_party/libwebrtc/rtc_base/timeutils_gn/moz.build
index c4eb7ac8de..c74be73063 100644
--- a/third_party/libwebrtc/rtc_base/timeutils_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/timeutils_gn/moz.build
@@ -197,7 +197,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -207,10 +206,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/type_traits_gn/moz.build b/third_party/libwebrtc/rtc_base/type_traits_gn/moz.build
index 1f72327af3..9b5c10f949 100644
--- a/third_party/libwebrtc/rtc_base/type_traits_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/type_traits_gn/moz.build
@@ -176,16 +176,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/unique_id_generator_gn/moz.build b/third_party/libwebrtc/rtc_base/unique_id_generator_gn/moz.build
index 975562a208..8b55572cb5 100644
--- a/third_party/libwebrtc/rtc_base/unique_id_generator_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/unique_id_generator_gn/moz.build
@@ -176,16 +176,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/units/unit_base_gn/moz.build b/third_party/libwebrtc/rtc_base/units/unit_base_gn/moz.build
index bb68c4ea3c..259ef161a2 100644
--- a/third_party/libwebrtc/rtc_base/units/unit_base_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/units/unit_base_gn/moz.build
@@ -180,16 +180,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/virtual_socket_server.cc b/third_party/libwebrtc/rtc_base/virtual_socket_server.cc
index efc206b219..bec7d9733f 100644
--- a/third_party/libwebrtc/rtc_base/virtual_socket_server.cc
+++ b/third_party/libwebrtc/rtc_base/virtual_socket_server.cc
@@ -18,6 +18,7 @@
#include <vector>
#include "absl/algorithm/container.h"
+#include "api/sequence_checker.h"
#include "api/units/time_delta.h"
#include "rtc_base/checks.h"
#include "rtc_base/event.h"
diff --git a/third_party/libwebrtc/rtc_base/weak_ptr_gn/moz.build b/third_party/libwebrtc/rtc_base/weak_ptr_gn/moz.build
index a7cf9dd19e..42342eee8b 100644
--- a/third_party/libwebrtc/rtc_base/weak_ptr_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/weak_ptr_gn/moz.build
@@ -188,7 +188,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -198,10 +197,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_base/zero_memory_gn/moz.build b/third_party/libwebrtc/rtc_base/zero_memory_gn/moz.build
index 1029a25252..0b8ed4f70c 100644
--- a/third_party/libwebrtc/rtc_base/zero_memory_gn/moz.build
+++ b/third_party/libwebrtc/rtc_base/zero_memory_gn/moz.build
@@ -188,7 +188,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -198,10 +197,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/rtc_tools/BUILD.gn b/third_party/libwebrtc/rtc_tools/BUILD.gn
index 7765cf522b..df3c55fec8 100644
--- a/third_party/libwebrtc/rtc_tools/BUILD.gn
+++ b/third_party/libwebrtc/rtc_tools/BUILD.gn
@@ -54,6 +54,7 @@ rtc_library("video_file_reader") {
]
deps = [
"../api:make_ref_counted",
+ "../api:ref_count",
"../api:scoped_refptr",
"../api/video:video_frame",
"../api/video:video_rtp_headers",
@@ -203,9 +204,8 @@ if (!is_component_build) {
"../api:create_frame_generator",
"../api:rtp_parameters",
"../api:transport_api",
- "../api/rtc_event_log",
- "../api/task_queue:default_task_queue_factory",
- "../api/task_queue:task_queue",
+ "../api/environment",
+ "../api/environment:environment_factory",
"../api/video:builtin_video_bitrate_allocator_factory",
"../api/video_codecs:video_codecs_api",
"../call",
@@ -256,8 +256,8 @@ if (!is_component_build) {
deps = [
"../api:field_trials",
"../api:rtp_parameters",
- "../api/rtc_event_log",
- "../api/task_queue:default_task_queue_factory",
+ "../api/environment",
+ "../api/environment:environment_factory",
"../api/test/video:function_video_factory",
"../api/transport:field_trial_based_config",
"../api/units:timestamp",
@@ -272,7 +272,6 @@ if (!is_component_build) {
"../rtc_base:checks",
"../rtc_base:rtc_json",
"../rtc_base:stringutils",
- "../rtc_base:timeutils",
"../system_wrappers",
"../test:call_config_utils",
"../test:encoder_settings",
@@ -372,10 +371,6 @@ if (!build_with_chromium) {
"rtc_event_log_visualizer/log_simulation.h",
"rtc_event_log_visualizer/plot_base.cc",
"rtc_event_log_visualizer/plot_base.h",
- "rtc_event_log_visualizer/plot_protobuf.cc",
- "rtc_event_log_visualizer/plot_protobuf.h",
- "rtc_event_log_visualizer/plot_python.cc",
- "rtc_event_log_visualizer/plot_python.h",
]
deps = [
":chart_proto",
@@ -420,7 +415,6 @@ if (!build_with_chromium) {
"../modules/rtp_rtcp",
"../modules/rtp_rtcp:rtp_rtcp_format",
"../rtc_base:checks",
- "../rtc_base:ignore_wundef",
"../rtc_base:logging",
"../rtc_base:macromagic",
"../rtc_base:rate_statistics",
@@ -659,7 +653,6 @@ if (rtc_include_tests) {
"../modules/audio_processing:audioproc_debug_proto",
"../modules/audio_processing:audioproc_protobuf_utils",
"../rtc_base:checks",
- "../rtc_base:ignore_wundef",
"../rtc_base:macromagic",
"../rtc_base:protobuf_utils",
"../rtc_base:stringutils",
diff --git a/third_party/libwebrtc/rtc_tools/network_tester/BUILD.gn b/third_party/libwebrtc/rtc_tools/network_tester/BUILD.gn
index efe2123510..5930431ab9 100644
--- a/third_party/libwebrtc/rtc_tools/network_tester/BUILD.gn
+++ b/third_party/libwebrtc/rtc_tools/network_tester/BUILD.gn
@@ -46,13 +46,13 @@ if (rtc_enable_protobuf) {
"../../p2p:rtc_p2p",
"../../rtc_base:async_packet_socket",
"../../rtc_base:checks",
- "../../rtc_base:ignore_wundef",
"../../rtc_base:ip_address",
"../../rtc_base:logging",
"../../rtc_base:macromagic",
"../../rtc_base:protobuf_utils",
"../../rtc_base:rtc_task_queue",
"../../rtc_base:socket_address",
+ "../../rtc_base:socket_server",
"../../rtc_base:threading",
"../../rtc_base:timeutils",
"../../rtc_base/synchronization:mutex",
diff --git a/third_party/libwebrtc/rtc_tools/network_tester/config_reader.h b/third_party/libwebrtc/rtc_tools/network_tester/config_reader.h
index 3735555d51..3903613b5b 100644
--- a/third_party/libwebrtc/rtc_tools/network_tester/config_reader.h
+++ b/third_party/libwebrtc/rtc_tools/network_tester/config_reader.h
@@ -15,12 +15,9 @@
#include <string>
#include "absl/types/optional.h"
-#include "rtc_base/ignore_wundef.h"
#ifdef WEBRTC_NETWORK_TESTER_PROTO
-RTC_PUSH_IGNORING_WUNDEF()
#include "rtc_tools/network_tester/network_tester_config.pb.h"
-RTC_POP_IGNORING_WUNDEF()
using webrtc::network_tester::config::NetworkTesterAllConfigs;
#else
class NetworkTesterConfigs;
diff --git a/third_party/libwebrtc/rtc_tools/network_tester/packet_logger.h b/third_party/libwebrtc/rtc_tools/network_tester/packet_logger.h
index c3ac309cf7..c8bfccba10 100644
--- a/third_party/libwebrtc/rtc_tools/network_tester/packet_logger.h
+++ b/third_party/libwebrtc/rtc_tools/network_tester/packet_logger.h
@@ -14,12 +14,8 @@
#include <fstream>
#include <string>
-#include "rtc_base/ignore_wundef.h"
-
#ifdef WEBRTC_NETWORK_TESTER_PROTO
-RTC_PUSH_IGNORING_WUNDEF()
#include "rtc_tools/network_tester/network_tester_packet.pb.h"
-RTC_POP_IGNORING_WUNDEF()
using webrtc::network_tester::packet::NetworkTesterPacket;
#else
class NetworkTesterPacket;
diff --git a/third_party/libwebrtc/rtc_tools/network_tester/packet_sender.h b/third_party/libwebrtc/rtc_tools/network_tester/packet_sender.h
index 7fd500f27c..caa92499f3 100644
--- a/third_party/libwebrtc/rtc_tools/network_tester/packet_sender.h
+++ b/third_party/libwebrtc/rtc_tools/network_tester/packet_sender.h
@@ -17,13 +17,10 @@
#include "api/sequence_checker.h"
#include "api/task_queue/pending_task_safety_flag.h"
#include "api/task_queue/task_queue_factory.h"
-#include "rtc_base/ignore_wundef.h"
#include "rtc_base/system/no_unique_address.h"
#ifdef WEBRTC_NETWORK_TESTER_PROTO
-RTC_PUSH_IGNORING_WUNDEF()
#include "rtc_tools/network_tester/network_tester_packet.pb.h"
-RTC_POP_IGNORING_WUNDEF()
using webrtc::network_tester::packet::NetworkTesterPacket;
#else
class NetworkTesterPacket;
diff --git a/third_party/libwebrtc/rtc_tools/network_tester/server.cc b/third_party/libwebrtc/rtc_tools/network_tester/server.cc
index f0f610a925..a6cd09fdca 100644
--- a/third_party/libwebrtc/rtc_tools/network_tester/server.cc
+++ b/third_party/libwebrtc/rtc_tools/network_tester/server.cc
@@ -9,6 +9,7 @@
*/
#include "rtc_base/null_socket_server.h"
+#include "rtc_base/thread.h"
#include "rtc_tools/network_tester/test_controller.h"
int main(int /*argn*/, char* /*argv*/[]) {
diff --git a/third_party/libwebrtc/rtc_tools/network_tester/test_controller.h b/third_party/libwebrtc/rtc_tools/network_tester/test_controller.h
index b08fbd5dd8..3638c75af1 100644
--- a/third_party/libwebrtc/rtc_tools/network_tester/test_controller.h
+++ b/third_party/libwebrtc/rtc_tools/network_tester/test_controller.h
@@ -22,19 +22,18 @@
#include "api/sequence_checker.h"
#include "p2p/base/basic_packet_socket_factory.h"
#include "rtc_base/async_packet_socket.h"
-#include "rtc_base/ignore_wundef.h"
#include "rtc_base/socket_address.h"
+#include "rtc_base/socket_server.h"
#include "rtc_base/synchronization/mutex.h"
#include "rtc_base/system/no_unique_address.h"
#include "rtc_base/third_party/sigslot/sigslot.h"
+#include "rtc_base/thread.h"
#include "rtc_base/thread_annotations.h"
#include "rtc_tools/network_tester/packet_logger.h"
#include "rtc_tools/network_tester/packet_sender.h"
#ifdef WEBRTC_NETWORK_TESTER_PROTO
-RTC_PUSH_IGNORING_WUNDEF()
#include "rtc_tools/network_tester/network_tester_packet.pb.h"
-RTC_POP_IGNORING_WUNDEF()
using webrtc::network_tester::packet::NetworkTesterPacket;
#else
class NetworkTesterPacket;
diff --git a/third_party/libwebrtc/rtc_tools/rtc_event_log_visualizer/plot_base.h b/third_party/libwebrtc/rtc_tools/rtc_event_log_visualizer/plot_base.h
index e970a6eeb8..aacffa4d59 100644
--- a/third_party/libwebrtc/rtc_tools/rtc_event_log_visualizer/plot_base.h
+++ b/third_party/libwebrtc/rtc_tools/rtc_event_log_visualizer/plot_base.h
@@ -19,11 +19,9 @@
#include "absl/base/attributes.h"
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
-#include "rtc_base/ignore_wundef.h"
-RTC_PUSH_IGNORING_WUNDEF()
+// Generated at build-time by the protobuf compiler.
#include "rtc_tools/rtc_event_log_visualizer/proto/chart.pb.h"
-RTC_POP_IGNORING_WUNDEF()
namespace webrtc {
diff --git a/third_party/libwebrtc/rtc_tools/rtc_event_log_visualizer/plot_protobuf.cc b/third_party/libwebrtc/rtc_tools/rtc_event_log_visualizer/plot_protobuf.cc
deleted file mode 100644
index 0b7a6f28ac..0000000000
--- a/third_party/libwebrtc/rtc_tools/rtc_event_log_visualizer/plot_protobuf.cc
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
- *
- * Use of this source code is governed by a BSD-style license
- * that can be found in the LICENSE file in the root of the source
- * tree. An additional intellectual property rights grant can be found
- * in the file PATENTS. All contributing project authors may
- * be found in the AUTHORS file in the root of the source tree.
- */
-
-#include "rtc_tools/rtc_event_log_visualizer/plot_protobuf.h"
-
-#include <stddef.h>
-
-#include <iostream>
-#include <memory>
-#include <vector>
-
-namespace webrtc {
-
-ProtobufPlot::ProtobufPlot() {}
-
-ProtobufPlot::~ProtobufPlot() {}
-
-void ProtobufPlot::Draw() {}
-
-ProtobufPlotCollection::ProtobufPlotCollection() {}
-
-ProtobufPlotCollection::~ProtobufPlotCollection() {}
-
-void ProtobufPlotCollection::Draw() {
- webrtc::analytics::ChartCollection collection;
- ExportProtobuf(&collection);
- std::cout << collection.SerializeAsString();
-}
-
-Plot* ProtobufPlotCollection::AppendNewPlot() {
- Plot* plot = new ProtobufPlot();
- plots_.push_back(std::unique_ptr<Plot>(plot));
- return plot;
-}
-
-} // namespace webrtc
diff --git a/third_party/libwebrtc/rtc_tools/rtc_event_log_visualizer/plot_protobuf.h b/third_party/libwebrtc/rtc_tools/rtc_event_log_visualizer/plot_protobuf.h
deleted file mode 100644
index fbe68853a3..0000000000
--- a/third_party/libwebrtc/rtc_tools/rtc_event_log_visualizer/plot_protobuf.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
- *
- * Use of this source code is governed by a BSD-style license
- * that can be found in the LICENSE file in the root of the source
- * tree. An additional intellectual property rights grant can be found
- * in the file PATENTS. All contributing project authors may
- * be found in the AUTHORS file in the root of the source tree.
- */
-#ifndef RTC_TOOLS_RTC_EVENT_LOG_VISUALIZER_PLOT_PROTOBUF_H_
-#define RTC_TOOLS_RTC_EVENT_LOG_VISUALIZER_PLOT_PROTOBUF_H_
-
-#include "absl/base/attributes.h"
-#include "rtc_base/ignore_wundef.h"
-RTC_PUSH_IGNORING_WUNDEF()
-#include "rtc_tools/rtc_event_log_visualizer/proto/chart.pb.h"
-RTC_POP_IGNORING_WUNDEF()
-#include "rtc_tools/rtc_event_log_visualizer/plot_base.h"
-
-namespace webrtc {
-
-class ProtobufPlot final : public Plot {
- public:
- ProtobufPlot();
- ~ProtobufPlot() override;
- void Draw() override;
-};
-
-class ABSL_DEPRECATED("Use PlotCollection and ExportProtobuf() instead")
- ProtobufPlotCollection final : public PlotCollection {
- public:
- ProtobufPlotCollection();
- ~ProtobufPlotCollection() override;
- void Draw() override;
- Plot* AppendNewPlot() override;
-};
-
-} // namespace webrtc
-
-#endif // RTC_TOOLS_RTC_EVENT_LOG_VISUALIZER_PLOT_PROTOBUF_H_
diff --git a/third_party/libwebrtc/rtc_tools/rtc_event_log_visualizer/plot_python.cc b/third_party/libwebrtc/rtc_tools/rtc_event_log_visualizer/plot_python.cc
deleted file mode 100644
index b3708110df..0000000000
--- a/third_party/libwebrtc/rtc_tools/rtc_event_log_visualizer/plot_python.cc
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
- *
- * Use of this source code is governed by a BSD-style license
- * that can be found in the LICENSE file in the root of the source
- * tree. An additional intellectual property rights grant can be found
- * in the file PATENTS. All contributing project authors may
- * be found in the AUTHORS file in the root of the source tree.
- */
-
-#include "rtc_tools/rtc_event_log_visualizer/plot_python.h"
-
-#include <stdio.h>
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "rtc_base/checks.h"
-
-namespace webrtc {
-
-PythonPlot::PythonPlot() {}
-
-PythonPlot::~PythonPlot() {}
-
-void PythonPlot::Draw() {
- PrintPythonCode();
-}
-
-PythonPlotCollection::PythonPlotCollection(bool shared_xaxis)
- : shared_xaxis_(shared_xaxis) {}
-
-PythonPlotCollection::~PythonPlotCollection() {}
-
-void PythonPlotCollection::Draw() {
- PrintPythonCode(shared_xaxis_);
-}
-
-Plot* PythonPlotCollection::AppendNewPlot() {
- Plot* plot = new PythonPlot();
- plots_.push_back(std::unique_ptr<Plot>(plot));
- return plot;
-}
-
-} // namespace webrtc
diff --git a/third_party/libwebrtc/rtc_tools/rtc_event_log_visualizer/plot_python.h b/third_party/libwebrtc/rtc_tools/rtc_event_log_visualizer/plot_python.h
deleted file mode 100644
index 6acc436d71..0000000000
--- a/third_party/libwebrtc/rtc_tools/rtc_event_log_visualizer/plot_python.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
- *
- * Use of this source code is governed by a BSD-style license
- * that can be found in the LICENSE file in the root of the source
- * tree. An additional intellectual property rights grant can be found
- * in the file PATENTS. All contributing project authors may
- * be found in the AUTHORS file in the root of the source tree.
- */
-#ifndef RTC_TOOLS_RTC_EVENT_LOG_VISUALIZER_PLOT_PYTHON_H_
-#define RTC_TOOLS_RTC_EVENT_LOG_VISUALIZER_PLOT_PYTHON_H_
-
-#include "absl/base/attributes.h"
-#include "rtc_tools/rtc_event_log_visualizer/plot_base.h"
-
-namespace webrtc {
-
-class PythonPlot final : public Plot {
- public:
- PythonPlot();
- ~PythonPlot() override;
- void Draw() override;
-};
-
-class ABSL_DEPRECATED("Use PlotCollection and PrintPythonCode() instead.")
- PythonPlotCollection final : public PlotCollection {
- public:
- explicit PythonPlotCollection(bool shared_xaxis = false);
- ~PythonPlotCollection() override;
- void Draw() override;
- Plot* AppendNewPlot() override;
-
- private:
- bool shared_xaxis_;
-};
-
-} // namespace webrtc
-
-#endif // RTC_TOOLS_RTC_EVENT_LOG_VISUALIZER_PLOT_PYTHON_H_
diff --git a/third_party/libwebrtc/rtc_tools/rtp_generator/rtp_generator.cc b/third_party/libwebrtc/rtc_tools/rtp_generator/rtp_generator.cc
index e50c125876..0c6d3e3a48 100644
--- a/third_party/libwebrtc/rtc_tools/rtp_generator/rtp_generator.cc
+++ b/third_party/libwebrtc/rtc_tools/rtp_generator/rtp_generator.cc
@@ -14,7 +14,7 @@
#include <memory>
#include <utility>
-#include "api/task_queue/default_task_queue_factory.h"
+#include "api/environment/environment_factory.h"
#include "api/test/create_frame_generator.h"
#include "api/video_codecs/video_decoder_factory_template.h"
#include "api/video_codecs/video_decoder_factory_template_dav1d_adapter.h"
@@ -171,6 +171,7 @@ absl::optional<RtpGeneratorOptions> ParseRtpGeneratorOptionsFromFile(
RtpGenerator::RtpGenerator(const RtpGeneratorOptions& options)
: options_(options),
+ env_(CreateEnvironment()),
video_encoder_factory_(
std::make_unique<webrtc::VideoEncoderFactoryTemplate<
webrtc::LibvpxVp8EncoderTemplateAdapter,
@@ -183,9 +184,7 @@ RtpGenerator::RtpGenerator(const RtpGeneratorOptions& options)
webrtc::Dav1dDecoderTemplateAdapter>>()),
video_bitrate_allocator_factory_(
CreateBuiltinVideoBitrateAllocatorFactory()),
- event_log_(std::make_unique<RtcEventLogNull>()),
- call_(Call::Create(CallConfig(event_log_.get()))),
- task_queue_(CreateDefaultTaskQueueFactory()) {
+ call_(Call::Create(CallConfig(env_))) {
constexpr int kMinBitrateBps = 30000; // 30 Kbps
constexpr int kMaxBitrateBps = 2500000; // 2.5 Mbps
@@ -246,11 +245,11 @@ RtpGenerator::RtpGenerator(const RtpGeneratorOptions& options)
// Setup the fake video stream for this.
std::unique_ptr<test::FrameGeneratorCapturer> frame_generator =
std::make_unique<test::FrameGeneratorCapturer>(
- Clock::GetRealTimeClock(),
+ &env_.clock(),
test::CreateSquareFrameGenerator(send_config.video_width,
send_config.video_height,
absl::nullopt, absl::nullopt),
- send_config.video_fps, *task_queue_);
+ send_config.video_fps, env_.task_queue_factory());
frame_generator->Init();
VideoSendStream* video_send_stream = call_->CreateVideoSendStream(
diff --git a/third_party/libwebrtc/rtc_tools/rtp_generator/rtp_generator.h b/third_party/libwebrtc/rtc_tools/rtp_generator/rtp_generator.h
index e9d23362ef..2f178d3466 100644
--- a/third_party/libwebrtc/rtc_tools/rtp_generator/rtp_generator.h
+++ b/third_party/libwebrtc/rtc_tools/rtp_generator/rtp_generator.h
@@ -16,9 +16,8 @@
#include <vector>
#include "api/call/transport.h"
+#include "api/environment/environment.h"
#include "api/media_types.h"
-#include "api/rtc_event_log/rtc_event_log.h"
-#include "api/task_queue/task_queue_factory.h"
#include "api/video/builtin_video_bitrate_allocator_factory.h"
#include "api/video_codecs/video_decoder_factory.h"
#include "api/video_codecs/video_encoder_factory.h"
@@ -104,18 +103,17 @@ class RtpGenerator final : public webrtc::Transport {
test::RtpPacket DataToRtpPacket(const uint8_t* packet, size_t packet_len);
const RtpGeneratorOptions options_;
+ const Environment env_;
std::unique_ptr<VideoEncoderFactory> video_encoder_factory_;
std::unique_ptr<VideoDecoderFactory> video_decoder_factory_;
std::unique_ptr<VideoBitrateAllocatorFactory>
video_bitrate_allocator_factory_;
- std::unique_ptr<RtcEventLog> event_log_;
std::unique_ptr<Call> call_;
std::unique_ptr<test::RtpFileWriter> rtp_dump_writer_;
std::vector<std::unique_ptr<test::FrameGeneratorCapturer>> frame_generators_;
std::vector<VideoSendStream*> video_send_streams_;
std::vector<uint32_t> durations_ms_;
uint32_t start_ms_ = 0;
- std::unique_ptr<TaskQueueFactory> task_queue_;
};
} // namespace webrtc
diff --git a/third_party/libwebrtc/rtc_tools/unpack_aecdump/unpack.cc b/third_party/libwebrtc/rtc_tools/unpack_aecdump/unpack.cc
index a43fe75b36..e8bb587cfa 100644
--- a/third_party/libwebrtc/rtc_tools/unpack_aecdump/unpack.cc
+++ b/third_party/libwebrtc/rtc_tools/unpack_aecdump/unpack.cc
@@ -29,13 +29,11 @@
#include "common_audio/wav_file.h"
#include "modules/audio_processing/test/protobuf_utils.h"
#include "rtc_base/checks.h"
-#include "rtc_base/ignore_wundef.h"
#include "rtc_base/strings/string_builder.h"
#include "rtc_base/system/arch.h"
-RTC_PUSH_IGNORING_WUNDEF()
+// Generated at build-time by the protobuf compiler.
#include "modules/audio_processing/debug.pb.h"
-RTC_POP_IGNORING_WUNDEF()
ABSL_FLAG(std::string,
input_file,
diff --git a/third_party/libwebrtc/rtc_tools/video_file_reader.h b/third_party/libwebrtc/rtc_tools/video_file_reader.h
index 1293beba2f..270955b5f5 100644
--- a/third_party/libwebrtc/rtc_tools/video_file_reader.h
+++ b/third_party/libwebrtc/rtc_tools/video_file_reader.h
@@ -16,16 +16,16 @@
#include <iterator>
#include <string>
+#include "api/ref_count.h"
#include "api/scoped_refptr.h"
#include "api/video/video_frame_buffer.h"
-#include "rtc_base/ref_count.h"
namespace webrtc {
namespace test {
// Iterable class representing a sequence of I420 buffers. This class is not
// thread safe because it is expected to be backed by a file.
-class Video : public rtc::RefCountInterface {
+class Video : public RefCountInterface {
public:
class Iterator {
public:
diff --git a/third_party/libwebrtc/rtc_tools/video_replay.cc b/third_party/libwebrtc/rtc_tools/video_replay.cc
index b19850ed57..52c8d68048 100644
--- a/third_party/libwebrtc/rtc_tools/video_replay.cc
+++ b/third_party/libwebrtc/rtc_tools/video_replay.cc
@@ -16,10 +16,10 @@
#include "absl/flags/flag.h"
#include "absl/flags/parse.h"
+#include "api/environment/environment.h"
+#include "api/environment/environment_factory.h"
#include "api/field_trials.h"
#include "api/media_types.h"
-#include "api/rtc_event_log/rtc_event_log.h"
-#include "api/task_queue/default_task_queue_factory.h"
#include "api/test/video/function_video_decoder_factory.h"
#include "api/transport/field_trial_based_config.h"
#include "api/units/timestamp.h"
@@ -36,7 +36,6 @@
#include "rtc_base/checks.h"
#include "rtc_base/string_to_number.h"
#include "rtc_base/strings/json.h"
-#include "rtc_base/time_utils.h"
#include "system_wrappers/include/clock.h"
#include "system_wrappers/include/sleep.h"
#include "test/call_config_utils.h"
@@ -478,26 +477,21 @@ class RtpReplayer final {
bool simulated_time)
: replay_config_path_(replay_config_path),
rtp_dump_path_(rtp_dump_path),
- field_trials_(std::move(field_trials)),
+ time_sim_(simulated_time
+ ? std::make_unique<GlobalSimulatedTimeController>(
+ Timestamp::Millis(1 << 30))
+ : nullptr),
+ env_(CreateEnvironment(
+ std::move(field_trials),
+ time_sim_ ? time_sim_->GetTaskQueueFactory() : nullptr,
+ time_sim_ ? time_sim_->GetClock() : nullptr)),
rtp_reader_(CreateRtpReader(rtp_dump_path_)) {
- TaskQueueFactory* task_queue_factory;
- if (simulated_time) {
- time_sim_ = std::make_unique<GlobalSimulatedTimeController>(
- Timestamp::Millis(1 << 30));
- task_queue_factory = time_sim_->GetTaskQueueFactory();
- } else {
- task_queue_factory_ = CreateDefaultTaskQueueFactory(field_trials_.get()),
- task_queue_factory = task_queue_factory_.get();
- }
- worker_thread_ =
- std::make_unique<rtc::TaskQueue>(task_queue_factory->CreateTaskQueue(
+ worker_thread_ = std::make_unique<rtc::TaskQueue>(
+ env_.task_queue_factory().CreateTaskQueue(
"worker_thread", TaskQueueFactory::Priority::NORMAL));
rtc::Event event;
worker_thread_->PostTask([&]() {
- CallConfig call_config(&event_log_);
- call_config.trials = field_trials_.get();
- call_config.task_queue_factory = task_queue_factory;
- call_ = Call::Create(call_config);
+ call_ = Call::Create(CallConfig(env_));
// Creation of the streams must happen inside a task queue because it is
// resued as a worker thread.
@@ -655,10 +649,7 @@ class RtpReplayer final {
}
}
- int64_t CurrentTimeMs() {
- return time_sim_ ? time_sim_->GetClock()->TimeInMilliseconds()
- : rtc::TimeMillis();
- }
+ int64_t CurrentTimeMs() { return env_.clock().CurrentTime().ms(); }
void SleepOrAdvanceTime(int64_t duration_ms) {
if (time_sim_) {
@@ -670,10 +661,8 @@ class RtpReplayer final {
const std::string replay_config_path_;
const std::string rtp_dump_path_;
- RtcEventLogNull event_log_;
- std::unique_ptr<FieldTrialsView> field_trials_;
std::unique_ptr<GlobalSimulatedTimeController> time_sim_;
- std::unique_ptr<TaskQueueFactory> task_queue_factory_;
+ Environment env_;
std::unique_ptr<rtc::TaskQueue> worker_thread_;
std::unique_ptr<Call> call_;
std::unique_ptr<test::RtpFileReader> rtp_reader_;
diff --git a/third_party/libwebrtc/sdk/BUILD.gn b/third_party/libwebrtc/sdk/BUILD.gn
index eea26dc31d..586d4d1911 100644
--- a/third_party/libwebrtc/sdk/BUILD.gn
+++ b/third_party/libwebrtc/sdk/BUILD.gn
@@ -254,7 +254,7 @@ if (is_ios || is_mac) {
visibility = [ "*" ]
allow_poison = [
"audio_codecs", # TODO(bugs.webrtc.org/8396): Remove.
- "default_task_queue",
+ "environment_construction",
]
sources = [
"objc/components/renderer/opengl/RTCDisplayLinkTimer.h",
@@ -482,7 +482,7 @@ if (is_ios || is_mac) {
rtc_library("audio_device_objc") {
visibility = [ "*" ]
- allow_poison = [ "default_task_queue" ]
+ allow_poison = [ "environment_construction" ]
sources = [
"objc/native/src/objc_audio_device.h",
"objc/native/src/objc_audio_device.mm",
@@ -515,7 +515,7 @@ if (is_ios || is_mac) {
rtc_library("objc_audio_device_module") {
visibility = [ "*" ]
- allow_poison = [ "default_task_queue" ]
+ allow_poison = [ "environment_construction" ]
sources = [
"objc/native/api/objc_audio_device_module.h",
"objc/native/api/objc_audio_device_module.mm",
@@ -601,7 +601,7 @@ if (is_ios || is_mac) {
visibility = [ "*" ]
allow_poison = [
"audio_codecs", # TODO(bugs.webrtc.org/8396): Remove.
- "default_task_queue",
+ "environment_construction",
]
sources = [
"objc/components/renderer/metal/RTCMTLI420Renderer.h",
@@ -925,7 +925,7 @@ if (is_ios || is_mac) {
visibility = [ "*" ]
allow_poison = [
"audio_codecs", # TODO(bugs.webrtc.org/8396): Remove.
- "default_task_queue",
+ "environment_construction",
]
configs += [ "..:no_global_constructors" ]
sources = [
@@ -1056,6 +1056,7 @@ if (is_ios || is_mac) {
":videosource_objc",
":videotoolbox_objc",
"../api:dtmf_sender_interface",
+ "../api:enable_media",
"../api:libjingle_peerconnection_api",
"../api:media_stream_interface",
"../api:rtc_event_log_output_file",
@@ -1076,7 +1077,6 @@ if (is_ios || is_mac) {
"../api/video_codecs:video_codecs_api",
"../common_video",
"../media:media_constants",
- "../media:rtc_audio_video",
"../media:rtc_media_base",
"../modules/audio_device:audio_device_api",
"../modules/audio_processing",
diff --git a/third_party/libwebrtc/sdk/android/api/org/webrtc/EglRenderer.java b/third_party/libwebrtc/sdk/android/api/org/webrtc/EglRenderer.java
index 921abe66e4..0a0479b311 100644
--- a/third_party/libwebrtc/sdk/android/api/org/webrtc/EglRenderer.java
+++ b/third_party/libwebrtc/sdk/android/api/org/webrtc/EglRenderer.java
@@ -332,7 +332,6 @@ public class EglRenderer implements VideoSink {
* Set if the video stream should be mirrored horizontally or not.
*/
public void setMirror(final boolean mirror) {
- logD("setMirrorHorizontally: " + mirror);
synchronized (layoutLock) {
this.mirrorHorizontally = mirror;
}
@@ -342,7 +341,6 @@ public class EglRenderer implements VideoSink {
* Set if the video stream should be mirrored vertically or not.
*/
public void setMirrorVertically(final boolean mirrorVertically) {
- logD("setMirrorVertically: " + mirrorVertically);
synchronized (layoutLock) {
this.mirrorVertically = mirrorVertically;
}
@@ -353,7 +351,6 @@ public class EglRenderer implements VideoSink {
* Set this to 0 to disable cropping.
*/
public void setLayoutAspectRatio(float layoutAspectRatio) {
- logD("setLayoutAspectRatio: " + layoutAspectRatio);
synchronized (layoutLock) {
this.layoutAspectRatio = layoutAspectRatio;
}
@@ -366,7 +363,6 @@ public class EglRenderer implements VideoSink {
* reduction.
*/
public void setFpsReduction(float fps) {
- logD("setFpsReduction: " + fps);
synchronized (fpsReductionLock) {
final long previousRenderPeriodNs = minRenderPeriodNs;
if (fps <= 0) {
diff --git a/third_party/libwebrtc/sdk/android/api/org/webrtc/RenderSynchronizer.java b/third_party/libwebrtc/sdk/android/api/org/webrtc/RenderSynchronizer.java
index b1ade84298..c89f798c9c 100644
--- a/third_party/libwebrtc/sdk/android/api/org/webrtc/RenderSynchronizer.java
+++ b/third_party/libwebrtc/sdk/android/api/org/webrtc/RenderSynchronizer.java
@@ -10,8 +10,11 @@
package org.webrtc;
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
import android.os.Handler;
import android.os.Looper;
+import android.os.Trace;
import android.view.Choreographer;
import androidx.annotation.GuardedBy;
import java.util.List;
@@ -100,8 +103,15 @@ public final class RenderSynchronizer {
}
}
+ private void traceRenderWindowChange() {
+ if (VERSION.SDK_INT >= VERSION_CODES.Q) {
+ Trace.setCounter("RenderWindow", renderWindowOpen ? 1 : 0);
+ }
+ }
+
private void openRenderWindow() {
renderWindowOpen = true;
+ traceRenderWindowChange();
for (Listener listener : listeners) {
listener.onRenderWindowOpen();
}
@@ -109,6 +119,7 @@ public final class RenderSynchronizer {
private void closeRenderWindow() {
renderWindowOpen = false;
+ traceRenderWindowChange();
for (Listener listener : listeners) {
listener.onRenderWindowClose();
}
diff --git a/third_party/libwebrtc/sdk/android/src/java/org/webrtc/HardwareVideoEncoder.java b/third_party/libwebrtc/sdk/android/src/java/org/webrtc/HardwareVideoEncoder.java
index 94dfdf0728..4512d9a049 100644
--- a/third_party/libwebrtc/sdk/android/src/java/org/webrtc/HardwareVideoEncoder.java
+++ b/third_party/libwebrtc/sdk/android/src/java/org/webrtc/HardwareVideoEncoder.java
@@ -292,7 +292,7 @@ class HardwareVideoEncoder implements VideoEncoder {
updateInputFormat(codec.getInputFormat());
codec.start();
- } catch (IllegalStateException e) {
+ } catch (IllegalArgumentException | IllegalStateException e) {
Logging.e(TAG, "initEncodeInternal failed", e);
release();
return VideoCodecStatus.FALLBACK_SOFTWARE;
diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCPeerConnectionFactory.mm b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCPeerConnectionFactory.mm
index 62b55543d4..15f9eb9ee4 100644
--- a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCPeerConnectionFactory.mm
+++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCPeerConnectionFactory.mm
@@ -32,12 +32,12 @@
#include "api/audio_codecs/builtin_audio_decoder_factory.h"
#include "api/audio_codecs/builtin_audio_encoder_factory.h"
+#include "api/enable_media.h"
#include "api/rtc_event_log/rtc_event_log_factory.h"
#include "api/task_queue/default_task_queue_factory.h"
#include "api/transport/field_trial_based_config.h"
#import "components/video_codec/RTCVideoDecoderFactoryH264.h"
#import "components/video_codec/RTCVideoEncoderFactoryH264.h"
-#include "media/engine/webrtc_media_engine.h"
#include "modules/audio_device/include/audio_device.h"
#include "modules/audio_processing/include/audio_processing.h"
@@ -195,21 +195,17 @@
dependencies.trials = std::make_unique<webrtc::FieldTrialBasedConfig>();
dependencies.task_queue_factory =
webrtc::CreateDefaultTaskQueueFactory(dependencies.trials.get());
- cricket::MediaEngineDependencies media_deps;
- media_deps.adm = std::move(audioDeviceModule);
- media_deps.task_queue_factory = dependencies.task_queue_factory.get();
- media_deps.audio_encoder_factory = std::move(audioEncoderFactory);
- media_deps.audio_decoder_factory = std::move(audioDecoderFactory);
- media_deps.video_encoder_factory = std::move(videoEncoderFactory);
- media_deps.video_decoder_factory = std::move(videoDecoderFactory);
+ dependencies.adm = std::move(audioDeviceModule);
+ dependencies.audio_encoder_factory = std::move(audioEncoderFactory);
+ dependencies.audio_decoder_factory = std::move(audioDecoderFactory);
+ dependencies.video_encoder_factory = std::move(videoEncoderFactory);
+ dependencies.video_decoder_factory = std::move(videoDecoderFactory);
if (audioProcessingModule) {
- media_deps.audio_processing = std::move(audioProcessingModule);
+ dependencies.audio_processing = std::move(audioProcessingModule);
} else {
- media_deps.audio_processing = webrtc::AudioProcessingBuilder().Create();
+ dependencies.audio_processing = webrtc::AudioProcessingBuilder().Create();
}
- media_deps.trials = dependencies.trials.get();
- dependencies.media_engine = cricket::CreateMediaEngine(std::move(media_deps));
- dependencies.call_factory = webrtc::CreateCallFactory();
+ webrtc::EnableMedia(dependencies);
dependencies.event_log_factory =
std::make_unique<webrtc::RtcEventLogFactory>(dependencies.task_queue_factory.get());
dependencies.network_controller_factory = std::move(networkControllerFactory);
diff --git a/third_party/libwebrtc/sdk/objc/components/audio/RTCAudioSession.mm b/third_party/libwebrtc/sdk/objc/components/audio/RTCAudioSession.mm
index f6b91d5409..641d2ed8c7 100644
--- a/third_party/libwebrtc/sdk/objc/components/audio/RTCAudioSession.mm
+++ b/third_party/libwebrtc/sdk/objc/components/audio/RTCAudioSession.mm
@@ -399,8 +399,10 @@ ABSL_CONST_INIT thread_local bool mutex_locked = false;
// Set isActive and decrement activation count on deactivation
// whether or not it succeeded.
if (!active) {
- self.isActive = active;
- [self notifyDidSetActive:active];
+ if (shouldSetActive) {
+ self.isActive = active;
+ [self notifyDidSetActive:active];
+ }
[self decrementActivationCount];
}
RTCLog(@"Number of current activations: %d", _activationCount.load());
diff --git a/third_party/libwebrtc/system_wrappers/denormal_disabler_gn/moz.build b/third_party/libwebrtc/system_wrappers/denormal_disabler_gn/moz.build
index 5a007d6343..e4ee1a2ead 100644
--- a/third_party/libwebrtc/system_wrappers/denormal_disabler_gn/moz.build
+++ b/third_party/libwebrtc/system_wrappers/denormal_disabler_gn/moz.build
@@ -188,7 +188,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -198,10 +197,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/system_wrappers/field_trial_gn/moz.build b/third_party/libwebrtc/system_wrappers/field_trial_gn/moz.build
index e23512e425..2535fad4cc 100644
--- a/third_party/libwebrtc/system_wrappers/field_trial_gn/moz.build
+++ b/third_party/libwebrtc/system_wrappers/field_trial_gn/moz.build
@@ -195,7 +195,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -205,10 +204,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/system_wrappers/metrics_gn/moz.build b/third_party/libwebrtc/system_wrappers/metrics_gn/moz.build
index aa35ad0401..484fc0d3ca 100644
--- a/third_party/libwebrtc/system_wrappers/metrics_gn/moz.build
+++ b/third_party/libwebrtc/system_wrappers/metrics_gn/moz.build
@@ -188,7 +188,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -198,10 +197,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/system_wrappers/system_wrappers_gn/moz.build b/third_party/libwebrtc/system_wrappers/system_wrappers_gn/moz.build
index e53ea6babd..3b2e26249e 100644
--- a/third_party/libwebrtc/system_wrappers/system_wrappers_gn/moz.build
+++ b/third_party/libwebrtc/system_wrappers/system_wrappers_gn/moz.build
@@ -213,7 +213,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -223,10 +222,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/test/BUILD.gn b/third_party/libwebrtc/test/BUILD.gn
index f7980f941d..854530c01e 100644
--- a/third_party/libwebrtc/test/BUILD.gn
+++ b/third_party/libwebrtc/test/BUILD.gn
@@ -724,6 +724,7 @@ if (rtc_include_tests) {
":test_main",
":test_support",
":test_support_test_artifacts",
+ ":video_codec_tester",
":video_test_common",
":video_test_support",
":y4m_frame_generator",
@@ -731,11 +732,15 @@ if (rtc_include_tests) {
"../api:create_frame_generator",
"../api:create_simulcast_test_fixture_api",
"../api:frame_generator_api",
+ "../api:mock_video_codec_factory",
+ "../api:mock_video_decoder",
+ "../api:mock_video_encoder",
"../api:scoped_refptr",
"../api:simulcast_test_fixture_api",
"../api/task_queue:task_queue_test",
"../api/test/video:function_video_factory",
"../api/test/video:video_frame_writer",
+ "../api/units:data_rate",
"../api/units:time_delta",
"../api/video:encoded_image",
"../api/video:video_frame",
@@ -752,6 +757,7 @@ if (rtc_include_tests) {
"../modules/video_coding:webrtc_h264",
"../modules/video_coding:webrtc_vp8",
"../modules/video_coding:webrtc_vp9",
+ "../modules/video_coding/svc:scalability_mode_util",
"../rtc_base:criticalsection",
"../rtc_base:rtc_event",
"../rtc_base:rtc_task_queue",
@@ -765,6 +771,7 @@ if (rtc_include_tests) {
"scenario:scenario_unittests",
"time_controller:time_controller",
"time_controller:time_controller_unittests",
+ "//third_party/libyuv",
]
absl_deps = [
"//third_party/abseil-cpp/absl/flags:flag",
@@ -789,6 +796,7 @@ if (rtc_include_tests) {
"testsupport/y4m_frame_writer_unittest.cc",
"testsupport/yuv_frame_reader_unittest.cc",
"testsupport/yuv_frame_writer_unittest.cc",
+ "video_codec_tester_unittest.cc",
]
if (rtc_enable_protobuf) {
@@ -1287,9 +1295,10 @@ if (!build_with_chromium) {
"../api:transport_api",
"../api/audio_codecs:builtin_audio_decoder_factory",
"../api/audio_codecs:builtin_audio_encoder_factory",
+ "../api/environment",
+ "../api/environment:environment_factory",
"../api/rtc_event_log",
"../api/task_queue",
- "../api/task_queue:default_task_queue_factory",
"../api/test/video:function_video_factory",
"../api/transport:field_trial_based_config",
"../api/units:time_delta",
@@ -1378,3 +1387,47 @@ rtc_library("fake_encoded_frame") {
]
absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ]
}
+
+rtc_library("video_codec_tester") {
+ testonly = true
+ sources = [
+ "video_codec_tester.cc",
+ "video_codec_tester.h",
+ ]
+ deps = [
+ "../api:array_view",
+ "../api/numerics:numerics",
+ "../api/test/metrics:metric",
+ "../api/test/metrics:metrics_logger",
+ "../api/units:data_rate",
+ "../api/units:data_size",
+ "../api/units:frequency",
+ "../api/units:time_delta",
+ "../api/units:timestamp",
+ "../api/video:builtin_video_bitrate_allocator_factory",
+ "../api/video:encoded_image",
+ "../api/video:resolution",
+ "../api/video:video_bitrate_allocator",
+ "../api/video:video_frame",
+ "../api/video_codecs:video_codecs_api",
+ "../media:media_constants",
+ "../modules/video_coding:video_codec_interface",
+ "../modules/video_coding:video_coding_utility",
+ "../modules/video_coding:webrtc_vp9_helpers",
+ "../modules/video_coding/codecs/av1:av1_svc_config",
+ "../modules/video_coding/svc:scalability_mode_util",
+ "../rtc_base:checks",
+ "../rtc_base:logging",
+ "../rtc_base:rtc_event",
+ "../rtc_base:stringutils",
+ "../rtc_base:task_queue_for_test",
+ "../rtc_base:timeutils",
+ "../rtc_base/synchronization:mutex",
+ "../system_wrappers",
+ "../test:fileutils",
+ "../test:video_test_support",
+ "//third_party/libyuv",
+ ]
+
+ absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ]
+}
diff --git a/third_party/libwebrtc/test/call_test.cc b/third_party/libwebrtc/test/call_test.cc
index 2b71608ac6..09099cccd6 100644
--- a/third_party/libwebrtc/test/call_test.cc
+++ b/third_party/libwebrtc/test/call_test.cc
@@ -15,7 +15,8 @@
#include "api/audio_codecs/builtin_audio_decoder_factory.h"
#include "api/audio_codecs/builtin_audio_encoder_factory.h"
-#include "api/task_queue/default_task_queue_factory.h"
+#include "api/environment/environment.h"
+#include "api/environment/environment_factory.h"
#include "api/task_queue/task_queue_base.h"
#include "api/test/create_frame_generator.h"
#include "api/video/builtin_video_bitrate_allocator_factory.h"
@@ -38,19 +39,18 @@ namespace webrtc {
namespace test {
CallTest::CallTest()
- : clock_(Clock::GetRealTimeClock()),
- task_queue_factory_(CreateDefaultTaskQueueFactory()),
- send_event_log_(std::make_unique<RtcEventLogNull>()),
- recv_event_log_(std::make_unique<RtcEventLogNull>()),
+ : env_(CreateEnvironment(&field_trials_)),
+ send_env_(env_),
+ recv_env_(env_),
audio_send_config_(/*send_transport=*/nullptr),
audio_send_stream_(nullptr),
frame_generator_capturer_(nullptr),
fake_encoder_factory_([this]() {
std::unique_ptr<FakeEncoder> fake_encoder;
if (video_encoder_configs_[0].codec_type == kVideoCodecVP8) {
- fake_encoder = std::make_unique<FakeVp8Encoder>(clock_);
+ fake_encoder = std::make_unique<FakeVp8Encoder>(&env_.clock());
} else {
- fake_encoder = std::make_unique<FakeEncoder>(clock_);
+ fake_encoder = std::make_unique<FakeEncoder>(&env_.clock());
}
fake_encoder->SetMaxBitrate(fake_encoder_max_bitrate_);
return fake_encoder;
@@ -62,12 +62,24 @@ CallTest::CallTest()
num_flexfec_streams_(0),
audio_decoder_factory_(CreateBuiltinAudioDecoderFactory()),
audio_encoder_factory_(CreateBuiltinAudioEncoderFactory()),
- task_queue_(task_queue_factory_->CreateTaskQueue(
+ task_queue_(env_.task_queue_factory().CreateTaskQueue(
"CallTestTaskQueue",
TaskQueueFactory::Priority::NORMAL)) {}
CallTest::~CallTest() = default;
+void CallTest::SetSendEventLog(std::unique_ptr<RtcEventLog> event_log) {
+ EnvironmentFactory f(env_);
+ f.Set(std::move(event_log));
+ send_env_ = f.Create();
+}
+
+void CallTest::SetRecvEventLog(std::unique_ptr<RtcEventLog> event_log) {
+ EnvironmentFactory f(env_);
+ f.Set(std::move(event_log));
+ recv_env_ = f.Create();
+}
+
void CallTest::RegisterRtpExtension(const RtpExtension& extension) {
for (const RtpExtension& registered_extension : rtp_extensions_) {
if (registered_extension.id == extension.id) {
@@ -97,7 +109,7 @@ void CallTest::RunBaseTest(BaseTest* test) {
num_audio_streams_ = test->GetNumAudioStreams();
num_flexfec_streams_ = test->GetNumFlexfecStreams();
RTC_DCHECK(num_video_streams_ > 0 || num_audio_streams_ > 0);
- CallConfig send_config(send_event_log_.get());
+ CallConfig send_config = SendCallConfig();
test->ModifySenderBitrateConfig(&send_config.bitrate_config);
if (num_audio_streams_ > 0) {
CreateFakeAudioDevices(test->CreateCapturer(), test->CreateRenderer());
@@ -117,7 +129,7 @@ void CallTest::RunBaseTest(BaseTest* test) {
}
CreateSenderCall(send_config);
if (test->ShouldCreateReceivers()) {
- CallConfig recv_config(recv_event_log_.get());
+ CallConfig recv_config = RecvCallConfig();
test->ModifyReceiverBitrateConfig(&recv_config.bitrate_config);
if (num_audio_streams_ > 0) {
AudioState::Config audio_state_config;
@@ -206,9 +218,20 @@ void CallTest::RunBaseTest(BaseTest* test) {
});
}
+CallConfig CallTest::SendCallConfig() const {
+ CallConfig sender_config(send_env_);
+ sender_config.network_state_predictor_factory =
+ network_state_predictor_factory_.get();
+ sender_config.network_controller_factory = network_controller_factory_.get();
+ return sender_config;
+}
+
+CallConfig CallTest::RecvCallConfig() const {
+ return CallConfig(recv_env_);
+}
+
void CallTest::CreateCalls() {
- CreateCalls(CallConfig(send_event_log_.get()),
- CallConfig(recv_event_log_.get()));
+ CreateCalls(SendCallConfig(), RecvCallConfig());
}
void CallTest::CreateCalls(const CallConfig& sender_config,
@@ -218,24 +241,15 @@ void CallTest::CreateCalls(const CallConfig& sender_config,
}
void CallTest::CreateSenderCall() {
- CreateSenderCall(CallConfig(send_event_log_.get()));
+ CreateSenderCall(SendCallConfig());
}
void CallTest::CreateSenderCall(const CallConfig& config) {
- auto sender_config = config;
- sender_config.task_queue_factory = task_queue_factory_.get();
- sender_config.network_state_predictor_factory =
- network_state_predictor_factory_.get();
- sender_config.network_controller_factory = network_controller_factory_.get();
- sender_config.trials = &field_trials_;
- sender_call_ = Call::Create(sender_config);
+ sender_call_ = Call::Create(config);
}
void CallTest::CreateReceiverCall(const CallConfig& config) {
- auto receiver_config = config;
- receiver_config.task_queue_factory = task_queue_factory_.get();
- receiver_config.trials = &field_trials_;
- receiver_call_ = Call::Create(receiver_config);
+ receiver_call_ = Call::Create(config);
}
void CallTest::DestroyCalls() {
@@ -489,7 +503,7 @@ void CallTest::CreateFrameGeneratorCapturerWithDrift(Clock* clock,
clock,
test::CreateSquareFrameGenerator(width, height, absl::nullopt,
absl::nullopt),
- framerate * speed, *task_queue_factory_);
+ framerate * speed, env_.task_queue_factory());
frame_generator_capturer_ = frame_generator_capturer.get();
frame_generator_capturer->Init();
video_sources_.push_back(std::move(frame_generator_capturer));
@@ -502,10 +516,10 @@ void CallTest::CreateFrameGeneratorCapturer(int framerate,
video_sources_.clear();
auto frame_generator_capturer =
std::make_unique<test::FrameGeneratorCapturer>(
- clock_,
+ &env_.clock(),
test::CreateSquareFrameGenerator(width, height, absl::nullopt,
absl::nullopt),
- framerate, *task_queue_factory_);
+ framerate, env_.task_queue_factory());
frame_generator_capturer_ = frame_generator_capturer.get();
frame_generator_capturer->Init();
video_sources_.push_back(std::move(frame_generator_capturer));
@@ -516,9 +530,9 @@ void CallTest::CreateFakeAudioDevices(
std::unique_ptr<TestAudioDeviceModule::Capturer> capturer,
std::unique_ptr<TestAudioDeviceModule::Renderer> renderer) {
fake_send_audio_device_ = TestAudioDeviceModule::Create(
- task_queue_factory_.get(), std::move(capturer), nullptr, 1.f);
+ &env_.task_queue_factory(), std::move(capturer), nullptr, 1.f);
fake_recv_audio_device_ = TestAudioDeviceModule::Create(
- task_queue_factory_.get(), nullptr, std::move(renderer), 1.f);
+ &env_.task_queue_factory(), nullptr, std::move(renderer), 1.f);
}
void CallTest::CreateVideoStreams() {
diff --git a/third_party/libwebrtc/test/call_test.h b/third_party/libwebrtc/test/call_test.h
index 8d2b001f72..decf02f20b 100644
--- a/third_party/libwebrtc/test/call_test.h
+++ b/third_party/libwebrtc/test/call_test.h
@@ -17,6 +17,7 @@
#include "absl/types/optional.h"
#include "api/array_view.h"
+#include "api/environment/environment.h"
#include "api/rtc_event_log/rtc_event_log.h"
#include "api/task_queue/task_queue_base.h"
#include "api/task_queue/task_queue_factory.h"
@@ -52,6 +53,11 @@ class CallTest : public ::testing::Test, public RtpPacketSinkInterface {
static const std::map<uint8_t, MediaType> payload_type_map_;
protected:
+ const Environment& env() const { return env_; }
+
+ void SetSendEventLog(std::unique_ptr<RtcEventLog> event_log);
+ void SetRecvEventLog(std::unique_ptr<RtcEventLog> event_log);
+
void RegisterRtpExtension(const RtpExtension& extension);
// Returns header extensions that can be parsed by the transport.
rtc::ArrayView<const RtpExtension> GetRegisteredExtensions() {
@@ -62,6 +68,9 @@ class CallTest : public ::testing::Test, public RtpPacketSinkInterface {
// to simplify test code.
void RunBaseTest(BaseTest* test);
+ CallConfig SendCallConfig() const;
+ CallConfig RecvCallConfig() const;
+
void CreateCalls();
void CreateCalls(const CallConfig& sender_config,
const CallConfig& receiver_config);
@@ -185,13 +194,11 @@ class CallTest : public ::testing::Test, public RtpPacketSinkInterface {
void OnRtpPacket(const RtpPacketReceived& packet) override;
test::RunLoop loop_;
-
- Clock* const clock_;
test::ScopedKeyValueConfig field_trials_;
+ Environment env_;
+ Environment send_env_;
+ Environment recv_env_;
- std::unique_ptr<TaskQueueFactory> task_queue_factory_;
- std::unique_ptr<webrtc::RtcEventLog> send_event_log_;
- std::unique_ptr<webrtc::RtcEventLog> recv_event_log_;
std::unique_ptr<Call> sender_call_;
std::unique_ptr<PacketTransport> send_transport_;
SimulatedNetworkInterface* send_simulated_network_ = nullptr;
diff --git a/third_party/libwebrtc/test/fuzzers/BUILD.gn b/third_party/libwebrtc/test/fuzzers/BUILD.gn
index 3d2ac7a7e7..43e9a5e922 100644
--- a/third_party/libwebrtc/test/fuzzers/BUILD.gn
+++ b/third_party/libwebrtc/test/fuzzers/BUILD.gn
@@ -12,6 +12,7 @@ import("../../webrtc.gni")
rtc_library("webrtc_fuzzer_main") {
sources = [ "webrtc_fuzzer_main.cc" ]
+ testonly = true
deps = [
"../../rtc_base:logging",
"//testing/libfuzzer:fuzzing_engine_main",
diff --git a/third_party/libwebrtc/test/fuzzers/forward_error_correction_fuzzer.cc b/third_party/libwebrtc/test/fuzzers/forward_error_correction_fuzzer.cc
index 04a459bc71..87b76858bb 100644
--- a/third_party/libwebrtc/test/fuzzers/forward_error_correction_fuzzer.cc
+++ b/third_party/libwebrtc/test/fuzzers/forward_error_correction_fuzzer.cc
@@ -34,7 +34,7 @@ void FuzzOneInput(const uint8_t* data, size_t size) {
ForwardErrorCorrection::CreateFlexfec(kFecSsrc, kMediaSsrc);
// Entropy from fuzzer.
- rtc::ByteBufferReader fuzz_buffer(reinterpret_cast<const char*>(data), size);
+ rtc::ByteBufferReader fuzz_buffer(rtc::MakeArrayView(data, size));
// Initial stream state.
uint16_t media_seqnum;
@@ -75,8 +75,8 @@ void FuzzOneInput(const uint8_t* data, size_t size) {
uint8_t packet_type;
uint8_t packet_loss;
while (true) {
- if (!fuzz_buffer.ReadBytes(reinterpret_cast<char*>(packet_buffer),
- kPacketSize)) {
+ if (!fuzz_buffer.ReadBytes(
+ rtc::ArrayView<uint8_t>(packet_buffer, kPacketSize))) {
return;
}
if (!fuzz_buffer.ReadUInt8(&reordering))
diff --git a/third_party/libwebrtc/test/fuzzers/rtp_frame_reference_finder_fuzzer.cc b/third_party/libwebrtc/test/fuzzers/rtp_frame_reference_finder_fuzzer.cc
index ea66c4a1c8..93673f0b8e 100644
--- a/third_party/libwebrtc/test/fuzzers/rtp_frame_reference_finder_fuzzer.cc
+++ b/third_party/libwebrtc/test/fuzzers/rtp_frame_reference_finder_fuzzer.cc
@@ -23,7 +23,7 @@ class DataReader {
template <typename T>
void CopyTo(T* object) {
- static_assert(std::is_pod<T>(), "");
+ static_assert(std::is_trivial_v<T> && std::is_standard_layout_v<T>, "");
uint8_t* destination = reinterpret_cast<uint8_t*>(object);
size_t object_size = sizeof(T);
size_t num_bytes = std::min(size_ - offset_, object_size);
diff --git a/third_party/libwebrtc/test/fuzzers/stun_parser_fuzzer.cc b/third_party/libwebrtc/test/fuzzers/stun_parser_fuzzer.cc
index 6ca9eac8b2..c8aad8c232 100644
--- a/third_party/libwebrtc/test/fuzzers/stun_parser_fuzzer.cc
+++ b/third_party/libwebrtc/test/fuzzers/stun_parser_fuzzer.cc
@@ -15,14 +15,13 @@
namespace webrtc {
void FuzzOneInput(const uint8_t* data, size_t size) {
- const char* message = reinterpret_cast<const char*>(data);
// Normally we'd check the integrity first, but those checks are
// fuzzed separately in stun_validator_fuzzer.cc. We still want to
// fuzz this target since the integrity checks could be forged by a
// malicious adversary who receives a call.
std::unique_ptr<cricket::IceMessage> stun_msg(new cricket::IceMessage());
- rtc::ByteBufferReader buf(message, size);
+ rtc::ByteBufferReader buf(rtc::MakeArrayView(data, size));
stun_msg->Read(&buf);
stun_msg->ValidateMessageIntegrity("");
}
diff --git a/third_party/libwebrtc/test/fuzzers/utils/BUILD.gn b/third_party/libwebrtc/test/fuzzers/utils/BUILD.gn
index dfb617857c..895e76e939 100644
--- a/third_party/libwebrtc/test/fuzzers/utils/BUILD.gn
+++ b/third_party/libwebrtc/test/fuzzers/utils/BUILD.gn
@@ -15,10 +15,8 @@ rtc_library("rtp_replayer") {
"rtp_replayer.h",
]
deps = [
- "../../../api/rtc_event_log",
- "../../../api/task_queue:default_task_queue_factory",
+ "../../../api/environment:environment_factory",
"../../../api/test/video:function_video_factory",
- "../../../api/transport:field_trial_based_config",
"../../../api/units:timestamp",
"../../../api/video_codecs:video_codecs_api",
"../../../call",
diff --git a/third_party/libwebrtc/test/fuzzers/utils/rtp_replayer.cc b/third_party/libwebrtc/test/fuzzers/utils/rtp_replayer.cc
index f6d7119338..1b1b96803f 100644
--- a/third_party/libwebrtc/test/fuzzers/utils/rtp_replayer.cc
+++ b/third_party/libwebrtc/test/fuzzers/utils/rtp_replayer.cc
@@ -16,8 +16,7 @@
#include <utility>
#include "absl/memory/memory.h"
-#include "api/task_queue/default_task_queue_factory.h"
-#include "api/transport/field_trial_based_config.h"
+#include "api/environment/environment_factory.h"
#include "api/units/timestamp.h"
#include "modules/rtp_rtcp/source/rtp_packet.h"
#include "modules/rtp_rtcp/source/rtp_packet_received.h"
@@ -73,13 +72,7 @@ void RtpReplayer::Replay(
}
// Setup the video streams based on the configuration.
- webrtc::RtcEventLogNull event_log;
- std::unique_ptr<TaskQueueFactory> task_queue_factory =
- CreateDefaultTaskQueueFactory();
- CallConfig call_config(&event_log);
- call_config.task_queue_factory = task_queue_factory.get();
- FieldTrialBasedConfig field_trials;
- call_config.trials = &field_trials;
+ CallConfig call_config(CreateEnvironment());
std::unique_ptr<Call> call(Call::Create(call_config));
SetupVideoStreams(&receive_stream_configs, stream_state.get(), call.get());
diff --git a/third_party/libwebrtc/test/fuzzers/utils/rtp_replayer.h b/third_party/libwebrtc/test/fuzzers/utils/rtp_replayer.h
index ae94a640a5..73220eecfa 100644
--- a/third_party/libwebrtc/test/fuzzers/utils/rtp_replayer.h
+++ b/third_party/libwebrtc/test/fuzzers/utils/rtp_replayer.h
@@ -18,7 +18,6 @@
#include <string>
#include <vector>
-#include "api/rtc_event_log/rtc_event_log.h"
#include "api/test/video/function_video_decoder_factory.h"
#include "api/video_codecs/video_decoder.h"
#include "call/call.h"
diff --git a/third_party/libwebrtc/test/network/BUILD.gn b/third_party/libwebrtc/test/network/BUILD.gn
index 5a6cb31f4b..b8255d35fd 100644
--- a/third_party/libwebrtc/test/network/BUILD.gn
+++ b/third_party/libwebrtc/test/network/BUILD.gn
@@ -76,6 +76,7 @@ rtc_library("emulated_network") {
"../../rtc_base:task_queue_for_test",
"../../rtc_base:threading",
"../../rtc_base/memory:always_valid_pointer",
+ "../../rtc_base/network:received_packet",
"../../rtc_base/synchronization:mutex",
"../../rtc_base/system:no_unique_address",
"../../rtc_base/task_utils:repeating_task",
@@ -117,6 +118,7 @@ if (rtc_include_tests && !build_with_chromium) {
":emulated_network",
"../:test_support",
"../../api:callfactory_api",
+ "../../api:enable_media_with_defaults",
"../../api:libjingle_peerconnection_api",
"../../api:scoped_refptr",
"../../api:simulated_network_api",
@@ -125,7 +127,6 @@ if (rtc_include_tests && !build_with_chromium) {
"../../api/transport:field_trial_based_config",
"../../call:simulated_network",
"../../media:rtc_audio_video",
- "../../media:rtc_media_engine_defaults",
"../../modules/audio_device:test_audio_device_module",
"../../p2p:rtc_p2p",
"../../pc:pc_test_utils",
diff --git a/third_party/libwebrtc/test/network/emulated_turn_server.cc b/third_party/libwebrtc/test/network/emulated_turn_server.cc
index 0bc7ec6e2a..93724ca8a3 100644
--- a/third_party/libwebrtc/test/network/emulated_turn_server.cc
+++ b/third_party/libwebrtc/test/network/emulated_turn_server.cc
@@ -14,6 +14,7 @@
#include <utility>
#include "api/packet_socket_factory.h"
+#include "rtc_base/network/received_packet.h"
#include "rtc_base/strings/string_builder.h"
#include "rtc_base/task_queue_for_test.h"
@@ -22,10 +23,56 @@ namespace {
static const char kTestRealm[] = "example.org";
static const char kTestSoftware[] = "TestTurnServer";
+// A wrapper class for cricket::TurnServer to allocate sockets.
+class PacketSocketFactoryWrapper : public rtc::PacketSocketFactory {
+ public:
+ explicit PacketSocketFactoryWrapper(
+ webrtc::test::EmulatedTURNServer* turn_server)
+ : turn_server_(turn_server) {}
+ ~PacketSocketFactoryWrapper() override {}
+
+ // This method is called from TurnServer when making a TURN ALLOCATION.
+ // It will create a socket on the `peer_` endpoint.
+ rtc::AsyncPacketSocket* CreateUdpSocket(const rtc::SocketAddress& address,
+ uint16_t min_port,
+ uint16_t max_port) override {
+ return turn_server_->CreatePeerSocket();
+ }
+
+ rtc::AsyncListenSocket* CreateServerTcpSocket(
+ const rtc::SocketAddress& local_address,
+ uint16_t min_port,
+ uint16_t max_port,
+ int opts) override {
+ return nullptr;
+ }
+ rtc::AsyncPacketSocket* CreateClientTcpSocket(
+ const rtc::SocketAddress& local_address,
+ const rtc::SocketAddress& remote_address,
+ const rtc::ProxyInfo& proxy_info,
+ const std::string& user_agent,
+ const rtc::PacketSocketTcpOptions& tcp_options) override {
+ return nullptr;
+ }
+ std::unique_ptr<webrtc::AsyncDnsResolverInterface> CreateAsyncDnsResolver()
+ override {
+ return nullptr;
+ }
+
+ private:
+ webrtc::test::EmulatedTURNServer* turn_server_;
+};
+
+} // namespace
+
+namespace webrtc {
+namespace test {
+
// A wrapper class for copying data between an AsyncPacketSocket and a
// EmulatedEndpoint. This is used by the cricket::TurnServer when
// sending data back into the emulated network.
-class AsyncPacketSocketWrapper : public rtc::AsyncPacketSocket {
+class EmulatedTURNServer::AsyncPacketSocketWrapper
+ : public rtc::AsyncPacketSocket {
public:
AsyncPacketSocketWrapper(webrtc::test::EmulatedTURNServer* turn_server,
webrtc::EmulatedEndpoint* endpoint,
@@ -56,6 +103,9 @@ class AsyncPacketSocketWrapper : public rtc::AsyncPacketSocket {
return cb;
}
int Close() override { return 0; }
+ void NotifyPacketReceived(const rtc::ReceivedPacket& packet) {
+ rtc::AsyncPacketSocket::NotifyPacketReceived(packet);
+ }
rtc::AsyncPacketSocket::State GetState() const override {
return rtc::AsyncPacketSocket::STATE_BOUND;
@@ -71,51 +121,6 @@ class AsyncPacketSocketWrapper : public rtc::AsyncPacketSocket {
const rtc::SocketAddress local_address_;
};
-// A wrapper class for cricket::TurnServer to allocate sockets.
-class PacketSocketFactoryWrapper : public rtc::PacketSocketFactory {
- public:
- explicit PacketSocketFactoryWrapper(
- webrtc::test::EmulatedTURNServer* turn_server)
- : turn_server_(turn_server) {}
- ~PacketSocketFactoryWrapper() override {}
-
- // This method is called from TurnServer when making a TURN ALLOCATION.
- // It will create a socket on the `peer_` endpoint.
- rtc::AsyncPacketSocket* CreateUdpSocket(const rtc::SocketAddress& address,
- uint16_t min_port,
- uint16_t max_port) override {
- return turn_server_->CreatePeerSocket();
- }
-
- rtc::AsyncListenSocket* CreateServerTcpSocket(
- const rtc::SocketAddress& local_address,
- uint16_t min_port,
- uint16_t max_port,
- int opts) override {
- return nullptr;
- }
- rtc::AsyncPacketSocket* CreateClientTcpSocket(
- const rtc::SocketAddress& local_address,
- const rtc::SocketAddress& remote_address,
- const rtc::ProxyInfo& proxy_info,
- const std::string& user_agent,
- const rtc::PacketSocketTcpOptions& tcp_options) override {
- return nullptr;
- }
- std::unique_ptr<webrtc::AsyncDnsResolverInterface> CreateAsyncDnsResolver()
- override {
- return nullptr;
- }
-
- private:
- webrtc::test::EmulatedTURNServer* turn_server_;
-};
-
-} // namespace
-
-namespace webrtc {
-namespace test {
-
EmulatedTURNServer::EmulatedTURNServer(std::unique_ptr<rtc::Thread> thread,
EmulatedEndpoint* client,
EmulatedEndpoint* peer)
@@ -170,9 +175,8 @@ void EmulatedTURNServer::OnPacketReceived(webrtc::EmulatedIpPacket packet) {
RTC_DCHECK_RUN_ON(thread_.get());
auto it = sockets_.find(packet.to);
if (it != sockets_.end()) {
- it->second->SignalReadPacket(
- it->second, reinterpret_cast<const char*>(packet.cdata()),
- packet.size(), packet.from, packet.arrival_time.ms());
+ it->second->NotifyPacketReceived(
+ rtc::ReceivedPacket(packet.data, packet.from, packet.arrival_time));
}
});
}
diff --git a/third_party/libwebrtc/test/network/emulated_turn_server.h b/third_party/libwebrtc/test/network/emulated_turn_server.h
index 9cb0ceabf6..de5d266897 100644
--- a/third_party/libwebrtc/test/network/emulated_turn_server.h
+++ b/third_party/libwebrtc/test/network/emulated_turn_server.h
@@ -84,7 +84,8 @@ class EmulatedTURNServer : public EmulatedTURNServerInterface,
EmulatedEndpoint* const client_;
EmulatedEndpoint* const peer_;
std::unique_ptr<cricket::TurnServer> turn_server_ RTC_GUARDED_BY(&thread_);
- std::map<rtc::SocketAddress, rtc::AsyncPacketSocket*> sockets_
+ class AsyncPacketSocketWrapper;
+ std::map<rtc::SocketAddress, AsyncPacketSocketWrapper*> sockets_
RTC_GUARDED_BY(&thread_);
// Wraps a EmulatedEndpoint in a AsyncPacketSocket to bridge interaction
diff --git a/third_party/libwebrtc/test/network/network_emulation_pc_unittest.cc b/third_party/libwebrtc/test/network/network_emulation_pc_unittest.cc
index 51a45a8234..09d3946747 100644
--- a/third_party/libwebrtc/test/network/network_emulation_pc_unittest.cc
+++ b/third_party/libwebrtc/test/network/network_emulation_pc_unittest.cc
@@ -11,7 +11,7 @@
#include <cstdint>
#include <memory>
-#include "api/call/call_factory_interface.h"
+#include "api/enable_media_with_defaults.h"
#include "api/peer_connection_interface.h"
#include "api/rtc_event_log/rtc_event_log_factory.h"
#include "api/scoped_refptr.h"
@@ -19,7 +19,6 @@
#include "api/transport/field_trial_based_config.h"
#include "call/simulated_network.h"
#include "media/engine/webrtc_media_engine.h"
-#include "media/engine/webrtc_media_engine_defaults.h"
#include "modules/audio_device/include/test_audio_device.h"
#include "p2p/base/basic_packet_socket_factory.h"
#include "p2p/client/basic_port_allocator.h"
@@ -57,23 +56,18 @@ rtc::scoped_refptr<PeerConnectionFactoryInterface> CreatePeerConnectionFactory(
rtc::Thread* network_thread) {
PeerConnectionFactoryDependencies pcf_deps;
pcf_deps.task_queue_factory = CreateDefaultTaskQueueFactory();
- pcf_deps.call_factory = CreateCallFactory();
pcf_deps.event_log_factory =
std::make_unique<RtcEventLogFactory>(pcf_deps.task_queue_factory.get());
pcf_deps.network_thread = network_thread;
pcf_deps.signaling_thread = signaling_thread;
pcf_deps.trials = std::make_unique<FieldTrialBasedConfig>();
- cricket::MediaEngineDependencies media_deps;
- media_deps.task_queue_factory = pcf_deps.task_queue_factory.get();
- media_deps.adm = TestAudioDeviceModule::Create(
- media_deps.task_queue_factory,
+ pcf_deps.adm = TestAudioDeviceModule::Create(
+ pcf_deps.task_queue_factory.get(),
TestAudioDeviceModule::CreatePulsedNoiseCapturer(kMaxAptitude,
kSamplingFrequency),
TestAudioDeviceModule::CreateDiscardRenderer(kSamplingFrequency),
/*speed=*/1.f);
- media_deps.trials = pcf_deps.trials.get();
- SetMediaEngineDefaults(&media_deps);
- pcf_deps.media_engine = cricket::CreateMediaEngine(std::move(media_deps));
+ EnableMediaWithDefaults(pcf_deps);
return CreateModularPeerConnectionFactory(std::move(pcf_deps));
}
diff --git a/third_party/libwebrtc/test/pc/e2e/BUILD.gn b/third_party/libwebrtc/test/pc/e2e/BUILD.gn
index 86a9110f58..9d1c1d437f 100644
--- a/third_party/libwebrtc/test/pc/e2e/BUILD.gn
+++ b/third_party/libwebrtc/test/pc/e2e/BUILD.gn
@@ -96,6 +96,7 @@ if (!build_with_chromium) {
":test_peer",
"../..:copy_to_file_audio_capturer",
"../../../api:create_time_controller",
+ "../../../api:enable_media_with_defaults",
"../../../api:time_controller",
"../../../api/rtc_event_log:rtc_event_log_factory",
"../../../api/task_queue:default_task_queue_factory",
@@ -105,8 +106,6 @@ if (!build_with_chromium) {
"../../../api/transport:field_trial_based_config",
"../../../api/video_codecs:builtin_video_decoder_factory",
"../../../api/video_codecs:builtin_video_encoder_factory",
- "../../../media:rtc_audio_video",
- "../../../media:rtc_media_engine_defaults",
"../../../modules/audio_device:test_audio_device_module",
"../../../modules/audio_processing/aec_dump",
"../../../p2p:rtc_p2p",
diff --git a/third_party/libwebrtc/test/pc/e2e/test_peer_factory.cc b/third_party/libwebrtc/test/pc/e2e/test_peer_factory.cc
index 41f7533c3a..dd900027ee 100644
--- a/third_party/libwebrtc/test/pc/e2e/test_peer_factory.cc
+++ b/third_party/libwebrtc/test/pc/e2e/test_peer_factory.cc
@@ -13,6 +13,7 @@
#include "absl/memory/memory.h"
#include "absl/strings/string_view.h"
+#include "api/enable_media_with_defaults.h"
#include "api/task_queue/default_task_queue_factory.h"
#include "api/test/create_time_controller.h"
#include "api/test/pclf/media_configuration.h"
@@ -21,8 +22,6 @@
#include "api/transport/field_trial_based_config.h"
#include "api/video_codecs/builtin_video_decoder_factory.h"
#include "api/video_codecs/builtin_video_encoder_factory.h"
-#include "media/engine/webrtc_media_engine.h"
-#include "media/engine/webrtc_media_engine_defaults.h"
#include "modules/audio_processing/aec_dump/aec_dump_factory.h"
#include "p2p/client/basic_port_allocator.h"
#include "rtc_base/thread.h"
@@ -146,28 +145,6 @@ rtc::scoped_refptr<AudioDeviceModule> CreateAudioDeviceModule(
std::move(renderer), /*speed=*/1.f);
}
-std::unique_ptr<cricket::MediaEngineInterface> CreateMediaEngine(
- TaskQueueFactory* task_queue_factory,
- PeerConnectionFactoryComponents* pcf_dependencies,
- rtc::scoped_refptr<AudioDeviceModule> audio_device_module) {
- cricket::MediaEngineDependencies media_deps;
- media_deps.task_queue_factory = task_queue_factory;
- media_deps.adm = audio_device_module;
- media_deps.audio_processing = pcf_dependencies->audio_processing;
- media_deps.audio_mixer = pcf_dependencies->audio_mixer;
- media_deps.video_encoder_factory =
- std::move(pcf_dependencies->video_encoder_factory);
- media_deps.video_decoder_factory =
- std::move(pcf_dependencies->video_decoder_factory);
- media_deps.audio_encoder_factory = pcf_dependencies->audio_encoder_factory;
- media_deps.audio_decoder_factory = pcf_dependencies->audio_decoder_factory;
- webrtc::SetMediaEngineDefaults(&media_deps);
- RTC_DCHECK(pcf_dependencies->trials);
- media_deps.trials = pcf_dependencies->trials.get();
-
- return cricket::CreateMediaEngine(std::move(media_deps));
-}
-
void WrapVideoEncoderFactory(
absl::string_view peer_name,
double bitrate_multiplier,
@@ -206,7 +183,7 @@ void WrapVideoDecoderFactory(
PeerConnectionFactoryDependencies CreatePCFDependencies(
std::unique_ptr<PeerConnectionFactoryComponents> pcf_dependencies,
TimeController& time_controller,
- std::unique_ptr<cricket::MediaEngineInterface> media_engine,
+ rtc::scoped_refptr<AudioDeviceModule> audio_device_module,
rtc::Thread* signaling_thread,
rtc::Thread* worker_thread,
rtc::Thread* network_thread) {
@@ -214,10 +191,7 @@ PeerConnectionFactoryDependencies CreatePCFDependencies(
pcf_deps.signaling_thread = signaling_thread;
pcf_deps.worker_thread = worker_thread;
pcf_deps.network_thread = network_thread;
- pcf_deps.media_engine = std::move(media_engine);
- pcf_deps.call_factory =
- CreateTimeControllerBasedCallFactory(&time_controller);
pcf_deps.event_log_factory = std::move(pcf_dependencies->event_log_factory);
pcf_deps.task_queue_factory = time_controller.CreateTaskQueueFactory();
@@ -236,6 +210,18 @@ PeerConnectionFactoryDependencies CreatePCFDependencies(
pcf_deps.trials = std::move(pcf_dependencies->trials);
}
+ // Media dependencies
+ pcf_deps.adm = std::move(audio_device_module);
+ pcf_deps.audio_processing = pcf_dependencies->audio_processing;
+ pcf_deps.audio_mixer = pcf_dependencies->audio_mixer;
+ pcf_deps.video_encoder_factory =
+ std::move(pcf_dependencies->video_encoder_factory);
+ pcf_deps.video_decoder_factory =
+ std::move(pcf_dependencies->video_decoder_factory);
+ pcf_deps.audio_encoder_factory = pcf_dependencies->audio_encoder_factory;
+ pcf_deps.audio_decoder_factory = pcf_dependencies->audio_decoder_factory;
+ EnableMediaWithDefaultsAndTimeController(time_controller, pcf_deps);
+
return pcf_deps;
}
@@ -324,10 +310,6 @@ std::unique_ptr<TestPeer> TestPeerFactory::CreateTestPeer(
WrapVideoDecoderFactory(params->name.value(),
components->pcf_dependencies.get(),
video_analyzer_helper_);
- std::unique_ptr<cricket::MediaEngineInterface> media_engine =
- CreateMediaEngine(time_controller_.GetTaskQueueFactory(),
- components->pcf_dependencies.get(),
- audio_device_module);
std::unique_ptr<rtc::Thread> owned_worker_thread =
components->worker_thread != nullptr
@@ -343,8 +325,8 @@ std::unique_ptr<TestPeer> TestPeerFactory::CreateTestPeer(
components->pcf_dependencies->audio_processing;
PeerConnectionFactoryDependencies pcf_deps = CreatePCFDependencies(
std::move(components->pcf_dependencies), time_controller_,
- std::move(media_engine), signaling_thread_, components->worker_thread,
- components->network_thread);
+ std::move(audio_device_module), signaling_thread_,
+ components->worker_thread, components->network_thread);
rtc::scoped_refptr<PeerConnectionFactoryInterface> peer_connection_factory =
CreateModularPeerConnectionFactory(std::move(pcf_deps));
diff --git a/third_party/libwebrtc/test/peer_scenario/peer_scenario_client.cc b/third_party/libwebrtc/test/peer_scenario/peer_scenario_client.cc
index 697bf055a7..60f2ea7f2e 100644
--- a/third_party/libwebrtc/test/peer_scenario/peer_scenario_client.cc
+++ b/third_party/libwebrtc/test/peer_scenario/peer_scenario_client.cc
@@ -246,17 +246,13 @@ PeerScenarioClient::PeerScenarioClient(
pcf_deps.network_thread = manager->network_thread();
pcf_deps.signaling_thread = signaling_thread_;
pcf_deps.worker_thread = worker_thread_.get();
- pcf_deps.call_factory =
- CreateTimeControllerBasedCallFactory(net->time_controller());
pcf_deps.task_queue_factory =
net->time_controller()->CreateTaskQueueFactory();
pcf_deps.event_log_factory =
std::make_unique<RtcEventLogFactory>(task_queue_factory_);
pcf_deps.trials = std::make_unique<FieldTrialBasedConfig>();
- cricket::MediaEngineDependencies media_deps;
- media_deps.task_queue_factory = task_queue_factory_;
- media_deps.adm = TestAudioDeviceModule::Create(
+ pcf_deps.adm = TestAudioDeviceModule::Create(
task_queue_factory_,
TestAudioDeviceModule::CreatePulsedNoiseCapturer(
config.audio.pulsed_noise->amplitude *
@@ -264,28 +260,24 @@ PeerScenarioClient::PeerScenarioClient(
config.audio.sample_rate, config.audio.channels),
TestAudioDeviceModule::CreateDiscardRenderer(config.audio.sample_rate));
- media_deps.audio_processing = AudioProcessingBuilder().Create();
if (config.video.use_fake_codecs) {
- media_deps.video_encoder_factory =
- std::make_unique<FakeVideoEncoderFactory>(
- net->time_controller()->GetClock());
- media_deps.video_decoder_factory =
+ pcf_deps.video_encoder_factory = std::make_unique<FakeVideoEncoderFactory>(
+ net->time_controller()->GetClock());
+ pcf_deps.video_decoder_factory =
std::make_unique<FakeVideoDecoderFactory>();
} else {
- media_deps.video_encoder_factory =
+ pcf_deps.video_encoder_factory =
std::make_unique<VideoEncoderFactoryTemplate<
LibvpxVp8EncoderTemplateAdapter, LibvpxVp9EncoderTemplateAdapter,
OpenH264EncoderTemplateAdapter, LibaomAv1EncoderTemplateAdapter>>();
- media_deps.video_decoder_factory =
+ pcf_deps.video_decoder_factory =
std::make_unique<VideoDecoderFactoryTemplate<
LibvpxVp8DecoderTemplateAdapter, LibvpxVp9DecoderTemplateAdapter,
OpenH264DecoderTemplateAdapter, Dav1dDecoderTemplateAdapter>>();
}
- media_deps.audio_encoder_factory = CreateBuiltinAudioEncoderFactory();
- media_deps.audio_decoder_factory = CreateBuiltinAudioDecoderFactory();
- media_deps.trials = pcf_deps.trials.get();
- pcf_deps.media_engine = cricket::CreateMediaEngine(std::move(media_deps));
+ EnableMediaWithDefaultsAndTimeController(*net->time_controller(), pcf_deps);
+
pcf_deps.fec_controller_factory = nullptr;
pcf_deps.network_controller_factory = nullptr;
pcf_deps.network_state_predictor_factory = nullptr;
diff --git a/third_party/libwebrtc/test/peer_scenario/scenario_connection.cc b/third_party/libwebrtc/test/peer_scenario/scenario_connection.cc
index 66eca275d1..8b2081a4c3 100644
--- a/third_party/libwebrtc/test/peer_scenario/scenario_connection.cc
+++ b/third_party/libwebrtc/test/peer_scenario/scenario_connection.cc
@@ -173,21 +173,14 @@ void ScenarioIceConnectionImpl::SetRemoteSdp(SdpType type,
});
auto res = jsep_controller_->SetRemoteDescription(
- remote_description_->GetType(), remote_description_->description());
+ remote_description_->GetType(),
+ local_description_ ? local_description_->description() : nullptr,
+ remote_description_->description());
RTC_CHECK(res.ok()) << res.message();
RtpDemuxerCriteria criteria;
for (const auto& content : remote_description_->description()->contents()) {
- if (content.media_description()->as_audio()) {
- for (const auto& codec :
- content.media_description()->as_audio()->codecs()) {
- criteria.payload_types().insert(codec.id);
- }
- }
- if (content.media_description()->as_video()) {
- for (const auto& codec :
- content.media_description()->as_video()->codecs()) {
- criteria.payload_types().insert(codec.id);
- }
+ for (const auto& codec : content.media_description()->codecs()) {
+ criteria.payload_types().insert(codec.id);
}
}
@@ -203,7 +196,8 @@ void ScenarioIceConnectionImpl::SetLocalSdp(SdpType type,
RTC_DCHECK_RUN_ON(signaling_thread_);
local_description_ = webrtc::CreateSessionDescription(type, local_sdp);
auto res = jsep_controller_->SetLocalDescription(
- local_description_->GetType(), local_description_->description());
+ local_description_->GetType(), local_description_->description(),
+ remote_description_ ? remote_description_->description() : nullptr);
RTC_CHECK(res.ok()) << res.message();
jsep_controller_->MaybeStartGathering();
}
diff --git a/third_party/libwebrtc/test/rtp_test_utils_gn/moz.build b/third_party/libwebrtc/test/rtp_test_utils_gn/moz.build
index f3ffb448cd..5f400c69f9 100644
--- a/third_party/libwebrtc/test/rtp_test_utils_gn/moz.build
+++ b/third_party/libwebrtc/test/rtp_test_utils_gn/moz.build
@@ -192,16 +192,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/test/scenario/BUILD.gn b/third_party/libwebrtc/test/scenario/BUILD.gn
index c3b8847fd1..7ac06c6822 100644
--- a/third_party/libwebrtc/test/scenario/BUILD.gn
+++ b/third_party/libwebrtc/test/scenario/BUILD.gn
@@ -86,10 +86,11 @@ if (rtc_include_tests && !build_with_chromium) {
"../../api:rtp_parameters",
"../../api:sequence_checker",
"../../api:time_controller",
- "../../api:time_controller",
"../../api:transport_api",
"../../api/audio_codecs:builtin_audio_decoder_factory",
"../../api/audio_codecs:builtin_audio_encoder_factory",
+ "../../api/environment",
+ "../../api/environment:environment_factory",
"../../api/rtc_event_log",
"../../api/rtc_event_log:rtc_event_log_factory",
"../../api/task_queue",
@@ -146,7 +147,6 @@ if (rtc_include_tests && !build_with_chromium) {
"../../rtc_base/synchronization:mutex",
"../../rtc_base/task_utils:repeating_task",
"../../system_wrappers",
- "../../system_wrappers:field_trial",
"../../video/config:streams_config",
"../logging:log_writer",
"../network:emulated_network",
diff --git a/third_party/libwebrtc/test/scenario/audio_stream.cc b/third_party/libwebrtc/test/scenario/audio_stream.cc
index 7715555e23..5f7db7acdf 100644
--- a/third_party/libwebrtc/test/scenario/audio_stream.cc
+++ b/third_party/libwebrtc/test/scenario/audio_stream.cc
@@ -14,13 +14,11 @@
#include "test/video_test_constants.h"
#if WEBRTC_ENABLE_PROTOBUF
-RTC_PUSH_IGNORING_WUNDEF()
#ifdef WEBRTC_ANDROID_PLATFORM_BUILD
#include "external/webrtc/webrtc/modules/audio_coding/audio_network_adaptor/config.pb.h"
#else
#include "modules/audio_coding/audio_network_adaptor/config.pb.h"
#endif
-RTC_POP_IGNORING_WUNDEF()
#endif
namespace webrtc {
diff --git a/third_party/libwebrtc/test/scenario/call_client.cc b/third_party/libwebrtc/test/scenario/call_client.cc
index fdf36dee08..8845ad6b0f 100644
--- a/third_party/libwebrtc/test/scenario/call_client.cc
+++ b/third_party/libwebrtc/test/scenario/call_client.cc
@@ -13,6 +13,8 @@
#include <memory>
#include <utility>
+#include "api/environment/environment.h"
+#include "api/environment/environment_factory.h"
#include "api/media_types.h"
#include "api/rtc_event_log/rtc_event_log.h"
#include "api/rtc_event_log/rtc_event_log_factory.h"
@@ -60,38 +62,27 @@ CallClientFakeAudio InitAudio(TimeController* time_controller) {
}
std::unique_ptr<Call> CreateCall(
- TimeController* time_controller,
- RtcEventLog* event_log,
+ const Environment& env,
CallClientConfig config,
LoggingNetworkControllerFactory* network_controller_factory,
rtc::scoped_refptr<AudioState> audio_state) {
- CallConfig call_config(event_log);
+ CallConfig call_config(env);
call_config.bitrate_config.max_bitrate_bps =
config.transport.rates.max_rate.bps_or(-1);
call_config.bitrate_config.min_bitrate_bps =
config.transport.rates.min_rate.bps();
call_config.bitrate_config.start_bitrate_bps =
config.transport.rates.start_rate.bps();
- call_config.task_queue_factory = time_controller->GetTaskQueueFactory();
call_config.network_controller_factory = network_controller_factory;
call_config.audio_state = audio_state;
- call_config.pacer_burst_interval = config.pacer_burst_interval;
- call_config.trials = config.field_trials;
- Clock* clock = time_controller->GetClock();
- return Call::Create(call_config, clock,
- RtpTransportControllerSendFactory().Create(
- call_config.ExtractTransportConfig(), clock));
+ return Call::Create(call_config);
}
std::unique_ptr<RtcEventLog> CreateEventLog(
- TaskQueueFactory* task_queue_factory,
- LogWriterFactoryInterface* log_writer_factory) {
- if (!log_writer_factory) {
- return std::make_unique<RtcEventLogNull>();
- }
- auto event_log = RtcEventLogFactory(task_queue_factory)
- .CreateRtcEventLog(RtcEventLog::EncodingType::NewFormat);
- bool success = event_log->StartLogging(log_writer_factory->Create(".rtc.dat"),
+ const Environment& env,
+ LogWriterFactoryInterface& log_writer_factory) {
+ auto event_log = RtcEventLogFactory().Create(env);
+ bool success = event_log->StartLogging(log_writer_factory.Create(".rtc.dat"),
kEventLogOutputIntervalMs);
RTC_CHECK(success);
return event_log;
@@ -219,22 +210,25 @@ CallClient::CallClient(
std::unique_ptr<LogWriterFactoryInterface> log_writer_factory,
CallClientConfig config)
: time_controller_(time_controller),
- clock_(time_controller->GetClock()),
+ env_(CreateEnvironment(time_controller_->CreateTaskQueueFactory(),
+ time_controller_->GetClock())),
log_writer_factory_(std::move(log_writer_factory)),
network_controller_factory_(log_writer_factory_.get(), config.transport),
- task_queue_(time_controller->GetTaskQueueFactory()->CreateTaskQueue(
+ task_queue_(env_.task_queue_factory().CreateTaskQueue(
"CallClient",
TaskQueueFactory::Priority::NORMAL)) {
- config.field_trials = &field_trials_;
SendTask([this, config] {
- event_log_ = CreateEventLog(time_controller_->GetTaskQueueFactory(),
- log_writer_factory_.get());
+ if (log_writer_factory_ != nullptr) {
+ EnvironmentFactory env_factory(env_);
+ env_factory.Set(CreateEventLog(env_, *log_writer_factory_));
+ env_ = env_factory.Create();
+ }
fake_audio_setup_ = InitAudio(time_controller_);
- call_ =
- CreateCall(time_controller_, event_log_.get(), config,
- &network_controller_factory_, fake_audio_setup_.audio_state);
- transport_ = std::make_unique<NetworkNodeTransport>(clock_, call_.get());
+ call_ = CreateCall(env_, config, &network_controller_factory_,
+ fake_audio_setup_.audio_state);
+ transport_ =
+ std::make_unique<NetworkNodeTransport>(&env_.clock(), call_.get());
});
}
@@ -243,9 +237,8 @@ CallClient::~CallClient() {
call_.reset();
fake_audio_setup_ = {};
rtc::Event done;
- event_log_->StopLogging([&done] { done.Set(); });
+ env_.event_log().StopLogging([&done] { done.Set(); });
done.Wait(rtc::Event::kForever);
- event_log_.reset();
});
}
@@ -283,7 +276,7 @@ DataRate CallClient::padding_rate() const {
void CallClient::SetRemoteBitrate(DataRate bitrate) {
RemoteBitrateReport msg;
msg.bandwidth = bitrate;
- msg.receive_time = clock_->CurrentTime();
+ msg.receive_time = env_.clock().CurrentTime();
network_controller_factory_.SetRemoteBitrateEstimate(msg);
}
diff --git a/third_party/libwebrtc/test/scenario/call_client.h b/third_party/libwebrtc/test/scenario/call_client.h
index 3717a7e796..f3c483ef28 100644
--- a/third_party/libwebrtc/test/scenario/call_client.h
+++ b/third_party/libwebrtc/test/scenario/call_client.h
@@ -17,6 +17,7 @@
#include <vector>
#include "api/array_view.h"
+#include "api/environment/environment.h"
#include "api/rtc_event_log/rtc_event_log.h"
#include "api/rtp_parameters.h"
#include "api/test/time_controller.h"
@@ -156,9 +157,8 @@ class CallClient : public EmulatedNetworkReceiverInterface {
void UnBind();
TimeController* const time_controller_;
- Clock* clock_;
+ Environment env_;
const std::unique_ptr<LogWriterFactoryInterface> log_writer_factory_;
- std::unique_ptr<RtcEventLog> event_log_;
LoggingNetworkControllerFactory network_controller_factory_;
CallClientFakeAudio fake_audio_setup_;
std::unique_ptr<Call> call_;
@@ -175,8 +175,6 @@ class CallClient : public EmulatedNetworkReceiverInterface {
std::map<uint32_t, MediaType> ssrc_media_types_;
// Defined last so it's destroyed first.
TaskQueueForTest task_queue_;
-
- const FieldTrialBasedConfig field_trials_;
};
class CallClientPair {
diff --git a/third_party/libwebrtc/test/scenario/scenario_config.h b/third_party/libwebrtc/test/scenario/scenario_config.h
index 9ce99401d7..50845cd677 100644
--- a/third_party/libwebrtc/test/scenario/scenario_config.h
+++ b/third_party/libwebrtc/test/scenario/scenario_config.h
@@ -57,7 +57,6 @@ struct CallClientConfig {
// The number of bites that can be sent in one burst is pacer_burst_interval *
// current bwe. 40ms is the default Chrome setting.
TimeDelta pacer_burst_interval = TimeDelta::Millis(40);
- const FieldTrialsView* field_trials = nullptr;
};
struct PacketStreamConfig {
diff --git a/third_party/libwebrtc/test/scenario/video_stream.cc b/third_party/libwebrtc/test/scenario/video_stream.cc
index 38e42c88e0..eb20f8dbc7 100644
--- a/third_party/libwebrtc/test/scenario/video_stream.cc
+++ b/third_party/libwebrtc/test/scenario/video_stream.cc
@@ -372,9 +372,9 @@ SendVideoStream::SendVideoStream(CallClient* sender,
VideoFrameMatcher* matcher)
: sender_(sender), config_(config) {
video_capturer_ = std::make_unique<FrameGeneratorCapturer>(
- sender_->clock_, CreateFrameGenerator(sender_->clock_, config.source),
- config.source.framerate,
- *sender->time_controller_->GetTaskQueueFactory());
+ &sender_->env_.clock(),
+ CreateFrameGenerator(&sender_->env_.clock(), config.source),
+ config.source.framerate, sender_->env_.task_queue_factory());
video_capturer_->Init();
using Encoder = VideoStreamConfig::Encoder;
@@ -386,9 +386,11 @@ SendVideoStream::SendVideoStream(CallClient* sender,
MutexLock lock(&mutex_);
std::unique_ptr<FakeEncoder> encoder;
if (config_.encoder.codec == Codec::kVideoCodecVP8) {
- encoder = std::make_unique<test::FakeVp8Encoder>(sender_->clock_);
+ encoder = std::make_unique<test::FakeVp8Encoder>(
+ &sender_->env_.clock());
} else if (config_.encoder.codec == Codec::kVideoCodecGeneric) {
- encoder = std::make_unique<test::FakeEncoder>(sender_->clock_);
+ encoder =
+ std::make_unique<test::FakeEncoder>(&sender_->env_.clock());
} else {
RTC_DCHECK_NOTREACHED();
}
@@ -436,7 +438,7 @@ SendVideoStream::SendVideoStream(CallClient* sender,
if (matcher->Active()) {
frame_tap_ = std::make_unique<ForwardingCapturedFrameTap>(
- sender_->clock_, matcher, video_capturer_.get());
+ &sender_->env_.clock(), matcher, video_capturer_.get());
send_stream_->SetSource(frame_tap_.get(),
config.encoder.degradation_preference);
} else {
@@ -565,8 +567,8 @@ ReceiveVideoStream::ReceiveVideoStream(CallClient* receiver,
for (size_t i = 0; i < num_streams; ++i) {
rtc::VideoSinkInterface<VideoFrame>* renderer = &fake_renderer_;
if (matcher->Active()) {
- render_taps_.emplace_back(
- std::make_unique<DecodedFrameTap>(receiver_->clock_, matcher, i));
+ render_taps_.emplace_back(std::make_unique<DecodedFrameTap>(
+ &receiver_->env_.clock(), matcher, i));
renderer = render_taps_.back().get();
}
auto recv_config = CreateVideoReceiveStreamConfig(
diff --git a/third_party/libwebrtc/test/video_codec_tester.cc b/third_party/libwebrtc/test/video_codec_tester.cc
new file mode 100644
index 0000000000..9453c3a7ef
--- /dev/null
+++ b/third_party/libwebrtc/test/video_codec_tester.cc
@@ -0,0 +1,1324 @@
+/*
+ * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "test/video_codec_tester.h"
+
+#include <algorithm>
+#include <set>
+#include <tuple>
+#include <utility>
+
+#include "api/array_view.h"
+#include "api/units/time_delta.h"
+#include "api/units/timestamp.h"
+#include "api/video/builtin_video_bitrate_allocator_factory.h"
+#include "api/video/i420_buffer.h"
+#include "api/video/video_bitrate_allocator.h"
+#include "api/video/video_codec_type.h"
+#include "api/video/video_frame.h"
+#include "api/video_codecs/video_decoder.h"
+#include "api/video_codecs/video_encoder.h"
+#include "media/base/media_constants.h"
+#include "modules/video_coding/codecs/av1/av1_svc_config.h"
+#include "modules/video_coding/codecs/vp9/svc_config.h"
+#include "modules/video_coding/include/video_codec_interface.h"
+#include "modules/video_coding/include/video_error_codes.h"
+#include "modules/video_coding/svc/scalability_mode_util.h"
+#include "modules/video_coding/utility/ivf_file_writer.h"
+#include "rtc_base/event.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/strings/string_builder.h"
+#include "rtc_base/synchronization/mutex.h"
+#include "rtc_base/task_queue_for_test.h"
+#include "rtc_base/time_utils.h"
+#include "system_wrappers/include/sleep.h"
+#include "test/testsupport/file_utils.h"
+#include "test/testsupport/frame_reader.h"
+#include "test/testsupport/video_frame_writer.h"
+#include "third_party/libyuv/include/libyuv/compare.h"
+
+namespace webrtc {
+namespace test {
+
+namespace {
+using CodedVideoSource = VideoCodecTester::CodedVideoSource;
+using VideoSourceSettings = VideoCodecTester::VideoSourceSettings;
+using EncodingSettings = VideoCodecTester::EncodingSettings;
+using LayerSettings = EncodingSettings::LayerSettings;
+using LayerId = VideoCodecTester::LayerId;
+using EncoderSettings = VideoCodecTester::EncoderSettings;
+using DecoderSettings = VideoCodecTester::DecoderSettings;
+using PacingSettings = VideoCodecTester::PacingSettings;
+using PacingMode = PacingSettings::PacingMode;
+using VideoCodecStats = VideoCodecTester::VideoCodecStats;
+using DecodeCallback =
+ absl::AnyInvocable<void(const VideoFrame& decoded_frame)>;
+using webrtc::test::ImprovementDirection;
+
+constexpr Frequency k90kHz = Frequency::Hertz(90000);
+
+const std::set<ScalabilityMode> kFullSvcScalabilityModes{
+ ScalabilityMode::kL2T1, ScalabilityMode::kL2T1h, ScalabilityMode::kL2T2,
+ ScalabilityMode::kL2T2h, ScalabilityMode::kL2T3, ScalabilityMode::kL2T3h,
+ ScalabilityMode::kL3T1, ScalabilityMode::kL3T1h, ScalabilityMode::kL3T2,
+ ScalabilityMode::kL3T2h, ScalabilityMode::kL3T3, ScalabilityMode::kL3T3h};
+
+const std::set<ScalabilityMode> kKeySvcScalabilityModes{
+ ScalabilityMode::kL2T1_KEY, ScalabilityMode::kL2T2_KEY,
+ ScalabilityMode::kL2T2_KEY_SHIFT, ScalabilityMode::kL2T3_KEY,
+ ScalabilityMode::kL3T1_KEY, ScalabilityMode::kL3T2_KEY,
+ ScalabilityMode::kL3T3_KEY};
+
+// A thread-safe raw video frame reader.
+class VideoSource {
+ public:
+ explicit VideoSource(VideoSourceSettings source_settings)
+ : source_settings_(source_settings) {
+ MutexLock lock(&mutex_);
+ frame_reader_ = CreateYuvFrameReader(
+ source_settings_.file_path, source_settings_.resolution,
+ YuvFrameReaderImpl::RepeatMode::kPingPong);
+ RTC_CHECK(frame_reader_);
+ }
+
+ // Pulls next frame.
+ VideoFrame PullFrame(uint32_t timestamp_rtp,
+ Resolution resolution,
+ Frequency framerate) {
+ MutexLock lock(&mutex_);
+ int frame_num;
+ auto buffer = frame_reader_->PullFrame(
+ &frame_num, resolution,
+ {.num = framerate.millihertz<int>(),
+ .den = source_settings_.framerate.millihertz<int>()});
+ RTC_CHECK(buffer) << "Can not pull frame. RTP timestamp " << timestamp_rtp;
+ frame_num_[timestamp_rtp] = frame_num;
+ return VideoFrame::Builder()
+ .set_video_frame_buffer(buffer)
+ .set_timestamp_rtp(timestamp_rtp)
+ .set_timestamp_us((timestamp_rtp / k90kHz).us())
+ .build();
+ }
+
+ // Reads frame specified by `timestamp_rtp`, scales it to `resolution` and
+ // returns. Frame with the given `timestamp_rtp` is expected to be pulled
+ // before.
+ VideoFrame ReadFrame(uint32_t timestamp_rtp, Resolution resolution) {
+ MutexLock lock(&mutex_);
+ RTC_CHECK(frame_num_.find(timestamp_rtp) != frame_num_.end())
+ << "Frame with RTP timestamp " << timestamp_rtp
+ << " was not pulled before";
+ auto buffer =
+ frame_reader_->ReadFrame(frame_num_.at(timestamp_rtp), resolution);
+ return VideoFrame::Builder()
+ .set_video_frame_buffer(buffer)
+ .set_timestamp_rtp(timestamp_rtp)
+ .build();
+ }
+
+ private:
+ VideoSourceSettings source_settings_;
+ std::unique_ptr<FrameReader> frame_reader_ RTC_GUARDED_BY(mutex_);
+ std::map<uint32_t, int> frame_num_ RTC_GUARDED_BY(mutex_);
+ Mutex mutex_;
+};
+
+// Pacer calculates delay necessary to keep frame encode or decode call spaced
+// from the previous calls by the pacing time. `Schedule` is expected to be
+// called as close as possible to posting frame encode or decode task. This
+// class is not thread safe.
+class Pacer {
+ public:
+ explicit Pacer(PacingSettings settings)
+ : settings_(settings), delay_(TimeDelta::Zero()) {}
+
+ Timestamp Schedule(Timestamp timestamp) {
+ Timestamp now = Timestamp::Micros(rtc::TimeMicros());
+ if (settings_.mode == PacingMode::kNoPacing) {
+ return now;
+ }
+
+ Timestamp scheduled = now;
+ if (prev_scheduled_) {
+ scheduled = *prev_scheduled_ + PacingTime(timestamp);
+ if (scheduled < now) {
+ scheduled = now;
+ }
+ }
+
+ prev_timestamp_ = timestamp;
+ prev_scheduled_ = scheduled;
+ return scheduled;
+ }
+
+ private:
+ TimeDelta PacingTime(Timestamp timestamp) {
+ if (settings_.mode == PacingMode::kRealTime) {
+ return timestamp - *prev_timestamp_;
+ }
+ RTC_CHECK_EQ(PacingMode::kConstantRate, settings_.mode);
+ return 1 / settings_.constant_rate;
+ }
+
+ PacingSettings settings_;
+ absl::optional<Timestamp> prev_timestamp_;
+ absl::optional<Timestamp> prev_scheduled_;
+ TimeDelta delay_;
+};
+
+class LimitedTaskQueue {
+ public:
+ // The codec tester reads frames from video source in the main thread.
+ // Encoding and decoding are done in separate threads. If encoding or
+ // decoding is slow, the reading may go far ahead and may buffer too many
+ // frames in memory. To prevent this we limit the encoding/decoding queue
+ // size. When the queue is full, the main thread and, hence, reading frames
+ // from video source is blocked until a previously posted encoding/decoding
+ // task starts.
+ static constexpr int kMaxTaskQueueSize = 3;
+
+ LimitedTaskQueue() : queue_size_(0) {}
+
+ void PostScheduledTask(absl::AnyInvocable<void() &&> task, Timestamp start) {
+ ++queue_size_;
+ task_queue_.PostTask([this, task = std::move(task), start]() mutable {
+ // `TaskQueue` doesn't guarantee FIFO order of execution for delayed
+ // tasks.
+ int64_t wait_ms = (start - Timestamp::Millis(rtc::TimeMillis())).ms();
+ if (wait_ms > 0) {
+ RTC_CHECK_LT(wait_ms, 10000) << "Too high wait_ms " << wait_ms;
+ SleepMs(wait_ms);
+ }
+ std::move(task)();
+ --queue_size_;
+ task_executed_.Set();
+ });
+
+ task_executed_.Reset();
+ if (queue_size_ > kMaxTaskQueueSize) {
+ task_executed_.Wait(rtc::Event::kForever);
+ RTC_CHECK(queue_size_ <= kMaxTaskQueueSize);
+ }
+ }
+
+ void PostTaskAndWait(absl::AnyInvocable<void() &&> task) {
+ PostScheduledTask(std::move(task), Timestamp::Millis(rtc::TimeMillis()));
+ task_queue_.WaitForPreviouslyPostedTasks();
+ }
+
+ private:
+ TaskQueueForTest task_queue_;
+ std::atomic_int queue_size_;
+ rtc::Event task_executed_;
+};
+
+class TesterY4mWriter {
+ public:
+ explicit TesterY4mWriter(absl::string_view base_path)
+ : base_path_(base_path) {}
+
+ ~TesterY4mWriter() {
+ task_queue_.SendTask([] {});
+ }
+
+ void Write(const VideoFrame& frame, int spatial_idx) {
+ task_queue_.PostTask([this, frame, spatial_idx] {
+ if (y4m_writers_.find(spatial_idx) == y4m_writers_.end()) {
+ std::string file_path =
+ base_path_ + "-s" + std::to_string(spatial_idx) + ".y4m";
+ Y4mVideoFrameWriterImpl* y4m_writer = new Y4mVideoFrameWriterImpl(
+ file_path, frame.width(), frame.height(), /*fps=*/30);
+ RTC_CHECK(y4m_writer);
+
+ y4m_writers_[spatial_idx] =
+ std::unique_ptr<VideoFrameWriter>(y4m_writer);
+ }
+
+ y4m_writers_.at(spatial_idx)->WriteFrame(frame);
+ });
+ }
+
+ private:
+ std::string base_path_;
+ std::map<int, std::unique_ptr<VideoFrameWriter>> y4m_writers_;
+ TaskQueueForTest task_queue_;
+};
+
+class TesterIvfWriter {
+ public:
+ explicit TesterIvfWriter(absl::string_view base_path)
+ : base_path_(base_path) {}
+
+ ~TesterIvfWriter() {
+ task_queue_.SendTask([] {});
+ }
+
+ void Write(const EncodedImage& encoded_frame) {
+ task_queue_.PostTask([this, encoded_frame] {
+ int spatial_idx = encoded_frame.SimulcastIndex().value_or(0);
+ if (ivf_file_writers_.find(spatial_idx) == ivf_file_writers_.end()) {
+ std::string ivf_path =
+ base_path_ + "-s" + std::to_string(spatial_idx) + ".ivf";
+ FileWrapper ivf_file = FileWrapper::OpenWriteOnly(ivf_path);
+ RTC_CHECK(ivf_file.is_open());
+
+ std::unique_ptr<IvfFileWriter> ivf_writer =
+ IvfFileWriter::Wrap(std::move(ivf_file), /*byte_limit=*/0);
+ RTC_CHECK(ivf_writer);
+
+ ivf_file_writers_[spatial_idx] = std::move(ivf_writer);
+ }
+
+ // To play: ffplay -vcodec vp8|vp9|av1|hevc|h264 filename
+ ivf_file_writers_.at(spatial_idx)
+ ->WriteFrame(encoded_frame, VideoCodecType::kVideoCodecGeneric);
+ });
+ }
+
+ private:
+ std::string base_path_;
+ std::map<int, std::unique_ptr<IvfFileWriter>> ivf_file_writers_;
+ TaskQueueForTest task_queue_;
+};
+
+class LeakyBucket {
+ public:
+ LeakyBucket() : level_bits_(0) {}
+
+ // Updates bucket level and returns its current level in bits. Data is remove
+ // from bucket with rate equal to target bitrate of previous frame. Bucket
+ // level is tracked with floating point precision. Returned value of bucket
+ // level is rounded up.
+ int Update(const VideoCodecStats::Frame& frame) {
+ RTC_CHECK(frame.target_bitrate) << "Bitrate must be specified.";
+ if (prev_frame_) {
+ RTC_CHECK_GT(frame.timestamp_rtp, prev_frame_->timestamp_rtp)
+ << "Timestamp must increase.";
+ TimeDelta passed =
+ (frame.timestamp_rtp - prev_frame_->timestamp_rtp) / k90kHz;
+ level_bits_ -=
+ prev_frame_->target_bitrate->bps<double>() * passed.seconds<double>();
+ level_bits_ = std::max(level_bits_, 0.0);
+ }
+ prev_frame_ = frame;
+ level_bits_ += frame.frame_size.bytes() * 8;
+ return static_cast<int>(std::ceil(level_bits_));
+ }
+
+ private:
+ absl::optional<VideoCodecStats::Frame> prev_frame_;
+ double level_bits_;
+};
+
+class VideoCodecAnalyzer : public VideoCodecTester::VideoCodecStats {
+ public:
+ explicit VideoCodecAnalyzer(VideoSource* video_source)
+ : video_source_(video_source) {}
+
+ void StartEncode(const VideoFrame& video_frame,
+ const EncodingSettings& encoding_settings) {
+ int64_t encode_start_us = rtc::TimeMicros();
+ task_queue_.PostTask([this, timestamp_rtp = video_frame.timestamp(),
+ encoding_settings, encode_start_us]() {
+ RTC_CHECK(frames_.find(timestamp_rtp) == frames_.end())
+ << "Duplicate frame. Frame with timestamp " << timestamp_rtp
+ << " was seen before";
+
+ Frame frame;
+ frame.timestamp_rtp = timestamp_rtp;
+ frame.encode_start = Timestamp::Micros(encode_start_us),
+ frames_.emplace(timestamp_rtp,
+ std::map<int, Frame>{{/*spatial_idx=*/0, frame}});
+ encoding_settings_.emplace(timestamp_rtp, encoding_settings);
+ });
+ }
+
+ void FinishEncode(const EncodedImage& encoded_frame) {
+ int64_t encode_finished_us = rtc::TimeMicros();
+ task_queue_.PostTask(
+ [this, timestamp_rtp = encoded_frame.RtpTimestamp(),
+ spatial_idx = encoded_frame.SpatialIndex().value_or(0),
+ temporal_idx = encoded_frame.TemporalIndex().value_or(0),
+ width = encoded_frame._encodedWidth,
+ height = encoded_frame._encodedHeight,
+ frame_type = encoded_frame._frameType,
+ frame_size_bytes = encoded_frame.size(), qp = encoded_frame.qp_,
+ encode_finished_us]() {
+ if (spatial_idx > 0) {
+ RTC_CHECK(frames_.find(timestamp_rtp) != frames_.end())
+ << "Spatial layer 0 frame with timestamp " << timestamp_rtp
+ << " was not seen before";
+ const Frame& base_frame =
+ frames_.at(timestamp_rtp).at(/*spatial_idx=*/0);
+ frames_.at(timestamp_rtp).emplace(spatial_idx, base_frame);
+ }
+
+ Frame& frame = frames_.at(timestamp_rtp).at(spatial_idx);
+ frame.layer_id = {.spatial_idx = spatial_idx,
+ .temporal_idx = temporal_idx};
+ frame.width = width;
+ frame.height = height;
+ frame.frame_size = DataSize::Bytes(frame_size_bytes);
+ frame.qp = qp;
+ frame.keyframe = frame_type == VideoFrameType::kVideoFrameKey;
+ frame.encode_time =
+ Timestamp::Micros(encode_finished_us) - frame.encode_start;
+ frame.encoded = true;
+ });
+ }
+
+ void StartDecode(const EncodedImage& encoded_frame) {
+ int64_t decode_start_us = rtc::TimeMicros();
+ task_queue_.PostTask(
+ [this, timestamp_rtp = encoded_frame.RtpTimestamp(),
+ spatial_idx = encoded_frame.SpatialIndex().value_or(0),
+ frame_size_bytes = encoded_frame.size(), decode_start_us]() {
+ if (frames_.find(timestamp_rtp) == frames_.end() ||
+ frames_.at(timestamp_rtp).find(spatial_idx) ==
+ frames_.at(timestamp_rtp).end()) {
+ Frame frame;
+ frame.timestamp_rtp = timestamp_rtp;
+ frame.layer_id = {.spatial_idx = spatial_idx};
+ frame.frame_size = DataSize::Bytes(frame_size_bytes);
+ frames_.emplace(timestamp_rtp,
+ std::map<int, Frame>{{spatial_idx, frame}});
+ }
+
+ Frame& frame = frames_.at(timestamp_rtp).at(spatial_idx);
+ frame.decode_start = Timestamp::Micros(decode_start_us);
+ });
+ }
+
+ void FinishDecode(const VideoFrame& decoded_frame, int spatial_idx) {
+ int64_t decode_finished_us = rtc::TimeMicros();
+ task_queue_.PostTask([this, timestamp_rtp = decoded_frame.timestamp(),
+ spatial_idx, width = decoded_frame.width(),
+ height = decoded_frame.height(),
+ decode_finished_us]() {
+ Frame& frame = frames_.at(timestamp_rtp).at(spatial_idx);
+ frame.decode_time =
+ Timestamp::Micros(decode_finished_us) - frame.decode_start;
+ if (!frame.encoded) {
+ frame.width = width;
+ frame.height = height;
+ }
+ frame.decoded = true;
+ });
+
+ if (video_source_ != nullptr) {
+ // Copy hardware-backed frame into main memory to release output buffers
+ // which number may be limited in hardware decoders.
+ rtc::scoped_refptr<I420BufferInterface> decoded_buffer =
+ decoded_frame.video_frame_buffer()->ToI420();
+
+ task_queue_.PostTask([this, decoded_buffer,
+ timestamp_rtp = decoded_frame.timestamp(),
+ spatial_idx]() {
+ VideoFrame ref_frame = video_source_->ReadFrame(
+ timestamp_rtp, {.width = decoded_buffer->width(),
+ .height = decoded_buffer->height()});
+ rtc::scoped_refptr<I420BufferInterface> ref_buffer =
+ ref_frame.video_frame_buffer()->ToI420();
+ Frame& frame = frames_.at(timestamp_rtp).at(spatial_idx);
+ frame.psnr = CalcPsnr(*decoded_buffer, *ref_buffer);
+ });
+ }
+ }
+
+ std::vector<Frame> Slice(Filter filter, bool merge) const {
+ std::vector<Frame> slice;
+ for (const auto& [timestamp_rtp, temporal_unit_frames] : frames_) {
+ if (temporal_unit_frames.empty()) {
+ continue;
+ }
+
+ bool is_svc = false;
+ if (!encoding_settings_.empty()) {
+ ScalabilityMode scalability_mode =
+ encoding_settings_.at(timestamp_rtp).scalability_mode;
+ if (kFullSvcScalabilityModes.count(scalability_mode) > 0 ||
+ (kKeySvcScalabilityModes.count(scalability_mode) > 0 &&
+ temporal_unit_frames.at(0).keyframe)) {
+ is_svc = true;
+ }
+ }
+
+ std::vector<Frame> subframes;
+ for (const auto& [spatial_idx, frame] : temporal_unit_frames) {
+ if (frame.timestamp_rtp < filter.min_timestamp_rtp ||
+ frame.timestamp_rtp > filter.max_timestamp_rtp) {
+ continue;
+ }
+ if (filter.layer_id) {
+ if (is_svc &&
+ frame.layer_id.spatial_idx > filter.layer_id->spatial_idx) {
+ continue;
+ }
+ if (!is_svc &&
+ frame.layer_id.spatial_idx != filter.layer_id->spatial_idx) {
+ continue;
+ }
+ if (frame.layer_id.temporal_idx > filter.layer_id->temporal_idx) {
+ continue;
+ }
+ }
+ subframes.push_back(frame);
+ }
+
+ if (subframes.empty()) {
+ continue;
+ }
+
+ if (!merge) {
+ std::copy(subframes.begin(), subframes.end(),
+ std::back_inserter(slice));
+ continue;
+ }
+
+ Frame superframe = subframes.back();
+ for (const Frame& frame :
+ rtc::ArrayView<Frame>(subframes).subview(0, subframes.size() - 1)) {
+ superframe.frame_size += frame.frame_size;
+ superframe.keyframe |= frame.keyframe;
+ superframe.encode_time =
+ std::max(superframe.encode_time, frame.encode_time);
+ superframe.decode_time =
+ std::max(superframe.decode_time, frame.decode_time);
+ }
+
+ if (!encoding_settings_.empty()) {
+ RTC_CHECK(encoding_settings_.find(superframe.timestamp_rtp) !=
+ encoding_settings_.end())
+ << "No encoding settings for frame " << superframe.timestamp_rtp;
+ const EncodingSettings& es =
+ encoding_settings_.at(superframe.timestamp_rtp);
+ superframe.target_bitrate = GetTargetBitrate(es, filter.layer_id);
+ superframe.target_framerate = GetTargetFramerate(es, filter.layer_id);
+ }
+
+ slice.push_back(superframe);
+ }
+ return slice;
+ }
+
+ Stream Aggregate(Filter filter) const {
+ std::vector<Frame> frames = Slice(filter, /*merge=*/true);
+ Stream stream;
+ LeakyBucket leaky_bucket;
+ for (const Frame& frame : frames) {
+ Timestamp time = Timestamp::Micros((frame.timestamp_rtp / k90kHz).us());
+ if (!frame.frame_size.IsZero()) {
+ stream.width.AddSample(StatsSample(frame.width, time));
+ stream.height.AddSample(StatsSample(frame.height, time));
+ stream.frame_size_bytes.AddSample(
+ StatsSample(frame.frame_size.bytes(), time));
+ stream.keyframe.AddSample(StatsSample(frame.keyframe, time));
+ if (frame.qp) {
+ stream.qp.AddSample(StatsSample(*frame.qp, time));
+ }
+ }
+ if (frame.encoded) {
+ stream.encode_time_ms.AddSample(
+ StatsSample(frame.encode_time.ms(), time));
+ }
+ if (frame.decoded) {
+ stream.decode_time_ms.AddSample(
+ StatsSample(frame.decode_time.ms(), time));
+ }
+ if (frame.psnr) {
+ stream.psnr.y.AddSample(StatsSample(frame.psnr->y, time));
+ stream.psnr.u.AddSample(StatsSample(frame.psnr->u, time));
+ stream.psnr.v.AddSample(StatsSample(frame.psnr->v, time));
+ }
+ if (frame.target_framerate) {
+ stream.target_framerate_fps.AddSample(
+ StatsSample(frame.target_framerate->hertz<double>(), time));
+ }
+ if (frame.target_bitrate) {
+ stream.target_bitrate_kbps.AddSample(
+ StatsSample(frame.target_bitrate->kbps<double>(), time));
+ int buffer_level_bits = leaky_bucket.Update(frame);
+ stream.transmission_time_ms.AddSample(StatsSample(
+ 1000 * buffer_level_bits / frame.target_bitrate->bps<double>(),
+ time));
+ }
+ }
+
+ int num_encoded_frames = stream.frame_size_bytes.NumSamples();
+ const Frame& first_frame = frames.front();
+
+ Filter filter_all_layers{.min_timestamp_rtp = filter.min_timestamp_rtp,
+ .max_timestamp_rtp = filter.max_timestamp_rtp};
+ std::vector<Frame> frames_all_layers =
+ Slice(filter_all_layers, /*merge=*/true);
+ const Frame& last_frame = frames_all_layers.back();
+ TimeDelta duration =
+ (last_frame.timestamp_rtp - first_frame.timestamp_rtp) / k90kHz;
+ if (last_frame.target_framerate) {
+ duration += 1 / *last_frame.target_framerate;
+ }
+
+ DataRate encoded_bitrate =
+ DataSize::Bytes(stream.frame_size_bytes.GetSum()) / duration;
+ Frequency encoded_framerate = num_encoded_frames / duration;
+
+ double bitrate_mismatch_pct = 0.0;
+ if (const auto& target_bitrate = first_frame.target_bitrate;
+ target_bitrate) {
+ bitrate_mismatch_pct = 100 * (encoded_bitrate / *target_bitrate - 1);
+ }
+ double framerate_mismatch_pct = 0.0;
+ if (const auto& target_framerate = first_frame.target_framerate;
+ target_framerate) {
+ framerate_mismatch_pct =
+ 100 * (encoded_framerate / *target_framerate - 1);
+ }
+
+ for (Frame& frame : frames) {
+ Timestamp time = Timestamp::Micros((frame.timestamp_rtp / k90kHz).us());
+ stream.encoded_bitrate_kbps.AddSample(
+ StatsSample(encoded_bitrate.kbps<double>(), time));
+ stream.encoded_framerate_fps.AddSample(
+ StatsSample(encoded_framerate.hertz<double>(), time));
+ stream.bitrate_mismatch_pct.AddSample(
+ StatsSample(bitrate_mismatch_pct, time));
+ stream.framerate_mismatch_pct.AddSample(
+ StatsSample(framerate_mismatch_pct, time));
+ }
+
+ return stream;
+ }
+
+ void LogMetrics(absl::string_view csv_path,
+ std::vector<Frame> frames,
+ std::map<std::string, std::string> metadata) const {
+ RTC_LOG(LS_INFO) << "Write metrics to " << csv_path;
+ FILE* csv_file = fopen(csv_path.data(), "w");
+ const std::string delimiter = ";";
+ rtc::StringBuilder header;
+ header
+ << "timestamp_rtp;spatial_idx;temporal_idx;width;height;frame_size_"
+ "bytes;keyframe;qp;encode_time_us;decode_time_us;psnr_y_db;psnr_u_"
+ "db;psnr_v_db;target_bitrate_kbps;target_framerate_fps";
+ for (const auto& data : metadata) {
+ header << ";" << data.first;
+ }
+ fwrite(header.str().c_str(), 1, header.size(), csv_file);
+
+ for (const Frame& f : frames) {
+ rtc::StringBuilder row;
+ row << "\n" << f.timestamp_rtp;
+ row << ";" << f.layer_id.spatial_idx;
+ row << ";" << f.layer_id.temporal_idx;
+ row << ";" << f.width;
+ row << ";" << f.height;
+ row << ";" << f.frame_size.bytes();
+ row << ";" << f.keyframe;
+ row << ";";
+ if (f.qp) {
+ row << *f.qp;
+ }
+ row << ";" << f.encode_time.us();
+ row << ";" << f.decode_time.us();
+ if (f.psnr) {
+ row << ";" << f.psnr->y;
+ row << ";" << f.psnr->u;
+ row << ";" << f.psnr->v;
+ } else {
+ row << ";;;";
+ }
+
+ const auto& es = encoding_settings_.at(f.timestamp_rtp);
+ row << ";"
+ << f.target_bitrate.value_or(GetTargetBitrate(es, f.layer_id)).kbps();
+ row << ";"
+ << f.target_framerate.value_or(GetTargetFramerate(es, f.layer_id))
+ .hertz<double>();
+
+ for (const auto& data : metadata) {
+ row << ";" << data.second;
+ }
+ fwrite(row.str().c_str(), 1, row.size(), csv_file);
+ }
+
+ fclose(csv_file);
+ }
+
+ void Flush() { task_queue_.WaitForPreviouslyPostedTasks(); }
+
+ private:
+ struct FrameId {
+ uint32_t timestamp_rtp;
+ int spatial_idx;
+
+ bool operator==(const FrameId& o) const {
+ return timestamp_rtp == o.timestamp_rtp && spatial_idx == o.spatial_idx;
+ }
+ bool operator<(const FrameId& o) const {
+ return timestamp_rtp < o.timestamp_rtp ||
+ (timestamp_rtp == o.timestamp_rtp && spatial_idx < o.spatial_idx);
+ }
+ };
+
+ Frame::Psnr CalcPsnr(const I420BufferInterface& ref_buffer,
+ const I420BufferInterface& dec_buffer) {
+ RTC_CHECK_EQ(ref_buffer.width(), dec_buffer.width());
+ RTC_CHECK_EQ(ref_buffer.height(), dec_buffer.height());
+
+ uint64_t sse_y = libyuv::ComputeSumSquareErrorPlane(
+ dec_buffer.DataY(), dec_buffer.StrideY(), ref_buffer.DataY(),
+ ref_buffer.StrideY(), dec_buffer.width(), dec_buffer.height());
+
+ uint64_t sse_u = libyuv::ComputeSumSquareErrorPlane(
+ dec_buffer.DataU(), dec_buffer.StrideU(), ref_buffer.DataU(),
+ ref_buffer.StrideU(), dec_buffer.width() / 2, dec_buffer.height() / 2);
+
+ uint64_t sse_v = libyuv::ComputeSumSquareErrorPlane(
+ dec_buffer.DataV(), dec_buffer.StrideV(), ref_buffer.DataV(),
+ ref_buffer.StrideV(), dec_buffer.width() / 2, dec_buffer.height() / 2);
+
+ int num_y_samples = dec_buffer.width() * dec_buffer.height();
+ Frame::Psnr psnr;
+ psnr.y = libyuv::SumSquareErrorToPsnr(sse_y, num_y_samples);
+ psnr.u = libyuv::SumSquareErrorToPsnr(sse_u, num_y_samples / 4);
+ psnr.v = libyuv::SumSquareErrorToPsnr(sse_v, num_y_samples / 4);
+ return psnr;
+ }
+
+ DataRate GetTargetBitrate(const EncodingSettings& encoding_settings,
+ absl::optional<LayerId> layer_id) const {
+ int base_spatial_idx;
+ if (layer_id.has_value()) {
+ bool is_svc =
+ kFullSvcScalabilityModes.count(encoding_settings.scalability_mode);
+ base_spatial_idx = is_svc ? 0 : layer_id->spatial_idx;
+ } else {
+ int num_spatial_layers =
+ ScalabilityModeToNumSpatialLayers(encoding_settings.scalability_mode);
+ int num_temporal_layers = ScalabilityModeToNumTemporalLayers(
+ encoding_settings.scalability_mode);
+ layer_id = LayerId({.spatial_idx = num_spatial_layers - 1,
+ .temporal_idx = num_temporal_layers - 1});
+ base_spatial_idx = 0;
+ }
+
+ DataRate bitrate = DataRate::Zero();
+ for (int sidx = base_spatial_idx; sidx <= layer_id->spatial_idx; ++sidx) {
+ for (int tidx = 0; tidx <= layer_id->temporal_idx; ++tidx) {
+ auto layer_settings = encoding_settings.layers_settings.find(
+ {.spatial_idx = sidx, .temporal_idx = tidx});
+ RTC_CHECK(layer_settings != encoding_settings.layers_settings.end())
+ << "bitrate is not specified for layer sidx=" << sidx
+ << " tidx=" << tidx;
+ bitrate += layer_settings->second.bitrate;
+ }
+ }
+ return bitrate;
+ }
+
+ Frequency GetTargetFramerate(const EncodingSettings& encoding_settings,
+ absl::optional<LayerId> layer_id) const {
+ if (layer_id.has_value()) {
+ auto layer_settings = encoding_settings.layers_settings.find(
+ {.spatial_idx = layer_id->spatial_idx,
+ .temporal_idx = layer_id->temporal_idx});
+ RTC_CHECK(layer_settings != encoding_settings.layers_settings.end())
+ << "framerate is not specified for layer sidx="
+ << layer_id->spatial_idx << " tidx=" << layer_id->temporal_idx;
+ return layer_settings->second.framerate;
+ }
+ return encoding_settings.layers_settings.rbegin()->second.framerate;
+ }
+
+ SamplesStatsCounter::StatsSample StatsSample(double value,
+ Timestamp time) const {
+ return SamplesStatsCounter::StatsSample{value, time};
+ }
+
+ VideoSource* const video_source_;
+ TaskQueueForTest task_queue_;
+ // RTP timestamp -> spatial layer -> Frame
+ std::map<uint32_t, std::map<int, Frame>> frames_;
+ std::map<uint32_t, EncodingSettings> encoding_settings_;
+};
+
+class Decoder : public DecodedImageCallback {
+ public:
+ Decoder(VideoDecoderFactory* decoder_factory,
+ const DecoderSettings& decoder_settings,
+ VideoCodecAnalyzer* analyzer)
+ : decoder_factory_(decoder_factory),
+ analyzer_(analyzer),
+ pacer_(decoder_settings.pacing_settings) {
+ RTC_CHECK(analyzer_) << "Analyzer must be provided";
+
+ if (decoder_settings.decoder_input_base_path) {
+ ivf_writer_ = std::make_unique<TesterIvfWriter>(
+ *decoder_settings.decoder_input_base_path);
+ }
+
+ if (decoder_settings.decoder_output_base_path) {
+ y4m_writer_ = std::make_unique<TesterY4mWriter>(
+ *decoder_settings.decoder_output_base_path);
+ }
+ }
+
+ void Initialize(const SdpVideoFormat& sdp_video_format) {
+ decoder_ = decoder_factory_->CreateVideoDecoder(sdp_video_format);
+ RTC_CHECK(decoder_) << "Could not create decoder for video format "
+ << sdp_video_format.ToString();
+
+ task_queue_.PostTaskAndWait([this, &sdp_video_format] {
+ decoder_->RegisterDecodeCompleteCallback(this);
+
+ VideoDecoder::Settings ds;
+ ds.set_codec_type(PayloadStringToCodecType(sdp_video_format.name));
+ ds.set_number_of_cores(1);
+ ds.set_max_render_resolution({1280, 720});
+ bool result = decoder_->Configure(ds);
+ RTC_CHECK(result) << "Failed to configure decoder";
+ });
+ }
+
+ void Decode(const EncodedImage& encoded_frame) {
+ Timestamp pts =
+ Timestamp::Micros((encoded_frame.RtpTimestamp() / k90kHz).us());
+
+ task_queue_.PostScheduledTask(
+ [this, encoded_frame] {
+ analyzer_->StartDecode(encoded_frame);
+ int error = decoder_->Decode(encoded_frame, /*render_time_ms*/ 0);
+ if (error != 0) {
+ RTC_LOG(LS_WARNING)
+ << "Decode failed with error code " << error
+ << " RTP timestamp " << encoded_frame.RtpTimestamp();
+ }
+ },
+ pacer_.Schedule(pts));
+
+ if (ivf_writer_) {
+ ivf_writer_->Write(encoded_frame);
+ }
+ }
+
+ void Flush() {
+ // TODO(webrtc:14852): Add Flush() to VideoDecoder API.
+ task_queue_.PostTaskAndWait([this] { decoder_->Release(); });
+ }
+
+ private:
+ int Decoded(VideoFrame& decoded_frame) override {
+ analyzer_->FinishDecode(decoded_frame, /*spatial_idx=*/0);
+
+ if (y4m_writer_) {
+ y4m_writer_->Write(decoded_frame, /*spatial_idx=*/0);
+ }
+
+ return WEBRTC_VIDEO_CODEC_OK;
+ }
+
+ VideoDecoderFactory* decoder_factory_;
+ std::unique_ptr<VideoDecoder> decoder_;
+ VideoCodecAnalyzer* const analyzer_;
+ Pacer pacer_;
+ LimitedTaskQueue task_queue_;
+ std::unique_ptr<TesterIvfWriter> ivf_writer_;
+ std::unique_ptr<TesterY4mWriter> y4m_writer_;
+};
+
+class Encoder : public EncodedImageCallback {
+ public:
+ using EncodeCallback =
+ absl::AnyInvocable<void(const EncodedImage& encoded_frame)>;
+
+ Encoder(VideoEncoderFactory* encoder_factory,
+ const EncoderSettings& encoder_settings,
+ VideoCodecAnalyzer* analyzer)
+ : encoder_factory_(encoder_factory),
+ analyzer_(analyzer),
+ pacer_(encoder_settings.pacing_settings) {
+ RTC_CHECK(analyzer_) << "Analyzer must be provided";
+
+ if (encoder_settings.encoder_input_base_path) {
+ y4m_writer_ = std::make_unique<TesterY4mWriter>(
+ *encoder_settings.encoder_input_base_path);
+ }
+
+ if (encoder_settings.encoder_output_base_path) {
+ ivf_writer_ = std::make_unique<TesterIvfWriter>(
+ *encoder_settings.encoder_output_base_path);
+ }
+ }
+
+ void Initialize(const EncodingSettings& encoding_settings) {
+ encoder_ = encoder_factory_->CreateVideoEncoder(
+ encoding_settings.sdp_video_format);
+ RTC_CHECK(encoder_) << "Could not create encoder for video format "
+ << encoding_settings.sdp_video_format.ToString();
+
+ task_queue_.PostTaskAndWait([this, encoding_settings] {
+ encoder_->RegisterEncodeCompleteCallback(this);
+ Configure(encoding_settings);
+ SetRates(encoding_settings);
+ });
+ }
+
+ void Encode(const VideoFrame& input_frame,
+ const EncodingSettings& encoding_settings,
+ EncodeCallback callback) {
+ {
+ MutexLock lock(&mutex_);
+ callbacks_[input_frame.timestamp()] = std::move(callback);
+ }
+
+ Timestamp pts = Timestamp::Micros((input_frame.timestamp() / k90kHz).us());
+
+ task_queue_.PostScheduledTask(
+ [this, input_frame, encoding_settings] {
+ analyzer_->StartEncode(input_frame, encoding_settings);
+
+ if (!last_encoding_settings_ ||
+ !IsSameRate(encoding_settings, *last_encoding_settings_)) {
+ SetRates(encoding_settings);
+ }
+
+ int error = encoder_->Encode(input_frame, /*frame_types=*/nullptr);
+ if (error != 0) {
+ RTC_LOG(LS_WARNING) << "Encode failed with error code " << error
+ << " RTP timestamp " << input_frame.timestamp();
+ }
+
+ last_encoding_settings_ = encoding_settings;
+ },
+ pacer_.Schedule(pts));
+
+ if (y4m_writer_) {
+ y4m_writer_->Write(input_frame, /*spatial_idx=*/0);
+ }
+ }
+
+ void Flush() {
+ task_queue_.PostTaskAndWait([this] { encoder_->Release(); });
+ }
+
+ private:
+ Result OnEncodedImage(const EncodedImage& encoded_frame,
+ const CodecSpecificInfo* codec_specific_info) override {
+ analyzer_->FinishEncode(encoded_frame);
+
+ {
+ MutexLock lock(&mutex_);
+ auto it = callbacks_.find(encoded_frame.RtpTimestamp());
+ RTC_CHECK(it != callbacks_.end());
+ it->second(encoded_frame);
+ callbacks_.erase(callbacks_.begin(), it);
+ }
+
+ if (ivf_writer_ != nullptr) {
+ ivf_writer_->Write(encoded_frame);
+ }
+
+ return Result(Result::Error::OK);
+ }
+
+ void Configure(const EncodingSettings& es) {
+ const LayerSettings& layer_settings = es.layers_settings.rbegin()->second;
+ const DataRate& bitrate = layer_settings.bitrate;
+
+ VideoCodec vc;
+ vc.width = layer_settings.resolution.width;
+ vc.height = layer_settings.resolution.height;
+ vc.startBitrate = bitrate.kbps();
+ vc.maxBitrate = bitrate.kbps();
+ vc.minBitrate = 0;
+ vc.maxFramerate = layer_settings.framerate.hertz<uint32_t>();
+ vc.active = true;
+ vc.numberOfSimulcastStreams = 0;
+ vc.mode = webrtc::VideoCodecMode::kRealtimeVideo;
+ vc.SetFrameDropEnabled(true);
+ vc.SetScalabilityMode(es.scalability_mode);
+ vc.SetVideoEncoderComplexity(VideoCodecComplexity::kComplexityNormal);
+
+ vc.codecType = PayloadStringToCodecType(es.sdp_video_format.name);
+ switch (vc.codecType) {
+ case kVideoCodecVP8:
+ *(vc.VP8()) = VideoEncoder::GetDefaultVp8Settings();
+ vc.VP8()->SetNumberOfTemporalLayers(
+ ScalabilityModeToNumTemporalLayers(es.scalability_mode));
+ vc.qpMax = cricket::kDefaultVideoMaxQpVpx;
+ // TODO(webrtc:14852): Configure simulcast.
+ break;
+ case kVideoCodecVP9:
+ *(vc.VP9()) = VideoEncoder::GetDefaultVp9Settings();
+ // See LibvpxVp9Encoder::ExplicitlyConfiguredSpatialLayers.
+ vc.spatialLayers[0].targetBitrate = vc.maxBitrate;
+ vc.qpMax = cricket::kDefaultVideoMaxQpVpx;
+ break;
+ case kVideoCodecAV1:
+ vc.qpMax = cricket::kDefaultVideoMaxQpVpx;
+ break;
+ case kVideoCodecH264:
+ *(vc.H264()) = VideoEncoder::GetDefaultH264Settings();
+ vc.qpMax = cricket::kDefaultVideoMaxQpH26x;
+ break;
+ case kVideoCodecH265:
+ vc.qpMax = cricket::kDefaultVideoMaxQpH26x;
+ break;
+ case kVideoCodecGeneric:
+ case kVideoCodecMultiplex:
+ RTC_CHECK_NOTREACHED();
+ break;
+ }
+
+ VideoEncoder::Settings ves(
+ VideoEncoder::Capabilities(/*loss_notification=*/false),
+ /*number_of_cores=*/1,
+ /*max_payload_size=*/1440);
+
+ int result = encoder_->InitEncode(&vc, ves);
+ RTC_CHECK(result == WEBRTC_VIDEO_CODEC_OK);
+
+ SetRates(es);
+ }
+
+ void SetRates(const EncodingSettings& es) {
+ VideoEncoder::RateControlParameters rc;
+ int num_spatial_layers =
+ ScalabilityModeToNumSpatialLayers(es.scalability_mode);
+ int num_temporal_layers =
+ ScalabilityModeToNumTemporalLayers(es.scalability_mode);
+ for (int sidx = 0; sidx < num_spatial_layers; ++sidx) {
+ for (int tidx = 0; tidx < num_temporal_layers; ++tidx) {
+ auto layers_settings = es.layers_settings.find(
+ {.spatial_idx = sidx, .temporal_idx = tidx});
+ RTC_CHECK(layers_settings != es.layers_settings.end())
+ << "Bitrate for layer S=" << sidx << " T=" << tidx << " is not set";
+ rc.bitrate.SetBitrate(sidx, tidx,
+ layers_settings->second.bitrate.bps());
+ }
+ }
+ rc.framerate_fps =
+ es.layers_settings.rbegin()->second.framerate.hertz<double>();
+ encoder_->SetRates(rc);
+ }
+
+ bool IsSameRate(const EncodingSettings& a, const EncodingSettings& b) const {
+ for (auto [layer_id, layer] : a.layers_settings) {
+ const auto& other_layer = b.layers_settings.at(layer_id);
+ if (layer.bitrate != other_layer.bitrate ||
+ layer.framerate != other_layer.framerate) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ VideoEncoderFactory* const encoder_factory_;
+ std::unique_ptr<VideoEncoder> encoder_;
+ VideoCodecAnalyzer* const analyzer_;
+ Pacer pacer_;
+ absl::optional<EncodingSettings> last_encoding_settings_;
+ std::unique_ptr<VideoBitrateAllocator> bitrate_allocator_;
+ LimitedTaskQueue task_queue_;
+ std::unique_ptr<TesterY4mWriter> y4m_writer_;
+ std::unique_ptr<TesterIvfWriter> ivf_writer_;
+ std::map<uint32_t, int> sidx_ RTC_GUARDED_BY(mutex_);
+ std::map<uint32_t, EncodeCallback> callbacks_ RTC_GUARDED_BY(mutex_);
+ Mutex mutex_;
+};
+
+std::tuple<std::vector<DataRate>, ScalabilityMode>
+SplitBitrateAndUpdateScalabilityMode(std::string codec_type,
+ ScalabilityMode scalability_mode,
+ int width,
+ int height,
+ std::vector<int> bitrates_kbps,
+ double framerate_fps) {
+ int num_spatial_layers = ScalabilityModeToNumSpatialLayers(scalability_mode);
+ int num_temporal_layers =
+ ScalabilityModeToNumTemporalLayers(scalability_mode);
+
+ if (bitrates_kbps.size() > 1 ||
+ (num_spatial_layers == 1 && num_temporal_layers == 1)) {
+ RTC_CHECK(bitrates_kbps.size() ==
+ static_cast<size_t>(num_spatial_layers * num_temporal_layers))
+ << "bitrates must be provided for all layers";
+ std::vector<DataRate> bitrates;
+ for (const auto& bitrate_kbps : bitrates_kbps) {
+ bitrates.push_back(DataRate::KilobitsPerSec(bitrate_kbps));
+ }
+ return std::make_tuple(bitrates, scalability_mode);
+ }
+
+ VideoCodec vc;
+ vc.codecType = PayloadStringToCodecType(codec_type);
+ vc.width = width;
+ vc.height = height;
+ vc.startBitrate = bitrates_kbps.front();
+ vc.maxBitrate = bitrates_kbps.front();
+ vc.minBitrate = 0;
+ vc.maxFramerate = static_cast<uint32_t>(framerate_fps);
+ vc.numberOfSimulcastStreams = 0;
+ vc.mode = webrtc::VideoCodecMode::kRealtimeVideo;
+ vc.SetScalabilityMode(scalability_mode);
+
+ switch (vc.codecType) {
+ case kVideoCodecVP8:
+ // TODO(webrtc:14852): Configure simulcast.
+ *(vc.VP8()) = VideoEncoder::GetDefaultVp8Settings();
+ vc.VP8()->SetNumberOfTemporalLayers(num_temporal_layers);
+ vc.simulcastStream[0].width = vc.width;
+ vc.simulcastStream[0].height = vc.height;
+ break;
+ case kVideoCodecVP9: {
+ *(vc.VP9()) = VideoEncoder::GetDefaultVp9Settings();
+ vc.VP9()->SetNumberOfTemporalLayers(num_temporal_layers);
+ const std::vector<SpatialLayer> spatialLayers = GetVp9SvcConfig(vc);
+ for (size_t i = 0; i < spatialLayers.size(); ++i) {
+ vc.spatialLayers[i] = spatialLayers[i];
+ vc.spatialLayers[i].active = true;
+ }
+ } break;
+ case kVideoCodecAV1: {
+ bool result =
+ SetAv1SvcConfig(vc, num_spatial_layers, num_temporal_layers);
+ RTC_CHECK(result) << "SetAv1SvcConfig failed";
+ } break;
+ case kVideoCodecH264: {
+ *(vc.H264()) = VideoEncoder::GetDefaultH264Settings();
+ vc.H264()->SetNumberOfTemporalLayers(num_temporal_layers);
+ } break;
+ case kVideoCodecH265:
+ break;
+ case kVideoCodecGeneric:
+ case kVideoCodecMultiplex:
+ RTC_CHECK_NOTREACHED();
+ }
+
+ if (*vc.GetScalabilityMode() != scalability_mode) {
+ RTC_LOG(LS_WARNING) << "Scalability mode changed from "
+ << ScalabilityModeToString(scalability_mode) << " to "
+ << ScalabilityModeToString(*vc.GetScalabilityMode());
+ num_spatial_layers =
+ ScalabilityModeToNumSpatialLayers(*vc.GetScalabilityMode());
+ num_temporal_layers =
+ ScalabilityModeToNumTemporalLayers(*vc.GetScalabilityMode());
+ }
+
+ std::unique_ptr<VideoBitrateAllocator> bitrate_allocator =
+ CreateBuiltinVideoBitrateAllocatorFactory()->CreateVideoBitrateAllocator(
+ vc);
+ VideoBitrateAllocation bitrate_allocation =
+ bitrate_allocator->Allocate(VideoBitrateAllocationParameters(
+ 1000 * bitrates_kbps.front(), framerate_fps));
+
+ std::vector<DataRate> bitrates;
+ for (int sidx = 0; sidx < num_spatial_layers; ++sidx) {
+ for (int tidx = 0; tidx < num_temporal_layers; ++tidx) {
+ int bitrate_bps = bitrate_allocation.GetBitrate(sidx, tidx);
+ bitrates.push_back(DataRate::BitsPerSec(bitrate_bps));
+ }
+ }
+
+ return std::make_tuple(bitrates, *vc.GetScalabilityMode());
+}
+
+} // namespace
+
+void VideoCodecStats::Stream::LogMetrics(
+ MetricsLogger* logger,
+ std::string test_case_name,
+ std::string prefix,
+ std::map<std::string, std::string> metadata) const {
+ logger->LogMetric(prefix + "width", test_case_name, width, Unit::kCount,
+ ImprovementDirection::kBiggerIsBetter, metadata);
+ logger->LogMetric(prefix + "height", test_case_name, height, Unit::kCount,
+ ImprovementDirection::kBiggerIsBetter, metadata);
+ logger->LogMetric(prefix + "frame_size_bytes", test_case_name,
+ frame_size_bytes, Unit::kBytes,
+ ImprovementDirection::kNeitherIsBetter, metadata);
+ logger->LogMetric(prefix + "keyframe", test_case_name, keyframe, Unit::kCount,
+ ImprovementDirection::kSmallerIsBetter, metadata);
+ logger->LogMetric(prefix + "qp", test_case_name, qp, Unit::kUnitless,
+ ImprovementDirection::kSmallerIsBetter, metadata);
+ // TODO(webrtc:14852): Change to us or even ns.
+ logger->LogMetric(prefix + "encode_time_ms", test_case_name, encode_time_ms,
+ Unit::kMilliseconds, ImprovementDirection::kSmallerIsBetter,
+ metadata);
+ logger->LogMetric(prefix + "decode_time_ms", test_case_name, decode_time_ms,
+ Unit::kMilliseconds, ImprovementDirection::kSmallerIsBetter,
+ metadata);
+ // TODO(webrtc:14852): Change to kUnitLess. kKilobitsPerSecond are converted
+ // to bytes per second in Chromeperf dash.
+ logger->LogMetric(prefix + "target_bitrate_kbps", test_case_name,
+ target_bitrate_kbps, Unit::kKilobitsPerSecond,
+ ImprovementDirection::kBiggerIsBetter, metadata);
+ logger->LogMetric(prefix + "target_framerate_fps", test_case_name,
+ target_framerate_fps, Unit::kHertz,
+ ImprovementDirection::kBiggerIsBetter, metadata);
+ // TODO(webrtc:14852): Change to kUnitLess. kKilobitsPerSecond are converted
+ // to bytes per second in Chromeperf dash.
+ logger->LogMetric(prefix + "encoded_bitrate_kbps", test_case_name,
+ encoded_bitrate_kbps, Unit::kKilobitsPerSecond,
+ ImprovementDirection::kBiggerIsBetter, metadata);
+ logger->LogMetric(prefix + "encoded_framerate_fps", test_case_name,
+ encoded_framerate_fps, Unit::kHertz,
+ ImprovementDirection::kBiggerIsBetter, metadata);
+ logger->LogMetric(prefix + "bitrate_mismatch_pct", test_case_name,
+ bitrate_mismatch_pct, Unit::kPercent,
+ ImprovementDirection::kNeitherIsBetter, metadata);
+ logger->LogMetric(prefix + "framerate_mismatch_pct", test_case_name,
+ framerate_mismatch_pct, Unit::kPercent,
+ ImprovementDirection::kNeitherIsBetter, metadata);
+ logger->LogMetric(prefix + "transmission_time_ms", test_case_name,
+ transmission_time_ms, Unit::kMilliseconds,
+ ImprovementDirection::kSmallerIsBetter, metadata);
+ logger->LogMetric(prefix + "psnr_y_db", test_case_name, psnr.y,
+ Unit::kUnitless, ImprovementDirection::kBiggerIsBetter,
+ metadata);
+ logger->LogMetric(prefix + "psnr_u_db", test_case_name, psnr.u,
+ Unit::kUnitless, ImprovementDirection::kBiggerIsBetter,
+ metadata);
+ logger->LogMetric(prefix + "psnr_v_db", test_case_name, psnr.v,
+ Unit::kUnitless, ImprovementDirection::kBiggerIsBetter,
+ metadata);
+}
+
+// TODO(ssilkin): use Frequency and DataRate for framerate and bitrate.
+std::map<uint32_t, EncodingSettings> VideoCodecTester::CreateEncodingSettings(
+ std::string codec_type,
+ std::string scalability_name,
+ int width,
+ int height,
+ std::vector<int> layer_bitrates_kbps,
+ double framerate_fps,
+ int num_frames,
+ uint32_t first_timestamp_rtp) {
+ auto [layer_bitrates, scalability_mode] =
+ SplitBitrateAndUpdateScalabilityMode(
+ codec_type, *ScalabilityModeFromString(scalability_name), width,
+ height, layer_bitrates_kbps, framerate_fps);
+
+ int num_spatial_layers = ScalabilityModeToNumSpatialLayers(scalability_mode);
+ int num_temporal_layers =
+ ScalabilityModeToNumTemporalLayers(scalability_mode);
+
+ std::map<LayerId, LayerSettings> layers_settings;
+ for (int sidx = 0; sidx < num_spatial_layers; ++sidx) {
+ int layer_width = width >> (num_spatial_layers - sidx - 1);
+ int layer_height = height >> (num_spatial_layers - sidx - 1);
+ for (int tidx = 0; tidx < num_temporal_layers; ++tidx) {
+ double layer_framerate_fps =
+ framerate_fps / (1 << (num_temporal_layers - tidx - 1));
+ layers_settings.emplace(
+ LayerId{.spatial_idx = sidx, .temporal_idx = tidx},
+ LayerSettings{
+ .resolution = {.width = layer_width, .height = layer_height},
+ .framerate = Frequency::MilliHertz(1000 * layer_framerate_fps),
+ .bitrate = layer_bitrates[sidx * num_temporal_layers + tidx]});
+ }
+ }
+
+ std::map<uint32_t, EncodingSettings> frames_settings;
+ uint32_t timestamp_rtp = first_timestamp_rtp;
+ for (int frame_num = 0; frame_num < num_frames; ++frame_num) {
+ frames_settings.emplace(
+ timestamp_rtp,
+ EncodingSettings{.sdp_video_format = SdpVideoFormat(codec_type),
+ .scalability_mode = scalability_mode,
+ .layers_settings = layers_settings});
+
+ timestamp_rtp += k90kHz / Frequency::MilliHertz(1000 * framerate_fps);
+ }
+
+ return frames_settings;
+}
+
+std::unique_ptr<VideoCodecTester::VideoCodecStats>
+VideoCodecTester::RunDecodeTest(CodedVideoSource* video_source,
+ VideoDecoderFactory* decoder_factory,
+ const DecoderSettings& decoder_settings,
+ const SdpVideoFormat& sdp_video_format) {
+ std::unique_ptr<VideoCodecAnalyzer> analyzer =
+ std::make_unique<VideoCodecAnalyzer>(/*video_source=*/nullptr);
+ Decoder decoder(decoder_factory, decoder_settings, analyzer.get());
+ decoder.Initialize(sdp_video_format);
+
+ while (auto frame = video_source->PullFrame()) {
+ decoder.Decode(*frame);
+ }
+
+ decoder.Flush();
+ analyzer->Flush();
+ return std::move(analyzer);
+}
+
+std::unique_ptr<VideoCodecTester::VideoCodecStats>
+VideoCodecTester::RunEncodeTest(
+ const VideoSourceSettings& source_settings,
+ VideoEncoderFactory* encoder_factory,
+ const EncoderSettings& encoder_settings,
+ const std::map<uint32_t, EncodingSettings>& encoding_settings) {
+ VideoSource video_source(source_settings);
+ std::unique_ptr<VideoCodecAnalyzer> analyzer =
+ std::make_unique<VideoCodecAnalyzer>(/*video_source=*/nullptr);
+ Encoder encoder(encoder_factory, encoder_settings, analyzer.get());
+ encoder.Initialize(encoding_settings.begin()->second);
+
+ for (const auto& [timestamp_rtp, frame_settings] : encoding_settings) {
+ const EncodingSettings::LayerSettings& top_layer =
+ frame_settings.layers_settings.rbegin()->second;
+ VideoFrame source_frame = video_source.PullFrame(
+ timestamp_rtp, top_layer.resolution, top_layer.framerate);
+ encoder.Encode(source_frame, frame_settings,
+ [](const EncodedImage& encoded_frame) {});
+ }
+
+ encoder.Flush();
+ analyzer->Flush();
+ return std::move(analyzer);
+}
+
+std::unique_ptr<VideoCodecTester::VideoCodecStats>
+VideoCodecTester::RunEncodeDecodeTest(
+ const VideoSourceSettings& source_settings,
+ VideoEncoderFactory* encoder_factory,
+ VideoDecoderFactory* decoder_factory,
+ const EncoderSettings& encoder_settings,
+ const DecoderSettings& decoder_settings,
+ const std::map<uint32_t, EncodingSettings>& encoding_settings) {
+ VideoSource video_source(source_settings);
+ std::unique_ptr<VideoCodecAnalyzer> analyzer =
+ std::make_unique<VideoCodecAnalyzer>(&video_source);
+ Decoder decoder(decoder_factory, decoder_settings, analyzer.get());
+ Encoder encoder(encoder_factory, encoder_settings, analyzer.get());
+ encoder.Initialize(encoding_settings.begin()->second);
+ decoder.Initialize(encoding_settings.begin()->second.sdp_video_format);
+
+ for (const auto& [timestamp_rtp, frame_settings] : encoding_settings) {
+ const EncodingSettings::LayerSettings& top_layer =
+ frame_settings.layers_settings.rbegin()->second;
+ VideoFrame source_frame = video_source.PullFrame(
+ timestamp_rtp, top_layer.resolution, top_layer.framerate);
+ encoder.Encode(source_frame, frame_settings,
+ [&decoder](const EncodedImage& encoded_frame) {
+ decoder.Decode(encoded_frame);
+ });
+ }
+
+ encoder.Flush();
+ decoder.Flush();
+ analyzer->Flush();
+ return std::move(analyzer);
+}
+
+} // namespace test
+} // namespace webrtc
diff --git a/third_party/libwebrtc/test/video_codec_tester.h b/third_party/libwebrtc/test/video_codec_tester.h
new file mode 100644
index 0000000000..87cc5f76f8
--- /dev/null
+++ b/third_party/libwebrtc/test/video_codec_tester.h
@@ -0,0 +1,227 @@
+/*
+ * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef TEST_VIDEO_CODEC_TESTER_H_
+#define TEST_VIDEO_CODEC_TESTER_H_
+
+#include <limits>
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "absl/types/optional.h"
+#include "api/numerics/samples_stats_counter.h"
+#include "api/test/metrics/metric.h"
+#include "api/test/metrics/metrics_logger.h"
+#include "api/units/data_rate.h"
+#include "api/units/data_size.h"
+#include "api/units/frequency.h"
+#include "api/video/encoded_image.h"
+#include "api/video/resolution.h"
+#include "api/video_codecs/video_decoder_factory.h"
+#include "api/video_codecs/video_encoder_factory.h"
+
+namespace webrtc {
+namespace test {
+
+class VideoCodecTester {
+ public:
+ struct LayerId {
+ int spatial_idx = 0;
+ int temporal_idx = 0;
+
+ bool operator==(const LayerId& o) const {
+ return spatial_idx == o.spatial_idx && temporal_idx == o.temporal_idx;
+ }
+ bool operator<(const LayerId& o) const {
+ return spatial_idx < o.spatial_idx ||
+ (spatial_idx == o.spatial_idx && temporal_idx < o.temporal_idx);
+ }
+ };
+
+ struct EncodingSettings {
+ SdpVideoFormat sdp_video_format = SdpVideoFormat("VP8");
+ ScalabilityMode scalability_mode = ScalabilityMode::kL1T1;
+
+ struct LayerSettings {
+ Resolution resolution;
+ Frequency framerate;
+ DataRate bitrate;
+ };
+ std::map<LayerId, LayerSettings> layers_settings;
+ };
+
+ class VideoCodecStats {
+ public:
+ struct Filter {
+ uint32_t min_timestamp_rtp = std::numeric_limits<uint32_t>::min();
+ uint32_t max_timestamp_rtp = std::numeric_limits<uint32_t>::max();
+ absl::optional<LayerId> layer_id;
+ };
+
+ struct Frame {
+ uint32_t timestamp_rtp = 0;
+ LayerId layer_id;
+ bool encoded = false;
+ bool decoded = false;
+ int width = 0;
+ int height = 0;
+ DataSize frame_size = DataSize::Zero();
+ bool keyframe = false;
+ absl::optional<int> qp;
+ Timestamp encode_start = Timestamp::Zero();
+ TimeDelta encode_time = TimeDelta::Zero();
+ Timestamp decode_start = Timestamp::Zero();
+ TimeDelta decode_time = TimeDelta::Zero();
+ absl::optional<DataRate> target_bitrate;
+ absl::optional<Frequency> target_framerate;
+
+ struct Psnr {
+ double y = 0.0;
+ double u = 0.0;
+ double v = 0.0;
+ };
+ absl::optional<Psnr> psnr;
+ };
+
+ struct Stream {
+ SamplesStatsCounter width;
+ SamplesStatsCounter height;
+ SamplesStatsCounter frame_size_bytes;
+ SamplesStatsCounter keyframe;
+ SamplesStatsCounter qp;
+ SamplesStatsCounter encode_time_ms;
+ SamplesStatsCounter decode_time_ms;
+ SamplesStatsCounter target_bitrate_kbps;
+ SamplesStatsCounter target_framerate_fps;
+ SamplesStatsCounter encoded_bitrate_kbps;
+ SamplesStatsCounter encoded_framerate_fps;
+ SamplesStatsCounter bitrate_mismatch_pct;
+ SamplesStatsCounter framerate_mismatch_pct;
+ SamplesStatsCounter transmission_time_ms;
+
+ struct Psnr {
+ SamplesStatsCounter y;
+ SamplesStatsCounter u;
+ SamplesStatsCounter v;
+ } psnr;
+
+ // Logs `Stream` metrics to provided `MetricsLogger`.
+ void LogMetrics(MetricsLogger* logger,
+ std::string test_case_name,
+ std::string prefix,
+ std::map<std::string, std::string> metadata = {}) const;
+ };
+
+ virtual ~VideoCodecStats() = default;
+
+ // Returns frames for the slice specified by `filter`. If `merge` is true,
+ // also merges frames belonging to the same temporal unit into one
+ // superframe.
+ virtual std::vector<Frame> Slice(Filter filter, bool merge) const = 0;
+
+ // Returns video statistics aggregated for the slice specified by `filter`.
+ virtual Stream Aggregate(Filter filter) const = 0;
+
+ // Write metrics to a CSV file.
+ virtual void LogMetrics(
+ absl::string_view csv_path,
+ std::vector<Frame> frames,
+ std::map<std::string, std::string> metadata) const = 0;
+ };
+
+ // Pacing settings for codec input.
+ struct PacingSettings {
+ enum PacingMode {
+ // Pacing is not used. Frames are sent to codec back-to-back.
+ kNoPacing,
+ // Pace with the rate equal to the target video frame rate. Pacing time is
+ // derived from RTP timestamp.
+ kRealTime,
+ // Pace with the explicitly provided rate.
+ kConstantRate,
+ };
+ PacingMode mode = PacingMode::kNoPacing;
+ // Pacing rate for `kConstantRate` mode.
+ Frequency constant_rate = Frequency::Zero();
+ };
+
+ struct VideoSourceSettings {
+ std::string file_path;
+ Resolution resolution;
+ Frequency framerate;
+ };
+
+ struct DecoderSettings {
+ PacingSettings pacing_settings;
+ absl::optional<std::string> decoder_input_base_path;
+ absl::optional<std::string> decoder_output_base_path;
+ };
+
+ struct EncoderSettings {
+ PacingSettings pacing_settings;
+ absl::optional<std::string> encoder_input_base_path;
+ absl::optional<std::string> encoder_output_base_path;
+ };
+
+ virtual ~VideoCodecTester() = default;
+
+ // Interface for a coded video frames source.
+ class CodedVideoSource {
+ public:
+ virtual ~CodedVideoSource() = default;
+
+ // Returns next frame. Returns `absl::nullopt` if the end-of-stream is
+ // reached. Frames should have RTP timestamps representing desired frame
+ // rate.
+ virtual absl::optional<EncodedImage> PullFrame() = 0;
+ };
+
+ // A helper function that creates `EncodingSettings` for `num_frames` frames,
+ // wraps the settings into RTP timestamp -> settings map and returns the map.
+ static std::map<uint32_t, EncodingSettings> CreateEncodingSettings(
+ std::string codec_type,
+ std::string scalability_name,
+ int width,
+ int height,
+ std::vector<int> bitrates_kbps,
+ double framerate_fps,
+ int num_frames,
+ uint32_t first_timestamp_rtp = 90000);
+
+ // Decodes video, collects and returns decode metrics.
+ static std::unique_ptr<VideoCodecStats> RunDecodeTest(
+ CodedVideoSource* video_source,
+ VideoDecoderFactory* decoder_factory,
+ const DecoderSettings& decoder_settings,
+ const SdpVideoFormat& sdp_video_format);
+
+ // Encodes video, collects and returns encode metrics.
+ static std::unique_ptr<VideoCodecStats> RunEncodeTest(
+ const VideoSourceSettings& source_settings,
+ VideoEncoderFactory* encoder_factory,
+ const EncoderSettings& encoder_settings,
+ const std::map<uint32_t, EncodingSettings>& encoding_settings);
+
+ // Encodes and decodes video, collects and returns encode and decode metrics.
+ static std::unique_ptr<VideoCodecStats> RunEncodeDecodeTest(
+ const VideoSourceSettings& source_settings,
+ VideoEncoderFactory* encoder_factory,
+ VideoDecoderFactory* decoder_factory,
+ const EncoderSettings& encoder_settings,
+ const DecoderSettings& decoder_settings,
+ const std::map<uint32_t, EncodingSettings>& encoding_settings);
+};
+
+} // namespace test
+} // namespace webrtc
+
+#endif // TEST_VIDEO_CODEC_TESTER_H_
diff --git a/third_party/libwebrtc/test/video_codec_tester_unittest.cc b/third_party/libwebrtc/test/video_codec_tester_unittest.cc
new file mode 100644
index 0000000000..af31fe2c13
--- /dev/null
+++ b/third_party/libwebrtc/test/video_codec_tester_unittest.cc
@@ -0,0 +1,513 @@
+/*
+ * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "test/video_codec_tester.h"
+
+#include <map>
+#include <memory>
+#include <string>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+#include "api/test/mock_video_decoder.h"
+#include "api/test/mock_video_decoder_factory.h"
+#include "api/test/mock_video_encoder.h"
+#include "api/test/mock_video_encoder_factory.h"
+#include "api/units/data_rate.h"
+#include "api/units/time_delta.h"
+#include "api/video/i420_buffer.h"
+#include "api/video/video_frame.h"
+#include "modules/video_coding/include/video_codec_interface.h"
+#include "modules/video_coding/svc/scalability_mode_util.h"
+#include "test/gmock.h"
+#include "test/gtest.h"
+#include "test/testsupport/file_utils.h"
+#include "third_party/libyuv/include/libyuv/planar_functions.h"
+
+namespace webrtc {
+namespace test {
+
+namespace {
+using ::testing::_;
+using ::testing::ElementsAre;
+using ::testing::Field;
+using ::testing::Invoke;
+using ::testing::InvokeWithoutArgs;
+using ::testing::NiceMock;
+using ::testing::Return;
+using ::testing::SizeIs;
+
+using VideoCodecStats = VideoCodecTester::VideoCodecStats;
+using VideoSourceSettings = VideoCodecTester::VideoSourceSettings;
+using CodedVideoSource = VideoCodecTester::CodedVideoSource;
+using EncodingSettings = VideoCodecTester::EncodingSettings;
+using LayerSettings = EncodingSettings::LayerSettings;
+using LayerId = VideoCodecTester::LayerId;
+using DecoderSettings = VideoCodecTester::DecoderSettings;
+using EncoderSettings = VideoCodecTester::EncoderSettings;
+using PacingSettings = VideoCodecTester::PacingSettings;
+using PacingMode = PacingSettings::PacingMode;
+using Filter = VideoCodecStats::Filter;
+using Frame = VideoCodecTester::VideoCodecStats::Frame;
+using Stream = VideoCodecTester::VideoCodecStats::Stream;
+
+constexpr int kWidth = 2;
+constexpr int kHeight = 2;
+const DataRate kTargetLayerBitrate = DataRate::BytesPerSec(100);
+const Frequency kTargetFramerate = Frequency::Hertz(30);
+constexpr Frequency k90kHz = Frequency::Hertz(90000);
+
+rtc::scoped_refptr<I420Buffer> CreateYuvBuffer(uint8_t y = 0,
+ uint8_t u = 0,
+ uint8_t v = 0) {
+ rtc::scoped_refptr<I420Buffer> buffer(I420Buffer::Create(2, 2));
+
+ libyuv::I420Rect(buffer->MutableDataY(), buffer->StrideY(),
+ buffer->MutableDataU(), buffer->StrideU(),
+ buffer->MutableDataV(), buffer->StrideV(), 0, 0,
+ buffer->width(), buffer->height(), y, u, v);
+ return buffer;
+}
+
+std::string CreateYuvFile(int width, int height, int num_frames) {
+ std::string path = webrtc::test::TempFilename(webrtc::test::OutputPath(),
+ "video_codec_tester_unittest");
+ FILE* file = fopen(path.c_str(), "wb");
+ for (int frame_num = 0; frame_num < num_frames; ++frame_num) {
+ uint8_t y = (frame_num + 0) & 255;
+ uint8_t u = (frame_num + 1) & 255;
+ uint8_t v = (frame_num + 2) & 255;
+ rtc::scoped_refptr<I420Buffer> buffer = CreateYuvBuffer(y, u, v);
+ fwrite(buffer->DataY(), 1, width * height, file);
+ int chroma_size_bytes = (width + 1) / 2 * (height + 1) / 2;
+ fwrite(buffer->DataU(), 1, chroma_size_bytes, file);
+ fwrite(buffer->DataV(), 1, chroma_size_bytes, file);
+ }
+ fclose(file);
+ return path;
+}
+
+std::unique_ptr<VideoCodecStats> RunTest(std::vector<std::vector<Frame>> frames,
+ ScalabilityMode scalability_mode) {
+ int num_frames = static_cast<int>(frames.size());
+ std::string source_yuv_path = CreateYuvFile(kWidth, kHeight, num_frames);
+ VideoSourceSettings source_settings{
+ .file_path = source_yuv_path,
+ .resolution = {.width = kWidth, .height = kHeight},
+ .framerate = kTargetFramerate};
+
+ int num_encoded_frames = 0;
+ EncodedImageCallback* encoded_frame_callback;
+ NiceMock<MockVideoEncoderFactory> encoder_factory;
+ ON_CALL(encoder_factory, CreateVideoEncoder)
+ .WillByDefault([&](const SdpVideoFormat&) {
+ auto encoder = std::make_unique<NiceMock<MockVideoEncoder>>();
+ ON_CALL(*encoder, RegisterEncodeCompleteCallback)
+ .WillByDefault([&](EncodedImageCallback* callback) {
+ encoded_frame_callback = callback;
+ return WEBRTC_VIDEO_CODEC_OK;
+ });
+ ON_CALL(*encoder, Encode)
+ .WillByDefault([&](const VideoFrame& input_frame,
+ const std::vector<VideoFrameType>*) {
+ for (const Frame& frame : frames[num_encoded_frames]) {
+ EncodedImage encoded_frame;
+ encoded_frame._encodedWidth = frame.width;
+ encoded_frame._encodedHeight = frame.height;
+ encoded_frame.SetFrameType(
+ frame.keyframe ? VideoFrameType::kVideoFrameKey
+ : VideoFrameType::kVideoFrameDelta);
+ encoded_frame.SetRtpTimestamp(input_frame.timestamp());
+ encoded_frame.SetSpatialIndex(frame.layer_id.spatial_idx);
+ encoded_frame.SetTemporalIndex(frame.layer_id.temporal_idx);
+ encoded_frame.SetEncodedData(
+ EncodedImageBuffer::Create(frame.frame_size.bytes()));
+ encoded_frame_callback->OnEncodedImage(
+ encoded_frame,
+ /*codec_specific_info=*/nullptr);
+ }
+ ++num_encoded_frames;
+ return WEBRTC_VIDEO_CODEC_OK;
+ });
+ return encoder;
+ });
+
+ int num_decoded_frames = 0;
+ DecodedImageCallback* decode_callback;
+ NiceMock<MockVideoDecoderFactory> decoder_factory;
+ ON_CALL(decoder_factory, CreateVideoDecoder)
+ .WillByDefault([&](const SdpVideoFormat&) {
+ auto decoder = std::make_unique<NiceMock<MockVideoDecoder>>();
+ ON_CALL(*decoder, RegisterDecodeCompleteCallback)
+ .WillByDefault([&](DecodedImageCallback* callback) {
+ decode_callback = callback;
+ return WEBRTC_VIDEO_CODEC_OK;
+ });
+ ON_CALL(*decoder, Decode(_, _))
+ .WillByDefault([&](const EncodedImage& encoded_frame, int64_t) {
+ // Make values to be different from source YUV generated in
+ // `CreateYuvFile`.
+ uint8_t y = ((num_decoded_frames + 1) * 2) & 255;
+ uint8_t u = ((num_decoded_frames + 2) * 2) & 255;
+ uint8_t v = ((num_decoded_frames + 3) * 2) & 255;
+ rtc::scoped_refptr<I420Buffer> frame_buffer =
+ CreateYuvBuffer(y, u, v);
+ VideoFrame decoded_frame =
+ VideoFrame::Builder()
+ .set_video_frame_buffer(frame_buffer)
+ .set_timestamp_rtp(encoded_frame.RtpTimestamp())
+ .build();
+ decode_callback->Decoded(decoded_frame);
+ ++num_decoded_frames;
+ return WEBRTC_VIDEO_CODEC_OK;
+ });
+ return decoder;
+ });
+
+ int num_spatial_layers = ScalabilityModeToNumSpatialLayers(scalability_mode);
+ int num_temporal_layers =
+ ScalabilityModeToNumTemporalLayers(scalability_mode);
+
+ std::map<uint32_t, EncodingSettings> encoding_settings;
+ for (int frame_num = 0; frame_num < num_frames; ++frame_num) {
+ std::map<LayerId, LayerSettings> layers_settings;
+ for (int sidx = 0; sidx < num_spatial_layers; ++sidx) {
+ for (int tidx = 0; tidx < num_temporal_layers; ++tidx) {
+ layers_settings.emplace(
+ LayerId{.spatial_idx = sidx, .temporal_idx = tidx},
+ LayerSettings{.resolution = {.width = kWidth, .height = kHeight},
+ .framerate = kTargetFramerate /
+ (1 << (num_temporal_layers - 1 - tidx)),
+ .bitrate = kTargetLayerBitrate});
+ }
+ }
+ encoding_settings.emplace(
+ frames[frame_num][0].timestamp_rtp,
+ EncodingSettings{.scalability_mode = scalability_mode,
+ .layers_settings = layers_settings});
+ }
+
+ EncoderSettings encoder_settings;
+ DecoderSettings decoder_settings;
+ std::unique_ptr<VideoCodecStats> stats =
+ VideoCodecTester::RunEncodeDecodeTest(
+ source_settings, &encoder_factory, &decoder_factory, encoder_settings,
+ decoder_settings, encoding_settings);
+ remove(source_yuv_path.c_str());
+ return stats;
+}
+
+EncodedImage CreateEncodedImage(uint32_t timestamp_rtp) {
+ EncodedImage encoded_image;
+ encoded_image.SetRtpTimestamp(timestamp_rtp);
+ return encoded_image;
+}
+
+class MockCodedVideoSource : public CodedVideoSource {
+ public:
+ MockCodedVideoSource(int num_frames, Frequency framerate)
+ : num_frames_(num_frames), frame_num_(0), framerate_(framerate) {}
+
+ absl::optional<EncodedImage> PullFrame() override {
+ if (frame_num_ >= num_frames_) {
+ return absl::nullopt;
+ }
+ uint32_t timestamp_rtp = frame_num_ * k90kHz / framerate_;
+ ++frame_num_;
+ return CreateEncodedImage(timestamp_rtp);
+ }
+
+ private:
+ int num_frames_;
+ int frame_num_;
+ Frequency framerate_;
+};
+
+} // namespace
+
+TEST(VideoCodecTester, Slice) {
+ std::unique_ptr<VideoCodecStats> stats = RunTest(
+ {{{.timestamp_rtp = 0, .layer_id = {.spatial_idx = 0, .temporal_idx = 0}},
+ {.timestamp_rtp = 0,
+ .layer_id = {.spatial_idx = 1, .temporal_idx = 0}}},
+ {{.timestamp_rtp = 1,
+ .layer_id = {.spatial_idx = 0, .temporal_idx = 1}}}},
+ ScalabilityMode::kL2T2);
+ std::vector<Frame> slice = stats->Slice(Filter{}, /*merge=*/false);
+ EXPECT_THAT(slice, ElementsAre(Field(&Frame::timestamp_rtp, 0),
+ Field(&Frame::timestamp_rtp, 0),
+ Field(&Frame::timestamp_rtp, 1)));
+
+ slice = stats->Slice({.min_timestamp_rtp = 1}, /*merge=*/false);
+ EXPECT_THAT(slice, ElementsAre(Field(&Frame::timestamp_rtp, 1)));
+
+ slice = stats->Slice({.max_timestamp_rtp = 0}, /*merge=*/false);
+ EXPECT_THAT(slice, ElementsAre(Field(&Frame::timestamp_rtp, 0),
+ Field(&Frame::timestamp_rtp, 0)));
+
+ slice = stats->Slice({.layer_id = {{.spatial_idx = 0, .temporal_idx = 0}}},
+ /*merge=*/false);
+ EXPECT_THAT(slice, ElementsAre(Field(&Frame::timestamp_rtp, 0)));
+
+ slice = stats->Slice({.layer_id = {{.spatial_idx = 0, .temporal_idx = 1}}},
+ /*merge=*/false);
+ EXPECT_THAT(slice, ElementsAre(Field(&Frame::timestamp_rtp, 0),
+ Field(&Frame::timestamp_rtp, 1)));
+}
+
+TEST(VideoCodecTester, Merge) {
+ std::unique_ptr<VideoCodecStats> stats =
+ RunTest({{{.timestamp_rtp = 0,
+ .layer_id = {.spatial_idx = 0, .temporal_idx = 0},
+ .frame_size = DataSize::Bytes(1),
+ .keyframe = true},
+ {.timestamp_rtp = 0,
+ .layer_id = {.spatial_idx = 1, .temporal_idx = 0},
+ .frame_size = DataSize::Bytes(2)}},
+ {{.timestamp_rtp = 1,
+ .layer_id = {.spatial_idx = 0, .temporal_idx = 1},
+ .frame_size = DataSize::Bytes(4)},
+ {.timestamp_rtp = 1,
+ .layer_id = {.spatial_idx = 1, .temporal_idx = 1},
+ .frame_size = DataSize::Bytes(8)}}},
+ ScalabilityMode::kL2T2_KEY);
+
+ std::vector<Frame> slice = stats->Slice(Filter{}, /*merge=*/true);
+ EXPECT_THAT(
+ slice,
+ ElementsAre(
+ AllOf(Field(&Frame::timestamp_rtp, 0), Field(&Frame::keyframe, true),
+ Field(&Frame::frame_size, DataSize::Bytes(3))),
+ AllOf(Field(&Frame::timestamp_rtp, 1), Field(&Frame::keyframe, false),
+ Field(&Frame::frame_size, DataSize::Bytes(12)))));
+}
+
+struct AggregationTestParameters {
+ Filter filter;
+ double expected_keyframe_sum;
+ double expected_encoded_bitrate_kbps;
+ double expected_encoded_framerate_fps;
+ double expected_bitrate_mismatch_pct;
+ double expected_framerate_mismatch_pct;
+};
+
+class VideoCodecTesterTestAggregation
+ : public ::testing::TestWithParam<AggregationTestParameters> {};
+
+TEST_P(VideoCodecTesterTestAggregation, Aggregate) {
+ AggregationTestParameters test_params = GetParam();
+ std::unique_ptr<VideoCodecStats> stats =
+ RunTest({{// L0T0
+ {.timestamp_rtp = 0,
+ .layer_id = {.spatial_idx = 0, .temporal_idx = 0},
+ .frame_size = DataSize::Bytes(1),
+ .keyframe = true},
+ // L1T0
+ {.timestamp_rtp = 0,
+ .layer_id = {.spatial_idx = 1, .temporal_idx = 0},
+ .frame_size = DataSize::Bytes(2)}},
+ // Emulate frame drop (frame_size = 0).
+ {{.timestamp_rtp = 3000,
+ .layer_id = {.spatial_idx = 0, .temporal_idx = 0},
+ .frame_size = DataSize::Zero()}},
+ {// L0T1
+ {.timestamp_rtp = 87000,
+ .layer_id = {.spatial_idx = 0, .temporal_idx = 1},
+ .frame_size = DataSize::Bytes(4)},
+ // L1T1
+ {.timestamp_rtp = 87000,
+ .layer_id = {.spatial_idx = 1, .temporal_idx = 1},
+ .frame_size = DataSize::Bytes(8)}}},
+ ScalabilityMode::kL2T2_KEY);
+
+ Stream stream = stats->Aggregate(test_params.filter);
+ EXPECT_EQ(stream.keyframe.GetSum(), test_params.expected_keyframe_sum);
+ EXPECT_EQ(stream.encoded_bitrate_kbps.GetAverage(),
+ test_params.expected_encoded_bitrate_kbps);
+ EXPECT_EQ(stream.encoded_framerate_fps.GetAverage(),
+ test_params.expected_encoded_framerate_fps);
+ EXPECT_EQ(stream.bitrate_mismatch_pct.GetAverage(),
+ test_params.expected_bitrate_mismatch_pct);
+ EXPECT_EQ(stream.framerate_mismatch_pct.GetAverage(),
+ test_params.expected_framerate_mismatch_pct);
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ All,
+ VideoCodecTesterTestAggregation,
+ ::testing::Values(
+ // No filtering.
+ AggregationTestParameters{
+ .filter = {},
+ .expected_keyframe_sum = 1,
+ .expected_encoded_bitrate_kbps =
+ DataRate::BytesPerSec(15).kbps<double>(),
+ .expected_encoded_framerate_fps = 2,
+ .expected_bitrate_mismatch_pct =
+ 100 * (15.0 / (kTargetLayerBitrate.bytes_per_sec() * 4) - 1),
+ .expected_framerate_mismatch_pct =
+ 100 * (2.0 / kTargetFramerate.hertz() - 1)},
+ // L0T0
+ AggregationTestParameters{
+ .filter = {.layer_id = {{.spatial_idx = 0, .temporal_idx = 0}}},
+ .expected_keyframe_sum = 1,
+ .expected_encoded_bitrate_kbps =
+ DataRate::BytesPerSec(1).kbps<double>(),
+ .expected_encoded_framerate_fps = 1,
+ .expected_bitrate_mismatch_pct =
+ 100 * (1.0 / kTargetLayerBitrate.bytes_per_sec() - 1),
+ .expected_framerate_mismatch_pct =
+ 100 * (1.0 / (kTargetFramerate.hertz() / 2) - 1)},
+ // L0T1
+ AggregationTestParameters{
+ .filter = {.layer_id = {{.spatial_idx = 0, .temporal_idx = 1}}},
+ .expected_keyframe_sum = 1,
+ .expected_encoded_bitrate_kbps =
+ DataRate::BytesPerSec(5).kbps<double>(),
+ .expected_encoded_framerate_fps = 2,
+ .expected_bitrate_mismatch_pct =
+ 100 * (5.0 / (kTargetLayerBitrate.bytes_per_sec() * 2) - 1),
+ .expected_framerate_mismatch_pct =
+ 100 * (2.0 / kTargetFramerate.hertz() - 1)},
+ // L1T0
+ AggregationTestParameters{
+ .filter = {.layer_id = {{.spatial_idx = 1, .temporal_idx = 0}}},
+ .expected_keyframe_sum = 1,
+ .expected_encoded_bitrate_kbps =
+ DataRate::BytesPerSec(3).kbps<double>(),
+ .expected_encoded_framerate_fps = 1,
+ .expected_bitrate_mismatch_pct =
+ 100 * (3.0 / kTargetLayerBitrate.bytes_per_sec() - 1),
+ .expected_framerate_mismatch_pct =
+ 100 * (1.0 / (kTargetFramerate.hertz() / 2) - 1)},
+ // L1T1
+ AggregationTestParameters{
+ .filter = {.layer_id = {{.spatial_idx = 1, .temporal_idx = 1}}},
+ .expected_keyframe_sum = 1,
+ .expected_encoded_bitrate_kbps =
+ DataRate::BytesPerSec(11).kbps<double>(),
+ .expected_encoded_framerate_fps = 2,
+ .expected_bitrate_mismatch_pct =
+ 100 * (11.0 / (kTargetLayerBitrate.bytes_per_sec() * 2) - 1),
+ .expected_framerate_mismatch_pct =
+ 100 * (2.0 / kTargetFramerate.hertz() - 1)}));
+
+TEST(VideoCodecTester, Psnr) {
+ std::unique_ptr<VideoCodecStats> stats =
+ RunTest({{{.timestamp_rtp = 0, .frame_size = DataSize::Bytes(1)}},
+ {{.timestamp_rtp = 3000, .frame_size = DataSize::Bytes(1)}}},
+ ScalabilityMode::kL1T1);
+
+ std::vector<Frame> slice = stats->Slice(Filter{}, /*merge=*/false);
+ ASSERT_THAT(slice, SizeIs(2));
+ ASSERT_TRUE(slice[0].psnr.has_value());
+ ASSERT_TRUE(slice[1].psnr.has_value());
+ EXPECT_NEAR(slice[0].psnr->y, 42, 1);
+ EXPECT_NEAR(slice[0].psnr->u, 38, 1);
+ EXPECT_NEAR(slice[0].psnr->v, 36, 1);
+ EXPECT_NEAR(slice[1].psnr->y, 38, 1);
+ EXPECT_NEAR(slice[1].psnr->u, 36, 1);
+ EXPECT_NEAR(slice[1].psnr->v, 34, 1);
+}
+
+class VideoCodecTesterTestPacing
+ : public ::testing::TestWithParam<std::tuple<PacingSettings, int>> {
+ public:
+ const int kSourceWidth = 2;
+ const int kSourceHeight = 2;
+ const int kNumFrames = 3;
+ const int kTargetLayerBitrateKbps = 128;
+ const Frequency kTargetFramerate = Frequency::Hertz(10);
+
+ void SetUp() override {
+ source_yuv_file_path_ = webrtc::test::TempFilename(
+ webrtc::test::OutputPath(), "video_codec_tester_impl_unittest");
+ FILE* file = fopen(source_yuv_file_path_.c_str(), "wb");
+ for (int i = 0; i < 3 * kSourceWidth * kSourceHeight / 2; ++i) {
+ fwrite("x", 1, 1, file);
+ }
+ fclose(file);
+ }
+
+ protected:
+ std::string source_yuv_file_path_;
+};
+
+TEST_P(VideoCodecTesterTestPacing, PaceEncode) {
+ auto [pacing_settings, expected_delta_ms] = GetParam();
+ VideoSourceSettings video_source{
+ .file_path = source_yuv_file_path_,
+ .resolution = {.width = kSourceWidth, .height = kSourceHeight},
+ .framerate = kTargetFramerate};
+
+ NiceMock<MockVideoEncoderFactory> encoder_factory;
+ ON_CALL(encoder_factory, CreateVideoEncoder(_))
+ .WillByDefault([](const SdpVideoFormat&) {
+ return std::make_unique<NiceMock<MockVideoEncoder>>();
+ });
+
+ std::map<uint32_t, EncodingSettings> encoding_settings =
+ VideoCodecTester::CreateEncodingSettings(
+ "VP8", "L1T1", kSourceWidth, kSourceHeight, {kTargetLayerBitrateKbps},
+ kTargetFramerate.hertz(), kNumFrames);
+
+ EncoderSettings encoder_settings;
+ encoder_settings.pacing_settings = pacing_settings;
+ std::vector<Frame> frames =
+ VideoCodecTester::RunEncodeTest(video_source, &encoder_factory,
+ encoder_settings, encoding_settings)
+ ->Slice(/*filter=*/{}, /*merge=*/false);
+ ASSERT_THAT(frames, SizeIs(kNumFrames));
+ EXPECT_NEAR((frames[1].encode_start - frames[0].encode_start).ms(),
+ expected_delta_ms, 10);
+ EXPECT_NEAR((frames[2].encode_start - frames[1].encode_start).ms(),
+ expected_delta_ms, 10);
+}
+
+TEST_P(VideoCodecTesterTestPacing, PaceDecode) {
+ auto [pacing_settings, expected_delta_ms] = GetParam();
+ MockCodedVideoSource video_source(kNumFrames, kTargetFramerate);
+
+ NiceMock<MockVideoDecoderFactory> decoder_factory;
+ ON_CALL(decoder_factory, CreateVideoDecoder(_))
+ .WillByDefault([](const SdpVideoFormat&) {
+ return std::make_unique<NiceMock<MockVideoDecoder>>();
+ });
+
+ DecoderSettings decoder_settings;
+ decoder_settings.pacing_settings = pacing_settings;
+ std::vector<Frame> frames =
+ VideoCodecTester::RunDecodeTest(&video_source, &decoder_factory,
+ decoder_settings, SdpVideoFormat("VP8"))
+ ->Slice(/*filter=*/{}, /*merge=*/false);
+ ASSERT_THAT(frames, SizeIs(kNumFrames));
+ EXPECT_NEAR((frames[1].decode_start - frames[0].decode_start).ms(),
+ expected_delta_ms, 10);
+ EXPECT_NEAR((frames[2].decode_start - frames[1].decode_start).ms(),
+ expected_delta_ms, 10);
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ DISABLED_All,
+ VideoCodecTesterTestPacing,
+ ::testing::Values(
+ // No pacing.
+ std::make_tuple(PacingSettings{.mode = PacingMode::kNoPacing},
+ /*expected_delta_ms=*/0),
+ // Real-time pacing.
+ std::make_tuple(PacingSettings{.mode = PacingMode::kRealTime},
+ /*expected_delta_ms=*/100),
+ // Pace with specified constant rate.
+ std::make_tuple(PacingSettings{.mode = PacingMode::kConstantRate,
+ .constant_rate = Frequency::Hertz(20)},
+ /*expected_delta_ms=*/50)));
+} // namespace test
+} // namespace webrtc
diff --git a/third_party/libwebrtc/third_party/abseil-cpp/absl/algorithm/algorithm_gn/moz.build b/third_party/libwebrtc/third_party/abseil-cpp/absl/algorithm/algorithm_gn/moz.build
index f0562eabf5..95f5f77730 100644
--- a/third_party/libwebrtc/third_party/abseil-cpp/absl/algorithm/algorithm_gn/moz.build
+++ b/third_party/libwebrtc/third_party/abseil-cpp/absl/algorithm/algorithm_gn/moz.build
@@ -126,16 +126,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/third_party/abseil-cpp/absl/algorithm/container_gn/moz.build b/third_party/libwebrtc/third_party/abseil-cpp/absl/algorithm/container_gn/moz.build
index 74a9d1986e..3289a5ec77 100644
--- a/third_party/libwebrtc/third_party/abseil-cpp/absl/algorithm/container_gn/moz.build
+++ b/third_party/libwebrtc/third_party/abseil-cpp/absl/algorithm/container_gn/moz.build
@@ -126,16 +126,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/third_party/abseil-cpp/absl/base/atomic_hook_gn/moz.build b/third_party/libwebrtc/third_party/abseil-cpp/absl/base/atomic_hook_gn/moz.build
index e3b931a7b2..3995b32103 100644
--- a/third_party/libwebrtc/third_party/abseil-cpp/absl/base/atomic_hook_gn/moz.build
+++ b/third_party/libwebrtc/third_party/abseil-cpp/absl/base/atomic_hook_gn/moz.build
@@ -126,16 +126,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/third_party/abseil-cpp/absl/base/base_internal_gn/moz.build b/third_party/libwebrtc/third_party/abseil-cpp/absl/base/base_internal_gn/moz.build
index 783c3186bb..a7dd82d8f3 100644
--- a/third_party/libwebrtc/third_party/abseil-cpp/absl/base/base_internal_gn/moz.build
+++ b/third_party/libwebrtc/third_party/abseil-cpp/absl/base/base_internal_gn/moz.build
@@ -126,16 +126,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/third_party/abseil-cpp/absl/base/config_gn/moz.build b/third_party/libwebrtc/third_party/abseil-cpp/absl/base/config_gn/moz.build
index ba78602e5e..0018526d35 100644
--- a/third_party/libwebrtc/third_party/abseil-cpp/absl/base/config_gn/moz.build
+++ b/third_party/libwebrtc/third_party/abseil-cpp/absl/base/config_gn/moz.build
@@ -126,16 +126,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/third_party/abseil-cpp/absl/base/core_headers_gn/moz.build b/third_party/libwebrtc/third_party/abseil-cpp/absl/base/core_headers_gn/moz.build
index e19ecba14a..781c7ab8f0 100644
--- a/third_party/libwebrtc/third_party/abseil-cpp/absl/base/core_headers_gn/moz.build
+++ b/third_party/libwebrtc/third_party/abseil-cpp/absl/base/core_headers_gn/moz.build
@@ -126,16 +126,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/third_party/abseil-cpp/absl/base/log_severity_gn/moz.build b/third_party/libwebrtc/third_party/abseil-cpp/absl/base/log_severity_gn/moz.build
index a249393628..04d6ac3880 100644
--- a/third_party/libwebrtc/third_party/abseil-cpp/absl/base/log_severity_gn/moz.build
+++ b/third_party/libwebrtc/third_party/abseil-cpp/absl/base/log_severity_gn/moz.build
@@ -136,7 +136,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -146,10 +145,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/third_party/abseil-cpp/absl/base/nullability_gn/moz.build b/third_party/libwebrtc/third_party/abseil-cpp/absl/base/nullability_gn/moz.build
index 04d1e20cac..d7a0c48a78 100644
--- a/third_party/libwebrtc/third_party/abseil-cpp/absl/base/nullability_gn/moz.build
+++ b/third_party/libwebrtc/third_party/abseil-cpp/absl/base/nullability_gn/moz.build
@@ -126,16 +126,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/third_party/abseil-cpp/absl/base/raw_logging_internal_gn/moz.build b/third_party/libwebrtc/third_party/abseil-cpp/absl/base/raw_logging_internal_gn/moz.build
index 8402338b98..b4d0002829 100644
--- a/third_party/libwebrtc/third_party/abseil-cpp/absl/base/raw_logging_internal_gn/moz.build
+++ b/third_party/libwebrtc/third_party/abseil-cpp/absl/base/raw_logging_internal_gn/moz.build
@@ -136,7 +136,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -146,10 +145,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/third_party/abseil-cpp/absl/base/throw_delegate_gn/moz.build b/third_party/libwebrtc/third_party/abseil-cpp/absl/base/throw_delegate_gn/moz.build
index 78f0a49f70..26e7ca1402 100644
--- a/third_party/libwebrtc/third_party/abseil-cpp/absl/base/throw_delegate_gn/moz.build
+++ b/third_party/libwebrtc/third_party/abseil-cpp/absl/base/throw_delegate_gn/moz.build
@@ -136,7 +136,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -146,10 +145,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/third_party/abseil-cpp/absl/cleanup/cleanup_gn/moz.build b/third_party/libwebrtc/third_party/abseil-cpp/absl/cleanup/cleanup_gn/moz.build
index 590a21038a..2b964a6593 100644
--- a/third_party/libwebrtc/third_party/abseil-cpp/absl/cleanup/cleanup_gn/moz.build
+++ b/third_party/libwebrtc/third_party/abseil-cpp/absl/cleanup/cleanup_gn/moz.build
@@ -126,16 +126,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/third_party/abseil-cpp/absl/cleanup/cleanup_internal_gn/moz.build b/third_party/libwebrtc/third_party/abseil-cpp/absl/cleanup/cleanup_internal_gn/moz.build
index 269410ddab..cc42f6b1cf 100644
--- a/third_party/libwebrtc/third_party/abseil-cpp/absl/cleanup/cleanup_internal_gn/moz.build
+++ b/third_party/libwebrtc/third_party/abseil-cpp/absl/cleanup/cleanup_internal_gn/moz.build
@@ -126,16 +126,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/third_party/abseil-cpp/absl/container/compressed_tuple_gn/moz.build b/third_party/libwebrtc/third_party/abseil-cpp/absl/container/compressed_tuple_gn/moz.build
index ee9640db8b..be74f2073f 100644
--- a/third_party/libwebrtc/third_party/abseil-cpp/absl/container/compressed_tuple_gn/moz.build
+++ b/third_party/libwebrtc/third_party/abseil-cpp/absl/container/compressed_tuple_gn/moz.build
@@ -126,16 +126,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/third_party/abseil-cpp/absl/container/inlined_vector_gn/moz.build b/third_party/libwebrtc/third_party/abseil-cpp/absl/container/inlined_vector_gn/moz.build
index bdf3ab1374..6007158942 100644
--- a/third_party/libwebrtc/third_party/abseil-cpp/absl/container/inlined_vector_gn/moz.build
+++ b/third_party/libwebrtc/third_party/abseil-cpp/absl/container/inlined_vector_gn/moz.build
@@ -126,16 +126,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/third_party/abseil-cpp/absl/container/inlined_vector_internal_gn/moz.build b/third_party/libwebrtc/third_party/abseil-cpp/absl/container/inlined_vector_internal_gn/moz.build
index a77c5b9bbf..67fafc74cc 100644
--- a/third_party/libwebrtc/third_party/abseil-cpp/absl/container/inlined_vector_internal_gn/moz.build
+++ b/third_party/libwebrtc/third_party/abseil-cpp/absl/container/inlined_vector_internal_gn/moz.build
@@ -126,16 +126,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/third_party/abseil-cpp/absl/functional/any_invocable_gn/moz.build b/third_party/libwebrtc/third_party/abseil-cpp/absl/functional/any_invocable_gn/moz.build
index 3494487234..a7dc8399fa 100644
--- a/third_party/libwebrtc/third_party/abseil-cpp/absl/functional/any_invocable_gn/moz.build
+++ b/third_party/libwebrtc/third_party/abseil-cpp/absl/functional/any_invocable_gn/moz.build
@@ -126,16 +126,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/third_party/abseil-cpp/absl/functional/bind_front_gn/moz.build b/third_party/libwebrtc/third_party/abseil-cpp/absl/functional/bind_front_gn/moz.build
index 564670abf0..7d7ddda3ab 100644
--- a/third_party/libwebrtc/third_party/abseil-cpp/absl/functional/bind_front_gn/moz.build
+++ b/third_party/libwebrtc/third_party/abseil-cpp/absl/functional/bind_front_gn/moz.build
@@ -126,16 +126,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/third_party/abseil-cpp/absl/memory/memory_gn/moz.build b/third_party/libwebrtc/third_party/abseil-cpp/absl/memory/memory_gn/moz.build
index bd6abd1983..011681082b 100644
--- a/third_party/libwebrtc/third_party/abseil-cpp/absl/memory/memory_gn/moz.build
+++ b/third_party/libwebrtc/third_party/abseil-cpp/absl/memory/memory_gn/moz.build
@@ -126,16 +126,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/third_party/abseil-cpp/absl/meta/type_traits_gn/moz.build b/third_party/libwebrtc/third_party/abseil-cpp/absl/meta/type_traits_gn/moz.build
index 5d0b080cb8..be165cdd32 100644
--- a/third_party/libwebrtc/third_party/abseil-cpp/absl/meta/type_traits_gn/moz.build
+++ b/third_party/libwebrtc/third_party/abseil-cpp/absl/meta/type_traits_gn/moz.build
@@ -126,16 +126,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/third_party/abseil-cpp/absl/numeric/bits_gn/moz.build b/third_party/libwebrtc/third_party/abseil-cpp/absl/numeric/bits_gn/moz.build
index 1923a18b1f..f08ba233b0 100644
--- a/third_party/libwebrtc/third_party/abseil-cpp/absl/numeric/bits_gn/moz.build
+++ b/third_party/libwebrtc/third_party/abseil-cpp/absl/numeric/bits_gn/moz.build
@@ -126,16 +126,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/third_party/abseil-cpp/absl/numeric/int128_gn/moz.build b/third_party/libwebrtc/third_party/abseil-cpp/absl/numeric/int128_gn/moz.build
index ec45e9281f..48c148b039 100644
--- a/third_party/libwebrtc/third_party/abseil-cpp/absl/numeric/int128_gn/moz.build
+++ b/third_party/libwebrtc/third_party/abseil-cpp/absl/numeric/int128_gn/moz.build
@@ -136,7 +136,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -146,10 +145,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/third_party/abseil-cpp/absl/strings/string_view_gn/moz.build b/third_party/libwebrtc/third_party/abseil-cpp/absl/strings/string_view_gn/moz.build
index 16fa4aa493..2dd442a89e 100644
--- a/third_party/libwebrtc/third_party/abseil-cpp/absl/strings/string_view_gn/moz.build
+++ b/third_party/libwebrtc/third_party/abseil-cpp/absl/strings/string_view_gn/moz.build
@@ -136,7 +136,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -146,10 +145,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/third_party/abseil-cpp/absl/strings/strings_gn/moz.build b/third_party/libwebrtc/third_party/abseil-cpp/absl/strings/strings_gn/moz.build
index f748c5d9c1..84461a1a28 100644
--- a/third_party/libwebrtc/third_party/abseil-cpp/absl/strings/strings_gn/moz.build
+++ b/third_party/libwebrtc/third_party/abseil-cpp/absl/strings/strings_gn/moz.build
@@ -141,7 +141,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -151,10 +150,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/third_party/abseil-cpp/absl/types/bad_optional_access_gn/moz.build b/third_party/libwebrtc/third_party/abseil-cpp/absl/types/bad_optional_access_gn/moz.build
index a2eeaa3f05..71a47188d4 100644
--- a/third_party/libwebrtc/third_party/abseil-cpp/absl/types/bad_optional_access_gn/moz.build
+++ b/third_party/libwebrtc/third_party/abseil-cpp/absl/types/bad_optional_access_gn/moz.build
@@ -136,7 +136,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -146,10 +145,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/third_party/abseil-cpp/absl/types/bad_variant_access_gn/moz.build b/third_party/libwebrtc/third_party/abseil-cpp/absl/types/bad_variant_access_gn/moz.build
index e20623795d..d46074339f 100644
--- a/third_party/libwebrtc/third_party/abseil-cpp/absl/types/bad_variant_access_gn/moz.build
+++ b/third_party/libwebrtc/third_party/abseil-cpp/absl/types/bad_variant_access_gn/moz.build
@@ -136,7 +136,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -146,10 +145,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/third_party/abseil-cpp/absl/types/optional_gn/moz.build b/third_party/libwebrtc/third_party/abseil-cpp/absl/types/optional_gn/moz.build
index 252d8a8376..7d3070b200 100644
--- a/third_party/libwebrtc/third_party/abseil-cpp/absl/types/optional_gn/moz.build
+++ b/third_party/libwebrtc/third_party/abseil-cpp/absl/types/optional_gn/moz.build
@@ -126,16 +126,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/third_party/abseil-cpp/absl/types/span_gn/moz.build b/third_party/libwebrtc/third_party/abseil-cpp/absl/types/span_gn/moz.build
index 91f8106b27..37133145c2 100644
--- a/third_party/libwebrtc/third_party/abseil-cpp/absl/types/span_gn/moz.build
+++ b/third_party/libwebrtc/third_party/abseil-cpp/absl/types/span_gn/moz.build
@@ -126,16 +126,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/third_party/abseil-cpp/absl/types/variant_gn/moz.build b/third_party/libwebrtc/third_party/abseil-cpp/absl/types/variant_gn/moz.build
index 1f2c40273d..fd47ce4b36 100644
--- a/third_party/libwebrtc/third_party/abseil-cpp/absl/types/variant_gn/moz.build
+++ b/third_party/libwebrtc/third_party/abseil-cpp/absl/types/variant_gn/moz.build
@@ -126,16 +126,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/third_party/abseil-cpp/absl/utility/utility_gn/moz.build b/third_party/libwebrtc/third_party/abseil-cpp/absl/utility/utility_gn/moz.build
index 172b27fecb..8a8c4fd340 100644
--- a/third_party/libwebrtc/third_party/abseil-cpp/absl/utility/utility_gn/moz.build
+++ b/third_party/libwebrtc/third_party/abseil-cpp/absl/utility/utility_gn/moz.build
@@ -126,16 +126,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/third_party/libyuv/libyuv_gn/moz.build b/third_party/libwebrtc/third_party/libyuv/libyuv_gn/moz.build
index 5c9e66f408..ed639610ed 100644
--- a/third_party/libwebrtc/third_party/libyuv/libyuv_gn/moz.build
+++ b/third_party/libwebrtc/third_party/libyuv/libyuv_gn/moz.build
@@ -178,16 +178,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/third_party/pffft/pffft_gn/moz.build b/third_party/libwebrtc/third_party/pffft/pffft_gn/moz.build
index ee4fd18a3e..f1aad5d0b4 100644
--- a/third_party/libwebrtc/third_party/pffft/pffft_gn/moz.build
+++ b/third_party/libwebrtc/third_party/pffft/pffft_gn/moz.build
@@ -150,7 +150,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -160,10 +159,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/third_party/rnnoise/rnn_vad_gn/moz.build b/third_party/libwebrtc/third_party/rnnoise/rnn_vad_gn/moz.build
index afb7f87ccf..7122118c08 100644
--- a/third_party/libwebrtc/third_party/rnnoise/rnn_vad_gn/moz.build
+++ b/third_party/libwebrtc/third_party/rnnoise/rnn_vad_gn/moz.build
@@ -139,7 +139,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -149,10 +148,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/tools/clang/OWNERS b/third_party/libwebrtc/tools/clang/OWNERS
deleted file mode 100644
index d86ef9424a..0000000000
--- a/third_party/libwebrtc/tools/clang/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-hans@chromium.org
-thakis@chromium.org
diff --git a/third_party/libwebrtc/tools/clang/plugins/ChromeClassTester.cpp b/third_party/libwebrtc/tools/clang/plugins/ChromeClassTester.cpp
deleted file mode 100644
index 055866c5c5..0000000000
--- a/third_party/libwebrtc/tools/clang/plugins/ChromeClassTester.cpp
+++ /dev/null
@@ -1,294 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// A general interface for filtering and only acting on classes in Chromium C++
-// code.
-
-#include "ChromeClassTester.h"
-
-#include <sys/param.h>
-
-#include "clang/AST/AST.h"
-#include "clang/Basic/FileManager.h"
-#include "clang/Basic/SourceManager.h"
-
-using namespace clang;
-
-namespace {
-
-bool starts_with(const std::string& one, const std::string& two) {
- return one.compare(0, two.size(), two) == 0;
-}
-
-std::string lstrip(const std::string& one, const std::string& two) {
- if (starts_with(one, two))
- return one.substr(two.size());
- return one;
-}
-
-bool ends_with(const std::string& one, const std::string& two) {
- if (two.size() > one.size())
- return false;
-
- return one.compare(one.size() - two.size(), two.size(), two) == 0;
-}
-
-} // namespace
-
-ChromeClassTester::ChromeClassTester(CompilerInstance& instance)
- : instance_(instance),
- diagnostic_(instance.getDiagnostics()) {
- BuildBannedLists();
-}
-
-ChromeClassTester::~ChromeClassTester() {}
-
-void ChromeClassTester::HandleTagDeclDefinition(TagDecl* tag) {
- pending_class_decls_.push_back(tag);
-}
-
-bool ChromeClassTester::HandleTopLevelDecl(DeclGroupRef group_ref) {
- for (size_t i = 0; i < pending_class_decls_.size(); ++i)
- CheckTag(pending_class_decls_[i]);
- pending_class_decls_.clear();
-
- return true; // true means continue parsing.
-}
-
-void ChromeClassTester::CheckTag(TagDecl* tag) {
- // We handle class types here where we have semantic information. We can only
- // check structs/classes/enums here, but we get a bunch of nice semantic
- // information instead of just parsing information.
-
- if (CXXRecordDecl* record = dyn_cast<CXXRecordDecl>(tag)) {
- // If this is a POD or a class template or a type dependent on a
- // templated class, assume there's no ctor/dtor/virtual method
- // optimization that we can do.
- if (record->isPOD() ||
- record->getDescribedClassTemplate() ||
- record->getTemplateSpecializationKind() ||
- record->isDependentType())
- return;
-
- if (InBannedNamespace(record))
- return;
-
- SourceLocation record_location = record->getInnerLocStart();
- if (InBannedDirectory(record_location))
- return;
-
- // We sadly need to maintain a blacklist of types that violate these
- // rules, but do so for good reason or due to limitations of this
- // checker (i.e., we don't handle extern templates very well).
- std::string base_name = record->getNameAsString();
- if (IsIgnoredType(base_name))
- return;
-
- // We ignore all classes that end with "Matcher" because they're probably
- // GMock artifacts.
- if (ends_with(base_name, "Matcher"))
- return;
-
- CheckChromeClass(record_location, record);
- }
-}
-
-void ChromeClassTester::emitWarning(SourceLocation loc,
- const char* raw_error) {
- FullSourceLoc full(loc, instance().getSourceManager());
- std::string err;
- err = "[chromium-style] ";
- err += raw_error;
- DiagnosticsEngine::Level level =
- diagnostic().getWarningsAsErrors() ?
- DiagnosticsEngine::Error :
- DiagnosticsEngine::Warning;
- unsigned id = diagnostic().getCustomDiagID(level, err);
- DiagnosticBuilder builder = diagnostic().Report(full, id);
-}
-
-bool ChromeClassTester::InBannedNamespace(const Decl* record) {
- std::string n = GetNamespace(record);
- if (!n.empty()) {
- return std::find(banned_namespaces_.begin(), banned_namespaces_.end(), n)
- != banned_namespaces_.end();
- }
-
- return false;
-}
-
-std::string ChromeClassTester::GetNamespace(const Decl* record) {
- return GetNamespaceImpl(record->getDeclContext(), "");
-}
-
-bool ChromeClassTester::InImplementationFile(SourceLocation record_location) {
- std::string filename;
- if (!GetFilename(record_location, &filename))
- return false;
-
- if (ends_with(filename, ".cc") || ends_with(filename, ".cpp") ||
- ends_with(filename, ".mm")) {
- return true;
- }
-
- return false;
-}
-
-void ChromeClassTester::BuildBannedLists() {
- banned_namespaces_.push_back("std");
- banned_namespaces_.push_back("__gnu_cxx");
- banned_namespaces_.push_back("WebKit");
-
- banned_directories_.push_back("third_party/");
- banned_directories_.push_back("native_client/");
- banned_directories_.push_back("breakpad/");
- banned_directories_.push_back("courgette/");
- banned_directories_.push_back("pdf/");
- banned_directories_.push_back("ppapi/");
- banned_directories_.push_back("usr/");
- banned_directories_.push_back("testing/");
- banned_directories_.push_back("googleurl/");
- banned_directories_.push_back("v8/");
- banned_directories_.push_back("dart/");
- banned_directories_.push_back("sdch/");
- banned_directories_.push_back("icu4c/");
- banned_directories_.push_back("frameworks/");
-
- // Don't check autogenerated headers.
- // Make puts them below $(builddir_name)/.../gen and geni.
- // Ninja puts them below OUTPUT_DIR/.../gen
- // Xcode has a fixed output directory for everything.
- banned_directories_.push_back("gen/");
- banned_directories_.push_back("geni/");
- banned_directories_.push_back("xcodebuild/");
-
- // You are standing in a mazy of twisty dependencies, all resolved by
- // putting everything in the header.
- banned_directories_.push_back("automation/");
-
- // Don't check system headers.
- banned_directories_.push_back("/Developer/");
-
- // Used in really low level threading code that probably shouldn't be out of
- // lined.
- ignored_record_names_.insert("ThreadLocalBoolean");
-
- // A complicated pickle derived struct that is all packed integers.
- ignored_record_names_.insert("Header");
-
- // Part of the GPU system that uses multiple included header
- // weirdness. Never getting this right.
- ignored_record_names_.insert("Validators");
-
- // Has a UNIT_TEST only constructor. Isn't *terribly* complex...
- ignored_record_names_.insert("AutocompleteController");
- ignored_record_names_.insert("HistoryURLProvider");
-
- // Because of chrome frame
- ignored_record_names_.insert("ReliabilityTestSuite");
-
- // Used over in the net unittests. A large enough bundle of integers with 1
- // non-pod class member. Probably harmless.
- ignored_record_names_.insert("MockTransaction");
-
- // Used heavily in ui_unittests and once in views_unittests. Fixing this
- // isn't worth the overhead of an additional library.
- ignored_record_names_.insert("TestAnimationDelegate");
-
- // Part of our public interface that nacl and friends use. (Arguably, this
- // should mean that this is a higher priority but fixing this looks hard.)
- ignored_record_names_.insert("PluginVersionInfo");
-}
-
-std::string ChromeClassTester::GetNamespaceImpl(const DeclContext* context,
- const std::string& candidate) {
- switch (context->getDeclKind()) {
- case Decl::TranslationUnit: {
- return candidate;
- }
- case Decl::Namespace: {
- const NamespaceDecl* decl = dyn_cast<NamespaceDecl>(context);
- std::string name_str;
- llvm::raw_string_ostream OS(name_str);
- if (decl->isAnonymousNamespace())
- OS << "<anonymous namespace>";
- else
- OS << *decl;
- return GetNamespaceImpl(context->getParent(),
- OS.str());
- }
- default: {
- return GetNamespaceImpl(context->getParent(), candidate);
- }
- }
-}
-
-bool ChromeClassTester::InBannedDirectory(SourceLocation loc) {
- std::string filename;
- if (!GetFilename(loc, &filename)) {
- // If the filename cannot be determined, simply treat this as a banned
- // location, instead of going through the full lookup process.
- return true;
- }
-
- // We need to special case scratch space; which is where clang does its
- // macro expansion. We explicitly want to allow people to do otherwise bad
- // things through macros that were defined due to third party libraries.
- if (filename == "<scratch space>")
- return true;
-
- // Don't complain about autogenerated protobuf files.
- if (ends_with(filename, ".pb.h")) {
- return true;
- }
-
- // We need to munge the paths so that they are relative to the repository
- // srcroot. We first resolve the symlinktastic relative path and then
- // remove our known srcroot from it if needed.
- char resolvedPath[MAXPATHLEN];
- if (realpath(filename.c_str(), resolvedPath)) {
- filename = resolvedPath;
- }
-
- // On linux, chrome is often checked out to /usr/local/google. Due to the
- // "usr" rule in banned_directories_, all diagnostics would be suppressed
- // in that case. As a workaround, strip that prefix.
- filename = lstrip(filename, "/usr/local/google");
-
- for (std::vector<std::string>::const_iterator it =
- banned_directories_.begin();
- it != banned_directories_.end(); ++it) {
- // If we can find any of the banned path components in this path, then
- // this file is rejected.
- size_t index = filename.find(*it);
- if (index != std::string::npos) {
- bool matches_full_dir_name = index == 0 || filename[index - 1] == '/';
- if ((*it)[0] == '/')
- matches_full_dir_name = true;
- if (matches_full_dir_name)
- return true;
- }
- }
-
- return false;
-}
-
-bool ChromeClassTester::IsIgnoredType(const std::string& base_name) {
- return ignored_record_names_.find(base_name) != ignored_record_names_.end();
-}
-
-bool ChromeClassTester::GetFilename(SourceLocation loc,
- std::string* filename) {
- const SourceManager& source_manager = instance_.getSourceManager();
- SourceLocation spelling_location = source_manager.getSpellingLoc(loc);
- PresumedLoc ploc = source_manager.getPresumedLoc(spelling_location);
- if (ploc.isInvalid()) {
- // If we're in an invalid location, we're looking at things that aren't
- // actually stated in the source.
- return false;
- }
-
- *filename = ploc.getFilename();
- return true;
-}
diff --git a/third_party/libwebrtc/tools/clang/plugins/ChromeClassTester.h b/third_party/libwebrtc/tools/clang/plugins/ChromeClassTester.h
deleted file mode 100644
index 588ae9cae5..0000000000
--- a/third_party/libwebrtc/tools/clang/plugins/ChromeClassTester.h
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef TOOLS_CLANG_PLUGINS_CHROMECLASSTESTER_H_
-#define TOOLS_CLANG_PLUGINS_CHROMECLASSTESTER_H_
-
-#include <set>
-#include <vector>
-
-#include "clang/AST/ASTConsumer.h"
-#include "clang/AST/TypeLoc.h"
-#include "clang/Frontend/CompilerInstance.h"
-
-// A class on top of ASTConsumer that forwards classes defined in Chromium
-// headers to subclasses which implement CheckChromeClass().
-class ChromeClassTester : public clang::ASTConsumer {
- public:
- explicit ChromeClassTester(clang::CompilerInstance& instance);
- virtual ~ChromeClassTester();
-
- // clang::ASTConsumer:
- virtual void HandleTagDeclDefinition(clang::TagDecl* tag);
- virtual bool HandleTopLevelDecl(clang::DeclGroupRef group_ref);
-
- protected:
- clang::CompilerInstance& instance() { return instance_; }
- clang::DiagnosticsEngine& diagnostic() { return diagnostic_; }
-
- // Emits a simple warning; this shouldn't be used if you require printf-style
- // printing.
- void emitWarning(clang::SourceLocation loc, const char* error);
-
- // Utility method for subclasses to check if this class is in a banned
- // namespace.
- bool InBannedNamespace(const clang::Decl* record);
-
- // Utility method for subclasses to determine the namespace of the
- // specified record, if any. Unnamed namespaces will be identified as
- // "<anonymous namespace>".
- std::string GetNamespace(const clang::Decl* record);
-
- // Utility method for subclasses to check if this class is within an
- // implementation (.cc, .cpp, .mm) file.
- bool InImplementationFile(clang::SourceLocation location);
-
- private:
- void BuildBannedLists();
-
- void CheckTag(clang::TagDecl*);
-
- // Filtered versions of tags that are only called with things defined in
- // chrome header files.
- virtual void CheckChromeClass(clang::SourceLocation record_location,
- clang::CXXRecordDecl* record) = 0;
-
- // Utility methods used for filtering out non-chrome classes (and ones we
- // deliberately ignore) in HandleTagDeclDefinition().
- std::string GetNamespaceImpl(const clang::DeclContext* context,
- const std::string& candidate);
- bool InBannedDirectory(clang::SourceLocation loc);
- bool IsIgnoredType(const std::string& base_name);
-
- // Attempts to determine the filename for the given SourceLocation.
- // Returns false if the filename could not be determined.
- bool GetFilename(clang::SourceLocation loc, std::string* filename);
-
- clang::CompilerInstance& instance_;
- clang::DiagnosticsEngine& diagnostic_;
-
- // List of banned namespaces.
- std::vector<std::string> banned_namespaces_;
-
- // List of banned directories.
- std::vector<std::string> banned_directories_;
-
- // List of types that we don't check.
- std::set<std::string> ignored_record_names_;
-
- // List of decls to check once the current top-level decl is parsed.
- std::vector<clang::TagDecl*> pending_class_decls_;
-};
-
-#endif // TOOLS_CLANG_PLUGINS_CHROMECLASSTESTER_H_
diff --git a/third_party/libwebrtc/tools/clang/plugins/FindBadConstructs.cpp b/third_party/libwebrtc/tools/clang/plugins/FindBadConstructs.cpp
deleted file mode 100644
index b79a64dbd1..0000000000
--- a/third_party/libwebrtc/tools/clang/plugins/FindBadConstructs.cpp
+++ /dev/null
@@ -1,435 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// This file defines a bunch of recurring problems in the Chromium C++ code.
-//
-// Checks that are implemented:
-// - Constructors/Destructors should not be inlined if they are of a complex
-// class type.
-// - Missing "virtual" keywords on methods that should be virtual.
-// - Non-annotated overriding virtual methods.
-// - Virtual methods with nonempty implementations in their headers.
-// - Classes that derive from base::RefCounted / base::RefCountedThreadSafe
-// should have protected or private destructors.
-
-#include "clang/Frontend/FrontendPluginRegistry.h"
-#include "clang/AST/ASTConsumer.h"
-#include "clang/AST/AST.h"
-#include "clang/AST/CXXInheritance.h"
-#include "clang/AST/TypeLoc.h"
-#include "clang/Basic/SourceManager.h"
-#include "clang/Frontend/CompilerInstance.h"
-#include "llvm/Support/raw_ostream.h"
-
-#include "ChromeClassTester.h"
-
-using namespace clang;
-
-namespace {
-
-bool TypeHasNonTrivialDtor(const Type* type) {
- if (const CXXRecordDecl* cxx_r = type->getCXXRecordDeclForPointerType())
- return cxx_r->hasTrivialDestructor();
-
- return false;
-}
-
-// Returns the underlying Type for |type| by expanding typedefs and removing
-// any namespace qualifiers.
-const Type* UnwrapType(const Type* type) {
- if (const ElaboratedType* elaborated = dyn_cast<ElaboratedType>(type))
- return UnwrapType(elaborated->getNamedType().getTypePtr());
- if (const TypedefType* typedefed = dyn_cast<TypedefType>(type))
- return UnwrapType(typedefed->desugar().getTypePtr());
- return type;
-}
-
-// Searches for constructs that we know we don't want in the Chromium code base.
-class FindBadConstructsConsumer : public ChromeClassTester {
- public:
- FindBadConstructsConsumer(CompilerInstance& instance,
- bool check_refcounted_dtors,
- bool check_virtuals_in_implementations)
- : ChromeClassTester(instance),
- check_refcounted_dtors_(check_refcounted_dtors),
- check_virtuals_in_implementations_(check_virtuals_in_implementations) {
- }
-
- virtual void CheckChromeClass(SourceLocation record_location,
- CXXRecordDecl* record) {
- bool implementation_file = InImplementationFile(record_location);
-
- if (!implementation_file) {
- // Only check for "heavy" constructors/destructors in header files;
- // within implementation files, there is no performance cost.
- CheckCtorDtorWeight(record_location, record);
- }
-
- if (!implementation_file || check_virtuals_in_implementations_) {
- bool warn_on_inline_bodies = !implementation_file;
-
- // Check that all virtual methods are marked accordingly with both
- // virtual and OVERRIDE.
- CheckVirtualMethods(record_location, record, warn_on_inline_bodies);
- }
-
- if (check_refcounted_dtors_)
- CheckRefCountedDtors(record_location, record);
- }
-
- private:
- bool check_refcounted_dtors_;
- bool check_virtuals_in_implementations_;
-
- // Returns true if |base| specifies one of the Chromium reference counted
- // classes (base::RefCounted / base::RefCountedThreadSafe). |user_data| is
- // ignored.
- static bool IsRefCountedCallback(const CXXBaseSpecifier* base,
- CXXBasePath& path,
- void* user_data) {
- FindBadConstructsConsumer* self =
- static_cast<FindBadConstructsConsumer*>(user_data);
-
- const TemplateSpecializationType* base_type =
- dyn_cast<TemplateSpecializationType>(
- UnwrapType(base->getType().getTypePtr()));
- if (!base_type) {
- // Base-most definition is not a template, so this cannot derive from
- // base::RefCounted. However, it may still be possible to use with a
- // scoped_refptr<> and support ref-counting, so this is not a perfect
- // guarantee of safety.
- return false;
- }
-
- TemplateName name = base_type->getTemplateName();
- if (TemplateDecl* decl = name.getAsTemplateDecl()) {
- std::string base_name = decl->getNameAsString();
-
- // Check for both base::RefCounted and base::RefCountedThreadSafe.
- if (base_name.compare(0, 10, "RefCounted") == 0 &&
- self->GetNamespace(decl) == "base") {
- return true;
- }
- }
- return false;
- }
-
- // Prints errors if the destructor of a RefCounted class is public.
- void CheckRefCountedDtors(SourceLocation record_location,
- CXXRecordDecl* record) {
- // Skip anonymous structs.
- if (record->getIdentifier() == NULL)
- return;
-
- CXXBasePaths paths;
- if (!record->lookupInBases(
- &FindBadConstructsConsumer::IsRefCountedCallback, this, paths)) {
- return; // Class does not derive from a ref-counted base class.
- }
-
- if (!record->hasUserDeclaredDestructor()) {
- emitWarning(
- record_location,
- "Classes that are ref-counted should have explicit "
- "destructors that are protected or private.");
- } else if (CXXDestructorDecl* dtor = record->getDestructor()) {
- if (dtor->getAccess() == AS_public) {
- emitWarning(
- dtor->getInnerLocStart(),
- "Classes that are ref-counted should not have "
- "public destructors.");
- }
- }
- }
-
- // Prints errors if the constructor/destructor weight is too heavy.
- void CheckCtorDtorWeight(SourceLocation record_location,
- CXXRecordDecl* record) {
- // We don't handle anonymous structs. If this record doesn't have a
- // name, it's of the form:
- //
- // struct {
- // ...
- // } name_;
- if (record->getIdentifier() == NULL)
- return;
-
- // Count the number of templated base classes as a feature of whether the
- // destructor can be inlined.
- int templated_base_classes = 0;
- for (CXXRecordDecl::base_class_const_iterator it = record->bases_begin();
- it != record->bases_end(); ++it) {
- if (it->getTypeSourceInfo()->getTypeLoc().getTypeLocClass() ==
- TypeLoc::TemplateSpecialization) {
- ++templated_base_classes;
- }
- }
-
- // Count the number of trivial and non-trivial member variables.
- int trivial_member = 0;
- int non_trivial_member = 0;
- int templated_non_trivial_member = 0;
- for (RecordDecl::field_iterator it = record->field_begin();
- it != record->field_end(); ++it) {
- CountType(it->getType().getTypePtr(),
- &trivial_member,
- &non_trivial_member,
- &templated_non_trivial_member);
- }
-
- // Check to see if we need to ban inlined/synthesized constructors. Note
- // that the cutoffs here are kind of arbitrary. Scores over 10 break.
- int dtor_score = 0;
- // Deriving from a templated base class shouldn't be enough to trigger
- // the ctor warning, but if you do *anything* else, it should.
- //
- // TODO(erg): This is motivated by templated base classes that don't have
- // any data members. Somehow detect when templated base classes have data
- // members and treat them differently.
- dtor_score += templated_base_classes * 9;
- // Instantiating a template is an insta-hit.
- dtor_score += templated_non_trivial_member * 10;
- // The fourth normal class member should trigger the warning.
- dtor_score += non_trivial_member * 3;
-
- int ctor_score = dtor_score;
- // You should be able to have 9 ints before we warn you.
- ctor_score += trivial_member;
-
- if (ctor_score >= 10) {
- if (!record->hasUserDeclaredConstructor()) {
- emitWarning(record_location,
- "Complex class/struct needs an explicit out-of-line "
- "constructor.");
- } else {
- // Iterate across all the constructors in this file and yell if we
- // find one that tries to be inline.
- for (CXXRecordDecl::ctor_iterator it = record->ctor_begin();
- it != record->ctor_end(); ++it) {
- if (it->hasInlineBody()) {
- if (it->isCopyConstructor() &&
- !record->hasUserDeclaredCopyConstructor()) {
- emitWarning(record_location,
- "Complex class/struct needs an explicit out-of-line "
- "copy constructor.");
- } else {
- emitWarning(it->getInnerLocStart(),
- "Complex constructor has an inlined body.");
- }
- }
- }
- }
- }
-
- // The destructor side is equivalent except that we don't check for
- // trivial members; 20 ints don't need a destructor.
- if (dtor_score >= 10 && !record->hasTrivialDestructor()) {
- if (!record->hasUserDeclaredDestructor()) {
- emitWarning(
- record_location,
- "Complex class/struct needs an explicit out-of-line "
- "destructor.");
- } else if (CXXDestructorDecl* dtor = record->getDestructor()) {
- if (dtor->hasInlineBody()) {
- emitWarning(dtor->getInnerLocStart(),
- "Complex destructor has an inline body.");
- }
- }
- }
- }
-
- void CheckVirtualMethod(const CXXMethodDecl* method,
- bool warn_on_inline_bodies) {
- if (!method->isVirtual())
- return;
-
- if (!method->isVirtualAsWritten()) {
- SourceLocation loc = method->getTypeSpecStartLoc();
- if (isa<CXXDestructorDecl>(method))
- loc = method->getInnerLocStart();
- emitWarning(loc, "Overriding method must have \"virtual\" keyword.");
- }
-
- // Virtual methods should not have inline definitions beyond "{}". This
- // only matters for header files.
- if (warn_on_inline_bodies && method->hasBody() &&
- method->hasInlineBody()) {
- if (CompoundStmt* cs = dyn_cast<CompoundStmt>(method->getBody())) {
- if (cs->size()) {
- emitWarning(
- cs->getLBracLoc(),
- "virtual methods with non-empty bodies shouldn't be "
- "declared inline.");
- }
- }
- }
- }
-
- bool InTestingNamespace(const Decl* record) {
- return GetNamespace(record).find("testing") != std::string::npos;
- }
-
- bool IsMethodInBannedNamespace(const CXXMethodDecl* method) {
- if (InBannedNamespace(method))
- return true;
- for (CXXMethodDecl::method_iterator i = method->begin_overridden_methods();
- i != method->end_overridden_methods();
- ++i) {
- const CXXMethodDecl* overridden = *i;
- if (IsMethodInBannedNamespace(overridden))
- return true;
- }
-
- return false;
- }
-
- void CheckOverriddenMethod(const CXXMethodDecl* method) {
- if (!method->size_overridden_methods() || method->getAttr<OverrideAttr>())
- return;
-
- if (isa<CXXDestructorDecl>(method) || method->isPure())
- return;
-
- if (IsMethodInBannedNamespace(method))
- return;
-
- SourceLocation loc = method->getTypeSpecStartLoc();
- emitWarning(loc, "Overriding method must be marked with OVERRIDE.");
- }
-
- // Makes sure there is a "virtual" keyword on virtual methods.
- //
- // Gmock objects trigger these for each MOCK_BLAH() macro used. So we have a
- // trick to get around that. If a class has member variables whose types are
- // in the "testing" namespace (which is how gmock works behind the scenes),
- // there's a really high chance we won't care about these errors
- void CheckVirtualMethods(SourceLocation record_location,
- CXXRecordDecl* record,
- bool warn_on_inline_bodies) {
- for (CXXRecordDecl::field_iterator it = record->field_begin();
- it != record->field_end(); ++it) {
- CXXRecordDecl* record_type =
- it->getTypeSourceInfo()->getTypeLoc().getTypePtr()->
- getAsCXXRecordDecl();
- if (record_type) {
- if (InTestingNamespace(record_type)) {
- return;
- }
- }
- }
-
- for (CXXRecordDecl::method_iterator it = record->method_begin();
- it != record->method_end(); ++it) {
- if (it->isCopyAssignmentOperator() || isa<CXXConstructorDecl>(*it)) {
- // Ignore constructors and assignment operators.
- } else if (isa<CXXDestructorDecl>(*it) &&
- !record->hasUserDeclaredDestructor()) {
- // Ignore non-user-declared destructors.
- } else {
- CheckVirtualMethod(*it, warn_on_inline_bodies);
- CheckOverriddenMethod(*it);
- }
- }
- }
-
- void CountType(const Type* type,
- int* trivial_member,
- int* non_trivial_member,
- int* templated_non_trivial_member) {
- switch (type->getTypeClass()) {
- case Type::Record: {
- // Simplifying; the whole class isn't trivial if the dtor is, but
- // we use this as a signal about complexity.
- if (TypeHasNonTrivialDtor(type))
- (*trivial_member)++;
- else
- (*non_trivial_member)++;
- break;
- }
- case Type::TemplateSpecialization: {
- TemplateName name =
- dyn_cast<TemplateSpecializationType>(type)->getTemplateName();
- bool whitelisted_template = false;
-
- // HACK: I'm at a loss about how to get the syntax checker to get
- // whether a template is exterened or not. For the first pass here,
- // just do retarded string comparisons.
- if (TemplateDecl* decl = name.getAsTemplateDecl()) {
- std::string base_name = decl->getNameAsString();
- if (base_name == "basic_string")
- whitelisted_template = true;
- }
-
- if (whitelisted_template)
- (*non_trivial_member)++;
- else
- (*templated_non_trivial_member)++;
- break;
- }
- case Type::Elaborated: {
- CountType(
- dyn_cast<ElaboratedType>(type)->getNamedType().getTypePtr(),
- trivial_member, non_trivial_member, templated_non_trivial_member);
- break;
- }
- case Type::Typedef: {
- while (const TypedefType* TT = dyn_cast<TypedefType>(type)) {
- type = TT->getDecl()->getUnderlyingType().getTypePtr();
- }
- CountType(type, trivial_member, non_trivial_member,
- templated_non_trivial_member);
- break;
- }
- default: {
- // Stupid assumption: anything we see that isn't the above is one of
- // the 20 integer types.
- (*trivial_member)++;
- break;
- }
- }
- }
-};
-
-class FindBadConstructsAction : public PluginASTAction {
- public:
- FindBadConstructsAction()
- : check_refcounted_dtors_(true),
- check_virtuals_in_implementations_(true) {
- }
-
- protected:
- // Overridden from PluginASTAction:
- virtual ASTConsumer* CreateASTConsumer(CompilerInstance& instance,
- llvm::StringRef ref) {
- return new FindBadConstructsConsumer(
- instance, check_refcounted_dtors_, check_virtuals_in_implementations_);
- }
-
- virtual bool ParseArgs(const CompilerInstance& instance,
- const std::vector<std::string>& args) {
- bool parsed = true;
-
- for (size_t i = 0; i < args.size() && parsed; ++i) {
- if (args[i] == "skip-refcounted-dtors") {
- check_refcounted_dtors_ = false;
- } else if (args[i] == "skip-virtuals-in-implementations") {
- check_virtuals_in_implementations_ = false;
- } else {
- parsed = false;
- llvm::errs() << "Unknown argument: " << args[i] << "\n";
- }
- }
-
- return parsed;
- }
-
- private:
- bool check_refcounted_dtors_;
- bool check_virtuals_in_implementations_;
-};
-
-} // namespace
-
-static FrontendPluginRegistry::Add<FindBadConstructsAction>
-X("find-bad-constructs", "Finds bad C++ constructs");
diff --git a/third_party/libwebrtc/tools/clang/plugins/Makefile b/third_party/libwebrtc/tools/clang/plugins/Makefile
deleted file mode 100644
index 0cfec71159..0000000000
--- a/third_party/libwebrtc/tools/clang/plugins/Makefile
+++ /dev/null
@@ -1,19 +0,0 @@
-# This file requires the clang build system, at least for now. So to use this
-# Makefile, you should execute the following commands to copy this directory
-# into a clang checkout:
-#
-# cp -R <this directory> third_party/llvm/tools/clang/tools/chrome-plugin
-# cd third_party/llvm/tools/clang/tools/chrome-plugin
-# make
-
-CLANG_LEVEL := ../..
-LIBRARYNAME = FindBadConstructs
-
-LINK_LIBS_IN_SHARED = 0
-SHARED_LIBRARY = 1
-
-include $(CLANG_LEVEL)/Makefile
-
-ifeq ($(OS),Darwin)
- LDFLAGS=-Wl,-undefined,dynamic_lookup
-endif
diff --git a/third_party/libwebrtc/tools/clang/plugins/OWNERS b/third_party/libwebrtc/tools/clang/plugins/OWNERS
deleted file mode 100644
index 4733a4f06b..0000000000
--- a/third_party/libwebrtc/tools/clang/plugins/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-erg@chromium.org
diff --git a/third_party/libwebrtc/tools/clang/plugins/README.chromium b/third_party/libwebrtc/tools/clang/plugins/README.chromium
deleted file mode 100644
index a2ce0ff557..0000000000
--- a/third_party/libwebrtc/tools/clang/plugins/README.chromium
+++ /dev/null
@@ -1,4 +0,0 @@
-Documentation for this code is:
-
-- http://code.google.com/p/chromium/wiki/Clang
-- http://code.google.com/p/chromium/wiki/WritingClangPlugins
diff --git a/third_party/libwebrtc/tools/clang/plugins/tests/base_refcounted.cpp b/third_party/libwebrtc/tools/clang/plugins/tests/base_refcounted.cpp
deleted file mode 100644
index 364a3e888c..0000000000
--- a/third_party/libwebrtc/tools/clang/plugins/tests/base_refcounted.cpp
+++ /dev/null
@@ -1,72 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base_refcounted.h"
-
-#include <cstddef>
-
-namespace {
-
-// Unsafe; should error.
-class AnonymousDerivedProtectedToPublicInImpl
- : public ProtectedRefCountedDtorInHeader {
- public:
- AnonymousDerivedProtectedToPublicInImpl() {}
- ~AnonymousDerivedProtectedToPublicInImpl() {}
-};
-
-} // namespace
-
-// Unsafe; should error.
-class PublicRefCountedDtorInImpl
- : public base::RefCounted<PublicRefCountedDtorInImpl> {
- public:
- PublicRefCountedDtorInImpl() {}
- ~PublicRefCountedDtorInImpl() {}
-
- private:
- friend class base::RefCounted<PublicRefCountedDtorInImpl>;
-};
-
-class Foo {
- public:
- class BarInterface {
- protected:
- virtual ~BarInterface() {}
- };
-
- typedef base::RefCounted<BarInterface> RefCountedBar;
- typedef RefCountedBar AnotherTypedef;
-};
-
-class Baz {
- public:
- typedef typename Foo::AnotherTypedef MyLocalTypedef;
-};
-
-// Unsafe; should error.
-class UnsafeTypedefChainInImpl : public Baz::MyLocalTypedef {
- public:
- UnsafeTypedefChainInImpl() {}
- ~UnsafeTypedefChainInImpl() {}
-};
-
-int main() {
- PublicRefCountedDtorInHeader bad;
- PublicRefCountedDtorInImpl also_bad;
-
- ProtectedRefCountedDtorInHeader* protected_ok = NULL;
- PrivateRefCountedDtorInHeader* private_ok = NULL;
-
- DerivedProtectedToPublicInHeader still_bad;
- PublicRefCountedThreadSafeDtorInHeader another_bad_variation;
- AnonymousDerivedProtectedToPublicInImpl and_this_is_bad_too;
- ImplicitDerivedProtectedToPublicInHeader bad_yet_again;
- UnsafeTypedefChainInImpl and_again_this_is_bad;
-
- WebKitPublicDtorInHeader ignored;
- WebKitDerivedPublicDtorInHeader still_ignored;
-
- return 0;
-}
diff --git a/third_party/libwebrtc/tools/clang/plugins/tests/base_refcounted.h b/third_party/libwebrtc/tools/clang/plugins/tests/base_refcounted.h
deleted file mode 100644
index 1e53215997..0000000000
--- a/third_party/libwebrtc/tools/clang/plugins/tests/base_refcounted.h
+++ /dev/null
@@ -1,121 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef BASE_REFCOUNTED_H_
-#define BASE_REFCOUNTED_H_
-
-namespace base {
-
-template <typename T>
-class RefCounted {
- public:
- RefCounted() {}
- ~RefCounted() {}
-};
-
-template <typename T>
-class RefCountedThreadSafe {
- public:
- RefCountedThreadSafe() {}
- ~RefCountedThreadSafe() {}
-};
-
-} // namespace base
-
-// Ignore classes whose inheritance tree ends in WebKit's RefCounted base
-// class. Though prone to error, this pattern is very prevalent in WebKit
-// code, so do not issue any warnings.
-namespace WebKit {
-
-template <typename T>
-class RefCounted {
- public:
- RefCounted() {}
- ~RefCounted() {}
-};
-
-} // namespace WebKit
-
-// Unsafe; should error.
-class PublicRefCountedDtorInHeader
- : public base::RefCounted<PublicRefCountedDtorInHeader> {
- public:
- PublicRefCountedDtorInHeader() {}
- ~PublicRefCountedDtorInHeader() {}
-
- private:
- friend class base::RefCounted<PublicRefCountedDtorInHeader>;
-};
-
-// Unsafe; should error.
-class PublicRefCountedThreadSafeDtorInHeader
- : public base::RefCountedThreadSafe<
- PublicRefCountedThreadSafeDtorInHeader> {
- public:
- PublicRefCountedThreadSafeDtorInHeader() {}
- ~PublicRefCountedThreadSafeDtorInHeader() {}
-
- private:
- friend class base::RefCountedThreadSafe<
- PublicRefCountedThreadSafeDtorInHeader>;
-};
-
-// Safe; should not have errors.
-class ProtectedRefCountedDtorInHeader
- : public base::RefCounted<ProtectedRefCountedDtorInHeader> {
- public:
- ProtectedRefCountedDtorInHeader() {}
-
- protected:
- ~ProtectedRefCountedDtorInHeader() {}
-
- private:
- friend class base::RefCounted<ProtectedRefCountedDtorInHeader>;
-};
-
-// Safe; should not have errors.
-class PrivateRefCountedDtorInHeader
- : public base::RefCounted<PrivateRefCountedDtorInHeader> {
- public:
- PrivateRefCountedDtorInHeader() {}
-
- private:
- ~PrivateRefCountedDtorInHeader() {}
- friend class base::RefCounted<PrivateRefCountedDtorInHeader>;
-};
-
-// Unsafe; A grandchild class ends up exposing their parent and grandparent's
-// destructors.
-class DerivedProtectedToPublicInHeader
- : public ProtectedRefCountedDtorInHeader {
- public:
- DerivedProtectedToPublicInHeader() {}
- ~DerivedProtectedToPublicInHeader() {}
-};
-
-// Unsafe; A grandchild ends up implicitly exposing their parent and
-// grantparent's destructors.
-class ImplicitDerivedProtectedToPublicInHeader
- : public ProtectedRefCountedDtorInHeader {
- public:
- ImplicitDerivedProtectedToPublicInHeader() {}
-};
-
-// Unsafe-but-ignored; should not have errors.
-class WebKitPublicDtorInHeader
- : public WebKit::RefCounted<WebKitPublicDtorInHeader> {
- public:
- WebKitPublicDtorInHeader() {}
- ~WebKitPublicDtorInHeader() {}
-};
-
-// Unsafe-but-ignored; should not have errors.
-class WebKitDerivedPublicDtorInHeader
- : public WebKitPublicDtorInHeader {
- public:
- WebKitDerivedPublicDtorInHeader() {}
- ~WebKitDerivedPublicDtorInHeader() {}
-};
-
-#endif // BASE_REFCOUNTED_H_
diff --git a/third_party/libwebrtc/tools/clang/plugins/tests/base_refcounted.txt b/third_party/libwebrtc/tools/clang/plugins/tests/base_refcounted.txt
deleted file mode 100644
index 4626424177..0000000000
--- a/third_party/libwebrtc/tools/clang/plugins/tests/base_refcounted.txt
+++ /dev/null
@@ -1,23 +0,0 @@
-In file included from base_refcounted.cpp:5:
-./base_refcounted.h:45:3: warning: [chromium-style] Classes that are ref-counted should not have public destructors.
- ~PublicRefCountedDtorInHeader() {}
- ^
-./base_refcounted.h:57:3: warning: [chromium-style] Classes that are ref-counted should not have public destructors.
- ~PublicRefCountedThreadSafeDtorInHeader() {}
- ^
-./base_refcounted.h:94:3: warning: [chromium-style] Classes that are ref-counted should not have public destructors.
- ~DerivedProtectedToPublicInHeader() {}
- ^
-./base_refcounted.h:99:1: warning: [chromium-style] Classes that are ref-counted should have explicit destructors that are protected or private.
-class ImplicitDerivedProtectedToPublicInHeader
-^
-base_refcounted.cpp:16:3: warning: [chromium-style] Classes that are ref-counted should not have public destructors.
- ~AnonymousDerivedProtectedToPublicInImpl() {}
- ^
-base_refcounted.cpp:26:3: warning: [chromium-style] Classes that are ref-counted should not have public destructors.
- ~PublicRefCountedDtorInImpl() {}
- ^
-base_refcounted.cpp:52:3: warning: [chromium-style] Classes that are ref-counted should not have public destructors.
- ~UnsafeTypedefChainInImpl() {}
- ^
-7 warnings generated.
diff --git a/third_party/libwebrtc/tools/clang/plugins/tests/inline_copy_ctor.cpp b/third_party/libwebrtc/tools/clang/plugins/tests/inline_copy_ctor.cpp
deleted file mode 100644
index dcd90020c5..0000000000
--- a/third_party/libwebrtc/tools/clang/plugins/tests/inline_copy_ctor.cpp
+++ /dev/null
@@ -1,5 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "inline_copy_ctor.h"
diff --git a/third_party/libwebrtc/tools/clang/plugins/tests/inline_copy_ctor.h b/third_party/libwebrtc/tools/clang/plugins/tests/inline_copy_ctor.h
deleted file mode 100644
index 619a18392b..0000000000
--- a/third_party/libwebrtc/tools/clang/plugins/tests/inline_copy_ctor.h
+++ /dev/null
@@ -1,12 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-struct C {
- C();
- ~C();
-
- static C foo() { return C(); }
-
- int a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p , q, r, s, t, u, v, w, x;
-};
diff --git a/third_party/libwebrtc/tools/clang/plugins/tests/inline_copy_ctor.txt b/third_party/libwebrtc/tools/clang/plugins/tests/inline_copy_ctor.txt
deleted file mode 100644
index bc4bd8911e..0000000000
--- a/third_party/libwebrtc/tools/clang/plugins/tests/inline_copy_ctor.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-In file included from inline_copy_ctor.cpp:5:
-./inline_copy_ctor.h:5:1: warning: [chromium-style] Complex class/struct needs an explicit out-of-line copy constructor.
-struct C {
-^
-1 warning generated.
diff --git a/third_party/libwebrtc/tools/clang/plugins/tests/inline_ctor.cpp b/third_party/libwebrtc/tools/clang/plugins/tests/inline_ctor.cpp
deleted file mode 100644
index 6a751fb405..0000000000
--- a/third_party/libwebrtc/tools/clang/plugins/tests/inline_ctor.cpp
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "inline_ctor.h"
-
-#include <string>
-#include <vector>
-
-// We don't warn on classes that are in CPP files.
-class InlineInCPPOK {
- public:
- InlineInCPPOK() {}
- ~InlineInCPPOK() {}
-
- private:
- std::vector<int> one_;
- std::vector<std::string> two_;
-};
-
-int main() {
- InlineInCPPOK one;
- InlineCtorsArentOKInHeader two;
- return 0;
-}
diff --git a/third_party/libwebrtc/tools/clang/plugins/tests/inline_ctor.h b/third_party/libwebrtc/tools/clang/plugins/tests/inline_ctor.h
deleted file mode 100644
index d053b2f57d..0000000000
--- a/third_party/libwebrtc/tools/clang/plugins/tests/inline_ctor.h
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef INLINE_CTOR_H_
-#define INLINE_CTOR_H_
-
-#include <string>
-#include <vector>
-
-class InlineCtorsArentOKInHeader {
- public:
- InlineCtorsArentOKInHeader() {}
- ~InlineCtorsArentOKInHeader() {}
-
- private:
- std::vector<int> one_;
- std::vector<std::string> two_;
-};
-
-#endif // INLINE_CTOR_H_
diff --git a/third_party/libwebrtc/tools/clang/plugins/tests/inline_ctor.txt b/third_party/libwebrtc/tools/clang/plugins/tests/inline_ctor.txt
deleted file mode 100644
index caa0cb4e3b..0000000000
--- a/third_party/libwebrtc/tools/clang/plugins/tests/inline_ctor.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-In file included from inline_ctor.cpp:5:
-./inline_ctor.h:13:3: warning: [chromium-style] Complex constructor has an inlined body.
- InlineCtorsArentOKInHeader() {}
- ^
-./inline_ctor.h:14:3: warning: [chromium-style] Complex destructor has an inline body.
- ~InlineCtorsArentOKInHeader() {}
- ^
-2 warnings generated.
diff --git a/third_party/libwebrtc/tools/clang/plugins/tests/missing_ctor.cpp b/third_party/libwebrtc/tools/clang/plugins/tests/missing_ctor.cpp
deleted file mode 100644
index 8ee2fb2ac8..0000000000
--- a/third_party/libwebrtc/tools/clang/plugins/tests/missing_ctor.cpp
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "missing_ctor.h"
-
-#include <string>
-#include <vector>
-
-// We don't warn on classes that use default ctors in cpp files.
-class MissingInCPPOK {
- public:
-
- private:
- std::vector<int> one_;
- std::vector<std::string> two_;
-};
-
-int main() {
- MissingInCPPOK one;
- MissingCtorsArentOKInHeader two;
- return 0;
-}
diff --git a/third_party/libwebrtc/tools/clang/plugins/tests/missing_ctor.h b/third_party/libwebrtc/tools/clang/plugins/tests/missing_ctor.h
deleted file mode 100644
index 1050457a1a..0000000000
--- a/third_party/libwebrtc/tools/clang/plugins/tests/missing_ctor.h
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef MISSING_CTOR_H_
-#define MISSING_CTOR_H_
-
-#include <string>
-#include <vector>
-
-class MissingCtorsArentOKInHeader {
- public:
-
- private:
- std::vector<int> one_;
- std::vector<std::string> two_;
-};
-
-#endif // MISSING_CTOR_H_
diff --git a/third_party/libwebrtc/tools/clang/plugins/tests/missing_ctor.txt b/third_party/libwebrtc/tools/clang/plugins/tests/missing_ctor.txt
deleted file mode 100644
index 301449c4ac..0000000000
--- a/third_party/libwebrtc/tools/clang/plugins/tests/missing_ctor.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-In file included from missing_ctor.cpp:5:
-./missing_ctor.h:11:1: warning: [chromium-style] Complex class/struct needs an explicit out-of-line constructor.
-class MissingCtorsArentOKInHeader {
-^
-./missing_ctor.h:11:1: warning: [chromium-style] Complex class/struct needs an explicit out-of-line destructor.
-2 warnings generated.
diff --git a/third_party/libwebrtc/tools/clang/plugins/tests/nested_class_inline_ctor.cpp b/third_party/libwebrtc/tools/clang/plugins/tests/nested_class_inline_ctor.cpp
deleted file mode 100644
index aa90a95eb3..0000000000
--- a/third_party/libwebrtc/tools/clang/plugins/tests/nested_class_inline_ctor.cpp
+++ /dev/null
@@ -1,5 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "nested_class_inline_ctor.h"
diff --git a/third_party/libwebrtc/tools/clang/plugins/tests/nested_class_inline_ctor.h b/third_party/libwebrtc/tools/clang/plugins/tests/nested_class_inline_ctor.h
deleted file mode 100644
index 01cfea9232..0000000000
--- a/third_party/libwebrtc/tools/clang/plugins/tests/nested_class_inline_ctor.h
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef NESTED_CLASS_INLINE_CTOR_H_
-#define NESTED_CLASS_INLINE_CTOR_H_
-
-#include <string>
-#include <vector>
-
-// See crbug.com/136863.
-
-class Foo {
- class Bar {
- Bar() {}
- ~Bar() {}
-
- std::vector<std::string> a;
- };
-};
-
-#endif // NESTED_CLASS_INLINE_CTOR_H_
diff --git a/third_party/libwebrtc/tools/clang/plugins/tests/nested_class_inline_ctor.txt b/third_party/libwebrtc/tools/clang/plugins/tests/nested_class_inline_ctor.txt
deleted file mode 100644
index 39bd6e1dce..0000000000
--- a/third_party/libwebrtc/tools/clang/plugins/tests/nested_class_inline_ctor.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-In file included from nested_class_inline_ctor.cpp:5:
-./nested_class_inline_ctor.h:15:5: warning: [chromium-style] Complex constructor has an inlined body.
- Bar() {}
- ^
-./nested_class_inline_ctor.h:16:5: warning: [chromium-style] Complex destructor has an inline body.
- ~Bar() {}
- ^
-2 warnings generated.
diff --git a/third_party/libwebrtc/tools/clang/plugins/tests/overridden_methods.cpp b/third_party/libwebrtc/tools/clang/plugins/tests/overridden_methods.cpp
deleted file mode 100644
index f572a41733..0000000000
--- a/third_party/libwebrtc/tools/clang/plugins/tests/overridden_methods.cpp
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "overridden_methods.h"
-
-// Fill in the implementations
-void DerivedClass::SomeMethod() {}
-void DerivedClass::SomeOtherMethod() {}
-void DerivedClass::WebKitModifiedSomething() {}
-
-class ImplementationInterimClass : public BaseClass {
- public:
- // Should not warn about pure virtual methods.
- virtual void SomeMethod() = 0;
-};
-
-class ImplementationDerivedClass : public ImplementationInterimClass,
- public webkit_glue::WebKitObserverImpl {
- public:
- // Should not warn about destructors.
- virtual ~ImplementationDerivedClass() {}
- // Should warn.
- virtual void SomeMethod();
- // Should not warn if marked as override.
- virtual void SomeOtherMethod() override;
- // Should not warn for inline implementations in implementation files.
- virtual void SomeInlineMethod() {}
- // Should not warn if overriding a method whose origin is WebKit.
- virtual void WebKitModifiedSomething();
- // Should warn if overridden method isn't pure.
- virtual void SomeNonPureBaseMethod() {}
-};
-
-int main() {
- DerivedClass something;
- ImplementationDerivedClass something_else;
-}
diff --git a/third_party/libwebrtc/tools/clang/plugins/tests/overridden_methods.h b/third_party/libwebrtc/tools/clang/plugins/tests/overridden_methods.h
deleted file mode 100644
index 150c79913f..0000000000
--- a/third_party/libwebrtc/tools/clang/plugins/tests/overridden_methods.h
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef OVERRIDDEN_METHODS_H_
-#define OVERRIDDEN_METHODS_H_
-
-// Should warn about overriding of methods.
-class BaseClass {
- public:
- virtual ~BaseClass() {}
- virtual void SomeMethod() = 0;
- virtual void SomeOtherMethod() = 0;
- virtual void SomeInlineMethod() = 0;
- virtual void SomeNonPureBaseMethod() {}
-};
-
-class InterimClass : public BaseClass {
- // Should not warn about pure virtual methods.
- virtual void SomeMethod() = 0;
-};
-
-namespace WebKit {
-class WebKitObserver {
- public:
- virtual void WebKitModifiedSomething() {};
-};
-} // namespace WebKit
-
-namespace webkit_glue {
-class WebKitObserverImpl : WebKit::WebKitObserver {
- public:
- virtual void WebKitModifiedSomething() {};
-};
-} // namespace webkit_glue
-
-class DerivedClass : public InterimClass,
- public webkit_glue::WebKitObserverImpl {
- public:
- // Should not warn about destructors.
- virtual ~DerivedClass() {}
- // Should warn.
- virtual void SomeMethod();
- // Should not warn if marked as override.
- virtual void SomeOtherMethod() override;
- // Should warn for inline implementations.
- virtual void SomeInlineMethod() {}
- // Should not warn if overriding a method whose origin is WebKit.
- virtual void WebKitModifiedSomething();
- // Should warn if overridden method isn't pure.
- virtual void SomeNonPureBaseMethod() {}
-};
-
-#endif // OVERRIDDEN_METHODS_H_
diff --git a/third_party/libwebrtc/tools/clang/plugins/tests/overridden_methods.txt b/third_party/libwebrtc/tools/clang/plugins/tests/overridden_methods.txt
deleted file mode 100644
index 7553ade70e..0000000000
--- a/third_party/libwebrtc/tools/clang/plugins/tests/overridden_methods.txt
+++ /dev/null
@@ -1,20 +0,0 @@
-In file included from overridden_methods.cpp:5:
-./overridden_methods.h:43:11: warning: [chromium-style] Overriding method must be marked with OVERRIDE.
- virtual void SomeMethod();
- ^
-./overridden_methods.h:47:11: warning: [chromium-style] Overriding method must be marked with OVERRIDE.
- virtual void SomeInlineMethod() {}
- ^
-./overridden_methods.h:51:11: warning: [chromium-style] Overriding method must be marked with OVERRIDE.
- virtual void SomeNonPureBaseMethod() {}
- ^
-overridden_methods.cpp:24:11: warning: [chromium-style] Overriding method must be marked with OVERRIDE.
- virtual void SomeMethod();
- ^
-overridden_methods.cpp:28:11: warning: [chromium-style] Overriding method must be marked with OVERRIDE.
- virtual void SomeInlineMethod() {}
- ^
-overridden_methods.cpp:32:11: warning: [chromium-style] Overriding method must be marked with OVERRIDE.
- virtual void SomeNonPureBaseMethod() {}
- ^
-6 warnings generated.
diff --git a/third_party/libwebrtc/tools/clang/plugins/tests/test.sh b/third_party/libwebrtc/tools/clang/plugins/tests/test.sh
deleted file mode 100755
index 262ebbba29..0000000000
--- a/third_party/libwebrtc/tools/clang/plugins/tests/test.sh
+++ /dev/null
@@ -1,72 +0,0 @@
-#!/bin/bash
-#
-# Copyright (c) 2011 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-#
-# Hacky, primitive testing: This runs the style plugin for a set of input files
-# and compares the output with golden result files.
-
-E_BADARGS=65
-E_FAILEDTEST=1
-
-failed_any_test=
-
-# Prints usage information.
-usage() {
- echo "Usage: $(basename "${0}")" \
- "<Path to the llvm build dir, usually Release+Asserts>"
- echo ""
- echo " Runs all the libFindBadConstructs unit tests"
- echo ""
-}
-
-# Runs a single test case.
-do_testcase() {
- local output="$("${CLANG_DIR}"/bin/clang -c -Wno-c++11-extensions \
- -Xclang -load -Xclang "${CLANG_DIR}"/lib/libFindBadConstructs.${LIB} \
- -Xclang -plugin -Xclang find-bad-constructs ${1} 2>&1)"
- local diffout="$(echo "${output}" | diff - "${2}")"
- if [ "${diffout}" = "" ]; then
- echo "PASS: ${1}"
- else
- failed_any_test=yes
- echo "FAIL: ${1}"
- echo "Output of compiler:"
- echo "${output}"
- echo "Expected output:"
- cat "${2}"
- echo
- fi
-}
-
-# Validate input to the script.
-if [[ -z "${1}" ]]; then
- usage
- exit ${E_BADARGS}
-elif [[ ! -d "${1}" ]]; then
- echo "${1} is not a directory."
- usage
- exit ${E_BADARGS}
-else
- export CLANG_DIR="${PWD}/${1}"
- echo "Using clang directory ${CLANG_DIR}..."
-
- # The golden files assume that the cwd is this directory. To make the script
- # work no matter what the cwd is, explicitly cd to there.
- cd "$(dirname "${0}")"
-
- if [ "$(uname -s)" = "Linux" ]; then
- export LIB=so
- elif [ "$(uname -s)" = "Darwin" ]; then
- export LIB=dylib
- fi
-fi
-
-for input in *.cpp; do
- do_testcase "${input}" "${input%cpp}txt"
-done
-
-if [[ "${failed_any_test}" ]]; then
- exit ${E_FAILEDTEST}
-fi
diff --git a/third_party/libwebrtc/tools/clang/plugins/tests/virtual_methods.cpp b/third_party/libwebrtc/tools/clang/plugins/tests/virtual_methods.cpp
deleted file mode 100644
index a07cbe4875..0000000000
--- a/third_party/libwebrtc/tools/clang/plugins/tests/virtual_methods.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "virtual_methods.h"
-
-// Shouldn't warn about method usage in the implementation file.
-class VirtualMethodsInImplementation {
- public:
- virtual void MethodIsAbstract() = 0;
- virtual void MethodHasNoArguments();
- virtual void MethodHasEmptyDefaultImpl() {}
- virtual bool ComplainAboutThis() { return true; }
-};
-
-// Stubs to fill in the abstract method
-class ConcreteVirtualMethodsInHeaders : public VirtualMethodsInHeaders {
- public:
- virtual void MethodIsAbstract() override {}
-};
-
-class ConcreteVirtualMethodsInImplementation
- : public VirtualMethodsInImplementation {
- public:
- virtual void MethodIsAbstract() override {}
-};
-
-// Fill in the implementations
-void VirtualMethodsInHeaders::MethodHasNoArguments() {}
-void WarnOnMissingVirtual::MethodHasNoArguments() {}
-void VirtualMethodsInImplementation::MethodHasNoArguments() {}
-
-int main() {
- ConcreteVirtualMethodsInHeaders one;
- ConcreteVirtualMethodsInImplementation two;
-}
diff --git a/third_party/libwebrtc/tools/clang/plugins/tests/virtual_methods.h b/third_party/libwebrtc/tools/clang/plugins/tests/virtual_methods.h
deleted file mode 100644
index d9fbf96ed3..0000000000
--- a/third_party/libwebrtc/tools/clang/plugins/tests/virtual_methods.h
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef VIRTUAL_METHODS_H_
-#define VIRTUAL_METHODS_H_
-
-// Should warn about virtual method usage.
-class VirtualMethodsInHeaders {
- public:
- // Don't complain about these.
- virtual void MethodIsAbstract() = 0;
- virtual void MethodHasNoArguments();
- virtual void MethodHasEmptyDefaultImpl() {}
-
- // But complain about this:
- virtual bool ComplainAboutThis() { return true; }
-};
-
-// Complain on missing 'virtual' keyword in overrides.
-class WarnOnMissingVirtual : public VirtualMethodsInHeaders {
- public:
- void MethodHasNoArguments() override;
-};
-
-// Don't complain about things in a 'testing' namespace.
-namespace testing {
-struct TestStruct {};
-} // namespace testing
-
-class VirtualMethodsInHeadersTesting : public VirtualMethodsInHeaders {
- public:
- // Don't complain about no virtual testing methods.
- void MethodHasNoArguments();
- private:
- testing::TestStruct tester_;
-};
-
-#endif // VIRTUAL_METHODS_H_
diff --git a/third_party/libwebrtc/tools/clang/plugins/tests/virtual_methods.txt b/third_party/libwebrtc/tools/clang/plugins/tests/virtual_methods.txt
deleted file mode 100644
index 571d6d667d..0000000000
--- a/third_party/libwebrtc/tools/clang/plugins/tests/virtual_methods.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-In file included from virtual_methods.cpp:5:
-./virtual_methods.h:17:36: warning: [chromium-style] virtual methods with non-empty bodies shouldn't be declared inline.
- virtual bool ComplainAboutThis() { return true; }
- ^
-./virtual_methods.h:23:3: warning: [chromium-style] Overriding method must have "virtual" keyword.
- void MethodHasNoArguments() override;
- ^
-2 warnings generated.
diff --git a/third_party/libwebrtc/tools/clang/scripts/package.sh b/third_party/libwebrtc/tools/clang/scripts/package.sh
deleted file mode 100755
index eb345810b9..0000000000
--- a/third_party/libwebrtc/tools/clang/scripts/package.sh
+++ /dev/null
@@ -1,87 +0,0 @@
-#!/bin/bash
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-# This script will check out llvm and clang, and then package the results up
-# to a tgz file.
-
-THIS_DIR="$(dirname "${0}")"
-LLVM_DIR="${THIS_DIR}/../../../third_party/llvm"
-LLVM_BOOTSTRAP_DIR="${THIS_DIR}/../../../third_party/llvm-bootstrap"
-LLVM_BUILD_DIR="${THIS_DIR}/../../../third_party/llvm-build"
-LLVM_BIN_DIR="${LLVM_BUILD_DIR}/Release+Asserts/bin"
-LLVM_LIB_DIR="${LLVM_BUILD_DIR}/Release+Asserts/lib"
-
-echo "Diff in llvm:" | tee buildlog.txt
-svn stat "${LLVM_DIR}" 2>&1 | tee -a buildlog.txt
-svn diff "${LLVM_DIR}" 2>&1 | tee -a buildlog.txt
-echo "Diff in llvm/tools/clang:" | tee -a buildlog.txt
-svn stat "${LLVM_DIR}/tools/clang" 2>&1 | tee -a buildlog.txt
-svn diff "${LLVM_DIR}/tools/clang" 2>&1 | tee -a buildlog.txt
-echo "Diff in llvm/projects/compiler-rt:" | tee -a buildlog.txt
-svn stat "${LLVM_DIR}/projects/compiler-rt" 2>&1 | tee -a buildlog.txt
-svn diff "${LLVM_DIR}/projects/compiler-rt" 2>&1 | tee -a buildlog.txt
-
-echo "Starting build" | tee -a buildlog.txt
-
-set -ex
-
-# Do a clobber build.
-rm -rf "${LLVM_BOOTSTRAP_DIR}"
-rm -rf "${LLVM_BUILD_DIR}"
-"${THIS_DIR}"/update.sh --run-tests --bootstrap --force-local-build 2>&1 | \
- tee -a buildlog.txt
-
-R=$("${LLVM_BIN_DIR}/clang" --version | \
- sed -ne 's/clang version .*(trunk \([0-9]*\))/\1/p')
-
-PDIR=clang-$R
-rm -rf $PDIR
-mkdir $PDIR
-mkdir $PDIR/bin
-mkdir $PDIR/lib
-
-# Copy buildlog over.
-cp buildlog.txt $PDIR/
-
-# Copy clang into pdir, symlink clang++ to it.
-cp "${LLVM_BIN_DIR}/clang" $PDIR/bin/
-(cd $PDIR/bin && ln -sf clang clang++ && cd -)
-
-# Copy plugins. Some of the dylibs are pretty big, so copy only the ones we
-# care about.
-if [ "$(uname -s)" = "Darwin" ]; then
- cp "${LLVM_LIB_DIR}/libFindBadConstructs.dylib" $PDIR/lib
-else
- cp "${LLVM_LIB_DIR}/libFindBadConstructs.so" $PDIR/lib
-fi
-
-# Copy built-in headers (lib/clang/3.2/include).
-# libcompiler-rt puts all kinds of libraries there too, but we want only ASan.
-if [ "$(uname -s)" = "Darwin" ]; then
- # Keep only Release+Asserts/lib/clang/3.2/lib/darwin/libclang_rt.asan_osx.a
- find "${LLVM_LIB_DIR}/clang" -type f -path '*lib/darwin*' | grep -v asan | \
- xargs rm
-else
- # Keep only
- # Release+Asserts/lib/clang/3.2/lib/linux/libclang_rt.{asan,tsan}-x86_64.a
- # TODO(thakis): Make sure the 32bit version of ASan runtime is kept too once
- # that's built. TSan runtime exists only for 64 bits.
- find "${LLVM_LIB_DIR}/clang" -type f -path '*lib/linux*' | \
- grep -v "asan\|tsan" | xargs rm
-fi
-
-cp -R "${LLVM_LIB_DIR}/clang" $PDIR/lib
-
-tar zcf $PDIR.tgz -C $PDIR bin lib buildlog.txt
-
-if [ "$(uname -s)" = "Darwin" ]; then
- PLATFORM=Mac
-else
- PLATFORM=Linux_x64
-fi
-
-echo To upload, run:
-echo gsutil cp -a public-read $PDIR.tgz \
- gs://chromium-browser-clang/$PLATFORM/$PDIR.tgz
diff --git a/third_party/libwebrtc/tools/clang/scripts/plugin_flags.sh b/third_party/libwebrtc/tools/clang/scripts/plugin_flags.sh
deleted file mode 100755
index 217c5c3bd6..0000000000
--- a/third_party/libwebrtc/tools/clang/scripts/plugin_flags.sh
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/bin/bash
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-# This script returns the flags that should be used when GYP_DEFINES contains
-# clang_use_chrome_plugins. The flags are stored in a script so that they can
-# be changed on the bots without requiring a master restart.
-
-THIS_ABS_DIR=$(cd $(dirname $0) && echo $PWD)
-CLANG_LIB_PATH=$THIS_ABS_DIR/../../../third_party/llvm-build/Release+Asserts/lib
-
-if uname -s | grep -q Darwin; then
- LIBSUFFIX=dylib
-else
- LIBSUFFIX=so
-fi
-
-echo -Xclang -load -Xclang $CLANG_LIB_PATH/libFindBadConstructs.$LIBSUFFIX \
- -Xclang -add-plugin -Xclang find-bad-constructs \
- -Xclang -plugin-arg-find-bad-constructs \
- -Xclang skip-virtuals-in-implementations \
- -Xclang -plugin-arg-find-bad-constructs \
- -Xclang check-cc-directory
diff --git a/third_party/libwebrtc/tools/clang/scripts/update.py b/third_party/libwebrtc/tools/clang/scripts/update.py
deleted file mode 100755
index bdc781f715..0000000000
--- a/third_party/libwebrtc/tools/clang/scripts/update.py
+++ /dev/null
@@ -1,34 +0,0 @@
-#!/usr/bin/env python
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-"""Windows can't run .sh files, so this is a small python wrapper around
-update.sh.
-"""
-
-import os
-import subprocess
-import sys
-
-
-def main():
- if sys.platform in ['win32', 'cygwin']:
- return 0
-
- # This script is called by gclient. gclient opens its hooks subprocesses with
- # (stdout=subprocess.PIPE, stderr=subprocess.STDOUT) and then does custom
- # output processing that breaks printing '\r' characters for single-line
- # updating status messages as printed by curl and wget.
- # Work around this by setting stderr of the update.sh process to stdin (!):
- # gclient doesn't redirect stdin, and while stdin itself is read-only, a
- # dup()ed sys.stdin is writable, try
- # fd2 = os.dup(sys.stdin.fileno()); os.write(fd2, 'hi')
- # TODO: Fix gclient instead, http://crbug.com/95350
- return subprocess.call(
- [os.path.join(os.path.dirname(__file__), 'update.sh')] + sys.argv[1:],
- stderr=os.fdopen(os.dup(sys.stdin.fileno())))
-
-
-if __name__ == '__main__':
- sys.exit(main())
diff --git a/third_party/libwebrtc/tools/clang/scripts/update.sh b/third_party/libwebrtc/tools/clang/scripts/update.sh
deleted file mode 100755
index e9448236c8..0000000000
--- a/third_party/libwebrtc/tools/clang/scripts/update.sh
+++ /dev/null
@@ -1,286 +0,0 @@
-#!/usr/bin/env bash
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-# This script will check out llvm and clang into third_party/llvm and build it.
-
-# Do NOT CHANGE this if you don't know what you're doing -- see
-# https://code.google.com/p/chromium/wiki/UpdatingClang
-# Reverting problematic clang rolls is safe, though.
-CLANG_REVISION=163674
-
-THIS_DIR="$(dirname "${0}")"
-LLVM_DIR="${THIS_DIR}/../../../third_party/llvm"
-LLVM_BUILD_DIR="${LLVM_DIR}/../llvm-build"
-LLVM_BOOTSTRAP_DIR="${LLVM_DIR}/../llvm-bootstrap"
-CLANG_DIR="${LLVM_DIR}/tools/clang"
-COMPILER_RT_DIR="${LLVM_DIR}/projects/compiler-rt"
-STAMP_FILE="${LLVM_BUILD_DIR}/cr_build_revision"
-
-# ${A:-a} returns $A if it's set, a else.
-LLVM_REPO_URL=${LLVM_URL:-https://llvm.org/svn/llvm-project}
-
-# Die if any command dies.
-set -e
-
-OS="$(uname -s)"
-
-# Parse command line options.
-force_local_build=
-mac_only=
-run_tests=
-bootstrap=
-while [[ $# > 0 ]]; do
- case $1 in
- --bootstrap)
- bootstrap=yes
- ;;
- --force-local-build)
- force_local_build=yes
- ;;
- --mac-only)
- mac_only=yes
- ;;
- --run-tests)
- run_tests=yes
- ;;
- --help)
- echo "usage: $0 [--force-local-build] [--mac-only] [--run-tests] "
- echo "--bootstrap: First build clang with CC, then with itself."
- echo "--force-local-build: Don't try to download prebuilt binaries."
- echo "--mac-only: Do initial download only on Mac systems."
- echo "--run-tests: Run tests after building. Only for local builds."
- exit 1
- ;;
- esac
- shift
-done
-
-# --mac-only prevents the initial download on non-mac systems, but if clang has
-# already been downloaded in the past, this script keeps it up to date even if
-# --mac-only is passed in and the system isn't a mac. People who don't like this
-# can just delete their third_party/llvm-build directory.
-if [[ -n "$mac_only" ]] && [[ "${OS}" != "Darwin" ]] &&
- [[ "$GYP_DEFINES" != *clang=1* ]] && ! [[ -d "${LLVM_BUILD_DIR}" ]]; then
- exit 0
-fi
-
-# Xcode and clang don't get along when predictive compilation is enabled.
-# http://crbug.com/96315
-if [[ "${OS}" = "Darwin" ]] && xcodebuild -version | grep -q 'Xcode 3.2' ; then
- XCONF=com.apple.Xcode
- if [[ "${GYP_GENERATORS}" != "make" ]] && \
- [ "$(defaults read "${XCONF}" EnablePredictiveCompilation)" != "0" ]; then
- echo
- echo " HEARKEN!"
- echo "You're using Xcode3 and you have 'Predictive Compilation' enabled."
- echo "This does not work well with clang (http://crbug.com/96315)."
- echo "Disable it in Preferences->Building (lower right), or run"
- echo " defaults write ${XCONF} EnablePredictiveCompilation -boolean NO"
- echo "while Xcode is not running."
- echo
- fi
-
- SUB_VERSION=$(xcodebuild -version | sed -Ene 's/Xcode 3\.2\.([0-9]+)/\1/p')
- if [[ "${SUB_VERSION}" < 6 ]]; then
- echo
- echo " YOUR LD IS BUGGY!"
- echo "Please upgrade Xcode to at least 3.2.6."
- echo
- fi
-fi
-
-
-# Check if there's anything to be done, exit early if not.
-if [[ -f "${STAMP_FILE}" ]]; then
- PREVIOUSLY_BUILT_REVISON=$(cat "${STAMP_FILE}")
- if [[ -z "$force_local_build" ]] && \
- [[ "${PREVIOUSLY_BUILT_REVISON}" = "${CLANG_REVISION}" ]]; then
- echo "Clang already at ${CLANG_REVISION}"
- exit 0
- fi
-fi
-# To always force a new build if someone interrupts their build half way.
-rm -f "${STAMP_FILE}"
-
-# Clobber pch files, since they only work with the compiler version that
-# created them. Also clobber .o files, to make sure everything will be built
-# with the new compiler.
-if [[ "${OS}" = "Darwin" ]]; then
- XCODEBUILD_DIR="${THIS_DIR}/../../../xcodebuild"
-
- # Xcode groups .o files by project first, configuration second.
- if [[ -d "${XCODEBUILD_DIR}" ]]; then
- echo "Clobbering .o files for Xcode build"
- find "${XCODEBUILD_DIR}" -name '*.o' -exec rm {} +
- fi
-fi
-
-if [ -f "${THIS_DIR}/../../../WebKit.gyp" ]; then
- # We're inside a WebKit checkout.
- # TODO(thakis): try to unify the directory layout of the xcode- and
- # make-based builds. http://crbug.com/110455
- MAKE_DIR="${THIS_DIR}/../../../../../../out"
-else
- # We're inside a Chromium checkout.
- MAKE_DIR="${THIS_DIR}/../../../out"
-fi
-
-for CONFIG in Debug Release; do
- if [[ -d "${MAKE_DIR}/${CONFIG}/obj.target" ||
- -d "${MAKE_DIR}/${CONFIG}/obj.host" ]]; then
- echo "Clobbering ${CONFIG} PCH and .o files for make build"
- if [[ -d "${MAKE_DIR}/${CONFIG}/obj.target" ]]; then
- find "${MAKE_DIR}/${CONFIG}/obj.target" -name '*.gch' -exec rm {} +
- find "${MAKE_DIR}/${CONFIG}/obj.target" -name '*.o' -exec rm {} +
- fi
- if [[ -d "${MAKE_DIR}/${CONFIG}/obj.host" ]]; then
- find "${MAKE_DIR}/${CONFIG}/obj.host" -name '*.o' -exec rm {} +
- fi
- fi
-
- # ninja puts its output below ${MAKE_DIR} as well.
- if [[ -d "${MAKE_DIR}/${CONFIG}/obj" ]]; then
- echo "Clobbering ${CONFIG} PCH and .o files for ninja build"
- find "${MAKE_DIR}/${CONFIG}/obj" -name '*.gch' -exec rm {} +
- find "${MAKE_DIR}/${CONFIG}/obj" -name '*.o' -exec rm {} +
- find "${MAKE_DIR}/${CONFIG}/obj" -name '*.o.d' -exec rm {} +
- fi
-
- if [[ "${OS}" = "Darwin" ]]; then
- if [[ -d "${XCODEBUILD_DIR}/${CONFIG}/SharedPrecompiledHeaders" ]]; then
- echo "Clobbering ${CONFIG} PCH files for Xcode build"
- rm -rf "${XCODEBUILD_DIR}/${CONFIG}/SharedPrecompiledHeaders"
- fi
- fi
-done
-
-if [[ -z "$force_local_build" ]]; then
- # Check if there's a prebuilt binary and if so just fetch that. That's faster,
- # and goma relies on having matching binary hashes on client and server too.
- CDS_URL=https://commondatastorage.googleapis.com/chromium-browser-clang
- CDS_FILE="clang-${CLANG_REVISION}.tgz"
- CDS_OUT_DIR=$(mktemp -d -t clang_download.XXXXXX)
- CDS_OUTPUT="${CDS_OUT_DIR}/${CDS_FILE}"
- if [ "${OS}" = "Linux" ]; then
- CDS_FULL_URL="${CDS_URL}/Linux_x64/${CDS_FILE}"
- elif [ "${OS}" = "Darwin" ]; then
- CDS_FULL_URL="${CDS_URL}/Mac/${CDS_FILE}"
- fi
- echo Trying to download prebuilt clang
- if which curl > /dev/null; then
- curl -L --fail "${CDS_FULL_URL}" -o "${CDS_OUTPUT}" || \
- rm -rf "${CDS_OUT_DIR}"
- elif which wget > /dev/null; then
- wget "${CDS_FULL_URL}" -O "${CDS_OUTPUT}" || rm -rf "${CDS_OUT_DIR}"
- else
- echo "Neither curl nor wget found. Please install one of these."
- exit 1
- fi
- if [ -f "${CDS_OUTPUT}" ]; then
- rm -rf "${LLVM_BUILD_DIR}/Release+Asserts"
- mkdir -p "${LLVM_BUILD_DIR}/Release+Asserts"
- tar -xzf "${CDS_OUTPUT}" -C "${LLVM_BUILD_DIR}/Release+Asserts"
- echo clang "${CLANG_REVISION}" unpacked
- echo "${CLANG_REVISION}" > "${STAMP_FILE}"
- rm -rf "${CDS_OUT_DIR}"
- exit 0
- else
- echo Did not find prebuilt clang at r"${CLANG_REVISION}", building
- fi
-fi
-
-echo Getting LLVM r"${CLANG_REVISION}" in "${LLVM_DIR}"
-if ! svn co --force "${LLVM_REPO_URL}/llvm/trunk@${CLANG_REVISION}" \
- "${LLVM_DIR}"; then
- echo Checkout failed, retrying
- rm -rf "${LLVM_DIR}"
- svn co --force "${LLVM_REPO_URL}/llvm/trunk@${CLANG_REVISION}" "${LLVM_DIR}"
-fi
-
-echo Getting clang r"${CLANG_REVISION}" in "${CLANG_DIR}"
-svn co --force "${LLVM_REPO_URL}/cfe/trunk@${CLANG_REVISION}" "${CLANG_DIR}"
-
-echo Getting compiler-rt r"${CLANG_REVISION}" in "${COMPILER_RT_DIR}"
-svn co --force "${LLVM_REPO_URL}/compiler-rt/trunk@${CLANG_REVISION}" \
- "${COMPILER_RT_DIR}"
-
-# Echo all commands.
-set -x
-
-NUM_JOBS=3
-if [[ "${OS}" = "Linux" ]]; then
- NUM_JOBS="$(grep -c "^processor" /proc/cpuinfo)"
-elif [ "${OS}" = "Darwin" ]; then
- NUM_JOBS="$(sysctl -n hw.ncpu)"
-fi
-
-# Build bootstrap clang if requested.
-if [[ -n "${bootstrap}" ]]; then
- echo "Building bootstrap compiler"
- mkdir -p "${LLVM_BOOTSTRAP_DIR}"
- cd "${LLVM_BOOTSTRAP_DIR}"
- if [[ ! -f ./config.status ]]; then
- # The bootstrap compiler only needs to be able to build the real compiler,
- # so it needs no cross-compiler output support. In general, the host
- # compiler should be as similar to the final compiler as possible, so do
- # keep --disable-threads & co.
- ../llvm/configure \
- --enable-optimized \
- --enable-targets=host-only \
- --disable-threads \
- --disable-pthreads \
- --without-llvmgcc \
- --without-llvmgxx
- MACOSX_DEPLOYMENT_TARGET=10.5 make -j"${NUM_JOBS}"
- fi
- if [[ -n "${run_tests}" ]]; then
- make check-all
- fi
- cd -
- export CC="${PWD}/${LLVM_BOOTSTRAP_DIR}/Release+Asserts/bin/clang"
- export CXX="${PWD}/${LLVM_BOOTSTRAP_DIR}/Release+Asserts/bin/clang++"
- echo "Building final compiler"
-fi
-
-# Build clang (in a separate directory).
-# The clang bots have this path hardcoded in built/scripts/slave/compile.py,
-# so if you change it you also need to change these links.
-mkdir -p "${LLVM_BUILD_DIR}"
-cd "${LLVM_BUILD_DIR}"
-if [[ ! -f ./config.status ]]; then
- ../llvm/configure \
- --enable-optimized \
- --disable-threads \
- --disable-pthreads \
- --without-llvmgcc \
- --without-llvmgxx
-fi
-
-MACOSX_DEPLOYMENT_TARGET=10.5 make -j"${NUM_JOBS}"
-cd -
-
-# Build plugin.
-# Copy it into the clang tree and use clang's build system to compile the
-# plugin.
-PLUGIN_SRC_DIR="${THIS_DIR}/../plugins"
-PLUGIN_DST_DIR="${LLVM_DIR}/tools/clang/tools/chrome-plugin"
-PLUGIN_BUILD_DIR="${LLVM_BUILD_DIR}/tools/clang/tools/chrome-plugin"
-rm -rf "${PLUGIN_DST_DIR}"
-cp -R "${PLUGIN_SRC_DIR}" "${PLUGIN_DST_DIR}"
-rm -rf "${PLUGIN_BUILD_DIR}"
-mkdir -p "${PLUGIN_BUILD_DIR}"
-cp "${PLUGIN_SRC_DIR}/Makefile" "${PLUGIN_BUILD_DIR}"
-MACOSX_DEPLOYMENT_TARGET=10.5 make -j"${NUM_JOBS}" -C "${PLUGIN_BUILD_DIR}"
-
-if [[ -n "$run_tests" ]]; then
- # Run a few tests.
- "${PLUGIN_SRC_DIR}/tests/test.sh" "${LLVM_BUILD_DIR}/Release+Asserts"
- cd "${LLVM_BUILD_DIR}"
- make check-all
- cd -
-fi
-
-# After everything is done, log success for this revision.
-echo "${CLANG_REVISION}" > "${STAMP_FILE}"
diff --git a/third_party/libwebrtc/tools_webrtc/mb/mb_config.pyl b/third_party/libwebrtc/tools_webrtc/mb/mb_config.pyl
index 6aea3c9cba..c0f5130ff0 100644
--- a/third_party/libwebrtc/tools_webrtc/mb/mb_config.pyl
+++ b/third_party/libwebrtc/tools_webrtc/mb/mb_config.pyl
@@ -227,18 +227,65 @@
'configs': {
'android_asan_shared_release_bot_arm':
['android', 'asan', 'clang', 'pure_release_bot', 'arm'],
- 'android_debug_static_bot_arm': ['android', 'debug_static_bot', 'arm'],
- 'android_debug_static_bot_arm64': ['android', 'debug_static_bot', 'arm64'],
- 'android_debug_static_bot_x64': ['android', 'debug_static_bot', 'x64'],
- 'android_debug_static_bot_x86': ['android', 'debug_static_bot', 'x86'],
+ 'android_debug_static_bot_arm': [
+ 'android',
+ 'debug_static_bot',
+ 'arm',
+ 'h265',
+ ],
+ 'android_debug_static_bot_arm64': [
+ 'android',
+ 'debug_static_bot',
+ 'arm64',
+ 'h265',
+ ],
+ 'android_debug_static_bot_x64': [
+ 'android',
+ 'debug_static_bot',
+ 'x64',
+ 'h265',
+ ],
+ 'android_debug_static_bot_x86': [
+ 'android',
+ 'debug_static_bot',
+ 'x86',
+ 'h265',
+ ],
'android_pure_release_bot_arm': ['android', 'pure_release_bot', 'arm'],
'android_pure_release_bot_arm64': ['android', 'pure_release_bot', 'arm64'],
- 'android_release_bot_arm': ['android', 'release_bot', 'arm'],
- 'android_release_bot_arm64': ['android', 'release_bot', 'arm64'],
- 'android_release_bot_x64': ['android', 'release_bot', 'x64'],
- 'android_release_bot_x86': ['android', 'release_bot', 'x86'],
- 'asan_lsan_clang_release_bot_x64':
- ['asan', 'lsan', 'clang', 'openh264', 'pure_release_bot', 'x64', 'h265'],
+ 'android_release_bot_arm': [
+ 'android',
+ 'release_bot',
+ 'arm',
+ 'h265',
+ ],
+ 'android_release_bot_arm64': [
+ 'android',
+ 'release_bot',
+ 'arm64',
+ 'h265',
+ ],
+ 'android_release_bot_x64': [
+ 'android',
+ 'release_bot',
+ 'x64',
+ 'h265',
+ ],
+ 'android_release_bot_x86': [
+ 'android',
+ 'release_bot',
+ 'x86',
+ 'h265',
+ ],
+ 'asan_lsan_clang_release_bot_x64': [
+ 'asan',
+ 'lsan',
+ 'clang',
+ 'openh264',
+ 'pure_release_bot',
+ 'x64',
+ 'h265',
+ ],
'bwe_test_logging_android_arm':
['android', 'debug_static_bot', 'arm', 'bwe_test_logging'],
'bwe_test_logging_x64': ['debug_bot', 'x64', 'bwe_test_logging'],
diff --git a/third_party/libwebrtc/tools_webrtc/remove_extra_namespace.py b/third_party/libwebrtc/tools_webrtc/remove_extra_namespace.py
new file mode 100755
index 0000000000..21ac2d1aa2
--- /dev/null
+++ b/third_party/libwebrtc/tools_webrtc/remove_extra_namespace.py
@@ -0,0 +1,93 @@
+#!/usr/bin/env vpython3
+
+# Copyright (c) 2023 The WebRTC project authors. All Rights Reserved.
+#
+# Use of this source code is governed by a BSD-style license
+# that can be found in the LICENSE file in the root of the source
+# tree. An additional intellectual property rights grant can be found
+# in the file PATENTS. All contributing project authors may
+# be found in the AUTHORS file in the root of the source tree.
+"""Remove extra namespace qualifications
+
+Looks for names that don't need to be qualified by namespace, and deletes
+the qualifier.
+
+Depends on namespace names being properly formatted
+"""
+import os
+import glob
+import sys
+import re
+import argparse
+
+
+def remove_extra_namespace_from_file(namespace, filename):
+ print('Processing namespace', namespace, 'file', filename)
+ with open(filename) as file:
+ newfile = open(filename + '.NEW', 'w')
+ namespaces = []
+ changes = 0
+ for line in file:
+ match = re.match(r'namespace (\S+) {', line)
+ if match is not None:
+ namespaces.insert(0, match.group(1))
+ newfile.write(line)
+ continue
+ match = re.match(r'}\s+// namespace (\S+)$', line)
+ if match is not None:
+ if match.group(1) != namespaces[0]:
+ print('Namespace mismatch')
+ raise RuntimeError('Namespace mismatch')
+ del namespaces[0]
+ newfile.write(line)
+ continue
+ # Remove namespace usage. Only replacing when target
+ # namespace is the innermost namespace.
+ if len(namespaces) > 0 and namespaces[0] == namespace:
+ # Note that in namespace foo, we match neither ::foo::name
+ # nor morefoo::name
+ # Neither do we match foo:: when it is not followed by
+ # an identifier character.
+ usage_re = r'(?<=[^a-z:]){}::(?=[a-zA-Z])'.format(
+ namespaces[0])
+ if re.search(usage_re, line):
+ line = re.sub(usage_re, '', line)
+ changes += 1
+ newfile.write(line)
+ if changes > 0:
+ print('Made', changes, 'changes to', filename)
+ os.remove(filename)
+ os.rename(filename + '.NEW', filename)
+ else:
+ os.remove(filename + '.NEW')
+
+
+def remove_extra_namespace_from_files(namespace, files):
+ for file in files:
+ if os.path.isfile(file):
+ if re.search(r'\.(h|cc)$', file):
+ remove_extra_namespace_from_file(namespace, file)
+ elif os.path.isdir(file):
+ if file in ('third_party', 'out'):
+ continue
+ subfiles = glob.glob(file + '/*')
+ remove_extra_namespace_from_files(namespace, subfiles)
+ else:
+ print(file, 'is not a file or directory, ignoring')
+
+
+def main():
+ parser = argparse.ArgumentParser(
+ prog='remove_extra_namespace.py',
+ description=__doc__.strip().splitlines()[0],
+ epilog=''.join(__doc__.splitlines(True)[1:]),
+ formatter_class=argparse.RawDescriptionHelpFormatter,
+ )
+ parser.add_argument('--namespace')
+ parser.add_argument('files', nargs=argparse.REMAINDER)
+ args = parser.parse_args()
+ return remove_extra_namespace_from_files(args.namespace, args.files)
+
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/third_party/libwebrtc/video/BUILD.gn b/third_party/libwebrtc/video/BUILD.gn
index e84b73a352..0a930053c0 100644
--- a/third_party/libwebrtc/video/BUILD.gn
+++ b/third_party/libwebrtc/video/BUILD.gn
@@ -836,6 +836,8 @@ if (rtc_include_tests) {
"../api:transport_api",
"../api/adaptation:resource_adaptation_api",
"../api/crypto:options",
+ "../api/environment",
+ "../api/environment:environment_factory",
"../api/metronome/test:fake_metronome",
"../api/rtc_event_log",
"../api/task_queue",
@@ -843,7 +845,6 @@ if (rtc_include_tests) {
"../api/test/metrics:global_metrics_logger_and_exporter",
"../api/test/metrics:metric",
"../api/test/video:function_video_factory",
- "../api/transport:field_trial_based_config",
"../api/units:data_rate",
"../api/units:frequency",
"../api/units:time_delta",
diff --git a/third_party/libwebrtc/video/adaptation/video_adaptation_gn/moz.build b/third_party/libwebrtc/video/adaptation/video_adaptation_gn/moz.build
index 2d1fd69ed0..18d29da556 100644
--- a/third_party/libwebrtc/video/adaptation/video_adaptation_gn/moz.build
+++ b/third_party/libwebrtc/video/adaptation/video_adaptation_gn/moz.build
@@ -209,7 +209,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -219,10 +218,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/video/config/encoder_config_gn/moz.build b/third_party/libwebrtc/video/config/encoder_config_gn/moz.build
index 1c2c9ef835..4dbe7bf32c 100644
--- a/third_party/libwebrtc/video/config/encoder_config_gn/moz.build
+++ b/third_party/libwebrtc/video/config/encoder_config_gn/moz.build
@@ -195,7 +195,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -205,10 +204,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/video/config/streams_config_gn/moz.build b/third_party/libwebrtc/video/config/streams_config_gn/moz.build
index e2d2f9963d..c34883fb37 100644
--- a/third_party/libwebrtc/video/config/streams_config_gn/moz.build
+++ b/third_party/libwebrtc/video/config/streams_config_gn/moz.build
@@ -201,7 +201,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -211,10 +210,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/video/decode_synchronizer_gn/moz.build b/third_party/libwebrtc/video/decode_synchronizer_gn/moz.build
index d0e4adeda2..0aff4a854c 100644
--- a/third_party/libwebrtc/video/decode_synchronizer_gn/moz.build
+++ b/third_party/libwebrtc/video/decode_synchronizer_gn/moz.build
@@ -199,7 +199,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -209,10 +208,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/video/end_to_end_tests/multi_stream_tester.cc b/third_party/libwebrtc/video/end_to_end_tests/multi_stream_tester.cc
index 1eb388cc76..759b75ccf9 100644
--- a/third_party/libwebrtc/video/end_to_end_tests/multi_stream_tester.cc
+++ b/third_party/libwebrtc/video/end_to_end_tests/multi_stream_tester.cc
@@ -14,8 +14,8 @@
#include <utility>
#include <vector>
-#include "api/rtc_event_log/rtc_event_log.h"
-#include "api/task_queue/default_task_queue_factory.h"
+#include "api/environment/environment.h"
+#include "api/environment/environment_factory.h"
#include "api/task_queue/task_queue_base.h"
#include "api/test/create_frame_generator.h"
#include "api/test/simulated_network.h"
@@ -42,17 +42,13 @@ MultiStreamTester::MultiStreamTester() {
MultiStreamTester::~MultiStreamTester() = default;
void MultiStreamTester::RunTest() {
- webrtc::RtcEventLogNull event_log;
- auto task_queue_factory = CreateDefaultTaskQueueFactory();
+ Environment env = CreateEnvironment();
// Use high prioirity since this task_queue used for fake network delivering
// at correct time. Those test tasks should be prefered over code under test
// to make test more stable.
- auto task_queue = task_queue_factory->CreateTaskQueue(
+ auto task_queue = env.task_queue_factory().CreateTaskQueue(
"TaskQueue", TaskQueueFactory::Priority::HIGH);
- CallConfig config(&event_log);
- test::ScopedKeyValueConfig field_trials;
- config.trials = &field_trials;
- config.task_queue_factory = task_queue_factory.get();
+ CallConfig config(env);
std::unique_ptr<Call> sender_call;
std::unique_ptr<Call> receiver_call;
std::unique_ptr<test::DirectTransport> sender_transport;
@@ -115,10 +111,10 @@ void MultiStreamTester::RunTest() {
receive_streams[i]->Start();
auto* frame_generator = new test::FrameGeneratorCapturer(
- Clock::GetRealTimeClock(),
+ &env.clock(),
test::CreateSquareFrameGenerator(width, height, absl::nullopt,
absl::nullopt),
- 30, *task_queue_factory);
+ 30, env.task_queue_factory());
frame_generators[i] = frame_generator;
send_streams[i]->SetSource(frame_generator,
DegradationPreference::MAINTAIN_FRAMERATE);
diff --git a/third_party/libwebrtc/video/end_to_end_tests/network_state_tests.cc b/third_party/libwebrtc/video/end_to_end_tests/network_state_tests.cc
index 7bc9f1493e..7e9c86362a 100644
--- a/third_party/libwebrtc/video/end_to_end_tests/network_state_tests.cc
+++ b/third_party/libwebrtc/video/end_to_end_tests/network_state_tests.cc
@@ -94,7 +94,7 @@ void NetworkStateEndToEndTest::VerifyNewVideoSendStreamsRespectNetworkState(
SendTask(task_queue(), [this, network_to_bring_up, &encoder_factory,
transport]() {
- CreateSenderCall(CallConfig(send_event_log_.get()));
+ CreateSenderCall();
sender_call_->SignalChannelNetworkState(network_to_bring_up, kNetworkUp);
CreateSendConfig(1, 0, 0, transport);
diff --git a/third_party/libwebrtc/video/end_to_end_tests/stats_tests.cc b/third_party/libwebrtc/video/end_to_end_tests/stats_tests.cc
index cc0b328b2b..d6820eeac2 100644
--- a/third_party/libwebrtc/video/end_to_end_tests/stats_tests.cc
+++ b/third_party/libwebrtc/video/end_to_end_tests/stats_tests.cc
@@ -518,9 +518,9 @@ TEST_F(StatsEndToEndTest, MAYBE_ContentTypeSwitches) {
metrics::Reset();
- CallConfig send_config(send_event_log_.get());
+ CallConfig send_config = SendCallConfig();
test.ModifySenderBitrateConfig(&send_config.bitrate_config);
- CallConfig recv_config(recv_event_log_.get());
+ CallConfig recv_config = RecvCallConfig();
test.ModifyReceiverBitrateConfig(&recv_config.bitrate_config);
VideoEncoderConfig encoder_config_with_screenshare;
@@ -732,13 +732,13 @@ TEST_F(StatsEndToEndTest, CallReportsRttForSender) {
Start();
});
- int64_t start_time_ms = clock_->TimeInMilliseconds();
+ int64_t start_time_ms = env().clock().TimeInMilliseconds();
while (true) {
Call::Stats stats;
SendTask(task_queue(),
[this, &stats]() { stats = sender_call_->GetStats(); });
ASSERT_GE(start_time_ms + test::VideoTestConstants::kDefaultTimeout.ms(),
- clock_->TimeInMilliseconds())
+ env().clock().TimeInMilliseconds())
<< "No RTT stats before timeout!";
if (stats.rtt_ms != -1) {
// To avoid failures caused by rounding or minor ntp clock adjustments,
diff --git a/third_party/libwebrtc/video/frame_cadence_adapter.cc b/third_party/libwebrtc/video/frame_cadence_adapter.cc
index b09986847b..2c4acdd6c2 100644
--- a/third_party/libwebrtc/video/frame_cadence_adapter.cc
+++ b/third_party/libwebrtc/video/frame_cadence_adapter.cc
@@ -50,7 +50,7 @@ class AdapterMode {
// Called on the worker thread for every frame that enters.
virtual void OnFrame(Timestamp post_time,
- int frames_scheduled_for_processing,
+ bool queue_overload,
const VideoFrame& frame) = 0;
// Returns the currently estimated input framerate.
@@ -71,10 +71,10 @@ class PassthroughAdapterMode : public AdapterMode {
// Adapter overrides.
void OnFrame(Timestamp post_time,
- int frames_scheduled_for_processing,
+ bool queue_overload,
const VideoFrame& frame) override {
RTC_DCHECK_RUN_ON(&sequence_checker_);
- callback_->OnFrame(post_time, frames_scheduled_for_processing, frame);
+ callback_->OnFrame(post_time, queue_overload, frame);
}
absl::optional<uint32_t> GetInputFrameRateFps() override {
@@ -119,7 +119,7 @@ class ZeroHertzAdapterMode : public AdapterMode {
// Adapter overrides.
void OnFrame(Timestamp post_time,
- int frames_scheduled_for_processing,
+ bool queue_overload,
const VideoFrame& frame) override;
absl::optional<uint32_t> GetInputFrameRateFps() override;
void UpdateFrameRate() override {}
@@ -131,6 +131,10 @@ class ZeroHertzAdapterMode : public AdapterMode {
// Callback::RequestRefreshFrame.
void ProcessKeyFrameRequest();
+ // Updates the restrictions of max frame rate for the video source.
+ // Always called during construction using latest `restricted_frame_delay_`.
+ void UpdateVideoSourceRestrictions(absl::optional<double> max_frame_rate);
+
private:
// The tracking state of each spatial layer. Used for determining when to
// stop repeating frames.
@@ -171,20 +175,22 @@ class ZeroHertzAdapterMode : public AdapterMode {
// after this call.
void ResetQualityConvergenceInfo() RTC_RUN_ON(sequence_checker_);
// Processes incoming frames on a delayed cadence.
- void ProcessOnDelayedCadence() RTC_RUN_ON(sequence_checker_);
- // Schedules a later repeat with delay depending on state of layer trackers.
+ void ProcessOnDelayedCadence(Timestamp post_time)
+ RTC_RUN_ON(sequence_checker_);
+ // Schedules a later repeat with delay depending on state of layer trackers
+ // and if UpdateVideoSourceRestrictions has been called or not.
// If true is passed in `idle_repeat`, the repeat is going to be
- // kZeroHertzIdleRepeatRatePeriod. Otherwise it'll be the value of
- // `frame_delay`.
+ // kZeroHertzIdleRepeatRatePeriod. Otherwise it'll be the maximum value of
+ // `frame_delay` or `restricted_frame_delay_` if it has been set.
void ScheduleRepeat(int frame_id, bool idle_repeat)
RTC_RUN_ON(sequence_checker_);
- // Repeats a frame in the abscence of incoming frames. Slows down when quality
+ // Repeats a frame in the absence of incoming frames. Slows down when quality
// convergence is attained, and stops the cadence terminally when new frames
// have arrived.
void ProcessRepeatedFrameOnDelayedCadence(int frame_id)
RTC_RUN_ON(sequence_checker_);
// Sends a frame, updating the timestamp to the current time.
- void SendFrameNow(const VideoFrame& frame) const
+ void SendFrameNow(Timestamp post_time, const VideoFrame& frame) const
RTC_RUN_ON(sequence_checker_);
// Returns the repeat duration depending on if it's an idle repeat or not.
TimeDelta RepeatDuration(bool idle_repeat) const
@@ -220,6 +226,10 @@ class ZeroHertzAdapterMode : public AdapterMode {
// they can be dropped in various places in the capture pipeline.
RepeatingTaskHandle refresh_frame_requester_
RTC_GUARDED_BY(sequence_checker_);
+ // Can be set by UpdateVideoSourceRestrictions when the video source restricts
+ // the max frame rate.
+ absl::optional<TimeDelta> restricted_frame_delay_
+ RTC_GUARDED_BY(sequence_checker_);
ScopedTaskSafety safety_;
};
@@ -240,6 +250,8 @@ class FrameCadenceAdapterImpl : public FrameCadenceAdapterInterface {
void UpdateLayerQualityConvergence(size_t spatial_index,
bool quality_converged) override;
void UpdateLayerStatus(size_t spatial_index, bool enabled) override;
+ void UpdateVideoSourceRestrictions(
+ absl::optional<double> max_frame_rate) override;
void ProcessKeyFrameRequest() override;
// VideoFrameSink overrides.
@@ -249,9 +261,9 @@ class FrameCadenceAdapterImpl : public FrameCadenceAdapterInterface {
const VideoTrackSourceConstraints& constraints) override;
private:
- // Called from OnFrame in zero-hertz mode.
+ // Called from OnFrame in both pass-through and zero-hertz mode.
void OnFrameOnMainQueue(Timestamp post_time,
- int frames_scheduled_for_processing,
+ bool queue_overload,
const VideoFrame& frame) RTC_RUN_ON(queue_);
// Returns true under all of the following conditions:
@@ -276,6 +288,7 @@ class FrameCadenceAdapterImpl : public FrameCadenceAdapterInterface {
absl::optional<ZeroHertzAdapterMode> zero_hertz_adapter_;
// If set, zero-hertz mode has been enabled.
absl::optional<ZeroHertzModeParams> zero_hertz_params_;
+ std::atomic<bool> zero_hertz_adapter_is_active_{false};
// Cache for the current adapter mode.
AdapterMode* current_adapter_mode_ = nullptr;
@@ -290,10 +303,14 @@ class FrameCadenceAdapterImpl : public FrameCadenceAdapterInterface {
absl::optional<VideoTrackSourceConstraints> source_constraints_
RTC_GUARDED_BY(queue_);
+ // Stores the latest restriction in max frame rate set by
+ // UpdateVideoSourceRestrictions. Ensures that a previously set restriction
+ // can be maintained during reconstructions of the adapter.
+ absl::optional<double> restricted_max_frame_rate_ RTC_GUARDED_BY(queue_);
+
// Race checker for incoming frames. This is the network thread in chromium,
// but may vary from test contexts.
rtc::RaceChecker incoming_frame_race_checker_;
- bool has_reported_screenshare_frame_rate_umas_ RTC_GUARDED_BY(queue_) = false;
// Number of frames that are currently scheduled for processing on the
// `queue_`.
@@ -355,7 +372,7 @@ void ZeroHertzAdapterMode::UpdateLayerStatus(size_t spatial_index,
}
void ZeroHertzAdapterMode::OnFrame(Timestamp post_time,
- int frames_scheduled_for_processing,
+ bool queue_overload,
const VideoFrame& frame) {
RTC_DCHECK_RUN_ON(&sequence_checker_);
TRACE_EVENT0("webrtc", "ZeroHertzAdapterMode::OnFrame");
@@ -382,7 +399,7 @@ void ZeroHertzAdapterMode::OnFrame(Timestamp post_time,
frame_id);
queue_->PostDelayedHighPrecisionTask(
SafeTask(safety_.flag(),
- [this, frame_id, frame] {
+ [this, post_time, frame_id, frame] {
RTC_UNUSED(frame_id);
RTC_DCHECK_RUN_ON(&sequence_checker_);
TRACE_EVENT_ASYNC_END0(TRACE_DISABLED_BY_DEFAULT("webrtc"),
@@ -390,7 +407,7 @@ void ZeroHertzAdapterMode::OnFrame(Timestamp post_time,
TRACE_EVENT_ASYNC_END0(TRACE_DISABLED_BY_DEFAULT("webrtc"),
"OnFrameToEncode",
frame.video_frame_buffer().get());
- ProcessOnDelayedCadence();
+ ProcessOnDelayedCadence(post_time);
}),
std::max(frame_delay_ - time_spent_since_post, TimeDelta::Zero()));
}
@@ -411,6 +428,20 @@ absl::optional<uint32_t> ZeroHertzAdapterMode::GetInputFrameRateFps() {
return max_fps_;
}
+void ZeroHertzAdapterMode::UpdateVideoSourceRestrictions(
+ absl::optional<double> max_frame_rate) {
+ RTC_DCHECK_RUN_ON(&sequence_checker_);
+ TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("webrtc"), __func__,
+ "max_frame_rate", max_frame_rate.value_or(-1));
+ if (max_frame_rate.value_or(0) > 0) {
+ // Set new, validated (> 0) and restricted frame rate.
+ restricted_frame_delay_ = TimeDelta::Seconds(1) / *max_frame_rate;
+ } else {
+ // Source reports that the frame rate is now unrestricted.
+ restricted_frame_delay_ = absl::nullopt;
+ }
+}
+
void ZeroHertzAdapterMode::ProcessKeyFrameRequest() {
RTC_DCHECK_RUN_ON(&sequence_checker_);
TRACE_EVENT_INSTANT0("webrtc", __func__);
@@ -475,13 +506,13 @@ void ZeroHertzAdapterMode::ResetQualityConvergenceInfo() {
}
}
-void ZeroHertzAdapterMode::ProcessOnDelayedCadence() {
+void ZeroHertzAdapterMode::ProcessOnDelayedCadence(Timestamp post_time) {
RTC_DCHECK_RUN_ON(&sequence_checker_);
RTC_DCHECK(!queued_frames_.empty());
TRACE_EVENT0("webrtc", __func__);
// Avoid sending the front frame for encoding (which could take a long time)
- // until we schedule a repeate.
+ // until we schedule a repeat.
VideoFrame front_frame = queued_frames_.front();
// If there were two or more frames stored, we do not have to schedule repeats
@@ -494,7 +525,7 @@ void ZeroHertzAdapterMode::ProcessOnDelayedCadence() {
// arrive.
ScheduleRepeat(current_frame_id_, HasQualityConverged());
}
- SendFrameNow(front_frame);
+ SendFrameNow(post_time, front_frame);
}
void ZeroHertzAdapterMode::ScheduleRepeat(int frame_id, bool idle_repeat) {
@@ -551,23 +582,36 @@ void ZeroHertzAdapterMode::ProcessRepeatedFrameOnDelayedCadence(int frame_id) {
// Schedule another repeat before sending the frame off which could take time.
ScheduleRepeat(frame_id, HasQualityConverged());
- SendFrameNow(frame);
+ // Mark `post_time` with 0 to signal that this is a repeated frame.
+ SendFrameNow(Timestamp::Zero(), frame);
}
-void ZeroHertzAdapterMode::SendFrameNow(const VideoFrame& frame) const {
+void ZeroHertzAdapterMode::SendFrameNow(Timestamp post_time,
+ const VideoFrame& frame) const {
RTC_DCHECK_RUN_ON(&sequence_checker_);
TRACE_EVENT0("webrtc", __func__);
- // TODO(crbug.com/1255737): figure out if frames_scheduled_for_processing
- // makes sense to compute in this implementation.
- callback_->OnFrame(/*post_time=*/clock_->CurrentTime(),
- /*frames_scheduled_for_processing=*/1, frame);
+ Timestamp now = clock_->CurrentTime();
+ // Exclude repeated frames which are marked with zero as post time.
+ if (post_time != Timestamp::Zero()) {
+ TimeDelta delay = (now - post_time);
+ RTC_HISTOGRAM_COUNTS_10000("WebRTC.Screenshare.ZeroHz.DelayMs", delay.ms());
+ }
+ // TODO(crbug.com/1255737): ensure queue_overload is computed from current
+ // conditions on the encoder queue.
+ callback_->OnFrame(/*post_time=*/now,
+ /*queue_overload=*/false, frame);
}
TimeDelta ZeroHertzAdapterMode::RepeatDuration(bool idle_repeat) const {
RTC_DCHECK_RUN_ON(&sequence_checker_);
+ // By default use `frame_delay_` in non-idle repeat mode but use the
+ // restricted frame delay instead if it is set in
+ // UpdateVideoSourceRestrictions.
+ TimeDelta frame_delay =
+ std::max(frame_delay_, restricted_frame_delay_.value_or(frame_delay_));
return idle_repeat
? FrameCadenceAdapterInterface::kZeroHertzIdleRepeatRatePeriod
- : frame_delay_;
+ : frame_delay;
}
void ZeroHertzAdapterMode::MaybeStartRefreshFrameRequester() {
@@ -609,8 +653,6 @@ void FrameCadenceAdapterImpl::SetZeroHertzModeEnabled(
absl::optional<ZeroHertzModeParams> params) {
RTC_DCHECK_RUN_ON(queue_);
bool was_zero_hertz_enabled = zero_hertz_params_.has_value();
- if (params.has_value() && !was_zero_hertz_enabled)
- has_reported_screenshare_frame_rate_umas_ = false;
zero_hertz_params_ = params;
MaybeReconfigureAdapters(was_zero_hertz_enabled);
}
@@ -642,6 +684,17 @@ void FrameCadenceAdapterImpl::UpdateLayerStatus(size_t spatial_index,
zero_hertz_adapter_->UpdateLayerStatus(spatial_index, enabled);
}
+void FrameCadenceAdapterImpl::UpdateVideoSourceRestrictions(
+ absl::optional<double> max_frame_rate) {
+ RTC_DCHECK_RUN_ON(queue_);
+ // Store the restriction to ensure that it can be reapplied in possible
+ // future adapter creations on configuration changes.
+ restricted_max_frame_rate_ = max_frame_rate;
+ if (zero_hertz_adapter_) {
+ zero_hertz_adapter_->UpdateVideoSourceRestrictions(max_frame_rate);
+ }
+}
+
void FrameCadenceAdapterImpl::ProcessKeyFrameRequest() {
RTC_DCHECK_RUN_ON(queue_);
if (zero_hertz_adapter_)
@@ -657,14 +710,21 @@ void FrameCadenceAdapterImpl::OnFrame(const VideoFrame& frame) {
// Local time in webrtc time base.
Timestamp post_time = clock_->CurrentTime();
frames_scheduled_for_processing_.fetch_add(1, std::memory_order_relaxed);
- TRACE_EVENT_ASYNC_BEGIN0(TRACE_DISABLED_BY_DEFAULT("webrtc"),
- "OnFrameToEncode", frame.video_frame_buffer().get());
- TRACE_EVENT_ASYNC_BEGIN0(TRACE_DISABLED_BY_DEFAULT("webrtc"),
- "OnFrameToQueue", frame.video_frame_buffer().get());
+ if (zero_hertz_adapter_is_active_.load(std::memory_order_relaxed)) {
+ TRACE_EVENT_ASYNC_BEGIN0(TRACE_DISABLED_BY_DEFAULT("webrtc"),
+ "OnFrameToEncode",
+ frame.video_frame_buffer().get());
+ TRACE_EVENT_ASYNC_BEGIN0(TRACE_DISABLED_BY_DEFAULT("webrtc"),
+ "OnFrameToQueue",
+ frame.video_frame_buffer().get());
+ }
queue_->PostTask(SafeTask(safety_.flag(), [this, post_time, frame] {
RTC_DCHECK_RUN_ON(queue_);
- TRACE_EVENT_ASYNC_END0(TRACE_DISABLED_BY_DEFAULT("webrtc"),
- "OnFrameToQueue", frame.video_frame_buffer().get());
+ if (zero_hertz_adapter_is_active_.load(std::memory_order_relaxed)) {
+ TRACE_EVENT_ASYNC_END0(TRACE_DISABLED_BY_DEFAULT("webrtc"),
+ "OnFrameToQueue",
+ frame.video_frame_buffer().get());
+ }
if (zero_hertz_adapter_created_timestamp_.has_value()) {
TimeDelta time_until_first_frame =
clock_->CurrentTime() - *zero_hertz_adapter_created_timestamp_;
@@ -677,7 +737,7 @@ void FrameCadenceAdapterImpl::OnFrame(const VideoFrame& frame) {
const int frames_scheduled_for_processing =
frames_scheduled_for_processing_.fetch_sub(1,
std::memory_order_relaxed);
- OnFrameOnMainQueue(post_time, frames_scheduled_for_processing,
+ OnFrameOnMainQueue(post_time, frames_scheduled_for_processing > 1,
std::move(frame));
}));
}
@@ -705,13 +765,11 @@ void FrameCadenceAdapterImpl::OnConstraintsChanged(
}));
}
-void FrameCadenceAdapterImpl::OnFrameOnMainQueue(
- Timestamp post_time,
- int frames_scheduled_for_processing,
- const VideoFrame& frame) {
+void FrameCadenceAdapterImpl::OnFrameOnMainQueue(Timestamp post_time,
+ bool queue_overload,
+ const VideoFrame& frame) {
RTC_DCHECK_RUN_ON(queue_);
- current_adapter_mode_->OnFrame(post_time, frames_scheduled_for_processing,
- frame);
+ current_adapter_mode_->OnFrame(post_time, queue_overload, frame);
}
bool FrameCadenceAdapterImpl::IsZeroHertzScreenshareEnabled() const {
@@ -727,17 +785,25 @@ void FrameCadenceAdapterImpl::MaybeReconfigureAdapters(
RTC_DCHECK_RUN_ON(queue_);
bool is_zero_hertz_enabled = IsZeroHertzScreenshareEnabled();
if (is_zero_hertz_enabled) {
- if (!was_zero_hertz_enabled) {
+ bool max_fps_has_changed = GetInputFrameRateFps().value_or(-1) !=
+ source_constraints_->max_fps.value_or(-1);
+ if (!was_zero_hertz_enabled || max_fps_has_changed) {
+ RTC_LOG(LS_INFO) << "Zero hertz mode enabled (max_fps="
+ << source_constraints_->max_fps.value() << ")";
zero_hertz_adapter_.emplace(queue_, clock_, callback_,
source_constraints_->max_fps.value());
- RTC_LOG(LS_INFO) << "Zero hertz mode activated.";
+ zero_hertz_adapter_->UpdateVideoSourceRestrictions(
+ restricted_max_frame_rate_);
zero_hertz_adapter_created_timestamp_ = clock_->CurrentTime();
}
zero_hertz_adapter_->ReconfigureParameters(zero_hertz_params_.value());
current_adapter_mode_ = &zero_hertz_adapter_.value();
} else {
- if (was_zero_hertz_enabled)
+ if (was_zero_hertz_enabled) {
zero_hertz_adapter_ = absl::nullopt;
+ zero_hertz_adapter_is_active_.store(false, std::memory_order_relaxed);
+ RTC_LOG(LS_INFO) << "Zero hertz mode disabled.";
+ }
current_adapter_mode_ = &passthrough_adapter_.value();
}
}
diff --git a/third_party/libwebrtc/video/frame_cadence_adapter.h b/third_party/libwebrtc/video/frame_cadence_adapter.h
index d0eab7e770..2b62bb26cd 100644
--- a/third_party/libwebrtc/video/frame_cadence_adapter.h
+++ b/third_party/libwebrtc/video/frame_cadence_adapter.h
@@ -60,14 +60,11 @@ class FrameCadenceAdapterInterface
// The |post_time| parameter indicates the current time sampled when
// FrameCadenceAdapterInterface::OnFrame was called.
//
- // |frames_scheduled_for_processing| indicates how many frames that have
- // been scheduled for processing. During sequential conditions where
- // FrameCadenceAdapterInterface::OnFrame is invoked and subsequently ending
- // up in this callback, this value will read 1. Otherwise if the
- // |queue| gets stalled for some reason, the value will increase
- // beyond 1.
+ // |queue_overload| is true if the frame cadence adapter notices it's
+ // not able to deliver the incoming |frame| to the |queue| in the expected
+ // time.
virtual void OnFrame(Timestamp post_time,
- int frames_scheduled_for_processing,
+ bool queue_overload,
const VideoFrame& frame) = 0;
// Called when the source has discarded a frame.
@@ -112,6 +109,12 @@ class FrameCadenceAdapterInterface
// Updates spatial layer enabled status.
virtual void UpdateLayerStatus(size_t spatial_index, bool enabled) = 0;
+ // Updates the restrictions of max frame rate for the video source.
+ // The new `max_frame_rate` will only affect the cadence of Callback::OnFrame
+ // for non-idle (non converged) repeated frames.
+ virtual void UpdateVideoSourceRestrictions(
+ absl::optional<double> max_frame_rate) = 0;
+
// Conditionally requests a refresh frame via
// Callback::RequestRefreshFrame.
virtual void ProcessKeyFrameRequest() = 0;
diff --git a/third_party/libwebrtc/video/frame_cadence_adapter_gn/moz.build b/third_party/libwebrtc/video/frame_cadence_adapter_gn/moz.build
index 51ec86b2cf..a860d550ad 100644
--- a/third_party/libwebrtc/video/frame_cadence_adapter_gn/moz.build
+++ b/third_party/libwebrtc/video/frame_cadence_adapter_gn/moz.build
@@ -199,7 +199,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -209,10 +208,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/video/frame_cadence_adapter_unittest.cc b/third_party/libwebrtc/video/frame_cadence_adapter_unittest.cc
index 052b0a6c61..0fef2400f0 100644
--- a/third_party/libwebrtc/video/frame_cadence_adapter_unittest.cc
+++ b/third_party/libwebrtc/video/frame_cadence_adapter_unittest.cc
@@ -70,7 +70,7 @@ std::unique_ptr<FrameCadenceAdapterInterface> CreateAdapter(
class MockCallback : public FrameCadenceAdapterInterface::Callback {
public:
- MOCK_METHOD(void, OnFrame, (Timestamp, int, const VideoFrame&), (override));
+ MOCK_METHOD(void, OnFrame, (Timestamp, bool, const VideoFrame&), (override));
MOCK_METHOD(void, OnDiscardedFrame, (), (override));
MOCK_METHOD(void, RequestRefreshFrame, (), (override));
};
@@ -115,13 +115,13 @@ TEST(FrameCadenceAdapterTest, CountsOutstandingFramesToProcess) {
MockCallback callback;
auto adapter = CreateAdapter(no_field_trials, time_controller.GetClock());
adapter->Initialize(&callback);
- EXPECT_CALL(callback, OnFrame(_, 2, _)).Times(1);
- EXPECT_CALL(callback, OnFrame(_, 1, _)).Times(1);
+ EXPECT_CALL(callback, OnFrame(_, true, _)).Times(1);
+ EXPECT_CALL(callback, OnFrame(_, false, _)).Times(1);
auto frame = CreateFrame();
adapter->OnFrame(frame);
adapter->OnFrame(frame);
time_controller.AdvanceTime(TimeDelta::Zero());
- EXPECT_CALL(callback, OnFrame(_, 1, _)).Times(1);
+ EXPECT_CALL(callback, OnFrame(_, false, _)).Times(1);
adapter->OnFrame(frame);
time_controller.AdvanceTime(TimeDelta::Zero());
}
@@ -184,6 +184,29 @@ TEST(FrameCadenceAdapterTest, FrameRateFollowsMaxFpsWhenZeroHertzActivated) {
}
}
+TEST(FrameCadenceAdapterTest, ZeroHertzAdapterSupportsMaxFpsChange) {
+ ZeroHertzFieldTrialEnabler enabler;
+ GlobalSimulatedTimeController time_controller(Timestamp::Zero());
+ auto adapter = CreateAdapter(enabler, time_controller.GetClock());
+ MockCallback callback;
+ adapter->Initialize(&callback);
+ adapter->SetZeroHertzModeEnabled(
+ FrameCadenceAdapterInterface::ZeroHertzModeParams{});
+ adapter->OnConstraintsChanged(VideoTrackSourceConstraints{0, 1});
+ time_controller.AdvanceTime(TimeDelta::Zero());
+ EXPECT_EQ(adapter->GetInputFrameRateFps(), 1u);
+ adapter->OnFrame(CreateFrame());
+ time_controller.AdvanceTime(TimeDelta::Seconds(1));
+ adapter->OnConstraintsChanged(VideoTrackSourceConstraints{0, 2});
+ time_controller.AdvanceTime(TimeDelta::Zero());
+ EXPECT_EQ(adapter->GetInputFrameRateFps(), 2u);
+ adapter->OnFrame(CreateFrame());
+ // Ensure that the max_fps has been changed from 1 to 2 fps even if it was
+ // changed while zero hertz was already active.
+ EXPECT_CALL(callback, OnFrame);
+ time_controller.AdvanceTime(TimeDelta::Millis(500));
+}
+
TEST(FrameCadenceAdapterTest,
FrameRateFollowsRateStatisticsAfterZeroHertzDeactivated) {
ZeroHertzFieldTrialEnabler enabler;
@@ -230,7 +253,7 @@ TEST(FrameCadenceAdapterTest, ForwardsFramesDelayed) {
EXPECT_CALL(callback, OnFrame).Times(0);
adapter->OnFrame(frame);
EXPECT_CALL(callback, OnFrame)
- .WillOnce(Invoke([&](Timestamp post_time, int,
+ .WillOnce(Invoke([&](Timestamp post_time, bool,
const VideoFrame& frame) {
EXPECT_EQ(post_time, time_controller.GetClock()->CurrentTime());
EXPECT_EQ(frame.timestamp_us(),
@@ -309,7 +332,7 @@ TEST(FrameCadenceAdapterTest, RepeatsFramesDelayed) {
adapter->OnFrame(frame);
EXPECT_CALL(callback, OnFrame)
- .WillOnce(Invoke([&](Timestamp post_time, int, const VideoFrame& frame) {
+ .WillOnce(Invoke([&](Timestamp post_time, bool, const VideoFrame& frame) {
EXPECT_EQ(post_time, time_controller.GetClock()->CurrentTime());
EXPECT_EQ(frame.timestamp_us(), original_timestamp_us);
EXPECT_EQ(frame.ntp_time_ms(), original_ntp_time.ToMs());
@@ -318,7 +341,7 @@ TEST(FrameCadenceAdapterTest, RepeatsFramesDelayed) {
Mock::VerifyAndClearExpectations(&callback);
EXPECT_CALL(callback, OnFrame)
- .WillOnce(Invoke([&](Timestamp post_time, int, const VideoFrame& frame) {
+ .WillOnce(Invoke([&](Timestamp post_time, bool, const VideoFrame& frame) {
EXPECT_EQ(post_time, time_controller.GetClock()->CurrentTime());
EXPECT_EQ(frame.timestamp_us(),
original_timestamp_us + rtc::kNumMicrosecsPerSec);
@@ -329,7 +352,7 @@ TEST(FrameCadenceAdapterTest, RepeatsFramesDelayed) {
Mock::VerifyAndClearExpectations(&callback);
EXPECT_CALL(callback, OnFrame)
- .WillOnce(Invoke([&](Timestamp post_time, int, const VideoFrame& frame) {
+ .WillOnce(Invoke([&](Timestamp post_time, bool, const VideoFrame& frame) {
EXPECT_EQ(post_time, time_controller.GetClock()->CurrentTime());
EXPECT_EQ(frame.timestamp_us(),
original_timestamp_us + 2 * rtc::kNumMicrosecsPerSec);
@@ -359,7 +382,7 @@ TEST(FrameCadenceAdapterTest,
// Send one frame, expect a repeat.
adapter->OnFrame(CreateFrame());
EXPECT_CALL(callback, OnFrame)
- .WillOnce(Invoke([&](Timestamp post_time, int, const VideoFrame& frame) {
+ .WillOnce(Invoke([&](Timestamp post_time, bool, const VideoFrame& frame) {
EXPECT_EQ(post_time, time_controller.GetClock()->CurrentTime());
EXPECT_EQ(frame.timestamp_us(), 0);
EXPECT_EQ(frame.ntp_time_ms(), 0);
@@ -367,7 +390,7 @@ TEST(FrameCadenceAdapterTest,
time_controller.AdvanceTime(TimeDelta::Seconds(1));
Mock::VerifyAndClearExpectations(&callback);
EXPECT_CALL(callback, OnFrame)
- .WillOnce(Invoke([&](Timestamp post_time, int, const VideoFrame& frame) {
+ .WillOnce(Invoke([&](Timestamp post_time, bool, const VideoFrame& frame) {
EXPECT_EQ(post_time, time_controller.GetClock()->CurrentTime());
EXPECT_EQ(frame.timestamp_us(), 0);
EXPECT_EQ(frame.ntp_time_ms(), 0);
@@ -399,7 +422,7 @@ TEST(FrameCadenceAdapterTest, StopsRepeatingFramesDelayed) {
// Send the new frame at 2.5s, which should appear after 3.5s.
adapter->OnFrame(CreateFrameWithTimestamps(&time_controller));
EXPECT_CALL(callback, OnFrame)
- .WillOnce(Invoke([&](Timestamp, int, const VideoFrame& frame) {
+ .WillOnce(Invoke([&](Timestamp, bool, const VideoFrame& frame) {
EXPECT_EQ(frame.timestamp_us(), 5 * rtc::kNumMicrosecsPerSec / 2);
EXPECT_EQ(frame.ntp_time_ms(),
original_ntp_time.ToMs() + 5u * rtc::kNumMillisecsPerSec / 2);
@@ -708,6 +731,8 @@ class ZeroHertzLayerQualityConvergenceTest : public ::testing::Test {
static constexpr TimeDelta kMinFrameDelay = TimeDelta::Millis(100);
static constexpr TimeDelta kIdleFrameDelay =
FrameCadenceAdapterInterface::kZeroHertzIdleRepeatRatePeriod;
+ // Restricts non-idle repeat rate to 5 fps (default is 10 fps);
+ static constexpr int kRestrictedMaxFps = 5;
ZeroHertzLayerQualityConvergenceTest() {
adapter_->Initialize(&callback_);
@@ -725,7 +750,7 @@ class ZeroHertzLayerQualityConvergenceTest : public ::testing::Test {
std::initializer_list<TimeDelta> list) {
Timestamp origin = time_controller_.GetClock()->CurrentTime();
for (auto delay : list) {
- EXPECT_CALL(callback_, OnFrame(origin + delay, _, _));
+ EXPECT_CALL(callback_, OnFrame(origin + delay, false, _));
time_controller_.AdvanceTime(origin + delay -
time_controller_.GetClock()->CurrentTime());
}
@@ -810,6 +835,100 @@ TEST_F(ZeroHertzLayerQualityConvergenceTest,
});
}
+TEST_F(ZeroHertzLayerQualityConvergenceTest,
+ UnconvergedRepeatRateAdaptsDownWhenRestricted) {
+ PassFrame();
+ ScheduleDelayed(1.5 * kMinFrameDelay, [&] {
+ adapter_->UpdateVideoSourceRestrictions(kRestrictedMaxFps);
+ });
+ ExpectFrameEntriesAtDelaysFromNow({
+ 1 * kMinFrameDelay, // Original frame emitted at non-restricted rate.
+
+ // 1.5 * kMinFrameDelay: restricts max fps to 5 fps which should result
+ // in a new non-idle repeat delay of 2 * kMinFrameDelay.
+ 2 * kMinFrameDelay, // Unconverged repeat at non-restricted rate.
+ 4 * kMinFrameDelay, // Unconverged repeats at restricted rate. This
+ // happens 2 * kMinFrameDelay after the last frame.
+ 6 * kMinFrameDelay, // ...
+ });
+}
+
+TEST_F(ZeroHertzLayerQualityConvergenceTest,
+ UnconvergedRepeatRateAdaptsUpWhenGoingFromRestrictedToUnrestricted) {
+ PassFrame();
+ ScheduleDelayed(1.5 * kMinFrameDelay, [&] {
+ adapter_->UpdateVideoSourceRestrictions(kRestrictedMaxFps);
+ });
+ ScheduleDelayed(5.5 * kMinFrameDelay, [&] {
+ adapter_->UpdateVideoSourceRestrictions(absl::nullopt);
+ });
+ ExpectFrameEntriesAtDelaysFromNow({
+ 1 * kMinFrameDelay, // Original frame emitted at non-restricted rate.
+
+ // 1.5 * kMinFrameDelay: restricts max fps to 5 fps which should result
+ // in a new non-idle repeat delay of 2 * kMinFrameDelay.
+ 2 * kMinFrameDelay, // Unconverged repeat at non-restricted rate.
+ 4 * kMinFrameDelay, // Unconverged repeat at restricted rate.
+
+ // 5.5 * kMinFrameDelay: removes frame-rate restriction and we should
+ // then go back to 10 fps as unconverged repeat rate.
+ 6 * kMinFrameDelay, // Last unconverged repeat at restricted rate.
+ 7 * kMinFrameDelay, // Back to unconverged repeat at non-restricted rate.
+ 8 * kMinFrameDelay, // We are now unrestricted.
+ 9 * kMinFrameDelay, // ...
+ });
+}
+
+TEST_F(ZeroHertzLayerQualityConvergenceTest,
+ UnconvergedRepeatRateMaintainsRestrictionOnReconfigureToHigherMaxFps) {
+ PassFrame();
+ ScheduleDelayed(1.5 * kMinFrameDelay, [&] {
+ adapter_->UpdateVideoSourceRestrictions(kRestrictedMaxFps);
+ });
+ ScheduleDelayed(2.5 * kMinFrameDelay, [&] {
+ adapter_->OnConstraintsChanged(VideoTrackSourceConstraints{
+ /*min_fps=*/0, /*max_fps=*/2 * TimeDelta::Seconds(1) / kMinFrameDelay});
+ });
+ ScheduleDelayed(3 * kMinFrameDelay, [&] { PassFrame(); });
+ ScheduleDelayed(8 * kMinFrameDelay, [&] {
+ adapter_->OnConstraintsChanged(VideoTrackSourceConstraints{
+ /*min_fps=*/0,
+ /*max_fps=*/0.2 * TimeDelta::Seconds(1) / kMinFrameDelay});
+ });
+ ScheduleDelayed(9 * kMinFrameDelay, [&] { PassFrame(); });
+ ExpectFrameEntriesAtDelaysFromNow({
+ 1 * kMinFrameDelay, // Original frame emitted at non-restricted rate.
+
+ // 1.5 * kMinFrameDelay: restricts max fps to 5 fps which should result
+ // in a new non-idle repeat delay of 2 * kMinFrameDelay.
+ 2 * kMinFrameDelay, // Unconverged repeat at non-restricted rate.
+
+ // 2.5 * kMinFrameDelay: new constraint asks for max rate of 20 fps.
+ // The 0Hz adapter is reconstructed for 20 fps but inherits the current
+ // restriction for rate of non-converged frames of 5 fps.
+
+ // A new frame is passed at 3 * kMinFrameDelay. The previous repeat
+ // cadence was stopped by the change in constraints.
+ 3.5 * kMinFrameDelay, // Original frame emitted at non-restricted 20 fps.
+ // The delay is 0.5 * kMinFrameDelay.
+ 5.5 * kMinFrameDelay, // Unconverged repeat at restricted rate.
+ // The delay is 2 * kMinFrameDelay when restricted.
+ 7.5 * kMinFrameDelay, // ...
+
+ // 8 * kMinFrameDelay: new constraint asks for max rate of 2 fps.
+ // The 0Hz adapter is reconstructed for 2 fps and will therefore not obey
+ // the current restriction for rate of non-converged frames of 5 fps
+ // since the new max rate is lower.
+
+ // A new frame is passed at 9 * kMinFrameDelay. The previous repeat
+ // cadence was stopped by the change in constraints.
+ 14 * kMinFrameDelay, // Original frame emitted at non-restricted 2 fps.
+ // The delay is 5 * kMinFrameDelay.
+ 19 * kMinFrameDelay, // Unconverged repeat at non-restricted rate.
+ 24 * kMinFrameDelay, // ...
+ });
+}
+
class FrameCadenceAdapterMetricsTest : public ::testing::Test {
public:
FrameCadenceAdapterMetricsTest() : time_controller_(Timestamp::Millis(1)) {
@@ -869,7 +988,7 @@ TEST(FrameCadenceAdapterRealTimeTest, TimestampsDoNotDrift) {
constexpr int kSleepMs = rtc::kNumMillisecsPerSec / 2;
EXPECT_CALL(callback, OnFrame)
.WillRepeatedly(
- Invoke([&](Timestamp, int, const VideoFrame& incoming_frame) {
+ Invoke([&](Timestamp, bool, const VideoFrame& incoming_frame) {
++frame_counter;
// Avoid the first OnFrame and sleep on the second.
if (frame_counter == 2) {
diff --git a/third_party/libwebrtc/video/frame_decode_scheduler_gn/moz.build b/third_party/libwebrtc/video/frame_decode_scheduler_gn/moz.build
index a53fde81f2..d3fb5a4710 100644
--- a/third_party/libwebrtc/video/frame_decode_scheduler_gn/moz.build
+++ b/third_party/libwebrtc/video/frame_decode_scheduler_gn/moz.build
@@ -191,16 +191,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/video/frame_decode_timing_gn/moz.build b/third_party/libwebrtc/video/frame_decode_timing_gn/moz.build
index ca09fac23b..9161f27d71 100644
--- a/third_party/libwebrtc/video/frame_decode_timing_gn/moz.build
+++ b/third_party/libwebrtc/video/frame_decode_timing_gn/moz.build
@@ -199,7 +199,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -209,10 +208,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/video/frame_dumping_decoder_gn/moz.build b/third_party/libwebrtc/video/frame_dumping_decoder_gn/moz.build
index 39e9878bd2..309c0b159e 100644
--- a/third_party/libwebrtc/video/frame_dumping_decoder_gn/moz.build
+++ b/third_party/libwebrtc/video/frame_dumping_decoder_gn/moz.build
@@ -200,7 +200,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -210,10 +209,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/video/frame_dumping_encoder_gn/moz.build b/third_party/libwebrtc/video/frame_dumping_encoder_gn/moz.build
index 615033eb1d..6719245825 100644
--- a/third_party/libwebrtc/video/frame_dumping_encoder_gn/moz.build
+++ b/third_party/libwebrtc/video/frame_dumping_encoder_gn/moz.build
@@ -200,7 +200,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -210,10 +209,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/video/render/incoming_video_stream_gn/moz.build b/third_party/libwebrtc/video/render/incoming_video_stream_gn/moz.build
index ad1d7fbfb3..36d087a36a 100644
--- a/third_party/libwebrtc/video/render/incoming_video_stream_gn/moz.build
+++ b/third_party/libwebrtc/video/render/incoming_video_stream_gn/moz.build
@@ -195,7 +195,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -205,10 +204,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/video/render/video_render_frames_gn/moz.build b/third_party/libwebrtc/video/render/video_render_frames_gn/moz.build
index d5de8cd178..b0b4923e34 100644
--- a/third_party/libwebrtc/video/render/video_render_frames_gn/moz.build
+++ b/third_party/libwebrtc/video/render/video_render_frames_gn/moz.build
@@ -195,7 +195,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -205,10 +204,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/video/send_statistics_proxy.cc b/third_party/libwebrtc/video/send_statistics_proxy.cc
index f8ba31beff..324a5d671d 100644
--- a/third_party/libwebrtc/video/send_statistics_proxy.cc
+++ b/third_party/libwebrtc/video/send_statistics_proxy.cc
@@ -658,6 +658,10 @@ void SendStatisticsProxy::UmaSamplesContainer::UpdateHistograms(
<< current_stats.frames_dropped_by_capturer << "\n";
RTC_HISTOGRAMS_COUNTS_1000(kIndex, uma_prefix_ + "DroppedFrames.Capturer",
current_stats.frames_dropped_by_capturer);
+ log_stream << uma_prefix_ << "DroppedFrames.BadTimestamp"
+ << current_stats.frames_dropped_by_bad_timestamp << "\n";
+ RTC_HISTOGRAMS_COUNTS_1000(kIndex, uma_prefix_ + "DroppedFrames.BadTimestamp",
+ current_stats.frames_dropped_by_bad_timestamp);
log_stream << uma_prefix_ << "DroppedFrames.EncoderQueue "
<< current_stats.frames_dropped_by_encoder_queue << "\n";
RTC_HISTOGRAMS_COUNTS_1000(kIndex, uma_prefix_ + "DroppedFrames.EncoderQueue",
@@ -1097,6 +1101,9 @@ void SendStatisticsProxy::OnFrameDropped(DropReason reason) {
case DropReason::kSource:
++stats_.frames_dropped_by_capturer;
break;
+ case DropReason::kBadTimestamp:
+ ++stats_.frames_dropped_by_bad_timestamp;
+ break;
case DropReason::kEncoderQueue:
++stats_.frames_dropped_by_encoder_queue;
break;
diff --git a/third_party/libwebrtc/video/task_queue_frame_decode_scheduler_gn/moz.build b/third_party/libwebrtc/video/task_queue_frame_decode_scheduler_gn/moz.build
index 2651f290bc..302f8420c9 100644
--- a/third_party/libwebrtc/video/task_queue_frame_decode_scheduler_gn/moz.build
+++ b/third_party/libwebrtc/video/task_queue_frame_decode_scheduler_gn/moz.build
@@ -199,7 +199,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -209,10 +208,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/video/unique_timestamp_counter_gn/moz.build b/third_party/libwebrtc/video/unique_timestamp_counter_gn/moz.build
index 24b3c5a30b..e38fabc4cc 100644
--- a/third_party/libwebrtc/video/unique_timestamp_counter_gn/moz.build
+++ b/third_party/libwebrtc/video/unique_timestamp_counter_gn/moz.build
@@ -184,7 +184,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -194,10 +193,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/video/video_gn/moz.build b/third_party/libwebrtc/video/video_gn/moz.build
index e8ac15dc40..5e3d75d621 100644
--- a/third_party/libwebrtc/video/video_gn/moz.build
+++ b/third_party/libwebrtc/video/video_gn/moz.build
@@ -220,7 +220,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -230,10 +229,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/video/video_quality_test.cc b/third_party/libwebrtc/video/video_quality_test.cc
index 971c129329..99b722322a 100644
--- a/third_party/libwebrtc/video/video_quality_test.cc
+++ b/third_party/libwebrtc/video/video_quality_test.cc
@@ -375,7 +375,6 @@ VideoQualityTest::VideoQualityTest(
std::unique_ptr<InjectionComponents> injection_components)
: clock_(Clock::GetRealTimeClock()),
task_queue_factory_(CreateDefaultTaskQueueFactory()),
- rtc_event_log_factory_(task_queue_factory_.get()),
video_decoder_factory_([this](const SdpVideoFormat& format) {
return this->CreateVideoDecoder(format);
}),
@@ -1221,10 +1220,10 @@ void VideoQualityTest::RunWithAnalyzer(const Params& params) {
}
if (!params.logging.rtc_event_log_name.empty()) {
- send_event_log_ = rtc_event_log_factory_.CreateRtcEventLog(
- RtcEventLog::EncodingType::NewFormat);
- recv_event_log_ = rtc_event_log_factory_.CreateRtcEventLog(
- RtcEventLog::EncodingType::NewFormat);
+ std::unique_ptr<RtcEventLog> send_event_log =
+ rtc_event_log_factory_.Create(env());
+ std::unique_ptr<RtcEventLog> recv_event_log =
+ rtc_event_log_factory_.Create(env());
std::unique_ptr<RtcEventLogOutputFile> send_output(
std::make_unique<RtcEventLogOutputFile>(
params.logging.rtc_event_log_name + "_send",
@@ -1234,19 +1233,18 @@ void VideoQualityTest::RunWithAnalyzer(const Params& params) {
params.logging.rtc_event_log_name + "_recv",
RtcEventLog::kUnlimitedOutput));
bool event_log_started =
- send_event_log_->StartLogging(std::move(send_output),
- RtcEventLog::kImmediateOutput) &&
- recv_event_log_->StartLogging(std::move(recv_output),
- RtcEventLog::kImmediateOutput);
+ send_event_log->StartLogging(std::move(send_output),
+ RtcEventLog::kImmediateOutput) &&
+ recv_event_log->StartLogging(std::move(recv_output),
+ RtcEventLog::kImmediateOutput);
RTC_DCHECK(event_log_started);
- } else {
- send_event_log_ = std::make_unique<RtcEventLogNull>();
- recv_event_log_ = std::make_unique<RtcEventLogNull>();
+ SetSendEventLog(std::move(send_event_log));
+ SetRecvEventLog(std::move(recv_event_log));
}
SendTask(task_queue(), [this, &params, &send_transport, &recv_transport]() {
- CallConfig send_call_config(send_event_log_.get());
- CallConfig recv_call_config(recv_event_log_.get());
+ CallConfig send_call_config = SendCallConfig();
+ CallConfig recv_call_config = RecvCallConfig();
send_call_config.bitrate_config = params.call.call_bitrate_config;
recv_call_config.bitrate_config = params.call.call_bitrate_config;
if (params_.audio.enabled)
@@ -1444,10 +1442,10 @@ void VideoQualityTest::RunWithRenderers(const Params& params) {
std::vector<std::unique_ptr<test::VideoRenderer>> loopback_renderers;
if (!params.logging.rtc_event_log_name.empty()) {
- send_event_log_ = rtc_event_log_factory_.CreateRtcEventLog(
- RtcEventLog::EncodingType::NewFormat);
- recv_event_log_ = rtc_event_log_factory_.CreateRtcEventLog(
- RtcEventLog::EncodingType::NewFormat);
+ std::unique_ptr<RtcEventLog> send_event_log =
+ rtc_event_log_factory_.Create(env());
+ std::unique_ptr<RtcEventLog> recv_event_log =
+ rtc_event_log_factory_.Create(env());
std::unique_ptr<RtcEventLogOutputFile> send_output(
std::make_unique<RtcEventLogOutputFile>(
params.logging.rtc_event_log_name + "_send",
@@ -1457,14 +1455,13 @@ void VideoQualityTest::RunWithRenderers(const Params& params) {
params.logging.rtc_event_log_name + "_recv",
RtcEventLog::kUnlimitedOutput));
bool event_log_started =
- send_event_log_->StartLogging(std::move(send_output),
- /*output_period_ms=*/5000) &&
- recv_event_log_->StartLogging(std::move(recv_output),
- /*output_period_ms=*/5000);
+ send_event_log->StartLogging(std::move(send_output),
+ /*output_period_ms=*/5000) &&
+ recv_event_log->StartLogging(std::move(recv_output),
+ /*output_period_ms=*/5000);
RTC_DCHECK(event_log_started);
- } else {
- send_event_log_ = std::make_unique<RtcEventLogNull>();
- recv_event_log_ = std::make_unique<RtcEventLogNull>();
+ SetSendEventLog(std::move(send_event_log));
+ SetRecvEventLog(std::move(recv_event_log));
}
SendTask(task_queue(), [&]() {
@@ -1473,9 +1470,9 @@ void VideoQualityTest::RunWithRenderers(const Params& params) {
// TODO(ivica): Remove bitrate_config and use the default CallConfig(), to
// match the full stack tests.
- CallConfig send_call_config(send_event_log_.get());
+ CallConfig send_call_config = SendCallConfig();
send_call_config.bitrate_config = params_.call.call_bitrate_config;
- CallConfig recv_call_config(recv_event_log_.get());
+ CallConfig recv_call_config = RecvCallConfig();
if (params_.audio.enabled)
InitializeAudioDevice(&send_call_config, &recv_call_config,
diff --git a/third_party/libwebrtc/video/video_receive_stream2.cc b/third_party/libwebrtc/video/video_receive_stream2.cc
index 85cc0aa0dc..33e2f39ced 100644
--- a/third_party/libwebrtc/video/video_receive_stream2.cc
+++ b/third_party/libwebrtc/video/video_receive_stream2.cc
@@ -568,10 +568,7 @@ VideoReceiveStreamInterface::Stats VideoReceiveStream2::GetStats() const {
rtp_receive_statistics_->GetStatistician(rtx_ssrc());
if (rtx_statistician) {
stats.total_bitrate_bps += rtx_statistician->BitrateReceived();
- // TODO(bugs.webrtc.org/15096): remove kill-switch after rollout.
- if (!call_->trials().IsDisabled("WebRTC-Stats-RtxReceiveStats")) {
- stats.rtx_rtp_stats = rtx_statistician->GetStats();
- }
+ stats.rtx_rtp_stats = rtx_statistician->GetStats();
}
}
@@ -917,6 +914,22 @@ int VideoReceiveStream2::DecodeAndMaybeDispatchEncodedFrame(
}
int decode_result = video_receiver_.Decode(frame_ptr);
+ if (decode_result < WEBRTC_VIDEO_CODEC_OK) {
+ // Asynchronous decoders may delay error reporting, potentially resulting in
+ // error reports reflecting issues that occurred several frames back.
+ RTC_LOG(LS_WARNING)
+ << "Failed to decode frame. Return code: " << decode_result
+ << ", SSRC: " << remote_ssrc()
+ << ", frame RTP timestamp: " << frame_ptr->RtpTimestamp()
+ << ", type: " << VideoFrameTypeToString(frame_ptr->FrameType())
+ << ", size: " << frame_ptr->size()
+ << ", width: " << frame_ptr->_encodedWidth
+ << ", height: " << frame_ptr->_encodedHeight
+ << ", spatial idx: " << frame_ptr->SpatialIndex().value_or(-1)
+ << ", temporal idx: " << frame_ptr->TemporalIndex().value_or(-1)
+ << ", id: " << frame_ptr->Id();
+ }
+
if (encoded_frame_output_enabled) {
absl::optional<RecordableEncodedFrame::EncodedResolution>
pending_resolution;
diff --git a/third_party/libwebrtc/video/video_receive_stream_timeout_tracker_gn/moz.build b/third_party/libwebrtc/video/video_receive_stream_timeout_tracker_gn/moz.build
index 8fb9a9c737..f7e5be24c0 100644
--- a/third_party/libwebrtc/video/video_receive_stream_timeout_tracker_gn/moz.build
+++ b/third_party/libwebrtc/video/video_receive_stream_timeout_tracker_gn/moz.build
@@ -199,7 +199,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -209,10 +208,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/video/video_stream_buffer_controller_gn/moz.build b/third_party/libwebrtc/video/video_stream_buffer_controller_gn/moz.build
index e60151d967..7da76222b1 100644
--- a/third_party/libwebrtc/video/video_stream_buffer_controller_gn/moz.build
+++ b/third_party/libwebrtc/video/video_stream_buffer_controller_gn/moz.build
@@ -200,7 +200,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -210,10 +209,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/video/video_stream_encoder.cc b/third_party/libwebrtc/video/video_stream_encoder.cc
index c5f086e155..669f165635 100644
--- a/third_party/libwebrtc/video/video_stream_encoder.cc
+++ b/third_party/libwebrtc/video/video_stream_encoder.cc
@@ -94,6 +94,18 @@ int GetNumSpatialLayers(const VideoCodec& codec) {
}
}
+absl::optional<EncodedImageCallback::DropReason> MaybeConvertDropReason(
+ VideoStreamEncoderObserver::DropReason reason) {
+ switch (reason) {
+ case VideoStreamEncoderObserver::DropReason::kMediaOptimization:
+ return EncodedImageCallback::DropReason::kDroppedByMediaOptimizations;
+ case VideoStreamEncoderObserver::DropReason::kEncoder:
+ return EncodedImageCallback::DropReason::kDroppedByEncoder;
+ default:
+ return absl::nullopt;
+ }
+}
+
bool RequiresEncoderReset(const VideoCodec& prev_send_codec,
const VideoCodec& new_send_codec,
bool was_encode_called_since_last_initialization) {
@@ -647,7 +659,6 @@ VideoStreamEncoder::VideoStreamEncoder(
: field_trials_(field_trials),
worker_queue_(TaskQueueBase::Current()),
number_of_cores_(number_of_cores),
- sink_(nullptr),
settings_(settings),
allocation_cb_type_(allocation_cb_type),
rate_control_settings_(
@@ -661,39 +672,12 @@ VideoStreamEncoder::VideoStreamEncoder(
? encoder_selector_from_constructor_
: encoder_selector_from_factory_.get()),
encoder_stats_observer_(encoder_stats_observer),
- cadence_callback_(*this),
frame_cadence_adapter_(std::move(frame_cadence_adapter)),
- encoder_initialized_(false),
- max_framerate_(-1),
- pending_encoder_reconfiguration_(false),
- pending_encoder_creation_(false),
- crop_width_(0),
- crop_height_(0),
- encoder_target_bitrate_bps_(absl::nullopt),
- max_data_payload_length_(0),
- encoder_paused_and_dropped_frame_(false),
- was_encode_called_since_last_initialization_(false),
- encoder_failed_(false),
clock_(clock),
- last_captured_timestamp_(0),
delta_ntp_internal_ms_(clock_->CurrentNtpInMilliseconds() -
clock_->TimeInMilliseconds()),
last_frame_log_ms_(clock_->TimeInMilliseconds()),
- captured_frame_count_(0),
- dropped_frame_cwnd_pushback_count_(0),
- dropped_frame_encoder_block_count_(0),
- pending_frame_post_time_us_(0),
- accumulated_update_rect_{0, 0, 0, 0},
- accumulated_update_rect_is_valid_(true),
- animation_start_time_(Timestamp::PlusInfinity()),
- cap_resolution_due_to_video_content_(false),
- expect_resize_state_(ExpectResizeState::kNoResize),
- fec_controller_override_(nullptr),
- force_disable_frame_dropper_(false),
- pending_frame_drops_(0),
- cwnd_frame_counter_(0),
next_frame_types_(1, VideoFrameType::kVideoFrameDelta),
- frame_encode_metadata_writer_(this),
automatic_animation_detection_experiment_(
ParseAutomatincAnimationDetectionFieldTrial()),
input_state_provider_(encoder_stats_observer),
@@ -704,7 +688,6 @@ VideoStreamEncoder::VideoStreamEncoder(
degradation_preference_manager_(
std::make_unique<DegradationPreferenceManager>(
video_stream_adapter_.get())),
- adaptation_constraints_(),
stream_resource_manager_(&input_state_provider_,
encoder_stats_observer,
clock_,
@@ -1499,7 +1482,7 @@ void VideoStreamEncoder::OnEncoderSettingsChanged() {
}
void VideoStreamEncoder::OnFrame(Timestamp post_time,
- int frames_scheduled_for_processing,
+ bool queue_overload,
const VideoFrame& video_frame) {
RTC_DCHECK_RUN_ON(&encoder_queue_);
VideoFrame incoming_frame = video_frame;
@@ -1538,11 +1521,8 @@ void VideoStreamEncoder::OnFrame(Timestamp post_time,
<< incoming_frame.ntp_time_ms()
<< " <= " << last_captured_timestamp_
<< ") for incoming frame. Dropping.";
- encoder_queue_.PostTask([this, incoming_frame]() {
- RTC_DCHECK_RUN_ON(&encoder_queue_);
- accumulated_update_rect_.Union(incoming_frame.update_rect());
- accumulated_update_rect_is_valid_ &= incoming_frame.has_update_rect();
- });
+ ProcessDroppedFrame(incoming_frame,
+ VideoStreamEncoderObserver::DropReason::kBadTimestamp);
return;
}
@@ -1561,25 +1541,24 @@ void VideoStreamEncoder::OnFrame(Timestamp post_time,
bool cwnd_frame_drop =
cwnd_frame_drop_interval_ &&
(cwnd_frame_counter_++ % cwnd_frame_drop_interval_.value() == 0);
- if (frames_scheduled_for_processing == 1 && !cwnd_frame_drop) {
+ if (!queue_overload && !cwnd_frame_drop) {
MaybeEncodeVideoFrame(incoming_frame, post_time.us());
} else {
if (cwnd_frame_drop) {
// Frame drop by congestion window pushback. Do not encode this
// frame.
++dropped_frame_cwnd_pushback_count_;
- encoder_stats_observer_->OnFrameDropped(
- VideoStreamEncoderObserver::DropReason::kCongestionWindow);
} else {
// There is a newer frame in flight. Do not encode this frame.
RTC_LOG(LS_VERBOSE)
<< "Incoming frame dropped due to that the encoder is blocked.";
++dropped_frame_encoder_block_count_;
- encoder_stats_observer_->OnFrameDropped(
- VideoStreamEncoderObserver::DropReason::kEncoderQueue);
}
- accumulated_update_rect_.Union(incoming_frame.update_rect());
- accumulated_update_rect_is_valid_ &= incoming_frame.has_update_rect();
+ ProcessDroppedFrame(
+ incoming_frame,
+ cwnd_frame_drop
+ ? VideoStreamEncoderObserver::DropReason::kCongestionWindow
+ : VideoStreamEncoderObserver::DropReason::kEncoderQueue);
}
if (log_stats) {
RTC_LOG(LS_INFO) << "Number of frames: captured " << captured_frame_count_
@@ -1816,10 +1795,8 @@ void VideoStreamEncoder::MaybeEncodeVideoFrame(const VideoFrame& video_frame,
// Because pending frame will be dropped in any case, we need to
// remember its updated region.
if (pending_frame_) {
- encoder_stats_observer_->OnFrameDropped(
- VideoStreamEncoderObserver::DropReason::kEncoderQueue);
- accumulated_update_rect_.Union(pending_frame_->update_rect());
- accumulated_update_rect_is_valid_ &= pending_frame_->has_update_rect();
+ ProcessDroppedFrame(*pending_frame_,
+ VideoStreamEncoderObserver::DropReason::kEncoderQueue);
}
if (DropDueToSize(video_frame.size())) {
@@ -1833,10 +1810,8 @@ void VideoStreamEncoder::MaybeEncodeVideoFrame(const VideoFrame& video_frame,
} else {
// Ensure that any previously stored frame is dropped.
pending_frame_.reset();
- accumulated_update_rect_.Union(video_frame.update_rect());
- accumulated_update_rect_is_valid_ &= video_frame.has_update_rect();
- encoder_stats_observer_->OnFrameDropped(
- VideoStreamEncoderObserver::DropReason::kEncoderQueue);
+ ProcessDroppedFrame(
+ video_frame, VideoStreamEncoderObserver::DropReason::kEncoderQueue);
}
return;
}
@@ -1854,10 +1829,8 @@ void VideoStreamEncoder::MaybeEncodeVideoFrame(const VideoFrame& video_frame,
// Ensure that any previously stored frame is dropped.
pending_frame_.reset();
TraceFrameDropStart();
- accumulated_update_rect_.Union(video_frame.update_rect());
- accumulated_update_rect_is_valid_ &= video_frame.has_update_rect();
- encoder_stats_observer_->OnFrameDropped(
- VideoStreamEncoderObserver::DropReason::kEncoderQueue);
+ ProcessDroppedFrame(
+ video_frame, VideoStreamEncoderObserver::DropReason::kEncoderQueue);
}
return;
}
@@ -1879,10 +1852,9 @@ void VideoStreamEncoder::MaybeEncodeVideoFrame(const VideoFrame& video_frame,
? last_encoder_rate_settings_->encoder_target.bps()
: 0)
<< ", input frame rate " << framerate_fps;
- OnDroppedFrame(
- EncodedImageCallback::DropReason::kDroppedByMediaOptimizations);
- accumulated_update_rect_.Union(video_frame.update_rect());
- accumulated_update_rect_is_valid_ &= video_frame.has_update_rect();
+ ProcessDroppedFrame(
+ video_frame,
+ VideoStreamEncoderObserver::DropReason::kMediaOptimization);
return;
}
@@ -2228,16 +2200,6 @@ EncodedImageCallback::Result VideoStreamEncoder::OnEncodedImage(
}
void VideoStreamEncoder::OnDroppedFrame(DropReason reason) {
- switch (reason) {
- case DropReason::kDroppedByMediaOptimizations:
- encoder_stats_observer_->OnFrameDropped(
- VideoStreamEncoderObserver::DropReason::kMediaOptimization);
- break;
- case DropReason::kDroppedByEncoder:
- encoder_stats_observer_->OnFrameDropped(
- VideoStreamEncoderObserver::DropReason::kEncoder);
- break;
- }
sink_->OnDroppedFrame(reason);
encoder_queue_.PostTask([this, reason] {
RTC_DCHECK_RUN_ON(&encoder_queue_);
@@ -2396,6 +2358,11 @@ void VideoStreamEncoder::OnVideoSourceRestrictionsUpdated(
<< (reason ? reason->Name() : std::string("<null>"))
<< " to " << restrictions.ToString();
+ if (frame_cadence_adapter_) {
+ frame_cadence_adapter_->UpdateVideoSourceRestrictions(
+ restrictions.max_frame_rate());
+ }
+
// TODO(webrtc:14451) Split video_source_sink_controller_
// so that ownership on restrictions/wants is kept on &encoder_queue_
latest_restrictions_ = restrictions;
@@ -2620,4 +2587,16 @@ void VideoStreamEncoder::RemoveRestrictionsListenerForTesting(
event.Wait(rtc::Event::kForever);
}
+// RTC_RUN_ON(&encoder_queue_)
+void VideoStreamEncoder::ProcessDroppedFrame(
+ const VideoFrame& frame,
+ VideoStreamEncoderObserver::DropReason reason) {
+ accumulated_update_rect_.Union(frame.update_rect());
+ accumulated_update_rect_is_valid_ &= frame.has_update_rect();
+ if (auto converted_reason = MaybeConvertDropReason(reason)) {
+ OnDroppedFrame(*converted_reason);
+ }
+ encoder_stats_observer_->OnFrameDropped(reason);
+}
+
} // namespace webrtc
diff --git a/third_party/libwebrtc/video/video_stream_encoder.h b/third_party/libwebrtc/video/video_stream_encoder.h
index 68f008afc0..f2c21c12b0 100644
--- a/third_party/libwebrtc/video/video_stream_encoder.h
+++ b/third_party/libwebrtc/video/video_stream_encoder.h
@@ -132,6 +132,8 @@ class VideoStreamEncoder : public VideoStreamEncoderInterface,
double cwnd_reduce_ratio);
protected:
+ friend class VideoStreamEncoderFrameCadenceRestrictionTest;
+
// Used for testing. For example the `ScalingObserverInterface` methods must
// be called on `encoder_queue_`.
TaskQueueBase* encoder_queue() { return encoder_queue_.Get(); }
@@ -160,10 +162,9 @@ class VideoStreamEncoder : public VideoStreamEncoderInterface,
: video_stream_encoder_(video_stream_encoder) {}
// FrameCadenceAdapterInterface::Callback overrides.
void OnFrame(Timestamp post_time,
- int frames_scheduled_for_processing,
+ bool queue_overload,
const VideoFrame& frame) override {
- video_stream_encoder_.OnFrame(post_time, frames_scheduled_for_processing,
- frame);
+ video_stream_encoder_.OnFrame(post_time, queue_overload, frame);
}
void OnDiscardedFrame() override {
video_stream_encoder_.OnDiscardedFrame();
@@ -212,7 +213,7 @@ class VideoStreamEncoder : public VideoStreamEncoderInterface,
void ReconfigureEncoder() RTC_RUN_ON(&encoder_queue_);
void OnEncoderSettingsChanged() RTC_RUN_ON(&encoder_queue_);
void OnFrame(Timestamp post_time,
- int frames_scheduled_for_processing,
+ bool queue_overload,
const VideoFrame& video_frame);
void OnDiscardedFrame();
void RequestRefreshFrame();
@@ -266,12 +267,16 @@ class VideoStreamEncoder : public VideoStreamEncoderInterface,
const EncodedImage& encoded_image,
const CodecSpecificInfo* codec_specific_info);
+ void ProcessDroppedFrame(const VideoFrame& frame,
+ VideoStreamEncoderObserver::DropReason reason)
+ RTC_RUN_ON(&encoder_queue_);
+
const FieldTrialsView& field_trials_;
TaskQueueBase* const worker_queue_;
const int number_of_cores_;
- EncoderSink* sink_;
+ EncoderSink* sink_ = nullptr;
const VideoStreamEncoderSettings settings_;
const BitrateAllocationCallbackType allocation_cb_type_;
const RateControlSettings rate_control_settings_;
@@ -287,7 +292,7 @@ class VideoStreamEncoder : public VideoStreamEncoderInterface,
VideoStreamEncoderObserver* const encoder_stats_observer_;
// Adapter that avoids public inheritance of the cadence adapter's callback
// interface.
- CadenceCallback cadence_callback_;
+ CadenceCallback cadence_callback_{*this};
// Frame cadence encoder adapter. Frames enter this adapter first, and it then
// forwards them to our OnFrame method.
std::unique_ptr<FrameCadenceAdapterInterface> frame_cadence_adapter_
@@ -296,70 +301,74 @@ class VideoStreamEncoder : public VideoStreamEncoderInterface,
VideoEncoderConfig encoder_config_ RTC_GUARDED_BY(&encoder_queue_);
std::unique_ptr<VideoEncoder> encoder_ RTC_GUARDED_BY(&encoder_queue_)
RTC_PT_GUARDED_BY(&encoder_queue_);
- bool encoder_initialized_;
+ bool encoder_initialized_ = false;
std::unique_ptr<VideoBitrateAllocator> rate_allocator_
RTC_GUARDED_BY(&encoder_queue_) RTC_PT_GUARDED_BY(&encoder_queue_);
- int max_framerate_ RTC_GUARDED_BY(&encoder_queue_);
+ int max_framerate_ RTC_GUARDED_BY(&encoder_queue_) = -1;
// Set when ConfigureEncoder has been called in order to lazy reconfigure the
// encoder on the next frame.
- bool pending_encoder_reconfiguration_ RTC_GUARDED_BY(&encoder_queue_);
+ bool pending_encoder_reconfiguration_ RTC_GUARDED_BY(&encoder_queue_) = false;
// Set when configuration must create a new encoder object, e.g.,
// because of a codec change.
- bool pending_encoder_creation_ RTC_GUARDED_BY(&encoder_queue_);
+ bool pending_encoder_creation_ RTC_GUARDED_BY(&encoder_queue_) = false;
absl::InlinedVector<SetParametersCallback, 2> encoder_configuration_callbacks_
RTC_GUARDED_BY(&encoder_queue_);
absl::optional<VideoFrameInfo> last_frame_info_
RTC_GUARDED_BY(&encoder_queue_);
- int crop_width_ RTC_GUARDED_BY(&encoder_queue_);
- int crop_height_ RTC_GUARDED_BY(&encoder_queue_);
+ int crop_width_ RTC_GUARDED_BY(&encoder_queue_) = 0;
+ int crop_height_ RTC_GUARDED_BY(&encoder_queue_) = 0;
absl::optional<uint32_t> encoder_target_bitrate_bps_
RTC_GUARDED_BY(&encoder_queue_);
- size_t max_data_payload_length_ RTC_GUARDED_BY(&encoder_queue_);
+ size_t max_data_payload_length_ RTC_GUARDED_BY(&encoder_queue_) = 0;
absl::optional<EncoderRateSettings> last_encoder_rate_settings_
RTC_GUARDED_BY(&encoder_queue_);
- bool encoder_paused_and_dropped_frame_ RTC_GUARDED_BY(&encoder_queue_);
+ bool encoder_paused_and_dropped_frame_ RTC_GUARDED_BY(&encoder_queue_) =
+ false;
// Set to true if at least one frame was sent to encoder since last encoder
// initialization.
bool was_encode_called_since_last_initialization_
- RTC_GUARDED_BY(&encoder_queue_);
+ RTC_GUARDED_BY(&encoder_queue_) = false;
- bool encoder_failed_ RTC_GUARDED_BY(&encoder_queue_);
+ bool encoder_failed_ RTC_GUARDED_BY(&encoder_queue_) = false;
Clock* const clock_;
// Used to make sure incoming time stamp is increasing for every frame.
- int64_t last_captured_timestamp_ RTC_GUARDED_BY(&encoder_queue_);
+ int64_t last_captured_timestamp_ RTC_GUARDED_BY(&encoder_queue_) = 0;
// Delta used for translating between NTP and internal timestamps.
const int64_t delta_ntp_internal_ms_ RTC_GUARDED_BY(&encoder_queue_);
int64_t last_frame_log_ms_ RTC_GUARDED_BY(&encoder_queue_);
- int captured_frame_count_ RTC_GUARDED_BY(&encoder_queue_);
- int dropped_frame_cwnd_pushback_count_ RTC_GUARDED_BY(&encoder_queue_);
- int dropped_frame_encoder_block_count_ RTC_GUARDED_BY(&encoder_queue_);
+ int captured_frame_count_ RTC_GUARDED_BY(&encoder_queue_) = 0;
+ int dropped_frame_cwnd_pushback_count_ RTC_GUARDED_BY(&encoder_queue_) = 0;
+ int dropped_frame_encoder_block_count_ RTC_GUARDED_BY(&encoder_queue_) = 0;
absl::optional<VideoFrame> pending_frame_ RTC_GUARDED_BY(&encoder_queue_);
- int64_t pending_frame_post_time_us_ RTC_GUARDED_BY(&encoder_queue_);
+ int64_t pending_frame_post_time_us_ RTC_GUARDED_BY(&encoder_queue_) = 0;
VideoFrame::UpdateRect accumulated_update_rect_
RTC_GUARDED_BY(&encoder_queue_);
- bool accumulated_update_rect_is_valid_ RTC_GUARDED_BY(&encoder_queue_);
+ bool accumulated_update_rect_is_valid_ RTC_GUARDED_BY(&encoder_queue_) = true;
// Used for automatic content type detection.
absl::optional<VideoFrame::UpdateRect> last_update_rect_
RTC_GUARDED_BY(&encoder_queue_);
- Timestamp animation_start_time_ RTC_GUARDED_BY(&encoder_queue_);
- bool cap_resolution_due_to_video_content_ RTC_GUARDED_BY(&encoder_queue_);
+ Timestamp animation_start_time_ RTC_GUARDED_BY(&encoder_queue_) =
+ Timestamp::PlusInfinity();
+ bool cap_resolution_due_to_video_content_ RTC_GUARDED_BY(&encoder_queue_) =
+ false;
// Used to correctly ignore changes in update_rect introduced by
// resize triggered by animation detection.
enum class ExpectResizeState {
kNoResize, // Normal operation.
kResize, // Resize was triggered by the animation detection.
kFirstFrameAfterResize // Resize observed.
- } expect_resize_state_ RTC_GUARDED_BY(&encoder_queue_);
+ } expect_resize_state_ RTC_GUARDED_BY(&encoder_queue_) =
+ ExpectResizeState::kNoResize;
FecControllerOverride* fec_controller_override_
- RTC_GUARDED_BY(&encoder_queue_);
+ RTC_GUARDED_BY(&encoder_queue_) = nullptr;
absl::optional<int64_t> last_parameters_update_ms_
RTC_GUARDED_BY(&encoder_queue_);
absl::optional<int64_t> last_encode_info_ms_ RTC_GUARDED_BY(&encoder_queue_);
@@ -372,18 +381,18 @@ class VideoStreamEncoder : public VideoStreamEncoderInterface,
// disabled if VideoEncoder::GetEncoderInfo() indicates that the encoder has a
// trusted rate controller. This is determined on a per-frame basis, as the
// encoder behavior might dynamically change.
- bool force_disable_frame_dropper_ RTC_GUARDED_BY(&encoder_queue_);
+ bool force_disable_frame_dropper_ RTC_GUARDED_BY(&encoder_queue_) = false;
// Incremented on worker thread whenever `frame_dropper_` determines that a
// frame should be dropped. Decremented on whichever thread runs
// OnEncodedImage(), which is only called by one thread but not necessarily
// the worker thread.
- std::atomic<int> pending_frame_drops_;
+ std::atomic<int> pending_frame_drops_{0};
// Congestion window frame drop ratio (drop 1 in every
// cwnd_frame_drop_interval_ frames).
absl::optional<int> cwnd_frame_drop_interval_ RTC_GUARDED_BY(&encoder_queue_);
// Frame counter for congestion window frame drop.
- int cwnd_frame_counter_ RTC_GUARDED_BY(&encoder_queue_);
+ int cwnd_frame_counter_ RTC_GUARDED_BY(&encoder_queue_) = 0;
std::unique_ptr<EncoderBitrateAdjuster> bitrate_adjuster_
RTC_GUARDED_BY(&encoder_queue_);
@@ -392,7 +401,7 @@ class VideoStreamEncoder : public VideoStreamEncoderInterface,
// turn this into a simple bool `pending_keyframe_request_`.
std::vector<VideoFrameType> next_frame_types_ RTC_GUARDED_BY(&encoder_queue_);
- FrameEncodeMetadataWriter frame_encode_metadata_writer_;
+ FrameEncodeMetadataWriter frame_encode_metadata_writer_{this};
struct AutomaticAnimationDetectionExperiment {
bool enabled = false;
diff --git a/third_party/libwebrtc/video/video_stream_encoder_impl_gn/moz.build b/third_party/libwebrtc/video/video_stream_encoder_impl_gn/moz.build
index 2ea2fce60b..af4e29ee0a 100644
--- a/third_party/libwebrtc/video/video_stream_encoder_impl_gn/moz.build
+++ b/third_party/libwebrtc/video/video_stream_encoder_impl_gn/moz.build
@@ -205,7 +205,6 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
@@ -215,10 +214,6 @@ if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
"-msse2"
]
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/video/video_stream_encoder_interface_gn/moz.build b/third_party/libwebrtc/video/video_stream_encoder_interface_gn/moz.build
index 0d40ef9c8f..beeb7f9117 100644
--- a/third_party/libwebrtc/video/video_stream_encoder_interface_gn/moz.build
+++ b/third_party/libwebrtc/video/video_stream_encoder_interface_gn/moz.build
@@ -187,16 +187,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["_GNU_SOURCE"] = True
diff --git a/third_party/libwebrtc/video/video_stream_encoder_observer.h b/third_party/libwebrtc/video/video_stream_encoder_observer.h
index c10412181d..95ca5fa887 100644
--- a/third_party/libwebrtc/video/video_stream_encoder_observer.h
+++ b/third_party/libwebrtc/video/video_stream_encoder_observer.h
@@ -58,6 +58,7 @@ class VideoStreamEncoderObserver : public CpuOveruseMetricsObserver {
enum class DropReason {
kSource,
+ kBadTimestamp,
kEncoderQueue,
kEncoder,
kMediaOptimization,
diff --git a/third_party/libwebrtc/video/video_stream_encoder_unittest.cc b/third_party/libwebrtc/video/video_stream_encoder_unittest.cc
index fa28368d68..6fa99081cd 100644
--- a/third_party/libwebrtc/video/video_stream_encoder_unittest.cc
+++ b/third_party/libwebrtc/video/video_stream_encoder_unittest.cc
@@ -136,8 +136,8 @@ void PassAFrame(
FrameCadenceAdapterInterface::Callback* video_stream_encoder_callback,
int64_t ntp_time_ms) {
encoder_queue->PostTask([video_stream_encoder_callback, ntp_time_ms] {
- video_stream_encoder_callback->OnFrame(Timestamp::Millis(ntp_time_ms), 1,
- CreateSimpleNV12Frame());
+ video_stream_encoder_callback->OnFrame(Timestamp::Millis(ntp_time_ms),
+ false, CreateSimpleNV12Frame());
});
}
@@ -783,6 +783,10 @@ class MockFrameCadenceAdapter : public FrameCadenceAdapterInterface {
UpdateLayerStatus,
(size_t spatial_index, bool enabled),
(override));
+ MOCK_METHOD(void,
+ UpdateVideoSourceRestrictions,
+ (absl::optional<double>),
+ (override));
MOCK_METHOD(void, ProcessKeyFrameRequest, (), (override));
};
@@ -9580,4 +9584,60 @@ TEST(VideoStreamEncoderFrameCadenceTest,
kMaxFps);
}
+class VideoStreamEncoderFrameCadenceRestrictionTest : public ::testing::Test {
+ public:
+ VideoStreamEncoderFrameCadenceRestrictionTest()
+ : adapter_ptr_(adapter_.get()),
+ fake_resource_(FakeResource::Create("FakeResource")),
+ video_stream_encoder_(
+ factory_.Create(std::move(adapter_), &encoder_queue_)) {}
+
+ ~VideoStreamEncoderFrameCadenceRestrictionTest() {
+ factory_.DepleteTaskQueues();
+ }
+
+ void UpdateVideoSourceRestrictions(VideoSourceRestrictions restrictions) {
+ encoder_queue_->PostTask([this, restrictions] {
+ RTC_DCHECK_RUN_ON(encoder_queue_);
+ video_stream_encoder_->OnVideoSourceRestrictionsUpdated(
+ restrictions, VideoAdaptationCounters(), fake_resource_,
+ VideoSourceRestrictions());
+ });
+ }
+
+ protected:
+ SimpleVideoStreamEncoderFactory factory_;
+ std::unique_ptr<NiceMock<MockFrameCadenceAdapter>> adapter_{
+ std::make_unique<NiceMock<MockFrameCadenceAdapter>>()};
+ NiceMock<MockFrameCadenceAdapter>* adapter_ptr_;
+ TaskQueueBase* encoder_queue_{nullptr};
+ rtc::scoped_refptr<FakeResource> fake_resource_;
+ VideoSourceRestrictions restrictions_;
+ std::unique_ptr<SimpleVideoStreamEncoderFactory::AdaptedVideoStreamEncoder>
+ video_stream_encoder_;
+};
+
+TEST_F(VideoStreamEncoderFrameCadenceRestrictionTest,
+ UpdatesVideoSourceRestrictionsUnRestricted) {
+ EXPECT_CALL(*adapter_ptr_, UpdateVideoSourceRestrictions(Eq(absl::nullopt)));
+ UpdateVideoSourceRestrictions(VideoSourceRestrictions());
+}
+
+TEST_F(VideoStreamEncoderFrameCadenceRestrictionTest,
+ UpdatesVideoSourceRestrictionsWithMaxFrameRateRestriction) {
+ restrictions_.set_max_frame_rate(20);
+ EXPECT_CALL(*adapter_ptr_, UpdateVideoSourceRestrictions(Optional(20)));
+ UpdateVideoSourceRestrictions(restrictions_);
+}
+
+TEST_F(VideoStreamEncoderFrameCadenceRestrictionTest,
+ UpdatesVideoSourceRestrictionsWithoutMaxFrameRateRestriction) {
+ // Restrictions in resolution count as restriction updated, even though the
+ // FPS is unlimited.
+ restrictions_.set_max_pixels_per_frame(99);
+ restrictions_.set_target_pixels_per_frame(101);
+ EXPECT_CALL(*adapter_ptr_, UpdateVideoSourceRestrictions(Eq(absl::nullopt)));
+ UpdateVideoSourceRestrictions(restrictions_);
+}
+
} // namespace webrtc
diff --git a/third_party/libwebrtc/webrtc.gni b/third_party/libwebrtc/webrtc.gni
index 931a0a24e5..e1273475b9 100644
--- a/third_party/libwebrtc/webrtc.gni
+++ b/third_party/libwebrtc/webrtc.gni
@@ -484,12 +484,14 @@ all_poison_types = [
# Encoders and decoders for specific audio codecs such as Opus and iSAC.
"audio_codecs",
- # Default task queue implementation.
- "default_task_queue",
-
# Default echo detector implementation.
"default_echo_detector",
+ # Implementations of the utilities exposed through `Environment`.
+ # Most webrtc classes must use propagated `Environment`. Only few top-level
+ # classes are allowed to create `Environment` from individual utilities.
+ "environment_construction",
+
# Software video codecs (VP8 and VP9 through libvpx).
"software_video_codecs",
]
diff --git a/third_party/libwebrtc/webrtc_gn/moz.build b/third_party/libwebrtc/webrtc_gn/moz.build
index f71e585125..b327aad030 100644
--- a/third_party/libwebrtc/webrtc_gn/moz.build
+++ b/third_party/libwebrtc/webrtc_gn/moz.build
@@ -235,16 +235,9 @@ if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
OS_LIBS += [
- "android_support",
"unwind"
]
-if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
-
- OS_LIBS += [
- "android_support"
- ]
-
if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
DEFINES["WEBRTC_USE_PIPEWIRE"] = True
diff --git a/third_party/libwebrtc/webrtc_lib_link_test.cc b/third_party/libwebrtc/webrtc_lib_link_test.cc
index 868c17287e..64da01e4ef 100644
--- a/third_party/libwebrtc/webrtc_lib_link_test.cc
+++ b/third_party/libwebrtc/webrtc_lib_link_test.cc
@@ -12,8 +12,8 @@
#include "api/audio_codecs/audio_encoder_factory_template.h"
#include "api/audio_codecs/opus/audio_decoder_opus.h"
#include "api/audio_codecs/opus/audio_encoder_opus.h"
-#include "api/call/call_factory_interface.h"
#include "api/create_peerconnection_factory.h"
+#include "api/enable_media.h"
#include "api/peer_connection_interface.h"
#include "api/rtc_event_log/rtc_event_log_factory.h"
#include "api/stats/rtcstats_objects.h"
@@ -28,18 +28,14 @@
#include "api/video_codecs/video_encoder_factory_template_libvpx_vp8_adapter.h"
#include "api/video_codecs/video_encoder_factory_template_libvpx_vp9_adapter.h"
#include "api/video_codecs/video_encoder_factory_template_open_h264_adapter.h"
-#include "media/engine/webrtc_media_engine.h"
#include "modules/audio_device/include/audio_device.h"
#include "modules/audio_processing/include/audio_processing.h"
namespace webrtc {
-cricket::MediaEngineDependencies CreateSomeMediaDeps(
- TaskQueueFactory* task_queue_factory) {
- cricket::MediaEngineDependencies media_deps;
- media_deps.task_queue_factory = task_queue_factory;
+void CreateSomeMediaDeps(PeerConnectionFactoryDependencies& media_deps) {
media_deps.adm = AudioDeviceModule::CreateForTest(
- AudioDeviceModule::kDummyAudio, task_queue_factory);
+ AudioDeviceModule::kDummyAudio, media_deps.task_queue_factory.get());
media_deps.audio_encoder_factory =
webrtc::CreateAudioEncoderFactory<webrtc::AudioEncoderOpus>();
media_deps.audio_decoder_factory =
@@ -53,7 +49,6 @@ cricket::MediaEngineDependencies CreateSomeMediaDeps(
LibvpxVp8DecoderTemplateAdapter, LibvpxVp9DecoderTemplateAdapter,
OpenH264DecoderTemplateAdapter, Dav1dDecoderTemplateAdapter>>();
media_deps.audio_processing = webrtc::AudioProcessingBuilder().Create();
- return media_deps;
}
webrtc::PeerConnectionFactoryDependencies CreateSomePcfDeps() {
@@ -62,11 +57,10 @@ webrtc::PeerConnectionFactoryDependencies CreateSomePcfDeps() {
pcf_deps.signaling_thread = rtc::Thread::Current();
pcf_deps.network_thread = rtc::Thread::Current();
pcf_deps.worker_thread = rtc::Thread::Current();
- pcf_deps.call_factory = webrtc::CreateCallFactory();
pcf_deps.event_log_factory = std::make_unique<webrtc::RtcEventLogFactory>(
pcf_deps.task_queue_factory.get());
- auto media_deps = CreateSomeMediaDeps(pcf_deps.task_queue_factory.get());
- pcf_deps.media_engine = cricket::CreateMediaEngine(std::move(media_deps));
+ CreateSomeMediaDeps(pcf_deps);
+ EnableMedia(pcf_deps);
return pcf_deps;
}
@@ -86,8 +80,9 @@ void TestCase1ModularFactory() {
}
void TestCase2RegularFactory() {
- auto task_queue_factory = CreateDefaultTaskQueueFactory();
- auto media_deps = CreateSomeMediaDeps(task_queue_factory.get());
+ PeerConnectionFactoryDependencies media_deps;
+ media_deps.task_queue_factory = CreateDefaultTaskQueueFactory();
+ CreateSomeMediaDeps(media_deps);
auto peer_connection_factory = webrtc::CreatePeerConnectionFactory(
rtc::Thread::Current(), rtc::Thread::Current(), rtc::Thread::Current(),
diff --git a/third_party/moz.build b/third_party/moz.build
index abb3aff2c1..27d7794efd 100644
--- a/third_party/moz.build
+++ b/third_party/moz.build
@@ -88,9 +88,6 @@ with Files('rlbox_wasm2c_sandbox/**'):
with Files('wasm2c/**'):
BUG_COMPONENT = ('Core', 'Security: RLBox')
-with Files('WinToast/**'):
- BUG_COMPONENT = ('Toolkit', 'General')
-
with Files('libsrtp/**'):
BUG_COMPONENT = ('Core', 'WebRTC: Networking')
diff --git a/third_party/python/Mako/Mako-1.1.2.dist-info/AUTHORS b/third_party/python/Mako/Mako-1.1.2.dist-info/AUTHORS
new file mode 100644
index 0000000000..81d16dc1a9
--- /dev/null
+++ b/third_party/python/Mako/Mako-1.1.2.dist-info/AUTHORS
@@ -0,0 +1,13 @@
+Mako was created by Michael Bayer.
+
+Major contributing authors include:
+
+- Michael Bayer <mike_mp@zzzcomputing.com>
+- Geoffrey T. Dairiki <dairiki@dairiki.org>
+- Philip Jenvey <pjenvey@underboss.org>
+- David Peckam
+- Armin Ronacher
+- Ben Bangert <ben@groovie.org>
+- Ben Trofatter
+
+
diff --git a/third_party/python/Mako/Mako-1.1.2.dist-info/LICENSE b/third_party/python/Mako/Mako-1.1.2.dist-info/LICENSE
new file mode 100644
index 0000000000..1f835e94a1
--- /dev/null
+++ b/third_party/python/Mako/Mako-1.1.2.dist-info/LICENSE
@@ -0,0 +1,19 @@
+Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE. \ No newline at end of file
diff --git a/third_party/python/Mako/Mako-1.1.2.dist-info/METADATA b/third_party/python/Mako/Mako-1.1.2.dist-info/METADATA
new file mode 100644
index 0000000000..fb64a7ae0c
--- /dev/null
+++ b/third_party/python/Mako/Mako-1.1.2.dist-info/METADATA
@@ -0,0 +1,82 @@
+Metadata-Version: 2.1
+Name: Mako
+Version: 1.1.2
+Summary: A super-fast templating language that borrows the best ideas from the existing templating languages.
+Home-page: https://www.makotemplates.org/
+Author: Mike Bayer
+Author-email: mike@zzzcomputing.com
+License: MIT
+Project-URL: Documentation, https://docs.makotemplates.org
+Project-URL: Issue Tracker, https://github.com/sqlalchemy/mako
+Keywords: templates
+Platform: UNKNOWN
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Environment :: Web Environment
+Classifier: Intended Audience :: Developers
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
+Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
+Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*
+Requires-Dist: MarkupSafe (>=0.9.2)
+Provides-Extra: babel
+Requires-Dist: Babel ; extra == 'babel'
+Provides-Extra: lingua
+Requires-Dist: lingua ; extra == 'lingua'
+
+=========================
+Mako Templates for Python
+=========================
+
+Mako is a template library written in Python. It provides a familiar, non-XML
+syntax which compiles into Python modules for maximum performance. Mako's
+syntax and API borrows from the best ideas of many others, including Django
+templates, Cheetah, Myghty, and Genshi. Conceptually, Mako is an embedded
+Python (i.e. Python Server Page) language, which refines the familiar ideas
+of componentized layout and inheritance to produce one of the most
+straightforward and flexible models available, while also maintaining close
+ties to Python calling and scoping semantics.
+
+Nutshell
+========
+
+::
+
+ <%inherit file="base.html"/>
+ <%
+ rows = [[v for v in range(0,10)] for row in range(0,10)]
+ %>
+ <table>
+ % for row in rows:
+ ${makerow(row)}
+ % endfor
+ </table>
+
+ <%def name="makerow(row)">
+ <tr>
+ % for name in row:
+ <td>${name}</td>\
+ % endfor
+ </tr>
+ </%def>
+
+Philosophy
+===========
+
+Python is a great scripting language. Don't reinvent the wheel...your templates can handle it !
+
+Documentation
+==============
+
+See documentation for Mako at https://docs.makotemplates.org/en/latest/
+
+License
+========
+
+Mako is licensed under an MIT-style license (see LICENSE).
+Other incorporated projects may be licensed under different licenses.
+All licenses allow for non-commercial and commercial use.
+
+
diff --git a/third_party/python/Mako/Mako-1.1.2.dist-info/RECORD b/third_party/python/Mako/Mako-1.1.2.dist-info/RECORD
new file mode 100644
index 0000000000..25f3b652f9
--- /dev/null
+++ b/third_party/python/Mako/Mako-1.1.2.dist-info/RECORD
@@ -0,0 +1,33 @@
+mako/__init__.py,sha256=kukH4UZzZx0XHsTBrfQMJ2fxcncjreyZv2m6PzvPWAM,242
+mako/_ast_util.py,sha256=QKXZC0DbpYefKhTrQZjLgjcNXlTgY38sbB-vmBR2HpU,20414
+mako/ast.py,sha256=T5KnOwZewqAfULULLLWp6joGD-j14SiCtrH1-KGJCpQ,6789
+mako/cache.py,sha256=N1VoKHul8K7RUwsGwoUL-HMtylDvrL6iGWNh7_AI1dc,7736
+mako/cmd.py,sha256=HZxSUsAFVHVrcWvb43Nh_vdbrGeJLFNTR6ejyhdZ0dc,2859
+mako/codegen.py,sha256=DoxSM34-305v0E4Ox7Y31nsVtKAmCEbRVC3BmNFy_54,47892
+mako/compat.py,sha256=08w8lB0Z3QKQi9vd4n4xUtjG_A3wOrk3QdvxkHlribY,3848
+mako/exceptions.py,sha256=ogXjpZO1beh37cWWa0pm4IHVNKsuNIUnqOjWznEKMLQ,13110
+mako/filters.py,sha256=vzpdxOOXWco5_evH_6_9a8b92lHuDC7Sl3XZhFyIVV8,6063
+mako/lexer.py,sha256=pNKb5MVSzOdW0L2S97TYPFBATmHD_mo8Br9-5RSfIUM,16926
+mako/lookup.py,sha256=TQ-wx1DR8rj2HqsNJBsrS4ZqROwAeTRkw-LrTbSQxFc,12718
+mako/parsetree.py,sha256=epGi5wKtZA8LcpzdrEXl_jjPGPvuO-IjuDSAYoLAp4Y,19411
+mako/pygen.py,sha256=dKxVMCSPMaXbMTgQyd5_J7WvdzPpuUprufR4PS3cyqY,10073
+mako/pyparser.py,sha256=eU3-mgdrmj1cL9SgFxh1rvIFcio_6oJxoNJnyMuGiCI,7789
+mako/runtime.py,sha256=2fhZBgmnP3wrWlZAVd6PZCSeuuGVXVA8BmRdXs6VEDo,28040
+mako/template.py,sha256=hKYaXvRzqU7Map8wXaGTGXc8gPl8EDF4WqoNpIF-EqQ,26558
+mako/util.py,sha256=5DoK9dvPpzFK6ZnL3hhzMHQ0meanhXrH8aHoO8fbkCs,11038
+mako/ext/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+mako/ext/autohandler.py,sha256=FJs1cY6Vz_NePboCUr-3STZY38btxFRZsPhMNe6NSms,1885
+mako/ext/babelplugin.py,sha256=EquybfGr6ffla72QapzkwTNpEwi_P87f1s9C7xNFuJw,2138
+mako/ext/beaker_cache.py,sha256=oDN-vSLeKfnAJKlPgrKKuHI-g7zszwd2y1uApBoOkeM,2599
+mako/ext/extract.py,sha256=oBx6lQqLOtDMu8YpBYK_klCZvMuVvbAAA3I-WUyTPXo,4616
+mako/ext/linguaplugin.py,sha256=Z8bV4RHjDJhqMApINSadycM1Xj-B2vB1_i3YN3l2KSc,1954
+mako/ext/preprocessors.py,sha256=TfHmG6EgzYumbCiFU06IHXG_n5y2sA6RFtDBNJ613M8,576
+mako/ext/pygmentplugin.py,sha256=wYJixnCqHJ7zHPT6gB3tGUg-R6yctFNpEhNIKbHHl-E,4951
+mako/ext/turbogears.py,sha256=BcKxkPpkeawkFqj6zS5sUQYt4I6LafRDYMLIDOg0ZPY,2165
+Mako-1.1.2.dist-info/AUTHORS,sha256=Io2Vw70mjYS7yFcUuJxhIGiMUQt8FWJuxiiwyUW1WRg,282
+Mako-1.1.2.dist-info/LICENSE,sha256=R80NQbEJL5Fhz7Yp7RXlzqGFFEcQ_0YzpCge8Ij_Xec,1097
+Mako-1.1.2.dist-info/METADATA,sha256=fxw2oNdTkNQnafc1Enid-QapQv1OaYnqwtNDJoeihoo,2600
+Mako-1.1.2.dist-info/WHEEL,sha256=8zNYZbwQSXoB9IfXOjPfeNwvAsALAjffgk27FqvCWbo,110
+Mako-1.1.2.dist-info/entry_points.txt,sha256=GSuruj6eMrGwr7dHBGOdDkmgTTUQXr5ZrQjMmkPclKA,603
+Mako-1.1.2.dist-info/top_level.txt,sha256=LItdH8cDPetpUu8rUyBG3DObS6h9Gcpr9j_WLj2S-R0,5
+Mako-1.1.2.dist-info/RECORD,,
diff --git a/third_party/python/Mako/Mako-1.1.2.dist-info/WHEEL b/third_party/python/Mako/Mako-1.1.2.dist-info/WHEEL
new file mode 100644
index 0000000000..8b701e93c2
--- /dev/null
+++ b/third_party/python/Mako/Mako-1.1.2.dist-info/WHEEL
@@ -0,0 +1,6 @@
+Wheel-Version: 1.0
+Generator: bdist_wheel (0.33.6)
+Root-Is-Purelib: true
+Tag: py2-none-any
+Tag: py3-none-any
+
diff --git a/third_party/python/Mako/Mako-1.1.2.dist-info/entry_points.txt b/third_party/python/Mako/Mako-1.1.2.dist-info/entry_points.txt
new file mode 100644
index 0000000000..8e033c0040
--- /dev/null
+++ b/third_party/python/Mako/Mako-1.1.2.dist-info/entry_points.txt
@@ -0,0 +1,20 @@
+
+ [python.templating.engines]
+ mako = mako.ext.turbogears:TGPlugin
+
+ [pygments.lexers]
+ mako = mako.ext.pygmentplugin:MakoLexer
+ html+mako = mako.ext.pygmentplugin:MakoHtmlLexer
+ xml+mako = mako.ext.pygmentplugin:MakoXmlLexer
+ js+mako = mako.ext.pygmentplugin:MakoJavascriptLexer
+ css+mako = mako.ext.pygmentplugin:MakoCssLexer
+
+ [babel.extractors]
+ mako = mako.ext.babelplugin:extract [babel]
+
+ [lingua.extractors]
+ mako = mako.ext.linguaplugin:LinguaMakoExtractor [lingua]
+
+ [console_scripts]
+ mako-render = mako.cmd:cmdline
+ \ No newline at end of file
diff --git a/third_party/python/Mako/Mako-1.1.2.dist-info/top_level.txt b/third_party/python/Mako/Mako-1.1.2.dist-info/top_level.txt
new file mode 100644
index 0000000000..2951cdd49d
--- /dev/null
+++ b/third_party/python/Mako/Mako-1.1.2.dist-info/top_level.txt
@@ -0,0 +1 @@
+mako
diff --git a/third_party/python/Mako/mako/__init__.py b/third_party/python/Mako/mako/__init__.py
new file mode 100644
index 0000000000..16d187c0ce
--- /dev/null
+++ b/third_party/python/Mako/mako/__init__.py
@@ -0,0 +1,8 @@
+# mako/__init__.py
+# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
+#
+# This module is part of Mako and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+
+
+__version__ = '1.1.2'
diff --git a/third_party/python/Mako/mako/_ast_util.py b/third_party/python/Mako/mako/_ast_util.py
new file mode 100644
index 0000000000..bdcdbf6913
--- /dev/null
+++ b/third_party/python/Mako/mako/_ast_util.py
@@ -0,0 +1,716 @@
+# mako/_ast_util.py
+# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
+#
+# This module is part of Mako and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+
+"""
+ ast
+ ~~~
+
+ This is a stripped down version of Armin Ronacher's ast module.
+
+ :copyright: Copyright 2008 by Armin Ronacher.
+ :license: Python License.
+"""
+
+
+from _ast import Add
+from _ast import And
+from _ast import AST
+from _ast import BitAnd
+from _ast import BitOr
+from _ast import BitXor
+from _ast import Div
+from _ast import Eq
+from _ast import FloorDiv
+from _ast import Gt
+from _ast import GtE
+from _ast import If
+from _ast import In
+from _ast import Invert
+from _ast import Is
+from _ast import IsNot
+from _ast import LShift
+from _ast import Lt
+from _ast import LtE
+from _ast import Mod
+from _ast import Mult
+from _ast import Name
+from _ast import Not
+from _ast import NotEq
+from _ast import NotIn
+from _ast import Or
+from _ast import PyCF_ONLY_AST
+from _ast import RShift
+from _ast import Sub
+from _ast import UAdd
+from _ast import USub
+
+from mako.compat import arg_stringname
+
+BOOLOP_SYMBOLS = {And: "and", Or: "or"}
+
+BINOP_SYMBOLS = {
+ Add: "+",
+ Sub: "-",
+ Mult: "*",
+ Div: "/",
+ FloorDiv: "//",
+ Mod: "%",
+ LShift: "<<",
+ RShift: ">>",
+ BitOr: "|",
+ BitAnd: "&",
+ BitXor: "^",
+}
+
+CMPOP_SYMBOLS = {
+ Eq: "==",
+ Gt: ">",
+ GtE: ">=",
+ In: "in",
+ Is: "is",
+ IsNot: "is not",
+ Lt: "<",
+ LtE: "<=",
+ NotEq: "!=",
+ NotIn: "not in",
+}
+
+UNARYOP_SYMBOLS = {Invert: "~", Not: "not", UAdd: "+", USub: "-"}
+
+ALL_SYMBOLS = {}
+ALL_SYMBOLS.update(BOOLOP_SYMBOLS)
+ALL_SYMBOLS.update(BINOP_SYMBOLS)
+ALL_SYMBOLS.update(CMPOP_SYMBOLS)
+ALL_SYMBOLS.update(UNARYOP_SYMBOLS)
+
+
+def parse(expr, filename="<unknown>", mode="exec"):
+ """Parse an expression into an AST node."""
+ return compile(expr, filename, mode, PyCF_ONLY_AST)
+
+
+def iter_fields(node):
+ """Iterate over all fields of a node, only yielding existing fields."""
+ # CPython 2.5 compat
+ if not hasattr(node, "_fields") or not node._fields:
+ return
+ for field in node._fields:
+ try:
+ yield field, getattr(node, field)
+ except AttributeError:
+ pass
+
+
+class NodeVisitor(object):
+
+ """
+ Walks the abstract syntax tree and call visitor functions for every node
+ found. The visitor functions may return values which will be forwarded
+ by the `visit` method.
+
+ Per default the visitor functions for the nodes are ``'visit_'`` +
+ class name of the node. So a `TryFinally` node visit function would
+ be `visit_TryFinally`. This behavior can be changed by overriding
+ the `get_visitor` function. If no visitor function exists for a node
+ (return value `None`) the `generic_visit` visitor is used instead.
+
+ Don't use the `NodeVisitor` if you want to apply changes to nodes during
+ traversing. For this a special visitor exists (`NodeTransformer`) that
+ allows modifications.
+ """
+
+ def get_visitor(self, node):
+ """
+ Return the visitor function for this node or `None` if no visitor
+ exists for this node. In that case the generic visit function is
+ used instead.
+ """
+ method = "visit_" + node.__class__.__name__
+ return getattr(self, method, None)
+
+ def visit(self, node):
+ """Visit a node."""
+ f = self.get_visitor(node)
+ if f is not None:
+ return f(node)
+ return self.generic_visit(node)
+
+ def generic_visit(self, node):
+ """Called if no explicit visitor function exists for a node."""
+ for field, value in iter_fields(node):
+ if isinstance(value, list):
+ for item in value:
+ if isinstance(item, AST):
+ self.visit(item)
+ elif isinstance(value, AST):
+ self.visit(value)
+
+
+class NodeTransformer(NodeVisitor):
+
+ """
+ Walks the abstract syntax tree and allows modifications of nodes.
+
+ The `NodeTransformer` will walk the AST and use the return value of the
+ visitor functions to replace or remove the old node. If the return
+ value of the visitor function is `None` the node will be removed
+ from the previous location otherwise it's replaced with the return
+ value. The return value may be the original node in which case no
+ replacement takes place.
+
+ Here an example transformer that rewrites all `foo` to `data['foo']`::
+
+ class RewriteName(NodeTransformer):
+
+ def visit_Name(self, node):
+ return copy_location(Subscript(
+ value=Name(id='data', ctx=Load()),
+ slice=Index(value=Str(s=node.id)),
+ ctx=node.ctx
+ ), node)
+
+ Keep in mind that if the node you're operating on has child nodes
+ you must either transform the child nodes yourself or call the generic
+ visit function for the node first.
+
+ Nodes that were part of a collection of statements (that applies to
+ all statement nodes) may also return a list of nodes rather than just
+ a single node.
+
+ Usually you use the transformer like this::
+
+ node = YourTransformer().visit(node)
+ """
+
+ def generic_visit(self, node):
+ for field, old_value in iter_fields(node):
+ old_value = getattr(node, field, None)
+ if isinstance(old_value, list):
+ new_values = []
+ for value in old_value:
+ if isinstance(value, AST):
+ value = self.visit(value)
+ if value is None:
+ continue
+ elif not isinstance(value, AST):
+ new_values.extend(value)
+ continue
+ new_values.append(value)
+ old_value[:] = new_values
+ elif isinstance(old_value, AST):
+ new_node = self.visit(old_value)
+ if new_node is None:
+ delattr(node, field)
+ else:
+ setattr(node, field, new_node)
+ return node
+
+
+class SourceGenerator(NodeVisitor):
+
+ """
+ This visitor is able to transform a well formed syntax tree into python
+ sourcecode. For more details have a look at the docstring of the
+ `node_to_source` function.
+ """
+
+ def __init__(self, indent_with):
+ self.result = []
+ self.indent_with = indent_with
+ self.indentation = 0
+ self.new_lines = 0
+
+ def write(self, x):
+ if self.new_lines:
+ if self.result:
+ self.result.append("\n" * self.new_lines)
+ self.result.append(self.indent_with * self.indentation)
+ self.new_lines = 0
+ self.result.append(x)
+
+ def newline(self, n=1):
+ self.new_lines = max(self.new_lines, n)
+
+ def body(self, statements):
+ self.new_line = True
+ self.indentation += 1
+ for stmt in statements:
+ self.visit(stmt)
+ self.indentation -= 1
+
+ def body_or_else(self, node):
+ self.body(node.body)
+ if node.orelse:
+ self.newline()
+ self.write("else:")
+ self.body(node.orelse)
+
+ def signature(self, node):
+ want_comma = []
+
+ def write_comma():
+ if want_comma:
+ self.write(", ")
+ else:
+ want_comma.append(True)
+
+ padding = [None] * (len(node.args) - len(node.defaults))
+ for arg, default in zip(node.args, padding + node.defaults):
+ write_comma()
+ self.visit(arg)
+ if default is not None:
+ self.write("=")
+ self.visit(default)
+ if node.vararg is not None:
+ write_comma()
+ self.write("*" + arg_stringname(node.vararg))
+ if node.kwarg is not None:
+ write_comma()
+ self.write("**" + arg_stringname(node.kwarg))
+
+ def decorators(self, node):
+ for decorator in node.decorator_list:
+ self.newline()
+ self.write("@")
+ self.visit(decorator)
+
+ # Statements
+
+ def visit_Assign(self, node):
+ self.newline()
+ for idx, target in enumerate(node.targets):
+ if idx:
+ self.write(", ")
+ self.visit(target)
+ self.write(" = ")
+ self.visit(node.value)
+
+ def visit_AugAssign(self, node):
+ self.newline()
+ self.visit(node.target)
+ self.write(BINOP_SYMBOLS[type(node.op)] + "=")
+ self.visit(node.value)
+
+ def visit_ImportFrom(self, node):
+ self.newline()
+ self.write("from %s%s import " % ("." * node.level, node.module))
+ for idx, item in enumerate(node.names):
+ if idx:
+ self.write(", ")
+ self.write(item)
+
+ def visit_Import(self, node):
+ self.newline()
+ for item in node.names:
+ self.write("import ")
+ self.visit(item)
+
+ def visit_Expr(self, node):
+ self.newline()
+ self.generic_visit(node)
+
+ def visit_FunctionDef(self, node):
+ self.newline(n=2)
+ self.decorators(node)
+ self.newline()
+ self.write("def %s(" % node.name)
+ self.signature(node.args)
+ self.write("):")
+ self.body(node.body)
+
+ def visit_ClassDef(self, node):
+ have_args = []
+
+ def paren_or_comma():
+ if have_args:
+ self.write(", ")
+ else:
+ have_args.append(True)
+ self.write("(")
+
+ self.newline(n=3)
+ self.decorators(node)
+ self.newline()
+ self.write("class %s" % node.name)
+ for base in node.bases:
+ paren_or_comma()
+ self.visit(base)
+ # XXX: the if here is used to keep this module compatible
+ # with python 2.6.
+ if hasattr(node, "keywords"):
+ for keyword in node.keywords:
+ paren_or_comma()
+ self.write(keyword.arg + "=")
+ self.visit(keyword.value)
+ if getattr(node, "starargs", None):
+ paren_or_comma()
+ self.write("*")
+ self.visit(node.starargs)
+ if getattr(node, "kwargs", None):
+ paren_or_comma()
+ self.write("**")
+ self.visit(node.kwargs)
+ self.write(have_args and "):" or ":")
+ self.body(node.body)
+
+ def visit_If(self, node):
+ self.newline()
+ self.write("if ")
+ self.visit(node.test)
+ self.write(":")
+ self.body(node.body)
+ while True:
+ else_ = node.orelse
+ if len(else_) == 1 and isinstance(else_[0], If):
+ node = else_[0]
+ self.newline()
+ self.write("elif ")
+ self.visit(node.test)
+ self.write(":")
+ self.body(node.body)
+ else:
+ self.newline()
+ self.write("else:")
+ self.body(else_)
+ break
+
+ def visit_For(self, node):
+ self.newline()
+ self.write("for ")
+ self.visit(node.target)
+ self.write(" in ")
+ self.visit(node.iter)
+ self.write(":")
+ self.body_or_else(node)
+
+ def visit_While(self, node):
+ self.newline()
+ self.write("while ")
+ self.visit(node.test)
+ self.write(":")
+ self.body_or_else(node)
+
+ def visit_With(self, node):
+ self.newline()
+ self.write("with ")
+ self.visit(node.context_expr)
+ if node.optional_vars is not None:
+ self.write(" as ")
+ self.visit(node.optional_vars)
+ self.write(":")
+ self.body(node.body)
+
+ def visit_Pass(self, node):
+ self.newline()
+ self.write("pass")
+
+ def visit_Print(self, node):
+ # XXX: python 2.6 only
+ self.newline()
+ self.write("print ")
+ want_comma = False
+ if node.dest is not None:
+ self.write(" >> ")
+ self.visit(node.dest)
+ want_comma = True
+ for value in node.values:
+ if want_comma:
+ self.write(", ")
+ self.visit(value)
+ want_comma = True
+ if not node.nl:
+ self.write(",")
+
+ def visit_Delete(self, node):
+ self.newline()
+ self.write("del ")
+ for idx, target in enumerate(node):
+ if idx:
+ self.write(", ")
+ self.visit(target)
+
+ def visit_TryExcept(self, node):
+ self.newline()
+ self.write("try:")
+ self.body(node.body)
+ for handler in node.handlers:
+ self.visit(handler)
+
+ def visit_TryFinally(self, node):
+ self.newline()
+ self.write("try:")
+ self.body(node.body)
+ self.newline()
+ self.write("finally:")
+ self.body(node.finalbody)
+
+ def visit_Global(self, node):
+ self.newline()
+ self.write("global " + ", ".join(node.names))
+
+ def visit_Nonlocal(self, node):
+ self.newline()
+ self.write("nonlocal " + ", ".join(node.names))
+
+ def visit_Return(self, node):
+ self.newline()
+ self.write("return ")
+ self.visit(node.value)
+
+ def visit_Break(self, node):
+ self.newline()
+ self.write("break")
+
+ def visit_Continue(self, node):
+ self.newline()
+ self.write("continue")
+
+ def visit_Raise(self, node):
+ # XXX: Python 2.6 / 3.0 compatibility
+ self.newline()
+ self.write("raise")
+ if hasattr(node, "exc") and node.exc is not None:
+ self.write(" ")
+ self.visit(node.exc)
+ if node.cause is not None:
+ self.write(" from ")
+ self.visit(node.cause)
+ elif hasattr(node, "type") and node.type is not None:
+ self.visit(node.type)
+ if node.inst is not None:
+ self.write(", ")
+ self.visit(node.inst)
+ if node.tback is not None:
+ self.write(", ")
+ self.visit(node.tback)
+
+ # Expressions
+
+ def visit_Attribute(self, node):
+ self.visit(node.value)
+ self.write("." + node.attr)
+
+ def visit_Call(self, node):
+ want_comma = []
+
+ def write_comma():
+ if want_comma:
+ self.write(", ")
+ else:
+ want_comma.append(True)
+
+ self.visit(node.func)
+ self.write("(")
+ for arg in node.args:
+ write_comma()
+ self.visit(arg)
+ for keyword in node.keywords:
+ write_comma()
+ self.write(keyword.arg + "=")
+ self.visit(keyword.value)
+ if getattr(node, "starargs", None):
+ write_comma()
+ self.write("*")
+ self.visit(node.starargs)
+ if getattr(node, "kwargs", None):
+ write_comma()
+ self.write("**")
+ self.visit(node.kwargs)
+ self.write(")")
+
+ def visit_Name(self, node):
+ self.write(node.id)
+
+ def visit_NameConstant(self, node):
+ self.write(str(node.value))
+
+ def visit_arg(self, node):
+ self.write(node.arg)
+
+ def visit_Str(self, node):
+ self.write(repr(node.s))
+
+ def visit_Bytes(self, node):
+ self.write(repr(node.s))
+
+ def visit_Num(self, node):
+ self.write(repr(node.n))
+
+ # newly needed in Python 3.8
+ def visit_Constant(self, node):
+ self.write(repr(node.value))
+
+ def visit_Tuple(self, node):
+ self.write("(")
+ idx = -1
+ for idx, item in enumerate(node.elts):
+ if idx:
+ self.write(", ")
+ self.visit(item)
+ self.write(idx and ")" or ",)")
+
+ def sequence_visit(left, right):
+ def visit(self, node):
+ self.write(left)
+ for idx, item in enumerate(node.elts):
+ if idx:
+ self.write(", ")
+ self.visit(item)
+ self.write(right)
+
+ return visit
+
+ visit_List = sequence_visit("[", "]")
+ visit_Set = sequence_visit("{", "}")
+ del sequence_visit
+
+ def visit_Dict(self, node):
+ self.write("{")
+ for idx, (key, value) in enumerate(zip(node.keys, node.values)):
+ if idx:
+ self.write(", ")
+ self.visit(key)
+ self.write(": ")
+ self.visit(value)
+ self.write("}")
+
+ def visit_BinOp(self, node):
+ self.write("(")
+ self.visit(node.left)
+ self.write(" %s " % BINOP_SYMBOLS[type(node.op)])
+ self.visit(node.right)
+ self.write(")")
+
+ def visit_BoolOp(self, node):
+ self.write("(")
+ for idx, value in enumerate(node.values):
+ if idx:
+ self.write(" %s " % BOOLOP_SYMBOLS[type(node.op)])
+ self.visit(value)
+ self.write(")")
+
+ def visit_Compare(self, node):
+ self.write("(")
+ self.visit(node.left)
+ for op, right in zip(node.ops, node.comparators):
+ self.write(" %s " % CMPOP_SYMBOLS[type(op)])
+ self.visit(right)
+ self.write(")")
+
+ def visit_UnaryOp(self, node):
+ self.write("(")
+ op = UNARYOP_SYMBOLS[type(node.op)]
+ self.write(op)
+ if op == "not":
+ self.write(" ")
+ self.visit(node.operand)
+ self.write(")")
+
+ def visit_Subscript(self, node):
+ self.visit(node.value)
+ self.write("[")
+ self.visit(node.slice)
+ self.write("]")
+
+ def visit_Slice(self, node):
+ if node.lower is not None:
+ self.visit(node.lower)
+ self.write(":")
+ if node.upper is not None:
+ self.visit(node.upper)
+ if node.step is not None:
+ self.write(":")
+ if not (isinstance(node.step, Name) and node.step.id == "None"):
+ self.visit(node.step)
+
+ def visit_ExtSlice(self, node):
+ for idx, item in node.dims:
+ if idx:
+ self.write(", ")
+ self.visit(item)
+
+ def visit_Yield(self, node):
+ self.write("yield ")
+ self.visit(node.value)
+
+ def visit_Lambda(self, node):
+ self.write("lambda ")
+ self.signature(node.args)
+ self.write(": ")
+ self.visit(node.body)
+
+ def visit_Ellipsis(self, node):
+ self.write("Ellipsis")
+
+ def generator_visit(left, right):
+ def visit(self, node):
+ self.write(left)
+ self.visit(node.elt)
+ for comprehension in node.generators:
+ self.visit(comprehension)
+ self.write(right)
+
+ return visit
+
+ visit_ListComp = generator_visit("[", "]")
+ visit_GeneratorExp = generator_visit("(", ")")
+ visit_SetComp = generator_visit("{", "}")
+ del generator_visit
+
+ def visit_DictComp(self, node):
+ self.write("{")
+ self.visit(node.key)
+ self.write(": ")
+ self.visit(node.value)
+ for comprehension in node.generators:
+ self.visit(comprehension)
+ self.write("}")
+
+ def visit_IfExp(self, node):
+ self.visit(node.body)
+ self.write(" if ")
+ self.visit(node.test)
+ self.write(" else ")
+ self.visit(node.orelse)
+
+ def visit_Starred(self, node):
+ self.write("*")
+ self.visit(node.value)
+
+ def visit_Repr(self, node):
+ # XXX: python 2.6 only
+ self.write("`")
+ self.visit(node.value)
+ self.write("`")
+
+ # Helper Nodes
+
+ def visit_alias(self, node):
+ self.write(node.name)
+ if node.asname is not None:
+ self.write(" as " + node.asname)
+
+ def visit_comprehension(self, node):
+ self.write(" for ")
+ self.visit(node.target)
+ self.write(" in ")
+ self.visit(node.iter)
+ if node.ifs:
+ for if_ in node.ifs:
+ self.write(" if ")
+ self.visit(if_)
+
+ def visit_excepthandler(self, node):
+ self.newline()
+ self.write("except")
+ if node.type is not None:
+ self.write(" ")
+ self.visit(node.type)
+ if node.name is not None:
+ self.write(" as ")
+ self.visit(node.name)
+ self.write(":")
+ self.body(node.body)
diff --git a/third_party/python/Mako/mako/ast.py b/third_party/python/Mako/mako/ast.py
new file mode 100644
index 0000000000..cfae28062c
--- /dev/null
+++ b/third_party/python/Mako/mako/ast.py
@@ -0,0 +1,205 @@
+# mako/ast.py
+# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
+#
+# This module is part of Mako and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+
+"""utilities for analyzing expressions and blocks of Python
+code, as well as generating Python from AST nodes"""
+
+import re
+
+from mako import compat
+from mako import exceptions
+from mako import pyparser
+
+
+class PythonCode(object):
+
+ """represents information about a string containing Python code"""
+
+ def __init__(self, code, **exception_kwargs):
+ self.code = code
+
+ # represents all identifiers which are assigned to at some point in
+ # the code
+ self.declared_identifiers = set()
+
+ # represents all identifiers which are referenced before their
+ # assignment, if any
+ self.undeclared_identifiers = set()
+
+ # note that an identifier can be in both the undeclared and declared
+ # lists.
+
+ # using AST to parse instead of using code.co_varnames,
+ # code.co_names has several advantages:
+ # - we can locate an identifier as "undeclared" even if
+ # its declared later in the same block of code
+ # - AST is less likely to break with version changes
+ # (for example, the behavior of co_names changed a little bit
+ # in python version 2.5)
+ if isinstance(code, compat.string_types):
+ expr = pyparser.parse(code.lstrip(), "exec", **exception_kwargs)
+ else:
+ expr = code
+
+ f = pyparser.FindIdentifiers(self, **exception_kwargs)
+ f.visit(expr)
+
+
+class ArgumentList(object):
+
+ """parses a fragment of code as a comma-separated list of expressions"""
+
+ def __init__(self, code, **exception_kwargs):
+ self.codeargs = []
+ self.args = []
+ self.declared_identifiers = set()
+ self.undeclared_identifiers = set()
+ if isinstance(code, compat.string_types):
+ if re.match(r"\S", code) and not re.match(r",\s*$", code):
+ # if theres text and no trailing comma, insure its parsed
+ # as a tuple by adding a trailing comma
+ code += ","
+ expr = pyparser.parse(code, "exec", **exception_kwargs)
+ else:
+ expr = code
+
+ f = pyparser.FindTuple(self, PythonCode, **exception_kwargs)
+ f.visit(expr)
+
+
+class PythonFragment(PythonCode):
+
+ """extends PythonCode to provide identifier lookups in partial control
+ statements
+
+ e.g.::
+
+ for x in 5:
+ elif y==9:
+ except (MyException, e):
+
+ """
+
+ def __init__(self, code, **exception_kwargs):
+ m = re.match(r"^(\w+)(?:\s+(.*?))?:\s*(#|$)", code.strip(), re.S)
+ if not m:
+ raise exceptions.CompileException(
+ "Fragment '%s' is not a partial control statement" % code,
+ **exception_kwargs
+ )
+ if m.group(3):
+ code = code[: m.start(3)]
+ (keyword, expr) = m.group(1, 2)
+ if keyword in ["for", "if", "while"]:
+ code = code + "pass"
+ elif keyword == "try":
+ code = code + "pass\nexcept:pass"
+ elif keyword == "elif" or keyword == "else":
+ code = "if False:pass\n" + code + "pass"
+ elif keyword == "except":
+ code = "try:pass\n" + code + "pass"
+ elif keyword == "with":
+ code = code + "pass"
+ else:
+ raise exceptions.CompileException(
+ "Unsupported control keyword: '%s'" % keyword,
+ **exception_kwargs
+ )
+ super(PythonFragment, self).__init__(code, **exception_kwargs)
+
+
+class FunctionDecl(object):
+
+ """function declaration"""
+
+ def __init__(self, code, allow_kwargs=True, **exception_kwargs):
+ self.code = code
+ expr = pyparser.parse(code, "exec", **exception_kwargs)
+
+ f = pyparser.ParseFunc(self, **exception_kwargs)
+ f.visit(expr)
+ if not hasattr(self, "funcname"):
+ raise exceptions.CompileException(
+ "Code '%s' is not a function declaration" % code,
+ **exception_kwargs
+ )
+ if not allow_kwargs and self.kwargs:
+ raise exceptions.CompileException(
+ "'**%s' keyword argument not allowed here"
+ % self.kwargnames[-1],
+ **exception_kwargs
+ )
+
+ def get_argument_expressions(self, as_call=False):
+ """Return the argument declarations of this FunctionDecl as a printable
+ list.
+
+ By default the return value is appropriate for writing in a ``def``;
+ set `as_call` to true to build arguments to be passed to the function
+ instead (assuming locals with the same names as the arguments exist).
+ """
+
+ namedecls = []
+
+ # Build in reverse order, since defaults and slurpy args come last
+ argnames = self.argnames[::-1]
+ kwargnames = self.kwargnames[::-1]
+ defaults = self.defaults[::-1]
+ kwdefaults = self.kwdefaults[::-1]
+
+ # Named arguments
+ if self.kwargs:
+ namedecls.append("**" + kwargnames.pop(0))
+
+ for name in kwargnames:
+ # Keyword-only arguments must always be used by name, so even if
+ # this is a call, print out `foo=foo`
+ if as_call:
+ namedecls.append("%s=%s" % (name, name))
+ elif kwdefaults:
+ default = kwdefaults.pop(0)
+ if default is None:
+ # The AST always gives kwargs a default, since you can do
+ # `def foo(*, a=1, b, c=3)`
+ namedecls.append(name)
+ else:
+ namedecls.append(
+ "%s=%s"
+ % (name, pyparser.ExpressionGenerator(default).value())
+ )
+ else:
+ namedecls.append(name)
+
+ # Positional arguments
+ if self.varargs:
+ namedecls.append("*" + argnames.pop(0))
+
+ for name in argnames:
+ if as_call or not defaults:
+ namedecls.append(name)
+ else:
+ default = defaults.pop(0)
+ namedecls.append(
+ "%s=%s"
+ % (name, pyparser.ExpressionGenerator(default).value())
+ )
+
+ namedecls.reverse()
+ return namedecls
+
+ @property
+ def allargnames(self):
+ return tuple(self.argnames) + tuple(self.kwargnames)
+
+
+class FunctionArgs(FunctionDecl):
+
+ """the argument portion of a function declaration"""
+
+ def __init__(self, code, **kwargs):
+ super(FunctionArgs, self).__init__(
+ "def ANON(%s):pass" % code, **kwargs
+ )
diff --git a/third_party/python/Mako/mako/cache.py b/third_party/python/Mako/mako/cache.py
new file mode 100644
index 0000000000..26aa93ee38
--- /dev/null
+++ b/third_party/python/Mako/mako/cache.py
@@ -0,0 +1,240 @@
+# mako/cache.py
+# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
+#
+# This module is part of Mako and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+
+from mako import compat
+from mako import util
+
+_cache_plugins = util.PluginLoader("mako.cache")
+
+register_plugin = _cache_plugins.register
+register_plugin("beaker", "mako.ext.beaker_cache", "BeakerCacheImpl")
+
+
+class Cache(object):
+
+ """Represents a data content cache made available to the module
+ space of a specific :class:`.Template` object.
+
+ .. versionadded:: 0.6
+ :class:`.Cache` by itself is mostly a
+ container for a :class:`.CacheImpl` object, which implements
+ a fixed API to provide caching services; specific subclasses exist to
+ implement different
+ caching strategies. Mako includes a backend that works with
+ the Beaker caching system. Beaker itself then supports
+ a number of backends (i.e. file, memory, memcached, etc.)
+
+ The construction of a :class:`.Cache` is part of the mechanics
+ of a :class:`.Template`, and programmatic access to this
+ cache is typically via the :attr:`.Template.cache` attribute.
+
+ """
+
+ impl = None
+ """Provide the :class:`.CacheImpl` in use by this :class:`.Cache`.
+
+ This accessor allows a :class:`.CacheImpl` with additional
+ methods beyond that of :class:`.Cache` to be used programmatically.
+
+ """
+
+ id = None
+ """Return the 'id' that identifies this cache.
+
+ This is a value that should be globally unique to the
+ :class:`.Template` associated with this cache, and can
+ be used by a caching system to name a local container
+ for data specific to this template.
+
+ """
+
+ starttime = None
+ """Epochal time value for when the owning :class:`.Template` was
+ first compiled.
+
+ A cache implementation may wish to invalidate data earlier than
+ this timestamp; this has the effect of the cache for a specific
+ :class:`.Template` starting clean any time the :class:`.Template`
+ is recompiled, such as when the original template file changed on
+ the filesystem.
+
+ """
+
+ def __init__(self, template, *args):
+ # check for a stale template calling the
+ # constructor
+ if isinstance(template, compat.string_types) and args:
+ return
+ self.template = template
+ self.id = template.module.__name__
+ self.starttime = template.module._modified_time
+ self._def_regions = {}
+ self.impl = self._load_impl(self.template.cache_impl)
+
+ def _load_impl(self, name):
+ return _cache_plugins.load(name)(self)
+
+ def get_or_create(self, key, creation_function, **kw):
+ """Retrieve a value from the cache, using the given creation function
+ to generate a new value."""
+
+ return self._ctx_get_or_create(key, creation_function, None, **kw)
+
+ def _ctx_get_or_create(self, key, creation_function, context, **kw):
+ """Retrieve a value from the cache, using the given creation function
+ to generate a new value."""
+
+ if not self.template.cache_enabled:
+ return creation_function()
+
+ return self.impl.get_or_create(
+ key, creation_function, **self._get_cache_kw(kw, context)
+ )
+
+ def set(self, key, value, **kw):
+ r"""Place a value in the cache.
+
+ :param key: the value's key.
+ :param value: the value.
+ :param \**kw: cache configuration arguments.
+
+ """
+
+ self.impl.set(key, value, **self._get_cache_kw(kw, None))
+
+ put = set
+ """A synonym for :meth:`.Cache.set`.
+
+ This is here for backwards compatibility.
+
+ """
+
+ def get(self, key, **kw):
+ r"""Retrieve a value from the cache.
+
+ :param key: the value's key.
+ :param \**kw: cache configuration arguments. The
+ backend is configured using these arguments upon first request.
+ Subsequent requests that use the same series of configuration
+ values will use that same backend.
+
+ """
+ return self.impl.get(key, **self._get_cache_kw(kw, None))
+
+ def invalidate(self, key, **kw):
+ r"""Invalidate a value in the cache.
+
+ :param key: the value's key.
+ :param \**kw: cache configuration arguments. The
+ backend is configured using these arguments upon first request.
+ Subsequent requests that use the same series of configuration
+ values will use that same backend.
+
+ """
+ self.impl.invalidate(key, **self._get_cache_kw(kw, None))
+
+ def invalidate_body(self):
+ """Invalidate the cached content of the "body" method for this
+ template.
+
+ """
+ self.invalidate("render_body", __M_defname="render_body")
+
+ def invalidate_def(self, name):
+ """Invalidate the cached content of a particular ``<%def>`` within this
+ template.
+
+ """
+
+ self.invalidate("render_%s" % name, __M_defname="render_%s" % name)
+
+ def invalidate_closure(self, name):
+ """Invalidate a nested ``<%def>`` within this template.
+
+ Caching of nested defs is a blunt tool as there is no
+ management of scope -- nested defs that use cache tags
+ need to have names unique of all other nested defs in the
+ template, else their content will be overwritten by
+ each other.
+
+ """
+
+ self.invalidate(name, __M_defname=name)
+
+ def _get_cache_kw(self, kw, context):
+ defname = kw.pop("__M_defname", None)
+ if not defname:
+ tmpl_kw = self.template.cache_args.copy()
+ tmpl_kw.update(kw)
+ elif defname in self._def_regions:
+ tmpl_kw = self._def_regions[defname]
+ else:
+ tmpl_kw = self.template.cache_args.copy()
+ tmpl_kw.update(kw)
+ self._def_regions[defname] = tmpl_kw
+ if context and self.impl.pass_context:
+ tmpl_kw = tmpl_kw.copy()
+ tmpl_kw.setdefault("context", context)
+ return tmpl_kw
+
+
+class CacheImpl(object):
+
+ """Provide a cache implementation for use by :class:`.Cache`."""
+
+ def __init__(self, cache):
+ self.cache = cache
+
+ pass_context = False
+ """If ``True``, the :class:`.Context` will be passed to
+ :meth:`get_or_create <.CacheImpl.get_or_create>` as the name ``'context'``.
+ """
+
+ def get_or_create(self, key, creation_function, **kw):
+ r"""Retrieve a value from the cache, using the given creation function
+ to generate a new value.
+
+ This function *must* return a value, either from
+ the cache, or via the given creation function.
+ If the creation function is called, the newly
+ created value should be populated into the cache
+ under the given key before being returned.
+
+ :param key: the value's key.
+ :param creation_function: function that when called generates
+ a new value.
+ :param \**kw: cache configuration arguments.
+
+ """
+ raise NotImplementedError()
+
+ def set(self, key, value, **kw):
+ r"""Place a value in the cache.
+
+ :param key: the value's key.
+ :param value: the value.
+ :param \**kw: cache configuration arguments.
+
+ """
+ raise NotImplementedError()
+
+ def get(self, key, **kw):
+ r"""Retrieve a value from the cache.
+
+ :param key: the value's key.
+ :param \**kw: cache configuration arguments.
+
+ """
+ raise NotImplementedError()
+
+ def invalidate(self, key, **kw):
+ r"""Invalidate a value in the cache.
+
+ :param key: the value's key.
+ :param \**kw: cache configuration arguments.
+
+ """
+ raise NotImplementedError()
diff --git a/third_party/python/Mako/mako/cmd.py b/third_party/python/Mako/mako/cmd.py
new file mode 100644
index 0000000000..c0f2c754ec
--- /dev/null
+++ b/third_party/python/Mako/mako/cmd.py
@@ -0,0 +1,103 @@
+# mako/cmd.py
+# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
+#
+# This module is part of Mako and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+from argparse import ArgumentParser
+import io
+from os.path import dirname
+from os.path import isfile
+import sys
+
+from mako import exceptions
+from mako.lookup import TemplateLookup
+from mako.template import Template
+
+
+def varsplit(var):
+ if "=" not in var:
+ return (var, "")
+ return var.split("=", 1)
+
+
+def _exit():
+ sys.stderr.write(exceptions.text_error_template().render())
+ sys.exit(1)
+
+
+def cmdline(argv=None):
+
+ parser = ArgumentParser()
+ parser.add_argument(
+ "--var",
+ default=[],
+ action="append",
+ help="variable (can be used multiple times, use name=value)",
+ )
+ parser.add_argument(
+ "--template-dir",
+ default=[],
+ action="append",
+ help="Directory to use for template lookup (multiple "
+ "directories may be provided). If not given then if the "
+ "template is read from stdin, the value defaults to be "
+ "the current directory, otherwise it defaults to be the "
+ "parent directory of the file provided.",
+ )
+ parser.add_argument(
+ "--output-encoding", default=None, help="force output encoding"
+ )
+ parser.add_argument(
+ "--output-file",
+ default=None,
+ help="Write to file upon successful render instead of stdout",
+ )
+ parser.add_argument("input", nargs="?", default="-")
+
+ options = parser.parse_args(argv)
+
+ output_encoding = options.output_encoding
+ output_file = options.output_file
+
+ if options.input == "-":
+ lookup_dirs = options.template_dir or ["."]
+ lookup = TemplateLookup(lookup_dirs)
+ try:
+ template = Template(
+ sys.stdin.read(),
+ lookup=lookup,
+ output_encoding=output_encoding,
+ )
+ except:
+ _exit()
+ else:
+ filename = options.input
+ if not isfile(filename):
+ raise SystemExit("error: can't find %s" % filename)
+ lookup_dirs = options.template_dir or [dirname(filename)]
+ lookup = TemplateLookup(lookup_dirs)
+ try:
+ template = Template(
+ filename=filename,
+ lookup=lookup,
+ output_encoding=output_encoding,
+ )
+ except:
+ _exit()
+
+ kw = dict([varsplit(var) for var in options.var])
+ try:
+ rendered = template.render(**kw)
+ except:
+ _exit()
+ else:
+ if output_file:
+ io.open(output_file, "wt", encoding=output_encoding).write(
+ rendered
+ )
+ else:
+ sys.stdout.write(rendered)
+
+
+if __name__ == "__main__":
+ cmdline()
diff --git a/third_party/python/Mako/mako/codegen.py b/third_party/python/Mako/mako/codegen.py
new file mode 100644
index 0000000000..a9ae55b847
--- /dev/null
+++ b/third_party/python/Mako/mako/codegen.py
@@ -0,0 +1,1318 @@
+# mako/codegen.py
+# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
+#
+# This module is part of Mako and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+
+"""provides functionality for rendering a parsetree constructing into module
+source code."""
+
+import json
+import re
+import time
+
+from mako import ast
+from mako import compat
+from mako import exceptions
+from mako import filters
+from mako import parsetree
+from mako import util
+from mako.pygen import PythonPrinter
+
+
+MAGIC_NUMBER = 10
+
+# names which are hardwired into the
+# template and are not accessed via the
+# context itself
+TOPLEVEL_DECLARED = set(["UNDEFINED", "STOP_RENDERING"])
+RESERVED_NAMES = set(["context", "loop"]).union(TOPLEVEL_DECLARED)
+
+
+def compile( # noqa
+ node,
+ uri,
+ filename=None,
+ default_filters=None,
+ buffer_filters=None,
+ imports=None,
+ future_imports=None,
+ source_encoding=None,
+ generate_magic_comment=True,
+ disable_unicode=False,
+ strict_undefined=False,
+ enable_loop=True,
+ reserved_names=frozenset(),
+):
+ """Generate module source code given a parsetree node,
+ uri, and optional source filename"""
+
+ # if on Py2K, push the "source_encoding" string to be
+ # a bytestring itself, as we will be embedding it into
+ # the generated source and we don't want to coerce the
+ # result into a unicode object, in "disable_unicode" mode
+ if not compat.py3k and isinstance(source_encoding, compat.text_type):
+ source_encoding = source_encoding.encode(source_encoding)
+
+ buf = util.FastEncodingBuffer()
+
+ printer = PythonPrinter(buf)
+ _GenerateRenderMethod(
+ printer,
+ _CompileContext(
+ uri,
+ filename,
+ default_filters,
+ buffer_filters,
+ imports,
+ future_imports,
+ source_encoding,
+ generate_magic_comment,
+ disable_unicode,
+ strict_undefined,
+ enable_loop,
+ reserved_names,
+ ),
+ node,
+ )
+ return buf.getvalue()
+
+
+class _CompileContext(object):
+ def __init__(
+ self,
+ uri,
+ filename,
+ default_filters,
+ buffer_filters,
+ imports,
+ future_imports,
+ source_encoding,
+ generate_magic_comment,
+ disable_unicode,
+ strict_undefined,
+ enable_loop,
+ reserved_names,
+ ):
+ self.uri = uri
+ self.filename = filename
+ self.default_filters = default_filters
+ self.buffer_filters = buffer_filters
+ self.imports = imports
+ self.future_imports = future_imports
+ self.source_encoding = source_encoding
+ self.generate_magic_comment = generate_magic_comment
+ self.disable_unicode = disable_unicode
+ self.strict_undefined = strict_undefined
+ self.enable_loop = enable_loop
+ self.reserved_names = reserved_names
+
+
+class _GenerateRenderMethod(object):
+
+ """A template visitor object which generates the
+ full module source for a template.
+
+ """
+
+ def __init__(self, printer, compiler, node):
+ self.printer = printer
+ self.compiler = compiler
+ self.node = node
+ self.identifier_stack = [None]
+ self.in_def = isinstance(node, (parsetree.DefTag, parsetree.BlockTag))
+
+ if self.in_def:
+ name = "render_%s" % node.funcname
+ args = node.get_argument_expressions()
+ filtered = len(node.filter_args.args) > 0
+ buffered = eval(node.attributes.get("buffered", "False"))
+ cached = eval(node.attributes.get("cached", "False"))
+ defs = None
+ pagetag = None
+ if node.is_block and not node.is_anonymous:
+ args += ["**pageargs"]
+ else:
+ defs = self.write_toplevel()
+ pagetag = self.compiler.pagetag
+ name = "render_body"
+ if pagetag is not None:
+ args = pagetag.body_decl.get_argument_expressions()
+ if not pagetag.body_decl.kwargs:
+ args += ["**pageargs"]
+ cached = eval(pagetag.attributes.get("cached", "False"))
+ self.compiler.enable_loop = self.compiler.enable_loop or eval(
+ pagetag.attributes.get("enable_loop", "False")
+ )
+ else:
+ args = ["**pageargs"]
+ cached = False
+ buffered = filtered = False
+ if args is None:
+ args = ["context"]
+ else:
+ args = [a for a in ["context"] + args]
+
+ self.write_render_callable(
+ pagetag or node, name, args, buffered, filtered, cached
+ )
+
+ if defs is not None:
+ for node in defs:
+ _GenerateRenderMethod(printer, compiler, node)
+
+ if not self.in_def:
+ self.write_metadata_struct()
+
+ def write_metadata_struct(self):
+ self.printer.source_map[self.printer.lineno] = max(
+ self.printer.source_map
+ )
+ struct = {
+ "filename": self.compiler.filename,
+ "uri": self.compiler.uri,
+ "source_encoding": self.compiler.source_encoding,
+ "line_map": self.printer.source_map,
+ }
+ self.printer.writelines(
+ '"""',
+ "__M_BEGIN_METADATA",
+ json.dumps(struct),
+ "__M_END_METADATA\n" '"""',
+ )
+
+ @property
+ def identifiers(self):
+ return self.identifier_stack[-1]
+
+ def write_toplevel(self):
+ """Traverse a template structure for module-level directives and
+ generate the start of module-level code.
+
+ """
+ inherit = []
+ namespaces = {}
+ module_code = []
+
+ self.compiler.pagetag = None
+
+ class FindTopLevel(object):
+ def visitInheritTag(s, node):
+ inherit.append(node)
+
+ def visitNamespaceTag(s, node):
+ namespaces[node.name] = node
+
+ def visitPageTag(s, node):
+ self.compiler.pagetag = node
+
+ def visitCode(s, node):
+ if node.ismodule:
+ module_code.append(node)
+
+ f = FindTopLevel()
+ for n in self.node.nodes:
+ n.accept_visitor(f)
+
+ self.compiler.namespaces = namespaces
+
+ module_ident = set()
+ for n in module_code:
+ module_ident = module_ident.union(n.declared_identifiers())
+
+ module_identifiers = _Identifiers(self.compiler)
+ module_identifiers.declared = module_ident
+
+ # module-level names, python code
+ if (
+ self.compiler.generate_magic_comment
+ and self.compiler.source_encoding
+ ):
+ self.printer.writeline(
+ "# -*- coding:%s -*-" % self.compiler.source_encoding
+ )
+
+ if self.compiler.future_imports:
+ self.printer.writeline(
+ "from __future__ import %s"
+ % (", ".join(self.compiler.future_imports),)
+ )
+ self.printer.writeline("from mako import runtime, filters, cache")
+ self.printer.writeline("UNDEFINED = runtime.UNDEFINED")
+ self.printer.writeline("STOP_RENDERING = runtime.STOP_RENDERING")
+ self.printer.writeline("__M_dict_builtin = dict")
+ self.printer.writeline("__M_locals_builtin = locals")
+ self.printer.writeline("_magic_number = %r" % MAGIC_NUMBER)
+ self.printer.writeline("_modified_time = %r" % time.time())
+ self.printer.writeline("_enable_loop = %r" % self.compiler.enable_loop)
+ self.printer.writeline(
+ "_template_filename = %r" % self.compiler.filename
+ )
+ self.printer.writeline("_template_uri = %r" % self.compiler.uri)
+ self.printer.writeline(
+ "_source_encoding = %r" % self.compiler.source_encoding
+ )
+ if self.compiler.imports:
+ buf = ""
+ for imp in self.compiler.imports:
+ buf += imp + "\n"
+ self.printer.writeline(imp)
+ impcode = ast.PythonCode(
+ buf,
+ source="",
+ lineno=0,
+ pos=0,
+ filename="template defined imports",
+ )
+ else:
+ impcode = None
+
+ main_identifiers = module_identifiers.branch(self.node)
+ mit = module_identifiers.topleveldefs
+ module_identifiers.topleveldefs = mit.union(
+ main_identifiers.topleveldefs
+ )
+ module_identifiers.declared.update(TOPLEVEL_DECLARED)
+ if impcode:
+ module_identifiers.declared.update(impcode.declared_identifiers)
+
+ self.compiler.identifiers = module_identifiers
+ self.printer.writeline(
+ "_exports = %r"
+ % [n.name for n in main_identifiers.topleveldefs.values()]
+ )
+ self.printer.write_blanks(2)
+
+ if len(module_code):
+ self.write_module_code(module_code)
+
+ if len(inherit):
+ self.write_namespaces(namespaces)
+ self.write_inherit(inherit[-1])
+ elif len(namespaces):
+ self.write_namespaces(namespaces)
+
+ return list(main_identifiers.topleveldefs.values())
+
+ def write_render_callable(
+ self, node, name, args, buffered, filtered, cached
+ ):
+ """write a top-level render callable.
+
+ this could be the main render() method or that of a top-level def."""
+
+ if self.in_def:
+ decorator = node.decorator
+ if decorator:
+ self.printer.writeline(
+ "@runtime._decorate_toplevel(%s)" % decorator
+ )
+
+ self.printer.start_source(node.lineno)
+ self.printer.writelines(
+ "def %s(%s):" % (name, ",".join(args)),
+ # push new frame, assign current frame to __M_caller
+ "__M_caller = context.caller_stack._push_frame()",
+ "try:",
+ )
+ if buffered or filtered or cached:
+ self.printer.writeline("context._push_buffer()")
+
+ self.identifier_stack.append(
+ self.compiler.identifiers.branch(self.node)
+ )
+ if (not self.in_def or self.node.is_block) and "**pageargs" in args:
+ self.identifier_stack[-1].argument_declared.add("pageargs")
+
+ if not self.in_def and (
+ len(self.identifiers.locally_assigned) > 0
+ or len(self.identifiers.argument_declared) > 0
+ ):
+ self.printer.writeline(
+ "__M_locals = __M_dict_builtin(%s)"
+ % ",".join(
+ [
+ "%s=%s" % (x, x)
+ for x in self.identifiers.argument_declared
+ ]
+ )
+ )
+
+ self.write_variable_declares(self.identifiers, toplevel=True)
+
+ for n in self.node.nodes:
+ n.accept_visitor(self)
+
+ self.write_def_finish(self.node, buffered, filtered, cached)
+ self.printer.writeline(None)
+ self.printer.write_blanks(2)
+ if cached:
+ self.write_cache_decorator(
+ node, name, args, buffered, self.identifiers, toplevel=True
+ )
+
+ def write_module_code(self, module_code):
+ """write module-level template code, i.e. that which
+ is enclosed in <%! %> tags in the template."""
+ for n in module_code:
+ self.printer.write_indented_block(n.text, starting_lineno=n.lineno)
+
+ def write_inherit(self, node):
+ """write the module-level inheritance-determination callable."""
+
+ self.printer.writelines(
+ "def _mako_inherit(template, context):",
+ "_mako_generate_namespaces(context)",
+ "return runtime._inherit_from(context, %s, _template_uri)"
+ % (node.parsed_attributes["file"]),
+ None,
+ )
+
+ def write_namespaces(self, namespaces):
+ """write the module-level namespace-generating callable."""
+ self.printer.writelines(
+ "def _mako_get_namespace(context, name):",
+ "try:",
+ "return context.namespaces[(__name__, name)]",
+ "except KeyError:",
+ "_mako_generate_namespaces(context)",
+ "return context.namespaces[(__name__, name)]",
+ None,
+ None,
+ )
+ self.printer.writeline("def _mako_generate_namespaces(context):")
+
+ for node in namespaces.values():
+ if "import" in node.attributes:
+ self.compiler.has_ns_imports = True
+ self.printer.start_source(node.lineno)
+ if len(node.nodes):
+ self.printer.writeline("def make_namespace():")
+ export = []
+ identifiers = self.compiler.identifiers.branch(node)
+ self.in_def = True
+
+ class NSDefVisitor(object):
+ def visitDefTag(s, node):
+ s.visitDefOrBase(node)
+
+ def visitBlockTag(s, node):
+ s.visitDefOrBase(node)
+
+ def visitDefOrBase(s, node):
+ if node.is_anonymous:
+ raise exceptions.CompileException(
+ "Can't put anonymous blocks inside "
+ "<%namespace>",
+ **node.exception_kwargs
+ )
+ self.write_inline_def(node, identifiers, nested=False)
+ export.append(node.funcname)
+
+ vis = NSDefVisitor()
+ for n in node.nodes:
+ n.accept_visitor(vis)
+ self.printer.writeline("return [%s]" % (",".join(export)))
+ self.printer.writeline(None)
+ self.in_def = False
+ callable_name = "make_namespace()"
+ else:
+ callable_name = "None"
+
+ if "file" in node.parsed_attributes:
+ self.printer.writeline(
+ "ns = runtime.TemplateNamespace(%r,"
+ " context._clean_inheritance_tokens(),"
+ " templateuri=%s, callables=%s, "
+ " calling_uri=_template_uri)"
+ % (
+ node.name,
+ node.parsed_attributes.get("file", "None"),
+ callable_name,
+ )
+ )
+ elif "module" in node.parsed_attributes:
+ self.printer.writeline(
+ "ns = runtime.ModuleNamespace(%r,"
+ " context._clean_inheritance_tokens(),"
+ " callables=%s, calling_uri=_template_uri,"
+ " module=%s)"
+ % (
+ node.name,
+ callable_name,
+ node.parsed_attributes.get("module", "None"),
+ )
+ )
+ else:
+ self.printer.writeline(
+ "ns = runtime.Namespace(%r,"
+ " context._clean_inheritance_tokens(),"
+ " callables=%s, calling_uri=_template_uri)"
+ % (node.name, callable_name)
+ )
+ if eval(node.attributes.get("inheritable", "False")):
+ self.printer.writeline("context['self'].%s = ns" % (node.name))
+
+ self.printer.writeline(
+ "context.namespaces[(__name__, %s)] = ns" % repr(node.name)
+ )
+ self.printer.write_blanks(1)
+ if not len(namespaces):
+ self.printer.writeline("pass")
+ self.printer.writeline(None)
+
+ def write_variable_declares(self, identifiers, toplevel=False, limit=None):
+ """write variable declarations at the top of a function.
+
+ the variable declarations are in the form of callable
+ definitions for defs and/or name lookup within the
+ function's context argument. the names declared are based
+ on the names that are referenced in the function body,
+ which don't otherwise have any explicit assignment
+ operation. names that are assigned within the body are
+ assumed to be locally-scoped variables and are not
+ separately declared.
+
+ for def callable definitions, if the def is a top-level
+ callable then a 'stub' callable is generated which wraps
+ the current Context into a closure. if the def is not
+ top-level, it is fully rendered as a local closure.
+
+ """
+
+ # collection of all defs available to us in this scope
+ comp_idents = dict([(c.funcname, c) for c in identifiers.defs])
+ to_write = set()
+
+ # write "context.get()" for all variables we are going to
+ # need that arent in the namespace yet
+ to_write = to_write.union(identifiers.undeclared)
+
+ # write closure functions for closures that we define
+ # right here
+ to_write = to_write.union(
+ [c.funcname for c in identifiers.closuredefs.values()]
+ )
+
+ # remove identifiers that are declared in the argument
+ # signature of the callable
+ to_write = to_write.difference(identifiers.argument_declared)
+
+ # remove identifiers that we are going to assign to.
+ # in this way we mimic Python's behavior,
+ # i.e. assignment to a variable within a block
+ # means that variable is now a "locally declared" var,
+ # which cannot be referenced beforehand.
+ to_write = to_write.difference(identifiers.locally_declared)
+
+ if self.compiler.enable_loop:
+ has_loop = "loop" in to_write
+ to_write.discard("loop")
+ else:
+ has_loop = False
+
+ # if a limiting set was sent, constraint to those items in that list
+ # (this is used for the caching decorator)
+ if limit is not None:
+ to_write = to_write.intersection(limit)
+
+ if toplevel and getattr(self.compiler, "has_ns_imports", False):
+ self.printer.writeline("_import_ns = {}")
+ self.compiler.has_imports = True
+ for ident, ns in self.compiler.namespaces.items():
+ if "import" in ns.attributes:
+ self.printer.writeline(
+ "_mako_get_namespace(context, %r)."
+ "_populate(_import_ns, %r)"
+ % (
+ ident,
+ re.split(r"\s*,\s*", ns.attributes["import"]),
+ )
+ )
+
+ if has_loop:
+ self.printer.writeline("loop = __M_loop = runtime.LoopStack()")
+
+ for ident in to_write:
+ if ident in comp_idents:
+ comp = comp_idents[ident]
+ if comp.is_block:
+ if not comp.is_anonymous:
+ self.write_def_decl(comp, identifiers)
+ else:
+ self.write_inline_def(comp, identifiers, nested=True)
+ else:
+ if comp.is_root():
+ self.write_def_decl(comp, identifiers)
+ else:
+ self.write_inline_def(comp, identifiers, nested=True)
+
+ elif ident in self.compiler.namespaces:
+ self.printer.writeline(
+ "%s = _mako_get_namespace(context, %r)" % (ident, ident)
+ )
+ else:
+ if getattr(self.compiler, "has_ns_imports", False):
+ if self.compiler.strict_undefined:
+ self.printer.writelines(
+ "%s = _import_ns.get(%r, UNDEFINED)"
+ % (ident, ident),
+ "if %s is UNDEFINED:" % ident,
+ "try:",
+ "%s = context[%r]" % (ident, ident),
+ "except KeyError:",
+ "raise NameError(\"'%s' is not defined\")" % ident,
+ None,
+ None,
+ )
+ else:
+ self.printer.writeline(
+ "%s = _import_ns.get"
+ "(%r, context.get(%r, UNDEFINED))"
+ % (ident, ident, ident)
+ )
+ else:
+ if self.compiler.strict_undefined:
+ self.printer.writelines(
+ "try:",
+ "%s = context[%r]" % (ident, ident),
+ "except KeyError:",
+ "raise NameError(\"'%s' is not defined\")" % ident,
+ None,
+ )
+ else:
+ self.printer.writeline(
+ "%s = context.get(%r, UNDEFINED)" % (ident, ident)
+ )
+
+ self.printer.writeline("__M_writer = context.writer()")
+
+ def write_def_decl(self, node, identifiers):
+ """write a locally-available callable referencing a top-level def"""
+ funcname = node.funcname
+ namedecls = node.get_argument_expressions()
+ nameargs = node.get_argument_expressions(as_call=True)
+
+ if not self.in_def and (
+ len(self.identifiers.locally_assigned) > 0
+ or len(self.identifiers.argument_declared) > 0
+ ):
+ nameargs.insert(0, "context._locals(__M_locals)")
+ else:
+ nameargs.insert(0, "context")
+ self.printer.writeline("def %s(%s):" % (funcname, ",".join(namedecls)))
+ self.printer.writeline(
+ "return render_%s(%s)" % (funcname, ",".join(nameargs))
+ )
+ self.printer.writeline(None)
+
+ def write_inline_def(self, node, identifiers, nested):
+ """write a locally-available def callable inside an enclosing def."""
+
+ namedecls = node.get_argument_expressions()
+
+ decorator = node.decorator
+ if decorator:
+ self.printer.writeline(
+ "@runtime._decorate_inline(context, %s)" % decorator
+ )
+ self.printer.writeline(
+ "def %s(%s):" % (node.funcname, ",".join(namedecls))
+ )
+ filtered = len(node.filter_args.args) > 0
+ buffered = eval(node.attributes.get("buffered", "False"))
+ cached = eval(node.attributes.get("cached", "False"))
+ self.printer.writelines(
+ # push new frame, assign current frame to __M_caller
+ "__M_caller = context.caller_stack._push_frame()",
+ "try:",
+ )
+ if buffered or filtered or cached:
+ self.printer.writelines("context._push_buffer()")
+
+ identifiers = identifiers.branch(node, nested=nested)
+
+ self.write_variable_declares(identifiers)
+
+ self.identifier_stack.append(identifiers)
+ for n in node.nodes:
+ n.accept_visitor(self)
+ self.identifier_stack.pop()
+
+ self.write_def_finish(node, buffered, filtered, cached)
+ self.printer.writeline(None)
+ if cached:
+ self.write_cache_decorator(
+ node,
+ node.funcname,
+ namedecls,
+ False,
+ identifiers,
+ inline=True,
+ toplevel=False,
+ )
+
+ def write_def_finish(
+ self, node, buffered, filtered, cached, callstack=True
+ ):
+ """write the end section of a rendering function, either outermost or
+ inline.
+
+ this takes into account if the rendering function was filtered,
+ buffered, etc. and closes the corresponding try: block if any, and
+ writes code to retrieve captured content, apply filters, send proper
+ return value."""
+
+ if not buffered and not cached and not filtered:
+ self.printer.writeline("return ''")
+ if callstack:
+ self.printer.writelines(
+ "finally:", "context.caller_stack._pop_frame()", None
+ )
+
+ if buffered or filtered or cached:
+ if buffered or cached:
+ # in a caching scenario, don't try to get a writer
+ # from the context after popping; assume the caching
+ # implemenation might be using a context with no
+ # extra buffers
+ self.printer.writelines(
+ "finally:", "__M_buf = context._pop_buffer()"
+ )
+ else:
+ self.printer.writelines(
+ "finally:",
+ "__M_buf, __M_writer = context._pop_buffer_and_writer()",
+ )
+
+ if callstack:
+ self.printer.writeline("context.caller_stack._pop_frame()")
+
+ s = "__M_buf.getvalue()"
+ if filtered:
+ s = self.create_filter_callable(
+ node.filter_args.args, s, False
+ )
+ self.printer.writeline(None)
+ if buffered and not cached:
+ s = self.create_filter_callable(
+ self.compiler.buffer_filters, s, False
+ )
+ if buffered or cached:
+ self.printer.writeline("return %s" % s)
+ else:
+ self.printer.writelines("__M_writer(%s)" % s, "return ''")
+
+ def write_cache_decorator(
+ self,
+ node_or_pagetag,
+ name,
+ args,
+ buffered,
+ identifiers,
+ inline=False,
+ toplevel=False,
+ ):
+ """write a post-function decorator to replace a rendering
+ callable with a cached version of itself."""
+
+ self.printer.writeline("__M_%s = %s" % (name, name))
+ cachekey = node_or_pagetag.parsed_attributes.get(
+ "cache_key", repr(name)
+ )
+
+ cache_args = {}
+ if self.compiler.pagetag is not None:
+ cache_args.update(
+ (pa[6:], self.compiler.pagetag.parsed_attributes[pa])
+ for pa in self.compiler.pagetag.parsed_attributes
+ if pa.startswith("cache_") and pa != "cache_key"
+ )
+ cache_args.update(
+ (pa[6:], node_or_pagetag.parsed_attributes[pa])
+ for pa in node_or_pagetag.parsed_attributes
+ if pa.startswith("cache_") and pa != "cache_key"
+ )
+ if "timeout" in cache_args:
+ cache_args["timeout"] = int(eval(cache_args["timeout"]))
+
+ self.printer.writeline("def %s(%s):" % (name, ",".join(args)))
+
+ # form "arg1, arg2, arg3=arg3, arg4=arg4", etc.
+ pass_args = [
+ "%s=%s" % ((a.split("=")[0],) * 2) if "=" in a else a for a in args
+ ]
+
+ self.write_variable_declares(
+ identifiers,
+ toplevel=toplevel,
+ limit=node_or_pagetag.undeclared_identifiers(),
+ )
+ if buffered:
+ s = (
+ "context.get('local')."
+ "cache._ctx_get_or_create("
+ "%s, lambda:__M_%s(%s), context, %s__M_defname=%r)"
+ % (
+ cachekey,
+ name,
+ ",".join(pass_args),
+ "".join(
+ ["%s=%s, " % (k, v) for k, v in cache_args.items()]
+ ),
+ name,
+ )
+ )
+ # apply buffer_filters
+ s = self.create_filter_callable(
+ self.compiler.buffer_filters, s, False
+ )
+ self.printer.writelines("return " + s, None)
+ else:
+ self.printer.writelines(
+ "__M_writer(context.get('local')."
+ "cache._ctx_get_or_create("
+ "%s, lambda:__M_%s(%s), context, %s__M_defname=%r))"
+ % (
+ cachekey,
+ name,
+ ",".join(pass_args),
+ "".join(
+ ["%s=%s, " % (k, v) for k, v in cache_args.items()]
+ ),
+ name,
+ ),
+ "return ''",
+ None,
+ )
+
+ def create_filter_callable(self, args, target, is_expression):
+ """write a filter-applying expression based on the filters
+ present in the given filter names, adjusting for the global
+ 'default' filter aliases as needed."""
+
+ def locate_encode(name):
+ if re.match(r"decode\..+", name):
+ return "filters." + name
+ elif self.compiler.disable_unicode:
+ return filters.NON_UNICODE_ESCAPES.get(name, name)
+ else:
+ return filters.DEFAULT_ESCAPES.get(name, name)
+
+ if "n" not in args:
+ if is_expression:
+ if self.compiler.pagetag:
+ args = self.compiler.pagetag.filter_args.args + args
+ if self.compiler.default_filters and "n" not in args:
+ args = self.compiler.default_filters + args
+ for e in args:
+ # if filter given as a function, get just the identifier portion
+ if e == "n":
+ continue
+ m = re.match(r"(.+?)(\(.*\))", e)
+ if m:
+ ident, fargs = m.group(1, 2)
+ f = locate_encode(ident)
+ e = f + fargs
+ else:
+ e = locate_encode(e)
+ assert e is not None
+ target = "%s(%s)" % (e, target)
+ return target
+
+ def visitExpression(self, node):
+ self.printer.start_source(node.lineno)
+ if (
+ len(node.escapes)
+ or (
+ self.compiler.pagetag is not None
+ and len(self.compiler.pagetag.filter_args.args)
+ )
+ or len(self.compiler.default_filters)
+ ):
+
+ s = self.create_filter_callable(
+ node.escapes_code.args, "%s" % node.text, True
+ )
+ self.printer.writeline("__M_writer(%s)" % s)
+ else:
+ self.printer.writeline("__M_writer(%s)" % node.text)
+
+ def visitControlLine(self, node):
+ if node.isend:
+ self.printer.writeline(None)
+ if node.has_loop_context:
+ self.printer.writeline("finally:")
+ self.printer.writeline("loop = __M_loop._exit()")
+ self.printer.writeline(None)
+ else:
+ self.printer.start_source(node.lineno)
+ if self.compiler.enable_loop and node.keyword == "for":
+ text = mangle_mako_loop(node, self.printer)
+ else:
+ text = node.text
+ self.printer.writeline(text)
+ children = node.get_children()
+ # this covers the three situations where we want to insert a pass:
+ # 1) a ternary control line with no children,
+ # 2) a primary control line with nothing but its own ternary
+ # and end control lines, and
+ # 3) any control line with no content other than comments
+ if not children or (
+ compat.all(
+ isinstance(c, (parsetree.Comment, parsetree.ControlLine))
+ for c in children
+ )
+ and compat.all(
+ (node.is_ternary(c.keyword) or c.isend)
+ for c in children
+ if isinstance(c, parsetree.ControlLine)
+ )
+ ):
+ self.printer.writeline("pass")
+
+ def visitText(self, node):
+ self.printer.start_source(node.lineno)
+ self.printer.writeline("__M_writer(%s)" % repr(node.content))
+
+ def visitTextTag(self, node):
+ filtered = len(node.filter_args.args) > 0
+ if filtered:
+ self.printer.writelines(
+ "__M_writer = context._push_writer()", "try:"
+ )
+ for n in node.nodes:
+ n.accept_visitor(self)
+ if filtered:
+ self.printer.writelines(
+ "finally:",
+ "__M_buf, __M_writer = context._pop_buffer_and_writer()",
+ "__M_writer(%s)"
+ % self.create_filter_callable(
+ node.filter_args.args, "__M_buf.getvalue()", False
+ ),
+ None,
+ )
+
+ def visitCode(self, node):
+ if not node.ismodule:
+ self.printer.write_indented_block(
+ node.text, starting_lineno=node.lineno
+ )
+
+ if not self.in_def and len(self.identifiers.locally_assigned) > 0:
+ # if we are the "template" def, fudge locally
+ # declared/modified variables into the "__M_locals" dictionary,
+ # which is used for def calls within the same template,
+ # to simulate "enclosing scope"
+ self.printer.writeline(
+ "__M_locals_builtin_stored = __M_locals_builtin()"
+ )
+ self.printer.writeline(
+ "__M_locals.update(__M_dict_builtin([(__M_key,"
+ " __M_locals_builtin_stored[__M_key]) for __M_key in"
+ " [%s] if __M_key in __M_locals_builtin_stored]))"
+ % ",".join([repr(x) for x in node.declared_identifiers()])
+ )
+
+ def visitIncludeTag(self, node):
+ self.printer.start_source(node.lineno)
+ args = node.attributes.get("args")
+ if args:
+ self.printer.writeline(
+ "runtime._include_file(context, %s, _template_uri, %s)"
+ % (node.parsed_attributes["file"], args)
+ )
+ else:
+ self.printer.writeline(
+ "runtime._include_file(context, %s, _template_uri)"
+ % (node.parsed_attributes["file"])
+ )
+
+ def visitNamespaceTag(self, node):
+ pass
+
+ def visitDefTag(self, node):
+ pass
+
+ def visitBlockTag(self, node):
+ if node.is_anonymous:
+ self.printer.writeline("%s()" % node.funcname)
+ else:
+ nameargs = node.get_argument_expressions(as_call=True)
+ nameargs += ["**pageargs"]
+ self.printer.writeline(
+ "if 'parent' not in context._data or "
+ "not hasattr(context._data['parent'], '%s'):" % node.funcname
+ )
+ self.printer.writeline(
+ "context['self'].%s(%s)" % (node.funcname, ",".join(nameargs))
+ )
+ self.printer.writeline("\n")
+
+ def visitCallNamespaceTag(self, node):
+ # TODO: we can put namespace-specific checks here, such
+ # as ensure the given namespace will be imported,
+ # pre-import the namespace, etc.
+ self.visitCallTag(node)
+
+ def visitCallTag(self, node):
+ self.printer.writeline("def ccall(caller):")
+ export = ["body"]
+ callable_identifiers = self.identifiers.branch(node, nested=True)
+ body_identifiers = callable_identifiers.branch(node, nested=False)
+ # we want the 'caller' passed to ccall to be used
+ # for the body() function, but for other non-body()
+ # <%def>s within <%call> we want the current caller
+ # off the call stack (if any)
+ body_identifiers.add_declared("caller")
+
+ self.identifier_stack.append(body_identifiers)
+
+ class DefVisitor(object):
+ def visitDefTag(s, node):
+ s.visitDefOrBase(node)
+
+ def visitBlockTag(s, node):
+ s.visitDefOrBase(node)
+
+ def visitDefOrBase(s, node):
+ self.write_inline_def(node, callable_identifiers, nested=False)
+ if not node.is_anonymous:
+ export.append(node.funcname)
+ # remove defs that are within the <%call> from the
+ # "closuredefs" defined in the body, so they dont render twice
+ if node.funcname in body_identifiers.closuredefs:
+ del body_identifiers.closuredefs[node.funcname]
+
+ vis = DefVisitor()
+ for n in node.nodes:
+ n.accept_visitor(vis)
+ self.identifier_stack.pop()
+
+ bodyargs = node.body_decl.get_argument_expressions()
+ self.printer.writeline("def body(%s):" % ",".join(bodyargs))
+
+ # TODO: figure out best way to specify
+ # buffering/nonbuffering (at call time would be better)
+ buffered = False
+ if buffered:
+ self.printer.writelines("context._push_buffer()", "try:")
+ self.write_variable_declares(body_identifiers)
+ self.identifier_stack.append(body_identifiers)
+
+ for n in node.nodes:
+ n.accept_visitor(self)
+ self.identifier_stack.pop()
+
+ self.write_def_finish(node, buffered, False, False, callstack=False)
+ self.printer.writelines(None, "return [%s]" % (",".join(export)), None)
+
+ self.printer.writelines(
+ # push on caller for nested call
+ "context.caller_stack.nextcaller = "
+ "runtime.Namespace('caller', context, "
+ "callables=ccall(__M_caller))",
+ "try:",
+ )
+ self.printer.start_source(node.lineno)
+ self.printer.writelines(
+ "__M_writer(%s)"
+ % self.create_filter_callable([], node.expression, True),
+ "finally:",
+ "context.caller_stack.nextcaller = None",
+ None,
+ )
+
+
+class _Identifiers(object):
+
+ """tracks the status of identifier names as template code is rendered."""
+
+ def __init__(self, compiler, node=None, parent=None, nested=False):
+ if parent is not None:
+ # if we are the branch created in write_namespaces(),
+ # we don't share any context from the main body().
+ if isinstance(node, parsetree.NamespaceTag):
+ self.declared = set()
+ self.topleveldefs = util.SetLikeDict()
+ else:
+ # things that have already been declared
+ # in an enclosing namespace (i.e. names we can just use)
+ self.declared = (
+ set(parent.declared)
+ .union([c.name for c in parent.closuredefs.values()])
+ .union(parent.locally_declared)
+ .union(parent.argument_declared)
+ )
+
+ # if these identifiers correspond to a "nested"
+ # scope, it means whatever the parent identifiers
+ # had as undeclared will have been declared by that parent,
+ # and therefore we have them in our scope.
+ if nested:
+ self.declared = self.declared.union(parent.undeclared)
+
+ # top level defs that are available
+ self.topleveldefs = util.SetLikeDict(**parent.topleveldefs)
+ else:
+ self.declared = set()
+ self.topleveldefs = util.SetLikeDict()
+
+ self.compiler = compiler
+
+ # things within this level that are referenced before they
+ # are declared (e.g. assigned to)
+ self.undeclared = set()
+
+ # things that are declared locally. some of these things
+ # could be in the "undeclared" list as well if they are
+ # referenced before declared
+ self.locally_declared = set()
+
+ # assignments made in explicit python blocks.
+ # these will be propagated to
+ # the context of local def calls.
+ self.locally_assigned = set()
+
+ # things that are declared in the argument
+ # signature of the def callable
+ self.argument_declared = set()
+
+ # closure defs that are defined in this level
+ self.closuredefs = util.SetLikeDict()
+
+ self.node = node
+
+ if node is not None:
+ node.accept_visitor(self)
+
+ illegal_names = self.compiler.reserved_names.intersection(
+ self.locally_declared
+ )
+ if illegal_names:
+ raise exceptions.NameConflictError(
+ "Reserved words declared in template: %s"
+ % ", ".join(illegal_names)
+ )
+
+ def branch(self, node, **kwargs):
+ """create a new Identifiers for a new Node, with
+ this Identifiers as the parent."""
+
+ return _Identifiers(self.compiler, node, self, **kwargs)
+
+ @property
+ def defs(self):
+ return set(self.topleveldefs.union(self.closuredefs).values())
+
+ def __repr__(self):
+ return (
+ "Identifiers(declared=%r, locally_declared=%r, "
+ "undeclared=%r, topleveldefs=%r, closuredefs=%r, "
+ "argumentdeclared=%r)"
+ % (
+ list(self.declared),
+ list(self.locally_declared),
+ list(self.undeclared),
+ [c.name for c in self.topleveldefs.values()],
+ [c.name for c in self.closuredefs.values()],
+ self.argument_declared,
+ )
+ )
+
+ def check_declared(self, node):
+ """update the state of this Identifiers with the undeclared
+ and declared identifiers of the given node."""
+
+ for ident in node.undeclared_identifiers():
+ if ident != "context" and ident not in self.declared.union(
+ self.locally_declared
+ ):
+ self.undeclared.add(ident)
+ for ident in node.declared_identifiers():
+ self.locally_declared.add(ident)
+
+ def add_declared(self, ident):
+ self.declared.add(ident)
+ if ident in self.undeclared:
+ self.undeclared.remove(ident)
+
+ def visitExpression(self, node):
+ self.check_declared(node)
+
+ def visitControlLine(self, node):
+ self.check_declared(node)
+
+ def visitCode(self, node):
+ if not node.ismodule:
+ self.check_declared(node)
+ self.locally_assigned = self.locally_assigned.union(
+ node.declared_identifiers()
+ )
+
+ def visitNamespaceTag(self, node):
+ # only traverse into the sub-elements of a
+ # <%namespace> tag if we are the branch created in
+ # write_namespaces()
+ if self.node is node:
+ for n in node.nodes:
+ n.accept_visitor(self)
+
+ def _check_name_exists(self, collection, node):
+ existing = collection.get(node.funcname)
+ collection[node.funcname] = node
+ if (
+ existing is not None
+ and existing is not node
+ and (node.is_block or existing.is_block)
+ ):
+ raise exceptions.CompileException(
+ "%%def or %%block named '%s' already "
+ "exists in this template." % node.funcname,
+ **node.exception_kwargs
+ )
+
+ def visitDefTag(self, node):
+ if node.is_root() and not node.is_anonymous:
+ self._check_name_exists(self.topleveldefs, node)
+ elif node is not self.node:
+ self._check_name_exists(self.closuredefs, node)
+
+ for ident in node.undeclared_identifiers():
+ if ident != "context" and ident not in self.declared.union(
+ self.locally_declared
+ ):
+ self.undeclared.add(ident)
+
+ # visit defs only one level deep
+ if node is self.node:
+ for ident in node.declared_identifiers():
+ self.argument_declared.add(ident)
+
+ for n in node.nodes:
+ n.accept_visitor(self)
+
+ def visitBlockTag(self, node):
+ if node is not self.node and not node.is_anonymous:
+
+ if isinstance(self.node, parsetree.DefTag):
+ raise exceptions.CompileException(
+ "Named block '%s' not allowed inside of def '%s'"
+ % (node.name, self.node.name),
+ **node.exception_kwargs
+ )
+ elif isinstance(
+ self.node, (parsetree.CallTag, parsetree.CallNamespaceTag)
+ ):
+ raise exceptions.CompileException(
+ "Named block '%s' not allowed inside of <%%call> tag"
+ % (node.name,),
+ **node.exception_kwargs
+ )
+
+ for ident in node.undeclared_identifiers():
+ if ident != "context" and ident not in self.declared.union(
+ self.locally_declared
+ ):
+ self.undeclared.add(ident)
+
+ if not node.is_anonymous:
+ self._check_name_exists(self.topleveldefs, node)
+ self.undeclared.add(node.funcname)
+ elif node is not self.node:
+ self._check_name_exists(self.closuredefs, node)
+ for ident in node.declared_identifiers():
+ self.argument_declared.add(ident)
+ for n in node.nodes:
+ n.accept_visitor(self)
+
+ def visitTextTag(self, node):
+ for ident in node.undeclared_identifiers():
+ if ident != "context" and ident not in self.declared.union(
+ self.locally_declared
+ ):
+ self.undeclared.add(ident)
+
+ def visitIncludeTag(self, node):
+ self.check_declared(node)
+
+ def visitPageTag(self, node):
+ for ident in node.declared_identifiers():
+ self.argument_declared.add(ident)
+ self.check_declared(node)
+
+ def visitCallNamespaceTag(self, node):
+ self.visitCallTag(node)
+
+ def visitCallTag(self, node):
+ if node is self.node:
+ for ident in node.undeclared_identifiers():
+ if ident != "context" and ident not in self.declared.union(
+ self.locally_declared
+ ):
+ self.undeclared.add(ident)
+ for ident in node.declared_identifiers():
+ self.argument_declared.add(ident)
+ for n in node.nodes:
+ n.accept_visitor(self)
+ else:
+ for ident in node.undeclared_identifiers():
+ if ident != "context" and ident not in self.declared.union(
+ self.locally_declared
+ ):
+ self.undeclared.add(ident)
+
+
+_FOR_LOOP = re.compile(
+ r"^for\s+((?:\(?)\s*[A-Za-z_][A-Za-z_0-9]*"
+ r"(?:\s*,\s*(?:[A-Za-z_][A-Za-z0-9_]*),??)*\s*(?:\)?))\s+in\s+(.*):"
+)
+
+
+def mangle_mako_loop(node, printer):
+ """converts a for loop into a context manager wrapped around a for loop
+ when access to the `loop` variable has been detected in the for loop body
+ """
+ loop_variable = LoopVariable()
+ node.accept_visitor(loop_variable)
+ if loop_variable.detected:
+ node.nodes[-1].has_loop_context = True
+ match = _FOR_LOOP.match(node.text)
+ if match:
+ printer.writelines(
+ "loop = __M_loop._enter(%s)" % match.group(2),
+ "try:"
+ # 'with __M_loop(%s) as loop:' % match.group(2)
+ )
+ text = "for %s in loop:" % match.group(1)
+ else:
+ raise SyntaxError("Couldn't apply loop context: %s" % node.text)
+ else:
+ text = node.text
+ return text
+
+
+class LoopVariable(object):
+
+ """A node visitor which looks for the name 'loop' within undeclared
+ identifiers."""
+
+ def __init__(self):
+ self.detected = False
+
+ def _loop_reference_detected(self, node):
+ if "loop" in node.undeclared_identifiers():
+ self.detected = True
+ else:
+ for n in node.get_children():
+ n.accept_visitor(self)
+
+ def visitControlLine(self, node):
+ self._loop_reference_detected(node)
+
+ def visitCode(self, node):
+ self._loop_reference_detected(node)
+
+ def visitExpression(self, node):
+ self._loop_reference_detected(node)
diff --git a/third_party/python/Mako/mako/compat.py b/third_party/python/Mako/mako/compat.py
new file mode 100644
index 0000000000..9aac98cb5b
--- /dev/null
+++ b/third_party/python/Mako/mako/compat.py
@@ -0,0 +1,166 @@
+# mako/compat.py
+# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
+#
+# This module is part of Mako and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+
+import collections
+import inspect
+import sys
+
+py3k = sys.version_info >= (3, 0)
+py2k = sys.version_info < (3,)
+py27 = sys.version_info >= (2, 7)
+jython = sys.platform.startswith("java")
+win32 = sys.platform.startswith("win")
+pypy = hasattr(sys, "pypy_version_info")
+
+ArgSpec = collections.namedtuple(
+ "ArgSpec", ["args", "varargs", "keywords", "defaults"]
+)
+
+
+def inspect_getargspec(func):
+ """getargspec based on fully vendored getfullargspec from Python 3.3."""
+
+ if inspect.ismethod(func):
+ func = func.__func__
+ if not inspect.isfunction(func):
+ raise TypeError("{!r} is not a Python function".format(func))
+
+ co = func.__code__
+ if not inspect.iscode(co):
+ raise TypeError("{!r} is not a code object".format(co))
+
+ nargs = co.co_argcount
+ names = co.co_varnames
+ nkwargs = co.co_kwonlyargcount if py3k else 0
+ args = list(names[:nargs])
+
+ nargs += nkwargs
+ varargs = None
+ if co.co_flags & inspect.CO_VARARGS:
+ varargs = co.co_varnames[nargs]
+ nargs = nargs + 1
+ varkw = None
+ if co.co_flags & inspect.CO_VARKEYWORDS:
+ varkw = co.co_varnames[nargs]
+
+ return ArgSpec(args, varargs, varkw, func.__defaults__)
+
+
+if py3k:
+ from io import StringIO
+ import builtins as compat_builtins
+ from urllib.parse import quote_plus, unquote_plus
+ from html.entities import codepoint2name, name2codepoint
+
+ string_types = (str,)
+ binary_type = bytes
+ text_type = str
+
+ from io import BytesIO as byte_buffer
+
+ def u(s):
+ return s
+
+ def b(s):
+ return s.encode("latin-1")
+
+ def octal(lit):
+ return eval("0o" + lit)
+
+
+else:
+ import __builtin__ as compat_builtins # noqa
+
+ try:
+ from cStringIO import StringIO
+ except:
+ from StringIO import StringIO
+
+ byte_buffer = StringIO
+
+ from urllib import quote_plus, unquote_plus # noqa
+ from htmlentitydefs import codepoint2name, name2codepoint # noqa
+
+ string_types = (basestring,) # noqa
+ binary_type = str
+ text_type = unicode # noqa
+
+ def u(s):
+ return unicode(s, "utf-8") # noqa
+
+ def b(s):
+ return s
+
+ def octal(lit):
+ return eval("0" + lit)
+
+
+if py3k:
+ from importlib import machinery
+
+ def load_module(module_id, path):
+ return machinery.SourceFileLoader(module_id, path).load_module()
+
+
+else:
+ import imp
+
+ def load_module(module_id, path):
+ fp = open(path, "rb")
+ try:
+ return imp.load_source(module_id, path, fp)
+ finally:
+ fp.close()
+
+
+if py3k:
+
+ def reraise(tp, value, tb=None, cause=None):
+ if cause is not None:
+ value.__cause__ = cause
+ if value.__traceback__ is not tb:
+ raise value.with_traceback(tb)
+ raise value
+
+
+else:
+ exec(
+ "def reraise(tp, value, tb=None, cause=None):\n"
+ " raise tp, value, tb\n"
+ )
+
+
+def exception_as():
+ return sys.exc_info()[1]
+
+
+all = all # noqa
+
+
+def exception_name(exc):
+ return exc.__class__.__name__
+
+
+################################################
+# cross-compatible metaclass implementation
+# Copyright (c) 2010-2012 Benjamin Peterson
+def with_metaclass(meta, base=object):
+ """Create a base class with a metaclass."""
+ return meta("%sBase" % meta.__name__, (base,), {})
+
+
+################################################
+
+
+def arg_stringname(func_arg):
+ """Gets the string name of a kwarg or vararg
+ In Python3.4 a function's args are
+ of _ast.arg type not _ast.name
+ """
+ if hasattr(func_arg, "arg"):
+ return func_arg.arg
+ else:
+ return str(func_arg)
diff --git a/third_party/python/Mako/mako/exceptions.py b/third_party/python/Mako/mako/exceptions.py
new file mode 100644
index 0000000000..ea7b20dbdd
--- /dev/null
+++ b/third_party/python/Mako/mako/exceptions.py
@@ -0,0 +1,430 @@
+# mako/exceptions.py
+# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
+#
+# This module is part of Mako and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+
+"""exception classes"""
+
+import sys
+import traceback
+
+from mako import compat
+from mako import util
+
+
+class MakoException(Exception):
+ pass
+
+
+class RuntimeException(MakoException):
+ pass
+
+
+def _format_filepos(lineno, pos, filename):
+ if filename is None:
+ return " at line: %d char: %d" % (lineno, pos)
+ else:
+ return " in file '%s' at line: %d char: %d" % (filename, lineno, pos)
+
+
+class CompileException(MakoException):
+ def __init__(self, message, source, lineno, pos, filename):
+ MakoException.__init__(
+ self, message + _format_filepos(lineno, pos, filename)
+ )
+ self.lineno = lineno
+ self.pos = pos
+ self.filename = filename
+ self.source = source
+
+
+class SyntaxException(MakoException):
+ def __init__(self, message, source, lineno, pos, filename):
+ MakoException.__init__(
+ self, message + _format_filepos(lineno, pos, filename)
+ )
+ self.lineno = lineno
+ self.pos = pos
+ self.filename = filename
+ self.source = source
+
+
+class UnsupportedError(MakoException):
+
+ """raised when a retired feature is used."""
+
+
+class NameConflictError(MakoException):
+
+ """raised when a reserved word is used inappropriately"""
+
+
+class TemplateLookupException(MakoException):
+ pass
+
+
+class TopLevelLookupException(TemplateLookupException):
+ pass
+
+
+class RichTraceback(object):
+
+ """Pull the current exception from the ``sys`` traceback and extracts
+ Mako-specific template information.
+
+ See the usage examples in :ref:`handling_exceptions`.
+
+ """
+
+ def __init__(self, error=None, traceback=None):
+ self.source, self.lineno = "", 0
+
+ if error is None or traceback is None:
+ t, value, tback = sys.exc_info()
+
+ if error is None:
+ error = value or t
+
+ if traceback is None:
+ traceback = tback
+
+ self.error = error
+ self.records = self._init(traceback)
+
+ if isinstance(self.error, (CompileException, SyntaxException)):
+ self.source = self.error.source
+ self.lineno = self.error.lineno
+ self._has_source = True
+
+ self._init_message()
+
+ @property
+ def errorname(self):
+ return compat.exception_name(self.error)
+
+ def _init_message(self):
+ """Find a unicode representation of self.error"""
+ try:
+ self.message = compat.text_type(self.error)
+ except UnicodeError:
+ try:
+ self.message = str(self.error)
+ except UnicodeEncodeError:
+ # Fallback to args as neither unicode nor
+ # str(Exception(u'\xe6')) work in Python < 2.6
+ self.message = self.error.args[0]
+ if not isinstance(self.message, compat.text_type):
+ self.message = compat.text_type(self.message, "ascii", "replace")
+
+ def _get_reformatted_records(self, records):
+ for rec in records:
+ if rec[6] is not None:
+ yield (rec[4], rec[5], rec[2], rec[6])
+ else:
+ yield tuple(rec[0:4])
+
+ @property
+ def traceback(self):
+ """Return a list of 4-tuple traceback records (i.e. normal python
+ format) with template-corresponding lines remapped to the originating
+ template.
+
+ """
+ return list(self._get_reformatted_records(self.records))
+
+ @property
+ def reverse_records(self):
+ return reversed(self.records)
+
+ @property
+ def reverse_traceback(self):
+ """Return the same data as traceback, except in reverse order.
+ """
+
+ return list(self._get_reformatted_records(self.reverse_records))
+
+ def _init(self, trcback):
+ """format a traceback from sys.exc_info() into 7-item tuples,
+ containing the regular four traceback tuple items, plus the original
+ template filename, the line number adjusted relative to the template
+ source, and code line from that line number of the template."""
+
+ import mako.template
+
+ mods = {}
+ rawrecords = traceback.extract_tb(trcback)
+ new_trcback = []
+ for filename, lineno, function, line in rawrecords:
+ if not line:
+ line = ""
+ try:
+ (line_map, template_lines, template_filename) = mods[filename]
+ except KeyError:
+ try:
+ info = mako.template._get_module_info(filename)
+ module_source = info.code
+ template_source = info.source
+ template_filename = (
+ info.template_filename or info.template_uri or filename
+ )
+ except KeyError:
+ # A normal .py file (not a Template)
+ if not compat.py3k:
+ try:
+ fp = open(filename, "rb")
+ encoding = util.parse_encoding(fp)
+ fp.close()
+ except IOError:
+ encoding = None
+ if encoding:
+ line = line.decode(encoding)
+ else:
+ line = line.decode("ascii", "replace")
+ new_trcback.append(
+ (
+ filename,
+ lineno,
+ function,
+ line,
+ None,
+ None,
+ None,
+ None,
+ )
+ )
+ continue
+
+ template_ln = 1
+
+ mtm = mako.template.ModuleInfo
+ source_map = mtm.get_module_source_metadata(
+ module_source, full_line_map=True
+ )
+ line_map = source_map["full_line_map"]
+
+ template_lines = [
+ line_ for line_ in template_source.split("\n")
+ ]
+ mods[filename] = (line_map, template_lines, template_filename)
+
+ template_ln = line_map[lineno - 1]
+
+ if template_ln <= len(template_lines):
+ template_line = template_lines[template_ln - 1]
+ else:
+ template_line = None
+ new_trcback.append(
+ (
+ filename,
+ lineno,
+ function,
+ line,
+ template_filename,
+ template_ln,
+ template_line,
+ template_source,
+ )
+ )
+ if not self.source:
+ for l in range(len(new_trcback) - 1, 0, -1):
+ if new_trcback[l][5]:
+ self.source = new_trcback[l][7]
+ self.lineno = new_trcback[l][5]
+ break
+ else:
+ if new_trcback:
+ try:
+ # A normal .py file (not a Template)
+ fp = open(new_trcback[-1][0], "rb")
+ encoding = util.parse_encoding(fp)
+ if compat.py3k and not encoding:
+ encoding = "utf-8"
+ fp.seek(0)
+ self.source = fp.read()
+ fp.close()
+ if encoding:
+ self.source = self.source.decode(encoding)
+ except IOError:
+ self.source = ""
+ self.lineno = new_trcback[-1][1]
+ return new_trcback
+
+
+def text_error_template(lookup=None):
+ """Provides a template that renders a stack trace in a similar format to
+ the Python interpreter, substituting source template filenames, line
+ numbers and code for that of the originating source template, as
+ applicable.
+
+ """
+ import mako.template
+
+ return mako.template.Template(
+ r"""
+<%page args="error=None, traceback=None"/>
+<%!
+ from mako.exceptions import RichTraceback
+%>\
+<%
+ tback = RichTraceback(error=error, traceback=traceback)
+%>\
+Traceback (most recent call last):
+% for (filename, lineno, function, line) in tback.traceback:
+ File "${filename}", line ${lineno}, in ${function or '?'}
+ ${line | trim}
+% endfor
+${tback.errorname}: ${tback.message}
+"""
+ )
+
+
+def _install_pygments():
+ global syntax_highlight, pygments_html_formatter
+ from mako.ext.pygmentplugin import syntax_highlight # noqa
+ from mako.ext.pygmentplugin import pygments_html_formatter # noqa
+
+
+def _install_fallback():
+ global syntax_highlight, pygments_html_formatter
+ from mako.filters import html_escape
+
+ pygments_html_formatter = None
+
+ def syntax_highlight(filename="", language=None):
+ return html_escape
+
+
+def _install_highlighting():
+ try:
+ _install_pygments()
+ except ImportError:
+ _install_fallback()
+
+
+_install_highlighting()
+
+
+def html_error_template():
+ """Provides a template that renders a stack trace in an HTML format,
+ providing an excerpt of code as well as substituting source template
+ filenames, line numbers and code for that of the originating source
+ template, as applicable.
+
+ The template's default ``encoding_errors`` value is
+ ``'htmlentityreplace'``. The template has two options. With the
+ ``full`` option disabled, only a section of an HTML document is
+ returned. With the ``css`` option disabled, the default stylesheet
+ won't be included.
+
+ """
+ import mako.template
+
+ return mako.template.Template(
+ r"""
+<%!
+ from mako.exceptions import RichTraceback, syntax_highlight,\
+ pygments_html_formatter
+%>
+<%page args="full=True, css=True, error=None, traceback=None"/>
+% if full:
+<html>
+<head>
+ <title>Mako Runtime Error</title>
+% endif
+% if css:
+ <style>
+ body { font-family:verdana; margin:10px 30px 10px 30px;}
+ .stacktrace { margin:5px 5px 5px 5px; }
+ .highlight { padding:0px 10px 0px 10px; background-color:#9F9FDF; }
+ .nonhighlight { padding:0px; background-color:#DFDFDF; }
+ .sample { padding:10px; margin:10px 10px 10px 10px;
+ font-family:monospace; }
+ .sampleline { padding:0px 10px 0px 10px; }
+ .sourceline { margin:5px 5px 10px 5px; font-family:monospace;}
+ .location { font-size:80%; }
+ .highlight { white-space:pre; }
+ .sampleline { white-space:pre; }
+
+ % if pygments_html_formatter:
+ ${pygments_html_formatter.get_style_defs()}
+ .linenos { min-width: 2.5em; text-align: right; }
+ pre { margin: 0; }
+ .syntax-highlighted { padding: 0 10px; }
+ .syntax-highlightedtable { border-spacing: 1px; }
+ .nonhighlight { border-top: 1px solid #DFDFDF;
+ border-bottom: 1px solid #DFDFDF; }
+ .stacktrace .nonhighlight { margin: 5px 15px 10px; }
+ .sourceline { margin: 0 0; font-family:monospace; }
+ .code { background-color: #F8F8F8; width: 100%; }
+ .error .code { background-color: #FFBDBD; }
+ .error .syntax-highlighted { background-color: #FFBDBD; }
+ % endif
+
+ </style>
+% endif
+% if full:
+</head>
+<body>
+% endif
+
+<h2>Error !</h2>
+<%
+ tback = RichTraceback(error=error, traceback=traceback)
+ src = tback.source
+ line = tback.lineno
+ if src:
+ lines = src.split('\n')
+ else:
+ lines = None
+%>
+<h3>${tback.errorname}: ${tback.message|h}</h3>
+
+% if lines:
+ <div class="sample">
+ <div class="nonhighlight">
+% for index in range(max(0, line-4),min(len(lines), line+5)):
+ <%
+ if pygments_html_formatter:
+ pygments_html_formatter.linenostart = index + 1
+ %>
+ % if index + 1 == line:
+ <%
+ if pygments_html_formatter:
+ old_cssclass = pygments_html_formatter.cssclass
+ pygments_html_formatter.cssclass = 'error ' + old_cssclass
+ %>
+ ${lines[index] | syntax_highlight(language='mako')}
+ <%
+ if pygments_html_formatter:
+ pygments_html_formatter.cssclass = old_cssclass
+ %>
+ % else:
+ ${lines[index] | syntax_highlight(language='mako')}
+ % endif
+% endfor
+ </div>
+ </div>
+% endif
+
+<div class="stacktrace">
+% for (filename, lineno, function, line) in tback.reverse_traceback:
+ <div class="location">${filename}, line ${lineno}:</div>
+ <div class="nonhighlight">
+ <%
+ if pygments_html_formatter:
+ pygments_html_formatter.linenostart = lineno
+ %>
+ <div class="sourceline">${line | syntax_highlight(filename)}</div>
+ </div>
+% endfor
+</div>
+
+% if full:
+</body>
+</html>
+% endif
+""",
+ output_encoding=sys.getdefaultencoding(),
+ encoding_errors="htmlentityreplace",
+ )
diff --git a/third_party/python/Mako/mako/ext/__init__.py b/third_party/python/Mako/mako/ext/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/third_party/python/Mako/mako/ext/__init__.py
diff --git a/third_party/python/Mako/mako/ext/autohandler.py b/third_party/python/Mako/mako/ext/autohandler.py
new file mode 100644
index 0000000000..8b1324ef00
--- /dev/null
+++ b/third_party/python/Mako/mako/ext/autohandler.py
@@ -0,0 +1,70 @@
+# ext/autohandler.py
+# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
+#
+# This module is part of Mako and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+
+"""adds autohandler functionality to Mako templates.
+
+requires that the TemplateLookup class is used with templates.
+
+usage::
+
+ <%!
+ from mako.ext.autohandler import autohandler
+ %>
+ <%inherit file="${autohandler(template, context)}"/>
+
+
+or with custom autohandler filename::
+
+ <%!
+ from mako.ext.autohandler import autohandler
+ %>
+ <%inherit file="${autohandler(template, context, name='somefilename')}"/>
+
+"""
+
+import os
+import posixpath
+import re
+
+
+def autohandler(template, context, name="autohandler"):
+ lookup = context.lookup
+ _template_uri = template.module._template_uri
+ if not lookup.filesystem_checks:
+ try:
+ return lookup._uri_cache[(autohandler, _template_uri, name)]
+ except KeyError:
+ pass
+
+ tokens = re.findall(r"([^/]+)", posixpath.dirname(_template_uri)) + [name]
+ while len(tokens):
+ path = "/" + "/".join(tokens)
+ if path != _template_uri and _file_exists(lookup, path):
+ if not lookup.filesystem_checks:
+ return lookup._uri_cache.setdefault(
+ (autohandler, _template_uri, name), path
+ )
+ else:
+ return path
+ if len(tokens) == 1:
+ break
+ tokens[-2:] = [name]
+
+ if not lookup.filesystem_checks:
+ return lookup._uri_cache.setdefault(
+ (autohandler, _template_uri, name), None
+ )
+ else:
+ return None
+
+
+def _file_exists(lookup, path):
+ psub = re.sub(r"^/", "", path)
+ for d in lookup.directories:
+ if os.path.exists(d + "/" + psub):
+ return True
+ else:
+ return False
diff --git a/third_party/python/Mako/mako/ext/babelplugin.py b/third_party/python/Mako/mako/ext/babelplugin.py
new file mode 100644
index 0000000000..76bbc5b03f
--- /dev/null
+++ b/third_party/python/Mako/mako/ext/babelplugin.py
@@ -0,0 +1,58 @@
+# ext/babelplugin.py
+# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
+#
+# This module is part of Mako and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+
+"""gettext message extraction via Babel: http://babel.edgewall.org/"""
+from babel.messages.extract import extract_python
+
+from mako.ext.extract import MessageExtractor
+
+
+class BabelMakoExtractor(MessageExtractor):
+ def __init__(self, keywords, comment_tags, options):
+ self.keywords = keywords
+ self.options = options
+ self.config = {
+ "comment-tags": u" ".join(comment_tags),
+ "encoding": options.get(
+ "input_encoding", options.get("encoding", None)
+ ),
+ }
+ super(BabelMakoExtractor, self).__init__()
+
+ def __call__(self, fileobj):
+ return self.process_file(fileobj)
+
+ def process_python(self, code, code_lineno, translator_strings):
+ comment_tags = self.config["comment-tags"]
+ for (
+ lineno,
+ funcname,
+ messages,
+ python_translator_comments,
+ ) in extract_python(code, self.keywords, comment_tags, self.options):
+ yield (
+ code_lineno + (lineno - 1),
+ funcname,
+ messages,
+ translator_strings + python_translator_comments,
+ )
+
+
+def extract(fileobj, keywords, comment_tags, options):
+ """Extract messages from Mako templates.
+
+ :param fileobj: the file-like object the messages should be extracted from
+ :param keywords: a list of keywords (i.e. function names) that should be
+ recognized as translation functions
+ :param comment_tags: a list of translator tags to search for and include
+ in the results
+ :param options: a dictionary of additional options (optional)
+ :return: an iterator over ``(lineno, funcname, message, comments)`` tuples
+ :rtype: ``iterator``
+ """
+ extractor = BabelMakoExtractor(keywords, comment_tags, options)
+ for message in extractor(fileobj):
+ yield message
diff --git a/third_party/python/Mako/mako/ext/beaker_cache.py b/third_party/python/Mako/mako/ext/beaker_cache.py
new file mode 100644
index 0000000000..f65ce43ae8
--- /dev/null
+++ b/third_party/python/Mako/mako/ext/beaker_cache.py
@@ -0,0 +1,82 @@
+# ext/beaker_cache.py
+# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
+#
+# This module is part of Mako and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+
+"""Provide a :class:`.CacheImpl` for the Beaker caching system."""
+
+from mako import exceptions
+from mako.cache import CacheImpl
+
+try:
+ from beaker import cache as beaker_cache
+except:
+ has_beaker = False
+else:
+ has_beaker = True
+
+_beaker_cache = None
+
+
+class BeakerCacheImpl(CacheImpl):
+
+ """A :class:`.CacheImpl` provided for the Beaker caching system.
+
+ This plugin is used by default, based on the default
+ value of ``'beaker'`` for the ``cache_impl`` parameter of the
+ :class:`.Template` or :class:`.TemplateLookup` classes.
+
+ """
+
+ def __init__(self, cache):
+ if not has_beaker:
+ raise exceptions.RuntimeException(
+ "Can't initialize Beaker plugin; Beaker is not installed."
+ )
+ global _beaker_cache
+ if _beaker_cache is None:
+ if "manager" in cache.template.cache_args:
+ _beaker_cache = cache.template.cache_args["manager"]
+ else:
+ _beaker_cache = beaker_cache.CacheManager()
+ super(BeakerCacheImpl, self).__init__(cache)
+
+ def _get_cache(self, **kw):
+ expiretime = kw.pop("timeout", None)
+ if "dir" in kw:
+ kw["data_dir"] = kw.pop("dir")
+ elif self.cache.template.module_directory:
+ kw["data_dir"] = self.cache.template.module_directory
+
+ if "manager" in kw:
+ kw.pop("manager")
+
+ if kw.get("type") == "memcached":
+ kw["type"] = "ext:memcached"
+
+ if "region" in kw:
+ region = kw.pop("region")
+ cache = _beaker_cache.get_cache_region(self.cache.id, region, **kw)
+ else:
+ cache = _beaker_cache.get_cache(self.cache.id, **kw)
+ cache_args = {"starttime": self.cache.starttime}
+ if expiretime:
+ cache_args["expiretime"] = expiretime
+ return cache, cache_args
+
+ def get_or_create(self, key, creation_function, **kw):
+ cache, kw = self._get_cache(**kw)
+ return cache.get(key, createfunc=creation_function, **kw)
+
+ def put(self, key, value, **kw):
+ cache, kw = self._get_cache(**kw)
+ cache.put(key, value, **kw)
+
+ def get(self, key, **kw):
+ cache, kw = self._get_cache(**kw)
+ return cache.get(key, **kw)
+
+ def invalidate(self, key, **kw):
+ cache, kw = self._get_cache(**kw)
+ cache.remove_value(key, **kw)
diff --git a/third_party/python/Mako/mako/ext/extract.py b/third_party/python/Mako/mako/ext/extract.py
new file mode 100644
index 0000000000..ad2348a523
--- /dev/null
+++ b/third_party/python/Mako/mako/ext/extract.py
@@ -0,0 +1,125 @@
+# ext/extract.py
+# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
+#
+# This module is part of Mako and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+
+import re
+
+from mako import compat
+from mako import lexer
+from mako import parsetree
+
+
+class MessageExtractor(object):
+ def process_file(self, fileobj):
+ template_node = lexer.Lexer(
+ fileobj.read(), input_encoding=self.config["encoding"]
+ ).parse()
+ for extracted in self.extract_nodes(template_node.get_children()):
+ yield extracted
+
+ def extract_nodes(self, nodes):
+ translator_comments = []
+ in_translator_comments = False
+ input_encoding = self.config["encoding"] or "ascii"
+ comment_tags = list(
+ filter(None, re.split(r"\s+", self.config["comment-tags"]))
+ )
+
+ for node in nodes:
+ child_nodes = None
+ if (
+ in_translator_comments
+ and isinstance(node, parsetree.Text)
+ and not node.content.strip()
+ ):
+ # Ignore whitespace within translator comments
+ continue
+
+ if isinstance(node, parsetree.Comment):
+ value = node.text.strip()
+ if in_translator_comments:
+ translator_comments.extend(
+ self._split_comment(node.lineno, value)
+ )
+ continue
+ for comment_tag in comment_tags:
+ if value.startswith(comment_tag):
+ in_translator_comments = True
+ translator_comments.extend(
+ self._split_comment(node.lineno, value)
+ )
+ continue
+
+ if isinstance(node, parsetree.DefTag):
+ code = node.function_decl.code
+ child_nodes = node.nodes
+ elif isinstance(node, parsetree.BlockTag):
+ code = node.body_decl.code
+ child_nodes = node.nodes
+ elif isinstance(node, parsetree.CallTag):
+ code = node.code.code
+ child_nodes = node.nodes
+ elif isinstance(node, parsetree.PageTag):
+ code = node.body_decl.code
+ elif isinstance(node, parsetree.CallNamespaceTag):
+ code = node.expression
+ child_nodes = node.nodes
+ elif isinstance(node, parsetree.ControlLine):
+ if node.isend:
+ in_translator_comments = False
+ continue
+ code = node.text
+ elif isinstance(node, parsetree.Code):
+ in_translator_comments = False
+ code = node.code.code
+ elif isinstance(node, parsetree.Expression):
+ code = node.code.code
+ else:
+ continue
+
+ # Comments don't apply unless they immediately precede the message
+ if (
+ translator_comments
+ and translator_comments[-1][0] < node.lineno - 1
+ ):
+ translator_comments = []
+
+ translator_strings = [
+ comment[1] for comment in translator_comments
+ ]
+
+ if isinstance(code, compat.text_type):
+ code = code.encode(input_encoding, "backslashreplace")
+
+ used_translator_comments = False
+ # We add extra newline to work around a pybabel bug
+ # (see python-babel/babel#274, parse_encoding dies if the first
+ # input string of the input is non-ascii)
+ # Also, because we added it, we have to subtract one from
+ # node.lineno
+ code = compat.byte_buffer(compat.b("\n") + code)
+
+ for message in self.process_python(
+ code, node.lineno - 1, translator_strings
+ ):
+ yield message
+ used_translator_comments = True
+
+ if used_translator_comments:
+ translator_comments = []
+ in_translator_comments = False
+
+ if child_nodes:
+ for extracted in self.extract_nodes(child_nodes):
+ yield extracted
+
+ @staticmethod
+ def _split_comment(lineno, comment):
+ """Return the multiline comment at lineno split into a list of
+ comment line numbers and the accompanying comment line"""
+ return [
+ (lineno + index, line)
+ for index, line in enumerate(comment.splitlines())
+ ]
diff --git a/third_party/python/Mako/mako/ext/linguaplugin.py b/third_party/python/Mako/mako/ext/linguaplugin.py
new file mode 100644
index 0000000000..0f6d165a2f
--- /dev/null
+++ b/third_party/python/Mako/mako/ext/linguaplugin.py
@@ -0,0 +1,57 @@
+# ext/linguaplugin.py
+# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
+#
+# This module is part of Mako and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+
+import io
+
+from lingua.extractors import Extractor
+from lingua.extractors import get_extractor
+from lingua.extractors import Message
+
+from mako import compat
+from mako.ext.extract import MessageExtractor
+
+
+class LinguaMakoExtractor(Extractor, MessageExtractor):
+
+ """Mako templates"""
+
+ extensions = [".mako"]
+ default_config = {"encoding": "utf-8", "comment-tags": ""}
+
+ def __call__(self, filename, options, fileobj=None):
+ self.options = options
+ self.filename = filename
+ self.python_extractor = get_extractor("x.py")
+ if fileobj is None:
+ fileobj = open(filename, "rb")
+ return self.process_file(fileobj)
+
+ def process_python(self, code, code_lineno, translator_strings):
+ source = code.getvalue().strip()
+ if source.endswith(compat.b(":")):
+ if source in (
+ compat.b("try:"),
+ compat.b("else:"),
+ ) or source.startswith(compat.b("except")):
+ source = compat.b("") # Ignore try/except and else
+ elif source.startswith(compat.b("elif")):
+ source = source[2:] # Replace "elif" with "if"
+ source += compat.b("pass")
+ code = io.BytesIO(source)
+ for msg in self.python_extractor(
+ self.filename, self.options, code, code_lineno - 1
+ ):
+ if translator_strings:
+ msg = Message(
+ msg.msgctxt,
+ msg.msgid,
+ msg.msgid_plural,
+ msg.flags,
+ compat.u(" ").join(translator_strings + [msg.comment]),
+ msg.tcomment,
+ msg.location,
+ )
+ yield msg
diff --git a/third_party/python/Mako/mako/ext/preprocessors.py b/third_party/python/Mako/mako/ext/preprocessors.py
new file mode 100644
index 0000000000..9cc0621482
--- /dev/null
+++ b/third_party/python/Mako/mako/ext/preprocessors.py
@@ -0,0 +1,20 @@
+# ext/preprocessors.py
+# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
+#
+# This module is part of Mako and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+
+"""preprocessing functions, used with the 'preprocessor'
+argument on Template, TemplateLookup"""
+
+import re
+
+
+def convert_comments(text):
+ """preprocess old style comments.
+
+ example:
+
+ from mako.ext.preprocessors import convert_comments
+ t = Template(..., preprocessor=convert_comments)"""
+ return re.sub(r"(?<=\n)\s*#[^#]", "##", text)
diff --git a/third_party/python/Mako/mako/ext/pygmentplugin.py b/third_party/python/Mako/mako/ext/pygmentplugin.py
new file mode 100644
index 0000000000..943a67a49c
--- /dev/null
+++ b/third_party/python/Mako/mako/ext/pygmentplugin.py
@@ -0,0 +1,157 @@
+# ext/pygmentplugin.py
+# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
+#
+# This module is part of Mako and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+
+from pygments import highlight
+from pygments.formatters.html import HtmlFormatter
+from pygments.lexer import bygroups
+from pygments.lexer import DelegatingLexer
+from pygments.lexer import include
+from pygments.lexer import RegexLexer
+from pygments.lexer import using
+from pygments.lexers.agile import Python3Lexer
+from pygments.lexers.agile import PythonLexer
+from pygments.lexers.web import CssLexer
+from pygments.lexers.web import HtmlLexer
+from pygments.lexers.web import JavascriptLexer
+from pygments.lexers.web import XmlLexer
+from pygments.token import Comment
+from pygments.token import Keyword
+from pygments.token import Name
+from pygments.token import Operator
+from pygments.token import Other
+from pygments.token import String
+from pygments.token import Text
+
+from mako import compat
+
+
+class MakoLexer(RegexLexer):
+ name = "Mako"
+ aliases = ["mako"]
+ filenames = ["*.mao"]
+
+ tokens = {
+ "root": [
+ (
+ r"(\s*)(\%)(\s*end(?:\w+))(\n|\Z)",
+ bygroups(Text, Comment.Preproc, Keyword, Other),
+ ),
+ (
+ r"(\s*)(\%(?!%))([^\n]*)(\n|\Z)",
+ bygroups(Text, Comment.Preproc, using(PythonLexer), Other),
+ ),
+ (
+ r"(\s*)(##[^\n]*)(\n|\Z)",
+ bygroups(Text, Comment.Preproc, Other),
+ ),
+ (r"""(?s)<%doc>.*?</%doc>""", Comment.Preproc),
+ (
+ r"(<%)([\w\.\:]+)",
+ bygroups(Comment.Preproc, Name.Builtin),
+ "tag",
+ ),
+ (
+ r"(</%)([\w\.\:]+)(>)",
+ bygroups(Comment.Preproc, Name.Builtin, Comment.Preproc),
+ ),
+ (r"<%(?=([\w\.\:]+))", Comment.Preproc, "ondeftags"),
+ (
+ r"(?s)(<%(?:!?))(.*?)(%>)",
+ bygroups(Comment.Preproc, using(PythonLexer), Comment.Preproc),
+ ),
+ (
+ r"(\$\{)(.*?)(\})",
+ bygroups(Comment.Preproc, using(PythonLexer), Comment.Preproc),
+ ),
+ (
+ r"""(?sx)
+ (.+?) # anything, followed by:
+ (?:
+ (?<=\n)(?=%(?!%)|\#\#) | # an eval or comment line
+ (?=\#\*) | # multiline comment
+ (?=</?%) | # a python block
+ # call start or end
+ (?=\$\{) | # a substitution
+ (?<=\n)(?=\s*%) |
+ # - don't consume
+ (\\\n) | # an escaped newline
+ \Z # end of string
+ )
+ """,
+ bygroups(Other, Operator),
+ ),
+ (r"\s+", Text),
+ ],
+ "ondeftags": [
+ (r"<%", Comment.Preproc),
+ (r"(?<=<%)(include|inherit|namespace|page)", Name.Builtin),
+ include("tag"),
+ ],
+ "tag": [
+ (r'((?:\w+)\s*=)\s*(".*?")', bygroups(Name.Attribute, String)),
+ (r"/?\s*>", Comment.Preproc, "#pop"),
+ (r"\s+", Text),
+ ],
+ "attr": [
+ ('".*?"', String, "#pop"),
+ ("'.*?'", String, "#pop"),
+ (r"[^\s>]+", String, "#pop"),
+ ],
+ }
+
+
+class MakoHtmlLexer(DelegatingLexer):
+ name = "HTML+Mako"
+ aliases = ["html+mako"]
+
+ def __init__(self, **options):
+ super(MakoHtmlLexer, self).__init__(HtmlLexer, MakoLexer, **options)
+
+
+class MakoXmlLexer(DelegatingLexer):
+ name = "XML+Mako"
+ aliases = ["xml+mako"]
+
+ def __init__(self, **options):
+ super(MakoXmlLexer, self).__init__(XmlLexer, MakoLexer, **options)
+
+
+class MakoJavascriptLexer(DelegatingLexer):
+ name = "JavaScript+Mako"
+ aliases = ["js+mako", "javascript+mako"]
+
+ def __init__(self, **options):
+ super(MakoJavascriptLexer, self).__init__(
+ JavascriptLexer, MakoLexer, **options
+ )
+
+
+class MakoCssLexer(DelegatingLexer):
+ name = "CSS+Mako"
+ aliases = ["css+mako"]
+
+ def __init__(self, **options):
+ super(MakoCssLexer, self).__init__(CssLexer, MakoLexer, **options)
+
+
+pygments_html_formatter = HtmlFormatter(
+ cssclass="syntax-highlighted", linenos=True
+)
+
+
+def syntax_highlight(filename="", language=None):
+ mako_lexer = MakoLexer()
+ if compat.py3k:
+ python_lexer = Python3Lexer()
+ else:
+ python_lexer = PythonLexer()
+ if filename.startswith("memory:") or language == "mako":
+ return lambda string: highlight(
+ string, mako_lexer, pygments_html_formatter
+ )
+ return lambda string: highlight(
+ string, python_lexer, pygments_html_formatter
+ )
diff --git a/third_party/python/Mako/mako/ext/turbogears.py b/third_party/python/Mako/mako/ext/turbogears.py
new file mode 100644
index 0000000000..722a6b4b40
--- /dev/null
+++ b/third_party/python/Mako/mako/ext/turbogears.py
@@ -0,0 +1,61 @@
+# ext/turbogears.py
+# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
+#
+# This module is part of Mako and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+
+from mako import compat
+from mako.lookup import TemplateLookup
+from mako.template import Template
+
+
+class TGPlugin(object):
+
+ """TurboGears compatible Template Plugin."""
+
+ def __init__(self, extra_vars_func=None, options=None, extension="mak"):
+ self.extra_vars_func = extra_vars_func
+ self.extension = extension
+ if not options:
+ options = {}
+
+ # Pull the options out and initialize the lookup
+ lookup_options = {}
+ for k, v in options.items():
+ if k.startswith("mako."):
+ lookup_options[k[5:]] = v
+ elif k in ["directories", "filesystem_checks", "module_directory"]:
+ lookup_options[k] = v
+ self.lookup = TemplateLookup(**lookup_options)
+
+ self.tmpl_options = {}
+ # transfer lookup args to template args, based on those available
+ # in getargspec
+ for kw in compat.inspect_getargspec(Template.__init__)[0]:
+ if kw in lookup_options:
+ self.tmpl_options[kw] = lookup_options[kw]
+
+ def load_template(self, templatename, template_string=None):
+ """Loads a template from a file or a string"""
+ if template_string is not None:
+ return Template(template_string, **self.tmpl_options)
+ # Translate TG dot notation to normal / template path
+ if "/" not in templatename:
+ templatename = (
+ "/" + templatename.replace(".", "/") + "." + self.extension
+ )
+
+ # Lookup template
+ return self.lookup.get_template(templatename)
+
+ def render(
+ self, info, format="html", fragment=False, template=None # noqa
+ ):
+ if isinstance(template, compat.string_types):
+ template = self.load_template(template)
+
+ # Load extra vars func if provided
+ if self.extra_vars_func:
+ info.update(self.extra_vars_func())
+
+ return template.render(**info)
diff --git a/third_party/python/Mako/mako/filters.py b/third_party/python/Mako/mako/filters.py
new file mode 100644
index 0000000000..0ae33ff486
--- /dev/null
+++ b/third_party/python/Mako/mako/filters.py
@@ -0,0 +1,219 @@
+# mako/filters.py
+# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
+#
+# This module is part of Mako and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+
+
+import codecs
+import re
+
+from mako import compat
+from mako.compat import codepoint2name
+from mako.compat import name2codepoint
+from mako.compat import quote_plus
+from mako.compat import unquote_plus
+
+xml_escapes = {
+ "&": "&amp;",
+ ">": "&gt;",
+ "<": "&lt;",
+ '"': "&#34;", # also &quot; in html-only
+ "'": "&#39;", # also &apos; in html-only
+}
+
+# XXX: &quot; is valid in HTML and XML
+# &apos; is not valid HTML, but is valid XML
+
+
+def legacy_html_escape(s):
+ """legacy HTML escape for non-unicode mode."""
+ s = s.replace("&", "&amp;")
+ s = s.replace(">", "&gt;")
+ s = s.replace("<", "&lt;")
+ s = s.replace('"', "&#34;")
+ s = s.replace("'", "&#39;")
+ return s
+
+
+try:
+ import markupsafe
+
+ html_escape = markupsafe.escape
+except ImportError:
+ html_escape = legacy_html_escape
+
+
+def xml_escape(string):
+ return re.sub(r'([&<"\'>])', lambda m: xml_escapes[m.group()], string)
+
+
+def url_escape(string):
+ # convert into a list of octets
+ string = string.encode("utf8")
+ return quote_plus(string)
+
+
+def legacy_url_escape(string):
+ # convert into a list of octets
+ return quote_plus(string)
+
+
+def url_unescape(string):
+ text = unquote_plus(string)
+ if not is_ascii_str(text):
+ text = text.decode("utf8")
+ return text
+
+
+def trim(string):
+ return string.strip()
+
+
+class Decode(object):
+ def __getattr__(self, key):
+ def decode(x):
+ if isinstance(x, compat.text_type):
+ return x
+ elif not isinstance(x, compat.binary_type):
+ return decode(str(x))
+ else:
+ return compat.text_type(x, encoding=key)
+
+ return decode
+
+
+decode = Decode()
+
+
+_ASCII_re = re.compile(r"\A[\x00-\x7f]*\Z")
+
+
+def is_ascii_str(text):
+ return isinstance(text, str) and _ASCII_re.match(text)
+
+
+################################################################
+
+
+class XMLEntityEscaper(object):
+ def __init__(self, codepoint2name, name2codepoint):
+ self.codepoint2entity = dict(
+ [
+ (c, compat.text_type("&%s;" % n))
+ for c, n in codepoint2name.items()
+ ]
+ )
+ self.name2codepoint = name2codepoint
+
+ def escape_entities(self, text):
+ """Replace characters with their character entity references.
+
+ Only characters corresponding to a named entity are replaced.
+ """
+ return compat.text_type(text).translate(self.codepoint2entity)
+
+ def __escape(self, m):
+ codepoint = ord(m.group())
+ try:
+ return self.codepoint2entity[codepoint]
+ except (KeyError, IndexError):
+ return "&#x%X;" % codepoint
+
+ __escapable = re.compile(r'["&<>]|[^\x00-\x7f]')
+
+ def escape(self, text):
+ """Replace characters with their character references.
+
+ Replace characters by their named entity references.
+ Non-ASCII characters, if they do not have a named entity reference,
+ are replaced by numerical character references.
+
+ The return value is guaranteed to be ASCII.
+ """
+ return self.__escapable.sub(
+ self.__escape, compat.text_type(text)
+ ).encode("ascii")
+
+ # XXX: This regexp will not match all valid XML entity names__.
+ # (It punts on details involving involving CombiningChars and Extenders.)
+ #
+ # .. __: http://www.w3.org/TR/2000/REC-xml-20001006#NT-EntityRef
+ __characterrefs = re.compile(
+ r"""& (?:
+ \#(\d+)
+ | \#x([\da-f]+)
+ | ( (?!\d) [:\w] [-.:\w]+ )
+ ) ;""",
+ re.X | re.UNICODE,
+ )
+
+ def __unescape(self, m):
+ dval, hval, name = m.groups()
+ if dval:
+ codepoint = int(dval)
+ elif hval:
+ codepoint = int(hval, 16)
+ else:
+ codepoint = self.name2codepoint.get(name, 0xFFFD)
+ # U+FFFD = "REPLACEMENT CHARACTER"
+ if codepoint < 128:
+ return chr(codepoint)
+ return chr(codepoint)
+
+ def unescape(self, text):
+ """Unescape character references.
+
+ All character references (both entity references and numerical
+ character references) are unescaped.
+ """
+ return self.__characterrefs.sub(self.__unescape, text)
+
+
+_html_entities_escaper = XMLEntityEscaper(codepoint2name, name2codepoint)
+
+html_entities_escape = _html_entities_escaper.escape_entities
+html_entities_unescape = _html_entities_escaper.unescape
+
+
+def htmlentityreplace_errors(ex):
+ """An encoding error handler.
+
+ This python codecs error handler replaces unencodable
+ characters with HTML entities, or, if no HTML entity exists for
+ the character, XML character references::
+
+ >>> u'The cost was \u20ac12.'.encode('latin1', 'htmlentityreplace')
+ 'The cost was &euro;12.'
+ """
+ if isinstance(ex, UnicodeEncodeError):
+ # Handle encoding errors
+ bad_text = ex.object[ex.start : ex.end]
+ text = _html_entities_escaper.escape(bad_text)
+ return (compat.text_type(text), ex.end)
+ raise ex
+
+
+codecs.register_error("htmlentityreplace", htmlentityreplace_errors)
+
+
+# TODO: options to make this dynamic per-compilation will be added in a later
+# release
+DEFAULT_ESCAPES = {
+ "x": "filters.xml_escape",
+ "h": "filters.html_escape",
+ "u": "filters.url_escape",
+ "trim": "filters.trim",
+ "entity": "filters.html_entities_escape",
+ "unicode": "unicode",
+ "decode": "decode",
+ "str": "str",
+ "n": "n",
+}
+
+if compat.py3k:
+ DEFAULT_ESCAPES.update({"unicode": "str"})
+
+NON_UNICODE_ESCAPES = DEFAULT_ESCAPES.copy()
+NON_UNICODE_ESCAPES["h"] = "filters.legacy_html_escape"
+NON_UNICODE_ESCAPES["u"] = "filters.legacy_url_escape"
diff --git a/third_party/python/Mako/mako/lexer.py b/third_party/python/Mako/mako/lexer.py
new file mode 100644
index 0000000000..a02b57f8a1
--- /dev/null
+++ b/third_party/python/Mako/mako/lexer.py
@@ -0,0 +1,490 @@
+# mako/lexer.py
+# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
+#
+# This module is part of Mako and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+
+"""provides the Lexer class for parsing template strings into parse trees."""
+
+import codecs
+import re
+
+from mako import compat
+from mako import exceptions
+from mako import parsetree
+from mako.pygen import adjust_whitespace
+
+_regexp_cache = {}
+
+
+class Lexer(object):
+ def __init__(
+ self,
+ text,
+ filename=None,
+ disable_unicode=False,
+ input_encoding=None,
+ preprocessor=None,
+ ):
+ self.text = text
+ self.filename = filename
+ self.template = parsetree.TemplateNode(self.filename)
+ self.matched_lineno = 1
+ self.matched_charpos = 0
+ self.lineno = 1
+ self.match_position = 0
+ self.tag = []
+ self.control_line = []
+ self.ternary_stack = []
+ self.disable_unicode = disable_unicode
+ self.encoding = input_encoding
+
+ if compat.py3k and disable_unicode:
+ raise exceptions.UnsupportedError(
+ "Mako for Python 3 does not " "support disabling Unicode"
+ )
+
+ if preprocessor is None:
+ self.preprocessor = []
+ elif not hasattr(preprocessor, "__iter__"):
+ self.preprocessor = [preprocessor]
+ else:
+ self.preprocessor = preprocessor
+
+ @property
+ def exception_kwargs(self):
+ return {
+ "source": self.text,
+ "lineno": self.matched_lineno,
+ "pos": self.matched_charpos,
+ "filename": self.filename,
+ }
+
+ def match(self, regexp, flags=None):
+ """compile the given regexp, cache the reg, and call match_reg()."""
+
+ try:
+ reg = _regexp_cache[(regexp, flags)]
+ except KeyError:
+ if flags:
+ reg = re.compile(regexp, flags)
+ else:
+ reg = re.compile(regexp)
+ _regexp_cache[(regexp, flags)] = reg
+
+ return self.match_reg(reg)
+
+ def match_reg(self, reg):
+ """match the given regular expression object to the current text
+ position.
+
+ if a match occurs, update the current text and line position.
+
+ """
+
+ mp = self.match_position
+
+ match = reg.match(self.text, self.match_position)
+ if match:
+ (start, end) = match.span()
+ if end == start:
+ self.match_position = end + 1
+ else:
+ self.match_position = end
+ self.matched_lineno = self.lineno
+ lines = re.findall(r"\n", self.text[mp : self.match_position])
+ cp = mp - 1
+ while cp >= 0 and cp < self.textlength and self.text[cp] != "\n":
+ cp -= 1
+ self.matched_charpos = mp - cp
+ self.lineno += len(lines)
+ # print "MATCHED:", match.group(0), "LINE START:",
+ # self.matched_lineno, "LINE END:", self.lineno
+ # print "MATCH:", regexp, "\n", self.text[mp : mp + 15], \
+ # (match and "TRUE" or "FALSE")
+ return match
+
+ def parse_until_text(self, watch_nesting, *text):
+ startpos = self.match_position
+ text_re = r"|".join(text)
+ brace_level = 0
+ paren_level = 0
+ bracket_level = 0
+ while True:
+ match = self.match(r"#.*\n")
+ if match:
+ continue
+ match = self.match(
+ r"(\"\"\"|\'\'\'|\"|\')[^\\]*?(\\.[^\\]*?)*\1", re.S
+ )
+ if match:
+ continue
+ match = self.match(r"(%s)" % text_re)
+ if match and not (
+ watch_nesting
+ and (brace_level > 0 or paren_level > 0 or bracket_level > 0)
+ ):
+ return (
+ self.text[
+ startpos : self.match_position - len(match.group(1))
+ ],
+ match.group(1),
+ )
+ elif not match:
+ match = self.match(r"(.*?)(?=\"|\'|#|%s)" % text_re, re.S)
+ if match:
+ brace_level += match.group(1).count("{")
+ brace_level -= match.group(1).count("}")
+ paren_level += match.group(1).count("(")
+ paren_level -= match.group(1).count(")")
+ bracket_level += match.group(1).count("[")
+ bracket_level -= match.group(1).count("]")
+ continue
+ raise exceptions.SyntaxException(
+ "Expected: %s" % ",".join(text), **self.exception_kwargs
+ )
+
+ def append_node(self, nodecls, *args, **kwargs):
+ kwargs.setdefault("source", self.text)
+ kwargs.setdefault("lineno", self.matched_lineno)
+ kwargs.setdefault("pos", self.matched_charpos)
+ kwargs["filename"] = self.filename
+ node = nodecls(*args, **kwargs)
+ if len(self.tag):
+ self.tag[-1].nodes.append(node)
+ else:
+ self.template.nodes.append(node)
+ # build a set of child nodes for the control line
+ # (used for loop variable detection)
+ # also build a set of child nodes on ternary control lines
+ # (used for determining if a pass needs to be auto-inserted
+ if self.control_line:
+ control_frame = self.control_line[-1]
+ control_frame.nodes.append(node)
+ if not (
+ isinstance(node, parsetree.ControlLine)
+ and control_frame.is_ternary(node.keyword)
+ ):
+ if self.ternary_stack and self.ternary_stack[-1]:
+ self.ternary_stack[-1][-1].nodes.append(node)
+ if isinstance(node, parsetree.Tag):
+ if len(self.tag):
+ node.parent = self.tag[-1]
+ self.tag.append(node)
+ elif isinstance(node, parsetree.ControlLine):
+ if node.isend:
+ self.control_line.pop()
+ self.ternary_stack.pop()
+ elif node.is_primary:
+ self.control_line.append(node)
+ self.ternary_stack.append([])
+ elif self.control_line and self.control_line[-1].is_ternary(
+ node.keyword
+ ):
+ self.ternary_stack[-1].append(node)
+ elif self.control_line and not self.control_line[-1].is_ternary(
+ node.keyword
+ ):
+ raise exceptions.SyntaxException(
+ "Keyword '%s' not a legal ternary for keyword '%s'"
+ % (node.keyword, self.control_line[-1].keyword),
+ **self.exception_kwargs
+ )
+
+ _coding_re = re.compile(r"#.*coding[:=]\s*([-\w.]+).*\r?\n")
+
+ def decode_raw_stream(self, text, decode_raw, known_encoding, filename):
+ """given string/unicode or bytes/string, determine encoding
+ from magic encoding comment, return body as unicode
+ or raw if decode_raw=False
+
+ """
+ if isinstance(text, compat.text_type):
+ m = self._coding_re.match(text)
+ encoding = m and m.group(1) or known_encoding or "ascii"
+ return encoding, text
+
+ if text.startswith(codecs.BOM_UTF8):
+ text = text[len(codecs.BOM_UTF8) :]
+ parsed_encoding = "utf-8"
+ m = self._coding_re.match(text.decode("utf-8", "ignore"))
+ if m is not None and m.group(1) != "utf-8":
+ raise exceptions.CompileException(
+ "Found utf-8 BOM in file, with conflicting "
+ "magic encoding comment of '%s'" % m.group(1),
+ text.decode("utf-8", "ignore"),
+ 0,
+ 0,
+ filename,
+ )
+ else:
+ m = self._coding_re.match(text.decode("utf-8", "ignore"))
+ if m:
+ parsed_encoding = m.group(1)
+ else:
+ parsed_encoding = known_encoding or "ascii"
+
+ if decode_raw:
+ try:
+ text = text.decode(parsed_encoding)
+ except UnicodeDecodeError:
+ raise exceptions.CompileException(
+ "Unicode decode operation of encoding '%s' failed"
+ % parsed_encoding,
+ text.decode("utf-8", "ignore"),
+ 0,
+ 0,
+ filename,
+ )
+
+ return parsed_encoding, text
+
+ def parse(self):
+ self.encoding, self.text = self.decode_raw_stream(
+ self.text, not self.disable_unicode, self.encoding, self.filename
+ )
+
+ for preproc in self.preprocessor:
+ self.text = preproc(self.text)
+
+ # push the match marker past the
+ # encoding comment.
+ self.match_reg(self._coding_re)
+
+ self.textlength = len(self.text)
+
+ while True:
+ if self.match_position > self.textlength:
+ break
+
+ if self.match_end():
+ break
+ if self.match_expression():
+ continue
+ if self.match_control_line():
+ continue
+ if self.match_comment():
+ continue
+ if self.match_tag_start():
+ continue
+ if self.match_tag_end():
+ continue
+ if self.match_python_block():
+ continue
+ if self.match_text():
+ continue
+
+ if self.match_position > self.textlength:
+ break
+ raise exceptions.CompileException("assertion failed")
+
+ if len(self.tag):
+ raise exceptions.SyntaxException(
+ "Unclosed tag: <%%%s>" % self.tag[-1].keyword,
+ **self.exception_kwargs
+ )
+ if len(self.control_line):
+ raise exceptions.SyntaxException(
+ "Unterminated control keyword: '%s'"
+ % self.control_line[-1].keyword,
+ self.text,
+ self.control_line[-1].lineno,
+ self.control_line[-1].pos,
+ self.filename,
+ )
+ return self.template
+
+ def match_tag_start(self):
+ match = self.match(
+ r"""
+ \<% # opening tag
+
+ ([\w\.\:]+) # keyword
+
+ ((?:\s+\w+|\s*=\s*|".*?"|'.*?')*) # attrname, = \
+ # sign, string expression
+
+ \s* # more whitespace
+
+ (/)?> # closing
+
+ """,
+ re.I | re.S | re.X,
+ )
+
+ if match:
+ keyword, attr, isend = match.groups()
+ self.keyword = keyword
+ attributes = {}
+ if attr:
+ for att in re.findall(
+ r"\s*(\w+)\s*=\s*(?:'([^']*)'|\"([^\"]*)\")", attr
+ ):
+ key, val1, val2 = att
+ text = val1 or val2
+ text = text.replace("\r\n", "\n")
+ attributes[key] = text
+ self.append_node(parsetree.Tag, keyword, attributes)
+ if isend:
+ self.tag.pop()
+ else:
+ if keyword == "text":
+ match = self.match(r"(.*?)(?=\</%text>)", re.S)
+ if not match:
+ raise exceptions.SyntaxException(
+ "Unclosed tag: <%%%s>" % self.tag[-1].keyword,
+ **self.exception_kwargs
+ )
+ self.append_node(parsetree.Text, match.group(1))
+ return self.match_tag_end()
+ return True
+ else:
+ return False
+
+ def match_tag_end(self):
+ match = self.match(r"\</%[\t ]*(.+?)[\t ]*>")
+ if match:
+ if not len(self.tag):
+ raise exceptions.SyntaxException(
+ "Closing tag without opening tag: </%%%s>"
+ % match.group(1),
+ **self.exception_kwargs
+ )
+ elif self.tag[-1].keyword != match.group(1):
+ raise exceptions.SyntaxException(
+ "Closing tag </%%%s> does not match tag: <%%%s>"
+ % (match.group(1), self.tag[-1].keyword),
+ **self.exception_kwargs
+ )
+ self.tag.pop()
+ return True
+ else:
+ return False
+
+ def match_end(self):
+ match = self.match(r"\Z", re.S)
+ if match:
+ string = match.group()
+ if string:
+ return string
+ else:
+ return True
+ else:
+ return False
+
+ def match_text(self):
+ match = self.match(
+ r"""
+ (.*?) # anything, followed by:
+ (
+ (?<=\n)(?=[ \t]*(?=%|\#\#)) # an eval or line-based
+ # comment preceded by a
+ # consumed newline and whitespace
+ |
+ (?=\${) # an expression
+ |
+ (?=</?[%&]) # a substitution or block or call start or end
+ # - don't consume
+ |
+ (\\\r?\n) # an escaped newline - throw away
+ |
+ \Z # end of string
+ )""",
+ re.X | re.S,
+ )
+
+ if match:
+ text = match.group(1)
+ if text:
+ self.append_node(parsetree.Text, text)
+ return True
+ else:
+ return False
+
+ def match_python_block(self):
+ match = self.match(r"<%(!)?")
+ if match:
+ line, pos = self.matched_lineno, self.matched_charpos
+ text, end = self.parse_until_text(False, r"%>")
+ # the trailing newline helps
+ # compiler.parse() not complain about indentation
+ text = adjust_whitespace(text) + "\n"
+ self.append_node(
+ parsetree.Code,
+ text,
+ match.group(1) == "!",
+ lineno=line,
+ pos=pos,
+ )
+ return True
+ else:
+ return False
+
+ def match_expression(self):
+ match = self.match(r"\${")
+ if match:
+ line, pos = self.matched_lineno, self.matched_charpos
+ text, end = self.parse_until_text(True, r"\|", r"}")
+ if end == "|":
+ escapes, end = self.parse_until_text(True, r"}")
+ else:
+ escapes = ""
+ text = text.replace("\r\n", "\n")
+ self.append_node(
+ parsetree.Expression,
+ text,
+ escapes.strip(),
+ lineno=line,
+ pos=pos,
+ )
+ return True
+ else:
+ return False
+
+ def match_control_line(self):
+ match = self.match(
+ r"(?<=^)[\t ]*(%(?!%)|##)[\t ]*((?:(?:\\r?\n)|[^\r\n])*)"
+ r"(?:\r?\n|\Z)",
+ re.M,
+ )
+ if match:
+ operator = match.group(1)
+ text = match.group(2)
+ if operator == "%":
+ m2 = re.match(r"(end)?(\w+)\s*(.*)", text)
+ if not m2:
+ raise exceptions.SyntaxException(
+ "Invalid control line: '%s'" % text,
+ **self.exception_kwargs
+ )
+ isend, keyword = m2.group(1, 2)
+ isend = isend is not None
+
+ if isend:
+ if not len(self.control_line):
+ raise exceptions.SyntaxException(
+ "No starting keyword '%s' for '%s'"
+ % (keyword, text),
+ **self.exception_kwargs
+ )
+ elif self.control_line[-1].keyword != keyword:
+ raise exceptions.SyntaxException(
+ "Keyword '%s' doesn't match keyword '%s'"
+ % (text, self.control_line[-1].keyword),
+ **self.exception_kwargs
+ )
+ self.append_node(parsetree.ControlLine, keyword, isend, text)
+ else:
+ self.append_node(parsetree.Comment, text)
+ return True
+ else:
+ return False
+
+ def match_comment(self):
+ """matches the multiline version of a comment"""
+ match = self.match(r"<%doc>(.*?)</%doc>", re.S)
+ if match:
+ self.append_node(parsetree.Comment, match.group(1))
+ return True
+ else:
+ return False
diff --git a/third_party/python/Mako/mako/lookup.py b/third_party/python/Mako/mako/lookup.py
new file mode 100644
index 0000000000..476326d4d2
--- /dev/null
+++ b/third_party/python/Mako/mako/lookup.py
@@ -0,0 +1,372 @@
+# mako/lookup.py
+# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
+#
+# This module is part of Mako and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+
+import os
+import posixpath
+import re
+import stat
+
+from mako import exceptions
+from mako import util
+from mako.template import Template
+
+try:
+ import threading
+except:
+ import dummy_threading as threading
+
+
+class TemplateCollection(object):
+
+ """Represent a collection of :class:`.Template` objects,
+ identifiable via URI.
+
+ A :class:`.TemplateCollection` is linked to the usage of
+ all template tags that address other templates, such
+ as ``<%include>``, ``<%namespace>``, and ``<%inherit>``.
+ The ``file`` attribute of each of those tags refers
+ to a string URI that is passed to that :class:`.Template`
+ object's :class:`.TemplateCollection` for resolution.
+
+ :class:`.TemplateCollection` is an abstract class,
+ with the usual default implementation being :class:`.TemplateLookup`.
+
+ """
+
+ def has_template(self, uri):
+ """Return ``True`` if this :class:`.TemplateLookup` is
+ capable of returning a :class:`.Template` object for the
+ given ``uri``.
+
+ :param uri: String URI of the template to be resolved.
+
+ """
+ try:
+ self.get_template(uri)
+ return True
+ except exceptions.TemplateLookupException:
+ return False
+
+ def get_template(self, uri, relativeto=None):
+ """Return a :class:`.Template` object corresponding to the given
+ ``uri``.
+
+ The default implementation raises
+ :class:`.NotImplementedError`. Implementations should
+ raise :class:`.TemplateLookupException` if the given ``uri``
+ cannot be resolved.
+
+ :param uri: String URI of the template to be resolved.
+ :param relativeto: if present, the given ``uri`` is assumed to
+ be relative to this URI.
+
+ """
+ raise NotImplementedError()
+
+ def filename_to_uri(self, uri, filename):
+ """Convert the given ``filename`` to a URI relative to
+ this :class:`.TemplateCollection`."""
+
+ return uri
+
+ def adjust_uri(self, uri, filename):
+ """Adjust the given ``uri`` based on the calling ``filename``.
+
+ When this method is called from the runtime, the
+ ``filename`` parameter is taken directly to the ``filename``
+ attribute of the calling template. Therefore a custom
+ :class:`.TemplateCollection` subclass can place any string
+ identifier desired in the ``filename`` parameter of the
+ :class:`.Template` objects it constructs and have them come back
+ here.
+
+ """
+ return uri
+
+
+class TemplateLookup(TemplateCollection):
+
+ """Represent a collection of templates that locates template source files
+ from the local filesystem.
+
+ The primary argument is the ``directories`` argument, the list of
+ directories to search:
+
+ .. sourcecode:: python
+
+ lookup = TemplateLookup(["/path/to/templates"])
+ some_template = lookup.get_template("/index.html")
+
+ The :class:`.TemplateLookup` can also be given :class:`.Template` objects
+ programatically using :meth:`.put_string` or :meth:`.put_template`:
+
+ .. sourcecode:: python
+
+ lookup = TemplateLookup()
+ lookup.put_string("base.html", '''
+ <html><body>${self.next()}</body></html>
+ ''')
+ lookup.put_string("hello.html", '''
+ <%include file='base.html'/>
+
+ Hello, world !
+ ''')
+
+
+ :param directories: A list of directory names which will be
+ searched for a particular template URI. The URI is appended
+ to each directory and the filesystem checked.
+
+ :param collection_size: Approximate size of the collection used
+ to store templates. If left at its default of ``-1``, the size
+ is unbounded, and a plain Python dictionary is used to
+ relate URI strings to :class:`.Template` instances.
+ Otherwise, a least-recently-used cache object is used which
+ will maintain the size of the collection approximately to
+ the number given.
+
+ :param filesystem_checks: When at its default value of ``True``,
+ each call to :meth:`.TemplateLookup.get_template()` will
+ compare the filesystem last modified time to the time in
+ which an existing :class:`.Template` object was created.
+ This allows the :class:`.TemplateLookup` to regenerate a
+ new :class:`.Template` whenever the original source has
+ been updated. Set this to ``False`` for a very minor
+ performance increase.
+
+ :param modulename_callable: A callable which, when present,
+ is passed the path of the source file as well as the
+ requested URI, and then returns the full path of the
+ generated Python module file. This is used to inject
+ alternate schemes for Python module location. If left at
+ its default of ``None``, the built in system of generation
+ based on ``module_directory`` plus ``uri`` is used.
+
+ All other keyword parameters available for
+ :class:`.Template` are mirrored here. When new
+ :class:`.Template` objects are created, the keywords
+ established with this :class:`.TemplateLookup` are passed on
+ to each new :class:`.Template`.
+
+ """
+
+ def __init__(
+ self,
+ directories=None,
+ module_directory=None,
+ filesystem_checks=True,
+ collection_size=-1,
+ format_exceptions=False,
+ error_handler=None,
+ disable_unicode=False,
+ bytestring_passthrough=False,
+ output_encoding=None,
+ encoding_errors="strict",
+ cache_args=None,
+ cache_impl="beaker",
+ cache_enabled=True,
+ cache_type=None,
+ cache_dir=None,
+ cache_url=None,
+ modulename_callable=None,
+ module_writer=None,
+ default_filters=None,
+ buffer_filters=(),
+ strict_undefined=False,
+ imports=None,
+ future_imports=None,
+ enable_loop=True,
+ input_encoding=None,
+ preprocessor=None,
+ lexer_cls=None,
+ include_error_handler=None,
+ ):
+
+ self.directories = [
+ posixpath.normpath(d) for d in util.to_list(directories, ())
+ ]
+ self.module_directory = module_directory
+ self.modulename_callable = modulename_callable
+ self.filesystem_checks = filesystem_checks
+ self.collection_size = collection_size
+
+ if cache_args is None:
+ cache_args = {}
+ # transfer deprecated cache_* args
+ if cache_dir:
+ cache_args.setdefault("dir", cache_dir)
+ if cache_url:
+ cache_args.setdefault("url", cache_url)
+ if cache_type:
+ cache_args.setdefault("type", cache_type)
+
+ self.template_args = {
+ "format_exceptions": format_exceptions,
+ "error_handler": error_handler,
+ "include_error_handler": include_error_handler,
+ "disable_unicode": disable_unicode,
+ "bytestring_passthrough": bytestring_passthrough,
+ "output_encoding": output_encoding,
+ "cache_impl": cache_impl,
+ "encoding_errors": encoding_errors,
+ "input_encoding": input_encoding,
+ "module_directory": module_directory,
+ "module_writer": module_writer,
+ "cache_args": cache_args,
+ "cache_enabled": cache_enabled,
+ "default_filters": default_filters,
+ "buffer_filters": buffer_filters,
+ "strict_undefined": strict_undefined,
+ "imports": imports,
+ "future_imports": future_imports,
+ "enable_loop": enable_loop,
+ "preprocessor": preprocessor,
+ "lexer_cls": lexer_cls,
+ }
+
+ if collection_size == -1:
+ self._collection = {}
+ self._uri_cache = {}
+ else:
+ self._collection = util.LRUCache(collection_size)
+ self._uri_cache = util.LRUCache(collection_size)
+ self._mutex = threading.Lock()
+
+ def get_template(self, uri):
+ """Return a :class:`.Template` object corresponding to the given
+ ``uri``.
+
+ .. note:: The ``relativeto`` argument is not supported here at
+ the moment.
+
+ """
+
+ try:
+ if self.filesystem_checks:
+ return self._check(uri, self._collection[uri])
+ else:
+ return self._collection[uri]
+ except KeyError:
+ u = re.sub(r"^\/+", "", uri)
+ for dir_ in self.directories:
+ # make sure the path seperators are posix - os.altsep is empty
+ # on POSIX and cannot be used.
+ dir_ = dir_.replace(os.path.sep, posixpath.sep)
+ srcfile = posixpath.normpath(posixpath.join(dir_, u))
+ if os.path.isfile(srcfile):
+ return self._load(srcfile, uri)
+ else:
+ raise exceptions.TopLevelLookupException(
+ "Cant locate template for uri %r" % uri
+ )
+
+ def adjust_uri(self, uri, relativeto):
+ """Adjust the given ``uri`` based on the given relative URI."""
+
+ key = (uri, relativeto)
+ if key in self._uri_cache:
+ return self._uri_cache[key]
+
+ if uri[0] != "/":
+ if relativeto is not None:
+ v = self._uri_cache[key] = posixpath.join(
+ posixpath.dirname(relativeto), uri
+ )
+ else:
+ v = self._uri_cache[key] = "/" + uri
+ else:
+ v = self._uri_cache[key] = uri
+ return v
+
+ def filename_to_uri(self, filename):
+ """Convert the given ``filename`` to a URI relative to
+ this :class:`.TemplateCollection`."""
+
+ try:
+ return self._uri_cache[filename]
+ except KeyError:
+ value = self._relativeize(filename)
+ self._uri_cache[filename] = value
+ return value
+
+ def _relativeize(self, filename):
+ """Return the portion of a filename that is 'relative'
+ to the directories in this lookup.
+
+ """
+
+ filename = posixpath.normpath(filename)
+ for dir_ in self.directories:
+ if filename[0 : len(dir_)] == dir_:
+ return filename[len(dir_) :]
+ else:
+ return None
+
+ def _load(self, filename, uri):
+ self._mutex.acquire()
+ try:
+ try:
+ # try returning from collection one
+ # more time in case concurrent thread already loaded
+ return self._collection[uri]
+ except KeyError:
+ pass
+ try:
+ if self.modulename_callable is not None:
+ module_filename = self.modulename_callable(filename, uri)
+ else:
+ module_filename = None
+ self._collection[uri] = template = Template(
+ uri=uri,
+ filename=posixpath.normpath(filename),
+ lookup=self,
+ module_filename=module_filename,
+ **self.template_args
+ )
+ return template
+ except:
+ # if compilation fails etc, ensure
+ # template is removed from collection,
+ # re-raise
+ self._collection.pop(uri, None)
+ raise
+ finally:
+ self._mutex.release()
+
+ def _check(self, uri, template):
+ if template.filename is None:
+ return template
+
+ try:
+ template_stat = os.stat(template.filename)
+ if template.module._modified_time < template_stat[stat.ST_MTIME]:
+ self._collection.pop(uri, None)
+ return self._load(template.filename, uri)
+ else:
+ return template
+ except OSError:
+ self._collection.pop(uri, None)
+ raise exceptions.TemplateLookupException(
+ "Cant locate template for uri %r" % uri
+ )
+
+ def put_string(self, uri, text):
+ """Place a new :class:`.Template` object into this
+ :class:`.TemplateLookup`, based on the given string of
+ ``text``.
+
+ """
+ self._collection[uri] = Template(
+ text, lookup=self, uri=uri, **self.template_args
+ )
+
+ def put_template(self, uri, template):
+ """Place a new :class:`.Template` object into this
+ :class:`.TemplateLookup`, based on the given
+ :class:`.Template` object.
+
+ """
+ self._collection[uri] = template
diff --git a/third_party/python/Mako/mako/parsetree.py b/third_party/python/Mako/mako/parsetree.py
new file mode 100644
index 0000000000..801e48a7f4
--- /dev/null
+++ b/third_party/python/Mako/mako/parsetree.py
@@ -0,0 +1,665 @@
+# mako/parsetree.py
+# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
+#
+# This module is part of Mako and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+
+"""defines the parse tree components for Mako templates."""
+
+import re
+
+from mako import ast
+from mako import compat
+from mako import exceptions
+from mako import filters
+from mako import util
+
+
+class Node(object):
+
+ """base class for a Node in the parse tree."""
+
+ def __init__(self, source, lineno, pos, filename):
+ self.source = source
+ self.lineno = lineno
+ self.pos = pos
+ self.filename = filename
+
+ @property
+ def exception_kwargs(self):
+ return {
+ "source": self.source,
+ "lineno": self.lineno,
+ "pos": self.pos,
+ "filename": self.filename,
+ }
+
+ def get_children(self):
+ return []
+
+ def accept_visitor(self, visitor):
+ def traverse(node):
+ for n in node.get_children():
+ n.accept_visitor(visitor)
+
+ method = getattr(visitor, "visit" + self.__class__.__name__, traverse)
+ method(self)
+
+
+class TemplateNode(Node):
+
+ """a 'container' node that stores the overall collection of nodes."""
+
+ def __init__(self, filename):
+ super(TemplateNode, self).__init__("", 0, 0, filename)
+ self.nodes = []
+ self.page_attributes = {}
+
+ def get_children(self):
+ return self.nodes
+
+ def __repr__(self):
+ return "TemplateNode(%s, %r)" % (
+ util.sorted_dict_repr(self.page_attributes),
+ self.nodes,
+ )
+
+
+class ControlLine(Node):
+
+ """defines a control line, a line-oriented python line or end tag.
+
+ e.g.::
+
+ % if foo:
+ (markup)
+ % endif
+
+ """
+
+ has_loop_context = False
+
+ def __init__(self, keyword, isend, text, **kwargs):
+ super(ControlLine, self).__init__(**kwargs)
+ self.text = text
+ self.keyword = keyword
+ self.isend = isend
+ self.is_primary = keyword in ["for", "if", "while", "try", "with"]
+ self.nodes = []
+ if self.isend:
+ self._declared_identifiers = []
+ self._undeclared_identifiers = []
+ else:
+ code = ast.PythonFragment(text, **self.exception_kwargs)
+ self._declared_identifiers = code.declared_identifiers
+ self._undeclared_identifiers = code.undeclared_identifiers
+
+ def get_children(self):
+ return self.nodes
+
+ def declared_identifiers(self):
+ return self._declared_identifiers
+
+ def undeclared_identifiers(self):
+ return self._undeclared_identifiers
+
+ def is_ternary(self, keyword):
+ """return true if the given keyword is a ternary keyword
+ for this ControlLine"""
+
+ return keyword in {
+ "if": set(["else", "elif"]),
+ "try": set(["except", "finally"]),
+ "for": set(["else"]),
+ }.get(self.keyword, [])
+
+ def __repr__(self):
+ return "ControlLine(%r, %r, %r, %r)" % (
+ self.keyword,
+ self.text,
+ self.isend,
+ (self.lineno, self.pos),
+ )
+
+
+class Text(Node):
+
+ """defines plain text in the template."""
+
+ def __init__(self, content, **kwargs):
+ super(Text, self).__init__(**kwargs)
+ self.content = content
+
+ def __repr__(self):
+ return "Text(%r, %r)" % (self.content, (self.lineno, self.pos))
+
+
+class Code(Node):
+
+ """defines a Python code block, either inline or module level.
+
+ e.g.::
+
+ inline:
+ <%
+ x = 12
+ %>
+
+ module level:
+ <%!
+ import logger
+ %>
+
+ """
+
+ def __init__(self, text, ismodule, **kwargs):
+ super(Code, self).__init__(**kwargs)
+ self.text = text
+ self.ismodule = ismodule
+ self.code = ast.PythonCode(text, **self.exception_kwargs)
+
+ def declared_identifiers(self):
+ return self.code.declared_identifiers
+
+ def undeclared_identifiers(self):
+ return self.code.undeclared_identifiers
+
+ def __repr__(self):
+ return "Code(%r, %r, %r)" % (
+ self.text,
+ self.ismodule,
+ (self.lineno, self.pos),
+ )
+
+
+class Comment(Node):
+
+ """defines a comment line.
+
+ # this is a comment
+
+ """
+
+ def __init__(self, text, **kwargs):
+ super(Comment, self).__init__(**kwargs)
+ self.text = text
+
+ def __repr__(self):
+ return "Comment(%r, %r)" % (self.text, (self.lineno, self.pos))
+
+
+class Expression(Node):
+
+ """defines an inline expression.
+
+ ${x+y}
+
+ """
+
+ def __init__(self, text, escapes, **kwargs):
+ super(Expression, self).__init__(**kwargs)
+ self.text = text
+ self.escapes = escapes
+ self.escapes_code = ast.ArgumentList(escapes, **self.exception_kwargs)
+ self.code = ast.PythonCode(text, **self.exception_kwargs)
+
+ def declared_identifiers(self):
+ return []
+
+ def undeclared_identifiers(self):
+ # TODO: make the "filter" shortcut list configurable at parse/gen time
+ return self.code.undeclared_identifiers.union(
+ self.escapes_code.undeclared_identifiers.difference(
+ set(filters.DEFAULT_ESCAPES.keys())
+ )
+ ).difference(self.code.declared_identifiers)
+
+ def __repr__(self):
+ return "Expression(%r, %r, %r)" % (
+ self.text,
+ self.escapes_code.args,
+ (self.lineno, self.pos),
+ )
+
+
+class _TagMeta(type):
+
+ """metaclass to allow Tag to produce a subclass according to
+ its keyword"""
+
+ _classmap = {}
+
+ def __init__(cls, clsname, bases, dict_):
+ if getattr(cls, "__keyword__", None) is not None:
+ cls._classmap[cls.__keyword__] = cls
+ super(_TagMeta, cls).__init__(clsname, bases, dict_)
+
+ def __call__(cls, keyword, attributes, **kwargs):
+ if ":" in keyword:
+ ns, defname = keyword.split(":")
+ return type.__call__(
+ CallNamespaceTag, ns, defname, attributes, **kwargs
+ )
+
+ try:
+ cls = _TagMeta._classmap[keyword]
+ except KeyError:
+ raise exceptions.CompileException(
+ "No such tag: '%s'" % keyword,
+ source=kwargs["source"],
+ lineno=kwargs["lineno"],
+ pos=kwargs["pos"],
+ filename=kwargs["filename"],
+ )
+ return type.__call__(cls, keyword, attributes, **kwargs)
+
+
+class Tag(compat.with_metaclass(_TagMeta, Node)):
+ """abstract base class for tags.
+
+ e.g.::
+
+ <%sometag/>
+
+ <%someothertag>
+ stuff
+ </%someothertag>
+
+ """
+
+ __keyword__ = None
+
+ def __init__(
+ self,
+ keyword,
+ attributes,
+ expressions,
+ nonexpressions,
+ required,
+ **kwargs
+ ):
+ r"""construct a new Tag instance.
+
+ this constructor not called directly, and is only called
+ by subclasses.
+
+ :param keyword: the tag keyword
+
+ :param attributes: raw dictionary of attribute key/value pairs
+
+ :param expressions: a set of identifiers that are legal attributes,
+ which can also contain embedded expressions
+
+ :param nonexpressions: a set of identifiers that are legal
+ attributes, which cannot contain embedded expressions
+
+ :param \**kwargs:
+ other arguments passed to the Node superclass (lineno, pos)
+
+ """
+ super(Tag, self).__init__(**kwargs)
+ self.keyword = keyword
+ self.attributes = attributes
+ self._parse_attributes(expressions, nonexpressions)
+ missing = [r for r in required if r not in self.parsed_attributes]
+ if len(missing):
+ raise exceptions.CompileException(
+ "Missing attribute(s): %s"
+ % ",".join([repr(m) for m in missing]),
+ **self.exception_kwargs
+ )
+ self.parent = None
+ self.nodes = []
+
+ def is_root(self):
+ return self.parent is None
+
+ def get_children(self):
+ return self.nodes
+
+ def _parse_attributes(self, expressions, nonexpressions):
+ undeclared_identifiers = set()
+ self.parsed_attributes = {}
+ for key in self.attributes:
+ if key in expressions:
+ expr = []
+ for x in re.compile(r"(\${.+?})", re.S).split(
+ self.attributes[key]
+ ):
+ m = re.compile(r"^\${(.+?)}$", re.S).match(x)
+ if m:
+ code = ast.PythonCode(
+ m.group(1).rstrip(), **self.exception_kwargs
+ )
+ # we aren't discarding "declared_identifiers" here,
+ # which we do so that list comprehension-declared
+ # variables aren't counted. As yet can't find a
+ # condition that requires it here.
+ undeclared_identifiers = undeclared_identifiers.union(
+ code.undeclared_identifiers
+ )
+ expr.append("(%s)" % m.group(1))
+ else:
+ if x:
+ expr.append(repr(x))
+ self.parsed_attributes[key] = " + ".join(expr) or repr("")
+ elif key in nonexpressions:
+ if re.search(r"\${.+?}", self.attributes[key]):
+ raise exceptions.CompileException(
+ "Attibute '%s' in tag '%s' does not allow embedded "
+ "expressions" % (key, self.keyword),
+ **self.exception_kwargs
+ )
+ self.parsed_attributes[key] = repr(self.attributes[key])
+ else:
+ raise exceptions.CompileException(
+ "Invalid attribute for tag '%s': '%s'"
+ % (self.keyword, key),
+ **self.exception_kwargs
+ )
+ self.expression_undeclared_identifiers = undeclared_identifiers
+
+ def declared_identifiers(self):
+ return []
+
+ def undeclared_identifiers(self):
+ return self.expression_undeclared_identifiers
+
+ def __repr__(self):
+ return "%s(%r, %s, %r, %r)" % (
+ self.__class__.__name__,
+ self.keyword,
+ util.sorted_dict_repr(self.attributes),
+ (self.lineno, self.pos),
+ self.nodes,
+ )
+
+
+class IncludeTag(Tag):
+ __keyword__ = "include"
+
+ def __init__(self, keyword, attributes, **kwargs):
+ super(IncludeTag, self).__init__(
+ keyword,
+ attributes,
+ ("file", "import", "args"),
+ (),
+ ("file",),
+ **kwargs
+ )
+ self.page_args = ast.PythonCode(
+ "__DUMMY(%s)" % attributes.get("args", ""), **self.exception_kwargs
+ )
+
+ def declared_identifiers(self):
+ return []
+
+ def undeclared_identifiers(self):
+ identifiers = self.page_args.undeclared_identifiers.difference(
+ set(["__DUMMY"])
+ ).difference(self.page_args.declared_identifiers)
+ return identifiers.union(
+ super(IncludeTag, self).undeclared_identifiers()
+ )
+
+
+class NamespaceTag(Tag):
+ __keyword__ = "namespace"
+
+ def __init__(self, keyword, attributes, **kwargs):
+ super(NamespaceTag, self).__init__(
+ keyword,
+ attributes,
+ ("file",),
+ ("name", "inheritable", "import", "module"),
+ (),
+ **kwargs
+ )
+
+ self.name = attributes.get("name", "__anon_%s" % hex(abs(id(self))))
+ if "name" not in attributes and "import" not in attributes:
+ raise exceptions.CompileException(
+ "'name' and/or 'import' attributes are required "
+ "for <%namespace>",
+ **self.exception_kwargs
+ )
+ if "file" in attributes and "module" in attributes:
+ raise exceptions.CompileException(
+ "<%namespace> may only have one of 'file' or 'module'",
+ **self.exception_kwargs
+ )
+
+ def declared_identifiers(self):
+ return []
+
+
+class TextTag(Tag):
+ __keyword__ = "text"
+
+ def __init__(self, keyword, attributes, **kwargs):
+ super(TextTag, self).__init__(
+ keyword, attributes, (), ("filter"), (), **kwargs
+ )
+ self.filter_args = ast.ArgumentList(
+ attributes.get("filter", ""), **self.exception_kwargs
+ )
+
+ def undeclared_identifiers(self):
+ return self.filter_args.undeclared_identifiers.difference(
+ filters.DEFAULT_ESCAPES.keys()
+ ).union(self.expression_undeclared_identifiers)
+
+
+class DefTag(Tag):
+ __keyword__ = "def"
+
+ def __init__(self, keyword, attributes, **kwargs):
+ expressions = ["buffered", "cached"] + [
+ c for c in attributes if c.startswith("cache_")
+ ]
+
+ super(DefTag, self).__init__(
+ keyword,
+ attributes,
+ expressions,
+ ("name", "filter", "decorator"),
+ ("name",),
+ **kwargs
+ )
+ name = attributes["name"]
+ if re.match(r"^[\w_]+$", name):
+ raise exceptions.CompileException(
+ "Missing parenthesis in %def", **self.exception_kwargs
+ )
+ self.function_decl = ast.FunctionDecl(
+ "def " + name + ":pass", **self.exception_kwargs
+ )
+ self.name = self.function_decl.funcname
+ self.decorator = attributes.get("decorator", "")
+ self.filter_args = ast.ArgumentList(
+ attributes.get("filter", ""), **self.exception_kwargs
+ )
+
+ is_anonymous = False
+ is_block = False
+
+ @property
+ def funcname(self):
+ return self.function_decl.funcname
+
+ def get_argument_expressions(self, **kw):
+ return self.function_decl.get_argument_expressions(**kw)
+
+ def declared_identifiers(self):
+ return self.function_decl.allargnames
+
+ def undeclared_identifiers(self):
+ res = []
+ for c in self.function_decl.defaults:
+ res += list(
+ ast.PythonCode(
+ c, **self.exception_kwargs
+ ).undeclared_identifiers
+ )
+ return (
+ set(res)
+ .union(
+ self.filter_args.undeclared_identifiers.difference(
+ filters.DEFAULT_ESCAPES.keys()
+ )
+ )
+ .union(self.expression_undeclared_identifiers)
+ .difference(self.function_decl.allargnames)
+ )
+
+
+class BlockTag(Tag):
+ __keyword__ = "block"
+
+ def __init__(self, keyword, attributes, **kwargs):
+ expressions = ["buffered", "cached", "args"] + [
+ c for c in attributes if c.startswith("cache_")
+ ]
+
+ super(BlockTag, self).__init__(
+ keyword,
+ attributes,
+ expressions,
+ ("name", "filter", "decorator"),
+ (),
+ **kwargs
+ )
+ name = attributes.get("name")
+ if name and not re.match(r"^[\w_]+$", name):
+ raise exceptions.CompileException(
+ "%block may not specify an argument signature",
+ **self.exception_kwargs
+ )
+ if not name and attributes.get("args", None):
+ raise exceptions.CompileException(
+ "Only named %blocks may specify args", **self.exception_kwargs
+ )
+ self.body_decl = ast.FunctionArgs(
+ attributes.get("args", ""), **self.exception_kwargs
+ )
+
+ self.name = name
+ self.decorator = attributes.get("decorator", "")
+ self.filter_args = ast.ArgumentList(
+ attributes.get("filter", ""), **self.exception_kwargs
+ )
+
+ is_block = True
+
+ @property
+ def is_anonymous(self):
+ return self.name is None
+
+ @property
+ def funcname(self):
+ return self.name or "__M_anon_%d" % (self.lineno,)
+
+ def get_argument_expressions(self, **kw):
+ return self.body_decl.get_argument_expressions(**kw)
+
+ def declared_identifiers(self):
+ return self.body_decl.allargnames
+
+ def undeclared_identifiers(self):
+ return (
+ self.filter_args.undeclared_identifiers.difference(
+ filters.DEFAULT_ESCAPES.keys()
+ )
+ ).union(self.expression_undeclared_identifiers)
+
+
+class CallTag(Tag):
+ __keyword__ = "call"
+
+ def __init__(self, keyword, attributes, **kwargs):
+ super(CallTag, self).__init__(
+ keyword, attributes, ("args"), ("expr",), ("expr",), **kwargs
+ )
+ self.expression = attributes["expr"]
+ self.code = ast.PythonCode(self.expression, **self.exception_kwargs)
+ self.body_decl = ast.FunctionArgs(
+ attributes.get("args", ""), **self.exception_kwargs
+ )
+
+ def declared_identifiers(self):
+ return self.code.declared_identifiers.union(self.body_decl.allargnames)
+
+ def undeclared_identifiers(self):
+ return self.code.undeclared_identifiers.difference(
+ self.code.declared_identifiers
+ )
+
+
+class CallNamespaceTag(Tag):
+ def __init__(self, namespace, defname, attributes, **kwargs):
+ super(CallNamespaceTag, self).__init__(
+ namespace + ":" + defname,
+ attributes,
+ tuple(attributes.keys()) + ("args",),
+ (),
+ (),
+ **kwargs
+ )
+
+ self.expression = "%s.%s(%s)" % (
+ namespace,
+ defname,
+ ",".join(
+ [
+ "%s=%s" % (k, v)
+ for k, v in self.parsed_attributes.items()
+ if k != "args"
+ ]
+ ),
+ )
+ self.code = ast.PythonCode(self.expression, **self.exception_kwargs)
+ self.body_decl = ast.FunctionArgs(
+ attributes.get("args", ""), **self.exception_kwargs
+ )
+
+ def declared_identifiers(self):
+ return self.code.declared_identifiers.union(self.body_decl.allargnames)
+
+ def undeclared_identifiers(self):
+ return self.code.undeclared_identifiers.difference(
+ self.code.declared_identifiers
+ )
+
+
+class InheritTag(Tag):
+ __keyword__ = "inherit"
+
+ def __init__(self, keyword, attributes, **kwargs):
+ super(InheritTag, self).__init__(
+ keyword, attributes, ("file",), (), ("file",), **kwargs
+ )
+
+
+class PageTag(Tag):
+ __keyword__ = "page"
+
+ def __init__(self, keyword, attributes, **kwargs):
+ expressions = [
+ "cached",
+ "args",
+ "expression_filter",
+ "enable_loop",
+ ] + [c for c in attributes if c.startswith("cache_")]
+
+ super(PageTag, self).__init__(
+ keyword, attributes, expressions, (), (), **kwargs
+ )
+ self.body_decl = ast.FunctionArgs(
+ attributes.get("args", ""), **self.exception_kwargs
+ )
+ self.filter_args = ast.ArgumentList(
+ attributes.get("expression_filter", ""), **self.exception_kwargs
+ )
+
+ def declared_identifiers(self):
+ return self.body_decl.allargnames
diff --git a/third_party/python/Mako/mako/pygen.py b/third_party/python/Mako/mako/pygen.py
new file mode 100644
index 0000000000..947721f1a5
--- /dev/null
+++ b/third_party/python/Mako/mako/pygen.py
@@ -0,0 +1,305 @@
+# mako/pygen.py
+# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
+#
+# This module is part of Mako and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+
+"""utilities for generating and formatting literal Python code."""
+
+import re
+
+from mako import exceptions
+
+
+class PythonPrinter(object):
+ def __init__(self, stream):
+ # indentation counter
+ self.indent = 0
+
+ # a stack storing information about why we incremented
+ # the indentation counter, to help us determine if we
+ # should decrement it
+ self.indent_detail = []
+
+ # the string of whitespace multiplied by the indent
+ # counter to produce a line
+ self.indentstring = " "
+
+ # the stream we are writing to
+ self.stream = stream
+
+ # current line number
+ self.lineno = 1
+
+ # a list of lines that represents a buffered "block" of code,
+ # which can be later printed relative to an indent level
+ self.line_buffer = []
+
+ self.in_indent_lines = False
+
+ self._reset_multi_line_flags()
+
+ # mapping of generated python lines to template
+ # source lines
+ self.source_map = {}
+
+ def _update_lineno(self, num):
+ self.lineno += num
+
+ def start_source(self, lineno):
+ if self.lineno not in self.source_map:
+ self.source_map[self.lineno] = lineno
+
+ def write_blanks(self, num):
+ self.stream.write("\n" * num)
+ self._update_lineno(num)
+
+ def write_indented_block(self, block, starting_lineno=None):
+ """print a line or lines of python which already contain indentation.
+
+ The indentation of the total block of lines will be adjusted to that of
+ the current indent level."""
+ self.in_indent_lines = False
+ for i, l in enumerate(re.split(r"\r?\n", block)):
+ self.line_buffer.append(l)
+ if starting_lineno is not None:
+ self.start_source(starting_lineno + i)
+ self._update_lineno(1)
+
+ def writelines(self, *lines):
+ """print a series of lines of python."""
+ for line in lines:
+ self.writeline(line)
+
+ def writeline(self, line):
+ """print a line of python, indenting it according to the current
+ indent level.
+
+ this also adjusts the indentation counter according to the
+ content of the line.
+
+ """
+
+ if not self.in_indent_lines:
+ self._flush_adjusted_lines()
+ self.in_indent_lines = True
+
+ if (
+ line is None
+ or re.match(r"^\s*#", line)
+ or re.match(r"^\s*$", line)
+ ):
+ hastext = False
+ else:
+ hastext = True
+
+ is_comment = line and len(line) and line[0] == "#"
+
+ # see if this line should decrease the indentation level
+ if not is_comment and (not hastext or self._is_unindentor(line)):
+
+ if self.indent > 0:
+ self.indent -= 1
+ # if the indent_detail stack is empty, the user
+ # probably put extra closures - the resulting
+ # module wont compile.
+ if len(self.indent_detail) == 0:
+ raise exceptions.SyntaxException(
+ "Too many whitespace closures"
+ )
+ self.indent_detail.pop()
+
+ if line is None:
+ return
+
+ # write the line
+ self.stream.write(self._indent_line(line) + "\n")
+ self._update_lineno(len(line.split("\n")))
+
+ # see if this line should increase the indentation level.
+ # note that a line can both decrase (before printing) and
+ # then increase (after printing) the indentation level.
+
+ if re.search(r":[ \t]*(?:#.*)?$", line):
+ # increment indentation count, and also
+ # keep track of what the keyword was that indented us,
+ # if it is a python compound statement keyword
+ # where we might have to look for an "unindent" keyword
+ match = re.match(r"^\s*(if|try|elif|while|for|with)", line)
+ if match:
+ # its a "compound" keyword, so we will check for "unindentors"
+ indentor = match.group(1)
+ self.indent += 1
+ self.indent_detail.append(indentor)
+ else:
+ indentor = None
+ # its not a "compound" keyword. but lets also
+ # test for valid Python keywords that might be indenting us,
+ # else assume its a non-indenting line
+ m2 = re.match(
+ r"^\s*(def|class|else|elif|except|finally)", line
+ )
+ if m2:
+ self.indent += 1
+ self.indent_detail.append(indentor)
+
+ def close(self):
+ """close this printer, flushing any remaining lines."""
+ self._flush_adjusted_lines()
+
+ def _is_unindentor(self, line):
+ """return true if the given line is an 'unindentor',
+ relative to the last 'indent' event received.
+
+ """
+
+ # no indentation detail has been pushed on; return False
+ if len(self.indent_detail) == 0:
+ return False
+
+ indentor = self.indent_detail[-1]
+
+ # the last indent keyword we grabbed is not a
+ # compound statement keyword; return False
+ if indentor is None:
+ return False
+
+ # if the current line doesnt have one of the "unindentor" keywords,
+ # return False
+ match = re.match(r"^\s*(else|elif|except|finally).*\:", line)
+ if not match:
+ return False
+
+ # whitespace matches up, we have a compound indentor,
+ # and this line has an unindentor, this
+ # is probably good enough
+ return True
+
+ # should we decide that its not good enough, heres
+ # more stuff to check.
+ # keyword = match.group(1)
+
+ # match the original indent keyword
+ # for crit in [
+ # (r'if|elif', r'else|elif'),
+ # (r'try', r'except|finally|else'),
+ # (r'while|for', r'else'),
+ # ]:
+ # if re.match(crit[0], indentor) and re.match(crit[1], keyword):
+ # return True
+
+ # return False
+
+ def _indent_line(self, line, stripspace=""):
+ """indent the given line according to the current indent level.
+
+ stripspace is a string of space that will be truncated from the
+ start of the line before indenting."""
+
+ return re.sub(
+ r"^%s" % stripspace, self.indentstring * self.indent, line
+ )
+
+ def _reset_multi_line_flags(self):
+ """reset the flags which would indicate we are in a backslashed
+ or triple-quoted section."""
+
+ self.backslashed, self.triplequoted = False, False
+
+ def _in_multi_line(self, line):
+ """return true if the given line is part of a multi-line block,
+ via backslash or triple-quote."""
+
+ # we are only looking for explicitly joined lines here, not
+ # implicit ones (i.e. brackets, braces etc.). this is just to
+ # guard against the possibility of modifying the space inside of
+ # a literal multiline string with unfortunately placed
+ # whitespace
+
+ current_state = self.backslashed or self.triplequoted
+
+ if re.search(r"\\$", line):
+ self.backslashed = True
+ else:
+ self.backslashed = False
+
+ triples = len(re.findall(r"\"\"\"|\'\'\'", line))
+ if triples == 1 or triples % 2 != 0:
+ self.triplequoted = not self.triplequoted
+
+ return current_state
+
+ def _flush_adjusted_lines(self):
+ stripspace = None
+ self._reset_multi_line_flags()
+
+ for entry in self.line_buffer:
+ if self._in_multi_line(entry):
+ self.stream.write(entry + "\n")
+ else:
+ entry = entry.expandtabs()
+ if stripspace is None and re.search(r"^[ \t]*[^# \t]", entry):
+ stripspace = re.match(r"^([ \t]*)", entry).group(1)
+ self.stream.write(self._indent_line(entry, stripspace) + "\n")
+
+ self.line_buffer = []
+ self._reset_multi_line_flags()
+
+
+def adjust_whitespace(text):
+ """remove the left-whitespace margin of a block of Python code."""
+
+ state = [False, False]
+ (backslashed, triplequoted) = (0, 1)
+
+ def in_multi_line(line):
+ start_state = state[backslashed] or state[triplequoted]
+
+ if re.search(r"\\$", line):
+ state[backslashed] = True
+ else:
+ state[backslashed] = False
+
+ def match(reg, t):
+ m = re.match(reg, t)
+ if m:
+ return m, t[len(m.group(0)) :]
+ else:
+ return None, t
+
+ while line:
+ if state[triplequoted]:
+ m, line = match(r"%s" % state[triplequoted], line)
+ if m:
+ state[triplequoted] = False
+ else:
+ m, line = match(r".*?(?=%s|$)" % state[triplequoted], line)
+ else:
+ m, line = match(r"#", line)
+ if m:
+ return start_state
+
+ m, line = match(r"\"\"\"|\'\'\'", line)
+ if m:
+ state[triplequoted] = m.group(0)
+ continue
+
+ m, line = match(r".*?(?=\"\"\"|\'\'\'|#|$)", line)
+
+ return start_state
+
+ def _indent_line(line, stripspace=""):
+ return re.sub(r"^%s" % stripspace, "", line)
+
+ lines = []
+ stripspace = None
+
+ for line in re.split(r"\r?\n", text):
+ if in_multi_line(line):
+ lines.append(line)
+ else:
+ line = line.expandtabs()
+ if stripspace is None and re.search(r"^[ \t]*[^# \t]", line):
+ stripspace = re.match(r"^([ \t]*)", line).group(1)
+ lines.append(_indent_line(line, stripspace))
+ return "\n".join(lines)
diff --git a/third_party/python/Mako/mako/pyparser.py b/third_party/python/Mako/mako/pyparser.py
new file mode 100644
index 0000000000..b16672d60c
--- /dev/null
+++ b/third_party/python/Mako/mako/pyparser.py
@@ -0,0 +1,242 @@
+# mako/pyparser.py
+# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
+#
+# This module is part of Mako and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+
+"""Handles parsing of Python code.
+
+Parsing to AST is done via _ast on Python > 2.5, otherwise the compiler
+module is used.
+"""
+
+import operator
+
+import _ast
+
+from mako import _ast_util
+from mako import compat
+from mako import exceptions
+from mako import util
+from mako.compat import arg_stringname
+
+if compat.py3k:
+ # words that cannot be assigned to (notably
+ # smaller than the total keys in __builtins__)
+ reserved = set(["True", "False", "None", "print"])
+
+ # the "id" attribute on a function node
+ arg_id = operator.attrgetter("arg")
+else:
+ # words that cannot be assigned to (notably
+ # smaller than the total keys in __builtins__)
+ reserved = set(["True", "False", "None"])
+
+ # the "id" attribute on a function node
+ arg_id = operator.attrgetter("id")
+
+util.restore__ast(_ast)
+
+
+def parse(code, mode="exec", **exception_kwargs):
+ """Parse an expression into AST"""
+
+ try:
+ return _ast_util.parse(code, "<unknown>", mode)
+ except Exception:
+ raise exceptions.SyntaxException(
+ "(%s) %s (%r)"
+ % (
+ compat.exception_as().__class__.__name__,
+ compat.exception_as(),
+ code[0:50],
+ ),
+ **exception_kwargs
+ )
+
+
+class FindIdentifiers(_ast_util.NodeVisitor):
+ def __init__(self, listener, **exception_kwargs):
+ self.in_function = False
+ self.in_assign_targets = False
+ self.local_ident_stack = set()
+ self.listener = listener
+ self.exception_kwargs = exception_kwargs
+
+ def _add_declared(self, name):
+ if not self.in_function:
+ self.listener.declared_identifiers.add(name)
+ else:
+ self.local_ident_stack.add(name)
+
+ def visit_ClassDef(self, node):
+ self._add_declared(node.name)
+
+ def visit_Assign(self, node):
+
+ # flip around the visiting of Assign so the expression gets
+ # evaluated first, in the case of a clause like "x=x+5" (x
+ # is undeclared)
+
+ self.visit(node.value)
+ in_a = self.in_assign_targets
+ self.in_assign_targets = True
+ for n in node.targets:
+ self.visit(n)
+ self.in_assign_targets = in_a
+
+ if compat.py3k:
+
+ # ExceptHandler is in Python 2, but this block only works in
+ # Python 3 (and is required there)
+
+ def visit_ExceptHandler(self, node):
+ if node.name is not None:
+ self._add_declared(node.name)
+ if node.type is not None:
+ self.visit(node.type)
+ for statement in node.body:
+ self.visit(statement)
+
+ def visit_Lambda(self, node, *args):
+ self._visit_function(node, True)
+
+ def visit_FunctionDef(self, node):
+ self._add_declared(node.name)
+ self._visit_function(node, False)
+
+ def _expand_tuples(self, args):
+ for arg in args:
+ if isinstance(arg, _ast.Tuple):
+ for n in arg.elts:
+ yield n
+ else:
+ yield arg
+
+ def _visit_function(self, node, islambda):
+
+ # push function state onto stack. dont log any more
+ # identifiers as "declared" until outside of the function,
+ # but keep logging identifiers as "undeclared". track
+ # argument names in each function header so they arent
+ # counted as "undeclared"
+
+ inf = self.in_function
+ self.in_function = True
+
+ local_ident_stack = self.local_ident_stack
+ self.local_ident_stack = local_ident_stack.union(
+ [arg_id(arg) for arg in self._expand_tuples(node.args.args)]
+ )
+ if islambda:
+ self.visit(node.body)
+ else:
+ for n in node.body:
+ self.visit(n)
+ self.in_function = inf
+ self.local_ident_stack = local_ident_stack
+
+ def visit_For(self, node):
+
+ # flip around visit
+
+ self.visit(node.iter)
+ self.visit(node.target)
+ for statement in node.body:
+ self.visit(statement)
+ for statement in node.orelse:
+ self.visit(statement)
+
+ def visit_Name(self, node):
+ if isinstance(node.ctx, _ast.Store):
+ # this is eqiuvalent to visit_AssName in
+ # compiler
+ self._add_declared(node.id)
+ elif (
+ node.id not in reserved
+ and node.id not in self.listener.declared_identifiers
+ and node.id not in self.local_ident_stack
+ ):
+ self.listener.undeclared_identifiers.add(node.id)
+
+ def visit_Import(self, node):
+ for name in node.names:
+ if name.asname is not None:
+ self._add_declared(name.asname)
+ else:
+ self._add_declared(name.name.split(".")[0])
+
+ def visit_ImportFrom(self, node):
+ for name in node.names:
+ if name.asname is not None:
+ self._add_declared(name.asname)
+ else:
+ if name.name == "*":
+ raise exceptions.CompileException(
+ "'import *' is not supported, since all identifier "
+ "names must be explicitly declared. Please use the "
+ "form 'from <modulename> import <name1>, <name2>, "
+ "...' instead.",
+ **self.exception_kwargs
+ )
+ self._add_declared(name.name)
+
+
+class FindTuple(_ast_util.NodeVisitor):
+ def __init__(self, listener, code_factory, **exception_kwargs):
+ self.listener = listener
+ self.exception_kwargs = exception_kwargs
+ self.code_factory = code_factory
+
+ def visit_Tuple(self, node):
+ for n in node.elts:
+ p = self.code_factory(n, **self.exception_kwargs)
+ self.listener.codeargs.append(p)
+ self.listener.args.append(ExpressionGenerator(n).value())
+ ldi = self.listener.declared_identifiers
+ self.listener.declared_identifiers = ldi.union(
+ p.declared_identifiers
+ )
+ lui = self.listener.undeclared_identifiers
+ self.listener.undeclared_identifiers = lui.union(
+ p.undeclared_identifiers
+ )
+
+
+class ParseFunc(_ast_util.NodeVisitor):
+ def __init__(self, listener, **exception_kwargs):
+ self.listener = listener
+ self.exception_kwargs = exception_kwargs
+
+ def visit_FunctionDef(self, node):
+ self.listener.funcname = node.name
+
+ argnames = [arg_id(arg) for arg in node.args.args]
+ if node.args.vararg:
+ argnames.append(arg_stringname(node.args.vararg))
+
+ if compat.py2k:
+ # kw-only args don't exist in Python 2
+ kwargnames = []
+ else:
+ kwargnames = [arg_id(arg) for arg in node.args.kwonlyargs]
+ if node.args.kwarg:
+ kwargnames.append(arg_stringname(node.args.kwarg))
+ self.listener.argnames = argnames
+ self.listener.defaults = node.args.defaults # ast
+ self.listener.kwargnames = kwargnames
+ if compat.py2k:
+ self.listener.kwdefaults = []
+ else:
+ self.listener.kwdefaults = node.args.kw_defaults
+ self.listener.varargs = node.args.vararg
+ self.listener.kwargs = node.args.kwarg
+
+
+class ExpressionGenerator(object):
+ def __init__(self, astnode):
+ self.generator = _ast_util.SourceGenerator(" " * 4)
+ self.generator.visit(astnode)
+
+ def value(self):
+ return "".join(self.generator.result)
diff --git a/third_party/python/Mako/mako/runtime.py b/third_party/python/Mako/mako/runtime.py
new file mode 100644
index 0000000000..465908e6d8
--- /dev/null
+++ b/third_party/python/Mako/mako/runtime.py
@@ -0,0 +1,970 @@
+# mako/runtime.py
+# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
+#
+# This module is part of Mako and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+
+"""provides runtime services for templates, including Context,
+Namespace, and various helper functions."""
+
+import functools
+import sys
+
+from mako import compat
+from mako import exceptions
+from mako import util
+from mako.compat import compat_builtins
+
+
+class Context(object):
+
+ """Provides runtime namespace, output buffer, and various
+ callstacks for templates.
+
+ See :ref:`runtime_toplevel` for detail on the usage of
+ :class:`.Context`.
+
+ """
+
+ def __init__(self, buffer, **data):
+ self._buffer_stack = [buffer]
+
+ self._data = data
+
+ self._kwargs = data.copy()
+ self._with_template = None
+ self._outputting_as_unicode = None
+ self.namespaces = {}
+
+ # "capture" function which proxies to the
+ # generic "capture" function
+ self._data["capture"] = functools.partial(capture, self)
+
+ # "caller" stack used by def calls with content
+ self.caller_stack = self._data["caller"] = CallerStack()
+
+ def _set_with_template(self, t):
+ self._with_template = t
+ illegal_names = t.reserved_names.intersection(self._data)
+ if illegal_names:
+ raise exceptions.NameConflictError(
+ "Reserved words passed to render(): %s"
+ % ", ".join(illegal_names)
+ )
+
+ @property
+ def lookup(self):
+ """Return the :class:`.TemplateLookup` associated
+ with this :class:`.Context`.
+
+ """
+ return self._with_template.lookup
+
+ @property
+ def kwargs(self):
+ """Return the dictionary of top level keyword arguments associated
+ with this :class:`.Context`.
+
+ This dictionary only includes the top-level arguments passed to
+ :meth:`.Template.render`. It does not include names produced within
+ the template execution such as local variable names or special names
+ such as ``self``, ``next``, etc.
+
+ The purpose of this dictionary is primarily for the case that
+ a :class:`.Template` accepts arguments via its ``<%page>`` tag,
+ which are normally expected to be passed via :meth:`.Template.render`,
+ except the template is being called in an inheritance context,
+ using the ``body()`` method. :attr:`.Context.kwargs` can then be
+ used to propagate these arguments to the inheriting template::
+
+ ${next.body(**context.kwargs)}
+
+ """
+ return self._kwargs.copy()
+
+ def push_caller(self, caller):
+ """Push a ``caller`` callable onto the callstack for
+ this :class:`.Context`."""
+
+ self.caller_stack.append(caller)
+
+ def pop_caller(self):
+ """Pop a ``caller`` callable onto the callstack for this
+ :class:`.Context`."""
+
+ del self.caller_stack[-1]
+
+ def keys(self):
+ """Return a list of all names established in this :class:`.Context`."""
+
+ return list(self._data.keys())
+
+ def __getitem__(self, key):
+ if key in self._data:
+ return self._data[key]
+ else:
+ return compat_builtins.__dict__[key]
+
+ def _push_writer(self):
+ """push a capturing buffer onto this Context and return
+ the new writer function."""
+
+ buf = util.FastEncodingBuffer()
+ self._buffer_stack.append(buf)
+ return buf.write
+
+ def _pop_buffer_and_writer(self):
+ """pop the most recent capturing buffer from this Context
+ and return the current writer after the pop.
+
+ """
+
+ buf = self._buffer_stack.pop()
+ return buf, self._buffer_stack[-1].write
+
+ def _push_buffer(self):
+ """push a capturing buffer onto this Context."""
+
+ self._push_writer()
+
+ def _pop_buffer(self):
+ """pop the most recent capturing buffer from this Context."""
+
+ return self._buffer_stack.pop()
+
+ def get(self, key, default=None):
+ """Return a value from this :class:`.Context`."""
+
+ return self._data.get(key, compat_builtins.__dict__.get(key, default))
+
+ def write(self, string):
+ """Write a string to this :class:`.Context` object's
+ underlying output buffer."""
+
+ self._buffer_stack[-1].write(string)
+
+ def writer(self):
+ """Return the current writer function."""
+
+ return self._buffer_stack[-1].write
+
+ def _copy(self):
+ c = Context.__new__(Context)
+ c._buffer_stack = self._buffer_stack
+ c._data = self._data.copy()
+ c._kwargs = self._kwargs
+ c._with_template = self._with_template
+ c._outputting_as_unicode = self._outputting_as_unicode
+ c.namespaces = self.namespaces
+ c.caller_stack = self.caller_stack
+ return c
+
+ def _locals(self, d):
+ """Create a new :class:`.Context` with a copy of this
+ :class:`.Context`'s current state,
+ updated with the given dictionary.
+
+ The :attr:`.Context.kwargs` collection remains
+ unaffected.
+
+
+ """
+
+ if not d:
+ return self
+ c = self._copy()
+ c._data.update(d)
+ return c
+
+ def _clean_inheritance_tokens(self):
+ """create a new copy of this :class:`.Context`. with
+ tokens related to inheritance state removed."""
+
+ c = self._copy()
+ x = c._data
+ x.pop("self", None)
+ x.pop("parent", None)
+ x.pop("next", None)
+ return c
+
+
+class CallerStack(list):
+ def __init__(self):
+ self.nextcaller = None
+
+ def __nonzero__(self):
+ return self.__bool__()
+
+ def __bool__(self):
+ return len(self) and self._get_caller() and True or False
+
+ def _get_caller(self):
+ # this method can be removed once
+ # codegen MAGIC_NUMBER moves past 7
+ return self[-1]
+
+ def __getattr__(self, key):
+ return getattr(self._get_caller(), key)
+
+ def _push_frame(self):
+ frame = self.nextcaller or None
+ self.append(frame)
+ self.nextcaller = None
+ return frame
+
+ def _pop_frame(self):
+ self.nextcaller = self.pop()
+
+
+class Undefined(object):
+
+ """Represents an undefined value in a template.
+
+ All template modules have a constant value
+ ``UNDEFINED`` present which is an instance of this
+ object.
+
+ """
+
+ def __str__(self):
+ raise NameError("Undefined")
+
+ def __nonzero__(self):
+ return self.__bool__()
+
+ def __bool__(self):
+ return False
+
+
+UNDEFINED = Undefined()
+STOP_RENDERING = ""
+
+
+class LoopStack(object):
+
+ """a stack for LoopContexts that implements the context manager protocol
+ to automatically pop off the top of the stack on context exit
+ """
+
+ def __init__(self):
+ self.stack = []
+
+ def _enter(self, iterable):
+ self._push(iterable)
+ return self._top
+
+ def _exit(self):
+ self._pop()
+ return self._top
+
+ @property
+ def _top(self):
+ if self.stack:
+ return self.stack[-1]
+ else:
+ return self
+
+ def _pop(self):
+ return self.stack.pop()
+
+ def _push(self, iterable):
+ new = LoopContext(iterable)
+ if self.stack:
+ new.parent = self.stack[-1]
+ return self.stack.append(new)
+
+ def __getattr__(self, key):
+ raise exceptions.RuntimeException("No loop context is established")
+
+ def __iter__(self):
+ return iter(self._top)
+
+
+class LoopContext(object):
+
+ """A magic loop variable.
+ Automatically accessible in any ``% for`` block.
+
+ See the section :ref:`loop_context` for usage
+ notes.
+
+ :attr:`parent` -> :class:`.LoopContext` or ``None``
+ The parent loop, if one exists.
+ :attr:`index` -> `int`
+ The 0-based iteration count.
+ :attr:`reverse_index` -> `int`
+ The number of iterations remaining.
+ :attr:`first` -> `bool`
+ ``True`` on the first iteration, ``False`` otherwise.
+ :attr:`last` -> `bool`
+ ``True`` on the last iteration, ``False`` otherwise.
+ :attr:`even` -> `bool`
+ ``True`` when ``index`` is even.
+ :attr:`odd` -> `bool`
+ ``True`` when ``index`` is odd.
+ """
+
+ def __init__(self, iterable):
+ self._iterable = iterable
+ self.index = 0
+ self.parent = None
+
+ def __iter__(self):
+ for i in self._iterable:
+ yield i
+ self.index += 1
+
+ @util.memoized_instancemethod
+ def __len__(self):
+ return len(self._iterable)
+
+ @property
+ def reverse_index(self):
+ return len(self) - self.index - 1
+
+ @property
+ def first(self):
+ return self.index == 0
+
+ @property
+ def last(self):
+ return self.index == len(self) - 1
+
+ @property
+ def even(self):
+ return not self.odd
+
+ @property
+ def odd(self):
+ return bool(self.index % 2)
+
+ def cycle(self, *values):
+ """Cycle through values as the loop progresses.
+ """
+ if not values:
+ raise ValueError("You must provide values to cycle through")
+ return values[self.index % len(values)]
+
+
+class _NSAttr(object):
+ def __init__(self, parent):
+ self.__parent = parent
+
+ def __getattr__(self, key):
+ ns = self.__parent
+ while ns:
+ if hasattr(ns.module, key):
+ return getattr(ns.module, key)
+ else:
+ ns = ns.inherits
+ raise AttributeError(key)
+
+
+class Namespace(object):
+
+ """Provides access to collections of rendering methods, which
+ can be local, from other templates, or from imported modules.
+
+ To access a particular rendering method referenced by a
+ :class:`.Namespace`, use plain attribute access:
+
+ .. sourcecode:: mako
+
+ ${some_namespace.foo(x, y, z)}
+
+ :class:`.Namespace` also contains several built-in attributes
+ described here.
+
+ """
+
+ def __init__(
+ self,
+ name,
+ context,
+ callables=None,
+ inherits=None,
+ populate_self=True,
+ calling_uri=None,
+ ):
+ self.name = name
+ self.context = context
+ self.inherits = inherits
+ if callables is not None:
+ self.callables = dict([(c.__name__, c) for c in callables])
+
+ callables = ()
+
+ module = None
+ """The Python module referenced by this :class:`.Namespace`.
+
+ If the namespace references a :class:`.Template`, then
+ this module is the equivalent of ``template.module``,
+ i.e. the generated module for the template.
+
+ """
+
+ template = None
+ """The :class:`.Template` object referenced by this
+ :class:`.Namespace`, if any.
+
+ """
+
+ context = None
+ """The :class:`.Context` object for this :class:`.Namespace`.
+
+ Namespaces are often created with copies of contexts that
+ contain slightly different data, particularly in inheritance
+ scenarios. Using the :class:`.Context` off of a :class:`.Namespace` one
+ can traverse an entire chain of templates that inherit from
+ one-another.
+
+ """
+
+ filename = None
+ """The path of the filesystem file used for this
+ :class:`.Namespace`'s module or template.
+
+ If this is a pure module-based
+ :class:`.Namespace`, this evaluates to ``module.__file__``. If a
+ template-based namespace, it evaluates to the original
+ template file location.
+
+ """
+
+ uri = None
+ """The URI for this :class:`.Namespace`'s template.
+
+ I.e. whatever was sent to :meth:`.TemplateLookup.get_template()`.
+
+ This is the equivalent of :attr:`.Template.uri`.
+
+ """
+
+ _templateuri = None
+
+ @util.memoized_property
+ def attr(self):
+ """Access module level attributes by name.
+
+ This accessor allows templates to supply "scalar"
+ attributes which are particularly handy in inheritance
+ relationships.
+
+ .. seealso::
+
+ :ref:`inheritance_attr`
+
+ :ref:`namespace_attr_for_includes`
+
+ """
+ return _NSAttr(self)
+
+ def get_namespace(self, uri):
+ """Return a :class:`.Namespace` corresponding to the given ``uri``.
+
+ If the given ``uri`` is a relative URI (i.e. it does not
+ contain a leading slash ``/``), the ``uri`` is adjusted to
+ be relative to the ``uri`` of the namespace itself. This
+ method is therefore mostly useful off of the built-in
+ ``local`` namespace, described in :ref:`namespace_local`.
+
+ In
+ most cases, a template wouldn't need this function, and
+ should instead use the ``<%namespace>`` tag to load
+ namespaces. However, since all ``<%namespace>`` tags are
+ evaluated before the body of a template ever runs,
+ this method can be used to locate namespaces using
+ expressions that were generated within the body code of
+ the template, or to conditionally use a particular
+ namespace.
+
+ """
+ key = (self, uri)
+ if key in self.context.namespaces:
+ return self.context.namespaces[key]
+ else:
+ ns = TemplateNamespace(
+ uri,
+ self.context._copy(),
+ templateuri=uri,
+ calling_uri=self._templateuri,
+ )
+ self.context.namespaces[key] = ns
+ return ns
+
+ def get_template(self, uri):
+ """Return a :class:`.Template` from the given ``uri``.
+
+ The ``uri`` resolution is relative to the ``uri`` of this
+ :class:`.Namespace` object's :class:`.Template`.
+
+ """
+ return _lookup_template(self.context, uri, self._templateuri)
+
+ def get_cached(self, key, **kwargs):
+ """Return a value from the :class:`.Cache` referenced by this
+ :class:`.Namespace` object's :class:`.Template`.
+
+ The advantage to this method versus direct access to the
+ :class:`.Cache` is that the configuration parameters
+ declared in ``<%page>`` take effect here, thereby calling
+ up the same configured backend as that configured
+ by ``<%page>``.
+
+ """
+
+ return self.cache.get(key, **kwargs)
+
+ @property
+ def cache(self):
+ """Return the :class:`.Cache` object referenced
+ by this :class:`.Namespace` object's
+ :class:`.Template`.
+
+ """
+ return self.template.cache
+
+ def include_file(self, uri, **kwargs):
+ """Include a file at the given ``uri``."""
+
+ _include_file(self.context, uri, self._templateuri, **kwargs)
+
+ def _populate(self, d, l):
+ for ident in l:
+ if ident == "*":
+ for (k, v) in self._get_star():
+ d[k] = v
+ else:
+ d[ident] = getattr(self, ident)
+
+ def _get_star(self):
+ if self.callables:
+ for key in self.callables:
+ yield (key, self.callables[key])
+
+ def __getattr__(self, key):
+ if key in self.callables:
+ val = self.callables[key]
+ elif self.inherits:
+ val = getattr(self.inherits, key)
+ else:
+ raise AttributeError(
+ "Namespace '%s' has no member '%s'" % (self.name, key)
+ )
+ setattr(self, key, val)
+ return val
+
+
+class TemplateNamespace(Namespace):
+
+ """A :class:`.Namespace` specific to a :class:`.Template` instance."""
+
+ def __init__(
+ self,
+ name,
+ context,
+ template=None,
+ templateuri=None,
+ callables=None,
+ inherits=None,
+ populate_self=True,
+ calling_uri=None,
+ ):
+ self.name = name
+ self.context = context
+ self.inherits = inherits
+ if callables is not None:
+ self.callables = dict([(c.__name__, c) for c in callables])
+
+ if templateuri is not None:
+ self.template = _lookup_template(context, templateuri, calling_uri)
+ self._templateuri = self.template.module._template_uri
+ elif template is not None:
+ self.template = template
+ self._templateuri = template.module._template_uri
+ else:
+ raise TypeError("'template' argument is required.")
+
+ if populate_self:
+ lclcallable, lclcontext = _populate_self_namespace(
+ context, self.template, self_ns=self
+ )
+
+ @property
+ def module(self):
+ """The Python module referenced by this :class:`.Namespace`.
+
+ If the namespace references a :class:`.Template`, then
+ this module is the equivalent of ``template.module``,
+ i.e. the generated module for the template.
+
+ """
+ return self.template.module
+
+ @property
+ def filename(self):
+ """The path of the filesystem file used for this
+ :class:`.Namespace`'s module or template.
+ """
+ return self.template.filename
+
+ @property
+ def uri(self):
+ """The URI for this :class:`.Namespace`'s template.
+
+ I.e. whatever was sent to :meth:`.TemplateLookup.get_template()`.
+
+ This is the equivalent of :attr:`.Template.uri`.
+
+ """
+ return self.template.uri
+
+ def _get_star(self):
+ if self.callables:
+ for key in self.callables:
+ yield (key, self.callables[key])
+
+ def get(key):
+ callable_ = self.template._get_def_callable(key)
+ return functools.partial(callable_, self.context)
+
+ for k in self.template.module._exports:
+ yield (k, get(k))
+
+ def __getattr__(self, key):
+ if key in self.callables:
+ val = self.callables[key]
+ elif self.template.has_def(key):
+ callable_ = self.template._get_def_callable(key)
+ val = functools.partial(callable_, self.context)
+ elif self.inherits:
+ val = getattr(self.inherits, key)
+
+ else:
+ raise AttributeError(
+ "Namespace '%s' has no member '%s'" % (self.name, key)
+ )
+ setattr(self, key, val)
+ return val
+
+
+class ModuleNamespace(Namespace):
+
+ """A :class:`.Namespace` specific to a Python module instance."""
+
+ def __init__(
+ self,
+ name,
+ context,
+ module,
+ callables=None,
+ inherits=None,
+ populate_self=True,
+ calling_uri=None,
+ ):
+ self.name = name
+ self.context = context
+ self.inherits = inherits
+ if callables is not None:
+ self.callables = dict([(c.__name__, c) for c in callables])
+
+ mod = __import__(module)
+ for token in module.split(".")[1:]:
+ mod = getattr(mod, token)
+ self.module = mod
+
+ @property
+ def filename(self):
+ """The path of the filesystem file used for this
+ :class:`.Namespace`'s module or template.
+ """
+ return self.module.__file__
+
+ def _get_star(self):
+ if self.callables:
+ for key in self.callables:
+ yield (key, self.callables[key])
+ for key in dir(self.module):
+ if key[0] != "_":
+ callable_ = getattr(self.module, key)
+ if callable(callable_):
+ yield key, functools.partial(callable_, self.context)
+
+ def __getattr__(self, key):
+ if key in self.callables:
+ val = self.callables[key]
+ elif hasattr(self.module, key):
+ callable_ = getattr(self.module, key)
+ val = functools.partial(callable_, self.context)
+ elif self.inherits:
+ val = getattr(self.inherits, key)
+ else:
+ raise AttributeError(
+ "Namespace '%s' has no member '%s'" % (self.name, key)
+ )
+ setattr(self, key, val)
+ return val
+
+
+def supports_caller(func):
+ """Apply a caller_stack compatibility decorator to a plain
+ Python function.
+
+ See the example in :ref:`namespaces_python_modules`.
+
+ """
+
+ def wrap_stackframe(context, *args, **kwargs):
+ context.caller_stack._push_frame()
+ try:
+ return func(context, *args, **kwargs)
+ finally:
+ context.caller_stack._pop_frame()
+
+ return wrap_stackframe
+
+
+def capture(context, callable_, *args, **kwargs):
+ """Execute the given template def, capturing the output into
+ a buffer.
+
+ See the example in :ref:`namespaces_python_modules`.
+
+ """
+
+ if not callable(callable_):
+ raise exceptions.RuntimeException(
+ "capture() function expects a callable as "
+ "its argument (i.e. capture(func, *args, **kwargs))"
+ )
+ context._push_buffer()
+ try:
+ callable_(*args, **kwargs)
+ finally:
+ buf = context._pop_buffer()
+ return buf.getvalue()
+
+
+def _decorate_toplevel(fn):
+ def decorate_render(render_fn):
+ def go(context, *args, **kw):
+ def y(*args, **kw):
+ return render_fn(context, *args, **kw)
+
+ try:
+ y.__name__ = render_fn.__name__[7:]
+ except TypeError:
+ # < Python 2.4
+ pass
+ return fn(y)(context, *args, **kw)
+
+ return go
+
+ return decorate_render
+
+
+def _decorate_inline(context, fn):
+ def decorate_render(render_fn):
+ dec = fn(render_fn)
+
+ def go(*args, **kw):
+ return dec(context, *args, **kw)
+
+ return go
+
+ return decorate_render
+
+
+def _include_file(context, uri, calling_uri, **kwargs):
+ """locate the template from the given uri and include it in
+ the current output."""
+
+ template = _lookup_template(context, uri, calling_uri)
+ (callable_, ctx) = _populate_self_namespace(
+ context._clean_inheritance_tokens(), template
+ )
+ kwargs = _kwargs_for_include(callable_, context._data, **kwargs)
+ if template.include_error_handler:
+ try:
+ callable_(ctx, **kwargs)
+ except Exception:
+ result = template.include_error_handler(ctx, compat.exception_as())
+ if not result:
+ compat.reraise(*sys.exc_info())
+ else:
+ callable_(ctx, **kwargs)
+
+
+def _inherit_from(context, uri, calling_uri):
+ """called by the _inherit method in template modules to set
+ up the inheritance chain at the start of a template's
+ execution."""
+
+ if uri is None:
+ return None
+ template = _lookup_template(context, uri, calling_uri)
+ self_ns = context["self"]
+ ih = self_ns
+ while ih.inherits is not None:
+ ih = ih.inherits
+ lclcontext = context._locals({"next": ih})
+ ih.inherits = TemplateNamespace(
+ "self:%s" % template.uri,
+ lclcontext,
+ template=template,
+ populate_self=False,
+ )
+ context._data["parent"] = lclcontext._data["local"] = ih.inherits
+ callable_ = getattr(template.module, "_mako_inherit", None)
+ if callable_ is not None:
+ ret = callable_(template, lclcontext)
+ if ret:
+ return ret
+
+ gen_ns = getattr(template.module, "_mako_generate_namespaces", None)
+ if gen_ns is not None:
+ gen_ns(context)
+ return (template.callable_, lclcontext)
+
+
+def _lookup_template(context, uri, relativeto):
+ lookup = context._with_template.lookup
+ if lookup is None:
+ raise exceptions.TemplateLookupException(
+ "Template '%s' has no TemplateLookup associated"
+ % context._with_template.uri
+ )
+ uri = lookup.adjust_uri(uri, relativeto)
+ try:
+ return lookup.get_template(uri)
+ except exceptions.TopLevelLookupException:
+ raise exceptions.TemplateLookupException(str(compat.exception_as()))
+
+
+def _populate_self_namespace(context, template, self_ns=None):
+ if self_ns is None:
+ self_ns = TemplateNamespace(
+ "self:%s" % template.uri,
+ context,
+ template=template,
+ populate_self=False,
+ )
+ context._data["self"] = context._data["local"] = self_ns
+ if hasattr(template.module, "_mako_inherit"):
+ ret = template.module._mako_inherit(template, context)
+ if ret:
+ return ret
+ return (template.callable_, context)
+
+
+def _render(template, callable_, args, data, as_unicode=False):
+ """create a Context and return the string
+ output of the given template and template callable."""
+
+ if as_unicode:
+ buf = util.FastEncodingBuffer(as_unicode=True)
+ elif template.bytestring_passthrough:
+ buf = compat.StringIO()
+ else:
+ buf = util.FastEncodingBuffer(
+ as_unicode=as_unicode,
+ encoding=template.output_encoding,
+ errors=template.encoding_errors,
+ )
+ context = Context(buf, **data)
+ context._outputting_as_unicode = as_unicode
+ context._set_with_template(template)
+
+ _render_context(
+ template,
+ callable_,
+ context,
+ *args,
+ **_kwargs_for_callable(callable_, data)
+ )
+ return context._pop_buffer().getvalue()
+
+
+def _kwargs_for_callable(callable_, data):
+ argspec = compat.inspect_getargspec(callable_)
+ # for normal pages, **pageargs is usually present
+ if argspec[2]:
+ return data
+
+ # for rendering defs from the top level, figure out the args
+ namedargs = argspec[0] + [v for v in argspec[1:3] if v is not None]
+ kwargs = {}
+ for arg in namedargs:
+ if arg != "context" and arg in data and arg not in kwargs:
+ kwargs[arg] = data[arg]
+ return kwargs
+
+
+def _kwargs_for_include(callable_, data, **kwargs):
+ argspec = compat.inspect_getargspec(callable_)
+ namedargs = argspec[0] + [v for v in argspec[1:3] if v is not None]
+ for arg in namedargs:
+ if arg != "context" and arg in data and arg not in kwargs:
+ kwargs[arg] = data[arg]
+ return kwargs
+
+
+def _render_context(tmpl, callable_, context, *args, **kwargs):
+ import mako.template as template
+
+ # create polymorphic 'self' namespace for this
+ # template with possibly updated context
+ if not isinstance(tmpl, template.DefTemplate):
+ # if main render method, call from the base of the inheritance stack
+ (inherit, lclcontext) = _populate_self_namespace(context, tmpl)
+ _exec_template(inherit, lclcontext, args=args, kwargs=kwargs)
+ else:
+ # otherwise, call the actual rendering method specified
+ (inherit, lclcontext) = _populate_self_namespace(context, tmpl.parent)
+ _exec_template(callable_, context, args=args, kwargs=kwargs)
+
+
+def _exec_template(callable_, context, args=None, kwargs=None):
+ """execute a rendering callable given the callable, a
+ Context, and optional explicit arguments
+
+ the contextual Template will be located if it exists, and
+ the error handling options specified on that Template will
+ be interpreted here.
+ """
+ template = context._with_template
+ if template is not None and (
+ template.format_exceptions or template.error_handler
+ ):
+ try:
+ callable_(context, *args, **kwargs)
+ except Exception:
+ _render_error(template, context, compat.exception_as())
+ except:
+ e = sys.exc_info()[0]
+ _render_error(template, context, e)
+ else:
+ callable_(context, *args, **kwargs)
+
+
+def _render_error(template, context, error):
+ if template.error_handler:
+ result = template.error_handler(context, error)
+ if not result:
+ compat.reraise(*sys.exc_info())
+ else:
+ error_template = exceptions.html_error_template()
+ if context._outputting_as_unicode:
+ context._buffer_stack[:] = [
+ util.FastEncodingBuffer(as_unicode=True)
+ ]
+ else:
+ context._buffer_stack[:] = [
+ util.FastEncodingBuffer(
+ error_template.output_encoding,
+ error_template.encoding_errors,
+ )
+ ]
+
+ context._set_with_template(error_template)
+ error_template.render_context(context, error=error)
diff --git a/third_party/python/Mako/mako/template.py b/third_party/python/Mako/mako/template.py
new file mode 100644
index 0000000000..3fd08714b8
--- /dev/null
+++ b/third_party/python/Mako/mako/template.py
@@ -0,0 +1,780 @@
+# mako/template.py
+# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
+#
+# This module is part of Mako and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+
+"""Provides the Template class, a facade for parsing, generating and executing
+template strings, as well as template runtime operations."""
+
+import json
+import os
+import re
+import shutil
+import stat
+import sys
+import tempfile
+import types
+import weakref
+
+from mako import cache
+from mako import codegen
+from mako import compat
+from mako import exceptions
+from mako import runtime
+from mako import util
+from mako.lexer import Lexer
+
+
+class Template(object):
+
+ r"""Represents a compiled template.
+
+ :class:`.Template` includes a reference to the original
+ template source (via the :attr:`.source` attribute)
+ as well as the source code of the
+ generated Python module (i.e. the :attr:`.code` attribute),
+ as well as a reference to an actual Python module.
+
+ :class:`.Template` is constructed using either a literal string
+ representing the template text, or a filename representing a filesystem
+ path to a source file.
+
+ :param text: textual template source. This argument is mutually
+ exclusive versus the ``filename`` parameter.
+
+ :param filename: filename of the source template. This argument is
+ mutually exclusive versus the ``text`` parameter.
+
+ :param buffer_filters: string list of filters to be applied
+ to the output of ``%def``\ s which are buffered, cached, or otherwise
+ filtered, after all filters
+ defined with the ``%def`` itself have been applied. Allows the
+ creation of default expression filters that let the output
+ of return-valued ``%def``\ s "opt out" of that filtering via
+ passing special attributes or objects.
+
+ :param bytestring_passthrough: When ``True``, and ``output_encoding`` is
+ set to ``None``, and :meth:`.Template.render` is used to render,
+ the `StringIO` or `cStringIO` buffer will be used instead of the
+ default "fast" buffer. This allows raw bytestrings in the
+ output stream, such as in expressions, to pass straight
+ through to the buffer. This flag is forced
+ to ``True`` if ``disable_unicode`` is also configured.
+
+ .. versionadded:: 0.4
+ Added to provide the same behavior as that of the previous series.
+
+ :param cache_args: Dictionary of cache configuration arguments that
+ will be passed to the :class:`.CacheImpl`. See :ref:`caching_toplevel`.
+
+ :param cache_dir:
+
+ .. deprecated:: 0.6
+ Use the ``'dir'`` argument in the ``cache_args`` dictionary.
+ See :ref:`caching_toplevel`.
+
+ :param cache_enabled: Boolean flag which enables caching of this
+ template. See :ref:`caching_toplevel`.
+
+ :param cache_impl: String name of a :class:`.CacheImpl` caching
+ implementation to use. Defaults to ``'beaker'``.
+
+ :param cache_type:
+
+ .. deprecated:: 0.6
+ Use the ``'type'`` argument in the ``cache_args`` dictionary.
+ See :ref:`caching_toplevel`.
+
+ :param cache_url:
+
+ .. deprecated:: 0.6
+ Use the ``'url'`` argument in the ``cache_args`` dictionary.
+ See :ref:`caching_toplevel`.
+
+ :param default_filters: List of string filter names that will
+ be applied to all expressions. See :ref:`filtering_default_filters`.
+
+ :param disable_unicode: Disables all awareness of Python Unicode
+ objects. See :ref:`unicode_disabled`.
+
+ :param enable_loop: When ``True``, enable the ``loop`` context variable.
+ This can be set to ``False`` to support templates that may
+ be making usage of the name "``loop``". Individual templates can
+ re-enable the "loop" context by placing the directive
+ ``enable_loop="True"`` inside the ``<%page>`` tag -- see
+ :ref:`migrating_loop`.
+
+ :param encoding_errors: Error parameter passed to ``encode()`` when
+ string encoding is performed. See :ref:`usage_unicode`.
+
+ :param error_handler: Python callable which is called whenever
+ compile or runtime exceptions occur. The callable is passed
+ the current context as well as the exception. If the
+ callable returns ``True``, the exception is considered to
+ be handled, else it is re-raised after the function
+ completes. Is used to provide custom error-rendering
+ functions.
+
+ .. seealso::
+
+ :paramref:`.Template.include_error_handler` - include-specific
+ error handler function
+
+ :param format_exceptions: if ``True``, exceptions which occur during
+ the render phase of this template will be caught and
+ formatted into an HTML error page, which then becomes the
+ rendered result of the :meth:`.render` call. Otherwise,
+ runtime exceptions are propagated outwards.
+
+ :param imports: String list of Python statements, typically individual
+ "import" lines, which will be placed into the module level
+ preamble of all generated Python modules. See the example
+ in :ref:`filtering_default_filters`.
+
+ :param future_imports: String list of names to import from `__future__`.
+ These will be concatenated into a comma-separated string and inserted
+ into the beginning of the template, e.g. ``futures_imports=['FOO',
+ 'BAR']`` results in ``from __future__ import FOO, BAR``. If you're
+ interested in using features like the new division operator, you must
+ use future_imports to convey that to the renderer, as otherwise the
+ import will not appear as the first executed statement in the generated
+ code and will therefore not have the desired effect.
+
+ :param include_error_handler: An error handler that runs when this template
+ is included within another one via the ``<%include>`` tag, and raises an
+ error. Compare to the :paramref:`.Template.error_handler` option.
+
+ .. versionadded:: 1.0.6
+
+ .. seealso::
+
+ :paramref:`.Template.error_handler` - top-level error handler function
+
+ :param input_encoding: Encoding of the template's source code. Can
+ be used in lieu of the coding comment. See
+ :ref:`usage_unicode` as well as :ref:`unicode_toplevel` for
+ details on source encoding.
+
+ :param lookup: a :class:`.TemplateLookup` instance that will be used
+ for all file lookups via the ``<%namespace>``,
+ ``<%include>``, and ``<%inherit>`` tags. See
+ :ref:`usage_templatelookup`.
+
+ :param module_directory: Filesystem location where generated
+ Python module files will be placed.
+
+ :param module_filename: Overrides the filename of the generated
+ Python module file. For advanced usage only.
+
+ :param module_writer: A callable which overrides how the Python
+ module is written entirely. The callable is passed the
+ encoded source content of the module and the destination
+ path to be written to. The default behavior of module writing
+ uses a tempfile in conjunction with a file move in order
+ to make the operation atomic. So a user-defined module
+ writing function that mimics the default behavior would be:
+
+ .. sourcecode:: python
+
+ import tempfile
+ import os
+ import shutil
+
+ def module_writer(source, outputpath):
+ (dest, name) = \\
+ tempfile.mkstemp(
+ dir=os.path.dirname(outputpath)
+ )
+
+ os.write(dest, source)
+ os.close(dest)
+ shutil.move(name, outputpath)
+
+ from mako.template import Template
+ mytemplate = Template(
+ filename="index.html",
+ module_directory="/path/to/modules",
+ module_writer=module_writer
+ )
+
+ The function is provided for unusual configurations where
+ certain platform-specific permissions or other special
+ steps are needed.
+
+ :param output_encoding: The encoding to use when :meth:`.render`
+ is called.
+ See :ref:`usage_unicode` as well as :ref:`unicode_toplevel`.
+
+ :param preprocessor: Python callable which will be passed
+ the full template source before it is parsed. The return
+ result of the callable will be used as the template source
+ code.
+
+ :param lexer_cls: A :class:`.Lexer` class used to parse
+ the template. The :class:`.Lexer` class is used by
+ default.
+
+ .. versionadded:: 0.7.4
+
+ :param strict_undefined: Replaces the automatic usage of
+ ``UNDEFINED`` for any undeclared variables not located in
+ the :class:`.Context` with an immediate raise of
+ ``NameError``. The advantage is immediate reporting of
+ missing variables which include the name.
+
+ .. versionadded:: 0.3.6
+
+ :param uri: string URI or other identifier for this template.
+ If not provided, the ``uri`` is generated from the filesystem
+ path, or from the in-memory identity of a non-file-based
+ template. The primary usage of the ``uri`` is to provide a key
+ within :class:`.TemplateLookup`, as well as to generate the
+ file path of the generated Python module file, if
+ ``module_directory`` is specified.
+
+ """
+
+ lexer_cls = Lexer
+
+ def __init__(
+ self,
+ text=None,
+ filename=None,
+ uri=None,
+ format_exceptions=False,
+ error_handler=None,
+ lookup=None,
+ output_encoding=None,
+ encoding_errors="strict",
+ module_directory=None,
+ cache_args=None,
+ cache_impl="beaker",
+ cache_enabled=True,
+ cache_type=None,
+ cache_dir=None,
+ cache_url=None,
+ module_filename=None,
+ input_encoding=None,
+ disable_unicode=False,
+ module_writer=None,
+ bytestring_passthrough=False,
+ default_filters=None,
+ buffer_filters=(),
+ strict_undefined=False,
+ imports=None,
+ future_imports=None,
+ enable_loop=True,
+ preprocessor=None,
+ lexer_cls=None,
+ include_error_handler=None,
+ ):
+ if uri:
+ self.module_id = re.sub(r"\W", "_", uri)
+ self.uri = uri
+ elif filename:
+ self.module_id = re.sub(r"\W", "_", filename)
+ drive, path = os.path.splitdrive(filename)
+ path = os.path.normpath(path).replace(os.path.sep, "/")
+ self.uri = path
+ else:
+ self.module_id = "memory:" + hex(id(self))
+ self.uri = self.module_id
+
+ u_norm = self.uri
+ if u_norm.startswith("/"):
+ u_norm = u_norm[1:]
+ u_norm = os.path.normpath(u_norm)
+ if u_norm.startswith(".."):
+ raise exceptions.TemplateLookupException(
+ 'Template uri "%s" is invalid - '
+ "it cannot be relative outside "
+ "of the root path." % self.uri
+ )
+
+ self.input_encoding = input_encoding
+ self.output_encoding = output_encoding
+ self.encoding_errors = encoding_errors
+ self.disable_unicode = disable_unicode
+ self.bytestring_passthrough = bytestring_passthrough or disable_unicode
+ self.enable_loop = enable_loop
+ self.strict_undefined = strict_undefined
+ self.module_writer = module_writer
+
+ if compat.py3k and disable_unicode:
+ raise exceptions.UnsupportedError(
+ "Mako for Python 3 does not " "support disabling Unicode"
+ )
+ elif output_encoding and disable_unicode:
+ raise exceptions.UnsupportedError(
+ "output_encoding must be set to "
+ "None when disable_unicode is used."
+ )
+ if default_filters is None:
+ if compat.py3k or self.disable_unicode:
+ self.default_filters = ["str"]
+ else:
+ self.default_filters = ["unicode"]
+ else:
+ self.default_filters = default_filters
+ self.buffer_filters = buffer_filters
+
+ self.imports = imports
+ self.future_imports = future_imports
+ self.preprocessor = preprocessor
+
+ if lexer_cls is not None:
+ self.lexer_cls = lexer_cls
+
+ # if plain text, compile code in memory only
+ if text is not None:
+ (code, module) = _compile_text(self, text, filename)
+ self._code = code
+ self._source = text
+ ModuleInfo(module, None, self, filename, code, text, uri)
+ elif filename is not None:
+ # if template filename and a module directory, load
+ # a filesystem-based module file, generating if needed
+ if module_filename is not None:
+ path = module_filename
+ elif module_directory is not None:
+ path = os.path.abspath(
+ os.path.join(
+ os.path.normpath(module_directory), u_norm + ".py"
+ )
+ )
+ else:
+ path = None
+ module = self._compile_from_file(path, filename)
+ else:
+ raise exceptions.RuntimeException(
+ "Template requires text or filename"
+ )
+
+ self.module = module
+ self.filename = filename
+ self.callable_ = self.module.render_body
+ self.format_exceptions = format_exceptions
+ self.error_handler = error_handler
+ self.include_error_handler = include_error_handler
+ self.lookup = lookup
+
+ self.module_directory = module_directory
+
+ self._setup_cache_args(
+ cache_impl,
+ cache_enabled,
+ cache_args,
+ cache_type,
+ cache_dir,
+ cache_url,
+ )
+
+ @util.memoized_property
+ def reserved_names(self):
+ if self.enable_loop:
+ return codegen.RESERVED_NAMES
+ else:
+ return codegen.RESERVED_NAMES.difference(["loop"])
+
+ def _setup_cache_args(
+ self,
+ cache_impl,
+ cache_enabled,
+ cache_args,
+ cache_type,
+ cache_dir,
+ cache_url,
+ ):
+ self.cache_impl = cache_impl
+ self.cache_enabled = cache_enabled
+ if cache_args:
+ self.cache_args = cache_args
+ else:
+ self.cache_args = {}
+
+ # transfer deprecated cache_* args
+ if cache_type:
+ self.cache_args["type"] = cache_type
+ if cache_dir:
+ self.cache_args["dir"] = cache_dir
+ if cache_url:
+ self.cache_args["url"] = cache_url
+
+ def _compile_from_file(self, path, filename):
+ if path is not None:
+ util.verify_directory(os.path.dirname(path))
+ filemtime = os.stat(filename)[stat.ST_MTIME]
+ if (
+ not os.path.exists(path)
+ or os.stat(path)[stat.ST_MTIME] < filemtime
+ ):
+ data = util.read_file(filename)
+ _compile_module_file(
+ self, data, filename, path, self.module_writer
+ )
+ module = compat.load_module(self.module_id, path)
+ del sys.modules[self.module_id]
+ if module._magic_number != codegen.MAGIC_NUMBER:
+ data = util.read_file(filename)
+ _compile_module_file(
+ self, data, filename, path, self.module_writer
+ )
+ module = compat.load_module(self.module_id, path)
+ del sys.modules[self.module_id]
+ ModuleInfo(module, path, self, filename, None, None, None)
+ else:
+ # template filename and no module directory, compile code
+ # in memory
+ data = util.read_file(filename)
+ code, module = _compile_text(self, data, filename)
+ self._source = None
+ self._code = code
+ ModuleInfo(module, None, self, filename, code, None, None)
+ return module
+
+ @property
+ def source(self):
+ """Return the template source code for this :class:`.Template`."""
+
+ return _get_module_info_from_callable(self.callable_).source
+
+ @property
+ def code(self):
+ """Return the module source code for this :class:`.Template`."""
+
+ return _get_module_info_from_callable(self.callable_).code
+
+ @util.memoized_property
+ def cache(self):
+ return cache.Cache(self)
+
+ @property
+ def cache_dir(self):
+ return self.cache_args["dir"]
+
+ @property
+ def cache_url(self):
+ return self.cache_args["url"]
+
+ @property
+ def cache_type(self):
+ return self.cache_args["type"]
+
+ def render(self, *args, **data):
+ """Render the output of this template as a string.
+
+ If the template specifies an output encoding, the string
+ will be encoded accordingly, else the output is raw (raw
+ output uses `cStringIO` and can't handle multibyte
+ characters). A :class:`.Context` object is created corresponding
+ to the given data. Arguments that are explicitly declared
+ by this template's internal rendering method are also
+ pulled from the given ``*args``, ``**data`` members.
+
+ """
+ return runtime._render(self, self.callable_, args, data)
+
+ def render_unicode(self, *args, **data):
+ """Render the output of this template as a unicode object."""
+
+ return runtime._render(
+ self, self.callable_, args, data, as_unicode=True
+ )
+
+ def render_context(self, context, *args, **kwargs):
+ """Render this :class:`.Template` with the given context.
+
+ The data is written to the context's buffer.
+
+ """
+ if getattr(context, "_with_template", None) is None:
+ context._set_with_template(self)
+ runtime._render_context(self, self.callable_, context, *args, **kwargs)
+
+ def has_def(self, name):
+ return hasattr(self.module, "render_%s" % name)
+
+ def get_def(self, name):
+ """Return a def of this template as a :class:`.DefTemplate`."""
+
+ return DefTemplate(self, getattr(self.module, "render_%s" % name))
+
+ def list_defs(self):
+ """return a list of defs in the template.
+
+ .. versionadded:: 1.0.4
+
+ """
+ return [i[7:] for i in dir(self.module) if i[:7] == "render_"]
+
+ def _get_def_callable(self, name):
+ return getattr(self.module, "render_%s" % name)
+
+ @property
+ def last_modified(self):
+ return self.module._modified_time
+
+
+class ModuleTemplate(Template):
+
+ """A Template which is constructed given an existing Python module.
+
+ e.g.::
+
+ t = Template("this is a template")
+ f = file("mymodule.py", "w")
+ f.write(t.code)
+ f.close()
+
+ import mymodule
+
+ t = ModuleTemplate(mymodule)
+ print(t.render())
+
+ """
+
+ def __init__(
+ self,
+ module,
+ module_filename=None,
+ template=None,
+ template_filename=None,
+ module_source=None,
+ template_source=None,
+ output_encoding=None,
+ encoding_errors="strict",
+ disable_unicode=False,
+ bytestring_passthrough=False,
+ format_exceptions=False,
+ error_handler=None,
+ lookup=None,
+ cache_args=None,
+ cache_impl="beaker",
+ cache_enabled=True,
+ cache_type=None,
+ cache_dir=None,
+ cache_url=None,
+ include_error_handler=None,
+ ):
+ self.module_id = re.sub(r"\W", "_", module._template_uri)
+ self.uri = module._template_uri
+ self.input_encoding = module._source_encoding
+ self.output_encoding = output_encoding
+ self.encoding_errors = encoding_errors
+ self.disable_unicode = disable_unicode
+ self.bytestring_passthrough = bytestring_passthrough or disable_unicode
+ self.enable_loop = module._enable_loop
+
+ if compat.py3k and disable_unicode:
+ raise exceptions.UnsupportedError(
+ "Mako for Python 3 does not " "support disabling Unicode"
+ )
+ elif output_encoding and disable_unicode:
+ raise exceptions.UnsupportedError(
+ "output_encoding must be set to "
+ "None when disable_unicode is used."
+ )
+
+ self.module = module
+ self.filename = template_filename
+ ModuleInfo(
+ module,
+ module_filename,
+ self,
+ template_filename,
+ module_source,
+ template_source,
+ module._template_uri,
+ )
+
+ self.callable_ = self.module.render_body
+ self.format_exceptions = format_exceptions
+ self.error_handler = error_handler
+ self.include_error_handler = include_error_handler
+ self.lookup = lookup
+ self._setup_cache_args(
+ cache_impl,
+ cache_enabled,
+ cache_args,
+ cache_type,
+ cache_dir,
+ cache_url,
+ )
+
+
+class DefTemplate(Template):
+
+ """A :class:`.Template` which represents a callable def in a parent
+ template."""
+
+ def __init__(self, parent, callable_):
+ self.parent = parent
+ self.callable_ = callable_
+ self.output_encoding = parent.output_encoding
+ self.module = parent.module
+ self.encoding_errors = parent.encoding_errors
+ self.format_exceptions = parent.format_exceptions
+ self.error_handler = parent.error_handler
+ self.include_error_handler = parent.include_error_handler
+ self.enable_loop = parent.enable_loop
+ self.lookup = parent.lookup
+ self.bytestring_passthrough = parent.bytestring_passthrough
+
+ def get_def(self, name):
+ return self.parent.get_def(name)
+
+
+class ModuleInfo(object):
+
+ """Stores information about a module currently loaded into
+ memory, provides reverse lookups of template source, module
+ source code based on a module's identifier.
+
+ """
+
+ _modules = weakref.WeakValueDictionary()
+
+ def __init__(
+ self,
+ module,
+ module_filename,
+ template,
+ template_filename,
+ module_source,
+ template_source,
+ template_uri,
+ ):
+ self.module = module
+ self.module_filename = module_filename
+ self.template_filename = template_filename
+ self.module_source = module_source
+ self.template_source = template_source
+ self.template_uri = template_uri
+ self._modules[module.__name__] = template._mmarker = self
+ if module_filename:
+ self._modules[module_filename] = self
+
+ @classmethod
+ def get_module_source_metadata(cls, module_source, full_line_map=False):
+ source_map = re.search(
+ r"__M_BEGIN_METADATA(.+?)__M_END_METADATA", module_source, re.S
+ ).group(1)
+ source_map = json.loads(source_map)
+ source_map["line_map"] = dict(
+ (int(k), int(v)) for k, v in source_map["line_map"].items()
+ )
+ if full_line_map:
+ f_line_map = source_map["full_line_map"] = []
+ line_map = source_map["line_map"]
+
+ curr_templ_line = 1
+ for mod_line in range(1, max(line_map)):
+ if mod_line in line_map:
+ curr_templ_line = line_map[mod_line]
+ f_line_map.append(curr_templ_line)
+ return source_map
+
+ @property
+ def code(self):
+ if self.module_source is not None:
+ return self.module_source
+ else:
+ return util.read_python_file(self.module_filename)
+
+ @property
+ def source(self):
+ if self.template_source is not None:
+ if self.module._source_encoding and not isinstance(
+ self.template_source, compat.text_type
+ ):
+ return self.template_source.decode(
+ self.module._source_encoding
+ )
+ else:
+ return self.template_source
+ else:
+ data = util.read_file(self.template_filename)
+ if self.module._source_encoding:
+ return data.decode(self.module._source_encoding)
+ else:
+ return data
+
+
+def _compile(template, text, filename, generate_magic_comment):
+ lexer = template.lexer_cls(
+ text,
+ filename,
+ disable_unicode=template.disable_unicode,
+ input_encoding=template.input_encoding,
+ preprocessor=template.preprocessor,
+ )
+ node = lexer.parse()
+ source = codegen.compile(
+ node,
+ template.uri,
+ filename,
+ default_filters=template.default_filters,
+ buffer_filters=template.buffer_filters,
+ imports=template.imports,
+ future_imports=template.future_imports,
+ source_encoding=lexer.encoding,
+ generate_magic_comment=generate_magic_comment,
+ disable_unicode=template.disable_unicode,
+ strict_undefined=template.strict_undefined,
+ enable_loop=template.enable_loop,
+ reserved_names=template.reserved_names,
+ )
+ return source, lexer
+
+
+def _compile_text(template, text, filename):
+ identifier = template.module_id
+ source, lexer = _compile(
+ template,
+ text,
+ filename,
+ generate_magic_comment=template.disable_unicode,
+ )
+
+ cid = identifier
+ if not compat.py3k and isinstance(cid, compat.text_type):
+ cid = cid.encode()
+ module = types.ModuleType(cid)
+ code = compile(source, cid, "exec")
+
+ # this exec() works for 2.4->3.3.
+ exec(code, module.__dict__, module.__dict__)
+ return (source, module)
+
+
+def _compile_module_file(template, text, filename, outputpath, module_writer):
+ source, lexer = _compile(
+ template, text, filename, generate_magic_comment=True
+ )
+
+ if isinstance(source, compat.text_type):
+ source = source.encode(lexer.encoding or "ascii")
+
+ if module_writer:
+ module_writer(source, outputpath)
+ else:
+ # make tempfiles in the same location as the ultimate
+ # location. this ensures they're on the same filesystem,
+ # avoiding synchronization issues.
+ (dest, name) = tempfile.mkstemp(dir=os.path.dirname(outputpath))
+
+ os.write(dest, source)
+ os.close(dest)
+ shutil.move(name, outputpath)
+
+
+def _get_module_info_from_callable(callable_):
+ if compat.py3k:
+ return _get_module_info(callable_.__globals__["__name__"])
+ else:
+ return _get_module_info(callable_.func_globals["__name__"])
+
+
+def _get_module_info(filename):
+ return ModuleInfo._modules[filename]
diff --git a/third_party/python/Mako/mako/util.py b/third_party/python/Mako/mako/util.py
new file mode 100644
index 0000000000..16e3c72658
--- /dev/null
+++ b/third_party/python/Mako/mako/util.py
@@ -0,0 +1,400 @@
+# mako/util.py
+# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
+#
+# This module is part of Mako and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+from __future__ import absolute_import
+
+from ast import parse
+import codecs
+import collections
+import operator
+import os
+import re
+import timeit
+
+from mako import compat
+
+
+def update_wrapper(decorated, fn):
+ decorated.__wrapped__ = fn
+ decorated.__name__ = fn.__name__
+ return decorated
+
+
+class PluginLoader(object):
+ def __init__(self, group):
+ self.group = group
+ self.impls = {}
+
+ def load(self, name):
+ if name in self.impls:
+ return self.impls[name]()
+ else:
+ import pkg_resources
+
+ for impl in pkg_resources.iter_entry_points(self.group, name):
+ self.impls[name] = impl.load
+ return impl.load()
+ else:
+ from mako import exceptions
+
+ raise exceptions.RuntimeException(
+ "Can't load plugin %s %s" % (self.group, name)
+ )
+
+ def register(self, name, modulepath, objname):
+ def load():
+ mod = __import__(modulepath)
+ for token in modulepath.split(".")[1:]:
+ mod = getattr(mod, token)
+ return getattr(mod, objname)
+
+ self.impls[name] = load
+
+
+def verify_directory(dir_):
+ """create and/or verify a filesystem directory."""
+
+ tries = 0
+
+ while not os.path.exists(dir_):
+ try:
+ tries += 1
+ os.makedirs(dir_, compat.octal("0775"))
+ except:
+ if tries > 5:
+ raise
+
+
+def to_list(x, default=None):
+ if x is None:
+ return default
+ if not isinstance(x, (list, tuple)):
+ return [x]
+ else:
+ return x
+
+
+class memoized_property(object):
+
+ """A read-only @property that is only evaluated once."""
+
+ def __init__(self, fget, doc=None):
+ self.fget = fget
+ self.__doc__ = doc or fget.__doc__
+ self.__name__ = fget.__name__
+
+ def __get__(self, obj, cls):
+ if obj is None:
+ return self
+ obj.__dict__[self.__name__] = result = self.fget(obj)
+ return result
+
+
+class memoized_instancemethod(object):
+
+ """Decorate a method memoize its return value.
+
+ Best applied to no-arg methods: memoization is not sensitive to
+ argument values, and will always return the same value even when
+ called with different arguments.
+
+ """
+
+ def __init__(self, fget, doc=None):
+ self.fget = fget
+ self.__doc__ = doc or fget.__doc__
+ self.__name__ = fget.__name__
+
+ def __get__(self, obj, cls):
+ if obj is None:
+ return self
+
+ def oneshot(*args, **kw):
+ result = self.fget(obj, *args, **kw)
+
+ def memo(*a, **kw):
+ return result
+
+ memo.__name__ = self.__name__
+ memo.__doc__ = self.__doc__
+ obj.__dict__[self.__name__] = memo
+ return result
+
+ oneshot.__name__ = self.__name__
+ oneshot.__doc__ = self.__doc__
+ return oneshot
+
+
+class SetLikeDict(dict):
+
+ """a dictionary that has some setlike methods on it"""
+
+ def union(self, other):
+ """produce a 'union' of this dict and another (at the key level).
+
+ values in the second dict take precedence over that of the first"""
+ x = SetLikeDict(**self)
+ x.update(other)
+ return x
+
+
+class FastEncodingBuffer(object):
+
+ """a very rudimentary buffer that is faster than StringIO,
+ but doesn't crash on unicode data like cStringIO."""
+
+ def __init__(self, encoding=None, errors="strict", as_unicode=False):
+ self.data = collections.deque()
+ self.encoding = encoding
+ if as_unicode:
+ self.delim = compat.u("")
+ else:
+ self.delim = ""
+ self.as_unicode = as_unicode
+ self.errors = errors
+ self.write = self.data.append
+
+ def truncate(self):
+ self.data = collections.deque()
+ self.write = self.data.append
+
+ def getvalue(self):
+ if self.encoding:
+ return self.delim.join(self.data).encode(
+ self.encoding, self.errors
+ )
+ else:
+ return self.delim.join(self.data)
+
+
+class LRUCache(dict):
+
+ """A dictionary-like object that stores a limited number of items,
+ discarding lesser used items periodically.
+
+ this is a rewrite of LRUCache from Myghty to use a periodic timestamp-based
+ paradigm so that synchronization is not really needed. the size management
+ is inexact.
+ """
+
+ class _Item(object):
+ def __init__(self, key, value):
+ self.key = key
+ self.value = value
+ self.timestamp = timeit.default_timer()
+
+ def __repr__(self):
+ return repr(self.value)
+
+ def __init__(self, capacity, threshold=0.5):
+ self.capacity = capacity
+ self.threshold = threshold
+
+ def __getitem__(self, key):
+ item = dict.__getitem__(self, key)
+ item.timestamp = timeit.default_timer()
+ return item.value
+
+ def values(self):
+ return [i.value for i in dict.values(self)]
+
+ def setdefault(self, key, value):
+ if key in self:
+ return self[key]
+ else:
+ self[key] = value
+ return value
+
+ def __setitem__(self, key, value):
+ item = dict.get(self, key)
+ if item is None:
+ item = self._Item(key, value)
+ dict.__setitem__(self, key, item)
+ else:
+ item.value = value
+ self._manage_size()
+
+ def _manage_size(self):
+ while len(self) > self.capacity + self.capacity * self.threshold:
+ bytime = sorted(
+ dict.values(self),
+ key=operator.attrgetter("timestamp"),
+ reverse=True,
+ )
+ for item in bytime[self.capacity :]:
+ try:
+ del self[item.key]
+ except KeyError:
+ # if we couldn't find a key, most likely some other thread
+ # broke in on us. loop around and try again
+ break
+
+
+# Regexp to match python magic encoding line
+_PYTHON_MAGIC_COMMENT_re = re.compile(
+ r"[ \t\f]* \# .* coding[=:][ \t]*([-\w.]+)", re.VERBOSE
+)
+
+
+def parse_encoding(fp):
+ """Deduce the encoding of a Python source file (binary mode) from magic
+ comment.
+
+ It does this in the same way as the `Python interpreter`__
+
+ .. __: http://docs.python.org/ref/encodings.html
+
+ The ``fp`` argument should be a seekable file object in binary mode.
+ """
+ pos = fp.tell()
+ fp.seek(0)
+ try:
+ line1 = fp.readline()
+ has_bom = line1.startswith(codecs.BOM_UTF8)
+ if has_bom:
+ line1 = line1[len(codecs.BOM_UTF8) :]
+
+ m = _PYTHON_MAGIC_COMMENT_re.match(line1.decode("ascii", "ignore"))
+ if not m:
+ try:
+ parse(line1.decode("ascii", "ignore"))
+ except (ImportError, SyntaxError):
+ # Either it's a real syntax error, in which case the source
+ # is not valid python source, or line2 is a continuation of
+ # line1, in which case we don't want to scan line2 for a magic
+ # comment.
+ pass
+ else:
+ line2 = fp.readline()
+ m = _PYTHON_MAGIC_COMMENT_re.match(
+ line2.decode("ascii", "ignore")
+ )
+
+ if has_bom:
+ if m:
+ raise SyntaxError(
+ "python refuses to compile code with both a UTF8"
+ " byte-order-mark and a magic encoding comment"
+ )
+ return "utf_8"
+ elif m:
+ return m.group(1)
+ else:
+ return None
+ finally:
+ fp.seek(pos)
+
+
+def sorted_dict_repr(d):
+ """repr() a dictionary with the keys in order.
+
+ Used by the lexer unit test to compare parse trees based on strings.
+
+ """
+ keys = list(d.keys())
+ keys.sort()
+ return "{" + ", ".join(["%r: %r" % (k, d[k]) for k in keys]) + "}"
+
+
+def restore__ast(_ast):
+ """Attempt to restore the required classes to the _ast module if it
+ appears to be missing them
+ """
+ if hasattr(_ast, "AST"):
+ return
+ _ast.PyCF_ONLY_AST = 2 << 9
+ m = compile(
+ """\
+def foo(): pass
+class Bar(object): pass
+if False: pass
+baz = 'mako'
+1 + 2 - 3 * 4 / 5
+6 // 7 % 8 << 9 >> 10
+11 & 12 ^ 13 | 14
+15 and 16 or 17
+-baz + (not +18) - ~17
+baz and 'foo' or 'bar'
+(mako is baz == baz) is not baz != mako
+mako > baz < mako >= baz <= mako
+mako in baz not in mako""",
+ "<unknown>",
+ "exec",
+ _ast.PyCF_ONLY_AST,
+ )
+ _ast.Module = type(m)
+
+ for cls in _ast.Module.__mro__:
+ if cls.__name__ == "mod":
+ _ast.mod = cls
+ elif cls.__name__ == "AST":
+ _ast.AST = cls
+
+ _ast.FunctionDef = type(m.body[0])
+ _ast.ClassDef = type(m.body[1])
+ _ast.If = type(m.body[2])
+
+ _ast.Name = type(m.body[3].targets[0])
+ _ast.Store = type(m.body[3].targets[0].ctx)
+ _ast.Str = type(m.body[3].value)
+
+ _ast.Sub = type(m.body[4].value.op)
+ _ast.Add = type(m.body[4].value.left.op)
+ _ast.Div = type(m.body[4].value.right.op)
+ _ast.Mult = type(m.body[4].value.right.left.op)
+
+ _ast.RShift = type(m.body[5].value.op)
+ _ast.LShift = type(m.body[5].value.left.op)
+ _ast.Mod = type(m.body[5].value.left.left.op)
+ _ast.FloorDiv = type(m.body[5].value.left.left.left.op)
+
+ _ast.BitOr = type(m.body[6].value.op)
+ _ast.BitXor = type(m.body[6].value.left.op)
+ _ast.BitAnd = type(m.body[6].value.left.left.op)
+
+ _ast.Or = type(m.body[7].value.op)
+ _ast.And = type(m.body[7].value.values[0].op)
+
+ _ast.Invert = type(m.body[8].value.right.op)
+ _ast.Not = type(m.body[8].value.left.right.op)
+ _ast.UAdd = type(m.body[8].value.left.right.operand.op)
+ _ast.USub = type(m.body[8].value.left.left.op)
+
+ _ast.Or = type(m.body[9].value.op)
+ _ast.And = type(m.body[9].value.values[0].op)
+
+ _ast.IsNot = type(m.body[10].value.ops[0])
+ _ast.NotEq = type(m.body[10].value.ops[1])
+ _ast.Is = type(m.body[10].value.left.ops[0])
+ _ast.Eq = type(m.body[10].value.left.ops[1])
+
+ _ast.Gt = type(m.body[11].value.ops[0])
+ _ast.Lt = type(m.body[11].value.ops[1])
+ _ast.GtE = type(m.body[11].value.ops[2])
+ _ast.LtE = type(m.body[11].value.ops[3])
+
+ _ast.In = type(m.body[12].value.ops[0])
+ _ast.NotIn = type(m.body[12].value.ops[1])
+
+
+def read_file(path, mode="rb"):
+ fp = open(path, mode)
+ try:
+ data = fp.read()
+ return data
+ finally:
+ fp.close()
+
+
+def read_python_file(path):
+ fp = open(path, "rb")
+ try:
+ encoding = parse_encoding(fp)
+ data = fp.read()
+ if encoding:
+ data = data.decode(encoding)
+ return data
+ finally:
+ fp.close()
diff --git a/third_party/python/glean_parser/glean_parser-11.0.1.dist-info/AUTHORS.md b/third_party/python/glean_parser/glean_parser-13.0.0.dist-info/AUTHORS.md
index 525116ee7e..525116ee7e 100644
--- a/third_party/python/glean_parser/glean_parser-11.0.1.dist-info/AUTHORS.md
+++ b/third_party/python/glean_parser/glean_parser-13.0.0.dist-info/AUTHORS.md
diff --git a/third_party/python/glean_parser/glean_parser-11.0.1.dist-info/LICENSE b/third_party/python/glean_parser/glean_parser-13.0.0.dist-info/LICENSE
index a612ad9813..a612ad9813 100644
--- a/third_party/python/glean_parser/glean_parser-11.0.1.dist-info/LICENSE
+++ b/third_party/python/glean_parser/glean_parser-13.0.0.dist-info/LICENSE
diff --git a/third_party/python/glean_parser/glean_parser-11.0.1.dist-info/METADATA b/third_party/python/glean_parser/glean_parser-13.0.0.dist-info/METADATA
index 201d8bb48b..1e31df3dd4 100644
--- a/third_party/python/glean_parser/glean_parser-11.0.1.dist-info/METADATA
+++ b/third_party/python/glean_parser/glean_parser-13.0.0.dist-info/METADATA
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: glean-parser
-Version: 11.0.1
+Version: 13.0.0
Summary: Parser tools for Mozilla's Glean telemetry
Home-page: https://github.com/mozilla/glean_parser
Author: The Glean Team
@@ -79,6 +79,23 @@ $ glean_parser check < ping.json
## Unreleased
+## 13.0.0
+
+- BREAKING CHANGE: Support metadata field `include_info_sections` ([bug 1866559](https://bugzilla.mozilla.org/show_bug.cgi?id=1866559))
+
+## 12.0.1
+
+- Fix Rust codegen for object metric type ([#662](https://github.com/mozilla/glean_parser/pull/662))
+
+## 12.0.0
+
+- Add new metric type object (only Rust codegen support right now) ([#587](https://github.com/mozilla/glean_parser/pull/587))
+
+## 11.1.0
+
+- Add Go log outputter (`go_server`) ([#645](https://github.com/mozilla/glean_parser/pull/645))
+- Add Python log outputter (`python_server`) ([MPP-3642](https://mozilla-hub.atlassian.net/browse/MPP-3642))
+
## 11.0.1
- Fix javascript_server template to include non-event metric parameters in #record call for event metrics ([#643](https://github.com/mozilla/glean_parser/pull/643))
diff --git a/third_party/python/glean_parser/glean_parser-11.0.1.dist-info/RECORD b/third_party/python/glean_parser/glean_parser-13.0.0.dist-info/RECORD
index 417484d30b..62e4bb6fbb 100644
--- a/third_party/python/glean_parser/glean_parser-11.0.1.dist-info/RECORD
+++ b/third_party/python/glean_parser/glean_parser-13.0.0.dist-info/RECORD
@@ -2,28 +2,31 @@ glean_parser/__init__.py,sha256=bJljD052_0y-efcBhYpllICVCXOMHLcXRLNyrvfgt5A,533
glean_parser/__main__.py,sha256=Rw0PpuQtAvdHJMK1YLozeZkc6x1yjeNZwidu4faovdk,8633
glean_parser/coverage.py,sha256=2IwC4XMDtDamMkBFoYilmqJzW4gyypq65YVCur8SNas,4405
glean_parser/data_review.py,sha256=BweeeTkNNS6HrIDkztawhbDByrk_-Avxpg7YeST3VAs,2152
+glean_parser/go_server.py,sha256=SCcGrjRktlPyl79LbjIvtBeCNYVOXOW4Q8xkuks0bzE,5345
glean_parser/javascript.py,sha256=w4ZhNBHBKWYk0h3t7G0Ud2tR__hRqzn9dlEXNKLdQrA,11230
glean_parser/javascript_server.py,sha256=SDV9tPL1uZMyS1VSyo5lOFuNPFHZu-PZxr1vhND-GzM,7971
glean_parser/kotlin.py,sha256=5z8_74xlqvHDsedwZhGf1_qb7swPEgIZumkJIuj3ef8,12598
glean_parser/lint.py,sha256=STqdgyOhR4Q3fHivSizgn9bOOyqrNHhzjaqyJxz6qzI,19948
glean_parser/markdown.py,sha256=GkCr1CrV6mnRQseT6FO1-JJ7Eup8X3lxUfRMBTxXpe4,9066
-glean_parser/metrics.py,sha256=uWOJdZRB9udMni2EWXcL3P1T4pRIlJ5kiE5fJsHkmdU,12450
+glean_parser/metrics.py,sha256=YAO8wPuRHTLkdT9M4zh9ZwoFI1_VS8O9oQqwZNYyDp0,14612
glean_parser/parser.py,sha256=cUOnvSXKfEBg8YTpRcWiPcMwpFpK1TTqsVO_zjUtpR4,15309
-glean_parser/pings.py,sha256=la9HdJTjtSqC7vc5-JuANW0otbozTnFARlIMgKoypGU,2982
+glean_parser/pings.py,sha256=AQ-fBmIx2GKQv6J2NyTFfHHZzSnApZZoC770LlstkoI,3180
+glean_parser/python_server.py,sha256=3ZsqeNJknKO9yvtBJWxe67JthzSMqNMuo9DfhgF2kvg,4790
glean_parser/ruby_server.py,sha256=-bNXjfXWwHWUHmLJVvfi6jCyw8q0MBwx9VXVWQ3bU-A,5189
-glean_parser/rust.py,sha256=PJzTfYWzAumJYCP5IYPc6fhS_Qa30Q8NTK9plg3sDnk,6744
+glean_parser/rust.py,sha256=UEHeIZlToxCBelfec5sl_l_uLZfk8f_OUXqa_ZoEvnk,7330
glean_parser/swift.py,sha256=T1BSGahd9wUd6VDeNC89SdN6M34jKXDlydMpSI0QLOs,8379
glean_parser/tags.py,sha256=bemKYvcbMO4JrghiNSe-A4BNNDtx_FlUPkgrPPJy84Y,1391
-glean_parser/translate.py,sha256=C7FY7AAbnVsPZOu2bKELW1CfTwnvLGpmgzY7uMDqOec,8233
+glean_parser/translate.py,sha256=luKQoraARZ2tjenHs0SVtCxflnYaMkzPYFfKEdKdSqQ,8403
glean_parser/translation_options.py,sha256=Lxzr6G7MP0tC_ZYlZXftS4j0SLiqO-5mGVTEc7ggXis,2037
-glean_parser/util.py,sha256=X5YFAU4kWdDJjMsJzXH-QJVSjUJc_qvXktiM-dJSfzo,16004
+glean_parser/util.py,sha256=KgvmjETOV1IIGD4hF_o5zcUDE-wp3SHxrNHM1niU0CM,16033
glean_parser/validate_ping.py,sha256=0TNvILH6dtzJDys3W8Kqorw6kk03me73OCUDtpoHcXU,2118
glean_parser/schemas/metrics.1-0-0.schema.yaml,sha256=cND3cvi6iBfPUVmtfIBQfGJV9AALpbvN7nu8E33_J-o,19566
-glean_parser/schemas/metrics.2-0-0.schema.yaml,sha256=sfrARxefWy1WN5HxUKjwjN8lGobbPds5l7Y46VHfP1g,25849
+glean_parser/schemas/metrics.2-0-0.schema.yaml,sha256=wx1q0L4C0-Vcwk1SPU6t8OfjDEQvgrwwEG6xfSHO1MI,26365
glean_parser/schemas/pings.1-0-0.schema.yaml,sha256=hwCnsKpEysmrmVp-QHGBArEkVY3vaU1rVsxlTwhAzws,4315
-glean_parser/schemas/pings.2-0-0.schema.yaml,sha256=l-nIuyXJ9-D0X_U6hzGVbhIBhtZDg-rGau-RDrhgpng,4705
+glean_parser/schemas/pings.2-0-0.schema.yaml,sha256=vDyvFT8KwAwaqyWHG4y6pFNrsc3NO7OyDDagA2eTeqM,5415
glean_parser/schemas/tags.1-0-0.schema.yaml,sha256=OGXIJlvvVW1vaqB_NVZnwKeZ-sLlfH57vjBSHbj6DNI,1231
glean_parser/templates/data_review.jinja2,sha256=jeYU29T1zLSyu9fKBBFu5BFPfIw8_hmOUXw8RXhRXK8,3287
+glean_parser/templates/go_server.jinja2,sha256=Jy1e0uQqr_WZNoj-AWnygRmygX2jyj_GQMMV8mSah2k,6825
glean_parser/templates/javascript.buildinfo.jinja2,sha256=4mXiZCQIk9if4lxlA05kpSIL4a95IdwGwqle2OqqNAs,474
glean_parser/templates/javascript.jinja2,sha256=cT_bG-jC6m4afECXmcsqHwiiHjRuVtJnfv90OD2Mwxw,2669
glean_parser/templates/javascript_server.jinja2,sha256=H991yQOKJMwSgM0bLEA-Q5Z15LWsfEPh6bTYz_owSCU,9423
@@ -31,14 +34,15 @@ glean_parser/templates/kotlin.buildinfo.jinja2,sha256=X0lk2SNu5OIIj2i6mUyF9CWFQI
glean_parser/templates/kotlin.geckoview.jinja2,sha256=MJOgtoDXmBjE9pwk-G6T89y36RZuMbDWM_-DBN_gFJo,5099
glean_parser/templates/kotlin.jinja2,sha256=3DqUMXJRkmTvSp_5IRyvGmw5iXYWdox7coMFe3YDxcc,5247
glean_parser/templates/markdown.jinja2,sha256=vAHHGGm28HRDPd3zO_wQMAUZIuxE9uQ7hl3NpXxcKV4,3425
+glean_parser/templates/python_server.jinja2,sha256=gu2C1rkn760IqBCG2SWaK7o32T1ify94wDEsudLPUg8,7260
glean_parser/templates/qmldir.jinja2,sha256=m6IGsp-tgTiOfQ7VN8XW6GqX0gJqJkt3B6Pkaul6FVo,156
glean_parser/templates/ruby_server.jinja2,sha256=vm4BEenOqzomQNTLFfMOzlWHARnsWUjTBbnR-v2cadI,6247
-glean_parser/templates/rust.jinja2,sha256=pdbjq_JGm8XWHsVXk0m2xZ5Pd-Y9T_zxJfZKBoT0ERU,3635
-glean_parser/templates/swift.jinja2,sha256=NfZdvrG8LGT4H2AWk-vB_GDTMcpW1XZJcApO4OF5AYE,4874
-glean_parser-11.0.1.dist-info/AUTHORS.md,sha256=yxgj8MioO4wUnrh0gmfb8l3DJJrf-l4HmmEDbQsbbNI,455
-glean_parser-11.0.1.dist-info/LICENSE,sha256=HyVuytGSiAUQ6ErWBHTqt1iSGHhLmlC8fO7jTCuR8dU,16725
-glean_parser-11.0.1.dist-info/METADATA,sha256=z5yLEYgY4EV1e_cHNQhenhkwK5ryURgljfTfaYK-NYs,30877
-glean_parser-11.0.1.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
-glean_parser-11.0.1.dist-info/entry_points.txt,sha256=mf9d3sv8BwSjjR58x9KDnpVkONCnv3fPQC2NjJl15Xg,68
-glean_parser-11.0.1.dist-info/top_level.txt,sha256=q7T3duD-9tYZFyDry6Wv2LcdMsK2jGnzdDFhxWcT2Z8,13
-glean_parser-11.0.1.dist-info/RECORD,,
+glean_parser/templates/rust.jinja2,sha256=wlV0OZvV3Mk2ulrqFkN1vGjdsahsupEy2TQvWxQKzww,5439
+glean_parser/templates/swift.jinja2,sha256=xkvVsTpfK0QK3tI32wGqzxm2hqFNaBQ6Y71rKIsCmAI,4944
+glean_parser-13.0.0.dist-info/AUTHORS.md,sha256=yxgj8MioO4wUnrh0gmfb8l3DJJrf-l4HmmEDbQsbbNI,455
+glean_parser-13.0.0.dist-info/LICENSE,sha256=HyVuytGSiAUQ6ErWBHTqt1iSGHhLmlC8fO7jTCuR8dU,16725
+glean_parser-13.0.0.dist-info/METADATA,sha256=BzYfW5GF-wZLrokfvUTiZg7JT5BTfB1E3xIDKW6h_BY,31493
+glean_parser-13.0.0.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
+glean_parser-13.0.0.dist-info/entry_points.txt,sha256=mf9d3sv8BwSjjR58x9KDnpVkONCnv3fPQC2NjJl15Xg,68
+glean_parser-13.0.0.dist-info/top_level.txt,sha256=q7T3duD-9tYZFyDry6Wv2LcdMsK2jGnzdDFhxWcT2Z8,13
+glean_parser-13.0.0.dist-info/RECORD,,
diff --git a/third_party/python/glean_parser/glean_parser-11.0.1.dist-info/WHEEL b/third_party/python/glean_parser/glean_parser-13.0.0.dist-info/WHEEL
index 98c0d20b7a..98c0d20b7a 100644
--- a/third_party/python/glean_parser/glean_parser-11.0.1.dist-info/WHEEL
+++ b/third_party/python/glean_parser/glean_parser-13.0.0.dist-info/WHEEL
diff --git a/third_party/python/glean_parser/glean_parser-11.0.1.dist-info/entry_points.txt b/third_party/python/glean_parser/glean_parser-13.0.0.dist-info/entry_points.txt
index 08fde9d655..08fde9d655 100644
--- a/third_party/python/glean_parser/glean_parser-11.0.1.dist-info/entry_points.txt
+++ b/third_party/python/glean_parser/glean_parser-13.0.0.dist-info/entry_points.txt
diff --git a/third_party/python/glean_parser/glean_parser-11.0.1.dist-info/top_level.txt b/third_party/python/glean_parser/glean_parser-13.0.0.dist-info/top_level.txt
index a7f3a37918..a7f3a37918 100644
--- a/third_party/python/glean_parser/glean_parser-11.0.1.dist-info/top_level.txt
+++ b/third_party/python/glean_parser/glean_parser-13.0.0.dist-info/top_level.txt
diff --git a/third_party/python/glean_parser/glean_parser/go_server.py b/third_party/python/glean_parser/glean_parser/go_server.py
new file mode 100644
index 0000000000..403a0d71f4
--- /dev/null
+++ b/third_party/python/glean_parser/glean_parser/go_server.py
@@ -0,0 +1,145 @@
+# -*- coding: utf-8 -*-
+
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+"""
+Outputter to generate server Go code for collecting events.
+
+This outputter is different from the rest of the outputters in that the code it
+generates does not use the Glean SDK. It is meant to be used to collect events
+in server-side environments. In these environments SDK assumptions to measurement
+window and connectivity don't hold.
+Generated code takes care of assembling pings with metrics, and serializing to messages
+conforming to Glean schema.
+
+Warning: this outputter supports limited set of metrics,
+see `SUPPORTED_METRIC_TYPES` below.
+
+The generated code creates the following:
+* Two methods for logging an Event metric
+ one with and one without user request info specified
+"""
+from collections import defaultdict
+from pathlib import Path
+from typing import Any, Dict, Optional, List
+
+from . import __version__
+from . import metrics
+from . import util
+
+# Adding a metric here will require updating the `generate_metric_type` function
+# and require adjustments to `metrics` variables the the template.
+SUPPORTED_METRIC_TYPES = ["string", "quantity", "event"]
+
+
+def generate_event_type_name(metric: metrics.Metric) -> str:
+ return f"Event{util.Camelize(metric.category)}{util.Camelize(metric.name)}"
+
+
+def generate_metric_name(metric: metrics.Metric) -> str:
+ return f"{metric.category}.{metric.name}"
+
+
+def generate_extra_name(extra: str) -> str:
+ return util.Camelize(extra)
+
+
+def generate_metric_argument_name(metric: metrics.Metric) -> str:
+ return f"{util.Camelize(metric.category)}{util.Camelize(metric.name)}"
+
+
+def generate_metric_type(metric_type: str) -> str:
+ if metric_type == "quantity":
+ return "int64"
+ elif metric_type == "string":
+ return "string"
+ elif metric_type == "boolean":
+ return "bool"
+ else:
+ print("❌ Unable to generate Go type from metric type: " + metric_type)
+ exit
+ return "NONE"
+
+
+def clean_string(s: str) -> str:
+ return s.replace("\n", " ").rstrip()
+
+
+def output_go(
+ objs: metrics.ObjectTree, output_dir: Path, options: Optional[Dict[str, Any]]
+) -> None:
+ """
+ Given a tree of objects, output Go code to `output_dir`.
+
+ The output is a single file containing all the code for assembling pings with
+ metrics, serializing, and submitting.
+
+ :param objects: A tree of objects (metrics and pings) as returned from
+ `parser.parse_objects`.
+ :param output_dir: Path to an output directory to write to.
+ """
+
+ template = util.get_jinja2_template(
+ "go_server.jinja2",
+ filters=(
+ ("event_type_name", generate_event_type_name),
+ ("event_extra_name", generate_extra_name),
+ ("metric_name", generate_metric_name),
+ ("metric_argument_name", generate_metric_argument_name),
+ ("go_metric_type", generate_metric_type),
+ ("clean_string", clean_string),
+ ),
+ )
+
+ PING_METRIC_ERROR_MSG = (
+ " Server-side environment is simplified and only supports the events ping type."
+ + " You should not be including pings.yaml with your parser call"
+ + " or referencing any other pings in your metric configuration."
+ )
+ if "pings" in objs:
+ print("❌ Ping definition found." + PING_METRIC_ERROR_MSG)
+ return
+
+ # Go through all metrics in objs and build a map of
+ # ping->list of metric categories->list of metrics
+ # for easier processing in the template.
+ ping_to_metrics: Dict[str, Dict[str, List[metrics.Metric]]] = defaultdict(dict)
+ for _category_key, category_val in objs.items():
+ for _metric_name, metric in category_val.items():
+ if isinstance(metric, metrics.Metric):
+ if metric.type not in SUPPORTED_METRIC_TYPES:
+ print(
+ "❌ Ignoring unsupported metric type: "
+ + f"{metric.type}:{metric.name}."
+ + " Reach out to Glean team to add support for this"
+ + " metric type."
+ )
+ continue
+ for ping in metric.send_in_pings:
+ if ping != "events":
+ (
+ print(
+ "❌ Non-events ping reference found."
+ + PING_METRIC_ERROR_MSG
+ + f"Ignoring the {ping} ping type."
+ )
+ )
+ continue
+ metrics_by_type = ping_to_metrics[ping]
+ metrics_list = metrics_by_type.setdefault(metric.type, [])
+ metrics_list.append(metric)
+
+ if "event" not in ping_to_metrics["events"]:
+ print("❌ No event metrics found...at least one event metric is required")
+ return
+
+ extension = ".go"
+ filepath = output_dir / ("server_events" + extension)
+ with filepath.open("w", encoding="utf-8") as fd:
+ fd.write(
+ template.render(
+ parser_version=__version__, events_ping=ping_to_metrics["events"]
+ )
+ )
diff --git a/third_party/python/glean_parser/glean_parser/metrics.py b/third_party/python/glean_parser/glean_parser/metrics.py
index 5738239f97..accfbd763d 100644
--- a/third_party/python/glean_parser/glean_parser/metrics.py
+++ b/third_party/python/glean_parser/glean_parser/metrics.py
@@ -181,6 +181,7 @@ class Metric:
d.pop("unit")
d.pop("_config", None)
d.pop("_generate_enums", None)
+ d.pop("_generate_structure", None)
return d
def _serialize_input(self) -> Dict[str, util.JSONType]:
@@ -434,4 +435,63 @@ class Text(Metric):
typename = "text"
+class Object(Metric):
+ typename = "object"
+
+ def __init__(self, *args, **kwargs):
+ structure = kwargs.pop("structure", None)
+ if not structure:
+ raise ValueError("`object` is missing required parameter `structure`")
+
+ self._generate_structure = self.validate_structure(structure)
+ super().__init__(*args, **kwargs)
+
+ ALLOWED_TOPLEVEL = {"type", "properties", "items"}
+ ALLOWED_TYPES = ["object", "array", "number", "string", "boolean"]
+
+ @staticmethod
+ def _validate_substructure(structure):
+ extra = set(structure.keys()) - Object.ALLOWED_TOPLEVEL
+ if extra:
+ extra = ", ".join(extra)
+ allowed = ", ".join(Object.ALLOWED_TOPLEVEL)
+ raise ValueError(
+ f"Found additional fields: {extra}. Only allowed: {allowed}"
+ )
+
+ if "type" not in structure or structure["type"] not in Object.ALLOWED_TYPES:
+ raise ValueError("invalid or missing `type` in object structure")
+
+ if structure["type"] == "object":
+ if "items" in structure:
+ raise ValueError("`items` not allowed in object structure")
+
+ if "properties" not in structure:
+ raise ValueError("`properties` missing for type `object`")
+
+ for key in structure["properties"]:
+ value = structure["properties"][key]
+ structure["properties"][key] = Object._validate_substructure(value)
+
+ if structure["type"] == "array":
+ if "properties" in structure:
+ raise ValueError("`properties` not allowed in array structure")
+
+ if "items" not in structure:
+ raise ValueError("`items` missing for type `array`")
+
+ value = structure["items"]
+ structure["items"] = Object._validate_substructure(value)
+
+ return structure
+
+ @staticmethod
+ def validate_structure(structure):
+ if None:
+ raise ValueError("`structure` needed for object metric.")
+
+ structure = Object._validate_substructure(structure)
+ return structure
+
+
ObjectTree = Dict[str, Dict[str, Union[Metric, pings.Ping, tags.Tag]]]
diff --git a/third_party/python/glean_parser/glean_parser/pings.py b/third_party/python/glean_parser/glean_parser/pings.py
index 3099fa1d16..b4145ea68d 100644
--- a/third_party/python/glean_parser/glean_parser/pings.py
+++ b/third_party/python/glean_parser/glean_parser/pings.py
@@ -45,6 +45,7 @@ class Ping:
metadata = {}
self.metadata = metadata
self.precise_timestamps = self.metadata.get("precise_timestamps", True)
+ self.include_info_sections = self.metadata.get("include_info_sections", True)
if data_reviews is None:
data_reviews = []
self.data_reviews = data_reviews
@@ -90,6 +91,9 @@ class Ping:
d = self.serialize()
modified_dict = util.remove_output_params(d, "defined_in")
modified_dict = util.remove_output_params(modified_dict, "precise_timestamps")
+ modified_dict = util.remove_output_params(
+ modified_dict, "include_info_sections"
+ )
return modified_dict
def identifier(self) -> str:
diff --git a/third_party/python/glean_parser/glean_parser/python_server.py b/third_party/python/glean_parser/glean_parser/python_server.py
new file mode 100644
index 0000000000..8ead0eb315
--- /dev/null
+++ b/third_party/python/glean_parser/glean_parser/python_server.py
@@ -0,0 +1,130 @@
+# -*- coding: utf-8 -*-
+
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+"""
+Outputter to generate server Python code for collecting events.
+
+This outputter is different from the rest of the outputters in that the code it
+generates does not use the Glean SDK. It is meant to be used to collect events
+in server-side environments. In these environments SDK assumptions to measurement
+window and connectivity don't hold.
+Generated code takes care of assembling pings with metrics, and serializing to messages
+conforming to Glean schema.
+
+Warning: this outputter supports limited set of metrics,
+see `SUPPORTED_METRIC_TYPES` below.
+
+The generated code creates a `ServerEventLogger` class for each ping that has
+at least one event metric. The class has a `record` method for each event metric.
+"""
+from collections import defaultdict
+from pathlib import Path
+from typing import Any, Dict, Optional, List
+
+from . import __version__
+from . import metrics
+from . import util
+
+# Adding a metric here will require updating the `generate_metric_type` function
+# and require adjustments to `metrics` variables the the template.
+SUPPORTED_METRIC_TYPES = ["string", "quantity", "event"]
+
+
+def camelize(s: str) -> str:
+ return util.Camelize(s)
+
+
+def generate_metric_type(metric_type: str) -> str:
+ if metric_type == "quantity":
+ return "int"
+ elif metric_type == "string":
+ return "str"
+ elif metric_type == "boolean":
+ return "bool"
+ else:
+ print("❌ Unable to generate Python type from metric type: " + metric_type)
+ exit
+ return "NONE"
+
+
+def clean_string(s: str) -> str:
+ return s.replace("\n", " ").rstrip()
+
+
+def generate_ping_factory_method(ping: str) -> str:
+ return f"create_{util.snake_case(ping)}_server_event_logger"
+
+
+def generate_event_record_function_name(event_metric: metrics.Metric) -> str:
+ return (
+ f"record_{util.snake_case(event_metric.category)}_"
+ + f"{util.snake_case(event_metric.name)}"
+ )
+
+
+def output_python(
+ objs: metrics.ObjectTree, output_dir: Path, options: Optional[Dict[str, Any]]
+) -> None:
+ """
+ Given a tree of objects, output Python code to `output_dir`.
+
+ The output is a file containing all the code for assembling pings with
+ metrics, serializing, and submitting, and an empty `__init__.py` file to
+ make the directory a package.
+
+ :param objects: A tree of objects (metrics and pings) as returned from
+ `parser.parse_objects`.
+ :param output_dir: Path to an output directory to write to.
+ """
+
+ template = util.get_jinja2_template(
+ "python_server.jinja2",
+ filters=(
+ ("camelize", camelize),
+ ("py_metric_type", generate_metric_type),
+ ("clean_string", clean_string),
+ ("factory_method", generate_ping_factory_method),
+ ("record_event_function_name", generate_event_record_function_name),
+ ),
+ )
+
+ # Go through all metrics in objs and build a map of
+ # ping->list of metric categories->list of metrics
+ # for easier processing in the template.
+ ping_to_metrics: Dict[str, Dict[str, List[metrics.Metric]]] = defaultdict(dict)
+ for _category_key, category_val in objs.items():
+ for _metric_name, metric in category_val.items():
+ if isinstance(metric, metrics.Metric):
+ if metric.type not in SUPPORTED_METRIC_TYPES:
+ print(
+ "❌ Ignoring unsupported metric type: "
+ + f"{metric.type}:{metric.name}."
+ + " Reach out to Glean team to add support for this"
+ + " metric type."
+ )
+ continue
+ for ping in metric.send_in_pings:
+ metrics_by_type = ping_to_metrics[ping]
+ metrics_list = metrics_by_type.setdefault(metric.type, [])
+ metrics_list.append(metric)
+
+ for ping, metrics_by_type in ping_to_metrics.items():
+ if "event" not in metrics_by_type:
+ print(
+ f"❌ No event metrics found for ping: {ping}."
+ + " At least one event metric is required."
+ )
+ return
+
+ extension = ".py"
+ filepath = output_dir / ("server_events" + extension)
+ with filepath.open("w", encoding="utf-8") as fd:
+ fd.write(template.render(parser_version=__version__, pings=ping_to_metrics))
+
+ # create an empty `__init__.py` file to make the directory a package
+ init_file = output_dir / "__init__.py"
+ with init_file.open("w", encoding="utf-8") as fd:
+ fd.write("")
diff --git a/third_party/python/glean_parser/glean_parser/rust.py b/third_party/python/glean_parser/glean_parser/rust.py
index eb3355e382..6dd4426d84 100644
--- a/third_party/python/glean_parser/glean_parser/rust.py
+++ b/third_party/python/glean_parser/glean_parser/rust.py
@@ -65,7 +65,7 @@ def rust_datatypes_filter(value):
elif isinstance(value, metrics.CowString):
yield f'::std::borrow::Cow::from("{value.inner}")'
elif isinstance(value, str):
- yield f'"{value}".into()'
+ yield f"{json.dumps(value)}.into()"
elif isinstance(value, metrics.Rate):
yield "CommonMetricData("
first = True
@@ -115,6 +115,11 @@ def type_name(obj):
return "{}<{}>".format(class_name(obj.type), generic)
+ generate_structure = getattr(obj, "_generate_structure", [])
+ if len(generate_structure):
+ generic = util.Camelize(obj.name) + "Object"
+ return "{}<{}>".format(class_name(obj.type), generic)
+
return class_name(obj.type)
@@ -133,6 +138,21 @@ def extra_type_name(typ: str) -> str:
return "UNSUPPORTED"
+def structure_type_name(typ: str) -> str:
+ """
+ Returns the corresponding Rust type for structure items.
+ """
+
+ if typ == "boolean":
+ return "bool"
+ elif typ == "string":
+ return "String"
+ elif typ == "number":
+ return "i64"
+ else:
+ return "UNSUPPORTED"
+
+
def class_name(obj_type):
"""
Returns the Rust class name for a given metric or ping type.
@@ -190,6 +210,7 @@ def output_rust(
("camelize", util.camelize),
("type_name", type_name),
("extra_type_name", extra_type_name),
+ ("structure_type_name", structure_type_name),
("ctor", ctor),
("extra_keys", extra_keys),
),
diff --git a/third_party/python/glean_parser/glean_parser/schemas/metrics.2-0-0.schema.yaml b/third_party/python/glean_parser/glean_parser/schemas/metrics.2-0-0.schema.yaml
index 0bc8d500c6..0e785c5303 100644
--- a/third_party/python/glean_parser/glean_parser/schemas/metrics.2-0-0.schema.yaml
+++ b/third_party/python/glean_parser/glean_parser/schemas/metrics.2-0-0.schema.yaml
@@ -119,6 +119,9 @@ definitions:
- `text`: Record long text data.
+ - `object`: Record structured data based on a pre-defined schema
+ Additional properties: `structure`.
+
type: string
enum:
- event
@@ -140,6 +143,7 @@ definitions:
- labeled_counter
- rate
- text
+ - object
description:
title: Description
@@ -567,6 +571,15 @@ definitions:
so glean_parser can find it.
type: string
+ structure:
+ title: A subset of a JSON schema definition
+ description: |
+ The expected structure of data, defined in a strict subset of
+ YAML-dialect JSON Schema (Draft 7) supporting keys "type"
+ (only values "object", "array", "number", "string", and "boolean"),
+ "properties", and "items".
+ type: object
+
required:
- type
- bugs
diff --git a/third_party/python/glean_parser/glean_parser/schemas/pings.2-0-0.schema.yaml b/third_party/python/glean_parser/glean_parser/schemas/pings.2-0-0.schema.yaml
index 2f25405d45..6679a8066b 100644
--- a/third_party/python/glean_parser/glean_parser/schemas/pings.2-0-0.schema.yaml
+++ b/third_party/python/glean_parser/glean_parser/schemas/pings.2-0-0.schema.yaml
@@ -84,6 +84,18 @@ additionalProperties:
When `false` Glean uses minute-precise timestamps for
the ping's start/end time.
type: boolean
+ include_info_sections:
+ title: Include Info Sections
+ description: |
+ When `true`, assemble and include the `client_info` and `ping_info`
+ sections in the ping on submission.
+ When `false`, omit the `client_info` and `ping_info` sections when
+ assembling the ping on submission.
+ Defaults to `true`.
+
+ Interaction with `include_client_id`: `include_client_id` only takes
+ effect when `metadata.include_info_sections` is `true`.
+ type: boolean
default: {}
@@ -93,6 +105,9 @@ additionalProperties:
**Required.**
When `true`, include the `client_id` value in the ping.
+
+ Interaction with `metadata.include_info_sections`: `include_client_id`
+ only takes effect when `metadata.include_info_sections` is `true`.
type: boolean
send_if_empty:
diff --git a/third_party/python/glean_parser/glean_parser/templates/go_server.jinja2 b/third_party/python/glean_parser/glean_parser/templates/go_server.jinja2
new file mode 100644
index 0000000000..0a26831b0f
--- /dev/null
+++ b/third_party/python/glean_parser/glean_parser/templates/go_server.jinja2
@@ -0,0 +1,225 @@
+{# The final Go code is autogenerated, but this template is not. Please file bugs! #}
+package glean
+
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+// AUTOGENERATED BY glean_parser v{{ parser_version }}. DO NOT EDIT.
+
+// required imports
+import (
+ "encoding/json"
+ "fmt"
+ "strconv"
+ "time"
+
+ "github.com/google/uuid"
+)
+
+// log type string used to identify logs to process in the Moz Data Pipeline
+var gleanEventMozlogType string = "glean-server-event"
+
+type GleanEventsLogger struct {
+ AppID string // Application Id to identify application per Glean standards
+ AppDisplayVersion string // Version of application emitting the event
+ AppChannel string // Channel to differentiate logs from prod/beta/staging/devel
+}
+
+// exported type for public method parameters
+type RequestInfo struct {
+ UserAgent string
+ IpAddress string
+}
+
+// default empty values will be omitted in json from ping struct definition
+var defaultRequestInfo = RequestInfo{
+ UserAgent: "",
+ IpAddress: "",
+}
+
+// structs to construct the glean ping
+type clientInfo struct {
+ TelemetrySDKBuild string `json:"telemetry_sdk_build"`
+ FirstRunDate string `json:"first_run_date"`
+ OS string `json:"os"`
+ OSVersion string `json:"os_version"`
+ Architecture string `json:"architecture"`
+ AppBuild string `json:"app_build"`
+ AppDisplayVersion string `json:"app_display_version"`
+ AppChannel string `json:"app_channel"`
+}
+
+type pingInfo struct {
+ Seq int `json:"seq"`
+ StartTime string `json:"start_time"`
+ EndTime string `json:"end_time"`
+}
+
+type ping struct {
+ DocumentNamespace string `json:"document_namespace"`
+ DocumentType string `json:"document_type"`
+ DocumentVersion string `json:"document_version"`
+ DocumentID string `json:"document_id"`
+ UserAgent string `json:"user_agent,omitempty"`
+ IpAddress string `json:"ip_address,omitempty"`
+ Payload string `json:"payload"`
+}
+
+type metrics map[string]map[string]interface{}
+
+type pingPayload struct {
+ ClientInfo clientInfo `json:"client_info"`
+ PingInfo pingInfo `json:"ping_info"`
+ Metrics metrics `json:"metrics"`
+ Events []gleanEvent `json:"events"`
+}
+
+type gleanEvent struct {
+ Category string `json:"category"`
+ Name string `json:"name"`
+ Timestamp int64 `json:"timestamp"`
+ Extra map[string]string `json:"extra"`
+}
+
+type logEnvelope struct {
+ Timestamp string
+ Logger string
+ Type string
+ Fields ping
+}
+
+func (g GleanEventsLogger) createClientInfo() clientInfo {
+ // Fields with default values are required in the Glean schema, but not used in server context
+ return clientInfo{
+ TelemetrySDKBuild: "glean_parser v{{ parser_version }}",
+ FirstRunDate: "Unknown",
+ OS: "Unknown",
+ OSVersion: "Unknown",
+ Architecture: "Unknown",
+ AppBuild: "Unknown",
+ AppDisplayVersion: g.AppDisplayVersion,
+ AppChannel: g.AppChannel,
+ }
+}
+
+func createPingInfo() pingInfo {
+ {# times are ISO-8601 strings, e.g. "2023-12-19T22:09:17.440Z" #}
+ var now = time.Now().UTC().Format("2006-01-02T15:04:05.000Z")
+ return pingInfo{
+ Seq: 0,
+ StartTime: now,
+ EndTime: now,
+ }
+}
+
+func (g GleanEventsLogger) createPing(documentType string, config RequestInfo, payload pingPayload) ping {
+ var payloadJson, payloadErr = json.Marshal(payload)
+ if payloadErr != nil {
+ panic("Unable to marshal payload to json")
+ }
+ var documentId = uuid.New()
+ return ping{
+ DocumentNamespace: g.AppID,
+ DocumentType: documentType,
+ DocumentVersion: "1",
+ DocumentID: documentId.String(),
+ UserAgent: config.UserAgent,
+ IpAddress: config.IpAddress,
+ Payload: string(payloadJson),
+ }
+}
+
+// method called by each event method.
+// construct the ping, wrap it in the envelope, and print to stdout
+func (g GleanEventsLogger) record(
+ documentType string,
+ requestInfo RequestInfo,
+ metrics metrics,
+ events []gleanEvent,
+) {
+ var telemetryPayload = pingPayload{
+ ClientInfo: g.createClientInfo(),
+ PingInfo: createPingInfo(),
+ Metrics: metrics,
+ Events: events,
+ }
+
+ var ping = g.createPing(documentType, requestInfo, telemetryPayload)
+
+ var envelope = logEnvelope{
+ Timestamp: strconv.FormatInt(time.Now().UnixNano(), 10),
+ Logger: "glean",
+ Type: gleanEventMozlogType,
+ Fields: ping,
+ }
+ var envelopeJson, envelopeErr = json.Marshal(envelope)
+ if envelopeErr != nil {
+ panic("Unable to marshal log envelope to json")
+ }
+ fmt.Println(string(envelopeJson))
+}
+
+{% for event in events_ping["event"] %}
+type {{ event|event_type_name }} struct {
+ {% for metric_type, metrics in events_ping.items() %}
+ {% if metric_type != 'event' %}
+ {% for metric in metrics %}
+ {{ metric|metric_argument_name }} {{ metric.type|go_metric_type }} // {{ metric.description|clean_string }}
+ {% endfor %}
+ {% endif %}
+ {% endfor %}
+ {% for extra, metadata in event.extra_keys.items() %}
+ {{ extra|event_extra_name }} {{ metadata.type|go_metric_type }} // {{ metadata.description|clean_string }}
+ {% endfor %}
+}
+
+// Record and submit an {{ event|event_type_name }} event.
+// {{ event.description|clean_string }}
+func (g GleanEventsLogger) Record{{ event|event_type_name }}(
+ requestInfo RequestInfo,
+ params {{ event|event_type_name }},
+) {
+ var metrics = metrics{
+ {% for metric_type, metrics in events_ping.items() %}
+ {% if metric_type != 'event' %}
+ "{{ metric_type }}": {
+ {% for metric in metrics %}
+ "{{ metric|metric_name }}": params.{{ metric|metric_argument_name }},
+ {% endfor %}
+ },
+ {% endif %}
+ {% endfor %}
+ }
+ var extraKeys = map[string]string{
+ {% for extra, metadata in event.extra_keys.items() %}
+ {# convert all extra fields to string for submission #}
+ {% if metadata.type == 'boolean' %}
+ "{{ extra }}": fmt.Sprintf("%t", params.{{ extra|event_extra_name }}),
+ {% elif metadata.type == 'quantity' %}
+ "{{ extra }}": fmt.Sprintf("%d", params.{{ extra|event_extra_name }}),
+ {% else %}
+ "{{ extra }}": params.{{ extra|event_extra_name }},
+ {% endif %}
+ {% endfor %}
+ }
+ var events = []gleanEvent{
+ gleanEvent{
+ Category: "{{ event.category }}",
+ Name: "{{ event.name }}",
+ Timestamp: time.Now().UnixMilli(),
+ Extra: extraKeys,
+ },
+ }
+ g.record("events", requestInfo, metrics, events)
+}
+
+// Record and submit an {{ event|event_type_name }} event omitting user request info
+// {{ event.description|clean_string }}
+func (g GleanEventsLogger) Record{{ event|event_type_name }}WithoutUserInfo(
+ params {{ event|event_type_name }},
+) {
+ g.Record{{ event|event_type_name }}(defaultRequestInfo, params)
+}
+
+{% endfor %}
diff --git a/third_party/python/glean_parser/glean_parser/templates/python_server.jinja2 b/third_party/python/glean_parser/glean_parser/templates/python_server.jinja2
new file mode 100644
index 0000000000..689fab2109
--- /dev/null
+++ b/third_party/python/glean_parser/glean_parser/templates/python_server.jinja2
@@ -0,0 +1,194 @@
+{# The final Go code is autogenerated, but this template is not. Please file bugs! #}
+"""
+This Source Code Form is subject to the terms of the Mozilla Public
+License, v. 2.0. If a copy of the MPL was not distributed with this
+file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+AUTOGENERATED BY glean_parser v{{ parser_version }}. DO NOT EDIT. DO NOT COMMIT.
+"""
+
+from __future__ import annotations
+from datetime import datetime, timezone
+from typing import Any
+from uuid import uuid4
+import json
+
+GLEAN_EVENT_MOZLOG_TYPE = "glean-server-event"
+
+
+{% for ping, metrics_by_type in pings.items() %}
+class {{ ping|camelize }}ServerEventLogger:
+ def __init__(
+ self, application_id: str, app_display_version: str, channel: str
+ ) -> None:
+ """
+ Create {{ ping|camelize }}ServerEventLogger instance.
+
+ :param str application_id: The application ID.
+ :param str app_display_version: The application display version.
+ :param str channel: The channel.
+ """
+ self._application_id = application_id
+ self._app_display_version = app_display_version
+ self._channel = channel
+
+ def _record(
+ self,
+ user_agent: str,
+ ip_address: str,
+ {% for metric_type, metrics in metrics_by_type.items() %}
+ {% if metric_type != 'event' %}
+ {% for metric in metrics %}
+ {{ metric.category }}_{{ metric.name }}: {{ metric.type|py_metric_type }},
+ {% endfor %}
+ {% endif %}
+ {% endfor %}
+ event: dict[str, Any]
+ ) -> None:
+ now = datetime.now(timezone.utc)
+ timestamp = now.isoformat()
+ event["timestamp"] = int(1000.0 * now.timestamp()) # Milliseconds since epoch
+ event_payload = {
+ "metrics": {
+ {% for metric_type, metrics in metrics_by_type.items() %}
+ {% if metric_type != 'event' %}
+ "{{ metric_type }}": {
+ {% for metric in metrics %}
+ "{{ metric.category }}.{{ metric.name }}": {{ metric.category }}_{{ metric.name }},
+ {% endfor %}
+ },
+ {% endif %}
+ {% endfor %}
+ },
+ "events": [event],
+ "ping_info": {
+ # seq is required in the Glean schema, however is not useful in server context
+ "seq": 0,
+ "start_time": timestamp,
+ "end_time": timestamp,
+ },
+ # `Unknown` fields below are required in the Glean schema, however they are
+ # not useful in server context
+ "client_info": {
+ "telemetry_sdk_build": "glean_parser v{{ parser_version }}",
+ "first_run_date": "Unknown",
+ "os": "Unknown",
+ "os_version": "Unknown",
+ "architecture": "Unknown",
+ "app_build": "Unknown",
+ "app_display_version": self._app_display_version,
+ "app_channel": self._channel,
+ },
+ }
+ event_payload_serialized = json.dumps(event_payload)
+
+ # This is the message structure that Decoder expects:
+ # https://github.com/mozilla/gcp-ingestion/pull/2400
+ ping = {
+ "document_namespace": self._application_id,
+ "document_type": "{{ ping }}",
+ "document_version": "1",
+ "document_id": str(uuid4()),
+ "user_agent": user_agent,
+ "ip_address": ip_address,
+ "payload": event_payload_serialized,
+ }
+
+
+ self.emit_record(now, ping)
+
+ def emit_record(self, now: datetime, ping:dict[str, Any]) -> None:
+ """Log the ping to STDOUT.
+ Applications might want to override this method to use their own logging.
+ If doing so, make sure to log the ping as JSON, and to include the
+ `Type: GLEAN_EVENT_MOZLOG_TYPE`."""
+ ping_envelope = {
+ "Timestamp": now.isoformat(),
+ "Logger": "glean",
+ "Type": GLEAN_EVENT_MOZLOG_TYPE,
+ "Fields": ping,
+ }
+ ping_envelope_serialized = json.dumps(ping_envelope)
+
+ print(ping_envelope_serialized)
+
+ {% for event in metrics_by_type["event"] %}
+ def {{ event|record_event_function_name }}(
+ self,
+ user_agent: str,
+ ip_address: str,
+ {% for metric_type, metrics in metrics_by_type.items() %}
+ {% if metric_type != 'event' %}
+ {% for metric in metrics %}
+ {{ metric.category }}_{{ metric.name }}: {{ metric.type|py_metric_type }},
+ {% endfor %}
+ {% endif %}
+ {% endfor %}
+ {% for extra, metadata in event.extra_keys.items() %}
+ {{ extra }}: {{ metadata.type|py_metric_type }},
+ {% endfor %}
+ ) -> None:
+ """
+ Record and submit a {{ event.category }}_{{ event.name }} event:
+ {{ event.description|clean_string }}
+ Event is logged to STDOUT via `print`.
+
+ :param str user_agent: The user agent.
+ :param str ip_address: The IP address. Will be used to decode Geo information
+ and scrubbed at ingestion.
+ {% for metric_type, metrics in metrics_by_type.items() %}
+ {% if metric_type != 'event' %}
+ {% for metric in metrics %}
+ :param {{ metric.type|py_metric_type }} {{ metric.category }}_{{ metric.name }}: {{ metric.description|clean_string }}
+ {% endfor %}
+ {% endif %}
+ {% endfor %}
+ {% if event.extra_keys %}
+ {% for extra, metadata in event.extra_keys.items() %}
+ :param {{ metadata.type|py_metric_type }} {{ extra }}: {{ metadata.description|clean_string }}
+ {% endfor %}
+ {% endif %}
+ """
+ event = {
+ "category": "{{ event.category }}",
+ "name": "{{ event.name }}",
+ {% if event.extra_keys %}
+ "extra": {
+ {% for extra, metadata in event.extra_keys.items() %}
+ "{{ extra }}": str({{ extra }}){% if 'bool' == metadata.type|py_metric_type %}.lower(){% endif %},
+ {% endfor %}
+ },
+ {% endif %}
+ }
+ self._record(
+ user_agent,
+ ip_address,
+ {% for metric_type, metrics in metrics_by_type.items() %}
+ {% if metric_type != 'event' %}
+ {% for metric in metrics %}
+ {{ metric.category }}_{{ metric.name }},
+ {% endfor %}
+ {% endif %}
+ {% endfor %}
+ event
+ )
+ {% endfor %}
+{% endfor %}
+
+{% for ping in pings %}
+def {{ ping|factory_method }}(
+ application_id: str,
+ app_display_version: str,
+ channel: str,
+) -> {{ ping|camelize }}ServerEventLogger:
+ """
+ Factory function that creates an instance of Glean Server Event Logger to record
+ `{{ ping }}` ping events.
+ :param str application_id: The application ID.
+ :param str app_display_version: The application display version.
+ :param str channel: The channel.
+ :return: An instance of {{ ping|camelize }}ServerEventLogger.
+ :rtype: {{ ping|camelize }}ServerEventLogger
+ """
+ return {{ ping|camelize }}ServerEventLogger(application_id, app_display_version, channel)
+{% endfor %}
diff --git a/third_party/python/glean_parser/glean_parser/templates/rust.jinja2 b/third_party/python/glean_parser/glean_parser/templates/rust.jinja2
index 51e458cddf..4c54dd2b2c 100644
--- a/third_party/python/glean_parser/glean_parser/templates/rust.jinja2
+++ b/third_party/python/glean_parser/glean_parser/templates/rust.jinja2
@@ -8,6 +8,49 @@ Jinja2 template is not. Please file bugs! #}
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+{%- macro generate_structure(name, struct) %}
+{% if struct.type == "array" %}
+ pub type {{ name }} = Vec<{{ name }}Item>;
+
+ {{ generate_structure(name ~ "Item", struct["items"]) }}
+
+{% elif struct.type == "object" %}
+ #[derive(Debug, Hash, Eq, PartialEq, ::glean::traits::__serde::Serialize, ::glean::traits::__serde::Deserialize)]
+ #[serde(crate = "::glean::traits::__serde")]
+ #[serde(deny_unknown_fields)]
+ pub struct {{ name }} {
+ {% for itemname, val in struct.properties.items() %}
+ {% if val.type == "object" %}
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub {{itemname|snake_case}}: Option<{{ name ~ "Item" ~ itemname|Camelize ~ "Object" }}>,
+ {% elif val.type == "array" %}
+ #[serde(skip_serializing_if = "Vec::is_empty")]
+ pub {{itemname|snake_case}}: {{ name ~ "Item" ~ itemname|Camelize }},
+ {% else %}
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub {{itemname|snake_case}}: Option<{{val.type|structure_type_name}}>,
+ {% endif %}
+ {% endfor %}
+ }
+
+ {% for itemname, val in struct.properties.items() %}
+ {% if val.type == "array" %}
+ {% set nested_name = name ~ "Item" ~ itemname|Camelize %}
+ {{ generate_structure(nested_name, val) }}
+ {% elif val.type == "object" %}
+ {% set nested_name = name ~ "Item" ~ itemname|Camelize ~ "Object" %}
+ {{ generate_structure(nested_name, val) }}
+ {% endif %}
+ {% endfor %}
+
+{% else %}
+
+pub type {{ name }} = {{ struct.type|structure_type_name }};
+
+{% endif %}
+
+{% endmacro %}
+
{% macro generate_extra_keys(obj) %}
{% for name, _ in obj["_generate_enums"] %}
{# we always use the `extra` suffix, because we only expose the new event API #}
@@ -44,7 +87,7 @@ impl ExtraKeys for {{ obj.name|Camelize }}{{ suffix }} {
/// {{ obj.description|wordwrap() | replace('\n', '\n/// ') }}
#[rustfmt::skip]
pub static {{ obj.name|snake_case }}: ::glean::private::__export::Lazy<::glean::private::PingType> =
- ::glean::private::__export::Lazy::new(|| ::glean::private::PingType::new("{{ obj.name }}", {{ obj.include_client_id|rust }}, {{ obj.send_if_empty|rust }}, {{ obj.precise_timestamps|rust }}, {{ obj.reason_codes|rust }}));
+ ::glean::private::__export::Lazy::new(|| ::glean::private::PingType::new("{{ obj.name }}", {{ obj.include_client_id|rust }}, {{ obj.send_if_empty|rust }}, {{ obj.precise_timestamps|rust }}, {{ obj.include_info_sections|rust }}, {{ obj.reason_codes|rust }}));
{% endfor %}
{% else %}
pub mod {{ category.name|snake_case }} {
@@ -52,6 +95,10 @@ pub mod {{ category.name|snake_case }} {
use glean::{private::*, traits::ExtraKeys, traits::NoExtraKeys, CommonMetricData, HistogramType, Lifetime, TimeUnit, MemoryUnit};
{% for obj in category.objs.values() %}
+ {% if obj|attr("_generate_structure") %}
+{{ generate_structure(obj.name|Camelize ~ "Object", obj._generate_structure) }}
+ {%- endif %}
+
{% if obj|attr("_generate_enums") %}
{{ generate_extra_keys(obj) }}
{%- endif %}
diff --git a/third_party/python/glean_parser/glean_parser/templates/swift.jinja2 b/third_party/python/glean_parser/glean_parser/templates/swift.jinja2
index 82ad37bf20..714bf20ec2 100644
--- a/third_party/python/glean_parser/glean_parser/templates/swift.jinja2
+++ b/third_party/python/glean_parser/glean_parser/templates/swift.jinja2
@@ -96,6 +96,7 @@ extension {{ namespace }} {
includeClientId: {{obj.include_client_id|swift}},
sendIfEmpty: {{obj.send_if_empty|swift}},
preciseTimestamps: {{obj.precise_timestamps|swift}},
+ includeInfoSections: {{obj.include_info_sections|swift}},
reasonCodes: {{obj.reason_codes|swift}}
)
diff --git a/third_party/python/glean_parser/glean_parser/translate.py b/third_party/python/glean_parser/glean_parser/translate.py
index 021fce47fb..6293a99491 100644
--- a/third_party/python/glean_parser/glean_parser/translate.py
+++ b/third_party/python/glean_parser/glean_parser/translate.py
@@ -17,8 +17,10 @@ from typing import Any, Callable, Dict, Iterable, List, Optional
from . import lint
from . import parser
+from . import go_server
from . import javascript
from . import javascript_server
+from . import python_server
from . import kotlin
from . import markdown
from . import metrics
@@ -54,10 +56,12 @@ class Outputter:
OUTPUTTERS = {
+ "go_server": Outputter(go_server.output_go, []),
"javascript": Outputter(javascript.output_javascript, []),
"typescript": Outputter(javascript.output_typescript, []),
"javascript_server": Outputter(javascript_server.output_javascript, []),
"typescript_server": Outputter(javascript_server.output_typescript, []),
+ "python_server": Outputter(python_server.output_python, []),
"ruby_server": Outputter(ruby_server.output_ruby, []),
"kotlin": Outputter(kotlin.output_kotlin, ["*.kt"]),
"markdown": Outputter(markdown.output_markdown, []),
diff --git a/third_party/python/glean_parser/glean_parser/util.py b/third_party/python/glean_parser/glean_parser/util.py
index edaeed9578..41cda8833d 100644
--- a/third_party/python/glean_parser/glean_parser/util.py
+++ b/third_party/python/glean_parser/glean_parser/util.py
@@ -525,6 +525,7 @@ ping_args = [
"include_client_id",
"send_if_empty",
"precise_timestamps",
+ "include_info_sections",
"reason_codes",
]
diff --git a/third_party/python/poetry.lock b/third_party/python/poetry.lock
index 108b754b8b..67d13cdfc3 100644
--- a/third_party/python/poetry.lock
+++ b/third_party/python/poetry.lock
@@ -592,14 +592,14 @@ files = [
[[package]]
name = "glean-parser"
-version = "11.0.1"
+version = "13.0.0"
description = "Parser tools for Mozilla's Glean telemetry"
category = "main"
optional = false
python-versions = "*"
files = [
- {file = "glean_parser-11.0.1-py3-none-any.whl", hash = "sha256:f6991ba1438909d1fb6f96d3efaaef2a92098eceff39d5d998d3bbb170276c6a"},
- {file = "glean_parser-11.0.1.tar.gz", hash = "sha256:79e523b51b0fddce9f6c6309fa8c64328bb706b2d6c847a53f88a93d01695e43"},
+ {file = "glean_parser-13.0.0-py3-none-any.whl", hash = "sha256:1c1e9d33fae3b804fc066ae6b2ae7ae8f4148cac1e5b248f2c1e2bfc2e3ae520"},
+ {file = "glean_parser-13.0.0.tar.gz", hash = "sha256:833780cab7e057034b352786203af94f21afcb0094cbed6010471f5dc21a5f91"},
]
[package.dependencies]
@@ -771,6 +771,25 @@ files = [
]
[[package]]
+name = "mako"
+version = "1.1.2"
+description = "A super-fast templating language that borrows the best ideas from the existing templating languages."
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+files = [
+ {file = "Mako-1.1.2-py2.py3-none-any.whl", hash = "sha256:8e8b53c71c7e59f3de716b6832c4e401d903af574f6962edbbbf6ecc2a5fe6c9"},
+ {file = "Mako-1.1.2.tar.gz", hash = "sha256:3139c5d64aa5d175dbafb95027057128b5fbd05a40c53999f3905ceb53366d9d"},
+]
+
+[package.dependencies]
+MarkupSafe = ">=0.9.2"
+
+[package.extras]
+babel = ["Babel"]
+lingua = ["lingua"]
+
+[[package]]
name = "markupsafe"
version = "2.0.1"
description = "Safely add untrusted strings to HTML/XML markup."
@@ -1142,7 +1161,6 @@ files = [
{file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"},
{file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"},
{file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"},
- {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"},
{file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"},
{file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"},
{file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"},
@@ -1606,4 +1624,4 @@ testing = ["func-timeout", "jaraco.itertools", "pytest (>=4.6)", "pytest-black (
[metadata]
lock-version = "2.0"
python-versions = "^3.8"
-content-hash = "22aaa5192c95ba8c8effd4a6e39b32d356a790e72e7bae067615bbfe0c4e9896"
+content-hash = "2bb8ac6bdb09e709fe469807ee6ed832f281cbc78dee9edf5d932bee0fde5d4f"
diff --git a/third_party/python/requirements.in b/third_party/python/requirements.in
index fc76e925ba..90a4049dcf 100644
--- a/third_party/python/requirements.in
+++ b/third_party/python/requirements.in
@@ -22,7 +22,7 @@ fluent.migrate==0.13.0
fluent.syntax==0.19.0
# Pin `frozenlist` as it is required for `aiohttp`. Use minimum required version.
frozenlist==1.1.1
-glean_parser==11.0.1
+glean_parser==13.0.0
importlib-metadata==6.0.0
# required for compatibility with Flask >= 2 in tools/tryselect/selectors/chooser
jinja2==3.1.2
@@ -30,6 +30,7 @@ jsmin==3.0.0
json-e==4.5.3
jsonschema==4.17.3
looseversion==1.0.1
+mako==1.1.2
mozilla-repo-urls==0.1.1
mozilla-version==2.0.0
packaging==23.1
diff --git a/third_party/python/requirements.txt b/third_party/python/requirements.txt
index 792d2898f2..e7df6a5d5a 100644
--- a/third_party/python/requirements.txt
+++ b/third_party/python/requirements.txt
@@ -275,9 +275,9 @@ frozenlist==1.1.1 ; python_version >= "3.8" and python_version < "4.0" \
giturlparse==0.10.0 ; python_version >= "3.8" and python_version < "4.0" \
--hash=sha256:04ba1a3a099c3093fa8d24a422913c6a9b2c2cd22bcffc939cf72e3e98f672d7 \
--hash=sha256:2595ab291d30717cda8474b874c9fd509f1b9802ad7f6968c36a45e4b13eb337
-glean-parser==11.0.1 ; python_version >= "3.8" and python_version < "4.0" \
- --hash=sha256:79e523b51b0fddce9f6c6309fa8c64328bb706b2d6c847a53f88a93d01695e43 \
- --hash=sha256:f6991ba1438909d1fb6f96d3efaaef2a92098eceff39d5d998d3bbb170276c6a
+glean-parser==13.0.0 ; python_version >= "3.8" and python_version < "4.0" \
+ --hash=sha256:1c1e9d33fae3b804fc066ae6b2ae7ae8f4148cac1e5b248f2c1e2bfc2e3ae520 \
+ --hash=sha256:833780cab7e057034b352786203af94f21afcb0094cbed6010471f5dc21a5f91
idna==2.10 ; python_version >= "3.8" and python_version < "4.0" \
--hash=sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6 \
--hash=sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0
@@ -307,6 +307,9 @@ jsonschema==4.17.3 ; python_version >= "3.8" and python_version < "4.0" \
looseversion==1.0.1 ; python_version >= "3.8" and python_version < "4.0" \
--hash=sha256:a205beabd0ffd40488edb9ccb3a39134510fc7c0c2847a25079f559e59c004ac \
--hash=sha256:b339dfde67680e9c5c2e96673e52bee9f94d2f0e1b8f4cbfd86d32311e86b952
+mako==1.1.2 ; python_version >= "3.8" and python_version < "4.0" \
+ --hash=sha256:3139c5d64aa5d175dbafb95027057128b5fbd05a40c53999f3905ceb53366d9d \
+ --hash=sha256:8e8b53c71c7e59f3de716b6832c4e401d903af574f6962edbbbf6ecc2a5fe6c9
markupsafe==2.0.1 ; python_version >= "3.8" and python_version < "4.0" \
--hash=sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298 \
--hash=sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64 \
@@ -489,7 +492,6 @@ pyyaml==6.0.1 ; python_version >= "3.8" and python_version < "4.0" \
--hash=sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4 \
--hash=sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba \
--hash=sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8 \
- --hash=sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef \
--hash=sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5 \
--hash=sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd \
--hash=sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3 \
diff --git a/third_party/rlbox_wasm2c_sandbox/include/rlbox_wasm2c_sandbox.hpp b/third_party/rlbox_wasm2c_sandbox/include/rlbox_wasm2c_sandbox.hpp
index cc6195d84e..d5f0403b6a 100644
--- a/third_party/rlbox_wasm2c_sandbox/include/rlbox_wasm2c_sandbox.hpp
+++ b/third_party/rlbox_wasm2c_sandbox/include/rlbox_wasm2c_sandbox.hpp
@@ -304,12 +304,13 @@ public:
private:
mutable typename RLBOX_WASM_MODULE_TYPE_CURR::instance_t wasm2c_instance{ 0 };
- struct w2c_env sandbox_memory_env;
- struct w2c_wasi__snapshot__preview1 wasi_env;
+ struct w2c_env sandbox_memory_env{ 0 };
+ struct w2c_wasi__snapshot__preview1 wasi_env{ 0 };
bool instance_initialized = false;
- wasm_rt_memory_t sandbox_memory_info;
- mutable wasm_rt_funcref_table_t sandbox_callback_table;
- uintptr_t heap_base;
+ bool minwasi_init_inst_succeeded = false;
+ wasm_rt_memory_t sandbox_memory_info{ 0 };
+ mutable wasm_rt_funcref_table_t sandbox_callback_table{ 0 };
+ uintptr_t heap_base = 0;
size_t return_slot_size = 0;
T_PointerType return_slot = 0;
mutable std::vector<T_PointerType> callback_free_list;
@@ -505,7 +506,7 @@ public:
FALLIBLE_DYNAMIC_CHECK(
infallible, minwasi_init_succeeded, "Could not initialize min wasi");
- const bool minwasi_init_inst_succeeded = minwasi_init_instance(&wasi_env);
+ minwasi_init_inst_succeeded = minwasi_init_instance(&wasi_env);
FALLIBLE_DYNAMIC_CHECK(
infallible, minwasi_init_inst_succeeded, "Could not initialize min wasi instance");
@@ -567,9 +568,20 @@ public:
RLBOX_WASM_MODULE_TYPE_CURR::free_instance(&wasm2c_instance);
}
- destroy_wasm2c_memory(&sandbox_memory_info);
- wasm_rt_free_funcref_table(&sandbox_callback_table);
- minwasi_cleanup_instance(&wasi_env);
+ if (sandbox_memory_info.data) {
+ destroy_wasm2c_memory(&sandbox_memory_info);
+ sandbox_memory_info.data = nullptr;
+ }
+
+ if (sandbox_callback_table.data) {
+ wasm_rt_free_funcref_table(&sandbox_callback_table);
+ sandbox_callback_table.data = nullptr;
+ }
+
+ if (minwasi_init_inst_succeeded) {
+ minwasi_init_inst_succeeded = false;
+ minwasi_cleanup_instance(&wasi_env);
+ }
}
template<typename T>
diff --git a/third_party/rust/aa-stroke/.cargo-checksum.json b/third_party/rust/aa-stroke/.cargo-checksum.json
index 40f73c9547..7fd07daa0a 100644
--- a/third_party/rust/aa-stroke/.cargo-checksum.json
+++ b/third_party/rust/aa-stroke/.cargo-checksum.json
@@ -1 +1 @@
-{"files":{".github/workflows/rust.yml":"6a9f1b122ea02367a2f1ff1fc7b9a728284ceb47fad12e1610cde9d760f4efc3","Cargo.toml":"f507cac11c3c26af28420d68ec3748a5453322d51ef1379a340fdd3b1c9b187a","README.md":"60b34cfa653114d5054009696df2ed2ea1d4926a6bc312d0cac4b84845c2beff","examples/simple.rs":"c196e79568fe4be31a08374aa451c70c9377db5428aef924a985e069c12ed91e","src/bezierflattener.rs":"61687da22490cb1bd901d0b5eb1de3a98802b46c03719ded4163c7a4997f0ad9","src/c_bindings.rs":"06225ddd132ae959eda1b445f4e375cead4d8e135c5cba81e828815fe6a5e88b","src/lib.rs":"fc7990e62434f3143b5162aba85ea828ceab51447c5fad5e26e8c6b06ec77050","src/tri_rasterize.rs":"fb6f595ab9340d8ea6429b41638c378bbd772c8e4d8f7793e225624c12cd3a21"},"package":null} \ No newline at end of file
+{"files":{".github/workflows/rust.yml":"6a9f1b122ea02367a2f1ff1fc7b9a728284ceb47fad12e1610cde9d760f4efc3","Cargo.toml":"f507cac11c3c26af28420d68ec3748a5453322d51ef1379a340fdd3b1c9b187a","README.md":"60b34cfa653114d5054009696df2ed2ea1d4926a6bc312d0cac4b84845c2beff","examples/simple.rs":"c196e79568fe4be31a08374aa451c70c9377db5428aef924a985e069c12ed91e","src/bezierflattener.rs":"c7183a850d51525db4389d5c0badb76e1d8c4110697bfa51ef746fda6a858bb9","src/c_bindings.rs":"06225ddd132ae959eda1b445f4e375cead4d8e135c5cba81e828815fe6a5e88b","src/lib.rs":"3009746efe5f6753cd999258077a4baea30a740190e7a8ccaec0d78f4719fdfb","src/tri_rasterize.rs":"fb6f595ab9340d8ea6429b41638c378bbd772c8e4d8f7793e225624c12cd3a21"},"package":null} \ No newline at end of file
diff --git a/third_party/rust/aa-stroke/src/bezierflattener.rs b/third_party/rust/aa-stroke/src/bezierflattener.rs
index 1a615941b6..ab2f96e4a8 100644
--- a/third_party/rust/aa-stroke/src/bezierflattener.rs
+++ b/third_party/rust/aa-stroke/src/bezierflattener.rs
@@ -16,8 +16,8 @@ pub type HRESULT = i32;
pub const S_OK: i32 = 0;
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct GpPointR {
- pub x: f64,
- pub y: f64
+ pub x: f32,
+ pub y: f32
}
impl Sub for GpPointR {
@@ -48,32 +48,32 @@ impl SubAssign for GpPointR {
}
}
-impl MulAssign<f64> for GpPointR {
- fn mul_assign(&mut self, rhs: f64) {
+impl MulAssign<f32> for GpPointR {
+ fn mul_assign(&mut self, rhs: f32) {
*self = *self * rhs;
}
}
-impl Mul<f64> for GpPointR {
+impl Mul<f32> for GpPointR {
type Output = Self;
- fn mul(self, rhs: f64) -> Self::Output {
+ fn mul(self, rhs: f32) -> Self::Output {
GpPointR { x: self.x * rhs, y: self.y * rhs }
}
}
-impl Div<f64> for GpPointR {
+impl Div<f32> for GpPointR {
type Output = Self;
- fn div(self, rhs: f64) -> Self::Output {
+ fn div(self, rhs: f32) -> Self::Output {
GpPointR { x: self.x / rhs, y: self.y / rhs }
}
}
impl Mul for GpPointR {
- type Output = f64;
+ type Output = f32;
fn mul(self, rhs: Self) -> Self::Output {
self.x * rhs.x + self.y * rhs.y
@@ -81,17 +81,17 @@ impl Mul for GpPointR {
}
impl GpPointR {
- pub fn ApproxNorm(&self) -> f64 {
+ pub fn ApproxNorm(&self) -> f32 {
self.x.abs().max(self.y.abs())
}
- pub fn Norm(&self) -> f64 {
+ pub fn Norm(&self) -> f32 {
self.x.hypot(self.y)
}
}
// Relative to this is relative to the tolerance squared. In other words, a vector
// whose length is less than .01*tolerance will be considered 0
-const SQ_LENGTH_FUZZ: f64 = 1.0e-4;
+const SQ_LENGTH_FUZZ: f32 = 1.0e-4;
// Some of these constants need further thinking
@@ -103,7 +103,7 @@ const SQ_LENGTH_FUZZ: f64 = 1.0e-4;
const FUZZ_DOUBLE: f64 = 1.0e-12; // Double-precision relative 0
const MIN_TOLERANCE: f64 = 1.0e-6;
const DEFAULT_FLATTENING_TOLERANCE: f64 = 0.25;*/
-const TWICE_MIN_BEZIER_STEP_SIZE: f64 = 1.0e-3; // The step size in the Bezier flattener should
+const TWICE_MIN_BEZIER_STEP_SIZE: f32 = 1.0e-3; // The step size in the Bezier flattener should
// never go below half this amount.
//+-----------------------------------------------------------------------------
//
@@ -318,7 +318,7 @@ pub trait CFlatteningSink {
fn AcceptPoint(&mut self,
pt: &GpPointR,
// The point
- t: f64,
+ t: f32,
// Parameter we're at
fAborted: &mut bool,
lastPoint: bool
@@ -339,16 +339,16 @@ pub struct CBezierFlattener<'a>
bezier: CBezier,
// Flattening defining data
m_pSink: &'a mut dyn CFlatteningSink, // The recipient of the flattening data
- m_rTolerance: f64, // Prescribed tolerance
+ m_rTolerance: f32, // Prescribed tolerance
m_fWithTangents: bool, // Generate tangent vectors if true
- m_rQuarterTolerance: f64,// Prescribed tolerance/4 (for doubling the step)
- m_rFuzz: f64, // Computational zero
+ m_rQuarterTolerance: f32,// Prescribed tolerance/4 (for doubling the step)
+ m_rFuzz: f32, // Computational zero
// Flattening working data
m_ptE: [GpPointR; 4], // The moving basis of the curve definition
m_cSteps: i32, // The number of steps left to the end of the curve
- m_rParameter: f64, // Parameter value
- m_rStepSize: f64, // Steps size in parameter domain
+ m_rParameter: f32, // Parameter value
+ m_rStepSize: f32, // Steps size in parameter domain
}
impl<'a> CBezierFlattener<'a> {
/*fn new(
@@ -449,7 +449,7 @@ impl<'a> CBezierFlattener<'a> {
pub fn new(bezier: &CBezier,
pSink: &'a mut dyn CFlatteningSink,
// The reciptient of the flattened data
- rTolerance: f64) // Flattening tolerance
+ rTolerance: f32) // Flattening tolerance
-> Self
{
let mut result = CBezierFlattener {
diff --git a/third_party/rust/aa-stroke/src/lib.rs b/third_party/rust/aa-stroke/src/lib.rs
index 38c47312ec..92fcf3bf47 100644
--- a/third_party/rust/aa-stroke/src/lib.rs
+++ b/third_party/rust/aa-stroke/src/lib.rs
@@ -230,8 +230,8 @@ fn arc_segment_tri(path: &mut PathBuilder, xc: f32, yc: f32, radius: f32, a: Vec
let h = (4. / 3.) * dot(perp(a), mid2) / dot(a, mid2);
- let last_point = GpPointR { x: (xc + r_cos_a) as f64, y: (yc + r_sin_a) as f64 };
- let initial_normal = GpPointR { x: a.x as f64, y: a.y as f64 };
+ let last_point = GpPointR { x: (xc + r_cos_a), y: (yc + r_sin_a) };
+ let initial_normal = GpPointR { x: a.x, y: a.y };
struct Target<'a, 'b> { last_point: GpPointR, last_normal: GpPointR, xc: f32, yc: f32, path: &'a mut PathBuilder<'b> }
@@ -253,24 +253,24 @@ fn arc_segment_tri(path: &mut PathBuilder, xc: f32, yc: f32, radius: f32, a: Vec
let width = 0.5;
self.path.ramp(
- (pt.x - normal.x * width) as f32,
- (pt.y - normal.y * width) as f32,
- (pt.x + normal.x * width) as f32,
- (pt.y + normal.y * width) as f32,
- (self.last_point.x + self.last_normal.x * width) as f32,
- (self.last_point.y + self.last_normal.y * width) as f32,
- (self.last_point.x - self.last_normal.x * width) as f32,
- (self.last_point.y - self.last_normal.y * width) as f32, );
+ pt.x - normal.x * width,
+ pt.y - normal.y * width,
+ pt.x + normal.x * width,
+ pt.y + normal.y * width,
+ self.last_point.x + self.last_normal.x * width,
+ self.last_point.y + self.last_normal.y * width,
+ self.last_point.x - self.last_normal.x * width,
+ self.last_point.y - self.last_normal.y * width, );
self.path.push_tri(
- (self.last_point.x - self.last_normal.x * 0.5) as f32,
- (self.last_point.y - self.last_normal.y * 0.5) as f32,
- (pt.x - normal.x * 0.5) as f32,
- (pt.y - normal.y * 0.5) as f32,
+ self.last_point.x - self.last_normal.x * 0.5,
+ self.last_point.y - self.last_normal.y * 0.5,
+ pt.x - normal.x * 0.5,
+ pt.y - normal.y * 0.5,
self.xc, self.yc);
self.last_normal = normal;
} else {
- self.path.push_tri(self.last_point.x as f32, self.last_point.y as f32, pt.x as f32, pt.y as f32, self.xc, self.yc);
+ self.path.push_tri(self.last_point.x, self.last_point.y, pt.x, pt.y, self.xc, self.yc);
}
self.last_point = pt.clone();
return S_OK;
@@ -279,19 +279,19 @@ fn arc_segment_tri(path: &mut PathBuilder, xc: f32, yc: f32, radius: f32, a: Vec
fn AcceptPoint(&mut self,
pt: &GpPointR,
// The point
- _t: f64,
+ _t: f32,
// Parameter we're at
_aborted: &mut bool,
_last_point: bool) -> HRESULT {
- self.path.push_tri(self.last_point.x as f32, self.last_point.y as f32, pt.x as f32, pt.y as f32, self.xc, self.yc);
+ self.path.push_tri(self.last_point.x, self.last_point.y, pt.x, pt.y, self.xc, self.yc);
self.last_point = pt.clone();
return S_OK;
}
}
- let bezier = CBezier::new([GpPointR { x: (xc + r_cos_a) as f64, y: (yc + r_sin_a) as f64, },
- GpPointR { x: (xc + r_cos_a - h * r_sin_a) as f64, y: (yc + r_sin_a + h * r_cos_a) as f64, },
- GpPointR { x: (xc + r_cos_b + h * r_sin_b) as f64, y: (yc + r_sin_b - h * r_cos_b) as f64, },
- GpPointR { x: (xc + r_cos_b) as f64, y: (yc + r_sin_b) as f64, }]);
+ let bezier = CBezier::new([GpPointR { x: (xc + r_cos_a), y: (yc + r_sin_a), },
+ GpPointR { x: (xc + r_cos_a - h * r_sin_a), y: (yc + r_sin_a + h * r_cos_a), },
+ GpPointR { x: (xc + r_cos_b + h * r_sin_b), y: (yc + r_sin_b - h * r_cos_b), },
+ GpPointR { x: (xc + r_cos_b), y: (yc + r_sin_b), }]);
if bezier.is_degenerate() {
return;
}
@@ -810,23 +810,23 @@ impl<'z> Stroker<'z> {
fn AcceptPoint(&mut self,
pt: &GpPointR,
// The point
- _t: f64,
+ _t: f32,
// Parameter we're at
_aborted: &mut bool,
last_point: bool) -> HRESULT {
if last_point && self.end {
- self.stroker.line_to_capped(Point::new(pt.x as f32, pt.y as f32));
+ self.stroker.line_to_capped(Point::new(pt.x, pt.y));
} else {
- self.stroker.line_to(Point::new(pt.x as f32, pt.y as f32));
+ self.stroker.line_to(Point::new(pt.x, pt.y));
}
return S_OK;
}
}
let cur_pt = self.cur_pt.unwrap_or(cx1);
- let bezier = CBezier::new([GpPointR { x: cur_pt.x as f64, y: cur_pt.y as f64, },
- GpPointR { x: cx1.x as f64, y: cx1.y as f64, },
- GpPointR { x: cx2.x as f64, y: cx2.y as f64, },
- GpPointR { x: pt.x as f64, y: pt.y as f64, }]);
+ let bezier = CBezier::new([GpPointR { x: cur_pt.x, y: cur_pt.y, },
+ GpPointR { x: cx1.x, y: cx1.y, },
+ GpPointR { x: cx2.x, y: cx2.y, },
+ GpPointR { x: pt.x, y: pt.y, }]);
let mut t = Target{ stroker: self, end };
let mut f = CBezierFlattener::new(&bezier, &mut t, 0.25);
f.Flatten(false);
diff --git a/third_party/rust/ahash/.cargo-checksum.json b/third_party/rust/ahash/.cargo-checksum.json
index 8c7a101059..d2fa45cb86 100644
--- a/third_party/rust/ahash/.cargo-checksum.json
+++ b/third_party/rust/ahash/.cargo-checksum.json
@@ -1 +1 @@
-{"files":{"Cargo.toml":"31399b9babab52fabfe71f52ffe38fef0d3d894fdd5829f84a4e69b031f463c9","FAQ.md":"9eb41898523ee209a0a937f9bcb78afe45ad55ca0556f8a4d4063558098f6d1e","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"ff8f68cb076caf8cefe7a6430d4ac086ce6af2ca8ce2c4e5a2004d4552ef52a2","README.md":"72185284f100e149998fe5301f70489e617cc4415b51cc77e967c63c6e970a67","build.rs":"123b322034273b5aa1b4934d84c277fef279afd533ecfb44831a3715e9887fcd","rustfmt.toml":"e090969e99df9360705680cc0097cfaddae10c22dc2e01470592cf3b9787fd36","src/aes_hash.rs":"04483498a0d86e3ab099e22f734ea6fcac384b92f349bd310456856bf6a9a5e2","src/convert.rs":"f0e78840046493d0679a9ec077c8164cf57cf30d5e852b11bfadfdd996d29bd1","src/fallback_hash.rs":"ec00691bd555c69f7446afe893b6631cb84207cb7b512260dec8ef488e1905f3","src/hash_map.rs":"ed0c79c41c2218ad9591a585670a2b9b983807c9725880b780138a44c126cbfd","src/hash_quality_test.rs":"6c1fea90a38dc7b2ac5b2fa6e44a565e0c3385c72df6e87231401f920912d053","src/hash_set.rs":"dc3d33e290aad62457ab1f5e64d3e33eb79e28c9468bfc8686339f0bbd8b19aa","src/lib.rs":"9fec7d1d412e414231c9b929081b1daa7c3b788a9f91eedd79a55efdf5d0d291","src/operations.rs":"10772e65b8b7106f195428c5eb8dbf6cbd49dd5a2165ac750e54af5995210f88","src/random_state.rs":"b7981967ec5ecbff04d166b5e05c59c386062b4de3b36816f3c98ef284372f63","src/specialize.rs":"38d3b56ef4f264d564f48dbcb8ac137928babf90635090c9771c1a62140d1f30","tests/bench.rs":"0851dffebaffd7a437f6f9946ed5e03a957e9a6eb0da7911451af58778c411ec","tests/map_tests.rs":"e0f155f964dd965740b072ee1da110a8c6ef34491c95219f7c89064112c7840f","tests/nopanic.rs":"3363675c4c1a197b86604a0aebbe958fb5ec7c01a414fbfd70e9eb8a29707400"},"package":"fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"} \ No newline at end of file
+{"files":{"Cargo.toml":"a519809f46c9aad28d6c48748f330fe31e05029d234b6d1743522ec403441744","FAQ.md":"9eb41898523ee209a0a937f9bcb78afe45ad55ca0556f8a4d4063558098f6d1e","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"ff8f68cb076caf8cefe7a6430d4ac086ce6af2ca8ce2c4e5a2004d4552ef52a2","README.md":"72185284f100e149998fe5301f70489e617cc4415b51cc77e967c63c6e970a67","build.rs":"f80cb1cdb731a63d16513f1f0b0871d9c077d2c6bf2312af40a202f9c0fefe49","rustfmt.toml":"e090969e99df9360705680cc0097cfaddae10c22dc2e01470592cf3b9787fd36","src/aes_hash.rs":"0b11ce066931396222d2bed7eff678fdd7c8351819485efb721f62a30551866b","src/convert.rs":"f0e78840046493d0679a9ec077c8164cf57cf30d5e852b11bfadfdd996d29bd1","src/fallback_hash.rs":"ec00691bd555c69f7446afe893b6631cb84207cb7b512260dec8ef488e1905f3","src/hash_map.rs":"ed0c79c41c2218ad9591a585670a2b9b983807c9725880b780138a44c126cbfd","src/hash_quality_test.rs":"61695e5cac46ea25021a9d04199fb00c513e0c0c9c0f67aca0c647b9d2f7dd5a","src/hash_set.rs":"dc3d33e290aad62457ab1f5e64d3e33eb79e28c9468bfc8686339f0bbd8b19aa","src/lib.rs":"9fec7d1d412e414231c9b929081b1daa7c3b788a9f91eedd79a55efdf5d0d291","src/operations.rs":"10772e65b8b7106f195428c5eb8dbf6cbd49dd5a2165ac750e54af5995210f88","src/random_state.rs":"ce9689147659efa975887debe1481daddca09386ea8e1d5b4ee90ebeda6c8745","src/specialize.rs":"38d3b56ef4f264d564f48dbcb8ac137928babf90635090c9771c1a62140d1f30","tests/bench.rs":"0851dffebaffd7a437f6f9946ed5e03a957e9a6eb0da7911451af58778c411ec","tests/map_tests.rs":"e0f155f964dd965740b072ee1da110a8c6ef34491c95219f7c89064112c7840f","tests/nopanic.rs":"3363675c4c1a197b86604a0aebbe958fb5ec7c01a414fbfd70e9eb8a29707400"},"package":"891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9"} \ No newline at end of file
diff --git a/third_party/rust/ahash/Cargo.toml b/third_party/rust/ahash/Cargo.toml
index b412f79166..5830a500d9 100644
--- a/third_party/rust/ahash/Cargo.toml
+++ b/third_party/rust/ahash/Cargo.toml
@@ -3,43 +3,63 @@
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
-# to registry (e.g., crates.io) dependencies
+# to registry (e.g., crates.io) dependencies.
#
-# If you believe there's an error in this file please file an
-# issue against the rust-lang/cargo repository. If you're
-# editing this file be aware that the upstream Cargo.toml
-# will likely look very different (and much more reasonable)
+# If you are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
[package]
edition = "2018"
name = "ahash"
-version = "0.7.6"
+version = "0.7.8"
authors = ["Tom Kaitchuck <Tom.Kaitchuck@gmail.com>"]
build = "./build.rs"
-exclude = ["/smhasher", "/benchmark_tools"]
+exclude = [
+ "/smhasher",
+ "/benchmark_tools",
+]
description = "A non-cryptographic hash function using AES-NI for high performance"
documentation = "https://docs.rs/ahash"
readme = "README.md"
-keywords = ["hash", "hasher", "hashmap", "aes", "no-std"]
-categories = ["algorithms", "data-structures", "no-std"]
+keywords = [
+ "hash",
+ "hasher",
+ "hashmap",
+ "aes",
+ "no-std",
+]
+categories = [
+ "algorithms",
+ "data-structures",
+ "no-std",
+]
license = "MIT OR Apache-2.0"
repository = "https://github.com/tkaitchuck/ahash"
+
[package.metadata.docs.rs]
features = ["std"]
-rustc-args = ["-C", "target-feature=+aes"]
-rustdoc-args = ["-C", "target-feature=+aes"]
+rustc-args = [
+ "-C",
+ "target-feature=+aes",
+]
+rustdoc-args = [
+ "-C",
+ "target-feature=+aes",
+]
+
[profile.bench]
opt-level = 3
lto = "fat"
codegen-units = 1
-debug = false
+debug = 0
debug-assertions = false
[profile.release]
opt-level = 3
lto = "fat"
codegen-units = 1
-debug = false
+debug = 0
debug-assertions = false
[profile.test]
@@ -63,6 +83,7 @@ harness = false
name = "map"
path = "tests/map_tests.rs"
harness = false
+
[dev-dependencies.criterion]
version = "0.3.2"
@@ -86,13 +107,19 @@ version = "4.0"
[dev-dependencies.serde_json]
version = "1.0.59"
+
[build-dependencies.version_check]
version = "0.9"
[features]
+atomic-polyfill = [
+ "dep:atomic-polyfill",
+ "once_cell/atomic-polyfill",
+]
compile-time-rng = ["const-random"]
default = ["std"]
std = []
+
[target."cfg(any(target_os = \"linux\", target_os = \"android\", target_os = \"windows\", target_os = \"macos\", target_os = \"ios\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\", target_os = \"dragonfly\", target_os = \"solaris\", target_os = \"illumos\", target_os = \"fuchsia\", target_os = \"redox\", target_os = \"cloudabi\", target_os = \"haiku\", target_os = \"vxworks\", target_os = \"emscripten\", target_os = \"wasi\"))".dependencies.const-random]
version = "0.1.12"
optional = true
@@ -103,10 +130,16 @@ version = "0.2.3"
[target."cfg(any(target_os = \"linux\", target_os = \"android\", target_os = \"windows\", target_os = \"macos\", target_os = \"ios\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\", target_os = \"dragonfly\", target_os = \"solaris\", target_os = \"illumos\", target_os = \"fuchsia\", target_os = \"redox\", target_os = \"cloudabi\", target_os = \"haiku\", target_os = \"vxworks\", target_os = \"emscripten\", target_os = \"wasi\"))".dependencies.serde]
version = "1.0.117"
optional = true
+
[target."cfg(not(all(target_arch = \"arm\", target_os = \"none\")))".dependencies.once_cell]
-version = "1.8"
+version = "1.13.1"
features = ["alloc"]
default-features = false
+
+[target."cfg(not(any(target_os = \"linux\", target_os = \"android\", target_os = \"windows\", target_os = \"macos\", target_os = \"ios\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\", target_os = \"dragonfly\", target_os = \"solaris\", target_os = \"illumos\", target_os = \"fuchsia\", target_os = \"redox\", target_os = \"cloudabi\", target_os = \"haiku\", target_os = \"vxworks\", target_os = \"emscripten\", target_os = \"wasi\")))".dependencies.atomic-polyfill]
+version = "1.0.1"
+optional = true
+
[target."cfg(not(any(target_os = \"linux\", target_os = \"android\", target_os = \"windows\", target_os = \"macos\", target_os = \"ios\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\", target_os = \"dragonfly\", target_os = \"solaris\", target_os = \"illumos\", target_os = \"fuchsia\", target_os = \"redox\", target_os = \"cloudabi\", target_os = \"haiku\", target_os = \"vxworks\", target_os = \"emscripten\", target_os = \"wasi\")))".dependencies.const-random]
version = "0.1.12"
optional = true
diff --git a/third_party/rust/ahash/build.rs b/third_party/rust/ahash/build.rs
index 8be4964e52..6aba02526b 100644
--- a/third_party/rust/ahash/build.rs
+++ b/third_party/rust/ahash/build.rs
@@ -7,7 +7,9 @@ fn main() {
if let Some(channel) = version_check::Channel::read() {
if channel.supports_features() {
println!("cargo:rustc-cfg=feature=\"specialize\"");
- println!("cargo:rustc-cfg=feature=\"stdsimd\"");
+ if version_check::Version::read().map_or(false, |v| v.at_most("1.77.9")) {
+ println!("cargo:rustc-cfg=feature=\"stdsimd\"");
+ }
}
}
let os = env::var("CARGO_CFG_TARGET_OS").expect("CARGO_CFG_TARGET_OS was not set");
diff --git a/third_party/rust/ahash/src/aes_hash.rs b/third_party/rust/ahash/src/aes_hash.rs
index 1c98582cee..7e619b520b 100644
--- a/third_party/rust/ahash/src/aes_hash.rs
+++ b/third_party/rust/ahash/src/aes_hash.rs
@@ -170,10 +170,10 @@ impl Hasher for AHasher {
let tail = data.read_last_u128x4();
let mut current: [u128; 4] = [self.key; 4];
current[0] = aesenc(current[0], tail[0]);
- current[1] = aesenc(current[1], tail[1]);
+ current[1] = aesdec(current[1], tail[1]);
current[2] = aesenc(current[2], tail[2]);
- current[3] = aesenc(current[3], tail[3]);
- let mut sum: [u128; 2] = [self.key, self.key];
+ current[3] = aesdec(current[3], tail[3]);
+ let mut sum: [u128; 2] = [self.key, !self.key];
sum[0] = add_by_64s(sum[0].convert(), tail[0].convert()).convert();
sum[1] = add_by_64s(sum[1].convert(), tail[1].convert()).convert();
sum[0] = shuffle_and_add(sum[0], tail[2]);
@@ -190,8 +190,9 @@ impl Hasher for AHasher {
sum[1] = shuffle_and_add(sum[1], blocks[3]);
data = rest;
}
- self.hash_in_2(aesenc(current[0], current[1]), aesenc(current[2], current[3]));
- self.hash_in(add_by_64s(sum[0].convert(), sum[1].convert()).convert());
+ self.hash_in_2(current[0], current[1]);
+ self.hash_in_2(current[2], current[3]);
+ self.hash_in_2(sum[0], sum[1]);
} else {
//len 33-64
let (head, _) = data.read_u128x2();
@@ -215,7 +216,7 @@ impl Hasher for AHasher {
fn finish(&self) -> u64 {
let combined = aesdec(self.sum, self.enc);
let result: [u64; 2] = aesenc(aesenc(combined, self.key), combined).convert();
- result[0]
+ result[1]
}
}
diff --git a/third_party/rust/ahash/src/hash_quality_test.rs b/third_party/rust/ahash/src/hash_quality_test.rs
index 4cd3156afe..17228d4716 100644
--- a/third_party/rust/ahash/src/hash_quality_test.rs
+++ b/third_party/rust/ahash/src/hash_quality_test.rs
@@ -1,5 +1,5 @@
use core::hash::{Hash, Hasher};
-use std::collections::HashMap;
+use std::collections::{HashMap};
fn assert_sufficiently_different(a: u64, b: u64, tolerance: i32) {
let (same_byte_count, same_nibble_count) = count_same_bytes_and_nibbles(a, b);
@@ -326,6 +326,28 @@ fn test_length_extension<T: Hasher>(hasher: impl Fn(u128, u128) -> T) {
}
}
+fn test_sparse<T: Hasher>(hasher: impl Fn() -> T) {
+ let mut buf = [0u8; 256];
+ let mut hashes = HashMap::new();
+ for idx_1 in 0..256 {
+ for idx_2 in idx_1+1..256 {
+ for value_1 in [1, 2, 4, 8, 16, 32, 64, 128] {
+ for value_2 in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 15, 16, 17, 18, 20, 24, 31, 32, 33, 48, 64, 96, 127, 128, 129, 192, 254, 255] {
+ buf[idx_1] = value_1;
+ buf[idx_2] = value_2;
+ let hash_value = hash_with(&buf, &mut hasher());
+ let keys = hashes.entry(hash_value).or_insert(Vec::new());
+ keys.push((idx_1, value_1, idx_2, value_2));
+ buf[idx_1] = 0;
+ buf[idx_2] = 0;
+ }
+ }
+ }
+ }
+ hashes.retain(|_key, value| value.len() != 1);
+ assert_eq!(0, hashes.len(), "Collision with: {:?}", hashes);
+}
+
#[cfg(test)]
mod fallback_tests {
use crate::fallback_hash::*;
@@ -392,6 +414,12 @@ mod fallback_tests {
fn fallback_length_extension() {
test_length_extension(|a, b| AHasher::new_with_keys(a, b));
}
+
+ #[test]
+ fn test_no_sparse_collisions() {
+ test_sparse(|| AHasher::new_with_keys(0, 0));
+ test_sparse(|| AHasher::new_with_keys(1, 2));
+ }
}
///Basic sanity tests of the cypto properties of aHash.
@@ -480,4 +508,10 @@ mod aes_tests {
fn aes_length_extension() {
test_length_extension(|a, b| AHasher::test_with_keys(a, b));
}
+
+ #[test]
+ fn aes_no_sparse_collisions() {
+ test_sparse(|| AHasher::test_with_keys(0, 0));
+ test_sparse(|| AHasher::test_with_keys(1, 2));
+ }
}
diff --git a/third_party/rust/ahash/src/random_state.rs b/third_party/rust/ahash/src/random_state.rs
index c3628bf145..9ac2f3ec43 100644
--- a/third_party/rust/ahash/src/random_state.rs
+++ b/third_party/rust/ahash/src/random_state.rs
@@ -29,8 +29,13 @@ extern crate alloc;
#[cfg(feature = "std")]
extern crate std as alloc;
+#[cfg(feature = "atomic-polyfill")]
+use atomic_polyfill as atomic;
+#[cfg(not(feature = "atomic-polyfill"))]
+use core::sync::atomic;
+
use alloc::boxed::Box;
-use core::sync::atomic::{AtomicUsize, Ordering};
+use atomic::{AtomicUsize, Ordering};
#[cfg(not(all(target_arch = "arm", target_os = "none")))]
use once_cell::race::OnceBox;
diff --git a/third_party/rust/cc/.cargo-checksum.json b/third_party/rust/cc/.cargo-checksum.json
index 4dc2fe2390..f7d42ebd81 100644
--- a/third_party/rust/cc/.cargo-checksum.json
+++ b/third_party/rust/cc/.cargo-checksum.json
@@ -1 +1 @@
-{"files":{"Cargo.lock":"23c26d62ba5114f5ac6e7ffa3ea233cea77e5cb7f98d9f056f40fe2c49971f67","Cargo.toml":"fd4b39488866b6717476fadc460ff91c89511628080769516eec452c0def8bc7","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397","README.md":"58af5106352aafa62175a90f8a5f25fa114028bf909220dc0735d79745999ec1","src/bin/gcc-shim.rs":"b77907875029494b6288841c3aed2e4939ed40708c7f597fca5c9e2570490ca6","src/com.rs":"29d0dee08a656ab1a4cc3e5fe24542e0fab5c1373cbc9b05059f7572cf9b8313","src/lib.rs":"e0cc228db97675d6a0d86b219a20e9e48925a1ccbfd9e9fd038ccf6ef129957e","src/registry.rs":"98ae2b71781acc49297e5544fa0cf059f735636f8f1338edef8dbf7232443945","src/setup_config.rs":"72deaf1927c0b713fd5c2b2d5b8f0ea3a303a00fda1579427895cac26a94122d","src/vs_instances.rs":"2d3f8278a803b0e7052f4eeb1979b29f963dd0143f4458e2cb5f33c4e5f0963b","src/winapi.rs":"e128e95b2d39ae7a02f54a7e25d33c488c14759b9f1a50a449e10545856950c3","src/windows_registry.rs":"c0340379c1f540cf96f45bbd4cf8fc28db555826f30ac937b75b87e4377b716b","tests/cc_env.rs":"e02b3b0824ad039b47e4462c5ef6dbe6c824c28e7953af94a0f28f7b5158042e","tests/cflags.rs":"57f06eb5ce1557e5b4a032d0c4673e18fbe6f8d26c1deb153126e368b96b41b3","tests/cxxflags.rs":"c2c6c6d8a0d7146616fa1caed26876ee7bc9fcfffd525eb4743593cade5f3371","tests/support/mod.rs":"a3c8d116973bb16066bf6ec4de5143183f97de7aad085d85f8118a2eaac3e1e0","tests/test.rs":"61fb35ae6dd5cf506ada000bdd82c92e9f8eac9cc053b63e83d3f897436fbf8f"},"package":"a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d"} \ No newline at end of file
+{"files":{"Cargo.toml":"1288f536f4ddf6bcdc664a91a070aad2ebd7c6edc32ce24e8d6bc04c2cd64d49","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397","README.md":"f1ddbede208a5b78333a25dac0a7598e678e9b601a7d99a791069bddaf180dfe","src/command_helpers.rs":"3ef95bdcd79a43406fdab275d8a8f45ba787876399b54df34068955ec0109e69","src/lib.rs":"91efa8f9242266752658edd66ee607ce30635f4c30710508a99eb62e7b3c54da","src/parallel/async_executor.rs":"4ce24435fff6b6555b43fee042c16bd65d4150d0346567f246b9190d85b45983","src/parallel/job_token.rs":"0676c3177b5be9d7ede483bf4bd45c5ca0f5511073e4d1c9f181a0bc83db05dc","src/parallel/mod.rs":"aaffed5ad3dc0d28641533ab0d6f522bf34a059d4b1a239dc4d217cb5d58e232","src/parallel/stderr.rs":"a2d18ba3f2e04deb9047ece9ab7ca5452d9a76b515afbe20a76307e31597f34b","src/tool.rs":"172cfcbecd7c6a363ea841a48a10a75b0a01e83b83c0691107c601598b68dedf","src/windows/com.rs":"be1564756c9f3ef1398eafeed7b54ba610caba28e8f6258d28a997737ebf9535","src/windows/find_tools.rs":"9234fe7ab27b0259c6fa9fb47826e7d1a3d1d2c7c4042ef7153ab90ccb9a3412","src/windows/mod.rs":"42f1ad7fee35a17686b003e6aa520d3d1940d47d2f531d626e9ae0c48ba49005","src/windows/registry.rs":"c521b72c825e8095843e73482ffa810ed066ad8bb9f86e6db0c5c143c171aba1","src/windows/setup_config.rs":"754439cbab492afd44c9755abcbec1a41c9b2c358131cee2df13c0e996dbbec8","src/windows/vs_instances.rs":"76e3cee74b5fd38ddaf533bba11fe401667c50dda5f9d064099840893eaa7587","src/windows/winapi.rs":"250d51c1826d1a2329e9889dd9f058cfce253dbf2a678b076147c6cdb5db046c","src/windows/windows_sys.rs":"f6b90b87f23e446284bde86749b53858c0d37b8a43515ed8d0e90b1ac8cf7771"},"package":"a0ba8f7aaa012f30d5b2861462f6708eccd49c3c39863fe083a308035f63d723"} \ No newline at end of file
diff --git a/third_party/rust/cc/Cargo.lock b/third_party/rust/cc/Cargo.lock
deleted file mode 100644
index 2d065bc6a8..0000000000
--- a/third_party/rust/cc/Cargo.lock
+++ /dev/null
@@ -1,110 +0,0 @@
-# This file is automatically @generated by Cargo.
-# It is not intended for manual editing.
-version = 3
-
-[[package]]
-name = "bitflags"
-version = "1.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
-
-[[package]]
-name = "cc"
-version = "1.0.78"
-dependencies = [
- "jobserver",
- "tempfile",
-]
-
-[[package]]
-name = "cfg-if"
-version = "1.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
-
-[[package]]
-name = "fastrand"
-version = "1.8.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499"
-dependencies = [
- "instant",
-]
-
-[[package]]
-name = "instant"
-version = "0.1.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
-dependencies = [
- "cfg-if",
-]
-
-[[package]]
-name = "jobserver"
-version = "0.1.25"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b"
-dependencies = [
- "libc",
-]
-
-[[package]]
-name = "libc"
-version = "0.2.138"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8"
-
-[[package]]
-name = "redox_syscall"
-version = "0.2.16"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
-dependencies = [
- "bitflags",
-]
-
-[[package]]
-name = "remove_dir_all"
-version = "0.5.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
-dependencies = [
- "winapi",
-]
-
-[[package]]
-name = "tempfile"
-version = "3.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4"
-dependencies = [
- "cfg-if",
- "fastrand",
- "libc",
- "redox_syscall",
- "remove_dir_all",
- "winapi",
-]
-
-[[package]]
-name = "winapi"
-version = "0.3.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
-dependencies = [
- "winapi-i686-pc-windows-gnu",
- "winapi-x86_64-pc-windows-gnu",
-]
-
-[[package]]
-name = "winapi-i686-pc-windows-gnu"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
-
-[[package]]
-name = "winapi-x86_64-pc-windows-gnu"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
diff --git a/third_party/rust/cc/Cargo.toml b/third_party/rust/cc/Cargo.toml
index c4ec0bf79d..5d974e076f 100644
--- a/third_party/rust/cc/Cargo.toml
+++ b/third_party/rust/cc/Cargo.toml
@@ -11,10 +11,15 @@
[package]
edition = "2018"
+rust-version = "1.53"
name = "cc"
-version = "1.0.78"
+version = "1.0.89"
authors = ["Alex Crichton <alex@alexcrichton.com>"]
-exclude = ["/.github"]
+exclude = [
+ "/.github",
+ "tests",
+ "src/bin",
+]
description = """
A build-time dependency for Cargo build scripts to assist in invoking the native
C compiler to compile native C code into a static archive to be linked into Rust
@@ -29,11 +34,20 @@ license = "MIT OR Apache-2.0"
repository = "https://github.com/rust-lang/cc-rs"
[dependencies.jobserver]
-version = "0.1.16"
+version = "0.1.20"
optional = true
+default-features = false
[dev-dependencies.tempfile]
version = "3"
[features]
-parallel = ["jobserver"]
+parallel = [
+ "libc",
+ "jobserver",
+]
+
+[target."cfg(unix)".dependencies.libc]
+version = "0.2.62"
+optional = true
+default-features = false
diff --git a/third_party/rust/cc/README.md b/third_party/rust/cc/README.md
index 863540d2d9..33d4bb40f8 100644
--- a/third_party/rust/cc/README.md
+++ b/third_party/rust/cc/README.md
@@ -1,209 +1,13 @@
# cc-rs
-A library to compile C/C++/assembly into a Rust library/application.
-
-[Documentation](https://docs.rs/cc)
-
-A simple library meant to be used as a build dependency with Cargo packages in
-order to build a set of C/C++ files into a static archive. This crate calls out
-to the most relevant compiler for a platform, for example using `cl` on MSVC.
-
-## Using cc-rs
-
-First, you'll want to both add a build script for your crate (`build.rs`) and
-also add this crate to your `Cargo.toml` via:
-
-```toml
-[build-dependencies]
-cc = "1.0"
-```
-
-Next up, you'll want to write a build script like so:
-
-```rust,no_run
-// build.rs
-
-fn main() {
- cc::Build::new()
- .file("foo.c")
- .file("bar.c")
- .compile("foo");
-}
-```
-
-And that's it! Running `cargo build` should take care of the rest and your Rust
-application will now have the C files `foo.c` and `bar.c` compiled into a file
-named `libfoo.a`. If the C files contain
-
-```c
-void foo_function(void) { ... }
-```
-
-and
-
-```c
-int32_t bar_function(int32_t x) { ... }
-```
-
-you can call them from Rust by declaring them in
-your Rust code like so:
-
-```rust,no_run
-extern {
- fn foo_function();
- fn bar_function(x: i32) -> i32;
-}
-
-pub fn call() {
- unsafe {
- foo_function();
- bar_function(42);
- }
-}
-
-fn main() {
- // ...
-}
-```
-
-See [the Rustonomicon](https://doc.rust-lang.org/nomicon/ffi.html) for more details.
-
-## External configuration via environment variables
-
-To control the programs and flags used for building, the builder can set a
-number of different environment variables.
-
-* `CFLAGS` - a series of space separated flags passed to compilers. Note that
- individual flags cannot currently contain spaces, so doing
- something like: `-L=foo\ bar` is not possible.
-* `CC` - the actual C compiler used. Note that this is used as an exact
- executable name, so (for example) no extra flags can be passed inside
- this variable, and the builder must ensure that there aren't any
- trailing spaces. This compiler must understand the `-c` flag. For
- certain `TARGET`s, it also is assumed to know about other flags (most
- common is `-fPIC`).
-* `AR` - the `ar` (archiver) executable to use to build the static library.
-* `CRATE_CC_NO_DEFAULTS` - the default compiler flags may cause conflicts in some cross compiling scenarios. Setting this variable will disable the generation of default compiler flags.
-* `CXX...` - see [C++ Support](#c-support).
-
-Each of these variables can also be supplied with certain prefixes and suffixes,
-in the following prioritized order:
-
-1. `<var>_<target>` - for example, `CC_x86_64-unknown-linux-gnu`
-2. `<var>_<target_with_underscores>` - for example, `CC_x86_64_unknown_linux_gnu`
-3. `<build-kind>_<var>` - for example, `HOST_CC` or `TARGET_CFLAGS`
-4. `<var>` - a plain `CC`, `AR` as above.
-
-If none of these variables exist, cc-rs uses built-in defaults
-
-In addition to the above optional environment variables, `cc-rs` has some
-functions with hard requirements on some variables supplied by [cargo's
-build-script driver][cargo] that it has the `TARGET`, `OUT_DIR`, `OPT_LEVEL`,
-and `HOST` variables.
-
-[cargo]: https://doc.rust-lang.org/cargo/reference/build-scripts.html#inputs-to-the-build-script
-
-## Optional features
-
-### Parallel
-
-Currently cc-rs supports parallel compilation (think `make -jN`) but this
-feature is turned off by default. To enable cc-rs to compile C/C++ in parallel,
-you can change your dependency to:
-
-```toml
-[build-dependencies]
-cc = { version = "1.0", features = ["parallel"] }
-```
-
-By default cc-rs will limit parallelism to `$NUM_JOBS`, or if not present it
-will limit it to the number of cpus on the machine. If you are using cargo,
-use `-jN` option of `build`, `test` and `run` commands as `$NUM_JOBS`
-is supplied by cargo.
-
-## Compile-time Requirements
-
-To work properly this crate needs access to a C compiler when the build script
-is being run. This crate does not ship a C compiler with it. The compiler
-required varies per platform, but there are three broad categories:
-
-* Unix platforms require `cc` to be the C compiler. This can be found by
- installing cc/clang on Linux distributions and Xcode on macOS, for example.
-* Windows platforms targeting MSVC (e.g. your target triple ends in `-msvc`)
- require `cl.exe` to be available and in `PATH`. This is typically found in
- standard Visual Studio installations and the `PATH` can be set up by running
- the appropriate developer tools shell.
-* Windows platforms targeting MinGW (e.g. your target triple ends in `-gnu`)
- require `cc` to be available in `PATH`. We recommend the
- [MinGW-w64](https://www.mingw-w64.org/) distribution, which is using the
- [Win-builds](http://win-builds.org/) installation system.
- You may also acquire it via
- [MSYS2](https://www.msys2.org/), as explained [here][msys2-help]. Make sure
- to install the appropriate architecture corresponding to your installation of
- rustc. GCC from older [MinGW](http://www.mingw.org/) project is compatible
- only with 32-bit rust compiler.
-
-[msys2-help]: https://github.com/rust-lang/rust#building-on-windows
-
-## C++ support
-
-`cc-rs` supports C++ libraries compilation by using the `cpp` method on
-`Build`:
-
-```rust,no_run
-fn main() {
- cc::Build::new()
- .cpp(true) // Switch to C++ library compilation.
- .file("foo.cpp")
- .compile("libfoo.a");
-}
-```
-
-For C++ libraries, the `CXX` and `CXXFLAGS` environment variables are used instead of `CC` and `CFLAGS`.
-
-The C++ standard library may be linked to the crate target. By default it's `libc++` for macOS, FreeBSD, and OpenBSD, `libc++_shared` for Android, nothing for MSVC, and `libstdc++` for anything else. It can be changed in one of two ways:
-
-1. by using the `cpp_link_stdlib` method on `Build`:
- ```rust,no-run
- fn main() {
- cc::Build::new()
- .cpp(true)
- .file("foo.cpp")
- .cpp_link_stdlib("stdc++") // use libstdc++
- .compile("libfoo.a");
- }
- ```
-2. by setting the `CXXSTDLIB` environment variable.
-
-In particular, for Android you may want to [use `c++_static` if you have at most one shared library](https://developer.android.com/ndk/guides/cpp-support).
-
-Remember that C++ does name mangling so `extern "C"` might be required to enable Rust linker to find your functions.
-
-## CUDA C++ support
-
-`cc-rs` also supports compiling CUDA C++ libraries by using the `cuda` method
-on `Build` (currently for GNU/Clang toolchains only):
-
-```rust,no_run
-fn main() {
- cc::Build::new()
- // Switch to CUDA C++ library compilation using NVCC.
- .cuda(true)
- .cudart("static")
- // Generate code for Maxwell (GTX 970, 980, 980 Ti, Titan X).
- .flag("-gencode").flag("arch=compute_52,code=sm_52")
- // Generate code for Maxwell (Jetson TX1).
- .flag("-gencode").flag("arch=compute_53,code=sm_53")
- // Generate code for Pascal (GTX 1070, 1080, 1080 Ti, Titan Xp).
- .flag("-gencode").flag("arch=compute_61,code=sm_61")
- // Generate code for Pascal (Tesla P100).
- .flag("-gencode").flag("arch=compute_60,code=sm_60")
- // Generate code for Pascal (Jetson TX2).
- .flag("-gencode").flag("arch=compute_62,code=sm_62")
- .file("bar.cu")
- .compile("libbar.a");
-}
-```
+A library for [Cargo build scripts](https://doc.rust-lang.org/cargo/reference/build-scripts.html)
+to compile a set of C/C++/assembly/CUDA files into a static archive for Cargo
+to link into the crate being built. This crate does not compile code itself;
+it calls out to the default compiler for the platform. This crate will
+automatically detect situations such as cross compilation and
+various environment variables and will build code appropriately.
+
+Refer to the [documentation](https://docs.rs/cc) for detailed usage instructions.
## License
diff --git a/third_party/rust/cc/src/bin/gcc-shim.rs b/third_party/rust/cc/src/bin/gcc-shim.rs
deleted file mode 100644
index 1731df82ea..0000000000
--- a/third_party/rust/cc/src/bin/gcc-shim.rs
+++ /dev/null
@@ -1,48 +0,0 @@
-#![cfg_attr(test, allow(dead_code))]
-
-use std::env;
-use std::fs::File;
-use std::io::prelude::*;
-use std::path::PathBuf;
-
-fn main() {
- let mut args = env::args();
- let program = args.next().expect("Unexpected empty args");
-
- let out_dir = PathBuf::from(
- env::var_os("GCCTEST_OUT_DIR").expect(&format!("{}: GCCTEST_OUT_DIR not found", program)),
- );
-
- // Find the first nonexistent candidate file to which the program's args can be written.
- for i in 0.. {
- let candidate = &out_dir.join(format!("out{}", i));
-
- // If the file exists, commands have already run. Try again.
- if candidate.exists() {
- continue;
- }
-
- // Create a file and record the args passed to the command.
- let mut f = File::create(candidate).expect(&format!(
- "{}: can't create candidate: {}",
- program,
- candidate.to_string_lossy()
- ));
- for arg in args {
- writeln!(f, "{}", arg).expect(&format!(
- "{}: can't write to candidate: {}",
- program,
- candidate.to_string_lossy()
- ));
- }
- break;
- }
-
- // Create a file used by some tests.
- let path = &out_dir.join("libfoo.a");
- File::create(path).expect(&format!(
- "{}: can't create libfoo.a: {}",
- program,
- path.to_string_lossy()
- ));
-}
diff --git a/third_party/rust/cc/src/command_helpers.rs b/third_party/rust/cc/src/command_helpers.rs
new file mode 100644
index 0000000000..919d276c84
--- /dev/null
+++ b/third_party/rust/cc/src/command_helpers.rs
@@ -0,0 +1,433 @@
+//! Miscellaneous helpers for running commands
+
+use std::{
+ collections::hash_map,
+ ffi::OsString,
+ fmt::Display,
+ fs,
+ hash::Hasher,
+ io::{self, Read, Write},
+ path::Path,
+ process::{Child, ChildStderr, Command, Stdio},
+ sync::{
+ atomic::{AtomicBool, Ordering},
+ Arc,
+ },
+};
+
+use crate::{Error, ErrorKind, Object};
+
+#[derive(Clone, Debug)]
+pub(crate) struct CargoOutput {
+ pub(crate) metadata: bool,
+ pub(crate) warnings: bool,
+ pub(crate) debug: bool,
+ checked_dbg_var: Arc<AtomicBool>,
+}
+
+impl CargoOutput {
+ pub(crate) fn new() -> Self {
+ Self {
+ metadata: true,
+ warnings: true,
+ debug: std::env::var_os("CC_ENABLE_DEBUG_OUTPUT").is_some(),
+ checked_dbg_var: Arc::new(AtomicBool::new(false)),
+ }
+ }
+
+ pub(crate) fn print_metadata(&self, s: &dyn Display) {
+ if self.metadata {
+ println!("{}", s);
+ }
+ }
+
+ pub(crate) fn print_warning(&self, arg: &dyn Display) {
+ if self.warnings {
+ println!("cargo:warning={}", arg);
+ }
+ }
+
+ pub(crate) fn print_debug(&self, arg: &dyn Display) {
+ if self.metadata && !self.checked_dbg_var.load(Ordering::Relaxed) {
+ self.checked_dbg_var.store(true, Ordering::Relaxed);
+ println!("cargo:rerun-if-env-changed=CC_ENABLE_DEBUG_OUTPUT");
+ }
+ if self.debug {
+ println!("{}", arg);
+ }
+ }
+
+ fn stdio_for_warnings(&self) -> Stdio {
+ if self.warnings {
+ Stdio::piped()
+ } else {
+ Stdio::null()
+ }
+ }
+}
+
+pub(crate) struct StderrForwarder {
+ inner: Option<(ChildStderr, Vec<u8>)>,
+ #[cfg(feature = "parallel")]
+ is_non_blocking: bool,
+ #[cfg(feature = "parallel")]
+ bytes_available_failed: bool,
+}
+
+const MIN_BUFFER_CAPACITY: usize = 100;
+
+impl StderrForwarder {
+ pub(crate) fn new(child: &mut Child) -> Self {
+ Self {
+ inner: child
+ .stderr
+ .take()
+ .map(|stderr| (stderr, Vec::with_capacity(MIN_BUFFER_CAPACITY))),
+ #[cfg(feature = "parallel")]
+ is_non_blocking: false,
+ #[cfg(feature = "parallel")]
+ bytes_available_failed: false,
+ }
+ }
+
+ #[allow(clippy::uninit_vec)]
+ fn forward_available(&mut self) -> bool {
+ if let Some((stderr, buffer)) = self.inner.as_mut() {
+ loop {
+ let old_data_end = buffer.len();
+
+ // For non-blocking we check to see if there is data available, so we should try to
+ // read at least that much. For blocking, always read at least the minimum amount.
+ #[cfg(not(feature = "parallel"))]
+ let to_reserve = MIN_BUFFER_CAPACITY;
+ #[cfg(feature = "parallel")]
+ let to_reserve = if self.is_non_blocking && !self.bytes_available_failed {
+ match crate::parallel::stderr::bytes_available(stderr) {
+ #[cfg(windows)]
+ Ok(0) => return false,
+ #[cfg(unix)]
+ Ok(0) => {
+ // On Unix, depending on the implementation, we may sometimes get 0 in a
+ // loop (either there is data available or the pipe is broken), so
+ // continue with the non-blocking read anyway.
+ MIN_BUFFER_CAPACITY
+ }
+ #[cfg(windows)]
+ Err(_) => {
+ // On Windows, if we get an error then the pipe is broken, so flush
+ // the buffer and bail.
+ if !buffer.is_empty() {
+ write_warning(&buffer[..]);
+ }
+ self.inner = None;
+ return true;
+ }
+ #[cfg(unix)]
+ Err(_) => {
+ // On Unix, depending on the implementation, we may get spurious
+ // errors so make a note not to use bytes_available again and try
+ // the non-blocking read anyway.
+ self.bytes_available_failed = true;
+ MIN_BUFFER_CAPACITY
+ }
+ Ok(bytes_available) => MIN_BUFFER_CAPACITY.max(bytes_available),
+ }
+ } else {
+ MIN_BUFFER_CAPACITY
+ };
+ buffer.reserve(to_reserve);
+
+ // SAFETY: 1) the length is set to the capacity, so we are never using memory beyond
+ // the underlying buffer and 2) we always call `truncate` below to set the len back
+ // to the initialized data.
+ unsafe {
+ buffer.set_len(buffer.capacity());
+ }
+ match stderr.read(&mut buffer[old_data_end..]) {
+ Err(err) if err.kind() == std::io::ErrorKind::WouldBlock => {
+ // No data currently, yield back.
+ buffer.truncate(old_data_end);
+ return false;
+ }
+ Err(err) if err.kind() == std::io::ErrorKind::Interrupted => {
+ // Interrupted, try again.
+ buffer.truncate(old_data_end);
+ }
+ Ok(0) | Err(_) => {
+ // End of stream: flush remaining data and bail.
+ if old_data_end > 0 {
+ write_warning(&buffer[..old_data_end]);
+ }
+ self.inner = None;
+ return true;
+ }
+ Ok(bytes_read) => {
+ buffer.truncate(old_data_end + bytes_read);
+ let mut consumed = 0;
+ for line in buffer.split_inclusive(|&b| b == b'\n') {
+ // Only forward complete lines, leave the rest in the buffer.
+ if let Some((b'\n', line)) = line.split_last() {
+ consumed += line.len() + 1;
+ write_warning(line);
+ }
+ }
+ buffer.drain(..consumed);
+ }
+ }
+ }
+ } else {
+ true
+ }
+ }
+
+ #[cfg(feature = "parallel")]
+ pub(crate) fn set_non_blocking(&mut self) -> Result<(), Error> {
+ assert!(!self.is_non_blocking);
+
+ #[cfg(unix)]
+ if let Some((stderr, _)) = self.inner.as_ref() {
+ crate::parallel::stderr::set_non_blocking(stderr)?;
+ }
+
+ self.is_non_blocking = true;
+ Ok(())
+ }
+
+ #[cfg(feature = "parallel")]
+ fn forward_all(&mut self) {
+ while !self.forward_available() {}
+ }
+
+ #[cfg(not(feature = "parallel"))]
+ fn forward_all(&mut self) {
+ let forward_result = self.forward_available();
+ assert!(forward_result, "Should have consumed all data");
+ }
+}
+
+fn write_warning(line: &[u8]) {
+ let stdout = io::stdout();
+ let mut stdout = stdout.lock();
+ stdout.write_all(b"cargo:warning=").unwrap();
+ stdout.write_all(line).unwrap();
+ stdout.write_all(b"\n").unwrap();
+}
+
+fn wait_on_child(
+ cmd: &Command,
+ program: &str,
+ child: &mut Child,
+ cargo_output: &CargoOutput,
+) -> Result<(), Error> {
+ StderrForwarder::new(child).forward_all();
+
+ let status = match child.wait() {
+ Ok(s) => s,
+ Err(e) => {
+ return Err(Error::new(
+ ErrorKind::ToolExecError,
+ format!(
+ "Failed to wait on spawned child process, command {:?} with args {:?}: {}.",
+ cmd, program, e
+ ),
+ ));
+ }
+ };
+
+ cargo_output.print_debug(&status);
+
+ if status.success() {
+ Ok(())
+ } else {
+ Err(Error::new(
+ ErrorKind::ToolExecError,
+ format!(
+ "Command {:?} with args {:?} did not execute successfully (status code {}).",
+ cmd, program, status
+ ),
+ ))
+ }
+}
+
+/// Find the destination object path for each file in the input source files,
+/// and store them in the output Object.
+pub(crate) fn objects_from_files(files: &[Arc<Path>], dst: &Path) -> Result<Vec<Object>, Error> {
+ let mut objects = Vec::with_capacity(files.len());
+ for file in files {
+ let basename = file
+ .file_name()
+ .ok_or_else(|| {
+ Error::new(
+ ErrorKind::InvalidArgument,
+ "No file_name for object file path!",
+ )
+ })?
+ .to_string_lossy();
+ let dirname = file
+ .parent()
+ .ok_or_else(|| {
+ Error::new(
+ ErrorKind::InvalidArgument,
+ "No parent for object file path!",
+ )
+ })?
+ .to_string_lossy();
+
+ // Hash the dirname. This should prevent conflicts if we have multiple
+ // object files with the same filename in different subfolders.
+ let mut hasher = hash_map::DefaultHasher::new();
+ hasher.write(dirname.to_string().as_bytes());
+ let obj = dst
+ .join(format!("{:016x}-{}", hasher.finish(), basename))
+ .with_extension("o");
+
+ match obj.parent() {
+ Some(s) => fs::create_dir_all(s)?,
+ None => {
+ return Err(Error::new(
+ ErrorKind::InvalidArgument,
+ "dst is an invalid path with no parent",
+ ));
+ }
+ };
+
+ objects.push(Object::new(file.to_path_buf(), obj));
+ }
+
+ Ok(objects)
+}
+
+pub(crate) fn run(
+ cmd: &mut Command,
+ program: &str,
+ cargo_output: &CargoOutput,
+) -> Result<(), Error> {
+ let mut child = spawn(cmd, program, cargo_output)?;
+ wait_on_child(cmd, program, &mut child, cargo_output)
+}
+
+pub(crate) fn run_output(
+ cmd: &mut Command,
+ program: &str,
+ cargo_output: &CargoOutput,
+) -> Result<Vec<u8>, Error> {
+ cmd.stdout(Stdio::piped());
+
+ let mut child = spawn(cmd, program, cargo_output)?;
+
+ let mut stdout = vec![];
+ child
+ .stdout
+ .take()
+ .unwrap()
+ .read_to_end(&mut stdout)
+ .unwrap();
+
+ wait_on_child(cmd, program, &mut child, cargo_output)?;
+
+ Ok(stdout)
+}
+
+pub(crate) fn spawn(
+ cmd: &mut Command,
+ program: &str,
+ cargo_output: &CargoOutput,
+) -> Result<Child, Error> {
+ struct ResetStderr<'cmd>(&'cmd mut Command);
+
+ impl Drop for ResetStderr<'_> {
+ fn drop(&mut self) {
+ // Reset stderr to default to release pipe_writer so that print thread will
+ // not block forever.
+ self.0.stderr(Stdio::inherit());
+ }
+ }
+
+ cargo_output.print_debug(&format_args!("running: {:?}", cmd));
+
+ let cmd = ResetStderr(cmd);
+ let child = cmd.0.stderr(cargo_output.stdio_for_warnings()).spawn();
+ match child {
+ Ok(child) => Ok(child),
+ Err(ref e) if e.kind() == io::ErrorKind::NotFound => {
+ let extra = if cfg!(windows) {
+ " (see https://github.com/rust-lang/cc-rs#compile-time-requirements \
+for help)"
+ } else {
+ ""
+ };
+ Err(Error::new(
+ ErrorKind::ToolNotFound,
+ format!("Failed to find tool. Is `{}` installed?{}", program, extra),
+ ))
+ }
+ Err(e) => Err(Error::new(
+ ErrorKind::ToolExecError,
+ format!(
+ "Command {:?} with args {:?} failed to start: {:?}",
+ cmd.0, program, e
+ ),
+ )),
+ }
+}
+
+pub(crate) fn command_add_output_file(
+ cmd: &mut Command,
+ dst: &Path,
+ cuda: bool,
+ msvc: bool,
+ clang: bool,
+ gnu: bool,
+ is_asm: bool,
+ is_arm: bool,
+) {
+ if msvc && !clang && !gnu && !cuda && !(is_asm && is_arm) {
+ let mut s = OsString::from("-Fo");
+ s.push(dst);
+ cmd.arg(s);
+ } else {
+ cmd.arg("-o").arg(dst);
+ }
+}
+
+#[cfg(feature = "parallel")]
+pub(crate) fn try_wait_on_child(
+ cmd: &Command,
+ program: &str,
+ child: &mut Child,
+ stdout: &mut dyn io::Write,
+ stderr_forwarder: &mut StderrForwarder,
+) -> Result<Option<()>, Error> {
+ stderr_forwarder.forward_available();
+
+ match child.try_wait() {
+ Ok(Some(status)) => {
+ stderr_forwarder.forward_all();
+
+ let _ = writeln!(stdout, "{}", status);
+
+ if status.success() {
+ Ok(Some(()))
+ } else {
+ Err(Error::new(
+ ErrorKind::ToolExecError,
+ format!(
+ "Command {:?} with args {:?} did not execute successfully (status code {}).",
+ cmd, program, status
+ ),
+ ))
+ }
+ }
+ Ok(None) => Ok(None),
+ Err(e) => {
+ stderr_forwarder.forward_all();
+ Err(Error::new(
+ ErrorKind::ToolExecError,
+ format!(
+ "Failed to wait on spawned child process, command {:?} with args {:?}: {}.",
+ cmd, program, e
+ ),
+ ))
+ }
+ }
+}
diff --git a/third_party/rust/cc/src/lib.rs b/third_party/rust/cc/src/lib.rs
index 1ebd2cc7a5..1b193dbd0a 100644
--- a/third_party/rust/cc/src/lib.rs
+++ b/third_party/rust/cc/src/lib.rs
@@ -1,88 +1,251 @@
-//! A library for build scripts to compile custom C code
+//! A library for [Cargo build scripts](https://doc.rust-lang.org/cargo/reference/build-scripts.html)
+//! to compile a set of C/C++/assembly/CUDA files into a static archive for Cargo
+//! to link into the crate being built. This crate does not compile code itself;
+//! it calls out to the default compiler for the platform. This crate will
+//! automatically detect situations such as cross compilation and
+//! [various environment variables](#external-configuration-via-environment-variables) and will build code appropriately.
//!
-//! This library is intended to be used as a `build-dependencies` entry in
-//! `Cargo.toml`:
+//! # Example
+//!
+//! First, you'll want to both add a build script for your crate (`build.rs`) and
+//! also add this crate to your `Cargo.toml` via:
//!
//! ```toml
//! [build-dependencies]
//! cc = "1.0"
//! ```
//!
-//! The purpose of this crate is to provide the utility functions necessary to
-//! compile C code into a static archive which is then linked into a Rust crate.
-//! Configuration is available through the `Build` struct.
+//! Next up, you'll want to write a build script like so:
+//!
+//! ```rust,no_run
+//! // build.rs
+//!
+//! fn main() {
+//! cc::Build::new()
+//! .file("foo.c")
+//! .file("bar.c")
+//! .compile("foo");
+//! }
+//! ```
+//!
+//! And that's it! Running `cargo build` should take care of the rest and your Rust
+//! application will now have the C files `foo.c` and `bar.c` compiled into a file
+//! named `libfoo.a`. If the C files contain
+//!
+//! ```c
+//! void foo_function(void) { ... }
+//! ```
+//!
+//! and
+//!
+//! ```c
+//! int32_t bar_function(int32_t x) { ... }
+//! ```
+//!
+//! you can call them from Rust by declaring them in
+//! your Rust code like so:
+//!
+//! ```rust,no_run
+//! extern "C" {
+//! fn foo_function();
+//! fn bar_function(x: i32) -> i32;
+//! }
//!
-//! This crate will automatically detect situations such as cross compilation or
-//! other environment variables set by Cargo and will build code appropriately.
+//! pub fn call() {
+//! unsafe {
+//! foo_function();
+//! bar_function(42);
+//! }
+//! }
+//!
+//! fn main() {
+//! call();
+//! }
+//! ```
//!
-//! The crate is not limited to C code, it can accept any source code that can
-//! be passed to a C or C++ compiler. As such, assembly files with extensions
-//! `.s` (gcc/clang) and `.asm` (MSVC) can also be compiled.
+//! See [the Rustonomicon](https://doc.rust-lang.org/nomicon/ffi.html) for more details.
//!
-//! [`Build`]: struct.Build.html
+//! # External configuration via environment variables
//!
-//! # Parallelism
+//! To control the programs and flags used for building, the builder can set a
+//! number of different environment variables.
//!
-//! To parallelize computation, enable the `parallel` feature for the crate.
+//! * `CFLAGS` - a series of space separated flags passed to compilers. Note that
+//! individual flags cannot currently contain spaces, so doing
+//! something like: `-L=foo\ bar` is not possible.
+//! * `CC` - the actual C compiler used. Note that this is used as an exact
+//! executable name, so (for example) no extra flags can be passed inside
+//! this variable, and the builder must ensure that there aren't any
+//! trailing spaces. This compiler must understand the `-c` flag. For
+//! certain `TARGET`s, it also is assumed to know about other flags (most
+//! common is `-fPIC`).
+//! * `AR` - the `ar` (archiver) executable to use to build the static library.
+//! * `CRATE_CC_NO_DEFAULTS` - the default compiler flags may cause conflicts in
+//! some cross compiling scenarios. Setting this variable
+//! will disable the generation of default compiler
+//! flags.
+//! * `CC_ENABLE_DEBUG_OUTPUT` - if set, compiler command invocations and exit codes will
+//! be logged to stdout. This is useful for debugging build script issues, but can be
+//! overly verbose for normal use.
+//! * `CXX...` - see [C++ Support](#c-support).
+//!
+//! Furthermore, projects using this crate may specify custom environment variables
+//! to be inspected, for example via the `Build::try_flags_from_environment`
+//! function. Consult the project’s own documentation or its use of the `cc` crate
+//! for any additional variables it may use.
+//!
+//! Each of these variables can also be supplied with certain prefixes and suffixes,
+//! in the following prioritized order:
+//!
+//! 1. `<var>_<target>` - for example, `CC_x86_64-unknown-linux-gnu`
+//! 2. `<var>_<target_with_underscores>` - for example, `CC_x86_64_unknown_linux_gnu`
+//! 3. `<build-kind>_<var>` - for example, `HOST_CC` or `TARGET_CFLAGS`
+//! 4. `<var>` - a plain `CC`, `AR` as above.
+//!
+//! If none of these variables exist, cc-rs uses built-in defaults.
+//!
+//! In addition to the above optional environment variables, `cc-rs` has some
+//! functions with hard requirements on some variables supplied by [cargo's
+//! build-script driver][cargo] that it has the `TARGET`, `OUT_DIR`, `OPT_LEVEL`,
+//! and `HOST` variables.
+//!
+//! [cargo]: https://doc.rust-lang.org/cargo/reference/build-scripts.html#inputs-to-the-build-script
+//!
+//! # Optional features
+//!
+//! ## Parallel
+//!
+//! Currently cc-rs supports parallel compilation (think `make -jN`) but this
+//! feature is turned off by default. To enable cc-rs to compile C/C++ in parallel,
+//! you can change your dependency to:
//!
//! ```toml
//! [build-dependencies]
//! cc = { version = "1.0", features = ["parallel"] }
//! ```
-//! To specify the max number of concurrent compilation jobs, set the `NUM_JOBS`
-//! environment variable to the desired amount.
//!
-//! Cargo will also set this environment variable when executed with the `-jN` flag.
+//! By default cc-rs will limit parallelism to `$NUM_JOBS`, or if not present it
+//! will limit it to the number of cpus on the machine. If you are using cargo,
+//! use `-jN` option of `build`, `test` and `run` commands as `$NUM_JOBS`
+//! is supplied by cargo.
+//!
+//! # Compile-time Requirements
+//!
+//! To work properly this crate needs access to a C compiler when the build script
+//! is being run. This crate does not ship a C compiler with it. The compiler
+//! required varies per platform, but there are three broad categories:
//!
-//! If `NUM_JOBS` is not set, the `RAYON_NUM_THREADS` environment variable can
-//! also specify the build parallelism.
+//! * Unix platforms require `cc` to be the C compiler. This can be found by
+//! installing cc/clang on Linux distributions and Xcode on macOS, for example.
+//! * Windows platforms targeting MSVC (e.g. your target triple ends in `-msvc`)
+//! require Visual Studio to be installed. `cc-rs` attempts to locate it, and
+//! if it fails, `cl.exe` is expected to be available in `PATH`. This can be
+//! set up by running the appropriate developer tools shell.
+//! * Windows platforms targeting MinGW (e.g. your target triple ends in `-gnu`)
+//! require `cc` to be available in `PATH`. We recommend the
+//! [MinGW-w64](https://www.mingw-w64.org/) distribution, which is using the
+//! [Win-builds](http://win-builds.org/) installation system.
+//! You may also acquire it via
+//! [MSYS2](https://www.msys2.org/), as explained [here][msys2-help]. Make sure
+//! to install the appropriate architecture corresponding to your installation of
+//! rustc. GCC from older [MinGW](http://www.mingw.org/) project is compatible
+//! only with 32-bit rust compiler.
//!
-//! # Examples
+//! [msys2-help]: https://github.com/rust-lang/rust#building-on-windows
//!
-//! Use the `Build` struct to compile `src/foo.c`:
+//! # C++ support
//!
-//! ```no_run
+//! `cc-rs` supports C++ libraries compilation by using the `cpp` method on
+//! `Build`:
+//!
+//! ```rust,no_run
+//! fn main() {
+//! cc::Build::new()
+//! .cpp(true) // Switch to C++ library compilation.
+//! .file("foo.cpp")
+//! .compile("foo");
+//! }
+//! ```
+//!
+//! For C++ libraries, the `CXX` and `CXXFLAGS` environment variables are used instead of `CC` and `CFLAGS`.
+//!
+//! The C++ standard library may be linked to the crate target. By default it's `libc++` for macOS, FreeBSD, and OpenBSD, `libc++_shared` for Android, nothing for MSVC, and `libstdc++` for anything else. It can be changed in one of two ways:
+//!
+//! 1. by using the `cpp_link_stdlib` method on `Build`:
+//! ```rust,no_run
//! fn main() {
//! cc::Build::new()
-//! .file("src/foo.c")
-//! .define("FOO", Some("bar"))
-//! .include("src")
+//! .cpp(true)
+//! .file("foo.cpp")
+//! .cpp_link_stdlib("stdc++") // use libstdc++
//! .compile("foo");
//! }
//! ```
+//! 2. by setting the `CXXSTDLIB` environment variable.
+//!
+//! In particular, for Android you may want to [use `c++_static` if you have at most one shared library](https://developer.android.com/ndk/guides/cpp-support).
+//!
+//! Remember that C++ does name mangling so `extern "C"` might be required to enable Rust linker to find your functions.
+//!
+//! # CUDA C++ support
+//!
+//! `cc-rs` also supports compiling CUDA C++ libraries by using the `cuda` method
+//! on `Build`:
+//!
+//! ```rust,no_run
+//! fn main() {
+//! cc::Build::new()
+//! // Switch to CUDA C++ library compilation using NVCC.
+//! .cuda(true)
+//! .cudart("static")
+//! // Generate code for Maxwell (GTX 970, 980, 980 Ti, Titan X).
+//! .flag("-gencode").flag("arch=compute_52,code=sm_52")
+//! // Generate code for Maxwell (Jetson TX1).
+//! .flag("-gencode").flag("arch=compute_53,code=sm_53")
+//! // Generate code for Pascal (GTX 1070, 1080, 1080 Ti, Titan Xp).
+//! .flag("-gencode").flag("arch=compute_61,code=sm_61")
+//! // Generate code for Pascal (Tesla P100).
+//! .flag("-gencode").flag("arch=compute_60,code=sm_60")
+//! // Generate code for Pascal (Jetson TX2).
+//! .flag("-gencode").flag("arch=compute_62,code=sm_62")
+//! // Generate code in parallel
+//! .flag("-t0")
+//! .file("bar.cu")
+//! .compile("bar");
+//! }
+//! ```
#![doc(html_root_url = "https://docs.rs/cc/1.0")]
#![cfg_attr(test, deny(warnings))]
#![allow(deprecated)]
#![deny(missing_docs)]
-use std::collections::{hash_map, HashMap};
+use std::borrow::Cow;
+use std::collections::HashMap;
use std::env;
use std::ffi::{OsStr, OsString};
use std::fmt::{self, Display, Formatter};
use std::fs;
-use std::hash::Hasher;
-use std::io::{self, BufRead, BufReader, Read, Write};
+use std::io::{self, Write};
use std::path::{Component, Path, PathBuf};
-use std::process::{Child, Command, Stdio};
+#[cfg(feature = "parallel")]
+use std::process::Child;
+use std::process::Command;
use std::sync::{Arc, Mutex};
-use std::thread::{self, JoinHandle};
-
-// These modules are all glue to support reading the MSVC version from
-// the registry and from COM interfaces
-#[cfg(windows)]
-mod registry;
-#[cfg(windows)]
-#[macro_use]
-mod winapi;
-#[cfg(windows)]
-mod com;
-#[cfg(windows)]
-mod setup_config;
-#[cfg(windows)]
-mod vs_instances;
-
-pub mod windows_registry;
+
+#[cfg(feature = "parallel")]
+mod parallel;
+mod windows;
+// Regardless of whether this should be in this crate's public API,
+// it has been since 2015, so don't break it.
+pub use windows::find_tools as windows_registry;
+
+mod command_helpers;
+use command_helpers::*;
+
+mod tool;
+pub use tool::Tool;
+use tool::ToolFamily;
/// A builder for compilation of a native library.
///
@@ -91,32 +254,34 @@ pub mod windows_registry;
/// documentation on each method itself.
#[derive(Clone, Debug)]
pub struct Build {
- include_directories: Vec<PathBuf>,
- definitions: Vec<(String, Option<String>)>,
- objects: Vec<PathBuf>,
- flags: Vec<String>,
- flags_supported: Vec<String>,
+ include_directories: Vec<Arc<Path>>,
+ definitions: Vec<(Arc<str>, Option<Arc<str>>)>,
+ objects: Vec<Arc<Path>>,
+ flags: Vec<Arc<str>>,
+ flags_supported: Vec<Arc<str>>,
known_flag_support_status: Arc<Mutex<HashMap<String, bool>>>,
- ar_flags: Vec<String>,
- asm_flags: Vec<String>,
+ ar_flags: Vec<Arc<str>>,
+ asm_flags: Vec<Arc<str>>,
no_default_flags: bool,
- files: Vec<PathBuf>,
+ files: Vec<Arc<Path>>,
cpp: bool,
- cpp_link_stdlib: Option<Option<String>>,
- cpp_set_stdlib: Option<String>,
+ cpp_link_stdlib: Option<Option<Arc<str>>>,
+ cpp_set_stdlib: Option<Arc<str>>,
cuda: bool,
- cudart: Option<String>,
- target: Option<String>,
- host: Option<String>,
- out_dir: Option<PathBuf>,
- opt_level: Option<String>,
+ cudart: Option<Arc<str>>,
+ std: Option<Arc<str>>,
+ target: Option<Arc<str>>,
+ host: Option<Arc<str>>,
+ out_dir: Option<Arc<Path>>,
+ opt_level: Option<Arc<str>>,
debug: Option<bool>,
force_frame_pointer: Option<bool>,
- env: Vec<(OsString, OsString)>,
- compiler: Option<PathBuf>,
- archiver: Option<PathBuf>,
- cargo_metadata: bool,
- link_lib_modifiers: Vec<String>,
+ env: Vec<(Arc<OsStr>, Arc<OsStr>)>,
+ compiler: Option<Arc<Path>>,
+ archiver: Option<Arc<Path>>,
+ ranlib: Option<Arc<Path>>,
+ cargo_output: CargoOutput,
+ link_lib_modifiers: Vec<Arc<str>>,
pic: Option<bool>,
use_plt: Option<bool>,
static_crt: Option<bool>,
@@ -125,9 +290,11 @@ pub struct Build {
warnings_into_errors: bool,
warnings: Option<bool>,
extra_warnings: Option<bool>,
- env_cache: Arc<Mutex<HashMap<String, Option<String>>>>,
+ env_cache: Arc<Mutex<HashMap<String, Option<Arc<str>>>>>,
apple_sdk_root_cache: Arc<Mutex<HashMap<String, OsString>>>,
+ apple_versions_cache: Arc<Mutex<HashMap<String, String>>>,
emit_rerun_if_env_changed: bool,
+ cached_compiler_family: Arc<Mutex<HashMap<Box<Path>, ToolFamily>>>,
}
/// Represents the types of errors that may occur while using cc-rs.
@@ -145,6 +312,9 @@ enum ErrorKind {
ToolNotFound,
/// One of the function arguments failed validation.
InvalidArgument,
+ #[cfg(feature = "parallel")]
+ /// jobserver helpthread failure
+ JobserverHelpThreadError,
}
/// Represents an internal error that occurred, with an explanation.
@@ -153,21 +323,21 @@ pub struct Error {
/// Describes the kind of error that occurred.
kind: ErrorKind,
/// More explanation of error that occurred.
- message: String,
+ message: Cow<'static, str>,
}
impl Error {
- fn new(kind: ErrorKind, message: &str) -> Error {
+ fn new(kind: ErrorKind, message: impl Into<Cow<'static, str>>) -> Error {
Error {
- kind: kind,
- message: message.to_owned(),
+ kind,
+ message: message.into(),
}
}
}
impl From<io::Error> for Error {
fn from(e: io::Error) -> Error {
- Error::new(ErrorKind::IOError, &format!("{}", e))
+ Error::new(ErrorKind::IOError, format!("{}", e))
}
}
@@ -179,97 +349,6 @@ impl Display for Error {
impl std::error::Error for Error {}
-/// Configuration used to represent an invocation of a C compiler.
-///
-/// This can be used to figure out what compiler is in use, what the arguments
-/// to it are, and what the environment variables look like for the compiler.
-/// This can be used to further configure other build systems (e.g. forward
-/// along CC and/or CFLAGS) or the `to_command` method can be used to run the
-/// compiler itself.
-#[derive(Clone, Debug)]
-pub struct Tool {
- path: PathBuf,
- cc_wrapper_path: Option<PathBuf>,
- cc_wrapper_args: Vec<OsString>,
- args: Vec<OsString>,
- env: Vec<(OsString, OsString)>,
- family: ToolFamily,
- cuda: bool,
- removed_args: Vec<OsString>,
-}
-
-/// Represents the family of tools this tool belongs to.
-///
-/// Each family of tools differs in how and what arguments they accept.
-///
-/// Detection of a family is done on best-effort basis and may not accurately reflect the tool.
-#[derive(Copy, Clone, Debug, PartialEq)]
-enum ToolFamily {
- /// Tool is GNU Compiler Collection-like.
- Gnu,
- /// Tool is Clang-like. It differs from the GCC in a sense that it accepts superset of flags
- /// and its cross-compilation approach is different.
- Clang,
- /// Tool is the MSVC cl.exe.
- Msvc { clang_cl: bool },
-}
-
-impl ToolFamily {
- /// What the flag to request debug info for this family of tools look like
- fn add_debug_flags(&self, cmd: &mut Tool, dwarf_version: Option<u32>) {
- match *self {
- ToolFamily::Msvc { .. } => {
- cmd.push_cc_arg("-Z7".into());
- }
- ToolFamily::Gnu | ToolFamily::Clang => {
- cmd.push_cc_arg(
- dwarf_version
- .map_or_else(|| "-g".into(), |v| format!("-gdwarf-{}", v))
- .into(),
- );
- }
- }
- }
-
- /// What the flag to force frame pointers.
- fn add_force_frame_pointer(&self, cmd: &mut Tool) {
- match *self {
- ToolFamily::Gnu | ToolFamily::Clang => {
- cmd.push_cc_arg("-fno-omit-frame-pointer".into());
- }
- _ => (),
- }
- }
-
- /// What the flags to enable all warnings
- fn warnings_flags(&self) -> &'static str {
- match *self {
- ToolFamily::Msvc { .. } => "-W4",
- ToolFamily::Gnu | ToolFamily::Clang => "-Wall",
- }
- }
-
- /// What the flags to enable extra warnings
- fn extra_warnings_flags(&self) -> Option<&'static str> {
- match *self {
- ToolFamily::Msvc { .. } => None,
- ToolFamily::Gnu | ToolFamily::Clang => Some("-Wextra"),
- }
- }
-
- /// What the flag to turn warning into errors
- fn warnings_to_errors_flag(&self) -> &'static str {
- match *self {
- ToolFamily::Msvc { .. } => "-WX",
- ToolFamily::Gnu | ToolFamily::Clang => "-Werror",
- }
- }
-
- fn verbose_stderr(&self) -> bool {
- *self == ToolFamily::Clang
- }
-}
-
/// Represents an object.
///
/// This is a source file -> object file pair.
@@ -282,7 +361,7 @@ struct Object {
impl Object {
/// Create a new source file -> object file pair.
fn new(src: PathBuf, dst: PathBuf) -> Object {
- Object { src: src, dst: dst }
+ Object { src, dst }
}
}
@@ -311,6 +390,7 @@ impl Build {
cpp_set_stdlib: None,
cuda: false,
cudart: None,
+ std: None,
target: None,
host: None,
out_dir: None,
@@ -320,7 +400,8 @@ impl Build {
env: Vec::new(),
compiler: None,
archiver: None,
- cargo_metadata: true,
+ ranlib: None,
+ cargo_output: CargoOutput::new(),
link_lib_modifiers: Vec::new(),
pic: None,
use_plt: None,
@@ -330,7 +411,9 @@ impl Build {
warnings_into_errors: false,
env_cache: Arc::new(Mutex::new(HashMap::new())),
apple_sdk_root_cache: Arc::new(Mutex::new(HashMap::new())),
+ apple_versions_cache: Arc::new(Mutex::new(HashMap::new())),
emit_rerun_if_env_changed: true,
+ cached_compiler_family: Arc::default(),
}
}
@@ -350,7 +433,7 @@ impl Build {
/// .compile("foo");
/// ```
pub fn include<P: AsRef<Path>>(&mut self, dir: P) -> &mut Build {
- self.include_directories.push(dir.as_ref().to_path_buf());
+ self.include_directories.push(dir.as_ref().into());
self
}
@@ -396,13 +479,13 @@ impl Build {
/// ```
pub fn define<'a, V: Into<Option<&'a str>>>(&mut self, var: &str, val: V) -> &mut Build {
self.definitions
- .push((var.to_string(), val.into().map(|s| s.to_string())));
+ .push((var.into(), val.into().map(Into::into)));
self
}
/// Add an arbitrary object file to link in
pub fn object<P: AsRef<Path>>(&mut self, obj: P) -> &mut Build {
- self.objects.push(obj.as_ref().to_path_buf());
+ self.objects.push(obj.as_ref().into());
self
}
@@ -417,7 +500,25 @@ impl Build {
/// .compile("foo");
/// ```
pub fn flag(&mut self, flag: &str) -> &mut Build {
- self.flags.push(flag.to_string());
+ self.flags.push(flag.into());
+ self
+ }
+
+ /// Removes a compiler flag that was added by [`Build::flag`].
+ ///
+ /// Will not remove flags added by other means (default flags,
+ /// flags from env, and so on).
+ ///
+ /// # Example
+ /// ```
+ /// cc::Build::new()
+ /// .file("src/foo.c")
+ /// .flag("unwanted_flag")
+ /// .remove_flag("unwanted_flag");
+ /// ```
+
+ pub fn remove_flag(&mut self, flag: &str) -> &mut Build {
+ self.flags.retain(|other_flag| &**other_flag != flag);
self
}
@@ -433,7 +534,7 @@ impl Build {
/// .compile("foo");
/// ```
pub fn ar_flag(&mut self, flag: &str) -> &mut Build {
- self.ar_flags.push(flag.to_string());
+ self.ar_flags.push(flag.into());
self
}
@@ -452,7 +553,7 @@ impl Build {
/// .compile("foo");
/// ```
pub fn asm_flag(&mut self, flag: &str) -> &mut Build {
- self.asm_flags.push(flag.to_string());
+ self.asm_flags.push(flag.into());
self
}
@@ -499,6 +600,7 @@ impl Build {
let host = self.get_host()?;
let mut cfg = Build::new();
cfg.flag(flag)
+ .cargo_metadata(self.cargo_output.metadata)
.target(&target)
.opt_level(0)
.host(&host)
@@ -515,30 +617,34 @@ impl Build {
if compiler.family.verbose_stderr() {
compiler.remove_arg("-v".into());
}
+ if compiler.family == ToolFamily::Clang {
+ // Avoid reporting that the arg is unsupported just because the
+ // compiler complains that it wasn't used.
+ compiler.push_cc_arg("-Wno-unused-command-line-argument".into());
+ }
let mut cmd = compiler.to_command();
let is_arm = target.contains("aarch64") || target.contains("arm");
let clang = compiler.family == ToolFamily::Clang;
+ let gnu = compiler.family == ToolFamily::Gnu;
command_add_output_file(
&mut cmd,
&obj,
self.cuda,
target.contains("msvc"),
clang,
+ gnu,
false,
is_arm,
);
- // We need to explicitly tell msvc not to link and create an exe
- // in the root directory of the crate
- if target.contains("msvc") && !self.cuda {
- cmd.arg("-c");
- }
+ // Checking for compiler flags does not require linking
+ cmd.arg("-c");
cmd.arg(&src);
let output = cmd.output()?;
- let is_supported = output.stderr.is_empty();
+ let is_supported = output.status.success() && output.stderr.is_empty();
known_status.insert(flag.to_owned(), is_supported);
Ok(is_supported)
@@ -556,10 +662,39 @@ impl Build {
/// .compile("foo");
/// ```
pub fn flag_if_supported(&mut self, flag: &str) -> &mut Build {
- self.flags_supported.push(flag.to_string());
+ self.flags_supported.push(flag.into());
self
}
+ /// Add flags from the specified environment variable.
+ ///
+ /// Normally the `cc` crate will consult with the standard set of environment
+ /// variables (such as `CFLAGS` and `CXXFLAGS`) to construct the compiler invocation. Use of
+ /// this method provides additional levers for the end user to use when configuring the build
+ /// process.
+ ///
+ /// Just like the standard variables, this method will search for an environment variable with
+ /// appropriate target prefixes, when appropriate.
+ ///
+ /// # Examples
+ ///
+ /// This method is particularly beneficial in introducing the ability to specify crate-specific
+ /// flags.
+ ///
+ /// ```no_run
+ /// cc::Build::new()
+ /// .file("src/foo.c")
+ /// .try_flags_from_environment(concat!(env!("CARGO_PKG_NAME"), "_CFLAGS"))
+ /// .expect("the environment variable must be specified and UTF-8")
+ /// .compile("foo");
+ /// ```
+ ///
+ pub fn try_flags_from_environment(&mut self, environ_key: &str) -> Result<&mut Build, Error> {
+ let flags = self.envflags(environ_key)?;
+ self.flags.extend(flags.into_iter().map(Into::into));
+ Ok(self)
+ }
+
/// Set the `-shared` flag.
///
/// When enabled, the compiler will produce a shared object which can
@@ -610,7 +745,7 @@ impl Build {
/// Add a file which will be compiled
pub fn file<P: AsRef<Path>>(&mut self, p: P) -> &mut Build {
- self.files.push(p.as_ref().to_path_buf());
+ self.files.push(p.as_ref().into());
self
}
@@ -626,10 +761,21 @@ impl Build {
self
}
+ /// Get the files which will be compiled
+ pub fn get_files(&self) -> impl Iterator<Item = &Path> {
+ self.files.iter().map(AsRef::as_ref)
+ }
+
/// Set C++ support.
///
/// The other `cpp_*` options will only become active if this is set to
/// `true`.
+ ///
+ /// The name of the C++ standard library to link is decided by:
+ /// 1. If [`cpp_link_stdlib`](Build::cpp_link_stdlib) is set, use its value.
+ /// 2. Else if the `CXXSTDLIB` environment variable is set, use its value.
+ /// 3. Else the default is `libc++` for OS X and BSDs, `libc++_shared` for Android,
+ /// `None` for MSVC and `libstdc++` for anything else.
pub fn cpp(&mut self, cpp: bool) -> &mut Build {
self.cpp = cpp;
self
@@ -637,17 +783,19 @@ impl Build {
/// Set CUDA C++ support.
///
- /// Enabling CUDA will pass the detected C/C++ toolchain as an argument to
- /// the CUDA compiler, NVCC. NVCC itself accepts some limited GNU-like args;
- /// any other arguments for the C/C++ toolchain will be redirected using
- /// "-Xcompiler" flags.
+ /// Enabling CUDA will invoke the CUDA compiler, NVCC. While NVCC accepts
+ /// the most common compiler flags, e.g. `-std=c++17`, some project-specific
+ /// flags might have to be prefixed with "-Xcompiler" flag, for example as
+ /// `.flag("-Xcompiler").flag("-fpermissive")`. See the documentation for
+ /// `nvcc`, the CUDA compiler driver, at <https://docs.nvidia.com/cuda/cuda-compiler-driver-nvcc/>
+ /// for more information.
///
/// If enabled, this also implicitly enables C++ support.
pub fn cuda(&mut self, cuda: bool) -> &mut Build {
self.cuda = cuda;
if cuda {
self.cpp = true;
- self.cudart = Some("static".to_string());
+ self.cudart = Some("static".into());
}
self
}
@@ -660,11 +808,42 @@ impl Build {
/// at all, if the default is right for the project.
pub fn cudart(&mut self, cudart: &str) -> &mut Build {
if self.cuda {
- self.cudart = Some(cudart.to_string());
+ self.cudart = Some(cudart.into());
}
self
}
+ /// Specify the C or C++ language standard version.
+ ///
+ /// These values are common to modern versions of GCC, Clang and MSVC:
+ /// - `c11` for ISO/IEC 9899:2011
+ /// - `c17` for ISO/IEC 9899:2018
+ /// - `c++14` for ISO/IEC 14882:2014
+ /// - `c++17` for ISO/IEC 14882:2017
+ /// - `c++20` for ISO/IEC 14882:2020
+ ///
+ /// Other values have less broad support, e.g. MSVC does not support `c++11`
+ /// (`c++14` is the minimum), `c89` (omit the flag instead) or `c99`.
+ ///
+ /// For compiling C++ code, you should also set `.cpp(true)`.
+ ///
+ /// The default is that no standard flag is passed to the compiler, so the
+ /// language version will be the compiler's default.
+ ///
+ /// # Example
+ ///
+ /// ```no_run
+ /// cc::Build::new()
+ /// .file("src/modern.cpp")
+ /// .cpp(true)
+ /// .std("c++17")
+ /// .compile("modern");
+ /// ```
+ pub fn std(&mut self, std: &str) -> &mut Build {
+ self.std = Some(std.into());
+ self
+ }
+
/// Set warnings into errors flag.
///
/// Disabled by default.
@@ -736,8 +915,6 @@ impl Build {
/// Set the standard library to link against when compiling with C++
/// support.
///
- /// See [`get_cpp_link_stdlib`](cc::Build::get_cpp_link_stdlib) documentation
- /// for the default value.
/// If the `CXXSTDLIB` environment variable is set, its value will
/// override the default value, but not the value explicitly set by calling
/// this function.
@@ -826,7 +1003,7 @@ impl Build {
/// .compile("foo");
/// ```
pub fn target(&mut self, target: &str) -> &mut Build {
- self.target = Some(target.to_string());
+ self.target = Some(target.into());
self
}
@@ -844,7 +1021,7 @@ impl Build {
/// .compile("foo");
/// ```
pub fn host(&mut self, host: &str) -> &mut Build {
- self.host = Some(host.to_string());
+ self.host = Some(host.into());
self
}
@@ -853,7 +1030,7 @@ impl Build {
/// This option is automatically scraped from the `OPT_LEVEL` environment
/// variable by build scripts, so it's not required to call this function.
pub fn opt_level(&mut self, opt_level: u32) -> &mut Build {
- self.opt_level = Some(opt_level.to_string());
+ self.opt_level = Some(opt_level.to_string().into());
self
}
@@ -862,7 +1039,7 @@ impl Build {
/// This option is automatically scraped from the `OPT_LEVEL` environment
/// variable by build scripts, so it's not required to call this function.
pub fn opt_level_str(&mut self, opt_level: &str) -> &mut Build {
- self.opt_level = Some(opt_level.to_string());
+ self.opt_level = Some(opt_level.into());
self
}
@@ -893,7 +1070,7 @@ impl Build {
/// This option is automatically scraped from the `OUT_DIR` environment
/// variable by build scripts, so it's not required to call this function.
pub fn out_dir<P: AsRef<Path>>(&mut self, out_dir: P) -> &mut Build {
- self.out_dir = Some(out_dir.as_ref().to_owned());
+ self.out_dir = Some(out_dir.as_ref().into());
self
}
@@ -903,7 +1080,7 @@ impl Build {
/// number of environment variables, so it's not required to call this
/// function.
pub fn compiler<P: AsRef<Path>>(&mut self, compiler: P) -> &mut Build {
- self.compiler = Some(compiler.as_ref().to_owned());
+ self.compiler = Some(compiler.as_ref().into());
self
}
@@ -913,9 +1090,20 @@ impl Build {
/// number of environment variables, so it's not required to call this
/// function.
pub fn archiver<P: AsRef<Path>>(&mut self, archiver: P) -> &mut Build {
- self.archiver = Some(archiver.as_ref().to_owned());
+ self.archiver = Some(archiver.as_ref().into());
self
}
+
+ /// Configures the tool used to index archives.
+ ///
+ /// This option is automatically determined from the target platform or a
+ /// number of environment variables, so it's not required to call this
+ /// function.
+ pub fn ranlib<P: AsRef<Path>>(&mut self, ranlib: P) -> &mut Build {
+ self.ranlib = Some(ranlib.as_ref().into());
+ self
+ }
+
/// Define whether metadata should be emitted for cargo allowing it to
/// automatically link the binary. Defaults to `true`.
///
@@ -928,17 +1116,37 @@ impl Build {
/// - If `emit_rerun_if_env_changed` is not `false`, `rerun-if-env-changed=`*env*
///
pub fn cargo_metadata(&mut self, cargo_metadata: bool) -> &mut Build {
- self.cargo_metadata = cargo_metadata;
+ self.cargo_output.metadata = cargo_metadata;
+ self
+ }
+
+ /// Define whether compile warnings should be emitted for cargo. Defaults to
+ /// `true`.
+ ///
+ /// If disabled, compiler messages will not be printed.
+ /// Issues unrelated to the compilation will always produce cargo warnings regardless of this setting.
+ pub fn cargo_warnings(&mut self, cargo_warnings: bool) -> &mut Build {
+ self.cargo_output.warnings = cargo_warnings;
+ self
+ }
+
+ /// Define whether debug information should be emitted for cargo. Defaults to whether
+ /// or not the environment variable `CC_ENABLE_DEBUG_OUTPUT` is set.
+ ///
+ /// If enabled, the compiler will emit debug information when generating object files,
+ /// such as the command invoked and the exit status.
+ pub fn cargo_debug(&mut self, cargo_debug: bool) -> &mut Build {
+ self.cargo_output.debug = cargo_debug;
self
}
/// Adds a native library modifier that will be added to the
/// `rustc-link-lib=static:MODIFIERS=LIBRARY_NAME` metadata line
/// emitted for cargo if `cargo_metadata` is enabled.
- /// See https://doc.rust-lang.org/rustc/command-line-arguments.html#-l-link-the-generated-crate-to-a-native-library
+ /// See <https://doc.rust-lang.org/rustc/command-line-arguments.html#-l-link-the-generated-crate-to-a-native-library>
/// for the list of modifiers accepted by rustc.
pub fn link_lib_modifier(&mut self, link_lib_modifier: &str) -> &mut Build {
- self.link_lib_modifiers.push(link_lib_modifier.to_string());
+ self.link_lib_modifiers.push(link_lib_modifier.into());
self
}
@@ -991,14 +1199,13 @@ impl Build {
A: AsRef<OsStr>,
B: AsRef<OsStr>,
{
- self.env
- .push((a.as_ref().to_owned(), b.as_ref().to_owned()));
+ self.env.push((a.as_ref().into(), b.as_ref().into()));
self
}
/// Run the compiler, generating the file `output`
///
- /// This will return a result instead of panicing; see compile() for the complete description.
+ /// This will return a result instead of panicking; see compile() for the complete description.
pub fn try_compile(&self, output: &str) -> Result<(), Error> {
let mut output_components = Path::new(output).components();
match (output_components.next(), output_components.next()) {
@@ -1016,52 +1223,14 @@ impl Build {
} else {
let mut gnu = String::with_capacity(5 + output.len());
gnu.push_str("lib");
- gnu.push_str(&output);
+ gnu.push_str(output);
gnu.push_str(".a");
(output, gnu)
};
let dst = self.get_out_dir()?;
- let mut objects = Vec::new();
- for file in self.files.iter() {
- let obj = if file.has_root() {
- // If `file` is an absolute path, prefix the `basename`
- // with the `dirname`'s hash to ensure name uniqueness.
- let basename = file
- .file_name()
- .ok_or_else(|| Error::new(ErrorKind::InvalidArgument, "file_name() failure"))?
- .to_string_lossy();
- let dirname = file
- .parent()
- .ok_or_else(|| Error::new(ErrorKind::InvalidArgument, "parent() failure"))?
- .to_string_lossy();
- let mut hasher = hash_map::DefaultHasher::new();
- hasher.write(dirname.to_string().as_bytes());
- dst.join(format!("{:016x}-{}", hasher.finish(), basename))
- .with_extension("o")
- } else {
- dst.join(file).with_extension("o")
- };
- let obj = if !obj.starts_with(&dst) {
- dst.join(obj.file_name().ok_or_else(|| {
- Error::new(ErrorKind::IOError, "Getting object file details failed.")
- })?)
- } else {
- obj
- };
+ let objects = objects_from_files(&self.files, &dst)?;
- match obj.parent() {
- Some(s) => fs::create_dir_all(s)?,
- None => {
- return Err(Error::new(
- ErrorKind::IOError,
- "Getting object file details failed.",
- ));
- }
- };
-
- objects.push(Object::new(file.to_path_buf(), obj));
- }
self.compile_objects(&objects)?;
self.assemble(lib_name, &dst.join(gnu_lib_name), &objects)?;
@@ -1070,8 +1239,8 @@ impl Build {
let atlmfc_lib = compiler
.env()
.iter()
- .find(|&&(ref var, _)| var.as_os_str() == OsStr::new("LIB"))
- .and_then(|&(_, ref lib_paths)| {
+ .find(|&(var, _)| var.as_os_str() == OsStr::new("LIB"))
+ .and_then(|(_, lib_paths)| {
env::split_paths(lib_paths).find(|path| {
let sub = Path::new("atlmfc/lib");
path.ends_with(sub) || path.parent().map_or(false, |p| p.ends_with(sub))
@@ -1079,7 +1248,7 @@ impl Build {
});
if let Some(atlmfc_lib) = atlmfc_lib {
- self.print(&format!(
+ self.cargo_output.print_metadata(&format_args!(
"cargo:rustc-link-search=native={}",
atlmfc_lib.display()
));
@@ -1087,26 +1256,34 @@ impl Build {
}
if self.link_lib_modifiers.is_empty() {
- self.print(&format!("cargo:rustc-link-lib=static={}", lib_name));
+ self.cargo_output
+ .print_metadata(&format_args!("cargo:rustc-link-lib=static={}", lib_name));
} else {
let m = self.link_lib_modifiers.join(",");
- self.print(&format!("cargo:rustc-link-lib=static:{}={}", m, lib_name));
+ self.cargo_output.print_metadata(&format_args!(
+ "cargo:rustc-link-lib=static:{}={}",
+ m, lib_name
+ ));
}
- self.print(&format!("cargo:rustc-link-search=native={}", dst.display()));
+ self.cargo_output.print_metadata(&format_args!(
+ "cargo:rustc-link-search=native={}",
+ dst.display()
+ ));
// Add specific C++ libraries, if enabled.
if self.cpp {
if let Some(stdlib) = self.get_cpp_link_stdlib()? {
- self.print(&format!("cargo:rustc-link-lib={}", stdlib));
+ self.cargo_output
+ .print_metadata(&format_args!("cargo:rustc-link-lib={}", stdlib));
}
}
let cudart = match &self.cudart {
- Some(opt) => opt.as_str(), // {none|shared|static}
+ Some(opt) => &*opt, // {none|shared|static}
None => "none",
};
if cudart != "none" {
- if let Some(nvcc) = which(&self.get_compiler().path) {
+ if let Some(nvcc) = which(&self.get_compiler().path, None) {
// Try to figure out the -L search path. If it fails,
// it's on user to specify one by passing it through
// RUSTFLAGS environment variable.
@@ -1135,10 +1312,10 @@ impl Build {
}
}
if libtst && libdir.is_dir() {
- println!(
+ self.cargo_output.print_metadata(&format_args!(
"cargo:rustc-link-search=native={}",
libdir.to_str().unwrap()
- );
+ ));
}
// And now the -l flag.
@@ -1147,7 +1324,8 @@ impl Build {
"static" => "cudart_static",
bad => panic!("unsupported cudart option: {}", bad),
};
- println!("cargo:rustc-link-lib={}", lib);
+ self.cargo_output
+ .print_metadata(&format_args!("cargo:rustc-link-lib={}", lib));
}
}
@@ -1197,18 +1375,48 @@ impl Build {
}
}
+ /// Run the compiler, generating intermediate files, but without linking
+ /// them into an archive file.
+ ///
+ /// This will return a list of compiled object files, in the same order
+ /// as they were passed in as `file`/`files` methods.
+ pub fn compile_intermediates(&self) -> Vec<PathBuf> {
+ match self.try_compile_intermediates() {
+ Ok(v) => v,
+ Err(e) => fail(&e.message),
+ }
+ }
+
+ /// Run the compiler, generating intermediate files, but without linking
+ /// them into an archive file.
+ ///
+ /// This will return a result instead of panicking; see `compile_intermediates()` for the complete description.
+ pub fn try_compile_intermediates(&self) -> Result<Vec<PathBuf>, Error> {
+ let dst = self.get_out_dir()?;
+ let objects = objects_from_files(&self.files, &dst)?;
+
+ self.compile_objects(&objects)?;
+
+ Ok(objects.into_iter().map(|v| v.dst).collect())
+ }
+
#[cfg(feature = "parallel")]
- fn compile_objects<'me>(&'me self, objs: &[Object]) -> Result<(), Error> {
- use std::sync::atomic::{AtomicBool, Ordering::SeqCst};
- use std::sync::Once;
-
- // Limit our parallelism globally with a jobserver. Start off by
- // releasing our own token for this process so we can have a bit of an
- // easier to write loop below. If this fails, though, then we're likely
- // on Windows with the main implicit token, so we just have a bit extra
- // parallelism for a bit and don't reacquire later.
- let server = jobserver();
- let reacquire = server.release_raw().is_ok();
+ fn compile_objects(&self, objs: &[Object]) -> Result<(), Error> {
+ use std::cell::Cell;
+
+ use parallel::async_executor::{block_on, YieldOnce};
+
+ if objs.len() <= 1 {
+ for obj in objs {
+ let (mut cmd, name) = self.create_compile_object_cmd(obj)?;
+ run(&mut cmd, &name, &self.cargo_output)?;
+ }
+
+ return Ok(());
+ }
+
+ // Limit our parallelism globally with a jobserver.
+ let tokens = parallel::job_token::ActiveJobTokenServer::new()?;
// When compiling objects in parallel we do a few dirty tricks to speed
// things up:
@@ -1222,153 +1430,154 @@ impl Build {
// Note that this jobserver is cached globally so we only used one per
// process and only worry about creating it once.
//
- // * Next we use a raw `thread::spawn` per thread to actually compile
- // objects in parallel. We only actually spawn a thread after we've
- // acquired a token to perform some work
- //
- // * Finally though we want to keep the dependencies of this crate
- // pretty light, so we avoid using a safe abstraction like `rayon` and
- // instead rely on some bits of `unsafe` code. We know that this stack
- // frame persists while everything is compiling so we use all the
- // stack-allocated objects without cloning/reallocating. We use a
- // transmute to `State` with a `'static` lifetime to persist
- // everything we need across the boundary, and the join-on-drop
- // semantics of `JoinOnDrop` should ensure that our stack frame is
- // alive while threads are alive.
+ // * Next we use spawn the process to actually compile objects in
+ // parallel after we've acquired a token to perform some work
//
// With all that in mind we compile all objects in a loop here, after we
// acquire the appropriate tokens, Once all objects have been compiled
- // we join on all the threads and propagate the results of compilation.
- //
- // Note that as a slight optimization we try to break out as soon as
- // possible as soon as any compilation fails to ensure that errors get
- // out to the user as fast as possible.
- let error = AtomicBool::new(false);
- let mut threads = Vec::new();
- for obj in objs {
- if error.load(SeqCst) {
- break;
- }
- let token = server.acquire()?;
- let state = State {
- build: self,
- obj,
- error: &error,
- };
- let state = unsafe { std::mem::transmute::<State, State<'static>>(state) };
- let thread = thread::spawn(|| {
- let state: State<'me> = state; // erase the `'static` lifetime
- let result = state.build.compile_object(state.obj);
- if result.is_err() {
- state.error.store(true, SeqCst);
+ // we wait on all the processes and propagate the results of compilation.
+
+ let pendings = Cell::new(Vec::<(
+ Command,
+ String,
+ KillOnDrop,
+ parallel::job_token::JobToken,
+ )>::new());
+ let is_disconnected = Cell::new(false);
+ let has_made_progress = Cell::new(false);
+
+ let wait_future = async {
+ let mut error = None;
+ // Buffer the stdout
+ let mut stdout = io::BufWriter::with_capacity(128, io::stdout());
+
+ loop {
+ // If the other end of the pipe is already disconnected, then we're not gonna get any new jobs,
+ // so it doesn't make sense to reuse the tokens; in fact,
+ // releasing them as soon as possible (once we know that the other end is disconnected) is beneficial.
+ // Imagine that the last file built takes an hour to finish; in this scenario,
+ // by not releasing the tokens before that last file is done we would effectively block other processes from
+ // starting sooner - even though we only need one token for that last file, not N others that were acquired.
+
+ let mut pendings_is_empty = false;
+
+ cell_update(&pendings, |mut pendings| {
+ // Try waiting on them.
+ parallel::retain_unordered_mut(
+ &mut pendings,
+ |(cmd, program, child, _token)| {
+ match try_wait_on_child(
+ cmd,
+ program,
+ &mut child.0,
+ &mut stdout,
+ &mut child.1,
+ ) {
+ Ok(Some(())) => {
+ // Task done, remove the entry
+ has_made_progress.set(true);
+ false
+ }
+ Ok(None) => true, // Task still not finished, keep the entry
+ Err(err) => {
+ // Task fail, remove the entry.
+ // Since we can only return one error, log the error to make
+ // sure users always see all the compilation failures.
+ has_made_progress.set(true);
+
+ if self.cargo_output.warnings {
+ let _ = writeln!(stdout, "cargo:warning={}", err);
+ }
+ error = Some(err);
+
+ false
+ }
+ }
+ },
+ );
+ pendings_is_empty = pendings.is_empty();
+ pendings
+ });
+
+ if pendings_is_empty && is_disconnected.get() {
+ break if let Some(err) = error {
+ Err(err)
+ } else {
+ Ok(())
+ };
}
- drop(token); // make sure our jobserver token is released after the compile
- return result;
- });
- threads.push(JoinOnDrop(Some(thread)));
- }
- for mut thread in threads {
- if let Some(thread) = thread.0.take() {
- thread.join().expect("thread should not panic")?;
+ YieldOnce::default().await;
}
- }
-
- // Reacquire our process's token before we proceed, which we released
- // before entering the loop above.
- if reacquire {
- server.acquire_raw()?;
- }
-
- return Ok(());
-
- /// Shared state from the parent thread to the child thread. This
- /// package of pointers is temporarily transmuted to a `'static`
- /// lifetime to cross the thread boundary and then once the thread is
- /// running we erase the `'static` to go back to an anonymous lifetime.
- struct State<'a> {
- build: &'a Build,
- obj: &'a Object,
- error: &'a AtomicBool,
- }
-
- /// Returns a suitable `jobserver::Client` used to coordinate
- /// parallelism between build scripts.
- fn jobserver() -> &'static jobserver::Client {
- static INIT: Once = Once::new();
- static mut JOBSERVER: Option<jobserver::Client> = None;
-
- fn _assert_sync<T: Sync>() {}
- _assert_sync::<jobserver::Client>();
-
- unsafe {
- INIT.call_once(|| {
- let server = default_jobserver();
- JOBSERVER = Some(server);
+ };
+ let spawn_future = async {
+ for obj in objs {
+ let (mut cmd, program) = self.create_compile_object_cmd(obj)?;
+ let token = tokens.acquire().await?;
+ let mut child = spawn(&mut cmd, &program, &self.cargo_output)?;
+ let mut stderr_forwarder = StderrForwarder::new(&mut child);
+ stderr_forwarder.set_non_blocking()?;
+
+ cell_update(&pendings, |mut pendings| {
+ pendings.push((cmd, program, KillOnDrop(child, stderr_forwarder), token));
+ pendings
});
- JOBSERVER.as_ref().unwrap()
- }
- }
- unsafe fn default_jobserver() -> jobserver::Client {
- // Try to use the environmental jobserver which Cargo typically
- // initializes for us...
- if let Some(client) = jobserver::Client::from_env() {
- return client;
+ has_made_progress.set(true);
}
+ is_disconnected.set(true);
- // ... but if that fails for whatever reason select something
- // reasonable and crate a new jobserver. Use `NUM_JOBS` if set (it's
- // configured by Cargo) and otherwise just fall back to a
- // semi-reasonable number. Note that we could use `num_cpus` here
- // but it's an extra dependency that will almost never be used, so
- // it's generally not too worth it.
- let mut parallelism = 4;
- if let Ok(amt) = env::var("NUM_JOBS") {
- if let Ok(amt) = amt.parse() {
- parallelism = amt;
- }
- }
+ Ok::<_, Error>(())
+ };
- // If we create our own jobserver then be sure to reserve one token
- // for ourselves.
- let client = jobserver::Client::new(parallelism).expect("failed to create jobserver");
- client.acquire_raw().expect("failed to acquire initial");
- return client;
- }
+ return block_on(wait_future, spawn_future, &has_made_progress);
- struct JoinOnDrop(Option<thread::JoinHandle<Result<(), Error>>>);
+ struct KillOnDrop(Child, StderrForwarder);
- impl Drop for JoinOnDrop {
+ impl Drop for KillOnDrop {
fn drop(&mut self) {
- if let Some(thread) = self.0.take() {
- drop(thread.join());
- }
+ let child = &mut self.0;
+
+ child.kill().ok();
}
}
+
+ fn cell_update<T, F>(cell: &Cell<T>, f: F)
+ where
+ T: Default,
+ F: FnOnce(T) -> T,
+ {
+ let old = cell.take();
+ let new = f(old);
+ cell.set(new);
+ }
}
#[cfg(not(feature = "parallel"))]
fn compile_objects(&self, objs: &[Object]) -> Result<(), Error> {
for obj in objs {
- self.compile_object(obj)?;
+ let (mut cmd, name) = self.create_compile_object_cmd(obj)?;
+ run(&mut cmd, &name, &self.cargo_output)?;
}
+
Ok(())
}
- fn compile_object(&self, obj: &Object) -> Result<(), Error> {
+ fn create_compile_object_cmd(&self, obj: &Object) -> Result<(Command, String), Error> {
let asm_ext = AsmFileExt::from_path(&obj.src);
let is_asm = asm_ext.is_some();
let target = self.get_target()?;
let msvc = target.contains("msvc");
let compiler = self.try_get_compiler()?;
let clang = compiler.family == ToolFamily::Clang;
+ let gnu = compiler.family == ToolFamily::Gnu;
- let (mut cmd, name) = if msvc && asm_ext == Some(AsmFileExt::DotAsm) {
+ let is_assembler_msvc = msvc && asm_ext == Some(AsmFileExt::DotAsm);
+ let (mut cmd, name) = if is_assembler_msvc {
self.msvc_macro_assembler()?
} else {
let mut cmd = compiler.to_command();
- for &(ref a, ref b) in self.env.iter() {
+ for (a, b) in self.env.iter() {
cmd.env(a, b);
}
(
@@ -1382,18 +1591,20 @@ impl Build {
)
};
let is_arm = target.contains("aarch64") || target.contains("arm");
- command_add_output_file(&mut cmd, &obj.dst, self.cuda, msvc, clang, is_asm, is_arm);
+ command_add_output_file(
+ &mut cmd, &obj.dst, self.cuda, msvc, clang, gnu, is_asm, is_arm,
+ );
// armasm and armasm64 don't requrie -c option
- if !msvc || !is_asm || !is_arm {
+ if !is_assembler_msvc || !is_arm {
cmd.arg("-c");
}
if self.cuda && self.cuda_file_count() > 1 {
cmd.arg("--device-c");
}
if is_asm {
- cmd.args(&self.asm_flags);
+ cmd.args(self.asm_flags.iter().map(std::ops::Deref::deref));
}
- if compiler.family == (ToolFamily::Msvc { clang_cl: true }) && !is_asm {
+ if compiler.family == (ToolFamily::Msvc { clang_cl: true }) && !is_assembler_msvc {
// #513: For `clang-cl`, separate flags/options from the input file.
// When cross-compiling macOS -> Windows, this avoids interpreting
// common `/Users/...` paths as the `/U` flag and triggering
@@ -1405,15 +1616,14 @@ impl Build {
self.fix_env_for_apple_os(&mut cmd)?;
}
- run(&mut cmd, &name)?;
- Ok(())
+ Ok((cmd, name))
}
- /// This will return a result instead of panicing; see expand() for the complete description.
+ /// This will return a result instead of panicking; see expand() for the complete description.
pub fn try_expand(&self) -> Result<Vec<u8>, Error> {
let compiler = self.try_get_compiler()?;
let mut cmd = compiler.to_command();
- for &(ref a, ref b) in self.env.iter() {
+ for (a, b) in self.env.iter() {
cmd.env(a, b);
}
cmd.arg("-E");
@@ -1423,10 +1633,23 @@ impl Build {
"Expand may only be called for a single file"
);
- for file in self.files.iter() {
- cmd.arg(file);
+ let is_asm = self
+ .files
+ .iter()
+ .map(std::ops::Deref::deref)
+ .find_map(AsmFileExt::from_path)
+ .is_some();
+
+ if compiler.family == (ToolFamily::Msvc { clang_cl: true }) && !is_asm {
+ // #513: For `clang-cl`, separate flags/options from the input file.
+ // When cross-compiling macOS -> Windows, this avoids interpreting
+ // common `/Users/...` paths as the `/U` flag and triggering
+ // `-Wslash-u-filename` warning.
+ cmd.arg("--");
}
+ cmd.args(self.files.iter().map(std::ops::Deref::deref));
+
let name = compiler
.path
.file_name()
@@ -1434,7 +1657,7 @@ impl Build {
.to_string_lossy()
.into_owned();
- Ok(run_output(&mut cmd, &name)?)
+ Ok(run_output(&mut cmd, &name, &self.cargo_output)?)
}
/// Run the compiler, returning the macro-expanded version of the input files.
@@ -1483,13 +1706,13 @@ impl Build {
/// Get the compiler that's in use for this configuration.
///
- /// This will return a result instead of panicing; see get_compiler() for the complete description.
+ /// This will return a result instead of panicking; see
+ /// [`get_compiler()`](Self::get_compiler) for the complete description.
pub fn try_get_compiler(&self) -> Result<Tool, Error> {
let opt_level = self.get_opt_level()?;
let target = self.get_target()?;
let mut cmd = self.get_base_compiler()?;
- let envflags = self.envflags(if self.cpp { "CXXFLAGS" } else { "CFLAGS" });
// Disable default flag generation via `no_default_flags` or environment variable
let no_defaults = self.no_default_flags || self.getenv("CRATE_CC_NO_DEFAULTS").is_some();
@@ -1500,13 +1723,23 @@ impl Build {
println!("Info: default compiler flags are disabled");
}
- for arg in envflags {
- cmd.push_cc_arg(arg.into());
+ if let Some(ref std) = self.std {
+ let separator = match cmd.family {
+ ToolFamily::Msvc { .. } => ':',
+ ToolFamily::Gnu | ToolFamily::Clang => '=',
+ };
+ cmd.push_cc_arg(format!("-std{}{}", separator, std).into());
+ }
+
+ if let Ok(flags) = self.envflags(if self.cpp { "CXXFLAGS" } else { "CFLAGS" }) {
+ for arg in flags {
+ cmd.push_cc_arg(arg.into());
+ }
}
for directory in self.include_directories.iter() {
cmd.args.push("-I".into());
- cmd.args.push(directory.into());
+ cmd.args.push(directory.as_os_str().into());
}
// If warnings and/or extra_warnings haven't been explicitly set,
@@ -1514,34 +1747,28 @@ impl Build {
// CFLAGS/CXXFLAGS, since those variables presumably already contain
// the desired set of warnings flags.
- if self
- .warnings
- .unwrap_or(if self.has_flags() { false } else { true })
- {
+ if self.warnings.unwrap_or(!self.has_flags()) {
let wflags = cmd.family.warnings_flags().into();
cmd.push_cc_arg(wflags);
}
- if self
- .extra_warnings
- .unwrap_or(if self.has_flags() { false } else { true })
- {
+ if self.extra_warnings.unwrap_or(!self.has_flags()) {
if let Some(wflags) = cmd.family.extra_warnings_flags() {
cmd.push_cc_arg(wflags.into());
}
}
for flag in self.flags.iter() {
- cmd.args.push(flag.into());
+ cmd.args.push((**flag).into());
}
for flag in self.flags_supported.iter() {
if self.is_flag_supported(flag).unwrap_or(false) {
- cmd.push_cc_arg(flag.into());
+ cmd.push_cc_arg((**flag).into());
}
}
- for &(ref key, ref value) in self.definitions.iter() {
+ for (key, value) in self.definitions.iter() {
if let Some(ref value) = *value {
cmd.args.push(format!("-D{}={}", key, value).into());
} else {
@@ -1573,9 +1800,8 @@ impl Build {
Some(true) => "-MT",
Some(false) => "-MD",
None => {
- let features = self
- .getenv("CARGO_CFG_TARGET_FEATURE")
- .unwrap_or(String::new());
+ let features = self.getenv("CARGO_CFG_TARGET_FEATURE");
+ let features = features.as_deref().unwrap_or_default();
if features.contains("crt-static") {
"-MT"
} else {
@@ -1602,6 +1828,13 @@ impl Build {
cmd.push_opt_unless_duplicate(format!("-O{}", opt_level).into());
}
+ if cmd.family == ToolFamily::Clang && target.contains("windows") {
+ // Disambiguate mingw and msvc on Windows. Problem is that
+ // depending on the origin clang can default to a mismatchig
+ // run-time.
+ cmd.push_cc_arg(format!("--target={}", target).into());
+ }
+
if cmd.family == ToolFamily::Clang && target.contains("android") {
// For compatibility with code that doesn't use pre-defined `__ANDROID__` macro.
// If compiler used via ndk-build or cmake (officially supported build methods)
@@ -1611,7 +1844,10 @@ impl Build {
cmd.push_opt_unless_duplicate("-DANDROID".into());
}
- if !target.contains("apple-ios") && !target.contains("apple-watchos") {
+ if !target.contains("apple-ios")
+ && !target.contains("apple-watchos")
+ && !target.contains("apple-tvos")
+ {
cmd.push_cc_arg("-ffunction-sections".into());
cmd.push_cc_arg("-fdata-sections".into());
}
@@ -1645,55 +1881,50 @@ impl Build {
family.add_force_frame_pointer(cmd);
}
+ if !cmd.is_like_msvc() {
+ if target.contains("i686") || target.contains("i586") {
+ cmd.args.push("-m32".into());
+ } else if target == "x86_64-unknown-linux-gnux32" {
+ cmd.args.push("-mx32".into());
+ } else if target.contains("x86_64") || target.contains("powerpc64") {
+ cmd.args.push("-m64".into());
+ }
+ }
+
// Target flags
+ if target.contains("-apple-") {
+ self.apple_flags(cmd, target)?;
+ } else {
+ self.target_flags(cmd, target);
+ }
+
+ if self.static_flag.unwrap_or(false) {
+ cmd.args.push("-static".into());
+ }
+ if self.shared_flag.unwrap_or(false) {
+ cmd.args.push("-shared".into());
+ }
+
+ if self.cpp {
+ match (self.cpp_set_stdlib.as_ref(), cmd.family) {
+ (None, _) => {}
+ (Some(stdlib), ToolFamily::Gnu) | (Some(stdlib), ToolFamily::Clang) => {
+ cmd.push_cc_arg(format!("-stdlib=lib{}", stdlib).into());
+ }
+ _ => {
+ self.cargo_output.print_warning(&format_args!("cpp_set_stdlib is specified, but the {:?} compiler does not support this option, ignored", cmd.family));
+ }
+ }
+ }
+
+ Ok(())
+ }
+
+ fn target_flags(&self, cmd: &mut Tool, target: &str) {
match cmd.family {
ToolFamily::Clang => {
- if !(target.contains("android")
- && android_clang_compiler_uses_target_arg_internally(&cmd.path))
- {
- if target.contains("darwin") {
- if let Some(arch) =
- map_darwin_target_from_rust_to_compiler_architecture(target)
- {
- cmd.args
- .push(format!("--target={}-apple-darwin", arch).into());
- }
- } else if target.contains("macabi") {
- if let Some(arch) =
- map_darwin_target_from_rust_to_compiler_architecture(target)
- {
- cmd.args
- .push(format!("--target={}-apple-ios-macabi", arch).into());
- }
- } else if target.contains("ios-sim") {
- if let Some(arch) =
- map_darwin_target_from_rust_to_compiler_architecture(target)
- {
- let deployment_target = env::var("IPHONEOS_DEPLOYMENT_TARGET")
- .unwrap_or_else(|_| "7.0".into());
- cmd.args.push(
- format!(
- "--target={}-apple-ios{}-simulator",
- arch, deployment_target
- )
- .into(),
- );
- }
- } else if target.contains("watchos-sim") {
- if let Some(arch) =
- map_darwin_target_from_rust_to_compiler_architecture(target)
- {
- let deployment_target = env::var("WATCHOS_DEPLOYMENT_TARGET")
- .unwrap_or_else(|_| "5.0".into());
- cmd.args.push(
- format!(
- "--target={}-apple-watchos{}-simulator",
- arch, deployment_target
- )
- .into(),
- );
- }
- } else if target.starts_with("riscv64gc-") {
+ if !(target.contains("android") && cmd.has_internal_target_arg) {
+ if target.starts_with("riscv64gc-") {
cmd.args.push(
format!("--target={}", target.replace("riscv64gc", "riscv64")).into(),
);
@@ -1709,6 +1940,30 @@ impl Build {
} else if target.contains("aarch64") {
cmd.args.push("--target=aarch64-unknown-windows-gnu".into())
}
+ } else if target.ends_with("-freebsd") {
+ // FreeBSD only supports C++11 and above when compiling against libc++
+ // (available from FreeBSD 10 onwards). Under FreeBSD, clang uses libc++ by
+ // default on FreeBSD 10 and newer unless `--target` is manually passed to
+ // the compiler, in which case its default behavior differs:
+ // * If --target=xxx-unknown-freebsdX(.Y) is specified and X is greater than
+ // or equal to 10, clang++ uses libc++
+ // * If --target=xxx-unknown-freebsd is specified (without a version),
+ // clang++ cannot assume libc++ is available and reverts to a default of
+ // libstdc++ (this behavior was changed in llvm 14).
+ //
+ // This breaks C++11 (or greater) builds if targeting FreeBSD with the
+ // generic xxx-unknown-freebsd triple on clang 13 or below *without*
+ // explicitly specifying that libc++ should be used.
+ // When cross-compiling, we can't infer from the rust/cargo target triple
+ // which major version of FreeBSD we are targeting, so we need to make sure
+ // that libc++ is used (unless the user has explicitly specified otherwise).
+ // There's no compelling reason to use a different approach when compiling
+ // natively.
+ if self.cpp && self.cpp_set_stdlib.is_none() {
+ cmd.push_cc_arg("-stdlib=libc++".into());
+ }
+
+ cmd.push_cc_arg(format!("--target={}", target).into());
} else {
cmd.push_cc_arg(format!("--target={}", target).into());
}
@@ -1732,6 +1987,8 @@ impl Build {
} else {
if target.contains("i586") {
cmd.push_cc_arg("-arch:IA32".into());
+ } else if target.contains("arm64ec") {
+ cmd.push_cc_arg("-arm64EC".into());
}
}
@@ -1750,30 +2007,13 @@ impl Build {
}
}
ToolFamily::Gnu => {
- if target.contains("i686") || target.contains("i586") {
- cmd.args.push("-m32".into());
- } else if target == "x86_64-unknown-linux-gnux32" {
- cmd.args.push("-mx32".into());
- } else if target.contains("x86_64") || target.contains("powerpc64") {
- cmd.args.push("-m64".into());
- }
-
- if target.contains("darwin") {
- if let Some(arch) = map_darwin_target_from_rust_to_compiler_architecture(target)
- {
- cmd.args.push("-arch".into());
- cmd.args.push(arch.into());
- }
- }
-
if target.contains("-kmc-solid_") {
cmd.args.push("-finput-charset=utf-8".into());
}
if self.static_flag.is_none() {
- let features = self
- .getenv("CARGO_CFG_TARGET_FEATURE")
- .unwrap_or(String::new());
+ let features = self.getenv("CARGO_CFG_TARGET_FEATURE");
+ let features = features.as_deref().unwrap_or_default();
if features.contains("crt-static") {
cmd.args.push("-static".into());
}
@@ -1927,64 +2167,38 @@ impl Build {
let mut parts = target.split('-');
if let Some(arch) = parts.next() {
let arch = &arch[5..];
- if target.contains("linux") && arch.starts_with("64") {
- cmd.args.push(("-march=rv64gc").into());
- cmd.args.push("-mabi=lp64d".into());
- } else if target.contains("freebsd") && arch.starts_with("64") {
- cmd.args.push(("-march=rv64gc").into());
- cmd.args.push("-mabi=lp64d".into());
- } else if target.contains("openbsd") && arch.starts_with("64") {
- cmd.args.push(("-march=rv64gc").into());
- cmd.args.push("-mabi=lp64d".into());
- } else if target.contains("linux") && arch.starts_with("32") {
- cmd.args.push(("-march=rv32gc").into());
- cmd.args.push("-mabi=ilp32d".into());
- } else if arch.starts_with("64") {
- cmd.args.push(("-march=rv".to_owned() + arch).into());
- cmd.args.push("-mabi=lp64".into());
+ if arch.starts_with("64") {
+ if target.contains("linux")
+ | target.contains("freebsd")
+ | target.contains("netbsd")
+ | target.contains("linux")
+ {
+ cmd.args.push(("-march=rv64gc").into());
+ cmd.args.push("-mabi=lp64d".into());
+ } else {
+ cmd.args.push(("-march=rv".to_owned() + arch).into());
+ cmd.args.push("-mabi=lp64".into());
+ }
+ } else if arch.starts_with("32") {
+ if target.contains("linux") {
+ cmd.args.push(("-march=rv32gc").into());
+ cmd.args.push("-mabi=ilp32d".into());
+ } else {
+ cmd.args.push(("-march=rv".to_owned() + arch).into());
+ cmd.args.push("-mabi=ilp32".into());
+ }
} else {
- cmd.args.push(("-march=rv".to_owned() + arch).into());
- cmd.args.push("-mabi=ilp32".into());
+ cmd.args.push("-mcmodel=medany".into());
}
- cmd.args.push("-mcmodel=medany".into());
}
}
}
}
-
- if target.contains("apple-ios") || target.contains("apple-watchos") {
- self.ios_watchos_flags(cmd)?;
- }
-
- if self.static_flag.unwrap_or(false) {
- cmd.args.push("-static".into());
- }
- if self.shared_flag.unwrap_or(false) {
- cmd.args.push("-shared".into());
- }
-
- if self.cpp {
- match (self.cpp_set_stdlib.as_ref(), cmd.family) {
- (None, _) => {}
- (Some(stdlib), ToolFamily::Gnu) | (Some(stdlib), ToolFamily::Clang) => {
- cmd.push_cc_arg(format!("-stdlib=lib{}", stdlib).into());
- }
- _ => {
- println!(
- "cargo:warning=cpp_set_stdlib is specified, but the {:?} compiler \
- does not support this option, ignored",
- cmd.family
- );
- }
- }
- }
-
- Ok(())
}
fn has_flags(&self) -> bool {
let flags_env_var_name = if self.cpp { "CXXFLAGS" } else { "CFLAGS" };
- let flags_env_var_value = self.get_var(flags_env_var_name);
+ let flags_env_var_value = self.getenv_with_target_prefixes(flags_env_var_name);
if let Ok(_) = flags_env_var_value {
true
} else {
@@ -2006,20 +2220,33 @@ impl Build {
let mut cmd = windows_registry::find(&target, tool).unwrap_or_else(|| self.cmd(tool));
cmd.arg("-nologo"); // undocumented, yet working with armasm[64]
for directory in self.include_directories.iter() {
- cmd.arg("-I").arg(directory);
+ cmd.arg("-I").arg(&**directory);
}
if target.contains("aarch64") || target.contains("arm") {
if self.get_debug() {
cmd.arg("-g");
}
- println!("cargo:warning=The MSVC ARM assemblers do not support -D flags");
+ for (key, value) in self.definitions.iter() {
+ cmd.arg("-PreDefine");
+ if let Some(ref value) = *value {
+ if let Ok(i) = value.parse::<i32>() {
+ cmd.arg(&format!("{} SETA {}", key, i));
+ } else if value.starts_with('"') && value.ends_with('"') {
+ cmd.arg(&format!("{} SETS {}", key, value));
+ } else {
+ cmd.arg(&format!("{} SETS \"{}\"", key, value));
+ }
+ } else {
+ cmd.arg(&format!("{} SETL {}", key, "{TRUE}"));
+ }
+ }
} else {
if self.get_debug() {
cmd.arg("-Zi");
}
- for &(ref key, ref value) in self.definitions.iter() {
+ for (key, value) in self.definitions.iter() {
if let Some(ref value) = *value {
cmd.arg(&format!("-D{}={}", key, value));
} else {
@@ -2031,9 +2258,6 @@ impl Build {
if target.contains("i686") || target.contains("i586") {
cmd.arg("-safeseh");
}
- for flag in self.flags.iter() {
- cmd.arg(flag);
- }
Ok((cmd, tool.to_string()))
}
@@ -2041,15 +2265,15 @@ impl Build {
fn assemble(&self, lib_name: &str, dst: &Path, objs: &[Object]) -> Result<(), Error> {
// Delete the destination if it exists as we want to
// create on the first iteration instead of appending.
- let _ = fs::remove_file(&dst);
+ let _ = fs::remove_file(dst);
// Add objects to the archive in limited-length batches. This helps keep
// the length of the command line within a reasonable length to avoid
// blowing system limits on limiting platforms like Windows.
let objs: Vec<_> = objs
.iter()
- .map(|o| o.dst.clone())
- .chain(self.objects.clone())
+ .map(|o| o.dst.as_path())
+ .chain(self.objects.iter().map(std::ops::Deref::deref))
.collect();
for chunk in objs.chunks(100) {
self.assemble_progressive(dst, chunk)?;
@@ -2062,12 +2286,9 @@ impl Build {
let out_dir = self.get_out_dir()?;
let dlink = out_dir.join(lib_name.to_owned() + "_dlink.o");
let mut nvcc = self.get_compiler().to_command();
- nvcc.arg("--device-link")
- .arg("-o")
- .arg(dlink.clone())
- .arg(dst);
- run(&mut nvcc, "nvcc")?;
- self.assemble_progressive(dst, &[dlink])?;
+ nvcc.arg("--device-link").arg("-o").arg(&dlink).arg(dst);
+ run(&mut nvcc, "nvcc", &self.cargo_output)?;
+ self.assemble_progressive(dst, &[dlink.as_path()])?;
}
let target = self.get_target()?;
@@ -2078,9 +2299,9 @@ impl Build {
let lib_dst = dst.with_file_name(format!("{}.lib", lib_name));
let _ = fs::remove_file(&lib_dst);
- match fs::hard_link(&dst, &lib_dst).or_else(|_| {
+ match fs::hard_link(dst, &lib_dst).or_else(|_| {
// if hard-link fails, just copy (ignoring the number of bytes written)
- fs::copy(&dst, &lib_dst).map(|_| ())
+ fs::copy(dst, &lib_dst).map(|_| ())
}) {
Ok(_) => (),
Err(_) => {
@@ -2094,23 +2315,31 @@ impl Build {
// Non-msvc targets (those using `ar`) need a separate step to add
// the symbol table to archives since our construction command of
// `cq` doesn't add it for us.
- let (mut ar, cmd) = self.get_ar()?;
- run(ar.arg("s").arg(dst), &cmd)?;
+ let (mut ar, cmd, _any_flags) = self.get_ar()?;
+
+ // NOTE: We add `s` even if flags were passed using $ARFLAGS/ar_flag, because `s`
+ // here represents a _mode_, not an arbitrary flag. Further discussion of this choice
+ // can be seen in https://github.com/rust-lang/cc-rs/pull/763.
+ run(ar.arg("s").arg(dst), &cmd, &self.cargo_output)?;
}
Ok(())
}
- fn assemble_progressive(&self, dst: &Path, objs: &[PathBuf]) -> Result<(), Error> {
+ fn assemble_progressive(&self, dst: &Path, objs: &[&Path]) -> Result<(), Error> {
let target = self.get_target()?;
if target.contains("msvc") {
- let (mut cmd, program) = self.get_ar()?;
+ let (mut cmd, program, any_flags) = self.get_ar()?;
+ // NOTE: -out: here is an I/O flag, and so must be included even if $ARFLAGS/ar_flag is
+ // in use. -nologo on the other hand is just a regular flag, and one that we'll skip if
+ // the caller has explicitly dictated the flags they want. See
+ // https://github.com/rust-lang/cc-rs/pull/763 for further discussion.
let mut out = OsString::from("-out:");
out.push(dst);
- cmd.arg(out).arg("-nologo");
- for flag in self.ar_flags.iter() {
- cmd.arg(flag);
+ cmd.arg(out);
+ if !any_flags {
+ cmd.arg("-nologo");
}
// If the library file already exists, add the library name
// as an argument to let lib.exe know we are appending the objs.
@@ -2118,9 +2347,9 @@ impl Build {
cmd.arg(dst);
}
cmd.args(objs);
- run(&mut cmd, &program)?;
+ run(&mut cmd, &program, &self.cargo_output)?;
} else {
- let (mut ar, cmd) = self.get_ar()?;
+ let (mut ar, cmd, _any_flags) = self.get_ar()?;
// Set an environment variable to tell the OSX archiver to ensure
// that all dates listed in the archive are zero, improving
@@ -2145,46 +2374,35 @@ impl Build {
// In any case if this doesn't end up getting read, it shouldn't
// cause that many issues!
ar.env("ZERO_AR_DATE", "1");
- for flag in self.ar_flags.iter() {
- ar.arg(flag);
- }
- run(ar.arg("cq").arg(dst).args(objs), &cmd)?;
+
+ // NOTE: We add cq here regardless of whether $ARFLAGS/ar_flag have been used because
+ // it dictates the _mode_ ar runs in, which the setter of $ARFLAGS/ar_flag can't
+ // dictate. See https://github.com/rust-lang/cc-rs/pull/763 for further discussion.
+ run(ar.arg("cq").arg(dst).args(objs), &cmd, &self.cargo_output)?;
}
Ok(())
}
- fn ios_watchos_flags(&self, cmd: &mut Tool) -> Result<(), Error> {
- enum ArchSpec {
- Device(&'static str),
- Simulator(&'static str),
- Catalyst(&'static str),
- }
-
- enum Os {
- Ios,
- WatchOs,
- }
- impl Display for Os {
- fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
- match self {
- Os::Ios => f.write_str("iOS"),
- Os::WatchOs => f.write_str("WatchOS"),
- }
- }
- }
-
- let target = self.get_target()?;
- let os = if target.contains("-watchos") {
- Os::WatchOs
+ fn apple_flags(&self, cmd: &mut Tool, target: &str) -> Result<(), Error> {
+ let os = if target.contains("-darwin") {
+ AppleOs::MacOs
+ } else if target.contains("-watchos") {
+ AppleOs::WatchOs
+ } else if target.contains("-tvos") {
+ AppleOs::TvOs
} else {
- Os::Ios
+ AppleOs::Ios
+ };
+ let is_mac = match os {
+ AppleOs::MacOs => true,
+ _ => false,
};
- let arch = target.split('-').nth(0).ok_or_else(|| {
+ let arch_str = target.split('-').nth(0).ok_or_else(|| {
Error::new(
ErrorKind::ArchitectureInvalid,
- format!("Unknown architecture for {} target.", os).as_str(),
+ format!("Unknown architecture for {:?} target.", os),
)
})?;
@@ -2193,16 +2411,27 @@ impl Build {
None => false,
};
- let is_sim = match target.split('-').nth(3) {
+ let is_arm_sim = match target.split('-').nth(3) {
Some(v) => v == "sim",
None => false,
};
- let arch = if is_catalyst {
- match arch {
- "arm64e" => ArchSpec::Catalyst("arm64e"),
- "arm64" | "aarch64" => ArchSpec::Catalyst("arm64"),
- "x86_64" => ArchSpec::Catalyst("-m64"),
+ let arch = if is_mac {
+ match arch_str {
+ "i686" => AppleArchSpec::Device("-m32"),
+ "x86_64" | "x86_64h" | "aarch64" => AppleArchSpec::Device("-m64"),
+ _ => {
+ return Err(Error::new(
+ ErrorKind::ArchitectureInvalid,
+ "Unknown architecture for macOS target.",
+ ));
+ }
+ }
+ } else if is_catalyst {
+ match arch_str {
+ "arm64e" => AppleArchSpec::Catalyst("arm64e"),
+ "arm64" | "aarch64" => AppleArchSpec::Catalyst("arm64"),
+ "x86_64" | "x86_64h" => AppleArchSpec::Catalyst("-m64"),
_ => {
return Err(Error::new(
ErrorKind::ArchitectureInvalid,
@@ -2210,105 +2439,192 @@ impl Build {
));
}
}
- } else if is_sim {
- match arch {
- "arm64" | "aarch64" => ArchSpec::Simulator("-arch arm64"),
- "x86_64" => ArchSpec::Simulator("-m64"),
+ } else if is_arm_sim {
+ match arch_str {
+ "arm64" | "aarch64" => AppleArchSpec::Simulator("arm64"),
+ "x86_64" | "x86_64h" => AppleArchSpec::Simulator("-m64"),
_ => {
return Err(Error::new(
ErrorKind::ArchitectureInvalid,
- "Unknown architecture for iOS simulator target.",
+ "Unknown architecture for simulator target.",
));
}
}
} else {
- match arch {
- "arm" | "armv7" | "thumbv7" => ArchSpec::Device("armv7"),
- "armv7k" => ArchSpec::Device("armv7k"),
- "armv7s" | "thumbv7s" => ArchSpec::Device("armv7s"),
- "arm64e" => ArchSpec::Device("arm64e"),
- "arm64" | "aarch64" => ArchSpec::Device("arm64"),
- "arm64_32" => ArchSpec::Device("arm64_32"),
- "i386" | "i686" => ArchSpec::Simulator("-m32"),
- "x86_64" => ArchSpec::Simulator("-m64"),
+ match arch_str {
+ "arm" | "armv7" | "thumbv7" => AppleArchSpec::Device("armv7"),
+ "armv7k" => AppleArchSpec::Device("armv7k"),
+ "armv7s" | "thumbv7s" => AppleArchSpec::Device("armv7s"),
+ "arm64e" => AppleArchSpec::Device("arm64e"),
+ "arm64" | "aarch64" => AppleArchSpec::Device("arm64"),
+ "arm64_32" => AppleArchSpec::Device("arm64_32"),
+ "i386" | "i686" => AppleArchSpec::Simulator("-m32"),
+ "x86_64" | "x86_64h" => AppleArchSpec::Simulator("-m64"),
_ => {
return Err(Error::new(
ErrorKind::ArchitectureInvalid,
- format!("Unknown architecture for {} target.", os).as_str(),
+ format!("Unknown architecture for {:?} target.", os),
));
}
}
};
- let (sdk_prefix, sim_prefix, min_version) = match os {
- Os::Ios => (
- "iphone",
- "ios-",
- std::env::var("IPHONEOS_DEPLOYMENT_TARGET").unwrap_or_else(|_| "7.0".into()),
- ),
- Os::WatchOs => (
- "watch",
- "watch",
- std::env::var("WATCHOS_DEPLOYMENT_TARGET").unwrap_or_else(|_| "2.0".into()),
- ),
- };
+ let sdk_details = apple_os_sdk_parts(os, &arch);
+ let min_version = self.apple_deployment_version(os, Some(arch_str), &sdk_details.sdk);
- let sdk = match arch {
- ArchSpec::Device(arch) => {
- cmd.args.push("-arch".into());
- cmd.args.push(arch.into());
+ match arch {
+ AppleArchSpec::Device(_) if is_mac => {
cmd.args
- .push(format!("-m{}os-version-min={}", sdk_prefix, min_version).into());
- format!("{}os", sdk_prefix)
+ .push(format!("-mmacosx-version-min={}", min_version).into());
}
- ArchSpec::Simulator(arch) => {
+ AppleArchSpec::Device(arch) => {
+ cmd.args.push("-arch".into());
cmd.args.push(arch.into());
- cmd.args
- .push(format!("-m{}simulator-version-min={}", sim_prefix, min_version).into());
- format!("{}simulator", sdk_prefix)
+ cmd.args.push(
+ format!("-m{}os-version-min={}", sdk_details.sdk_prefix, min_version).into(),
+ );
}
- ArchSpec::Catalyst(_) => "macosx".to_owned(),
+ AppleArchSpec::Simulator(arch) => {
+ if arch.starts_with('-') {
+ // -m32 or -m64
+ cmd.args.push(arch.into());
+ } else {
+ cmd.args.push("-arch".into());
+ cmd.args.push(arch.into());
+ }
+ cmd.args.push(
+ format!(
+ "-m{}simulator-version-min={}",
+ sdk_details.sim_prefix, min_version
+ )
+ .into(),
+ );
+ }
+ AppleArchSpec::Catalyst(_) => {}
};
- self.print(&format!("Detecting {} SDK path for {}", os, sdk));
- let sdk_path = if let Some(sdkroot) = env::var_os("SDKROOT") {
- sdkroot
- } else {
- self.apple_sdk_root(sdk.as_str())?
- };
+ // AppleClang sometimes requires sysroot even for darwin
+ if cmd.is_xctoolchain_clang() || !target.ends_with("-darwin") {
+ self.cargo_output.print_metadata(&format_args!(
+ "Detecting {:?} SDK path for {}",
+ os, sdk_details.sdk
+ ));
+ let sdk_path = self.apple_sdk_root(&sdk_details.sdk)?;
- cmd.args.push("-isysroot".into());
- cmd.args.push(sdk_path);
- cmd.args.push("-fembed-bitcode".into());
- /*
- * TODO we probably ultimately want the -fembed-bitcode-marker flag
- * but can't have it now because of an issue in LLVM:
- * https://github.com/rust-lang/cc-rs/issues/301
- * https://github.com/rust-lang/rust/pull/48896#comment-372192660
- */
- /*
- if self.get_opt_level()? == "0" {
- cmd.args.push("-fembed-bitcode-marker".into());
- }
- */
+ cmd.args.push("-isysroot".into());
+ cmd.args.push(sdk_path);
+ }
+
+ match cmd.family {
+ ToolFamily::Gnu => {
+ if target.contains("darwin") {
+ if let Some(arch) = map_darwin_target_from_rust_to_compiler_architecture(target)
+ {
+ cmd.args.push("-arch".into());
+ cmd.args.push(arch.into());
+ }
+ }
+ }
+ ToolFamily::Clang => {
+ if target.contains("darwin") {
+ if let Some(arch) = map_darwin_target_from_rust_to_compiler_architecture(target)
+ {
+ cmd.args
+ .push(format!("--target={}-apple-darwin", arch).into());
+ }
+ } else if target.contains("macabi") {
+ if let Some(arch) = map_darwin_target_from_rust_to_compiler_architecture(target)
+ {
+ cmd.args
+ .push(format!("--target={}-apple-ios-macabi", arch).into());
+ }
+ } else if target.contains("ios-sim") {
+ if let Some(arch) = map_darwin_target_from_rust_to_compiler_architecture(target)
+ {
+ cmd.args.push(
+ format!("--target={}-apple-ios{}-simulator", arch, min_version).into(),
+ );
+ }
+ } else if target.contains("watchos-sim") {
+ if let Some(arch) = map_darwin_target_from_rust_to_compiler_architecture(target)
+ {
+ cmd.args.push(
+ format!("--target={}-apple-watchos{}-simulator", arch, min_version)
+ .into(),
+ );
+ }
+ } else if target.contains("tvos-sim") || target.contains("x86_64-apple-tvos") {
+ if let Some(arch) = map_darwin_target_from_rust_to_compiler_architecture(target)
+ {
+ cmd.args.push(
+ format!("--target={}-apple-tvos{}-simulator", arch, min_version).into(),
+ );
+ }
+ } else if target.contains("aarch64-apple-tvos") {
+ if let Some(arch) = map_darwin_target_from_rust_to_compiler_architecture(target)
+ {
+ cmd.args
+ .push(format!("--target={}-apple-tvos{}", arch, min_version).into());
+ }
+ }
+ }
+ _ => unreachable!("unexpected compiler for apple architectures"),
+ }
+
+ if let AppleArchSpec::Catalyst(_) = arch {
+ // Mac Catalyst uses the macOS SDK, but to compile against and
+ // link to iOS-specific frameworks, we should have the support
+ // library stubs in the include and library search path.
+ let sdk_path = self.apple_sdk_root(&sdk_details.sdk)?;
+ let ios_support = PathBuf::from(sdk_path).join("/System/iOSSupport");
+
+ cmd.args.extend([
+ // Header search path
+ OsString::from("-isystem"),
+ ios_support.join("/usr/include").into(),
+ // Framework header search path
+ OsString::from("-iframework"),
+ ios_support.join("/System/Library/Frameworks").into(),
+ // Library search path
+ {
+ let mut s = OsString::from("-L");
+ s.push(&ios_support.join("/usr/lib"));
+ s
+ },
+ // Framework linker search path
+ {
+ // Technically, we _could_ avoid emitting `-F`, as
+ // `-iframework` implies it, but let's keep it in for
+ // clarity.
+ let mut s = OsString::from("-F");
+ s.push(&ios_support.join("/System/Library/Frameworks"));
+ s
+ },
+ ]);
+ }
Ok(())
}
fn cmd<P: AsRef<OsStr>>(&self, prog: P) -> Command {
let mut cmd = Command::new(prog);
- for &(ref a, ref b) in self.env.iter() {
+ for (a, b) in self.env.iter() {
cmd.env(a, b);
}
cmd
}
fn get_base_compiler(&self) -> Result<Tool, Error> {
- if let Some(ref c) = self.compiler {
- return Ok(Tool::new(c.clone()));
+ if let Some(c) = &self.compiler {
+ return Ok(Tool::new(
+ (**c).to_owned(),
+ &self.cached_compiler_family,
+ &self.cargo_output,
+ ));
}
let host = self.get_host()?;
let target = self.get_target()?;
+ let target = &*target;
let (env, msvc, gnu, traditional, clang) = if self.cpp {
("CXX", "cl.exe", "g++", "c++", "clang++")
} else {
@@ -2325,7 +2641,7 @@ impl Build {
traditional
};
- let cl_exe = windows_registry::find_tool(&target, "cl.exe");
+ let cl_exe = windows_registry::find_tool(target, "cl.exe");
let tool_opt: Option<Tool> = self
.env_tool(env)
@@ -2340,7 +2656,12 @@ impl Build {
// semi-buggy build scripts which are shared in
// makefiles/configure scripts (where spaces are far more
// lenient)
- let mut t = Tool::with_clang_driver(PathBuf::from(tool.trim()), driver_mode);
+ let mut t = Tool::with_clang_driver(
+ tool,
+ driver_mode,
+ &self.cached_compiler_family,
+ &self.cargo_output,
+ );
if let Some(cc_wrapper) = wrapper {
t.cc_wrapper_path = Some(PathBuf::from(cc_wrapper));
}
@@ -2354,12 +2675,20 @@ impl Build {
let tool = if self.cpp { "em++" } else { "emcc" };
// Windows uses bat file so we have to be a bit more specific
if cfg!(windows) {
- let mut t = Tool::new(PathBuf::from("cmd"));
+ let mut t = Tool::new(
+ PathBuf::from("cmd"),
+ &self.cached_compiler_family,
+ &self.cargo_output,
+ );
t.args.push("/c".into());
t.args.push(format!("{}.bat", tool).into());
Some(t)
} else {
- Some(Tool::new(PathBuf::from(tool)))
+ Some(Tool::new(
+ PathBuf::from(tool),
+ &self.cached_compiler_family,
+ &self.cargo_output,
+ ))
}
} else {
None
@@ -2377,12 +2706,13 @@ impl Build {
let cc = if target.contains("llvm") { clang } else { gnu };
format!("{}.exe", cc)
}
- } else if target.contains("apple-ios") {
- clang.to_string()
- } else if target.contains("apple-watchos") {
+ } else if target.contains("apple-ios")
+ | target.contains("apple-watchos")
+ | target.contains("apple-tvos")
+ {
clang.to_string()
} else if target.contains("android") {
- autodetect_android_compiler(&target, &host, gnu, clang)
+ autodetect_android_compiler(target, &host, gnu, clang)
} else if target.contains("cloudabi") {
format!("{}-{}", target, traditional)
} else if target == "wasm32-wasi"
@@ -2400,8 +2730,8 @@ impl Build {
format!("arm-kmc-eabi-{}", gnu)
} else if target.starts_with("aarch64-kmc-solid_") {
format!("aarch64-kmc-elf-{}", gnu)
- } else if self.get_host()? != target {
- let prefix = self.prefix_for_target(&target);
+ } else if &*self.get_host()? != target {
+ let prefix = self.prefix_for_target(target);
match prefix {
Some(prefix) => {
let cc = if target.contains("llvm") { clang } else { gnu };
@@ -2413,7 +2743,11 @@ impl Build {
default.to_string()
};
- let mut t = Tool::new(PathBuf::from(compiler));
+ let mut t = Tool::new(
+ PathBuf::from(compiler),
+ &self.cached_compiler_family,
+ &self.cargo_output,
+ );
if let Some(cc_wrapper) = Self::rustc_wrapper_fallback() {
t.cc_wrapper_path = Some(PathBuf::from(cc_wrapper));
}
@@ -2426,11 +2760,17 @@ impl Build {
tool.args.is_empty(),
"CUDA compilation currently assumes empty pre-existing args"
);
- let nvcc = match self.get_var("NVCC") {
- Err(_) => "nvcc".into(),
- Ok(nvcc) => nvcc,
+ let nvcc = match self.getenv_with_target_prefixes("NVCC") {
+ Err(_) => PathBuf::from("nvcc"),
+ Ok(nvcc) => PathBuf::from(&*nvcc),
};
- let mut nvcc_tool = Tool::with_features(PathBuf::from(nvcc), None, self.cuda);
+ let mut nvcc_tool = Tool::with_features(
+ nvcc,
+ None,
+ self.cuda,
+ &self.cached_compiler_family,
+ &self.cargo_output,
+ );
nvcc_tool
.args
.push(format!("-ccbin={}", tool.path.display()).into());
@@ -2455,16 +2795,17 @@ impl Build {
{
if let Some(path) = tool.path.file_name() {
let file_name = path.to_str().unwrap().to_owned();
- let (target, clang) = file_name.split_at(file_name.rfind("-").unwrap());
+ let (target, clang) = file_name.split_at(file_name.rfind('-').unwrap());
- tool.path.set_file_name(clang.trim_start_matches("-"));
+ tool.has_internal_target_arg = true;
+ tool.path.set_file_name(clang.trim_start_matches('-'));
tool.path.set_extension("exe");
tool.args.push(format!("--target={}", target).into());
// Additionally, shell scripts for target i686-linux-android versions 16 to 24
// pass the `mstackrealign` option so we do that here as well.
if target.contains("i686-linux-android") {
- let (_, version) = target.split_at(target.rfind("d").unwrap() + 1);
+ let (_, version) = target.split_at(target.rfind('d').unwrap() + 1);
if let Ok(version) = version.parse::<u32>() {
if version > 15 && version < 25 {
tool.args.push("-mstackrealign".into());
@@ -2489,41 +2830,18 @@ impl Build {
&& tool.env.len() == 0
&& target.contains("msvc")
{
- for &(ref k, ref v) in cl_exe.env.iter() {
+ for (k, v) in cl_exe.env.iter() {
tool.env.push((k.to_owned(), v.to_owned()));
}
}
}
- Ok(tool)
- }
-
- fn get_var(&self, var_base: &str) -> Result<String, Error> {
- let target = self.get_target()?;
- let host = self.get_host()?;
- let kind = if host == target { "HOST" } else { "TARGET" };
- let target_u = target.replace("-", "_");
- let res = self
- .getenv(&format!("{}_{}", var_base, target))
- .or_else(|| self.getenv(&format!("{}_{}", var_base, target_u)))
- .or_else(|| self.getenv(&format!("{}_{}", kind, var_base)))
- .or_else(|| self.getenv(var_base));
-
- match res {
- Some(res) => Ok(res),
- None => Err(Error::new(
- ErrorKind::EnvVarNotFound,
- &format!("Could not find environment variable {}.", var_base),
- )),
+ if target.contains("msvc") && tool.family == ToolFamily::Gnu {
+ self.cargo_output
+ .print_warning(&"GNU compiler is not supported for this target");
}
- }
- fn envflags(&self, name: &str) -> Vec<String> {
- self.get_var(name)
- .unwrap_or(String::new())
- .split_ascii_whitespace()
- .map(|slice| slice.to_string())
- .collect()
+ Ok(tool)
}
/// Returns a fallback `cc_compiler_wrapper` by introspecting `RUSTC_WRAPPER`
@@ -2545,8 +2863,8 @@ impl Build {
}
/// Returns compiler path, optional modifier name from whitelist, and arguments vec
- fn env_tool(&self, name: &str) -> Option<(String, Option<String>, Vec<String>)> {
- let tool = match self.get_var(name) {
+ fn env_tool(&self, name: &str) -> Option<(PathBuf, Option<String>, Vec<String>)> {
+ let tool = match self.getenv_with_target_prefixes(name) {
Ok(tool) => tool,
Err(_) => return None,
};
@@ -2554,8 +2872,12 @@ impl Build {
// If this is an exact path on the filesystem we don't want to do any
// interpretation at all, just pass it on through. This'll hopefully get
// us to support spaces-in-paths.
- if Path::new(&tool).exists() {
- return Some((tool, None, Vec::new()));
+ if Path::new(&*tool).exists() {
+ return Some((
+ PathBuf::from(&*tool),
+ Self::rustc_wrapper_fallback(),
+ Vec::new(),
+ ));
}
// Ok now we want to handle a couple of scenarios. We'll assume from
@@ -2594,7 +2916,7 @@ impl Build {
if known_wrappers.contains(&file_stem) {
if let Some(compiler) = parts.next() {
return Some((
- compiler.to_string(),
+ compiler.into(),
Some(maybe_wrapper.to_string()),
parts.map(|s| s.to_string()).collect(),
));
@@ -2602,36 +2924,37 @@ impl Build {
}
Some((
- maybe_wrapper.to_string(),
+ maybe_wrapper.into(),
Self::rustc_wrapper_fallback(),
parts.map(|s| s.to_string()).collect(),
))
}
/// Returns the C++ standard library:
- /// 1. If [cpp_link_stdlib](cc::Build::cpp_link_stdlib) is set, uses its value.
+ /// 1. If [`cpp_link_stdlib`](cc::Build::cpp_link_stdlib) is set, uses its value.
/// 2. Else if the `CXXSTDLIB` environment variable is set, uses its value.
/// 3. Else the default is `libc++` for OS X and BSDs, `libc++_shared` for Android,
/// `None` for MSVC and `libstdc++` for anything else.
fn get_cpp_link_stdlib(&self) -> Result<Option<String>, Error> {
- match self.cpp_link_stdlib.clone() {
- Some(s) => Ok(s),
+ match &self.cpp_link_stdlib {
+ Some(s) => Ok(s.as_ref().map(|s| (*s).to_string())),
None => {
- if let Ok(stdlib) = self.get_var("CXXSTDLIB") {
+ if let Ok(stdlib) = self.getenv_with_target_prefixes("CXXSTDLIB") {
if stdlib.is_empty() {
Ok(None)
} else {
- Ok(Some(stdlib))
+ Ok(Some(stdlib.to_string()))
}
} else {
let target = self.get_target()?;
if target.contains("msvc") {
Ok(None)
- } else if target.contains("apple") {
- Ok(Some("c++".to_string()))
- } else if target.contains("freebsd") {
- Ok(Some("c++".to_string()))
- } else if target.contains("openbsd") {
+ } else if target.contains("apple")
+ | target.contains("freebsd")
+ | target.contains("openbsd")
+ | target.contains("aix")
+ | target.contains("linux-ohos")
+ {
Ok(Some("c++".to_string()))
} else if target.contains("android") {
Ok(Some("c++_shared".to_string()))
@@ -2643,101 +2966,243 @@ impl Build {
}
}
- fn get_ar(&self) -> Result<(Command, String), Error> {
- if let Some(ref p) = self.archiver {
- let name = p.file_name().and_then(|s| s.to_str()).unwrap_or("ar");
- return Ok((self.cmd(p), name.to_string()));
+ fn get_ar(&self) -> Result<(Command, String, bool), Error> {
+ self.try_get_archiver_and_flags()
+ }
+
+ /// Get the archiver (ar) that's in use for this configuration.
+ ///
+ /// You can use [`Command::get_program`] to get just the path to the command.
+ ///
+ /// This method will take into account all configuration such as debug
+ /// information, optimization level, include directories, defines, etc.
+ /// Additionally, the compiler binary in use follows the standard
+ /// conventions for this path, e.g. looking at the explicitly set compiler,
+ /// environment variables (a number of which are inspected here), and then
+ /// falling back to the default configuration.
+ ///
+ /// # Panics
+ ///
+ /// Panics if an error occurred while determining the architecture.
+ pub fn get_archiver(&self) -> Command {
+ match self.try_get_archiver() {
+ Ok(tool) => tool,
+ Err(e) => fail(&e.message),
}
- if let Ok(p) = self.get_var("AR") {
- return Ok((self.cmd(&p), p));
+ }
+
+ /// Get the archiver that's in use for this configuration.
+ ///
+ /// This will return a result instead of panicking;
+ /// see [`Self::get_archiver`] for the complete description.
+ pub fn try_get_archiver(&self) -> Result<Command, Error> {
+ Ok(self.try_get_archiver_and_flags()?.0)
+ }
+
+ fn try_get_archiver_and_flags(&self) -> Result<(Command, String, bool), Error> {
+ let (mut cmd, name) = self.get_base_archiver()?;
+ let mut any_flags = false;
+ if let Ok(flags) = self.envflags("ARFLAGS") {
+ any_flags = any_flags | !flags.is_empty();
+ cmd.args(flags);
}
- let target = self.get_target()?;
- let default_ar = "ar".to_string();
- let program = if target.contains("android") {
- format!("{}-ar", target.replace("armv7", "arm"))
- } else if target.contains("emscripten") {
- // Windows use bat files so we have to be a bit more specific
- if cfg!(windows) {
- let mut cmd = self.cmd("cmd");
- cmd.arg("/c").arg("emar.bat");
- return Ok((cmd, "emar.bat".to_string()));
- }
+ for flag in &self.ar_flags {
+ any_flags = true;
+ cmd.arg(&**flag);
+ }
+ Ok((cmd, name, any_flags))
+ }
- "emar".to_string()
- } else if target.contains("msvc") {
- let compiler = self.get_base_compiler()?;
- let mut lib = String::new();
- if compiler.family == (ToolFamily::Msvc { clang_cl: true }) {
- // See if there is 'llvm-lib' next to 'clang-cl'
- // Another possibility could be to see if there is 'clang'
- // next to 'clang-cl' and use 'search_programs()' to locate
- // 'llvm-lib'. This is because 'clang-cl' doesn't support
- // the -print-search-dirs option.
- if let Some(mut cmd) = which(&compiler.path) {
- cmd.pop();
- cmd.push("llvm-lib.exe");
- if let Some(llvm_lib) = which(&cmd) {
- lib = llvm_lib.to_str().unwrap().to_owned();
+ fn get_base_archiver(&self) -> Result<(Command, String), Error> {
+ if let Some(ref a) = self.archiver {
+ return Ok((self.cmd(&**a), a.to_string_lossy().into_owned()));
+ }
+
+ self.get_base_archiver_variant("AR", "ar")
+ }
+
+ /// Get the ranlib that's in use for this configuration.
+ ///
+ /// You can use [`Command::get_program`] to get just the path to the command.
+ ///
+ /// This method will take into account all configuration such as debug
+ /// information, optimization level, include directories, defines, etc.
+ /// Additionally, the compiler binary in use follows the standard
+ /// conventions for this path, e.g. looking at the explicitly set compiler,
+ /// environment variables (a number of which are inspected here), and then
+ /// falling back to the default configuration.
+ ///
+ /// # Panics
+ ///
+ /// Panics if an error occurred while determining the architecture.
+ pub fn get_ranlib(&self) -> Command {
+ match self.try_get_ranlib() {
+ Ok(tool) => tool,
+ Err(e) => fail(&e.message),
+ }
+ }
+
+ /// Get the ranlib that's in use for this configuration.
+ ///
+ /// This will return a result instead of panicking;
+ /// see [`Self::get_ranlib`] for the complete description.
+ pub fn try_get_ranlib(&self) -> Result<Command, Error> {
+ let mut cmd = self.get_base_ranlib()?;
+ if let Ok(flags) = self.envflags("RANLIBFLAGS") {
+ cmd.args(flags);
+ }
+ Ok(cmd)
+ }
+
+ fn get_base_ranlib(&self) -> Result<Command, Error> {
+ if let Some(ref r) = self.ranlib {
+ return Ok(self.cmd(&**r));
+ }
+
+ Ok(self.get_base_archiver_variant("RANLIB", "ranlib")?.0)
+ }
+
+ fn get_base_archiver_variant(&self, env: &str, tool: &str) -> Result<(Command, String), Error> {
+ let target = self.get_target()?;
+ let mut name = String::new();
+ let tool_opt: Option<Command> = self
+ .env_tool(env)
+ .map(|(tool, _wrapper, args)| {
+ let mut cmd = self.cmd(tool);
+ cmd.args(args);
+ cmd
+ })
+ .or_else(|| {
+ if target.contains("emscripten") {
+ // Windows use bat files so we have to be a bit more specific
+ if cfg!(windows) {
+ let mut cmd = self.cmd("cmd");
+ name = format!("em{}.bat", tool);
+ cmd.arg("/c").arg(&name);
+ Some(cmd)
+ } else {
+ name = format!("em{}", tool);
+ Some(self.cmd(&name))
}
+ } else if target.starts_with("wasm32") {
+ // Formally speaking one should be able to use this approach,
+ // parsing -print-search-dirs output, to cover all clang targets,
+ // including Android SDKs and other cross-compilation scenarios...
+ // And even extend it to gcc targets by searching for "ar" instead
+ // of "llvm-ar"...
+ let compiler = self.get_base_compiler().ok()?;
+ if compiler.family == ToolFamily::Clang {
+ name = format!("llvm-{}", tool);
+ search_programs(&mut self.cmd(&compiler.path), &name, &self.cargo_output)
+ .map(|name| self.cmd(name))
+ } else {
+ None
+ }
+ } else {
+ None
}
- }
- if lib.is_empty() {
- lib = match windows_registry::find(&target, "lib.exe") {
- Some(t) => return Ok((t, "lib.exe".to_string())),
- None => "lib.exe".to_string(),
- }
- }
- lib
- } else if target.contains("illumos") {
- // The default 'ar' on illumos uses a non-standard flags,
- // but the OS comes bundled with a GNU-compatible variant.
- //
- // Use the GNU-variant to match other Unix systems.
- "gar".to_string()
- } else if self.get_host()? != target {
- match self.prefix_for_target(&target) {
- Some(p) => {
- // GCC uses $target-gcc-ar, whereas binutils uses $target-ar -- try both.
- // Prefer -ar if it exists, as builds of `-gcc-ar` have been observed to be
- // outright broken (such as when targetting freebsd with `--disable-lto`
- // toolchain where the archiver attempts to load the LTO plugin anyway but
- // fails to find one).
- let mut ar = default_ar;
- for &infix in &["", "-gcc"] {
- let target_ar = format!("{}{}-ar", p, infix);
- if Command::new(&target_ar).output().is_ok() {
- ar = target_ar;
- break;
+ });
+
+ let default = tool.to_string();
+ let tool = match tool_opt {
+ Some(t) => t,
+ None => {
+ if target.contains("android") {
+ name = format!("llvm-{}", tool);
+ match Command::new(&name).arg("--version").status() {
+ Ok(status) if status.success() => (),
+ _ => name = format!("{}-{}", target.replace("armv7", "arm"), tool),
+ }
+ self.cmd(&name)
+ } else if target.contains("msvc") {
+ // NOTE: There isn't really a ranlib on msvc, so arguably we should return
+ // `None` somehow here. But in general, callers will already have to be aware
+ // of not running ranlib on Windows anyway, so it feels okay to return lib.exe
+ // here.
+
+ let compiler = self.get_base_compiler()?;
+ let mut lib = String::new();
+ if compiler.family == (ToolFamily::Msvc { clang_cl: true }) {
+ // See if there is 'llvm-lib' next to 'clang-cl'
+ // Another possibility could be to see if there is 'clang'
+ // next to 'clang-cl' and use 'search_programs()' to locate
+ // 'llvm-lib'. This is because 'clang-cl' doesn't support
+ // the -print-search-dirs option.
+ if let Some(mut cmd) = which(&compiler.path, None) {
+ cmd.pop();
+ cmd.push("llvm-lib.exe");
+ if let Some(llvm_lib) = which(&cmd, None) {
+ lib = llvm_lib.to_str().unwrap().to_owned();
+ }
+ }
+ }
+
+ if lib.is_empty() {
+ name = String::from("lib.exe");
+ let mut cmd = match windows_registry::find(&target, "lib.exe") {
+ Some(t) => t,
+ None => self.cmd("lib.exe"),
+ };
+ if target.contains("arm64ec") {
+ cmd.arg("/machine:arm64ec");
}
+ cmd
+ } else {
+ name = lib;
+ self.cmd(&name)
}
- ar
+ } else if target.contains("illumos") {
+ // The default 'ar' on illumos uses a non-standard flags,
+ // but the OS comes bundled with a GNU-compatible variant.
+ //
+ // Use the GNU-variant to match other Unix systems.
+ name = format!("g{}", tool);
+ self.cmd(&name)
+ } else if self.get_host()? != target {
+ match self.prefix_for_target(&target) {
+ Some(p) => {
+ // GCC uses $target-gcc-ar, whereas binutils uses $target-ar -- try both.
+ // Prefer -ar if it exists, as builds of `-gcc-ar` have been observed to be
+ // outright broken (such as when targeting freebsd with `--disable-lto`
+ // toolchain where the archiver attempts to load the LTO plugin anyway but
+ // fails to find one).
+ //
+ // The same applies to ranlib.
+ let mut chosen = default;
+ for &infix in &["", "-gcc"] {
+ let target_p = format!("{}{}-{}", p, infix, tool);
+ if Command::new(&target_p).output().is_ok() {
+ chosen = target_p;
+ break;
+ }
+ }
+ name = chosen;
+ self.cmd(&name)
+ }
+ None => {
+ name = default;
+ self.cmd(&name)
+ }
+ }
+ } else {
+ name = default;
+ self.cmd(&name)
}
- None => default_ar,
}
- } else {
- default_ar
};
- Ok((self.cmd(&program), program))
+
+ Ok((tool, name))
}
fn prefix_for_target(&self, target: &str) -> Option<String> {
- // Put aside RUSTC_LINKER's prefix to be used as last resort
- let rustc_linker = self.getenv("RUSTC_LINKER").unwrap_or("".to_string());
- // let linker_prefix = rustc_linker.strip_suffix("-gcc"); // >=1.45.0
- let linker_prefix = if rustc_linker.len() > 4 {
- let (prefix, suffix) = rustc_linker.split_at(rustc_linker.len() - 4);
- if suffix == "-gcc" {
- Some(prefix)
- } else {
- None
- }
- } else {
- None
- };
+ // Put aside RUSTC_LINKER's prefix to be used as second choice, after CROSS_COMPILE
+ let linker_prefix = self
+ .getenv("RUSTC_LINKER")
+ .and_then(|var| var.strip_suffix("-gcc").map(str::to_string));
// CROSS_COMPILE is of the form: "arm-linux-gnueabi-"
let cc_env = self.getenv("CROSS_COMPILE");
let cross_compile = cc_env.as_ref().map(|s| s.trim_end_matches('-').to_owned());
- cross_compile.or(match &target[..] {
+ cross_compile.or(linker_prefix).or(match &target[..] {
// Note: there is no `aarch64-pc-windows-gnu` target, only `-gnullvm`
"aarch64-pc-windows-gnullvm" => Some("aarch64-w64-mingw32"),
"aarch64-uwp-windows-gnu" => Some("aarch64-w64-mingw32"),
@@ -2774,6 +3239,7 @@ impl Build {
]), // explicit None if not found, so caller knows to fall back
"i686-unknown-linux-musl" => Some("musl"),
"i686-unknown-netbsd" => Some("i486--netbsdelf"),
+ "loongarch64-unknown-linux-gnu" => Some("loongarch64-linux-gnu"),
"mips-unknown-linux-gnu" => Some("mips-linux-gnu"),
"mips-unknown-linux-musl" => Some("mips-linux-musl"),
"mipsel-unknown-linux-gnu" => Some("mipsel-linux-gnu"),
@@ -2794,6 +3260,7 @@ impl Build {
"riscv64-unknown-elf",
"riscv-none-embed",
]),
+ "riscv32imac-esp-espidf" => Some("riscv32-esp-elf"),
"riscv32imac-unknown-none-elf" => self.find_working_gnu_prefix(&[
"riscv32-unknown-elf",
"riscv64-unknown-elf",
@@ -2804,6 +3271,7 @@ impl Build {
"riscv64-unknown-elf",
"riscv-none-embed",
]),
+ "riscv32imc-esp-espidf" => Some("riscv32-esp-elf"),
"riscv32imc-unknown-none-elf" => self.find_working_gnu_prefix(&[
"riscv32-unknown-elf",
"riscv64-unknown-elf",
@@ -2823,6 +3291,7 @@ impl Build {
"riscv32gc-unknown-linux-gnu" => Some("riscv32-linux-gnu"),
"riscv64gc-unknown-linux-musl" => Some("riscv64-linux-musl"),
"riscv32gc-unknown-linux-musl" => Some("riscv32-linux-musl"),
+ "riscv64gc-unknown-netbsd" => Some("riscv64--netbsd"),
"s390x-unknown-linux-gnu" => Some("s390x-linux-gnu"),
"sparc-unknown-linux-gnu" => Some("sparc-linux-gnu"),
"sparc64-unknown-linux-gnu" => Some("sparc64-linux-gnu"),
@@ -2834,6 +3303,7 @@ impl Build {
"armebv7r-none-eabihf" => Some("arm-none-eabi"),
"armv7r-none-eabi" => Some("arm-none-eabi"),
"armv7r-none-eabihf" => Some("arm-none-eabi"),
+ "armv8r-none-eabihf" => Some("arm-none-eabi"),
"thumbv6m-none-eabi" => Some("arm-none-eabi"),
"thumbv7em-none-eabi" => Some("arm-none-eabi"),
"thumbv7em-none-eabihf" => Some("arm-none-eabi"),
@@ -2850,7 +3320,7 @@ impl Build {
]), // explicit None if not found, so caller knows to fall back
"x86_64-unknown-linux-musl" => Some("musl"),
"x86_64-unknown-netbsd" => Some("x86_64--netbsd"),
- _ => linker_prefix,
+ _ => None,
}
.map(|x| x.to_owned()))
}
@@ -2887,30 +3357,30 @@ impl Build {
prefixes.first().map(|prefix| *prefix))
}
- fn get_target(&self) -> Result<String, Error> {
- match self.target.clone() {
- Some(t) => Ok(t),
- None => Ok(self.getenv_unwrap("TARGET")?),
+ fn get_target(&self) -> Result<Arc<str>, Error> {
+ match &self.target {
+ Some(t) => Ok(t.clone()),
+ None => self.getenv_unwrap("TARGET"),
}
}
- fn get_host(&self) -> Result<String, Error> {
- match self.host.clone() {
- Some(h) => Ok(h),
- None => Ok(self.getenv_unwrap("HOST")?),
+ fn get_host(&self) -> Result<Arc<str>, Error> {
+ match &self.host {
+ Some(h) => Ok(h.clone()),
+ None => self.getenv_unwrap("HOST"),
}
}
- fn get_opt_level(&self) -> Result<String, Error> {
- match self.opt_level.as_ref().cloned() {
- Some(ol) => Ok(ol),
- None => Ok(self.getenv_unwrap("OPT_LEVEL")?),
+ fn get_opt_level(&self) -> Result<Arc<str>, Error> {
+ match &self.opt_level {
+ Some(ol) => Ok(ol.clone()),
+ None => self.getenv_unwrap("OPT_LEVEL"),
}
}
fn get_debug(&self) -> bool {
self.debug.unwrap_or_else(|| match self.getenv("DEBUG") {
- Some(s) => s != "false",
+ Some(s) => &*s != "false",
None => false,
})
}
@@ -2938,19 +3408,22 @@ impl Build {
self.force_frame_pointer.unwrap_or_else(|| self.get_debug())
}
- fn get_out_dir(&self) -> Result<PathBuf, Error> {
- match self.out_dir.clone() {
- Some(p) => Ok(p),
- None => Ok(env::var_os("OUT_DIR").map(PathBuf::from).ok_or_else(|| {
- Error::new(
- ErrorKind::EnvVarNotFound,
- "Environment variable OUT_DIR not defined.",
- )
- })?),
+ fn get_out_dir(&self) -> Result<Cow<'_, Path>, Error> {
+ match &self.out_dir {
+ Some(p) => Ok(Cow::Borrowed(&**p)),
+ None => env::var_os("OUT_DIR")
+ .map(PathBuf::from)
+ .map(Cow::Owned)
+ .ok_or_else(|| {
+ Error::new(
+ ErrorKind::EnvVarNotFound,
+ "Environment variable OUT_DIR not defined.",
+ )
+ }),
}
}
- fn getenv(&self, v: &str) -> Option<String> {
+ fn getenv(&self, v: &str) -> Option<Arc<str>> {
// Returns true for environment variables cargo sets for build scripts:
// https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-build-scripts
//
@@ -2970,47 +3443,58 @@ impl Build {
return val.clone();
}
if self.emit_rerun_if_env_changed && !provided_by_cargo(v) {
- self.print(&format!("cargo:rerun-if-env-changed={}", v));
+ self.cargo_output
+ .print_metadata(&format_args!("cargo:rerun-if-env-changed={}", v));
}
- let r = env::var(v).ok();
- self.print(&format!("{} = {:?}", v, r));
+ let r = env::var(v).ok().map(Arc::from);
+ self.cargo_output
+ .print_metadata(&format_args!("{} = {:?}", v, r));
cache.insert(v.to_string(), r.clone());
r
}
- fn getenv_unwrap(&self, v: &str) -> Result<String, Error> {
+ fn getenv_unwrap(&self, v: &str) -> Result<Arc<str>, Error> {
match self.getenv(v) {
Some(s) => Ok(s),
None => Err(Error::new(
ErrorKind::EnvVarNotFound,
- &format!("Environment variable {} not defined.", v.to_string()),
+ format!("Environment variable {} not defined.", v),
)),
}
}
- fn print(&self, s: &str) {
- if self.cargo_metadata {
- println!("{}", s);
+ fn getenv_with_target_prefixes(&self, var_base: &str) -> Result<Arc<str>, Error> {
+ let target = self.get_target()?;
+ let host = self.get_host()?;
+ let kind = if host == target { "HOST" } else { "TARGET" };
+ let target_u = target.replace('-', "_");
+ let res = self
+ .getenv(&format!("{}_{}", var_base, target))
+ .or_else(|| self.getenv(&format!("{}_{}", var_base, target_u)))
+ .or_else(|| self.getenv(&format!("{}_{}", kind, var_base)))
+ .or_else(|| self.getenv(var_base));
+
+ match res {
+ Some(res) => Ok(res),
+ None => Err(Error::new(
+ ErrorKind::EnvVarNotFound,
+ format!("Could not find environment variable {}.", var_base),
+ )),
}
}
+ fn envflags(&self, name: &str) -> Result<Vec<String>, Error> {
+ Ok(self
+ .getenv_with_target_prefixes(name)?
+ .split_ascii_whitespace()
+ .map(|slice| slice.to_string())
+ .collect())
+ }
+
fn fix_env_for_apple_os(&self, cmd: &mut Command) -> Result<(), Error> {
let target = self.get_target()?;
let host = self.get_host()?;
if host.contains("apple-darwin") && target.contains("apple-darwin") {
- // If, for example, `cargo` runs during the build of an XCode project, then `SDKROOT` environment variable
- // would represent the current target, and this is the problem for us, if we want to compile something
- // for the host, when host != target.
- // We can not just remove `SDKROOT`, because, again, for example, XCode add to PATH
- // /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
- // and `cc` from this path can not find system include files, like `pthread.h`, if `SDKROOT`
- // is not set
- if let Ok(sdkroot) = env::var("SDKROOT") {
- if !sdkroot.contains("MacOSX") {
- let macos_sdk = self.apple_sdk_root("macosx")?;
- cmd.env("SDKROOT", macos_sdk);
- }
- }
// Additionally, `IPHONEOS_DEPLOYMENT_TARGET` must not be set when using the Xcode linker at
// "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld",
// although this is apparently ignored when using the linker at "/usr/bin/ld".
@@ -3020,6 +3504,10 @@ impl Build {
}
fn apple_sdk_root(&self, sdk: &str) -> Result<OsString, Error> {
+ if let Some(sdkroot) = env::var_os("SDKROOT") {
+ return Ok(sdkroot);
+ }
+
let mut cache = self
.apple_sdk_root_cache
.lock()
@@ -3034,6 +3522,7 @@ impl Build {
.arg("--sdk")
.arg(sdk),
"xcrun",
+ &self.cargo_output,
)?;
let sdk_path = match String::from_utf8(sdk_path) {
@@ -3050,362 +3539,199 @@ impl Build {
Ok(ret)
}
- fn cuda_file_count(&self) -> usize {
- self.files
- .iter()
- .filter(|file| file.extension() == Some(OsStr::new("cu")))
- .count()
- }
-}
+ fn apple_deployment_version(&self, os: AppleOs, arch_str: Option<&str>, sdk: &str) -> String {
+ let default_deployment_from_sdk = || {
+ let mut cache = self
+ .apple_versions_cache
+ .lock()
+ .expect("apple_versions_cache lock failed");
-impl Default for Build {
- fn default() -> Build {
- Build::new()
- }
-}
+ if let Some(ret) = cache.get(sdk) {
+ return Some(ret.clone());
+ }
-impl Tool {
- fn new(path: PathBuf) -> Self {
- Tool::with_features(path, None, false)
- }
+ let version = run_output(
+ self.cmd("xcrun")
+ .arg("--show-sdk-platform-version")
+ .arg("--sdk")
+ .arg(sdk),
+ "xcrun",
+ &self.cargo_output,
+ )
+ .ok()?;
- fn with_clang_driver(path: PathBuf, clang_driver: Option<&str>) -> Self {
- Self::with_features(path, clang_driver, false)
- }
+ let version = std::str::from_utf8(&version).ok()?.trim().to_owned();
- #[cfg(windows)]
- /// Explicitly set the `ToolFamily`, skipping name-based detection.
- fn with_family(path: PathBuf, family: ToolFamily) -> Self {
- Self {
- path: path,
- cc_wrapper_path: None,
- cc_wrapper_args: Vec::new(),
- args: Vec::new(),
- env: Vec::new(),
- family: family,
- cuda: false,
- removed_args: Vec::new(),
- }
- }
+ cache.insert(sdk.into(), version.clone());
+ Some(version)
+ };
- fn with_features(path: PathBuf, clang_driver: Option<&str>, cuda: bool) -> Self {
- // Try to detect family of the tool from its name, falling back to Gnu.
- let family = if let Some(fname) = path.file_name().and_then(|p| p.to_str()) {
- if fname.contains("clang-cl") {
- ToolFamily::Msvc { clang_cl: true }
- } else if fname.ends_with("cl") || fname == "cl.exe" {
- ToolFamily::Msvc { clang_cl: false }
- } else if fname.contains("clang") {
- match clang_driver {
- Some("cl") => ToolFamily::Msvc { clang_cl: true },
- _ => ToolFamily::Clang,
- }
+ let deployment_from_env = |name: &str| {
+ // note this isn't hit in production codepaths, its mostly just for tests which don't
+ // set the real env
+ if let Some((_, v)) = self.env.iter().find(|(k, _)| &**k == OsStr::new(name)) {
+ Some(v.to_str().unwrap().to_string())
} else {
- ToolFamily::Gnu
+ env::var(name).ok()
}
- } else {
- ToolFamily::Gnu
};
- Tool {
- path: path,
- cc_wrapper_path: None,
- cc_wrapper_args: Vec::new(),
- args: Vec::new(),
- env: Vec::new(),
- family: family,
- cuda: cuda,
- removed_args: Vec::new(),
- }
- }
-
- /// Add an argument to be stripped from the final command arguments.
- fn remove_arg(&mut self, flag: OsString) {
- self.removed_args.push(flag);
- }
-
- /// Add a flag, and optionally prepend the NVCC wrapper flag "-Xcompiler".
- ///
- /// Currently this is only used for compiling CUDA sources, since NVCC only
- /// accepts a limited set of GNU-like flags, and the rest must be prefixed
- /// with a "-Xcompiler" flag to get passed to the underlying C++ compiler.
- fn push_cc_arg(&mut self, flag: OsString) {
- if self.cuda {
- self.args.push("-Xcompiler".into());
- }
- self.args.push(flag);
- }
-
- fn is_duplicate_opt_arg(&self, flag: &OsString) -> bool {
- let flag = flag.to_str().unwrap();
- let mut chars = flag.chars();
-
- // Only duplicate check compiler flags
- if self.is_like_msvc() {
- if chars.next() != Some('/') {
- return false;
- }
- } else if self.is_like_gnu() || self.is_like_clang() {
- if chars.next() != Some('-') {
- return false;
+ // Determines if the acquired deployment target is too low to support modern C++ on some Apple platform.
+ //
+ // A long time ago they used libstdc++, but since macOS 10.9 and iOS 7 libc++ has been the library the SDKs provide to link against.
+ // If a `cc`` config wants to use C++, we round up to these versions as the baseline.
+ let maybe_cpp_version_baseline = |deployment_target_ver: String| -> Option<String> {
+ if !self.cpp {
+ return Some(deployment_target_ver);
}
- }
- // Check for existing optimization flags (-O, /O)
- if chars.next() == Some('O') {
- return self
- .args()
- .iter()
- .any(|ref a| a.to_str().unwrap_or("").chars().nth(1) == Some('O'));
- }
-
- // TODO Check for existing -m..., -m...=..., /arch:... flags
- return false;
- }
-
- /// Don't push optimization arg if it conflicts with existing args
- fn push_opt_unless_duplicate(&mut self, flag: OsString) {
- if self.is_duplicate_opt_arg(&flag) {
- println!("Info: Ignoring duplicate arg {:?}", &flag);
- } else {
- self.push_cc_arg(flag);
- }
- }
-
- /// Converts this compiler into a `Command` that's ready to be run.
- ///
- /// This is useful for when the compiler needs to be executed and the
- /// command returned will already have the initial arguments and environment
- /// variables configured.
- pub fn to_command(&self) -> Command {
- let mut cmd = match self.cc_wrapper_path {
- Some(ref cc_wrapper_path) => {
- let mut cmd = Command::new(&cc_wrapper_path);
- cmd.arg(&self.path);
- cmd
+ let mut deployment_target = deployment_target_ver
+ .split('.')
+ .map(|v| v.parse::<u32>().expect("integer version"));
+
+ match os {
+ AppleOs::MacOs => {
+ let major = deployment_target.next().unwrap_or(0);
+ let minor = deployment_target.next().unwrap_or(0);
+
+ // If below 10.9, we ignore it and let the SDK's target definitions handle it.
+ if major == 10 && minor < 9 {
+ self.cargo_output.print_warning(&format_args!(
+ "macOS deployment target ({}) too low, it will be increased",
+ deployment_target_ver
+ ));
+ return None;
+ }
+ }
+ AppleOs::Ios => {
+ let major = deployment_target.next().unwrap_or(0);
+
+ // If below 10.7, we ignore it and let the SDK's target definitions handle it.
+ if major < 7 {
+ self.cargo_output.print_warning(&format_args!(
+ "iOS deployment target ({}) too low, it will be increased",
+ deployment_target_ver
+ ));
+ return None;
+ }
+ }
+ // watchOS, tvOS, and others are all new enough that libc++ is their baseline.
+ _ => {}
}
- None => Command::new(&self.path),
- };
- cmd.args(&self.cc_wrapper_args);
- let value = self
- .args
- .iter()
- .filter(|a| !self.removed_args.contains(a))
- .collect::<Vec<_>>();
- cmd.args(&value);
-
- for &(ref k, ref v) in self.env.iter() {
- cmd.env(k, v);
- }
- cmd
- }
+ // If the deployment target met or exceeded the C++ baseline
+ Some(deployment_target_ver)
+ };
- /// Returns the path for this compiler.
- ///
- /// Note that this may not be a path to a file on the filesystem, e.g. "cc",
- /// but rather something which will be resolved when a process is spawned.
- pub fn path(&self) -> &Path {
- &self.path
- }
+ // The hardcoded minimums here are subject to change in a future compiler release,
+ // and only exist as last resort fallbacks. Don't consider them stable.
+ // `cc` doesn't use rustc's `--print deployment-target`` because the compiler's defaults
+ // don't align well with Apple's SDKs and other third-party libraries that require ~generally~ higher
+ // deployment targets. rustc isn't interested in those by default though so its fine to be different here.
+ //
+ // If no explicit target is passed, `cc` defaults to the current Xcode SDK's `DefaultDeploymentTarget` for better
+ // compatibility. This is also the crate's historical behavior and what has become a relied-on value.
+ //
+ // The ordering of env -> XCode SDK -> old rustc defaults is intentional for performance when using
+ // an explicit target.
+ match os {
+ AppleOs::MacOs => deployment_from_env("MACOSX_DEPLOYMENT_TARGET")
+ .and_then(maybe_cpp_version_baseline)
+ .or_else(default_deployment_from_sdk)
+ .unwrap_or_else(|| {
+ if arch_str == Some("aarch64") {
+ "11.0".into()
+ } else {
+ let default = "10.7";
+ maybe_cpp_version_baseline(default.into()).unwrap_or_else(|| default.into())
+ }
+ }),
- /// Returns the default set of arguments to the compiler needed to produce
- /// executables for the target this compiler generates.
- pub fn args(&self) -> &[OsString] {
- &self.args
- }
+ AppleOs::Ios => deployment_from_env("IPHONEOS_DEPLOYMENT_TARGET")
+ .and_then(maybe_cpp_version_baseline)
+ .or_else(default_deployment_from_sdk)
+ .unwrap_or_else(|| "7.0".into()),
- /// Returns the set of environment variables needed for this compiler to
- /// operate.
- ///
- /// This is typically only used for MSVC compilers currently.
- pub fn env(&self) -> &[(OsString, OsString)] {
- &self.env
- }
+ AppleOs::WatchOs => deployment_from_env("WATCHOS_DEPLOYMENT_TARGET")
+ .or_else(default_deployment_from_sdk)
+ .unwrap_or_else(|| "5.0".into()),
- /// Returns the compiler command in format of CC environment variable.
- /// Or empty string if CC env was not present
- ///
- /// This is typically used by configure script
- pub fn cc_env(&self) -> OsString {
- match self.cc_wrapper_path {
- Some(ref cc_wrapper_path) => {
- let mut cc_env = cc_wrapper_path.as_os_str().to_owned();
- cc_env.push(" ");
- cc_env.push(self.path.to_path_buf().into_os_string());
- for arg in self.cc_wrapper_args.iter() {
- cc_env.push(" ");
- cc_env.push(arg);
- }
- cc_env
- }
- None => OsString::from(""),
+ AppleOs::TvOs => deployment_from_env("TVOS_DEPLOYMENT_TARGET")
+ .or_else(default_deployment_from_sdk)
+ .unwrap_or_else(|| "9.0".into()),
}
}
- /// Returns the compiler flags in format of CFLAGS environment variable.
- /// Important here - this will not be CFLAGS from env, its internal gcc's flags to use as CFLAGS
- /// This is typically used by configure script
- pub fn cflags_env(&self) -> OsString {
- let mut flags = OsString::new();
- for (i, arg) in self.args.iter().enumerate() {
- if i > 0 {
- flags.push(" ");
- }
- flags.push(arg);
- }
- flags
+ fn cuda_file_count(&self) -> usize {
+ self.files
+ .iter()
+ .filter(|file| file.extension() == Some(OsStr::new("cu")))
+ .count()
}
+}
- /// Whether the tool is GNU Compiler Collection-like.
- pub fn is_like_gnu(&self) -> bool {
- self.family == ToolFamily::Gnu
+impl Default for Build {
+ fn default() -> Build {
+ Build::new()
}
+}
- /// Whether the tool is Clang-like.
- pub fn is_like_clang(&self) -> bool {
- self.family == ToolFamily::Clang
- }
+fn fail(s: &str) -> ! {
+ eprintln!("\n\nerror occurred: {}\n\n", s);
+ std::process::exit(1);
+}
- /// Whether the tool is MSVC-like.
- pub fn is_like_msvc(&self) -> bool {
- match self.family {
- ToolFamily::Msvc { .. } => true,
- _ => false,
+#[derive(Clone, Copy, PartialEq)]
+enum AppleOs {
+ MacOs,
+ Ios,
+ WatchOs,
+ TvOs,
+}
+impl std::fmt::Debug for AppleOs {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ match self {
+ AppleOs::MacOs => f.write_str("macOS"),
+ AppleOs::Ios => f.write_str("iOS"),
+ AppleOs::WatchOs => f.write_str("WatchOS"),
+ AppleOs::TvOs => f.write_str("AppleTVOS"),
}
}
}
-fn run(cmd: &mut Command, program: &str) -> Result<(), Error> {
- let (mut child, print) = spawn(cmd, program)?;
- let status = match child.wait() {
- Ok(s) => s,
- Err(_) => {
- return Err(Error::new(
- ErrorKind::ToolExecError,
- &format!(
- "Failed to wait on spawned child process, command {:?} with args {:?}.",
- cmd, program
- ),
- ));
- }
- };
- print.join().unwrap();
- println!("{}", status);
-
- if status.success() {
- Ok(())
- } else {
- Err(Error::new(
- ErrorKind::ToolExecError,
- &format!(
- "Command {:?} with args {:?} did not execute successfully (status code {}).",
- cmd, program, status
- ),
- ))
- }
+struct AppleSdkTargetParts {
+ sdk_prefix: &'static str,
+ sim_prefix: &'static str,
+ sdk: Cow<'static, str>,
}
-fn run_output(cmd: &mut Command, program: &str) -> Result<Vec<u8>, Error> {
- cmd.stdout(Stdio::piped());
- let (mut child, print) = spawn(cmd, program)?;
- let mut stdout = vec![];
- child
- .stdout
- .take()
- .unwrap()
- .read_to_end(&mut stdout)
- .unwrap();
- let status = match child.wait() {
- Ok(s) => s,
- Err(_) => {
- return Err(Error::new(
- ErrorKind::ToolExecError,
- &format!(
- "Failed to wait on spawned child process, command {:?} with args {:?}.",
- cmd, program
- ),
- ));
- }
+fn apple_os_sdk_parts(os: AppleOs, arch: &AppleArchSpec) -> AppleSdkTargetParts {
+ let (sdk_prefix, sim_prefix) = match os {
+ AppleOs::MacOs => ("macosx", ""),
+ AppleOs::Ios => ("iphone", "ios-"),
+ AppleOs::WatchOs => ("watch", "watch"),
+ AppleOs::TvOs => ("appletv", "appletv"),
+ };
+ let sdk = match arch {
+ AppleArchSpec::Device(_) if os == AppleOs::MacOs => Cow::Borrowed("macosx"),
+ AppleArchSpec::Device(_) => format!("{}os", sdk_prefix).into(),
+ AppleArchSpec::Simulator(_) => format!("{}simulator", sdk_prefix).into(),
+ AppleArchSpec::Catalyst(_) => Cow::Borrowed("macosx"),
};
- print.join().unwrap();
- println!("{}", status);
-
- if status.success() {
- Ok(stdout)
- } else {
- Err(Error::new(
- ErrorKind::ToolExecError,
- &format!(
- "Command {:?} with args {:?} did not execute successfully (status code {}).",
- cmd, program, status
- ),
- ))
- }
-}
-fn spawn(cmd: &mut Command, program: &str) -> Result<(Child, JoinHandle<()>), Error> {
- println!("running: {:?}", cmd);
-
- // Capture the standard error coming from these programs, and write it out
- // with cargo:warning= prefixes. Note that this is a bit wonky to avoid
- // requiring the output to be UTF-8, we instead just ship bytes from one
- // location to another.
- match cmd.stderr(Stdio::piped()).spawn() {
- Ok(mut child) => {
- let stderr = BufReader::new(child.stderr.take().unwrap());
- let print = thread::spawn(move || {
- for line in stderr.split(b'\n').filter_map(|l| l.ok()) {
- print!("cargo:warning=");
- std::io::stdout().write_all(&line).unwrap();
- println!("");
- }
- });
- Ok((child, print))
- }
- Err(ref e) if e.kind() == io::ErrorKind::NotFound => {
- let extra = if cfg!(windows) {
- " (see https://github.com/rust-lang/cc-rs#compile-time-requirements \
- for help)"
- } else {
- ""
- };
- Err(Error::new(
- ErrorKind::ToolNotFound,
- &format!("Failed to find tool. Is `{}` installed?{}", program, extra),
- ))
- }
- Err(ref e) => Err(Error::new(
- ErrorKind::ToolExecError,
- &format!(
- "Command {:?} with args {:?} failed to start: {:?}",
- cmd, program, e
- ),
- )),
+ AppleSdkTargetParts {
+ sdk_prefix,
+ sim_prefix,
+ sdk,
}
}
-fn fail(s: &str) -> ! {
- eprintln!("\n\nerror occurred: {}\n\n", s);
- std::process::exit(1);
-}
-
-fn command_add_output_file(
- cmd: &mut Command,
- dst: &Path,
- cuda: bool,
- msvc: bool,
- clang: bool,
- is_asm: bool,
- is_arm: bool,
-) {
- if msvc && !clang && !cuda && !(is_asm && is_arm) {
- let mut s = OsString::from("-Fo");
- s.push(&dst);
- cmd.arg(s);
- } else {
- cmd.arg("-o").arg(&dst);
- }
+#[allow(dead_code)]
+enum AppleArchSpec {
+ Device(&'static str),
+ Simulator(&'static str),
+ #[allow(dead_code)]
+ Catalyst(&'static str),
}
// Use by default minimum available API level
@@ -3429,13 +3755,12 @@ static NEW_STANDALONE_ANDROID_COMPILERS: [&str; 4] = [
fn android_clang_compiler_uses_target_arg_internally(clang_path: &Path) -> bool {
if let Some(filename) = clang_path.file_name() {
if let Some(filename_str) = filename.to_str() {
- filename_str.contains("android")
- } else {
- false
+ if let Some(idx) = filename_str.rfind('-') {
+ return filename_str.split_at(idx).0.contains("android");
+ }
}
- } else {
- false
}
+ false
}
#[test]
@@ -3449,6 +3774,9 @@ fn test_android_clang_compiler_uses_target_arg_internally() {
));
}
assert!(!android_clang_compiler_uses_target_arg_internally(
+ &PathBuf::from("clang-i686-linux-android")
+ ));
+ assert!(!android_clang_compiler_uses_target_arg_internally(
&PathBuf::from("clang")
));
assert!(!android_clang_compiler_uses_target_arg_internally(
@@ -3505,7 +3833,9 @@ fn autodetect_android_compiler(target: &str, host: &str, gnu: &str, clang: &str)
// Rust and clang/cc don't agree on how to name the target.
fn map_darwin_target_from_rust_to_compiler_architecture(target: &str) -> Option<&'static str> {
- if target.contains("x86_64") {
+ if target.contains("x86_64h") {
+ Some("x86_64h")
+ } else if target.contains("x86_64") {
Some("x86_64")
} else if target.contains("arm64e") {
Some("arm64e")
@@ -3522,7 +3852,7 @@ fn map_darwin_target_from_rust_to_compiler_architecture(target: &str) -> Option<
}
}
-fn which(tool: &Path) -> Option<PathBuf> {
+fn which(tool: &Path, path_entries: Option<OsString>) -> Option<PathBuf> {
fn check_exe(exe: &mut PathBuf) -> bool {
let exe_ext = std::env::consts::EXE_EXTENSION;
exe.exists() || (!exe_ext.is_empty() && exe.set_extension(exe_ext) && exe.exists())
@@ -3535,13 +3865,37 @@ fn which(tool: &Path) -> Option<PathBuf> {
}
// Loop through PATH entries searching for the |tool|.
- let path_entries = env::var_os("PATH")?;
+ let path_entries = path_entries.or(env::var_os("PATH"))?;
env::split_paths(&path_entries).find_map(|path_entry| {
let mut exe = path_entry.join(tool);
- return if check_exe(&mut exe) { Some(exe) } else { None };
+ if check_exe(&mut exe) {
+ Some(exe)
+ } else {
+ None
+ }
})
}
+// search for |prog| on 'programs' path in '|cc| -print-search-dirs' output
+fn search_programs(cc: &mut Command, prog: &str, cargo_output: &CargoOutput) -> Option<PathBuf> {
+ let search_dirs = run_output(
+ cc.arg("-print-search-dirs"),
+ "cc",
+ // this doesn't concern the compilation so we always want to show warnings.
+ cargo_output,
+ )
+ .ok()?;
+ // clang driver appears to be forcing UTF-8 output even on Windows,
+ // hence from_utf8 is assumed to be usable in all cases.
+ let search_dirs = std::str::from_utf8(&search_dirs).ok()?;
+ for dirs in search_dirs.split(|c| c == '\r' || c == '\n') {
+ if let Some(path) = dirs.strip_prefix("programs: =") {
+ return which(Path::new(prog), Some(OsString::from(path)));
+ }
+ }
+ None
+}
+
#[derive(Clone, Copy, PartialEq)]
enum AsmFileExt {
/// `.asm` files. On MSVC targets, we assume these should be passed to MASM
diff --git a/third_party/rust/cc/src/parallel/async_executor.rs b/third_party/rust/cc/src/parallel/async_executor.rs
new file mode 100644
index 0000000000..9ebd1ad562
--- /dev/null
+++ b/third_party/rust/cc/src/parallel/async_executor.rs
@@ -0,0 +1,118 @@
+use std::{
+ cell::Cell,
+ future::Future,
+ pin::Pin,
+ ptr,
+ task::{Context, Poll, RawWaker, RawWakerVTable, Waker},
+ thread,
+ time::Duration,
+};
+
+use crate::Error;
+
+const NOOP_WAKER_VTABLE: RawWakerVTable = RawWakerVTable::new(
+ // Cloning just returns a new no-op raw waker
+ |_| NOOP_RAW_WAKER,
+ // `wake` does nothing
+ |_| {},
+ // `wake_by_ref` does nothing
+ |_| {},
+ // Dropping does nothing as we don't allocate anything
+ |_| {},
+);
+const NOOP_RAW_WAKER: RawWaker = RawWaker::new(ptr::null(), &NOOP_WAKER_VTABLE);
+
+#[derive(Default)]
+pub(crate) struct YieldOnce(bool);
+
+impl Future for YieldOnce {
+ type Output = ();
+
+ fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<()> {
+ let flag = &mut std::pin::Pin::into_inner(self).0;
+ if !*flag {
+ *flag = true;
+ Poll::Pending
+ } else {
+ Poll::Ready(())
+ }
+ }
+}
+
+/// Execute the futures and return when they are all done.
+///
+/// Here we use our own homebrew async executor since cc is used in the build
+/// script of many popular projects, pulling in additional dependencies would
+/// significantly slow down its compilation.
+pub(crate) fn block_on<Fut1, Fut2>(
+ mut fut1: Fut1,
+ mut fut2: Fut2,
+ has_made_progress: &Cell<bool>,
+) -> Result<(), Error>
+where
+ Fut1: Future<Output = Result<(), Error>>,
+ Fut2: Future<Output = Result<(), Error>>,
+{
+ // Shadows the future so that it can never be moved and is guaranteed
+ // to be pinned.
+ //
+ // The same trick used in `pin!` macro.
+ //
+ // TODO: Once MSRV is bumped to 1.68, replace this with `std::pin::pin!`
+ let mut fut1 = Some(unsafe { Pin::new_unchecked(&mut fut1) });
+ let mut fut2 = Some(unsafe { Pin::new_unchecked(&mut fut2) });
+
+ // TODO: Once `Waker::noop` stablised and our MSRV is bumped to the version
+ // which it is stablised, replace this with `Waker::noop`.
+ let waker = unsafe { Waker::from_raw(NOOP_RAW_WAKER) };
+ let mut context = Context::from_waker(&waker);
+
+ let mut backoff_cnt = 0;
+
+ loop {
+ has_made_progress.set(false);
+
+ if let Some(fut) = fut2.as_mut() {
+ if let Poll::Ready(res) = fut.as_mut().poll(&mut context) {
+ fut2 = None;
+ res?;
+ }
+ }
+
+ if let Some(fut) = fut1.as_mut() {
+ if let Poll::Ready(res) = fut.as_mut().poll(&mut context) {
+ fut1 = None;
+ res?;
+ }
+ }
+
+ if fut1.is_none() && fut2.is_none() {
+ return Ok(());
+ }
+
+ if !has_made_progress.get() {
+ if backoff_cnt > 3 {
+ // We have yielded at least three times without making'
+ // any progress, so we will sleep for a while.
+ let duration = Duration::from_millis(100 * (backoff_cnt - 3).min(10));
+ thread::sleep(duration);
+ } else {
+ // Given that we spawned a lot of compilation tasks, it is unlikely
+ // that OS cannot find other ready task to execute.
+ //
+ // If all of them are done, then we will yield them and spawn more,
+ // or simply return.
+ //
+ // Thus this will not be turned into a busy-wait loop and it will not
+ // waste CPU resource.
+ thread::yield_now();
+ }
+ }
+
+ backoff_cnt = if has_made_progress.get() {
+ 0
+ } else {
+ backoff_cnt + 1
+ };
+ }
+}
diff --git a/third_party/rust/cc/src/parallel/job_token.rs b/third_party/rust/cc/src/parallel/job_token.rs
new file mode 100644
index 0000000000..4fec982f85
--- /dev/null
+++ b/third_party/rust/cc/src/parallel/job_token.rs
@@ -0,0 +1,255 @@
+use std::{marker::PhantomData, mem::MaybeUninit, sync::Once};
+
+use crate::Error;
+
+pub(crate) struct JobToken(PhantomData<()>);
+
+impl JobToken {
+ fn new() -> Self {
+ Self(PhantomData)
+ }
+}
+
+impl Drop for JobToken {
+ fn drop(&mut self) {
+ match JobTokenServer::new() {
+ JobTokenServer::Inherited(jobserver) => jobserver.release_token_raw(),
+ JobTokenServer::InProcess(jobserver) => jobserver.release_token_raw(),
+ }
+ }
+}
+
+enum JobTokenServer {
+ Inherited(inherited_jobserver::JobServer),
+ InProcess(inprocess_jobserver::JobServer),
+}
+
+impl JobTokenServer {
+ /// This function returns a static reference to the jobserver because
+ /// - creating a jobserver from env is a bit fd-unsafe (e.g. the fd might
+ /// be closed by other jobserver users in the process) and better do it
+ /// at the start of the program.
+ /// - in case a jobserver cannot be created from env (e.g. it's not
+ /// present), we will create a global in-process only jobserver
+ /// that has to be static so that it will be shared by all cc
+ /// compilation.
+ fn new() -> &'static Self {
+ static INIT: Once = Once::new();
+ static mut JOBSERVER: MaybeUninit<JobTokenServer> = MaybeUninit::uninit();
+
+ unsafe {
+ INIT.call_once(|| {
+ let server = inherited_jobserver::JobServer::from_env()
+ .map(Self::Inherited)
+ .unwrap_or_else(|| Self::InProcess(inprocess_jobserver::JobServer::new()));
+ JOBSERVER = MaybeUninit::new(server);
+ });
+ // TODO: Poor man's assume_init_ref, as that'd require a MSRV of 1.55.
+ &*JOBSERVER.as_ptr()
+ }
+ }
+}
+
+pub(crate) enum ActiveJobTokenServer {
+ Inherited(inherited_jobserver::ActiveJobServer<'static>),
+ InProcess(&'static inprocess_jobserver::JobServer),
+}
+
+impl ActiveJobTokenServer {
+ pub(crate) fn new() -> Result<Self, Error> {
+ match JobTokenServer::new() {
+ JobTokenServer::Inherited(inherited_jobserver) => {
+ inherited_jobserver.enter_active().map(Self::Inherited)
+ }
+ JobTokenServer::InProcess(inprocess_jobserver) => {
+ Ok(Self::InProcess(inprocess_jobserver))
+ }
+ }
+ }
+
+ pub(crate) async fn acquire(&self) -> Result<JobToken, Error> {
+ match &self {
+ Self::Inherited(jobserver) => jobserver.acquire().await,
+ Self::InProcess(jobserver) => Ok(jobserver.acquire().await),
+ }
+ }
+}
+
+mod inherited_jobserver {
+ use super::JobToken;
+
+ use crate::{parallel::async_executor::YieldOnce, Error, ErrorKind};
+
+ use std::{
+ io, mem,
+ sync::{mpsc, Mutex, MutexGuard, PoisonError},
+ };
+
+ pub(super) struct JobServer {
+ /// Implicit token for this process which is obtained and will be
+ /// released in parent. Since JobTokens only give back what they got,
+ /// there should be at most one global implicit token in the wild.
+ ///
+ /// Since Rust does not execute any `Drop` for global variables,
+ /// we can't just put it back to jobserver and then re-acquire it at
+ /// the end of the process.
+ ///
+ /// Use `Mutex` to avoid race between acquire and release.
+ /// If an `AtomicBool` is used, then it's possible for:
+ /// - `release_token_raw`: Tries to set `global_implicit_token` to true, but it is already
+ /// set to `true`, continue to release it to jobserver
+ /// - `acquire` takes the global implicit token, set `global_implicit_token` to false
+ /// - `release_token_raw` now writes the token back into the jobserver, while
+ /// `global_implicit_token` is `false`
+ ///
+ /// If the program exits here, then cc effectively increases parallelism by one, which is
+ /// incorrect, hence we use a `Mutex` here.
+ global_implicit_token: Mutex<bool>,
+ inner: jobserver::Client,
+ }
+
+ impl JobServer {
+ pub(super) unsafe fn from_env() -> Option<Self> {
+ jobserver::Client::from_env().map(|inner| Self {
+ inner,
+ global_implicit_token: Mutex::new(true),
+ })
+ }
+
+ fn get_global_implicit_token(&self) -> MutexGuard<'_, bool> {
+ self.global_implicit_token
+ .lock()
+ .unwrap_or_else(PoisonError::into_inner)
+ }
+
+ /// All tokens except for the global implicit token will be put back into the jobserver
+ /// immediately and they cannot be cached, since Rust does not call `Drop::drop` on
+ /// global variables.
+ pub(super) fn release_token_raw(&self) {
+ let mut global_implicit_token = self.get_global_implicit_token();
+
+ if *global_implicit_token {
+ // There's already a global implicit token, so this token must
+ // be released back into jobserver.
+ //
+ // `release_raw` should not block
+ let _ = self.inner.release_raw();
+ } else {
+ *global_implicit_token = true;
+ }
+ }
+
+ pub(super) fn enter_active(&self) -> Result<ActiveJobServer<'_>, Error> {
+ ActiveJobServer::new(self)
+ }
+ }
+
+ pub(crate) struct ActiveJobServer<'a> {
+ jobserver: &'a JobServer,
+ helper_thread: jobserver::HelperThread,
+ /// When rx is dropped, all the token stored within it will be dropped.
+ rx: mpsc::Receiver<io::Result<jobserver::Acquired>>,
+ }
+
+ impl<'a> ActiveJobServer<'a> {
+ fn new(jobserver: &'a JobServer) -> Result<Self, Error> {
+ let (tx, rx) = mpsc::channel();
+
+ Ok(Self {
+ rx,
+ helper_thread: jobserver.inner.clone().into_helper_thread(move |res| {
+ let _ = tx.send(res);
+ })?,
+ jobserver,
+ })
+ }
+
+ pub(super) async fn acquire(&self) -> Result<JobToken, Error> {
+ let mut has_requested_token = false;
+
+ loop {
+ // Fast path
+ if mem::replace(&mut *self.jobserver.get_global_implicit_token(), false) {
+ break Ok(JobToken::new());
+ }
+
+ // Cold path, no global implicit token, obtain one
+ match self.rx.try_recv() {
+ Ok(res) => {
+ let acquired = res?;
+ acquired.drop_without_releasing();
+ break Ok(JobToken::new());
+ }
+ Err(mpsc::TryRecvError::Disconnected) => {
+ break Err(Error::new(
+ ErrorKind::JobserverHelpThreadError,
+ "jobserver help thread has returned before ActiveJobServer is dropped",
+ ))
+ }
+ Err(mpsc::TryRecvError::Empty) => {
+ if !has_requested_token {
+ self.helper_thread.request_token();
+ has_requested_token = true;
+ }
+ YieldOnce::default().await
+ }
+ }
+ }
+ }
+ }
+}
+
+mod inprocess_jobserver {
+ use super::JobToken;
+
+ use crate::parallel::async_executor::YieldOnce;
+
+ use std::{
+ env::var,
+ sync::atomic::{
+ AtomicU32,
+ Ordering::{AcqRel, Acquire},
+ },
+ };
+
+ pub(crate) struct JobServer(AtomicU32);
+
+ impl JobServer {
+ pub(super) fn new() -> Self {
+ // Use `NUM_JOBS` if set (it's configured by Cargo) and otherwise
+ // just fall back to a semi-reasonable number.
+ //
+ // Note that we could use `num_cpus` here but it's an extra
+ // dependency that will almost never be used, so
+ // it's generally not too worth it.
+ let mut parallelism = 4;
+ // TODO: Use std::thread::available_parallelism as an upper bound
+ // when MSRV is bumped.
+ if let Ok(amt) = var("NUM_JOBS") {
+ if let Ok(amt) = amt.parse() {
+ parallelism = amt;
+ }
+ }
+
+ Self(AtomicU32::new(parallelism))
+ }
+
+ pub(super) async fn acquire(&self) -> JobToken {
+ loop {
+ let res = self
+ .0
+ .fetch_update(AcqRel, Acquire, |tokens| tokens.checked_sub(1));
+
+ if res.is_ok() {
+ break JobToken::new();
+ }
+
+ YieldOnce::default().await
+ }
+ }
+
+ pub(super) fn release_token_raw(&self) {
+ self.0.fetch_add(1, AcqRel);
+ }
+ }
+}
diff --git a/third_party/rust/cc/src/parallel/mod.rs b/third_party/rust/cc/src/parallel/mod.rs
new file mode 100644
index 0000000000..d69146dc59
--- /dev/null
+++ b/third_party/rust/cc/src/parallel/mod.rs
@@ -0,0 +1,20 @@
+pub(crate) mod async_executor;
+pub(crate) mod job_token;
+pub(crate) mod stderr;
+
+/// Remove all element in `vec` which `f(element)` returns `false`.
+///
+/// TODO: Remove this once the MSRV is bumped to v1.61
+pub(crate) fn retain_unordered_mut<T, F>(vec: &mut Vec<T>, mut f: F)
+where
+ F: FnMut(&mut T) -> bool,
+{
+ let mut i = 0;
+ while i < vec.len() {
+ if f(&mut vec[i]) {
+ i += 1;
+ } else {
+ vec.swap_remove(i);
+ }
+ }
+}
diff --git a/third_party/rust/cc/src/parallel/stderr.rs b/third_party/rust/cc/src/parallel/stderr.rs
new file mode 100644
index 0000000000..47fa085dba
--- /dev/null
+++ b/third_party/rust/cc/src/parallel/stderr.rs
@@ -0,0 +1,90 @@
+/// Helpers functions for [ChildStderr].
+use std::{convert::TryInto, process::ChildStderr};
+
+use crate::{Error, ErrorKind};
+
+#[cfg(all(not(unix), not(windows)))]
+compile_error!("Only unix and windows support non-blocking pipes! For other OSes, disable the parallel feature.");
+
+#[cfg(unix)]
+fn get_flags(fd: std::os::unix::io::RawFd) -> Result<i32, Error> {
+ let flags = unsafe { libc::fcntl(fd, libc::F_GETFL, 0) };
+ if flags == -1 {
+ Err(Error::new(
+ ErrorKind::IOError,
+ format!(
+ "Failed to get flags for pipe {}: {}",
+ fd,
+ std::io::Error::last_os_error()
+ ),
+ ))
+ } else {
+ Ok(flags)
+ }
+}
+
+#[cfg(unix)]
+fn set_flags(fd: std::os::unix::io::RawFd, flags: std::os::raw::c_int) -> Result<(), Error> {
+ if unsafe { libc::fcntl(fd, libc::F_SETFL, flags) } == -1 {
+ Err(Error::new(
+ ErrorKind::IOError,
+ format!(
+ "Failed to set flags for pipe {}: {}",
+ fd,
+ std::io::Error::last_os_error()
+ ),
+ ))
+ } else {
+ Ok(())
+ }
+}
+
+#[cfg(unix)]
+pub fn set_non_blocking(pipe: &impl std::os::unix::io::AsRawFd) -> Result<(), Error> {
+ // On Unix, switch the pipe to non-blocking mode.
+ // On Windows, we have a different way to be non-blocking.
+ let fd = pipe.as_raw_fd();
+
+ let flags = get_flags(fd)?;
+ set_flags(fd, flags | libc::O_NONBLOCK)
+}
+
+pub fn bytes_available(stderr: &mut ChildStderr) -> Result<usize, Error> {
+ let mut bytes_available = 0;
+ #[cfg(windows)]
+ {
+ use crate::windows::windows_sys::PeekNamedPipe;
+ use std::os::windows::io::AsRawHandle;
+ use std::ptr::null_mut;
+ if unsafe {
+ PeekNamedPipe(
+ stderr.as_raw_handle(),
+ null_mut(),
+ 0,
+ null_mut(),
+ &mut bytes_available,
+ null_mut(),
+ )
+ } == 0
+ {
+ return Err(Error::new(
+ ErrorKind::IOError,
+ format!(
+ "PeekNamedPipe failed with {}",
+ std::io::Error::last_os_error()
+ ),
+ ));
+ }
+ }
+ #[cfg(unix)]
+ {
+ use std::os::unix::io::AsRawFd;
+ if unsafe { libc::ioctl(stderr.as_raw_fd(), libc::FIONREAD, &mut bytes_available) } != 0 {
+ return Err(Error::new(
+ ErrorKind::IOError,
+ format!("ioctl failed with {}", std::io::Error::last_os_error()),
+ ));
+ }
+ }
+ Ok(bytes_available.try_into().unwrap())
+}
diff --git a/third_party/rust/cc/src/tool.rs b/third_party/rust/cc/src/tool.rs
new file mode 100644
index 0000000000..a193a90ff7
--- /dev/null
+++ b/third_party/rust/cc/src/tool.rs
@@ -0,0 +1,399 @@
+use std::{
+ collections::HashMap,
+ ffi::OsString,
+ path::{Path, PathBuf},
+ process::Command,
+ sync::Mutex,
+};
+
+use crate::command_helpers::{run_output, CargoOutput};
+
+/// Configuration used to represent an invocation of a C compiler.
+///
+/// This can be used to figure out what compiler is in use, what the arguments
+/// to it are, and what the environment variables look like for the compiler.
+/// This can be used to further configure other build systems (e.g. forward
+/// along CC and/or CFLAGS) or the `to_command` method can be used to run the
+/// compiler itself.
+#[derive(Clone, Debug)]
+#[allow(missing_docs)]
+pub struct Tool {
+ pub(crate) path: PathBuf,
+ pub(crate) cc_wrapper_path: Option<PathBuf>,
+ pub(crate) cc_wrapper_args: Vec<OsString>,
+ pub(crate) args: Vec<OsString>,
+ pub(crate) env: Vec<(OsString, OsString)>,
+ pub(crate) family: ToolFamily,
+ pub(crate) cuda: bool,
+ pub(crate) removed_args: Vec<OsString>,
+ pub(crate) has_internal_target_arg: bool,
+}
+
+impl Tool {
+ pub(crate) fn new(
+ path: PathBuf,
+ cached_compiler_family: &Mutex<HashMap<Box<Path>, ToolFamily>>,
+ cargo_output: &CargoOutput,
+ ) -> Self {
+ Self::with_features(path, None, false, cached_compiler_family, cargo_output)
+ }
+
+ pub(crate) fn with_clang_driver(
+ path: PathBuf,
+ clang_driver: Option<&str>,
+ cached_compiler_family: &Mutex<HashMap<Box<Path>, ToolFamily>>,
+ cargo_output: &CargoOutput,
+ ) -> Self {
+ Self::with_features(
+ path,
+ clang_driver,
+ false,
+ cached_compiler_family,
+ cargo_output,
+ )
+ }
+
+ /// Explicitly set the `ToolFamily`, skipping name-based detection.
+ pub(crate) fn with_family(path: PathBuf, family: ToolFamily) -> Self {
+ Self {
+ path,
+ cc_wrapper_path: None,
+ cc_wrapper_args: Vec::new(),
+ args: Vec::new(),
+ env: Vec::new(),
+ family,
+ cuda: false,
+ removed_args: Vec::new(),
+ has_internal_target_arg: false,
+ }
+ }
+
+ pub(crate) fn with_features(
+ path: PathBuf,
+ clang_driver: Option<&str>,
+ cuda: bool,
+ cached_compiler_family: &Mutex<HashMap<Box<Path>, ToolFamily>>,
+ cargo_output: &CargoOutput,
+ ) -> Self {
+ fn detect_family_inner(path: &Path, cargo_output: &CargoOutput) -> ToolFamily {
+ let mut cmd = Command::new(path);
+ cmd.arg("--version");
+
+ let stdout = match run_output(
+ &mut cmd,
+ &path.to_string_lossy(),
+ // tool detection issues should always be shown as warnings
+ cargo_output,
+ )
+ .ok()
+ .and_then(|o| String::from_utf8(o).ok())
+ {
+ Some(s) => s,
+ None => {
+ // --version failed. fallback to gnu
+ cargo_output.print_warning(&format_args!("Failed to run: {:?}", cmd));
+ return ToolFamily::Gnu;
+ }
+ };
+ if stdout.contains("clang") {
+ ToolFamily::Clang
+ } else if stdout.contains("GCC") {
+ ToolFamily::Gnu
+ } else {
+ // --version doesn't include clang for GCC
+ cargo_output.print_warning(&format_args!(
+ "Compiler version doesn't include clang or GCC: {:?}",
+ cmd
+ ));
+ ToolFamily::Gnu
+ }
+ }
+ let detect_family = |path: &Path| -> ToolFamily {
+ if let Some(family) = cached_compiler_family.lock().unwrap().get(path) {
+ return *family;
+ }
+
+ let family = detect_family_inner(path, cargo_output);
+ cached_compiler_family
+ .lock()
+ .unwrap()
+ .insert(path.into(), family);
+ family
+ };
+
+ // Try to detect family of the tool from its name, falling back to Gnu.
+ let family = if let Some(fname) = path.file_name().and_then(|p| p.to_str()) {
+ if fname.contains("clang-cl") {
+ ToolFamily::Msvc { clang_cl: true }
+ } else if fname.ends_with("cl") || fname == "cl.exe" {
+ ToolFamily::Msvc { clang_cl: false }
+ } else if fname.contains("clang") {
+ match clang_driver {
+ Some("cl") => ToolFamily::Msvc { clang_cl: true },
+ _ => ToolFamily::Clang,
+ }
+ } else {
+ detect_family(&path)
+ }
+ } else {
+ detect_family(&path)
+ };
+
+ Tool {
+ path,
+ cc_wrapper_path: None,
+ cc_wrapper_args: Vec::new(),
+ args: Vec::new(),
+ env: Vec::new(),
+ family,
+ cuda,
+ removed_args: Vec::new(),
+ has_internal_target_arg: false,
+ }
+ }
+
+ /// Add an argument to be stripped from the final command arguments.
+ pub(crate) fn remove_arg(&mut self, flag: OsString) {
+ self.removed_args.push(flag);
+ }
+
+ /// Push an "exotic" flag to the end of the compiler's arguments list.
+ ///
+ /// Nvidia compiler accepts only the most common compiler flags like `-D`,
+ /// `-I`, `-c`, etc. Options meant specifically for the underlying
+ /// host C++ compiler have to be prefixed with `-Xcompiler`.
+ /// [Another possible future application for this function is passing
+ /// clang-specific flags to clang-cl, which otherwise accepts only
+ /// MSVC-specific options.]
+ pub(crate) fn push_cc_arg(&mut self, flag: OsString) {
+ if self.cuda {
+ self.args.push("-Xcompiler".into());
+ }
+ self.args.push(flag);
+ }
+
+ /// Checks if an argument or flag has already been specified or conflicts.
+ ///
+ /// Currently only checks optimization flags.
+ pub(crate) fn is_duplicate_opt_arg(&self, flag: &OsString) -> bool {
+ let flag = flag.to_str().unwrap();
+ let mut chars = flag.chars();
+
+ // Only duplicate check compiler flags
+ if self.is_like_msvc() {
+ if chars.next() != Some('/') {
+ return false;
+ }
+ } else if self.is_like_gnu() || self.is_like_clang() {
+ if chars.next() != Some('-') {
+ return false;
+ }
+ }
+
+ // Check for existing optimization flags (-O, /O)
+ if chars.next() == Some('O') {
+ return self
+ .args()
+ .iter()
+ .any(|a| a.to_str().unwrap_or("").chars().nth(1) == Some('O'));
+ }
+
+ // TODO Check for existing -m..., -m...=..., /arch:... flags
+ false
+ }
+
+ /// Don't push optimization arg if it conflicts with existing args.
+ pub(crate) fn push_opt_unless_duplicate(&mut self, flag: OsString) {
+ if self.is_duplicate_opt_arg(&flag) {
+ println!("Info: Ignoring duplicate arg {:?}", &flag);
+ } else {
+ self.push_cc_arg(flag);
+ }
+ }
+
+ /// Converts this compiler into a `Command` that's ready to be run.
+ ///
+ /// This is useful for when the compiler needs to be executed and the
+ /// command returned will already have the initial arguments and environment
+ /// variables configured.
+ pub fn to_command(&self) -> Command {
+ let mut cmd = match self.cc_wrapper_path {
+ Some(ref cc_wrapper_path) => {
+ let mut cmd = Command::new(cc_wrapper_path);
+ cmd.arg(&self.path);
+ cmd
+ }
+ None => Command::new(&self.path),
+ };
+ cmd.args(&self.cc_wrapper_args);
+
+ let value = self
+ .args
+ .iter()
+ .filter(|a| !self.removed_args.contains(a))
+ .collect::<Vec<_>>();
+ cmd.args(&value);
+
+ for (k, v) in self.env.iter() {
+ cmd.env(k, v);
+ }
+ cmd
+ }
+
+ /// Returns the path for this compiler.
+ ///
+ /// Note that this may not be a path to a file on the filesystem, e.g. "cc",
+ /// but rather something which will be resolved when a process is spawned.
+ pub fn path(&self) -> &Path {
+ &self.path
+ }
+
+ /// Returns the default set of arguments to the compiler needed to produce
+ /// executables for the target this compiler generates.
+ pub fn args(&self) -> &[OsString] {
+ &self.args
+ }
+
+ /// Returns the set of environment variables needed for this compiler to
+ /// operate.
+ ///
+ /// This is typically only used for MSVC compilers currently.
+ pub fn env(&self) -> &[(OsString, OsString)] {
+ &self.env
+ }
+
+ /// Returns the compiler command in format of CC environment variable.
+ /// Or empty string if CC env was not present
+ ///
+ /// This is typically used by configure script
+ pub fn cc_env(&self) -> OsString {
+ match self.cc_wrapper_path {
+ Some(ref cc_wrapper_path) => {
+ let mut cc_env = cc_wrapper_path.as_os_str().to_owned();
+ cc_env.push(" ");
+ cc_env.push(self.path.to_path_buf().into_os_string());
+ for arg in self.cc_wrapper_args.iter() {
+ cc_env.push(" ");
+ cc_env.push(arg);
+ }
+ cc_env
+ }
+ None => OsString::from(""),
+ }
+ }
+
+ /// Returns the compiler flags in format of CFLAGS environment variable.
+ /// Important here - this will not be CFLAGS from env, its internal gcc's flags to use as CFLAGS
+ /// This is typically used by configure script
+ pub fn cflags_env(&self) -> OsString {
+ let mut flags = OsString::new();
+ for (i, arg) in self.args.iter().enumerate() {
+ if i > 0 {
+ flags.push(" ");
+ }
+ flags.push(arg);
+ }
+ flags
+ }
+
+ /// Whether the tool is GNU Compiler Collection-like.
+ pub fn is_like_gnu(&self) -> bool {
+ self.family == ToolFamily::Gnu
+ }
+
+ /// Whether the tool is Clang-like.
+ pub fn is_like_clang(&self) -> bool {
+ self.family == ToolFamily::Clang
+ }
+
+ /// Whether the tool is AppleClang under .xctoolchain
+ #[cfg(target_vendor = "apple")]
+ pub(crate) fn is_xctoolchain_clang(&self) -> bool {
+ let path = self.path.to_string_lossy();
+ path.contains(".xctoolchain/")
+ }
+ #[cfg(not(target_vendor = "apple"))]
+ pub(crate) fn is_xctoolchain_clang(&self) -> bool {
+ false
+ }
+
+ /// Whether the tool is MSVC-like.
+ pub fn is_like_msvc(&self) -> bool {
+ match self.family {
+ ToolFamily::Msvc { .. } => true,
+ _ => false,
+ }
+ }
+}
+
+/// Represents the family of tools this tool belongs to.
+///
+/// Each family of tools differs in how and what arguments they accept.
+///
+/// Detection of a family is done on best-effort basis and may not accurately reflect the tool.
+#[derive(Copy, Clone, Debug, PartialEq)]
+pub enum ToolFamily {
+ /// Tool is GNU Compiler Collection-like.
+ Gnu,
+ /// Tool is Clang-like. It differs from the GCC in a sense that it accepts superset of flags
+ /// and its cross-compilation approach is different.
+ Clang,
+ /// Tool is the MSVC cl.exe.
+ Msvc { clang_cl: bool },
+}
+
+impl ToolFamily {
+ /// What the flag to request debug info for this family of tools look like
+ pub(crate) fn add_debug_flags(&self, cmd: &mut Tool, dwarf_version: Option<u32>) {
+ match *self {
+ ToolFamily::Msvc { .. } => {
+ cmd.push_cc_arg("-Z7".into());
+ }
+ ToolFamily::Gnu | ToolFamily::Clang => {
+ cmd.push_cc_arg(
+ dwarf_version
+ .map_or_else(|| "-g".into(), |v| format!("-gdwarf-{}", v))
+ .into(),
+ );
+ }
+ }
+ }
+
+ /// What the flag to force frame pointers.
+ pub(crate) fn add_force_frame_pointer(&self, cmd: &mut Tool) {
+ match *self {
+ ToolFamily::Gnu | ToolFamily::Clang => {
+ cmd.push_cc_arg("-fno-omit-frame-pointer".into());
+ }
+ _ => (),
+ }
+ }
+
+ /// What the flags to enable all warnings
+ pub(crate) fn warnings_flags(&self) -> &'static str {
+ match *self {
+ ToolFamily::Msvc { .. } => "-W4",
+ ToolFamily::Gnu | ToolFamily::Clang => "-Wall",
+ }
+ }
+
+ /// What the flags to enable extra warnings
+ pub(crate) fn extra_warnings_flags(&self) -> Option<&'static str> {
+ match *self {
+ ToolFamily::Msvc { .. } => None,
+ ToolFamily::Gnu | ToolFamily::Clang => Some("-Wextra"),
+ }
+ }
+
+ /// What the flag to turn warning into errors
+ pub(crate) fn warnings_to_errors_flag(&self) -> &'static str {
+ match *self {
+ ToolFamily::Msvc { .. } => "-WX",
+ ToolFamily::Gnu | ToolFamily::Clang => "-Werror",
+ }
+ }
+
+ pub(crate) fn verbose_stderr(&self) -> bool {
+ *self == ToolFamily::Clang
+ }
+}
diff --git a/third_party/rust/cc/src/com.rs b/third_party/rust/cc/src/windows/com.rs
index 843247e588..e81bb1d3c3 100644
--- a/third_party/rust/cc/src/com.rs
+++ b/third_party/rust/cc/src/windows/com.rs
@@ -7,27 +7,31 @@
#![allow(unused)]
-use crate::winapi::CoInitializeEx;
-use crate::winapi::IUnknown;
-use crate::winapi::Interface;
-use crate::winapi::BSTR;
-use crate::winapi::COINIT_MULTITHREADED;
-use crate::winapi::{SysFreeString, SysStringLen};
-use crate::winapi::{HRESULT, S_FALSE, S_OK};
-use std::ffi::{OsStr, OsString};
-use std::mem::forget;
-use std::ops::Deref;
-use std::os::windows::ffi::{OsStrExt, OsStringExt};
-use std::ptr::null_mut;
-use std::slice::from_raw_parts;
+use crate::windows::{
+ winapi::{IUnknown, Interface},
+ windows_sys::{
+ CoInitializeEx, SysFreeString, SysStringLen, BSTR, COINIT_MULTITHREADED, HRESULT, S_FALSE,
+ S_OK,
+ },
+};
+use std::{
+ convert::TryInto,
+ ffi::{OsStr, OsString},
+ mem::ManuallyDrop,
+ ops::Deref,
+ os::windows::ffi::{OsStrExt, OsStringExt},
+ ptr::{null, null_mut},
+ slice::from_raw_parts,
+};
pub fn initialize() -> Result<(), HRESULT> {
- let err = unsafe { CoInitializeEx(null_mut(), COINIT_MULTITHREADED) };
+ let err = unsafe { CoInitializeEx(null(), COINIT_MULTITHREADED.try_into().unwrap()) };
if err != S_OK && err != S_FALSE {
// S_FALSE just means COM is already initialized
- return Err(err);
+ Err(err)
+ } else {
+ Ok(())
}
- Ok(())
}
pub struct ComPtr<T>(*mut T)
@@ -55,15 +59,13 @@ where
/// Extracts the raw pointer.
/// You are now responsible for releasing it yourself.
pub fn into_raw(self) -> *mut T {
- let p = self.0;
- forget(self);
- p
+ ManuallyDrop::new(self).0
}
/// For internal use only.
fn as_unknown(&self) -> &IUnknown {
unsafe { &*(self.0 as *mut IUnknown) }
}
- /// Performs QueryInterface fun.
+ /// Performs `QueryInterface` fun.
pub fn cast<U>(&self) -> Result<ComPtr<U>, i32>
where
U: Interface,
diff --git a/third_party/rust/cc/src/windows_registry.rs b/third_party/rust/cc/src/windows/find_tools.rs
index 276688b03f..4f0ed87411 100644
--- a/third_party/rust/cc/src/windows_registry.rs
+++ b/third_party/rust/cc/src/windows/find_tools.rs
@@ -8,18 +8,34 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-//! A helper module to probe the Windows Registry when looking for
-//! windows-specific tools.
+//! A helper module to looking for windows-specific tools:
+//! 1. On Windows host, probe the Windows Registry if needed;
+//! 2. On non-Windows host, check specified environment variables.
+
+#![allow(clippy::upper_case_acronyms)]
use std::process::Command;
use crate::Tool;
-#[cfg(windows)]
use crate::ToolFamily;
-#[cfg(windows)]
const MSVC_FAMILY: ToolFamily = ToolFamily::Msvc { clang_cl: false };
+#[derive(Copy, Clone)]
+struct TargetArch<'a>(pub &'a str);
+
+impl PartialEq<&str> for TargetArch<'_> {
+ fn eq(&self, other: &&str) -> bool {
+ self.0 == *other
+ }
+}
+
+impl<'a> From<TargetArch<'a>> for &'a str {
+ fn from(target: TargetArch<'a>) -> Self {
+ target.0
+ }
+}
+
/// Attempts to find a tool within an MSVC installation using the Windows
/// registry as a point to search from.
///
@@ -39,13 +55,6 @@ pub fn find(target: &str, tool: &str) -> Option<Command> {
/// Similar to the `find` function above, this function will attempt the same
/// operation (finding a MSVC tool in a local install) but instead returns a
/// `Tool` which may be introspected.
-#[cfg(not(windows))]
-pub fn find_tool(_target: &str, _tool: &str) -> Option<Tool> {
- None
-}
-
-/// Documented above.
-#[cfg(windows)]
pub fn find_tool(target: &str, tool: &str) -> Option<Tool> {
// This logic is all tailored for MSVC, if we're not that then bail out
// early.
@@ -53,13 +62,17 @@ pub fn find_tool(target: &str, tool: &str) -> Option<Tool> {
return None;
}
+ // Split the target to get the arch.
+ let target = TargetArch(target.split_once('-')?.0);
+
// Looks like msbuild isn't located in the same location as other tools like
- // cl.exe and lib.exe. To handle this we probe for it manually with
- // dedicated registry keys.
+ // cl.exe and lib.exe.
if tool.contains("msbuild") {
return impl_::find_msbuild(target);
}
+ // Looks like devenv isn't located in the same location as other tools like
+ // cl.exe and lib.exe.
if tool.contains("devenv") {
return impl_::find_devenv(target);
}
@@ -71,15 +84,16 @@ pub fn find_tool(target: &str, tool: &str) -> Option<Tool> {
// environment variables like `LIB`, `INCLUDE`, and `PATH` to ensure that
// the tool is actually usable.
- return impl_::find_msvc_environment(tool, target)
+ impl_::find_msvc_environment(tool, target)
.or_else(|| impl_::find_msvc_15plus(tool, target))
.or_else(|| impl_::find_msvc_14(tool, target))
.or_else(|| impl_::find_msvc_12(tool, target))
- .or_else(|| impl_::find_msvc_11(tool, target));
+ .or_else(|| impl_::find_msvc_11(tool, target))
}
/// A version of Visual Studio
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
+#[non_exhaustive]
pub enum VsVers {
/// Visual Studio 12 (2013)
Vs12,
@@ -91,30 +105,14 @@ pub enum VsVers {
Vs16,
/// Visual Studio 17 (2022)
Vs17,
-
- /// Hidden variant that should not be matched on. Callers that want to
- /// handle an enumeration of `VsVers` instances should always have a default
- /// case meaning that it's a VS version they don't understand.
- #[doc(hidden)]
- #[allow(bad_style)]
- __Nonexhaustive_do_not_match_this_or_your_code_will_break,
}
/// Find the most recent installed version of Visual Studio
///
/// This is used by the cmake crate to figure out the correct
/// generator.
-#[cfg(not(windows))]
pub fn find_vs_version() -> Result<VsVers, String> {
- Err(format!("not windows"))
-}
-
-/// Documented above
-#[cfg(windows)]
-pub fn find_vs_version() -> Result<VsVers, String> {
- use std::env;
-
- match env::var("VisualStudioVersion") {
+ match std::env::var("VisualStudioVersion") {
Ok(version) => match &version[..] {
"17.0" => Ok(VsVers::Vs17),
"16.0" => Ok(VsVers::Vs16),
@@ -158,12 +156,17 @@ pub fn find_vs_version() -> Result<VsVers, String> {
}
}
+/// Windows Implementation.
#[cfg(windows)]
mod impl_ {
- use crate::com;
- use crate::registry::{RegistryKey, LOCAL_MACHINE};
- use crate::setup_config::SetupConfiguration;
- use crate::vs_instances::{VsInstances, VswhereInstance};
+ use crate::windows::com;
+ use crate::windows::registry::{RegistryKey, LOCAL_MACHINE};
+ use crate::windows::setup_config::SetupConfiguration;
+ use crate::windows::vs_instances::{VsInstances, VswhereInstance};
+ use crate::windows::windows_sys::{
+ FreeLibrary, GetMachineTypeAttributes, GetProcAddress, LoadLibraryA, UserEnabled, HMODULE,
+ IMAGE_FILE_MACHINE_AMD64, MACHINE_ATTRIBUTES, S_OK,
+ };
use std::convert::TryFrom;
use std::env;
use std::ffi::OsString;
@@ -174,8 +177,10 @@ mod impl_ {
use std::path::{Path, PathBuf};
use std::process::Command;
use std::str::FromStr;
+ use std::sync::atomic::{AtomicBool, Ordering};
+ use std::sync::Once;
- use super::MSVC_FAMILY;
+ use super::{TargetArch, MSVC_FAMILY};
use crate::Tool;
struct MsvcTool {
@@ -185,10 +190,75 @@ mod impl_ {
include: Vec<PathBuf>,
}
+ struct LibraryHandle(HMODULE);
+
+ impl LibraryHandle {
+ fn new(name: &[u8]) -> Option<Self> {
+ let handle = unsafe { LoadLibraryA(name.as_ptr() as _) };
+ (!handle.is_null()).then(|| Self(handle))
+ }
+
+ /// Get a function pointer to a function in the library.
+ /// SAFETY: The caller must ensure that the function signature matches the actual function.
+ /// The easiest way to do this is to add an entry to windows_sys_no_link.list and use the
+ /// generated function for `func_signature`.
+ unsafe fn get_proc_address<F>(&self, name: &[u8]) -> Option<F> {
+ let symbol = unsafe { GetProcAddress(self.0, name.as_ptr() as _) };
+ symbol.map(|symbol| unsafe { mem::transmute_copy(&symbol) })
+ }
+ }
+
+ impl Drop for LibraryHandle {
+ fn drop(&mut self) {
+ unsafe { FreeLibrary(self.0) };
+ }
+ }
+
+ type GetMachineTypeAttributesFuncType =
+ unsafe extern "system" fn(u16, *mut MACHINE_ATTRIBUTES) -> i32;
+ const _: () = {
+ // Ensure that our hand-written signature matches the actual function signature.
+ // We can't use `GetMachineTypeAttributes` outside of a const scope otherwise we'll end up statically linking to
+ // it, which will fail to load on older versions of Windows.
+ let _: GetMachineTypeAttributesFuncType = GetMachineTypeAttributes;
+ };
+
+ fn is_amd64_emulation_supported_inner() -> Option<bool> {
+ // GetMachineTypeAttributes is only available on Win11 22000+, so dynamically load it.
+ let kernel32 = LibraryHandle::new(b"kernel32.dll\0")?;
+ // SAFETY: GetMachineTypeAttributesFuncType is checked to match the real function signature.
+ let get_machine_type_attributes = unsafe {
+ kernel32
+ .get_proc_address::<GetMachineTypeAttributesFuncType>(b"GetMachineTypeAttributes\0")
+ }?;
+ let mut attributes = Default::default();
+ if unsafe { get_machine_type_attributes(IMAGE_FILE_MACHINE_AMD64, &mut attributes) } == S_OK
+ {
+ Some((attributes & UserEnabled) != 0)
+ } else {
+ Some(false)
+ }
+ }
+
+ fn is_amd64_emulation_supported() -> bool {
+ // TODO: Replace with a OnceLock once MSRV is 1.70.
+ static LOAD_VALUE: Once = Once::new();
+ static IS_SUPPORTED: AtomicBool = AtomicBool::new(false);
+
+ // Using Relaxed ordering since the Once is providing synchronization.
+ LOAD_VALUE.call_once(|| {
+ IS_SUPPORTED.store(
+ is_amd64_emulation_supported_inner().unwrap_or(false),
+ Ordering::Relaxed,
+ );
+ });
+ IS_SUPPORTED.load(Ordering::Relaxed)
+ }
+
impl MsvcTool {
fn new(tool: PathBuf) -> MsvcTool {
MsvcTool {
- tool: tool,
+ tool,
libs: Vec::new(),
path: Vec::new(),
include: Vec::new(),
@@ -202,7 +272,7 @@ mod impl_ {
path,
include,
} = self;
- let mut tool = Tool::with_family(tool.into(), MSVC_FAMILY);
+ let mut tool = Tool::with_family(tool, MSVC_FAMILY);
add_env(&mut tool, "LIB", libs);
add_env(&mut tool, "PATH", path);
add_env(&mut tool, "INCLUDE", include);
@@ -212,15 +282,14 @@ mod impl_ {
/// Checks to see if the `VSCMD_ARG_TGT_ARCH` environment variable matches the
/// given target's arch. Returns `None` if the variable does not exist.
- #[cfg(windows)]
- fn is_vscmd_target(target: &str) -> Option<bool> {
+ fn is_vscmd_target(target: TargetArch<'_>) -> Option<bool> {
let vscmd_arch = env::var("VSCMD_ARG_TGT_ARCH").ok()?;
// Convert the Rust target arch to its VS arch equivalent.
- let arch = match target.split("-").next() {
- Some("x86_64") => "x64",
- Some("aarch64") => "arm64",
- Some("i686") | Some("i586") => "x86",
- Some("thumbv7a") => "arm",
+ let arch = match target.into() {
+ "x86_64" => "x64",
+ "aarch64" | "arm64ec" => "arm64",
+ "i686" | "i586" => "x86",
+ "thumbv7a" => "arm",
// An unrecognized arch.
_ => return Some(false),
};
@@ -228,7 +297,7 @@ mod impl_ {
}
/// Attempt to find the tool using environment variables set by vcvars.
- pub fn find_msvc_environment(tool: &str, target: &str) -> Option<Tool> {
+ pub(super) fn find_msvc_environment(tool: &str, target: TargetArch<'_>) -> Option<Tool> {
// Early return if the environment doesn't contain a VC install.
if env::var_os("VCINSTALLDIR").is_none() {
return None;
@@ -248,16 +317,19 @@ mod impl_ {
.map(|p| p.join(tool))
.find(|p| p.exists())
})
- .map(|path| Tool::with_family(path.into(), MSVC_FAMILY))
+ .map(|path| Tool::with_family(path, MSVC_FAMILY))
}
}
- fn find_msbuild_vs17(target: &str) -> Option<Tool> {
+ fn find_msbuild_vs17(target: TargetArch<'_>) -> Option<Tool> {
find_tool_in_vs16plus_path(r"MSBuild\Current\Bin\MSBuild.exe", target, "17")
}
#[allow(bare_trait_objects)]
- fn vs16plus_instances(target: &str, version: &'static str) -> Box<Iterator<Item = PathBuf>> {
+ fn vs16plus_instances(
+ target: TargetArch<'_>,
+ version: &'static str,
+ ) -> Box<Iterator<Item = PathBuf>> {
let instances = if let Some(instances) = vs15plus_instances(target) {
instances
} else {
@@ -275,7 +347,11 @@ mod impl_ {
}))
}
- fn find_tool_in_vs16plus_path(tool: &str, target: &str, version: &'static str) -> Option<Tool> {
+ fn find_tool_in_vs16plus_path(
+ tool: &str,
+ target: TargetArch<'_>,
+ version: &'static str,
+ ) -> Option<Tool> {
vs16plus_instances(target, version)
.filter_map(|path| {
let path = path.join(tool);
@@ -283,10 +359,10 @@ mod impl_ {
return None;
}
let mut tool = Tool::with_family(path, MSVC_FAMILY);
- if target.contains("x86_64") {
+ if target == "x86_64" {
tool.env.push(("Platform".into(), "X64".into()));
}
- if target.contains("aarch64") {
+ if target == "aarch64" || target == "arm64ec" {
tool.env.push(("Platform".into(), "ARM64".into()));
}
Some(tool)
@@ -294,7 +370,7 @@ mod impl_ {
.next()
}
- fn find_msbuild_vs16(target: &str) -> Option<Tool> {
+ fn find_msbuild_vs16(target: TargetArch<'_>) -> Option<Tool> {
find_tool_in_vs16plus_path(r"MSBuild\Current\Bin\MSBuild.exe", target, "16")
}
@@ -310,7 +386,7 @@ mod impl_ {
//
// However, on ARM64 this method doesn't work because VS Installer fails to register COM component on ARM64.
// Hence, as the last resort we try to use vswhere.exe to list available instances.
- fn vs15plus_instances(target: &str) -> Option<VsInstances> {
+ fn vs15plus_instances(target: TargetArch<'_>) -> Option<VsInstances> {
vs15plus_instances_using_com().or_else(|| vs15plus_instances_using_vswhere(target))
}
@@ -323,7 +399,7 @@ mod impl_ {
Some(VsInstances::ComBased(enum_setup_instances))
}
- fn vs15plus_instances_using_vswhere(target: &str) -> Option<VsInstances> {
+ fn vs15plus_instances_using_vswhere(target: TargetArch<'_>) -> Option<VsInstances> {
let program_files_path: PathBuf = env::var("ProgramFiles(x86)")
.or_else(|_| env::var("ProgramFiles"))
.ok()?
@@ -336,11 +412,10 @@ mod impl_ {
return None;
}
- let arch = target.split('-').next().unwrap();
- let tools_arch = match arch {
+ let tools_arch = match target.into() {
"i586" | "i686" | "x86_64" => Some("x86.x64"),
"arm" | "thumbv7a" => Some("ARM"),
- "aarch64" => Some("ARM64"),
+ "aarch64" | "arm64ec" => Some("ARM64"),
_ => None,
};
@@ -374,7 +449,7 @@ mod impl_ {
.collect()
}
- pub fn find_msvc_15plus(tool: &str, target: &str) -> Option<Tool> {
+ pub(super) fn find_msvc_15plus(tool: &str, target: TargetArch<'_>) -> Option<Tool> {
let iter = vs15plus_instances(target)?;
iter.into_iter()
.filter_map(|instance| {
@@ -394,13 +469,13 @@ mod impl_ {
// we keep the registry method as a fallback option.
//
// [more reliable]: https://github.com/rust-lang/cc-rs/pull/331
- fn find_tool_in_vs15_path(tool: &str, target: &str) -> Option<Tool> {
+ fn find_tool_in_vs15_path(tool: &str, target: TargetArch<'_>) -> Option<Tool> {
let mut path = match vs15plus_instances(target) {
Some(instances) => instances
.into_iter()
.filter_map(|instance| instance.installation_path())
.map(|path| path.join(tool))
- .find(|ref path| path.is_file()),
+ .find(|path| path.is_file()),
None => None,
};
@@ -416,10 +491,9 @@ mod impl_ {
path.map(|path| {
let mut tool = Tool::with_family(path, MSVC_FAMILY);
- if target.contains("x86_64") {
+ if target == "x86_64" {
tool.env.push(("Platform".into(), "X64".into()));
- }
- if target.contains("aarch64") {
+ } else if target == "aarch64" {
tool.env.push(("Platform".into(), "ARM64".into()));
}
tool
@@ -428,10 +502,10 @@ mod impl_ {
fn tool_from_vs15plus_instance(
tool: &str,
- target: &str,
+ target: TargetArch<'_>,
instance_path: &PathBuf,
) -> Option<Tool> {
- let (root_path, bin_path, host_dylib_path, lib_path, include_path) =
+ let (root_path, bin_path, host_dylib_path, lib_path, alt_lib_path, include_path) =
vs15plus_vc_paths(target, instance_path)?;
let tool_path = bin_path.join(tool);
if !tool_path.exists() {
@@ -441,6 +515,9 @@ mod impl_ {
let mut tool = MsvcTool::new(tool_path);
tool.path.push(bin_path.clone());
tool.path.push(host_dylib_path);
+ if let Some(alt_lib_path) = alt_lib_path {
+ tool.libs.push(alt_lib_path);
+ }
tool.libs.push(lib_path);
tool.include.push(include_path);
@@ -455,45 +532,97 @@ mod impl_ {
}
fn vs15plus_vc_paths(
- target: &str,
- instance_path: &PathBuf,
- ) -> Option<(PathBuf, PathBuf, PathBuf, PathBuf, PathBuf)> {
- let version_path =
- instance_path.join(r"VC\Auxiliary\Build\Microsoft.VCToolsVersion.default.txt");
- let mut version_file = File::open(version_path).ok()?;
- let mut version = String::new();
- version_file.read_to_string(&mut version).ok()?;
- let version = version.trim();
- let host = match host_arch() {
- X86 => "X86",
- X86_64 => "X64",
- // There is no natively hosted compiler on ARM64.
- // Instead, use the x86 toolchain under emulation (there is no x64 emulation).
- AARCH64 => "X86",
+ target: TargetArch<'_>,
+ instance_path: &Path,
+ ) -> Option<(PathBuf, PathBuf, PathBuf, PathBuf, Option<PathBuf>, PathBuf)> {
+ let version = vs15plus_vc_read_version(instance_path)?;
+
+ let hosts = match host_arch() {
+ X86 => &["X86"],
+ X86_64 => &["X64"],
+ // Starting with VS 17.4, there is a natively hosted compiler on ARM64:
+ // https://devblogs.microsoft.com/visualstudio/arm64-visual-studio-is-officially-here/
+ // On older versions of VS, we use x64 if running under emulation is supported,
+ // otherwise use x86.
+ AARCH64 => {
+ if is_amd64_emulation_supported() {
+ &["ARM64", "X64", "X86"][..]
+ } else {
+ &["ARM64", "X86"]
+ }
+ }
_ => return None,
};
let target = lib_subdir(target)?;
// The directory layout here is MSVC/bin/Host$host/$target/
let path = instance_path.join(r"VC\Tools\MSVC").join(version);
+ // We use the first available host architecture that can build for the target
+ let (host_path, host) = hosts.iter().find_map(|&x| {
+ let candidate = path.join("bin").join(format!("Host{}", x));
+ if candidate.join(target).exists() {
+ Some((candidate, x))
+ } else {
+ None
+ }
+ })?;
// This is the path to the toolchain for a particular target, running
// on a given host
- let bin_path = path
- .join("bin")
- .join(&format!("Host{}", host))
- .join(&target);
+ let bin_path = host_path.join(target);
// But! we also need PATH to contain the target directory for the host
// architecture, because it contains dlls like mspdb140.dll compiled for
// the host architecture.
- let host_dylib_path = path
- .join("bin")
- .join(&format!("Host{}", host))
- .join(&host.to_lowercase());
- let lib_path = path.join("lib").join(&target);
+ let host_dylib_path = host_path.join(host.to_lowercase());
+ let lib_path = path.join("lib").join(target);
+ let alt_lib_path = (target == "arm64ec").then(|| path.join("lib").join("arm64ec"));
let include_path = path.join("include");
- Some((path, bin_path, host_dylib_path, lib_path, include_path))
+ Some((
+ path,
+ bin_path,
+ host_dylib_path,
+ lib_path,
+ alt_lib_path,
+ include_path,
+ ))
}
- fn atl_paths(target: &str, path: &Path) -> Option<(PathBuf, PathBuf)> {
+ fn vs15plus_vc_read_version(dir: &Path) -> Option<String> {
+ // Try to open the default version file.
+ let mut version_path: PathBuf =
+ dir.join(r"VC\Auxiliary\Build\Microsoft.VCToolsVersion.default.txt");
+ let mut version_file = if let Ok(f) = File::open(&version_path) {
+ f
+ } else {
+ // If the default doesn't exist, search for other version files.
+ // These are in the form Microsoft.VCToolsVersion.v143.default.txt
+ // where `143` is any three decimal digit version number.
+ // This sorts versions by lexical order and selects the highest version.
+ let mut version_file = String::new();
+ version_path.pop();
+ for file in version_path.read_dir().ok()? {
+ let name = file.ok()?.file_name();
+ let name = name.to_str()?;
+ if name.starts_with("Microsoft.VCToolsVersion.v")
+ && name.ends_with(".default.txt")
+ && name > &version_file
+ {
+ version_file.replace_range(.., name);
+ }
+ }
+ if version_file.is_empty() {
+ return None;
+ }
+ version_path.push(version_file);
+ File::open(version_path).ok()?
+ };
+
+ // Get the version string from the file we found.
+ let mut version = String::new();
+ version_file.read_to_string(&mut version).ok()?;
+ version.truncate(version.trim_end().len());
+ Some(version)
+ }
+
+ fn atl_paths(target: TargetArch<'_>, path: &Path) -> Option<(PathBuf, PathBuf)> {
let atl_path = path.join("atlmfc");
let sub = lib_subdir(target)?;
if atl_path.exists() {
@@ -505,14 +634,14 @@ mod impl_ {
// For MSVC 14 we need to find the Universal CRT as well as either
// the Windows 10 SDK or Windows 8.1 SDK.
- pub fn find_msvc_14(tool: &str, target: &str) -> Option<Tool> {
+ pub(super) fn find_msvc_14(tool: &str, target: TargetArch<'_>) -> Option<Tool> {
let vcdir = get_vc_dir("14.0")?;
let mut tool = get_tool(tool, &vcdir, target)?;
add_sdks(&mut tool, target)?;
Some(tool.into_tool())
}
- fn add_sdks(tool: &mut MsvcTool, target: &str) -> Option<()> {
+ fn add_sdks(tool: &mut MsvcTool, target: TargetArch<'_>) -> Option<()> {
let sub = lib_subdir(target)?;
let (ucrt, ucrt_version) = get_ucrt_dir()?;
@@ -555,7 +684,7 @@ mod impl_ {
}
// For MSVC 12 we need to find the Windows 8.1 SDK.
- pub fn find_msvc_12(tool: &str, target: &str) -> Option<Tool> {
+ pub(super) fn find_msvc_12(tool: &str, target: TargetArch<'_>) -> Option<Tool> {
let vcdir = get_vc_dir("12.0")?;
let mut tool = get_tool(tool, &vcdir, target)?;
let sub = lib_subdir(target)?;
@@ -571,7 +700,7 @@ mod impl_ {
}
// For MSVC 11 we need to find the Windows 8 SDK.
- pub fn find_msvc_11(tool: &str, target: &str) -> Option<Tool> {
+ pub(super) fn find_msvc_11(tool: &str, target: TargetArch<'_>) -> Option<Tool> {
let vcdir = get_vc_dir("11.0")?;
let mut tool = get_tool(tool, &vcdir, target)?;
let sub = lib_subdir(target)?;
@@ -596,7 +725,7 @@ mod impl_ {
// Given a possible MSVC installation directory, we look for the linker and
// then add the MSVC library path.
- fn get_tool(tool: &str, path: &Path, target: &str) -> Option<MsvcTool> {
+ fn get_tool(tool: &str, path: &Path, target: TargetArch<'_>) -> Option<MsvcTool> {
bin_subdir(target)
.into_iter()
.map(|(sub, host)| {
@@ -605,7 +734,7 @@ mod impl_ {
path.join("bin").join(host),
)
})
- .filter(|&(ref path, _)| path.is_file())
+ .filter(|(path, _)| path.is_file())
.map(|(path, host)| {
let mut tool = MsvcTool::new(path);
tool.path.push(host);
@@ -734,9 +863,8 @@ mod impl_ {
// linkers that can target the architecture we desire. The 64-bit host
// linker is preferred, and hence first, due to 64-bit allowing it more
// address space to work with and potentially being faster.
- fn bin_subdir(target: &str) -> Vec<(&'static str, &'static str)> {
- let arch = target.split('-').next().unwrap();
- match (arch, host_arch()) {
+ fn bin_subdir(target: TargetArch<'_>) -> Vec<(&'static str, &'static str)> {
+ match (target.into(), host_arch()) {
("i586", X86) | ("i686", X86) => vec![("", "")],
("i586", X86_64) | ("i686", X86_64) => vec![("amd64_x86", "amd64"), ("", "")],
("x86_64", X86) => vec![("x86_amd64", "")],
@@ -747,21 +875,19 @@ mod impl_ {
}
}
- fn lib_subdir(target: &str) -> Option<&'static str> {
- let arch = target.split('-').next().unwrap();
- match arch {
+ fn lib_subdir(target: TargetArch<'_>) -> Option<&'static str> {
+ match target.into() {
"i586" | "i686" => Some("x86"),
"x86_64" => Some("x64"),
"arm" | "thumbv7a" => Some("arm"),
- "aarch64" => Some("arm64"),
+ "aarch64" | "arm64ec" => Some("arm64"),
_ => None,
}
}
// MSVC's x86 libraries are not in a subfolder
- fn vc_lib_subdir(target: &str) -> Option<&'static str> {
- let arch = target.split('-').next().unwrap();
- match arch {
+ fn vc_lib_subdir(target: TargetArch<'_>) -> Option<&'static str> {
+ match target.into() {
"i586" | "i686" => Some(""),
"x86_64" => Some("amd64"),
"arm" | "thumbv7a" => Some("arm"),
@@ -813,7 +939,7 @@ mod impl_ {
for subkey in key.iter().filter_map(|k| k.ok()) {
let val = subkey
.to_str()
- .and_then(|s| s.trim_left_matches("v").replace(".", "").parse().ok());
+ .and_then(|s| s.trim_left_matches("v").replace('.', "").parse().ok());
let val = match val {
Some(s) => s,
None => continue,
@@ -828,22 +954,22 @@ mod impl_ {
max_key
}
- pub fn has_msbuild_version(version: &str) -> bool {
+ pub(super) fn has_msbuild_version(version: &str) -> bool {
match version {
"17.0" => {
- find_msbuild_vs17("x86_64-pc-windows-msvc").is_some()
- || find_msbuild_vs17("i686-pc-windows-msvc").is_some()
- || find_msbuild_vs17("aarch64-pc-windows-msvc").is_some()
+ find_msbuild_vs17(TargetArch("x86_64")).is_some()
+ || find_msbuild_vs17(TargetArch("i686")).is_some()
+ || find_msbuild_vs17(TargetArch("aarch64")).is_some()
}
"16.0" => {
- find_msbuild_vs16("x86_64-pc-windows-msvc").is_some()
- || find_msbuild_vs16("i686-pc-windows-msvc").is_some()
- || find_msbuild_vs16("aarch64-pc-windows-msvc").is_some()
+ find_msbuild_vs16(TargetArch("x86_64")).is_some()
+ || find_msbuild_vs16(TargetArch("i686")).is_some()
+ || find_msbuild_vs16(TargetArch("aarch64")).is_some()
}
"15.0" => {
- find_msbuild_vs15("x86_64-pc-windows-msvc").is_some()
- || find_msbuild_vs15("i686-pc-windows-msvc").is_some()
- || find_msbuild_vs15("aarch64-pc-windows-msvc").is_some()
+ find_msbuild_vs15(TargetArch("x86_64")).is_some()
+ || find_msbuild_vs15(TargetArch("i686")).is_some()
+ || find_msbuild_vs15(TargetArch("aarch64")).is_some()
}
"12.0" | "14.0" => LOCAL_MACHINE
.open(&OsString::from(format!(
@@ -855,18 +981,20 @@ mod impl_ {
}
}
- pub fn find_devenv(target: &str) -> Option<Tool> {
- find_devenv_vs15(&target)
+ pub(super) fn find_devenv(target: TargetArch<'_>) -> Option<Tool> {
+ find_devenv_vs15(target)
}
- fn find_devenv_vs15(target: &str) -> Option<Tool> {
+ fn find_devenv_vs15(target: TargetArch<'_>) -> Option<Tool> {
find_tool_in_vs15_path(r"Common7\IDE\devenv.exe", target)
}
// see http://stackoverflow.com/questions/328017/path-to-msbuild
- pub fn find_msbuild(target: &str) -> Option<Tool> {
+ pub(super) fn find_msbuild(target: TargetArch<'_>) -> Option<Tool> {
// VS 15 (2017) changed how to locate msbuild
- if let Some(r) = find_msbuild_vs16(target) {
+ if let Some(r) = find_msbuild_vs17(target) {
+ Some(r)
+ } else if let Some(r) = find_msbuild_vs16(target) {
return Some(r);
} else if let Some(r) = find_msbuild_vs15(target) {
return Some(r);
@@ -875,11 +1003,11 @@ mod impl_ {
}
}
- fn find_msbuild_vs15(target: &str) -> Option<Tool> {
+ fn find_msbuild_vs15(target: TargetArch<'_>) -> Option<Tool> {
find_tool_in_vs15_path(r"MSBuild\15.0\Bin\MSBuild.exe", target)
}
- fn find_old_msbuild(target: &str) -> Option<Tool> {
+ fn find_old_msbuild(target: TargetArch<'_>) -> Option<Tool> {
let key = r"SOFTWARE\Microsoft\MSBuild\ToolsVersions";
LOCAL_MACHINE
.open(key.as_ref())
@@ -891,10 +1019,82 @@ mod impl_ {
let mut path = PathBuf::from(path);
path.push("MSBuild.exe");
let mut tool = Tool::with_family(path, MSVC_FAMILY);
- if target.contains("x86_64") {
+ if target == "x86_64" {
tool.env.push(("Platform".into(), "X64".into()));
}
tool
})
}
}
+
+/// Non-Windows Implementation.
+#[cfg(not(windows))]
+mod impl_ {
+ use std::{env, ffi::OsString};
+
+ use super::{TargetArch, MSVC_FAMILY};
+ use crate::Tool;
+
+ /// Finding msbuild.exe tool under unix system is not currently supported.
+ /// Maybe can check it using an environment variable looks like `MSBUILD_BIN`.
+ pub(super) fn find_msbuild(_target: TargetArch<'_>) -> Option<Tool> {
+ None
+ }
+
+ // Finding devenv.exe tool under unix system is not currently supported.
+ // Maybe can check it using an environment variable looks like `DEVENV_BIN`.
+ pub(super) fn find_devenv(_target: TargetArch<'_>) -> Option<Tool> {
+ None
+ }
+
+ /// Attempt to find the tool using environment variables set by vcvars.
+ pub(super) fn find_msvc_environment(tool: &str, _target: TargetArch<'_>) -> Option<Tool> {
+ // Early return if the environment doesn't contain a VC install.
+ let vc_install_dir = env::var_os("VCINSTALLDIR")?;
+ let vs_install_dir = env::var_os("VSINSTALLDIR")?;
+
+ let get_tool = |install_dir: OsString| {
+ env::split_paths(&install_dir)
+ .map(|p| p.join(tool))
+ .find(|p| p.exists())
+ .map(|path| Tool::with_family(path.into(), MSVC_FAMILY))
+ };
+
+ // Take the path of tool for the vc install directory.
+ get_tool(vc_install_dir)
+ // Take the path of tool for the vs install directory.
+ .or_else(|| get_tool(vs_install_dir))
+ // Take the path of tool for the current path environment.
+ .or_else(|| env::var_os("PATH").and_then(|path| get_tool(path)))
+ }
+
+ pub(super) fn find_msvc_15plus(_tool: &str, _target: TargetArch<'_>) -> Option<Tool> {
+ None
+ }
+
+ // For MSVC 14 we need to find the Universal CRT as well as either
+ // the Windows 10 SDK or Windows 8.1 SDK.
+ pub(super) fn find_msvc_14(_tool: &str, _target: TargetArch<'_>) -> Option<Tool> {
+ None
+ }
+
+ // For MSVC 12 we need to find the Windows 8.1 SDK.
+ pub(super) fn find_msvc_12(_tool: &str, _target: TargetArch<'_>) -> Option<Tool> {
+ None
+ }
+
+ // For MSVC 11 we need to find the Windows 8 SDK.
+ pub(super) fn find_msvc_11(_tool: &str, _target: TargetArch<'_>) -> Option<Tool> {
+ None
+ }
+
+ pub(super) fn has_msbuild_version(version: &str) -> bool {
+ match version {
+ "17.0" => false,
+ "16.0" => false,
+ "15.0" => false,
+ "12.0" | "14.0" => false,
+ _ => false,
+ }
+ }
+}
diff --git a/third_party/rust/cc/src/windows/mod.rs b/third_party/rust/cc/src/windows/mod.rs
new file mode 100644
index 0000000000..9b6f297e1a
--- /dev/null
+++ b/third_party/rust/cc/src/windows/mod.rs
@@ -0,0 +1,20 @@
+//! These modules are all glue to support reading the MSVC version from
+//! the registry and from COM interfaces.
+
+// This is used in the crate's public API, so don't use #[cfg(windows)]
+pub mod find_tools;
+
+#[cfg(windows)]
+pub(crate) mod windows_sys;
+
+#[cfg(windows)]
+mod registry;
+#[cfg(windows)]
+#[macro_use]
+mod winapi;
+#[cfg(windows)]
+mod com;
+#[cfg(windows)]
+mod setup_config;
+#[cfg(windows)]
+mod vs_instances;
diff --git a/third_party/rust/cc/src/registry.rs b/third_party/rust/cc/src/windows/registry.rs
index cae32219c7..83983032de 100644
--- a/third_party/rust/cc/src/registry.rs
+++ b/third_party/rust/cc/src/windows/registry.rs
@@ -8,63 +8,23 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-use std::ffi::{OsStr, OsString};
-use std::io;
-use std::ops::RangeFrom;
-use std::os::raw;
-use std::os::windows::prelude::*;
+use crate::windows::windows_sys::{
+ RegCloseKey, RegEnumKeyExW, RegOpenKeyExW, RegQueryValueExW, ERROR_NO_MORE_ITEMS,
+ ERROR_SUCCESS, HKEY, HKEY_LOCAL_MACHINE, KEY_READ, KEY_WOW64_32KEY, REG_SZ,
+};
+use std::{
+ ffi::{OsStr, OsString},
+ io,
+ ops::RangeFrom,
+ os::windows::prelude::*,
+ ptr::null_mut,
+};
/// Must never be `HKEY_PERFORMANCE_DATA`.
pub(crate) struct RegistryKey(Repr);
-type HKEY = *mut u8;
+#[allow(clippy::upper_case_acronyms)]
type DWORD = u32;
-type LPDWORD = *mut DWORD;
-type LPCWSTR = *const u16;
-type LPWSTR = *mut u16;
-type LONG = raw::c_long;
-type PHKEY = *mut HKEY;
-type PFILETIME = *mut u8;
-type LPBYTE = *mut u8;
-type REGSAM = u32;
-
-const ERROR_SUCCESS: DWORD = 0;
-const ERROR_NO_MORE_ITEMS: DWORD = 259;
-// Sign-extend into 64 bits if needed.
-const HKEY_LOCAL_MACHINE: HKEY = 0x80000002u32 as i32 as isize as HKEY;
-const REG_SZ: DWORD = 1;
-const KEY_READ: DWORD = 0x20019;
-const KEY_WOW64_32KEY: DWORD = 0x200;
-
-#[link(name = "advapi32")]
-extern "system" {
- fn RegOpenKeyExW(
- key: HKEY,
- lpSubKey: LPCWSTR,
- ulOptions: DWORD,
- samDesired: REGSAM,
- phkResult: PHKEY,
- ) -> LONG;
- fn RegEnumKeyExW(
- key: HKEY,
- dwIndex: DWORD,
- lpName: LPWSTR,
- lpcName: LPDWORD,
- lpReserved: LPDWORD,
- lpClass: LPWSTR,
- lpcClass: LPDWORD,
- lpftLastWriteTime: PFILETIME,
- ) -> LONG;
- fn RegQueryValueExW(
- hKey: HKEY,
- lpValueName: LPCWSTR,
- lpReserved: LPDWORD,
- lpType: LPDWORD,
- lpData: LPBYTE,
- lpcbData: LPDWORD,
- ) -> LONG;
- fn RegCloseKey(hKey: HKEY) -> LONG;
-}
struct OwnedKey(HKEY);
@@ -97,7 +57,7 @@ impl RegistryKey {
/// Open a sub-key of `self`.
pub fn open(&self, key: &OsStr) -> io::Result<RegistryKey> {
let key = key.encode_wide().chain(Some(0)).collect::<Vec<_>>();
- let mut ret = 0 as *mut _;
+ let mut ret = null_mut();
let err = unsafe {
RegOpenKeyExW(
self.raw(),
@@ -107,7 +67,7 @@ impl RegistryKey {
&mut ret,
)
};
- if err == ERROR_SUCCESS as LONG {
+ if err == ERROR_SUCCESS {
Ok(RegistryKey(Repr::Owned(OwnedKey(ret))))
} else {
Err(io::Error::from_raw_os_error(err as i32))
@@ -130,12 +90,12 @@ impl RegistryKey {
let err = RegQueryValueExW(
self.raw(),
name.as_ptr(),
- 0 as *mut _,
+ null_mut(),
&mut kind,
- 0 as *mut _,
+ null_mut(),
&mut len,
);
- if err != ERROR_SUCCESS as LONG {
+ if err != ERROR_SUCCESS {
return Err(io::Error::from_raw_os_error(err as i32));
}
if kind != REG_SZ {
@@ -156,8 +116,8 @@ impl RegistryKey {
let err = RegQueryValueExW(
self.raw(),
name.as_ptr(),
- 0 as *mut _,
- 0 as *mut _,
+ null_mut(),
+ null_mut(),
v.as_mut_ptr() as *mut _,
&mut len,
);
@@ -165,7 +125,7 @@ impl RegistryKey {
// grew between the first and second call to `RegQueryValueExW`),
// both because it's extremely unlikely, and this is a bit more
// defensive more defensive against weird types of registry keys.
- if err != ERROR_SUCCESS as LONG {
+ if err != ERROR_SUCCESS {
return Err(io::Error::from_raw_os_error(err as i32));
}
// The length is allowed to change, but should still be even, as
@@ -188,7 +148,7 @@ impl RegistryKey {
if !v.is_empty() && v[v.len() - 1] == 0 {
v.pop();
}
- return Ok(OsString::from_wide(&v));
+ Ok(OsString::from_wide(&v))
}
}
}
@@ -213,14 +173,14 @@ impl<'a> Iterator for Iter<'a> {
i,
v.as_mut_ptr(),
&mut len,
- 0 as *mut _,
- 0 as *mut _,
- 0 as *mut _,
- 0 as *mut _,
+ null_mut(),
+ null_mut(),
+ null_mut(),
+ null_mut(),
);
- if ret == ERROR_NO_MORE_ITEMS as LONG {
+ if ret == ERROR_NO_MORE_ITEMS {
None
- } else if ret != ERROR_SUCCESS as LONG {
+ } else if ret != ERROR_SUCCESS {
Some(Err(io::Error::from_raw_os_error(ret as i32)))
} else {
v.set_len(len as usize);
diff --git a/third_party/rust/cc/src/setup_config.rs b/third_party/rust/cc/src/windows/setup_config.rs
index 030051ca69..5739ecf7d6 100644
--- a/third_party/rust/cc/src/setup_config.rs
+++ b/third_party/rust/cc/src/windows/setup_config.rs
@@ -8,19 +8,19 @@
#![allow(bad_style)]
#![allow(unused)]
-use crate::winapi::Interface;
-use crate::winapi::BSTR;
-use crate::winapi::LPCOLESTR;
-use crate::winapi::LPSAFEARRAY;
-use crate::winapi::S_FALSE;
-use crate::winapi::{CoCreateInstance, CLSCTX_ALL};
-use crate::winapi::{IUnknown, IUnknownVtbl};
-use crate::winapi::{HRESULT, LCID, LPCWSTR, PULONGLONG};
-use crate::winapi::{LPFILETIME, ULONG};
-use std::ffi::OsString;
-use std::ptr::null_mut;
+use crate::windows::{
+ com::{BStr, ComPtr},
+ winapi::{
+ IUnknown, IUnknownVtbl, Interface, LCID, LPCOLESTR, LPCWSTR, LPFILETIME, LPSAFEARRAY,
+ PULONGLONG, ULONG,
+ },
+ windows_sys::{CoCreateInstance, BSTR, CLSCTX_ALL, HRESULT, S_FALSE},
+};
-use crate::com::{BStr, ComPtr};
+use std::{
+ ffi::OsString,
+ ptr::{null, null_mut},
+};
// Bindings to the Setup.Configuration stuff
pub type InstanceState = u32;
@@ -212,7 +212,7 @@ impl SetupInstance {
SetupInstance(ComPtr::from_raw(obj))
}
pub fn instance_id(&self) -> Result<OsString, i32> {
- let mut s = null_mut();
+ let mut s = null();
let err = unsafe { self.0.GetInstanceId(&mut s) };
let bstr = unsafe { BStr::from_raw(s) };
if err < 0 {
@@ -221,7 +221,7 @@ impl SetupInstance {
Ok(bstr.to_osstring())
}
pub fn installation_name(&self) -> Result<OsString, i32> {
- let mut s = null_mut();
+ let mut s = null();
let err = unsafe { self.0.GetInstallationName(&mut s) };
let bstr = unsafe { BStr::from_raw(s) };
if err < 0 {
@@ -230,7 +230,7 @@ impl SetupInstance {
Ok(bstr.to_osstring())
}
pub fn installation_path(&self) -> Result<OsString, i32> {
- let mut s = null_mut();
+ let mut s = null();
let err = unsafe { self.0.GetInstallationPath(&mut s) };
let bstr = unsafe { BStr::from_raw(s) };
if err < 0 {
@@ -239,7 +239,7 @@ impl SetupInstance {
Ok(bstr.to_osstring())
}
pub fn installation_version(&self) -> Result<OsString, i32> {
- let mut s = null_mut();
+ let mut s = null();
let err = unsafe { self.0.GetInstallationVersion(&mut s) };
let bstr = unsafe { BStr::from_raw(s) };
if err < 0 {
@@ -248,7 +248,7 @@ impl SetupInstance {
Ok(bstr.to_osstring())
}
pub fn product_path(&self) -> Result<OsString, i32> {
- let mut s = null_mut();
+ let mut s = null();
let this = self.0.cast::<ISetupInstance2>()?;
let err = unsafe { this.GetProductPath(&mut s) };
let bstr = unsafe { BStr::from_raw(s) };
diff --git a/third_party/rust/cc/src/vs_instances.rs b/third_party/rust/cc/src/windows/vs_instances.rs
index 31d3dd1470..e863dadabb 100644
--- a/third_party/rust/cc/src/vs_instances.rs
+++ b/third_party/rust/cc/src/windows/vs_instances.rs
@@ -4,7 +4,7 @@ use std::convert::TryFrom;
use std::io::BufRead;
use std::path::PathBuf;
-use crate::setup_config::{EnumSetupInstances, SetupInstance};
+use crate::windows::setup_config::{EnumSetupInstances, SetupInstance};
pub enum VsInstance {
Com(SetupInstance),
diff --git a/third_party/rust/cc/src/winapi.rs b/third_party/rust/cc/src/windows/winapi.rs
index 8e04ce9cbd..09965daa89 100644
--- a/third_party/rust/cc/src/winapi.rs
+++ b/third_party/rust/cc/src/windows/winapi.rs
@@ -5,26 +5,19 @@
// All files in the project carrying such notice may not be copied, modified, or distributed
// except according to those terms.
-#![allow(bad_style)]
+#![allow(bad_style, clippy::upper_case_acronyms)]
use std::os::raw;
pub type wchar_t = u16;
-pub type UINT = raw::c_uint;
-pub type LPUNKNOWN = *mut IUnknown;
+pub use crate::windows::windows_sys::{FILETIME, GUID, HRESULT, SAFEARRAY};
+
pub type REFIID = *const IID;
pub type IID = GUID;
-pub type REFCLSID = *const IID;
-pub type PVOID = *mut raw::c_void;
-pub type USHORT = raw::c_ushort;
pub type ULONG = raw::c_ulong;
-pub type LONG = raw::c_long;
pub type DWORD = u32;
-pub type LPVOID = *mut raw::c_void;
-pub type HRESULT = raw::c_long;
pub type LPFILETIME = *mut FILETIME;
-pub type BSTR = *mut OLECHAR;
pub type OLECHAR = WCHAR;
pub type WCHAR = wchar_t;
pub type LPCOLESTR = *const OLECHAR;
@@ -33,75 +26,10 @@ pub type LPCWSTR = *const WCHAR;
pub type PULONGLONG = *mut ULONGLONG;
pub type ULONGLONG = u64;
-pub const S_OK: HRESULT = 0;
-pub const S_FALSE: HRESULT = 1;
-pub const COINIT_MULTITHREADED: u32 = 0x0;
-
-pub type CLSCTX = u32;
-
-pub const CLSCTX_INPROC_SERVER: CLSCTX = 0x1;
-pub const CLSCTX_INPROC_HANDLER: CLSCTX = 0x2;
-pub const CLSCTX_LOCAL_SERVER: CLSCTX = 0x4;
-pub const CLSCTX_REMOTE_SERVER: CLSCTX = 0x10;
-
-pub const CLSCTX_ALL: CLSCTX =
- CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER | CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER;
-
-#[repr(C)]
-#[derive(Copy, Clone)]
-pub struct GUID {
- pub Data1: raw::c_ulong,
- pub Data2: raw::c_ushort,
- pub Data3: raw::c_ushort,
- pub Data4: [raw::c_uchar; 8],
-}
-
-#[repr(C)]
-#[derive(Copy, Clone)]
-pub struct FILETIME {
- pub dwLowDateTime: DWORD,
- pub dwHighDateTime: DWORD,
-}
-
pub trait Interface {
fn uuidof() -> GUID;
}
-#[link(name = "ole32")]
-#[link(name = "oleaut32")]
-extern "C" {}
-
-extern "system" {
- pub fn CoInitializeEx(pvReserved: LPVOID, dwCoInit: DWORD) -> HRESULT;
- pub fn CoCreateInstance(
- rclsid: REFCLSID,
- pUnkOuter: LPUNKNOWN,
- dwClsContext: DWORD,
- riid: REFIID,
- ppv: *mut LPVOID,
- ) -> HRESULT;
- pub fn SysFreeString(bstrString: BSTR);
- pub fn SysStringLen(pbstr: BSTR) -> UINT;
-}
-
-#[repr(C)]
-#[derive(Copy, Clone)]
-pub struct SAFEARRAYBOUND {
- pub cElements: ULONG,
- pub lLbound: LONG,
-}
-
-#[repr(C)]
-#[derive(Copy, Clone)]
-pub struct SAFEARRAY {
- pub cDims: USHORT,
- pub fFeatures: USHORT,
- pub cbElements: ULONG,
- pub cLocks: ULONG,
- pub pvData: PVOID,
- pub rgsabound: [SAFEARRAYBOUND; 1],
-}
-
pub type LPSAFEARRAY = *mut SAFEARRAY;
macro_rules! DEFINE_GUID {
@@ -109,11 +37,11 @@ macro_rules! DEFINE_GUID {
$name:ident, $l:expr, $w1:expr, $w2:expr,
$b1:expr, $b2:expr, $b3:expr, $b4:expr, $b5:expr, $b6:expr, $b7:expr, $b8:expr
) => {
- pub const $name: $crate::winapi::GUID = $crate::winapi::GUID {
- Data1: $l,
- Data2: $w1,
- Data3: $w2,
- Data4: [$b1, $b2, $b3, $b4, $b5, $b6, $b7, $b8],
+ pub const $name: $crate::windows::winapi::GUID = $crate::windows::winapi::GUID {
+ data1: $l,
+ data2: $w1,
+ data3: $w2,
+ data4: [$b1, $b2, $b3, $b4, $b5, $b6, $b7, $b8],
};
};
}
@@ -193,14 +121,14 @@ macro_rules! RIDL {
$l:expr, $w1:expr, $w2:expr,
$b1:expr, $b2:expr, $b3:expr, $b4:expr, $b5:expr, $b6:expr, $b7:expr, $b8:expr
) => (
- impl $crate::winapi::Interface for $interface {
+ impl $crate::windows::winapi::Interface for $interface {
#[inline]
- fn uuidof() -> $crate::winapi::GUID {
- $crate::winapi::GUID {
- Data1: $l,
- Data2: $w1,
- Data3: $w2,
- Data4: [$b1, $b2, $b3, $b4, $b5, $b6, $b7, $b8],
+ fn uuidof() -> $crate::windows::winapi::GUID {
+ $crate::windows::winapi::GUID {
+ data1: $l,
+ data2: $w1,
+ data3: $w2,
+ data4: [$b1, $b2, $b3, $b4, $b5, $b6, $b7, $b8],
}
}
}
diff --git a/third_party/rust/cc/src/windows/windows_sys.rs b/third_party/rust/cc/src/windows/windows_sys.rs
new file mode 100644
index 0000000000..8b98ce97f8
--- /dev/null
+++ b/third_party/rust/cc/src/windows/windows_sys.rs
@@ -0,0 +1,223 @@
+// This file is autogenerated.
+//
+// To add bindings, edit windows_sys.lst then run:
+//
+// ```
+// cd generate-windows-sys/
+// cargo run
+// ```
+// Bindings generated by `windows-bindgen` 0.53.0
+
+#![allow(
+ non_snake_case,
+ non_upper_case_globals,
+ non_camel_case_types,
+ dead_code,
+ clippy::all
+)]
+#[link(name = "advapi32")]
+extern "system" {
+ pub fn RegCloseKey(hkey: HKEY) -> WIN32_ERROR;
+}
+#[link(name = "advapi32")]
+extern "system" {
+ pub fn RegEnumKeyExW(
+ hkey: HKEY,
+ dwindex: u32,
+ lpname: PWSTR,
+ lpcchname: *mut u32,
+ lpreserved: *const u32,
+ lpclass: PWSTR,
+ lpcchclass: *mut u32,
+ lpftlastwritetime: *mut FILETIME,
+ ) -> WIN32_ERROR;
+}
+#[link(name = "advapi32")]
+extern "system" {
+ pub fn RegOpenKeyExW(
+ hkey: HKEY,
+ lpsubkey: PCWSTR,
+ uloptions: u32,
+ samdesired: REG_SAM_FLAGS,
+ phkresult: *mut HKEY,
+ ) -> WIN32_ERROR;
+}
+#[link(name = "advapi32")]
+extern "system" {
+ pub fn RegQueryValueExW(
+ hkey: HKEY,
+ lpvaluename: PCWSTR,
+ lpreserved: *const u32,
+ lptype: *mut REG_VALUE_TYPE,
+ lpdata: *mut u8,
+ lpcbdata: *mut u32,
+ ) -> WIN32_ERROR;
+}
+#[link(name = "kernel32")]
+extern "system" {
+ pub fn FreeLibrary(hlibmodule: HMODULE) -> BOOL;
+}
+#[link(name = "kernel32")]
+extern "system" {
+ pub fn GetMachineTypeAttributes(
+ machine: u16,
+ machinetypeattributes: *mut MACHINE_ATTRIBUTES,
+ ) -> HRESULT;
+}
+#[link(name = "kernel32")]
+extern "system" {
+ pub fn GetProcAddress(hmodule: HMODULE, lpprocname: PCSTR) -> FARPROC;
+}
+#[link(name = "kernel32")]
+extern "system" {
+ pub fn LoadLibraryA(lplibfilename: PCSTR) -> HMODULE;
+}
+#[link(name = "kernel32")]
+extern "system" {
+ pub fn OpenSemaphoreA(dwdesiredaccess: u32, binherithandle: BOOL, lpname: PCSTR) -> HANDLE;
+}
+#[link(name = "kernel32")]
+extern "system" {
+ pub fn PeekNamedPipe(
+ hnamedpipe: HANDLE,
+ lpbuffer: *mut ::core::ffi::c_void,
+ nbuffersize: u32,
+ lpbytesread: *mut u32,
+ lptotalbytesavail: *mut u32,
+ lpbytesleftthismessage: *mut u32,
+ ) -> BOOL;
+}
+#[link(name = "kernel32")]
+extern "system" {
+ pub fn ReleaseSemaphore(
+ hsemaphore: HANDLE,
+ lreleasecount: i32,
+ lppreviouscount: *mut i32,
+ ) -> BOOL;
+}
+#[link(name = "kernel32")]
+extern "system" {
+ pub fn WaitForSingleObject(hhandle: HANDLE, dwmilliseconds: u32) -> WAIT_EVENT;
+}
+#[link(name = "ole32")]
+extern "system" {
+ pub fn CoCreateInstance(
+ rclsid: *const GUID,
+ punkouter: *mut ::core::ffi::c_void,
+ dwclscontext: CLSCTX,
+ riid: *const GUID,
+ ppv: *mut *mut ::core::ffi::c_void,
+ ) -> HRESULT;
+}
+#[link(name = "ole32")]
+extern "system" {
+ pub fn CoInitializeEx(pvreserved: *const ::core::ffi::c_void, dwcoinit: u32) -> HRESULT;
+}
+#[link(name = "oleaut32")]
+extern "system" {
+ pub fn SysFreeString(bstrstring: BSTR);
+}
+#[link(name = "oleaut32")]
+extern "system" {
+ pub fn SysStringLen(pbstr: BSTR) -> u32;
+}
+pub type ADVANCED_FEATURE_FLAGS = u16;
+pub type BOOL = i32;
+pub type BSTR = *const u16;
+pub type CLSCTX = u32;
+pub const CLSCTX_ALL: CLSCTX = 23u32;
+pub type COINIT = i32;
+pub const COINIT_MULTITHREADED: COINIT = 0i32;
+pub const ERROR_NO_MORE_ITEMS: WIN32_ERROR = 259u32;
+pub const ERROR_SUCCESS: WIN32_ERROR = 0u32;
+pub const FALSE: BOOL = 0i32;
+pub type FARPROC = ::core::option::Option<unsafe extern "system" fn() -> isize>;
+#[repr(C)]
+pub struct FILETIME {
+ pub dwLowDateTime: u32,
+ pub dwHighDateTime: u32,
+}
+impl ::core::marker::Copy for FILETIME {}
+impl ::core::clone::Clone for FILETIME {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+#[repr(C)]
+pub struct GUID {
+ pub data1: u32,
+ pub data2: u16,
+ pub data3: u16,
+ pub data4: [u8; 8],
+}
+impl ::core::marker::Copy for GUID {}
+impl ::core::clone::Clone for GUID {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+impl GUID {
+ pub const fn from_u128(uuid: u128) -> Self {
+ Self {
+ data1: (uuid >> 96) as u32,
+ data2: (uuid >> 80 & 0xffff) as u16,
+ data3: (uuid >> 64 & 0xffff) as u16,
+ data4: (uuid as u64).to_be_bytes(),
+ }
+ }
+}
+pub type HANDLE = *mut ::core::ffi::c_void;
+pub type HKEY = *mut ::core::ffi::c_void;
+pub const HKEY_LOCAL_MACHINE: HKEY = -2147483646i32 as _;
+pub type HMODULE = *mut ::core::ffi::c_void;
+pub type HRESULT = i32;
+pub type IMAGE_FILE_MACHINE = u16;
+pub const IMAGE_FILE_MACHINE_AMD64: IMAGE_FILE_MACHINE = 34404u16;
+pub const KEY_READ: REG_SAM_FLAGS = 131097u32;
+pub const KEY_WOW64_32KEY: REG_SAM_FLAGS = 512u32;
+pub type MACHINE_ATTRIBUTES = i32;
+pub type PCSTR = *const u8;
+pub type PCWSTR = *const u16;
+pub type PWSTR = *mut u16;
+pub type REG_SAM_FLAGS = u32;
+pub const REG_SZ: REG_VALUE_TYPE = 1u32;
+pub type REG_VALUE_TYPE = u32;
+#[repr(C)]
+pub struct SAFEARRAY {
+ pub cDims: u16,
+ pub fFeatures: ADVANCED_FEATURE_FLAGS,
+ pub cbElements: u32,
+ pub cLocks: u32,
+ pub pvData: *mut ::core::ffi::c_void,
+ pub rgsabound: [SAFEARRAYBOUND; 1],
+}
+impl ::core::marker::Copy for SAFEARRAY {}
+impl ::core::clone::Clone for SAFEARRAY {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+#[repr(C)]
+pub struct SAFEARRAYBOUND {
+ pub cElements: u32,
+ pub lLbound: i32,
+}
+impl ::core::marker::Copy for SAFEARRAYBOUND {}
+impl ::core::clone::Clone for SAFEARRAYBOUND {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+pub const SEMAPHORE_MODIFY_STATE: SYNCHRONIZATION_ACCESS_RIGHTS = 2u32;
+pub type SYNCHRONIZATION_ACCESS_RIGHTS = u32;
+pub const S_FALSE: HRESULT = 0x1_u32 as _;
+pub const S_OK: HRESULT = 0x0_u32 as _;
+pub type THREAD_ACCESS_RIGHTS = u32;
+pub const THREAD_SYNCHRONIZE: THREAD_ACCESS_RIGHTS = 1048576u32;
+pub const UserEnabled: MACHINE_ATTRIBUTES = 1i32;
+pub const WAIT_ABANDONED: WAIT_EVENT = 128u32;
+pub type WAIT_EVENT = u32;
+pub const WAIT_FAILED: WAIT_EVENT = 4294967295u32;
+pub const WAIT_OBJECT_0: WAIT_EVENT = 0u32;
+pub const WAIT_TIMEOUT: WAIT_EVENT = 258u32;
+pub type WIN32_ERROR = u32;
diff --git a/third_party/rust/cc/tests/cc_env.rs b/third_party/rust/cc/tests/cc_env.rs
deleted file mode 100644
index 43eb689f0f..0000000000
--- a/third_party/rust/cc/tests/cc_env.rs
+++ /dev/null
@@ -1,118 +0,0 @@
-use std::env;
-use std::ffi::OsString;
-use std::path::Path;
-
-mod support;
-use crate::support::Test;
-
-#[test]
-fn main() {
- ccache();
- distcc();
- ccache_spaces();
- ccache_env_flags();
- leading_spaces();
- extra_flags();
- path_to_ccache();
- more_spaces();
-}
-
-fn ccache() {
- let test = Test::gnu();
-
- env::set_var("CC", "ccache cc");
- let compiler = test.gcc().file("foo.c").get_compiler();
-
- assert_eq!(compiler.path(), Path::new("cc"));
-}
-
-fn ccache_spaces() {
- let test = Test::gnu();
- test.shim("ccache");
-
- env::set_var("CC", "ccache cc");
- let compiler = test.gcc().file("foo.c").get_compiler();
- assert_eq!(compiler.path(), Path::new("cc"));
-}
-
-fn distcc() {
- let test = Test::gnu();
- test.shim("distcc");
-
- env::set_var("CC", "distcc cc");
- let compiler = test.gcc().file("foo.c").get_compiler();
- assert_eq!(compiler.path(), Path::new("cc"));
-}
-
-fn ccache_env_flags() {
- let test = Test::gnu();
- test.shim("ccache");
-
- env::set_var("CC", "ccache lol-this-is-not-a-compiler");
- let compiler = test.gcc().file("foo.c").get_compiler();
- assert_eq!(compiler.path(), Path::new("lol-this-is-not-a-compiler"));
- assert_eq!(
- compiler.cc_env(),
- OsString::from("ccache lol-this-is-not-a-compiler")
- );
- assert!(
- compiler
- .cflags_env()
- .into_string()
- .unwrap()
- .contains("ccache")
- == false
- );
- assert!(
- compiler
- .cflags_env()
- .into_string()
- .unwrap()
- .contains(" lol-this-is-not-a-compiler")
- == false
- );
-
- env::set_var("CC", "");
-}
-
-fn leading_spaces() {
- let test = Test::gnu();
- test.shim("ccache");
-
- env::set_var("CC", " test ");
- let compiler = test.gcc().file("foo.c").get_compiler();
- assert_eq!(compiler.path(), Path::new("test"));
-
- env::set_var("CC", "");
-}
-
-fn extra_flags() {
- let test = Test::gnu();
- test.shim("ccache");
-
- env::set_var("CC", "ccache cc -m32");
- let compiler = test.gcc().file("foo.c").get_compiler();
- assert_eq!(compiler.path(), Path::new("cc"));
-}
-
-fn path_to_ccache() {
- let test = Test::gnu();
- test.shim("ccache");
-
- env::set_var("CC", "/path/to/ccache.exe cc -m32");
- let compiler = test.gcc().file("foo.c").get_compiler();
- assert_eq!(compiler.path(), Path::new("cc"));
- assert_eq!(
- compiler.cc_env(),
- OsString::from("/path/to/ccache.exe cc -m32"),
- );
-}
-
-fn more_spaces() {
- let test = Test::gnu();
- test.shim("ccache");
-
- env::set_var("CC", "cc -m32");
- let compiler = test.gcc().file("foo.c").get_compiler();
- assert_eq!(compiler.path(), Path::new("cc"));
-}
diff --git a/third_party/rust/cc/tests/cflags.rs b/third_party/rust/cc/tests/cflags.rs
deleted file mode 100644
index caec6ea4ed..0000000000
--- a/third_party/rust/cc/tests/cflags.rs
+++ /dev/null
@@ -1,15 +0,0 @@
-mod support;
-
-use crate::support::Test;
-use std::env;
-
-/// This test is in its own module because it modifies the environment and would affect other tests
-/// when run in parallel with them.
-#[test]
-fn gnu_no_warnings_if_cflags() {
- env::set_var("CFLAGS", "-arbitrary");
- let test = Test::gnu();
- test.gcc().file("foo.c").compile("foo");
-
- test.cmd(0).must_not_have("-Wall").must_not_have("-Wextra");
-}
diff --git a/third_party/rust/cc/tests/cxxflags.rs b/third_party/rust/cc/tests/cxxflags.rs
deleted file mode 100644
index c524c7da4e..0000000000
--- a/third_party/rust/cc/tests/cxxflags.rs
+++ /dev/null
@@ -1,15 +0,0 @@
-mod support;
-
-use crate::support::Test;
-use std::env;
-
-/// This test is in its own module because it modifies the environment and would affect other tests
-/// when run in parallel with them.
-#[test]
-fn gnu_no_warnings_if_cxxflags() {
- env::set_var("CXXFLAGS", "-arbitrary");
- let test = Test::gnu();
- test.gcc().file("foo.cpp").cpp(true).compile("foo");
-
- test.cmd(0).must_not_have("-Wall").must_not_have("-Wextra");
-}
diff --git a/third_party/rust/cc/tests/support/mod.rs b/third_party/rust/cc/tests/support/mod.rs
deleted file mode 100644
index f3c04405a3..0000000000
--- a/third_party/rust/cc/tests/support/mod.rs
+++ /dev/null
@@ -1,172 +0,0 @@
-#![allow(dead_code)]
-
-use std::env;
-use std::ffi::{OsStr, OsString};
-use std::fs::{self, File};
-use std::io;
-use std::io::prelude::*;
-use std::path::{Path, PathBuf};
-
-use cc;
-use tempfile::{Builder, TempDir};
-
-pub struct Test {
- pub td: TempDir,
- pub gcc: PathBuf,
- pub msvc: bool,
-}
-
-pub struct Execution {
- args: Vec<String>,
-}
-
-impl Test {
- pub fn new() -> Test {
- // This is ugly: `sccache` needs to introspect the compiler it is
- // executing, as it adjusts its behavior depending on the
- // language/compiler. This crate's test driver uses mock compilers that
- // are obviously not supported by sccache, so the tests fail if
- // RUSTC_WRAPPER is set. rust doesn't build test dependencies with
- // the `test` feature enabled, so we can't conditionally disable the
- // usage of `sccache` if running in a test environment, at least not
- // without setting an environment variable here and testing for it
- // there. Explicitly deasserting RUSTC_WRAPPER here seems to be the
- // lesser of the two evils.
- env::remove_var("RUSTC_WRAPPER");
-
- let mut gcc = PathBuf::from(env::current_exe().unwrap());
- gcc.pop();
- if gcc.ends_with("deps") {
- gcc.pop();
- }
- let td = Builder::new().prefix("gcc-test").tempdir_in(&gcc).unwrap();
- gcc.push(format!("gcc-shim{}", env::consts::EXE_SUFFIX));
- Test {
- td: td,
- gcc: gcc,
- msvc: false,
- }
- }
-
- pub fn gnu() -> Test {
- let t = Test::new();
- t.shim("cc").shim("c++").shim("ar");
- t
- }
-
- pub fn msvc() -> Test {
- let mut t = Test::new();
- t.shim("cl").shim("lib.exe");
- t.msvc = true;
- t
- }
-
- pub fn shim(&self, name: &str) -> &Test {
- let name = if name.ends_with(env::consts::EXE_SUFFIX) {
- name.to_string()
- } else {
- format!("{}{}", name, env::consts::EXE_SUFFIX)
- };
- link_or_copy(&self.gcc, self.td.path().join(name)).unwrap();
- self
- }
-
- pub fn gcc(&self) -> cc::Build {
- let mut cfg = cc::Build::new();
- let target = if self.msvc {
- "x86_64-pc-windows-msvc"
- } else {
- "x86_64-unknown-linux-gnu"
- };
-
- cfg.target(target)
- .host(target)
- .opt_level(2)
- .debug(false)
- .out_dir(self.td.path())
- .__set_env("PATH", self.path())
- .__set_env("GCCTEST_OUT_DIR", self.td.path());
- if self.msvc {
- cfg.compiler(self.td.path().join("cl"));
- cfg.archiver(self.td.path().join("lib.exe"));
- }
- cfg
- }
-
- fn path(&self) -> OsString {
- let mut path = env::split_paths(&env::var_os("PATH").unwrap()).collect::<Vec<_>>();
- path.insert(0, self.td.path().to_owned());
- env::join_paths(path).unwrap()
- }
-
- pub fn cmd(&self, i: u32) -> Execution {
- let mut s = String::new();
- File::open(self.td.path().join(format!("out{}", i)))
- .unwrap()
- .read_to_string(&mut s)
- .unwrap();
- Execution {
- args: s.lines().map(|s| s.to_string()).collect(),
- }
- }
-}
-
-impl Execution {
- pub fn must_have<P: AsRef<OsStr>>(&self, p: P) -> &Execution {
- if !self.has(p.as_ref()) {
- panic!("didn't find {:?} in {:?}", p.as_ref(), self.args);
- } else {
- self
- }
- }
-
- pub fn must_not_have<P: AsRef<OsStr>>(&self, p: P) -> &Execution {
- if self.has(p.as_ref()) {
- panic!("found {:?}", p.as_ref());
- } else {
- self
- }
- }
-
- pub fn has(&self, p: &OsStr) -> bool {
- self.args.iter().any(|arg| OsStr::new(arg) == p)
- }
-
- pub fn must_have_in_order(&self, before: &str, after: &str) -> &Execution {
- let before_position = self
- .args
- .iter()
- .rposition(|x| OsStr::new(x) == OsStr::new(before));
- let after_position = self
- .args
- .iter()
- .rposition(|x| OsStr::new(x) == OsStr::new(after));
- match (before_position, after_position) {
- (Some(b), Some(a)) if b < a => {}
- (b, a) => panic!(
- "{:?} (last position: {:?}) did not appear before {:?} (last position: {:?})",
- before, b, after, a
- ),
- };
- self
- }
-}
-
-/// Hard link an executable or copy it if that fails.
-///
-/// We first try to hard link an executable to save space. If that fails (as on Windows with
-/// different mount points, issue #60), we copy.
-#[cfg(not(target_os = "macos"))]
-fn link_or_copy<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<()> {
- let from = from.as_ref();
- let to = to.as_ref();
- fs::hard_link(from, to).or_else(|_| fs::copy(from, to).map(|_| ()))
-}
-
-/// Copy an executable.
-///
-/// On macOS, hard linking the executable leads to strange failures (issue #419), so we just copy.
-#[cfg(target_os = "macos")]
-fn link_or_copy<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<()> {
- fs::copy(from, to).map(|_| ())
-}
diff --git a/third_party/rust/cc/tests/test.rs b/third_party/rust/cc/tests/test.rs
deleted file mode 100644
index 161abd8ab7..0000000000
--- a/third_party/rust/cc/tests/test.rs
+++ /dev/null
@@ -1,461 +0,0 @@
-use crate::support::Test;
-
-mod support;
-
-// Some tests check that a flag is *not* present. These tests might fail if the flag is set in the
-// CFLAGS or CXXFLAGS environment variables. This function clears the CFLAGS and CXXFLAGS
-// variables to make sure that the tests can run correctly.
-fn reset_env() {
- std::env::set_var("CFLAGS", "");
- std::env::set_var("CXXFLAGS", "");
-}
-
-#[test]
-fn gnu_smoke() {
- reset_env();
-
- let test = Test::gnu();
- test.gcc().file("foo.c").compile("foo");
-
- test.cmd(0)
- .must_have("-O2")
- .must_have("foo.c")
- .must_not_have("-gdwarf-4")
- .must_have("-c")
- .must_have("-ffunction-sections")
- .must_have("-fdata-sections");
- test.cmd(1).must_have(test.td.path().join("foo.o"));
-}
-
-#[test]
-fn gnu_opt_level_1() {
- reset_env();
-
- let test = Test::gnu();
- test.gcc().opt_level(1).file("foo.c").compile("foo");
-
- test.cmd(0).must_have("-O1").must_not_have("-O2");
-}
-
-#[test]
-fn gnu_opt_level_s() {
- reset_env();
-
- let test = Test::gnu();
- test.gcc().opt_level_str("s").file("foo.c").compile("foo");
-
- test.cmd(0)
- .must_have("-Os")
- .must_not_have("-O1")
- .must_not_have("-O2")
- .must_not_have("-O3")
- .must_not_have("-Oz");
-}
-
-#[test]
-fn gnu_debug() {
- let test = Test::gnu();
- test.gcc().debug(true).file("foo.c").compile("foo");
- test.cmd(0).must_have("-gdwarf-4");
-
- let test = Test::gnu();
- test.gcc()
- .target("x86_64-apple-darwin")
- .debug(true)
- .file("foo.c")
- .compile("foo");
- test.cmd(0).must_have("-gdwarf-2");
-}
-
-#[test]
-fn gnu_debug_fp_auto() {
- let test = Test::gnu();
- test.gcc().debug(true).file("foo.c").compile("foo");
- test.cmd(0).must_have("-gdwarf-4");
- test.cmd(0).must_have("-fno-omit-frame-pointer");
-}
-
-#[test]
-fn gnu_debug_fp() {
- let test = Test::gnu();
- test.gcc().debug(true).file("foo.c").compile("foo");
- test.cmd(0).must_have("-gdwarf-4");
- test.cmd(0).must_have("-fno-omit-frame-pointer");
-}
-
-#[test]
-fn gnu_debug_nofp() {
- reset_env();
-
- let test = Test::gnu();
- test.gcc()
- .debug(true)
- .force_frame_pointer(false)
- .file("foo.c")
- .compile("foo");
- test.cmd(0).must_have("-gdwarf-4");
- test.cmd(0).must_not_have("-fno-omit-frame-pointer");
-
- let test = Test::gnu();
- test.gcc()
- .force_frame_pointer(false)
- .debug(true)
- .file("foo.c")
- .compile("foo");
- test.cmd(0).must_have("-gdwarf-4");
- test.cmd(0).must_not_have("-fno-omit-frame-pointer");
-}
-
-#[test]
-fn gnu_warnings_into_errors() {
- let test = Test::gnu();
- test.gcc()
- .warnings_into_errors(true)
- .file("foo.c")
- .compile("foo");
-
- test.cmd(0).must_have("-Werror");
-}
-
-#[test]
-fn gnu_warnings() {
- let test = Test::gnu();
- test.gcc()
- .warnings(true)
- .flag("-Wno-missing-field-initializers")
- .file("foo.c")
- .compile("foo");
-
- test.cmd(0).must_have("-Wall").must_have("-Wextra");
-}
-
-#[test]
-fn gnu_extra_warnings0() {
- reset_env();
-
- let test = Test::gnu();
- test.gcc()
- .warnings(true)
- .extra_warnings(false)
- .flag("-Wno-missing-field-initializers")
- .file("foo.c")
- .compile("foo");
-
- test.cmd(0).must_have("-Wall").must_not_have("-Wextra");
-}
-
-#[test]
-fn gnu_extra_warnings1() {
- reset_env();
-
- let test = Test::gnu();
- test.gcc()
- .warnings(false)
- .extra_warnings(true)
- .flag("-Wno-missing-field-initializers")
- .file("foo.c")
- .compile("foo");
-
- test.cmd(0).must_not_have("-Wall").must_have("-Wextra");
-}
-
-#[test]
-fn gnu_warnings_overridable() {
- reset_env();
-
- let test = Test::gnu();
- test.gcc()
- .warnings(true)
- .flag("-Wno-missing-field-initializers")
- .file("foo.c")
- .compile("foo");
-
- test.cmd(0)
- .must_have_in_order("-Wall", "-Wno-missing-field-initializers");
-}
-
-#[test]
-fn gnu_x86_64() {
- for vendor in &["unknown-linux-gnu", "apple-darwin"] {
- let target = format!("x86_64-{}", vendor);
- let test = Test::gnu();
- test.gcc()
- .target(&target)
- .host(&target)
- .file("foo.c")
- .compile("foo");
-
- test.cmd(0).must_have("-fPIC").must_have("-m64");
- }
-}
-
-#[test]
-fn gnu_x86_64_no_pic() {
- reset_env();
-
- for vendor in &["unknown-linux-gnu", "apple-darwin"] {
- let target = format!("x86_64-{}", vendor);
- let test = Test::gnu();
- test.gcc()
- .pic(false)
- .target(&target)
- .host(&target)
- .file("foo.c")
- .compile("foo");
-
- test.cmd(0).must_not_have("-fPIC");
- }
-}
-
-#[test]
-fn gnu_i686() {
- for vendor in &["unknown-linux-gnu", "apple-darwin"] {
- let target = format!("i686-{}", vendor);
- let test = Test::gnu();
- test.gcc()
- .target(&target)
- .host(&target)
- .file("foo.c")
- .compile("foo");
-
- test.cmd(0).must_have("-m32");
- }
-}
-
-#[test]
-fn gnu_i686_pic() {
- for vendor in &["unknown-linux-gnu", "apple-darwin"] {
- let target = format!("i686-{}", vendor);
- let test = Test::gnu();
- test.gcc()
- .pic(true)
- .target(&target)
- .host(&target)
- .file("foo.c")
- .compile("foo");
-
- test.cmd(0).must_have("-fPIC");
- }
-}
-
-#[test]
-fn gnu_x86_64_no_plt() {
- let target = "x86_64-unknown-linux-gnu";
- let test = Test::gnu();
- test.gcc()
- .pic(true)
- .use_plt(false)
- .target(&target)
- .host(&target)
- .file("foo.c")
- .compile("foo");
- test.cmd(0).must_have("-fno-plt");
-}
-
-#[test]
-fn gnu_set_stdlib() {
- reset_env();
-
- let test = Test::gnu();
- test.gcc()
- .cpp_set_stdlib(Some("foo"))
- .file("foo.c")
- .compile("foo");
-
- test.cmd(0).must_not_have("-stdlib=foo");
-}
-
-#[test]
-fn gnu_include() {
- let test = Test::gnu();
- test.gcc().include("foo/bar").file("foo.c").compile("foo");
-
- test.cmd(0).must_have("-I").must_have("foo/bar");
-}
-
-#[test]
-fn gnu_define() {
- let test = Test::gnu();
- test.gcc()
- .define("FOO", "bar")
- .define("BAR", None)
- .file("foo.c")
- .compile("foo");
-
- test.cmd(0).must_have("-DFOO=bar").must_have("-DBAR");
-}
-
-#[test]
-fn gnu_compile_assembly() {
- let test = Test::gnu();
- test.gcc().file("foo.S").compile("foo");
- test.cmd(0).must_have("foo.S");
-}
-
-#[test]
-fn gnu_shared() {
- reset_env();
-
- let test = Test::gnu();
- test.gcc()
- .file("foo.c")
- .shared_flag(true)
- .static_flag(false)
- .compile("foo");
-
- test.cmd(0).must_have("-shared").must_not_have("-static");
-}
-
-#[test]
-fn gnu_flag_if_supported() {
- reset_env();
-
- if cfg!(windows) {
- return;
- }
- let test = Test::gnu();
- test.gcc()
- .file("foo.c")
- .flag("-v")
- .flag_if_supported("-Wall")
- .flag_if_supported("-Wflag-does-not-exist")
- .flag_if_supported("-std=c++11")
- .compile("foo");
-
- test.cmd(0)
- .must_have("-v")
- .must_have("-Wall")
- .must_not_have("-Wflag-does-not-exist")
- .must_not_have("-std=c++11");
-}
-
-#[test]
-fn gnu_flag_if_supported_cpp() {
- if cfg!(windows) {
- return;
- }
- let test = Test::gnu();
- test.gcc()
- .cpp(true)
- .file("foo.cpp")
- .flag_if_supported("-std=c++11")
- .compile("foo");
-
- test.cmd(0).must_have("-std=c++11");
-}
-
-#[test]
-fn gnu_static() {
- reset_env();
-
- let test = Test::gnu();
- test.gcc()
- .file("foo.c")
- .shared_flag(false)
- .static_flag(true)
- .compile("foo");
-
- test.cmd(0).must_have("-static").must_not_have("-shared");
-}
-
-#[test]
-fn gnu_no_dash_dash() {
- let test = Test::gnu();
- test.gcc().file("foo.c").compile("foo");
-
- test.cmd(0).must_not_have("--");
-}
-
-#[test]
-fn msvc_smoke() {
- reset_env();
-
- let test = Test::msvc();
- test.gcc().file("foo.c").compile("foo");
-
- test.cmd(0)
- .must_have("-O2")
- .must_have("foo.c")
- .must_not_have("-Z7")
- .must_have("-c")
- .must_have("-MD");
- test.cmd(1).must_have(test.td.path().join("foo.o"));
-}
-
-#[test]
-fn msvc_opt_level_0() {
- reset_env();
-
- let test = Test::msvc();
- test.gcc().opt_level(0).file("foo.c").compile("foo");
-
- test.cmd(0).must_not_have("-O2");
-}
-
-#[test]
-fn msvc_debug() {
- let test = Test::msvc();
- test.gcc().debug(true).file("foo.c").compile("foo");
- test.cmd(0).must_have("-Z7");
-}
-
-#[test]
-fn msvc_include() {
- let test = Test::msvc();
- test.gcc().include("foo/bar").file("foo.c").compile("foo");
-
- test.cmd(0).must_have("-I").must_have("foo/bar");
-}
-
-#[test]
-fn msvc_define() {
- let test = Test::msvc();
- test.gcc()
- .define("FOO", "bar")
- .define("BAR", None)
- .file("foo.c")
- .compile("foo");
-
- test.cmd(0).must_have("-DFOO=bar").must_have("-DBAR");
-}
-
-#[test]
-fn msvc_static_crt() {
- let test = Test::msvc();
- test.gcc().static_crt(true).file("foo.c").compile("foo");
-
- test.cmd(0).must_have("-MT");
-}
-
-#[test]
-fn msvc_no_static_crt() {
- let test = Test::msvc();
- test.gcc().static_crt(false).file("foo.c").compile("foo");
-
- test.cmd(0).must_have("-MD");
-}
-
-#[test]
-fn msvc_no_dash_dash() {
- let test = Test::msvc();
- test.gcc().file("foo.c").compile("foo");
-
- test.cmd(0).must_not_have("--");
-}
-
-// Disable this test with the parallel feature because the execution
-// order is not deterministic.
-#[cfg(not(feature = "parallel"))]
-#[test]
-fn asm_flags() {
- let test = Test::gnu();
- test.gcc()
- .file("foo.c")
- .file("x86_64.asm")
- .file("x86_64.S")
- .asm_flag("--abc")
- .compile("foo");
- test.cmd(0).must_not_have("--abc");
- test.cmd(1).must_have("--abc");
- test.cmd(2).must_have("--abc");
-}
diff --git a/third_party/rust/document-features/.cargo-checksum.json b/third_party/rust/document-features/.cargo-checksum.json
new file mode 100644
index 0000000000..0b9d0b431c
--- /dev/null
+++ b/third_party/rust/document-features/.cargo-checksum.json
@@ -0,0 +1 @@
+{"files":{"CHANGELOG.md":"c1ccf4587ca168b3baa54580469c5dcc776decac0d996d3bb31d2341b47efa11","Cargo.toml":"390d32c2b791a6745c075c474e6d57c65d5f77f0e7190ff8a8c5342fbb40722a","LICENSE-APACHE":"074e6e32c86a4c0ef8b3ed25b721ca23aca83df277cd88106ef7177c354615ff","LICENSE-MIT":"aa893340d14b9844625be6a50ac644169a01b52f0211cbf81b09e1874c8cd81d","README.md":"89a83c4acc6891e5651772fc78a1d6362070774eaa6c5b5d4bfbe9e57a957be9","lib.rs":"2f4ede9d0619d85449891d9055605188db681d57b405e40e529831266e014ee5","rustfmt.toml":"f74204a6f92aa7422a16ecb2ffe2d5bae0f123b778d08b5db1a398a3c9ca4306","tests/self-doc.rs":"24bbda93f3b323c0b7c543c1df3bf45522b8026283103211805f070de66abadc"},"package":"ef5282ad69563b5fc40319526ba27e0e7363d552a896f0297d54f767717f9b95"} \ No newline at end of file
diff --git a/third_party/rust/document-features/CHANGELOG.md b/third_party/rust/document-features/CHANGELOG.md
new file mode 100644
index 0000000000..f94ee19ead
--- /dev/null
+++ b/third_party/rust/document-features/CHANGELOG.md
@@ -0,0 +1,44 @@
+# Changelog
+
+
+## 0.2.7 - 2023-12-29
+
+* Remove `\n` between features (#17)
+* Don't throw an error when there is no features in Cargo.toml (#20)
+
+## 0.2.7 - 2022-12-21
+
+* Fix parsing of Cargo.toml with multi-line array of array (#16)
+
+## 0.2.6 - 2022-09-24
+
+* Fix parsing of escaped string literal in the macro arguments
+
+## 0.2.5 - 2022-09-17
+
+* Allow customization of the output with the `feature_label=` parameter
+
+## 0.2.4 - 2022-09-14
+
+* Fix dependencies or features written with quotes
+
+## 0.2.3 - 2022-08-15
+
+* Fix parsing of table with `#` within strings (#10)
+
+## 0.2.2 - 2022-07-25
+
+* Fix parsing of dependencies or feature spanning multiple lines (#9)
+
+## 0.2.1 - 2022-02-12
+
+* Fix indentation of multi-lines feature comments (#5)
+
+## 0.2.0 - 2022-02-11
+
+* Added ability to document optional features. (This is a breaking change in the
+ sense that previously ignored comments may now result in errors)
+
+## 0.1.0 - 2022-02-01
+
+Initial release
diff --git a/third_party/rust/document-features/Cargo.toml b/third_party/rust/document-features/Cargo.toml
new file mode 100644
index 0000000000..bae5ff2a3b
--- /dev/null
+++ b/third_party/rust/document-features/Cargo.toml
@@ -0,0 +1,40 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies.
+#
+# If you are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
+
+[package]
+edition = "2018"
+name = "document-features"
+version = "0.2.8"
+authors = ["Slint Developers <info@slint-ui.com>"]
+description = "Extract documentation for the feature flags from comments in Cargo.toml"
+homepage = "https://slint-ui.com"
+readme = "README.md"
+keywords = [
+ "documentation",
+ "features",
+ "rustdoc",
+ "macro",
+]
+categories = ["development-tools"]
+license = "MIT OR Apache-2.0"
+repository = "https://github.com/slint-ui/document-features"
+
+[lib]
+path = "lib.rs"
+proc-macro = true
+
+[dependencies.litrs]
+version = "0.4.1"
+default-features = false
+
+[features]
+default = []
+self-test = []
diff --git a/third_party/rust/document-features/LICENSE-APACHE b/third_party/rust/document-features/LICENSE-APACHE
new file mode 100644
index 0000000000..137069b823
--- /dev/null
+++ b/third_party/rust/document-features/LICENSE-APACHE
@@ -0,0 +1,73 @@
+Apache License
+Version 2.0, January 2004
+http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
+
+"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
+
+"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
+
+"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
+
+"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
+
+"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
+
+"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
+
+"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
+
+"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
+
+"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
+
+ (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work.
+
+To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives.
+
+Copyright [yyyy] [name of copyright owner]
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/third_party/WinToast/LICENSE b/third_party/rust/document-features/LICENSE-MIT
index c3a4fb8868..13a82b270c 100644
--- a/third_party/WinToast/LICENSE
+++ b/third_party/rust/document-features/LICENSE-MIT
@@ -1,6 +1,4 @@
-MIT License
-
-Copyright (c) 2016 Mohammed Boujemaoui Boulaghmoudi
+Copyright (c) 2020 Olivier Goffart <ogoffart@sixtyfps.io>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/third_party/rust/document-features/README.md b/third_party/rust/document-features/README.md
new file mode 100644
index 0000000000..81fccc0f44
--- /dev/null
+++ b/third_party/rust/document-features/README.md
@@ -0,0 +1,43 @@
+# Document your crate's feature flags
+
+[![Crates.io](https://img.shields.io/crates/v/document-features)](https://crates.io/crates/document-features)
+[![Documentation](https://docs.rs/document-features/badge.svg)](https://docs.rs/document-features/)
+
+This crate provides a macro that extracts documentation comments from Cargo.toml
+
+To use this crate, add `#![doc = document_features::document_features!()]` in your crate documentation.
+The `document_features!()` macro reads your `Cargo.toml` file, extracts feature comments and generates
+a markdown string for your documentation.
+
+Use `## ` and `#! ` comments in your Cargo.toml to document features, for example:
+
+```toml
+[dependencies]
+document-features = "0.2"
+## ...
+
+[features]
+## The foo feature enables the `foo` functions
+foo = []
+## The bar feature enables the [`bar`] module
+bar = []
+
+#! ### Experimental features
+#! The following features are experimental
+
+## Activate the fusion reactor
+fusion = []
+```
+
+These comments keep the feature definition and documentation next to each other, and they are then
+rendered into your crate documentation.
+
+Check out the [documentation](https://docs.rs/document-features/) for more details.
+
+## Contributions
+
+Contributions are welcome. We accept pull requests and bug reports.
+
+## License
+
+MIT OR Apache-2.0
diff --git a/third_party/rust/document-features/lib.rs b/third_party/rust/document-features/lib.rs
new file mode 100644
index 0000000000..b30ebe7f42
--- /dev/null
+++ b/third_party/rust/document-features/lib.rs
@@ -0,0 +1,877 @@
+// Copyright © SixtyFPS GmbH <info@sixtyfps.io>
+// SPDX-License-Identifier: MIT OR Apache-2.0
+
+/*!
+Document your crate's feature flags.
+
+This crates provides a macro that extracts "documentation" comments from Cargo.toml
+
+To use this crate, add `#![doc = document_features::document_features!()]` in your crate documentation.
+The `document_features!()` macro reads your `Cargo.toml` file, extracts feature comments and generates
+a markdown string for your documentation.
+
+Basic example:
+
+```rust
+//! Normal crate documentation goes here.
+//!
+//! ## Feature flags
+#![doc = document_features::document_features!()]
+
+// rest of the crate goes here.
+```
+
+## Documentation format:
+
+The documentation of your crate features goes into `Cargo.toml`, where they are defined.
+
+The `document_features!()` macro analyzes the contents of `Cargo.toml`.
+Similar to Rust's documentation comments `///` and `//!`, the macro understands
+comments that start with `## ` and `#! `. Note the required trailing space.
+Lines starting with `###` will not be understood as doc comment.
+
+`## ` comments are meant to be *above* the feature they document.
+There can be several `## ` comments, but they must always be followed by a
+feature name or an optional dependency.
+There should not be `#! ` comments between the comment and the feature they document.
+
+`#! ` comments are not associated with a particular feature, and will be printed
+in where they occur. Use them to group features, for example.
+
+## Examples:
+
+*/
+// Note: because rustdoc escapes the first `#` of a line starting with `#`,
+// these docs comments have one more `#` ,
+#![doc = self_test!(/**
+[package]
+name = "..."
+## ...
+
+[features]
+default = ["foo"]
+##! This comments goes on top
+
+### The foo feature enables the `foo` functions
+foo = []
+
+### The bar feature enables the bar module
+bar = []
+
+##! ### Experimental features
+##! The following features are experimental
+
+### Enable the fusion reactor
+###
+### ⚠️ Can lead to explosions
+fusion = []
+
+[dependencies]
+document-features = "0.2"
+
+##! ### Optional dependencies
+
+### Enable this feature to implement the trait for the types from the genial crate
+genial = { version = "0.2", optional = true }
+
+### This awesome dependency is specified in its own table
+[dependencies.awesome]
+version = "1.3.5"
+optional = true
+*/
+=>
+ /**
+This comments goes on top
+* **`foo`** *(enabled by default)* — The foo feature enables the `foo` functions
+* **`bar`** — The bar feature enables the bar module
+
+#### Experimental features
+The following features are experimental
+* **`fusion`** — Enable the fusion reactor
+
+ ⚠️ Can lead to explosions
+
+#### Optional dependencies
+* **`genial`** — Enable this feature to implement the trait for the types from the genial crate
+* **`awesome`** — This awesome dependency is specified in its own table
+*/
+)]
+/*!
+
+## Customization
+
+You can customize the formatting of the features in the generated documentation by setting
+the key **`feature_label=`** to a given format string. This format string must be either
+a [string literal](https://doc.rust-lang.org/reference/tokens.html#string-literals) or
+a [raw string literal](https://doc.rust-lang.org/reference/tokens.html#raw-string-literals).
+Every occurrence of `{feature}` inside the format string will be substituted with the name of the feature.
+
+For instance, to emulate the HTML formatting used by `rustdoc` one can use the following:
+
+```rust
+#![doc = document_features::document_features!(feature_label = r#"<span class="stab portability"><code>{feature}</code></span>"#)]
+```
+
+The default formatting is equivalent to:
+
+```rust
+#![doc = document_features::document_features!(feature_label = "**`{feature}`**")]
+```
+
+## Compatibility
+
+The minimum Rust version required to use this crate is Rust 1.54 because of the
+feature to have macro in doc comments. You can make this crate optional and use
+`#[cfg_attr()]` statements to enable it only when building the documentation:
+You need to have two levels of `cfg_attr` because Rust < 1.54 doesn't parse the attribute
+otherwise.
+
+```rust,ignore
+#![cfg_attr(
+ feature = "document-features",
+ cfg_attr(doc, doc = ::document_features::document_features!())
+)]
+```
+
+In your Cargo.toml, enable this feature while generating the documentation on docs.rs:
+
+```toml
+[dependencies]
+document-features = { version = "0.2", optional = true }
+
+[package.metadata.docs.rs]
+features = ["document-features"]
+## Alternative: enable all features so they are all documented
+## all-features = true
+```
+ */
+
+#[cfg(not(feature = "default"))]
+compile_error!(
+ "The feature `default` must be enabled to ensure \
+ forward compatibility with future version of this crate"
+);
+
+extern crate proc_macro;
+
+use proc_macro::{TokenStream, TokenTree};
+use std::borrow::Cow;
+use std::collections::HashSet;
+use std::convert::TryFrom;
+use std::fmt::Write;
+use std::path::Path;
+use std::str::FromStr;
+
+fn error(e: &str) -> TokenStream {
+ TokenStream::from_str(&format!("::core::compile_error!{{\"{}\"}}", e.escape_default())).unwrap()
+}
+
+fn compile_error(msg: &str, tt: Option<TokenTree>) -> TokenStream {
+ let span = tt.as_ref().map_or_else(proc_macro::Span::call_site, TokenTree::span);
+ use proc_macro::{Delimiter, Group, Ident, Literal, Punct, Spacing};
+ use std::iter::FromIterator;
+ TokenStream::from_iter(vec![
+ TokenTree::Ident(Ident::new("compile_error", span)),
+ TokenTree::Punct({
+ let mut punct = Punct::new('!', Spacing::Alone);
+ punct.set_span(span);
+ punct
+ }),
+ TokenTree::Group({
+ let mut group = Group::new(Delimiter::Brace, {
+ TokenStream::from_iter([TokenTree::Literal({
+ let mut string = Literal::string(msg);
+ string.set_span(span);
+ string
+ })])
+ });
+ group.set_span(span);
+ group
+ }),
+ ])
+}
+
+#[derive(Default)]
+struct Args {
+ feature_label: Option<String>,
+}
+
+fn parse_args(input: TokenStream) -> Result<Args, TokenStream> {
+ let mut token_trees = input.into_iter().fuse();
+
+ // parse the key, ensuring that it is the identifier `feature_label`
+ match token_trees.next() {
+ None => return Ok(Args::default()),
+ Some(TokenTree::Ident(ident)) if ident.to_string() == "feature_label" => (),
+ tt => return Err(compile_error("expected `feature_label`", tt)),
+ }
+
+ // parse a single equal sign `=`
+ match token_trees.next() {
+ Some(TokenTree::Punct(p)) if p.as_char() == '=' => (),
+ tt => return Err(compile_error("expected `=`", tt)),
+ }
+
+ // parse the value, ensuring that it is a string literal containing the substring `"{feature}"`
+ let feature_label;
+ if let Some(tt) = token_trees.next() {
+ match litrs::StringLit::<String>::try_from(&tt) {
+ Ok(string_lit) if string_lit.value().contains("{feature}") => {
+ feature_label = string_lit.value().to_string()
+ }
+ _ => {
+ return Err(compile_error(
+ "expected a string literal containing the substring \"{feature}\"",
+ Some(tt),
+ ))
+ }
+ }
+ } else {
+ return Err(compile_error(
+ "expected a string literal containing the substring \"{feature}\"",
+ None,
+ ));
+ }
+
+ // ensure there is nothing left after the format string
+ if let tt @ Some(_) = token_trees.next() {
+ return Err(compile_error("unexpected token after the format string", tt));
+ }
+
+ Ok(Args { feature_label: Some(feature_label) })
+}
+
+/// Produce a literal string containing documentation extracted from Cargo.toml
+///
+/// See the [crate] documentation for details
+#[proc_macro]
+pub fn document_features(tokens: TokenStream) -> TokenStream {
+ parse_args(tokens)
+ .and_then(|args| document_features_impl(&args))
+ .unwrap_or_else(std::convert::identity)
+}
+
+fn document_features_impl(args: &Args) -> Result<TokenStream, TokenStream> {
+ let path = std::env::var("CARGO_MANIFEST_DIR").unwrap();
+ let mut cargo_toml = std::fs::read_to_string(Path::new(&path).join("Cargo.toml"))
+ .map_err(|e| error(&format!("Can't open Cargo.toml: {:?}", e)))?;
+
+ if !cargo_toml.contains("\n##") && !cargo_toml.contains("\n#!") {
+ // On crates.io, Cargo.toml is usually "normalized" and stripped of all comments.
+ // The original Cargo.toml has been renamed Cargo.toml.orig
+ if let Ok(orig) = std::fs::read_to_string(Path::new(&path).join("Cargo.toml.orig")) {
+ if orig.contains("##") || orig.contains("#!") {
+ cargo_toml = orig;
+ }
+ }
+ }
+
+ let result = process_toml(&cargo_toml, args).map_err(|e| error(&e))?;
+ Ok(std::iter::once(proc_macro::TokenTree::from(proc_macro::Literal::string(&result))).collect())
+}
+
+fn process_toml(cargo_toml: &str, args: &Args) -> Result<String, String> {
+ // Get all lines between the "[features]" and the next block
+ let mut lines = cargo_toml
+ .lines()
+ .map(str::trim)
+ // and skip empty lines and comments that are not docs comments
+ .filter(|l| {
+ !l.is_empty() && (!l.starts_with('#') || l.starts_with("##") || l.starts_with("#!"))
+ });
+ let mut top_comment = String::new();
+ let mut current_comment = String::new();
+ let mut features = vec![];
+ let mut default_features = HashSet::new();
+ let mut current_table = "";
+ while let Some(line) = lines.next() {
+ if let Some(x) = line.strip_prefix("#!") {
+ if !x.is_empty() && !x.starts_with(' ') {
+ continue; // it's not a doc comment
+ }
+ if !current_comment.is_empty() {
+ return Err("Cannot mix ## and #! comments between features.".into());
+ }
+ if top_comment.is_empty() && !features.is_empty() {
+ top_comment = "\n".into();
+ }
+ writeln!(top_comment, "{}", x).unwrap();
+ } else if let Some(x) = line.strip_prefix("##") {
+ if !x.is_empty() && !x.starts_with(' ') {
+ continue; // it's not a doc comment
+ }
+ writeln!(current_comment, " {}", x).unwrap();
+ } else if let Some(table) = line.strip_prefix('[') {
+ current_table = table
+ .split_once(']')
+ .map(|(t, _)| t.trim())
+ .ok_or_else(|| format!("Parse error while parsing line: {}", line))?;
+ if !current_comment.is_empty() {
+ let dep = current_table
+ .rsplit_once('.')
+ .and_then(|(table, dep)| table.trim().ends_with("dependencies").then(|| dep))
+ .ok_or_else(|| format!("Not a feature: `{}`", line))?;
+ features.push((
+ dep.trim(),
+ std::mem::take(&mut top_comment),
+ std::mem::take(&mut current_comment),
+ ));
+ }
+ } else if let Some((dep, rest)) = line.split_once('=') {
+ let dep = dep.trim().trim_matches('"');
+ let rest = get_balanced(rest, &mut lines)
+ .map_err(|e| format!("Parse error while parsing value {}: {}", dep, e))?;
+ if current_table == "features" && dep == "default" {
+ let defaults = rest
+ .trim()
+ .strip_prefix('[')
+ .and_then(|r| r.strip_suffix(']'))
+ .ok_or_else(|| format!("Parse error while parsing dependency {}", dep))?
+ .split(',')
+ .map(|d| d.trim().trim_matches(|c| c == '"' || c == '\'').trim().to_string())
+ .filter(|d| !d.is_empty());
+ default_features.extend(defaults);
+ }
+ if !current_comment.is_empty() {
+ if current_table.ends_with("dependencies") {
+ if !rest
+ .split_once("optional")
+ .and_then(|(_, r)| r.trim().strip_prefix('='))
+ .map_or(false, |r| r.trim().starts_with("true"))
+ {
+ return Err(format!("Dependency {} is not an optional dependency", dep));
+ }
+ } else if current_table != "features" {
+ return Err(format!(
+ r#"Comment cannot be associated with a feature: "{}""#,
+ current_comment.trim()
+ ));
+ }
+ features.push((
+ dep,
+ std::mem::take(&mut top_comment),
+ std::mem::take(&mut current_comment),
+ ));
+ }
+ }
+ }
+ if !current_comment.is_empty() {
+ return Err("Found comment not associated with a feature".into());
+ }
+ if features.is_empty() {
+ return Ok("*No documented features in Cargo.toml*".into());
+ }
+ let mut result = String::new();
+ for (f, top, comment) in features {
+ let default = if default_features.contains(f) { " *(enabled by default)*" } else { "" };
+ if !comment.trim().is_empty() {
+ if let Some(feature_label) = &args.feature_label {
+ writeln!(
+ result,
+ "{}* {}{} —{}",
+ top,
+ feature_label.replace("{feature}", f),
+ default,
+ comment.trim_end(),
+ )
+ .unwrap();
+ } else {
+ writeln!(result, "{}* **`{}`**{} —{}", top, f, default, comment.trim_end())
+ .unwrap();
+ }
+ } else if let Some(feature_label) = &args.feature_label {
+ writeln!(result, "{}* {}{}", top, feature_label.replace("{feature}", f), default,)
+ .unwrap();
+ } else {
+ writeln!(result, "{}* **`{}`**{}", top, f, default).unwrap();
+ }
+ }
+ result += &top_comment;
+ Ok(result)
+}
+
+fn get_balanced<'a>(
+ first_line: &'a str,
+ lines: &mut impl Iterator<Item = &'a str>,
+) -> Result<Cow<'a, str>, String> {
+ let mut line = first_line;
+ let mut result = Cow::from("");
+
+ let mut in_quote = false;
+ let mut level = 0;
+ loop {
+ let mut last_slash = false;
+ for (idx, b) in line.as_bytes().iter().enumerate() {
+ if last_slash {
+ last_slash = false
+ } else if in_quote {
+ match b {
+ b'\\' => last_slash = true,
+ b'"' | b'\'' => in_quote = false,
+ _ => (),
+ }
+ } else {
+ match b {
+ b'\\' => last_slash = true,
+ b'"' => in_quote = true,
+ b'{' | b'[' => level += 1,
+ b'}' | b']' if level == 0 => return Err("unbalanced source".into()),
+ b'}' | b']' => level -= 1,
+ b'#' => {
+ line = &line[..idx];
+ break;
+ }
+ _ => (),
+ }
+ }
+ }
+ if result.len() == 0 {
+ result = Cow::from(line);
+ } else {
+ *result.to_mut() += line;
+ }
+ if level == 0 {
+ return Ok(result);
+ }
+ line = if let Some(l) = lines.next() {
+ l
+ } else {
+ return Err("unbalanced source".into());
+ };
+ }
+}
+
+#[test]
+fn test_get_balanced() {
+ assert_eq!(
+ get_balanced(
+ "{",
+ &mut IntoIterator::into_iter(["a", "{ abc[], #ignore", " def }", "}", "xxx"])
+ ),
+ Ok("{a{ abc[], def }}".into())
+ );
+ assert_eq!(
+ get_balanced("{ foo = \"{#\" } #ignore", &mut IntoIterator::into_iter(["xxx"])),
+ Ok("{ foo = \"{#\" } ".into())
+ );
+ assert_eq!(
+ get_balanced("]", &mut IntoIterator::into_iter(["["])),
+ Err("unbalanced source".into())
+ );
+}
+
+#[cfg(feature = "self-test")]
+#[proc_macro]
+#[doc(hidden)]
+/// Helper macro for the tests. Do not use
+pub fn self_test_helper(input: TokenStream) -> TokenStream {
+ process_toml((&input).to_string().trim_matches(|c| c == '"' || c == '#'), &Args::default())
+ .map_or_else(
+ |e| error(&e),
+ |r| {
+ std::iter::once(proc_macro::TokenTree::from(proc_macro::Literal::string(&r)))
+ .collect()
+ },
+ )
+}
+
+#[cfg(feature = "self-test")]
+macro_rules! self_test {
+ (#[doc = $toml:literal] => #[doc = $md:literal]) => {
+ concat!(
+ "\n`````rust\n\
+ fn normalize_md(md : &str) -> String {
+ md.lines().skip_while(|l| l.is_empty()).map(|l| l.trim())
+ .collect::<Vec<_>>().join(\"\\n\")
+ }
+ assert_eq!(normalize_md(document_features::self_test_helper!(",
+ stringify!($toml),
+ ")), normalize_md(",
+ stringify!($md),
+ "));\n`````\n\n"
+ )
+ };
+}
+
+#[cfg(not(feature = "self-test"))]
+macro_rules! self_test {
+ (#[doc = $toml:literal] => #[doc = $md:literal]) => {
+ concat!(
+ "This contents in Cargo.toml:\n`````toml",
+ $toml,
+ "\n`````\n Generates the following:\n\
+ <table><tr><th>Preview</th></tr><tr><td>\n\n",
+ $md,
+ "\n</td></tr></table>\n\n&nbsp;\n",
+ )
+ };
+}
+
+// The following struct is inserted only during generation of the documentation in order to exploit doc-tests.
+// These doc-tests are used to check that invalid arguments to the `document_features!` macro cause a compile time error.
+// For a more principled way of testing compilation error, maybe investigate <https://docs.rs/trybuild>.
+//
+/// ```rust
+/// #![doc = document_features::document_features!()]
+/// #![doc = document_features::document_features!(feature_label = "**`{feature}`**")]
+/// #![doc = document_features::document_features!(feature_label = r"**`{feature}`**")]
+/// #![doc = document_features::document_features!(feature_label = r#"**`{feature}`**"#)]
+/// #![doc = document_features::document_features!(feature_label = "<span class=\"stab portability\"><code>{feature}</code></span>")]
+/// #![doc = document_features::document_features!(feature_label = r#"<span class="stab portability"><code>{feature}</code></span>"#)]
+/// ```
+/// ```compile_fail
+/// #![doc = document_features::document_features!(feature_label > "<span>{feature}</span>")]
+/// ```
+/// ```compile_fail
+/// #![doc = document_features::document_features!(label = "<span>{feature}</span>")]
+/// ```
+/// ```compile_fail
+/// #![doc = document_features::document_features!(feature_label = "{feat}")]
+/// ```
+/// ```compile_fail
+/// #![doc = document_features::document_features!(feature_label = 3.14)]
+/// ```
+/// ```compile_fail
+/// #![doc = document_features::document_features!(feature_label = )]
+/// ```
+/// ```compile_fail
+/// #![doc = document_features::document_features!(feature_label = "**`{feature}`**" extra)]
+/// ```
+#[cfg(doc)]
+struct FeatureLabelCompilationTest;
+
+#[cfg(test)]
+mod tests {
+ use super::{process_toml, Args};
+
+ #[track_caller]
+ fn test_error(toml: &str, expected: &str) {
+ let err = process_toml(toml, &Args::default()).unwrap_err();
+ assert!(err.contains(expected), "{:?} does not contain {:?}", err, expected)
+ }
+
+ #[test]
+ fn only_get_balanced_in_correct_table() {
+ process_toml(
+ r#"
+
+[package.metadata.release]
+pre-release-replacements = [
+ {test=\"\#\# \"},
+]
+[abcd]
+[features]#xyz
+#! abc
+#
+###
+#! def
+#!
+## 123
+## 456
+feat1 = ["plop"]
+#! ghi
+no_doc = []
+##
+feat2 = ["momo"]
+#! klm
+default = ["feat1", "something_else"]
+#! end
+ "#,
+ &Args::default(),
+ )
+ .unwrap();
+ }
+
+ #[test]
+ fn no_features() {
+ let r = process_toml(
+ r#"
+[features]
+[dependencies]
+foo = 4;
+"#,
+ &Args::default(),
+ )
+ .unwrap();
+ assert_eq!(r, "*No documented features in Cargo.toml*");
+ }
+
+ #[test]
+ fn no_features2() {
+ let r = process_toml(
+ r#"
+[packages]
+[dependencies]
+"#,
+ &Args::default(),
+ )
+ .unwrap();
+ assert_eq!(r, "*No documented features in Cargo.toml*");
+ }
+
+ #[test]
+ fn parse_error3() {
+ test_error(
+ r#"
+[features]
+ff = []
+[abcd
+efgh
+[dependencies]
+"#,
+ "Parse error while parsing line: [abcd",
+ );
+ }
+
+ #[test]
+ fn parse_error4() {
+ test_error(
+ r#"
+[features]
+## dd
+## ff
+#! ee
+## ff
+"#,
+ "Cannot mix",
+ );
+ }
+
+ #[test]
+ fn parse_error5() {
+ test_error(
+ r#"
+[features]
+## dd
+"#,
+ "not associated with a feature",
+ );
+ }
+
+ #[test]
+ fn parse_error6() {
+ test_error(
+ r#"
+[features]
+# ff
+foo = []
+default = [
+#ffff
+# ff
+"#,
+ "Parse error while parsing value default",
+ );
+ }
+
+ #[test]
+ fn parse_error7() {
+ test_error(
+ r#"
+[features]
+# f
+foo = [ x = { ]
+bar = []
+"#,
+ "Parse error while parsing value foo",
+ );
+ }
+
+ #[test]
+ fn not_a_feature1() {
+ test_error(
+ r#"
+## hallo
+[features]
+"#,
+ "Not a feature: `[features]`",
+ );
+ }
+
+ #[test]
+ fn not_a_feature2() {
+ test_error(
+ r#"
+[package]
+## hallo
+foo = []
+"#,
+ "Comment cannot be associated with a feature: \"hallo\"",
+ );
+ }
+
+ #[test]
+ fn non_optional_dep1() {
+ test_error(
+ r#"
+[dev-dependencies]
+## Not optional
+foo = { version = "1.2", optional = false }
+"#,
+ "Dependency foo is not an optional dependency",
+ );
+ }
+
+ #[test]
+ fn non_optional_dep2() {
+ test_error(
+ r#"
+[dev-dependencies]
+## Not optional
+foo = { version = "1.2" }
+"#,
+ "Dependency foo is not an optional dependency",
+ );
+ }
+
+ #[test]
+ fn basic() {
+ let toml = r#"
+[abcd]
+[features]#xyz
+#! abc
+#
+###
+#! def
+#!
+## 123
+## 456
+feat1 = ["plop"]
+#! ghi
+no_doc = []
+##
+feat2 = ["momo"]
+#! klm
+default = ["feat1", "something_else"]
+#! end
+ "#;
+ let parsed = process_toml(toml, &Args::default()).unwrap();
+ assert_eq!(
+ parsed,
+ " abc\n def\n\n* **`feat1`** *(enabled by default)* — 123\n 456\n\n ghi\n* **`feat2`**\n\n klm\n end\n"
+ );
+ let parsed = process_toml(
+ toml,
+ &Args {
+ feature_label: Some(
+ "<span class=\"stab portability\"><code>{feature}</code></span>".into(),
+ ),
+ },
+ )
+ .unwrap();
+ assert_eq!(
+ parsed,
+ " abc\n def\n\n* <span class=\"stab portability\"><code>feat1</code></span> *(enabled by default)* — 123\n 456\n\n ghi\n* <span class=\"stab portability\"><code>feat2</code></span>\n\n klm\n end\n"
+ );
+ }
+
+ #[test]
+ fn dependencies() {
+ let toml = r#"
+#! top
+[dev-dependencies] #yo
+## dep1
+dep1 = { version="1.2", optional=true}
+#! yo
+dep2 = "1.3"
+## dep3
+[target.'cfg(unix)'.build-dependencies.dep3]
+version = "42"
+optional = true
+ "#;
+ let parsed = process_toml(toml, &Args::default()).unwrap();
+ assert_eq!(parsed, " top\n* **`dep1`** — dep1\n\n yo\n* **`dep3`** — dep3\n");
+ let parsed = process_toml(
+ toml,
+ &Args {
+ feature_label: Some(
+ "<span class=\"stab portability\"><code>{feature}</code></span>".into(),
+ ),
+ },
+ )
+ .unwrap();
+ assert_eq!(parsed, " top\n* <span class=\"stab portability\"><code>dep1</code></span> — dep1\n\n yo\n* <span class=\"stab portability\"><code>dep3</code></span> — dep3\n");
+ }
+
+ #[test]
+ fn multi_lines() {
+ let toml = r#"
+[package.metadata.foo]
+ixyz = [
+ ["array"],
+ [
+ "of",
+ "arrays"
+ ]
+]
+[dev-dependencies]
+## dep1
+dep1 = {
+ version="1.2-}",
+ optional=true
+}
+[features]
+default = [
+ "goo",
+ "\"]",
+ "bar",
+]
+## foo
+foo = [
+ "bar"
+]
+## bar
+bar = [
+
+]
+ "#;
+ let parsed = process_toml(toml, &Args::default()).unwrap();
+ assert_eq!(
+ parsed,
+ "* **`dep1`** — dep1\n* **`foo`** — foo\n* **`bar`** *(enabled by default)* — bar\n"
+ );
+ let parsed = process_toml(
+ toml,
+ &Args {
+ feature_label: Some(
+ "<span class=\"stab portability\"><code>{feature}</code></span>".into(),
+ ),
+ },
+ )
+ .unwrap();
+ assert_eq!(
+ parsed,
+ "* <span class=\"stab portability\"><code>dep1</code></span> — dep1\n* <span class=\"stab portability\"><code>foo</code></span> — foo\n* <span class=\"stab portability\"><code>bar</code></span> *(enabled by default)* — bar\n"
+ );
+ }
+
+ #[test]
+ fn dots_in_feature() {
+ let toml = r#"
+[features]
+## This is a test
+"teßt." = []
+default = ["teßt."]
+[dependencies]
+## A dep
+"dep" = { version = "123", optional = true }
+ "#;
+ let parsed = process_toml(toml, &Args::default()).unwrap();
+ assert_eq!(
+ parsed,
+ "* **`teßt.`** *(enabled by default)* — This is a test\n* **`dep`** — A dep\n"
+ );
+ let parsed = process_toml(
+ toml,
+ &Args {
+ feature_label: Some(
+ "<span class=\"stab portability\"><code>{feature}</code></span>".into(),
+ ),
+ },
+ )
+ .unwrap();
+ assert_eq!(
+ parsed,
+ "* <span class=\"stab portability\"><code>teßt.</code></span> *(enabled by default)* — This is a test\n* <span class=\"stab portability\"><code>dep</code></span> — A dep\n"
+ );
+ }
+}
diff --git a/third_party/rust/document-features/rustfmt.toml b/third_party/rust/document-features/rustfmt.toml
new file mode 100644
index 0000000000..2a35f0230c
--- /dev/null
+++ b/third_party/rust/document-features/rustfmt.toml
@@ -0,0 +1 @@
+use_small_heuristics = "Max"
diff --git a/third_party/rust/document-features/tests/self-doc.rs b/third_party/rust/document-features/tests/self-doc.rs
new file mode 100644
index 0000000000..4e27113145
--- /dev/null
+++ b/third_party/rust/document-features/tests/self-doc.rs
@@ -0,0 +1,37 @@
+#[test]
+fn ensure_it_compiles() {
+ document_features::document_features!();
+ document_features::document_features!(feature_label = "**`{feature}`**");
+ document_features::document_features!(feature_label = r"**`{feature}`**");
+ document_features::document_features!(feature_label = r#"**`{feature}`**"#);
+ document_features::document_features!(
+ feature_label = "<span class=\"stab portability\"><code>{feature}</code></span>"
+ );
+ document_features::document_features!(
+ feature_label = r#"<span class="stab portability"><code>{feature}</code></span>"#
+ );
+ document_features::document_features!(
+ feature_label = r##"<span class="stab portability"><code>{feature}</code></span>"##
+ );
+}
+
+#[test]
+fn self_doc() {
+ let actual = document_features::document_features!();
+ let expected = "* **`self-test`** — Internal feature used only for the tests, don't enable\n";
+ assert_eq!(actual, expected);
+}
+
+#[test]
+fn self_doc_with_custom_label() {
+ let actual = document_features::document_features!(
+ feature_label = r#"<span class="stab portability"><code>{feature}</code></span>"#
+ );
+ let expected =
+ "* <span class=\"stab portability\"><code>self-test</code></span> — Internal feature used only for the tests, don't enable\n";
+ assert_eq!(actual, expected);
+ let actual2 = document_features::document_features!(
+ feature_label = "<span class=\"stab\u{0020}portability\"><code>{feature}</code></span>"
+ );
+ assert_eq!(actual2, expected);
+}
diff --git a/third_party/rust/glean-core/.cargo-checksum.json b/third_party/rust/glean-core/.cargo-checksum.json
index 859a8ecdae..54674fc768 100644
--- a/third_party/rust/glean-core/.cargo-checksum.json
+++ b/third_party/rust/glean-core/.cargo-checksum.json
@@ -1 +1 @@
-{"files":{"Cargo.toml":"85eba7a464630582734247336bb970481f894c7b6e418d6eb5c868693af35a14","LICENSE":"1f256ecad192880510e84ad60474eab7589218784b9a50bc7ceee34c2b91f1d5","README.md":"026495898699b54608eb4ec16074ffafc57920d80ccb59961c501a1ea28c9985","build.rs":"4857bea99c6b8c08db8818efa9d3738716f52d3acb68159323957ae52892a3eb","src/common_metric_data.rs":"72051c4349885d4a94fc41bb4edda88d31551f2f9ebcdb4e868a98161bc76233","src/core/mod.rs":"4749e71ba495d5aed48cb4d311e0beb7e9d53c10b18ab33938e8fd9c3cfa5666","src/core_metrics.rs":"a877e42e0f8b932adb52a5681ad76fd977808cb48c7eeb29b1e4bbe804f1ea96","src/coverage.rs":"49613fd310bd24d779472720975fbe6c97ec370a95eb55f10afa43f67539c942","src/database/mod.rs":"3917bad7773696a43ab58e7860d5a8f1d63dca7c27920343aa8786acc5a586cd","src/debug.rs":"90158cc5d488ba67b60d06647e54e59a1d7bdeb906087e4fe4cfab4373c1cc6c","src/dispatcher/global.rs":"f69cd81a90a37c306d4e0ce8177ea5a3ae2ffda5b431ae46b9a22c9e38891271","src/dispatcher/mod.rs":"440a331a7beeaa6e9824c2fd4306c09ce2a115a358d5beb830dba4d69aec3563","src/error.rs":"b93c7d3e243b21bb9eafc95f17860aba1a942b2f2b0a7f43307690f05fece516","src/error_recording.rs":"d7858647808d81173558e12de88f3fbe4e589969e3bd246bfb388f90f8ff3814","src/event_database/mod.rs":"9d4d3e4b075dc585c00317304401b2c9115f39db8fdbd9f1c93d3fc6fd350fd5","src/fd_logger.rs":"0c9def6fa53db1a2ab93c85795f8a7df57797bcfd3978146923e151752e291a6","src/glean.udl":"13150d3344874d8d801aaa76880ba191ab99e337af5ac5c3cbb55bc70e5daa8a","src/glean_metrics.rs":"9414fb1453d19f6832df33e4c6ef7383d62203e47026bf5bc9552b083101ddd1","src/histogram/exponential.rs":"58bb1770bae45770d92995515d328eb50a7e78726224f779446ae7d1632a6a3e","src/histogram/functional.rs":"1a63a305b48bcef7bc38136b40d916df4bb8f098dc602514ada54a9b091f6951","src/histogram/linear.rs":"4342a1733175d7f97b2b41adb18100537c206100c9fccb5bd13bd782c9cb3c9a","src/histogram/mod.rs":"eeb7aff80806ab76cdce101dc08887b5552f8b4bdf64683f64f767e0f06a889d","src/internal_metrics.rs":"263779535963a804c8c7fa6f8e284ac8ec7f415ceeadbb6a8f913a1e7073ae18","src/internal_pings.rs":"f7a3a3aef3661ae0066ba1d2890a5e8e6871a7a8016b5f7b4da077663bc0c0d0","src/lib.rs":"778a3778ce89db007b175e08b3a4bfd1104585709ba6d5c1e3972d388f3a6f8e","src/lib_unit_tests.rs":"f053c04aab63ddb86303bca7ceea5bbf104bc7de7c89213e7cadf0157db57645","src/metrics/boolean.rs":"0591043a88e81c77b694cc8124b9e4996e81a46df1402e1bdf364d2885d8215e","src/metrics/counter.rs":"57544e9fa971086b89a4844ab9cbc55ee5b79c9d391c7d9d9ac74a7bc844b076","src/metrics/custom_distribution.rs":"9ca60176837e216da3655b2a3ba08a296444aaa7d966ddd498478fdfb2b21049","src/metrics/datetime.rs":"ca12a426fbc564847d64e6ddcaa3f298065ba4bca0372c638058c20909f626d5","src/metrics/denominator.rs":"7bacb81ea2b81da8775f61544022d35d91f159e1aa494944eaf9536cacc18b4d","src/metrics/event.rs":"62d450e4db227b6c12b9179933e4b3a9df92abc4aa7da14c9274dc82e549e210","src/metrics/experiment.rs":"48aaf06b32a691500dbc51be95955071947d7760e3b81d30ac8d8d0161c1df3f","src/metrics/labeled.rs":"c6e2200694e3cd623e8369420d3b35759b117ef9e8188fd33afaa81685fdce28","src/metrics/memory_distribution.rs":"e9456afc7759980d06d1e2e3620a3117995d27d16bc3c5a05ea8c023475ae47a","src/metrics/memory_unit.rs":"d7a678e5242febd021283b30c0099a9e62729944816a3f17d2d91e2808bc0570","src/metrics/metrics_enabled_config.rs":"87fed12219c756ecf1e5c8cd6a21f26999b6bbcf3ffc1b5467b0a58ca5ad35d8","src/metrics/mod.rs":"759ba845d4598e3fea4877d2687da958b15480ec453562c48dac7872ab300ee8","src/metrics/numerator.rs":"442236e1d63b31299f3f073bead683101de995845a638834201c6f30fc03ea90","src/metrics/ping.rs":"5a7b483450fdc145ee8e73f7efef27a9e2b8b551ef5682fc08fcb445a92b5c0d","src/metrics/quantity.rs":"915ab10cf1e1666fc2c707bc51232843e914d93eea2a249eb15df28b6a74cd2b","src/metrics/rate.rs":"38a6986387ec3927dbd81d8a881e257674e91bb97ccd43eb6285f691d5e06531","src/metrics/recorded_experiment.rs":"33958abee79d8b55dec4cb5d20742640423713010f76314075cefde18b5c118a","src/metrics/string.rs":"f7ffc07c23bedc1b8579121d931468b0713834fc037f7945267257fdbdf9a5d0","src/metrics/string_list.rs":"4eeb320cb24dec60c5551c6c9630dbbb292725a5d4c10df8f6c6e2a111eea25e","src/metrics/text.rs":"d727bcc14d2c4f946a517ac0110f585bfbe4898f813bdbad68756505529300f6","src/metrics/time_unit.rs":"b7578010c6270a45b30342b59189a862b2ede9dd24e9afae3e90fa6b970b3d24","src/metrics/timespan.rs":"ae1a2966f0a5446327ad2b9ca0738992976350ad334a5e11fd4ee611a690394e","src/metrics/timing_distribution.rs":"57d4b3f19e7382f80cc596406d3d1c191f61bc3c81f92bf137a13c2fa174c822","src/metrics/url.rs":"9bec842b7a811f01c1f0a0deb54b2dac2f8488882e7e9251cd91d18e6b500939","src/metrics/uuid.rs":"f824019fe3e8d8945490b7eb0ac1c6b7b2ffc991543f4b6332d12827955cf420","src/ping/mod.rs":"e805bfa51007c30c5a549be8d8f7ccbe502db4a8ad5727abaf298a9348f584c0","src/scheduler.rs":"129863e31205404a3d1708627a62583324c347d143f976216f769893ec541ea0","src/storage/mod.rs":"04dc1a94be1d59097cd87b14386952a6ec8b9115bc06397ae389a323f6f55dcc","src/system.rs":"e3d1b54e1d39cafe6f4dc7ff5021b08c879733f909951b0e1332b3efa9ed97bd","src/traits/boolean.rs":"be0e130f8043215705becc956d45b126c340568f1b24a396c0af9b4334a41ced","src/traits/counter.rs":"c686d26e131d854cd7a7df83c900ca7c17a03c663a30cf58ab48c7259476ce85","src/traits/custom_distribution.rs":"159a5e26fb2326f5bcdc46979aa9c6481c5f0e93ecf957f668bb3f6988d8b00f","src/traits/datetime.rs":"636ac1456b1b042e38cf5ae6193c5b232ea0b80df62f583a2097891baef9641b","src/traits/event.rs":"3f48aa336854141784d121f7fa9e283f6ff708a9214f9c0aade3a68cc38dda99","src/traits/labeled.rs":"c633c68e70a44e73f8aff88aaab1029c0faded3cad08d822590ed8838f24b4fd","src/traits/memory_distribution.rs":"55bb8f45e948319fbba9d28a50d8742da134b066a42e480887db7c7e435f4096","src/traits/mod.rs":"d0aa19a7cd97326fd2e026635406a5a9403953ced4954443a2bcbca32d868554","src/traits/numerator.rs":"6e4f236bdc448f1bde7a8c249dcd086204c2c69990d3f444e746290929226ed3","src/traits/ping.rs":"8831c106c03afeb458b0b028fa1ce61f056ebf8e82bc0a171a1bff255d920748","src/traits/quantity.rs":"6ffe25c913bef4315573d747308c182de740b2a4e02ba22cd21d0c33ba521f31","src/traits/rate.rs":"f000790440e0f389f0b160526a9a9a266e58d1405915ae56ac550f482858222c","src/traits/string.rs":"0c3c88382ff2e8eba89c7cfe129c4b84e31140af717819533c14919541ad790c","src/traits/string_list.rs":"14e56b62c2c2be1dd8013f12001f235b084abd2a0d5aa2f7932843877af49ac0","src/traits/text.rs":"8af7d3a0c87cfd8c6d33d6ad47532b431055bbdd395f9110da5630222c23cf93","src/traits/timespan.rs":"52be325a9c061916f34c5b638a07a93b4a14aa89fe365783103d2e06b998f547","src/traits/timing_distribution.rs":"76a7b8640128769763a275e831ed1f1e9ba9bfaab5ff10de638d5be3c57b5421","src/traits/url.rs":"c27f7add23214ff051078b65b88120b620560d2841a1056c7214d5237e86b9e4","src/traits/uuid.rs":"81322e71c7e847bacaf827a2cd58f6193bdc208355524207f7f38db039da6aa8","src/upload/directory.rs":"5fa2c64a2bc561883ec01ed274179d91ff86d40d4d9837661be1b85cd067177c","src/upload/mod.rs":"d281050d7ab29980d48a44240256c21e82858dd795b60549fb46cfd5ce7a0214","src/upload/policy.rs":"c250957a37783e74af8002cd80ba06ef9780a389fb0f61b8b665b79688f0a360","src/upload/request.rs":"a16fbe823228e73a2acc6f4324d6c635be22dfefb19ae71146245d9236baa87a","src/upload/result.rs":"7efbbe50e8d36beb3f23e7bfd172d22e1c003472d2dd8055b06f6050c36437c5","src/util.rs":"ee7500434d9758a320dd410f18d7e18da956591e19d2555db87eef9623e4b916","tests/boolean.rs":"76d6014ff108cb6514d9bceb1b2b14749a55b09921f4595a5e30f1bd3546e9f0","tests/common/mod.rs":"c1d980a9cff0b64f452ebbe43f24d70aa685b80b48db08fc4338a60466b07a5e","tests/counter.rs":"3663a3f5ec5c0bd2b758a9920cd20cc619a12566b445e4421ec7c98232bf5a32","tests/custom_distribution.rs":"53530972243670ef58c85a906b70931d14e81ae2e1f69092c71a27e561edff6c","tests/datetime.rs":"ec3c9760e70bb2cbc61ab23281c891bc1ec493c5c545466c29fd13e4f05c2c96","tests/event.rs":"bf5b8d3ee9d12d12e91b71dd46a813a2cf17f0544f6d2e3b14f6f931ce276fa1","tests/labeled.rs":"e9ea6dba17059d68114efce0c23373be9ceed922bf5e638a2158a6422c75a1c1","tests/memory_distribution.rs":"a5a7aa955e60823ea29a6f4bc96c61e41f1e41f08958aa4854668cf8fe04cde6","tests/ping.rs":"a0fbaed178459c6e3ed3363d966d4a9119cbbcf94574f336f2e7c37eb4b59324","tests/ping_maker.rs":"40dfa0f45af6a35364c068bc53f540b27c95483204104377e0f619a9b10bc711","tests/quantity.rs":"55e7dca346fd1d27f0974b78ca3fb12427cb5da2ee637afc08a54f360f947361","tests/rate.rs":"1de571b9f0ee9a9006cbc8a31f91352d3ff1190b50840f0f668b470a7cd2a3a5","tests/storage.rs":"f0c8312bd789d7bda502cd45f35fef6b8591652bd194d07da4d81935ebe69b48","tests/string.rs":"7ece988a4b8efe6932ccb90bfe2f3c8aaea983777e99d7de6028bf6a29459ee6","tests/string_list.rs":"77188a2b90663c3f8dac5da89a6cb6b1d16a9f8c66ccd032d02966dfd14a3486","tests/text.rs":"1d43f6b90a43124311cacf0a6ee16f9e1e9263bcd11fee8b996d6efd81633638","tests/timespan.rs":"d50d75c7d75da3a878d67331cb0df8ae5e6a099ffab474361f71a408e02528d7","tests/timing_distribution.rs":"dbe2c8efa5dfb8037765b50433568c04ba111953822584da1c4931837fdfc060","tests/uuid.rs":"052ad26a6927c56272219340211cf4a059d200f14287b482fe8621d7bce3cc54","uniffi.toml":"6ddc98b686b0925a81abd9d1c769e5c98ac29771b210a1c535931a46dec9a8e3"},"package":"6831cadd28b625bc296732d71dc7c978f208ba27911cad072785f87f23b1e634"} \ No newline at end of file
+{"files":{"Cargo.toml":"2dde200f0e0e4e523634f8c2c8c1c2ca75af83163ac7b0ba8f62f3096fd0c97d","LICENSE":"1f256ecad192880510e84ad60474eab7589218784b9a50bc7ceee34c2b91f1d5","README.md":"026495898699b54608eb4ec16074ffafc57920d80ccb59961c501a1ea28c9985","build.rs":"4857bea99c6b8c08db8818efa9d3738716f52d3acb68159323957ae52892a3eb","src/common_metric_data.rs":"72051c4349885d4a94fc41bb4edda88d31551f2f9ebcdb4e868a98161bc76233","src/core/mod.rs":"8f5e98a108ec5d1849402af1de90b5f53ba839240743c2c5283a49a4045e1293","src/core_metrics.rs":"a877e42e0f8b932adb52a5681ad76fd977808cb48c7eeb29b1e4bbe804f1ea96","src/coverage.rs":"49613fd310bd24d779472720975fbe6c97ec370a95eb55f10afa43f67539c942","src/database/mod.rs":"3917bad7773696a43ab58e7860d5a8f1d63dca7c27920343aa8786acc5a586cd","src/debug.rs":"90158cc5d488ba67b60d06647e54e59a1d7bdeb906087e4fe4cfab4373c1cc6c","src/dispatcher/global.rs":"f69cd81a90a37c306d4e0ce8177ea5a3ae2ffda5b431ae46b9a22c9e38891271","src/dispatcher/mod.rs":"440a331a7beeaa6e9824c2fd4306c09ce2a115a358d5beb830dba4d69aec3563","src/error.rs":"b93c7d3e243b21bb9eafc95f17860aba1a942b2f2b0a7f43307690f05fece516","src/error_recording.rs":"d7858647808d81173558e12de88f3fbe4e589969e3bd246bfb388f90f8ff3814","src/event_database/mod.rs":"9d4d3e4b075dc585c00317304401b2c9115f39db8fdbd9f1c93d3fc6fd350fd5","src/fd_logger.rs":"0c9def6fa53db1a2ab93c85795f8a7df57797bcfd3978146923e151752e291a6","src/glean.udl":"24d9e431f95d79dc4254feff68f19a4ea4e6e76c33b110e10c5e5dbd5bc64ff2","src/glean_metrics.rs":"9414fb1453d19f6832df33e4c6ef7383d62203e47026bf5bc9552b083101ddd1","src/histogram/exponential.rs":"58bb1770bae45770d92995515d328eb50a7e78726224f779446ae7d1632a6a3e","src/histogram/functional.rs":"1a63a305b48bcef7bc38136b40d916df4bb8f098dc602514ada54a9b091f6951","src/histogram/linear.rs":"4342a1733175d7f97b2b41adb18100537c206100c9fccb5bd13bd782c9cb3c9a","src/histogram/mod.rs":"eeb7aff80806ab76cdce101dc08887b5552f8b4bdf64683f64f767e0f06a889d","src/internal_metrics.rs":"263779535963a804c8c7fa6f8e284ac8ec7f415ceeadbb6a8f913a1e7073ae18","src/internal_pings.rs":"7267166a8e357053526c545cf62bb502a7b6f07aed1de48d43041228d8835366","src/lib.rs":"367ea21f9d3f1c808b258011821d8505cd47d29eff8e8e6d938623e6e9997b73","src/lib_unit_tests.rs":"46897c6bb4003c5e00152d7b55c00d3176b5bffb28d8669a3fb0d10e5233e3a5","src/metrics/boolean.rs":"2b9ef57e3582c9bd8b2cca8ab94c962a4871ecc00e837b913c9b0349ba9dff08","src/metrics/counter.rs":"b4a52a8167fb0edd6354f952525e59f3eadb4261de3483374f03c94449d30b92","src/metrics/custom_distribution.rs":"e1f2edfefb67da4bf369bab3d3047f4ff6539a1fea0eee81c78d96626e5b4bb0","src/metrics/datetime.rs":"e4405762fc71718299fa1b208e3d5fda654bd1b82fe908c884c284e3530de2ec","src/metrics/denominator.rs":"95e8442f90bad97f80fc74b146782f215344b52c5f3825ae0a8baffdc001a714","src/metrics/event.rs":"7281d8b63f34758a47abd7ae3956f44701d1fd48433ccba7a4302526a9912255","src/metrics/experiment.rs":"5f9278cca4e133eb8df33bbfe36d1fe0ef3eade8c09f1b46db3c4d0790515412","src/metrics/labeled.rs":"8d6e76a07064d132cd617c7901f2bc11ff6ba31e3483ba3b96354a4a3736b58d","src/metrics/memory_distribution.rs":"7f6ca51acb470df277ff14427c0e7bb07d921c0a0087d0cc56aebe038d198ccc","src/metrics/memory_unit.rs":"d7a678e5242febd021283b30c0099a9e62729944816a3f17d2d91e2808bc0570","src/metrics/metrics_enabled_config.rs":"87fed12219c756ecf1e5c8cd6a21f26999b6bbcf3ffc1b5467b0a58ca5ad35d8","src/metrics/mod.rs":"8f8958b8cedfe01df6c97ec26b63f14fd7516f9de7ba62984062db96b5708720","src/metrics/numerator.rs":"937dfd583b797ac798a525cedca95c5a36262356760a89670d8113983c263154","src/metrics/object.rs":"89ce5190ed681b26b74a06a4ecaf9f96c36f96be1276f1fdb40f4406648e08c1","src/metrics/ping.rs":"4ccdf0ae2ac6f3e5a352334797d2805f1a3d932e92f08447285dd9bec4e7d724","src/metrics/quantity.rs":"aa13a8f8cf8e5e0281668fbbafc2998411df2a499479423558fd91b9bd7f8702","src/metrics/rate.rs":"603cc45c149c7a27c93b6a80146bf43f8ce70d9655f905bb5be6bc2c15bcb22b","src/metrics/recorded_experiment.rs":"33958abee79d8b55dec4cb5d20742640423713010f76314075cefde18b5c118a","src/metrics/string.rs":"2418632c492463970c3eca533d5318f519698bb361d73dd8781db108d7d1fbd8","src/metrics/string_list.rs":"ed53a095184c3e8224d0511809b5d7601ba3166505a39b0570f24ebeb0a5b97c","src/metrics/text.rs":"5c994a282b16b9dde6d6dc4922475457a72c82f64248778811b84db70ed4c116","src/metrics/time_unit.rs":"b7578010c6270a45b30342b59189a862b2ede9dd24e9afae3e90fa6b970b3d24","src/metrics/timespan.rs":"b0fda3a45597c8306a0d1928dcf0837538859e66ebd9db113ebb6efbea721d4c","src/metrics/timing_distribution.rs":"5da04272dd8b44502ffd0b60b12c84239a7fe359a51754b6c0cd96388a4e8a3c","src/metrics/url.rs":"f6b27a60d13a1268f0115c5d292c9b16b6bc370055961368cb2648283b7140a0","src/metrics/uuid.rs":"cacffd95ab30ed327ec2fa5feaf1359e667706746401f1e2c1195ad9553c4b54","src/ping/mod.rs":"fcadd52d2d536c9ace01f8a3812c3fb3c39b8094915db1b3656839fb87f771b5","src/scheduler.rs":"129863e31205404a3d1708627a62583324c347d143f976216f769893ec541ea0","src/storage/mod.rs":"04dc1a94be1d59097cd87b14386952a6ec8b9115bc06397ae389a323f6f55dcc","src/system.rs":"e3d1b54e1d39cafe6f4dc7ff5021b08c879733f909951b0e1332b3efa9ed97bd","src/traits/boolean.rs":"be0e130f8043215705becc956d45b126c340568f1b24a396c0af9b4334a41ced","src/traits/counter.rs":"c686d26e131d854cd7a7df83c900ca7c17a03c663a30cf58ab48c7259476ce85","src/traits/custom_distribution.rs":"0bd1d425e4c059cca6af2dfb13c78e5e4c6c07fb46c7e31489ad0c5959854833","src/traits/datetime.rs":"636ac1456b1b042e38cf5ae6193c5b232ea0b80df62f583a2097891baef9641b","src/traits/event.rs":"3f48aa336854141784d121f7fa9e283f6ff708a9214f9c0aade3a68cc38dda99","src/traits/labeled.rs":"c633c68e70a44e73f8aff88aaab1029c0faded3cad08d822590ed8838f24b4fd","src/traits/memory_distribution.rs":"55bb8f45e948319fbba9d28a50d8742da134b066a42e480887db7c7e435f4096","src/traits/mod.rs":"d14b69d0946848c1f92cc8977cbc3fc9338ff1b53b7acc31ea0fe2f1122beecb","src/traits/numerator.rs":"6e4f236bdc448f1bde7a8c249dcd086204c2c69990d3f444e746290929226ed3","src/traits/object.rs":"c03bad670ec7affbc578247f9e1904e898c1870b9bf25750c5094113f995623f","src/traits/ping.rs":"8831c106c03afeb458b0b028fa1ce61f056ebf8e82bc0a171a1bff255d920748","src/traits/quantity.rs":"6ffe25c913bef4315573d747308c182de740b2a4e02ba22cd21d0c33ba521f31","src/traits/rate.rs":"f000790440e0f389f0b160526a9a9a266e58d1405915ae56ac550f482858222c","src/traits/string.rs":"0c3c88382ff2e8eba89c7cfe129c4b84e31140af717819533c14919541ad790c","src/traits/string_list.rs":"14e56b62c2c2be1dd8013f12001f235b084abd2a0d5aa2f7932843877af49ac0","src/traits/text.rs":"8af7d3a0c87cfd8c6d33d6ad47532b431055bbdd395f9110da5630222c23cf93","src/traits/timespan.rs":"52be325a9c061916f34c5b638a07a93b4a14aa89fe365783103d2e06b998f547","src/traits/timing_distribution.rs":"00ebdef647a7a208c01d13ba7b3996750e36de98d1f63859b609c80c8df25b6f","src/traits/url.rs":"c27f7add23214ff051078b65b88120b620560d2841a1056c7214d5237e86b9e4","src/traits/uuid.rs":"81322e71c7e847bacaf827a2cd58f6193bdc208355524207f7f38db039da6aa8","src/upload/directory.rs":"6359220db9d85ee0f3931ca518f95ffb2020c1c03bd632f17ed5c16ddd00343b","src/upload/mod.rs":"a388563d5e2940c5c28b48fc7b67ca507512efccae95fd1c2f04b15ec21aa08c","src/upload/policy.rs":"c250957a37783e74af8002cd80ba06ef9780a389fb0f61b8b665b79688f0a360","src/upload/request.rs":"0b7e215f61499a681d1cebc9cf4a0efbaae2f543a5d44e5db40cbe61ed90549e","src/upload/result.rs":"7efbbe50e8d36beb3f23e7bfd172d22e1c003472d2dd8055b06f6050c36437c5","src/util.rs":"ee7500434d9758a320dd410f18d7e18da956591e19d2555db87eef9623e4b916","tests/boolean.rs":"76d6014ff108cb6514d9bceb1b2b14749a55b09921f4595a5e30f1bd3546e9f0","tests/common/mod.rs":"c1d980a9cff0b64f452ebbe43f24d70aa685b80b48db08fc4338a60466b07a5e","tests/counter.rs":"3663a3f5ec5c0bd2b758a9920cd20cc619a12566b445e4421ec7c98232bf5a32","tests/custom_distribution.rs":"41c593a0b4561e21f29d1a5b948de964a866253c58ca76ffefebe370fca150e0","tests/datetime.rs":"ec3c9760e70bb2cbc61ab23281c891bc1ec493c5c545466c29fd13e4f05c2c96","tests/event.rs":"67291cbcc4d1cba56ada6ba733fb1dc4c6327680059e8d7637add2ae45cd344b","tests/labeled.rs":"e9ea6dba17059d68114efce0c23373be9ceed922bf5e638a2158a6422c75a1c1","tests/memory_distribution.rs":"a5a7aa955e60823ea29a6f4bc96c61e41f1e41f08958aa4854668cf8fe04cde6","tests/object.rs":"8c35676e04f6ccf54a28764700915e753fc0355bfa5d7804d72caba66fd564cd","tests/ping.rs":"eb9f6be1aba21acc5dc670622bf622976718a706df1cc2095efa56a8e8b3fe1a","tests/ping_maker.rs":"b267ecf7c714ff27512424b743da0ea4f05a87755c1b96355bfca3e173e3f62e","tests/quantity.rs":"55e7dca346fd1d27f0974b78ca3fb12427cb5da2ee637afc08a54f360f947361","tests/rate.rs":"1de571b9f0ee9a9006cbc8a31f91352d3ff1190b50840f0f668b470a7cd2a3a5","tests/storage.rs":"f0c8312bd789d7bda502cd45f35fef6b8591652bd194d07da4d81935ebe69b48","tests/string.rs":"7ece988a4b8efe6932ccb90bfe2f3c8aaea983777e99d7de6028bf6a29459ee6","tests/string_list.rs":"77188a2b90663c3f8dac5da89a6cb6b1d16a9f8c66ccd032d02966dfd14a3486","tests/text.rs":"1d43f6b90a43124311cacf0a6ee16f9e1e9263bcd11fee8b996d6efd81633638","tests/timespan.rs":"d50d75c7d75da3a878d67331cb0df8ae5e6a099ffab474361f71a408e02528d7","tests/timing_distribution.rs":"20860a7baccdcee6aed40c9cc8202b94f3b2e61164fbaf8f2af96b0f404a895a","tests/uuid.rs":"052ad26a6927c56272219340211cf4a059d200f14287b482fe8621d7bce3cc54","uniffi.toml":"6ddc98b686b0925a81abd9d1c769e5c98ac29771b210a1c535931a46dec9a8e3"},"package":"ed9acc46fd38c5c995a0537e76364496addace660839dc279079e5957e3c1093"} \ No newline at end of file
diff --git a/third_party/rust/glean-core/Cargo.toml b/third_party/rust/glean-core/Cargo.toml
index 44c159051d..9d33444fbd 100644
--- a/third_party/rust/glean-core/Cargo.toml
+++ b/third_party/rust/glean-core/Cargo.toml
@@ -13,7 +13,7 @@
edition = "2021"
rust-version = "1.66"
name = "glean-core"
-version = "57.0.0"
+version = "58.1.0"
authors = [
"Jan-Erik Rediger <jrediger@mozilla.com>",
"The Glean Team <glean-team@mozilla.com>",
@@ -35,7 +35,7 @@ license = "MPL-2.0"
repository = "https://github.com/mozilla/glean"
[package.metadata.glean]
-glean-parser = "11.0.1"
+glean-parser = "13.0.0"
[dependencies.bincode]
version = "1.2.1"
diff --git a/third_party/rust/glean-core/src/core/mod.rs b/third_party/rust/glean-core/src/core/mod.rs
index 5a8dd56cde..30f9a34f11 100644
--- a/third_party/rust/glean-core/src/core/mod.rs
+++ b/third_party/rust/glean-core/src/core/mod.rs
@@ -118,11 +118,11 @@ where
/// trim_data_to_registered_pings: false,
/// log_level: None,
/// rate_limit: None,
-/// enable_event_timestamps: false,
+/// enable_event_timestamps: true,
/// experimentation_id: None,
/// };
/// let mut glean = Glean::new(cfg).unwrap();
-/// let ping = PingType::new("sample", true, false, true, vec![]);
+/// let ping = PingType::new("sample", true, false, true, true, vec![]);
/// glean.register_ping_type(&ping);
///
/// let call_counter: CounterMetric = CounterMetric::new(CommonMetricData {
@@ -318,7 +318,7 @@ impl Glean {
trim_data_to_registered_pings: false,
log_level: None,
rate_limit: None,
- enable_event_timestamps: false,
+ enable_event_timestamps: true,
experimentation_id: None,
};
diff --git a/third_party/rust/glean-core/src/glean.udl b/third_party/rust/glean-core/src/glean.udl
index e531f64a26..e68a57ea4c 100644
--- a/third_party/rust/glean-core/src/glean.udl
+++ b/third_party/rust/glean-core/src/glean.udl
@@ -201,6 +201,10 @@ dictionary PingRequest {
sequence<u8> body;
// A map with all the headers to be sent with the request.
record<DOMString, string> headers;
+ // Whether the body has {client|ping}_info sections.
+ boolean body_has_info_sections;
+ // The ping's name. Likely also somewhere in `path`.
+ string ping_name;
};
// An enum representing the possible upload tasks to be performed by an uploader.
@@ -287,7 +291,7 @@ enum ErrorType {
};
interface PingType {
- constructor(string name, boolean include_client_id, boolean send_if_empty, boolean precise_timestamps, sequence<string> reason_codes);
+ constructor(string name, boolean include_client_id, boolean send_if_empty, boolean precise_timestamps, boolean include_info_sections, sequence<string> reason_codes);
void submit(optional string? reason = null);
};
@@ -480,6 +484,8 @@ interface TimingDistributionMetric {
void accumulate_samples(sequence<i64> samples);
+ void accumulate_single_sample(i64 sample);
+
DistributionData? test_get_value(optional string? ping_name = null);
i32 test_get_num_recorded_errors(ErrorType error);
@@ -523,6 +529,8 @@ interface CustomDistributionMetric {
void accumulate_samples(sequence<i64> samples);
+ void accumulate_single_sample(i64 sample);
+
DistributionData? test_get_value(optional string? ping_name = null);
i32 test_get_num_recorded_errors(ErrorType error);
diff --git a/third_party/rust/glean-core/src/internal_pings.rs b/third_party/rust/glean-core/src/internal_pings.rs
index 076a1f7485..07c3849006 100644
--- a/third_party/rust/glean-core/src/internal_pings.rs
+++ b/third_party/rust/glean-core/src/internal_pings.rs
@@ -26,6 +26,7 @@ impl InternalPings {
true,
true,
true,
+ true,
vec![
"active".to_string(),
"dirty_startup".to_string(),
@@ -37,6 +38,7 @@ impl InternalPings {
true,
false,
true,
+ true,
vec![
"overdue".to_string(),
"reschedule".to_string(),
@@ -50,6 +52,7 @@ impl InternalPings {
true,
false,
true,
+ true,
vec![
"startup".to_string(),
"inactive".to_string(),
@@ -61,6 +64,7 @@ impl InternalPings {
true,
true,
true,
+ true,
vec!["at_init".to_string(), "set_upload_enabled".to_string()],
),
}
diff --git a/third_party/rust/glean-core/src/lib.rs b/third_party/rust/glean-core/src/lib.rs
index a54e57a95b..b7f9d73beb 100644
--- a/third_party/rust/glean-core/src/lib.rs
+++ b/third_party/rust/glean-core/src/lib.rs
@@ -91,12 +91,12 @@ pub(crate) const DELETION_REQUEST_PINGS_DIRECTORY: &str = "deletion_request";
static INITIALIZE_CALLED: AtomicBool = AtomicBool::new(false);
/// Keep track of the debug features before Glean is initialized.
-static PRE_INIT_DEBUG_VIEW_TAG: OnceCell<Mutex<String>> = OnceCell::new();
+static PRE_INIT_DEBUG_VIEW_TAG: Mutex<String> = Mutex::new(String::new());
static PRE_INIT_LOG_PINGS: AtomicBool = AtomicBool::new(false);
-static PRE_INIT_SOURCE_TAGS: OnceCell<Mutex<Vec<String>>> = OnceCell::new();
+static PRE_INIT_SOURCE_TAGS: Mutex<Vec<String>> = Mutex::new(Vec::new());
/// Keep track of pings registered before Glean is initialized.
-static PRE_INIT_PING_REGISTRATION: OnceCell<Mutex<Vec<metrics::PingType>>> = OnceCell::new();
+static PRE_INIT_PING_REGISTRATION: Mutex<Vec<metrics::PingType>> = Mutex::new(Vec::new());
/// Global singleton of the handles of the glean.init threads.
/// For joining. For tests.
@@ -396,11 +396,9 @@ fn initialize_inner(
core::with_glean_mut(|glean| {
// The debug view tag might have been set before initialize,
// get the cached value and set it.
- if let Some(tag) = PRE_INIT_DEBUG_VIEW_TAG.get() {
- let lock = tag.try_lock();
- if let Ok(ref debug_tag) = lock {
- glean.set_debug_view_tag(debug_tag);
- }
+ let debug_tag = PRE_INIT_DEBUG_VIEW_TAG.lock().unwrap();
+ if debug_tag.len() > 0 {
+ glean.set_debug_view_tag(&debug_tag);
}
// The log pings debug option might have been set before initialize,
@@ -412,11 +410,9 @@ fn initialize_inner(
// The source tags might have been set before initialize,
// get the cached value and set them.
- if let Some(tags) = PRE_INIT_SOURCE_TAGS.get() {
- let lock = tags.try_lock();
- if let Ok(ref source_tags) = lock {
- glean.set_source_tags(source_tags.to_vec());
- }
+ let source_tags = PRE_INIT_SOURCE_TAGS.lock().unwrap();
+ if source_tags.len() > 0 {
+ glean.set_source_tags(source_tags.to_vec());
}
// Get the current value of the dirty flag so we know whether to
@@ -428,13 +424,9 @@ fn initialize_inner(
// Perform registration of pings that were attempted to be
// registered before init.
- if let Some(tags) = PRE_INIT_PING_REGISTRATION.get() {
- let lock = tags.try_lock();
- if let Ok(pings) = lock {
- for ping in &*pings {
- glean.register_ping_type(ping);
- }
- }
+ let pings = PRE_INIT_PING_REGISTRATION.lock().unwrap();
+ for ping in pings.iter() {
+ glean.register_ping_type(ping);
}
// If this is the first time ever the Glean SDK runs, make sure to set
@@ -861,7 +853,7 @@ pub(crate) fn register_ping_type(ping: &PingType) {
// if ping registration is attempted before Glean initializes.
// This state is kept across Glean resets, which should only ever happen in test mode.
// It's a set and keeping them around forever should not have much of an impact.
- let m = PRE_INIT_PING_REGISTRATION.get_or_init(Default::default);
+ let m = &PRE_INIT_PING_REGISTRATION;
let mut lock = m.lock().unwrap();
lock.push(ping.clone());
}
@@ -956,7 +948,7 @@ pub fn glean_set_debug_view_tag(tag: String) -> bool {
true
} else {
// Glean has not been initialized yet. Cache the provided tag value.
- let m = PRE_INIT_DEBUG_VIEW_TAG.get_or_init(Default::default);
+ let m = &PRE_INIT_DEBUG_VIEW_TAG;
let mut lock = m.lock().unwrap();
*lock = tag;
// When setting the debug view tag before initialization,
@@ -984,7 +976,7 @@ pub fn glean_set_source_tags(tags: Vec<String>) -> bool {
true
} else {
// Glean has not been initialized yet. Cache the provided source tags.
- let m = PRE_INIT_SOURCE_TAGS.get_or_init(Default::default);
+ let m = &PRE_INIT_SOURCE_TAGS;
let mut lock = m.lock().unwrap();
*lock = tags;
// When setting the source tags before initialization,
diff --git a/third_party/rust/glean-core/src/lib_unit_tests.rs b/third_party/rust/glean-core/src/lib_unit_tests.rs
index 0fc85b4602..cb1e4129d8 100644
--- a/third_party/rust/glean-core/src/lib_unit_tests.rs
+++ b/third_party/rust/glean-core/src/lib_unit_tests.rs
@@ -197,7 +197,7 @@ fn experimentation_id_is_set_correctly() {
trim_data_to_registered_pings: false,
log_level: None,
rate_limit: None,
- enable_event_timestamps: false,
+ enable_event_timestamps: true,
experimentation_id: Some(experimentation_id.to_string()),
})
.unwrap();
@@ -423,6 +423,7 @@ fn correct_order() {
Jwe("eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ.OKOawDo13gRp2ojaHV7LFpZcgV7T6DVZKTyKOMTYUmKoTCVJRgckCL9kiMT03JGeipsEdY3mx_etLbbWSrFr05kLzcSr4qKAq7YN7e9jwQRb23nfa6c9d-StnImGyFDbSv04uVuxIp5Zms1gNxKKK2Da14B8S4rzVRltdYwam_lDp5XnZAYpQdb76FdIKLaVmqgfwX7XWRxv2322i-vDxRfqNzo_tETKzpVLzfiwQyeyPGLBIO56YJ7eObdv0je81860ppamavo35UgoRdbYaBcoh9QcfylQr66oc6vFWXRcZ_ZT2LawVCWTIy3brGPi6UklfCpIMfIjf7iGdXKHzg.48V1_ALb6US04U3b.5eym8TW_c8SuK0ltJ3rpYIzOeDQz7TALvtu6UG9oMo4vpzs9tX_EFShS8iB7j6jiSdiwkIr3ajwQzaBtQD_A.XFBoMYUZodetZdvTiFvSkQ".into()),
Rate(0, 0),
Text(long_string),
+ Object("{}".into()),
];
for metric in all_metrics {
@@ -451,6 +452,7 @@ fn correct_order() {
Rate(..) => assert_eq!(14, disc),
Url(..) => assert_eq!(15, disc),
Text(..) => assert_eq!(16, disc),
+ Object(..) => assert_eq!(17, disc),
}
}
}
diff --git a/third_party/rust/glean-core/src/metrics/boolean.rs b/third_party/rust/glean-core/src/metrics/boolean.rs
index 71ed2372c2..ade4a22bfc 100644
--- a/third_party/rust/glean-core/src/metrics/boolean.rs
+++ b/third_party/rust/glean-core/src/metrics/boolean.rs
@@ -74,7 +74,6 @@ impl BooleanMetric {
///
/// # Arguments
///
- /// * `glean` - the Glean instance this metric belongs to.
/// * `value` - the value to set.
pub fn set(&self, value: bool) {
let metric = self.clone();
@@ -106,6 +105,15 @@ impl BooleanMetric {
/// Gets the currently stored value as an integer.
///
/// This doesn't clear the stored value.
+ ///
+ /// # Arguments
+ ///
+ /// * `ping_name` - the optional name of the ping to retrieve the metric
+ /// for. Defaults to the first value in `send_in_pings`.
+ ///
+ /// # Returns
+ ///
+ /// The stored value or `None` if nothing stored.
pub fn test_get_value(&self, ping_name: Option<String>) -> Option<bool> {
crate::block_on_dispatcher();
crate::core::with_glean(|glean| self.get_value(glean, ping_name.as_deref()))
@@ -118,8 +126,6 @@ impl BooleanMetric {
/// # Arguments
///
/// * `error` - The type of error
- /// * `ping_name` - represents the optional name of the ping to retrieve the
- /// metric for. inner to the first value in `send_in_pings`.
///
/// # Returns
///
diff --git a/third_party/rust/glean-core/src/metrics/counter.rs b/third_party/rust/glean-core/src/metrics/counter.rs
index 8f0a01cc3e..7e262c7d68 100644
--- a/third_party/rust/glean-core/src/metrics/counter.rs
+++ b/third_party/rust/glean-core/src/metrics/counter.rs
@@ -105,7 +105,6 @@ impl CounterMetric {
///
/// # Arguments
///
- /// * `glean` - The Glean instance this metric belongs to.
/// * `amount` - The amount to increase by. Should be positive.
///
/// ## Notes
@@ -143,6 +142,15 @@ impl CounterMetric {
/// Gets the currently stored value as an integer.
///
/// This doesn't clear the stored value.
+ ///
+ /// # Arguments
+ ///
+ /// * `ping_name` - the optional name of the ping to retrieve the metric
+ /// for. Defaults to the first value in `send_in_pings`.
+ ///
+ /// # Returns
+ ///
+ /// The stored value or `None` if nothing stored.
pub fn test_get_value(&self, ping_name: Option<String>) -> Option<i32> {
crate::block_on_dispatcher();
crate::core::with_glean(|glean| self.get_value(glean, ping_name.as_deref()))
@@ -155,8 +163,6 @@ impl CounterMetric {
/// # Arguments
///
/// * `error` - The type of error
- /// * `ping_name` - represents the optional name of the ping to retrieve the
- /// metric for. inner to the first value in `send_in_pings`.
///
/// # Returns
///
diff --git a/third_party/rust/glean-core/src/metrics/custom_distribution.rs b/third_party/rust/glean-core/src/metrics/custom_distribution.rs
index 929e4863ec..c7f3fbc56f 100644
--- a/third_party/rust/glean-core/src/metrics/custom_distribution.rs
+++ b/third_party/rust/glean-core/src/metrics/custom_distribution.rs
@@ -84,14 +84,33 @@ impl CustomDistributionMetric {
/// for each of them.
pub fn accumulate_samples(&self, samples: Vec<i64>) {
let metric = self.clone();
- crate::launch_with_glean(move |glean| metric.accumulate_samples_sync(glean, samples))
+ crate::launch_with_glean(move |glean| metric.accumulate_samples_sync(glean, &samples))
+ }
+
+ /// Accumulates precisely one signed sample and appends it to the metric.
+ ///
+ /// Signed is required so that the platform-specific code can provide us with a
+ /// 64 bit signed integer if no `u64` comparable type is available. This
+ /// will take care of filtering and reporting errors.
+ ///
+ /// # Arguments
+ ///
+ /// - `sample` - The singular sample to be recorded by the metric.
+ ///
+ /// ## Notes
+ ///
+ /// Discards any negative value of `sample` and reports an
+ /// [`ErrorType::InvalidValue`](crate::ErrorType::InvalidValue).
+ pub fn accumulate_single_sample(&self, sample: i64) {
+ let metric = self.clone();
+ crate::launch_with_glean(move |glean| metric.accumulate_samples_sync(glean, &[sample]))
}
/// Accumulates the provided sample in the metric synchronously.
///
/// See [`accumulate_samples`](Self::accumulate_samples) for details.
#[doc(hidden)]
- pub fn accumulate_samples_sync(&self, glean: &Glean, samples: Vec<i64>) {
+ pub fn accumulate_samples_sync(&self, glean: &Glean, samples: &[i64]) {
if !self.should_record(glean) {
return;
}
@@ -132,7 +151,7 @@ impl CustomDistributionMetric {
self.bucket_count as usize,
)
};
- accumulate(&samples, hist, Metric::CustomDistributionLinear)
+ accumulate(samples, hist, Metric::CustomDistributionLinear)
}
HistogramType::Exponential => {
let hist = if let Some(Metric::CustomDistributionExponential(hist)) = old_value
@@ -145,7 +164,7 @@ impl CustomDistributionMetric {
self.bucket_count as usize,
)
};
- accumulate(&samples, hist, Metric::CustomDistributionExponential)
+ accumulate(samples, hist, Metric::CustomDistributionExponential)
}
};
@@ -194,6 +213,15 @@ impl CustomDistributionMetric {
/// Gets the currently stored value as an integer.
///
/// This doesn't clear the stored value.
+ ///
+ /// # Arguments
+ ///
+ /// * `ping_name` - the optional name of the ping to retrieve the metric
+ /// for. Defaults to the first value in `send_in_pings`.
+ ///
+ /// # Returns
+ ///
+ /// The stored value or `None` if nothing stored.
pub fn test_get_value(&self, ping_name: Option<String>) -> Option<DistributionData> {
crate::block_on_dispatcher();
crate::core::with_glean(|glean| self.get_value(glean, ping_name.as_deref()))
@@ -206,8 +234,6 @@ impl CustomDistributionMetric {
/// # Arguments
///
/// * `error` - The type of error
- /// * `ping_name` - represents the optional name of the ping to retrieve the
- /// metric for. inner to the first value in `send_in_pings`.
///
/// # Returns
///
diff --git a/third_party/rust/glean-core/src/metrics/datetime.rs b/third_party/rust/glean-core/src/metrics/datetime.rs
index 3ef846a32c..e04f7fc051 100644
--- a/third_party/rust/glean-core/src/metrics/datetime.rs
+++ b/third_party/rust/glean-core/src/metrics/datetime.rs
@@ -262,8 +262,8 @@ impl DatetimeMetric {
///
/// # Arguments
///
- /// * `glean` - the Glean instance this metric belongs to.
- /// * `storage_name` - the storage name to look into.
+ /// * `ping_name` - the optional name of the ping to retrieve the metric
+ /// for. Defaults to the first value in `send_in_pings`.
///
/// # Returns
///
@@ -284,8 +284,8 @@ impl DatetimeMetric {
///
/// # Arguments
///
- /// * `glean` - the Glean instance this metric belongs to.
- /// * `storage_name` - the storage name to look into.
+ /// * `ping_name` - the optional name of the ping to retrieve the metric
+ /// for. Defaults to the first value in `send_in_pings`.
///
/// # Returns
///
@@ -311,8 +311,6 @@ impl DatetimeMetric {
/// # Arguments
///
/// * `error` - The type of error
- /// * `ping_name` - represents the optional name of the ping to retrieve the
- /// metric for. inner to the first value in `send_in_pings`.
///
/// # Returns
///
diff --git a/third_party/rust/glean-core/src/metrics/denominator.rs b/third_party/rust/glean-core/src/metrics/denominator.rs
index fb80874924..3083d6e78a 100644
--- a/third_party/rust/glean-core/src/metrics/denominator.rs
+++ b/third_party/rust/glean-core/src/metrics/denominator.rs
@@ -91,6 +91,15 @@ impl DenominatorMetric {
/// Gets the currently stored value as an integer.
///
/// This doesn't clear the stored value.
+ ///
+ /// # Arguments
+ ///
+ /// * `ping_name` - the optional name of the ping to retrieve the metric
+ /// for. Defaults to the first value in `send_in_pings`.
+ ///
+ /// # Returns
+ ///
+ /// The stored value or `None` if nothing stored.
pub fn test_get_value(&self, ping_name: Option<String>) -> Option<i32> {
crate::block_on_dispatcher();
crate::core::with_glean(|glean| self.get_value(glean, ping_name.as_deref()))
@@ -124,8 +133,6 @@ impl DenominatorMetric {
/// # Arguments
///
/// * `error` - The type of error
- /// * `ping_name` - the optional name of the ping to retrieve the metric
- /// for. inner to the first value in `send_in_pings`.
///
/// # Returns
///
diff --git a/third_party/rust/glean-core/src/metrics/event.rs b/third_party/rust/glean-core/src/metrics/event.rs
index 5ad6e6d50c..c7aefd9cd6 100644
--- a/third_party/rust/glean-core/src/metrics/event.rs
+++ b/third_party/rust/glean-core/src/metrics/event.rs
@@ -185,6 +185,11 @@ impl EventMetric {
/// Get the vector of currently stored events for this event metric.
///
/// This doesn't clear the stored value.
+ ///
+ /// # Arguments
+ ///
+ /// * `ping_name` - the optional name of the ping to retrieve the metric
+ /// for. Defaults to the first value in `send_in_pings`.
pub fn test_get_value(&self, ping_name: Option<String>) -> Option<Vec<RecordedEvent>> {
crate::block_on_dispatcher();
crate::core::with_glean(|glean| self.get_value(glean, ping_name.as_deref()))
@@ -197,8 +202,6 @@ impl EventMetric {
/// # Arguments
///
/// * `error` - The type of error
- /// * `ping_name` - represents the optional name of the ping to retrieve the
- /// metric for. inner to the first value in `send_in_pings`.
///
/// # Returns
///
diff --git a/third_party/rust/glean-core/src/metrics/experiment.rs b/third_party/rust/glean-core/src/metrics/experiment.rs
index 23e6c41ce2..5695bf942e 100644
--- a/third_party/rust/glean-core/src/metrics/experiment.rs
+++ b/third_party/rust/glean-core/src/metrics/experiment.rs
@@ -195,6 +195,15 @@ impl ExperimentMetric {
/// the RecordedExperiment.
///
/// This doesn't clear the stored value.
+ ///
+ /// # Arguments
+ ///
+ /// * `ping_name` - the optional name of the ping to retrieve the metric
+ /// for. Defaults to the first value in `send_in_pings`.
+ ///
+ /// # Returns
+ ///
+ /// The stored value or `None` if nothing stored.
pub fn test_get_value(&self, glean: &Glean) -> Option<RecordedExperiment> {
match StorageManager.snapshot_metric_for_test(
glean.storage(),
diff --git a/third_party/rust/glean-core/src/metrics/labeled.rs b/third_party/rust/glean-core/src/metrics/labeled.rs
index fa3e6a6a75..f9f6a28880 100644
--- a/third_party/rust/glean-core/src/metrics/labeled.rs
+++ b/third_party/rust/glean-core/src/metrics/labeled.rs
@@ -205,8 +205,6 @@ where
/// # Arguments
///
/// * `error` - The type of error
- /// * `ping_name` - represents the optional name of the ping to retrieve the
- /// metric for. Defaults to the first value in `send_in_pings`.
///
/// # Returns
///
diff --git a/third_party/rust/glean-core/src/metrics/memory_distribution.rs b/third_party/rust/glean-core/src/metrics/memory_distribution.rs
index ac9eda1a90..7b5e5ee192 100644
--- a/third_party/rust/glean-core/src/metrics/memory_distribution.rs
+++ b/third_party/rust/glean-core/src/metrics/memory_distribution.rs
@@ -254,6 +254,15 @@ impl MemoryDistributionMetric {
/// Gets the currently stored value.
///
/// This doesn't clear the stored value.
+ ///
+ /// # Arguments
+ ///
+ /// * `ping_name` - the optional name of the ping to retrieve the metric
+ /// for. Defaults to the first value in `send_in_pings`.
+ ///
+ /// # Returns
+ ///
+ /// The stored value or `None` if nothing stored.
pub fn test_get_value(&self, ping_name: Option<String>) -> Option<DistributionData> {
crate::block_on_dispatcher();
crate::core::with_glean(|glean| self.get_value(glean, ping_name.as_deref()))
@@ -266,8 +275,6 @@ impl MemoryDistributionMetric {
/// # Arguments
///
/// * `error` - The type of error
- /// * `ping_name` - represents the optional name of the ping to retrieve the
- /// metric for. Defaults to the first value in `send_in_pings`.
///
/// # Returns
///
diff --git a/third_party/rust/glean-core/src/metrics/mod.rs b/third_party/rust/glean-core/src/metrics/mod.rs
index 43253b9aa7..92001efd2a 100644
--- a/third_party/rust/glean-core/src/metrics/mod.rs
+++ b/third_party/rust/glean-core/src/metrics/mod.rs
@@ -9,7 +9,8 @@ use std::sync::atomic::Ordering;
use chrono::{DateTime, FixedOffset};
use serde::{Deserialize, Serialize};
-use serde_json::{json, Value as JsonValue};
+use serde_json::json;
+pub use serde_json::Value as JsonValue;
mod boolean;
mod counter;
@@ -23,6 +24,7 @@ mod memory_distribution;
mod memory_unit;
mod metrics_enabled_config;
mod numerator;
+mod object;
mod ping;
mod quantity;
mod rate;
@@ -54,6 +56,7 @@ pub use self::labeled::{LabeledBoolean, LabeledCounter, LabeledMetric, LabeledSt
pub use self::memory_distribution::MemoryDistributionMetric;
pub use self::memory_unit::MemoryUnit;
pub use self::numerator::NumeratorMetric;
+pub use self::object::ObjectMetric;
pub use self::ping::PingType;
pub use self::quantity::QuantityMetric;
pub use self::rate::{Rate, RateMetric};
@@ -141,6 +144,8 @@ pub enum Metric {
Url(String),
/// A Text metric. See [`TextMetric`] for more information.
Text(String),
+ /// An Object metric. See [`ObjectMetric`] for more information.
+ Object(String),
}
/// A [`MetricType`] describes common behavior across all metrics.
@@ -251,6 +256,7 @@ impl Metric {
Metric::MemoryDistribution(_) => "memory_distribution",
Metric::Jwe(_) => "jwe",
Metric::Text(_) => "text",
+ Metric::Object(_) => "object",
}
}
@@ -280,6 +286,9 @@ impl Metric {
Metric::MemoryDistribution(hist) => json!(memory_distribution::snapshot(hist)),
Metric::Jwe(s) => json!(s),
Metric::Text(s) => json!(s),
+ Metric::Object(s) => {
+ serde_json::from_str(s).expect("object storage should have been json")
+ }
}
}
}
diff --git a/third_party/rust/glean-core/src/metrics/numerator.rs b/third_party/rust/glean-core/src/metrics/numerator.rs
index 3c340cab1d..de29338a5c 100644
--- a/third_party/rust/glean-core/src/metrics/numerator.rs
+++ b/third_party/rust/glean-core/src/metrics/numerator.rs
@@ -55,12 +55,16 @@ impl NumeratorMetric {
///
/// Gets the currently stored value as a pair of integers.
///
+ /// This doesn't clear the stored value.
+ ///
/// # Arguments
///
/// * `ping_name` - the optional name of the ping to retrieve the metric
/// for. Defaults to the first value in `send_in_pings`.
///
- /// This doesn't clear the stored value.
+ /// # Returns
+ ///
+ /// The stored value or `None` if nothing stored.
pub fn test_get_value(&self, ping_name: Option<String>) -> Option<Rate> {
crate::block_on_dispatcher();
crate::core::with_glean(|glean| self.get_value(glean, ping_name.as_deref()))
@@ -82,8 +86,6 @@ impl NumeratorMetric {
/// # Arguments
///
/// * `error` - The type of error
- /// * `ping_name` - the optional name of the ping to retrieve the metric
- /// for. Defaults to the first value in `send_in_pings`.
///
/// # Returns
///
diff --git a/third_party/rust/glean-core/src/metrics/object.rs b/third_party/rust/glean-core/src/metrics/object.rs
new file mode 100644
index 0000000000..6071e2b33a
--- /dev/null
+++ b/third_party/rust/glean-core/src/metrics/object.rs
@@ -0,0 +1,135 @@
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at https://mozilla.org/MPL/2.0/.
+
+use std::sync::Arc;
+
+use crate::common_metric_data::CommonMetricDataInternal;
+use crate::error_recording::{record_error, test_get_num_recorded_errors, ErrorType};
+use crate::metrics::JsonValue;
+use crate::metrics::Metric;
+use crate::metrics::MetricType;
+use crate::storage::StorageManager;
+use crate::CommonMetricData;
+use crate::Glean;
+
+/// An object metric.
+///
+/// Record structured data.
+/// The value must adhere to a predefined structure and is serialized into JSON.
+#[derive(Clone, Debug)]
+pub struct ObjectMetric {
+ meta: Arc<CommonMetricDataInternal>,
+}
+
+impl MetricType for ObjectMetric {
+ fn meta(&self) -> &CommonMetricDataInternal {
+ &self.meta
+ }
+}
+
+// IMPORTANT:
+//
+// When changing this implementation, make sure all the operations are
+// also declared in the related trait in `../traits/`.
+impl ObjectMetric {
+ /// Creates a new object metric.
+ pub fn new(meta: CommonMetricData) -> Self {
+ Self {
+ meta: Arc::new(meta.into()),
+ }
+ }
+
+ /// Sets to the specified structure.
+ ///
+ /// # Arguments
+ ///
+ /// * `glean` - the Glean instance this metric belongs to.
+ /// * `value` - the value to set.
+ #[doc(hidden)]
+ pub fn set_sync(&self, glean: &Glean, value: JsonValue) {
+ let value = Metric::Object(serde_json::to_string(&value).unwrap());
+ glean.storage().record(glean, &self.meta, &value)
+ }
+
+ /// Sets to the specified structure.
+ ///
+ /// No additional verification is done.
+ /// The shape needs to be externally verified.
+ ///
+ /// # Arguments
+ ///
+ /// * `value` - the value to set.
+ pub fn set(&self, value: JsonValue) {
+ let metric = self.clone();
+ crate::launch_with_glean(move |glean| metric.set_sync(glean, value))
+ }
+
+ /// Record an `InvalidValue` error for this metric.
+ ///
+ /// Only to be used by the RLB.
+ // TODO(bug 1691073): This can probably go once we have a more generic mechanism to record
+ // errors
+ pub fn record_schema_error(&self) {
+ let metric = self.clone();
+ crate::launch_with_glean(move |glean| {
+ let msg = "Value did not match predefined schema";
+ record_error(glean, &metric.meta, ErrorType::InvalidValue, msg, None);
+ });
+ }
+
+ /// Get current value
+ #[doc(hidden)]
+ pub fn get_value<'a, S: Into<Option<&'a str>>>(
+ &self,
+ glean: &Glean,
+ ping_name: S,
+ ) -> Option<String> {
+ let queried_ping_name = ping_name
+ .into()
+ .unwrap_or_else(|| &self.meta().inner.send_in_pings[0]);
+
+ match StorageManager.snapshot_metric_for_test(
+ glean.storage(),
+ queried_ping_name,
+ &self.meta.identifier(glean),
+ self.meta.inner.lifetime,
+ ) {
+ Some(Metric::Object(o)) => Some(o),
+ _ => None,
+ }
+ }
+
+ /// **Test-only API (exported for FFI purposes).**
+ ///
+ /// Gets the currently stored value as JSON.
+ ///
+ /// This doesn't clear the stored value.
+ pub fn test_get_value(&self, ping_name: Option<String>) -> Option<JsonValue> {
+ crate::block_on_dispatcher();
+ let value = crate::core::with_glean(|glean| self.get_value(glean, ping_name.as_deref()));
+ // We only store valid JSON
+ value.map(|val| serde_json::from_str(&val).unwrap())
+ }
+
+ /// **Exported for test purposes.**
+ ///
+ /// Gets the number of recorded errors for the given metric and error type.
+ ///
+ /// # Arguments
+ ///
+ /// * `error` - The type of error
+ /// * `ping_name` - represents the optional name of the ping to retrieve the
+ /// metric for. inner to the first value in `send_in_pings`.
+ ///
+ /// # Returns
+ ///
+ /// The number of errors reported.
+ pub fn test_get_num_recorded_errors(&self, error: ErrorType) -> i32 {
+ crate::block_on_dispatcher();
+
+ crate::core::with_glean(|glean| {
+ test_get_num_recorded_errors(glean, self.meta(), error).unwrap_or(0)
+ })
+ }
+}
diff --git a/third_party/rust/glean-core/src/metrics/ping.rs b/third_party/rust/glean-core/src/metrics/ping.rs
index dc37d76a45..e60284b1e2 100644
--- a/third_party/rust/glean-core/src/metrics/ping.rs
+++ b/third_party/rust/glean-core/src/metrics/ping.rs
@@ -6,6 +6,7 @@ use std::fmt;
use std::sync::Arc;
use crate::ping::PingMaker;
+use crate::upload::PingPayload;
use crate::Glean;
use uuid::Uuid;
@@ -26,6 +27,8 @@ struct InnerPing {
pub send_if_empty: bool,
/// Whether to use millisecond-precise start/end times.
pub precise_timestamps: bool,
+ /// Whether to include the {client|ping}_info sections on assembly.
+ pub include_info_sections: bool,
/// The "reason" codes that this ping can send
pub reason_codes: Vec<String>,
}
@@ -37,6 +40,7 @@ impl fmt::Debug for PingType {
.field("include_client_id", &self.0.include_client_id)
.field("send_if_empty", &self.0.send_if_empty)
.field("precise_timestamps", &self.0.precise_timestamps)
+ .field("include_info_sections", &self.0.include_info_sections)
.field("reason_codes", &self.0.reason_codes)
.finish()
}
@@ -61,6 +65,7 @@ impl PingType {
include_client_id: bool,
send_if_empty: bool,
precise_timestamps: bool,
+ include_info_sections: bool,
reason_codes: Vec<String>,
) -> Self {
let this = Self(Arc::new(InnerPing {
@@ -68,6 +73,7 @@ impl PingType {
include_client_id,
send_if_empty,
precise_timestamps,
+ include_info_sections,
reason_codes,
}));
@@ -94,6 +100,10 @@ impl PingType {
self.0.precise_timestamps
}
+ pub(crate) fn include_info_sections(&self) -> bool {
+ self.0.include_info_sections
+ }
+
/// Submits the ping for eventual uploading.
///
/// The ping content is assembled as soon as possible, but upload is not
@@ -186,13 +196,17 @@ impl PingType {
// so both scenarios should be impossible.
let content =
::serde_json::to_string(&ping.content).expect("ping serialization failed");
- glean.upload_manager.enqueue_ping(
- glean,
- ping.doc_id,
- ping.url_path,
- &content,
- Some(ping.headers),
- );
+ // TODO: Shouldn't we consolidate on a single collected Ping representation?
+ let ping = PingPayload {
+ document_id: ping.doc_id.to_string(),
+ upload_path: ping.url_path.to_string(),
+ json_body: content,
+ headers: Some(ping.headers),
+ body_has_info_sections: self.0.include_info_sections,
+ ping_name: self.0.name.to_string(),
+ };
+
+ glean.upload_manager.enqueue_ping(glean, ping);
return true;
}
diff --git a/third_party/rust/glean-core/src/metrics/quantity.rs b/third_party/rust/glean-core/src/metrics/quantity.rs
index c59d3a4a21..92216625d6 100644
--- a/third_party/rust/glean-core/src/metrics/quantity.rs
+++ b/third_party/rust/glean-core/src/metrics/quantity.rs
@@ -98,6 +98,15 @@ impl QuantityMetric {
/// Gets the currently stored value as an integer.
///
/// This doesn't clear the stored value.
+ ///
+ /// # Arguments
+ ///
+ /// * `ping_name` - the optional name of the ping to retrieve the metric
+ /// for. Defaults to the first value in `send_in_pings`.
+ ///
+ /// # Returns
+ ///
+ /// The stored value or `None` if nothing stored.
pub fn test_get_value(&self, ping_name: Option<String>) -> Option<i64> {
crate::block_on_dispatcher();
crate::core::with_glean(|glean| self.get_value(glean, ping_name.as_deref()))
@@ -110,8 +119,6 @@ impl QuantityMetric {
/// # Arguments
///
/// * `error` - The type of error
- /// * `ping_name` - represents the optional name of the ping to retrieve the
- /// metric for. Defaults to the first value in `send_in_pings`.
///
/// # Returns
///
diff --git a/third_party/rust/glean-core/src/metrics/rate.rs b/third_party/rust/glean-core/src/metrics/rate.rs
index ba7f085b55..843d35002e 100644
--- a/third_party/rust/glean-core/src/metrics/rate.rs
+++ b/third_party/rust/glean-core/src/metrics/rate.rs
@@ -141,6 +141,15 @@ impl RateMetric {
/// Gets the currently stored value as a pair of integers.
///
/// This doesn't clear the stored value.
+ ///
+ /// # Arguments
+ ///
+ /// * `ping_name` - the optional name of the ping to retrieve the metric
+ /// for. Defaults to the first value in `send_in_pings`.
+ ///
+ /// # Returns
+ ///
+ /// The stored value or `None` if nothing stored.
pub fn test_get_value(&self, ping_name: Option<String>) -> Option<Rate> {
crate::block_on_dispatcher();
crate::core::with_glean(|glean| self.get_value(glean, ping_name.as_deref()))
@@ -175,8 +184,6 @@ impl RateMetric {
/// # Arguments
///
/// * `error` - The type of error
- /// * `ping_name` - represents the optional name of the ping to retrieve the
- /// metric for. Defaults to the first value in `send_in_pings`.
///
/// # Returns
///
diff --git a/third_party/rust/glean-core/src/metrics/string.rs b/third_party/rust/glean-core/src/metrics/string.rs
index 5ed7b2c7f1..4aa30a8d7e 100644
--- a/third_party/rust/glean-core/src/metrics/string.rs
+++ b/third_party/rust/glean-core/src/metrics/string.rs
@@ -112,6 +112,15 @@ impl StringMetric {
/// Gets the currently stored value as a string.
///
/// This doesn't clear the stored value.
+ ///
+ /// # Arguments
+ ///
+ /// * `ping_name` - the optional name of the ping to retrieve the metric
+ /// for. Defaults to the first value in `send_in_pings`.
+ ///
+ /// # Returns
+ ///
+ /// The stored value or `None` if nothing stored.
pub fn test_get_value(&self, ping_name: Option<String>) -> Option<String> {
crate::block_on_dispatcher();
crate::core::with_glean(|glean| self.get_value(glean, ping_name.as_deref()))
@@ -124,8 +133,6 @@ impl StringMetric {
/// # Arguments
///
/// * `error` - The type of error
- /// * `ping_name` - represents the optional name of the ping to retrieve the
- /// metric for. Defaults to the first value in `send_in_pings`.
///
/// # Returns
///
diff --git a/third_party/rust/glean-core/src/metrics/string_list.rs b/third_party/rust/glean-core/src/metrics/string_list.rs
index 75b2df7f80..cd4e71b885 100644
--- a/third_party/rust/glean-core/src/metrics/string_list.rs
+++ b/third_party/rust/glean-core/src/metrics/string_list.rs
@@ -171,6 +171,15 @@ impl StringListMetric {
/// Gets the currently-stored values.
///
/// This doesn't clear the stored value.
+ ///
+ /// # Arguments
+ ///
+ /// * `ping_name` - the optional name of the ping to retrieve the metric
+ /// for. Defaults to the first value in `send_in_pings`.
+ ///
+ /// # Returns
+ ///
+ /// The stored value or `None` if nothing stored.
pub fn test_get_value(&self, ping_name: Option<String>) -> Option<Vec<String>> {
crate::block_on_dispatcher();
crate::core::with_glean(|glean| self.get_value(glean, ping_name.as_deref()))
@@ -183,8 +192,6 @@ impl StringListMetric {
/// # Arguments
///
/// * `error` - The type of error
- /// * `ping_name` - represents the optional name of the ping to retrieve the
- /// metric for. Defaults to the first value in `send_in_pings`.
///
/// # Returns
///
diff --git a/third_party/rust/glean-core/src/metrics/text.rs b/third_party/rust/glean-core/src/metrics/text.rs
index 06ad5c0d78..baa8e88d75 100644
--- a/third_party/rust/glean-core/src/metrics/text.rs
+++ b/third_party/rust/glean-core/src/metrics/text.rs
@@ -116,6 +116,15 @@ impl TextMetric {
/// Gets the currently stored value as a string.
///
/// This doesn't clear the stored value.
+ ///
+ /// # Arguments
+ ///
+ /// * `ping_name` - the optional name of the ping to retrieve the metric
+ /// for. Defaults to the first value in `send_in_pings`.
+ ///
+ /// # Returns
+ ///
+ /// The stored value or `None` if nothing stored.
pub fn test_get_value(&self, ping_name: Option<String>) -> Option<String> {
crate::block_on_dispatcher();
crate::core::with_glean(|glean| self.get_value(glean, ping_name.as_deref()))
@@ -128,8 +137,6 @@ impl TextMetric {
/// # Arguments
///
/// * `error` - The type of error
- /// * `ping_name` - represents the optional name of the ping to retrieve the
- /// metric for. Defaults to the first value in `send_in_pings`.
///
/// # Returns
///
diff --git a/third_party/rust/glean-core/src/metrics/timespan.rs b/third_party/rust/glean-core/src/metrics/timespan.rs
index b4d3bd5902..ee63fb52f8 100644
--- a/third_party/rust/glean-core/src/metrics/timespan.rs
+++ b/third_party/rust/glean-core/src/metrics/timespan.rs
@@ -253,6 +253,15 @@ impl TimespanMetric {
/// Gets the currently stored value as an integer.
///
/// This doesn't clear the stored value.
+ ///
+ /// # Arguments
+ ///
+ /// * `ping_name` - the optional name of the ping to retrieve the metric
+ /// for. Defaults to the first value in `send_in_pings`.
+ ///
+ /// # Returns
+ ///
+ /// The stored value or `None` if nothing stored.
pub fn test_get_value(&self, ping_name: Option<String>) -> Option<i64> {
crate::block_on_dispatcher();
crate::core::with_glean(|glean| {
@@ -292,8 +301,6 @@ impl TimespanMetric {
/// # Arguments
///
/// * `error` - The type of error
- /// * `ping_name` - represents the optional name of the ping to retrieve the
- /// metric for. Defaults to the first value in `send_in_pings`.
///
/// # Returns
///
diff --git a/third_party/rust/glean-core/src/metrics/timing_distribution.rs b/third_party/rust/glean-core/src/metrics/timing_distribution.rs
index e339ef8882..3293be9518 100644
--- a/third_party/rust/glean-core/src/metrics/timing_distribution.rs
+++ b/third_party/rust/glean-core/src/metrics/timing_distribution.rs
@@ -293,7 +293,35 @@ impl TimingDistributionMetric {
/// are longer than `MAX_SAMPLE_TIME`.
pub fn accumulate_samples(&self, samples: Vec<i64>) {
let metric = self.clone();
- crate::launch_with_glean(move |glean| metric.accumulate_samples_sync(glean, samples))
+ crate::launch_with_glean(move |glean| metric.accumulate_samples_sync(glean, &samples))
+ }
+
+ /// Accumulates precisely one signed sample and appends it to the metric.
+ ///
+ /// Precludes the need for a collection in the most common use case.
+ ///
+ /// Sign is required so that the platform-specific code can provide us with
+ /// a 64 bit signed integer if no `u64` comparable type is available. This
+ /// will take care of filtering and reporting errors for any provided negative
+ /// sample.
+ ///
+ /// Please note that this assumes that the provided sample is already in
+ /// the "unit" declared by the instance of the metric type (e.g. if the
+ /// instance this method was called on is using [`crate::TimeUnit::Second`], then
+ /// `sample` is assumed to be in that unit).
+ ///
+ /// # Arguments
+ ///
+ /// * `sample` - The singular sample to be recorded by the metric.
+ ///
+ /// ## Notes
+ ///
+ /// Discards any negative value and reports an [`ErrorType::InvalidValue`].
+ /// Reports an [`ErrorType::InvalidOverflow`] error if the sample is longer than
+ /// `MAX_SAMPLE_TIME`.
+ pub fn accumulate_single_sample(&self, sample: i64) {
+ let metric = self.clone();
+ crate::launch_with_glean(move |glean| metric.accumulate_samples_sync(glean, &[sample]))
}
/// **Test-only API (exported for testing purposes).**
@@ -301,7 +329,7 @@ impl TimingDistributionMetric {
///
/// Use [`accumulate_samples`](Self::accumulate_samples)
#[doc(hidden)]
- pub fn accumulate_samples_sync(&self, glean: &Glean, samples: Vec<i64>) {
+ pub fn accumulate_samples_sync(&self, glean: &Glean, samples: &[i64]) {
if !self.should_record(glean) {
return;
}
@@ -464,6 +492,15 @@ impl TimingDistributionMetric {
/// Gets the currently stored value as an integer.
///
/// This doesn't clear the stored value.
+ ///
+ /// # Arguments
+ ///
+ /// * `ping_name` - the optional name of the ping to retrieve the metric
+ /// for. Defaults to the first value in `send_in_pings`.
+ ///
+ /// # Returns
+ ///
+ /// The stored value or `None` if nothing stored.
pub fn test_get_value(&self, ping_name: Option<String>) -> Option<DistributionData> {
crate::block_on_dispatcher();
crate::core::with_glean(|glean| self.get_value(glean, ping_name.as_deref()))
@@ -476,8 +513,6 @@ impl TimingDistributionMetric {
/// # Arguments
///
/// * `error` - The type of error
- /// * `ping_name` - represents the optional name of the ping to retrieve the
- /// metric for. Defaults to the first value in `send_in_pings`.
///
/// # Returns
///
diff --git a/third_party/rust/glean-core/src/metrics/url.rs b/third_party/rust/glean-core/src/metrics/url.rs
index c9eb824a3e..48b3f9e7ae 100644
--- a/third_party/rust/glean-core/src/metrics/url.rs
+++ b/third_party/rust/glean-core/src/metrics/url.rs
@@ -131,6 +131,15 @@ impl UrlMetric {
/// Gets the currently stored value as a string.
///
/// This doesn't clear the stored value.
+ ///
+ /// # Arguments
+ ///
+ /// * `ping_name` - the optional name of the ping to retrieve the metric
+ /// for. Defaults to the first value in `send_in_pings`.
+ ///
+ /// # Returns
+ ///
+ /// The stored value or `None` if nothing stored.
pub fn test_get_value(&self, ping_name: Option<String>) -> Option<String> {
crate::block_on_dispatcher();
crate::core::with_glean(|glean| self.get_value(glean, ping_name.as_deref()))
@@ -143,8 +152,6 @@ impl UrlMetric {
/// # Arguments
///
/// * `error` - The type of error
- /// * `ping_name` - represents the optional name of the ping to retrieve the
- /// metric for. Defaults to the first value in `send_in_pings`.
///
/// # Returns
///
diff --git a/third_party/rust/glean-core/src/metrics/uuid.rs b/third_party/rust/glean-core/src/metrics/uuid.rs
index e78d15ad3b..77d0f82320 100644
--- a/third_party/rust/glean-core/src/metrics/uuid.rs
+++ b/third_party/rust/glean-core/src/metrics/uuid.rs
@@ -128,6 +128,15 @@ impl UuidMetric {
/// Gets the currently stored value as a string.
///
/// This doesn't clear the stored value.
+ ///
+ /// # Arguments
+ ///
+ /// * `ping_name` - the optional name of the ping to retrieve the metric
+ /// for. Defaults to the first value in `send_in_pings`.
+ ///
+ /// # Returns
+ ///
+ /// The stored value or `None` if nothing stored.
pub fn test_get_value(&self, ping_name: Option<String>) -> Option<String> {
crate::block_on_dispatcher();
crate::core::with_glean(|glean| {
@@ -143,8 +152,6 @@ impl UuidMetric {
/// # Arguments
///
/// * `error` - The type of error
- /// * `ping_name` - represents the optional name of the ping to retrieve the
- /// metric for. Defaults to the first value in `send_in_pings`.
///
/// # Returns
///
diff --git a/third_party/rust/glean-core/src/ping/mod.rs b/third_party/rust/glean-core/src/ping/mod.rs
index c22a890aa2..d1a67ae360 100644
--- a/third_party/rust/glean-core/src/ping/mod.rs
+++ b/third_party/rust/glean-core/src/ping/mod.rs
@@ -14,7 +14,7 @@ use serde_json::{json, Value as JsonValue};
use crate::common_metric_data::{CommonMetricData, Lifetime};
use crate::metrics::{CounterMetric, DatetimeMetric, Metric, MetricType, PingType, TimeUnit};
use crate::storage::{StorageManager, INTERNAL_STORAGE};
-use crate::upload::HeaderMap;
+use crate::upload::{HeaderMap, PingMetadata};
use crate::util::{get_iso_time_string, local_now_with_offset};
use crate::{Glean, Result, DELETION_REQUEST_PINGS_DIRECTORY, PENDING_PINGS_DIRECTORY};
@@ -30,6 +30,8 @@ pub struct Ping<'a> {
pub content: JsonValue,
/// The headers to upload with the payload.
pub headers: HeaderMap,
+ /// Whether the content contains {client|ping}_info sections.
+ pub includes_info_sections: bool,
}
/// Collect a ping's data, assemble it into its full payload and store it on disk.
@@ -237,9 +239,9 @@ impl PingMaker {
.snapshot_as_json(glean, ping.name(), true);
// Due to the way the experimentation identifier could link datasets that are intentionally unlinked,
- // it will not be included in pings that specifically exclude the Glean client-id and those pings that
- // should not be sent if empty.
- if (!ping.include_client_id() || !ping.send_if_empty())
+ // it will not be included in pings that specifically exclude the Glean client-id, those pings that
+ // should not be sent if empty, or pings that exclude the {client|ping}_info sections wholesale.
+ if (!ping.include_client_id() || !ping.send_if_empty() || !ping.include_info_sections())
&& glean.test_get_experimentation_id().is_some()
&& metrics_data.is_some()
{
@@ -285,13 +287,18 @@ impl PingMaker {
TimeUnit::Minute
};
- let ping_info = self.get_ping_info(glean, ping.name(), reason, precision);
- let client_info = self.get_client_info(glean, ping.include_client_id());
+ let mut json = if ping.include_info_sections() {
+ let ping_info = self.get_ping_info(glean, ping.name(), reason, precision);
+ let client_info = self.get_client_info(glean, ping.include_client_id());
+
+ json!({
+ "ping_info": ping_info,
+ "client_info": client_info
+ })
+ } else {
+ json!({})
+ };
- let mut json = json!({
- "ping_info": ping_info,
- "client_info": client_info
- });
let json_obj = json.as_object_mut()?;
if let Some(metrics_data) = metrics_data {
json_obj.insert("metrics".to_string(), metrics_data);
@@ -306,6 +313,7 @@ impl PingMaker {
doc_id,
url_path,
headers: self.get_headers(glean),
+ includes_info_sections: ping.include_info_sections(),
})
}
@@ -355,11 +363,17 @@ impl PingMaker {
file.write_all(ping.url_path.as_bytes())?;
file.write_all(b"\n")?;
file.write_all(::serde_json::to_string(&ping.content)?.as_bytes())?;
- if !ping.headers.is_empty() {
- file.write_all(b"\n{\"headers\":")?;
- file.write_all(::serde_json::to_string(&ping.headers)?.as_bytes())?;
- file.write_all(b"}")?;
- }
+ file.write_all(b"\n")?;
+ let metadata = PingMetadata {
+ // We don't actually need to clone the headers except to match PingMetadata's ownership.
+ // But since we're going to write a file to disk in a sec,
+ // and HeaderMaps tend to have only like two things in them, tops,
+ // the cost is bearable.
+ headers: Some(ping.headers.clone()),
+ body_has_info_sections: Some(ping.includes_info_sections),
+ ping_name: Some(ping.name.to_string()),
+ };
+ file.write_all(::serde_json::to_string(&metadata)?.as_bytes())?;
}
if let Err(e) = std::fs::rename(&temp_ping_path, &ping_path) {
diff --git a/third_party/rust/glean-core/src/traits/custom_distribution.rs b/third_party/rust/glean-core/src/traits/custom_distribution.rs
index c0c80c028b..43dfdb7da8 100644
--- a/third_party/rust/glean-core/src/traits/custom_distribution.rs
+++ b/third_party/rust/glean-core/src/traits/custom_distribution.rs
@@ -28,6 +28,22 @@ pub trait CustomDistribution {
/// them.
fn accumulate_samples_signed(&self, samples: Vec<i64>);
+ /// Accumulates precisely one signed sample in the metric.
+ ///
+ /// This is required so that the platform-specific code can provide us with a
+ /// 64 bit signed integer if no `u64` comparable type is available. This
+ /// will take care of filtering and reporting errors.
+ ///
+ /// # Arguments
+ ///
+ /// - `sample` - The singular sample to be recorded by the metric.
+ ///
+ /// ## Notes
+ ///
+ /// Discards any negative value of `sample` and reports an
+ /// [`ErrorType::InvalidValue`](crate::ErrorType::InvalidValue).
+ fn accumulate_single_sample_signed(&self, sample: i64);
+
/// **Exported for test purposes.**
///
/// Gets the currently stored histogram.
diff --git a/third_party/rust/glean-core/src/traits/mod.rs b/third_party/rust/glean-core/src/traits/mod.rs
index c4bcf7cdd6..4115609fdd 100644
--- a/third_party/rust/glean-core/src/traits/mod.rs
+++ b/third_party/rust/glean-core/src/traits/mod.rs
@@ -7,6 +7,10 @@
//! Individual metric types implement this trait to expose the specific metrics API.
//! It can be used by wrapping implementations to guarantee API conformance.
+/// Re-export for use in generated code.
+#[doc(hidden)]
+pub extern crate serde as __serde;
+
mod boolean;
mod counter;
mod custom_distribution;
@@ -15,6 +19,7 @@ mod event;
mod labeled;
mod memory_distribution;
mod numerator;
+mod object;
mod ping;
mod quantity;
mod rate;
@@ -37,6 +42,7 @@ pub use self::event::NoExtraKeys;
pub use self::labeled::Labeled;
pub use self::memory_distribution::MemoryDistribution;
pub use self::numerator::Numerator;
+pub use self::object::{ObjectError, ObjectSerialize};
pub use self::ping::Ping;
pub use self::quantity::Quantity;
pub use self::rate::Rate;
diff --git a/third_party/rust/glean-core/src/traits/object.rs b/third_party/rust/glean-core/src/traits/object.rs
new file mode 100644
index 0000000000..c579efeac7
--- /dev/null
+++ b/third_party/rust/glean-core/src/traits/object.rs
@@ -0,0 +1,53 @@
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at https://mozilla.org/MPL/2.0/.
+
+use std::fmt::Display;
+
+use serde::{Deserialize, Serialize};
+use serde_json::Value as JsonValue;
+
+/// This type represents all possible errors that can occur when serializing or deserializing an object from/to JSON.
+#[derive(Debug)]
+pub struct ObjectError(serde_json::Error);
+
+impl Display for ObjectError {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ Display::fmt(&self.0, f)
+ }
+}
+
+impl std::error::Error for ObjectError {}
+
+/// An object that can be serialized into JSON.
+///
+/// Objects are defined by their structure in the metrics definition.
+///
+/// This is essentially a wrapper around serde's `Serialize`/`Deserialize`,
+/// but in a way we can name it for our JSON (de)serialization.
+pub trait ObjectSerialize {
+ /// Deserialize the object from its JSON representation.
+ ///
+ /// Returns an error if deserialization fails.
+ /// This should not happen for glean_parser-generated and later serialized objects.
+ fn from_str(obj: &str) -> Result<Self, ObjectError>
+ where
+ Self: Sized;
+
+ /// Serialize this object into a JSON string.
+ fn into_serialized_object(self) -> Result<JsonValue, ObjectError>;
+}
+
+impl<V> ObjectSerialize for V
+where
+ V: Serialize,
+ V: for<'de> Deserialize<'de>,
+{
+ fn from_str(obj: &str) -> Result<Self, ObjectError> {
+ serde_json::from_str(obj).map_err(ObjectError)
+ }
+
+ fn into_serialized_object(self) -> Result<JsonValue, ObjectError> {
+ serde_json::to_value(self).map_err(ObjectError)
+ }
+}
diff --git a/third_party/rust/glean-core/src/traits/timing_distribution.rs b/third_party/rust/glean-core/src/traits/timing_distribution.rs
index 03083753c6..ba618d2b4b 100644
--- a/third_party/rust/glean-core/src/traits/timing_distribution.rs
+++ b/third_party/rust/glean-core/src/traits/timing_distribution.rs
@@ -66,6 +66,31 @@ pub trait TimingDistribution {
/// are longer than `MAX_SAMPLE_TIME`.
fn accumulate_samples(&self, samples: Vec<i64>);
+ /// Accumulates precisely one signed sample in the metric.
+ ///
+ /// Precludes the need for a collection in the most common use case.
+ ///
+ /// Sign is required so that the platform-specific code can provide us with
+ /// a 64 bit signed integer if no `u64` comparable type is available. This
+ /// will take care of filtering and reporting errors for any provided negative
+ /// sample.
+ ///
+ /// Please note that this assumes that the provided sample is already in
+ /// the "unit" declared by the instance of the metric type (e.g. if the
+ /// instance this method was called on is using [`crate::TimeUnit::Second`], then
+ /// `sample` is assumed to be in that unit).
+ ///
+ /// # Arguments
+ ///
+ /// * `sample` - The singular sample to be recorded by the metric.
+ ///
+ /// ## Notes
+ ///
+ /// Discards any negative value and reports an [`ErrorType::InvalidValue`].
+ /// Reports an [`ErrorType::InvalidOverflow`] error if the sample is longer than
+ /// `MAX_SAMPLE_TIME`.
+ fn accumulate_single_sample(&self, sample: i64);
+
/// Accumulates the provided samples in the metric.
///
/// # Arguments
diff --git a/third_party/rust/glean-core/src/upload/directory.rs b/third_party/rust/glean-core/src/upload/directory.rs
index a78bbf0bdb..706550fe6c 100644
--- a/third_party/rust/glean-core/src/upload/directory.rs
+++ b/third_party/rust/glean-core/src/upload/directory.rs
@@ -9,15 +9,28 @@ use std::fs::{self, File};
use std::io::{BufRead, BufReader};
use std::path::{Path, PathBuf};
-use serde::Deserialize;
+use serde::{Deserialize, Serialize};
use uuid::Uuid;
use super::request::HeaderMap;
use crate::{DELETION_REQUEST_PINGS_DIRECTORY, PENDING_PINGS_DIRECTORY};
/// A representation of the data extracted from a ping file,
-/// this will contain the document_id, path, JSON encoded body of a ping and the persisted headers.
-pub type PingPayload = (String, String, String, Option<HeaderMap>);
+#[derive(Clone, Debug, Default)]
+pub struct PingPayload {
+ /// The ping's doc_id.
+ pub document_id: String,
+ /// The path to upload the ping to.
+ pub upload_path: String,
+ /// The ping body as JSON-encoded string.
+ pub json_body: String,
+ /// HTTP headers to include in the upload request.
+ pub headers: Option<HeaderMap>,
+ /// Whether the ping body contains {client|ping}_info
+ pub body_has_info_sections: bool,
+ /// The ping's name. (Also likely in the upload_path.)
+ pub ping_name: String,
+}
/// A struct to hold the result of scanning all pings directories.
#[derive(Clone, Debug, Default)]
@@ -62,20 +75,28 @@ fn get_file_name_as_str(path: &Path) -> Option<&str> {
}
}
+/// A ping's metadata, as (optionally) represented on disk.
+///
+/// Anything that isn't the upload path or the ping body.
+#[derive(Default, Deserialize, Serialize)]
+pub struct PingMetadata {
+ /// HTTP headers to include when uploading the ping.
+ pub headers: Option<HeaderMap>,
+ /// Whether the body has {client|ping}_info sections.
+ pub body_has_info_sections: Option<bool>,
+ /// The name of the ping.
+ pub ping_name: Option<String>,
+}
+
/// Processes a ping's metadata.
///
/// The metadata is an optional third line in the ping file,
/// currently it contains only additonal headers to be added to each ping request.
/// Therefore, we will process the contents of this line
/// and return a HeaderMap of the persisted headers.
-fn process_metadata(path: &str, metadata: &str) -> Option<HeaderMap> {
- #[derive(Deserialize)]
- struct PingMetadata {
- pub headers: HeaderMap,
- }
-
+fn process_metadata(path: &str, metadata: &str) -> Option<PingMetadata> {
if let Ok(metadata) = serde_json::from_str::<PingMetadata>(metadata) {
- return Some(metadata.headers);
+ return Some(metadata);
} else {
log::warn!("Error while parsing ping metadata: {}", path);
}
@@ -171,8 +192,23 @@ impl PingDirectoryManager {
if let (Some(Ok(path)), Some(Ok(body)), Ok(metadata)) =
(lines.next(), lines.next(), lines.next().transpose())
{
- let headers = metadata.and_then(|m| process_metadata(&path, &m));
- return Some((document_id.into(), path, body, headers));
+ let PingMetadata {
+ headers,
+ body_has_info_sections,
+ ping_name,
+ } = metadata
+ .and_then(|m| process_metadata(&path, &m))
+ .unwrap_or_default();
+ let ping_name =
+ ping_name.unwrap_or_else(|| path.split('/').nth(3).unwrap_or("").into());
+ return Some(PingPayload {
+ document_id: document_id.into(),
+ upload_path: path,
+ json_body: body,
+ headers,
+ body_has_info_sections: body_has_info_sections.unwrap_or(true),
+ ping_name,
+ });
} else {
log::warn!(
"Error processing ping file: {}. Ping file is not formatted as expected.",
@@ -303,7 +339,7 @@ mod test {
let (mut glean, dir) = new_glean(None);
// Register a ping for testing
- let ping_type = PingType::new("test", true, true, true, vec![]);
+ let ping_type = PingType::new("test", true, true, true, true, vec![]);
glean.register_ping_type(&ping_type);
// Submit the ping to populate the pending_pings directory
@@ -320,7 +356,8 @@ mod test {
// Verify request was returned for the "test" ping
let ping = &data.pending_pings[0].1;
- let request_ping_type = ping.1.split('/').nth(3).unwrap();
+ let request_ping_type = ping.upload_path.split('/').nth(3).unwrap();
+ assert_eq!(request_ping_type, ping.ping_name);
assert_eq!(request_ping_type, "test");
}
@@ -329,7 +366,7 @@ mod test {
let (mut glean, dir) = new_glean(None);
// Register a ping for testing
- let ping_type = PingType::new("test", true, true, true, vec![]);
+ let ping_type = PingType::new("test", true, true, true, true, vec![]);
glean.register_ping_type(&ping_type);
// Submit the ping to populate the pending_pings directory
@@ -352,7 +389,8 @@ mod test {
// Verify request was returned for the "test" ping
let ping = &data.pending_pings[0].1;
- let request_ping_type = ping.1.split('/').nth(3).unwrap();
+ let request_ping_type = ping.upload_path.split('/').nth(3).unwrap();
+ assert_eq!(request_ping_type, ping.ping_name);
assert_eq!(request_ping_type, "test");
// Verify that file was indeed deleted
@@ -364,7 +402,7 @@ mod test {
let (mut glean, dir) = new_glean(None);
// Register a ping for testing
- let ping_type = PingType::new("test", true, true, true, vec![]);
+ let ping_type = PingType::new("test", true, true, true, true, vec![]);
glean.register_ping_type(&ping_type);
// Submit the ping to populate the pending_pings directory
@@ -387,7 +425,8 @@ mod test {
// Verify request was returned for the "test" ping
let ping = &data.pending_pings[0].1;
- let request_ping_type = ping.1.split('/').nth(3).unwrap();
+ let request_ping_type = ping.upload_path.split('/').nth(3).unwrap();
+ assert_eq!(request_ping_type, ping.ping_name);
assert_eq!(request_ping_type, "test");
// Verify that file was indeed deleted
@@ -414,7 +453,8 @@ mod test {
// Verify request was returned for the "deletion-request" ping
let ping = &data.deletion_request_pings[0].1;
- let request_ping_type = ping.1.split('/').nth(3).unwrap();
+ let request_ping_type = ping.upload_path.split('/').nth(3).unwrap();
+ assert_eq!(request_ping_type, ping.ping_name);
assert_eq!(request_ping_type, "deletion-request");
}
}
diff --git a/third_party/rust/glean-core/src/upload/mod.rs b/third_party/rust/glean-core/src/upload/mod.rs
index d764dcd29e..e51a9d9508 100644
--- a/third_party/rust/glean-core/src/upload/mod.rs
+++ b/third_party/rust/glean-core/src/upload/mod.rs
@@ -30,6 +30,7 @@ use directory::{PingDirectoryManager, PingPayloadsByDirectory};
use policy::Policy;
use request::create_date_header_value;
+pub use directory::{PingMetadata, PingPayload};
pub use request::{HeaderMap, PingRequest};
pub use result::{UploadResult, UploadTaskAction};
@@ -322,21 +323,24 @@ impl PingUploadManager {
///
/// Returns the `PingRequest` or `None` if unable to build,
/// in which case it will delete the ping file and record an error.
- fn build_ping_request(
- &self,
- glean: &Glean,
- document_id: &str,
- path: &str,
- body: &str,
- headers: Option<HeaderMap>,
- ) -> Option<PingRequest> {
+ fn build_ping_request(&self, glean: &Glean, ping: PingPayload) -> Option<PingRequest> {
+ let PingPayload {
+ document_id,
+ upload_path: path,
+ json_body: body,
+ headers,
+ body_has_info_sections,
+ ping_name,
+ } = ping;
let mut request = PingRequest::builder(
&self.language_binding_name,
self.policy.max_ping_body_size(),
)
- .document_id(document_id)
+ .document_id(&document_id)
.path(path)
- .body(body);
+ .body(body)
+ .body_has_info_sections(body_has_info_sections)
+ .ping_name(ping_name);
if let Some(headers) = headers {
request = request.headers(headers);
@@ -346,7 +350,7 @@ impl PingUploadManager {
Ok(request) => Some(request),
Err(e) => {
log::warn!("Error trying to build ping request: {}", e);
- self.directory_manager.delete_file(document_id);
+ self.directory_manager.delete_file(&document_id);
// Record the error.
// Currently the only possible error is PingBodyOverflow.
@@ -362,23 +366,21 @@ impl PingUploadManager {
}
/// Enqueue a ping for upload.
- pub fn enqueue_ping(
- &self,
- glean: &Glean,
- document_id: &str,
- path: &str,
- body: &str,
- headers: Option<HeaderMap>,
- ) {
+ pub fn enqueue_ping(&self, glean: &Glean, ping: PingPayload) {
let mut queue = self
.queue
.write()
.expect("Can't write to pending pings queue.");
+ let PingPayload {
+ ref document_id,
+ upload_path: ref path,
+ ..
+ } = ping;
// Checks if a ping with this `document_id` is already enqueued.
if queue
.iter()
- .any(|request| request.document_id == document_id)
+ .any(|request| request.document_id.as_str() == document_id)
{
log::warn!(
"Attempted to enqueue a duplicate ping {} at {}.",
@@ -404,7 +406,7 @@ impl PingUploadManager {
}
log::trace!("Enqueuing ping {} at {}", document_id, path);
- if let Some(request) = self.build_ping_request(glean, document_id, path, body, headers) {
+ if let Some(request) = self.build_ping_request(glean, ping) {
queue.push_back(request)
}
}
@@ -455,7 +457,7 @@ impl PingUploadManager {
// Thus, we reverse the order of the pending pings vector,
// so that we iterate in descending order (newest -> oldest).
cached_pings.pending_pings.reverse();
- cached_pings.pending_pings.retain(|(file_size, (document_id, _, _, _))| {
+ cached_pings.pending_pings.retain(|(file_size, PingPayload {document_id, ..})| {
pending_pings_count += 1;
pending_pings_directory_size += file_size;
@@ -493,14 +495,14 @@ impl PingUploadManager {
// Enqueue the remaining pending pings and
// enqueue all deletion-request pings.
- let deletion_request_pings = cached_pings.deletion_request_pings.drain(..);
- for (_, (document_id, path, body, headers)) in deletion_request_pings {
- self.enqueue_ping(glean, &document_id, &path, &body, headers);
- }
- let pending_pings = cached_pings.pending_pings.drain(..);
- for (_, (document_id, path, body, headers)) in pending_pings {
- self.enqueue_ping(glean, &document_id, &path, &body, headers);
- }
+ cached_pings
+ .deletion_request_pings
+ .drain(..)
+ .for_each(|(_, ping)| self.enqueue_ping(glean, ping));
+ cached_pings
+ .pending_pings
+ .drain(..)
+ .for_each(|(_, ping)| self.enqueue_ping(glean, ping));
}
}
@@ -532,10 +534,8 @@ impl PingUploadManager {
/// * `glean` - The Glean object holding the database.
/// * `document_id` - The UUID of the ping in question.
pub fn enqueue_ping_from_file(&self, glean: &Glean, document_id: &str) {
- if let Some((doc_id, path, body, headers)) =
- self.directory_manager.process_file(document_id)
- {
- self.enqueue_ping(glean, &doc_id, &path, &body, headers)
+ if let Some(ping) = self.directory_manager.process_file(document_id) {
+ self.enqueue_ping(glean, ping);
}
}
@@ -883,7 +883,17 @@ mod test {
let upload_manager = PingUploadManager::no_policy(dir.path());
// Enqueue a ping
- upload_manager.enqueue_ping(&glean, &Uuid::new_v4().to_string(), PATH, "", None);
+ upload_manager.enqueue_ping(
+ &glean,
+ PingPayload {
+ document_id: Uuid::new_v4().to_string(),
+ upload_path: PATH.into(),
+ json_body: "".into(),
+ headers: None,
+ body_has_info_sections: true,
+ ping_name: "ping-name".into(),
+ },
+ );
// Try and get the next request.
// Verify request was returned
@@ -900,7 +910,17 @@ mod test {
// Enqueue a ping multiple times
let n = 10;
for _ in 0..n {
- upload_manager.enqueue_ping(&glean, &Uuid::new_v4().to_string(), PATH, "", None);
+ upload_manager.enqueue_ping(
+ &glean,
+ PingPayload {
+ document_id: Uuid::new_v4().to_string(),
+ upload_path: PATH.into(),
+ json_body: "".into(),
+ headers: None,
+ body_has_info_sections: true,
+ ping_name: "ping-name".into(),
+ },
+ );
}
// Verify a request is returned for each submitted ping
@@ -928,7 +948,17 @@ mod test {
// Enqueue the max number of pings allowed per uploading window
for _ in 0..max_pings_per_interval {
- upload_manager.enqueue_ping(&glean, &Uuid::new_v4().to_string(), PATH, "", None);
+ upload_manager.enqueue_ping(
+ &glean,
+ PingPayload {
+ document_id: Uuid::new_v4().to_string(),
+ upload_path: PATH.into(),
+ json_body: "".into(),
+ headers: None,
+ body_has_info_sections: true,
+ ping_name: "ping-name".into(),
+ },
+ );
}
// Verify a request is returned for each submitted ping
@@ -938,7 +968,17 @@ mod test {
}
// Enqueue just one more ping
- upload_manager.enqueue_ping(&glean, &Uuid::new_v4().to_string(), PATH, "", None);
+ upload_manager.enqueue_ping(
+ &glean,
+ PingPayload {
+ document_id: Uuid::new_v4().to_string(),
+ upload_path: PATH.into(),
+ json_body: "".into(),
+ headers: None,
+ body_has_info_sections: true,
+ ping_name: "ping-name".into(),
+ },
+ );
// Verify that we are indeed told to wait because we are at capacity
match upload_manager.get_upload_task(&glean, false) {
@@ -961,7 +1001,17 @@ mod test {
// Enqueue a ping multiple times
for _ in 0..10 {
- upload_manager.enqueue_ping(&glean, &Uuid::new_v4().to_string(), PATH, "", None);
+ upload_manager.enqueue_ping(
+ &glean,
+ PingPayload {
+ document_id: Uuid::new_v4().to_string(),
+ upload_path: PATH.into(),
+ json_body: "".into(),
+ headers: None,
+ body_has_info_sections: true,
+ ping_name: "ping-name".into(),
+ },
+ );
}
// Clear the queue
@@ -979,7 +1029,14 @@ mod test {
let (mut glean, _t) = new_glean(None);
// Register a ping for testing
- let ping_type = PingType::new("test", true, /* send_if_empty */ true, true, vec![]);
+ let ping_type = PingType::new(
+ "test",
+ true,
+ /* send_if_empty */ true,
+ true,
+ true,
+ vec![],
+ );
glean.register_ping_type(&ping_type);
// Submit the ping multiple times
@@ -1011,7 +1068,14 @@ mod test {
let (mut glean, dir) = new_glean(None);
// Register a ping for testing
- let ping_type = PingType::new("test", true, /* send_if_empty */ true, true, vec![]);
+ let ping_type = PingType::new(
+ "test",
+ true,
+ /* send_if_empty */ true,
+ true,
+ true,
+ vec![],
+ );
glean.register_ping_type(&ping_type);
// Submit the ping multiple times
@@ -1041,7 +1105,14 @@ mod test {
let (mut glean, dir) = new_glean(None);
// Register a ping for testing
- let ping_type = PingType::new("test", true, /* send_if_empty */ true, true, vec![]);
+ let ping_type = PingType::new(
+ "test",
+ true,
+ /* send_if_empty */ true,
+ true,
+ true,
+ vec![],
+ );
glean.register_ping_type(&ping_type);
// Submit a ping
@@ -1071,7 +1142,14 @@ mod test {
let (mut glean, dir) = new_glean(None);
// Register a ping for testing
- let ping_type = PingType::new("test", true, /* send_if_empty */ true, true, vec![]);
+ let ping_type = PingType::new(
+ "test",
+ true,
+ /* send_if_empty */ true,
+ true,
+ true,
+ vec![],
+ );
glean.register_ping_type(&ping_type);
// Submit a ping
@@ -1101,7 +1179,14 @@ mod test {
let (mut glean, _t) = new_glean(None);
// Register a ping for testing
- let ping_type = PingType::new("test", true, /* send_if_empty */ true, true, vec![]);
+ let ping_type = PingType::new(
+ "test",
+ true,
+ /* send_if_empty */ true,
+ true,
+ true,
+ vec![],
+ );
glean.register_ping_type(&ping_type);
// Submit a ping
@@ -1133,7 +1218,14 @@ mod test {
let (mut glean, dir) = new_glean(None);
// Register a ping for testing
- let ping_type = PingType::new("test", true, /* send_if_empty */ true, true, vec![]);
+ let ping_type = PingType::new(
+ "test",
+ true,
+ /* send_if_empty */ true,
+ true,
+ true,
+ vec![],
+ );
glean.register_ping_type(&ping_type);
// Submit a ping
@@ -1174,7 +1266,17 @@ mod test {
let path2 = format!("/submit/app_id/test-ping/1/{}", doc2);
// Enqueue a ping
- upload_manager.enqueue_ping(&glean, &doc1, &path1, "", None);
+ upload_manager.enqueue_ping(
+ &glean,
+ PingPayload {
+ document_id: doc1.clone(),
+ upload_path: path1,
+ json_body: "".into(),
+ headers: None,
+ body_has_info_sections: true,
+ ping_name: "test-ping".into(),
+ },
+ );
// Try and get the first request.
let req = match upload_manager.get_upload_task(&glean, false) {
@@ -1184,7 +1286,17 @@ mod test {
assert_eq!(doc1, req.document_id);
// Schedule the next one while the first one is "in progress"
- upload_manager.enqueue_ping(&glean, &doc2, &path2, "", None);
+ upload_manager.enqueue_ping(
+ &glean,
+ PingPayload {
+ document_id: doc2.clone(),
+ upload_path: path2,
+ json_body: "".into(),
+ headers: None,
+ body_has_info_sections: true,
+ ping_name: "test-ping".into(),
+ },
+ );
// Mark as processed
upload_manager.process_ping_upload_response(
@@ -1221,7 +1333,14 @@ mod test {
glean.set_debug_view_tag("valid-tag");
// Register a ping for testing
- let ping_type = PingType::new("test", true, /* send_if_empty */ true, true, vec![]);
+ let ping_type = PingType::new(
+ "test",
+ true,
+ /* send_if_empty */ true,
+ true,
+ true,
+ vec![],
+ );
glean.register_ping_type(&ping_type);
// Submit a ping
@@ -1248,8 +1367,28 @@ mod test {
let path = format!("/submit/app_id/test-ping/1/{}", doc_id);
// Try to enqueue a ping with the same doc_id twice
- upload_manager.enqueue_ping(&glean, &doc_id, &path, "", None);
- upload_manager.enqueue_ping(&glean, &doc_id, &path, "", None);
+ upload_manager.enqueue_ping(
+ &glean,
+ PingPayload {
+ document_id: doc_id.clone(),
+ upload_path: path.clone(),
+ json_body: "".into(),
+ headers: None,
+ body_has_info_sections: true,
+ ping_name: "test-ping".into(),
+ },
+ );
+ upload_manager.enqueue_ping(
+ &glean,
+ PingPayload {
+ document_id: doc_id,
+ upload_path: path,
+ json_body: "".into(),
+ headers: None,
+ body_has_info_sections: true,
+ ping_name: "test-ping".into(),
+ },
+ );
// Get a task once
let task = upload_manager.get_upload_task(&glean, false);
@@ -1267,7 +1406,14 @@ mod test {
let (mut glean, dir) = new_glean(None);
// Register a ping for testing
- let ping_type = PingType::new("test", true, /* send_if_empty */ true, true, vec![]);
+ let ping_type = PingType::new(
+ "test",
+ true,
+ /* send_if_empty */ true,
+ true,
+ true,
+ vec![],
+ );
glean.register_ping_type(&ping_type);
// Submit the ping multiple times
@@ -1317,7 +1463,14 @@ mod test {
let (mut glean, dir) = new_glean(None);
// Register a ping for testing
- let ping_type = PingType::new("test", true, /* send_if_empty */ true, true, vec![]);
+ let ping_type = PingType::new(
+ "test",
+ true,
+ /* send_if_empty */ true,
+ true,
+ true,
+ vec![],
+ );
glean.register_ping_type(&ping_type);
// Submit the ping multiple times
@@ -1331,7 +1484,10 @@ mod test {
// The pending pings array is sorted by date in ascending order,
// the newest element is the last one.
let (_, newest_ping) = &pending_pings.last().unwrap();
- let (newest_ping_id, _, _, _) = &newest_ping;
+ let PingPayload {
+ document_id: newest_ping_id,
+ ..
+ } = &newest_ping;
// Create a new upload manager pointing to the same data_path as the glean instance.
let mut upload_manager = PingUploadManager::no_policy(dir.path());
@@ -1385,7 +1541,14 @@ mod test {
let (mut glean, dir) = new_glean(None);
// Register a ping for testing
- let ping_type = PingType::new("test", true, /* send_if_empty */ true, true, vec![]);
+ let ping_type = PingType::new(
+ "test",
+ true,
+ /* send_if_empty */ true,
+ true,
+ true,
+ vec![],
+ );
glean.register_ping_type(&ping_type);
// How many pings we allow at maximum
@@ -1406,7 +1569,7 @@ mod test {
.iter()
.rev()
.take(count_quota)
- .map(|(_, ping)| ping.0.clone())
+ .map(|(_, ping)| ping.document_id.clone())
.collect::<Vec<_>>();
// Create a new upload manager pointing to the same data_path as the glean instance.
@@ -1457,7 +1620,14 @@ mod test {
let (mut glean, dir) = new_glean(None);
// Register a ping for testing
- let ping_type = PingType::new("test", true, /* send_if_empty */ true, true, vec![]);
+ let ping_type = PingType::new(
+ "test",
+ true,
+ /* send_if_empty */ true,
+ true,
+ true,
+ vec![],
+ );
glean.register_ping_type(&ping_type);
let expected_number_of_pings = 3;
@@ -1477,7 +1647,7 @@ mod test {
.iter()
.rev()
.take(expected_number_of_pings)
- .map(|(_, ping)| ping.0.clone())
+ .map(|(_, ping)| ping.document_id.clone())
.collect::<Vec<_>>();
// Create a new upload manager pointing to the same data_path as the glean instance.
@@ -1531,7 +1701,14 @@ mod test {
let (mut glean, dir) = new_glean(None);
// Register a ping for testing
- let ping_type = PingType::new("test", true, /* send_if_empty */ true, true, vec![]);
+ let ping_type = PingType::new(
+ "test",
+ true,
+ /* send_if_empty */ true,
+ true,
+ true,
+ vec![],
+ );
glean.register_ping_type(&ping_type);
let expected_number_of_pings = 2;
@@ -1551,7 +1728,7 @@ mod test {
.iter()
.rev()
.take(expected_number_of_pings)
- .map(|(_, ping)| ping.0.clone())
+ .map(|(_, ping)| ping.document_id.clone())
.collect::<Vec<_>>();
// Create a new upload manager pointing to the same data_path as the glean instance.
@@ -1622,8 +1799,28 @@ mod test {
upload_manager.set_rate_limiter(secs_per_interval, max_pings_per_interval);
// Enqueue two pings
- upload_manager.enqueue_ping(&glean, &Uuid::new_v4().to_string(), PATH, "", None);
- upload_manager.enqueue_ping(&glean, &Uuid::new_v4().to_string(), PATH, "", None);
+ upload_manager.enqueue_ping(
+ &glean,
+ PingPayload {
+ document_id: Uuid::new_v4().to_string(),
+ upload_path: PATH.into(),
+ json_body: "".into(),
+ headers: None,
+ body_has_info_sections: true,
+ ping_name: "ping-name".into(),
+ },
+ );
+ upload_manager.enqueue_ping(
+ &glean,
+ PingPayload {
+ document_id: Uuid::new_v4().to_string(),
+ upload_path: PATH.into(),
+ json_body: "".into(),
+ headers: None,
+ body_has_info_sections: true,
+ ping_name: "ping-name".into(),
+ },
+ );
// Get the first ping, it should be returned normally.
match upload_manager.get_upload_task(&glean, false) {
@@ -1679,12 +1876,28 @@ mod test {
let upload_manager = PingUploadManager::no_policy(dir.path());
// Enqueue a ping and start processing it
- let identifier = &Uuid::new_v4().to_string();
- upload_manager.enqueue_ping(&glean, identifier, PATH, "", None);
+ let identifier = &Uuid::new_v4();
+ let ping = PingPayload {
+ document_id: identifier.to_string(),
+ upload_path: PATH.into(),
+ json_body: "".into(),
+ headers: None,
+ body_has_info_sections: true,
+ ping_name: "ping-name".into(),
+ };
+ upload_manager.enqueue_ping(&glean, ping);
assert!(upload_manager.get_upload_task(&glean, false).is_upload());
// Attempt to re-enqueue the same ping
- upload_manager.enqueue_ping(&glean, identifier, PATH, "", None);
+ let ping = PingPayload {
+ document_id: identifier.to_string(),
+ upload_path: PATH.into(),
+ json_body: "".into(),
+ headers: None,
+ body_has_info_sections: true,
+ ping_name: "ping-name".into(),
+ };
+ upload_manager.enqueue_ping(&glean, ping);
// No new pings should have been enqueued so the upload task is Done.
assert_eq!(
@@ -1695,7 +1908,7 @@ mod test {
// Process the upload response
upload_manager.process_ping_upload_response(
&glean,
- identifier,
+ &identifier.to_string(),
UploadResult::http_status(200),
);
}
diff --git a/third_party/rust/glean-core/src/upload/request.rs b/third_party/rust/glean-core/src/upload/request.rs
index 0fd5ec5713..b4ac6eba97 100644
--- a/third_party/rust/glean-core/src/upload/request.rs
+++ b/third_party/rust/glean-core/src/upload/request.rs
@@ -62,6 +62,8 @@ pub struct Builder {
body: Option<Vec<u8>>,
headers: HeaderMap,
body_max_size: usize,
+ body_has_info_sections: Option<bool>,
+ ping_name: Option<String>,
}
impl Builder {
@@ -87,6 +89,8 @@ impl Builder {
body: None,
headers,
body_max_size,
+ body_has_info_sections: None,
+ ping_name: None,
}
}
@@ -138,6 +142,18 @@ impl Builder {
self
}
+ /// Sets whether the request body has {client|ping}_info sections.
+ pub fn body_has_info_sections(mut self, body_has_info_sections: bool) -> Self {
+ self.body_has_info_sections = Some(body_has_info_sections);
+ self
+ }
+
+ /// Sets the ping's name aka doctype.
+ pub fn ping_name<S: Into<String>>(mut self, ping_name: S) -> Self {
+ self.ping_name = Some(ping_name.into());
+ self
+ }
+
/// Sets a header for this request.
pub fn header<S: Into<String>>(mut self, key: S, value: S) -> Self {
self.headers.insert(key.into(), value.into());
@@ -174,6 +190,12 @@ impl Builder {
.expect("path must be set before attempting to build PingRequest"),
body,
headers: self.headers,
+ body_has_info_sections: self.body_has_info_sections.expect(
+ "body_has_info_sections must be set before attempting to build PingRequest",
+ ),
+ ping_name: self
+ .ping_name
+ .expect("ping_name must be set before attempting to build PingRequest"),
})
}
}
@@ -192,6 +214,10 @@ pub struct PingRequest {
pub body: Vec<u8>,
/// A map with all the headers to be sent with the request.
pub headers: HeaderMap,
+ /// Whether the body has {client|ping}_info sections.
+ pub body_has_info_sections: bool,
+ /// The ping's name. Likely also somewhere in `path`.
+ pub ping_name: String,
}
impl PingRequest {
@@ -208,12 +234,7 @@ impl PingRequest {
/// Verifies if current request is for a deletion-request ping.
pub fn is_deletion_request(&self) -> bool {
- // The path format should be `/submit/<app_id>/<ping_name>/<schema_version/<doc_id>`
- self.path
- .split('/')
- .nth(3)
- .map(|url| url == "deletion-request")
- .unwrap_or(false)
+ self.ping_name == "deletion-request"
}
/// Decompresses and pretty-format the ping payload
@@ -257,11 +278,15 @@ mod test {
.document_id("woop")
.path("/random/path/doesnt/matter")
.body("{}")
+ .body_has_info_sections(false)
+ .ping_name("whatevs")
.build()
.unwrap();
assert_eq!(request.document_id, "woop");
assert_eq!(request.path, "/random/path/doesnt/matter");
+ assert!(!request.body_has_info_sections);
+ assert_eq!(request.ping_name, "whatevs");
// Make sure all the expected headers were added.
assert!(request.headers.contains_key("X-Telemetry-Agent"));
diff --git a/third_party/rust/glean-core/tests/custom_distribution.rs b/third_party/rust/glean-core/tests/custom_distribution.rs
index 43c69fb26d..4cdfa5e99b 100644
--- a/third_party/rust/glean-core/tests/custom_distribution.rs
+++ b/third_party/rust/glean-core/tests/custom_distribution.rs
@@ -40,7 +40,7 @@ mod linear {
HistogramType::Linear,
);
- metric.accumulate_samples_sync(&glean, vec![50]);
+ metric.accumulate_samples_sync(&glean, &[50]);
let snapshot = metric
.get_value(&glean, "store1")
@@ -84,7 +84,7 @@ mod linear {
HistogramType::Linear,
);
- metric.accumulate_samples_sync(&glean, vec![50]);
+ metric.accumulate_samples_sync(&glean, &[50]);
for store_name in store_names {
let snapshot = StorageManager
@@ -126,7 +126,7 @@ mod linear {
// Accumulate the samples. We intentionally do not report
// negative values to not trigger error reporting.
- metric.accumulate_samples_sync(&glean, [1, 2, 3].to_vec());
+ metric.accumulate_samples_sync(&glean, &[1, 2, 3]);
let snapshot = metric
.get_value(&glean, "store1")
@@ -168,7 +168,7 @@ mod linear {
);
// Accumulate the samples.
- metric.accumulate_samples_sync(&glean, [-1, 1, 2, 3].to_vec());
+ metric.accumulate_samples_sync(&glean, &[-1, 1, 2, 3]);
let snapshot = metric
.get_value(&glean, "store1")
@@ -209,7 +209,7 @@ mod linear {
HistogramType::Linear,
);
- metric.accumulate_samples_sync(&glean, vec![50]);
+ metric.accumulate_samples_sync(&glean, &[50]);
let snapshot = metric.get_value(&glean, "store1");
assert!(snapshot.is_some());
@@ -242,7 +242,7 @@ mod exponential {
HistogramType::Exponential,
);
- metric.accumulate_samples_sync(&glean, vec![50]);
+ metric.accumulate_samples_sync(&glean, &[50]);
let snapshot = metric
.get_value(&glean, "store1")
@@ -286,7 +286,7 @@ mod exponential {
HistogramType::Exponential,
);
- metric.accumulate_samples_sync(&glean, vec![50]);
+ metric.accumulate_samples_sync(&glean, &[50]);
for store_name in store_names {
let snapshot = StorageManager
@@ -328,7 +328,7 @@ mod exponential {
// Accumulate the samples. We intentionally do not report
// negative values to not trigger error reporting.
- metric.accumulate_samples_sync(&glean, [1, 2, 3].to_vec());
+ metric.accumulate_samples_sync(&glean, &[1, 2, 3]);
let snapshot = metric
.get_value(&glean, "store1")
@@ -370,7 +370,7 @@ mod exponential {
);
// Accumulate the samples.
- metric.accumulate_samples_sync(&glean, [-1, 1, 2, 3].to_vec());
+ metric.accumulate_samples_sync(&glean, &[-1, 1, 2, 3]);
let snapshot = metric
.get_value(&glean, "store1")
@@ -411,7 +411,7 @@ mod exponential {
HistogramType::Exponential,
);
- metric.accumulate_samples_sync(&glean, vec![50]);
+ metric.accumulate_samples_sync(&glean, &[50]);
let snapshot = metric.get_value(&glean, "store1");
assert!(snapshot.is_some());
diff --git a/third_party/rust/glean-core/tests/event.rs b/third_party/rust/glean-core/tests/event.rs
index ed8f7d807f..c83e225ca2 100644
--- a/third_party/rust/glean-core/tests/event.rs
+++ b/third_party/rust/glean-core/tests/event.rs
@@ -166,6 +166,7 @@ fn test_sending_of_event_ping_when_it_fills_up() {
true,
false,
true,
+ true,
vec!["max_capacity".to_string()],
));
}
@@ -450,6 +451,7 @@ fn event_storage_trimming() {
true,
false,
true,
+ true,
vec![],
));
diff --git a/third_party/rust/glean-core/tests/object.rs b/third_party/rust/glean-core/tests/object.rs
new file mode 100644
index 0000000000..1e734e99d2
--- /dev/null
+++ b/third_party/rust/glean-core/tests/object.rs
@@ -0,0 +1,104 @@
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at https://mozilla.org/MPL/2.0/.
+
+mod common;
+use crate::common::*;
+
+use serde_json::json;
+
+use glean_core::metrics::*;
+use glean_core::storage::StorageManager;
+use glean_core::{CommonMetricData, Lifetime};
+
+#[test]
+fn object_serializer_should_correctly_serialize_objects() {
+ let (mut tempdir, _) = tempdir();
+
+ {
+ // We give tempdir to the `new_glean` function...
+ let (glean, dir) = new_glean(Some(tempdir));
+ // And then we get it back once that function returns.
+ tempdir = dir;
+
+ let metric = ObjectMetric::new(CommonMetricData {
+ name: "object_metric".into(),
+ category: "telemetry".into(),
+ send_in_pings: vec!["store1".into()],
+ disabled: false,
+ lifetime: Lifetime::User,
+ ..Default::default()
+ });
+
+ let obj = serde_json::from_str("{ \"value\": 1 }").unwrap();
+ metric.set_sync(&glean, obj);
+
+ let snapshot = StorageManager
+ .snapshot_as_json(glean.storage(), "store1", true)
+ .unwrap();
+ assert_eq!(
+ json!({"object": {"telemetry.object_metric": { "value": 1 }}}),
+ snapshot
+ );
+ }
+
+ // Make a new Glean instance here, which should force reloading of the data from disk
+ // so we can ensure it persisted, because it has User lifetime
+ {
+ let (glean, _t) = new_glean(Some(tempdir));
+ let snapshot = StorageManager
+ .snapshot_as_json(glean.storage(), "store1", true)
+ .unwrap();
+ assert_eq!(
+ json!({"object": {"telemetry.object_metric": { "value": 1 }}}),
+ snapshot
+ );
+ }
+}
+
+#[test]
+fn set_value_properly_sets_the_value_in_all_stores() {
+ let (glean, _t) = new_glean(None);
+ let store_names: Vec<String> = vec!["store1".into(), "store2".into()];
+
+ let metric = ObjectMetric::new(CommonMetricData {
+ name: "object_metric".into(),
+ category: "telemetry".into(),
+ send_in_pings: store_names.clone(),
+ disabled: false,
+ lifetime: Lifetime::Ping,
+ ..Default::default()
+ });
+
+ let obj = serde_json::from_str("{ \"value\": 1 }").unwrap();
+ metric.set_sync(&glean, obj);
+
+ for store_name in store_names {
+ let snapshot = StorageManager
+ .snapshot_as_json(glean.storage(), &store_name, true)
+ .unwrap();
+
+ assert_eq!(
+ json!({"object": {"telemetry.object_metric": { "value": 1 }}}),
+ snapshot
+ );
+ }
+}
+
+#[test]
+fn getting_data_json_encoded() {
+ let (glean, _t) = new_glean(None);
+
+ let object: ObjectMetric = ObjectMetric::new(CommonMetricData {
+ name: "transformation".into(),
+ category: "local".into(),
+ send_in_pings: vec!["store1".into()],
+ ..Default::default()
+ });
+
+ let obj_str = "{\"value\":1}";
+ let obj = serde_json::from_str(obj_str).unwrap();
+ object.set_sync(&glean, obj);
+
+ assert_eq!(obj_str, object.get_value(&glean, Some("store1")).unwrap());
+}
diff --git a/third_party/rust/glean-core/tests/ping.rs b/third_party/rust/glean-core/tests/ping.rs
index 0ee3736168..17944b4c24 100644
--- a/third_party/rust/glean-core/tests/ping.rs
+++ b/third_party/rust/glean-core/tests/ping.rs
@@ -15,7 +15,7 @@ use glean_core::Lifetime;
fn write_ping_to_disk() {
let (mut glean, _temp) = new_glean(None);
- let ping = PingType::new("metrics", true, false, true, vec![]);
+ let ping = PingType::new("metrics", true, false, true, true, vec![]);
glean.register_ping_type(&ping);
// We need to store a metric as an empty ping is not stored.
@@ -36,7 +36,7 @@ fn write_ping_to_disk() {
fn disabling_upload_clears_pending_pings() {
let (mut glean, _t) = new_glean(None);
- let ping = PingType::new("metrics", true, false, true, vec![]);
+ let ping = PingType::new("metrics", true, false, true, true, vec![]);
glean.register_ping_type(&ping);
// We need to store a metric as an empty ping is not stored.
@@ -105,9 +105,9 @@ fn deletion_request_only_when_toggled_from_on_to_off() {
fn empty_pings_with_flag_are_sent() {
let (mut glean, _t) = new_glean(None);
- let ping1 = PingType::new("custom-ping1", true, true, true, vec![]);
+ let ping1 = PingType::new("custom-ping1", true, true, true, true, vec![]);
glean.register_ping_type(&ping1);
- let ping2 = PingType::new("custom-ping2", true, false, true, vec![]);
+ let ping2 = PingType::new("custom-ping2", true, false, true, true, vec![]);
glean.register_ping_type(&ping2);
// No data is stored in either of the custom pings
@@ -139,10 +139,10 @@ fn test_pings_submitted_metric() {
None,
);
- let metrics_ping = PingType::new("metrics", true, false, true, vec![]);
+ let metrics_ping = PingType::new("metrics", true, false, true, true, vec![]);
glean.register_ping_type(&metrics_ping);
- let baseline_ping = PingType::new("baseline", true, false, true, vec![]);
+ let baseline_ping = PingType::new("baseline", true, false, true, true, vec![]);
glean.register_ping_type(&baseline_ping);
// We need to store a metric as an empty ping is not stored.
@@ -218,7 +218,7 @@ fn test_pings_submitted_metric() {
fn events_ping_with_metric_but_no_events_is_not_sent() {
let (mut glean, _t) = new_glean(None);
- let events_ping = PingType::new("events", true, true, true, vec![]);
+ let events_ping = PingType::new("events", true, true, true, true, vec![]);
glean.register_ping_type(&events_ping);
let counter = CounterMetric::new(CommonMetricData {
name: "counter".into(),
diff --git a/third_party/rust/glean-core/tests/ping_maker.rs b/third_party/rust/glean-core/tests/ping_maker.rs
index 29b6bccaca..bc3aac6311 100644
--- a/third_party/rust/glean-core/tests/ping_maker.rs
+++ b/third_party/rust/glean-core/tests/ping_maker.rs
@@ -13,7 +13,7 @@ fn set_up_basic_ping() -> (Glean, PingMaker, PingType, tempfile::TempDir) {
let (tempdir, _) = tempdir();
let (mut glean, t) = new_glean(Some(tempdir));
let ping_maker = PingMaker::new();
- let ping_type = PingType::new("store1", true, false, true, vec![]);
+ let ping_type = PingType::new("store1", true, false, true, true, vec![]);
glean.register_ping_type(&ping_type);
// Record something, so the ping will have data
@@ -89,12 +89,12 @@ fn test_metrics_must_report_experimentation_id() {
trim_data_to_registered_pings: false,
log_level: None,
rate_limit: None,
- enable_event_timestamps: false,
+ enable_event_timestamps: true,
experimentation_id: Some("test-experimentation-id".to_string()),
})
.unwrap();
let ping_maker = PingMaker::new();
- let ping_type = PingType::new("store1", true, false, true, vec![]);
+ let ping_type = PingType::new("store1", true, false, true, true, vec![]);
glean.register_ping_type(&ping_type);
// Record something, so the ping will have data
@@ -141,13 +141,13 @@ fn experimentation_id_is_removed_if_send_if_empty_is_false() {
trim_data_to_registered_pings: false,
log_level: None,
rate_limit: None,
- enable_event_timestamps: false,
+ enable_event_timestamps: true,
experimentation_id: Some("test-experimentation-id".to_string()),
})
.unwrap();
let ping_maker = PingMaker::new();
- let unknown_ping_type = PingType::new("unknown", true, false, true, vec![]);
+ let unknown_ping_type = PingType::new("unknown", true, false, true, true, vec![]);
glean.register_ping_type(&unknown_ping_type);
assert!(ping_maker
@@ -163,7 +163,7 @@ fn collect_must_report_none_when_no_data_is_stored() {
let (mut glean, ping_maker, ping_type, _t) = set_up_basic_ping();
- let unknown_ping_type = PingType::new("unknown", true, false, true, vec![]);
+ let unknown_ping_type = PingType::new("unknown", true, false, true, true, vec![]);
glean.register_ping_type(&ping_type);
assert!(ping_maker
@@ -187,7 +187,7 @@ fn seq_number_must_be_sequential() {
for i in 0..=1 {
for ping_name in ["store1", "store2"].iter() {
- let ping_type = PingType::new(*ping_name, true, false, true, vec![]);
+ let ping_type = PingType::new(*ping_name, true, false, true, true, vec![]);
let ping = ping_maker
.collect(&glean, &ping_type, None, "", "")
.unwrap();
@@ -200,7 +200,7 @@ fn seq_number_must_be_sequential() {
// Test that ping sequence numbers increase independently.
{
- let ping_type = PingType::new("store1", true, false, true, vec![]);
+ let ping_type = PingType::new("store1", true, false, true, true, vec![]);
// 3rd ping of store1
let ping = ping_maker
@@ -218,7 +218,7 @@ fn seq_number_must_be_sequential() {
}
{
- let ping_type = PingType::new("store2", true, false, true, vec![]);
+ let ping_type = PingType::new("store2", true, false, true, true, vec![]);
// 3rd ping of store2
let ping = ping_maker
@@ -229,7 +229,7 @@ fn seq_number_must_be_sequential() {
}
{
- let ping_type = PingType::new("store1", true, false, true, vec![]);
+ let ping_type = PingType::new("store1", true, false, true, true, vec![]);
// 5th ping of store1
let ping = ping_maker
@@ -244,7 +244,7 @@ fn seq_number_must_be_sequential() {
fn clear_pending_pings() {
let (mut glean, _t) = new_glean(None);
let ping_maker = PingMaker::new();
- let ping_type = PingType::new("store1", true, false, true, vec![]);
+ let ping_type = PingType::new("store1", true, false, true, true, vec![]);
glean.register_ping_type(&ping_type);
// Record something, so the ping will have data
@@ -272,7 +272,7 @@ fn no_pings_submitted_if_upload_disabled() {
// Regression test, bug 1603571
let (mut glean, _t) = new_glean(None);
- let ping_type = PingType::new("store1", true, true, true, vec![]);
+ let ping_type = PingType::new("store1", true, true, true, true, vec![]);
glean.register_ping_type(&ping_type);
assert!(ping_type.submit_sync(&glean, None));
@@ -290,7 +290,7 @@ fn no_pings_submitted_if_upload_disabled() {
fn metadata_is_correctly_added_when_necessary() {
let (mut glean, _t) = new_glean(None);
glean.set_debug_view_tag("valid-tag");
- let ping_type = PingType::new("store1", true, true, true, vec![]);
+ let ping_type = PingType::new("store1", true, true, true, true, vec![]);
glean.register_ping_type(&ping_type);
assert!(ping_type.submit_sync(&glean, None));
diff --git a/third_party/rust/glean-core/tests/timing_distribution.rs b/third_party/rust/glean-core/tests/timing_distribution.rs
index 96f7fae5af..59ce5fbadb 100644
--- a/third_party/rust/glean-core/tests/timing_distribution.rs
+++ b/third_party/rust/glean-core/tests/timing_distribution.rs
@@ -169,7 +169,7 @@ fn the_accumulate_samples_api_correctly_stores_timing_values() {
// Accumulate the samples. We intentionally do not report
// negative values to not trigger error reporting.
- metric.accumulate_samples_sync(&glean, [1, 2, 3].to_vec());
+ metric.accumulate_samples_sync(&glean, &[1, 2, 3]);
let snapshot = metric
.get_value(&glean, "store1")
@@ -211,7 +211,7 @@ fn the_accumulate_samples_api_correctly_handles_negative_values() {
);
// Accumulate the samples.
- metric.accumulate_samples_sync(&glean, [-1, 1, 2, 3].to_vec());
+ metric.accumulate_samples_sync(&glean, &[-1, 1, 2, 3]);
let snapshot = metric
.get_value(&glean, "store1")
@@ -255,7 +255,7 @@ fn the_accumulate_samples_api_correctly_handles_overflowing_values() {
const MAX_SAMPLE_TIME: u64 = 1000 * 1000 * 1000 * 60 * 10;
let overflowing_val = MAX_SAMPLE_TIME as i64 + 1;
// Accumulate the samples.
- metric.accumulate_samples_sync(&glean, [overflowing_val, 1, 2, 3].to_vec());
+ metric.accumulate_samples_sync(&glean, &[overflowing_val, 1, 2, 3]);
let snapshot = metric
.get_value(&glean, "store1")
diff --git a/third_party/rust/glean/.cargo-checksum.json b/third_party/rust/glean/.cargo-checksum.json
index 74c885aad7..f624e73c99 100644
--- a/third_party/rust/glean/.cargo-checksum.json
+++ b/third_party/rust/glean/.cargo-checksum.json
@@ -1 +1 @@
-{"files":{"Cargo.toml":"13aebaf3dc74c32fa745410731828cf3b8d27025f5d3371b31178531bf49b009","LICENSE":"1f256ecad192880510e84ad60474eab7589218784b9a50bc7ceee34c2b91f1d5","README.md":"5627cc81e6187ab6c2b4dff061af16d559edcab64ba786bac39daa69c703c595","src/common_test.rs":"de47b53dcca37985c0a2b8c02daecbf32309aa54f5a4dd9290719c2c1fd0fa55","src/configuration.rs":"186b9d92e48f2f34f1ea51023ad83c9d7abec97bcb0b862641bcb79a93c97d9e","src/core_metrics.rs":"fef8fb4e5fa57c179836c6eb2cf59278fe3b8b036dbe57b0ff02971b4acd822f","src/lib.rs":"c0229403026edc22ddf53ea60ea92cdd2eb820c06f63a6057b3ef10050800279","src/net/http_uploader.rs":"43812a70d19a38e8d7a093c8076c2b6345372c3c861b0f3511428762700a65e0","src/net/mod.rs":"612a9f13ade0b202c8762bccc7b5dc288101cb3820e47be2755331911a221c55","src/private/event.rs":"d7c70c02648584c19c73af89e5180d3c6153c911f2c6830f7d1599b18d6150eb","src/private/mod.rs":"eb8fe4e588bb32a54617324db39319920c627e6fc23c23cf4da5c17c63e0afed","src/private/ping.rs":"da7545b8fc3b8cbaa8f598dc18fa4ba7c499573c360e8de6e1714d38dd4a665d","src/system.rs":"6eae5b41c15eba9cad6dbd116abe3519ee3e1fe034e79bdd692b029829a8c384","src/test.rs":"3ff6cf151f416e7a8d0f1b54ff28360ad7ebd3dcf47df02c08a8039702d636d9","tests/common/mod.rs":"08fb9483d9b6ed9fe873b4395245166ae8a15263be750c7a8e298c41d9604745","tests/init_fails.rs":"96bbb67ec64976f505fc05eb2c2e101f06c11bd2eaf6a0c6383b1a3e05a1cf4b","tests/never_init.rs":"2cf13a67b34cda8447f489cf3755eba0595978d242d29025670f9bb7c1025937","tests/no_time_to_init.rs":"5fd92258e3fb0302c7f8ab1dee374ece7343cc727206d9da134442bca746b88d","tests/overflowing_preinit.rs":"7ad4b2274dd9240b53430859a4eb1d2597cf508a5a678333f3d3abbadd2ed4a7","tests/persist_ping_lifetime.rs":"81415dc1d74743f02269f0d0dfa524003147056853f080276972e64a0b761d3c","tests/persist_ping_lifetime_nopanic.rs":"18379d3ffbf4a2c8c684c04ff7a0660b86dfbbb447db2d24dfed6073cb7ddf8f","tests/schema.rs":"e611cc168b648b5f515c54873c71b3e5e86431afa2aaff94581eeeb4f1d4935d","tests/simple.rs":"3a762995c06c0adc5adb94ad9c37e41dbbcf08a96b87bc20a449240fa0d93b0c","tests/test-shutdown-blocking.sh":"9b16a01c190c7062474dd92182298a3d9a27928c8fa990340fdd798e6cdb7ab2","tests/test-thread-crashing.sh":"ff1bc8e5d7e4ba3a10d0d38bef222db8bfba469e7d30e45b1053d177a4084f09","tests/upload_timing.rs":"4100ddba97cad28e1abfeb1492dfff61d151a67360392393d6f90d891b3e86be"},"package":"ae5847ad58b7f925c984de7f4dffcad67d7d0befa59a5a888cf93741b5ef1e6a"} \ No newline at end of file
+{"files":{"Cargo.toml":"29b8551de6fff2f0fa3a821eb933f71a2a326b3ce3d37c25bcef3001f9146dfb","LICENSE":"1f256ecad192880510e84ad60474eab7589218784b9a50bc7ceee34c2b91f1d5","README.md":"5627cc81e6187ab6c2b4dff061af16d559edcab64ba786bac39daa69c703c595","src/common_test.rs":"454df3d99eef045270e813946f921f56c39b16c18a5fadedc32829c3d44129cf","src/configuration.rs":"82b3a7933d913e1e2a4f328a76621db2d2e618d209d9785086d64c5c78c2a2d6","src/core_metrics.rs":"fef8fb4e5fa57c179836c6eb2cf59278fe3b8b036dbe57b0ff02971b4acd822f","src/lib.rs":"aa9c81fc6dc19ca1cb4bede25d554377a5d717fb3b246967edb1be12a395ce61","src/net/http_uploader.rs":"01ad5bd91384411a12c74434cd1c5cd585078cb34faba4615c70bdb669a9bccb","src/net/mod.rs":"f47b96bb878f1a6c771cedbaeaeefb270bc87fb1d1bbbed1b282dddca16216ed","src/private/event.rs":"d7c70c02648584c19c73af89e5180d3c6153c911f2c6830f7d1599b18d6150eb","src/private/mod.rs":"3565eb569d2b96f938f130abe0fc3ee3f55e7e03fd6501e309d3ef6af72ef6ee","src/private/object.rs":"3f70363a196aea46cc163af025a53e48c117c6208babc4bce772bb4c337cced8","src/private/ping.rs":"a6262a3453c77cbf30766c19b535a1bf66a37b2a316e8f87baee03025255c33e","src/system.rs":"6eae5b41c15eba9cad6dbd116abe3519ee3e1fe034e79bdd692b029829a8c384","src/test.rs":"6388b9e8bf96e0fb56ad71b7a5b5630d209ae62f1a65c62e878cbc1757ddd585","tests/common/mod.rs":"08fb9483d9b6ed9fe873b4395245166ae8a15263be750c7a8e298c41d9604745","tests/init_fails.rs":"906bbf0faa613976623e0cf782bd86545b49d76afaab182af7634690b747ebf7","tests/never_init.rs":"19bad996e22f7d6958cc1a650528530aa7d1aeb4a8ab42229a90bbc0315c8ed1","tests/no_time_to_init.rs":"06c81148c27d383cb708c0c80a2e806024c9955337d7adfba8c53aaeade9be67","tests/overflowing_preinit.rs":"7ad4b2274dd9240b53430859a4eb1d2597cf508a5a678333f3d3abbadd2ed4a7","tests/persist_ping_lifetime.rs":"81415dc1d74743f02269f0d0dfa524003147056853f080276972e64a0b761d3c","tests/persist_ping_lifetime_nopanic.rs":"18379d3ffbf4a2c8c684c04ff7a0660b86dfbbb447db2d24dfed6073cb7ddf8f","tests/schema.rs":"9d24028cab4dc60fe3c4d7a0bafbff0815cbc0249fa3e23625d42c3b4fa71734","tests/simple.rs":"1b8b227249ae9d3cc281db07ed779bc75252c7849b1c48b4ac3d765228d65b20","tests/test-shutdown-blocking.sh":"9b16a01c190c7062474dd92182298a3d9a27928c8fa990340fdd798e6cdb7ab2","tests/test-thread-crashing.sh":"ff1bc8e5d7e4ba3a10d0d38bef222db8bfba469e7d30e45b1053d177a4084f09","tests/upload_timing.rs":"3024b7999a0c23f2c3d7e59725b5455522e4e9fdf63e3265b93fea4cec18725f"},"package":"f58388f10d013e2d12bb58e6e76983ede120789956fe827913a3d2560c66d44d"} \ No newline at end of file
diff --git a/third_party/rust/glean/Cargo.toml b/third_party/rust/glean/Cargo.toml
index 1a702be403..bc25a08940 100644
--- a/third_party/rust/glean/Cargo.toml
+++ b/third_party/rust/glean/Cargo.toml
@@ -13,7 +13,7 @@
edition = "2021"
rust-version = "1.66"
name = "glean"
-version = "57.0.0"
+version = "58.1.0"
authors = [
"Jan-Erik Rediger <jrediger@mozilla.com>",
"The Glean Team <glean-team@mozilla.com>",
@@ -34,15 +34,8 @@ keywords = [
license = "MPL-2.0"
repository = "https://github.com/mozilla/glean"
-[dependencies.chrono]
-version = "0.4.10"
-features = ["serde"]
-
-[dependencies.crossbeam-channel]
-version = "0.5"
-
[dependencies.glean-core]
-version = "57.0.0"
+version = "58.1.0"
[dependencies.inherent]
version = "1"
@@ -53,26 +46,12 @@ version = "0.4.8"
[dependencies.once_cell]
version = "1.18.0"
-[dependencies.serde]
-version = "1.0.104"
-features = ["derive"]
-
-[dependencies.serde_json]
-version = "1.0.44"
-
-[dependencies.thiserror]
-version = "1.0.4"
-
-[dependencies.time]
-version = "0.1.40"
-
-[dependencies.uuid]
-version = "1.0"
-features = ["v4"]
-
[dependencies.whatsys]
version = "0.3.0"
+[dev-dependencies.crossbeam-channel]
+version = "0.5"
+
[dev-dependencies.env_logger]
version = "0.10.0"
features = ["humantime"]
@@ -87,6 +66,9 @@ version = "0.5.0"
[dev-dependencies.libc]
version = "0.2"
+[dev-dependencies.serde_json]
+version = "1.0.44"
+
[dev-dependencies.tempfile]
version = "3.1.0"
diff --git a/third_party/rust/glean/src/common_test.rs b/third_party/rust/glean/src/common_test.rs
index fdb7cfadbf..e3c80da5f2 100644
--- a/third_party/rust/glean/src/common_test.rs
+++ b/third_party/rust/glean/src/common_test.rs
@@ -42,6 +42,7 @@ pub(crate) fn new_glean(
Some(c) => c,
None => ConfigurationBuilder::new(true, tmpname, GLOBAL_APPLICATION_ID)
.with_server_endpoint("invalid-test-host")
+ .with_event_timestamps(false)
.build(),
};
diff --git a/third_party/rust/glean/src/configuration.rs b/third_party/rust/glean/src/configuration.rs
index 42360e96e0..ca0a39c3f1 100644
--- a/third_party/rust/glean/src/configuration.rs
+++ b/third_party/rust/glean/src/configuration.rs
@@ -113,7 +113,7 @@ impl Builder {
trim_data_to_registered_pings: false,
log_level: None,
rate_limit: None,
- enable_event_timestamps: false,
+ enable_event_timestamps: true,
experimentation_id: None,
}
}
diff --git a/third_party/rust/glean/src/lib.rs b/third_party/rust/glean/src/lib.rs
index 538b8c590d..5c5b945a95 100644
--- a/third_party/rust/glean/src/lib.rs
+++ b/third_party/rust/glean/src/lib.rs
@@ -23,7 +23,7 @@
//! let cfg = ConfigurationBuilder::new(true, "/tmp/data", "org.mozilla.glean_core.example").build();
//! glean::initialize(cfg, ClientInfoMetrics::unknown());
//!
-//! let prototype_ping = PingType::new("prototype", true, true, true, vec!());
+//! let prototype_ping = PingType::new("prototype", true, true, true, true, vec!());
//!
//! prototype_ping.submit(None);
//! ```
diff --git a/third_party/rust/glean/src/net/http_uploader.rs b/third_party/rust/glean/src/net/http_uploader.rs
index 4646fe61b4..4ca1687acf 100644
--- a/third_party/rust/glean/src/net/http_uploader.rs
+++ b/third_party/rust/glean/src/net/http_uploader.rs
@@ -2,7 +2,7 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
-use crate::net::{PingUploader, UploadResult};
+use crate::net::{PingUploadRequest, PingUploader, UploadResult};
/// A simple mechanism to upload pings over HTTPS.
#[derive(Debug)]
@@ -13,12 +13,9 @@ impl PingUploader for HttpUploader {
///
/// # Arguments
///
- /// * `url` - the URL path to upload the data to.
- /// * `body` - the serialized text data to send.
- /// * `headers` - a vector of tuples containing the headers to send with
- /// the request, i.e. (Name, Value).
- fn upload(&self, url: String, _body: Vec<u8>, _headers: Vec<(String, String)>) -> UploadResult {
- log::debug!("TODO bug 1675468: submitting to {:?}", url);
+ /// * `upload_request` - the requested upload.
+ fn upload(&self, upload_request: PingUploadRequest) -> UploadResult {
+ log::debug!("TODO bug 1675468: submitting to {:?}", upload_request.url);
UploadResult::http_status(200)
}
}
diff --git a/third_party/rust/glean/src/net/mod.rs b/third_party/rust/glean/src/net/mod.rs
index 5571d30e67..5546078e63 100644
--- a/third_party/rust/glean/src/net/mod.rs
+++ b/third_party/rust/glean/src/net/mod.rs
@@ -19,6 +19,20 @@ use thread_state::{AtomicState, State};
mod http_uploader;
+/// Everything you need to request a ping to be uploaded.
+pub struct PingUploadRequest {
+ /// The URL the Glean SDK expects you to use to upload the ping.
+ pub url: String,
+ /// The body, already content-encoded, for upload.
+ pub body: Vec<u8>,
+ /// The HTTP headers, including any Content-Encoding.
+ pub headers: Vec<(String, String)>,
+ /// Whether the body has {client|ping}_info sections in it.
+ pub body_has_info_sections: bool,
+ /// The name (aka doctype) of the ping.
+ pub ping_name: String,
+}
+
/// A description of a component used to upload pings.
pub trait PingUploader: std::fmt::Debug + Send + Sync {
/// Uploads a ping to a server.
@@ -29,7 +43,7 @@ pub trait PingUploader: std::fmt::Debug + Send + Sync {
/// * `body` - the serialized text data to send.
/// * `headers` - a vector of tuples containing the headers to send with
/// the request, i.e. (Name, Value).
- fn upload(&self, url: String, body: Vec<u8>, headers: Vec<(String, String)>) -> UploadResult;
+ fn upload(&self, upload_request: PingUploadRequest) -> UploadResult;
}
/// The logic for uploading pings: this leaves the actual upload mechanism as
@@ -105,7 +119,14 @@ impl UploadManager {
let upload_url = format!("{}{}", inner.server_endpoint, request.path);
let headers: Vec<(String, String)> =
request.headers.into_iter().collect();
- let result = inner.uploader.upload(upload_url, request.body, headers);
+ let upload_request = PingUploadRequest {
+ url: upload_url,
+ body: request.body,
+ headers,
+ body_has_info_sections: request.body_has_info_sections,
+ ping_name: request.ping_name,
+ };
+ let result = inner.uploader.upload(upload_request);
// Process the upload response.
match glean_core::glean_process_ping_upload_response(doc_id, result) {
UploadTaskAction::Next => (),
diff --git a/third_party/rust/glean/src/private/mod.rs b/third_party/rust/glean/src/private/mod.rs
index 8a5c304193..575707cf59 100644
--- a/third_party/rust/glean/src/private/mod.rs
+++ b/third_party/rust/glean/src/private/mod.rs
@@ -5,6 +5,7 @@
//! The different metric types supported by the Glean SDK to handle data.
mod event;
+mod object;
mod ping;
pub use event::EventMetric;
@@ -26,6 +27,7 @@ pub use glean_core::UrlMetric;
pub use glean_core::UuidMetric;
pub use glean_core::{AllowLabeled, LabeledMetric};
pub use glean_core::{Datetime, DatetimeMetric};
+pub use object::ObjectMetric;
pub use ping::PingType;
// Re-export types that are used by the glean_parser-generated code.
diff --git a/third_party/rust/glean/src/private/object.rs b/third_party/rust/glean/src/private/object.rs
new file mode 100644
index 0000000000..f7403ec889
--- /dev/null
+++ b/third_party/rust/glean/src/private/object.rs
@@ -0,0 +1,192 @@
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at https://mozilla.org/MPL/2.0/.
+
+use std::marker::PhantomData;
+
+use glean_core::metrics::JsonValue;
+use glean_core::traits;
+
+use crate::ErrorType;
+
+// We need to wrap the glean-core type: otherwise if we try to implement
+// the trait for the metric in `glean_core::metrics` we hit error[E0117]:
+// only traits defined in the current crate can be implemented for arbitrary
+// types.
+
+/// Developer-facing API for recording object metrics.
+///
+/// Instances of this class type are automatically generated by the parsers
+/// at build time, allowing developers to record values that were previously
+/// registered in the metrics.yaml file.
+#[derive(Clone)]
+pub struct ObjectMetric<K> {
+ pub(crate) inner: glean_core::metrics::ObjectMetric,
+ object_type: PhantomData<K>,
+}
+
+impl<K: traits::ObjectSerialize> ObjectMetric<K> {
+ /// The public constructor used by automatically generated metrics.
+ pub fn new(meta: glean_core::CommonMetricData) -> Self {
+ let inner = glean_core::metrics::ObjectMetric::new(meta);
+ Self {
+ inner,
+ object_type: PhantomData,
+ }
+ }
+
+ /// Sets to the specified structure.
+ ///
+ /// # Arguments
+ ///
+ /// * `object` - the object to set.
+ pub fn set(&self, object: K) {
+ let obj = object
+ .into_serialized_object()
+ .expect("failed to serialize object. This should be impossible.");
+ self.inner.set(obj);
+ }
+
+ /// Sets to the specified structure.
+ ///
+ /// Parses the passed JSON string.
+ /// If it can't be parsed into a valid object it records an invalid value error.
+ ///
+ /// # Arguments
+ ///
+ /// * `object` - JSON representation of the object to set.
+ pub fn set_string(&self, object: String) {
+ let data = match K::from_str(&object) {
+ Ok(data) => data,
+ Err(_) => {
+ self.inner.record_schema_error();
+ return;
+ }
+ };
+ self.set(data)
+ }
+
+ /// **Test-only API (exported for FFI purposes).**
+ ///
+ /// Gets the currently stored value as JSON-encoded string.
+ ///
+ /// This doesn't clear the stored value.
+ pub fn test_get_value<'a, S: Into<Option<&'a str>>>(&self, ping_name: S) -> Option<JsonValue> {
+ let ping_name = ping_name.into().map(|s| s.to_string());
+ self.inner.test_get_value(ping_name)
+ }
+
+ /// **Exported for test purposes.**
+ ///
+ /// Gets the number of recorded errors for the given metric and error type.
+ ///
+ /// # Arguments
+ ///
+ /// * `error` - The type of error
+ ///
+ /// # Returns
+ ///
+ /// The number of errors reported.
+ pub fn test_get_num_recorded_errors(&self, error: ErrorType) -> i32 {
+ self.inner.test_get_num_recorded_errors(error)
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+ use crate::common_test::{lock_test, new_glean};
+ use crate::CommonMetricData;
+
+ use serde_json::json;
+
+ #[test]
+ fn simple_array() {
+ let _lock = lock_test();
+ let _t = new_glean(None, true);
+
+ type SimpleArray = Vec<i64>;
+
+ let metric: ObjectMetric<SimpleArray> = ObjectMetric::new(CommonMetricData {
+ name: "object".into(),
+ category: "test".into(),
+ send_in_pings: vec!["test1".into()],
+ ..Default::default()
+ });
+
+ let arr = SimpleArray::from([1, 2, 3]);
+ metric.set(arr);
+
+ let data = metric.test_get_value(None).expect("no object recorded");
+ let expected = json!([1, 2, 3]);
+ assert_eq!(expected, data);
+ }
+
+ #[test]
+ fn complex_nested_object() {
+ let _lock = lock_test();
+ let _t = new_glean(None, true);
+
+ type BalloonsObject = Vec<BalloonsObjectItem>;
+
+ #[derive(
+ Debug, Hash, Eq, PartialEq, traits::__serde::Deserialize, traits::__serde::Serialize,
+ )]
+ #[serde(crate = "traits::__serde")]
+ #[serde(deny_unknown_fields)]
+ struct BalloonsObjectItem {
+ #[serde(skip_serializing_if = "Option::is_none")]
+ colour: Option<String>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ diameter: Option<i64>,
+ }
+
+ let metric: ObjectMetric<BalloonsObject> = ObjectMetric::new(CommonMetricData {
+ name: "object".into(),
+ category: "test".into(),
+ send_in_pings: vec!["test1".into()],
+ ..Default::default()
+ });
+
+ let balloons = BalloonsObject::from([
+ BalloonsObjectItem {
+ colour: Some("red".to_string()),
+ diameter: Some(5),
+ },
+ BalloonsObjectItem {
+ colour: Some("green".to_string()),
+ diameter: None,
+ },
+ ]);
+ metric.set(balloons);
+
+ let data = metric.test_get_value(None).expect("no object recorded");
+ let expected = json!([
+ { "colour": "red", "diameter": 5 },
+ { "colour": "green" },
+ ]);
+ assert_eq!(expected, data);
+ }
+
+ #[test]
+ fn set_string_api() {
+ let _lock = lock_test();
+ let _t = new_glean(None, true);
+
+ type SimpleArray = Vec<i64>;
+
+ let metric: ObjectMetric<SimpleArray> = ObjectMetric::new(CommonMetricData {
+ name: "object".into(),
+ category: "test".into(),
+ send_in_pings: vec!["test1".into()],
+ ..Default::default()
+ });
+
+ let arr_str = String::from("[1, 2, 3]");
+ metric.set_string(arr_str);
+
+ let data = metric.test_get_value(None).expect("no object recorded");
+ let expected = json!([1, 2, 3]);
+ assert_eq!(expected, data);
+ }
+}
diff --git a/third_party/rust/glean/src/private/ping.rs b/third_party/rust/glean/src/private/ping.rs
index c9c68a10a2..6c126992bc 100644
--- a/third_party/rust/glean/src/private/ping.rs
+++ b/third_party/rust/glean/src/private/ping.rs
@@ -33,6 +33,7 @@ impl PingType {
include_client_id: bool,
send_if_empty: bool,
precise_timestamps: bool,
+ include_info_sections: bool,
reason_codes: Vec<String>,
) -> Self {
let inner = glean_core::metrics::PingType::new(
@@ -40,6 +41,7 @@ impl PingType {
include_client_id,
send_if_empty,
precise_timestamps,
+ include_info_sections,
reason_codes,
);
diff --git a/third_party/rust/glean/src/test.rs b/third_party/rust/glean/src/test.rs
index cb41b49b66..16d6d05447 100644
--- a/third_party/rust/glean/src/test.rs
+++ b/third_party/rust/glean/src/test.rs
@@ -22,22 +22,16 @@ use crate::common_test::{lock_test, new_glean, GLOBAL_APPLICATION_ID};
fn send_a_ping() {
let _lock = lock_test();
- let (s, r) = crossbeam_channel::bounded::<String>(1);
+ let (s, r) = crossbeam_channel::bounded::<net::PingUploadRequest>(1);
- // Define a fake uploader that reports back the submission URL
- // using a crossbeam channel.
+ // Define a fake uploader that reports back the ping upload request.
#[derive(Debug)]
pub struct FakeUploader {
- sender: crossbeam_channel::Sender<String>,
+ sender: crossbeam_channel::Sender<net::PingUploadRequest>,
}
impl net::PingUploader for FakeUploader {
- fn upload(
- &self,
- url: String,
- _body: Vec<u8>,
- _headers: Vec<(String, String)>,
- ) -> net::UploadResult {
- self.sender.send(url).unwrap();
+ fn upload(&self, upload_request: net::PingUploadRequest) -> net::UploadResult {
+ self.sender.send(upload_request).unwrap();
net::UploadResult::http_status(200)
}
}
@@ -55,12 +49,54 @@ fn send_a_ping() {
// Define a new ping and submit it.
const PING_NAME: &str = "test-ping";
- let custom_ping = private::PingType::new(PING_NAME, true, true, true, vec![]);
+ let custom_ping = private::PingType::new(PING_NAME, true, true, true, true, vec![]);
custom_ping.submit(None);
// Wait for the ping to arrive.
- let url = r.recv().unwrap();
- assert!(url.contains(PING_NAME));
+ let upload_request = r.recv().unwrap();
+ assert!(upload_request.body_has_info_sections);
+ assert_eq!(upload_request.ping_name, PING_NAME);
+ assert!(upload_request.url.contains(PING_NAME));
+}
+
+#[test]
+fn send_a_ping_without_info_sections() {
+ let _lock = lock_test();
+
+ let (s, r) = crossbeam_channel::bounded::<net::PingUploadRequest>(1);
+
+ // Define a fake uploader that reports back the ping upload request.
+ #[derive(Debug)]
+ pub struct FakeUploader {
+ sender: crossbeam_channel::Sender<net::PingUploadRequest>,
+ }
+ impl net::PingUploader for FakeUploader {
+ fn upload(&self, upload_request: net::PingUploadRequest) -> net::UploadResult {
+ self.sender.send(upload_request).unwrap();
+ net::UploadResult::http_status(200)
+ }
+ }
+
+ // Create a custom configuration to use a fake uploader.
+ let dir = tempfile::tempdir().unwrap();
+ let tmpname = dir.path().to_path_buf();
+
+ let cfg = ConfigurationBuilder::new(true, tmpname, GLOBAL_APPLICATION_ID)
+ .with_server_endpoint("invalid-test-host")
+ .with_uploader(FakeUploader { sender: s })
+ .build();
+
+ let _t = new_glean(Some(cfg), true);
+
+ // Define a new ping and submit it.
+ const PING_NAME: &str = "noinfo-ping";
+ let custom_ping = private::PingType::new(PING_NAME, true, true, true, false, vec![]);
+ custom_ping.submit(None);
+
+ // Wait for the ping to arrive.
+ let upload_request = r.recv().unwrap();
+ assert!(!upload_request.body_has_info_sections);
+ assert_eq!(upload_request.ping_name, PING_NAME);
}
#[test]
@@ -190,13 +226,8 @@ fn sending_of_foreground_background_pings() {
sender: crossbeam_channel::Sender<String>,
}
impl net::PingUploader for FakeUploader {
- fn upload(
- &self,
- url: String,
- _body: Vec<u8>,
- _headers: Vec<(String, String)>,
- ) -> net::UploadResult {
- self.sender.send(url).unwrap();
+ fn upload(&self, upload_request: net::PingUploadRequest) -> net::UploadResult {
+ self.sender.send(upload_request.url).unwrap();
net::UploadResult::http_status(200)
}
}
@@ -263,13 +294,8 @@ fn sending_of_startup_baseline_ping() {
sender: crossbeam_channel::Sender<String>,
}
impl net::PingUploader for FakeUploader {
- fn upload(
- &self,
- url: String,
- _body: Vec<u8>,
- _headers: Vec<(String, String)>,
- ) -> net::UploadResult {
- self.sender.send(url).unwrap();
+ fn upload(&self, upload_request: net::PingUploadRequest) -> net::UploadResult {
+ self.sender.send(upload_request.url).unwrap();
net::UploadResult::http_status(200)
}
}
@@ -315,13 +341,8 @@ fn no_dirty_baseline_on_clean_shutdowns() {
sender: crossbeam_channel::Sender<String>,
}
impl net::PingUploader for FakeUploader {
- fn upload(
- &self,
- url: String,
- _body: Vec<u8>,
- _headers: Vec<(String, String)>,
- ) -> net::UploadResult {
- self.sender.send(url).unwrap();
+ fn upload(&self, upload_request: net::PingUploadRequest) -> net::UploadResult {
+ self.sender.send(upload_request.url).unwrap();
net::UploadResult::http_status(200)
}
}
@@ -543,12 +564,8 @@ fn ping_collection_must_happen_after_concurrently_scheduled_metrics_recordings()
sender: crossbeam_channel::Sender<(String, JsonValue)>,
}
impl net::PingUploader for FakeUploader {
- fn upload(
- &self,
- url: String,
- body: Vec<u8>,
- _headers: Vec<(String, String)>,
- ) -> net::UploadResult {
+ fn upload(&self, upload_request: net::PingUploadRequest) -> net::UploadResult {
+ let net::PingUploadRequest { body, url, .. } = upload_request;
// Decode the gzipped body.
let mut gzip_decoder = GzDecoder::new(&body[..]);
let mut s = String::with_capacity(body.len());
@@ -577,7 +594,7 @@ fn ping_collection_must_happen_after_concurrently_scheduled_metrics_recordings()
);
let ping_name = "custom_ping_1";
- let ping = private::PingType::new(ping_name, true, false, true, vec![]);
+ let ping = private::PingType::new(ping_name, true, false, true, true, vec![]);
let metric = private::StringMetric::new(CommonMetricData {
name: "string_metric".into(),
category: "telemetry".into(),
@@ -681,13 +698,8 @@ fn sending_deletion_ping_if_disabled_outside_of_run() {
sender: crossbeam_channel::Sender<String>,
}
impl net::PingUploader for FakeUploader {
- fn upload(
- &self,
- url: String,
- _body: Vec<u8>,
- _headers: Vec<(String, String)>,
- ) -> net::UploadResult {
- self.sender.send(url).unwrap();
+ fn upload(&self, upload_request: net::PingUploadRequest) -> net::UploadResult {
+ self.sender.send(upload_request.url).unwrap();
net::UploadResult::http_status(200)
}
}
@@ -731,13 +743,8 @@ fn no_sending_of_deletion_ping_if_unchanged_outside_of_run() {
sender: crossbeam_channel::Sender<String>,
}
impl net::PingUploader for FakeUploader {
- fn upload(
- &self,
- url: String,
- _body: Vec<u8>,
- _headers: Vec<(String, String)>,
- ) -> net::UploadResult {
- self.sender.send(url).unwrap();
+ fn upload(&self, upload_request: net::PingUploadRequest) -> net::UploadResult {
+ self.sender.send(upload_request.url).unwrap();
net::UploadResult::http_status(200)
}
}
@@ -779,12 +786,8 @@ fn deletion_request_ping_contains_experimentation_id() {
sender: crossbeam_channel::Sender<JsonValue>,
}
impl net::PingUploader for FakeUploader {
- fn upload(
- &self,
- _url: String,
- body: Vec<u8>,
- _headers: Vec<(String, String)>,
- ) -> net::UploadResult {
+ fn upload(&self, upload_request: net::PingUploadRequest) -> net::UploadResult {
+ let body = upload_request.body;
let mut gzip_decoder = GzDecoder::new(&body[..]);
let mut body_str = String::with_capacity(body.len());
let data: JsonValue = gzip_decoder
@@ -847,12 +850,8 @@ fn test_sending_of_startup_baseline_ping_with_application_lifetime_metric() {
sender: crossbeam_channel::Sender<(String, JsonValue)>,
}
impl net::PingUploader for FakeUploader {
- fn upload(
- &self,
- url: String,
- body: Vec<u8>,
- _headers: Vec<(String, String)>,
- ) -> net::UploadResult {
+ fn upload(&self, upload_request: net::PingUploadRequest) -> net::UploadResult {
+ let net::PingUploadRequest { url, body, .. } = upload_request;
// Decode the gzipped body.
let mut gzip_decoder = GzDecoder::new(&body[..]);
let mut s = String::with_capacity(body.len());
@@ -932,13 +931,8 @@ fn setting_debug_view_tag_before_initialization_should_not_crash() {
sender: crossbeam_channel::Sender<Vec<(String, String)>>,
}
impl net::PingUploader for FakeUploader {
- fn upload(
- &self,
- _url: String,
- _body: Vec<u8>,
- headers: Vec<(String, String)>,
- ) -> net::UploadResult {
- self.sender.send(headers).unwrap();
+ fn upload(&self, upload_request: net::PingUploadRequest) -> net::UploadResult {
+ self.sender.send(upload_request.headers).unwrap();
net::UploadResult::http_status(200)
}
}
@@ -983,13 +977,8 @@ fn setting_source_tags_before_initialization_should_not_crash() {
sender: crossbeam_channel::Sender<Vec<(String, String)>>,
}
impl net::PingUploader for FakeUploader {
- fn upload(
- &self,
- _url: String,
- _body: Vec<u8>,
- headers: Vec<(String, String)>,
- ) -> net::UploadResult {
- self.sender.send(headers).unwrap();
+ fn upload(&self, upload_request: net::PingUploadRequest) -> net::UploadResult {
+ self.sender.send(upload_request.headers).unwrap();
net::UploadResult::http_status(200)
}
}
@@ -1033,13 +1022,8 @@ fn setting_source_tags_after_initialization_should_not_crash() {
sender: crossbeam_channel::Sender<Vec<(String, String)>>,
}
impl net::PingUploader for FakeUploader {
- fn upload(
- &self,
- _url: String,
- _body: Vec<u8>,
- headers: Vec<(String, String)>,
- ) -> net::UploadResult {
- self.sender.send(headers).unwrap();
+ fn upload(&self, upload_request: net::PingUploadRequest) -> net::UploadResult {
+ self.sender.send(upload_request.headers).unwrap();
net::UploadResult::http_status(200)
}
}
@@ -1097,13 +1081,8 @@ fn flipping_upload_enabled_respects_order_of_events() {
sender: crossbeam_channel::Sender<String>,
}
impl net::PingUploader for FakeUploader {
- fn upload(
- &self,
- url: String,
- _body: Vec<u8>,
- _headers: Vec<(String, String)>,
- ) -> net::UploadResult {
- self.sender.send(url).unwrap();
+ fn upload(&self, upload_request: net::PingUploadRequest) -> net::UploadResult {
+ self.sender.send(upload_request.url).unwrap();
net::UploadResult::http_status(200)
}
}
@@ -1118,7 +1097,7 @@ fn flipping_upload_enabled_respects_order_of_events() {
.build();
// We create a ping and a metric before we initialize Glean
- let sample_ping = PingType::new("sample-ping-1", true, false, true, vec![]);
+ let sample_ping = PingType::new("sample-ping-1", true, false, true, true, vec![]);
let metric = private::StringMetric::new(CommonMetricData {
name: "string_metric".into(),
category: "telemetry".into(),
@@ -1155,19 +1134,14 @@ fn registering_pings_before_init_must_work() {
sender: crossbeam_channel::Sender<String>,
}
impl net::PingUploader for FakeUploader {
- fn upload(
- &self,
- url: String,
- _body: Vec<u8>,
- _headers: Vec<(String, String)>,
- ) -> net::UploadResult {
- self.sender.send(url).unwrap();
+ fn upload(&self, upload_request: net::PingUploadRequest) -> net::UploadResult {
+ self.sender.send(upload_request.url).unwrap();
net::UploadResult::http_status(200)
}
}
// Create a custom ping and attempt its registration.
- let sample_ping = PingType::new("pre-register", true, true, true, vec![]);
+ let sample_ping = PingType::new("pre-register", true, true, true, true, vec![]);
// Create a custom configuration to use a fake uploader.
let dir = tempfile::tempdir().unwrap();
@@ -1201,13 +1175,8 @@ fn test_a_ping_before_submission() {
sender: crossbeam_channel::Sender<String>,
}
impl net::PingUploader for FakeUploader {
- fn upload(
- &self,
- url: String,
- _body: Vec<u8>,
- _headers: Vec<(String, String)>,
- ) -> net::UploadResult {
- self.sender.send(url).unwrap();
+ fn upload(&self, upload_request: net::PingUploadRequest) -> net::UploadResult {
+ self.sender.send(upload_request.url).unwrap();
net::UploadResult::http_status(200)
}
}
@@ -1224,7 +1193,7 @@ fn test_a_ping_before_submission() {
let _t = new_glean(Some(cfg), true);
// Create a custom ping and register it.
- let sample_ping = PingType::new("custom1", true, true, true, vec![]);
+ let sample_ping = PingType::new("custom1", true, true, true, true, vec![]);
let metric = CounterMetric::new(CommonMetricData {
name: "counter_metric".into(),
@@ -1308,12 +1277,7 @@ fn signaling_done() {
counter: Arc<Mutex<HashMap<ThreadId, u32>>>,
}
impl net::PingUploader for FakeUploader {
- fn upload(
- &self,
- _url: String,
- _body: Vec<u8>,
- _headers: Vec<(String, String)>,
- ) -> net::UploadResult {
+ fn upload(&self, _upload_request: net::PingUploadRequest) -> net::UploadResult {
let mut map = self.counter.lock().unwrap();
*map.entry(thread::current().id()).or_insert(0) += 1;
@@ -1346,7 +1310,7 @@ fn signaling_done() {
// Define a new ping and submit it.
const PING_NAME: &str = "test-ping";
- let custom_ping = private::PingType::new(PING_NAME, true, true, true, vec![]);
+ let custom_ping = private::PingType::new(PING_NAME, true, true, true, true, vec![]);
custom_ping.submit(None);
custom_ping.submit(None);
@@ -1385,17 +1349,12 @@ fn configure_ping_throttling() {
done: Arc<std::sync::atomic::AtomicBool>,
}
impl net::PingUploader for FakeUploader {
- fn upload(
- &self,
- url: String,
- _body: Vec<u8>,
- _headers: Vec<(String, String)>,
- ) -> net::UploadResult {
+ fn upload(&self, upload_request: net::PingUploadRequest) -> net::UploadResult {
if self.done.load(std::sync::atomic::Ordering::SeqCst) {
// If we've outlived the test, just lie.
return net::UploadResult::http_status(200);
}
- self.sender.send(url).unwrap();
+ self.sender.send(upload_request.url).unwrap();
net::UploadResult::http_status(200)
}
}
@@ -1422,7 +1381,7 @@ fn configure_ping_throttling() {
// Define a new ping.
const PING_NAME: &str = "test-ping";
- let custom_ping = private::PingType::new(PING_NAME, true, true, true, vec![]);
+ let custom_ping = private::PingType::new(PING_NAME, true, true, true, true, vec![]);
// Submit and receive it `pings_per_interval` times.
for _ in 0..pings_per_interval {
diff --git a/third_party/rust/glean/tests/init_fails.rs b/third_party/rust/glean/tests/init_fails.rs
index def5acc4b9..a0c23ca277 100644
--- a/third_party/rust/glean/tests/init_fails.rs
+++ b/third_party/rust/glean/tests/init_fails.rs
@@ -43,7 +43,7 @@ mod pings {
#[allow(non_upper_case_globals)]
pub static validation: Lazy<PingType> =
- Lazy::new(|| glean::private::PingType::new("validation", true, true, true, vec![]));
+ Lazy::new(|| glean::private::PingType::new("validation", true, true, true, true, vec![]));
}
/// Test scenario: Glean initialization fails.
diff --git a/third_party/rust/glean/tests/never_init.rs b/third_party/rust/glean/tests/never_init.rs
index 3df472ee31..0d0d3768ff 100644
--- a/third_party/rust/glean/tests/never_init.rs
+++ b/third_party/rust/glean/tests/never_init.rs
@@ -39,7 +39,7 @@ mod pings {
#[allow(non_upper_case_globals)]
pub static validation: Lazy<PingType> =
- Lazy::new(|| glean::private::PingType::new("validation", true, true, true, vec![]));
+ Lazy::new(|| glean::private::PingType::new("validation", true, true, true, true, vec![]));
}
/// Test scenario: Glean is never initialized.
diff --git a/third_party/rust/glean/tests/no_time_to_init.rs b/third_party/rust/glean/tests/no_time_to_init.rs
index 763835f2f3..c312b397af 100644
--- a/third_party/rust/glean/tests/no_time_to_init.rs
+++ b/third_party/rust/glean/tests/no_time_to_init.rs
@@ -41,7 +41,7 @@ mod pings {
#[allow(non_upper_case_globals)]
pub static validation: Lazy<PingType> =
- Lazy::new(|| glean::private::PingType::new("validation", true, true, true, vec![]));
+ Lazy::new(|| glean::private::PingType::new("validation", true, true, true, true, vec![]));
}
/// Test scenario: Glean initialization fails.
diff --git a/third_party/rust/glean/tests/schema.rs b/third_party/rust/glean/tests/schema.rs
index 0a1bf4d2e8..01a2108b3c 100644
--- a/third_party/rust/glean/tests/schema.rs
+++ b/third_party/rust/glean/tests/schema.rs
@@ -10,7 +10,7 @@ use glean_core::TextMetric;
use jsonschema_valid::{self, schemas::Draft};
use serde_json::Value;
-use glean::net::UploadResult;
+use glean::net::{PingUploadRequest, UploadResult};
use glean::private::*;
use glean::{
traits, ClientInfoMetrics, CommonMetricData, ConfigurationBuilder, HistogramType, MemoryUnit,
@@ -60,13 +60,8 @@ fn validate_against_schema() {
sender: crossbeam_channel::Sender<Vec<u8>>,
}
impl glean::net::PingUploader for ValidatingUploader {
- fn upload(
- &self,
- _url: String,
- body: Vec<u8>,
- _headers: Vec<(String, String)>,
- ) -> UploadResult {
- self.sender.send(body).unwrap();
+ fn upload(&self, ping_request: PingUploadRequest) -> UploadResult {
+ self.sender.send(ping_request.body).unwrap();
UploadResult::http_status(200)
}
}
@@ -176,7 +171,7 @@ fn validate_against_schema() {
text_metric.set("loooooong text".repeat(100));
// Define a new ping and submit it.
- let custom_ping = glean::private::PingType::new(PING_NAME, true, true, true, vec![]);
+ let custom_ping = glean::private::PingType::new(PING_NAME, true, true, true, true, vec![]);
custom_ping.submit(None);
// Wait for the ping to arrive.
diff --git a/third_party/rust/glean/tests/simple.rs b/third_party/rust/glean/tests/simple.rs
index 3685d44faa..3baa4df14e 100644
--- a/third_party/rust/glean/tests/simple.rs
+++ b/third_party/rust/glean/tests/simple.rs
@@ -41,7 +41,7 @@ mod pings {
#[allow(non_upper_case_globals)]
pub static validation: Lazy<PingType> =
- Lazy::new(|| glean::private::PingType::new("validation", true, true, true, vec![]));
+ Lazy::new(|| glean::private::PingType::new("validation", true, true, true, true, vec![]));
}
/// Test scenario: A clean run
diff --git a/third_party/rust/glean/tests/upload_timing.rs b/third_party/rust/glean/tests/upload_timing.rs
index 9e77fc3eb5..ba0eee3402 100644
--- a/third_party/rust/glean/tests/upload_timing.rs
+++ b/third_party/rust/glean/tests/upload_timing.rs
@@ -97,7 +97,7 @@ mod pings {
#[allow(non_upper_case_globals)]
pub static validation: Lazy<PingType> =
- Lazy::new(|| glean::private::PingType::new("validation", true, true, true, vec![]));
+ Lazy::new(|| glean::private::PingType::new("validation", true, true, true, true, vec![]));
}
// Define a fake uploader that sleeps.
@@ -108,13 +108,9 @@ struct FakeUploader {
}
impl net::PingUploader for FakeUploader {
- fn upload(
- &self,
- _url: String,
- body: Vec<u8>,
- _headers: Vec<(String, String)>,
- ) -> net::UploadResult {
+ fn upload(&self, upload_request: net::PingUploadRequest) -> net::UploadResult {
let calls = self.calls.fetch_add(1, Ordering::SeqCst);
+ let body = upload_request.body;
let decode = |body: Vec<u8>| {
let mut gzip_decoder = GzDecoder::new(&body[..]);
let mut s = String::with_capacity(body.len());
diff --git a/third_party/rust/glslopt/.cargo-checksum.json b/third_party/rust/glslopt/.cargo-checksum.json
index 4e4e911018..a09c03e66a 100644
--- a/third_party/rust/glslopt/.cargo-checksum.json
+++ b/third_party/rust/glslopt/.cargo-checksum.json
@@ -1 +1 @@
-{"files":{"Cargo.toml":"5e6232fe2ab7866ef301c1dacaa3135a02e2fd69744c9372eceffbc3c1fd1c32","README.md":"4468e08c64c19977707d792bfab0080e35ff927b64990eab77873f8ba056ba1c","build.rs":"6a64610018701781af182c418a4355c9ac5d99d000be9457f0e38a7dadf7542a","glsl-optimizer/CMakeLists.txt":"42ce94744e82ffa000da8b64d81fc140e293b9f5da7dd4cf6b49e7404a2448d9","glsl-optimizer/README.md":"b18eef11a92d267d88a937b1154f7670ee433c730b102fdf7e2da0b02722b146","glsl-optimizer/contrib/glslopt/Main.cpp":"14ba213210c62e234b8d9b0052105fed28eedd83d535ebe85acc10bda7322dd4","glsl-optimizer/contrib/glslopt/Readme":"65d2a6f1aa1dc61e903e090cdade027abad33e02e7c9c81e07dc80508acadec4","glsl-optimizer/generateParsers.sh":"878a97db5d3b69eb3b4c3a95780763b373cfcc0c02e0b28894f162dbbd1b8848","glsl-optimizer/include/GL/gl.h":"1989b51365b6d7d0c48ff6e8b181ef75e2cdf71bfb1626b1cc4362e2f54854a3","glsl-optimizer/include/GL/glext.h":"2ac3681045a35a2194a81a960cad395c04bef1c8a20ef46b799fb24af3ec5f70","glsl-optimizer/include/KHR/khrplatform.h":"1448141a0c054d7f46edfb63f4fe6c203acf9591974049481c32442fb03fd6ed","glsl-optimizer/include/c11/threads.h":"56e9e592b28df19f0db432125223cb3eb5c0c1f960c22db96a15692e14776337","glsl-optimizer/include/c11/threads_posix.h":"f8ad2b69fa472e332b50572c1b2dcc1c8a0fa783a1199aad245398d3df421b4b","glsl-optimizer/include/c11/threads_win32.h":"95bf19d7fc14d328a016889afd583e4c49c050a93bcfb114bd2e9130a4532488","glsl-optimizer/include/c11_compat.h":"103fedb48f658d36cb416c9c9e5ea4d70dff181aab551fcb1028107d098ffa3e","glsl-optimizer/include/c99_alloca.h":"96ffde34c6cabd17e41df0ea8b79b034ce8f406a60ef58fe8f068af406d8b194","glsl-optimizer/include/c99_compat.h":"aafad02f1ea90a7857636913ea21617a0fcd6197256dcfc6dd97bb3410ba892e","glsl-optimizer/include/c99_math.h":"9730d800899f1e3a605f58e19451cd016385024a05a5300e1ed9c7aeeb1c3463","glsl-optimizer/include/no_extern_c.h":"40069dbb6dd2843658d442f926e609c7799b9c296046a90b62b570774fd618f5","glsl-optimizer/license.txt":"e26a745226f4a46b3ca00ffbe8be18507362189a2863d04b4f563ba176a9a836","glsl-optimizer/src/compiler/builtin_type_macros.h":"5b4fc4d4da7b07f997b6eb569e37db79fa0735286575ef1fab08d419e76776ff","glsl-optimizer/src/compiler/glsl/README":"e7d408b621c1b605857c4cab63902f615edb06b530142b91ac040808df6e22f7","glsl-optimizer/src/compiler/glsl/TODO":"dd3b7a098e6f9c85ca8c99ce6dea49d65bb75d4cea243b917f29e4ad2c974603","glsl-optimizer/src/compiler/glsl/ast.h":"3e68ff374350c49211a9931f7f55a485d8d89fc4b21caaffbf6655009ad95bf8","glsl-optimizer/src/compiler/glsl/ast_array_index.cpp":"92b4d501f33e0544c00d14e4f8837753afd916c2b42e076ccc95c9e8fc37ba94","glsl-optimizer/src/compiler/glsl/ast_expr.cpp":"afd712a7b1beb2b633888f4a0911b0a8e4ae5eb5ab9c1e3f247d518cdaaa56d6","glsl-optimizer/src/compiler/glsl/ast_function.cpp":"74f4fbd490e366b37f4715168bb3465ecd9334d4130942f75dcc8e80e8e7f027","glsl-optimizer/src/compiler/glsl/ast_to_hir.cpp":"d0f798eb09271d41d068b9e7b18220d37f1ed0083300ab51eba30989698fe23d","glsl-optimizer/src/compiler/glsl/ast_type.cpp":"8eb790b24b26dfb72bdc333744b566c26d8464c5d47d20eae659461f5c4899f7","glsl-optimizer/src/compiler/glsl/builtin_functions.cpp":"454189d643c220fcb49116ee5c8a34f7b349aa67564040deb8607f6a41a15e70","glsl-optimizer/src/compiler/glsl/builtin_functions.h":"a37cad7ed09b522c5b8bec7b80115a36846e7ba6e0874a2a858e32f7f202c665","glsl-optimizer/src/compiler/glsl/builtin_int64.h":"619def6f3aebf180da3944ef08f159ab12a58b24767e41d8b985ac37ded54d62","glsl-optimizer/src/compiler/glsl/builtin_types.cpp":"afec060b62d6f3b00bfbf94e9fa5f96341ce096c128d1eef322791e6ed9cea4d","glsl-optimizer/src/compiler/glsl/builtin_variables.cpp":"6563bfb1345cbca4c77e00eef09ad152f3e1dc271d246a08c5ce9e1f4ce4250a","glsl-optimizer/src/compiler/glsl/float64.glsl":"1072fd888be48c2a7a5117cd2d92a65f034965a66375f598bb856bff5d7be766","glsl-optimizer/src/compiler/glsl/generate_ir.cpp":"e5f0175370a0d07f93c48d3f0f1b8233d12c64a7b02de02dcc753ef7b398ef0f","glsl-optimizer/src/compiler/glsl/glcpp/README":"a0332a1b221d047e9cce5181a64d4ac4056046fd878360ec8ae3a7b1e062bcff","glsl-optimizer/src/compiler/glsl/glcpp/glcpp-lex.c":"2d179879b1ffe84f58875eee5b0c19b6bae9c973b0c48e6bcd99978f2f501c80","glsl-optimizer/src/compiler/glsl/glcpp/glcpp-lex.l":"e4c5744c837200dafd7c15a912d13f650308ea552454d4fa67271bc0a5bde118","glsl-optimizer/src/compiler/glsl/glcpp/glcpp-parse.c":"03494f9ce1cb82260506e2559e73a3eeb622c4bd51b65eaa0a2c3351862bd4c8","glsl-optimizer/src/compiler/glsl/glcpp/glcpp-parse.h":"264d9a18421cde255ce34a0a62b3d8e73465359f0d167e64aa3973062aae5bdd","glsl-optimizer/src/compiler/glsl/glcpp/glcpp-parse.y":"fafb66e3a8f149d19e085f18a4273ba6d4c11af9e9a01d665cc784dddf97b79f","glsl-optimizer/src/compiler/glsl/glcpp/glcpp.c":"37ed294403c2abfd17fd999d1ae8d11b170e5e9c878979fefac74a31195c96b0","glsl-optimizer/src/compiler/glsl/glcpp/glcpp.h":"85ac8b444bcbd0822b66448a1da407b6ae5467b649f5afaf5c58325bd7569468","glsl-optimizer/src/compiler/glsl/glcpp/pp.c":"a52d94f1bcb3fb2747a95709c4a77c25de7eea8354d2b83bb18efd96976a4473","glsl-optimizer/src/compiler/glsl/glcpp/pp_standalone_scaffolding.c":"d11aeb3acfe966d1b78f1ee49804093f2434214c41391d139ffcb67b69dc9862","glsl-optimizer/src/compiler/glsl/glcpp/pp_standalone_scaffolding.h":"abbf1f36ec5a92d035bfbb841b9452287d147616e56373cdbee1c0e55af46406","glsl-optimizer/src/compiler/glsl/glsl_lexer.cpp":"272b9fc1383d72b81bfc03fa11fdf82270ed91a294e523f9ce2b4554bd3effa9","glsl-optimizer/src/compiler/glsl/glsl_lexer.ll":"2b57d9f9eb830c3d7961d4533048a158ee6f458c8d05c65bea7b7cfbc36e4458","glsl-optimizer/src/compiler/glsl/glsl_optimizer.cpp":"f8095d20629d0af70be930b0612e169edb274551a1d25a3cd1bf9995a11ce2e8","glsl-optimizer/src/compiler/glsl/glsl_optimizer.h":"22e843b4ec53ba5f6cd85ca5f7bad33922dca8061b19fb512d46f1caca8d4757","glsl-optimizer/src/compiler/glsl/glsl_parser.cpp":"126baf368d525aba301854e3d91ba60b5aee32e1102376af71416f32cb95ec48","glsl-optimizer/src/compiler/glsl/glsl_parser.h":"2ea9a50716098a8f7bef782d2a030d757b68da73afb01b4d4940d3e8381d44e8","glsl-optimizer/src/compiler/glsl/glsl_parser.yy":"6b1fd1576b29fce005dff744a6dbd0219e4c695c361d61864e1f3a8d6fa6b764","glsl-optimizer/src/compiler/glsl/glsl_parser_extras.cpp":"aad64b5b66467da650091430681e8c6a820cf3cadc4db3c160bf2f15875390ae","glsl-optimizer/src/compiler/glsl/glsl_parser_extras.h":"71fd0e92bbdb193dfb067d7bfdb1200d77392be2fbd0cbfc9ca89d1bb4c7e741","glsl-optimizer/src/compiler/glsl/glsl_symbol_table.cpp":"6660fb83c0ddddbbd64581d46ccfdb9c84bfaa99d13348c289e6442ab00df046","glsl-optimizer/src/compiler/glsl/glsl_symbol_table.h":"24682b8304e0ea3f6318ddb8c859686bd1faee23cd0511d1760977ae975d41bf","glsl-optimizer/src/compiler/glsl/hir_field_selection.cpp":"72a039b0fcab4161788def9e4bedac7ac06a20d8e13146529c6d246bd5202afd","glsl-optimizer/src/compiler/glsl/int64.glsl":"303dbe95dde44b91aee3e38b115b92028400d6a92f9268975d607471984e13eb","glsl-optimizer/src/compiler/glsl/ir.cpp":"2b4741cce90b5d4abff5d719c7324e2693c67294d4d99736cb241554adb281bc","glsl-optimizer/src/compiler/glsl/ir.h":"990b1c74447c4eb4835353ccb0ed9aea644f97fc1129ef1739cd935075d85d2e","glsl-optimizer/src/compiler/glsl/ir_array_refcount.cpp":"8cdc1cffe01e42e0566fa2193a75f789628e8025ad1b82f0ee6f204451b7f9f7","glsl-optimizer/src/compiler/glsl/ir_array_refcount.h":"75f06ec81342b379096ca52e1dc0fd5f19a11ff8e9b58203c20628179d644c12","glsl-optimizer/src/compiler/glsl/ir_basic_block.cpp":"1e2920b1c0ecb08424c745c558f84d0d7e44b74585cf2cc2265dc4dfede3fa2f","glsl-optimizer/src/compiler/glsl/ir_basic_block.h":"81be7da0fc0ee547cd13ec60c1fcd7d3ce3d70d7e5e988f01a3b43a827acdf05","glsl-optimizer/src/compiler/glsl/ir_builder.cpp":"daba29c5a1efdd5a9754f420eb3e2ebdf73485273497f40d4863dadeddb23c0d","glsl-optimizer/src/compiler/glsl/ir_builder.h":"2822e74dd3f6e3df8b300af27d5b11ea2dd99d0e5e7ca809b7bbcce9833c483c","glsl-optimizer/src/compiler/glsl/ir_builder_print_visitor.cpp":"8c6df5abf2fe313363f285f171c19ca6c8ee4f3bc2ed79d33c0c88cc8be45c48","glsl-optimizer/src/compiler/glsl/ir_builder_print_visitor.h":"799852adc3a0e54d04080655e7cebfa0d3bf5b6ffed5d8414f141380665d4db7","glsl-optimizer/src/compiler/glsl/ir_clone.cpp":"d897a4e1f5bbec4a6a2f15044c1be9a4d13899c73be77335b041049a4589aa5d","glsl-optimizer/src/compiler/glsl/ir_constant_expression.cpp":"78bd87ddb09db67f6c499067728d72aef4f16aa02721a99a4b769d1e0cfa9010","glsl-optimizer/src/compiler/glsl/ir_equals.cpp":"bca28533a6310b0fc152b56d80872368f1510dc62ed6e8ac199b9ffa7fac02e7","glsl-optimizer/src/compiler/glsl/ir_expression_flattening.cpp":"7e918d4e1f237eca01396004015865ce345afe32a876c9dbc6728576a1a7eae4","glsl-optimizer/src/compiler/glsl/ir_expression_flattening.h":"f45b66aa9497520e7e08e612d24b308477c34477fbd963ee9320eac664957f16","glsl-optimizer/src/compiler/glsl/ir_expression_operation.h":"cc9f10727dbd26cac506804f51456302c702650f9eeb59054a7e1575d5cf6687","glsl-optimizer/src/compiler/glsl/ir_expression_operation.py":"7b86c96021b9fbe165957f4ecb0b612fefcde1c2cf3c6d75e3cdb22e369216ba","glsl-optimizer/src/compiler/glsl/ir_expression_operation_constant.h":"9ad3346416392e3efa11e12ecf2feca7453c5253d241eb96c91dfb85d4f2b971","glsl-optimizer/src/compiler/glsl/ir_expression_operation_strings.h":"a6826daf496a8b9e89885bc2a161ac3445d501b23c6e0ac33e2c01b506b273c8","glsl-optimizer/src/compiler/glsl/ir_function.cpp":"7537365fc0fbe4b37a26b9a2146cc64d3e9a774d60eab63b65002ad165ae8fc7","glsl-optimizer/src/compiler/glsl/ir_function_can_inline.cpp":"faddbf112187a048d502716a3fb82570a322299ba2a3abd79388382c82040bfc","glsl-optimizer/src/compiler/glsl/ir_function_detect_recursion.cpp":"9176973eaf5c0a984701f953bb7a80f37dca43d59b5bce50fc69b3f02f2902d7","glsl-optimizer/src/compiler/glsl/ir_function_inlining.h":"9739493f99c489987d650762fccdd3fb3d432f6481d67f6c799176685bd59632","glsl-optimizer/src/compiler/glsl/ir_hierarchical_visitor.cpp":"3725861fbe2b98e0617f52d3b14cf6d3b25fb5ec00f5ef5d308b03642f592767","glsl-optimizer/src/compiler/glsl/ir_hierarchical_visitor.h":"e0560210e966c0c31e4ca843e80ea154e64db5a444b8c2df845b6ba5b3a43fc1","glsl-optimizer/src/compiler/glsl/ir_hv_accept.cpp":"caf7ce2cd9494aadd3c58bcf77f29de58368dc9e347a362bbf37f8bda9509b80","glsl-optimizer/src/compiler/glsl/ir_optimization.h":"8b3dcfc7f9e96b21a8dd47a0040d90be483a9e67a2cdce3a697188fb758d4630","glsl-optimizer/src/compiler/glsl/ir_print_glsl_visitor.cpp":"f8e34a983452be0dcb5a695e9c8e895eead24f9e540992a8afe510ae85da4c4c","glsl-optimizer/src/compiler/glsl/ir_print_glsl_visitor.h":"1ad1bd3efd1ace39051c13f904c05fd80425d329444f9a8d47fd6d948faf46e0","glsl-optimizer/src/compiler/glsl/ir_print_visitor.cpp":"643f5a68aae3fb37267fd793f1216d1cfdeb2c09338c26b1f30e4c6deaef4de5","glsl-optimizer/src/compiler/glsl/ir_print_visitor.h":"4573eb93268a2654c14b505253dd651e2695d43dc745904d824da18305269b95","glsl-optimizer/src/compiler/glsl/ir_reader.cpp":"06bfba802c8354e5a8b2334b6d78d6297de18235bedd3f8fbb382c89870b02f2","glsl-optimizer/src/compiler/glsl/ir_reader.h":"63e3f7f1597936a7011d5b520e171b197bf82bee6c1560d822c3edf5aaa6f9e9","glsl-optimizer/src/compiler/glsl/ir_rvalue_visitor.cpp":"84b5c5d746555adca85759c2912fe48010232b7c1c0bd2cf03bd04067a85e66f","glsl-optimizer/src/compiler/glsl/ir_rvalue_visitor.h":"fd8c561b71085d3211fff85ed514fecb299d8ce19a04bc063419a55b6d840525","glsl-optimizer/src/compiler/glsl/ir_set_program_inouts.cpp":"ab9f115ce9e7f312d9c7978340ced0dc4ae6d13a80e08442ba9709d11d50cae5","glsl-optimizer/src/compiler/glsl/ir_uniform.h":"683ae6896b1a08470c090be5f822fc31cd434eab9216e954b9bba24a46975109","glsl-optimizer/src/compiler/glsl/ir_unused_structs.cpp":"9c1620c45f2fc071fe5ed828472040b14c5f42effe06aa0e3b8352c95ef78786","glsl-optimizer/src/compiler/glsl/ir_unused_structs.h":"13387b49c23093575276b25b9dfd31fedd8f131c5c4f3128ab04cf03e15b5295","glsl-optimizer/src/compiler/glsl/ir_validate.cpp":"6b232be5999a86ea278f4f15b2832d76843246509118d924243055a3b9b0299f","glsl-optimizer/src/compiler/glsl/ir_variable_refcount.cpp":"2764a3cad937d53f36db7447c3a5b98b04bf153acf81074d971857fc5bca460d","glsl-optimizer/src/compiler/glsl/ir_variable_refcount.h":"b0668e3eb1501ef65e38fe12830742ecb3d28e6039f30e366c8924efc29b4a39","glsl-optimizer/src/compiler/glsl/ir_visitor.h":"f21b3534c3d66d5fb707d1581fece7e1eb043523afbaedf89918cfb031c6df94","glsl-optimizer/src/compiler/glsl/link_atomics.cpp":"360f0209e11f367ba358223597b0a118bae095bff16337cf03f1fb89c5b80ca6","glsl-optimizer/src/compiler/glsl/link_functions.cpp":"de7895da8aa33a1e3c2c1eb2fdaf267ab5d1fbfdb79ae2e67f95211e946e294c","glsl-optimizer/src/compiler/glsl/link_interface_blocks.cpp":"1926cfa73810704eb19b916c1b2cdb9321155e2f98b2a0a57c7c3c6e960540cd","glsl-optimizer/src/compiler/glsl/link_uniform_block_active_visitor.cpp":"1e14e06ca3b2c1089cfba2e8eaf0c1f373d9d6374b6082f320962dd71ae09611","glsl-optimizer/src/compiler/glsl/link_uniform_block_active_visitor.h":"fd58c155af645295bb6aec08797889de586f4d919731de2bce57e8dce59bb048","glsl-optimizer/src/compiler/glsl/link_uniform_blocks.cpp":"09589f49776dce32e6c4044937de7e0c839a9754ad31960148f8f9e010658997","glsl-optimizer/src/compiler/glsl/link_uniform_initializers.cpp":"bf98e08c12db466acf9623cbeb8fa8e3b4002512722e7a6521287f558a099f37","glsl-optimizer/src/compiler/glsl/link_uniforms.cpp":"84bad5b1377362cecf259b05124239be5220b03ce1c0c61b59bd9a47e4379af2","glsl-optimizer/src/compiler/glsl/link_varyings.cpp":"a5f1a53e7c80d635515fe808ff223d89fef1767abb0f2b7aa28fa6773dca353f","glsl-optimizer/src/compiler/glsl/link_varyings.h":"b9dbe018f038df69763df2e928742ce81bbc6e3aaba26f50621e30a6d9aa6220","glsl-optimizer/src/compiler/glsl/linker.cpp":"40b1ecd5d4f6c7f13d5a87ce390561a51fdf6f3fcd9b2197b9c88b03a773ba94","glsl-optimizer/src/compiler/glsl/linker.h":"ecf94b4ad75ef461c27c557fda4bd25f34c91930822b8e1d729ec84520d4a049","glsl-optimizer/src/compiler/glsl/linker_util.cpp":"1663ad88e2a369305659aeeffaedb5bd752cf76340a2ba5797fc0bf600633cf9","glsl-optimizer/src/compiler/glsl/linker_util.h":"6db788daf9c8e87ae2764b61a8b37ebe419e69c1b82ddee01986e37c978c6993","glsl-optimizer/src/compiler/glsl/list.h":"b1f46ce0e552fe7c45b2a19408a9d97662e23e4b182ab335491c26f8cf25886f","glsl-optimizer/src/compiler/glsl/loop_analysis.cpp":"57ecd573477c68091c7cc99537faa7139a8f395935e3d4f10144cefdefb5a611","glsl-optimizer/src/compiler/glsl/loop_analysis.h":"a85f045a038ee5b5176063e85d7988865862c44ab0580f771b993a042d0b69cc","glsl-optimizer/src/compiler/glsl/loop_unroll.cpp":"bd4292ea2809f5a669bcb76ceaa1ac365772dcd638c579c3ed10275214901a54","glsl-optimizer/src/compiler/glsl/lower_blend_equation_advanced.cpp":"8cfbef140d9c4b4d2f57bfa05c9c374d31a121d0f87afce94333f049023b654a","glsl-optimizer/src/compiler/glsl/lower_buffer_access.cpp":"1ae221c3c7a95aeb867207e7a742be635f91b406c157747bfd6ddf10274d97fb","glsl-optimizer/src/compiler/glsl/lower_buffer_access.h":"807886953a576a323591798cbca5e2df24295ea893b28affd8ffb5926cebaa04","glsl-optimizer/src/compiler/glsl/lower_builtins.cpp":"4d81afc32cf58e1481fcb5e42888ab93dbe6820310a20ff7a9982b77b2152d9b","glsl-optimizer/src/compiler/glsl/lower_const_arrays_to_uniforms.cpp":"608403f0eeeedf21cfcd3014116e0f44e28cbdf6c4c32aac7e613e64e30205e1","glsl-optimizer/src/compiler/glsl/lower_cs_derived.cpp":"179905cd47a294122adeb5b0abfed6f2f67782dcde21b544d1ee2c1985154e66","glsl-optimizer/src/compiler/glsl/lower_discard.cpp":"3b361b2db0004d544d64611cb50d5a6e364cf6c5f2e60c449085d7d753dd7fb0","glsl-optimizer/src/compiler/glsl/lower_discard_flow.cpp":"f5c29b6a27690bb5c91f196d1a1cf9f6be4f1025292311fe2dac561ce6774dee","glsl-optimizer/src/compiler/glsl/lower_distance.cpp":"a118c85493d5d22b2c059a930c51a5854896d4b1dade76598eaa985e5a3dff8c","glsl-optimizer/src/compiler/glsl/lower_if_to_cond_assign.cpp":"469e617757fd1728709cce021aac5c8da05ee503bf5366977bdc4ef7a6d83950","glsl-optimizer/src/compiler/glsl/lower_instructions.cpp":"defd043e8576437c1ef63c7d0bf5f828068bbfb5fdbec16457a9c191a1e9242d","glsl-optimizer/src/compiler/glsl/lower_int64.cpp":"d1ed41196880dd53c7b13e2782f9423f8442bf1d46186e8be92b1b66218a83ee","glsl-optimizer/src/compiler/glsl/lower_jumps.cpp":"34de7b493f281589fb0c2c0f6e885d0a0fabbe7a4e97a73de374dd714777a58c","glsl-optimizer/src/compiler/glsl/lower_mat_op_to_vec.cpp":"dff7a308edc4846c348ed4225c6699a9c75abac68d88f41f85954276552779f4","glsl-optimizer/src/compiler/glsl/lower_named_interface_blocks.cpp":"16063ac127bff75a68272070ab11c21c25101edbff62b4c68f4983b4cd941af0","glsl-optimizer/src/compiler/glsl/lower_offset_array.cpp":"3b00773399135aea85746a5a68b96ef000bc6841be1a2c8e6f25c516628b0949","glsl-optimizer/src/compiler/glsl/lower_output_reads.cpp":"a0fc9975d5aa1617e21fc6c353659a9802da9e83779a3eef4ec584f74b4dadc5","glsl-optimizer/src/compiler/glsl/lower_packed_varyings.cpp":"7550099d4ae123d71541c2fc88bc04fbfe9271ec75d7e210987d1c8cac3cf3ea","glsl-optimizer/src/compiler/glsl/lower_packing_builtins.cpp":"79a13d161fe505a410ab948d92769395708693ec888153630fa240e5b97e356f","glsl-optimizer/src/compiler/glsl/lower_precision.cpp":"f82a185b879872b977a1787d8061b9a80bc4cf8db1b970db6efba2ad9cc20fa2","glsl-optimizer/src/compiler/glsl/lower_shared_reference.cpp":"ea2dccf50a83bc19391bf6b7ab6aa53c0005f427af4066d25140340af9a4beef","glsl-optimizer/src/compiler/glsl/lower_subroutine.cpp":"f69fa53650eeb6f2944fce4d36a6e0a423e6705f3a3bd3389c7fadb83cfc8802","glsl-optimizer/src/compiler/glsl/lower_tess_level.cpp":"b196c9d424c0569f3e85d75c2d125af21566cb113d69036db87c0990703e0fa7","glsl-optimizer/src/compiler/glsl/lower_texture_projection.cpp":"4d247f244272adc8250fd888d8d932a140dd5de4d1efc7a58492c3c2b8291527","glsl-optimizer/src/compiler/glsl/lower_ubo_reference.cpp":"89bdbc6c1669230c644c0857db1ce2781ec61d349ecd08c7914146e1f4750a4a","glsl-optimizer/src/compiler/glsl/lower_variable_index_to_cond_assign.cpp":"fce930f29ac9405b297d1f749d68f59506b89c70b4ee1b1ab8cf49a34cc71ecf","glsl-optimizer/src/compiler/glsl/lower_vec_index_to_cond_assign.cpp":"3c67d851a11a55fad1c49a550f3a0cfe50892d33a3f238ce266cd829eba510a8","glsl-optimizer/src/compiler/glsl/lower_vec_index_to_swizzle.cpp":"f5ec666b73e1415cbab32519a53605ed385f3b03e889560373dbce69dda5000e","glsl-optimizer/src/compiler/glsl/lower_vector.cpp":"f7c13f5572ebe09b6a71553133b2cf003cd4b77b9657600672ee3b21bf890725","glsl-optimizer/src/compiler/glsl/lower_vector_derefs.cpp":"b05793da6dd620a531b43df5af8b2ecbc37b9db0c88910f5724ea10bcd057e19","glsl-optimizer/src/compiler/glsl/lower_vector_insert.cpp":"fee772ec17eea5e86a529bf9c5fa2ee0d29a5982bb75ebc6d68ed36cd19aa299","glsl-optimizer/src/compiler/glsl/lower_vertex_id.cpp":"690e8715182e03fead5cc5a35251fb4f41b357e4c71a1dfbc4bd7be19862b56d","glsl-optimizer/src/compiler/glsl/lower_xfb_varying.cpp":"58c0e8b270e4bbde54250be03cdb2f36966bcafb785372ad2e2b786835df7f9f","glsl-optimizer/src/compiler/glsl/main.cpp":"ae5e88abbbc8a12f769e1296bad938b9d7398cc6da0d3d0caeceeeb876536850","glsl-optimizer/src/compiler/glsl/opt_add_neg_to_sub.h":"f5054944bfd068810629080d0ea11df78b3f57a8f86df75e13ca50157ad1964d","glsl-optimizer/src/compiler/glsl/opt_algebraic.cpp":"25f45b20e1972ee8c789177a1aeda6e4286c25db2eae3a43ff83029ae64969c0","glsl-optimizer/src/compiler/glsl/opt_array_splitting.cpp":"19d3ce0e815438f4df9ab2890e767b03a4f3f191b53bb30c0217cf2ae6a95430","glsl-optimizer/src/compiler/glsl/opt_conditional_discard.cpp":"0e44e0e126711a3725c1f3a2aa65ff03c381fed08680ffc30101aae60f716c4e","glsl-optimizer/src/compiler/glsl/opt_constant_folding.cpp":"a088d04d9b45f9e55e235835648f614c89b7803c03a6d4f6a6d1a6bc1f0228bd","glsl-optimizer/src/compiler/glsl/opt_constant_propagation.cpp":"8a9440d77ecd6dcf13e683cbb99943aab6311c8fd4b5f6a9189a8d4f270746f4","glsl-optimizer/src/compiler/glsl/opt_constant_variable.cpp":"63d3ccd4dd09f19c9cf1a2f51592111bed41284504f29f3c0de4cadebc439a37","glsl-optimizer/src/compiler/glsl/opt_copy_propagation_elements.cpp":"ffa0f50863995e0d2e31f55a52e82319edc71e520987bebd7f7e561ea331c64b","glsl-optimizer/src/compiler/glsl/opt_dead_builtin_variables.cpp":"84e8747b948232f01dd56b428b9315f96f9511f605f240119fc446fae28981a9","glsl-optimizer/src/compiler/glsl/opt_dead_builtin_varyings.cpp":"761523e88f5b3ba785170f4d7205e94fa99acb7e74d29efbe40e1c010e1dbdb3","glsl-optimizer/src/compiler/glsl/opt_dead_code.cpp":"fd1ba2da7337d4e5dad17f5c2d73d9cc8880305f423e85d64cf94553588fa401","glsl-optimizer/src/compiler/glsl/opt_dead_code_local.cpp":"969a598b4df322baf222258a66cd64a326ea20e5b3125be9d8d1771f522c69e0","glsl-optimizer/src/compiler/glsl/opt_dead_functions.cpp":"774cae6536d02edf26e996a2a895e1f62d5098f16dc96b44798b4fc731a9a95f","glsl-optimizer/src/compiler/glsl/opt_flatten_nested_if_blocks.cpp":"3696a5c55f02e20056e085bc2714f73ac992f221b6f3387d655068e86b512046","glsl-optimizer/src/compiler/glsl/opt_flip_matrices.cpp":"44f0fe05b49329667671f88c96dc86ab3fe1459ff7b87f2b2d88de2d49829f9f","glsl-optimizer/src/compiler/glsl/opt_function_inlining.cpp":"fb56a33c90419a01676b57cbd91d0674a54cca40e6defaacc88dd33facebc131","glsl-optimizer/src/compiler/glsl/opt_if_simplification.cpp":"ac406eb35e379c357641d6c5749f50c65961455924d3dc884e2b90046fa92c5c","glsl-optimizer/src/compiler/glsl/opt_minmax.cpp":"8abd59d3b14ef60ff14a9c69660e6945f5cf10b97edb4afebe56be3f81d96316","glsl-optimizer/src/compiler/glsl/opt_rebalance_tree.cpp":"8bb6329dc0f299042368fc81934c2df019b45ab9f7aa0415d4e57b8d1ff98c9f","glsl-optimizer/src/compiler/glsl/opt_redundant_jumps.cpp":"222c73e2ac7a938ebb6428cc6c780c908ff6156d8ff935b04fed93a48fc10496","glsl-optimizer/src/compiler/glsl/opt_structure_splitting.cpp":"2edc79cc13f3177934e0443ad62f5976a1991f01f86ea303a803434849b13a47","glsl-optimizer/src/compiler/glsl/opt_swizzle.cpp":"015d0abddfe507f67c4b96c82988d861d018ededf7bf055e2bcbe9ea92da694e","glsl-optimizer/src/compiler/glsl/opt_tree_grafting.cpp":"46d28ac983ea244a4315bdc0e8892979ec4d1f9b9a96ac8a8a08006d9bc5e878","glsl-optimizer/src/compiler/glsl/opt_vectorize.cpp":"d80ee43bb97d9f016fb9c5e1e06f5b2afa569811f368ba067be794ec11d085fb","glsl-optimizer/src/compiler/glsl/program.h":"2982447e2abd35371e273ad87951722782a8b21c08294f67c39d987da1e1c55f","glsl-optimizer/src/compiler/glsl/propagate_invariance.cpp":"080943e21baa32494723a2eefb185915d2daae1f46d6df420145c5ad6857e119","glsl-optimizer/src/compiler/glsl/s_expression.cpp":"1ced972bc6ecc8eab4116ea71fb0212ab9ae5bcc0be3b47aa5d9d903566b3af1","glsl-optimizer/src/compiler/glsl/s_expression.h":"65b847e30e22a809b57d0bc70243049c99d9c6318803c5b8d0826aba55dc217e","glsl-optimizer/src/compiler/glsl/serialize.cpp":"be0eb4251348a9d921acb839a5c48c6023a2e9d116d602bb0432787ab623655d","glsl-optimizer/src/compiler/glsl/serialize.h":"57425732eba1233d928e5f07f88b623ce65af46b3bb034bf147f0a4b7f94f9a1","glsl-optimizer/src/compiler/glsl/shader_cache.cpp":"e0c5c433f2df3fccdf1d61281bfcb0ee5633433339b97c697d64db99611cbaaf","glsl-optimizer/src/compiler/glsl/shader_cache.h":"9217164d8d7f54aca0fe5922c7187095a6ae0cb703b196b79805aeef07a7e697","glsl-optimizer/src/compiler/glsl/standalone.cpp":"8e6c416a14d631261917a5fe4cc91880c287b22b2dfd70eb22028289a8fa5364","glsl-optimizer/src/compiler/glsl/standalone.h":"a7c397d1dfdd1e7fb2cfe99db35cd9df93251e642059208533202b7f20497f83","glsl-optimizer/src/compiler/glsl/standalone_scaffolding.cpp":"970d14b7a9d58e5270321f97bf5d57795558b1c570a56678e04a65b26c60bf4f","glsl-optimizer/src/compiler/glsl/standalone_scaffolding.h":"d921a617ea82b9e49413314492a645c44356de503581b1be3f1b57de236e480d","glsl-optimizer/src/compiler/glsl/string_to_uint_map.cpp":"d824bf5b839bd39498dc9e457103cdbe3e5289ddf7564107c27b1505948dd31f","glsl-optimizer/src/compiler/glsl/string_to_uint_map.h":"e2f18e66359c9d620e085de7f4a334a47df9c66e65a5bfe8b734c627bec04104","glsl-optimizer/src/compiler/glsl/test_optpass.h":"b27b8f35f5387e7ce4982bb51c7b63ccf14f91757f3108a5d02ed006925bb8a0","glsl-optimizer/src/compiler/glsl/xxd.py":"376484142f27f45090ea8203ae2621abf73f06175cb0ee8d96f44a3b9327f4bd","glsl-optimizer/src/compiler/glsl_types.cpp":"044bb6754f45419a3151e7a25c39202a82009ae3c6bc54ff7f0bb4258a5deefe","glsl-optimizer/src/compiler/glsl_types.h":"fd899a42f34ddeb8601bc3cd6c5e3aed82fc8aef4042dde1b39b3c01e1dcc219","glsl-optimizer/src/compiler/shader_enums.c":"436bff5216b11b0980bdfada5885fc6ac9afa2037a3027fcd6eea2a8635597ac","glsl-optimizer/src/compiler/shader_enums.h":"13220442a5c02e83540cf2c0ad4f8417b2fbda5f2586dec4e92082544c937cdd","glsl-optimizer/src/compiler/shader_info.h":"4c5453e81197ca83593ee4f365074b23530f2ab21c78e1733b63dec6f344c12a","glsl-optimizer/src/gallium/auxiliary/util/u_half.h":"3c2b37bda3ccb64387e44b723d29cf9046decab1a893bf42d842e9603398bdee","glsl-optimizer/src/gallium/include/pipe/p_compiler.h":"c75620096ce8523dae90599e50aa2ef6468d3b0e368a77795edeb20dd1abfc0c","glsl-optimizer/src/gallium/include/pipe/p_config.h":"a27692fc35f9e55df3224b7529e66b3001e911e94e6bc5f8f569e493e1ee3fb7","glsl-optimizer/src/gallium/include/pipe/p_defines.h":"be26d68c0acc67c5e44788c6299716a9eee415fd81d7d747e3738a829e3b6b38","glsl-optimizer/src/gallium/include/pipe/p_format.h":"5674215fc41d27496f037cf837717daefbf23ebb38d40ace7c0c414bc08182b0","glsl-optimizer/src/gallium/include/pipe/p_state.h":"d600593aba5f5a17072a6c38f6baa81e01c7994b0174250f7e433bb41684b702","glsl-optimizer/src/mapi/glapi/glapi.h":"73632a625c0ddabc401205e8b5a81eb8af8506868efe4b170d7979ec3619e9c5","glsl-optimizer/src/mesa/main/config.h":"5800259373099e5405de2eb52619f9de242552a479902a3a642a333c8cb3c1e7","glsl-optimizer/src/mesa/main/context.c":"2f3208473d99c94f734b1137ba91889d4a1babb9e7534bf1dc85d851ee98274e","glsl-optimizer/src/mesa/main/context.h":"cc7e4194797db9d007f01884e23d786c453b3860821f7f2ddcdf0f1bf3f8ffb1","glsl-optimizer/src/mesa/main/dd.h":"6a964acd06b6c2d88700e69fb75fe3c6b3b3d45bbc41db24f3f897a29695fe0c","glsl-optimizer/src/mesa/main/debug_output.h":"7312422e90b8c0e34028ac27280e438139b5cba525c99deb3ac883cd3d87e452","glsl-optimizer/src/mesa/main/draw.h":"7eaef3a9e27a60ea6f7937109bf3a6190b831162fde0479abb12077ce27c353d","glsl-optimizer/src/mesa/main/enums.h":"87d562a6764f51c014a2274fa7c3aca17c04441537ddd56b2554f13c6fffea92","glsl-optimizer/src/mesa/main/errors.h":"c79444b5df289c90fbb22a33b2d0c23917d9fc4510960088f0b79e53bb56b1b2","glsl-optimizer/src/mesa/main/extensions.h":"a38b2f87cc93c513994281350d69e06c84ff8eded5313ec0a1be33f375e0ebbd","glsl-optimizer/src/mesa/main/extensions_table.c":"17642d1a8c9a0bf2bd61060052d33ff14a005d2b962e6cf91465797a50851e85","glsl-optimizer/src/mesa/main/extensions_table.h":"2c879571c238d2e14461031ac740372fd0f9ac3a34c0d5541bb9b7ed4c0376c8","glsl-optimizer/src/mesa/main/formats.h":"02e2f7ec3e39286cf9f27e2641043e6df8ecb1dfde9e643313210e214af2a929","glsl-optimizer/src/mesa/main/glheader.h":"58217b33eead6aa6b23cd4a291cefeaa6cb84e465f4960daffca97c44d6d1c35","glsl-optimizer/src/mesa/main/glthread.h":"51fb2711f77e7eafcfc52d29d5b844978832b24c930d88accd48d143a6eb9c6f","glsl-optimizer/src/mesa/main/hash.h":"7e7f782034c16a8e693de48e00c31d4a90b0129f4029fd074033d7d16ccbe718","glsl-optimizer/src/mesa/main/macros.h":"73d15ddfd64f2b57b9b2ffeeb993b9c2c0899a80563e9d6ff337b11ccbe6eee5","glsl-optimizer/src/mesa/main/menums.h":"5dfac0e2279d60b0cd0c7b9fc2a5021620d0f6282ed2e738c420214e3af152d3","glsl-optimizer/src/mesa/main/mesa_private.h":"edda678b93438944279a551f663b8858ad84814a9fc88ba9672ef195599c24ae","glsl-optimizer/src/mesa/main/mtypes.h":"6efddefa099e4d2e3fdd97f0055644f47aba21711385edfeabc2d9b0676f2eec","glsl-optimizer/src/mesa/main/shaderobj.h":"9f0dfe96d0c2154201adef942bd36053533ac7b2492fb3786acda5bea514c75e","glsl-optimizer/src/mesa/main/uniforms.h":"4e331e6ad6e9cbded978b4082dbe0a57c1f8f01327446bb6892bfc179976c38b","glsl-optimizer/src/mesa/main/version.h":"9d0a13a758099302dc55cf7d045791834a89b0f9d4cf17b2692259b369a8a9a1","glsl-optimizer/src/mesa/math/m_matrix.h":"a37b19f182e070db3df93b0ede43c22fb8be8c2906504133ee6dbd7db1185d8b","glsl-optimizer/src/mesa/program/dummy_errors.c":"1820e305515b4c5e041f5e1623266a48ec8f076a155310be7d60637101f593e4","glsl-optimizer/src/mesa/program/ir_to_mesa.h":"b47f58d22e3ca2ae42d52501ea769d15c4476834944fa97eeccd3a3439211d00","glsl-optimizer/src/mesa/program/prog_instruction.h":"ab3832152a7e144b59e5a2264b2c29db56d93be31e76bbd958527a56771b40eb","glsl-optimizer/src/mesa/program/prog_parameter.h":"ba18c743284eadbc837c2c364c73e5d372321a7637a76e589d8d39fe8b5de225","glsl-optimizer/src/mesa/program/prog_statevars.h":"fc413698f84bc52d45fdeae0471934ee9904bfb7eac1a2b5f70446e54bcbbdca","glsl-optimizer/src/mesa/program/program.h":"1f01026a4eff440a3f122fd9b519d03546fe7f7d8be60dca834e95a2f8fbbfd2","glsl-optimizer/src/mesa/program/symbol_table.c":"6611cb9f078035bf5ff8c9112093a6c7d99f8af99a3931d0c07f227cc72283ea","glsl-optimizer/src/mesa/program/symbol_table.h":"631dc35ac48d5e87962d45507461920f6575610960ffcc42a08cefeb43300cda","glsl-optimizer/src/mesa/vbo/vbo.h":"6eb1dcd9a08c92f276c5fe08da184ff9d455d1be421913b8ad732a7b65e858fb","glsl-optimizer/src/util/bitscan.h":"d4fcb47b57a50d70cb97f99ca3e619bc06282a877768a435e009775ce8d77f36","glsl-optimizer/src/util/bitset.h":"c40f78515c6230fed18345c6751ce33833a49da7a27901c7e6d7340cbdcbc5e7","glsl-optimizer/src/util/blob.c":"8f729846f66efc9c15485cc5fc24c6ec861fc1fecb2f652573f2a237d481b791","glsl-optimizer/src/util/blob.h":"93e1eaac866b9a7cd6fc03b533c18fb2edf0e97f03395eff4f3a605c4fc14d0c","glsl-optimizer/src/util/compiler.h":"79e3bf40a5bab704e6c949f23a1352759607bb57d80e5d8df2ef159755f10b68","glsl-optimizer/src/util/crc32.c":"2f3467a046b3a76784ecb9aa55d527698c8607fd0b12c622f6691aaa77b58505","glsl-optimizer/src/util/crc32.h":"59bd81865e51042b73a86f8fb117c312418df095fed2d828c5c1d1c8b6fc6cd4","glsl-optimizer/src/util/debug.c":"c3d68e9752ccc19e66c669562cd113cf1d0ac83cbb30174789e7fb8d1df58f9c","glsl-optimizer/src/util/debug.h":"50068d745c4199ccbd33d68dd4c8a36d2b5179c7869a21e75906ddd0718ca456","glsl-optimizer/src/util/detect_os.h":"343a8790d17a3710c6dd015ee367f84e3902ff3f2e36faca2bf93f9d725d3574","glsl-optimizer/src/util/disk_cache.c":"f533937e5a4fffe76e2739ef4b6b1e1da097d96d63eb808e68ebbc7027641c23","glsl-optimizer/src/util/disk_cache.h":"e83314fb14134a8e079b15e470a6376ba5a8253701f048c890a62b7e55d64bc8","glsl-optimizer/src/util/fast_urem_by_const.h":"e108fce804616c47d071dfe4a04163eec1126e448ed1aa89abb6b3a6d772bd5b","glsl-optimizer/src/util/fnv1a.h":"ab2596f19c6adf431ae27618f62c5743e24ad23ef83bb359a4c4c218245ab459","glsl-optimizer/src/util/format/u_format.h":"4cdfc0c59cbc99a092e5ec5a396910f2d93b9643e5d8141050b011e66f11e45b","glsl-optimizer/src/util/futex.h":"26f7c9d86e9ffef4c0fa2761f1aaa1918337302e20bd6ca10e61dc3c47356deb","glsl-optimizer/src/util/half_float.c":"11bc2584493d5d9d46e8c8a619a0307cf150bf5ab5d0f96bb764b061dc37a00e","glsl-optimizer/src/util/half_float.h":"7f7c380f126da1400a91758cc0392f24bf967bce1672890b62be26fe9fbd922b","glsl-optimizer/src/util/hash_table.c":"0ca40352e35dedab0a84c64c903f1b16d47e950bb5f43b4d22bb57d499bfea6e","glsl-optimizer/src/util/hash_table.h":"217191bb360592e2232f187473c10287d2cda8ae6fa5c53d0ef74c8c206118b4","glsl-optimizer/src/util/list.h":"9fab03c6a78186bb5f173269f825f6ce976b409d931852e3d93bac632e07989a","glsl-optimizer/src/util/macros.h":"63faf65b51058c483b17f1f77da51d1c53c8beab52678cb6bd01f1228a63b6b0","glsl-optimizer/src/util/mesa-sha1.c":"00c692ec353ebc02c06c57c5a71de0ab7a119f86a4146f452e65ec87e4944417","glsl-optimizer/src/util/mesa-sha1.h":"bff4c29f4bf7cdbcefb30fa0c996a7604a380eba8976467c2a60e7cd328f7e26","glsl-optimizer/src/util/mesa-sha1_test.c":"25da89a59d51469f77b4c468ca23ffdce0a7a1166a70b6cc23026a6800b0143c","glsl-optimizer/src/util/os_memory.h":"64555faf1760ae6954f42c83727c38dfc4c278e9152115779ffaad58b42adacf","glsl-optimizer/src/util/os_memory_aligned.h":"12d86fa94be38c13f7eeebdf313795e1267dd5a7187d2f0072e0e896f41702f6","glsl-optimizer/src/util/os_memory_stdc.h":"07360363b88c927065e10df71bebf6c8a0cc3b9167c9dfce55f2d65f11e6f787","glsl-optimizer/src/util/os_misc.c":"a9936e613ec84803abd59ad47c192c8e3939993c950ac91973fdc4cec1801bb8","glsl-optimizer/src/util/os_misc.h":"cc68eb12e05b5e749c54298cb4a6f4cd20cc5af7db3403e70b3c27b56090c740","glsl-optimizer/src/util/os_time.h":"73e775f7335244ff5964c678c27eedf1aea6abea44c4169d327ea8c7ce4a3a88","glsl-optimizer/src/util/ralloc.c":"4b51189595ef67bcef52c40cbf654d969041dbd15e15d4a893ad494ac060aeca","glsl-optimizer/src/util/ralloc.h":"e573c45875ff1530f0dbee9a93ae55535fdac8d5cc88a79ebc327c688824bde5","glsl-optimizer/src/util/rounding.h":"fe22a2a198057b5442de8034968a68a70909811aa12e1096ced221415f237e2b","glsl-optimizer/src/util/set.c":"86f8c9a830bead5a5a79bc970b0ff97809312af07b3beb39ef9d90af04d40a1b","glsl-optimizer/src/util/set.h":"3e39ca161e7ed4ec7c436cc9c7919ed9a55ed1b71edbf2caf6f9bcfd9bc578ed","glsl-optimizer/src/util/sha1/README":"00af7419af05247081858acb2902efd99fcda2ce16e331079f701645bb3729c0","glsl-optimizer/src/util/sha1/sha1.c":"1403bbe0aad42ba3e6be7e09f7cad87a6a8c4ad5b63962f7b92b9f37d8133b04","glsl-optimizer/src/util/sha1/sha1.h":"68d9f240eab2918026ecdf22be36811abbd4f1389f6c36e31258041aeaedd247","glsl-optimizer/src/util/simple_mtx.h":"12c6c3c4b7db9168bc656d5b3c65912075084d2b388c415d5c3d3f5953a9d6c7","glsl-optimizer/src/util/softfloat.c":"a97e51a96fe5e6a052c02aa6bbec683fe73fb88a8c087d9c930503e2120d8a2e","glsl-optimizer/src/util/softfloat.h":"66664b0250e83bf5dd4cc743acd119d076efcea624a0eab3d6b60718e6ee8811","glsl-optimizer/src/util/string_buffer.c":"63a1d1b1e34926c88ea00159cafbcd56568b805c4f64d1e8c97169fe313921fc","glsl-optimizer/src/util/string_buffer.h":"7b88d1b1d9c6cfb8e93331813535c127289437c75f822029e9a3bca8ea6b52ee","glsl-optimizer/src/util/strndup.h":"0273c4fdb7482cd7746881a63d3998648c6d63415ba85af1d1860f0e0dc504c6","glsl-optimizer/src/util/strtod.c":"5cf610d8a37373cf37cfb7aae903525d943b2674b1f32594c70b0eb19a8c9697","glsl-optimizer/src/util/strtod.h":"237396def4e264d35ed4bedea00ef9a4ceab6d7a11a18c770d9747d22c69ed2d","glsl-optimizer/src/util/u_atomic.h":"c02e809526c6c09ba8fe51f50b2490d1b6c8e5c7f3c4031ae958250d098fc3bb","glsl-optimizer/src/util/u_debug.c":"8c060e379b816618f3dd22c9ea523c68b9425c76c36a7dfe5d6d375b337f5f4a","glsl-optimizer/src/util/u_debug.h":"e11e26edd9b9e4e6f8e6a435e69f4d9edda27e9a379f68f4c82ea2525aaaea68","glsl-optimizer/src/util/u_dynarray.h":"853d0fa6ff2261614488be624deb8a2b01e57c2c8eabc28578cbeed4ccc95694","glsl-optimizer/src/util/u_endian.h":"3ccea7e529740318d8a4b05c00db3adc9d1e292a52bdc56a05c9fae99209720f","glsl-optimizer/src/util/u_math.c":"c868a8c0886dc78f1b06b13404ba8b253090449045774dd56893ac9d75795184","glsl-optimizer/src/util/u_math.h":"ae235e8f73fea1b83e68e4141244666280094b3871ce299023b4c2daa1d7555c","glsl-optimizer/src/util/u_memory.h":"c5db17c724c70283ddbe04165722f6988d4e0eb9aa3602ae472feff016649af9","glsl-optimizer/src/util/u_queue.h":"92930ce236c0528a98b695f5cea8c5c6aa9683beaf71a2227bdc5d33d1b21506","glsl-optimizer/src/util/u_string.h":"c5a2f4ef576d1547bda12c4ea219179fefa54414977743ac094abcaf696ef6ca","glsl-optimizer/src/util/u_thread.h":"00b708459b27f9910d18db92c18cc65cfc618ac2b3cd144e45f8640057b10d58","glsl-optimizer/src/util/xxhash.h":"2f2aff2fc6c0c929f52cf6ae7314122124c5be026d41ad1c357608383c4a37ad","src/bindings.rs":"79993db2058bde39f99ef483d02560d33b1cb882f6a552319e8b86eb6f9021e1","src/lib.rs":"04be1554cd829eb40864b06d80b491dd48117a4e3a601c7d482117f7a0391e67","wrapper.hpp":"f3ea34cc496f7d90b9bfcada3250b37b314c3524dac693b2ece9517bc7d274ac"},"package":"74a3f5c04450dfdadb4b08f6e5ee6f5110f674de1acbd6199bfec68392a8cbaf"} \ No newline at end of file
+{"files":{"Cargo.toml":"7706cf6777f1f50fd565d1ba608ae08f8b2c63342a2c6e5b40f171ffc8d6e6b6","README.md":"4468e08c64c19977707d792bfab0080e35ff927b64990eab77873f8ba056ba1c","build.rs":"6a64610018701781af182c418a4355c9ac5d99d000be9457f0e38a7dadf7542a","glsl-optimizer/CMakeLists.txt":"42ce94744e82ffa000da8b64d81fc140e293b9f5da7dd4cf6b49e7404a2448d9","glsl-optimizer/README.md":"b18eef11a92d267d88a937b1154f7670ee433c730b102fdf7e2da0b02722b146","glsl-optimizer/contrib/glslopt/Main.cpp":"14ba213210c62e234b8d9b0052105fed28eedd83d535ebe85acc10bda7322dd4","glsl-optimizer/contrib/glslopt/Readme":"65d2a6f1aa1dc61e903e090cdade027abad33e02e7c9c81e07dc80508acadec4","glsl-optimizer/generateParsers.sh":"878a97db5d3b69eb3b4c3a95780763b373cfcc0c02e0b28894f162dbbd1b8848","glsl-optimizer/include/GL/gl.h":"1989b51365b6d7d0c48ff6e8b181ef75e2cdf71bfb1626b1cc4362e2f54854a3","glsl-optimizer/include/GL/glext.h":"2ac3681045a35a2194a81a960cad395c04bef1c8a20ef46b799fb24af3ec5f70","glsl-optimizer/include/KHR/khrplatform.h":"1448141a0c054d7f46edfb63f4fe6c203acf9591974049481c32442fb03fd6ed","glsl-optimizer/include/c11/threads.h":"56e9e592b28df19f0db432125223cb3eb5c0c1f960c22db96a15692e14776337","glsl-optimizer/include/c11/threads_posix.h":"f8ad2b69fa472e332b50572c1b2dcc1c8a0fa783a1199aad245398d3df421b4b","glsl-optimizer/include/c11/threads_win32.h":"95bf19d7fc14d328a016889afd583e4c49c050a93bcfb114bd2e9130a4532488","glsl-optimizer/include/c11_compat.h":"103fedb48f658d36cb416c9c9e5ea4d70dff181aab551fcb1028107d098ffa3e","glsl-optimizer/include/c99_compat.h":"aafad02f1ea90a7857636913ea21617a0fcd6197256dcfc6dd97bb3410ba892e","glsl-optimizer/include/no_extern_c.h":"40069dbb6dd2843658d442f926e609c7799b9c296046a90b62b570774fd618f5","glsl-optimizer/license.txt":"e26a745226f4a46b3ca00ffbe8be18507362189a2863d04b4f563ba176a9a836","glsl-optimizer/src/compiler/builtin_type_macros.h":"5b4fc4d4da7b07f997b6eb569e37db79fa0735286575ef1fab08d419e76776ff","glsl-optimizer/src/compiler/glsl/README":"e7d408b621c1b605857c4cab63902f615edb06b530142b91ac040808df6e22f7","glsl-optimizer/src/compiler/glsl/TODO":"dd3b7a098e6f9c85ca8c99ce6dea49d65bb75d4cea243b917f29e4ad2c974603","glsl-optimizer/src/compiler/glsl/ast.h":"3e68ff374350c49211a9931f7f55a485d8d89fc4b21caaffbf6655009ad95bf8","glsl-optimizer/src/compiler/glsl/ast_array_index.cpp":"92b4d501f33e0544c00d14e4f8837753afd916c2b42e076ccc95c9e8fc37ba94","glsl-optimizer/src/compiler/glsl/ast_expr.cpp":"afd712a7b1beb2b633888f4a0911b0a8e4ae5eb5ab9c1e3f247d518cdaaa56d6","glsl-optimizer/src/compiler/glsl/ast_function.cpp":"74f4fbd490e366b37f4715168bb3465ecd9334d4130942f75dcc8e80e8e7f027","glsl-optimizer/src/compiler/glsl/ast_to_hir.cpp":"d0f798eb09271d41d068b9e7b18220d37f1ed0083300ab51eba30989698fe23d","glsl-optimizer/src/compiler/glsl/ast_type.cpp":"8eb790b24b26dfb72bdc333744b566c26d8464c5d47d20eae659461f5c4899f7","glsl-optimizer/src/compiler/glsl/builtin_functions.cpp":"454189d643c220fcb49116ee5c8a34f7b349aa67564040deb8607f6a41a15e70","glsl-optimizer/src/compiler/glsl/builtin_functions.h":"a37cad7ed09b522c5b8bec7b80115a36846e7ba6e0874a2a858e32f7f202c665","glsl-optimizer/src/compiler/glsl/builtin_int64.h":"619def6f3aebf180da3944ef08f159ab12a58b24767e41d8b985ac37ded54d62","glsl-optimizer/src/compiler/glsl/builtin_types.cpp":"afec060b62d6f3b00bfbf94e9fa5f96341ce096c128d1eef322791e6ed9cea4d","glsl-optimizer/src/compiler/glsl/builtin_variables.cpp":"6563bfb1345cbca4c77e00eef09ad152f3e1dc271d246a08c5ce9e1f4ce4250a","glsl-optimizer/src/compiler/glsl/float64.glsl":"1072fd888be48c2a7a5117cd2d92a65f034965a66375f598bb856bff5d7be766","glsl-optimizer/src/compiler/glsl/generate_ir.cpp":"e5f0175370a0d07f93c48d3f0f1b8233d12c64a7b02de02dcc753ef7b398ef0f","glsl-optimizer/src/compiler/glsl/glcpp/README":"a0332a1b221d047e9cce5181a64d4ac4056046fd878360ec8ae3a7b1e062bcff","glsl-optimizer/src/compiler/glsl/glcpp/glcpp-lex.c":"2d179879b1ffe84f58875eee5b0c19b6bae9c973b0c48e6bcd99978f2f501c80","glsl-optimizer/src/compiler/glsl/glcpp/glcpp-lex.l":"e4c5744c837200dafd7c15a912d13f650308ea552454d4fa67271bc0a5bde118","glsl-optimizer/src/compiler/glsl/glcpp/glcpp-parse.c":"03494f9ce1cb82260506e2559e73a3eeb622c4bd51b65eaa0a2c3351862bd4c8","glsl-optimizer/src/compiler/glsl/glcpp/glcpp-parse.h":"264d9a18421cde255ce34a0a62b3d8e73465359f0d167e64aa3973062aae5bdd","glsl-optimizer/src/compiler/glsl/glcpp/glcpp-parse.y":"fafb66e3a8f149d19e085f18a4273ba6d4c11af9e9a01d665cc784dddf97b79f","glsl-optimizer/src/compiler/glsl/glcpp/glcpp.c":"37ed294403c2abfd17fd999d1ae8d11b170e5e9c878979fefac74a31195c96b0","glsl-optimizer/src/compiler/glsl/glcpp/glcpp.h":"85ac8b444bcbd0822b66448a1da407b6ae5467b649f5afaf5c58325bd7569468","glsl-optimizer/src/compiler/glsl/glcpp/pp.c":"a52d94f1bcb3fb2747a95709c4a77c25de7eea8354d2b83bb18efd96976a4473","glsl-optimizer/src/compiler/glsl/glcpp/pp_standalone_scaffolding.c":"d11aeb3acfe966d1b78f1ee49804093f2434214c41391d139ffcb67b69dc9862","glsl-optimizer/src/compiler/glsl/glcpp/pp_standalone_scaffolding.h":"abbf1f36ec5a92d035bfbb841b9452287d147616e56373cdbee1c0e55af46406","glsl-optimizer/src/compiler/glsl/glsl_lexer.cpp":"272b9fc1383d72b81bfc03fa11fdf82270ed91a294e523f9ce2b4554bd3effa9","glsl-optimizer/src/compiler/glsl/glsl_lexer.ll":"2b57d9f9eb830c3d7961d4533048a158ee6f458c8d05c65bea7b7cfbc36e4458","glsl-optimizer/src/compiler/glsl/glsl_optimizer.cpp":"f8095d20629d0af70be930b0612e169edb274551a1d25a3cd1bf9995a11ce2e8","glsl-optimizer/src/compiler/glsl/glsl_optimizer.h":"22e843b4ec53ba5f6cd85ca5f7bad33922dca8061b19fb512d46f1caca8d4757","glsl-optimizer/src/compiler/glsl/glsl_parser.cpp":"126baf368d525aba301854e3d91ba60b5aee32e1102376af71416f32cb95ec48","glsl-optimizer/src/compiler/glsl/glsl_parser.h":"2ea9a50716098a8f7bef782d2a030d757b68da73afb01b4d4940d3e8381d44e8","glsl-optimizer/src/compiler/glsl/glsl_parser.yy":"6b1fd1576b29fce005dff744a6dbd0219e4c695c361d61864e1f3a8d6fa6b764","glsl-optimizer/src/compiler/glsl/glsl_parser_extras.cpp":"aad64b5b66467da650091430681e8c6a820cf3cadc4db3c160bf2f15875390ae","glsl-optimizer/src/compiler/glsl/glsl_parser_extras.h":"71fd0e92bbdb193dfb067d7bfdb1200d77392be2fbd0cbfc9ca89d1bb4c7e741","glsl-optimizer/src/compiler/glsl/glsl_symbol_table.cpp":"6660fb83c0ddddbbd64581d46ccfdb9c84bfaa99d13348c289e6442ab00df046","glsl-optimizer/src/compiler/glsl/glsl_symbol_table.h":"24682b8304e0ea3f6318ddb8c859686bd1faee23cd0511d1760977ae975d41bf","glsl-optimizer/src/compiler/glsl/hir_field_selection.cpp":"72a039b0fcab4161788def9e4bedac7ac06a20d8e13146529c6d246bd5202afd","glsl-optimizer/src/compiler/glsl/int64.glsl":"303dbe95dde44b91aee3e38b115b92028400d6a92f9268975d607471984e13eb","glsl-optimizer/src/compiler/glsl/ir.cpp":"2b4741cce90b5d4abff5d719c7324e2693c67294d4d99736cb241554adb281bc","glsl-optimizer/src/compiler/glsl/ir.h":"990b1c74447c4eb4835353ccb0ed9aea644f97fc1129ef1739cd935075d85d2e","glsl-optimizer/src/compiler/glsl/ir_array_refcount.cpp":"8cdc1cffe01e42e0566fa2193a75f789628e8025ad1b82f0ee6f204451b7f9f7","glsl-optimizer/src/compiler/glsl/ir_array_refcount.h":"75f06ec81342b379096ca52e1dc0fd5f19a11ff8e9b58203c20628179d644c12","glsl-optimizer/src/compiler/glsl/ir_basic_block.cpp":"1e2920b1c0ecb08424c745c558f84d0d7e44b74585cf2cc2265dc4dfede3fa2f","glsl-optimizer/src/compiler/glsl/ir_basic_block.h":"81be7da0fc0ee547cd13ec60c1fcd7d3ce3d70d7e5e988f01a3b43a827acdf05","glsl-optimizer/src/compiler/glsl/ir_builder.cpp":"daba29c5a1efdd5a9754f420eb3e2ebdf73485273497f40d4863dadeddb23c0d","glsl-optimizer/src/compiler/glsl/ir_builder.h":"2822e74dd3f6e3df8b300af27d5b11ea2dd99d0e5e7ca809b7bbcce9833c483c","glsl-optimizer/src/compiler/glsl/ir_builder_print_visitor.cpp":"8c6df5abf2fe313363f285f171c19ca6c8ee4f3bc2ed79d33c0c88cc8be45c48","glsl-optimizer/src/compiler/glsl/ir_builder_print_visitor.h":"799852adc3a0e54d04080655e7cebfa0d3bf5b6ffed5d8414f141380665d4db7","glsl-optimizer/src/compiler/glsl/ir_clone.cpp":"d897a4e1f5bbec4a6a2f15044c1be9a4d13899c73be77335b041049a4589aa5d","glsl-optimizer/src/compiler/glsl/ir_constant_expression.cpp":"78bd87ddb09db67f6c499067728d72aef4f16aa02721a99a4b769d1e0cfa9010","glsl-optimizer/src/compiler/glsl/ir_equals.cpp":"bca28533a6310b0fc152b56d80872368f1510dc62ed6e8ac199b9ffa7fac02e7","glsl-optimizer/src/compiler/glsl/ir_expression_flattening.cpp":"7e918d4e1f237eca01396004015865ce345afe32a876c9dbc6728576a1a7eae4","glsl-optimizer/src/compiler/glsl/ir_expression_flattening.h":"f45b66aa9497520e7e08e612d24b308477c34477fbd963ee9320eac664957f16","glsl-optimizer/src/compiler/glsl/ir_expression_operation.h":"cc9f10727dbd26cac506804f51456302c702650f9eeb59054a7e1575d5cf6687","glsl-optimizer/src/compiler/glsl/ir_expression_operation.py":"7b86c96021b9fbe165957f4ecb0b612fefcde1c2cf3c6d75e3cdb22e369216ba","glsl-optimizer/src/compiler/glsl/ir_expression_operation_constant.h":"9ad3346416392e3efa11e12ecf2feca7453c5253d241eb96c91dfb85d4f2b971","glsl-optimizer/src/compiler/glsl/ir_expression_operation_strings.h":"a6826daf496a8b9e89885bc2a161ac3445d501b23c6e0ac33e2c01b506b273c8","glsl-optimizer/src/compiler/glsl/ir_function.cpp":"7537365fc0fbe4b37a26b9a2146cc64d3e9a774d60eab63b65002ad165ae8fc7","glsl-optimizer/src/compiler/glsl/ir_function_can_inline.cpp":"faddbf112187a048d502716a3fb82570a322299ba2a3abd79388382c82040bfc","glsl-optimizer/src/compiler/glsl/ir_function_detect_recursion.cpp":"9176973eaf5c0a984701f953bb7a80f37dca43d59b5bce50fc69b3f02f2902d7","glsl-optimizer/src/compiler/glsl/ir_function_inlining.h":"9739493f99c489987d650762fccdd3fb3d432f6481d67f6c799176685bd59632","glsl-optimizer/src/compiler/glsl/ir_hierarchical_visitor.cpp":"3725861fbe2b98e0617f52d3b14cf6d3b25fb5ec00f5ef5d308b03642f592767","glsl-optimizer/src/compiler/glsl/ir_hierarchical_visitor.h":"e0560210e966c0c31e4ca843e80ea154e64db5a444b8c2df845b6ba5b3a43fc1","glsl-optimizer/src/compiler/glsl/ir_hv_accept.cpp":"caf7ce2cd9494aadd3c58bcf77f29de58368dc9e347a362bbf37f8bda9509b80","glsl-optimizer/src/compiler/glsl/ir_optimization.h":"8b3dcfc7f9e96b21a8dd47a0040d90be483a9e67a2cdce3a697188fb758d4630","glsl-optimizer/src/compiler/glsl/ir_print_glsl_visitor.cpp":"f8e34a983452be0dcb5a695e9c8e895eead24f9e540992a8afe510ae85da4c4c","glsl-optimizer/src/compiler/glsl/ir_print_glsl_visitor.h":"1ad1bd3efd1ace39051c13f904c05fd80425d329444f9a8d47fd6d948faf46e0","glsl-optimizer/src/compiler/glsl/ir_print_visitor.cpp":"643f5a68aae3fb37267fd793f1216d1cfdeb2c09338c26b1f30e4c6deaef4de5","glsl-optimizer/src/compiler/glsl/ir_print_visitor.h":"4573eb93268a2654c14b505253dd651e2695d43dc745904d824da18305269b95","glsl-optimizer/src/compiler/glsl/ir_reader.cpp":"06bfba802c8354e5a8b2334b6d78d6297de18235bedd3f8fbb382c89870b02f2","glsl-optimizer/src/compiler/glsl/ir_reader.h":"63e3f7f1597936a7011d5b520e171b197bf82bee6c1560d822c3edf5aaa6f9e9","glsl-optimizer/src/compiler/glsl/ir_rvalue_visitor.cpp":"84b5c5d746555adca85759c2912fe48010232b7c1c0bd2cf03bd04067a85e66f","glsl-optimizer/src/compiler/glsl/ir_rvalue_visitor.h":"fd8c561b71085d3211fff85ed514fecb299d8ce19a04bc063419a55b6d840525","glsl-optimizer/src/compiler/glsl/ir_set_program_inouts.cpp":"ab9f115ce9e7f312d9c7978340ced0dc4ae6d13a80e08442ba9709d11d50cae5","glsl-optimizer/src/compiler/glsl/ir_uniform.h":"683ae6896b1a08470c090be5f822fc31cd434eab9216e954b9bba24a46975109","glsl-optimizer/src/compiler/glsl/ir_unused_structs.cpp":"9c1620c45f2fc071fe5ed828472040b14c5f42effe06aa0e3b8352c95ef78786","glsl-optimizer/src/compiler/glsl/ir_unused_structs.h":"13387b49c23093575276b25b9dfd31fedd8f131c5c4f3128ab04cf03e15b5295","glsl-optimizer/src/compiler/glsl/ir_validate.cpp":"6b232be5999a86ea278f4f15b2832d76843246509118d924243055a3b9b0299f","glsl-optimizer/src/compiler/glsl/ir_variable_refcount.cpp":"2764a3cad937d53f36db7447c3a5b98b04bf153acf81074d971857fc5bca460d","glsl-optimizer/src/compiler/glsl/ir_variable_refcount.h":"b0668e3eb1501ef65e38fe12830742ecb3d28e6039f30e366c8924efc29b4a39","glsl-optimizer/src/compiler/glsl/ir_visitor.h":"f21b3534c3d66d5fb707d1581fece7e1eb043523afbaedf89918cfb031c6df94","glsl-optimizer/src/compiler/glsl/link_atomics.cpp":"360f0209e11f367ba358223597b0a118bae095bff16337cf03f1fb89c5b80ca6","glsl-optimizer/src/compiler/glsl/link_functions.cpp":"de7895da8aa33a1e3c2c1eb2fdaf267ab5d1fbfdb79ae2e67f95211e946e294c","glsl-optimizer/src/compiler/glsl/link_interface_blocks.cpp":"1926cfa73810704eb19b916c1b2cdb9321155e2f98b2a0a57c7c3c6e960540cd","glsl-optimizer/src/compiler/glsl/link_uniform_block_active_visitor.cpp":"1e14e06ca3b2c1089cfba2e8eaf0c1f373d9d6374b6082f320962dd71ae09611","glsl-optimizer/src/compiler/glsl/link_uniform_block_active_visitor.h":"fd58c155af645295bb6aec08797889de586f4d919731de2bce57e8dce59bb048","glsl-optimizer/src/compiler/glsl/link_uniform_blocks.cpp":"09589f49776dce32e6c4044937de7e0c839a9754ad31960148f8f9e010658997","glsl-optimizer/src/compiler/glsl/link_uniform_initializers.cpp":"bf98e08c12db466acf9623cbeb8fa8e3b4002512722e7a6521287f558a099f37","glsl-optimizer/src/compiler/glsl/link_uniforms.cpp":"84bad5b1377362cecf259b05124239be5220b03ce1c0c61b59bd9a47e4379af2","glsl-optimizer/src/compiler/glsl/link_varyings.cpp":"a5f1a53e7c80d635515fe808ff223d89fef1767abb0f2b7aa28fa6773dca353f","glsl-optimizer/src/compiler/glsl/link_varyings.h":"b9dbe018f038df69763df2e928742ce81bbc6e3aaba26f50621e30a6d9aa6220","glsl-optimizer/src/compiler/glsl/linker.cpp":"40b1ecd5d4f6c7f13d5a87ce390561a51fdf6f3fcd9b2197b9c88b03a773ba94","glsl-optimizer/src/compiler/glsl/linker.h":"ecf94b4ad75ef461c27c557fda4bd25f34c91930822b8e1d729ec84520d4a049","glsl-optimizer/src/compiler/glsl/linker_util.cpp":"1663ad88e2a369305659aeeffaedb5bd752cf76340a2ba5797fc0bf600633cf9","glsl-optimizer/src/compiler/glsl/linker_util.h":"6db788daf9c8e87ae2764b61a8b37ebe419e69c1b82ddee01986e37c978c6993","glsl-optimizer/src/compiler/glsl/list.h":"b1f46ce0e552fe7c45b2a19408a9d97662e23e4b182ab335491c26f8cf25886f","glsl-optimizer/src/compiler/glsl/loop_analysis.cpp":"57ecd573477c68091c7cc99537faa7139a8f395935e3d4f10144cefdefb5a611","glsl-optimizer/src/compiler/glsl/loop_analysis.h":"a85f045a038ee5b5176063e85d7988865862c44ab0580f771b993a042d0b69cc","glsl-optimizer/src/compiler/glsl/loop_unroll.cpp":"bd4292ea2809f5a669bcb76ceaa1ac365772dcd638c579c3ed10275214901a54","glsl-optimizer/src/compiler/glsl/lower_blend_equation_advanced.cpp":"8cfbef140d9c4b4d2f57bfa05c9c374d31a121d0f87afce94333f049023b654a","glsl-optimizer/src/compiler/glsl/lower_buffer_access.cpp":"1ae221c3c7a95aeb867207e7a742be635f91b406c157747bfd6ddf10274d97fb","glsl-optimizer/src/compiler/glsl/lower_buffer_access.h":"807886953a576a323591798cbca5e2df24295ea893b28affd8ffb5926cebaa04","glsl-optimizer/src/compiler/glsl/lower_builtins.cpp":"4d81afc32cf58e1481fcb5e42888ab93dbe6820310a20ff7a9982b77b2152d9b","glsl-optimizer/src/compiler/glsl/lower_const_arrays_to_uniforms.cpp":"608403f0eeeedf21cfcd3014116e0f44e28cbdf6c4c32aac7e613e64e30205e1","glsl-optimizer/src/compiler/glsl/lower_cs_derived.cpp":"179905cd47a294122adeb5b0abfed6f2f67782dcde21b544d1ee2c1985154e66","glsl-optimizer/src/compiler/glsl/lower_discard.cpp":"3b361b2db0004d544d64611cb50d5a6e364cf6c5f2e60c449085d7d753dd7fb0","glsl-optimizer/src/compiler/glsl/lower_discard_flow.cpp":"f5c29b6a27690bb5c91f196d1a1cf9f6be4f1025292311fe2dac561ce6774dee","glsl-optimizer/src/compiler/glsl/lower_distance.cpp":"a118c85493d5d22b2c059a930c51a5854896d4b1dade76598eaa985e5a3dff8c","glsl-optimizer/src/compiler/glsl/lower_if_to_cond_assign.cpp":"469e617757fd1728709cce021aac5c8da05ee503bf5366977bdc4ef7a6d83950","glsl-optimizer/src/compiler/glsl/lower_instructions.cpp":"6ff5c396abe40d8a2145d571e99e2bbe9143393e15aafc28adc2803a01d821b6","glsl-optimizer/src/compiler/glsl/lower_int64.cpp":"d1ed41196880dd53c7b13e2782f9423f8442bf1d46186e8be92b1b66218a83ee","glsl-optimizer/src/compiler/glsl/lower_jumps.cpp":"34de7b493f281589fb0c2c0f6e885d0a0fabbe7a4e97a73de374dd714777a58c","glsl-optimizer/src/compiler/glsl/lower_mat_op_to_vec.cpp":"dff7a308edc4846c348ed4225c6699a9c75abac68d88f41f85954276552779f4","glsl-optimizer/src/compiler/glsl/lower_named_interface_blocks.cpp":"16063ac127bff75a68272070ab11c21c25101edbff62b4c68f4983b4cd941af0","glsl-optimizer/src/compiler/glsl/lower_offset_array.cpp":"3b00773399135aea85746a5a68b96ef000bc6841be1a2c8e6f25c516628b0949","glsl-optimizer/src/compiler/glsl/lower_output_reads.cpp":"a0fc9975d5aa1617e21fc6c353659a9802da9e83779a3eef4ec584f74b4dadc5","glsl-optimizer/src/compiler/glsl/lower_packed_varyings.cpp":"7550099d4ae123d71541c2fc88bc04fbfe9271ec75d7e210987d1c8cac3cf3ea","glsl-optimizer/src/compiler/glsl/lower_packing_builtins.cpp":"79a13d161fe505a410ab948d92769395708693ec888153630fa240e5b97e356f","glsl-optimizer/src/compiler/glsl/lower_precision.cpp":"f82a185b879872b977a1787d8061b9a80bc4cf8db1b970db6efba2ad9cc20fa2","glsl-optimizer/src/compiler/glsl/lower_shared_reference.cpp":"ea2dccf50a83bc19391bf6b7ab6aa53c0005f427af4066d25140340af9a4beef","glsl-optimizer/src/compiler/glsl/lower_subroutine.cpp":"f69fa53650eeb6f2944fce4d36a6e0a423e6705f3a3bd3389c7fadb83cfc8802","glsl-optimizer/src/compiler/glsl/lower_tess_level.cpp":"b196c9d424c0569f3e85d75c2d125af21566cb113d69036db87c0990703e0fa7","glsl-optimizer/src/compiler/glsl/lower_texture_projection.cpp":"4d247f244272adc8250fd888d8d932a140dd5de4d1efc7a58492c3c2b8291527","glsl-optimizer/src/compiler/glsl/lower_ubo_reference.cpp":"89bdbc6c1669230c644c0857db1ce2781ec61d349ecd08c7914146e1f4750a4a","glsl-optimizer/src/compiler/glsl/lower_variable_index_to_cond_assign.cpp":"fce930f29ac9405b297d1f749d68f59506b89c70b4ee1b1ab8cf49a34cc71ecf","glsl-optimizer/src/compiler/glsl/lower_vec_index_to_cond_assign.cpp":"3c67d851a11a55fad1c49a550f3a0cfe50892d33a3f238ce266cd829eba510a8","glsl-optimizer/src/compiler/glsl/lower_vec_index_to_swizzle.cpp":"f5ec666b73e1415cbab32519a53605ed385f3b03e889560373dbce69dda5000e","glsl-optimizer/src/compiler/glsl/lower_vector.cpp":"f7c13f5572ebe09b6a71553133b2cf003cd4b77b9657600672ee3b21bf890725","glsl-optimizer/src/compiler/glsl/lower_vector_derefs.cpp":"b05793da6dd620a531b43df5af8b2ecbc37b9db0c88910f5724ea10bcd057e19","glsl-optimizer/src/compiler/glsl/lower_vector_insert.cpp":"fee772ec17eea5e86a529bf9c5fa2ee0d29a5982bb75ebc6d68ed36cd19aa299","glsl-optimizer/src/compiler/glsl/lower_vertex_id.cpp":"690e8715182e03fead5cc5a35251fb4f41b357e4c71a1dfbc4bd7be19862b56d","glsl-optimizer/src/compiler/glsl/lower_xfb_varying.cpp":"58c0e8b270e4bbde54250be03cdb2f36966bcafb785372ad2e2b786835df7f9f","glsl-optimizer/src/compiler/glsl/main.cpp":"ae5e88abbbc8a12f769e1296bad938b9d7398cc6da0d3d0caeceeeb876536850","glsl-optimizer/src/compiler/glsl/opt_add_neg_to_sub.h":"f5054944bfd068810629080d0ea11df78b3f57a8f86df75e13ca50157ad1964d","glsl-optimizer/src/compiler/glsl/opt_algebraic.cpp":"25f45b20e1972ee8c789177a1aeda6e4286c25db2eae3a43ff83029ae64969c0","glsl-optimizer/src/compiler/glsl/opt_array_splitting.cpp":"19d3ce0e815438f4df9ab2890e767b03a4f3f191b53bb30c0217cf2ae6a95430","glsl-optimizer/src/compiler/glsl/opt_conditional_discard.cpp":"0e44e0e126711a3725c1f3a2aa65ff03c381fed08680ffc30101aae60f716c4e","glsl-optimizer/src/compiler/glsl/opt_constant_folding.cpp":"a088d04d9b45f9e55e235835648f614c89b7803c03a6d4f6a6d1a6bc1f0228bd","glsl-optimizer/src/compiler/glsl/opt_constant_propagation.cpp":"8a9440d77ecd6dcf13e683cbb99943aab6311c8fd4b5f6a9189a8d4f270746f4","glsl-optimizer/src/compiler/glsl/opt_constant_variable.cpp":"63d3ccd4dd09f19c9cf1a2f51592111bed41284504f29f3c0de4cadebc439a37","glsl-optimizer/src/compiler/glsl/opt_copy_propagation_elements.cpp":"ffa0f50863995e0d2e31f55a52e82319edc71e520987bebd7f7e561ea331c64b","glsl-optimizer/src/compiler/glsl/opt_dead_builtin_variables.cpp":"84e8747b948232f01dd56b428b9315f96f9511f605f240119fc446fae28981a9","glsl-optimizer/src/compiler/glsl/opt_dead_builtin_varyings.cpp":"761523e88f5b3ba785170f4d7205e94fa99acb7e74d29efbe40e1c010e1dbdb3","glsl-optimizer/src/compiler/glsl/opt_dead_code.cpp":"fd1ba2da7337d4e5dad17f5c2d73d9cc8880305f423e85d64cf94553588fa401","glsl-optimizer/src/compiler/glsl/opt_dead_code_local.cpp":"969a598b4df322baf222258a66cd64a326ea20e5b3125be9d8d1771f522c69e0","glsl-optimizer/src/compiler/glsl/opt_dead_functions.cpp":"774cae6536d02edf26e996a2a895e1f62d5098f16dc96b44798b4fc731a9a95f","glsl-optimizer/src/compiler/glsl/opt_flatten_nested_if_blocks.cpp":"3696a5c55f02e20056e085bc2714f73ac992f221b6f3387d655068e86b512046","glsl-optimizer/src/compiler/glsl/opt_flip_matrices.cpp":"44f0fe05b49329667671f88c96dc86ab3fe1459ff7b87f2b2d88de2d49829f9f","glsl-optimizer/src/compiler/glsl/opt_function_inlining.cpp":"fb56a33c90419a01676b57cbd91d0674a54cca40e6defaacc88dd33facebc131","glsl-optimizer/src/compiler/glsl/opt_if_simplification.cpp":"ac406eb35e379c357641d6c5749f50c65961455924d3dc884e2b90046fa92c5c","glsl-optimizer/src/compiler/glsl/opt_minmax.cpp":"8abd59d3b14ef60ff14a9c69660e6945f5cf10b97edb4afebe56be3f81d96316","glsl-optimizer/src/compiler/glsl/opt_rebalance_tree.cpp":"8bb6329dc0f299042368fc81934c2df019b45ab9f7aa0415d4e57b8d1ff98c9f","glsl-optimizer/src/compiler/glsl/opt_redundant_jumps.cpp":"222c73e2ac7a938ebb6428cc6c780c908ff6156d8ff935b04fed93a48fc10496","glsl-optimizer/src/compiler/glsl/opt_structure_splitting.cpp":"2edc79cc13f3177934e0443ad62f5976a1991f01f86ea303a803434849b13a47","glsl-optimizer/src/compiler/glsl/opt_swizzle.cpp":"015d0abddfe507f67c4b96c82988d861d018ededf7bf055e2bcbe9ea92da694e","glsl-optimizer/src/compiler/glsl/opt_tree_grafting.cpp":"46d28ac983ea244a4315bdc0e8892979ec4d1f9b9a96ac8a8a08006d9bc5e878","glsl-optimizer/src/compiler/glsl/opt_vectorize.cpp":"d80ee43bb97d9f016fb9c5e1e06f5b2afa569811f368ba067be794ec11d085fb","glsl-optimizer/src/compiler/glsl/program.h":"2982447e2abd35371e273ad87951722782a8b21c08294f67c39d987da1e1c55f","glsl-optimizer/src/compiler/glsl/propagate_invariance.cpp":"080943e21baa32494723a2eefb185915d2daae1f46d6df420145c5ad6857e119","glsl-optimizer/src/compiler/glsl/s_expression.cpp":"1ced972bc6ecc8eab4116ea71fb0212ab9ae5bcc0be3b47aa5d9d903566b3af1","glsl-optimizer/src/compiler/glsl/s_expression.h":"65b847e30e22a809b57d0bc70243049c99d9c6318803c5b8d0826aba55dc217e","glsl-optimizer/src/compiler/glsl/serialize.cpp":"be0eb4251348a9d921acb839a5c48c6023a2e9d116d602bb0432787ab623655d","glsl-optimizer/src/compiler/glsl/serialize.h":"57425732eba1233d928e5f07f88b623ce65af46b3bb034bf147f0a4b7f94f9a1","glsl-optimizer/src/compiler/glsl/shader_cache.cpp":"e0c5c433f2df3fccdf1d61281bfcb0ee5633433339b97c697d64db99611cbaaf","glsl-optimizer/src/compiler/glsl/shader_cache.h":"9217164d8d7f54aca0fe5922c7187095a6ae0cb703b196b79805aeef07a7e697","glsl-optimizer/src/compiler/glsl/standalone.cpp":"8e6c416a14d631261917a5fe4cc91880c287b22b2dfd70eb22028289a8fa5364","glsl-optimizer/src/compiler/glsl/standalone.h":"a7c397d1dfdd1e7fb2cfe99db35cd9df93251e642059208533202b7f20497f83","glsl-optimizer/src/compiler/glsl/standalone_scaffolding.cpp":"970d14b7a9d58e5270321f97bf5d57795558b1c570a56678e04a65b26c60bf4f","glsl-optimizer/src/compiler/glsl/standalone_scaffolding.h":"d921a617ea82b9e49413314492a645c44356de503581b1be3f1b57de236e480d","glsl-optimizer/src/compiler/glsl/string_to_uint_map.cpp":"d824bf5b839bd39498dc9e457103cdbe3e5289ddf7564107c27b1505948dd31f","glsl-optimizer/src/compiler/glsl/string_to_uint_map.h":"e2f18e66359c9d620e085de7f4a334a47df9c66e65a5bfe8b734c627bec04104","glsl-optimizer/src/compiler/glsl/test_optpass.h":"b27b8f35f5387e7ce4982bb51c7b63ccf14f91757f3108a5d02ed006925bb8a0","glsl-optimizer/src/compiler/glsl/xxd.py":"376484142f27f45090ea8203ae2621abf73f06175cb0ee8d96f44a3b9327f4bd","glsl-optimizer/src/compiler/glsl_types.cpp":"044bb6754f45419a3151e7a25c39202a82009ae3c6bc54ff7f0bb4258a5deefe","glsl-optimizer/src/compiler/glsl_types.h":"fd899a42f34ddeb8601bc3cd6c5e3aed82fc8aef4042dde1b39b3c01e1dcc219","glsl-optimizer/src/compiler/shader_enums.c":"436bff5216b11b0980bdfada5885fc6ac9afa2037a3027fcd6eea2a8635597ac","glsl-optimizer/src/compiler/shader_enums.h":"13220442a5c02e83540cf2c0ad4f8417b2fbda5f2586dec4e92082544c937cdd","glsl-optimizer/src/compiler/shader_info.h":"4c5453e81197ca83593ee4f365074b23530f2ab21c78e1733b63dec6f344c12a","glsl-optimizer/src/gallium/auxiliary/util/u_half.h":"3c2b37bda3ccb64387e44b723d29cf9046decab1a893bf42d842e9603398bdee","glsl-optimizer/src/gallium/include/pipe/p_compiler.h":"c75620096ce8523dae90599e50aa2ef6468d3b0e368a77795edeb20dd1abfc0c","glsl-optimizer/src/gallium/include/pipe/p_config.h":"a27692fc35f9e55df3224b7529e66b3001e911e94e6bc5f8f569e493e1ee3fb7","glsl-optimizer/src/gallium/include/pipe/p_defines.h":"be26d68c0acc67c5e44788c6299716a9eee415fd81d7d747e3738a829e3b6b38","glsl-optimizer/src/gallium/include/pipe/p_format.h":"5674215fc41d27496f037cf837717daefbf23ebb38d40ace7c0c414bc08182b0","glsl-optimizer/src/gallium/include/pipe/p_state.h":"d600593aba5f5a17072a6c38f6baa81e01c7994b0174250f7e433bb41684b702","glsl-optimizer/src/mapi/glapi/glapi.h":"73632a625c0ddabc401205e8b5a81eb8af8506868efe4b170d7979ec3619e9c5","glsl-optimizer/src/mesa/main/config.h":"5800259373099e5405de2eb52619f9de242552a479902a3a642a333c8cb3c1e7","glsl-optimizer/src/mesa/main/context.c":"2f3208473d99c94f734b1137ba91889d4a1babb9e7534bf1dc85d851ee98274e","glsl-optimizer/src/mesa/main/context.h":"cc7e4194797db9d007f01884e23d786c453b3860821f7f2ddcdf0f1bf3f8ffb1","glsl-optimizer/src/mesa/main/dd.h":"6a964acd06b6c2d88700e69fb75fe3c6b3b3d45bbc41db24f3f897a29695fe0c","glsl-optimizer/src/mesa/main/debug_output.h":"7312422e90b8c0e34028ac27280e438139b5cba525c99deb3ac883cd3d87e452","glsl-optimizer/src/mesa/main/draw.h":"7eaef3a9e27a60ea6f7937109bf3a6190b831162fde0479abb12077ce27c353d","glsl-optimizer/src/mesa/main/enums.h":"87d562a6764f51c014a2274fa7c3aca17c04441537ddd56b2554f13c6fffea92","glsl-optimizer/src/mesa/main/errors.h":"c79444b5df289c90fbb22a33b2d0c23917d9fc4510960088f0b79e53bb56b1b2","glsl-optimizer/src/mesa/main/extensions.h":"a38b2f87cc93c513994281350d69e06c84ff8eded5313ec0a1be33f375e0ebbd","glsl-optimizer/src/mesa/main/extensions_table.c":"17642d1a8c9a0bf2bd61060052d33ff14a005d2b962e6cf91465797a50851e85","glsl-optimizer/src/mesa/main/extensions_table.h":"2c879571c238d2e14461031ac740372fd0f9ac3a34c0d5541bb9b7ed4c0376c8","glsl-optimizer/src/mesa/main/formats.h":"02e2f7ec3e39286cf9f27e2641043e6df8ecb1dfde9e643313210e214af2a929","glsl-optimizer/src/mesa/main/glheader.h":"58217b33eead6aa6b23cd4a291cefeaa6cb84e465f4960daffca97c44d6d1c35","glsl-optimizer/src/mesa/main/glthread.h":"51fb2711f77e7eafcfc52d29d5b844978832b24c930d88accd48d143a6eb9c6f","glsl-optimizer/src/mesa/main/hash.h":"7e7f782034c16a8e693de48e00c31d4a90b0129f4029fd074033d7d16ccbe718","glsl-optimizer/src/mesa/main/macros.h":"73d15ddfd64f2b57b9b2ffeeb993b9c2c0899a80563e9d6ff337b11ccbe6eee5","glsl-optimizer/src/mesa/main/menums.h":"5dfac0e2279d60b0cd0c7b9fc2a5021620d0f6282ed2e738c420214e3af152d3","glsl-optimizer/src/mesa/main/mesa_private.h":"edda678b93438944279a551f663b8858ad84814a9fc88ba9672ef195599c24ae","glsl-optimizer/src/mesa/main/mtypes.h":"6efddefa099e4d2e3fdd97f0055644f47aba21711385edfeabc2d9b0676f2eec","glsl-optimizer/src/mesa/main/shaderobj.h":"9f0dfe96d0c2154201adef942bd36053533ac7b2492fb3786acda5bea514c75e","glsl-optimizer/src/mesa/main/uniforms.h":"4e331e6ad6e9cbded978b4082dbe0a57c1f8f01327446bb6892bfc179976c38b","glsl-optimizer/src/mesa/main/version.h":"9d0a13a758099302dc55cf7d045791834a89b0f9d4cf17b2692259b369a8a9a1","glsl-optimizer/src/mesa/math/m_matrix.h":"a37b19f182e070db3df93b0ede43c22fb8be8c2906504133ee6dbd7db1185d8b","glsl-optimizer/src/mesa/program/dummy_errors.c":"1820e305515b4c5e041f5e1623266a48ec8f076a155310be7d60637101f593e4","glsl-optimizer/src/mesa/program/ir_to_mesa.h":"b47f58d22e3ca2ae42d52501ea769d15c4476834944fa97eeccd3a3439211d00","glsl-optimizer/src/mesa/program/prog_instruction.h":"ab3832152a7e144b59e5a2264b2c29db56d93be31e76bbd958527a56771b40eb","glsl-optimizer/src/mesa/program/prog_parameter.h":"ba18c743284eadbc837c2c364c73e5d372321a7637a76e589d8d39fe8b5de225","glsl-optimizer/src/mesa/program/prog_statevars.h":"fc413698f84bc52d45fdeae0471934ee9904bfb7eac1a2b5f70446e54bcbbdca","glsl-optimizer/src/mesa/program/program.h":"1f01026a4eff440a3f122fd9b519d03546fe7f7d8be60dca834e95a2f8fbbfd2","glsl-optimizer/src/mesa/program/symbol_table.c":"6611cb9f078035bf5ff8c9112093a6c7d99f8af99a3931d0c07f227cc72283ea","glsl-optimizer/src/mesa/program/symbol_table.h":"631dc35ac48d5e87962d45507461920f6575610960ffcc42a08cefeb43300cda","glsl-optimizer/src/mesa/vbo/vbo.h":"6eb1dcd9a08c92f276c5fe08da184ff9d455d1be421913b8ad732a7b65e858fb","glsl-optimizer/src/util/bitscan.h":"d4fcb47b57a50d70cb97f99ca3e619bc06282a877768a435e009775ce8d77f36","glsl-optimizer/src/util/bitset.h":"c40f78515c6230fed18345c6751ce33833a49da7a27901c7e6d7340cbdcbc5e7","glsl-optimizer/src/util/blob.c":"8f729846f66efc9c15485cc5fc24c6ec861fc1fecb2f652573f2a237d481b791","glsl-optimizer/src/util/blob.h":"93e1eaac866b9a7cd6fc03b533c18fb2edf0e97f03395eff4f3a605c4fc14d0c","glsl-optimizer/src/util/compiler.h":"79e3bf40a5bab704e6c949f23a1352759607bb57d80e5d8df2ef159755f10b68","glsl-optimizer/src/util/crc32.c":"2f3467a046b3a76784ecb9aa55d527698c8607fd0b12c622f6691aaa77b58505","glsl-optimizer/src/util/crc32.h":"59bd81865e51042b73a86f8fb117c312418df095fed2d828c5c1d1c8b6fc6cd4","glsl-optimizer/src/util/debug.c":"c3d68e9752ccc19e66c669562cd113cf1d0ac83cbb30174789e7fb8d1df58f9c","glsl-optimizer/src/util/debug.h":"50068d745c4199ccbd33d68dd4c8a36d2b5179c7869a21e75906ddd0718ca456","glsl-optimizer/src/util/detect_os.h":"343a8790d17a3710c6dd015ee367f84e3902ff3f2e36faca2bf93f9d725d3574","glsl-optimizer/src/util/disk_cache.c":"f533937e5a4fffe76e2739ef4b6b1e1da097d96d63eb808e68ebbc7027641c23","glsl-optimizer/src/util/disk_cache.h":"e83314fb14134a8e079b15e470a6376ba5a8253701f048c890a62b7e55d64bc8","glsl-optimizer/src/util/fast_urem_by_const.h":"e108fce804616c47d071dfe4a04163eec1126e448ed1aa89abb6b3a6d772bd5b","glsl-optimizer/src/util/fnv1a.h":"ab2596f19c6adf431ae27618f62c5743e24ad23ef83bb359a4c4c218245ab459","glsl-optimizer/src/util/format/u_format.h":"4cdfc0c59cbc99a092e5ec5a396910f2d93b9643e5d8141050b011e66f11e45b","glsl-optimizer/src/util/futex.h":"26f7c9d86e9ffef4c0fa2761f1aaa1918337302e20bd6ca10e61dc3c47356deb","glsl-optimizer/src/util/half_float.c":"11bc2584493d5d9d46e8c8a619a0307cf150bf5ab5d0f96bb764b061dc37a00e","glsl-optimizer/src/util/half_float.h":"7f7c380f126da1400a91758cc0392f24bf967bce1672890b62be26fe9fbd922b","glsl-optimizer/src/util/hash_table.c":"0ca40352e35dedab0a84c64c903f1b16d47e950bb5f43b4d22bb57d499bfea6e","glsl-optimizer/src/util/hash_table.h":"217191bb360592e2232f187473c10287d2cda8ae6fa5c53d0ef74c8c206118b4","glsl-optimizer/src/util/list.h":"9fab03c6a78186bb5f173269f825f6ce976b409d931852e3d93bac632e07989a","glsl-optimizer/src/util/macros.h":"63faf65b51058c483b17f1f77da51d1c53c8beab52678cb6bd01f1228a63b6b0","glsl-optimizer/src/util/mesa-sha1.c":"00c692ec353ebc02c06c57c5a71de0ab7a119f86a4146f452e65ec87e4944417","glsl-optimizer/src/util/mesa-sha1.h":"bff4c29f4bf7cdbcefb30fa0c996a7604a380eba8976467c2a60e7cd328f7e26","glsl-optimizer/src/util/mesa-sha1_test.c":"25da89a59d51469f77b4c468ca23ffdce0a7a1166a70b6cc23026a6800b0143c","glsl-optimizer/src/util/os_memory.h":"64555faf1760ae6954f42c83727c38dfc4c278e9152115779ffaad58b42adacf","glsl-optimizer/src/util/os_memory_aligned.h":"12d86fa94be38c13f7eeebdf313795e1267dd5a7187d2f0072e0e896f41702f6","glsl-optimizer/src/util/os_memory_stdc.h":"07360363b88c927065e10df71bebf6c8a0cc3b9167c9dfce55f2d65f11e6f787","glsl-optimizer/src/util/os_misc.c":"a9936e613ec84803abd59ad47c192c8e3939993c950ac91973fdc4cec1801bb8","glsl-optimizer/src/util/os_misc.h":"cc68eb12e05b5e749c54298cb4a6f4cd20cc5af7db3403e70b3c27b56090c740","glsl-optimizer/src/util/os_time.h":"73e775f7335244ff5964c678c27eedf1aea6abea44c4169d327ea8c7ce4a3a88","glsl-optimizer/src/util/ralloc.c":"4b51189595ef67bcef52c40cbf654d969041dbd15e15d4a893ad494ac060aeca","glsl-optimizer/src/util/ralloc.h":"e573c45875ff1530f0dbee9a93ae55535fdac8d5cc88a79ebc327c688824bde5","glsl-optimizer/src/util/rounding.h":"0450722353caf83de07e67f335949dbe95fe53b534052d4ee9d28d2781387614","glsl-optimizer/src/util/set.c":"86f8c9a830bead5a5a79bc970b0ff97809312af07b3beb39ef9d90af04d40a1b","glsl-optimizer/src/util/set.h":"3e39ca161e7ed4ec7c436cc9c7919ed9a55ed1b71edbf2caf6f9bcfd9bc578ed","glsl-optimizer/src/util/sha1/README":"00af7419af05247081858acb2902efd99fcda2ce16e331079f701645bb3729c0","glsl-optimizer/src/util/sha1/sha1.c":"1403bbe0aad42ba3e6be7e09f7cad87a6a8c4ad5b63962f7b92b9f37d8133b04","glsl-optimizer/src/util/sha1/sha1.h":"68d9f240eab2918026ecdf22be36811abbd4f1389f6c36e31258041aeaedd247","glsl-optimizer/src/util/simple_mtx.h":"12c6c3c4b7db9168bc656d5b3c65912075084d2b388c415d5c3d3f5953a9d6c7","glsl-optimizer/src/util/softfloat.c":"a97e51a96fe5e6a052c02aa6bbec683fe73fb88a8c087d9c930503e2120d8a2e","glsl-optimizer/src/util/softfloat.h":"66664b0250e83bf5dd4cc743acd119d076efcea624a0eab3d6b60718e6ee8811","glsl-optimizer/src/util/string_buffer.c":"63a1d1b1e34926c88ea00159cafbcd56568b805c4f64d1e8c97169fe313921fc","glsl-optimizer/src/util/string_buffer.h":"7b88d1b1d9c6cfb8e93331813535c127289437c75f822029e9a3bca8ea6b52ee","glsl-optimizer/src/util/strndup.h":"0273c4fdb7482cd7746881a63d3998648c6d63415ba85af1d1860f0e0dc504c6","glsl-optimizer/src/util/strtod.c":"5cf610d8a37373cf37cfb7aae903525d943b2674b1f32594c70b0eb19a8c9697","glsl-optimizer/src/util/strtod.h":"237396def4e264d35ed4bedea00ef9a4ceab6d7a11a18c770d9747d22c69ed2d","glsl-optimizer/src/util/u_atomic.h":"c02e809526c6c09ba8fe51f50b2490d1b6c8e5c7f3c4031ae958250d098fc3bb","glsl-optimizer/src/util/u_debug.c":"8c060e379b816618f3dd22c9ea523c68b9425c76c36a7dfe5d6d375b337f5f4a","glsl-optimizer/src/util/u_debug.h":"e11e26edd9b9e4e6f8e6a435e69f4d9edda27e9a379f68f4c82ea2525aaaea68","glsl-optimizer/src/util/u_dynarray.h":"853d0fa6ff2261614488be624deb8a2b01e57c2c8eabc28578cbeed4ccc95694","glsl-optimizer/src/util/u_endian.h":"3ccea7e529740318d8a4b05c00db3adc9d1e292a52bdc56a05c9fae99209720f","glsl-optimizer/src/util/u_math.c":"c868a8c0886dc78f1b06b13404ba8b253090449045774dd56893ac9d75795184","glsl-optimizer/src/util/u_math.h":"a04e32e126db016413f9de0a2028a3e71737137463b1289eae576f884b06fcf1","glsl-optimizer/src/util/u_memory.h":"c5db17c724c70283ddbe04165722f6988d4e0eb9aa3602ae472feff016649af9","glsl-optimizer/src/util/u_queue.h":"92930ce236c0528a98b695f5cea8c5c6aa9683beaf71a2227bdc5d33d1b21506","glsl-optimizer/src/util/u_string.h":"c5a2f4ef576d1547bda12c4ea219179fefa54414977743ac094abcaf696ef6ca","glsl-optimizer/src/util/u_thread.h":"00b708459b27f9910d18db92c18cc65cfc618ac2b3cd144e45f8640057b10d58","glsl-optimizer/src/util/xxhash.h":"2f2aff2fc6c0c929f52cf6ae7314122124c5be026d41ad1c357608383c4a37ad","src/bindings.rs":"79993db2058bde39f99ef483d02560d33b1cb882f6a552319e8b86eb6f9021e1","src/lib.rs":"04be1554cd829eb40864b06d80b491dd48117a4e3a601c7d482117f7a0391e67","wrapper.hpp":"f3ea34cc496f7d90b9bfcada3250b37b314c3524dac693b2ece9517bc7d274ac"},"package":"ee5be629003d587bab188f3e2e3b010aa2cde7c41ec967b3a244f388d4d81877"} \ No newline at end of file
diff --git a/third_party/rust/glslopt/Cargo.toml b/third_party/rust/glslopt/Cargo.toml
index 2ecdbb976a..206ad8f149 100644
--- a/third_party/rust/glslopt/Cargo.toml
+++ b/third_party/rust/glslopt/Cargo.toml
@@ -3,21 +3,28 @@
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
-# to registry (e.g., crates.io) dependencies
+# to registry (e.g., crates.io) dependencies.
#
-# If you believe there's an error in this file please file an
-# issue against the rust-lang/cargo repository. If you're
-# editing this file be aware that the upstream Cargo.toml
-# will likely look very different (and much more reasonable)
+# If you are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
[package]
edition = "2018"
name = "glslopt"
-version = "0.1.9"
+version = "0.1.10"
authors = ["Jamie Nicol <jnicol@mozilla.com>"]
description = "Optimizes GLSL shader code"
-keywords = ["opengl", "gl", "gles", "glsl", "shader"]
+readme = "README.md"
+keywords = [
+ "opengl",
+ "gl",
+ "gles",
+ "glsl",
+ "shader",
+]
license = "MIT"
repository = "https://github.com/jamienicol/glslopt-rs"
+
[build-dependencies.cc]
version = "1.0"
diff --git a/third_party/rust/glslopt/glsl-optimizer/include/c99_alloca.h b/third_party/rust/glslopt/glsl-optimizer/include/c99_alloca.h
deleted file mode 100644
index 5a3b8c19ab..0000000000
--- a/third_party/rust/glslopt/glsl-optimizer/include/c99_alloca.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/**************************************************************************
- *
- * Copyright 2015 VMware, Inc.
- * All Rights Reserved.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the
- * "Software"), to deal in the Software without restriction, including
- * without limitation the rights to use, copy, modify, merge, publish,
- * distribute, sub license, and/or sell copies of the Software, and to
- * permit persons to whom the Software is furnished to do so, subject to
- * the following conditions:
- *
- * The above copyright notice and this permission notice (including the
- * next paragraph) shall be included in all copies or substantial portions
- * of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
- * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
- * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
- * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- *
- **************************************************************************/
-
-#ifndef _C99_ALLOCA_H_
-#define _C99_ALLOCA_H_
-
-
-#if defined(_MSC_VER)
-
-# include <malloc.h>
-
-# define alloca _alloca
-
-#elif defined(__sun) || defined(__CYGWIN__)
-
-# include <alloca.h>
-
-#else /* !defined(_MSC_VER) */
-
-# include <stdlib.h>
-
-#endif /* !defined(_MSC_VER) */
-
-
-#endif
diff --git a/third_party/rust/glslopt/glsl-optimizer/include/c99_math.h b/third_party/rust/glslopt/glsl-optimizer/include/c99_math.h
deleted file mode 100644
index e906c26aa5..0000000000
--- a/third_party/rust/glslopt/glsl-optimizer/include/c99_math.h
+++ /dev/null
@@ -1,211 +0,0 @@
-/**************************************************************************
- *
- * Copyright 2007-2015 VMware, Inc.
- * All Rights Reserved.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the
- * "Software"), to deal in the Software without restriction, including
- * without limitation the rights to use, copy, modify, merge, publish,
- * distribute, sub license, and/or sell copies of the Software, and to
- * permit persons to whom the Software is furnished to do so, subject to
- * the following conditions:
- *
- * The above copyright notice and this permission notice (including the
- * next paragraph) shall be included in all copies or substantial portions
- * of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
- * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
- * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
- * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- *
- **************************************************************************/
-
-/**
- * Wrapper for math.h which makes sure we have definitions of all the c99
- * functions.
- */
-
-
-#ifndef _C99_MATH_H_
-#define _C99_MATH_H_
-
-#include <math.h>
-#include "c99_compat.h"
-
-
-/* This is to ensure that we get M_PI, etc. definitions */
-#if defined(_MSC_VER) && !defined(_USE_MATH_DEFINES)
-#error _USE_MATH_DEFINES define required when building with MSVC
-#endif
-
-
-#if !defined(_MSC_VER) && \
- __STDC_VERSION__ < 199901L && \
- (!defined(_XOPEN_SOURCE) || _XOPEN_SOURCE < 600) && \
- !defined(__cplusplus)
-
-static inline long int
-lrint(double d)
-{
- long int rounded = (long int)(d + 0.5);
-
- if (d - floor(d) == 0.5) {
- if (rounded % 2 != 0)
- rounded += (d > 0) ? -1 : 1;
- }
-
- return rounded;
-}
-
-static inline long int
-lrintf(float f)
-{
- long int rounded = (long int)(f + 0.5f);
-
- if (f - floorf(f) == 0.5f) {
- if (rounded % 2 != 0)
- rounded += (f > 0) ? -1 : 1;
- }
-
- return rounded;
-}
-
-static inline long long int
-llrint(double d)
-{
- long long int rounded = (long long int)(d + 0.5);
-
- if (d - floor(d) == 0.5) {
- if (rounded % 2 != 0)
- rounded += (d > 0) ? -1 : 1;
- }
-
- return rounded;
-}
-
-static inline long long int
-llrintf(float f)
-{
- long long int rounded = (long long int)(f + 0.5f);
-
- if (f - floorf(f) == 0.5f) {
- if (rounded % 2 != 0)
- rounded += (f > 0) ? -1 : 1;
- }
-
- return rounded;
-}
-
-static inline float
-exp2f(float f)
-{
- return powf(2.0f, f);
-}
-
-static inline double
-exp2(double d)
-{
- return pow(2.0, d);
-}
-
-#endif /* C99 */
-
-
-/*
- * signbit() is a macro on Linux. Not available on Windows.
- */
-#ifndef signbit
-#define signbit(x) ((x) < 0.0f)
-#endif
-
-
-#ifndef M_PI
-#define M_PI (3.14159265358979323846)
-#endif
-
-#ifndef M_E
-#define M_E (2.7182818284590452354)
-#endif
-
-#ifndef M_LOG2E
-#define M_LOG2E (1.4426950408889634074)
-#endif
-
-#ifndef FLT_MAX_EXP
-#define FLT_MAX_EXP 128
-#endif
-
-
-#if defined(fpclassify)
-/* ISO C99 says that fpclassify is a macro. Assume that any implementation
- * of fpclassify, whether it's in a C99 compiler or not, will be a macro.
- */
-#elif defined(__cplusplus)
-/* For C++, fpclassify() should be defined in <cmath> */
-#elif defined(_MSC_VER)
-/* Not required on VS2013 and above. Oddly, the fpclassify() function
- * doesn't exist in such a form on MSVC. This is an implementation using
- * slightly different lower-level Windows functions.
- */
-#include <float.h>
-
-static inline enum {FP_NAN, FP_INFINITE, FP_ZERO, FP_SUBNORMAL, FP_NORMAL}
-fpclassify(double x)
-{
- switch(_fpclass(x)) {
- case _FPCLASS_SNAN: /* signaling NaN */
- case _FPCLASS_QNAN: /* quiet NaN */
- return FP_NAN;
- case _FPCLASS_NINF: /* negative infinity */
- case _FPCLASS_PINF: /* positive infinity */
- return FP_INFINITE;
- case _FPCLASS_NN: /* negative normal */
- case _FPCLASS_PN: /* positive normal */
- return FP_NORMAL;
- case _FPCLASS_ND: /* negative denormalized */
- case _FPCLASS_PD: /* positive denormalized */
- return FP_SUBNORMAL;
- case _FPCLASS_NZ: /* negative zero */
- case _FPCLASS_PZ: /* positive zero */
- return FP_ZERO;
- default:
- /* Should never get here; but if we do, this will guarantee
- * that the pattern is not treated like a number.
- */
- return FP_NAN;
- }
-}
-#else
-#error "Need to include or define an fpclassify function"
-#endif
-
-
-/* Since C++11, the following functions are part of the std namespace. Their C
- * counteparts should still exist in the global namespace, however cmath
- * undefines those functions, which in glibc 2.23, are defined as macros rather
- * than functions as in glibc 2.22.
- */
-#if __cplusplus >= 201103L && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 23))
-#include <cmath>
-
-using std::fpclassify;
-using std::isfinite;
-using std::isinf;
-using std::isnan;
-using std::isnormal;
-using std::signbit;
-using std::isgreater;
-using std::isgreaterequal;
-using std::isless;
-using std::islessequal;
-using std::islessgreater;
-using std::isunordered;
-#endif
-
-
-#endif /* #define _C99_MATH_H_ */
diff --git a/third_party/rust/glslopt/glsl-optimizer/src/compiler/glsl/lower_instructions.cpp b/third_party/rust/glslopt/glsl-optimizer/src/compiler/glsl/lower_instructions.cpp
index c549d16d2a..3bf6959e7f 100644
--- a/third_party/rust/glslopt/glsl-optimizer/src/compiler/glsl/lower_instructions.cpp
+++ b/third_party/rust/glslopt/glsl-optimizer/src/compiler/glsl/lower_instructions.cpp
@@ -118,7 +118,7 @@
* Converts double trunc, ceil, floor, round to fract
*/
-#include "c99_math.h"
+#include <math.h>
#include "program/prog_instruction.h" /* for swizzle */
#include "compiler/glsl_types.h"
#include "ir.h"
diff --git a/third_party/rust/glslopt/glsl-optimizer/src/util/rounding.h b/third_party/rust/glslopt/glsl-optimizer/src/util/rounding.h
index e329d43824..f897ec7969 100644
--- a/third_party/rust/glslopt/glsl-optimizer/src/util/rounding.h
+++ b/third_party/rust/glslopt/glsl-optimizer/src/util/rounding.h
@@ -24,9 +24,8 @@
#ifndef _ROUNDING_H
#define _ROUNDING_H
-#include "c99_math.h"
-
#include <limits.h>
+#include <math.h>
#include <stdint.h>
#if defined(__SSE__) || (defined(_M_IX86_FP) && (_M_IX86_FP >= 1)) || defined(_M_X64)
diff --git a/third_party/rust/glslopt/glsl-optimizer/src/util/u_math.h b/third_party/rust/glslopt/glsl-optimizer/src/util/u_math.h
index 59266c1692..42d9e348ec 100644
--- a/third_party/rust/glslopt/glsl-optimizer/src/util/u_math.h
+++ b/third_party/rust/glslopt/glsl-optimizer/src/util/u_math.h
@@ -38,10 +38,9 @@
#ifndef U_MATH_H
#define U_MATH_H
-
-#include "c99_math.h"
#include <assert.h>
#include <float.h>
+#include <math.h>
#include <stdarg.h>
#include "bitscan.h"
diff --git a/third_party/rust/litrs/.cargo-checksum.json b/third_party/rust/litrs/.cargo-checksum.json
new file mode 100644
index 0000000000..d12ccffda0
--- /dev/null
+++ b/third_party/rust/litrs/.cargo-checksum.json
@@ -0,0 +1 @@
+{"files":{"CHANGELOG.md":"03cea7c394dd09087f6b2c7ba4b4641b5c2c50b32b7286cabd5be4850f62f170","Cargo.toml":"6ef884164a0139f0591a381ada2c99d850d38e5f3af3451efa12f808f8a799e0","LICENSE-APACHE":"62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a","LICENSE-MIT":"7dc1552e88f49132cb358b1b962fc5e79fa42d70bcbb88c526d33e45b8e98036","README.md":"533d31adf3b4258b838cd6a1cdb58139e2cf761c3c38aa4654f66f34335c9073","src/bool/mod.rs":"53c6eedfd94552689e51233fffb8a99ce9321a32db0f08de8b18d48cda9b1877","src/bool/tests.rs":"a0e6d034036aa04aac6b847bb561bdba759d85c78d4cbb7fb93f4422efb83656","src/byte/mod.rs":"ff2a3e6108a9b32ae0d925ec34735d20194d5c6b27af060516a46d21397c75be","src/byte/tests.rs":"ac36dace42cd151ac9d26cc35701bc8b65f8f1ed6ee1cfef4eeb6caa9dd702bc","src/bytestr/mod.rs":"8fd951374f7edc2077465cd4f97001eece46358f2bb0c45fddb2942aac6ee13b","src/bytestr/tests.rs":"194b28f157196260b1c2a612dfb36fb1dace491db2ed2bbb39227771ed6baf60","src/char/mod.rs":"2bb6f25da83670f18ec40f8a38565aa2294a4cdf81c8bbaf081531a32b6c6d0c","src/char/tests.rs":"9de497c8c7d7a139ff81f3d7bf8b5c682316d983bebb58c58d2af97f4cd26c35","src/err.rs":"54d000c4f37258c6886dd5b7069e2f5282e51aec3731feb77935582ae8c18908","src/escape.rs":"a944e95344df54c16bf4cc6a8fb01a81e2eac2aacd4758b938d3339212fce60c","src/float/mod.rs":"defaf83526acdc8f9b34c7d1ac17d866a93409dc392eb608160778d6bb4a1e25","src/float/tests.rs":"5875403f1a72104973ed83d0cf29d766e7b2fa5c23615c85a5f2eeed02b115c9","src/impls.rs":"c5dd37dd3ecd29c40a0ed243b907765a27729a1b1f73fa2c6762105feb6527bc","src/integer/mod.rs":"2b9109ddd34faf76fc9ce9dfb04bcc6aed4834231c74bd8a774bd256cc57c18a","src/integer/tests.rs":"01147ce9b6742bb1614cf863090699c54bf660b9f2c6a5eb529d67ae92230c0d","src/lib.rs":"2e79c8035d0fb77db9414b5569eeef13b6db8cde48ef2a45ffcf5f2492d02a4a","src/parse.rs":"e1fa4a76331d52f711e1b06cdba853a4f815281366f4f4f68b4c0a109f8a1734","src/string/mod.rs":"52a9cda38f7cd5b025bc5ec7edb8106487ba3d141789f5bc239c4561490cdc29","src/string/tests.rs":"1e0150ddd921a74ed5ebf6216708132d7768f3beb11a8c7bbfcf4ba01db40a5b","src/test_util.rs":"3badda83d7f256bb25b840820bc0d3a6523b4ded913555cbea5533b6ccad5654","src/tests.rs":"9f0dc2fe7a0eefb6575acd824767bb7d837a584dc7999ef59a457255a2cd7f3d"},"package":"b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5"} \ No newline at end of file
diff --git a/third_party/rust/litrs/CHANGELOG.md b/third_party/rust/litrs/CHANGELOG.md
new file mode 100644
index 0000000000..e2927c2964
--- /dev/null
+++ b/third_party/rust/litrs/CHANGELOG.md
@@ -0,0 +1,103 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+
+## [Unreleased]
+
+## [0.4.1] - 2023-10-18
+- Fixed incorrectly labeling `27f32` a float literals in docs.
+- Added hint to integer literal docs about parsing as `u128`.
+
+## [0.4.0] - 2023-03-05
+### Added
+- Add ability to parse literals with arbitrary suffixes (e.g. `"foo"bla` or `23px`)
+- Add `suffix()` method to all literal types except `BoolLit`
+- Add `IntegerBase::value`
+- Add `from_suffix` and `suffix` methods to `FloatType` and `IntegerType`
+- Add `FromStr` and `Display` impls to `FloatType` and `IntegerType`
+
+### Changed
+- **Breaking**: Mark `FloatType` and `IntegerType` as `#[non_exhaustive]`
+- **Breaking**: Fix integer parsing for cases like `27f32`. `Literal::parse`
+ and `IntegerLit::parse` will both identify this as an integer literal.
+- **Breaking**: Fix float parsing by correctly rejecting inputs like `27f32`. A
+ float literal must have a period OR an exponent part, according to the spec.
+ Previously decimal integers were accepted in `FloatLit::parse`.
+- Improved some parts of the docs
+
+### Removed
+- **Breaking**: Remove `OwnedLiteral` and `SharedLiteral`
+
+## [0.3.0] - 2022-12-19
+### Breaking
+- Bump MSRV (minimal supported Rust version) to 1.54
+
+### Added
+- Add `raw_input` and `into_raw_input` to non-bool `*Lit` types
+- Add `impl From<*Lit> for pm::Literal` (for non-bool literals)
+- Add `impl From<BoolLit> for pm::Ident`
+
+### Fixed
+- Fix link to reference and clarify bool literals ([#7](https://github.com/LukasKalbertodt/litrs/pull/7))
+
+### Internals
+- Move lots of parsing code into non-generic functions (this hopefully reduces compile times)
+- To implement `[into_]raw_input` for integer and float literals, their
+ internals were changed a bit so that they store the full input string now.
+
+## [0.2.3] - 2021-06-09
+### Changed
+- Minor internal code change to bring MSRV from 1.52 to 1.42
+
+## [0.2.2] - 2021-06-09
+### Changed
+- Fixed (byte) string literal parsing by:
+ - Correctly handling "string continue" sequences
+ - Correctly converting `\n\r` into `\n`
+
+## [0.2.1] - 2021-06-04
+### Changed
+- Fixed the `expected` value of the error returned from `TryFrom<TokenTree>` impls in some cases
+
+## [0.2.0] - 2021-05-28
+### Changed
+- **Breaking**: rename `Error` to `ParseError`. That describes its purpose more
+ closely and is particular useful now that other error types exist in the library.
+
+### Removed
+- **Breaking**: remove `proc-macro` feature and instead offer the corresponding
+ `impl`s unconditionally. Since the feature didn't enable/disable a
+ dependency (`proc-macro` is a compiler provided crate) and since apparently
+ it works fine in `no_std` environments, I dropped this feature. I don't
+ currently see a reason why the corresponding impls should be conditional.
+
+### Added
+- `TryFrom<TokenTree> for litrs::Literal` impls
+- `From<*Lit> for litrs::Literal` impls
+- `TryFrom<proc_macro[2]::Literal> for *Lit`
+- `TryFrom<TokenTree> for *Lit`
+- `InvalidToken` error type for all new `TryFrom` impls
+
+
+## [0.1.1] - 2021-05-25
+### Added
+- `From` impls to create a `Literal` from references to proc-macro literal types:
+ - `From<&proc_macro::Literal>`
+ - `From<&proc_macro2::Literal>`
+- Better examples in README and repository
+
+## 0.1.0 - 2021-05-24
+### Added
+- Everything
+
+
+[Unreleased]: https://github.com/LukasKalbertodt/litrs/compare/v0.4.1...HEAD
+[0.4.1]: https://github.com/LukasKalbertodt/litrs/compare/v0.4.0...v0.4.1
+[0.4.0]: https://github.com/LukasKalbertodt/litrs/compare/v0.3.0...v0.4.0
+[0.3.0]: https://github.com/LukasKalbertodt/litrs/compare/v0.2.3...v0.3.0
+[0.2.3]: https://github.com/LukasKalbertodt/litrs/compare/v0.2.2...v0.2.3
+[0.2.2]: https://github.com/LukasKalbertodt/litrs/compare/v0.2.1...v0.2.2
+[0.2.1]: https://github.com/LukasKalbertodt/litrs/compare/v0.2.0...v0.2.1
+[0.2.0]: https://github.com/LukasKalbertodt/litrs/compare/v0.1.1...v0.2.0
+[0.1.1]: https://github.com/LukasKalbertodt/litrs/compare/v0.1.0...v0.1.1
diff --git a/third_party/rust/litrs/Cargo.toml b/third_party/rust/litrs/Cargo.toml
new file mode 100644
index 0000000000..6e65403490
--- /dev/null
+++ b/third_party/rust/litrs/Cargo.toml
@@ -0,0 +1,51 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies.
+#
+# If you are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
+
+[package]
+edition = "2018"
+rust-version = "1.54"
+name = "litrs"
+version = "0.4.1"
+authors = ["Lukas Kalbertodt <lukas.kalbertodt@gmail.com>"]
+exclude = [".github"]
+description = """
+Parse and inspect Rust literals (i.e. tokens in the Rust programming language
+representing fixed values). Particularly useful for proc macros, but can also
+be used outside of a proc-macro context.
+"""
+documentation = "https://docs.rs/litrs/"
+readme = "README.md"
+keywords = [
+ "literal",
+ "parsing",
+ "proc-macro",
+ "type",
+ "procedural",
+]
+categories = [
+ "development-tools::procedural-macro-helpers",
+ "parser-implementations",
+ "development-tools::build-utils",
+]
+license = "MIT/Apache-2.0"
+repository = "https://github.com/LukasKalbertodt/litrs/"
+
+[dependencies.proc-macro2]
+version = "1"
+optional = true
+
+[dependencies.unicode-xid]
+version = "0.2.4"
+optional = true
+
+[features]
+check_suffix = ["unicode-xid"]
+default = ["proc-macro2"]
diff --git a/third_party/rust/litrs/LICENSE-APACHE b/third_party/rust/litrs/LICENSE-APACHE
new file mode 100644
index 0000000000..1b5ec8b78e
--- /dev/null
+++ b/third_party/rust/litrs/LICENSE-APACHE
@@ -0,0 +1,176 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
diff --git a/third_party/rust/litrs/LICENSE-MIT b/third_party/rust/litrs/LICENSE-MIT
new file mode 100644
index 0000000000..4fa8658f71
--- /dev/null
+++ b/third_party/rust/litrs/LICENSE-MIT
@@ -0,0 +1,25 @@
+Copyright (c) 2020 Project Developers
+
+Permission is hereby granted, free of charge, to any
+person obtaining a copy of this software and associated
+documentation files (the "Software"), to deal in the
+Software without restriction, including without
+limitation the rights to use, copy, modify, merge,
+publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software
+is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice
+shall be included in all copies or substantial portions
+of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
+IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/third_party/rust/litrs/README.md b/third_party/rust/litrs/README.md
new file mode 100644
index 0000000000..2307a5f429
--- /dev/null
+++ b/third_party/rust/litrs/README.md
@@ -0,0 +1,88 @@
+# `litrs`: parsing and inspecting Rust literals
+
+[<img alt="CI status of main" src="https://img.shields.io/github/actions/workflow/status/LukasKalbertodt/litrs/ci.yml?branch=main&label=CI&logo=github&logoColor=white&style=for-the-badge" height="23">](https://github.com/LukasKalbertodt/litrs/actions/workflows/ci.yml)
+[<img alt="Crates.io Version" src="https://img.shields.io/crates/v/litrs?logo=rust&style=for-the-badge" height="23">](https://crates.io/crates/litrs)
+[<img alt="docs.rs" src="https://img.shields.io/crates/v/litrs?color=blue&label=docs&style=for-the-badge" height="23">](https://docs.rs/litrs)
+
+`litrs` offers functionality to parse Rust literals, i.e. tokens in the Rust programming language that represent fixed values.
+For example: `27`, `"crab"`, `bool`.
+This is particularly useful for proc macros, but can also be used outside of a proc-macro context.
+
+**Why this library?**
+Unfortunately, the `proc_macro` API shipped with the compiler offers no easy way to inspect literals.
+There are mainly two libraries for this purpose:
+[`syn`](https://github.com/dtolnay/syn) and [`literalext`](https://github.com/mystor/literalext).
+The latter is deprecated.
+And `syn` is oftentimes overkill for the task at hand, especially when developing function-like proc-macros (e.g. `foo!(..)`).
+This crate is a lightweight alternative.
+Also, when it comes to literals, `litrs` offers a bit more flexibility and a few more features compared to `syn`.
+
+I'm interested in community feedback!
+If you consider using this, please speak your mind [in this issue](https://github.com/LukasKalbertodt/litrs/issues/1).
+
+## Example
+
+### In proc macro
+
+```rust
+use std::convert::TryFrom;
+use proc_macro::TokenStream;
+use litrs::Literal;
+
+#[proc_macro]
+pub fn foo(input: TokenStream) -> TokenStream {
+ // Please do proper error handling in your real code!
+ let first_token = input.into_iter().next().expect("no input");
+
+ // `try_from` will return an error if the token is not a literal.
+ match Literal::try_from(first_token) {
+ // Convenient methods to produce decent errors via `compile_error!`.
+ Err(e) => return e.to_compile_error(),
+
+ // You can now inspect your literal!
+ Ok(Literal::Integer(i)) => {
+ println!("Got an integer specified in base {:?}", i.base());
+
+ let value = i.value::<u64>().expect("integer literal too large");
+ println!("Is your integer even? {}", value % 2 == 0);
+ }
+ Ok(other) => {
+ println!("Got a non-integer literal");
+ }
+ }
+
+ TokenStream::new() // dummy output
+}
+```
+
+If you are expecting a specific kind of literal, you can also use this, which will return an error if the token is not a float literal.
+
+```rust
+FloatLit::try_from(first_token)
+```
+
+### Parsing from a `&str`
+
+Outside of a proc macro context you might want to parse a string directly.
+
+```rust
+use litrs::{FloatLit, Literal};
+
+let lit = Literal::parse("'🦀'").expect("failed to parse literal");
+let float_lit = FloatLit::parse("2.7e3").expect("failed to parse as float literal");
+```
+
+See [**the documentation**](https://docs.rs/litrs) or the `examples/` directory for more examples and information.
+
+
+<br />
+
+---
+
+## License
+
+Licensed under either of <a href="LICENSE-APACHE">Apache License, Version
+2.0</a> or <a href="LICENSE-MIT">MIT license</a> at your option.
+Unless you explicitly state otherwise, any contribution intentionally submitted
+for inclusion in this project by you, as defined in the Apache-2.0 license,
+shall be dual licensed as above, without any additional terms or conditions.
diff --git a/third_party/rust/litrs/src/bool/mod.rs b/third_party/rust/litrs/src/bool/mod.rs
new file mode 100644
index 0000000000..d7b54a1b9f
--- /dev/null
+++ b/third_party/rust/litrs/src/bool/mod.rs
@@ -0,0 +1,55 @@
+use std::fmt;
+
+use crate::{ParseError, err::{perr, ParseErrorKind::*}};
+
+
+/// A bool literal: `true` or `false`. Also see [the reference][ref].
+///
+/// Notice that, strictly speaking, from Rust point of view "boolean literals" are not
+/// actual literals but [keywords].
+///
+/// [ref]: https://doc.rust-lang.org/reference/expressions/literal-expr.html#boolean-literal-expressions
+/// [keywords]: https://doc.rust-lang.org/reference/keywords.html#strict-keywords
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum BoolLit {
+ False,
+ True,
+}
+
+impl BoolLit {
+ /// Parses the input as a bool literal. Returns an error if the input is
+ /// invalid or represents a different kind of literal.
+ pub fn parse(s: &str) -> Result<Self, ParseError> {
+ match s {
+ "false" => Ok(Self::False),
+ "true" => Ok(Self::True),
+ _ => Err(perr(None, InvalidLiteral)),
+ }
+ }
+
+ /// Returns the actual Boolean value of this literal.
+ pub fn value(self) -> bool {
+ match self {
+ Self::False => false,
+ Self::True => true,
+ }
+ }
+
+ /// Returns the literal as string.
+ pub fn as_str(&self) -> &'static str {
+ match self {
+ Self::False => "false",
+ Self::True => "true",
+ }
+ }
+}
+
+impl fmt::Display for BoolLit {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.pad(self.as_str())
+ }
+}
+
+
+#[cfg(test)]
+mod tests;
diff --git a/third_party/rust/litrs/src/bool/tests.rs b/third_party/rust/litrs/src/bool/tests.rs
new file mode 100644
index 0000000000..4b829244b8
--- /dev/null
+++ b/third_party/rust/litrs/src/bool/tests.rs
@@ -0,0 +1,48 @@
+use crate::{
+ Literal, BoolLit,
+ test_util::assert_parse_ok_eq,
+};
+
+macro_rules! assert_bool_parse {
+ ($input:literal, $expected:expr) => {
+ assert_parse_ok_eq(
+ $input, Literal::parse($input), Literal::Bool($expected), "Literal::parse");
+ assert_parse_ok_eq($input, BoolLit::parse($input), $expected, "BoolLit::parse");
+ };
+}
+
+
+
+#[test]
+fn parse_ok() {
+ assert_bool_parse!("false", BoolLit::False);
+ assert_bool_parse!("true", BoolLit::True);
+}
+
+#[test]
+fn parse_err() {
+ assert!(Literal::parse("fa").is_err());
+ assert!(Literal::parse("fal").is_err());
+ assert!(Literal::parse("fals").is_err());
+ assert!(Literal::parse(" false").is_err());
+ assert!(Literal::parse("false ").is_err());
+ assert!(Literal::parse("False").is_err());
+
+ assert!(Literal::parse("tr").is_err());
+ assert!(Literal::parse("tru").is_err());
+ assert!(Literal::parse(" true").is_err());
+ assert!(Literal::parse("true ").is_err());
+ assert!(Literal::parse("True").is_err());
+}
+
+#[test]
+fn value() {
+ assert!(!BoolLit::False.value());
+ assert!(BoolLit::True.value());
+}
+
+#[test]
+fn as_str() {
+ assert_eq!(BoolLit::False.as_str(), "false");
+ assert_eq!(BoolLit::True.as_str(), "true");
+}
diff --git a/third_party/rust/litrs/src/byte/mod.rs b/third_party/rust/litrs/src/byte/mod.rs
new file mode 100644
index 0000000000..ffdff5d04a
--- /dev/null
+++ b/third_party/rust/litrs/src/byte/mod.rs
@@ -0,0 +1,107 @@
+use core::fmt;
+
+use crate::{
+ Buffer, ParseError,
+ err::{perr, ParseErrorKind::*},
+ escape::unescape,
+ parse::check_suffix,
+};
+
+
+/// A (single) byte literal, e.g. `b'k'` or `b'!'`.
+///
+/// See [the reference][ref] for more information.
+///
+/// [ref]: https://doc.rust-lang.org/reference/tokens.html#byte-literals
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct ByteLit<B: Buffer> {
+ raw: B,
+ /// Start index of the suffix or `raw.len()` if there is no suffix.
+ start_suffix: usize,
+ value: u8,
+}
+
+impl<B: Buffer> ByteLit<B> {
+ /// Parses the input as a byte literal. Returns an error if the input is
+ /// invalid or represents a different kind of literal.
+ pub fn parse(input: B) -> Result<Self, ParseError> {
+ if input.is_empty() {
+ return Err(perr(None, Empty));
+ }
+ if !input.starts_with("b'") {
+ return Err(perr(None, InvalidByteLiteralStart));
+ }
+
+ let (value, start_suffix) = parse_impl(&input)?;
+ Ok(Self { raw: input, value, start_suffix })
+ }
+
+ /// Returns the byte value that this literal represents.
+ pub fn value(&self) -> u8 {
+ self.value
+ }
+
+ /// The optional suffix. Returns `""` if the suffix is empty/does not exist.
+ pub fn suffix(&self) -> &str {
+ &(*self.raw)[self.start_suffix..]
+ }
+
+ /// Returns the raw input that was passed to `parse`.
+ pub fn raw_input(&self) -> &str {
+ &self.raw
+ }
+
+ /// Returns the raw input that was passed to `parse`, potentially owned.
+ pub fn into_raw_input(self) -> B {
+ self.raw
+ }
+
+}
+
+impl ByteLit<&str> {
+ /// Makes a copy of the underlying buffer and returns the owned version of
+ /// `Self`.
+ pub fn to_owned(&self) -> ByteLit<String> {
+ ByteLit {
+ raw: self.raw.to_owned(),
+ start_suffix: self.start_suffix,
+ value: self.value,
+ }
+ }
+}
+
+impl<B: Buffer> fmt::Display for ByteLit<B> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.pad(&self.raw)
+ }
+}
+
+/// Precondition: must start with `b'`.
+#[inline(never)]
+pub(crate) fn parse_impl(input: &str) -> Result<(u8, usize), ParseError> {
+ let input_bytes = input.as_bytes();
+ let first = input_bytes.get(2).ok_or(perr(None, UnterminatedByteLiteral))?;
+ let (c, len) = match first {
+ b'\'' if input_bytes.get(3) == Some(&b'\'') => return Err(perr(2, UnescapedSingleQuote)),
+ b'\'' => return Err(perr(None, EmptyByteLiteral)),
+ b'\n' | b'\t' | b'\r' => return Err(perr(2, UnescapedSpecialWhitespace)),
+ b'\\' => unescape::<u8>(&input[2..], 2)?,
+ other if other.is_ascii() => (*other, 1),
+ _ => return Err(perr(2, NonAsciiInByteLiteral)),
+ };
+
+ match input[2 + len..].find('\'') {
+ Some(0) => {}
+ Some(_) => return Err(perr(None, OverlongByteLiteral)),
+ None => return Err(perr(None, UnterminatedByteLiteral)),
+ }
+
+ let start_suffix = 2 + len + 1;
+ let suffix = &input[start_suffix..];
+ check_suffix(suffix).map_err(|kind| perr(start_suffix, kind))?;
+
+ Ok((c, start_suffix))
+}
+
+#[cfg(test)]
+mod tests;
diff --git a/third_party/rust/litrs/src/byte/tests.rs b/third_party/rust/litrs/src/byte/tests.rs
new file mode 100644
index 0000000000..3cf16b5fc2
--- /dev/null
+++ b/third_party/rust/litrs/src/byte/tests.rs
@@ -0,0 +1,188 @@
+use crate::{ByteLit, Literal, test_util::{assert_parse_ok_eq, assert_roundtrip}};
+
+// ===== Utility functions =======================================================================
+
+macro_rules! check {
+ ($lit:literal) => { check!($lit, stringify!($lit), "") };
+ ($lit:literal, $input:expr, $suffix:literal) => {
+ let input = $input;
+ let expected = ByteLit {
+ raw: input,
+ start_suffix: input.len() - $suffix.len(),
+ value: $lit,
+ };
+
+ assert_parse_ok_eq(input, ByteLit::parse(input), expected.clone(), "ByteLit::parse");
+ assert_parse_ok_eq(input, Literal::parse(input), Literal::Byte(expected), "Literal::parse");
+ let lit = ByteLit::parse(input).unwrap();
+ assert_eq!(lit.value(), $lit);
+ assert_eq!(lit.suffix(), $suffix);
+ assert_roundtrip(expected.to_owned(), input);
+ };
+}
+
+
+// ===== Actual tests ============================================================================
+
+#[test]
+fn alphanumeric() {
+ check!(b'a');
+ check!(b'b');
+ check!(b'y');
+ check!(b'z');
+ check!(b'A');
+ check!(b'B');
+ check!(b'Y');
+ check!(b'Z');
+
+ check!(b'0');
+ check!(b'1');
+ check!(b'8');
+ check!(b'9');
+}
+
+#[test]
+fn special_chars() {
+ check!(b' ');
+ check!(b'!');
+ check!(b'"');
+ check!(b'#');
+ check!(b'$');
+ check!(b'%');
+ check!(b'&');
+ check!(b'(');
+ check!(b')');
+ check!(b'*');
+ check!(b'+');
+ check!(b',');
+ check!(b'-');
+ check!(b'.');
+ check!(b'/');
+ check!(b':');
+ check!(b';');
+ check!(b'<');
+ check!(b'=');
+ check!(b'>');
+ check!(b'?');
+ check!(b'@');
+ check!(b'[');
+ check!(b']');
+ check!(b'^');
+ check!(b'_');
+ check!(b'`');
+ check!(b'{');
+ check!(b'|');
+ check!(b'}');
+ check!(b'~');
+}
+
+#[test]
+fn quote_escapes() {
+ check!(b'\'');
+ check!(b'\"');
+}
+
+#[test]
+fn ascii_escapes() {
+ check!(b'\n');
+ check!(b'\r');
+ check!(b'\t');
+ check!(b'\\');
+ check!(b'\0');
+
+ check!(b'\x00');
+ check!(b'\x01');
+ check!(b'\x0c');
+ check!(b'\x0D');
+ check!(b'\x13');
+ check!(b'\x30');
+ check!(b'\x30');
+ check!(b'\x4B');
+ check!(b'\x6b');
+ check!(b'\x7F');
+ check!(b'\x7f');
+}
+
+#[test]
+fn byte_escapes() {
+ check!(b'\x80');
+ check!(b'\x8a');
+ check!(b'\x8C');
+ check!(b'\x99');
+ check!(b'\xa0');
+ check!(b'\xAd');
+ check!(b'\xfe');
+ check!(b'\xFe');
+ check!(b'\xfF');
+ check!(b'\xFF');
+}
+
+#[test]
+fn suffixes() {
+ check!(b'a', r##"b'a'peter"##, "peter");
+ check!(b'#', r##"b'#'peter"##, "peter");
+ check!(b'\n', r##"b'\n'peter"##, "peter");
+ check!(b'\'', r##"b'\''peter"##, "peter");
+ check!(b'\"', r##"b'\"'peter"##, "peter");
+ check!(b'\xFF', r##"b'\xFF'peter"##, "peter");
+}
+
+#[test]
+fn invald_escapes() {
+ assert_err!(ByteLit, r"b'\a'", UnknownEscape, 2..4);
+ assert_err!(ByteLit, r"b'\y'", UnknownEscape, 2..4);
+ assert_err!(ByteLit, r"b'\", UnterminatedEscape, 2..3);
+ assert_err!(ByteLit, r"b'\x'", UnterminatedEscape, 2..5);
+ assert_err!(ByteLit, r"b'\x1'", InvalidXEscape, 2..6);
+ assert_err!(ByteLit, r"b'\xaj'", InvalidXEscape, 2..6);
+ assert_err!(ByteLit, r"b'\xjb'", InvalidXEscape, 2..6);
+}
+
+#[test]
+fn unicode_escape_not_allowed() {
+ assert_err!(ByteLit, r"b'\u{0}'", UnicodeEscapeInByteLiteral, 2..4);
+ assert_err!(ByteLit, r"b'\u{00}'", UnicodeEscapeInByteLiteral, 2..4);
+ assert_err!(ByteLit, r"b'\u{b}'", UnicodeEscapeInByteLiteral, 2..4);
+ assert_err!(ByteLit, r"b'\u{B}'", UnicodeEscapeInByteLiteral, 2..4);
+ assert_err!(ByteLit, r"b'\u{7e}'", UnicodeEscapeInByteLiteral, 2..4);
+ assert_err!(ByteLit, r"b'\u{E4}'", UnicodeEscapeInByteLiteral, 2..4);
+ assert_err!(ByteLit, r"b'\u{e4}'", UnicodeEscapeInByteLiteral, 2..4);
+ assert_err!(ByteLit, r"b'\u{fc}'", UnicodeEscapeInByteLiteral, 2..4);
+ assert_err!(ByteLit, r"b'\u{Fc}'", UnicodeEscapeInByteLiteral, 2..4);
+ assert_err!(ByteLit, r"b'\u{fC}'", UnicodeEscapeInByteLiteral, 2..4);
+ assert_err!(ByteLit, r"b'\u{FC}'", UnicodeEscapeInByteLiteral, 2..4);
+ assert_err!(ByteLit, r"b'\u{b10}'", UnicodeEscapeInByteLiteral, 2..4);
+ assert_err!(ByteLit, r"b'\u{B10}'", UnicodeEscapeInByteLiteral, 2..4);
+ assert_err!(ByteLit, r"b'\u{0b10}'", UnicodeEscapeInByteLiteral, 2..4);
+ assert_err!(ByteLit, r"b'\u{2764}'", UnicodeEscapeInByteLiteral, 2..4);
+ assert_err!(ByteLit, r"b'\u{1f602}'", UnicodeEscapeInByteLiteral, 2..4);
+ assert_err!(ByteLit, r"b'\u{1F602}'", UnicodeEscapeInByteLiteral, 2..4);
+}
+
+#[test]
+fn parse_err() {
+ assert_err!(ByteLit, r"b''", EmptyByteLiteral, None);
+ assert_err!(ByteLit, r"b' ''", UnexpectedChar, 4..5);
+
+ assert_err!(ByteLit, r"b'", UnterminatedByteLiteral, None);
+ assert_err!(ByteLit, r"b'a", UnterminatedByteLiteral, None);
+ assert_err!(ByteLit, r"b'\n", UnterminatedByteLiteral, None);
+ assert_err!(ByteLit, r"b'\x35", UnterminatedByteLiteral, None);
+
+ assert_err!(ByteLit, r"b'ab'", OverlongByteLiteral, None);
+ assert_err!(ByteLit, r"b'a _'", OverlongByteLiteral, None);
+ assert_err!(ByteLit, r"b'\n3'", OverlongByteLiteral, None);
+
+ assert_err!(ByteLit, r"", Empty, None);
+
+ assert_err!(ByteLit, r"b'''", UnescapedSingleQuote, 2);
+ assert_err!(ByteLit, r"b''''", UnescapedSingleQuote, 2);
+
+ assert_err!(ByteLit, "b'\n'", UnescapedSpecialWhitespace, 2);
+ assert_err!(ByteLit, "b'\t'", UnescapedSpecialWhitespace, 2);
+ assert_err!(ByteLit, "b'\r'", UnescapedSpecialWhitespace, 2);
+
+ assert_err!(ByteLit, "b'న'", NonAsciiInByteLiteral, 2);
+ assert_err!(ByteLit, "b'犬'", NonAsciiInByteLiteral, 2);
+ assert_err!(ByteLit, "b'🦊'", NonAsciiInByteLiteral, 2);
+}
diff --git a/third_party/rust/litrs/src/bytestr/mod.rs b/third_party/rust/litrs/src/bytestr/mod.rs
new file mode 100644
index 0000000000..a0e09727f4
--- /dev/null
+++ b/third_party/rust/litrs/src/bytestr/mod.rs
@@ -0,0 +1,126 @@
+use std::{fmt, ops::Range};
+
+use crate::{
+ Buffer, ParseError,
+ err::{perr, ParseErrorKind::*},
+ escape::{scan_raw_string, unescape_string},
+};
+
+
+/// A byte string or raw byte string literal, e.g. `b"hello"` or `br#"abc"def"#`.
+///
+/// See [the reference][ref] for more information.
+///
+/// [ref]: https://doc.rust-lang.org/reference/tokens.html#byte-string-literals
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct ByteStringLit<B: Buffer> {
+ /// The raw input.
+ raw: B,
+
+ /// The string value (with all escaped unescaped), or `None` if there were
+ /// no escapes. In the latter case, `input` is the string value.
+ value: Option<Vec<u8>>,
+
+ /// The number of hash signs in case of a raw string literal, or `None` if
+ /// it's not a raw string literal.
+ num_hashes: Option<u32>,
+
+ /// Start index of the suffix or `raw.len()` if there is no suffix.
+ start_suffix: usize,
+}
+
+impl<B: Buffer> ByteStringLit<B> {
+ /// Parses the input as a (raw) byte string literal. Returns an error if the
+ /// input is invalid or represents a different kind of literal.
+ pub fn parse(input: B) -> Result<Self, ParseError> {
+ if input.is_empty() {
+ return Err(perr(None, Empty));
+ }
+ if !input.starts_with(r#"b""#) && !input.starts_with("br") {
+ return Err(perr(None, InvalidByteStringLiteralStart));
+ }
+
+ let (value, num_hashes, start_suffix) = parse_impl(&input)?;
+ Ok(Self { raw: input, value, num_hashes, start_suffix })
+ }
+
+ /// Returns the string value this literal represents (where all escapes have
+ /// been turned into their respective values).
+ pub fn value(&self) -> &[u8] {
+ self.value.as_deref().unwrap_or(&self.raw.as_bytes()[self.inner_range()])
+ }
+
+ /// Like `value` but returns a potentially owned version of the value.
+ ///
+ /// The return value is either `Cow<'static, [u8]>` if `B = String`, or
+ /// `Cow<'a, [u8]>` if `B = &'a str`.
+ pub fn into_value(self) -> B::ByteCow {
+ let inner_range = self.inner_range();
+ let Self { raw, value, .. } = self;
+ value.map(B::ByteCow::from).unwrap_or_else(|| raw.cut(inner_range).into_byte_cow())
+ }
+
+ /// The optional suffix. Returns `""` if the suffix is empty/does not exist.
+ pub fn suffix(&self) -> &str {
+ &(*self.raw)[self.start_suffix..]
+ }
+
+ /// Returns whether this literal is a raw string literal (starting with
+ /// `r`).
+ pub fn is_raw_byte_string(&self) -> bool {
+ self.num_hashes.is_some()
+ }
+
+ /// Returns the raw input that was passed to `parse`.
+ pub fn raw_input(&self) -> &str {
+ &self.raw
+ }
+
+ /// Returns the raw input that was passed to `parse`, potentially owned.
+ pub fn into_raw_input(self) -> B {
+ self.raw
+ }
+
+ /// The range within `self.raw` that excludes the quotes and potential `r#`.
+ fn inner_range(&self) -> Range<usize> {
+ match self.num_hashes {
+ None => 2..self.start_suffix - 1,
+ Some(n) => 2 + n as usize + 1..self.start_suffix - n as usize - 1,
+ }
+ }
+}
+
+impl ByteStringLit<&str> {
+ /// Makes a copy of the underlying buffer and returns the owned version of
+ /// `Self`.
+ pub fn into_owned(self) -> ByteStringLit<String> {
+ ByteStringLit {
+ raw: self.raw.to_owned(),
+ value: self.value,
+ num_hashes: self.num_hashes,
+ start_suffix: self.start_suffix,
+ }
+ }
+}
+
+impl<B: Buffer> fmt::Display for ByteStringLit<B> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.pad(&self.raw)
+ }
+}
+
+
+/// Precondition: input has to start with either `b"` or `br`.
+#[inline(never)]
+fn parse_impl(input: &str) -> Result<(Option<Vec<u8>>, Option<u32>, usize), ParseError> {
+ if input.starts_with("br") {
+ scan_raw_string::<u8>(&input, 2)
+ .map(|(v, num, start_suffix)| (v.map(String::into_bytes), Some(num), start_suffix))
+ } else {
+ unescape_string::<u8>(&input, 2)
+ .map(|(v, start_suffix)| (v.map(String::into_bytes), None, start_suffix))
+ }
+}
+
+#[cfg(test)]
+mod tests;
diff --git a/third_party/rust/litrs/src/bytestr/tests.rs b/third_party/rust/litrs/src/bytestr/tests.rs
new file mode 100644
index 0000000000..2afef5a99c
--- /dev/null
+++ b/third_party/rust/litrs/src/bytestr/tests.rs
@@ -0,0 +1,224 @@
+use crate::{Literal, ByteStringLit, test_util::{assert_parse_ok_eq, assert_roundtrip}};
+
+// ===== Utility functions =======================================================================
+
+macro_rules! check {
+ ($lit:literal, $has_escapes:expr, $num_hashes:expr) => {
+ check!($lit, stringify!($lit), $has_escapes, $num_hashes, "")
+ };
+ ($lit:literal, $input:expr, $has_escapes:expr, $num_hashes:expr, $suffix:literal) => {
+ let input = $input;
+ let expected = ByteStringLit {
+ raw: input,
+ value: if $has_escapes { Some($lit.to_vec()) } else { None },
+ num_hashes: $num_hashes,
+ start_suffix: input.len() - $suffix.len(),
+ };
+
+ assert_parse_ok_eq(
+ input, ByteStringLit::parse(input), expected.clone(), "ByteStringLit::parse");
+ assert_parse_ok_eq(
+ input, Literal::parse(input), Literal::ByteString(expected.clone()), "Literal::parse");
+ let lit = ByteStringLit::parse(input).unwrap();
+ assert_eq!(lit.value(), $lit);
+ assert_eq!(lit.suffix(), $suffix);
+ assert_eq!(lit.into_value().as_ref(), $lit);
+ assert_roundtrip(expected.into_owned(), input);
+ };
+}
+
+
+// ===== Actual tests ============================================================================
+
+#[test]
+fn simple() {
+ check!(b"", false, None);
+ check!(b"a", false, None);
+ check!(b"peter", false, None);
+}
+
+#[test]
+fn special_whitespace() {
+ let strings = ["\n", "\t", "foo\tbar", "baz\n"];
+
+ for &s in &strings {
+ let input = format!(r#"b"{}""#, s);
+ let input_raw = format!(r#"br"{}""#, s);
+ for (input, num_hashes) in vec![(input, None), (input_raw, Some(0))] {
+ let expected = ByteStringLit {
+ raw: &*input,
+ value: None,
+ num_hashes,
+ start_suffix: input.len(),
+ };
+ assert_parse_ok_eq(
+ &input, ByteStringLit::parse(&*input), expected.clone(), "ByteStringLit::parse");
+ assert_parse_ok_eq(
+ &input, Literal::parse(&*input), Literal::ByteString(expected), "Literal::parse");
+ assert_eq!(ByteStringLit::parse(&*input).unwrap().value(), s.as_bytes());
+ assert_eq!(ByteStringLit::parse(&*input).unwrap().into_value(), s.as_bytes());
+ }
+ }
+
+ let res = ByteStringLit::parse("br\"\r\"").expect("failed to parse");
+ assert_eq!(res.value(), b"\r");
+}
+
+#[test]
+fn simple_escapes() {
+ check!(b"a\nb", true, None);
+ check!(b"\nb", true, None);
+ check!(b"a\n", true, None);
+ check!(b"\n", true, None);
+
+ check!(b"\x60foo \t bar\rbaz\n banana \0kiwi", true, None);
+ check!(b"foo \\ferris", true, None);
+ check!(b"baz \\ferris\"box", true, None);
+ check!(b"\\foo\\ banana\" baz\"", true, None);
+ check!(b"\"foo \\ferris \" baz\\", true, None);
+
+ check!(b"\x00", true, None);
+ check!(b" \x01", true, None);
+ check!(b"\x0c foo", true, None);
+ check!(b" foo\x0D ", true, None);
+ check!(b"\\x13", true, None);
+ check!(b"\"x30", true, None);
+}
+
+#[test]
+fn string_continue() {
+ check!(b"foo\
+ bar", true, None);
+ check!(b"foo\
+bar", true, None);
+
+ check!(b"foo\
+
+ banana", true, None);
+
+ // Weird whitespace characters
+ let lit = ByteStringLit::parse("b\"foo\\\n\r\t\n \n\tbar\"").expect("failed to parse");
+ assert_eq!(lit.value(), b"foobar");
+
+ // Raw strings do not handle "string continues"
+ check!(br"foo\
+ bar", false, Some(0));
+}
+
+#[test]
+fn crlf_newlines() {
+ let lit = ByteStringLit::parse("b\"foo\r\nbar\"").expect("failed to parse");
+ assert_eq!(lit.value(), b"foo\nbar");
+
+ let lit = ByteStringLit::parse("b\"\r\nbar\"").expect("failed to parse");
+ assert_eq!(lit.value(), b"\nbar");
+
+ let lit = ByteStringLit::parse("b\"foo\r\n\"").expect("failed to parse");
+ assert_eq!(lit.value(), b"foo\n");
+
+ let lit = ByteStringLit::parse("br\"foo\r\nbar\"").expect("failed to parse");
+ assert_eq!(lit.value(), b"foo\nbar");
+
+ let lit = ByteStringLit::parse("br#\"\r\nbar\"#").expect("failed to parse");
+ assert_eq!(lit.value(), b"\nbar");
+
+ let lit = ByteStringLit::parse("br##\"foo\r\n\"##").expect("failed to parse");
+ assert_eq!(lit.value(), b"foo\n");
+}
+
+#[test]
+fn raw_byte_string() {
+ check!(br"", false, Some(0));
+ check!(br"a", false, Some(0));
+ check!(br"peter", false, Some(0));
+ check!(br"Greetings jason!", false, Some(0));
+
+ check!(br#""#, false, Some(1));
+ check!(br#"a"#, false, Some(1));
+ check!(br##"peter"##, false, Some(2));
+ check!(br###"Greetings # Jason!"###, false, Some(3));
+ check!(br########"we ## need #### more ####### hashtags"########, false, Some(8));
+
+ check!(br#"foo " bar"#, false, Some(1));
+ check!(br##"foo " bar"##, false, Some(2));
+ check!(br#"foo """" '"'" bar"#, false, Some(1));
+ check!(br#""foo""#, false, Some(1));
+ check!(br###""foo'"###, false, Some(3));
+ check!(br#""x'#_#s'"#, false, Some(1));
+ check!(br"#", false, Some(0));
+ check!(br"foo#", false, Some(0));
+ check!(br"##bar", false, Some(0));
+ check!(br###""##foo"##bar'"###, false, Some(3));
+
+ check!(br"foo\n\t\r\0\\x60\u{123}doggo", false, Some(0));
+ check!(br#"cat\n\t\r\0\\x60\u{123}doggo"#, false, Some(1));
+}
+
+#[test]
+fn suffixes() {
+ check!(b"hello", r###"b"hello"suffix"###, false, None, "suffix");
+ check!(b"fox", r#"b"fox"peter"#, false, None, "peter");
+ check!(b"a\x0cb\\", r#"b"a\x0cb\\"_jürgen"#, true, None, "_jürgen");
+ check!(br"a\x0cb\\", r###"br#"a\x0cb\\"#_jürgen"###, false, Some(1), "_jürgen");
+}
+
+#[test]
+fn parse_err() {
+ assert_err!(ByteStringLit, r#"b""#, UnterminatedString, None);
+ assert_err!(ByteStringLit, r#"b"cat"#, UnterminatedString, None);
+ assert_err!(ByteStringLit, r#"b"Jurgen"#, UnterminatedString, None);
+ assert_err!(ByteStringLit, r#"b"foo bar baz"#, UnterminatedString, None);
+
+ assert_err!(ByteStringLit, r#"b"fox"peter""#, InvalidSuffix, 6);
+ assert_err!(ByteStringLit, r###"br#"foo "# bar"#"###, UnexpectedChar, 10);
+
+ assert_err!(ByteStringLit, "b\"\r\"", IsolatedCr, 2);
+ assert_err!(ByteStringLit, "b\"fo\rx\"", IsolatedCr, 4);
+
+ assert_err!(ByteStringLit, r##"br####""##, UnterminatedRawString, None);
+ assert_err!(ByteStringLit, r#####"br##"foo"#bar"#####, UnterminatedRawString, None);
+ assert_err!(ByteStringLit, r##"br####"##, InvalidLiteral, None);
+ assert_err!(ByteStringLit, r##"br####x"##, InvalidLiteral, None);
+}
+
+#[test]
+fn non_ascii() {
+ assert_err!(ByteStringLit, r#"b"న""#, NonAsciiInByteLiteral, 2);
+ assert_err!(ByteStringLit, r#"b"foo犬""#, NonAsciiInByteLiteral, 5);
+ assert_err!(ByteStringLit, r#"b"x🦊baz""#, NonAsciiInByteLiteral, 3);
+ assert_err!(ByteStringLit, r#"br"న""#, NonAsciiInByteLiteral, 3);
+ assert_err!(ByteStringLit, r#"br"foo犬""#, NonAsciiInByteLiteral, 6);
+ assert_err!(ByteStringLit, r#"br"x🦊baz""#, NonAsciiInByteLiteral, 4);
+}
+
+#[test]
+fn invalid_escapes() {
+ assert_err!(ByteStringLit, r#"b"\a""#, UnknownEscape, 2..4);
+ assert_err!(ByteStringLit, r#"b"foo\y""#, UnknownEscape, 5..7);
+ assert_err!(ByteStringLit, r#"b"\"#, UnterminatedEscape, 2);
+ assert_err!(ByteStringLit, r#"b"\x""#, UnterminatedEscape, 2..4);
+ assert_err!(ByteStringLit, r#"b"foo\x1""#, UnterminatedEscape, 5..8);
+ assert_err!(ByteStringLit, r#"b" \xaj""#, InvalidXEscape, 3..7);
+ assert_err!(ByteStringLit, r#"b"\xjbbaz""#, InvalidXEscape, 2..6);
+}
+
+#[test]
+fn unicode_escape_not_allowed() {
+ assert_err!(ByteStringLit, r#"b"\u{0}""#, UnicodeEscapeInByteLiteral, 2..4);
+ assert_err!(ByteStringLit, r#"b"\u{00}""#, UnicodeEscapeInByteLiteral, 2..4);
+ assert_err!(ByteStringLit, r#"b"\u{b}""#, UnicodeEscapeInByteLiteral, 2..4);
+ assert_err!(ByteStringLit, r#"b"\u{B}""#, UnicodeEscapeInByteLiteral, 2..4);
+ assert_err!(ByteStringLit, r#"b"\u{7e}""#, UnicodeEscapeInByteLiteral, 2..4);
+ assert_err!(ByteStringLit, r#"b"\u{E4}""#, UnicodeEscapeInByteLiteral, 2..4);
+ assert_err!(ByteStringLit, r#"b"\u{e4}""#, UnicodeEscapeInByteLiteral, 2..4);
+ assert_err!(ByteStringLit, r#"b"\u{fc}""#, UnicodeEscapeInByteLiteral, 2..4);
+ assert_err!(ByteStringLit, r#"b"\u{Fc}""#, UnicodeEscapeInByteLiteral, 2..4);
+ assert_err!(ByteStringLit, r#"b"\u{fC}""#, UnicodeEscapeInByteLiteral, 2..4);
+ assert_err!(ByteStringLit, r#"b"\u{FC}""#, UnicodeEscapeInByteLiteral, 2..4);
+ assert_err!(ByteStringLit, r#"b"\u{b10}""#, UnicodeEscapeInByteLiteral, 2..4);
+ assert_err!(ByteStringLit, r#"b"\u{B10}""#, UnicodeEscapeInByteLiteral, 2..4);
+ assert_err!(ByteStringLit, r#"b"\u{0b10}""#, UnicodeEscapeInByteLiteral, 2..4);
+ assert_err!(ByteStringLit, r#"b"\u{2764}""#, UnicodeEscapeInByteLiteral, 2..4);
+ assert_err!(ByteStringLit, r#"b"\u{1f602}""#, UnicodeEscapeInByteLiteral, 2..4);
+ assert_err!(ByteStringLit, r#"b"\u{1F602}""#, UnicodeEscapeInByteLiteral, 2..4);
+}
diff --git a/third_party/rust/litrs/src/char/mod.rs b/third_party/rust/litrs/src/char/mod.rs
new file mode 100644
index 0000000000..54f6f1137f
--- /dev/null
+++ b/third_party/rust/litrs/src/char/mod.rs
@@ -0,0 +1,105 @@
+use std::fmt;
+
+use crate::{
+ Buffer, ParseError,
+ err::{perr, ParseErrorKind::*},
+ escape::unescape,
+ parse::{first_byte_or_empty, check_suffix},
+};
+
+
+/// A character literal, e.g. `'g'` or `'🦊'`.
+///
+/// See [the reference][ref] for more information.
+///
+/// [ref]: https://doc.rust-lang.org/reference/tokens.html#character-literals
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct CharLit<B: Buffer> {
+ raw: B,
+ /// Start index of the suffix or `raw.len()` if there is no suffix.
+ start_suffix: usize,
+ value: char,
+}
+
+impl<B: Buffer> CharLit<B> {
+ /// Parses the input as a character literal. Returns an error if the input
+ /// is invalid or represents a different kind of literal.
+ pub fn parse(input: B) -> Result<Self, ParseError> {
+ match first_byte_or_empty(&input)? {
+ b'\'' => {
+ let (value, start_suffix) = parse_impl(&input)?;
+ Ok(Self { raw: input, value, start_suffix })
+ },
+ _ => Err(perr(0, DoesNotStartWithQuote)),
+ }
+ }
+
+ /// Returns the character value that this literal represents.
+ pub fn value(&self) -> char {
+ self.value
+ }
+
+ /// The optional suffix. Returns `""` if the suffix is empty/does not exist.
+ pub fn suffix(&self) -> &str {
+ &(*self.raw)[self.start_suffix..]
+ }
+
+ /// Returns the raw input that was passed to `parse`.
+ pub fn raw_input(&self) -> &str {
+ &self.raw
+ }
+
+ /// Returns the raw input that was passed to `parse`, potentially owned.
+ pub fn into_raw_input(self) -> B {
+ self.raw
+ }
+
+}
+
+impl CharLit<&str> {
+ /// Makes a copy of the underlying buffer and returns the owned version of
+ /// `Self`.
+ pub fn to_owned(&self) -> CharLit<String> {
+ CharLit {
+ raw: self.raw.to_owned(),
+ start_suffix: self.start_suffix,
+ value: self.value,
+ }
+ }
+}
+
+impl<B: Buffer> fmt::Display for CharLit<B> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.pad(&self.raw)
+ }
+}
+
+/// Precondition: first character in input must be `'`.
+#[inline(never)]
+pub(crate) fn parse_impl(input: &str) -> Result<(char, usize), ParseError> {
+ let first = input.chars().nth(1).ok_or(perr(None, UnterminatedCharLiteral))?;
+ let (c, len) = match first {
+ '\'' if input.chars().nth(2) == Some('\'') => return Err(perr(1, UnescapedSingleQuote)),
+ '\'' => return Err(perr(None, EmptyCharLiteral)),
+ '\n' | '\t' | '\r'
+ => return Err(perr(1, UnescapedSpecialWhitespace)),
+
+ '\\' => unescape::<char>(&input[1..], 1)?,
+ other => (other, other.len_utf8()),
+ };
+
+ match input[1 + len..].find('\'') {
+ Some(0) => {}
+ Some(_) => return Err(perr(None, OverlongCharLiteral)),
+ None => return Err(perr(None, UnterminatedCharLiteral)),
+ }
+
+ let start_suffix = 1 + len + 1;
+ let suffix = &input[start_suffix..];
+ check_suffix(suffix).map_err(|kind| perr(start_suffix, kind))?;
+
+ Ok((c, start_suffix))
+}
+
+#[cfg(test)]
+mod tests;
diff --git a/third_party/rust/litrs/src/char/tests.rs b/third_party/rust/litrs/src/char/tests.rs
new file mode 100644
index 0000000000..19219db73b
--- /dev/null
+++ b/third_party/rust/litrs/src/char/tests.rs
@@ -0,0 +1,227 @@
+use crate::{Literal, test_util::{assert_parse_ok_eq, assert_roundtrip}};
+use super::CharLit;
+
+// ===== Utility functions =======================================================================
+
+macro_rules! check {
+ ($lit:literal) => { check!($lit, stringify!($lit), "") };
+ ($lit:literal, $input:expr, $suffix:literal) => {
+ let input = $input;
+ let expected = CharLit {
+ raw: input,
+ start_suffix: input.len() - $suffix.len(),
+ value: $lit,
+ };
+
+ assert_parse_ok_eq(input, CharLit::parse(input), expected.clone(), "CharLit::parse");
+ assert_parse_ok_eq(input, Literal::parse(input), Literal::Char(expected), "Literal::parse");
+ let lit = CharLit::parse(input).unwrap();
+ assert_eq!(lit.value(), $lit);
+ assert_eq!(lit.suffix(), $suffix);
+ assert_roundtrip(expected.to_owned(), input);
+ };
+}
+
+
+// ===== Actual tests ============================================================================
+
+#[test]
+fn alphanumeric() {
+ check!('a');
+ check!('b');
+ check!('y');
+ check!('z');
+ check!('A');
+ check!('B');
+ check!('Y');
+ check!('Z');
+
+ check!('0');
+ check!('1');
+ check!('8');
+ check!('9');
+}
+
+#[test]
+fn special_chars() {
+ check!(' ');
+ check!('!');
+ check!('"');
+ check!('#');
+ check!('$');
+ check!('%');
+ check!('&');
+ check!('(');
+ check!(')');
+ check!('*');
+ check!('+');
+ check!(',');
+ check!('-');
+ check!('.');
+ check!('/');
+ check!(':');
+ check!(';');
+ check!('<');
+ check!('=');
+ check!('>');
+ check!('?');
+ check!('@');
+ check!('[');
+ check!(']');
+ check!('^');
+ check!('_');
+ check!('`');
+ check!('{');
+ check!('|');
+ check!('}');
+ check!('~');
+}
+
+#[test]
+fn unicode() {
+ check!('న');
+ check!('犬');
+ check!('🦊');
+}
+
+#[test]
+fn quote_escapes() {
+ check!('\'');
+ check!('\"');
+}
+
+#[test]
+fn ascii_escapes() {
+ check!('\n');
+ check!('\r');
+ check!('\t');
+ check!('\\');
+ check!('\0');
+
+ check!('\x00');
+ check!('\x01');
+ check!('\x0c');
+ check!('\x0D');
+ check!('\x13');
+ check!('\x30');
+ check!('\x30');
+ check!('\x4B');
+ check!('\x6b');
+ check!('\x7F');
+ check!('\x7f');
+}
+
+#[test]
+fn unicode_escapes() {
+ check!('\u{0}');
+ check!('\u{00}');
+ check!('\u{b}');
+ check!('\u{B}');
+ check!('\u{7e}');
+ check!('\u{E4}');
+ check!('\u{e4}');
+ check!('\u{fc}');
+ check!('\u{Fc}');
+ check!('\u{fC}');
+ check!('\u{FC}');
+ check!('\u{b10}');
+ check!('\u{B10}');
+ check!('\u{0b10}');
+ check!('\u{2764}');
+ check!('\u{1f602}');
+ check!('\u{1F602}');
+
+ check!('\u{0}');
+ check!('\u{0__}');
+ check!('\u{3_b}');
+ check!('\u{1_F_6_0_2}');
+ check!('\u{1_F6_02_____}');
+}
+
+#[test]
+fn suffixes() {
+ check!('a', r##"'a'peter"##, "peter");
+ check!('#', r##"'#'peter"##, "peter");
+ check!('\n', r##"'\n'peter"##, "peter");
+ check!('\'', r##"'\''peter"##, "peter");
+ check!('\"', r##"'\"'peter"##, "peter");
+}
+
+#[test]
+fn invald_ascii_escapes() {
+ assert_err!(CharLit, r"'\x80'", NonAsciiXEscape, 1..5);
+ assert_err!(CharLit, r"'\x81'", NonAsciiXEscape, 1..5);
+ assert_err!(CharLit, r"'\x8a'", NonAsciiXEscape, 1..5);
+ assert_err!(CharLit, r"'\x8F'", NonAsciiXEscape, 1..5);
+ assert_err!(CharLit, r"'\xa0'", NonAsciiXEscape, 1..5);
+ assert_err!(CharLit, r"'\xB0'", NonAsciiXEscape, 1..5);
+ assert_err!(CharLit, r"'\xc3'", NonAsciiXEscape, 1..5);
+ assert_err!(CharLit, r"'\xDf'", NonAsciiXEscape, 1..5);
+ assert_err!(CharLit, r"'\xff'", NonAsciiXEscape, 1..5);
+ assert_err!(CharLit, r"'\xfF'", NonAsciiXEscape, 1..5);
+ assert_err!(CharLit, r"'\xFf'", NonAsciiXEscape, 1..5);
+ assert_err!(CharLit, r"'\xFF'", NonAsciiXEscape, 1..5);
+}
+
+#[test]
+fn invalid_escapes() {
+ assert_err!(CharLit, r"'\a'", UnknownEscape, 1..3);
+ assert_err!(CharLit, r"'\y'", UnknownEscape, 1..3);
+ assert_err!(CharLit, r"'\", UnterminatedEscape, 1);
+ assert_err!(CharLit, r"'\x'", UnterminatedEscape, 1..4);
+ assert_err!(CharLit, r"'\x1'", InvalidXEscape, 1..5);
+ assert_err!(CharLit, r"'\xaj'", InvalidXEscape, 1..5);
+ assert_err!(CharLit, r"'\xjb'", InvalidXEscape, 1..5);
+}
+
+#[test]
+fn invalid_unicode_escapes() {
+ assert_err!(CharLit, r"'\u'", UnicodeEscapeWithoutBrace, 1..3);
+ assert_err!(CharLit, r"'\u '", UnicodeEscapeWithoutBrace, 1..3);
+ assert_err!(CharLit, r"'\u3'", UnicodeEscapeWithoutBrace, 1..3);
+
+ assert_err!(CharLit, r"'\u{'", UnterminatedUnicodeEscape, 1..5);
+ assert_err!(CharLit, r"'\u{12'", UnterminatedUnicodeEscape, 1..7);
+ assert_err!(CharLit, r"'\u{a0b'", UnterminatedUnicodeEscape, 1..8);
+ assert_err!(CharLit, r"'\u{a0_b '", UnterminatedUnicodeEscape, 1..11);
+
+ assert_err!(CharLit, r"'\u{_}'", InvalidStartOfUnicodeEscape, 4);
+ assert_err!(CharLit, r"'\u{_5f}'", InvalidStartOfUnicodeEscape, 4);
+
+ assert_err!(CharLit, r"'\u{x}'", NonHexDigitInUnicodeEscape, 4);
+ assert_err!(CharLit, r"'\u{0x}'", NonHexDigitInUnicodeEscape, 5);
+ assert_err!(CharLit, r"'\u{3bx}'", NonHexDigitInUnicodeEscape, 6);
+ assert_err!(CharLit, r"'\u{3b_x}'", NonHexDigitInUnicodeEscape, 7);
+ assert_err!(CharLit, r"'\u{4x_}'", NonHexDigitInUnicodeEscape, 5);
+
+ assert_err!(CharLit, r"'\u{1234567}'", TooManyDigitInUnicodeEscape, 10);
+ assert_err!(CharLit, r"'\u{1234567}'", TooManyDigitInUnicodeEscape, 10);
+ assert_err!(CharLit, r"'\u{1_23_4_56_7}'", TooManyDigitInUnicodeEscape, 14);
+ assert_err!(CharLit, r"'\u{abcdef123}'", TooManyDigitInUnicodeEscape, 10);
+
+ assert_err!(CharLit, r"'\u{110000}'", InvalidUnicodeEscapeChar, 1..10);
+}
+
+#[test]
+fn parse_err() {
+ assert_err!(CharLit, r"''", EmptyCharLiteral, None);
+ assert_err!(CharLit, r"' ''", UnexpectedChar, 3);
+
+ assert_err!(CharLit, r"'", UnterminatedCharLiteral, None);
+ assert_err!(CharLit, r"'a", UnterminatedCharLiteral, None);
+ assert_err!(CharLit, r"'\n", UnterminatedCharLiteral, None);
+ assert_err!(CharLit, r"'\x35", UnterminatedCharLiteral, None);
+
+ assert_err!(CharLit, r"'ab'", OverlongCharLiteral, None);
+ assert_err!(CharLit, r"'a _'", OverlongCharLiteral, None);
+ assert_err!(CharLit, r"'\n3'", OverlongCharLiteral, None);
+
+ assert_err!(CharLit, r"", Empty, None);
+
+ assert_err!(CharLit, r"'''", UnescapedSingleQuote, 1);
+ assert_err!(CharLit, r"''''", UnescapedSingleQuote, 1);
+
+ assert_err!(CharLit, "'\n'", UnescapedSpecialWhitespace, 1);
+ assert_err!(CharLit, "'\t'", UnescapedSpecialWhitespace, 1);
+ assert_err!(CharLit, "'\r'", UnescapedSpecialWhitespace, 1);
+}
diff --git a/third_party/rust/litrs/src/err.rs b/third_party/rust/litrs/src/err.rs
new file mode 100644
index 0000000000..86d51dc4a8
--- /dev/null
+++ b/third_party/rust/litrs/src/err.rs
@@ -0,0 +1,371 @@
+use std::{fmt, ops::Range};
+
+
+/// An error signaling that a different kind of token was expected. Returned by
+/// the various `TryFrom` impls.
+#[derive(Debug, Clone, Copy)]
+pub struct InvalidToken {
+ pub(crate) expected: TokenKind,
+ pub(crate) actual: TokenKind,
+ pub(crate) span: Span,
+}
+
+impl InvalidToken {
+ /// Returns a token stream representing `compile_error!("msg");` where
+ /// `"msg"` is the output of `self.to_string()`. **Panics if called outside
+ /// of a proc-macro context!**
+ pub fn to_compile_error(&self) -> proc_macro::TokenStream {
+ use proc_macro::{Delimiter, Ident, Group, Punct, Spacing, TokenTree};
+
+ let span = match self.span {
+ Span::One(s) => s,
+ #[cfg(feature = "proc-macro2")]
+ Span::Two(s) => s.unwrap(),
+ };
+ let msg = self.to_string();
+ let tokens = vec![
+ TokenTree::from(Ident::new("compile_error", span)),
+ TokenTree::from(Punct::new('!', Spacing::Alone)),
+ TokenTree::from(Group::new(
+ Delimiter::Parenthesis,
+ TokenTree::from(proc_macro::Literal::string(&msg)).into(),
+ )),
+ ];
+
+
+ tokens.into_iter().map(|mut t| { t.set_span(span); t }).collect()
+ }
+
+ /// Like [`to_compile_error`][Self::to_compile_error], but returns a token
+ /// stream from `proc_macro2` and does not panic outside of a proc-macro
+ /// context.
+ #[cfg(feature = "proc-macro2")]
+ pub fn to_compile_error2(&self) -> proc_macro2::TokenStream {
+ use proc_macro2::{Delimiter, Ident, Group, Punct, Spacing, TokenTree};
+
+ let span = match self.span {
+ Span::One(s) => proc_macro2::Span::from(s),
+ Span::Two(s) => s,
+ };
+ let msg = self.to_string();
+ let tokens = vec![
+ TokenTree::from(Ident::new("compile_error", span)),
+ TokenTree::from(Punct::new('!', Spacing::Alone)),
+ TokenTree::from(Group::new(
+ Delimiter::Parenthesis,
+ TokenTree::from(proc_macro2::Literal::string(&msg)).into(),
+ )),
+ ];
+
+
+ tokens.into_iter().map(|mut t| { t.set_span(span); t }).collect()
+ }
+}
+
+impl std::error::Error for InvalidToken {}
+
+impl fmt::Display for InvalidToken {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ fn kind_desc(kind: TokenKind) -> &'static str {
+ match kind {
+ TokenKind::Punct => "a punctuation character",
+ TokenKind::Ident => "an identifier",
+ TokenKind::Group => "a group",
+ TokenKind::Literal => "a literal",
+ TokenKind::BoolLit => "a bool literal (`true` or `false`)",
+ TokenKind::ByteLit => "a byte literal (e.g. `b'r')",
+ TokenKind::ByteStringLit => r#"a byte string literal (e.g. `b"fox"`)"#,
+ TokenKind::CharLit => "a character literal (e.g. `'P'`)",
+ TokenKind::FloatLit => "a float literal (e.g. `3.14`)",
+ TokenKind::IntegerLit => "an integer literal (e.g. `27`)",
+ TokenKind::StringLit => r#"a string literal (e.g. "Ferris")"#,
+ }
+ }
+
+ write!(f, "expected {}, but found {}", kind_desc(self.expected), kind_desc(self.actual))
+ }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub(crate) enum TokenKind {
+ Punct,
+ Ident,
+ Group,
+ Literal,
+ BoolLit,
+ ByteLit,
+ ByteStringLit,
+ CharLit,
+ FloatLit,
+ IntegerLit,
+ StringLit,
+}
+
+/// Unfortunately, we have to deal with both cases.
+#[derive(Debug, Clone, Copy)]
+pub(crate) enum Span {
+ One(proc_macro::Span),
+ #[cfg(feature = "proc-macro2")]
+ Two(proc_macro2::Span),
+}
+
+impl From<proc_macro::Span> for Span {
+ fn from(src: proc_macro::Span) -> Self {
+ Self::One(src)
+ }
+}
+
+#[cfg(feature = "proc-macro2")]
+impl From<proc_macro2::Span> for Span {
+ fn from(src: proc_macro2::Span) -> Self {
+ Self::Two(src)
+ }
+}
+
+/// Errors during parsing.
+///
+/// This type should be seen primarily for error reporting and not for catching
+/// specific cases. The span and error kind are not guaranteed to be stable
+/// over different versions of this library, meaning that a returned error can
+/// change from one version to the next. There are simply too many fringe cases
+/// that are not easy to classify as a specific error kind. It depends entirely
+/// on the specific parser code how an invalid input is categorized.
+///
+/// Consider these examples:
+/// - `'\` can be seen as
+/// - invalid escape in character literal, or
+/// - unterminated character literal.
+/// - `'''` can be seen as
+/// - empty character literal, or
+/// - unescaped quote character in character literal.
+/// - `0b64` can be seen as
+/// - binary integer literal with invalid digit 6, or
+/// - binary integer literal with invalid digit 4, or
+/// - decimal integer literal with invalid digit b, or
+/// - decimal integer literal 0 with unknown type suffix `b64`.
+///
+/// If you want to see more if these examples, feel free to check out the unit
+/// tests of this library.
+///
+/// While this library does its best to emit sensible and precise errors, and to
+/// keep the returned errors as stable as possible, full stability cannot be
+/// guaranteed.
+#[derive(Debug, Clone)]
+pub struct ParseError {
+ pub(crate) span: Option<Range<usize>>,
+ pub(crate) kind: ParseErrorKind,
+}
+
+impl ParseError {
+ /// Returns a span of this error, if available. **Note**: the returned span
+ /// might change in future versions of this library. See [the documentation
+ /// of this type][ParseError] for more information.
+ pub fn span(&self) -> Option<Range<usize>> {
+ self.span.clone()
+ }
+}
+
+/// This is a free standing function instead of an associated one to reduce
+/// noise around parsing code. There are lots of places that create errors, we
+/// I wanna keep them as short as possible.
+pub(crate) fn perr(span: impl SpanLike, kind: ParseErrorKind) -> ParseError {
+ ParseError {
+ span: span.into_span(),
+ kind,
+ }
+}
+
+pub(crate) trait SpanLike {
+ fn into_span(self) -> Option<Range<usize>>;
+}
+
+impl SpanLike for Option<Range<usize>> {
+ #[inline(always)]
+ fn into_span(self) -> Option<Range<usize>> {
+ self
+ }
+}
+impl SpanLike for Range<usize> {
+ #[inline(always)]
+ fn into_span(self) -> Option<Range<usize>> {
+ Some(self)
+ }
+}
+impl SpanLike for usize {
+ #[inline(always)]
+ fn into_span(self) -> Option<Range<usize>> {
+ Some(self..self + 1)
+ }
+}
+
+
+/// Kinds of errors.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+#[non_exhaustive]
+pub(crate) enum ParseErrorKind {
+ /// The input was an empty string
+ Empty,
+
+ /// An unexpected char was encountered.
+ UnexpectedChar,
+
+ /// Literal was not recognized.
+ InvalidLiteral,
+
+ /// Input does not start with decimal digit when trying to parse an integer.
+ DoesNotStartWithDigit,
+
+ /// A digit invalid for the specified integer base was found.
+ InvalidDigit,
+
+ /// Integer literal does not contain any valid digits.
+ NoDigits,
+
+ /// Exponent of a float literal does not contain any digits.
+ NoExponentDigits,
+
+ /// An unknown escape code, e.g. `\b`.
+ UnknownEscape,
+
+ /// A started escape sequence where the input ended before the escape was
+ /// finished.
+ UnterminatedEscape,
+
+ /// An `\x` escape where the two digits are not valid hex digits.
+ InvalidXEscape,
+
+ /// A string or character literal using the `\xNN` escape where `NN > 0x7F`.
+ NonAsciiXEscape,
+
+ /// A `\u{...}` escape in a byte or byte string literal.
+ UnicodeEscapeInByteLiteral,
+
+ /// A Unicode escape that does not start with a hex digit.
+ InvalidStartOfUnicodeEscape,
+
+ /// A `\u{...}` escape that lacks the opening brace.
+ UnicodeEscapeWithoutBrace,
+
+ /// In a `\u{...}` escape, a non-hex digit and non-underscore character was
+ /// found.
+ NonHexDigitInUnicodeEscape,
+
+ /// More than 6 digits found in unicode escape.
+ TooManyDigitInUnicodeEscape,
+
+ /// The value from a unicode escape does not represent a valid character.
+ InvalidUnicodeEscapeChar,
+
+ /// A `\u{..` escape that is not terminated (lacks the closing brace).
+ UnterminatedUnicodeEscape,
+
+ /// A character literal that's not terminated.
+ UnterminatedCharLiteral,
+
+ /// A character literal that contains more than one character.
+ OverlongCharLiteral,
+
+ /// An empty character literal, i.e. `''`.
+ EmptyCharLiteral,
+
+ UnterminatedByteLiteral,
+ OverlongByteLiteral,
+ EmptyByteLiteral,
+ NonAsciiInByteLiteral,
+
+ /// A `'` character was not escaped in a character or byte literal, or a `"`
+ /// character was not escaped in a string or byte string literal.
+ UnescapedSingleQuote,
+
+ /// A \n, \t or \r raw character in a char or byte literal.
+ UnescapedSpecialWhitespace,
+
+ /// When parsing a character, byte, string or byte string literal directly
+ /// and the input does not start with the corresponding quote character
+ /// (plus optional raw string prefix).
+ DoesNotStartWithQuote,
+
+ /// Unterminated raw string literal.
+ UnterminatedRawString,
+
+ /// String literal without a `"` at the end.
+ UnterminatedString,
+
+ /// Invalid start for a string literal.
+ InvalidStringLiteralStart,
+
+ /// Invalid start for a byte literal.
+ InvalidByteLiteralStart,
+
+ InvalidByteStringLiteralStart,
+
+ /// An literal `\r` character not followed by a `\n` character in a
+ /// (raw) string or byte string literal.
+ IsolatedCr,
+
+ /// Literal suffix is not a valid identifier.
+ InvalidSuffix,
+
+ /// Returned by `Float::parse` if an integer literal (no fractional nor
+ /// exponent part) is passed.
+ UnexpectedIntegerLit,
+
+ /// Integer suffixes cannot start with `e` or `E` as this conflicts with the
+ /// grammar for float literals.
+ IntegerSuffixStartingWithE,
+}
+
+impl std::error::Error for ParseError {}
+
+impl fmt::Display for ParseError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use ParseErrorKind::*;
+
+ let description = match self.kind {
+ Empty => "input is empty",
+ UnexpectedChar => "unexpected character",
+ InvalidLiteral => "invalid literal",
+ DoesNotStartWithDigit => "number literal does not start with decimal digit",
+ InvalidDigit => "integer literal contains a digit invalid for its base",
+ NoDigits => "integer literal does not contain any digits",
+ NoExponentDigits => "exponent of floating point literal does not contain any digits",
+ UnknownEscape => "unknown escape",
+ UnterminatedEscape => "unterminated escape: input ended too soon",
+ InvalidXEscape => r"invalid `\x` escape: not followed by two hex digits",
+ NonAsciiXEscape => r"`\x` escape in char/string literal exceed ASCII range",
+ UnicodeEscapeInByteLiteral => r"`\u{...}` escape in byte (string) literal not allowed",
+ InvalidStartOfUnicodeEscape => r"invalid start of `\u{...}` escape",
+ UnicodeEscapeWithoutBrace => r"`Unicode \u{...}` escape without opening brace",
+ NonHexDigitInUnicodeEscape => r"non-hex digit found in `\u{...}` escape",
+ TooManyDigitInUnicodeEscape => r"more than six digits in `\u{...}` escape",
+ InvalidUnicodeEscapeChar => r"value specified in `\u{...}` escape is not a valid char",
+ UnterminatedUnicodeEscape => r"unterminated `\u{...}` escape",
+ UnterminatedCharLiteral => "character literal is not terminated",
+ OverlongCharLiteral => "character literal contains more than one character",
+ EmptyCharLiteral => "empty character literal",
+ UnterminatedByteLiteral => "byte literal is not terminated",
+ OverlongByteLiteral => "byte literal contains more than one byte",
+ EmptyByteLiteral => "empty byte literal",
+ NonAsciiInByteLiteral => "non ASCII character in byte (string) literal",
+ UnescapedSingleQuote => "character literal contains unescaped ' character",
+ UnescapedSpecialWhitespace => r"unescaped newline (\n), tab (\t) or cr (\r) character",
+ DoesNotStartWithQuote => "invalid start for char/byte/string literal",
+ UnterminatedRawString => "unterminated raw (byte) string literal",
+ UnterminatedString => "unterminated (byte) string literal",
+ InvalidStringLiteralStart => "invalid start for string literal",
+ InvalidByteLiteralStart => "invalid start for byte literal",
+ InvalidByteStringLiteralStart => "invalid start for byte string literal",
+ IsolatedCr => r"`\r` not immediately followed by `\n` in string",
+ InvalidSuffix => "literal suffix is not a valid identifier",
+ UnexpectedIntegerLit => "expected float literal, but found integer",
+ IntegerSuffixStartingWithE => "integer literal suffix must not start with 'e' or 'E'",
+ };
+
+ description.fmt(f)?;
+ if let Some(span) = &self.span {
+ write!(f, " (at {}..{})", span.start, span.end)?;
+ }
+
+ Ok(())
+ }
+}
diff --git a/third_party/rust/litrs/src/escape.rs b/third_party/rust/litrs/src/escape.rs
new file mode 100644
index 0000000000..5eb8382bc4
--- /dev/null
+++ b/third_party/rust/litrs/src/escape.rs
@@ -0,0 +1,262 @@
+use crate::{ParseError, err::{perr, ParseErrorKind::*}, parse::{hex_digit_value, check_suffix}};
+
+
+/// Must start with `\`
+pub(crate) fn unescape<E: Escapee>(input: &str, offset: usize) -> Result<(E, usize), ParseError> {
+ let first = input.as_bytes().get(1)
+ .ok_or(perr(offset, UnterminatedEscape))?;
+ let out = match first {
+ // Quote escapes
+ b'\'' => (E::from_byte(b'\''), 2),
+ b'"' => (E::from_byte(b'"'), 2),
+
+ // Ascii escapes
+ b'n' => (E::from_byte(b'\n'), 2),
+ b'r' => (E::from_byte(b'\r'), 2),
+ b't' => (E::from_byte(b'\t'), 2),
+ b'\\' => (E::from_byte(b'\\'), 2),
+ b'0' => (E::from_byte(b'\0'), 2),
+ b'x' => {
+ let hex_string = input.get(2..4)
+ .ok_or(perr(offset..offset + input.len(), UnterminatedEscape))?
+ .as_bytes();
+ let first = hex_digit_value(hex_string[0])
+ .ok_or(perr(offset..offset + 4, InvalidXEscape))?;
+ let second = hex_digit_value(hex_string[1])
+ .ok_or(perr(offset..offset + 4, InvalidXEscape))?;
+ let value = second + 16 * first;
+
+ if E::SUPPORTS_UNICODE && value > 0x7F {
+ return Err(perr(offset..offset + 4, NonAsciiXEscape));
+ }
+
+ (E::from_byte(value), 4)
+ },
+
+ // Unicode escape
+ b'u' => {
+ if !E::SUPPORTS_UNICODE {
+ return Err(perr(offset..offset + 2, UnicodeEscapeInByteLiteral));
+ }
+
+ if input.as_bytes().get(2) != Some(&b'{') {
+ return Err(perr(offset..offset + 2, UnicodeEscapeWithoutBrace));
+ }
+
+ let closing_pos = input.bytes().position(|b| b == b'}')
+ .ok_or(perr(offset..offset + input.len(), UnterminatedUnicodeEscape))?;
+
+ let inner = &input[3..closing_pos];
+ if inner.as_bytes().first() == Some(&b'_') {
+ return Err(perr(4, InvalidStartOfUnicodeEscape));
+ }
+
+ let mut v: u32 = 0;
+ let mut digit_count = 0;
+ for (i, b) in inner.bytes().enumerate() {
+ if b == b'_'{
+ continue;
+ }
+
+ let digit = hex_digit_value(b)
+ .ok_or(perr(offset + 3 + i, NonHexDigitInUnicodeEscape))?;
+
+ if digit_count == 6 {
+ return Err(perr(offset + 3 + i, TooManyDigitInUnicodeEscape));
+ }
+ digit_count += 1;
+ v = 16 * v + digit as u32;
+ }
+
+ let c = std::char::from_u32(v)
+ .ok_or(perr(offset..closing_pos + 1, InvalidUnicodeEscapeChar))?;
+
+ (E::from_char(c), closing_pos + 1)
+ }
+
+ _ => return Err(perr(offset..offset + 2, UnknownEscape)),
+ };
+
+ Ok(out)
+}
+
+pub(crate) trait Escapee: Into<char> {
+ const SUPPORTS_UNICODE: bool;
+ fn from_byte(b: u8) -> Self;
+ fn from_char(c: char) -> Self;
+}
+
+impl Escapee for u8 {
+ const SUPPORTS_UNICODE: bool = false;
+ fn from_byte(b: u8) -> Self {
+ b
+ }
+ fn from_char(_: char) -> Self {
+ panic!("bug: `<u8 as Escapee>::from_char` was called");
+ }
+}
+
+impl Escapee for char {
+ const SUPPORTS_UNICODE: bool = true;
+ fn from_byte(b: u8) -> Self {
+ b.into()
+ }
+ fn from_char(c: char) -> Self {
+ c
+ }
+}
+
+/// Checks whether the character is skipped after a string continue start
+/// (unescaped backlash followed by `\n`).
+fn is_string_continue_skipable_whitespace(b: u8) -> bool {
+ b == b' ' || b == b'\t' || b == b'\n' || b == b'\r'
+}
+
+/// Unescapes a whole string or byte string.
+#[inline(never)]
+pub(crate) fn unescape_string<E: Escapee>(
+ input: &str,
+ offset: usize,
+) -> Result<(Option<String>, usize), ParseError> {
+ let mut closing_quote_pos = None;
+ let mut i = offset;
+ let mut end_last_escape = offset;
+ let mut value = String::new();
+ while i < input.len() {
+ match input.as_bytes()[i] {
+ // Handle "string continue".
+ b'\\' if input.as_bytes().get(i + 1) == Some(&b'\n') => {
+ value.push_str(&input[end_last_escape..i]);
+
+ // Find the first non-whitespace character.
+ let end_escape = input[i + 2..].bytes()
+ .position(|b| !is_string_continue_skipable_whitespace(b))
+ .ok_or(perr(None, UnterminatedString))?;
+
+ i += 2 + end_escape;
+ end_last_escape = i;
+ }
+ b'\\' => {
+ let (c, len) = unescape::<E>(&input[i..input.len() - 1], i)?;
+ value.push_str(&input[end_last_escape..i]);
+ value.push(c.into());
+ i += len;
+ end_last_escape = i;
+ }
+ b'\r' => {
+ if input.as_bytes().get(i + 1) == Some(&b'\n') {
+ value.push_str(&input[end_last_escape..i]);
+ value.push('\n');
+ i += 2;
+ end_last_escape = i;
+ } else {
+ return Err(perr(i, IsolatedCr))
+ }
+ }
+ b'"' => {
+ closing_quote_pos = Some(i);
+ break;
+ },
+ b if !E::SUPPORTS_UNICODE && !b.is_ascii()
+ => return Err(perr(i, NonAsciiInByteLiteral)),
+ _ => i += 1,
+ }
+ }
+
+ let closing_quote_pos = closing_quote_pos.ok_or(perr(None, UnterminatedString))?;
+
+ let start_suffix = closing_quote_pos + 1;
+ let suffix = &input[start_suffix..];
+ check_suffix(suffix).map_err(|kind| perr(start_suffix, kind))?;
+
+ // `value` is only empty if there was no escape in the input string
+ // (with the special case of the input being empty). This means the
+ // string value basically equals the input, so we store `None`.
+ let value = if value.is_empty() {
+ None
+ } else {
+ // There was an escape in the string, so we need to push the
+ // remaining unescaped part of the string still.
+ value.push_str(&input[end_last_escape..closing_quote_pos]);
+ Some(value)
+ };
+
+ Ok((value, start_suffix))
+}
+
+/// Reads and checks a raw (byte) string literal, converting `\r\n` sequences to
+/// just `\n` sequences. Returns an optional new string (if the input contained
+/// any `\r\n`) and the number of hashes used by the literal.
+#[inline(never)]
+pub(crate) fn scan_raw_string<E: Escapee>(
+ input: &str,
+ offset: usize,
+) -> Result<(Option<String>, u32, usize), ParseError> {
+ // Raw string literal
+ let num_hashes = input[offset..].bytes().position(|b| b != b'#')
+ .ok_or(perr(None, InvalidLiteral))?;
+
+ if input.as_bytes().get(offset + num_hashes) != Some(&b'"') {
+ return Err(perr(None, InvalidLiteral));
+ }
+ let start_inner = offset + num_hashes + 1;
+ let hashes = &input[offset..num_hashes + offset];
+
+ let mut closing_quote_pos = None;
+ let mut i = start_inner;
+ let mut end_last_escape = start_inner;
+ let mut value = String::new();
+ while i < input.len() {
+ let b = input.as_bytes()[i];
+ if b == b'"' && input[i + 1..].starts_with(hashes) {
+ closing_quote_pos = Some(i);
+ break;
+ }
+
+ if b == b'\r' {
+ // Convert `\r\n` into `\n`. This is currently not well documented
+ // in the Rust reference, but is done even for raw strings. That's
+ // because rustc simply converts all line endings when reading
+ // source files.
+ if input.as_bytes().get(i + 1) == Some(&b'\n') {
+ value.push_str(&input[end_last_escape..i]);
+ value.push('\n');
+ i += 2;
+ end_last_escape = i;
+ continue;
+ } else if E::SUPPORTS_UNICODE {
+ // If no \n follows the \r and we are scanning a raw string
+ // (not raw byte string), we error.
+ return Err(perr(i, IsolatedCr))
+ }
+ }
+
+ if !E::SUPPORTS_UNICODE {
+ if !b.is_ascii() {
+ return Err(perr(i, NonAsciiInByteLiteral));
+ }
+ }
+
+ i += 1;
+ }
+
+ let closing_quote_pos = closing_quote_pos.ok_or(perr(None, UnterminatedRawString))?;
+
+ let start_suffix = closing_quote_pos + num_hashes + 1;
+ let suffix = &input[start_suffix..];
+ check_suffix(suffix).map_err(|kind| perr(start_suffix, kind))?;
+
+ // `value` is only empty if there was no \r\n in the input string (with the
+ // special case of the input being empty). This means the string value
+ // equals the input, so we store `None`.
+ let value = if value.is_empty() {
+ None
+ } else {
+ // There was an \r\n in the string, so we need to push the remaining
+ // unescaped part of the string still.
+ value.push_str(&input[end_last_escape..closing_quote_pos]);
+ Some(value)
+ };
+
+ Ok((value, num_hashes as u32, start_suffix))
+}
diff --git a/third_party/rust/litrs/src/float/mod.rs b/third_party/rust/litrs/src/float/mod.rs
new file mode 100644
index 0000000000..0518633a6b
--- /dev/null
+++ b/third_party/rust/litrs/src/float/mod.rs
@@ -0,0 +1,257 @@
+use std::{fmt, str::FromStr};
+
+use crate::{
+ Buffer, ParseError,
+ err::{perr, ParseErrorKind::*},
+ parse::{end_dec_digits, first_byte_or_empty, check_suffix},
+};
+
+
+
+/// A floating point literal, e.g. `3.14`, `8.`, `135e12`, or `1.956e2f64`.
+///
+/// This kind of literal has several forms, but generally consists of a main
+/// number part, an optional exponent and an optional type suffix. See
+/// [the reference][ref] for more information.
+///
+/// A leading minus sign `-` is not part of the literal grammar! `-3.14` are two
+/// tokens in the Rust grammar. Further, `27` and `27f32` are both not float,
+/// but integer literals! Consequently `FloatLit::parse` will reject them.
+///
+///
+/// [ref]: https://doc.rust-lang.org/reference/tokens.html#floating-point-literals
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct FloatLit<B: Buffer> {
+ /// The whole raw input. The `usize` fields in this struct partition this
+ /// string. Always true: `end_integer_part <= end_fractional_part`.
+ ///
+ /// ```text
+ /// 12_3.4_56e789f32
+ /// ╷ ╷ ╷
+ /// | | └ end_number_part = 13
+ /// | └ end_fractional_part = 9
+ /// └ end_integer_part = 4
+ ///
+ /// 246.
+ /// ╷╷
+ /// |└ end_fractional_part = end_number_part = 4
+ /// └ end_integer_part = 3
+ ///
+ /// 1234e89
+ /// ╷ ╷
+ /// | └ end_number_part = 7
+ /// └ end_integer_part = end_fractional_part = 4
+ /// ```
+ raw: B,
+
+ /// The first index not part of the integer part anymore. Since the integer
+ /// part is at the start, this is also the length of that part.
+ end_integer_part: usize,
+
+ /// The first index after the fractional part.
+ end_fractional_part: usize,
+
+ /// The first index after the whole number part (everything except type suffix).
+ end_number_part: usize,
+}
+
+impl<B: Buffer> FloatLit<B> {
+ /// Parses the input as a floating point literal. Returns an error if the
+ /// input is invalid or represents a different kind of literal. Will also
+ /// reject decimal integer literals like `23` or `17f32`, in accordance
+ /// with the spec.
+ pub fn parse(s: B) -> Result<Self, ParseError> {
+ match first_byte_or_empty(&s)? {
+ b'0'..=b'9' => {
+ // TODO: simplify once RFC 2528 is stabilized
+ let FloatLit {
+ end_integer_part,
+ end_fractional_part,
+ end_number_part,
+ ..
+ } = parse_impl(&s)?;
+
+ Ok(Self { raw: s, end_integer_part, end_fractional_part, end_number_part })
+ },
+ _ => Err(perr(0, DoesNotStartWithDigit)),
+ }
+ }
+
+ /// Returns the number part (including integer part, fractional part and
+ /// exponent), but without the suffix. If you want an actual floating
+ /// point value, you need to parse this string, e.g. with `f32::from_str`
+ /// or an external crate.
+ pub fn number_part(&self) -> &str {
+ &(*self.raw)[..self.end_number_part]
+ }
+
+ /// Returns the non-empty integer part of this literal.
+ pub fn integer_part(&self) -> &str {
+ &(*self.raw)[..self.end_integer_part]
+ }
+
+ /// Returns the optional fractional part of this literal. Does not include
+ /// the period. If a period exists in the input, `Some` is returned, `None`
+ /// otherwise. Note that `Some("")` might be returned, e.g. for `3.`.
+ pub fn fractional_part(&self) -> Option<&str> {
+ if self.end_integer_part == self.end_fractional_part {
+ None
+ } else {
+ Some(&(*self.raw)[self.end_integer_part + 1..self.end_fractional_part])
+ }
+ }
+
+ /// Optional exponent part. Might be empty if there was no exponent part in
+ /// the input. Includes the `e` or `E` at the beginning.
+ pub fn exponent_part(&self) -> &str {
+ &(*self.raw)[self.end_fractional_part..self.end_number_part]
+ }
+
+ /// The optional suffix. Returns `""` if the suffix is empty/does not exist.
+ pub fn suffix(&self) -> &str {
+ &(*self.raw)[self.end_number_part..]
+ }
+
+ /// Returns the raw input that was passed to `parse`.
+ pub fn raw_input(&self) -> &str {
+ &self.raw
+ }
+
+ /// Returns the raw input that was passed to `parse`, potentially owned.
+ pub fn into_raw_input(self) -> B {
+ self.raw
+ }
+}
+
+impl FloatLit<&str> {
+ /// Makes a copy of the underlying buffer and returns the owned version of
+ /// `Self`.
+ pub fn to_owned(&self) -> FloatLit<String> {
+ FloatLit {
+ raw: self.raw.to_owned(),
+ end_integer_part: self.end_integer_part,
+ end_fractional_part: self.end_fractional_part,
+ end_number_part: self.end_number_part,
+ }
+ }
+}
+
+impl<B: Buffer> fmt::Display for FloatLit<B> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "{}", &*self.raw)
+ }
+}
+
+/// Precondition: first byte of string has to be in `b'0'..=b'9'`.
+#[inline(never)]
+pub(crate) fn parse_impl(input: &str) -> Result<FloatLit<&str>, ParseError> {
+ // Integer part.
+ let end_integer_part = end_dec_digits(input.as_bytes());
+ let rest = &input[end_integer_part..];
+
+
+ // Fractional part.
+ let end_fractional_part = if rest.as_bytes().get(0) == Some(&b'.') {
+ // The fractional part must not start with `_`.
+ if rest.as_bytes().get(1) == Some(&b'_') {
+ return Err(perr(end_integer_part + 1, UnexpectedChar));
+ }
+
+ end_dec_digits(rest[1..].as_bytes()) + 1 + end_integer_part
+ } else {
+ end_integer_part
+ };
+ let rest = &input[end_fractional_part..];
+
+ // If we have a period that is not followed by decimal digits, the
+ // literal must end now.
+ if end_integer_part + 1 == end_fractional_part && !rest.is_empty() {
+ return Err(perr(end_integer_part + 1, UnexpectedChar));
+ }
+
+ // Optional exponent.
+ let end_number_part = if rest.starts_with('e') || rest.starts_with('E') {
+ // Strip single - or + sign at the beginning.
+ let exp_number_start = match rest.as_bytes().get(1) {
+ Some(b'-') | Some(b'+') => 2,
+ _ => 1,
+ };
+
+ // Find end of exponent and make sure there is at least one digit.
+ let end_exponent = end_dec_digits(rest[exp_number_start..].as_bytes()) + exp_number_start;
+ if !rest[exp_number_start..end_exponent].bytes().any(|b| matches!(b, b'0'..=b'9')) {
+ return Err(perr(
+ end_fractional_part..end_fractional_part + end_exponent,
+ NoExponentDigits,
+ ));
+ }
+
+ end_exponent + end_fractional_part
+ } else {
+ end_fractional_part
+ };
+
+ // Make sure the suffix is valid.
+ let suffix = &input[end_number_part..];
+ check_suffix(suffix).map_err(|kind| perr(end_number_part..input.len(), kind))?;
+
+ // A float literal needs either a fractional or exponent part, otherwise its
+ // an integer literal.
+ if end_integer_part == end_number_part {
+ return Err(perr(None, UnexpectedIntegerLit));
+ }
+
+ Ok(FloatLit {
+ raw: input,
+ end_integer_part,
+ end_fractional_part,
+ end_number_part,
+ })
+}
+
+
+/// All possible float type suffixes.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+#[non_exhaustive]
+pub enum FloatType {
+ F32,
+ F64,
+}
+
+impl FloatType {
+ /// Returns the type corresponding to the given suffix (e.g. `"f32"` is
+ /// mapped to `Self::F32`). If the suffix is not a valid float type, `None`
+ /// is returned.
+ pub fn from_suffix(suffix: &str) -> Option<Self> {
+ match suffix {
+ "f32" => Some(FloatType::F32),
+ "f64" => Some(FloatType::F64),
+ _ => None,
+ }
+ }
+
+ /// Returns the suffix for this type, e.g. `"f32"` for `Self::F32`.
+ pub fn suffix(self) -> &'static str {
+ match self {
+ Self::F32 => "f32",
+ Self::F64 => "f64",
+ }
+ }
+}
+
+impl FromStr for FloatType {
+ type Err = ();
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ Self::from_suffix(s).ok_or(())
+ }
+}
+
+impl fmt::Display for FloatType {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.suffix().fmt(f)
+ }
+}
+
+
+#[cfg(test)]
+mod tests;
diff --git a/third_party/rust/litrs/src/float/tests.rs b/third_party/rust/litrs/src/float/tests.rs
new file mode 100644
index 0000000000..f22443bd19
--- /dev/null
+++ b/third_party/rust/litrs/src/float/tests.rs
@@ -0,0 +1,253 @@
+use crate::{
+ Literal, ParseError,
+ test_util::{assert_parse_ok_eq, assert_roundtrip},
+};
+use super::{FloatLit, FloatType};
+
+
+// ===== Utility functions =======================================================================
+
+/// Helper macro to check parsing a float.
+///
+/// This macro contains quite a bit of logic itself (which can be buggy of
+/// course), so we have a few test functions below to test a bunch of cases
+/// manually.
+macro_rules! check {
+ ($intpart:literal $fracpart:literal $exppart:literal $suffix:tt) => {
+ let input = concat!($intpart, $fracpart, $exppart, check!(@stringify_suffix $suffix));
+ let expected_float = FloatLit {
+ raw: input,
+ end_integer_part: $intpart.len(),
+ end_fractional_part: $intpart.len() + $fracpart.len(),
+ end_number_part: $intpart.len() + $fracpart.len() + $exppart.len(),
+ };
+
+ assert_parse_ok_eq(
+ input, FloatLit::parse(input), expected_float.clone(), "FloatLit::parse");
+ assert_parse_ok_eq(
+ input, Literal::parse(input), Literal::Float(expected_float), "Literal::parse");
+ assert_eq!(FloatLit::parse(input).unwrap().suffix(), check!(@ty $suffix));
+ assert_roundtrip(expected_float.to_owned(), input);
+ };
+ (@ty f32) => { "f32" };
+ (@ty f64) => { "f64" };
+ (@ty -) => { "" };
+ (@stringify_suffix -) => { "" };
+ (@stringify_suffix $suffix:ident) => { stringify!($suffix) };
+}
+
+
+// ===== Actual tests ===========================================================================
+
+#[test]
+fn manual_without_suffix() -> Result<(), ParseError> {
+ let f = FloatLit::parse("3.14")?;
+ assert_eq!(f.number_part(), "3.14");
+ assert_eq!(f.integer_part(), "3");
+ assert_eq!(f.fractional_part(), Some("14"));
+ assert_eq!(f.exponent_part(), "");
+ assert_eq!(f.suffix(), "");
+
+ let f = FloatLit::parse("9.")?;
+ assert_eq!(f.number_part(), "9.");
+ assert_eq!(f.integer_part(), "9");
+ assert_eq!(f.fractional_part(), Some(""));
+ assert_eq!(f.exponent_part(), "");
+ assert_eq!(f.suffix(), "");
+
+ let f = FloatLit::parse("8e1")?;
+ assert_eq!(f.number_part(), "8e1");
+ assert_eq!(f.integer_part(), "8");
+ assert_eq!(f.fractional_part(), None);
+ assert_eq!(f.exponent_part(), "e1");
+ assert_eq!(f.suffix(), "");
+
+ let f = FloatLit::parse("8E3")?;
+ assert_eq!(f.number_part(), "8E3");
+ assert_eq!(f.integer_part(), "8");
+ assert_eq!(f.fractional_part(), None);
+ assert_eq!(f.exponent_part(), "E3");
+ assert_eq!(f.suffix(), "");
+
+ let f = FloatLit::parse("8_7_6.1_23e15")?;
+ assert_eq!(f.number_part(), "8_7_6.1_23e15");
+ assert_eq!(f.integer_part(), "8_7_6");
+ assert_eq!(f.fractional_part(), Some("1_23"));
+ assert_eq!(f.exponent_part(), "e15");
+ assert_eq!(f.suffix(), "");
+
+ let f = FloatLit::parse("8.2e-_04_9")?;
+ assert_eq!(f.number_part(), "8.2e-_04_9");
+ assert_eq!(f.integer_part(), "8");
+ assert_eq!(f.fractional_part(), Some("2"));
+ assert_eq!(f.exponent_part(), "e-_04_9");
+ assert_eq!(f.suffix(), "");
+
+ Ok(())
+}
+
+#[test]
+fn manual_with_suffix() -> Result<(), ParseError> {
+ let f = FloatLit::parse("3.14f32")?;
+ assert_eq!(f.number_part(), "3.14");
+ assert_eq!(f.integer_part(), "3");
+ assert_eq!(f.fractional_part(), Some("14"));
+ assert_eq!(f.exponent_part(), "");
+ assert_eq!(FloatType::from_suffix(f.suffix()), Some(FloatType::F32));
+
+ let f = FloatLit::parse("8e1f64")?;
+ assert_eq!(f.number_part(), "8e1");
+ assert_eq!(f.integer_part(), "8");
+ assert_eq!(f.fractional_part(), None);
+ assert_eq!(f.exponent_part(), "e1");
+ assert_eq!(FloatType::from_suffix(f.suffix()), Some(FloatType::F64));
+
+ let f = FloatLit::parse("8_7_6.1_23e15f32")?;
+ assert_eq!(f.number_part(), "8_7_6.1_23e15");
+ assert_eq!(f.integer_part(), "8_7_6");
+ assert_eq!(f.fractional_part(), Some("1_23"));
+ assert_eq!(f.exponent_part(), "e15");
+ assert_eq!(FloatType::from_suffix(f.suffix()), Some(FloatType::F32));
+
+ let f = FloatLit::parse("8.2e-_04_9f64")?;
+ assert_eq!(f.number_part(), "8.2e-_04_9");
+ assert_eq!(f.integer_part(), "8");
+ assert_eq!(f.fractional_part(), Some("2"));
+ assert_eq!(f.exponent_part(), "e-_04_9");
+ assert_eq!(FloatType::from_suffix(f.suffix()), Some(FloatType::F64));
+
+ Ok(())
+}
+
+#[test]
+fn simple() {
+ check!("3" ".14" "" -);
+ check!("3" ".14" "" f32);
+ check!("3" ".14" "" f64);
+
+ check!("3" "" "e987654321" -);
+ check!("3" "" "e987654321" f64);
+
+ check!("42_888" ".05" "" -);
+ check!("42_888" ".05" "E5___" f32);
+ check!("123456789" "" "e_1" f64);
+ check!("123456789" ".99" "e_1" f64);
+ check!("123456789" ".99" "" f64);
+ check!("123456789" ".99" "" -);
+
+ check!("147" ".3_33" "" -);
+ check!("147" ".3_33__" "E3" f64);
+ check!("147" ".3_33__" "" f32);
+
+ check!("147" ".333" "e-10" -);
+ check!("147" ".333" "e-_7" f32);
+ check!("147" ".333" "e+10" -);
+ check!("147" ".333" "e+_7" f32);
+
+ check!("86" "." "" -);
+ check!("0" "." "" -);
+ check!("0_" "." "" -);
+ check!("0" ".0000001" "" -);
+ check!("0" ".000_0001" "" -);
+
+ check!("0" ".0" "e+0" -);
+ check!("0" "" "E+0" -);
+ check!("34" "" "e+0" -);
+ check!("0" ".9182" "E+0" f32);
+}
+
+#[test]
+fn non_standard_suffixes() {
+ #[track_caller]
+ fn check_suffix(
+ input: &str,
+ integer_part: &str,
+ fractional_part: Option<&str>,
+ exponent_part: &str,
+ suffix: &str,
+ ) {
+ let lit = FloatLit::parse(input)
+ .unwrap_or_else(|e| panic!("expected to parse '{}' but got {}", input, e));
+ assert_eq!(lit.integer_part(), integer_part);
+ assert_eq!(lit.fractional_part(), fractional_part);
+ assert_eq!(lit.exponent_part(), exponent_part);
+ assert_eq!(lit.suffix(), suffix);
+
+ let lit = match Literal::parse(input) {
+ Ok(Literal::Float(f)) => f,
+ other => panic!("Expected float literal, but got {:?} for '{}'", other, input),
+ };
+ assert_eq!(lit.integer_part(), integer_part);
+ assert_eq!(lit.fractional_part(), fractional_part);
+ assert_eq!(lit.exponent_part(), exponent_part);
+ assert_eq!(lit.suffix(), suffix);
+ }
+
+ check_suffix("7.1f23", "7", Some("1"), "", "f23");
+ check_suffix("7.1f320", "7", Some("1"), "", "f320");
+ check_suffix("7.1f64_", "7", Some("1"), "", "f64_");
+ check_suffix("8.1f649", "8", Some("1"), "", "f649");
+ check_suffix("8.1f64f32", "8", Some("1"), "", "f64f32");
+ check_suffix("23e2_banana", "23", None, "e2_", "banana");
+ check_suffix("23.2_banana", "23", Some("2_"), "", "banana");
+ check_suffix("23e2pe55ter", "23", None, "e2", "pe55ter");
+ check_suffix("23e2p_e55ter", "23", None, "e2", "p_e55ter");
+ check_suffix("3.15Jürgen", "3", Some("15"), "", "Jürgen");
+ check_suffix("3e2e5", "3", None, "e2", "e5");
+ check_suffix("3e2e5f", "3", None, "e2", "e5f");
+}
+
+#[test]
+fn parse_err() {
+ assert_err!(FloatLit, "", Empty, None);
+ assert_err_single!(FloatLit::parse("."), DoesNotStartWithDigit, 0);
+ assert_err_single!(FloatLit::parse("+"), DoesNotStartWithDigit, 0);
+ assert_err_single!(FloatLit::parse("-"), DoesNotStartWithDigit, 0);
+ assert_err_single!(FloatLit::parse("e"), DoesNotStartWithDigit, 0);
+ assert_err_single!(FloatLit::parse("e8"), DoesNotStartWithDigit, 0);
+ assert_err!(FloatLit, "0e", NoExponentDigits, 1..2);
+ assert_err_single!(FloatLit::parse("f32"), DoesNotStartWithDigit, 0);
+ assert_err_single!(FloatLit::parse("foo"), DoesNotStartWithDigit, 0);
+
+ assert_err_single!(FloatLit::parse("inf"), DoesNotStartWithDigit, 0);
+ assert_err_single!(FloatLit::parse("nan"), DoesNotStartWithDigit, 0);
+ assert_err_single!(FloatLit::parse("NaN"), DoesNotStartWithDigit, 0);
+ assert_err_single!(FloatLit::parse("NAN"), DoesNotStartWithDigit, 0);
+
+ assert_err_single!(FloatLit::parse("_2.7"), DoesNotStartWithDigit, 0);
+ assert_err_single!(FloatLit::parse(".5"), DoesNotStartWithDigit, 0);
+ assert_err!(FloatLit, "1e", NoExponentDigits, 1..2);
+ assert_err!(FloatLit, "1.e4", UnexpectedChar, 2);
+ assert_err!(FloatLit, "3._4", UnexpectedChar, 2);
+ assert_err!(FloatLit, "3.f32", UnexpectedChar, 2);
+ assert_err!(FloatLit, "3.e5", UnexpectedChar, 2);
+ assert_err!(FloatLit, "12345._987", UnexpectedChar, 6);
+ assert_err!(FloatLit, "46._", UnexpectedChar, 3);
+ assert_err!(FloatLit, "46.f32", UnexpectedChar, 3);
+ assert_err!(FloatLit, "46.e3", UnexpectedChar, 3);
+ assert_err!(FloatLit, "46._e3", UnexpectedChar, 3);
+ assert_err!(FloatLit, "46.e3f64", UnexpectedChar, 3);
+ assert_err!(FloatLit, "23.4e_", NoExponentDigits, 4..6);
+ assert_err!(FloatLit, "23E___f32", NoExponentDigits, 2..6);
+ assert_err!(FloatLit, "55e3.1", UnexpectedChar, 4..6);
+
+ assert_err!(FloatLit, "3.7+", UnexpectedChar, 3..4);
+ assert_err!(FloatLit, "3.7+2", UnexpectedChar, 3..5);
+ assert_err!(FloatLit, "3.7-", UnexpectedChar, 3..4);
+ assert_err!(FloatLit, "3.7-2", UnexpectedChar, 3..5);
+ assert_err!(FloatLit, "3.7e+", NoExponentDigits, 3..5);
+ assert_err!(FloatLit, "3.7e-", NoExponentDigits, 3..5);
+ assert_err!(FloatLit, "3.7e-+3", NoExponentDigits, 3..5); // suboptimal error
+ assert_err!(FloatLit, "3.7e+-3", NoExponentDigits, 3..5); // suboptimal error
+ assert_err_single!(FloatLit::parse("0x44.5"), InvalidSuffix, 1..6);
+
+ assert_err_single!(FloatLit::parse("3"), UnexpectedIntegerLit, None);
+ assert_err_single!(FloatLit::parse("35_389"), UnexpectedIntegerLit, None);
+ assert_err_single!(FloatLit::parse("9_8_7f32"), UnexpectedIntegerLit, None);
+ assert_err_single!(FloatLit::parse("9_8_7banana"), UnexpectedIntegerLit, None);
+ assert_err_single!(FloatLit::parse("7f23"), UnexpectedIntegerLit, None);
+ assert_err_single!(FloatLit::parse("7f320"), UnexpectedIntegerLit, None);
+ assert_err_single!(FloatLit::parse("7f64_"), UnexpectedIntegerLit, None);
+ assert_err_single!(FloatLit::parse("8f649"), UnexpectedIntegerLit, None);
+ assert_err_single!(FloatLit::parse("8f64f32"), UnexpectedIntegerLit, None);
+}
diff --git a/third_party/rust/litrs/src/impls.rs b/third_party/rust/litrs/src/impls.rs
new file mode 100644
index 0000000000..61a314dd84
--- /dev/null
+++ b/third_party/rust/litrs/src/impls.rs
@@ -0,0 +1,401 @@
+use std::convert::TryFrom;
+
+use crate::{Literal, err::{InvalidToken, TokenKind}};
+
+
+/// Helper macro to call a `callback` macro four times for all combinations of
+/// `proc_macro`/`proc_macro2` and `&`/owned.
+macro_rules! helper {
+ ($callback:ident, $($input:tt)*) => {
+ $callback!([proc_macro::] => $($input)*);
+ $callback!([&proc_macro::] => $($input)*);
+ #[cfg(feature = "proc-macro2")]
+ $callback!([proc_macro2::] => $($input)*);
+ #[cfg(feature = "proc-macro2")]
+ $callback!([&proc_macro2::] => $($input)*);
+ };
+}
+
+/// Like `helper!` but without reference types.
+macro_rules! helper_no_refs {
+ ($callback:ident, $($input:tt)*) => {
+ $callback!([proc_macro::] => $($input)*);
+ #[cfg(feature = "proc-macro2")]
+ $callback!([proc_macro2::] => $($input)*);
+ };
+}
+
+
+// ==============================================================================================
+// ===== `From<*Lit> for Literal`
+// ==============================================================================================
+
+macro_rules! impl_specific_lit_to_lit {
+ ($ty:ty, $variant:ident) => {
+ impl<B: crate::Buffer> From<$ty> for Literal<B> {
+ fn from(src: $ty) -> Self {
+ Literal::$variant(src)
+ }
+ }
+ };
+}
+
+impl_specific_lit_to_lit!(crate::BoolLit, Bool);
+impl_specific_lit_to_lit!(crate::IntegerLit<B>, Integer);
+impl_specific_lit_to_lit!(crate::FloatLit<B>, Float);
+impl_specific_lit_to_lit!(crate::CharLit<B>, Char);
+impl_specific_lit_to_lit!(crate::StringLit<B>, String);
+impl_specific_lit_to_lit!(crate::ByteLit<B>, Byte);
+impl_specific_lit_to_lit!(crate::ByteStringLit<B>, ByteString);
+
+
+
+// ==============================================================================================
+// ===== `From<pm::Literal> for Literal`
+// ==============================================================================================
+
+
+macro_rules! impl_tt_to_lit {
+ ([$($prefix:tt)*] => ) => {
+ impl From<$($prefix)* Literal> for Literal<String> {
+ fn from(src: $($prefix)* Literal) -> Self {
+ // We call `expect` in all these impls: this library aims to implement exactly
+ // the Rust grammar, so if we have a valid Rust literal, we should always be
+ // able to parse it.
+ Self::parse(src.to_string())
+ .expect("bug: failed to parse output of `Literal::to_string`")
+ }
+ }
+ }
+}
+
+helper!(impl_tt_to_lit, );
+
+
+// ==============================================================================================
+// ===== `TryFrom<pm::TokenTree> for Literal`
+// ==============================================================================================
+
+macro_rules! impl_tt_to_lit {
+ ([$($prefix:tt)*] => ) => {
+ impl TryFrom<$($prefix)* TokenTree> for Literal<String> {
+ type Error = InvalidToken;
+ fn try_from(tt: $($prefix)* TokenTree) -> Result<Self, Self::Error> {
+ let span = tt.span();
+ let res = match tt {
+ $($prefix)* TokenTree::Group(_) => Err(TokenKind::Group),
+ $($prefix)* TokenTree::Punct(_) => Err(TokenKind::Punct),
+ $($prefix)* TokenTree::Ident(ref ident) if ident.to_string() == "true"
+ => return Ok(Literal::Bool(crate::BoolLit::True)),
+ $($prefix)* TokenTree::Ident(ref ident) if ident.to_string() == "false"
+ => return Ok(Literal::Bool(crate::BoolLit::False)),
+ $($prefix)* TokenTree::Ident(_) => Err(TokenKind::Ident),
+ $($prefix)* TokenTree::Literal(ref lit) => Ok(lit),
+ };
+
+ match res {
+ Ok(lit) => Ok(From::from(lit)),
+ Err(actual) => Err(InvalidToken {
+ actual,
+ expected: TokenKind::Literal,
+ span: span.into(),
+ }),
+ }
+ }
+ }
+ }
+}
+
+helper!(impl_tt_to_lit, );
+
+
+// ==============================================================================================
+// ===== `TryFrom<pm::Literal>`, `TryFrom<pm::TokenTree>` for non-bool `*Lit`
+// ==============================================================================================
+
+fn kind_of(lit: &Literal<String>) -> TokenKind {
+ match lit {
+ Literal::String(_) => TokenKind::StringLit,
+ Literal::Bool(_) => TokenKind::BoolLit,
+ Literal::Integer(_) => TokenKind::IntegerLit,
+ Literal::Float(_) => TokenKind::FloatLit,
+ Literal::Char(_) => TokenKind::CharLit,
+ Literal::Byte(_) => TokenKind::ByteLit,
+ Literal::ByteString(_) => TokenKind::ByteStringLit,
+ }
+}
+
+macro_rules! impl_for_specific_lit {
+ ([$($prefix:tt)*] => $ty:ty, $variant:ident, $kind:ident) => {
+ impl TryFrom<$($prefix)* Literal> for $ty {
+ type Error = InvalidToken;
+ fn try_from(src: $($prefix)* Literal) -> Result<Self, Self::Error> {
+ let span = src.span();
+ let lit: Literal<String> = src.into();
+ match lit {
+ Literal::$variant(s) => Ok(s),
+ other => Err(InvalidToken {
+ expected: TokenKind::$kind,
+ actual: kind_of(&other),
+ span: span.into(),
+ }),
+ }
+ }
+ }
+
+ impl TryFrom<$($prefix)* TokenTree> for $ty {
+ type Error = InvalidToken;
+ fn try_from(tt: $($prefix)* TokenTree) -> Result<Self, Self::Error> {
+ let span = tt.span();
+ let res = match tt {
+ $($prefix)* TokenTree::Group(_) => Err(TokenKind::Group),
+ $($prefix)* TokenTree::Punct(_) => Err(TokenKind::Punct),
+ $($prefix)* TokenTree::Ident(_) => Err(TokenKind::Ident),
+ $($prefix)* TokenTree::Literal(ref lit) => Ok(lit),
+ };
+
+ match res {
+ Ok(lit) => <$ty>::try_from(lit),
+ Err(actual) => Err(InvalidToken {
+ actual,
+ expected: TokenKind::$kind,
+ span: span.into(),
+ }),
+ }
+ }
+ }
+ };
+}
+
+helper!(impl_for_specific_lit, crate::IntegerLit<String>, Integer, IntegerLit);
+helper!(impl_for_specific_lit, crate::FloatLit<String>, Float, FloatLit);
+helper!(impl_for_specific_lit, crate::CharLit<String>, Char, CharLit);
+helper!(impl_for_specific_lit, crate::StringLit<String>, String, StringLit);
+helper!(impl_for_specific_lit, crate::ByteLit<String>, Byte, ByteLit);
+helper!(impl_for_specific_lit, crate::ByteStringLit<String>, ByteString, ByteStringLit);
+
+
+// ==============================================================================================
+// ===== `From<*Lit> for pm::Literal`
+// ==============================================================================================
+
+macro_rules! impl_specific_lit_to_pm_lit {
+ ([$($prefix:tt)*] => $ty:ident, $variant:ident, $kind:ident) => {
+ impl<B: crate::Buffer> From<crate::$ty<B>> for $($prefix)* Literal {
+ fn from(l: crate::$ty<B>) -> Self {
+ // This should never fail: an input that is parsed successfuly
+ // as one of our literal types should always parse as a
+ // proc_macro literal as well!
+ l.raw_input().parse().unwrap_or_else(|e| {
+ panic!(
+ "failed to parse `{}` as `{}`: {}",
+ l.raw_input(),
+ std::any::type_name::<Self>(),
+ e,
+ )
+ })
+ }
+ }
+ };
+}
+
+helper_no_refs!(impl_specific_lit_to_pm_lit, IntegerLit, Integer, IntegerLit);
+helper_no_refs!(impl_specific_lit_to_pm_lit, FloatLit, Float, FloatLit);
+helper_no_refs!(impl_specific_lit_to_pm_lit, CharLit, Char, CharLit);
+helper_no_refs!(impl_specific_lit_to_pm_lit, StringLit, String, StringLit);
+helper_no_refs!(impl_specific_lit_to_pm_lit, ByteLit, Byte, ByteLit);
+helper_no_refs!(impl_specific_lit_to_pm_lit, ByteStringLit, ByteString, ByteStringLit);
+
+
+// ==============================================================================================
+// ===== `TryFrom<pm::TokenTree> for BoolLit`
+// ==============================================================================================
+
+macro_rules! impl_from_tt_for_bool {
+ ([$($prefix:tt)*] => ) => {
+ impl TryFrom<$($prefix)* TokenTree> for crate::BoolLit {
+ type Error = InvalidToken;
+ fn try_from(tt: $($prefix)* TokenTree) -> Result<Self, Self::Error> {
+ let span = tt.span();
+ let actual = match tt {
+ $($prefix)* TokenTree::Ident(ref ident) if ident.to_string() == "true"
+ => return Ok(crate::BoolLit::True),
+ $($prefix)* TokenTree::Ident(ref ident) if ident.to_string() == "false"
+ => return Ok(crate::BoolLit::False),
+
+ $($prefix)* TokenTree::Group(_) => TokenKind::Group,
+ $($prefix)* TokenTree::Punct(_) => TokenKind::Punct,
+ $($prefix)* TokenTree::Ident(_) => TokenKind::Ident,
+ $($prefix)* TokenTree::Literal(ref lit) => kind_of(&Literal::from(lit)),
+ };
+
+ Err(InvalidToken {
+ actual,
+ expected: TokenKind::BoolLit,
+ span: span.into(),
+ })
+ }
+ }
+ };
+}
+
+helper!(impl_from_tt_for_bool, );
+
+// ==============================================================================================
+// ===== `From<BoolLit> for pm::Ident`
+// ==============================================================================================
+
+macro_rules! impl_bool_lit_to_pm_lit {
+ ([$($prefix:tt)*] => ) => {
+ impl From<crate::BoolLit> for $($prefix)* Ident {
+ fn from(l: crate::BoolLit) -> Self {
+ Self::new(l.as_str(), $($prefix)* Span::call_site())
+ }
+ }
+ };
+}
+
+helper_no_refs!(impl_bool_lit_to_pm_lit, );
+
+
+mod tests {
+ //! # Tests
+ //!
+ //! ```no_run
+ //! extern crate proc_macro;
+ //!
+ //! use std::convert::TryFrom;
+ //! use litrs::Literal;
+ //!
+ //! fn give<T>() -> T {
+ //! panic!()
+ //! }
+ //!
+ //! let _ = litrs::Literal::<String>::from(give::<litrs::BoolLit>());
+ //! let _ = litrs::Literal::<String>::from(give::<litrs::IntegerLit<String>>());
+ //! let _ = litrs::Literal::<String>::from(give::<litrs::FloatLit<String>>());
+ //! let _ = litrs::Literal::<String>::from(give::<litrs::CharLit<String>>());
+ //! let _ = litrs::Literal::<String>::from(give::<litrs::StringLit<String>>());
+ //! let _ = litrs::Literal::<String>::from(give::<litrs::ByteLit<String>>());
+ //! let _ = litrs::Literal::<String>::from(give::<litrs::ByteStringLit<String>>());
+ //!
+ //! let _ = litrs::Literal::<&'static str>::from(give::<litrs::BoolLit>());
+ //! let _ = litrs::Literal::<&'static str>::from(give::<litrs::IntegerLit<&'static str>>());
+ //! let _ = litrs::Literal::<&'static str>::from(give::<litrs::FloatLit<&'static str>>());
+ //! let _ = litrs::Literal::<&'static str>::from(give::<litrs::CharLit<&'static str>>());
+ //! let _ = litrs::Literal::<&'static str>::from(give::<litrs::StringLit<&'static str>>());
+ //! let _ = litrs::Literal::<&'static str>::from(give::<litrs::ByteLit<&'static str>>());
+ //! let _ = litrs::Literal::<&'static str>::from(give::<litrs::ByteStringLit<&'static str>>());
+ //!
+ //!
+ //! let _ = litrs::Literal::from(give::<proc_macro::Literal>());
+ //! let _ = litrs::Literal::from(give::<&proc_macro::Literal>());
+ //!
+ //! let _ = litrs::Literal::try_from(give::<proc_macro::TokenTree>());
+ //! let _ = litrs::Literal::try_from(give::<&proc_macro::TokenTree>());
+ //!
+ //!
+ //! let _ = litrs::IntegerLit::try_from(give::<proc_macro::Literal>());
+ //! let _ = litrs::IntegerLit::try_from(give::<&proc_macro::Literal>());
+ //!
+ //! let _ = litrs::FloatLit::try_from(give::<proc_macro::Literal>());
+ //! let _ = litrs::FloatLit::try_from(give::<&proc_macro::Literal>());
+ //!
+ //! let _ = litrs::CharLit::try_from(give::<proc_macro::Literal>());
+ //! let _ = litrs::CharLit::try_from(give::<&proc_macro::Literal>());
+ //!
+ //! let _ = litrs::StringLit::try_from(give::<proc_macro::Literal>());
+ //! let _ = litrs::StringLit::try_from(give::<&proc_macro::Literal>());
+ //!
+ //! let _ = litrs::ByteLit::try_from(give::<proc_macro::Literal>());
+ //! let _ = litrs::ByteLit::try_from(give::<&proc_macro::Literal>());
+ //!
+ //! let _ = litrs::ByteStringLit::try_from(give::<proc_macro::Literal>());
+ //! let _ = litrs::ByteStringLit::try_from(give::<&proc_macro::Literal>());
+ //!
+ //!
+ //! let _ = litrs::BoolLit::try_from(give::<proc_macro::TokenTree>());
+ //! let _ = litrs::BoolLit::try_from(give::<&proc_macro::TokenTree>());
+ //!
+ //! let _ = litrs::IntegerLit::try_from(give::<proc_macro::TokenTree>());
+ //! let _ = litrs::IntegerLit::try_from(give::<&proc_macro::TokenTree>());
+ //!
+ //! let _ = litrs::FloatLit::try_from(give::<proc_macro::TokenTree>());
+ //! let _ = litrs::FloatLit::try_from(give::<&proc_macro::TokenTree>());
+ //!
+ //! let _ = litrs::CharLit::try_from(give::<proc_macro::TokenTree>());
+ //! let _ = litrs::CharLit::try_from(give::<&proc_macro::TokenTree>());
+ //!
+ //! let _ = litrs::StringLit::try_from(give::<proc_macro::TokenTree>());
+ //! let _ = litrs::StringLit::try_from(give::<&proc_macro::TokenTree>());
+ //!
+ //! let _ = litrs::ByteLit::try_from(give::<proc_macro::TokenTree>());
+ //! let _ = litrs::ByteLit::try_from(give::<&proc_macro::TokenTree>());
+ //!
+ //! let _ = litrs::ByteStringLit::try_from(give::<proc_macro::TokenTree>());
+ //! let _ = litrs::ByteStringLit::try_from(give::<&proc_macro::TokenTree>());
+ //! ```
+}
+
+#[cfg(feature = "proc-macro2")]
+mod tests_proc_macro2 {
+ //! # Tests
+ //!
+ //! ```no_run
+ //! extern crate proc_macro;
+ //!
+ //! use std::convert::TryFrom;
+ //! use litrs::Literal;
+ //!
+ //! fn give<T>() -> T {
+ //! panic!()
+ //! }
+ //!
+ //! let _ = litrs::Literal::from(give::<proc_macro2::Literal>());
+ //! let _ = litrs::Literal::from(give::<&proc_macro2::Literal>());
+ //!
+ //! let _ = litrs::Literal::try_from(give::<proc_macro2::TokenTree>());
+ //! let _ = litrs::Literal::try_from(give::<&proc_macro2::TokenTree>());
+ //!
+ //!
+ //! let _ = litrs::IntegerLit::try_from(give::<proc_macro2::Literal>());
+ //! let _ = litrs::IntegerLit::try_from(give::<&proc_macro2::Literal>());
+ //!
+ //! let _ = litrs::FloatLit::try_from(give::<proc_macro2::Literal>());
+ //! let _ = litrs::FloatLit::try_from(give::<&proc_macro2::Literal>());
+ //!
+ //! let _ = litrs::CharLit::try_from(give::<proc_macro2::Literal>());
+ //! let _ = litrs::CharLit::try_from(give::<&proc_macro2::Literal>());
+ //!
+ //! let _ = litrs::StringLit::try_from(give::<proc_macro2::Literal>());
+ //! let _ = litrs::StringLit::try_from(give::<&proc_macro2::Literal>());
+ //!
+ //! let _ = litrs::ByteLit::try_from(give::<proc_macro2::Literal>());
+ //! let _ = litrs::ByteLit::try_from(give::<&proc_macro2::Literal>());
+ //!
+ //! let _ = litrs::ByteStringLit::try_from(give::<proc_macro2::Literal>());
+ //! let _ = litrs::ByteStringLit::try_from(give::<&proc_macro2::Literal>());
+ //!
+ //!
+ //! let _ = litrs::BoolLit::try_from(give::<proc_macro2::TokenTree>());
+ //! let _ = litrs::BoolLit::try_from(give::<&proc_macro2::TokenTree>());
+ //!
+ //! let _ = litrs::IntegerLit::try_from(give::<proc_macro2::TokenTree>());
+ //! let _ = litrs::IntegerLit::try_from(give::<&proc_macro2::TokenTree>());
+ //!
+ //! let _ = litrs::FloatLit::try_from(give::<proc_macro2::TokenTree>());
+ //! let _ = litrs::FloatLit::try_from(give::<&proc_macro2::TokenTree>());
+ //!
+ //! let _ = litrs::CharLit::try_from(give::<proc_macro2::TokenTree>());
+ //! let _ = litrs::CharLit::try_from(give::<&proc_macro2::TokenTree>());
+ //!
+ //! let _ = litrs::StringLit::try_from(give::<proc_macro2::TokenTree>());
+ //! let _ = litrs::StringLit::try_from(give::<&proc_macro2::TokenTree>());
+ //!
+ //! let _ = litrs::ByteLit::try_from(give::<proc_macro2::TokenTree>());
+ //! let _ = litrs::ByteLit::try_from(give::<&proc_macro2::TokenTree>());
+ //!
+ //! let _ = litrs::ByteStringLit::try_from(give::<proc_macro2::TokenTree>());
+ //! let _ = litrs::ByteStringLit::try_from(give::<&proc_macro2::TokenTree>());
+ //! ```
+}
diff --git a/third_party/rust/litrs/src/integer/mod.rs b/third_party/rust/litrs/src/integer/mod.rs
new file mode 100644
index 0000000000..cecd79d3fb
--- /dev/null
+++ b/third_party/rust/litrs/src/integer/mod.rs
@@ -0,0 +1,356 @@
+use std::{fmt, str::FromStr};
+
+use crate::{
+ Buffer, ParseError,
+ err::{perr, ParseErrorKind::*},
+ parse::{first_byte_or_empty, hex_digit_value, check_suffix},
+};
+
+
+/// An integer literal, e.g. `27`, `0x7F`, `0b101010u8` or `5_000_000i64`.
+///
+/// An integer literal consists of an optional base prefix (`0b`, `0o`, `0x`),
+/// the main part (digits and underscores), and an optional type suffix
+/// (e.g. `u64` or `i8`). See [the reference][ref] for more information.
+///
+/// Note that integer literals are always positive: the grammar does not contain
+/// the minus sign at all. The minus sign is just the unary negate operator,
+/// not part of the literal. Which is interesting for cases like `- 128i8`:
+/// here, the literal itself would overflow the specified type (`i8` cannot
+/// represent 128). That's why in rustc, the literal overflow check is
+/// performed as a lint after parsing, not during the lexing stage. Similarly,
+/// [`IntegerLit::parse`] does not perform an overflow check.
+///
+/// [ref]: https://doc.rust-lang.org/reference/tokens.html#integer-literals
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+#[non_exhaustive]
+pub struct IntegerLit<B: Buffer> {
+ /// The raw literal. Grammar: `<prefix?><main part><suffix?>`.
+ raw: B,
+ /// First index of the main number part (after the base prefix).
+ start_main_part: usize,
+ /// First index not part of the main number part.
+ end_main_part: usize,
+ /// Parsed `raw[..start_main_part]`.
+ base: IntegerBase,
+}
+
+impl<B: Buffer> IntegerLit<B> {
+ /// Parses the input as an integer literal. Returns an error if the input is
+ /// invalid or represents a different kind of literal.
+ pub fn parse(input: B) -> Result<Self, ParseError> {
+ match first_byte_or_empty(&input)? {
+ digit @ b'0'..=b'9' => {
+ // TODO: simplify once RFC 2528 is stabilized
+ let IntegerLit {
+ start_main_part,
+ end_main_part,
+ base,
+ ..
+ } = parse_impl(&input, digit)?;
+
+ Ok(Self { raw: input, start_main_part, end_main_part, base })
+ },
+ _ => Err(perr(0, DoesNotStartWithDigit)),
+ }
+ }
+
+ /// Performs the actual string to int conversion to obtain the integer
+ /// value. The optional type suffix of the literal **is ignored by this
+ /// method**. This means `N` does not need to match the type suffix!
+ ///
+ /// Returns `None` if the literal overflows `N`.
+ ///
+ /// Hint: `u128` can represent all possible values integer literal values,
+ /// as there are no negative literals (see type docs). Thus you can, for
+ /// example, safely use `lit.value::<u128>().to_string()` to get a decimal
+ /// string. (Technically, Rust integer literals can represent arbitrarily
+ /// large numbers, but those would be rejected at a later stage by the Rust
+ /// compiler).
+ pub fn value<N: FromIntegerLiteral>(&self) -> Option<N> {
+ let base = N::from_small_number(self.base.value());
+
+ let mut acc = N::from_small_number(0);
+ for digit in self.raw_main_part().bytes() {
+ if digit == b'_' {
+ continue;
+ }
+
+ // We don't actually need the base here: we already know this main
+ // part only contains digits valid for the specified base.
+ let digit = hex_digit_value(digit)
+ .unwrap_or_else(|| unreachable!("bug: integer main part contains non-digit"));
+
+ acc = acc.checked_mul(base)?;
+ acc = acc.checked_add(N::from_small_number(digit))?;
+ }
+
+ Some(acc)
+ }
+
+ /// The base of this integer literal.
+ pub fn base(&self) -> IntegerBase {
+ self.base
+ }
+
+ /// The main part containing the digits and potentially `_`. Do not try to
+ /// parse this directly as that would ignore the base!
+ pub fn raw_main_part(&self) -> &str {
+ &(*self.raw)[self.start_main_part..self.end_main_part]
+ }
+
+ /// The optional suffix. Returns `""` if the suffix is empty/does not exist.
+ ///
+ /// If you want the type, try `IntegerType::from_suffix(lit.suffix())`.
+ pub fn suffix(&self) -> &str {
+ &(*self.raw)[self.end_main_part..]
+ }
+
+ /// Returns the raw input that was passed to `parse`.
+ pub fn raw_input(&self) -> &str {
+ &self.raw
+ }
+
+ /// Returns the raw input that was passed to `parse`, potentially owned.
+ pub fn into_raw_input(self) -> B {
+ self.raw
+ }
+}
+
+impl IntegerLit<&str> {
+ /// Makes a copy of the underlying buffer and returns the owned version of
+ /// `Self`.
+ pub fn to_owned(&self) -> IntegerLit<String> {
+ IntegerLit {
+ raw: self.raw.to_owned(),
+ start_main_part: self.start_main_part,
+ end_main_part: self.end_main_part,
+ base: self.base,
+ }
+ }
+}
+
+impl<B: Buffer> fmt::Display for IntegerLit<B> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "{}", &*self.raw)
+ }
+}
+
+/// Integer literal types. *Implementation detail*.
+///
+/// Implemented for all integer literal types. This trait is sealed and cannot
+/// be implemented outside of this crate. The trait's methods are implementation
+/// detail of this library and are not subject to semver.
+pub trait FromIntegerLiteral: self::sealed::Sealed + Copy {
+ /// Creates itself from the given number. `n` is guaranteed to be `<= 16`.
+ #[doc(hidden)]
+ fn from_small_number(n: u8) -> Self;
+
+ #[doc(hidden)]
+ fn checked_add(self, rhs: Self) -> Option<Self>;
+
+ #[doc(hidden)]
+ fn checked_mul(self, rhs: Self) -> Option<Self>;
+
+ #[doc(hidden)]
+ fn ty() -> IntegerType;
+}
+
+macro_rules! impl_from_int_literal {
+ ($( $ty:ty => $variant:ident ,)* ) => {
+ $(
+ impl self::sealed::Sealed for $ty {}
+ impl FromIntegerLiteral for $ty {
+ fn from_small_number(n: u8) -> Self {
+ n as Self
+ }
+ fn checked_add(self, rhs: Self) -> Option<Self> {
+ self.checked_add(rhs)
+ }
+ fn checked_mul(self, rhs: Self) -> Option<Self> {
+ self.checked_mul(rhs)
+ }
+ fn ty() -> IntegerType {
+ IntegerType::$variant
+ }
+ }
+ )*
+ };
+}
+
+impl_from_int_literal!(
+ u8 => U8, u16 => U16, u32 => U32, u64 => U64, u128 => U128, usize => Usize,
+ i8 => I8, i16 => I16, i32 => I32, i64 => I64, i128 => I128, isize => Isize,
+);
+
+mod sealed {
+ pub trait Sealed {}
+}
+
+/// Precondition: first byte of string has to be in `b'0'..=b'9'`.
+#[inline(never)]
+pub(crate) fn parse_impl(input: &str, first: u8) -> Result<IntegerLit<&str>, ParseError> {
+ // Figure out base and strip prefix base, if it exists.
+ let (end_prefix, base) = match (first, input.as_bytes().get(1)) {
+ (b'0', Some(b'b')) => (2, IntegerBase::Binary),
+ (b'0', Some(b'o')) => (2, IntegerBase::Octal),
+ (b'0', Some(b'x')) => (2, IntegerBase::Hexadecimal),
+
+ // Everything else is treated as decimal. Several cases are caught
+ // by this:
+ // - "123"
+ // - "0"
+ // - "0u8"
+ // - "0r" -> this will error later
+ _ => (0, IntegerBase::Decimal),
+ };
+ let without_prefix = &input[end_prefix..];
+
+
+ // Scan input to find the first character that's not a valid digit.
+ let is_valid_digit = match base {
+ IntegerBase::Binary => |b| matches!(b, b'0' | b'1' | b'_'),
+ IntegerBase::Octal => |b| matches!(b, b'0'..=b'7' | b'_'),
+ IntegerBase::Decimal => |b| matches!(b, b'0'..=b'9' | b'_'),
+ IntegerBase::Hexadecimal => |b| matches!(b, b'0'..=b'9' | b'a'..=b'f' | b'A'..=b'F' | b'_'),
+ };
+ let end_main = without_prefix.bytes()
+ .position(|b| !is_valid_digit(b))
+ .unwrap_or(without_prefix.len());
+ let (main_part, suffix) = without_prefix.split_at(end_main);
+
+ check_suffix(suffix).map_err(|kind| {
+ // This is just to have a nicer error kind for this special case. If the
+ // suffix is invalid, it is non-empty -> unwrap ok.
+ let first = suffix.as_bytes()[0];
+ if !is_valid_digit(first) && first.is_ascii_digit() {
+ perr(end_main + end_prefix, InvalidDigit)
+ } else {
+ perr(end_main + end_prefix..input.len(), kind)
+ }
+ })?;
+ if suffix.starts_with('e') || suffix.starts_with('E') {
+ return Err(perr(end_main, IntegerSuffixStartingWithE));
+ }
+
+ // Make sure main number part is not empty.
+ if main_part.bytes().filter(|&b| b != b'_').count() == 0 {
+ return Err(perr(end_prefix..end_prefix + end_main, NoDigits));
+ }
+
+ Ok(IntegerLit {
+ raw: input,
+ start_main_part: end_prefix,
+ end_main_part: end_main + end_prefix,
+ base,
+ })
+}
+
+
+/// The bases in which an integer can be specified.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum IntegerBase {
+ Binary,
+ Octal,
+ Decimal,
+ Hexadecimal,
+}
+
+impl IntegerBase {
+ /// Returns the literal prefix that indicates this base, i.e. `"0b"`,
+ /// `"0o"`, `""` and `"0x"`.
+ pub fn prefix(self) -> &'static str {
+ match self {
+ Self::Binary => "0b",
+ Self::Octal => "0o",
+ Self::Decimal => "",
+ Self::Hexadecimal => "0x",
+ }
+ }
+
+ /// Returns the base value, i.e. 2, 8, 10 or 16.
+ pub fn value(self) -> u8 {
+ match self {
+ Self::Binary => 2,
+ Self::Octal => 8,
+ Self::Decimal => 10,
+ Self::Hexadecimal => 16,
+ }
+ }
+}
+
+/// All possible integer type suffixes.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+#[non_exhaustive]
+pub enum IntegerType {
+ U8,
+ U16,
+ U32,
+ U64,
+ U128,
+ Usize,
+ I8,
+ I16,
+ I32,
+ I64,
+ I128,
+ Isize,
+}
+
+impl IntegerType {
+ /// Returns the type corresponding to the given suffix (e.g. `"u8"` is
+ /// mapped to `Self::U8`). If the suffix is not a valid integer type,
+ /// `None` is returned.
+ pub fn from_suffix(suffix: &str) -> Option<Self> {
+ match suffix {
+ "u8" => Some(Self::U8),
+ "u16" => Some(Self::U16),
+ "u32" => Some(Self::U32),
+ "u64" => Some(Self::U64),
+ "u128" => Some(Self::U128),
+ "usize" => Some(Self::Usize),
+ "i8" => Some(Self::I8),
+ "i16" => Some(Self::I16),
+ "i32" => Some(Self::I32),
+ "i64" => Some(Self::I64),
+ "i128" => Some(Self::I128),
+ "isize" => Some(Self::Isize),
+ _ => None,
+ }
+ }
+
+ /// Returns the suffix for this type, e.g. `"u8"` for `Self::U8`.
+ pub fn suffix(self) -> &'static str {
+ match self {
+ Self::U8 => "u8",
+ Self::U16 => "u16",
+ Self::U32 => "u32",
+ Self::U64 => "u64",
+ Self::U128 => "u128",
+ Self::Usize => "usize",
+ Self::I8 => "i8",
+ Self::I16 => "i16",
+ Self::I32 => "i32",
+ Self::I64 => "i64",
+ Self::I128 => "i128",
+ Self::Isize => "isize",
+ }
+ }
+}
+
+impl FromStr for IntegerType {
+ type Err = ();
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ Self::from_suffix(s).ok_or(())
+ }
+}
+
+impl fmt::Display for IntegerType {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.suffix().fmt(f)
+ }
+}
+
+
+#[cfg(test)]
+mod tests;
diff --git a/third_party/rust/litrs/src/integer/tests.rs b/third_party/rust/litrs/src/integer/tests.rs
new file mode 100644
index 0000000000..e6dad3f031
--- /dev/null
+++ b/third_party/rust/litrs/src/integer/tests.rs
@@ -0,0 +1,357 @@
+use std::fmt::{Debug, Display};
+use crate::{
+ FromIntegerLiteral, Literal, IntegerLit, IntegerType as Ty, IntegerBase, IntegerBase::*,
+ test_util::{assert_parse_ok_eq, assert_roundtrip},
+};
+
+
+// ===== Utility functions =======================================================================
+
+#[track_caller]
+fn check<T: FromIntegerLiteral + PartialEq + Debug + Display>(
+ input: &str,
+ value: T,
+ base: IntegerBase,
+ main_part: &str,
+ type_suffix: Option<Ty>,
+) {
+ let expected_integer = IntegerLit {
+ raw: input,
+ start_main_part: base.prefix().len(),
+ end_main_part: base.prefix().len() + main_part.len(),
+ base,
+ };
+ assert_parse_ok_eq(
+ input, IntegerLit::parse(input), expected_integer.clone(), "IntegerLit::parse");
+ assert_parse_ok_eq(
+ input, Literal::parse(input), Literal::Integer(expected_integer), "Literal::parse");
+ assert_roundtrip(expected_integer.to_owned(), input);
+ assert_eq!(Ty::from_suffix(IntegerLit::parse(input).unwrap().suffix()), type_suffix);
+
+ let actual_value = IntegerLit::parse(input)
+ .unwrap()
+ .value::<T>()
+ .unwrap_or_else(|| panic!("unexpected overflow in `IntegerLit::value` for `{}`", input));
+ if actual_value != value {
+ panic!(
+ "Parsing int literal `{}` should give value `{}`, but actually resulted in `{}`",
+ input,
+ value,
+ actual_value,
+ );
+ }
+}
+
+
+// ===== Actual tests ===========================================================================
+
+#[test]
+fn parse_decimal() {
+ check("0", 0u128, Decimal, "0", None);
+ check("1", 1u8, Decimal, "1", None);
+ check("8", 8u16, Decimal, "8", None);
+ check("9", 9u32, Decimal, "9", None);
+ check("10", 10u64, Decimal, "10", None);
+ check("11", 11i8, Decimal, "11", None);
+ check("123456789", 123456789i128, Decimal, "123456789", None);
+
+ check("05", 5i16, Decimal, "05", None);
+ check("00005", 5i32, Decimal, "00005", None);
+ check("0123456789", 123456789i64, Decimal, "0123456789", None);
+
+ check("123_456_789", 123_456_789, Decimal, "123_456_789", None);
+ check("0___4", 4, Decimal, "0___4", None);
+ check("0___4_3", 43, Decimal, "0___4_3", None);
+ check("0___4_3", 43, Decimal, "0___4_3", None);
+ check("123___________", 123, Decimal, "123___________", None);
+
+ check(
+ "340282366920938463463374607431768211455",
+ 340282366920938463463374607431768211455u128,
+ Decimal,
+ "340282366920938463463374607431768211455",
+ None,
+ );
+ check(
+ "340_282_366_920_938_463_463_374_607_431_768_211_455",
+ 340282366920938463463374607431768211455u128,
+ Decimal,
+ "340_282_366_920_938_463_463_374_607_431_768_211_455",
+ None,
+ );
+ check(
+ "3_40_282_3669_20938_463463_3746074_31768211_455___",
+ 340282366920938463463374607431768211455u128,
+ Decimal,
+ "3_40_282_3669_20938_463463_3746074_31768211_455___",
+ None,
+ );
+}
+
+#[test]
+fn parse_binary() {
+ check("0b0", 0b0, Binary, "0", None);
+ check("0b000", 0b000, Binary, "000", None);
+ check("0b1", 0b1, Binary, "1", None);
+ check("0b01", 0b01, Binary, "01", None);
+ check("0b101010", 0b101010, Binary, "101010", None);
+ check("0b10_10_10", 0b10_10_10, Binary, "10_10_10", None);
+ check("0b01101110____", 0b01101110____, Binary, "01101110____", None);
+
+ check("0b10010u8", 0b10010u8, Binary, "10010", Some(Ty::U8));
+ check("0b10010i8", 0b10010u8, Binary, "10010", Some(Ty::I8));
+ check("0b10010u64", 0b10010u64, Binary, "10010", Some(Ty::U64));
+ check("0b10010i64", 0b10010i64, Binary, "10010", Some(Ty::I64));
+ check(
+ "0b1011001_00110000_00101000_10100101u32",
+ 0b1011001_00110000_00101000_10100101u32,
+ Binary,
+ "1011001_00110000_00101000_10100101",
+ Some(Ty::U32),
+ );
+}
+
+#[test]
+fn parse_octal() {
+ check("0o0", 0o0, Octal, "0", None);
+ check("0o1", 0o1, Octal, "1", None);
+ check("0o6", 0o6, Octal, "6", None);
+ check("0o7", 0o7, Octal, "7", None);
+ check("0o17", 0o17, Octal, "17", None);
+ check("0o123", 0o123, Octal, "123", None);
+ check("0o7654321", 0o7654321, Octal, "7654321", None);
+ check("0o7_53_1", 0o7_53_1, Octal, "7_53_1", None);
+ check("0o66_", 0o66_, Octal, "66_", None);
+
+ check("0o755u16", 0o755u16, Octal, "755", Some(Ty::U16));
+ check("0o755i128", 0o755i128, Octal, "755", Some(Ty::I128));
+}
+
+#[test]
+fn parse_hexadecimal() {
+ check("0x0", 0x0, Hexadecimal, "0", None);
+ check("0x1", 0x1, Hexadecimal, "1", None);
+ check("0x9", 0x9, Hexadecimal, "9", None);
+
+ check("0xa", 0xa, Hexadecimal, "a", None);
+ check("0xf", 0xf, Hexadecimal, "f", None);
+ check("0x17", 0x17, Hexadecimal, "17", None);
+ check("0x1b", 0x1b, Hexadecimal, "1b", None);
+ check("0x123", 0x123, Hexadecimal, "123", None);
+ check("0xace", 0xace, Hexadecimal, "ace", None);
+ check("0xfdb971", 0xfdb971, Hexadecimal, "fdb971", None);
+ check("0xa_54_f", 0xa_54_f, Hexadecimal, "a_54_f", None);
+ check("0x6d_", 0x6d_, Hexadecimal, "6d_", None);
+
+ check("0xA", 0xA, Hexadecimal, "A", None);
+ check("0xF", 0xF, Hexadecimal, "F", None);
+ check("0x17", 0x17, Hexadecimal, "17", None);
+ check("0x1B", 0x1B, Hexadecimal, "1B", None);
+ check("0x123", 0x123, Hexadecimal, "123", None);
+ check("0xACE", 0xACE, Hexadecimal, "ACE", None);
+ check("0xFDB971", 0xFDB971, Hexadecimal, "FDB971", None);
+ check("0xA_54_F", 0xA_54_F, Hexadecimal, "A_54_F", None);
+ check("0x6D_", 0x6D_, Hexadecimal, "6D_", None);
+
+ check("0xFdB97a1", 0xFdB97a1, Hexadecimal, "FdB97a1", None);
+ check("0xfdB97A1", 0xfdB97A1, Hexadecimal, "fdB97A1", None);
+
+ check("0x40u16", 0x40u16, Hexadecimal, "40", Some(Ty::U16));
+ check("0xffi128", 0xffi128, Hexadecimal, "ff", Some(Ty::I128));
+}
+
+#[test]
+fn starting_underscore() {
+ check("0b_1", 1, Binary, "_1", None);
+ check("0b_010i16", 0b_010, Binary, "_010", Some(Ty::I16));
+
+ check("0o_5", 5, Octal, "_5", None);
+ check("0o_750u128", 0o_750u128, Octal, "_750", Some(Ty::U128));
+
+ check("0x_c", 0xc, Hexadecimal, "_c", None);
+ check("0x_cf3i8", 0x_cf3, Hexadecimal, "_cf3", Some(Ty::I8));
+}
+
+#[test]
+fn parse_overflowing_just_fine() {
+ check("256u8", 256u16, Decimal, "256", Some(Ty::U8));
+ check("123_456_789u8", 123_456_789u32, Decimal, "123_456_789", Some(Ty::U8));
+ check("123_456_789u16", 123_456_789u32, Decimal, "123_456_789", Some(Ty::U16));
+
+ check("123_123_456_789u8", 123_123_456_789u64, Decimal, "123_123_456_789", Some(Ty::U8));
+ check("123_123_456_789u16", 123_123_456_789u64, Decimal, "123_123_456_789", Some(Ty::U16));
+ check("123_123_456_789u32", 123_123_456_789u64, Decimal, "123_123_456_789", Some(Ty::U32));
+}
+
+#[test]
+fn suffixes() {
+ [
+ ("123i8", Ty::I8),
+ ("123i16", Ty::I16),
+ ("123i32", Ty::I32),
+ ("123i64", Ty::I64),
+ ("123i128", Ty::I128),
+ ("123u8", Ty::U8),
+ ("123u16", Ty::U16),
+ ("123u32", Ty::U32),
+ ("123u64", Ty::U64),
+ ("123u128", Ty::U128),
+ ].iter().for_each(|&(s, ty)| {
+ assert_eq!(Ty::from_suffix(IntegerLit::parse(s).unwrap().suffix()), Some(ty));
+ });
+}
+
+#[test]
+fn overflow_u128() {
+ let inputs = [
+ "340282366920938463463374607431768211456",
+ "0x100000000000000000000000000000000",
+ "0o4000000000000000000000000000000000000000000",
+ "0b1000000000000000000000000000000000000000000000000000000000000000000\
+ 00000000000000000000000000000000000000000000000000000000000000",
+ "340282366920938463463374607431768211456u128",
+ "340282366920938463463374607431768211457",
+ "3_40_282_3669_20938_463463_3746074_31768211_456___",
+ "3_40_282_3669_20938_463463_3746074_31768211_455___1",
+ "3_40_282_3669_20938_463463_3746074_31768211_455___0u128",
+ "3402823669209384634633746074317682114570",
+ ];
+
+ for &input in &inputs {
+ let lit = IntegerLit::parse(input).expect("failed to parse");
+ assert!(lit.value::<u128>().is_none());
+ }
+}
+
+#[test]
+fn overflow_u8() {
+ let inputs = [
+ "256", "0x100", "0o400", "0b100000000",
+ "257", "0x101", "0o401", "0b100000001",
+ "300",
+ "1548",
+ "2548985",
+ "256u128",
+ "256u8",
+ "2_5_6",
+ "256_____1",
+ "256__",
+ ];
+
+ for &input in &inputs {
+ let lit = IntegerLit::parse(input).expect("failed to parse");
+ assert!(lit.value::<u8>().is_none());
+ }
+}
+
+#[test]
+fn parse_err() {
+ assert_err!(IntegerLit, "", Empty, None);
+ assert_err_single!(IntegerLit::parse("a"), DoesNotStartWithDigit, 0);
+ assert_err_single!(IntegerLit::parse(";"), DoesNotStartWithDigit, 0);
+ assert_err_single!(IntegerLit::parse("0;"), UnexpectedChar, 1..2);
+ assert_err!(IntegerLit, "0b", NoDigits, 2..2);
+ assert_err_single!(IntegerLit::parse(" 0"), DoesNotStartWithDigit, 0);
+ assert_err_single!(IntegerLit::parse("0 "), UnexpectedChar, 1);
+ assert_err!(IntegerLit, "0b3", InvalidDigit, 2);
+ assert_err_single!(IntegerLit::parse("_"), DoesNotStartWithDigit, 0);
+ assert_err_single!(IntegerLit::parse("_3"), DoesNotStartWithDigit, 0);
+ assert_err!(IntegerLit, "0x44.5", UnexpectedChar, 4..6);
+ assert_err_single!(IntegerLit::parse("123em"), IntegerSuffixStartingWithE, 3);
+}
+
+#[test]
+fn invalid_digits() {
+ assert_err!(IntegerLit, "0b10201", InvalidDigit, 4);
+ assert_err!(IntegerLit, "0b9", InvalidDigit, 2);
+ assert_err!(IntegerLit, "0b07", InvalidDigit, 3);
+
+ assert_err!(IntegerLit, "0o12380", InvalidDigit, 5);
+ assert_err!(IntegerLit, "0o192", InvalidDigit, 3);
+
+ assert_err_single!(IntegerLit::parse("a_123"), DoesNotStartWithDigit, 0);
+ assert_err_single!(IntegerLit::parse("B_123"), DoesNotStartWithDigit, 0);
+}
+
+#[test]
+fn no_valid_digits() {
+ assert_err!(IntegerLit, "0x_", NoDigits, 2..3);
+ assert_err!(IntegerLit, "0x__", NoDigits, 2..4);
+ assert_err!(IntegerLit, "0x________", NoDigits, 2..10);
+ assert_err!(IntegerLit, "0x_i8", NoDigits, 2..3);
+ assert_err!(IntegerLit, "0x_u8", NoDigits, 2..3);
+ assert_err!(IntegerLit, "0x_isize", NoDigits, 2..3);
+ assert_err!(IntegerLit, "0x_usize", NoDigits, 2..3);
+
+ assert_err!(IntegerLit, "0o_", NoDigits, 2..3);
+ assert_err!(IntegerLit, "0o__", NoDigits, 2..4);
+ assert_err!(IntegerLit, "0o________", NoDigits, 2..10);
+ assert_err!(IntegerLit, "0o_i32", NoDigits, 2..3);
+ assert_err!(IntegerLit, "0o_u32", NoDigits, 2..3);
+
+ assert_err!(IntegerLit, "0b_", NoDigits, 2..3);
+ assert_err!(IntegerLit, "0b__", NoDigits, 2..4);
+ assert_err!(IntegerLit, "0b________", NoDigits, 2..10);
+ assert_err!(IntegerLit, "0b_i128", NoDigits, 2..3);
+ assert_err!(IntegerLit, "0b_u128", NoDigits, 2..3);
+}
+
+#[test]
+fn non_standard_suffixes() {
+ #[track_caller]
+ fn check_suffix<T: FromIntegerLiteral + PartialEq + Debug + Display>(
+ input: &str,
+ value: T,
+ base: IntegerBase,
+ main_part: &str,
+ suffix: &str,
+ ) {
+ check(input, value, base, main_part, None);
+ assert_eq!(IntegerLit::parse(input).unwrap().suffix(), suffix);
+ }
+
+ check_suffix("5u7", 5, Decimal, "5", "u7");
+ check_suffix("5u7", 5, Decimal, "5", "u7");
+ check_suffix("5u9", 5, Decimal, "5", "u9");
+ check_suffix("5u0", 5, Decimal, "5", "u0");
+ check_suffix("33u12", 33, Decimal, "33", "u12");
+ check_suffix("84u17", 84, Decimal, "84", "u17");
+ check_suffix("99u80", 99, Decimal, "99", "u80");
+ check_suffix("1234uu16", 1234, Decimal, "1234", "uu16");
+
+ check_suffix("5i7", 5, Decimal, "5", "i7");
+ check_suffix("5i9", 5, Decimal, "5", "i9");
+ check_suffix("5i0", 5, Decimal, "5", "i0");
+ check_suffix("33i12", 33, Decimal, "33", "i12");
+ check_suffix("84i17", 84, Decimal, "84", "i17");
+ check_suffix("99i80", 99, Decimal, "99", "i80");
+ check_suffix("1234ii16", 1234, Decimal, "1234", "ii16");
+
+ check_suffix("0ui32", 0, Decimal, "0", "ui32");
+ check_suffix("1iu32", 1, Decimal, "1", "iu32");
+ check_suffix("54321a64", 54321, Decimal, "54321", "a64");
+ check_suffix("54321b64", 54321, Decimal, "54321", "b64");
+ check_suffix("54321x64", 54321, Decimal, "54321", "x64");
+ check_suffix("54321o64", 54321, Decimal, "54321", "o64");
+
+ check_suffix("0a", 0, Decimal, "0", "a");
+ check_suffix("0a3", 0, Decimal, "0", "a3");
+ check_suffix("0z", 0, Decimal, "0", "z");
+ check_suffix("0z3", 0, Decimal, "0", "z3");
+ check_suffix("0b0a", 0, Binary, "0", "a");
+ check_suffix("0b0A", 0, Binary, "0", "A");
+ check_suffix("0b01f", 1, Binary, "01", "f");
+ check_suffix("0b01F", 1, Binary, "01", "F");
+ check_suffix("0o7a_", 7, Octal, "7", "a_");
+ check_suffix("0o7A_", 7, Octal, "7", "A_");
+ check_suffix("0o72f_0", 0o72, Octal, "72", "f_0");
+ check_suffix("0o72F_0", 0o72, Octal, "72", "F_0");
+
+ check_suffix("0x8cg", 0x8c, Hexadecimal, "8c", "g");
+ check_suffix("0x8cG", 0x8c, Hexadecimal, "8c", "G");
+ check_suffix("0x8c1h_", 0x8c1, Hexadecimal, "8c1", "h_");
+ check_suffix("0x8c1H_", 0x8c1, Hexadecimal, "8c1", "H_");
+ check_suffix("0x8czu16", 0x8c, Hexadecimal, "8c", "zu16");
+
+ check_suffix("123_foo", 123, Decimal, "123_", "foo");
+}
diff --git a/third_party/rust/litrs/src/lib.rs b/third_party/rust/litrs/src/lib.rs
new file mode 100644
index 0000000000..64ed7813c9
--- /dev/null
+++ b/third_party/rust/litrs/src/lib.rs
@@ -0,0 +1,370 @@
+//! Parsing and inspecting Rust literal tokens.
+//!
+//! This library offers functionality to parse Rust literals, i.e. tokens in the
+//! Rust programming language that represent fixed values. The grammar for
+//! those is defined [here][ref].
+//!
+//! This kind of functionality already exists in the crate `syn`. However, as
+//! you oftentimes don't need (nor want) the full power of `syn`, `litrs` was
+//! built. This crate also offers a bit more flexibility compared to `syn`
+//! (only regarding literals, of course).
+//!
+//!
+//! # Quick start
+//!
+//! | **`StringLit::try_from(tt)?.value()`** |
+//! | - |
+//!
+//! ... where `tt` is a `proc_macro::TokenTree` and where [`StringLit`] can be
+//! replaced with [`Literal`] or other types of literals (e.g. [`FloatLit`]).
+//! Calling `value()` returns the value that is represented by the literal.
+//!
+//! **Mini Example**
+//!
+//! ```ignore
+//! use proc_macro::TokenStream;
+//!
+//! #[proc_macro]
+//! pub fn foo(input: TokenStream) -> TokenStream {
+//! let first_token = input.into_iter().next().unwrap(); // Do proper error handling!
+//! let string_value = match litrs::StringLit::try_from(first_token) {
+//! Ok(string_lit) => string_lit.value(),
+//! Err(e) => return e.to_compile_error(),
+//! };
+//!
+//! // `string_value` is the string value with all escapes resolved.
+//! todo!()
+//! }
+//! ```
+//!
+//! # Overview
+//!
+//! The main types of this library are [`Literal`], representing any kind of
+//! literal, and `*Lit`, like [`StringLit`] or [`FloatLit`], representing a
+//! specific kind of literal.
+//!
+//! There are different ways to obtain such a literal type:
+//!
+//! - **`parse`**: parses a `&str` or `String` and returns `Result<_,
+//! ParseError>`. For example: [`Literal::parse`] and
+//! [`IntegerLit::parse`].
+//!
+//! - **`From<proc_macro::Literal> for Literal`**: turns a `Literal` value from
+//! the `proc_macro` crate into a `Literal` from this crate.
+//!
+//! - **`TryFrom<proc_macro::Literal> for *Lit`**: tries to turn a
+//! `proc_macro::Literal` into a specific literal type of this crate. If
+//! the input is a literal of a different kind, `Err(InvalidToken)` is
+//! returned.
+//!
+//! - **`TryFrom<proc_macro::TokenTree>`**: attempts to turn a token tree into a
+//! literal type of this crate. An error is returned if the token tree is
+//! not a literal, or if you are trying to turn it into a specific kind of
+//! literal and the token tree is a different kind of literal.
+//!
+//! All of the `From` and `TryFrom` conversions also work for reference to
+//! `proc_macro` types. Additionally, if the crate feature `proc-macro2` is
+//! enabled (which it is by default), all these `From` and `TryFrom` impls also
+//! exist for the corresponding `proc_macro2` types.
+//!
+//! **Note**: `true` and `false` are `Ident`s when passed to your proc macro.
+//! The `TryFrom<TokenTree>` impls check for those two special idents and
+//! return a [`BoolLit`] appropriately. For that reason, there is also no
+//! `TryFrom<proc_macro::Literal>` impl for [`BoolLit`]. The `proc_macro::Literal`
+//! simply cannot represent bool literals.
+//!
+//!
+//! # Examples
+//!
+//! In a proc-macro:
+//!
+//! ```ignore
+//! use std::convert::TryFrom;
+//! use proc_macro::TokenStream;
+//! use litrs::FloatLit;
+//!
+//! #[proc_macro]
+//! pub fn foo(input: TokenStream) -> TokenStream {
+//! let mut input = input.into_iter().collect::<Vec<_>>();
+//! if input.len() != 1 {
+//! // Please do proper error handling in your real code!
+//! panic!("expected exactly one token as input");
+//! }
+//! let token = input.remove(0);
+//!
+//! match FloatLit::try_from(token) {
+//! Ok(float_lit) => { /* do something */ }
+//! Err(e) => return e.to_compile_error(),
+//! }
+//!
+//! // Dummy output
+//! TokenStream::new()
+//! }
+//! ```
+//!
+//! Parsing from string:
+//!
+//! ```
+//! use litrs::{FloatLit, Literal};
+//!
+//! // Parse a specific kind of literal (float in this case):
+//! let float_lit = FloatLit::parse("3.14f32");
+//! assert!(float_lit.is_ok());
+//! assert_eq!(float_lit.unwrap().suffix(), "f32");
+//! assert!(FloatLit::parse("'c'").is_err());
+//!
+//! // Parse any kind of literal. After parsing, you can inspect the literal
+//! // and decide what to do in each case.
+//! let lit = Literal::parse("0xff80").expect("failed to parse literal");
+//! match lit {
+//! Literal::Integer(lit) => { /* ... */ }
+//! Literal::Float(lit) => { /* ... */ }
+//! Literal::Bool(lit) => { /* ... */ }
+//! Literal::Char(lit) => { /* ... */ }
+//! Literal::String(lit) => { /* ... */ }
+//! Literal::Byte(lit) => { /* ... */ }
+//! Literal::ByteString(lit) => { /* ... */ }
+//! }
+//! ```
+//!
+//!
+//!
+//! # Crate features
+//!
+//! - `proc-macro2` (**default**): adds the dependency `proc_macro2`, a bunch of
+//! `From` and `TryFrom` impls, and [`InvalidToken::to_compile_error2`].
+//! - `check_suffix`: if enabled, `parse` functions will exactly verify that the
+//! literal suffix is valid. Adds the dependency `unicode-xid`. If disabled,
+//! only an approximate check (only in ASCII range) is done. If you are
+//! writing a proc macro, you don't need to enable this as the suffix is
+//! already checked by the compiler.
+//!
+//!
+//! [ref]: https://doc.rust-lang.org/reference/tokens.html#literals
+//!
+
+#![deny(missing_debug_implementations)]
+
+extern crate proc_macro;
+
+#[cfg(test)]
+#[macro_use]
+mod test_util;
+
+#[cfg(test)]
+mod tests;
+
+mod bool;
+mod byte;
+mod bytestr;
+mod char;
+mod err;
+mod escape;
+mod float;
+mod impls;
+mod integer;
+mod parse;
+mod string;
+
+
+use std::{borrow::{Borrow, Cow}, fmt, ops::{Deref, Range}};
+
+pub use self::{
+ bool::BoolLit,
+ byte::ByteLit,
+ bytestr::ByteStringLit,
+ char::CharLit,
+ err::{InvalidToken, ParseError},
+ float::{FloatLit, FloatType},
+ integer::{FromIntegerLiteral, IntegerLit, IntegerBase, IntegerType},
+ string::StringLit,
+};
+
+
+// ==============================================================================================
+// ===== `Literal` and type defs
+// ==============================================================================================
+
+/// A literal. This is the main type of this library.
+///
+/// This type is generic over the underlying buffer `B`, which can be `&str` or
+/// `String`.
+///
+/// To create this type, you have to either call [`Literal::parse`] with an
+/// input string or use the `From<_>` impls of this type. The impls are only
+/// available of the corresponding crate features are enabled (they are enabled
+/// by default).
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum Literal<B: Buffer> {
+ Bool(BoolLit),
+ Integer(IntegerLit<B>),
+ Float(FloatLit<B>),
+ Char(CharLit<B>),
+ String(StringLit<B>),
+ Byte(ByteLit<B>),
+ ByteString(ByteStringLit<B>),
+}
+
+impl<B: Buffer> Literal<B> {
+ /// Parses the given input as a Rust literal.
+ pub fn parse(input: B) -> Result<Self, ParseError> {
+ parse::parse(input)
+ }
+
+ /// Returns the suffix of this literal or `""` if it doesn't have one.
+ ///
+ /// Rust token grammar actually allows suffixes for all kinds of tokens.
+ /// Most Rust programmer only know the type suffixes for integer and
+ /// floats, e.g. `0u32`. And in normal Rust code, everything else causes an
+ /// error. But it is possible to pass literals with arbitrary suffixes to
+ /// proc macros, for example:
+ ///
+ /// ```ignore
+ /// some_macro!(3.14f33 16px '🦊'good_boy "toph"beifong);
+ /// ```
+ ///
+ /// Boolean literals, not actually being literals, but idents, cannot have
+ /// suffixes and this method always returns `""` for those.
+ ///
+ /// There are some edge cases to be aware of:
+ /// - Integer suffixes must not start with `e` or `E` as that conflicts with
+ /// the exponent grammar for floats. `0e1` is a float; `0eel` is also
+ /// parsed as a float and results in an error.
+ /// - Hexadecimal integers eagerly parse digits, so `0x5abcdefgh` has a
+ /// suffix von `gh`.
+ /// - Suffixes can contain and start with `_`, but for integer and number
+ /// literals, `_` is eagerly parsed as part of the number, so `1_x` has
+ /// the suffix `x`.
+ /// - The input `55f32` is regarded as integer literal with suffix `f32`.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use litrs::Literal;
+ ///
+ /// assert_eq!(Literal::parse(r##"3.14f33"##).unwrap().suffix(), "f33");
+ /// assert_eq!(Literal::parse(r##"123hackerman"##).unwrap().suffix(), "hackerman");
+ /// assert_eq!(Literal::parse(r##"0x0fuck"##).unwrap().suffix(), "uck");
+ /// assert_eq!(Literal::parse(r##"'🦊'good_boy"##).unwrap().suffix(), "good_boy");
+ /// assert_eq!(Literal::parse(r##""toph"beifong"##).unwrap().suffix(), "beifong");
+ /// ```
+ pub fn suffix(&self) -> &str {
+ match self {
+ Literal::Bool(_) => "",
+ Literal::Integer(l) => l.suffix(),
+ Literal::Float(l) => l.suffix(),
+ Literal::Char(l) => l.suffix(),
+ Literal::String(l) => l.suffix(),
+ Literal::Byte(l) => l.suffix(),
+ Literal::ByteString(l) => l.suffix(),
+ }
+ }
+}
+
+impl Literal<&str> {
+ /// Makes a copy of the underlying buffer and returns the owned version of
+ /// `Self`.
+ pub fn into_owned(self) -> Literal<String> {
+ match self {
+ Literal::Bool(l) => Literal::Bool(l.to_owned()),
+ Literal::Integer(l) => Literal::Integer(l.to_owned()),
+ Literal::Float(l) => Literal::Float(l.to_owned()),
+ Literal::Char(l) => Literal::Char(l.to_owned()),
+ Literal::String(l) => Literal::String(l.into_owned()),
+ Literal::Byte(l) => Literal::Byte(l.to_owned()),
+ Literal::ByteString(l) => Literal::ByteString(l.into_owned()),
+ }
+ }
+}
+
+impl<B: Buffer> fmt::Display for Literal<B> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ Literal::Bool(l) => l.fmt(f),
+ Literal::Integer(l) => l.fmt(f),
+ Literal::Float(l) => l.fmt(f),
+ Literal::Char(l) => l.fmt(f),
+ Literal::String(l) => l.fmt(f),
+ Literal::Byte(l) => l.fmt(f),
+ Literal::ByteString(l) => l.fmt(f),
+ }
+ }
+}
+
+
+// ==============================================================================================
+// ===== Buffer
+// ==============================================================================================
+
+/// A shared or owned string buffer. Implemented for `String` and `&str`. *Implementation detail*.
+///
+/// This is trait is implementation detail of this library, cannot be
+/// implemented in other crates and is not subject to semantic versioning.
+/// `litrs` only guarantees that this trait is implemented for `String` and
+/// `for<'a> &'a str`.
+pub trait Buffer: sealed::Sealed + Deref<Target = str> {
+ /// This is `Cow<'static, str>` for `String`, and `Cow<'a, str>` for `&'a str`.
+ type Cow: From<String> + AsRef<str> + Borrow<str> + Deref<Target = str>;
+
+ #[doc(hidden)]
+ fn into_cow(self) -> Self::Cow;
+
+ /// This is `Cow<'static, [u8]>` for `String`, and `Cow<'a, [u8]>` for `&'a str`.
+ type ByteCow: From<Vec<u8>> + AsRef<[u8]> + Borrow<[u8]> + Deref<Target = [u8]>;
+
+ #[doc(hidden)]
+ fn into_byte_cow(self) -> Self::ByteCow;
+
+ /// Cuts away some characters at the beginning and some at the end. Given
+ /// range has to be in bounds.
+ #[doc(hidden)]
+ fn cut(self, range: Range<usize>) -> Self;
+}
+
+mod sealed {
+ pub trait Sealed {}
+}
+
+impl<'a> sealed::Sealed for &'a str {}
+impl<'a> Buffer for &'a str {
+ #[doc(hidden)]
+ fn cut(self, range: Range<usize>) -> Self {
+ &self[range]
+ }
+
+ type Cow = Cow<'a, str>;
+ #[doc(hidden)]
+ fn into_cow(self) -> Self::Cow {
+ self.into()
+ }
+ type ByteCow = Cow<'a, [u8]>;
+ #[doc(hidden)]
+ fn into_byte_cow(self) -> Self::ByteCow {
+ self.as_bytes().into()
+ }
+}
+
+impl sealed::Sealed for String {}
+impl Buffer for String {
+ #[doc(hidden)]
+ fn cut(mut self, range: Range<usize>) -> Self {
+ // This is not the most efficient way, but it works. First we cut the
+ // end, then the beginning. Note that `drain` also removes the range if
+ // the iterator is not consumed.
+ self.truncate(range.end);
+ self.drain(..range.start);
+ self
+ }
+
+ type Cow = Cow<'static, str>;
+ #[doc(hidden)]
+ fn into_cow(self) -> Self::Cow {
+ self.into()
+ }
+
+ type ByteCow = Cow<'static, [u8]>;
+ #[doc(hidden)]
+ fn into_byte_cow(self) -> Self::ByteCow {
+ self.into_bytes().into()
+ }
+}
diff --git a/third_party/rust/litrs/src/parse.rs b/third_party/rust/litrs/src/parse.rs
new file mode 100644
index 0000000000..efc6b870f6
--- /dev/null
+++ b/third_party/rust/litrs/src/parse.rs
@@ -0,0 +1,125 @@
+use crate::{
+ BoolLit,
+ Buffer,
+ ByteLit,
+ ByteStringLit,
+ CharLit,
+ ParseError,
+ FloatLit,
+ IntegerLit,
+ Literal,
+ StringLit,
+ err::{perr, ParseErrorKind::{*, self}},
+};
+
+
+pub fn parse<B: Buffer>(input: B) -> Result<Literal<B>, ParseError> {
+ let (first, rest) = input.as_bytes().split_first().ok_or(perr(None, Empty))?;
+ let second = input.as_bytes().get(1).copied();
+
+ match first {
+ b'f' if &*input == "false" => Ok(Literal::Bool(BoolLit::False)),
+ b't' if &*input == "true" => Ok(Literal::Bool(BoolLit::True)),
+
+ // A number literal (integer or float).
+ b'0'..=b'9' => {
+ // To figure out whether this is a float or integer, we do some
+ // quick inspection here. Yes, this is technically duplicate
+ // work with what is happening in the integer/float parse
+ // methods, but it makes the code way easier for now and won't
+ // be a huge performance loss.
+ //
+ // The first non-decimal char in a float literal must
+ // be '.', 'e' or 'E'.
+ match input.as_bytes().get(1 + end_dec_digits(rest)) {
+ Some(b'.') | Some(b'e') | Some(b'E')
+ => FloatLit::parse(input).map(Literal::Float),
+
+ _ => IntegerLit::parse(input).map(Literal::Integer),
+ }
+ },
+
+ b'\'' => CharLit::parse(input).map(Literal::Char),
+ b'"' | b'r' => StringLit::parse(input).map(Literal::String),
+
+ b'b' if second == Some(b'\'') => ByteLit::parse(input).map(Literal::Byte),
+ b'b' if second == Some(b'r') || second == Some(b'"')
+ => ByteStringLit::parse(input).map(Literal::ByteString),
+
+ _ => Err(perr(None, InvalidLiteral)),
+ }
+}
+
+
+pub(crate) fn first_byte_or_empty(s: &str) -> Result<u8, ParseError> {
+ s.as_bytes().get(0).copied().ok_or(perr(None, Empty))
+}
+
+/// Returns the index of the first non-underscore, non-decimal digit in `input`,
+/// or the `input.len()` if all characters are decimal digits.
+pub(crate) fn end_dec_digits(input: &[u8]) -> usize {
+ input.iter()
+ .position(|b| !matches!(b, b'_' | b'0'..=b'9'))
+ .unwrap_or(input.len())
+}
+
+pub(crate) fn hex_digit_value(digit: u8) -> Option<u8> {
+ match digit {
+ b'0'..=b'9' => Some(digit - b'0'),
+ b'a'..=b'f' => Some(digit - b'a' + 10),
+ b'A'..=b'F' => Some(digit - b'A' + 10),
+ _ => None,
+ }
+}
+
+/// Makes sure that `s` is a valid literal suffix.
+pub(crate) fn check_suffix(s: &str) -> Result<(), ParseErrorKind> {
+ if s.is_empty() {
+ return Ok(());
+ }
+
+ let mut chars = s.chars();
+ let first = chars.next().unwrap();
+ let rest = chars.as_str();
+ if first == '_' && rest.is_empty() {
+ return Err(InvalidSuffix);
+ }
+
+ // This is just an extra check to improve the error message. If the first
+ // character of the "suffix" is already some invalid ASCII
+ // char, "unexpected character" seems like the more fitting error.
+ if first.is_ascii() && !(first.is_ascii_alphabetic() || first == '_') {
+ return Err(UnexpectedChar);
+ }
+
+ // Proper check is optional as it's not really necessary in proc macro
+ // context.
+ #[cfg(feature = "check_suffix")]
+ fn is_valid_suffix(first: char, rest: &str) -> bool {
+ use unicode_xid::UnicodeXID;
+
+ (first == '_' || first.is_xid_start())
+ && rest.chars().all(|c| c.is_xid_continue())
+ }
+
+ // When avoiding the dependency on `unicode_xid`, we just do a best effort
+ // to catch the most common errors.
+ #[cfg(not(feature = "check_suffix"))]
+ fn is_valid_suffix(first: char, rest: &str) -> bool {
+ if first.is_ascii() && !(first.is_ascii_alphabetic() || first == '_') {
+ return false;
+ }
+ for c in rest.chars() {
+ if c.is_ascii() && !(c.is_ascii_alphanumeric() || c == '_') {
+ return false;
+ }
+ }
+ true
+ }
+
+ if is_valid_suffix(first, rest) {
+ Ok(())
+ } else {
+ Err(InvalidSuffix)
+ }
+}
diff --git a/third_party/rust/litrs/src/string/mod.rs b/third_party/rust/litrs/src/string/mod.rs
new file mode 100644
index 0000000000..d2034a62a9
--- /dev/null
+++ b/third_party/rust/litrs/src/string/mod.rs
@@ -0,0 +1,125 @@
+use std::{fmt, ops::Range};
+
+use crate::{
+ Buffer, ParseError,
+ err::{perr, ParseErrorKind::*},
+ escape::{scan_raw_string, unescape_string},
+ parse::first_byte_or_empty,
+};
+
+
+/// A string or raw string literal, e.g. `"foo"`, `"Grüße"` or `r#"a🦊c"d🦀f"#`.
+///
+/// See [the reference][ref] for more information.
+///
+/// [ref]: https://doc.rust-lang.org/reference/tokens.html#string-literals
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct StringLit<B: Buffer> {
+ /// The raw input.
+ raw: B,
+
+ /// The string value (with all escapes unescaped), or `None` if there were
+ /// no escapes. In the latter case, the string value is in `raw`.
+ value: Option<String>,
+
+ /// The number of hash signs in case of a raw string literal, or `None` if
+ /// it's not a raw string literal.
+ num_hashes: Option<u32>,
+
+ /// Start index of the suffix or `raw.len()` if there is no suffix.
+ start_suffix: usize,
+}
+
+impl<B: Buffer> StringLit<B> {
+ /// Parses the input as a (raw) string literal. Returns an error if the
+ /// input is invalid or represents a different kind of literal.
+ pub fn parse(input: B) -> Result<Self, ParseError> {
+ match first_byte_or_empty(&input)? {
+ b'r' | b'"' => {
+ let (value, num_hashes, start_suffix) = parse_impl(&input)?;
+ Ok(Self { raw: input, value, num_hashes, start_suffix })
+ }
+ _ => Err(perr(0, InvalidStringLiteralStart)),
+ }
+ }
+
+ /// Returns the string value this literal represents (where all escapes have
+ /// been turned into their respective values).
+ pub fn value(&self) -> &str {
+ self.value.as_deref().unwrap_or(&self.raw[self.inner_range()])
+ }
+
+ /// Like `value` but returns a potentially owned version of the value.
+ ///
+ /// The return value is either `Cow<'static, str>` if `B = String`, or
+ /// `Cow<'a, str>` if `B = &'a str`.
+ pub fn into_value(self) -> B::Cow {
+ let inner_range = self.inner_range();
+ let Self { raw, value, .. } = self;
+ value.map(B::Cow::from).unwrap_or_else(|| raw.cut(inner_range).into_cow())
+ }
+
+ /// The optional suffix. Returns `""` if the suffix is empty/does not exist.
+ pub fn suffix(&self) -> &str {
+ &(*self.raw)[self.start_suffix..]
+ }
+
+ /// Returns whether this literal is a raw string literal (starting with
+ /// `r`).
+ pub fn is_raw_string(&self) -> bool {
+ self.num_hashes.is_some()
+ }
+
+ /// Returns the raw input that was passed to `parse`.
+ pub fn raw_input(&self) -> &str {
+ &self.raw
+ }
+
+ /// Returns the raw input that was passed to `parse`, potentially owned.
+ pub fn into_raw_input(self) -> B {
+ self.raw
+ }
+
+ /// The range within `self.raw` that excludes the quotes and potential `r#`.
+ fn inner_range(&self) -> Range<usize> {
+ match self.num_hashes {
+ None => 1..self.start_suffix - 1,
+ Some(n) => 1 + n as usize + 1..self.start_suffix - n as usize - 1,
+ }
+ }
+}
+
+impl StringLit<&str> {
+ /// Makes a copy of the underlying buffer and returns the owned version of
+ /// `Self`.
+ pub fn into_owned(self) -> StringLit<String> {
+ StringLit {
+ raw: self.raw.to_owned(),
+ value: self.value,
+ num_hashes: self.num_hashes,
+ start_suffix: self.start_suffix,
+ }
+ }
+}
+
+impl<B: Buffer> fmt::Display for StringLit<B> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.pad(&self.raw)
+ }
+}
+
+/// Precondition: input has to start with either `"` or `r`.
+#[inline(never)]
+pub(crate) fn parse_impl(input: &str) -> Result<(Option<String>, Option<u32>, usize), ParseError> {
+ if input.starts_with('r') {
+ scan_raw_string::<char>(&input, 1)
+ .map(|(v, hashes, start_suffix)| (v, Some(hashes), start_suffix))
+ } else {
+ unescape_string::<char>(&input, 1)
+ .map(|(v, start_suffix)| (v, None, start_suffix))
+ }
+}
+
+
+#[cfg(test)]
+mod tests;
diff --git a/third_party/rust/litrs/src/string/tests.rs b/third_party/rust/litrs/src/string/tests.rs
new file mode 100644
index 0000000000..1c0cb63061
--- /dev/null
+++ b/third_party/rust/litrs/src/string/tests.rs
@@ -0,0 +1,278 @@
+use crate::{Literal, StringLit, test_util::{assert_parse_ok_eq, assert_roundtrip}};
+
+// ===== Utility functions =======================================================================
+
+macro_rules! check {
+ ($lit:literal, $has_escapes:expr, $num_hashes:expr) => {
+ check!($lit, stringify!($lit), $has_escapes, $num_hashes, "")
+ };
+ ($lit:literal, $input:expr, $has_escapes:expr, $num_hashes:expr, $suffix:literal) => {
+ let input = $input;
+ let expected = StringLit {
+ raw: input,
+ value: if $has_escapes { Some($lit.to_string()) } else { None },
+ num_hashes: $num_hashes,
+ start_suffix: input.len() - $suffix.len(),
+ };
+
+ assert_parse_ok_eq(input, StringLit::parse(input), expected.clone(), "StringLit::parse");
+ assert_parse_ok_eq(
+ input, Literal::parse(input), Literal::String(expected.clone()), "Literal::parse");
+ let lit = StringLit::parse(input).unwrap();
+ assert_eq!(lit.value(), $lit);
+ assert_eq!(lit.suffix(), $suffix);
+ assert_eq!(lit.into_value(), $lit);
+ assert_roundtrip(expected.into_owned(), input);
+ };
+}
+
+
+// ===== Actual tests ============================================================================
+
+#[test]
+fn simple() {
+ check!("", false, None);
+ check!("a", false, None);
+ check!("peter", false, None);
+ check!("Sei gegrüßt, Bärthelt!", false, None);
+ check!("أنا لا أتحدث العربية", false, None);
+ check!("お前はもう死んでいる", false, None);
+ check!("Пушки - интересные музыкальные инструменты", false, None);
+ check!("lit 👌 😂 af", false, None);
+}
+
+#[test]
+fn special_whitespace() {
+ let strings = ["\n", "\t", "foo\tbar", "🦊\n"];
+
+ for &s in &strings {
+ let input = format!(r#""{}""#, s);
+ let input_raw = format!(r#"r"{}""#, s);
+ for (input, num_hashes) in vec![(input, None), (input_raw, Some(0))] {
+ let expected = StringLit {
+ raw: &*input,
+ value: None,
+ num_hashes,
+ start_suffix: input.len(),
+ };
+ assert_parse_ok_eq(
+ &input, StringLit::parse(&*input), expected.clone(), "StringLit::parse");
+ assert_parse_ok_eq(
+ &input, Literal::parse(&*input), Literal::String(expected), "Literal::parse");
+ assert_eq!(StringLit::parse(&*input).unwrap().value(), s);
+ assert_eq!(StringLit::parse(&*input).unwrap().into_value(), s);
+ }
+ }
+}
+
+#[test]
+fn simple_escapes() {
+ check!("a\nb", true, None);
+ check!("\nb", true, None);
+ check!("a\n", true, None);
+ check!("\n", true, None);
+
+ check!("\x60犬 \t 猫\r馬\n うさぎ \0ネズミ", true, None);
+ check!("నా \\పిల్లి లావుగా ఉంది", true, None);
+ check!("నా \\పిల్లి లావుగా 🐈\"ఉంది", true, None);
+ check!("\\నా\\ పిల్లి లావుగా\" ఉంది\"", true, None);
+ check!("\"నా \\🐈 పిల్లి లావుగా \" ఉంది\\", true, None);
+
+ check!("\x00", true, None);
+ check!(" \x01", true, None);
+ check!("\x0c 🦊", true, None);
+ check!(" 🦊\x0D ", true, None);
+ check!("\\x13", true, None);
+ check!("\"x30", true, None);
+}
+
+#[test]
+fn unicode_escapes() {
+ check!("\u{0}", true, None);
+ check!(" \u{00}", true, None);
+ check!("\u{b} ", true, None);
+ check!(" \u{B} ", true, None);
+ check!("\u{7e}", true, None);
+ check!("నక్క\u{E4}", true, None);
+ check!("\u{e4} నక్క", true, None);
+ check!(" \u{fc}నక్క ", true, None);
+ check!("\u{Fc}", true, None);
+ check!("\u{fC}🦊\nлиса", true, None);
+ check!("лиса\u{FC}", true, None);
+ check!("лиса\u{b10}నక్క🦊", true, None);
+ check!("\"నక్క\u{B10}", true, None);
+ check!("лиса\\\u{0b10}", true, None);
+ check!("ли🦊са\\\"\u{0b10}", true, None);
+ check!("నక్క\\\\u{0b10}", true, None);
+ check!("\u{2764}Füchsin", true, None);
+ check!("Füchse \u{1f602}", true, None);
+ check!("cd\u{1F602}ab", true, None);
+
+ check!("\u{0}🦊", true, None);
+ check!("лиса\u{0__}", true, None);
+ check!("\\🦊\u{3_b}", true, None);
+ check!("🦊\u{1_F_6_0_2}Füchsin", true, None);
+ check!("నక్క\\\u{1_F6_02_____}నక్క", true, None);
+}
+
+#[test]
+fn string_continue() {
+ check!("నక్క\
+ bar", true, None);
+ check!("foo\
+🦊", true, None);
+
+ check!("foo\
+
+ banana", true, None);
+
+ // Weird whitespace characters
+ let lit = StringLit::parse("\"foo\\\n\r\t\n \n\tbar\"").expect("failed to parse");
+ assert_eq!(lit.value(), "foobar");
+ let lit = StringLit::parse("\"foo\\\n\u{85}bar\"").expect("failed to parse");
+ assert_eq!(lit.value(), "foo\u{85}bar");
+ let lit = StringLit::parse("\"foo\\\n\u{a0}bar\"").expect("failed to parse");
+ assert_eq!(lit.value(), "foo\u{a0}bar");
+
+ // Raw strings do not handle "string continues"
+ check!(r"foo\
+ bar", false, Some(0));
+}
+
+#[test]
+fn crlf_newlines() {
+ let lit = StringLit::parse("\"foo\r\nbar\"").expect("failed to parse");
+ assert_eq!(lit.value(), "foo\nbar");
+
+ let lit = StringLit::parse("\"\r\nbar\"").expect("failed to parse");
+ assert_eq!(lit.value(), "\nbar");
+
+ let lit = StringLit::parse("\"лиса\r\n\"").expect("failed to parse");
+ assert_eq!(lit.value(), "лиса\n");
+
+ let lit = StringLit::parse("r\"foo\r\nbar\"").expect("failed to parse");
+ assert_eq!(lit.value(), "foo\nbar");
+
+ let lit = StringLit::parse("r#\"\r\nbar\"#").expect("failed to parse");
+ assert_eq!(lit.value(), "\nbar");
+
+ let lit = StringLit::parse("r##\"лиса\r\n\"##").expect("failed to parse");
+ assert_eq!(lit.value(), "лиса\n");
+}
+
+#[test]
+fn raw_string() {
+ check!(r"", false, Some(0));
+ check!(r"a", false, Some(0));
+ check!(r"peter", false, Some(0));
+ check!(r"Sei gegrüßt, Bärthelt!", false, Some(0));
+ check!(r"أنا لا أتحدث العربية", false, Some(0));
+ check!(r"お前はもう死んでいる", false, Some(0));
+ check!(r"Пушки - интересные музыкальные инструменты", false, Some(0));
+ check!(r"lit 👌 😂 af", false, Some(0));
+
+ check!(r#""#, false, Some(1));
+ check!(r#"a"#, false, Some(1));
+ check!(r##"peter"##, false, Some(2));
+ check!(r###"Sei gegrüßt, Bärthelt!"###, false, Some(3));
+ check!(r########"lit 👌 😂 af"########, false, Some(8));
+
+ check!(r#"foo " bar"#, false, Some(1));
+ check!(r##"foo " bar"##, false, Some(2));
+ check!(r#"foo """" '"'" bar"#, false, Some(1));
+ check!(r#""foo""#, false, Some(1));
+ check!(r###""foo'"###, false, Some(3));
+ check!(r#""x'#_#s'"#, false, Some(1));
+ check!(r"#", false, Some(0));
+ check!(r"foo#", false, Some(0));
+ check!(r"##bar", false, Some(0));
+ check!(r###""##foo"##bar'"###, false, Some(3));
+
+ check!(r"さび\n\t\r\0\\x60\u{123}フェリス", false, Some(0));
+ check!(r#"さび\n\t\r\0\\x60\u{123}フェリス"#, false, Some(1));
+}
+
+#[test]
+fn suffixes() {
+ check!("hello", r###""hello"suffix"###, false, None, "suffix");
+ check!(r"お前はもう死んでいる", r###"r"お前はもう死んでいる"_banana"###, false, Some(0), "_banana");
+ check!("fox", r#""fox"peter"#, false, None, "peter");
+ check!("🦊", r#""🦊"peter"#, false, None, "peter");
+ check!("నక్క\\\\u{0b10}", r###""నక్క\\\\u{0b10}"jü_rgen"###, true, None, "jü_rgen");
+}
+
+#[test]
+fn parse_err() {
+ assert_err!(StringLit, r#"""#, UnterminatedString, None);
+ assert_err!(StringLit, r#""犬"#, UnterminatedString, None);
+ assert_err!(StringLit, r#""Jürgen"#, UnterminatedString, None);
+ assert_err!(StringLit, r#""foo bar baz"#, UnterminatedString, None);
+
+ assert_err!(StringLit, r#""fox"peter""#, InvalidSuffix, 5);
+ assert_err!(StringLit, r###"r#"foo "# bar"#"###, UnexpectedChar, 9);
+
+ assert_err!(StringLit, "\"\r\"", IsolatedCr, 1);
+ assert_err!(StringLit, "\"fo\rx\"", IsolatedCr, 3);
+ assert_err!(StringLit, "r\"\r\"", IsolatedCr, 2);
+ assert_err!(StringLit, "r\"fo\rx\"", IsolatedCr, 4);
+
+ assert_err!(StringLit, r##"r####""##, UnterminatedRawString, None);
+ assert_err!(StringLit, r#####"r##"foo"#bar"#####, UnterminatedRawString, None);
+ assert_err!(StringLit, r##"r####"##, InvalidLiteral, None);
+ assert_err!(StringLit, r##"r####x"##, InvalidLiteral, None);
+}
+
+#[test]
+fn invald_ascii_escapes() {
+ assert_err!(StringLit, r#""\x80""#, NonAsciiXEscape, 1..5);
+ assert_err!(StringLit, r#""🦊\x81""#, NonAsciiXEscape, 5..9);
+ assert_err!(StringLit, r#"" \x8a""#, NonAsciiXEscape, 2..6);
+ assert_err!(StringLit, r#""\x8Ff""#, NonAsciiXEscape, 1..5);
+ assert_err!(StringLit, r#""\xa0 ""#, NonAsciiXEscape, 1..5);
+ assert_err!(StringLit, r#""నక్క\xB0""#, NonAsciiXEscape, 13..17);
+ assert_err!(StringLit, r#""\xc3నక్క""#, NonAsciiXEscape, 1..5);
+ assert_err!(StringLit, r#""\xDf🦊""#, NonAsciiXEscape, 1..5);
+ assert_err!(StringLit, r#""నక్క\xffనక్క""#, NonAsciiXEscape, 13..17);
+ assert_err!(StringLit, r#""\xfF ""#, NonAsciiXEscape, 1..5);
+ assert_err!(StringLit, r#"" \xFf""#, NonAsciiXEscape, 2..6);
+ assert_err!(StringLit, r#""నక్క \xFF""#, NonAsciiXEscape, 15..19);
+}
+
+#[test]
+fn invalid_escapes() {
+ assert_err!(StringLit, r#""\a""#, UnknownEscape, 1..3);
+ assert_err!(StringLit, r#""foo\y""#, UnknownEscape, 4..6);
+ assert_err!(StringLit, r#""\"#, UnterminatedEscape, 1);
+ assert_err!(StringLit, r#""\x""#, UnterminatedEscape, 1..3);
+ assert_err!(StringLit, r#""🦊\x1""#, UnterminatedEscape, 5..8);
+ assert_err!(StringLit, r#"" \xaj""#, InvalidXEscape, 2..6);
+ assert_err!(StringLit, r#""నక్క\xjb""#, InvalidXEscape, 13..17);
+}
+
+#[test]
+fn invalid_unicode_escapes() {
+ assert_err!(StringLit, r#""\u""#, UnicodeEscapeWithoutBrace, 1..3);
+ assert_err!(StringLit, r#""🦊\u ""#, UnicodeEscapeWithoutBrace, 5..7);
+ assert_err!(StringLit, r#""\u3""#, UnicodeEscapeWithoutBrace, 1..3);
+
+ assert_err!(StringLit, r#""\u{""#, UnterminatedUnicodeEscape, 1..4);
+ assert_err!(StringLit, r#""\u{12""#, UnterminatedUnicodeEscape, 1..6);
+ assert_err!(StringLit, r#""🦊\u{a0b""#, UnterminatedUnicodeEscape, 5..11);
+ assert_err!(StringLit, r#""\u{a0_b ""#, UnterminatedUnicodeEscape, 1..10);
+
+ assert_err!(StringLit, r#""\u{_}నక్క""#, InvalidStartOfUnicodeEscape, 4);
+ assert_err!(StringLit, r#""\u{_5f}""#, InvalidStartOfUnicodeEscape, 4);
+
+ assert_err!(StringLit, r#""fox\u{x}""#, NonHexDigitInUnicodeEscape, 7);
+ assert_err!(StringLit, r#""\u{0x}🦊""#, NonHexDigitInUnicodeEscape, 5);
+ assert_err!(StringLit, r#""నక్క\u{3bx}""#, NonHexDigitInUnicodeEscape, 18);
+ assert_err!(StringLit, r#""\u{3b_x}лиса""#, NonHexDigitInUnicodeEscape, 7);
+ assert_err!(StringLit, r#""\u{4x_}""#, NonHexDigitInUnicodeEscape, 5);
+
+ assert_err!(StringLit, r#""\u{1234567}""#, TooManyDigitInUnicodeEscape, 10);
+ assert_err!(StringLit, r#""నక్క\u{1234567}🦊""#, TooManyDigitInUnicodeEscape, 22);
+ assert_err!(StringLit, r#""నక్క\u{1_23_4_56_7}""#, TooManyDigitInUnicodeEscape, 26);
+ assert_err!(StringLit, r#""\u{abcdef123}лиса""#, TooManyDigitInUnicodeEscape, 10);
+
+ assert_err!(StringLit, r#""\u{110000}fox""#, InvalidUnicodeEscapeChar, 1..10);
+}
diff --git a/third_party/rust/litrs/src/test_util.rs b/third_party/rust/litrs/src/test_util.rs
new file mode 100644
index 0000000000..fd284e984e
--- /dev/null
+++ b/third_party/rust/litrs/src/test_util.rs
@@ -0,0 +1,128 @@
+use crate::*;
+use std::fmt::{Debug, Display};
+
+
+#[track_caller]
+pub(crate) fn assert_parse_ok_eq<T: PartialEq + Debug + Display>(
+ input: &str,
+ result: Result<T, ParseError>,
+ expected: T,
+ parse_method: &str,
+) {
+ match result {
+ Ok(actual) if actual == expected => {
+ if actual.to_string() != input {
+ panic!(
+ "formatting does not yield original input `{}`: {:?}",
+ input,
+ actual,
+ );
+ }
+ }
+ Ok(actual) => {
+ panic!(
+ "unexpected parsing result (with `{}`) for `{}`:\nactual: {:?}\nexpected: {:?}",
+ parse_method,
+ input,
+ actual,
+ expected,
+ );
+ }
+ Err(e) => {
+ panic!(
+ "expected `{}` to be parsed (with `{}`) successfully, but it failed: {:?}",
+ input,
+ parse_method,
+ e,
+ );
+ }
+ }
+}
+
+// This is not ideal, but to perform this check we need `proc-macro2`. So we
+// just don't do anything if that feature is not enabled.
+#[cfg(not(feature = "proc-macro2"))]
+pub(crate) fn assert_roundtrip<T>(_: T, _: &str) {}
+
+#[cfg(feature = "proc-macro2")]
+#[track_caller]
+pub(crate) fn assert_roundtrip<T>(ours: T, input: &str)
+where
+ T: std::convert::TryFrom<proc_macro2::Literal> + fmt::Debug + PartialEq + Clone,
+ proc_macro2::Literal: From<T>,
+ <T as std::convert::TryFrom<proc_macro2::Literal>>::Error: std::fmt::Display,
+{
+ let pm_lit = input.parse::<proc_macro2::Literal>()
+ .expect("failed to parse input as proc_macro2::Literal");
+ let t_name = std::any::type_name::<T>();
+
+ // Unfortunately, `proc_macro2::Literal` does not implement `PartialEq`, so
+ // this is the next best thing.
+ if proc_macro2::Literal::from(ours.clone()).to_string() != pm_lit.to_string() {
+ panic!(
+ "Converting {} to proc_macro2::Literal has unexpected result:\
+ \nconverted: {:?}\nexpected: {:?}",
+ t_name,
+ proc_macro2::Literal::from(ours),
+ pm_lit,
+ );
+ }
+
+ match T::try_from(pm_lit) {
+ Err(e) => {
+ panic!("Trying to convert proc_macro2::Literal to {} results in error: {}", t_name, e);
+ }
+ Ok(res) => {
+ if res != ours {
+ panic!(
+ "Converting proc_macro2::Literal to {} has unexpected result:\
+ \nactual: {:?}\nexpected: {:?}",
+ t_name,
+ res,
+ ours,
+ );
+ }
+ }
+ }
+}
+
+macro_rules! assert_err {
+ ($ty:ident, $input:literal, $kind:ident, $( $span:tt )+ ) => {
+ assert_err_single!($ty::parse($input), $kind, $($span)+);
+ assert_err_single!($crate::Literal::parse($input), $kind, $($span)+);
+ };
+}
+
+macro_rules! assert_err_single {
+ ($expr:expr, $kind:ident, $( $span:tt )+ ) => {
+ let res = $expr;
+ let err = match res {
+ Err(e) => e,
+ Ok(v) => panic!(
+ "Expected `{}` to return an error, but it returned Ok({:?})",
+ stringify!($expr),
+ v,
+ ),
+ };
+ if err.kind != $crate::err::ParseErrorKind::$kind {
+ panic!(
+ "Expected error kind {} for `{}` but got {:?}",
+ stringify!($kind),
+ stringify!($expr),
+ err.kind,
+ )
+ }
+ let expected_span = assert_err_single!(@span $($span)+);
+ if err.span != expected_span {
+ panic!(
+ "Expected error span {:?} for `{}` but got {:?}",
+ expected_span,
+ stringify!($expr),
+ err.span,
+ )
+ }
+ };
+ (@span $start:literal .. $end:literal) => { Some($start .. $end) };
+ (@span $at:literal) => { Some($at.. $at + 1) };
+ (@span None) => { None };
+}
diff --git a/third_party/rust/litrs/src/tests.rs b/third_party/rust/litrs/src/tests.rs
new file mode 100644
index 0000000000..613b429540
--- /dev/null
+++ b/third_party/rust/litrs/src/tests.rs
@@ -0,0 +1,349 @@
+use crate::Literal;
+
+
+#[test]
+fn empty() {
+ assert_err!(Literal, "", Empty, None);
+}
+
+#[test]
+fn invalid_literals() {
+ assert_err_single!(Literal::parse("."), InvalidLiteral, None);
+ assert_err_single!(Literal::parse("+"), InvalidLiteral, None);
+ assert_err_single!(Literal::parse("-"), InvalidLiteral, None);
+ assert_err_single!(Literal::parse("e"), InvalidLiteral, None);
+ assert_err_single!(Literal::parse("e8"), InvalidLiteral, None);
+ assert_err_single!(Literal::parse("f32"), InvalidLiteral, None);
+ assert_err_single!(Literal::parse("foo"), InvalidLiteral, None);
+ assert_err_single!(Literal::parse("inf"), InvalidLiteral, None);
+ assert_err_single!(Literal::parse("nan"), InvalidLiteral, None);
+ assert_err_single!(Literal::parse("NaN"), InvalidLiteral, None);
+ assert_err_single!(Literal::parse("NAN"), InvalidLiteral, None);
+ assert_err_single!(Literal::parse("_2.7"), InvalidLiteral, None);
+ assert_err_single!(Literal::parse(".5"), InvalidLiteral, None);
+}
+
+#[test]
+fn misc() {
+ assert_err_single!(Literal::parse("0x44.5"), UnexpectedChar, 4..6);
+ assert_err_single!(Literal::parse("a"), InvalidLiteral, None);
+ assert_err_single!(Literal::parse(";"), InvalidLiteral, None);
+ assert_err_single!(Literal::parse("0;"), UnexpectedChar, 1);
+ assert_err_single!(Literal::parse(" 0"), InvalidLiteral, None);
+ assert_err_single!(Literal::parse("0 "), UnexpectedChar, 1);
+ assert_err_single!(Literal::parse("_"), InvalidLiteral, None);
+ assert_err_single!(Literal::parse("_3"), InvalidLiteral, None);
+ assert_err_single!(Literal::parse("a_123"), InvalidLiteral, None);
+ assert_err_single!(Literal::parse("B_123"), InvalidLiteral, None);
+}
+
+macro_rules! assert_no_panic {
+ ($input:expr) => {
+ let arr = $input;
+ let input = std::str::from_utf8(&arr).expect("not unicode");
+ let res = std::panic::catch_unwind(move || {
+ let _ = Literal::parse(input);
+ let _ = crate::BoolLit::parse(input);
+ let _ = crate::IntegerLit::parse(input);
+ let _ = crate::FloatLit::parse(input);
+ let _ = crate::CharLit::parse(input);
+ let _ = crate::StringLit::parse(input);
+ let _ = crate::ByteLit::parse(input);
+ let _ = crate::ByteStringLit::parse(input);
+ });
+
+ if let Err(e) = res {
+ println!("\n!!! panic for: {:?}", input);
+ std::panic::resume_unwind(e);
+ }
+ };
+}
+
+#[test]
+#[ignore]
+fn never_panic_up_to_3() {
+ for a in 0..128 {
+ assert_no_panic!([a]);
+ for b in 0..128 {
+ assert_no_panic!([a, b]);
+ for c in 0..128 {
+ assert_no_panic!([a, b, c]);
+ }
+ }
+ }
+}
+
+// This test takes super long in debug mode, but in release mode it's fine.
+#[test]
+#[ignore]
+fn never_panic_len_4() {
+ for a in 0..128 {
+ for b in 0..128 {
+ for c in 0..128 {
+ for d in 0..128 {
+ assert_no_panic!([a, b, c, d]);
+ }
+ }
+ }
+ }
+}
+
+#[cfg(feature = "proc-macro2")]
+#[test]
+fn proc_macro() {
+ use std::convert::TryFrom;
+ use proc_macro2::{
+ self as pm2, TokenTree, Group, TokenStream, Delimiter, Spacing, Punct, Span, Ident,
+ };
+ use crate::{
+ BoolLit, ByteLit, ByteStringLit, CharLit, FloatLit, IntegerLit, StringLit, err::TokenKind
+ };
+
+
+ macro_rules! assert_invalid_token {
+ ($input:expr, expected: $expected:path, actual: $actual:path $(,)?) => {
+ let err = $input.unwrap_err();
+ if err.expected != $expected {
+ panic!(
+ "err.expected was expected to be {:?}, but is {:?}",
+ $expected,
+ err.expected,
+ );
+ }
+ if err.actual != $actual {
+ panic!("err.actual was expected to be {:?}, but is {:?}", $actual, err.actual);
+ }
+ };
+ }
+
+
+ let pm_u16_lit = pm2::Literal::u16_suffixed(2700);
+ let pm_i16_lit = pm2::Literal::i16_unsuffixed(3912);
+ let pm_f32_lit = pm2::Literal::f32_unsuffixed(3.14);
+ let pm_f64_lit = pm2::Literal::f64_suffixed(99.3);
+ let pm_string_lit = pm2::Literal::string("hello 🦊");
+ let pm_bytestr_lit = pm2::Literal::byte_string(b"hello \nfoxxo");
+ let pm_char_lit = pm2::Literal::character('🦀');
+
+ let u16_lit = Literal::parse("2700u16".to_string()).unwrap();
+ let i16_lit = Literal::parse("3912".to_string()).unwrap();
+ let f32_lit = Literal::parse("3.14".to_string()).unwrap();
+ let f64_lit = Literal::parse("99.3f64".to_string()).unwrap();
+ let string_lit = Literal::parse(r#""hello 🦊""#.to_string()).unwrap();
+ let bytestr_lit = Literal::parse(r#"b"hello \nfoxxo""#.to_string()).unwrap();
+ let char_lit = Literal::parse("'🦀'".to_string()).unwrap();
+
+ assert_eq!(Literal::from(&pm_u16_lit), u16_lit);
+ assert_eq!(Literal::from(&pm_i16_lit), i16_lit);
+ assert_eq!(Literal::from(&pm_f32_lit), f32_lit);
+ assert_eq!(Literal::from(&pm_f64_lit), f64_lit);
+ assert_eq!(Literal::from(&pm_string_lit), string_lit);
+ assert_eq!(Literal::from(&pm_bytestr_lit), bytestr_lit);
+ assert_eq!(Literal::from(&pm_char_lit), char_lit);
+
+
+ let group = TokenTree::from(Group::new(Delimiter::Brace, TokenStream::new()));
+ let punct = TokenTree::from(Punct::new(':', Spacing::Alone));
+ let ident = TokenTree::from(Ident::new("peter", Span::call_site()));
+
+ assert_eq!(
+ Literal::try_from(TokenTree::Literal(pm2::Literal::string("hello 🦊"))).unwrap(),
+ Literal::String(StringLit::parse(r#""hello 🦊""#.to_string()).unwrap()),
+ );
+ assert_invalid_token!(
+ Literal::try_from(punct.clone()),
+ expected: TokenKind::Literal,
+ actual: TokenKind::Punct,
+ );
+ assert_invalid_token!(
+ Literal::try_from(group.clone()),
+ expected: TokenKind::Literal,
+ actual: TokenKind::Group,
+ );
+ assert_invalid_token!(
+ Literal::try_from(ident.clone()),
+ expected: TokenKind::Literal,
+ actual: TokenKind::Ident,
+ );
+
+
+ assert_eq!(Literal::from(IntegerLit::try_from(pm_u16_lit.clone()).unwrap()), u16_lit);
+ assert_eq!(Literal::from(IntegerLit::try_from(pm_i16_lit.clone()).unwrap()), i16_lit);
+ assert_eq!(Literal::from(FloatLit::try_from(pm_f32_lit.clone()).unwrap()), f32_lit);
+ assert_eq!(Literal::from(FloatLit::try_from(pm_f64_lit.clone()).unwrap()), f64_lit);
+ assert_eq!(Literal::from(StringLit::try_from(pm_string_lit.clone()).unwrap()), string_lit);
+ assert_eq!(
+ Literal::from(ByteStringLit::try_from(pm_bytestr_lit.clone()).unwrap()),
+ bytestr_lit,
+ );
+ assert_eq!(Literal::from(CharLit::try_from(pm_char_lit.clone()).unwrap()), char_lit);
+
+ assert_invalid_token!(
+ StringLit::try_from(pm_u16_lit.clone()),
+ expected: TokenKind::StringLit,
+ actual: TokenKind::IntegerLit,
+ );
+ assert_invalid_token!(
+ StringLit::try_from(pm_f32_lit.clone()),
+ expected: TokenKind::StringLit,
+ actual: TokenKind::FloatLit,
+ );
+ assert_invalid_token!(
+ ByteLit::try_from(pm_bytestr_lit.clone()),
+ expected: TokenKind::ByteLit,
+ actual: TokenKind::ByteStringLit,
+ );
+ assert_invalid_token!(
+ ByteLit::try_from(pm_i16_lit.clone()),
+ expected: TokenKind::ByteLit,
+ actual: TokenKind::IntegerLit,
+ );
+ assert_invalid_token!(
+ IntegerLit::try_from(pm_string_lit.clone()),
+ expected: TokenKind::IntegerLit,
+ actual: TokenKind::StringLit,
+ );
+ assert_invalid_token!(
+ IntegerLit::try_from(pm_char_lit.clone()),
+ expected: TokenKind::IntegerLit,
+ actual: TokenKind::CharLit,
+ );
+
+
+ assert_eq!(
+ Literal::from(IntegerLit::try_from(TokenTree::from(pm_u16_lit.clone())).unwrap()),
+ u16_lit,
+ );
+ assert_eq!(
+ Literal::from(IntegerLit::try_from(TokenTree::from(pm_i16_lit.clone())).unwrap()),
+ i16_lit,
+ );
+ assert_eq!(
+ Literal::from(FloatLit::try_from(TokenTree::from(pm_f32_lit.clone())).unwrap()),
+ f32_lit,
+ );
+ assert_eq!(
+ Literal::from(FloatLit::try_from(TokenTree::from(pm_f64_lit.clone())).unwrap()),
+ f64_lit,
+ );
+ assert_eq!(
+ Literal::from(StringLit::try_from(TokenTree::from(pm_string_lit.clone())).unwrap()),
+ string_lit,
+ );
+ assert_eq!(
+ Literal::from(ByteStringLit::try_from(TokenTree::from(pm_bytestr_lit.clone())).unwrap()),
+ bytestr_lit,
+ );
+ assert_eq!(
+ Literal::from(CharLit::try_from(TokenTree::from(pm_char_lit.clone())).unwrap()),
+ char_lit,
+ );
+
+ assert_invalid_token!(
+ StringLit::try_from(TokenTree::from(pm_u16_lit.clone())),
+ expected: TokenKind::StringLit,
+ actual: TokenKind::IntegerLit,
+ );
+ assert_invalid_token!(
+ StringLit::try_from(TokenTree::from(pm_f32_lit.clone())),
+ expected: TokenKind::StringLit,
+ actual: TokenKind::FloatLit,
+ );
+ assert_invalid_token!(
+ BoolLit::try_from(TokenTree::from(pm_bytestr_lit.clone())),
+ expected: TokenKind::BoolLit,
+ actual: TokenKind::ByteStringLit,
+ );
+ assert_invalid_token!(
+ BoolLit::try_from(TokenTree::from(pm_i16_lit.clone())),
+ expected: TokenKind::BoolLit,
+ actual: TokenKind::IntegerLit,
+ );
+ assert_invalid_token!(
+ IntegerLit::try_from(TokenTree::from(pm_string_lit.clone())),
+ expected: TokenKind::IntegerLit,
+ actual: TokenKind::StringLit,
+ );
+ assert_invalid_token!(
+ IntegerLit::try_from(TokenTree::from(pm_char_lit.clone())),
+ expected: TokenKind::IntegerLit,
+ actual: TokenKind::CharLit,
+ );
+
+ assert_invalid_token!(
+ StringLit::try_from(TokenTree::from(group)),
+ expected: TokenKind::StringLit,
+ actual: TokenKind::Group,
+ );
+ assert_invalid_token!(
+ BoolLit::try_from(TokenTree::from(punct)),
+ expected: TokenKind::BoolLit,
+ actual: TokenKind::Punct,
+ );
+ assert_invalid_token!(
+ FloatLit::try_from(TokenTree::from(ident)),
+ expected: TokenKind::FloatLit,
+ actual: TokenKind::Ident,
+ );
+}
+
+#[cfg(feature = "proc-macro2")]
+#[test]
+fn bool_try_from_tt() {
+ use std::convert::TryFrom;
+ use proc_macro2::{Ident, Span, TokenTree};
+ use crate::BoolLit;
+
+
+ let ident = |s: &str| Ident::new(s, Span::call_site());
+
+ assert_eq!(BoolLit::try_from(TokenTree::Ident(ident("true"))).unwrap(), BoolLit::True);
+ assert_eq!(BoolLit::try_from(TokenTree::Ident(ident("false"))).unwrap(), BoolLit::False);
+
+ assert!(BoolLit::try_from(TokenTree::Ident(ident("falsex"))).is_err());
+ assert!(BoolLit::try_from(TokenTree::Ident(ident("_false"))).is_err());
+ assert!(BoolLit::try_from(TokenTree::Ident(ident("False"))).is_err());
+ assert!(BoolLit::try_from(TokenTree::Ident(ident("True"))).is_err());
+ assert!(BoolLit::try_from(TokenTree::Ident(ident("ltrue"))).is_err());
+
+
+ assert_eq!(
+ Literal::try_from(TokenTree::Ident(ident("true"))).unwrap(),
+ Literal::Bool(BoolLit::True),
+ );
+ assert_eq!(
+ Literal::try_from(TokenTree::Ident(ident("false"))).unwrap(),
+ Literal::Bool(BoolLit::False),
+ );
+
+ assert!(Literal::try_from(TokenTree::Ident(ident("falsex"))).is_err());
+ assert!(Literal::try_from(TokenTree::Ident(ident("_false"))).is_err());
+ assert!(Literal::try_from(TokenTree::Ident(ident("False"))).is_err());
+ assert!(Literal::try_from(TokenTree::Ident(ident("True"))).is_err());
+ assert!(Literal::try_from(TokenTree::Ident(ident("ltrue"))).is_err());
+}
+
+#[cfg(feature = "proc-macro2")]
+#[test]
+fn invalid_token_display() {
+ use crate::{InvalidToken, err::TokenKind};
+
+ let span = crate::err::Span::Two(proc_macro2::Span::call_site());
+ assert_eq!(
+ InvalidToken {
+ actual: TokenKind::StringLit,
+ expected: TokenKind::FloatLit,
+ span,
+ }.to_string(),
+ r#"expected a float literal (e.g. `3.14`), but found a string literal (e.g. "Ferris")"#,
+ );
+
+ assert_eq!(
+ InvalidToken {
+ actual: TokenKind::Punct,
+ expected: TokenKind::Literal,
+ span,
+ }.to_string(),
+ r#"expected a literal, but found a punctuation character"#,
+ );
+}
diff --git a/third_party/rust/naga/.cargo-checksum.json b/third_party/rust/naga/.cargo-checksum.json
index 9696ce31b8..8ac93423af 100644
--- a/third_party/rust/naga/.cargo-checksum.json
+++ b/third_party/rust/naga/.cargo-checksum.json
@@ -1 +1 @@
-{"files":{".cargo/config.toml":"d7389d2a0c08ec72b79e83a3c76980903e3f9123625c32e69c798721193e2e74","CHANGELOG.md":"6b2c4d8dfd8c537811c33744703b4c03fa8aa15f5fab8f0e2be76f597cb7e273","Cargo.toml":"dcdd9d3b310431871c1b37fbe3f75b2baa193820376a466d9ab0bbd481ff7e6c","README.md":"daa4717a9952b52604bbc3a55af902b252adeacc779991317d8f301f07faa94b","benches/criterion.rs":"f45e38b26e1323e934d32623572ff5395a53fed06f760eb1e07b22ed07858a38","src/arena.rs":"33ed2ec7b36429b133ed2a7de6fb9735827f69ea8b6c2ce97f64746a24a5bf36","src/back/dot/mod.rs":"a40050a73ac00c8fa43dd0b45a84fca6959d28c8c99ab3046b01f33c02f8c8f4","src/back/glsl/features.rs":"092bb95d34735b8cff198b8a50ec8ec2f8f89a1190cb33dfc59c1d0f9f0064cc","src/back/glsl/keywords.rs":"1546facbaddf696602102f32e47db7afc875f8ca3fbccc2122e0bcc45e022b53","src/back/glsl/mod.rs":"deb3e2cdcc5845e59c2644abfff4bd8a5c5d7d5e6ec70b818107d7abeddda328","src/back/hlsl/conv.rs":"5e40946f2d5ad6589dd2b0570d2c300dd96f92f0f954b829dc54a822de7774e8","src/back/hlsl/help.rs":"8f4ec24f74f3153a58b04f441bef16ecc8d400466d53187b06fb6b60a934a1ec","src/back/hlsl/keywords.rs":"eb4af8d697fb7f3991859d66339b5b2eac27c9fe41b73146ac838b207f462c9c","src/back/hlsl/mod.rs":"c677ebbb649a1c5f85f350d8da7129457d50ff52b1c88c1f0fac4a9d11eb020c","src/back/hlsl/storage.rs":"3170f165ff68d2482f8a8dbfa4bbf4b65d2446a7f208f33eea9eb187bb57eb09","src/back/hlsl/writer.rs":"3e054ef1e1d38ac5606fbfed4fe0400e2c39cfac0525dee9e1756ed2361e2244","src/back/mod.rs":"b941caed50c086f49d25e76228d247ba6c2da6dbeea18d968c02dc68bb97f409","src/back/msl/keywords.rs":"998c0d86a26e5cf031c75f35cde28f2b390fe207a2e7d0eed8516ffdb99c1a8e","src/back/msl/mod.rs":"16d905902e30cf900ec924b66ff496adbbbc54af15c59713f358bfac042a625a","src/back/msl/sampler.rs":"9b01d68669e12ff7123243284b85e1a9d2c4d49140bd74ca32dedc007cbf15af","src/back/msl/writer.rs":"e8af7054f33a812948731bd451be510e80552c61dfaed9f3079b4c3a7b369304","src/back/spv/block.rs":"2881cbc6c0a3e310a777b61c950dd97bbee5776583fe6ef13ee04095a8214768","src/back/spv/helpers.rs":"a4e260130f39c7345decec40dadf1e94419c8f6d236ce7a53b5300aa72952a1b","src/back/spv/image.rs":"e4b982ce430e17881d6370191d849dbe6bb8f6d86f4896815eb1736e43b4e302","src/back/spv/index.rs":"26611dd50df5cfd214900e19415f5374dd301d3b7d3bfedbc5ec0f254328287a","src/back/spv/instructions.rs":"d0ced535fdec49323105a7d6ee40a8ed6b4966ac5f0f40b062f0eb11a531b106","src/back/spv/layout.rs":"e263de53cd2f9a03ad94b82b434ce636609bc1ed435a2d1132951663bfaa8ebd","src/back/spv/mod.rs":"31b0229f59b5784b57851fcf6325095add58af6de3afa85d518a4e266c4b99a9","src/back/spv/ray.rs":"a34bf6b26d873f7270caa45841d9ef291aca8d9732ecd086b14d8856038e1e41","src/back/spv/recyclable.rs":"114db0ea12774d6514f995d07295cb9a42631ab75165fc60980c10e9b5ecb832","src/back/spv/selection.rs":"81e404abfa0a977f7c1f76ccb37a78d13ccadbda229048dad53cc67687cc39db","src/back/spv/writer.rs":"a76a73c0692162da24ab5508bc3ca70eb5e01367fe54472d100e237dbd594467","src/back/wgsl/mod.rs":"2dd12bbea9ace835850192bb68c5760953da6bac6a636073d1eca19381c0c0b6","src/back/wgsl/writer.rs":"15ba0e1ab7358b725d1cbc2e0b0b2284c33cc240ae84b20e186519efbb5d96d9","src/block.rs":"c69089e5bbb6de6ba24efb15b21d5d434fcabfbc4d48feae948d2a4da135aae7","src/compact/expressions.rs":"7a4c916282a5b484519ed29ab451c7b595d8dea73c83c5c2cf7efc6fbc648fda","src/compact/functions.rs":"174bd9167ecf6353afb8c36d365ba3f9b483233eb4bacf578e50183c7433aa15","src/compact/handle_set_map.rs":"817c5193352d5fd6a61a5c970daba23224e14a65aea15f8f1c8679c99f834ca2","src/compact/mod.rs":"f1a606e8732f3c5837ab40ba5569eb1687336ef412f7f4b6cc348dd52b8076b3","src/compact/statements.rs":"4df33ee9589300e769e75c674bdc30578e93704ec3eb2aabc7132121745b55c8","src/compact/types.rs":"18343f2ca2c123eea2531cffc1d54a7798797caccecaa1f9b8c4fd5dd6ca1a05","src/front/glsl/ast.rs":"a4615f0c52b0dc9fdb07f816b4534c1ca547c2d176008ca86d66f9e6874f227d","src/front/glsl/builtins.rs":"d35501d5b42b61c261da24436b82eafdf96371b1600d148648d90d041f736ae4","src/front/glsl/context.rs":"066203c24ff5bc6154aa671f4492b5e8dfede8b57ef886f093cc95470d66411b","src/front/glsl/error.rs":"cca4a3aa9de2808952ff68c183755df5fdf6a7cb81f170ba747795176c0342fd","src/front/glsl/functions.rs":"60838c34b8295112e5696b52d710acebb93e0a982e05f8eb87d3b80f52eb7793","src/front/glsl/lex.rs":"08736ae8beb955da5b0e6e3e0f45995a824995f7096d516a2910417e9c7afa32","src/front/glsl/mod.rs":"c6e81710ae94a52583ba6f2a80a505d6bcd6ea6552008b80b27539af48838df1","src/front/glsl/offset.rs":"9358602ca4f9ef21d5066d674dae757bf88fdf5c289c4360534354d13bd41dc0","src/front/glsl/parser.rs":"fe5291512db412b33b6c09d5b3dcf7c54ff6ec55b47f0a078dcc11695e78471d","src/front/glsl/parser/declarations.rs":"d637cc52e553910a2e97b70b3366c15aefbe737f413adb11c27efd184c1fbf9d","src/front/glsl/parser/expressions.rs":"520cfc9402d5fbd48e52ef1d36562c6b74794c09ec33ec1ebb10aa48d129b66f","src/front/glsl/parser/functions.rs":"67615684e1c13a1b0e6c0b6028bdf040a14f5d1aea3fde82a5783921244d90d3","src/front/glsl/parser/types.rs":"aeb97e1a5fb03205cd5630c29da59d81a376ce9a83a603b62b037e63ad948e88","src/front/glsl/parser_tests.rs":"bfd4dff2580f4369a57edbcace47d23e2666751ffc3ab55f8d7dfe01f1a66311","src/front/glsl/token.rs":"c25c489b152ee2d445ace3c2046473abe64d558b8d27fa08709110e58718b6ac","src/front/glsl/types.rs":"58c9cf3d570dff8cb68f2931faf5b18e875e510741bf035ec10b9ff6df27c5d8","src/front/glsl/variables.rs":"fb2a09e386b6e98ca9fb8fb744afac1e8b19d1b67c6ede5c474e3ba860d3d4cb","src/front/interpolator.rs":"9b6ca498d5fbd9bc1515510a04e303a00b324121d7285da3c955cfe18eb4224c","src/front/mod.rs":"77acd7fb71d004969d1ee69fc728647f03242762988786c4e15fadf8315600af","src/front/spv/convert.rs":"dccc6671e6a4a7f1139aecdf979faa3689609081af5fa2cbbd6a2e8c4128c004","src/front/spv/error.rs":"6438aac57cfcf5d3858dd7652ccda1967a3123c6374f1cab829092b00549f70f","src/front/spv/function.rs":"3a3f0c07862750f79f8ebc273c5df11efc67272566458410f776bd8fa271a0f8","src/front/spv/image.rs":"5d55cfbf6752732a594114cd09a9a207216e1ee85d8f2c9bc4310217a55ea321","src/front/spv/mod.rs":"af2771e7e6b38b44e11b8ca2dba31dfdc81a3bbde041b2e73eed361b892b9a91","src/front/spv/null.rs":"e1446d99d04c76a9c3bbd24dd9b20c4711ce8a918a9b403be6cccbde1175b3b4","src/front/type_gen.rs":"b4f1df23380e06c9fdad4140810ce96ab041dbb1d371a07045b4e0069aa8ba55","src/front/wgsl/error.rs":"cbc87c24ef97bbec96c04cab0ee75fe64f855f263b9edca90498a7cbd253b801","src/front/wgsl/index.rs":"2b9a4929a46bd822d3ed6f9a150e24d437e5bdca8293eb748aebe80ce7e74153","src/front/wgsl/lower/construction.rs":"92342e27f5bdeb598e178799b74aa610788549c19a49fe0ae8914916bfa3c7be","src/front/wgsl/lower/conversion.rs":"961b19bf8ddd4667c6caf854a1889f3d6477757f4125538c3e9ca7d730975dd7","src/front/wgsl/lower/mod.rs":"174def9462bae5c2aed3aa0eb1b4773c282e9ff0320a7dfdb662aeb4bf51cc22","src/front/wgsl/mod.rs":"02b194a0a29ef7281f71b424564e18ada4a8b1a0d8c26ec40b6be195bd4c4904","src/front/wgsl/parse/ast.rs":"c7eaae40179f0889f2b142d3b31968cbfab6d3cfe02e425912c6da8dadac51df","src/front/wgsl/parse/conv.rs":"01b25edbe80b263a3fa51bc980c075630bb31d4af851441323383eb4f3b83360","src/front/wgsl/parse/lexer.rs":"17db87d0017f8f9a80fa151b8545f04e1b40c4e5feef6197f4a117efa03488bf","src/front/wgsl/parse/mod.rs":"3b4895a2baf91c719b95f0afb6441ffac2036c2a9ff817e633882fd257afcc38","src/front/wgsl/parse/number.rs":"43b2a03963e61ee047eeac144ab817bf9f9e9a9517b26b68ff40f2f6236de05d","src/front/wgsl/tests.rs":"39d0b44d0f073a7599c88b7c4efd1572886f3af074fa2015454623be313b297f","src/front/wgsl/to_wgsl.rs":"2e2e30d86b07f209b866e530d3a882803bf28b39ce379052561a749f628e8e28","src/keywords/mod.rs":"0138f3931f8af0b0a05174549d0fd2152945b027dc3febefc1bbd676581d2e45","src/keywords/wgsl.rs":"7c3b364b60ca29cb8a68ef781de9ecd28b76b74bed18bf18a35d2ebffaa855ab","src/lib.rs":"ec1ac0883866f5bf60fc8409b97671bbafc21eaca5e13c68894bd3e7fb93e348","src/proc/constant_evaluator.rs":"8f53985da9d8f1ea16938ab3561b4a5ec496c6c5f5df116830b286eaddd3ba14","src/proc/emitter.rs":"39ac886c651e2ad33c06a676a7e4826a0e93de0af660c01e8e4b1f7406742f88","src/proc/index.rs":"f4250f6944c2b631e8140979024e8deb86fa8d5352d8641ba954a388b2c0940e","src/proc/layouter.rs":"b3d061c87424f36981c902716f37ab7b72f2bb2d0c2d7e900c51149318ea1a0a","src/proc/mod.rs":"c780f9bb2464f6c61af34e782d711128501a1c769ef27184c388a92b88bfac38","src/proc/namer.rs":"7328fac41e40890c64c7ee2fa985a4395424f18b08d30f30ca2583fdabd2fd35","src/proc/terminator.rs":"13c59bf00f5b26171d971effc421091f5e00dedddd246c2daa44fe65aeda060a","src/proc/typifier.rs":"99de19270d01c12ec49d14323aa1d9b8774f1ee715804af7235deff70739ba3d","src/span.rs":"6560599f20b8bc2de746ee9fd6b05c32bb630af914fce8845d84fdc72f9a636c","src/valid/analyzer.rs":"8472b98f16a4a4a0fa7079197db25696f77ef3e1602a7cddea1930daebd27917","src/valid/compose.rs":"83e4c09c39f853cf085b83b87e48b3db571da619132960d3ec954ebdfb0a74f2","src/valid/expression.rs":"1cdbd594dbdb33d8473d93c11112cf717e262bb8c35cee10b01db4322b2237d7","src/valid/function.rs":"40754e51906b053becdd8813b189fe709b7693c08babd28b5d3f5c576475b171","src/valid/handles.rs":"0878915e67b16d7c41cf8245d9ab3b3f4a604e7d4e87527ea40e03efcbf1f74a","src/valid/interface.rs":"32ef8e4665106b5c71540833e17ee9cd1dde5a900c9b81f61e0b7b8192c4aaf2","src/valid/mod.rs":"1690984337db07c119abd481d71d8bc9be6323dd39998dc6cf464f586deb3a7a","src/valid/type.rs":"09e18bb9510dbb0cfb4a8ac054afee4c4f56063d614159ab5b956aa1e5850468"},"package":null} \ No newline at end of file
+{"files":{".cargo/config.toml":"d7389d2a0c08ec72b79e83a3c76980903e3f9123625c32e69c798721193e2e74","CHANGELOG.md":"6b2c4d8dfd8c537811c33744703b4c03fa8aa15f5fab8f0e2be76f597cb7e273","Cargo.toml":"eab8de21e33a65dbcddbdfd97fca5b98d5cf684f288ac32cf6177a761e44a2e0","README.md":"daa4717a9952b52604bbc3a55af902b252adeacc779991317d8f301f07faa94b","benches/criterion.rs":"f45e38b26e1323e934d32623572ff5395a53fed06f760eb1e07b22ed07858a38","src/arena.rs":"33ed2ec7b36429b133ed2a7de6fb9735827f69ea8b6c2ce97f64746a24a5bf36","src/back/dot/mod.rs":"a40050a73ac00c8fa43dd0b45a84fca6959d28c8c99ab3046b01f33c02f8c8f4","src/back/glsl/features.rs":"3d12147d201aaed746a94741356458a435a1ff7cf30b66baf44ba0b8dfe4b0ca","src/back/glsl/keywords.rs":"1546facbaddf696602102f32e47db7afc875f8ca3fbccc2122e0bcc45e022b53","src/back/glsl/mod.rs":"9e8b34a09401744a2ad4deae4d4863bd0be1d7d5da6ca72a98ca80fe0e3dfde6","src/back/hlsl/conv.rs":"2d7a8e7753b8fb21659e582612eea82e42e353abd23df719de450074a4da731e","src/back/hlsl/help.rs":"06da97ea0d58e2b94823ca1dae67a8611be6d5d047649b1d83755acb4c110808","src/back/hlsl/keywords.rs":"a7164690a4da866e6bfb18ced20e32cc8c42dd7387e0e84addf0c2674f529cf5","src/back/hlsl/mod.rs":"2f5296c45a2147093cae17250321580e7f01c57f907e529d19521eccd0cd4147","src/back/hlsl/storage.rs":"2c2a0071cafe487a398e396dddc85bdb319b1a5d74c097d529078e247a904359","src/back/hlsl/writer.rs":"36f0410edf9c0a8295e4916ca0e7a4e98cd170fcd5ecf6826df6051bef003b9c","src/back/mod.rs":"b941caed50c086f49d25e76228d247ba6c2da6dbeea18d968c02dc68bb97f409","src/back/msl/keywords.rs":"e6a4ef77363f995de1f8079c0b8591497cbf9520c5d3b2d41c7e1f483e8abd24","src/back/msl/mod.rs":"15fdb90b8cd2b98273b22b9569fc322eb473fd135865eef82cc615d27320d779","src/back/msl/sampler.rs":"9b01d68669e12ff7123243284b85e1a9d2c4d49140bd74ca32dedc007cbf15af","src/back/msl/writer.rs":"27e8604a5d11391b91b328f420e93f7cf475364a783b3dc5ba8bb720f17d9d86","src/back/spv/block.rs":"e2326e10cc8ca64398636c1b27166b406611006ffc2388c20fca4a271d609afe","src/back/spv/helpers.rs":"a4e260130f39c7345decec40dadf1e94419c8f6d236ce7a53b5300aa72952a1b","src/back/spv/image.rs":"e4b982ce430e17881d6370191d849dbe6bb8f6d86f4896815eb1736e43b4e302","src/back/spv/index.rs":"26611dd50df5cfd214900e19415f5374dd301d3b7d3bfedbc5ec0f254328287a","src/back/spv/instructions.rs":"d0ced535fdec49323105a7d6ee40a8ed6b4966ac5f0f40b062f0eb11a531b106","src/back/spv/layout.rs":"e263de53cd2f9a03ad94b82b434ce636609bc1ed435a2d1132951663bfaa8ebd","src/back/spv/mod.rs":"31b0229f59b5784b57851fcf6325095add58af6de3afa85d518a4e266c4b99a9","src/back/spv/ray.rs":"a34bf6b26d873f7270caa45841d9ef291aca8d9732ecd086b14d8856038e1e41","src/back/spv/recyclable.rs":"114db0ea12774d6514f995d07295cb9a42631ab75165fc60980c10e9b5ecb832","src/back/spv/selection.rs":"81e404abfa0a977f7c1f76ccb37a78d13ccadbda229048dad53cc67687cc39db","src/back/spv/writer.rs":"e90f76d7de82429db5d375b679de5dd73f205e245c97622924271995db373d1e","src/back/wgsl/mod.rs":"2dd12bbea9ace835850192bb68c5760953da6bac6a636073d1eca19381c0c0b6","src/back/wgsl/writer.rs":"96795df390cffade8ca27baea88ab29592d7e425c4351a9e2591d4f4ecdc73f3","src/block.rs":"c69089e5bbb6de6ba24efb15b21d5d434fcabfbc4d48feae948d2a4da135aae7","src/compact/expressions.rs":"7a4c916282a5b484519ed29ab451c7b595d8dea73c83c5c2cf7efc6fbc648fda","src/compact/functions.rs":"174bd9167ecf6353afb8c36d365ba3f9b483233eb4bacf578e50183c7433aa15","src/compact/handle_set_map.rs":"817c5193352d5fd6a61a5c970daba23224e14a65aea15f8f1c8679c99f834ca2","src/compact/mod.rs":"f1a606e8732f3c5837ab40ba5569eb1687336ef412f7f4b6cc348dd52b8076b3","src/compact/statements.rs":"4df33ee9589300e769e75c674bdc30578e93704ec3eb2aabc7132121745b55c8","src/compact/types.rs":"18343f2ca2c123eea2531cffc1d54a7798797caccecaa1f9b8c4fd5dd6ca1a05","src/front/glsl/ast.rs":"a4615f0c52b0dc9fdb07f816b4534c1ca547c2d176008ca86d66f9e6874f227d","src/front/glsl/builtins.rs":"d35501d5b42b61c261da24436b82eafdf96371b1600d148648d90d041f736ae4","src/front/glsl/context.rs":"066203c24ff5bc6154aa671f4492b5e8dfede8b57ef886f093cc95470d66411b","src/front/glsl/error.rs":"cca4a3aa9de2808952ff68c183755df5fdf6a7cb81f170ba747795176c0342fd","src/front/glsl/functions.rs":"b420be6b54195e9cdabdf76bb854e3e1f3be6542c6c129656fd0b1bd900dcebd","src/front/glsl/lex.rs":"08736ae8beb955da5b0e6e3e0f45995a824995f7096d516a2910417e9c7afa32","src/front/glsl/mod.rs":"c6e81710ae94a52583ba6f2a80a505d6bcd6ea6552008b80b27539af48838df1","src/front/glsl/offset.rs":"9358602ca4f9ef21d5066d674dae757bf88fdf5c289c4360534354d13bd41dc0","src/front/glsl/parser.rs":"fe5291512db412b33b6c09d5b3dcf7c54ff6ec55b47f0a078dcc11695e78471d","src/front/glsl/parser/declarations.rs":"d637cc52e553910a2e97b70b3366c15aefbe737f413adb11c27efd184c1fbf9d","src/front/glsl/parser/expressions.rs":"520cfc9402d5fbd48e52ef1d36562c6b74794c09ec33ec1ebb10aa48d129b66f","src/front/glsl/parser/functions.rs":"75aedcea4133bc4aba06ef49b1697eac96cc28d191e9830689fc4a6c0c4856eb","src/front/glsl/parser/types.rs":"aeb97e1a5fb03205cd5630c29da59d81a376ce9a83a603b62b037e63ad948e88","src/front/glsl/parser_tests.rs":"bfd4dff2580f4369a57edbcace47d23e2666751ffc3ab55f8d7dfe01f1a66311","src/front/glsl/token.rs":"c25c489b152ee2d445ace3c2046473abe64d558b8d27fa08709110e58718b6ac","src/front/glsl/types.rs":"58c9cf3d570dff8cb68f2931faf5b18e875e510741bf035ec10b9ff6df27c5d8","src/front/glsl/variables.rs":"fb2a09e386b6e98ca9fb8fb744afac1e8b19d1b67c6ede5c474e3ba860d3d4cb","src/front/interpolator.rs":"9b6ca498d5fbd9bc1515510a04e303a00b324121d7285da3c955cfe18eb4224c","src/front/mod.rs":"77acd7fb71d004969d1ee69fc728647f03242762988786c4e15fadf8315600af","src/front/spv/convert.rs":"dccc6671e6a4a7f1139aecdf979faa3689609081af5fa2cbbd6a2e8c4128c004","src/front/spv/error.rs":"6438aac57cfcf5d3858dd7652ccda1967a3123c6374f1cab829092b00549f70f","src/front/spv/function.rs":"1acb7bdd34ecfe08c6f4b4d06c2a0ea74aaf9975352e8804e3e4fab90745132f","src/front/spv/image.rs":"5d55cfbf6752732a594114cd09a9a207216e1ee85d8f2c9bc4310217a55ea321","src/front/spv/mod.rs":"22d0de7c43c42279e788144ff806cadfe3a3ea7923d961d11740af22492c4087","src/front/spv/null.rs":"e1446d99d04c76a9c3bbd24dd9b20c4711ce8a918a9b403be6cccbde1175b3b4","src/front/type_gen.rs":"b4f1df23380e06c9fdad4140810ce96ab041dbb1d371a07045b4e0069aa8ba55","src/front/wgsl/error.rs":"e1efd61062a5eb5f7e0413dc05d17abdbe0679c08f2fbdb7478e2b6e8dd13b25","src/front/wgsl/index.rs":"2b9a4929a46bd822d3ed6f9a150e24d437e5bdca8293eb748aebe80ce7e74153","src/front/wgsl/lower/construction.rs":"92342e27f5bdeb598e178799b74aa610788549c19a49fe0ae8914916bfa3c7be","src/front/wgsl/lower/conversion.rs":"961b19bf8ddd4667c6caf854a1889f3d6477757f4125538c3e9ca7d730975dd7","src/front/wgsl/lower/mod.rs":"08eece7a5460e414e2f8398cec96f12a8b9f6a457270426d8e4f045b62290d1f","src/front/wgsl/mod.rs":"02b194a0a29ef7281f71b424564e18ada4a8b1a0d8c26ec40b6be195bd4c4904","src/front/wgsl/parse/ast.rs":"c7eaae40179f0889f2b142d3b31968cbfab6d3cfe02e425912c6da8dadac51df","src/front/wgsl/parse/conv.rs":"9b2a06b5fd577e1881b2212e1d675d3aefe4d1fee99a17b5f7b07c36913e8186","src/front/wgsl/parse/lexer.rs":"17db87d0017f8f9a80fa151b8545f04e1b40c4e5feef6197f4a117efa03488bf","src/front/wgsl/parse/mod.rs":"3b4895a2baf91c719b95f0afb6441ffac2036c2a9ff817e633882fd257afcc38","src/front/wgsl/parse/number.rs":"dafd3d8651cfa1389cb359d76d39bd689e54f8d5025aa23e06c6edd871369efd","src/front/wgsl/tests.rs":"7a0a083a5b66af8e7d4b1a02401b27f077eb72d07181b610693f35b11f107c6c","src/front/wgsl/to_wgsl.rs":"2e2e30d86b07f209b866e530d3a882803bf28b39ce379052561a749f628e8e28","src/keywords/mod.rs":"0138f3931f8af0b0a05174549d0fd2152945b027dc3febefc1bbd676581d2e45","src/keywords/wgsl.rs":"c648ac44241ad55c8c8bad3d8f1bab973d11ddb9c380dcca369b735ed3975309","src/lib.rs":"f2072172957699d4282f247c452d8d8f0a0da08b9d78b279ee010296669d28d8","src/proc/constant_evaluator.rs":"bea5d259dbc4d9f9dacf3717dcb17a0774a22f1b3e5251b7e5b6991330ed3057","src/proc/emitter.rs":"39ac886c651e2ad33c06a676a7e4826a0e93de0af660c01e8e4b1f7406742f88","src/proc/index.rs":"f4250f6944c2b631e8140979024e8deb86fa8d5352d8641ba954a388b2c0940e","src/proc/layouter.rs":"b3d061c87424f36981c902716f37ab7b72f2bb2d0c2d7e900c51149318ea1a0a","src/proc/mod.rs":"4be5dcb137147cd8182a291f90959c46f1681c2d2c7da9e63f702a5f84c8809d","src/proc/namer.rs":"7328fac41e40890c64c7ee2fa985a4395424f18b08d30f30ca2583fdabd2fd35","src/proc/terminator.rs":"13c59bf00f5b26171d971effc421091f5e00dedddd246c2daa44fe65aeda060a","src/proc/typifier.rs":"99de19270d01c12ec49d14323aa1d9b8774f1ee715804af7235deff70739ba3d","src/span.rs":"6560599f20b8bc2de746ee9fd6b05c32bb630af914fce8845d84fdc72f9a636c","src/valid/analyzer.rs":"8472b98f16a4a4a0fa7079197db25696f77ef3e1602a7cddea1930daebd27917","src/valid/compose.rs":"83e4c09c39f853cf085b83b87e48b3db571da619132960d3ec954ebdfb0a74f2","src/valid/expression.rs":"7a8d5f74677c627dee3e15d223e83453ea7f6567dc806fcdfeebd32081012779","src/valid/function.rs":"40754e51906b053becdd8813b189fe709b7693c08babd28b5d3f5c576475b171","src/valid/handles.rs":"0878915e67b16d7c41cf8245d9ab3b3f4a604e7d4e87527ea40e03efcbf1f74a","src/valid/interface.rs":"32ef8e4665106b5c71540833e17ee9cd1dde5a900c9b81f61e0b7b8192c4aaf2","src/valid/mod.rs":"3b2772c88561aeb4dc8f5ce0d8ed5169bcdf5f7db04a62aaf22d04c171cb4f35","src/valid/type.rs":"61357577fa2dffa9c7326504f5c1a5fe7c44afd5d6f439c2354b390c6783fc86"},"package":null} \ No newline at end of file
diff --git a/third_party/rust/naga/Cargo.toml b/third_party/rust/naga/Cargo.toml
index 5fa521a779..dc2434e4b0 100644
--- a/third_party/rust/naga/Cargo.toml
+++ b/third_party/rust/naga/Cargo.toml
@@ -52,7 +52,7 @@ bitflags = "2.4"
log = "0.4"
num-traits = "0.2"
rustc-hash = "1.1.0"
-thiserror = "1.0.56"
+thiserror = "1.0.57"
[dependencies.arbitrary]
version = "1.3"
@@ -79,7 +79,7 @@ version = "0.2.1"
optional = true
[dependencies.serde]
-version = "1.0.195"
+version = "1.0.196"
features = ["derive"]
optional = true
diff --git a/third_party/rust/naga/src/back/glsl/features.rs b/third_party/rust/naga/src/back/glsl/features.rs
index e7de05f695..99c128c6d9 100644
--- a/third_party/rust/naga/src/back/glsl/features.rs
+++ b/third_party/rust/naga/src/back/glsl/features.rs
@@ -1,8 +1,8 @@
use super::{BackendResult, Error, Version, Writer};
use crate::{
back::glsl::{Options, WriterFlags},
- AddressSpace, Binding, Expression, Handle, ImageClass, ImageDimension, Interpolation, Sampling,
- Scalar, ScalarKind, ShaderStage, StorageFormat, Type, TypeInner,
+ AddressSpace, Binding, Expression, Handle, ImageClass, ImageDimension, Interpolation,
+ SampleLevel, Sampling, Scalar, ScalarKind, ShaderStage, StorageFormat, Type, TypeInner,
};
use std::fmt::Write;
@@ -48,6 +48,8 @@ bitflags::bitflags! {
///
/// We can always support this, either through the language or a polyfill
const INSTANCE_INDEX = 1 << 22;
+ /// Sample specific LODs of cube / array shadow textures
+ const TEXTURE_SHADOW_LOD = 1 << 23;
}
}
@@ -125,6 +127,7 @@ impl FeaturesManager {
check_feature!(TEXTURE_SAMPLES, 150);
check_feature!(TEXTURE_LEVELS, 130);
check_feature!(IMAGE_SIZE, 430, 310);
+ check_feature!(TEXTURE_SHADOW_LOD, 200, 300);
// Return an error if there are missing features
if missing.is_empty() {
@@ -251,6 +254,11 @@ impl FeaturesManager {
}
}
+ if self.0.contains(Features::TEXTURE_SHADOW_LOD) {
+ // https://registry.khronos.org/OpenGL/extensions/EXT/EXT_texture_shadow_lod.txt
+ writeln!(out, "#extension GL_EXT_texture_shadow_lod : require")?;
+ }
+
Ok(())
}
}
@@ -469,6 +477,47 @@ impl<'a, W> Writer<'a, W> {
}
}
}
+ Expression::ImageSample { image, level, offset, .. } => {
+ if let TypeInner::Image {
+ dim,
+ arrayed,
+ class: ImageClass::Depth { .. },
+ } = *info[image].ty.inner_with(&module.types) {
+ let lod = matches!(level, SampleLevel::Zero | SampleLevel::Exact(_));
+ let bias = matches!(level, SampleLevel::Bias(_));
+ let auto = matches!(level, SampleLevel::Auto);
+ let cube = dim == ImageDimension::Cube;
+ let array2d = dim == ImageDimension::D2 && arrayed;
+ let gles = self.options.version.is_es();
+
+ // We have a workaround of using `textureGrad` instead of `textureLod` if the LOD is zero,
+ // so we don't *need* this extension for those cases.
+ // But if we're explicitly allowed to use the extension (`WriterFlags::TEXTURE_SHADOW_LOD`),
+ // we always use it instead of the workaround.
+ let grad_workaround_applicable = (array2d || (cube && !arrayed)) && level == SampleLevel::Zero;
+ let prefer_grad_workaround = grad_workaround_applicable && !self.options.writer_flags.contains(WriterFlags::TEXTURE_SHADOW_LOD);
+
+ let mut ext_used = false;
+
+ // float texture(sampler2DArrayShadow sampler, vec4 P [, float bias])
+ // float texture(samplerCubeArrayShadow sampler, vec4 P, float compare [, float bias])
+ ext_used |= (array2d || cube && arrayed) && bias;
+
+ // The non `bias` version of this was standardized in GL 4.3, but never in GLES.
+ // float textureOffset(sampler2DArrayShadow sampler, vec4 P, ivec2 offset [, float bias])
+ ext_used |= array2d && (bias || (gles && auto)) && offset.is_some();
+
+ // float textureLod(sampler2DArrayShadow sampler, vec4 P, float lod)
+ // float textureLodOffset(sampler2DArrayShadow sampler, vec4 P, float lod, ivec2 offset)
+ // float textureLod(samplerCubeShadow sampler, vec4 P, float lod)
+ // float textureLod(samplerCubeArrayShadow sampler, vec4 P, float compare, float lod)
+ ext_used |= (cube || array2d) && lod && !prefer_grad_workaround;
+
+ if ext_used {
+ features.request(Features::TEXTURE_SHADOW_LOD);
+ }
+ }
+ }
_ => {}
}
}
diff --git a/third_party/rust/naga/src/back/glsl/mod.rs b/third_party/rust/naga/src/back/glsl/mod.rs
index e346d43257..9bda594610 100644
--- a/third_party/rust/naga/src/back/glsl/mod.rs
+++ b/third_party/rust/naga/src/back/glsl/mod.rs
@@ -178,7 +178,7 @@ impl Version {
/// Note: `location=` for vertex inputs and fragment outputs is supported
/// unconditionally for GLES 300.
fn supports_explicit_locations(&self) -> bool {
- *self >= Version::Desktop(410) || *self >= Version::new_gles(310)
+ *self >= Version::Desktop(420) || *self >= Version::new_gles(310)
}
fn supports_early_depth_test(&self) -> bool {
@@ -646,16 +646,6 @@ impl<'a, W: Write> Writer<'a, W> {
// preprocessor not the processor ¯\_(ツ)_/¯
self.features.write(self.options, &mut self.out)?;
- // Write the additional extensions
- if self
- .options
- .writer_flags
- .contains(WriterFlags::TEXTURE_SHADOW_LOD)
- {
- // https://www.khronos.org/registry/OpenGL/extensions/EXT/EXT_texture_shadow_lod.txt
- writeln!(self.out, "#extension GL_EXT_texture_shadow_lod : require")?;
- }
-
// glsl es requires a precision to be specified for floats and ints
// TODO: Should this be user configurable?
if es {
@@ -1300,7 +1290,14 @@ impl<'a, W: Write> Writer<'a, W> {
let inner = expr_info.ty.inner_with(&self.module.types);
- if let Expression::Math { fun, arg, arg1, .. } = *expr {
+ if let Expression::Math {
+ fun,
+ arg,
+ arg1,
+ arg2,
+ ..
+ } = *expr
+ {
match fun {
crate::MathFunction::Dot => {
// if the expression is a Dot product with integer arguments,
@@ -1315,6 +1312,14 @@ impl<'a, W: Write> Writer<'a, W> {
}
}
}
+ crate::MathFunction::ExtractBits => {
+ // Only argument 1 is re-used.
+ self.need_bake_expressions.insert(arg1.unwrap());
+ }
+ crate::MathFunction::InsertBits => {
+ // Only argument 2 is re-used.
+ self.need_bake_expressions.insert(arg2.unwrap());
+ }
crate::MathFunction::CountLeadingZeros => {
if let Some(crate::ScalarKind::Sint) = inner.scalar_kind() {
self.need_bake_expressions.insert(arg);
@@ -2451,6 +2456,9 @@ impl<'a, W: Write> Writer<'a, W> {
crate::Literal::I64(_) => {
return Err(Error::Custom("GLSL has no 64-bit integer type".into()));
}
+ crate::Literal::U64(_) => {
+ return Err(Error::Custom("GLSL has no 64-bit integer type".into()));
+ }
crate::Literal::AbstractInt(_) | crate::Literal::AbstractFloat(_) => {
return Err(Error::Custom(
"Abstract types should not appear in IR presented to backends".into(),
@@ -2620,51 +2628,49 @@ impl<'a, W: Write> Writer<'a, W> {
level,
depth_ref,
} => {
- let dim = match *ctx.resolve_type(image, &self.module.types) {
- TypeInner::Image { dim, .. } => dim,
+ let (dim, class, arrayed) = match *ctx.resolve_type(image, &self.module.types) {
+ TypeInner::Image {
+ dim,
+ class,
+ arrayed,
+ ..
+ } => (dim, class, arrayed),
_ => unreachable!(),
};
-
- if dim == crate::ImageDimension::Cube
- && array_index.is_some()
- && depth_ref.is_some()
- {
- match level {
- crate::SampleLevel::Zero
- | crate::SampleLevel::Exact(_)
- | crate::SampleLevel::Gradient { .. }
- | crate::SampleLevel::Bias(_) => {
- return Err(Error::Custom(String::from(
- "gsamplerCubeArrayShadow isn't supported in textureGrad, \
- textureLod or texture with bias",
- )))
- }
- crate::SampleLevel::Auto => {}
+ let mut err = None;
+ if dim == crate::ImageDimension::Cube {
+ if offset.is_some() {
+ err = Some("gsamplerCube[Array][Shadow] doesn't support texture sampling with offsets");
+ }
+ if arrayed
+ && matches!(class, crate::ImageClass::Depth { .. })
+ && matches!(level, crate::SampleLevel::Gradient { .. })
+ {
+ err = Some("samplerCubeArrayShadow don't support textureGrad");
}
}
+ if gather.is_some() && level != crate::SampleLevel::Zero {
+ err = Some("textureGather doesn't support LOD parameters");
+ }
+ if let Some(err) = err {
+ return Err(Error::Custom(String::from(err)));
+ }
- // textureLod on sampler2DArrayShadow and samplerCubeShadow does not exist in GLSL.
- // To emulate this, we will have to use textureGrad with a constant gradient of 0.
- let workaround_lod_array_shadow_as_grad = (array_index.is_some()
- || dim == crate::ImageDimension::Cube)
- && depth_ref.is_some()
- && gather.is_none()
- && !self
- .options
- .writer_flags
- .contains(WriterFlags::TEXTURE_SHADOW_LOD);
-
- //Write the function to be used depending on the sample level
+ // `textureLod[Offset]` on `sampler2DArrayShadow` and `samplerCubeShadow` does not exist in GLSL,
+ // unless `GL_EXT_texture_shadow_lod` is present.
+ // But if the target LOD is zero, we can emulate that by using `textureGrad[Offset]` with a constant gradient of 0.
+ let workaround_lod_with_grad = ((dim == crate::ImageDimension::Cube && !arrayed)
+ || (dim == crate::ImageDimension::D2 && arrayed))
+ && level == crate::SampleLevel::Zero
+ && matches!(class, crate::ImageClass::Depth { .. })
+ && !self.features.contains(Features::TEXTURE_SHADOW_LOD);
+
+ // Write the function to be used depending on the sample level
let fun_name = match level {
crate::SampleLevel::Zero if gather.is_some() => "textureGather",
+ crate::SampleLevel::Zero if workaround_lod_with_grad => "textureGrad",
crate::SampleLevel::Auto | crate::SampleLevel::Bias(_) => "texture",
- crate::SampleLevel::Zero | crate::SampleLevel::Exact(_) => {
- if workaround_lod_array_shadow_as_grad {
- "textureGrad"
- } else {
- "textureLod"
- }
- }
+ crate::SampleLevel::Zero | crate::SampleLevel::Exact(_) => "textureLod",
crate::SampleLevel::Gradient { .. } => "textureGrad",
};
let offset_name = match offset {
@@ -2727,7 +2733,7 @@ impl<'a, W: Write> Writer<'a, W> {
crate::SampleLevel::Auto => (),
// Zero needs level set to 0
crate::SampleLevel::Zero => {
- if workaround_lod_array_shadow_as_grad {
+ if workaround_lod_with_grad {
let vec_dim = match dim {
crate::ImageDimension::Cube => 3,
_ => 2,
@@ -2739,13 +2745,8 @@ impl<'a, W: Write> Writer<'a, W> {
}
// Exact and bias require another argument
crate::SampleLevel::Exact(expr) => {
- if workaround_lod_array_shadow_as_grad {
- log::warn!("Unable to `textureLod` a shadow array, ignoring the LOD");
- write!(self.out, ", vec2(0,0), vec2(0,0)")?;
- } else {
- write!(self.out, ", ")?;
- self.write_expr(expr, ctx)?;
- }
+ write!(self.out, ", ")?;
+ self.write_expr(expr, ctx)?;
}
crate::SampleLevel::Bias(_) => {
// This needs to be done after the offset writing
@@ -3155,7 +3156,29 @@ impl<'a, W: Write> Writer<'a, W> {
Mf::Abs => "abs",
Mf::Min => "min",
Mf::Max => "max",
- Mf::Clamp => "clamp",
+ Mf::Clamp => {
+ let scalar_kind = ctx
+ .resolve_type(arg, &self.module.types)
+ .scalar_kind()
+ .unwrap();
+ match scalar_kind {
+ crate::ScalarKind::Float => "clamp",
+ // Clamp is undefined if min > max. In practice this means it can use a median-of-three
+ // instruction to determine the value. This is fine according to the WGSL spec for float
+ // clamp, but integer clamp _must_ use min-max. As such we write out min/max.
+ _ => {
+ write!(self.out, "min(max(")?;
+ self.write_expr(arg, ctx)?;
+ write!(self.out, ", ")?;
+ self.write_expr(arg1.unwrap(), ctx)?;
+ write!(self.out, "), ")?;
+ self.write_expr(arg2.unwrap(), ctx)?;
+ write!(self.out, ")")?;
+
+ return Ok(());
+ }
+ }
+ }
Mf::Saturate => {
write!(self.out, "clamp(")?;
@@ -3370,8 +3393,59 @@ impl<'a, W: Write> Writer<'a, W> {
}
Mf::CountOneBits => "bitCount",
Mf::ReverseBits => "bitfieldReverse",
- Mf::ExtractBits => "bitfieldExtract",
- Mf::InsertBits => "bitfieldInsert",
+ Mf::ExtractBits => {
+ // The behavior of ExtractBits is undefined when offset + count > bit_width. We need
+ // to first sanitize the offset and count first. If we don't do this, AMD and Intel chips
+ // will return out-of-spec values if the extracted range is not within the bit width.
+ //
+ // This encodes the exact formula specified by the wgsl spec, without temporary values:
+ // https://gpuweb.github.io/gpuweb/wgsl/#extractBits-unsigned-builtin
+ //
+ // w = sizeof(x) * 8
+ // o = min(offset, w)
+ // c = min(count, w - o)
+ //
+ // bitfieldExtract(x, o, c)
+ //
+ // extract_bits(e, min(offset, w), min(count, w - min(offset, w))))
+ let scalar_bits = ctx
+ .resolve_type(arg, &self.module.types)
+ .scalar_width()
+ .unwrap();
+
+ write!(self.out, "bitfieldExtract(")?;
+ self.write_expr(arg, ctx)?;
+ write!(self.out, ", int(min(")?;
+ self.write_expr(arg1.unwrap(), ctx)?;
+ write!(self.out, ", {scalar_bits}u)), int(min(",)?;
+ self.write_expr(arg2.unwrap(), ctx)?;
+ write!(self.out, ", {scalar_bits}u - min(")?;
+ self.write_expr(arg1.unwrap(), ctx)?;
+ write!(self.out, ", {scalar_bits}u))))")?;
+
+ return Ok(());
+ }
+ Mf::InsertBits => {
+ // InsertBits has the same considerations as ExtractBits above
+ let scalar_bits = ctx
+ .resolve_type(arg, &self.module.types)
+ .scalar_width()
+ .unwrap();
+
+ write!(self.out, "bitfieldInsert(")?;
+ self.write_expr(arg, ctx)?;
+ write!(self.out, ", ")?;
+ self.write_expr(arg1.unwrap(), ctx)?;
+ write!(self.out, ", int(min(")?;
+ self.write_expr(arg2.unwrap(), ctx)?;
+ write!(self.out, ", {scalar_bits}u)), int(min(",)?;
+ self.write_expr(arg3.unwrap(), ctx)?;
+ write!(self.out, ", {scalar_bits}u - min(")?;
+ self.write_expr(arg2.unwrap(), ctx)?;
+ write!(self.out, ", {scalar_bits}u))))")?;
+
+ return Ok(());
+ }
Mf::FindLsb => "findLSB",
Mf::FindMsb => "findMSB",
// data packing
diff --git a/third_party/rust/naga/src/back/hlsl/conv.rs b/third_party/rust/naga/src/back/hlsl/conv.rs
index b6918ddc42..2a6db35db8 100644
--- a/third_party/rust/naga/src/back/hlsl/conv.rs
+++ b/third_party/rust/naga/src/back/hlsl/conv.rs
@@ -21,8 +21,16 @@ impl crate::Scalar {
/// <https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-scalar>
pub(super) const fn to_hlsl_str(self) -> Result<&'static str, Error> {
match self.kind {
- crate::ScalarKind::Sint => Ok("int"),
- crate::ScalarKind::Uint => Ok("uint"),
+ crate::ScalarKind::Sint => match self.width {
+ 4 => Ok("int"),
+ 8 => Ok("int64_t"),
+ _ => Err(Error::UnsupportedScalar(self)),
+ },
+ crate::ScalarKind::Uint => match self.width {
+ 4 => Ok("uint"),
+ 8 => Ok("uint64_t"),
+ _ => Err(Error::UnsupportedScalar(self)),
+ },
crate::ScalarKind::Float => match self.width {
2 => Ok("half"),
4 => Ok("float"),
diff --git a/third_party/rust/naga/src/back/hlsl/help.rs b/third_party/rust/naga/src/back/hlsl/help.rs
index fa6062a1ad..4dd9ea5987 100644
--- a/third_party/rust/naga/src/back/hlsl/help.rs
+++ b/third_party/rust/naga/src/back/hlsl/help.rs
@@ -26,7 +26,11 @@ int dim_1d = NagaDimensions1D(image_1d);
```
*/
-use super::{super::FunctionCtx, BackendResult};
+use super::{
+ super::FunctionCtx,
+ writer::{EXTRACT_BITS_FUNCTION, INSERT_BITS_FUNCTION},
+ BackendResult,
+};
use crate::{arena::Handle, proc::NameKey};
use std::fmt::Write;
@@ -59,6 +63,13 @@ pub(super) struct WrappedMatCx2 {
pub(super) columns: crate::VectorSize,
}
+#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
+pub(super) struct WrappedMath {
+ pub(super) fun: crate::MathFunction,
+ pub(super) scalar: crate::Scalar,
+ pub(super) components: Option<u32>,
+}
+
/// HLSL backend requires its own `ImageQuery` enum.
///
/// It is used inside `WrappedImageQuery` and should be unique per ImageQuery function.
@@ -851,12 +862,149 @@ impl<'a, W: Write> super::Writer<'a, W> {
Ok(())
}
+ pub(super) fn write_wrapped_math_functions(
+ &mut self,
+ module: &crate::Module,
+ func_ctx: &FunctionCtx,
+ ) -> BackendResult {
+ for (_, expression) in func_ctx.expressions.iter() {
+ if let crate::Expression::Math {
+ fun,
+ arg,
+ arg1: _arg1,
+ arg2: _arg2,
+ arg3: _arg3,
+ } = *expression
+ {
+ match fun {
+ crate::MathFunction::ExtractBits => {
+ // The behavior of our extractBits polyfill is undefined if offset + count > bit_width. We need
+ // to first sanitize the offset and count first. If we don't do this, we will get out-of-spec
+ // values if the extracted range is not within the bit width.
+ //
+ // This encodes the exact formula specified by the wgsl spec:
+ // https://gpuweb.github.io/gpuweb/wgsl/#extractBits-unsigned-builtin
+ //
+ // w = sizeof(x) * 8
+ // o = min(offset, w)
+ // c = min(count, w - o)
+ //
+ // bitfieldExtract(x, o, c)
+ let arg_ty = func_ctx.resolve_type(arg, &module.types);
+ let scalar = arg_ty.scalar().unwrap();
+ let components = arg_ty.components();
+
+ let wrapped = WrappedMath {
+ fun,
+ scalar,
+ components,
+ };
+
+ if !self.wrapped.math.insert(wrapped) {
+ continue;
+ }
+
+ // Write return type
+ self.write_value_type(module, arg_ty)?;
+
+ let scalar_width: u8 = scalar.width * 8;
+
+ // Write function name and parameters
+ writeln!(self.out, " {EXTRACT_BITS_FUNCTION}(")?;
+ write!(self.out, " ")?;
+ self.write_value_type(module, arg_ty)?;
+ writeln!(self.out, " e,")?;
+ writeln!(self.out, " uint offset,")?;
+ writeln!(self.out, " uint count")?;
+ writeln!(self.out, ") {{")?;
+
+ // Write function body
+ writeln!(self.out, " uint w = {scalar_width};")?;
+ writeln!(self.out, " uint o = min(offset, w);")?;
+ writeln!(self.out, " uint c = min(count, w - o);")?;
+ writeln!(
+ self.out,
+ " return (c == 0 ? 0 : (e << (w - c - o)) >> (w - c));"
+ )?;
+
+ // End of function body
+ writeln!(self.out, "}}")?;
+ }
+ crate::MathFunction::InsertBits => {
+ // The behavior of our insertBits polyfill has the same constraints as the extractBits polyfill.
+
+ let arg_ty = func_ctx.resolve_type(arg, &module.types);
+ let scalar = arg_ty.scalar().unwrap();
+ let components = arg_ty.components();
+
+ let wrapped = WrappedMath {
+ fun,
+ scalar,
+ components,
+ };
+
+ if !self.wrapped.math.insert(wrapped) {
+ continue;
+ }
+
+ // Write return type
+ self.write_value_type(module, arg_ty)?;
+
+ let scalar_width: u8 = scalar.width * 8;
+ let scalar_max: u64 = match scalar.width {
+ 1 => 0xFF,
+ 2 => 0xFFFF,
+ 4 => 0xFFFFFFFF,
+ 8 => 0xFFFFFFFFFFFFFFFF,
+ _ => unreachable!(),
+ };
+
+ // Write function name and parameters
+ writeln!(self.out, " {INSERT_BITS_FUNCTION}(")?;
+ write!(self.out, " ")?;
+ self.write_value_type(module, arg_ty)?;
+ writeln!(self.out, " e,")?;
+ write!(self.out, " ")?;
+ self.write_value_type(module, arg_ty)?;
+ writeln!(self.out, " newbits,")?;
+ writeln!(self.out, " uint offset,")?;
+ writeln!(self.out, " uint count")?;
+ writeln!(self.out, ") {{")?;
+
+ // Write function body
+ writeln!(self.out, " uint w = {scalar_width}u;")?;
+ writeln!(self.out, " uint o = min(offset, w);")?;
+ writeln!(self.out, " uint c = min(count, w - o);")?;
+
+ // The `u` suffix on the literals is _extremely_ important. Otherwise it will use
+ // i32 shifting instead of the intended u32 shifting.
+ writeln!(
+ self.out,
+ " uint mask = (({scalar_max}u >> ({scalar_width}u - c)) << o);"
+ )?;
+ writeln!(
+ self.out,
+ " return (c == 0 ? e : ((e & ~mask) | ((newbits << o) & mask)));"
+ )?;
+
+ // End of function body
+ writeln!(self.out, "}}")?;
+ }
+ _ => {}
+ }
+ }
+ }
+
+ Ok(())
+ }
+
/// Helper function that writes various wrapped functions
pub(super) fn write_wrapped_functions(
&mut self,
module: &crate::Module,
func_ctx: &FunctionCtx,
) -> BackendResult {
+ self.write_wrapped_math_functions(module, func_ctx)?;
self.write_wrapped_compose_functions(module, func_ctx.expressions)?;
for (handle, _) in func_ctx.expressions.iter() {
diff --git a/third_party/rust/naga/src/back/hlsl/keywords.rs b/third_party/rust/naga/src/back/hlsl/keywords.rs
index 059e533ff7..2cb715c42c 100644
--- a/third_party/rust/naga/src/back/hlsl/keywords.rs
+++ b/third_party/rust/naga/src/back/hlsl/keywords.rs
@@ -817,6 +817,8 @@ pub const RESERVED: &[&str] = &[
// Naga utilities
super::writer::MODF_FUNCTION,
super::writer::FREXP_FUNCTION,
+ super::writer::EXTRACT_BITS_FUNCTION,
+ super::writer::INSERT_BITS_FUNCTION,
];
// DXC scalar types, from https://github.com/microsoft/DirectXShaderCompiler/blob/18c9e114f9c314f93e68fbc72ce207d4ed2e65ae/tools/clang/lib/AST/ASTContextHLSL.cpp#L48-L254
diff --git a/third_party/rust/naga/src/back/hlsl/mod.rs b/third_party/rust/naga/src/back/hlsl/mod.rs
index 37ddbd3d67..f37a223f47 100644
--- a/third_party/rust/naga/src/back/hlsl/mod.rs
+++ b/third_party/rust/naga/src/back/hlsl/mod.rs
@@ -256,6 +256,7 @@ struct Wrapped {
constructors: crate::FastHashSet<help::WrappedConstructor>,
struct_matrix_access: crate::FastHashSet<help::WrappedStructMatrixAccess>,
mat_cx2s: crate::FastHashSet<help::WrappedMatCx2>,
+ math: crate::FastHashSet<help::WrappedMath>,
}
impl Wrapped {
@@ -265,6 +266,7 @@ impl Wrapped {
self.constructors.clear();
self.struct_matrix_access.clear();
self.mat_cx2s.clear();
+ self.math.clear();
}
}
diff --git a/third_party/rust/naga/src/back/hlsl/storage.rs b/third_party/rust/naga/src/back/hlsl/storage.rs
index 1b8a6ec12d..4d3a6af56d 100644
--- a/third_party/rust/naga/src/back/hlsl/storage.rs
+++ b/third_party/rust/naga/src/back/hlsl/storage.rs
@@ -32,6 +32,16 @@ The [`temp_access_chain`] field is a member of [`Writer`] solely to
allow re-use of the `Vec`'s dynamic allocation. Its value is no longer
needed once HLSL for the access has been generated.
+Note about DXC and Load/Store functions:
+
+DXC's HLSL has a generic [`Load` and `Store`] function for [`ByteAddressBuffer`] and
+[`RWByteAddressBuffer`]. This is not available in FXC's HLSL, so we use
+it only for types that are only available in DXC. Notably 64 and 16 bit types.
+
+FXC's HLSL has functions Load, Load2, Load3, and Load4 and Store, Store2, Store3, Store4.
+This loads/stores a vector of length 1, 2, 3, or 4. We use that for 32bit types, bitcasting to the
+correct type if necessary.
+
[`Storage`]: crate::AddressSpace::Storage
[`ByteAddressBuffer`]: https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/sm5-object-byteaddressbuffer
[`RWByteAddressBuffer`]: https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/sm5-object-rwbyteaddressbuffer
@@ -42,6 +52,7 @@ needed once HLSL for the access has been generated.
[`Writer::temp_access_chain`]: super::Writer::temp_access_chain
[`temp_access_chain`]: super::Writer::temp_access_chain
[`Writer`]: super::Writer
+[`Load` and `Store`]: https://github.com/microsoft/DirectXShaderCompiler/wiki/ByteAddressBuffer-Load-Store-Additions
*/
use super::{super::FunctionCtx, BackendResult, Error};
@@ -161,20 +172,39 @@ impl<W: fmt::Write> super::Writer<'_, W> {
// working around the borrow checker in `self.write_expr`
let chain = mem::take(&mut self.temp_access_chain);
let var_name = &self.names[&NameKey::GlobalVariable(var_handle)];
- let cast = scalar.kind.to_hlsl_cast();
- write!(self.out, "{cast}({var_name}.Load(")?;
+ // See note about DXC and Load/Store in the module's documentation.
+ if scalar.width == 4 {
+ let cast = scalar.kind.to_hlsl_cast();
+ write!(self.out, "{cast}({var_name}.Load(")?;
+ } else {
+ let ty = scalar.to_hlsl_str()?;
+ write!(self.out, "{var_name}.Load<{ty}>(")?;
+ };
self.write_storage_address(module, &chain, func_ctx)?;
- write!(self.out, "))")?;
+ write!(self.out, ")")?;
+ if scalar.width == 4 {
+ write!(self.out, ")")?;
+ }
self.temp_access_chain = chain;
}
crate::TypeInner::Vector { size, scalar } => {
// working around the borrow checker in `self.write_expr`
let chain = mem::take(&mut self.temp_access_chain);
let var_name = &self.names[&NameKey::GlobalVariable(var_handle)];
- let cast = scalar.kind.to_hlsl_cast();
- write!(self.out, "{}({}.Load{}(", cast, var_name, size as u8)?;
+ let size = size as u8;
+ // See note about DXC and Load/Store in the module's documentation.
+ if scalar.width == 4 {
+ let cast = scalar.kind.to_hlsl_cast();
+ write!(self.out, "{cast}({var_name}.Load{size}(")?;
+ } else {
+ let ty = scalar.to_hlsl_str()?;
+ write!(self.out, "{var_name}.Load<{ty}{size}>(")?;
+ };
self.write_storage_address(module, &chain, func_ctx)?;
- write!(self.out, "))")?;
+ write!(self.out, ")")?;
+ if scalar.width == 4 {
+ write!(self.out, ")")?;
+ }
self.temp_access_chain = chain;
}
crate::TypeInner::Matrix {
@@ -288,26 +318,44 @@ impl<W: fmt::Write> super::Writer<'_, W> {
}
};
match *ty_resolution.inner_with(&module.types) {
- crate::TypeInner::Scalar(_) => {
+ crate::TypeInner::Scalar(scalar) => {
// working around the borrow checker in `self.write_expr`
let chain = mem::take(&mut self.temp_access_chain);
let var_name = &self.names[&NameKey::GlobalVariable(var_handle)];
- write!(self.out, "{level}{var_name}.Store(")?;
- self.write_storage_address(module, &chain, func_ctx)?;
- write!(self.out, ", asuint(")?;
- self.write_store_value(module, &value, func_ctx)?;
- writeln!(self.out, "));")?;
+ // See note about DXC and Load/Store in the module's documentation.
+ if scalar.width == 4 {
+ write!(self.out, "{level}{var_name}.Store(")?;
+ self.write_storage_address(module, &chain, func_ctx)?;
+ write!(self.out, ", asuint(")?;
+ self.write_store_value(module, &value, func_ctx)?;
+ writeln!(self.out, "));")?;
+ } else {
+ write!(self.out, "{level}{var_name}.Store(")?;
+ self.write_storage_address(module, &chain, func_ctx)?;
+ write!(self.out, ", ")?;
+ self.write_store_value(module, &value, func_ctx)?;
+ writeln!(self.out, ");")?;
+ }
self.temp_access_chain = chain;
}
- crate::TypeInner::Vector { size, .. } => {
+ crate::TypeInner::Vector { size, scalar } => {
// working around the borrow checker in `self.write_expr`
let chain = mem::take(&mut self.temp_access_chain);
let var_name = &self.names[&NameKey::GlobalVariable(var_handle)];
- write!(self.out, "{}{}.Store{}(", level, var_name, size as u8)?;
- self.write_storage_address(module, &chain, func_ctx)?;
- write!(self.out, ", asuint(")?;
- self.write_store_value(module, &value, func_ctx)?;
- writeln!(self.out, "));")?;
+ // See note about DXC and Load/Store in the module's documentation.
+ if scalar.width == 4 {
+ write!(self.out, "{}{}.Store{}(", level, var_name, size as u8)?;
+ self.write_storage_address(module, &chain, func_ctx)?;
+ write!(self.out, ", asuint(")?;
+ self.write_store_value(module, &value, func_ctx)?;
+ writeln!(self.out, "));")?;
+ } else {
+ write!(self.out, "{}{}.Store(", level, var_name)?;
+ self.write_storage_address(module, &chain, func_ctx)?;
+ write!(self.out, ", ")?;
+ self.write_store_value(module, &value, func_ctx)?;
+ writeln!(self.out, ");")?;
+ }
self.temp_access_chain = chain;
}
crate::TypeInner::Matrix {
diff --git a/third_party/rust/naga/src/back/hlsl/writer.rs b/third_party/rust/naga/src/back/hlsl/writer.rs
index 43f7212837..4ba856946b 100644
--- a/third_party/rust/naga/src/back/hlsl/writer.rs
+++ b/third_party/rust/naga/src/back/hlsl/writer.rs
@@ -19,6 +19,8 @@ const SPECIAL_OTHER: &str = "other";
pub(crate) const MODF_FUNCTION: &str = "naga_modf";
pub(crate) const FREXP_FUNCTION: &str = "naga_frexp";
+pub(crate) const EXTRACT_BITS_FUNCTION: &str = "naga_extractBits";
+pub(crate) const INSERT_BITS_FUNCTION: &str = "naga_insertBits";
struct EpStructMember {
name: String,
@@ -125,14 +127,7 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
self.need_bake_expressions.insert(fun_handle);
}
- if let Expression::Math {
- fun,
- arg,
- arg1,
- arg2,
- arg3,
- } = *expr
- {
+ if let Expression::Math { fun, arg, .. } = *expr {
match fun {
crate::MathFunction::Asinh
| crate::MathFunction::Acosh
@@ -149,17 +144,6 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
| crate::MathFunction::Pack4x8unorm => {
self.need_bake_expressions.insert(arg);
}
- crate::MathFunction::ExtractBits => {
- self.need_bake_expressions.insert(arg);
- self.need_bake_expressions.insert(arg1.unwrap());
- self.need_bake_expressions.insert(arg2.unwrap());
- }
- crate::MathFunction::InsertBits => {
- self.need_bake_expressions.insert(arg);
- self.need_bake_expressions.insert(arg1.unwrap());
- self.need_bake_expressions.insert(arg2.unwrap());
- self.need_bake_expressions.insert(arg3.unwrap());
- }
crate::MathFunction::CountLeadingZeros => {
let inner = info[fun_handle].ty.inner_with(&module.types);
if let Some(crate::ScalarKind::Sint) = inner.scalar_kind() {
@@ -2038,6 +2022,7 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
crate::Literal::F32(value) => write!(self.out, "{value:?}")?,
crate::Literal::U32(value) => write!(self.out, "{}u", value)?,
crate::Literal::I32(value) => write!(self.out, "{}", value)?,
+ crate::Literal::U64(value) => write!(self.out, "{}uL", value)?,
crate::Literal::I64(value) => write!(self.out, "{}L", value)?,
crate::Literal::Bool(value) => write!(self.out, "{}", value)?,
crate::Literal::AbstractInt(_) | crate::Literal::AbstractFloat(_) => {
@@ -2567,7 +2552,7 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
convert,
} => {
let inner = func_ctx.resolve_type(expr, &module.types);
- match convert {
+ let close_paren = match convert {
Some(dst_width) => {
let scalar = crate::Scalar {
kind,
@@ -2600,13 +2585,21 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
)));
}
};
+ true
}
None => {
- write!(self.out, "{}(", kind.to_hlsl_cast(),)?;
+ if inner.scalar_width() == Some(64) {
+ false
+ } else {
+ write!(self.out, "{}(", kind.to_hlsl_cast(),)?;
+ true
+ }
}
- }
+ };
self.write_expr(module, expr, func_ctx)?;
- write!(self.out, ")")?;
+ if close_paren {
+ write!(self.out, ")")?;
+ }
}
Expression::Math {
fun,
@@ -2620,8 +2613,6 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
enum Function {
Asincosh { is_sin: bool },
Atanh,
- ExtractBits,
- InsertBits,
Pack2x16float,
Pack2x16snorm,
Pack2x16unorm,
@@ -2705,8 +2696,8 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
Mf::ReverseBits => Function::MissingIntOverload("reversebits"),
Mf::FindLsb => Function::MissingIntReturnType("firstbitlow"),
Mf::FindMsb => Function::MissingIntReturnType("firstbithigh"),
- Mf::ExtractBits => Function::ExtractBits,
- Mf::InsertBits => Function::InsertBits,
+ Mf::ExtractBits => Function::Regular(EXTRACT_BITS_FUNCTION),
+ Mf::InsertBits => Function::Regular(INSERT_BITS_FUNCTION),
// Data Packing
Mf::Pack2x16float => Function::Pack2x16float,
Mf::Pack2x16snorm => Function::Pack2x16snorm,
@@ -2742,70 +2733,6 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
self.write_expr(module, arg, func_ctx)?;
write!(self.out, "))")?;
}
- Function::ExtractBits => {
- // e: T,
- // offset: u32,
- // count: u32
- // T is u32 or i32 or vecN<u32> or vecN<i32>
- if let (Some(offset), Some(count)) = (arg1, arg2) {
- let scalar_width: u8 = 32;
- // Works for signed and unsigned
- // (count == 0 ? 0 : (e << (32 - count - offset)) >> (32 - count))
- write!(self.out, "(")?;
- self.write_expr(module, count, func_ctx)?;
- write!(self.out, " == 0 ? 0 : (")?;
- self.write_expr(module, arg, func_ctx)?;
- write!(self.out, " << ({scalar_width} - ")?;
- self.write_expr(module, count, func_ctx)?;
- write!(self.out, " - ")?;
- self.write_expr(module, offset, func_ctx)?;
- write!(self.out, ")) >> ({scalar_width} - ")?;
- self.write_expr(module, count, func_ctx)?;
- write!(self.out, "))")?;
- }
- }
- Function::InsertBits => {
- // e: T,
- // newbits: T,
- // offset: u32,
- // count: u32
- // returns T
- // T is i32, u32, vecN<i32>, or vecN<u32>
- if let (Some(newbits), Some(offset), Some(count)) = (arg1, arg2, arg3) {
- let scalar_width: u8 = 32;
- let scalar_max: u32 = 0xFFFFFFFF;
- // mask = ((0xFFFFFFFFu >> (32 - count)) << offset)
- // (count == 0 ? e : ((e & ~mask) | ((newbits << offset) & mask)))
- write!(self.out, "(")?;
- self.write_expr(module, count, func_ctx)?;
- write!(self.out, " == 0 ? ")?;
- self.write_expr(module, arg, func_ctx)?;
- write!(self.out, " : ")?;
- write!(self.out, "(")?;
- self.write_expr(module, arg, func_ctx)?;
- write!(self.out, " & ~")?;
- // mask
- write!(self.out, "(({scalar_max}u >> ({scalar_width}u - ")?;
- self.write_expr(module, count, func_ctx)?;
- write!(self.out, ")) << ")?;
- self.write_expr(module, offset, func_ctx)?;
- write!(self.out, ")")?;
- // end mask
- write!(self.out, ") | ((")?;
- self.write_expr(module, newbits, func_ctx)?;
- write!(self.out, " << ")?;
- self.write_expr(module, offset, func_ctx)?;
- write!(self.out, ") & ")?;
- // // mask
- write!(self.out, "(({scalar_max}u >> ({scalar_width}u - ")?;
- self.write_expr(module, count, func_ctx)?;
- write!(self.out, ")) << ")?;
- self.write_expr(module, offset, func_ctx)?;
- write!(self.out, ")")?;
- // // end mask
- write!(self.out, "))")?;
- }
- }
Function::Pack2x16float => {
write!(self.out, "(f32tof16(")?;
self.write_expr(module, arg, func_ctx)?;
@@ -2944,9 +2871,15 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
}
write!(self.out, ")")?
}
+ // These overloads are only missing on FXC, so this is only needed for 32bit types,
+ // as non-32bit types are DXC only.
Function::MissingIntOverload(fun_name) => {
- let scalar_kind = func_ctx.resolve_type(arg, &module.types).scalar_kind();
- if let Some(ScalarKind::Sint) = scalar_kind {
+ let scalar_kind = func_ctx.resolve_type(arg, &module.types).scalar();
+ if let Some(crate::Scalar {
+ kind: ScalarKind::Sint,
+ width: 4,
+ }) = scalar_kind
+ {
write!(self.out, "asint({fun_name}(asuint(")?;
self.write_expr(module, arg, func_ctx)?;
write!(self.out, ")))")?;
@@ -2956,9 +2889,15 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
write!(self.out, ")")?;
}
}
+ // These overloads are only missing on FXC, so this is only needed for 32bit types,
+ // as non-32bit types are DXC only.
Function::MissingIntReturnType(fun_name) => {
- let scalar_kind = func_ctx.resolve_type(arg, &module.types).scalar_kind();
- if let Some(ScalarKind::Sint) = scalar_kind {
+ let scalar_kind = func_ctx.resolve_type(arg, &module.types).scalar();
+ if let Some(crate::Scalar {
+ kind: ScalarKind::Sint,
+ width: 4,
+ }) = scalar_kind
+ {
write!(self.out, "asint({fun_name}(")?;
self.write_expr(module, arg, func_ctx)?;
write!(self.out, "))")?;
@@ -2977,23 +2916,38 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
crate::VectorSize::Quad => ".xxxx",
};
- if let ScalarKind::Uint = scalar.kind {
- write!(self.out, "min((32u){s}, firstbitlow(")?;
+ let scalar_width_bits = scalar.width * 8;
+
+ if scalar.kind == ScalarKind::Uint || scalar.width != 4 {
+ write!(
+ self.out,
+ "min(({scalar_width_bits}u){s}, firstbitlow("
+ )?;
self.write_expr(module, arg, func_ctx)?;
write!(self.out, "))")?;
} else {
- write!(self.out, "asint(min((32u){s}, firstbitlow(")?;
+ // This is only needed for the FXC path, on 32bit signed integers.
+ write!(
+ self.out,
+ "asint(min(({scalar_width_bits}u){s}, firstbitlow("
+ )?;
self.write_expr(module, arg, func_ctx)?;
write!(self.out, ")))")?;
}
}
TypeInner::Scalar(scalar) => {
- if let ScalarKind::Uint = scalar.kind {
- write!(self.out, "min(32u, firstbitlow(")?;
+ let scalar_width_bits = scalar.width * 8;
+
+ if scalar.kind == ScalarKind::Uint || scalar.width != 4 {
+ write!(self.out, "min({scalar_width_bits}u, firstbitlow(")?;
self.write_expr(module, arg, func_ctx)?;
write!(self.out, "))")?;
} else {
- write!(self.out, "asint(min(32u, firstbitlow(")?;
+ // This is only needed for the FXC path, on 32bit signed integers.
+ write!(
+ self.out,
+ "asint(min({scalar_width_bits}u, firstbitlow("
+ )?;
self.write_expr(module, arg, func_ctx)?;
write!(self.out, ")))")?;
}
@@ -3012,30 +2966,47 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
crate::VectorSize::Quad => ".xxxx",
};
- if let ScalarKind::Uint = scalar.kind {
- write!(self.out, "((31u){s} - firstbithigh(")?;
+ // scalar width - 1
+ let constant = scalar.width * 8 - 1;
+
+ if scalar.kind == ScalarKind::Uint {
+ write!(self.out, "(({constant}u){s} - firstbithigh(")?;
self.write_expr(module, arg, func_ctx)?;
write!(self.out, "))")?;
} else {
+ let conversion_func = match scalar.width {
+ 4 => "asint",
+ _ => "",
+ };
write!(self.out, "(")?;
self.write_expr(module, arg, func_ctx)?;
write!(
self.out,
- " < (0){s} ? (0){s} : (31){s} - asint(firstbithigh("
+ " < (0){s} ? (0){s} : ({constant}){s} - {conversion_func}(firstbithigh("
)?;
self.write_expr(module, arg, func_ctx)?;
write!(self.out, ")))")?;
}
}
TypeInner::Scalar(scalar) => {
+ // scalar width - 1
+ let constant = scalar.width * 8 - 1;
+
if let ScalarKind::Uint = scalar.kind {
- write!(self.out, "(31u - firstbithigh(")?;
+ write!(self.out, "({constant}u - firstbithigh(")?;
self.write_expr(module, arg, func_ctx)?;
write!(self.out, "))")?;
} else {
+ let conversion_func = match scalar.width {
+ 4 => "asint",
+ _ => "",
+ };
write!(self.out, "(")?;
self.write_expr(module, arg, func_ctx)?;
- write!(self.out, " < 0 ? 0 : 31 - asint(firstbithigh(")?;
+ write!(
+ self.out,
+ " < 0 ? 0 : {constant} - {conversion_func}(firstbithigh("
+ )?;
self.write_expr(module, arg, func_ctx)?;
write!(self.out, ")))")?;
}
diff --git a/third_party/rust/naga/src/back/msl/keywords.rs b/third_party/rust/naga/src/back/msl/keywords.rs
index f0025bf239..73c457dd34 100644
--- a/third_party/rust/naga/src/back/msl/keywords.rs
+++ b/third_party/rust/naga/src/back/msl/keywords.rs
@@ -4,6 +4,8 @@
// C++ - Standard for Programming Language C++ (N4431)
// https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4431.pdf
pub const RESERVED: &[&str] = &[
+ // Undocumented
+ "assert", // found in https://github.com/gfx-rs/wgpu/issues/5347
// Standard for Programming Language C++ (N4431): 2.5 Alternative tokens
"and",
"bitor",
diff --git a/third_party/rust/naga/src/back/msl/mod.rs b/third_party/rust/naga/src/back/msl/mod.rs
index 5ef18730c9..68e5b79906 100644
--- a/third_party/rust/naga/src/back/msl/mod.rs
+++ b/third_party/rust/naga/src/back/msl/mod.rs
@@ -121,8 +121,8 @@ pub enum Error {
UnsupportedCall(String),
#[error("feature '{0}' is not implemented yet")]
FeatureNotImplemented(String),
- #[error("module is not valid")]
- Validation,
+ #[error("internal naga error: module should not have validated: {0}")]
+ GenericValidation(String),
#[error("BuiltIn {0:?} is not supported")]
UnsupportedBuiltIn(crate::BuiltIn),
#[error("capability {0:?} is not supported")]
@@ -306,13 +306,10 @@ impl Options {
},
})
}
- LocationMode::Uniform => {
- log::error!(
- "Unexpected Binding::Location({}) for the Uniform mode",
- location
- );
- Err(Error::Validation)
- }
+ LocationMode::Uniform => Err(Error::GenericValidation(format!(
+ "Unexpected Binding::Location({}) for the Uniform mode",
+ location
+ ))),
},
}
}
diff --git a/third_party/rust/naga/src/back/msl/writer.rs b/third_party/rust/naga/src/back/msl/writer.rs
index 1e496b5f50..5227d8e7db 100644
--- a/third_party/rust/naga/src/back/msl/writer.rs
+++ b/third_party/rust/naga/src/back/msl/writer.rs
@@ -319,7 +319,7 @@ pub struct Writer<W> {
}
impl crate::Scalar {
- const fn to_msl_name(self) -> &'static str {
+ fn to_msl_name(self) -> &'static str {
use crate::ScalarKind as Sk;
match self {
Self {
@@ -328,20 +328,29 @@ impl crate::Scalar {
} => "float",
Self {
kind: Sk::Sint,
- width: _,
+ width: 4,
} => "int",
Self {
kind: Sk::Uint,
- width: _,
+ width: 4,
} => "uint",
Self {
+ kind: Sk::Sint,
+ width: 8,
+ } => "long",
+ Self {
+ kind: Sk::Uint,
+ width: 8,
+ } => "ulong",
+ Self {
kind: Sk::Bool,
width: _,
} => "bool",
Self {
kind: Sk::AbstractInt | Sk::AbstractFloat,
width: _,
- } => unreachable!(),
+ } => unreachable!("Found Abstract scalar kind"),
+ _ => unreachable!("Unsupported scalar kind: {:?}", self),
}
}
}
@@ -735,7 +744,11 @@ impl<W: Write> Writer<W> {
crate::TypeInner::Vector { size, .. } => {
put_numeric_type(&mut self.out, crate::Scalar::U32, &[size])?
}
- _ => return Err(Error::Validation),
+ _ => {
+ return Err(Error::GenericValidation(
+ "Invalid type for image coordinate".into(),
+ ))
+ }
};
write!(self.out, "(")?;
@@ -1068,13 +1081,17 @@ impl<W: Write> Writer<W> {
let (offset, array_ty) = match context.module.types[global.ty].inner {
crate::TypeInner::Struct { ref members, .. } => match members.last() {
Some(&crate::StructMember { offset, ty, .. }) => (offset, ty),
- None => return Err(Error::Validation),
+ None => return Err(Error::GenericValidation("Struct has no members".into())),
},
crate::TypeInner::Array {
size: crate::ArraySize::Dynamic,
..
} => (0, global.ty),
- _ => return Err(Error::Validation),
+ ref ty => {
+ return Err(Error::GenericValidation(format!(
+ "Expected type with dynamic array, got {ty:?}"
+ )))
+ }
};
let (size, stride) = match context.module.types[array_ty].inner {
@@ -1084,7 +1101,11 @@ impl<W: Write> Writer<W> {
.size(context.module.to_ctx()),
stride,
),
- _ => return Err(Error::Validation),
+ ref ty => {
+ return Err(Error::GenericValidation(format!(
+ "Expected array type, got {ty:?}"
+ )))
+ }
};
// When the stride length is larger than the size, the final element's stride of
@@ -1273,6 +1294,9 @@ impl<W: Write> Writer<W> {
crate::Literal::I32(value) => {
write!(self.out, "{value}")?;
}
+ crate::Literal::U64(value) => {
+ write!(self.out, "{value}uL")?;
+ }
crate::Literal::I64(value) => {
write!(self.out, "{value}L")?;
}
@@ -1280,7 +1304,9 @@ impl<W: Write> Writer<W> {
write!(self.out, "{value}")?;
}
crate::Literal::AbstractInt(_) | crate::Literal::AbstractFloat(_) => {
- return Err(Error::Validation);
+ return Err(Error::GenericValidation(
+ "Unsupported abstract literal".into(),
+ ));
}
},
crate::Expression::Constant(handle) => {
@@ -1342,7 +1368,11 @@ impl<W: Write> Writer<W> {
crate::Expression::Splat { size, value } => {
let scalar = match *get_expr_ty(ctx, value).inner_with(&module.types) {
crate::TypeInner::Scalar(scalar) => scalar,
- _ => return Err(Error::Validation),
+ ref ty => {
+ return Err(Error::GenericValidation(format!(
+ "Expected splat value type must be a scalar, got {ty:?}",
+ )))
+ }
};
put_numeric_type(&mut self.out, scalar, &[size])?;
write!(self.out, "(")?;
@@ -1672,7 +1702,11 @@ impl<W: Write> Writer<W> {
self.put_expression(condition, context, true)?;
write!(self.out, ")")?;
}
- _ => return Err(Error::Validation),
+ ref ty => {
+ return Err(Error::GenericValidation(format!(
+ "Expected select condition to be a non-bool type, got {ty:?}",
+ )))
+ }
},
crate::Expression::Derivative { axis, expr, .. } => {
use crate::DerivativeAxis as Axis;
@@ -1794,8 +1828,8 @@ impl<W: Write> Writer<W> {
Mf::CountLeadingZeros => "clz",
Mf::CountOneBits => "popcount",
Mf::ReverseBits => "reverse_bits",
- Mf::ExtractBits => "extract_bits",
- Mf::InsertBits => "insert_bits",
+ Mf::ExtractBits => "",
+ Mf::InsertBits => "",
Mf::FindLsb => "",
Mf::FindMsb => "",
// data packing
@@ -1836,15 +1870,23 @@ impl<W: Write> Writer<W> {
self.put_expression(arg1.unwrap(), context, false)?;
write!(self.out, ")")?;
} else if fun == Mf::FindLsb {
+ let scalar = context.resolve_type(arg).scalar().unwrap();
+ let constant = scalar.width * 8 + 1;
+
write!(self.out, "((({NAMESPACE}::ctz(")?;
self.put_expression(arg, context, true)?;
- write!(self.out, ") + 1) % 33) - 1)")?;
+ write!(self.out, ") + 1) % {constant}) - 1)")?;
} else if fun == Mf::FindMsb {
let inner = context.resolve_type(arg);
+ let scalar = inner.scalar().unwrap();
+ let constant = scalar.width * 8 - 1;
- write!(self.out, "{NAMESPACE}::select(31 - {NAMESPACE}::clz(")?;
+ write!(
+ self.out,
+ "{NAMESPACE}::select({constant} - {NAMESPACE}::clz("
+ )?;
- if let Some(crate::ScalarKind::Sint) = inner.scalar_kind() {
+ if scalar.kind == crate::ScalarKind::Sint {
write!(self.out, "{NAMESPACE}::select(")?;
self.put_expression(arg, context, true)?;
write!(self.out, ", ~")?;
@@ -1862,18 +1904,12 @@ impl<W: Write> Writer<W> {
match *inner {
crate::TypeInner::Vector { size, scalar } => {
let size = back::vector_size_str(size);
- if let crate::ScalarKind::Sint = scalar.kind {
- write!(self.out, "int{size}")?;
- } else {
- write!(self.out, "uint{size}")?;
- }
+ let name = scalar.to_msl_name();
+ write!(self.out, "{name}{size}")?;
}
crate::TypeInner::Scalar(scalar) => {
- if let crate::ScalarKind::Sint = scalar.kind {
- write!(self.out, "int")?;
- } else {
- write!(self.out, "uint")?;
- }
+ let name = scalar.to_msl_name();
+ write!(self.out, "{name}")?;
}
_ => (),
}
@@ -1891,6 +1927,52 @@ impl<W: Write> Writer<W> {
write!(self.out, "as_type<uint>(half2(")?;
self.put_expression(arg, context, false)?;
write!(self.out, "))")?;
+ } else if fun == Mf::ExtractBits {
+ // The behavior of ExtractBits is undefined when offset + count > bit_width. We need
+ // to first sanitize the offset and count first. If we don't do this, Apple chips
+ // will return out-of-spec values if the extracted range is not within the bit width.
+ //
+ // This encodes the exact formula specified by the wgsl spec, without temporary values:
+ // https://gpuweb.github.io/gpuweb/wgsl/#extractBits-unsigned-builtin
+ //
+ // w = sizeof(x) * 8
+ // o = min(offset, w)
+ // tmp = w - o
+ // c = min(count, tmp)
+ //
+ // bitfieldExtract(x, o, c)
+ //
+ // extract_bits(e, min(offset, w), min(count, w - min(offset, w))))
+
+ let scalar_bits = context.resolve_type(arg).scalar_width().unwrap();
+
+ write!(self.out, "{NAMESPACE}::extract_bits(")?;
+ self.put_expression(arg, context, true)?;
+ write!(self.out, ", {NAMESPACE}::min(")?;
+ self.put_expression(arg1.unwrap(), context, true)?;
+ write!(self.out, ", {scalar_bits}u), {NAMESPACE}::min(")?;
+ self.put_expression(arg2.unwrap(), context, true)?;
+ write!(self.out, ", {scalar_bits}u - {NAMESPACE}::min(")?;
+ self.put_expression(arg1.unwrap(), context, true)?;
+ write!(self.out, ", {scalar_bits}u)))")?;
+ } else if fun == Mf::InsertBits {
+ // The behavior of InsertBits has the same issue as ExtractBits.
+ //
+ // insertBits(e, newBits, min(offset, w), min(count, w - min(offset, w))))
+
+ let scalar_bits = context.resolve_type(arg).scalar_width().unwrap();
+
+ write!(self.out, "{NAMESPACE}::insert_bits(")?;
+ self.put_expression(arg, context, true)?;
+ write!(self.out, ", ")?;
+ self.put_expression(arg1.unwrap(), context, true)?;
+ write!(self.out, ", {NAMESPACE}::min(")?;
+ self.put_expression(arg2.unwrap(), context, true)?;
+ write!(self.out, ", {scalar_bits}u), {NAMESPACE}::min(")?;
+ self.put_expression(arg3.unwrap(), context, true)?;
+ write!(self.out, ", {scalar_bits}u - {NAMESPACE}::min(")?;
+ self.put_expression(arg2.unwrap(), context, true)?;
+ write!(self.out, ", {scalar_bits}u)))")?;
} else if fun == Mf::Radians {
write!(self.out, "((")?;
self.put_expression(arg, context, false)?;
@@ -1920,14 +2002,8 @@ impl<W: Write> Writer<W> {
kind,
width: convert.unwrap_or(src.width),
};
- let is_bool_cast =
- kind == crate::ScalarKind::Bool || src.kind == crate::ScalarKind::Bool;
let op = match convert {
- Some(w) if w == src.width || is_bool_cast => "static_cast",
- Some(8) if kind == crate::ScalarKind::Float => {
- return Err(Error::CapabilityNotSupported(valid::Capabilities::FLOAT64))
- }
- Some(_) => return Err(Error::Validation),
+ Some(_) => "static_cast",
None => "as_type",
};
write!(self.out, "{op}<")?;
@@ -1955,7 +2031,11 @@ impl<W: Write> Writer<W> {
self.put_expression(expr, context, true)?;
write!(self.out, ")")?;
}
- _ => return Err(Error::Validation),
+ ref ty => {
+ return Err(Error::GenericValidation(format!(
+ "Unsupported type for As: {ty:?}"
+ )))
+ }
},
// has to be a named expression
crate::Expression::CallResult(_)
@@ -1970,11 +2050,19 @@ impl<W: Write> Writer<W> {
crate::Expression::AccessIndex { base, .. } => {
match context.function.expressions[base] {
crate::Expression::GlobalVariable(handle) => handle,
- _ => return Err(Error::Validation),
+ ref ex => {
+ return Err(Error::GenericValidation(format!(
+ "Expected global variable in AccessIndex, got {ex:?}"
+ )))
+ }
}
}
crate::Expression::GlobalVariable(handle) => handle,
- _ => return Err(Error::Validation),
+ ref ex => {
+ return Err(Error::GenericValidation(format!(
+ "Unexpected expression in ArrayLength, got {ex:?}"
+ )))
+ }
};
if !is_scoped {
@@ -2140,10 +2228,12 @@ impl<W: Write> Writer<W> {
match length {
index::IndexableLength::Known(value) => write!(self.out, "{value}")?,
index::IndexableLength::Dynamic => {
- let global = context
- .function
- .originating_global(base)
- .ok_or(Error::Validation)?;
+ let global =
+ context.function.originating_global(base).ok_or_else(|| {
+ Error::GenericValidation(
+ "Could not find originating global".into(),
+ )
+ })?;
write!(self.out, "1 + ")?;
self.put_dynamic_array_max_index(global, context)?
}
@@ -2300,10 +2390,9 @@ impl<W: Write> Writer<W> {
write!(self.out, "{}u", limit - 1)?;
}
index::IndexableLength::Dynamic => {
- let global = context
- .function
- .originating_global(base)
- .ok_or(Error::Validation)?;
+ let global = context.function.originating_global(base).ok_or_else(|| {
+ Error::GenericValidation("Could not find originating global".into())
+ })?;
self.put_dynamic_array_max_index(global, context)?;
}
}
@@ -2489,7 +2578,14 @@ impl<W: Write> Writer<W> {
}
}
- if let Expression::Math { fun, arg, arg1, .. } = *expr {
+ if let Expression::Math {
+ fun,
+ arg,
+ arg1,
+ arg2,
+ ..
+ } = *expr
+ {
match fun {
crate::MathFunction::Dot => {
// WGSL's `dot` function works on any `vecN` type, but Metal's only
@@ -2514,6 +2610,14 @@ impl<W: Write> Writer<W> {
crate::MathFunction::FindMsb => {
self.need_bake_expressions.insert(arg);
}
+ crate::MathFunction::ExtractBits => {
+ // Only argument 1 is re-used.
+ self.need_bake_expressions.insert(arg1.unwrap());
+ }
+ crate::MathFunction::InsertBits => {
+ // Only argument 2 is re-used.
+ self.need_bake_expressions.insert(arg2.unwrap());
+ }
crate::MathFunction::Sign => {
// WGSL's `sign` function works also on signed ints, but Metal's only
// works on floating points, so we emit inline code for integer `sign`
@@ -3048,7 +3152,7 @@ impl<W: Write> Writer<W> {
for statement in statements {
if let crate::Statement::Emit(ref range) = *statement {
for handle in range.clone() {
- self.named_expressions.remove(&handle);
+ self.named_expressions.shift_remove(&handle);
}
}
}
@@ -3897,7 +4001,9 @@ impl<W: Write> Writer<W> {
binding: None,
first_time: true,
};
- let binding = binding.ok_or(Error::Validation)?;
+ let binding = binding.ok_or_else(|| {
+ Error::GenericValidation("Expected binding, got None".into())
+ })?;
if let crate::Binding::BuiltIn(crate::BuiltIn::PointSize) = *binding {
has_point_size = true;
diff --git a/third_party/rust/naga/src/back/spv/block.rs b/third_party/rust/naga/src/back/spv/block.rs
index 6c96fa09e3..81f2fc10e0 100644
--- a/third_party/rust/naga/src/back/spv/block.rs
+++ b/third_party/rust/naga/src/back/spv/block.rs
@@ -731,12 +731,41 @@ impl<'w> BlockContext<'w> {
Some(crate::ScalarKind::Uint) => spirv::GLOp::UMax,
other => unimplemented!("Unexpected max({:?})", other),
}),
- Mf::Clamp => MathOp::Ext(match arg_scalar_kind {
- Some(crate::ScalarKind::Float) => spirv::GLOp::FClamp,
- Some(crate::ScalarKind::Sint) => spirv::GLOp::SClamp,
- Some(crate::ScalarKind::Uint) => spirv::GLOp::UClamp,
+ Mf::Clamp => match arg_scalar_kind {
+ // Clamp is undefined if min > max. In practice this means it can use a median-of-three
+ // instruction to determine the value. This is fine according to the WGSL spec for float
+ // clamp, but integer clamp _must_ use min-max. As such we write out min/max.
+ Some(crate::ScalarKind::Float) => MathOp::Ext(spirv::GLOp::FClamp),
+ Some(_) => {
+ let (min_op, max_op) = match arg_scalar_kind {
+ Some(crate::ScalarKind::Sint) => {
+ (spirv::GLOp::SMin, spirv::GLOp::SMax)
+ }
+ Some(crate::ScalarKind::Uint) => {
+ (spirv::GLOp::UMin, spirv::GLOp::UMax)
+ }
+ _ => unreachable!(),
+ };
+
+ let max_id = self.gen_id();
+ block.body.push(Instruction::ext_inst(
+ self.writer.gl450_ext_inst_id,
+ max_op,
+ result_type_id,
+ max_id,
+ &[arg0_id, arg1_id],
+ ));
+
+ MathOp::Custom(Instruction::ext_inst(
+ self.writer.gl450_ext_inst_id,
+ min_op,
+ result_type_id,
+ id,
+ &[max_id, arg2_id],
+ ))
+ }
other => unimplemented!("Unexpected max({:?})", other),
- }),
+ },
Mf::Saturate => {
let (maybe_size, scalar) = match *arg_ty {
crate::TypeInner::Vector { size, scalar } => (Some(size), scalar),
@@ -915,8 +944,7 @@ impl<'w> BlockContext<'w> {
)),
Mf::CountTrailingZeros => {
let uint_id = match *arg_ty {
- crate::TypeInner::Vector { size, mut scalar } => {
- scalar.kind = crate::ScalarKind::Uint;
+ crate::TypeInner::Vector { size, scalar } => {
let ty = LocalType::Value {
vector_size: Some(size),
scalar,
@@ -927,15 +955,15 @@ impl<'w> BlockContext<'w> {
self.temp_list.clear();
self.temp_list.resize(
size as _,
- self.writer.get_constant_scalar_with(32, scalar)?,
+ self.writer
+ .get_constant_scalar_with(scalar.width * 8, scalar)?,
);
self.writer.get_constant_composite(ty, &self.temp_list)
}
- crate::TypeInner::Scalar(mut scalar) => {
- scalar.kind = crate::ScalarKind::Uint;
- self.writer.get_constant_scalar_with(32, scalar)?
- }
+ crate::TypeInner::Scalar(scalar) => self
+ .writer
+ .get_constant_scalar_with(scalar.width * 8, scalar)?,
_ => unreachable!(),
};
@@ -957,9 +985,8 @@ impl<'w> BlockContext<'w> {
))
}
Mf::CountLeadingZeros => {
- let (int_type_id, int_id) = match *arg_ty {
- crate::TypeInner::Vector { size, mut scalar } => {
- scalar.kind = crate::ScalarKind::Sint;
+ let (int_type_id, int_id, width) = match *arg_ty {
+ crate::TypeInner::Vector { size, scalar } => {
let ty = LocalType::Value {
vector_size: Some(size),
scalar,
@@ -970,32 +997,41 @@ impl<'w> BlockContext<'w> {
self.temp_list.clear();
self.temp_list.resize(
size as _,
- self.writer.get_constant_scalar_with(31, scalar)?,
+ self.writer
+ .get_constant_scalar_with(scalar.width * 8 - 1, scalar)?,
);
(
self.get_type_id(ty),
self.writer.get_constant_composite(ty, &self.temp_list),
+ scalar.width,
)
}
- crate::TypeInner::Scalar(mut scalar) => {
- scalar.kind = crate::ScalarKind::Sint;
- (
- self.get_type_id(LookupType::Local(LocalType::Value {
- vector_size: None,
- scalar,
- pointer_space: None,
- })),
- self.writer.get_constant_scalar_with(31, scalar)?,
- )
- }
+ crate::TypeInner::Scalar(scalar) => (
+ self.get_type_id(LookupType::Local(LocalType::Value {
+ vector_size: None,
+ scalar,
+ pointer_space: None,
+ })),
+ self.writer
+ .get_constant_scalar_with(scalar.width * 8 - 1, scalar)?,
+ scalar.width,
+ ),
_ => unreachable!(),
};
+ if width != 4 {
+ unreachable!("This is validated out until a polyfill is implemented. https://github.com/gfx-rs/wgpu/issues/5276");
+ };
+
let msb_id = self.gen_id();
block.body.push(Instruction::ext_inst(
self.writer.gl450_ext_inst_id,
- spirv::GLOp::FindUMsb,
+ if width != 4 {
+ spirv::GLOp::FindILsb
+ } else {
+ spirv::GLOp::FindUMsb
+ },
int_type_id,
msb_id,
&[arg0_id],
@@ -1021,30 +1057,144 @@ impl<'w> BlockContext<'w> {
Some(crate::ScalarKind::Sint) => spirv::Op::BitFieldSExtract,
other => unimplemented!("Unexpected sign({:?})", other),
};
+
+ // The behavior of ExtractBits is undefined when offset + count > bit_width. We need
+ // to first sanitize the offset and count first. If we don't do this, AMD and Intel
+ // will return out-of-spec values if the extracted range is not within the bit width.
+ //
+ // This encodes the exact formula specified by the wgsl spec:
+ // https://gpuweb.github.io/gpuweb/wgsl/#extractBits-unsigned-builtin
+ //
+ // w = sizeof(x) * 8
+ // o = min(offset, w)
+ // tmp = w - o
+ // c = min(count, tmp)
+ //
+ // bitfieldExtract(x, o, c)
+
+ let bit_width = arg_ty.scalar_width().unwrap();
+ let width_constant = self
+ .writer
+ .get_constant_scalar(crate::Literal::U32(bit_width as u32));
+
+ let u32_type = self.get_type_id(LookupType::Local(LocalType::Value {
+ vector_size: None,
+ scalar: crate::Scalar {
+ kind: crate::ScalarKind::Uint,
+ width: 4,
+ },
+ pointer_space: None,
+ }));
+
+ // o = min(offset, w)
+ let offset_id = self.gen_id();
+ block.body.push(Instruction::ext_inst(
+ self.writer.gl450_ext_inst_id,
+ spirv::GLOp::UMin,
+ u32_type,
+ offset_id,
+ &[arg1_id, width_constant],
+ ));
+
+ // tmp = w - o
+ let max_count_id = self.gen_id();
+ block.body.push(Instruction::binary(
+ spirv::Op::ISub,
+ u32_type,
+ max_count_id,
+ width_constant,
+ offset_id,
+ ));
+
+ // c = min(count, tmp)
+ let count_id = self.gen_id();
+ block.body.push(Instruction::ext_inst(
+ self.writer.gl450_ext_inst_id,
+ spirv::GLOp::UMin,
+ u32_type,
+ count_id,
+ &[arg2_id, max_count_id],
+ ));
+
MathOp::Custom(Instruction::ternary(
op,
result_type_id,
id,
arg0_id,
+ offset_id,
+ count_id,
+ ))
+ }
+ Mf::InsertBits => {
+ // The behavior of InsertBits has the same undefined behavior as ExtractBits.
+
+ let bit_width = arg_ty.scalar_width().unwrap();
+ let width_constant = self
+ .writer
+ .get_constant_scalar(crate::Literal::U32(bit_width as u32));
+
+ let u32_type = self.get_type_id(LookupType::Local(LocalType::Value {
+ vector_size: None,
+ scalar: crate::Scalar {
+ kind: crate::ScalarKind::Uint,
+ width: 4,
+ },
+ pointer_space: None,
+ }));
+
+ // o = min(offset, w)
+ let offset_id = self.gen_id();
+ block.body.push(Instruction::ext_inst(
+ self.writer.gl450_ext_inst_id,
+ spirv::GLOp::UMin,
+ u32_type,
+ offset_id,
+ &[arg2_id, width_constant],
+ ));
+
+ // tmp = w - o
+ let max_count_id = self.gen_id();
+ block.body.push(Instruction::binary(
+ spirv::Op::ISub,
+ u32_type,
+ max_count_id,
+ width_constant,
+ offset_id,
+ ));
+
+ // c = min(count, tmp)
+ let count_id = self.gen_id();
+ block.body.push(Instruction::ext_inst(
+ self.writer.gl450_ext_inst_id,
+ spirv::GLOp::UMin,
+ u32_type,
+ count_id,
+ &[arg3_id, max_count_id],
+ ));
+
+ MathOp::Custom(Instruction::quaternary(
+ spirv::Op::BitFieldInsert,
+ result_type_id,
+ id,
+ arg0_id,
arg1_id,
- arg2_id,
+ offset_id,
+ count_id,
))
}
- Mf::InsertBits => MathOp::Custom(Instruction::quaternary(
- spirv::Op::BitFieldInsert,
- result_type_id,
- id,
- arg0_id,
- arg1_id,
- arg2_id,
- arg3_id,
- )),
Mf::FindLsb => MathOp::Ext(spirv::GLOp::FindILsb),
- Mf::FindMsb => MathOp::Ext(match arg_scalar_kind {
- Some(crate::ScalarKind::Uint) => spirv::GLOp::FindUMsb,
- Some(crate::ScalarKind::Sint) => spirv::GLOp::FindSMsb,
- other => unimplemented!("Unexpected findMSB({:?})", other),
- }),
+ Mf::FindMsb => {
+ if arg_ty.scalar_width() == Some(32) {
+ let thing = match arg_scalar_kind {
+ Some(crate::ScalarKind::Uint) => spirv::GLOp::FindUMsb,
+ Some(crate::ScalarKind::Sint) => spirv::GLOp::FindSMsb,
+ other => unimplemented!("Unexpected findMSB({:?})", other),
+ };
+ MathOp::Ext(thing)
+ } else {
+ unreachable!("This is validated out until a polyfill is implemented. https://github.com/gfx-rs/wgpu/issues/5276");
+ }
+ }
Mf::Pack4x8unorm => MathOp::Ext(spirv::GLOp::PackUnorm4x8),
Mf::Pack4x8snorm => MathOp::Ext(spirv::GLOp::PackSnorm4x8),
Mf::Pack2x16float => MathOp::Ext(spirv::GLOp::PackHalf2x16),
@@ -1250,6 +1400,12 @@ impl<'w> BlockContext<'w> {
(Sk::Uint, Sk::Uint, Some(dst_width)) if src_scalar.width != dst_width => {
Cast::Unary(spirv::Op::UConvert)
}
+ (Sk::Uint, Sk::Sint, Some(dst_width)) if src_scalar.width != dst_width => {
+ Cast::Unary(spirv::Op::SConvert)
+ }
+ (Sk::Sint, Sk::Uint, Some(dst_width)) if src_scalar.width != dst_width => {
+ Cast::Unary(spirv::Op::UConvert)
+ }
// We assume it's either an identity cast, or int-uint.
_ => Cast::Unary(spirv::Op::Bitcast),
}
diff --git a/third_party/rust/naga/src/back/spv/writer.rs b/third_party/rust/naga/src/back/spv/writer.rs
index 4db86c93a7..de3220bbda 100644
--- a/third_party/rust/naga/src/back/spv/writer.rs
+++ b/third_party/rust/naga/src/back/spv/writer.rs
@@ -1182,6 +1182,9 @@ impl Writer {
crate::Literal::F32(value) => Instruction::constant_32bit(type_id, id, value.to_bits()),
crate::Literal::U32(value) => Instruction::constant_32bit(type_id, id, value),
crate::Literal::I32(value) => Instruction::constant_32bit(type_id, id, value as u32),
+ crate::Literal::U64(value) => {
+ Instruction::constant_64bit(type_id, id, value as u32, (value >> 32) as u32)
+ }
crate::Literal::I64(value) => {
Instruction::constant_64bit(type_id, id, value as u32, (value >> 32) as u32)
}
diff --git a/third_party/rust/naga/src/back/wgsl/writer.rs b/third_party/rust/naga/src/back/wgsl/writer.rs
index c737934f5e..3039cbbbe4 100644
--- a/third_party/rust/naga/src/back/wgsl/writer.rs
+++ b/third_party/rust/naga/src/back/wgsl/writer.rs
@@ -109,7 +109,7 @@ impl<W: Write> Writer<W> {
self.reset(module);
// Save all ep result types
- for (_, ep) in module.entry_points.iter().enumerate() {
+ for ep in &module.entry_points {
if let Some(ref result) = ep.function.result {
self.ep_results.push((ep.stage, result.ty));
}
@@ -593,6 +593,7 @@ impl<W: Write> Writer<W> {
}
write!(self.out, ">")?;
}
+ TypeInner::AccelerationStructure => write!(self.out, "acceleration_structure")?,
_ => {
return Err(Error::Unimplemented(format!("write_value_type {inner:?}")));
}
@@ -1095,16 +1096,24 @@ impl<W: Write> Writer<W> {
// value can only be expressed in WGSL using AbstractInt and
// a unary negation operator.
if value == i32::MIN {
- write!(self.out, "i32(-2147483648)")?;
+ write!(self.out, "i32({})", value)?;
} else {
write!(self.out, "{}i", value)?;
}
}
crate::Literal::Bool(value) => write!(self.out, "{}", value)?,
crate::Literal::F64(value) => write!(self.out, "{:?}lf", value)?,
- crate::Literal::I64(_) => {
- return Err(Error::Custom("unsupported i64 literal".to_string()));
+ crate::Literal::I64(value) => {
+ // `-9223372036854775808li` is not valid WGSL. The most negative `i64`
+ // value can only be expressed in WGSL using AbstractInt and
+ // a unary negation operator.
+ if value == i64::MIN {
+ write!(self.out, "i64({})", value)?;
+ } else {
+ write!(self.out, "{}li", value)?;
+ }
}
+ crate::Literal::U64(value) => write!(self.out, "{:?}lu", value)?,
crate::Literal::AbstractInt(_) | crate::Literal::AbstractFloat(_) => {
return Err(Error::Custom(
"Abstract types should not appear in IR presented to backends".into(),
@@ -1828,6 +1837,14 @@ const fn scalar_kind_str(scalar: crate::Scalar) -> &'static str {
width: 4,
} => "u32",
Scalar {
+ kind: Sk::Sint,
+ width: 8,
+ } => "i64",
+ Scalar {
+ kind: Sk::Uint,
+ width: 8,
+ } => "u64",
+ Scalar {
kind: Sk::Bool,
width: 1,
} => "bool",
diff --git a/third_party/rust/naga/src/front/glsl/functions.rs b/third_party/rust/naga/src/front/glsl/functions.rs
index df8cc8a30e..01846eb814 100644
--- a/third_party/rust/naga/src/front/glsl/functions.rs
+++ b/third_party/rust/naga/src/front/glsl/functions.rs
@@ -160,7 +160,7 @@ impl Frontend {
} => self.matrix_one_arg(ctx, ty, columns, rows, scalar, (value, expr_meta), meta)?,
TypeInner::Struct { ref members, .. } => {
let scalar_components = members
- .get(0)
+ .first()
.and_then(|member| scalar_components(&ctx.module.types[member.ty].inner));
if let Some(scalar) = scalar_components {
ctx.implicit_conversion(&mut value, expr_meta, scalar)?;
diff --git a/third_party/rust/naga/src/front/glsl/parser/functions.rs b/third_party/rust/naga/src/front/glsl/parser/functions.rs
index 38184eedf7..d428d74761 100644
--- a/third_party/rust/naga/src/front/glsl/parser/functions.rs
+++ b/third_party/rust/naga/src/front/glsl/parser/functions.rs
@@ -435,7 +435,7 @@ impl<'source> ParsingContext<'source> {
if self.bump_if(frontend, TokenValue::Semicolon).is_none() {
if self.peek_type_name(frontend) || self.peek_type_qualifier(frontend) {
- self.parse_declaration(frontend, ctx, false, false)?;
+ self.parse_declaration(frontend, ctx, false, is_inside_loop)?;
} else {
let mut stmt = ctx.stmt_ctx();
let expr = self.parse_expression(frontend, ctx, &mut stmt)?;
diff --git a/third_party/rust/naga/src/front/spv/function.rs b/third_party/rust/naga/src/front/spv/function.rs
index 198d9c52dd..e81ecf5c9b 100644
--- a/third_party/rust/naga/src/front/spv/function.rs
+++ b/third_party/rust/naga/src/front/spv/function.rs
@@ -292,278 +292,286 @@ impl<I: Iterator<Item = u32>> super::Frontend<I> {
);
if let Some(ep) = self.lookup_entry_point.remove(&fun_id) {
- // create a wrapping function
- let mut function = crate::Function {
- name: Some(format!("{}_wrap", ep.name)),
- arguments: Vec::new(),
- result: None,
- local_variables: Arena::new(),
- expressions: Arena::new(),
- named_expressions: crate::NamedExpressions::default(),
- body: crate::Block::new(),
- };
+ self.deferred_entry_points.push((ep, fun_id));
+ }
- // 1. copy the inputs from arguments to privates
- for &v_id in ep.variable_ids.iter() {
- let lvar = self.lookup_variable.lookup(v_id)?;
- if let super::Variable::Input(ref arg) = lvar.inner {
- let span = module.global_variables.get_span(lvar.handle);
- let arg_expr = function.expressions.append(
- crate::Expression::FunctionArgument(function.arguments.len() as u32),
- span,
- );
- let load_expr = if arg.ty == module.global_variables[lvar.handle].ty {
- arg_expr
- } else {
- // The only case where the type is different is if we need to treat
- // unsigned integer as signed.
- let mut emitter = Emitter::default();
- emitter.start(&function.expressions);
- let handle = function.expressions.append(
- crate::Expression::As {
- expr: arg_expr,
- kind: crate::ScalarKind::Sint,
- convert: Some(4),
- },
- span,
- );
- function.body.extend(emitter.finish(&function.expressions));
- handle
- };
- function.body.push(
- crate::Statement::Store {
- pointer: function
- .expressions
- .append(crate::Expression::GlobalVariable(lvar.handle), span),
- value: load_expr,
+ Ok(())
+ }
+
+ pub(super) fn process_entry_point(
+ &mut self,
+ module: &mut crate::Module,
+ ep: super::EntryPoint,
+ fun_id: u32,
+ ) -> Result<(), Error> {
+ // create a wrapping function
+ let mut function = crate::Function {
+ name: Some(format!("{}_wrap", ep.name)),
+ arguments: Vec::new(),
+ result: None,
+ local_variables: Arena::new(),
+ expressions: Arena::new(),
+ named_expressions: crate::NamedExpressions::default(),
+ body: crate::Block::new(),
+ };
+
+ // 1. copy the inputs from arguments to privates
+ for &v_id in ep.variable_ids.iter() {
+ let lvar = self.lookup_variable.lookup(v_id)?;
+ if let super::Variable::Input(ref arg) = lvar.inner {
+ let span = module.global_variables.get_span(lvar.handle);
+ let arg_expr = function.expressions.append(
+ crate::Expression::FunctionArgument(function.arguments.len() as u32),
+ span,
+ );
+ let load_expr = if arg.ty == module.global_variables[lvar.handle].ty {
+ arg_expr
+ } else {
+ // The only case where the type is different is if we need to treat
+ // unsigned integer as signed.
+ let mut emitter = Emitter::default();
+ emitter.start(&function.expressions);
+ let handle = function.expressions.append(
+ crate::Expression::As {
+ expr: arg_expr,
+ kind: crate::ScalarKind::Sint,
+ convert: Some(4),
},
span,
);
+ function.body.extend(emitter.finish(&function.expressions));
+ handle
+ };
+ function.body.push(
+ crate::Statement::Store {
+ pointer: function
+ .expressions
+ .append(crate::Expression::GlobalVariable(lvar.handle), span),
+ value: load_expr,
+ },
+ span,
+ );
- let mut arg = arg.clone();
- if ep.stage == crate::ShaderStage::Fragment {
- if let Some(ref mut binding) = arg.binding {
- binding.apply_default_interpolation(&module.types[arg.ty].inner);
- }
+ let mut arg = arg.clone();
+ if ep.stage == crate::ShaderStage::Fragment {
+ if let Some(ref mut binding) = arg.binding {
+ binding.apply_default_interpolation(&module.types[arg.ty].inner);
}
- function.arguments.push(arg);
}
+ function.arguments.push(arg);
}
- // 2. call the wrapped function
- let fake_id = !(module.entry_points.len() as u32); // doesn't matter, as long as it's not a collision
- let dummy_handle = self.add_call(fake_id, fun_id);
- function.body.push(
- crate::Statement::Call {
- function: dummy_handle,
- arguments: Vec::new(),
- result: None,
- },
- crate::Span::default(),
- );
-
- // 3. copy the outputs from privates to the result
- let mut members = Vec::new();
- let mut components = Vec::new();
- for &v_id in ep.variable_ids.iter() {
- let lvar = self.lookup_variable.lookup(v_id)?;
- if let super::Variable::Output(ref result) = lvar.inner {
- let span = module.global_variables.get_span(lvar.handle);
- let expr_handle = function
- .expressions
- .append(crate::Expression::GlobalVariable(lvar.handle), span);
+ }
+ // 2. call the wrapped function
+ let fake_id = !(module.entry_points.len() as u32); // doesn't matter, as long as it's not a collision
+ let dummy_handle = self.add_call(fake_id, fun_id);
+ function.body.push(
+ crate::Statement::Call {
+ function: dummy_handle,
+ arguments: Vec::new(),
+ result: None,
+ },
+ crate::Span::default(),
+ );
- // Cull problematic builtins of gl_PerVertex.
- // See the docs for `Frontend::gl_per_vertex_builtin_access`.
+ // 3. copy the outputs from privates to the result
+ let mut members = Vec::new();
+ let mut components = Vec::new();
+ for &v_id in ep.variable_ids.iter() {
+ let lvar = self.lookup_variable.lookup(v_id)?;
+ if let super::Variable::Output(ref result) = lvar.inner {
+ let span = module.global_variables.get_span(lvar.handle);
+ let expr_handle = function
+ .expressions
+ .append(crate::Expression::GlobalVariable(lvar.handle), span);
+
+ // Cull problematic builtins of gl_PerVertex.
+ // See the docs for `Frontend::gl_per_vertex_builtin_access`.
+ {
+ let ty = &module.types[result.ty];
+ if let crate::TypeInner::Struct {
+ members: ref original_members,
+ span,
+ } = ty.inner
{
- let ty = &module.types[result.ty];
- match ty.inner {
- crate::TypeInner::Struct {
- members: ref original_members,
- span,
- } if ty.name.as_deref() == Some("gl_PerVertex") => {
- let mut new_members = original_members.clone();
- for member in &mut new_members {
- if let Some(crate::Binding::BuiltIn(built_in)) = member.binding
- {
- if !self.gl_per_vertex_builtin_access.contains(&built_in) {
- member.binding = None
- }
- }
- }
- if &new_members != original_members {
- module.types.replace(
- result.ty,
- crate::Type {
- name: ty.name.clone(),
- inner: crate::TypeInner::Struct {
- members: new_members,
- span,
- },
- },
- );
+ let mut new_members = None;
+ for (idx, member) in original_members.iter().enumerate() {
+ if let Some(crate::Binding::BuiltIn(built_in)) = member.binding {
+ if !self.gl_per_vertex_builtin_access.contains(&built_in) {
+ new_members.get_or_insert_with(|| original_members.clone())
+ [idx]
+ .binding = None;
}
}
- _ => {}
+ }
+ if let Some(new_members) = new_members {
+ module.types.replace(
+ result.ty,
+ crate::Type {
+ name: ty.name.clone(),
+ inner: crate::TypeInner::Struct {
+ members: new_members,
+ span,
+ },
+ },
+ );
}
}
+ }
- match module.types[result.ty].inner {
- crate::TypeInner::Struct {
- members: ref sub_members,
- ..
- } => {
- for (index, sm) in sub_members.iter().enumerate() {
- if sm.binding.is_none() {
- continue;
- }
- let mut sm = sm.clone();
-
- if let Some(ref mut binding) = sm.binding {
- if ep.stage == crate::ShaderStage::Vertex {
- binding.apply_default_interpolation(
- &module.types[sm.ty].inner,
- );
- }
- }
-
- members.push(sm);
-
- components.push(function.expressions.append(
- crate::Expression::AccessIndex {
- base: expr_handle,
- index: index as u32,
- },
- span,
- ));
+ match module.types[result.ty].inner {
+ crate::TypeInner::Struct {
+ members: ref sub_members,
+ ..
+ } => {
+ for (index, sm) in sub_members.iter().enumerate() {
+ if sm.binding.is_none() {
+ continue;
}
- }
- ref inner => {
- let mut binding = result.binding.clone();
- if let Some(ref mut binding) = binding {
+ let mut sm = sm.clone();
+
+ if let Some(ref mut binding) = sm.binding {
if ep.stage == crate::ShaderStage::Vertex {
- binding.apply_default_interpolation(inner);
+ binding.apply_default_interpolation(&module.types[sm.ty].inner);
}
}
- members.push(crate::StructMember {
- name: None,
- ty: result.ty,
- binding,
- offset: 0,
- });
- // populate just the globals first, then do `Load` in a
- // separate step, so that we can get a range.
- components.push(expr_handle);
+ members.push(sm);
+
+ components.push(function.expressions.append(
+ crate::Expression::AccessIndex {
+ base: expr_handle,
+ index: index as u32,
+ },
+ span,
+ ));
}
}
- }
- }
+ ref inner => {
+ let mut binding = result.binding.clone();
+ if let Some(ref mut binding) = binding {
+ if ep.stage == crate::ShaderStage::Vertex {
+ binding.apply_default_interpolation(inner);
+ }
+ }
- for (member_index, member) in members.iter().enumerate() {
- match member.binding {
- Some(crate::Binding::BuiltIn(crate::BuiltIn::Position { .. }))
- if self.options.adjust_coordinate_space =>
- {
- let mut emitter = Emitter::default();
- emitter.start(&function.expressions);
- let global_expr = components[member_index];
- let span = function.expressions.get_span(global_expr);
- let access_expr = function.expressions.append(
- crate::Expression::AccessIndex {
- base: global_expr,
- index: 1,
- },
- span,
- );
- let load_expr = function.expressions.append(
- crate::Expression::Load {
- pointer: access_expr,
- },
- span,
- );
- let neg_expr = function.expressions.append(
- crate::Expression::Unary {
- op: crate::UnaryOperator::Negate,
- expr: load_expr,
- },
- span,
- );
- function.body.extend(emitter.finish(&function.expressions));
- function.body.push(
- crate::Statement::Store {
- pointer: access_expr,
- value: neg_expr,
- },
- span,
- );
+ members.push(crate::StructMember {
+ name: None,
+ ty: result.ty,
+ binding,
+ offset: 0,
+ });
+ // populate just the globals first, then do `Load` in a
+ // separate step, so that we can get a range.
+ components.push(expr_handle);
}
- _ => {}
}
}
+ }
- let mut emitter = Emitter::default();
- emitter.start(&function.expressions);
- for component in components.iter_mut() {
- let load_expr = crate::Expression::Load {
- pointer: *component,
- };
- let span = function.expressions.get_span(*component);
- *component = function.expressions.append(load_expr, span);
- }
-
- match members[..] {
- [] => {}
- [ref member] => {
- function.body.extend(emitter.finish(&function.expressions));
- let span = function.expressions.get_span(components[0]);
- function.body.push(
- crate::Statement::Return {
- value: components.first().cloned(),
+ for (member_index, member) in members.iter().enumerate() {
+ match member.binding {
+ Some(crate::Binding::BuiltIn(crate::BuiltIn::Position { .. }))
+ if self.options.adjust_coordinate_space =>
+ {
+ let mut emitter = Emitter::default();
+ emitter.start(&function.expressions);
+ let global_expr = components[member_index];
+ let span = function.expressions.get_span(global_expr);
+ let access_expr = function.expressions.append(
+ crate::Expression::AccessIndex {
+ base: global_expr,
+ index: 1,
},
span,
);
- function.result = Some(crate::FunctionResult {
- ty: member.ty,
- binding: member.binding.clone(),
- });
- }
- _ => {
- let span = crate::Span::total_span(
- components.iter().map(|h| function.expressions.get_span(*h)),
+ let load_expr = function.expressions.append(
+ crate::Expression::Load {
+ pointer: access_expr,
+ },
+ span,
);
- let ty = module.types.insert(
- crate::Type {
- name: None,
- inner: crate::TypeInner::Struct {
- members,
- span: 0xFFFF, // shouldn't matter
- },
+ let neg_expr = function.expressions.append(
+ crate::Expression::Unary {
+ op: crate::UnaryOperator::Negate,
+ expr: load_expr,
},
span,
);
- let result_expr = function
- .expressions
- .append(crate::Expression::Compose { ty, components }, span);
function.body.extend(emitter.finish(&function.expressions));
function.body.push(
- crate::Statement::Return {
- value: Some(result_expr),
+ crate::Statement::Store {
+ pointer: access_expr,
+ value: neg_expr,
},
span,
);
- function.result = Some(crate::FunctionResult { ty, binding: None });
}
+ _ => {}
}
+ }
- module.entry_points.push(crate::EntryPoint {
- name: ep.name,
- stage: ep.stage,
- early_depth_test: ep.early_depth_test,
- workgroup_size: ep.workgroup_size,
- function,
- });
+ let mut emitter = Emitter::default();
+ emitter.start(&function.expressions);
+ for component in components.iter_mut() {
+ let load_expr = crate::Expression::Load {
+ pointer: *component,
+ };
+ let span = function.expressions.get_span(*component);
+ *component = function.expressions.append(load_expr, span);
}
+ match members[..] {
+ [] => {}
+ [ref member] => {
+ function.body.extend(emitter.finish(&function.expressions));
+ let span = function.expressions.get_span(components[0]);
+ function.body.push(
+ crate::Statement::Return {
+ value: components.first().cloned(),
+ },
+ span,
+ );
+ function.result = Some(crate::FunctionResult {
+ ty: member.ty,
+ binding: member.binding.clone(),
+ });
+ }
+ _ => {
+ let span = crate::Span::total_span(
+ components.iter().map(|h| function.expressions.get_span(*h)),
+ );
+ let ty = module.types.insert(
+ crate::Type {
+ name: None,
+ inner: crate::TypeInner::Struct {
+ members,
+ span: 0xFFFF, // shouldn't matter
+ },
+ },
+ span,
+ );
+ let result_expr = function
+ .expressions
+ .append(crate::Expression::Compose { ty, components }, span);
+ function.body.extend(emitter.finish(&function.expressions));
+ function.body.push(
+ crate::Statement::Return {
+ value: Some(result_expr),
+ },
+ span,
+ );
+ function.result = Some(crate::FunctionResult { ty, binding: None });
+ }
+ }
+
+ module.entry_points.push(crate::EntryPoint {
+ name: ep.name,
+ stage: ep.stage,
+ early_depth_test: ep.early_depth_test,
+ workgroup_size: ep.workgroup_size,
+ function,
+ });
+
Ok(())
}
}
diff --git a/third_party/rust/naga/src/front/spv/mod.rs b/third_party/rust/naga/src/front/spv/mod.rs
index 8b1c854358..b793448597 100644
--- a/third_party/rust/naga/src/front/spv/mod.rs
+++ b/third_party/rust/naga/src/front/spv/mod.rs
@@ -577,6 +577,9 @@ pub struct Frontend<I> {
lookup_function_type: FastHashMap<spirv::Word, LookupFunctionType>,
lookup_function: FastHashMap<spirv::Word, LookupFunction>,
lookup_entry_point: FastHashMap<spirv::Word, EntryPoint>,
+ // When parsing functions, each entry point function gets an entry here so that additional
+ // processing for them can be performed after all function parsing.
+ deferred_entry_points: Vec<(EntryPoint, spirv::Word)>,
//Note: each `OpFunctionCall` gets a single entry here, indexed by the
// dummy `Handle<crate::Function>` of the call site.
deferred_function_calls: Vec<spirv::Word>,
@@ -628,6 +631,7 @@ impl<I: Iterator<Item = u32>> Frontend<I> {
lookup_function_type: FastHashMap::default(),
lookup_function: FastHashMap::default(),
lookup_entry_point: FastHashMap::default(),
+ deferred_entry_points: Vec::default(),
deferred_function_calls: Vec::default(),
dummy_functions: Arena::new(),
function_call_graph: GraphMap::new(),
@@ -1561,12 +1565,10 @@ impl<I: Iterator<Item = u32>> Frontend<I> {
span,
);
- if ty.name.as_deref() == Some("gl_PerVertex") {
- if let Some(crate::Binding::BuiltIn(built_in)) =
- members[index as usize].binding
- {
- self.gl_per_vertex_builtin_access.insert(built_in);
- }
+ if let Some(crate::Binding::BuiltIn(built_in)) =
+ members[index as usize].binding
+ {
+ self.gl_per_vertex_builtin_access.insert(built_in);
}
AccessExpression {
@@ -3956,6 +3958,12 @@ impl<I: Iterator<Item = u32>> Frontend<I> {
}?;
}
+ // Do entry point specific processing after all functions are parsed so that we can
+ // cull unused problematic builtins of gl_PerVertex.
+ for (ep, fun_id) in core::mem::take(&mut self.deferred_entry_points) {
+ self.process_entry_point(&mut module, ep, fun_id)?;
+ }
+
log::info!("Patching...");
{
let mut nodes = petgraph::algo::toposort(&self.function_call_graph, None)
@@ -4868,6 +4876,11 @@ impl<I: Iterator<Item = u32>> Frontend<I> {
let low = self.next()?;
match width {
4 => crate::Literal::U32(low),
+ 8 => {
+ inst.expect(5)?;
+ let high = self.next()?;
+ crate::Literal::U64(u64::from(high) << 32 | u64::from(low))
+ }
_ => return Err(Error::InvalidTypeWidth(width as u32)),
}
}
@@ -5081,7 +5094,7 @@ impl<I: Iterator<Item = u32>> Frontend<I> {
None
};
let span = self.span_from_with_op(start);
- let mut dec = self.future_decor.remove(&id).unwrap_or_default();
+ let dec = self.future_decor.remove(&id).unwrap_or_default();
let original_ty = self.lookup_type.lookup(type_id)?.handle;
let mut ty = original_ty;
@@ -5127,17 +5140,6 @@ impl<I: Iterator<Item = u32>> Frontend<I> {
None => map_storage_class(storage_class)?,
};
- // Fix empty name for gl_PerVertex struct generated by glslang
- if let crate::TypeInner::Pointer { .. } = module.types[original_ty].inner {
- if ext_class == ExtendedClass::Input || ext_class == ExtendedClass::Output {
- if let Some(ref dec_name) = dec.name {
- if dec_name.is_empty() {
- dec.name = Some("perVertexStruct".to_string())
- }
- }
- }
- }
-
let (inner, var) = match ext_class {
ExtendedClass::Global(mut space) => {
if let crate::AddressSpace::Storage { ref mut access } = space {
@@ -5323,6 +5325,21 @@ pub fn parse_u8_slice(data: &[u8], options: &Options) -> Result<crate::Module, E
Frontend::new(words, options).parse()
}
+/// Helper function to check if `child` is in the scope of `parent`
+fn is_parent(mut child: usize, parent: usize, block_ctx: &BlockContext) -> bool {
+ loop {
+ if child == parent {
+ // The child is in the scope parent
+ break true;
+ } else if child == 0 {
+ // Searched finished at the root the child isn't in the parent's body
+ break false;
+ }
+
+ child = block_ctx.bodies[child].parent;
+ }
+}
+
#[cfg(test)]
mod test {
#[test]
@@ -5339,18 +5356,3 @@ mod test {
let _ = super::parse_u8_slice(&bin, &Default::default()).unwrap();
}
}
-
-/// Helper function to check if `child` is in the scope of `parent`
-fn is_parent(mut child: usize, parent: usize, block_ctx: &BlockContext) -> bool {
- loop {
- if child == parent {
- // The child is in the scope parent
- break true;
- } else if child == 0 {
- // Searched finished at the root the child isn't in the parent's body
- break false;
- }
-
- child = block_ctx.bodies[child].parent;
- }
-}
diff --git a/third_party/rust/naga/src/front/wgsl/error.rs b/third_party/rust/naga/src/front/wgsl/error.rs
index 07e68f8dd9..54aa8296b1 100644
--- a/third_party/rust/naga/src/front/wgsl/error.rs
+++ b/third_party/rust/naga/src/front/wgsl/error.rs
@@ -87,7 +87,7 @@ impl ParseError {
/// Returns a [`SourceLocation`] for the first label in the error message.
pub fn location(&self, source: &str) -> Option<SourceLocation> {
- self.labels.get(0).map(|label| label.0.location(source))
+ self.labels.first().map(|label| label.0.location(source))
}
}
diff --git a/third_party/rust/naga/src/front/wgsl/lower/mod.rs b/third_party/rust/naga/src/front/wgsl/lower/mod.rs
index ba9b49e135..2ca6c182b7 100644
--- a/third_party/rust/naga/src/front/wgsl/lower/mod.rs
+++ b/third_party/rust/naga/src/front/wgsl/lower/mod.rs
@@ -1530,6 +1530,8 @@ impl<'source, 'temp> Lowerer<'source, 'temp> {
ast::Literal::Number(Number::F32(f)) => crate::Literal::F32(f),
ast::Literal::Number(Number::I32(i)) => crate::Literal::I32(i),
ast::Literal::Number(Number::U32(u)) => crate::Literal::U32(u),
+ ast::Literal::Number(Number::I64(i)) => crate::Literal::I64(i),
+ ast::Literal::Number(Number::U64(u)) => crate::Literal::U64(u),
ast::Literal::Number(Number::F64(f)) => crate::Literal::F64(f),
ast::Literal::Number(Number::AbstractInt(i)) => crate::Literal::AbstractInt(i),
ast::Literal::Number(Number::AbstractFloat(f)) => {
diff --git a/third_party/rust/naga/src/front/wgsl/parse/conv.rs b/third_party/rust/naga/src/front/wgsl/parse/conv.rs
index 08f1e39285..1a4911a3bd 100644
--- a/third_party/rust/naga/src/front/wgsl/parse/conv.rs
+++ b/third_party/rust/naga/src/front/wgsl/parse/conv.rs
@@ -124,6 +124,14 @@ pub fn get_scalar_type(word: &str) -> Option<Scalar> {
kind: Sk::Uint,
width: 4,
}),
+ "i64" => Some(Scalar {
+ kind: Sk::Sint,
+ width: 8,
+ }),
+ "u64" => Some(Scalar {
+ kind: Sk::Uint,
+ width: 8,
+ }),
"bool" => Some(Scalar {
kind: Sk::Bool,
width: crate::BOOL_WIDTH,
diff --git a/third_party/rust/naga/src/front/wgsl/parse/number.rs b/third_party/rust/naga/src/front/wgsl/parse/number.rs
index 7b09ac59bb..ceb2cb336c 100644
--- a/third_party/rust/naga/src/front/wgsl/parse/number.rs
+++ b/third_party/rust/naga/src/front/wgsl/parse/number.rs
@@ -12,6 +12,10 @@ pub enum Number {
I32(i32),
/// Concrete u32
U32(u32),
+ /// Concrete i64
+ I64(i64),
+ /// Concrete u64
+ U64(u64),
/// Concrete f32
F32(f32),
/// Concrete f64
@@ -31,6 +35,8 @@ enum Kind {
enum IntKind {
I32,
U32,
+ I64,
+ U64,
}
#[derive(Debug)]
@@ -270,6 +276,8 @@ fn parse(input: &str) -> (Result<Number, NumberError>, &str) {
let kind = consume_map!(bytes, [
b'i' => Kind::Int(IntKind::I32),
b'u' => Kind::Int(IntKind::U32),
+ b'l', b'i' => Kind::Int(IntKind::I64),
+ b'l', b'u' => Kind::Int(IntKind::U64),
b'h' => Kind::Float(FloatKind::F16),
b'f' => Kind::Float(FloatKind::F32),
b'l', b'f' => Kind::Float(FloatKind::F64),
@@ -416,5 +424,13 @@ fn parse_int(input: &str, kind: Option<IntKind>, radix: u32) -> Result<Number, N
Ok(num) => Ok(Number::U32(num)),
Err(e) => Err(map_err(e)),
},
+ Some(IntKind::I64) => match i64::from_str_radix(input, radix) {
+ Ok(num) => Ok(Number::I64(num)),
+ Err(e) => Err(map_err(e)),
+ },
+ Some(IntKind::U64) => match u64::from_str_radix(input, radix) {
+ Ok(num) => Ok(Number::U64(num)),
+ Err(e) => Err(map_err(e)),
+ },
}
}
diff --git a/third_party/rust/naga/src/front/wgsl/tests.rs b/third_party/rust/naga/src/front/wgsl/tests.rs
index eb2f8a2eb3..cc3d858317 100644
--- a/third_party/rust/naga/src/front/wgsl/tests.rs
+++ b/third_party/rust/naga/src/front/wgsl/tests.rs
@@ -17,6 +17,7 @@ fn parse_comment() {
#[test]
fn parse_types() {
parse_str("const a : i32 = 2;").unwrap();
+ parse_str("const a : u64 = 2lu;").unwrap();
assert!(parse_str("const a : x32 = 2;").is_err());
parse_str("var t: texture_2d<f32>;").unwrap();
parse_str("var t: texture_cube_array<i32>;").unwrap();
diff --git a/third_party/rust/naga/src/keywords/wgsl.rs b/third_party/rust/naga/src/keywords/wgsl.rs
index 7b47a13128..683840dc1f 100644
--- a/third_party/rust/naga/src/keywords/wgsl.rs
+++ b/third_party/rust/naga/src/keywords/wgsl.rs
@@ -14,6 +14,7 @@ pub const RESERVED: &[&str] = &[
"f32",
"f16",
"i32",
+ "i64",
"mat2x2",
"mat2x3",
"mat2x4",
@@ -43,6 +44,7 @@ pub const RESERVED: &[&str] = &[
"texture_depth_cube_array",
"texture_depth_multisampled_2d",
"u32",
+ "u64",
"vec2",
"vec3",
"vec4",
diff --git a/third_party/rust/naga/src/lib.rs b/third_party/rust/naga/src/lib.rs
index d6b9c6a7f4..4b45174300 100644
--- a/third_party/rust/naga/src/lib.rs
+++ b/third_party/rust/naga/src/lib.rs
@@ -252,7 +252,8 @@ An override expression can be evaluated at pipeline creation time.
clippy::collapsible_if,
clippy::derive_partial_eq_without_eq,
clippy::needless_borrowed_reference,
- clippy::single_match
+ clippy::single_match,
+ clippy::enum_variant_names
)]
#![warn(
trivial_casts,
@@ -490,7 +491,7 @@ pub enum ScalarKind {
}
/// Characteristics of a scalar type.
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
@@ -884,6 +885,7 @@ pub enum Literal {
F32(f32),
U32(u32),
I32(i32),
+ U64(u64),
I64(i64),
Bool(bool),
AbstractInt(i64),
@@ -1255,15 +1257,18 @@ pub enum SampleLevel {
#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
pub enum ImageQuery {
/// Get the size at the specified level.
+ ///
+ /// The return value is a `u32` for 1D images, and a `vecN<u32>`
+ /// for an image with dimensions N > 2.
Size {
/// If `None`, the base level is considered.
level: Option<Handle<Expression>>,
},
- /// Get the number of mipmap levels.
+ /// Get the number of mipmap levels, a `u32`.
NumLevels,
- /// Get the number of array layers.
+ /// Get the number of array layers, a `u32`.
NumLayers,
- /// Get the number of samples.
+ /// Get the number of samples, a `u32`.
NumSamples,
}
@@ -1683,6 +1688,10 @@ pub enum Statement {
/// A block containing more statements, to be executed sequentially.
Block(Block),
/// Conditionally executes one of two blocks, based on the value of the condition.
+ ///
+ /// Naga IR does not have "phi" instructions. If you need to use
+ /// values computed in an `accept` or `reject` block after the `If`,
+ /// store them in a [`LocalVariable`].
If {
condition: Handle<Expression>, //bool
accept: Block,
@@ -1702,6 +1711,10 @@ pub enum Statement {
/// represented in the IR as a series of fallthrough cases with empty
/// bodies, except for the last.
///
+ /// Naga IR does not have "phi" instructions. If you need to use
+ /// values computed in a [`SwitchCase::body`] block after the `Switch`,
+ /// store them in a [`LocalVariable`].
+ ///
/// [`value`]: SwitchCase::value
/// [`body`]: SwitchCase::body
/// [`Default`]: SwitchValue::Default
@@ -1736,6 +1749,10 @@ pub enum Statement {
/// if" statement in WGSL, or a loop whose back edge is an
/// `OpBranchConditional` instruction in SPIR-V.
///
+ /// Naga IR does not have "phi" instructions. If you need to use
+ /// values computed in a `body` or `continuing` block after the
+ /// `Loop`, store them in a [`LocalVariable`].
+ ///
/// [`Break`]: Statement::Break
/// [`Continue`]: Statement::Continue
/// [`Kill`]: Statement::Kill
diff --git a/third_party/rust/naga/src/proc/constant_evaluator.rs b/third_party/rust/naga/src/proc/constant_evaluator.rs
index b3884b04b1..983af3718c 100644
--- a/third_party/rust/naga/src/proc/constant_evaluator.rs
+++ b/third_party/rust/naga/src/proc/constant_evaluator.rs
@@ -31,7 +31,7 @@ macro_rules! gen_component_wise_extractor {
$(
#[doc = concat!(
"Maps to [`Literal::",
- stringify!($mapping),
+ stringify!($literal),
"`]",
)]
$mapping([$ty; N]),
@@ -200,6 +200,8 @@ gen_component_wise_extractor! {
AbstractInt => AbstractInt: i64,
U32 => U32: u32,
I32 => I32: i32,
+ U64 => U64: u64,
+ I64 => I64: i64,
],
scalar_kinds: [
Float,
@@ -847,6 +849,8 @@ impl<'a> ConstantEvaluator<'a> {
Scalar::AbstractInt([e]) => Ok(Scalar::AbstractInt([e.abs()])),
Scalar::I32([e]) => Ok(Scalar::I32([e.wrapping_abs()])),
Scalar::U32([e]) => Ok(Scalar::U32([e])), // TODO: just re-use the expression, ezpz
+ Scalar::I64([e]) => Ok(Scalar::I64([e.wrapping_abs()])),
+ Scalar::U64([e]) => Ok(Scalar::U64([e])),
})
}
crate::MathFunction::Min => {
@@ -1280,7 +1284,7 @@ impl<'a> ConstantEvaluator<'a> {
Literal::U32(v) => v as i32,
Literal::F32(v) => v as i32,
Literal::Bool(v) => v as i32,
- Literal::F64(_) | Literal::I64(_) => {
+ Literal::F64(_) | Literal::I64(_) | Literal::U64(_) => {
return make_error();
}
Literal::AbstractInt(v) => i32::try_from_abstract(v)?,
@@ -1291,18 +1295,40 @@ impl<'a> ConstantEvaluator<'a> {
Literal::U32(v) => v,
Literal::F32(v) => v as u32,
Literal::Bool(v) => v as u32,
- Literal::F64(_) | Literal::I64(_) => {
+ Literal::F64(_) | Literal::I64(_) | Literal::U64(_) => {
return make_error();
}
Literal::AbstractInt(v) => u32::try_from_abstract(v)?,
Literal::AbstractFloat(v) => u32::try_from_abstract(v)?,
}),
+ Sc::I64 => Literal::I64(match literal {
+ Literal::I32(v) => v as i64,
+ Literal::U32(v) => v as i64,
+ Literal::F32(v) => v as i64,
+ Literal::Bool(v) => v as i64,
+ Literal::F64(v) => v as i64,
+ Literal::I64(v) => v,
+ Literal::U64(v) => v as i64,
+ Literal::AbstractInt(v) => i64::try_from_abstract(v)?,
+ Literal::AbstractFloat(v) => i64::try_from_abstract(v)?,
+ }),
+ Sc::U64 => Literal::U64(match literal {
+ Literal::I32(v) => v as u64,
+ Literal::U32(v) => v as u64,
+ Literal::F32(v) => v as u64,
+ Literal::Bool(v) => v as u64,
+ Literal::F64(v) => v as u64,
+ Literal::I64(v) => v as u64,
+ Literal::U64(v) => v,
+ Literal::AbstractInt(v) => u64::try_from_abstract(v)?,
+ Literal::AbstractFloat(v) => u64::try_from_abstract(v)?,
+ }),
Sc::F32 => Literal::F32(match literal {
Literal::I32(v) => v as f32,
Literal::U32(v) => v as f32,
Literal::F32(v) => v,
Literal::Bool(v) => v as u32 as f32,
- Literal::F64(_) | Literal::I64(_) => {
+ Literal::F64(_) | Literal::I64(_) | Literal::U64(_) => {
return make_error();
}
Literal::AbstractInt(v) => f32::try_from_abstract(v)?,
@@ -1314,7 +1340,7 @@ impl<'a> ConstantEvaluator<'a> {
Literal::F32(v) => v as f64,
Literal::F64(v) => v,
Literal::Bool(v) => v as u32 as f64,
- Literal::I64(_) => return make_error(),
+ Literal::I64(_) | Literal::U64(_) => return make_error(),
Literal::AbstractInt(v) => f64::try_from_abstract(v)?,
Literal::AbstractFloat(v) => f64::try_from_abstract(v)?,
}),
@@ -1325,6 +1351,7 @@ impl<'a> ConstantEvaluator<'a> {
Literal::Bool(v) => v,
Literal::F64(_)
| Literal::I64(_)
+ | Literal::U64(_)
| Literal::AbstractInt(_)
| Literal::AbstractFloat(_) => {
return make_error();
@@ -1877,6 +1904,122 @@ impl<'a> ConstantEvaluator<'a> {
}
}
+/// Trait for conversions of abstract values to concrete types.
+trait TryFromAbstract<T>: Sized {
+ /// Convert an abstract literal `value` to `Self`.
+ ///
+ /// Since Naga's `AbstractInt` and `AbstractFloat` exist to support
+ /// WGSL, we follow WGSL's conversion rules here:
+ ///
+ /// - WGSL §6.1.2. Conversion Rank says that automatic conversions
+ /// to integers are either lossless or an error.
+ ///
+ /// - WGSL §14.6.4 Floating Point Conversion says that conversions
+ /// to floating point in constant expressions and override
+ /// expressions are errors if the value is out of range for the
+ /// destination type, but rounding is okay.
+ ///
+ /// [`AbstractInt`]: crate::Literal::AbstractInt
+ /// [`Float`]: crate::Literal::Float
+ fn try_from_abstract(value: T) -> Result<Self, ConstantEvaluatorError>;
+}
+
+impl TryFromAbstract<i64> for i32 {
+ fn try_from_abstract(value: i64) -> Result<i32, ConstantEvaluatorError> {
+ i32::try_from(value).map_err(|_| ConstantEvaluatorError::AutomaticConversionLossy {
+ value: format!("{value:?}"),
+ to_type: "i32",
+ })
+ }
+}
+
+impl TryFromAbstract<i64> for u32 {
+ fn try_from_abstract(value: i64) -> Result<u32, ConstantEvaluatorError> {
+ u32::try_from(value).map_err(|_| ConstantEvaluatorError::AutomaticConversionLossy {
+ value: format!("{value:?}"),
+ to_type: "u32",
+ })
+ }
+}
+
+impl TryFromAbstract<i64> for u64 {
+ fn try_from_abstract(value: i64) -> Result<u64, ConstantEvaluatorError> {
+ u64::try_from(value).map_err(|_| ConstantEvaluatorError::AutomaticConversionLossy {
+ value: format!("{value:?}"),
+ to_type: "u64",
+ })
+ }
+}
+
+impl TryFromAbstract<i64> for i64 {
+ fn try_from_abstract(value: i64) -> Result<i64, ConstantEvaluatorError> {
+ Ok(value)
+ }
+}
+
+impl TryFromAbstract<i64> for f32 {
+ fn try_from_abstract(value: i64) -> Result<Self, ConstantEvaluatorError> {
+ let f = value as f32;
+ // The range of `i64` is roughly ±18 × 10¹⁸, whereas the range of
+ // `f32` is roughly ±3.4 × 10³⁸, so there's no opportunity for
+ // overflow here.
+ Ok(f)
+ }
+}
+
+impl TryFromAbstract<f64> for f32 {
+ fn try_from_abstract(value: f64) -> Result<f32, ConstantEvaluatorError> {
+ let f = value as f32;
+ if f.is_infinite() {
+ return Err(ConstantEvaluatorError::AutomaticConversionLossy {
+ value: format!("{value:?}"),
+ to_type: "f32",
+ });
+ }
+ Ok(f)
+ }
+}
+
+impl TryFromAbstract<i64> for f64 {
+ fn try_from_abstract(value: i64) -> Result<Self, ConstantEvaluatorError> {
+ let f = value as f64;
+ // The range of `i64` is roughly ±18 × 10¹⁸, whereas the range of
+ // `f64` is roughly ±1.8 × 10³⁰⁸, so there's no opportunity for
+ // overflow here.
+ Ok(f)
+ }
+}
+
+impl TryFromAbstract<f64> for f64 {
+ fn try_from_abstract(value: f64) -> Result<f64, ConstantEvaluatorError> {
+ Ok(value)
+ }
+}
+
+impl TryFromAbstract<f64> for i32 {
+ fn try_from_abstract(_: f64) -> Result<Self, ConstantEvaluatorError> {
+ Err(ConstantEvaluatorError::AutomaticConversionFloatToInt { to_type: "i32" })
+ }
+}
+
+impl TryFromAbstract<f64> for u32 {
+ fn try_from_abstract(_: f64) -> Result<Self, ConstantEvaluatorError> {
+ Err(ConstantEvaluatorError::AutomaticConversionFloatToInt { to_type: "u32" })
+ }
+}
+
+impl TryFromAbstract<f64> for i64 {
+ fn try_from_abstract(_: f64) -> Result<Self, ConstantEvaluatorError> {
+ Err(ConstantEvaluatorError::AutomaticConversionFloatToInt { to_type: "i64" })
+ }
+}
+
+impl TryFromAbstract<f64> for u64 {
+ fn try_from_abstract(_: f64) -> Result<Self, ConstantEvaluatorError> {
+ Err(ConstantEvaluatorError::AutomaticConversionFloatToInt { to_type: "u64" })
+ }
+}
+
#[cfg(test)]
mod tests {
use std::vec;
@@ -2384,92 +2527,3 @@ mod tests {
}
}
}
-
-/// Trait for conversions of abstract values to concrete types.
-trait TryFromAbstract<T>: Sized {
- /// Convert an abstract literal `value` to `Self`.
- ///
- /// Since Naga's `AbstractInt` and `AbstractFloat` exist to support
- /// WGSL, we follow WGSL's conversion rules here:
- ///
- /// - WGSL §6.1.2. Conversion Rank says that automatic conversions
- /// to integers are either lossless or an error.
- ///
- /// - WGSL §14.6.4 Floating Point Conversion says that conversions
- /// to floating point in constant expressions and override
- /// expressions are errors if the value is out of range for the
- /// destination type, but rounding is okay.
- ///
- /// [`AbstractInt`]: crate::Literal::AbstractInt
- /// [`Float`]: crate::Literal::Float
- fn try_from_abstract(value: T) -> Result<Self, ConstantEvaluatorError>;
-}
-
-impl TryFromAbstract<i64> for i32 {
- fn try_from_abstract(value: i64) -> Result<i32, ConstantEvaluatorError> {
- i32::try_from(value).map_err(|_| ConstantEvaluatorError::AutomaticConversionLossy {
- value: format!("{value:?}"),
- to_type: "i32",
- })
- }
-}
-
-impl TryFromAbstract<i64> for u32 {
- fn try_from_abstract(value: i64) -> Result<u32, ConstantEvaluatorError> {
- u32::try_from(value).map_err(|_| ConstantEvaluatorError::AutomaticConversionLossy {
- value: format!("{value:?}"),
- to_type: "u32",
- })
- }
-}
-
-impl TryFromAbstract<i64> for f32 {
- fn try_from_abstract(value: i64) -> Result<Self, ConstantEvaluatorError> {
- let f = value as f32;
- // The range of `i64` is roughly ±18 × 10¹⁸, whereas the range of
- // `f32` is roughly ±3.4 × 10³⁸, so there's no opportunity for
- // overflow here.
- Ok(f)
- }
-}
-
-impl TryFromAbstract<f64> for f32 {
- fn try_from_abstract(value: f64) -> Result<f32, ConstantEvaluatorError> {
- let f = value as f32;
- if f.is_infinite() {
- return Err(ConstantEvaluatorError::AutomaticConversionLossy {
- value: format!("{value:?}"),
- to_type: "f32",
- });
- }
- Ok(f)
- }
-}
-
-impl TryFromAbstract<i64> for f64 {
- fn try_from_abstract(value: i64) -> Result<Self, ConstantEvaluatorError> {
- let f = value as f64;
- // The range of `i64` is roughly ±18 × 10¹⁸, whereas the range of
- // `f64` is roughly ±1.8 × 10³⁰⁸, so there's no opportunity for
- // overflow here.
- Ok(f)
- }
-}
-
-impl TryFromAbstract<f64> for f64 {
- fn try_from_abstract(value: f64) -> Result<f64, ConstantEvaluatorError> {
- Ok(value)
- }
-}
-
-impl TryFromAbstract<f64> for i32 {
- fn try_from_abstract(_: f64) -> Result<Self, ConstantEvaluatorError> {
- Err(ConstantEvaluatorError::AutomaticConversionFloatToInt { to_type: "i32" })
- }
-}
-
-impl TryFromAbstract<f64> for u32 {
- fn try_from_abstract(_: f64) -> Result<Self, ConstantEvaluatorError> {
- Err(ConstantEvaluatorError::AutomaticConversionFloatToInt { to_type: "u32" })
- }
-}
diff --git a/third_party/rust/naga/src/proc/mod.rs b/third_party/rust/naga/src/proc/mod.rs
index b9ce80b5ea..46cbb6c3b3 100644
--- a/third_party/rust/naga/src/proc/mod.rs
+++ b/third_party/rust/naga/src/proc/mod.rs
@@ -102,6 +102,10 @@ impl super::Scalar {
kind: crate::ScalarKind::Sint,
width: 8,
};
+ pub const U64: Self = Self {
+ kind: crate::ScalarKind::Uint,
+ width: 8,
+ };
pub const BOOL: Self = Self {
kind: crate::ScalarKind::Bool,
width: crate::BOOL_WIDTH,
@@ -156,6 +160,7 @@ impl PartialEq for crate::Literal {
(Self::F32(a), Self::F32(b)) => a.to_bits() == b.to_bits(),
(Self::U32(a), Self::U32(b)) => a == b,
(Self::I32(a), Self::I32(b)) => a == b,
+ (Self::U64(a), Self::U64(b)) => a == b,
(Self::I64(a), Self::I64(b)) => a == b,
(Self::Bool(a), Self::Bool(b)) => a == b,
_ => false,
@@ -186,10 +191,18 @@ impl std::hash::Hash for crate::Literal {
hasher.write_u8(4);
v.hash(hasher);
}
- Self::I64(v) | Self::AbstractInt(v) => {
+ Self::I64(v) => {
hasher.write_u8(5);
v.hash(hasher);
}
+ Self::U64(v) => {
+ hasher.write_u8(6);
+ v.hash(hasher);
+ }
+ Self::AbstractInt(v) => {
+ hasher.write_u8(7);
+ v.hash(hasher);
+ }
}
}
}
@@ -201,6 +214,7 @@ impl crate::Literal {
(value, crate::ScalarKind::Float, 4) => Some(Self::F32(value as _)),
(value, crate::ScalarKind::Uint, 4) => Some(Self::U32(value as _)),
(value, crate::ScalarKind::Sint, 4) => Some(Self::I32(value as _)),
+ (value, crate::ScalarKind::Uint, 8) => Some(Self::U64(value as _)),
(value, crate::ScalarKind::Sint, 8) => Some(Self::I64(value as _)),
(1, crate::ScalarKind::Bool, 4) => Some(Self::Bool(true)),
(0, crate::ScalarKind::Bool, 4) => Some(Self::Bool(false)),
@@ -218,7 +232,7 @@ impl crate::Literal {
pub const fn width(&self) -> crate::Bytes {
match *self {
- Self::F64(_) | Self::I64(_) => 8,
+ Self::F64(_) | Self::I64(_) | Self::U64(_) => 8,
Self::F32(_) | Self::U32(_) | Self::I32(_) => 4,
Self::Bool(_) => crate::BOOL_WIDTH,
Self::AbstractInt(_) | Self::AbstractFloat(_) => crate::ABSTRACT_WIDTH,
@@ -230,6 +244,7 @@ impl crate::Literal {
Self::F32(_) => crate::Scalar::F32,
Self::U32(_) => crate::Scalar::U32,
Self::I32(_) => crate::Scalar::I32,
+ Self::U64(_) => crate::Scalar::U64,
Self::I64(_) => crate::Scalar::I64,
Self::Bool(_) => crate::Scalar::BOOL,
Self::AbstractInt(_) => crate::Scalar::ABSTRACT_INT,
diff --git a/third_party/rust/naga/src/valid/expression.rs b/third_party/rust/naga/src/valid/expression.rs
index c82d60f062..838ecc4e27 100644
--- a/third_party/rust/naga/src/valid/expression.rs
+++ b/third_party/rust/naga/src/valid/expression.rs
@@ -124,6 +124,8 @@ pub enum ExpressionError {
MissingCapabilities(super::Capabilities),
#[error(transparent)]
Literal(#[from] LiteralError),
+ #[error("{0:?} is not supported for Width {2} {1:?} arguments yet, see https://github.com/gfx-rs/wgpu/issues/5276")]
+ UnsupportedWidth(crate::MathFunction, crate::ScalarKind, crate::Bytes),
}
#[derive(Clone, Debug, thiserror::Error)]
@@ -1332,28 +1334,29 @@ impl super::Validator {
_ => return Err(ExpressionError::InvalidArgumentType(fun, 0, arg)),
}
}
- Mf::CountTrailingZeros
- | Mf::CountLeadingZeros
+ // Remove once fixed https://github.com/gfx-rs/wgpu/issues/5276
+ Mf::CountLeadingZeros
+ | Mf::CountTrailingZeros
| Mf::CountOneBits
| Mf::ReverseBits
- | Mf::FindLsb
- | Mf::FindMsb => {
+ | Mf::FindMsb
+ | Mf::FindLsb => {
if arg1_ty.is_some() || arg2_ty.is_some() || arg3_ty.is_some() {
return Err(ExpressionError::WrongArgumentCount(fun));
}
match *arg_ty {
- Ti::Scalar(Sc {
- kind: Sk::Sint | Sk::Uint,
- ..
- })
- | Ti::Vector {
- scalar:
- Sc {
- kind: Sk::Sint | Sk::Uint,
- ..
- },
- ..
- } => {}
+ Ti::Scalar(scalar) | Ti::Vector { scalar, .. } => match scalar.kind {
+ Sk::Sint | Sk::Uint => {
+ if scalar.width != 4 {
+ return Err(ExpressionError::UnsupportedWidth(
+ fun,
+ scalar.kind,
+ scalar.width,
+ ));
+ }
+ }
+ _ => return Err(ExpressionError::InvalidArgumentType(fun, 0, arg)),
+ },
_ => return Err(ExpressionError::InvalidArgumentType(fun, 0, arg)),
}
}
@@ -1404,6 +1407,21 @@ impl super::Validator {
))
}
}
+ // Remove once fixed https://github.com/gfx-rs/wgpu/issues/5276
+ for &arg in [arg_ty, arg1_ty, arg2_ty, arg3_ty].iter() {
+ match *arg {
+ Ti::Scalar(scalar) | Ti::Vector { scalar, .. } => {
+ if scalar.width != 4 {
+ return Err(ExpressionError::UnsupportedWidth(
+ fun,
+ scalar.kind,
+ scalar.width,
+ ));
+ }
+ }
+ _ => {}
+ }
+ }
}
Mf::ExtractBits => {
let (arg1_ty, arg2_ty) = match (arg1_ty, arg2_ty, arg3_ty) {
@@ -1445,6 +1463,21 @@ impl super::Validator {
))
}
}
+ // Remove once fixed https://github.com/gfx-rs/wgpu/issues/5276
+ for &arg in [arg_ty, arg1_ty, arg2_ty].iter() {
+ match *arg {
+ Ti::Scalar(scalar) | Ti::Vector { scalar, .. } => {
+ if scalar.width != 4 {
+ return Err(ExpressionError::UnsupportedWidth(
+ fun,
+ scalar.kind,
+ scalar.width,
+ ));
+ }
+ }
+ _ => {}
+ }
+ }
}
Mf::Pack2x16unorm | Mf::Pack2x16snorm | Mf::Pack2x16float => {
if arg1_ty.is_some() || arg2_ty.is_some() || arg3_ty.is_some() {
diff --git a/third_party/rust/naga/src/valid/mod.rs b/third_party/rust/naga/src/valid/mod.rs
index 388495a3ac..5459434f33 100644
--- a/third_party/rust/naga/src/valid/mod.rs
+++ b/third_party/rust/naga/src/valid/mod.rs
@@ -28,7 +28,7 @@ pub use expression::{check_literal_value, LiteralError};
pub use expression::{ConstExpressionError, ExpressionError};
pub use function::{CallError, FunctionError, LocalVariableError};
pub use interface::{EntryPointError, GlobalVariableError, VaryingError};
-pub use r#type::{Disalignment, TypeError, TypeFlags};
+pub use r#type::{Disalignment, TypeError, TypeFlags, WidthError};
use self::handles::InvalidHandleError;
@@ -108,6 +108,8 @@ bitflags::bitflags! {
const DUAL_SOURCE_BLENDING = 0x2000;
/// Support for arrayed cube textures.
const CUBE_ARRAY_TEXTURES = 0x4000;
+ /// Support for 64-bit signed and unsigned integers.
+ const SHADER_INT64 = 0x8000;
}
}
diff --git a/third_party/rust/naga/src/valid/type.rs b/third_party/rust/naga/src/valid/type.rs
index 1e3e03fe19..d44a295b1a 100644
--- a/third_party/rust/naga/src/valid/type.rs
+++ b/third_party/rust/naga/src/valid/type.rs
@@ -107,6 +107,12 @@ pub enum TypeError {
MatrixElementNotFloat,
#[error("The constant {0:?} is specialized, and cannot be used as an array size")]
UnsupportedSpecializedArrayLength(Handle<crate::Constant>),
+ #[error("{} of dimensionality {dim:?} and class {class:?} are not supported", if *.arrayed {"Arrayed images"} else {"Images"})]
+ UnsupportedImageType {
+ dim: crate::ImageDimension,
+ arrayed: bool,
+ class: crate::ImageClass,
+ },
#[error("Array stride {stride} does not match the expected {expected}")]
InvalidArrayStride { stride: u32, expected: u32 },
#[error("Field '{0}' can't be dynamically-sized, has type {1:?}")]
@@ -141,9 +147,6 @@ pub enum WidthError {
flag: &'static str,
},
- #[error("64-bit integers are not yet supported")]
- Unsupported64Bit,
-
#[error("Abstract types may only appear in constant expressions")]
Abstract,
}
@@ -245,11 +248,31 @@ impl super::Validator {
scalar.width == 4
}
}
- crate::ScalarKind::Sint | crate::ScalarKind::Uint => {
+ crate::ScalarKind::Sint => {
+ if scalar.width == 8 {
+ if !self.capabilities.contains(Capabilities::SHADER_INT64) {
+ return Err(WidthError::MissingCapability {
+ name: "i64",
+ flag: "SHADER_INT64",
+ });
+ }
+ true
+ } else {
+ scalar.width == 4
+ }
+ }
+ crate::ScalarKind::Uint => {
if scalar.width == 8 {
- return Err(WidthError::Unsupported64Bit);
+ if !self.capabilities.contains(Capabilities::SHADER_INT64) {
+ return Err(WidthError::MissingCapability {
+ name: "u64",
+ flag: "SHADER_INT64",
+ });
+ }
+ true
+ } else {
+ scalar.width == 4
}
- scalar.width == 4
}
crate::ScalarKind::AbstractInt | crate::ScalarKind::AbstractFloat => {
return Err(WidthError::Abstract);
@@ -596,8 +619,15 @@ impl super::Validator {
Ti::Image {
dim,
arrayed,
- class: _,
+ class,
} => {
+ if arrayed && matches!(dim, crate::ImageDimension::D3) {
+ return Err(TypeError::UnsupportedImageType {
+ dim,
+ arrayed,
+ class,
+ });
+ }
if arrayed && matches!(dim, crate::ImageDimension::Cube) {
self.require_type_capability(Capabilities::CUBE_ARRAY_TEXTURES)?;
}
diff --git a/third_party/rust/neqo-common/.cargo-checksum.json b/third_party/rust/neqo-common/.cargo-checksum.json
index f8b692fdfe..e7daca1191 100644
--- a/third_party/rust/neqo-common/.cargo-checksum.json
+++ b/third_party/rust/neqo-common/.cargo-checksum.json
@@ -1 +1 @@
-{"files":{"Cargo.toml":"dbb5500f87df7aee6e680ac210ddb56b833aa82d6be5c407474de0895cee14e9","build.rs":"a17b1bb1bd3de3fc958f72d4d1357f7bc4432faa26640c95b5fbfccf40579d67","src/codec.rs":"8c14f09864b095e28ff52e7d96a12a6591fc9c4b20a9cafca6720d132c80efdc","src/datagram.rs":"1a7028d96a2e7385e94265de53189eb824b7cf12e0e2de5d67c3f3f8751b6043","src/event.rs":"4ef9e6f3f5168f2eacb7be982e062e743c64a64e809765d2139122839aa407e5","src/header.rs":"467b947f78bfe354d8bb51e8df0c2be69e75a45e2be688d81f0d268aa77c89ef","src/hrtime.rs":"d7c8849e9ec7a312878ea2bc28939717fa03969fb9aee259a4a516351ee37643","src/incrdecoder.rs":"577c32b9ace51f2daaf940be6d0c391c4f55cd42ef6848c68c1ffc970d8c57b5","src/lib.rs":"47c14084c6d475ebb855f3ed9302b31fa42780b93a816bf098c96987ffe33572","src/log.rs":"c68099eae0e9014be35173ac802165b128433d973390e1111c08df56e71df063","src/qlog.rs":"3f43dc4e5fdccb9d6ee74d9e7b3ff29da63e4eb9f631e4e35446e452d8ec7af6","src/timer.rs":"50a2de20933b7b5884337aded69e59e2523503481308f25de1bba1a11d505be8","src/tos.rs":"5b5a61c699266716afce2f5bda7c98151db3223ede41ce451c390863198e30a2","tests/log.rs":"480b165b7907ec642c508b303d63005eee1427115d6973a349eaf6b2242ed18d"},"package":null} \ No newline at end of file
+{"files":{"Cargo.toml":"b49758e5e8f0a6955d761e689be39530f193f7089de07f2295a7a3aef4df5898","build.rs":"306b2f909a25ae38daf5404a4e128d2a94e8975b70870864c2a71cafec9717c7","src/codec.rs":"fd239f75d374db6ff744211344c82bcd19ecf753e07410e1fe37732bbb81dfe9","src/datagram.rs":"f2ff56faa0e513edbf4331b6ee2c9e6d6111483bda7aff08d16b9f05bce5c320","src/event.rs":"106ca6c4afb107fa49a1bc72f5eb4ae95f4baa1ba19736aa38c8ba973774c160","src/header.rs":"467b947f78bfe354d8bb51e8df0c2be69e75a45e2be688d81f0d268aa77c89ef","src/hrtime.rs":"112dc758e65301b8a7a508b125d3d61063180d432bffaec566a050d4f907ab18","src/incrdecoder.rs":"577c32b9ace51f2daaf940be6d0c391c4f55cd42ef6848c68c1ffc970d8c57b5","src/lib.rs":"a86aae69900933bf83044fa96166ee51216277415eafcdb15c04a907bb2dd10e","src/log.rs":"7246053bffd704b264d42fc82f986b9d62079472a76a9fc3749c25cfc7698532","src/qlog.rs":"9b081f32bf158fd340300693acc97fe0554b617ae664eba86e4d3572e2b1e16e","src/timer.rs":"350a730cc5a159dfdac5d78ec8e8a34c5172a476d827a566703edec24c791842","src/tos.rs":"440616cb0aee9082abe00623b33e68dbe80eda47aec889ac5f4145b1566bf692","src/udp.rs":"2b92132e078791e35b66f68d99d79ff5df55efd03e788474f7781a00403a5533","tests/log.rs":"a11e21fb570258ca93bb40e3923817d381e1e605accbc3aed1df5a0a9918b41d"},"package":null} \ No newline at end of file
diff --git a/third_party/rust/neqo-common/Cargo.toml b/third_party/rust/neqo-common/Cargo.toml
index b04537bb0a..dc5bed385f 100644
--- a/third_party/rust/neqo-common/Cargo.toml
+++ b/third_party/rust/neqo-common/Cargo.toml
@@ -10,17 +10,22 @@
# See Cargo.toml.orig for the original contents.
[package]
-edition = "2018"
-rust-version = "1.70.0"
+edition = "2021"
+rust-version = "1.74.0"
name = "neqo-common"
-version = "0.7.0"
-authors = ["Bobby Holley <bobbyholley@gmail.com>"]
+version = "0.7.2"
+authors = ["The Neqo Authors <necko@mozilla.com>"]
build = "build.rs"
+homepage = "https://github.com/mozilla/neqo/"
license = "MIT OR Apache-2.0"
+repository = "https://github.com/mozilla/neqo/"
-[dependencies]
-enum-map = "2.7"
-lazy_static = "1.4"
+[lib]
+bench = false
+
+[dependencies.enum-map]
+version = "2.7"
+default-features = false
[dependencies.env_logger]
version = "0.10"
@@ -31,20 +36,45 @@ version = "0.4"
default-features = false
[dependencies.qlog]
-git = "https://github.com/cloudflare/quiche"
-rev = "09ea4b244096a013071cfe2175bbf2945fb7f8d1"
+version = "0.12"
+default-features = false
+
+[dependencies.quinn-udp]
+git = "https://github.com/quinn-rs/quinn/"
+rev = "a947962131aba8a6521253d03cc948b20098a2d6"
+optional = true
[dependencies.time]
-version = "0.3.23"
+version = "0.3"
features = ["formatting"]
+default-features = false
+
+[dependencies.tokio]
+version = "1"
+features = [
+ "net",
+ "time",
+ "macros",
+ "rt",
+ "rt-multi-thread",
+]
+optional = true
+default-features = false
[dev-dependencies.test-fixture]
path = "../test-fixture"
[features]
ci = []
-deny-warnings = []
+udp = [
+ "dep:quinn-udp",
+ "dep:tokio",
+]
[target."cfg(windows)".dependencies.winapi]
version = "0.3"
features = ["timeapi"]
+
+[lints.clippy.pedantic]
+level = "warn"
+priority = -1
diff --git a/third_party/rust/neqo-common/build.rs b/third_party/rust/neqo-common/build.rs
index 0af1a1dbbd..9047b1f5d0 100644
--- a/third_party/rust/neqo-common/build.rs
+++ b/third_party/rust/neqo-common/build.rs
@@ -1,3 +1,9 @@
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
use std::env;
fn main() {
diff --git a/third_party/rust/neqo-common/src/codec.rs b/third_party/rust/neqo-common/src/codec.rs
index 57ff13f39f..7fea2f71ab 100644
--- a/third_party/rust/neqo-common/src/codec.rs
+++ b/third_party/rust/neqo-common/src/codec.rs
@@ -4,7 +4,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-use std::{convert::TryFrom, fmt::Debug};
+use std::fmt::Debug;
use crate::hex_with_len;
@@ -112,9 +112,7 @@ impl<'a> Decoder<'a> {
/// Decodes a QUIC varint.
pub fn decode_varint(&mut self) -> Option<u64> {
- let Some(b1) = self.decode_byte() else {
- return None;
- };
+ let b1 = self.decode_byte()?;
match b1 >> 6 {
0 => Some(u64::from(b1 & 0x3f)),
1 => Some((u64::from(b1 & 0x3f) << 8) | self.decode_uint(1)?),
diff --git a/third_party/rust/neqo-common/src/datagram.rs b/third_party/rust/neqo-common/src/datagram.rs
index 1729c8ed8d..04ba1a45a1 100644
--- a/third_party/rust/neqo-common/src/datagram.rs
+++ b/third_party/rust/neqo-common/src/datagram.rs
@@ -53,6 +53,12 @@ impl Datagram {
pub fn ttl(&self) -> Option<u8> {
self.ttl
}
+
+ #[cfg(feature = "udp")]
+ #[must_use]
+ pub(crate) fn into_data(self) -> Vec<u8> {
+ self.d
+ }
}
impl Deref for Datagram {
diff --git a/third_party/rust/neqo-common/src/event.rs b/third_party/rust/neqo-common/src/event.rs
index 26052b7571..ea8d491822 100644
--- a/third_party/rust/neqo-common/src/event.rs
+++ b/third_party/rust/neqo-common/src/event.rs
@@ -4,7 +4,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-use std::{iter::Iterator, marker::PhantomData};
+use std::marker::PhantomData;
/// An event provider is able to generate a stream of events.
pub trait Provider {
diff --git a/third_party/rust/neqo-common/src/hrtime.rs b/third_party/rust/neqo-common/src/hrtime.rs
index 62d2567d42..e70b5f0ffb 100644
--- a/third_party/rust/neqo-common/src/hrtime.rs
+++ b/third_party/rust/neqo-common/src/hrtime.rs
@@ -6,7 +6,6 @@
use std::{
cell::RefCell,
- convert::TryFrom,
rc::{Rc, Weak},
time::Duration,
};
@@ -340,9 +339,7 @@ impl Time {
/// The handle can also be used to update the resolution.
#[must_use]
pub fn get(period: Duration) -> Handle {
- thread_local! {
- static HR_TIME: RefCell<Weak<RefCell<Time>>> = RefCell::default();
- }
+ thread_local!(static HR_TIME: RefCell<Weak<RefCell<Time>>> = RefCell::default());
HR_TIME.with(|r| {
let mut b = r.borrow_mut();
diff --git a/third_party/rust/neqo-common/src/lib.rs b/third_party/rust/neqo-common/src/lib.rs
index 853b05705b..fe88097983 100644
--- a/third_party/rust/neqo-common/src/lib.rs
+++ b/third_party/rust/neqo-common/src/lib.rs
@@ -4,8 +4,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-#![cfg_attr(feature = "deny-warnings", deny(warnings))]
-#![warn(clippy::pedantic)]
+#![allow(clippy::module_name_repetitions)] // This lint doesn't work here.
mod codec;
mod datagram;
@@ -17,6 +16,8 @@ pub mod log;
pub mod qlog;
pub mod timer;
pub mod tos;
+#[cfg(feature = "udp")]
+pub mod udp;
use std::fmt::Write;
diff --git a/third_party/rust/neqo-common/src/log.rs b/third_party/rust/neqo-common/src/log.rs
index d9c30b98b1..c5b89be8a6 100644
--- a/third_party/rust/neqo-common/src/log.rs
+++ b/third_party/rust/neqo-common/src/log.rs
@@ -6,16 +6,19 @@
#![allow(clippy::module_name_repetitions)]
-use std::{io::Write, sync::Once, time::Instant};
+use std::{
+ io::Write,
+ sync::{Once, OnceLock},
+ time::{Duration, Instant},
+};
use env_logger::Builder;
-use lazy_static::lazy_static;
#[macro_export]
macro_rules! do_log {
(target: $target:expr, $lvl:expr, $($arg:tt)+) => ({
let lvl = $lvl;
- if lvl <= ::log::max_level() {
+ if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() {
::log::logger().log(
&::log::Record::builder()
.args(format_args!($($arg)+))
@@ -42,17 +45,22 @@ macro_rules! log_subject {
}};
}
-static INIT_ONCE: Once = Once::new();
-
-lazy_static! {
- static ref START_TIME: Instant = Instant::now();
+fn since_start() -> Duration {
+ static START_TIME: OnceLock<Instant> = OnceLock::new();
+ START_TIME.get_or_init(Instant::now).elapsed()
}
pub fn init() {
+ static INIT_ONCE: Once = Once::new();
+
+ if ::log::STATIC_MAX_LEVEL == ::log::LevelFilter::Off {
+ return;
+ }
+
INIT_ONCE.call_once(|| {
let mut builder = Builder::from_env("RUST_LOG");
builder.format(|buf, record| {
- let elapsed = START_TIME.elapsed();
+ let elapsed = since_start();
writeln!(
buf,
"{}s{:3}ms {} {}",
diff --git a/third_party/rust/neqo-common/src/qlog.rs b/third_party/rust/neqo-common/src/qlog.rs
index 3da8350990..c67ce62afe 100644
--- a/third_party/rust/neqo-common/src/qlog.rs
+++ b/third_party/rust/neqo-common/src/qlog.rs
@@ -12,8 +12,7 @@ use std::{
};
use qlog::{
- self, streamer::QlogStreamer, CommonFields, Configuration, TraceSeq, VantagePoint,
- VantagePointType,
+ streamer::QlogStreamer, CommonFields, Configuration, TraceSeq, VantagePoint, VantagePointType,
};
use crate::Role;
diff --git a/third_party/rust/neqo-common/src/timer.rs b/third_party/rust/neqo-common/src/timer.rs
index e8532af442..a413252e08 100644
--- a/third_party/rust/neqo-common/src/timer.rs
+++ b/third_party/rust/neqo-common/src/timer.rs
@@ -5,7 +5,6 @@
// except according to those terms.
use std::{
- convert::TryFrom,
mem,
time::{Duration, Instant},
};
@@ -247,49 +246,50 @@ impl<T> Timer<T> {
#[cfg(test)]
mod test {
- use lazy_static::lazy_static;
+ use std::sync::OnceLock;
use super::{Duration, Instant, Timer};
- lazy_static! {
- static ref NOW: Instant = Instant::now();
+ fn now() -> Instant {
+ static NOW: OnceLock<Instant> = OnceLock::new();
+ *NOW.get_or_init(Instant::now)
}
const GRANULARITY: Duration = Duration::from_millis(10);
const CAPACITY: usize = 10;
#[test]
fn create() {
- let t: Timer<()> = Timer::new(*NOW, GRANULARITY, CAPACITY);
+ let t: Timer<()> = Timer::new(now(), GRANULARITY, CAPACITY);
assert_eq!(t.span(), Duration::from_millis(100));
assert_eq!(None, t.next_time());
}
#[test]
fn immediate_entry() {
- let mut t = Timer::new(*NOW, GRANULARITY, CAPACITY);
- t.add(*NOW, 12);
- assert_eq!(*NOW, t.next_time().expect("should have an entry"));
- let values: Vec<_> = t.take_until(*NOW).collect();
+ let mut t = Timer::new(now(), GRANULARITY, CAPACITY);
+ t.add(now(), 12);
+ assert_eq!(now(), t.next_time().expect("should have an entry"));
+ let values: Vec<_> = t.take_until(now()).collect();
assert_eq!(vec![12], values);
}
#[test]
fn same_time() {
- let mut t = Timer::new(*NOW, GRANULARITY, CAPACITY);
+ let mut t = Timer::new(now(), GRANULARITY, CAPACITY);
let v1 = 12;
let v2 = 13;
- t.add(*NOW, v1);
- t.add(*NOW, v2);
- assert_eq!(*NOW, t.next_time().expect("should have an entry"));
- let values: Vec<_> = t.take_until(*NOW).collect();
+ t.add(now(), v1);
+ t.add(now(), v2);
+ assert_eq!(now(), t.next_time().expect("should have an entry"));
+ let values: Vec<_> = t.take_until(now()).collect();
assert!(values.contains(&v1));
assert!(values.contains(&v2));
}
#[test]
fn add() {
- let mut t = Timer::new(*NOW, GRANULARITY, CAPACITY);
- let near_future = *NOW + Duration::from_millis(17);
+ let mut t = Timer::new(now(), GRANULARITY, CAPACITY);
+ let near_future = now() + Duration::from_millis(17);
let v = 9;
t.add(near_future, v);
assert_eq!(near_future, t.next_time().expect("should return a value"));
@@ -305,8 +305,8 @@ mod test {
#[test]
fn add_future() {
- let mut t = Timer::new(*NOW, GRANULARITY, CAPACITY);
- let future = *NOW + Duration::from_millis(117);
+ let mut t = Timer::new(now(), GRANULARITY, CAPACITY);
+ let future = now() + Duration::from_millis(117);
let v = 9;
t.add(future, v);
assert_eq!(future, t.next_time().expect("should return a value"));
@@ -315,8 +315,8 @@ mod test {
#[test]
fn add_far_future() {
- let mut t = Timer::new(*NOW, GRANULARITY, CAPACITY);
- let far_future = *NOW + Duration::from_millis(892);
+ let mut t = Timer::new(now(), GRANULARITY, CAPACITY);
+ let far_future = now() + Duration::from_millis(892);
let v = 9;
t.add(far_future, v);
assert_eq!(far_future, t.next_time().expect("should return a value"));
@@ -333,12 +333,12 @@ mod test {
];
fn with_times() -> Timer<usize> {
- let mut t = Timer::new(*NOW, GRANULARITY, CAPACITY);
+ let mut t = Timer::new(now(), GRANULARITY, CAPACITY);
for (i, time) in TIMES.iter().enumerate() {
- t.add(*NOW + *time, i);
+ t.add(now() + *time, i);
}
assert_eq!(
- *NOW + *TIMES.iter().min().unwrap(),
+ now() + *TIMES.iter().min().unwrap(),
t.next_time().expect("should have a time")
);
t
@@ -348,7 +348,7 @@ mod test {
#[allow(clippy::needless_collect)] // false positive
fn multiple_values() {
let mut t = with_times();
- let values: Vec<_> = t.take_until(*NOW + *TIMES.iter().max().unwrap()).collect();
+ let values: Vec<_> = t.take_until(now() + *TIMES.iter().max().unwrap()).collect();
for i in 0..TIMES.len() {
assert!(values.contains(&i));
}
@@ -358,7 +358,7 @@ mod test {
#[allow(clippy::needless_collect)] // false positive
fn take_far_future() {
let mut t = with_times();
- let values: Vec<_> = t.take_until(*NOW + Duration::from_secs(100)).collect();
+ let values: Vec<_> = t.take_until(now() + Duration::from_secs(100)).collect();
for i in 0..TIMES.len() {
assert!(values.contains(&i));
}
@@ -368,15 +368,15 @@ mod test {
fn remove_each() {
let mut t = with_times();
for (i, time) in TIMES.iter().enumerate() {
- assert_eq!(Some(i), t.remove(*NOW + *time, |&x| x == i));
+ assert_eq!(Some(i), t.remove(now() + *time, |&x| x == i));
}
assert_eq!(None, t.next_time());
}
#[test]
fn remove_future() {
- let mut t = Timer::new(*NOW, GRANULARITY, CAPACITY);
- let future = *NOW + Duration::from_millis(117);
+ let mut t = Timer::new(now(), GRANULARITY, CAPACITY);
+ let future = now() + Duration::from_millis(117);
let v = 9;
t.add(future, v);
@@ -385,9 +385,9 @@ mod test {
#[test]
fn remove_too_far_future() {
- let mut t = Timer::new(*NOW, GRANULARITY, CAPACITY);
- let future = *NOW + Duration::from_millis(117);
- let too_far_future = *NOW + t.span() + Duration::from_millis(117);
+ let mut t = Timer::new(now(), GRANULARITY, CAPACITY);
+ let future = now() + Duration::from_millis(117);
+ let too_far_future = now() + t.span() + Duration::from_millis(117);
let v = 9;
t.add(future, v);
diff --git a/third_party/rust/neqo-common/src/tos.rs b/third_party/rust/neqo-common/src/tos.rs
index aa360d1d53..3610f72750 100644
--- a/third_party/rust/neqo-common/src/tos.rs
+++ b/third_party/rust/neqo-common/src/tos.rs
@@ -46,6 +46,12 @@ impl From<u8> for IpTosEcn {
}
}
+impl From<IpTos> for IpTosEcn {
+ fn from(value: IpTos) -> Self {
+ IpTosEcn::from(value.0 & 0x3)
+ }
+}
+
/// Diffserv Codepoints, mapped to the upper six bits of the TOS field.
/// <https://www.iana.org/assignments/dscp-registry/dscp-registry.xhtml>
#[derive(Copy, Clone, PartialEq, Eq, Enum, Default, Debug)]
@@ -159,6 +165,12 @@ impl From<u8> for IpTosDscp {
}
}
+impl From<IpTos> for IpTosDscp {
+ fn from(value: IpTos) -> Self {
+ IpTosDscp::from(value.0 & 0xfc)
+ }
+}
+
/// The type-of-service field in an IP packet.
#[allow(clippy::module_name_repetitions)]
#[derive(Copy, Clone, PartialEq, Eq)]
@@ -169,22 +181,37 @@ impl From<IpTosEcn> for IpTos {
Self(u8::from(v))
}
}
+
impl From<IpTosDscp> for IpTos {
fn from(v: IpTosDscp) -> Self {
Self(u8::from(v))
}
}
+
impl From<(IpTosDscp, IpTosEcn)> for IpTos {
fn from(v: (IpTosDscp, IpTosEcn)) -> Self {
Self(u8::from(v.0) | u8::from(v.1))
}
}
+
+impl From<(IpTosEcn, IpTosDscp)> for IpTos {
+ fn from(v: (IpTosEcn, IpTosDscp)) -> Self {
+ Self(u8::from(v.0) | u8::from(v.1))
+ }
+}
+
impl From<IpTos> for u8 {
fn from(v: IpTos) -> Self {
v.0
}
}
+impl From<u8> for IpTos {
+ fn from(v: u8) -> Self {
+ Self(v)
+ }
+}
+
impl Debug for IpTos {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("IpTos")
@@ -202,7 +229,7 @@ impl Default for IpTos {
#[cfg(test)]
mod tests {
- use super::*;
+ use crate::{IpTos, IpTosDscp, IpTosEcn};
#[test]
fn iptosecn_into_u8() {
@@ -287,4 +314,12 @@ mod tests {
let iptos_dscp: IpTos = dscp.into();
assert_eq!(u8::from(iptos_dscp), dscp as u8);
}
+
+ #[test]
+ fn u8_to_iptos() {
+ let tos = 0x8b;
+ let iptos: IpTos = (IpTosEcn::Ce, IpTosDscp::Af41).into();
+ assert_eq!(tos, u8::from(iptos));
+ assert_eq!(IpTos::from(tos), iptos);
+ }
}
diff --git a/third_party/rust/neqo-common/src/udp.rs b/third_party/rust/neqo-common/src/udp.rs
new file mode 100644
index 0000000000..c27b0632ff
--- /dev/null
+++ b/third_party/rust/neqo-common/src/udp.rs
@@ -0,0 +1,222 @@
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![allow(clippy::missing_errors_doc)] // Functions simply delegate to tokio and quinn-udp.
+#![allow(clippy::missing_panics_doc)] // Functions simply delegate to tokio and quinn-udp.
+
+use std::{
+ io::{self, IoSliceMut},
+ net::{SocketAddr, ToSocketAddrs},
+ slice,
+};
+
+use quinn_udp::{EcnCodepoint, RecvMeta, Transmit, UdpSocketState};
+use tokio::io::Interest;
+
+use crate::{Datagram, IpTos};
+
+/// Socket receive buffer size.
+///
+/// Allows reading multiple datagrams in a single [`Socket::recv`] call.
+const RECV_BUF_SIZE: usize = u16::MAX as usize;
+
+pub struct Socket {
+ socket: tokio::net::UdpSocket,
+ state: UdpSocketState,
+ recv_buf: Vec<u8>,
+}
+
+impl Socket {
+ /// Calls [`std::net::UdpSocket::bind`] and instantiates [`quinn_udp::UdpSocketState`].
+ pub fn bind<A: ToSocketAddrs>(addr: A) -> Result<Self, io::Error> {
+ let socket = std::net::UdpSocket::bind(addr)?;
+
+ Ok(Self {
+ state: quinn_udp::UdpSocketState::new((&socket).into())?,
+ socket: tokio::net::UdpSocket::from_std(socket)?,
+ recv_buf: vec![0; RECV_BUF_SIZE],
+ })
+ }
+
+ /// See [`tokio::net::UdpSocket::local_addr`].
+ pub fn local_addr(&self) -> io::Result<SocketAddr> {
+ self.socket.local_addr()
+ }
+
+ /// See [`tokio::net::UdpSocket::writable`].
+ pub async fn writable(&self) -> Result<(), io::Error> {
+ self.socket.writable().await
+ }
+
+ /// See [`tokio::net::UdpSocket::readable`].
+ pub async fn readable(&self) -> Result<(), io::Error> {
+ self.socket.readable().await
+ }
+
+ /// Send the UDP datagram on the specified socket.
+ pub fn send(&self, d: Datagram) -> io::Result<usize> {
+ let transmit = Transmit {
+ destination: d.destination(),
+ ecn: EcnCodepoint::from_bits(Into::<u8>::into(d.tos())),
+ contents: d.into_data().into(),
+ segment_size: None,
+ src_ip: None,
+ };
+
+ let n = self.socket.try_io(Interest::WRITABLE, || {
+ self.state
+ .send((&self.socket).into(), slice::from_ref(&transmit))
+ })?;
+
+ assert_eq!(n, 1, "only passed one slice");
+
+ Ok(n)
+ }
+
+ /// Receive a UDP datagram on the specified socket.
+ pub fn recv(&mut self, local_address: &SocketAddr) -> Result<Vec<Datagram>, io::Error> {
+ let mut meta = RecvMeta::default();
+
+ match self.socket.try_io(Interest::READABLE, || {
+ self.state.recv(
+ (&self.socket).into(),
+ &mut [IoSliceMut::new(&mut self.recv_buf)],
+ slice::from_mut(&mut meta),
+ )
+ }) {
+ Ok(n) => {
+ assert_eq!(n, 1, "only passed one slice");
+ }
+ Err(ref err)
+ if err.kind() == io::ErrorKind::WouldBlock
+ || err.kind() == io::ErrorKind::Interrupted =>
+ {
+ return Ok(vec![])
+ }
+ Err(err) => {
+ return Err(err);
+ }
+ };
+
+ if meta.len == 0 {
+ eprintln!("zero length datagram received?");
+ return Ok(vec![]);
+ }
+ if meta.len == self.recv_buf.len() {
+ eprintln!(
+ "Might have received more than {} bytes",
+ self.recv_buf.len()
+ );
+ }
+
+ Ok(self.recv_buf[0..meta.len]
+ .chunks(meta.stride.min(self.recv_buf.len()))
+ .map(|d| {
+ Datagram::new(
+ meta.addr,
+ *local_address,
+ meta.ecn.map(|n| IpTos::from(n as u8)).unwrap_or_default(),
+ None, // TODO: get the real TTL https://github.com/quinn-rs/quinn/issues/1749
+ d,
+ )
+ })
+ .collect())
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::{IpTosDscp, IpTosEcn};
+
+ #[tokio::test]
+ async fn datagram_tos() -> Result<(), io::Error> {
+ let sender = Socket::bind("127.0.0.1:0")?;
+ let receiver_addr: SocketAddr = "127.0.0.1:0".parse().unwrap();
+ let mut receiver = Socket::bind(receiver_addr)?;
+
+ let datagram = Datagram::new(
+ sender.local_addr()?,
+ receiver.local_addr()?,
+ IpTos::from((IpTosDscp::Le, IpTosEcn::Ect1)),
+ None,
+ "Hello, world!".as_bytes().to_vec(),
+ );
+
+ sender.writable().await?;
+ sender.send(datagram.clone())?;
+
+ receiver.readable().await?;
+ let received_datagram = receiver
+ .recv(&receiver_addr)
+ .expect("receive to succeed")
+ .into_iter()
+ .next()
+ .expect("receive to yield datagram");
+
+ // Assert that the ECN is correct.
+ assert_eq!(
+ IpTosEcn::from(datagram.tos()),
+ IpTosEcn::from(received_datagram.tos())
+ );
+
+ Ok(())
+ }
+
+ /// Expect [`Socket::recv`] to handle multiple [`Datagram`]s on GRO read.
+ #[tokio::test]
+ #[cfg_attr(not(any(target_os = "linux", target_os = "windows")), ignore)]
+ async fn many_datagrams_through_gro() -> Result<(), io::Error> {
+ const SEGMENT_SIZE: usize = 128;
+
+ let sender = Socket::bind("127.0.0.1:0")?;
+ let receiver_addr: SocketAddr = "127.0.0.1:0".parse().unwrap();
+ let mut receiver = Socket::bind(receiver_addr)?;
+
+ // `neqo_common::udp::Socket::send` does not yet
+ // (https://github.com/mozilla/neqo/issues/1693) support GSO. Use
+ // `quinn_udp` directly.
+ let max_gso_segments = sender.state.max_gso_segments();
+ let msg = vec![0xAB; SEGMENT_SIZE * max_gso_segments];
+ let transmit = Transmit {
+ destination: receiver.local_addr()?,
+ ecn: EcnCodepoint::from_bits(Into::<u8>::into(IpTos::from((
+ IpTosDscp::Le,
+ IpTosEcn::Ect1,
+ )))),
+ contents: msg.clone().into(),
+ segment_size: Some(SEGMENT_SIZE),
+ src_ip: None,
+ };
+ sender.writable().await?;
+ let n = sender.socket.try_io(Interest::WRITABLE, || {
+ sender
+ .state
+ .send((&sender.socket).into(), slice::from_ref(&transmit))
+ })?;
+ assert_eq!(n, 1, "only passed one slice");
+
+ // Allow for one GSO sendmmsg to result in multiple GRO recvmmsg.
+ let mut num_received = 0;
+ while num_received < max_gso_segments {
+ receiver.readable().await?;
+ receiver
+ .recv(&receiver_addr)
+ .expect("receive to succeed")
+ .into_iter()
+ .for_each(|d| {
+ assert_eq!(
+ SEGMENT_SIZE,
+ d.len(),
+ "Expect received datagrams to have same length as sent datagrams."
+ );
+ num_received += 1;
+ });
+ }
+
+ Ok(())
+ }
+}
diff --git a/third_party/rust/neqo-common/tests/log.rs b/third_party/rust/neqo-common/tests/log.rs
index 33b42d1411..135a667146 100644
--- a/third_party/rust/neqo-common/tests/log.rs
+++ b/third_party/rust/neqo-common/tests/log.rs
@@ -4,9 +4,6 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-#![cfg_attr(feature = "deny-warnings", deny(warnings))]
-#![warn(clippy::use_self)]
-
use neqo_common::{qdebug, qerror, qinfo, qtrace, qwarn};
#[test]
diff --git a/third_party/rust/neqo-crypto/.cargo-checksum.json b/third_party/rust/neqo-crypto/.cargo-checksum.json
index ff4ab0fc66..5622e7f4ad 100644
--- a/third_party/rust/neqo-crypto/.cargo-checksum.json
+++ b/third_party/rust/neqo-crypto/.cargo-checksum.json
@@ -1 +1 @@
-{"files":{"Cargo.toml":"7f7348b55033e19bbe51b07ee50313c87237fe09b56b338af9ab24e00aab32c6","bindings/bindings.toml":"0660c1661318b8a5094834c2f1bb12266287ef467307f66947eff7762528f70a","bindings/mozpkix.hpp":"77072c8bb0f6eb6bfe8cbadc111dcd92e0c79936d13f2e501aae1e5d289a6675","bindings/nspr_err.h":"2d5205d017b536c2d838bcf9bc4ec79f96dd50e7bb9b73892328781f1ee6629d","bindings/nspr_error.h":"e41c03c77b8c22046f8618832c9569fbcc7b26d8b9bbc35eea7168f35e346889","bindings/nspr_io.h":"085b289849ef0e77f88512a27b4d9bdc28252bd4d39c6a17303204e46ef45f72","bindings/nspr_time.h":"2e637fd338a5cf0fd3fb0070a47f474a34c2a7f4447f31b6875f5a9928d0a261","bindings/nss_ciphers.h":"95ec6344a607558b3c5ba8510f463b6295f3a2fb3f538a01410531045a5f62d1","bindings/nss_init.h":"ef49045063782fb612aff459172cc6a89340f15005808608ade5320ca9974310","bindings/nss_p11.h":"0b81e64fe6db49b2ecff94edd850be111ef99ec11220e88ceb1c67be90143a78","bindings/nss_secerr.h":"713e8368bdae5159af7893cfa517dabfe5103cede051dee9c9557c850a2defc6","bindings/nss_ssl.h":"af222fb957b989e392e762fa2125c82608a0053aff4fb97e556691646c88c335","bindings/nss_sslerr.h":"24b97f092183d8486f774cdaef5030d0249221c78343570d83a4ee5b594210ae","bindings/nss_sslopt.h":"b7807eb7abdad14db6ad7bc51048a46b065a0ea65a4508c95a12ce90e59d1eea","build.rs":"e712c16cb830a83eb4ea1f50dd341a4c30e1cce95d8c45af97030bc8ad0ae829","src/aead.rs":"b7cda4b89298cfd122cd2e1e94c462840e966c60f4832eb441106563ac332e00","src/aead_fuzzing.rs":"c3e590572314e0bb3fafa13dac3c831358b8a7b5570fe9cfe592752fce8cbdee","src/agent.rs":"c4fe47f9f5b0af20e3418da2e2ddce0ac2ca9665c0502115904f66a554e486ee","src/agentio.rs":"847ac63f6406e33bf20a861cadbfe6301ffa15bd73a5291298ffa93511b87dd5","src/auth.rs":"ced1a18f691894984244088020ea25dc1ee678603317f0c7dfc8b8842fa750b4","src/cert.rs":"6fc09012f994300ff4a7951bf8981aa266220521f58b8ff0989fee6dc1f27df9","src/constants.rs":"f22bf16bd8cb539862cb1e47138dbba79e93fe738f4b907e465891326f98883c","src/ech.rs":"58b7e0a1d2d52c59889cf8b735902577f7c3df93dfb89c72af2646b7aef29f39","src/err.rs":"fca0222167883231a5e0a569a593f44214501819adf5aadf814be27891c87c24","src/exp.rs":"cec59d61fc95914f9703d2fb6490a8507af993c9db710dde894f2f8fd38123c7","src/ext.rs":"c6ab9aefbbca531466dea938d853b1e42ed51816238afe400b20dbdb0111690b","src/hkdf.rs":"8e6cc5dce0f36efa4e13f5a24e2879bdbf10fb9a2b7dc8f13692e47d8959cdc8","src/hp.rs":"62ec073d99cf8bf3a123838c7d9b51bfdf68887148961f6307288e8dd56ac711","src/lib.rs":"40d9ac97c307c8161c2bf48156cc82377f81ad6e709f99cfd7dc0131dc192f86","src/once.rs":"b9850384899a1a016e839743d3489c0d4d916e1973746ef8c89872105d7d9736","src/p11.rs":"6c0f2f1b18e9bf9088a5ca5bdc99e789bb42234f7d2fe24d0b463bc957cb84a2","src/prio.rs":"e5e169296c0ac69919c59fb6c1f8bd6bf079452eaa13d75da0edd41d435d3f6f","src/replay.rs":"1ff4a12f6135ef2c42aef2b0947e26fd6241cd4b359020245608046452a7fcb0","src/result.rs":"0587cbb6aace71a7f9765ef7c01dcd9f73a49dcc6331e1d8fe4de2aef6ca65b6","src/secrets.rs":"4ffaa66f25df47dadf042063bff5953effa7bf2f4920cafe827757d6a659cb58","src/selfencrypt.rs":"4d2f4a6ea0fc94502130413ab5e2ea82612228f38a96a1865bf7d2b3f440620e","src/ssl.rs":"c83baa5518b81dd06f2e4072ea3c2d666ccdeb8b1ff6e3746eea9f1af47023a6","src/time.rs":"9204f3a384fb9dd2c3816c88666ad61ac3538f9e2f028954e81fd335a1479070","tests/aead.rs":"efdb92a060ca1957d890da1604513369559cb43195ee54149ed3ab47958dad59","tests/agent.rs":"0e55354595ae5f0e1ab83731564da57ba88a296e00692147c47df7067a0f416a","tests/ext.rs":"54657b45bd86d2561bb0f548736bc6f141bb664a5b043506f428422919ab95d4","tests/handshake.rs":"40701bc22f16d1ba9b9bd9683738e52b96faafee4119f7057437dae705f7867a","tests/hkdf.rs":"4160978b96505c1f1b7d6c4b5f43536ff7bd791c8746f9546c9fbc0fce5cf1c7","tests/hp.rs":"8eeee21a439e0f991145dff07b01283ae39ccd4b8dac4d011d43a464f73db670","tests/init.rs":"fc9e392b1efa0d8efb28952f73ffc05e5348e7b2b69207b60e375c3888a252a2","tests/selfencrypt.rs":"6edd0914b8466d79ecfb569c6d86995fd364b0dc71be2a0554e82f736ebd6b7c"},"package":null} \ No newline at end of file
+{"files":{"Cargo.toml":"6f1917fbd4cbf53cb4883c30e8fcb9c20f8ebe15e19576c7d37cb6ba0ab9e42b","bindings/bindings.toml":"0660c1661318b8a5094834c2f1bb12266287ef467307f66947eff7762528f70a","bindings/mozpkix.hpp":"77072c8bb0f6eb6bfe8cbadc111dcd92e0c79936d13f2e501aae1e5d289a6675","bindings/nspr_err.h":"2d5205d017b536c2d838bcf9bc4ec79f96dd50e7bb9b73892328781f1ee6629d","bindings/nspr_error.h":"e41c03c77b8c22046f8618832c9569fbcc7b26d8b9bbc35eea7168f35e346889","bindings/nspr_io.h":"085b289849ef0e77f88512a27b4d9bdc28252bd4d39c6a17303204e46ef45f72","bindings/nspr_time.h":"2e637fd338a5cf0fd3fb0070a47f474a34c2a7f4447f31b6875f5a9928d0a261","bindings/nss_ciphers.h":"95ec6344a607558b3c5ba8510f463b6295f3a2fb3f538a01410531045a5f62d1","bindings/nss_init.h":"ef49045063782fb612aff459172cc6a89340f15005808608ade5320ca9974310","bindings/nss_p11.h":"0b81e64fe6db49b2ecff94edd850be111ef99ec11220e88ceb1c67be90143a78","bindings/nss_secerr.h":"713e8368bdae5159af7893cfa517dabfe5103cede051dee9c9557c850a2defc6","bindings/nss_ssl.h":"af222fb957b989e392e762fa2125c82608a0053aff4fb97e556691646c88c335","bindings/nss_sslerr.h":"24b97f092183d8486f774cdaef5030d0249221c78343570d83a4ee5b594210ae","bindings/nss_sslopt.h":"b7807eb7abdad14db6ad7bc51048a46b065a0ea65a4508c95a12ce90e59d1eea","build.rs":"21d9a0140b2afd708583f58f2af0a4ba93ab07ec088680b4cbf0e184aeb8785b","src/aead.rs":"8f50e4557b7829edb67f57c80c777c6ae23c868e2b2eeaaae0736af04dc0d298","src/aead_fuzzing.rs":"c3e590572314e0bb3fafa13dac3c831358b8a7b5570fe9cfe592752fce8cbdee","src/agent.rs":"e995e9cc5108470594bae1b0d4e4bc6b7a8ac2b66488f71ea99e2836c0edbd7e","src/agentio.rs":"c4cb1b3cd92ef53eb0b4fb0b34a597068d82d78ba470dae5821670a0f06c9cda","src/auth.rs":"ced1a18f691894984244088020ea25dc1ee678603317f0c7dfc8b8842fa750b4","src/cert.rs":"8942cb3ce25a61f92b6ffc30fb286052ed6f56eeda3be12fd46ea76ceba6c1cf","src/constants.rs":"f22bf16bd8cb539862cb1e47138dbba79e93fe738f4b907e465891326f98883c","src/ech.rs":"9d322fcc01c0886f1dfe9bb6273cb9f88a746452ac9a802761b1816a05930c1f","src/err.rs":"fca0222167883231a5e0a569a593f44214501819adf5aadf814be27891c87c24","src/exp.rs":"cec59d61fc95914f9703d2fb6490a8507af993c9db710dde894f2f8fd38123c7","src/ext.rs":"cbf7d9f5ecabf4b8c9efd6c334637ab1596ec5266d38ab8d2d6ceae305283deb","src/hkdf.rs":"ef32f20e30a9bd7f094199536d19c87c4231b7fbbe4a9c54c70e84ca9c6575be","src/hp.rs":"644f1bed67f1c6189a67c8d02ab3358aaa7f63af4b913dd7395becbc01a84291","src/lib.rs":"23732c7799be038c0e0835b54e7c40cf6c6536113e0adb6ae3b41b216a6e5220","src/p11.rs":"e8c366def0df470101f3d120dcc4391f74f921fe59e2f3db2a56832e2852b855","src/prio.rs":"e5e169296c0ac69919c59fb6c1f8bd6bf079452eaa13d75da0edd41d435d3f6f","src/replay.rs":"96b7af8eff9e14313e79303092018b12e8834f780c96b8e247c497fdc680c696","src/result.rs":"0587cbb6aace71a7f9765ef7c01dcd9f73a49dcc6331e1d8fe4de2aef6ca65b6","src/secrets.rs":"4ffaa66f25df47dadf042063bff5953effa7bf2f4920cafe827757d6a659cb58","src/selfencrypt.rs":"ac65b13f5bade9d03ab4709364f9ec937fa4ca009965c77ca73b481534a0a470","src/ssl.rs":"c83baa5518b81dd06f2e4072ea3c2d666ccdeb8b1ff6e3746eea9f1af47023a6","src/time.rs":"3b2829a98a1648eb052db19bb470808b6b015a1eca27ab7be64b5d196c0271c0","tests/aead.rs":"3ac4fe4ab79922b5d0191a9717058fc8d0710380ce9b25448095f870f511844f","tests/agent.rs":"824735f88e487a3748200844e9481e81a72163ad74d82faa9aa16594d9b9bb25","tests/ext.rs":"1b047d23d9b224ad06eb65d8f3a7b351e263774e404c79bbcbe8f43790e29c18","tests/handshake.rs":"e892a2839b31414be16e96cdf3b1a65978716094700c1a4989229f7edbf578a0","tests/hkdf.rs":"1d2098dc8398395864baf13e4886cfd1da6d36118727c3b264f457ee3da6b048","tests/hp.rs":"b24fec53771c169be788772532d2617a5349196cf87d6444dc74214f7c73e92c","tests/init.rs":"44fe7626b75ab8c57adfee361bb70a83d5958797e1eb6c4531bb74988ba3a990","tests/selfencrypt.rs":"25813b0c6f32fc8383bb7685745feb750eb3fdc0a6a172a50d961c68d39f2a46"},"package":null} \ No newline at end of file
diff --git a/third_party/rust/neqo-crypto/Cargo.toml b/third_party/rust/neqo-crypto/Cargo.toml
index 73c1fcb364..499921e531 100644
--- a/third_party/rust/neqo-crypto/Cargo.toml
+++ b/third_party/rust/neqo-crypto/Cargo.toml
@@ -10,16 +10,21 @@
# See Cargo.toml.orig for the original contents.
[package]
-edition = "2018"
-rust-version = "1.70.0"
+edition = "2021"
+rust-version = "1.74.0"
name = "neqo-crypto"
-version = "0.7.0"
-authors = ["Martin Thomson <mt@lowentropy.net>"]
+version = "0.7.2"
+authors = ["The Neqo Authors <necko@mozilla.com>"]
build = "build.rs"
+homepage = "https://github.com/mozilla/neqo/"
license = "MIT OR Apache-2.0"
+repository = "https://github.com/mozilla/neqo/"
+
+[lib]
+bench = false
[dependencies.log]
-version = "~0.4.17"
+version = "0.4"
default-features = false
[dependencies.neqo-common]
@@ -28,21 +33,32 @@ path = "../neqo-common"
[dev-dependencies.test-fixture]
path = "../test-fixture"
-[build-dependencies]
-serde = "1.0.195"
-serde_derive = "1.0.195"
-toml = "0.5.11"
-
[build-dependencies.bindgen]
-version = "0.69.1"
+version = "0.69"
features = ["runtime"]
default-features = false
[build-dependencies.mozbuild]
version = "0.1"
optional = true
+default-features = false
+
+[build-dependencies.serde]
+version = "1.0"
+default-features = false
+
+[build-dependencies.serde_derive]
+version = "1.0"
+default-features = false
+
+[build-dependencies.toml]
+version = "0.5"
+default-features = false
[features]
-deny-warnings = []
fuzzing = []
gecko = ["mozbuild"]
+
+[lints.clippy.pedantic]
+level = "warn"
+priority = -1
diff --git a/third_party/rust/neqo-crypto/build.rs b/third_party/rust/neqo-crypto/build.rs
index a63c34dedb..c4c2a73e75 100644
--- a/third_party/rust/neqo-crypto/build.rs
+++ b/third_party/rust/neqo-crypto/build.rs
@@ -4,9 +4,6 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-#![cfg_attr(feature = "deny-warnings", deny(warnings))]
-#![warn(clippy::pedantic)]
-
use std::{
collections::HashMap,
env, fs,
@@ -37,7 +34,7 @@ struct Bindings {
opaque: Vec<String>,
/// enumerations that are turned into a module (without this, the enum is
/// mapped using the default, which means that the individual values are
- /// formed with an underscore as <enum_type>_<enum_value_name>).
+ /// formed with an underscore as <`enum_type`>_<`enum_value_name`>).
#[serde(default)]
enums: Vec<String>,
@@ -53,9 +50,10 @@ struct Bindings {
}
fn is_debug() -> bool {
- env::var("DEBUG")
- .map(|d| d.parse::<bool>().unwrap_or(false))
- .unwrap_or(false)
+ // Check the build profile and not whether debug symbols are enabled (i.e.,
+ // `env::var("DEBUG")`), because we enable those for benchmarking/profiling and still want
+ // to build NSS in release mode.
+ env::var("PROFILE").unwrap_or_default() == "debug"
}
// bindgen needs access to libclang.
@@ -126,7 +124,7 @@ fn nss_dir() -> PathBuf {
}
dir
};
- assert!(dir.is_dir(), "NSS_DIR {:?} doesn't exist", dir);
+ assert!(dir.is_dir(), "NSS_DIR {dir:?} doesn't exist");
// Note that this returns a relative path because UNC
// paths on windows cause certain tools to explode.
dir
@@ -150,10 +148,10 @@ fn build_nss(dir: PathBuf) {
let mut build_nss = vec![
String::from("./build.sh"),
String::from("-Ddisable_tests=1"),
+ // Generate static libraries in addition to shared libraries.
+ String::from("--static"),
];
- if is_debug() {
- build_nss.push(String::from("--static"));
- } else {
+ if !is_debug() {
build_nss.push(String::from("-o"));
}
if let Ok(d) = env::var("NSS_JOBS") {
@@ -318,7 +316,7 @@ fn setup_standalone() -> Vec<String> {
"cargo:rustc-link-search=native={}",
nsslibdir.to_str().unwrap()
);
- if is_debug() {
+ if is_debug() || env::consts::OS == "windows" {
static_link();
} else {
dynamic_link();
diff --git a/third_party/rust/neqo-crypto/src/aead.rs b/third_party/rust/neqo-crypto/src/aead.rs
index a2f009a403..bf7d7fe9d7 100644
--- a/third_party/rust/neqo-crypto/src/aead.rs
+++ b/third_party/rust/neqo-crypto/src/aead.rs
@@ -5,7 +5,6 @@
// except according to those terms.
use std::{
- convert::{TryFrom, TryInto},
fmt,
ops::{Deref, DerefMut},
os::raw::{c_char, c_uint},
diff --git a/third_party/rust/neqo-crypto/src/agent.rs b/third_party/rust/neqo-crypto/src/agent.rs
index cd0bb4cb12..82a6dacd48 100644
--- a/third_party/rust/neqo-crypto/src/agent.rs
+++ b/third_party/rust/neqo-crypto/src/agent.rs
@@ -6,7 +6,6 @@
use std::{
cell::RefCell,
- convert::TryFrom,
ffi::{CStr, CString},
mem::{self, MaybeUninit},
ops::{Deref, DerefMut},
@@ -33,6 +32,7 @@ use crate::{
ech,
err::{is_blocked, secstatus_to_res, Error, PRErrorCode, Res},
ext::{ExtensionHandler, ExtensionTracker},
+ null_safe_slice,
p11::{self, PrivateKey, PublicKey},
prio,
replay::AntiReplay,
@@ -897,7 +897,7 @@ impl Client {
let resumption = arg.cast::<Vec<ResumptionToken>>().as_mut().unwrap();
let len = usize::try_from(len).unwrap();
let mut v = Vec::with_capacity(len);
- v.extend_from_slice(std::slice::from_raw_parts(token, len));
+ v.extend_from_slice(null_safe_slice(token, len));
qinfo!(
[format!("{fd:p}")],
"Got resumption token {}",
@@ -1015,7 +1015,7 @@ pub enum ZeroRttCheckResult {
Accept,
/// Reject 0-RTT, but continue the handshake normally.
Reject,
- /// Send HelloRetryRequest (probably not needed for QUIC).
+ /// Send `HelloRetryRequest` (probably not needed for QUIC).
HelloRetryRequest(Vec<u8>),
/// Fail the handshake.
Fail,
@@ -1105,11 +1105,7 @@ impl Server {
}
let check_state = arg.cast::<ZeroRttCheckState>().as_mut().unwrap();
- let token = if client_token.is_null() {
- &[]
- } else {
- std::slice::from_raw_parts(client_token, usize::try_from(client_token_len).unwrap())
- };
+ let token = null_safe_slice(client_token, usize::try_from(client_token_len).unwrap());
match check_state.checker.check(token) {
ZeroRttCheckResult::Accept => ssl::SSLHelloRetryRequestAction::ssl_hello_retry_accept,
ZeroRttCheckResult::Fail => ssl::SSLHelloRetryRequestAction::ssl_hello_retry_fail,
diff --git a/third_party/rust/neqo-crypto/src/agentio.rs b/third_party/rust/neqo-crypto/src/agentio.rs
index 2bcc540530..7c57a0ef45 100644
--- a/third_party/rust/neqo-crypto/src/agentio.rs
+++ b/third_party/rust/neqo-crypto/src/agentio.rs
@@ -6,13 +6,11 @@
use std::{
cmp::min,
- convert::{TryFrom, TryInto},
fmt, mem,
ops::Deref,
os::raw::{c_uint, c_void},
pin::Pin,
ptr::{null, null_mut},
- vec::Vec,
};
use neqo_common::{hex, hex_with_len, qtrace};
@@ -20,7 +18,7 @@ use neqo_common::{hex, hex_with_len, qtrace};
use crate::{
constants::{ContentType, Epoch},
err::{nspr, Error, PR_SetError, Res},
- prio, ssl,
+ null_safe_slice, prio, ssl,
};
// Alias common types.
@@ -100,7 +98,7 @@ impl RecordList {
) -> ssl::SECStatus {
let records = arg.cast::<Self>().as_mut().unwrap();
- let slice = std::slice::from_raw_parts(data, len as usize);
+ let slice = null_safe_slice(data, len);
records.append(epoch, ContentType::try_from(ct).unwrap(), slice);
ssl::SECSuccess
}
@@ -178,6 +176,7 @@ impl AgentIoInput {
return Err(Error::NoDataAvailable);
}
+ #[allow(clippy::disallowed_methods)] // We just checked if this was empty.
let src = unsafe { std::slice::from_raw_parts(self.input, amount) };
qtrace!([self], "read {}", hex(src));
let dst = unsafe { std::slice::from_raw_parts_mut(buf, amount) };
@@ -232,7 +231,7 @@ impl AgentIo {
// Stage output from TLS into the output buffer.
fn save_output(&mut self, buf: *const u8, count: usize) {
- let slice = unsafe { std::slice::from_raw_parts(buf, count) };
+ let slice = unsafe { null_safe_slice(buf, count) };
qtrace!([self], "save output {}", hex(slice));
self.output.extend_from_slice(slice);
}
diff --git a/third_party/rust/neqo-crypto/src/cert.rs b/third_party/rust/neqo-crypto/src/cert.rs
index 64e63ec71a..2836b5237c 100644
--- a/third_party/rust/neqo-crypto/src/cert.rs
+++ b/third_party/rust/neqo-crypto/src/cert.rs
@@ -4,16 +4,13 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-use std::{
- convert::TryFrom,
- ptr::{addr_of, NonNull},
- slice,
-};
+use std::ptr::{addr_of, NonNull};
use neqo_common::qerror;
use crate::{
err::secstatus_to_res,
+ null_safe_slice,
p11::{CERTCertListNode, CERT_GetCertificateDer, CertList, Item, SECItem, SECItemArray},
ssl::{
PRFileDesc, SSL_PeerCertificateChain, SSL_PeerSignedCertTimestamps,
@@ -24,7 +21,7 @@ use crate::{
pub struct CertificateInfo {
certs: CertList,
cursor: *const CERTCertListNode,
- /// stapled_ocsp_responses and signed_cert_timestamp are properties
+ /// `stapled_ocsp_responses` and `signed_cert_timestamp` are properties
/// associated with each of the certificates. Right now, NSS only
/// reports the value for the end-entity certificate (the first).
stapled_ocsp_responses: Option<Vec<Vec<u8>>>,
@@ -52,7 +49,7 @@ fn stapled_ocsp_responses(fd: *mut PRFileDesc) -> Option<Vec<Vec<u8>>> {
};
for idx in 0..len {
let itemp: *const SECItem = unsafe { ocsp_ptr.as_ref().items.offset(idx).cast() };
- let item = unsafe { slice::from_raw_parts((*itemp).data, (*itemp).len as usize) };
+ let item = unsafe { null_safe_slice((*itemp).data, (*itemp).len) };
ocsp_helper.push(item.to_owned());
}
Some(ocsp_helper)
@@ -68,9 +65,8 @@ fn signed_cert_timestamp(fd: *mut PRFileDesc) -> Option<Vec<u8>> {
if unsafe { sct_ptr.as_ref().len == 0 || sct_ptr.as_ref().data.is_null() } {
Some(Vec::new())
} else {
- let sct_slice = unsafe {
- slice::from_raw_parts(sct_ptr.as_ref().data, sct_ptr.as_ref().len as usize)
- };
+ let sct_slice =
+ unsafe { null_safe_slice(sct_ptr.as_ref().data, sct_ptr.as_ref().len) };
Some(sct_slice.to_owned())
}
}
@@ -105,7 +101,7 @@ impl<'a> Iterator for &'a mut CertificateInfo {
let cert = unsafe { *self.cursor }.cert;
secstatus_to_res(unsafe { CERT_GetCertificateDer(cert, &mut item) })
.expect("getting DER from certificate should work");
- Some(unsafe { std::slice::from_raw_parts(item.data, item.len as usize) })
+ Some(unsafe { null_safe_slice(item.data, item.len) })
}
}
diff --git a/third_party/rust/neqo-crypto/src/ech.rs b/third_party/rust/neqo-crypto/src/ech.rs
index 1f54c4592e..4ff2cda7e8 100644
--- a/third_party/rust/neqo-crypto/src/ech.rs
+++ b/third_party/rust/neqo-crypto/src/ech.rs
@@ -5,7 +5,6 @@
// except according to those terms.
use std::{
- convert::TryFrom,
ffi::CString,
os::raw::{c_char, c_uint},
ptr::{addr_of_mut, null_mut},
@@ -15,7 +14,7 @@ use neqo_common::qtrace;
use crate::{
err::{ssl::SSL_ERROR_ECH_RETRY_WITH_ECH, Error, Res},
- experimental_api,
+ experimental_api, null_safe_slice,
p11::{
self, Item, PrivateKey, PublicKey, SECITEM_FreeItem, SECItem, SECKEYPrivateKey,
SECKEYPublicKey, Slot,
@@ -76,7 +75,7 @@ pub fn convert_ech_error(fd: *mut PRFileDesc, err: Error) -> Error {
return Error::InternalError;
}
let buf = unsafe {
- let slc = std::slice::from_raw_parts(item.data, usize::try_from(item.len).unwrap());
+ let slc = null_safe_slice(item.data, item.len);
let buf = Vec::from(slc);
SECITEM_FreeItem(&mut item, PRBool::from(false));
buf
@@ -101,8 +100,7 @@ pub fn generate_keys() -> Res<(PrivateKey, PublicKey)> {
let oid_data = unsafe { p11::SECOID_FindOIDByTag(p11::SECOidTag::SEC_OID_CURVE25519) };
let oid = unsafe { oid_data.as_ref() }.ok_or(Error::InternalError)?;
- let oid_slc =
- unsafe { std::slice::from_raw_parts(oid.oid.data, usize::try_from(oid.oid.len).unwrap()) };
+ let oid_slc = unsafe { null_safe_slice(oid.oid.data, oid.oid.len) };
let mut params: Vec<u8> = Vec::with_capacity(oid_slc.len() + 2);
params.push(u8::try_from(p11::SEC_ASN1_OBJECT_ID).unwrap());
params.push(u8::try_from(oid.oid.len).unwrap());
@@ -113,7 +111,6 @@ pub fn generate_keys() -> Res<(PrivateKey, PublicKey)> {
// If we have tracing on, try to ensure that key data can be read.
let insensitive_secret_ptr = if log::log_enabled!(log::Level::Trace) {
- #[allow(clippy::useless_conversion)] // TODO: Remove when we bump the MSRV to 1.74.0.
unsafe {
p11::PK11_GenerateKeyPairWithOpFlags(
*slot,
@@ -131,7 +128,6 @@ pub fn generate_keys() -> Res<(PrivateKey, PublicKey)> {
};
assert_eq!(insensitive_secret_ptr.is_null(), public_ptr.is_null());
let secret_ptr = if insensitive_secret_ptr.is_null() {
- #[allow(clippy::useless_conversion)] // TODO: Remove when we bump the MSRV to 1.74.0.
unsafe {
p11::PK11_GenerateKeyPairWithOpFlags(
*slot,
diff --git a/third_party/rust/neqo-crypto/src/ext.rs b/third_party/rust/neqo-crypto/src/ext.rs
index 310e87a1b7..02ee6340c1 100644
--- a/third_party/rust/neqo-crypto/src/ext.rs
+++ b/third_party/rust/neqo-crypto/src/ext.rs
@@ -6,7 +6,6 @@
use std::{
cell::RefCell,
- convert::TryFrom,
os::raw::{c_uint, c_void},
pin::Pin,
rc::Rc,
@@ -16,6 +15,7 @@ use crate::{
agentio::as_c_void,
constants::{Extension, HandshakeMessage, TLS_HS_CLIENT_HELLO, TLS_HS_ENCRYPTED_EXTENSIONS},
err::Res,
+ null_safe_slice,
ssl::{
PRBool, PRFileDesc, SECFailure, SECStatus, SECSuccess, SSLAlertDescription,
SSLExtensionHandler, SSLExtensionWriter, SSLHandshakeType,
@@ -105,7 +105,7 @@ impl ExtensionTracker {
alert: *mut SSLAlertDescription,
arg: *mut c_void,
) -> SECStatus {
- let d = std::slice::from_raw_parts(data, len as usize);
+ let d = null_safe_slice(data, len);
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
Self::wrap_handler_call(arg, |handler| {
// Cast is safe here because the message type is always part of the enum
diff --git a/third_party/rust/neqo-crypto/src/hkdf.rs b/third_party/rust/neqo-crypto/src/hkdf.rs
index e3cf77418c..3706be6c3b 100644
--- a/third_party/rust/neqo-crypto/src/hkdf.rs
+++ b/third_party/rust/neqo-crypto/src/hkdf.rs
@@ -5,7 +5,6 @@
// except according to those terms.
use std::{
- convert::TryFrom,
os::raw::{c_char, c_uint},
ptr::null_mut,
};
@@ -17,9 +16,10 @@ use crate::{
},
err::{Error, Res},
p11::{
- random, Item, PK11Origin, PK11SymKey, PK11_ImportDataKey, Slot, SymKey, CKA_DERIVE,
+ Item, PK11Origin, PK11SymKey, PK11_ImportDataKey, Slot, SymKey, CKA_DERIVE,
CKM_HKDF_DERIVE, CK_ATTRIBUTE_TYPE, CK_MECHANISM_TYPE,
},
+ random,
};
experimental_api!(SSL_HkdfExtract(
@@ -40,24 +40,32 @@ experimental_api!(SSL_HkdfExpandLabel(
secret: *mut *mut PK11SymKey,
));
-fn key_size(version: Version, cipher: Cipher) -> Res<usize> {
+const MAX_KEY_SIZE: usize = 48;
+const fn key_size(version: Version, cipher: Cipher) -> Res<usize> {
if version != TLS_VERSION_1_3 {
return Err(Error::UnsupportedVersion);
}
- Ok(match cipher {
+ let size = match cipher {
TLS_AES_128_GCM_SHA256 | TLS_CHACHA20_POLY1305_SHA256 => 32,
TLS_AES_256_GCM_SHA384 => 48,
_ => return Err(Error::UnsupportedCipher),
- })
+ };
+ debug_assert!(size <= MAX_KEY_SIZE);
+ Ok(size)
}
/// Generate a random key of the right size for the given suite.
///
/// # Errors
///
-/// Only if NSS fails.
+/// If the ciphersuite or protocol version is not supported.
pub fn generate_key(version: Version, cipher: Cipher) -> Res<SymKey> {
- import_key(version, &random(key_size(version, cipher)?))
+ // With generic_const_expr, this becomes:
+ // import_key(version, &random::<{ key_size(version, cipher) }>())
+ import_key(
+ version,
+ &random::<MAX_KEY_SIZE>()[0..key_size(version, cipher)?],
+ )
}
/// Import a symmetric key for use with HKDF.
@@ -70,7 +78,6 @@ pub fn import_key(version: Version, buf: &[u8]) -> Res<SymKey> {
return Err(Error::UnsupportedVersion);
}
let slot = Slot::internal()?;
- #[allow(clippy::useless_conversion)] // TODO: Remove when we bump the MSRV to 1.74.0.
let key_ptr = unsafe {
PK11_ImportDataKey(
*slot,
diff --git a/third_party/rust/neqo-crypto/src/hp.rs b/third_party/rust/neqo-crypto/src/hp.rs
index 2479eff8f5..1eba6a9cb5 100644
--- a/third_party/rust/neqo-crypto/src/hp.rs
+++ b/third_party/rust/neqo-crypto/src/hp.rs
@@ -6,7 +6,6 @@
use std::{
cell::RefCell,
- convert::TryFrom,
fmt::{self, Debug},
os::raw::{c_char, c_int, c_uint},
ptr::{addr_of_mut, null, null_mut},
@@ -46,7 +45,7 @@ pub enum HpKey {
/// track references using `Rc`. `PK11Context` can't be used with `PK11_CloneContext`
/// as that is not supported for these contexts.
Aes(Rc<RefCell<Context>>),
- /// The ChaCha20 mask has to invoke a new PK11_Encrypt every time as it needs to
+ /// The `ChaCha20` mask has to invoke a new `PK11_Encrypt` every time as it needs to
/// change the counter and nonce on each invocation.
Chacha(SymKey),
}
@@ -76,7 +75,6 @@ impl HpKey {
let l = label.as_bytes();
let mut secret: *mut PK11SymKey = null_mut();
- #[allow(clippy::useless_conversion)] // TODO: Remove when we bump the MSRV to 1.74.0.
let (mech, key_size) = match cipher {
TLS_AES_128_GCM_SHA256 => (CK_MECHANISM_TYPE::from(CKM_AES_ECB), 16),
TLS_AES_256_GCM_SHA384 => (CK_MECHANISM_TYPE::from(CKM_AES_ECB), 32),
@@ -104,8 +102,6 @@ impl HpKey {
let res = match cipher {
TLS_AES_128_GCM_SHA256 | TLS_AES_256_GCM_SHA384 => {
- // TODO: Remove when we bump the MSRV to 1.74.0.
- #[allow(clippy::useless_conversion)]
let context_ptr = unsafe {
PK11_CreateContextBySymKey(
mech,
@@ -181,8 +177,6 @@ impl HpKey {
};
let mut output_len: c_uint = 0;
let mut param_item = Item::wrap_struct(&params);
- // TODO: Remove when we bump the MSRV to 1.74.0.
- #[allow(clippy::useless_conversion)]
secstatus_to_res(unsafe {
PK11_Encrypt(
**key,
diff --git a/third_party/rust/neqo-crypto/src/lib.rs b/third_party/rust/neqo-crypto/src/lib.rs
index 05424ee1f3..2ec1b4a3ea 100644
--- a/third_party/rust/neqo-crypto/src/lib.rs
+++ b/third_party/rust/neqo-crypto/src/lib.rs
@@ -4,13 +4,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-#![cfg_attr(feature = "deny-warnings", deny(warnings))]
-#![warn(clippy::pedantic)]
-// Bindgen auto generated code
-// won't adhere to the clippy rules below
-#![allow(clippy::module_name_repetitions)]
-#![allow(clippy::unseparated_literal_suffix)]
-#![allow(clippy::used_underscore_binding)]
+#![allow(clippy::module_name_repetitions)] // This lint doesn't work here.
+#![allow(clippy::unseparated_literal_suffix, clippy::used_underscore_binding)] // For bindgen code.
mod aead;
#[cfg(feature = "fuzzing")]
@@ -27,7 +22,6 @@ mod exp;
pub mod ext;
pub mod hkdf;
pub mod hp;
-mod once;
#[macro_use]
mod p11;
mod prio;
@@ -37,11 +31,7 @@ pub mod selfencrypt;
mod ssl;
mod time;
-use std::{
- ffi::CString,
- path::{Path, PathBuf},
- ptr::null,
-};
+use std::{ffi::CString, path::PathBuf, ptr::null, sync::OnceLock};
#[cfg(not(feature = "fuzzing"))]
pub use self::aead::RealAead as Aead;
@@ -49,7 +39,6 @@ pub use self::aead::RealAead as Aead;
pub use self::aead::RealAead;
#[cfg(feature = "fuzzing")]
pub use self::aead_fuzzing::FuzzingAead as Aead;
-use self::once::OnceResult;
pub use self::{
agent::{
Agent, AllowZeroRtt, Client, HandshakeState, Record, RecordList, ResumptionToken,
@@ -64,7 +53,7 @@ pub use self::{
},
err::{Error, PRErrorCode, Res},
ext::{ExtensionHandler, ExtensionHandlerResult, ExtensionWriterResult},
- p11::{random, PrivateKey, PublicKey, SymKey},
+ p11::{random, randomize, PrivateKey, PublicKey, SymKey},
replay::AntiReplay,
secrets::SecretDirection,
ssl::Opt,
@@ -87,7 +76,7 @@ fn secstatus_to_res(code: nss::SECStatus) -> Res<()> {
enum NssLoaded {
External,
NoDb,
- Db(Box<Path>),
+ Db,
}
impl Drop for NssLoaded {
@@ -100,7 +89,7 @@ impl Drop for NssLoaded {
}
}
-static mut INITIALIZED: OnceResult<NssLoaded> = OnceResult::new();
+static INITIALIZED: OnceLock<NssLoaded> = OnceLock::new();
fn already_initialized() -> bool {
unsafe { nss::NSS_IsInitialized() != 0 }
@@ -124,19 +113,18 @@ fn version_check() {
pub fn init() {
// Set time zero.
time::init();
- unsafe {
- INITIALIZED.call_once(|| {
- version_check();
- if already_initialized() {
- return NssLoaded::External;
- }
+ _ = INITIALIZED.get_or_init(|| {
+ version_check();
+ if already_initialized() {
+ return NssLoaded::External;
+ }
- secstatus_to_res(nss::NSS_NoDB_Init(null())).expect("NSS_NoDB_Init failed");
- secstatus_to_res(nss::NSS_SetDomesticPolicy()).expect("NSS_SetDomesticPolicy failed");
+ secstatus_to_res(unsafe { nss::NSS_NoDB_Init(null()) }).expect("NSS_NoDB_Init failed");
+ secstatus_to_res(unsafe { nss::NSS_SetDomesticPolicy() })
+ .expect("NSS_SetDomesticPolicy failed");
- NssLoaded::NoDb
- });
- }
+ NssLoaded::NoDb
+ });
}
/// This enables SSLTRACE by calling a simple, harmless function to trigger its
@@ -158,51 +146,71 @@ fn enable_ssl_trace() {
/// If NSS cannot be initialized.
pub fn init_db<P: Into<PathBuf>>(dir: P) {
time::init();
- unsafe {
- INITIALIZED.call_once(|| {
- version_check();
- if already_initialized() {
- return NssLoaded::External;
- }
+ _ = INITIALIZED.get_or_init(|| {
+ version_check();
+ if already_initialized() {
+ return NssLoaded::External;
+ }
- let path = dir.into();
- assert!(path.is_dir());
- let pathstr = path.to_str().expect("path converts to string").to_string();
- let dircstr = CString::new(pathstr).unwrap();
- let empty = CString::new("").unwrap();
- secstatus_to_res(nss::NSS_Initialize(
+ let path = dir.into();
+ assert!(path.is_dir());
+ let pathstr = path.to_str().expect("path converts to string").to_string();
+ let dircstr = CString::new(pathstr).unwrap();
+ let empty = CString::new("").unwrap();
+ secstatus_to_res(unsafe {
+ nss::NSS_Initialize(
dircstr.as_ptr(),
empty.as_ptr(),
empty.as_ptr(),
nss::SECMOD_DB.as_ptr().cast(),
nss::NSS_INIT_READONLY,
- ))
- .expect("NSS_Initialize failed");
-
- secstatus_to_res(nss::NSS_SetDomesticPolicy()).expect("NSS_SetDomesticPolicy failed");
- secstatus_to_res(ssl::SSL_ConfigServerSessionIDCache(
- 1024,
- 0,
- 0,
- dircstr.as_ptr(),
- ))
- .expect("SSL_ConfigServerSessionIDCache failed");
-
- #[cfg(debug_assertions)]
- enable_ssl_trace();
-
- NssLoaded::Db(path.into_boxed_path())
- });
- }
+ )
+ })
+ .expect("NSS_Initialize failed");
+
+ secstatus_to_res(unsafe { nss::NSS_SetDomesticPolicy() })
+ .expect("NSS_SetDomesticPolicy failed");
+ secstatus_to_res(unsafe {
+ ssl::SSL_ConfigServerSessionIDCache(1024, 0, 0, dircstr.as_ptr())
+ })
+ .expect("SSL_ConfigServerSessionIDCache failed");
+
+ #[cfg(debug_assertions)]
+ enable_ssl_trace();
+
+ NssLoaded::Db
+ });
}
/// # Panics
///
/// If NSS isn't initialized.
pub fn assert_initialized() {
- unsafe {
- INITIALIZED.call_once(|| {
- panic!("NSS not initialized with init or init_db");
- });
+ INITIALIZED
+ .get()
+ .expect("NSS not initialized with init or init_db");
+}
+
+/// NSS tends to return empty "slices" with a null pointer, which will cause
+/// `std::slice::from_raw_parts` to panic if passed directly. This wrapper avoids
+/// that issue. It also performs conversion for lengths, as a convenience.
+///
+/// # Panics
+/// If the provided length doesn't fit into a `usize`.
+///
+/// # Safety
+/// The caller must adhere to the safety constraints of `std::slice::from_raw_parts`,
+/// except that this will accept a null value for `data`.
+unsafe fn null_safe_slice<'a, T>(data: *const u8, len: T) -> &'a [u8]
+where
+ usize: TryFrom<T>,
+{
+ if data.is_null() {
+ &[]
+ } else if let Ok(len) = usize::try_from(len) {
+ #[allow(clippy::disallowed_methods)]
+ std::slice::from_raw_parts(data, len)
+ } else {
+ panic!("null_safe_slice: size overflow");
}
}
diff --git a/third_party/rust/neqo-crypto/src/once.rs b/third_party/rust/neqo-crypto/src/once.rs
deleted file mode 100644
index 80657cfe26..0000000000
--- a/third_party/rust/neqo-crypto/src/once.rs
+++ /dev/null
@@ -1,44 +0,0 @@
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-use std::sync::Once;
-
-#[allow(clippy::module_name_repetitions)]
-pub struct OnceResult<T> {
- once: Once,
- v: Option<T>,
-}
-
-impl<T> OnceResult<T> {
- #[must_use]
- pub const fn new() -> Self {
- Self {
- once: Once::new(),
- v: None,
- }
- }
-
- pub fn call_once<F: FnOnce() -> T>(&mut self, f: F) -> &T {
- let v = &mut self.v;
- self.once.call_once(|| {
- *v = Some(f());
- });
- self.v.as_ref().unwrap()
- }
-}
-
-#[cfg(test)]
-mod test {
- use super::OnceResult;
-
- static mut STATIC_ONCE_RESULT: OnceResult<u64> = OnceResult::new();
-
- #[test]
- fn static_update() {
- assert_eq!(*unsafe { STATIC_ONCE_RESULT.call_once(|| 23) }, 23);
- assert_eq!(*unsafe { STATIC_ONCE_RESULT.call_once(|| 24) }, 23);
- }
-}
diff --git a/third_party/rust/neqo-crypto/src/p11.rs b/third_party/rust/neqo-crypto/src/p11.rs
index 508d240062..5552882e2e 100644
--- a/third_party/rust/neqo-crypto/src/p11.rs
+++ b/third_party/rust/neqo-crypto/src/p11.rs
@@ -10,7 +10,7 @@
#![allow(non_snake_case)]
use std::{
- convert::TryFrom,
+ cell::RefCell,
mem,
ops::{Deref, DerefMut},
os::raw::{c_int, c_uint},
@@ -19,7 +19,10 @@ use std::{
use neqo_common::hex_with_len;
-use crate::err::{secstatus_to_res, Error, Res};
+use crate::{
+ err::{secstatus_to_res, Error, Res},
+ null_safe_slice,
+};
#[allow(clippy::upper_case_acronyms)]
#[allow(clippy::unreadable_literal)]
@@ -139,7 +142,6 @@ impl PrivateKey {
/// When the values are too large to fit. So never.
pub fn key_data(&self) -> Res<Vec<u8>> {
let mut key_item = Item::make_empty();
- #[allow(clippy::useless_conversion)] // TODO: Remove when we bump the MSRV to 1.74.0.
secstatus_to_res(unsafe {
PK11_ReadRawAttribute(
PK11ObjectType::PK11_TypePrivKey,
@@ -148,9 +150,7 @@ impl PrivateKey {
&mut key_item,
)
})?;
- let slc = unsafe {
- std::slice::from_raw_parts(key_item.data, usize::try_from(key_item.len).unwrap())
- };
+ let slc = unsafe { null_safe_slice(key_item.data, key_item.len) };
let key = Vec::from(slc);
// The data that `key_item` refers to needs to be freed, but we can't
// use the scoped `Item` implementation. This is OK as long as nothing
@@ -206,7 +206,7 @@ impl SymKey {
// This is accessing a value attached to the key, so we can treat this as a borrow.
match unsafe { key_item.as_mut() } {
None => Err(Error::InternalError),
- Some(key) => Ok(unsafe { std::slice::from_raw_parts(key.data, key.len as usize) }),
+ Some(key) => Ok(unsafe { null_safe_slice(key.data, key.len) }),
}
}
}
@@ -285,36 +285,112 @@ impl Item {
let b = self.ptr.as_ref().unwrap();
// Sanity check the type, as some types don't count bytes in `Item::len`.
assert_eq!(b.type_, SECItemType::siBuffer);
- let slc = std::slice::from_raw_parts(b.data, usize::try_from(b.len).unwrap());
+ let slc = null_safe_slice(b.data, b.len);
Vec::from(slc)
}
}
-/// Generate a randomized buffer.
+/// Fill a buffer with randomness.
///
/// # Panics
///
/// When `size` is too large or NSS fails.
-#[must_use]
-pub fn random(size: usize) -> Vec<u8> {
- let mut buf = vec![0; size];
- secstatus_to_res(unsafe {
- PK11_GenerateRandom(buf.as_mut_ptr(), c_int::try_from(buf.len()).unwrap())
- })
- .unwrap();
+pub fn randomize<B: AsMut<[u8]>>(mut buf: B) -> B {
+ let m_buf = buf.as_mut();
+ let len = c_int::try_from(m_buf.len()).unwrap();
+ secstatus_to_res(unsafe { PK11_GenerateRandom(m_buf.as_mut_ptr(), len) }).unwrap();
buf
}
+struct RandomCache {
+ cache: [u8; Self::SIZE],
+ used: usize,
+}
+
+impl RandomCache {
+ const SIZE: usize = 256;
+ const CUTOFF: usize = 32;
+
+ fn new() -> Self {
+ RandomCache {
+ cache: [0; Self::SIZE],
+ used: Self::SIZE,
+ }
+ }
+
+ fn randomize<B: AsMut<[u8]>>(&mut self, mut buf: B) -> B {
+ let m_buf = buf.as_mut();
+ debug_assert!(m_buf.len() <= Self::CUTOFF);
+ let avail = Self::SIZE - self.used;
+ if m_buf.len() <= avail {
+ m_buf.copy_from_slice(&self.cache[self.used..self.used + m_buf.len()]);
+ self.used += m_buf.len();
+ } else {
+ if avail > 0 {
+ m_buf[..avail].copy_from_slice(&self.cache[self.used..]);
+ }
+ randomize(&mut self.cache[..]);
+ self.used = m_buf.len() - avail;
+ m_buf[avail..].copy_from_slice(&self.cache[..self.used]);
+ }
+ buf
+ }
+}
+
+/// Generate a randomized array.
+///
+/// # Panics
+///
+/// When `size` is too large or NSS fails.
+#[must_use]
+pub fn random<const N: usize>() -> [u8; N] {
+ thread_local!(static CACHE: RefCell<RandomCache> = RefCell::new(RandomCache::new()));
+
+ let buf = [0; N];
+ if N <= RandomCache::CUTOFF {
+ CACHE.with_borrow_mut(|c| c.randomize(buf))
+ } else {
+ randomize(buf)
+ }
+}
+
#[cfg(test)]
mod test {
use test_fixture::fixture_init;
- use super::random;
+ use super::RandomCache;
+ use crate::{random, randomize};
#[test]
fn randomness() {
fixture_init();
- // If this ever fails, there is either a bug, or it's time to buy a lottery ticket.
- assert_ne!(random(16), random(16));
+ // If any of these ever fail, there is either a bug, or it's time to buy a lottery ticket.
+ assert_ne!(random::<16>(), randomize([0; 16]));
+ assert_ne!([0; 16], random::<16>());
+ assert_ne!([0; 64], random::<64>());
+ }
+
+ #[test]
+ fn cache_random_lengths() {
+ const ZERO: [u8; 256] = [0; 256];
+
+ fixture_init();
+ let mut cache = RandomCache::new();
+ let mut buf = [0; 256];
+ let bits = usize::BITS - (RandomCache::CUTOFF - 1).leading_zeros();
+ let mask = 0xff >> (u8::BITS - bits);
+
+ for _ in 0..100 {
+ let len = loop {
+ let len = usize::from(random::<1>()[0] & mask) + 1;
+ if len <= RandomCache::CUTOFF {
+ break len;
+ }
+ };
+ buf.fill(0);
+ if len >= 16 {
+ assert_ne!(&cache.randomize(&mut buf[..len])[..len], &ZERO[..len]);
+ }
+ }
}
}
diff --git a/third_party/rust/neqo-crypto/src/replay.rs b/third_party/rust/neqo-crypto/src/replay.rs
index d4d3677f5c..5fd6fd1250 100644
--- a/third_party/rust/neqo-crypto/src/replay.rs
+++ b/third_party/rust/neqo-crypto/src/replay.rs
@@ -5,7 +5,6 @@
// except according to those terms.
use std::{
- convert::{TryFrom, TryInto},
ops::{Deref, DerefMut},
os::raw::c_uint,
ptr::null_mut,
diff --git a/third_party/rust/neqo-crypto/src/selfencrypt.rs b/third_party/rust/neqo-crypto/src/selfencrypt.rs
index b8a63153fd..1130c35250 100644
--- a/third_party/rust/neqo-crypto/src/selfencrypt.rs
+++ b/third_party/rust/neqo-crypto/src/selfencrypt.rs
@@ -82,7 +82,7 @@ impl SelfEncrypt {
// opaque aead_encrypted(plaintext)[length as expanded];
// };
// AAD covers the entire header, plus the value of the AAD parameter that is provided.
- let salt = random(Self::SALT_LENGTH);
+ let salt = random::<{ Self::SALT_LENGTH }>();
let cipher = self.make_aead(&self.key, &salt)?;
let encoded_len = 2 + salt.len() + plaintext.len() + cipher.expansion();
diff --git a/third_party/rust/neqo-crypto/src/time.rs b/third_party/rust/neqo-crypto/src/time.rs
index 84dbfdb4a5..0e59c4f5e2 100644
--- a/third_party/rust/neqo-crypto/src/time.rs
+++ b/third_party/rust/neqo-crypto/src/time.rs
@@ -7,18 +7,16 @@
#![allow(clippy::upper_case_acronyms)]
use std::{
- boxed::Box,
- convert::{TryFrom, TryInto},
ops::Deref,
os::raw::c_void,
pin::Pin,
+ sync::OnceLock,
time::{Duration, Instant},
};
use crate::{
agentio::as_c_void,
err::{Error, Res},
- once::OnceResult,
ssl::{PRFileDesc, SSLTimeFunc},
};
@@ -67,14 +65,13 @@ impl TimeZero {
}
}
-static mut BASE_TIME: OnceResult<TimeZero> = OnceResult::new();
+static BASE_TIME: OnceLock<TimeZero> = OnceLock::new();
fn get_base() -> &'static TimeZero {
- let f = || TimeZero {
+ BASE_TIME.get_or_init(|| TimeZero {
instant: Instant::now(),
prtime: unsafe { PR_Now() },
- };
- unsafe { BASE_TIME.call_once(f) }
+ })
}
pub(crate) fn init() {
@@ -97,9 +94,8 @@ impl Deref for Time {
impl From<Instant> for Time {
/// Convert from an Instant into a Time.
fn from(t: Instant) -> Self {
- // Call `TimeZero::baseline(t)` so that time zero can be set.
- let f = || TimeZero::baseline(t);
- _ = unsafe { BASE_TIME.call_once(f) };
+ // Initialize `BASE_TIME` using `TimeZero::baseline(t)`.
+ BASE_TIME.get_or_init(|| TimeZero::baseline(t));
Self { t }
}
}
@@ -108,14 +104,17 @@ impl TryFrom<PRTime> for Time {
type Error = Error;
fn try_from(prtime: PRTime) -> Res<Self> {
let base = get_base();
- if let Some(delta) = prtime.checked_sub(base.prtime) {
- let d = Duration::from_micros(delta.try_into()?);
- base.instant
- .checked_add(d)
- .map_or(Err(Error::TimeTravelError), |t| Ok(Self { t }))
+ let delta = prtime
+ .checked_sub(base.prtime)
+ .ok_or(Error::TimeTravelError)?;
+ let d = Duration::from_micros(u64::try_from(delta.abs())?);
+ let t = if delta >= 0 {
+ base.instant.checked_add(d)
} else {
- Err(Error::TimeTravelError)
- }
+ base.instant.checked_sub(d)
+ };
+ let t = t.ok_or(Error::TimeTravelError)?;
+ Ok(Self { t })
}
}
@@ -123,14 +122,21 @@ impl TryInto<PRTime> for Time {
type Error = Error;
fn try_into(self) -> Res<PRTime> {
let base = get_base();
- let delta = self
- .t
- .checked_duration_since(base.instant)
- .ok_or(Error::TimeTravelError)?;
- if let Ok(d) = PRTime::try_from(delta.as_micros()) {
- d.checked_add(base.prtime).ok_or(Error::TimeTravelError)
+
+ if let Some(delta) = self.t.checked_duration_since(base.instant) {
+ if let Ok(d) = PRTime::try_from(delta.as_micros()) {
+ d.checked_add(base.prtime).ok_or(Error::TimeTravelError)
+ } else {
+ Err(Error::TimeTravelError)
+ }
} else {
- Err(Error::TimeTravelError)
+ // Try to go backwards from the base time.
+ let backwards = base.instant - self.t; // infallible
+ if let Ok(d) = PRTime::try_from(backwards.as_micros()) {
+ base.prtime.checked_sub(d).ok_or(Error::TimeTravelError)
+ } else {
+ Err(Error::TimeTravelError)
+ }
}
}
}
@@ -207,10 +213,7 @@ impl Default for TimeHolder {
#[cfg(test)]
mod test {
- use std::{
- convert::{TryFrom, TryInto},
- time::{Duration, Instant},
- };
+ use std::time::{Duration, Instant};
use super::{get_base, init, Interval, PRTime, Time};
use crate::err::Res;
@@ -228,16 +231,23 @@ mod test {
}
#[test]
- fn past_time() {
+ fn past_prtime() {
+ const DELTA: Duration = Duration::from_secs(1);
init();
let base = get_base();
- assert!(Time::try_from(base.prtime - 1).is_err());
+ let delta_micros = PRTime::try_from(DELTA.as_micros()).unwrap();
+ println!("{} - {}", base.prtime, delta_micros);
+ let t = Time::try_from(base.prtime - delta_micros).unwrap();
+ assert_eq!(Instant::from(t) + DELTA, base.instant);
}
#[test]
- fn negative_time() {
+ fn past_instant() {
+ const DELTA: Duration = Duration::from_secs(1);
init();
- assert!(Time::try_from(-1).is_err());
+ let base = get_base();
+ let t = Time::from(base.instant.checked_sub(DELTA).unwrap());
+ assert_eq!(Instant::from(t) + DELTA, base.instant);
}
#[test]
diff --git a/third_party/rust/neqo-crypto/tests/aead.rs b/third_party/rust/neqo-crypto/tests/aead.rs
index 0ee1e66c38..5cf0034aec 100644
--- a/third_party/rust/neqo-crypto/tests/aead.rs
+++ b/third_party/rust/neqo-crypto/tests/aead.rs
@@ -1,4 +1,9 @@
-#![cfg_attr(feature = "deny-warnings", deny(warnings))]
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
#![warn(clippy::pedantic)]
#![cfg(not(feature = "fuzzing"))]
diff --git a/third_party/rust/neqo-crypto/tests/agent.rs b/third_party/rust/neqo-crypto/tests/agent.rs
index c2c83c467c..80bf816930 100644
--- a/third_party/rust/neqo-crypto/tests/agent.rs
+++ b/third_party/rust/neqo-crypto/tests/agent.rs
@@ -1,7 +1,8 @@
-#![cfg_attr(feature = "deny-warnings", deny(warnings))]
-#![warn(clippy::pedantic)]
-
-use std::boxed::Box;
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
use neqo_crypto::{
generate_ech_keys, AuthenticationStatus, Client, Error, HandshakeState, SecretAgentPreInfo,
diff --git a/third_party/rust/neqo-crypto/tests/ext.rs b/third_party/rust/neqo-crypto/tests/ext.rs
index 9ae81133f5..c8732dd014 100644
--- a/third_party/rust/neqo-crypto/tests/ext.rs
+++ b/third_party/rust/neqo-crypto/tests/ext.rs
@@ -1,5 +1,8 @@
-#![cfg_attr(feature = "deny-warnings", deny(warnings))]
-#![warn(clippy::pedantic)]
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
use std::{cell::RefCell, rc::Rc};
diff --git a/third_party/rust/neqo-crypto/tests/handshake.rs b/third_party/rust/neqo-crypto/tests/handshake.rs
index b2d8b9cc34..3cb31337fd 100644
--- a/third_party/rust/neqo-crypto/tests/handshake.rs
+++ b/third_party/rust/neqo-crypto/tests/handshake.rs
@@ -1,4 +1,12 @@
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
#![allow(dead_code)]
+#![allow(clippy::missing_panics_doc)]
+#![allow(clippy::missing_errors_doc)]
use std::{mem, time::Instant};
@@ -127,6 +135,7 @@ fn zero_rtt_setup(
}
}
+#[must_use]
pub fn resumption_setup(mode: Resumption) -> (Option<AntiReplay>, ResumptionToken) {
fixture_init();
diff --git a/third_party/rust/neqo-crypto/tests/hkdf.rs b/third_party/rust/neqo-crypto/tests/hkdf.rs
index b4dde482f8..acb5bbdda8 100644
--- a/third_party/rust/neqo-crypto/tests/hkdf.rs
+++ b/third_party/rust/neqo-crypto/tests/hkdf.rs
@@ -1,5 +1,8 @@
-#![cfg_attr(feature = "deny-warnings", deny(warnings))]
-#![warn(clippy::pedantic)]
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
use neqo_crypto::{
constants::{
diff --git a/third_party/rust/neqo-crypto/tests/hp.rs b/third_party/rust/neqo-crypto/tests/hp.rs
index 43b96869d8..da7df2cc19 100644
--- a/third_party/rust/neqo-crypto/tests/hp.rs
+++ b/third_party/rust/neqo-crypto/tests/hp.rs
@@ -1,5 +1,8 @@
-#![cfg_attr(feature = "deny-warnings", deny(warnings))]
-#![warn(clippy::pedantic)]
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
use std::mem;
diff --git a/third_party/rust/neqo-crypto/tests/init.rs b/third_party/rust/neqo-crypto/tests/init.rs
index 21291ceebb..13218cc340 100644
--- a/third_party/rust/neqo-crypto/tests/init.rs
+++ b/third_party/rust/neqo-crypto/tests/init.rs
@@ -1,5 +1,8 @@
-#![cfg_attr(feature = "deny-warnings", deny(warnings))]
-#![warn(clippy::pedantic)]
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
// This uses external interfaces to neqo_crypto rather than being a module
// inside of lib.rs. Because all other code uses the test_fixture module,
diff --git a/third_party/rust/neqo-crypto/tests/selfencrypt.rs b/third_party/rust/neqo-crypto/tests/selfencrypt.rs
index fd9d4ea1ea..4c574a3ae9 100644
--- a/third_party/rust/neqo-crypto/tests/selfencrypt.rs
+++ b/third_party/rust/neqo-crypto/tests/selfencrypt.rs
@@ -1,5 +1,9 @@
-#![cfg_attr(feature = "deny-warnings", deny(warnings))]
-#![warn(clippy::pedantic)]
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
#![cfg(not(feature = "fuzzing"))]
use neqo_crypto::{
diff --git a/third_party/rust/neqo-http3/.cargo-checksum.json b/third_party/rust/neqo-http3/.cargo-checksum.json
index 2705291744..0459fea7cc 100644
--- a/third_party/rust/neqo-http3/.cargo-checksum.json
+++ b/third_party/rust/neqo-http3/.cargo-checksum.json
@@ -1 +1 @@
-{"files":{"Cargo.toml":"fe3c1114cfbb94004bf56740c0d373568cb459efdb12504e67f31923fbd436e1","src/buffered_send_stream.rs":"f45bdf9ad2a04b3828c74ff5440681d3c9d1af39b55470e4f729842dc2412295","src/client_events.rs":"e1392e7bbb62fb0505a4d8bcd27559699bbf38f3c94e7d8cae7291db82e6334c","src/conn_params.rs":"224a8ea6ef632930a7788a1cabf47ce69ad41bd4bc8dcf3053fbd998fdb38e82","src/connection.rs":"09aeb123f8dc6b903dd7d30579e5bb09ed8f70bfae563fb2fcc1871c67d604d4","src/connection_client.rs":"ed1c9ebf443f49dbf12c193953a71ec0e6b95555e1927afce813d2a8324758be","src/connection_server.rs":"ca33b50650bd1ca2a952851b72712d55ec2e48b48f1f06e4184c808b8e1e009a","src/control_stream_local.rs":"d6ecc0adc926e1d5cec9a378317f9dfcfeeb9840a0873a2afb380c2d252d8c54","src/control_stream_remote.rs":"59eb4041e366d92f9f294e8446755caa5e91fd943bba7b79b726698ba13be248","src/features/extended_connect/mod.rs":"3b02f6b18627f3855465a81b1d9b285e6f13839e75a8a6db648ed9082908d7f0","src/features/extended_connect/tests/mod.rs":"fd6aee37243713e80fc526552f21f0222338cec9890409b6575a2a637b17ec1f","src/features/extended_connect/tests/webtransport/datagrams.rs":"7e3bdd591b9c7d02f69954629f889d52bd54f13dbca11d555e614138c2a55107","src/features/extended_connect/tests/webtransport/mod.rs":"fed03f0ded21a9f17be5be99e4572e16dd0c8598e37044df3228990ea7fcc9f4","src/features/extended_connect/tests/webtransport/negotiation.rs":"98254ef8446581ec520026b04ef9549645602181b61602c9936f6660141edf0b","src/features/extended_connect/tests/webtransport/sessions.rs":"de3d836f666c2bec31e70b33bdc2669572cabbe17df2225db7282613a224a364","src/features/extended_connect/tests/webtransport/streams.rs":"8b3c34cac1b2171252a4bb53d420ac2098549a20309c327bf56e2e9ba9e33538","src/features/extended_connect/webtransport_session.rs":"a6472eca50a2d097aa6ba8a76b45ae69fe2edd2696b2953945faa3ce6e7417f9","src/features/extended_connect/webtransport_streams.rs":"a9a106eefc93a9f6e9e1c246df64904353de1c4fbcd394b338e6b117f6c677f5","src/features/mod.rs":"925aae4427ad82e4d019354802b223d53db5e5585d4a940f5417a24a9503d7ee","src/frames/hframe.rs":"726842108261c9af1e7576bc546e7bd7bea86fbef4a5804f4b45a2b4612e2679","src/frames/mod.rs":"7d0a46ca147336d14781edb8dbee8b03c2e4bcd6646f5473a9d93d31fe73fecb","src/frames/reader.rs":"4883e25064da1fb3a6ae46b5d15e6bcfec9c5bbea55a1937ecdb9465b62a93b2","src/frames/tests/hframe.rs":"01ec74eb3eb25d95042aa0263f9267f89535e6b7b8c1161fab4ba9ee5352d4a7","src/frames/tests/mod.rs":"0610609b316767a6a022837d32ee0452e37ea296fde37e51bec87e7c77e923a3","src/frames/tests/reader.rs":"2bfadc7afbc41bff9f5f930b31550259a8a92484d35f6c5d8dd8fd9acfb88f5b","src/frames/tests/wtframe.rs":"589ebe1e62ce4da63b37b7d22cde7ba572ddbf29336fdcdbbcd0a745f79dacd8","src/frames/wtframe.rs":"c80518d1569de277767c7ccb7441898aadbfc5fb2afb968c1d5105f8d175ccff","src/headers_checks.rs":"44891c16dda6b7ef742058ecb0a8d34e219c51cae1216c09c661cf72d9a5e7d5","src/lib.rs":"ed8da14e573cc5a97afb012a78af7f076eb83b5cc20cb4fe432eb7136a3ffe52","src/priority.rs":"10d9dcfcd4585f2ca256daf254c78a428297c41976c6548f19cd3ed2222b7cd2","src/push_controller.rs":"eb27c7c2a52c6108c0e4d040b021775a2b573f32d78b7ac8652ff46fd549f780","src/qlog.rs":"b1e6108b018abb077f218d1806e0a83370afa87709e26b3d51f482ae5d9b9c82","src/qpack_decoder_receiver.rs":"c927dfc3e58c71d282210ba79280f6f03e789733bc3bedc247e68bab516b9e9e","src/qpack_encoder_receiver.rs":"d0ac03cc111b6e1c555a8654d3234116f2b135b5b040edac23cefe2d640beba9","src/recv_message.rs":"06666c22101cda41de14682dc7e2e6721f2821bd45baefc22caceae4ccfcf2e0","src/request_target.rs":"6041a69a0a74969ec08bc164509c055e9bad99f53bbeb16c0aa17d108dd68b8c","src/send_message.rs":"70f8a91d85515f42a64a88bd2a9480175b12596bc082f77587cc5bcff9ce996c","src/server.rs":"ab6d4c80cb5f6c070f74d8df27e7bd62d5c8a8e7756ff9d1a31d3f9ff91327a1","src/server_connection_events.rs":"12d353ca6301467f6d475dde3b789951a5716c89ddd7dbf1383efef8082361f3","src/server_events.rs":"c96cff96d5893a9ab7165d17e3d1afaafc5492418b30003c1c26ca8f489ab7ca","src/settings.rs":"476b154b5eea4c8d69a4a790fee3e527cef4d375df1cfb5eed04ec56406fe15a","src/stream_type_reader.rs":"7a7226b7911d69f7e00ec4987c2a32a5e8a33463203398cbee1e6645d2691478","tests/httpconn.rs":"bb6927801a8c75e4f05eb6cdb1e7f2d57be69b74e68ddad2a1614f2aeed04369","tests/priority.rs":"3418be17fbdfdbcfd80dc4532f9365f405925442fabc916f2b22f90aee89629f","tests/send_message.rs":"1e893216d9252e6fb69a0fb291b4f8b8ea954847c346ff7f9347d7895618cabf","tests/webtransport.rs":"cb30d348c0ce05efb722abac3b1c524216fa4cbde8b62a1d1e3238c3fadecbe7"},"package":null} \ No newline at end of file
+{"files":{"Cargo.toml":"458f04261cda071d61402c52cf64062ad7cfc24f3f312bfaa5d52cae47409010","src/buffered_send_stream.rs":"f45bdf9ad2a04b3828c74ff5440681d3c9d1af39b55470e4f729842dc2412295","src/client_events.rs":"77fedca72ce54956eaba3fb7103085d196a631b764662584ea2629224c5c234e","src/conn_params.rs":"224a8ea6ef632930a7788a1cabf47ce69ad41bd4bc8dcf3053fbd998fdb38e82","src/connection.rs":"9384cdfd8481a30a0cd13f56f590188ccfa47b4472f35f7a4978537bab19adc1","src/connection_client.rs":"8db29409f3a265f7dff7c7a7eaf2ac607d6923e4b3238e82eab6dc22854e4303","src/connection_server.rs":"ca33b50650bd1ca2a952851b72712d55ec2e48b48f1f06e4184c808b8e1e009a","src/control_stream_local.rs":"ae52e3286f1686ca1265e7de841392addd42616db02799bb967a59feb6039cb5","src/control_stream_remote.rs":"59eb4041e366d92f9f294e8446755caa5e91fd943bba7b79b726698ba13be248","src/features/extended_connect/mod.rs":"3b02f6b18627f3855465a81b1d9b285e6f13839e75a8a6db648ed9082908d7f0","src/features/extended_connect/tests/mod.rs":"fd6aee37243713e80fc526552f21f0222338cec9890409b6575a2a637b17ec1f","src/features/extended_connect/tests/webtransport/datagrams.rs":"4c85a90afb753ce588e3fdeb773669bc49c013aebc28912340359eb01b74fd70","src/features/extended_connect/tests/webtransport/mod.rs":"a30ea715f5271a826a739278b18e145964dedbce7026eed45f1b7d0355c407d5","src/features/extended_connect/tests/webtransport/negotiation.rs":"98254ef8446581ec520026b04ef9549645602181b61602c9936f6660141edf0b","src/features/extended_connect/tests/webtransport/sessions.rs":"de3d836f666c2bec31e70b33bdc2669572cabbe17df2225db7282613a224a364","src/features/extended_connect/tests/webtransport/streams.rs":"8b3c34cac1b2171252a4bb53d420ac2098549a20309c327bf56e2e9ba9e33538","src/features/extended_connect/webtransport_session.rs":"239d92c06fbc5f6226078bb411a803f57b555dea0077349d49d7f57671cf2eab","src/features/extended_connect/webtransport_streams.rs":"5d7507aaf6a819d266fbea9b7a415c8324329df0f6936d9045b73e17a5b844ee","src/features/mod.rs":"925aae4427ad82e4d019354802b223d53db5e5585d4a940f5417a24a9503d7ee","src/frames/hframe.rs":"56c36ac597504f28c73cf2370acd82104f8c7a7b9ffc0f6d222378abc524482d","src/frames/mod.rs":"7d0a46ca147336d14781edb8dbee8b03c2e4bcd6646f5473a9d93d31fe73fecb","src/frames/reader.rs":"e07ee9de74bc499c10afcda592fefd9a7eef3381c045aa14f6596d67313546ca","src/frames/tests/hframe.rs":"01ec74eb3eb25d95042aa0263f9267f89535e6b7b8c1161fab4ba9ee5352d4a7","src/frames/tests/mod.rs":"0610609b316767a6a022837d32ee0452e37ea296fde37e51bec87e7c77e923a3","src/frames/tests/reader.rs":"2bfadc7afbc41bff9f5f930b31550259a8a92484d35f6c5d8dd8fd9acfb88f5b","src/frames/tests/wtframe.rs":"589ebe1e62ce4da63b37b7d22cde7ba572ddbf29336fdcdbbcd0a745f79dacd8","src/frames/wtframe.rs":"1d9d0256ace2ba7262343ed035df795f21a4d45065792d3fd45b3391b6916b2f","src/headers_checks.rs":"be0f0109298dcc3a40350b7c0950076ddfe20617d195b305e3ffc8582557ab18","src/lib.rs":"4f908a021222bcc79b9d569bc3759a493379a20b47dfa228fddf51600bf6e446","src/priority.rs":"f3b77c208962e44a4e2d13138c6998b703d40e7bcf8f73ea84d8ef5b556e0aee","src/push_controller.rs":"13bccf2834ae19109504cf695a5948c3b2d03fd101bc032a92bb77a033423854","src/qlog.rs":"2debd75c7ea103c95ff79e44412f1408c3e496e324976100c55d5a833912b6c3","src/qpack_decoder_receiver.rs":"c927dfc3e58c71d282210ba79280f6f03e789733bc3bedc247e68bab516b9e9e","src/qpack_encoder_receiver.rs":"d0ac03cc111b6e1c555a8654d3234116f2b135b5b040edac23cefe2d640beba9","src/recv_message.rs":"eb711dbc6b3371373c26b75333ac5858edf0d30184b0e05d67ab02c656eb6619","src/request_target.rs":"6041a69a0a74969ec08bc164509c055e9bad99f53bbeb16c0aa17d108dd68b8c","src/send_message.rs":"7785af11b77cee398faf3f7a2875b41e251ed7a1b272c23f81a48334596ab836","src/server.rs":"b9e6060da36cfb467478f5b78b17e22a123214ad2d64c919ce688ea2bc0e24bb","src/server_connection_events.rs":"12d353ca6301467f6d475dde3b789951a5716c89ddd7dbf1383efef8082361f3","src/server_events.rs":"463dd2cb6f97a800bac32c93c4aa2a6289f71e33a89f3b33152460cb941fc378","src/settings.rs":"476b154b5eea4c8d69a4a790fee3e527cef4d375df1cfb5eed04ec56406fe15a","src/stream_type_reader.rs":"7a7226b7911d69f7e00ec4987c2a32a5e8a33463203398cbee1e6645d2691478","tests/httpconn.rs":"bb6927801a8c75e4f05eb6cdb1e7f2d57be69b74e68ddad2a1614f2aeed04369","tests/priority.rs":"364754507873298612ad12e8d1d106d26d993712142d0be4cbf056da5338854c","tests/send_message.rs":"b5435045b16429d9e626ea94a8f10e2937e1a5a878af0035763a4f5ec09bf53c","tests/webtransport.rs":"25794305017ff58e57dc3c3b9b078e5bfc1814ea82a521b7b7156228e613c092"},"package":null} \ No newline at end of file
diff --git a/third_party/rust/neqo-http3/Cargo.toml b/third_party/rust/neqo-http3/Cargo.toml
index 8eeb2a58bf..12b9a125d0 100644
--- a/third_party/rust/neqo-http3/Cargo.toml
+++ b/third_party/rust/neqo-http3/Cargo.toml
@@ -10,22 +10,24 @@
# See Cargo.toml.orig for the original contents.
[package]
-edition = "2018"
-rust-version = "1.70.0"
+edition = "2021"
+rust-version = "1.74.0"
name = "neqo-http3"
-version = "0.7.0"
-authors = ["Dragana Damjanovic <dragana.damjano@gmail.com>"]
+version = "0.7.2"
+authors = ["The Neqo Authors <necko@mozilla.com>"]
+homepage = "https://github.com/mozilla/neqo/"
license = "MIT OR Apache-2.0"
+repository = "https://github.com/mozilla/neqo/"
-[dependencies]
-enumset = "1.1.2"
-lazy_static = "1.4"
-sfv = "0.9.3"
-smallvec = "1.11.1"
-url = "2.5"
+[lib]
+bench = false
+
+[dependencies.enumset]
+version = "1.1"
+default-features = false
[dependencies.log]
-version = "0.4.17"
+version = "0.4"
default-features = false
[dependencies.neqo-common]
@@ -41,15 +43,30 @@ path = "./../neqo-qpack"
path = "./../neqo-transport"
[dependencies.qlog]
-git = "https://github.com/cloudflare/quiche"
-rev = "09ea4b244096a013071cfe2175bbf2945fb7f8d1"
+version = "0.12"
+default-features = false
+
+[dependencies.sfv]
+version = "0.9"
+default-features = false
+
+[dependencies.smallvec]
+version = "1.11"
+default-features = false
+
+[dependencies.url]
+version = "2.5"
+default-features = false
[dev-dependencies.test-fixture]
path = "../test-fixture"
[features]
-deny-warnings = []
fuzzing = [
"neqo-transport/fuzzing",
"neqo-crypto/fuzzing",
]
+
+[lints.clippy.pedantic]
+level = "warn"
+priority = -1
diff --git a/third_party/rust/neqo-http3/src/client_events.rs b/third_party/rust/neqo-http3/src/client_events.rs
index 4b2ebc6c30..61aba8f9f1 100644
--- a/third_party/rust/neqo-http3/src/client_events.rs
+++ b/third_party/rust/neqo-http3/src/client_events.rs
@@ -61,7 +61,7 @@ pub enum Http3ClientEvent {
error: AppError,
local: bool,
},
- /// Peer has sent a STOP_SENDING.
+ /// Peer has sent a `STOP_SENDING`.
StopSending {
stream_id: StreamId,
error: AppError,
@@ -83,7 +83,7 @@ pub enum Http3ClientEvent {
PushDataReadable { push_id: u64 },
/// A push has been canceled.
PushCanceled { push_id: u64 },
- /// A push stream was been reset due to a HttpGeneralProtocol error.
+ /// A push stream was been reset due to a `HttpGeneralProtocol` error.
/// Most common case are malformed response headers.
PushReset { push_id: u64, error: AppError },
/// New stream can be created
@@ -102,7 +102,7 @@ pub enum Http3ClientEvent {
GoawayReceived,
/// Connection state change.
StateChange(Http3State),
- /// WebTransport events
+ /// `WebTransport` events
WebTransport(WebTransportEvent),
}
diff --git a/third_party/rust/neqo-http3/src/connection.rs b/third_party/rust/neqo-http3/src/connection.rs
index bb2b5a6ce0..287ea2c2af 100644
--- a/third_party/rust/neqo-http3/src/connection.rs
+++ b/third_party/rust/neqo-http3/src/connection.rs
@@ -835,9 +835,6 @@ impl Http3Connection {
final_headers.push(Header::new(":protocol", conn_type.string()));
}
- if let Some(priority_header) = request.priority.header() {
- final_headers.push(priority_header);
- }
final_headers.extend_from_slice(request.headers);
Ok(final_headers)
}
diff --git a/third_party/rust/neqo-http3/src/connection_client.rs b/third_party/rust/neqo-http3/src/connection_client.rs
index 5cc0541c0c..52572a760d 100644
--- a/third_party/rust/neqo-http3/src/connection_client.rs
+++ b/third_party/rust/neqo-http3/src/connection_client.rs
@@ -6,7 +6,6 @@
use std::{
cell::RefCell,
- convert::TryFrom,
fmt::{Debug, Display},
mem,
net::SocketAddr,
@@ -894,13 +893,6 @@ impl Http3Client {
self.process_http3(now);
}
- /// This should not be used because it gives access to functionalities that may disrupt the
- /// proper functioning of the HTTP/3 session.
- /// Only used by `neqo-interop`.
- pub fn conn(&mut self) -> &mut Connection {
- &mut self.conn
- }
-
/// Process HTTP3 layer.
/// When `process_output`, `process_input`, or `process` is called we must call this function
/// as well. The functions calls `Http3Client::check_connection_events` to handle events from
@@ -943,12 +935,12 @@ impl Http3Client {
/// returned. After that, the application should call the function again if a new UDP packet is
/// received and processed or the timer value expires.
///
- /// The HTTP/3 neqo implementation drives the HTTP/3 and QUC layers, therefore this function
+ /// The HTTP/3 neqo implementation drives the HTTP/3 and QUIC layers, therefore this function
/// will call both layers:
/// - First it calls HTTP/3 layer processing (`process_http3`) to make sure the layer writes
/// data to QUIC layer or cancels streams if needed.
/// - Then QUIC layer processing is called - [`Connection::process_output`][3]. This produces a
- /// packet or a timer value. It may also produce ned [`ConnectionEvent`][2]s, e.g. connection
+ /// packet or a timer value. It may also produce new [`ConnectionEvent`][2]s, e.g. connection
/// state-change event.
/// - Therefore the HTTP/3 layer processing (`process_http3`) is called again.
///
@@ -1296,7 +1288,7 @@ impl EventProvider for Http3Client {
#[cfg(test)]
mod tests {
- use std::{convert::TryFrom, mem, time::Duration};
+ use std::{mem, time::Duration};
use neqo_common::{event::Provider, qtrace, Datagram, Decoder, Encoder};
use neqo_crypto::{AllowZeroRtt, AntiReplay, ResumptionToken};
@@ -1306,8 +1298,9 @@ mod tests {
StreamType, Version, RECV_BUFFER_SIZE, SEND_BUFFER_SIZE,
};
use test_fixture::{
- addr, anti_replay, default_server_h3, fixture_init, new_server, now,
- CountingConnectionIdGenerator, DEFAULT_ALPN_H3, DEFAULT_KEYS, DEFAULT_SERVER_NAME,
+ anti_replay, default_server_h3, fixture_init, new_server, now,
+ CountingConnectionIdGenerator, DEFAULT_ADDR, DEFAULT_ALPN_H3, DEFAULT_KEYS,
+ DEFAULT_SERVER_NAME,
};
use super::{
@@ -1340,8 +1333,8 @@ mod tests {
Http3Client::new(
DEFAULT_SERVER_NAME,
Rc::new(RefCell::new(CountingConnectionIdGenerator::default())),
- addr(),
- addr(),
+ DEFAULT_ADDR,
+ DEFAULT_ADDR,
Http3Parameters::default()
.connection_parameters(
// Disable compatible upgrade, which complicates tests.
@@ -1996,7 +1989,7 @@ mod tests {
// The response header from PUSH_DATA (0x01, 0x06, 0x00, 0x00, 0xd9, 0x54, 0x01, 0x34) are
// decoded into:
fn check_push_response_header(header: &[Header]) {
- let expected_push_response_header = vec![
+ let expected_push_response_header = [
Header::new(":status", "200"),
Header::new("content-length", "4"),
];
@@ -3952,7 +3945,7 @@ mod tests {
);
}
x => {
- panic!("event {:?}", x);
+ panic!("event {x:?}");
}
}
@@ -3998,7 +3991,7 @@ mod tests {
assert!(fin);
}
x => {
- panic!("event {:?}", x);
+ panic!("event {x:?}");
}
}
// Stream should now be closed and gone
@@ -4071,7 +4064,7 @@ mod tests {
assert_eq!(stream_id, request_stream_id);
}
x => {
- panic!("event {:?}", x);
+ panic!("event {x:?}");
}
}
}
@@ -4135,7 +4128,7 @@ mod tests {
assert!(!interim);
recv_header = true;
} else {
- panic!("event {:?}", e);
+ panic!("event {e:?}");
}
}
assert!(recv_header);
diff --git a/third_party/rust/neqo-http3/src/control_stream_local.rs b/third_party/rust/neqo-http3/src/control_stream_local.rs
index 62676ee391..2f336c63a4 100644
--- a/third_party/rust/neqo-http3/src/control_stream_local.rs
+++ b/third_party/rust/neqo-http3/src/control_stream_local.rs
@@ -4,10 +4,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-use std::{
- collections::{HashMap, VecDeque},
- convert::TryFrom,
-};
+use std::collections::{HashMap, VecDeque};
use neqo_common::{qtrace, Encoder};
use neqo_transport::{Connection, StreamId, StreamType};
diff --git a/third_party/rust/neqo-http3/src/features/extended_connect/tests/webtransport/datagrams.rs b/third_party/rust/neqo-http3/src/features/extended_connect/tests/webtransport/datagrams.rs
index 1c58596dd3..27b7d2b2f2 100644
--- a/third_party/rust/neqo-http3/src/features/extended_connect/tests/webtransport/datagrams.rs
+++ b/third_party/rust/neqo-http3/src/features/extended_connect/tests/webtransport/datagrams.rs
@@ -4,8 +4,6 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-use std::convert::TryFrom;
-
use neqo_common::Encoder;
use neqo_transport::Error as TransportError;
diff --git a/third_party/rust/neqo-http3/src/features/extended_connect/tests/webtransport/mod.rs b/third_party/rust/neqo-http3/src/features/extended_connect/tests/webtransport/mod.rs
index 51dc47e4c1..3753c3122d 100644
--- a/third_party/rust/neqo-http3/src/features/extended_connect/tests/webtransport/mod.rs
+++ b/third_party/rust/neqo-http3/src/features/extended_connect/tests/webtransport/mod.rs
@@ -14,7 +14,7 @@ use neqo_common::event::Provider;
use neqo_crypto::AuthenticationStatus;
use neqo_transport::{ConnectionParameters, StreamId, StreamType};
use test_fixture::{
- addr, anti_replay, fixture_init, now, CountingConnectionIdGenerator, DEFAULT_ALPN_H3,
+ anti_replay, fixture_init, now, CountingConnectionIdGenerator, DEFAULT_ADDR, DEFAULT_ALPN_H3,
DEFAULT_KEYS, DEFAULT_SERVER_NAME,
};
@@ -38,8 +38,8 @@ pub fn default_http3_client(client_params: Http3Parameters) -> Http3Client {
Http3Client::new(
DEFAULT_SERVER_NAME,
Rc::new(RefCell::new(CountingConnectionIdGenerator::default())),
- addr(),
- addr(),
+ DEFAULT_ADDR,
+ DEFAULT_ADDR,
client_params,
now(),
)
diff --git a/third_party/rust/neqo-http3/src/features/extended_connect/webtransport_session.rs b/third_party/rust/neqo-http3/src/features/extended_connect/webtransport_session.rs
index adbdf07e11..5e89225956 100644
--- a/third_party/rust/neqo-http3/src/features/extended_connect/webtransport_session.rs
+++ b/third_party/rust/neqo-http3/src/features/extended_connect/webtransport_session.rs
@@ -4,13 +4,11 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-#![allow(clippy::module_name_repetitions)]
-
-use std::{any::Any, cell::RefCell, collections::BTreeSet, mem, rc::Rc};
+use std::{cell::RefCell, collections::BTreeSet, mem, rc::Rc};
use neqo_common::{qtrace, Encoder, Header, MessageType, Role};
use neqo_qpack::{QPackDecoder, QPackEncoder};
-use neqo_transport::{streams::SendOrder, Connection, DatagramTracking, StreamId};
+use neqo_transport::{Connection, DatagramTracking, StreamId};
use super::{ExtendedConnectEvents, ExtendedConnectType, SessionCloseReason};
use crate::{
@@ -473,10 +471,6 @@ impl HttpRecvStream for Rc<RefCell<WebTransportSession>> {
fn priority_update_sent(&mut self) {
self.borrow_mut().priority_update_sent();
}
-
- fn any(&self) -> &dyn Any {
- self
- }
}
impl SendStream for Rc<RefCell<WebTransportSession>> {
@@ -492,16 +486,6 @@ impl SendStream for Rc<RefCell<WebTransportSession>> {
self.borrow_mut().has_data_to_send()
}
- fn set_sendorder(&mut self, _conn: &mut Connection, _sendorder: Option<SendOrder>) -> Res<()> {
- // Not relevant on session
- Ok(())
- }
-
- fn set_fairness(&mut self, _conn: &mut Connection, _fairness: bool) -> Res<()> {
- // Not relevant on session
- Ok(())
- }
-
fn stream_writable(&self) {}
fn done(&self) -> bool {
diff --git a/third_party/rust/neqo-http3/src/features/extended_connect/webtransport_streams.rs b/third_party/rust/neqo-http3/src/features/extended_connect/webtransport_streams.rs
index 84dcd20618..cdc692b8d7 100644
--- a/third_party/rust/neqo-http3/src/features/extended_connect/webtransport_streams.rs
+++ b/third_party/rust/neqo-http3/src/features/extended_connect/webtransport_streams.rs
@@ -215,16 +215,6 @@ impl SendStream for WebTransportSendStream {
}
}
- fn set_sendorder(&mut self, conn: &mut Connection, sendorder: Option<i64>) -> Res<()> {
- conn.stream_sendorder(self.stream_id, sendorder)
- .map_err(|_| crate::Error::InvalidStreamId)
- }
-
- fn set_fairness(&mut self, conn: &mut Connection, fairness: bool) -> Res<()> {
- conn.stream_fairness(self.stream_id, fairness)
- .map_err(|_| crate::Error::InvalidStreamId)
- }
-
fn handle_stop_sending(&mut self, close_type: CloseType) {
self.set_done(close_type);
}
diff --git a/third_party/rust/neqo-http3/src/frames/hframe.rs b/third_party/rust/neqo-http3/src/frames/hframe.rs
index 83e69ba894..e69f7b449e 100644
--- a/third_party/rust/neqo-http3/src/frames/hframe.rs
+++ b/third_party/rust/neqo-http3/src/frames/hframe.rs
@@ -74,10 +74,7 @@ impl HFrame {
Self::MaxPushId { .. } => H3_FRAME_TYPE_MAX_PUSH_ID,
Self::PriorityUpdateRequest { .. } => H3_FRAME_TYPE_PRIORITY_UPDATE_REQUEST,
Self::PriorityUpdatePush { .. } => H3_FRAME_TYPE_PRIORITY_UPDATE_PUSH,
- Self::Grease => {
- let r = random(7);
- Decoder::from(&r).decode_uint(7).unwrap() * 0x1f + 0x21
- }
+ Self::Grease => Decoder::from(&random::<7>()).decode_uint(7).unwrap() * 0x1f + 0x21,
}
}
@@ -120,7 +117,7 @@ impl HFrame {
}
Self::Grease => {
// Encode some number of random bytes.
- let r = random(8);
+ let r = random::<8>();
enc.encode_vvec(&r[1..usize::from(1 + (r[0] & 0x7))]);
}
Self::PriorityUpdateRequest {
diff --git a/third_party/rust/neqo-http3/src/frames/reader.rs b/third_party/rust/neqo-http3/src/frames/reader.rs
index 5017c666a4..1a086683cf 100644
--- a/third_party/rust/neqo-http3/src/frames/reader.rs
+++ b/third_party/rust/neqo-http3/src/frames/reader.rs
@@ -6,7 +6,7 @@
#![allow(clippy::module_name_repetitions)]
-use std::{convert::TryFrom, fmt::Debug};
+use std::fmt::Debug;
use neqo_common::{
hex_with_len, qtrace, Decoder, IncrementalDecoderBuffer, IncrementalDecoderIgnore,
diff --git a/third_party/rust/neqo-http3/src/frames/wtframe.rs b/third_party/rust/neqo-http3/src/frames/wtframe.rs
index deb7a026a0..20e9b81936 100644
--- a/third_party/rust/neqo-http3/src/frames/wtframe.rs
+++ b/third_party/rust/neqo-http3/src/frames/wtframe.rs
@@ -4,8 +4,6 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-use std::convert::TryFrom;
-
use neqo_common::{Decoder, Encoder};
use crate::{frames::reader::FrameDecoder, Error, Res};
diff --git a/third_party/rust/neqo-http3/src/headers_checks.rs b/third_party/rust/neqo-http3/src/headers_checks.rs
index 9bf661c8fe..2dbf43cd32 100644
--- a/third_party/rust/neqo-http3/src/headers_checks.rs
+++ b/third_party/rust/neqo-http3/src/headers_checks.rs
@@ -6,8 +6,6 @@
#![allow(clippy::unused_unit)] // see https://github.com/Lymia/enumset/issues/44
-use std::convert::TryFrom;
-
use enumset::{enum_set, EnumSet, EnumSetType};
use neqo_common::Header;
diff --git a/third_party/rust/neqo-http3/src/lib.rs b/third_party/rust/neqo-http3/src/lib.rs
index 635707ca7c..8272151cc1 100644
--- a/third_party/rust/neqo-http3/src/lib.rs
+++ b/third_party/rust/neqo-http3/src/lib.rs
@@ -4,8 +4,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-#![cfg_attr(feature = "deny-warnings", deny(warnings))]
-#![warn(clippy::pedantic)]
+#![allow(clippy::module_name_repetitions)] // This lint doesn't work here.
/*!
@@ -160,7 +159,7 @@ mod server_events;
mod settings;
mod stream_type_reader;
-use std::{any::Any, cell::RefCell, fmt::Debug, rc::Rc};
+use std::{cell::RefCell, fmt::Debug, rc::Rc};
use buffered_send_stream::BufferedStream;
pub use client_events::{Http3ClientEvent, WebTransportEvent};
@@ -433,20 +432,15 @@ pub enum Http3StreamType {
}
#[must_use]
-#[derive(PartialEq, Eq, Debug)]
+#[derive(Default, PartialEq, Eq, Debug)]
enum ReceiveOutput {
+ #[default]
NoOutput,
ControlFrames(Vec<HFrame>),
UnblockedStreams(Vec<StreamId>),
NewStream(NewStreamType),
}
-impl Default for ReceiveOutput {
- fn default() -> Self {
- Self::NoOutput
- }
-}
-
trait Stream: Debug {
fn stream_type(&self) -> Http3StreamType;
}
@@ -509,8 +503,6 @@ trait HttpRecvStream: RecvStream {
fn extended_connect_wait_for_response(&self) -> bool {
false
}
-
- fn any(&self) -> &dyn Any;
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
@@ -567,27 +559,25 @@ trait HttpRecvStreamEvents: RecvStreamEvents {
trait SendStream: Stream {
/// # Errors
///
- /// Error my occur during sending data, e.g. protocol error, etc.
+ /// Error may occur during sending data, e.g. protocol error, etc.
fn send(&mut self, conn: &mut Connection) -> Res<()>;
fn has_data_to_send(&self) -> bool;
fn stream_writable(&self);
fn done(&self) -> bool;
- fn set_sendorder(&mut self, conn: &mut Connection, sendorder: Option<SendOrder>) -> Res<()>;
- fn set_fairness(&mut self, conn: &mut Connection, fairness: bool) -> Res<()>;
/// # Errors
///
- /// Error my occur during sending data, e.g. protocol error, etc.
+ /// Error may occur during sending data, e.g. protocol error, etc.
fn send_data(&mut self, _conn: &mut Connection, _buf: &[u8]) -> Res<usize>;
/// # Errors
///
- /// It may happen that the transport stream is already close. This is unlikely.
+ /// It may happen that the transport stream is already closed. This is unlikely.
fn close(&mut self, conn: &mut Connection) -> Res<()>;
/// # Errors
///
- /// It may happen that the transport stream is already close. This is unlikely.
+ /// It may happen that the transport stream is already closed. This is unlikely.
fn close_with_message(
&mut self,
_conn: &mut Connection,
@@ -606,7 +596,7 @@ trait SendStream: Stream {
/// # Errors
///
- /// It may happen that the transport stream is already close. This is unlikely.
+ /// It may happen that the transport stream is already closed. This is unlikely.
fn send_data_atomic(&mut self, _conn: &mut Connection, _buf: &[u8]) -> Res<()> {
Err(Error::InvalidStreamId)
}
@@ -627,7 +617,6 @@ trait HttpSendStream: SendStream {
/// This can also return an error if the underlying stream is closed.
fn send_headers(&mut self, headers: &[Header], conn: &mut Connection) -> Res<()>;
fn set_new_listener(&mut self, _conn_events: Box<dyn SendStreamEvents>) {}
- fn any(&self) -> &dyn Any;
}
trait SendStreamEvents: Debug {
diff --git a/third_party/rust/neqo-http3/src/priority.rs b/third_party/rust/neqo-http3/src/priority.rs
index f2651d3bb5..76a2cb9a85 100644
--- a/third_party/rust/neqo-http3/src/priority.rs
+++ b/third_party/rust/neqo-http3/src/priority.rs
@@ -1,4 +1,10 @@
-use std::{convert::TryFrom, fmt};
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::fmt;
use neqo_transport::StreamId;
use sfv::{BareItem, Item, ListEntry, Parser};
diff --git a/third_party/rust/neqo-http3/src/push_controller.rs b/third_party/rust/neqo-http3/src/push_controller.rs
index c4591991ae..ab6afccdf6 100644
--- a/third_party/rust/neqo-http3/src/push_controller.rs
+++ b/third_party/rust/neqo-http3/src/push_controller.rs
@@ -1,3 +1,4 @@
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
@@ -6,7 +7,6 @@
use std::{
cell::RefCell,
collections::VecDeque,
- convert::TryFrom,
fmt::{Debug, Display},
mem,
rc::Rc,
diff --git a/third_party/rust/neqo-http3/src/qlog.rs b/third_party/rust/neqo-http3/src/qlog.rs
index c3a13fd19f..81f9245a3c 100644
--- a/third_party/rust/neqo-http3/src/qlog.rs
+++ b/third_party/rust/neqo-http3/src/qlog.rs
@@ -6,14 +6,9 @@
// Functions that handle capturing QLOG traces.
-use std::convert::TryFrom;
-
use neqo_common::qlog::NeqoQlog;
use neqo_transport::StreamId;
-use qlog::{
- self,
- events::{DataRecipient, EventData},
-};
+use qlog::events::{DataRecipient, EventData};
pub fn h3_data_moved_up(qlog: &mut NeqoQlog, stream_id: StreamId, amount: usize) {
qlog.add_event_data(|| {
diff --git a/third_party/rust/neqo-http3/src/recv_message.rs b/third_party/rust/neqo-http3/src/recv_message.rs
index 36e8f65b19..be58b7e47c 100644
--- a/third_party/rust/neqo-http3/src/recv_message.rs
+++ b/third_party/rust/neqo-http3/src/recv_message.rs
@@ -4,9 +4,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-use std::{
- any::Any, cell::RefCell, cmp::min, collections::VecDeque, convert::TryFrom, fmt::Debug, rc::Rc,
-};
+use std::{cell::RefCell, cmp::min, collections::VecDeque, fmt::Debug, rc::Rc};
use neqo_common::{qdebug, qinfo, qtrace, Header};
use neqo_qpack::decoder::QPackDecoder;
@@ -494,8 +492,4 @@ impl HttpRecvStream for RecvMessage {
fn extended_connect_wait_for_response(&self) -> bool {
matches!(self.state, RecvMessageState::ExtendedConnect)
}
-
- fn any(&self) -> &dyn Any {
- self
- }
}
diff --git a/third_party/rust/neqo-http3/src/send_message.rs b/third_party/rust/neqo-http3/src/send_message.rs
index 96156938a0..c50e3e056a 100644
--- a/third_party/rust/neqo-http3/src/send_message.rs
+++ b/third_party/rust/neqo-http3/src/send_message.rs
@@ -4,11 +4,11 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-use std::{any::Any, cell::RefCell, cmp::min, fmt::Debug, rc::Rc};
+use std::{cell::RefCell, cmp::min, fmt::Debug, rc::Rc};
use neqo_common::{qdebug, qinfo, qtrace, Encoder, Header, MessageType};
use neqo_qpack::encoder::QPackEncoder;
-use neqo_transport::{streams::SendOrder, Connection, StreamId};
+use neqo_transport::{Connection, StreamId};
use crate::{
frames::HFrame,
@@ -270,16 +270,6 @@ impl SendStream for SendMessage {
self.stream.has_buffered_data()
}
- fn set_sendorder(&mut self, _conn: &mut Connection, _sendorder: Option<SendOrder>) -> Res<()> {
- // Not relevant for SendMessage
- Ok(())
- }
-
- fn set_fairness(&mut self, _conn: &mut Connection, _fairness: bool) -> Res<()> {
- // Not relevant for SendMessage
- Ok(())
- }
-
fn close(&mut self, conn: &mut Connection) -> Res<()> {
self.state.fin()?;
if !self.stream.has_buffered_data() {
@@ -332,10 +322,6 @@ impl HttpSendStream for SendMessage {
self.stream_type = Http3StreamType::ExtendedConnect;
self.conn_events = conn_events;
}
-
- fn any(&self) -> &dyn Any {
- self
- }
}
impl ::std::fmt::Display for SendMessage {
diff --git a/third_party/rust/neqo-http3/src/server.rs b/third_party/rust/neqo-http3/src/server.rs
index b29f715451..1396a4e4cf 100644
--- a/third_party/rust/neqo-http3/src/server.rs
+++ b/third_party/rust/neqo-http3/src/server.rs
@@ -151,7 +151,7 @@ impl Http3Server {
active_conns.dedup();
active_conns
.iter()
- .for_each(|conn| self.server.add_to_waiting(conn.clone()));
+ .for_each(|conn| self.server.add_to_waiting(conn));
for mut conn in active_conns {
self.process_events(&mut conn, now);
}
@@ -1271,11 +1271,11 @@ mod tests {
while let Some(event) = hconn.next_event() {
match event {
Http3ServerEvent::Headers { stream, .. } => {
- assert!(requests.get(&stream).is_none());
+ assert!(!requests.contains_key(&stream));
requests.insert(stream, 0);
}
Http3ServerEvent::Data { stream, .. } => {
- assert!(requests.get(&stream).is_some());
+ assert!(requests.contains_key(&stream));
}
Http3ServerEvent::DataWritable { .. }
| Http3ServerEvent::StreamReset { .. }
diff --git a/third_party/rust/neqo-http3/src/server_events.rs b/third_party/rust/neqo-http3/src/server_events.rs
index 4be48363df..a85ece0bfb 100644
--- a/third_party/rust/neqo-http3/src/server_events.rs
+++ b/third_party/rust/neqo-http3/src/server_events.rs
@@ -9,7 +9,6 @@
use std::{
cell::RefCell,
collections::VecDeque,
- convert::TryFrom,
ops::{Deref, DerefMut},
rc::Rc,
};
diff --git a/third_party/rust/neqo-http3/tests/priority.rs b/third_party/rust/neqo-http3/tests/priority.rs
index cdec161058..77d19e6fcf 100644
--- a/third_party/rust/neqo-http3/tests/priority.rs
+++ b/third_party/rust/neqo-http3/tests/priority.rs
@@ -68,7 +68,7 @@ fn priority_update() {
Instant::now(),
"GET",
&("https", "something.com", "/"),
- &[],
+ &[Header::new("priority", "u=4,i")],
Priority::new(4, true),
)
.unwrap();
@@ -98,7 +98,7 @@ fn priority_update() {
assert_eq!(&headers, expected_headers);
assert!(!fin);
}
- other => panic!("unexpected server event: {:?}", other),
+ other => panic!("unexpected server event: {other:?}"),
}
let update_priority = Priority::new(3, false);
@@ -129,7 +129,7 @@ fn priority_update_dont_send_for_cancelled_stream() {
Instant::now(),
"GET",
&("https", "something.com", "/"),
- &[],
+ &[Header::new("priority", "u=5")],
Priority::new(5, false),
)
.unwrap();
diff --git a/third_party/rust/neqo-http3/tests/send_message.rs b/third_party/rust/neqo-http3/tests/send_message.rs
index 507c4bd552..fbf9a7a3ea 100644
--- a/third_party/rust/neqo-http3/tests/send_message.rs
+++ b/third_party/rust/neqo-http3/tests/send_message.rs
@@ -4,7 +4,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-use lazy_static::lazy_static;
+use std::sync::OnceLock;
+
use neqo_common::event::Provider;
use neqo_crypto::AuthenticationStatus;
use neqo_http3::{
@@ -15,14 +16,14 @@ use test_fixture::*;
const RESPONSE_DATA: &[u8] = &[0x61, 0x62, 0x63];
-lazy_static! {
- static ref RESPONSE_HEADER_NO_DATA: Vec<Header> =
- vec![Header::new(":status", "200"), Header::new("something", "3")];
+fn response_header_no_data() -> &'static Vec<Header> {
+ static HEADERS: OnceLock<Vec<Header>> = OnceLock::new();
+ HEADERS.get_or_init(|| vec![Header::new(":status", "200"), Header::new("something", "3")])
}
-lazy_static! {
- static ref RESPONSE_HEADER_103: Vec<Header> =
- vec![Header::new(":status", "103"), Header::new("link", "...")];
+fn response_header_103() -> &'static Vec<Header> {
+ static HEADERS: OnceLock<Vec<Header>> = OnceLock::new();
+ HEADERS.get_or_init(|| vec![Header::new(":status", "103"), Header::new("link", "...")])
}
fn exchange_packets(client: &mut Http3Client, server: &mut Http3Server) {
@@ -68,7 +69,7 @@ fn send_trailers(request: &mut Http3OrWebTransportStream) -> Result<(), Error> {
}
fn send_informational_headers(request: &mut Http3OrWebTransportStream) -> Result<(), Error> {
- request.send_headers(&RESPONSE_HEADER_103)
+ request.send_headers(response_header_103())
}
fn send_headers(request: &mut Http3OrWebTransportStream) -> Result<(), Error> {
@@ -90,7 +91,7 @@ fn process_client_events(conn: &mut Http3Client) {
Header::new(":status", "200"),
Header::new("content-length", "3"),
])
- || (headers.as_ref() == *RESPONSE_HEADER_103)
+ || (headers.as_ref() == *response_header_103())
);
assert!(!fin);
response_header_found = true;
@@ -116,7 +117,7 @@ fn process_client_events_no_data(conn: &mut Http3Client) {
while let Some(event) = conn.next_event() {
match event {
Http3ClientEvent::HeaderReady { headers, fin, .. } => {
- assert_eq!(headers.as_ref(), *RESPONSE_HEADER_NO_DATA);
+ assert_eq!(headers.as_ref(), *response_header_no_data());
fin_received = fin;
response_header_found = true;
}
@@ -201,7 +202,7 @@ fn response_trailers3() {
#[test]
fn response_trailers_no_data() {
let (mut hconn_c, mut hconn_s, mut request) = connect_send_and_receive_request();
- request.send_headers(&RESPONSE_HEADER_NO_DATA).unwrap();
+ request.send_headers(response_header_no_data()).unwrap();
exchange_packets(&mut hconn_c, &mut hconn_s);
send_trailers(&mut request).unwrap();
exchange_packets(&mut hconn_c, &mut hconn_s);
@@ -258,10 +259,10 @@ fn trailers_after_close() {
#[test]
fn multiple_response_headers() {
let (mut hconn_c, mut hconn_s, mut request) = connect_send_and_receive_request();
- request.send_headers(&RESPONSE_HEADER_NO_DATA).unwrap();
+ request.send_headers(response_header_no_data()).unwrap();
assert_eq!(
- request.send_headers(&RESPONSE_HEADER_NO_DATA),
+ request.send_headers(response_header_no_data()),
Err(Error::InvalidHeader)
);
@@ -273,7 +274,7 @@ fn multiple_response_headers() {
#[test]
fn informational_after_response_headers() {
let (mut hconn_c, mut hconn_s, mut request) = connect_send_and_receive_request();
- request.send_headers(&RESPONSE_HEADER_NO_DATA).unwrap();
+ request.send_headers(response_header_no_data()).unwrap();
assert_eq!(
send_informational_headers(&mut request),
@@ -307,7 +308,7 @@ fn non_trailers_headers_after_data() {
exchange_packets(&mut hconn_c, &mut hconn_s);
assert_eq!(
- request.send_headers(&RESPONSE_HEADER_NO_DATA),
+ request.send_headers(response_header_no_data()),
Err(Error::InvalidHeader)
);
diff --git a/third_party/rust/neqo-http3/tests/webtransport.rs b/third_party/rust/neqo-http3/tests/webtransport.rs
index 4e943d86cb..b1e18a5a98 100644
--- a/third_party/rust/neqo-http3/tests/webtransport.rs
+++ b/third_party/rust/neqo-http3/tests/webtransport.rs
@@ -15,7 +15,7 @@ use neqo_http3::{
};
use neqo_transport::{StreamId, StreamType};
use test_fixture::{
- addr, anti_replay, fixture_init, now, CountingConnectionIdGenerator, DEFAULT_ALPN_H3,
+ anti_replay, fixture_init, now, CountingConnectionIdGenerator, DEFAULT_ADDR, DEFAULT_ALPN_H3,
DEFAULT_KEYS, DEFAULT_SERVER_NAME,
};
@@ -24,8 +24,8 @@ fn connect() -> (Http3Client, Http3Server) {
let mut client = Http3Client::new(
DEFAULT_SERVER_NAME,
Rc::new(RefCell::new(CountingConnectionIdGenerator::default())),
- addr(),
- addr(),
+ DEFAULT_ADDR,
+ DEFAULT_ADDR,
Http3Parameters::default().webtransport(true),
now(),
)
diff --git a/third_party/rust/neqo-qpack/.cargo-checksum.json b/third_party/rust/neqo-qpack/.cargo-checksum.json
index 2bbec5df43..aae0a1e594 100644
--- a/third_party/rust/neqo-qpack/.cargo-checksum.json
+++ b/third_party/rust/neqo-qpack/.cargo-checksum.json
@@ -1 +1 @@
-{"files":{"Cargo.toml":"2eabb2ad2846a08b51b306634ed75dc14ab3a43738b1190e3b4c4f2beb00b8e2","src/decoder.rs":"7e468d59adff1fa9373cbb703d13a7503f721a89bebafd049feaf0308a39b606","src/decoder_instructions.rs":"d991d70e51f079bc5b30d3982fd0176edfa9bb7ba14c17a20ec3eea878c56206","src/encoder.rs":"e026da38c2c3410a4e9aa330cda09ac411008772dd66d262d6c375601cebf481","src/encoder_instructions.rs":"86e3abbd9cf94332041326ac6cf806ed64623e3fd38dbc0385b1f63c37e73fd9","src/header_block.rs":"3925476df69b90d950594faadc5cb24c374d46de8c75a374a235f0d27323a7d8","src/huffman.rs":"8b0b2ea069c2a6eb6677b076b99b08ac0d29eabe1f2bbbab37f18f49187ef276","src/huffman_decode_helper.rs":"81309e27ff8f120a10c0b1598888ded21b76e297dc02cea8c7378d6a6627d62a","src/huffman_table.rs":"06fea766a6276ac56c7ee0326faed800a742c15fda1f33bf2513e6cc6a5e6d27","src/lib.rs":"196114397c2b1bf6ef154206018f519b012789cf712e89b069a7616d7278ef3a","src/prefix.rs":"fb4a9acbcf6fd3178f4474404cd3d3b131abca934f69fe14a9d744bc7e636dc5","src/qlog.rs":"e320007ea8309546b26f9c0019ab8722da80dbd38fa976233fd8ae19a0af637c","src/qpack_send_buf.rs":"14d71310c260ee15ea40a783998b507c968eef12db2892b47c689e872b5242a5","src/reader.rs":"b9a7dccd726f471fc24f1d3304f03ac0a039c0828aac7b33c927be07d395c655","src/static_table.rs":"fda9d5c6f38f94b0bf92d3afdf8432dce6e27e189736596e16727090c77b78ec","src/stats.rs":"624dfa3b40858c304097bb0ce5b1be1bb4d7916b1abfc222f1aa705907009730","src/table.rs":"05dbec6483bb24c9fc8d721b70fdfefc2df53b458488b55104147f13c386a47d"},"package":null} \ No newline at end of file
+{"files":{"Cargo.toml":"c2152600379c3961ba79e661e164630a63531744f79e082fce39cdf1cbe75ddd","src/decoder.rs":"0675444129e074e9d5d56f0d45d2eaed614c85e22cfe9f2d28cdee912c15b420","src/decoder_instructions.rs":"d991d70e51f079bc5b30d3982fd0176edfa9bb7ba14c17a20ec3eea878c56206","src/encoder.rs":"84649cbee81e050f55d7ea691ac871e072741abd8bbf96303eb2e98aa8ee0aea","src/encoder_instructions.rs":"86e3abbd9cf94332041326ac6cf806ed64623e3fd38dbc0385b1f63c37e73fd9","src/header_block.rs":"3925476df69b90d950594faadc5cb24c374d46de8c75a374a235f0d27323a7d8","src/huffman.rs":"71ec740426eee0abb6205104e504f5b97f525a76c4a5f5827b78034d28ce1876","src/huffman_decode_helper.rs":"9ce470e318b3664f58aa109bed483ab15bfd9e0b17d261ea2b609668a42a9d80","src/huffman_table.rs":"06fea766a6276ac56c7ee0326faed800a742c15fda1f33bf2513e6cc6a5e6d27","src/lib.rs":"fd673630b5ed64197851c9a9758685096d3c0aa04f4994290733a38057004ee6","src/prefix.rs":"fb4a9acbcf6fd3178f4474404cd3d3b131abca934f69fe14a9d744bc7e636dc5","src/qlog.rs":"e320007ea8309546b26f9c0019ab8722da80dbd38fa976233fd8ae19a0af637c","src/qpack_send_buf.rs":"755af90fe077b1bcca34a1a2a1bdce5ce601ea490b2ca3f1313e0107d13e67e2","src/reader.rs":"1581261741a0922b147a6975cc8b1a3503846f6dbfdb771d254760c298996982","src/static_table.rs":"fda9d5c6f38f94b0bf92d3afdf8432dce6e27e189736596e16727090c77b78ec","src/stats.rs":"624dfa3b40858c304097bb0ce5b1be1bb4d7916b1abfc222f1aa705907009730","src/table.rs":"6e16debdceadc453546f247f8316883af9eeeedd12f2070219d8484a0a131d46"},"package":null} \ No newline at end of file
diff --git a/third_party/rust/neqo-qpack/Cargo.toml b/third_party/rust/neqo-qpack/Cargo.toml
index 7df63b7bf6..1becac8190 100644
--- a/third_party/rust/neqo-qpack/Cargo.toml
+++ b/third_party/rust/neqo-qpack/Cargo.toml
@@ -10,19 +10,20 @@
# See Cargo.toml.orig for the original contents.
[package]
-edition = "2018"
-rust-version = "1.70.0"
+edition = "2021"
+rust-version = "1.74.0"
name = "neqo-qpack"
-version = "0.7.0"
-authors = ["Dragana Damjanovic <dragana.damjano@gmail.com>"]
+version = "0.7.2"
+authors = ["The Neqo Authors <necko@mozilla.com>"]
+homepage = "https://github.com/mozilla/neqo/"
license = "MIT OR Apache-2.0"
+repository = "https://github.com/mozilla/neqo/"
-[dependencies]
-lazy_static = "~1.4.0"
-static_assertions = "~1.1.0"
+[lib]
+bench = false
[dependencies.log]
-version = "~0.4.17"
+version = "0.4"
default-features = false
[dependencies.neqo-common]
@@ -35,11 +36,16 @@ path = "./../neqo-crypto"
path = "./../neqo-transport"
[dependencies.qlog]
-git = "https://github.com/cloudflare/quiche"
-rev = "09ea4b244096a013071cfe2175bbf2945fb7f8d1"
+version = "0.12"
+default-features = false
+
+[dependencies.static_assertions]
+version = "1.1"
+default-features = false
[dev-dependencies.test-fixture]
path = "../test-fixture"
-[features]
-deny-warnings = []
+[lints.clippy.pedantic]
+level = "warn"
+priority = -1
diff --git a/third_party/rust/neqo-qpack/src/decoder.rs b/third_party/rust/neqo-qpack/src/decoder.rs
index 2119db0256..b2cfb6629a 100644
--- a/third_party/rust/neqo-qpack/src/decoder.rs
+++ b/third_party/rust/neqo-qpack/src/decoder.rs
@@ -4,8 +4,6 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-use std::convert::TryFrom;
-
use neqo_common::{qdebug, Header};
use neqo_transport::{Connection, StreamId};
@@ -287,7 +285,7 @@ fn map_error(err: &Error) -> Error {
#[cfg(test)]
mod tests {
- use std::{convert::TryFrom, mem};
+ use std::mem;
use neqo_common::Header;
use neqo_transport::{StreamId, StreamType};
diff --git a/third_party/rust/neqo-qpack/src/encoder.rs b/third_party/rust/neqo-qpack/src/encoder.rs
index c7921ee2c0..c90570ccdc 100644
--- a/third_party/rust/neqo-qpack/src/encoder.rs
+++ b/third_party/rust/neqo-qpack/src/encoder.rs
@@ -4,10 +4,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-use std::{
- collections::{HashMap, HashSet, VecDeque},
- convert::TryFrom,
-};
+use std::collections::{HashMap, HashSet, VecDeque};
use neqo_common::{qdebug, qerror, qlog::NeqoQlog, qtrace, Header};
use neqo_transport::{Connection, Error as TransportError, StreamId};
diff --git a/third_party/rust/neqo-qpack/src/huffman.rs b/third_party/rust/neqo-qpack/src/huffman.rs
index 283a501b32..30bb880438 100644
--- a/third_party/rust/neqo-qpack/src/huffman.rs
+++ b/third_party/rust/neqo-qpack/src/huffman.rs
@@ -4,10 +4,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-use std::convert::TryFrom;
-
use crate::{
- huffman_decode_helper::{HuffmanDecoderNode, HUFFMAN_DECODE_ROOT},
+ huffman_decode_helper::{huffman_decoder_root, HuffmanDecoderNode},
huffman_table::HUFFMAN_TABLE,
Error, Res,
};
@@ -93,7 +91,7 @@ pub fn decode_huffman(input: &[u8]) -> Res<Vec<u8>> {
}
fn decode_character(reader: &mut BitReader) -> Res<Option<u16>> {
- let mut node: &HuffmanDecoderNode = &HUFFMAN_DECODE_ROOT;
+ let mut node: &HuffmanDecoderNode = huffman_decoder_root();
let mut i = 0;
while node.value.is_none() {
match reader.read_bit() {
diff --git a/third_party/rust/neqo-qpack/src/huffman_decode_helper.rs b/third_party/rust/neqo-qpack/src/huffman_decode_helper.rs
index 122226dd1f..939312ab22 100644
--- a/third_party/rust/neqo-qpack/src/huffman_decode_helper.rs
+++ b/third_party/rust/neqo-qpack/src/huffman_decode_helper.rs
@@ -4,9 +4,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-use std::convert::TryFrom;
-
-use lazy_static::lazy_static;
+use std::sync::OnceLock;
use crate::huffman_table::HUFFMAN_TABLE;
@@ -15,8 +13,9 @@ pub struct HuffmanDecoderNode {
pub value: Option<u16>,
}
-lazy_static! {
- pub static ref HUFFMAN_DECODE_ROOT: HuffmanDecoderNode = make_huffman_tree(0, 0);
+pub fn huffman_decoder_root() -> &'static HuffmanDecoderNode {
+ static ROOT: OnceLock<HuffmanDecoderNode> = OnceLock::new();
+ ROOT.get_or_init(|| make_huffman_tree(0, 0))
}
fn make_huffman_tree(prefix: u32, len: u8) -> HuffmanDecoderNode {
diff --git a/third_party/rust/neqo-qpack/src/lib.rs b/third_party/rust/neqo-qpack/src/lib.rs
index 1581712017..10ee5df61c 100644
--- a/third_party/rust/neqo-qpack/src/lib.rs
+++ b/third_party/rust/neqo-qpack/src/lib.rs
@@ -4,11 +4,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-#![cfg_attr(feature = "deny-warnings", deny(warnings))]
-#![warn(clippy::pedantic)]
-// This is because of Encoder and Decoder structs. TODO: think about a better namings for crate and
-// structs.
-#![allow(clippy::module_name_repetitions)]
+#![allow(clippy::module_name_repetitions)] // This lint doesn't work here.
pub mod decoder;
mod decoder_instructions;
diff --git a/third_party/rust/neqo-qpack/src/qpack_send_buf.rs b/third_party/rust/neqo-qpack/src/qpack_send_buf.rs
index a443859081..c0b8d7af1b 100644
--- a/third_party/rust/neqo-qpack/src/qpack_send_buf.rs
+++ b/third_party/rust/neqo-qpack/src/qpack_send_buf.rs
@@ -4,7 +4,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-use std::{convert::TryFrom, ops::Deref};
+use std::ops::Deref;
use neqo_common::Encoder;
diff --git a/third_party/rust/neqo-qpack/src/reader.rs b/third_party/rust/neqo-qpack/src/reader.rs
index ff9c42b246..0173ed7888 100644
--- a/third_party/rust/neqo-qpack/src/reader.rs
+++ b/third_party/rust/neqo-qpack/src/reader.rs
@@ -4,7 +4,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-use std::{convert::TryInto, mem, str};
+use std::{mem, str};
use neqo_common::{qdebug, qerror};
use neqo_transport::{Connection, StreamId};
@@ -223,20 +223,19 @@ impl IntReader {
}
}
-#[derive(Debug)]
+#[derive(Debug, Default)]
enum LiteralReaderState {
+ #[default]
ReadHuffman,
- ReadLength { reader: IntReader },
- ReadLiteral { offset: usize },
+ ReadLength {
+ reader: IntReader,
+ },
+ ReadLiteral {
+ offset: usize,
+ },
Done,
}
-impl Default for LiteralReaderState {
- fn default() -> Self {
- Self::ReadHuffman
- }
-}
-
/// This is decoder of a literal with a prefix:
/// 1) ignores `prefix_len` bits of the first byte,
/// 2) reads "huffman bit"
diff --git a/third_party/rust/neqo-qpack/src/table.rs b/third_party/rust/neqo-qpack/src/table.rs
index 7ce8572542..517e98db09 100644
--- a/third_party/rust/neqo-qpack/src/table.rs
+++ b/third_party/rust/neqo-qpack/src/table.rs
@@ -4,7 +4,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-use std::{collections::VecDeque, convert::TryFrom};
+use std::collections::VecDeque;
use neqo_common::qtrace;
diff --git a/third_party/rust/neqo-transport/.cargo-checksum.json b/third_party/rust/neqo-transport/.cargo-checksum.json
index ba33141b7a..669c0120f0 100644
--- a/third_party/rust/neqo-transport/.cargo-checksum.json
+++ b/third_party/rust/neqo-transport/.cargo-checksum.json
@@ -1 +1 @@
-{"files":{"Cargo.toml":"d31e1132faa19d4a3025d3b7a98a38d09591b8b75735896d5afbd7e8fdb4434d","benches/rx_stream_orderer.rs":"5f32aba0066bca15aedbf059f9b00f64ced11aa7222c0b0c5ea202bdd9e6ef14","src/ackrate.rs":"c8d8933ccd8255e5b0712a4a7c4a7304de16a430325d5125fdc538a623874279","src/addr_valid.rs":"d1badfd0ab71ad8c6368a398f52d23f817d70e70653a3313353af34542525603","src/cc/classic_cc.rs":"15b735d6c7054489fd0fadc25cbee8b88b4efe1ee0dcc43354b1552183a8b2d8","src/cc/cubic.rs":"f6669242f6566b1de711b8ff59051919a5aa9106da43ed16ae83d6fe614cec11","src/cc/mod.rs":"0141bcadb719a7fe75d037f4ebe19c7f7bdbf9177314ea5b97ee7244b14b162b","src/cc/new_reno.rs":"1d2790260fe8147b4a40c1792e862ab30b204cf4cf8fef45f5d50d6975011ec2","src/cc/tests/cubic.rs":"5367da8fa627046379bacba45a0f993b7305aef48d954c13004cb7ae88dc04ec","src/cc/tests/mod.rs":"1567bf0ddaff5cb679217f2fd65f01e15a302b9b9e68b69f3e617dcaf7b3e5ec","src/cc/tests/new_reno.rs":"7e8a81c3f16d1f21f8b42b2abba4cf8ea6f78cb2ea05c4d85d7c1cb71c1db464","src/cid.rs":"91ed2b635aabde43ed5e9d383d26e9b3a28e92f218adb8feea650d9c4e55ec0a","src/connection/dump.rs":"aea2f97fa78f3d9e0fe32c2a58ce70a7050aced3abde8b06183ed88d02571ec1","src/connection/idle.rs":"b3bc2ad1290e54278d8703092d135eda973eb12316d1f6dffedaffdf25e2a47e","src/connection/mod.rs":"c38de7f0114d2218f3fc5024bd7570199712f57c399642a7b3be0a107845d947","src/connection/params.rs":"c6433e78953df329fa241c7eba0220743f8028d0ca9c1da0021c7f5973aae5c8","src/connection/saved.rs":"97eb19792be3c4d721057021a43ea50a52f89a3cfa583d3d3dcf5d9144b332f5","src/connection/state.rs":"04352beb60ec9b51b41ae2999acb0086f3f90dc94fa1b2becf3921ec0e6ba5b1","src/connection/test_internal.rs":"f3ebfe97b25c9c716d41406066295e5aff4e96a3051ef4e2b5fb258282bbc14c","src/connection/tests/ackrate.rs":"aa92c91185a74eeb2abcc86d19d746b8de3feb7ad507494be9042a6ec37b491e","src/connection/tests/cc.rs":"ee567e43b626353beaae2f0a9e09266bbb8d62bc14178743fc3606bc53c5b6b1","src/connection/tests/close.rs":"c309974598b0b51793d54be1470338d692f1111f79ea985a5c73d62d780d15f7","src/connection/tests/datagram.rs":"ae2853c4b8dbae4b00940adcc8bd114065f134944f182270987d55daa1b27adb","src/connection/tests/fuzzing.rs":"a877ce6cb005099eb4ae1f5649c63c4b7a5c108c9459a3bb36490965712f5310","src/connection/tests/handshake.rs":"1bed309e8358dfb5026e12da7ea2f8bdf42e910fb8b41809b554c0a484a264e8","src/connection/tests/idle.rs":"30077587ed3e22934c82d675119bdcc696a91d4d0d1908fb5f4c9f8b085fd8d9","src/connection/tests/keys.rs":"792cf24ac51daff62c19dcb8c266824a9fd22cb1b715d416551ee270a26c9eb2","src/connection/tests/migration.rs":"33e0442b0d2d3e940ba4a42313419dd2d183174bede0b3046165410ce05b21b1","src/connection/tests/mod.rs":"f64c200035396f96adb6b86742c7117dc96cf500969c1eae2bddcb1d1c1c43f3","src/connection/tests/priority.rs":"e2299a873dca794573a10b0f94bbc5fdf8b75ed7531ee931c31ad7507bc71e6f","src/connection/tests/recovery.rs":"7f28767f3cca2ff60e3dcfa803e12ef043486a222f54681a8faf2ea2fee564a1","src/connection/tests/resumption.rs":"94550cd961b98fba6ab30ff97a538919c76112394470ac00568ea1ac66e6e323","src/connection/tests/stream.rs":"8d3b6fa884847de15910b005c5f9cdfcbdf5eecec5bb84804a842a85f075b0c3","src/connection/tests/vn.rs":"d2539caf17b455c9259d7cfbd823e110da463890997e578f8e74af5f468a4a7b","src/connection/tests/zerortt.rs":"73180babcf24b0eccef91656acfaac953d3abeab52d7d14cede0188ea9d40fc6","src/crypto.rs":"2070f445d4b25d6dc500ba9cf5dcf29383aba217e4ba2e0e312a45e073b28dc6","src/events.rs":"70f989e60004f62d39398c557e36337457810c5942dcfb9631f707f7ac59466d","src/fc.rs":"6c4cd4a3854e5442b33f309a24c0b1a040cdc8193611ea0e05c66b7f6fa1f68c","src/frame.rs":"5e2d28051ef585fdcfb47e7ed666f5000ad38b5407356b07da4ccd6595d3cc34","src/lib.rs":"ef0481f82f5d2173aa202fad0376dbf99e14ae3d58b3bfca4f3da8ec4e07ce8c","src/pace.rs":"05e38e711af705ea52b75843c2226c86fba3605f00d56be00038f5f769a5c8a2","src/packet/mod.rs":"a52648a30a2f066d74a7021b97b99163cf8037644faddef8138ee2dca8ec6ffa","src/packet/retry.rs":"d4cd0f68af678d212040a9e21440697cddb61811261d6e5e9117b47926b49eda","src/path.rs":"fb5240ec491f087eaa86bc737fdfaa3d2c063d69ab12c11d54186a9c8e68714f","src/qlog.rs":"83a605006a98bedd1ed13de8fc1b46aca6b0eaf26a1af5ce8bb936d1dcd6ed9a","src/quic_datagrams.rs":"bd035ac89cf4c7f94d9b839c54cc0298c5db76f7c9e55138455e3246aac01e1e","src/recovery.rs":"fdd85ae2c11bb4efa0f83fec8723a55466089384bea72f86fd1c921d586fe692","src/recv_stream.rs":"40e9da357e43fe668853f2f8251b012cea8e1331230148c448def092551f3b49","src/rtt.rs":"39039f8140e0729085e8090a7f3572cc9f1334936d2f04ff221d387abaecb282","src/send_stream.rs":"da5564521eb7ecfd31326a168c6bc957ec6e1ac832e885d7877570a5fae77222","src/sender.rs":"fe3970579b1d3869ca967058db1261b710f5b8ab6a3f47199e0db5ed5bae75ce","src/server.rs":"b2d9acbe5455255187611599f3c21af2c706968a1b042bdde9a59bdb96b5ac2a","src/stats.rs":"7e7aabe62b0d67151fdfd6b2024230ea1418955ed0ed2f03cbaef0d0877d4221","src/stream_id.rs":"188a8177fd7819e9206bab359ff3002516ecc87322211f536c2bd766a5b7d9d8","src/streams.rs":"476460ce385cfd403656af37f544f75f25bfd261a25fe528df6e926fecd7c785","src/tparams.rs":"08359a7a24f51d741c38294b362c5a0206a2ed9335f0ef61502c7a2d6447b6d8","src/tracking.rs":"6b12710c73344b366c985ff01511601cd35137359c4be015a505e01b082f4f88","src/version.rs":"afe8b5db2a9303e411a24071539cbc58c857fdecd19b3898b37ee3ecac24253c","tests/common/mod.rs":"922872201a673248889c9f511ecc995831d0316e2c5dd2918194b63ee28560ac","tests/conn_vectors.rs":"6b3a1177ca0cb123008ee52a89606d94c19ee6f36f92c040266ce4b6ea13904b","tests/connection.rs":"601100578c1c8f8562917e585654309a8f2bc268c4bc6ab37e29af33b8328eac","tests/network.rs":"1d07b5db4079db3766443b773e661a7f070d402d51be94fb75c6f96696a6b016","tests/retry.rs":"b1077e3500f7387bdd23a65481b5d937cd3f4b0b220f635f2b1db27966a5d538","tests/server.rs":"d46a736c03855f06634b3aedba9d32563d861d4408ad3bb875450d723ea3742a","tests/sim/connection.rs":"6a1ffe23fbbcae957eacf0f9d53672e3b3a0dc133e5b3bb3e2aaba872b597a67","tests/sim/delay.rs":"31171d53ced9609c4f327cef48950dbbe9fecad0d208cbcd2b1bfee474d90e31","tests/sim/drop.rs":"02e6471466f4168d6a507c621dd10d0dfeb26f13ae08ed97401c7e3857a1f43a","tests/sim/mod.rs":"fd62f126e7ddef141fe07c7ea9b4c8ba00dfc4002698a3431eaf3badebca1a53","tests/sim/net.rs":"597f4d37bc26c3d82eeeaa6d14dd03bc2be3930686df2b293748b43c07c497d7","tests/sim/rng.rs":"4d5ef201e51b5ed5a0c63ad83cf514c9b117c9d6a07da94d91acc538edb56633","tests/sim/taildrop.rs":"638adda0a3f295550692a471d471a6d0673e1e61c96f5cf6f013a98f6641201c"},"package":null} \ No newline at end of file
+{"files":{"Cargo.toml":"8eac0a271cef9232d100eb45093a3f7978a93e0576c64c99c161092ff445825d","benches/range_tracker.rs":"4821443d3cccc697b8976b7c50d787a7aa8cb86ab8633a7582be3f85135168db","benches/rx_stream_orderer.rs":"a8db922390d8506c483a3a1f40ac9bf12806ebdb4f501716904776dd58e995be","benches/transfer.rs":"11343c1ac9131585c42236749d32d9e272a33b6acd58831fa3415be4d4f1cf86","src/ackrate.rs":"4bb882e1069a0707dc85338b75327e2910c93ee5f36575767a0d58c4c41c9d4f","src/addr_valid.rs":"03c0b2ff85254179c5d425b12acfdcc6b1ea5735aeb0f604b9b3603451b3ef0a","src/cc/classic_cc.rs":"4528bb4e9059524942ee7ef931de5de90c78ee13f76489185a964ad45c12c0b3","src/cc/cubic.rs":"24c6913cc6346e5361007221c26e8096ece51583431fc3ab9c99e4ce4b0a9f5d","src/cc/mod.rs":"e0837937c9991b37edad15cd870ea9e0623489babfccc340074dd8322e1ef401","src/cc/new_reno.rs":"25d0921005688e0f0666efd0a4931b4f8cd44363587d98e5b6404818c5d05dd4","src/cc/tests/cubic.rs":"109fc8be5efba8959e777288c32ae8f2db581fc08719f318ad676e187f655478","src/cc/tests/mod.rs":"44f8df551e742ae1037cd1cdb85b2c1334c2e5ab3c23ed63d856dbc6b8743afc","src/cc/tests/new_reno.rs":"5414e26b6c928c5f82c5eeb42f04772b05be1ec2c8ee21c2b698ce8cb32829a1","src/cid.rs":"9686a3070c593cfca846d7549863728e31211b304b9fa876220f79bff5e24173","src/connection/dump.rs":"c539caffdf5b4dfaf0173bb20d1974f5242b5432a0a32fc0b8ab56ee682cb1eb","src/connection/idle.rs":"b3bc2ad1290e54278d8703092d135eda973eb12316d1f6dffedaffdf25e2a47e","src/connection/mod.rs":"dcfba9574b707318292f460dc40f54f3cdf8fd883f5f0d604f1d0d466f99f481","src/connection/params.rs":"9731bc5faa584874c48538ed19839c7a310277df39144c580cdf3001153f5a56","src/connection/saved.rs":"97eb19792be3c4d721057021a43ea50a52f89a3cfa583d3d3dcf5d9144b332f5","src/connection/state.rs":"c1820864cc63073e1f44b875be1fcde9835df644e0fa8c2e05652421ad78b7b2","src/connection/test_internal.rs":"f3ebfe97b25c9c716d41406066295e5aff4e96a3051ef4e2b5fb258282bbc14c","src/connection/tests/ackrate.rs":"4a2b835575850ae4a14209d3e51883ecb1e69afb44ef91b5e13a5e6cb7174fab","src/connection/tests/cc.rs":"d0d6ac038572ad3dcd9e6734596eaeedc6d3222d24e31b023aaab3540d195e46","src/connection/tests/close.rs":"20bf9b044ba52517693c2bd10820ff04a8c07de01d59c8c47b4e9478aa730211","src/connection/tests/datagram.rs":"f4c85099b6a8739fb99eadd8711b02066ad80fc8341a2e5e0dae2520378af9fe","src/connection/tests/fuzzing.rs":"79d9ac83fe2d952a3a13140d603569c332d29dbba2e0d2b8ee5f6e42e8f4708a","src/connection/tests/handshake.rs":"eda7308fdd46570ee3b5569ad34e63761ccde89eb5ca854c877e3a53e7de5ec8","src/connection/tests/idle.rs":"f3bcb12cd79cb8eabc969ce3fb0fab4eea26d6383b81a323c0e18ca9c42cfb59","src/connection/tests/keys.rs":"55558c057beb4221245beb262076de3991dca3f2661411db61c09d21194873df","src/connection/tests/migration.rs":"624985d925236be197eee52208dbdebe35c0df5bd9d30982d6f183dfda4cbab5","src/connection/tests/mod.rs":"8b6709a5c89becf2daed407515f894ba3337e87b2d45b21acffa02e67f37eeec","src/connection/tests/priority.rs":"dd3504f52d3fce7a96441624bc1c82c733e6bb556b9b79d24d0f4fb4efaf5a9e","src/connection/tests/recovery.rs":"7f28767f3cca2ff60e3dcfa803e12ef043486a222f54681a8faf2ea2fee564a1","src/connection/tests/resumption.rs":"1a0de0993cd325224fc79a3c094d22636d5b122ab1123d16265d4fafb23574bd","src/connection/tests/stream.rs":"e5590c2b52d33fbe1b4e995edf1c016dda460ecfa2a9f021005e4abe8ea13580","src/connection/tests/vn.rs":"550eb6b4d39d5960aafc70037c25a1a0f5db1232ce0ec6080b2c29a731a9574e","src/connection/tests/zerortt.rs":"67f77721e33d9fa2691c5ea3ef4a90935987541d81f0f42fbcfca31e690b352a","src/crypto.rs":"c5780ab85ca84e830024c31346a416f1f470694372d732ee5e5b7c5df3adc202","src/events.rs":"6e115f309c5c46f30f6223e1347bea477ada457f8bb2189ecccc6d65483318d6","src/fc.rs":"ec9de1028286870c0adf88a92e1355acf13dede8b1e91179230df3263e3827a9","src/frame.rs":"eb35c4add314f0013ad7837157fa9daeb76a2286fc7f8c922993624f54a09569","src/lib.rs":"f8d83b370cab19b3d172d0689f8d76115f5fd26c742e394fca62e253809cedc4","src/pace.rs":"86a674ac4d086148ea297214910458c3705918bd627b996ba8bbb12f2c4bf99e","src/packet/mod.rs":"9fac8f4046ada084dbbcc6601391a2bf8bbc23a09d6fe7df3c135a36840dbee3","src/packet/retry.rs":"1f10bb2c39ae4335e65b8d5d97f2b6df62e04877740af27c7b965a65e7f7ca66","src/path.rs":"3eb7e5e3bc496bfefc729c1e15fba0f9f83572151a850bf13b9c931297789279","src/qlog.rs":"b94aa36d5bac2799d8635cf6b25b9bb9383944d5607ea85aff55715f70af5f7b","src/quic_datagrams.rs":"3d33ecb9e6e80c77b812e8260fc807352300fb2305a29b797259ae34c52b67c5","src/recovery.rs":"1dadc6717dd133007943e762231a50680087392466904c2f2e6fface084e2ba9","src/recv_stream.rs":"f21ae0bb786901bb7d726a94cb3352607b0057128beaa331808137f2f57a330b","src/rtt.rs":"4635dc0c401b78a1fd9d34da6f9bf7f6e7f5be3a57ed9716e0efc8f0f94f1e47","src/send_stream.rs":"f717f64b75e368cf5fa4ca43078aa7c1b5aff48b4f6266713e6fa7dc458328aa","src/sender.rs":"5f760988bdd6fbbd5956877a97abe7c17370dd531f68b751a9e4e4459583f87b","src/server.rs":"048aaac84e15d49fd25850294759107fe1855bbbc0481c16f8bd888d8f2a8f6d","src/stats.rs":"b2a4c03d5b24edeecd00d809019c56f1a22a4e35967309ae6e6053331aafcf30","src/stream_id.rs":"fd07cbb81709a54bdb0659f676ef851cd145c004b817044ede5b21e54fdb60e4","src/streams.rs":"062b1b61edd1a76a86914f2cc1ca007c03edd9136c0c3409d960ddb805805fc6","src/tparams.rs":"10d62ac64865e0606c3c14141f9674631c610b3f042e274e110bdcef5d388491","src/tracking.rs":"f9a9aa01abc79fdd7a2cfb2c3ae342b9ab709e6a2a11076ec5c475fc89c1f598","src/version.rs":"182484ed9ecc2e17cab73cc61914a86a2d206936cab313825ae76fd37eeade77","tests/common/mod.rs":"0aa6674ae4efd2f151a65737ed5eab9e700bd1b3da5b4c165cb24de2b01598ce","tests/conn_vectors.rs":"290550072bd0c37652b79ac119729064dd486452c3a740353a6669bcdb2b82cf","tests/connection.rs":"b3c2ce0c62c4b79f80e42289eadd51933931b0ae44c0adc20ce5141edd454e00","tests/network.rs":"9e30b8610124250262fceef27d09fdecf2d6e9c3a96b1e676ff4189b9e06d5ba","tests/retry.rs":"da5c6a6f9ec1a8f556073b2d2e11fbcd2f58463818b0f08f8d23158016fea0d5","tests/server.rs":"cb83de909d858950bfd75a789fc23c3c44fcdf1d965b63800b2c7b498507987f"},"package":null} \ No newline at end of file
diff --git a/third_party/rust/neqo-transport/Cargo.toml b/third_party/rust/neqo-transport/Cargo.toml
index f993a858b8..a309987434 100644
--- a/third_party/rust/neqo-transport/Cargo.toml
+++ b/third_party/rust/neqo-transport/Cargo.toml
@@ -10,28 +10,39 @@
# See Cargo.toml.orig for the original contents.
[package]
-edition = "2018"
-rust-version = "1.70.0"
+edition = "2021"
+rust-version = "1.74.0"
name = "neqo-transport"
-version = "0.7.0"
-authors = [
- "EKR <ekr@rtfm.com>",
- "Andy Grover <agrover@mozilla.com>",
-]
+version = "0.7.2"
+authors = ["The Neqo Authors <necko@mozilla.com>"]
+homepage = "https://github.com/mozilla/neqo/"
license = "MIT OR Apache-2.0"
+repository = "https://github.com/mozilla/neqo/"
+
+[lib]
+bench = false
+
+[[bench]]
+name = "transfer"
+harness = false
+required-features = ["bench"]
[[bench]]
name = "rx_stream_orderer"
harness = false
required-features = ["bench"]
-[dependencies]
-indexmap = "1.9.3"
-lazy_static = "1.4"
-smallvec = "1.11.1"
+[[bench]]
+name = "range_tracker"
+harness = false
+required-features = ["bench"]
+
+[dependencies.indexmap]
+version = "1.9"
+default-features = false
[dependencies.log]
-version = "0.4.17"
+version = "0.4"
default-features = false
[dependencies.neqo-common]
@@ -41,17 +52,29 @@ path = "../neqo-common"
path = "../neqo-crypto"
[dependencies.qlog]
-git = "https://github.com/cloudflare/quiche"
-rev = "09ea4b244096a013071cfe2175bbf2945fb7f8d1"
+version = "0.12"
+default-features = false
+
+[dependencies.smallvec]
+version = "1.11"
+default-features = false
-[dev-dependencies]
-criterion = "0.5.1"
-enum-map = "2.7"
+[dev-dependencies.criterion]
+version = "0.5"
+features = ["html_reports"]
+default-features = false
+
+[dev-dependencies.enum-map]
+version = "2.7"
+default-features = false
[dev-dependencies.test-fixture]
path = "../test-fixture"
[features]
bench = []
-deny-warnings = []
fuzzing = ["neqo-crypto/fuzzing"]
+
+[lints.clippy.pedantic]
+level = "warn"
+priority = -1
diff --git a/third_party/rust/neqo-transport/benches/range_tracker.rs b/third_party/rust/neqo-transport/benches/range_tracker.rs
new file mode 100644
index 0000000000..c2f78f4874
--- /dev/null
+++ b/third_party/rust/neqo-transport/benches/range_tracker.rs
@@ -0,0 +1,50 @@
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use criterion::{criterion_group, criterion_main, Criterion}; // black_box
+use neqo_transport::send_stream::RangeTracker;
+
+const CHUNK: u64 = 1000;
+const END: u64 = 100_000;
+fn build_coalesce(len: u64) -> RangeTracker {
+ let mut used = RangeTracker::default();
+ used.mark_acked(0, CHUNK as usize);
+ used.mark_sent(CHUNK, END as usize);
+ // leave a gap or it will coalesce here
+ for i in 2..=len {
+ // These do not get immediately coalesced when marking since they're not at the end or start
+ used.mark_acked(i * CHUNK, CHUNK as usize);
+ }
+ used
+}
+
+fn coalesce(c: &mut Criterion, count: u64) {
+ c.bench_function(
+ &format!("coalesce_acked_from_zero {count}+1 entries"),
+ |b| {
+ b.iter_batched_ref(
+ || build_coalesce(count),
+ |used| {
+ used.mark_acked(CHUNK, CHUNK as usize);
+ let tail = (count + 1) * CHUNK;
+ used.mark_sent(tail, CHUNK as usize);
+ used.mark_acked(tail, CHUNK as usize);
+ },
+ criterion::BatchSize::SmallInput,
+ )
+ },
+ );
+}
+
+fn benchmark_coalesce(c: &mut Criterion) {
+ coalesce(c, 1);
+ coalesce(c, 3);
+ coalesce(c, 10);
+ coalesce(c, 1000);
+}
+
+criterion_group!(benches, benchmark_coalesce);
+criterion_main!(benches);
diff --git a/third_party/rust/neqo-transport/benches/rx_stream_orderer.rs b/third_party/rust/neqo-transport/benches/rx_stream_orderer.rs
index 03b401ba06..0a1e763e97 100644
--- a/third_party/rust/neqo-transport/benches/rx_stream_orderer.rs
+++ b/third_party/rust/neqo-transport/benches/rx_stream_orderer.rs
@@ -1,3 +1,9 @@
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
use criterion::{criterion_group, criterion_main, Criterion};
use neqo_transport::recv_stream::RxStreamOrderer;
diff --git a/third_party/rust/neqo-transport/benches/transfer.rs b/third_party/rust/neqo-transport/benches/transfer.rs
new file mode 100644
index 0000000000..444f738f9c
--- /dev/null
+++ b/third_party/rust/neqo-transport/benches/transfer.rs
@@ -0,0 +1,70 @@
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::time::Duration;
+
+use criterion::{criterion_group, criterion_main, BatchSize::SmallInput, Criterion};
+use test_fixture::{
+ boxed,
+ sim::{
+ connection::{ConnectionNode, ReceiveData, SendData},
+ network::{Delay, TailDrop},
+ Simulator,
+ },
+};
+
+const ZERO: Duration = Duration::from_millis(0);
+const JITTER: Duration = Duration::from_millis(10);
+const TRANSFER_AMOUNT: usize = 1 << 22; // 4Mbyte
+
+fn benchmark_transfer(c: &mut Criterion, label: &str, seed: Option<impl AsRef<str>>) {
+ c.bench_function(label, |b| {
+ b.iter_batched(
+ || {
+ let nodes = boxed![
+ ConnectionNode::default_client(boxed![SendData::new(TRANSFER_AMOUNT)]),
+ TailDrop::dsl_uplink(),
+ Delay::new(ZERO..JITTER),
+ ConnectionNode::default_server(boxed![ReceiveData::new(TRANSFER_AMOUNT)]),
+ TailDrop::dsl_downlink(),
+ Delay::new(ZERO..JITTER),
+ ];
+ let mut sim = Simulator::new(label, nodes);
+ if let Some(seed) = &seed {
+ sim.seed_str(seed);
+ }
+ sim.setup()
+ },
+ |sim| {
+ sim.run();
+ },
+ SmallInput,
+ )
+ });
+}
+
+fn benchmark_transfer_variable(c: &mut Criterion) {
+ benchmark_transfer(
+ c,
+ "Run multiple transfers with varying seeds",
+ std::env::var("SIMULATION_SEED").ok(),
+ );
+}
+
+fn benchmark_transfer_fixed(c: &mut Criterion) {
+ benchmark_transfer(
+ c,
+ "Run multiple transfers with the same seed",
+ Some("62df6933ba1f543cece01db8f27fb2025529b27f93df39e19f006e1db3b8c843"),
+ );
+}
+
+criterion_group! {
+ name = transfer;
+ config = Criterion::default().warm_up_time(Duration::from_secs(5)).measurement_time(Duration::from_secs(15));
+ targets = benchmark_transfer_variable, benchmark_transfer_fixed
+}
+criterion_main!(transfer);
diff --git a/third_party/rust/neqo-transport/src/ackrate.rs b/third_party/rust/neqo-transport/src/ackrate.rs
index cf68f9021f..d5923805d9 100644
--- a/third_party/rust/neqo-transport/src/ackrate.rs
+++ b/third_party/rust/neqo-transport/src/ackrate.rs
@@ -5,9 +5,8 @@
// except according to those terms.
// Management of the peer's ack rate.
-#![deny(clippy::pedantic)]
-use std::{cmp::max, convert::TryFrom, time::Duration};
+use std::{cmp::max, time::Duration};
use neqo_common::qtrace;
diff --git a/third_party/rust/neqo-transport/src/addr_valid.rs b/third_party/rust/neqo-transport/src/addr_valid.rs
index b5ed2d07d1..f596cfc3cb 100644
--- a/third_party/rust/neqo-transport/src/addr_valid.rs
+++ b/third_party/rust/neqo-transport/src/addr_valid.rs
@@ -7,7 +7,6 @@
// This file implements functions necessary for address validation.
use std::{
- convert::TryFrom,
net::{IpAddr, SocketAddr},
time::{Duration, Instant},
};
@@ -23,15 +22,15 @@ use crate::{
cid::ConnectionId, packet::PacketBuilder, recovery::RecoveryToken, stats::FrameStats, Res,
};
-/// A prefix we add to Retry tokens to distinguish them from NEW_TOKEN tokens.
+/// A prefix we add to Retry tokens to distinguish them from `NEW_TOKEN` tokens.
const TOKEN_IDENTIFIER_RETRY: &[u8] = &[0x52, 0x65, 0x74, 0x72, 0x79];
-/// A prefix on NEW_TOKEN tokens, that is maximally Hamming distant from NEW_TOKEN.
+/// A prefix on `NEW_TOKEN` tokens, that is maximally Hamming distant from `NEW_TOKEN`.
/// Together, these need to have a low probability of collision, even if there is
/// corruption of individual bits in transit.
const TOKEN_IDENTIFIER_NEW_TOKEN: &[u8] = &[0xad, 0x9a, 0x8b, 0x8d, 0x86];
-/// The maximum number of tokens we'll save from NEW_TOKEN frames.
-/// This should be the same as the value of MAX_TICKETS in neqo-crypto.
+/// The maximum number of tokens we'll save from `NEW_TOKEN` frames.
+/// This should be the same as the value of `MAX_TICKETS` in neqo-crypto.
const MAX_NEW_TOKEN: usize = 4;
/// The number of tokens we'll track for the purposes of looking for duplicates.
/// This is based on how many might be received over a period where could be
@@ -44,9 +43,9 @@ const MAX_SAVED_TOKENS: usize = 8;
pub enum ValidateAddress {
/// Require address validation never.
Never,
- /// Require address validation unless a NEW_TOKEN token is provided.
+ /// Require address validation unless a `NEW_TOKEN` token is provided.
NoToken,
- /// Require address validation even if a NEW_TOKEN token is provided.
+ /// Require address validation even if a `NEW_TOKEN` token is provided.
Always,
}
@@ -143,7 +142,7 @@ impl AddressValidation {
self.generate_token(Some(dcid), peer_address, now)
}
- /// This generates a token for use with NEW_TOKEN.
+ /// This generates a token for use with `NEW_TOKEN`.
pub fn generate_new_token(&self, peer_address: SocketAddr, now: Instant) -> Res<Vec<u8>> {
self.generate_token(None, peer_address, now)
}
@@ -184,7 +183,7 @@ impl AddressValidation {
/// Less than one difference per byte indicates that it is likely not a Retry.
/// This generous interpretation allows for a lot of damage in transit.
/// Note that if this check fails, then the token will be treated like it came
- /// from NEW_TOKEN instead. If there truly is corruption of packets that causes
+ /// from `NEW_TOKEN` instead. If there truly is corruption of packets that causes
/// validation failure, it will be a failure that we try to recover from.
fn is_likely_retry(token: &[u8]) -> bool {
let mut difference = 0;
@@ -210,10 +209,9 @@ impl AddressValidation {
if self.validation == ValidateAddress::Never {
qinfo!("AddressValidation: no token; accepting");
return AddressValidationResult::Pass;
- } else {
- qinfo!("AddressValidation: no token; validating");
- return AddressValidationResult::Validate;
}
+ qinfo!("AddressValidation: no token; validating");
+ return AddressValidationResult::Validate;
}
if token.len() <= TOKEN_IDENTIFIER_RETRY.len() {
// Treat bad tokens strictly.
@@ -231,7 +229,7 @@ impl AddressValidation {
qinfo!("AddressValidation: valid Retry token for {}", cid);
AddressValidationResult::ValidRetry(cid)
} else {
- panic!("AddressValidation: Retry token with small CID {}", cid);
+ panic!("AddressValidation: Retry token with small CID {cid}");
}
} else if cid.is_empty() {
// An empty connection ID means NEW_TOKEN.
@@ -243,7 +241,7 @@ impl AddressValidation {
AddressValidationResult::Pass
}
} else {
- panic!("AddressValidation: NEW_TOKEN token with CID {}", cid);
+ panic!("AddressValidation: NEW_TOKEN token with CID {cid}");
}
} else {
// From here on, we have a token that we couldn't decrypt.
@@ -351,14 +349,13 @@ impl NewTokenState {
builder: &mut PacketBuilder,
tokens: &mut Vec<RecoveryToken>,
stats: &mut FrameStats,
- ) -> Res<()> {
+ ) {
if let Self::Server(ref mut sender) = self {
- sender.write_frames(builder, tokens, stats)?;
+ sender.write_frames(builder, tokens, stats);
}
- Ok(())
}
- /// If this a server, buffer a NEW_TOKEN for sending.
+ /// If this a server, buffer a `NEW_TOKEN` for sending.
/// If this is a client, panic.
pub fn send_new_token(&mut self, token: Vec<u8>) {
if let Self::Server(ref mut sender) = self {
@@ -368,7 +365,7 @@ impl NewTokenState {
}
}
- /// If this a server, process a lost signal for a NEW_TOKEN frame.
+ /// If this a server, process a lost signal for a `NEW_TOKEN` frame.
/// If this is a client, panic.
pub fn lost(&mut self, seqno: usize) {
if let Self::Server(ref mut sender) = self {
@@ -378,7 +375,7 @@ impl NewTokenState {
}
}
- /// If this a server, process remove the acknowledged NEW_TOKEN frame.
+ /// If this a server, process remove the acknowledged `NEW_TOKEN` frame.
/// If this is a client, panic.
pub fn acked(&mut self, seqno: usize) {
if let Self::Server(ref mut sender) = self {
@@ -403,7 +400,7 @@ impl NewTokenFrameStatus {
#[derive(Default)]
pub struct NewTokenSender {
- /// The unacknowledged NEW_TOKEN frames we are yet to send.
+ /// The unacknowledged `NEW_TOKEN` frames we are yet to send.
tokens: Vec<NewTokenFrameStatus>,
/// A sequence number that is used to track individual tokens
/// by reference (so that recovery tokens can be simple).
@@ -426,8 +423,8 @@ impl NewTokenSender {
builder: &mut PacketBuilder,
tokens: &mut Vec<RecoveryToken>,
stats: &mut FrameStats,
- ) -> Res<()> {
- for t in self.tokens.iter_mut() {
+ ) {
+ for t in &mut self.tokens {
if t.needs_sending && t.len() <= builder.remaining() {
t.needs_sending = false;
@@ -438,11 +435,10 @@ impl NewTokenSender {
stats.new_token += 1;
}
}
- Ok(())
}
pub fn lost(&mut self, seqno: usize) {
- for t in self.tokens.iter_mut() {
+ for t in &mut self.tokens {
if t.seqno == seqno {
t.needs_sending = true;
break;
diff --git a/third_party/rust/neqo-transport/src/cc/classic_cc.rs b/third_party/rust/neqo-transport/src/cc/classic_cc.rs
index 6f4a01d795..89be6c4b0f 100644
--- a/third_party/rust/neqo-transport/src/cc/classic_cc.rs
+++ b/third_party/rust/neqo-transport/src/cc/classic_cc.rs
@@ -5,7 +5,6 @@
// except according to those terms.
// Congestion control
-#![deny(clippy::pedantic)]
use std::{
cmp::{max, min},
@@ -536,10 +535,7 @@ impl<T: WindowAdjustment> ClassicCongestionControl<T> {
#[cfg(test)]
mod tests {
- use std::{
- convert::TryFrom,
- time::{Duration, Instant},
- };
+ use std::time::{Duration, Instant};
use neqo_common::qinfo;
use test_fixture::now;
diff --git a/third_party/rust/neqo-transport/src/cc/cubic.rs b/third_party/rust/neqo-transport/src/cc/cubic.rs
index c04a29b443..058a4c2aa4 100644
--- a/third_party/rust/neqo-transport/src/cc/cubic.rs
+++ b/third_party/rust/neqo-transport/src/cc/cubic.rs
@@ -4,10 +4,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-#![deny(clippy::pedantic)]
-
use std::{
- convert::TryFrom,
fmt::{self, Display},
time::{Duration, Instant},
};
diff --git a/third_party/rust/neqo-transport/src/cc/mod.rs b/third_party/rust/neqo-transport/src/cc/mod.rs
index a1a43bd157..486d15e67e 100644
--- a/third_party/rust/neqo-transport/src/cc/mod.rs
+++ b/third_party/rust/neqo-transport/src/cc/mod.rs
@@ -5,7 +5,6 @@
// except according to those terms.
// Congestion control
-#![deny(clippy::pedantic)]
use std::{
fmt::{Debug, Display},
diff --git a/third_party/rust/neqo-transport/src/cc/new_reno.rs b/third_party/rust/neqo-transport/src/cc/new_reno.rs
index e51b3d6cc0..47d0d56f37 100644
--- a/third_party/rust/neqo-transport/src/cc/new_reno.rs
+++ b/third_party/rust/neqo-transport/src/cc/new_reno.rs
@@ -5,7 +5,6 @@
// except according to those terms.
// Congestion control
-#![deny(clippy::pedantic)]
use std::{
fmt::{self, Display},
diff --git a/third_party/rust/neqo-transport/src/cc/tests/cubic.rs b/third_party/rust/neqo-transport/src/cc/tests/cubic.rs
index 0c82e47817..2e0200fd6d 100644
--- a/third_party/rust/neqo-transport/src/cc/tests/cubic.rs
+++ b/third_party/rust/neqo-transport/src/cc/tests/cubic.rs
@@ -8,7 +8,6 @@
#![allow(clippy::cast_sign_loss)]
use std::{
- convert::TryFrom,
ops::Sub,
time::{Duration, Instant},
};
diff --git a/third_party/rust/neqo-transport/src/cc/tests/mod.rs b/third_party/rust/neqo-transport/src/cc/tests/mod.rs
index 238a7ad012..879693fb24 100644
--- a/third_party/rust/neqo-transport/src/cc/tests/mod.rs
+++ b/third_party/rust/neqo-transport/src/cc/tests/mod.rs
@@ -1,3 +1,4 @@
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
diff --git a/third_party/rust/neqo-transport/src/cc/tests/new_reno.rs b/third_party/rust/neqo-transport/src/cc/tests/new_reno.rs
index a73844a755..4cc20de5a7 100644
--- a/third_party/rust/neqo-transport/src/cc/tests/new_reno.rs
+++ b/third_party/rust/neqo-transport/src/cc/tests/new_reno.rs
@@ -5,7 +5,6 @@
// except according to those terms.
// Congestion control
-#![deny(clippy::pedantic)]
use std::time::Duration;
diff --git a/third_party/rust/neqo-transport/src/cid.rs b/third_party/rust/neqo-transport/src/cid.rs
index be202daf25..6b3a95eaf0 100644
--- a/third_party/rust/neqo-transport/src/cid.rs
+++ b/third_party/rust/neqo-transport/src/cid.rs
@@ -10,14 +10,13 @@ use std::{
borrow::Borrow,
cell::{Ref, RefCell},
cmp::{max, min},
- convert::{AsRef, TryFrom},
ops::Deref,
rc::Rc,
};
use neqo_common::{hex, hex_with_len, qinfo, Decoder, Encoder};
-use neqo_crypto::random;
-use smallvec::SmallVec;
+use neqo_crypto::{random, randomize};
+use smallvec::{smallvec, SmallVec};
use crate::{
frame::FRAME_TYPE_NEW_CONNECTION_ID, packet::PacketBuilder, recovery::RecoveryToken,
@@ -39,19 +38,26 @@ pub struct ConnectionId {
}
impl ConnectionId {
+ /// # Panics
+ /// When `len` is larger than `MAX_CONNECTION_ID_LEN`.
+ #[must_use]
pub fn generate(len: usize) -> Self {
assert!(matches!(len, 0..=MAX_CONNECTION_ID_LEN));
- Self::from(random(len))
+ let mut cid = smallvec![0; len];
+ randomize(&mut cid);
+ Self { cid }
}
// Apply a wee bit of greasing here in picking a length between 8 and 20 bytes long.
+ #[must_use]
pub fn generate_initial() -> Self {
- let v = random(1);
+ let v = random::<1>()[0];
// Bias selection toward picking 8 (>50% of the time).
- let len: usize = max(8, 5 + (v[0] & (v[0] >> 4))).into();
+ let len: usize = max(8, 5 + (v & (v >> 4))).into();
Self::generate(len)
}
+ #[must_use]
pub fn as_cid_ref(&self) -> ConnectionIdRef {
ConnectionIdRef::from(&self.cid[..])
}
@@ -75,12 +81,6 @@ impl From<SmallVec<[u8; MAX_CONNECTION_ID_LEN]>> for ConnectionId {
}
}
-impl From<Vec<u8>> for ConnectionId {
- fn from(cid: Vec<u8>) -> Self {
- Self::from(SmallVec::from(cid))
- }
-}
-
impl<T: AsRef<[u8]> + ?Sized> From<&T> for ConnectionId {
fn from(buf: &T) -> Self {
Self::from(SmallVec::from(buf.as_ref()))
@@ -201,7 +201,7 @@ impl ConnectionIdGenerator for EmptyConnectionIdGenerator {
}
}
-/// An RandomConnectionIdGenerator produces connection IDs of
+/// An `RandomConnectionIdGenerator` produces connection IDs of
/// a fixed length and random content. No effort is made to
/// prevent collisions.
pub struct RandomConnectionIdGenerator {
@@ -209,6 +209,7 @@ pub struct RandomConnectionIdGenerator {
}
impl RandomConnectionIdGenerator {
+ #[must_use]
pub fn new(len: usize) -> Self {
Self { len }
}
@@ -222,7 +223,9 @@ impl ConnectionIdDecoder for RandomConnectionIdGenerator {
impl ConnectionIdGenerator for RandomConnectionIdGenerator {
fn generate_cid(&mut self) -> Option<ConnectionId> {
- Some(ConnectionId::from(&random(self.len)))
+ let mut buf = smallvec![0; self.len];
+ randomize(&mut buf);
+ Some(ConnectionId::from(buf))
}
fn as_decoder(&self) -> &dyn ConnectionIdDecoder {
@@ -234,7 +237,7 @@ impl ConnectionIdGenerator for RandomConnectionIdGenerator {
}
}
-/// A single connection ID, as saved from NEW_CONNECTION_ID.
+/// A single connection ID, as saved from `NEW_CONNECTION_ID`.
/// This is templated so that the connection ID entries from a peer can be
/// saved with a stateless reset token. Local entries don't need that.
#[derive(Debug, PartialEq, Eq, Clone)]
@@ -250,8 +253,8 @@ pub struct ConnectionIdEntry<SRT: Clone + PartialEq> {
impl ConnectionIdEntry<[u8; 16]> {
/// Create a random stateless reset token so that it is hard to guess the correct
/// value and reset the connection.
- fn random_srt() -> [u8; 16] {
- <[u8; 16]>::try_from(&random(16)[..]).unwrap()
+ pub fn random_srt() -> [u8; 16] {
+ random::<16>()
}
/// Create the first entry, which won't have a stateless reset token.
@@ -294,6 +297,23 @@ impl ConnectionIdEntry<[u8; 16]> {
pub fn sequence_number(&self) -> u64 {
self.seqno
}
+
+ /// Write the entry out in a `NEW_CONNECTION_ID` frame.
+ /// Returns `true` if the frame was written, `false` if there is insufficient space.
+ pub fn write(&self, builder: &mut PacketBuilder, stats: &mut FrameStats) -> bool {
+ let len = 1 + Encoder::varint_len(self.seqno) + 1 + 1 + self.cid.len() + 16;
+ if builder.remaining() < len {
+ return false;
+ }
+
+ builder.encode_varint(FRAME_TYPE_NEW_CONNECTION_ID);
+ builder.encode_varint(self.seqno);
+ builder.encode_varint(0u64);
+ builder.encode_vec(1, &self.cid);
+ builder.encode(&self.srt);
+ stats.new_connection_id += 1;
+ true
+ }
}
impl ConnectionIdEntry<()> {
@@ -430,7 +450,7 @@ pub struct ConnectionIdManager {
limit: usize,
/// The next sequence number that will be used for sending `NEW_CONNECTION_ID` frames.
next_seqno: u64,
- /// Outstanding, but lost NEW_CONNECTION_ID frames will be stored here.
+ /// Outstanding, but lost `NEW_CONNECTION_ID` frames will be stored here.
lost_new_connection_id: Vec<ConnectionIdEntry<[u8; 16]>>,
}
@@ -476,7 +496,7 @@ impl ConnectionIdManager {
.add_local(ConnectionIdEntry::new(self.next_seqno, cid.clone(), ()));
self.next_seqno += 1;
- let srt = <[u8; 16]>::try_from(&random(16)[..]).unwrap();
+ let srt = ConnectionIdEntry::random_srt();
Ok((cid, srt))
} else {
Err(Error::ConnectionIdsExhausted)
@@ -516,39 +536,19 @@ impl ConnectionIdManager {
);
}
- fn write_entry(
- &mut self,
- entry: &ConnectionIdEntry<[u8; 16]>,
- builder: &mut PacketBuilder,
- stats: &mut FrameStats,
- ) -> Res<bool> {
- let len = 1 + Encoder::varint_len(entry.seqno) + 1 + 1 + entry.cid.len() + 16;
- if builder.remaining() < len {
- return Ok(false);
- }
-
- builder.encode_varint(FRAME_TYPE_NEW_CONNECTION_ID);
- builder.encode_varint(entry.seqno);
- builder.encode_varint(0u64);
- builder.encode_vec(1, &entry.cid);
- builder.encode(&entry.srt);
- stats.new_connection_id += 1;
- Ok(true)
- }
-
pub fn write_frames(
&mut self,
builder: &mut PacketBuilder,
tokens: &mut Vec<RecoveryToken>,
stats: &mut FrameStats,
- ) -> Res<()> {
+ ) {
if self.generator.deref().borrow().generates_empty_cids() {
debug_assert_eq!(self.generator.borrow_mut().generate_cid().unwrap().len(), 0);
- return Ok(());
+ return;
}
while let Some(entry) = self.lost_new_connection_id.pop() {
- if self.write_entry(&entry, builder, stats)? {
+ if entry.write(builder, stats) {
tokens.push(RecoveryToken::NewConnectionId(entry));
} else {
// This shouldn't happen often.
@@ -565,7 +565,7 @@ impl ConnectionIdManager {
if let Some(cid) = maybe_cid {
assert_ne!(cid.len(), 0);
// TODO: generate the stateless reset tokens from the connection ID and a key.
- let srt = <[u8; 16]>::try_from(&random(16)[..]).unwrap();
+ let srt = ConnectionIdEntry::random_srt();
let seqno = self.next_seqno;
self.next_seqno += 1;
@@ -573,11 +573,10 @@ impl ConnectionIdManager {
.add_local(ConnectionIdEntry::new(seqno, cid.clone(), ()));
let entry = ConnectionIdEntry::new(seqno, cid, srt);
- debug_assert!(self.write_entry(&entry, builder, stats)?);
+ entry.write(builder, stats);
tokens.push(RecoveryToken::NewConnectionId(entry));
}
}
- Ok(())
}
pub fn lost(&mut self, entry: &ConnectionIdEntry<[u8; 16]>) {
@@ -594,16 +593,17 @@ impl ConnectionIdManager {
mod tests {
use test_fixture::fixture_init;
- use super::*;
+ use crate::{cid::MAX_CONNECTION_ID_LEN, ConnectionId};
#[test]
fn generate_initial_cid() {
fixture_init();
for _ in 0..100 {
let cid = ConnectionId::generate_initial();
- if !matches!(cid.len(), 8..=MAX_CONNECTION_ID_LEN) {
- panic!("connection ID {:?}", cid);
- }
+ assert!(
+ matches!(cid.len(), 8..=MAX_CONNECTION_ID_LEN),
+ "connection ID length {cid:?}",
+ );
}
}
}
diff --git a/third_party/rust/neqo-transport/src/connection/dump.rs b/third_party/rust/neqo-transport/src/connection/dump.rs
index 77d51c605c..8a4f34dbb8 100644
--- a/third_party/rust/neqo-transport/src/connection/dump.rs
+++ b/third_party/rust/neqo-transport/src/connection/dump.rs
@@ -27,11 +27,11 @@ pub fn dump_packet(
pn: PacketNumber,
payload: &[u8],
) {
- if !log::log_enabled!(log::Level::Debug) {
+ if log::STATIC_MAX_LEVEL == log::LevelFilter::Off || !log::log_enabled!(log::Level::Debug) {
return;
}
- let mut s = String::from("");
+ let mut s = String::new();
let mut d = Decoder::from(payload);
while d.remaining() > 0 {
let Ok(f) = Frame::decode(&mut d) else {
diff --git a/third_party/rust/neqo-transport/src/connection/mod.rs b/third_party/rust/neqo-transport/src/connection/mod.rs
index 2de388418a..c81a3727c6 100644
--- a/third_party/rust/neqo-transport/src/connection/mod.rs
+++ b/third_party/rust/neqo-transport/src/connection/mod.rs
@@ -9,7 +9,6 @@
use std::{
cell::RefCell,
cmp::{max, min},
- convert::TryFrom,
fmt::{self, Debug},
mem,
net::{IpAddr, SocketAddr},
@@ -23,7 +22,7 @@ use neqo_common::{
qlog::NeqoQlog, qtrace, qwarn, Datagram, Decoder, Encoder, Role,
};
use neqo_crypto::{
- agent::CertificateInfo, random, Agent, AntiReplay, AuthenticationStatus, Cipher, Client, Group,
+ agent::CertificateInfo, Agent, AntiReplay, AuthenticationStatus, Cipher, Client, Group,
HandshakeState, PrivateKey, PublicKey, ResumptionToken, SecretAgentInfo, SecretAgentPreInfo,
Server, ZeroRttChecker,
};
@@ -48,6 +47,7 @@ use crate::{
recovery::{LossRecovery, RecoveryToken, SendProfile},
recv_stream::RecvStreamStats,
rtt::GRANULARITY,
+ send_stream::SendStream,
stats::{Stats, StatsCell},
stream_id::StreamType,
streams::{SendOrder, Streams},
@@ -59,6 +59,7 @@ use crate::{
version::{Version, WireVersion},
AppError, ConnectionError, Error, Res, StreamId,
};
+
mod dump;
mod idle;
pub mod params;
@@ -66,6 +67,7 @@ mod saved;
mod state;
#[cfg(test)]
pub mod test_internal;
+
use dump::dump_packet;
use idle::IdleTimeout;
pub use params::ConnectionParameters;
@@ -78,9 +80,6 @@ pub use state::{ClosingFrame, State};
pub use crate::send_stream::{RetransmissionPriority, SendStreamStats, TransmissionPriority};
-#[derive(Debug, Default)]
-struct Packet(Vec<u8>);
-
/// The number of Initial packets that the client will send in response
/// to receiving an undecryptable packet during the early part of the
/// handshake. This is a hack, but a useful one.
@@ -96,7 +95,7 @@ pub enum ZeroRttState {
}
#[derive(Clone, Debug, PartialEq, Eq)]
-/// Type returned from process() and `process_output()`. Users are required to
+/// Type returned from `process()` and `process_output()`. Users are required to
/// call these repeatedly until `Callback` or `None` is returned.
pub enum Output {
/// Connection requires no action.
@@ -119,6 +118,7 @@ impl Output {
}
/// Get a reference to the Datagram, if any.
+ #[must_use]
pub fn as_dgram_ref(&self) -> Option<&Datagram> {
match self {
Self::Datagram(dg) => Some(dg),
@@ -136,7 +136,7 @@ impl Output {
}
}
-/// Used by inner functions like Connection::output.
+/// Used by inner functions like `Connection::output`.
enum SendOption {
/// Yes, please send this datagram.
Yes(Datagram),
@@ -257,7 +257,7 @@ pub struct Connection {
/// Some packets were received, but not tracked.
received_untracked: bool,
- /// This is responsible for the QuicDatagrams' handling:
+ /// This is responsible for the `QuicDatagrams`' handling:
/// <https://datatracker.ietf.org/doc/html/draft-ietf-quic-datagram>
quic_datagrams: QuicDatagrams,
@@ -271,8 +271,8 @@ pub struct Connection {
new_token: NewTokenState,
stats: StatsCell,
qlog: NeqoQlog,
- /// A session ticket was received without NEW_TOKEN,
- /// this is when that turns into an event without NEW_TOKEN.
+ /// A session ticket was received without `NEW_TOKEN`,
+ /// this is when that turns into an event without `NEW_TOKEN`.
release_resumption_token_timer: Option<Instant>,
conn_params: ConnectionParameters,
hrtime: hrtime::Handle,
@@ -302,6 +302,8 @@ impl Connection {
const LOOSE_TIMER_RESOLUTION: Duration = Duration::from_millis(50);
/// Create a new QUIC connection with Client role.
+ /// # Errors
+ /// When NSS fails and an agent cannot be created.
pub fn new_client(
server_name: impl Into<String>,
protocols: &[impl AsRef<str>],
@@ -338,6 +340,8 @@ impl Connection {
}
/// Create a new QUIC connection with Server role.
+ /// # Errors
+ /// When NSS fails and an agent cannot be created.
pub fn new_server(
certs: &[impl AsRef<str>],
protocols: &[impl AsRef<str>],
@@ -427,6 +431,8 @@ impl Connection {
Ok(c)
}
+ /// # Errors
+ /// When the operation fails.
pub fn server_enable_0rtt(
&mut self,
anti_replay: &AntiReplay,
@@ -436,6 +442,8 @@ impl Connection {
.server_enable_0rtt(self.tps.clone(), anti_replay, zero_rtt_checker)
}
+ /// # Errors
+ /// When the operation fails.
pub fn server_enable_ech(
&mut self,
config: u8,
@@ -447,10 +455,13 @@ impl Connection {
}
/// Get the active ECH configuration, which is empty if ECH is disabled.
+ #[must_use]
pub fn ech_config(&self) -> &[u8] {
self.crypto.ech_config()
}
+ /// # Errors
+ /// When the operation fails.
pub fn client_enable_ech(&mut self, ech_config_list: impl AsRef<[u8]>) -> Res<()> {
self.crypto.client_enable_ech(ech_config_list)
}
@@ -468,8 +479,9 @@ impl Connection {
}
/// Get the original destination connection id for this connection. This
- /// will always be present for Role::Client but not if Role::Server is in
- /// State::Init.
+ /// will always be present for `Role::Client` but not if `Role::Server` is in
+ /// `State::Init`.
+ #[must_use]
pub fn odcid(&self) -> Option<&ConnectionId> {
self.original_destination_cid.as_ref()
}
@@ -478,8 +490,9 @@ impl Connection {
/// This only sets transport parameters without dealing with other aspects of
/// setting the value.
///
+ /// # Errors
+ /// When the transport parameter is invalid.
/// # Panics
- ///
/// This panics if the transport parameter is known to this crate.
pub fn set_local_tparam(&self, tp: TransportParameterId, value: TransportParameter) -> Res<()> {
#[cfg(not(test))]
@@ -502,9 +515,9 @@ impl Connection {
/// Retry.
pub(crate) fn set_retry_cids(
&mut self,
- odcid: ConnectionId,
+ odcid: &ConnectionId,
remote_cid: ConnectionId,
- retry_cid: ConnectionId,
+ retry_cid: &ConnectionId,
) {
debug_assert_eq!(self.role, Role::Server);
qtrace!(
@@ -533,12 +546,16 @@ impl Connection {
/// Set ALPN preferences. Strings that appear earlier in the list are given
/// higher preference.
+ /// # Errors
+ /// When the operation fails, which is usually due to bad inputs or bad connection state.
pub fn set_alpn(&mut self, protocols: &[impl AsRef<str>]) -> Res<()> {
self.crypto.tls.set_alpn(protocols)?;
Ok(())
}
/// Enable a set of ciphers.
+ /// # Errors
+ /// When the operation fails, which is usually due to bad inputs or bad connection state.
pub fn set_ciphers(&mut self, ciphers: &[Cipher]) -> Res<()> {
if self.state != State::Init {
qerror!([self], "Cannot enable ciphers in state {:?}", self.state);
@@ -549,6 +566,8 @@ impl Connection {
}
/// Enable a set of key exchange groups.
+ /// # Errors
+ /// When the operation fails, which is usually due to bad inputs or bad connection state.
pub fn set_groups(&mut self, groups: &[Group]) -> Res<()> {
if self.state != State::Init {
qerror!([self], "Cannot enable groups in state {:?}", self.state);
@@ -559,6 +578,8 @@ impl Connection {
}
/// Set the number of additional key shares to send in the client hello.
+ /// # Errors
+ /// When the operation fails, which is usually due to bad inputs or bad connection state.
pub fn send_additional_key_shares(&mut self, count: usize) -> Res<()> {
if self.state != State::Init {
qerror!([self], "Cannot enable groups in state {:?}", self.state);
@@ -667,6 +688,8 @@ impl Connection {
/// This can only be called once and only on the client.
/// After calling the function, it should be possible to attempt 0-RTT
/// if the token supports that.
+ /// # Errors
+ /// When the operation fails, which is usually due to bad inputs or bad connection state.
pub fn enable_resumption(&mut self, now: Instant, token: impl AsRef<[u8]>) -> Res<()> {
if self.state != State::Init {
qerror!([self], "set token in state {:?}", self.state);
@@ -683,8 +706,9 @@ impl Connection {
);
let mut dec = Decoder::from(token.as_ref());
- let version =
- Version::try_from(dec.decode_uint(4).ok_or(Error::InvalidResumptionToken)? as u32)?;
+ let version = Version::try_from(u32::try_from(
+ dec.decode_uint(4).ok_or(Error::InvalidResumptionToken)?,
+ )?)?;
qtrace!([self], " version {:?}", version);
if !self.conn_params.get_versions().all().contains(&version) {
return Err(Error::DisabledVersion);
@@ -732,13 +756,15 @@ impl Connection {
Ok(())
}
- pub(crate) fn set_validation(&mut self, validation: Rc<RefCell<AddressValidation>>) {
+ pub(crate) fn set_validation(&mut self, validation: &Rc<RefCell<AddressValidation>>) {
qtrace!([self], "Enabling NEW_TOKEN");
assert_eq!(self.role, Role::Server);
- self.address_validation = AddressValidationInfo::Server(Rc::downgrade(&validation));
+ self.address_validation = AddressValidationInfo::Server(Rc::downgrade(validation));
}
- /// Send a TLS session ticket AND a NEW_TOKEN frame (if possible).
+ /// Send a TLS session ticket AND a `NEW_TOKEN` frame (if possible).
+ /// # Errors
+ /// When the operation fails, which is usually due to bad inputs or bad connection state.
pub fn send_ticket(&mut self, now: Instant, extra: &[u8]) -> Res<()> {
if self.role == Role::Client {
return Err(Error::WrongRole);
@@ -774,15 +800,19 @@ impl Connection {
}
}
+ #[must_use]
pub fn tls_info(&self) -> Option<&SecretAgentInfo> {
self.crypto.tls.info()
}
+ /// # Errors
+ /// When there is no information to obtain.
pub fn tls_preinfo(&self) -> Res<SecretAgentPreInfo> {
Ok(self.crypto.tls.preinfo()?)
}
/// Get the peer's certificate chain and other info.
+ #[must_use]
pub fn peer_certificate(&self) -> Option<CertificateInfo> {
self.crypto.tls.peer_certificate()
}
@@ -802,26 +832,31 @@ impl Connection {
}
/// Get the role of the connection.
+ #[must_use]
pub fn role(&self) -> Role {
self.role
}
/// Get the state of the connection.
+ #[must_use]
pub fn state(&self) -> &State {
&self.state
}
/// The QUIC version in use.
+ #[must_use]
pub fn version(&self) -> Version {
self.version
}
/// Get the 0-RTT state of the connection.
+ #[must_use]
pub fn zero_rtt_state(&self) -> ZeroRttState {
self.zero_rtt_state
}
/// Get a snapshot of collected statistics.
+ #[must_use]
pub fn stats(&self) -> Stats {
let mut v = self.stats.borrow().clone();
if let Some(p) = self.paths.primary_fallible() {
@@ -888,7 +923,7 @@ impl Connection {
res
}
- /// For use with process_input(). Errors there can be ignored, but this
+ /// For use with `process_input()`. Errors there can be ignored, but this
/// needs to ensure that the state is updated.
fn absorb_error<T>(&mut self, now: Instant, res: Res<T>) -> Option<T> {
self.capture_error(None, now, 0, res).ok()
@@ -1234,6 +1269,7 @@ impl Connection {
/// Perform any processing that we might have to do on packets prior to
/// attempting to remove protection.
+ #[allow(clippy::too_many_lines)] // Yeah, it's a work in progress.
fn preprocess_packet(
&mut self,
packet: &PublicPacket,
@@ -1346,17 +1382,17 @@ impl Connection {
}
State::WaitInitial => PreprocessResult::Continue,
State::WaitVersion | State::Handshaking | State::Connected | State::Confirmed => {
- if !self.cid_manager.is_valid(packet.dcid()) {
- self.stats
- .borrow_mut()
- .pkt_dropped(format!("Invalid DCID {:?}", packet.dcid()));
- PreprocessResult::Next
- } else {
+ if self.cid_manager.is_valid(packet.dcid()) {
if self.role == Role::Server && packet.packet_type() == PacketType::Handshake {
// Server has received a Handshake packet -> discard Initial keys and states
self.discard_keys(PacketNumberSpace::Initial, now);
}
PreprocessResult::Continue
+ } else {
+ self.stats
+ .borrow_mut()
+ .pkt_dropped(format!("Invalid DCID {:?}", packet.dcid()));
+ PreprocessResult::Next
}
}
State::Closing { .. } => {
@@ -1376,7 +1412,7 @@ impl Connection {
Ok(res)
}
- /// After a Initial, Handshake, ZeroRtt, or Short packet is successfully processed.
+ /// After a Initial, Handshake, `ZeroRtt`, or Short packet is successfully processed.
fn postprocess_packet(
&mut self,
path: &PathRef,
@@ -1576,7 +1612,6 @@ impl Connection {
/// During connection setup, the first path needs to be setup.
/// This uses the connection IDs that were provided during the handshake
/// to setup that path.
- #[allow(clippy::or_fun_call)] // Remove when MSRV >= 1.59
fn setup_handshake_path(&mut self, path: &PathRef, now: Instant) {
self.paths.make_permanent(
path,
@@ -1616,7 +1651,7 @@ impl Connection {
}
}
- /// After an error, a permanent path is needed to send the CONNECTION_CLOSE.
+ /// After an error, a permanent path is needed to send the `CONNECTION_CLOSE`.
/// This attempts to ensure that this exists. As the connection is now
/// temporary, there is no reason to do anything special here.
fn ensure_error_path(&mut self, path: &PathRef, packet: &PublicPacket, now: Instant) {
@@ -1815,7 +1850,7 @@ impl Connection {
State::Closing { .. } | State::Draining { .. } | State::Closed(_) => {
if let Some(details) = self.state_signaling.close_frame() {
let path = Rc::clone(details.path());
- let res = self.output_close(details);
+ let res = self.output_close(&details);
self.capture_error(Some(path), now, 0, res)
} else {
Ok(SendOption::default())
@@ -1892,7 +1927,7 @@ impl Connection {
}
}
- fn output_close(&mut self, close: ClosingFrame) -> Res<SendOption> {
+ fn output_close(&mut self, close: &ClosingFrame) -> Res<SendOption> {
let mut encoder = Encoder::with_capacity(256);
let grease_quic_bit = self.can_grease_quic_bit();
let version = self.version();
@@ -1902,6 +1937,14 @@ impl Connection {
};
let path = close.path().borrow();
+ // In some error cases, we will not be able to make a new, permanent path.
+ // For example, if we run out of connection IDs and the error results from
+ // a packet on a new path, we avoid sending (and the privacy risk) rather
+ // than reuse a connection ID.
+ if path.is_temporary() {
+ assert!(!cfg!(test), "attempting to close with a temporary path");
+ return Err(Error::InternalError);
+ }
let (_, mut builder) = Self::build_packet_header(
&path,
cspace,
@@ -1932,7 +1975,7 @@ impl Connection {
};
sanitized
.as_ref()
- .unwrap_or(&close)
+ .unwrap_or(close)
.write_frame(&mut builder);
encoder = builder.build(tx)?;
}
@@ -1946,11 +1989,11 @@ impl Connection {
&mut self,
builder: &mut PacketBuilder,
tokens: &mut Vec<RecoveryToken>,
- ) -> Res<()> {
+ ) {
let stats = &mut self.stats.borrow_mut();
let frame_stats = &mut stats.frame_tx;
if self.role == Role::Server {
- if let Some(t) = self.state_signaling.write_done(builder)? {
+ if let Some(t) = self.state_signaling.write_done(builder) {
tokens.push(t);
frame_stats.handshake_done += 1;
}
@@ -1959,7 +2002,7 @@ impl Connection {
self.streams
.write_frames(TransmissionPriority::Critical, builder, tokens, frame_stats);
if builder.is_full() {
- return Ok(());
+ return;
}
self.streams.write_frames(
@@ -1969,36 +2012,35 @@ impl Connection {
frame_stats,
);
if builder.is_full() {
- return Ok(());
+ return;
}
// NEW_CONNECTION_ID, RETIRE_CONNECTION_ID, and ACK_FREQUENCY.
- self.cid_manager
- .write_frames(builder, tokens, frame_stats)?;
+ self.cid_manager.write_frames(builder, tokens, frame_stats);
if builder.is_full() {
- return Ok(());
+ return;
}
self.paths.write_frames(builder, tokens, frame_stats);
if builder.is_full() {
- return Ok(());
+ return;
}
self.streams
.write_frames(TransmissionPriority::High, builder, tokens, frame_stats);
if builder.is_full() {
- return Ok(());
+ return;
}
self.streams
.write_frames(TransmissionPriority::Normal, builder, tokens, frame_stats);
if builder.is_full() {
- return Ok(());
+ return;
}
// Datagrams are best-effort and unreliable. Let streams starve them for now.
self.quic_datagrams.write_frames(builder, tokens, stats);
if builder.is_full() {
- return Ok(());
+ return;
}
let frame_stats = &mut stats.frame_tx;
@@ -2009,13 +2051,13 @@ impl Connection {
builder,
tokens,
frame_stats,
- )?;
+ );
if builder.is_full() {
- return Ok(());
+ return;
}
- self.new_token.write_frames(builder, tokens, frame_stats)?;
+ self.new_token.write_frames(builder, tokens, frame_stats);
if builder.is_full() {
- return Ok(());
+ return;
}
self.streams
@@ -2027,8 +2069,6 @@ impl Connection {
w.write_frames(builder);
}
}
-
- Ok(())
}
// Maybe send a probe. Return true if the packet was ack-eliciting.
@@ -2089,7 +2129,7 @@ impl Connection {
profile: &SendProfile,
builder: &mut PacketBuilder,
now: Instant,
- ) -> Res<(Vec<RecoveryToken>, bool, bool)> {
+ ) -> (Vec<RecoveryToken>, bool, bool) {
let mut tokens = Vec::new();
let primary = path.borrow().is_primary();
let mut ack_eliciting = false;
@@ -2125,16 +2165,15 @@ impl Connection {
if profile.ack_only(space) {
// If we are CC limited we can only send acks!
- return Ok((tokens, false, false));
+ return (tokens, false, false);
}
if primary {
if space == PacketNumberSpace::ApplicationData {
- self.write_appdata_frames(builder, &mut tokens)?;
+ self.write_appdata_frames(builder, &mut tokens);
} else {
let stats = &mut self.stats.borrow_mut().frame_tx;
- self.crypto
- .write_frame(space, builder, &mut tokens, stats)?;
+ self.crypto.write_frame(space, builder, &mut tokens, stats);
}
}
@@ -2158,11 +2197,12 @@ impl Connection {
};
stats.all += tokens.len();
- Ok((tokens, ack_eliciting, padded))
+ (tokens, ack_eliciting, padded)
}
/// Build a datagram, possibly from multiple packets (for different PN
/// spaces) and each containing 1+ frames.
+ #[allow(clippy::too_many_lines)] // Yeah, that's just the way it is.
fn output_path(&mut self, path: &PathRef, now: Instant) -> Res<SendOption> {
let mut initial_sent = None;
let mut needs_padding = false;
@@ -2217,7 +2257,7 @@ impl Connection {
// Add frames to the packet.
let payload_start = builder.len();
let (tokens, ack_eliciting, padded) =
- self.write_frames(path, *space, &profile, &mut builder, now)?;
+ self.write_frames(path, *space, &profile, &mut builder, now);
if builder.packet_empty() {
// Nothing to include in this packet.
encoder = builder.abort();
@@ -2306,6 +2346,8 @@ impl Connection {
}
}
+ /// # Errors
+ /// When connection state is not valid.
pub fn initiate_key_update(&mut self) -> Res<()> {
if self.state == State::Confirmed {
let la = self
@@ -2319,6 +2361,7 @@ impl Connection {
}
#[cfg(test)]
+ #[must_use]
pub fn get_epochs(&self) -> (Option<usize>, Option<usize>) {
self.crypto.states.get_epochs()
}
@@ -2377,6 +2420,7 @@ impl Connection {
);
}
+ #[must_use]
pub fn is_stream_id_allowed(&self, stream_id: StreamId) -> bool {
self.streams.is_stream_id_allowed(stream_id)
}
@@ -2404,7 +2448,7 @@ impl Connection {
} else {
// The other side didn't provide a stateless reset token.
// That's OK, they can try guessing this.
- <[u8; 16]>::try_from(&random(16)[..]).unwrap()
+ ConnectionIdEntry::random_srt()
};
self.paths
.primary()
@@ -2585,10 +2629,16 @@ impl Connection {
) -> Res<()> {
qtrace!([self], "Handshake space={} data={:0x?}", space, data);
+ let was_authentication_pending =
+ *self.crypto.tls.state() == HandshakeState::AuthenticationPending;
let try_update = data.is_some();
match self.crypto.handshake(now, space, data)? {
HandshakeState::Authenticated(_) | HandshakeState::InProgress => (),
- HandshakeState::AuthenticationPending => self.events.authentication_needed(),
+ HandshakeState::AuthenticationPending => {
+ if !was_authentication_pending {
+ self.events.authentication_needed();
+ }
+ }
HandshakeState::EchFallbackAuthenticationPending(public_name) => self
.events
.ech_fallback_authentication_needed(public_name.clone()),
@@ -2623,6 +2673,7 @@ impl Connection {
Ok(())
}
+ #[allow(clippy::too_many_lines)] // Yep, but it's a nice big match, which is basically lots of little functions.
fn input_frame(
&mut self,
path: &PathRef,
@@ -2640,7 +2691,7 @@ impl Connection {
if frame.is_stream() {
return self
.streams
- .input_frame(frame, &mut self.stats.borrow_mut().frame_rx);
+ .input_frame(&frame, &mut self.stats.borrow_mut().frame_rx);
}
match frame {
Frame::Padding => {
@@ -3005,11 +3056,10 @@ impl Connection {
Ok(())
}
- /// Set the SendOrder of a stream. Re-enqueues to keep the ordering correct
+ /// Set the `SendOrder` of a stream. Re-enqueues to keep the ordering correct
///
/// # Errors
- ///
- /// Returns InvalidStreamId if the stream id doesn't exist
+ /// When the stream does not exist.
pub fn stream_sendorder(
&mut self,
stream_id: StreamId,
@@ -3021,16 +3071,21 @@ impl Connection {
/// Set the Fairness of a stream
///
/// # Errors
- ///
- /// Returns InvalidStreamId if the stream id doesn't exist
+ /// When the stream does not exist.
pub fn stream_fairness(&mut self, stream_id: StreamId, fairness: bool) -> Res<()> {
self.streams.set_fairness(stream_id, fairness)
}
+ /// # Errors
+ /// When the stream does not exist.
pub fn send_stream_stats(&self, stream_id: StreamId) -> Res<SendStreamStats> {
- self.streams.get_send_stream(stream_id).map(|s| s.stats())
+ self.streams
+ .get_send_stream(stream_id)
+ .map(SendStream::stats)
}
+ /// # Errors
+ /// When the stream does not exist.
pub fn recv_stream_stats(&mut self, stream_id: StreamId) -> Res<RecvStreamStats> {
let stream = self.streams.get_recv_stream_mut(stream_id)?;
@@ -3050,8 +3105,8 @@ impl Connection {
self.streams.get_send_stream_mut(stream_id)?.send(data)
}
- /// Send all data or nothing on a stream. May cause DATA_BLOCKED or
- /// STREAM_DATA_BLOCKED frames to be sent.
+ /// Send all data or nothing on a stream. May cause `DATA_BLOCKED` or
+ /// `STREAM_DATA_BLOCKED` frames to be sent.
/// Returns true if data was successfully sent, otherwise false.
///
/// # Errors
@@ -3075,20 +3130,26 @@ impl Connection {
val.map(|v| v == data.len())
}
- /// Bytes that stream_send() is guaranteed to accept for sending.
+ /// Bytes that `stream_send()` is guaranteed to accept for sending.
/// i.e. that will not be blocked by flow credits or send buffer max
/// capacity.
+ /// # Errors
+ /// When the stream ID is invalid.
pub fn stream_avail_send_space(&self, stream_id: StreamId) -> Res<usize> {
Ok(self.streams.get_send_stream(stream_id)?.avail())
}
/// Close the stream. Enqueued data will be sent.
+ /// # Errors
+ /// When the stream ID is invalid.
pub fn stream_close_send(&mut self, stream_id: StreamId) -> Res<()> {
self.streams.get_send_stream_mut(stream_id)?.close();
Ok(())
}
/// Abandon transmission of in-flight and future stream data.
+ /// # Errors
+ /// When the stream ID is invalid.
pub fn stream_reset_send(&mut self, stream_id: StreamId, err: AppError) -> Res<()> {
self.streams.get_send_stream_mut(stream_id)?.reset(err);
Ok(())
@@ -3109,6 +3170,8 @@ impl Connection {
}
/// Application is no longer interested in this stream.
+ /// # Errors
+ /// When the stream ID is invalid.
pub fn stream_stop_sending(&mut self, stream_id: StreamId, err: AppError) -> Res<()> {
let stream = self.streams.get_recv_stream_mut(stream_id)?;
@@ -3142,6 +3205,7 @@ impl Connection {
self.streams.keep_alive(stream_id, keep)
}
+ #[must_use]
pub fn remote_datagram_size(&self) -> u64 {
self.quic_datagrams.remote_datagram_size()
}
@@ -3150,9 +3214,10 @@ impl Connection {
/// The value will change over time depending on the encoded size of the
/// packet number, ack frames, etc.
///
- /// # Error
- ///
+ /// # Errors
/// The function returns `NotAvailable` if datagrams are not enabled.
+ /// # Panics
+ /// Basically never, because that unwrap won't fail.
pub fn max_datagram_size(&self) -> Res<u64> {
let max_dgram_size = self.quic_datagrams.remote_datagram_size();
if max_dgram_size == 0 {
@@ -3193,7 +3258,7 @@ impl Connection {
/// Queue a datagram for sending.
///
- /// # Error
+ /// # Errors
///
/// The function returns `TooMuchData` if the supply buffer is bigger than
/// the allowed remote datagram size. The funcion does not check if the
@@ -3203,7 +3268,6 @@ impl Connection {
/// to check the estimated max datagram size and to use smaller datagrams.
/// `max_datagram_size` is just a current estimate and will change over
/// time depending on the encoded size of the packet number, ack frames, etc.
-
pub fn send_datagram(&mut self, buf: &[u8], id: impl Into<DatagramTracking>) -> Res<()> {
self.quic_datagrams
.add_datagram(buf, id.into(), &mut self.stats.borrow_mut())
diff --git a/third_party/rust/neqo-transport/src/connection/params.rs b/third_party/rust/neqo-transport/src/connection/params.rs
index 48aba4303b..72d1efa3ee 100644
--- a/third_party/rust/neqo-transport/src/connection/params.rs
+++ b/third_party/rust/neqo-transport/src/connection/params.rs
@@ -4,7 +4,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-use std::{cmp::max, convert::TryFrom, time::Duration};
+use std::{cmp::max, time::Duration};
pub use crate::recovery::FAST_PTO_SCALE;
use crate::{
@@ -41,7 +41,7 @@ pub enum PreferredAddressConfig {
Address(PreferredAddress),
}
-/// ConnectionParameters use for setting intitial value for QUIC parameters.
+/// `ConnectionParameters` use for setting intitial value for QUIC parameters.
/// This collects configuration like initial limits, protocol version, and
/// congestion control algorithm.
#[derive(Debug, Clone)]
@@ -108,6 +108,7 @@ impl Default for ConnectionParameters {
}
impl ConnectionParameters {
+ #[must_use]
pub fn get_versions(&self) -> &VersionConfig {
&self.versions
}
@@ -120,29 +121,35 @@ impl ConnectionParameters {
/// versions that should be enabled. This list should contain the initial
/// version and be in order of preference, with more preferred versions
/// before less preferred.
+ #[must_use]
pub fn versions(mut self, initial: Version, all: Vec<Version>) -> Self {
self.versions = VersionConfig::new(initial, all);
self
}
+ #[must_use]
pub fn get_cc_algorithm(&self) -> CongestionControlAlgorithm {
self.cc_algorithm
}
+ #[must_use]
pub fn cc_algorithm(mut self, v: CongestionControlAlgorithm) -> Self {
self.cc_algorithm = v;
self
}
+ #[must_use]
pub fn get_max_data(&self) -> u64 {
self.max_data
}
+ #[must_use]
pub fn max_data(mut self, v: u64) -> Self {
self.max_data = v;
self
}
+ #[must_use]
pub fn get_max_streams(&self, stream_type: StreamType) -> u64 {
match stream_type {
StreamType::BiDi => self.max_streams_bidi,
@@ -153,6 +160,7 @@ impl ConnectionParameters {
/// # Panics
///
/// If v > 2^60 (the maximum allowed by the protocol).
+ #[must_use]
pub fn max_streams(mut self, stream_type: StreamType, v: u64) -> Self {
assert!(v <= (1 << 60), "max_streams is too large");
match stream_type {
@@ -171,6 +179,7 @@ impl ConnectionParameters {
/// # Panics
///
/// If `StreamType::UniDi` and `false` are passed as that is not a valid combination.
+ #[must_use]
pub fn get_max_stream_data(&self, stream_type: StreamType, remote: bool) -> u64 {
match (stream_type, remote) {
(StreamType::BiDi, false) => self.max_stream_data_bidi_local,
@@ -188,6 +197,7 @@ impl ConnectionParameters {
///
/// If `StreamType::UniDi` and `false` are passed as that is not a valid combination
/// or if v >= 62 (the maximum allowed by the protocol).
+ #[must_use]
pub fn max_stream_data(mut self, stream_type: StreamType, remote: bool, v: u64) -> Self {
assert!(v < (1 << 62), "max stream data is too large");
match (stream_type, remote) {
@@ -208,26 +218,31 @@ impl ConnectionParameters {
}
/// Set a preferred address (which only has an effect for a server).
+ #[must_use]
pub fn preferred_address(mut self, preferred: PreferredAddress) -> Self {
self.preferred_address = PreferredAddressConfig::Address(preferred);
self
}
/// Disable the use of preferred addresses.
+ #[must_use]
pub fn disable_preferred_address(mut self) -> Self {
self.preferred_address = PreferredAddressConfig::Disabled;
self
}
+ #[must_use]
pub fn get_preferred_address(&self) -> &PreferredAddressConfig {
&self.preferred_address
}
+ #[must_use]
pub fn ack_ratio(mut self, ack_ratio: u8) -> Self {
self.ack_ratio = ack_ratio;
self
}
+ #[must_use]
pub fn get_ack_ratio(&self) -> u8 {
self.ack_ratio
}
@@ -235,45 +250,54 @@ impl ConnectionParameters {
/// # Panics
///
/// If `timeout` is 2^62 milliseconds or more.
+ #[must_use]
pub fn idle_timeout(mut self, timeout: Duration) -> Self {
assert!(timeout.as_millis() < (1 << 62), "idle timeout is too long");
self.idle_timeout = timeout;
self
}
+ #[must_use]
pub fn get_idle_timeout(&self) -> Duration {
self.idle_timeout
}
+ #[must_use]
pub fn get_datagram_size(&self) -> u64 {
self.datagram_size
}
+ #[must_use]
pub fn datagram_size(mut self, v: u64) -> Self {
self.datagram_size = v;
self
}
+ #[must_use]
pub fn get_outgoing_datagram_queue(&self) -> usize {
self.outgoing_datagram_queue
}
+ #[must_use]
pub fn outgoing_datagram_queue(mut self, v: usize) -> Self {
// The max queue length must be at least 1.
self.outgoing_datagram_queue = max(v, 1);
self
}
+ #[must_use]
pub fn get_incoming_datagram_queue(&self) -> usize {
self.incoming_datagram_queue
}
+ #[must_use]
pub fn incoming_datagram_queue(mut self, v: usize) -> Self {
// The max queue length must be at least 1.
self.incoming_datagram_queue = max(v, 1);
self
}
+ #[must_use]
pub fn get_fast_pto(&self) -> u8 {
self.fast_pto
}
@@ -293,39 +317,50 @@ impl ConnectionParameters {
/// # Panics
///
/// A value of 0 is invalid and will cause a panic.
+ #[must_use]
pub fn fast_pto(mut self, scale: u8) -> Self {
assert_ne!(scale, 0);
self.fast_pto = scale;
self
}
+ #[must_use]
pub fn is_fuzzing(&self) -> bool {
self.fuzzing
}
+ #[must_use]
pub fn fuzzing(mut self, enable: bool) -> Self {
self.fuzzing = enable;
self
}
+ #[must_use]
pub fn is_greasing(&self) -> bool {
self.grease
}
+ #[must_use]
pub fn grease(mut self, grease: bool) -> Self {
self.grease = grease;
self
}
+ #[must_use]
pub fn pacing_enabled(&self) -> bool {
self.pacing
}
+ #[must_use]
pub fn pacing(mut self, pacing: bool) -> Self {
self.pacing = pacing;
self
}
+ /// # Errors
+ /// When a connection ID cannot be obtained.
+ /// # Panics
+ /// Only when this code includes a transport parameter that is invalid.
pub fn create_transport_parameter(
&self,
role: Role,
diff --git a/third_party/rust/neqo-transport/src/connection/state.rs b/third_party/rust/neqo-transport/src/connection/state.rs
index 9afb42174f..9789151d3f 100644
--- a/third_party/rust/neqo-transport/src/connection/state.rs
+++ b/third_party/rust/neqo-transport/src/connection/state.rs
@@ -21,7 +21,7 @@ use crate::{
packet::PacketBuilder,
path::PathRef,
recovery::RecoveryToken,
- ConnectionError, Error, Res,
+ ConnectionError, Error,
};
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -66,6 +66,7 @@ impl State {
)
}
+ #[must_use]
pub fn error(&self) -> Option<&ConnectionError> {
if let Self::Closing { error, .. } | Self::Draining { error, .. } | Self::Closed(error) =
self
@@ -184,13 +185,13 @@ impl ClosingFrame {
}
}
-/// `StateSignaling` manages whether we need to send HANDSHAKE_DONE and CONNECTION_CLOSE.
+/// `StateSignaling` manages whether we need to send `HANDSHAKE_DONE` and `CONNECTION_CLOSE`.
/// Valid state transitions are:
-/// * Idle -> HandshakeDone: at the server when the handshake completes
-/// * HandshakeDone -> Idle: when a HANDSHAKE_DONE frame is sent
+/// * Idle -> `HandshakeDone`: at the server when the handshake completes
+/// * `HandshakeDone` -> Idle: when a `HANDSHAKE_DONE` frame is sent
/// * Idle/HandshakeDone -> Closing/Draining: when closing or draining
-/// * Closing/Draining -> CloseSent: after sending CONNECTION_CLOSE
-/// * CloseSent -> Closing: any time a new CONNECTION_CLOSE is needed
+/// * Closing/Draining -> `CloseSent`: after sending `CONNECTION_CLOSE`
+/// * `CloseSent` -> Closing: any time a new `CONNECTION_CLOSE` is needed
/// * -> Reset: from any state in case of a stateless reset
#[derive(Debug, Clone)]
pub enum StateSignaling {
@@ -214,13 +215,13 @@ impl StateSignaling {
*self = Self::HandshakeDone;
}
- pub fn write_done(&mut self, builder: &mut PacketBuilder) -> Res<Option<RecoveryToken>> {
+ pub fn write_done(&mut self, builder: &mut PacketBuilder) -> Option<RecoveryToken> {
if matches!(self, Self::HandshakeDone) && builder.remaining() >= 1 {
*self = Self::Idle;
builder.encode_varint(FRAME_TYPE_HANDSHAKE_DONE);
- Ok(Some(RecoveryToken::HandshakeDone))
+ Some(RecoveryToken::HandshakeDone)
} else {
- Ok(None)
+ None
}
}
diff --git a/third_party/rust/neqo-transport/src/connection/tests/ackrate.rs b/third_party/rust/neqo-transport/src/connection/tests/ackrate.rs
index 1b83d42acd..f0a1d17cd9 100644
--- a/third_party/rust/neqo-transport/src/connection/tests/ackrate.rs
+++ b/third_party/rust/neqo-transport/src/connection/tests/ackrate.rs
@@ -6,7 +6,7 @@
use std::{mem, time::Duration};
-use test_fixture::{addr_v4, assertions};
+use test_fixture::{assertions, DEFAULT_ADDR_V4};
use super::{
super::{ConnectionParameters, ACK_RATIO_SCALE},
@@ -164,7 +164,7 @@ fn migrate_ack_delay() {
let mut now = connect_rtt_idle(&mut client, &mut server, DEFAULT_RTT);
client
- .migrate(Some(addr_v4()), Some(addr_v4()), true, now)
+ .migrate(Some(DEFAULT_ADDR_V4), Some(DEFAULT_ADDR_V4), true, now)
.unwrap();
let client1 = send_something(&mut client, now);
diff --git a/third_party/rust/neqo-transport/src/connection/tests/cc.rs b/third_party/rust/neqo-transport/src/connection/tests/cc.rs
index b3467ea67c..b708bc421d 100644
--- a/third_party/rust/neqo-transport/src/connection/tests/cc.rs
+++ b/third_party/rust/neqo-transport/src/connection/tests/cc.rs
@@ -4,7 +4,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-use std::{convert::TryFrom, mem, time::Duration};
+use std::{mem, time::Duration};
use neqo_common::{qdebug, qinfo, Datagram};
@@ -71,6 +71,7 @@ fn cc_slow_start_to_cong_avoidance_recovery_period() {
client.stats().frame_rx.largest_acknowledged,
flight1_largest
);
+ let cwnd_before_cong = cwnd(&client);
// Client: send more
let (mut c_tx_dgrams, mut now) = fill_cwnd(&mut client, stream_id, now);
@@ -93,6 +94,7 @@ fn cc_slow_start_to_cong_avoidance_recovery_period() {
client.stats().frame_rx.largest_acknowledged,
flight2_largest
);
+ assert!(cwnd(&client) < cwnd_before_cong);
}
#[test]
diff --git a/third_party/rust/neqo-transport/src/connection/tests/close.rs b/third_party/rust/neqo-transport/src/connection/tests/close.rs
index f45e77e549..5351dd0d5c 100644
--- a/third_party/rust/neqo-transport/src/connection/tests/close.rs
+++ b/third_party/rust/neqo-transport/src/connection/tests/close.rs
@@ -6,7 +6,7 @@
use std::time::Duration;
-use test_fixture::{self, datagram, now};
+use test_fixture::{datagram, now};
use super::{
super::{Connection, Output, State},
diff --git a/third_party/rust/neqo-transport/src/connection/tests/datagram.rs b/third_party/rust/neqo-transport/src/connection/tests/datagram.rs
index 5b7b8dc0b4..ade8c753be 100644
--- a/third_party/rust/neqo-transport/src/connection/tests/datagram.rs
+++ b/third_party/rust/neqo-transport/src/connection/tests/datagram.rs
@@ -4,7 +4,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-use std::{cell::RefCell, convert::TryFrom, rc::Rc};
+use std::{cell::RefCell, rc::Rc};
use neqo_common::event::Provider;
use test_fixture::now;
diff --git a/third_party/rust/neqo-transport/src/connection/tests/fuzzing.rs b/third_party/rust/neqo-transport/src/connection/tests/fuzzing.rs
index 5425e1a16e..9924c06fa4 100644
--- a/third_party/rust/neqo-transport/src/connection/tests/fuzzing.rs
+++ b/third_party/rust/neqo-transport/src/connection/tests/fuzzing.rs
@@ -4,8 +4,6 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-#![cfg_attr(feature = "deny-warnings", deny(warnings))]
-#![warn(clippy::pedantic)]
#![cfg(feature = "fuzzing")]
use neqo_crypto::FIXED_TAG_FUZZING;
diff --git a/third_party/rust/neqo-transport/src/connection/tests/handshake.rs b/third_party/rust/neqo-transport/src/connection/tests/handshake.rs
index 93385ac1bc..af0352ce90 100644
--- a/third_party/rust/neqo-transport/src/connection/tests/handshake.rs
+++ b/third_party/rust/neqo-transport/src/connection/tests/handshake.rs
@@ -6,7 +6,6 @@
use std::{
cell::RefCell,
- convert::TryFrom,
mem,
net::{IpAddr, Ipv6Addr, SocketAddr},
rc::Rc,
@@ -18,8 +17,8 @@ use neqo_crypto::{
constants::TLS_CHACHA20_POLY1305_SHA256, generate_ech_keys, AuthenticationStatus,
};
use test_fixture::{
- self, addr, assertions, assertions::assert_coalesced_0rtt, datagram, fixture_init, now,
- split_datagram,
+ assertions, assertions::assert_coalesced_0rtt, datagram, fixture_init, now, split_datagram,
+ DEFAULT_ADDR,
};
use super::{
@@ -122,8 +121,8 @@ fn no_alpn() {
"example.com",
&["bad-alpn"],
Rc::new(RefCell::new(CountingConnectionIdGenerator::default())),
- addr(),
- addr(),
+ DEFAULT_ADDR,
+ DEFAULT_ADDR,
ConnectionParameters::default(),
now(),
)
@@ -251,8 +250,8 @@ fn chacha20poly1305() {
test_fixture::DEFAULT_SERVER_NAME,
test_fixture::DEFAULT_ALPN,
Rc::new(RefCell::new(EmptyConnectionIdGenerator::default())),
- addr(),
- addr(),
+ DEFAULT_ADDR,
+ DEFAULT_ADDR,
ConnectionParameters::default(),
now(),
)
@@ -347,7 +346,7 @@ fn reorder_05rtt_with_0rtt() {
let mut server = default_server();
let validation = AddressValidation::new(now(), ValidateAddress::NoToken).unwrap();
let validation = Rc::new(RefCell::new(validation));
- server.set_validation(Rc::clone(&validation));
+ server.set_validation(&validation);
let mut now = connect_with_rtt(&mut client, &mut server, now(), RTT);
// Include RTT in sending the ticket or the ticket age reported by the
@@ -730,8 +729,8 @@ fn connect_one_version() {
test_fixture::DEFAULT_SERVER_NAME,
test_fixture::DEFAULT_ALPN,
Rc::new(RefCell::new(CountingConnectionIdGenerator::default())),
- addr(),
- addr(),
+ DEFAULT_ADDR,
+ DEFAULT_ADDR,
ConnectionParameters::default().versions(version, vec![version]),
now(),
)
@@ -1135,3 +1134,54 @@ fn implicit_rtt_server() {
// an RTT estimate from having discarded the Initial packet number space.
assert_eq!(server.stats().rtt, RTT);
}
+
+#[test]
+fn emit_authentication_needed_once() {
+ let mut client = default_client();
+
+ let mut server = Connection::new_server(
+ test_fixture::LONG_CERT_KEYS,
+ test_fixture::DEFAULT_ALPN,
+ Rc::new(RefCell::new(CountingConnectionIdGenerator::default())),
+ ConnectionParameters::default(),
+ )
+ .expect("create a server");
+
+ let client1 = client.process(None, now());
+ assert!(client1.as_dgram_ref().is_some());
+
+ // The entire server flight doesn't fit in a single packet because the
+ // certificate is large, therefore the server will produce 2 packets.
+ let server1 = server.process(client1.as_dgram_ref(), now());
+ assert!(server1.as_dgram_ref().is_some());
+ let server2 = server.process(None, now());
+ assert!(server2.as_dgram_ref().is_some());
+
+ let authentication_needed_count = |client: &mut Connection| {
+ client
+ .events()
+ .filter(|e| matches!(e, ConnectionEvent::AuthenticationNeeded))
+ .count()
+ };
+
+ // Upon receiving the first packet, the client has the server certificate,
+ // but not yet all required handshake data. It moves to
+ // `HandshakeState::AuthenticationPending` and emits a
+ // `ConnectionEvent::AuthenticationNeeded` event.
+ //
+ // Note that this is a tiny bit fragile in that it depends on having a certificate
+ // that is within a fairly narrow range of sizes. It has to fit in a single
+ // packet, but be large enough that the CertificateVerify message does not
+ // also fit in the same packet. Our default test setup achieves this, but
+ // changes to the setup might invalidate this test.
+ let _ = client.process(server1.as_dgram_ref(), now());
+ assert_eq!(1, authentication_needed_count(&mut client));
+ assert!(client.peer_certificate().is_some());
+
+ // The `AuthenticationNeeded` event is still pending a call to
+ // `Connection::authenticated`. On receiving the second packet from the
+ // server, the client must not emit a another
+ // `ConnectionEvent::AuthenticationNeeded`.
+ let _ = client.process(server2.as_dgram_ref(), now());
+ assert_eq!(0, authentication_needed_count(&mut client));
+}
diff --git a/third_party/rust/neqo-transport/src/connection/tests/idle.rs b/third_party/rust/neqo-transport/src/connection/tests/idle.rs
index c33726917a..5d01131541 100644
--- a/third_party/rust/neqo-transport/src/connection/tests/idle.rs
+++ b/third_party/rust/neqo-transport/src/connection/tests/idle.rs
@@ -10,7 +10,7 @@ use std::{
};
use neqo_common::{qtrace, Encoder};
-use test_fixture::{self, now, split_datagram};
+use test_fixture::{now, split_datagram};
use super::{
super::{Connection, ConnectionParameters, IdleTimeout, Output, State},
@@ -310,28 +310,20 @@ fn idle_caching() {
server.process_input(&dgram.unwrap(), middle);
assert_eq!(server.stats().frame_rx.ping, ping_before_s + 1);
let mut tokens = Vec::new();
- server
- .crypto
- .streams
- .write_frame(
- PacketNumberSpace::Initial,
- &mut builder,
- &mut tokens,
- &mut FrameStats::default(),
- )
- .unwrap();
+ server.crypto.streams.write_frame(
+ PacketNumberSpace::Initial,
+ &mut builder,
+ &mut tokens,
+ &mut FrameStats::default(),
+ );
assert_eq!(tokens.len(), 1);
tokens.clear();
- server
- .crypto
- .streams
- .write_frame(
- PacketNumberSpace::Initial,
- &mut builder,
- &mut tokens,
- &mut FrameStats::default(),
- )
- .unwrap();
+ server.crypto.streams.write_frame(
+ PacketNumberSpace::Initial,
+ &mut builder,
+ &mut tokens,
+ &mut FrameStats::default(),
+ );
assert!(tokens.is_empty());
let dgram = server.process_output(middle).dgram();
diff --git a/third_party/rust/neqo-transport/src/connection/tests/keys.rs b/third_party/rust/neqo-transport/src/connection/tests/keys.rs
index c247bba670..847b253284 100644
--- a/third_party/rust/neqo-transport/src/connection/tests/keys.rs
+++ b/third_party/rust/neqo-transport/src/connection/tests/keys.rs
@@ -7,7 +7,7 @@
use std::mem;
use neqo_common::{qdebug, Datagram};
-use test_fixture::{self, now};
+use test_fixture::now;
use super::{
super::{
diff --git a/third_party/rust/neqo-transport/src/connection/tests/migration.rs b/third_party/rust/neqo-transport/src/connection/tests/migration.rs
index 8307a7dd84..405ae161a4 100644
--- a/third_party/rust/neqo-transport/src/connection/tests/migration.rs
+++ b/third_party/rust/neqo-transport/src/connection/tests/migration.rs
@@ -6,6 +6,7 @@
use std::{
cell::RefCell,
+ mem,
net::{IpAddr, Ipv6Addr, SocketAddr},
rc::Rc,
time::{Duration, Instant},
@@ -13,9 +14,8 @@ use std::{
use neqo_common::{Datagram, Decoder};
use test_fixture::{
- self, addr, addr_v4,
assertions::{assert_v4_path, assert_v6_path},
- fixture_init, new_neqo_qlog, now,
+ fixture_init, new_neqo_qlog, now, DEFAULT_ADDR, DEFAULT_ADDR_V4,
};
use super::{
@@ -94,8 +94,8 @@ fn rebinding_port() {
server.stream_close_send(stream_id).unwrap();
let dgram = server.process_output(now()).dgram();
let dgram = dgram.unwrap();
- assert_eq!(dgram.source(), addr());
- assert_eq!(dgram.destination(), new_port(addr()));
+ assert_eq!(dgram.source(), DEFAULT_ADDR);
+ assert_eq!(dgram.destination(), new_port(DEFAULT_ADDR));
}
/// This simulates an attack where a valid packet is forwarded on
@@ -109,7 +109,7 @@ fn path_forwarding_attack() {
let mut now = now();
let dgram = send_something(&mut client, now);
- let dgram = change_path(&dgram, addr_v4());
+ let dgram = change_path(&dgram, DEFAULT_ADDR_V4);
server.process_input(&dgram, now);
// The server now probes the new (primary) path.
@@ -188,7 +188,7 @@ fn migrate_immediate() {
let now = now();
client
- .migrate(Some(addr_v4()), Some(addr_v4()), true, now)
+ .migrate(Some(DEFAULT_ADDR_V4), Some(DEFAULT_ADDR_V4), true, now)
.unwrap();
let client1 = send_something(&mut client, now);
@@ -229,7 +229,7 @@ fn migrate_rtt() {
let now = connect_rtt_idle(&mut client, &mut server, RTT);
client
- .migrate(Some(addr_v4()), Some(addr_v4()), true, now)
+ .migrate(Some(DEFAULT_ADDR_V4), Some(DEFAULT_ADDR_V4), true, now)
.unwrap();
// The RTT might be increased for the new path, so allow a little flexibility.
let rtt = client.paths.rtt();
@@ -245,7 +245,7 @@ fn migrate_immediate_fail() {
let mut now = now();
client
- .migrate(Some(addr_v4()), Some(addr_v4()), true, now)
+ .migrate(Some(DEFAULT_ADDR_V4), Some(DEFAULT_ADDR_V4), true, now)
.unwrap();
let probe = client.process_output(now).dgram().unwrap();
@@ -293,7 +293,7 @@ fn migrate_same() {
let now = now();
client
- .migrate(Some(addr()), Some(addr()), true, now)
+ .migrate(Some(DEFAULT_ADDR), Some(DEFAULT_ADDR), true, now)
.unwrap();
let probe = client.process_output(now).dgram().unwrap();
@@ -320,7 +320,7 @@ fn migrate_same_fail() {
let mut now = now();
client
- .migrate(Some(addr()), Some(addr()), true, now)
+ .migrate(Some(DEFAULT_ADDR), Some(DEFAULT_ADDR), true, now)
.unwrap();
let probe = client.process_output(now).dgram().unwrap();
@@ -375,7 +375,7 @@ fn migration(mut client: Connection) {
let now = now();
client
- .migrate(Some(addr_v4()), Some(addr_v4()), false, now)
+ .migrate(Some(DEFAULT_ADDR_V4), Some(DEFAULT_ADDR_V4), false, now)
.unwrap();
let probe = client.process_output(now).dgram().unwrap();
@@ -449,8 +449,8 @@ fn migration_client_empty_cid() {
test_fixture::DEFAULT_SERVER_NAME,
test_fixture::DEFAULT_ALPN,
Rc::new(RefCell::new(EmptyConnectionIdGenerator::default())),
- addr(),
- addr(),
+ DEFAULT_ADDR,
+ DEFAULT_ADDR,
ConnectionParameters::default(),
now(),
)
@@ -568,22 +568,22 @@ fn preferred_address(hs_client: SocketAddr, hs_server: SocketAddr, preferred: So
/// Migration works for a new port number.
#[test]
fn preferred_address_new_port() {
- let a = addr();
+ let a = DEFAULT_ADDR;
preferred_address(a, a, new_port(a));
}
/// Migration works for a new address too.
#[test]
fn preferred_address_new_address() {
- let mut preferred = addr();
+ let mut preferred = DEFAULT_ADDR;
preferred.set_ip(IpAddr::V6(Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 2)));
- preferred_address(addr(), addr(), preferred);
+ preferred_address(DEFAULT_ADDR, DEFAULT_ADDR, preferred);
}
/// Migration works for IPv4 addresses.
#[test]
fn preferred_address_new_port_v4() {
- let a = addr_v4();
+ let a = DEFAULT_ADDR_V4;
preferred_address(a, a, new_port(a));
}
@@ -623,7 +623,7 @@ fn preferred_address_ignore_loopback() {
/// A preferred address in the wrong address family is ignored.
#[test]
fn preferred_address_ignore_different_family() {
- preferred_address_ignored(PreferredAddress::new_any(Some(addr_v4()), None));
+ preferred_address_ignored(PreferredAddress::new_any(Some(DEFAULT_ADDR_V4), None));
}
/// Disabling preferred addresses at the client means that it ignores a perfectly
@@ -631,7 +631,7 @@ fn preferred_address_ignore_different_family() {
#[test]
fn preferred_address_disabled_client() {
let mut client = new_client(ConnectionParameters::default().disable_preferred_address());
- let mut preferred = addr();
+ let mut preferred = DEFAULT_ADDR;
preferred.set_ip(IpAddr::V6(Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 2)));
let spa = PreferredAddress::new_any(None, Some(preferred));
let mut server = new_server(ConnectionParameters::default().preferred_address(spa));
@@ -643,7 +643,7 @@ fn preferred_address_disabled_client() {
fn preferred_address_empty_cid() {
fixture_init();
- let spa = PreferredAddress::new_any(None, Some(new_port(addr())));
+ let spa = PreferredAddress::new_any(None, Some(new_port(DEFAULT_ADDR)));
let res = Connection::new_server(
test_fixture::DEFAULT_KEYS,
test_fixture::DEFAULT_ALPN,
@@ -706,33 +706,33 @@ fn preferred_address_client() {
fn migration_invalid_state() {
let mut client = default_client();
assert!(client
- .migrate(Some(addr()), Some(addr()), false, now())
+ .migrate(Some(DEFAULT_ADDR), Some(DEFAULT_ADDR), false, now())
.is_err());
let mut server = default_server();
assert!(server
- .migrate(Some(addr()), Some(addr()), false, now())
+ .migrate(Some(DEFAULT_ADDR), Some(DEFAULT_ADDR), false, now())
.is_err());
connect_force_idle(&mut client, &mut server);
assert!(server
- .migrate(Some(addr()), Some(addr()), false, now())
+ .migrate(Some(DEFAULT_ADDR), Some(DEFAULT_ADDR), false, now())
.is_err());
client.close(now(), 0, "closing");
assert!(client
- .migrate(Some(addr()), Some(addr()), false, now())
+ .migrate(Some(DEFAULT_ADDR), Some(DEFAULT_ADDR), false, now())
.is_err());
let close = client.process(None, now()).dgram();
let dgram = server.process(close.as_ref(), now()).dgram();
assert!(server
- .migrate(Some(addr()), Some(addr()), false, now())
+ .migrate(Some(DEFAULT_ADDR), Some(DEFAULT_ADDR), false, now())
.is_err());
client.process_input(&dgram.unwrap(), now());
assert!(client
- .migrate(Some(addr()), Some(addr()), false, now())
+ .migrate(Some(DEFAULT_ADDR), Some(DEFAULT_ADDR), false, now())
.is_err());
}
@@ -753,32 +753,32 @@ fn migration_invalid_address() {
cant_migrate(None, None);
// Providing a zero port number isn't valid.
- let mut zero_port = addr();
+ let mut zero_port = DEFAULT_ADDR;
zero_port.set_port(0);
cant_migrate(None, Some(zero_port));
cant_migrate(Some(zero_port), None);
// An unspecified remote address is bad.
- let mut remote_unspecified = addr();
+ let mut remote_unspecified = DEFAULT_ADDR;
remote_unspecified.set_ip(IpAddr::V6(Ipv6Addr::from(0)));
cant_migrate(None, Some(remote_unspecified));
// Mixed address families is bad.
- cant_migrate(Some(addr()), Some(addr_v4()));
- cant_migrate(Some(addr_v4()), Some(addr()));
+ cant_migrate(Some(DEFAULT_ADDR), Some(DEFAULT_ADDR_V4));
+ cant_migrate(Some(DEFAULT_ADDR_V4), Some(DEFAULT_ADDR));
// Loopback to non-loopback is bad.
- cant_migrate(Some(addr()), Some(loopback()));
- cant_migrate(Some(loopback()), Some(addr()));
+ cant_migrate(Some(DEFAULT_ADDR), Some(loopback()));
+ cant_migrate(Some(loopback()), Some(DEFAULT_ADDR));
assert_eq!(
client
- .migrate(Some(addr()), Some(loopback()), true, now())
+ .migrate(Some(DEFAULT_ADDR), Some(loopback()), true, now())
.unwrap_err(),
Error::InvalidMigration
);
assert_eq!(
client
- .migrate(Some(loopback()), Some(addr()), true, now())
+ .migrate(Some(loopback()), Some(DEFAULT_ADDR), true, now())
.unwrap_err(),
Error::InvalidMigration
);
@@ -864,7 +864,7 @@ fn retire_prior_to_migration_failure() {
let original_cid = ConnectionId::from(get_cid(&send_something(&mut client, now())));
client
- .migrate(Some(addr_v4()), Some(addr_v4()), false, now())
+ .migrate(Some(DEFAULT_ADDR_V4), Some(DEFAULT_ADDR_V4), false, now())
.unwrap();
// The client now probes the new path.
@@ -919,7 +919,7 @@ fn retire_prior_to_migration_success() {
let original_cid = ConnectionId::from(get_cid(&send_something(&mut client, now())));
client
- .migrate(Some(addr_v4()), Some(addr_v4()), false, now())
+ .migrate(Some(DEFAULT_ADDR_V4), Some(DEFAULT_ADDR_V4), false, now())
.unwrap();
// The client now probes the new path.
@@ -951,3 +951,39 @@ fn retire_prior_to_migration_success() {
assert_ne!(get_cid(&dgram), original_cid);
assert_ne!(get_cid(&dgram), probe_cid);
}
+
+struct GarbageWriter {}
+
+impl crate::connection::test_internal::FrameWriter for GarbageWriter {
+ fn write_frames(&mut self, builder: &mut PacketBuilder) {
+ // Not a valid frame type.
+ builder.encode_varint(u32::MAX);
+ }
+}
+
+/// Test the case that we run out of connection ID and receive an invalid frame
+/// from a new path.
+#[test]
+#[should_panic(expected = "attempting to close with a temporary path")]
+fn error_on_new_path_with_no_connection_id() {
+ let mut client = default_client();
+ let mut server = default_server();
+ connect_force_idle(&mut client, &mut server);
+
+ let cid_gen: Rc<RefCell<dyn ConnectionIdGenerator>> =
+ Rc::new(RefCell::new(CountingConnectionIdGenerator::default()));
+ server.test_frame_writer = Some(Box::new(RetireAll { cid_gen }));
+ let retire_all = send_something(&mut server, now());
+
+ client.process_input(&retire_all, now());
+
+ server.test_frame_writer = Some(Box::new(GarbageWriter {}));
+ let garbage = send_something(&mut server, now());
+
+ let dgram = change_path(&garbage, DEFAULT_ADDR_V4);
+ client.process_input(&dgram, now());
+
+ // See issue #1697. We had a crash when the client had a temporary path and
+ // process_output is called.
+ mem::drop(client.process_output(now()));
+}
diff --git a/third_party/rust/neqo-transport/src/connection/tests/mod.rs b/third_party/rust/neqo-transport/src/connection/tests/mod.rs
index 8a999f4048..b6ce08f8d1 100644
--- a/third_party/rust/neqo-transport/src/connection/tests/mod.rs
+++ b/third_party/rust/neqo-transport/src/connection/tests/mod.rs
@@ -4,12 +4,9 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-#![deny(clippy::pedantic)]
-
use std::{
cell::RefCell,
cmp::min,
- convert::TryFrom,
mem,
rc::Rc,
time::{Duration, Instant},
@@ -18,7 +15,7 @@ use std::{
use enum_map::enum_map;
use neqo_common::{event::Provider, qdebug, qtrace, Datagram, Decoder, Role};
use neqo_crypto::{random, AllowZeroRtt, AuthenticationStatus, ResumptionToken};
-use test_fixture::{self, addr, fixture_init, new_neqo_qlog, now};
+use test_fixture::{fixture_init, new_neqo_qlog, now, DEFAULT_ADDR};
use super::{Connection, ConnectionError, ConnectionId, Output, State};
use crate::{
@@ -79,7 +76,7 @@ impl ConnectionIdDecoder for CountingConnectionIdGenerator {
impl ConnectionIdGenerator for CountingConnectionIdGenerator {
fn generate_cid(&mut self) -> Option<ConnectionId> {
- let mut r = random(20);
+ let mut r = random::<20>();
r[0] = 8;
r[1] = u8::try_from(self.counter >> 24).unwrap();
r[2] = u8::try_from((self.counter >> 16) & 0xff).unwrap();
@@ -107,8 +104,8 @@ pub fn new_client(params: ConnectionParameters) -> Connection {
test_fixture::DEFAULT_SERVER_NAME,
test_fixture::DEFAULT_ALPN,
Rc::new(RefCell::new(CountingConnectionIdGenerator::default())),
- addr(),
- addr(),
+ DEFAULT_ADDR,
+ DEFAULT_ADDR,
params,
now(),
)
@@ -278,7 +275,7 @@ fn exchange_ticket(
) -> ResumptionToken {
let validation = AddressValidation::new(now, ValidateAddress::NoToken).unwrap();
let validation = Rc::new(RefCell::new(validation));
- server.set_validation(Rc::clone(&validation));
+ server.set_validation(&validation);
server.send_ticket(now, &[]).expect("can send ticket");
let ticket = server.process_output(now).dgram();
assert!(ticket.is_some());
diff --git a/third_party/rust/neqo-transport/src/connection/tests/priority.rs b/third_party/rust/neqo-transport/src/connection/tests/priority.rs
index 1f86aa22e5..079ba93b9f 100644
--- a/third_party/rust/neqo-transport/src/connection/tests/priority.rs
+++ b/third_party/rust/neqo-transport/src/connection/tests/priority.rs
@@ -7,7 +7,7 @@
use std::{cell::RefCell, mem, rc::Rc};
use neqo_common::event::Provider;
-use test_fixture::{self, now};
+use test_fixture::now;
use super::{
super::{Connection, Error, Output},
@@ -370,7 +370,7 @@ fn low() {
let validation = Rc::new(RefCell::new(
AddressValidation::new(now, ValidateAddress::Never).unwrap(),
));
- server.set_validation(Rc::clone(&validation));
+ server.set_validation(&validation);
connect(&mut client, &mut server);
let id = server.stream_create(StreamType::UniDi).unwrap();
diff --git a/third_party/rust/neqo-transport/src/connection/tests/resumption.rs b/third_party/rust/neqo-transport/src/connection/tests/resumption.rs
index a8c45a9f06..7410e76ef8 100644
--- a/third_party/rust/neqo-transport/src/connection/tests/resumption.rs
+++ b/third_party/rust/neqo-transport/src/connection/tests/resumption.rs
@@ -6,7 +6,7 @@
use std::{cell::RefCell, mem, rc::Rc, time::Duration};
-use test_fixture::{self, assertions, now};
+use test_fixture::{assertions, now};
use super::{
connect, connect_with_rtt, default_client, default_server, exchange_ticket, get_tokens,
@@ -50,7 +50,7 @@ fn remember_smoothed_rtt() {
// wants to acknowledge; so the ticket will include an ACK frame too.
let validation = AddressValidation::new(now, ValidateAddress::NoToken).unwrap();
let validation = Rc::new(RefCell::new(validation));
- server.set_validation(Rc::clone(&validation));
+ server.set_validation(&validation);
server.send_ticket(now, &[]).expect("can send ticket");
let ticket = server.process_output(now).dgram();
assert!(ticket.is_some());
@@ -84,7 +84,7 @@ fn address_validation_token_resume() {
let mut server = default_server();
let validation = AddressValidation::new(now(), ValidateAddress::Always).unwrap();
let validation = Rc::new(RefCell::new(validation));
- server.set_validation(Rc::clone(&validation));
+ server.set_validation(&validation);
let mut now = connect_with_rtt(&mut client, &mut server, now(), RTT);
let token = exchange_ticket(&mut client, &mut server, now);
@@ -155,7 +155,7 @@ fn two_tickets_with_new_token() {
let mut server = default_server();
let validation = AddressValidation::new(now(), ValidateAddress::Always).unwrap();
let validation = Rc::new(RefCell::new(validation));
- server.set_validation(Rc::clone(&validation));
+ server.set_validation(&validation);
connect(&mut client, &mut server);
// Send two tickets with tokens and then bundle those into a packet.
diff --git a/third_party/rust/neqo-transport/src/connection/tests/stream.rs b/third_party/rust/neqo-transport/src/connection/tests/stream.rs
index 586a537b9d..f469866d50 100644
--- a/third_party/rust/neqo-transport/src/connection/tests/stream.rs
+++ b/third_party/rust/neqo-transport/src/connection/tests/stream.rs
@@ -4,7 +4,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-use std::{cmp::max, collections::HashMap, convert::TryFrom, mem};
+use std::{cmp::max, collections::HashMap, mem};
use neqo_common::{event::Provider, qdebug};
use test_fixture::now;
diff --git a/third_party/rust/neqo-transport/src/connection/tests/vn.rs b/third_party/rust/neqo-transport/src/connection/tests/vn.rs
index 22f15c991c..93872a94f4 100644
--- a/third_party/rust/neqo-transport/src/connection/tests/vn.rs
+++ b/third_party/rust/neqo-transport/src/connection/tests/vn.rs
@@ -7,7 +7,7 @@
use std::{mem, time::Duration};
use neqo_common::{event::Provider, Decoder, Encoder};
-use test_fixture::{self, assertions, datagram, now};
+use test_fixture::{assertions, datagram, now};
use super::{
super::{ConnectionError, ConnectionEvent, Output, State, ZeroRttState},
diff --git a/third_party/rust/neqo-transport/src/connection/tests/zerortt.rs b/third_party/rust/neqo-transport/src/connection/tests/zerortt.rs
index 0aa5573c98..b5e5f0d758 100644
--- a/third_party/rust/neqo-transport/src/connection/tests/zerortt.rs
+++ b/third_party/rust/neqo-transport/src/connection/tests/zerortt.rs
@@ -8,7 +8,7 @@ use std::{cell::RefCell, rc::Rc};
use neqo_common::event::Provider;
use neqo_crypto::{AllowZeroRtt, AntiReplay};
-use test_fixture::{self, assertions, now};
+use test_fixture::{assertions, now};
use super::{
super::Connection, connect, default_client, default_server, exchange_ticket, new_server,
diff --git a/third_party/rust/neqo-transport/src/crypto.rs b/third_party/rust/neqo-transport/src/crypto.rs
index f6cc7c0e2f..9840eaa1e1 100644
--- a/third_party/rust/neqo-transport/src/crypto.rs
+++ b/third_party/rust/neqo-transport/src/crypto.rs
@@ -8,7 +8,6 @@ use std::{
cell::RefCell,
cmp::{max, min},
collections::HashMap,
- convert::TryFrom,
mem,
ops::{Index, IndexMut, Range},
rc::Rc,
@@ -101,10 +100,10 @@ impl Crypto {
version,
protocols,
tls: agent,
- streams: Default::default(),
+ streams: CryptoStreams::default(),
states: CryptoStates {
fuzzing,
- ..Default::default()
+ ..CryptoStates::default()
},
})
}
@@ -239,14 +238,14 @@ impl Crypto {
/// Returns true if new handshake keys were installed.
pub fn install_keys(&mut self, role: Role) -> Res<bool> {
- if !self.tls.state().is_final() {
+ if self.tls.state().is_final() {
+ Ok(false)
+ } else {
let installed_hs = self.install_handshake_keys()?;
if role == Role::Server {
self.maybe_install_application_write_key(self.version)?;
}
Ok(installed_hs)
- } else {
- Ok(false)
}
}
@@ -274,7 +273,7 @@ impl Crypto {
fn maybe_install_application_write_key(&mut self, version: Version) -> Res<()> {
qtrace!([self], "Attempt to install application write key");
if let Some(secret) = self.tls.write_secret(TLS_EPOCH_APPLICATION_DATA) {
- self.states.set_application_write_key(version, secret)?;
+ self.states.set_application_write_key(version, &secret)?;
qdebug!([self], "Application write key installed");
}
Ok(())
@@ -290,7 +289,7 @@ impl Crypto {
.read_secret(TLS_EPOCH_APPLICATION_DATA)
.ok_or(Error::InternalError)?;
self.states
- .set_application_read_key(version, read_secret, expire_0rtt)?;
+ .set_application_read_key(version, &read_secret, expire_0rtt)?;
qdebug!([self], "application read keys installed");
Ok(())
}
@@ -313,8 +312,8 @@ impl Crypto {
builder: &mut PacketBuilder,
tokens: &mut Vec<RecoveryToken>,
stats: &mut FrameStats,
- ) -> Res<()> {
- self.streams.write_frame(space, builder, tokens, stats)
+ ) {
+ self.streams.write_frame(space, builder, tokens, stats);
}
pub fn acked(&mut self, token: &CryptoRecoveryToken) {
@@ -767,7 +766,7 @@ impl CryptoDxAppData {
pub fn new(
version: Version,
dir: CryptoDxDirection,
- secret: SymKey,
+ secret: &SymKey,
cipher: Cipher,
fuzzing: bool,
) -> Res<Self> {
@@ -776,12 +775,12 @@ impl CryptoDxAppData {
version,
dir,
TLS_EPOCH_APPLICATION_DATA,
- &secret,
+ secret,
cipher,
fuzzing,
),
cipher,
- next_secret: Self::update_secret(cipher, &secret)?,
+ next_secret: Self::update_secret(cipher, secret)?,
fuzzing,
})
}
@@ -1111,7 +1110,7 @@ impl CryptoStates {
});
}
- pub fn set_application_write_key(&mut self, version: Version, secret: SymKey) -> Res<()> {
+ pub fn set_application_write_key(&mut self, version: Version, secret: &SymKey) -> Res<()> {
debug_assert!(self.app_write.is_none());
debug_assert_ne!(self.cipher, 0);
let mut app = CryptoDxAppData::new(
@@ -1134,7 +1133,7 @@ impl CryptoStates {
pub fn set_application_read_key(
&mut self,
version: Version,
- secret: SymKey,
+ secret: &SymKey,
expire_0rtt: Instant,
) -> Res<()> {
debug_assert!(self.app_write.is_some(), "should have write keys installed");
@@ -1530,14 +1529,14 @@ impl CryptoStreams {
builder: &mut PacketBuilder,
tokens: &mut Vec<RecoveryToken>,
stats: &mut FrameStats,
- ) -> Res<()> {
+ ) {
let cs = self.get_mut(space).unwrap();
if let Some((offset, data)) = cs.tx.next_bytes() {
let mut header_len = 1 + Encoder::varint_len(offset) + 1;
// Don't bother if there isn't room for the header and some data.
if builder.remaining() < header_len + 1 {
- return Ok(());
+ return;
}
// Calculate length of data based on the minimum of:
// - available data
@@ -1561,7 +1560,6 @@ impl CryptoStreams {
}));
stats.crypto += 1;
}
- Ok(())
}
}
diff --git a/third_party/rust/neqo-transport/src/events.rs b/third_party/rust/neqo-transport/src/events.rs
index 88a85250ee..a892e384b9 100644
--- a/third_party/rust/neqo-transport/src/events.rs
+++ b/third_party/rust/neqo-transport/src/events.rs
@@ -52,7 +52,7 @@ pub enum ConnectionEvent {
stream_id: StreamId,
app_error: AppError,
},
- /// Peer has sent STOP_SENDING
+ /// Peer has sent `STOP_SENDING`
SendStreamStopSending {
stream_id: StreamId,
app_error: AppError,
@@ -61,7 +61,7 @@ pub enum ConnectionEvent {
SendStreamComplete {
stream_id: StreamId,
},
- /// Peer increased MAX_STREAMS
+ /// Peer increased `MAX_STREAMS`
SendStreamCreatable {
stream_type: StreamType,
},
@@ -254,8 +254,9 @@ impl EventProvider for ConnectionEvents {
#[cfg(test)]
mod tests {
- use super::*;
- use crate::{ConnectionError, Error};
+ use neqo_common::event::Provider;
+
+ use crate::{ConnectionError, ConnectionEvent, ConnectionEvents, Error, State, StreamId};
#[test]
fn event_culling() {
diff --git a/third_party/rust/neqo-transport/src/fc.rs b/third_party/rust/neqo-transport/src/fc.rs
index a219ca7e8d..5ddfce6463 100644
--- a/third_party/rust/neqo-transport/src/fc.rs
+++ b/third_party/rust/neqo-transport/src/fc.rs
@@ -8,7 +8,6 @@
// into flow control frames needing to be sent to the remote.
use std::{
- convert::TryFrom,
fmt::Debug,
ops::{Deref, DerefMut, Index, IndexMut},
};
@@ -249,7 +248,7 @@ where
}
}
- /// This function is called when STREAM_DATA_BLOCKED frame is received.
+ /// This function is called when `STREAM_DATA_BLOCKED` frame is received.
/// The flow control will try to send an update if possible.
pub fn send_flowc_update(&mut self) {
if self.retired + self.max_active > self.max_allowed {
diff --git a/third_party/rust/neqo-transport/src/frame.rs b/third_party/rust/neqo-transport/src/frame.rs
index f3d567ac7c..b3bb024a2c 100644
--- a/third_party/rust/neqo-transport/src/frame.rs
+++ b/third_party/rust/neqo-transport/src/frame.rs
@@ -6,7 +6,7 @@
// Directly relating to QUIC frames.
-use std::{convert::TryFrom, ops::RangeInclusive};
+use std::ops::RangeInclusive;
use neqo_common::{qtrace, Decoder};
@@ -78,6 +78,7 @@ impl CloseError {
}
}
+ #[must_use]
pub fn code(&self) -> u64 {
match self {
Self::Transport(c) | Self::Application(c) => *c,
@@ -303,7 +304,7 @@ impl<'a> Frame<'a> {
)
}
- /// Converts AckRanges as encoded in a ACK frame (see -transport
+ /// Converts `AckRanges` as encoded in a ACK frame (see -transport
/// 19.3.1) into ranges of acked packets (end, start), inclusive of
/// start and end values.
pub fn decode_ack_frame(
@@ -387,6 +388,7 @@ impl<'a> Frame<'a> {
}
}
+ #[allow(clippy::too_many_lines)] // Yeah, but it's a nice match statement.
pub fn decode(dec: &mut Decoder<'a>) -> Res<Self> {
/// Maximum ACK Range Count in ACK Frame
///
@@ -430,7 +432,7 @@ impl<'a> Frame<'a> {
}
})?;
let fa = dv(dec)?;
- let mut arr: Vec<AckRange> = Vec::with_capacity(nr as usize);
+ let mut arr: Vec<AckRange> = Vec::with_capacity(usize::try_from(nr)?);
for _ in 0..nr {
let ar = AckRange {
gap: dv(dec)?,
@@ -615,7 +617,11 @@ impl<'a> Frame<'a> {
mod tests {
use neqo_common::{Decoder, Encoder};
- use super::*;
+ use crate::{
+ cid::MAX_CONNECTION_ID_LEN,
+ frame::{AckRange, Frame, FRAME_TYPE_ACK},
+ CloseError, Error, StreamId, StreamType,
+ };
fn just_dec(f: &Frame, s: &str) {
let encoded = Encoder::from_hex(s);
diff --git a/third_party/rust/neqo-transport/src/lib.rs b/third_party/rust/neqo-transport/src/lib.rs
index ecf7ee2f73..be482c466f 100644
--- a/third_party/rust/neqo-transport/src/lib.rs
+++ b/third_party/rust/neqo-transport/src/lib.rs
@@ -4,8 +4,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-#![cfg_attr(feature = "deny-warnings", deny(warnings))]
-#![warn(clippy::use_self)]
+#![allow(clippy::module_name_repetitions)] // This lint doesn't work here.
use neqo_common::qinfo;
use neqo_crypto::Error as CryptoError;
@@ -30,6 +29,9 @@ pub mod recv_stream;
#[cfg(not(feature = "bench"))]
mod recv_stream;
mod rtt;
+#[cfg(feature = "bench")]
+pub mod send_stream;
+#[cfg(not(feature = "bench"))]
mod send_stream;
mod sender;
pub mod server;
@@ -130,6 +132,7 @@ pub enum Error {
}
impl Error {
+ #[must_use]
pub fn code(&self) -> TransportError {
match self {
Self::NoError
@@ -206,6 +209,7 @@ pub enum ConnectionError {
}
impl ConnectionError {
+ #[must_use]
pub fn app_code(&self) -> Option<AppError> {
match self {
Self::Application(e) => Some(*e),
diff --git a/third_party/rust/neqo-transport/src/pace.rs b/third_party/rust/neqo-transport/src/pace.rs
index e5214c1bc8..5b88e5c0c4 100644
--- a/third_party/rust/neqo-transport/src/pace.rs
+++ b/third_party/rust/neqo-transport/src/pace.rs
@@ -5,11 +5,9 @@
// except according to those terms.
// Pacer
-#![deny(clippy::pedantic)]
use std::{
cmp::min,
- convert::TryFrom,
fmt::{Debug, Display},
time::{Duration, Instant},
};
diff --git a/third_party/rust/neqo-transport/src/packet/mod.rs b/third_party/rust/neqo-transport/src/packet/mod.rs
index ccfd212d5f..8458f69779 100644
--- a/third_party/rust/neqo-transport/src/packet/mod.rs
+++ b/third_party/rust/neqo-transport/src/packet/mod.rs
@@ -7,9 +7,7 @@
// Encoding and decoding packets off the wire.
use std::{
cmp::min,
- convert::TryFrom,
fmt,
- iter::ExactSizeIterator,
ops::{Deref, DerefMut, Range},
time::Instant,
};
@@ -172,11 +170,12 @@ impl PacketBuilder {
}
/// Start building a long header packet.
- /// For an Initial packet you will need to call initial_token(),
+ /// For an Initial packet you will need to call `initial_token()`,
/// even if the token is empty.
///
/// See `short()` for more on how to handle this in cases where there is no space.
#[allow(clippy::reversed_empty_ranges)] // For initializing an empty range.
+ #[allow(clippy::similar_names)] // For dcid and scid, which are fine here.
pub fn long(
mut encoder: Encoder,
pt: PacketType,
@@ -271,7 +270,7 @@ impl PacketBuilder {
let mask = if quic_bit { PACKET_BIT_FIXED_QUIC } else { 0 }
| if self.is_long() { 0 } else { PACKET_BIT_SPIN };
let first = self.header.start;
- self.encoder.as_mut()[first] ^= random(1)[0] & mask;
+ self.encoder.as_mut()[first] ^= random::<1>()[0] & mask;
}
/// For an Initial packet, encode the token.
@@ -315,6 +314,7 @@ impl PacketBuilder {
self.pn = pn;
}
+ #[allow(clippy::cast_possible_truncation)] // Nope.
fn write_len(&mut self, expansion: usize) {
let len = self.encoder.len() - (self.offsets.len + 2) + expansion;
self.encoder.as_mut()[self.offsets.len] = 0x40 | ((len >> 8) & 0x3f) as u8;
@@ -410,6 +410,7 @@ impl PacketBuilder {
/// As this is a simple packet, this is just an associated function.
/// As Retry is odd (it has to be constructed with leading bytes),
/// this returns a [`Vec<u8>`] rather than building on an encoder.
+ #[allow(clippy::similar_names)] // scid and dcid are fine here.
pub fn retry(
version: Version,
dcid: &[u8],
@@ -424,7 +425,7 @@ impl PacketBuilder {
PACKET_BIT_LONG
| PACKET_BIT_FIXED_QUIC
| (PacketType::Retry.to_byte(version) << 4)
- | (random(1)[0] & 0xf),
+ | (random::<1>()[0] & 0xf),
);
encoder.encode_uint(4, version.wire_version());
encoder.encode_vec(1, dcid);
@@ -441,6 +442,7 @@ impl PacketBuilder {
}
/// Make a Version Negotiation packet.
+ #[allow(clippy::similar_names)] // scid and dcid are fine here.
pub fn version_negotiation(
dcid: &[u8],
scid: &[u8],
@@ -448,7 +450,7 @@ impl PacketBuilder {
versions: &[Version],
) -> Vec<u8> {
let mut encoder = Encoder::default();
- let mut grease = random(4);
+ let mut grease = random::<4>();
// This will not include the "QUIC bit" sometimes. Intentionally.
encoder.encode_byte(PACKET_BIT_LONG | (grease[3] & 0x7f));
encoder.encode(&[0; 4]); // Zero version == VN.
@@ -492,7 +494,7 @@ impl From<PacketBuilder> for Encoder {
}
}
-/// PublicPacket holds information from packets that is public only. This allows for
+/// `PublicPacket` holds information from packets that is public only. This allows for
/// processing of packets prior to decryption.
pub struct PublicPacket<'a> {
/// The packet type.
@@ -552,6 +554,7 @@ impl<'a> PublicPacket<'a> {
/// Decode the common parts of a packet. This provides minimal parsing and validation.
/// Returns a tuple of a `PublicPacket` and a slice with any remainder from the datagram.
+ #[allow(clippy::similar_names)] // For dcid and scid, which are fine.
pub fn decode(data: &'a [u8], dcid_decoder: &dyn ConnectionIdDecoder) -> Res<(Self, &'a [u8])> {
let mut decoder = Decoder::new(data);
let first = Self::opt(decoder.decode_byte())?;
@@ -868,10 +871,14 @@ mod tests {
use neqo_common::Encoder;
use test_fixture::{fixture_init, now};
- use super::*;
use crate::{
+ cid::MAX_CONNECTION_ID_LEN,
crypto::{CryptoDxState, CryptoStates},
- EmptyConnectionIdGenerator, RandomConnectionIdGenerator, Version,
+ packet::{
+ PacketBuilder, PacketType, PublicPacket, PACKET_BIT_FIXED_QUIC, PACKET_BIT_LONG,
+ PACKET_BIT_SPIN,
+ },
+ ConnectionId, EmptyConnectionIdGenerator, RandomConnectionIdGenerator, Version,
};
const CLIENT_CID: &[u8] = &[0x83, 0x94, 0xc8, 0xf0, 0x3e, 0x51, 0x57, 0x08];
@@ -1366,8 +1373,12 @@ mod tests {
#[test]
fn build_vn() {
fixture_init();
- let mut vn =
- PacketBuilder::version_negotiation(SERVER_CID, CLIENT_CID, 0x0a0a0a0a, &Version::all());
+ let mut vn = PacketBuilder::version_negotiation(
+ SERVER_CID,
+ CLIENT_CID,
+ 0x0a0a_0a0a,
+ &Version::all(),
+ );
// Erase randomness from greasing...
assert_eq!(vn.len(), SAMPLE_VN.len());
vn[0] &= 0x80;
@@ -1380,8 +1391,12 @@ mod tests {
#[test]
fn vn_do_not_repeat_client_grease() {
fixture_init();
- let vn =
- PacketBuilder::version_negotiation(SERVER_CID, CLIENT_CID, 0x0a0a0a0a, &Version::all());
+ let vn = PacketBuilder::version_negotiation(
+ SERVER_CID,
+ CLIENT_CID,
+ 0x0a0a_0a0a,
+ &Version::all(),
+ );
assert_ne!(&vn[SAMPLE_VN.len() - 4..], &[0x0a, 0x0a, 0x0a, 0x0a]);
}
diff --git a/third_party/rust/neqo-transport/src/packet/retry.rs b/third_party/rust/neqo-transport/src/packet/retry.rs
index 004e9de6e7..72036d3b49 100644
--- a/third_party/rust/neqo-transport/src/packet/retry.rs
+++ b/third_party/rust/neqo-transport/src/packet/retry.rs
@@ -4,8 +4,6 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-#![deny(clippy::pedantic)]
-
use std::cell::RefCell;
use neqo_common::qerror;
diff --git a/third_party/rust/neqo-transport/src/path.rs b/third_party/rust/neqo-transport/src/path.rs
index d6920c8d94..4e8d9958ab 100644
--- a/third_party/rust/neqo-transport/src/path.rs
+++ b/third_party/rust/neqo-transport/src/path.rs
@@ -4,12 +4,10 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-#![deny(clippy::pedantic)]
#![allow(clippy::module_name_repetitions)]
use std::{
cell::RefCell,
- convert::TryFrom,
fmt::{self, Display},
mem,
net::{IpAddr, SocketAddr},
@@ -72,7 +70,7 @@ pub struct Paths {
/// Connection IDs that need to be retired.
to_retire: Vec<u64>,
- /// QLog handler.
+ /// `QLog` handler.
qlog: NeqoQlog,
}
@@ -156,7 +154,7 @@ impl Paths {
/// Get a reference to the primary path. Use this prior to handshake completion.
pub fn primary_fallible(&self) -> Option<PathRef> {
- self.primary.as_ref().map(Rc::clone)
+ self.primary.clone()
}
/// Returns true if the path is not permanent.
@@ -341,7 +339,7 @@ impl Paths {
None
}
})
- .or_else(|| self.primary.as_ref().map(Rc::clone))
+ .or_else(|| self.primary.clone())
}
/// A `PATH_RESPONSE` was received.
@@ -527,7 +525,7 @@ pub struct Path {
/// For a path that is not validated, this is `None`. For a validated
/// path, the time that the path was last valid.
validated: Option<Instant>,
- /// A path challenge was received and PATH_RESPONSE has not been sent.
+ /// A path challenge was received and `PATH_RESPONSE` has not been sent.
challenge: Option<[u8; 8]>,
/// The round trip time estimate for this path.
@@ -796,7 +794,7 @@ impl Path {
// Send PATH_CHALLENGE.
if let ProbeState::ProbeNeeded { probe_count } = self.state {
qtrace!([self], "Initiating path challenge {}", probe_count);
- let data = <[u8; 8]>::try_from(&random(8)[..]).unwrap();
+ let data = random::<8>();
builder.encode_varint(FRAME_TYPE_PATH_CHALLENGE);
builder.encode(&data);
diff --git a/third_party/rust/neqo-transport/src/qlog.rs b/third_party/rust/neqo-transport/src/qlog.rs
index 434395fd23..2572966104 100644
--- a/third_party/rust/neqo-transport/src/qlog.rs
+++ b/third_party/rust/neqo-transport/src/qlog.rs
@@ -7,9 +7,7 @@
// Functions that handle capturing QLOG traces.
use std::{
- convert::TryFrom,
ops::{Deref, RangeInclusive},
- string::String,
time::Duration,
};
@@ -38,6 +36,7 @@ use crate::{
pub fn connection_tparams_set(qlog: &mut NeqoQlog, tph: &TransportParametersHandler) {
qlog.add_event_data(|| {
let remote = tph.remote();
+ #[allow(clippy::cast_possible_truncation)] // Nope.
let ev_data = EventData::TransportParametersSet(
qlog::events::quic::TransportParametersSet {
owner: None,
@@ -206,7 +205,7 @@ pub fn packet_sent(
let mut frames = SmallVec::new();
while d.remaining() > 0 {
if let Ok(f) = Frame::decode(&mut d) {
- frames.push(frame_to_qlogframe(&f))
+ frames.push(frame_to_qlogframe(&f));
} else {
qinfo!("qlog: invalid frame");
break;
@@ -300,7 +299,7 @@ pub fn packet_received(
while d.remaining() > 0 {
if let Ok(f) = Frame::decode(&mut d) {
- frames.push(frame_to_qlogframe(&f))
+ frames.push(frame_to_qlogframe(&f));
} else {
qinfo!("qlog: invalid frame");
break;
@@ -355,6 +354,7 @@ pub fn metrics_updated(qlog: &mut NeqoQlog, updated_metrics: &[QlogMetric]) {
let mut pacing_rate: Option<u64> = None;
for metric in updated_metrics {
+ #[allow(clippy::cast_precision_loss)] // Nought to do here.
match metric {
QlogMetric::MinRtt(v) => min_rtt = Some(v.as_secs_f32() * 1000.0),
QlogMetric::SmoothedRtt(v) => smoothed_rtt = Some(v.as_secs_f32() * 1000.0),
@@ -391,6 +391,8 @@ pub fn metrics_updated(qlog: &mut NeqoQlog, updated_metrics: &[QlogMetric]) {
// Helper functions
+#[allow(clippy::too_many_lines)] // Yeah, but it's a nice match.
+#[allow(clippy::cast_possible_truncation, clippy::cast_precision_loss)] // No choice here.
fn frame_to_qlogframe(frame: &Frame) -> QuicFrame {
match frame {
Frame::Padding => QuicFrame::Padding,
diff --git a/third_party/rust/neqo-transport/src/quic_datagrams.rs b/third_party/rust/neqo-transport/src/quic_datagrams.rs
index 07f3594768..d7c4769e31 100644
--- a/third_party/rust/neqo-transport/src/quic_datagrams.rs
+++ b/third_party/rust/neqo-transport/src/quic_datagrams.rs
@@ -6,7 +6,7 @@
// https://datatracker.ietf.org/doc/html/draft-ietf-quic-datagram
-use std::{cmp::min, collections::VecDeque, convert::TryFrom};
+use std::{cmp::min, collections::VecDeque};
use neqo_common::Encoder;
@@ -103,7 +103,7 @@ impl QuicDatagrams {
/// This function tries to write a datagram frame into a packet.
/// If the frame does not fit into the packet, the datagram will
- /// be dropped and a DatagramLost event will be posted.
+ /// be dropped and a `DatagramLost` event will be posted.
pub fn write_frames(
&mut self,
builder: &mut PacketBuilder,
diff --git a/third_party/rust/neqo-transport/src/recovery.rs b/third_party/rust/neqo-transport/src/recovery.rs
index d90989b486..dbea3aaf57 100644
--- a/third_party/rust/neqo-transport/src/recovery.rs
+++ b/third_party/rust/neqo-transport/src/recovery.rs
@@ -6,12 +6,9 @@
// Tracking of sent packets and detecting their loss.
-#![deny(clippy::pedantic)]
-
use std::{
cmp::{max, min},
collections::BTreeMap,
- convert::TryFrom,
mem,
ops::RangeInclusive,
time::{Duration, Instant},
@@ -1020,14 +1017,13 @@ impl ::std::fmt::Display for LossRecovery {
mod tests {
use std::{
cell::RefCell,
- convert::TryInto,
ops::{Deref, DerefMut, RangeInclusive},
rc::Rc,
time::{Duration, Instant},
};
use neqo_common::qlog::NeqoQlog;
- use test_fixture::{addr, now};
+ use test_fixture::{now, DEFAULT_ADDR};
use super::{
LossRecovery, LossRecoverySpace, PacketNumberSpace, SendProfile, SentPacket, FAST_PTO_SCALE,
@@ -1105,7 +1101,14 @@ mod tests {
impl Default for Fixture {
fn default() -> Self {
const CC: CongestionControlAlgorithm = CongestionControlAlgorithm::NewReno;
- let mut path = Path::temporary(addr(), addr(), CC, true, NeqoQlog::default(), now());
+ let mut path = Path::temporary(
+ DEFAULT_ADDR,
+ DEFAULT_ADDR,
+ CC,
+ true,
+ NeqoQlog::default(),
+ now(),
+ );
path.make_permanent(
None,
ConnectionIdEntry::new(0, ConnectionId::from(&[1, 2, 3]), [0; 16]),
diff --git a/third_party/rust/neqo-transport/src/recv_stream.rs b/third_party/rust/neqo-transport/src/recv_stream.rs
index 06ca59685d..5da80d6004 100644
--- a/third_party/rust/neqo-transport/src/recv_stream.rs
+++ b/third_party/rust/neqo-transport/src/recv_stream.rs
@@ -11,7 +11,6 @@ use std::{
cell::RefCell,
cmp::max,
collections::BTreeMap,
- convert::TryFrom,
mem,
rc::{Rc, Weak},
};
@@ -34,6 +33,7 @@ use crate::{
const RX_STREAM_DATA_WINDOW: u64 = 0x10_0000; // 1MiB
// Export as usize for consistency with SEND_BUFFER_SIZE
+#[allow(clippy::cast_possible_truncation)] // Yeah, nope.
pub const RECV_BUFFER_SIZE: usize = RX_STREAM_DATA_WINDOW as usize;
#[derive(Debug, Default)]
@@ -130,6 +130,7 @@ pub struct RxStreamOrderer {
}
impl RxStreamOrderer {
+ #[must_use]
pub fn new() -> Self {
Self::default()
}
@@ -137,6 +138,9 @@ impl RxStreamOrderer {
/// Process an incoming stream frame off the wire. This may result in data
/// being available to upper layers if frame is not out of order (ooo) or
/// if the frame fills a gap.
+ /// # Panics
+ /// Only when `u64` values cannot be converted to `usize`, which only
+ /// happens on 32-bit machines that hold far too much data at the same time.
pub fn inbound_frame(&mut self, mut new_start: u64, mut new_data: &[u8]) {
qtrace!("Inbound data offset={} len={}", new_start, new_data.len());
@@ -275,6 +279,7 @@ impl RxStreamOrderer {
}
/// Are any bytes readable?
+ #[must_use]
pub fn data_ready(&self) -> bool {
self.data_ranges
.keys()
@@ -301,20 +306,24 @@ impl RxStreamOrderer {
false
}
})
- .map(|(_, data_len)| data_len as usize)
- .sum()
+ // Accumulate, but saturate at usize::MAX.
+ .fold(0, |acc: usize, (_, data_len)| {
+ acc.saturating_add(usize::try_from(data_len).unwrap_or(usize::MAX))
+ })
}
/// Bytes read by the application.
+ #[must_use]
pub fn retired(&self) -> u64 {
self.retired
}
+ #[must_use]
pub fn received(&self) -> u64 {
self.received
}
- /// Data bytes buffered. Could be more than bytes_readable if there are
+ /// Data bytes buffered. Could be more than `bytes_readable` if there are
/// ranges missing.
fn buffered(&self) -> u64 {
self.data_ranges
@@ -588,6 +597,7 @@ impl RecvStream {
self.state = new_state;
}
+ #[must_use]
pub fn stats(&self) -> RecvStreamStats {
match &self.state {
RecvStreamState::Recv { recv_buf, .. }
@@ -622,6 +632,11 @@ impl RecvStream {
}
}
+ /// # Errors
+ /// When the incoming data violates flow control limits.
+ /// # Panics
+ /// Only when `u64` values are so big that they can't fit in a `usize`, which
+ /// only happens on a 32-bit machine that has far too much unread data.
pub fn inbound_stream_frame(&mut self, fin: bool, offset: u64, data: &[u8]) -> Res<()> {
// We should post a DataReadable event only once when we change from no-data-ready to
// data-ready. Therefore remember the state before processing a new frame.
@@ -691,6 +706,8 @@ impl RecvStream {
Ok(())
}
+ /// # Errors
+ /// When the reset occurs at an invalid point.
pub fn reset(&mut self, application_error_code: AppError, final_size: u64) -> Res<()> {
self.state.flow_control_consume_data(final_size, true)?;
match &mut self.state {
@@ -773,6 +790,7 @@ impl RecvStream {
}
}
+ #[must_use]
pub fn is_terminal(&self) -> bool {
matches!(
self.state,
@@ -792,8 +810,8 @@ impl RecvStream {
}
/// # Errors
- ///
/// `NoMoreData` if data and fin bit were previously read by the application.
+ #[allow(clippy::missing_panics_doc)] // with a >16 exabyte packet on a 128-bit machine, maybe
pub fn read(&mut self, buf: &mut [u8]) -> Res<(usize, bool)> {
let data_recvd_state = matches!(self.state, RecvStreamState::DataRecvd { .. });
match &mut self.state {
@@ -967,6 +985,7 @@ impl RecvStream {
}
#[cfg(test)]
+ #[must_use]
pub fn has_frames_to_write(&self) -> bool {
if let RecvStreamState::Recv { fc, .. } = &self.state {
fc.frame_needed()
@@ -976,6 +995,7 @@ impl RecvStream {
}
#[cfg(test)]
+ #[must_use]
pub fn fc(&self) -> Option<&ReceiverFlowControl<StreamId>> {
match &self.state {
RecvStreamState::Recv { fc, .. }
@@ -990,11 +1010,18 @@ impl RecvStream {
#[cfg(test)]
mod tests {
- use std::ops::Range;
+ use std::{cell::RefCell, ops::Range, rc::Rc};
- use neqo_common::Encoder;
+ use neqo_common::{qtrace, Encoder};
- use super::*;
+ use super::RecvStream;
+ use crate::{
+ fc::ReceiverFlowControl,
+ packet::PacketBuilder,
+ recv_stream::{RxStreamOrderer, RX_STREAM_DATA_WINDOW},
+ stats::FrameStats,
+ ConnectionEvents, Error, StreamId, RECV_BUFFER_SIZE,
+ };
const SESSION_WINDOW: usize = 1024;
@@ -1444,8 +1471,8 @@ mod tests {
let mut buf = vec![0u8; RECV_BUFFER_SIZE + 100]; // Make it overlarge
assert!(!s.has_frames_to_write());
- s.inbound_stream_frame(false, 0, &[0; RECV_BUFFER_SIZE])
- .unwrap();
+ let big_buf = vec![0; RECV_BUFFER_SIZE];
+ s.inbound_stream_frame(false, 0, &big_buf).unwrap();
assert!(!s.has_frames_to_write());
assert_eq!(s.read(&mut buf).unwrap(), (RECV_BUFFER_SIZE, false));
assert!(!s.data_ready());
@@ -1476,8 +1503,8 @@ mod tests {
fn stream_max_stream_data() {
let mut s = create_stream(1024 * RX_STREAM_DATA_WINDOW);
assert!(!s.has_frames_to_write());
- s.inbound_stream_frame(false, 0, &[0; RECV_BUFFER_SIZE])
- .unwrap();
+ let big_buf = vec![0; RECV_BUFFER_SIZE];
+ s.inbound_stream_frame(false, 0, &big_buf).unwrap();
s.inbound_stream_frame(false, RX_STREAM_DATA_WINDOW, &[1; 1])
.unwrap_err();
}
@@ -1520,9 +1547,10 @@ mod tests {
#[test]
fn no_stream_flowc_event_after_exiting_recv() {
let mut s = create_stream(1024 * RX_STREAM_DATA_WINDOW);
- s.inbound_stream_frame(false, 0, &[0; RECV_BUFFER_SIZE])
- .unwrap();
- let mut buf = [0; RECV_BUFFER_SIZE];
+ let mut buf = vec![0; RECV_BUFFER_SIZE];
+ // Write from buf at first.
+ s.inbound_stream_frame(false, 0, &buf).unwrap();
+ // Then read into it.
s.read(&mut buf).unwrap();
assert!(s.has_frames_to_write());
s.inbound_stream_frame(true, RX_STREAM_DATA_WINDOW, &[])
@@ -1634,7 +1662,7 @@ mod tests {
assert_eq!(fc.retired(), retired);
}
- /// Test consuming the flow control in RecvStreamState::Recv
+ /// Test consuming the flow control in `RecvStreamState::Recv`
#[test]
fn fc_state_recv_1() {
const SW: u64 = 1024;
@@ -1651,7 +1679,7 @@ mod tests {
check_fc(s.fc().unwrap(), SW / 4, 0);
}
- /// Test consuming the flow control in RecvStreamState::Recv
+ /// Test consuming the flow control in `RecvStreamState::Recv`
/// with multiple streams
#[test]
fn fc_state_recv_2() {
@@ -1678,7 +1706,7 @@ mod tests {
check_fc(s2.fc().unwrap(), SW / 4, 0);
}
- /// Test retiring the flow control in RecvStreamState::Recv
+ /// Test retiring the flow control in `RecvStreamState::Recv`
/// with multiple streams
#[test]
fn fc_state_recv_3() {
@@ -1730,7 +1758,7 @@ mod tests {
check_fc(s2.fc().unwrap(), SW / 4, SW / 4);
}
- /// Test consuming the flow control in RecvStreamState::Recv - duplicate data
+ /// Test consuming the flow control in `RecvStreamState::Recv` - duplicate data
#[test]
fn fc_state_recv_4() {
const SW: u64 = 1024;
@@ -1753,7 +1781,7 @@ mod tests {
check_fc(s.fc().unwrap(), SW / 4, 0);
}
- /// Test consuming the flow control in RecvStreamState::Recv - filling a gap in the
+ /// Test consuming the flow control in `RecvStreamState::Recv` - filling a gap in the
/// data stream.
#[test]
fn fc_state_recv_5() {
@@ -1774,7 +1802,7 @@ mod tests {
check_fc(s.fc().unwrap(), SW / 4, 0);
}
- /// Test consuming the flow control in RecvStreamState::Recv - receiving frame past
+ /// Test consuming the flow control in `RecvStreamState::Recv` - receiving frame past
/// the flow control will cause an error.
#[test]
fn fc_state_recv_6() {
@@ -1859,7 +1887,7 @@ mod tests {
assert_eq!(stats.max_stream_data, 1);
}
- /// Test flow control in RecvStreamState::SizeKnown
+ /// Test flow control in `RecvStreamState::SizeKnown`
#[test]
fn fc_state_size_known() {
const SW: u64 = 1024;
@@ -1916,7 +1944,7 @@ mod tests {
assert!(s.fc().is_none());
}
- /// Test flow control in RecvStreamState::DataRecvd
+ /// Test flow control in `RecvStreamState::DataRecvd`
#[test]
fn fc_state_data_recv() {
const SW: u64 = 1024;
@@ -1961,7 +1989,7 @@ mod tests {
assert!(s.fc().is_none());
}
- /// Test flow control in RecvStreamState::DataRead
+ /// Test flow control in `RecvStreamState::DataRead`
#[test]
fn fc_state_data_read() {
const SW: u64 = 1024;
@@ -1999,7 +2027,7 @@ mod tests {
assert!(s.fc().is_none());
}
- /// Test flow control in RecvStreamState::AbortReading and final size is known
+ /// Test flow control in `RecvStreamState::AbortReading` and final size is known
#[test]
fn fc_state_abort_reading_1() {
const SW: u64 = 1024;
@@ -2041,7 +2069,7 @@ mod tests {
check_fc(s.fc().unwrap(), SW / 2, SW / 2);
}
- /// Test flow control in RecvStreamState::AbortReading and final size is unknown
+ /// Test flow control in `RecvStreamState::AbortReading` and final size is unknown
#[test]
fn fc_state_abort_reading_2() {
const SW: u64 = 1024;
@@ -2099,7 +2127,7 @@ mod tests {
check_fc(s.fc().unwrap(), SW / 2 + 20, SW / 2 + 20);
}
- /// Test flow control in RecvStreamState::WaitForReset
+ /// Test flow control in `RecvStreamState::WaitForReset`
#[test]
fn fc_state_wait_for_reset() {
const SW: u64 = 1024;
diff --git a/third_party/rust/neqo-transport/src/rtt.rs b/third_party/rust/neqo-transport/src/rtt.rs
index 4b05198bc9..3b2969f689 100644
--- a/third_party/rust/neqo-transport/src/rtt.rs
+++ b/third_party/rust/neqo-transport/src/rtt.rs
@@ -6,8 +6,6 @@
// Tracking of sent packets and detecting their loss.
-#![deny(clippy::pedantic)]
-
use std::{
cmp::{max, min},
time::{Duration, Instant},
diff --git a/third_party/rust/neqo-transport/src/send_stream.rs b/third_party/rust/neqo-transport/src/send_stream.rs
index 5feb785ac6..8771ec7765 100644
--- a/third_party/rust/neqo-transport/src/send_stream.rs
+++ b/third_party/rust/neqo-transport/src/send_stream.rs
@@ -9,8 +9,7 @@
use std::{
cell::RefCell,
cmp::{max, min, Ordering},
- collections::{BTreeMap, VecDeque},
- convert::TryFrom,
+ collections::{btree_map::Entry, BTreeMap, VecDeque},
hash::{Hash, Hasher},
mem,
ops::Add,
@@ -18,7 +17,7 @@ use std::{
};
use indexmap::IndexMap;
-use neqo_common::{qdebug, qerror, qinfo, qtrace, Encoder, Role};
+use neqo_common::{qdebug, qerror, qtrace, Encoder, Role};
use smallvec::SmallVec;
use crate::{
@@ -111,7 +110,7 @@ impl Add<RetransmissionPriority> for TransmissionPriority {
/// If data is lost, this determines the priority that applies to retransmissions
/// of that data.
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub enum RetransmissionPriority {
/// Prioritize retransmission at a fixed priority.
/// With this, it is possible to prioritize retransmissions lower than transmissions.
@@ -123,19 +122,14 @@ pub enum RetransmissionPriority {
Same,
/// Increase the priority of retransmissions (the default).
/// Retransmissions of `Critical` or `Important` aren't elevated at all.
+ #[default]
Higher,
/// Increase the priority of retransmissions a lot.
/// This is useful for streams that are particularly exposed to head-of-line blocking.
MuchHigher,
}
-impl Default for RetransmissionPriority {
- fn default() -> Self {
- Self::Higher
- }
-}
-
-#[derive(Debug, PartialEq, Clone, Copy)]
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
enum RangeState {
Sent,
Acked,
@@ -144,169 +138,268 @@ enum RangeState {
/// Track ranges in the stream as sent or acked. Acked implies sent. Not in a
/// range implies needing-to-be-sent, either initially or as a retransmission.
#[derive(Debug, Default, PartialEq)]
-struct RangeTracker {
- // offset, (len, RangeState). Use u64 for len because ranges can exceed 32bits.
+pub struct RangeTracker {
+ /// The number of bytes that have been acknowledged starting from offset 0.
+ acked: u64,
+ /// A map that tracks the state of ranges.
+ /// Keys are the offset of the start of the range.
+ /// Values is a tuple of the range length and its state.
used: BTreeMap<u64, (u64, RangeState)>,
+ /// This is a cache for the output of `first_unmarked_range`, which we check a lot.
+ first_unmarked: Option<(u64, Option<u64>)>,
}
impl RangeTracker {
fn highest_offset(&self) -> u64 {
self.used
- .range(..)
- .next_back()
- .map_or(0, |(k, (v, _))| *k + *v)
+ .last_key_value()
+ .map_or(self.acked, |(&k, &(v, _))| k + v)
}
fn acked_from_zero(&self) -> u64 {
- self.used
- .get(&0)
- .filter(|(_, state)| *state == RangeState::Acked)
- .map_or(0, |(v, _)| *v)
+ self.acked
}
/// Find the first unmarked range. If all are contiguous, this will return
- /// (highest_offset(), None).
- fn first_unmarked_range(&self) -> (u64, Option<u64>) {
- let mut prev_end = 0;
+ /// (`highest_offset()`, None).
+ fn first_unmarked_range(&mut self) -> (u64, Option<u64>) {
+ if let Some(first_unmarked) = self.first_unmarked {
+ return first_unmarked;
+ }
+
+ let mut prev_end = self.acked;
- for (cur_off, (cur_len, _)) in &self.used {
- if prev_end == *cur_off {
+ for (&cur_off, &(cur_len, _)) in &self.used {
+ if prev_end == cur_off {
prev_end = cur_off + cur_len;
} else {
- return (prev_end, Some(cur_off - prev_end));
+ let res = (prev_end, Some(cur_off - prev_end));
+ self.first_unmarked = Some(res);
+ return res;
}
}
+ self.first_unmarked = Some((prev_end, None));
(prev_end, None)
}
- /// Turn one range into a list of subranges that align with existing
- /// ranges.
- /// Check impermissible overlaps in subregions: Sent cannot overwrite Acked.
- //
- // e.g. given N is new and ABC are existing:
- // NNNNNNNNNNNNNNNN
- // AAAAA BBBCCCCC ...then we want 5 chunks:
- // 1122222333444555
- //
- // but also if we have this:
- // NNNNNNNNNNNNNNNN
- // AAAAAAAAAA BBBB ...then break existing A and B ranges up:
- //
- // 1111111122222233
- // aaAAAAAAAA BBbb
- //
- // Doing all this work up front should make handling each chunk much
- // easier.
- fn chunk_range_on_edges(
- &mut self,
- new_off: u64,
- new_len: u64,
- new_state: RangeState,
- ) -> Vec<(u64, u64, RangeState)> {
- let mut tmp_off = new_off;
- let mut tmp_len = new_len;
- let mut v = Vec::new();
-
- // cut previous overlapping range if needed
- let prev = self.used.range_mut(..tmp_off).next_back();
- if let Some((prev_off, (prev_len, prev_state))) = prev {
- let prev_state = *prev_state;
- let overlap = (*prev_off + *prev_len).saturating_sub(new_off);
- *prev_len -= overlap;
- if overlap > 0 {
- self.used.insert(new_off, (overlap, prev_state));
+ /// When the range of acknowledged bytes from zero increases, we need to drop any
+ /// ranges within that span AND maybe extend it to include any adjacent acknowledged ranges.
+ fn coalesce_acked(&mut self) {
+ while let Some(e) = self.used.first_entry() {
+ match self.acked.cmp(e.key()) {
+ Ordering::Greater => {
+ let (off, (len, state)) = e.remove_entry();
+ let overflow = (off + len).saturating_sub(self.acked);
+ if overflow > 0 {
+ if state == RangeState::Acked {
+ self.acked += overflow;
+ } else {
+ self.used.insert(self.acked, (overflow, state));
+ }
+ break;
+ }
+ }
+ Ordering::Equal => {
+ if e.get().1 == RangeState::Acked {
+ let (len, _) = e.remove();
+ self.acked += len;
+ }
+ break;
+ }
+ Ordering::Less => break,
}
}
+ }
- let mut last_existing_remaining = None;
- for (off, (len, state)) in self.used.range(tmp_off..tmp_off + tmp_len) {
- // Create chunk for "overhang" before an existing range
- if tmp_off < *off {
- let sub_len = off - tmp_off;
- v.push((tmp_off, sub_len, new_state));
- tmp_off += sub_len;
- tmp_len -= sub_len;
- }
+ /// Mark a range as acknowledged. This is simpler than marking a range as sent
+ /// because an acknowledged range can never turn back into a sent range, so
+ /// this function can just override the entire range.
+ ///
+ /// The only tricky parts are making sure that we maintain `self.acked`,
+ /// which is the first acknowledged range. And making sure that we don't create
+ /// ranges of the same type that are adjacent; these need to be merged.
+ #[allow(clippy::missing_panics_doc)] // with a >16 exabyte packet on a 128-bit machine, maybe
+ pub fn mark_acked(&mut self, new_off: u64, new_len: usize) {
+ let end = new_off + u64::try_from(new_len).unwrap();
+ let new_off = max(self.acked, new_off);
+ let mut new_len = end.saturating_sub(new_off);
+ if new_len == 0 {
+ return;
+ }
- // Create chunk to match existing range
- let sub_len = min(*len, tmp_len);
- let remaining_len = len - sub_len;
- if new_state == RangeState::Sent && *state == RangeState::Acked {
- qinfo!(
- "Attempted to downgrade overlapping range Acked range {}-{} with Sent {}-{}",
- off,
- len,
- new_off,
- new_len
- );
- } else {
- v.push((tmp_off, sub_len, new_state));
- }
- tmp_off += sub_len;
- tmp_len -= sub_len;
+ self.first_unmarked = None;
+ if new_off == self.acked {
+ self.acked += new_len;
+ self.coalesce_acked();
+ return;
+ }
+ let mut new_end = new_off + new_len;
- if remaining_len > 0 {
- last_existing_remaining = Some((*off, sub_len, remaining_len, *state));
+ // Get all existing ranges that start within this new range.
+ let mut covered = self
+ .used
+ .range(new_off..new_end)
+ .map(|(&k, _)| k)
+ .collect::<SmallVec<[_; 8]>>();
+
+ if let Entry::Occupied(next_entry) = self.used.entry(new_end) {
+ // Check if the very next entry is the same type as this.
+ if next_entry.get().1 == RangeState::Acked {
+ // If is is acked, drop it and extend this new range.
+ let (extra_len, _) = next_entry.remove();
+ new_len += extra_len;
+ new_end += extra_len;
+ }
+ } else if let Some(last) = covered.pop() {
+ // Otherwise, the last of the existing ranges might overhang this one by some.
+ let (old_off, (old_len, old_state)) = self.used.remove_entry(&last).unwrap(); // can't fail
+ let remainder = (old_off + old_len).saturating_sub(new_end);
+ if remainder > 0 {
+ if old_state == RangeState::Acked {
+ // Just extend the current range.
+ new_len += remainder;
+ new_end += remainder;
+ } else {
+ self.used.insert(new_end, (remainder, RangeState::Sent));
+ }
}
}
-
- // Maybe break last existing range in two so that a final chunk will
- // have the same length as an existing range entry
- if let Some((off, sub_len, remaining_len, state)) = last_existing_remaining {
- *self.used.get_mut(&off).expect("must be there") = (sub_len, state);
- self.used.insert(off + sub_len, (remaining_len, state));
+ // All covered ranges can just be trashed.
+ for k in covered {
+ self.used.remove(&k);
}
- // Create final chunk if anything remains of the new range
- if tmp_len > 0 {
- v.push((tmp_off, tmp_len, new_state));
+ // Now either merge with a preceding acked range
+ // or cut a preceding sent range as needed.
+ let prev = self.used.range_mut(..new_off).next_back();
+ if let Some((prev_off, (prev_len, prev_state))) = prev {
+ let prev_end = *prev_off + *prev_len;
+ if prev_end >= new_off {
+ if *prev_state == RangeState::Sent {
+ *prev_len = new_off - *prev_off;
+ if prev_end > new_end {
+ // There is some extra sent range after the new acked range.
+ self.used
+ .insert(new_end, (prev_end - new_end, RangeState::Sent));
+ }
+ } else {
+ *prev_len = max(prev_end, new_end) - *prev_off;
+ return;
+ }
+ }
+ }
+ self.used.insert(new_off, (new_len, RangeState::Acked));
+ }
+
+ /// Turn a single sent range into a list of subranges that align with existing
+ /// acknowledged ranges.
+ ///
+ /// This is more complicated than adding acked ranges because any acked ranges
+ /// need to be kept in place, with sent ranges filling the gaps.
+ ///
+ /// This means:
+ /// ```ignore
+ /// AAA S AAAS AAAAA
+ /// + SSSSSSSSSSSSS
+ /// = AAASSSAAASSAAAAA
+ /// ```
+ ///
+ /// But we also have to ensure that:
+ /// ```ignore
+ /// SSSS
+ /// + SS
+ /// = SSSSSS
+ /// ```
+ /// and
+ /// ```ignore
+ /// SSSSS
+ /// + SS
+ /// = SSSSSS
+ /// ```
+ #[allow(clippy::missing_panics_doc)] // not possible
+ pub fn mark_sent(&mut self, mut new_off: u64, new_len: usize) {
+ let new_end = new_off + u64::try_from(new_len).unwrap();
+ new_off = max(self.acked, new_off);
+ let mut new_len = new_end.saturating_sub(new_off);
+ if new_len == 0 {
+ return;
}
- v
- }
+ self.first_unmarked = None;
- /// Merge contiguous Acked ranges into the first entry (0). This range may
- /// be dropped from the send buffer.
- fn coalesce_acked_from_zero(&mut self) {
- let acked_range_from_zero = self
+ // Get all existing ranges that start within this new range.
+ let covered = self
.used
- .get_mut(&0)
- .filter(|(_, state)| *state == RangeState::Acked)
- .map(|(len, _)| *len);
-
- if let Some(len_from_zero) = acked_range_from_zero {
- let mut new_len_from_zero = len_from_zero;
-
- // See if there's another Acked range entry contiguous to this one
- while let Some((next_len, _)) = self
- .used
- .get(&new_len_from_zero)
- .filter(|(_, state)| *state == RangeState::Acked)
- {
- let to_remove = new_len_from_zero;
- new_len_from_zero += *next_len;
- self.used.remove(&to_remove);
- }
-
- if len_from_zero != new_len_from_zero {
- self.used.get_mut(&0).expect("must be there").0 = new_len_from_zero;
+ .range(new_off..(new_off + new_len))
+ .map(|(&k, _)| k)
+ .collect::<SmallVec<[u64; 8]>>();
+
+ if let Entry::Occupied(next_entry) = self.used.entry(new_end) {
+ if next_entry.get().1 == RangeState::Sent {
+ // Check if the very next entry is the same type as this, so it can be merged.
+ let (extra_len, _) = next_entry.remove();
+ new_len += extra_len;
}
}
- }
- fn mark_range(&mut self, off: u64, len: usize, state: RangeState) {
- if len == 0 {
- qinfo!("mark 0-length range at {}", off);
- return;
- }
+ // Merge with any preceding sent range that might overlap,
+ // or cut the head of this if the preceding range is acked.
+ let prev = self.used.range(..new_off).next_back();
+ if let Some((&prev_off, &(prev_len, prev_state))) = prev {
+ if prev_off + prev_len >= new_off {
+ let overlap = prev_off + prev_len - new_off;
+ new_len = new_len.saturating_sub(overlap);
+ if new_len == 0 {
+ // The previous range completely covers this one (no more to do).
+ return;
+ }
- let subranges = self.chunk_range_on_edges(off, len as u64, state);
+ if prev_state == RangeState::Acked {
+ // The previous range is acked, so it cuts this one.
+ new_off += overlap;
+ } else {
+ // Extend the current range backwards.
+ new_off = prev_off;
+ new_len += prev_len;
+ // The previous range will be updated below.
+ // It might need to be cut because of a covered acked range.
+ }
+ }
+ }
- for (sub_off, sub_len, sub_state) in subranges {
- self.used.insert(sub_off, (sub_len, sub_state));
+ // Now interleave new sent chunks with any existing acked chunks.
+ for old_off in covered {
+ let Entry::Occupied(e) = self.used.entry(old_off) else {
+ unreachable!();
+ };
+ let &(old_len, old_state) = e.get();
+ if old_state == RangeState::Acked {
+ // Now we have to insert a chunk ahead of this acked chunk.
+ let chunk_len = old_off - new_off;
+ if chunk_len > 0 {
+ self.used.insert(new_off, (chunk_len, RangeState::Sent));
+ }
+ let included = chunk_len + old_len;
+ new_len = new_len.saturating_sub(included);
+ if new_len == 0 {
+ return;
+ }
+ new_off += included;
+ } else {
+ let overhang = (old_off + old_len).saturating_sub(new_off + new_len);
+ new_len += overhang;
+ if *e.key() != new_off {
+ // Retain a sent entry at `new_off`.
+ // This avoids the work of removing and re-creating an entry.
+ // The value will be overwritten when the next insert occurs,
+ // either when this loop hits an acked range (above)
+ // or for any remainder (below).
+ e.remove();
+ }
+ }
}
- self.coalesce_acked_from_zero();
+ self.used.insert(new_off, (new_len, RangeState::Sent));
}
fn unmark_range(&mut self, off: u64, len: usize) {
@@ -315,6 +408,7 @@ impl RangeTracker {
return;
}
+ self.first_unmarked = None;
let len = u64::try_from(len).unwrap();
let end_off = off + len;
@@ -376,6 +470,9 @@ impl RangeTracker {
}
/// Unmark all sent ranges.
+ /// # Panics
+ /// On 32-bit machines where far too much is sent before calling this.
+ /// Note that this should not be called for handshakes, which should never exceed that limit.
pub fn unmark_sent(&mut self) {
self.unmark_range(0, usize::try_from(self.highest_offset()).unwrap());
}
@@ -384,36 +481,37 @@ impl RangeTracker {
/// Buffer to contain queued bytes and track their state.
#[derive(Debug, Default, PartialEq)]
pub struct TxBuffer {
- retired: u64, // contig acked bytes, no longer in buffer
send_buf: VecDeque<u8>, // buffer of not-acked bytes
ranges: RangeTracker, // ranges in buffer that have been sent or acked
}
impl TxBuffer {
+ #[must_use]
pub fn new() -> Self {
Self::default()
}
- /// Attempt to add some or all of the passed-in buffer to the TxBuffer.
+ /// Attempt to add some or all of the passed-in buffer to the `TxBuffer`.
pub fn send(&mut self, buf: &[u8]) -> usize {
let can_buffer = min(SEND_BUFFER_SIZE - self.buffered(), buf.len());
if can_buffer > 0 {
self.send_buf.extend(&buf[..can_buffer]);
- assert!(self.send_buf.len() <= SEND_BUFFER_SIZE);
+ debug_assert!(self.send_buf.len() <= SEND_BUFFER_SIZE);
}
can_buffer
}
- pub fn next_bytes(&self) -> Option<(u64, &[u8])> {
+ #[allow(clippy::missing_panics_doc)] // These are not possible.
+ pub fn next_bytes(&mut self) -> Option<(u64, &[u8])> {
let (start, maybe_len) = self.ranges.first_unmarked_range();
- if start == self.retired + u64::try_from(self.buffered()).unwrap() {
+ if start == self.retired() + u64::try_from(self.buffered()).unwrap() {
return None;
}
// Convert from ranges-relative-to-zero to
// ranges-relative-to-buffer-start
- let buff_off = usize::try_from(start - self.retired).unwrap();
+ let buff_off = usize::try_from(start - self.retired()).unwrap();
// Deque returns two slices. Create a subslice from whichever
// one contains the first unmarked data.
@@ -437,23 +535,22 @@ impl TxBuffer {
}
pub fn mark_as_sent(&mut self, offset: u64, len: usize) {
- self.ranges.mark_range(offset, len, RangeState::Sent);
+ self.ranges.mark_sent(offset, len);
}
+ #[allow(clippy::missing_panics_doc)] // Not possible here.
pub fn mark_as_acked(&mut self, offset: u64, len: usize) {
- self.ranges.mark_range(offset, len, RangeState::Acked);
+ let prev_retired = self.retired();
+ self.ranges.mark_acked(offset, len);
- // We can drop contig acked range from the buffer
- let new_retirable = self.ranges.acked_from_zero() - self.retired;
+ // Any newly-retired bytes can be dropped from the buffer.
+ let new_retirable = self.retired() - prev_retired;
debug_assert!(new_retirable <= self.buffered() as u64);
- let keep_len =
- self.buffered() - usize::try_from(new_retirable).expect("should fit in usize");
+ let keep = self.buffered() - usize::try_from(new_retirable).unwrap();
// Truncate front
- self.send_buf.rotate_left(self.buffered() - keep_len);
- self.send_buf.truncate(keep_len);
-
- self.retired += new_retirable;
+ self.send_buf.rotate_left(self.buffered() - keep);
+ self.send_buf.truncate(keep);
}
pub fn mark_as_lost(&mut self, offset: u64, len: usize) {
@@ -465,8 +562,9 @@ impl TxBuffer {
self.ranges.unmark_sent();
}
+ #[must_use]
pub fn retired(&self) -> u64 {
- self.retired
+ self.ranges.acked_from_zero()
}
fn buffered(&self) -> usize {
@@ -478,7 +576,7 @@ impl TxBuffer {
}
fn used(&self) -> u64 {
- self.retired + u64::try_from(self.buffered()).unwrap()
+ self.retired() + u64::try_from(self.buffered()).unwrap()
}
}
@@ -693,6 +791,7 @@ impl SendStream {
self.fair = make_fair;
}
+ #[must_use]
pub fn is_fair(&self) -> bool {
self.fair
}
@@ -706,6 +805,7 @@ impl SendStream {
self.retransmission_priority = retransmission;
}
+ #[must_use]
pub fn sendorder(&self) -> Option<SendOrder> {
self.sendorder
}
@@ -715,6 +815,7 @@ impl SendStream {
}
/// If all data has been buffered or written, how much was sent.
+ #[must_use]
pub fn final_size(&self) -> Option<u64> {
match &self.state {
SendStreamState::DataSent { send_buf, .. } => Some(send_buf.used()),
@@ -723,10 +824,13 @@ impl SendStream {
}
}
+ #[must_use]
pub fn stats(&self) -> SendStreamStats {
SendStreamStats::new(self.bytes_written(), self.bytes_sent, self.bytes_acked())
}
+ #[must_use]
+ #[allow(clippy::missing_panics_doc)] // not possible
pub fn bytes_written(&self) -> u64 {
match &self.state {
SendStreamState::Send { send_buf, .. } | SendStreamState::DataSent { send_buf, .. } => {
@@ -749,6 +853,7 @@ impl SendStream {
}
}
+ #[must_use]
pub fn bytes_acked(&self) -> u64 {
match &self.state {
SendStreamState::Send { send_buf, .. } | SendStreamState::DataSent { send_buf, .. } => {
@@ -766,11 +871,13 @@ impl SendStream {
/// offset.
fn next_bytes(&mut self, retransmission_only: bool) -> Option<(u64, &[u8])> {
match self.state {
- SendStreamState::Send { ref send_buf, .. } => {
- send_buf.next_bytes().and_then(|(offset, slice)| {
+ SendStreamState::Send {
+ ref mut send_buf, ..
+ } => {
+ let result = send_buf.next_bytes();
+ if let Some((offset, slice)) = result {
if retransmission_only {
qtrace!(
- [self],
"next_bytes apply retransmission limit at {}",
self.retransmission_offset
);
@@ -786,13 +893,16 @@ impl SendStream {
} else {
Some((offset, slice))
}
- })
+ } else {
+ None
+ }
}
SendStreamState::DataSent {
- ref send_buf,
+ ref mut send_buf,
fin_sent,
..
} => {
+ let used = send_buf.used(); // immutable first
let bytes = send_buf.next_bytes();
if bytes.is_some() {
bytes
@@ -800,7 +910,7 @@ impl SendStream {
None
} else {
// Send empty stream frame with fin set
- Some((send_buf.used(), &[]))
+ Some((used, &[]))
}
}
SendStreamState::Ready { .. }
@@ -833,6 +943,7 @@ impl SendStream {
}
/// Maybe write a `STREAM` frame.
+ #[allow(clippy::missing_panics_doc)] // not possible
pub fn write_stream_frame(
&mut self,
priority: TransmissionPriority,
@@ -995,6 +1106,7 @@ impl SendStream {
}
}
+ #[allow(clippy::missing_panics_doc)] // not possible
pub fn mark_as_sent(&mut self, offset: u64, len: usize, fin: bool) {
self.bytes_sent = max(self.bytes_sent, offset + u64::try_from(len).unwrap());
@@ -1010,6 +1122,7 @@ impl SendStream {
}
}
+ #[allow(clippy::missing_panics_doc)] // not possible
pub fn mark_as_acked(&mut self, offset: u64, len: usize, fin: bool) {
match self.state {
SendStreamState::Send {
@@ -1047,6 +1160,7 @@ impl SendStream {
}
}
+ #[allow(clippy::missing_panics_doc)] // not possible
pub fn mark_as_lost(&mut self, offset: u64, len: usize, fin: bool) {
self.retransmission_offset = max(
self.retransmission_offset,
@@ -1075,6 +1189,7 @@ impl SendStream {
/// Bytes sendable on stream. Constrained by stream credit available,
/// connection credit available, and space in the tx buffer.
+ #[must_use]
pub fn avail(&self) -> usize {
if let SendStreamState::Ready { fc, conn_fc } | SendStreamState::Send { fc, conn_fc, .. } =
&self.state
@@ -1100,6 +1215,7 @@ impl SendStream {
}
}
+ #[must_use]
pub fn is_terminal(&self) -> bool {
matches!(
self.state,
@@ -1107,10 +1223,14 @@ impl SendStream {
)
}
+ /// # Errors
+ /// When `buf` is empty or when the stream is already closed.
pub fn send(&mut self, buf: &[u8]) -> Res<usize> {
self.send_internal(buf, false)
}
+ /// # Errors
+ /// When `buf` is empty or when the stream is already closed.
pub fn send_atomic(&mut self, buf: &[u8]) -> Res<usize> {
self.send_internal(buf, true)
}
@@ -1155,9 +1275,9 @@ impl SendStream {
if atomic {
self.send_blocked_if_space_needed(buf.len());
return Ok(0);
- } else {
- &buf[..self.avail()]
}
+
+ &buf[..self.avail()]
} else {
buf
};
@@ -1202,6 +1322,7 @@ impl SendStream {
}
}
+ #[allow(clippy::missing_panics_doc)] // not possible
pub fn reset(&mut self, err: AppError) {
match &self.state {
SendStreamState::Ready { fc, .. } => {
@@ -1296,6 +1417,7 @@ impl OrderGroup {
}
}
+ #[must_use]
pub fn stream_ids(&self) -> &Vec<StreamId> {
&self.vec
}
@@ -1319,26 +1441,24 @@ impl OrderGroup {
next
}
+ /// # Panics
+ /// If the stream ID is already present.
pub fn insert(&mut self, stream_id: StreamId) {
- match self.vec.binary_search(&stream_id) {
- Ok(_) => {
- // element already in vector @ `pos`
- panic!("Duplicate stream_id {}", stream_id)
- }
- Err(pos) => self.vec.insert(pos, stream_id),
- }
+ let Err(pos) = self.vec.binary_search(&stream_id) else {
+ // element already in vector @ `pos`
+ panic!("Duplicate stream_id {stream_id}");
+ };
+ self.vec.insert(pos, stream_id);
}
+ /// # Panics
+ /// If the stream ID is not present.
pub fn remove(&mut self, stream_id: StreamId) {
- match self.vec.binary_search(&stream_id) {
- Ok(pos) => {
- self.vec.remove(pos);
- }
- Err(_) => {
- // element already in vector @ `pos`
- panic!("Missing stream_id {}", stream_id)
- }
- }
+ let Ok(pos) = self.vec.binary_search(&stream_id) else {
+ // element already in vector @ `pos`
+ panic!("Missing stream_id {stream_id}");
+ };
+ self.vec.remove(pos);
}
}
@@ -1579,16 +1699,16 @@ impl SendStreams {
// Iterate the map, but only those without fairness, then iterate
// OrderGroups, then iterate each group
- qdebug!("processing streams... unfair:");
+ qtrace!("processing streams... unfair:");
for stream in self.map.values_mut() {
if !stream.is_fair() {
- qdebug!(" {}", stream);
+ qtrace!(" {}", stream);
if !stream.write_frames_with_early_return(priority, builder, tokens, stats) {
break;
}
}
}
- qdebug!("fair streams:");
+ qtrace!("fair streams:");
let stream_ids = self.regular.iter().chain(
self.sendordered
.values_mut()
@@ -1598,9 +1718,9 @@ impl SendStreams {
for stream_id in stream_ids {
let stream = self.map.get_mut(&stream_id).unwrap();
if let Some(order) = stream.sendorder() {
- qdebug!(" {} ({})", stream_id, order)
+ qtrace!(" {} ({})", stream_id, order);
} else {
- qdebug!(" None")
+ qtrace!(" None");
}
if !stream.write_frames_with_early_return(priority, builder, tokens, stats) {
break;
@@ -1609,7 +1729,7 @@ impl SendStreams {
}
pub fn update_initial_limit(&mut self, remote: &TransportParameters) {
- for (id, ss) in self.map.iter_mut() {
+ for (id, ss) in &mut self.map {
let limit = if id.is_bidi() {
assert!(!id.is_remote_initiated(Role::Client));
remote.get_integer(tparams::INITIAL_MAX_STREAM_DATA_BIDI_REMOTE)
@@ -1640,55 +1760,391 @@ pub struct SendStreamRecoveryToken {
#[cfg(test)]
mod tests {
- use neqo_common::{event::Provider, hex_with_len, qtrace};
-
- use super::*;
- use crate::events::ConnectionEvent;
+ use std::{cell::RefCell, collections::VecDeque, rc::Rc};
+
+ use neqo_common::{event::Provider, hex_with_len, qtrace, Encoder};
+
+ use super::SendStreamRecoveryToken;
+ use crate::{
+ connection::{RetransmissionPriority, TransmissionPriority},
+ events::ConnectionEvent,
+ fc::SenderFlowControl,
+ packet::PacketBuilder,
+ recovery::{RecoveryToken, StreamRecoveryToken},
+ send_stream::{
+ RangeState, RangeTracker, SendStream, SendStreamState, SendStreams, TxBuffer,
+ },
+ stats::FrameStats,
+ ConnectionEvents, StreamId, SEND_BUFFER_SIZE,
+ };
fn connection_fc(limit: u64) -> Rc<RefCell<SenderFlowControl<()>>> {
Rc::new(RefCell::new(SenderFlowControl::new((), limit)))
}
#[test]
- fn test_mark_range() {
+ fn mark_acked_from_zero() {
let mut rt = RangeTracker::default();
// ranges can go from nothing->Sent if queued for retrans and then
// acks arrive
- rt.mark_range(5, 5, RangeState::Acked);
+ rt.mark_acked(5, 5);
assert_eq!(rt.highest_offset(), 10);
assert_eq!(rt.acked_from_zero(), 0);
- rt.mark_range(10, 4, RangeState::Acked);
+ rt.mark_acked(10, 4);
assert_eq!(rt.highest_offset(), 14);
assert_eq!(rt.acked_from_zero(), 0);
- rt.mark_range(0, 5, RangeState::Sent);
+ rt.mark_sent(0, 5);
assert_eq!(rt.highest_offset(), 14);
assert_eq!(rt.acked_from_zero(), 0);
- rt.mark_range(0, 5, RangeState::Acked);
+ rt.mark_acked(0, 5);
assert_eq!(rt.highest_offset(), 14);
assert_eq!(rt.acked_from_zero(), 14);
- rt.mark_range(12, 20, RangeState::Acked);
+ rt.mark_acked(12, 20);
assert_eq!(rt.highest_offset(), 32);
assert_eq!(rt.acked_from_zero(), 32);
// ack the lot
- rt.mark_range(0, 400, RangeState::Acked);
+ rt.mark_acked(0, 400);
assert_eq!(rt.highest_offset(), 400);
assert_eq!(rt.acked_from_zero(), 400);
// acked trumps sent
- rt.mark_range(0, 200, RangeState::Sent);
+ rt.mark_sent(0, 200);
assert_eq!(rt.highest_offset(), 400);
assert_eq!(rt.acked_from_zero(), 400);
}
+ /// Check that `marked_acked` correctly handles all paths.
+ /// ```ignore
+ /// SSS SSSAAASSS
+ /// + AAAAAAAAA
+ /// = SSSAAAAAAAAASS
+ /// ```
+ #[test]
+ fn mark_acked_1() {
+ let mut rt = RangeTracker::default();
+ rt.mark_sent(0, 3);
+ rt.mark_sent(6, 3);
+ rt.mark_acked(9, 3);
+ rt.mark_sent(12, 3);
+
+ rt.mark_acked(3, 10);
+
+ let mut canon = RangeTracker::default();
+ canon.used.insert(0, (3, RangeState::Sent));
+ canon.used.insert(3, (10, RangeState::Acked));
+ canon.used.insert(13, (2, RangeState::Sent));
+ assert_eq!(rt, canon);
+ }
+
+ /// Check that `marked_acked` correctly handles all paths.
+ /// ```ignore
+ /// SSS SSS AAA
+ /// + AAAAAAAAA
+ /// = SSAAAAAAAAAAAA
+ /// ```
+ #[test]
+ fn mark_acked_2() {
+ let mut rt = RangeTracker::default();
+ rt.mark_sent(0, 3);
+ rt.mark_sent(6, 3);
+ rt.mark_acked(12, 3);
+
+ rt.mark_acked(2, 10);
+
+ let mut canon = RangeTracker::default();
+ canon.used.insert(0, (2, RangeState::Sent));
+ canon.used.insert(2, (13, RangeState::Acked));
+ assert_eq!(rt, canon);
+ }
+
+ /// Check that `marked_acked` correctly handles all paths.
+ /// ```ignore
+ /// AASSS AAAA
+ /// + AAAAAAAAA
+ /// = AAAAAAAAAAAA
+ /// ```
+ #[test]
+ fn mark_acked_3() {
+ let mut rt = RangeTracker::default();
+ rt.mark_acked(1, 2);
+ rt.mark_sent(3, 3);
+ rt.mark_acked(8, 4);
+
+ rt.mark_acked(0, 9);
+
+ let canon = RangeTracker {
+ acked: 12,
+ ..RangeTracker::default()
+ };
+ assert_eq!(rt, canon);
+ }
+
+ /// Check that `marked_acked` correctly handles all paths.
+ /// ```ignore
+ /// SSS
+ /// + AAAA
+ /// = AAAASS
+ /// ```
+ #[test]
+ fn mark_acked_4() {
+ let mut rt = RangeTracker::default();
+ rt.mark_sent(3, 3);
+
+ rt.mark_acked(0, 4);
+
+ let mut canon = RangeTracker {
+ acked: 4,
+ ..Default::default()
+ };
+ canon.used.insert(4, (2, RangeState::Sent));
+ assert_eq!(rt, canon);
+ }
+
+ /// Check that `marked_acked` correctly handles all paths.
+ /// ```ignore
+ /// AAAAAASSS
+ /// + AAA
+ /// = AAAAAASSS
+ /// ```
+ #[test]
+ fn mark_acked_5() {
+ let mut rt = RangeTracker::default();
+ rt.mark_acked(0, 6);
+ rt.mark_sent(6, 3);
+
+ rt.mark_acked(3, 3);
+
+ let mut canon = RangeTracker {
+ acked: 6,
+ ..RangeTracker::default()
+ };
+ canon.used.insert(6, (3, RangeState::Sent));
+ assert_eq!(rt, canon);
+ }
+
+ /// Check that `marked_acked` correctly handles all paths.
+ /// ```ignore
+ /// AAA AAA AAA
+ /// + AAAAAAA
+ /// = AAAAAAAAAAAAA
+ /// ```
+ #[test]
+ fn mark_acked_6() {
+ let mut rt = RangeTracker::default();
+ rt.mark_acked(3, 3);
+ rt.mark_acked(8, 3);
+ rt.mark_acked(13, 3);
+
+ rt.mark_acked(6, 7);
+
+ let mut canon = RangeTracker::default();
+ canon.used.insert(3, (13, RangeState::Acked));
+ assert_eq!(rt, canon);
+ }
+
+ /// Check that `marked_acked` correctly handles all paths.
+ /// ```ignore
+ /// AAA AAA
+ /// + AAA
+ /// = AAAAAAAA
+ /// ```
+ #[test]
+ fn mark_acked_7() {
+ let mut rt = RangeTracker::default();
+ rt.mark_acked(3, 3);
+ rt.mark_acked(8, 3);
+
+ rt.mark_acked(6, 3);
+
+ let mut canon = RangeTracker::default();
+ canon.used.insert(3, (8, RangeState::Acked));
+ assert_eq!(rt, canon);
+ }
+
+ /// Check that `marked_acked` correctly handles all paths.
+ /// ```ignore
+ /// SSSSSSSS
+ /// + AAAA
+ /// = SSAAAASS
+ /// ```
+ #[test]
+ fn mark_acked_8() {
+ let mut rt = RangeTracker::default();
+ rt.mark_sent(0, 8);
+
+ rt.mark_acked(2, 4);
+
+ let mut canon = RangeTracker::default();
+ canon.used.insert(0, (2, RangeState::Sent));
+ canon.used.insert(2, (4, RangeState::Acked));
+ canon.used.insert(6, (2, RangeState::Sent));
+ assert_eq!(rt, canon);
+ }
+
+ /// Check that `marked_acked` correctly handles all paths.
+ /// ```ignore
+ /// SSS
+ /// + AAA
+ /// = AAA SSS
+ /// ```
+ #[test]
+ fn mark_acked_9() {
+ let mut rt = RangeTracker::default();
+ rt.mark_sent(5, 3);
+
+ rt.mark_acked(0, 3);
+
+ let mut canon = RangeTracker {
+ acked: 3,
+ ..Default::default()
+ };
+ canon.used.insert(5, (3, RangeState::Sent));
+ assert_eq!(rt, canon);
+ }
+
+ /// Check that `marked_sent` correctly handles all paths.
+ /// ```ignore
+ /// AAA AAA SSS
+ /// + SSSSSSSSSSSS
+ /// = AAASSSAAASSSSSS
+ /// ```
+ #[test]
+ fn mark_sent_1() {
+ let mut rt = RangeTracker::default();
+ rt.mark_acked(0, 3);
+ rt.mark_acked(6, 3);
+ rt.mark_sent(12, 3);
+
+ rt.mark_sent(0, 12);
+
+ let mut canon = RangeTracker {
+ acked: 3,
+ ..RangeTracker::default()
+ };
+ canon.used.insert(3, (3, RangeState::Sent));
+ canon.used.insert(6, (3, RangeState::Acked));
+ canon.used.insert(9, (6, RangeState::Sent));
+ assert_eq!(rt, canon);
+ }
+
+ /// Check that `marked_sent` correctly handles all paths.
+ /// ```ignore
+ /// AAASS AAA S SSSS
+ /// + SSSSSSSSSSSSS
+ /// = AAASSSAAASSSSSSS
+ /// ```
+ #[test]
+ fn mark_sent_2() {
+ let mut rt = RangeTracker::default();
+ rt.mark_acked(0, 3);
+ rt.mark_sent(3, 2);
+ rt.mark_acked(6, 3);
+ rt.mark_sent(10, 1);
+ rt.mark_sent(12, 4);
+
+ rt.mark_sent(0, 13);
+
+ let mut canon = RangeTracker {
+ acked: 3,
+ ..RangeTracker::default()
+ };
+ canon.used.insert(3, (3, RangeState::Sent));
+ canon.used.insert(6, (3, RangeState::Acked));
+ canon.used.insert(9, (7, RangeState::Sent));
+ assert_eq!(rt, canon);
+ }
+
+ /// Check that `marked_sent` correctly handles all paths.
+ /// ```ignore
+ /// AAA AAA
+ /// + SSSS
+ /// = AAASSAAA
+ /// ```
+ #[test]
+ fn mark_sent_3() {
+ let mut rt = RangeTracker::default();
+ rt.mark_acked(0, 3);
+ rt.mark_acked(5, 3);
+
+ rt.mark_sent(2, 4);
+
+ let mut canon = RangeTracker {
+ acked: 3,
+ ..RangeTracker::default()
+ };
+ canon.used.insert(3, (2, RangeState::Sent));
+ canon.used.insert(5, (3, RangeState::Acked));
+ assert_eq!(rt, canon);
+ }
+
+ /// Check that `marked_sent` correctly handles all paths.
+ /// ```ignore
+ /// SSS AAA SS
+ /// + SSSSSSSS
+ /// = SSSSSAAASSSS
+ /// ```
+ #[test]
+ fn mark_sent_4() {
+ let mut rt = RangeTracker::default();
+ rt.mark_sent(0, 3);
+ rt.mark_acked(5, 3);
+ rt.mark_sent(10, 2);
+
+ rt.mark_sent(2, 8);
+
+ let mut canon = RangeTracker::default();
+ canon.used.insert(0, (5, RangeState::Sent));
+ canon.used.insert(5, (3, RangeState::Acked));
+ canon.used.insert(8, (4, RangeState::Sent));
+ assert_eq!(rt, canon);
+ }
+
+ /// Check that `marked_sent` correctly handles all paths.
+ /// ```ignore
+ /// AAA
+ /// + SSSSSS
+ /// = AAASSS
+ /// ```
+ #[test]
+ fn mark_sent_5() {
+ let mut rt = RangeTracker::default();
+ rt.mark_acked(3, 3);
+
+ rt.mark_sent(3, 6);
+
+ let mut canon = RangeTracker::default();
+ canon.used.insert(3, (3, RangeState::Acked));
+ canon.used.insert(6, (3, RangeState::Sent));
+ assert_eq!(rt, canon);
+ }
+
+ /// Check that `marked_sent` correctly handles all paths.
+ /// ```ignore
+ /// SSSSS
+ /// + SSS
+ /// = SSSSS
+ /// ```
+ #[test]
+ fn mark_sent_6() {
+ let mut rt = RangeTracker::default();
+ rt.mark_sent(0, 5);
+
+ rt.mark_sent(1, 3);
+
+ let mut canon = RangeTracker::default();
+ canon.used.insert(0, (5, RangeState::Sent));
+ assert_eq!(rt, canon);
+ }
+
#[test]
fn unmark_sent_start() {
let mut rt = RangeTracker::default();
- rt.mark_range(0, 5, RangeState::Sent);
+ rt.mark_sent(0, 5);
assert_eq!(rt.highest_offset(), 5);
assert_eq!(rt.acked_from_zero(), 0);
@@ -1702,13 +2158,13 @@ mod tests {
fn unmark_sent_middle() {
let mut rt = RangeTracker::default();
- rt.mark_range(0, 5, RangeState::Acked);
+ rt.mark_acked(0, 5);
assert_eq!(rt.highest_offset(), 5);
assert_eq!(rt.acked_from_zero(), 5);
- rt.mark_range(5, 5, RangeState::Sent);
+ rt.mark_sent(5, 5);
assert_eq!(rt.highest_offset(), 10);
assert_eq!(rt.acked_from_zero(), 5);
- rt.mark_range(10, 5, RangeState::Acked);
+ rt.mark_acked(10, 5);
assert_eq!(rt.highest_offset(), 15);
assert_eq!(rt.acked_from_zero(), 5);
assert_eq!(rt.first_unmarked_range(), (15, None));
@@ -1723,10 +2179,10 @@ mod tests {
fn unmark_sent_end() {
let mut rt = RangeTracker::default();
- rt.mark_range(0, 5, RangeState::Acked);
+ rt.mark_acked(0, 5);
assert_eq!(rt.highest_offset(), 5);
assert_eq!(rt.acked_from_zero(), 5);
- rt.mark_range(5, 5, RangeState::Sent);
+ rt.mark_sent(5, 5);
assert_eq!(rt.highest_offset(), 10);
assert_eq!(rt.acked_from_zero(), 5);
assert_eq!(rt.first_unmarked_range(), (10, None));
@@ -1752,11 +2208,11 @@ mod tests {
}
#[test]
- fn test_unmark_range() {
+ fn unmark_range() {
let mut rt = RangeTracker::default();
- rt.mark_range(5, 5, RangeState::Acked);
- rt.mark_range(10, 5, RangeState::Sent);
+ rt.mark_acked(5, 5);
+ rt.mark_sent(10, 5);
// Should unmark sent but not acked range
rt.unmark_range(7, 6);
@@ -1772,11 +2228,11 @@ mod tests {
(&13, &(2, RangeState::Sent))
);
assert!(rt.used.iter().nth(2).is_none());
- rt.mark_range(0, 5, RangeState::Sent);
+ rt.mark_sent(0, 5);
let res = rt.first_unmarked_range();
assert_eq!(res, (10, Some(3)));
- rt.mark_range(10, 3, RangeState::Sent);
+ rt.mark_sent(10, 3);
let res = rt.first_unmarked_range();
assert_eq!(res, (15, None));
@@ -1790,14 +2246,15 @@ mod tests {
assert_eq!(txb.avail(), SEND_BUFFER_SIZE);
// Fill the buffer
- assert_eq!(txb.send(&[1; SEND_BUFFER_SIZE * 2]), SEND_BUFFER_SIZE);
+ let big_buf = vec![1; SEND_BUFFER_SIZE * 2];
+ assert_eq!(txb.send(&big_buf), SEND_BUFFER_SIZE);
assert!(matches!(txb.next_bytes(),
- Some((0, x)) if x.len()==SEND_BUFFER_SIZE
+ Some((0, x)) if x.len() == SEND_BUFFER_SIZE
&& x.iter().all(|ch| *ch == 1)));
// Mark almost all as sent. Get what's left
let one_byte_from_end = SEND_BUFFER_SIZE as u64 - 1;
- txb.mark_as_sent(0, one_byte_from_end as usize);
+ txb.mark_as_sent(0, usize::try_from(one_byte_from_end).unwrap());
assert!(matches!(txb.next_bytes(),
Some((start, x)) if x.len() == 1
&& start == one_byte_from_end
@@ -1826,14 +2283,14 @@ mod tests {
// Contig acked range at start means it can be removed from buffer
// Impl of vecdeque should now result in a split buffer when more data
// is sent
- txb.mark_as_acked(0, five_bytes_from_end as usize);
+ txb.mark_as_acked(0, usize::try_from(five_bytes_from_end).unwrap());
assert_eq!(txb.send(&[2; 30]), 30);
// Just get 5 even though there is more
assert!(matches!(txb.next_bytes(),
Some((start, x)) if x.len() == 5
&& start == five_bytes_from_end
&& x.iter().all(|ch| *ch == 1)));
- assert_eq!(txb.retired, five_bytes_from_end);
+ assert_eq!(txb.retired(), five_bytes_from_end);
assert_eq!(txb.buffered(), 35);
// Marking that bit as sent should let the last contig bit be returned
@@ -1852,7 +2309,8 @@ mod tests {
assert_eq!(txb.avail(), SEND_BUFFER_SIZE);
// Fill the buffer
- assert_eq!(txb.send(&[1; SEND_BUFFER_SIZE * 2]), SEND_BUFFER_SIZE);
+ let big_buf = vec![1; SEND_BUFFER_SIZE * 2];
+ assert_eq!(txb.send(&big_buf), SEND_BUFFER_SIZE);
assert!(matches!(txb.next_bytes(),
Some((0, x)) if x.len()==SEND_BUFFER_SIZE
&& x.iter().all(|ch| *ch == 1)));
@@ -1860,7 +2318,7 @@ mod tests {
// As above
let forty_bytes_from_end = SEND_BUFFER_SIZE as u64 - 40;
- txb.mark_as_acked(0, forty_bytes_from_end as usize);
+ txb.mark_as_acked(0, usize::try_from(forty_bytes_from_end).unwrap());
assert!(matches!(txb.next_bytes(),
Some((start, x)) if x.len() == 40
&& start == forty_bytes_from_end
@@ -1888,7 +2346,7 @@ mod tests {
// Ack entire first slice and into second slice
let ten_bytes_past_end = SEND_BUFFER_SIZE as u64 + 10;
- txb.mark_as_acked(0, ten_bytes_past_end as usize);
+ txb.mark_as_acked(0, usize::try_from(ten_bytes_past_end).unwrap());
// Get up to marked range A
assert!(matches!(txb.next_bytes(),
@@ -1910,7 +2368,7 @@ mod tests {
}
#[test]
- fn test_stream_tx() {
+ fn stream_tx() {
let conn_fc = connection_fc(4096);
let conn_events = ConnectionEvents::default();
@@ -1926,22 +2384,23 @@ mod tests {
}
// Should hit stream flow control limit before filling up send buffer
- let res = s.send(&[4; SEND_BUFFER_SIZE]).unwrap();
+ let big_buf = vec![4; SEND_BUFFER_SIZE + 100];
+ let res = s.send(&big_buf[..SEND_BUFFER_SIZE]).unwrap();
assert_eq!(res, 1024 - 100);
// should do nothing, max stream data already 1024
s.set_max_stream_data(1024);
- let res = s.send(&[4; SEND_BUFFER_SIZE]).unwrap();
+ let res = s.send(&big_buf[..SEND_BUFFER_SIZE]).unwrap();
assert_eq!(res, 0);
// should now hit the conn flow control (4096)
s.set_max_stream_data(1_048_576);
- let res = s.send(&[4; SEND_BUFFER_SIZE]).unwrap();
+ let res = s.send(&big_buf[..SEND_BUFFER_SIZE]).unwrap();
assert_eq!(res, 3072);
// should now hit the tx buffer size
conn_fc.borrow_mut().update(SEND_BUFFER_SIZE as u64);
- let res = s.send(&[4; SEND_BUFFER_SIZE + 100]).unwrap();
+ let res = s.send(&big_buf).unwrap();
assert_eq!(res, SEND_BUFFER_SIZE - 4096);
// TODO(agrover@mozilla.com): test ooo acks somehow
@@ -2012,10 +2471,8 @@ mod tests {
// tx buffer size.
assert_eq!(s.avail(), SEND_BUFFER_SIZE - 4);
- assert_eq!(
- s.send(&[b'a'; SEND_BUFFER_SIZE]).unwrap(),
- SEND_BUFFER_SIZE - 4
- );
+ let big_buf = vec![b'a'; SEND_BUFFER_SIZE];
+ assert_eq!(s.send(&big_buf).unwrap(), SEND_BUFFER_SIZE - 4);
// No event because still blocked by tx buffer full
s.set_max_stream_data(2_000_000_000);
@@ -2395,8 +2852,7 @@ mod tests {
);
let mut send_buf = TxBuffer::new();
- send_buf.retired = u64::try_from(offset).unwrap();
- send_buf.ranges.mark_range(0, offset, RangeState::Acked);
+ send_buf.ranges.mark_acked(0, offset);
let mut fc = SenderFlowControl::new(StreamId::from(stream), MAX_VARINT);
fc.consume(offset);
let conn_fc = Rc::new(RefCell::new(SenderFlowControl::new((), MAX_VARINT)));
diff --git a/third_party/rust/neqo-transport/src/sender.rs b/third_party/rust/neqo-transport/src/sender.rs
index 9a00dfc7a7..3a54851533 100644
--- a/third_party/rust/neqo-transport/src/sender.rs
+++ b/third_party/rust/neqo-transport/src/sender.rs
@@ -5,7 +5,7 @@
// except according to those terms.
// Congestion control
-#![deny(clippy::pedantic)]
+
#![allow(clippy::module_name_repetitions)]
use std::{
diff --git a/third_party/rust/neqo-transport/src/server.rs b/third_party/rust/neqo-transport/src/server.rs
index 12a7d2f9e0..96a6244ef1 100644
--- a/third_party/rust/neqo-transport/src/server.rs
+++ b/third_party/rust/neqo-transport/src/server.rs
@@ -43,7 +43,7 @@ pub enum InitialResult {
Retry(Vec<u8>),
}
-/// MIN_INITIAL_PACKET_SIZE is the smallest packet that can be used to establish
+/// `MIN_INITIAL_PACKET_SIZE` is the smallest packet that can be used to establish
/// a new connection across all QUIC versions this server supports.
const MIN_INITIAL_PACKET_SIZE: usize = 1200;
/// The size of timer buckets. This is higher than the actual timer granularity
@@ -168,7 +168,7 @@ pub struct Server {
/// the same key are routed to the connection that was first accepted.
/// This is cleared out when the connection is closed or established.
active_attempts: HashMap<AttemptKey, StateRef>,
- /// All connections, keyed by ConnectionId.
+ /// All connections, keyed by `ConnectionId`.
connections: ConnectionTableRef,
/// The connections that have new events.
active: HashSet<ActiveConnectionRef>,
@@ -195,6 +195,8 @@ impl Server {
/// OK.
/// * `cid_generator` is responsible for generating connection IDs and parsing them; connection
/// IDs produced by the manager cannot be zero-length.
+ /// # Errors
+ /// When address validation state cannot be created.
pub fn new(
now: Instant,
certs: &[impl AsRef<str>],
@@ -240,6 +242,8 @@ impl Server {
self.ciphers = Vec::from(ciphers.as_ref());
}
+ /// # Errors
+ /// When the configuration is invalid.
pub fn enable_ech(
&mut self,
config: u8,
@@ -251,6 +255,7 @@ impl Server {
Ok(())
}
+ #[must_use]
pub fn ech_config(&self) -> &[u8] {
self.ech_config.as_ref().map_or(&[], |cfg| &cfg.encoded)
}
@@ -262,7 +267,7 @@ impl Server {
fn process_connection(
&mut self,
- c: StateRef,
+ c: &StateRef,
dgram: Option<&Datagram>,
now: Instant,
) -> Option<Datagram> {
@@ -271,24 +276,24 @@ impl Server {
match out {
Output::Datagram(_) => {
qtrace!([self], "Sending packet, added to waiting connections");
- self.waiting.push_back(Rc::clone(&c));
+ self.waiting.push_back(Rc::clone(c));
}
Output::Callback(delay) => {
let next = now + delay;
if next != c.borrow().last_timer {
qtrace!([self], "Change timer to {:?}", next);
- self.remove_timer(&c);
+ self.remove_timer(c);
c.borrow_mut().last_timer = next;
- self.timers.add(next, Rc::clone(&c));
+ self.timers.add(next, Rc::clone(c));
}
}
Output::None => {
- self.remove_timer(&c);
+ self.remove_timer(c);
}
}
if c.borrow().has_events() {
qtrace!([self], "Connection active: {:?}", c);
- self.active.insert(ActiveConnectionRef { c: Rc::clone(&c) });
+ self.active.insert(ActiveConnectionRef { c: Rc::clone(c) });
}
if *c.borrow().state() > State::Handshaking {
@@ -302,13 +307,13 @@ impl Server {
c.borrow_mut().set_qlog(NeqoQlog::disabled());
self.connections
.borrow_mut()
- .retain(|_, v| !Rc::ptr_eq(v, &c));
+ .retain(|_, v| !Rc::ptr_eq(v, c));
}
out.dgram()
}
fn connection(&self, cid: ConnectionIdRef) -> Option<StateRef> {
- self.connections.borrow().get(&cid[..]).map(Rc::clone)
+ self.connections.borrow().get(&cid[..]).cloned()
}
fn handle_initial(
@@ -387,7 +392,7 @@ impl Server {
attempt_key
);
let c = Rc::clone(c);
- self.process_connection(c, Some(dgram), now)
+ self.process_connection(&c, Some(dgram), now)
} else {
self.accept_connection(attempt_key, initial, dgram, orig_dcid, now)
}
@@ -395,9 +400,9 @@ impl Server {
fn create_qlog_trace(&self, odcid: ConnectionIdRef<'_>) -> NeqoQlog {
if let Some(qlog_dir) = &self.qlog_dir {
- let mut qlog_path = qlog_dir.to_path_buf();
+ let mut qlog_path = qlog_dir.clone();
- qlog_path.push(format!("{}.qlog", odcid));
+ qlog_path.push(format!("{odcid}.qlog"));
// The original DCID is chosen by the client. Using create_new()
// prevents attackers from overwriting existing logs.
@@ -456,9 +461,9 @@ impl Server {
}
if let Some(odcid) = orig_dcid {
// There was a retry, so set the connection IDs for.
- c.set_retry_cids(odcid, initial.src_cid, initial.dst_cid);
+ c.set_retry_cids(&odcid, initial.src_cid, &initial.dst_cid);
}
- c.set_validation(Rc::clone(&self.address_validation));
+ c.set_validation(&self.address_validation);
c.set_qlog(self.create_qlog_trace(attempt_key.odcid.as_cid_ref()));
if let Some(cfg) = &self.ech_config {
if c.server_enable_ech(cfg.config, &cfg.public_name, &cfg.sk, &cfg.pk)
@@ -505,10 +510,10 @@ impl Server {
last_timer: now,
active_attempt: Some(attempt_key.clone()),
}));
- cid_mgr.borrow_mut().set_connection(Rc::clone(&c));
+ cid_mgr.borrow_mut().set_connection(&c);
let previous_attempt = self.active_attempts.insert(attempt_key, Rc::clone(&c));
debug_assert!(previous_attempt.is_none());
- self.process_connection(c, Some(dgram), now)
+ self.process_connection(&c, Some(dgram), now)
}
Err(e) => {
qwarn!([self], "Unable to create connection");
@@ -517,7 +522,7 @@ impl Server {
&mut self.create_qlog_trace(attempt_key.odcid.as_cid_ref()),
self.conn_params.get_versions().all(),
initial.version.wire_version(),
- )
+ );
}
None
}
@@ -544,7 +549,7 @@ impl Server {
attempt_key
);
let c = Rc::clone(c);
- self.process_connection(c, Some(dgram), now)
+ self.process_connection(&c, Some(dgram), now)
} else {
qdebug!([self], "Dropping 0-RTT for unknown connection");
None
@@ -564,7 +569,7 @@ impl Server {
// Finding an existing connection. Should be the most common case.
if let Some(c) = self.connection(packet.dcid()) {
- return self.process_connection(c, Some(dgram), now);
+ return self.process_connection(&c, Some(dgram), now);
}
if packet.packet_type() == PacketType::Short {
@@ -637,13 +642,13 @@ impl Server {
fn process_next_output(&mut self, now: Instant) -> Option<Datagram> {
qtrace!([self], "No packet to send, look at waiting connections");
while let Some(c) = self.waiting.pop_front() {
- if let Some(d) = self.process_connection(c, None, now) {
+ if let Some(d) = self.process_connection(&c, None, now) {
return Some(d);
}
}
qtrace!([self], "No packet to send still, run timers");
while let Some(c) = self.timers.take_next(now) {
- if let Some(d) = self.process_connection(c, None, now) {
+ if let Some(d) = self.process_connection(&c, None, now) {
return Some(d);
}
}
@@ -684,7 +689,7 @@ impl Server {
mem::take(&mut self.active).into_iter().collect()
}
- pub fn add_to_waiting(&mut self, c: ActiveConnectionRef) {
+ pub fn add_to_waiting(&mut self, c: &ActiveConnectionRef) {
self.waiting.push_back(c.connection());
}
}
@@ -695,6 +700,7 @@ pub struct ActiveConnectionRef {
}
impl ActiveConnectionRef {
+ #[must_use]
pub fn borrow(&self) -> impl Deref<Target = Connection> + '_ {
std::cell::Ref::map(self.c.borrow(), |c| &c.c)
}
@@ -703,6 +709,7 @@ impl ActiveConnectionRef {
std::cell::RefMut::map(self.c.borrow_mut(), |c| &mut c.c)
}
+ #[must_use]
pub fn connection(&self) -> StateRef {
Rc::clone(&self.c)
}
@@ -731,13 +738,13 @@ struct ServerConnectionIdGenerator {
}
impl ServerConnectionIdGenerator {
- pub fn set_connection(&mut self, c: StateRef) {
+ pub fn set_connection(&mut self, c: &StateRef) {
let saved = std::mem::replace(&mut self.saved_cids, Vec::with_capacity(0));
for cid in saved {
qtrace!("ServerConnectionIdGenerator inserting saved cid {}", cid);
- self.insert_cid(cid, Rc::clone(&c));
+ self.insert_cid(cid, Rc::clone(c));
}
- self.c = Rc::downgrade(&c);
+ self.c = Rc::downgrade(c);
}
fn insert_cid(&mut self, cid: ConnectionId, rc: StateRef) {
diff --git a/third_party/rust/neqo-transport/src/stats.rs b/third_party/rust/neqo-transport/src/stats.rs
index d6c7a911f9..9eff503dcf 100644
--- a/third_party/rust/neqo-transport/src/stats.rs
+++ b/third_party/rust/neqo-transport/src/stats.rs
@@ -5,7 +5,6 @@
// except according to those terms.
// Tracking of some useful statistics.
-#![deny(clippy::pedantic)]
use std::{
cell::RefCell,
diff --git a/third_party/rust/neqo-transport/src/stream_id.rs b/third_party/rust/neqo-transport/src/stream_id.rs
index f3b07b86a8..8dbe2dcfbc 100644
--- a/third_party/rust/neqo-transport/src/stream_id.rs
+++ b/third_party/rust/neqo-transport/src/stream_id.rs
@@ -20,10 +20,12 @@ pub enum StreamType {
pub struct StreamId(u64);
impl StreamId {
+ #[must_use]
pub const fn new(id: u64) -> Self {
Self(id)
}
+ #[must_use]
pub fn init(stream_type: StreamType, role: Role) -> Self {
let type_val = match stream_type {
StreamType::BiDi => 0,
@@ -32,18 +34,22 @@ impl StreamId {
Self(type_val + Self::role_bit(role))
}
+ #[must_use]
pub fn as_u64(self) -> u64 {
self.0
}
+ #[must_use]
pub fn is_bidi(self) -> bool {
self.as_u64() & 0x02 == 0
}
+ #[must_use]
pub fn is_uni(self) -> bool {
!self.is_bidi()
}
+ #[must_use]
pub fn stream_type(self) -> StreamType {
if self.is_bidi() {
StreamType::BiDi
@@ -52,14 +58,17 @@ impl StreamId {
}
}
+ #[must_use]
pub fn is_client_initiated(self) -> bool {
self.as_u64() & 0x01 == 0
}
+ #[must_use]
pub fn is_server_initiated(self) -> bool {
!self.is_client_initiated()
}
+ #[must_use]
pub fn role(self) -> Role {
if self.is_client_initiated() {
Role::Client
@@ -68,6 +77,7 @@ impl StreamId {
}
}
+ #[must_use]
pub fn is_self_initiated(self, my_role: Role) -> bool {
match my_role {
Role::Client if self.is_client_initiated() => true,
@@ -76,14 +86,17 @@ impl StreamId {
}
}
+ #[must_use]
pub fn is_remote_initiated(self, my_role: Role) -> bool {
!self.is_self_initiated(my_role)
}
+ #[must_use]
pub fn is_send_only(self, my_role: Role) -> bool {
self.is_uni() && self.is_self_initiated(my_role)
}
+ #[must_use]
pub fn is_recv_only(self, my_role: Role) -> bool {
self.is_uni() && self.is_remote_initiated(my_role)
}
@@ -93,6 +106,7 @@ impl StreamId {
}
/// This returns a bit that is shared by all streams created by this role.
+ #[must_use]
pub fn role_bit(role: Role) -> u64 {
match role {
Role::Server => 1,
diff --git a/third_party/rust/neqo-transport/src/streams.rs b/third_party/rust/neqo-transport/src/streams.rs
index 7cbb29ce02..d8662afa3b 100644
--- a/third_party/rust/neqo-transport/src/streams.rs
+++ b/third_party/rust/neqo-transport/src/streams.rs
@@ -95,6 +95,7 @@ impl Streams {
}
}
+ #[must_use]
pub fn is_stream_id_allowed(&self, stream_id: StreamId) -> bool {
self.remote_stream_limits[stream_id.stream_type()].is_allowed(stream_id)
}
@@ -118,7 +119,9 @@ impl Streams {
self.local_stream_limits = LocalStreamLimits::new(self.role);
}
- pub fn input_frame(&mut self, frame: Frame, stats: &mut FrameStats) -> Res<()> {
+ /// # Errors
+ /// When the frame is invalid.
+ pub fn input_frame(&mut self, frame: &Frame, stats: &mut FrameStats) -> Res<()> {
match frame {
Frame::ResetStream {
stream_id,
@@ -126,8 +129,8 @@ impl Streams {
final_size,
} => {
stats.reset_stream += 1;
- if let (_, Some(rs)) = self.obtain_stream(stream_id)? {
- rs.reset(application_error_code, final_size)?;
+ if let (_, Some(rs)) = self.obtain_stream(*stream_id)? {
+ rs.reset(*application_error_code, *final_size)?;
}
}
Frame::StopSending {
@@ -136,9 +139,9 @@ impl Streams {
} => {
stats.stop_sending += 1;
self.events
- .send_stream_stop_sending(stream_id, application_error_code);
- if let (Some(ss), _) = self.obtain_stream(stream_id)? {
- ss.reset(application_error_code);
+ .send_stream_stop_sending(*stream_id, *application_error_code);
+ if let (Some(ss), _) = self.obtain_stream(*stream_id)? {
+ ss.reset(*application_error_code);
}
}
Frame::Stream {
@@ -149,13 +152,13 @@ impl Streams {
..
} => {
stats.stream += 1;
- if let (_, Some(rs)) = self.obtain_stream(stream_id)? {
- rs.inbound_stream_frame(fin, offset, data)?;
+ if let (_, Some(rs)) = self.obtain_stream(*stream_id)? {
+ rs.inbound_stream_frame(*fin, *offset, data)?;
}
}
Frame::MaxData { maximum_data } => {
stats.max_data += 1;
- self.handle_max_data(maximum_data);
+ self.handle_max_data(*maximum_data);
}
Frame::MaxStreamData {
stream_id,
@@ -163,12 +166,12 @@ impl Streams {
} => {
qtrace!(
"Stream {} Received MaxStreamData {}",
- stream_id,
- maximum_stream_data
+ *stream_id,
+ *maximum_stream_data
);
stats.max_stream_data += 1;
- if let (Some(ss), _) = self.obtain_stream(stream_id)? {
- ss.set_max_stream_data(maximum_stream_data);
+ if let (Some(ss), _) = self.obtain_stream(*stream_id)? {
+ ss.set_max_stream_data(*maximum_stream_data);
}
}
Frame::MaxStreams {
@@ -176,7 +179,7 @@ impl Streams {
maximum_streams,
} => {
stats.max_streams += 1;
- self.handle_max_streams(stream_type, maximum_streams);
+ self.handle_max_streams(*stream_type, *maximum_streams);
}
Frame::DataBlocked { data_limit } => {
// Should never happen since we set data limit to max
@@ -193,7 +196,7 @@ impl Streams {
return Err(Error::StreamStateError);
}
- if let (_, Some(rs)) = self.obtain_stream(stream_id)? {
+ if let (_, Some(rs)) = self.obtain_stream(*stream_id)? {
rs.send_flowc_update();
}
}
@@ -401,6 +404,8 @@ impl Streams {
/// Get or make a stream, and implicitly open additional streams as
/// indicated by its stream id.
+ /// # Errors
+ /// When the stream cannot be created due to stream limits.
pub fn obtain_stream(
&mut self,
stream_id: StreamId,
@@ -412,14 +417,20 @@ impl Streams {
))
}
+ /// # Errors
+ /// When the stream does not exist.
pub fn set_sendorder(&mut self, stream_id: StreamId, sendorder: Option<SendOrder>) -> Res<()> {
self.send.set_sendorder(stream_id, sendorder)
}
+ /// # Errors
+ /// When the stream does not exist.
pub fn set_fairness(&mut self, stream_id: StreamId, fairness: bool) -> Res<()> {
self.send.set_fairness(stream_id, fairness)
}
+ /// # Errors
+ /// When a stream cannot be created, which might be temporary.
pub fn stream_create(&mut self, st: StreamType) -> Res<StreamId> {
match self.local_stream_limits.take_stream_id(st) {
None => Err(Error::StreamLimitError),
@@ -525,18 +536,26 @@ impl Streams {
}
}
+ /// # Errors
+ /// When the stream does not exist.
pub fn get_send_stream_mut(&mut self, stream_id: StreamId) -> Res<&mut SendStream> {
self.send.get_mut(stream_id)
}
+ /// # Errors
+ /// When the stream does not exist.
pub fn get_send_stream(&self, stream_id: StreamId) -> Res<&SendStream> {
self.send.get(stream_id)
}
+ /// # Errors
+ /// When the stream does not exist.
pub fn get_recv_stream_mut(&mut self, stream_id: StreamId) -> Res<&mut RecvStream> {
self.recv.get_mut(stream_id)
}
+ /// # Errors
+ /// When the stream does not exist.
pub fn keep_alive(&mut self, stream_id: StreamId, keep: bool) -> Res<()> {
self.recv.keep_alive(stream_id, keep)
}
diff --git a/third_party/rust/neqo-transport/src/tparams.rs b/third_party/rust/neqo-transport/src/tparams.rs
index 1297829094..eada56cc4c 100644
--- a/third_party/rust/neqo-transport/src/tparams.rs
+++ b/third_party/rust/neqo-transport/src/tparams.rs
@@ -9,7 +9,6 @@
use std::{
cell::RefCell,
collections::HashMap,
- convert::TryFrom,
net::{Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6},
rc::Rc,
};
@@ -88,6 +87,8 @@ impl PreferredAddress {
}
/// A generic version of `new()` for testing.
+ /// # Panics
+ /// When the addresses are the wrong type.
#[must_use]
#[cfg(test)]
pub fn new_any(v4: Option<std::net::SocketAddr>, v6: Option<std::net::SocketAddr>) -> Self {
@@ -231,7 +232,7 @@ impl TransportParameter {
if v == 0 {
Err(Error::TransportParameterError)
} else {
- Ok(v as WireVersion)
+ Ok(WireVersion::try_from(v)?)
}
}
@@ -353,6 +354,9 @@ impl TransportParameters {
}
// Get an integer type or a default.
+ /// # Panics
+ /// When the transport parameter isn't recognized as being an integer.
+ #[must_use]
pub fn get_integer(&self, tp: TransportParameterId) -> u64 {
let default = match tp {
IDLE_TIMEOUT
@@ -378,6 +382,8 @@ impl TransportParameters {
}
// Set an integer type or a default.
+ /// # Panics
+ /// When the transport parameter isn't recognized as being an integer.
pub fn set_integer(&mut self, tp: TransportParameterId, value: u64) {
match tp {
IDLE_TIMEOUT
@@ -399,6 +405,9 @@ impl TransportParameters {
}
}
+ /// # Panics
+ /// When the transport parameter isn't recognized as containing bytes.
+ #[must_use]
pub fn get_bytes(&self, tp: TransportParameterId) -> Option<&[u8]> {
match tp {
ORIGINAL_DESTINATION_CONNECTION_ID
@@ -415,6 +424,8 @@ impl TransportParameters {
}
}
+ /// # Panics
+ /// When the transport parameter isn't recognized as containing bytes.
pub fn set_bytes(&mut self, tp: TransportParameterId, value: Vec<u8>) {
match tp {
ORIGINAL_DESTINATION_CONNECTION_ID
@@ -427,6 +438,8 @@ impl TransportParameters {
}
}
+ /// # Panics
+ /// When the transport parameter isn't recognized as being empty.
pub fn set_empty(&mut self, tp: TransportParameterId) {
match tp {
DISABLE_MIGRATION | GREASE_QUIC_BIT => {
@@ -437,11 +450,14 @@ impl TransportParameters {
}
/// Set version information.
+ /// # Panics
+ /// Never. But rust doesn't know that.
pub fn set_versions(&mut self, role: Role, versions: &VersionConfig) {
- let rbuf = random(4);
+ let rbuf = random::<4>();
let mut other = Vec::with_capacity(versions.all().len() + 1);
let mut dec = Decoder::new(&rbuf);
- let grease = (dec.decode_uint(4).unwrap() as u32) & 0xf0f0_f0f0 | 0x0a0a_0a0a;
+ let grease =
+ (u32::try_from(dec.decode_uint(4).unwrap()).unwrap()) & 0xf0f0_f0f0 | 0x0a0a_0a0a;
other.push(grease);
for &v in versions.all() {
if role == Role::Client && !versions.initial().is_compatible(v) {
@@ -467,6 +483,10 @@ impl TransportParameters {
}
}
+ /// # Panics
+ /// When the indicated transport parameter is present but NOT empty.
+ /// This should not happen if the parsing code in `TransportParameter::decode` is correct.
+ #[must_use]
pub fn get_empty(&self, tipe: TransportParameterId) -> bool {
match self.params.get(&tipe) {
None => false,
@@ -568,6 +588,7 @@ pub struct TransportParametersHandler {
}
impl TransportParametersHandler {
+ #[must_use]
pub fn new(role: Role, versions: VersionConfig) -> Self {
let mut local = TransportParameters::default();
local.set_versions(role, &versions);
@@ -588,6 +609,10 @@ impl TransportParametersHandler {
self.local.set_versions(self.role, &self.versions);
}
+ /// # Panics
+ /// When this function is called before the peer has provided transport parameters.
+ /// Do not call this function if you are not also able to send data.
+ #[must_use]
pub fn remote(&self) -> &TransportParameters {
match (self.remote.as_ref(), self.remote_0rtt.as_ref()) {
(Some(tp), _) | (_, Some(tp)) => tp,
@@ -596,6 +621,7 @@ impl TransportParametersHandler {
}
/// Get the version as set (or as determined by a compatible upgrade).
+ #[must_use]
pub fn version(&self) -> Version {
self.versions.initial()
}
@@ -749,7 +775,24 @@ where
#[cfg(test)]
#[allow(unused_variables)]
mod tests {
- use super::*;
+ use std::net::{Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6};
+
+ use neqo_common::{Decoder, Encoder};
+
+ use super::PreferredAddress;
+ use crate::{
+ tparams::{
+ TransportParameter, TransportParameterId, TransportParameters,
+ ACTIVE_CONNECTION_ID_LIMIT, IDLE_TIMEOUT, INITIAL_MAX_DATA, INITIAL_MAX_STREAMS_BIDI,
+ INITIAL_MAX_STREAMS_UNI, INITIAL_MAX_STREAM_DATA_BIDI_LOCAL,
+ INITIAL_MAX_STREAM_DATA_BIDI_REMOTE, INITIAL_MAX_STREAM_DATA_UNI,
+ INITIAL_SOURCE_CONNECTION_ID, MAX_ACK_DELAY, MAX_DATAGRAM_FRAME_SIZE,
+ MAX_UDP_PAYLOAD_SIZE, MIN_ACK_DELAY, ORIGINAL_DESTINATION_CONNECTION_ID,
+ PREFERRED_ADDRESS, RETRY_SOURCE_CONNECTION_ID, STATELESS_RESET_TOKEN,
+ VERSION_INFORMATION,
+ },
+ ConnectionId, Error, Version,
+ };
#[test]
fn basic_tps() {
@@ -843,7 +886,7 @@ mod tests {
/// This takes a `TransportParameter::PreferredAddress` that has been mutilated.
/// It then encodes it, working from the knowledge that the `encode` function
/// doesn't care about validity, and decodes it. The result should be failure.
- fn assert_invalid_spa(spa: TransportParameter) {
+ fn assert_invalid_spa(spa: &TransportParameter) {
let mut enc = Encoder::new();
spa.encode(&mut enc, PREFERRED_ADDRESS);
assert_eq!(
@@ -853,40 +896,40 @@ mod tests {
}
/// This is for those rare mutations that are acceptable.
- fn assert_valid_spa(spa: TransportParameter) {
+ fn assert_valid_spa(spa: &TransportParameter) {
let mut enc = Encoder::new();
spa.encode(&mut enc, PREFERRED_ADDRESS);
let mut dec = enc.as_decoder();
let (id, decoded) = TransportParameter::decode(&mut dec).unwrap().unwrap();
assert_eq!(id, PREFERRED_ADDRESS);
- assert_eq!(decoded, spa);
+ assert_eq!(&decoded, spa);
}
#[test]
fn preferred_address_zero_address() {
// Either port being zero is bad.
- assert_invalid_spa(mutate_spa(|v4, _, _| {
+ assert_invalid_spa(&mutate_spa(|v4, _, _| {
v4.as_mut().unwrap().set_port(0);
}));
- assert_invalid_spa(mutate_spa(|_, v6, _| {
+ assert_invalid_spa(&mutate_spa(|_, v6, _| {
v6.as_mut().unwrap().set_port(0);
}));
// Either IP being zero is bad.
- assert_invalid_spa(mutate_spa(|v4, _, _| {
+ assert_invalid_spa(&mutate_spa(|v4, _, _| {
v4.as_mut().unwrap().set_ip(Ipv4Addr::from(0));
}));
- assert_invalid_spa(mutate_spa(|_, v6, _| {
+ assert_invalid_spa(&mutate_spa(|_, v6, _| {
v6.as_mut().unwrap().set_ip(Ipv6Addr::from(0));
}));
// Either address being absent is OK.
- assert_valid_spa(mutate_spa(|v4, _, _| {
+ assert_valid_spa(&mutate_spa(|v4, _, _| {
*v4 = None;
}));
- assert_valid_spa(mutate_spa(|_, v6, _| {
+ assert_valid_spa(&mutate_spa(|_, v6, _| {
*v6 = None;
}));
// Both addresses being absent is bad.
- assert_invalid_spa(mutate_spa(|v4, v6, _| {
+ assert_invalid_spa(&mutate_spa(|v4, v6, _| {
*v4 = None;
*v6 = None;
}));
@@ -894,10 +937,10 @@ mod tests {
#[test]
fn preferred_address_bad_cid() {
- assert_invalid_spa(mutate_spa(|_, _, cid| {
+ assert_invalid_spa(&mutate_spa(|_, _, cid| {
*cid = ConnectionId::from(&[]);
}));
- assert_invalid_spa(mutate_spa(|_, _, cid| {
+ assert_invalid_spa(&mutate_spa(|_, _, cid| {
*cid = ConnectionId::from(&[0x0c; 21]);
}));
}
@@ -975,7 +1018,6 @@ mod tests {
#[test]
fn compatible_0rtt_integers() {
- let mut tps_a = TransportParameters::default();
const INTEGER_KEYS: &[TransportParameterId] = &[
INITIAL_MAX_DATA,
INITIAL_MAX_STREAM_DATA_BIDI_LOCAL,
@@ -987,6 +1029,8 @@ mod tests {
MIN_ACK_DELAY,
MAX_DATAGRAM_FRAME_SIZE,
];
+
+ let mut tps_a = TransportParameters::default();
for i in INTEGER_KEYS {
tps_a.set(*i, TransportParameter::Integer(12));
}
diff --git a/third_party/rust/neqo-transport/src/tracking.rs b/third_party/rust/neqo-transport/src/tracking.rs
index 64d00257d3..bdd0f250c7 100644
--- a/third_party/rust/neqo-transport/src/tracking.rs
+++ b/third_party/rust/neqo-transport/src/tracking.rs
@@ -6,12 +6,9 @@
// Tracking of received packets and generating acks thereof.
-#![deny(clippy::pedantic)]
-
use std::{
cmp::min,
collections::VecDeque,
- convert::TryFrom,
ops::{Index, IndexMut},
time::{Duration, Instant},
};
@@ -746,8 +743,8 @@ impl Default for AckTracker {
mod tests {
use std::collections::HashSet;
- use lazy_static::lazy_static;
use neqo_common::Encoder;
+ use test_fixture::now;
use super::{
AckTracker, Duration, Instant, PacketNumberSpace, PacketNumberSpaceSet, RecoveryToken,
@@ -760,16 +757,13 @@ mod tests {
};
const RTT: Duration = Duration::from_millis(100);
- lazy_static! {
- static ref NOW: Instant = Instant::now();
- }
fn test_ack_range(pns: &[PacketNumber], nranges: usize) {
let mut rp = RecvdPackets::new(PacketNumberSpace::Initial); // Any space will do.
let mut packets = HashSet::new();
for pn in pns {
- rp.set_received(*NOW, *pn, true);
+ rp.set_received(now(), *pn, true);
packets.insert(*pn);
}
@@ -824,7 +818,7 @@ mod tests {
// This will add one too many disjoint ranges.
for i in 0..=MAX_TRACKED_RANGES {
- rp.set_received(*NOW, (i * 2) as u64, true);
+ rp.set_received(now(), (i * 2) as u64, true);
}
assert_eq!(rp.ranges.len(), MAX_TRACKED_RANGES);
@@ -843,22 +837,22 @@ mod tests {
// Only application data packets are delayed.
let mut rp = RecvdPackets::new(PacketNumberSpace::ApplicationData);
assert!(rp.ack_time().is_none());
- assert!(!rp.ack_now(*NOW, RTT));
+ assert!(!rp.ack_now(now(), RTT));
rp.ack_freq(0, COUNT, DELAY, false);
// Some packets won't cause an ACK to be needed.
for i in 0..COUNT {
- rp.set_received(*NOW, i, true);
- assert_eq!(Some(*NOW + DELAY), rp.ack_time());
- assert!(!rp.ack_now(*NOW, RTT));
- assert!(rp.ack_now(*NOW + DELAY, RTT));
+ rp.set_received(now(), i, true);
+ assert_eq!(Some(now() + DELAY), rp.ack_time());
+ assert!(!rp.ack_now(now(), RTT));
+ assert!(rp.ack_now(now() + DELAY, RTT));
}
// Exceeding COUNT will move the ACK time to now.
- rp.set_received(*NOW, COUNT, true);
- assert_eq!(Some(*NOW), rp.ack_time());
- assert!(rp.ack_now(*NOW, RTT));
+ rp.set_received(now(), COUNT, true);
+ assert_eq!(Some(now()), rp.ack_time());
+ assert!(rp.ack_now(now(), RTT));
}
#[test]
@@ -866,12 +860,12 @@ mod tests {
for space in &[PacketNumberSpace::Initial, PacketNumberSpace::Handshake] {
let mut rp = RecvdPackets::new(*space);
assert!(rp.ack_time().is_none());
- assert!(!rp.ack_now(*NOW, RTT));
+ assert!(!rp.ack_now(now(), RTT));
// Any packet in these spaces is acknowledged straight away.
- rp.set_received(*NOW, 0, true);
- assert_eq!(Some(*NOW), rp.ack_time());
- assert!(rp.ack_now(*NOW, RTT));
+ rp.set_received(now(), 0, true);
+ assert_eq!(Some(now()), rp.ack_time());
+ assert!(rp.ack_now(now(), RTT));
}
}
@@ -879,12 +873,12 @@ mod tests {
fn ooo_no_ack_delay_new() {
let mut rp = RecvdPackets::new(PacketNumberSpace::ApplicationData);
assert!(rp.ack_time().is_none());
- assert!(!rp.ack_now(*NOW, RTT));
+ assert!(!rp.ack_now(now(), RTT));
// Anything other than packet 0 is acknowledged immediately.
- rp.set_received(*NOW, 1, true);
- assert_eq!(Some(*NOW), rp.ack_time());
- assert!(rp.ack_now(*NOW, RTT));
+ rp.set_received(now(), 1, true);
+ assert_eq!(Some(now()), rp.ack_time());
+ assert!(rp.ack_now(now(), RTT));
}
fn write_frame_at(rp: &mut RecvdPackets, now: Instant) {
@@ -897,37 +891,37 @@ mod tests {
}
fn write_frame(rp: &mut RecvdPackets) {
- write_frame_at(rp, *NOW);
+ write_frame_at(rp, now());
}
#[test]
fn ooo_no_ack_delay_fill() {
let mut rp = RecvdPackets::new(PacketNumberSpace::ApplicationData);
- rp.set_received(*NOW, 1, true);
+ rp.set_received(now(), 1, true);
write_frame(&mut rp);
// Filling in behind the largest acknowledged causes immediate ACK.
- rp.set_received(*NOW, 0, true);
+ rp.set_received(now(), 0, true);
write_frame(&mut rp);
// Receiving the next packet won't elicit an ACK.
- rp.set_received(*NOW, 2, true);
- assert!(!rp.ack_now(*NOW, RTT));
+ rp.set_received(now(), 2, true);
+ assert!(!rp.ack_now(now(), RTT));
}
#[test]
fn immediate_ack_after_rtt() {
let mut rp = RecvdPackets::new(PacketNumberSpace::ApplicationData);
- rp.set_received(*NOW, 1, true);
+ rp.set_received(now(), 1, true);
write_frame(&mut rp);
// Filling in behind the largest acknowledged causes immediate ACK.
- rp.set_received(*NOW, 0, true);
+ rp.set_received(now(), 0, true);
write_frame(&mut rp);
// A new packet ordinarily doesn't result in an ACK, but this time it does.
- rp.set_received(*NOW + RTT, 2, true);
- write_frame_at(&mut rp, *NOW + RTT);
+ rp.set_received(now() + RTT, 2, true);
+ write_frame_at(&mut rp, now() + RTT);
}
#[test]
@@ -937,29 +931,29 @@ mod tests {
// Set tolerance to 2 and then it takes three packets.
rp.ack_freq(0, 2, Duration::from_millis(10), true);
- rp.set_received(*NOW, 1, true);
- assert_ne!(Some(*NOW), rp.ack_time());
- rp.set_received(*NOW, 2, true);
- assert_ne!(Some(*NOW), rp.ack_time());
- rp.set_received(*NOW, 3, true);
- assert_eq!(Some(*NOW), rp.ack_time());
+ rp.set_received(now(), 1, true);
+ assert_ne!(Some(now()), rp.ack_time());
+ rp.set_received(now(), 2, true);
+ assert_ne!(Some(now()), rp.ack_time());
+ rp.set_received(now(), 3, true);
+ assert_eq!(Some(now()), rp.ack_time());
}
#[test]
fn ooo_no_ack_delay_threshold_gap() {
let mut rp = RecvdPackets::new(PacketNumberSpace::ApplicationData);
- rp.set_received(*NOW, 1, true);
+ rp.set_received(now(), 1, true);
write_frame(&mut rp);
// Set tolerance to 2 and then it takes three packets.
rp.ack_freq(0, 2, Duration::from_millis(10), true);
- rp.set_received(*NOW, 3, true);
- assert_ne!(Some(*NOW), rp.ack_time());
- rp.set_received(*NOW, 4, true);
- assert_ne!(Some(*NOW), rp.ack_time());
- rp.set_received(*NOW, 5, true);
- assert_eq!(Some(*NOW), rp.ack_time());
+ rp.set_received(now(), 3, true);
+ assert_ne!(Some(now()), rp.ack_time());
+ rp.set_received(now(), 4, true);
+ assert_ne!(Some(now()), rp.ack_time());
+ rp.set_received(now(), 5, true);
+ assert_eq!(Some(now()), rp.ack_time());
}
/// Test that an in-order packet that is not ack-eliciting doesn't
@@ -970,13 +964,13 @@ mod tests {
rp.ack_freq(0, 1, Duration::from_millis(10), true);
// This should be ignored.
- rp.set_received(*NOW, 0, false);
- assert_ne!(Some(*NOW), rp.ack_time());
+ rp.set_received(now(), 0, false);
+ assert_ne!(Some(now()), rp.ack_time());
// Skip 1 (it has no effect).
- rp.set_received(*NOW, 2, true);
- assert_ne!(Some(*NOW), rp.ack_time());
- rp.set_received(*NOW, 3, true);
- assert_eq!(Some(*NOW), rp.ack_time());
+ rp.set_received(now(), 2, true);
+ assert_ne!(Some(now()), rp.ack_time());
+ rp.set_received(now(), 3, true);
+ assert_eq!(Some(now()), rp.ack_time());
}
/// If a packet that is not ack-eliciting is reordered, that's fine too.
@@ -986,16 +980,16 @@ mod tests {
rp.ack_freq(0, 1, Duration::from_millis(10), false);
// These are out of order, but they are not ack-eliciting.
- rp.set_received(*NOW, 1, false);
- assert_ne!(Some(*NOW), rp.ack_time());
- rp.set_received(*NOW, 0, false);
- assert_ne!(Some(*NOW), rp.ack_time());
+ rp.set_received(now(), 1, false);
+ assert_ne!(Some(now()), rp.ack_time());
+ rp.set_received(now(), 0, false);
+ assert_ne!(Some(now()), rp.ack_time());
// These are in order.
- rp.set_received(*NOW, 2, true);
- assert_ne!(Some(*NOW), rp.ack_time());
- rp.set_received(*NOW, 3, true);
- assert_eq!(Some(*NOW), rp.ack_time());
+ rp.set_received(now(), 2, true);
+ assert_ne!(Some(now()), rp.ack_time());
+ rp.set_received(now(), 3, true);
+ assert_eq!(Some(now()), rp.ack_time());
}
#[test]
@@ -1007,23 +1001,23 @@ mod tests {
tracker
.get_mut(PacketNumberSpace::Handshake)
.unwrap()
- .set_received(*NOW, 0, false);
- assert_eq!(None, tracker.ack_time(*NOW));
+ .set_received(now(), 0, false);
+ assert_eq!(None, tracker.ack_time(now()));
// This should be delayed.
tracker
.get_mut(PacketNumberSpace::ApplicationData)
.unwrap()
- .set_received(*NOW, 0, true);
- assert_eq!(Some(*NOW + DELAY), tracker.ack_time(*NOW));
+ .set_received(now(), 0, true);
+ assert_eq!(Some(now() + DELAY), tracker.ack_time(now()));
// This should move the time forward.
- let later = *NOW + (DELAY / 2);
+ let later = now() + (DELAY / 2);
tracker
.get_mut(PacketNumberSpace::Initial)
.unwrap()
.set_received(later, 0, true);
- assert_eq!(Some(later), tracker.ack_time(*NOW));
+ assert_eq!(Some(later), tracker.ack_time(now()));
}
#[test]
@@ -1047,17 +1041,17 @@ mod tests {
tracker
.get_mut(PacketNumberSpace::Initial)
.unwrap()
- .set_received(*NOW, 0, true);
+ .set_received(now(), 0, true);
// The reference time for `ack_time` has to be in the past or we filter out the timer.
assert!(tracker
- .ack_time(NOW.checked_sub(Duration::from_millis(1)).unwrap())
+ .ack_time(now().checked_sub(Duration::from_millis(1)).unwrap())
.is_some());
let mut tokens = Vec::new();
let mut stats = FrameStats::default();
tracker.write_frame(
PacketNumberSpace::Initial,
- *NOW,
+ now(),
RTT,
&mut builder,
&mut tokens,
@@ -1069,9 +1063,9 @@ mod tests {
tracker
.get_mut(PacketNumberSpace::Initial)
.unwrap()
- .set_received(*NOW, 1, true);
+ .set_received(now(), 1, true);
assert!(tracker
- .ack_time(NOW.checked_sub(Duration::from_millis(1)).unwrap())
+ .ack_time(now().checked_sub(Duration::from_millis(1)).unwrap())
.is_some());
// Now drop that space.
@@ -1079,11 +1073,11 @@ mod tests {
assert!(tracker.get_mut(PacketNumberSpace::Initial).is_none());
assert!(tracker
- .ack_time(NOW.checked_sub(Duration::from_millis(1)).unwrap())
+ .ack_time(now().checked_sub(Duration::from_millis(1)).unwrap())
.is_none());
tracker.write_frame(
PacketNumberSpace::Initial,
- *NOW,
+ now(),
RTT,
&mut builder,
&mut tokens,
@@ -1103,9 +1097,9 @@ mod tests {
tracker
.get_mut(PacketNumberSpace::Initial)
.unwrap()
- .set_received(*NOW, 0, true);
+ .set_received(now(), 0, true);
assert!(tracker
- .ack_time(NOW.checked_sub(Duration::from_millis(1)).unwrap())
+ .ack_time(now().checked_sub(Duration::from_millis(1)).unwrap())
.is_some());
let mut builder = PacketBuilder::short(Encoder::new(), false, []);
@@ -1114,7 +1108,7 @@ mod tests {
let mut stats = FrameStats::default();
tracker.write_frame(
PacketNumberSpace::Initial,
- *NOW,
+ now(),
RTT,
&mut builder,
&mut Vec::new(),
@@ -1130,13 +1124,13 @@ mod tests {
tracker
.get_mut(PacketNumberSpace::Initial)
.unwrap()
- .set_received(*NOW, 0, true);
+ .set_received(now(), 0, true);
tracker
.get_mut(PacketNumberSpace::Initial)
.unwrap()
- .set_received(*NOW, 2, true);
+ .set_received(now(), 2, true);
assert!(tracker
- .ack_time(NOW.checked_sub(Duration::from_millis(1)).unwrap())
+ .ack_time(now().checked_sub(Duration::from_millis(1)).unwrap())
.is_some());
let mut builder = PacketBuilder::short(Encoder::new(), false, []);
@@ -1145,7 +1139,7 @@ mod tests {
let mut stats = FrameStats::default();
tracker.write_frame(
PacketNumberSpace::Initial,
- *NOW,
+ now(),
RTT,
&mut builder,
&mut Vec::new(),
@@ -1168,19 +1162,19 @@ mod tests {
let mut tracker = AckTracker::default();
// While we have multiple PN spaces, we ignore ACK timers from the past.
- // Send out of order to cause the delayed ack timer to be set to `*NOW`.
+ // Send out of order to cause the delayed ack timer to be set to `now()`.
tracker
.get_mut(PacketNumberSpace::ApplicationData)
.unwrap()
- .set_received(*NOW, 3, true);
- assert!(tracker.ack_time(*NOW + Duration::from_millis(1)).is_none());
+ .set_received(now(), 3, true);
+ assert!(tracker.ack_time(now() + Duration::from_millis(1)).is_none());
// When we are reduced to one space, that filter is off.
tracker.drop_space(PacketNumberSpace::Initial);
tracker.drop_space(PacketNumberSpace::Handshake);
assert_eq!(
- tracker.ack_time(*NOW + Duration::from_millis(1)),
- Some(*NOW)
+ tracker.ack_time(now() + Duration::from_millis(1)),
+ Some(now())
);
}
diff --git a/third_party/rust/neqo-transport/src/version.rs b/third_party/rust/neqo-transport/src/version.rs
index 13db0bf024..eee598fdd0 100644
--- a/third_party/rust/neqo-transport/src/version.rs
+++ b/third_party/rust/neqo-transport/src/version.rs
@@ -4,17 +4,16 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-use std::convert::TryFrom;
-
use neqo_common::qdebug;
use crate::{Error, Res};
pub type WireVersion = u32;
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Version {
Version2,
+ #[default]
Version1,
Draft29,
Draft30,
@@ -23,6 +22,7 @@ pub enum Version {
}
impl Version {
+ #[must_use]
pub const fn wire_version(self) -> WireVersion {
match self {
Self::Version2 => 0x6b33_43cf,
@@ -94,6 +94,7 @@ impl Version {
}
/// Determine if `self` can be upgraded to `other` compatibly.
+ #[must_use]
pub fn is_compatible(self, other: Self) -> bool {
self == other
|| matches!(
@@ -102,6 +103,7 @@ impl Version {
)
}
+ #[must_use]
pub fn all() -> Vec<Self> {
vec![
Self::Version2,
@@ -121,12 +123,6 @@ impl Version {
}
}
-impl Default for Version {
- fn default() -> Self {
- Self::Version1
- }
-}
-
impl TryFrom<WireVersion> for Version {
type Error = Error;
@@ -176,15 +172,20 @@ pub struct VersionConfig {
}
impl VersionConfig {
+ /// # Panics
+ /// When `all` does not include `initial`.
+ #[must_use]
pub fn new(initial: Version, all: Vec<Version>) -> Self {
assert!(all.contains(&initial));
Self { initial, all }
}
+ #[must_use]
pub fn initial(&self) -> Version {
self.initial
}
+ #[must_use]
pub fn all(&self) -> &[Version] {
&self.all
}
diff --git a/third_party/rust/neqo-transport/tests/common/mod.rs b/third_party/rust/neqo-transport/tests/common/mod.rs
index a43f91e3fe..faff216eb9 100644
--- a/third_party/rust/neqo-transport/tests/common/mod.rs
+++ b/third_party/rust/neqo-transport/tests/common/mod.rs
@@ -4,11 +4,9 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-#![cfg_attr(feature = "deny-warnings", deny(warnings))]
-#![warn(clippy::pedantic)]
#![allow(unused)]
-use std::{cell::RefCell, convert::TryFrom, mem, ops::Range, rc::Rc};
+use std::{cell::RefCell, mem, ops::Range, rc::Rc};
use neqo_common::{event::Provider, hex_with_len, qtrace, Datagram, Decoder, Role};
use neqo_crypto::{
@@ -21,7 +19,7 @@ use neqo_transport::{
server::{ActiveConnectionRef, Server, ValidateAddress},
Connection, ConnectionEvent, ConnectionParameters, State,
};
-use test_fixture::{self, default_client, now, CountingConnectionIdGenerator};
+use test_fixture::{default_client, now, CountingConnectionIdGenerator};
/// Create a server. This is different than the one in the fixture, which is a single connection.
pub fn new_server(params: ConnectionParameters) -> Server {
diff --git a/third_party/rust/neqo-transport/tests/conn_vectors.rs b/third_party/rust/neqo-transport/tests/conn_vectors.rs
index 91dbbf31cc..f478883075 100644
--- a/third_party/rust/neqo-transport/tests/conn_vectors.rs
+++ b/third_party/rust/neqo-transport/tests/conn_vectors.rs
@@ -5,7 +5,7 @@
// except according to those terms.
// Tests with the test vectors from the spec.
-#![deny(clippy::pedantic)]
+
#![cfg(not(feature = "fuzzing"))]
use std::{cell::RefCell, rc::Rc};
@@ -13,7 +13,7 @@ use std::{cell::RefCell, rc::Rc};
use neqo_transport::{
Connection, ConnectionParameters, RandomConnectionIdGenerator, State, Version,
};
-use test_fixture::{self, datagram, now};
+use test_fixture::{datagram, now};
const INITIAL_PACKET_V2: &[u8] = &[
0xd7, 0x6b, 0x33, 0x43, 0xcf, 0x08, 0x83, 0x94, 0xc8, 0xf0, 0x3e, 0x51, 0x57, 0x08, 0x00, 0x00,
diff --git a/third_party/rust/neqo-transport/tests/connection.rs b/third_party/rust/neqo-transport/tests/connection.rs
index 4cbf57f405..0b91fcf306 100644
--- a/third_party/rust/neqo-transport/tests/connection.rs
+++ b/third_party/rust/neqo-transport/tests/connection.rs
@@ -4,19 +4,14 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-#![cfg_attr(feature = "deny-warnings", deny(warnings))]
-#![warn(clippy::use_self)]
-
mod common;
-use std::convert::TryFrom;
-
use common::{
apply_header_protection, decode_initial_header, initial_aead_and_hp, remove_header_protection,
};
use neqo_common::{Datagram, Decoder, Encoder, Role};
use neqo_transport::{ConnectionError, ConnectionParameters, Error, State, Version};
-use test_fixture::{self, default_client, default_server, new_client, now, split_datagram};
+use test_fixture::{default_client, default_server, new_client, now, split_datagram};
#[test]
fn connect() {
@@ -133,6 +128,7 @@ fn reorder_server_initial() {
}
/// Overflow the crypto buffer.
+#[allow(clippy::similar_names)] // For ..._scid and ..._dcid, which are fine.
#[test]
fn overflow_crypto() {
let mut client = new_client(
diff --git a/third_party/rust/neqo-transport/tests/network.rs b/third_party/rust/neqo-transport/tests/network.rs
index 8c388457c5..27e5a83cd6 100644
--- a/third_party/rust/neqo-transport/tests/network.rs
+++ b/third_party/rust/neqo-transport/tests/network.rs
@@ -4,18 +4,17 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-#![cfg_attr(feature = "deny-warnings", deny(warnings))]
-#![warn(clippy::pedantic)]
-
-mod sim;
-
use std::{ops::Range, time::Duration};
use neqo_transport::{ConnectionError, ConnectionParameters, Error, State};
-use sim::{
- connection::{ConnectionNode, ReachState, ReceiveData, SendData},
- network::{Delay, Drop, TailDrop},
- Simulator,
+use test_fixture::{
+ boxed,
+ sim::{
+ connection::{ConnectionNode, ReachState, ReceiveData, SendData},
+ network::{Delay, Drop, TailDrop},
+ Simulator,
+ },
+ simulate,
};
/// The amount of transfer. Much more than this takes a surprising amount of time.
@@ -32,26 +31,28 @@ const fn weeks(m: u32) -> Duration {
simulate!(
connect_direct,
[
- ConnectionNode::default_client(boxed![ReachState::new(State::Confirmed)]),
- ConnectionNode::default_server(boxed![ReachState::new(State::Confirmed)]),
+ ConnectionNode::new_client(
+ ConnectionParameters::default(),
+ [],
+ boxed![ReachState::new(State::Confirmed)]
+ ),
+ ConnectionNode::new_server(
+ ConnectionParameters::default(),
+ [],
+ boxed![ReachState::new(State::Confirmed)]
+ ),
]
);
simulate!(
idle_timeout,
[
- ConnectionNode::default_client(boxed![
- ReachState::new(State::Confirmed),
- ReachState::new(State::Closed(ConnectionError::Transport(
- Error::IdleTimeout
- )))
- ]),
- ConnectionNode::default_server(boxed![
- ReachState::new(State::Confirmed),
- ReachState::new(State::Closed(ConnectionError::Transport(
- Error::IdleTimeout
- )))
- ]),
+ ConnectionNode::default_client(boxed![ReachState::new(State::Closed(
+ ConnectionError::Transport(Error::IdleTimeout)
+ ))]),
+ ConnectionNode::default_server(boxed![ReachState::new(State::Closed(
+ ConnectionError::Transport(Error::IdleTimeout)
+ ))]),
]
);
@@ -60,23 +61,19 @@ simulate!(
[
ConnectionNode::new_client(
ConnectionParameters::default().idle_timeout(weeks(1000)),
- boxed![
- ReachState::new(State::Confirmed),
- ReachState::new(State::Closed(ConnectionError::Transport(
- Error::IdleTimeout
- )))
- ]
+ boxed![ReachState::new(State::Confirmed),],
+ boxed![ReachState::new(State::Closed(ConnectionError::Transport(
+ Error::IdleTimeout
+ )))]
),
Delay::new(weeks(6)..weeks(6)),
Drop::percentage(10),
ConnectionNode::new_server(
ConnectionParameters::default().idle_timeout(weeks(1000)),
- boxed![
- ReachState::new(State::Confirmed),
- ReachState::new(State::Closed(ConnectionError::Transport(
- Error::IdleTimeout
- )))
- ]
+ boxed![ReachState::new(State::Confirmed),],
+ boxed![ReachState::new(State::Closed(ConnectionError::Transport(
+ Error::IdleTimeout
+ )))]
),
Delay::new(weeks(8)..weeks(8)),
Drop::percentage(10),
@@ -94,9 +91,17 @@ simulate!(
simulate!(
connect_fixed_rtt,
[
- ConnectionNode::default_client(boxed![ReachState::new(State::Confirmed)]),
+ ConnectionNode::new_client(
+ ConnectionParameters::default(),
+ [],
+ boxed![ReachState::new(State::Confirmed)]
+ ),
Delay::new(DELAY..DELAY),
- ConnectionNode::default_server(boxed![ReachState::new(State::Confirmed)]),
+ ConnectionNode::new_server(
+ ConnectionParameters::default(),
+ [],
+ boxed![ReachState::new(State::Confirmed)]
+ ),
Delay::new(DELAY..DELAY),
],
);
@@ -104,22 +109,38 @@ simulate!(
simulate!(
connect_taildrop_jitter,
[
- ConnectionNode::default_client(boxed![ReachState::new(State::Confirmed)]),
- TailDrop::dsl_uplink(),
- Delay::new(ZERO..JITTER),
- ConnectionNode::default_server(boxed![ReachState::new(State::Confirmed)]),
+ ConnectionNode::new_client(
+ ConnectionParameters::default(),
+ [],
+ boxed![ReachState::new(State::Confirmed)]
+ ),
TailDrop::dsl_downlink(),
Delay::new(ZERO..JITTER),
+ ConnectionNode::new_server(
+ ConnectionParameters::default(),
+ [],
+ boxed![ReachState::new(State::Confirmed)]
+ ),
+ TailDrop::dsl_uplink(),
+ Delay::new(ZERO..JITTER),
],
);
simulate!(
connect_taildrop,
[
- ConnectionNode::default_client(boxed![ReachState::new(State::Confirmed)]),
- TailDrop::dsl_uplink(),
- ConnectionNode::default_server(boxed![ReachState::new(State::Confirmed)]),
+ ConnectionNode::new_client(
+ ConnectionParameters::default(),
+ [],
+ boxed![ReachState::new(State::Confirmed)]
+ ),
TailDrop::dsl_downlink(),
+ ConnectionNode::new_server(
+ ConnectionParameters::default(),
+ [],
+ boxed![ReachState::new(State::Confirmed)]
+ ),
+ TailDrop::dsl_uplink(),
],
);
@@ -139,9 +160,9 @@ simulate!(
transfer_taildrop,
[
ConnectionNode::default_client(boxed![SendData::new(TRANSFER_AMOUNT)]),
- TailDrop::dsl_uplink(),
- ConnectionNode::default_server(boxed![ReceiveData::new(TRANSFER_AMOUNT)]),
TailDrop::dsl_downlink(),
+ ConnectionNode::default_server(boxed![ReceiveData::new(TRANSFER_AMOUNT)]),
+ TailDrop::dsl_uplink(),
],
);
@@ -149,10 +170,10 @@ simulate!(
transfer_taildrop_jitter,
[
ConnectionNode::default_client(boxed![SendData::new(TRANSFER_AMOUNT)]),
- TailDrop::dsl_uplink(),
+ TailDrop::dsl_downlink(),
Delay::new(ZERO..JITTER),
ConnectionNode::default_server(boxed![ReceiveData::new(TRANSFER_AMOUNT)]),
- TailDrop::dsl_downlink(),
+ TailDrop::dsl_uplink(),
Delay::new(ZERO..JITTER),
],
);
diff --git a/third_party/rust/neqo-transport/tests/retry.rs b/third_party/rust/neqo-transport/tests/retry.rs
index 93759c7df9..e583fcae0f 100644
--- a/third_party/rust/neqo-transport/tests/retry.rs
+++ b/third_party/rust/neqo-transport/tests/retry.rs
@@ -4,14 +4,11 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-#![cfg_attr(feature = "deny-warnings", deny(warnings))]
-#![warn(clippy::pedantic)]
#![cfg(not(feature = "fuzzing"))]
mod common;
use std::{
- convert::TryFrom,
mem,
net::{IpAddr, Ipv4Addr, SocketAddr},
time::Duration,
@@ -24,7 +21,7 @@ use common::{
use neqo_common::{hex_with_len, qdebug, qtrace, Datagram, Encoder, Role};
use neqo_crypto::AuthenticationStatus;
use neqo_transport::{server::ValidateAddress, ConnectionError, Error, State, StreamType};
-use test_fixture::{self, assertions, datagram, default_client, now, split_datagram};
+use test_fixture::{assertions, datagram, default_client, now, split_datagram};
#[test]
fn retry_basic() {
diff --git a/third_party/rust/neqo-transport/tests/server.rs b/third_party/rust/neqo-transport/tests/server.rs
index d6c9c2df95..7388e0fee7 100644
--- a/third_party/rust/neqo-transport/tests/server.rs
+++ b/third_party/rust/neqo-transport/tests/server.rs
@@ -4,12 +4,9 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-#![cfg_attr(feature = "deny-warnings", deny(warnings))]
-#![warn(clippy::pedantic)]
-
mod common;
-use std::{cell::RefCell, convert::TryFrom, mem, net::SocketAddr, rc::Rc, time::Duration};
+use std::{cell::RefCell, mem, net::SocketAddr, rc::Rc, time::Duration};
use common::{
apply_header_protection, connect, connected_server, decode_initial_header, default_server,
@@ -24,7 +21,7 @@ use neqo_transport::{
Connection, ConnectionError, ConnectionParameters, Error, Output, State, StreamType, Version,
};
use test_fixture::{
- self, assertions, datagram, default_client, new_client, now, split_datagram,
+ assertions, datagram, default_client, new_client, now, split_datagram,
CountingConnectionIdGenerator,
};
diff --git a/third_party/rust/neqo-transport/tests/sim/connection.rs b/third_party/rust/neqo-transport/tests/sim/connection.rs
deleted file mode 100644
index 45a5234512..0000000000
--- a/third_party/rust/neqo-transport/tests/sim/connection.rs
+++ /dev/null
@@ -1,315 +0,0 @@
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-#![allow(clippy::module_name_repetitions)]
-
-use std::{
- cmp::min,
- fmt::{self, Debug},
- time::Instant,
-};
-
-use neqo_common::{event::Provider, qdebug, qtrace, Datagram};
-use neqo_crypto::AuthenticationStatus;
-use neqo_transport::{
- Connection, ConnectionEvent, ConnectionParameters, Output, State, StreamId, StreamType,
-};
-
-use super::{Node, Rng};
-
-/// The status of the processing of an event.
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub enum GoalStatus {
- /// The event didn't result in doing anything; the goal is waiting for something.
- Waiting,
- /// An action was taken as a result of the event.
- Active,
- /// The goal was accomplished.
- Done,
-}
-
-/// A goal for the connection.
-/// Goals can be accomplished in any order.
-pub trait ConnectionGoal {
- fn init(&mut self, _c: &mut Connection, _now: Instant) {}
- /// Perform some processing.
- fn process(&mut self, _c: &mut Connection, _now: Instant) -> GoalStatus {
- GoalStatus::Waiting
- }
- /// Handle an event from the provided connection, returning `true` when the
- /// goal is achieved.
- fn handle_event(&mut self, c: &mut Connection, e: &ConnectionEvent, now: Instant)
- -> GoalStatus;
-}
-
-pub struct ConnectionNode {
- c: Connection,
- goals: Vec<Box<dyn ConnectionGoal>>,
-}
-
-impl ConnectionNode {
- pub fn new_client(
- params: ConnectionParameters,
- goals: impl IntoIterator<Item = Box<dyn ConnectionGoal>>,
- ) -> Self {
- Self {
- c: test_fixture::new_client(params),
- goals: goals.into_iter().collect(),
- }
- }
-
- pub fn new_server(
- params: ConnectionParameters,
- goals: impl IntoIterator<Item = Box<dyn ConnectionGoal>>,
- ) -> Self {
- Self {
- c: test_fixture::new_server(test_fixture::DEFAULT_ALPN, params),
- goals: goals.into_iter().collect(),
- }
- }
-
- pub fn default_client(goals: impl IntoIterator<Item = Box<dyn ConnectionGoal>>) -> Self {
- Self::new_client(ConnectionParameters::default(), goals)
- }
-
- pub fn default_server(goals: impl IntoIterator<Item = Box<dyn ConnectionGoal>>) -> Self {
- Self::new_server(ConnectionParameters::default(), goals)
- }
-
- #[allow(dead_code)]
- pub fn clear_goals(&mut self) {
- self.goals.clear();
- }
-
- #[allow(dead_code)]
- pub fn add_goal(&mut self, goal: Box<dyn ConnectionGoal>) {
- self.goals.push(goal);
- }
-
- /// Process all goals using the given closure and return whether any were active.
- fn process_goals<F>(&mut self, mut f: F) -> bool
- where
- F: FnMut(&mut Box<dyn ConnectionGoal>, &mut Connection) -> GoalStatus,
- {
- // Waiting on drain_filter...
- // self.goals.drain_filter(|g| f(g, &mut self.c, &e)).count();
- let mut active = false;
- let mut i = 0;
- while i < self.goals.len() {
- let status = f(&mut self.goals[i], &mut self.c);
- if status == GoalStatus::Done {
- self.goals.remove(i);
- active = true;
- } else {
- active |= status == GoalStatus::Active;
- i += 1;
- }
- }
- active
- }
-}
-
-impl Node for ConnectionNode {
- fn init(&mut self, _rng: Rng, now: Instant) {
- for g in &mut self.goals {
- g.init(&mut self.c, now);
- }
- }
-
- fn process(&mut self, mut d: Option<Datagram>, now: Instant) -> Output {
- _ = self.process_goals(|goal, c| goal.process(c, now));
- loop {
- let res = self.c.process(d.take().as_ref(), now);
-
- let mut active = false;
- while let Some(e) = self.c.next_event() {
- qtrace!([self.c], "received event {:?}", e);
-
- // Perform authentication automatically.
- if matches!(e, ConnectionEvent::AuthenticationNeeded) {
- self.c.authenticated(AuthenticationStatus::Ok, now);
- }
-
- active |= self.process_goals(|goal, c| goal.handle_event(c, &e, now));
- }
- // Exit at this point if the connection produced a datagram.
- // We also exit if none of the goals were active, as there is
- // no point trying again if they did nothing.
- if matches!(res, Output::Datagram(_)) || !active {
- return res;
- }
- qdebug!([self.c], "no datagram and goal activity, looping");
- }
- }
-
- fn done(&self) -> bool {
- self.goals.is_empty()
- }
-
- fn print_summary(&self, test_name: &str) {
- println!("{}: {:?}", test_name, self.c.stats());
- }
-}
-
-impl Debug for ConnectionNode {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- fmt::Display::fmt(&self.c, f)
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct ReachState {
- target: State,
-}
-
-impl ReachState {
- pub fn new(target: State) -> Self {
- Self { target }
- }
-}
-
-impl ConnectionGoal for ReachState {
- fn handle_event(
- &mut self,
- _c: &mut Connection,
- e: &ConnectionEvent,
- _now: Instant,
- ) -> GoalStatus {
- if matches!(e, ConnectionEvent::StateChange(state) if *state == self.target) {
- GoalStatus::Done
- } else {
- GoalStatus::Waiting
- }
- }
-}
-
-#[derive(Debug)]
-pub struct SendData {
- remaining: usize,
- stream_id: Option<StreamId>,
-}
-
-impl SendData {
- pub fn new(amount: usize) -> Self {
- Self {
- remaining: amount,
- stream_id: None,
- }
- }
-
- fn make_stream(&mut self, c: &mut Connection) {
- if self.stream_id.is_none() {
- if let Ok(stream_id) = c.stream_create(StreamType::UniDi) {
- qdebug!([c], "made stream {} for sending", stream_id);
- self.stream_id = Some(stream_id);
- }
- }
- }
-
- fn send(&mut self, c: &mut Connection, stream_id: StreamId) -> GoalStatus {
- const DATA: &[u8] = &[0; 4096];
- let mut status = GoalStatus::Waiting;
- loop {
- let end = min(self.remaining, DATA.len());
- let sent = c.stream_send(stream_id, &DATA[..end]).unwrap();
- if sent == 0 {
- return status;
- }
- self.remaining -= sent;
- qtrace!("sent {} remaining {}", sent, self.remaining);
- if self.remaining == 0 {
- c.stream_close_send(stream_id).unwrap();
- return GoalStatus::Done;
- }
- status = GoalStatus::Active;
- }
- }
-}
-
-impl ConnectionGoal for SendData {
- fn init(&mut self, c: &mut Connection, _now: Instant) {
- self.make_stream(c);
- }
-
- fn process(&mut self, c: &mut Connection, _now: Instant) -> GoalStatus {
- self.stream_id
- .map_or(GoalStatus::Waiting, |stream_id| self.send(c, stream_id))
- }
-
- fn handle_event(
- &mut self,
- c: &mut Connection,
- e: &ConnectionEvent,
- _now: Instant,
- ) -> GoalStatus {
- match e {
- ConnectionEvent::SendStreamCreatable {
- stream_type: StreamType::UniDi,
- }
- // TODO(mt): remove the second condition when #842 is fixed.
- | ConnectionEvent::StateChange(_) => {
- self.make_stream(c);
- GoalStatus::Active
- }
-
- ConnectionEvent::SendStreamWritable { stream_id }
- if Some(*stream_id) == self.stream_id =>
- {
- self.send(c, *stream_id)
- }
-
- // If we sent data in 0-RTT, then we didn't track how much we should
- // have sent. This is trivial to fix if 0-RTT testing is ever needed.
- ConnectionEvent::ZeroRttRejected => panic!("not supported"),
- _ => GoalStatus::Waiting,
- }
- }
-}
-
-/// Receive a prescribed amount of data from any stream.
-#[derive(Debug)]
-pub struct ReceiveData {
- remaining: usize,
-}
-
-impl ReceiveData {
- pub fn new(amount: usize) -> Self {
- Self { remaining: amount }
- }
-
- fn recv(&mut self, c: &mut Connection, stream_id: StreamId) -> GoalStatus {
- let mut buf = vec![0; 4096];
- let mut status = GoalStatus::Waiting;
- loop {
- let end = min(self.remaining, buf.len());
- let (recvd, _) = c.stream_recv(stream_id, &mut buf[..end]).unwrap();
- qtrace!("received {} remaining {}", recvd, self.remaining);
- if recvd == 0 {
- return status;
- }
- self.remaining -= recvd;
- if self.remaining == 0 {
- return GoalStatus::Done;
- }
- status = GoalStatus::Active;
- }
- }
-}
-
-impl ConnectionGoal for ReceiveData {
- fn handle_event(
- &mut self,
- c: &mut Connection,
- e: &ConnectionEvent,
- _now: Instant,
- ) -> GoalStatus {
- if let ConnectionEvent::RecvStreamReadable { stream_id } = e {
- self.recv(c, *stream_id)
- } else {
- GoalStatus::Waiting
- }
- }
-}
diff --git a/third_party/rust/neqo-transport/tests/sim/delay.rs b/third_party/rust/neqo-transport/tests/sim/delay.rs
deleted file mode 100644
index 34cb923084..0000000000
--- a/third_party/rust/neqo-transport/tests/sim/delay.rs
+++ /dev/null
@@ -1,102 +0,0 @@
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-#![allow(clippy::module_name_repetitions)]
-
-use std::{
- collections::BTreeMap,
- convert::TryFrom,
- fmt::{self, Debug},
- ops::Range,
- time::{Duration, Instant},
-};
-
-use neqo_common::Datagram;
-use neqo_transport::Output;
-
-use super::{Node, Rng};
-
-/// An iterator that shares a `Random` instance and produces uniformly
-/// random `Duration`s within a specified range.
-pub struct RandomDelay {
- start: Duration,
- max: u64,
- rng: Option<Rng>,
-}
-
-impl RandomDelay {
- /// Make a new random `Duration` generator. This panics if the range provided
- /// is inverted (i.e., `bounds.start > bounds.end`), or spans 2^64
- /// or more nanoseconds.
- /// A zero-length range means that random values won't be taken from the Rng
- pub fn new(bounds: Range<Duration>) -> Self {
- let max = u64::try_from((bounds.end - bounds.start).as_nanos()).unwrap();
- Self {
- start: bounds.start,
- max,
- rng: None,
- }
- }
-
- pub fn set_rng(&mut self, rng: Rng) {
- self.rng = Some(rng);
- }
-
- pub fn next(&mut self) -> Duration {
- let mut rng = self.rng.as_ref().unwrap().borrow_mut();
- let r = rng.random_from(0..self.max);
- self.start + Duration::from_nanos(r)
- }
-}
-
-pub struct Delay {
- random: RandomDelay,
- queue: BTreeMap<Instant, Datagram>,
-}
-
-impl Delay {
- pub fn new(bounds: Range<Duration>) -> Self {
- Self {
- random: RandomDelay::new(bounds),
- queue: BTreeMap::default(),
- }
- }
-
- fn insert(&mut self, d: Datagram, now: Instant) {
- let mut t = now + self.random.next();
- while self.queue.contains_key(&t) {
- // This is a little inefficient, but it avoids drops on collisions,
- // which are super-common for a fixed delay.
- t += Duration::from_nanos(1);
- }
- self.queue.insert(t, d);
- }
-}
-
-impl Node for Delay {
- fn init(&mut self, rng: Rng, _now: Instant) {
- self.random.set_rng(rng);
- }
-
- fn process(&mut self, d: Option<Datagram>, now: Instant) -> Output {
- if let Some(dgram) = d {
- self.insert(dgram, now);
- }
- if let Some((&k, _)) = self.queue.range(..=now).next() {
- Output::Datagram(self.queue.remove(&k).unwrap())
- } else if let Some(&t) = self.queue.keys().next() {
- Output::Callback(t - now)
- } else {
- Output::None
- }
- }
-}
-
-impl Debug for Delay {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- f.write_str("delay")
- }
-}
diff --git a/third_party/rust/neqo-transport/tests/sim/drop.rs b/third_party/rust/neqo-transport/tests/sim/drop.rs
deleted file mode 100644
index 629fbf48d3..0000000000
--- a/third_party/rust/neqo-transport/tests/sim/drop.rs
+++ /dev/null
@@ -1,75 +0,0 @@
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-#![allow(clippy::module_name_repetitions)]
-
-use std::{
- fmt::{self, Debug},
- time::Instant,
-};
-
-use neqo_common::{qtrace, Datagram};
-use neqo_transport::Output;
-
-use super::{Node, Rng};
-
-/// A random dropper.
-pub struct Drop {
- threshold: u64,
- max: u64,
- rng: Option<Rng>,
-}
-
-impl Drop {
- /// Make a new random drop generator. Each `drop` is called, this generates a
- /// random value between 0 and `max` (exclusive). If this value is less than
- /// `threshold` a value of `true` is returned.
- pub fn new(threshold: u64, max: u64) -> Self {
- Self {
- threshold,
- max,
- rng: None,
- }
- }
-
- /// Generate random drops with the given percentage.
- pub fn percentage(pct: u8) -> Self {
- // Multiply by 10 so that the random number generator works more efficiently.
- Self::new(u64::from(pct) * 10, 1000)
- }
-
- pub fn drop(&mut self) -> bool {
- let mut rng = self.rng.as_ref().unwrap().borrow_mut();
- let r = rng.random_from(0..self.max);
- r < self.threshold
- }
-}
-
-impl Node for Drop {
- fn init(&mut self, rng: Rng, _now: Instant) {
- self.rng = Some(rng);
- }
-
- // Pass any datagram provided directly out, but drop some of them.
- fn process(&mut self, d: Option<Datagram>, _now: Instant) -> Output {
- if let Some(dgram) = d {
- if self.drop() {
- qtrace!("drop {}", dgram.len());
- Output::None
- } else {
- Output::Datagram(dgram)
- }
- } else {
- Output::None
- }
- }
-}
-
-impl Debug for Drop {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- f.write_str("drop")
- }
-}
diff --git a/third_party/rust/neqo-transport/tests/sim/mod.rs b/third_party/rust/neqo-transport/tests/sim/mod.rs
deleted file mode 100644
index 9ab9d57a4a..0000000000
--- a/third_party/rust/neqo-transport/tests/sim/mod.rs
+++ /dev/null
@@ -1,232 +0,0 @@
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-// Tests with simulated network
-#![cfg_attr(feature = "deny-warnings", deny(warnings))]
-#![warn(clippy::pedantic)]
-
-pub mod connection;
-mod delay;
-mod drop;
-pub mod rng;
-mod taildrop;
-
-use std::{
- cell::RefCell,
- cmp::min,
- convert::TryFrom,
- fmt::Debug,
- rc::Rc,
- time::{Duration, Instant},
-};
-
-use neqo_common::{qdebug, qinfo, qtrace, Datagram, Encoder};
-use neqo_transport::Output;
-use rng::Random;
-use test_fixture::{self, now};
-use NodeState::{Active, Idle, Waiting};
-
-pub mod network {
- pub use super::{delay::Delay, drop::Drop, taildrop::TailDrop};
-}
-
-type Rng = Rc<RefCell<Random>>;
-
-/// A macro that turns a list of values into boxed versions of the same.
-#[macro_export]
-macro_rules! boxed {
- [$($v:expr),+ $(,)?] => {
- vec![ $( Box::new($v) as _ ),+ ]
- };
-}
-
-/// Create a simulation test case. This takes either two or three arguments.
-/// The two argument form takes a bare name (`ident`), a comma, and an array of
-/// items that implement `Node`.
-/// The three argument form adds a setup block that can be used to construct a
-/// complex value that is then shared between all nodes. The values in the
-/// three-argument form have to be closures (or functions) that accept a reference
-/// to the value returned by the setup.
-#[macro_export]
-macro_rules! simulate {
- ($n:ident, [ $($v:expr),+ $(,)? ] $(,)?) => {
- simulate!($n, (), [ $(|_| $v),+ ]);
- };
- ($n:ident, $setup:expr, [ $( $v:expr ),+ $(,)? ] $(,)?) => {
- #[test]
- fn $n() {
- let fixture = $setup;
- let mut nodes: Vec<Box<dyn $crate::sim::Node>> = Vec::new();
- $(
- let f: Box<dyn FnOnce(&_) -> _> = Box::new($v);
- nodes.push(Box::new(f(&fixture)));
- )*
- let mut sim = Simulator::new(stringify!($n), nodes);
- if let Ok(seed) = std::env::var("SIMULATION_SEED") {
- sim.seed_str(seed);
- }
- sim.run();
- }
- };
-}
-
-pub trait Node: Debug {
- fn init(&mut self, _rng: Rng, _now: Instant) {}
- /// Perform processing. This optionally takes a datagram and produces either
- /// another data, a time that the simulator needs to wait, or nothing.
- fn process(&mut self, d: Option<Datagram>, now: Instant) -> Output;
- /// An node can report when it considers itself "done".
- fn done(&self) -> bool {
- true
- }
- fn print_summary(&self, _test_name: &str) {}
-}
-
-/// The state of a single node. Nodes will be activated if they are `Active`
-/// or if the previous node in the loop generated a datagram. Nodes that return
-/// `true` from `Node::done` will be activated as normal.
-#[derive(Debug, PartialEq)]
-enum NodeState {
- /// The node just produced a datagram. It should be activated again as soon as possible.
- Active,
- /// The node is waiting.
- Waiting(Instant),
- /// The node became idle.
- Idle,
-}
-
-#[derive(Debug)]
-struct NodeHolder {
- node: Box<dyn Node>,
- state: NodeState,
-}
-
-impl NodeHolder {
- fn ready(&self, now: Instant) -> bool {
- match self.state {
- Active => true,
- Waiting(t) => t >= now,
- Idle => false,
- }
- }
-}
-
-pub struct Simulator {
- name: String,
- nodes: Vec<NodeHolder>,
- rng: Rng,
-}
-
-impl Simulator {
- pub fn new(name: impl AsRef<str>, nodes: impl IntoIterator<Item = Box<dyn Node>>) -> Self {
- let name = String::from(name.as_ref());
- // The first node is marked as Active, the rest are idle.
- let mut it = nodes.into_iter();
- let nodes = it
- .next()
- .map(|node| NodeHolder {
- node,
- state: Active,
- })
- .into_iter()
- .chain(it.map(|node| NodeHolder { node, state: Idle }))
- .collect::<Vec<_>>();
- Self {
- name,
- nodes,
- rng: Rc::default(),
- }
- }
-
- pub fn seed(&mut self, seed: [u8; 32]) {
- self.rng = Rc::new(RefCell::new(Random::new(seed)));
- }
-
- /// Seed from a hex string.
- /// Though this is convenient, it panics if this isn't a 64 character hex string.
- pub fn seed_str(&mut self, seed: impl AsRef<str>) {
- let seed = Encoder::from_hex(seed);
- self.seed(<[u8; 32]>::try_from(seed.as_ref()).unwrap());
- }
-
- fn next_time(&self, now: Instant) -> Instant {
- let mut next = None;
- for n in &self.nodes {
- match n.state {
- Idle => continue,
- Active => return now,
- Waiting(a) => next = Some(next.map_or(a, |b| min(a, b))),
- }
- }
- next.expect("a node cannot be idle and not done")
- }
-
- /// Runs the simulation.
- pub fn run(mut self) -> Duration {
- let start = now();
- let mut now = start;
- let mut dgram = None;
-
- for n in &mut self.nodes {
- n.node.init(self.rng.clone(), now);
- }
- println!("{}: seed {}", self.name, self.rng.borrow().seed_str());
-
- let real_start = Instant::now();
- loop {
- for n in &mut self.nodes {
- if dgram.is_none() && !n.ready(now) {
- qdebug!([self.name], "skipping {:?}", n.node);
- continue;
- }
-
- qdebug!([self.name], "processing {:?}", n.node);
- let res = n.node.process(dgram.take(), now);
- n.state = match res {
- Output::Datagram(d) => {
- qtrace!([self.name], " => datagram {}", d.len());
- dgram = Some(d);
- Active
- }
- Output::Callback(delay) => {
- qtrace!([self.name], " => callback {:?}", delay);
- assert_ne!(delay, Duration::new(0, 0));
- Waiting(now + delay)
- }
- Output::None => {
- qtrace!([self.name], " => nothing");
- assert!(n.node.done(), "nodes have to be done when they go idle");
- Idle
- }
- };
- }
-
- if self.nodes.iter().all(|n| n.node.done()) {
- let real_elapsed = real_start.elapsed();
- println!("{}: real elapsed time: {:?}", self.name, real_elapsed);
- let elapsed = now - start;
- println!("{}: simulated elapsed time: {:?}", self.name, elapsed);
- for n in &self.nodes {
- n.node.print_summary(&self.name);
- }
- return elapsed;
- }
-
- if dgram.is_none() {
- let next = self.next_time(now);
- if next > now {
- qinfo!(
- [self.name],
- "advancing time by {:?} to {:?}",
- next - now,
- next - start
- );
- now = next;
- }
- }
- }
- }
-}
diff --git a/third_party/rust/neqo-transport/tests/sim/net.rs b/third_party/rust/neqo-transport/tests/sim/net.rs
deleted file mode 100644
index 754426f895..0000000000
--- a/third_party/rust/neqo-transport/tests/sim/net.rs
+++ /dev/null
@@ -1,111 +0,0 @@
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-use super::rng::RandomDuration;
-use super::{Node, Rng};
-use neqo_common::Datagram;
-use neqo_transport::Output;
-use std::collections::BTreeMap;
-use std::fmt::{self, Debug};
-use std::iter;
-use std::ops::Range;
-use std::time::{Duration, Instant};
-
-///
-pub struct RandomDrop {
- threshold: u64,
- max: u64,
- rng: Rng,
-}
-
-impl RandomDuration {
- /// Make a new random `Duration` generator. This asserts if the range provided
- /// is inverted (i.e., `bounds.start > bounds.end`), or spans 2^64
- /// or more nanoseconds.
- /// A zero-length range means that random values won't be taken from the Rng
- pub fn new(bounds: Range<Duration>, rng: Rng) -> Self {
- let max = u64::try_from((bounds.end - bounds.start).as_nanos()).unwrap();
- Self {
- start: bounds.start,
- max,
- rng,
- }
- }
-
- fn next(&mut self) -> Duration {
- let r = if self.max == 0 {
- Duration::new(0, 0)
- } else {
- self.rng.borrow_mut().random_from(0..self.max)
- }
- self.start + Duration::from_nanos(r)
- }
-}
-
-enum DelayState {
- New(Range<Duration>),
- Ready(RandomDuration),
-}
-
-pub struct Delay {
- state: DelayState,
- queue: BTreeMap<Instant, Datagram>,
-}
-
-impl Delay
-{
- pub fn new(bounds: Range<Duration>) -> Self
- {
- Self {
- State: DelayState::New(bounds),
- queue: BTreeMap::default(),
- }
- }
-
- fn insert(&mut self, d: Datagram, now: Instant) {
- let mut t = if let State::Ready(r) = self.state {
- now + self.source.next()
- } else {
- unreachable!();
- }
- while self.queue.contains_key(&t) {
- // This is a little inefficient, but it avoids drops on collisions,
- // which are super-common for a fixed delay.
- t += Duration::from_nanos(1);
- }
- self.queue.insert(t, d);
- }
-}
-
-impl Node for Delay
-{
- fn init(&mut self, rng: Rng, now: Instant) {
- if let DelayState::New(bounds) = self.state {
- self.state = RandomDuration::new(bounds);
- } else {
- unreachable!();
- }
- }
-
- fn process(&mut self, d: Option<Datagram>, now: Instant) -> Output {
- if let Some(dgram) = d {
- self.insert(dgram, now);
- }
- if let Some((&k, _)) = self.queue.range(..now).nth(0) {
- Output::Datagram(self.queue.remove(&k).unwrap())
- } else if let Some(&t) = self.queue.keys().nth(0) {
- Output::Callback(t - now)
- } else {
- Output::None
- }
- }
-}
-
-impl<T> Debug for Delay<T> {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- f.write_str("delay")
- }
-}
diff --git a/third_party/rust/neqo-transport/tests/sim/rng.rs b/third_party/rust/neqo-transport/tests/sim/rng.rs
deleted file mode 100644
index af4f70eb5f..0000000000
--- a/third_party/rust/neqo-transport/tests/sim/rng.rs
+++ /dev/null
@@ -1,81 +0,0 @@
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-use std::{convert::TryFrom, ops::Range};
-
-use neqo_common::Decoder;
-
-/// An implementation of a xoshiro256** pseudorandom generator.
-pub struct Random {
- state: [u64; 4],
-}
-
-impl Random {
- pub fn new(seed: [u8; 32]) -> Self {
- assert!(seed.iter().any(|&x| x != 0));
- let mut dec = Decoder::from(&seed);
- Self {
- state: [
- dec.decode_uint(8).unwrap(),
- dec.decode_uint(8).unwrap(),
- dec.decode_uint(8).unwrap(),
- dec.decode_uint(8).unwrap(),
- ],
- }
- }
-
- pub fn random(&mut self) -> u64 {
- let result = (self.state[1].overflowing_mul(5).0)
- .rotate_right(7)
- .overflowing_mul(9)
- .0;
- let t = self.state[1] << 17;
-
- self.state[2] ^= self.state[0];
- self.state[3] ^= self.state[1];
- self.state[1] ^= self.state[2];
- self.state[0] ^= self.state[3];
-
- self.state[2] ^= t;
- self.state[3] = self.state[3].rotate_right(45);
-
- result
- }
-
- /// Generate a random value from the range.
- /// If the range is empty or inverted (`range.start > range.end`), then
- /// this returns the value of `range.start` without generating any random values.
- pub fn random_from(&mut self, range: Range<u64>) -> u64 {
- let max = range.end.saturating_sub(range.start);
- if max == 0 {
- return range.start;
- }
-
- let shift = (max - 1).leading_zeros();
- assert_ne!(max, 0);
- loop {
- let r = self.random() >> shift;
- if r < max {
- return range.start + r;
- }
- }
- }
-
- /// Get the seed necessary to continue from this point.
- pub fn seed_str(&self) -> String {
- format!(
- "{:8x}{:8x}{:8x}{:8x}",
- self.state[0], self.state[1], self.state[2], self.state[3],
- )
- }
-}
-
-impl Default for Random {
- fn default() -> Self {
- let buf = neqo_crypto::random(32);
- Random::new(<[u8; 32]>::try_from(&buf[..]).unwrap())
- }
-}
diff --git a/third_party/rust/neqo-transport/tests/sim/taildrop.rs b/third_party/rust/neqo-transport/tests/sim/taildrop.rs
deleted file mode 100644
index 26813800c9..0000000000
--- a/third_party/rust/neqo-transport/tests/sim/taildrop.rs
+++ /dev/null
@@ -1,188 +0,0 @@
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-#![allow(clippy::module_name_repetitions)]
-
-use std::{
- cmp::max,
- collections::VecDeque,
- convert::TryFrom,
- fmt::{self, Debug},
- time::{Duration, Instant},
-};
-
-use neqo_common::{qtrace, Datagram};
-use neqo_transport::Output;
-
-use super::Node;
-
-/// One second in nanoseconds.
-const ONE_SECOND_NS: u128 = 1_000_000_000;
-
-/// This models a link with a tail drop router at the front of it.
-pub struct TailDrop {
- /// An overhead associated with each entry. This accounts for
- /// layer 2, IP, and UDP overheads.
- overhead: usize,
- /// The rate at which bytes egress the link, in bytes per second.
- rate: usize,
- /// The depth of the queue, in bytes.
- capacity: usize,
-
- /// A counter for how many bytes are enqueued.
- used: usize,
- /// A queue of unsent bytes.
- queue: VecDeque<Datagram>,
- /// The time that the next datagram can enter the link.
- next_deque: Option<Instant>,
-
- /// Any sub-ns delay from the last enqueue.
- sub_ns_delay: u32,
- /// The time it takes a byte to exit the other end of the link.
- delay: Duration,
- /// The packets that are on the link and when they can be delivered.
- on_link: VecDeque<(Instant, Datagram)>,
-
- /// The number of packets received.
- received: usize,
- /// The number of packets dropped.
- dropped: usize,
- /// The number of packets delivered.
- delivered: usize,
- /// The maximum amount of queue capacity ever used.
- /// As packets leave the queue as soon as they start being used, this doesn't
- /// count them.
- maxq: usize,
-}
-
-impl TailDrop {
- /// Make a new taildrop node with the given rate, queue capacity, and link delay.
- pub fn new(rate: usize, capacity: usize, delay: Duration) -> Self {
- Self {
- overhead: 64,
- rate,
- capacity,
- used: 0,
- queue: VecDeque::new(),
- next_deque: None,
- sub_ns_delay: 0,
- delay,
- on_link: VecDeque::new(),
- received: 0,
- dropped: 0,
- delivered: 0,
- maxq: 0,
- }
- }
-
- /// A tail drop queue on a 10Mbps link (approximated to 1 million bytes per second)
- /// with a fat 32k buffer (about 30ms), and the default forward delay of 50ms.
- pub fn dsl_uplink() -> Self {
- TailDrop::new(1_000_000, 32_768, Duration::from_millis(50))
- }
-
- /// Cut downlink to one fifth of the uplink (2Mbps), and reduce the buffer to 1/4.
- pub fn dsl_downlink() -> Self {
- TailDrop::new(200_000, 8_192, Duration::from_millis(50))
- }
-
- /// How "big" is this datagram, accounting for overheads.
- /// This approximates by using the same overhead for storing in the queue
- /// and for sending on the wire.
- fn size(&self, d: &Datagram) -> usize {
- d.len() + self.overhead
- }
-
- /// Start sending a datagram.
- fn send(&mut self, d: Datagram, now: Instant) {
- // How many bytes are we "transmitting"?
- let sz = u128::try_from(self.size(&d)).unwrap();
-
- // Calculate how long it takes to put the packet on the link.
- // Perform the calculation based on 2^32 seconds and save any remainder.
- // This ensures that high rates and small packets don't result in rounding
- // down times too badly.
- // Duration consists of a u64 and a u32, so we have 32 high bits to spare.
- let t = sz * (ONE_SECOND_NS << 32) / u128::try_from(self.rate).unwrap()
- + u128::from(self.sub_ns_delay);
- let send_ns = u64::try_from(t >> 32).unwrap();
- assert_ne!(send_ns, 0, "sending a packet takes <1ns");
- self.sub_ns_delay = u32::try_from(t & u128::from(u32::MAX)).unwrap();
- let deque_time = now + Duration::from_nanos(send_ns);
- self.next_deque = Some(deque_time);
-
- // Now work out when the packet is fully received at the other end of
- // the link. Setup to deliver the packet then.
- let delivery_time = deque_time + self.delay;
- self.on_link.push_back((delivery_time, d));
- }
-
- /// Enqueue for sending. Maybe. If this overflows the queue, drop it instead.
- fn maybe_enqueue(&mut self, d: Datagram, now: Instant) {
- self.received += 1;
- if self.next_deque.is_none() {
- // Nothing in the queue and nothing still sending.
- debug_assert!(self.queue.is_empty());
- self.send(d, now);
- } else if self.used + self.size(&d) <= self.capacity {
- self.used += self.size(&d);
- self.maxq = max(self.maxq, self.used);
- self.queue.push_back(d);
- } else {
- qtrace!("taildrop dropping {} bytes", d.len());
- self.dropped += 1;
- }
- }
-
- /// If the last packet that was sending has been sent, start sending
- /// the next one.
- fn maybe_send(&mut self, now: Instant) {
- if self.next_deque.as_ref().map_or(false, |t| *t <= now) {
- if let Some(d) = self.queue.pop_front() {
- self.used -= self.size(&d);
- self.send(d, now);
- } else {
- self.next_deque = None;
- self.sub_ns_delay = 0;
- }
- }
- }
-}
-
-impl Node for TailDrop {
- fn process(&mut self, d: Option<Datagram>, now: Instant) -> Output {
- if let Some(dgram) = d {
- self.maybe_enqueue(dgram, now);
- }
-
- self.maybe_send(now);
-
- if let Some((t, _)) = self.on_link.front() {
- if *t <= now {
- let (_, d) = self.on_link.pop_front().unwrap();
- self.delivered += 1;
- Output::Datagram(d)
- } else {
- Output::Callback(*t - now)
- }
- } else {
- Output::None
- }
- }
-
- fn print_summary(&self, test_name: &str) {
- println!(
- "{}: taildrop: rx {} drop {} tx {} maxq {}",
- test_name, self.received, self.dropped, self.delivered, self.maxq,
- );
- }
-}
-
-impl Debug for TailDrop {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- f.write_str("taildrop")
- }
-}
diff --git a/third_party/rust/qlog/.cargo-checksum.json b/third_party/rust/qlog/.cargo-checksum.json
index 17ad1f8978..4aae6bf3b8 100644
--- a/third_party/rust/qlog/.cargo-checksum.json
+++ b/third_party/rust/qlog/.cargo-checksum.json
@@ -1 +1 @@
-{"files":{"Cargo.toml":"15c2606defff66515f2ded1ace2aeb729229a3558d9a026c058e51a7518e6859","README.md":"597691eb766c2cbd7a6591bda56d3e70e6836b62b6327fb73497523eabd5b53d","src/events/connectivity.rs":"116993412e200e375c97762980ffb638d2244197fd752b9569b5b20baf574308","src/events/h3.rs":"45dfa1dea722f3c8adb989f04ff24e8c39550a65a35325885b3a915cafd3a550","src/events/mod.rs":"75f57b4717fa9777e19d61b99b6a79164f0e8bca9b4681c3ab11b204320c8c55","src/events/qpack.rs":"5c7267c45e3fb947cdfa946f9f9692d3e3e36a166f70124ba293dc27534267d0","src/events/quic.rs":"88b884f5788c671ffee79a3448f367c18f95ee30531262fcc14310d80e662f4a","src/events/security.rs":"e9852d7de16851b62c3e0a886a2c1a31d237e62574ef88428ef62dd179b0b008","src/lib.rs":"bbc190a6d0f484fd723f9df6c1b2a4596f826e0282ad40ee17a0822ea28a5626","src/reader.rs":"4e0069c24aca9cb99d75075c9b784fa02855ea449d2f1528bea944a4e02a9af5","src/streamer.rs":"4774c2abde1a5b0f4448aac06c62c7927208c12f338c46981f80c98703b54074"},"package":null} \ No newline at end of file
+{"files":{"Cargo.toml":"66c6d85a84c84711f3abddaafb163bc6025a8505701fea8b61bfa961851be757","README.md":"597691eb766c2cbd7a6591bda56d3e70e6836b62b6327fb73497523eabd5b53d","src/events/connectivity.rs":"116993412e200e375c97762980ffb638d2244197fd752b9569b5b20baf574308","src/events/h3.rs":"4799179c4d18403a3936a327c483d110bd058d42061f9cdd39601b3e91500c1b","src/events/mod.rs":"75f57b4717fa9777e19d61b99b6a79164f0e8bca9b4681c3ab11b204320c8c55","src/events/qpack.rs":"5c7267c45e3fb947cdfa946f9f9692d3e3e36a166f70124ba293dc27534267d0","src/events/quic.rs":"88b884f5788c671ffee79a3448f367c18f95ee30531262fcc14310d80e662f4a","src/events/security.rs":"e9852d7de16851b62c3e0a886a2c1a31d237e62574ef88428ef62dd179b0b008","src/lib.rs":"bbc190a6d0f484fd723f9df6c1b2a4596f826e0282ad40ee17a0822ea28a5626","src/reader.rs":"4e0069c24aca9cb99d75075c9b784fa02855ea449d2f1528bea944a4e02a9af5","src/streamer.rs":"4774c2abde1a5b0f4448aac06c62c7927208c12f338c46981f80c98703b54074"},"package":"9c0407438c69b3d99714a796a135cbfb2d60744e4747fb2b46a87acd1c1fcd0e"} \ No newline at end of file
diff --git a/third_party/rust/qlog/Cargo.toml b/third_party/rust/qlog/Cargo.toml
index 665559312f..4a5eb1ff96 100644
--- a/third_party/rust/qlog/Cargo.toml
+++ b/third_party/rust/qlog/Cargo.toml
@@ -12,7 +12,7 @@
[package]
edition = "2018"
name = "qlog"
-version = "0.11.0"
+version = "0.12.0"
authors = ["Lucas Pardue <lucaspardue.24.7@gmail.com>"]
description = "qlog data model for QUIC and HTTP/3"
readme = "README.md"
@@ -25,13 +25,13 @@ categories = ["network-programming"]
license = "BSD-2-Clause"
repository = "https://github.com/cloudflare/quiche"
-[dependencies]
-serde_derive = "1.0"
-
[dependencies.serde]
version = "1.0.139"
features = ["derive"]
+[dependencies.serde_derive]
+version = "1.0"
+
[dependencies.serde_json]
version = "1.0"
features = ["preserve_order"]
diff --git a/third_party/rust/qlog/src/events/h3.rs b/third_party/rust/qlog/src/events/h3.rs
index eaf3cadf36..632c80538f 100644
--- a/third_party/rust/qlog/src/events/h3.rs
+++ b/third_party/rust/qlog/src/events/h3.rs
@@ -209,9 +209,8 @@ pub struct H3ParametersRestored {
pub struct H3StreamTypeSet {
pub owner: Option<H3Owner>,
pub stream_id: u64,
-
pub stream_type: H3StreamType,
-
+ pub stream_type_value: Option<u64>,
pub associated_push_id: Option<u64>,
}
diff --git a/third_party/rust/serde/.cargo-checksum.json b/third_party/rust/serde/.cargo-checksum.json
index b8aaa174e0..d55068bd63 100644
--- a/third_party/rust/serde/.cargo-checksum.json
+++ b/third_party/rust/serde/.cargo-checksum.json
@@ -1 +1 @@
-{"files":{"Cargo.toml":"3c553cd5adc065a5e6412e518b55ef85f7ffa307171ad4369d016de03a8f163f","LICENSE-APACHE":"62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a","LICENSE-MIT":"23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3","README.md":"c3ece10a36d19b4e857a770eaf74a2164d220f55fa11947065a3898c1697ecef","build.rs":"f9ba30324b9ce085c903595fb55a5293f8c2348ff36bfe870521b935ae6d105c","crates-io.md":"56e988ac4944c45f5bf5051e3827892ed8fb817853d99d9df1fff6621108e270","src/de/format.rs":"c85071b016df643b161859682d21ce34fa0ebf2a3bdbeeea69859da48f5d934f","src/de/ignored_any.rs":"6480f2b2a83dc4764d01b2eec7309729eef2492eede2e5ee98d23a60b05198eb","src/de/impls.rs":"2857d734176a0b78a41c9358354b0b0b83c6b2d948590be072d98606a8cae9d6","src/de/mod.rs":"7fb7427de1981bfa13af06c898d213a6bc34697148e96cef08d3c447c1999527","src/de/seed.rs":"045d890712a04eb33ffc5a021e5d948a63c89402b8ffeea749df2171b7484f8f","src/de/size_hint.rs":"fff83dc39d30e75e8e611991f9c5399188a1aad23a6462dbca2c8b62655cfedb","src/de/value.rs":"5d8dcae3a98a2203f2c0934adb84dbf741f120f246dfc02aa6d0d10673dc39c7","src/integer128.rs":"29ef30b7d94507b34807090e68173767cdc7aff62edccd38affe69e75338dddc","src/lib.rs":"b16783d3e69a1e3b499d28810823aac2fd92d696c0b511f7bcda0d011399167c","src/macros.rs":"e3486ef4a9a4ed1b27234aa1817ccb25ec0eb026ffc95e2c71c7b917f1f45629","src/private/de.rs":"6557a124fdaf61f9c7cd80163e40f4a453354e45b63a4eb55dafdfe0159f6881","src/private/doc.rs":"9ad740e9ea2eedf861b77116eda9a6fb74bc8553541cd17d1bc5791a3ef3271a","src/private/mod.rs":"b8f0c348621d91dd9da3db83d8877e70bc61ad0a2dc2d6fb57c6fc2c2cbafa26","src/private/ser.rs":"656613691bd8d40cb70a52d4ebe3ee96a993c8a1292d50822d9ca5bdad84426b","src/ser/fmt.rs":"77a5583e5c227ea1982b097ed6378af5c899d43761d71e33440262fd35944695","src/ser/impls.rs":"850619164b399c37cd373d24f5a2c83453f40b34bb978c5722d2c1ae226775b5","src/ser/impossible.rs":"e11b37689ec1395378d546fce74221ca9046d0761744301f12029102fd07e30e","src/ser/mod.rs":"a7fd082203d63cbe4f0fe86d9be16bf4f3b2444653dac6bb61d82e0f4f6b4214","src/std_error.rs":"25a07149e2e468747ffa5a58051c7f93d7b3c0fa0372f012a96c97ec8ab03b97"},"package":"63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02"} \ No newline at end of file
+{"files":{"Cargo.toml":"f03b626efae73a6dd42f07d722dad2da3a4add51f4e653e30a6d696853bab209","LICENSE-APACHE":"62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a","LICENSE-MIT":"23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3","README.md":"731c044fc5f98b37a89e9049c9214267db98763309cb63146b45c029640f82a3","build.rs":"f9ba30324b9ce085c903595fb55a5293f8c2348ff36bfe870521b935ae6d105c","crates-io.md":"56e988ac4944c45f5bf5051e3827892ed8fb817853d99d9df1fff6621108e270","src/de/format.rs":"c85071b016df643b161859682d21ce34fa0ebf2a3bdbeeea69859da48f5d934f","src/de/ignored_any.rs":"6480f2b2a83dc4764d01b2eec7309729eef2492eede2e5ee98d23a60b05198eb","src/de/impls.rs":"2857d734176a0b78a41c9358354b0b0b83c6b2d948590be072d98606a8cae9d6","src/de/mod.rs":"fc34da49f692803d2c2f131322d9b48ad8e4f39ed10b2b304d6193ab09d621fb","src/de/seed.rs":"045d890712a04eb33ffc5a021e5d948a63c89402b8ffeea749df2171b7484f8f","src/de/size_hint.rs":"fff83dc39d30e75e8e611991f9c5399188a1aad23a6462dbca2c8b62655cfedb","src/de/value.rs":"0fd511a288c20a1b768718f4baadf9c7d4146d276af6a71ba1d0f7679b28644a","src/integer128.rs":"29ef30b7d94507b34807090e68173767cdc7aff62edccd38affe69e75338dddc","src/lib.rs":"638b231a280519f1861ea5f1bfbe97e2394b2f7662a9701b8e57ed95093dd298","src/macros.rs":"e3486ef4a9a4ed1b27234aa1817ccb25ec0eb026ffc95e2c71c7b917f1f45629","src/private/de.rs":"6557a124fdaf61f9c7cd80163e40f4a453354e45b63a4eb55dafdfe0159f6881","src/private/doc.rs":"9ad740e9ea2eedf861b77116eda9a6fb74bc8553541cd17d1bc5791a3ef3271a","src/private/mod.rs":"b8f0c348621d91dd9da3db83d8877e70bc61ad0a2dc2d6fb57c6fc2c2cbafa26","src/private/ser.rs":"656613691bd8d40cb70a52d4ebe3ee96a993c8a1292d50822d9ca5bdad84426b","src/ser/fmt.rs":"77a5583e5c227ea1982b097ed6378af5c899d43761d71e33440262fd35944695","src/ser/impls.rs":"850619164b399c37cd373d24f5a2c83453f40b34bb978c5722d2c1ae226775b5","src/ser/impossible.rs":"e11b37689ec1395378d546fce74221ca9046d0761744301f12029102fd07e30e","src/ser/mod.rs":"a7fd082203d63cbe4f0fe86d9be16bf4f3b2444653dac6bb61d82e0f4f6b4214","src/std_error.rs":"25a07149e2e468747ffa5a58051c7f93d7b3c0fa0372f012a96c97ec8ab03b97"},"package":"3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2"} \ No newline at end of file
diff --git a/third_party/rust/serde/Cargo.toml b/third_party/rust/serde/Cargo.toml
index a11aad026a..6ba68688ac 100644
--- a/third_party/rust/serde/Cargo.toml
+++ b/third_party/rust/serde/Cargo.toml
@@ -13,7 +13,7 @@
edition = "2018"
rust-version = "1.31"
name = "serde"
-version = "1.0.195"
+version = "1.0.197"
authors = [
"Erick Tryzelaar <erick.tryzelaar@gmail.com>",
"David Tolnay <dtolnay@gmail.com>",
@@ -74,4 +74,4 @@ std = []
unstable = []
[target."cfg(any())".dependencies.serde_derive]
-version = "=1.0.195"
+version = "=1.0.197"
diff --git a/third_party/rust/serde/README.md b/third_party/rust/serde/README.md
index 477fd36478..31292944af 100644
--- a/third_party/rust/serde/README.md
+++ b/third_party/rust/serde/README.md
@@ -1,11 +1,11 @@
-# Serde &emsp; [![Build Status]][actions] [![Latest Version]][crates.io] [![serde: rustc 1.31+]][Rust 1.31] [![serde_derive: rustc 1.56+]][Rust 1.56]
+# Serde &emsp; [![Build Status]][actions] [![Latest Version]][crates.io] [![serde msrv]][Rust 1.31] [![serde_derive msrv]][Rust 1.56]
[Build Status]: https://img.shields.io/github/actions/workflow/status/serde-rs/serde/ci.yml?branch=master
[actions]: https://github.com/serde-rs/serde/actions?query=branch%3Amaster
[Latest Version]: https://img.shields.io/crates/v/serde.svg
[crates.io]: https://crates.io/crates/serde
-[serde: rustc 1.31+]: https://img.shields.io/badge/serde-rustc_1.31+-lightgray.svg
-[serde_derive: rustc 1.56+]: https://img.shields.io/badge/serde_derive-rustc_1.56+-lightgray.svg
+[serde msrv]: https://img.shields.io/crates/msrv/serde.svg?label=serde%20msrv&color=lightgray
+[serde_derive msrv]: https://img.shields.io/crates/msrv/serde_derive.svg?label=serde_derive%20msrv&color=lightgray
[Rust 1.31]: https://blog.rust-lang.org/2018/12/06/Rust-1.31-and-rust-2018.html
[Rust 1.56]: https://blog.rust-lang.org/2021/10/21/Rust-1.56.0.html
diff --git a/third_party/rust/serde/src/de/mod.rs b/third_party/rust/serde/src/de/mod.rs
index c9919d92b8..1924fe3d88 100644
--- a/third_party/rust/serde/src/de/mod.rs
+++ b/third_party/rust/serde/src/de/mod.rs
@@ -402,20 +402,20 @@ impl<'a> fmt::Display for Unexpected<'a> {
Bool(b) => write!(formatter, "boolean `{}`", b),
Unsigned(i) => write!(formatter, "integer `{}`", i),
Signed(i) => write!(formatter, "integer `{}`", i),
- Float(f) => write!(formatter, "floating point `{}`", f),
+ Float(f) => write!(formatter, "floating point `{}`", WithDecimalPoint(f)),
Char(c) => write!(formatter, "character `{}`", c),
Str(s) => write!(formatter, "string {:?}", s),
- Bytes(_) => write!(formatter, "byte array"),
- Unit => write!(formatter, "unit value"),
- Option => write!(formatter, "Option value"),
- NewtypeStruct => write!(formatter, "newtype struct"),
- Seq => write!(formatter, "sequence"),
- Map => write!(formatter, "map"),
- Enum => write!(formatter, "enum"),
- UnitVariant => write!(formatter, "unit variant"),
- NewtypeVariant => write!(formatter, "newtype variant"),
- TupleVariant => write!(formatter, "tuple variant"),
- StructVariant => write!(formatter, "struct variant"),
+ Bytes(_) => formatter.write_str("byte array"),
+ Unit => formatter.write_str("unit value"),
+ Option => formatter.write_str("Option value"),
+ NewtypeStruct => formatter.write_str("newtype struct"),
+ Seq => formatter.write_str("sequence"),
+ Map => formatter.write_str("map"),
+ Enum => formatter.write_str("enum"),
+ UnitVariant => formatter.write_str("unit variant"),
+ NewtypeVariant => formatter.write_str("newtype variant"),
+ TupleVariant => formatter.write_str("tuple variant"),
+ StructVariant => formatter.write_str("struct variant"),
Other(other) => formatter.write_str(other),
}
}
@@ -2278,10 +2278,10 @@ impl Display for OneOf {
1 => write!(formatter, "`{}`", self.names[0]),
2 => write!(formatter, "`{}` or `{}`", self.names[0], self.names[1]),
_ => {
- tri!(write!(formatter, "one of "));
+ tri!(formatter.write_str("one of "));
for (i, alt) in self.names.iter().enumerate() {
if i > 0 {
- tri!(write!(formatter, ", "));
+ tri!(formatter.write_str(", "));
}
tri!(write!(formatter, "`{}`", alt));
}
@@ -2290,3 +2290,36 @@ impl Display for OneOf {
}
}
}
+
+struct WithDecimalPoint(f64);
+
+impl Display for WithDecimalPoint {
+ fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ struct LookForDecimalPoint<'f, 'a> {
+ formatter: &'f mut fmt::Formatter<'a>,
+ has_decimal_point: bool,
+ }
+
+ impl<'f, 'a> fmt::Write for LookForDecimalPoint<'f, 'a> {
+ fn write_str(&mut self, fragment: &str) -> fmt::Result {
+ self.has_decimal_point |= fragment.contains('.');
+ self.formatter.write_str(fragment)
+ }
+
+ fn write_char(&mut self, ch: char) -> fmt::Result {
+ self.has_decimal_point |= ch == '.';
+ self.formatter.write_char(ch)
+ }
+ }
+
+ let mut writer = LookForDecimalPoint {
+ formatter,
+ has_decimal_point: false,
+ };
+ tri!(write!(writer, "{}", self.0));
+ if !writer.has_decimal_point {
+ tri!(formatter.write_str(".0"));
+ }
+ Ok(())
+ }
+}
diff --git a/third_party/rust/serde/src/de/value.rs b/third_party/rust/serde/src/de/value.rs
index b229ebab77..3bc0c71c57 100644
--- a/third_party/rust/serde/src/de/value.rs
+++ b/third_party/rust/serde/src/de/value.rs
@@ -983,7 +983,7 @@ struct ExpectedInSeq(usize);
impl Expected for ExpectedInSeq {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
if self.0 == 1 {
- write!(formatter, "1 element in sequence")
+ formatter.write_str("1 element in sequence")
} else {
write!(formatter, "{} elements in sequence", self.0)
}
@@ -1411,7 +1411,7 @@ struct ExpectedInMap(usize);
impl Expected for ExpectedInMap {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
if self.0 == 1 {
- write!(formatter, "1 element in map")
+ formatter.write_str("1 element in map")
} else {
write!(formatter, "{} elements in map", self.0)
}
diff --git a/third_party/rust/serde/src/lib.rs b/third_party/rust/serde/src/lib.rs
index b0756af6dc..5cf44c1c18 100644
--- a/third_party/rust/serde/src/lib.rs
+++ b/third_party/rust/serde/src/lib.rs
@@ -95,7 +95,7 @@
////////////////////////////////////////////////////////////////////////////////
// Serde types in rustdoc of other crates get linked to here.
-#![doc(html_root_url = "https://docs.rs/serde/1.0.195")]
+#![doc(html_root_url = "https://docs.rs/serde/1.0.197")]
// Support using Serde without the standard library!
#![cfg_attr(not(feature = "std"), no_std)]
// Show which crate feature enables conditionally compiled APIs in documentation.
@@ -130,6 +130,7 @@
clippy::derive_partial_eq_without_eq,
clippy::enum_glob_use,
clippy::explicit_auto_deref,
+ clippy::incompatible_msrv,
clippy::let_underscore_untyped,
clippy::map_err_ignore,
clippy::new_without_default,
@@ -178,16 +179,16 @@ mod lib {
pub use self::core::{cmp, mem, slice};
pub use self::core::cell::{Cell, RefCell};
- pub use self::core::clone::{self, Clone};
+ pub use self::core::clone;
pub use self::core::cmp::Reverse;
- pub use self::core::convert::{self, From, Into};
- pub use self::core::default::{self, Default};
- pub use self::core::fmt::{self, Debug, Display};
+ pub use self::core::convert;
+ pub use self::core::default;
+ pub use self::core::fmt::{self, Debug, Display, Write as FmtWrite};
pub use self::core::marker::{self, PhantomData};
pub use self::core::num::Wrapping;
pub use self::core::ops::{Bound, Range, RangeFrom, RangeInclusive, RangeTo};
- pub use self::core::option::{self, Option};
- pub use self::core::result::{self, Result};
+ pub use self::core::option;
+ pub use self::core::result;
pub use self::core::time::Duration;
#[cfg(all(feature = "alloc", not(feature = "std")))]
diff --git a/third_party/rust/serde_derive/.cargo-checksum.json b/third_party/rust/serde_derive/.cargo-checksum.json
index 3e0519d169..331652acd7 100644
--- a/third_party/rust/serde_derive/.cargo-checksum.json
+++ b/third_party/rust/serde_derive/.cargo-checksum.json
@@ -1 +1 @@
-{"files":{"Cargo.toml":"292f527e949377d3d23759746419c447bb8e93603f48d933ef1af34a0bf8b666","LICENSE-APACHE":"62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a","LICENSE-MIT":"23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3","README.md":"c3ece10a36d19b4e857a770eaf74a2164d220f55fa11947065a3898c1697ecef","crates-io.md":"56e988ac4944c45f5bf5051e3827892ed8fb817853d99d9df1fff6621108e270","src/bound.rs":"6c5c20785ac95af9480f8d0de35a7e844cc36a16012f6468db148acd03cb15c2","src/de.rs":"c221ab2b94a5d80dccff74a37f3448b3d695656552b452595dc289c73b12fb2b","src/dummy.rs":"9533dfee23f20d92ea75734c739022820c2787ded0d54f459feacdeb770ec912","src/fragment.rs":"6757cb4c3131d4300f093572efc273c4ab5a20e3e1efb54a311dcfa52d0bd6eb","src/internals/ast.rs":"7dc997e4090033bbd1d0bdd870e8bb87b096b7f66cfd02047f6b85ebdd569b12","src/internals/attr.rs":"6584c0a02de0d17993877303f3cc2c1bccf235257632220421f98082d82d387a","src/internals/case.rs":"10c8dda2b32d8c6c6b63cf09cdc63d02375af7e95ecefe8fecb34f93b65191bb","src/internals/check.rs":"d842eb9912fd29311060b67f3bc62c438eb7b5d86093355acb4de7eee02a0ef8","src/internals/ctxt.rs":"83a4e6fbe0e439d578478883594407e03f2f340541be479bdf0b04a202633a37","src/internals/mod.rs":"ed021ca635c18132a0e5c3d90f21b7f65def0a61e946421a30200b5b9ab6ad43","src/internals/receiver.rs":"fe8a480669511b5edcfe71f5dd290cf72ccec54c9016ec85f2ac59dce538077f","src/internals/respan.rs":"899753859c58ce5f532a3ec4584796a52f13ed5a0533191e48c953ba5c1b52ff","src/internals/symbol.rs":"d619e88caa3c7a09b03014257f2b349ee922290062d9b97b4dd19d0e64532690","src/lib.rs":"3fc5a148c35cda8a27bc0a65da41a991d63f8ea0e0d607336082dd0f528750ef","src/pretend.rs":"7facc10a5b805564dd95735ae11118ec17ca6adcc49a59764e7c920e27b9fc4a","src/ser.rs":"e3341471cea9d7e2fb4043e5d1746862beb9a4e25196170879eeac529d460920","src/this.rs":"87818dc80cbb521b51938a653d09daf10aafc220bb10425948de82ad670fcb85"},"package":"46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c"} \ No newline at end of file
+{"files":{"Cargo.toml":"daf3d8f8efdf30d3575c7d1e1372ff7287891fb95625223e4a8a2f792c4474e1","LICENSE-APACHE":"62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a","LICENSE-MIT":"23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3","README.md":"731c044fc5f98b37a89e9049c9214267db98763309cb63146b45c029640f82a3","crates-io.md":"56e988ac4944c45f5bf5051e3827892ed8fb817853d99d9df1fff6621108e270","src/bound.rs":"6c5c20785ac95af9480f8d0de35a7e844cc36a16012f6468db148acd03cb15c2","src/de.rs":"c221ab2b94a5d80dccff74a37f3448b3d695656552b452595dc289c73b12fb2b","src/dummy.rs":"9533dfee23f20d92ea75734c739022820c2787ded0d54f459feacdeb770ec912","src/fragment.rs":"6757cb4c3131d4300f093572efc273c4ab5a20e3e1efb54a311dcfa52d0bd6eb","src/internals/ast.rs":"7dc997e4090033bbd1d0bdd870e8bb87b096b7f66cfd02047f6b85ebdd569b12","src/internals/attr.rs":"6584c0a02de0d17993877303f3cc2c1bccf235257632220421f98082d82d387a","src/internals/case.rs":"10c8dda2b32d8c6c6b63cf09cdc63d02375af7e95ecefe8fecb34f93b65191bb","src/internals/check.rs":"d842eb9912fd29311060b67f3bc62c438eb7b5d86093355acb4de7eee02a0ef8","src/internals/ctxt.rs":"83a4e6fbe0e439d578478883594407e03f2f340541be479bdf0b04a202633a37","src/internals/mod.rs":"ed021ca635c18132a0e5c3d90f21b7f65def0a61e946421a30200b5b9ab6ad43","src/internals/receiver.rs":"fe8a480669511b5edcfe71f5dd290cf72ccec54c9016ec85f2ac59dce538077f","src/internals/respan.rs":"899753859c58ce5f532a3ec4584796a52f13ed5a0533191e48c953ba5c1b52ff","src/internals/symbol.rs":"d619e88caa3c7a09b03014257f2b349ee922290062d9b97b4dd19d0e64532690","src/lib.rs":"7a6c2796244658f62d398ebc6819c4f3064dac4a1ad7c52b40359f9411f1c266","src/pretend.rs":"7facc10a5b805564dd95735ae11118ec17ca6adcc49a59764e7c920e27b9fc4a","src/ser.rs":"e3341471cea9d7e2fb4043e5d1746862beb9a4e25196170879eeac529d460920","src/this.rs":"87818dc80cbb521b51938a653d09daf10aafc220bb10425948de82ad670fcb85"},"package":"7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b"} \ No newline at end of file
diff --git a/third_party/rust/serde_derive/Cargo.toml b/third_party/rust/serde_derive/Cargo.toml
index 66d752cf73..33642ef04c 100644
--- a/third_party/rust/serde_derive/Cargo.toml
+++ b/third_party/rust/serde_derive/Cargo.toml
@@ -12,7 +12,7 @@
[package]
rust-version = "1.56"
name = "serde_derive"
-version = "1.0.195"
+version = "1.0.197"
authors = [
"Erick Tryzelaar <erick.tryzelaar@gmail.com>",
"David Tolnay <dtolnay@gmail.com>",
@@ -44,12 +44,24 @@ proc-macro = true
[dependencies.proc-macro2]
version = "1.0.74"
+features = ["proc-macro"]
+default-features = false
[dependencies.quote]
version = "1.0.35"
+features = ["proc-macro"]
+default-features = false
[dependencies.syn]
version = "2.0.46"
+features = [
+ "clone-impls",
+ "derive",
+ "parsing",
+ "printing",
+ "proc-macro",
+]
+default-features = false
[dev-dependencies.serde]
version = "1"
diff --git a/third_party/rust/serde_derive/README.md b/third_party/rust/serde_derive/README.md
index 477fd36478..31292944af 100644
--- a/third_party/rust/serde_derive/README.md
+++ b/third_party/rust/serde_derive/README.md
@@ -1,11 +1,11 @@
-# Serde &emsp; [![Build Status]][actions] [![Latest Version]][crates.io] [![serde: rustc 1.31+]][Rust 1.31] [![serde_derive: rustc 1.56+]][Rust 1.56]
+# Serde &emsp; [![Build Status]][actions] [![Latest Version]][crates.io] [![serde msrv]][Rust 1.31] [![serde_derive msrv]][Rust 1.56]
[Build Status]: https://img.shields.io/github/actions/workflow/status/serde-rs/serde/ci.yml?branch=master
[actions]: https://github.com/serde-rs/serde/actions?query=branch%3Amaster
[Latest Version]: https://img.shields.io/crates/v/serde.svg
[crates.io]: https://crates.io/crates/serde
-[serde: rustc 1.31+]: https://img.shields.io/badge/serde-rustc_1.31+-lightgray.svg
-[serde_derive: rustc 1.56+]: https://img.shields.io/badge/serde_derive-rustc_1.56+-lightgray.svg
+[serde msrv]: https://img.shields.io/crates/msrv/serde.svg?label=serde%20msrv&color=lightgray
+[serde_derive msrv]: https://img.shields.io/crates/msrv/serde_derive.svg?label=serde_derive%20msrv&color=lightgray
[Rust 1.31]: https://blog.rust-lang.org/2018/12/06/Rust-1.31-and-rust-2018.html
[Rust 1.56]: https://blog.rust-lang.org/2021/10/21/Rust-1.56.0.html
diff --git a/third_party/rust/serde_derive/src/lib.rs b/third_party/rust/serde_derive/src/lib.rs
index 8c4a6cacc7..b91f17b1a9 100644
--- a/third_party/rust/serde_derive/src/lib.rs
+++ b/third_party/rust/serde_derive/src/lib.rs
@@ -13,7 +13,7 @@
//!
//! [https://serde.rs/derive.html]: https://serde.rs/derive.html
-#![doc(html_root_url = "https://docs.rs/serde_derive/1.0.195")]
+#![doc(html_root_url = "https://docs.rs/serde_derive/1.0.197")]
// Ignored clippy lints
#![allow(
// clippy false positive: https://github.com/rust-lang/rust-clippy/issues/7054
diff --git a/third_party/rust/smallvec/.cargo-checksum.json b/third_party/rust/smallvec/.cargo-checksum.json
index f8adf7532e..bdb1fcd312 100644
--- a/third_party/rust/smallvec/.cargo-checksum.json
+++ b/third_party/rust/smallvec/.cargo-checksum.json
@@ -1 +1 @@
-{"files":{"Cargo.toml":"964f828a4ed019af9a728f6a0f63dc5860446c7b21abe2be1a0b92ddc82d1140","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"0b28172679e0009b655da42797c03fd163a3379d5cfa67ba1f1655e974a2a1a9","README.md":"a01127c37308457e8d396b176fb790846be0978c173be3f13260b62efcef011b","benches/bench.rs":"e2a235d68be20996014c00468b369887d2041ce95486625de3cef35b8f2e4acd","debug_metadata/README.md":"4d7f1c1b2c25ce2231ef71864d06e54323867459035b53bc9e00f66a0a44f82e","debug_metadata/smallvec.natvis":"3092ddebd8fffc3486536d7f27f8c5eae3a8a093d45cd8eeb3946ea2b0c35a15","scripts/run_miri.sh":"74a9f9adc43f986e81977b03846f7dd00122a0150bd8ec3fe4842a1a787e0f07","src/arbitrary.rs":"22e55cfbf60374945b30e6d0855129eff67cd8b878cef6fa997e1f4be67b9e3d","src/lib.rs":"aed3176e0c74d7eb1d405ee096a4d1027626ed5f1bb65da4c0ef89f83b8f66ed","src/specialization.rs":"46433586203399251cba496d67b88d34e1be3c2b591986b77463513da1c66471","src/tests.rs":"d0a70bb7b4e1d0a174f2a195c8ab55280a40589bab7028999afd787b3fff6eae","tests/debugger_visualizer.rs":"185456ad253957fc0c9e904ff8a1135397ac991c29fa3c60f75d8d81f7463022","tests/macro.rs":"22ad4f6f104a599fdcba19cad8834105b8656b212fb6c7573a427d447f5db14f"},"package":"942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a"} \ No newline at end of file
+{"files":{"Cargo.toml":"668bb964a243127d65605bb7a0d8d3c81bcbd8f7656a5b5734766ef534b4abcb","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"0b28172679e0009b655da42797c03fd163a3379d5cfa67ba1f1655e974a2a1a9","README.md":"a01127c37308457e8d396b176fb790846be0978c173be3f13260b62efcef011b","benches/bench.rs":"d82015eae942ee5cf74ace8c3c260ee2c6b5bcbeeb87254d2c72622c747a708a","debug_metadata/README.md":"4d7f1c1b2c25ce2231ef71864d06e54323867459035b53bc9e00f66a0a44f82e","debug_metadata/smallvec.natvis":"3092ddebd8fffc3486536d7f27f8c5eae3a8a093d45cd8eeb3946ea2b0c35a15","scripts/run_miri.sh":"74a9f9adc43f986e81977b03846f7dd00122a0150bd8ec3fe4842a1a787e0f07","src/arbitrary.rs":"22e55cfbf60374945b30e6d0855129eff67cd8b878cef6fa997e1f4be67b9e3d","src/lib.rs":"25fe85b6ae7b3972211bf57aeded4c7b72c47e4d843c7a4ba66908442197b5a0","src/specialization.rs":"46433586203399251cba496d67b88d34e1be3c2b591986b77463513da1c66471","src/tests.rs":"29c6e5dad62ebfea74e5116ac4a344b127b91cfb769fe9ba8b02b53773cf7ec8","tests/debugger_visualizer.rs":"185456ad253957fc0c9e904ff8a1135397ac991c29fa3c60f75d8d81f7463022","tests/macro.rs":"22ad4f6f104a599fdcba19cad8834105b8656b212fb6c7573a427d447f5db14f"},"package":"e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7"} \ No newline at end of file
diff --git a/third_party/rust/smallvec/Cargo.toml b/third_party/rust/smallvec/Cargo.toml
index 4a69944339..baba15315a 100644
--- a/third_party/rust/smallvec/Cargo.toml
+++ b/third_party/rust/smallvec/Cargo.toml
@@ -12,7 +12,7 @@
[package]
edition = "2018"
name = "smallvec"
-version = "1.11.1"
+version = "1.13.1"
authors = ["The Servo Project Developers"]
description = "'Small vector' optimization: store up to a small number of items on the stack"
documentation = "https://docs.rs/smallvec/"
diff --git a/third_party/rust/smallvec/benches/bench.rs b/third_party/rust/smallvec/benches/bench.rs
index b52ee15504..b6a8b857d3 100644
--- a/third_party/rust/smallvec/benches/bench.rs
+++ b/third_party/rust/smallvec/benches/bench.rs
@@ -1,12 +1,10 @@
#![feature(test)]
#![allow(deprecated)]
-#[macro_use]
-extern crate smallvec;
extern crate test;
use self::test::Bencher;
-use smallvec::{ExtendFromSlice, SmallVec};
+use smallvec::{ExtendFromSlice, smallvec, SmallVec};
const VEC_SIZE: usize = 16;
const SPILLED_SIZE: usize = 100;
diff --git a/third_party/rust/smallvec/src/lib.rs b/third_party/rust/smallvec/src/lib.rs
index 8281fb1808..cadb5d8b15 100644
--- a/third_party/rust/smallvec/src/lib.rs
+++ b/third_party/rust/smallvec/src/lib.rs
@@ -149,8 +149,7 @@ use core::mem::ManuallyDrop;
/// - Create a [`SmallVec`] containing a given list of elements:
///
/// ```
-/// # #[macro_use] extern crate smallvec;
-/// # use smallvec::SmallVec;
+/// # use smallvec::{smallvec, SmallVec};
/// # fn main() {
/// let v: SmallVec<[_; 128]> = smallvec![1, 2, 3];
/// assert_eq!(v[0], 1);
@@ -162,8 +161,7 @@ use core::mem::ManuallyDrop;
/// - Create a [`SmallVec`] from a given element and size:
///
/// ```
-/// # #[macro_use] extern crate smallvec;
-/// # use smallvec::SmallVec;
+/// # use smallvec::{smallvec, SmallVec};
/// # fn main() {
/// let v: SmallVec<[_; 0x8000]> = smallvec![1; 3];
/// assert_eq!(v, SmallVec::from_buf([1, 1, 1]));
@@ -209,8 +207,7 @@ macro_rules! smallvec {
/// - Create a [`SmallVec`] containing a given list of elements:
///
/// ```
-/// # #[macro_use] extern crate smallvec;
-/// # use smallvec::SmallVec;
+/// # use smallvec::{smallvec_inline, SmallVec};
/// # fn main() {
/// const V: SmallVec<[i32; 3]> = smallvec_inline![1, 2, 3];
/// assert_eq!(V[0], 1);
@@ -222,8 +219,7 @@ macro_rules! smallvec {
/// - Create a [`SmallVec`] from a given element and size:
///
/// ```
-/// # #[macro_use] extern crate smallvec;
-/// # use smallvec::SmallVec;
+/// # use smallvec::{smallvec_inline, SmallVec};
/// # fn main() {
/// const V: SmallVec<[i32; 3]> = smallvec_inline![1; 3];
/// assert_eq!(V, SmallVec::from_buf([1, 1, 1]));
@@ -328,7 +324,7 @@ fn infallible<T>(result: Result<T, CollectionAllocErr>) -> T {
}
/// FIXME: use `Layout::array` when we require a Rust version where it’s stable
-/// https://github.com/rust-lang/rust/issues/55724
+/// <https://github.com/rust-lang/rust/issues/55724>
fn layout_array<T>(n: usize) -> Result<Layout, CollectionAllocErr> {
let size = mem::size_of::<T>()
.checked_mul(n)
@@ -430,7 +426,7 @@ impl<'a, T: 'a + Array> Drop for Drain<'a, T> {
/// An iterator which uses a closure to determine if an element should be removed.
///
/// Returned from [`SmallVec::drain_filter`][1].
-///
+///
/// [1]: struct.SmallVec.html#method.drain_filter
pub struct DrainFilter<'a, T, F>
where
@@ -815,7 +811,7 @@ impl<A: Array> SmallVec<A> {
/// Construct a new `SmallVec` from a `Vec<A::Item>`.
///
- /// Elements will be copied to the inline buffer if vec.capacity() <= Self::inline_capacity().
+ /// Elements will be copied to the inline buffer if `vec.capacity() <= Self::inline_capacity()`.
///
/// ```rust
/// use smallvec::SmallVec;
@@ -970,7 +966,7 @@ impl<A: Array> SmallVec<A> {
}
/// Returns a tuple with (data ptr, len, capacity)
- /// Useful to get all SmallVec properties with a single check of the current storage variant.
+ /// Useful to get all `SmallVec` properties with a single check of the current storage variant.
#[inline]
fn triple(&self) -> (ConstNonNull<A::Item>, usize, usize) {
unsafe {
@@ -1055,13 +1051,12 @@ impl<A: Array> SmallVec<A> {
}
}
-
#[cfg(feature = "drain_filter")]
/// Creates an iterator which uses a closure to determine if an element should be removed.
- ///
+ ///
/// If the closure returns true, the element is removed and yielded. If the closure returns
/// false, the element will remain in the vector and will not be yielded by the iterator.
- ///
+ ///
/// Using this method is equivalent to the following code:
/// ```
/// # use smallvec::SmallVec;
@@ -1076,7 +1071,7 @@ impl<A: Array> SmallVec<A> {
/// i += 1;
/// }
/// }
- ///
+ ///
/// # assert_eq!(vec, SmallVec::<[i32; 8]>::from_slice(&[1i32, 4, 5]));
/// ```
/// ///
@@ -1476,7 +1471,7 @@ impl<A: Array> SmallVec<A> {
}
}
- /// Convert a SmallVec to a Vec, without reallocating if the SmallVec has already spilled onto
+ /// Convert a `SmallVec` to a `Vec`, without reallocating if the `SmallVec` has already spilled onto
/// the heap.
pub fn into_vec(mut self) -> Vec<A::Item> {
if self.spilled() {
@@ -1499,10 +1494,10 @@ impl<A: Array> SmallVec<A> {
self.into_vec().into_boxed_slice()
}
- /// Convert the SmallVec into an `A` if possible. Otherwise return `Err(Self)`.
+ /// Convert the `SmallVec` into an `A` if possible. Otherwise return `Err(Self)`.
///
- /// This method returns `Err(Self)` if the SmallVec is too short (and the `A` contains uninitialized elements),
- /// or if the SmallVec is too long (and all the elements were spilled to the heap).
+ /// This method returns `Err(Self)` if the `SmallVec` is too short (and the `A` contains uninitialized elements),
+ /// or if the `SmallVec` is too long (and all the elements were spilled to the heap).
pub fn into_inner(self) -> Result<A, Self> {
if self.spilled() || self.len() != A::size() {
// Note: A::size, not Self::inline_capacity
@@ -1596,7 +1591,7 @@ impl<A: Array> SmallVec<A> {
///
/// If `new_len` is greater than `len`, the `SmallVec` is extended by the difference, with each
/// additional slot filled with the result of calling the closure `f`. The return values from `f`
- //// will end up in the `SmallVec` in the order they have been generated.
+ /// will end up in the `SmallVec` in the order they have been generated.
///
/// If `new_len` is less than `len`, the `SmallVec` is simply truncated.
///
@@ -1604,7 +1599,7 @@ impl<A: Array> SmallVec<A> {
/// value, use `resize`. If you want to use the `Default` trait to generate values, you can pass
/// `Default::default()` as the second argument.
///
- /// Added for std::vec::Vec compatibility (added in Rust 1.33.0)
+ /// Added for `std::vec::Vec` compatibility (added in Rust 1.33.0)
///
/// ```
/// # use smallvec::{smallvec, SmallVec};
@@ -1667,8 +1662,7 @@ impl<A: Array> SmallVec<A> {
/// # Examples
///
/// ```
- /// # #[macro_use] extern crate smallvec;
- /// # use smallvec::SmallVec;
+ /// # use smallvec::{smallvec, SmallVec};
/// use std::mem;
/// use std::ptr;
///
@@ -2322,7 +2316,7 @@ impl<'a, A: Array> IntoIterator for &'a mut SmallVec<A> {
}
}
-/// Types that can be used as the backing store for a SmallVec
+/// Types that can be used as the backing store for a [`SmallVec`].
pub unsafe trait Array {
/// The type of the array's elements.
type Item;
@@ -2332,7 +2326,7 @@ pub unsafe trait Array {
/// Set the length of the vec when the `SetLenOnDrop` value goes out of scope.
///
-/// Copied from https://github.com/rust-lang/rust/pull/36355
+/// Copied from <https://github.com/rust-lang/rust/pull/36355>
struct SetLenOnDrop<'a> {
len: &'a mut usize,
local_len: usize,
@@ -2390,9 +2384,23 @@ impl<T, const N: usize> SmallVec<[T; N]> {
data: SmallVecData::from_const(MaybeUninit::new(items)),
}
}
+
+ /// Constructs a new `SmallVec` on the stack from an array without
+ /// copying elements. Also sets the length. The user is responsible
+ /// for ensuring that `len <= N`.
+ ///
+ /// This is a `const` version of [`SmallVec::from_buf_and_len_unchecked`] that is enabled by the feature `const_new`, with the limitation that it only works for arrays.
+ #[cfg_attr(docsrs, doc(cfg(feature = "const_new")))]
+ #[inline]
+ pub const unsafe fn from_const_with_len_unchecked(items: [T; N], len: usize) -> Self {
+ SmallVec {
+ capacity: len,
+ data: SmallVecData::from_const(MaybeUninit::new(items)),
+ }
+ }
}
-#[cfg(all(feature = "const_generics", not(doc)))]
+#[cfg(feature = "const_generics")]
#[cfg_attr(docsrs, doc(cfg(feature = "const_generics")))]
unsafe impl<T, const N: usize> Array for [T; N] {
type Item = T;
@@ -2402,7 +2410,7 @@ unsafe impl<T, const N: usize> Array for [T; N] {
}
}
-#[cfg(any(not(feature = "const_generics"), doc))]
+#[cfg(not(feature = "const_generics"))]
macro_rules! impl_array(
($($size:expr),+) => {
$(
@@ -2415,7 +2423,7 @@ macro_rules! impl_array(
}
);
-#[cfg(any(not(feature = "const_generics"), doc))]
+#[cfg(not(feature = "const_generics"))]
impl_array!(
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
26, 27, 28, 29, 30, 31, 32, 36, 0x40, 0x60, 0x80, 0x100, 0x200, 0x400, 0x600, 0x800, 0x1000,
diff --git a/third_party/rust/smallvec/src/tests.rs b/third_party/rust/smallvec/src/tests.rs
index bb39ddeb31..3eab846bf2 100644
--- a/third_party/rust/smallvec/src/tests.rs
+++ b/third_party/rust/smallvec/src/tests.rs
@@ -72,13 +72,13 @@ pub fn test_double_spill() {
);
}
-/// https://github.com/servo/rust-smallvec/issues/4
+// https://github.com/servo/rust-smallvec/issues/4
#[test]
fn issue_4() {
SmallVec::<[Box<u32>; 2]>::new();
}
-/// https://github.com/servo/rust-smallvec/issues/5
+// https://github.com/servo/rust-smallvec/issues/5
#[test]
fn issue_5() {
assert!(Some(SmallVec::<[&u32; 2]>::new()).is_some());
@@ -833,12 +833,9 @@ fn test_write() {
}
#[cfg(feature = "serde")]
-extern crate bincode;
-
-#[cfg(feature = "serde")]
#[test]
fn test_serde() {
- use self::bincode::{config, deserialize};
+ use bincode::{config, deserialize};
let mut small_vec: SmallVec<[i32; 2]> = SmallVec::new();
small_vec.push(1);
let encoded = config().limit(100).serialize(&small_vec).unwrap();
@@ -925,6 +922,12 @@ fn const_new() {
assert_eq!(v.len(), 2);
assert_eq!(v[0], 1);
assert_eq!(v[1], 4);
+ let v = const_new_with_len();
+ assert_eq!(v.capacity(), 4);
+ assert_eq!(v.len(), 3);
+ assert_eq!(v[0], 2);
+ assert_eq!(v[1], 5);
+ assert_eq!(v[2], 7);
}
#[cfg(feature = "const_new")]
const fn const_new_inner() -> SmallVec<[i32; 4]> {
@@ -938,6 +941,12 @@ const fn const_new_inline_sized() -> SmallVec<[i32; 4]> {
const fn const_new_inline_args() -> SmallVec<[i32; 2]> {
crate::smallvec_inline![1, 4]
}
+#[cfg(feature = "const_new")]
+const fn const_new_with_len() -> SmallVec<[i32; 4]> {
+ unsafe {
+ SmallVec::<[i32; 4]>::from_const_with_len_unchecked([2, 5, 7, 0], 3)
+ }
+}
#[test]
fn empty_macro() {
diff --git a/third_party/rust/thiserror-impl/.cargo-checksum.json b/third_party/rust/thiserror-impl/.cargo-checksum.json
index f091d556de..6adcf117c2 100644
--- a/third_party/rust/thiserror-impl/.cargo-checksum.json
+++ b/third_party/rust/thiserror-impl/.cargo-checksum.json
@@ -1 +1 @@
-{"files":{"Cargo.toml":"b387bf85702168709e2619d65e68e1860dca1021ddd91346a09ec8bd1cc80471","LICENSE-APACHE":"62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a","LICENSE-MIT":"23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3","src/ast.rs":"e2fbc32e91cd462757107c1f8ab2f888f08c865ac71270583f9e11b83e3c32ca","src/attr.rs":"ad5319f053a18ec3841300b2ae553ffa005749ba7099fe4b318126223e4dcbdf","src/expand.rs":"9469753e0949556848183084e0f22521c6300d38ca0de196441446c50f350d3c","src/fmt.rs":"d63d39120c18712596f9f2a1715821148c2becd4d8bad5bc1b307210a84dbe98","src/generics.rs":"2076cde22271be355a8131a77add4b93f83ab0af4317cd2df5471fffa4f95c66","src/lib.rs":"5eea86c771e643328ad9bc3b881cce4bf9d50adae1b33e0d07645bdd9044003d","src/prop.rs":"5ba613e38430831259f20b258f33d57dcb783fbaeeb49e5faffa7b2a7be99e67","src/span.rs":"430460a4fa0d1fa9c627c1ddd575d2b101778fea84217591e1a93a5f6a2a0132","src/valid.rs":"ac95253944fd360d3578d0643a7baabb2cfa6bf9fbced7a6ce1f7b0529a3bb98"},"package":"fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471"} \ No newline at end of file
+{"files":{"Cargo.toml":"0a1dbd3bcb92455b7ed1d661e0c05958a64fd694a18357d3c61a8e1f2cffb9bd","LICENSE-APACHE":"62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a","LICENSE-MIT":"23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3","src/ast.rs":"e2fbc32e91cd462757107c1f8ab2f888f08c865ac71270583f9e11b83e3c32ca","src/attr.rs":"7451fc0b0a06649e62679c7558e5aeb0e2019400c170cde54c23289ef44188f7","src/expand.rs":"9469753e0949556848183084e0f22521c6300d38ca0de196441446c50f350d3c","src/fmt.rs":"5d1cefc012403c2d4ff7ab2513c0ec559166df4271d5983a6463939b5ec8c3e1","src/generics.rs":"2076cde22271be355a8131a77add4b93f83ab0af4317cd2df5471fffa4f95c66","src/lib.rs":"5eea86c771e643328ad9bc3b881cce4bf9d50adae1b33e0d07645bdd9044003d","src/prop.rs":"5ba613e38430831259f20b258f33d57dcb783fbaeeb49e5faffa7b2a7be99e67","src/span.rs":"430460a4fa0d1fa9c627c1ddd575d2b101778fea84217591e1a93a5f6a2a0132","src/valid.rs":"ac95253944fd360d3578d0643a7baabb2cfa6bf9fbced7a6ce1f7b0529a3bb98"},"package":"a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81"} \ No newline at end of file
diff --git a/third_party/rust/thiserror-impl/Cargo.toml b/third_party/rust/thiserror-impl/Cargo.toml
index fffdf5c3a4..5445c126ec 100644
--- a/third_party/rust/thiserror-impl/Cargo.toml
+++ b/third_party/rust/thiserror-impl/Cargo.toml
@@ -13,7 +13,7 @@
edition = "2021"
rust-version = "1.56"
name = "thiserror-impl"
-version = "1.0.56"
+version = "1.0.57"
authors = ["David Tolnay <dtolnay@gmail.com>"]
description = "Implementation detail of the `thiserror` crate"
license = "MIT OR Apache-2.0"
diff --git a/third_party/rust/thiserror-impl/src/attr.rs b/third_party/rust/thiserror-impl/src/attr.rs
index 4beb8c9628..269c69eca5 100644
--- a/third_party/rust/thiserror-impl/src/attr.rs
+++ b/third_party/rust/thiserror-impl/src/attr.rs
@@ -1,6 +1,7 @@
use proc_macro2::{Delimiter, Group, Span, TokenStream, TokenTree};
use quote::{format_ident, quote, ToTokens};
use std::collections::BTreeSet as Set;
+use syn::parse::discouraged::Speculative;
use syn::parse::ParseStream;
use syn::{
braced, bracketed, parenthesized, token, Attribute, Error, Ident, Index, LitInt, LitStr, Meta,
@@ -20,6 +21,7 @@ pub struct Display<'a> {
pub original: &'a Attribute,
pub fmt: LitStr,
pub args: TokenStream,
+ pub requires_fmt_machinery: bool,
pub has_bonus_display: bool,
pub implied_bounds: Set<(usize, Trait)>,
}
@@ -103,10 +105,24 @@ fn parse_error_attribute<'a>(attrs: &mut Attrs<'a>, attr: &'a Attribute) -> Resu
return Ok(());
}
+ let fmt: LitStr = input.parse()?;
+
+ let ahead = input.fork();
+ ahead.parse::<Option<Token![,]>>()?;
+ let args = if ahead.is_empty() {
+ input.advance_to(&ahead);
+ TokenStream::new()
+ } else {
+ parse_token_expr(input, false)?
+ };
+
+ let requires_fmt_machinery = !args.is_empty();
+
let display = Display {
original: attr,
- fmt: input.parse()?,
- args: parse_token_expr(input, false)?,
+ fmt,
+ args,
+ requires_fmt_machinery,
has_bonus_display: false,
implied_bounds: Set::new(),
};
@@ -196,8 +212,18 @@ impl ToTokens for Display<'_> {
fn to_tokens(&self, tokens: &mut TokenStream) {
let fmt = &self.fmt;
let args = &self.args;
- tokens.extend(quote! {
- ::core::write!(__formatter, #fmt #args)
+
+ // Currently `write!(f, "text")` produces less efficient code than
+ // `f.write_str("text")`. We recognize the case when the format string
+ // has no braces and no interpolated values, and generate simpler code.
+ tokens.extend(if self.requires_fmt_machinery {
+ quote! {
+ ::core::write!(__formatter, #fmt #args)
+ }
+ } else {
+ quote! {
+ __formatter.write_str(#fmt)
+ }
});
}
}
diff --git a/third_party/rust/thiserror-impl/src/fmt.rs b/third_party/rust/thiserror-impl/src/fmt.rs
index 807dfb9677..b38b7bf1f5 100644
--- a/third_party/rust/thiserror-impl/src/fmt.rs
+++ b/third_party/rust/thiserror-impl/src/fmt.rs
@@ -32,7 +32,10 @@ impl Display<'_> {
}
}
+ self.requires_fmt_machinery = self.requires_fmt_machinery || fmt.contains('}');
+
while let Some(brace) = read.find('{') {
+ self.requires_fmt_machinery = true;
out += &read[..brace + 1];
read = &read[brace + 1..];
if read.starts_with('{') {
diff --git a/third_party/rust/thiserror/.cargo-checksum.json b/third_party/rust/thiserror/.cargo-checksum.json
index cee7027f74..efb4bd5017 100644
--- a/third_party/rust/thiserror/.cargo-checksum.json
+++ b/third_party/rust/thiserror/.cargo-checksum.json
@@ -1 +1 @@
-{"files":{"Cargo.toml":"d78abe9a046d804b1c51260cefe516c36dfbb9994edfe47175bbd838cd46f68f","LICENSE-APACHE":"62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a","LICENSE-MIT":"23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3","README.md":"106c5a937767d49503e1fc5eae1b924f57f15decd8583720a3c652483e348a64","build.rs":"532f6ac494cdddfad3267067a46969a8052b02c1bd94567361f7103ab0dc8c28","build/probe.rs":"3245569a228727091f335db44c7c2f729729b2dfac9f46c1143eb179439f223d","rust-toolchain.toml":"6bbb61302978c736b2da03e4fb40e3beab908f85d533ab46fd541e637b5f3e0f","src/aserror.rs":"cf3c98abb2b9e06afa3c4aba0df14938417c3e330315863437561cbb3573888b","src/display.rs":"0adeeeb524c6bee06de179d54e82a43dc12d2c5b7f69f6fd268ba4611ebf5233","src/lib.rs":"074676e9bbe9402ebe3f41dedcaa289774e47e075d10e2f523d2dcbd8f648f79","src/provide.rs":"8007e22953bacfcc57bb7d12a03fbeb0acece5d2ec889cf55522a4e071d26df3","tests/compiletest.rs":"022a8e400ef813d7ea1875b944549cee5125f6a995dc33e93b48cba3e1b57bd1","tests/test_backtrace.rs":"a8f038490fb881463c0e8c36557617c47cf2d181f16c00525d4d139c7964fade","tests/test_deprecated.rs":"7b80a10f090a3982da017556d3d71398abcead59afd8278c7b9d9b1f7b66c7b3","tests/test_display.rs":"28e0f938fe0f6354529c35722eff04830451e27718145c27522c9acf7f8a6639","tests/test_error.rs":"d06dca3c38f22d7ce4e27dadd6c0f78e5cefe3a2ebbc5fe44abc9ddd5ee1985f","tests/test_expr.rs":"d35b11040ebc547467f52571051854e3e094d52b8e229fa3d44700d5f40959a2","tests/test_from.rs":"36bd22be7b048cd187a19076aeac1456040f20a0b677b01c6003998b63439ea1","tests/test_generics.rs":"adc61f0d5fe8d53796848d44fb0373be5eab19a1eeb6a7172bc6f0dd7b91199c","tests/test_lints.rs":"c17d79d77edfcdd4b8f6dcdcd1c70ad065cfbc747e1a618ac6343315d0b59ea4","tests/test_option.rs":"ac30d929c019d6c54d1c1792b09e43c18dc0e4123b82051ff9e5db5e63c15e43","tests/test_path.rs":"ef5452c7e828a0179f5ace7e19f95b9762aa887caf10244adbfe36ded712c090","tests/test_source.rs":"f2f04f11bf8a709eddb1c68f113cda0c2be87e56800d6b9d991bedd545b4642f","tests/test_transparent.rs":"cd8d5be14d00d610a1782104bea6c013618501dab5c3625178ecfcf66e31f939","tests/ui/bad-field-attr.rs":"c5b567e3091969a01061843fb2d95c5e1aa3fa81edfeecdf416a84a6fba40aa8","tests/ui/bad-field-attr.stderr":"78f576d5ec66464a77f1cdf0f5bb7dcdf18f7f04f1165983a6239ec59d908ea3","tests/ui/concat-display.rs":"3995bd6b3bdd67df7bb16499775d89600c0dd20895633fe807396a64c117078d","tests/ui/concat-display.stderr":"256dfde61ee689ebe51588b135e2e030bdf95ba5adef1cb59f588c797bbdeef2","tests/ui/duplicate-enum-source.rs":"bfe28ce18042d446a76c7411aa233598211ce1157fdd3cb87bff3b3fa7c33131","tests/ui/duplicate-enum-source.stderr":"3d32fead420b27b4497be49080bc3b78f7f0ba339ead3de6c94e5dc20302c18f","tests/ui/duplicate-fmt.rs":"af53b66445bcce076a114376747f176b42c060a156563a41ccb638ae14c451fd","tests/ui/duplicate-fmt.stderr":"998bb121ce6f1595fd99529a7a1b06451b6bf476924337dce5524a83a7a5f1a1","tests/ui/duplicate-struct-source.rs":"f3d5f8e3d6fccfcdbb630db291353709583a920c6bf46f9f9de9966b67ea4c0f","tests/ui/duplicate-struct-source.stderr":"fb761d76668ac42357cf37b03c0abdbae5de0a828034990850291c9cb6ab766d","tests/ui/duplicate-transparent.rs":"41a9447e85f1a47027023442acde55c3d8610ec46d91b39bd43a42d7a004d747","tests/ui/duplicate-transparent.stderr":"4975abad43e973df158f18098d9bcb9dc39f8e75d3e733ed5d6620d1ee065c11","tests/ui/fallback-impl-with-display.rs":"141a8efbabe3fdac584bec8a61e6cceb58a34a70b825f6277037bf9d591150eb","tests/ui/fallback-impl-with-display.stderr":"1b3dad712b97598fbee70125471de1a8106eb161d42ce1f790ae07be8c8984ba","tests/ui/from-backtrace-backtrace.rs":"0caac64486c0eb9c076553c0502d468fbc477602a9a2595165a1dcd95524e5ff","tests/ui/from-backtrace-backtrace.stderr":"e24156ae0828878f3282341732b6e032eaa8cb4b4db366a6b5437ed0731d40a7","tests/ui/from-not-source.rs":"744a55aeffe11066830159ac023c33aaa5576e313b341fa24440ee13dfe3ac98","tests/ui/from-not-source.stderr":"525038e8b841707b927434cca4549168f73bd305faca17552a0d1fffa542ccc4","tests/ui/invalid-input-impl-anyway.rs":"6de91f71ddf038dffa3b9da33763a2ec3a5aa0047528e19ba998d5efe3aada5b","tests/ui/invalid-input-impl-anyway.stderr":"fa2725053cd87fc37f87546b377e6e5eed95c45e2a960863303b21a1935fdddb","tests/ui/lifetime.rs":"e72e0391695e47fcd07edbf3819f114e468e2097086ec687781c7c8d6b4b7da7","tests/ui/lifetime.stderr":"d889a23f71324afe95dafc5f9d15337fbdbc9977cb8924f0cafe3a3becf4ced7","tests/ui/missing-display.rs":"c1fd1bc0ec0fb103d7f7b128837f717d49485662839899d570b3c983f1332888","tests/ui/missing-display.stderr":"a8de0f1559da9320ee99ef334937d532d9e9f40a32ed7f8ce56fb465628bff96","tests/ui/missing-fmt.rs":"bc9e2830e54c2474ff6c27a766ed3dee88d29e40f93f30e8d64d63233866c17d","tests/ui/missing-fmt.stderr":"9a20ccee9b660fe31a5b3199307b48580bb8305cb9ce33d97d3fc767a0cfc614","tests/ui/no-display.rs":"962245372272d23e9833311c15e73221b3c7da822a2ff90189613af56ffb5c2e","tests/ui/no-display.stderr":"9e2161baf5f66ab22370322f2e66e7633bf04b1ec07ef656e904b984bcc45d09","tests/ui/source-enum-not-error.rs":"3add5e7b4503d964bcae0848904822e1473c1d08c5a146c2df5347314ce1b8df","tests/ui/source-enum-not-error.stderr":"c093580558a259489eef92728d19aeca3b6c27ec17c39a02a75f0a924b095675","tests/ui/source-enum-unnamed-field-not-error.rs":"a98989e908b84a8e6e6dceef02af7bdd1098a444d229950f67ed4f54d55c62e7","tests/ui/source-enum-unnamed-field-not-error.stderr":"45b520f44e6fd10792d7f48e8ca7bc89850aa039278cba7c9f6ea11aa6378494","tests/ui/source-struct-not-error.rs":"09fb7713637242dca9192585a6daeb8d732dc1c1d0fa522b74f1c98618e6d949","tests/ui/source-struct-not-error.stderr":"66fb5fa85d59f11d8b5f7ec99469a843c51943b0010e554bdf56376a0614a2ca","tests/ui/source-struct-unnamed-field-not-error.rs":"eee605a9aafbb093666393e25cef4f7d7b8e90d518b9fadbdbed9685c66ccfcd","tests/ui/source-struct-unnamed-field-not-error.stderr":"38e4bd380ff1637c179b277ea1beb0a1ce688d191e5a9502ee69ab752e9ba70f","tests/ui/transparent-display.rs":"b3c59583eb64b0b5a246444456d03cf52d51bcdc08885023600dbb44fd87e5f2","tests/ui/transparent-display.stderr":"16d538914e0d92026bde4b4bec75660217da9ecc6b621d12d2eb81d33ed1d1da","tests/ui/transparent-enum-many.rs":"2a40a764fb4683bff57973eec61507a6c00f7d4d7a32da6e7bd0190c2e445434","tests/ui/transparent-enum-many.stderr":"f1d78c1d6d8edbef153420db4fb9ca3dc6076fa043b5b1bc0cd291daa417a3ea","tests/ui/transparent-enum-not-error.rs":"f6315993e68bc71d6d4d39afa4d059695308ef785f92cc0d1df3e9ff55be2a9a","tests/ui/transparent-enum-not-error.stderr":"e485c39f861ab66a6a41f0a423b1b13ba277968210284148883b350b5d629ccc","tests/ui/transparent-enum-source.rs":"18f606a98ac0a53f08dc56f5f923b9cbe75d25ed34479c777b48dac305d5968c","tests/ui/transparent-enum-source.stderr":"1b2e0ac53951034575d43ec0396c4e2b3cfb272db2aef8d6baa13a7e1632cc84","tests/ui/transparent-enum-unnamed-field-not-error.rs":"0c720567e9e0f87921055dfa9f607661725377633445e01a4b5048c7a7a50a85","tests/ui/transparent-enum-unnamed-field-not-error.stderr":"6b8ba244eba94006039d10f35bdd7526136bcff4751b13313ab12283d5bdc24c","tests/ui/transparent-struct-many.rs":"72c6b6c1a44c203d3bc68989b2f1ec092531ef75b745432824c3776c290326f6","tests/ui/transparent-struct-many.stderr":"7bd0536dbb54a0ce7d4a8e66ca7624a1b132d8a1d1e4fecca642ec77494ac01c","tests/ui/transparent-struct-not-error.rs":"fd814d3141c9182b1267b558d9daef8dd6e8b6462e1ad42b197f3a1c0703dce2","tests/ui/transparent-struct-not-error.stderr":"bb8b856515b34711c046f195b4267d2bb21309c5d3ac0a39d6660c55dadafb41","tests/ui/transparent-struct-source.rs":"863fa691ed7d27e8767da58d9ee11fd40d6642274b36338ca1074c07964ea2b3","tests/ui/transparent-struct-source.stderr":"267dab65929e67d32347fb467a00b43af931f8205d727d7671938580217fc70e","tests/ui/transparent-struct-unnamed-field-not-error.rs":"fbff5874be44a5dcc347693d7929537256b187dfec467ed72c9968c095228d8d","tests/ui/transparent-struct-unnamed-field-not-error.stderr":"684d4ed4325f2e7fb95c84a6d231585b2be313990c41222fda66b99a84e7b884","tests/ui/unexpected-field-fmt.rs":"29fba7b4d81c642ec8e47cfe053aa515acf9080a86d65e685363a48993becfe3","tests/ui/unexpected-field-fmt.stderr":"20731c4a08af04bed3ff513903adadd690b6bc532b15604557e7f25575a8338f","tests/ui/unexpected-struct-source.rs":"c6cbe882d622635c216feb8290b1bd536ce0ec4feee16bc087667a21b3641d5c","tests/ui/unexpected-struct-source.stderr":"7c8227513478f6cc09e8a28be337c8a0e758a06ca5978d774c91bd43c4a54043","tests/ui/union.rs":"331adff27cebd8b95b03b6742cc8247331fda1f961e1590ed39c8d39f50cf1d8","tests/ui/union.stderr":"5f67ad29753d6fb14bc03aef7d4a1f660ee7796e469c037efbf8b13456934ad3"},"package":"d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad"} \ No newline at end of file
+{"files":{"Cargo.toml":"315a0379d474f762b74a40fb83f8a52b7efe0f5a352e7a7ab0b9d118f32f609e","LICENSE-APACHE":"62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a","LICENSE-MIT":"23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3","README.md":"106c5a937767d49503e1fc5eae1b924f57f15decd8583720a3c652483e348a64","build.rs":"532f6ac494cdddfad3267067a46969a8052b02c1bd94567361f7103ab0dc8c28","build/probe.rs":"3245569a228727091f335db44c7c2f729729b2dfac9f46c1143eb179439f223d","rust-toolchain.toml":"6bbb61302978c736b2da03e4fb40e3beab908f85d533ab46fd541e637b5f3e0f","src/aserror.rs":"cf3c98abb2b9e06afa3c4aba0df14938417c3e330315863437561cbb3573888b","src/display.rs":"0adeeeb524c6bee06de179d54e82a43dc12d2c5b7f69f6fd268ba4611ebf5233","src/lib.rs":"6903e561c2a83901061ed717061040a1d0ffb4296c2c52e19e617e1ca6c07211","src/provide.rs":"8007e22953bacfcc57bb7d12a03fbeb0acece5d2ec889cf55522a4e071d26df3","tests/compiletest.rs":"022a8e400ef813d7ea1875b944549cee5125f6a995dc33e93b48cba3e1b57bd1","tests/test_backtrace.rs":"a8f038490fb881463c0e8c36557617c47cf2d181f16c00525d4d139c7964fade","tests/test_deprecated.rs":"7b80a10f090a3982da017556d3d71398abcead59afd8278c7b9d9b1f7b66c7b3","tests/test_display.rs":"91a6bf704fd78a5565330f438f30ae4088aa8bc7ac5e1412401e440072408c03","tests/test_error.rs":"d06dca3c38f22d7ce4e27dadd6c0f78e5cefe3a2ebbc5fe44abc9ddd5ee1985f","tests/test_expr.rs":"d35b11040ebc547467f52571051854e3e094d52b8e229fa3d44700d5f40959a2","tests/test_from.rs":"36bd22be7b048cd187a19076aeac1456040f20a0b677b01c6003998b63439ea1","tests/test_generics.rs":"adc61f0d5fe8d53796848d44fb0373be5eab19a1eeb6a7172bc6f0dd7b91199c","tests/test_lints.rs":"c17d79d77edfcdd4b8f6dcdcd1c70ad065cfbc747e1a618ac6343315d0b59ea4","tests/test_option.rs":"ac30d929c019d6c54d1c1792b09e43c18dc0e4123b82051ff9e5db5e63c15e43","tests/test_path.rs":"ef5452c7e828a0179f5ace7e19f95b9762aa887caf10244adbfe36ded712c090","tests/test_source.rs":"f2f04f11bf8a709eddb1c68f113cda0c2be87e56800d6b9d991bedd545b4642f","tests/test_transparent.rs":"cd8d5be14d00d610a1782104bea6c013618501dab5c3625178ecfcf66e31f939","tests/ui/bad-field-attr.rs":"c5b567e3091969a01061843fb2d95c5e1aa3fa81edfeecdf416a84a6fba40aa8","tests/ui/bad-field-attr.stderr":"78f576d5ec66464a77f1cdf0f5bb7dcdf18f7f04f1165983a6239ec59d908ea3","tests/ui/concat-display.rs":"3995bd6b3bdd67df7bb16499775d89600c0dd20895633fe807396a64c117078d","tests/ui/concat-display.stderr":"256dfde61ee689ebe51588b135e2e030bdf95ba5adef1cb59f588c797bbdeef2","tests/ui/duplicate-enum-source.rs":"bfe28ce18042d446a76c7411aa233598211ce1157fdd3cb87bff3b3fa7c33131","tests/ui/duplicate-enum-source.stderr":"3d32fead420b27b4497be49080bc3b78f7f0ba339ead3de6c94e5dc20302c18f","tests/ui/duplicate-fmt.rs":"af53b66445bcce076a114376747f176b42c060a156563a41ccb638ae14c451fd","tests/ui/duplicate-fmt.stderr":"998bb121ce6f1595fd99529a7a1b06451b6bf476924337dce5524a83a7a5f1a1","tests/ui/duplicate-struct-source.rs":"f3d5f8e3d6fccfcdbb630db291353709583a920c6bf46f9f9de9966b67ea4c0f","tests/ui/duplicate-struct-source.stderr":"fb761d76668ac42357cf37b03c0abdbae5de0a828034990850291c9cb6ab766d","tests/ui/duplicate-transparent.rs":"41a9447e85f1a47027023442acde55c3d8610ec46d91b39bd43a42d7a004d747","tests/ui/duplicate-transparent.stderr":"4975abad43e973df158f18098d9bcb9dc39f8e75d3e733ed5d6620d1ee065c11","tests/ui/fallback-impl-with-display.rs":"141a8efbabe3fdac584bec8a61e6cceb58a34a70b825f6277037bf9d591150eb","tests/ui/fallback-impl-with-display.stderr":"1b3dad712b97598fbee70125471de1a8106eb161d42ce1f790ae07be8c8984ba","tests/ui/from-backtrace-backtrace.rs":"0caac64486c0eb9c076553c0502d468fbc477602a9a2595165a1dcd95524e5ff","tests/ui/from-backtrace-backtrace.stderr":"e24156ae0828878f3282341732b6e032eaa8cb4b4db366a6b5437ed0731d40a7","tests/ui/from-not-source.rs":"744a55aeffe11066830159ac023c33aaa5576e313b341fa24440ee13dfe3ac98","tests/ui/from-not-source.stderr":"525038e8b841707b927434cca4549168f73bd305faca17552a0d1fffa542ccc4","tests/ui/invalid-input-impl-anyway.rs":"6de91f71ddf038dffa3b9da33763a2ec3a5aa0047528e19ba998d5efe3aada5b","tests/ui/invalid-input-impl-anyway.stderr":"fa2725053cd87fc37f87546b377e6e5eed95c45e2a960863303b21a1935fdddb","tests/ui/lifetime.rs":"e72e0391695e47fcd07edbf3819f114e468e2097086ec687781c7c8d6b4b7da7","tests/ui/lifetime.stderr":"d889a23f71324afe95dafc5f9d15337fbdbc9977cb8924f0cafe3a3becf4ced7","tests/ui/missing-display.rs":"c1fd1bc0ec0fb103d7f7b128837f717d49485662839899d570b3c983f1332888","tests/ui/missing-display.stderr":"a8de0f1559da9320ee99ef334937d532d9e9f40a32ed7f8ce56fb465628bff96","tests/ui/missing-fmt.rs":"bc9e2830e54c2474ff6c27a766ed3dee88d29e40f93f30e8d64d63233866c17d","tests/ui/missing-fmt.stderr":"9a20ccee9b660fe31a5b3199307b48580bb8305cb9ce33d97d3fc767a0cfc614","tests/ui/no-display.rs":"962245372272d23e9833311c15e73221b3c7da822a2ff90189613af56ffb5c2e","tests/ui/no-display.stderr":"c145dbdd39e145b5c6f26f8504fbf1e33efa2eada37e09900b39dd62667f22d7","tests/ui/source-enum-not-error.rs":"3add5e7b4503d964bcae0848904822e1473c1d08c5a146c2df5347314ce1b8df","tests/ui/source-enum-not-error.stderr":"aeba0a8a0084833e470b6be2250370809f53c279ad603232af5302b9de9f8cce","tests/ui/source-enum-unnamed-field-not-error.rs":"a98989e908b84a8e6e6dceef02af7bdd1098a444d229950f67ed4f54d55c62e7","tests/ui/source-enum-unnamed-field-not-error.stderr":"4f3d90525dd462e67f633e83b26acec75d9af3626e40d28ded2c2438e0c73192","tests/ui/source-struct-not-error.rs":"09fb7713637242dca9192585a6daeb8d732dc1c1d0fa522b74f1c98618e6d949","tests/ui/source-struct-not-error.stderr":"b45eb66f078fec68d44feff1f8d6a676c341e9e9d9acb35daa58ec1ea20f9dae","tests/ui/source-struct-unnamed-field-not-error.rs":"eee605a9aafbb093666393e25cef4f7d7b8e90d518b9fadbdbed9685c66ccfcd","tests/ui/source-struct-unnamed-field-not-error.stderr":"10e408f71c1b61880b1d52739f222ec58a66be70a1df17e44c536fe0f9ffe2a6","tests/ui/transparent-display.rs":"b3c59583eb64b0b5a246444456d03cf52d51bcdc08885023600dbb44fd87e5f2","tests/ui/transparent-display.stderr":"16d538914e0d92026bde4b4bec75660217da9ecc6b621d12d2eb81d33ed1d1da","tests/ui/transparent-enum-many.rs":"2a40a764fb4683bff57973eec61507a6c00f7d4d7a32da6e7bd0190c2e445434","tests/ui/transparent-enum-many.stderr":"f1d78c1d6d8edbef153420db4fb9ca3dc6076fa043b5b1bc0cd291daa417a3ea","tests/ui/transparent-enum-not-error.rs":"f6315993e68bc71d6d4d39afa4d059695308ef785f92cc0d1df3e9ff55be2a9a","tests/ui/transparent-enum-not-error.stderr":"3a5fe056cd4566f402b03cb591394e0ba85bd74da53df3c8d50bda4a05c2e5ba","tests/ui/transparent-enum-source.rs":"18f606a98ac0a53f08dc56f5f923b9cbe75d25ed34479c777b48dac305d5968c","tests/ui/transparent-enum-source.stderr":"1b2e0ac53951034575d43ec0396c4e2b3cfb272db2aef8d6baa13a7e1632cc84","tests/ui/transparent-enum-unnamed-field-not-error.rs":"0c720567e9e0f87921055dfa9f607661725377633445e01a4b5048c7a7a50a85","tests/ui/transparent-enum-unnamed-field-not-error.stderr":"6d84e9a7f4e88daba12931a6c1508be60bb19125d06e44fa2fa205126d953bb1","tests/ui/transparent-struct-many.rs":"72c6b6c1a44c203d3bc68989b2f1ec092531ef75b745432824c3776c290326f6","tests/ui/transparent-struct-many.stderr":"7bd0536dbb54a0ce7d4a8e66ca7624a1b132d8a1d1e4fecca642ec77494ac01c","tests/ui/transparent-struct-not-error.rs":"fd814d3141c9182b1267b558d9daef8dd6e8b6462e1ad42b197f3a1c0703dce2","tests/ui/transparent-struct-not-error.stderr":"ac7634ea72096d8a5c1a91fd4f1b45ef870130a2698d9ae7c6530cec2f9799d5","tests/ui/transparent-struct-source.rs":"863fa691ed7d27e8767da58d9ee11fd40d6642274b36338ca1074c07964ea2b3","tests/ui/transparent-struct-source.stderr":"267dab65929e67d32347fb467a00b43af931f8205d727d7671938580217fc70e","tests/ui/transparent-struct-unnamed-field-not-error.rs":"fbff5874be44a5dcc347693d7929537256b187dfec467ed72c9968c095228d8d","tests/ui/transparent-struct-unnamed-field-not-error.stderr":"ea99d5422348c2588ad7374360e2a24649f040b9c5614c9308eff958f61960ec","tests/ui/unexpected-field-fmt.rs":"29fba7b4d81c642ec8e47cfe053aa515acf9080a86d65e685363a48993becfe3","tests/ui/unexpected-field-fmt.stderr":"20731c4a08af04bed3ff513903adadd690b6bc532b15604557e7f25575a8338f","tests/ui/unexpected-struct-source.rs":"c6cbe882d622635c216feb8290b1bd536ce0ec4feee16bc087667a21b3641d5c","tests/ui/unexpected-struct-source.stderr":"7c8227513478f6cc09e8a28be337c8a0e758a06ca5978d774c91bd43c4a54043","tests/ui/union.rs":"331adff27cebd8b95b03b6742cc8247331fda1f961e1590ed39c8d39f50cf1d8","tests/ui/union.stderr":"5f67ad29753d6fb14bc03aef7d4a1f660ee7796e469c037efbf8b13456934ad3"},"package":"1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b"} \ No newline at end of file
diff --git a/third_party/rust/thiserror/Cargo.toml b/third_party/rust/thiserror/Cargo.toml
index 7871761242..bb7315063c 100644
--- a/third_party/rust/thiserror/Cargo.toml
+++ b/third_party/rust/thiserror/Cargo.toml
@@ -13,7 +13,7 @@
edition = "2021"
rust-version = "1.56"
name = "thiserror"
-version = "1.0.56"
+version = "1.0.57"
authors = ["David Tolnay <dtolnay@gmail.com>"]
description = "derive(Error)"
documentation = "https://docs.rs/thiserror"
@@ -32,7 +32,7 @@ rustdoc-args = ["--generate-link-to-definition"]
targets = ["x86_64-unknown-linux-gnu"]
[dependencies.thiserror-impl]
-version = "=1.0.56"
+version = "=1.0.57"
[dev-dependencies.anyhow]
version = "1.0.73"
diff --git a/third_party/rust/thiserror/src/lib.rs b/third_party/rust/thiserror/src/lib.rs
index 73e6e21736..717cdc6f19 100644
--- a/third_party/rust/thiserror/src/lib.rs
+++ b/third_party/rust/thiserror/src/lib.rs
@@ -228,7 +228,7 @@
//!
//! [`anyhow`]: https://github.com/dtolnay/anyhow
-#![doc(html_root_url = "https://docs.rs/thiserror/1.0.56")]
+#![doc(html_root_url = "https://docs.rs/thiserror/1.0.57")]
#![allow(
clippy::module_name_repetitions,
clippy::needless_lifetimes,
diff --git a/third_party/rust/thiserror/tests/test_display.rs b/third_party/rust/thiserror/tests/test_display.rs
index 6f603882eb..95a210f0b6 100644
--- a/third_party/rust/thiserror/tests/test_display.rs
+++ b/third_party/rust/thiserror/tests/test_display.rs
@@ -1,4 +1,4 @@
-#![allow(clippy::uninlined_format_args)]
+#![allow(clippy::needless_raw_string_hashes, clippy::uninlined_format_args)]
use std::fmt::{self, Display};
use thiserror::Error;
@@ -301,3 +301,58 @@ fn test_keyword() {
assert("error: 1", Error);
}
+
+#[test]
+fn test_str_special_chars() {
+ #[derive(Error, Debug)]
+ pub enum Error {
+ #[error("brace left {{")]
+ BraceLeft,
+ #[error("brace left 2 \x7B\x7B")]
+ BraceLeft2,
+ #[error("brace left 3 \u{7B}\u{7B}")]
+ BraceLeft3,
+ #[error("brace right }}")]
+ BraceRight,
+ #[error("brace right 2 \x7D\x7D")]
+ BraceRight2,
+ #[error("brace right 3 \u{7D}\u{7D}")]
+ BraceRight3,
+ #[error(
+ "new_\
+line"
+ )]
+ NewLine,
+ #[error("escape24 \u{78}")]
+ Escape24,
+ }
+
+ assert("brace left {", Error::BraceLeft);
+ assert("brace left 2 {", Error::BraceLeft2);
+ assert("brace left 3 {", Error::BraceLeft3);
+ assert("brace right }", Error::BraceRight);
+ assert("brace right 2 }", Error::BraceRight2);
+ assert("brace right 3 }", Error::BraceRight3);
+ assert("new_line", Error::NewLine);
+ assert("escape24 x", Error::Escape24);
+}
+
+#[test]
+fn test_raw_str() {
+ #[derive(Error, Debug)]
+ pub enum Error {
+ #[error(r#"raw brace left {{"#)]
+ BraceLeft,
+ #[error(r#"raw brace left 2 \x7B"#)]
+ BraceLeft2,
+ #[error(r#"raw brace right }}"#)]
+ BraceRight,
+ #[error(r#"raw brace right 2 \x7D"#)]
+ BraceRight2,
+ }
+
+ assert(r#"raw brace left {"#, Error::BraceLeft);
+ assert(r#"raw brace left 2 \x7B"#, Error::BraceLeft2);
+ assert(r#"raw brace right }"#, Error::BraceRight);
+ assert(r#"raw brace right 2 \x7D"#, Error::BraceRight2);
+}
diff --git a/third_party/rust/thiserror/tests/ui/no-display.stderr b/third_party/rust/thiserror/tests/ui/no-display.stderr
index 0f47c24b62..88d0092678 100644
--- a/third_party/rust/thiserror/tests/ui/no-display.stderr
+++ b/third_party/rust/thiserror/tests/ui/no-display.stderr
@@ -15,3 +15,6 @@ note: the trait `std::fmt::Display` must be implemented
|
| pub trait Display {
| ^^^^^^^^^^^^^^^^^
+ = help: items from traits can only be used if the trait is implemented and in scope
+ = note: the following trait defines an item `as_display`, perhaps you need to implement it:
+ candidate #1: `AsDisplay`
diff --git a/third_party/rust/thiserror/tests/ui/source-enum-not-error.stderr b/third_party/rust/thiserror/tests/ui/source-enum-not-error.stderr
index 4c44742d54..649d77df81 100644
--- a/third_party/rust/thiserror/tests/ui/source-enum-not-error.stderr
+++ b/third_party/rust/thiserror/tests/ui/source-enum-not-error.stderr
@@ -2,10 +2,7 @@ error[E0599]: the method `as_dyn_error` exists for reference `&NotError`, but it
--> tests/ui/source-enum-not-error.rs:9:14
|
4 | pub struct NotError;
- | -------------------
- | |
- | doesn't satisfy `NotError: AsDynError<'_>`
- | doesn't satisfy `NotError: std::error::Error`
+ | ------------------- doesn't satisfy `NotError: AsDynError<'_>` or `NotError: std::error::Error`
...
9 | Broken { source: NotError },
| ^^^^^^ method cannot be called on `&NotError` due to unsatisfied trait bounds
@@ -20,3 +17,6 @@ note: the trait `std::error::Error` must be implemented
|
| pub trait Error: Debug + Display {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = help: items from traits can only be used if the trait is implemented and in scope
+ = note: the following trait defines an item `as_dyn_error`, perhaps you need to implement it:
+ candidate #1: `AsDynError`
diff --git a/third_party/rust/thiserror/tests/ui/source-enum-unnamed-field-not-error.stderr b/third_party/rust/thiserror/tests/ui/source-enum-unnamed-field-not-error.stderr
index da6d225f86..a1fe2b5b53 100644
--- a/third_party/rust/thiserror/tests/ui/source-enum-unnamed-field-not-error.stderr
+++ b/third_party/rust/thiserror/tests/ui/source-enum-unnamed-field-not-error.stderr
@@ -2,10 +2,7 @@ error[E0599]: the method `as_dyn_error` exists for reference `&NotError`, but it
--> tests/ui/source-enum-unnamed-field-not-error.rs:9:14
|
4 | pub struct NotError;
- | -------------------
- | |
- | doesn't satisfy `NotError: AsDynError<'_>`
- | doesn't satisfy `NotError: std::error::Error`
+ | ------------------- doesn't satisfy `NotError: AsDynError<'_>` or `NotError: std::error::Error`
...
9 | Broken(#[source] NotError),
| ^^^^^^ method cannot be called on `&NotError` due to unsatisfied trait bounds
@@ -20,3 +17,6 @@ note: the trait `std::error::Error` must be implemented
|
| pub trait Error: Debug + Display {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = help: items from traits can only be used if the trait is implemented and in scope
+ = note: the following trait defines an item `as_dyn_error`, perhaps you need to implement it:
+ candidate #1: `AsDynError`
diff --git a/third_party/rust/thiserror/tests/ui/source-struct-not-error.stderr b/third_party/rust/thiserror/tests/ui/source-struct-not-error.stderr
index b98460fcbe..07cd67ac64 100644
--- a/third_party/rust/thiserror/tests/ui/source-struct-not-error.stderr
+++ b/third_party/rust/thiserror/tests/ui/source-struct-not-error.stderr
@@ -2,11 +2,7 @@ error[E0599]: the method `as_dyn_error` exists for struct `NotError`, but its tr
--> tests/ui/source-struct-not-error.rs:9:5
|
4 | struct NotError;
- | ---------------
- | |
- | method `as_dyn_error` not found for this struct
- | doesn't satisfy `NotError: AsDynError<'_>`
- | doesn't satisfy `NotError: std::error::Error`
+ | --------------- method `as_dyn_error` not found for this struct because it doesn't satisfy `NotError: AsDynError<'_>` or `NotError: std::error::Error`
...
9 | source: NotError,
| ^^^^^^ method cannot be called on `NotError` due to unsatisfied trait bounds
@@ -19,3 +15,6 @@ note: the trait `std::error::Error` must be implemented
|
| pub trait Error: Debug + Display {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = help: items from traits can only be used if the trait is implemented and in scope
+ = note: the following trait defines an item `as_dyn_error`, perhaps you need to implement it:
+ candidate #1: `AsDynError`
diff --git a/third_party/rust/thiserror/tests/ui/source-struct-unnamed-field-not-error.stderr b/third_party/rust/thiserror/tests/ui/source-struct-unnamed-field-not-error.stderr
index a23f26823f..2022ea67cd 100644
--- a/third_party/rust/thiserror/tests/ui/source-struct-unnamed-field-not-error.stderr
+++ b/third_party/rust/thiserror/tests/ui/source-struct-unnamed-field-not-error.stderr
@@ -2,11 +2,7 @@ error[E0599]: the method `as_dyn_error` exists for struct `NotError`, but its tr
--> tests/ui/source-struct-unnamed-field-not-error.rs:8:26
|
4 | struct NotError;
- | ---------------
- | |
- | method `as_dyn_error` not found for this struct
- | doesn't satisfy `NotError: AsDynError<'_>`
- | doesn't satisfy `NotError: std::error::Error`
+ | --------------- method `as_dyn_error` not found for this struct because it doesn't satisfy `NotError: AsDynError<'_>` or `NotError: std::error::Error`
...
8 | pub struct ErrorStruct(#[source] NotError);
| ^^^^^^ method cannot be called on `NotError` due to unsatisfied trait bounds
@@ -19,3 +15,6 @@ note: the trait `std::error::Error` must be implemented
|
| pub trait Error: Debug + Display {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = help: items from traits can only be used if the trait is implemented and in scope
+ = note: the following trait defines an item `as_dyn_error`, perhaps you need to implement it:
+ candidate #1: `AsDynError`
diff --git a/third_party/rust/thiserror/tests/ui/transparent-enum-not-error.stderr b/third_party/rust/thiserror/tests/ui/transparent-enum-not-error.stderr
index 9be51434a6..bb836d4e8d 100644
--- a/third_party/rust/thiserror/tests/ui/transparent-enum-not-error.stderr
+++ b/third_party/rust/thiserror/tests/ui/transparent-enum-not-error.stderr
@@ -7,10 +7,7 @@ error[E0599]: the method `as_dyn_error` exists for reference `&String`, but its
::: $RUST/alloc/src/string.rs
|
| pub struct String {
- | -----------------
- | |
- | doesn't satisfy `String: AsDynError<'_>`
- | doesn't satisfy `String: std::error::Error`
+ | ----------------- doesn't satisfy `String: AsDynError<'_>` or `String: std::error::Error`
|
= note: the following trait bounds were not satisfied:
`String: std::error::Error`
diff --git a/third_party/rust/thiserror/tests/ui/transparent-enum-unnamed-field-not-error.stderr b/third_party/rust/thiserror/tests/ui/transparent-enum-unnamed-field-not-error.stderr
index 3d23c3a0e5..f337c592ee 100644
--- a/third_party/rust/thiserror/tests/ui/transparent-enum-unnamed-field-not-error.stderr
+++ b/third_party/rust/thiserror/tests/ui/transparent-enum-unnamed-field-not-error.stderr
@@ -7,10 +7,7 @@ error[E0599]: the method `as_dyn_error` exists for reference `&String`, but its
::: $RUST/alloc/src/string.rs
|
| pub struct String {
- | -----------------
- | |
- | doesn't satisfy `String: AsDynError<'_>`
- | doesn't satisfy `String: std::error::Error`
+ | ----------------- doesn't satisfy `String: AsDynError<'_>` or `String: std::error::Error`
|
= note: the following trait bounds were not satisfied:
`String: std::error::Error`
diff --git a/third_party/rust/thiserror/tests/ui/transparent-struct-not-error.stderr b/third_party/rust/thiserror/tests/ui/transparent-struct-not-error.stderr
index d67a694467..ee50d03a7b 100644
--- a/third_party/rust/thiserror/tests/ui/transparent-struct-not-error.stderr
+++ b/third_party/rust/thiserror/tests/ui/transparent-struct-not-error.stderr
@@ -7,10 +7,7 @@ error[E0599]: the method `as_dyn_error` exists for struct `String`, but its trai
::: $RUST/alloc/src/string.rs
|
| pub struct String {
- | -----------------
- | |
- | doesn't satisfy `String: AsDynError<'_>`
- | doesn't satisfy `String: std::error::Error`
+ | ----------------- doesn't satisfy `String: AsDynError<'_>` or `String: std::error::Error`
|
= note: the following trait bounds were not satisfied:
`String: std::error::Error`
diff --git a/third_party/rust/thiserror/tests/ui/transparent-struct-unnamed-field-not-error.stderr b/third_party/rust/thiserror/tests/ui/transparent-struct-unnamed-field-not-error.stderr
index f715a15175..c3d6c0023d 100644
--- a/third_party/rust/thiserror/tests/ui/transparent-struct-unnamed-field-not-error.stderr
+++ b/third_party/rust/thiserror/tests/ui/transparent-struct-unnamed-field-not-error.stderr
@@ -7,10 +7,7 @@ error[E0599]: the method `as_dyn_error` exists for struct `String`, but its trai
::: $RUST/alloc/src/string.rs
|
| pub struct String {
- | -----------------
- | |
- | doesn't satisfy `String: AsDynError<'_>`
- | doesn't satisfy `String: std::error::Error`
+ | ----------------- doesn't satisfy `String: AsDynError<'_>` or `String: std::error::Error`
|
= note: the following trait bounds were not satisfied:
`String: std::error::Error`
diff --git a/third_party/rust/unicode-bidi/.appveyor.yml b/third_party/rust/unicode-bidi/.appveyor.yml
new file mode 100644
index 0000000000..1bd43a1145
--- /dev/null
+++ b/third_party/rust/unicode-bidi/.appveyor.yml
@@ -0,0 +1,19 @@
+install:
+ - appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe
+ - rustup-init -yv --default-toolchain nightly
+ - set PATH=%PATH%;%USERPROFILE%\.cargo\bin
+ - rustc -V
+ - cargo -V
+ - git submodule update --init --recursive
+
+build: false
+
+environment:
+ RUST_BACKTRACE: full
+
+test_script:
+ - cargo build --verbose --all
+ - cargo doc --verbose --all --no-deps
+
+ - cargo test --verbose --all
+ - cargo test --verbose --all --features serde
diff --git a/third_party/rust/unicode-bidi/.cargo-checksum.json b/third_party/rust/unicode-bidi/.cargo-checksum.json
index 3628dc8f6f..fa0b5995c0 100644
--- a/third_party/rust/unicode-bidi/.cargo-checksum.json
+++ b/third_party/rust/unicode-bidi/.cargo-checksum.json
@@ -1 +1 @@
-{"files":{"AUTHORS":"1ff3a7c8519b29544bb28ba9b1e7502df0cb764051fb9a1172e60006aa2b8dcc","COPYRIGHT":"edb20b474f6cbd4f4db066b54a9e0f687d0009d309412a63431189b59b8e2a07","Cargo.toml":"9cd0be282dbaeacf5d1fdf07096114c7b3f16f275755f30a0d2e873ab1fbc150","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"7b63ecd5f1902af1b63729947373683c32745c16a10e8e6292e2e2dcd7e90ae0","README.md":"81d3dc6d894a68894d982760b0a907f9dcbb0da179a8063ed9de1d2257518957","src/char_data/mod.rs":"0622df8ce4b4de60aea7e4787635d3187f79f7a3f9001e3d209f58fd07d03887","src/char_data/tables.rs":"50faf4eef73c831a38b735309ff3415e9f65992a0474ff5c055138f91c91ee16","src/data_source.rs":"36fa0785e51c549c1f72f09040cfe515b848d1b23fb30d469770a6b4b17b49df","src/deprecated.rs":"46c5a8bb1e6aa6193eec8269891d4cbbb5fd92214eb55eac7ea5e7ca193386aa","src/explicit.rs":"afa7599674fc8daad2939e5987ec5d937ed9fdbb78b8492b1e137db88d0a3af7","src/format_chars.rs":"678399fec3f4bfaf4093f38cfdb8956288313386dc3511dab9fb58164e8dc01b","src/implicit.rs":"e96484178d1bab97439b2c0cf4b3a8d6ee18459b9de64499aa07607aa304be0c","src/level.rs":"921fb7b8960f6cc703a51936075302db513927b8c5d5d760536b6ff70ddab0dd","src/lib.rs":"ca09c7dedc56ec139fa92fec26c3983346a3b6456992acdfbfe18b324174e0d8","src/prepare.rs":"c4aaad603f5c1be5c4364265aac7590335dc234288a4151e0f30bcefe532e14d","src/utf16.rs":"30d31c4d8c814315b48b56a2dfb755b8b442dde23f76429c6df1af754999fe3b"},"package":"08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"} \ No newline at end of file
+{"files":{".appveyor.yml":"15bdeea0e836ac2ccbb259cde1509a0673a73300e90e970f3e533b189234b6fd",".github/workflows/main.yml":"e0bee93284a8b39c9d419038bfa72a6389ebdae39ce55c40624e764ac1c98a9e",".rustfmt.toml":"168c973274f3f5946e90cac6ae0f017d0832a5c830872d9d3b9b387ad6c1a81e","AUTHORS":"1ff3a7c8519b29544bb28ba9b1e7502df0cb764051fb9a1172e60006aa2b8dcc","COPYRIGHT":"edb20b474f6cbd4f4db066b54a9e0f687d0009d309412a63431189b59b8e2a07","Cargo.lock":"8842f03d0fcea88aa1546244d0455834732603175b293218f8e9a9f44c297b7c","Cargo.toml":"099454ebee9b081080e1521eccbe447db30b17ac36e9e655ed1d0d1e20e657fb","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"7b63ecd5f1902af1b63729947373683c32745c16a10e8e6292e2e2dcd7e90ae0","README.md":"81d3dc6d894a68894d982760b0a907f9dcbb0da179a8063ed9de1d2257518957","src/char_data/mod.rs":"8cbdcaacddb3dd9b70d615693fa73d0e7dca6332102a95f0d3ce447df7645284","src/char_data/tables.rs":"8adf126131f573a3b6d2c35849c1cc13c831c9b55c4d3fcb5a3961d8ed7a0d44","src/data_source.rs":"36fa0785e51c549c1f72f09040cfe515b848d1b23fb30d469770a6b4b17b49df","src/deprecated.rs":"f94c0e75dec7e70cb9802e26b7f82fe618dcdd50e9973927bacd4eccc6899c62","src/explicit.rs":"86c3c55bf2cc90aab1411aac6cf05de505ca74e44a76fe829572dd7dc4dd2aa3","src/format_chars.rs":"678399fec3f4bfaf4093f38cfdb8956288313386dc3511dab9fb58164e8dc01b","src/implicit.rs":"8d5b003464aee3f333785c6170a884945251f39601e4ea658e669a2ad575d588","src/level.rs":"ce1eaa9940f1b90bc59aba296488b8cd128aefeb4b6b2e3ecc34da26c569150b","src/lib.rs":"9dff9c105f481a03823de6ad9a0a11733af019649ae211644061d5a525670244","src/prepare.rs":"aeb8b88cfb2d2e6b74473f5903205dd3683d57abcc8801de7b9fdea6a432a0fe","src/utf16.rs":"12ee177127a0b5b0350a1fcc1edf7387c26b51ec5654f724629aab723881c313"},"package":null} \ No newline at end of file
diff --git a/third_party/rust/unicode-bidi/.github/workflows/main.yml b/third_party/rust/unicode-bidi/.github/workflows/main.yml
new file mode 100644
index 0000000000..303bac8b97
--- /dev/null
+++ b/third_party/rust/unicode-bidi/.github/workflows/main.yml
@@ -0,0 +1,49 @@
+name: CI
+
+on:
+ push:
+ branches: ['master', 'auto']
+ pull_request:
+
+jobs:
+ Test:
+ strategy:
+ matrix:
+ os: [ubuntu-latest]
+ rust: [1.47.0, stable, beta, nightly]
+ runs-on: ${{ matrix.os }}
+ steps:
+ - uses: actions/checkout@v4
+ - uses: dtolnay/rust-toolchain@master
+ with:
+ toolchain: ${{ matrix.rust }}
+ - name: Unpin dependencies except on MSRV
+ if: matrix.rust != '1.47.0'
+ run: cargo update
+ - run: cargo build --all-targets
+ - run: cargo test
+ - run: cargo test --features "serde"
+ - run: cargo test --no-default-features
+ - run: cargo test --no-default-features --features=hardcoded-data
+ Fmt:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: dtolnay/rust-toolchain@stable
+ with:
+ components: rustfmt
+ - run: cargo fmt --check
+
+ build_result:
+ name: homu build finished
+ runs-on: ubuntu-latest
+ needs:
+ - "Test"
+ - "Fmt"
+ steps:
+ - name: Mark the job as successful
+ run: exit 0
+ if: success()
+ - name: Mark the job as unsuccessful
+ run: exit 1
+ if: "!success()"
diff --git a/third_party/rust/unicode-bidi/.rustfmt.toml b/third_party/rust/unicode-bidi/.rustfmt.toml
new file mode 100644
index 0000000000..e416686ee5
--- /dev/null
+++ b/third_party/rust/unicode-bidi/.rustfmt.toml
@@ -0,0 +1 @@
+array_width = 80
diff --git a/third_party/rust/unicode-bidi/Cargo.lock b/third_party/rust/unicode-bidi/Cargo.lock
new file mode 100644
index 0000000000..63f01ebdcc
--- /dev/null
+++ b/third_party/rust/unicode-bidi/Cargo.lock
@@ -0,0 +1,175 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+[[package]]
+name = "flame"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.156 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_derive 1.0.156 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_json 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)",
+ "thread-id 3.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "flamer"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "flame 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 1.0.109 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "lazy_static"
+version = "0.2.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "libc"
+version = "0.2.149"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.65"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "unicode-ident 1.0.12 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "proc-macro2 1.0.65 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.1.57"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "ryu"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "serde"
+version = "1.0.156"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "serde_derive 1.0.156 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.156"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "proc-macro2 1.0.65 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 1.0.109 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.99"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "itoa 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "ryu 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.156 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "serde_test"
+version = "1.0.175"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "serde 1.0.156 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "syn"
+version = "1.0.109"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "proc-macro2 1.0.65 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-ident 1.0.12 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "thread-id"
+version = "3.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.2.149 (registry+https://github.com/rust-lang/crates.io-index)",
+ "redox_syscall 0.1.57 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "unicode-bidi"
+version = "0.3.13"
+dependencies = [
+ "flame 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "flamer 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.156 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_test 1.0.175 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[metadata]
+"checksum flame 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1fc2706461e1ee94f55cab2ed2e3d34ae9536cfa830358ef80acff1a3dacab30"
+"checksum flamer 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "36b732da54fd4ea34452f2431cf464ac7be94ca4b339c9cd3d3d12eb06fe7aab"
+"checksum itoa 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
+"checksum lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73"
+"checksum libc 0.2.149 (registry+https://github.com/rust-lang/crates.io-index)" = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b"
+"checksum proc-macro2 1.0.65 (registry+https://github.com/rust-lang/crates.io-index)" = "92de25114670a878b1261c79c9f8f729fb97e95bac93f6312f583c60dd6a1dfe"
+"checksum quote 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)" = "5907a1b7c277254a8b15170f6e7c97cfa60ee7872a3217663bb81151e48184bb"
+"checksum redox_syscall 0.1.57 (registry+https://github.com/rust-lang/crates.io-index)" = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
+"checksum ryu 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)" = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
+"checksum serde 1.0.156 (registry+https://github.com/rust-lang/crates.io-index)" = "314b5b092c0ade17c00142951e50ced110ec27cea304b1037c6969246c2469a4"
+"checksum serde_derive 1.0.156 (registry+https://github.com/rust-lang/crates.io-index)" = "d7e29c4601e36bcec74a223228dce795f4cd3616341a4af93520ca1a837c087d"
+"checksum serde_json 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)" = "46266871c240a00b8f503b877622fe33430b3c7d963bdc0f2adc511e54a1eae3"
+"checksum serde_test 1.0.175 (registry+https://github.com/rust-lang/crates.io-index)" = "29baf0f77ca9ad9c6ed46e1b408b5e0f30b5184bcd66884e7f6d36bd7a65a8a4"
+"checksum syn 1.0.109 (registry+https://github.com/rust-lang/crates.io-index)" = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
+"checksum thread-id 3.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c7fbf4c9d56b320106cd64fd024dadfa0be7cb4706725fc44a7d7ce952d820c1"
+"checksum unicode-ident 1.0.12 (registry+https://github.com/rust-lang/crates.io-index)" = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+"checksum winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
diff --git a/third_party/rust/unicode-bidi/Cargo.toml b/third_party/rust/unicode-bidi/Cargo.toml
index 29be4e5a71..584d471c8a 100644
--- a/third_party/rust/unicode-bidi/Cargo.toml
+++ b/third_party/rust/unicode-bidi/Cargo.toml
@@ -11,6 +11,7 @@
[package]
edition = "2018"
+rust-version = "1.47.0"
name = "unicode-bidi"
version = "0.3.15"
authors = ["The Servo Project Developers"]
@@ -61,8 +62,13 @@ features = ["derive"]
optional = true
default-features = false
-[dev-dependencies.serde_test]
-version = ">=0.8, <2.0"
+[dependencies.smallvec]
+version = ">=1.13"
+features = ["union"]
+optional = true
+
+[dev-dependencies]
+serde_test = ">=0.8, <2.0"
[features]
bench_it = []
diff --git a/third_party/rust/unicode-bidi/src/char_data/mod.rs b/third_party/rust/unicode-bidi/src/char_data/mod.rs
index 4edf5b8f4c..543b0ed8fd 100644
--- a/third_party/rust/unicode-bidi/src/char_data/mod.rs
+++ b/third_party/rust/unicode-bidi/src/char_data/mod.rs
@@ -59,10 +59,7 @@ pub(crate) fn bidi_matched_opening_bracket(c: char) -> Option<BidiMatchedOpening
}
pub fn is_rtl(bidi_class: BidiClass) -> bool {
- match bidi_class {
- RLE | RLO | RLI => true,
- _ => false,
- }
+ matches!(bidi_class, RLE | RLO | RLI)
}
#[cfg(feature = "hardcoded-data")]
diff --git a/third_party/rust/unicode-bidi/src/char_data/tables.rs b/third_party/rust/unicode-bidi/src/char_data/tables.rs
index ecdcf496d1..f10265d214 100644
--- a/third_party/rust/unicode-bidi/src/char_data/tables.rs
+++ b/third_party/rust/unicode-bidi/src/char_data/tables.rs
@@ -45,7 +45,7 @@ pub enum BidiClass {
use self::BidiClass::*;
#[cfg(feature = "hardcoded-data")]
-pub const bidi_class_table: &'static [(char, char, BidiClass)] = &[
+pub const bidi_class_table: &[(char, char, BidiClass)] = &[
('\u{0}', '\u{8}', BN), ('\u{9}', '\u{9}', S), ('\u{a}', '\u{a}', B), ('\u{b}', '\u{b}', S),
('\u{c}', '\u{c}', WS), ('\u{d}', '\u{d}', B), ('\u{e}', '\u{1b}', BN), ('\u{1c}', '\u{1e}', B),
('\u{1f}', '\u{1f}', S), ('\u{20}', '\u{20}', WS), ('\u{21}', '\u{22}', ON), ('\u{23}',
@@ -516,7 +516,7 @@ pub const bidi_class_table: &'static [(char, char, BidiClass)] = &[
'\u{e01ef}', NSM), ('\u{f0000}', '\u{ffffd}', L), ('\u{100000}', '\u{10fffd}', L)
];
-pub const bidi_pairs_table: &'static [(char, char, Option<char>)] = &[
+pub const bidi_pairs_table: &[(char, char, Option<char>)] = &[
('\u{28}', '\u{29}', None), ('\u{5b}', '\u{5d}', None), ('\u{7b}', '\u{7d}', None), ('\u{f3a}',
'\u{f3b}', None), ('\u{f3c}', '\u{f3d}', None), ('\u{169b}', '\u{169c}', None), ('\u{2045}',
'\u{2046}', None), ('\u{207d}', '\u{207e}', None), ('\u{208d}', '\u{208e}', None), ('\u{2308}',
diff --git a/third_party/rust/unicode-bidi/src/deprecated.rs b/third_party/rust/unicode-bidi/src/deprecated.rs
index 74a24f5b8b..c903663e99 100644
--- a/third_party/rust/unicode-bidi/src/deprecated.rs
+++ b/third_party/rust/unicode-bidi/src/deprecated.rs
@@ -9,8 +9,6 @@
//! This module holds deprecated assets only.
-use alloc::vec::Vec;
-
use super::*;
/// Find the level runs within a line and return them in visual order.
@@ -71,10 +69,8 @@ pub fn visual_runs(line: Range<usize>, levels: &[Level]) -> Vec<LevelRun> {
// Found the start of a sequence. Now find the end.
let mut seq_end = seq_start + 1;
- while seq_end < run_count {
- if levels[runs[seq_end].start] < max_level {
- break;
- }
+
+ while seq_end < run_count && levels[runs[seq_end].start] >= max_level {
seq_end += 1;
}
@@ -83,6 +79,7 @@ pub fn visual_runs(line: Range<usize>, levels: &[Level]) -> Vec<LevelRun> {
seq_start = seq_end;
}
+
max_level
.lower(1)
.expect("Lowering embedding level below zero");
diff --git a/third_party/rust/unicode-bidi/src/explicit.rs b/third_party/rust/unicode-bidi/src/explicit.rs
index d4ad897b54..5760ab8ece 100644
--- a/third_party/rust/unicode-bidi/src/explicit.rs
+++ b/third_party/rust/unicode-bidi/src/explicit.rs
@@ -11,19 +11,25 @@
//!
//! <http://www.unicode.org/reports/tr9/#Explicit_Levels_and_Directions>
-use alloc::vec::Vec;
+#[cfg(feature = "smallvec")]
+use smallvec::{smallvec, SmallVec};
use super::char_data::{
is_rtl,
BidiClass::{self, *},
};
use super::level::Level;
+use super::prepare::removed_by_x9;
+use super::LevelRunVec;
use super::TextSource;
-/// Compute explicit embedding levels for one paragraph of text (X1-X8).
+/// Compute explicit embedding levels for one paragraph of text (X1-X8), and identify
+/// level runs (BD7) for use when determining Isolating Run Sequences (X10).
///
/// `processing_classes[i]` must contain the `BidiClass` of the char at byte index `i`,
/// for each char in `text`.
+///
+/// `runs` returns the list of level runs (BD7) of the text.
#[cfg_attr(feature = "flame_it", flamer::flame)]
pub fn compute<'a, T: TextSource<'a> + ?Sized>(
text: &'a T,
@@ -31,35 +37,44 @@ pub fn compute<'a, T: TextSource<'a> + ?Sized>(
original_classes: &[BidiClass],
levels: &mut [Level],
processing_classes: &mut [BidiClass],
+ runs: &mut LevelRunVec,
) {
assert_eq!(text.len(), original_classes.len());
// <http://www.unicode.org/reports/tr9/#X1>
- let mut stack = DirectionalStatusStack::new();
- stack.push(para_level, OverrideStatus::Neutral);
+ #[cfg(feature = "smallvec")]
+ let mut stack: SmallVec<[Status; 8]> = smallvec![Status {
+ level: para_level,
+ status: OverrideStatus::Neutral,
+ }];
+ #[cfg(not(feature = "smallvec"))]
+ let mut stack = vec![Status {
+ level: para_level,
+ status: OverrideStatus::Neutral,
+ }];
let mut overflow_isolate_count = 0u32;
let mut overflow_embedding_count = 0u32;
let mut valid_isolate_count = 0u32;
+ let mut current_run_level = Level::ltr();
+ let mut current_run_start = 0;
+
for (i, len) in text.indices_lengths() {
+ let last = stack.last().unwrap();
+
match original_classes[i] {
// Rules X2-X5c
RLE | LRE | RLO | LRO | RLI | LRI | FSI => {
- let last_level = stack.last().level;
-
// <https://www.unicode.org/reports/tr9/#Retaining_Explicit_Formatting_Characters>
- levels[i] = last_level;
+ levels[i] = last.level;
// X5a-X5c: Isolate initiators get the level of the last entry on the stack.
- let is_isolate = match original_classes[i] {
- RLI | LRI | FSI => true,
- _ => false,
- };
+ let is_isolate = matches!(original_classes[i], RLI | LRI | FSI);
if is_isolate {
// Redundant due to "Retaining explicit formatting characters" step.
- // levels[i] = last_level;
- match stack.last().status {
+ // levels[i] = last.level;
+ match last.status {
OverrideStatus::RTL => processing_classes[i] = R,
OverrideStatus::LTR => processing_classes[i] = L,
_ => {}
@@ -67,22 +82,25 @@ pub fn compute<'a, T: TextSource<'a> + ?Sized>(
}
let new_level = if is_rtl(original_classes[i]) {
- last_level.new_explicit_next_rtl()
+ last.level.new_explicit_next_rtl()
} else {
- last_level.new_explicit_next_ltr()
+ last.level.new_explicit_next_ltr()
};
+
if new_level.is_ok() && overflow_isolate_count == 0 && overflow_embedding_count == 0
{
let new_level = new_level.unwrap();
- stack.push(
- new_level,
- match original_classes[i] {
+
+ stack.push(Status {
+ level: new_level,
+ status: match original_classes[i] {
RLO => OverrideStatus::RTL,
LRO => OverrideStatus::LTR,
RLI | LRI | FSI => OverrideStatus::Isolate,
_ => OverrideStatus::Neutral,
},
- );
+ });
+
if is_isolate {
valid_isolate_count += 1;
} else {
@@ -110,21 +128,21 @@ pub fn compute<'a, T: TextSource<'a> + ?Sized>(
overflow_isolate_count -= 1;
} else if valid_isolate_count > 0 {
overflow_embedding_count = 0;
- loop {
- // Pop everything up to and including the last Isolate status.
- match stack.vec.pop() {
- None
- | Some(Status {
- status: OverrideStatus::Isolate,
- ..
- }) => break,
- _ => continue,
- }
- }
+
+ while !matches!(
+ stack.pop(),
+ None | Some(Status {
+ status: OverrideStatus::Isolate,
+ ..
+ })
+ ) {}
+
valid_isolate_count -= 1;
}
- let last = stack.last();
+
+ let last = stack.last().unwrap();
levels[i] = last.level;
+
match last.status {
OverrideStatus::RTL => processing_classes[i] = R,
OverrideStatus::LTR => processing_classes[i] = L,
@@ -138,11 +156,12 @@ pub fn compute<'a, T: TextSource<'a> + ?Sized>(
// do nothing
} else if overflow_embedding_count > 0 {
overflow_embedding_count -= 1;
- } else if stack.last().status != OverrideStatus::Isolate && stack.vec.len() >= 2 {
- stack.vec.pop();
+ } else if last.status != OverrideStatus::Isolate && stack.len() >= 2 {
+ stack.pop();
}
+
// <https://www.unicode.org/reports/tr9/#Retaining_Explicit_Formatting_Characters>
- levels[i] = stack.last().level;
+ levels[i] = stack.last().unwrap().level;
// X9 part of retaining explicit formatting characters.
processing_classes[i] = BN;
}
@@ -153,8 +172,8 @@ pub fn compute<'a, T: TextSource<'a> + ?Sized>(
// <http://www.unicode.org/reports/tr9/#X6>
_ => {
- let last = stack.last();
levels[i] = last.level;
+
// This condition is not in the spec, but I am pretty sure that is a spec bug.
// https://www.unicode.org/L2/L2023/23014-amd-to-uax9.pdf
if original_classes[i] != BN {
@@ -172,6 +191,26 @@ pub fn compute<'a, T: TextSource<'a> + ?Sized>(
levels[i + j] = levels[i];
processing_classes[i + j] = processing_classes[i];
}
+
+ // Identify level runs to be passed to prepare::isolating_run_sequences().
+ if i == 0 {
+ // Initialize for the first (or only) run.
+ current_run_level = levels[i];
+ } else {
+ // Check if we need to start a new level run.
+ // <https://www.unicode.org/reports/tr9/#BD7>
+ if !removed_by_x9(original_classes[i]) && levels[i] != current_run_level {
+ // End the last run and start a new one.
+ runs.push(current_run_start..i);
+ current_run_level = levels[i];
+ current_run_start = i;
+ }
+ }
+ }
+
+ // Append the trailing level run, if non-empty.
+ if levels.len() > current_run_start {
+ runs.push(current_run_start..levels.len());
}
}
@@ -188,23 +227,3 @@ enum OverrideStatus {
LTR,
Isolate,
}
-
-struct DirectionalStatusStack {
- vec: Vec<Status>,
-}
-
-impl DirectionalStatusStack {
- fn new() -> Self {
- DirectionalStatusStack {
- vec: Vec::with_capacity(Level::max_explicit_depth() as usize + 2),
- }
- }
-
- fn push(&mut self, level: Level, status: OverrideStatus) {
- self.vec.push(Status { level, status });
- }
-
- fn last(&self) -> &Status {
- self.vec.last().unwrap()
- }
-}
diff --git a/third_party/rust/unicode-bidi/src/implicit.rs b/third_party/rust/unicode-bidi/src/implicit.rs
index 0311053c0a..334afec049 100644
--- a/third_party/rust/unicode-bidi/src/implicit.rs
+++ b/third_party/rust/unicode-bidi/src/implicit.rs
@@ -9,8 +9,11 @@
//! 3.3.4 - 3.3.6. Resolve implicit levels and types.
+#[cfg(not(feature = "smallvec"))]
use alloc::vec::Vec;
use core::cmp::max;
+#[cfg(feature = "smallvec")]
+use smallvec::SmallVec;
use super::char_data::BidiClass::{self, *};
use super::level::Level;
@@ -39,7 +42,13 @@ pub fn resolve_weak<'a, T: TextSource<'a> + ?Sized>(
// The previous class for the purposes of rule W1, not tracking changes from any other rules.
let mut prev_class_before_w1 = sequence.sos;
let mut last_strong_is_al = false;
+ #[cfg(feature = "smallvec")]
+ let mut et_run_indices = SmallVec::<[usize; 8]>::new(); // for W5
+ #[cfg(not(feature = "smallvec"))]
let mut et_run_indices = Vec::new(); // for W5
+ #[cfg(feature = "smallvec")]
+ let mut bn_run_indices = SmallVec::<[usize; 8]>::new(); // for W5 + <https://www.unicode.org/reports/tr9/#Retaining_Explicit_Formatting_Characters>
+ #[cfg(not(feature = "smallvec"))]
let mut bn_run_indices = Vec::new(); // for W5 + <https://www.unicode.org/reports/tr9/#Retaining_Explicit_Formatting_Characters>
for (run_index, level_run) in sequence.runs.iter().enumerate() {
@@ -177,7 +186,7 @@ pub fn resolve_weak<'a, T: TextSource<'a> + ?Sized>(
_ => {
// <https://www.unicode.org/reports/tr9/#Retaining_Explicit_Formatting_Characters>
// If there was a BN run before this, that's now a part of this ET run.
- et_run_indices.extend(&bn_run_indices);
+ et_run_indices.extend(bn_run_indices.clone());
// In case this is followed by an EN.
et_run_indices.push(i);
@@ -224,26 +233,29 @@ pub fn resolve_weak<'a, T: TextSource<'a> + ?Sized>(
// W7. If the previous strong char was L, change EN to L.
let mut last_strong_is_l = sequence.sos == L;
- for run in &sequence.runs {
- for i in run.clone() {
- match processing_classes[i] {
- EN if last_strong_is_l => {
- processing_classes[i] = L;
- }
- L => {
- last_strong_is_l = true;
- }
- R | AL => {
- last_strong_is_l = false;
- }
- // <https://www.unicode.org/reports/tr9/#Retaining_Explicit_Formatting_Characters>
- // Already scanning past BN here.
- _ => {}
+ for i in sequence.runs.iter().cloned().flatten() {
+ match processing_classes[i] {
+ EN if last_strong_is_l => {
+ processing_classes[i] = L;
}
+ L => {
+ last_strong_is_l = true;
+ }
+ R | AL => {
+ last_strong_is_l = false;
+ }
+ // <https://www.unicode.org/reports/tr9/#Retaining_Explicit_Formatting_Characters>
+ // Already scanning past BN here.
+ _ => {}
}
}
}
+#[cfg(feature = "smallvec")]
+type BracketPairVec = SmallVec<[BracketPair; 8]>;
+#[cfg(not(feature = "smallvec"))]
+type BracketPairVec = Vec<BracketPair>;
+
/// 3.3.5 Resolving Neutral Types
///
/// <http://www.unicode.org/reports/tr9/#Resolving_Neutral_Types>
@@ -267,7 +279,14 @@ pub fn resolve_neutral<'a, D: BidiDataSource, T: TextSource<'a> + ?Sized>(
// > Identify the bracket pairs in the current isolating run sequence according to BD16.
// We use processing_classes, not original_classes, due to BD14/BD15
- let bracket_pairs = identify_bracket_pairs(text, data_source, sequence, processing_classes);
+ let mut bracket_pairs = BracketPairVec::new();
+ identify_bracket_pairs(
+ text,
+ data_source,
+ sequence,
+ processing_classes,
+ &mut bracket_pairs,
+ );
// > For each bracket-pair element in the list of pairs of text positions
//
@@ -308,7 +327,7 @@ pub fn resolve_neutral<'a, D: BidiDataSource, T: TextSource<'a> + ?Sized>(
found_e = true;
} else if class == not_e {
found_not_e = true;
- } else if class == BidiClass::EN || class == BidiClass::AN {
+ } else if matches!(class, BidiClass::EN | BidiClass::AN) {
// > Within this scope, bidirectional types EN and AN are treated as R.
if e == BidiClass::L {
found_not_e = true;
@@ -337,15 +356,15 @@ pub fn resolve_neutral<'a, D: BidiDataSource, T: TextSource<'a> + ?Sized>(
.iter_backwards_from(pair.start, pair.start_run)
.map(|i| processing_classes[i])
.find(|class| {
- *class == BidiClass::L
- || *class == BidiClass::R
- || *class == BidiClass::EN
- || *class == BidiClass::AN
+ matches!(
+ class,
+ BidiClass::L | BidiClass::R | BidiClass::EN | BidiClass::AN
+ )
})
.unwrap_or(sequence.sos);
// > Within this scope, bidirectional types EN and AN are treated as R.
- if previous_strong == BidiClass::EN || previous_strong == BidiClass::AN {
+ if matches!(previous_strong, BidiClass::EN | BidiClass::AN) {
previous_strong = BidiClass::R;
}
@@ -413,6 +432,9 @@ pub fn resolve_neutral<'a, D: BidiDataSource, T: TextSource<'a> + ?Sized>(
let mut prev_class = sequence.sos;
while let Some(mut i) = indices.next() {
// Process sequences of NI characters.
+ #[cfg(feature = "smallvec")]
+ let mut ni_run = SmallVec::<[usize; 8]>::new();
+ #[cfg(not(feature = "smallvec"))]
let mut ni_run = Vec::new();
// The BN is for <https://www.unicode.org/reports/tr9/#Retaining_Explicit_Formatting_Characters>
if is_NI(processing_classes[i]) || processing_classes[i] == BN {
@@ -484,9 +506,12 @@ fn identify_bracket_pairs<'a, T: TextSource<'a> + ?Sized, D: BidiDataSource>(
data_source: &D,
run_sequence: &IsolatingRunSequence,
original_classes: &[BidiClass],
-) -> Vec<BracketPair> {
- let mut ret = vec![];
- let mut stack = vec![];
+ bracket_pairs: &mut BracketPairVec,
+) {
+ #[cfg(feature = "smallvec")]
+ let mut stack = SmallVec::<[(char, usize, usize); 8]>::new();
+ #[cfg(not(feature = "smallvec"))]
+ let mut stack = Vec::new();
for (run_index, level_run) in run_sequence.runs.iter().enumerate() {
for (i, ch) in text.subrange(level_run.clone()).char_indices() {
@@ -532,7 +557,7 @@ fn identify_bracket_pairs<'a, T: TextSource<'a> + ?Sized, D: BidiDataSource>(
start_run: element.2,
end_run: run_index,
};
- ret.push(pair);
+ bracket_pairs.push(pair);
// > Pop the stack through the current stack element inclusively.
stack.truncate(stack_index);
@@ -545,8 +570,7 @@ fn identify_bracket_pairs<'a, T: TextSource<'a> + ?Sized, D: BidiDataSource>(
}
// > Sort the list of pairs of text positions in ascending order based on
// > the text position of the opening paired bracket.
- ret.sort_by_key(|r| r.start);
- ret
+ bracket_pairs.sort_by_key(|r| r.start);
}
/// 3.3.6 Resolving Implicit Levels
@@ -555,11 +579,11 @@ fn identify_bracket_pairs<'a, T: TextSource<'a> + ?Sized, D: BidiDataSource>(
///
/// <http://www.unicode.org/reports/tr9/#Resolving_Implicit_Levels>
#[cfg_attr(feature = "flame_it", flamer::flame)]
-pub fn resolve_levels(original_classes: &[BidiClass], levels: &mut [Level]) -> Level {
+pub fn resolve_levels(processing_classes: &[BidiClass], levels: &mut [Level]) -> Level {
let mut max_level = Level::ltr();
- assert_eq!(original_classes.len(), levels.len());
+ assert_eq!(processing_classes.len(), levels.len());
for i in 0..levels.len() {
- match (levels[i].is_rtl(), original_classes[i]) {
+ match (levels[i].is_rtl(), processing_classes[i]) {
(false, AN) | (false, EN) => levels[i].raise(2).expect("Level number error"),
(false, R) | (true, L) | (true, EN) | (true, AN) => {
levels[i].raise(1).expect("Level number error")
@@ -578,8 +602,5 @@ pub fn resolve_levels(original_classes: &[BidiClass], levels: &mut [Level]) -> L
/// <http://www.unicode.org/reports/tr9/#NI>
#[allow(non_snake_case)]
fn is_NI(class: BidiClass) -> bool {
- match class {
- B | S | WS | ON | FSI | LRI | RLI | PDI => true,
- _ => false,
- }
+ matches!(class, B | S | WS | ON | FSI | LRI | RLI | PDI)
}
diff --git a/third_party/rust/unicode-bidi/src/level.rs b/third_party/rust/unicode-bidi/src/level.rs
index ef4f6d9e40..5ece0251a5 100644
--- a/third_party/rust/unicode-bidi/src/level.rs
+++ b/third_party/rust/unicode-bidi/src/level.rs
@@ -13,9 +13,10 @@
//!
//! <http://www.unicode.org/reports/tr9/#BD2>
-use alloc::string::{String, ToString};
-use alloc::vec::Vec;
-use core::convert::{From, Into};
+use alloc::{
+ string::{String, ToString},
+ vec::Vec,
+};
use core::slice;
use super::char_data::BidiClass;
@@ -219,11 +220,11 @@ pub fn has_rtl(levels: &[Level]) -> bool {
levels.iter().any(|&lvl| lvl.is_rtl())
}
-impl Into<u8> for Level {
+impl From<Level> for u8 {
/// Convert to the level number
#[inline]
- fn into(self) -> u8 {
- self.number()
+ fn from(val: Level) -> Self {
+ val.number()
}
}
@@ -244,7 +245,7 @@ impl<'a> PartialEq<&'a str> for Level {
}
/// Used for matching levels in conformance tests
-impl<'a> PartialEq<String> for Level {
+impl PartialEq<String> for Level {
#[inline]
fn eq(&self, s: &String) -> bool {
self == &s.as_str()
diff --git a/third_party/rust/unicode-bidi/src/lib.rs b/third_party/rust/unicode-bidi/src/lib.rs
index 1072b67fe0..489927588a 100644
--- a/third_party/rust/unicode-bidi/src/lib.rs
+++ b/third_party/rust/unicode-bidi/src/lib.rs
@@ -71,6 +71,8 @@
extern crate std;
#[macro_use]
extern crate alloc;
+#[cfg(feature = "smallvec")]
+extern crate smallvec;
pub mod data_source;
pub mod deprecated;
@@ -86,7 +88,7 @@ mod prepare;
pub use crate::char_data::{BidiClass, UNICODE_VERSION};
pub use crate::data_source::BidiDataSource;
pub use crate::level::{Level, LTR_LEVEL, RTL_LEVEL};
-pub use crate::prepare::LevelRun;
+pub use crate::prepare::{LevelRun, LevelRunVec};
#[cfg(feature = "hardcoded-data")]
pub use crate::char_data::{bidi_class, HardcodedBidiData};
@@ -99,6 +101,8 @@ use core::cmp;
use core::iter::repeat;
use core::ops::Range;
use core::str::CharIndices;
+#[cfg(feature = "smallvec")]
+use smallvec::SmallVec;
use crate::format_chars as chars;
use crate::BidiClass::*;
@@ -244,8 +248,14 @@ struct InitialInfoExt<'text> {
/// Parallel to base.paragraphs, records whether each paragraph is "pure LTR" that
/// requires no further bidi processing (i.e. there are no RTL characters or bidi
- /// control codes present).
- pure_ltr: Vec<bool>,
+ /// control codes present), and whether any bidi isolation controls are present.
+ flags: Vec<ParagraphInfoFlags>,
+}
+
+#[derive(PartialEq, Debug)]
+struct ParagraphInfoFlags {
+ is_pure_ltr: bool,
+ has_isolate_controls: bool,
}
impl<'text> InitialInfoExt<'text> {
@@ -265,12 +275,12 @@ impl<'text> InitialInfoExt<'text> {
default_para_level: Option<Level>,
) -> InitialInfoExt<'a> {
let mut paragraphs = Vec::<ParagraphInfo>::new();
- let mut pure_ltr = Vec::<bool>::new();
- let (original_classes, _, _) = compute_initial_info(
+ let mut flags = Vec::<ParagraphInfoFlags>::new();
+ let (original_classes, _, _, _) = compute_initial_info(
data_source,
text,
default_para_level,
- Some((&mut paragraphs, &mut pure_ltr)),
+ Some((&mut paragraphs, &mut flags)),
);
InitialInfoExt {
@@ -279,7 +289,7 @@ impl<'text> InitialInfoExt<'text> {
original_classes,
paragraphs,
},
- pure_ltr,
+ flags,
}
}
}
@@ -295,16 +305,19 @@ fn compute_initial_info<'a, D: BidiDataSource, T: TextSource<'a> + ?Sized>(
data_source: &D,
text: &'a T,
default_para_level: Option<Level>,
- mut split_paragraphs: Option<(&mut Vec<ParagraphInfo>, &mut Vec<bool>)>,
-) -> (Vec<BidiClass>, Level, bool) {
+ mut split_paragraphs: Option<(&mut Vec<ParagraphInfo>, &mut Vec<ParagraphInfoFlags>)>,
+) -> (Vec<BidiClass>, Level, bool, bool) {
let mut original_classes = Vec::with_capacity(text.len());
// The stack contains the starting code unit index for each nested isolate we're inside.
+ #[cfg(feature = "smallvec")]
+ let mut isolate_stack = SmallVec::<[usize; 8]>::new();
+ #[cfg(not(feature = "smallvec"))]
let mut isolate_stack = Vec::new();
debug_assert!(
- if let Some((ref paragraphs, ref pure_ltr)) = split_paragraphs {
- paragraphs.is_empty() && pure_ltr.is_empty()
+ if let Some((ref paragraphs, ref flags)) = split_paragraphs {
+ paragraphs.is_empty() && flags.is_empty()
} else {
true
}
@@ -316,6 +329,8 @@ fn compute_initial_info<'a, D: BidiDataSource, T: TextSource<'a> + ?Sized>(
// Per-paragraph flag: can subsequent processing be skipped? Set to false if any
// RTL characters or bidi control characters are encountered in the paragraph.
let mut is_pure_ltr = true;
+ // Set to true if any bidi isolation controls are present in the paragraph.
+ let mut has_isolate_controls = false;
#[cfg(feature = "flame_it")]
flame::start("compute_initial_info(): iter text.char_indices()");
@@ -334,7 +349,7 @@ fn compute_initial_info<'a, D: BidiDataSource, T: TextSource<'a> + ?Sized>(
match class {
B => {
- if let Some((ref mut paragraphs, ref mut pure_ltr)) = split_paragraphs {
+ if let Some((ref mut paragraphs, ref mut flags)) = split_paragraphs {
// P1. Split the text into separate paragraphs. The paragraph separator is kept
// with the previous paragraph.
let para_end = i + len;
@@ -343,7 +358,10 @@ fn compute_initial_info<'a, D: BidiDataSource, T: TextSource<'a> + ?Sized>(
// P3. If no character is found in p2, set the paragraph level to zero.
level: para_level.unwrap_or(LTR_LEVEL),
});
- pure_ltr.push(is_pure_ltr);
+ flags.push(ParagraphInfoFlags {
+ is_pure_ltr,
+ has_isolate_controls,
+ });
// Reset state for the start of the next paragraph.
para_start = para_end;
// TODO: Support defaulting to direction of previous paragraph
@@ -351,6 +369,7 @@ fn compute_initial_info<'a, D: BidiDataSource, T: TextSource<'a> + ?Sized>(
// <http://www.unicode.org/reports/tr9/#HL1>
para_level = default_para_level;
is_pure_ltr = true;
+ has_isolate_controls = false;
isolate_stack.clear();
}
}
@@ -387,6 +406,7 @@ fn compute_initial_info<'a, D: BidiDataSource, T: TextSource<'a> + ?Sized>(
RLI | LRI | FSI => {
is_pure_ltr = false;
+ has_isolate_controls = true;
isolate_stack.push(i);
}
@@ -398,15 +418,18 @@ fn compute_initial_info<'a, D: BidiDataSource, T: TextSource<'a> + ?Sized>(
}
}
- if let Some((paragraphs, pure_ltr)) = split_paragraphs {
+ if let Some((paragraphs, flags)) = split_paragraphs {
if para_start < text.len() {
paragraphs.push(ParagraphInfo {
range: para_start..text.len(),
level: para_level.unwrap_or(LTR_LEVEL),
});
- pure_ltr.push(is_pure_ltr);
+ flags.push(ParagraphInfoFlags {
+ is_pure_ltr,
+ has_isolate_controls,
+ });
}
- debug_assert_eq!(paragraphs.len(), pure_ltr.len());
+ debug_assert_eq!(paragraphs.len(), flags.len());
}
debug_assert_eq!(original_classes.len(), text.len());
@@ -417,6 +440,7 @@ fn compute_initial_info<'a, D: BidiDataSource, T: TextSource<'a> + ?Sized>(
original_classes,
para_level.unwrap_or(LTR_LEVEL),
is_pure_ltr,
+ has_isolate_controls,
)
}
@@ -475,20 +499,21 @@ impl<'text> BidiInfo<'text> {
text: &'a str,
default_para_level: Option<Level>,
) -> BidiInfo<'a> {
- let InitialInfoExt { base, pure_ltr, .. } =
+ let InitialInfoExt { base, flags, .. } =
InitialInfoExt::new_with_data_source(data_source, text, default_para_level);
let mut levels = Vec::<Level>::with_capacity(text.len());
let mut processing_classes = base.original_classes.clone();
- for (para, is_pure_ltr) in base.paragraphs.iter().zip(pure_ltr.iter()) {
+ for (para, flags) in base.paragraphs.iter().zip(flags.iter()) {
let text = &text[para.range.clone()];
let original_classes = &base.original_classes[para.range.clone()];
compute_bidi_info_for_para(
data_source,
para,
- *is_pure_ltr,
+ flags.is_pure_ltr,
+ flags.has_isolate_controls,
text,
original_classes,
&mut processing_classes,
@@ -713,7 +738,7 @@ impl<'text> ParagraphBidiInfo<'text> {
) -> ParagraphBidiInfo<'a> {
// Here we could create a ParagraphInitialInfo struct to parallel the one
// used by BidiInfo, but there doesn't seem any compelling reason for it.
- let (original_classes, paragraph_level, is_pure_ltr) =
+ let (original_classes, paragraph_level, is_pure_ltr, has_isolate_controls) =
compute_initial_info(data_source, text, default_para_level, None);
let mut levels = Vec::<Level>::with_capacity(text.len());
@@ -731,6 +756,7 @@ impl<'text> ParagraphBidiInfo<'text> {
data_source,
&para_info,
is_pure_ltr,
+ has_isolate_controls,
text,
&original_classes,
&mut processing_classes,
@@ -855,12 +881,12 @@ impl<'text> ParagraphBidiInfo<'text> {
///
/// [Rule L3]: https://www.unicode.org/reports/tr9/#L3
/// [Rule L4]: https://www.unicode.org/reports/tr9/#L4
-fn reorder_line<'text>(
- text: &'text str,
+fn reorder_line(
+ text: &str,
line: Range<usize>,
levels: Vec<Level>,
runs: Vec<LevelRun>,
-) -> Cow<'text, str> {
+) -> Cow<'_, str> {
// If all isolating run sequences are LTR, no reordering is needed
if runs.iter().all(|run| levels[run.start].is_ltr()) {
return text[line].into();
@@ -1059,6 +1085,7 @@ fn compute_bidi_info_for_para<'a, D: BidiDataSource, T: TextSource<'a> + ?Sized>
data_source: &D,
para: &ParagraphInfo,
is_pure_ltr: bool,
+ has_isolate_controls: bool,
text: &'a T,
original_classes: &[BidiClass],
processing_classes: &mut [BidiClass],
@@ -1072,6 +1099,7 @@ fn compute_bidi_info_for_para<'a, D: BidiDataSource, T: TextSource<'a> + ?Sized>
let processing_classes = &mut processing_classes[para.range.clone()];
let levels = &mut levels[para.range.clone()];
+ let mut level_runs = LevelRunVec::new();
explicit::compute(
text,
@@ -1079,9 +1107,18 @@ fn compute_bidi_info_for_para<'a, D: BidiDataSource, T: TextSource<'a> + ?Sized>
original_classes,
levels,
processing_classes,
+ &mut level_runs,
);
- let sequences = prepare::isolating_run_sequences(para.level, original_classes, levels);
+ let mut sequences = prepare::IsolatingRunSequenceVec::new();
+ prepare::isolating_run_sequences(
+ para.level,
+ original_classes,
+ levels,
+ level_runs,
+ has_isolate_controls,
+ &mut sequences,
+ );
for sequence in &sequences {
implicit::resolve_weak(text, sequence, processing_classes);
implicit::resolve_neutral(
@@ -1093,6 +1130,7 @@ fn compute_bidi_info_for_para<'a, D: BidiDataSource, T: TextSource<'a> + ?Sized>
processing_classes,
);
}
+
implicit::resolve_levels(processing_classes, levels);
assign_levels_to_removed_chars(para.level, original_classes, levels);
@@ -1122,20 +1160,20 @@ fn reorder_levels<'a, T: TextSource<'a> + ?Sized>(
B | S => {
assert_eq!(reset_to, None);
reset_to = Some(i + T::char_len(c));
- if reset_from == None {
+ if reset_from.is_none() {
reset_from = Some(i);
}
}
// Whitespace, isolate formatting
WS | FSI | LRI | RLI | PDI => {
- if reset_from == None {
+ if reset_from.is_none() {
reset_from = Some(i);
}
}
// <https://www.unicode.org/reports/tr9/#Retaining_Explicit_Formatting_Characters>
// same as above + set the level
RLE | LRE | RLO | LRO | PDF | BN => {
- if reset_from == None {
+ if reset_from.is_none() {
reset_from = Some(i);
}
// also set the level to previous
@@ -1294,8 +1332,8 @@ fn get_base_direction_impl<'a, D: BidiDataSource, T: TextSource<'a> + ?Sized>(
let mut isolate_level = 0;
for c in text.chars() {
match data_source.bidi_class(c) {
- LRI | RLI | FSI => isolate_level = isolate_level + 1,
- PDI if isolate_level > 0 => isolate_level = isolate_level - 1,
+ LRI | RLI | FSI => isolate_level += 1,
+ PDI if isolate_level > 0 => isolate_level -= 1,
L if isolate_level == 0 => return Direction::Ltr,
R | AL if isolate_level == 0 => return Direction::Rtl,
B if !use_full_text => break,
@@ -1342,7 +1380,7 @@ impl<'text> TextSource<'text> for str {
}
#[inline]
fn indices_lengths(&'text self) -> Self::IndexLenIter {
- Utf8IndexLenIter::new(&self)
+ Utf8IndexLenIter::new(self)
}
#[inline]
fn char_len(ch: char) -> usize {
@@ -1544,6 +1582,24 @@ mod tests {
let tests = vec![
(
// text
+ "",
+ // base level
+ Some(RTL_LEVEL),
+ // levels
+ Level::vec(&[]),
+ // original_classes
+ vec![],
+ // paragraphs
+ vec![],
+ // levels_u16
+ Level::vec(&[]),
+ // original_classes_u16
+ vec![],
+ // paragraphs_u16
+ vec![],
+ ),
+ (
+ // text
"abc123",
// base level
Some(LTR_LEVEL),
@@ -1703,6 +1759,19 @@ mod tests {
paragraphs: t.4.clone(),
}
);
+ // If it was empty, also test that ParagraphBidiInfo handles it safely.
+ if t.4.len() == 0 {
+ assert_eq!(
+ ParagraphBidiInfo::new(t.0, t.1),
+ ParagraphBidiInfo {
+ text: t.0,
+ original_classes: t.3.clone(),
+ levels: t.2.clone(),
+ paragraph_level: RTL_LEVEL,
+ is_pure_ltr: true,
+ }
+ )
+ }
// If it was a single paragraph, also test ParagraphBidiInfo.
if t.4.len() == 1 {
assert_eq!(
diff --git a/third_party/rust/unicode-bidi/src/prepare.rs b/third_party/rust/unicode-bidi/src/prepare.rs
index 9234e1aa61..f7b35ad689 100644
--- a/third_party/rust/unicode-bidi/src/prepare.rs
+++ b/third_party/rust/unicode-bidi/src/prepare.rs
@@ -14,6 +14,8 @@
use alloc::vec::Vec;
use core::cmp::max;
use core::ops::Range;
+#[cfg(feature = "smallvec")]
+use smallvec::{smallvec, SmallVec};
use super::level::Level;
use super::BidiClass::{self, *};
@@ -23,6 +25,11 @@ use super::BidiClass::{self, *};
/// Represented as a range of byte indices.
pub type LevelRun = Range<usize>;
+#[cfg(feature = "smallvec")]
+pub type LevelRunVec = SmallVec<[LevelRun; 8]>;
+#[cfg(not(feature = "smallvec"))]
+pub type LevelRunVec = Vec<LevelRun>;
+
/// Output of `isolating_run_sequences` (steps X9-X10)
#[derive(Debug, PartialEq)]
pub struct IsolatingRunSequence {
@@ -31,6 +38,11 @@ pub struct IsolatingRunSequence {
pub eos: BidiClass, // End-of-sequence type.
}
+#[cfg(feature = "smallvec")]
+pub type IsolatingRunSequenceVec = SmallVec<[IsolatingRunSequence; 8]>;
+#[cfg(not(feature = "smallvec"))]
+pub type IsolatingRunSequenceVec = Vec<IsolatingRunSequence>;
+
/// Compute the set of isolating run sequences.
///
/// An isolating run sequence is a maximal sequence of level runs such that for all level runs
@@ -43,8 +55,59 @@ pub fn isolating_run_sequences(
para_level: Level,
original_classes: &[BidiClass],
levels: &[Level],
-) -> Vec<IsolatingRunSequence> {
- let runs = level_runs(levels, original_classes);
+ runs: LevelRunVec,
+ has_isolate_controls: bool,
+ isolating_run_sequences: &mut IsolatingRunSequenceVec,
+) {
+ // Per http://www.unicode.org/reports/tr9/#BD13:
+ // "In the absence of isolate initiators, each isolating run sequence in a paragraph
+ // consists of exactly one level run, and each level run constitutes a separate
+ // isolating run sequence."
+ // We can take a simplified path to handle this case.
+ if !has_isolate_controls {
+ isolating_run_sequences.reserve_exact(runs.len());
+ for run in runs {
+ // Determine the `sos` and `eos` class for the sequence.
+ // <http://www.unicode.org/reports/tr9/#X10>
+
+ let run_levels = &levels[run.clone()];
+ let run_classes = &original_classes[run.clone()];
+ let seq_level = run_levels[run_classes
+ .iter()
+ .position(|c| not_removed_by_x9(c))
+ .unwrap_or(0)];
+
+ let end_level = run_levels[run_classes
+ .iter()
+ .rposition(|c| not_removed_by_x9(c))
+ .unwrap_or(run.end - run.start - 1)];
+
+ // Get the level of the last non-removed char before the run.
+ let pred_level = match original_classes[..run.start]
+ .iter()
+ .rposition(not_removed_by_x9)
+ {
+ Some(idx) => levels[idx],
+ None => para_level,
+ };
+
+ // Get the level of the next non-removed char after the run.
+ let succ_level = match original_classes[run.end..]
+ .iter()
+ .position(not_removed_by_x9)
+ {
+ Some(idx) => levels[run.end + idx],
+ None => para_level,
+ };
+
+ isolating_run_sequences.push(IsolatingRunSequence {
+ runs: vec![run],
+ sos: max(seq_level, pred_level).bidi_class(),
+ eos: max(end_level, succ_level).bidi_class(),
+ });
+ }
+ return;
+ }
// Compute the set of isolating run sequences.
// <http://www.unicode.org/reports/tr9/#BD13>
@@ -52,10 +115,13 @@ pub fn isolating_run_sequences(
// When we encounter an isolate initiator, we push the current sequence onto the
// stack so we can resume it after the matching PDI.
- let mut stack = vec![Vec::new()];
+ #[cfg(feature = "smallvec")]
+ let mut stack: SmallVec<[Vec<Range<usize>>; 8]> = smallvec![vec![]];
+ #[cfg(not(feature = "smallvec"))]
+ let mut stack = vec![vec![]];
for run in runs {
- assert!(run.len() > 0);
+ assert!(!run.is_empty());
assert!(!stack.is_empty());
let start_class = original_classes[run.start];
@@ -67,8 +133,7 @@ pub fn isolating_run_sequences(
.iter()
.copied()
.rev()
- .filter(not_removed_by_x9)
- .next()
+ .find(not_removed_by_x9)
.unwrap_or(start_class);
let mut sequence = if start_class == PDI && stack.len() > 1 {
@@ -81,7 +146,7 @@ pub fn isolating_run_sequences(
sequence.push(run);
- if let RLI | LRI | FSI = end_class {
+ if matches!(end_class, RLI | LRI | FSI) {
// Resume this sequence after the isolate.
stack.push(sequence);
} else {
@@ -89,90 +154,82 @@ pub fn isolating_run_sequences(
sequences.push(sequence);
}
}
- // Pop any remaning sequences off the stack.
+ // Pop any remaining sequences off the stack.
sequences.extend(stack.into_iter().rev().filter(|seq| !seq.is_empty()));
// Determine the `sos` and `eos` class for each sequence.
// <http://www.unicode.org/reports/tr9/#X10>
- sequences
- .into_iter()
- .map(|sequence: Vec<LevelRun>| {
- assert!(!sequence.is_empty());
+ for sequence in sequences {
+ assert!(!sequence.is_empty());
- let mut result = IsolatingRunSequence {
- runs: sequence,
- sos: L,
- eos: L,
- };
+ let start_of_seq = sequence[0].start;
+ let runs_len = sequence.len();
+ let end_of_seq = sequence[runs_len - 1].end;
- let start_of_seq = result.runs[0].start;
- let runs_len = result.runs.len();
- let end_of_seq = result.runs[runs_len - 1].end;
-
- // > (not counting characters removed by X9)
- let seq_level = result
- .iter_forwards_from(start_of_seq, 0)
- .filter(|i| not_removed_by_x9(&original_classes[*i]))
- .map(|i| levels[i])
- .next()
- .unwrap_or(levels[start_of_seq]);
-
- // XXXManishearth the spec talks of a start and end level,
- // but for a given IRS the two should be equivalent, yes?
- let end_level = result
- .iter_backwards_from(end_of_seq, runs_len - 1)
- .filter(|i| not_removed_by_x9(&original_classes[*i]))
- .map(|i| levels[i])
- .next()
- .unwrap_or(levels[end_of_seq - 1]);
-
- #[cfg(test)]
- for run in result.runs.clone() {
- for idx in run {
- if not_removed_by_x9(&original_classes[idx]) {
- assert_eq!(seq_level, levels[idx]);
- }
- }
+ let mut result = IsolatingRunSequence {
+ runs: sequence,
+ sos: L,
+ eos: L,
+ };
+
+ // > (not counting characters removed by X9)
+ let seq_level = levels[result
+ .iter_forwards_from(start_of_seq, 0)
+ .find(|i| not_removed_by_x9(&original_classes[*i]))
+ .unwrap_or(start_of_seq)];
+
+ // XXXManishearth the spec talks of a start and end level,
+ // but for a given IRS the two should be equivalent, yes?
+ let end_level = levels[result
+ .iter_backwards_from(end_of_seq, runs_len - 1)
+ .find(|i| not_removed_by_x9(&original_classes[*i]))
+ .unwrap_or(end_of_seq - 1)];
+
+ #[cfg(test)]
+ for idx in result.runs.clone().into_iter().flatten() {
+ if not_removed_by_x9(&original_classes[idx]) {
+ assert_eq!(seq_level, levels[idx]);
}
+ }
+
+ // Get the level of the last non-removed char before the runs.
+ let pred_level = match original_classes[..start_of_seq]
+ .iter()
+ .rposition(not_removed_by_x9)
+ {
+ Some(idx) => levels[idx],
+ None => para_level,
+ };
- // Get the level of the last non-removed char before the runs.
- let pred_level = match original_classes[..start_of_seq]
+ // Get the last non-removed character to check if it is an isolate initiator.
+ // The spec calls for an unmatched one, but matched isolate initiators
+ // will never be at the end of a level run (otherwise there would be more to the run).
+ // We unwrap_or(BN) because BN marks removed classes and it won't matter for the check.
+ let last_non_removed = original_classes[..end_of_seq]
+ .iter()
+ .copied()
+ .rev()
+ .find(not_removed_by_x9)
+ .unwrap_or(BN);
+
+ // Get the level of the next non-removed char after the runs.
+ let succ_level = if matches!(last_non_removed, RLI | LRI | FSI) {
+ para_level
+ } else {
+ match original_classes[end_of_seq..]
.iter()
- .rposition(not_removed_by_x9)
+ .position(not_removed_by_x9)
{
- Some(idx) => levels[idx],
+ Some(idx) => levels[end_of_seq + idx],
None => para_level,
- };
+ }
+ };
- // Get the last non-removed character to check if it is an isolate initiator.
- // The spec calls for an unmatched one, but matched isolate initiators
- // will never be at the end of a level run (otherwise there would be more to the run).
- // We unwrap_or(BN) because BN marks removed classes and it won't matter for the check.
- let last_non_removed = original_classes[..end_of_seq]
- .iter()
- .copied()
- .rev()
- .find(not_removed_by_x9)
- .unwrap_or(BN);
-
- // Get the level of the next non-removed char after the runs.
- let succ_level = if let RLI | LRI | FSI = last_non_removed {
- para_level
- } else {
- match original_classes[end_of_seq..]
- .iter()
- .position(not_removed_by_x9)
- {
- Some(idx) => levels[end_of_seq + idx],
- None => para_level,
- }
- };
+ result.sos = max(seq_level, pred_level).bidi_class();
+ result.eos = max(end_level, succ_level).bidi_class();
- result.sos = max(seq_level, pred_level).bidi_class();
- result.eos = max(end_level, succ_level).bidi_class();
- result
- })
- .collect()
+ isolating_run_sequences.push(result);
+ }
}
impl IsolatingRunSequence {
@@ -219,6 +276,9 @@ impl IsolatingRunSequence {
/// Finds the level runs in a paragraph.
///
/// <http://www.unicode.org/reports/tr9/#BD7>
+///
+/// This is only used by tests; normally level runs are identified during explicit::compute.
+#[cfg(test)]
fn level_runs(levels: &[Level], original_classes: &[BidiClass]) -> Vec<LevelRun> {
assert_eq!(levels.len(), original_classes.len());
@@ -246,10 +306,7 @@ fn level_runs(levels: &[Level], original_classes: &[BidiClass]) -> Vec<LevelRun>
///
/// <http://www.unicode.org/reports/tr9/#X9>
pub fn removed_by_x9(class: BidiClass) -> bool {
- match class {
- RLE | LRE | RLO | LRO | PDF | BN => true,
- _ => false,
- }
+ matches!(class, RLE | LRE | RLO | LRO | PDF | BN)
}
// For use as a predicate for `position` / `rposition`
@@ -281,7 +338,14 @@ mod tests {
let classes = &[L, RLE, L, PDF, RLE, L, PDF, L];
let levels = &[0, 1, 1, 1, 1, 1, 1, 0];
let para_level = Level::ltr();
- let mut sequences = isolating_run_sequences(para_level, classes, &Level::vec(levels));
+ let mut sequences = IsolatingRunSequenceVec::new();
+ isolating_run_sequences(
+ para_level,
+ classes,
+ &Level::vec(levels),
+ level_runs(&Level::vec(levels), classes).into(),
+ false,
+ &mut sequences);
sequences.sort_by(|a, b| a.runs[0].clone().cmp(b.runs[0].clone()));
assert_eq!(
sequences.iter().map(|s| s.runs.clone()).collect::<Vec<_>>(),
@@ -294,7 +358,14 @@ mod tests {
let classes = &[L, RLI, L, PDI, RLI, L, PDI, L];
let levels = &[0, 0, 1, 0, 0, 1, 0, 0];
let para_level = Level::ltr();
- let mut sequences = isolating_run_sequences(para_level, classes, &Level::vec(levels));
+ let mut sequences = IsolatingRunSequenceVec::new();
+ isolating_run_sequences(
+ para_level,
+ classes,
+ &Level::vec(levels),
+ level_runs(&Level::vec(levels), classes).into(),
+ true,
+ &mut sequences);
sequences.sort_by(|a, b| a.runs[0].clone().cmp(b.runs[0].clone()));
assert_eq!(
sequences.iter().map(|s| s.runs.clone()).collect::<Vec<_>>(),
@@ -307,7 +378,14 @@ mod tests {
let classes = &[L, RLI, L, LRI, L, RLE, L, PDF, L, PDI, L, PDI, L];
let levels = &[0, 0, 1, 1, 2, 3, 3, 3, 2, 1, 1, 0, 0];
let para_level = Level::ltr();
- let mut sequences = isolating_run_sequences(para_level, classes, &Level::vec(levels));
+ let mut sequences = IsolatingRunSequenceVec::new();
+ isolating_run_sequences(
+ para_level,
+ classes,
+ &Level::vec(levels),
+ level_runs(&Level::vec(levels), classes).into(),
+ true,
+ &mut sequences);
sequences.sort_by(|a, b| a.runs[0].clone().cmp(b.runs[0].clone()));
assert_eq!(
sequences.iter().map(|s| s.runs.clone()).collect::<Vec<_>>(),
@@ -326,7 +404,14 @@ mod tests {
let classes = &[L, RLE, L, LRE, L, PDF, L, PDF, RLE, L, PDF, L];
let levels = &[0, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0];
let para_level = Level::ltr();
- let mut sequences = isolating_run_sequences(para_level, classes, &Level::vec(levels));
+ let mut sequences = IsolatingRunSequenceVec::new();
+ isolating_run_sequences(
+ para_level,
+ classes,
+ &Level::vec(levels),
+ level_runs(&Level::vec(levels), classes).into(),
+ false,
+ &mut sequences);
sequences.sort_by(|a, b| a.runs[0].clone().cmp(b.runs[0].clone()));
// text1
@@ -385,7 +470,14 @@ mod tests {
let classes = &[L, RLI, L, LRI, L, PDI, L, PDI, RLI, L, PDI, L];
let levels = &[0, 0, 1, 1, 2, 1, 1, 0, 0, 1, 0, 0];
let para_level = Level::ltr();
- let mut sequences = isolating_run_sequences(para_level, classes, &Level::vec(levels));
+ let mut sequences = IsolatingRunSequenceVec::new();
+ isolating_run_sequences(
+ para_level,
+ classes,
+ &Level::vec(levels),
+ level_runs(&Level::vec(levels), classes).into(),
+ true,
+ &mut sequences);
sequences.sort_by(|a, b| a.runs[0].clone().cmp(b.runs[0].clone()));
// text1·RLI·PDI·RLI·PDI·text6
diff --git a/third_party/rust/unicode-bidi/src/utf16.rs b/third_party/rust/unicode-bidi/src/utf16.rs
index dcd9baf2be..11b386f91e 100644
--- a/third_party/rust/unicode-bidi/src/utf16.rs
+++ b/third_party/rust/unicode-bidi/src/utf16.rs
@@ -18,7 +18,9 @@ use crate::{
compute_bidi_info_for_para, compute_initial_info, level, para_direction, reorder_levels,
reorder_visual, visual_runs_for_line,
};
-use crate::{BidiClass, BidiDataSource, Direction, Level, LevelRun, ParagraphInfo};
+use crate::{
+ BidiClass, BidiDataSource, Direction, Level, LevelRun, ParagraphInfo, ParagraphInfoFlags,
+};
#[cfg(feature = "hardcoded-data")]
use crate::HardcodedBidiData;
@@ -83,7 +85,7 @@ struct InitialInfoExt<'text> {
/// Parallel to base.paragraphs, records whether each paragraph is "pure LTR" that
/// requires no further bidi processing (i.e. there are no RTL characters or bidi
/// control codes present).
- pure_ltr: Vec<bool>,
+ flags: Vec<ParagraphInfoFlags>,
}
impl<'text> InitialInfoExt<'text> {
@@ -103,12 +105,12 @@ impl<'text> InitialInfoExt<'text> {
default_para_level: Option<Level>,
) -> InitialInfoExt<'a> {
let mut paragraphs = Vec::<ParagraphInfo>::new();
- let mut pure_ltr = Vec::<bool>::new();
- let (original_classes, _, _) = compute_initial_info(
+ let mut flags = Vec::<ParagraphInfoFlags>::new();
+ let (original_classes, _, _, _) = compute_initial_info(
data_source,
text,
default_para_level,
- Some((&mut paragraphs, &mut pure_ltr)),
+ Some((&mut paragraphs, &mut flags)),
);
InitialInfoExt {
@@ -117,7 +119,7 @@ impl<'text> InitialInfoExt<'text> {
original_classes,
paragraphs,
},
- pure_ltr,
+ flags,
}
}
}
@@ -177,20 +179,21 @@ impl<'text> BidiInfo<'text> {
text: &'a [u16],
default_para_level: Option<Level>,
) -> BidiInfo<'a> {
- let InitialInfoExt { base, pure_ltr, .. } =
+ let InitialInfoExt { base, flags, .. } =
InitialInfoExt::new_with_data_source(data_source, text, default_para_level);
let mut levels = Vec::<Level>::with_capacity(text.len());
let mut processing_classes = base.original_classes.clone();
- for (para, is_pure_ltr) in base.paragraphs.iter().zip(pure_ltr.iter()) {
+ for (para, flags) in base.paragraphs.iter().zip(flags.iter()) {
let text = &text[para.range.clone()];
let original_classes = &base.original_classes[para.range.clone()];
compute_bidi_info_for_para(
data_source,
para,
- *is_pure_ltr,
+ flags.is_pure_ltr,
+ flags.has_isolate_controls,
text,
original_classes,
&mut processing_classes,
@@ -411,7 +414,7 @@ impl<'text> ParagraphBidiInfo<'text> {
) -> ParagraphBidiInfo<'a> {
// Here we could create a ParagraphInitialInfo struct to parallel the one
// used by BidiInfo, but there doesn't seem any compelling reason for it.
- let (original_classes, paragraph_level, is_pure_ltr) =
+ let (original_classes, paragraph_level, is_pure_ltr, has_isolate_controls) =
compute_initial_info(data_source, text, default_para_level, None);
let mut levels = Vec::<Level>::with_capacity(text.len());
@@ -429,6 +432,7 @@ impl<'text> ParagraphBidiInfo<'text> {
data_source,
&para_info,
is_pure_ltr,
+ has_isolate_controls,
text,
&original_classes,
&mut processing_classes,
@@ -551,12 +555,12 @@ impl<'text> ParagraphBidiInfo<'text> {
///
/// [Rule L3]: https://www.unicode.org/reports/tr9/#L3
/// [Rule L4]: https://www.unicode.org/reports/tr9/#L4
-fn reorder_line<'text>(
- text: &'text [u16],
+fn reorder_line(
+ text: &[u16],
line: Range<usize>,
levels: Vec<Level>,
runs: Vec<LevelRun>,
-) -> Cow<'text, [u16]> {
+) -> Cow<'_, [u16]> {
// If all isolating run sequences are LTR, no reordering is needed
if runs.iter().all(|run| levels[run.start].is_ltr()) {
return text[line].into();
@@ -668,15 +672,15 @@ impl<'text> TextSource<'text> for [u16] {
}
#[inline]
fn chars(&'text self) -> Self::CharIter {
- Utf16CharIter::new(&self)
+ Utf16CharIter::new(self)
}
#[inline]
fn char_indices(&'text self) -> Self::CharIndexIter {
- Utf16CharIndexIter::new(&self)
+ Utf16CharIndexIter::new(self)
}
#[inline]
fn indices_lengths(&'text self) -> Self::IndexLenIter {
- Utf16IndexLenIter::new(&self)
+ Utf16IndexLenIter::new(self)
}
#[inline]
fn char_len(ch: char) -> usize {
diff --git a/third_party/rust/wgpu-core/.cargo-checksum.json b/third_party/rust/wgpu-core/.cargo-checksum.json
index bf0a48e81a..d22e0914d7 100644
--- a/third_party/rust/wgpu-core/.cargo-checksum.json
+++ b/third_party/rust/wgpu-core/.cargo-checksum.json
@@ -1 +1 @@
-{"files":{"Cargo.toml":"92c0bcfb5bf68fb55acb6e7b826ec07c1cfdd6d53b057c16a5c698e044ea228e","LICENSE.APACHE":"a6cba85bc92e0cff7a450b1d873c0eaa2e9fc96bf472df0247a26bec77bf3ff9","LICENSE.MIT":"c7fea58d1cfe49634cd92e54fc10a9d871f4b275321a4cd8c09e449122caaeb4","build.rs":"a99478d7f63fb41429e3834f4d0e5cd333f94ba1834c68295f929170e16987de","src/any_surface.rs":"1c032bc1894a222a47f0116b976f1543c1140c0534678502ee1172d4f77fc515","src/binding_model.rs":"2bd4e4a36742ccf0cab0afa039411a791e2a6e9ea3909d0b85cc9a84cc151c6b","src/command/bind.rs":"a37f042484b65d9fdea4cdab3667381623ee9a8943a6d32683d410b92736d306","src/command/bundle.rs":"91513a3be0adf46a9f3454b6a3d00ff6686729eb91fe9dd6d732cbfa1ff6d1d8","src/command/clear.rs":"b20e93c4b8cb47062b38e472f78d28d9ec00fd1169b17a87094be7f9d1c995e1","src/command/compute.rs":"eb60f0e2842dd20b366905225af24f4ca2a1b0c67914b86009c5b870b26f747f","src/command/draw.rs":"e8a664fc248e273e8c0e4aaeb645010b3f4ec61d29d858137f31f6f653c86542","src/command/memory_init.rs":"6ec93b9e2eb21edaa534e60770b4ba95735e9de61e74d827bc492df8e3639449","src/command/mod.rs":"d6a66a5796bd824be72af2c8d3ece59a507090c61cb50e9856eb4c70a28945e2","src/command/query.rs":"dffc843746b10ba9a50b8a2b92a59b407b56a845619a96d72a5883588fcb50f0","src/command/render.rs":"c3783b4f19b4eafb33f94554aea69408d42e40b5e98da22aa804a0931430ea6f","src/command/transfer.rs":"bf1077d1a99a258bad46087ae7234703627e7f4d30b38e6142d016c02deaad3a","src/conv.rs":"7e3ffe33b47a6fd3617aabf9f11cc68f1ccbee2c7343b8dbbcd0e8f3447e1ad8","src/device/any_device.rs":"65f47b58939b60f88f47861e65d5d45209492df8e73e7c1b60b3b459f510c09e","src/device/bgl.rs":"ec8bdd6e9b4cd50c25bed317275863d0c16bb6619f62ed85bf0464948010dfc1","src/device/global.rs":"7d70a45bd39e251c6945fc475883c4e69632f92a7abe263adab6e47a248de5a4","src/device/life.rs":"cd12343d5a14d82b18b787991811b36f420719776336f8a65b45c32fd47a77d4","src/device/mod.rs":"fff41f92e1a9f6660e18dc30452d9911ca827701bb8303af2ae06f1c1e1a795f","src/device/queue.rs":"2ffc477d1bebb35a1fc8e46f4ca2c5ef50a4eb6034968f076062461b2e678699","src/device/resource.rs":"4f22cf27da8d829b624877d7d3bb10971a0e8fb7c4f95d85d5011049a010684a","src/device/trace.rs":"9deb1b083165e07253b4928ac2f564aba06f9089c3aca1c0a1d438d87d981542","src/error.rs":"e3b6b7a69877437f4e46af7f0e8ca1db1822beae7c8448db41c2bae0f64b2bb4","src/global.rs":"0966475959706650fd036a18d51441a8e14c3ef10107db617f597614ca47e50a","src/hal_api.rs":"1cd9c3fe1c9d8c3a24e3e7f963a2ef26e056a2b26d529b840dbc969090aaf201","src/hash_utils.rs":"e8d484027c7ce81978e4679a5e20af9416ab7d2fa595f1ca95992b29d625b0ca","src/hub.rs":"352a1b75d4535f24b06d16134421db98f910e6e719f50f863a204df6768e3369","src/id.rs":"c736c0b3d35cf620e2c01322d57c4938b42828b39948ecad82d39fc39c1093c1","src/identity.rs":"c6a719389d71bb11c9ceaeadb0496f8b4c6ad24e35597e12b40980ad7ad72f10","src/init_tracker/buffer.rs":"61eb9cfaa312135b7a937ff6a3117f531b5b7323fae6553a41d6de9bc106d7e0","src/init_tracker/mod.rs":"a0f64730cc025113b656b4690f9dcb0ec18b8770bc7ef24c7b4ad8bebae03d24","src/init_tracker/texture.rs":"030fd594bf9948fad391390d85c5e1fec7eaf67b6e812c60f2dd59bc4fda8fd5","src/instance.rs":"c9b5b53a0aeac8e117d49a3a007fab001cd5737e29dd75388cdbfc24f3d8df08","src/lib.rs":"49174591f8116c3b8fadb185f89ce69ae931ee6e9f639d2558848db82ea1651f","src/pipeline.rs":"300f58afc16c454ce52aabff6debd7a7db85ed627b111a8801bcb201827f110c","src/pool.rs":"778ea1c23fcfaaa5001606e686f712f606826039d60dd5a3cd26e7de91ac057a","src/present.rs":"86b1e8bd7314f77f083be6d89a2f734e92f2ed11c86eb4c912c754fcdaa2e597","src/registry.rs":"dbc9310a24a843cf6b94a4bab78b0bb5f325e18c1f3c19c94d4f12b4f29e8598","src/resource.rs":"cd568c9d1abd4bf740cb86efae7862b5478518f3b1cdaf792ae05b3c0920c8e0","src/snatch.rs":"29a1135ee09c06883eac4df6f45b7220c2ba8f89f34232ea1d270d6e7b05c7a8","src/storage.rs":"f0c41461b8f9cdc862dbd3de04c8e720ee416c7c57310696f6f4fd22183fcc85","src/track/buffer.rs":"65c27dfabe7a1c3e4ddbde7189e53b2e95f3f3663aa82b121801a2fd0dcbd304","src/track/metadata.rs":"ac82a9c69b0a141b5c3ca69b203c5aa2a17578b598cab3ae156b917cef734b97","src/track/mod.rs":"8f03955447544f3ebcb48547440a48d321ad1ff0e0c601a62623b5457763b8de","src/track/range.rs":"2a15794e79b0470d5ba6b3267173a42f34312878e1cb288f198d2854a7888e53","src/track/stateless.rs":"2da10160c46d07ad15986ba6f1356b7933806fc5c3fa5a9d8deea44d9a3c93a7","src/track/texture.rs":"15892e639f2ecbb13c8d34c29e3fd6ad719cb71e2d40c64910b552b8985ddab0","src/validation.rs":"613c58c3601f36d6aa5986cea01f30497c6bd4ceb990824904d101b2327941a9"},"package":null} \ No newline at end of file
+{"files":{"Cargo.toml":"4880d66b004519ca6e424fc9e2e6ac065536d36334a2e327b90422e97f2a2a35","LICENSE.APACHE":"a6cba85bc92e0cff7a450b1d873c0eaa2e9fc96bf472df0247a26bec77bf3ff9","LICENSE.MIT":"c7fea58d1cfe49634cd92e54fc10a9d871f4b275321a4cd8c09e449122caaeb4","build.rs":"a99478d7f63fb41429e3834f4d0e5cd333f94ba1834c68295f929170e16987de","src/any_surface.rs":"1c032bc1894a222a47f0116b976f1543c1140c0534678502ee1172d4f77fc515","src/binding_model.rs":"bb4aefad17957e770a5f70f00bf5853dc13da1d9f836493c9aa9adbbe7bb8147","src/command/bind.rs":"a37f042484b65d9fdea4cdab3667381623ee9a8943a6d32683d410b92736d306","src/command/bundle.rs":"fea00382acdf204bcb58522953335dd8f0092565693fa65d0c008e2698e39445","src/command/clear.rs":"03cfc0d4c689d56010391440ab279e615ef1d3235eb1f9f9df0323682d275109","src/command/compute.rs":"2b6beed328ed351ad6fe7088cfa1824c1bf4be50deaeab971cdcb09914d791de","src/command/draw.rs":"15f9ad857504d8098279f9c789317feba321c9b6b8f0de20b8ba98f358c99d89","src/command/memory_init.rs":"6ec93b9e2eb21edaa534e60770b4ba95735e9de61e74d827bc492df8e3639449","src/command/mod.rs":"1d347e1746194f7a07d1f75bd3a9d3cbe121fbaa479c25ba6b8c16e9d699e06b","src/command/query.rs":"43b78a163eb0eb5f1427b7a57b6d39a2748c25f880ba024c91e2f71e2a6a817d","src/command/render.rs":"808dc8106811b32877637851e63baeba7c7438748dec67cbb17ea93c58dc61bd","src/command/transfer.rs":"bf1077d1a99a258bad46087ae7234703627e7f4d30b38e6142d016c02deaad3a","src/conv.rs":"7e3ffe33b47a6fd3617aabf9f11cc68f1ccbee2c7343b8dbbcd0e8f3447e1ad8","src/device/any_device.rs":"65f47b58939b60f88f47861e65d5d45209492df8e73e7c1b60b3b459f510c09e","src/device/bgl.rs":"ec8bdd6e9b4cd50c25bed317275863d0c16bb6619f62ed85bf0464948010dfc1","src/device/global.rs":"ff90a9e3b261bedbec37ab1aed0bf23f1e50c5418da72184e2b175057ed18fce","src/device/life.rs":"3cacaaa74df04bb1285a36d70395b35cfa17059f8d6289b41e665ecbc64cb66a","src/device/mod.rs":"fff41f92e1a9f6660e18dc30452d9911ca827701bb8303af2ae06f1c1e1a795f","src/device/queue.rs":"da0aeebfd1d1c6e155dc89cebf75dfdb6ec18062f9512044ed7e0fef0bda2f74","src/device/resource.rs":"74d3180c12602133bee46925d3788ac510d2ad5ea141a2b46f6904f38549053b","src/device/trace.rs":"9deb1b083165e07253b4928ac2f564aba06f9089c3aca1c0a1d438d87d981542","src/error.rs":"e3b6b7a69877437f4e46af7f0e8ca1db1822beae7c8448db41c2bae0f64b2bb4","src/global.rs":"0966475959706650fd036a18d51441a8e14c3ef10107db617f597614ca47e50a","src/hal_api.rs":"1cd9c3fe1c9d8c3a24e3e7f963a2ef26e056a2b26d529b840dbc969090aaf201","src/hash_utils.rs":"e8d484027c7ce81978e4679a5e20af9416ab7d2fa595f1ca95992b29d625b0ca","src/hub.rs":"352a1b75d4535f24b06d16134421db98f910e6e719f50f863a204df6768e3369","src/id.rs":"9f67dbef5d7a416eb740281ecf8a94673f624da16f21ec33c425c11d9ed01e90","src/identity.rs":"12b820eb4b8bd7b226e15eec97d0f100a695f6b9be7acd79ad2421f2d0fe1985","src/init_tracker/buffer.rs":"61eb9cfaa312135b7a937ff6a3117f531b5b7323fae6553a41d6de9bc106d7e0","src/init_tracker/mod.rs":"a0f64730cc025113b656b4690f9dcb0ec18b8770bc7ef24c7b4ad8bebae03d24","src/init_tracker/texture.rs":"030fd594bf9948fad391390d85c5e1fec7eaf67b6e812c60f2dd59bc4fda8fd5","src/instance.rs":"b6de2a371ef3b43d3217102fe87e423dd1eb12da86b65f54b902d9eaa38b6b9f","src/lib.rs":"4ad9979442cf88557fb3b9f8d3b26c7b929a710c60cabcd1f51788917c95aecb","src/pipeline.rs":"89d88de4b8b8e1dd2bc834d101a1bdf34816ebcaa616dc795f604e9183a21cd0","src/pool.rs":"778ea1c23fcfaaa5001606e686f712f606826039d60dd5a3cd26e7de91ac057a","src/present.rs":"f69580ee0baf181162f9dd82b159596c738558d8abb60db93047effbe1436b2f","src/registry.rs":"913e651dc585ff12fe7659443c38d635a2904881e56cb7159c5ca72d45ae5800","src/resource.rs":"59731bc9a207d87b07b6db9c897e20d64be27c144bb8eb8ab2505807163acfc4","src/snatch.rs":"29a1135ee09c06883eac4df6f45b7220c2ba8f89f34232ea1d270d6e7b05c7a8","src/storage.rs":"f0c41461b8f9cdc862dbd3de04c8e720ee416c7c57310696f6f4fd22183fcc85","src/track/buffer.rs":"83a0cbb8026dbd651d32ea5a47f332f691afed1c5e6f14e78a4fe8aa25e2ad12","src/track/metadata.rs":"655985fdfdd1c7fe8220af98abadf33de7e8920b485e3dd27c28688c3dd2e47d","src/track/mod.rs":"52470a48de6b5dce55385e23ba7a3cbf512cc10cdf431a35aa42190e2fc4306d","src/track/range.rs":"2a15794e79b0470d5ba6b3267173a42f34312878e1cb288f198d2854a7888e53","src/track/stateless.rs":"305e0a493fb1cd0a325274c0757e99c19f9d14deaa8ca11ada41c1399a4ae5c4","src/track/texture.rs":"ba3e3814b341b5242548b55d77bef1d1d9e7d52d63784be98c51e342da7fefff","src/validation.rs":"026168ac4f23bc6a58a90c78fd3eb73485b3c1aad630ef43755604d1babade79"},"package":null} \ No newline at end of file
diff --git a/third_party/rust/wgpu-core/Cargo.toml b/third_party/rust/wgpu-core/Cargo.toml
index f9692cf607..3d3b4dc80c 100644
--- a/third_party/rust/wgpu-core/Cargo.toml
+++ b/third_party/rust/wgpu-core/Cargo.toml
@@ -41,6 +41,7 @@ arrayvec = "0.7"
bit-vec = "0.6"
bitflags = "2"
codespan-reporting = "0.11"
+document-features = "0.2.8"
indexmap = "2"
log = "0.4"
once_cell = "1"
diff --git a/third_party/rust/wgpu-core/src/binding_model.rs b/third_party/rust/wgpu-core/src/binding_model.rs
index d7b54ad5a5..8689af2ac1 100644
--- a/third_party/rust/wgpu-core/src/binding_model.rs
+++ b/third_party/rust/wgpu-core/src/binding_model.rs
@@ -38,6 +38,8 @@ pub enum BindGroupLayoutEntryError {
ArrayUnsupported,
#[error("Multisampled binding with sample type `TextureSampleType::Float` must have filterable set to false.")]
SampleTypeFloatFilterableBindingMultisampled,
+ #[error("Multisampled texture binding view dimension must be 2d, got {0:?}")]
+ Non2DMultisampled(wgt::TextureViewDimension),
#[error(transparent)]
MissingFeatures(#[from] MissingFeatures),
#[error(transparent)]
@@ -219,7 +221,7 @@ pub enum BindingZone {
}
#[derive(Clone, Debug, Error)]
-#[error("Too many bindings of type {kind:?} in {zone}, limit is {limit}, count was {count}")]
+#[error("Too many bindings of type {kind:?} in {zone}, limit is {limit}, count was {count}. Check the limit `{}` passed to `Adapter::request_device`", .kind.to_config_str())]
pub struct BindingTypeMaxCountError {
pub kind: BindingTypeMaxCountErrorKind,
pub zone: BindingZone,
@@ -238,6 +240,28 @@ pub enum BindingTypeMaxCountErrorKind {
UniformBuffers,
}
+impl BindingTypeMaxCountErrorKind {
+ fn to_config_str(&self) -> &'static str {
+ match self {
+ BindingTypeMaxCountErrorKind::DynamicUniformBuffers => {
+ "max_dynamic_uniform_buffers_per_pipeline_layout"
+ }
+ BindingTypeMaxCountErrorKind::DynamicStorageBuffers => {
+ "max_dynamic_storage_buffers_per_pipeline_layout"
+ }
+ BindingTypeMaxCountErrorKind::SampledTextures => {
+ "max_sampled_textures_per_shader_stage"
+ }
+ BindingTypeMaxCountErrorKind::Samplers => "max_samplers_per_shader_stage",
+ BindingTypeMaxCountErrorKind::StorageBuffers => "max_storage_buffers_per_shader_stage",
+ BindingTypeMaxCountErrorKind::StorageTextures => {
+ "max_storage_textures_per_shader_stage"
+ }
+ BindingTypeMaxCountErrorKind::UniformBuffers => "max_uniform_buffers_per_shader_stage",
+ }
+ }
+}
+
#[derive(Debug, Default)]
pub(crate) struct PerStageBindingTypeCounter {
vertex: u32,
diff --git a/third_party/rust/wgpu-core/src/command/bundle.rs b/third_party/rust/wgpu-core/src/command/bundle.rs
index 9d80c62f85..ab2d18bc59 100644
--- a/third_party/rust/wgpu-core/src/command/bundle.rs
+++ b/third_party/rust/wgpu-core/src/command/bundle.rs
@@ -97,7 +97,7 @@ use crate::{
id,
init_tracker::{BufferInitTrackerAction, MemoryInitKind, TextureInitTrackerAction},
pipeline::{PipelineFlags, RenderPipeline, VertexStep},
- resource::{Resource, ResourceInfo, ResourceType},
+ resource::{Buffer, Resource, ResourceInfo, ResourceType},
resource_log,
track::RenderBundleScope,
validation::check_buffer_usage,
@@ -110,9 +110,11 @@ use thiserror::Error;
use hal::CommandEncoder as _;
+use super::ArcRenderCommand;
+
/// https://gpuweb.github.io/gpuweb/#dom-gpurendercommandsmixin-draw
-fn validate_draw(
- vertex: &[Option<VertexState>],
+fn validate_draw<A: HalApi>(
+ vertex: &[Option<VertexState<A>>],
step: &[VertexStep],
first_vertex: u32,
vertex_count: u32,
@@ -152,10 +154,10 @@ fn validate_draw(
}
// See https://gpuweb.github.io/gpuweb/#dom-gpurendercommandsmixin-drawindexed
-fn validate_indexed_draw(
- vertex: &[Option<VertexState>],
+fn validate_indexed_draw<A: HalApi>(
+ vertex: &[Option<VertexState<A>>],
step: &[VertexStep],
- index_state: &IndexState,
+ index_state: &IndexState<A>,
first_index: u32,
index_count: u32,
first_instance: u32,
@@ -260,6 +262,9 @@ impl RenderBundleEncoder {
None => (true, true),
};
+ // TODO: should be device.limits.max_color_attachments
+ let max_color_attachments = hal::MAX_COLOR_ATTACHMENTS;
+
//TODO: validate that attachment formats are renderable,
// have expected aspects, support multisampling.
Ok(Self {
@@ -267,11 +272,11 @@ impl RenderBundleEncoder {
parent_id,
context: RenderPassContext {
attachments: AttachmentData {
- colors: if desc.color_formats.len() > hal::MAX_COLOR_ATTACHMENTS {
+ colors: if desc.color_formats.len() > max_color_attachments {
return Err(CreateRenderBundleError::ColorAttachment(
ColorAttachmentError::TooMany {
given: desc.color_formats.len(),
- limit: hal::MAX_COLOR_ATTACHMENTS,
+ limit: max_color_attachments,
},
));
} else {
@@ -345,24 +350,44 @@ impl RenderBundleEncoder {
) -> Result<RenderBundle<A>, RenderBundleError> {
let bind_group_guard = hub.bind_groups.read();
let pipeline_guard = hub.render_pipelines.read();
- let query_set_guard = hub.query_sets.read();
let buffer_guard = hub.buffers.read();
- let texture_guard = hub.textures.read();
let mut state = State {
- trackers: RenderBundleScope::new(
- &*buffer_guard,
- &*texture_guard,
- &*bind_group_guard,
- &*pipeline_guard,
- &*query_set_guard,
- ),
+ trackers: RenderBundleScope::new(),
pipeline: None,
bind: (0..hal::MAX_BIND_GROUPS).map(|_| None).collect(),
vertex: (0..hal::MAX_VERTEX_BUFFERS).map(|_| None).collect(),
index: None,
flat_dynamic_offsets: Vec::new(),
};
+
+ let indices = &device.tracker_indices;
+ state
+ .trackers
+ .buffers
+ .write()
+ .set_size(indices.buffers.size());
+ state
+ .trackers
+ .textures
+ .write()
+ .set_size(indices.textures.size());
+ state
+ .trackers
+ .bind_groups
+ .write()
+ .set_size(indices.bind_groups.size());
+ state
+ .trackers
+ .render_pipelines
+ .write()
+ .set_size(indices.render_pipelines.size());
+ state
+ .trackers
+ .query_sets
+ .write()
+ .set_size(indices.query_sets.size());
+
let mut commands = Vec::new();
let mut buffer_memory_init_actions = Vec::new();
let mut texture_memory_init_actions = Vec::new();
@@ -399,7 +424,6 @@ impl RenderBundleEncoder {
}
// Identify the next `num_dynamic_offsets` entries from `base.dynamic_offsets`.
- let num_dynamic_offsets = num_dynamic_offsets;
let offsets_range =
next_dynamic_offset..next_dynamic_offset + num_dynamic_offsets;
next_dynamic_offset = offsets_range.end;
@@ -471,7 +495,7 @@ impl RenderBundleEncoder {
let pipeline_state = PipelineState::new(pipeline);
- commands.push(command);
+ commands.push(ArcRenderCommand::SetPipeline(pipeline.clone()));
// If this pipeline uses push constants, zero out their values.
if let Some(iter) = pipeline_state.zero_push_constants() {
@@ -496,7 +520,7 @@ impl RenderBundleEncoder {
.map_pass_err(scope)?;
self.check_valid_to_use(buffer.device.info.id())
.map_pass_err(scope)?;
- check_buffer_usage(buffer.usage, wgt::BufferUsages::INDEX)
+ check_buffer_usage(buffer_id, buffer.usage, wgt::BufferUsages::INDEX)
.map_pass_err(scope)?;
let end = match size {
@@ -508,7 +532,7 @@ impl RenderBundleEncoder {
offset..end,
MemoryInitKind::NeedsInitializedMemory,
));
- state.set_index_buffer(buffer_id, index_format, offset..end);
+ state.set_index_buffer(buffer.clone(), index_format, offset..end);
}
RenderCommand::SetVertexBuffer {
slot,
@@ -535,7 +559,7 @@ impl RenderBundleEncoder {
.map_pass_err(scope)?;
self.check_valid_to_use(buffer.device.info.id())
.map_pass_err(scope)?;
- check_buffer_usage(buffer.usage, wgt::BufferUsages::VERTEX)
+ check_buffer_usage(buffer_id, buffer.usage, wgt::BufferUsages::VERTEX)
.map_pass_err(scope)?;
let end = match size {
@@ -547,13 +571,13 @@ impl RenderBundleEncoder {
offset..end,
MemoryInitKind::NeedsInitializedMemory,
));
- state.vertex[slot as usize] = Some(VertexState::new(buffer_id, offset..end));
+ state.vertex[slot as usize] = Some(VertexState::new(buffer.clone(), offset..end));
}
RenderCommand::SetPushConstant {
stages,
offset,
size_bytes,
- values_offset: _,
+ values_offset,
} => {
let scope = PassErrorScope::SetPushConstant;
let end_offset = offset + size_bytes;
@@ -564,7 +588,7 @@ impl RenderBundleEncoder {
.validate_push_constant_ranges(stages, offset, end_offset)
.map_pass_err(scope)?;
- commands.push(command);
+ commands.push(ArcRenderCommand::SetPushConstant { stages, offset, size_bytes, values_offset });
}
RenderCommand::Draw {
vertex_count,
@@ -592,14 +616,19 @@ impl RenderBundleEncoder {
if instance_count > 0 && vertex_count > 0 {
commands.extend(state.flush_vertices());
commands.extend(state.flush_binds(used_bind_groups, base.dynamic_offsets));
- commands.push(command);
+ commands.push(ArcRenderCommand::Draw {
+ vertex_count,
+ instance_count,
+ first_vertex,
+ first_instance,
+ });
}
}
RenderCommand::DrawIndexed {
index_count,
instance_count,
first_index,
- base_vertex: _,
+ base_vertex,
first_instance,
} => {
let scope = PassErrorScope::Draw {
@@ -628,7 +657,7 @@ impl RenderBundleEncoder {
commands.extend(state.flush_index());
commands.extend(state.flush_vertices());
commands.extend(state.flush_binds(used_bind_groups, base.dynamic_offsets));
- commands.push(command);
+ commands.push(ArcRenderCommand::DrawIndexed { index_count, instance_count, first_index, base_vertex, first_instance });
}
}
RenderCommand::MultiDrawIndirect {
@@ -657,7 +686,7 @@ impl RenderBundleEncoder {
.map_pass_err(scope)?;
self.check_valid_to_use(buffer.device.info.id())
.map_pass_err(scope)?;
- check_buffer_usage(buffer.usage, wgt::BufferUsages::INDIRECT)
+ check_buffer_usage(buffer_id, buffer.usage, wgt::BufferUsages::INDIRECT)
.map_pass_err(scope)?;
buffer_memory_init_actions.extend(buffer.initialization_status.read().create_action(
@@ -668,7 +697,7 @@ impl RenderBundleEncoder {
commands.extend(state.flush_vertices());
commands.extend(state.flush_binds(used_bind_groups, base.dynamic_offsets));
- commands.push(command);
+ commands.push(ArcRenderCommand::MultiDrawIndirect { buffer: buffer.clone(), offset, count: None, indexed: false });
}
RenderCommand::MultiDrawIndirect {
buffer_id,
@@ -696,7 +725,7 @@ impl RenderBundleEncoder {
.map_pass_err(scope)?;
self.check_valid_to_use(buffer.device.info.id())
.map_pass_err(scope)?;
- check_buffer_usage(buffer.usage, wgt::BufferUsages::INDIRECT)
+ check_buffer_usage(buffer_id, buffer.usage, wgt::BufferUsages::INDIRECT)
.map_pass_err(scope)?;
buffer_memory_init_actions.extend(buffer.initialization_status.read().create_action(
@@ -713,7 +742,7 @@ impl RenderBundleEncoder {
commands.extend(index.flush());
commands.extend(state.flush_vertices());
commands.extend(state.flush_binds(used_bind_groups, base.dynamic_offsets));
- commands.push(command);
+ commands.push(ArcRenderCommand::MultiDrawIndirect { buffer: buffer.clone(), offset, count: None, indexed: true });
}
RenderCommand::MultiDrawIndirect { .. }
| RenderCommand::MultiDrawIndirectCount { .. } => unimplemented!(),
@@ -748,7 +777,10 @@ impl RenderBundleEncoder {
buffer_memory_init_actions,
texture_memory_init_actions,
context: self.context,
- info: ResourceInfo::new(desc.label.borrow_or_default()),
+ info: ResourceInfo::new(
+ desc.label.borrow_or_default(),
+ Some(device.tracker_indices.bundles.clone()),
+ ),
discard_hal_labels: device
.instance_flags
.contains(wgt::InstanceFlags::DISCARD_HAL_LABELS),
@@ -824,7 +856,7 @@ pub type RenderBundleDescriptor<'a> = wgt::RenderBundleDescriptor<Label<'a>>;
pub struct RenderBundle<A: HalApi> {
// Normalized command stream. It can be executed verbatim,
// without re-binding anything on the pipeline change.
- base: BasePass<RenderCommand>,
+ base: BasePass<ArcRenderCommand<A>>,
pub(super) is_depth_read_only: bool,
pub(super) is_stencil_read_only: bool,
pub(crate) device: Arc<Device<A>>,
@@ -863,7 +895,6 @@ impl<A: HalApi> RenderBundle<A> {
/// All the validation has already been done by this point.
/// The only failure condition is if some of the used buffers are destroyed.
pub(super) unsafe fn execute(&self, raw: &mut A::CommandEncoder) -> Result<(), ExecutionError> {
- let trackers = &self.used;
let mut offsets = self.base.dynamic_offsets.as_slice();
let mut pipeline_layout = None::<Arc<PipelineLayout<A>>>;
if !self.discard_hal_labels {
@@ -874,74 +905,65 @@ impl<A: HalApi> RenderBundle<A> {
let snatch_guard = self.device.snatchable_lock.read();
+ use ArcRenderCommand as Cmd;
for command in self.base.commands.iter() {
- match *command {
- RenderCommand::SetBindGroup {
+ match command {
+ Cmd::SetBindGroup {
index,
num_dynamic_offsets,
- bind_group_id,
+ bind_group,
} => {
- let bind_groups = trackers.bind_groups.read();
- let bind_group = bind_groups.get(bind_group_id).unwrap();
let raw_bg = bind_group
.raw(&snatch_guard)
- .ok_or(ExecutionError::InvalidBindGroup(bind_group_id))?;
+ .ok_or(ExecutionError::InvalidBindGroup(bind_group.info.id()))?;
unsafe {
raw.set_bind_group(
pipeline_layout.as_ref().unwrap().raw(),
- index,
+ *index,
raw_bg,
- &offsets[..num_dynamic_offsets],
+ &offsets[..*num_dynamic_offsets],
)
};
- offsets = &offsets[num_dynamic_offsets..];
+ offsets = &offsets[*num_dynamic_offsets..];
}
- RenderCommand::SetPipeline(pipeline_id) => {
- let render_pipelines = trackers.render_pipelines.read();
- let pipeline = render_pipelines.get(pipeline_id).unwrap();
+ Cmd::SetPipeline(pipeline) => {
unsafe { raw.set_render_pipeline(pipeline.raw()) };
pipeline_layout = Some(pipeline.layout.clone());
}
- RenderCommand::SetIndexBuffer {
- buffer_id,
+ Cmd::SetIndexBuffer {
+ buffer,
index_format,
offset,
size,
} => {
- let buffers = trackers.buffers.read();
- let buffer: &A::Buffer = buffers
- .get(buffer_id)
- .ok_or(ExecutionError::DestroyedBuffer(buffer_id))?
+ let buffer: &A::Buffer = buffer
.raw(&snatch_guard)
- .ok_or(ExecutionError::DestroyedBuffer(buffer_id))?;
+ .ok_or(ExecutionError::DestroyedBuffer(buffer.info.id()))?;
let bb = hal::BufferBinding {
buffer,
- offset,
- size,
+ offset: *offset,
+ size: *size,
};
- unsafe { raw.set_index_buffer(bb, index_format) };
+ unsafe { raw.set_index_buffer(bb, *index_format) };
}
- RenderCommand::SetVertexBuffer {
+ Cmd::SetVertexBuffer {
slot,
- buffer_id,
+ buffer,
offset,
size,
} => {
- let buffers = trackers.buffers.read();
- let buffer = buffers
- .get(buffer_id)
- .ok_or(ExecutionError::DestroyedBuffer(buffer_id))?
+ let buffer = buffer
.raw(&snatch_guard)
- .ok_or(ExecutionError::DestroyedBuffer(buffer_id))?;
+ .ok_or(ExecutionError::DestroyedBuffer(buffer.info.id()))?;
let bb = hal::BufferBinding {
buffer,
- offset,
- size,
+ offset: *offset,
+ size: *size,
};
- unsafe { raw.set_vertex_buffer(slot, bb) };
+ unsafe { raw.set_vertex_buffer(*slot, bb) };
}
- RenderCommand::SetPushConstant {
+ Cmd::SetPushConstant {
stages,
offset,
size_bytes,
@@ -949,7 +971,7 @@ impl<A: HalApi> RenderBundle<A> {
} => {
let pipeline_layout = pipeline_layout.as_ref().unwrap();
- if let Some(values_offset) = values_offset {
+ if let Some(values_offset) = *values_offset {
let values_end_offset =
(values_offset + size_bytes / wgt::PUSH_CONSTANT_ALIGNMENT) as usize;
let data_slice = &self.base.push_constant_data
@@ -958,20 +980,20 @@ impl<A: HalApi> RenderBundle<A> {
unsafe {
raw.set_push_constants(
pipeline_layout.raw(),
- stages,
- offset,
+ *stages,
+ *offset,
data_slice,
)
}
} else {
super::push_constant_clear(
- offset,
- size_bytes,
+ *offset,
+ *size_bytes,
|clear_offset, clear_data| {
unsafe {
raw.set_push_constants(
pipeline_layout.raw(),
- stages,
+ *stages,
clear_offset,
clear_data,
)
@@ -980,15 +1002,22 @@ impl<A: HalApi> RenderBundle<A> {
);
}
}
- RenderCommand::Draw {
+ Cmd::Draw {
vertex_count,
instance_count,
first_vertex,
first_instance,
} => {
- unsafe { raw.draw(first_vertex, vertex_count, first_instance, instance_count) };
+ unsafe {
+ raw.draw(
+ *first_vertex,
+ *vertex_count,
+ *first_instance,
+ *instance_count,
+ )
+ };
}
- RenderCommand::DrawIndexed {
+ Cmd::DrawIndexed {
index_count,
instance_count,
first_index,
@@ -997,63 +1026,54 @@ impl<A: HalApi> RenderBundle<A> {
} => {
unsafe {
raw.draw_indexed(
- first_index,
- index_count,
- base_vertex,
- first_instance,
- instance_count,
+ *first_index,
+ *index_count,
+ *base_vertex,
+ *first_instance,
+ *instance_count,
)
};
}
- RenderCommand::MultiDrawIndirect {
- buffer_id,
+ Cmd::MultiDrawIndirect {
+ buffer,
offset,
count: None,
indexed: false,
} => {
- let buffers = trackers.buffers.read();
- let buffer = buffers
- .get(buffer_id)
- .ok_or(ExecutionError::DestroyedBuffer(buffer_id))?
+ let buffer = buffer
.raw(&snatch_guard)
- .ok_or(ExecutionError::DestroyedBuffer(buffer_id))?;
- unsafe { raw.draw_indirect(buffer, offset, 1) };
+ .ok_or(ExecutionError::DestroyedBuffer(buffer.info.id()))?;
+ unsafe { raw.draw_indirect(buffer, *offset, 1) };
}
- RenderCommand::MultiDrawIndirect {
- buffer_id,
+ Cmd::MultiDrawIndirect {
+ buffer,
offset,
count: None,
indexed: true,
} => {
- let buffers = trackers.buffers.read();
- let buffer = buffers
- .get(buffer_id)
- .ok_or(ExecutionError::DestroyedBuffer(buffer_id))?
+ let buffer = buffer
.raw(&snatch_guard)
- .ok_or(ExecutionError::DestroyedBuffer(buffer_id))?;
- unsafe { raw.draw_indexed_indirect(buffer, offset, 1) };
+ .ok_or(ExecutionError::DestroyedBuffer(buffer.info.id()))?;
+ unsafe { raw.draw_indexed_indirect(buffer, *offset, 1) };
}
- RenderCommand::MultiDrawIndirect { .. }
- | RenderCommand::MultiDrawIndirectCount { .. } => {
+ Cmd::MultiDrawIndirect { .. } | Cmd::MultiDrawIndirectCount { .. } => {
return Err(ExecutionError::Unimplemented("multi-draw-indirect"))
}
- RenderCommand::PushDebugGroup { .. }
- | RenderCommand::InsertDebugMarker { .. }
- | RenderCommand::PopDebugGroup => {
+ Cmd::PushDebugGroup { .. } | Cmd::InsertDebugMarker { .. } | Cmd::PopDebugGroup => {
return Err(ExecutionError::Unimplemented("debug-markers"))
}
- RenderCommand::WriteTimestamp { .. }
- | RenderCommand::BeginOcclusionQuery { .. }
- | RenderCommand::EndOcclusionQuery
- | RenderCommand::BeginPipelineStatisticsQuery { .. }
- | RenderCommand::EndPipelineStatisticsQuery => {
+ Cmd::WriteTimestamp { .. }
+ | Cmd::BeginOcclusionQuery { .. }
+ | Cmd::EndOcclusionQuery
+ | Cmd::BeginPipelineStatisticsQuery { .. }
+ | Cmd::EndPipelineStatisticsQuery => {
return Err(ExecutionError::Unimplemented("queries"))
}
- RenderCommand::ExecuteBundle(_)
- | RenderCommand::SetBlendConstant(_)
- | RenderCommand::SetStencilReference(_)
- | RenderCommand::SetViewport { .. }
- | RenderCommand::SetScissor(_) => unreachable!(),
+ Cmd::ExecuteBundle(_)
+ | Cmd::SetBlendConstant(_)
+ | Cmd::SetStencilReference(_)
+ | Cmd::SetViewport { .. }
+ | Cmd::SetScissor(_) => unreachable!(),
}
}
@@ -1087,14 +1107,14 @@ impl<A: HalApi> Resource for RenderBundle<A> {
/// and calls [`State::flush_index`] before any indexed draw command to produce
/// a `SetIndexBuffer` command if one is necessary.
#[derive(Debug)]
-struct IndexState {
- buffer: id::BufferId,
+struct IndexState<A: HalApi> {
+ buffer: Arc<Buffer<A>>,
format: wgt::IndexFormat,
range: Range<wgt::BufferAddress>,
is_dirty: bool,
}
-impl IndexState {
+impl<A: HalApi> IndexState<A> {
/// Return the number of entries in the current index buffer.
///
/// Panic if no index buffer has been set.
@@ -1109,11 +1129,11 @@ impl IndexState {
/// Generate a `SetIndexBuffer` command to prepare for an indexed draw
/// command, if needed.
- fn flush(&mut self) -> Option<RenderCommand> {
+ fn flush(&mut self) -> Option<ArcRenderCommand<A>> {
if self.is_dirty {
self.is_dirty = false;
- Some(RenderCommand::SetIndexBuffer {
- buffer_id: self.buffer,
+ Some(ArcRenderCommand::SetIndexBuffer {
+ buffer: self.buffer.clone(),
index_format: self.format,
offset: self.range.start,
size: wgt::BufferSize::new(self.range.end - self.range.start),
@@ -1134,14 +1154,14 @@ impl IndexState {
///
/// [`flush`]: IndexState::flush
#[derive(Debug)]
-struct VertexState {
- buffer: id::BufferId,
+struct VertexState<A: HalApi> {
+ buffer: Arc<Buffer<A>>,
range: Range<wgt::BufferAddress>,
is_dirty: bool,
}
-impl VertexState {
- fn new(buffer: id::BufferId, range: Range<wgt::BufferAddress>) -> Self {
+impl<A: HalApi> VertexState<A> {
+ fn new(buffer: Arc<Buffer<A>>, range: Range<wgt::BufferAddress>) -> Self {
Self {
buffer,
range,
@@ -1152,12 +1172,12 @@ impl VertexState {
/// Generate a `SetVertexBuffer` command for this slot, if necessary.
///
/// `slot` is the index of the vertex buffer slot that `self` tracks.
- fn flush(&mut self, slot: u32) -> Option<RenderCommand> {
+ fn flush(&mut self, slot: u32) -> Option<ArcRenderCommand<A>> {
if self.is_dirty {
self.is_dirty = false;
- Some(RenderCommand::SetVertexBuffer {
+ Some(ArcRenderCommand::SetVertexBuffer {
slot,
- buffer_id: self.buffer,
+ buffer: self.buffer.clone(),
offset: self.range.start,
size: wgt::BufferSize::new(self.range.end - self.range.start),
})
@@ -1219,7 +1239,7 @@ impl<A: HalApi> PipelineState<A> {
/// Return a sequence of commands to zero the push constant ranges this
/// pipeline uses. If no initialization is necessary, return `None`.
- fn zero_push_constants(&self) -> Option<impl Iterator<Item = RenderCommand>> {
+ fn zero_push_constants(&self) -> Option<impl Iterator<Item = ArcRenderCommand<A>>> {
if !self.push_constant_ranges.is_empty() {
let nonoverlapping_ranges =
super::bind::compute_nonoverlapping_ranges(&self.push_constant_ranges);
@@ -1227,7 +1247,7 @@ impl<A: HalApi> PipelineState<A> {
Some(
nonoverlapping_ranges
.into_iter()
- .map(|range| RenderCommand::SetPushConstant {
+ .map(|range| ArcRenderCommand::SetPushConstant {
stages: range.stages,
offset: range.range.start,
size_bytes: range.range.end - range.range.start,
@@ -1261,11 +1281,11 @@ struct State<A: HalApi> {
bind: ArrayVec<Option<BindState<A>>, { hal::MAX_BIND_GROUPS }>,
/// The state of each vertex buffer slot.
- vertex: ArrayVec<Option<VertexState>, { hal::MAX_VERTEX_BUFFERS }>,
+ vertex: ArrayVec<Option<VertexState<A>>, { hal::MAX_VERTEX_BUFFERS }>,
/// The current index buffer, if one has been set. We flush this state
/// before indexed draw commands.
- index: Option<IndexState>,
+ index: Option<IndexState<A>>,
/// Dynamic offset values used by the cleaned-up command sequence.
///
@@ -1375,13 +1395,13 @@ impl<A: HalApi> State<A> {
/// Set the bundle's current index buffer and its associated parameters.
fn set_index_buffer(
&mut self,
- buffer: id::BufferId,
+ buffer: Arc<Buffer<A>>,
format: wgt::IndexFormat,
range: Range<wgt::BufferAddress>,
) {
match self.index {
Some(ref current)
- if current.buffer == buffer
+ if Arc::ptr_eq(&current.buffer, &buffer)
&& current.format == format
&& current.range == range =>
{
@@ -1400,11 +1420,11 @@ impl<A: HalApi> State<A> {
/// Generate a `SetIndexBuffer` command to prepare for an indexed draw
/// command, if needed.
- fn flush_index(&mut self) -> Option<RenderCommand> {
+ fn flush_index(&mut self) -> Option<ArcRenderCommand<A>> {
self.index.as_mut().and_then(|index| index.flush())
}
- fn flush_vertices(&mut self) -> impl Iterator<Item = RenderCommand> + '_ {
+ fn flush_vertices(&mut self) -> impl Iterator<Item = ArcRenderCommand<A>> + '_ {
self.vertex
.iter_mut()
.enumerate()
@@ -1416,7 +1436,7 @@ impl<A: HalApi> State<A> {
&mut self,
used_bind_groups: usize,
dynamic_offsets: &[wgt::DynamicOffset],
- ) -> impl Iterator<Item = RenderCommand> + '_ {
+ ) -> impl Iterator<Item = ArcRenderCommand<A>> + '_ {
// Append each dirty bind group's dynamic offsets to `flat_dynamic_offsets`.
for contents in self.bind[..used_bind_groups].iter().flatten() {
if contents.is_dirty {
@@ -1435,9 +1455,9 @@ impl<A: HalApi> State<A> {
if contents.is_dirty {
contents.is_dirty = false;
let offsets = &contents.dynamic_offsets;
- return Some(RenderCommand::SetBindGroup {
+ return Some(ArcRenderCommand::SetBindGroup {
index: i.try_into().unwrap(),
- bind_group_id: contents.bind_group.as_info().id(),
+ bind_group: contents.bind_group.clone(),
num_dynamic_offsets: offsets.end - offsets.start,
});
}
diff --git a/third_party/rust/wgpu-core/src/command/clear.rs b/third_party/rust/wgpu-core/src/command/clear.rs
index 2569fea1a4..e404fabb14 100644
--- a/third_party/rust/wgpu-core/src/command/clear.rs
+++ b/third_party/rust/wgpu-core/src/command/clear.rs
@@ -39,6 +39,11 @@ pub enum ClearError {
UnalignedFillSize(BufferAddress),
#[error("Buffer offset {0:?} is not a multiple of `COPY_BUFFER_ALIGNMENT`")]
UnalignedBufferOffset(BufferAddress),
+ #[error("Clear starts at offset {start_offset} with size of {requested_size}, but these added together exceed `u64::MAX`")]
+ OffsetPlusSizeExceeds64BitBounds {
+ start_offset: BufferAddress,
+ requested_size: BufferAddress,
+ },
#[error("Clear of {start_offset}..{end_offset} would end up overrunning the bounds of the buffer of size {buffer_size}")]
BufferOverrun {
start_offset: BufferAddress,
@@ -117,25 +122,27 @@ impl Global {
if offset % wgt::COPY_BUFFER_ALIGNMENT != 0 {
return Err(ClearError::UnalignedBufferOffset(offset));
}
- if let Some(size) = size {
- if size % wgt::COPY_BUFFER_ALIGNMENT != 0 {
- return Err(ClearError::UnalignedFillSize(size));
- }
- let destination_end_offset = offset + size;
- if destination_end_offset > dst_buffer.size {
- return Err(ClearError::BufferOverrun {
+
+ let size = size.unwrap_or(dst_buffer.size.saturating_sub(offset));
+ if size % wgt::COPY_BUFFER_ALIGNMENT != 0 {
+ return Err(ClearError::UnalignedFillSize(size));
+ }
+ let end_offset =
+ offset
+ .checked_add(size)
+ .ok_or(ClearError::OffsetPlusSizeExceeds64BitBounds {
start_offset: offset,
- end_offset: destination_end_offset,
- buffer_size: dst_buffer.size,
- });
- }
+ requested_size: size,
+ })?;
+ if end_offset > dst_buffer.size {
+ return Err(ClearError::BufferOverrun {
+ start_offset: offset,
+ end_offset,
+ buffer_size: dst_buffer.size,
+ });
}
- let end = match size {
- Some(size) => offset + size,
- None => dst_buffer.size,
- };
- if offset == end {
+ if offset == end_offset {
log::trace!("Ignoring fill_buffer of size 0");
return Ok(());
}
@@ -144,7 +151,7 @@ impl Global {
cmd_buf_data.buffer_memory_init_actions.extend(
dst_buffer.initialization_status.read().create_action(
&dst_buffer,
- offset..end,
+ offset..end_offset,
MemoryInitKind::ImplicitlyInitialized,
),
);
@@ -154,7 +161,7 @@ impl Global {
let cmd_buf_raw = cmd_buf_data.encoder.open()?;
unsafe {
cmd_buf_raw.transition_buffers(dst_barrier.into_iter());
- cmd_buf_raw.clear_buffer(dst_raw, offset..end);
+ cmd_buf_raw.clear_buffer(dst_raw, offset..end_offset);
}
Ok(())
}
@@ -366,7 +373,7 @@ fn clear_texture_via_buffer_copies<A: HalApi>(
assert!(
max_rows_per_copy > 0,
"Zero buffer size is too small to fill a single row \
- of a texture with format {:?} and desc {:?}",
+ of a texture with format {:?} and desc {:?}",
texture_desc.format,
texture_desc.size
);
diff --git a/third_party/rust/wgpu-core/src/command/compute.rs b/third_party/rust/wgpu-core/src/command/compute.rs
index 804186a01e..c2fd3ab397 100644
--- a/third_party/rust/wgpu-core/src/command/compute.rs
+++ b/third_party/rust/wgpu-core/src/command/compute.rs
@@ -1,6 +1,7 @@
use crate::device::DeviceError;
use crate::resource::Resource;
use crate::snatch::SnatchGuard;
+use crate::track::TrackerIndex;
use crate::{
binding_model::{
BindError, BindGroup, LateMinBufferBindingSizeMismatch, PushConstantUploadError,
@@ -305,7 +306,7 @@ impl<A: HalApi> State<A> {
raw_encoder: &mut A::CommandEncoder,
base_trackers: &mut Tracker<A>,
bind_group_guard: &Storage<BindGroup<A>>,
- indirect_buffer: Option<id::BufferId>,
+ indirect_buffer: Option<TrackerIndex>,
snatch_guard: &SnatchGuard,
) -> Result<(), UsageConflict> {
for id in self.binder.list_active() {
@@ -402,12 +403,11 @@ impl Global {
let pipeline_guard = hub.compute_pipelines.read();
let query_set_guard = hub.query_sets.read();
let buffer_guard = hub.buffers.read();
- let texture_guard = hub.textures.read();
let mut state = State {
binder: Binder::new(),
pipeline: None,
- scope: UsageScope::new(&*buffer_guard, &*texture_guard),
+ scope: UsageScope::new(&device.tracker_indices),
debug_scope_depth: 0,
};
let mut temp_offsets = Vec::new();
@@ -452,17 +452,14 @@ impl Global {
let snatch_guard = device.snatchable_lock.read();
- tracker.set_size(
- Some(&*buffer_guard),
- Some(&*texture_guard),
- None,
- None,
- Some(&*bind_group_guard),
- Some(&*pipeline_guard),
- None,
- None,
- Some(&*query_set_guard),
- );
+ let indices = &device.tracker_indices;
+ tracker.buffers.set_size(indices.buffers.size());
+ tracker.textures.set_size(indices.textures.size());
+ tracker.bind_groups.set_size(indices.bind_groups.size());
+ tracker
+ .compute_pipelines
+ .set_size(indices.compute_pipelines.size());
+ tracker.query_sets.set_size(indices.query_sets.size());
let discard_hal_labels = self
.instance
@@ -719,8 +716,12 @@ impl Global {
.buffers
.merge_single(&*buffer_guard, buffer_id, hal::BufferUses::INDIRECT)
.map_pass_err(scope)?;
- check_buffer_usage(indirect_buffer.usage, wgt::BufferUsages::INDIRECT)
- .map_pass_err(scope)?;
+ check_buffer_usage(
+ buffer_id,
+ indirect_buffer.usage,
+ wgt::BufferUsages::INDIRECT,
+ )
+ .map_pass_err(scope)?;
let end_offset = offset + mem::size_of::<wgt::DispatchIndirectArgs>() as u64;
if end_offset > indirect_buffer.size {
@@ -753,7 +754,7 @@ impl Global {
raw,
&mut intermediate_trackers,
&*bind_group_guard,
- Some(buffer_id),
+ Some(indirect_buffer.as_info().tracker_index()),
&snatch_guard,
)
.map_pass_err(scope)?;
diff --git a/third_party/rust/wgpu-core/src/command/draw.rs b/third_party/rust/wgpu-core/src/command/draw.rs
index e03a78ee93..98aa689b78 100644
--- a/third_party/rust/wgpu-core/src/command/draw.rs
+++ b/third_party/rust/wgpu-core/src/command/draw.rs
@@ -2,17 +2,22 @@
!*/
use crate::{
- binding_model::{LateMinBufferBindingSizeMismatch, PushConstantUploadError},
+ binding_model::{BindGroup, LateMinBufferBindingSizeMismatch, PushConstantUploadError},
error::ErrorFormatter,
+ hal_api::HalApi,
id,
+ pipeline::RenderPipeline,
+ resource::{Buffer, QuerySet},
track::UsageConflict,
validation::{MissingBufferUsageError, MissingTextureUsageError},
};
use wgt::{BufferAddress, BufferSize, Color, VertexStepMode};
-use std::num::NonZeroU32;
+use std::{num::NonZeroU32, sync::Arc};
use thiserror::Error;
+use super::RenderBundle;
+
/// Error validating a draw call.
#[derive(Clone, Debug, Error, Eq, PartialEq)]
#[non_exhaustive]
@@ -245,3 +250,114 @@ pub enum RenderCommand {
EndPipelineStatisticsQuery,
ExecuteBundle(id::RenderBundleId),
}
+
+/// Equivalent to `RenderCommand` with the Ids resolved into resource Arcs.
+#[doc(hidden)]
+#[derive(Clone, Debug)]
+pub enum ArcRenderCommand<A: HalApi> {
+ SetBindGroup {
+ index: u32,
+ num_dynamic_offsets: usize,
+ bind_group: Arc<BindGroup<A>>,
+ },
+ SetPipeline(Arc<RenderPipeline<A>>),
+ SetIndexBuffer {
+ buffer: Arc<Buffer<A>>,
+ index_format: wgt::IndexFormat,
+ offset: BufferAddress,
+ size: Option<BufferSize>,
+ },
+ SetVertexBuffer {
+ slot: u32,
+ buffer: Arc<Buffer<A>>,
+ offset: BufferAddress,
+ size: Option<BufferSize>,
+ },
+ SetBlendConstant(Color),
+ SetStencilReference(u32),
+ SetViewport {
+ rect: Rect<f32>,
+ depth_min: f32,
+ depth_max: f32,
+ },
+ SetScissor(Rect<u32>),
+
+ /// Set a range of push constants to values stored in [`BasePass::push_constant_data`].
+ ///
+ /// See [`wgpu::RenderPass::set_push_constants`] for a detailed explanation
+ /// of the restrictions these commands must satisfy.
+ SetPushConstant {
+ /// Which stages we are setting push constant values for.
+ stages: wgt::ShaderStages,
+
+ /// The byte offset within the push constant storage to write to. This
+ /// must be a multiple of four.
+ offset: u32,
+
+ /// The number of bytes to write. This must be a multiple of four.
+ size_bytes: u32,
+
+ /// Index in [`BasePass::push_constant_data`] of the start of the data
+ /// to be written.
+ ///
+ /// Note: this is not a byte offset like `offset`. Rather, it is the
+ /// index of the first `u32` element in `push_constant_data` to read.
+ ///
+ /// `None` means zeros should be written to the destination range, and
+ /// there is no corresponding data in `push_constant_data`. This is used
+ /// by render bundles, which explicitly clear out any state that
+ /// post-bundle code might see.
+ values_offset: Option<u32>,
+ },
+ Draw {
+ vertex_count: u32,
+ instance_count: u32,
+ first_vertex: u32,
+ first_instance: u32,
+ },
+ DrawIndexed {
+ index_count: u32,
+ instance_count: u32,
+ first_index: u32,
+ base_vertex: i32,
+ first_instance: u32,
+ },
+ MultiDrawIndirect {
+ buffer: Arc<Buffer<A>>,
+ offset: BufferAddress,
+ /// Count of `None` represents a non-multi call.
+ count: Option<NonZeroU32>,
+ indexed: bool,
+ },
+ MultiDrawIndirectCount {
+ buffer: Arc<Buffer<A>>,
+ offset: BufferAddress,
+ count_buffer: Arc<Buffer<A>>,
+ count_buffer_offset: BufferAddress,
+ max_count: u32,
+ indexed: bool,
+ },
+ PushDebugGroup {
+ color: u32,
+ len: usize,
+ },
+ PopDebugGroup,
+ InsertDebugMarker {
+ color: u32,
+ len: usize,
+ },
+ WriteTimestamp {
+ query_set: Arc<QuerySet<A>>,
+ query_index: u32,
+ },
+ BeginOcclusionQuery {
+ query_index: u32,
+ },
+ EndOcclusionQuery,
+ BeginPipelineStatisticsQuery {
+ query_set: Arc<QuerySet<A>>,
+ query_index: u32,
+ },
+ EndPipelineStatisticsQuery,
+ ExecuteBundle(Arc<RenderBundle<A>>),
+}
diff --git a/third_party/rust/wgpu-core/src/command/mod.rs b/third_party/rust/wgpu-core/src/command/mod.rs
index 2d5fca200a..febed4fc97 100644
--- a/third_party/rust/wgpu-core/src/command/mod.rs
+++ b/third_party/rust/wgpu-core/src/command/mod.rs
@@ -75,7 +75,7 @@ impl<A: HalApi> CommandEncoder<A> {
Ok(())
}
- fn discard(&mut self) {
+ pub(crate) fn discard(&mut self) {
if self.is_open {
self.is_open = false;
unsafe { self.raw.discard_encoding() };
@@ -112,7 +112,7 @@ pub(crate) struct DestroyedBufferError(pub id::BufferId);
pub(crate) struct DestroyedTextureError(pub id::TextureId);
pub struct CommandBufferMutable<A: HalApi> {
- encoder: CommandEncoder<A>,
+ pub(crate) encoder: CommandEncoder<A>,
status: CommandEncoderStatus,
pub(crate) trackers: Tracker<A>,
buffer_memory_init_actions: Vec<BufferInitTrackerAction<A>>,
@@ -174,6 +174,7 @@ impl<A: HalApi> CommandBuffer<A> {
.as_ref()
.unwrap_or(&String::from("<CommandBuffer>"))
.as_str(),
+ None,
),
data: Mutex::new(Some(CommandBufferMutable {
encoder: CommandEncoder {
diff --git a/third_party/rust/wgpu-core/src/command/query.rs b/third_party/rust/wgpu-core/src/command/query.rs
index 39d7a9cc93..89cba6fbf3 100644
--- a/third_party/rust/wgpu-core/src/command/query.rs
+++ b/third_party/rust/wgpu-core/src/command/query.rs
@@ -4,7 +4,7 @@ use hal::CommandEncoder as _;
use crate::device::trace::Command as TraceCommand;
use crate::{
command::{CommandBuffer, CommandEncoderError},
- device::DeviceError,
+ device::{DeviceError, MissingFeatures},
global::Global,
hal_api::HalApi,
id::{self, Id},
@@ -108,6 +108,8 @@ pub enum QueryError {
Device(#[from] DeviceError),
#[error(transparent)]
Encoder(#[from] CommandEncoderError),
+ #[error(transparent)]
+ MissingFeature(#[from] MissingFeatures),
#[error("Error encountered while trying to use queries")]
Use(#[from] QueryUseError),
#[error("Error encountered while trying to resolve a query")]
@@ -355,6 +357,11 @@ impl Global {
let hub = A::hub(self);
let cmd_buf = CommandBuffer::get_encoder(hub, command_encoder_id)?;
+
+ cmd_buf
+ .device
+ .require_features(wgt::Features::TIMESTAMP_QUERY_INSIDE_ENCODERS)?;
+
let mut cmd_buf_data = cmd_buf.data.lock();
let cmd_buf_data = cmd_buf_data.as_mut().unwrap();
diff --git a/third_party/rust/wgpu-core/src/command/render.rs b/third_party/rust/wgpu-core/src/command/render.rs
index d3de3e26e1..9141ddb021 100644
--- a/third_party/rust/wgpu-core/src/command/render.rs
+++ b/third_party/rust/wgpu-core/src/command/render.rs
@@ -22,7 +22,7 @@ use crate::{
hal_label, id,
init_tracker::{MemoryInitKind, TextureInitRange, TextureInitTrackerAction},
pipeline::{self, PipelineFlags},
- resource::{Buffer, QuerySet, Texture, TextureView, TextureViewNotRenderableReason},
+ resource::{QuerySet, Texture, TextureView, TextureViewNotRenderableReason},
storage::Storage,
track::{TextureSelector, Tracker, UsageConflict, UsageScope},
validation::{
@@ -531,6 +531,8 @@ pub enum ColorAttachmentError {
InvalidFormat(wgt::TextureFormat),
#[error("The number of color attachments {given} exceeds the limit {limit}")]
TooMany { given: usize, limit: usize },
+ #[error("The total number of bytes per sample in color attachments {total} exceeds the limit {limit}")]
+ TooManyBytesPerSample { total: u32, limit: u32 },
}
/// Error encountered when performing a render pass.
@@ -799,8 +801,6 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> {
texture_memory_actions: &mut CommandBufferTextureMemoryActions<A>,
pending_query_resets: &mut QueryResetMap<A>,
view_guard: &'a Storage<TextureView<A>>,
- buffer_guard: &'a Storage<Buffer<A>>,
- texture_guard: &'a Storage<Texture<A>>,
query_set_guard: &'a Storage<QuerySet<A>>,
snatch_guard: &SnatchGuard<'a>,
) -> Result<Self, RenderPassErrorInner> {
@@ -1214,7 +1214,7 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> {
Ok(Self {
context,
- usage_scope: UsageScope::new(buffer_guard, texture_guard),
+ usage_scope: UsageScope::new(&device.tracker_indices),
render_attachments,
is_depth_read_only,
is_stencil_read_only,
@@ -1386,7 +1386,6 @@ impl Global {
let render_pipeline_guard = hub.render_pipelines.read();
let query_set_guard = hub.query_sets.read();
let buffer_guard = hub.buffers.read();
- let texture_guard = hub.textures.read();
let view_guard = hub.texture_views.read();
log::trace!(
@@ -1406,24 +1405,21 @@ impl Global {
texture_memory_actions,
pending_query_resets,
&*view_guard,
- &*buffer_guard,
- &*texture_guard,
&*query_set_guard,
&snatch_guard,
)
.map_pass_err(pass_scope)?;
- tracker.set_size(
- Some(&*buffer_guard),
- Some(&*texture_guard),
- Some(&*view_guard),
- None,
- Some(&*bind_group_guard),
- None,
- Some(&*render_pipeline_guard),
- Some(&*bundle_guard),
- Some(&*query_set_guard),
- );
+ let indices = &device.tracker_indices;
+ tracker.buffers.set_size(indices.buffers.size());
+ tracker.textures.set_size(indices.textures.size());
+ tracker.views.set_size(indices.texture_views.size());
+ tracker.bind_groups.set_size(indices.bind_groups.size());
+ tracker
+ .render_pipelines
+ .set_size(indices.render_pipelines.size());
+ tracker.bundles.set_size(indices.bundles.size());
+ tracker.query_sets.set_size(indices.query_sets.size());
let raw = &mut encoder.raw;
@@ -1675,7 +1671,7 @@ impl Global {
return Err(DeviceError::WrongDevice).map_pass_err(scope);
}
- check_buffer_usage(buffer.usage, BufferUsages::INDEX)
+ check_buffer_usage(buffer_id, buffer.usage, BufferUsages::INDEX)
.map_pass_err(scope)?;
let buf_raw = buffer
.raw
@@ -1737,7 +1733,7 @@ impl Global {
.map_pass_err(scope);
}
- check_buffer_usage(buffer.usage, BufferUsages::VERTEX)
+ check_buffer_usage(buffer_id, buffer.usage, BufferUsages::VERTEX)
.map_pass_err(scope)?;
let buf_raw = buffer
.raw
@@ -2034,8 +2030,12 @@ impl Global {
.buffers
.merge_single(&*buffer_guard, buffer_id, hal::BufferUses::INDIRECT)
.map_pass_err(scope)?;
- check_buffer_usage(indirect_buffer.usage, BufferUsages::INDIRECT)
- .map_pass_err(scope)?;
+ check_buffer_usage(
+ buffer_id,
+ indirect_buffer.usage,
+ BufferUsages::INDIRECT,
+ )
+ .map_pass_err(scope)?;
let indirect_raw = indirect_buffer
.raw
.get(&snatch_guard)
@@ -2106,8 +2106,12 @@ impl Global {
.buffers
.merge_single(&*buffer_guard, buffer_id, hal::BufferUses::INDIRECT)
.map_pass_err(scope)?;
- check_buffer_usage(indirect_buffer.usage, BufferUsages::INDIRECT)
- .map_pass_err(scope)?;
+ check_buffer_usage(
+ buffer_id,
+ indirect_buffer.usage,
+ BufferUsages::INDIRECT,
+ )
+ .map_pass_err(scope)?;
let indirect_raw = indirect_buffer
.raw
.get(&snatch_guard)
@@ -2123,7 +2127,7 @@ impl Global {
hal::BufferUses::INDIRECT,
)
.map_pass_err(scope)?;
- check_buffer_usage(count_buffer.usage, BufferUsages::INDIRECT)
+ check_buffer_usage(buffer_id, count_buffer.usage, BufferUsages::INDIRECT)
.map_pass_err(scope)?;
let count_raw = count_buffer
.raw
diff --git a/third_party/rust/wgpu-core/src/device/global.rs b/third_party/rust/wgpu-core/src/device/global.rs
index 64fd6d4de7..539b92e0f3 100644
--- a/third_party/rust/wgpu-core/src/device/global.rs
+++ b/third_party/rust/wgpu-core/src/device/global.rs
@@ -26,9 +26,7 @@ use wgt::{BufferAddress, TextureFormat};
use std::{
borrow::Cow,
- iter,
- ops::Range,
- ptr,
+ iter, ptr,
sync::{atomic::Ordering, Arc},
};
@@ -219,7 +217,7 @@ impl Global {
mapped_at_creation: false,
};
let stage = match device.create_buffer(&stage_desc, true) {
- Ok(stage) => stage,
+ Ok(stage) => Arc::new(stage),
Err(e) => {
to_destroy.push(buffer);
break e;
@@ -232,14 +230,10 @@ impl Global {
Ok(mapping) => mapping,
Err(e) => {
to_destroy.push(buffer);
- to_destroy.push(stage);
break CreateBufferError::Device(e.into());
}
};
- let stage_fid = hub.buffers.request();
- let stage = stage_fid.init(stage);
-
assert_eq!(buffer.size % wgt::COPY_BUFFER_ALIGNMENT, 0);
// Zero initialize memory and then mark both staging and buffer as initialized
// (it's guaranteed that this is the case by the time the buffer is usable)
@@ -262,7 +256,7 @@ impl Global {
.trackers
.lock()
.buffers
- .insert_single(id, resource, buffer_use);
+ .insert_single(resource, buffer_use);
return (id, None);
};
@@ -383,7 +377,7 @@ impl Global {
.buffers
.get(buffer_id)
.map_err(|_| BufferAccessError::Invalid)?;
- check_buffer_usage(buffer.usage, wgt::BufferUsages::MAP_WRITE)?;
+ check_buffer_usage(buffer_id, buffer.usage, wgt::BufferUsages::MAP_WRITE)?;
//assert!(buffer isn't used by the GPU);
#[cfg(feature = "trace")]
@@ -446,7 +440,7 @@ impl Global {
.buffers
.get(buffer_id)
.map_err(|_| BufferAccessError::Invalid)?;
- check_buffer_usage(buffer.usage, wgt::BufferUsages::MAP_READ)?;
+ check_buffer_usage(buffer_id, buffer.usage, wgt::BufferUsages::MAP_READ)?;
//assert!(buffer isn't used by the GPU);
let raw_buf = buffer
@@ -529,7 +523,7 @@ impl Global {
.lock_life()
.suspected_resources
.buffers
- .insert(buffer_id, buffer);
+ .insert(buffer.info.tracker_index(), buffer);
}
if wait {
@@ -573,11 +567,11 @@ impl Global {
let (id, resource) = fid.assign(texture);
api_log!("Device::create_texture({desc:?}) -> {id:?}");
- device.trackers.lock().textures.insert_single(
- id,
- resource,
- hal::TextureUses::UNINITIALIZED,
- );
+ device
+ .trackers
+ .lock()
+ .textures
+ .insert_single(resource, hal::TextureUses::UNINITIALIZED);
return (id, None);
};
@@ -647,11 +641,11 @@ impl Global {
let (id, resource) = fid.assign(texture);
api_log!("Device::create_texture({desc:?}) -> {id:?}");
- device.trackers.lock().textures.insert_single(
- id,
- resource,
- hal::TextureUses::UNINITIALIZED,
- );
+ device
+ .trackers
+ .lock()
+ .textures
+ .insert_single(resource, hal::TextureUses::UNINITIALIZED);
return (id, None);
};
@@ -704,7 +698,7 @@ impl Global {
.trackers
.lock()
.buffers
- .insert_single(id, buffer, hal::BufferUses::empty());
+ .insert_single(buffer, hal::BufferUses::empty());
return (id, None);
};
@@ -764,7 +758,7 @@ impl Global {
.lock_life()
.suspected_resources
.textures
- .insert(texture_id, texture.clone());
+ .insert(texture.info.tracker_index(), texture.clone());
}
}
@@ -824,7 +818,7 @@ impl Global {
}
api_log!("Texture::create_view({texture_id:?}) -> {id:?}");
- device.trackers.lock().views.insert_single(id, resource);
+ device.trackers.lock().views.insert_single(resource);
return (id, None);
};
@@ -854,7 +848,7 @@ impl Global {
.lock_life()
.suspected_resources
.texture_views
- .insert(texture_view_id, view.clone());
+ .insert(view.info.tracker_index(), view.clone());
if wait {
match view.device.wait_for_submit(last_submit_index) {
@@ -900,7 +894,7 @@ impl Global {
let (id, resource) = fid.assign(sampler);
api_log!("Device::create_sampler -> {id:?}");
- device.trackers.lock().samplers.insert_single(id, resource);
+ device.trackers.lock().samplers.insert_single(resource);
return (id, None);
};
@@ -925,7 +919,7 @@ impl Global {
.lock_life()
.suspected_resources
.samplers
- .insert(sampler_id, sampler.clone());
+ .insert(sampler.info.tracker_index(), sampler.clone());
}
}
@@ -1024,7 +1018,7 @@ impl Global {
.lock_life()
.suspected_resources
.bind_group_layouts
- .insert(bind_group_layout_id, layout.clone());
+ .insert(layout.info.tracker_index(), layout.clone());
}
}
@@ -1085,7 +1079,7 @@ impl Global {
.lock_life()
.suspected_resources
.pipeline_layouts
- .insert(pipeline_layout_id, layout.clone());
+ .insert(layout.info.tracker_index(), layout.clone());
}
}
@@ -1140,11 +1134,7 @@ impl Global {
api_log!("Device::create_bind_group -> {id:?}");
- device
- .trackers
- .lock()
- .bind_groups
- .insert_single(id, resource);
+ device.trackers.lock().bind_groups.insert_single(resource);
return (id, None);
};
@@ -1168,7 +1158,7 @@ impl Global {
.lock_life()
.suspected_resources
.bind_groups
- .insert(bind_group_id, bind_group.clone());
+ .insert(bind_group.info.tracker_index(), bind_group.clone());
}
}
@@ -1332,9 +1322,8 @@ impl Global {
if !device.is_valid() {
break DeviceError::Lost;
}
- let queue = match hub.queues.get(device.queue_id.read().unwrap()) {
- Ok(queue) => queue,
- Err(_) => break DeviceError::InvalidQueueId,
+ let Some(queue) = device.get_queue() else {
+ break DeviceError::InvalidQueueId;
};
let encoder = match device
.command_allocator
@@ -1379,6 +1368,7 @@ impl Global {
.command_buffers
.unregister(command_encoder_id.transmute())
{
+ cmd_buf.data.lock().as_mut().unwrap().encoder.discard();
cmd_buf
.device
.untrack(&cmd_buf.data.lock().as_ref().unwrap().trackers);
@@ -1450,7 +1440,7 @@ impl Global {
let (id, resource) = fid.assign(render_bundle);
api_log!("RenderBundleEncoder::finish -> {id:?}");
- device.trackers.lock().bundles.insert_single(id, resource);
+ device.trackers.lock().bundles.insert_single(resource);
return (id, None);
};
@@ -1474,7 +1464,7 @@ impl Global {
.lock_life()
.suspected_resources
.render_bundles
- .insert(render_bundle_id, bundle.clone());
+ .insert(bundle.info.tracker_index(), bundle.clone());
}
}
@@ -1513,11 +1503,7 @@ impl Global {
let (id, resource) = fid.assign(query_set);
api_log!("Device::create_query_set -> {id:?}");
- device
- .trackers
- .lock()
- .query_sets
- .insert_single(id, resource);
+ device.trackers.lock().query_sets.insert_single(resource);
return (id, None);
};
@@ -1544,7 +1530,7 @@ impl Global {
.lock_life()
.suspected_resources
.query_sets
- .insert(query_set_id, query_set.clone());
+ .insert(query_set.info.tracker_index(), query_set.clone());
}
}
@@ -1600,7 +1586,7 @@ impl Global {
.trackers
.lock()
.render_pipelines
- .insert_single(id, resource);
+ .insert_single(resource);
return (id, None);
};
@@ -1672,18 +1658,17 @@ impl Global {
let hub = A::hub(self);
if let Some(pipeline) = hub.render_pipelines.unregister(render_pipeline_id) {
- let layout_id = pipeline.layout.as_info().id();
let device = &pipeline.device;
let mut life_lock = device.lock_life();
life_lock
.suspected_resources
.render_pipelines
- .insert(render_pipeline_id, pipeline.clone());
+ .insert(pipeline.info.tracker_index(), pipeline.clone());
- life_lock
- .suspected_resources
- .pipeline_layouts
- .insert(layout_id, pipeline.layout.clone());
+ life_lock.suspected_resources.pipeline_layouts.insert(
+ pipeline.layout.info.tracker_index(),
+ pipeline.layout.clone(),
+ );
}
}
@@ -1734,7 +1719,7 @@ impl Global {
.trackers
.lock()
.compute_pipelines
- .insert_single(id, resource);
+ .insert_single(resource);
return (id, None);
};
@@ -1804,17 +1789,16 @@ impl Global {
let hub = A::hub(self);
if let Some(pipeline) = hub.compute_pipelines.unregister(compute_pipeline_id) {
- let layout_id = pipeline.layout.as_info().id();
let device = &pipeline.device;
let mut life_lock = device.lock_life();
life_lock
.suspected_resources
.compute_pipelines
- .insert(compute_pipeline_id, pipeline.clone());
- life_lock
- .suspected_resources
- .pipeline_layouts
- .insert(layout_id, pipeline.layout.clone());
+ .insert(pipeline.info.tracker_index(), pipeline.clone());
+ life_lock.suspected_resources.pipeline_layouts.insert(
+ pipeline.layout.info.tracker_index(),
+ pipeline.layout.clone(),
+ );
}
}
@@ -2113,28 +2097,41 @@ impl Global {
.get(device_id)
.map_err(|_| DeviceError::Invalid)?;
- let (closures, queue_empty) = {
- if let wgt::Maintain::WaitForSubmissionIndex(submission_index) = maintain {
- if submission_index.queue_id != device_id.transmute() {
- return Err(WaitIdleError::WrongSubmissionIndex(
- submission_index.queue_id,
- device_id,
- ));
- }
+ if let wgt::Maintain::WaitForSubmissionIndex(submission_index) = maintain {
+ if submission_index.queue_id != device_id.transmute() {
+ return Err(WaitIdleError::WrongSubmissionIndex(
+ submission_index.queue_id,
+ device_id,
+ ));
}
+ }
- let fence = device.fence.read();
- let fence = fence.as_ref().unwrap();
- device.maintain(fence, maintain)?
- };
+ let DevicePoll {
+ closures,
+ queue_empty,
+ } = Self::poll_single_device(&device, maintain)?;
+
+ closures.fire();
+
+ Ok(queue_empty)
+ }
+
+ fn poll_single_device<A: HalApi>(
+ device: &crate::device::Device<A>,
+ maintain: wgt::Maintain<queue::WrappedSubmissionIndex>,
+ ) -> Result<DevicePoll, WaitIdleError> {
+ let fence = device.fence.read();
+ let fence = fence.as_ref().unwrap();
+ let (closures, queue_empty) = device.maintain(fence, maintain)?;
// Some deferred destroys are scheduled in maintain so run this right after
// to avoid holding on to them until the next device poll.
device.deferred_resource_destruction();
- closures.fire();
-
- Ok(queue_empty)
+ Ok(DevicePoll {
+ closures,
+ queue_empty,
+ })
}
/// Poll all devices belonging to the backend `A`.
@@ -2143,7 +2140,7 @@ impl Global {
///
/// Return `all_queue_empty` indicating whether there are more queue
/// submissions still in flight.
- fn poll_device<A: HalApi>(
+ fn poll_all_devices_of_api<A: HalApi>(
&self,
force_wait: bool,
closures: &mut UserClosures,
@@ -2161,10 +2158,13 @@ impl Global {
} else {
wgt::Maintain::Poll
};
- let fence = device.fence.read();
- let fence = fence.as_ref().unwrap();
- let (cbs, queue_empty) = device.maintain(fence, maintain)?;
- all_queue_empty = all_queue_empty && queue_empty;
+
+ let DevicePoll {
+ closures: cbs,
+ queue_empty,
+ } = Self::poll_single_device(device, maintain)?;
+
+ all_queue_empty &= queue_empty;
closures.extend(cbs);
}
@@ -2186,23 +2186,23 @@ impl Global {
#[cfg(vulkan)]
{
- all_queue_empty =
- self.poll_device::<hal::api::Vulkan>(force_wait, &mut closures)? && all_queue_empty;
+ all_queue_empty &=
+ self.poll_all_devices_of_api::<hal::api::Vulkan>(force_wait, &mut closures)?;
}
#[cfg(metal)]
{
- all_queue_empty =
- self.poll_device::<hal::api::Metal>(force_wait, &mut closures)? && all_queue_empty;
+ all_queue_empty &=
+ self.poll_all_devices_of_api::<hal::api::Metal>(force_wait, &mut closures)?;
}
#[cfg(dx12)]
{
- all_queue_empty =
- self.poll_device::<hal::api::Dx12>(force_wait, &mut closures)? && all_queue_empty;
+ all_queue_empty &=
+ self.poll_all_devices_of_api::<hal::api::Dx12>(force_wait, &mut closures)?;
}
#[cfg(gles)]
{
- all_queue_empty =
- self.poll_device::<hal::api::Gles>(force_wait, &mut closures)? && all_queue_empty;
+ all_queue_empty &=
+ self.poll_all_devices_of_api::<hal::api::Gles>(force_wait, &mut closures)?;
}
closures.fire();
@@ -2336,15 +2336,18 @@ impl Global {
pub fn buffer_map_async<A: HalApi>(
&self,
buffer_id: id::BufferId,
- range: Range<BufferAddress>,
+ offset: BufferAddress,
+ size: Option<BufferAddress>,
op: BufferMapOperation,
) -> BufferAccessResult {
- api_log!("Buffer::map_async {buffer_id:?} range {range:?} op: {op:?}");
+ api_log!("Buffer::map_async {buffer_id:?} offset {offset:?} size {size:?} op: {op:?}");
// User callbacks must not be called while holding buffer_map_async_inner's locks, so we
// defer the error callback if it needs to be called immediately (typically when running
// into errors).
- if let Err((mut operation, err)) = self.buffer_map_async_inner::<A>(buffer_id, range, op) {
+ if let Err((mut operation, err)) =
+ self.buffer_map_async_inner::<A>(buffer_id, offset, size, op)
+ {
if let Some(callback) = operation.callback.take() {
callback.call(Err(err.clone()));
}
@@ -2360,7 +2363,8 @@ impl Global {
fn buffer_map_async_inner<A: HalApi>(
&self,
buffer_id: id::BufferId,
- range: Range<BufferAddress>,
+ offset: BufferAddress,
+ size: Option<BufferAddress>,
op: BufferMapOperation,
) -> Result<(), (BufferMapOperation, BufferAccessError)> {
profiling::scope!("Buffer::map_async");
@@ -2372,29 +2376,50 @@ impl Global {
HostMap::Write => (wgt::BufferUsages::MAP_WRITE, hal::BufferUses::MAP_WRITE),
};
- if range.start % wgt::MAP_ALIGNMENT != 0 || range.end % wgt::COPY_BUFFER_ALIGNMENT != 0 {
- return Err((op, BufferAccessError::UnalignedRange));
- }
-
let buffer = {
- let buffer = hub
- .buffers
- .get(buffer_id)
- .map_err(|_| BufferAccessError::Invalid);
+ let buffer = hub.buffers.get(buffer_id);
let buffer = match buffer {
Ok(b) => b,
- Err(e) => {
- return Err((op, e));
+ Err(_) => {
+ return Err((op, BufferAccessError::Invalid));
}
};
+ {
+ let snatch_guard = buffer.device.snatchable_lock.read();
+ if buffer.is_destroyed(&snatch_guard) {
+ return Err((op, BufferAccessError::Destroyed));
+ }
+ }
+
+ let range_size = if let Some(size) = size {
+ size
+ } else if offset > buffer.size {
+ 0
+ } else {
+ buffer.size - offset
+ };
+
+ if offset % wgt::MAP_ALIGNMENT != 0 {
+ return Err((op, BufferAccessError::UnalignedOffset { offset }));
+ }
+ if range_size % wgt::COPY_BUFFER_ALIGNMENT != 0 {
+ return Err((op, BufferAccessError::UnalignedRangeSize { range_size }));
+ }
+
+ let range = offset..(offset + range_size);
+
+ if range.start % wgt::MAP_ALIGNMENT != 0 || range.end % wgt::COPY_BUFFER_ALIGNMENT != 0
+ {
+ return Err((op, BufferAccessError::UnalignedRange));
+ }
let device = &buffer.device;
if !device.is_valid() {
return Err((op, DeviceError::Lost.into()));
}
- if let Err(e) = check_buffer_usage(buffer.usage, pub_usage) {
+ if let Err(e) = check_buffer_usage(buffer.info.id(), buffer.usage, pub_usage) {
return Err((op, e.into()));
}
@@ -2417,11 +2442,6 @@ impl Global {
));
}
- let snatch_guard = device.snatchable_lock.read();
- if buffer.is_destroyed(&snatch_guard) {
- return Err((op, BufferAccessError::Destroyed));
- }
-
{
let map_state = &mut *buffer.map_state.lock();
*map_state = match *map_state {
@@ -2442,6 +2462,8 @@ impl Global {
};
}
+ let snatch_guard = buffer.device.snatchable_lock.read();
+
{
let mut trackers = buffer.device.as_ref().trackers.lock();
trackers.buffers.set_single(&buffer, internal_use);
@@ -2557,3 +2579,8 @@ impl Global {
buffer.unmap()
}
}
+
+struct DevicePoll {
+ closures: UserClosures,
+ queue_empty: bool,
+}
diff --git a/third_party/rust/wgpu-core/src/device/life.rs b/third_party/rust/wgpu-core/src/device/life.rs
index 86c5d027c7..7b06a4a30b 100644
--- a/third_party/rust/wgpu-core/src/device/life.rs
+++ b/third_party/rust/wgpu-core/src/device/life.rs
@@ -6,17 +6,13 @@ use crate::{
DeviceError, DeviceLostClosure,
},
hal_api::HalApi,
- id::{
- self, BindGroupId, BindGroupLayoutId, BufferId, ComputePipelineId, Id, PipelineLayoutId,
- QuerySetId, RenderBundleId, RenderPipelineId, SamplerId, StagingBufferId, TextureId,
- TextureViewId,
- },
+ id,
pipeline::{ComputePipeline, RenderPipeline},
resource::{
self, Buffer, DestroyedBuffer, DestroyedTexture, QuerySet, Resource, Sampler,
StagingBuffer, Texture, TextureView,
},
- track::{ResourceTracker, Tracker},
+ track::{ResourceTracker, Tracker, TrackerIndex},
FastHashMap, SubmissionIndex,
};
use smallvec::SmallVec;
@@ -28,20 +24,20 @@ use thiserror::Error;
/// A struct that keeps lists of resources that are no longer needed by the user.
#[derive(Default)]
pub(crate) struct ResourceMaps<A: HalApi> {
- pub buffers: FastHashMap<BufferId, Arc<Buffer<A>>>,
- pub staging_buffers: FastHashMap<StagingBufferId, Arc<StagingBuffer<A>>>,
- pub textures: FastHashMap<TextureId, Arc<Texture<A>>>,
- pub texture_views: FastHashMap<TextureViewId, Arc<TextureView<A>>>,
- pub samplers: FastHashMap<SamplerId, Arc<Sampler<A>>>,
- pub bind_groups: FastHashMap<BindGroupId, Arc<BindGroup<A>>>,
- pub bind_group_layouts: FastHashMap<BindGroupLayoutId, Arc<BindGroupLayout<A>>>,
- pub render_pipelines: FastHashMap<RenderPipelineId, Arc<RenderPipeline<A>>>,
- pub compute_pipelines: FastHashMap<ComputePipelineId, Arc<ComputePipeline<A>>>,
- pub pipeline_layouts: FastHashMap<PipelineLayoutId, Arc<PipelineLayout<A>>>,
- pub render_bundles: FastHashMap<RenderBundleId, Arc<RenderBundle<A>>>,
- pub query_sets: FastHashMap<QuerySetId, Arc<QuerySet<A>>>,
- pub destroyed_buffers: FastHashMap<BufferId, Arc<DestroyedBuffer<A>>>,
- pub destroyed_textures: FastHashMap<TextureId, Arc<DestroyedTexture<A>>>,
+ pub buffers: FastHashMap<TrackerIndex, Arc<Buffer<A>>>,
+ pub staging_buffers: FastHashMap<TrackerIndex, Arc<StagingBuffer<A>>>,
+ pub textures: FastHashMap<TrackerIndex, Arc<Texture<A>>>,
+ pub texture_views: FastHashMap<TrackerIndex, Arc<TextureView<A>>>,
+ pub samplers: FastHashMap<TrackerIndex, Arc<Sampler<A>>>,
+ pub bind_groups: FastHashMap<TrackerIndex, Arc<BindGroup<A>>>,
+ pub bind_group_layouts: FastHashMap<TrackerIndex, Arc<BindGroupLayout<A>>>,
+ pub render_pipelines: FastHashMap<TrackerIndex, Arc<RenderPipeline<A>>>,
+ pub compute_pipelines: FastHashMap<TrackerIndex, Arc<ComputePipeline<A>>>,
+ pub pipeline_layouts: FastHashMap<TrackerIndex, Arc<PipelineLayout<A>>>,
+ pub render_bundles: FastHashMap<TrackerIndex, Arc<RenderBundle<A>>>,
+ pub query_sets: FastHashMap<TrackerIndex, Arc<QuerySet<A>>>,
+ pub destroyed_buffers: FastHashMap<TrackerIndex, Arc<DestroyedBuffer<A>>>,
+ pub destroyed_textures: FastHashMap<TrackerIndex, Arc<DestroyedTexture<A>>>,
}
impl<A: HalApi> ResourceMaps<A> {
@@ -276,25 +272,29 @@ impl<A: HalApi> LifetimeTracker<A> {
for res in temp_resources {
match res {
TempResource::Buffer(raw) => {
- last_resources.buffers.insert(raw.as_info().id(), raw);
+ last_resources
+ .buffers
+ .insert(raw.as_info().tracker_index(), raw);
}
TempResource::StagingBuffer(raw) => {
last_resources
.staging_buffers
- .insert(raw.as_info().id(), raw);
+ .insert(raw.as_info().tracker_index(), raw);
}
TempResource::DestroyedBuffer(destroyed) => {
last_resources
.destroyed_buffers
- .insert(destroyed.id, destroyed);
+ .insert(destroyed.tracker_index, destroyed);
}
TempResource::Texture(raw) => {
- last_resources.textures.insert(raw.as_info().id(), raw);
+ last_resources
+ .textures
+ .insert(raw.as_info().tracker_index(), raw);
}
TempResource::DestroyedTexture(destroyed) => {
last_resources
.destroyed_textures
- .insert(destroyed.id, destroyed);
+ .insert(destroyed.tracker_index, destroyed);
}
}
}
@@ -310,12 +310,14 @@ impl<A: HalApi> LifetimeTracker<A> {
pub fn post_submit(&mut self) {
for v in self.future_suspected_buffers.drain(..).take(1) {
- self.suspected_resources.buffers.insert(v.as_info().id(), v);
+ self.suspected_resources
+ .buffers
+ .insert(v.as_info().tracker_index(), v);
}
for v in self.future_suspected_textures.drain(..).take(1) {
self.suspected_resources
.textures
- .insert(v.as_info().id(), v);
+ .insert(v.as_info().tracker_index(), v);
}
}
@@ -386,19 +388,27 @@ impl<A: HalApi> LifetimeTracker<A> {
if let Some(resources) = resources {
match temp_resource {
TempResource::Buffer(raw) => {
- resources.buffers.insert(raw.as_info().id(), raw);
+ resources.buffers.insert(raw.as_info().tracker_index(), raw);
}
TempResource::StagingBuffer(raw) => {
- resources.staging_buffers.insert(raw.as_info().id(), raw);
+ resources
+ .staging_buffers
+ .insert(raw.as_info().tracker_index(), raw);
}
TempResource::DestroyedBuffer(destroyed) => {
- resources.destroyed_buffers.insert(destroyed.id, destroyed);
+ resources
+ .destroyed_buffers
+ .insert(destroyed.tracker_index, destroyed);
}
TempResource::Texture(raw) => {
- resources.textures.insert(raw.as_info().id(), raw);
+ resources
+ .textures
+ .insert(raw.as_info().tracker_index(), raw);
}
TempResource::DestroyedTexture(destroyed) => {
- resources.destroyed_textures.insert(destroyed.id, destroyed);
+ resources
+ .destroyed_textures
+ .insert(destroyed.tracker_index, destroyed);
}
}
}
@@ -420,27 +430,27 @@ impl<A: HalApi> LifetimeTracker<A> {
impl<A: HalApi> LifetimeTracker<A> {
fn triage_resources<R>(
- resources_map: &mut FastHashMap<Id<R::Marker>, Arc<R>>,
+ resources_map: &mut FastHashMap<TrackerIndex, Arc<R>>,
active: &mut [ActiveSubmission<A>],
- trackers: &mut impl ResourceTracker<R>,
- get_resource_map: impl Fn(&mut ResourceMaps<A>) -> &mut FastHashMap<Id<R::Marker>, Arc<R>>,
+ trackers: &mut impl ResourceTracker,
+ get_resource_map: impl Fn(&mut ResourceMaps<A>) -> &mut FastHashMap<TrackerIndex, Arc<R>>,
) -> Vec<Arc<R>>
where
R: Resource,
{
let mut removed_resources = Vec::new();
- resources_map.retain(|&id, resource| {
+ resources_map.retain(|&index, resource| {
let submit_index = resource.as_info().submission_index();
let non_referenced_resources = active
.iter_mut()
.find(|a| a.index == submit_index)
.map(|a| &mut a.last_resources);
- let is_removed = trackers.remove_abandoned(id);
+ let is_removed = trackers.remove_abandoned(index);
if is_removed {
removed_resources.push(resource.clone());
if let Some(resources) = non_referenced_resources {
- get_resource_map(resources).insert(id, resource.clone());
+ get_resource_map(resources).insert(index, resource.clone());
}
}
!is_removed
@@ -459,27 +469,29 @@ impl<A: HalApi> LifetimeTracker<A> {
);
removed_resources.drain(..).for_each(|bundle| {
for v in bundle.used.buffers.write().drain_resources() {
- self.suspected_resources.buffers.insert(v.as_info().id(), v);
+ self.suspected_resources
+ .buffers
+ .insert(v.as_info().tracker_index(), v);
}
for v in bundle.used.textures.write().drain_resources() {
self.suspected_resources
.textures
- .insert(v.as_info().id(), v);
+ .insert(v.as_info().tracker_index(), v);
}
for v in bundle.used.bind_groups.write().drain_resources() {
self.suspected_resources
.bind_groups
- .insert(v.as_info().id(), v);
+ .insert(v.as_info().tracker_index(), v);
}
for v in bundle.used.render_pipelines.write().drain_resources() {
self.suspected_resources
.render_pipelines
- .insert(v.as_info().id(), v);
+ .insert(v.as_info().tracker_index(), v);
}
for v in bundle.used.query_sets.write().drain_resources() {
self.suspected_resources
.query_sets
- .insert(v.as_info().id(), v);
+ .insert(v.as_info().tracker_index(), v);
}
});
self
@@ -496,27 +508,30 @@ impl<A: HalApi> LifetimeTracker<A> {
);
removed_resource.drain(..).for_each(|bind_group| {
for v in bind_group.used.buffers.drain_resources() {
- self.suspected_resources.buffers.insert(v.as_info().id(), v);
+ self.suspected_resources
+ .buffers
+ .insert(v.as_info().tracker_index(), v);
}
for v in bind_group.used.textures.drain_resources() {
self.suspected_resources
.textures
- .insert(v.as_info().id(), v);
+ .insert(v.as_info().tracker_index(), v);
}
for v in bind_group.used.views.drain_resources() {
self.suspected_resources
.texture_views
- .insert(v.as_info().id(), v);
+ .insert(v.as_info().tracker_index(), v);
}
for v in bind_group.used.samplers.drain_resources() {
self.suspected_resources
.samplers
- .insert(v.as_info().id(), v);
+ .insert(v.as_info().tracker_index(), v);
}
- self.suspected_resources
- .bind_group_layouts
- .insert(bind_group.layout.as_info().id(), bind_group.layout.clone());
+ self.suspected_resources.bind_group_layouts.insert(
+ bind_group.layout.as_info().tracker_index(),
+ bind_group.layout.clone(),
+ );
});
self
}
@@ -605,7 +620,7 @@ impl<A: HalApi> LifetimeTracker<A> {
);
removed_resources.drain(..).for_each(|compute_pipeline| {
self.suspected_resources.pipeline_layouts.insert(
- compute_pipeline.layout.as_info().id(),
+ compute_pipeline.layout.as_info().tracker_index(),
compute_pipeline.layout.clone(),
);
});
@@ -623,7 +638,7 @@ impl<A: HalApi> LifetimeTracker<A> {
);
removed_resources.drain(..).for_each(|render_pipeline| {
self.suspected_resources.pipeline_layouts.insert(
- render_pipeline.layout.as_info().id(),
+ render_pipeline.layout.as_info().tracker_index(),
render_pipeline.layout.clone(),
);
});
@@ -642,7 +657,7 @@ impl<A: HalApi> LifetimeTracker<A> {
for bgl in &pipeline_layout.bind_group_layouts {
self.suspected_resources
.bind_group_layouts
- .insert(bgl.as_info().id(), bgl.clone());
+ .insert(bgl.as_info().tracker_index(), bgl.clone());
}
});
self
@@ -773,14 +788,14 @@ impl<A: HalApi> LifetimeTracker<A> {
Vec::with_capacity(self.ready_to_map.len());
for buffer in self.ready_to_map.drain(..) {
- let buffer_id = buffer.info.id();
+ let tracker_index = buffer.info.tracker_index();
let is_removed = {
let mut trackers = trackers.lock();
- trackers.buffers.remove_abandoned(buffer_id)
+ trackers.buffers.remove_abandoned(tracker_index)
};
if is_removed {
*buffer.map_state.lock() = resource::BufferMapState::Idle;
- log::trace!("Buffer ready to map {:?} is not tracked anymore", buffer_id);
+ log::trace!("Buffer ready to map {tracker_index:?} is not tracked anymore");
} else {
let mapping = match std::mem::replace(
&mut *buffer.map_state.lock(),
@@ -798,7 +813,7 @@ impl<A: HalApi> LifetimeTracker<A> {
_ => panic!("No pending mapping."),
};
let status = if mapping.range.start != mapping.range.end {
- log::debug!("Buffer {:?} map state -> Active", buffer_id);
+ log::debug!("Buffer {tracker_index:?} map state -> Active");
let host = mapping.op.host;
let size = mapping.range.end - mapping.range.start;
match super::map_buffer(raw, &buffer, mapping.range.start, size, host) {
diff --git a/third_party/rust/wgpu-core/src/device/queue.rs b/third_party/rust/wgpu-core/src/device/queue.rs
index 08c5b767b6..6ebb9eb09b 100644
--- a/third_party/rust/wgpu-core/src/device/queue.rs
+++ b/third_party/rust/wgpu-core/src/device/queue.rs
@@ -12,7 +12,7 @@ use crate::{
global::Global,
hal_api::HalApi,
hal_label,
- id::{self, QueueId},
+ id::{self, DeviceId, QueueId},
init_tracker::{has_copy_partial_init_tracker_coverage, TextureInitRange},
resource::{
Buffer, BufferAccessError, BufferMapState, DestroyedBuffer, DestroyedTexture, Resource,
@@ -188,10 +188,17 @@ impl<A: HalApi> EncoderInFlight<A> {
#[derive(Debug)]
pub(crate) struct PendingWrites<A: HalApi> {
pub command_encoder: A::CommandEncoder,
- pub is_active: bool,
+
+ /// True if `command_encoder` is in the "recording" state, as
+ /// described in the docs for the [`wgpu_hal::CommandEncoder`]
+ /// trait.
+ pub is_recording: bool,
+
pub temp_resources: Vec<TempResource<A>>,
pub dst_buffers: FastHashMap<id::BufferId, Arc<Buffer<A>>>,
pub dst_textures: FastHashMap<id::TextureId, Arc<Texture<A>>>,
+
+ /// All command buffers allocated from `command_encoder`.
pub executing_command_buffers: Vec<A::CommandBuffer>,
}
@@ -199,7 +206,7 @@ impl<A: HalApi> PendingWrites<A> {
pub fn new(command_encoder: A::CommandEncoder) -> Self {
Self {
command_encoder,
- is_active: false,
+ is_recording: false,
temp_resources: Vec::new(),
dst_buffers: FastHashMap::default(),
dst_textures: FastHashMap::default(),
@@ -209,7 +216,7 @@ impl<A: HalApi> PendingWrites<A> {
pub fn dispose(mut self, device: &A::Device) {
unsafe {
- if self.is_active {
+ if self.is_recording {
self.command_encoder.discard_encoding();
}
self.command_encoder
@@ -232,9 +239,9 @@ impl<A: HalApi> PendingWrites<A> {
fn pre_submit(&mut self) -> Result<Option<&A::CommandBuffer>, DeviceError> {
self.dst_buffers.clear();
self.dst_textures.clear();
- if self.is_active {
+ if self.is_recording {
let cmd_buf = unsafe { self.command_encoder.end_encoding()? };
- self.is_active = false;
+ self.is_recording = false;
self.executing_command_buffers.push(cmd_buf);
return Ok(self.executing_command_buffers.last());
@@ -262,23 +269,23 @@ impl<A: HalApi> PendingWrites<A> {
}
pub fn activate(&mut self) -> &mut A::CommandEncoder {
- if !self.is_active {
+ if !self.is_recording {
unsafe {
self.command_encoder
.begin_encoding(Some("(wgpu internal) PendingWrites"))
.unwrap();
}
- self.is_active = true;
+ self.is_recording = true;
}
&mut self.command_encoder
}
pub fn deactivate(&mut self) {
- if self.is_active {
+ if self.is_recording {
unsafe {
self.command_encoder.discard_encoding();
}
- self.is_active = false;
+ self.is_recording = false;
}
}
}
@@ -303,7 +310,10 @@ fn prepare_staging_buffer<A: HalApi>(
raw: Mutex::new(Some(buffer)),
device: device.clone(),
size,
- info: ResourceInfo::new("<StagingBuffer>"),
+ info: ResourceInfo::new(
+ "<StagingBuffer>",
+ Some(device.tracker_indices.staging_buffers.clone()),
+ ),
is_coherent: mapping.is_coherent,
};
@@ -332,6 +342,15 @@ pub struct InvalidQueue;
#[derive(Clone, Debug, Error)]
#[non_exhaustive]
pub enum QueueWriteError {
+ #[error(
+ "Device of queue ({:?}) does not match device of write recipient ({:?})",
+ queue_device_id,
+ target_device_id
+ )]
+ DeviceMismatch {
+ queue_device_id: DeviceId,
+ target_device_id: DeviceId,
+ },
#[error(transparent)]
Queue(#[from] DeviceError),
#[error(transparent)]
@@ -376,6 +395,14 @@ impl Global {
let hub = A::hub(self);
+ let buffer_device_id = hub
+ .buffers
+ .get(buffer_id)
+ .map_err(|_| TransferError::InvalidBuffer(buffer_id))?
+ .device
+ .as_info()
+ .id();
+
let queue = hub
.queues
.get(queue_id)
@@ -383,6 +410,16 @@ impl Global {
let device = queue.device.as_ref().unwrap();
+ {
+ let queue_device_id = device.as_info().id();
+ if buffer_device_id != queue_device_id {
+ return Err(QueueWriteError::DeviceMismatch {
+ queue_device_id,
+ target_device_id: buffer_device_id,
+ });
+ }
+ }
+
let data_size = data.len() as wgt::BufferAddress;
#[cfg(feature = "trace")]
@@ -1143,7 +1180,7 @@ impl Global {
for &cmb_id in command_buffer_ids {
// we reset the used surface textures every time we use
// it, so make sure to set_size on it.
- used_surface_textures.set_size(hub.textures.read().len());
+ used_surface_textures.set_size(device.tracker_indices.textures.size());
#[allow(unused_mut)]
let mut cmdbuf = match command_buffer_guard.replace_with_error(cmb_id) {
@@ -1188,11 +1225,13 @@ impl Global {
// update submission IDs
for buffer in cmd_buf_trackers.buffers.used_resources() {
- let id = buffer.info.id();
+ let tracker_index = buffer.info.tracker_index();
let raw_buf = match buffer.raw.get(&snatch_guard) {
Some(raw) => raw,
None => {
- return Err(QueueSubmitError::DestroyedBuffer(id));
+ return Err(QueueSubmitError::DestroyedBuffer(
+ buffer.info.id(),
+ ));
}
};
buffer.info.use_at(submit_index);
@@ -1207,28 +1246,28 @@ impl Global {
.as_mut()
.unwrap()
.buffers
- .insert(id, buffer.clone());
+ .insert(tracker_index, buffer.clone());
} else {
match *buffer.map_state.lock() {
BufferMapState::Idle => (),
- _ => return Err(QueueSubmitError::BufferStillMapped(id)),
+ _ => {
+ return Err(QueueSubmitError::BufferStillMapped(
+ buffer.info.id(),
+ ))
+ }
}
}
}
for texture in cmd_buf_trackers.textures.used_resources() {
- let id = texture.info.id();
+ let tracker_index = texture.info.tracker_index();
let should_extend = match texture.inner.get(&snatch_guard) {
None => {
- return Err(QueueSubmitError::DestroyedTexture(id));
+ return Err(QueueSubmitError::DestroyedTexture(
+ texture.info.id(),
+ ));
}
Some(TextureInner::Native { .. }) => false,
- Some(TextureInner::Surface {
- ref has_work,
- ref raw,
- ..
- }) => {
- has_work.store(true, Ordering::Relaxed);
-
+ Some(TextureInner::Surface { ref raw, .. }) => {
if raw.is_some() {
submit_surface_textures_owned.push(texture.clone());
}
@@ -1242,7 +1281,7 @@ impl Global {
.as_mut()
.unwrap()
.textures
- .insert(id, texture.clone());
+ .insert(tracker_index, texture.clone());
}
if should_extend {
unsafe {
@@ -1255,11 +1294,10 @@ impl Global {
for texture_view in cmd_buf_trackers.views.used_resources() {
texture_view.info.use_at(submit_index);
if texture_view.is_unique() {
- temp_suspected
- .as_mut()
- .unwrap()
- .texture_views
- .insert(texture_view.as_info().id(), texture_view.clone());
+ temp_suspected.as_mut().unwrap().texture_views.insert(
+ texture_view.as_info().tracker_index(),
+ texture_view.clone(),
+ );
}
}
{
@@ -1279,7 +1317,7 @@ impl Global {
.as_mut()
.unwrap()
.bind_groups
- .insert(bg.as_info().id(), bg.clone());
+ .insert(bg.as_info().tracker_index(), bg.clone());
}
}
}
@@ -1290,7 +1328,7 @@ impl Global {
compute_pipeline.info.use_at(submit_index);
if compute_pipeline.is_unique() {
temp_suspected.as_mut().unwrap().compute_pipelines.insert(
- compute_pipeline.as_info().id(),
+ compute_pipeline.as_info().tracker_index(),
compute_pipeline.clone(),
);
}
@@ -1301,7 +1339,7 @@ impl Global {
render_pipeline.info.use_at(submit_index);
if render_pipeline.is_unique() {
temp_suspected.as_mut().unwrap().render_pipelines.insert(
- render_pipeline.as_info().id(),
+ render_pipeline.as_info().tracker_index(),
render_pipeline.clone(),
);
}
@@ -1309,11 +1347,10 @@ impl Global {
for query_set in cmd_buf_trackers.query_sets.used_resources() {
query_set.info.use_at(submit_index);
if query_set.is_unique() {
- temp_suspected
- .as_mut()
- .unwrap()
- .query_sets
- .insert(query_set.as_info().id(), query_set.clone());
+ temp_suspected.as_mut().unwrap().query_sets.insert(
+ query_set.as_info().tracker_index(),
+ query_set.clone(),
+ );
}
}
for bundle in cmd_buf_trackers.bundles.used_resources() {
@@ -1334,7 +1371,7 @@ impl Global {
.as_mut()
.unwrap()
.render_bundles
- .insert(bundle.as_info().id(), bundle.clone());
+ .insert(bundle.as_info().tracker_index(), bundle.clone());
}
}
}
@@ -1423,13 +1460,7 @@ impl Global {
return Err(QueueSubmitError::DestroyedTexture(id));
}
Some(TextureInner::Native { .. }) => {}
- Some(TextureInner::Surface {
- ref has_work,
- ref raw,
- ..
- }) => {
- has_work.store(true, Ordering::Relaxed);
-
+ Some(TextureInner::Surface { ref raw, .. }) => {
if raw.is_some() {
submit_surface_textures_owned.push(texture.clone());
}
diff --git a/third_party/rust/wgpu-core/src/device/resource.rs b/third_party/rust/wgpu-core/src/device/resource.rs
index b2c85a056a..28ba0eafb1 100644
--- a/third_party/rust/wgpu-core/src/device/resource.rs
+++ b/third_party/rust/wgpu-core/src/device/resource.rs
@@ -13,7 +13,6 @@ use crate::{
hal_api::HalApi,
hal_label,
hub::Hub,
- id::QueueId,
init_tracker::{
BufferInitTracker, BufferInitTrackerAction, MemoryInitKind, TextureInitRange,
TextureInitTracker, TextureInitTrackerAction,
@@ -29,13 +28,16 @@ use crate::{
resource_log,
snatch::{SnatchGuard, SnatchLock, Snatchable},
storage::Storage,
- track::{BindGroupStates, TextureSelector, Tracker},
- validation::{self, check_buffer_usage, check_texture_usage},
+ track::{BindGroupStates, TextureSelector, Tracker, TrackerIndexAllocators},
+ validation::{
+ self, check_buffer_usage, check_texture_usage, validate_color_attachment_bytes_per_sample,
+ },
FastHashMap, LabelHelpers as _, SubmissionIndex,
};
use arrayvec::ArrayVec;
use hal::{CommandEncoder as _, Device as _};
+use once_cell::sync::OnceCell;
use parking_lot::{Mutex, MutexGuard, RwLock};
use smallvec::SmallVec;
@@ -54,7 +56,7 @@ use std::{
use super::{
life::{self, ResourceMaps},
- queue::{self},
+ queue::{self, Queue},
DeviceDescriptor, DeviceError, ImplicitPipelineContext, UserClosures, ENTRYPOINT_FAILURE_ERROR,
IMPLICIT_BIND_GROUP_LAYOUT_ERROR_LABEL, ZERO_BUFFER_SIZE,
};
@@ -87,8 +89,8 @@ use super::{
pub struct Device<A: HalApi> {
raw: Option<A::Device>,
pub(crate) adapter: Arc<Adapter<A>>,
- pub(crate) queue_id: RwLock<Option<QueueId>>,
- queue_to_drop: RwLock<Option<A::Queue>>,
+ pub(crate) queue: OnceCell<Weak<Queue<A>>>,
+ queue_to_drop: OnceCell<A::Queue>,
pub(crate) zero_buffer: Option<A::Buffer>,
pub(crate) info: ResourceInfo<Device<A>>,
@@ -116,6 +118,7 @@ pub struct Device<A: HalApi> {
/// Has to be locked temporarily only (locked last)
/// and never before pending_writes
pub(crate) trackers: Mutex<Tracker<A>>,
+ pub(crate) tracker_indices: TrackerIndexAllocators,
// Life tracker should be locked right after the device and before anything else.
life_tracker: Mutex<LifetimeTracker<A>>,
/// Temporary storage for resource management functions. Cleared at the end
@@ -160,7 +163,7 @@ impl<A: HalApi> Drop for Device<A> {
unsafe {
raw.destroy_buffer(self.zero_buffer.take().unwrap());
raw.destroy_fence(self.fence.write().take().unwrap());
- let queue = self.queue_to_drop.write().take().unwrap();
+ let queue = self.queue_to_drop.take().unwrap();
raw.exit(queue);
}
}
@@ -258,16 +261,17 @@ impl<A: HalApi> Device<A> {
Ok(Self {
raw: Some(raw_device),
adapter: adapter.clone(),
- queue_id: RwLock::new(None),
- queue_to_drop: RwLock::new(None),
+ queue: OnceCell::new(),
+ queue_to_drop: OnceCell::new(),
zero_buffer: Some(zero_buffer),
- info: ResourceInfo::new("<device>"),
+ info: ResourceInfo::new("<device>", None),
command_allocator: Mutex::new(Some(com_alloc)),
active_submission_index: AtomicU64::new(0),
fence: RwLock::new(Some(fence)),
snatchable_lock: unsafe { SnatchLock::new() },
valid: AtomicBool::new(true),
trackers: Mutex::new(Tracker::new()),
+ tracker_indices: TrackerIndexAllocators::new(),
life_tracker: Mutex::new(life::LifetimeTracker::new()),
temp_suspected: Mutex::new(Some(life::ResourceMaps::new())),
bgl_pool: ResourcePool::new(),
@@ -300,7 +304,7 @@ impl<A: HalApi> Device<A> {
}
pub(crate) fn release_queue(&self, queue: A::Queue) {
- self.queue_to_drop.write().replace(queue);
+ assert!(self.queue_to_drop.set(queue).is_ok());
}
pub(crate) fn lock_life<'a>(&'a self) -> MutexGuard<'a, LifetimeTracker<A>> {
@@ -339,7 +343,8 @@ impl<A: HalApi> Device<A> {
let Some(bind_group) = bind_group.upgrade() else {
continue;
};
- let Some(raw_bind_group) = bind_group.raw.snatch(self.snatchable_lock.write()) else {
+ let Some(raw_bind_group) = bind_group.raw.snatch(self.snatchable_lock.write())
+ else {
continue;
};
@@ -357,6 +362,14 @@ impl<A: HalApi> Device<A> {
}
}
+ pub fn get_queue(&self) -> Option<Arc<Queue<A>>> {
+ self.queue.get().as_ref()?.upgrade()
+ }
+
+ pub fn set_queue(&self, queue: Arc<Queue<A>>) {
+ assert!(self.queue.set(Arc::downgrade(&queue)).is_ok());
+ }
+
/// Check this device for completed commands.
///
/// The `maintain` argument tells how the maintence function should behave, either
@@ -483,56 +496,56 @@ impl<A: HalApi> Device<A> {
if resource.is_unique() {
temp_suspected
.buffers
- .insert(resource.as_info().id(), resource.clone());
+ .insert(resource.as_info().tracker_index(), resource.clone());
}
}
for resource in trackers.textures.used_resources() {
if resource.is_unique() {
temp_suspected
.textures
- .insert(resource.as_info().id(), resource.clone());
+ .insert(resource.as_info().tracker_index(), resource.clone());
}
}
for resource in trackers.views.used_resources() {
if resource.is_unique() {
temp_suspected
.texture_views
- .insert(resource.as_info().id(), resource.clone());
+ .insert(resource.as_info().tracker_index(), resource.clone());
}
}
for resource in trackers.bind_groups.used_resources() {
if resource.is_unique() {
temp_suspected
.bind_groups
- .insert(resource.as_info().id(), resource.clone());
+ .insert(resource.as_info().tracker_index(), resource.clone());
}
}
for resource in trackers.samplers.used_resources() {
if resource.is_unique() {
temp_suspected
.samplers
- .insert(resource.as_info().id(), resource.clone());
+ .insert(resource.as_info().tracker_index(), resource.clone());
}
}
for resource in trackers.compute_pipelines.used_resources() {
if resource.is_unique() {
temp_suspected
.compute_pipelines
- .insert(resource.as_info().id(), resource.clone());
+ .insert(resource.as_info().tracker_index(), resource.clone());
}
}
for resource in trackers.render_pipelines.used_resources() {
if resource.is_unique() {
temp_suspected
.render_pipelines
- .insert(resource.as_info().id(), resource.clone());
+ .insert(resource.as_info().tracker_index(), resource.clone());
}
}
for resource in trackers.query_sets.used_resources() {
if resource.is_unique() {
temp_suspected
.query_sets
- .insert(resource.as_info().id(), resource.clone());
+ .insert(resource.as_info().tracker_index(), resource.clone());
}
}
}
@@ -633,7 +646,10 @@ impl<A: HalApi> Device<A> {
initialization_status: RwLock::new(BufferInitTracker::new(aligned_size)),
sync_mapped_writes: Mutex::new(None),
map_state: Mutex::new(resource::BufferMapState::Idle),
- info: ResourceInfo::new(desc.label.borrow_or_default()),
+ info: ResourceInfo::new(
+ desc.label.borrow_or_default(),
+ Some(self.tracker_indices.buffers.clone()),
+ ),
bind_groups: Mutex::new(Vec::new()),
})
}
@@ -662,7 +678,10 @@ impl<A: HalApi> Device<A> {
mips: 0..desc.mip_level_count,
layers: 0..desc.array_layer_count(),
},
- info: ResourceInfo::new(desc.label.borrow_or_default()),
+ info: ResourceInfo::new(
+ desc.label.borrow_or_default(),
+ Some(self.tracker_indices.textures.clone()),
+ ),
clear_mode: RwLock::new(clear_mode),
views: Mutex::new(Vec::new()),
bind_groups: Mutex::new(Vec::new()),
@@ -684,7 +703,10 @@ impl<A: HalApi> Device<A> {
initialization_status: RwLock::new(BufferInitTracker::new(0)),
sync_mapped_writes: Mutex::new(None),
map_state: Mutex::new(resource::BufferMapState::Idle),
- info: ResourceInfo::new(desc.label.borrow_or_default()),
+ info: ResourceInfo::new(
+ desc.label.borrow_or_default(),
+ Some(self.tracker_indices.buffers.clone()),
+ ),
bind_groups: Mutex::new(Vec::new()),
}
}
@@ -1262,7 +1284,10 @@ impl<A: HalApi> Device<A> {
render_extent,
samples: texture.desc.sample_count,
selector,
- info: ResourceInfo::new(desc.label.borrow_or_default()),
+ info: ResourceInfo::new(
+ desc.label.borrow_or_default(),
+ Some(self.tracker_indices.texture_views.clone()),
+ ),
})
}
@@ -1366,7 +1391,10 @@ impl<A: HalApi> Device<A> {
Ok(Sampler {
raw: Some(raw),
device: self.clone(),
- info: ResourceInfo::new(desc.label.borrow_or_default()),
+ info: ResourceInfo::new(
+ desc.label.borrow_or_default(),
+ Some(self.tracker_indices.samplers.clone()),
+ ),
comparison: desc.compare.is_some(),
filtering: desc.min_filter == wgt::FilterMode::Linear
|| desc.mag_filter == wgt::FilterMode::Linear,
@@ -1484,6 +1512,10 @@ impl<A: HalApi> Device<A> {
.contains(wgt::Features::SHADER_EARLY_DEPTH_TEST),
);
caps.set(
+ Caps::SHADER_INT64,
+ self.features.contains(wgt::Features::SHADER_INT64),
+ );
+ caps.set(
Caps::MULTISAMPLED_SHADING,
self.downlevel
.flags
@@ -1559,7 +1591,7 @@ impl<A: HalApi> Device<A> {
raw: Some(raw),
device: self.clone(),
interface: Some(interface),
- info: ResourceInfo::new(desc.label.borrow_or_default()),
+ info: ResourceInfo::new(desc.label.borrow_or_default(), None),
label: desc.label.borrow_or_default().to_string(),
})
}
@@ -1600,7 +1632,7 @@ impl<A: HalApi> Device<A> {
raw: Some(raw),
device: self.clone(),
interface: None,
- info: ResourceInfo::new(desc.label.borrow_or_default()),
+ info: ResourceInfo::new(desc.label.borrow_or_default(), None),
label: desc.label.borrow_or_default().to_string(),
})
}
@@ -1704,10 +1736,23 @@ impl<A: HalApi> Device<A> {
BindGroupLayoutEntryError::SampleTypeFloatFilterableBindingMultisampled,
});
}
- Bt::Texture { .. } => (
- Some(wgt::Features::TEXTURE_BINDING_ARRAY),
- WritableStorage::No,
- ),
+ Bt::Texture {
+ multisampled,
+ view_dimension,
+ ..
+ } => {
+ if multisampled && view_dimension != TextureViewDimension::D2 {
+ return Err(binding_model::CreateBindGroupLayoutError::Entry {
+ binding: entry.binding,
+ error: BindGroupLayoutEntryError::Non2DMultisampled(view_dimension),
+ });
+ }
+
+ (
+ Some(wgt::Features::TEXTURE_BINDING_ARRAY),
+ WritableStorage::No,
+ )
+ }
Bt::StorageTexture {
access,
view_dimension,
@@ -1840,7 +1885,10 @@ impl<A: HalApi> Device<A> {
entries: entry_map,
origin,
binding_count_validator: count_validator,
- info: ResourceInfo::new(label.unwrap_or("<BindGroupLayout>")),
+ info: ResourceInfo::new(
+ label.unwrap_or("<BindGroupLayout>"),
+ Some(self.tracker_indices.bind_group_layouts.clone()),
+ ),
label: label.unwrap_or_default().to_string(),
})
}
@@ -1905,7 +1953,7 @@ impl<A: HalApi> Device<A> {
.add_single(storage, bb.buffer_id, internal_use)
.ok_or(Error::InvalidBuffer(bb.buffer_id))?;
- check_buffer_usage(buffer.usage, pub_usage)?;
+ check_buffer_usage(bb.buffer_id, buffer.usage, pub_usage)?;
let raw_buffer = buffer
.raw
.get(snatch_guard)
@@ -2273,7 +2321,10 @@ impl<A: HalApi> Device<A> {
raw: Snatchable::new(raw),
device: self.clone(),
layout: layout.clone(),
- info: ResourceInfo::new(desc.label.borrow_or_default()),
+ info: ResourceInfo::new(
+ desc.label.borrow_or_default(),
+ Some(self.tracker_indices.bind_groups.clone()),
+ ),
used,
used_buffer_ranges,
used_texture_ranges,
@@ -2555,7 +2606,10 @@ impl<A: HalApi> Device<A> {
Ok(binding_model::PipelineLayout {
raw: Some(raw),
device: self.clone(),
- info: ResourceInfo::new(desc.label.borrow_or_default()),
+ info: ResourceInfo::new(
+ desc.label.borrow_or_default(),
+ Some(self.tracker_indices.pipeline_layouts.clone()),
+ ),
bind_group_layouts,
push_constant_ranges: desc.push_constant_ranges.iter().cloned().collect(),
})
@@ -2656,14 +2710,21 @@ impl<A: HalApi> Device<A> {
let mut shader_binding_sizes = FastHashMap::default();
let io = validation::StageIo::default();
+ let final_entry_point_name;
+
{
let stage = wgt::ShaderStages::COMPUTE;
+ final_entry_point_name = shader_module.finalize_entry_point_name(
+ stage,
+ desc.stage.entry_point.as_ref().map(|ep| ep.as_ref()),
+ )?;
+
if let Some(ref interface) = shader_module.interface {
let _ = interface.check_stage(
&mut binding_layout_source,
&mut shader_binding_sizes,
- &desc.stage.entry_point,
+ &final_entry_point_name,
stage,
io,
None,
@@ -2691,7 +2752,7 @@ impl<A: HalApi> Device<A> {
label: desc.label.to_hal(self.instance_flags),
layout: pipeline_layout.raw(),
stage: hal::ProgrammableStage {
- entry_point: desc.stage.entry_point.as_ref(),
+ entry_point: final_entry_point_name.as_ref(),
module: shader_module.raw(),
},
};
@@ -2720,7 +2781,10 @@ impl<A: HalApi> Device<A> {
device: self.clone(),
_shader_module: shader_module,
late_sized_buffer_groups,
- info: ResourceInfo::new(desc.label.borrow_or_default()),
+ info: ResourceInfo::new(
+ desc.label.borrow_or_default(),
+ Some(self.tracker_indices.compute_pipelines.clone()),
+ ),
};
Ok(pipeline)
}
@@ -2749,11 +2813,12 @@ impl<A: HalApi> Device<A> {
let mut shader_binding_sizes = FastHashMap::default();
let num_attachments = desc.fragment.as_ref().map(|f| f.targets.len()).unwrap_or(0);
- if num_attachments > hal::MAX_COLOR_ATTACHMENTS {
+ let max_attachments = self.limits.max_color_attachments as usize;
+ if num_attachments > max_attachments {
return Err(pipeline::CreateRenderPipelineError::ColorAttachment(
command::ColorAttachmentError::TooMany {
given: num_attachments,
- limit: hal::MAX_COLOR_ATTACHMENTS,
+ limit: max_attachments,
},
));
}
@@ -2959,6 +3024,7 @@ impl<A: HalApi> Device<A> {
}
}
}
+
break None;
};
if let Some(e) = error {
@@ -2967,6 +3033,16 @@ impl<A: HalApi> Device<A> {
}
}
+ let limit = self.limits.max_color_attachment_bytes_per_sample;
+ let formats = color_targets
+ .iter()
+ .map(|cs| cs.as_ref().map(|cs| cs.format));
+ if let Err(total) = validate_color_attachment_bytes_per_sample(formats, limit) {
+ return Err(pipeline::CreateRenderPipelineError::ColorAttachment(
+ command::ColorAttachmentError::TooManyBytesPerSample { total, limit },
+ ));
+ }
+
if let Some(ds) = depth_stencil_state {
let error = loop {
let format_features = self.describe_format_features(adapter, ds.format)?;
@@ -3051,6 +3127,7 @@ impl<A: HalApi> Device<A> {
};
let vertex_shader_module;
+ let vertex_entry_point_name;
let vertex_stage = {
let stage_desc = &desc.vertex.stage;
let stage = wgt::ShaderStages::VERTEX;
@@ -3065,27 +3142,37 @@ impl<A: HalApi> Device<A> {
return Err(DeviceError::WrongDevice.into());
}
+ let stage_err = |error| pipeline::CreateRenderPipelineError::Stage { stage, error };
+
+ vertex_entry_point_name = vertex_shader_module
+ .finalize_entry_point_name(
+ stage,
+ stage_desc.entry_point.as_ref().map(|ep| ep.as_ref()),
+ )
+ .map_err(stage_err)?;
+
if let Some(ref interface) = vertex_shader_module.interface {
io = interface
.check_stage(
&mut binding_layout_source,
&mut shader_binding_sizes,
- &stage_desc.entry_point,
+ &vertex_entry_point_name,
stage,
io,
desc.depth_stencil.as_ref().map(|d| d.depth_compare),
)
- .map_err(|error| pipeline::CreateRenderPipelineError::Stage { stage, error })?;
+ .map_err(stage_err)?;
validated_stages |= stage;
}
hal::ProgrammableStage {
module: vertex_shader_module.raw(),
- entry_point: stage_desc.entry_point.as_ref(),
+ entry_point: &vertex_entry_point_name,
}
};
let mut fragment_shader_module = None;
+ let fragment_entry_point_name;
let fragment_stage = match desc.fragment {
Some(ref fragment_state) => {
let stage = wgt::ShaderStages::FRAGMENT;
@@ -3099,28 +3186,38 @@ impl<A: HalApi> Device<A> {
})?,
);
+ let stage_err = |error| pipeline::CreateRenderPipelineError::Stage { stage, error };
+
+ fragment_entry_point_name = shader_module
+ .finalize_entry_point_name(
+ stage,
+ fragment_state
+ .stage
+ .entry_point
+ .as_ref()
+ .map(|ep| ep.as_ref()),
+ )
+ .map_err(stage_err)?;
+
if validated_stages == wgt::ShaderStages::VERTEX {
if let Some(ref interface) = shader_module.interface {
io = interface
.check_stage(
&mut binding_layout_source,
&mut shader_binding_sizes,
- &fragment_state.stage.entry_point,
+ &fragment_entry_point_name,
stage,
io,
desc.depth_stencil.as_ref().map(|d| d.depth_compare),
)
- .map_err(|error| pipeline::CreateRenderPipelineError::Stage {
- stage,
- error,
- })?;
+ .map_err(stage_err)?;
validated_stages |= stage;
}
}
if let Some(ref interface) = shader_module.interface {
shader_expects_dual_source_blending = interface
- .fragment_uses_dual_source_blending(&fragment_state.stage.entry_point)
+ .fragment_uses_dual_source_blending(&fragment_entry_point_name)
.map_err(|error| pipeline::CreateRenderPipelineError::Stage {
stage,
error,
@@ -3129,7 +3226,7 @@ impl<A: HalApi> Device<A> {
Some(hal::ProgrammableStage {
module: shader_module.raw(),
- entry_point: fragment_state.stage.entry_point.as_ref(),
+ entry_point: &fragment_entry_point_name,
})
}
None => None,
@@ -3302,7 +3399,10 @@ impl<A: HalApi> Device<A> {
strip_index_format: desc.primitive.strip_index_format,
vertex_steps,
late_sized_buffer_groups,
- info: ResourceInfo::new(desc.label.borrow_or_default()),
+ info: ResourceInfo::new(
+ desc.label.borrow_or_default(),
+ Some(self.tracker_indices.render_pipelines.clone()),
+ ),
};
Ok(pipeline)
}
@@ -3415,7 +3515,7 @@ impl<A: HalApi> Device<A> {
Ok(QuerySet {
raw: Some(unsafe { self.raw().create_query_set(&hal_desc).unwrap() }),
device: self.clone(),
- info: ResourceInfo::new(""),
+ info: ResourceInfo::new("", Some(self.tracker_indices.query_sets.clone())),
desc: desc.map_label(|_| ()),
})
}
diff --git a/third_party/rust/wgpu-core/src/id.rs b/third_party/rust/wgpu-core/src/id.rs
index 1dbb491e60..72b74218d0 100644
--- a/third_party/rust/wgpu-core/src/id.rs
+++ b/third_party/rust/wgpu-core/src/id.rs
@@ -275,7 +275,7 @@ where
{
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
- self.0.partial_cmp(&other.0)
+ Some(self.cmp(other))
}
}
diff --git a/third_party/rust/wgpu-core/src/identity.rs b/third_party/rust/wgpu-core/src/identity.rs
index 0e34055c74..d76d29341a 100644
--- a/third_party/rust/wgpu-core/src/identity.rs
+++ b/third_party/rust/wgpu-core/src/identity.rs
@@ -3,10 +3,17 @@ use wgt::Backend;
use crate::{
id::{Id, Marker},
- Epoch, FastHashMap, Index,
+ Epoch, Index,
};
use std::{fmt::Debug, marker::PhantomData};
+#[derive(Copy, Clone, Debug, PartialEq)]
+enum IdSource {
+ External,
+ Allocated,
+ None,
+}
+
/// A simple structure to allocate [`Id`] identifiers.
///
/// Calling [`alloc`] returns a fresh, never-before-seen id. Calling [`free`]
@@ -34,12 +41,15 @@ use std::{fmt::Debug, marker::PhantomData};
/// [`Backend`]: wgt::Backend;
/// [`alloc`]: IdentityManager::alloc
/// [`free`]: IdentityManager::free
-#[derive(Debug, Default)]
+#[derive(Debug)]
pub(super) struct IdentityValues {
free: Vec<(Index, Epoch)>,
- //sorted by Index
- used: FastHashMap<Epoch, Vec<Index>>,
+ next_index: Index,
count: usize,
+ // Sanity check: The allocation logic works under the assumption that we don't
+ // do a mix of allocating ids from here and providing ids manually for the same
+ // storage container.
+ id_source: IdSource,
}
impl IdentityValues {
@@ -48,35 +58,41 @@ impl IdentityValues {
/// The backend is incorporated into the id, so that ids allocated with
/// different `backend` values are always distinct.
pub fn alloc<T: Marker>(&mut self, backend: Backend) -> Id<T> {
+ assert!(
+ self.id_source != IdSource::External,
+ "Mix of internally allocated and externally provided IDs"
+ );
+ self.id_source = IdSource::Allocated;
+
self.count += 1;
match self.free.pop() {
Some((index, epoch)) => Id::zip(index, epoch + 1, backend),
None => {
+ let index = self.next_index;
+ self.next_index += 1;
let epoch = 1;
- let used = self.used.entry(epoch).or_insert_with(Default::default);
- let index = if let Some(i) = used.iter().max_by_key(|v| *v) {
- i + 1
- } else {
- 0
- };
- used.push(index);
Id::zip(index, epoch, backend)
}
}
}
pub fn mark_as_used<T: Marker>(&mut self, id: Id<T>) -> Id<T> {
+ assert!(
+ self.id_source != IdSource::Allocated,
+ "Mix of internally allocated and externally provided IDs"
+ );
+ self.id_source = IdSource::External;
+
self.count += 1;
- let (index, epoch, _backend) = id.unzip();
- let used = self.used.entry(epoch).or_insert_with(Default::default);
- used.push(index);
id
}
/// Free `id`. It will never be returned from `alloc` again.
pub fn release<T: Marker>(&mut self, id: Id<T>) {
- let (index, epoch, _backend) = id.unzip();
- self.free.push((index, epoch));
+ if let IdSource::Allocated = self.id_source {
+ let (index, epoch, _backend) = id.unzip();
+ self.free.push((index, epoch));
+ }
self.count -= 1;
}
@@ -106,7 +122,12 @@ impl<T: Marker> IdentityManager<T> {
impl<T: Marker> IdentityManager<T> {
pub fn new() -> Self {
Self {
- values: Mutex::new(IdentityValues::default()),
+ values: Mutex::new(IdentityValues {
+ free: Vec::new(),
+ next_index: 0,
+ count: 0,
+ id_source: IdSource::None,
+ }),
_phantom: PhantomData,
}
}
@@ -115,15 +136,11 @@ impl<T: Marker> IdentityManager<T> {
#[test]
fn test_epoch_end_of_life() {
use crate::id;
-
let man = IdentityManager::<id::markers::Buffer>::new();
- let forced_id = man.mark_as_used(id::BufferId::zip(0, 1, Backend::Empty));
- assert_eq!(forced_id.unzip().0, 0);
let id1 = man.process(Backend::Empty);
- assert_eq!(id1.unzip().0, 1);
+ assert_eq!(id1.unzip(), (0, 1, Backend::Empty));
man.free(id1);
let id2 = man.process(Backend::Empty);
// confirm that the epoch 1 is no longer re-used
- assert_eq!(id2.unzip().0, 1);
- assert_eq!(id2.unzip().1, 2);
+ assert_eq!(id2.unzip(), (0, 2, Backend::Empty));
}
diff --git a/third_party/rust/wgpu-core/src/instance.rs b/third_party/rust/wgpu-core/src/instance.rs
index 582571c2b8..b909245fac 100644
--- a/third_party/rust/wgpu-core/src/instance.rs
+++ b/third_party/rust/wgpu-core/src/instance.rs
@@ -198,7 +198,7 @@ impl<A: HalApi> Adapter<A> {
Self {
raw,
- info: ResourceInfo::new("<Adapter>"),
+ info: ResourceInfo::new("<Adapter>", None),
}
}
@@ -303,7 +303,7 @@ impl<A: HalApi> Adapter<A> {
let queue = Queue {
device: None,
raw: Some(hal_device.queue),
- info: ResourceInfo::new("<Queue>"),
+ info: ResourceInfo::new("<Queue>", None),
};
return Ok((device, queue));
}
@@ -521,7 +521,7 @@ impl Global {
let surface = Surface {
presentation: Mutex::new(None),
- info: ResourceInfo::new("<Surface>"),
+ info: ResourceInfo::new("<Surface>", None),
raw: hal_surface,
};
@@ -542,7 +542,7 @@ impl Global {
let surface = Surface {
presentation: Mutex::new(None),
- info: ResourceInfo::new("<Surface>"),
+ info: ResourceInfo::new("<Surface>", None),
raw: {
let hal_surface = self
.instance
@@ -575,7 +575,7 @@ impl Global {
let surface = Surface {
presentation: Mutex::new(None),
- info: ResourceInfo::new("<Surface>"),
+ info: ResourceInfo::new("<Surface>", None),
raw: {
let hal_surface = self
.instance
@@ -604,7 +604,7 @@ impl Global {
let surface = Surface {
presentation: Mutex::new(None),
- info: ResourceInfo::new("<Surface>"),
+ info: ResourceInfo::new("<Surface>", None),
raw: {
let hal_surface = self
.instance
@@ -633,7 +633,7 @@ impl Global {
let surface = Surface {
presentation: Mutex::new(None),
- info: ResourceInfo::new("<Surface>"),
+ info: ResourceInfo::new("<Surface>", None),
raw: {
let hal_surface = self
.instance
@@ -1072,10 +1072,10 @@ impl Global {
let device = hub.devices.get(device_id).unwrap();
queue.device = Some(device.clone());
- let (queue_id, _) = queue_fid.assign(queue);
+ let (queue_id, queue) = queue_fid.assign(queue);
resource_log!("Created Queue {:?}", queue_id);
- device.queue_id.write().replace(queue_id);
+ device.set_queue(queue);
return (device_id, queue_id, None);
};
@@ -1124,10 +1124,10 @@ impl Global {
let device = hub.devices.get(device_id).unwrap();
queue.device = Some(device.clone());
- let (queue_id, _) = queues_fid.assign(queue);
+ let (queue_id, queue) = queues_fid.assign(queue);
resource_log!("Created Queue {:?}", queue_id);
- device.queue_id.write().replace(queue_id);
+ device.set_queue(queue);
return (device_id, queue_id, None);
};
diff --git a/third_party/rust/wgpu-core/src/lib.rs b/third_party/rust/wgpu-core/src/lib.rs
index 9f6526fc11..5454f0d682 100644
--- a/third_party/rust/wgpu-core/src/lib.rs
+++ b/third_party/rust/wgpu-core/src/lib.rs
@@ -3,36 +3,8 @@
//! into other language-specific user-friendly libraries.
//!
//! ## Feature flags
-// NOTE: feature docs. below should be kept in sync. with `Cargo.toml`!
+#![doc = document_features::document_features!()]
//!
-//! - **`api_log_info`** --- Log all API entry points at info instead of trace level.
-//! - **`resource_log_info`** --- Log resource lifecycle management at info instead of trace level.
-//! - **`link`** _(enabled by default)_ --- Use static linking for libraries. Disable to manually
-//! link. Enabled by default.
-//! - **`renderdoc`** --- Support the Renderdoc graphics debugger:
-//! [https://renderdoc.org/](https://renderdoc.org/)
-//! - **`strict_asserts`** --- Apply run-time checks, even in release builds. These are in addition
-//! to the validation carried out at public APIs in all builds.
-//! - **`serde`** --- Enables serialization via `serde` on common wgpu types.
-//! - **`trace`** --- Enable API tracing.
-//! - **`replay`** --- Enable API replaying
-//! - **`wgsl`** --- Enable `ShaderModuleSource::Wgsl`
-//! - **`fragile-send-sync-non-atomic-wasm`** --- Implement `Send` and `Sync` on Wasm, but only if
-//! atomics are not enabled.
-//!
-//! WebGL/WebGPU objects can not be shared between threads. However, it can be useful to
-//! artificially mark them as `Send` and `Sync` anyways to make it easier to write cross-platform
-//! code. This is technically _very_ unsafe in a multithreaded environment, but on a wasm binary
-//! compiled without atomics we know we are definitely not in a multithreaded environment.
-//!
-//! ### Backends, passed through to wgpu-hal
-//!
-//! - **`metal`** --- Enable the `metal` backend.
-//! - **`vulkan`** --- Enable the `vulkan` backend.
-//! - **`gles`** --- Enable the `GLES` backend.
-//!
-//! This is used for all of GLES, OpenGL, and WebGL.
-//! - **`dx12`** --- Enable the `dx12` backend.
// When we have no backends, we end up with a lot of dead or otherwise unreachable code.
#![cfg_attr(
diff --git a/third_party/rust/wgpu-core/src/pipeline.rs b/third_party/rust/wgpu-core/src/pipeline.rs
index acc1b24b0c..4a7651b327 100644
--- a/third_party/rust/wgpu-core/src/pipeline.rs
+++ b/third_party/rust/wgpu-core/src/pipeline.rs
@@ -92,6 +92,19 @@ impl<A: HalApi> ShaderModule<A> {
pub(crate) fn raw(&self) -> &A::ShaderModule {
self.raw.as_ref().unwrap()
}
+
+ pub(crate) fn finalize_entry_point_name(
+ &self,
+ stage_bit: wgt::ShaderStages,
+ entry_point: Option<&str>,
+ ) -> Result<String, validation::StageError> {
+ match &self.interface {
+ Some(interface) => interface.finalize_entry_point_name(stage_bit, entry_point),
+ None => entry_point
+ .map(|ep| ep.to_string())
+ .ok_or(validation::StageError::NoEntryPointFound),
+ }
+ }
}
#[derive(Clone, Debug)]
@@ -213,9 +226,13 @@ impl CreateShaderModuleError {
pub struct ProgrammableStageDescriptor<'a> {
/// The compiled shader module for this stage.
pub module: ShaderModuleId,
- /// The name of the entry point in the compiled shader. There must be a function with this name
- /// in the shader.
- pub entry_point: Cow<'a, str>,
+ /// The name of the entry point in the compiled shader. The name is selected using the
+ /// following logic:
+ ///
+ /// * If `Some(name)` is specified, there must be a function with this name in the shader.
+ /// * If a single entry point associated with this stage must be in the shader, then proceed as
+ /// if `Some(…)` was specified with that entry point's name.
+ pub entry_point: Option<Cow<'a, str>>,
}
/// Number of implicit bind groups derived at pipeline creation.
diff --git a/third_party/rust/wgpu-core/src/present.rs b/third_party/rust/wgpu-core/src/present.rs
index 4d8e1df73e..cb4e17798f 100644
--- a/third_party/rust/wgpu-core/src/present.rs
+++ b/third_party/rust/wgpu-core/src/present.rs
@@ -9,10 +9,7 @@ When this texture is presented, we remove it from the device tracker as well as
extract it from the hub.
!*/
-use std::{
- borrow::Borrow,
- sync::atomic::{AtomicBool, Ordering},
-};
+use std::borrow::Borrow;
#[cfg(feature = "trace")]
use crate::device::trace::Action;
@@ -73,7 +70,7 @@ pub enum ConfigureSurfaceError {
PreviousOutputExists,
#[error("Both `Surface` width and height must be non-zero. Wait to recreate the `Surface` until the window has non-zero area.")]
ZeroArea,
- #[error("`Surface` width and height must be within the maximum supported texture size. Requested was ({width}, {height}), maximum extent is {max_texture_dimension_2d}.")]
+ #[error("`Surface` width and height must be within the maximum supported texture size. Requested was ({width}, {height}), maximum extent for either dimension is {max_texture_dimension_2d}.")]
TooLarge {
width: u32,
height: u32,
@@ -213,7 +210,6 @@ impl Global {
inner: Snatchable::new(resource::TextureInner::Surface {
raw: Some(ast.texture),
parent_id: surface_id,
- has_work: AtomicBool::new(false),
}),
device: device.clone(),
desc: texture_desc,
@@ -224,7 +220,10 @@ impl Global {
layers: 0..1,
mips: 0..1,
},
- info: ResourceInfo::new("<Surface>"),
+ info: ResourceInfo::new(
+ "<Surface Texture>",
+ Some(device.tracker_indices.textures.clone()),
+ ),
clear_mode: RwLock::new(resource::TextureClearMode::Surface {
clear_view: Some(clear_view),
}),
@@ -240,7 +239,7 @@ impl Global {
let mut trackers = device.trackers.lock();
trackers
.textures
- .insert_single(id, resource, hal::TextureUses::UNINITIALIZED);
+ .insert_single(resource, hal::TextureUses::UNINITIALIZED);
}
if present.acquired_texture.is_some() {
@@ -298,8 +297,7 @@ impl Global {
if !device.is_valid() {
return Err(DeviceError::Lost.into());
}
- let queue_id = device.queue_id.read().unwrap();
- let queue = hub.queues.get(queue_id).unwrap();
+ let queue = device.get_queue().unwrap();
#[cfg(feature = "trace")]
if let Some(ref mut trace) = *device.trace.lock() {
@@ -318,10 +316,13 @@ impl Global {
"Removing swapchain texture {:?} from the device tracker",
texture_id
);
- device.trackers.lock().textures.remove(texture_id);
-
let texture = hub.textures.unregister(texture_id);
if let Some(texture) = texture {
+ device
+ .trackers
+ .lock()
+ .textures
+ .remove(texture.info.tracker_index());
let mut exclusive_snatch_guard = device.snatchable_lock.write();
let suf = A::get_surface(&surface);
let mut inner = texture.inner_mut(&mut exclusive_snatch_guard);
@@ -331,15 +332,10 @@ impl Global {
resource::TextureInner::Surface {
ref mut raw,
ref parent_id,
- ref has_work,
} => {
if surface_id != *parent_id {
log::error!("Presented frame is from a different surface");
Err(hal::SurfaceError::Lost)
- } else if !has_work.load(Ordering::Relaxed) {
- log::error!("No work has been submitted for this frame");
- unsafe { suf.unwrap().discard_texture(raw.take().unwrap()) };
- Err(hal::SurfaceError::Outdated)
} else {
unsafe {
queue
@@ -413,18 +409,19 @@ impl Global {
"Removing swapchain texture {:?} from the device tracker",
texture_id
);
- device.trackers.lock().textures.remove(texture_id);
let texture = hub.textures.unregister(texture_id);
+
if let Some(texture) = texture {
+ device
+ .trackers
+ .lock()
+ .textures
+ .remove(texture.info.tracker_index());
let suf = A::get_surface(&surface);
let exclusive_snatch_guard = device.snatchable_lock.write();
match texture.inner.snatch(exclusive_snatch_guard).unwrap() {
- resource::TextureInner::Surface {
- mut raw,
- parent_id,
- has_work: _,
- } => {
+ resource::TextureInner::Surface { mut raw, parent_id } => {
if surface_id == parent_id {
unsafe { suf.unwrap().discard_texture(raw.take().unwrap()) };
} else {
diff --git a/third_party/rust/wgpu-core/src/registry.rs b/third_party/rust/wgpu-core/src/registry.rs
index f55809770b..80394351af 100644
--- a/third_party/rust/wgpu-core/src/registry.rs
+++ b/third_party/rust/wgpu-core/src/registry.rs
@@ -60,7 +60,6 @@ impl<T: Resource> Registry<T> {
#[must_use]
pub(crate) struct FutureId<'a, T: Resource> {
id: Id<T::Marker>,
- identity: Arc<IdentityManager<T::Marker>>,
data: &'a RwLock<Storage<T>>,
}
@@ -75,7 +74,7 @@ impl<T: Resource> FutureId<'_, T> {
}
pub fn init(&self, mut value: T) -> Arc<T> {
- value.as_info_mut().set_id(self.id, &self.identity);
+ value.as_info_mut().set_id(self.id);
Arc::new(value)
}
@@ -117,7 +116,6 @@ impl<T: Resource> Registry<T> {
}
None => self.identity.process(self.backend),
},
- identity: self.identity.clone(),
data: &self.storage,
}
}
@@ -125,7 +123,6 @@ impl<T: Resource> Registry<T> {
pub(crate) fn request(&self) -> FutureId<T> {
FutureId {
id: self.identity.process(self.backend),
- identity: self.identity.clone(),
data: &self.storage,
}
}
@@ -142,11 +139,12 @@ impl<T: Resource> Registry<T> {
self.storage.write()
}
pub fn unregister_locked(&self, id: Id<T::Marker>, storage: &mut Storage<T>) -> Option<Arc<T>> {
+ self.identity.free(id);
storage.remove(id)
}
pub fn force_replace(&self, id: Id<T::Marker>, mut value: T) {
let mut storage = self.storage.write();
- value.as_info_mut().set_id(id, &self.identity);
+ value.as_info_mut().set_id(id);
storage.force_replace(id, value)
}
pub fn force_replace_with_error(&self, id: Id<T::Marker>, label: &str) {
@@ -155,6 +153,7 @@ impl<T: Resource> Registry<T> {
storage.insert_error(id, label);
}
pub(crate) fn unregister(&self, id: Id<T::Marker>) -> Option<Arc<T>> {
+ self.identity.free(id);
let value = self.storage.write().remove(id);
//Returning None is legal if it's an error ID
value
diff --git a/third_party/rust/wgpu-core/src/resource.rs b/third_party/rust/wgpu-core/src/resource.rs
index de5d1868a3..aca077caab 100644
--- a/third_party/rust/wgpu-core/src/resource.rs
+++ b/third_party/rust/wgpu-core/src/resource.rs
@@ -9,11 +9,10 @@ use crate::{
global::Global,
hal_api::HalApi,
id::{AdapterId, BufferId, DeviceId, Id, Marker, SurfaceId, TextureId},
- identity::IdentityManager,
init_tracker::{BufferInitTracker, TextureInitTracker},
resource, resource_log,
snatch::{ExclusiveSnatchGuard, SnatchGuard, Snatchable},
- track::TextureSelector,
+ track::{SharedTrackerIndexAllocator, TextureSelector, TrackerIndex},
validation::MissingBufferUsageError,
Label, SubmissionIndex,
};
@@ -31,7 +30,7 @@ use std::{
ops::Range,
ptr::NonNull,
sync::{
- atomic::{AtomicBool, AtomicUsize, Ordering},
+ atomic::{AtomicUsize, Ordering},
Arc, Weak,
},
};
@@ -58,7 +57,8 @@ use std::{
#[derive(Debug)]
pub struct ResourceInfo<T: Resource> {
id: Option<Id<T::Marker>>,
- identity: Option<Arc<IdentityManager<T::Marker>>>,
+ tracker_index: TrackerIndex,
+ tracker_indices: Option<Arc<SharedTrackerIndexAllocator>>,
/// The index of the last queue submission in which the resource
/// was used.
///
@@ -74,19 +74,26 @@ pub struct ResourceInfo<T: Resource> {
impl<T: Resource> Drop for ResourceInfo<T> {
fn drop(&mut self) {
- if let Some(identity) = self.identity.as_ref() {
- let id = self.id.as_ref().unwrap();
- identity.free(*id);
+ if let Some(indices) = &self.tracker_indices {
+ indices.free(self.tracker_index);
}
}
}
impl<T: Resource> ResourceInfo<T> {
#[allow(unused_variables)]
- pub(crate) fn new(label: &str) -> Self {
+ pub(crate) fn new(
+ label: &str,
+ tracker_indices: Option<Arc<SharedTrackerIndexAllocator>>,
+ ) -> Self {
+ let tracker_index = tracker_indices
+ .as_ref()
+ .map(|indices| indices.alloc())
+ .unwrap_or(TrackerIndex::INVALID);
Self {
id: None,
- identity: None,
+ tracker_index,
+ tracker_indices,
submission_index: AtomicUsize::new(0),
label: label.to_string(),
}
@@ -111,9 +118,13 @@ impl<T: Resource> ResourceInfo<T> {
self.id.unwrap()
}
- pub(crate) fn set_id(&mut self, id: Id<T::Marker>, identity: &Arc<IdentityManager<T::Marker>>) {
+ pub(crate) fn tracker_index(&self) -> TrackerIndex {
+ debug_assert!(self.tracker_index != TrackerIndex::INVALID);
+ self.tracker_index
+ }
+
+ pub(crate) fn set_id(&mut self, id: Id<T::Marker>) {
self.id = Some(id);
- self.identity = Some(identity.clone());
}
/// Record that this resource will be used by the queue submission with the
@@ -551,6 +562,7 @@ impl<A: HalApi> Buffer<A> {
device: Arc::clone(&self.device),
submission_index: self.info.submission_index(),
id: self.info.id.unwrap(),
+ tracker_index: self.info.tracker_index(),
label: self.info.label.clone(),
bind_groups,
}))
@@ -611,6 +623,7 @@ pub struct DestroyedBuffer<A: HalApi> {
device: Arc<Device<A>>,
label: String,
pub(crate) id: BufferId,
+ pub(crate) tracker_index: TrackerIndex,
pub(crate) submission_index: u64,
bind_groups: Vec<Weak<BindGroup<A>>>,
}
@@ -717,7 +730,6 @@ pub(crate) enum TextureInner<A: HalApi> {
Surface {
raw: Option<A::SurfaceTexture>,
parent_id: SurfaceId,
- has_work: AtomicBool,
},
}
@@ -886,6 +898,7 @@ impl<A: HalApi> Texture<A> {
views,
bind_groups,
device: Arc::clone(&self.device),
+ tracker_index: self.info.tracker_index(),
submission_index: self.info.submission_index(),
id: self.info.id.unwrap(),
label: self.info.label.clone(),
@@ -1003,6 +1016,7 @@ pub struct DestroyedTexture<A: HalApi> {
device: Arc<Device<A>>,
label: String,
pub(crate) id: TextureId,
+ pub(crate) tracker_index: TrackerIndex,
pub(crate) submission_index: u64,
}
diff --git a/third_party/rust/wgpu-core/src/track/buffer.rs b/third_party/rust/wgpu-core/src/track/buffer.rs
index 323d2dab9d..a30ac2a225 100644
--- a/third_party/rust/wgpu-core/src/track/buffer.rs
+++ b/third_party/rust/wgpu-core/src/track/buffer.rs
@@ -7,7 +7,7 @@
use std::{borrow::Cow, marker::PhantomData, sync::Arc};
-use super::{PendingTransition, ResourceTracker};
+use super::{PendingTransition, ResourceTracker, TrackerIndex};
use crate::{
hal_api::HalApi,
id::BufferId,
@@ -64,16 +64,16 @@ impl<A: HalApi> BufferBindGroupState<A> {
#[allow(clippy::pattern_type_mismatch)]
pub(crate) fn optimize(&self) {
let mut buffers = self.buffers.lock();
- buffers.sort_unstable_by_key(|(b, _)| b.as_info().id().unzip().0);
+ buffers.sort_unstable_by_key(|(b, _)| b.as_info().tracker_index());
}
/// Returns a list of all buffers tracked. May contain duplicates.
#[allow(clippy::pattern_type_mismatch)]
- pub fn used_ids(&self) -> impl Iterator<Item = BufferId> + '_ {
+ pub fn used_tracker_indices(&self) -> impl Iterator<Item = TrackerIndex> + '_ {
let buffers = self.buffers.lock();
buffers
.iter()
- .map(|(ref b, _)| b.as_info().id())
+ .map(|(ref b, _)| b.as_info().tracker_index())
.collect::<Vec<_>>()
.into_iter()
}
@@ -149,20 +149,6 @@ impl<A: HalApi> BufferUsageScope<A> {
resources.into_iter()
}
- pub fn get(&self, id: BufferId) -> Option<&Arc<Buffer<A>>> {
- let index = id.unzip().0 as usize;
- if index > self.metadata.size() {
- return None;
- }
- self.tracker_assert_in_bounds(index);
- unsafe {
- if self.metadata.contains_unchecked(index) {
- return Some(self.metadata.get_resource_unchecked(index));
- }
- }
- None
- }
-
/// Merge the list of buffer states in the given bind group into this usage scope.
///
/// If any of the resulting states is invalid, stops the merge and returns a usage
@@ -181,7 +167,7 @@ impl<A: HalApi> BufferUsageScope<A> {
) -> Result<(), UsageConflict> {
let buffers = bind_group.buffers.lock();
for &(ref resource, state) in &*buffers {
- let index = resource.as_info().id().unzip().0 as usize;
+ let index = resource.as_info().tracker_index().as_usize();
unsafe {
insert_or_merge(
@@ -255,7 +241,7 @@ impl<A: HalApi> BufferUsageScope<A> {
.get(id)
.map_err(|_| UsageConflict::BufferInvalid { id })?;
- let index = id.unzip().0 as usize;
+ let index = buffer.info.tracker_index().as_usize();
self.allow_index(index);
@@ -292,7 +278,7 @@ pub(crate) struct BufferTracker<A: HalApi> {
temp: Vec<PendingTransition<BufferUses>>,
}
-impl<A: HalApi> ResourceTracker<Buffer<A>> for BufferTracker<A> {
+impl<A: HalApi> ResourceTracker for BufferTracker<A> {
/// Try to remove the buffer `id` from this tracker if it is otherwise unused.
///
/// A buffer is 'otherwise unused' when the only references to it are:
@@ -313,8 +299,8 @@ impl<A: HalApi> ResourceTracker<Buffer<A>> for BufferTracker<A> {
/// [`Device::trackers`]: crate::device::Device
/// [`self.metadata`]: BufferTracker::metadata
/// [`Hub::buffers`]: crate::hub::Hub::buffers
- fn remove_abandoned(&mut self, id: BufferId) -> bool {
- let index = id.unzip().0 as usize;
+ fn remove_abandoned(&mut self, index: TrackerIndex) -> bool {
+ let index = index.as_usize();
if index > self.metadata.size() {
return false;
@@ -329,16 +315,10 @@ impl<A: HalApi> ResourceTracker<Buffer<A>> for BufferTracker<A> {
//so it's already been released from user and so it's not inside Registry\Storage
if existing_ref_count <= 2 {
self.metadata.remove(index);
- log::trace!("Buffer {:?} is not tracked anymore", id,);
return true;
- } else {
- log::trace!(
- "Buffer {:?} is still referenced from {}",
- id,
- existing_ref_count
- );
- return false;
}
+
+ return false;
}
}
true
@@ -404,8 +384,8 @@ impl<A: HalApi> BufferTracker<A> {
///
/// If the ID is higher than the length of internal vectors,
/// the vectors will be extended. A call to set_size is not needed.
- pub fn insert_single(&mut self, id: BufferId, resource: Arc<Buffer<A>>, state: BufferUses) {
- let index = id.unzip().0 as usize;
+ pub fn insert_single(&mut self, resource: Arc<Buffer<A>>, state: BufferUses) {
+ let index = resource.info.tracker_index().as_usize();
self.allow_index(index);
@@ -440,7 +420,7 @@ impl<A: HalApi> BufferTracker<A> {
/// If the ID is higher than the length of internal vectors,
/// the vectors will be extended. A call to set_size is not needed.
pub fn set_single(&mut self, buffer: &Arc<Buffer<A>>, state: BufferUses) -> SetSingleResult<A> {
- let index: usize = buffer.as_info().id().unzip().0 as usize;
+ let index: usize = buffer.as_info().tracker_index().as_usize();
self.allow_index(index);
@@ -561,16 +541,15 @@ impl<A: HalApi> BufferTracker<A> {
pub unsafe fn set_and_remove_from_usage_scope_sparse(
&mut self,
scope: &mut BufferUsageScope<A>,
- id_source: impl IntoIterator<Item = BufferId>,
+ index_source: impl IntoIterator<Item = TrackerIndex>,
) {
let incoming_size = scope.state.len();
if incoming_size > self.start.len() {
self.set_size(incoming_size);
}
- for id in id_source {
- let (index32, _, _) = id.unzip();
- let index = index32 as usize;
+ for index in index_source {
+ let index = index.as_usize();
scope.tracker_assert_in_bounds(index);
@@ -599,8 +578,8 @@ impl<A: HalApi> BufferTracker<A> {
}
#[allow(dead_code)]
- pub fn get(&self, id: BufferId) -> Option<&Arc<Buffer<A>>> {
- let index = id.unzip().0 as usize;
+ pub fn get(&self, index: TrackerIndex) -> Option<&Arc<Buffer<A>>> {
+ let index = index.as_usize();
if index > self.metadata.size() {
return None;
}
@@ -785,11 +764,7 @@ unsafe fn merge<A: HalApi>(
if invalid_resource_state(merged_state) {
return Err(UsageConflict::from_buffer(
- BufferId::zip(
- index32,
- unsafe { metadata_provider.get_epoch(index) },
- A::VARIANT,
- ),
+ unsafe { metadata_provider.get_own(index).info.id() },
*current_state,
new_state,
));
diff --git a/third_party/rust/wgpu-core/src/track/metadata.rs b/third_party/rust/wgpu-core/src/track/metadata.rs
index e5f4d5e969..744783a7fa 100644
--- a/third_party/rust/wgpu-core/src/track/metadata.rs
+++ b/third_party/rust/wgpu-core/src/track/metadata.rs
@@ -1,6 +1,6 @@
//! The `ResourceMetadata` type.
-use crate::{resource::Resource, Epoch};
+use crate::resource::Resource;
use bit_vec::BitVec;
use std::{borrow::Cow, mem, sync::Arc};
use wgt::strict_assert;
@@ -194,15 +194,6 @@ impl<T: Resource> ResourceMetadataProvider<'_, T> {
}
}
}
- /// Get the epoch from this.
- ///
- /// # Safety
- ///
- /// - The index must be in bounds of the metadata tracker if this uses an indirect source.
- #[inline(always)]
- pub(super) unsafe fn get_epoch(self, index: usize) -> Epoch {
- unsafe { self.get_own(index).as_info().id().unzip().1 }
- }
}
/// Resizes the given bitvec to the given size. I'm not sure why this is hard to do but it is.
diff --git a/third_party/rust/wgpu-core/src/track/mod.rs b/third_party/rust/wgpu-core/src/track/mod.rs
index a36280d03b..9ca37ebadc 100644
--- a/third_party/rust/wgpu-core/src/track/mod.rs
+++ b/third_party/rust/wgpu-core/src/track/mod.rs
@@ -102,16 +102,11 @@ mod stateless;
mod texture;
use crate::{
- binding_model, command, conv,
- hal_api::HalApi,
- id::{self, Id},
- pipeline, resource,
- snatch::SnatchGuard,
- storage::Storage,
+ binding_model, command, conv, hal_api::HalApi, id, pipeline, resource, snatch::SnatchGuard,
};
-use parking_lot::RwLock;
-use std::{fmt, ops};
+use parking_lot::{Mutex, RwLock};
+use std::{fmt, ops, sync::Arc};
use thiserror::Error;
pub(crate) use buffer::{BufferBindGroupState, BufferTracker, BufferUsageScope};
@@ -122,6 +117,130 @@ pub(crate) use texture::{
};
use wgt::strict_assert_ne;
+#[repr(transparent)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
+pub(crate) struct TrackerIndex(u32);
+
+impl TrackerIndex {
+ /// A dummy value to place in ResourceInfo for resources that are never tracked.
+ pub const INVALID: Self = TrackerIndex(u32::MAX);
+
+ pub fn as_usize(self) -> usize {
+ debug_assert!(self != Self::INVALID);
+ self.0 as usize
+ }
+}
+
+/// wgpu-core internally use some array-like storage for tracking resources.
+/// To that end, there needs to be a uniquely assigned index for each live resource
+/// of a certain type. This index is separate from the resource ID for various reasons:
+/// - There can be multiple resource IDs pointing the the same resource.
+/// - IDs of dead handles can be recycled while resources are internally held alive (and tracked).
+/// - The plan is to remove IDs in the long run (https://github.com/gfx-rs/wgpu/issues/5121).
+/// In order to produce these tracker indices, there is a shared TrackerIndexAllocator
+/// per resource type. Indices have the same lifetime as the internal resource they
+/// are associated to (alloc happens when creating the resource and free is called when
+/// the resource is dropped).
+struct TrackerIndexAllocator {
+ unused: Vec<TrackerIndex>,
+ next_index: TrackerIndex,
+}
+
+impl TrackerIndexAllocator {
+ pub fn new() -> Self {
+ TrackerIndexAllocator {
+ unused: Vec::new(),
+ next_index: TrackerIndex(0),
+ }
+ }
+
+ pub fn alloc(&mut self) -> TrackerIndex {
+ if let Some(index) = self.unused.pop() {
+ return index;
+ }
+
+ let index = self.next_index;
+ self.next_index.0 += 1;
+
+ index
+ }
+
+ pub fn free(&mut self, index: TrackerIndex) {
+ self.unused.push(index);
+ }
+
+ // This is used to pre-allocate the tracker storage.
+ pub fn size(&self) -> usize {
+ self.next_index.0 as usize
+ }
+}
+
+impl std::fmt::Debug for TrackerIndexAllocator {
+ fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
+ Ok(())
+ }
+}
+
+/// See TrackerIndexAllocator.
+#[derive(Debug)]
+pub(crate) struct SharedTrackerIndexAllocator {
+ inner: Mutex<TrackerIndexAllocator>,
+}
+
+impl SharedTrackerIndexAllocator {
+ pub fn new() -> Self {
+ SharedTrackerIndexAllocator {
+ inner: Mutex::new(TrackerIndexAllocator::new()),
+ }
+ }
+
+ pub fn alloc(&self) -> TrackerIndex {
+ self.inner.lock().alloc()
+ }
+
+ pub fn free(&self, index: TrackerIndex) {
+ self.inner.lock().free(index);
+ }
+
+ pub fn size(&self) -> usize {
+ self.inner.lock().size()
+ }
+}
+
+pub(crate) struct TrackerIndexAllocators {
+ pub buffers: Arc<SharedTrackerIndexAllocator>,
+ pub staging_buffers: Arc<SharedTrackerIndexAllocator>,
+ pub textures: Arc<SharedTrackerIndexAllocator>,
+ pub texture_views: Arc<SharedTrackerIndexAllocator>,
+ pub samplers: Arc<SharedTrackerIndexAllocator>,
+ pub bind_groups: Arc<SharedTrackerIndexAllocator>,
+ pub bind_group_layouts: Arc<SharedTrackerIndexAllocator>,
+ pub compute_pipelines: Arc<SharedTrackerIndexAllocator>,
+ pub render_pipelines: Arc<SharedTrackerIndexAllocator>,
+ pub pipeline_layouts: Arc<SharedTrackerIndexAllocator>,
+ pub bundles: Arc<SharedTrackerIndexAllocator>,
+ pub query_sets: Arc<SharedTrackerIndexAllocator>,
+}
+
+impl TrackerIndexAllocators {
+ pub fn new() -> Self {
+ TrackerIndexAllocators {
+ buffers: Arc::new(SharedTrackerIndexAllocator::new()),
+ staging_buffers: Arc::new(SharedTrackerIndexAllocator::new()),
+ textures: Arc::new(SharedTrackerIndexAllocator::new()),
+ texture_views: Arc::new(SharedTrackerIndexAllocator::new()),
+ samplers: Arc::new(SharedTrackerIndexAllocator::new()),
+ bind_groups: Arc::new(SharedTrackerIndexAllocator::new()),
+ bind_group_layouts: Arc::new(SharedTrackerIndexAllocator::new()),
+ compute_pipelines: Arc::new(SharedTrackerIndexAllocator::new()),
+ render_pipelines: Arc::new(SharedTrackerIndexAllocator::new()),
+ pipeline_layouts: Arc::new(SharedTrackerIndexAllocator::new()),
+ bundles: Arc::new(SharedTrackerIndexAllocator::new()),
+ query_sets: Arc::new(SharedTrackerIndexAllocator::new()),
+ }
+ }
+}
+
/// A structure containing all the information about a particular resource
/// transition. User code should be able to generate a pipeline barrier
/// based on the contents.
@@ -359,31 +478,14 @@ pub(crate) struct RenderBundleScope<A: HalApi> {
impl<A: HalApi> RenderBundleScope<A> {
/// Create the render bundle scope and pull the maximum IDs from the hubs.
- pub fn new(
- buffers: &Storage<resource::Buffer<A>>,
- textures: &Storage<resource::Texture<A>>,
- bind_groups: &Storage<binding_model::BindGroup<A>>,
- render_pipelines: &Storage<pipeline::RenderPipeline<A>>,
- query_sets: &Storage<resource::QuerySet<A>>,
- ) -> Self {
- let value = Self {
+ pub fn new() -> Self {
+ Self {
buffers: RwLock::new(BufferUsageScope::new()),
textures: RwLock::new(TextureUsageScope::new()),
bind_groups: RwLock::new(StatelessTracker::new()),
render_pipelines: RwLock::new(StatelessTracker::new()),
query_sets: RwLock::new(StatelessTracker::new()),
- };
-
- value.buffers.write().set_size(buffers.len());
- value.textures.write().set_size(textures.len());
- value.bind_groups.write().set_size(bind_groups.len());
- value
- .render_pipelines
- .write()
- .set_size(render_pipelines.len());
- value.query_sets.write().set_size(query_sets.len());
-
- value
+ }
}
/// Merge the inner contents of a bind group into the render bundle tracker.
@@ -420,17 +522,14 @@ pub(crate) struct UsageScope<A: HalApi> {
impl<A: HalApi> UsageScope<A> {
/// Create the render bundle scope and pull the maximum IDs from the hubs.
- pub fn new(
- buffers: &Storage<resource::Buffer<A>>,
- textures: &Storage<resource::Texture<A>>,
- ) -> Self {
+ pub fn new(tracker_indices: &TrackerIndexAllocators) -> Self {
let mut value = Self {
buffers: BufferUsageScope::new(),
textures: TextureUsageScope::new(),
};
- value.buffers.set_size(buffers.len());
- value.textures.set_size(textures.len());
+ value.buffers.set_size(tracker_indices.buffers.size());
+ value.textures.set_size(tracker_indices.textures.size());
value
}
@@ -478,11 +577,8 @@ impl<A: HalApi> UsageScope<A> {
}
}
-pub(crate) trait ResourceTracker<R>
-where
- R: resource::Resource,
-{
- fn remove_abandoned(&mut self, id: Id<R::Marker>) -> bool;
+pub(crate) trait ResourceTracker {
+ fn remove_abandoned(&mut self, index: TrackerIndex) -> bool;
}
/// A full double sided tracker used by CommandBuffers and the Device.
@@ -513,48 +609,6 @@ impl<A: HalApi> Tracker<A> {
}
}
- /// Pull the maximum IDs from the hubs.
- pub fn set_size(
- &mut self,
- buffers: Option<&Storage<resource::Buffer<A>>>,
- textures: Option<&Storage<resource::Texture<A>>>,
- views: Option<&Storage<resource::TextureView<A>>>,
- samplers: Option<&Storage<resource::Sampler<A>>>,
- bind_groups: Option<&Storage<binding_model::BindGroup<A>>>,
- compute_pipelines: Option<&Storage<pipeline::ComputePipeline<A>>>,
- render_pipelines: Option<&Storage<pipeline::RenderPipeline<A>>>,
- bundles: Option<&Storage<command::RenderBundle<A>>>,
- query_sets: Option<&Storage<resource::QuerySet<A>>>,
- ) {
- if let Some(buffers) = buffers {
- self.buffers.set_size(buffers.len());
- };
- if let Some(textures) = textures {
- self.textures.set_size(textures.len());
- };
- if let Some(views) = views {
- self.views.set_size(views.len());
- };
- if let Some(samplers) = samplers {
- self.samplers.set_size(samplers.len());
- };
- if let Some(bind_groups) = bind_groups {
- self.bind_groups.set_size(bind_groups.len());
- };
- if let Some(compute_pipelines) = compute_pipelines {
- self.compute_pipelines.set_size(compute_pipelines.len());
- }
- if let Some(render_pipelines) = render_pipelines {
- self.render_pipelines.set_size(render_pipelines.len());
- };
- if let Some(bundles) = bundles {
- self.bundles.set_size(bundles.len());
- };
- if let Some(query_sets) = query_sets {
- self.query_sets.set_size(query_sets.len());
- };
- }
-
/// Iterates through all resources in the given bind group and adopts
/// the state given for those resources in the UsageScope. It also
/// removes all touched resources from the usage scope.
@@ -585,7 +639,7 @@ impl<A: HalApi> Tracker<A> {
unsafe {
self.buffers.set_and_remove_from_usage_scope_sparse(
&mut scope.buffers,
- bind_group.buffers.used_ids(),
+ bind_group.buffers.used_tracker_indices(),
)
};
unsafe {
diff --git a/third_party/rust/wgpu-core/src/track/stateless.rs b/third_party/rust/wgpu-core/src/track/stateless.rs
index 4111a90f79..00225f2305 100644
--- a/third_party/rust/wgpu-core/src/track/stateless.rs
+++ b/third_party/rust/wgpu-core/src/track/stateless.rs
@@ -10,7 +10,7 @@ use parking_lot::Mutex;
use crate::{id::Id, resource::Resource, resource_log, storage::Storage, track::ResourceMetadata};
-use super::ResourceTracker;
+use super::{ResourceTracker, TrackerIndex};
/// Satisfy clippy.
type Pair<T> = (Id<<T as Resource>::Marker>, Arc<T>);
@@ -74,7 +74,7 @@ pub(crate) struct StatelessTracker<T: Resource> {
metadata: ResourceMetadata<T>,
}
-impl<T: Resource> ResourceTracker<T> for StatelessTracker<T> {
+impl<T: Resource> ResourceTracker for StatelessTracker<T> {
/// Try to remove the given resource from the tracker iff we have the last reference to the
/// resource and the epoch matches.
///
@@ -82,14 +82,14 @@ impl<T: Resource> ResourceTracker<T> for StatelessTracker<T> {
///
/// If the ID is higher than the length of internal vectors,
/// false will be returned.
- fn remove_abandoned(&mut self, id: Id<T::Marker>) -> bool {
- let index = id.unzip().0 as usize;
+ fn remove_abandoned(&mut self, index: TrackerIndex) -> bool {
+ let index = index.as_usize();
if index >= self.metadata.size() {
return false;
}
- resource_log!("StatelessTracker::remove_abandoned {id:?}");
+ resource_log!("StatelessTracker::remove_abandoned {index:?}");
self.tracker_assert_in_bounds(index);
@@ -100,17 +100,10 @@ impl<T: Resource> ResourceTracker<T> for StatelessTracker<T> {
//so it's already been released from user and so it's not inside Registry\Storage
if existing_ref_count <= 2 {
self.metadata.remove(index);
- log::trace!("{} {:?} is not tracked anymore", T::TYPE, id,);
return true;
- } else {
- log::trace!(
- "{} {:?} is still referenced from {}",
- T::TYPE,
- id,
- existing_ref_count
- );
- return false;
}
+
+ return false;
}
}
true
@@ -160,9 +153,8 @@ impl<T: Resource> StatelessTracker<T> {
///
/// If the ID is higher than the length of internal vectors,
/// the vectors will be extended. A call to set_size is not needed.
- pub fn insert_single(&mut self, id: Id<T::Marker>, resource: Arc<T>) {
- let (index32, _epoch, _) = id.unzip();
- let index = index32 as usize;
+ pub fn insert_single(&mut self, resource: Arc<T>) {
+ let index = resource.as_info().tracker_index().as_usize();
self.allow_index(index);
@@ -184,8 +176,7 @@ impl<T: Resource> StatelessTracker<T> {
) -> Option<&'a Arc<T>> {
let resource = storage.get(id).ok()?;
- let (index32, _epoch, _) = id.unzip();
- let index = index32 as usize;
+ let index = resource.as_info().tracker_index().as_usize();
self.allow_index(index);
@@ -221,18 +212,4 @@ impl<T: Resource> StatelessTracker<T> {
}
}
}
-
- pub fn get(&self, id: Id<T::Marker>) -> Option<&Arc<T>> {
- let index = id.unzip().0 as usize;
- if index > self.metadata.size() {
- return None;
- }
- self.tracker_assert_in_bounds(index);
- unsafe {
- if self.metadata.contains_unchecked(index) {
- return Some(self.metadata.get_resource_unchecked(index));
- }
- }
- None
- }
}
diff --git a/third_party/rust/wgpu-core/src/track/texture.rs b/third_party/rust/wgpu-core/src/track/texture.rs
index 601df11e1b..e7c4707c93 100644
--- a/third_party/rust/wgpu-core/src/track/texture.rs
+++ b/third_party/rust/wgpu-core/src/track/texture.rs
@@ -19,10 +19,11 @@
* will treat the contents as junk.
!*/
-use super::{range::RangedStates, PendingTransition, PendingTransitionList, ResourceTracker};
+use super::{
+ range::RangedStates, PendingTransition, PendingTransitionList, ResourceTracker, TrackerIndex,
+};
use crate::{
hal_api::HalApi,
- id::TextureId,
resource::{Resource, Texture, TextureInner},
snatch::SnatchGuard,
track::{
@@ -173,7 +174,7 @@ impl<A: HalApi> TextureBindGroupState<A> {
/// accesses will be in a constant ascending order.
pub(crate) fn optimize(&self) {
let mut textures = self.textures.lock();
- textures.sort_unstable_by_key(|v| v.texture.as_info().id().unzip().0);
+ textures.sort_unstable_by_key(|v| v.texture.as_info().tracker_index());
}
/// Returns a list of all textures tracked. May contain duplicates.
@@ -359,7 +360,7 @@ impl<A: HalApi> TextureUsageScope<A> {
selector: Option<TextureSelector>,
new_state: TextureUses,
) -> Result<(), UsageConflict> {
- let index = texture.as_info().id().unzip().0 as usize;
+ let index = texture.as_info().tracker_index().as_usize();
self.tracker_assert_in_bounds(index);
@@ -393,7 +394,7 @@ pub(crate) struct TextureTracker<A: HalApi> {
_phantom: PhantomData<A>,
}
-impl<A: HalApi> ResourceTracker<Texture<A>> for TextureTracker<A> {
+impl<A: HalApi> ResourceTracker for TextureTracker<A> {
/// Try to remove the given resource from the tracker iff we have the last reference to the
/// resource and the epoch matches.
///
@@ -401,10 +402,10 @@ impl<A: HalApi> ResourceTracker<Texture<A>> for TextureTracker<A> {
///
/// If the ID is higher than the length of internal vectors,
/// false will be returned.
- fn remove_abandoned(&mut self, id: TextureId) -> bool {
- let index = id.unzip().0 as usize;
+ fn remove_abandoned(&mut self, index: TrackerIndex) -> bool {
+ let index = index.as_usize();
- if index > self.metadata.size() {
+ if index >= self.metadata.size() {
return false;
}
@@ -419,16 +420,10 @@ impl<A: HalApi> ResourceTracker<Texture<A>> for TextureTracker<A> {
self.start_set.complex.remove(&index);
self.end_set.complex.remove(&index);
self.metadata.remove(index);
- log::trace!("Texture {:?} is not tracked anymore", id,);
return true;
- } else {
- log::trace!(
- "Texture {:?} is still referenced from {}",
- id,
- existing_ref_count
- );
- return false;
}
+
+ return false;
}
}
true
@@ -518,8 +513,8 @@ impl<A: HalApi> TextureTracker<A> {
///
/// If the ID is higher than the length of internal vectors,
/// the vectors will be extended. A call to set_size is not needed.
- pub fn insert_single(&mut self, id: TextureId, resource: Arc<Texture<A>>, usage: TextureUses) {
- let index = id.unzip().0 as usize;
+ pub fn insert_single(&mut self, resource: Arc<Texture<A>>, usage: TextureUses) {
+ let index = resource.info.tracker_index().as_usize();
self.allow_index(index);
@@ -560,7 +555,7 @@ impl<A: HalApi> TextureTracker<A> {
selector: TextureSelector,
new_state: TextureUses,
) -> Option<Drain<'_, PendingTransition<TextureUses>>> {
- let index = texture.as_info().id().unzip().0 as usize;
+ let index = texture.as_info().tracker_index().as_usize();
self.allow_index(index);
@@ -694,7 +689,7 @@ impl<A: HalApi> TextureTracker<A> {
let textures = bind_group_state.textures.lock();
for t in textures.iter() {
- let index = t.texture.as_info().id().unzip().0 as usize;
+ let index = t.texture.as_info().tracker_index().as_usize();
scope.tracker_assert_in_bounds(index);
if unsafe { !scope.metadata.contains_unchecked(index) } {
@@ -727,10 +722,10 @@ impl<A: HalApi> TextureTracker<A> {
///
/// If the ID is higher than the length of internal vectors,
/// false will be returned.
- pub fn remove(&mut self, id: TextureId) -> bool {
- let index = id.unzip().0 as usize;
+ pub fn remove(&mut self, index: TrackerIndex) -> bool {
+ let index = index.as_usize();
- if index > self.metadata.size() {
+ if index >= self.metadata.size() {
return false;
}
@@ -1080,11 +1075,7 @@ unsafe fn merge<A: HalApi>(
if invalid_resource_state(merged_state) {
return Err(UsageConflict::from_texture(
- TextureId::zip(
- index as _,
- unsafe { metadata_provider.get_epoch(index) },
- A::VARIANT,
- ),
+ unsafe { metadata_provider.get_own(index).info.id() },
texture_selector.clone(),
*current_simple,
new_simple,
@@ -1111,11 +1102,7 @@ unsafe fn merge<A: HalApi>(
if invalid_resource_state(merged_state) {
return Err(UsageConflict::from_texture(
- TextureId::zip(
- index as _,
- unsafe { metadata_provider.get_epoch(index) },
- A::VARIANT,
- ),
+ unsafe { metadata_provider.get_own(index).info.id() },
selector,
*current_simple,
new_state,
@@ -1156,11 +1143,7 @@ unsafe fn merge<A: HalApi>(
if invalid_resource_state(merged_state) {
return Err(UsageConflict::from_texture(
- TextureId::zip(
- index as _,
- unsafe { metadata_provider.get_epoch(index) },
- A::VARIANT,
- ),
+ unsafe { metadata_provider.get_own(index).info.id() },
TextureSelector {
mips: mip_id..mip_id + 1,
layers: layers.clone(),
@@ -1201,11 +1184,7 @@ unsafe fn merge<A: HalApi>(
if invalid_resource_state(merged_state) {
return Err(UsageConflict::from_texture(
- TextureId::zip(
- index as _,
- unsafe { metadata_provider.get_epoch(index) },
- A::VARIANT,
- ),
+ unsafe { metadata_provider.get_own(index).info.id() },
TextureSelector {
mips: mip_id..mip_id + 1,
layers: layers.clone(),
diff --git a/third_party/rust/wgpu-core/src/validation.rs b/third_party/rust/wgpu-core/src/validation.rs
index a0947ae83f..e4846c4000 100644
--- a/third_party/rust/wgpu-core/src/validation.rs
+++ b/third_party/rust/wgpu-core/src/validation.rs
@@ -1,4 +1,8 @@
-use crate::{device::bgl, FastHashMap, FastHashSet};
+use crate::{
+ device::bgl,
+ id::{markers::Buffer, Id},
+ FastHashMap, FastHashSet,
+};
use arrayvec::ArrayVec;
use std::{collections::hash_map::Entry, fmt};
use thiserror::Error;
@@ -134,8 +138,11 @@ pub struct Interface {
}
#[derive(Clone, Debug, Error)]
-#[error("Buffer usage is {actual:?} which does not contain required usage {expected:?}")]
+#[error(
+ "Usage flags {actual:?} for buffer {id:?} do not contain required usage flags {expected:?}"
+)]
pub struct MissingBufferUsageError {
+ pub(crate) id: Id<Buffer>,
pub(crate) actual: wgt::BufferUsages,
pub(crate) expected: wgt::BufferUsages,
}
@@ -143,11 +150,16 @@ pub struct MissingBufferUsageError {
/// Checks that the given buffer usage contains the required buffer usage,
/// returns an error otherwise.
pub fn check_buffer_usage(
+ id: Id<Buffer>,
actual: wgt::BufferUsages,
expected: wgt::BufferUsages,
) -> Result<(), MissingBufferUsageError> {
if !actual.contains(expected) {
- Err(MissingBufferUsageError { actual, expected })
+ Err(MissingBufferUsageError {
+ id,
+ actual,
+ expected,
+ })
} else {
Ok(())
}
@@ -271,6 +283,16 @@ pub enum StageError {
},
#[error("Location[{location}] is provided by the previous stage output but is not consumed as input by this stage.")]
InputNotConsumed { location: wgt::ShaderLocation },
+ #[error(
+ "Unable to select an entry point: no entry point was found in the provided shader module"
+ )]
+ NoEntryPointFound,
+ #[error(
+ "Unable to select an entry point: \
+ multiple entry points were found in the provided shader module, \
+ but no entry point was specified"
+ )]
+ MultipleEntryPointsFound,
}
fn map_storage_format_to_naga(format: wgt::TextureFormat) -> Option<naga::StorageFormat> {
@@ -892,9 +914,15 @@ impl Interface {
class,
},
naga::TypeInner::Sampler { comparison } => ResourceType::Sampler { comparison },
- naga::TypeInner::Array { stride, .. } => ResourceType::Buffer {
- size: wgt::BufferSize::new(stride as u64).unwrap(),
- },
+ naga::TypeInner::Array { stride, size, .. } => {
+ let size = match size {
+ naga::ArraySize::Constant(size) => size.get() * stride,
+ naga::ArraySize::Dynamic => stride,
+ };
+ ResourceType::Buffer {
+ size: wgt::BufferSize::new(size as u64).unwrap(),
+ }
+ }
ref other => ResourceType::Buffer {
size: wgt::BufferSize::new(other.size(module.to_ctx()) as u64).unwrap(),
},
@@ -953,6 +981,37 @@ impl Interface {
}
}
+ pub fn finalize_entry_point_name(
+ &self,
+ stage_bit: wgt::ShaderStages,
+ entry_point_name: Option<&str>,
+ ) -> Result<String, StageError> {
+ let stage = Self::shader_stage_from_stage_bit(stage_bit);
+ entry_point_name
+ .map(|ep| ep.to_string())
+ .map(Ok)
+ .unwrap_or_else(|| {
+ let mut entry_points = self
+ .entry_points
+ .keys()
+ .filter_map(|(ep_stage, name)| (ep_stage == &stage).then_some(name));
+ let first = entry_points.next().ok_or(StageError::NoEntryPointFound)?;
+ if entry_points.next().is_some() {
+ return Err(StageError::MultipleEntryPointsFound);
+ }
+ Ok(first.clone())
+ })
+ }
+
+ pub(crate) fn shader_stage_from_stage_bit(stage_bit: wgt::ShaderStages) -> naga::ShaderStage {
+ match stage_bit {
+ wgt::ShaderStages::VERTEX => naga::ShaderStage::Vertex,
+ wgt::ShaderStages::FRAGMENT => naga::ShaderStage::Fragment,
+ wgt::ShaderStages::COMPUTE => naga::ShaderStage::Compute,
+ _ => unreachable!(),
+ }
+ }
+
pub fn check_stage(
&self,
layouts: &mut BindingLayoutSource<'_>,
@@ -964,17 +1023,13 @@ impl Interface {
) -> Result<StageIo, StageError> {
// Since a shader module can have multiple entry points with the same name,
// we need to look for one with the right execution model.
- let shader_stage = match stage_bit {
- wgt::ShaderStages::VERTEX => naga::ShaderStage::Vertex,
- wgt::ShaderStages::FRAGMENT => naga::ShaderStage::Fragment,
- wgt::ShaderStages::COMPUTE => naga::ShaderStage::Compute,
- _ => unreachable!(),
- };
+ let shader_stage = Self::shader_stage_from_stage_bit(stage_bit);
let pair = (shader_stage, entry_point_name.to_string());
- let entry_point = self
- .entry_points
- .get(&pair)
- .ok_or(StageError::MissingEntryPoint(pair.1))?;
+ let entry_point = match self.entry_points.get(&pair) {
+ Some(some) => some,
+ None => return Err(StageError::MissingEntryPoint(pair.1)),
+ };
+ let (_stage, entry_point_name) = pair;
// check resources visibility
for &handle in entry_point.resources.iter() {
@@ -1246,3 +1301,31 @@ impl Interface {
.map(|ep| ep.dual_source_blending)
}
}
+
+// https://gpuweb.github.io/gpuweb/#abstract-opdef-calculating-color-attachment-bytes-per-sample
+pub fn validate_color_attachment_bytes_per_sample(
+ attachment_formats: impl Iterator<Item = Option<wgt::TextureFormat>>,
+ limit: u32,
+) -> Result<(), u32> {
+ let mut total_bytes_per_sample = 0;
+ for format in attachment_formats {
+ let Some(format) = format else {
+ continue;
+ };
+
+ let byte_cost = format.target_pixel_byte_cost().unwrap();
+ let alignment = format.target_component_alignment().unwrap();
+
+ let rem = total_bytes_per_sample % alignment;
+ if rem != 0 {
+ total_bytes_per_sample += alignment - rem;
+ }
+ total_bytes_per_sample += byte_cost;
+ }
+
+ if total_bytes_per_sample > limit {
+ return Err(total_bytes_per_sample);
+ }
+
+ Ok(())
+}
diff --git a/third_party/rust/wgpu-hal/.cargo-checksum.json b/third_party/rust/wgpu-hal/.cargo-checksum.json
index 65fb8499cb..de9bc38719 100644
--- a/third_party/rust/wgpu-hal/.cargo-checksum.json
+++ b/third_party/rust/wgpu-hal/.cargo-checksum.json
@@ -1 +1 @@
-{"files":{"Cargo.toml":"24691ea259ad568f29d8b25dff9720f0f30dbaaf47df1b7c223bb2e1dc2a943f","LICENSE.APACHE":"a6cba85bc92e0cff7a450b1d873c0eaa2e9fc96bf472df0247a26bec77bf3ff9","LICENSE.MIT":"c7fea58d1cfe49634cd92e54fc10a9d871f4b275321a4cd8c09e449122caaeb4","README.md":"099ee611a911dc19330a61bffcde13663929a51b25ac528ee33ea796d695491e","build.rs":"c80bdc0152a00471eec6ed0dd0f7d55d0b975498a00ba05e94100c84ad639a49","examples/halmark/main.rs":"4604737f714943383c57feac2b8468ecf15e9e60c54a5303455e9953ec5c79fb","examples/halmark/shader.wgsl":"26c256ec36d6f0e9a1647431ca772766bee4382d64eaa718ba7b488dcfb6bcca","examples/raw-gles.em.html":"70fbe68394a1a4522192de1dcfaf7d399f60d7bdf5de70b708f9bb0417427546","examples/raw-gles.rs":"095113a1ba0851652a77aabfc8fa6ea7edcc2d09e91fd1e5009ead87d5998ea9","examples/ray-traced-triangle/main.rs":"955c2b8700c3b2daf14e9ef963ff499ed185b6f349dbc63caa422b2cf4942a1f","examples/ray-traced-triangle/shader.wgsl":"cc10caf92746724a71f6dd0dbc3a71e57b37c7d1d83278556805a535c0728a9d","src/auxil/dxgi/conv.rs":"760cd4eaa79b530368a30140b96bf73ac4fbdb4025eb95f0bed581638c8bb1cb","src/auxil/dxgi/exception.rs":"f0cfb5a0adcdc3b6db909601fee51ad51368f5da269bcd46e4dbea45a3bec4b1","src/auxil/dxgi/factory.rs":"5f861fbfe2f4cce08722a95283549b8f62b96f24a306d080d9f1730ae53501d8","src/auxil/dxgi/mod.rs":"a202564d9ac97530b16a234b87d180cd345aae705e082a9b1177dcde813645f9","src/auxil/dxgi/result.rs":"20c8eb03d738062dff198feca6327addb9882ed0462be842c789eadf7dca0573","src/auxil/dxgi/time.rs":"b6f966b250e9424d5d7e4065f2108cba87197c1e30baae6d87083055d1bc5a4b","src/auxil/mod.rs":"720ef2aae258733322a3274fd858f91effb8951dabaf7bbfd8a9a0be2d2dba97","src/auxil/renderdoc.rs":"c2f849f70f576b0c9b0d32dd155b6a6353f74dff59cbeeaa994a12789d047c0f","src/dx12/adapter.rs":"5143d009ab75950df6f6e311ca07108dedd373b99029d0eac9b882e4880893ea","src/dx12/command.rs":"bb3cc2ff1e77c0e4434eef8cab57e9018a1d00738fda71b860cdfc4fe802c0a4","src/dx12/conv.rs":"94d35f117ae003b07049f3a0bc6c45a0ffda9fb8053233d39c173cfb1b644403","src/dx12/descriptor.rs":"e06eb08bee4c805fa76b6ab791893b5b563ee60de9c8f8d8e0e21ab97ade5664","src/dx12/device.rs":"2a72beac1496b1682700e07923e9ad6ce7271e5a88641bf6c6d0b9b893b46cd9","src/dx12/instance.rs":"351a4e0d526de8eafc74bf5f01a41da48efa39e0c66704a85da72e1140b159d4","src/dx12/mod.rs":"4ec20d1082f10c7429db0fcdc6261210a0ff1565e87f4ab799719dc00aa636e0","src/dx12/shader_compilation.rs":"419ce7fe4df2973845851fac045dab21157eec6b26a573012f22fa41fc130b5b","src/dx12/suballocation.rs":"6939fc36223a15cc070c744d0418f9ac6fa2829d794af17cdea7c61eb5f8d2c0","src/dx12/types.rs":"9573736baaa0ef607367c3b72144556d24faf677a26bb8df49a4372a1348e06b","src/dx12/view.rs":"792772e9c87840dcd045b7381a03162eb4a501492a95ca586e77e81aed621c67","src/empty.rs":"5c3a5e39d45b4522ff3496fe6ec3b4a7afd906b6095dff1cad113c826aa9ea62","src/gles/adapter.rs":"05dd64c42b8b8265cfa1913dfdb9d1d7730abc05d189ed48bb0aa190debd90f6","src/gles/command.rs":"7118e42376e403e0d13db007534529d0e0650ff938a327cbdb0d6c90bee876de","src/gles/conv.rs":"5d15d3a33032d32ff99bc338fba0689fa54c76d0714e335fe48523d841df386f","src/gles/device.rs":"087fcfaf796b3fba2e6d638bb9840df941dd89aae43fcd8f528baf7b9ad9bd05","src/gles/egl.rs":"5ae9499e56f48ebe1797533c091529e77494ef69e32ea23e08e9135ba63188d1","src/gles/emscripten.rs":"19bb73a9d140645f3f32cd48b002151711a9b8456e213eab5f3a2be79239e147","src/gles/mod.rs":"772cf714874d12d815f2b0cf3309fd970545c582e8c2bc56eb1b266b013f5afb","src/gles/queue.rs":"9159af1636e838462ec562f25bbcacd15bc0a7e63606a3352e04f1f39818c61b","src/gles/shaders/clear.frag":"9133ed8ed97d3641fbb6b5f5ea894a3554c629ccc1b80a5fc9221d7293aa1954","src/gles/shaders/clear.vert":"a543768725f4121ff2e9e1fb5b00644931e9d6f2f946c0ef01968afb5a135abd","src/gles/shaders/srgb_present.frag":"dd9a43c339a2fa4ccf7f6a1854c6f400cabf271a7d5e9230768e9f39d47f3ff5","src/gles/shaders/srgb_present.vert":"6e85d489403d80b81cc94790730bb53b309dfc5eeede8f1ea3412a660f31d357","src/gles/web.rs":"d263695d45736d3c6ec3528c8c33fe6cf3767d3429a13a92d88b4fdc7b6340fb","src/gles/wgl.rs":"80351e261e2eaa47fff3ec4118d4ce781b24ab9a40072c8b3525baf09f041aca","src/lib.rs":"93873ebd663ed115a4bdd554eb5e33658658c89dd2fd4a5b33eda57417ab8d7d","src/metal/adapter.rs":"48747609f839dd9dbb5f6bc0a89f7f7017458e40dabc375efb07fbc93e36dfaa","src/metal/command.rs":"661b38a75d4f4cd1b0d6957f1f09db0743ec3a13bbafba9baa931894ee193f48","src/metal/conv.rs":"0bce6a8d0ccef16783475803d70d35e03ab7938c19374e22c9d253abe1f8b111","src/metal/device.rs":"c5deeecf475e0aa4b2027c656ea19207716f84b56cfa7c9132dca504d1abebfb","src/metal/mod.rs":"17665544754102ccf5f4bb1ccc0493ee8d2dbe45b22470bddaf9e609c24c0774","src/metal/surface.rs":"f2b9b65d4117db2b16c04469c573358eb65de104d5a72aa02da8483ee243cbd3","src/metal/time.rs":"c32d69f30e846dfcc0e39e01097fb80df63b2bebb6586143bb62494999850246","src/vulkan/adapter.rs":"a0f365b9d4fea8ec81b8f6211648a78ecf1e8442aaed3f41819b59ce1c66f05d","src/vulkan/command.rs":"e5a88eab59b3864cdf44ba2231270e16045505dc549b8b90251031de452ba826","src/vulkan/conv.rs":"7e6266e3a0b7d0b8d5d51362a0386a84bc047350eeac663b6352a94d5e5c0a87","src/vulkan/device.rs":"9824d597dbb51030bd337e80bb0f1eab6fdb6935fc87dfd8beae2c1f1048fbcf","src/vulkan/instance.rs":"fb583496865eb67b3997503ec58e8e2518fc88175aa3cc4c19b8022be267f1ec","src/vulkan/mod.rs":"5c873db859e740876e072bed752e76940dd97a35f3d532509a6357cb0fb9119b"},"package":null} \ No newline at end of file
+{"files":{"Cargo.toml":"eaa7b2b51fbe98c0721dc52d94c64b48d2d6e351bf36da3e756378a8d8ebc1de","LICENSE.APACHE":"a6cba85bc92e0cff7a450b1d873c0eaa2e9fc96bf472df0247a26bec77bf3ff9","LICENSE.MIT":"c7fea58d1cfe49634cd92e54fc10a9d871f4b275321a4cd8c09e449122caaeb4","README.md":"099ee611a911dc19330a61bffcde13663929a51b25ac528ee33ea796d695491e","build.rs":"c80bdc0152a00471eec6ed0dd0f7d55d0b975498a00ba05e94100c84ad639a49","examples/halmark/main.rs":"4604737f714943383c57feac2b8468ecf15e9e60c54a5303455e9953ec5c79fb","examples/halmark/shader.wgsl":"26c256ec36d6f0e9a1647431ca772766bee4382d64eaa718ba7b488dcfb6bcca","examples/raw-gles.em.html":"70fbe68394a1a4522192de1dcfaf7d399f60d7bdf5de70b708f9bb0417427546","examples/raw-gles.rs":"095113a1ba0851652a77aabfc8fa6ea7edcc2d09e91fd1e5009ead87d5998ea9","examples/ray-traced-triangle/main.rs":"955c2b8700c3b2daf14e9ef963ff499ed185b6f349dbc63caa422b2cf4942a1f","examples/ray-traced-triangle/shader.wgsl":"cc10caf92746724a71f6dd0dbc3a71e57b37c7d1d83278556805a535c0728a9d","src/auxil/dxgi/conv.rs":"760cd4eaa79b530368a30140b96bf73ac4fbdb4025eb95f0bed581638c8bb1cb","src/auxil/dxgi/exception.rs":"f0cfb5a0adcdc3b6db909601fee51ad51368f5da269bcd46e4dbea45a3bec4b1","src/auxil/dxgi/factory.rs":"5f861fbfe2f4cce08722a95283549b8f62b96f24a306d080d9f1730ae53501d8","src/auxil/dxgi/mod.rs":"a202564d9ac97530b16a234b87d180cd345aae705e082a9b1177dcde813645f9","src/auxil/dxgi/result.rs":"79fe5aa17a2b21a7f06b1b604200c3c3e73fca31e8193aab80b5b15e7e9818a0","src/auxil/dxgi/time.rs":"b6f966b250e9424d5d7e4065f2108cba87197c1e30baae6d87083055d1bc5a4b","src/auxil/mod.rs":"720ef2aae258733322a3274fd858f91effb8951dabaf7bbfd8a9a0be2d2dba97","src/auxil/renderdoc.rs":"c2f849f70f576b0c9b0d32dd155b6a6353f74dff59cbeeaa994a12789d047c0f","src/dx12/adapter.rs":"7d647c9a1211e564fb1220c65df26fe2c519e5eddfa89291eaea45be4b60746a","src/dx12/command.rs":"6fe77b8b27c6428128ed0c3bcf7517e511c3c1eec8491a08936a696d5cb30751","src/dx12/conv.rs":"94d35f117ae003b07049f3a0bc6c45a0ffda9fb8053233d39c173cfb1b644403","src/dx12/descriptor.rs":"e06eb08bee4c805fa76b6ab791893b5b563ee60de9c8f8d8e0e21ab97ade5664","src/dx12/device.rs":"f7ca4a30085fdaecc321a01344f9d8cd907b7ba5a1b92f13a3bd9faad1934ed8","src/dx12/instance.rs":"351a4e0d526de8eafc74bf5f01a41da48efa39e0c66704a85da72e1140b159d4","src/dx12/mod.rs":"4b9d5e2414d628ed537f32f46604eeb95912ad9d5ee61cf4ce11c8dd6a88c8ab","src/dx12/shader_compilation.rs":"5087adb8576e2d7751619dfdf8b37c573bb4e494290c594077ca3208cce1e746","src/dx12/suballocation.rs":"6939fc36223a15cc070c744d0418f9ac6fa2829d794af17cdea7c61eb5f8d2c0","src/dx12/types.rs":"9573736baaa0ef607367c3b72144556d24faf677a26bb8df49a4372a1348e06b","src/dx12/view.rs":"792772e9c87840dcd045b7381a03162eb4a501492a95ca586e77e81aed621c67","src/empty.rs":"5c3a5e39d45b4522ff3496fe6ec3b4a7afd906b6095dff1cad113c826aa9ea62","src/gles/adapter.rs":"3175c86212b6c8caa099a3e34750c18251107461314c02f77c984e5b8301051a","src/gles/command.rs":"9f9ef3d97fcb2bc521b85141dee1ca9e8fe06b08d861766c3b3e9a2f3a53b494","src/gles/conv.rs":"5d15d3a33032d32ff99bc338fba0689fa54c76d0714e335fe48523d841df386f","src/gles/device.rs":"7ccd7aa3b878159190092bf279158289d754cc695bd27b9ec7177cd9b86b37c5","src/gles/egl.rs":"ad9b0ddc66877ae4088511283b8c860dd09b0b4d2c1fc51246c6935aa16703eb","src/gles/emscripten.rs":"19bb73a9d140645f3f32cd48b002151711a9b8456e213eab5f3a2be79239e147","src/gles/mod.rs":"b8999f76ad45e07312b291457100f12699ba6a2635c1f1913b0648e9a9394015","src/gles/queue.rs":"3ead252c54c673da6736a0c0c6b63c848791bc78042def3f3ffff8ffce2c6e64","src/gles/shaders/clear.frag":"9133ed8ed97d3641fbb6b5f5ea894a3554c629ccc1b80a5fc9221d7293aa1954","src/gles/shaders/clear.vert":"a543768725f4121ff2e9e1fb5b00644931e9d6f2f946c0ef01968afb5a135abd","src/gles/shaders/srgb_present.frag":"dd9a43c339a2fa4ccf7f6a1854c6f400cabf271a7d5e9230768e9f39d47f3ff5","src/gles/shaders/srgb_present.vert":"6e85d489403d80b81cc94790730bb53b309dfc5eeede8f1ea3412a660f31d357","src/gles/web.rs":"d263695d45736d3c6ec3528c8c33fe6cf3767d3429a13a92d88b4fdc7b6340fb","src/gles/wgl.rs":"06e947912c357c5275090b12b7e31e596ff264fd460e2449b6db4b79284eb74d","src/lib.rs":"c8b8a95f5bfd58eaada0af2cd0abc80f888aeea85969a1363f4061cc9b542ca4","src/metal/adapter.rs":"bb5d0ca1cecbd914cbb29487303be4ed69035469a8bc137784d5bbb6ab36cec7","src/metal/command.rs":"661b38a75d4f4cd1b0d6957f1f09db0743ec3a13bbafba9baa931894ee193f48","src/metal/conv.rs":"0bce6a8d0ccef16783475803d70d35e03ab7938c19374e22c9d253abe1f8b111","src/metal/device.rs":"c5deeecf475e0aa4b2027c656ea19207716f84b56cfa7c9132dca504d1abebfb","src/metal/mod.rs":"f6d12246a6c7e6d998db796a009702f289b5f56bd35f01c0a619f5345fb363c9","src/metal/surface.rs":"f2b9b65d4117db2b16c04469c573358eb65de104d5a72aa02da8483ee243cbd3","src/metal/time.rs":"c32d69f30e846dfcc0e39e01097fb80df63b2bebb6586143bb62494999850246","src/vulkan/adapter.rs":"ed980734c8239bad7f3371e0e778ec63ecea5fe971f04c3dcdd3fe55c359f63b","src/vulkan/command.rs":"e5a88eab59b3864cdf44ba2231270e16045505dc549b8b90251031de452ba826","src/vulkan/conv.rs":"7e6266e3a0b7d0b8d5d51362a0386a84bc047350eeac663b6352a94d5e5c0a87","src/vulkan/device.rs":"9824d597dbb51030bd337e80bb0f1eab6fdb6935fc87dfd8beae2c1f1048fbcf","src/vulkan/instance.rs":"cd4aa3a8ed343076446117bae21fc438fe8761054489ec7d1ed7c31512c2e5ec","src/vulkan/mod.rs":"0c6bfb321b693930bcae3e61d06ff7b71965a64761ce39d757fc609d4b46a03e"},"package":null} \ No newline at end of file
diff --git a/third_party/rust/wgpu-hal/Cargo.toml b/third_party/rust/wgpu-hal/Cargo.toml
index 88f96fb59b..47195f996d 100644
--- a/third_party/rust/wgpu-hal/Cargo.toml
+++ b/third_party/rust/wgpu-hal/Cargo.toml
@@ -95,6 +95,7 @@ cfg_aliases = "0.1"
[features]
default = ["link"]
+device_lost_panic = []
dx12 = [
"naga/hlsl-out",
"d3d12",
@@ -116,12 +117,15 @@ gles = [
"glutin_wgl_sys",
"khronos-egl",
"libloading",
+ "ndk-sys",
]
+internal_error_panic = []
link = ["metal/link"]
metal = [
"naga/msl-out",
"block",
]
+oom_panic = []
renderdoc = [
"libloading",
"renderdoc-sys",
@@ -133,6 +137,7 @@ vulkan = [
"gpu-descriptor",
"libloading",
"smallvec",
+ "android_system_properties",
]
windows_rs = ["gpu-allocator"]
@@ -191,8 +196,13 @@ optional = true
[target."cfg(not(target_arch = \"wasm32\"))".dev-dependencies]
glutin = "0.29.1"
-[target."cfg(target_os = \"android\")".dependencies]
-android_system_properties = "0.1.1"
+[target."cfg(target_os = \"android\")".dependencies.android_system_properties]
+version = "0.1.1"
+optional = true
+
+[target."cfg(target_os = \"android\")".dependencies.ndk-sys]
+version = "0.5.0"
+optional = true
[target."cfg(target_os = \"emscripten\")".dependencies.khronos-egl]
version = "6"
diff --git a/third_party/rust/wgpu-hal/src/auxil/dxgi/result.rs b/third_party/rust/wgpu-hal/src/auxil/dxgi/result.rs
index db013d2dec..2ac4464568 100644
--- a/third_party/rust/wgpu-hal/src/auxil/dxgi/result.rs
+++ b/third_party/rust/wgpu-hal/src/auxil/dxgi/result.rs
@@ -21,8 +21,26 @@ impl HResult<()> for i32 {
Err(Cow::Borrowed(description))
}
fn into_device_result(self, description: &str) -> Result<(), crate::DeviceError> {
+ #![allow(unreachable_code)]
+
self.into_result().map_err(|err| {
log::error!("{} failed: {}", description, err);
+
+ match self {
+ winerror::E_OUTOFMEMORY => {
+ #[cfg(feature = "oom_panic")]
+ panic!("{description} failed: Out of memory");
+ }
+ winerror::DXGI_ERROR_DEVICE_RESET | winerror::DXGI_ERROR_DEVICE_REMOVED => {
+ #[cfg(feature = "device_lost_panic")]
+ panic!("{description} failed: Device lost ({err})");
+ }
+ _ => {
+ #[cfg(feature = "internal_error_panic")]
+ panic!("{description} failed: {err}");
+ }
+ }
+
if self == winerror::E_OUTOFMEMORY {
crate::DeviceError::OutOfMemory
} else {
diff --git a/third_party/rust/wgpu-hal/src/dx12/adapter.rs b/third_party/rust/wgpu-hal/src/dx12/adapter.rs
index f6027014d2..960e1790a9 100644
--- a/third_party/rust/wgpu-hal/src/dx12/adapter.rs
+++ b/third_party/rust/wgpu-hal/src/dx12/adapter.rs
@@ -242,6 +242,7 @@ impl super::Adapter {
| wgt::Features::POLYGON_MODE_LINE
| wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES
| wgt::Features::TIMESTAMP_QUERY
+ | wgt::Features::TIMESTAMP_QUERY_INSIDE_ENCODERS
| wgt::Features::TIMESTAMP_QUERY_INSIDE_PASSES
| wgt::Features::TEXTURE_COMPRESSION_BC
| wgt::Features::CLEAR_TEXTURE
@@ -294,6 +295,22 @@ impl super::Adapter {
bgra8unorm_storage_supported,
);
+ // we must be using DXC because uint64_t was added with Shader Model 6
+ // and FXC only supports up to 5.1
+ let int64_shader_ops_supported = dxc_container.is_some() && {
+ let mut features1: d3d12_ty::D3D12_FEATURE_DATA_D3D12_OPTIONS1 =
+ unsafe { mem::zeroed() };
+ let hr = unsafe {
+ device.CheckFeatureSupport(
+ d3d12_ty::D3D12_FEATURE_D3D12_OPTIONS1,
+ &mut features1 as *mut _ as *mut _,
+ mem::size_of::<d3d12_ty::D3D12_FEATURE_DATA_D3D12_OPTIONS1>() as _,
+ )
+ };
+ hr == 0 && features1.Int64ShaderOps != 0
+ };
+ features.set(wgt::Features::SHADER_INT64, int64_shader_ops_supported);
+
// float32-filterable should always be available on d3d12
features.set(wgt::Features::FLOAT32_FILTERABLE, true);
@@ -307,6 +324,12 @@ impl super::Adapter {
downlevel.flags -=
wgt::DownlevelFlags::VERTEX_AND_INSTANCE_INDEX_RESPECTS_RESPECTIVE_FIRST_VALUE_IN_INDIRECT_DRAW;
+ // See https://learn.microsoft.com/en-us/windows/win32/direct3d12/hardware-feature-levels#feature-level-support
+ let max_color_attachments = 8;
+ // TODO: determine this programmatically if possible.
+ // https://github.com/gpuweb/gpuweb/issues/2965#issuecomment-1361315447
+ let max_color_attachment_bytes_per_sample = 64;
+
Some(crate::ExposedAdapter {
adapter: super::Adapter {
raw: adapter,
@@ -377,6 +400,8 @@ impl super::Adapter {
d3d12_ty::D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT,
min_storage_buffer_offset_alignment: 4,
max_inter_stage_shader_components: base.max_inter_stage_shader_components,
+ max_color_attachments,
+ max_color_attachment_bytes_per_sample,
max_compute_workgroup_storage_size: base.max_compute_workgroup_storage_size, //TODO?
max_compute_invocations_per_workgroup:
d3d12_ty::D3D12_CS_4_X_THREAD_GROUP_MAX_THREADS_PER_GROUP,
diff --git a/third_party/rust/wgpu-hal/src/dx12/command.rs b/third_party/rust/wgpu-hal/src/dx12/command.rs
index f527898d90..9d96d29cae 100644
--- a/third_party/rust/wgpu-hal/src/dx12/command.rs
+++ b/third_party/rust/wgpu-hal/src/dx12/command.rs
@@ -56,6 +56,13 @@ impl super::Temp {
}
}
+impl Drop for super::CommandEncoder {
+ fn drop(&mut self) {
+ use crate::CommandEncoder;
+ unsafe { self.discard_encoding() }
+ }
+}
+
impl super::CommandEncoder {
unsafe fn begin_pass(&mut self, kind: super::PassKind, label: crate::Label) {
let list = self.list.as_ref().unwrap();
diff --git a/third_party/rust/wgpu-hal/src/dx12/device.rs b/third_party/rust/wgpu-hal/src/dx12/device.rs
index 2507c125f8..3603b033b8 100644
--- a/third_party/rust/wgpu-hal/src/dx12/device.rs
+++ b/third_party/rust/wgpu-hal/src/dx12/device.rs
@@ -663,11 +663,7 @@ impl crate::Device<super::Api> for super::Device {
end_of_pass_timer_query: None,
})
}
- unsafe fn destroy_command_encoder(&self, encoder: super::CommandEncoder) {
- if let Some(list) = encoder.list {
- list.close();
- }
- }
+ unsafe fn destroy_command_encoder(&self, _encoder: super::CommandEncoder) {}
unsafe fn create_bind_group_layout(
&self,
diff --git a/third_party/rust/wgpu-hal/src/dx12/mod.rs b/third_party/rust/wgpu-hal/src/dx12/mod.rs
index 053b880689..13b43f8aca 100644
--- a/third_party/rust/wgpu-hal/src/dx12/mod.rs
+++ b/third_party/rust/wgpu-hal/src/dx12/mod.rs
@@ -238,6 +238,9 @@ struct DeviceShared {
heap_samplers: descriptor::GeneralHeap,
}
+unsafe impl Send for DeviceShared {}
+unsafe impl Sync for DeviceShared {}
+
pub struct Device {
raw: d3d12::Device,
present_queue: d3d12::CommandQueue,
diff --git a/third_party/rust/wgpu-hal/src/dx12/shader_compilation.rs b/third_party/rust/wgpu-hal/src/dx12/shader_compilation.rs
index df040dba15..288fc24745 100644
--- a/third_party/rust/wgpu-hal/src/dx12/shader_compilation.rs
+++ b/third_party/rust/wgpu-hal/src/dx12/shader_compilation.rs
@@ -13,7 +13,7 @@ use crate::auxil::dxgi::result::HResult;
pub(super) fn compile_fxc(
device: &super::Device,
- source: &String,
+ source: &str,
source_name: &str,
raw_ep: &std::ffi::CString,
stage_bit: wgt::ShaderStages,
@@ -211,7 +211,7 @@ mod dxc {
Err(crate::PipelineError::Linkage(
stage_bit,
format!(
- "DXC compile error: {:?}",
+ "DXC compile error: {}",
get_error_string_from_dxc_result(&dxc_container.library, &e.0)
.unwrap_or_default()
),
diff --git a/third_party/rust/wgpu-hal/src/gles/adapter.rs b/third_party/rust/wgpu-hal/src/gles/adapter.rs
index afa4023797..c09725e85f 100644
--- a/third_party/rust/wgpu-hal/src/gles/adapter.rs
+++ b/third_party/rust/wgpu-hal/src/gles/adapter.rs
@@ -4,6 +4,7 @@ use std::sync::{atomic::AtomicU8, Arc};
use wgt::AstcChannel;
use crate::auxil::db;
+use crate::gles::ShaderClearProgram;
// https://webgl2fundamentals.org/webgl/lessons/webgl-data-textures.html
@@ -435,7 +436,8 @@ impl super::Adapter {
let mut features = wgt::Features::empty()
| wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES
| wgt::Features::CLEAR_TEXTURE
- | wgt::Features::PUSH_CONSTANTS;
+ | wgt::Features::PUSH_CONSTANTS
+ | wgt::Features::DEPTH32FLOAT_STENCIL8;
features.set(
wgt::Features::ADDRESS_MODE_CLAMP_TO_BORDER | wgt::Features::ADDRESS_MODE_CLAMP_TO_ZERO,
extensions.contains("GL_EXT_texture_border_clamp")
@@ -472,6 +474,7 @@ impl super::Adapter {
features.set(wgt::Features::SHADER_UNUSED_VERTEX_OUTPUT, true);
if extensions.contains("GL_ARB_timer_query") {
features.set(wgt::Features::TIMESTAMP_QUERY, true);
+ features.set(wgt::Features::TIMESTAMP_QUERY_INSIDE_ENCODERS, true);
features.set(wgt::Features::TIMESTAMP_QUERY_INSIDE_PASSES, true);
}
let gl_bcn_exts = [
@@ -652,6 +655,15 @@ impl super::Adapter {
0
};
+ let max_color_attachments = unsafe {
+ gl.get_parameter_i32(glow::MAX_COLOR_ATTACHMENTS)
+ .min(gl.get_parameter_i32(glow::MAX_DRAW_BUFFERS))
+ .min(crate::MAX_COLOR_ATTACHMENTS as i32) as u32
+ };
+
+ // TODO: programmatically determine this.
+ let max_color_attachment_bytes_per_sample = 32;
+
let limits = wgt::Limits {
max_texture_dimension_1d: max_texture_size,
max_texture_dimension_2d: max_texture_size,
@@ -719,9 +731,21 @@ impl super::Adapter {
max_push_constant_size: super::MAX_PUSH_CONSTANTS as u32 * 4,
min_uniform_buffer_offset_alignment,
min_storage_buffer_offset_alignment,
- max_inter_stage_shader_components: unsafe {
- gl.get_parameter_i32(glow::MAX_VARYING_COMPONENTS)
- } as u32,
+ max_inter_stage_shader_components: {
+ // MAX_VARYING_COMPONENTS may return 0, because it is deprecated since OpenGL 3.2 core,
+ // and an OpenGL Context with the core profile and with forward-compatibility=true,
+ // will make deprecated constants unavailable.
+ let max_varying_components =
+ unsafe { gl.get_parameter_i32(glow::MAX_VARYING_COMPONENTS) } as u32;
+ if max_varying_components == 0 {
+ // default value for max_inter_stage_shader_components
+ 60
+ } else {
+ max_varying_components
+ }
+ },
+ max_color_attachments,
+ max_color_attachment_bytes_per_sample,
max_compute_workgroup_storage_size: if supports_work_group_params {
(unsafe { gl.get_parameter_i32(glow::MAX_COMPUTE_SHARED_MEMORY_SIZE) } as u32)
} else {
@@ -779,6 +803,7 @@ impl super::Adapter {
}
let downlevel_defaults = wgt::DownlevelLimits {};
+ let max_samples = unsafe { gl.get_parameter_i32(glow::MAX_SAMPLES) };
// Drop the GL guard so we can move the context into AdapterShared
// ( on Wasm the gl handle is just a ref so we tell clippy to allow
@@ -797,6 +822,7 @@ impl super::Adapter {
next_shader_id: Default::default(),
program_cache: Default::default(),
es: es_ver.is_some(),
+ max_msaa_samples: max_samples,
}),
},
info: Self::make_info(vendor, renderer),
@@ -825,7 +851,14 @@ impl super::Adapter {
let source = if es {
format!("#version 300 es\nprecision lowp float;\n{source}")
} else {
- format!("#version 130\n{source}")
+ let version = gl.version();
+ if version.major == 3 && version.minor == 0 {
+ // OpenGL 3.0 only supports this format
+ format!("#version 130\n{source}")
+ } else {
+ // OpenGL 3.1+ support this format
+ format!("#version 140\n{source}")
+ }
};
let shader = unsafe { gl.create_shader(shader_type) }.expect("Could not create shader");
unsafe { gl.shader_source(shader, &source) };
@@ -846,7 +879,7 @@ impl super::Adapter {
unsafe fn create_shader_clear_program(
gl: &glow::Context,
es: bool,
- ) -> Option<(glow::Program, glow::UniformLocation)> {
+ ) -> Option<ShaderClearProgram> {
let program = unsafe { gl.create_program() }.expect("Could not create shader program");
let vertex = unsafe {
Self::compile_shader(
@@ -882,7 +915,10 @@ impl super::Adapter {
unsafe { gl.delete_shader(vertex) };
unsafe { gl.delete_shader(fragment) };
- Some((program, color_uniform_location))
+ Some(ShaderClearProgram {
+ program,
+ color_uniform_location,
+ })
}
}
@@ -908,9 +944,18 @@ impl crate::Adapter<super::Api> for super::Adapter {
// Compile the shader program we use for doing manual clears to work around Mesa fastclear
// bug.
- let (shader_clear_program, shader_clear_program_color_uniform_location) = unsafe {
- Self::create_shader_clear_program(gl, self.shared.es)
- .ok_or(crate::DeviceError::ResourceCreationFailed)?
+ let shader_clear_program = if self
+ .shared
+ .workarounds
+ .contains(super::Workarounds::MESA_I915_SRGB_SHADER_CLEAR)
+ {
+ Some(unsafe {
+ Self::create_shader_clear_program(gl, self.shared.es)
+ .ok_or(crate::DeviceError::ResourceCreationFailed)?
+ })
+ } else {
+ // If we don't need the workaround, don't waste time and resources compiling the clear program
+ None
};
Ok(crate::OpenDevice {
@@ -928,7 +973,6 @@ impl crate::Adapter<super::Api> for super::Adapter {
copy_fbo: unsafe { gl.create_framebuffer() }
.map_err(|_| crate::DeviceError::OutOfMemory)?,
shader_clear_program,
- shader_clear_program_color_uniform_location,
zero_buffer,
temp_query_results: Mutex::new(Vec::new()),
draw_buffer_count: AtomicU8::new(1),
@@ -945,12 +989,7 @@ impl crate::Adapter<super::Api> for super::Adapter {
use wgt::TextureFormat as Tf;
let sample_count = {
- let max_samples = unsafe {
- self.shared
- .context
- .lock()
- .get_parameter_i32(glow::MAX_SAMPLES)
- };
+ let max_samples = self.shared.max_msaa_samples;
if max_samples >= 16 {
Tfc::MULTISAMPLE_X2
| Tfc::MULTISAMPLE_X4
diff --git a/third_party/rust/wgpu-hal/src/gles/command.rs b/third_party/rust/wgpu-hal/src/gles/command.rs
index 926122e4ad..4385e2a31e 100644
--- a/third_party/rust/wgpu-hal/src/gles/command.rs
+++ b/third_party/rust/wgpu-hal/src/gles/command.rs
@@ -93,6 +93,13 @@ impl super::CommandBuffer {
}
}
+impl Drop for super::CommandEncoder {
+ fn drop(&mut self) {
+ use crate::CommandEncoder;
+ unsafe { self.discard_encoding() }
+ }
+}
+
impl super::CommandEncoder {
fn rebind_stencil_func(&mut self) {
fn make(s: &super::StencilSide, face: u32) -> C {
diff --git a/third_party/rust/wgpu-hal/src/gles/device.rs b/third_party/rust/wgpu-hal/src/gles/device.rs
index d0abe2c169..2678488cf8 100644
--- a/third_party/rust/wgpu-hal/src/gles/device.rs
+++ b/third_party/rust/wgpu-hal/src/gles/device.rs
@@ -1194,13 +1194,16 @@ impl crate::Device<super::Api> for super::Device {
let sampler = desc.samplers[entry.resource_index as usize];
super::RawBinding::Sampler(sampler.raw)
}
- wgt::BindingType::Texture { .. } => {
+ wgt::BindingType::Texture { view_dimension, .. } => {
let view = desc.textures[entry.resource_index as usize].view;
if view.array_layers.start != 0 {
log::error!("Unable to create a sampled texture binding for non-zero array layer.\n{}",
"This is an implementation problem of wgpu-hal/gles backend.")
}
let (raw, target) = view.inner.as_native();
+
+ super::Texture::log_failing_target_heuristics(view_dimension, target);
+
super::RawBinding::Texture {
raw,
target,
diff --git a/third_party/rust/wgpu-hal/src/gles/egl.rs b/third_party/rust/wgpu-hal/src/gles/egl.rs
index aa985d8121..f4bfcf5487 100644
--- a/third_party/rust/wgpu-hal/src/gles/egl.rs
+++ b/third_party/rust/wgpu-hal/src/gles/egl.rs
@@ -1,7 +1,8 @@
use glow::HasContext;
+use once_cell::sync::Lazy;
use parking_lot::{Mutex, MutexGuard, RwLock};
-use std::{ffi, os::raw, ptr, rc::Rc, sync::Arc, time::Duration};
+use std::{collections::HashMap, ffi, os::raw, ptr, rc::Rc, sync::Arc, time::Duration};
/// The amount of time to wait while trying to obtain a lock to the adapter context
const CONTEXT_LOCK_TIMEOUT_SECS: u64 = 1;
@@ -50,16 +51,6 @@ type WlEglWindowResizeFun = unsafe extern "system" fn(
type WlEglWindowDestroyFun = unsafe extern "system" fn(window: *const raw::c_void);
-#[cfg(target_os = "android")]
-extern "C" {
- pub fn ANativeWindow_setBuffersGeometry(
- window: *mut raw::c_void,
- width: i32,
- height: i32,
- format: i32,
- ) -> i32;
-}
-
type EglLabel = *const raw::c_void;
#[allow(clippy::upper_case_acronyms)]
@@ -161,7 +152,7 @@ impl Drop for DisplayOwner {
fn open_x_display() -> Option<DisplayOwner> {
log::debug!("Loading X11 library to get the current display");
unsafe {
- let library = libloading::Library::new("libX11.so").ok()?;
+ let library = find_library(&["libX11.so.6", "libX11.so"])?;
let func: libloading::Symbol<XOpenDisplayFun> = library.get(b"XOpenDisplay").unwrap();
let result = func(ptr::null());
ptr::NonNull::new(result).map(|ptr| DisplayOwner {
@@ -442,6 +433,45 @@ struct Inner {
srgb_kind: SrgbFrameBufferKind,
}
+// Different calls to `eglGetPlatformDisplay` may return the same `Display`, making it a global
+// state of all our `EglContext`s. This forces us to track the number of such context to prevent
+// terminating the display if it's currently used by another `EglContext`.
+static DISPLAYS_REFERENCE_COUNT: Lazy<Mutex<HashMap<usize, usize>>> = Lazy::new(Default::default);
+
+fn initialize_display(
+ egl: &EglInstance,
+ display: khronos_egl::Display,
+) -> Result<(i32, i32), khronos_egl::Error> {
+ let mut guard = DISPLAYS_REFERENCE_COUNT.lock();
+ *guard.entry(display.as_ptr() as usize).or_default() += 1;
+
+ // We don't need to check the reference count here since according to the `eglInitialize`
+ // documentation, initializing an already initialized EGL display connection has no effect
+ // besides returning the version numbers.
+ egl.initialize(display)
+}
+
+fn terminate_display(
+ egl: &EglInstance,
+ display: khronos_egl::Display,
+) -> Result<(), khronos_egl::Error> {
+ let key = &(display.as_ptr() as usize);
+ let mut guard = DISPLAYS_REFERENCE_COUNT.lock();
+ let count_ref = guard
+ .get_mut(key)
+ .expect("Attempted to decref a display before incref was called");
+
+ if *count_ref > 1 {
+ *count_ref -= 1;
+
+ Ok(())
+ } else {
+ guard.remove(key);
+
+ egl.terminate(display)
+ }
+}
+
impl Inner {
fn create(
flags: wgt::InstanceFlags,
@@ -449,7 +479,7 @@ impl Inner {
display: khronos_egl::Display,
force_gles_minor_version: wgt::Gles3MinorVersion,
) -> Result<Self, crate::InstanceError> {
- let version = egl.initialize(display).map_err(|e| {
+ let version = initialize_display(&egl, display).map_err(|e| {
crate::InstanceError::with_source(
String::from("failed to initialize EGL display connection"),
e,
@@ -618,7 +648,8 @@ impl Drop for Inner {
{
log::warn!("Error in destroy_context: {:?}", e);
}
- if let Err(e) = self.egl.instance.terminate(self.egl.display) {
+
+ if let Err(e) = terminate_display(&self.egl.instance, self.egl.display) {
log::warn!("Error in terminate: {:?}", e);
}
}
@@ -783,11 +814,12 @@ impl crate::Instance<super::Api> for Instance {
(display, Some(Rc::new(display_owner)), WindowKind::AngleX11)
} else if client_ext_str.contains("EGL_MESA_platform_surfaceless") {
log::warn!("No windowing system present. Using surfaceless platform");
+ #[allow(clippy::unnecessary_literal_unwrap)] // This is only a literal on Emscripten
let egl = egl1_5.expect("Failed to get EGL 1.5 for surfaceless");
let display = unsafe {
egl.get_platform_display(
EGL_PLATFORM_SURFACELESS_MESA,
- std::ptr::null_mut(),
+ khronos_egl::DEFAULT_DISPLAY,
&[khronos_egl::ATTRIB_NONE],
)
}
@@ -863,7 +895,12 @@ impl crate::Instance<super::Api> for Instance {
.unwrap();
let ret = unsafe {
- ANativeWindow_setBuffersGeometry(handle.a_native_window.as_ptr(), 0, 0, format)
+ ndk_sys::ANativeWindow_setBuffersGeometry(
+ handle.a_native_window.as_ptr() as *mut ndk_sys::ANativeWindow,
+ 0,
+ 0,
+ format,
+ )
};
if ret != 0 {
diff --git a/third_party/rust/wgpu-hal/src/gles/mod.rs b/third_party/rust/wgpu-hal/src/gles/mod.rs
index 646419c7fe..6f41f7c000 100644
--- a/third_party/rust/wgpu-hal/src/gles/mod.rs
+++ b/third_party/rust/wgpu-hal/src/gles/mod.rs
@@ -251,6 +251,11 @@ struct AdapterShared {
next_shader_id: AtomicU32,
program_cache: Mutex<ProgramCache>,
es: bool,
+
+ /// Result of `gl.get_parameter_i32(glow::MAX_SAMPLES)`.
+ /// Cached here so it doesn't need to be queried every time texture format capabilities are requested.
+ /// (this has been shown to be a significant enough overhead)
+ max_msaa_samples: i32,
}
pub struct Adapter {
@@ -264,6 +269,11 @@ pub struct Device {
render_doc: crate::auxil::renderdoc::RenderDoc,
}
+pub struct ShaderClearProgram {
+ pub program: glow::Program,
+ pub color_uniform_location: glow::UniformLocation,
+}
+
pub struct Queue {
shared: Arc<AdapterShared>,
features: wgt::Features,
@@ -271,9 +281,7 @@ pub struct Queue {
copy_fbo: glow::Framebuffer,
/// Shader program used to clear the screen for [`Workarounds::MESA_I915_SRGB_SHADER_CLEAR`]
/// devices.
- shader_clear_program: glow::Program,
- /// The uniform location of the color uniform in the shader clear program
- shader_clear_program_color_uniform_location: glow::UniformLocation,
+ shader_clear_program: Option<ShaderClearProgram>,
/// Keep a reasonably large buffer filled with zeroes, so that we can implement `ClearBuffer` of
/// zeroes by copying from it.
zero_buffer: glow::Buffer,
@@ -366,6 +374,8 @@ impl Texture {
/// Returns the `target`, whether the image is 3d and whether the image is a cubemap.
fn get_info_from_desc(desc: &TextureDescriptor) -> u32 {
match desc.dimension {
+ // WebGL (1 and 2) as well as some GLES versions do not have 1D textures, so we are
+ // doing `TEXTURE_2D` instead
wgt::TextureDimension::D1 => glow::TEXTURE_2D,
wgt::TextureDimension::D2 => {
// HACK: detect a cube map; forces cube compatible textures to be cube textures
@@ -379,6 +389,43 @@ impl Texture {
wgt::TextureDimension::D3 => glow::TEXTURE_3D,
}
}
+
+ /// More information can be found in issues #1614 and #1574
+ fn log_failing_target_heuristics(view_dimension: wgt::TextureViewDimension, target: u32) {
+ let expected_target = match view_dimension {
+ wgt::TextureViewDimension::D1 => glow::TEXTURE_2D,
+ wgt::TextureViewDimension::D2 => glow::TEXTURE_2D,
+ wgt::TextureViewDimension::D2Array => glow::TEXTURE_2D_ARRAY,
+ wgt::TextureViewDimension::Cube => glow::TEXTURE_CUBE_MAP,
+ wgt::TextureViewDimension::CubeArray => glow::TEXTURE_CUBE_MAP_ARRAY,
+ wgt::TextureViewDimension::D3 => glow::TEXTURE_3D,
+ };
+
+ if expected_target == target {
+ return;
+ }
+
+ let buffer;
+ let got = match target {
+ glow::TEXTURE_2D => "D2",
+ glow::TEXTURE_2D_ARRAY => "D2Array",
+ glow::TEXTURE_CUBE_MAP => "Cube",
+ glow::TEXTURE_CUBE_MAP_ARRAY => "CubeArray",
+ glow::TEXTURE_3D => "D3",
+ target => {
+ buffer = target.to_string();
+ &buffer
+ }
+ };
+
+ log::error!(
+ "wgpu-hal heuristics assumed that the view dimension will be equal to `{got}` rather than `{view_dimension:?}`.\n{}\n{}\n{}\n{}",
+ "`D2` textures with `depth_or_array_layers == 1` are assumed to have view dimension `D2`",
+ "`D2` textures with `depth_or_array_layers > 1` are assumed to have view dimension `D2Array`",
+ "`D2` textures with `depth_or_array_layers == 6` are assumed to have view dimension `Cube`",
+ "`D2` textures with `depth_or_array_layers > 6 && depth_or_array_layers % 6 == 0` are assumed to have view dimension `CubeArray`",
+ );
+ }
}
#[derive(Clone, Debug)]
diff --git a/third_party/rust/wgpu-hal/src/gles/queue.rs b/third_party/rust/wgpu-hal/src/gles/queue.rs
index 6ec553bd29..5db5af9a16 100644
--- a/third_party/rust/wgpu-hal/src/gles/queue.rs
+++ b/third_party/rust/wgpu-hal/src/gles/queue.rs
@@ -40,10 +40,14 @@ fn get_z_offset(target: u32, base: &crate::TextureCopyBase) -> u32 {
impl super::Queue {
/// Performs a manual shader clear, used as a workaround for a clearing bug on mesa
unsafe fn perform_shader_clear(&self, gl: &glow::Context, draw_buffer: u32, color: [f32; 4]) {
- unsafe { gl.use_program(Some(self.shader_clear_program)) };
+ let shader_clear = self
+ .shader_clear_program
+ .as_ref()
+ .expect("shader_clear_program should always be set if the workaround is enabled");
+ unsafe { gl.use_program(Some(shader_clear.program)) };
unsafe {
gl.uniform_4_f32(
- Some(&self.shader_clear_program_color_uniform_location),
+ Some(&shader_clear.color_uniform_location),
color[0],
color[1],
color[2],
diff --git a/third_party/rust/wgpu-hal/src/gles/wgl.rs b/third_party/rust/wgpu-hal/src/gles/wgl.rs
index 6243430dc2..c9039090b7 100644
--- a/third_party/rust/wgpu-hal/src/gles/wgl.rs
+++ b/third_party/rust/wgpu-hal/src/gles/wgl.rs
@@ -160,6 +160,9 @@ struct Inner {
context: WglContext,
}
+unsafe impl Send for Inner {}
+unsafe impl Sync for Inner {}
+
pub struct Instance {
srgb_capable: bool,
inner: Arc<Mutex<Inner>>,
diff --git a/third_party/rust/wgpu-hal/src/lib.rs b/third_party/rust/wgpu-hal/src/lib.rs
index 5d8c6ddda8..f1794a4a89 100644
--- a/third_party/rust/wgpu-hal/src/lib.rs
+++ b/third_party/rust/wgpu-hal/src/lib.rs
@@ -16,6 +16,8 @@
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
#![allow(
+ // this happens on the GL backend, where it is both thread safe and non-thread safe in the same code.
+ clippy::arc_with_non_send_sync,
// for `if_then_panic` until it reaches stable
unknown_lints,
// We use loops for getting early-out of scope without closures.
@@ -329,6 +331,9 @@ pub trait Device<A: Api>: WasmNotSendSync {
unsafe fn create_sampler(&self, desc: &SamplerDescriptor) -> Result<A::Sampler, DeviceError>;
unsafe fn destroy_sampler(&self, sampler: A::Sampler);
+ /// Create a fresh [`CommandEncoder`].
+ ///
+ /// The new `CommandEncoder` is in the "closed" state.
unsafe fn create_command_encoder(
&self,
desc: &CommandEncoderDescriptor<A>,
@@ -429,19 +434,95 @@ pub trait Queue<A: Api>: WasmNotSendSync {
unsafe fn get_timestamp_period(&self) -> f32;
}
-/// Encoder for commands in command buffers.
-/// Serves as a parent for all the encoded command buffers.
-/// Works in bursts of action: one or more command buffers are recorded,
-/// then submitted to a queue, and then it needs to be `reset_all()`.
+/// Encoder and allocation pool for `CommandBuffer`.
+///
+/// The life cycle of a `CommandBuffer` is as follows:
+///
+/// - Call [`Device::create_command_encoder`] to create a new
+/// `CommandEncoder`, in the "closed" state.
+///
+/// - Call `begin_encoding` on a closed `CommandEncoder` to begin
+/// recording commands. This puts the `CommandEncoder` in the
+/// "recording" state.
+///
+/// - Call methods like `copy_buffer_to_buffer`, `begin_render_pass`,
+/// etc. on a "recording" `CommandEncoder` to add commands to the
+/// list.
+///
+/// - Call `end_encoding` on a recording `CommandEncoder` to close the
+/// encoder and construct a fresh `CommandBuffer` consisting of the
+/// list of commands recorded up to that point.
+///
+/// - Call `discard_encoding` on a recording `CommandEncoder` to drop
+/// the commands recorded thus far and close the encoder.
+///
+/// - Call `reset_all` on a closed `CommandEncoder`, passing all the
+/// live `CommandBuffers` built from it. All the `CommandBuffer`s
+/// are destroyed, and their resources are freed.
+///
+/// # Safety
+///
+/// - The `CommandEncoder` must be in the states described above to
+/// make the given calls.
+///
+/// - A `CommandBuffer` that has been submitted for execution on the
+/// GPU must live until its execution is complete.
+///
+/// - A `CommandBuffer` must not outlive the `CommandEncoder` that
+/// built it.
+///
+/// - A `CommandEncoder` must not outlive its `Device`.
pub trait CommandEncoder<A: Api>: WasmNotSendSync + fmt::Debug {
/// Begin encoding a new command buffer.
+ ///
+ /// This puts this `CommandEncoder` in the "recording" state.
+ ///
+ /// # Safety
+ ///
+ /// This `CommandEncoder` must be in the "closed" state.
unsafe fn begin_encoding(&mut self, label: Label) -> Result<(), DeviceError>;
- /// Discard currently recorded list, if any.
+
+ /// Discard the command list under construction, if any.
+ ///
+ /// This puts this `CommandEncoder` in the "closed" state.
+ ///
+ /// # Safety
+ ///
+ /// This `CommandEncoder` must be in the "recording" state.
unsafe fn discard_encoding(&mut self);
+
+ /// Return a fresh [`CommandBuffer`] holding the recorded commands.
+ ///
+ /// The returned [`CommandBuffer`] holds all the commands recorded
+ /// on this `CommandEncoder` since the last call to
+ /// [`begin_encoding`].
+ ///
+ /// This puts this `CommandEncoder` in the "closed" state.
+ ///
+ /// # Safety
+ ///
+ /// This `CommandEncoder` must be in the "recording" state.
+ ///
+ /// The returned [`CommandBuffer`] must not outlive this
+ /// `CommandEncoder`. Implementations are allowed to build
+ /// `CommandBuffer`s that depend on storage owned by this
+ /// `CommandEncoder`.
+ ///
+ /// [`CommandBuffer`]: Api::CommandBuffer
+ /// [`begin_encoding`]: CommandEncoder::begin_encoding
unsafe fn end_encoding(&mut self) -> Result<A::CommandBuffer, DeviceError>;
- /// Reclaims all resources that are allocated for this encoder.
- /// Must get all of the produced command buffers back,
- /// and they must not be used by GPU at this moment.
+
+ /// Reclaim all resources belonging to this `CommandEncoder`.
+ ///
+ /// # Safety
+ ///
+ /// This `CommandEncoder` must be in the "closed" state.
+ ///
+ /// The `command_buffers` iterator must produce all the live
+ /// [`CommandBuffer`]s built using this `CommandEncoder` --- that
+ /// is, every extant `CommandBuffer` returned from `end_encoding`.
+ ///
+ /// [`CommandBuffer`]: Api::CommandBuffer
unsafe fn reset_all<I>(&mut self, command_buffers: I)
where
I: Iterator<Item = A::CommandBuffer>;
diff --git a/third_party/rust/wgpu-hal/src/metal/adapter.rs b/third_party/rust/wgpu-hal/src/metal/adapter.rs
index a946ce5819..9ec777b0f0 100644
--- a/third_party/rust/wgpu-hal/src/metal/adapter.rs
+++ b/third_party/rust/wgpu-hal/src/metal/adapter.rs
@@ -731,6 +731,12 @@ impl super::PrivateCapabilities {
} else {
4
},
+ // Per https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf
+ max_color_attachment_bytes_per_sample: if device.supports_family(MTLGPUFamily::Apple4) {
+ 64
+ } else {
+ 32
+ },
max_varying_components: if device
.supports_feature_set(MTLFeatureSet::macOS_GPUFamily1_v1)
{
@@ -833,7 +839,7 @@ impl super::PrivateCapabilities {
self.indirect_draw_dispatch,
);
features.set(
- F::TIMESTAMP_QUERY,
+ F::TIMESTAMP_QUERY | F::TIMESTAMP_QUERY_INSIDE_ENCODERS,
self.timestamp_query_support
.contains(TimestampQuerySupport::STAGE_BOUNDARIES),
);
@@ -872,6 +878,10 @@ impl super::PrivateCapabilities {
{
features.insert(F::STORAGE_RESOURCE_BINDING_ARRAY);
}
+ features.set(
+ F::SHADER_INT64,
+ self.msl_version >= MTLLanguageVersion::V2_3,
+ );
features.set(
F::ADDRESS_MODE_CLAMP_TO_BORDER,
@@ -940,6 +950,10 @@ impl super::PrivateCapabilities {
min_uniform_buffer_offset_alignment: self.buffer_alignment as u32,
min_storage_buffer_offset_alignment: self.buffer_alignment as u32,
max_inter_stage_shader_components: self.max_varying_components,
+ max_color_attachments: (self.max_color_render_targets as u32)
+ .min(crate::MAX_COLOR_ATTACHMENTS as u32),
+ max_color_attachment_bytes_per_sample: self.max_color_attachment_bytes_per_sample
+ as u32,
max_compute_workgroup_storage_size: self.max_total_threadgroup_memory,
max_compute_invocations_per_workgroup: self.max_threads_per_group,
max_compute_workgroup_size_x: self.max_threads_per_group,
diff --git a/third_party/rust/wgpu-hal/src/metal/mod.rs b/third_party/rust/wgpu-hal/src/metal/mod.rs
index 298f60faac..62fbf3d49d 100644
--- a/third_party/rust/wgpu-hal/src/metal/mod.rs
+++ b/third_party/rust/wgpu-hal/src/metal/mod.rs
@@ -248,6 +248,7 @@ struct PrivateCapabilities {
max_texture_layers: u64,
max_fragment_input_components: u64,
max_color_render_targets: u8,
+ max_color_attachment_bytes_per_sample: u8,
max_varying_components: u32,
max_threads_per_group: u32,
max_total_threadgroup_memory: u32,
diff --git a/third_party/rust/wgpu-hal/src/vulkan/adapter.rs b/third_party/rust/wgpu-hal/src/vulkan/adapter.rs
index 85e620d23c..83b3dfa8e5 100644
--- a/third_party/rust/wgpu-hal/src/vulkan/adapter.rs
+++ b/third_party/rust/wgpu-hal/src/vulkan/adapter.rs
@@ -189,7 +189,7 @@ impl PhysicalDeviceFeatures {
//.shader_clip_distance(requested_features.contains(wgt::Features::SHADER_CLIP_DISTANCE))
//.shader_cull_distance(requested_features.contains(wgt::Features::SHADER_CULL_DISTANCE))
.shader_float64(requested_features.contains(wgt::Features::SHADER_F64))
- //.shader_int64(requested_features.contains(wgt::Features::SHADER_INT64))
+ .shader_int64(requested_features.contains(wgt::Features::SHADER_INT64))
.shader_int16(requested_features.contains(wgt::Features::SHADER_I16))
//.shader_resource_residency(requested_features.contains(wgt::Features::SHADER_RESOURCE_RESIDENCY))
.geometry_shader(requested_features.contains(wgt::Features::SHADER_PRIMITIVE_INDEX))
@@ -369,6 +369,7 @@ impl PhysicalDeviceFeatures {
| F::ADDRESS_MODE_CLAMP_TO_BORDER
| F::ADDRESS_MODE_CLAMP_TO_ZERO
| F::TIMESTAMP_QUERY
+ | F::TIMESTAMP_QUERY_INSIDE_ENCODERS
| F::TIMESTAMP_QUERY_INSIDE_PASSES
| F::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES
| F::CLEAR_TEXTURE;
@@ -468,7 +469,7 @@ impl PhysicalDeviceFeatures {
//if self.core.shader_clip_distance != 0 {
//if self.core.shader_cull_distance != 0 {
features.set(F::SHADER_F64, self.core.shader_float64 != 0);
- //if self.core.shader_int64 != 0 {
+ features.set(F::SHADER_INT64, self.core.shader_int64 != 0);
features.set(F::SHADER_I16, self.core.shader_int16 != 0);
//if caps.supports_extension(vk::KhrSamplerMirrorClampToEdgeFn::name()) {
@@ -827,6 +828,11 @@ impl PhysicalDeviceCapabilities {
u64::MAX
};
+ // TODO: programmatically determine this, if possible. It's unclear whether we can
+ // as of https://github.com/gpuweb/gpuweb/issues/2965#issuecomment-1361315447.
+ // We could increase the limit when we aren't on a tiled GPU.
+ let max_color_attachment_bytes_per_sample = 32;
+
wgt::Limits {
max_texture_dimension_1d: limits.max_image_dimension1_d,
max_texture_dimension_2d: limits.max_image_dimension2_d,
@@ -862,6 +868,10 @@ impl PhysicalDeviceCapabilities {
max_inter_stage_shader_components: limits
.max_vertex_output_components
.min(limits.max_fragment_input_components),
+ max_color_attachments: limits
+ .max_color_attachments
+ .min(crate::MAX_COLOR_ATTACHMENTS as u32),
+ max_color_attachment_bytes_per_sample,
max_compute_workgroup_storage_size: limits.max_compute_shared_memory_size,
max_compute_invocations_per_workgroup: limits.max_compute_work_group_invocations,
max_compute_workgroup_size_x: max_compute_workgroup_sizes[0],
@@ -1444,6 +1454,10 @@ impl super::Adapter {
capabilities.push(spv::Capability::RayQueryKHR);
}
+ if features.contains(wgt::Features::SHADER_INT64) {
+ capabilities.push(spv::Capability::Int64);
+ }
+
let mut flags = spv::WriterFlags::empty();
flags.set(
spv::WriterFlags::DEBUG,
diff --git a/third_party/rust/wgpu-hal/src/vulkan/instance.rs b/third_party/rust/wgpu-hal/src/vulkan/instance.rs
index c4ef573461..771938b0b0 100644
--- a/third_party/rust/wgpu-hal/src/vulkan/instance.rs
+++ b/third_party/rust/wgpu-hal/src/vulkan/instance.rs
@@ -6,6 +6,7 @@ use std::{
thread,
};
+use arrayvec::ArrayVec;
use ash::{
extensions::{ext, khr},
vk,
@@ -34,11 +35,13 @@ unsafe extern "system" fn debug_utils_messenger_callback(
// the debug range start and end appear in different command buffers.
let khronos_validation_layer =
std::ffi::CStr::from_bytes_with_nul(b"Khronos Validation Layer\0").unwrap();
- if user_data.validation_layer_description.as_ref() == khronos_validation_layer
- && user_data.validation_layer_spec_version >= vk::make_api_version(0, 1, 3, 240)
- && user_data.validation_layer_spec_version <= vk::make_api_version(0, 1, 3, 250)
- {
- return vk::FALSE;
+ if let Some(layer_properties) = user_data.validation_layer_properties.as_ref() {
+ if layer_properties.layer_description.as_ref() == khronos_validation_layer
+ && layer_properties.layer_spec_version >= vk::make_api_version(0, 1, 3, 240)
+ && layer_properties.layer_spec_version <= vk::make_api_version(0, 1, 3, 250)
+ {
+ return vk::FALSE;
+ }
}
}
@@ -211,6 +214,22 @@ impl super::Instance {
&self.shared
}
+ fn enumerate_instance_extension_properties(
+ entry: &ash::Entry,
+ layer_name: Option<&CStr>,
+ ) -> Result<Vec<vk::ExtensionProperties>, crate::InstanceError> {
+ let instance_extensions = {
+ profiling::scope!("vkEnumerateInstanceExtensionProperties");
+ entry.enumerate_instance_extension_properties(layer_name)
+ };
+ instance_extensions.map_err(|e| {
+ crate::InstanceError::with_source(
+ String::from("enumerate_instance_extension_properties() failed"),
+ e,
+ )
+ })
+ }
+
/// Return the instance extension names wgpu would like to enable.
///
/// Return a vector of the names of instance extensions actually available
@@ -229,16 +248,7 @@ impl super::Instance {
_instance_api_version: u32,
flags: wgt::InstanceFlags,
) -> Result<Vec<&'static CStr>, crate::InstanceError> {
- let instance_extensions = {
- profiling::scope!("vkEnumerateInstanceExtensionProperties");
- entry.enumerate_instance_extension_properties(None)
- };
- let instance_extensions = instance_extensions.map_err(|e| {
- crate::InstanceError::with_source(
- String::from("enumerate_instance_extension_properties() failed"),
- e,
- )
- })?;
+ let instance_extensions = Self::enumerate_instance_extension_properties(entry, None)?;
// Check our extensions against the available extensions
let mut extensions: Vec<&'static CStr> = Vec::new();
@@ -643,6 +653,31 @@ impl crate::Instance<super::Api> for super::Instance {
.find(|inst_layer| cstr_from_bytes_until_nul(&inst_layer.layer_name) == Some(name))
}
+ let validation_layer_name =
+ CStr::from_bytes_with_nul(b"VK_LAYER_KHRONOS_validation\0").unwrap();
+ let validation_layer_properties = find_layer(&instance_layers, validation_layer_name);
+
+ // Determine if VK_EXT_validation_features is available, so we can enable
+ // GPU assisted validation and synchronization validation.
+ let validation_features_are_enabled = if validation_layer_properties.is_some() {
+ // Get the all the instance extension properties.
+ let exts =
+ Self::enumerate_instance_extension_properties(&entry, Some(validation_layer_name))?;
+ // Convert all the names of the extensions into an iterator of CStrs.
+ let mut ext_names = exts
+ .iter()
+ .filter_map(|ext| cstr_from_bytes_until_nul(&ext.extension_name));
+ // Find the validation features extension.
+ ext_names.any(|ext_name| ext_name == vk::ExtValidationFeaturesFn::name())
+ } else {
+ false
+ };
+
+ let should_enable_gpu_based_validation = desc
+ .flags
+ .intersects(wgt::InstanceFlags::GPU_BASED_VALIDATION)
+ && validation_features_are_enabled;
+
let nv_optimus_layer = CStr::from_bytes_with_nul(b"VK_LAYER_NV_optimus\0").unwrap();
let has_nv_optimus = find_layer(&instance_layers, nv_optimus_layer).is_some();
@@ -651,52 +686,33 @@ impl crate::Instance<super::Api> for super::Instance {
let mut layers: Vec<&'static CStr> = Vec::new();
+ let has_debug_extension = extensions.contains(&ext::DebugUtils::name());
+ let mut debug_user_data = has_debug_extension.then(|| {
+ // Put the callback data on the heap, to ensure it will never be
+ // moved.
+ Box::new(super::DebugUtilsMessengerUserData {
+ validation_layer_properties: None,
+ has_obs_layer,
+ })
+ });
+
// Request validation layer if asked.
- let mut debug_utils = None;
- if desc.flags.intersects(wgt::InstanceFlags::VALIDATION) {
- let validation_layer_name =
- CStr::from_bytes_with_nul(b"VK_LAYER_KHRONOS_validation\0").unwrap();
- if let Some(layer_properties) = find_layer(&instance_layers, validation_layer_name) {
+ if desc.flags.intersects(wgt::InstanceFlags::VALIDATION)
+ || should_enable_gpu_based_validation
+ {
+ if let Some(layer_properties) = validation_layer_properties {
layers.push(validation_layer_name);
- if extensions.contains(&ext::DebugUtils::name()) {
- // Put the callback data on the heap, to ensure it will never be
- // moved.
- let callback_data = Box::new(super::DebugUtilsMessengerUserData {
- validation_layer_description: cstr_from_bytes_until_nul(
- &layer_properties.description,
- )
- .unwrap()
- .to_owned(),
- validation_layer_spec_version: layer_properties.spec_version,
- has_obs_layer,
- });
-
- // having ERROR unconditionally because Vk doesn't like empty flags
- let mut severity = vk::DebugUtilsMessageSeverityFlagsEXT::ERROR;
- if log::max_level() >= log::LevelFilter::Debug {
- severity |= vk::DebugUtilsMessageSeverityFlagsEXT::VERBOSE;
- }
- if log::max_level() >= log::LevelFilter::Info {
- severity |= vk::DebugUtilsMessageSeverityFlagsEXT::INFO;
- }
- if log::max_level() >= log::LevelFilter::Warn {
- severity |= vk::DebugUtilsMessageSeverityFlagsEXT::WARNING;
- }
-
- let message_type = vk::DebugUtilsMessageTypeFlagsEXT::GENERAL
- | vk::DebugUtilsMessageTypeFlagsEXT::VALIDATION
- | vk::DebugUtilsMessageTypeFlagsEXT::PERFORMANCE;
-
- let create_info = super::DebugUtilsCreateInfo {
- severity,
- message_type,
- callback_data,
- };
-
- let vk_create_info = create_info.to_vk_create_info().build();
-
- debug_utils = Some((create_info, vk_create_info));
+ if let Some(debug_user_data) = debug_user_data.as_mut() {
+ debug_user_data.validation_layer_properties =
+ Some(super::ValidationLayerProperties {
+ layer_description: cstr_from_bytes_until_nul(
+ &layer_properties.description,
+ )
+ .unwrap()
+ .to_owned(),
+ layer_spec_version: layer_properties.spec_version,
+ });
}
} else {
log::warn!(
@@ -705,6 +721,35 @@ impl crate::Instance<super::Api> for super::Instance {
);
}
}
+ let mut debug_utils = if let Some(callback_data) = debug_user_data {
+ // having ERROR unconditionally because Vk doesn't like empty flags
+ let mut severity = vk::DebugUtilsMessageSeverityFlagsEXT::ERROR;
+ if log::max_level() >= log::LevelFilter::Debug {
+ severity |= vk::DebugUtilsMessageSeverityFlagsEXT::VERBOSE;
+ }
+ if log::max_level() >= log::LevelFilter::Info {
+ severity |= vk::DebugUtilsMessageSeverityFlagsEXT::INFO;
+ }
+ if log::max_level() >= log::LevelFilter::Warn {
+ severity |= vk::DebugUtilsMessageSeverityFlagsEXT::WARNING;
+ }
+
+ let message_type = vk::DebugUtilsMessageTypeFlagsEXT::GENERAL
+ | vk::DebugUtilsMessageTypeFlagsEXT::VALIDATION
+ | vk::DebugUtilsMessageTypeFlagsEXT::PERFORMANCE;
+
+ let create_info = super::DebugUtilsCreateInfo {
+ severity,
+ message_type,
+ callback_data,
+ };
+
+ let vk_create_info = create_info.to_vk_create_info().build();
+
+ Some((create_info, vk_create_info))
+ } else {
+ None
+ };
#[cfg(target_os = "android")]
let android_sdk_version = {
@@ -756,6 +801,28 @@ impl crate::Instance<super::Api> for super::Instance {
create_info = create_info.push_next(vk_create_info);
}
+ // Enable explicit validation features if available
+ let mut validation_features;
+ let mut validation_feature_list: ArrayVec<_, 3>;
+ if validation_features_are_enabled {
+ validation_feature_list = ArrayVec::new();
+
+ // Always enable synchronization validation
+ validation_feature_list
+ .push(vk::ValidationFeatureEnableEXT::SYNCHRONIZATION_VALIDATION);
+
+ // Only enable GPU assisted validation if requested.
+ if should_enable_gpu_based_validation {
+ validation_feature_list.push(vk::ValidationFeatureEnableEXT::GPU_ASSISTED);
+ validation_feature_list
+ .push(vk::ValidationFeatureEnableEXT::GPU_ASSISTED_RESERVE_BINDING_SLOT);
+ }
+
+ validation_features = vk::ValidationFeaturesEXT::builder()
+ .enabled_validation_features(&validation_feature_list);
+ create_info = create_info.push_next(&mut validation_features);
+ }
+
unsafe {
profiling::scope!("vkCreateInstance");
entry.create_instance(&create_info, None)
diff --git a/third_party/rust/wgpu-hal/src/vulkan/mod.rs b/third_party/rust/wgpu-hal/src/vulkan/mod.rs
index 787ebd7267..1f922e83da 100644
--- a/third_party/rust/wgpu-hal/src/vulkan/mod.rs
+++ b/third_party/rust/wgpu-hal/src/vulkan/mod.rs
@@ -101,17 +101,25 @@ pub struct DebugUtilsCreateInfo {
callback_data: Box<DebugUtilsMessengerUserData>,
}
+#[derive(Debug)]
+/// The properties related to the validation layer needed for the
+/// DebugUtilsMessenger for their workarounds
+struct ValidationLayerProperties {
+ /// Validation layer description, from `vk::LayerProperties`.
+ layer_description: std::ffi::CString,
+
+ /// Validation layer specification version, from `vk::LayerProperties`.
+ layer_spec_version: u32,
+}
+
/// User data needed by `instance::debug_utils_messenger_callback`.
///
/// When we create the [`vk::DebugUtilsMessengerEXT`], the `pUserData`
/// pointer refers to one of these values.
#[derive(Debug)]
pub struct DebugUtilsMessengerUserData {
- /// Validation layer description, from `vk::LayerProperties`.
- validation_layer_description: std::ffi::CString,
-
- /// Validation layer specification version, from `vk::LayerProperties`.
- validation_layer_spec_version: u32,
+ /// The properties related to the validation layer, if present
+ validation_layer_properties: Option<ValidationLayerProperties>,
/// If the OBS layer is present. OBS never increments the version of their layer,
/// so there's no reason to have the version.
@@ -724,13 +732,25 @@ impl crate::Queue<Api> for Queue {
impl From<vk::Result> for crate::DeviceError {
fn from(result: vk::Result) -> Self {
+ #![allow(unreachable_code)]
match result {
vk::Result::ERROR_OUT_OF_HOST_MEMORY | vk::Result::ERROR_OUT_OF_DEVICE_MEMORY => {
+ #[cfg(feature = "oom_panic")]
+ panic!("Out of memory ({result:?})");
+
Self::OutOfMemory
}
- vk::Result::ERROR_DEVICE_LOST => Self::Lost,
+ vk::Result::ERROR_DEVICE_LOST => {
+ #[cfg(feature = "device_lost_panic")]
+ panic!("Device lost");
+
+ Self::Lost
+ }
_ => {
- log::warn!("Unrecognized device error {:?}", result);
+ #[cfg(feature = "internal_error_panic")]
+ panic!("Internal error: {result:?}");
+
+ log::warn!("Unrecognized device error {result:?}");
Self::Lost
}
}
diff --git a/third_party/rust/wgpu-types/.cargo-checksum.json b/third_party/rust/wgpu-types/.cargo-checksum.json
index f45cbfc589..dea747bd18 100644
--- a/third_party/rust/wgpu-types/.cargo-checksum.json
+++ b/third_party/rust/wgpu-types/.cargo-checksum.json
@@ -1 +1 @@
-{"files":{"Cargo.toml":"6b0d7ddecc26e3b72cb6d47793770203147f851f048da8d1f5d8f508e40d4f82","LICENSE.APACHE":"a6cba85bc92e0cff7a450b1d873c0eaa2e9fc96bf472df0247a26bec77bf3ff9","LICENSE.MIT":"c7fea58d1cfe49634cd92e54fc10a9d871f4b275321a4cd8c09e449122caaeb4","src/assertions.rs":"3fe98027aa73970c8ab7874a3e13dbfd6faa87df2081beb5c83aeec4c60f372f","src/lib.rs":"33e1cf343a848c5deecbac6949d5a1378a70da0a48b2120fc62d600ce98a2da2","src/math.rs":"4d03039736dd6926feb139bc68734cb59df34ede310427bbf059e5c925e0af3b"},"package":null} \ No newline at end of file
+{"files":{"Cargo.toml":"c536ec4d70291834fb3e6bcd6f03900bb3a651eda9449e7adf03ef2611be96a9","LICENSE.APACHE":"a6cba85bc92e0cff7a450b1d873c0eaa2e9fc96bf472df0247a26bec77bf3ff9","LICENSE.MIT":"c7fea58d1cfe49634cd92e54fc10a9d871f4b275321a4cd8c09e449122caaeb4","src/assertions.rs":"3fe98027aa73970c8ab7874a3e13dbfd6faa87df2081beb5c83aeec4c60f372f","src/lib.rs":"bafa964caee2fdc6fc2adbb1dea540d5575e29c45946bc51e9912bfbdb13a352","src/math.rs":"4d03039736dd6926feb139bc68734cb59df34ede310427bbf059e5c925e0af3b"},"package":null} \ No newline at end of file
diff --git a/third_party/rust/wgpu-types/Cargo.toml b/third_party/rust/wgpu-types/Cargo.toml
index 9900e77771..792b03dcc8 100644
--- a/third_party/rust/wgpu-types/Cargo.toml
+++ b/third_party/rust/wgpu-types/Cargo.toml
@@ -45,7 +45,7 @@ features = ["serde_derive"]
optional = true
[dev-dependencies]
-serde_json = "1.0.111"
+serde_json = "1.0.113"
[dev-dependencies.serde]
version = "1"
diff --git a/third_party/rust/wgpu-types/src/lib.rs b/third_party/rust/wgpu-types/src/lib.rs
index d2d493a7ca..347aad76f9 100644
--- a/third_party/rust/wgpu-types/src/lib.rs
+++ b/third_party/rust/wgpu-types/src/lib.rs
@@ -267,15 +267,75 @@ bitflags::bitflags! {
///
/// This is a web and native feature.
const DEPTH_CLIP_CONTROL = 1 << 0;
+
+ /// Allows for explicit creation of textures of format [`TextureFormat::Depth32FloatStencil8`]
+ ///
+ /// Supported platforms:
+ /// - Vulkan (mostly)
+ /// - DX12
+ /// - Metal
+ /// - OpenGL
+ ///
+ /// This is a web and native feature.
+ const DEPTH32FLOAT_STENCIL8 = 1 << 1;
+
+ /// Enables BCn family of compressed textures. All BCn textures use 4x4 pixel blocks
+ /// with 8 or 16 bytes per block.
+ ///
+ /// Compressed textures sacrifice some quality in exchange for significantly reduced
+ /// bandwidth usage.
+ ///
+ /// Support for this feature guarantees availability of [`TextureUsages::COPY_SRC | TextureUsages::COPY_DST | TextureUsages::TEXTURE_BINDING`] for BCn formats.
+ /// [`Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES`] may enable additional usages.
+ ///
+ /// Supported Platforms:
+ /// - desktops
+ ///
+ /// This is a web and native feature.
+ const TEXTURE_COMPRESSION_BC = 1 << 2;
+
+ /// Enables ETC family of compressed textures. All ETC textures use 4x4 pixel blocks.
+ /// ETC2 RGB and RGBA1 are 8 bytes per block. RTC2 RGBA8 and EAC are 16 bytes per block.
+ ///
+ /// Compressed textures sacrifice some quality in exchange for significantly reduced
+ /// bandwidth usage.
+ ///
+ /// Support for this feature guarantees availability of [`TextureUsages::COPY_SRC | TextureUsages::COPY_DST | TextureUsages::TEXTURE_BINDING`] for ETC2 formats.
+ /// [`Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES`] may enable additional usages.
+ ///
+ /// Supported Platforms:
+ /// - Vulkan on Intel
+ /// - Mobile (some)
+ ///
+ /// This is a web and native feature.
+ const TEXTURE_COMPRESSION_ETC2 = 1 << 3;
+
+ /// Enables ASTC family of compressed textures. ASTC textures use pixel blocks varying from 4x4 to 12x12.
+ /// Blocks are always 16 bytes.
+ ///
+ /// Compressed textures sacrifice some quality in exchange for significantly reduced
+ /// bandwidth usage.
+ ///
+ /// Support for this feature guarantees availability of [`TextureUsages::COPY_SRC | TextureUsages::COPY_DST | TextureUsages::TEXTURE_BINDING`] for ASTC formats with Unorm/UnormSrgb channel type.
+ /// [`Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES`] may enable additional usages.
+ ///
+ /// Supported Platforms:
+ /// - Vulkan on Intel
+ /// - Mobile (some)
+ ///
+ /// This is a web and native feature.
+ const TEXTURE_COMPRESSION_ASTC = 1 << 4;
+
/// Enables use of Timestamp Queries. These queries tell the current gpu timestamp when
/// all work before the query is finished.
///
/// This feature allows the use of
- /// - [`CommandEncoder::write_timestamp`]
/// - [`RenderPassDescriptor::timestamp_writes`]
/// - [`ComputePassDescriptor::timestamp_writes`]
/// to write out timestamps.
- /// For timestamps within passes refer to [`Features::TIMESTAMP_QUERY_INSIDE_PASSES`]
+ ///
+ /// For arbitrary timestamp write commands on encoders refer to [`Features::TIMESTAMP_QUERY_INSIDE_ENCODERS`].
+ /// For arbitrary timestamp write commands on passes refer to [`Features::TIMESTAMP_QUERY_INSIDE_PASSES`].
///
/// They must be resolved using [`CommandEncoder::resolve_query_sets`] into a buffer,
/// then the result must be multiplied by the timestamp period [`Queue::get_timestamp_period`]
@@ -288,7 +348,8 @@ bitflags::bitflags! {
/// - Metal
///
/// This is a web and native feature.
- const TIMESTAMP_QUERY = 1 << 1;
+ const TIMESTAMP_QUERY = 1 << 5;
+
/// Allows non-zero value for the `first_instance` member in indirect draw calls.
///
/// If this feature is not enabled, and the `first_instance` member is non-zero, the behavior may be:
@@ -306,33 +367,29 @@ bitflags::bitflags! {
/// - OpenGL ES / WebGL
///
/// This is a web and native feature.
- const INDIRECT_FIRST_INSTANCE = 1 << 2;
-
- // 3..8 available
-
- // Shader:
+ const INDIRECT_FIRST_INSTANCE = 1 << 6;
/// Allows shaders to acquire the FP16 ability
///
- /// Note: this is not supported in `naga` yet,only through `spirv-passthrough` right now.
+ /// Note: this is not supported in `naga` yet, only through `spirv-passthrough` right now.
///
/// Supported Platforms:
/// - Vulkan
/// - Metal
///
/// This is a web and native feature.
- const SHADER_F16 = 1 << 8;
-
- // 9..14 available
+ const SHADER_F16 = 1 << 7;
- // Texture Formats:
- // The features starting with a ? are features that might become part of the spec or
- // at the very least we can implement as native features; since they should cover all
- // possible formats and capabilities across backends.
- //
- // ? const FORMATS_TIER_1 = 1 << 14; (https://github.com/gpuweb/gpuweb/issues/3837)
- // ? const RW_STORAGE_TEXTURE_TIER_1 = 1 << 15; (https://github.com/gpuweb/gpuweb/issues/3838)
+ /// Allows for usage of textures of format [`TextureFormat::Rg11b10Float`] as a render target
+ ///
+ /// Supported platforms:
+ /// - Vulkan
+ /// - DX12
+ /// - Metal
+ ///
+ /// This is a web and native feature.
+ const RG11B10UFLOAT_RENDERABLE = 1 << 8;
/// Allows the [`wgpu::TextureUsages::STORAGE_BINDING`] usage on textures with format [`TextureFormat::Bgra8unorm`]
///
@@ -342,10 +399,8 @@ bitflags::bitflags! {
/// - Metal
///
/// This is a web and native feature.
- const BGRA8UNORM_STORAGE = 1 << 16;
+ const BGRA8UNORM_STORAGE = 1 << 9;
- // ? const NORM16_FILTERABLE = 1 << 17; (https://github.com/gpuweb/gpuweb/issues/3839)
- // ? const NORM16_RESOLVE = 1 << 18; (https://github.com/gpuweb/gpuweb/issues/3839)
/// Allows textures with formats "r32float", "rg32float", and "rgba32float" to be filterable.
///
@@ -356,81 +411,11 @@ bitflags::bitflags! {
/// - GL with one of `GL_ARB_color_buffer_float`/`GL_EXT_color_buffer_float`/`OES_texture_float_linear`
///
/// This is a web and native feature.
- const FLOAT32_FILTERABLE = 1 << 19;
+ const FLOAT32_FILTERABLE = 1 << 10;
- // ? const FLOAT32_BLENDABLE = 1 << 20; (https://github.com/gpuweb/gpuweb/issues/3556)
- // ? const 32BIT_FORMAT_MULTISAMPLE = 1 << 21; (https://github.com/gpuweb/gpuweb/issues/3844)
- // ? const 32BIT_FORMAT_RESOLVE = 1 << 22; (https://github.com/gpuweb/gpuweb/issues/3844)
-
- /// Allows for usage of textures of format [`TextureFormat::Rg11b10Float`] as a render target
- ///
- /// Supported platforms:
- /// - Vulkan
- /// - DX12
- /// - Metal
- ///
- /// This is a web and native feature.
- const RG11B10UFLOAT_RENDERABLE = 1 << 23;
-
- /// Allows for explicit creation of textures of format [`TextureFormat::Depth32FloatStencil8`]
- ///
- /// Supported platforms:
- /// - Vulkan (mostly)
- /// - DX12
- /// - Metal
- ///
- /// This is a web and native feature.
- const DEPTH32FLOAT_STENCIL8 = 1 << 24;
- /// Enables BCn family of compressed textures. All BCn textures use 4x4 pixel blocks
- /// with 8 or 16 bytes per block.
- ///
- /// Compressed textures sacrifice some quality in exchange for significantly reduced
- /// bandwidth usage.
- ///
- /// Support for this feature guarantees availability of [`TextureUsages::COPY_SRC | TextureUsages::COPY_DST | TextureUsages::TEXTURE_BINDING`] for BCn formats.
- /// [`Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES`] may enable additional usages.
- ///
- /// Supported Platforms:
- /// - desktops
- ///
- /// This is a web and native feature.
- const TEXTURE_COMPRESSION_BC = 1 << 25;
- /// Enables ETC family of compressed textures. All ETC textures use 4x4 pixel blocks.
- /// ETC2 RGB and RGBA1 are 8 bytes per block. RTC2 RGBA8 and EAC are 16 bytes per block.
- ///
- /// Compressed textures sacrifice some quality in exchange for significantly reduced
- /// bandwidth usage.
- ///
- /// Support for this feature guarantees availability of [`TextureUsages::COPY_SRC | TextureUsages::COPY_DST | TextureUsages::TEXTURE_BINDING`] for ETC2 formats.
- /// [`Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES`] may enable additional usages.
- ///
- /// Supported Platforms:
- /// - Vulkan on Intel
- /// - Mobile (some)
- ///
- /// This is a web and native feature.
- const TEXTURE_COMPRESSION_ETC2 = 1 << 26;
- /// Enables ASTC family of compressed textures. ASTC textures use pixel blocks varying from 4x4 to 12x12.
- /// Blocks are always 16 bytes.
- ///
- /// Compressed textures sacrifice some quality in exchange for significantly reduced
- /// bandwidth usage.
- ///
- /// Support for this feature guarantees availability of [`TextureUsages::COPY_SRC | TextureUsages::COPY_DST | TextureUsages::TEXTURE_BINDING`] for ASTC formats with Unorm/UnormSrgb channel type.
- /// [`Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES`] may enable additional usages.
- ///
- /// Supported Platforms:
- /// - Vulkan on Intel
- /// - Mobile (some)
- ///
- /// This is a web and native feature.
- const TEXTURE_COMPRESSION_ASTC = 1 << 27;
-
- // ? const TEXTURE_COMPRESSION_ASTC_HDR = 1 << 28; (https://github.com/gpuweb/gpuweb/issues/3856)
-
- // 29..32 should be available but are for now occupied by native only texture related features
- // TEXTURE_FORMAT_16BIT_NORM & TEXTURE_COMPRESSION_ASTC_HDR will most likely become web features as well
- // TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES might not be necessary if we have all the texture features implemented
+ // Bits 11-19 available for webgpu features. Should you chose to use some of them for
+ // for native features, don't forget to update `all_webgpu_mask` and `all_native_mask`
+ // accordingly.
//
// ---- Restart Numbering for Native Features ---
@@ -438,6 +423,21 @@ bitflags::bitflags! {
// Native Features:
//
+ // The features starting with a ? are features that might become part of the spec or
+ // at the very least we can implement as native features; since they should cover all
+ // possible formats and capabilities across backends.
+ //
+ // ? const FORMATS_TIER_1 = 1 << ??; (https://github.com/gpuweb/gpuweb/issues/3837)
+ // ? const RW_STORAGE_TEXTURE_TIER_1 = 1 << ??; (https://github.com/gpuweb/gpuweb/issues/3838)
+ // ? const NORM16_FILTERABLE = 1 << ??; (https://github.com/gpuweb/gpuweb/issues/3839)
+ // ? const NORM16_RESOLVE = 1 << ??; (https://github.com/gpuweb/gpuweb/issues/3839)
+ // ? const FLOAT32_BLENDABLE = 1 << ??; (https://github.com/gpuweb/gpuweb/issues/3556)
+ // ? const 32BIT_FORMAT_MULTISAMPLE = 1 << ??; (https://github.com/gpuweb/gpuweb/issues/3844)
+ // ? const 32BIT_FORMAT_RESOLVE = 1 << ??; (https://github.com/gpuweb/gpuweb/issues/3844)
+ // ? const TEXTURE_COMPRESSION_ASTC_HDR = 1 << ??; (https://github.com/gpuweb/gpuweb/issues/3856)
+ // TEXTURE_FORMAT_16BIT_NORM & TEXTURE_COMPRESSION_ASTC_HDR will most likely become web features as well
+ // TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES might not be necessary if we have all the texture features implemented
+
// Texture Formats:
/// Enables normalized `16-bit` texture formats.
@@ -448,7 +448,7 @@ bitflags::bitflags! {
/// - Metal
///
/// This is a native only feature.
- const TEXTURE_FORMAT_16BIT_NORM = 1 << 29;
+ const TEXTURE_FORMAT_16BIT_NORM = 1 << 20;
/// Enables ASTC HDR family of compressed textures.
///
/// Compressed textures sacrifice some quality in exchange for significantly reduced
@@ -463,7 +463,7 @@ bitflags::bitflags! {
/// - OpenGL
///
/// This is a native only feature.
- const TEXTURE_COMPRESSION_ASTC_HDR = 1 << 30;
+ const TEXTURE_COMPRESSION_ASTC_HDR = 1 << 21;
/// Enables device specific texture format features.
///
/// See `TextureFormatFeatures` for a listing of the features in question.
@@ -475,7 +475,7 @@ bitflags::bitflags! {
/// This extension does not enable additional formats.
///
/// This is a native only feature.
- const TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES = 1 << 31;
+ const TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES = 1 << 22;
// API:
@@ -491,11 +491,25 @@ bitflags::bitflags! {
/// - DX12
///
/// This is a native only feature with a [proposal](https://github.com/gpuweb/gpuweb/blob/0008bd30da2366af88180b511a5d0d0c1dffbc36/proposals/pipeline-statistics-query.md) for the web.
- const PIPELINE_STATISTICS_QUERY = 1 << 32;
- /// Allows for timestamp queries inside render passes.
+ const PIPELINE_STATISTICS_QUERY = 1 << 23;
+ /// Allows for timestamp queries directly on command encoders.
///
/// Implies [`Features::TIMESTAMP_QUERY`] is supported.
///
+ /// Additionally allows for timestamp writes on command encoders
+ /// using [`CommandEncoder::write_timestamp`].
+ ///
+ /// Supported platforms:
+ /// - Vulkan
+ /// - DX12
+ /// - Metal
+ ///
+ /// This is a native only feature.
+ const TIMESTAMP_QUERY_INSIDE_ENCODERS = 1 << 24;
+ /// Allows for timestamp queries directly on command encoders.
+ ///
+ /// Implies [`Features::TIMESTAMP_QUERY`] & [`Features::TIMESTAMP_QUERY_INSIDE_ENCODERS`] is supported.
+ ///
/// Additionally allows for timestamp queries to be used inside render & compute passes using:
/// - [`RenderPassEncoder::write_timestamp`]
/// - [`ComputePassEncoder::write_timestamp`]
@@ -508,7 +522,7 @@ bitflags::bitflags! {
/// This is generally not available on tile-based rasterization GPUs.
///
/// This is a native only feature with a [proposal](https://github.com/gpuweb/gpuweb/blob/0008bd30da2366af88180b511a5d0d0c1dffbc36/proposals/timestamp-query-inside-passes.md) for the web.
- const TIMESTAMP_QUERY_INSIDE_PASSES = 1 << 33;
+ const TIMESTAMP_QUERY_INSIDE_PASSES = 1 << 25;
/// Webgpu only allows the MAP_READ and MAP_WRITE buffer usage to be matched with
/// COPY_DST and COPY_SRC respectively. This removes this requirement.
///
@@ -522,7 +536,7 @@ bitflags::bitflags! {
/// - Metal
///
/// This is a native only feature.
- const MAPPABLE_PRIMARY_BUFFERS = 1 << 34;
+ const MAPPABLE_PRIMARY_BUFFERS = 1 << 26;
/// Allows the user to create uniform arrays of textures in shaders:
///
/// ex.
@@ -545,7 +559,7 @@ bitflags::bitflags! {
/// - Vulkan
///
/// This is a native only feature.
- const TEXTURE_BINDING_ARRAY = 1 << 35;
+ const TEXTURE_BINDING_ARRAY = 1 << 27;
/// Allows the user to create arrays of buffers in shaders:
///
/// ex.
@@ -567,7 +581,7 @@ bitflags::bitflags! {
/// - Vulkan
///
/// This is a native only feature.
- const BUFFER_BINDING_ARRAY = 1 << 36;
+ const BUFFER_BINDING_ARRAY = 1 << 28;
/// Allows the user to create uniform arrays of storage buffers or textures in shaders,
/// if resp. [`Features::BUFFER_BINDING_ARRAY`] or [`Features::TEXTURE_BINDING_ARRAY`]
/// is supported.
@@ -580,7 +594,7 @@ bitflags::bitflags! {
/// - Vulkan
///
/// This is a native only feature.
- const STORAGE_RESOURCE_BINDING_ARRAY = 1 << 37;
+ const STORAGE_RESOURCE_BINDING_ARRAY = 1 << 29;
/// Allows shaders to index sampled texture and storage buffer resource arrays with dynamically non-uniform values:
///
/// ex. `texture_array[vertex_data]`
@@ -605,7 +619,7 @@ bitflags::bitflags! {
/// - Vulkan 1.2+ (or VK_EXT_descriptor_indexing)'s shaderSampledImageArrayNonUniformIndexing & shaderStorageBufferArrayNonUniformIndexing feature)
///
/// This is a native only feature.
- const SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING = 1 << 38;
+ const SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING = 1 << 30;
/// Allows shaders to index uniform buffer and storage texture resource arrays with dynamically non-uniform values:
///
/// ex. `texture_array[vertex_data]`
@@ -630,11 +644,11 @@ bitflags::bitflags! {
/// - Vulkan 1.2+ (or VK_EXT_descriptor_indexing)'s shaderUniformBufferArrayNonUniformIndexing & shaderStorageTextureArrayNonUniformIndexing feature)
///
/// This is a native only feature.
- const UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING = 1 << 39;
+ const UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING = 1 << 31;
/// Allows the user to create bind groups containing arrays with less bindings than the BindGroupLayout.
///
/// This is a native only feature.
- const PARTIALLY_BOUND_BINDING_ARRAY = 1 << 40;
+ const PARTIALLY_BOUND_BINDING_ARRAY = 1 << 32;
/// Allows the user to call [`RenderPass::multi_draw_indirect`] and [`RenderPass::multi_draw_indexed_indirect`].
///
/// Allows multiple indirect calls to be dispatched from a single buffer.
@@ -648,7 +662,7 @@ bitflags::bitflags! {
///
/// [`RenderPass::multi_draw_indirect`]: ../wgpu/struct.RenderPass.html#method.multi_draw_indirect
/// [`RenderPass::multi_draw_indexed_indirect`]: ../wgpu/struct.RenderPass.html#method.multi_draw_indexed_indirect
- const MULTI_DRAW_INDIRECT = 1 << 41;
+ const MULTI_DRAW_INDIRECT = 1 << 33;
/// Allows the user to call [`RenderPass::multi_draw_indirect_count`] and [`RenderPass::multi_draw_indexed_indirect_count`].
///
/// This allows the use of a buffer containing the actual number of draw calls.
@@ -661,7 +675,7 @@ bitflags::bitflags! {
///
/// [`RenderPass::multi_draw_indirect_count`]: ../wgpu/struct.RenderPass.html#method.multi_draw_indirect_count
/// [`RenderPass::multi_draw_indexed_indirect_count`]: ../wgpu/struct.RenderPass.html#method.multi_draw_indexed_indirect_count
- const MULTI_DRAW_INDIRECT_COUNT = 1 << 42;
+ const MULTI_DRAW_INDIRECT_COUNT = 1 << 34;
/// Allows the use of push constants: small, fast bits of memory that can be updated
/// inside a [`RenderPass`].
///
@@ -681,7 +695,7 @@ bitflags::bitflags! {
/// [`RenderPass`]: ../wgpu/struct.RenderPass.html
/// [`PipelineLayoutDescriptor`]: ../wgpu/struct.PipelineLayoutDescriptor.html
/// [`RenderPass::set_push_constants`]: ../wgpu/struct.RenderPass.html#method.set_push_constants
- const PUSH_CONSTANTS = 1 << 43;
+ const PUSH_CONSTANTS = 1 << 35;
/// Allows the use of [`AddressMode::ClampToBorder`] with a border color
/// of [`SamplerBorderColor::Zero`].
///
@@ -692,7 +706,7 @@ bitflags::bitflags! {
/// - OpenGL
///
/// This is a native only feature.
- const ADDRESS_MODE_CLAMP_TO_ZERO = 1 << 44;
+ const ADDRESS_MODE_CLAMP_TO_ZERO = 1 << 36;
/// Allows the use of [`AddressMode::ClampToBorder`] with a border color
/// other than [`SamplerBorderColor::Zero`].
///
@@ -703,7 +717,7 @@ bitflags::bitflags! {
/// - OpenGL
///
/// This is a native only feature.
- const ADDRESS_MODE_CLAMP_TO_BORDER = 1 << 45;
+ const ADDRESS_MODE_CLAMP_TO_BORDER = 1 << 37;
/// Allows the user to set [`PolygonMode::Line`] in [`PrimitiveState::polygon_mode`]
///
/// This allows drawing polygons/triangles as lines (wireframe) instead of filled
@@ -714,7 +728,7 @@ bitflags::bitflags! {
/// - Metal
///
/// This is a native only feature.
- const POLYGON_MODE_LINE = 1 << 46;
+ const POLYGON_MODE_LINE = 1 << 38;
/// Allows the user to set [`PolygonMode::Point`] in [`PrimitiveState::polygon_mode`]
///
/// This allows only drawing the vertices of polygons/triangles instead of filled
@@ -723,7 +737,7 @@ bitflags::bitflags! {
/// - Vulkan
///
/// This is a native only feature.
- const POLYGON_MODE_POINT = 1 << 47;
+ const POLYGON_MODE_POINT = 1 << 39;
/// Allows the user to set a overestimation-conservative-rasterization in [`PrimitiveState::conservative`]
///
/// Processing of degenerate triangles/lines is hardware specific.
@@ -733,7 +747,7 @@ bitflags::bitflags! {
/// - Vulkan
///
/// This is a native only feature.
- const CONSERVATIVE_RASTERIZATION = 1 << 48;
+ const CONSERVATIVE_RASTERIZATION = 1 << 40;
/// Enables bindings of writable storage buffers and textures visible to vertex shaders.
///
/// Note: some (tiled-based) platforms do not support vertex shaders with any side-effects.
@@ -742,14 +756,14 @@ bitflags::bitflags! {
/// - All
///
/// This is a native only feature.
- const VERTEX_WRITABLE_STORAGE = 1 << 49;
+ const VERTEX_WRITABLE_STORAGE = 1 << 41;
/// Enables clear to zero for textures.
///
/// Supported platforms:
/// - All
///
/// This is a native only feature.
- const CLEAR_TEXTURE = 1 << 50;
+ const CLEAR_TEXTURE = 1 << 42;
/// Enables creating shader modules from SPIR-V binary data (unsafe).
///
/// SPIR-V data is not parsed or interpreted in any way; you can use
@@ -761,7 +775,7 @@ bitflags::bitflags! {
/// Vulkan implementation.
///
/// This is a native only feature.
- const SPIRV_SHADER_PASSTHROUGH = 1 << 51;
+ const SPIRV_SHADER_PASSTHROUGH = 1 << 43;
/// Enables multiview render passes and `builtin(view_index)` in vertex shaders.
///
/// Supported platforms:
@@ -769,7 +783,7 @@ bitflags::bitflags! {
/// - OpenGL (web only)
///
/// This is a native only feature.
- const MULTIVIEW = 1 << 52;
+ const MULTIVIEW = 1 << 44;
/// Enables using 64-bit types for vertex attributes.
///
/// Requires SHADER_FLOAT64.
@@ -777,7 +791,7 @@ bitflags::bitflags! {
/// Supported Platforms: N/A
///
/// This is a native only feature.
- const VERTEX_ATTRIBUTE_64BIT = 1 << 53;
+ const VERTEX_ATTRIBUTE_64BIT = 1 << 45;
/// Allows vertex shaders to have outputs which are not consumed
/// by the fragment shader.
///
@@ -785,7 +799,7 @@ bitflags::bitflags! {
/// - Vulkan
/// - Metal
/// - OpenGL
- const SHADER_UNUSED_VERTEX_OUTPUT = 1 << 54;
+ const SHADER_UNUSED_VERTEX_OUTPUT = 1 << 46;
/// Allows for creation of textures of format [`TextureFormat::NV12`]
///
/// Supported platforms:
@@ -793,16 +807,14 @@ bitflags::bitflags! {
/// - Vulkan
///
/// This is a native only feature.
- const TEXTURE_FORMAT_NV12 = 1 << 55;
+ const TEXTURE_FORMAT_NV12 = 1 << 47;
/// Allows for the creation of ray-tracing acceleration structures.
///
/// Supported platforms:
/// - Vulkan
///
/// This is a native-only feature.
- const RAY_TRACING_ACCELERATION_STRUCTURE = 1 << 56;
-
- // 57 available
+ const RAY_TRACING_ACCELERATION_STRUCTURE = 1 << 48;
// Shader:
@@ -812,7 +824,7 @@ bitflags::bitflags! {
/// - Vulkan
///
/// This is a native-only feature.
- const RAY_QUERY = 1 << 58;
+ const RAY_QUERY = 1 << 49;
/// Enables 64-bit floating point types in SPIR-V shaders.
///
/// Note: even when supported by GPU hardware, 64-bit floating point operations are
@@ -822,14 +834,14 @@ bitflags::bitflags! {
/// - Vulkan
///
/// This is a native only feature.
- const SHADER_F64 = 1 << 59;
+ const SHADER_F64 = 1 << 50;
/// Allows shaders to use i16. Not currently supported in `naga`, only available through `spirv-passthrough`.
///
/// Supported platforms:
/// - Vulkan
///
/// This is a native only feature.
- const SHADER_I16 = 1 << 60;
+ const SHADER_I16 = 1 << 51;
/// Enables `builtin(primitive_index)` in fragment shaders.
///
/// Note: enables geometry processing for pipelines using the builtin.
@@ -843,14 +855,14 @@ bitflags::bitflags! {
/// - OpenGL (some)
///
/// This is a native only feature.
- const SHADER_PRIMITIVE_INDEX = 1 << 61;
+ const SHADER_PRIMITIVE_INDEX = 1 << 52;
/// Allows shaders to use the `early_depth_test` attribute.
///
/// Supported platforms:
/// - GLES 3.1+
///
/// This is a native only feature.
- const SHADER_EARLY_DEPTH_TEST = 1 << 62;
+ const SHADER_EARLY_DEPTH_TEST = 1 << 53;
/// Allows two outputs from a shader to be used for blending.
/// Note that dual-source blending doesn't support multiple render targets.
///
@@ -861,7 +873,16 @@ bitflags::bitflags! {
/// - Metal (with MSL 1.2+)
/// - Vulkan (with dualSrcBlend)
/// - DX12
- const DUAL_SOURCE_BLENDING = 1 << 63;
+ const DUAL_SOURCE_BLENDING = 1 << 54;
+ /// Allows shaders to use i64 and u64.
+ ///
+ /// Supported platforms:
+ /// - Vulkan
+ /// - DX12 (DXC only)
+ /// - Metal (with MSL 2.3+)
+ ///
+ /// This is a native only feature.
+ const SHADER_INT64 = 1 << 55;
}
}
@@ -870,12 +891,12 @@ impl_bitflags!(Features);
impl Features {
/// Mask of all features which are part of the upstream WebGPU standard.
pub const fn all_webgpu_mask() -> Self {
- Self::from_bits_truncate(0x0000_0000_0000_FFFF)
+ Self::from_bits_truncate(0xFFFFF)
}
/// Mask of all features that are only available when targeting native (not web).
pub const fn all_native_mask() -> Self {
- Self::from_bits_truncate(0xFFFF_FFFF_FFFF_0000)
+ Self::from_bits_truncate(!Self::all_webgpu_mask().bits())
}
}
@@ -904,13 +925,15 @@ bitflags::bitflags! {
/// This mainly applies to a Vulkan driver's compliance version. If the major compliance version
/// is `0`, then the driver is ignored. This flag allows that driver to be enabled for testing.
const ALLOW_UNDERLYING_NONCOMPLIANT_ADAPTER = 1 << 3;
- /// Enable GPU-based validation. Currently, this only changes behavior on the DX12
- /// backend.
+ /// Enable GPU-based validation. Implies [`Self::VALIDATION`]. Currently, this only changes
+ /// behavior on the DX12 and Vulkan backends.
///
/// Supported platforms:
///
/// - D3D12; called ["GPU-based validation", or
/// "GBV"](https://web.archive.org/web/20230206120404/https://learn.microsoft.com/en-us/windows/win32/direct3d12/using-d3d12-debug-layer-gpu-based-validation)
+ /// - Vulkan, via the `VK_LAYER_KHRONOS_validation` layer; called ["GPU-Assisted
+ /// Validation"](https://github.com/KhronosGroup/Vulkan-ValidationLayers/blob/e45aeb85079e0835694cb8f03e6681fd18ae72c9/docs/gpu_validation.md#gpu-assisted-validation)
const GPU_BASED_VALIDATION = 1 << 4;
}
}
@@ -924,7 +947,12 @@ impl Default for InstanceFlags {
impl InstanceFlags {
/// Enable recommended debugging and validation flags.
pub fn debugging() -> Self {
- InstanceFlags::DEBUG | InstanceFlags::VALIDATION | InstanceFlags::GPU_BASED_VALIDATION
+ InstanceFlags::DEBUG | InstanceFlags::VALIDATION
+ }
+
+ /// Enable advanced debugging and validation flags (potentially very slow).
+ pub fn advanced_debugging() -> Self {
+ Self::debugging() | InstanceFlags::GPU_BASED_VALIDATION
}
/// Infer good defaults from the build type
@@ -1078,6 +1106,11 @@ pub struct Limits {
/// inter-stage communication (vertex outputs to fragment inputs). Defaults to 60.
/// Higher is "better".
pub max_inter_stage_shader_components: u32,
+ /// The maximum allowed number of color attachments.
+ pub max_color_attachments: u32,
+ /// The maximum number of bytes necessary to hold one sample (pixel or subpixel) of render
+ /// pipeline output data, across all color attachments.
+ pub max_color_attachment_bytes_per_sample: u32,
/// Maximum number of bytes used for workgroup memory in a compute entry point. Defaults to
/// 16352. Higher is "better".
pub max_compute_workgroup_storage_size: u32,
@@ -1139,6 +1172,8 @@ impl Default for Limits {
min_uniform_buffer_offset_alignment: 256,
min_storage_buffer_offset_alignment: 256,
max_inter_stage_shader_components: 60,
+ max_color_attachments: 8,
+ max_color_attachment_bytes_per_sample: 32,
max_compute_workgroup_storage_size: 16384,
max_compute_invocations_per_workgroup: 256,
max_compute_workgroup_size_x: 256,
@@ -1180,6 +1215,8 @@ impl Limits {
/// min_uniform_buffer_offset_alignment: 256,
/// min_storage_buffer_offset_alignment: 256,
/// max_inter_stage_shader_components: 60,
+ /// max_color_attachments: 8,
+ /// max_color_attachment_bytes_per_sample: 32,
/// max_compute_workgroup_storage_size: 16352,
/// max_compute_invocations_per_workgroup: 256,
/// max_compute_workgroup_size_x: 256,
@@ -1190,7 +1227,7 @@ impl Limits {
/// max_non_sampler_bindings: 1_000_000,
/// });
/// ```
- pub fn downlevel_defaults() -> Self {
+ pub const fn downlevel_defaults() -> Self {
Self {
max_texture_dimension_1d: 2048,
max_texture_dimension_2d: 2048,
@@ -1214,6 +1251,8 @@ impl Limits {
min_uniform_buffer_offset_alignment: 256,
min_storage_buffer_offset_alignment: 256,
max_inter_stage_shader_components: 60,
+ max_color_attachments: 8,
+ max_color_attachment_bytes_per_sample: 32,
max_compute_workgroup_storage_size: 16352,
max_compute_invocations_per_workgroup: 256,
max_compute_workgroup_size_x: 256,
@@ -1254,6 +1293,8 @@ impl Limits {
/// min_uniform_buffer_offset_alignment: 256,
/// min_storage_buffer_offset_alignment: 256,
/// max_inter_stage_shader_components: 31,
+ /// max_color_attachments: 8,
+ /// max_color_attachment_bytes_per_sample: 32,
/// max_compute_workgroup_storage_size: 0, // +
/// max_compute_invocations_per_workgroup: 0, // +
/// max_compute_workgroup_size_x: 0, // +
@@ -1264,7 +1305,7 @@ impl Limits {
/// max_non_sampler_bindings: 1_000_000,
/// });
/// ```
- pub fn downlevel_webgl2_defaults() -> Self {
+ pub const fn downlevel_webgl2_defaults() -> Self {
Self {
max_uniform_buffers_per_shader_stage: 11,
max_storage_buffers_per_shader_stage: 0,
@@ -1292,7 +1333,7 @@ impl Limits {
/// This is useful because the swapchain might need to be larger than any other image in the application.
///
/// If your application only needs 512x512, you might be running on a 4k display and need extremely high resolution limits.
- pub fn using_resolution(self, other: Self) -> Self {
+ pub const fn using_resolution(self, other: Self) -> Self {
Self {
max_texture_dimension_1d: other.max_texture_dimension_1d,
max_texture_dimension_2d: other.max_texture_dimension_2d,
@@ -1304,7 +1345,7 @@ impl Limits {
/// Modify the current limits to use the buffer alignment limits of the adapter.
///
/// This is useful for when you'd like to dynamically use the "best" supported buffer alignments.
- pub fn using_alignment(self, other: Self) -> Self {
+ pub const fn using_alignment(self, other: Self) -> Self {
Self {
min_uniform_buffer_offset_alignment: other.min_uniform_buffer_offset_alignment,
min_storage_buffer_offset_alignment: other.min_storage_buffer_offset_alignment,
@@ -2098,6 +2139,9 @@ pub struct PrimitiveState {
pub topology: PrimitiveTopology,
/// When drawing strip topologies with indices, this is the required format for the index buffer.
/// This has no effect on non-indexed or non-strip draws.
+ ///
+ /// Specifying this value enables primitive restart, allowing individual strips to be separated
+ /// with the index value `0xFFFF` when using `Uint16`, or `0xFFFFFFFF` when using `Uint32`.
#[cfg_attr(feature = "serde", serde(default))]
pub strip_index_format: Option<IndexFormat>,
/// The face to consider the front for the purpose of culling and stencil operations.
@@ -3522,6 +3566,87 @@ impl TextureFormat {
}
}
+ /// The number of bytes occupied per pixel in a color attachment
+ /// <https://gpuweb.github.io/gpuweb/#render-target-pixel-byte-cost>
+ pub fn target_pixel_byte_cost(&self) -> Option<u32> {
+ match *self {
+ Self::R8Unorm | Self::R8Uint | Self::R8Sint => Some(1),
+ Self::Rg8Unorm
+ | Self::Rg8Uint
+ | Self::Rg8Sint
+ | Self::R16Uint
+ | Self::R16Sint
+ | Self::R16Float => Some(2),
+ Self::Rgba8Uint
+ | Self::Rgba8Sint
+ | Self::Rg16Uint
+ | Self::Rg16Sint
+ | Self::Rg16Float
+ | Self::R32Uint
+ | Self::R32Sint
+ | Self::R32Float => Some(4),
+ Self::Rgba8Unorm
+ | Self::Rgba8UnormSrgb
+ | Self::Bgra8Unorm
+ | Self::Bgra8UnormSrgb
+ | Self::Rgba16Uint
+ | Self::Rgba16Sint
+ | Self::Rgba16Float
+ | Self::Rg32Uint
+ | Self::Rg32Sint
+ | Self::Rg32Float
+ | Self::Rgb10a2Uint
+ | Self::Rgb10a2Unorm
+ | Self::Rg11b10Float => Some(8),
+ Self::Rgba32Uint | Self::Rgba32Sint | Self::Rgba32Float => Some(16),
+ Self::Rgba8Snorm | Self::Rg8Snorm | Self::R8Snorm => None,
+ _ => None,
+ }
+ }
+
+ /// See <https://gpuweb.github.io/gpuweb/#render-target-component-alignment>
+ pub fn target_component_alignment(&self) -> Option<u32> {
+ match self {
+ Self::R8Unorm
+ | Self::R8Snorm
+ | Self::R8Uint
+ | Self::R8Sint
+ | Self::Rg8Unorm
+ | Self::Rg8Snorm
+ | Self::Rg8Uint
+ | Self::Rg8Sint
+ | Self::Rgba8Unorm
+ | Self::Rgba8UnormSrgb
+ | Self::Rgba8Snorm
+ | Self::Rgba8Uint
+ | Self::Rgba8Sint
+ | Self::Bgra8Unorm
+ | Self::Bgra8UnormSrgb => Some(1),
+ Self::R16Uint
+ | Self::R16Sint
+ | Self::R16Float
+ | Self::Rg16Uint
+ | Self::Rg16Sint
+ | Self::Rg16Float
+ | Self::Rgba16Uint
+ | Self::Rgba16Sint
+ | Self::Rgba16Float => Some(2),
+ Self::R32Uint
+ | Self::R32Sint
+ | Self::R32Float
+ | Self::Rg32Uint
+ | Self::Rg32Sint
+ | Self::Rg32Float
+ | Self::Rgba32Uint
+ | Self::Rgba32Sint
+ | Self::Rgba32Float
+ | Self::Rgb10a2Uint
+ | Self::Rgb10a2Unorm
+ | Self::Rg11b10Float => Some(4),
+ _ => None,
+ }
+ }
+
/// Returns the number of components this format has.
pub fn components(&self) -> u8 {
self.components_with_aspect(TextureAspect::All)
diff --git a/third_party/sqlite3/ext/fts5.c b/third_party/sqlite3/ext/fts5.c
index 1660c182d3..09a61d5556 100644
--- a/third_party/sqlite3/ext/fts5.c
+++ b/third_party/sqlite3/ext/fts5.c
@@ -16004,23 +16004,26 @@ static void fts5IterSetOutputsTokendata(Fts5Iter *pIter){
static void fts5TokendataIterNext(Fts5Iter *pIter, int bFrom, i64 iFrom){
int ii;
Fts5TokenDataIter *pT = pIter->pTokenDataIter;
+ Fts5Index *pIndex = pIter->pIndex;
for(ii=0; ii<pT->nIter; ii++){
Fts5Iter *p = pT->apIter[ii];
if( p->base.bEof==0
&& (p->base.iRowid==pIter->base.iRowid || (bFrom && p->base.iRowid<iFrom))
){
- fts5MultiIterNext(p->pIndex, p, bFrom, iFrom);
+ fts5MultiIterNext(pIndex, p, bFrom, iFrom);
while( bFrom && p->base.bEof==0
&& p->base.iRowid<iFrom
- && p->pIndex->rc==SQLITE_OK
+ && pIndex->rc==SQLITE_OK
){
- fts5MultiIterNext(p->pIndex, p, 0, 0);
+ fts5MultiIterNext(pIndex, p, 0, 0);
}
}
}
- fts5IterSetOutputsTokendata(pIter);
+ if( pIndex->rc==SQLITE_OK ){
+ fts5IterSetOutputsTokendata(pIter);
+ }
}
/*
@@ -21175,7 +21178,7 @@ static void fts5SourceIdFunc(
){
assert( nArg==0 );
UNUSED_PARAM2(nArg, apUnused);
- sqlite3_result_text(pCtx, "fts5: 2024-01-30 16:01:20 e876e51a0ed5c5b3126f52e532044363a014bc594cfefa87ffb5b82257cc467a", -1, SQLITE_TRANSIENT);
+ sqlite3_result_text(pCtx, "fts5: 2024-03-12 11:06:23 d8cd6d49b46a395b13955387d05e9e1a2a47e54fb99f3c9b59835bbefad6af77", -1, SQLITE_TRANSIENT);
}
/*
diff --git a/third_party/sqlite3/moz.yaml b/third_party/sqlite3/moz.yaml
index 4860eaf52b..c97bfc3277 100644
--- a/third_party/sqlite3/moz.yaml
+++ b/third_party/sqlite3/moz.yaml
@@ -10,8 +10,8 @@ origin:
SQLite is a C-language library that implements a small, fast,
self-contained, high-reliability, full-featured, SQL database engine.
url: https://www.sqlite.org/
- release: vesion-3.45.1 (2024-01-30T16:01:20+00:00).
- revision: vesion-3.45.1
+ release: version-3.45.2 (2024-03-12T11:06:23+00:00).
+ revision: version-3.45.2
license: Public Domain
license-file: LICENSE.md
notes: >
diff --git a/third_party/sqlite3/src/VERSION.txt b/third_party/sqlite3/src/VERSION.txt
index 08d7ea82ba..caa72fa198 100644
--- a/third_party/sqlite3/src/VERSION.txt
+++ b/third_party/sqlite3/src/VERSION.txt
@@ -1 +1 @@
-3.45.1
+3.45.2
diff --git a/third_party/sqlite3/src/sqlite3.c b/third_party/sqlite3/src/sqlite3.c
index 139ee46a6a..55ca309401 100644
--- a/third_party/sqlite3/src/sqlite3.c
+++ b/third_party/sqlite3/src/sqlite3.c
@@ -1,6 +1,6 @@
/******************************************************************************
** This file is an amalgamation of many separate C source files from SQLite
-** version 3.45.1. By combining all the individual C code files into this
+** version 3.45.2. By combining all the individual C code files into this
** single large file, the entire code can be compiled as a single translation
** unit. This allows many compilers to do optimizations that would not be
** possible if the files were compiled separately. Performance improvements
@@ -18,7 +18,7 @@
** separate file. This file contains only code for the core SQLite library.
**
** The content in this amalgamation comes from Fossil check-in
-** e876e51a0ed5c5b3126f52e532044363a014.
+** d8cd6d49b46a395b13955387d05e9e1a2a47.
*/
#define SQLITE_CORE 1
#define SQLITE_AMALGAMATION 1
@@ -459,9 +459,9 @@ extern "C" {
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
-#define SQLITE_VERSION "3.45.1"
-#define SQLITE_VERSION_NUMBER 3045001
-#define SQLITE_SOURCE_ID "2024-01-30 16:01:20 e876e51a0ed5c5b3126f52e532044363a014bc594cfefa87ffb5b82257cc467a"
+#define SQLITE_VERSION "3.45.2"
+#define SQLITE_VERSION_NUMBER 3045002
+#define SQLITE_SOURCE_ID "2024-03-12 11:06:23 d8cd6d49b46a395b13955387d05e9e1a2a47e54fb99f3c9b59835bbefad6af77"
/*
** CAPI3REF: Run-Time Library Version Numbers
@@ -733,6 +733,8 @@ typedef int (*sqlite3_callback)(void*,int,char**, char**);
** the 1st parameter to sqlite3_exec() while sqlite3_exec() is running.
** <li> The application must not modify the SQL statement text passed into
** the 2nd parameter of sqlite3_exec() while sqlite3_exec() is running.
+** <li> The application must not dereference the arrays or string pointers
+** passed as the 3rd and 4th callback parameters after it returns.
** </ul>
*/
SQLITE_API int sqlite3_exec(
@@ -15097,6 +15099,7 @@ SQLITE_PRIVATE u32 sqlite3TreeTrace;
** 0x00010000 Beginning of DELETE/INSERT/UPDATE processing
** 0x00020000 Transform DISTINCT into GROUP BY
** 0x00040000 SELECT tree dump after all code has been generated
+** 0x00080000 NOT NULL strength reduction
*/
/*
@@ -19346,6 +19349,7 @@ struct NameContext {
#define NC_InAggFunc 0x020000 /* True if analyzing arguments to an agg func */
#define NC_FromDDL 0x040000 /* SQL text comes from sqlite_schema */
#define NC_NoSelect 0x080000 /* Do not descend into sub-selects */
+#define NC_Where 0x100000 /* Processing WHERE clause of a SELECT */
#define NC_OrderAgg 0x8000000 /* Has an aggregate other than count/min/max */
/*
@@ -19369,6 +19373,7 @@ struct Upsert {
Expr *pUpsertWhere; /* WHERE clause for the ON CONFLICT UPDATE */
Upsert *pNextUpsert; /* Next ON CONFLICT clause in the list */
u8 isDoUpdate; /* True for DO UPDATE. False for DO NOTHING */
+ u8 isDup; /* True if 2nd or later with same pUpsertIdx */
/* Above this point is the parse tree for the ON CONFLICT clauses.
** The next group of fields stores intermediate data. */
void *pToFree; /* Free memory when deleting the Upsert object */
@@ -21444,7 +21449,7 @@ SQLITE_PRIVATE With *sqlite3WithPush(Parse*, With*, u8);
SQLITE_PRIVATE Upsert *sqlite3UpsertNew(sqlite3*,ExprList*,Expr*,ExprList*,Expr*,Upsert*);
SQLITE_PRIVATE void sqlite3UpsertDelete(sqlite3*,Upsert*);
SQLITE_PRIVATE Upsert *sqlite3UpsertDup(sqlite3*,Upsert*);
-SQLITE_PRIVATE int sqlite3UpsertAnalyzeTarget(Parse*,SrcList*,Upsert*);
+SQLITE_PRIVATE int sqlite3UpsertAnalyzeTarget(Parse*,SrcList*,Upsert*,Upsert*);
SQLITE_PRIVATE void sqlite3UpsertDoUpdate(Parse*,Upsert*,Table*,Index*,int);
SQLITE_PRIVATE Upsert *sqlite3UpsertOfIndex(Upsert*,Index*);
SQLITE_PRIVATE int sqlite3UpsertNextIsIPK(Upsert*);
@@ -31309,6 +31314,7 @@ SQLITE_API void sqlite3_str_vappendf(
if( xtype==etFLOAT ){
iRound = -precision;
}else if( xtype==etGENERIC ){
+ if( precision==0 ) precision = 1;
iRound = precision;
}else{
iRound = precision+1;
@@ -35199,6 +35205,9 @@ do_atof_calc:
u64 s2;
rr[0] = (double)s;
s2 = (u64)rr[0];
+#if defined(_MSC_VER) && _MSC_VER<1700
+ if( s2==0x8000000000000000LL ){ s2 = 2*(u64)(0.5*rr[0]); }
+#endif
rr[1] = s>=s2 ? (double)(s - s2) : -(double)(s2 - s);
if( e>0 ){
while( e>=100 ){
@@ -35641,7 +35650,7 @@ SQLITE_PRIVATE void sqlite3FpDecode(FpDecode *p, double r, int iRound, int mxRou
assert( p->n>0 );
assert( p->n<sizeof(p->zBuf) );
p->iDP = p->n + exp;
- if( iRound<0 ){
+ if( iRound<=0 ){
iRound = p->iDP - iRound;
if( iRound==0 && p->zBuf[i+1]>='5' ){
iRound = 1;
@@ -53262,6 +53271,14 @@ SQLITE_API unsigned char *sqlite3_serialize(
pOut = 0;
}else{
sz = sqlite3_column_int64(pStmt, 0)*szPage;
+ if( sz==0 ){
+ sqlite3_reset(pStmt);
+ sqlite3_exec(db, "BEGIN IMMEDIATE; COMMIT;", 0, 0, 0);
+ rc = sqlite3_step(pStmt);
+ if( rc==SQLITE_ROW ){
+ sz = sqlite3_column_int64(pStmt, 0)*szPage;
+ }
+ }
if( piSize ) *piSize = sz;
if( mFlags & SQLITE_SERIALIZE_NOCOPY ){
pOut = 0;
@@ -77088,7 +77105,10 @@ static int fillInCell(
n = nHeader + nPayload;
testcase( n==3 );
testcase( n==4 );
- if( n<4 ) n = 4;
+ if( n<4 ){
+ n = 4;
+ pPayload[nPayload] = 0;
+ }
*pnSize = n;
assert( nSrc<=nPayload );
testcase( nSrc<nPayload );
@@ -79534,7 +79554,10 @@ SQLITE_PRIVATE int sqlite3BtreeInsert(
if( flags & BTREE_PREFORMAT ){
rc = SQLITE_OK;
szNew = p->pBt->nPreformatSize;
- if( szNew<4 ) szNew = 4;
+ if( szNew<4 ){
+ szNew = 4;
+ newCell[3] = 0;
+ }
if( ISAUTOVACUUM(p->pBt) && szNew>pPage->maxLocal ){
CellInfo info;
pPage->xParseCell(pPage, newCell, &info);
@@ -88379,6 +88402,23 @@ static void serialGet(
pMem->flags = IsNaN(x) ? MEM_Null : MEM_Real;
}
}
+static int serialGet7(
+ const unsigned char *buf, /* Buffer to deserialize from */
+ Mem *pMem /* Memory cell to write value into */
+){
+ u64 x = FOUR_BYTE_UINT(buf);
+ u32 y = FOUR_BYTE_UINT(buf+4);
+ x = (x<<32) + y;
+ assert( sizeof(x)==8 && sizeof(pMem->u.r)==8 );
+ swapMixedEndianFloat(x);
+ memcpy(&pMem->u.r, &x, sizeof(x));
+ if( IsNaN(x) ){
+ pMem->flags = MEM_Null;
+ return 1;
+ }
+ pMem->flags = MEM_Real;
+ return 0;
+}
SQLITE_PRIVATE void sqlite3VdbeSerialGet(
const unsigned char *buf, /* Buffer to deserialize from */
u32 serial_type, /* Serial type to deserialize */
@@ -89058,7 +89098,7 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip(
}else if( serial_type==0 ){
rc = -1;
}else if( serial_type==7 ){
- sqlite3VdbeSerialGet(&aKey1[d1], serial_type, &mem1);
+ serialGet7(&aKey1[d1], &mem1);
rc = -sqlite3IntFloatCompare(pRhs->u.i, mem1.u.r);
}else{
i64 lhs = vdbeRecordDecodeInt(serial_type, &aKey1[d1]);
@@ -89083,14 +89123,18 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip(
}else if( serial_type==0 ){
rc = -1;
}else{
- sqlite3VdbeSerialGet(&aKey1[d1], serial_type, &mem1);
if( serial_type==7 ){
- if( mem1.u.r<pRhs->u.r ){
+ if( serialGet7(&aKey1[d1], &mem1) ){
+ rc = -1; /* mem1 is a NaN */
+ }else if( mem1.u.r<pRhs->u.r ){
rc = -1;
}else if( mem1.u.r>pRhs->u.r ){
rc = +1;
+ }else{
+ assert( rc==0 );
}
}else{
+ sqlite3VdbeSerialGet(&aKey1[d1], serial_type, &mem1);
rc = sqlite3IntFloatCompare(mem1.u.i, pRhs->u.r);
}
}
@@ -89160,7 +89204,14 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip(
/* RHS is null */
else{
serial_type = aKey1[idx1];
- rc = (serial_type!=0 && serial_type!=10);
+ if( serial_type==0
+ || serial_type==10
+ || (serial_type==7 && serialGet7(&aKey1[d1], &mem1)!=0)
+ ){
+ assert( rc==0 );
+ }else{
+ rc = 1;
+ }
}
if( rc!=0 ){
@@ -94858,7 +94909,9 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
}
}
}else if( affinity==SQLITE_AFF_TEXT && ((flags1 | flags3) & MEM_Str)!=0 ){
- if( (flags1 & MEM_Str)==0 && (flags1&(MEM_Int|MEM_Real|MEM_IntReal))!=0 ){
+ if( (flags1 & MEM_Str)!=0 ){
+ pIn1->flags &= ~(MEM_Int|MEM_Real|MEM_IntReal);
+ }else if( (flags1&(MEM_Int|MEM_Real|MEM_IntReal))!=0 ){
testcase( pIn1->flags & MEM_Int );
testcase( pIn1->flags & MEM_Real );
testcase( pIn1->flags & MEM_IntReal );
@@ -94867,7 +94920,9 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
flags1 = (pIn1->flags & ~MEM_TypeMask) | (flags1 & MEM_TypeMask);
if( NEVER(pIn1==pIn3) ) flags3 = flags1 | MEM_Str;
}
- if( (flags3 & MEM_Str)==0 && (flags3&(MEM_Int|MEM_Real|MEM_IntReal))!=0 ){
+ if( (flags3 & MEM_Str)!=0 ){
+ pIn3->flags &= ~(MEM_Int|MEM_Real|MEM_IntReal);
+ }else if( (flags3&(MEM_Int|MEM_Real|MEM_IntReal))!=0 ){
testcase( pIn3->flags & MEM_Int );
testcase( pIn3->flags & MEM_Real );
testcase( pIn3->flags & MEM_IntReal );
@@ -106212,6 +106267,8 @@ static void resolveAlias(
assert( iCol>=0 && iCol<pEList->nExpr );
pOrig = pEList->a[iCol].pExpr;
assert( pOrig!=0 );
+ assert( !ExprHasProperty(pExpr, EP_Reduced|EP_TokenOnly) );
+ if( pExpr->pAggInfo ) return;
db = pParse->db;
pDup = sqlite3ExprDup(db, pOrig, 0);
if( db->mallocFailed ){
@@ -107097,6 +107154,19 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
** resolved. This prevents "column" from being counted as having been
** referenced, which might prevent a SELECT from being erroneously
** marked as correlated.
+ **
+ ** 2024-03-28: Beware of aggregates. A bare column of aggregated table
+ ** can still evaluate to NULL even though it is marked as NOT NULL.
+ ** Example:
+ **
+ ** CREATE TABLE t1(a INT NOT NULL);
+ ** SELECT a, a IS NULL, a IS NOT NULL, count(*) FROM t1;
+ **
+ ** The "a IS NULL" and "a IS NOT NULL" expressions cannot be optimized
+ ** here because at the time this case is hit, we do not yet know whether
+ ** or not t1 is being aggregated. We have to assume the worst and omit
+ ** the optimization. The only time it is safe to apply this optimization
+ ** is within the WHERE clause.
*/
case TK_NOTNULL:
case TK_ISNULL: {
@@ -107107,19 +107177,36 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
anRef[i] = p->nRef;
}
sqlite3WalkExpr(pWalker, pExpr->pLeft);
- if( 0==sqlite3ExprCanBeNull(pExpr->pLeft) && !IN_RENAME_OBJECT ){
- testcase( ExprHasProperty(pExpr, EP_OuterON) );
- assert( !ExprHasProperty(pExpr, EP_IntValue) );
- pExpr->u.iValue = (pExpr->op==TK_NOTNULL);
- pExpr->flags |= EP_IntValue;
- pExpr->op = TK_INTEGER;
+ if( IN_RENAME_OBJECT ) return WRC_Prune;
+ if( sqlite3ExprCanBeNull(pExpr->pLeft) ){
+ /* The expression can be NULL. So the optimization does not apply */
+ return WRC_Prune;
+ }
- for(i=0, p=pNC; p && i<ArraySize(anRef); p=p->pNext, i++){
- p->nRef = anRef[i];
+ for(i=0, p=pNC; p; p=p->pNext, i++){
+ if( (p->ncFlags & NC_Where)==0 ){
+ return WRC_Prune; /* Not in a WHERE clause. Unsafe to optimize. */
}
- sqlite3ExprDelete(pParse->db, pExpr->pLeft);
- pExpr->pLeft = 0;
}
+ testcase( ExprHasProperty(pExpr, EP_OuterON) );
+ assert( !ExprHasProperty(pExpr, EP_IntValue) );
+#if TREETRACE_ENABLED
+ if( sqlite3TreeTrace & 0x80000 ){
+ sqlite3DebugPrintf(
+ "NOT NULL strength reduction converts the following to %d:\n",
+ pExpr->op==TK_NOTNULL
+ );
+ sqlite3ShowExpr(pExpr);
+ }
+#endif /* TREETRACE_ENABLED */
+ pExpr->u.iValue = (pExpr->op==TK_NOTNULL);
+ pExpr->flags |= EP_IntValue;
+ pExpr->op = TK_INTEGER;
+ for(i=0, p=pNC; p && i<ArraySize(anRef); p=p->pNext, i++){
+ p->nRef = anRef[i];
+ }
+ sqlite3ExprDelete(pParse->db, pExpr->pLeft);
+ pExpr->pLeft = 0;
return WRC_Prune;
}
@@ -108019,7 +108106,9 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
}
if( sqlite3ResolveExprNames(&sNC, p->pHaving) ) return WRC_Abort;
}
+ sNC.ncFlags |= NC_Where;
if( sqlite3ResolveExprNames(&sNC, p->pWhere) ) return WRC_Abort;
+ sNC.ncFlags &= ~NC_Where;
/* Resolve names in table-valued-function arguments */
for(i=0; i<p->pSrc->nSrc; i++){
@@ -128947,13 +129036,13 @@ SQLITE_PRIVATE void sqlite3QuoteValue(StrAccum *pStr, sqlite3_value *pValue){
double r1, r2;
const char *zVal;
r1 = sqlite3_value_double(pValue);
- sqlite3_str_appendf(pStr, "%!.15g", r1);
+ sqlite3_str_appendf(pStr, "%!0.15g", r1);
zVal = sqlite3_str_value(pStr);
if( zVal ){
sqlite3AtoF(zVal, &r2, pStr->nChar, SQLITE_UTF8);
if( r1!=r2 ){
sqlite3_str_reset(pStr);
- sqlite3_str_appendf(pStr, "%!.20e", r1);
+ sqlite3_str_appendf(pStr, "%!0.20e", r1);
}
}
break;
@@ -129255,7 +129344,7 @@ static void replaceFunc(
}
if( zPattern[0]==0 ){
assert( sqlite3_value_type(argv[1])!=SQLITE_NULL );
- sqlite3_result_value(context, argv[0]);
+ sqlite3_result_text(context, (const char*)zStr, nStr, SQLITE_TRANSIENT);
return;
}
nPattern = sqlite3_value_bytes(argv[1]);
@@ -133175,7 +133264,7 @@ SQLITE_PRIVATE void sqlite3Insert(
pNx->iDataCur = iDataCur;
pNx->iIdxCur = iIdxCur;
if( pNx->pUpsertTarget ){
- if( sqlite3UpsertAnalyzeTarget(pParse, pTabList, pNx) ){
+ if( sqlite3UpsertAnalyzeTarget(pParse, pTabList, pNx, pUpsert) ){
goto insert_cleanup;
}
}
@@ -139474,31 +139563,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
int mxCol; /* Maximum non-virtual column number */
if( pObjTab && pObjTab!=pTab ) continue;
- if( !IsOrdinaryTable(pTab) ){
-#ifndef SQLITE_OMIT_VIRTUALTABLE
- sqlite3_vtab *pVTab;
- int a1;
- if( !IsVirtual(pTab) ) continue;
- if( pTab->nCol<=0 ){
- const char *zMod = pTab->u.vtab.azArg[0];
- if( sqlite3HashFind(&db->aModule, zMod)==0 ) continue;
- }
- sqlite3ViewGetColumnNames(pParse, pTab);
- if( pTab->u.vtab.p==0 ) continue;
- pVTab = pTab->u.vtab.p->pVtab;
- if( NEVER(pVTab==0) ) continue;
- if( NEVER(pVTab->pModule==0) ) continue;
- if( pVTab->pModule->iVersion<4 ) continue;
- if( pVTab->pModule->xIntegrity==0 ) continue;
- sqlite3VdbeAddOp3(v, OP_VCheck, i, 3, isQuick);
- pTab->nTabRef++;
- sqlite3VdbeAppendP4(v, pTab, P4_TABLEREF);
- a1 = sqlite3VdbeAddOp1(v, OP_IsNull, 3); VdbeCoverage(v);
- integrityCheckResultRow(v);
- sqlite3VdbeJumpHere(v, a1);
-#endif
- continue;
- }
+ if( !IsOrdinaryTable(pTab) ) continue;
if( isQuick || HasRowid(pTab) ){
pPk = 0;
r2 = 0;
@@ -139633,6 +139698,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
** is REAL, we have to load the actual data using OP_Column
** to reliably determine if the value is a NULL. */
sqlite3VdbeAddOp3(v, OP_Column, p1, p3, 3);
+ sqlite3ColumnDefault(v, pTab, j, 3);
jmp3 = sqlite3VdbeAddOp2(v, OP_NotNull, 3, labelOk);
VdbeCoverage(v);
}
@@ -139823,6 +139889,38 @@ SQLITE_PRIVATE void sqlite3Pragma(
}
}
}
+
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+ /* Second pass to invoke the xIntegrity method on all virtual
+ ** tables.
+ */
+ for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){
+ Table *pTab = sqliteHashData(x);
+ sqlite3_vtab *pVTab;
+ int a1;
+ if( pObjTab && pObjTab!=pTab ) continue;
+ if( IsOrdinaryTable(pTab) ) continue;
+ if( !IsVirtual(pTab) ) continue;
+ if( pTab->nCol<=0 ){
+ const char *zMod = pTab->u.vtab.azArg[0];
+ if( sqlite3HashFind(&db->aModule, zMod)==0 ) continue;
+ }
+ sqlite3ViewGetColumnNames(pParse, pTab);
+ if( pTab->u.vtab.p==0 ) continue;
+ pVTab = pTab->u.vtab.p->pVtab;
+ if( NEVER(pVTab==0) ) continue;
+ if( NEVER(pVTab->pModule==0) ) continue;
+ if( pVTab->pModule->iVersion<4 ) continue;
+ if( pVTab->pModule->xIntegrity==0 ) continue;
+ sqlite3VdbeAddOp3(v, OP_VCheck, i, 3, isQuick);
+ pTab->nTabRef++;
+ sqlite3VdbeAppendP4(v, pTab, P4_TABLEREF);
+ a1 = sqlite3VdbeAddOp1(v, OP_IsNull, 3); VdbeCoverage(v);
+ integrityCheckResultRow(v);
+ sqlite3VdbeJumpHere(v, a1);
+ continue;
+ }
+#endif
}
{
static const int iLn = VDBE_OFFSET_LINENO(2);
@@ -153460,7 +153558,8 @@ SQLITE_PRIVATE Upsert *sqlite3UpsertNew(
SQLITE_PRIVATE int sqlite3UpsertAnalyzeTarget(
Parse *pParse, /* The parsing context */
SrcList *pTabList, /* Table into which we are inserting */
- Upsert *pUpsert /* The ON CONFLICT clauses */
+ Upsert *pUpsert, /* The ON CONFLICT clauses */
+ Upsert *pAll /* Complete list of all ON CONFLICT clauses */
){
Table *pTab; /* That table into which we are inserting */
int rc; /* Result code */
@@ -153563,6 +153662,14 @@ SQLITE_PRIVATE int sqlite3UpsertAnalyzeTarget(
continue;
}
pUpsert->pUpsertIdx = pIdx;
+ if( sqlite3UpsertOfIndex(pAll,pIdx)!=pUpsert ){
+ /* Really this should be an error. The isDup ON CONFLICT clause will
+ ** never fire. But this problem was not discovered until three years
+ ** after multi-CONFLICT upsert was added, and so we silently ignore
+ ** the problem to prevent breaking applications that might actually
+ ** have redundant ON CONFLICT clauses. */
+ pUpsert->isDup = 1;
+ }
break;
}
if( pUpsert->pUpsertIdx==0 ){
@@ -153589,9 +153696,13 @@ SQLITE_PRIVATE int sqlite3UpsertNextIsIPK(Upsert *pUpsert){
Upsert *pNext;
if( NEVER(pUpsert==0) ) return 0;
pNext = pUpsert->pNextUpsert;
- if( pNext==0 ) return 1;
- if( pNext->pUpsertTarget==0 ) return 1;
- if( pNext->pUpsertIdx==0 ) return 1;
+ while( 1 /*exit-by-return*/ ){
+ if( pNext==0 ) return 1;
+ if( pNext->pUpsertTarget==0 ) return 1;
+ if( pNext->pUpsertIdx==0 ) return 1;
+ if( !pNext->isDup ) return 0;
+ pNext = pNext->pNextUpsert;
+ }
return 0;
}
@@ -204785,6 +204896,7 @@ json_parse_restart:
case '[': {
/* Parse array */
iThis = pParse->nBlob;
+ assert( i<=(u32)pParse->nJson );
jsonBlobAppendNode(pParse, JSONB_ARRAY, pParse->nJson - i, 0);
iStart = pParse->nBlob;
if( pParse->oom ) return -1;
@@ -205183,6 +205295,10 @@ static void jsonReturnStringAsBlob(JsonString *pStr){
JsonParse px;
memset(&px, 0, sizeof(px));
jsonStringTerminate(pStr);
+ if( pStr->eErr ){
+ sqlite3_result_error_nomem(pStr->pCtx);
+ return;
+ }
px.zJson = pStr->zBuf;
px.nJson = pStr->nUsed;
px.db = sqlite3_context_db_handle(pStr->pCtx);
@@ -206508,8 +206624,9 @@ rebuild_from_cache:
}
p->zJson = (char*)sqlite3_value_text(pArg);
p->nJson = sqlite3_value_bytes(pArg);
+ if( db->mallocFailed ) goto json_pfa_oom;
if( p->nJson==0 ) goto json_pfa_malformed;
- if( NEVER(p->zJson==0) ) goto json_pfa_oom;
+ assert( p->zJson!=0 );
if( jsonConvertTextToBlob(p, (flgs & JSON_KEEPERROR) ? 0 : ctx) ){
if( flgs & JSON_KEEPERROR ){
p->nErr = 1;
@@ -206675,10 +206792,10 @@ static void jsonDebugPrintBlob(
if( sz==0 && x<=JSONB_FALSE ){
sqlite3_str_append(pOut, "\n", 1);
}else{
- u32 i;
+ u32 j;
sqlite3_str_appendall(pOut, ": \"");
- for(i=iStart+n; i<iStart+n+sz; i++){
- u8 c = pParse->aBlob[i];
+ for(j=iStart+n; j<iStart+n+sz; j++){
+ u8 c = pParse->aBlob[j];
if( c<0x20 || c>=0x7f ) c = '.';
sqlite3_str_append(pOut, (char*)&c, 1);
}
@@ -208086,6 +208203,9 @@ static int jsonEachColumn(
case JEACH_VALUE: {
u32 i = jsonSkipLabel(p);
jsonReturnFromBlob(&p->sParse, i, ctx, 1);
+ if( (p->sParse.aBlob[i] & 0x0f)>=JSONB_ARRAY ){
+ sqlite3_result_subtype(ctx, JSON_SUBTYPE);
+ }
break;
}
case JEACH_TYPE: {
@@ -208132,9 +208252,9 @@ static int jsonEachColumn(
case JEACH_JSON: {
if( p->sParse.zJson==0 ){
sqlite3_result_blob(ctx, p->sParse.aBlob, p->sParse.nBlob,
- SQLITE_STATIC);
+ SQLITE_TRANSIENT);
}else{
- sqlite3_result_text(ctx, p->sParse.zJson, -1, SQLITE_STATIC);
+ sqlite3_result_text(ctx, p->sParse.zJson, -1, SQLITE_TRANSIENT);
}
break;
}
@@ -209160,11 +209280,9 @@ static RtreeNode *nodeNew(Rtree *pRtree, RtreeNode *pParent){
** Clear the Rtree.pNodeBlob object
*/
static void nodeBlobReset(Rtree *pRtree){
- if( pRtree->pNodeBlob && pRtree->inWrTrans==0 && pRtree->nCursor==0 ){
- sqlite3_blob *pBlob = pRtree->pNodeBlob;
- pRtree->pNodeBlob = 0;
- sqlite3_blob_close(pBlob);
- }
+ sqlite3_blob *pBlob = pRtree->pNodeBlob;
+ pRtree->pNodeBlob = 0;
+ sqlite3_blob_close(pBlob);
}
/*
@@ -209208,7 +209326,6 @@ static int nodeAcquire(
&pRtree->pNodeBlob);
}
if( rc ){
- nodeBlobReset(pRtree);
*ppNode = 0;
/* If unable to open an sqlite3_blob on the desired row, that can only
** be because the shadow tables hold erroneous data. */
@@ -209268,6 +209385,7 @@ static int nodeAcquire(
}
*ppNode = pNode;
}else{
+ nodeBlobReset(pRtree);
if( pNode ){
pRtree->nNodeRef--;
sqlite3_free(pNode);
@@ -209412,6 +209530,7 @@ static void nodeGetCoord(
int iCoord, /* Which coordinate to extract */
RtreeCoord *pCoord /* OUT: Space to write result to */
){
+ assert( iCell<NCELL(pNode) );
readCoord(&pNode->zData[12 + pRtree->nBytesPerCell*iCell + 4*iCoord], pCoord);
}
@@ -209601,7 +209720,9 @@ static int rtreeClose(sqlite3_vtab_cursor *cur){
sqlite3_finalize(pCsr->pReadAux);
sqlite3_free(pCsr);
pRtree->nCursor--;
- nodeBlobReset(pRtree);
+ if( pRtree->nCursor==0 && pRtree->inWrTrans==0 ){
+ nodeBlobReset(pRtree);
+ }
return SQLITE_OK;
}
@@ -210186,7 +210307,11 @@ static int rtreeRowid(sqlite3_vtab_cursor *pVtabCursor, sqlite_int64 *pRowid){
int rc = SQLITE_OK;
RtreeNode *pNode = rtreeNodeOfFirstSearchPoint(pCsr, &rc);
if( rc==SQLITE_OK && ALWAYS(p) ){
- *pRowid = nodeGetRowid(RTREE_OF_CURSOR(pCsr), pNode, p->iCell);
+ if( p->iCell>=NCELL(pNode) ){
+ rc = SQLITE_ABORT;
+ }else{
+ *pRowid = nodeGetRowid(RTREE_OF_CURSOR(pCsr), pNode, p->iCell);
+ }
}
return rc;
}
@@ -210204,6 +210329,7 @@ static int rtreeColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
if( rc ) return rc;
if( NEVER(p==0) ) return SQLITE_OK;
+ if( p->iCell>=NCELL(pNode) ) return SQLITE_ABORT;
if( i==0 ){
sqlite3_result_int64(ctx, nodeGetRowid(pRtree, pNode, p->iCell));
}else if( i<=pRtree->nDim2 ){
@@ -211685,8 +211811,7 @@ constraint:
*/
static int rtreeBeginTransaction(sqlite3_vtab *pVtab){
Rtree *pRtree = (Rtree *)pVtab;
- assert( pRtree->inWrTrans==0 );
- pRtree->inWrTrans++;
+ pRtree->inWrTrans = 1;
return SQLITE_OK;
}
@@ -211700,6 +211825,9 @@ static int rtreeEndTransaction(sqlite3_vtab *pVtab){
nodeBlobReset(pRtree);
return SQLITE_OK;
}
+static int rtreeRollback(sqlite3_vtab *pVtab){
+ return rtreeEndTransaction(pVtab);
+}
/*
** The xRename method for rtree module virtual tables.
@@ -211818,7 +211946,7 @@ static sqlite3_module rtreeModule = {
rtreeBeginTransaction, /* xBegin - begin transaction */
rtreeEndTransaction, /* xSync - sync transaction */
rtreeEndTransaction, /* xCommit - commit transaction */
- rtreeEndTransaction, /* xRollback - rollback transaction */
+ rtreeRollback, /* xRollback - rollback transaction */
0, /* xFindFunction - function overloading */
rtreeRename, /* xRename - rename the table */
rtreeSavepoint, /* xSavepoint */
@@ -245377,23 +245505,26 @@ static void fts5IterSetOutputsTokendata(Fts5Iter *pIter){
static void fts5TokendataIterNext(Fts5Iter *pIter, int bFrom, i64 iFrom){
int ii;
Fts5TokenDataIter *pT = pIter->pTokenDataIter;
+ Fts5Index *pIndex = pIter->pIndex;
for(ii=0; ii<pT->nIter; ii++){
Fts5Iter *p = pT->apIter[ii];
if( p->base.bEof==0
&& (p->base.iRowid==pIter->base.iRowid || (bFrom && p->base.iRowid<iFrom))
){
- fts5MultiIterNext(p->pIndex, p, bFrom, iFrom);
+ fts5MultiIterNext(pIndex, p, bFrom, iFrom);
while( bFrom && p->base.bEof==0
&& p->base.iRowid<iFrom
- && p->pIndex->rc==SQLITE_OK
+ && pIndex->rc==SQLITE_OK
){
- fts5MultiIterNext(p->pIndex, p, 0, 0);
+ fts5MultiIterNext(pIndex, p, 0, 0);
}
}
}
- fts5IterSetOutputsTokendata(pIter);
+ if( pIndex->rc==SQLITE_OK ){
+ fts5IterSetOutputsTokendata(pIter);
+ }
}
/*
@@ -250547,7 +250678,7 @@ static void fts5SourceIdFunc(
){
assert( nArg==0 );
UNUSED_PARAM2(nArg, apUnused);
- sqlite3_result_text(pCtx, "fts5: 2024-01-30 16:01:20 e876e51a0ed5c5b3126f52e532044363a014bc594cfefa87ffb5b82257cc467a", -1, SQLITE_TRANSIENT);
+ sqlite3_result_text(pCtx, "fts5: 2024-03-12 11:06:23 d8cd6d49b46a395b13955387d05e9e1a2a47e54fb99f3c9b59835bbefad6af77", -1, SQLITE_TRANSIENT);
}
/*
diff --git a/third_party/sqlite3/src/sqlite3.h b/third_party/sqlite3/src/sqlite3.h
index 4fdfde004e..c9fc77fb86 100644
--- a/third_party/sqlite3/src/sqlite3.h
+++ b/third_party/sqlite3/src/sqlite3.h
@@ -146,9 +146,9 @@ extern "C" {
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
-#define SQLITE_VERSION "3.45.1"
-#define SQLITE_VERSION_NUMBER 3045001
-#define SQLITE_SOURCE_ID "2024-01-30 16:01:20 e876e51a0ed5c5b3126f52e532044363a014bc594cfefa87ffb5b82257cc467a"
+#define SQLITE_VERSION "3.45.2"
+#define SQLITE_VERSION_NUMBER 3045002
+#define SQLITE_SOURCE_ID "2024-03-12 11:06:23 d8cd6d49b46a395b13955387d05e9e1a2a47e54fb99f3c9b59835bbefad6af77"
/*
** CAPI3REF: Run-Time Library Version Numbers
@@ -420,6 +420,8 @@ typedef int (*sqlite3_callback)(void*,int,char**, char**);
** the 1st parameter to sqlite3_exec() while sqlite3_exec() is running.
** <li> The application must not modify the SQL statement text passed into
** the 2nd parameter of sqlite3_exec() while sqlite3_exec() is running.
+** <li> The application must not dereference the arrays or string pointers
+** passed as the 3rd and 4th callback parameters after it returns.
** </ul>
*/
SQLITE_API int sqlite3_exec(
diff --git a/third_party/wayland-proxy/wayland-proxy.cpp b/third_party/wayland-proxy/wayland-proxy.cpp
index 900c1162d9..065035e5c9 100644
--- a/third_party/wayland-proxy/wayland-proxy.cpp
+++ b/third_party/wayland-proxy/wayland-proxy.cpp
@@ -46,33 +46,35 @@ void Print(const char* aFormat, ...) {
va_end(args);
}
-void Warning(const char* aOperation, bool aPrintErrno = true) {
- fprintf(stderr, "Warning: %s : %s\n",
- aOperation,
- aPrintErrno ? strerror(errno) : "");
+void Warning(const char* aOperation) {
+ fprintf(stderr, "Warning: %s : %s\n", aOperation, strerror(errno));
}
-void Error(const char* aOperation, bool aPrintErrno = true) {
- fprintf(stderr, "Error: %s : %s\n",
- aOperation,
- aPrintErrno ? strerror(errno) : "");
+void Error(const char* aOperation) {
+ fprintf(stderr, "Error: %s : %s\n", aOperation, strerror(errno));
+}
+
+void ErrorPlain(const char* aFormat, ...) {
+ va_list args;
+ va_start(args, aFormat);
+ vfprintf(stderr, aFormat, args);
+ va_end(args);
}
class WaylandMessage {
public:
bool Write(int aSocket);
- bool Loaded() const { return mLoaded && (mFds.size() || mData.size()); }
+ bool Loaded() const { return !mFailed && (mFds.size() || mData.size()); }
bool Failed() const { return mFailed; }
explicit WaylandMessage(int aSocket) { Read(aSocket); }
~WaylandMessage();
private:
- bool Read(int aSocket);
+ void Read(int aSocket);
private:
- bool mLoaded = false;
bool mFailed = false;
std::vector<int> mFds;
@@ -110,6 +112,10 @@ class ProxiedConnection {
// We don't have connected compositor yet. Try to connect
bool mCompositorConnected = false;
+ // Don't cycle endlessly over compositor connection
+ int mFailedCompositorConnections = 0;
+ static constexpr int sMaxFailedCompositorConnections = 100;
+
// We're disconnected from app or compositor. We will close this connection.
bool mFailed = false;
@@ -130,9 +136,9 @@ WaylandMessage::~WaylandMessage() {
}
}
-bool WaylandMessage::Read(int aSocket) {
+void WaylandMessage::Read(int aSocket) {
// We don't expect WaylandMessage re-read
- assert(!mLoaded && !mFailed);
+ assert(!Loaded() && !mFailed);
mData.resize(MAX_DATA_SIZE);
@@ -149,7 +155,7 @@ bool WaylandMessage::Read(int aSocket) {
if (msg.msg_flags & (MSG_CTRUNC | MSG_TRUNC)) {
Error("WaylandMessage::Read() data truncated, small buffer?");
mFailed = true;
- return false;
+ return;
}
if (ret < 1) {
@@ -157,12 +163,12 @@ bool WaylandMessage::Read(int aSocket) {
case EAGAIN:
case EINTR:
// Neither loaded nor failed, we'll try again later
- Print("WaylandMessage::Write() failed %s\n", strerror(errno));
- return false;
+ Print("WaylandMessage::Read() failed %s\n", strerror(errno));
+ return;
default:
- Error("WaylandMessage::Write() failed");
+ Error("WaylandMessage::Read() failed");
mFailed = true;
- return false;
+ return;
}
}
@@ -180,6 +186,11 @@ bool WaylandMessage::Read(int aSocket) {
int* data = (int*)CMSG_DATA(header);
int filenum = (int)((header->cmsg_len - CMSG_LEN(0)) / sizeof(int));
+ if (filenum > MAX_LIBWAY_FDS) {
+ ErrorPlain("WaylandMessage::Read(): too many files to read\n");
+ mFailed = true;
+ return;
+ }
for (int i = 0; i < filenum; i++) {
#ifdef DEBUG
int flags = fcntl(data[i], F_GETFL, 0);
@@ -191,13 +202,10 @@ bool WaylandMessage::Read(int aSocket) {
}
header = next;
}
-
- mLoaded = true;
- return true;
}
bool WaylandMessage::Write(int aSocket) {
- if (!mLoaded || mFailed) {
+ if (!Loaded()) {
return false;
}
@@ -214,8 +222,8 @@ bool WaylandMessage::Write(int aSocket) {
int filenum = mFds.size();
if (filenum) {
- if (filenum >= MAX_LIBWAY_FDS) {
- Error("WaylandMessage::Write() too many files to send\n", false);
+ if (filenum > MAX_LIBWAY_FDS) {
+ ErrorPlain("WaylandMessage::Write() too many files to send\n");
return false;
}
#ifdef DEBUG
@@ -318,11 +326,6 @@ struct pollfd* ProxiedConnection::LoadPollFd(struct pollfd* aPfds) {
}
bool ProxiedConnection::ConnectToCompositor() {
- if (!(mCompositorFlags & POLLOUT)) {
- // Try again later
- return true;
- }
-
struct sockaddr_un addr = {};
addr.sun_family = AF_UNIX;
strcpy(addr.sun_path, mWaylandDisplay);
@@ -339,6 +342,11 @@ bool ProxiedConnection::ConnectToCompositor() {
case EINTR:
case EISCONN:
case ETIMEDOUT:
+ mFailedCompositorConnections++;
+ if (mFailedCompositorConnections > sMaxFailedCompositorConnections) {
+ Error("ConnectToCompositor() connect() failed repeatedly");
+ return false;
+ }
// We can recover from these errors and try again
Warning("ConnectToCompositor() try again");
return true;
@@ -460,27 +468,65 @@ bool ProxiedConnection::Process() {
return !mFailed;
}
+bool WaylandProxy::CheckWaylandDisplay(const char* aWaylandDisplay) {
+ struct sockaddr_un addr = {};
+ addr.sun_family = AF_UNIX;
+ strcpy(addr.sun_path, aWaylandDisplay);
+
+ int sc = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
+ if (sc == -1) {
+ Error("CheckWaylandDisplay(): failed to create socket");
+ return false;
+ }
+
+ bool ret =
+ connect(sc, (const struct sockaddr*)&addr,
+ sizeof(struct sockaddr_un)) != -1;
+ if (!ret) {
+ switch (errno) {
+ case EAGAIN:
+ case EALREADY:
+ case ECONNREFUSED:
+ case EINPROGRESS:
+ case EINTR:
+ case EISCONN:
+ case ETIMEDOUT:
+ // We can recover from these errors and try again
+ ret = true;
+ break;
+ default:
+ ErrorPlain(
+ "CheckWaylandDisplay(): Failed to connect to Wayland display '%s' error: %s\n",
+ mWaylandDisplay, strerror(errno));
+ break;
+ }
+ }
+
+ close(sc);
+ return ret;
+}
+
+
bool WaylandProxy::SetupWaylandDisplays() {
char* waylandDisplay = getenv("WAYLAND_DISPLAY_COMPOSITOR");
if (!waylandDisplay) {
waylandDisplay = getenv("WAYLAND_DISPLAY");
- if (!waylandDisplay) {
- Error("Init(), Missing Wayland display, WAYLAND_DISPLAY is empty.",
- false);
+ if (!waylandDisplay || waylandDisplay[0] == '\0') {
+ ErrorPlain("WaylandProxy::SetupWaylandDisplays(), Missing Wayland display, WAYLAND_DISPLAY is empty.\n");
return false;
}
}
char* XDGRuntimeDir = getenv("XDG_RUNTIME_DIR");
if (!XDGRuntimeDir) {
- Error("Init() Missing XDG_RUNTIME_DIR", false);
+ ErrorPlain("WaylandProxy::SetupWaylandDisplays() Missing XDG_RUNTIME_DIR\n");
return false;
}
// WAYLAND_DISPLAY can be absolute path
if (waylandDisplay[0] == '/') {
if (strlen(mWaylandDisplay) >= sMaxDisplayNameLen) {
- Error("Init() WAYLAND_DISPLAY is too large.", false);
+ ErrorPlain("WaylandProxy::SetupWaylandDisplays() WAYLAND_DISPLAY is too large.\n");
return false;
}
strcpy(mWaylandDisplay, waylandDisplay);
@@ -488,22 +534,27 @@ bool WaylandProxy::SetupWaylandDisplays() {
int ret = snprintf(mWaylandDisplay, sMaxDisplayNameLen, "%s/%s",
XDGRuntimeDir, waylandDisplay);
if (ret < 0 || ret >= sMaxDisplayNameLen) {
- Error("Init() WAYLAND_DISPLAY/XDG_RUNTIME_DIR is too large.", false);
+ ErrorPlain("WaylandProxy::SetupWaylandDisplays() WAYLAND_DISPLAY/XDG_RUNTIME_DIR is too large.\n");
return false;
}
}
- // Save original Wayland display variable for potential reuse
- setenv("WAYLAND_DISPLAY_COMPOSITOR", waylandDisplay, /* overwrite = */ true);
+ if (!CheckWaylandDisplay(mWaylandDisplay)) {
+ return false;
+ }
int ret = snprintf(mWaylandProxy, sMaxDisplayNameLen,
"%s/wayland-proxy-%d", XDGRuntimeDir, getpid());
if (ret < 0 || ret >= sMaxDisplayNameLen) {
- Error("Init() WAYLAND_DISPLAY/XDG_RUNTIME_DIR is too large.", false);
+ ErrorPlain("WaylandProxy::SetupWaylandDisplays() WAYLAND_DISPLAY/XDG_RUNTIME_DIR is too large.\n");
return false;
}
- Info("SetupWaylandDisplays() Wayland '%s' proxy '%s'\n", mWaylandDisplay, mWaylandProxy);
+ // Save original Wayland display variable for potential reuse
+ setenv("WAYLAND_DISPLAY_COMPOSITOR", waylandDisplay, /* overwrite = */ true);
+
+ Info("SetupWaylandDisplays() Wayland '%s' proxy '%s'\n",
+ mWaylandDisplay, mWaylandProxy);
return true;
}
@@ -698,7 +749,7 @@ WaylandProxy::~WaylandProxy() {
}
void* WaylandProxy::RunProxyThread(WaylandProxy* aProxy) {
-#ifdef __linux__
+#if defined(__linux__) || defined(__FreeBSD__)
pthread_setname_np(pthread_self(), "WaylandProxy");
#endif
aProxy->Run();
@@ -718,7 +769,7 @@ std::unique_ptr<WaylandProxy> WaylandProxy::Create() {
bool WaylandProxy::RunChildApplication(char* argv[]) {
if (!argv[0]) {
- Error("WaylandProxy::RunChildApplication: missing application to run", false);
+ ErrorPlain("WaylandProxy::RunChildApplication: missing application to run\n");
return false;
}
@@ -730,7 +781,9 @@ bool WaylandProxy::RunChildApplication(char* argv[]) {
if (mApplicationPID == 0) {
SetWaylandProxyDisplay();
if (execv(argv[0], argv) == -1) {
- ErrorPlain("WaylandProxy::RunChildApplication: failed to run %s error %s\n", argv[0], strerror(errno));
+ ErrorPlain(
+ "WaylandProxy::RunChildApplication: failed to run %s error %s\n",
+ argv[0], strerror(errno));
exit(1);
}
}
@@ -742,7 +795,7 @@ bool WaylandProxy::RunChildApplication(char* argv[]) {
bool WaylandProxy::RunThread() {
pthread_attr_t attr;
if (pthread_attr_init(&attr) != 0) {
- ErrorPlain("pthread_attr_init() failed\n");
+ ErrorPlain("WaylandProxy::RunThread(): pthread_attr_init() failed\n");
return false;
}
@@ -756,7 +809,7 @@ bool WaylandProxy::RunThread() {
mThreadRunning = pthread_create(&mThread, nullptr, (void* (*)(void*))RunProxyThread, this) == 0;
if (!mThreadRunning) {
- ErrorPlain("pthread_create() failed\n");
+ ErrorPlain("WaylandProxy::RunThread(): pthread_create() failed\n");
// If we failed to run proxy thread, set WAYLAND_DISPLAY back.
RestoreWaylandDisplay();
}
@@ -778,16 +831,14 @@ void WaylandProxy::Info(const char* aFormat, ...) {
va_end(args);
}
-void WaylandProxy::Warning(const char* aOperation, bool aPrintErrno) {
+void WaylandProxy::Warning(const char* aOperation) {
fprintf(stderr, "[%d] Wayland Proxy [%p] Warning: %s : %s\n",
- getpid(), this, aOperation,
- aPrintErrno ? strerror(errno) : "");
+ getpid(), this, aOperation, strerror(errno));
}
-void WaylandProxy::Error(const char* aOperation, bool aPrintErrno) {
+void WaylandProxy::Error(const char* aOperation) {
fprintf(stderr, "[%d] Wayland Proxy [%p] Error: %s : %s\n",
- getpid(), this, aOperation,
- aPrintErrno ? strerror(errno) : "");
+ getpid(), this, aOperation, strerror(errno));
}
void WaylandProxy::ErrorPlain(const char* aFormat, ...) {
diff --git a/third_party/wayland-proxy/wayland-proxy.h b/third_party/wayland-proxy/wayland-proxy.h
index 1ec690a596..657563b569 100644
--- a/third_party/wayland-proxy/wayland-proxy.h
+++ b/third_party/wayland-proxy/wayland-proxy.h
@@ -39,6 +39,7 @@ class WaylandProxy {
void SetWaylandProxyDisplay();
static void* RunProxyThread(WaylandProxy* aProxy);
+ bool CheckWaylandDisplay(const char* aWaylandDisplay);
bool SetupWaylandDisplays();
bool StartProxyServer();
@@ -48,8 +49,8 @@ class WaylandProxy {
bool ProcessConnections();
void Info(const char* aFormat, ...);
- void Warning(const char* aOperation, bool aPrintErrno = true);
- void Error(const char* aOperation, bool aPrintErrno = true);
+ void Warning(const char* aOperation);
+ void Error(const char* aOperation);
void ErrorPlain(const char* aFormat, ...);
private:
diff --git a/third_party/xsimd/include/xsimd/arch/generic/xsimd_generic_math.hpp b/third_party/xsimd/include/xsimd/arch/generic/xsimd_generic_math.hpp
index 8fa887dc57..7bcc4da241 100644
--- a/third_party/xsimd/include/xsimd/arch/generic/xsimd_generic_math.hpp
+++ b/third_party/xsimd/include/xsimd/arch/generic/xsimd_generic_math.hpp
@@ -26,7 +26,7 @@ namespace xsimd
using namespace types;
// abs
- template <class A, class T, class /*=typename std::enable_if<std::is_integral<T>::value, void>::type*/>
+ template <class A, class T, class>
inline batch<T, A> abs(batch<T, A> const& self, requires_arch<generic>) noexcept
{
if (std::is_unsigned<T>::value)
@@ -45,6 +45,63 @@ namespace xsimd
return hypot(z.real(), z.imag());
}
+ // avg
+ namespace detail
+ {
+ template <class A, class T>
+ inline batch<T, A> avg(batch<T, A> const& x, batch<T, A> const& y, std::true_type, std::false_type) noexcept
+ {
+ return (x & y) + ((x ^ y) >> 1);
+ }
+
+ template <class A, class T>
+ inline batch<T, A> avg(batch<T, A> const& x, batch<T, A> const& y, std::true_type, std::true_type) noexcept
+ {
+ // Inspired by
+ // https://stackoverflow.com/questions/5697500/take-the-average-of-two-signed-numbers-in-c
+ auto t = (x & y) + ((x ^ y) >> 1);
+ auto t_u = bitwise_cast<typename std::make_unsigned<T>::type>(t);
+ auto avg = t + (bitwise_cast<T>(t_u >> (8 * sizeof(T) - 1)) & (x ^ y));
+ return avg;
+ }
+
+ template <class A, class T>
+ inline batch<T, A> avg(batch<T, A> const& x, batch<T, A> const& y, std::false_type, std::true_type) noexcept
+ {
+ return (x + y) / 2;
+ }
+ }
+
+ template <class A, class T>
+ inline batch<T, A> avg(batch<T, A> const& x, batch<T, A> const& y, requires_arch<generic>) noexcept
+ {
+ return detail::avg(x, y, typename std::is_integral<T>::type {}, typename std::is_signed<T>::type {});
+ }
+
+ // avgr
+ namespace detail
+ {
+ template <class A, class T>
+ inline batch<T, A> avgr(batch<T, A> const& x, batch<T, A> const& y, std::true_type) noexcept
+ {
+ constexpr unsigned shift = 8 * sizeof(T) - 1;
+ auto adj = std::is_signed<T>::value ? ((x ^ y) & 0x1) : (((x ^ y) << shift) >> shift);
+ return ::xsimd::kernel::avg(x, y, A {}) + adj;
+ }
+
+ template <class A, class T>
+ inline batch<T, A> avgr(batch<T, A> const& x, batch<T, A> const& y, std::false_type) noexcept
+ {
+ return ::xsimd::kernel::avg(x, y, A {});
+ }
+ }
+
+ template <class A, class T>
+ inline batch<T, A> avgr(batch<T, A> const& x, batch<T, A> const& y, requires_arch<generic>) noexcept
+ {
+ return detail::avgr(x, y, typename std::is_integral<T>::type {});
+ }
+
// batch_cast
template <class A, class T>
inline batch<T, A> batch_cast(batch<T, A> const& self, batch<T, A> const&, requires_arch<generic>) noexcept
diff --git a/third_party/xsimd/include/xsimd/arch/xsimd_avx2.hpp b/third_party/xsimd/include/xsimd/arch/xsimd_avx2.hpp
index a5b07ec9da..a4881778e0 100644
--- a/third_party/xsimd/include/xsimd/arch/xsimd_avx2.hpp
+++ b/third_party/xsimd/include/xsimd/arch/xsimd_avx2.hpp
@@ -76,6 +76,44 @@ namespace xsimd
}
}
+ // avgr
+ template <class A, class T, class = typename std::enable_if<std::is_unsigned<T>::value, void>::type>
+ inline batch<T, A> avgr(batch<T, A> const& self, batch<T, A> const& other, requires_arch<avx2>) noexcept
+ {
+ XSIMD_IF_CONSTEXPR(sizeof(T) == 1)
+ {
+ return _mm256_avg_epu8(self, other);
+ }
+ else XSIMD_IF_CONSTEXPR(sizeof(T) == 2)
+ {
+ return _mm256_avg_epu16(self, other);
+ }
+ else
+ {
+ return avgr(self, other, generic {});
+ }
+ }
+
+ // avg
+ template <class A, class T, class = typename std::enable_if<std::is_unsigned<T>::value, void>::type>
+ inline batch<T, A> avg(batch<T, A> const& self, batch<T, A> const& other, requires_arch<avx2>) noexcept
+ {
+ XSIMD_IF_CONSTEXPR(sizeof(T) == 1)
+ {
+ auto adj = ((self ^ other) << 7) >> 7;
+ return avgr(self, other, A {}) - adj;
+ }
+ else XSIMD_IF_CONSTEXPR(sizeof(T) == 2)
+ {
+ auto adj = ((self ^ other) << 15) >> 15;
+ return avgr(self, other, A {}) - adj;
+ }
+ else
+ {
+ return avg(self, other, generic {});
+ }
+ }
+
// bitwise_and
template <class A, class T, class = typename std::enable_if<std::is_integral<T>::value, void>::type>
inline batch<T, A> bitwise_and(batch<T, A> const& self, batch<T, A> const& other, requires_arch<avx2>) noexcept
diff --git a/third_party/xsimd/include/xsimd/arch/xsimd_avx512bw.hpp b/third_party/xsimd/include/xsimd/arch/xsimd_avx512bw.hpp
index 94a194dab7..95fbcbd461 100644
--- a/third_party/xsimd/include/xsimd/arch/xsimd_avx512bw.hpp
+++ b/third_party/xsimd/include/xsimd/arch/xsimd_avx512bw.hpp
@@ -112,6 +112,44 @@ namespace xsimd
}
}
+ // avgr
+ template <class A, class T, class = typename std::enable_if<std::is_unsigned<T>::value, void>::type>
+ inline batch<T, A> avgr(batch<T, A> const& self, batch<T, A> const& other, requires_arch<avx512bw>) noexcept
+ {
+ XSIMD_IF_CONSTEXPR(sizeof(T) == 1)
+ {
+ return _mm512_avg_epu8(self, other);
+ }
+ else XSIMD_IF_CONSTEXPR(sizeof(T) == 2)
+ {
+ return _mm512_avg_epu16(self, other);
+ }
+ else
+ {
+ return avgr(self, other, generic {});
+ }
+ }
+
+ // avg
+ template <class A, class T, class = typename std::enable_if<std::is_unsigned<T>::value, void>::type>
+ inline batch<T, A> avg(batch<T, A> const& self, batch<T, A> const& other, requires_arch<avx512bw>) noexcept
+ {
+ XSIMD_IF_CONSTEXPR(sizeof(T) == 1)
+ {
+ auto adj = ((self ^ other) << 7) >> 7;
+ return avgr(self, other, A {}) - adj;
+ }
+ else XSIMD_IF_CONSTEXPR(sizeof(T) == 2)
+ {
+ auto adj = ((self ^ other) << 15) >> 15;
+ return avgr(self, other, A {}) - adj;
+ }
+ else
+ {
+ return avg(self, other, generic {});
+ }
+ }
+
// bitwise_lshift
template <class A, class T, class = typename std::enable_if<std::is_integral<T>::value, void>::type>
inline batch<T, A> bitwise_lshift(batch<T, A> const& self, int32_t other, requires_arch<avx512bw>) noexcept
diff --git a/third_party/xsimd/include/xsimd/arch/xsimd_i8mm_neon64.hpp b/third_party/xsimd/include/xsimd/arch/xsimd_i8mm_neon64.hpp
new file mode 100644
index 0000000000..5533923020
--- /dev/null
+++ b/third_party/xsimd/include/xsimd/arch/xsimd_i8mm_neon64.hpp
@@ -0,0 +1,17 @@
+/***************************************************************************
+ * Copyright (c) Johan Mabille, Sylvain Corlay, Wolf Vollprecht and *
+ * Martin Renou *
+ * Copyright (c) QuantStack *
+ * Copyright (c) Serge Guelton *
+ * *
+ * Distributed under the terms of the BSD 3-Clause License. *
+ * *
+ * The full license is in the file LICENSE, distributed with this software. *
+ ****************************************************************************/
+
+#ifndef XSIMD_I8MM_NEON64_HPP
+#define XSIMD_I8MM_NEON64_HPP
+
+#include "../types/xsimd_i8mm_neon64_register.hpp"
+
+#endif
diff --git a/third_party/xsimd/include/xsimd/arch/xsimd_isa.hpp b/third_party/xsimd/include/xsimd/arch/xsimd_isa.hpp
index 0edd776741..dcd2df3fa9 100644
--- a/third_party/xsimd/include/xsimd/arch/xsimd_isa.hpp
+++ b/third_party/xsimd/include/xsimd/arch/xsimd_isa.hpp
@@ -104,6 +104,10 @@
#include "./xsimd_neon64.hpp"
#endif
+#if XSIMD_WITH_I8MM_NEON64
+#include "./xsimd_i8mm_neon64.hpp"
+#endif
+
#if XSIMD_WITH_SVE
#include "./xsimd_sve.hpp"
#endif
diff --git a/third_party/xsimd/include/xsimd/arch/xsimd_neon.hpp b/third_party/xsimd/include/xsimd/arch/xsimd_neon.hpp
index 54f09fb663..3510eb21d9 100644
--- a/third_party/xsimd/include/xsimd/arch/xsimd_neon.hpp
+++ b/third_party/xsimd/include/xsimd/arch/xsimd_neon.hpp
@@ -23,33 +23,39 @@
// Wrap intrinsics so we can pass them as function pointers
// - OP: intrinsics name prefix, e.g., vorrq
// - RT: type traits to deduce intrinsics return types
-#define WRAP_BINARY_INT_EXCLUDING_64(OP, RT) \
+#define WRAP_BINARY_UINT_EXCLUDING_64(OP, RT) \
namespace wrap \
{ \
inline RT<uint8x16_t> OP##_u8(uint8x16_t a, uint8x16_t b) noexcept \
{ \
return ::OP##_u8(a, b); \
} \
- inline RT<int8x16_t> OP##_s8(int8x16_t a, int8x16_t b) noexcept \
- { \
- return ::OP##_s8(a, b); \
- } \
inline RT<uint16x8_t> OP##_u16(uint16x8_t a, uint16x8_t b) noexcept \
{ \
return ::OP##_u16(a, b); \
} \
- inline RT<int16x8_t> OP##_s16(int16x8_t a, int16x8_t b) noexcept \
- { \
- return ::OP##_s16(a, b); \
- } \
inline RT<uint32x4_t> OP##_u32(uint32x4_t a, uint32x4_t b) noexcept \
{ \
return ::OP##_u32(a, b); \
} \
- inline RT<int32x4_t> OP##_s32(int32x4_t a, int32x4_t b) noexcept \
- { \
- return ::OP##_s32(a, b); \
- } \
+ }
+
+#define WRAP_BINARY_INT_EXCLUDING_64(OP, RT) \
+ WRAP_BINARY_UINT_EXCLUDING_64(OP, RT) \
+ namespace wrap \
+ { \
+ inline RT<int8x16_t> OP##_s8(int8x16_t a, int8x16_t b) noexcept \
+ { \
+ return ::OP##_s8(a, b); \
+ } \
+ inline RT<int16x8_t> OP##_s16(int16x8_t a, int16x8_t b) noexcept \
+ { \
+ return ::OP##_s16(a, b); \
+ } \
+ inline RT<int32x4_t> OP##_s32(int32x4_t a, int32x4_t b) noexcept \
+ { \
+ return ::OP##_s32(a, b); \
+ } \
}
#define WRAP_BINARY_INT(OP, RT) \
@@ -204,6 +210,10 @@ namespace xsimd
uint32x4_t, int32x4_t,
float32x4_t>;
+ using excluding_int64f32_dispatcher = neon_dispatcher_impl<uint8x16_t, int8x16_t,
+ uint16x8_t, int16x8_t,
+ uint32x4_t, int32x4_t>;
+
/**************************
* comparison dispatchers *
**************************/
@@ -744,6 +754,38 @@ namespace xsimd
return dispatcher.apply(register_type(lhs), register_type(rhs));
}
+ /*******
+ * avg *
+ *******/
+
+ WRAP_BINARY_UINT_EXCLUDING_64(vhaddq, detail::identity_return_type)
+
+ template <class A, class T, class = typename std::enable_if<(std::is_unsigned<T>::value && sizeof(T) != 8), void>::type>
+ inline batch<T, A> avg(batch<T, A> const& lhs, batch<T, A> const& rhs, requires_arch<neon>) noexcept
+ {
+ using register_type = typename batch<T, A>::register_type;
+ const detail::neon_dispatcher_impl<uint8x16_t, uint16x8_t, uint32x4_t>::binary dispatcher = {
+ std::make_tuple(wrap::vhaddq_u8, wrap::vhaddq_u16, wrap::vhaddq_u32)
+ };
+ return dispatcher.apply(register_type(lhs), register_type(rhs));
+ }
+
+ /********
+ * avgr *
+ ********/
+
+ WRAP_BINARY_UINT_EXCLUDING_64(vrhaddq, detail::identity_return_type)
+
+ template <class A, class T, class = typename std::enable_if<(std::is_unsigned<T>::value && sizeof(T) != 8), void>::type>
+ inline batch<T, A> avgr(batch<T, A> const& lhs, batch<T, A> const& rhs, requires_arch<neon>) noexcept
+ {
+ using register_type = typename batch<T, A>::register_type;
+ const detail::neon_dispatcher_impl<uint8x16_t, uint16x8_t, uint32x4_t>::binary dispatcher = {
+ std::make_tuple(wrap::vrhaddq_u8, wrap::vrhaddq_u16, wrap::vrhaddq_u32)
+ };
+ return dispatcher.apply(register_type(lhs), register_type(rhs));
+ }
+
/********
* sadd *
********/
diff --git a/third_party/xsimd/include/xsimd/arch/xsimd_neon64.hpp b/third_party/xsimd/include/xsimd/arch/xsimd_neon64.hpp
index bc982c7ce6..77538d1c2d 100644
--- a/third_party/xsimd/include/xsimd/arch/xsimd_neon64.hpp
+++ b/third_party/xsimd/include/xsimd/arch/xsimd_neon64.hpp
@@ -92,7 +92,7 @@ namespace xsimd
template <class A, class T>
inline batch<T, A> broadcast(T val, requires_arch<neon64>) noexcept
{
- return broadcast<neon64>(val, neon {});
+ return broadcast<A>(val, neon {});
}
template <class A>
@@ -952,6 +952,41 @@ namespace xsimd
/**********
* zip_lo *
**********/
+ template <class A, class T, detail::enable_sized_unsigned_t<T, 1> = 0>
+ inline batch<T, A> zip_lo(batch<T, A> const& lhs, batch<T, A> const& rhs, requires_arch<neon64>) noexcept
+ {
+ return vzip1q_u8(lhs, rhs);
+ }
+
+ template <class A, class T, detail::enable_sized_signed_t<T, 1> = 0>
+ inline batch<T, A> zip_lo(batch<T, A> const& lhs, batch<T, A> const& rhs, requires_arch<neon64>) noexcept
+ {
+ return vzip1q_s8(lhs, rhs);
+ }
+
+ template <class A, class T, detail::enable_sized_unsigned_t<T, 2> = 0>
+ inline batch<T, A> zip_lo(batch<T, A> const& lhs, batch<T, A> const& rhs, requires_arch<neon64>) noexcept
+ {
+ return vzip1q_u16(lhs, rhs);
+ }
+
+ template <class A, class T, detail::enable_sized_signed_t<T, 2> = 0>
+ inline batch<T, A> zip_lo(batch<T, A> const& lhs, batch<T, A> const& rhs, requires_arch<neon64>) noexcept
+ {
+ return vzip1q_s16(lhs, rhs);
+ }
+
+ template <class A, class T, detail::enable_sized_unsigned_t<T, 4> = 0>
+ inline batch<T, A> zip_lo(batch<T, A> const& lhs, batch<T, A> const& rhs, requires_arch<neon64>) noexcept
+ {
+ return vzip1q_u32(lhs, rhs);
+ }
+
+ template <class A, class T, detail::enable_sized_signed_t<T, 4> = 0>
+ inline batch<T, A> zip_lo(batch<T, A> const& lhs, batch<T, A> const& rhs, requires_arch<neon64>) noexcept
+ {
+ return vzip1q_s32(lhs, rhs);
+ }
template <class A, class T, detail::enable_sized_unsigned_t<T, 8> = 0>
inline batch<T, A> zip_lo(batch<T, A> const& lhs, batch<T, A> const& rhs, requires_arch<neon64>) noexcept
@@ -966,6 +1001,12 @@ namespace xsimd
}
template <class A>
+ inline batch<float, A> zip_lo(batch<float, A> const& lhs, batch<float, A> const& rhs, requires_arch<neon64>) noexcept
+ {
+ return vzip1q_f32(lhs, rhs);
+ }
+
+ template <class A>
inline batch<double, A> zip_lo(batch<double, A> const& lhs, batch<double, A> const& rhs, requires_arch<neon64>) noexcept
{
return vzip1q_f64(lhs, rhs);
@@ -975,6 +1016,42 @@ namespace xsimd
* zip_hi *
**********/
+ template <class A, class T, detail::enable_sized_unsigned_t<T, 1> = 0>
+ inline batch<T, A> zip_hi(batch<T, A> const& lhs, batch<T, A> const& rhs, requires_arch<neon64>) noexcept
+ {
+ return vzip2q_u8(lhs, rhs);
+ }
+
+ template <class A, class T, detail::enable_sized_signed_t<T, 1> = 0>
+ inline batch<T, A> zip_hi(batch<T, A> const& lhs, batch<T, A> const& rhs, requires_arch<neon64>) noexcept
+ {
+ return vzip2q_s8(lhs, rhs);
+ }
+
+ template <class A, class T, detail::enable_sized_unsigned_t<T, 2> = 0>
+ inline batch<T, A> zip_hi(batch<T, A> const& lhs, batch<T, A> const& rhs, requires_arch<neon64>) noexcept
+ {
+ return vzip2q_u16(lhs, rhs);
+ }
+
+ template <class A, class T, detail::enable_sized_signed_t<T, 2> = 0>
+ inline batch<T, A> zip_hi(batch<T, A> const& lhs, batch<T, A> const& rhs, requires_arch<neon64>) noexcept
+ {
+ return vzip2q_s16(lhs, rhs);
+ }
+
+ template <class A, class T, detail::enable_sized_unsigned_t<T, 4> = 0>
+ inline batch<T, A> zip_hi(batch<T, A> const& lhs, batch<T, A> const& rhs, requires_arch<neon64>) noexcept
+ {
+ return vzip2q_u32(lhs, rhs);
+ }
+
+ template <class A, class T, detail::enable_sized_signed_t<T, 4> = 0>
+ inline batch<T, A> zip_hi(batch<T, A> const& lhs, batch<T, A> const& rhs, requires_arch<neon64>) noexcept
+ {
+ return vzip2q_s32(lhs, rhs);
+ }
+
template <class A, class T, detail::enable_sized_unsigned_t<T, 8> = 0>
inline batch<T, A> zip_hi(batch<T, A> const& lhs, batch<T, A> const& rhs, requires_arch<neon64>) noexcept
{
@@ -988,6 +1065,12 @@ namespace xsimd
}
template <class A>
+ inline batch<float, A> zip_hi(batch<float, A> const& lhs, batch<float, A> const& rhs, requires_arch<neon64>) noexcept
+ {
+ return vzip2q_f32(lhs, rhs);
+ }
+
+ template <class A>
inline batch<double, A> zip_hi(batch<double, A> const& lhs, batch<double, A> const& rhs, requires_arch<neon64>) noexcept
{
return vzip2q_f64(lhs, rhs);
diff --git a/third_party/xsimd/include/xsimd/arch/xsimd_scalar.hpp b/third_party/xsimd/include/xsimd/arch/xsimd_scalar.hpp
index 39bd607be9..1cde15ffe1 100644
--- a/third_party/xsimd/include/xsimd/arch/xsimd_scalar.hpp
+++ b/third_party/xsimd/include/xsimd/arch/xsimd_scalar.hpp
@@ -142,6 +142,39 @@ namespace xsimd
return x + y;
}
+ template <class T, class Tp>
+ inline typename std::common_type<T, Tp>::type avg(T const& x, Tp const& y) noexcept
+ {
+ using common_type = typename std::common_type<T, Tp>::type;
+ if (std::is_floating_point<common_type>::value)
+ return (x + y) / 2;
+ else if (std::is_unsigned<common_type>::value)
+ {
+ return (x & y) + ((x ^ y) >> 1);
+ }
+ else
+ {
+ // Inspired by
+ // https://stackoverflow.com/questions/5697500/take-the-average-of-two-signed-numbers-in-c
+ auto t = (x & y) + ((x ^ y) >> 1);
+ auto t_u = static_cast<typename std::make_unsigned<common_type>::type>(t);
+ auto avg = t + (static_cast<T>(t_u >> (8 * sizeof(T) - 1)) & (x ^ y));
+ return avg;
+ }
+ }
+
+ template <class T, class Tp>
+ inline typename std::common_type<T, Tp>::type avgr(T const& x, Tp const& y) noexcept
+ {
+ using common_type = typename std::common_type<T, Tp>::type;
+ if (std::is_floating_point<common_type>::value)
+ return avg(x, y);
+ else
+ {
+ return avg(x, y) + ((x ^ y) & 1);
+ }
+ }
+
template <class T>
inline T incr(T const& x) noexcept
{
diff --git a/third_party/xsimd/include/xsimd/arch/xsimd_sse2.hpp b/third_party/xsimd/include/xsimd/arch/xsimd_sse2.hpp
index 0a34cb1e9b..d39cc201f9 100644
--- a/third_party/xsimd/include/xsimd/arch/xsimd_sse2.hpp
+++ b/third_party/xsimd/include/xsimd/arch/xsimd_sse2.hpp
@@ -60,6 +60,10 @@ namespace xsimd
inline batch<T, A> insert(batch<T, A> const& self, T val, index<I>, requires_arch<generic>) noexcept;
template <class A, typename T, typename ITy, ITy... Indices>
inline batch<T, A> shuffle(batch<T, A> const& x, batch<T, A> const& y, batch_constant<batch<ITy, A>, Indices...>, requires_arch<generic>) noexcept;
+ template <class A, class T>
+ inline batch<T, A> avg(batch<T, A> const&, batch<T, A> const&, requires_arch<generic>) noexcept;
+ template <class A, class T>
+ inline batch<T, A> avgr(batch<T, A> const&, batch<T, A> const&, requires_arch<generic>) noexcept;
// abs
template <class A>
@@ -148,6 +152,44 @@ namespace xsimd
return _mm_movemask_epi8(self) != 0;
}
+ // avgr
+ template <class A, class T, class = typename std::enable_if<std::is_unsigned<T>::value, void>::type>
+ inline batch<T, A> avgr(batch<T, A> const& self, batch<T, A> const& other, requires_arch<sse2>) noexcept
+ {
+ XSIMD_IF_CONSTEXPR(sizeof(T) == 1)
+ {
+ return _mm_avg_epu8(self, other);
+ }
+ else XSIMD_IF_CONSTEXPR(sizeof(T) == 2)
+ {
+ return _mm_avg_epu16(self, other);
+ }
+ else
+ {
+ return avgr(self, other, generic {});
+ }
+ }
+
+ // avg
+ template <class A, class T, class = typename std::enable_if<std::is_unsigned<T>::value, void>::type>
+ inline batch<T, A> avg(batch<T, A> const& self, batch<T, A> const& other, requires_arch<sse2>) noexcept
+ {
+ XSIMD_IF_CONSTEXPR(sizeof(T) == 1)
+ {
+ auto adj = ((self ^ other) << 7) >> 7;
+ return avgr(self, other, A {}) - adj;
+ }
+ else XSIMD_IF_CONSTEXPR(sizeof(T) == 2)
+ {
+ auto adj = ((self ^ other) << 15) >> 15;
+ return avgr(self, other, A {}) - adj;
+ }
+ else
+ {
+ return avg(self, other, generic {});
+ }
+ }
+
// batch_bool_cast
template <class A, class T_out, class T_in>
inline batch_bool<T_out, A> batch_bool_cast(batch_bool<T_in, A> const& self, batch_bool<T_out, A> const&, requires_arch<sse2>) noexcept
diff --git a/third_party/xsimd/include/xsimd/arch/xsimd_wasm.hpp b/third_party/xsimd/include/xsimd/arch/xsimd_wasm.hpp
index 8160b2423b..ab9acdc8c3 100644
--- a/third_party/xsimd/include/xsimd/arch/xsimd_wasm.hpp
+++ b/third_party/xsimd/include/xsimd/arch/xsimd_wasm.hpp
@@ -37,6 +37,8 @@ namespace xsimd
inline batch<T, A> insert(batch<T, A> const& self, T val, index<I>, requires_arch<generic>) noexcept;
template <class A, typename T, typename ITy, ITy... Indices>
inline batch<T, A> shuffle(batch<T, A> const& x, batch<T, A> const& y, batch_constant<batch<ITy, A>, Indices...>, requires_arch<generic>) noexcept;
+ template <class A, class T>
+ inline batch<T, A> avg(batch<T, A> const&, batch<T, A> const&, requires_arch<generic>) noexcept;
// abs
template <class A, class T, typename std::enable_if<std::is_integral<T>::value && std::is_signed<T>::value, void>::type>
@@ -116,6 +118,44 @@ namespace xsimd
return wasm_f64x2_add(self, other);
}
+ // avgr
+ template <class A, class T, class = typename std::enable_if<std::is_unsigned<T>::value, void>::type>
+ inline batch<T, A> avgr(batch<T, A> const& self, batch<T, A> const& other, requires_arch<wasm>) noexcept
+ {
+ XSIMD_IF_CONSTEXPR(sizeof(T) == 1)
+ {
+ return wasm_u8x16_avgr(self, other);
+ }
+ else XSIMD_IF_CONSTEXPR(sizeof(T) == 2)
+ {
+ return wasm_u16x8_avgr(self, other);
+ }
+ else
+ {
+ return avgr(self, other, generic {});
+ }
+ }
+
+ // avg
+ template <class A, class T, class = typename std::enable_if<std::is_unsigned<T>::value, void>::type>
+ inline batch<T, A> avg(batch<T, A> const& self, batch<T, A> const& other, requires_arch<wasm>) noexcept
+ {
+ XSIMD_IF_CONSTEXPR(sizeof(T) == 1)
+ {
+ auto adj = ((self ^ other) << 7) >> 7;
+ return avgr(self, other, A {}) - adj;
+ }
+ else XSIMD_IF_CONSTEXPR(sizeof(T) == 2)
+ {
+ auto adj = ((self ^ other) << 15) >> 15;
+ return avgr(self, other, A {}) - adj;
+ }
+ else
+ {
+ return avg(self, other, generic {});
+ }
+ }
+
// all
template <class A>
inline bool all(batch_bool<float, A> const& self, requires_arch<wasm>) noexcept
diff --git a/third_party/xsimd/include/xsimd/config/xsimd_arch.hpp b/third_party/xsimd/include/xsimd/config/xsimd_arch.hpp
index 575459a009..ea48aa057d 100644
--- a/third_party/xsimd/include/xsimd/config/xsimd_arch.hpp
+++ b/third_party/xsimd/include/xsimd/config/xsimd_arch.hpp
@@ -194,7 +194,7 @@ namespace xsimd
using all_sve_architectures = arch_list<detail::sve<512>, detail::sve<256>, detail::sve<128>>;
using all_rvv_architectures = arch_list<detail::rvv<512>, detail::rvv<256>, detail::rvv<128>>;
- using all_arm_architectures = typename detail::join<all_sve_architectures, arch_list<neon64, neon>>::type;
+ using all_arm_architectures = typename detail::join<all_sve_architectures, arch_list<i8mm<neon64>, neon64, neon>>::type;
using all_riscv_architectures = all_rvv_architectures;
using all_wasm_architectures = arch_list<wasm>;
using all_architectures = typename detail::join<all_riscv_architectures, all_wasm_architectures, all_arm_architectures, all_x86_architectures>::type;
diff --git a/third_party/xsimd/include/xsimd/config/xsimd_config.hpp b/third_party/xsimd/include/xsimd/config/xsimd_config.hpp
index cf5163c37e..10a074dedb 100644
--- a/third_party/xsimd/include/xsimd/config/xsimd_config.hpp
+++ b/third_party/xsimd/include/xsimd/config/xsimd_config.hpp
@@ -352,6 +352,17 @@
/**
* @ingroup xsimd_config_macro
*
+ * Set to 1 if i8mm neon64 extension is available at compile-time, to 0 otherwise.
+ */
+#if defined(__ARM_FEATURE_MATMUL_INT8)
+#define XSIMD_WITH_I8MM_NEON64 1
+#else
+#define XSIMD_WITH_I8MM_NEON64 0
+#endif
+
+/**
+ * @ingroup xsimd_config_macro
+ *
* Set to 1 if SVE is available and bit width is pre-set at compile-time, to 0 otherwise.
*/
#if defined(__ARM_FEATURE_SVE) && defined(__ARM_FEATURE_SVE_BITS) && __ARM_FEATURE_SVE_BITS > 0
diff --git a/third_party/xsimd/include/xsimd/config/xsimd_cpuid.hpp b/third_party/xsimd/include/xsimd/config/xsimd_cpuid.hpp
index 5c8b1f38d0..89b883a396 100644
--- a/third_party/xsimd/include/xsimd/config/xsimd_cpuid.hpp
+++ b/third_party/xsimd/include/xsimd/config/xsimd_cpuid.hpp
@@ -18,6 +18,11 @@
#if defined(__linux__) && (defined(__ARM_NEON) || defined(_M_ARM) || defined(__riscv_vector))
#include <asm/hwcap.h>
#include <sys/auxv.h>
+
+#ifndef HWCAP2_I8MM
+#define HWCAP2_I8MM (1 << 13)
+#endif
+
#endif
#if defined(_MSC_VER)
@@ -66,6 +71,7 @@ namespace xsimd
ARCH_FIELD_EX(avx512vnni<::xsimd::avx512vbmi>, avx512vnni_vbmi)
ARCH_FIELD(neon)
ARCH_FIELD(neon64)
+ ARCH_FIELD_EX(i8mm<::xsimd::neon64>, i8mm_neon64)
ARCH_FIELD(sve)
ARCH_FIELD(rvv)
ARCH_FIELD(wasm)
@@ -83,6 +89,9 @@ namespace xsimd
#if defined(__aarch64__) || defined(_M_ARM64)
neon = 1;
neon64 = 1;
+#if defined(__linux__) && (!defined(__ANDROID_API__) || __ANDROID_API__ >= 18)
+ i8mm_neon64 = bool(getauxval(AT_HWCAP2) & HWCAP2_I8MM);
+#endif
#elif defined(__ARM_NEON) || defined(_M_ARM)
#if defined(__linux__) && (!defined(__ANDROID_API__) || __ANDROID_API__ >= 18)
diff --git a/third_party/xsimd/include/xsimd/types/xsimd_all_registers.hpp b/third_party/xsimd/include/xsimd/types/xsimd_all_registers.hpp
index 4350ca0a28..6537157bc6 100644
--- a/third_party/xsimd/include/xsimd/types/xsimd_all_registers.hpp
+++ b/third_party/xsimd/include/xsimd/types/xsimd_all_registers.hpp
@@ -36,6 +36,8 @@
#include "xsimd_avx512dq_register.hpp"
#include "xsimd_avx512f_register.hpp"
+#include "xsimd_i8mm_neon64_register.hpp"
+
#include "xsimd_neon64_register.hpp"
#include "xsimd_neon_register.hpp"
diff --git a/third_party/xsimd/include/xsimd/types/xsimd_api.hpp b/third_party/xsimd/include/xsimd/types/xsimd_api.hpp
index 0420f0a09d..751e31d33a 100644
--- a/third_party/xsimd/include/xsimd/types/xsimd_api.hpp
+++ b/third_party/xsimd/include/xsimd/types/xsimd_api.hpp
@@ -203,6 +203,36 @@ namespace xsimd
}
/**
+ * @ingroup batch_math
+ *
+ * Computes the average of batches \c x and \c y
+ * @param x batch of T
+ * @param y batch of T
+ * @return the average of elements between \c x and \c y.
+ */
+ template <class T, class A>
+ inline batch<T, A> avg(batch<T, A> const& x, batch<T, A> const& y) noexcept
+ {
+ detail::static_check_supported_config<T, A>();
+ return kernel::avg<A>(x, y, A {});
+ }
+
+ /**
+ * @ingroup batch_math
+ *
+ * Computes the rounded average of batches \c x and \c y
+ * @param x batch of T
+ * @param y batch of T
+ * @return the rounded average of elements between \c x and \c y.
+ */
+ template <class T, class A>
+ inline batch<T, A> avgr(batch<T, A> const& x, batch<T, A> const& y) noexcept
+ {
+ detail::static_check_supported_config<T, A>();
+ return kernel::avgr<A>(x, y, A {});
+ }
+
+ /**
* @ingroup batch_conversion
*
* Perform a static_cast from \c T_in to \c T_out on \c \c x.
diff --git a/third_party/xsimd/include/xsimd/types/xsimd_i8mm_neon64_register.hpp b/third_party/xsimd/include/xsimd/types/xsimd_i8mm_neon64_register.hpp
new file mode 100644
index 0000000000..fc0c884d0b
--- /dev/null
+++ b/third_party/xsimd/include/xsimd/types/xsimd_i8mm_neon64_register.hpp
@@ -0,0 +1,46 @@
+/***************************************************************************
+ * Copyright (c) Johan Mabille, Sylvain Corlay, Wolf Vollprecht and *
+ * Martin Renou *
+ * Copyright (c) QuantStack *
+ * Copyright (c) Serge Guelton *
+ * *
+ * Distributed under the terms of the BSD 3-Clause License. *
+ * *
+ * The full license is in the file LICENSE, distributed with this software. *
+ ****************************************************************************/
+
+#ifndef XSIMD_I8MM_NEON64_REGISTER_HPP
+#define XSIMD_I8MM_NEON64_REGISTER_HPP
+
+#include "./xsimd_neon64_register.hpp"
+
+namespace xsimd
+{
+ template <typename arch>
+ struct i8mm;
+
+ /**
+ * @ingroup architectures
+ *
+ * Neon64 + i8mm instructions
+ */
+ template <>
+ struct i8mm<neon64> : neon64
+ {
+ static constexpr bool supported() noexcept { return XSIMD_WITH_I8MM_NEON64; }
+ static constexpr bool available() noexcept { return true; }
+ static constexpr unsigned version() noexcept { return generic::version(8, 2, 0); }
+ static constexpr char const* name() noexcept { return "i8mm+neon64"; }
+ };
+
+#if XSIMD_WITH_I8MM_NEON64
+ namespace types
+ {
+
+ XSIMD_DECLARE_SIMD_REGISTER_ALIAS(i8mm<neon64>, neon64);
+
+ }
+#endif
+
+}
+#endif
diff --git a/third_party/xsimd/moz.yaml b/third_party/xsimd/moz.yaml
index 33b7a20dba..7bd3d2fd13 100644
--- a/third_party/xsimd/moz.yaml
+++ b/third_party/xsimd/moz.yaml
@@ -10,8 +10,8 @@ origin:
url: https://github.com/QuantStack/xsimd
- release: 2af7de3641ba38324375699ce261f20557c02dc9 (2024-01-09T11:30:28Z).
- revision: 2af7de3641ba38324375699ce261f20557c02dc9
+ release: ce58d62666c315140eb54042498d93114edbaa68 (2024-02-27T16:05:37Z).
+ revision: ce58d62666c315140eb54042498d93114edbaa68
license: BSD-3-Clause